fastlane-plugin-firebase_app_distribution 0.2.0.pre.1 → 0.2.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
- SHA1:
3
- metadata.gz: ab7379d2d8feb6112f778c3b4a64e2f2181a099a
4
- data.tar.gz: bdb617b84fb387a5486faee45d23be3043a9d9af
2
+ SHA256:
3
+ metadata.gz: 5069b6e632432b25f206acbd47973be4326f582b8e555c7783c26638a6da9c21
4
+ data.tar.gz: a8f5e1ff46a220466eaa79e2e1072532b8715c79297462ac064e41da330e4c6e
5
5
  SHA512:
6
- metadata.gz: 69f808e806793eb81839ac45241445c8764884f35c92c25066c86121c5ecb7ebf981371ed347fceadbfb0161df03ac6613fbd3f2aebe42a935783ca55d239df5
7
- data.tar.gz: 83c298cdfd08b945957f10f9194142423418b89ee5dc17a2718b1506013e79fbba2f94ad11e1b7c0b92a874c3e69731eba327e66f393bb61b99fae0bf423d5b5
6
+ metadata.gz: 93749ffc7b47c65964da87b9db79925d71f1a0fc81bc0f33bd8710c8af34b934d19c7aad04f1e79bed2f90000190485ed07b86b1fb623cccbd1a81839d0ccdff
7
+ data.tar.gz: ebba43e6bc7de7d8742bab36a7a8139b176f1cbfdf2130bdddd2131e114448077f51b3458c308ede0a828335a58ad21cdcfa7fc9a5ca1955f479919868236505
@@ -13,7 +13,6 @@ require_relative '../helper/firebase_app_distribution_auth_client'
13
13
  module Fastlane
14
14
  module Actions
15
15
  class FirebaseAppDistributionAction < Action
16
- DEFAULT_FIREBASE_CLI_PATH = `which firebase`
17
16
  FIREBASECMD_ACTION = "appdistribution:distribute".freeze
18
17
 
19
18
  extend Auth::FirebaseAppDistributionAuthClient
@@ -21,10 +20,10 @@ module Fastlane
21
20
 
22
21
  def self.run(params)
23
22
  params.values # to validate all inputs before looking for the ipa/apk
24
- auth_token = fetch_auth_token(params[:service_credentials_file])
25
- fad_api_client = Client::FirebaseAppDistributionApiClient.new(auth_token)
26
- platform = Actions.lane_context[Actions::SharedValues::PLATFORM_NAME]
23
+ auth_token = fetch_auth_token(params[:service_credentials_file], params[:firebase_cli_token])
27
24
  binary_path = params[:ipa_path] || params[:apk_path]
25
+ platform = lane_platform || platform_from_path(binary_path)
26
+ fad_api_client = Client::FirebaseAppDistributionApiClient.new(auth_token, platform)
28
27
 
29
28
  if params[:app] # Set app_id if it is specified as a parameter
30
29
  app_id = params[:app]
@@ -38,7 +37,7 @@ module Fastlane
38
37
  if app_id.nil?
39
38
  UI.crash!(ErrorMessage::MISSING_APP_ID)
40
39
  end
41
- release_id = fad_api_client.upload(app_id, binary_path)
40
+ release_id = fad_api_client.upload(app_id, binary_path, platform.to_s)
42
41
  if release_id.nil?
43
42
  return
44
43
  end
@@ -51,7 +50,7 @@ module Fastlane
51
50
  emails = string_to_array(testers)
52
51
  group_ids = string_to_array(groups)
53
52
  fad_api_client.enable_access(app_id, release_id, emails, group_ids)
54
- UI.success("App Distribution upload finished successfully")
53
+ UI.success("🎉 App Distribution upload finished successfully.")
55
54
  end
56
55
 
57
56
  def self.description
@@ -67,14 +66,26 @@ module Fastlane
67
66
  "Release your beta builds with Firebase App Distribution"
68
67
  end
69
68
 
70
- def self.available_options
71
- platform = Actions.lane_context[Actions::SharedValues::PLATFORM_NAME]
69
+ def self.lane_platform
70
+ Actions.lane_context[Actions::SharedValues::PLATFORM_NAME]
71
+ end
72
+
73
+ def self.platform_from_path(binary_path)
74
+ return nil unless binary_path
75
+ case binary_path.split('.').last
76
+ when 'ipa'
77
+ :ios
78
+ when 'apk'
79
+ :android
80
+ end
81
+ end
72
82
 
73
- if platform == :ios || platform.nil?
83
+ def self.available_options
84
+ if lane_platform == :ios || lane_platform.nil?
74
85
  ipa_path_default = Dir["*.ipa"].sort_by { |x| File.mtime(x) }.last
75
86
  end
76
87
 
77
- if platform == :android
88
+ if lane_platform == :android
78
89
  apk_path_default = Dir["*.apk"].last || Dir[File.join("app", "build", "outputs", "apk", "app-release.apk")].last
79
90
  end
80
91
 
@@ -105,22 +116,10 @@ module Fastlane
105
116
  optional: true,
106
117
  type: String),
107
118
  FastlaneCore::ConfigItem.new(key: :firebase_cli_path,
119
+ deprecated: "This plugin no longer uses the Firebase CLI",
108
120
  env_name: "FIREBASEAPPDISTRO_FIREBASE_CLI_PATH",
109
121
  description: "The absolute path of the firebase cli command",
110
- default_value: DEFAULT_FIREBASE_CLI_PATH,
111
- default_value_dynamic: true,
112
- optional: false,
113
- type: String,
114
- verify_block: proc do |value|
115
- value.chomp!
116
- if value.to_s == "" || !File.exist?(value)
117
- UI.user_error!("firebase_cli_path: missing path to firebase cli tool. Please install firebase in $PATH or specify path")
118
- end
119
-
120
- unless is_firebasecmd_supported?(value)
121
- UI.user_error!("firebase_cli_path: `#{value}` does not support the `#{FIREBASECMD_ACTION}` command. Please download (https://appdistro.page.link/firebase-cli-download) or specify the path to the correct version of firebse")
122
- end
123
- end),
122
+ type: String),
124
123
  FastlaneCore::ConfigItem.new(key: :groups,
125
124
  env_name: "FIREBASEAPPDISTRO_GROUPS",
126
125
  description: "The groups used for distribution, separated by commas",
@@ -187,18 +186,6 @@ module Fastlane
187
186
  CODE
188
187
  ]
189
188
  end
190
-
191
- ## TODO: figure out if we can surpress color output.
192
- def self.is_firebasecmd_supported?(cmd)
193
- outerr, status = Open3.capture2e(cmd, "--non-interactive", FIREBASECMD_ACTION, "--help")
194
- return false unless status.success?
195
-
196
- if outerr =~ /is not a Firebase command/
197
- return false
198
- end
199
-
200
- true
201
- end
202
189
  end
203
190
  end
204
191
  end
@@ -16,20 +16,20 @@ module Fastlane
16
16
  authorizer = Google::Auth::UserAuthorizer.new(client_id, SCOPE, nil)
17
17
  url = authorizer.get_authorization_url(base_url: OOB_URI)
18
18
 
19
- UI.message("Please open the following address in your browser:")
19
+ UI.message("Open the following address in your browser and sign in with your Google account:")
20
20
  UI.message(url)
21
21
  UI.message("")
22
22
  code = UI.input("Enter the resulting code here: ")
23
23
  credentials = authorizer.get_credentials_from_code(code: code, base_url: OOB_URI)
24
-
25
- UI.message("Refresh Token: #{credentials.refresh_token}")
26
24
  UI.message("")
27
- UI.message("Set the refresh token as a FIREBASE_TOKEN environment variable")
25
+
26
+ UI.success("Set the refresh token as the FIREBASE_TOKEN environment variable")
27
+ UI.success("Refresh Token: #{credentials.refresh_token}")
28
28
  rescue Signet::AuthorizationError
29
- UI.error("The code you entered was invalid. Ensure that you have copied the code correctly.")
29
+ UI.error("The code you entered is invalid. Copy and paste the code and try again.")
30
30
  rescue => error
31
31
  UI.error(error.to_s)
32
- UI.crash!("An error has occured please login again.")
32
+ UI.crash!("An error has occured, please login again.")
33
33
  end
34
34
 
35
35
  #####################################################
@@ -9,8 +9,16 @@ module Fastlane
9
9
  MAX_POLLING_RETRIES = 60
10
10
  POLLING_INTERVAL_SECONDS = 2
11
11
 
12
- def initialize(auth_token)
12
+ def initialize(auth_token, platform)
13
13
  @auth_token = auth_token
14
+
15
+ if platform.nil?
16
+ @binary_type = "IPA/APK"
17
+ elsif platform == :ios
18
+ @binary_type = "IPA"
19
+ else
20
+ @binary_type = "APK"
21
+ end
14
22
  end
15
23
 
16
24
  # Enables tester access to the specified app release. Skips this
@@ -25,7 +33,7 @@ module Fastlane
25
33
  # Throws a user_error if app_id, emails, or group_ids are invalid
26
34
  def enable_access(app_id, release_id, emails, group_ids)
27
35
  if (emails.nil? || emails.empty?) && (group_ids.nil? || group_ids.empty?)
28
- UI.message("No testers passed in. Skipping this step")
36
+ UI.success("No testers passed in. Skipping this step.")
29
37
  return
30
38
  end
31
39
  payload = { emails: emails, groupIds: group_ids }
@@ -38,6 +46,7 @@ module Fastlane
38
46
  rescue Faraday::ClientError
39
47
  UI.user_error!("#{ErrorMessage::INVALID_TESTERS} \nEmails: #{emails} \nGroups: #{group_ids}")
40
48
  end
49
+ UI.success("✅ Added testers/groups.")
41
50
  end
42
51
 
43
52
  # Posts notes for the specified app release. Skips this
@@ -52,7 +61,7 @@ module Fastlane
52
61
  def post_notes(app_id, release_id, release_notes)
53
62
  payload = { releaseNotes: { releaseNotes: release_notes } }
54
63
  if release_notes.nil? || release_notes.empty?
55
- UI.message("No release notes passed in. Skipping this step.")
64
+ UI.success("No release notes passed in. Skipping this step.")
56
65
  return
57
66
  end
58
67
  begin
@@ -64,38 +73,57 @@ module Fastlane
64
73
  # rescue Faraday::ClientError
65
74
  # UI.user_error!("#{ErrorMessage::INVALID_RELEASE_ID}: #{release_id}")
66
75
  end
67
- UI.success("Release notes have been posted.")
76
+ UI.success(" Posted release notes.")
68
77
  end
69
78
 
79
+ # Returns the url encoded upload token used for get_upload_status calls:
80
+ # projects/<project-number>/apps/<app-id>/releases/-/binaries/<binary-hash>
81
+ #
82
+ # args
83
+ # app_id - Firebase App ID
84
+ # binary_path - Absolute path to your app's apk/ipa file
85
+ #
86
+ # Throws a user_error if an invalid app id is passed in, the binary file does
87
+ # not exist, or invalid auth credentials are used (e.g. wrong project permissions)
70
88
  def get_upload_token(app_id, binary_path)
71
- begin
72
- binary_hash = Digest::SHA256.hexdigest(File.open(binary_path).read)
73
- rescue Errno::ENOENT
74
- UI.crash!("#{ErrorMessage::APK_NOT_FOUND}: #{binary_path}")
89
+ if binary_path.nil? || !File.exist?(binary_path)
90
+ UI.crash!("#{ErrorMessage.binary_not_found(@binary_type)}: #{binary_path}")
75
91
  end
92
+ binary_hash = Digest::SHA256.hexdigest(read_binary(binary_path))
76
93
 
77
94
  begin
78
95
  response = connection.get(v1_apps_url(app_id)) do |request|
79
96
  request.headers["Authorization"] = "Bearer " + @auth_token
80
97
  end
81
98
  rescue Faraday::ResourceNotFound
82
- UI.crash!("#{ErrorMessage::INVALID_APP_ID}: #{app_id}")
99
+ UI.user_error!("#{ErrorMessage::INVALID_APP_ID}: #{app_id}")
83
100
  end
84
101
  contact_email = response.body[:contactEmail]
85
102
  if contact_email.nil? || contact_email.strip.empty?
86
- UI.crash!(ErrorMessage::GET_APP_NO_CONTACT_EMAIL_ERROR)
103
+ UI.user_error!(ErrorMessage::GET_APP_NO_CONTACT_EMAIL_ERROR)
87
104
  end
88
105
  return upload_token_format(response.body[:appId], response.body[:projectNumber], binary_hash)
89
106
  end
90
107
 
91
- def upload_binary(app_id, binary_path)
92
- connection.post(binary_upload_url(app_id), File.open(binary_path).read) do |request|
108
+ # Uploads the app binary to the Firebase API
109
+ #
110
+ # args
111
+ # app_id - Firebase App ID
112
+ # binary_path - Absolute path to your app's apk/ipa file
113
+ # platform - 'android' or 'ios'
114
+ #
115
+ # Throws a user_error if an invalid app id is passed in, or if the binary file does not exist
116
+ def upload_binary(app_id, binary_path, platform)
117
+ connection.post(binary_upload_url(app_id), read_binary(binary_path)) do |request|
93
118
  request.headers["Authorization"] = "Bearer " + @auth_token
119
+ request.headers["X-APP-DISTRO-API-CLIENT-ID"] = "fastlane"
120
+ request.headers["X-APP-DISTRO-API-CLIENT-TYPE"] = platform
121
+ request.headers["X-APP-DISTRO-API-CLIENT-VERSION"] = Fastlane::FirebaseAppDistribution::VERSION
94
122
  end
95
123
  rescue Faraday::ResourceNotFound
96
- UI.crash!("#{ErrorMessage::INVALID_APP_ID}: #{app_id}")
97
- rescue Errno::ENOENT
98
- UI.crash!("#{ErrorMessage::APK_NOT_FOUND}: #{binary_path}")
124
+ UI.user_error!("#{ErrorMessage::INVALID_APP_ID}: #{app_id}")
125
+ rescue Errno::ENOENT # Raised when binary_path file does not exist
126
+ UI.user_error!("#{ErrorMessage.binary_not_found(@binary_type)}: #{binary_path}")
99
127
  end
100
128
 
101
129
  # Uploads the binary file if it has not already been uploaded
@@ -107,42 +135,57 @@ module Fastlane
107
135
  #
108
136
  # Returns the release_id on a successful release, otherwise returns nil.
109
137
  #
110
- # Throws an error if the number of polling retries exceeds MAX_POLLING_RETRIES
111
- def upload(app_id, binary_path)
138
+ # Throws a UI error if the number of polling retries exceeds MAX_POLLING_RETRIES
139
+ # Crashes if not able to upload the binary
140
+ def upload(app_id, binary_path, platform)
112
141
  upload_token = get_upload_token(app_id, binary_path)
113
142
  upload_status_response = get_upload_status(app_id, upload_token)
114
- if upload_status_response.success?
115
- UI.success("This APK/IPA has been uploaded before. Skipping upload step.")
143
+ if upload_status_response.success? || upload_status_response.already_uploaded?
144
+ UI.success("This #{@binary_type} has been uploaded before. Skipping upload step.")
116
145
  else
117
- UI.message("This APK has not been uploaded before.")
146
+ unless upload_status_response.in_progress?
147
+ UI.message("⌛ Uploading the #{@binary_type}.")
148
+ upload_binary(app_id, binary_path, platform)
149
+ end
118
150
  MAX_POLLING_RETRIES.times do
119
- if upload_status_response.success?
120
- UI.success("Uploaded APK/IPA Successfully!")
151
+ upload_status_response = get_upload_status(app_id, upload_token)
152
+ if upload_status_response.success? || upload_status_response.already_uploaded?
153
+ UI.success("✅ Uploaded the #{@binary_type}.")
121
154
  break
122
155
  elsif upload_status_response.in_progress?
123
156
  sleep(POLLING_INTERVAL_SECONDS)
124
157
  else
125
- UI.message("Uploading the APK/IPA.")
126
- upload_binary(app_id, binary_path)
158
+ if !upload_status_response.message.nil?
159
+ UI.user_error!("#{ErrorMessage.upload_binary_error(@binary_type)}: #{upload_status_response.message}")
160
+ else
161
+ UI.user_error!(ErrorMessage.upload_binary_error(@binary_type))
162
+ end
127
163
  end
128
- upload_status_response = get_upload_status(app_id, upload_token)
129
164
  end
130
165
  unless upload_status_response.success?
131
- UI.error("It took longer than expected to process your APK/IPA, please try again.")
166
+ UI.error("It took longer than expected to process your #{@binary_type}, please try again.")
132
167
  return nil
133
168
  end
134
169
  end
135
170
  upload_status_response.release_id
136
171
  end
137
172
 
138
- # Gets the upload status for the app release.
139
- def get_upload_status(app_id, app_token)
173
+ # Fetches the status of an uploaded binary
174
+ #
175
+ # args
176
+ # app_id - Firebase App ID
177
+ # upload_token - URL encoded upload token
178
+ #
179
+ # Returns the release ID on a successful release, otherwise returns nil.
180
+ #
181
+ # Throws a user_error if an invalid app_id is passed in
182
+ def get_upload_status(app_id, upload_token)
140
183
  begin
141
- response = connection.get(upload_status_url(app_id, app_token)) do |request|
184
+ response = connection.get(upload_status_url(app_id, upload_token)) do |request|
142
185
  request.headers["Authorization"] = "Bearer " + @auth_token
143
186
  end
144
187
  rescue Faraday::ResourceNotFound
145
- UI.crash!("#{ErrorMessage::INVALID_APP_ID}: #{app_id}")
188
+ UI.user_error!("#{ErrorMessage::INVALID_APP_ID}: #{app_id}")
146
189
  end
147
190
  return UploadStatusResponse.new(response.body)
148
191
  end
@@ -180,6 +223,11 @@ module Fastlane
180
223
  conn.adapter(Faraday.default_adapter)
181
224
  end
182
225
  end
226
+
227
+ def read_binary(path)
228
+ # File must be read in binary mode to work on Windows
229
+ File.open(path, 'rb').read
230
+ end
183
231
  end
184
232
  end
185
233
  end
@@ -5,33 +5,74 @@ module Fastlane
5
5
  module FirebaseAppDistributionAuthClient
6
6
  TOKEN_CREDENTIAL_URI = "https://oauth2.googleapis.com/token"
7
7
 
8
- def fetch_auth_token(google_service_path)
8
+ # Returns the auth token for any of the auth methods (Firebase CLI token,
9
+ # Google service account, firebase-tools). To ensure that a specific
10
+ # auth method is used, unset all other auth variables/parameters to nil/empty
11
+ #
12
+ # args
13
+ # google_service_path - Absolute path to the Google service account file
14
+ # firebase_cli_token - Firebase CLI refresh token from login action or
15
+ # CI environment
16
+ #
17
+ # env variables
18
+ # GOOGLE_APPLICATION_CREDENTIALS - see google_service_path
19
+ # FIREBASE_TOKEN - see firebase_cli_token
20
+ #
21
+ # Crashes if given invalid or missing credentials
22
+ def fetch_auth_token(google_service_path, firebase_cli_token)
9
23
  if !google_service_path.nil? && !google_service_path.empty?
10
- service_account(google_service_path)
11
- elsif ENV["FIREBASE_TOKEN"]
12
- firebase_token
13
- elsif ENV["GOOGLE_APPLICATION_CREDENTIALS"]
14
- service_account(ENV["GOOGLE_APPLICATION_CREDENTIALS"])
24
+ UI.message("Authenticating with --service_credentials_file path parameter: #{google_service_path}")
25
+ token = service_account(google_service_path)
26
+ elsif !firebase_cli_token.nil? && !firebase_cli_token.empty?
27
+ UI.message("Authenticating with --firebase_cli_token parameter")
28
+ token = firebase_token(firebase_cli_token)
29
+ elsif !ENV["FIREBASE_TOKEN"].nil? && !ENV["FIREBASE_TOKEN"].empty?
30
+ UI.message("Authenticating with FIREBASE_TOKEN environment variable")
31
+ token = firebase_token(ENV["FIREBASE_TOKEN"])
32
+ elsif !ENV["GOOGLE_APPLICATION_CREDENTIALS"].nil? && !ENV["GOOGLE_APPLICATION_CREDENTIALS"].empty?
33
+ UI.message("Authenticating with GOOGLE_APPLICATION_CREDENTIALS environment variable: #{ENV['GOOGLE_APPLICATION_CREDENTIALS']}")
34
+ token = service_account(ENV["GOOGLE_APPLICATION_CREDENTIALS"])
35
+ elsif (refresh_token = refresh_token_from_firebase_tools)
36
+ UI.message("No authentication method specified. Using cached Firebase CLI credentials.")
37
+ token = firebase_token(refresh_token)
15
38
  else
16
- UI.crash!(ErrorMessage::MISSING_CREDENTIALS)
39
+ UI.user_error!(ErrorMessage::MISSING_CREDENTIALS)
17
40
  end
41
+ UI.success("🔐 Authenticated successfully.")
42
+ token
18
43
  end
19
44
 
20
45
  private
21
46
 
22
- def firebase_token
23
- begin
24
- client = Signet::OAuth2::Client.new(
25
- token_credential_uri: TOKEN_CREDENTIAL_URI,
26
- client_id: Fastlane::Actions::FirebaseAppDistributionLoginAction::CLIENT_ID,
27
- client_secret: Fastlane::Actions::FirebaseAppDistributionLoginAction::CLIENT_SECRET,
28
- refresh_token: ENV["FIREBASE_TOKEN"]
29
- )
30
- rescue Signet::AuthorizationError
31
- UI.crash!(ErrorMessage::REFRESH_TOKEN_ERROR)
47
+ def refresh_token_from_firebase_tools
48
+ if ENV["XDG_CONFIG_HOME"].nil? || ENV["XDG_CONFIG_HOME"].empty?
49
+ config_path = File.expand_path(".config/configstore/firebase-tools.json", "~")
50
+ else
51
+ config_path = File.expand_path("configstore/firebase-tools.json", ENV["XDG_CONFIG_HOME"])
52
+ end
53
+
54
+ if File.exist?(config_path)
55
+ begin
56
+ refresh_token = JSON.parse(File.read(config_path))['tokens']['refresh_token']
57
+ refresh_token unless refresh_token.nil? || refresh_token.empty?
58
+ # TODO: Catch parser errors, improve error handling here
59
+ # Returns nil when there is an empty "tokens" field in the firebase-tools json
60
+ rescue NoMethodError
61
+ end
32
62
  end
63
+ end
64
+
65
+ def firebase_token(refresh_token)
66
+ client = Signet::OAuth2::Client.new(
67
+ token_credential_uri: TOKEN_CREDENTIAL_URI,
68
+ client_id: Fastlane::Actions::FirebaseAppDistributionLoginAction::CLIENT_ID,
69
+ client_secret: Fastlane::Actions::FirebaseAppDistributionLoginAction::CLIENT_SECRET,
70
+ refresh_token: refresh_token
71
+ )
33
72
  client.fetch_access_token!
34
73
  client.access_token
74
+ rescue Signet::AuthorizationError
75
+ UI.user_error!(ErrorMessage::REFRESH_TOKEN_ERROR)
35
76
  end
36
77
 
37
78
  def service_account(google_service_path)
@@ -41,7 +82,9 @@ module Fastlane
41
82
  )
42
83
  service_account_credentials.fetch_access_token!["access_token"]
43
84
  rescue Errno::ENOENT
44
- UI.crash!("#{ErrorMessage::SERVICE_CREDENTIALS_NOT_FOUND}: #{google_service_path}")
85
+ UI.user_error!("#{ErrorMessage::SERVICE_CREDENTIALS_NOT_FOUND}: #{google_service_path}")
86
+ rescue Signet::AuthorizationError
87
+ UI.user_error!("#{ErrorMessage::SERVICE_CREDENTIALS_ERROR}: #{google_service_path}")
45
88
  end
46
89
  end
47
90
  end
@@ -1,21 +1,34 @@
1
1
  module ErrorMessage
2
- MISSING_CREDENTIALS = "Missing credentials. Please check that a refresh token was set or service credentials were passed in and try again"
3
- APK_NOT_FOUND = "Could not find the APK/IPA. Make sure you set the apk_path parameter to point to your APK/IPA"
4
- MISSING_APP_ID = "Missing app id. Please check that it was passed in and try again"
2
+ MISSING_CREDENTIALS = "Missing authentication credentials. Check that your Firebase refresh token is set or that your service account file path is correct and try again."
3
+ MISSING_APP_ID = "Missing app id. Please check that the app parameter is set and try again"
5
4
  SERVICE_CREDENTIALS_NOT_FOUND = "Service credentials file does not exist. Please check the service credentials path and try again"
6
5
  PARSE_SERVICE_CREDENTIALS_ERROR = "Failed to extract service account information from the service credentials file"
7
- PARSE_APK_METADATA_ERROR = "Failed to extract APK/IPA metadata from the APK/IPA path"
8
6
  UPLOAD_RELEASE_NOTES_ERROR = "App Distribution halted because it had a problem uploading release notes"
9
7
  UPLOAD_TESTERS_ERROR = "App Distribution halted because it had a problem adding testers/groups"
10
- UPLOAD_APK_ERROR = "App Distribution halted because it had a problem uploading the APK/IPA"
11
- APK_PROCESSING_ERROR = "App Distribution failed to process the APK/IPA"
12
8
  GET_RELEASE_TIMEOUT = "App Distribution failed to fetch release information"
13
- REFRESH_TOKEN_ERROR = "Could not generate credentials from the refresh token specified"
9
+ REFRESH_TOKEN_ERROR = "App Distribution could not generate credentials from the refresh token specified."
14
10
  GET_APP_ERROR = "App Distribution failed to fetch app information"
15
11
  APP_NOT_ONBOARDED_ERROR = "App Distribution not onboarded"
16
12
  GET_APP_NO_CONTACT_EMAIL_ERROR = "App Distribution could not find a contact email associated with this app. Contact Email"
17
13
  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"
18
14
  INVALID_PATH = "Could not read content from"
19
- INVALID_TESTERS = "Could not enable access for testers. Ensure that the groups exist and the tester emails are formatted correctly"
15
+ INVALID_TESTERS = "Could not enable access for testers. Check that the groups exist and the tester emails are formatted correctly"
20
16
  INVALID_RELEASE_ID = "App distribution failed to fetch release with id"
17
+ SERVICE_CREDENTIALS_ERROR = "App Distribution could not generate credentials from the service credentials file specified. Service Account Path"
18
+
19
+ def self.binary_not_found(binary_type)
20
+ "Could not find the #{binary_type}. Make sure you set the #{binary_type} path parameter to point to your #{binary_type}"
21
+ end
22
+
23
+ def self.parse_binary_metadata_error(binary_type)
24
+ "Failed to extract #{binary_type} metadata from the #{binary_type} path"
25
+ end
26
+
27
+ def self.upload_binary_error(binary_type)
28
+ "App Distribution halted because it had a problem uploading the #{binary_type}"
29
+ end
30
+
31
+ def self.binary_processing_error(binary_type)
32
+ "App Distribution failed to process the #{binary_type}"
33
+ end
21
34
  end
@@ -1,4 +1,5 @@
1
1
  require 'fastlane_core/ui/ui'
2
+ require 'cfpropertylist'
2
3
  module Fastlane
3
4
  UI = FastlaneCore::UI unless Fastlane.const_defined?("UI")
4
5
  module Helper
@@ -23,6 +24,10 @@ module Fastlane
23
24
  return string_array
24
25
  end
25
26
 
27
+ def parse_plist(path)
28
+ CFPropertyList.native_types(CFPropertyList::List.new(file: path).value)
29
+ end
30
+
26
31
  def get_ios_app_id_from_archive(path)
27
32
  app_path = parse_plist("#{path}/Info.plist")["ApplicationProperties"]["ApplicationPath"]
28
33
  UI.shell_error!("can't extract application path from Info.plist at #{path}") if app_path.empty?
@@ -12,7 +12,15 @@ class UploadStatusResponse
12
12
  end
13
13
 
14
14
  def in_progress?
15
- status == "IN_PROGRESS"
15
+ status == 'IN_PROGRESS'
16
+ end
17
+
18
+ def error?
19
+ status == 'ERROR'
20
+ end
21
+
22
+ def already_uploaded?
23
+ status == 'ALREADY_UPLOADED'
16
24
  end
17
25
 
18
26
  def release_hash
@@ -22,4 +30,8 @@ class UploadStatusResponse
22
30
  def release_id
23
31
  release_hash ? release_hash[:id] : nil
24
32
  end
33
+
34
+ def message
35
+ @response_json_hash[:message]
36
+ end
25
37
  end
@@ -1,5 +1,5 @@
1
1
  module Fastlane
2
2
  module FirebaseAppDistribution
3
- VERSION = "0.2.0.pre.1"
3
+ VERSION = "0.2.1"
4
4
  end
5
5
  end
metadata CHANGED
@@ -1,16 +1,16 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: fastlane-plugin-firebase_app_distribution
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.0.pre.1
4
+ version: 0.2.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Stefan Natchev
8
8
  - Manny Jimenez
9
9
  - Alonso Salas Infante
10
- autorequire:
10
+ autorequire:
11
11
  bindir: bin
12
12
  cert_chain: []
13
- date: 2020-07-28 00:00:00.000000000 Z
13
+ date: 2020-09-16 00:00:00.000000000 Z
14
14
  dependencies:
15
15
  - !ruby/object:Gem::Dependency
16
16
  name: pry
@@ -138,7 +138,7 @@ dependencies:
138
138
  - - ">="
139
139
  - !ruby/object:Gem::Version
140
140
  version: 2.127.1
141
- description:
141
+ description:
142
142
  email:
143
143
  - snatchev@google.com
144
144
  - mannyjimenez@google.com
@@ -162,7 +162,7 @@ homepage: https://github.com/fastlane/fastlane-plugin-firebase_app_distribution
162
162
  licenses:
163
163
  - MIT
164
164
  metadata: {}
165
- post_install_message:
165
+ post_install_message:
166
166
  rdoc_options: []
167
167
  require_paths:
168
168
  - lib
@@ -173,13 +173,12 @@ required_ruby_version: !ruby/object:Gem::Requirement
173
173
  version: '0'
174
174
  required_rubygems_version: !ruby/object:Gem::Requirement
175
175
  requirements:
176
- - - ">"
176
+ - - ">="
177
177
  - !ruby/object:Gem::Version
178
- version: 1.3.1
178
+ version: '0'
179
179
  requirements: []
180
- rubyforge_project:
181
- rubygems_version: 2.2.5
182
- signing_key:
180
+ rubygems_version: 3.1.2
181
+ signing_key:
183
182
  specification_version: 4
184
183
  summary: Release your beta builds to Firebase App Distribution. https://firebase.google.com/docs/app-distribution
185
184
  test_files: []