fastlane-plugin-wpmreleasetoolkit 11.0.1 → 11.0.3

Sign up to get free protection for your applications and to get access to all the features.
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