fastlane-plugin-wpmreleasetoolkit 11.0.1 → 11.0.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 5ea9e7c3db27c974122c8c22696a0d065cd0a25b2c44990123f89a6a0c87f590
4
- data.tar.gz: a67b6010b3a1aef1c803584d806561ee24029ebb748ecbe5134841f3f87e2ce3
3
+ metadata.gz: 7eae107cbe0b856fbe2c3292db7b84bc7ba57ec98da3717e7872243923e63b1d
4
+ data.tar.gz: 9576cd4c24122685ab6447fcdd64a2751e44fc36b368b9b22b623e5fe34353c4
5
5
  SHA512:
6
- metadata.gz: a5bb77d369eb8018713b28ff64a1f80ed5f43ef01b72dc252fbe7707234319b41054ae49110b43de487c79eea7c667892e273b40531c14af30aff7dad4a2be9a
7
- data.tar.gz: 9626fe07b3a4a48954a98b75c269a3bcd79f0a747d29ca975c513f22ee2ad817b28cf4d66f5300042168cea39240ddc42572a8210e5db5c5357b7315c1676c4e
6
+ metadata.gz: c2f85f419598ad21a97f91767bd264303cd4ce47514f61648bceb482214fbab0dc379704aa15dc9d189743ff31220c6e9c61b283826c453ab088594f3886fa3f
7
+ data.tar.gz: 4486133931d4c775ff8ec0a084bce9eba9309cebf85f06f9e9aa3182a5974b34d07cb598b48fc30834fb09f8e23a40c0d98aa0c08cc12d982ddea468f4a93c1a
@@ -14,9 +14,9 @@ module Fastlane
14
14
  commit: params[:commit],
15
15
  env: params[:environment].merge(pipeline_name),
16
16
  message: params[:message],
17
- # Buildkite will not trigger a build if the GitHub activity for that branch is turned off
18
- # We want API triggers to work regardless of the GitHub activity settings, so this option is necessary
19
- # https://forum.buildkite.community/t/request-build-error-branches-have-been-disabled-for-this-pipeline/1463/2
17
+ # Buildkite will not trigger a build if the GitHub activity for that branch is turned off.
18
+ # We want API triggers to work regardless of the GitHub activity settings, so this option is necessary.
19
+ # See https://forum.buildkite.community/t/request-build-error-branches-have-been-disabled-for-this-pipeline/1463/2
20
20
  ignore_pipeline_branch_filters: true
21
21
  }.compact # remove entries with `nil` values from the Hash, if any
22
22
 
@@ -27,11 +27,15 @@ module Fastlane
27
27
  options
28
28
  )
29
29
 
30
+ build_url = response.web_url
31
+
30
32
  if response.state == 'scheduled'
31
- UI.success("Successfully scheduled new build. You can see it at '#{response.web_url}'")
33
+ UI.success("Successfully scheduled new build. You can see it at '#{build_url}'")
32
34
  else
33
- UI.crash!("Failed to start job\nError: [#{response}]")
35
+ UI.crash!("Failed to start build.\nError: [#{response}]")
34
36
  end
37
+
38
+ build_url
35
39
  end
36
40
 
37
41
  #####################################################
@@ -39,7 +43,8 @@ module Fastlane
39
43
  #####################################################
40
44
 
41
45
  def self.description
42
- 'Triggers a job on Buildkite'
46
+ # https://buildkite.com/docs/pipelines/glossary#build
47
+ 'Triggers a build on Buildkite'
43
48
  end
44
49
 
45
50
  def self.available_options
@@ -99,6 +104,10 @@ module Fastlane
99
104
  ['Automattic']
100
105
  end
101
106
 
107
+ def self.return_value
108
+ 'The web URL of the build the action started.'
109
+ end
110
+
102
111
  def self.is_supported?(platform)
103
112
  true
104
113
  end
@@ -152,6 +152,13 @@ module Fastlane
152
152
  (added_count + updated_count) != 0
153
153
  end
154
154
 
155
+ ########
156
+ # @!group Verify diff of library vs main strings matches
157
+ #
158
+ # @note This set of methods is used by `an_validate_lib_strings_action`
159
+ # (which doesn't seem to be used by any of our Android projects nowadays?)
160
+ ########
161
+
155
162
  def self.verify_diff(diff_string, main_strings, lib_strings, library)
156
163
  return unless diff_string.start_with?('name=')
157
164
 
@@ -198,6 +205,8 @@ module Fastlane
198
205
  end
199
206
  end
200
207
 
208
+ # @!endgroup
209
+
201
210
  ########
202
211
  # @!group Downloading translations from GlotPress
203
212
  ########
@@ -223,7 +232,7 @@ module Fastlane
223
232
  #
224
233
  # @param [String] res_dir The relative path to the `…/src/main/res` directory.
225
234
  # @param [String] glotpress_project_url The base URL to the glotpress project to download the strings from.
226
- # @param [Hash{String=>String}, Array] glotpress_filters
235
+ # @param [Hash{Symbol=>String}, Array] glotpress_filters
227
236
  # The filters to apply when exporting strings from GlotPress.
228
237
  # Typical examples include `{ status: 'current' }` or `{ status: 'review' }`.
229
238
  # If an array of Hashes is provided instead of a single Hash, this method will perform as many
@@ -235,10 +244,8 @@ module Fastlane
235
244
  def self.download_from_glotpress(res_dir:, glotpress_project_url:, locales_map:, glotpress_filters: { status: 'current' })
236
245
  glotpress_filters = [glotpress_filters] unless glotpress_filters.is_a?(Array)
237
246
 
238
- attributes_to_copy = %w[formatted] # Attributes that we want to replicate into translated `string.xml` files
239
247
  orig_file = File.join(res_dir, 'values', 'strings.xml')
240
248
  orig_xml = File.open(orig_file) { |f| Nokogiri::XML(f, nil, Encoding::UTF_8.to_s) }
241
- orig_attributes = orig_xml.xpath('//string').to_h { |tag| [tag['name'], tag.attributes.select { |k, _| attributes_to_copy.include?(k) }] }
242
249
 
243
250
  locales_map.each do |lang_codes|
244
251
  all_xml_documents = glotpress_filters.map do |filters|
@@ -250,13 +257,7 @@ module Fastlane
250
257
  # Merge all XMLs together
251
258
  merged_xml = merge_xml_documents(all_xml_documents)
252
259
 
253
- # Process XML (text substitutions, replicate attributes, quick-lint string)
254
- merged_xml.xpath('//string').each do |string_tag|
255
- apply_substitutions(string_tag)
256
- orig_attributes[string_tag['name']]&.each { |k, v| string_tag[k] = v }
257
- quick_lint(string_tag, lang_codes[:android])
258
- end
259
- merged_xml.xpath('//string-array/item').each { |item_tag| apply_substitutions(item_tag) }
260
+ post_process_xml!(merged_xml, locale_code: lang_codes[:android], original_xml: orig_xml)
260
261
 
261
262
  # Save
262
263
  lang_dir = File.join(res_dir, "values-#{lang_codes[:android]}")
@@ -266,8 +267,10 @@ module Fastlane
266
267
  end
267
268
  end
268
269
 
270
+ # @!endgroup
271
+
269
272
  #####################
270
- # Private Helpers
273
+ # @!group Private Helpers
271
274
  #####################
272
275
 
273
276
  # Downloads the export from GlotPress for a given locale and given filters
@@ -276,7 +279,7 @@ module Fastlane
276
279
  # @param [String] locale The GlotPress locale code to download strings for.
277
280
  # @param [Hash{Symbol=>String}] filters The hash of filters to apply when exporting from GlotPress.
278
281
  # Typical examples include `{ status: 'current' }` or `{ status: 'review' }`.
279
- # @return [Nokogiri::XML] the download XML document, parsed as a Nokogiri::XML object
282
+ # @return [Nokogiri::XML::Document] the download XML document, parsed as a Nokogiri::XML object
280
283
  #
281
284
  def self.download_glotpress_export_file(project_url:, locale:, filters:)
282
285
  query_params = filters.transform_keys { |k| "filters[#{k}]" }.merge(format: 'android')
@@ -297,6 +300,9 @@ module Fastlane
297
300
 
298
301
  # Merge multiple Nokogiri::XML `strings.xml` documents together
299
302
  #
303
+ # Used especially when we provided multiple GlotPress filters to `download_from_glotpress`,
304
+ # as in this case we'd trigger one export per filter, then merge the result in a single XML
305
+ #
300
306
  # @param [Array<Nokogiri::XML::Document>] all_xmls Array of the Nokogiri XML documents to merge together
301
307
  # @return [Nokogiri::XML::Document] The merged document.
302
308
  #
@@ -323,11 +329,53 @@ module Fastlane
323
329
  end
324
330
  private_class_method :merge_xml_documents
325
331
 
326
- # Apply some common text substitutions to tag contents
332
+ # Process a downloaded XML (in-place), to apply the following
333
+ # - replicate attributes from the nodes of the original XML (`translatable`, `tools:ignore`, …) to the translated XML
334
+ # - text substitutions for common special characters
335
+ # - quick-lint string by searching for common issue patterns (using `%%` in a `formatted=false` string, etc)
336
+ #
337
+ # @param [Nokogiri::XML::Document] translated_xml The downloaded XML to post-process
338
+ # @param [String] locale_code The android locale code associated with the translated_xml
339
+ # @param [Nokogiri::XML::Document] original_xml The original `values/strings.xml` to use as reference
340
+ #
341
+ def self.post_process_xml!(translated_xml, locale_code:, original_xml:)
342
+ copy_orig_attributes = lambda do |node, xpath|
343
+ orig_attributes = original_xml.xpath(xpath)&.first&.attribute_nodes&.to_h do |attr|
344
+ [[attr.namespace&.prefix, attr.name].compact.join(':'), attr.value]
345
+ end
346
+ orig_attributes&.each { |k, v| node[k] = v unless k == 'name' }
347
+ end
348
+
349
+ # 1. Replicate namespaces on the document (especially `xmlns:tools` if present)
350
+ original_xml.namespaces.each { |k, v| translated_xml.root&.add_namespace(k.delete_prefix('xmlns:'), v) }
351
+ # 2. Replicate attributes on any node with `@name` attribute (`string`, `string-array`, `plurals`)
352
+ translated_xml.xpath('//*[@name]').each do |node|
353
+ copy_orig_attributes.call(node, "//#{node.name}[@name = '#{node['name']}']")
354
+ end
355
+ # 3. Process copies for `string` nodes
356
+ translated_xml.xpath('//string[@name]').each do |string_node|
357
+ apply_substitutions!(string_node)
358
+ quick_lint(string_node, locale_code)
359
+ end
360
+ # 4. Process copies for `string-array/item` nodes
361
+ translated_xml.xpath('//string-array[@name]/item').each do |item_node|
362
+ apply_substitutions!(item_node)
363
+ quick_lint(item_node, locale_code)
364
+ end
365
+ # 5. Replicate attributes + Process copies for `plurals/item` nodes
366
+ translated_xml.xpath('//plurals[@name]/item[@quantity]').each do |item_node|
367
+ copy_orig_attributes.call(item_node, "//plurals[@name = '#{item_node.parent['name']}']/item[@quantity = '#{item_node['quantity']}']")
368
+ apply_substitutions!(item_node)
369
+ quick_lint(item_node, locale_code)
370
+ end
371
+ end
372
+ private_class_method :post_process_xml!
373
+
374
+ # Apply some common text substitutions to tag contents, like `... => …` or en-dash instead of regular dash for ranges of numbers
327
375
  #
328
376
  # @param [Nokogiri::XML::Node] tag The XML tag/node to apply substitutions to
329
377
  #
330
- def self.apply_substitutions(tag)
378
+ def self.apply_substitutions!(tag)
331
379
  tag.content = tag.content.gsub('...', '…')
332
380
 
333
381
  # Typography en-dash
@@ -339,17 +387,28 @@ module Fastlane
339
387
  is_negative_number ? str : "#{match[1]}\u{2013}#{match[2]}"
340
388
  end
341
389
  end
342
- private_class_method :apply_substitutions
390
+ private_class_method :apply_substitutions!
343
391
 
344
- # Perform some quick basic checks about an individual `<string>` tag and print warnings accordingly
392
+ # Perform some quick basic checks about an individual `<string>` tag and print warnings accordingly:
393
+ # - detect the use of `%%` in the string even if `formatted=false` is set
394
+ # - detect the presence of `\@string/` in translated XML, which suggests the original key that referenced `@string/…` did not set `translatable=false`
395
+ # and thus that `@string/…` copy was sent to GlotPress for translation, then escaped during exporting it back.
345
396
  #
346
- # @param [Nokogiri::XML::Node] string_tag The XML tag/node to check
397
+ # @param [Nokogiri::XML::Node] node The XML tag/node to check the content of
347
398
  # @param [String] lang The language we are currently processing. Used for providing context during logging / warning message
348
399
  #
349
- def self.quick_lint(string_tag, lang)
350
- return unless string_tag['formatted'] == 'false' && string_tag.content.include?('%%')
351
-
352
- UI.important "Warning: [#{lang}] translation for '#{string_tag['name']}' has attribute formatted=false, but still contains escaped '%%' in translation."
400
+ def self.quick_lint(node, lang)
401
+ named_node = node.has_attribute?('name') ? node : node.parent
402
+ if named_node['formatted'] == 'false' && node.content.include?('%%')
403
+ UI.important "Warning: [#{lang}] translation for '#{named_node['name']}' has attribute formatted=false, but still contains escaped '%%' in translation."
404
+ end
405
+ # rubocop:disable Style/GuardClause
406
+ if node.content.include?('\\@string/')
407
+ UI.important "Warning: [#{lang}] exported translation for '#{named_node['name']}' contains `\\@string/`. This is a sign that this entry was not marked as `translatable=false` " \
408
+ + 'in the original `values/strings.xml`, and was thus sent to GlotPress, which added the backslash when exporting it back.'
409
+ node.content = node.content.gsub('\\@string/', '@string/')
410
+ end
411
+ # rubocop:enable Style/GuardClause
353
412
  end
354
413
  private_class_method :quick_lint
355
414
 
@@ -2,6 +2,6 @@
2
2
 
3
3
  module Fastlane
4
4
  module Wpmreleasetoolkit
5
- VERSION = '11.0.1'
5
+ VERSION = '11.0.3'
6
6
  end
7
7
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: fastlane-plugin-wpmreleasetoolkit
3
3
  version: !ruby/object:Gem::Version
4
- version: 11.0.1
4
+ version: 11.0.3
5
5
  platform: ruby
6
6
  authors:
7
7
  - Automattic
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2024-04-15 00:00:00.000000000 Z
11
+ date: 2024-07-18 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activesupport