Class: Fastlane::Actions::PrototypeBuildDetailsCommentAction

Inherits:
Action
  • Object
show all
Defined in:
lib/fastlane/plugin/wpmreleasetoolkit/actions/common/prototype_build_details_comment_action.rb

Defined Under Namespace

Classes: FirebaseReleaseInfo

Helpers collapse

NO_INSTALL_URL_ERROR_MESSAGE =
<<~NO_URL_ERROR
  No URL provided to download or install the app.
   - Either use this action right after using `firebase_app_distribution` so this action can extract the download URL from the `lane_context`
   - Or provide an explicit value for the `download_url` parameter
NO_URL_ERROR
DEFAULT_FOOTNOTE =
'<em>Automatticians: You can use our internal self-serve MC tool to give yourself access to those builds if needed.</em>'

Helpers collapse

Documentation collapse

Class Method Summary collapse

Class Method Details

.authorsObject



285
286
287
# File 'lib/fastlane/plugin/wpmreleasetoolkit/actions/common/prototype_build_details_comment_action.rb', line 285

def self.authors
  ['Automattic']
end

.available_optionsObject



225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
# File 'lib/fastlane/plugin/wpmreleasetoolkit/actions/common/prototype_build_details_comment_action.rb', line 225

def self.available_options
  [
    FastlaneCore::ConfigItem.new(
      key: :app_display_name,
      description: 'The display name to use for the app in the comment message',
      optional: false,
      type: String
    ),
    FastlaneCore::ConfigItem.new(
      key: :app_icon,
      description: 'The name of an emoji from the https://github.com/buildkite/emojis list or the full image URL to use for the icon of the app in the message',
      type: String,
      optional: true,
      default_value_dynamic: true # Defaults to `:firebase:` only if `firebase_app_distribution` was used
    ),
    FastlaneCore::ConfigItem.new(
      key: :download_url,
      description: <<~DESC,
        The URL to use to download/install the build.
        - If you used `firebase_app_distribution` to upload the build during the same `fastlane` run, you should leave this nil
        - If you used `firebase_app_distribution` during a separate CI job, you can store the `:testingUri` of that call's returned hash (in e.g. Buildkite metadata), then pass that URI to this parameter
        - Otherwise, you can provide a direct download URL for the build (e.g. link to Cloudfront or AppsCDN URL)
      DESC
      type: String,
      optional: true,
      default_value: nil
    ),
    FastlaneCore::ConfigItem.new(
      key: :fold,
      description: 'If true, will wrap the HTML table inside a <details> block (hidden by default)',
      type: Boolean,
      default_value: false
    ),
    FastlaneCore::ConfigItem.new(
      key: :metadata,
      description: 'All additional metadata (as key/value pairs) you want to include in the HTML table of the comment. ' \
       + 'If you are running this action after `firebase_app_distribution`, some metadata will automatically be added and merged with this list',
      type: Hash,
      optional: true,
      default_value_dynamic: true # As some metadata will be auto-filled if you used `firebase_app_distribution`
    ),
    FastlaneCore::ConfigItem.new(
      key: :footnote,
      description: 'Optional footnote to add below the HTML table of the comment. ' \
       + 'If you are running this action after `firebase_app_distribution`, a default footnote for Automatticians will be used unless you provide an explicit value',
      type: String,
      optional: true,
      default_value_dynamic: true # We have a default footnote for the case when you used Firebase App Distribution
    ),
  ]
end

.descriptionObject



198
199
200
# File 'lib/fastlane/plugin/wpmreleasetoolkit/actions/common/prototype_build_details_comment_action.rb', line 198

def self.description
  'Generates a string providing all the details of a prototype build, nicely-formatted and ready to be used as a PR comment (e.g. via `comment_on_pr`).'
end

.detailsObject



202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
# File 'lib/fastlane/plugin/wpmreleasetoolkit/actions/common/prototype_build_details_comment_action.rb', line 202

def self.details
  <<~DESC
    Generates a string providing all the details of a prototype build, nicely-formatted as HTML.
    The returned string will typically be subsequently used by the `comment_on_pr` action to post that HTML as comment on a PR.

    If you used the `firebase_app_distribution` action (to upload the Prototype build to Firebase App Distribution) before calling this action,
    then many of the metadata will be automatically extracted from the `lane_context` it exposed:

    - "Version" (from `:displayVersion`) and "Build Number" (from `:buildVersion`)
    - "Bundle ID" (extracted from `:firebaseConsoleUri`)
    - "Commit" (from `BUILDKITE_COMMIT` environment variable or last git commit)
    - "Installation URL" (from `:testingUri`)

    You can also pass additional metadata to this action via the `metadata` parameter, and they will also be included in the HTML table of the comment.

    This means that if you are using Firebase App Distribution to distribute your Prototype Build, the can just provide
    `app_display_name` and optionally `app_icon`, and the rest will be automatically inferred from the `lane_context`.

    If you are not using Firebase App Distribution, you can pass an explicit value for the `download_url` parameter,
    and the action will use it to generate the installation link and QR code.
  DESC
end

.generate_metadata_hash(params:, release_info:) ⇒ Hash<String, String>

Constructs the Hash of metadata, based on the explicit ones passed by the user as parameter + the implicit ones from ‘FirebaseReleaseInfo`

Parameters:

  • params (Hash<Symbol, Any>)

    The action’s parameters, as received by ‘self.run`

  • release_info (FirebaseReleaseInfo?)

    The information about the Firebase Release extracted from the ‘lane_context`

Returns:

  • (Hash<String, String>)

    A hash of all the metadata, consolidated from both the explicit and the implicit ones



104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
# File 'lib/fastlane/plugin/wpmreleasetoolkit/actions/common/prototype_build_details_comment_action.rb', line 104

def self.(params:, release_info:)
  # Add explicit metadata provided by the caller
   = params[:metadata]&.transform_keys(&:to_s) || {}

  # Add Firebase-specific metadata if available
  unless release_info.nil?
    ['Build Number'] ||= "<code>#{release_info.build_version}</code>"
    ['Version'] ||= "<code>#{release_info.display_version}</code>"
    [release_info.os == 'ios' ? 'Bundle ID' : 'Application ID'] ||= "<code>#{release_info.bundle_id}</code>"
  end

  # Add git metadata
  ['Commit'] ||= ENV.fetch('BUILDKITE_COMMIT', nil) || other_action.last_git_commit[:abbreviated_commit_hash]
  
end

.img_tag(url_or_emoji) ⇒ String

Creates an HTML ‘<img>` tag for an icon URL or the image URL to represent a given Buildkite emoji

Parameters:

  • url_or_emoji (String)

    A ‘String` which can be:

    • Either a valid URI to an image

    • Or a string formatted like ‘:emojiname:`, using a valid Buildite emoji name as defined in github.com/buildkite/emojis

Returns:

  • (String)

    The ‘<img …>` tag with the proper image and alt tag

Raises:

  • (FastlaneCore::Interface::FastlaneError)

    if the URL is invalid



182
183
184
185
186
187
188
189
190
191
192
# File 'lib/fastlane/plugin/wpmreleasetoolkit/actions/common/prototype_build_details_comment_action.rb', line 182

def self.img_tag(url_or_emoji)
  return nil if url_or_emoji.nil?

  emoji = url_or_emoji.match(/:(.*):/)&.captures&.first
  app_icon_url = if emoji
                   "https://raw.githubusercontent.com/buildkite/emojis/main/img-buildkite-64/#{emoji}.png"
                 else
                   url_or_emoji.tap { parse_url!(url_or_emoji) }
                 end
  app_icon_url ? "<img align='top' src='#{app_icon_url}' width='20px' alt='App Icon' />" : ''
end

Constructs the installation link, QR code URL and extra metadata for download links from the available info

Parameters:

  • release_info (FirebaseReleaseInfo?)

    The information about the Firebase Release extracted from the ‘lane_context`

  • download_url (String)

    The ‘download_url` parameter passed to the action, if one was provided

Returns:

  • ((String, Hash<String,String>))

    A tuple containing:

    • The URL for the QR Code

    • A Hash of the extra metadata key/value pairs to add to the existing metadata, to enrich them with download/install links

Raises:

  • (FastlaneCore::Interface::FastlaneError)

    if no valid installation URL could be determined



129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
# File 'lib/fastlane/plugin/wpmreleasetoolkit/actions/common/prototype_build_details_comment_action.rb', line 129

def self.install_links(release_info:, download_url:)
  install_url = nil
   = {}
  firebase_release_id = nil

  # Validate and process direct download URL if provided
  if download_url
    uri = parse_url!(download_url)
    install_url = download_url

    if is_firebase_url?(uri)
      firebase_release_id = File.basename(uri.path)
    else
      filename = File.basename(uri.path)
      ['Direct Download'] = "<a href='#{CGI.escape_html(install_url)}'><code>#{CGI.escape_html(filename)}</code></a>"
    end
  end

  # Process Firebase testing URL if available from release_info
  if release_info&.testing_url
    install_url = release_info.testing_url
    firebase_release_id = release_info.release_id
  end

  UI.user_error!(NO_INSTALL_URL_ERROR_MESSAGE) if install_url.nil?

  # Add Installation URL metadata if we have a release_id
  ['Installation URL'] = "<a href='#{CGI.escape_html(install_url)}'>#{CGI.escape_html(firebase_release_id)}</a>" if firebase_release_id

  # Generate QR code URL with proper escaping
  qr_code_url = "https://api.qrserver.com/v1/create-qr-code/?size=500x500&qzone=4&data=#{CGI.escape(install_url)}"
  [qr_code_url, ]
end

.is_firebase_url?(url) ⇒ Boolean

Determines if a given URI is a Firebase App Distribution URL

Parameters:

  • url (String, URI)

    The URL to check, either as a String or an already-parsed URI

Returns:

  • (Boolean)

    true if the URL is a Firebase App Distribution URL

Raises:

  • (FastlaneCore::Interface::FastlaneError)

    if the URL is invalid



169
170
171
172
# File 'lib/fastlane/plugin/wpmreleasetoolkit/actions/common/prototype_build_details_comment_action.rb', line 169

def self.is_firebase_url?(url)
  uri = url.is_a?(URI) ? url : parse_url!(url)
  uri.host == 'appdistribution.firebase.google.com' && uri.path.start_with?('/testerapps/')
end

.is_supported?(platform) ⇒ Boolean

Returns:

  • (Boolean)


289
290
291
# File 'lib/fastlane/plugin/wpmreleasetoolkit/actions/common/prototype_build_details_comment_action.rb', line 289

def self.is_supported?(platform)
  true
end

.parse_url!(url) ⇒ URI

Parse and validate a URL string

Parameters:

  • url (String)

    The URL string to parse and validate

Returns:

  • (URI)

    The parsed URI object

Raises:

  • (FastlaneCore::Interface::FastlaneError)

    if the URL is invalid



64
65
66
67
68
69
70
# File 'lib/fastlane/plugin/wpmreleasetoolkit/actions/common/prototype_build_details_comment_action.rb', line 64

def self.parse_url!(url)
  URI.parse(url).tap do |uri|
    raise URI::InvalidURIError unless uri.is_a?(URI::HTTP) || uri.is_a?(URI::HTTPS)
  end
rescue URI::InvalidURIError
  UI.user_error!("Invalid URL: #{url}")
end

.return_typeObject



277
278
279
# File 'lib/fastlane/plugin/wpmreleasetoolkit/actions/common/prototype_build_details_comment_action.rb', line 277

def self.return_type
  :string
end

.return_valueObject



281
282
283
# File 'lib/fastlane/plugin/wpmreleasetoolkit/actions/common/prototype_build_details_comment_action.rb', line 281

def self.return_value
  'The HTML comment containing all the relevant info about a Prototype build and links to install it'
end

.run(params) ⇒ Object



9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
# File 'lib/fastlane/plugin/wpmreleasetoolkit/actions/common/prototype_build_details_comment_action.rb', line 9

def self.run(params)
  app_display_name = params[:app_display_name]
  download_url = params[:download_url]
  release_info = FirebaseReleaseInfo.from_lane_context

  # Merge explicit extra metadata passed from params with ones derived from FirebaseReleaseInfo
   = (params: params, release_info: release_info)
  # Build the installation link, QR code URL and extra metadata for download links from the available info
  qr_code_url,  = install_links(release_info: release_info, download_url: download_url)
  .merge!()

  # Build the comment parts and body
  app_icon = params[:app_icon]
  app_icon ||= ':firebase:' if !release_info.nil? || (download_url && is_firebase_url?(download_url))
  intro = "#{img_tag(app_icon)}📲 You can test the changes from this Pull Request in <b>#{CGI.escape_html(app_display_name)}</b> by scanning the QR code below to install the corresponding build."
   = .compact.map { |key, value| "<tr><td><b>#{key}</b></td><td>#{value}</td></tr>" }
  footnote = params[:footnote]
  footnote ||= DEFAULT_FOOTNOTE if !release_info.nil? || (download_url && is_firebase_url?(download_url))

  body = <<~COMMENT_BODY.chomp('')
    <table>
    <tr>
      <td rowspan='#{.count + 1}' width='260px'><img src='#{qr_code_url}' width='250' height='250' /></td>
      <td><b>App Name</b></td><td>#{CGI.escape_html(app_display_name)}</td>
    </tr>
    #{.join("\n")}
    </table>
    #{footnote}
  COMMENT_BODY

  if params[:fold]
    "<details><summary>#{intro}</summary>\n#{body}\n</details>\n"
  else
    "<p>#{intro}</p>\n#{body}\n"
  end
end