fastlane-plugin-firebase_app_distribution 0.8.0 → 0.9.0.pre.1

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 825076928662d5ad48eb2ad1990c68952c93785d118c46911a5aa5cdc365cc03
4
- data.tar.gz: 6ac4fe07a6460242f75e08f13bb57cf2079365f89bca7c981d82574908947904
3
+ metadata.gz: 02dd5d9fe833cb902ea3f91b4124d4ba8ee7add6036e57da2cd16c6a79f7e503
4
+ data.tar.gz: b3c4d2740c8ab4a800554dae193068ffeea1d914f526005779be8792997a6d36
5
5
  SHA512:
6
- metadata.gz: 457efecae9d8b5e9b2462f8f0557de75998d077aab9dd2177105d9ab0b958c1f13a7a53dada5ec156d08a8c330cae70c6a3d9b5d8cd5752721e95632571d6893
7
- data.tar.gz: b8a4603cafe2e129b942a28a3d739d35048972bd8a726db4fc56295aa907703c66622563de59ca3910ee8a32a7ba89d2c2f276b7caa77ea20caaab093def915a
6
+ metadata.gz: 74bd1a05693eccb0c711208af34d8ae2d48bf3ed2cc623b754eb284c8337f6d78280583587e0ac5aaf892726ad9e266b160563eb8ae26df1fd4e4540b1c34dcc
7
+ data.tar.gz: d6de7b9d5154851e14ebba73a95ca98e2fab0bbf6895220715f5ee5ee089bb0cf4cdf5968f4d47b4c83b38dfcb7cde86228c250cfd68e1296e42685b0c1cc553
@@ -2,6 +2,7 @@ require 'fastlane/action'
2
2
  require 'open3'
3
3
  require 'shellwords'
4
4
  require 'googleauth'
5
+ require 'google/apis/firebaseappdistribution_v1'
5
6
  require_relative '../helper/firebase_app_distribution_helper'
6
7
  require_relative '../helper/firebase_app_distribution_error_message'
7
8
  require_relative '../helper/firebase_app_distribution_auth_client'
@@ -19,8 +20,10 @@ module Fastlane
19
20
  extend Helper::FirebaseAppDistributionHelper
20
21
 
21
22
  DEFAULT_UPLOAD_TIMEOUT_SECONDS = 300
22
- MAX_POLLING_RETRIES = 60
23
- POLLING_INTERVAL_SECONDS = 5
23
+ UPLOAD_MAX_POLLING_RETRIES = 60
24
+ UPLOAD_POLLING_INTERVAL_SECONDS = 5
25
+ TEST_MAX_POLLING_RETRIES = 25
26
+ TEST_POLLING_INTERVAL_SECONDS = 30
24
27
 
25
28
  def self.run(params)
26
29
  params.values # to validate all inputs before looking for the ipa/apk/aab
@@ -31,16 +34,18 @@ module Fastlane
31
34
  timeout = get_upload_timeout(params)
32
35
 
33
36
  binary_path = get_binary_path(platform, params)
34
- UI.user_error!("Couldn't find binary") if binary_path.nil?
35
- UI.user_error!("Couldn't find binary at path #{binary_path}") unless File.exist?(binary_path)
37
+ UI.user_error!("Couldn't find binary.") if binary_path.nil?
38
+ UI.user_error!("Couldn't find binary at path #{binary_path}.") unless File.exist?(binary_path)
36
39
  binary_type = binary_type_from_path(binary_path)
37
40
 
38
41
  # TODO(lkellogg): This sets the send timeout for all POST requests made by the client, but
39
- # ideally the timeout should only apply to the binary upload
40
- client = init_v1_client(params[:service_credentials_file],
41
- params[:firebase_cli_token],
42
- params[:debug],
43
- timeout)
42
+ # ideally the timeout should only apply to the binary upload
43
+ init_google_api_client(params[:debug], timeout)
44
+ authorization = get_authorization(params[:service_credentials_file], params[:firebase_cli_token], params[:service_credentials_json_data], params[:debug])
45
+ client = Google::Apis::FirebaseappdistributionV1::FirebaseAppDistributionService.new
46
+ client.authorization = authorization
47
+ alpha_client = Google::Apis::FirebaseappdistributionV1alpha::FirebaseAppDistributionService.new
48
+ alpha_client.authorization = authorization
44
49
 
45
50
  # If binary is an AAB, get the AAB info for this app, which includes the integration state
46
51
  # and certificate data
@@ -50,9 +55,9 @@ module Fastlane
50
55
  end
51
56
 
52
57
  binary_type = binary_type_from_path(binary_path)
53
- UI.message(" Uploading the #{binary_type}.")
58
+ UI.message("📡 Uploading the #{binary_type}.")
54
59
  operation = upload_binary(app_name, binary_path, client, timeout)
55
- UI.message("🕵️ Validating upload.")
60
+ UI.message("🕵️ Validating upload")
56
61
  release = poll_upload_release_operation(client, operation, binary_type)
57
62
 
58
63
  if binary_type == :AAB && aab_info && !aab_certs_included?(aab_info.test_certificate)
@@ -79,6 +84,16 @@ module Fastlane
79
84
  release = update_release(client, release)
80
85
  end
81
86
 
87
+ test_devices =
88
+ get_value_from_value_or_file(params[:test_devices], params[:test_devices_file])
89
+ if present?(test_devices)
90
+ UI.message("🤖 Starting automated tests.")
91
+ release_test = test_release(alpha_client, release, test_devices, params[:test_username], params[:test_password], params[:test_username_resource], params[:test_password_resource])
92
+ unless params[:test_async]
93
+ poll_test_finished(alpha_client, release_test.name)
94
+ end
95
+ end
96
+
82
97
  testers = get_value_from_value_or_file(params[:testers], params[:testers_file])
83
98
  groups = get_value_from_value_or_file(params[:groups], params[:groups_file])
84
99
  emails = string_to_array(testers)
@@ -214,24 +229,22 @@ module Fastlane
214
229
  end
215
230
 
216
231
  def self.poll_upload_release_operation(client, operation, binary_type)
217
- operation = client.get_project_app_release_operation(operation.name)
218
- MAX_POLLING_RETRIES.times do
232
+ UPLOAD_MAX_POLLING_RETRIES.times do
233
+ sleep(UPLOAD_POLLING_INTERVAL_SECONDS)
234
+ operation = client.get_project_app_release_operation(operation.name)
219
235
  if operation.done && operation.response && operation.response['release']
220
236
  release = extract_release(operation)
221
- result = operation.response['result']
222
- if result == 'RELEASE_UPDATED'
237
+ case operation.response['result']
238
+ when 'RELEASE_UPDATED'
223
239
  UI.success("✅ Uploaded #{binary_type} successfully; updated provisioning profile of existing release #{release_version(release)}.")
224
- break
225
- elsif result == 'RELEASE_UNMODIFIED'
240
+ when 'RELEASE_UNMODIFIED'
226
241
  UI.success("✅ The same #{binary_type} was found in release #{release_version(release)} with no changes, skipping.")
227
- break
228
242
  else
229
243
  UI.success("✅ Uploaded #{binary_type} successfully and created release #{release_version(release)}.")
230
244
  end
231
245
  break
232
246
  elsif !operation.done
233
- sleep(POLLING_INTERVAL_SECONDS)
234
- operation = client.get_project_app_release_operation(operation.name)
247
+ next
235
248
  else
236
249
  if operation.error && operation.error.message
237
250
  UI.user_error!("#{ErrorMessage.upload_binary_error(binary_type)}: #{operation.error.message}")
@@ -261,7 +274,7 @@ module Fastlane
261
274
  # it should return a long running operation object, so we make a
262
275
  # standard http call instead and convert it to a long running object
263
276
  # https://github.com/googleapis/google-api-ruby-client/blob/main/generated/google-apis-firebaseappdistribution_v1/lib/google/apis/firebaseappdistribution_v1/service.rb#L79
264
- # TODO(kbolay) Prefer client.upload_medium
277
+ # TODO(kbolay): Prefer client.upload_medium
265
278
  response = client.http(
266
279
  :post,
267
280
  "https://firebaseappdistribution.googleapis.com/upload/v1/#{app_name}/releases:upload",
@@ -319,6 +332,99 @@ module Fastlane
319
332
  end
320
333
  end
321
334
 
335
+ def self.test_release(alpha_client, release, test_devices, username = nil, password = nil, username_resource = nil, password_resource = nil)
336
+ if username_resource.nil? ^ password_resource.nil?
337
+ UI.user_error!("Username and password resource names for automated tests need to be specified together.")
338
+ end
339
+ field_hints = nil
340
+ if !username_resource.nil? && !password_resource.nil?
341
+ field_hints =
342
+ Google::Apis::FirebaseappdistributionV1alpha::GoogleFirebaseAppdistroV1alphaLoginCredentialFieldHints.new(
343
+ username_resource_name: username_resource,
344
+ password_resource_name: password_resource
345
+ )
346
+ end
347
+
348
+ if username.nil? ^ password.nil?
349
+ UI.user_error!("Username and password for automated tests need to be specified together.")
350
+ end
351
+ login_credential = nil
352
+ if !username.nil? && !password.nil?
353
+ login_credential =
354
+ Google::Apis::FirebaseappdistributionV1alpha::GoogleFirebaseAppdistroV1alphaLoginCredential.new(
355
+ username: username,
356
+ password: password,
357
+ field_hints: field_hints
358
+ )
359
+ else
360
+ unless field_hints.nil?
361
+ UI.user_error!("Must specify username and password for automated tests if resource names are set.")
362
+ end
363
+ end
364
+
365
+ device_executions = string_to_array(test_devices, ';').map do |td_string|
366
+ td_hash = parse_test_device_string(td_string)
367
+ Google::Apis::FirebaseappdistributionV1alpha::GoogleFirebaseAppdistroV1alphaDeviceExecution.new(
368
+ device: Google::Apis::FirebaseappdistributionV1alpha::GoogleFirebaseAppdistroV1alphaTestDevice.new(
369
+ model: td_hash['model'],
370
+ version: td_hash['version'],
371
+ orientation: td_hash['orientation'],
372
+ locale: td_hash['locale']
373
+ )
374
+ )
375
+ end
376
+
377
+ release_test =
378
+ Google::Apis::FirebaseappdistributionV1alpha::GoogleFirebaseAppdistroV1alphaReleaseTest.new(
379
+ login_credential: login_credential,
380
+ device_executions: device_executions
381
+ )
382
+ alpha_client.create_project_app_release_test(release.name, release_test)
383
+ rescue Google::Apis::Error => err
384
+ UI.crash!(err)
385
+ end
386
+
387
+ def self.poll_test_finished(alpha_client, release_test_name)
388
+ TEST_MAX_POLLING_RETRIES.times do
389
+ UI.message("⏳ Waiting for test(s) to complete…")
390
+ sleep(TEST_POLLING_INTERVAL_SECONDS)
391
+ release_test = alpha_client.get_project_app_release_test(release_test_name)
392
+ if release_test.device_executions.all? { |e| e.state == 'PASSED' }
393
+ UI.success("✅ Passed automated test(s).")
394
+ return
395
+ end
396
+ release_test.device_executions.each do |de|
397
+ case de.state
398
+ when 'PASSED', 'IN_PROGRESS'
399
+ next
400
+ when 'FAILED'
401
+ UI.test_failure!("Automated test failed for #{device_to_s(de.device)}: #{de.failed_reason}.")
402
+ when 'INCONCLUSIVE'
403
+ UI.test_failure!("Automated test inconclusive for #{device_to_s(de.device)}: #{de.inconclusive_reason}.")
404
+ else
405
+ UI.test_failure!("Unsupported automated test state for #{device_to_s(de.device)}: #{de.state}.")
406
+ end
407
+ end
408
+ end
409
+ UI.test_failure!("Tests are running longer than expected.")
410
+ end
411
+
412
+ def self.parse_test_device_string(td_string)
413
+ allowed_keys = %w[model version locale orientation]
414
+ key_value_pairs = td_string.split(',').map do |key_value_string|
415
+ key, value = key_value_string.split('=')
416
+ unless allowed_keys.include?(key)
417
+ UI.user_error!("Unrecognized key in test_devices. Can only contain keys #{allowed_keys.join(', ')}.")
418
+ end
419
+ [key, value]
420
+ end
421
+ Hash[key_value_pairs]
422
+ end
423
+
424
+ def self.device_to_s(device)
425
+ "#{device.model} (#{device.version}/#{device.orientation}/#{device.locale})"
426
+ end
427
+
322
428
  def self.available_options
323
429
  [
324
430
  # iOS Specific
@@ -350,7 +456,7 @@ module Fastlane
350
456
  verify_block: proc do |value|
351
457
  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)
352
458
  end),
353
- # Generic
459
+ # General
354
460
  FastlaneCore::ConfigItem.new(key: :app,
355
461
  env_name: "FIREBASEAPPDISTRO_APP",
356
462
  description: "Your app's Firebase App ID. You can find the App ID in the Firebase console, on the General Settings page",
@@ -359,26 +465,38 @@ module Fastlane
359
465
  FastlaneCore::ConfigItem.new(key: :firebase_cli_path,
360
466
  deprecated: "This plugin no longer uses the Firebase CLI",
361
467
  env_name: "FIREBASEAPPDISTRO_FIREBASE_CLI_PATH",
362
- description: "The absolute path of the firebase cli command",
468
+ description: "Absolute path of the Firebase CLI command",
363
469
  type: String),
470
+ FastlaneCore::ConfigItem.new(key: :debug,
471
+ description: "Print verbose debug output",
472
+ optional: true,
473
+ default_value: false,
474
+ type: Boolean),
475
+
476
+ # Release Distribution
477
+ FastlaneCore::ConfigItem.new(key: :upload_timeout,
478
+ description: "Amount of seconds before the upload will timeout, if not completed",
479
+ optional: true,
480
+ default_value: DEFAULT_UPLOAD_TIMEOUT_SECONDS,
481
+ type: Integer),
364
482
  FastlaneCore::ConfigItem.new(key: :groups,
365
483
  env_name: "FIREBASEAPPDISTRO_GROUPS",
366
- description: "The group aliases used for distribution, separated by commas",
484
+ description: "Group aliases used for distribution, separated by commas",
367
485
  optional: true,
368
486
  type: String),
369
487
  FastlaneCore::ConfigItem.new(key: :groups_file,
370
488
  env_name: "FIREBASEAPPDISTRO_GROUPS_FILE",
371
- description: "The group aliases used for distribution, separated by commas",
489
+ description: "Path to file containing group aliases used for distribution, separated by commas",
372
490
  optional: true,
373
491
  type: String),
374
492
  FastlaneCore::ConfigItem.new(key: :testers,
375
493
  env_name: "FIREBASEAPPDISTRO_TESTERS",
376
- description: "Pass email addresses of testers, separated by commas",
494
+ description: "Email addresses of testers, separated by commas",
377
495
  optional: true,
378
496
  type: String),
379
497
  FastlaneCore::ConfigItem.new(key: :testers_file,
380
498
  env_name: "FIREBASEAPPDISTRO_TESTERS_FILE",
381
- description: "Pass email addresses of testers, separated by commas",
499
+ description: "Path to file containing email addresses of testers, separated by commas",
382
500
  optional: true,
383
501
  type: String),
384
502
  FastlaneCore::ConfigItem.new(key: :release_notes,
@@ -388,27 +506,56 @@ module Fastlane
388
506
  type: String),
389
507
  FastlaneCore::ConfigItem.new(key: :release_notes_file,
390
508
  env_name: "FIREBASEAPPDISTRO_RELEASE_NOTES_FILE",
391
- description: "Release notes file for this build",
509
+ description: "Path to file containing release notes for this build",
392
510
  optional: true,
393
511
  type: String),
394
- FastlaneCore::ConfigItem.new(key: :firebase_cli_token,
395
- description: "Auth token generated using the Firebase CLI's login:ci command",
512
+
513
+ # Release Testing
514
+ FastlaneCore::ConfigItem.new(key: :test_devices,
515
+ env_name: "FIREBASEAPPDISTRO_TEST_DEVICES",
516
+ description: "List of devices to run automated tests on, in the format 'model=<model-id>,version=<os-version-id>,locale=<locale>,orientation=<orientation>;model=<model-id>,...'. Run 'gcloud firebase test android|ios models list' to see available devices",
396
517
  optional: true,
397
518
  type: String),
398
- FastlaneCore::ConfigItem.new(key: :debug,
399
- description: "Print verbose debug output",
519
+ FastlaneCore::ConfigItem.new(key: :test_devices_file,
520
+ env_name: "FIREBASEAPPDISTRO_TEST_DEVICES_FILE",
521
+ description: "Path to file containing a list of devices to run automated tests on, in the format 'model=<model-id>,version=<os-version-id>,locale=<locale>,orientation=<orientation>;model=<model-id>,...'. Run 'gcloud firebase test android|ios models list' to see available devices",
522
+ optional: true,
523
+ type: String),
524
+ FastlaneCore::ConfigItem.new(key: :test_username,
525
+ description: "Username for automatic login",
400
526
  optional: true,
527
+ type: String),
528
+ FastlaneCore::ConfigItem.new(key: :test_password,
529
+ description: "Password for automatic login",
530
+ optional: true,
531
+ type: String),
532
+ FastlaneCore::ConfigItem.new(key: :test_username_resource,
533
+ description: "Resource name of the username field for automatic login",
534
+ optional: true,
535
+ type: String),
536
+ FastlaneCore::ConfigItem.new(key: :test_password_resource,
537
+ description: "Resource name of the password field for automatic login",
538
+ optional: true,
539
+ type: String),
540
+ FastlaneCore::ConfigItem.new(key: :test_async,
541
+ description: "Don't wait for automatic test results",
542
+ optional: false,
401
543
  default_value: false,
402
- is_string: false),
544
+ type: Boolean),
545
+
546
+ # Auth
547
+ FastlaneCore::ConfigItem.new(key: :firebase_cli_token,
548
+ description: "Auth token generated using the Firebase CLI's login:ci command",
549
+ optional: true,
550
+ type: String),
403
551
  FastlaneCore::ConfigItem.new(key: :service_credentials_file,
404
- description: "Path to Google service account json",
552
+ description: "Path to Google service account json file",
405
553
  optional: true,
406
554
  type: String),
407
- FastlaneCore::ConfigItem.new(key: :upload_timeout,
408
- description: "The amount of seconds before the upload will timeout, if not completed",
555
+ FastlaneCore::ConfigItem.new(key: :service_credentials_json_data,
556
+ description: "Google service account json file content",
409
557
  optional: true,
410
- default_value: DEFAULT_UPLOAD_TIMEOUT_SECONDS,
411
- type: Integer)
558
+ type: String)
412
559
  ]
413
560
  end
414
561
 
@@ -425,7 +572,8 @@ module Fastlane
425
572
  <<-CODE
426
573
  firebase_app_distribution(
427
574
  app: "<your Firebase app ID>",
428
- testers: "snatchev@google.com, rebeccahe@google.com"
575
+ testers: "snatchev@google.com, rebeccahe@google.com",
576
+ test_devices: "model=shiba,version=34,locale=en,orientation=portrait;model=b0q,version=33,locale=en,orientation=portrait",
429
577
  )
430
578
  CODE
431
579
  ]
@@ -1,6 +1,6 @@
1
1
  require 'fastlane/action'
2
2
  require 'fastlane_core/ui/ui'
3
-
3
+ require 'google/apis/firebaseappdistribution_v1'
4
4
  require_relative '../helper/firebase_app_distribution_helper'
5
5
  require_relative '../helper/firebase_app_distribution_auth_client'
6
6
 
@@ -11,7 +11,9 @@ module Fastlane
11
11
  extend Helper::FirebaseAppDistributionHelper
12
12
 
13
13
  def self.run(params)
14
- client = init_v1_client(params[:service_credentials_file], params[:firebase_cli_token], params[:debug])
14
+ init_google_api_client(params[:debug])
15
+ client = Google::Apis::FirebaseappdistributionV1::FirebaseAppDistributionService.new
16
+ client.authorization = get_authorization(params[:service_credentials_file], params[:firebase_cli_token], params[:service_credentials_json_data], params[:debug])
15
17
 
16
18
  if blank?(params[:emails]) && blank?(params[:file])
17
19
  UI.user_error!("Must specify `emails` or `file`.")
@@ -78,6 +80,10 @@ module Fastlane
78
80
  description: "Path to Google service credentials file",
79
81
  optional: true,
80
82
  type: String),
83
+ FastlaneCore::ConfigItem.new(key: :service_credentials_json_data,
84
+ description: "Google service account json file content",
85
+ optional: true,
86
+ type: String),
81
87
  FastlaneCore::ConfigItem.new(key: :firebase_cli_token,
82
88
  description: "Auth token generated using the Firebase CLI's login:ci command",
83
89
  optional: true,
@@ -1,7 +1,6 @@
1
1
  require 'fastlane/action'
2
2
  require 'fastlane_core/ui/ui'
3
3
  require 'google/apis/firebaseappdistribution_v1'
4
-
5
4
  require_relative '../helper/firebase_app_distribution_helper'
6
5
  require_relative '../helper/firebase_app_distribution_auth_client'
7
6
 
@@ -20,7 +19,9 @@ module Fastlane
20
19
  UI.user_error!("Must specify `display_name`.")
21
20
  end
22
21
 
23
- client = init_v1_client(params[:service_credentials_file], params[:firebase_cli_token], params[:debug])
22
+ init_google_api_client(params[:debug])
23
+ client = Google::Apis::FirebaseappdistributionV1::FirebaseAppDistributionService.new
24
+ client.authorization = get_authorization(params[:service_credentials_file], params[:firebase_cli_token], params[:service_credentials_json_data], params[:debug])
24
25
 
25
26
  project_number = params[:project_number]
26
27
  group_alias = params[:alias]
@@ -86,6 +87,10 @@ module Fastlane
86
87
  description: "Path to Google service credentials file",
87
88
  optional: true,
88
89
  type: String),
90
+ FastlaneCore::ConfigItem.new(key: :service_credentials_json_data,
91
+ description: "Google service account json file content",
92
+ optional: true,
93
+ type: String),
89
94
  FastlaneCore::ConfigItem.new(key: :firebase_cli_token,
90
95
  description: "Auth token generated using the Firebase CLI's login:ci command",
91
96
  optional: true,
@@ -1,6 +1,6 @@
1
1
  require 'fastlane/action'
2
2
  require 'fastlane_core/ui/ui'
3
-
3
+ require 'google/apis/firebaseappdistribution_v1'
4
4
  require_relative '../helper/firebase_app_distribution_helper'
5
5
  require_relative '../helper/firebase_app_distribution_auth_client'
6
6
 
@@ -11,7 +11,9 @@ module Fastlane
11
11
  extend Helper::FirebaseAppDistributionHelper
12
12
 
13
13
  def self.run(params)
14
- client = init_v1_client(params[:service_credentials_file], params[:firebase_cli_token], params[:debug])
14
+ init_google_api_client(params[:debug])
15
+ client = Google::Apis::FirebaseappdistributionV1::FirebaseAppDistributionService.new
16
+ client.authorization = get_authorization(params[:service_credentials_file], params[:firebase_cli_token], params[:service_credentials_json_data], params[:debug])
15
17
 
16
18
  if blank?(params[:alias])
17
19
  UI.user_error!("Must specify `alias`.")
@@ -65,6 +67,10 @@ module Fastlane
65
67
  description: "Path to Google service credentials file",
66
68
  optional: true,
67
69
  type: String),
70
+ FastlaneCore::ConfigItem.new(key: :service_credentials_json_data,
71
+ description: "Google service account json file content",
72
+ optional: true,
73
+ type: String),
68
74
  FastlaneCore::ConfigItem.new(key: :firebase_cli_token,
69
75
  description: "Auth token generated using the Firebase CLI's login:ci command",
70
76
  optional: true,
@@ -13,7 +13,9 @@ module Fastlane
13
13
  extend Helper::FirebaseAppDistributionHelper
14
14
 
15
15
  def self.run(params)
16
- client = init_v1_client(params[:service_credentials_file], params[:firebase_cli_token], params[:debug])
16
+ init_google_api_client(params[:debug])
17
+ client = Google::Apis::FirebaseappdistributionV1::FirebaseAppDistributionService.new
18
+ client.authorization = get_authorization(params[:service_credentials_file], params[:firebase_cli_token], params[:service_credentials_json_data], params[:debug])
17
19
 
18
20
  UI.message("⏳ Fetching latest release for app #{params[:app]}...")
19
21
 
@@ -89,6 +91,10 @@ module Fastlane
89
91
  description: "Path to Google service account json",
90
92
  optional: true,
91
93
  type: String),
94
+ FastlaneCore::ConfigItem.new(key: :service_credentials_json_data,
95
+ description: "Google service account json file content",
96
+ optional: true,
97
+ type: String),
92
98
  FastlaneCore::ConfigItem.new(key: :debug,
93
99
  description: "Print verbose debug output",
94
100
  optional: true,
@@ -2,6 +2,7 @@ require 'fastlane/action'
2
2
  require 'open3'
3
3
  require 'shellwords'
4
4
  require 'googleauth'
5
+ require 'google/apis/firebaseappdistribution_v1alpha'
5
6
  require_relative '../helper/firebase_app_distribution_helper'
6
7
  require_relative '../helper/firebase_app_distribution_error_message'
7
8
  require_relative '../helper/firebase_app_distribution_auth_client'
@@ -13,7 +14,9 @@ module Fastlane
13
14
  extend Helper::FirebaseAppDistributionHelper
14
15
 
15
16
  def self.run(params)
16
- client = init_v1alpha_client(params[:service_credentials_file], params[:firebase_cli_token], params[:debug])
17
+ init_google_api_client(params[:debug])
18
+ client = Google::Apis::FirebaseappdistributionV1alpha::FirebaseAppDistributionService.new
19
+ client.authorization = get_authorization(params[:service_credentials_file], params[:firebase_cli_token], params[:service_credentials_json_data], params[:debug])
17
20
 
18
21
  project_number = params[:project_number]
19
22
  if blank?(project_number)
@@ -83,6 +86,10 @@ module Fastlane
83
86
  description: "Path to Google service account json",
84
87
  optional: true,
85
88
  type: String),
89
+ FastlaneCore::ConfigItem.new(key: :service_credentials_json_data,
90
+ description: "Google service account json file content",
91
+ optional: true,
92
+ type: String),
86
93
  FastlaneCore::ConfigItem.new(key: :debug,
87
94
  description: "Print verbose debug output",
88
95
  optional: true,
@@ -1,6 +1,6 @@
1
1
  require 'fastlane/action'
2
2
  require 'fastlane_core/ui/ui'
3
-
3
+ require 'google/apis/firebaseappdistribution_v1'
4
4
  require_relative '../helper/firebase_app_distribution_helper'
5
5
  require_relative '../helper/firebase_app_distribution_auth_client'
6
6
 
@@ -11,7 +11,9 @@ module Fastlane
11
11
  extend Helper::FirebaseAppDistributionHelper
12
12
 
13
13
  def self.run(params)
14
- client = init_v1_client(params[:service_credentials_file], params[:firebase_cli_token], params[:debug])
14
+ init_google_api_client(params[:debug])
15
+ client = Google::Apis::FirebaseappdistributionV1::FirebaseAppDistributionService.new
16
+ client.authorization = get_authorization(params[:service_credentials_file], params[:firebase_cli_token], params[:service_credentials_json_data], params[:debug])
15
17
 
16
18
  if blank?(params[:emails]) && blank?(params[:file])
17
19
  UI.user_error!("Must specify `emails` or `file`.")
@@ -73,6 +75,10 @@ module Fastlane
73
75
  description: "Path to Google service credentials file",
74
76
  optional: true,
75
77
  type: String),
78
+ FastlaneCore::ConfigItem.new(key: :service_credentials_json_data,
79
+ description: "Google service account json file content",
80
+ optional: true,
81
+ type: String),
76
82
  FastlaneCore::ConfigItem.new(key: :firebase_cli_token,
77
83
  description: "Auth token generated using the Firebase CLI's login:ci command",
78
84
  optional: true,
@@ -20,6 +20,7 @@ module Fastlane
20
20
  # auth method is used, unset all other auth variables/parameters to nil/empty
21
21
  #
22
22
  # args
23
+ # google_service_json_data - Google service account json file content as a string
23
24
  # google_service_path - Absolute path to the Google service account file
24
25
  # firebase_cli_token - Refresh token
25
26
  # debug - Whether to enable debug-level logging
@@ -28,10 +29,13 @@ module Fastlane
28
29
  # FIREBASE_TOKEN - see firebase_cli_token
29
30
  #
30
31
  # Crashes if given invalid or missing credentials
31
- def get_authorization(google_service_path, firebase_cli_token, debug = false)
32
+ def get_authorization(google_service_path, firebase_cli_token, google_service_json_data, debug = false)
32
33
  if !google_service_path.nil? && !google_service_path.empty?
33
34
  UI.message("🔐 Authenticating with --service_credentials_file path parameter: #{google_service_path}")
34
- service_account(google_service_path, debug)
35
+ service_account_from_file(google_service_path, debug)
36
+ elsif !google_service_json_data.nil? && !google_service_json_data.empty?
37
+ UI.message("🔐 Authenticating with --service_credentials_json content parameter")
38
+ service_account_from_json(google_service_json_data, debug)
35
39
  elsif !firebase_cli_token.nil? && !firebase_cli_token.empty?
36
40
  UI.message("🔐 Authenticating with --firebase_cli_token parameter")
37
41
  firebase_token(firebase_cli_token, debug)
@@ -100,24 +104,32 @@ module Fastlane
100
104
  UI.user_error!(error_message)
101
105
  end
102
106
 
103
- def service_account(google_service_path, debug)
107
+ def service_account_from_json(google_service_json_data, debug)
108
+ get_service_account_credentials(google_service_json_data, debug)
109
+ end
110
+
111
+ def service_account_from_file(google_service_path, debug)
112
+ get_service_account_credentials(File.read(google_service_path), debug)
113
+ rescue Errno::ENOENT
114
+ UI.user_error!("#{ErrorMessage::SERVICE_CREDENTIALS_NOT_FOUND}: #{google_service_path}")
115
+ end
116
+
117
+ def get_service_account_credentials(json_data, debug)
118
+ json_file = JSON.parse(json_data)
104
119
  # check if it's an external account or service account
105
- json_file = JSON.parse(File.read(google_service_path))
106
120
  auth = json_file["type"] == "external_account" ? Google::Auth::ExternalAccount::Credentials : Google::Auth::ServiceAccountCredentials
107
121
  service_account_credentials = auth.make_creds(
108
- json_key_io: File.open(google_service_path),
122
+ json_key_io: StringIO.new(json_data),
109
123
  scope: SCOPE
110
124
  )
111
125
  service_account_credentials.fetch_access_token!
112
126
  service_account_credentials
113
- rescue Errno::ENOENT
114
- UI.user_error!("#{ErrorMessage::SERVICE_CREDENTIALS_NOT_FOUND}: #{google_service_path}")
115
127
  rescue Signet::AuthorizationError => error
116
- error_message = "#{ErrorMessage::SERVICE_CREDENTIALS_ERROR}: \"#{google_service_path}\""
128
+ error_message = "#{ErrorMessage::SERVICE_CREDENTIALS_ERROR}: "
117
129
  if debug
118
130
  error_message += "\n#{error_details(error)}"
119
131
  else
120
- error_message += ". #{debug_instructions}"
132
+ error_message += debug_instructions.to_s
121
133
  end
122
134
  UI.user_error!(error_message)
123
135
  end
@@ -1,46 +1,46 @@
1
1
  module ErrorMessage
2
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
- MISSING_APP_ID = "Missing app id. Please check that the app parameter is set and try again"
4
- SERVICE_CREDENTIALS_NOT_FOUND = "Service credentials file does not exist. Please check the service credentials path and try again"
5
- PARSE_SERVICE_CREDENTIALS_ERROR = "Failed to extract service account information from the service credentials file"
6
- PARSE_FIREBASE_TOOLS_JSON_ERROR = "Encountered error parsing json file. Ensure the firebase-tools.json file is formatted correctly"
7
- UPLOAD_RELEASE_NOTES_ERROR = "App Distribution halted because it had a problem uploading release notes"
8
- UPLOAD_TESTERS_ERROR = "App Distribution halted because it had a problem adding testers/groups"
9
- GET_RELEASE_TIMEOUT = "App Distribution failed to fetch release information"
3
+ MISSING_APP_ID = "Missing app id. Please check that the app parameter is set and try again."
4
+ SERVICE_CREDENTIALS_NOT_FOUND = "Service credentials file does not exist. Please check the service credentials path and try again."
5
+ PARSE_SERVICE_CREDENTIALS_ERROR = "Failed to extract service account information from the service credentials file."
6
+ PARSE_FIREBASE_TOOLS_JSON_ERROR = "Encountered error parsing json file. Ensure the firebase-tools.json file is formatted correctly."
7
+ UPLOAD_RELEASE_NOTES_ERROR = "App Distribution halted because it had a problem uploading release notes."
8
+ UPLOAD_TESTERS_ERROR = "App Distribution halted because it had a problem adding testers/groups."
9
+ GET_RELEASE_TIMEOUT = "App Distribution failed to fetch release information."
10
10
  REFRESH_TOKEN_ERROR = "App Distribution could not generate credentials from the refresh token specified."
11
- APP_NOT_ONBOARDED_ERROR = "App Distribution not onboarded"
11
+ APP_NOT_ONBOARDED_ERROR = "App Distribution not onboarded."
12
12
  INVALID_APP_ID = "App Distribution could not find your app. Make sure to onboard your app by pressing the \"Get started\" button on the App Distribution page in the Firebase console: https://console.firebase.google.com/project/_/appdistribution. App ID"
13
13
  INVALID_PROJECT = "App Distribution could not find your Firebase project. Make sure to onboard an app in your project by pressing the \"Get started\" button on the App Distribution page in the Firebase console: https://console.firebase.google.com/project/_/appdistribution."
14
14
  INVALID_PATH = "Could not read content from"
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 set release notes"
19
- SERVICE_CREDENTIALS_ERROR = "App Distribution could not generate credentials from the service credentials file specified"
18
+ INVALID_RELEASE_NOTES = "Failed to set release notes."
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."
22
22
  NO_APP_WITH_GIVEN_BUNDLE_ID_IN_PLAY_ACCOUNT = "App with matching package name does not exist in Google Play."
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
- TESTER_LIMIT_VIOLATION = "Creating testers would exceed tester limit"
25
+ TESTER_LIMIT_VIOLATION = "Creating testers would exceed tester limit."
26
26
 
27
27
  def self.aab_upload_error(aab_state)
28
28
  "Failed to process the AAB: #{aab_state}"
29
29
  end
30
30
 
31
31
  def self.binary_not_found(binary_type)
32
- "Could not find the #{binary_type}. Make sure you set the #{binary_type} path parameter to point to your #{binary_type}"
32
+ "Could not find the #{binary_type}. Make sure you set the #{binary_type} path parameter to point to your #{binary_type}."
33
33
  end
34
34
 
35
35
  def self.parse_binary_metadata_error(binary_type)
36
- "Failed to extract #{binary_type} metadata from the #{binary_type} path"
36
+ "Failed to extract #{binary_type} metadata from the #{binary_type} path."
37
37
  end
38
38
 
39
39
  def self.upload_binary_error(binary_type)
40
- "App Distribution halted because it had a problem uploading the #{binary_type}"
40
+ "App Distribution halted because it had a problem uploading the #{binary_type}."
41
41
  end
42
42
 
43
43
  def self.binary_processing_error(binary_type)
44
- "App Distribution failed to process the #{binary_type}"
44
+ "App Distribution failed to process the #{binary_type}."
45
45
  end
46
46
  end
@@ -1,6 +1,4 @@
1
1
  require 'fastlane_core/ui/ui'
2
- require 'google/apis/firebaseappdistribution_v1'
3
- require 'google/apis/firebaseappdistribution_v1alpha'
4
2
  require 'cfpropertylist'
5
3
 
6
4
  module Fastlane
@@ -29,10 +27,10 @@ module Fastlane
29
27
 
30
28
  # Returns the array representation of a string with trimmed comma
31
29
  # seperated values.
32
- def string_to_array(string)
30
+ def string_to_array(string, delimiter = ",")
33
31
  return [] if string.nil?
34
32
  # Strip string and then strip individual values
35
- string.strip.split(",").map(&:strip)
33
+ string.strip.split(delimiter).map(&:strip)
36
34
  end
37
35
 
38
36
  def parse_plist(path)
@@ -72,17 +70,7 @@ module Fastlane
72
70
  "#{project_name(project_number)}/groups/#{group_alias}"
73
71
  end
74
72
 
75
- def init_v1_client(service_credentials_file, firebase_cli_token, debug, timeout = nil)
76
- init_client(Google::Apis::FirebaseappdistributionV1::FirebaseAppDistributionService.new,
77
- service_credentials_file, firebase_cli_token, debug, timeout)
78
- end
79
-
80
- def init_v1alpha_client(service_credentials_file, firebase_cli_token, debug, timeout = nil)
81
- init_client(Google::Apis::FirebaseappdistributionV1alpha::FirebaseAppDistributionService.new,
82
- service_credentials_file, firebase_cli_token, debug, timeout)
83
- end
84
-
85
- def init_client(client, service_credentials_file, firebase_cli_token, debug, timeout = nil)
73
+ def init_google_api_client(debug, timeout = nil)
86
74
  if debug
87
75
  UI.important("Warning: Debug logging enabled. Output may include sensitive information.")
88
76
  Google::Apis.logger.level = Logger::DEBUG
@@ -93,9 +81,6 @@ module Fastlane
93
81
  unless timeout.nil?
94
82
  Google::Apis::ClientOptions.default.send_timeout_sec = timeout
95
83
  end
96
-
97
- client.authorization = get_authorization(service_credentials_file, firebase_cli_token, debug)
98
- client
99
84
  end
100
85
 
101
86
  def deep_symbolize_keys(hash)
@@ -1,5 +1,5 @@
1
1
  module Fastlane
2
2
  module FirebaseAppDistribution
3
- VERSION = "0.8.0"
3
+ VERSION = "0.9.0.pre.1"
4
4
  end
5
5
  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.8.0
4
+ version: 0.9.0.pre.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Stefan Natchev
@@ -10,7 +10,7 @@ authors:
10
10
  autorequire:
11
11
  bindir: bin
12
12
  cert_chain: []
13
- date: 2024-01-02 00:00:00.000000000 Z
13
+ date: 2024-01-29 00:00:00.000000000 Z
14
14
  dependencies:
15
15
  - !ruby/object:Gem::Dependency
16
16
  name: google-apis-firebaseappdistribution_v1