fastlane-plugin-firebase_app_distribution 0.2.5 → 0.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 1415793a828ae696b67f1568c6393a9ae68eb6dbf17fc2a379904e3256201c1b
4
- data.tar.gz: b1e08e8e78359f62c2c76c868c8e80e90bdf3d463951072ecf77aaa46a232ad1
3
+ metadata.gz: 745bfb3dfc4c6c48f389c8e4d75b50026cf37e872ce26e7510aefef049280329
4
+ data.tar.gz: 8122801b30f95422216c9c5343d102bc6b11fbc16308f268515e7a3405bbf070
5
5
  SHA512:
6
- metadata.gz: a3c2f313a6401ac052c48329d9f21228c084983c6f6a3c252e22c494c4216b57ee68882668e1f3f6f0e5f639b3389b0701d97dad3c3542289099450aea2f1952
7
- data.tar.gz: 29c7622c069712c0012429c26e3ad7e306499c6a29d7a7d837185a008837831dba795787a16930817dbbdbb3aabb0f38f37c06094208d2e1376cff030c79733d
6
+ metadata.gz: 6ac42a57fdbda601b6ef2656a7e0d1b38304f022a26c970a75f1e0a43d01f2607f993c5500bceaf8ffb5b24ada6fbcbcb1ccf84d7dc8d87dc7d62049561d7886
7
+ data.tar.gz: ae12197e0149e187b02716e28a71207cd5ce67d14ea331bb9c230e41a53237e1c3e4359b1178f09bf059ac2d9697cb4b667d358a1d4751a3b47006efcaf131bb
data/fad-icon.png ADDED
Binary file
@@ -8,40 +8,93 @@ require_relative '../helper/firebase_app_distribution_error_message'
8
8
  require_relative '../client/firebase_app_distribution_api_client'
9
9
  require_relative '../helper/firebase_app_distribution_auth_client'
10
10
 
11
- ## TODO: should always use a file underneath? I think so.
12
11
  ## How should we document the usage of release notes?
13
12
  module Fastlane
14
13
  module Actions
15
- class FirebaseAppDistributionAction < Action
16
- FIREBASECMD_ACTION = "appdistribution:distribute".freeze
14
+ module SharedValues
15
+ FIREBASE_APP_DISTRO_RELEASE ||= :FIREBASE_APP_DISTRO_RELEASE
16
+ end
17
17
 
18
+ class FirebaseAppDistributionAction < Action
18
19
  extend Auth::FirebaseAppDistributionAuthClient
19
20
  extend Helper::FirebaseAppDistributionHelper
20
21
 
22
+ DEFAULT_UPLOAD_TIMEOUT_SECONDS = 300
23
+
21
24
  def self.run(params)
22
- params.values # to validate all inputs before looking for the ipa/apk
25
+ params.values # to validate all inputs before looking for the ipa/apk/aab
26
+
27
+ if params[:debug]
28
+ UI.important("Warning: Debug logging enabled. Output may include sensitive information.")
29
+ end
23
30
 
24
31
  app_id = app_id_from_params(params)
32
+ app_name = app_name_from_app_id(app_id)
25
33
  platform = lane_platform || platform_from_app_id(app_id)
26
- binary_path = binary_path_from_platform(platform, params[:ipa_path], params[:apk_path])
27
34
 
28
- auth_token = fetch_auth_token(params[:service_credentials_file], params[:firebase_cli_token])
29
- fad_api_client = Client::FirebaseAppDistributionApiClient.new(auth_token, platform, params[:debug])
35
+ binary_path = get_binary_path(platform, params)
36
+ UI.user_error!("Couldn't find binary") if binary_path.nil?
37
+ UI.user_error!("Couldn't find binary at path #{binary_path}") unless File.exist?(binary_path)
38
+ binary_type = binary_type_from_path(binary_path)
39
+
40
+ auth_token = fetch_auth_token(
41
+ params[:service_credentials_file], params[:firebase_cli_token], params[:debug]
42
+ )
43
+ fad_api_client = Client::FirebaseAppDistributionApiClient.new(auth_token, params[:debug])
44
+
45
+ # If binary is an AAB, get the AAB info for this app, which includes the integration state and certificate data
46
+ if binary_type == :AAB
47
+ aab_info = fad_api_client.get_aab_info(app_name)
48
+ validate_aab_setup!(aab_info)
49
+ end
50
+
51
+ upload_timeout = get_upload_timeout(params)
52
+
53
+ upload_status_response = fad_api_client.upload(app_name, binary_path, platform.to_s, upload_timeout)
54
+ release_name = upload_status_response.release_name
55
+ release = upload_status_response.release
30
56
 
31
- release_id = fad_api_client.upload(app_id, binary_path, platform.to_s)
32
- if release_id.nil?
33
- return
57
+ if binary_type == :AAB && aab_info && !aab_info.certs_provided?
58
+ updated_aab_info = fad_api_client.get_aab_info(app_name)
59
+ if updated_aab_info.certs_provided?
60
+ UI.message("After you upload an AAB for the first time, App Distribution " \
61
+ "generates a new test certificate. All AAB uploads are re-signed with this test " \
62
+ "certificate. Use the certificate fingerprints below to register your app " \
63
+ "signing key with API providers, such as Google Sign-In and Google Maps.\n" \
64
+ "MD-1 certificate fingerprint: #{updated_aab_info.md5_certificate_hash}\n" \
65
+ "SHA-1 certificate fingerprint: #{updated_aab_info.sha1_certificate_hash}\n" \
66
+ "SHA-256 certificate fingerprint: #{updated_aab_info.sha256_certificate_hash}")
67
+ end
34
68
  end
35
69
 
36
- release_notes = get_value_from_value_or_file(params[:release_notes], params[:release_notes_file])
37
- fad_api_client.post_notes(app_id, release_id, release_notes)
70
+ release_notes = release_notes(params)
71
+ if release_notes.nil? || release_notes.empty?
72
+ UI.message("⏩ No release notes passed in. Skipping this step.")
73
+ else
74
+ release = fad_api_client.update_release_notes(release_name, release_notes)
75
+ end
38
76
 
39
77
  testers = get_value_from_value_or_file(params[:testers], params[:testers_file])
40
78
  groups = get_value_from_value_or_file(params[:groups], params[:groups_file])
41
79
  emails = string_to_array(testers)
42
- group_ids = string_to_array(groups)
43
- fad_api_client.enable_access(app_id, release_id, emails, group_ids)
44
- UI.success("🎉 App Distribution upload finished successfully.")
80
+ group_aliases = string_to_array(groups)
81
+ fad_api_client.distribute(release_name, emails, group_aliases)
82
+ UI.success("🎉 App Distribution upload finished successfully. Setting Actions.lane_context[SharedValues::FIREBASE_APP_DISTRO_RELEASE] to the uploaded release.")
83
+
84
+ if upload_status_response.firebase_console_uri
85
+ UI.message("🔗 View this release in the Firebase console: #{upload_status_response.firebase_console_uri}")
86
+ end
87
+
88
+ if upload_status_response.testing_uri
89
+ UI.message("🔗 Share this release with testers who have access: #{upload_status_response.testing_uri}")
90
+ end
91
+
92
+ if upload_status_response.binary_download_uri
93
+ UI.message("🔗 Download the release binary (link expires in 1 hour): #{upload_status_response.binary_download_uri}")
94
+ end
95
+
96
+ Actions.lane_context[SharedValues::FIREBASE_APP_DISTRO_RELEASE] = release
97
+ release
45
98
  end
46
99
 
47
100
  def self.description
@@ -90,37 +143,66 @@ module Fastlane
90
143
  end
91
144
  end
92
145
 
93
- def self.binary_path_from_platform(platform, ipa_path, apk_path)
94
- case platform
95
- when :ios
96
- ipa_path
97
- when :android
98
- apk_path
99
- else
100
- ipa_path || apk_path
146
+ def self.get_binary_path(platform, params)
147
+ if platform == :ios
148
+ return params[:ipa_path] ||
149
+ Actions.lane_context[SharedValues::IPA_OUTPUT_PATH] ||
150
+ Dir["*.ipa"].sort_by { |x| File.mtime(x) }.last
151
+ end
152
+
153
+ if platform == :android
154
+ return params[:apk_path] || params[:android_artifact_path] if params[:apk_path] || params[:android_artifact_path]
155
+
156
+ if params[:android_artifact_type] == 'AAB'
157
+ return Actions.lane_context[SharedValues::GRADLE_AAB_OUTPUT_PATH] ||
158
+ Dir["*.aab"].last ||
159
+ Dir[File.join("app", "build", "outputs", "bundle", "release", "app-release.aab")].last
160
+ end
161
+
162
+ return Actions.lane_context[SharedValues::GRADLE_APK_OUTPUT_PATH] ||
163
+ Dir["*.apk"].last ||
164
+ Dir[File.join("app", "build", "outputs", "apk", "release", "app-release.apk")].last
101
165
  end
102
166
  end
103
167
 
104
- def self.available_options
105
- if lane_platform == :ios || lane_platform.nil?
106
- ipa_path_default = Dir["*.ipa"].sort_by { |x| File.mtime(x) }.last
168
+ def self.get_upload_timeout(params)
169
+ if params[:upload_timeout]
170
+ return params[:upload_timeout]
171
+ else
172
+ return DEFAULT_UPLOAD_TIMEOUT_SECONDS
107
173
  end
174
+ end
108
175
 
109
- if lane_platform == :android
110
- apk_path_default = Dir["*.apk"].last || Dir[File.join("app", "build", "outputs", "apk", "app-release.apk")].last
176
+ def self.validate_aab_setup!(aab_info)
177
+ if aab_info && aab_info.integration_state != AabInfo::AabState::INTEGRATED && aab_info.integration_state != AabInfo::AabState::UNAVAILABLE
178
+ case aab_info.integration_state
179
+ when AabInfo::AabState::PLAY_ACCOUNT_NOT_LINKED
180
+ UI.user_error!(ErrorMessage::PLAY_ACCOUNT_NOT_LINKED)
181
+ when AabInfo::AabState::APP_NOT_PUBLISHED
182
+ UI.user_error!(ErrorMessage::APP_NOT_PUBLISHED)
183
+ when AabInfo::AabState::NO_APP_WITH_GIVEN_BUNDLE_ID_IN_PLAY_ACCOUNT
184
+ UI.user_error!(ErrorMessage::NO_APP_WITH_GIVEN_BUNDLE_ID_IN_PLAY_ACCOUNT)
185
+ when AabInfo::AabState::PLAY_IAS_TERMS_NOT_ACCEPTED
186
+ UI.user_error!(ErrorMessage::PLAY_IAS_TERMS_NOT_ACCEPTED)
187
+ else
188
+ UI.user_error!(ErrorMessage.aab_upload_error(aab_info.integration_state))
189
+ end
111
190
  end
191
+ end
192
+
193
+ def self.release_notes(params)
194
+ release_notes_param =
195
+ get_value_from_value_or_file(params[:release_notes], params[:release_notes_file])
196
+ release_notes_param || Actions.lane_context[SharedValues::FL_CHANGELOG]
197
+ end
112
198
 
199
+ def self.available_options
113
200
  [
114
201
  # iOS Specific
115
202
  FastlaneCore::ConfigItem.new(key: :ipa_path,
116
203
  env_name: "FIREBASEAPPDISTRO_IPA_PATH",
117
204
  description: "Path to your IPA file. Optional if you use the _gym_ or _xcodebuild_ action",
118
- default_value: Actions.lane_context[SharedValues::IPA_OUTPUT_PATH] || ipa_path_default,
119
- default_value_dynamic: true,
120
- optional: true,
121
- verify_block: proc do |value|
122
- UI.user_error!("firebase_app_distribution: Couldn't find ipa file at path '#{value}'") unless File.exist?(value)
123
- end),
205
+ optional: true),
124
206
  FastlaneCore::ConfigItem.new(key: :googleservice_info_plist_path,
125
207
  env_name: "GOOGLESERVICE_INFO_PLIST_PATH",
126
208
  description: "Path to your GoogleService-Info.plist file, relative to the archived product path",
@@ -131,12 +213,21 @@ module Fastlane
131
213
  FastlaneCore::ConfigItem.new(key: :apk_path,
132
214
  env_name: "FIREBASEAPPDISTRO_APK_PATH",
133
215
  description: "Path to your APK file",
134
- default_value: Actions.lane_context[SharedValues::GRADLE_APK_OUTPUT_PATH] || apk_path_default,
216
+ optional: true),
217
+ FastlaneCore::ConfigItem.new(key: :android_artifact_path,
218
+ env_name: "FIREBASEAPPDISTRO_ANDROID_ARTIFACT_PATH",
219
+ description: "Path to your APK or AAB file",
220
+ optional: true),
221
+ FastlaneCore::ConfigItem.new(key: :android_artifact_type,
222
+ env_name: "FIREBASEAPPDISTRO_ANDROID_ARTIFACT_TYPE",
223
+ description: "Android artifact type. Set to 'APK' or 'AAB'. Defaults to 'APK' if not set",
224
+ default_value: "APK",
135
225
  default_value_dynamic: true,
136
226
  optional: true,
137
227
  verify_block: proc do |value|
138
- UI.user_error!("firebase_app_distribution: Couldn't find apk file at path '#{value}'") unless File.exist?(value)
228
+ UI.user_error!("firebase_app_distribution: '#{value}' is not a valid value for android_artifact_type. Should be 'APK' or 'AAB'") unless ['APK', 'AAB'].include?(value)
139
229
  end),
230
+ # Generic
140
231
  FastlaneCore::ConfigItem.new(key: :app,
141
232
  env_name: "FIREBASEAPPDISTRO_APP",
142
233
  description: "Your app's Firebase App ID. You can find the App ID in the Firebase console, on the General Settings page",
@@ -149,12 +240,12 @@ module Fastlane
149
240
  type: String),
150
241
  FastlaneCore::ConfigItem.new(key: :groups,
151
242
  env_name: "FIREBASEAPPDISTRO_GROUPS",
152
- description: "The groups used for distribution, separated by commas",
243
+ description: "The group aliases used for distribution, separated by commas",
153
244
  optional: true,
154
245
  type: String),
155
246
  FastlaneCore::ConfigItem.new(key: :groups_file,
156
247
  env_name: "FIREBASEAPPDISTRO_GROUPS_FILE",
157
- description: "The groups used for distribution, separated by commas",
248
+ description: "The group aliases used for distribution, separated by commas",
158
249
  optional: true,
159
250
  type: String),
160
251
  FastlaneCore::ConfigItem.new(key: :testers,
@@ -170,8 +261,6 @@ module Fastlane
170
261
  FastlaneCore::ConfigItem.new(key: :release_notes,
171
262
  env_name: "FIREBASEAPPDISTRO_RELEASE_NOTES",
172
263
  description: "Release notes for this build",
173
- default_value: Actions.lane_context[SharedValues::FL_CHANGELOG],
174
- default_value_dynamic: true,
175
264
  optional: true,
176
265
  type: String),
177
266
  FastlaneCore::ConfigItem.new(key: :release_notes_file,
@@ -180,7 +269,7 @@ module Fastlane
180
269
  optional: true,
181
270
  type: String),
182
271
  FastlaneCore::ConfigItem.new(key: :firebase_cli_token,
183
- description: "Auth token for firebase cli",
272
+ description: "Auth token generated using the Firebase CLI's login:ci command",
184
273
  optional: true,
185
274
  type: String),
186
275
  FastlaneCore::ConfigItem.new(key: :debug,
@@ -191,7 +280,12 @@ module Fastlane
191
280
  FastlaneCore::ConfigItem.new(key: :service_credentials_file,
192
281
  description: "Path to Google service account json",
193
282
  optional: true,
194
- type: String)
283
+ type: String),
284
+ FastlaneCore::ConfigItem.new(key: :upload_timeout,
285
+ description: "The amount of seconds before the upload will timeout, if not completed",
286
+ optional: true,
287
+ default_value: DEFAULT_UPLOAD_TIMEOUT_SECONDS,
288
+ type: Integer)
195
289
  ]
196
290
  end
197
291
 
@@ -207,12 +301,18 @@ module Fastlane
207
301
  [
208
302
  <<-CODE
209
303
  firebase_app_distribution(
210
- app: "1:1234567890:ios:0a1b2c3d4e5f67890",
304
+ app: "<your Firebase app ID>",
211
305
  testers: "snatchev@google.com, rebeccahe@google.com"
212
306
  )
213
307
  CODE
214
308
  ]
215
309
  end
310
+
311
+ def self.output
312
+ [
313
+ ['FIREBASE_APP_DISTRO_RELEASE', 'A hash representing the uploaded release created in Firebase App Distribution']
314
+ ]
315
+ end
216
316
  end
217
317
  end
218
318
  end
@@ -0,0 +1,90 @@
1
+ require 'fastlane/action'
2
+ require 'fastlane_core/ui/ui'
3
+
4
+ require_relative '../helper/firebase_app_distribution_helper'
5
+ require_relative '../helper/firebase_app_distribution_auth_client'
6
+
7
+ module Fastlane
8
+ module Actions
9
+ class FirebaseAppDistributionAddTestersAction < Action
10
+ extend Auth::FirebaseAppDistributionAuthClient
11
+ extend Helper::FirebaseAppDistributionHelper
12
+
13
+ def self.run(params)
14
+ auth_token = fetch_auth_token(params[:service_credentials_file], params[:firebase_cli_token])
15
+ fad_api_client = Client::FirebaseAppDistributionApiClient.new(auth_token, params[:debug])
16
+
17
+ if blank?(params[:emails]) && blank?(params[:file])
18
+ UI.user_error!("Must specify `emails` or `file`.")
19
+ end
20
+
21
+ emails = string_to_array(get_value_from_value_or_file(params[:emails], params[:file]))
22
+
23
+ UI.user_error!("Must pass at least one email") if blank?(emails)
24
+
25
+ if emails.count > 1000
26
+ UI.user_error!("A maximum of 1000 testers can be added at a time.")
27
+ end
28
+
29
+ UI.message("⏳ Adding #{emails.count} testers to project #{params[:project_number]}...")
30
+
31
+ fad_api_client.add_testers(params[:project_number], emails)
32
+
33
+ # The add_testers response lists all the testers from the request
34
+ # regardless of whether or not they were created or if they already
35
+ # exists so can't get an accurate count of the number of newly created testers
36
+ UI.success("✅ Tester(s) successfully added.")
37
+ end
38
+
39
+ def self.description
40
+ "Create testers in bulk from a comma-separated list or a file"
41
+ end
42
+
43
+ def self.authors
44
+ ["Tunde Agboola"]
45
+ end
46
+
47
+ # supports markdown.
48
+ def self.details
49
+ "Create testers in bulk from a comma-separated list or a file"
50
+ end
51
+
52
+ def self.available_options
53
+ [
54
+ FastlaneCore::ConfigItem.new(key: :project_number,
55
+ env_name: "FIREBASEAPPDISTRO_PROJECT_NUMBER",
56
+ description: "Your Firebase project number. You can find the project number in the Firebase console, on the General Settings page",
57
+ type: Integer,
58
+ optional: false),
59
+ FastlaneCore::ConfigItem.new(key: :emails,
60
+ env_name: "FIREBASEAPPDISTRO_ADD_TESTERS_EMAILS",
61
+ description: "Comma separated list of tester emails to be created. A maximum of 1000 testers can be created at a time",
62
+ optional: true,
63
+ type: String),
64
+ FastlaneCore::ConfigItem.new(key: :file,
65
+ env_name: "FIREBASEAPPDISTRO_ADD_TESTERS_FILE",
66
+ description: "Path to a file containing a comma separated list of tester emails to be created. A maximum of 1000 testers can be deleted at a time",
67
+ optional: true,
68
+ type: String),
69
+ FastlaneCore::ConfigItem.new(key: :service_credentials_file,
70
+ description: "Path to Google service credentials file",
71
+ optional: true,
72
+ type: String),
73
+ FastlaneCore::ConfigItem.new(key: :firebase_cli_token,
74
+ description: "Auth token generated using the Firebase CLI's login:ci command",
75
+ optional: true,
76
+ type: String),
77
+ FastlaneCore::ConfigItem.new(key: :debug,
78
+ description: "Print verbose debug output",
79
+ optional: true,
80
+ default_value: false,
81
+ is_string: false)
82
+ ]
83
+ end
84
+
85
+ def self.is_supported?(platform)
86
+ true
87
+ end
88
+ end
89
+ end
90
+ end
@@ -0,0 +1,114 @@
1
+ require 'fastlane/action'
2
+ require_relative '../client/firebase_app_distribution_api_client'
3
+ require_relative '../helper/firebase_app_distribution_auth_client'
4
+ require_relative '../helper/firebase_app_distribution_helper'
5
+
6
+ module Fastlane
7
+ module Actions
8
+ module SharedValues
9
+ FIREBASE_APP_DISTRO_LATEST_RELEASE ||= :FIREBASE_APP_DISTRO_LATEST_RELEASE
10
+ end
11
+ class FirebaseAppDistributionGetLatestReleaseAction < Action
12
+ extend Auth::FirebaseAppDistributionAuthClient
13
+ extend Helper::FirebaseAppDistributionHelper
14
+
15
+ def self.run(params)
16
+ auth_token = fetch_auth_token(params[:service_credentials_file], params[:firebase_cli_token])
17
+ fad_api_client = Client::FirebaseAppDistributionApiClient.new(auth_token, params[:debug])
18
+
19
+ UI.message("⏳ Fetching latest release for app #{params[:app]}...")
20
+
21
+ releases = fad_api_client.list_releases(app_name_from_app_id(params[:app]), 1)[:releases] || []
22
+ if releases.empty?
23
+ latest_release = nil
24
+ UI.important("No releases for app #{params[:app]} found in App Distribution. Returning nil and setting Actions.lane_context[SharedValues::FIREBASE_APP_DISTRO_LATEST_RELEASE].")
25
+ else
26
+ latest_release = releases[0]
27
+ UI.success("✅ Latest release fetched successfully. Returning release and setting Actions.lane_context[SharedValues::FIREBASE_APP_DISTRO_LATEST_RELEASE].")
28
+ end
29
+ Actions.lane_context[SharedValues::FIREBASE_APP_DISTRO_LATEST_RELEASE] = latest_release
30
+ return latest_release
31
+ end
32
+
33
+ #####################################################
34
+ # @!group Documentation
35
+ #####################################################
36
+
37
+ def self.description
38
+ "Fetches the latest release in Firebase App Distribution"
39
+ end
40
+
41
+ def self.details
42
+ [
43
+ "Fetches information about the most recently created release in App Distribution, including the version and release notes. Returns nil if no releases are found."
44
+ ].join("\n")
45
+ end
46
+
47
+ def self.available_options
48
+ [
49
+ FastlaneCore::ConfigItem.new(key: :app,
50
+ env_name: "FIREBASEAPPDISTRO_APP",
51
+ description: "Your app's Firebase App ID. You can find the App ID in the Firebase console, on the General Settings page",
52
+ optional: false,
53
+ type: String),
54
+ FastlaneCore::ConfigItem.new(key: :firebase_cli_token,
55
+ description: "Auth token generated using Firebase CLI's login:ci command",
56
+ optional: true,
57
+ type: String),
58
+ FastlaneCore::ConfigItem.new(key: :service_credentials_file,
59
+ description: "Path to Google service account json",
60
+ optional: true,
61
+ type: String),
62
+ FastlaneCore::ConfigItem.new(key: :debug,
63
+ description: "Print verbose debug output",
64
+ optional: true,
65
+ default_value: false,
66
+ is_string: false)
67
+ ]
68
+ end
69
+
70
+ def self.output
71
+ [
72
+ ['FIREBASE_APP_DISTRO_LATEST_RELEASE', 'A hash representing the lastest release created in Firebase App Distribution']
73
+ ]
74
+ end
75
+
76
+ def self.return_value
77
+ "Hash representation of the lastest release created in Firebase App Distribution (see https://firebase.google.com/docs/reference/app-distribution/rest/v1/projects.apps.releases#resource:-release)"
78
+ end
79
+
80
+ def self.return_type
81
+ :hash
82
+ end
83
+
84
+ def self.authors
85
+ ["lkellogg@google.com"]
86
+ end
87
+
88
+ def self.is_supported?(platform)
89
+ true
90
+ end
91
+
92
+ def self.example_code
93
+ [
94
+ 'release = firebase_app_distribution_get_latest_release(app: "<your Firebase app ID>")',
95
+ 'increment_build_number({
96
+ build_number: firebase_app_distribution_get_latest_release(app: "<your Firebase app ID>")[:buildVersion].to_i + 1
97
+ })'
98
+ ]
99
+ end
100
+
101
+ def self.sample_return_value
102
+ {
103
+ name: "projects/123456789/apps/1:1234567890:ios:0a1b2c3d4e5f67890/releases/0a1b2c3d4",
104
+ releaseNotes: {
105
+ text: "Here are some release notes!"
106
+ },
107
+ displayVersion: "1.2.3",
108
+ buildVersion: "10",
109
+ createTime: "2021-10-06T15:01:23Z"
110
+ }
111
+ end
112
+ end
113
+ end
114
+ end
@@ -0,0 +1,97 @@
1
+ require 'fastlane/action'
2
+ require 'open3'
3
+ require 'shellwords'
4
+ require 'googleauth'
5
+ require_relative '../helper/firebase_app_distribution_helper'
6
+ require_relative '../helper/firebase_app_distribution_error_message'
7
+ require_relative '../client/firebase_app_distribution_api_client'
8
+ require_relative '../helper/firebase_app_distribution_auth_client'
9
+
10
+ module Fastlane
11
+ module Actions
12
+ class FirebaseAppDistributionGetUdidsAction < Action
13
+ extend Auth::FirebaseAppDistributionAuthClient
14
+ extend Helper::FirebaseAppDistributionHelper
15
+
16
+ def self.run(params)
17
+ auth_token = fetch_auth_token(params[:service_credentials_file], params[:firebase_cli_token])
18
+ fad_api_client = Client::FirebaseAppDistributionApiClient.new(auth_token, params[:debug])
19
+
20
+ app_id = params[:app]
21
+ udids = fad_api_client.get_udids(app_id)
22
+
23
+ if udids.empty?
24
+ UI.important("App Distribution fetched 0 tester UDIDs. Nothing written to output file.")
25
+ else
26
+ write_udids_to_file(udids, params[:output_file])
27
+ UI.success("🎉 App Distribution tester UDIDs written to: #{params[:output_file]}")
28
+ end
29
+ end
30
+
31
+ def self.write_udids_to_file(udids, output_file)
32
+ File.open(output_file, 'w') do |f|
33
+ f.write("Device ID\tDevice Name\tDevice Platform\n")
34
+ udids.each do |tester_udid|
35
+ f.write("#{tester_udid[:udid]}\t#{tester_udid[:name]}\t#{tester_udid[:platform]}\n")
36
+ end
37
+ end
38
+ end
39
+
40
+ def self.description
41
+ "Download the UDIDs of your Firebase App Distribution testers"
42
+ end
43
+
44
+ def self.authors
45
+ ["Lee Kellogg"]
46
+ end
47
+
48
+ # supports markdown.
49
+ def self.details
50
+ "Export your testers' device identifiers in a CSV file, so you can add them your provisioning profile. This file can be imported into your Apple developer account using the Register Multiple Devices option. See the [App Distribution docs](https://firebase.google.com/docs/app-distribution/ios/distribute-console#register-tester-devices) for more info."
51
+ end
52
+
53
+ def self.available_options
54
+ [
55
+ FastlaneCore::ConfigItem.new(key: :app,
56
+ env_name: "FIREBASEAPPDISTRO_APP",
57
+ description: "Your app's Firebase App ID. You can find the App ID in the Firebase console, on the General Settings page",
58
+ optional: false,
59
+ type: String),
60
+ FastlaneCore::ConfigItem.new(key: :output_file,
61
+ env_name: "FIREBASEAPPDISTRO_OUTPUT_FILE",
62
+ description: "The path to the file where the tester UDIDs will be written",
63
+ optional: false,
64
+ type: String),
65
+ FastlaneCore::ConfigItem.new(key: :firebase_cli_token,
66
+ description: "Auth token generated using the Firebase CLI's login:ci command",
67
+ optional: true,
68
+ type: String),
69
+ FastlaneCore::ConfigItem.new(key: :service_credentials_file,
70
+ description: "Path to Google service account json",
71
+ optional: true,
72
+ type: String),
73
+ FastlaneCore::ConfigItem.new(key: :debug,
74
+ description: "Print verbose debug output",
75
+ optional: true,
76
+ default_value: false,
77
+ is_string: false)
78
+ ]
79
+ end
80
+
81
+ def self.is_supported?(platform)
82
+ [:ios].include?(platform)
83
+ end
84
+
85
+ def self.example_code
86
+ [
87
+ <<-CODE
88
+ firebase_app_distribution_get_udids(
89
+ app: "<your Firebase app ID>",
90
+ output_file: "tester_udids.txt",
91
+ )
92
+ CODE
93
+ ]
94
+ end
95
+ end
96
+ end
97
+ end