fastlane-plugin-wpmreleasetoolkit 4.0.0 → 5.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (93) hide show
  1. checksums.yaml +4 -4
  2. data/lib/fastlane/plugin/wpmreleasetoolkit/actions/android/an_localize_libs_action.rb +10 -5
  3. data/lib/fastlane/plugin/wpmreleasetoolkit/actions/android/an_update_metadata_source_action.rb +1 -1
  4. data/lib/fastlane/plugin/wpmreleasetoolkit/actions/android/an_validate_lib_strings_action.rb +1 -1
  5. data/lib/fastlane/plugin/wpmreleasetoolkit/actions/android/android_betabuild_prechecks.rb +1 -1
  6. data/lib/fastlane/plugin/wpmreleasetoolkit/actions/android/android_build_prechecks.rb +1 -1
  7. data/lib/fastlane/plugin/wpmreleasetoolkit/actions/android/android_build_preflight.rb +1 -1
  8. data/lib/fastlane/plugin/wpmreleasetoolkit/actions/android/android_bump_version_beta.rb +1 -1
  9. data/lib/fastlane/plugin/wpmreleasetoolkit/actions/android/android_bump_version_final_release.rb +1 -1
  10. data/lib/fastlane/plugin/wpmreleasetoolkit/actions/android/android_bump_version_hotfix.rb +1 -1
  11. data/lib/fastlane/plugin/wpmreleasetoolkit/actions/android/android_bump_version_release.rb +1 -1
  12. data/lib/fastlane/plugin/wpmreleasetoolkit/actions/android/android_codefreeze_prechecks.rb +1 -1
  13. data/lib/fastlane/plugin/wpmreleasetoolkit/actions/android/android_completecodefreeze_prechecks.rb +1 -1
  14. data/lib/fastlane/plugin/wpmreleasetoolkit/actions/android/android_create_xml_release_notes.rb +1 -1
  15. data/lib/fastlane/plugin/wpmreleasetoolkit/actions/android/android_current_branch_is_hotfix.rb +1 -1
  16. data/lib/fastlane/plugin/wpmreleasetoolkit/actions/android/android_download_file_by_version.rb +1 -1
  17. data/lib/fastlane/plugin/wpmreleasetoolkit/actions/android/android_download_translations_action.rb +1 -1
  18. data/lib/fastlane/plugin/wpmreleasetoolkit/actions/android/android_finalize_prechecks.rb +1 -1
  19. data/lib/fastlane/plugin/wpmreleasetoolkit/actions/android/android_firebase_test.rb +187 -0
  20. data/lib/fastlane/plugin/wpmreleasetoolkit/actions/android/android_get_alpha_version.rb +1 -1
  21. data/lib/fastlane/plugin/wpmreleasetoolkit/actions/android/android_get_app_version.rb +1 -1
  22. data/lib/fastlane/plugin/wpmreleasetoolkit/actions/android/android_get_release_version.rb +1 -1
  23. data/lib/fastlane/plugin/wpmreleasetoolkit/actions/android/{android_hotifx_prechecks.rb → android_hotfix_prechecks.rb} +1 -1
  24. data/lib/fastlane/plugin/wpmreleasetoolkit/actions/android/android_send_app_size_metrics.rb +279 -0
  25. data/lib/fastlane/plugin/wpmreleasetoolkit/actions/android/android_tag_build.rb +1 -1
  26. data/lib/fastlane/plugin/wpmreleasetoolkit/actions/android/android_update_release_notes.rb +1 -1
  27. data/lib/fastlane/plugin/wpmreleasetoolkit/actions/common/check_for_toolkit_updates_action.rb +1 -1
  28. data/lib/fastlane/plugin/wpmreleasetoolkit/actions/common/check_translation_progress.rb +1 -1
  29. data/lib/fastlane/plugin/wpmreleasetoolkit/actions/common/circleci_trigger_job_action.rb +1 -1
  30. data/lib/fastlane/plugin/wpmreleasetoolkit/actions/common/close_milestone_action.rb +1 -1
  31. data/lib/fastlane/plugin/wpmreleasetoolkit/actions/common/create_new_milestone_action.rb +1 -1
  32. data/lib/fastlane/plugin/wpmreleasetoolkit/actions/common/create_release_action.rb +1 -1
  33. data/lib/fastlane/plugin/wpmreleasetoolkit/actions/common/extract_release_notes_for_version_action.rb +1 -1
  34. data/lib/fastlane/plugin/wpmreleasetoolkit/actions/common/firebase_login.rb +44 -0
  35. data/lib/fastlane/plugin/wpmreleasetoolkit/actions/common/get_prs_list_action.rb +1 -1
  36. data/lib/fastlane/plugin/wpmreleasetoolkit/actions/common/gp_downloadmetadata_action.rb +1 -1
  37. data/lib/fastlane/plugin/wpmreleasetoolkit/actions/common/gp_update_metadata_source.rb +1 -2
  38. data/lib/fastlane/plugin/wpmreleasetoolkit/actions/common/promo_screenshots_action.rb +1 -1
  39. data/lib/fastlane/plugin/wpmreleasetoolkit/actions/common/removebranchprotection_action.rb +1 -1
  40. data/lib/fastlane/plugin/wpmreleasetoolkit/actions/common/setbranchprotection_action.rb +1 -1
  41. data/lib/fastlane/plugin/wpmreleasetoolkit/actions/common/setfrozentag_action.rb +1 -1
  42. data/lib/fastlane/plugin/wpmreleasetoolkit/actions/configure/configure_add_files_to_copy_action.rb +1 -1
  43. data/lib/fastlane/plugin/wpmreleasetoolkit/actions/configure/configure_apply_action.rb +1 -1
  44. data/lib/fastlane/plugin/wpmreleasetoolkit/actions/configure/configure_download_action.rb +2 -3
  45. data/lib/fastlane/plugin/wpmreleasetoolkit/actions/configure/configure_setup_action.rb +1 -1
  46. data/lib/fastlane/plugin/wpmreleasetoolkit/actions/configure/configure_update_action.rb +1 -1
  47. data/lib/fastlane/plugin/wpmreleasetoolkit/actions/configure/configure_validate_action.rb +3 -3
  48. data/lib/fastlane/plugin/wpmreleasetoolkit/actions/ios/add_development_certificates_to_provisioning_profiles.rb +1 -1
  49. data/lib/fastlane/plugin/wpmreleasetoolkit/actions/ios/add_devices_to_provisioning_profiles.rb +1 -1
  50. data/lib/fastlane/plugin/wpmreleasetoolkit/actions/ios/ios_betabuild_prechecks.rb +1 -1
  51. data/lib/fastlane/plugin/wpmreleasetoolkit/actions/ios/ios_build_prechecks.rb +1 -1
  52. data/lib/fastlane/plugin/wpmreleasetoolkit/actions/ios/ios_build_preflight.rb +1 -1
  53. data/lib/fastlane/plugin/wpmreleasetoolkit/actions/ios/ios_bump_version_beta.rb +1 -1
  54. data/lib/fastlane/plugin/wpmreleasetoolkit/actions/ios/ios_bump_version_hotfix.rb +1 -1
  55. data/lib/fastlane/plugin/wpmreleasetoolkit/actions/ios/ios_bump_version_release.rb +1 -1
  56. data/lib/fastlane/plugin/wpmreleasetoolkit/actions/ios/ios_check_beta_deps.rb +1 -1
  57. data/lib/fastlane/plugin/wpmreleasetoolkit/actions/ios/ios_clear_intermediate_tags.rb +1 -1
  58. data/lib/fastlane/plugin/wpmreleasetoolkit/actions/ios/ios_codefreeze_prechecks.rb +1 -1
  59. data/lib/fastlane/plugin/wpmreleasetoolkit/actions/ios/ios_completecodefreeze_prechecks.rb +1 -1
  60. data/lib/fastlane/plugin/wpmreleasetoolkit/actions/ios/ios_current_branch_is_hotfix.rb +1 -1
  61. data/lib/fastlane/plugin/wpmreleasetoolkit/actions/ios/ios_final_tag.rb +1 -1
  62. data/lib/fastlane/plugin/wpmreleasetoolkit/actions/ios/ios_finalize_prechecks.rb +1 -1
  63. data/lib/fastlane/plugin/wpmreleasetoolkit/actions/ios/ios_get_app_version.rb +1 -1
  64. data/lib/fastlane/plugin/wpmreleasetoolkit/actions/ios/ios_get_build_version.rb +1 -1
  65. data/lib/fastlane/plugin/wpmreleasetoolkit/actions/ios/ios_get_store_app_sizes.rb +1 -1
  66. data/lib/fastlane/plugin/wpmreleasetoolkit/actions/ios/{ios_hotifx_prechecks.rb → ios_hotfix_prechecks.rb} +1 -1
  67. data/lib/fastlane/plugin/wpmreleasetoolkit/actions/ios/ios_lint_localizations.rb +48 -9
  68. data/lib/fastlane/plugin/wpmreleasetoolkit/actions/ios/ios_localize_project.rb +1 -1
  69. data/lib/fastlane/plugin/wpmreleasetoolkit/actions/ios/ios_merge_strings_files.rb +1 -1
  70. data/lib/fastlane/plugin/wpmreleasetoolkit/actions/ios/ios_send_app_size_metrics.rb +170 -0
  71. data/lib/fastlane/plugin/wpmreleasetoolkit/actions/ios/ios_tag_build.rb +1 -1
  72. data/lib/fastlane/plugin/wpmreleasetoolkit/actions/ios/ios_update_metadata.rb +1 -1
  73. data/lib/fastlane/plugin/wpmreleasetoolkit/actions/ios/ios_update_metadata_source.rb +1 -1
  74. data/lib/fastlane/plugin/wpmreleasetoolkit/actions/ios/ios_update_release_notes.rb +1 -1
  75. data/lib/fastlane/plugin/wpmreleasetoolkit/actions/ios/ios_validate_ci_build.rb +1 -1
  76. data/lib/fastlane/plugin/wpmreleasetoolkit/helper/android/android_localize_helper.rb +72 -49
  77. data/lib/fastlane/plugin/wpmreleasetoolkit/helper/app_size_metrics_helper.rb +95 -0
  78. data/lib/fastlane/plugin/wpmreleasetoolkit/helper/git_helper.rb +10 -18
  79. data/lib/fastlane/plugin/wpmreleasetoolkit/helper/ios/ios_l10n_helper.rb +7 -3
  80. data/lib/fastlane/plugin/wpmreleasetoolkit/helper/ios/ios_l10n_linter_helper.rb +20 -34
  81. data/lib/fastlane/plugin/wpmreleasetoolkit/helper/ios/ios_strings_file_validation_helper.rb +107 -0
  82. data/lib/fastlane/plugin/wpmreleasetoolkit/helper/metadata_download_helper.rb +3 -5
  83. data/lib/fastlane/plugin/wpmreleasetoolkit/helper/promo_screenshots_helper.rb +2 -3
  84. data/lib/fastlane/plugin/wpmreleasetoolkit/helper/release_notes_helper.rb +4 -2
  85. data/lib/fastlane/plugin/wpmreleasetoolkit/helper/user_agent.rb +5 -0
  86. data/lib/fastlane/plugin/wpmreleasetoolkit/models/file_reference.rb +2 -4
  87. data/lib/fastlane/plugin/wpmreleasetoolkit/models/firebase_account.rb +19 -0
  88. data/lib/fastlane/plugin/wpmreleasetoolkit/models/firebase_device.rb +62 -0
  89. data/lib/fastlane/plugin/wpmreleasetoolkit/models/firebase_test_lab_result.rb +36 -0
  90. data/lib/fastlane/plugin/wpmreleasetoolkit/models/firebase_test_runner.rb +104 -0
  91. data/lib/fastlane/plugin/wpmreleasetoolkit/version.rb +1 -1
  92. data/lib/fastlane/plugin/wpmreleasetoolkit.rb +2 -2
  93. metadata +122 -83
@@ -49,7 +49,7 @@ module Fastlane
49
49
  end
50
50
 
51
51
  def self.authors
52
- ['loremattei']
52
+ ['Automattic']
53
53
  end
54
54
 
55
55
  def self.is_supported?(platform)
@@ -65,7 +65,7 @@ module Fastlane
65
65
  end
66
66
 
67
67
  def self.authors
68
- ['loremattei']
68
+ ['Automattic']
69
69
  end
70
70
 
71
71
  def self.is_supported?(platform)
@@ -52,7 +52,7 @@ module Fastlane
52
52
  end
53
53
 
54
54
  def self.authors
55
- ['loremattei']
55
+ ['Automattic']
56
56
  end
57
57
 
58
58
  def self.is_supported?(platform)
@@ -29,7 +29,7 @@ module Fastlane
29
29
  end
30
30
 
31
31
  def self.authors
32
- ['loremattei']
32
+ ['Automattic']
33
33
  end
34
34
 
35
35
  def self.is_supported?(platform)
@@ -41,7 +41,7 @@ module Fastlane
41
41
  end
42
42
 
43
43
  def self.authors
44
- ['loremattei']
44
+ ['Automattic']
45
45
  end
46
46
 
47
47
  def self.is_supported?(platform)
@@ -53,7 +53,7 @@ module Fastlane
53
53
  end
54
54
 
55
55
  def self.authors
56
- ['loremattei']
56
+ ['Automattic']
57
57
  end
58
58
 
59
59
  def self.is_supported?(platform)
@@ -36,7 +36,7 @@ module Fastlane
36
36
 
37
37
  def self.authors
38
38
  # So no one will ever forget your contribution to fastlane :) You are awesome btw!
39
- ['loremattei']
39
+ ['Automattic']
40
40
  end
41
41
 
42
42
  def self.is_supported?(platform)
@@ -49,7 +49,7 @@ module Fastlane
49
49
 
50
50
  def self.authors
51
51
  # So no one will ever forget your contribution to fastlane :) You are awesome btw!
52
- ['AliSoftware']
52
+ ['Automattic']
53
53
  end
54
54
 
55
55
  def self.is_supported?(platform)
@@ -110,7 +110,7 @@ module Fastlane
110
110
 
111
111
  def self.authors
112
112
  # So no one will ever forget your contribution to fastlane :) You are awesome btw!
113
- ['AliSoftware']
113
+ ['Automattic']
114
114
  end
115
115
 
116
116
  def self.is_supported?(platform)
@@ -67,7 +67,7 @@ module Fastlane
67
67
  end
68
68
 
69
69
  def self.authors
70
- ['loremattei']
70
+ ['Automattic']
71
71
  end
72
72
 
73
73
  def self.is_supported?(platform)
@@ -2,10 +2,20 @@ module Fastlane
2
2
  module Actions
3
3
  class IosLintLocalizationsAction < Action
4
4
  def self.run(params)
5
- violations = {}
5
+ violations = Hash.new([])
6
6
 
7
7
  loop do
8
- violations = self.run_linter(params)
8
+ # If we did `violations = self.run...` we'd lose the default value for missing key being `[]` that we set above with `Hash.new`.
9
+ # We want that default value so that we can use `+=` when adding the duplicate keys violations below.
10
+ violations = violations.merge(self.run_linter(params))
11
+
12
+ if params[:check_duplicate_keys]
13
+ find_duplicated_keys(params).each do |language, duplicates|
14
+ violations[language] += duplicates
15
+ end
16
+ end
17
+
18
+ report(violations: violations, base_lang: params[:base_lang])
9
19
  break unless !violations.empty? && params[:allow_retry] && UI.confirm(RETRY_MESSAGE)
10
20
  end
11
21
 
@@ -22,17 +32,38 @@ module Fastlane
22
32
  install_path: resolve_path(params[:install_path]),
23
33
  version: params[:version]
24
34
  )
25
- violations = helper.run(
35
+
36
+ helper.run(
26
37
  input_dir: resolve_path(params[:input_dir]),
27
38
  base_lang: params[:base_lang],
28
39
  only_langs: params[:only_langs]
29
40
  )
41
+ end
30
42
 
31
- violations.each do |lang, diff|
32
- UI.error "Inconsistencies found between '#{params[:base_lang]}' and '#{lang}':\n\n#{diff}\n"
43
+ def self.report(violations:, base_lang:)
44
+ violations.each do |lang, lang_violations|
45
+ UI.error "Inconsistencies found between '#{base_lang}' and '#{lang}':\n\n#{lang_violations.join("\n")}\n"
33
46
  end
47
+ end
34
48
 
35
- violations
49
+ def self.find_duplicated_keys(params)
50
+ duplicate_keys = {}
51
+
52
+ files_to_lint = Dir.chdir(params[:input_dir]) do
53
+ Dir.glob('*.lproj/Localizable.strings').map do |file|
54
+ {
55
+ language: File.basename(File.dirname(file), '.lproj'),
56
+ path: File.join(params[:input_dir], file)
57
+ }
58
+ end
59
+ end
60
+
61
+ files_to_lint.each do |file|
62
+ duplicates = Fastlane::Helper::Ios::StringsFileValidationHelper.find_duplicated_keys(file: file[:path])
63
+ duplicate_keys[file[:language]] = duplicates.map { |key, value| "`#{key}` was found at multiple lines: #{value.join(', ')}" } unless duplicates.empty?
64
+ end
65
+
66
+ duplicate_keys
36
67
  end
37
68
 
38
69
  RETRY_MESSAGE = <<~MSG
@@ -140,6 +171,14 @@ module Fastlane
140
171
  default_value: false,
141
172
  is_string: false # https://docs.fastlane.tools/advanced/actions/#boolean-parameters
142
173
  ),
174
+ FastlaneCore::ConfigItem.new(
175
+ key: :check_duplicate_keys,
176
+ env_name: 'FL_IOS_LINT_TRANSLATIONS_CHECK_DUPLICATE_KEYS',
177
+ description: 'Checks the input files for duplicate keys',
178
+ optional: true,
179
+ default_value: true,
180
+ is_string: false # https://docs.fastlane.tools/advanced/actions/#boolean-parameters
181
+ ),
143
182
  ]
144
183
  end
145
184
 
@@ -148,15 +187,15 @@ module Fastlane
148
187
  end
149
188
 
150
189
  def self.return_type
151
- :hash_of_strings
190
+ :hash
152
191
  end
153
192
 
154
193
  def self.return_value
155
- 'A hash, keyed by language code, whose values are the diff found for said language'
194
+ 'A hash, keyed by language code, whose values are arrays of violations found for that language'
156
195
  end
157
196
 
158
197
  def self.authors
159
- ['AliSoftware']
198
+ ['Automattic']
160
199
  end
161
200
 
162
201
  def self.is_supported?(platform)
@@ -32,7 +32,7 @@ module Fastlane
32
32
  end
33
33
 
34
34
  def self.authors
35
- ['loremattei']
35
+ ['Automattic']
36
36
  end
37
37
 
38
38
  def self.is_supported?(platform)
@@ -68,7 +68,7 @@ module Fastlane
68
68
  end
69
69
 
70
70
  def self.authors
71
- ['automattic']
71
+ ['Automattic']
72
72
  end
73
73
 
74
74
  def self.is_supported?(platform)
@@ -0,0 +1,170 @@
1
+ require 'plist'
2
+ require_relative '../../helper/app_size_metrics_helper'
3
+
4
+ module Fastlane
5
+ module Actions
6
+ class IosSendAppSizeMetricsAction < Action
7
+ # Keys used by the metrics payload
8
+ IPA_FILE_SIZE_KEY = 'File Size'.freeze # value from `File.size` of the Universal `.ipa`
9
+ IPA_DOWNLOAD_SIZE_KEY = 'Download Size'.freeze # value from `app-thinning.plist`
10
+ IPA_INSTALL_SIZE_KEY = 'Install Size'.freeze # value from `app-thinning.plist`
11
+
12
+ def self.run(params)
13
+ # Check input parameters
14
+ api_url = URI(params[:api_url])
15
+ api_token = params[:api_token]
16
+ if (api_token.nil? || api_token.empty?) && !api_url.is_a?(URI::File)
17
+ UI.user_error!('An API token is required when using an `api_url` with a scheme other than `file://`')
18
+ end
19
+
20
+ # Build the payload base
21
+ metrics_helper = Fastlane::Helper::AppSizeMetricsHelper.new(
22
+ Platform: 'iOS',
23
+ 'App Name': params[:app_name],
24
+ 'App Version': params[:app_version],
25
+ 'Build Type': params[:build_type],
26
+ Source: params[:source]
27
+ )
28
+ metrics_helper.add_metric(name: IPA_FILE_SIZE_KEY, value: File.size(params[:ipa_path]))
29
+
30
+ # Add app-thinning metrics to the payload if a `.plist` is provided
31
+ app_thinning_plist_path = params[:app_thinning_plist_path] || File.join(File.dirname(params[:ipa_path]), 'app-thinning.plist')
32
+ if File.exist?(app_thinning_plist_path)
33
+ plist = Plist.parse_xml(app_thinning_plist_path)
34
+ plist['variants'].each do |_key, variant|
35
+ variant_descriptors = variant['variantDescriptors'] || [{ 'device' => 'Universal' }]
36
+ variant_descriptors.each do |desc|
37
+ variant_metadata = { device: desc['device'], 'OS Version': desc['os-version'] }
38
+ metrics_helper.add_metric(name: IPA_DOWNLOAD_SIZE_KEY, value: variant['sizeCompressedApp'], metadata: variant_metadata)
39
+ metrics_helper.add_metric(name: IPA_INSTALL_SIZE_KEY, value: variant['sizeUncompressedApp'], metadata: variant_metadata)
40
+ end
41
+ end
42
+ end
43
+
44
+ # Send the payload
45
+ metrics_helper.send_metrics(
46
+ to: api_url,
47
+ api_token: api_token,
48
+ use_gzip: params[:use_gzip_content_encoding]
49
+ )
50
+ end
51
+
52
+ #####################################################
53
+ # @!group Documentation
54
+ #####################################################
55
+
56
+ def self.description
57
+ 'Send iOS app size metrics to our metrics server'
58
+ end
59
+
60
+ def self.details
61
+ <<~DETAILS
62
+ Send iOS app size metrics to our metrics server.
63
+
64
+ In order to get Xcode generate the `app-thinning.plist` file (during `gym` and the export of the `.xcarchive`), you need to:
65
+ (1) Use either `ad-hoc`, `enterprise` or `development` export method (in particular, won't work with `app-store`),
66
+ (2) Provide `thinning: '<thin-for-all-variants>'` as part of your `export_options` of `gym` (or in your `options.plist` file if you use raw `xcodebuild`)
67
+ See https://help.apple.com/xcode/mac/11.0/index.html#/devde46df08a
68
+
69
+ For builds exported with the `app-store` method, `xcodebuild` won't generate an `app-thinning.plist` file; so you will only be able to get
70
+ the Universal `.ipa` file size as a metric, but won't get the per-device, broken-down install and download sizes for each thinned variant.
71
+
72
+ See https://github.com/Automattic/apps-metrics for the API contract expected by the Metrics server you are expected to send those metrics to.
73
+
74
+ Tip: If you provide a `file://` URL for the `api_url`, the action will write the payload on disk at the specified path instead of sending
75
+ the data to a endpoint over network. This can be useful e.g. to inspect the payload and debug it, or to store the metrics data as CI artefacts.
76
+ DETAILS
77
+ end
78
+
79
+ def self.available_options
80
+ [
81
+ FastlaneCore::ConfigItem.new(
82
+ key: :api_url,
83
+ env_name: 'FL_IOS_SEND_APP_SIZE_METRICS_API_URL',
84
+ description: 'The endpoint API URL to publish metrics to. (Note: you can also point to a `file://` URL to write the payload to a file instead)',
85
+ type: String,
86
+ optional: false
87
+ ),
88
+ FastlaneCore::ConfigItem.new(
89
+ key: :api_token,
90
+ env_name: 'FL_IOS_SEND_APP_SIZE_METRICS_API_TOKEN',
91
+ description: 'The bearer token to call the API. Required, unless `api_url` is a `file://` URL',
92
+ type: String,
93
+ optional: true
94
+ ),
95
+ FastlaneCore::ConfigItem.new(
96
+ key: :use_gzip_content_encoding,
97
+ env_name: 'FL_IOS_SEND_APP_SIZE_METRICS_USE_GZIP_CONTENT_ENCODING',
98
+ description: 'Specify that we should use `Content-Encoding: gzip` and gzip the body when sending the request',
99
+ type: FastlaneCore::Boolean,
100
+ default_value: true
101
+ ),
102
+ FastlaneCore::ConfigItem.new(
103
+ key: :app_name,
104
+ env_name: 'FL_IOS_SEND_APP_SIZE_METRICS_APP_NAME',
105
+ description: 'The name of the app for which we are publishing metrics, to help with filtering and grouping',
106
+ type: String,
107
+ optional: false
108
+ ),
109
+ FastlaneCore::ConfigItem.new(
110
+ key: :app_version,
111
+ env_name: 'FL_IOS_SEND_APP_SIZE_METRICS_APP_VERSION',
112
+ description: 'The version of the app for which we are publishing metrics, to help with filtering and grouping',
113
+ type: String,
114
+ optional: false
115
+ ),
116
+ FastlaneCore::ConfigItem.new(
117
+ key: :build_type,
118
+ env_name: 'FL_IOS_SEND_APP_SIZE_METRICS_BUILD_TYPE',
119
+ description: 'The build configuration for which we are publishing metrics, to help with filtering and grouping. E.g. `Debug`, `Release`',
120
+ type: String,
121
+ optional: true
122
+ ),
123
+ FastlaneCore::ConfigItem.new(
124
+ key: :source,
125
+ env_name: 'FL_IOS_SEND_APP_SIZE_METRICS_SOURCE',
126
+ description: 'The type of event at the origin of that build, to help with filtering and grouping. E.g. `pr`, `beta`, `final-release`',
127
+ type: String,
128
+ optional: true
129
+ ),
130
+ FastlaneCore::ConfigItem.new(
131
+ key: :ipa_path,
132
+ env_name: 'FL_IOS_SEND_APP_SIZE_METRICS_IPA_PATH',
133
+ description: 'The path to the `.ipa` to extract size information from',
134
+ type: String,
135
+ optional: false,
136
+ default_value: Actions.lane_context[SharedValues::IPA_OUTPUT_PATH],
137
+ verify_block: proc do |value|
138
+ UI.user_error!('You must provide an path to an existing `.ipa` file') unless File.exist?(value)
139
+ end
140
+ ),
141
+ FastlaneCore::ConfigItem.new(
142
+ key: :app_thinning_plist_path,
143
+ env_name: 'FL_IOS_SEND_APP_SIZE_METRICS_APP_THINNING_PLIST_PATH',
144
+ description: 'The path to the `app-thinning.plist` file to extract thinning size information from. ' \
145
+ + 'By default, will try to use the `app-thinning.plist` file next to the `ipa_path`, if that file exists',
146
+ type: String,
147
+ optional: true,
148
+ default_value_dynamic: true
149
+ ),
150
+ ]
151
+ end
152
+
153
+ def self.return_type
154
+ :integer
155
+ end
156
+
157
+ def self.return_value
158
+ 'The HTTP return code from the call. Expect a 201 when new metrics were received successfully and entries created in the database'
159
+ end
160
+
161
+ def self.authors
162
+ ['Automattic']
163
+ end
164
+
165
+ def self.is_supported?(platform)
166
+ platform == :ios
167
+ end
168
+ end
169
+ end
170
+ end
@@ -33,7 +33,7 @@ module Fastlane
33
33
  end
34
34
 
35
35
  def self.authors
36
- ['loremattei']
36
+ ['Automattic']
37
37
  end
38
38
 
39
39
  def self.is_supported?(platform)
@@ -29,7 +29,7 @@ module Fastlane
29
29
  end
30
30
 
31
31
  def self.authors
32
- ['loremattei']
32
+ ['Automattic']
33
33
  end
34
34
 
35
35
  def self.is_supported?(platform)
@@ -70,7 +70,7 @@ module Fastlane
70
70
  end
71
71
 
72
72
  def self.authors
73
- ['loremattei']
73
+ ['Automattic']
74
74
  end
75
75
 
76
76
  def self.is_supported?(platform)
@@ -45,7 +45,7 @@ module Fastlane
45
45
  end
46
46
 
47
47
  def self.authors
48
- ['loremattei']
48
+ ['Automattic']
49
49
  end
50
50
 
51
51
  def self.is_supported?(platform)
@@ -35,7 +35,7 @@ module Fastlane
35
35
  end
36
36
 
37
37
  def self.authors
38
- ['loremattei']
38
+ ['Automattic']
39
39
  end
40
40
 
41
41
  def self.is_supported?(platform)
@@ -9,86 +9,99 @@ module Fastlane
9
9
  module Helper
10
10
  module Android
11
11
  module LocalizeHelper
12
- # Checks if string_line has the content_override flag set
13
- def self.skip_string_by_tag(string_line)
14
- skip = string_line.attr('content_override') == 'true' unless string_line.attr('content_override').nil?
12
+ LIB_SOURCE_XML_ATTR = 'a8c-src-lib'.freeze
13
+
14
+ # Checks if `string_node` has the `content_override` flag set
15
+ def self.skip_string_by_tag?(string_node)
16
+ skip = string_node.attr('content_override') == 'true' unless string_node.attr('content_override').nil?
15
17
  if skip
16
- puts " - Skipping #{string_line.attr('name')} string"
18
+ UI.message " - Skipping #{string_node.attr('name')} string"
17
19
  return true
18
20
  end
19
21
 
20
22
  return false
21
23
  end
22
24
 
23
- # Checks if string_name is in the excluesion list
24
- def self.skip_string_by_exclusion_list(library, string_name)
25
- return false unless library.key?(:exclusions)
25
+ # Checks if `string_name` is in the exclusion list
26
+ def self.skip_string_by_exclusion_list?(library, string_name)
27
+ return false if library[:exclusions].nil?
26
28
 
27
29
  skip = library[:exclusions].include?(string_name)
28
30
  if skip
29
- puts " - Skipping #{string_name} string"
31
+ UI.message " - Skipping #{string_name} string"
30
32
  return true
31
33
  end
32
34
  end
33
35
 
34
- # Merge string_line into main_string
35
- def self.merge_string(main_strings, library, string_line)
36
- string_name = string_line.attr('name')
37
- string_content = string_line.content
36
+ # Adds the appropriate XML attributes to an XML `<string>` node according to library configuration
37
+ def self.add_xml_attributes!(string_node, library)
38
+ if library[:add_ignore_attr] == true
39
+ existing_ignores = (string_node['tools:ignore'] || '').split(',')
40
+ existing_ignores.append('UnusedResources') unless existing_ignores.include?('UnusedResources')
41
+ string_node['tools:ignore'] = existing_ignores.join(',')
42
+ end
43
+ string_node[LIB_SOURCE_XML_ATTR] = library[:source_id] unless library[:source_id].nil?
44
+ end
45
+
46
+ # Merge a single `lib_string_node` XML node into the `main_strings_xml``
47
+ def self.merge_string_node(main_strings_xml, library, lib_string_node)
48
+ string_name = lib_string_node.attr('name')
49
+ string_content = lib_string_node.content
38
50
 
39
51
  # Skip strings in the exclusions list
40
- return :skipped if skip_string_by_exclusion_list(library, string_name)
52
+ return :skipped if skip_string_by_exclusion_list?(library, string_name)
41
53
 
42
54
  # Search for the string in the main file
43
55
  result = :added
44
- main_strings.xpath('//string').each do |this_string|
45
- if this_string.attr('name') == string_name
56
+ main_strings_xml.xpath('//string').each do |main_string_node|
57
+ if main_string_node.attr('name') == string_name
46
58
  # Skip if the string has the content_override tag
47
- return :skipped if skip_string_by_tag(this_string)
59
+ return :skipped if skip_string_by_tag?(main_string_node)
48
60
 
49
61
  # If nodes are equivalent, skip
50
- return :found if string_line =~ this_string
62
+ return :found if lib_string_node =~ main_string_node
51
63
 
52
64
  # The string needs an update
53
- result = :updated
54
- if this_string.attr('tools:ignore').nil?
55
- # It can be updated, so remove the current one and move ahead
56
- this_string.remove
57
- break
65
+ if main_string_node.attr('tools:ignore').nil?
66
+ # No `tools:ignore` attribute; completely replace existing main string node with lib's one
67
+ add_xml_attributes!(lib_string_node, library)
68
+ main_string_node.replace lib_string_node
58
69
  else
59
- # It has the tools:ignore flag, so update the content without touching the other attributes
60
- this_string.content = string_content
61
- return result
70
+ # Has the `tools:ignore` flag; update the content without touching the other existing attributes
71
+ add_xml_attributes!(main_string_node, library)
72
+ main_string_node.content = string_content
62
73
  end
74
+ return :updated
63
75
  end
64
76
  end
65
77
 
66
78
  # String not found, or removed because needing update and not in the exclusion list: add to the main file
67
- main_strings.xpath('//string').last().add_next_sibling("\n#{' ' * 4}#{string_line.to_xml().strip}")
79
+ add_xml_attributes!(lib_string_node, library)
80
+ main_strings_xml.xpath('//string').last().add_next_sibling("\n#{' ' * 4}#{lib_string_node.to_xml().strip}")
68
81
  return result
69
82
  end
70
83
 
71
- # Verify a string
72
- def self.verify_string(main_strings, library, string_line)
73
- string_name = string_line.attr('name')
74
- string_content = string_line.content
84
+ # Verify a string node from a library has properly been merged into the main one
85
+ def self.verify_string(main_strings_xml, library, lib_string_node)
86
+ string_name = lib_string_node.attr('name')
87
+ string_content = lib_string_node.content
75
88
 
76
89
  # Skip strings in the exclusions list
77
- return if skip_string_by_exclusion_list(library, string_name)
90
+ return if skip_string_by_exclusion_list?(library, string_name)
78
91
 
79
92
  # Search for the string in the main file
80
- main_strings.xpath('//string').each do |this_string|
81
- if this_string.attr('name') == string_name
93
+ main_strings_xml.xpath('//string').each do |main_string_node|
94
+ if main_string_node.attr('name') == string_name
82
95
  # Skip if the string has the content_override tag
83
- return if skip_string_by_tag(this_string)
96
+ return if skip_string_by_tag?(main_string_node)
84
97
 
85
- # Update if needed
86
- UI.user_error!("String #{string_name} [#{string_content}] has been updated in the main file but not in the library #{library[:library]}.") if this_string.content != string_content
98
+ # Check if up-to-date
99
+ UI.user_error!("String #{string_name} [#{string_content}] has been updated in the main file but not in the library #{library[:library]}.") if main_string_node.content != string_content
87
100
  return
88
101
  end
89
102
  end
90
103
 
91
- # String not found and not in the exclusion list:
104
+ # String not found and not in the exclusion list
92
105
  UI.user_error!("String #{string_name} [#{string_content}] was found in library #{library[:library]} but not in the main file.")
93
106
  end
94
107
 
@@ -98,27 +111,33 @@ module Fastlane
98
111
  # @param [Hash] library Hash describing the library to merge. The Hash should contain the following keys:
99
112
  # - `:library`: The human readable name of the library, used to display in console messages
100
113
  # - `:strings_path`: The path to the strings.xml file of the library to merge into the main one
101
- # - `:exclusions`: An array of strings keys to exclude during merge. Any of those keys from the library's `strings.xml` will be skipped and won't be merged into the main one.
114
+ # - `:exclusions`: An array of strings keys to exclude during merge. Any of those keys from the
115
+ # library's `strings.xml` will be skipped and won't be merged into the main one.
116
+ # - `:source_id`: An optional `String` which will be added as the `a8c-src-lib` XML attribute
117
+ # to strings coming from this library, to help identify their source in the merged file.
118
+ # - `:add_ignore_attr`: If set to `true`, will add `tools:ignore="UnusedResources"` to merged strings.
119
+ #
102
120
  # @return [Boolean] True if at least one string from the library has been added to (or has updated) the main strings file.
121
+ #
103
122
  def self.merge_lib(main, library)
104
123
  UI.message("Merging #{library[:library]} strings into #{main}")
105
- main_strings = File.open(main) { |f| Nokogiri::XML(f, nil, Encoding::UTF_8.to_s) }
106
- lib_strings = File.open(library[:strings_path]) { |f| Nokogiri::XML(f, nil, Encoding::UTF_8.to_s) }
124
+ main_strings_xml = File.open(main) { |f| Nokogiri::XML(f, nil, Encoding::UTF_8.to_s) }
125
+ lib_strings_xml = File.open(library[:strings_path]) { |f| Nokogiri::XML(f, nil, Encoding::UTF_8.to_s) }
107
126
 
108
127
  updated_count = 0
109
128
  untouched_count = 0
110
129
  added_count = 0
111
130
  skipped_count = 0
112
- lib_strings.xpath('//string').each do |string_line|
113
- res = merge_string(main_strings, library, string_line)
131
+ lib_strings_xml.xpath('//string').each do |string_node|
132
+ res = merge_string_node(main_strings_xml, library, string_node)
114
133
  case res
115
134
  when :updated
116
- puts "#{string_line.attr('name')} updated."
135
+ UI.verbose "#{string_node.attr('name')} updated."
117
136
  updated_count = updated_count + 1
118
137
  when :found
119
138
  untouched_count = untouched_count + 1
120
139
  when :added
121
- puts "#{string_line.attr('name')} added."
140
+ UI.verbose "#{string_node.attr('name')} added."
122
141
  added_count = added_count + 1
123
142
  when :skipped
124
143
  skipped_count = skipped_count + 1
@@ -128,7 +147,7 @@ module Fastlane
128
147
  end
129
148
 
130
149
  File.open(main, 'w:UTF-8') do |f|
131
- f.write(main_strings.to_xml(indent: 4))
150
+ f.write(main_strings_xml.to_xml(indent: 4))
132
151
  end
133
152
 
134
153
  UI.message("Done (#{added_count} added, #{updated_count} updated, #{untouched_count} untouched, #{skipped_count} skipped).")
@@ -144,8 +163,8 @@ module Fastlane
144
163
 
145
164
  diff_string = diff_string.slice(0..(end_index - 1))
146
165
 
147
- lib_strings.xpath('//string').each do |string_line|
148
- res = verify_string(main_strings, library, string_line) if string_line.attr('name') == diff_string
166
+ lib_strings.xpath('//string').each do |string_node|
167
+ res = verify_string(main_strings, library, string_node) if string_node.attr('name') == diff_string
149
168
  end
150
169
  end
151
170
  end
@@ -263,9 +282,13 @@ module Fastlane
263
282
  #
264
283
  def self.download_glotpress_export_file(project_url:, locale:, filters:)
265
284
  query_params = filters.transform_keys { |k| "filters[#{k}]" }.merge(format: 'android')
266
- uri = URI.parse("#{project_url.chomp('/')}/#{locale}/default/export-translations?#{URI.encode_www_form(query_params)}")
285
+ uri = URI.parse("#{project_url.chomp('/')}/#{locale}/default/export-translations/?#{URI.encode_www_form(query_params)}")
286
+
287
+ # Set an unambiguous User Agent so GlotPress won't rate-limit us
288
+ options = { 'User-Agent' => Wpmreleasetoolkit::USER_AGENT }
289
+
267
290
  begin
268
- uri.open { |f| Nokogiri::XML(f.read.gsub("\t", ' '), nil, Encoding::UTF_8.to_s) }
291
+ uri.open(options) { |f| Nokogiri::XML(f.read.gsub("\t", ' '), nil, Encoding::UTF_8.to_s) }
269
292
  rescue StandardError => e
270
293
  UI.error "Error downloading #{locale} - #{e.message}"
271
294
  return nil