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.
- checksums.yaml +4 -4
- data/README.md +15 -0
- data/lib/fastlane/plugin/wpmreleasetoolkit/actions/android/an_localize_libs_action.rb +1 -1
- data/lib/fastlane/plugin/wpmreleasetoolkit/actions/android/an_update_metadata_source_action.rb +1 -1
- data/lib/fastlane/plugin/wpmreleasetoolkit/actions/android/an_validate_lib_strings_action.rb +1 -1
- data/lib/fastlane/plugin/wpmreleasetoolkit/actions/android/android_betabuild_prechecks.rb +1 -1
- data/lib/fastlane/plugin/wpmreleasetoolkit/actions/android/android_build_prechecks.rb +1 -1
- data/lib/fastlane/plugin/wpmreleasetoolkit/actions/android/android_build_preflight.rb +1 -1
- data/lib/fastlane/plugin/wpmreleasetoolkit/actions/android/android_bump_version_beta.rb +1 -1
- data/lib/fastlane/plugin/wpmreleasetoolkit/actions/android/android_bump_version_final_release.rb +1 -1
- data/lib/fastlane/plugin/wpmreleasetoolkit/actions/android/android_bump_version_hotfix.rb +1 -1
- data/lib/fastlane/plugin/wpmreleasetoolkit/actions/android/android_bump_version_release.rb +1 -1
- data/lib/fastlane/plugin/wpmreleasetoolkit/actions/android/android_codefreeze_prechecks.rb +1 -1
- data/lib/fastlane/plugin/wpmreleasetoolkit/actions/android/android_completecodefreeze_prechecks.rb +1 -1
- data/lib/fastlane/plugin/wpmreleasetoolkit/actions/android/android_create_xml_release_notes.rb +1 -1
- data/lib/fastlane/plugin/wpmreleasetoolkit/actions/android/android_current_branch_is_hotfix.rb +1 -1
- data/lib/fastlane/plugin/wpmreleasetoolkit/actions/android/android_download_file_by_version.rb +1 -1
- data/lib/fastlane/plugin/wpmreleasetoolkit/actions/android/android_download_translations_action.rb +1 -1
- data/lib/fastlane/plugin/wpmreleasetoolkit/actions/android/android_finalize_prechecks.rb +1 -1
- data/lib/fastlane/plugin/wpmreleasetoolkit/actions/android/android_firebase_test.rb +187 -0
- data/lib/fastlane/plugin/wpmreleasetoolkit/actions/android/android_get_alpha_version.rb +1 -1
- data/lib/fastlane/plugin/wpmreleasetoolkit/actions/android/android_get_app_version.rb +1 -1
- data/lib/fastlane/plugin/wpmreleasetoolkit/actions/android/android_get_release_version.rb +1 -1
- data/lib/fastlane/plugin/wpmreleasetoolkit/actions/android/android_hotfix_prechecks.rb +1 -1
- data/lib/fastlane/plugin/wpmreleasetoolkit/actions/android/android_send_app_size_metrics.rb +279 -0
- data/lib/fastlane/plugin/wpmreleasetoolkit/actions/android/android_tag_build.rb +1 -1
- data/lib/fastlane/plugin/wpmreleasetoolkit/actions/android/android_update_release_notes.rb +1 -1
- data/lib/fastlane/plugin/wpmreleasetoolkit/actions/common/buildkite_trigger_build_action.rb +15 -6
- data/lib/fastlane/plugin/wpmreleasetoolkit/actions/common/check_for_toolkit_updates_action.rb +1 -1
- data/lib/fastlane/plugin/wpmreleasetoolkit/actions/common/check_translation_progress.rb +1 -1
- data/lib/fastlane/plugin/wpmreleasetoolkit/actions/common/circleci_trigger_job_action.rb +1 -1
- data/lib/fastlane/plugin/wpmreleasetoolkit/actions/common/close_milestone_action.rb +1 -1
- data/lib/fastlane/plugin/wpmreleasetoolkit/actions/common/create_new_milestone_action.rb +1 -1
- data/lib/fastlane/plugin/wpmreleasetoolkit/actions/common/create_release_action.rb +1 -1
- data/lib/fastlane/plugin/wpmreleasetoolkit/actions/common/extract_release_notes_for_version_action.rb +1 -1
- data/lib/fastlane/plugin/wpmreleasetoolkit/actions/common/firebase_login.rb +44 -0
- data/lib/fastlane/plugin/wpmreleasetoolkit/actions/common/get_prs_list_action.rb +1 -1
- data/lib/fastlane/plugin/wpmreleasetoolkit/actions/common/gp_downloadmetadata_action.rb +1 -1
- data/lib/fastlane/plugin/wpmreleasetoolkit/actions/common/gp_update_metadata_source.rb +1 -1
- data/lib/fastlane/plugin/wpmreleasetoolkit/actions/common/promo_screenshots_action.rb +1 -1
- data/lib/fastlane/plugin/wpmreleasetoolkit/actions/common/removebranchprotection_action.rb +1 -1
- data/lib/fastlane/plugin/wpmreleasetoolkit/actions/common/setbranchprotection_action.rb +1 -1
- data/lib/fastlane/plugin/wpmreleasetoolkit/actions/common/setfrozentag_action.rb +1 -1
- data/lib/fastlane/plugin/wpmreleasetoolkit/actions/configure/configure_add_files_to_copy_action.rb +1 -1
- data/lib/fastlane/plugin/wpmreleasetoolkit/actions/configure/configure_apply_action.rb +1 -1
- data/lib/fastlane/plugin/wpmreleasetoolkit/actions/configure/configure_download_action.rb +1 -1
- data/lib/fastlane/plugin/wpmreleasetoolkit/actions/configure/configure_setup_action.rb +1 -1
- data/lib/fastlane/plugin/wpmreleasetoolkit/actions/configure/configure_update_action.rb +1 -1
- data/lib/fastlane/plugin/wpmreleasetoolkit/actions/configure/configure_validate_action.rb +1 -1
- data/lib/fastlane/plugin/wpmreleasetoolkit/actions/ios/add_development_certificates_to_provisioning_profiles.rb +1 -1
- data/lib/fastlane/plugin/wpmreleasetoolkit/actions/ios/add_devices_to_provisioning_profiles.rb +1 -1
- data/lib/fastlane/plugin/wpmreleasetoolkit/actions/ios/ios_betabuild_prechecks.rb +1 -1
- data/lib/fastlane/plugin/wpmreleasetoolkit/actions/ios/ios_build_prechecks.rb +1 -1
- data/lib/fastlane/plugin/wpmreleasetoolkit/actions/ios/ios_build_preflight.rb +1 -1
- data/lib/fastlane/plugin/wpmreleasetoolkit/actions/ios/ios_bump_version_beta.rb +1 -1
- data/lib/fastlane/plugin/wpmreleasetoolkit/actions/ios/ios_bump_version_hotfix.rb +1 -1
- data/lib/fastlane/plugin/wpmreleasetoolkit/actions/ios/ios_bump_version_release.rb +1 -1
- data/lib/fastlane/plugin/wpmreleasetoolkit/actions/ios/ios_check_beta_deps.rb +1 -1
- data/lib/fastlane/plugin/wpmreleasetoolkit/actions/ios/ios_clear_intermediate_tags.rb +1 -1
- data/lib/fastlane/plugin/wpmreleasetoolkit/actions/ios/ios_codefreeze_prechecks.rb +1 -1
- data/lib/fastlane/plugin/wpmreleasetoolkit/actions/ios/ios_completecodefreeze_prechecks.rb +1 -1
- data/lib/fastlane/plugin/wpmreleasetoolkit/actions/ios/ios_current_branch_is_hotfix.rb +1 -1
- data/lib/fastlane/plugin/wpmreleasetoolkit/actions/ios/ios_final_tag.rb +1 -1
- data/lib/fastlane/plugin/wpmreleasetoolkit/actions/ios/ios_finalize_prechecks.rb +1 -1
- data/lib/fastlane/plugin/wpmreleasetoolkit/actions/ios/ios_get_app_version.rb +1 -1
- data/lib/fastlane/plugin/wpmreleasetoolkit/actions/ios/ios_get_build_version.rb +1 -1
- data/lib/fastlane/plugin/wpmreleasetoolkit/actions/ios/ios_get_store_app_sizes.rb +1 -1
- data/lib/fastlane/plugin/wpmreleasetoolkit/actions/ios/ios_hotfix_prechecks.rb +1 -1
- data/lib/fastlane/plugin/wpmreleasetoolkit/actions/ios/ios_localize_project.rb +1 -1
- data/lib/fastlane/plugin/wpmreleasetoolkit/actions/ios/ios_merge_strings_files.rb +1 -1
- data/lib/fastlane/plugin/wpmreleasetoolkit/actions/ios/ios_send_app_size_metrics.rb +170 -0
- data/lib/fastlane/plugin/wpmreleasetoolkit/actions/ios/ios_tag_build.rb +1 -1
- data/lib/fastlane/plugin/wpmreleasetoolkit/actions/ios/ios_update_metadata.rb +1 -1
- data/lib/fastlane/plugin/wpmreleasetoolkit/actions/ios/ios_update_metadata_source.rb +1 -1
- data/lib/fastlane/plugin/wpmreleasetoolkit/actions/ios/ios_update_release_notes.rb +1 -1
- data/lib/fastlane/plugin/wpmreleasetoolkit/actions/ios/ios_validate_ci_build.rb +1 -1
- data/lib/fastlane/plugin/wpmreleasetoolkit/helper/android/android_localize_helper.rb +2 -2
- data/lib/fastlane/plugin/wpmreleasetoolkit/helper/app_size_metrics_helper.rb +95 -0
- data/lib/fastlane/plugin/wpmreleasetoolkit/helper/github_helper.rb +1 -1
- data/lib/fastlane/plugin/wpmreleasetoolkit/helper/metadata_download_helper.rb +4 -4
- data/lib/fastlane/plugin/wpmreleasetoolkit/models/firebase_account.rb +19 -0
- data/lib/fastlane/plugin/wpmreleasetoolkit/models/firebase_device.rb +62 -0
- data/lib/fastlane/plugin/wpmreleasetoolkit/models/firebase_test_lab_result.rb +36 -0
- data/lib/fastlane/plugin/wpmreleasetoolkit/models/firebase_test_runner.rb +104 -0
- data/lib/fastlane/plugin/wpmreleasetoolkit/version.rb +1 -1
- data/lib/fastlane/plugin/wpmreleasetoolkit.rb +2 -2
- metadata +118 -81
@@ -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
|
@@ -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.
|
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}
|
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
|