fastlane-plugin-firebase_app_distribution 0.6.1 → 0.7.0.pre.1
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 +4 -4
- data/lib/fastlane/plugin/firebase_app_distribution/actions/firebase_app_distribution_action.rb +150 -40
- data/lib/fastlane/plugin/firebase_app_distribution/actions/firebase_app_distribution_add_testers_action.rb +52 -9
- data/lib/fastlane/plugin/firebase_app_distribution/actions/firebase_app_distribution_create_group_action.rb +25 -4
- data/lib/fastlane/plugin/firebase_app_distribution/actions/firebase_app_distribution_delete_group_action.rb +11 -3
- data/lib/fastlane/plugin/firebase_app_distribution/actions/firebase_app_distribution_get_latest_release.rb +39 -6
- data/lib/fastlane/plugin/firebase_app_distribution/actions/firebase_app_distribution_get_udids.rb +2 -2
- data/lib/fastlane/plugin/firebase_app_distribution/actions/firebase_app_distribution_remove_testers_action.rb +48 -9
- data/lib/fastlane/plugin/firebase_app_distribution/client/firebase_app_distribution_api_client.rb +1 -396
- data/lib/fastlane/plugin/firebase_app_distribution/helper/firebase_app_distribution_auth_client.rb +24 -19
- data/lib/fastlane/plugin/firebase_app_distribution/helper/firebase_app_distribution_error_message.rb +2 -3
- data/lib/fastlane/plugin/firebase_app_distribution/helper/firebase_app_distribution_helper.rb +31 -2
- data/lib/fastlane/plugin/firebase_app_distribution/version.rb +1 -1
- metadata +32 -7
- data/lib/fastlane/plugin/firebase_app_distribution/client/aab_info.rb +0 -42
- data/lib/fastlane/plugin/firebase_app_distribution/client/error_response.rb +0 -16
- data/lib/fastlane/plugin/firebase_app_distribution/helper/upload_status_response.rb +0 -77
data/lib/fastlane/plugin/firebase_app_distribution/client/firebase_app_distribution_api_client.rb
CHANGED
@@ -1,6 +1,4 @@
|
|
1
1
|
require 'fastlane_core/ui/ui'
|
2
|
-
require_relative '../client/error_response'
|
3
|
-
require_relative '../client/aab_info'
|
4
2
|
require_relative '../helper/firebase_app_distribution_helper'
|
5
3
|
|
6
4
|
module Fastlane
|
@@ -9,14 +7,8 @@ module Fastlane
|
|
9
7
|
include Helper::FirebaseAppDistributionHelper
|
10
8
|
|
11
9
|
BASE_URL = "https://firebaseappdistribution.googleapis.com"
|
12
|
-
TOKEN_CREDENTIAL_URI = "https://oauth2.googleapis.com/token"
|
13
|
-
MAX_POLLING_RETRIES = 60
|
14
|
-
POLLING_INTERVAL_SECONDS = 5
|
15
10
|
|
16
11
|
AUTHORIZATION = "Authorization"
|
17
|
-
CONTENT_TYPE = "Content-Type"
|
18
|
-
APPLICATION_JSON = "application/json"
|
19
|
-
APPLICATION_OCTET_STREAM = "application/octet-stream"
|
20
12
|
CLIENT_VERSION = "X-Client-Version"
|
21
13
|
|
22
14
|
def initialize(auth_token, debug = false)
|
@@ -24,169 +16,6 @@ module Fastlane
|
|
24
16
|
@debug = debug
|
25
17
|
end
|
26
18
|
|
27
|
-
# Enables tester access to the specified app release. Skips this
|
28
|
-
# step if no testers are passed in (emails and group_aliases are nil/empty).
|
29
|
-
#
|
30
|
-
# args
|
31
|
-
# release_name - App release resource name, returned by upload_status endpoint
|
32
|
-
# emails - String array of app testers' email addresses
|
33
|
-
# group_aliases - String array of Firebase tester group aliases
|
34
|
-
#
|
35
|
-
# Throws a user_error if emails or group_aliases are invalid
|
36
|
-
def distribute(release_name, emails, group_aliases)
|
37
|
-
if (emails.nil? || emails.empty?) && (group_aliases.nil? || group_aliases.empty?)
|
38
|
-
UI.success("✅ No testers passed in. Skipping this step.")
|
39
|
-
return
|
40
|
-
end
|
41
|
-
payload = { testerEmails: emails, groupAliases: group_aliases }
|
42
|
-
begin
|
43
|
-
connection.post(distribute_url(release_name), payload.to_json) do |request|
|
44
|
-
request.headers[AUTHORIZATION] = "Bearer " + @auth_token
|
45
|
-
request.headers[CONTENT_TYPE] = APPLICATION_JSON
|
46
|
-
request.headers[CLIENT_VERSION] = client_version_header_value
|
47
|
-
end
|
48
|
-
rescue Faraday::ClientError
|
49
|
-
UI.user_error!("#{ErrorMessage::INVALID_TESTERS} \nEmails: #{emails} \nGroup Aliases: #{group_aliases}")
|
50
|
-
end
|
51
|
-
UI.success("✅ Added testers/groups.")
|
52
|
-
end
|
53
|
-
|
54
|
-
# Update release notes for the specified app release. Skips this
|
55
|
-
# step if no notes are passed in (release_notes is nil/empty).
|
56
|
-
#
|
57
|
-
# args
|
58
|
-
# release_name - App release resource name, returned by upload_status endpoint
|
59
|
-
# release_notes - String of notes for this release
|
60
|
-
#
|
61
|
-
# Returns a hash of the release
|
62
|
-
#
|
63
|
-
# Throws a user_error if the release_notes are invalid
|
64
|
-
def update_release_notes(release_name, release_notes)
|
65
|
-
payload = {
|
66
|
-
name: release_name,
|
67
|
-
releaseNotes: {
|
68
|
-
text: release_notes
|
69
|
-
}
|
70
|
-
}
|
71
|
-
response = connection.patch(update_release_notes_url(release_name), payload.to_json) do |request|
|
72
|
-
request.headers[AUTHORIZATION] = "Bearer " + @auth_token
|
73
|
-
request.headers[CONTENT_TYPE] = APPLICATION_JSON
|
74
|
-
request.headers[CLIENT_VERSION] = client_version_header_value
|
75
|
-
end
|
76
|
-
UI.success("✅ Posted release notes.")
|
77
|
-
response.body
|
78
|
-
rescue Faraday::ClientError => e
|
79
|
-
error = ErrorResponse.new(e.response)
|
80
|
-
UI.user_error!("#{ErrorMessage::INVALID_RELEASE_NOTES}: #{error.message}")
|
81
|
-
end
|
82
|
-
|
83
|
-
# Get AAB info (Android apps only)
|
84
|
-
#
|
85
|
-
# args
|
86
|
-
# app_name - Firebase App resource name
|
87
|
-
#
|
88
|
-
# Throws a user_error if the app hasn't been onboarded to App Distribution
|
89
|
-
def get_aab_info(app_name)
|
90
|
-
begin
|
91
|
-
response = connection.get(aab_info_url(app_name)) do |request|
|
92
|
-
request.headers[AUTHORIZATION] = "Bearer " + @auth_token
|
93
|
-
request.headers[CLIENT_VERSION] = client_version_header_value
|
94
|
-
end
|
95
|
-
rescue Faraday::ResourceNotFound
|
96
|
-
UI.user_error!("#{ErrorMessage::INVALID_APP_ID}: #{app_name}")
|
97
|
-
end
|
98
|
-
|
99
|
-
AabInfo.new(response.body)
|
100
|
-
end
|
101
|
-
|
102
|
-
# Uploads the app binary to the Firebase API
|
103
|
-
#
|
104
|
-
# args
|
105
|
-
# app_name - Firebase App resource name
|
106
|
-
# binary_path - Absolute path to your app's aab/apk/ipa file
|
107
|
-
# platform - 'android' or 'ios'
|
108
|
-
# timeout - The amount of seconds before the upload will timeout, if not completed
|
109
|
-
#
|
110
|
-
# Throws a user_error if the binary file does not exist
|
111
|
-
def upload_binary(app_name, binary_path, platform, timeout)
|
112
|
-
response = connection.post(binary_upload_url(app_name), read_binary(binary_path)) do |request|
|
113
|
-
request.options.timeout = timeout # seconds
|
114
|
-
request.headers[AUTHORIZATION] = "Bearer " + @auth_token
|
115
|
-
request.headers[CONTENT_TYPE] = APPLICATION_OCTET_STREAM
|
116
|
-
request.headers[CLIENT_VERSION] = client_version_header_value
|
117
|
-
request.headers["X-Goog-Upload-File-Name"] = File.basename(binary_path)
|
118
|
-
request.headers["X-Goog-Upload-Protocol"] = "raw"
|
119
|
-
end
|
120
|
-
|
121
|
-
response.body[:name] || ''
|
122
|
-
rescue Errno::ENOENT # Raised when binary_path file does not exist
|
123
|
-
binary_type = binary_type_from_path(binary_path)
|
124
|
-
UI.user_error!("#{ErrorMessage.binary_not_found(binary_type)}: #{binary_path}")
|
125
|
-
end
|
126
|
-
|
127
|
-
# Uploads the binary file if it has not already been uploaded
|
128
|
-
# Takes at least POLLING_INTERVAL_SECONDS between polling get_upload_status
|
129
|
-
#
|
130
|
-
# args
|
131
|
-
# app_name - Firebase App resource name
|
132
|
-
# binary_path - Absolute path to your app's aab/apk/ipa file
|
133
|
-
# timeout - The amount of seconds before the upload will timeout, if not completed
|
134
|
-
#
|
135
|
-
# Returns a `UploadStatusResponse` with the upload is complete.
|
136
|
-
#
|
137
|
-
# Crashes if the number of polling retries exceeds MAX_POLLING_RETRIES or if the binary cannot
|
138
|
-
# be uploaded.
|
139
|
-
def upload(app_name, binary_path, platform, timeout)
|
140
|
-
binary_type = binary_type_from_path(binary_path)
|
141
|
-
|
142
|
-
UI.message("⌛ Uploading the #{binary_type}.")
|
143
|
-
operation_name = upload_binary(app_name, binary_path, platform, timeout)
|
144
|
-
|
145
|
-
upload_status_response = get_upload_status(operation_name)
|
146
|
-
MAX_POLLING_RETRIES.times do
|
147
|
-
if upload_status_response.success?
|
148
|
-
if upload_status_response.release_updated?
|
149
|
-
UI.success("✅ Uploaded #{binary_type} successfully; updated provisioning profile of existing release #{upload_status_response.release_version}.")
|
150
|
-
break
|
151
|
-
elsif upload_status_response.release_unmodified?
|
152
|
-
UI.success("✅ The same #{binary_type} was found in release #{upload_status_response.release_version} with no changes, skipping.")
|
153
|
-
break
|
154
|
-
else
|
155
|
-
UI.success("✅ Uploaded #{binary_type} successfully and created release #{upload_status_response.release_version}.")
|
156
|
-
end
|
157
|
-
break
|
158
|
-
elsif upload_status_response.in_progress?
|
159
|
-
sleep(POLLING_INTERVAL_SECONDS)
|
160
|
-
upload_status_response = get_upload_status(operation_name)
|
161
|
-
else
|
162
|
-
if !upload_status_response.error_message.nil?
|
163
|
-
UI.user_error!("#{ErrorMessage.upload_binary_error(binary_type)}: #{upload_status_response.error_message}")
|
164
|
-
else
|
165
|
-
UI.user_error!(ErrorMessage.upload_binary_error(binary_type))
|
166
|
-
end
|
167
|
-
end
|
168
|
-
end
|
169
|
-
unless upload_status_response.success?
|
170
|
-
UI.crash!("It took longer than expected to process your #{binary_type}, please try again.")
|
171
|
-
end
|
172
|
-
|
173
|
-
upload_status_response
|
174
|
-
end
|
175
|
-
|
176
|
-
# Fetches the status of an uploaded binary
|
177
|
-
#
|
178
|
-
# args
|
179
|
-
# operation_name - Upload operation name (with binary hash)
|
180
|
-
#
|
181
|
-
# Returns the `done` status, as well as a release, error, or nil
|
182
|
-
def get_upload_status(operation_name)
|
183
|
-
response = connection.get(upload_status_url(operation_name)) do |request|
|
184
|
-
request.headers[AUTHORIZATION] = "Bearer " + @auth_token
|
185
|
-
request.headers[CLIENT_VERSION] = client_version_header_value
|
186
|
-
end
|
187
|
-
UploadStatusResponse.new(response.body)
|
188
|
-
end
|
189
|
-
|
190
19
|
# Get tester UDIDs
|
191
20
|
#
|
192
21
|
# args
|
@@ -205,233 +34,14 @@ module Fastlane
|
|
205
34
|
response.body[:testerUdids] || []
|
206
35
|
end
|
207
36
|
|
208
|
-
# Create testers
|
209
|
-
#
|
210
|
-
# args
|
211
|
-
# project_number - Firebase project number
|
212
|
-
# emails - An array of emails to be created as testers. A maximum of
|
213
|
-
# 1000 testers can be created at a time.
|
214
|
-
#
|
215
|
-
def add_testers(project_number, emails)
|
216
|
-
payload = { emails: emails }
|
217
|
-
connection.post(add_testers_url(project_number), payload.to_json) do |request|
|
218
|
-
request.headers[AUTHORIZATION] = "Bearer " + @auth_token
|
219
|
-
request.headers[CONTENT_TYPE] = APPLICATION_JSON
|
220
|
-
request.headers[CLIENT_VERSION] = client_version_header_value
|
221
|
-
end
|
222
|
-
rescue Faraday::BadRequestError
|
223
|
-
UI.user_error!(ErrorMessage::INVALID_EMAIL_ADDRESS)
|
224
|
-
rescue Faraday::ResourceNotFound
|
225
|
-
UI.user_error!(ErrorMessage::INVALID_PROJECT)
|
226
|
-
rescue Faraday::ClientError => e
|
227
|
-
if e.response[:status] == 429
|
228
|
-
UI.user_error!(ErrorMessage::TESTER_LIMIT_VIOLATION)
|
229
|
-
else
|
230
|
-
raise e
|
231
|
-
end
|
232
|
-
end
|
233
|
-
|
234
|
-
# Delete testers
|
235
|
-
#
|
236
|
-
# args
|
237
|
-
# project_number - Firebase project number
|
238
|
-
# emails - An array of emails to be deleted as testers. A maximum of
|
239
|
-
# 1000 testers can be deleted at a time.
|
240
|
-
#
|
241
|
-
# Returns the number of testers that were deleted
|
242
|
-
def remove_testers(project_number, emails)
|
243
|
-
payload = { emails: emails }
|
244
|
-
response = connection.post(remove_testers_url(project_number), payload.to_json) do |request|
|
245
|
-
request.headers[AUTHORIZATION] = "Bearer " + @auth_token
|
246
|
-
request.headers[CONTENT_TYPE] = APPLICATION_JSON
|
247
|
-
request.headers[CLIENT_VERSION] = client_version_header_value
|
248
|
-
end
|
249
|
-
response.body[:emails] ? response.body[:emails].count : 0
|
250
|
-
rescue Faraday::ResourceNotFound
|
251
|
-
UI.user_error!(ErrorMessage::INVALID_PROJECT)
|
252
|
-
end
|
253
|
-
|
254
|
-
# Create tester group
|
255
|
-
#
|
256
|
-
# args
|
257
|
-
# project_number - Firebase project number
|
258
|
-
# group_alias - Alias of the tester group
|
259
|
-
# display_name - Display name of the tester group
|
260
|
-
#
|
261
|
-
def create_group(project_number, group_alias, display_name)
|
262
|
-
payload = { name: "projects/#{project_number}/groups/#{group_alias}",
|
263
|
-
displayName: display_name }
|
264
|
-
response = connection.post(add_tester_group_url(project_number), payload.to_json) do |request|
|
265
|
-
request.params["groupId"] = group_alias
|
266
|
-
request.headers[AUTHORIZATION] = "Bearer " + @auth_token
|
267
|
-
request.headers[CONTENT_TYPE] = APPLICATION_JSON
|
268
|
-
request.headers[CLIENT_VERSION] = client_version_header_value
|
269
|
-
end
|
270
|
-
response.body
|
271
|
-
rescue Faraday::BadRequestError
|
272
|
-
UI.user_error!(ErrorMessage::INVALID_TESTER_GROUP_NAME)
|
273
|
-
rescue Faraday::ResourceNotFound
|
274
|
-
UI.user_error!(ErrorMessage::INVALID_PROJECT)
|
275
|
-
rescue Faraday::ConflictError
|
276
|
-
UI.important("Tester group #{group_alias} already exists.")
|
277
|
-
return {
|
278
|
-
name: "projects/#{project_number}/groups/#{group_alias}"
|
279
|
-
}
|
280
|
-
rescue Faraday::ClientError => e
|
281
|
-
raise e
|
282
|
-
end
|
283
|
-
|
284
|
-
# Add testers to group
|
285
|
-
#
|
286
|
-
# args
|
287
|
-
# project_number - Firebase project number
|
288
|
-
# group_alias - Alias of the tester group
|
289
|
-
# emails - An array of emails to be added to the group.
|
290
|
-
# A maximum of 1000 testers can be added at a time, if creating missing testers is enabled.
|
291
|
-
# create_missing_testers - If true, missing testers will be created and added to the group.
|
292
|
-
#
|
293
|
-
def add_testers_to_group(project_number, group_alias, emails, create_missing_testers = false)
|
294
|
-
payload = { emails: emails,
|
295
|
-
createMissingTesters: create_missing_testers }
|
296
|
-
response = connection.post(add_testers_to_group_url(project_number, group_alias), payload.to_json) do |request|
|
297
|
-
request.headers[AUTHORIZATION] = "Bearer " + @auth_token
|
298
|
-
request.headers[CONTENT_TYPE] = APPLICATION_JSON
|
299
|
-
request.headers[CLIENT_VERSION] = client_version_header_value
|
300
|
-
end
|
301
|
-
response.body
|
302
|
-
rescue Faraday::BadRequestError
|
303
|
-
UI.user_error!(ErrorMessage::INVALID_EMAIL_ADDRESS)
|
304
|
-
rescue Faraday::ResourceNotFound
|
305
|
-
UI.user_error!(ErrorMessage::INVALID_TESTER_GROUP)
|
306
|
-
rescue Faraday::ClientError => e
|
307
|
-
raise e
|
308
|
-
end
|
309
|
-
|
310
|
-
# Remove testers from group
|
311
|
-
#
|
312
|
-
# args
|
313
|
-
# project_number - Firebase project number
|
314
|
-
# group_alias - Alias of the tester group
|
315
|
-
# emails - An array of emails to be removed from the group.
|
316
|
-
#
|
317
|
-
def remove_testers_from_group(project_number, group_alias, emails)
|
318
|
-
payload = { emails: emails }
|
319
|
-
response = connection.post(remove_testers_from_group_url(project_number, group_alias), payload.to_json) do |request|
|
320
|
-
request.headers[AUTHORIZATION] = "Bearer " + @auth_token
|
321
|
-
request.headers[CONTENT_TYPE] = APPLICATION_JSON
|
322
|
-
request.headers[CLIENT_VERSION] = client_version_header_value
|
323
|
-
end
|
324
|
-
response.body
|
325
|
-
rescue Faraday::BadRequestError
|
326
|
-
UI.user_error!(ErrorMessage::INVALID_EMAIL_ADDRESS)
|
327
|
-
rescue Faraday::ResourceNotFound
|
328
|
-
UI.user_error!(ErrorMessage::INVALID_TESTER_GROUP)
|
329
|
-
rescue Faraday::ClientError => e
|
330
|
-
raise e
|
331
|
-
end
|
332
|
-
|
333
|
-
# Delete tester group
|
334
|
-
#
|
335
|
-
# args
|
336
|
-
# project_number - Firebase project number
|
337
|
-
# group_alias - Alias of the tester group
|
338
|
-
#
|
339
|
-
def delete_group(project_number, group_alias)
|
340
|
-
response = connection.delete(delete_tester_group_url(project_number, group_alias)) do |request|
|
341
|
-
request.headers[AUTHORIZATION] = "Bearer " + @auth_token
|
342
|
-
request.headers[CONTENT_TYPE] = APPLICATION_JSON
|
343
|
-
request.headers[CLIENT_VERSION] = client_version_header_value
|
344
|
-
end
|
345
|
-
response.body
|
346
|
-
rescue Faraday::ResourceNotFound
|
347
|
-
UI.user_error!(ErrorMessage::INVALID_TESTER_GROUP)
|
348
|
-
end
|
349
|
-
|
350
|
-
# List releases
|
351
|
-
#
|
352
|
-
# args
|
353
|
-
# app_name - Firebase App resource name
|
354
|
-
# page_size - The number of releases to return in the page
|
355
|
-
# page_token - A page token, received from a previous call
|
356
|
-
#
|
357
|
-
# Returns the response body. Throws a user_error if the app hasn't been onboarded to App Distribution.
|
358
|
-
def list_releases(app_name, page_size = 100, page_token = nil)
|
359
|
-
begin
|
360
|
-
response = connection.get(list_releases_url(app_name), { pageSize: page_size.to_s, pageToken: page_token }) do |request|
|
361
|
-
request.headers[AUTHORIZATION] = "Bearer " + @auth_token
|
362
|
-
request.headers[CLIENT_VERSION] = client_version_header_value
|
363
|
-
end
|
364
|
-
rescue Faraday::ResourceNotFound
|
365
|
-
UI.user_error!("#{ErrorMessage::INVALID_APP_ID}: #{app_name}")
|
366
|
-
end
|
367
|
-
|
368
|
-
response.body
|
369
|
-
end
|
370
|
-
|
371
37
|
private
|
372
38
|
|
373
39
|
def client_version_header_value
|
374
40
|
"fastlane/#{Fastlane::FirebaseAppDistribution::VERSION}"
|
375
41
|
end
|
376
42
|
|
377
|
-
def v1alpha_apps_url(app_id)
|
378
|
-
"/v1alpha/apps/#{app_id}"
|
379
|
-
end
|
380
|
-
|
381
|
-
def v1_apps_url(app_name)
|
382
|
-
"/v1/#{app_name}"
|
383
|
-
end
|
384
|
-
|
385
|
-
def aab_info_url(app_name)
|
386
|
-
"#{v1_apps_url(app_name)}/aabInfo"
|
387
|
-
end
|
388
|
-
|
389
|
-
def update_release_notes_url(release_name)
|
390
|
-
"/v1/#{release_name}?updateMask=release_notes.text"
|
391
|
-
end
|
392
|
-
|
393
|
-
def distribute_url(release_name)
|
394
|
-
"/v1/#{release_name}:distribute"
|
395
|
-
end
|
396
|
-
|
397
|
-
def binary_upload_url(app_name)
|
398
|
-
"/upload#{v1_apps_url(app_name)}/releases:upload"
|
399
|
-
end
|
400
|
-
|
401
|
-
def upload_status_url(operation_name)
|
402
|
-
"/v1/#{operation_name}"
|
403
|
-
end
|
404
|
-
|
405
|
-
def list_releases_url(app_name)
|
406
|
-
"#{v1_apps_url(app_name)}/releases"
|
407
|
-
end
|
408
|
-
|
409
43
|
def get_udids_url(app_id)
|
410
|
-
"
|
411
|
-
end
|
412
|
-
|
413
|
-
def add_testers_url(project_number)
|
414
|
-
"/v1/projects/#{project_number}/testers:batchAdd"
|
415
|
-
end
|
416
|
-
|
417
|
-
def remove_testers_url(project_number)
|
418
|
-
"/v1/projects/#{project_number}/testers:batchRemove"
|
419
|
-
end
|
420
|
-
|
421
|
-
def add_tester_group_url(project_number)
|
422
|
-
"/v1/projects/#{project_number}/groups"
|
423
|
-
end
|
424
|
-
|
425
|
-
def delete_tester_group_url(project_number, group_alias)
|
426
|
-
"/v1/projects/#{project_number}/groups/#{group_alias}"
|
427
|
-
end
|
428
|
-
|
429
|
-
def add_testers_to_group_url(project_number, group_alias)
|
430
|
-
"/v1/projects/#{project_number}/groups/#{group_alias}:batchJoin"
|
431
|
-
end
|
432
|
-
|
433
|
-
def remove_testers_from_group_url(project_number, group_alias)
|
434
|
-
"/v1/projects/#{project_number}/groups/#{group_alias}:batchLeave"
|
44
|
+
"/v1alpha/apps/#{app_id}/testers:getTesterUdids"
|
435
45
|
end
|
436
46
|
|
437
47
|
def connection
|
@@ -442,11 +52,6 @@ module Fastlane
|
|
442
52
|
conn.adapter(Faraday.default_adapter)
|
443
53
|
end
|
444
54
|
end
|
445
|
-
|
446
|
-
def read_binary(path)
|
447
|
-
# File must be read in binary mode to work on Windows
|
448
|
-
File.open(path, 'rb').read
|
449
|
-
end
|
450
55
|
end
|
451
56
|
end
|
452
57
|
end
|
data/lib/fastlane/plugin/firebase_app_distribution/helper/firebase_app_distribution_auth_client.rb
CHANGED
@@ -1,4 +1,6 @@
|
|
1
|
+
require 'googleauth'
|
1
2
|
require 'fastlane_core/ui/ui'
|
3
|
+
|
2
4
|
module Fastlane
|
3
5
|
UI = FastlaneCore::UI unless Fastlane.const_defined?("UI")
|
4
6
|
module Auth
|
@@ -13,8 +15,8 @@ module Fastlane
|
|
13
15
|
CLIENT_ID = "563584335869-fgrhgmd47bqnekij5i8b5pr03ho849e6.apps.googleusercontent.com"
|
14
16
|
CLIENT_SECRET = "j9iVZfS8kkCEFUPaAeJV0sAi"
|
15
17
|
|
16
|
-
# Returns
|
17
|
-
#
|
18
|
+
# Returns an authorization object for any of the auth methods (Firebase CLI token,
|
19
|
+
# Application Default Credentials, firebase-tools). To ensure that a specific
|
18
20
|
# auth method is used, unset all other auth variables/parameters to nil/empty
|
19
21
|
#
|
20
22
|
# args
|
@@ -23,43 +25,45 @@ module Fastlane
|
|
23
25
|
# debug - Whether to enable debug-level logging
|
24
26
|
#
|
25
27
|
# env variables
|
26
|
-
# GOOGLE_APPLICATION_CREDENTIALS - see google_service_path
|
27
28
|
# FIREBASE_TOKEN - see firebase_cli_token
|
28
29
|
#
|
29
30
|
# Crashes if given invalid or missing credentials
|
30
|
-
def
|
31
|
+
def get_authorization(google_service_path, firebase_cli_token, debug = false)
|
31
32
|
if !google_service_path.nil? && !google_service_path.empty?
|
32
33
|
UI.message("🔐 Authenticating with --service_credentials_file path parameter: #{google_service_path}")
|
33
|
-
|
34
|
+
service_account(google_service_path, debug)
|
34
35
|
elsif !firebase_cli_token.nil? && !firebase_cli_token.empty?
|
35
36
|
UI.message("🔐 Authenticating with --firebase_cli_token parameter")
|
36
|
-
|
37
|
+
firebase_token(firebase_cli_token, debug)
|
37
38
|
elsif !ENV["FIREBASE_TOKEN"].nil? && !ENV["FIREBASE_TOKEN"].empty?
|
38
39
|
UI.message("🔐 Authenticating with FIREBASE_TOKEN environment variable")
|
39
|
-
|
40
|
-
elsif !
|
41
|
-
UI.message("🔐 Authenticating with
|
42
|
-
|
40
|
+
firebase_token(ENV["FIREBASE_TOKEN"], debug)
|
41
|
+
elsif !application_default_creds.nil?
|
42
|
+
UI.message("🔐 Authenticating with Application Default Credentials")
|
43
|
+
application_default_creds
|
43
44
|
elsif (refresh_token = refresh_token_from_firebase_tools)
|
44
|
-
UI.message("🔐 No authentication method
|
45
|
-
|
45
|
+
UI.message("🔐 No authentication method found. Using cached Firebase CLI credentials.")
|
46
|
+
firebase_token(refresh_token, debug)
|
46
47
|
else
|
47
48
|
UI.user_error!(ErrorMessage::MISSING_CREDENTIALS)
|
49
|
+
nil
|
48
50
|
end
|
49
|
-
token
|
50
51
|
end
|
51
52
|
|
52
53
|
private
|
53
54
|
|
55
|
+
def application_default_creds
|
56
|
+
Google::Auth.get_application_default([SCOPE])
|
57
|
+
rescue
|
58
|
+
nil
|
59
|
+
end
|
60
|
+
|
54
61
|
def refresh_token_from_firebase_tools
|
55
62
|
config_path = format_config_path
|
56
63
|
if File.exist?(config_path)
|
57
64
|
begin
|
58
65
|
firebase_tools_tokens = JSON.parse(File.read(config_path))['tokens']
|
59
|
-
if firebase_tools_tokens.nil?
|
60
|
-
UI.user_error!(ErrorMessage::EMPTY_TOKENS_FIELD)
|
61
|
-
return
|
62
|
-
end
|
66
|
+
return if firebase_tools_tokens.nil?
|
63
67
|
refresh_token = firebase_tools_tokens['refresh_token']
|
64
68
|
rescue JSON::ParserError
|
65
69
|
UI.user_error!(ErrorMessage::PARSE_FIREBASE_TOOLS_JSON_ERROR)
|
@@ -84,7 +88,7 @@ module Fastlane
|
|
84
88
|
refresh_token: refresh_token
|
85
89
|
)
|
86
90
|
client.fetch_access_token!
|
87
|
-
client
|
91
|
+
client
|
88
92
|
rescue Signet::AuthorizationError => error
|
89
93
|
error_message = ErrorMessage::REFRESH_TOKEN_ERROR
|
90
94
|
if debug
|
@@ -101,7 +105,8 @@ module Fastlane
|
|
101
105
|
json_key_io: File.open(google_service_path),
|
102
106
|
scope: SCOPE
|
103
107
|
)
|
104
|
-
service_account_credentials.fetch_access_token!
|
108
|
+
service_account_credentials.fetch_access_token!
|
109
|
+
service_account_credentials
|
105
110
|
rescue Errno::ENOENT
|
106
111
|
UI.user_error!("#{ErrorMessage::SERVICE_CREDENTIALS_NOT_FOUND}: #{google_service_path}")
|
107
112
|
rescue Signet::AuthorizationError => error
|
data/lib/fastlane/plugin/firebase_app_distribution/helper/firebase_app_distribution_error_message.rb
CHANGED
@@ -1,5 +1,5 @@
|
|
1
1
|
module ErrorMessage
|
2
|
-
MISSING_CREDENTIALS = "Missing authentication credentials.
|
2
|
+
MISSING_CREDENTIALS = "Missing authentication credentials. Set up Application Default Credentials, your Firebase refresh token, or sign in with the Firebase CLI, and try again."
|
3
3
|
MISSING_APP_ID = "Missing app id. Please check that the app parameter is set and try again"
|
4
4
|
SERVICE_CREDENTIALS_NOT_FOUND = "Service credentials file does not exist. Please check the service credentials path and try again"
|
5
5
|
PARSE_SERVICE_CREDENTIALS_ERROR = "Failed to extract service account information from the service credentials file"
|
@@ -15,7 +15,7 @@ module ErrorMessage
|
|
15
15
|
INVALID_TESTERS = "Could not enable access for testers. Check that the tester emails are formatted correctly, the groups exist and you are using group aliases (not group names) for specifying groups."
|
16
16
|
INVALID_TESTER_GROUP = "App Distribution could not find your tester group. Make sure that it exists before trying to add testers, and that the group alias is specified correctly."
|
17
17
|
INVALID_TESTER_GROUP_NAME = "The tester group name should be 4-63 characters, and valid characters are /[a-z][0-9]-/."
|
18
|
-
INVALID_RELEASE_NOTES = "Failed to
|
18
|
+
INVALID_RELEASE_NOTES = "Failed to set release notes"
|
19
19
|
SERVICE_CREDENTIALS_ERROR = "App Distribution could not generate credentials from the service credentials file specified"
|
20
20
|
PLAY_ACCOUNT_NOT_LINKED = "This project is not linked to a Google Play account."
|
21
21
|
APP_NOT_PUBLISHED = "This app is not published in the Google Play console."
|
@@ -23,7 +23,6 @@ module ErrorMessage
|
|
23
23
|
PLAY_IAS_TERMS_NOT_ACCEPTED = "You must accept the Play Internal App Sharing (IAS) terms to upload AABs."
|
24
24
|
INVALID_EMAIL_ADDRESS = "You passed an invalid email address."
|
25
25
|
TESTER_LIMIT_VIOLATION = "Creating testers would exceed tester limit"
|
26
|
-
EMPTY_TOKENS_FIELD = "Unable to find \"tokens\" field in the firebase-tools.json file. Ensure that the file has a tokens field and try again"
|
27
26
|
|
28
27
|
def self.aab_upload_error(aab_state)
|
29
28
|
"Failed to process the AAB: #{aab_state}"
|
data/lib/fastlane/plugin/firebase_app_distribution/helper/firebase_app_distribution_helper.rb
CHANGED
@@ -27,7 +27,7 @@ module Fastlane
|
|
27
27
|
# Returns the array representation of a string with trimmed comma
|
28
28
|
# seperated values.
|
29
29
|
def string_to_array(string)
|
30
|
-
return
|
30
|
+
return [] if string.nil?
|
31
31
|
# Strip string and then strip individual values
|
32
32
|
string.strip.split(",").map(&:strip)
|
33
33
|
end
|
@@ -54,7 +54,36 @@ module Fastlane
|
|
54
54
|
end
|
55
55
|
|
56
56
|
def app_name_from_app_id(app_id)
|
57
|
-
"
|
57
|
+
"#{project_name(app_id.split(':')[1])}/apps/#{app_id}"
|
58
|
+
end
|
59
|
+
|
60
|
+
def project_name(project_number)
|
61
|
+
"projects/#{project_number}"
|
62
|
+
end
|
63
|
+
|
64
|
+
def group_name(project_number, group_alias)
|
65
|
+
"#{project_name(project_number)}/groups/#{group_alias}"
|
66
|
+
end
|
67
|
+
|
68
|
+
def init_client(service_credentials_file, firebase_cli_token, debug = false)
|
69
|
+
if debug
|
70
|
+
UI.important("Warning: Debug logging enabled. Output may include sensitive information.")
|
71
|
+
Google::Apis.logger.level = Logger::DEBUG
|
72
|
+
end
|
73
|
+
|
74
|
+
Google::Apis::ClientOptions.default.application_name = "fastlane"
|
75
|
+
Google::Apis::ClientOptions.default.application_version = Fastlane::FirebaseAppDistribution::VERSION
|
76
|
+
client = Google::Apis::FirebaseappdistributionV1::FirebaseAppDistributionService.new
|
77
|
+
client.authorization = get_authorization(service_credentials_file, firebase_cli_token)
|
78
|
+
client
|
79
|
+
end
|
80
|
+
|
81
|
+
def deep_symbolize_keys(hash)
|
82
|
+
result = {}
|
83
|
+
hash.each do |key, value|
|
84
|
+
result[key.to_sym] = value.kind_of?(Hash) ? deep_symbolize_keys(value) : value
|
85
|
+
end
|
86
|
+
result
|
58
87
|
end
|
59
88
|
end
|
60
89
|
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: fastlane-plugin-firebase_app_distribution
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.7.0.pre.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Stefan Natchev
|
@@ -10,8 +10,36 @@ authors:
|
|
10
10
|
autorequire:
|
11
11
|
bindir: bin
|
12
12
|
cert_chain: []
|
13
|
-
date: 2023-
|
13
|
+
date: 2023-07-26 00:00:00.000000000 Z
|
14
14
|
dependencies:
|
15
|
+
- !ruby/object:Gem::Dependency
|
16
|
+
name: google-api-client
|
17
|
+
requirement: !ruby/object:Gem::Requirement
|
18
|
+
requirements:
|
19
|
+
- - "~>"
|
20
|
+
- !ruby/object:Gem::Version
|
21
|
+
version: '0.38'
|
22
|
+
type: :runtime
|
23
|
+
prerelease: false
|
24
|
+
version_requirements: !ruby/object:Gem::Requirement
|
25
|
+
requirements:
|
26
|
+
- - "~>"
|
27
|
+
- !ruby/object:Gem::Version
|
28
|
+
version: '0.38'
|
29
|
+
- !ruby/object:Gem::Dependency
|
30
|
+
name: google-apis-firebaseappdistribution_v1
|
31
|
+
requirement: !ruby/object:Gem::Requirement
|
32
|
+
requirements:
|
33
|
+
- - "~>"
|
34
|
+
- !ruby/object:Gem::Version
|
35
|
+
version: 0.3.0
|
36
|
+
type: :runtime
|
37
|
+
prerelease: false
|
38
|
+
version_requirements: !ruby/object:Gem::Requirement
|
39
|
+
requirements:
|
40
|
+
- - "~>"
|
41
|
+
- !ruby/object:Gem::Version
|
42
|
+
version: 0.3.0
|
15
43
|
- !ruby/object:Gem::Dependency
|
16
44
|
name: pry
|
17
45
|
requirement: !ruby/object:Gem::Requirement
|
@@ -158,13 +186,10 @@ files:
|
|
158
186
|
- lib/fastlane/plugin/firebase_app_distribution/actions/firebase_app_distribution_get_latest_release.rb
|
159
187
|
- lib/fastlane/plugin/firebase_app_distribution/actions/firebase_app_distribution_get_udids.rb
|
160
188
|
- lib/fastlane/plugin/firebase_app_distribution/actions/firebase_app_distribution_remove_testers_action.rb
|
161
|
-
- lib/fastlane/plugin/firebase_app_distribution/client/aab_info.rb
|
162
|
-
- lib/fastlane/plugin/firebase_app_distribution/client/error_response.rb
|
163
189
|
- lib/fastlane/plugin/firebase_app_distribution/client/firebase_app_distribution_api_client.rb
|
164
190
|
- lib/fastlane/plugin/firebase_app_distribution/helper/firebase_app_distribution_auth_client.rb
|
165
191
|
- lib/fastlane/plugin/firebase_app_distribution/helper/firebase_app_distribution_error_message.rb
|
166
192
|
- lib/fastlane/plugin/firebase_app_distribution/helper/firebase_app_distribution_helper.rb
|
167
|
-
- lib/fastlane/plugin/firebase_app_distribution/helper/upload_status_response.rb
|
168
193
|
- lib/fastlane/plugin/firebase_app_distribution/version.rb
|
169
194
|
homepage: https://github.com/fastlane/fastlane-plugin-firebase_app_distribution
|
170
195
|
licenses:
|
@@ -181,9 +206,9 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
181
206
|
version: '0'
|
182
207
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
183
208
|
requirements:
|
184
|
-
- - "
|
209
|
+
- - ">"
|
185
210
|
- !ruby/object:Gem::Version
|
186
|
-
version:
|
211
|
+
version: 1.3.1
|
187
212
|
requirements: []
|
188
213
|
rubygems_version: 3.4.10
|
189
214
|
signing_key:
|