fastlane-plugin-wpmreleasetoolkit 4.2.0 → 5.2.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (87) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +15 -0
  3. data/lib/fastlane/plugin/wpmreleasetoolkit/actions/android/an_localize_libs_action.rb +1 -1
  4. data/lib/fastlane/plugin/wpmreleasetoolkit/actions/android/an_update_metadata_source_action.rb +1 -1
  5. data/lib/fastlane/plugin/wpmreleasetoolkit/actions/android/an_validate_lib_strings_action.rb +1 -1
  6. data/lib/fastlane/plugin/wpmreleasetoolkit/actions/android/android_betabuild_prechecks.rb +1 -1
  7. data/lib/fastlane/plugin/wpmreleasetoolkit/actions/android/android_build_prechecks.rb +1 -1
  8. data/lib/fastlane/plugin/wpmreleasetoolkit/actions/android/android_build_preflight.rb +1 -1
  9. data/lib/fastlane/plugin/wpmreleasetoolkit/actions/android/android_bump_version_beta.rb +1 -1
  10. data/lib/fastlane/plugin/wpmreleasetoolkit/actions/android/android_bump_version_final_release.rb +1 -1
  11. data/lib/fastlane/plugin/wpmreleasetoolkit/actions/android/android_bump_version_hotfix.rb +1 -1
  12. data/lib/fastlane/plugin/wpmreleasetoolkit/actions/android/android_bump_version_release.rb +1 -1
  13. data/lib/fastlane/plugin/wpmreleasetoolkit/actions/android/android_codefreeze_prechecks.rb +1 -1
  14. data/lib/fastlane/plugin/wpmreleasetoolkit/actions/android/android_completecodefreeze_prechecks.rb +1 -1
  15. data/lib/fastlane/plugin/wpmreleasetoolkit/actions/android/android_create_xml_release_notes.rb +1 -1
  16. data/lib/fastlane/plugin/wpmreleasetoolkit/actions/android/android_current_branch_is_hotfix.rb +1 -1
  17. data/lib/fastlane/plugin/wpmreleasetoolkit/actions/android/android_download_file_by_version.rb +1 -1
  18. data/lib/fastlane/plugin/wpmreleasetoolkit/actions/android/android_download_translations_action.rb +1 -1
  19. data/lib/fastlane/plugin/wpmreleasetoolkit/actions/android/android_finalize_prechecks.rb +1 -1
  20. data/lib/fastlane/plugin/wpmreleasetoolkit/actions/android/android_firebase_test.rb +187 -0
  21. data/lib/fastlane/plugin/wpmreleasetoolkit/actions/android/android_get_alpha_version.rb +1 -1
  22. data/lib/fastlane/plugin/wpmreleasetoolkit/actions/android/android_get_app_version.rb +1 -1
  23. data/lib/fastlane/plugin/wpmreleasetoolkit/actions/android/android_get_release_version.rb +1 -1
  24. data/lib/fastlane/plugin/wpmreleasetoolkit/actions/android/android_hotfix_prechecks.rb +1 -1
  25. data/lib/fastlane/plugin/wpmreleasetoolkit/actions/android/android_send_app_size_metrics.rb +279 -0
  26. data/lib/fastlane/plugin/wpmreleasetoolkit/actions/android/android_tag_build.rb +1 -1
  27. data/lib/fastlane/plugin/wpmreleasetoolkit/actions/android/android_update_release_notes.rb +1 -1
  28. data/lib/fastlane/plugin/wpmreleasetoolkit/actions/common/buildkite_trigger_build_action.rb +15 -6
  29. data/lib/fastlane/plugin/wpmreleasetoolkit/actions/common/check_for_toolkit_updates_action.rb +1 -1
  30. data/lib/fastlane/plugin/wpmreleasetoolkit/actions/common/check_translation_progress.rb +1 -1
  31. data/lib/fastlane/plugin/wpmreleasetoolkit/actions/common/circleci_trigger_job_action.rb +1 -1
  32. data/lib/fastlane/plugin/wpmreleasetoolkit/actions/common/close_milestone_action.rb +1 -1
  33. data/lib/fastlane/plugin/wpmreleasetoolkit/actions/common/create_new_milestone_action.rb +1 -1
  34. data/lib/fastlane/plugin/wpmreleasetoolkit/actions/common/create_release_action.rb +1 -1
  35. data/lib/fastlane/plugin/wpmreleasetoolkit/actions/common/extract_release_notes_for_version_action.rb +1 -1
  36. data/lib/fastlane/plugin/wpmreleasetoolkit/actions/common/firebase_login.rb +44 -0
  37. data/lib/fastlane/plugin/wpmreleasetoolkit/actions/common/get_prs_list_action.rb +1 -1
  38. data/lib/fastlane/plugin/wpmreleasetoolkit/actions/common/gp_downloadmetadata_action.rb +1 -1
  39. data/lib/fastlane/plugin/wpmreleasetoolkit/actions/common/gp_update_metadata_source.rb +1 -1
  40. data/lib/fastlane/plugin/wpmreleasetoolkit/actions/common/promo_screenshots_action.rb +1 -1
  41. data/lib/fastlane/plugin/wpmreleasetoolkit/actions/common/removebranchprotection_action.rb +1 -1
  42. data/lib/fastlane/plugin/wpmreleasetoolkit/actions/common/setbranchprotection_action.rb +1 -1
  43. data/lib/fastlane/plugin/wpmreleasetoolkit/actions/common/setfrozentag_action.rb +1 -1
  44. data/lib/fastlane/plugin/wpmreleasetoolkit/actions/configure/configure_add_files_to_copy_action.rb +1 -1
  45. data/lib/fastlane/plugin/wpmreleasetoolkit/actions/configure/configure_apply_action.rb +1 -1
  46. data/lib/fastlane/plugin/wpmreleasetoolkit/actions/configure/configure_download_action.rb +1 -1
  47. data/lib/fastlane/plugin/wpmreleasetoolkit/actions/configure/configure_setup_action.rb +1 -1
  48. data/lib/fastlane/plugin/wpmreleasetoolkit/actions/configure/configure_update_action.rb +1 -1
  49. data/lib/fastlane/plugin/wpmreleasetoolkit/actions/configure/configure_validate_action.rb +1 -1
  50. data/lib/fastlane/plugin/wpmreleasetoolkit/actions/ios/add_development_certificates_to_provisioning_profiles.rb +1 -1
  51. data/lib/fastlane/plugin/wpmreleasetoolkit/actions/ios/add_devices_to_provisioning_profiles.rb +1 -1
  52. data/lib/fastlane/plugin/wpmreleasetoolkit/actions/ios/ios_betabuild_prechecks.rb +1 -1
  53. data/lib/fastlane/plugin/wpmreleasetoolkit/actions/ios/ios_build_prechecks.rb +1 -1
  54. data/lib/fastlane/plugin/wpmreleasetoolkit/actions/ios/ios_build_preflight.rb +1 -1
  55. data/lib/fastlane/plugin/wpmreleasetoolkit/actions/ios/ios_bump_version_beta.rb +1 -1
  56. data/lib/fastlane/plugin/wpmreleasetoolkit/actions/ios/ios_bump_version_hotfix.rb +1 -1
  57. data/lib/fastlane/plugin/wpmreleasetoolkit/actions/ios/ios_bump_version_release.rb +1 -1
  58. data/lib/fastlane/plugin/wpmreleasetoolkit/actions/ios/ios_check_beta_deps.rb +1 -1
  59. data/lib/fastlane/plugin/wpmreleasetoolkit/actions/ios/ios_clear_intermediate_tags.rb +1 -1
  60. data/lib/fastlane/plugin/wpmreleasetoolkit/actions/ios/ios_codefreeze_prechecks.rb +1 -1
  61. data/lib/fastlane/plugin/wpmreleasetoolkit/actions/ios/ios_completecodefreeze_prechecks.rb +1 -1
  62. data/lib/fastlane/plugin/wpmreleasetoolkit/actions/ios/ios_current_branch_is_hotfix.rb +1 -1
  63. data/lib/fastlane/plugin/wpmreleasetoolkit/actions/ios/ios_final_tag.rb +1 -1
  64. data/lib/fastlane/plugin/wpmreleasetoolkit/actions/ios/ios_finalize_prechecks.rb +1 -1
  65. data/lib/fastlane/plugin/wpmreleasetoolkit/actions/ios/ios_get_app_version.rb +1 -1
  66. data/lib/fastlane/plugin/wpmreleasetoolkit/actions/ios/ios_get_build_version.rb +1 -1
  67. data/lib/fastlane/plugin/wpmreleasetoolkit/actions/ios/ios_get_store_app_sizes.rb +1 -1
  68. data/lib/fastlane/plugin/wpmreleasetoolkit/actions/ios/ios_hotfix_prechecks.rb +1 -1
  69. data/lib/fastlane/plugin/wpmreleasetoolkit/actions/ios/ios_localize_project.rb +1 -1
  70. data/lib/fastlane/plugin/wpmreleasetoolkit/actions/ios/ios_merge_strings_files.rb +1 -1
  71. data/lib/fastlane/plugin/wpmreleasetoolkit/actions/ios/ios_send_app_size_metrics.rb +170 -0
  72. data/lib/fastlane/plugin/wpmreleasetoolkit/actions/ios/ios_tag_build.rb +1 -1
  73. data/lib/fastlane/plugin/wpmreleasetoolkit/actions/ios/ios_update_metadata.rb +1 -1
  74. data/lib/fastlane/plugin/wpmreleasetoolkit/actions/ios/ios_update_metadata_source.rb +1 -1
  75. data/lib/fastlane/plugin/wpmreleasetoolkit/actions/ios/ios_update_release_notes.rb +1 -1
  76. data/lib/fastlane/plugin/wpmreleasetoolkit/actions/ios/ios_validate_ci_build.rb +1 -1
  77. data/lib/fastlane/plugin/wpmreleasetoolkit/helper/android/android_localize_helper.rb +2 -2
  78. data/lib/fastlane/plugin/wpmreleasetoolkit/helper/app_size_metrics_helper.rb +95 -0
  79. data/lib/fastlane/plugin/wpmreleasetoolkit/helper/github_helper.rb +1 -1
  80. data/lib/fastlane/plugin/wpmreleasetoolkit/helper/metadata_download_helper.rb +4 -4
  81. data/lib/fastlane/plugin/wpmreleasetoolkit/models/firebase_account.rb +19 -0
  82. data/lib/fastlane/plugin/wpmreleasetoolkit/models/firebase_device.rb +62 -0
  83. data/lib/fastlane/plugin/wpmreleasetoolkit/models/firebase_test_lab_result.rb +36 -0
  84. data/lib/fastlane/plugin/wpmreleasetoolkit/models/firebase_test_runner.rb +104 -0
  85. data/lib/fastlane/plugin/wpmreleasetoolkit/version.rb +1 -1
  86. data/lib/fastlane/plugin/wpmreleasetoolkit.rb +2 -2
  87. metadata +118 -81
@@ -71,7 +71,7 @@ module Fastlane
71
71
  end
72
72
 
73
73
  def self.authors
74
- ['loremattei']
74
+ ['Automattic']
75
75
  end
76
76
 
77
77
  def self.is_supported?(platform)
@@ -80,7 +80,7 @@ module Fastlane
80
80
  end
81
81
 
82
82
  def self.authors
83
- ['loremattei']
83
+ ['Automattic']
84
84
  end
85
85
 
86
86
  def self.is_supported?(platform)
@@ -51,7 +51,7 @@ module Fastlane
51
51
  end
52
52
 
53
53
  def self.authors
54
- ['loremattei']
54
+ ['Automattic']
55
55
  end
56
56
 
57
57
  def self.is_supported?(platform)
@@ -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)
@@ -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)
@@ -212,8 +212,8 @@ module Fastlane
212
212
  def self.create_available_languages_file(res_dir:, locale_codes:)
213
213
  doc = Nokogiri::XML::Builder.new(encoding: 'UTF-8') do |xml|
214
214
  xml.comment('Warning: Auto-generated file, do not edit.')
215
- xml.resources do
216
- xml.send(:'string-array', name: 'available_languages', translatable: 'false') do
215
+ xml.resources('xmlns:tools': 'http://schemas.android.com/tools') do
216
+ xml.send(:'string-array', name: 'available_languages', translatable: 'false', 'tools:ignore': 'InconsistentArrays') do
217
217
  locale_codes.each { |code| xml.item(code.gsub('-r', '_')) }
218
218
  end
219
219
  end
@@ -0,0 +1,95 @@
1
+ require 'json'
2
+ require 'net/http'
3
+ require 'zlib'
4
+
5
+ module Fastlane
6
+ module Helper
7
+ # A helper class to build an App Size Metrics payload and send it to a server (or write it to disk)
8
+ #
9
+ # The payload generated (and sent) by this helper conforms to the API for grouped metrics described in
10
+ # https://github.com/Automattic/apps-metrics
11
+ #
12
+ class AppSizeMetricsHelper
13
+ # @param [Hash] metadata Metadata common to all the metrics. Can be any arbitrary set of key/value pairs.
14
+ #
15
+ def initialize(metadata = {})
16
+ self.metadata = metadata
17
+ @metrics = []
18
+ end
19
+
20
+ # Sets the metadata common to the whole group of metrics in the payload being built by this helper instance
21
+ #
22
+ # @param [Hash] hash The metadata common to all the metrics of the payload built by that helper instance. Can be any arbitrary set of key/value pairs
23
+ #
24
+ def metadata=(hash)
25
+ @metadata = (hash.compact || {}).map { |key, value| { name: key.to_s, value: value } }
26
+ end
27
+
28
+ # Adds a single metric to the group of metrics
29
+ #
30
+ # @param [String] name The metric name
31
+ # @param [Integer] value The metric value
32
+ # @param [Hash] metadata The arbitrary dictionary of metadata to associate to that metric entry
33
+ #
34
+ def add_metric(name:, value:, metadata: nil)
35
+ metric = { name: name, value: value }
36
+ metadata = metadata&.compact || {} # Remove nil values if any (and use empty Hash if nil was provided)
37
+ metric[:meta] = metadata.map { |meta_key, meta_value| { name: meta_key.to_s, value: meta_value } } unless metadata.empty?
38
+ @metrics.append(metric)
39
+ end
40
+
41
+ def to_h
42
+ {
43
+ meta: @metadata,
44
+ metrics: @metrics
45
+ }
46
+ end
47
+
48
+ # Send the metrics to the given App Metrics endpoint.
49
+ #
50
+ # Must conform to the API described in https://github.com/Automattic/apps-metrics/wiki/Queue-Group-of-Metrics
51
+ #
52
+ # @param [String,URI] to The URL of the App Metrics service, or a `file://` URL to write the payload to disk
53
+ # @param [String] api_token The API bearer token to use to register the metric.
54
+ # @return [Integer] the HTTP response code
55
+ #
56
+ def send_metrics(to:, api_token:, use_gzip: true)
57
+ uri = URI(to)
58
+ json_payload = use_gzip ? Zlib.gzip(to_h.to_json) : to_h.to_json
59
+
60
+ # Allow using a `file:` URI for debugging
61
+ if uri.is_a?(URI::File)
62
+ UI.message("Writing metrics payload to file #{uri.path} (instead of sending it to a remote API endpoint)")
63
+ File.write(uri.path, json_payload)
64
+ return 201 # To make it easy at call site to check for pseudo-status code 201 even in non-HTTP cases
65
+ end
66
+
67
+ UI.message("Sending metrics to #{uri}...")
68
+ headers = {
69
+ Authorization: "Bearer #{api_token}",
70
+ Accept: 'application/json',
71
+ 'Content-Type': 'application/json'
72
+ }
73
+ headers[:'Content-Encoding'] = 'gzip' if use_gzip
74
+
75
+ request = Net::HTTP::Post.new(uri, headers)
76
+ request.body = json_payload
77
+
78
+ response = Net::HTTP.start(uri.hostname, uri.port, use_ssl: uri.scheme == 'https') do |http|
79
+ http.request(request)
80
+ end
81
+
82
+ if response.is_a?(Net::HTTPSuccess)
83
+ UI.success("Metrics sent. (#{response.code} #{response.message})")
84
+ else
85
+ UI.error("Metrics failed to send. Received: #{response.code} #{response.message}")
86
+ UI.message("Request was #{request.method} to #{request.uri}")
87
+ UI.message("Request headers were: #{headers}")
88
+ UI.message("Request body was #{request.body.length} bytes")
89
+ UI.message("Response was #{response.body}")
90
+ end
91
+ response.code.to_i
92
+ end
93
+ end
94
+ end
95
+ end
@@ -70,7 +70,7 @@ module Fastlane
70
70
  else
71
71
  begin
72
72
  last_vcomps = last_stone[:title].split[0].split('.')
73
- last_stone = mile if mile_vcomps[0] > last_vcomps[0] || mile_vcomps[1] > last_vcomps[1]
73
+ last_stone = mile if Integer(mile_vcomps[0]) > Integer(last_vcomps[0]) || Integer(mile_vcomps[1]) > Integer(last_vcomps[1])
74
74
  rescue StandardError
75
75
  puts 'Found invalid milestone'
76
76
  end
@@ -40,7 +40,7 @@ module Fastlane
40
40
  target_files.each do |file|
41
41
  if file[0].to_s == key
42
42
  data = file[1]
43
- msg = is_source ? source : d[1]
43
+ msg = is_source ? source : d[1].first || '' # In the JSON, each Hash value is an array, with zero or one entry
44
44
  update_key(target_locale, key, file, data, msg)
45
45
  end
46
46
  end
@@ -58,7 +58,7 @@ module Fastlane
58
58
  if file[0].to_s == key
59
59
  puts "Alternate: #{key}"
60
60
  data = file[1]
61
- msg = is_source ? source : d[1]
61
+ msg = is_source ? source : d[1].first || '' # In the JSON, each Hash value is an array, with zero or one entry
62
62
  update_key(target_locale, key, file, data, msg)
63
63
  end
64
64
  end
@@ -66,13 +66,13 @@ module Fastlane
66
66
  end
67
67
 
68
68
  def update_key(target_locale, key, file, data, msg)
69
- message_len = msg.to_s.length - 4 # Don't count JSON delimiters.
69
+ message_len = msg.length
70
70
  if (data.key?(:max_size)) && (data[:max_size] != 0) && ((message_len) > data[:max_size])
71
71
  if data.key?(:alternate_key)
72
72
  UI.message("#{target_locale} translation for #{key} exceeds maximum length (#{message_len}). Switching to the alternate translation.")
73
73
  @alternates[data[:alternate_key]] = { desc: data[:desc], max_size: 0 }
74
74
  else
75
- UI.message("Rejecting #{target_locale} traslation for #{key}: translation length: #{message_len} - max allowed length: #{data[:max_size]}")
75
+ UI.message("Rejecting #{target_locale} translation for #{key}: translation length: #{message_len} - max allowed length: #{data[:max_size]}")
76
76
  end
77
77
  else
78
78
  save_metadata(target_locale, file[1][:desc], msg)
@@ -0,0 +1,19 @@
1
+ module Fastlane
2
+ class FirebaseAccount
3
+ def self.activate_service_account_with_key_file(key_file_path)
4
+ Fastlane::Actions.sh('gcloud', 'auth', 'activate-service-account', '--key-file', key_file_path)
5
+ end
6
+
7
+ def self.authenticated?
8
+ auth_status = JSON.parse(auth_status_data)
9
+ auth_status.any? do |account|
10
+ account['status'] == 'ACTIVE'
11
+ end
12
+ end
13
+
14
+ # Lookup the current account authentication status
15
+ def self.auth_status_data
16
+ Fastlane::Actions.sh('gcloud', 'auth', 'list', '--format', 'json', log: false)
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,62 @@
1
+ module Fastlane
2
+ class FirebaseDevice
3
+ attr_reader :model, :version, :locale, :orientation
4
+
5
+ def initialize(model:, version:, orientation:, locale: 'en')
6
+ raise 'Invalid Model' unless FirebaseDevice.valid_model_names.include? model
7
+ raise 'Invalid Version' unless FirebaseDevice.valid_version_numbers.include? version
8
+ raise 'Invalid Locale' unless FirebaseDevice.valid_locales.include? locale
9
+ raise 'Invalid Orientation' unless FirebaseDevice.valid_orientations.include? orientation
10
+
11
+ @model = model
12
+ @version = version
13
+ @locale = locale
14
+ @orientation = orientation
15
+ end
16
+
17
+ def to_s
18
+ "model=#{@model},version=#{@version},locale=#{@locale},orientation=#{@orientation}"
19
+ end
20
+
21
+ class << self
22
+ @locale_data = nil
23
+ @model_data = nil
24
+ @version_data = nil
25
+
26
+ def valid_model_names
27
+ JSON.parse(model_data).map { |device| device['codename'] }
28
+ end
29
+
30
+ def valid_version_numbers
31
+ JSON.parse(version_data).map { |version| version['apiLevel'].to_i }
32
+ end
33
+
34
+ def valid_locales
35
+ JSON.parse(locale_data).map { |locale| locale['id'] }
36
+ end
37
+
38
+ def valid_orientations
39
+ %w[portrait landscape]
40
+ end
41
+
42
+ def locale_data
43
+ FirebaseDevice.verify_logged_in!
44
+ @locale_data ||= Fastlane::Actions.sh('gcloud', 'firebase', 'test', 'android', 'locales', 'list', '--format="json"', log: false)
45
+ end
46
+
47
+ def model_data
48
+ FirebaseDevice.verify_logged_in!
49
+ @model_data ||= Fastlane::Actions.sh('gcloud', 'firebase', 'test', 'android', 'models', 'list', '--format="json"', log: false)
50
+ end
51
+
52
+ def version_data
53
+ FirebaseDevice.verify_logged_in!
54
+ @version_data ||= Fastlane::Actions.sh('gcloud', 'firebase', 'test', 'android', 'versions', 'list', '--format="json"', log: false)
55
+ end
56
+
57
+ def verify_logged_in!
58
+ UI.user_error!('You must call `firebase_login` before creating a FirebaseDevice object') unless FirebaseAccount.authenticated?
59
+ end
60
+ end
61
+ end
62
+ end
@@ -0,0 +1,36 @@
1
+ module Fastlane
2
+ class FirebaseTestLabResult
3
+ def initialize(log_file_path:)
4
+ raise "No log file found at path #{log_file_path}" unless File.file? log_file_path
5
+
6
+ @path = log_file_path
7
+ end
8
+
9
+ # Scan the log file to for indications that no test cases failed
10
+ def success?
11
+ File.readlines(@path).any? { |line| line.include?('Passed') && line.include?('test cases passed') }
12
+ end
13
+
14
+ # Parse the log for the "More details are available..." URL
15
+ def more_details_url
16
+ File.readlines(@path)
17
+ .flat_map { |line| URI.extract(line) }
18
+ .find { |url| URI(url).host == 'console.firebase.google.com' && url.include?('/matrices/') }
19
+ end
20
+
21
+ # Parse the log for the Google Cloud Storage Bucket URL
22
+ def raw_results_paths
23
+ uri = File.readlines(@path)
24
+ .flat_map { |line| URI.extract(line) }
25
+ .map { |string| URI(string) }
26
+ .find { |u| u.scheme == 'gs' }
27
+
28
+ return nil if uri.nil?
29
+
30
+ return {
31
+ bucket: uri.host,
32
+ prefix: uri.path.delete_prefix('/').chomp('/')
33
+ }
34
+ end
35
+ end
36
+ end