fastlane-plugin-firebase_app_distribution 0.2.2.pre.2 → 0.2.6.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: c5ca3144300e192d87eb1e9457152c5484a057dfcb0d492d672da8cbbf983368
4
- data.tar.gz: ab60ae5cf17ba96547e2278b34c4858149960247fcc593e3bd3cb1794d9eaef9
3
+ metadata.gz: d129a8d78fc5bca84c4e7484dbe02436ee9cd4ef9e9005bec4e1646401b9b143
4
+ data.tar.gz: 1677a9d201117796c171ed8a0a9472b69bc8b763bdd5492e3a3fd8f3dae19d84
5
5
  SHA512:
6
- metadata.gz: 14da8737b9424a01aca4e6df1fdc3ce5254343b89e01fbdac31c9e9176cec5b5d351a78dc1d03adc0d1c031207fee049c5a517cf63ce440cf45fa7326cab45a2
7
- data.tar.gz: eda22a0b5c8cee1b0c990db5f0137c867d7e5a2be8ee7bd30c50811ba80e9b2585e4af5f56bdd82fcaee1adcfa0367286511c6be3fbdf7f550abb69e88941ea6
6
+ metadata.gz: 1c6e3502a696a1d759f838028812a526745e6d11fe678e26a829ead65fd8114b4244565695d338b1d60ef34cb1129b70948e87c8d297c72334abc46e9bbc20ec
7
+ data.tar.gz: 47504b8df8a313b588aea6c5fa8b7d8cebc5d7ea66117c0025b73f53e6f529bbc443e395c66baa357248c81b864ed81f5cfe46cd957c42c92d03e63e16cbc89a
@@ -13,26 +13,46 @@ require_relative '../helper/firebase_app_distribution_auth_client'
13
13
  module Fastlane
14
14
  module Actions
15
15
  class FirebaseAppDistributionAction < Action
16
- FIREBASECMD_ACTION = "appdistribution:distribute".freeze
17
-
18
16
  extend Auth::FirebaseAppDistributionAuthClient
19
17
  extend Helper::FirebaseAppDistributionHelper
20
18
 
21
19
  def self.run(params)
22
- params.values # to validate all inputs before looking for the ipa/apk
20
+ params.values # to validate all inputs before looking for the ipa/apk/aab
23
21
 
24
22
  app_id = app_id_from_params(params)
25
23
  platform = lane_platform || platform_from_app_id(app_id)
26
- binary_path = binary_path_from_platform(platform, params[:ipa_path], params[:apk_path])
24
+
25
+ binary_path = get_binary_path(platform, params)
26
+ UI.user_error!("Couldn't find binary") if binary_path.nil?
27
+ UI.user_error!("Couldn't find binary at path #{binary_path}") unless File.exist?(binary_path)
28
+ binary_type = binary_type_from_path(binary_path)
27
29
 
28
30
  auth_token = fetch_auth_token(params[:service_credentials_file], params[:firebase_cli_token])
29
- fad_api_client = Client::FirebaseAppDistributionApiClient.new(auth_token, platform)
31
+ fad_api_client = Client::FirebaseAppDistributionApiClient.new(auth_token, params[:debug])
30
32
 
31
- release_id = fad_api_client.upload(app_id, binary_path, platform.to_s)
33
+ # If binary is an AAB get FULL view of app which includes the aab_state
34
+ app_view = binary_type == :AAB ? 'FULL' : 'BASIC'
35
+ app = fad_api_client.get_app(app_id, app_view)
36
+ validate_app!(app, binary_type)
37
+
38
+ release_id = fad_api_client.upload(app.project_number, app_id, binary_path, platform.to_s)
32
39
  if release_id.nil?
33
40
  return
34
41
  end
35
42
 
43
+ if binary_type == :AAB && app.aab_certificate.empty?
44
+ updated_app = fad_api_client.get_app(app_id)
45
+ unless updated_app.aab_certificate.empty?
46
+ UI.message("After you upload an AAB for the first time, App Distribution " \
47
+ "generates a new test certificate. All AAB uploads are re-signed with this test " \
48
+ "certificate. Use the certificate fingerprints below to register your app " \
49
+ "signing key with API providers, such as Google Sign-In and Google Maps.\n" \
50
+ "MD-1 certificate fingerprint: #{updated_app.aab_certificate.md5_certificate_hash}\n" \
51
+ "SHA-1 certificate fingerprint: #{updated_app.aab_certificate.sha1_certificate_hash}\n" \
52
+ "SHA-256 certificate fingerprint: #{updated_app.aab_certificate.sha256_certificate_hash}")
53
+ end
54
+ end
55
+
36
56
  release_notes = get_value_from_value_or_file(params[:release_notes], params[:release_notes_file])
37
57
  fad_api_client.post_notes(app_id, release_id, release_notes)
38
58
 
@@ -90,52 +110,80 @@ module Fastlane
90
110
  end
91
111
  end
92
112
 
93
- def self.binary_path_from_platform(platform, ipa_path, apk_path)
94
- case platform
95
- when :ios
96
- ipa_path
97
- when :android
98
- apk_path
99
- else
100
- ipa_path || apk_path
113
+ def self.get_binary_path(platform, params)
114
+ if platform == :ios
115
+ return params[:ipa_path] ||
116
+ Actions.lane_context[SharedValues::IPA_OUTPUT_PATH] ||
117
+ Dir["*.ipa"].sort_by { |x| File.mtime(x) }.last
118
+ end
119
+
120
+ if platform == :android
121
+ return params[:apk_path] || params[:android_artifact_path] if params[:apk_path] || params[:android_artifact_path]
122
+
123
+ if params[:android_artifact_type] == 'AAB'
124
+ return Actions.lane_context[SharedValues::GRADLE_AAB_OUTPUT_PATH] ||
125
+ Dir["*.aab"].last ||
126
+ Dir[File.join("app", "build", "outputs", "bundle", "release", "app-release.aab")].last
127
+ end
128
+
129
+ return Actions.lane_context[SharedValues::GRADLE_APK_OUTPUT_PATH] ||
130
+ Dir["*.apk"].last ||
131
+ Dir[File.join("app", "build", "outputs", "apk", "release", "app-release.apk")].last
101
132
  end
102
133
  end
103
134
 
104
- def self.available_options
105
- if lane_platform == :ios || lane_platform.nil?
106
- ipa_path_default = Dir["*.ipa"].sort_by { |x| File.mtime(x) }.last
135
+ def self.validate_app!(app, binary_type)
136
+ if app.contact_email.nil? || app.contact_email.strip.empty?
137
+ UI.user_error!(ErrorMessage::GET_APP_NO_CONTACT_EMAIL_ERROR)
107
138
  end
108
139
 
109
- if lane_platform == :android
110
- apk_path_default = Dir["*.apk"].last || Dir[File.join("app", "build", "outputs", "apk", "app-release.apk")].last
140
+ if binary_type == :AAB && app.aab_state != App::AabState::ACTIVE && app.aab_state != App::AabState::UNAVAILABLE
141
+ case app.aab_state
142
+ when App::AabState::PLAY_ACCOUNT_NOT_LINKED
143
+ UI.user_error!(ErrorMessage::PLAY_ACCOUNT_NOT_LINKED)
144
+ when App::AabState::APP_NOT_PUBLISHED
145
+ UI.user_error!(ErrorMessage::APP_NOT_PUBLISHED)
146
+ when App::AabState::NO_APP_WITH_GIVEN_BUNDLE_ID_IN_PLAY_ACCOUNT
147
+ UI.user_error!(ErrorMessage::NO_APP_WITH_GIVEN_BUNDLE_ID_IN_PLAY_ACCOUNT)
148
+ when App::AabState::PLAY_IAS_TERMS_NOT_ACCEPTED
149
+ UI.user_error!(ErrorMessage::PLAY_IAS_TERMS_NOT_ACCEPTED)
150
+ else
151
+ UI.user_error!(ErrorMessage.aab_upload_error(app.aab_state))
152
+ end
111
153
  end
154
+ end
112
155
 
156
+ def self.available_options
113
157
  [
114
158
  # iOS Specific
115
159
  FastlaneCore::ConfigItem.new(key: :ipa_path,
116
160
  env_name: "FIREBASEAPPDISTRO_IPA_PATH",
117
161
  description: "Path to your IPA file. Optional if you use the _gym_ or _xcodebuild_ action",
118
- default_value: Actions.lane_context[SharedValues::IPA_OUTPUT_PATH] || ipa_path_default,
119
- default_value_dynamic: true,
120
- optional: true,
121
- verify_block: proc do |value|
122
- UI.user_error!("firebase_app_distribution: Couldn't find ipa file at path '#{value}'") unless File.exist?(value)
123
- end),
162
+ optional: true),
124
163
  FastlaneCore::ConfigItem.new(key: :googleservice_info_plist_path,
125
164
  env_name: "GOOGLESERVICE_INFO_PLIST_PATH",
126
- description: "Path to your GoogleService-Info.plist file, relative to the root of your Xcode project",
165
+ description: "Path to your GoogleService-Info.plist file, relative to the archived product path",
127
166
  default_value: "GoogleService-Info.plist",
128
167
  optional: true,
129
168
  type: String),
130
169
  # Android Specific
131
170
  FastlaneCore::ConfigItem.new(key: :apk_path,
132
171
  env_name: "FIREBASEAPPDISTRO_APK_PATH",
172
+ deprecated: "The apk_path parameter is deprecated. Please use android_artifact_path instead",
133
173
  description: "Path to your APK file",
134
- default_value: Actions.lane_context[SharedValues::GRADLE_APK_OUTPUT_PATH] || apk_path_default,
174
+ optional: true),
175
+ FastlaneCore::ConfigItem.new(key: :android_artifact_path,
176
+ env_name: "FIREBASEAPPDISTRO_ANDROID_ARTIFACT_PATH",
177
+ description: "Path to your APK or AAB file",
178
+ optional: true),
179
+ FastlaneCore::ConfigItem.new(key: :android_artifact_type,
180
+ env_name: "FIREBASEAPPDISTRO_ANDROID_ARTIFACT_TYPE",
181
+ description: "Android artifact type. Set to 'APK' or 'AAB'. Defaults to 'APK' if not set",
182
+ default_value: "APK",
135
183
  default_value_dynamic: true,
136
184
  optional: true,
137
185
  verify_block: proc do |value|
138
- UI.user_error!("firebase_app_distribution: Couldn't find apk file at path '#{value}'") unless File.exist?(value)
186
+ UI.user_error!("firebase_app_distribution: '#{value}' is not a valid value for android_artifact_path. Should be 'APK' or 'AAB'") unless ['APK', 'AAB'].include?(value)
139
187
  end),
140
188
  FastlaneCore::ConfigItem.new(key: :app,
141
189
  env_name: "FIREBASEAPPDISTRO_APP",
@@ -8,6 +8,9 @@ module Fastlane
8
8
  class FirebaseAppDistributionLoginAction < Action
9
9
  OOB_URI = "urn:ietf:wg:oauth:2.0:oob"
10
10
  SCOPE = "https://www.googleapis.com/auth/cloud-platform"
11
+
12
+ # In this type of application, the client secret is not treated as a secret.
13
+ # See: https://developers.google.com/identity/protocols/OAuth2InstalledApp
11
14
  CLIENT_ID = "563584335869-fgrhgmd47bqnekij5i8b5pr03ho849e6.apps.googleusercontent.com"
12
15
  CLIENT_SECRET = "j9iVZfS8kkCEFUPaAeJV0sAi"
13
16
 
@@ -0,0 +1,23 @@
1
+ class AabCertificate
2
+ def initialize(response)
3
+ @response = response || {}
4
+ end
5
+
6
+ def md5_certificate_hash
7
+ @response[:certificateHashMd5]
8
+ end
9
+
10
+ def sha1_certificate_hash
11
+ @response[:certificateHashSha1]
12
+ end
13
+
14
+ def sha256_certificate_hash
15
+ @response[:certificateHashSha256]
16
+ end
17
+
18
+ def empty?
19
+ (md5_certificate_hash.nil? || md5_certificate_hash.empty?) &&
20
+ (sha1_certificate_hash.nil? || sha1_certificate_hash.empty?) &&
21
+ (sha256_certificate_hash.nil? || sha256_certificate_hash.empty?)
22
+ end
23
+ end
@@ -0,0 +1,37 @@
1
+ require_relative 'aab_certificate'
2
+
3
+ class App
4
+ # AAB states
5
+ class AabState
6
+ UNSPECIFIED = "AAB_STATE_UNSPECIFIED"
7
+ PLAY_ACCOUNT_NOT_LINKED = "PLAY_ACCOUNT_NOT_LINKED"
8
+ NO_APP_WITH_GIVEN_BUNDLE_ID_IN_PLAY_ACCOUNT = "NO_APP_WITH_GIVEN_BUNDLE_ID_IN_PLAY_ACCOUNT"
9
+ APP_NOT_PUBLISHED = "APP_NOT_PUBLISHED"
10
+ PLAY_IAS_TERMS_NOT_ACCEPTED = "PLAY_IAS_TERMS_NOT_ACCEPTED"
11
+ ACTIVE = "ACTIVE"
12
+ UNAVAILABLE = "AAB_STATE_UNAVAILABLE"
13
+ end
14
+
15
+ attr_reader :aab_certificate
16
+
17
+ def initialize(response)
18
+ @response = response
19
+ @aab_certificate = AabCertificate.new(response[:aabCertificate])
20
+ end
21
+
22
+ def app_id
23
+ @response[:appId]
24
+ end
25
+
26
+ def project_number
27
+ @response[:projectNumber]
28
+ end
29
+
30
+ def contact_email
31
+ @response[:contactEmail]
32
+ end
33
+
34
+ def aab_state
35
+ @response[:aabState]
36
+ end
37
+ end
@@ -0,0 +1,16 @@
1
+ module Fastlane
2
+ module Client
3
+ class ErrorResponse
4
+ attr_accessor :code, :message, :status
5
+
6
+ def initialize(response)
7
+ unless response[:body].nil? || response[:body].empty?
8
+ response_body = JSON.parse(response[:body], symbolize_names: true)
9
+ @code = response_body[:error][:code]
10
+ @message = response_body[:error][:message]
11
+ @status = response_body[:error][:status]
12
+ end
13
+ end
14
+ end
15
+ end
16
+ end
@@ -1,24 +1,27 @@
1
1
  require 'fastlane_core/ui/ui'
2
2
  require_relative '../actions/firebase_app_distribution_login'
3
+ require_relative '../client/error_response'
4
+ require_relative '../client/app'
5
+ require_relative '../helper/firebase_app_distribution_helper'
3
6
 
4
7
  module Fastlane
5
8
  module Client
6
9
  class FirebaseAppDistributionApiClient
10
+ include Helper::FirebaseAppDistributionHelper
11
+
7
12
  BASE_URL = "https://firebaseappdistribution.googleapis.com"
8
13
  TOKEN_CREDENTIAL_URI = "https://oauth2.googleapis.com/token"
9
14
  MAX_POLLING_RETRIES = 60
10
15
  POLLING_INTERVAL_SECONDS = 2
11
16
 
12
- def initialize(auth_token, platform)
13
- @auth_token = auth_token
17
+ AUTHORIZATION = "Authorization"
18
+ CONTENT_TYPE = "Content-Type"
19
+ APPLICATION_JSON = "application/json"
20
+ APPLICATION_OCTET_STREAM = "application/octet-stream"
14
21
 
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
22
+ def initialize(auth_token, debug = false)
23
+ @auth_token = auth_token
24
+ @debug = debug
22
25
  end
23
26
 
24
27
  # Enables tester access to the specified app release. Skips this
@@ -30,7 +33,7 @@ module Fastlane
30
33
  # emails - String array of app testers' email addresses
31
34
  # group_ids - String array of Firebase tester group IDs
32
35
  #
33
- # Throws a user_error if app_id, emails, or group_ids are invalid
36
+ # Throws a user_error if emails or group_ids are invalid
34
37
  def enable_access(app_id, release_id, emails, group_ids)
35
38
  if (emails.nil? || emails.empty?) && (group_ids.nil? || group_ids.empty?)
36
39
  UI.success("✅ No testers passed in. Skipping this step.")
@@ -39,10 +42,9 @@ module Fastlane
39
42
  payload = { emails: emails, groupIds: group_ids }
40
43
  begin
41
44
  connection.post(enable_access_url(app_id, release_id), payload.to_json) do |request|
42
- request.headers["Authorization"] = "Bearer " + @auth_token
45
+ request.headers[AUTHORIZATION] = "Bearer " + @auth_token
46
+ request.headers[CONTENT_TYPE] = APPLICATION_JSON
43
47
  end
44
- rescue Faraday::ResourceNotFound
45
- UI.user_error!("#{ErrorMessage::INVALID_APP_ID}: #{app_id}")
46
48
  rescue Faraday::ClientError
47
49
  UI.user_error!("#{ErrorMessage::INVALID_TESTERS} \nEmails: #{emails} \nGroups: #{group_ids}")
48
50
  end
@@ -57,7 +59,7 @@ module Fastlane
57
59
  # release_id - App release ID, returned by upload_status endpoint
58
60
  # release_notes - String of notes for this release
59
61
  #
60
- # Throws a user_error if app_id or release_id are invalid
62
+ # Throws a user_error if the release_notes are invalid
61
63
  def post_notes(app_id, release_id, release_notes)
62
64
  payload = { releaseNotes: { releaseNotes: release_notes } }
63
65
  if release_notes.nil? || release_notes.empty?
@@ -66,104 +68,98 @@ module Fastlane
66
68
  end
67
69
  begin
68
70
  connection.post(release_notes_create_url(app_id, release_id), payload.to_json) do |request|
69
- request.headers["Authorization"] = "Bearer " + @auth_token
71
+ request.headers[AUTHORIZATION] = "Bearer " + @auth_token
72
+ request.headers[CONTENT_TYPE] = APPLICATION_JSON
70
73
  end
71
- rescue Faraday::ResourceNotFound
72
- UI.user_error!("#{ErrorMessage::INVALID_APP_ID}: #{app_id}")
73
- # rescue Faraday::ClientError
74
- # UI.user_error!("#{ErrorMessage::INVALID_RELEASE_ID}: #{release_id}")
74
+ rescue Faraday::ClientError => e
75
+ error = ErrorResponse.new(e.response)
76
+ UI.user_error!("#{ErrorMessage::INVALID_RELEASE_NOTES}: #{error.message}")
75
77
  end
76
78
  UI.success("✅ Posted release notes.")
77
79
  end
78
80
 
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
+ # Get app
81
82
  #
82
83
  # args
83
84
  # app_id - Firebase App ID
84
- # binary_path - Absolute path to your app's apk/ipa file
85
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)
88
- def get_upload_token(app_id, binary_path)
89
- if binary_path.nil? || !File.exist?(binary_path)
90
- UI.crash!("#{ErrorMessage.binary_not_found(@binary_type)}: #{binary_path}")
91
- end
92
- binary_hash = Digest::SHA256.hexdigest(read_binary(binary_path))
93
-
86
+ # Throws a user_error if the app hasn't been onboarded to App Distribution
87
+ def get_app(app_id, app_view = 'BASIC')
94
88
  begin
95
- response = connection.get(v1_apps_url(app_id)) do |request|
96
- request.headers["Authorization"] = "Bearer " + @auth_token
89
+ response = connection.get("#{v1_apps_url(app_id)}?appView=#{app_view}") do |request|
90
+ request.headers[AUTHORIZATION] = "Bearer " + @auth_token
97
91
  end
98
92
  rescue Faraday::ResourceNotFound
99
93
  UI.user_error!("#{ErrorMessage::INVALID_APP_ID}: #{app_id}")
100
94
  end
101
- contact_email = response.body[:contactEmail]
102
- if contact_email.nil? || contact_email.strip.empty?
103
- UI.user_error!(ErrorMessage::GET_APP_NO_CONTACT_EMAIL_ERROR)
104
- end
105
- return upload_token_format(response.body[:appId], response.body[:projectNumber], binary_hash)
95
+
96
+ App.new(response.body)
106
97
  end
107
98
 
108
99
  # Uploads the app binary to the Firebase API
109
100
  #
110
101
  # args
111
102
  # app_id - Firebase App ID
112
- # binary_path - Absolute path to your app's apk/ipa file
103
+ # binary_path - Absolute path to your app's aab/apk/ipa file
113
104
  # platform - 'android' or 'ios'
114
105
  #
115
- # Throws a user_error if an invalid app id is passed in, or if the binary file does not exist
106
+ # Throws a user_error if the binary file does not exist
116
107
  def upload_binary(app_id, binary_path, platform)
117
108
  connection.post(binary_upload_url(app_id), read_binary(binary_path)) do |request|
118
- request.headers["Authorization"] = "Bearer " + @auth_token
109
+ request.headers[AUTHORIZATION] = "Bearer " + @auth_token
110
+ request.headers[CONTENT_TYPE] = APPLICATION_OCTET_STREAM
119
111
  request.headers["X-APP-DISTRO-API-CLIENT-ID"] = "fastlane"
120
112
  request.headers["X-APP-DISTRO-API-CLIENT-TYPE"] = platform
121
113
  request.headers["X-APP-DISTRO-API-CLIENT-VERSION"] = Fastlane::FirebaseAppDistribution::VERSION
114
+ request.headers["X-GOOG-UPLOAD-FILE-NAME"] = File.basename(binary_path)
115
+ request.headers["X-GOOG-UPLOAD-PROTOCOL"] = "raw"
122
116
  end
123
- rescue Faraday::ResourceNotFound
124
- UI.user_error!("#{ErrorMessage::INVALID_APP_ID}: #{app_id}")
125
117
  rescue Errno::ENOENT # Raised when binary_path file does not exist
126
- UI.user_error!("#{ErrorMessage.binary_not_found(@binary_type)}: #{binary_path}")
118
+ binary_type = binary_type_from_path(binary_path)
119
+ UI.user_error!("#{ErrorMessage.binary_not_found(binary_type)}: #{binary_path}")
127
120
  end
128
121
 
129
122
  # Uploads the binary file if it has not already been uploaded
130
123
  # Takes at least POLLING_INTERVAL_SECONDS between polling get_upload_status
131
124
  #
132
125
  # args
133
- # app_id - Firebase App ID
134
- # binary_path - Absolute path to your app's apk/ipa file
126
+ # project_number - Firebase project number
127
+ # app_id - Firebase app ID
128
+ # binary_path - Absolute path to your app's aab/apk/ipa file
135
129
  #
136
130
  # Returns the release_id on a successful release, otherwise returns nil.
137
131
  #
138
132
  # Throws a UI error if the number of polling retries exceeds MAX_POLLING_RETRIES
139
133
  # Crashes if not able to upload the binary
140
- def upload(app_id, binary_path, platform)
141
- upload_token = get_upload_token(app_id, binary_path)
134
+ def upload(project_number, app_id, binary_path, platform)
135
+ binary_type = binary_type_from_path(binary_path)
136
+
137
+ upload_token = get_upload_token(project_number, app_id, binary_path)
142
138
  upload_status_response = get_upload_status(app_id, upload_token)
143
139
  if upload_status_response.success? || upload_status_response.already_uploaded?
144
- UI.success("✅ This #{@binary_type} has been uploaded before. Skipping upload step.")
140
+ UI.success("✅ This #{binary_type} has been uploaded before. Skipping upload step.")
145
141
  else
146
142
  unless upload_status_response.in_progress?
147
- UI.message("⌛ Uploading the #{@binary_type}.")
143
+ UI.message("⌛ Uploading the #{binary_type}.")
148
144
  upload_binary(app_id, binary_path, platform)
149
145
  end
150
146
  MAX_POLLING_RETRIES.times do
151
147
  upload_status_response = get_upload_status(app_id, upload_token)
152
148
  if upload_status_response.success? || upload_status_response.already_uploaded?
153
- UI.success("✅ Uploaded the #{@binary_type}.")
149
+ UI.success("✅ Uploaded the #{binary_type}.")
154
150
  break
155
151
  elsif upload_status_response.in_progress?
156
152
  sleep(POLLING_INTERVAL_SECONDS)
157
153
  else
158
154
  if !upload_status_response.message.nil?
159
- UI.user_error!("#{ErrorMessage.upload_binary_error(@binary_type)}: #{upload_status_response.message}")
155
+ UI.user_error!("#{ErrorMessage.upload_binary_error(binary_type)}: #{upload_status_response.message}")
160
156
  else
161
- UI.user_error!(ErrorMessage.upload_binary_error(@binary_type))
157
+ UI.user_error!(ErrorMessage.upload_binary_error(binary_type))
162
158
  end
163
159
  end
164
160
  end
165
161
  unless upload_status_response.success?
166
- UI.error("It took longer than expected to process your #{@binary_type}, please try again.")
162
+ UI.error("It took longer than expected to process your #{binary_type}, please try again.")
167
163
  return nil
168
164
  end
169
165
  end
@@ -177,15 +173,9 @@ module Fastlane
177
173
  # upload_token - URL encoded upload token
178
174
  #
179
175
  # 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
176
  def get_upload_status(app_id, upload_token)
183
- begin
184
- response = connection.get(upload_status_url(app_id, upload_token)) do |request|
185
- request.headers["Authorization"] = "Bearer " + @auth_token
186
- end
187
- rescue Faraday::ResourceNotFound
188
- UI.user_error!("#{ErrorMessage::INVALID_APP_ID}: #{app_id}")
177
+ response = connection.get(upload_status_url(app_id, upload_token)) do |request|
178
+ request.headers[AUTHORIZATION] = "Bearer " + @auth_token
189
179
  end
190
180
  return UploadStatusResponse.new(response.body)
191
181
  end
@@ -212,7 +202,8 @@ module Fastlane
212
202
  "#{v1_apps_url(app_id)}/upload_status/#{app_token}"
213
203
  end
214
204
 
215
- def upload_token_format(app_id, project_number, binary_hash)
205
+ def get_upload_token(project_number, app_id, binary_path)
206
+ binary_hash = Digest::SHA256.hexdigest(read_binary(binary_path))
216
207
  CGI.escape("projects/#{project_number}/apps/#{app_id}/releases/-/binaries/#{binary_hash}")
217
208
  end
218
209
 
@@ -220,6 +211,7 @@ module Fastlane
220
211
  @connection ||= Faraday.new(url: BASE_URL) do |conn|
221
212
  conn.response(:json, parser_options: { symbolize_names: true })
222
213
  conn.response(:raise_error) # raise_error middleware will run before the json middleware
214
+ conn.response(:logger, nil, { headers: false, bodies: { response: true }, log_level: :debug }) if @debug
223
215
  conn.adapter(Faraday.default_adapter)
224
216
  end
225
217
  end
@@ -14,7 +14,16 @@ module ErrorMessage
14
14
  INVALID_PATH = "Could not read content from"
15
15
  INVALID_TESTERS = "Could not enable access for testers. Check that the groups exist and the tester emails are formatted correctly"
16
16
  INVALID_RELEASE_ID = "App distribution failed to fetch release with id"
17
+ INVALID_RELEASE_NOTES = "Failed to add release notes"
17
18
  SERVICE_CREDENTIALS_ERROR = "App Distribution could not generate credentials from the service credentials file specified. Service Account Path"
19
+ PLAY_ACCOUNT_NOT_LINKED = "This project is not linked to a Google Play account."
20
+ APP_NOT_PUBLISHED = "This app is not published in the Google Play console."
21
+ NO_APP_WITH_GIVEN_BUNDLE_ID_IN_PLAY_ACCOUNT = "App with matching package name does not exist in Google Play."
22
+ PLAY_IAS_TERMS_NOT_ACCEPTED = "You must accept the Play Internal App Sharing (IAS) terms to upload AABs."
23
+
24
+ def self.aab_upload_error(aab_state)
25
+ "Failed to process the AAB: #{aab_state}"
26
+ end
18
27
 
19
28
  def self.binary_not_found(binary_type)
20
29
  "Could not find the #{binary_type}. Make sure you set the #{binary_type} path parameter to point to your #{binary_type}"
@@ -4,6 +4,15 @@ module Fastlane
4
4
  UI = FastlaneCore::UI unless Fastlane.const_defined?("UI")
5
5
  module Helper
6
6
  module FirebaseAppDistributionHelper
7
+ def binary_type_from_path(binary_path)
8
+ extension = File.extname(binary_path)
9
+ return :APK if extension == '.apk'
10
+ return :AAB if extension == '.aab'
11
+ return :IPA if extension == '.ipa'
12
+
13
+ UI.user_error!("Unsupported distribution file format, should be .ipa, .apk or .aab")
14
+ end
15
+
7
16
  def get_value_from_value_or_file(value, path)
8
17
  if (value.nil? || value.empty?) && !path.nil?
9
18
  begin
@@ -1,5 +1,5 @@
1
1
  module Fastlane
2
2
  module FirebaseAppDistribution
3
- VERSION = "0.2.2.pre.2"
3
+ VERSION = "0.2.6.pre.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.2.pre.2
4
+ version: 0.2.6.pre.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-09-23 00:00:00.000000000 Z
13
+ date: 2021-03-08 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
@@ -152,6 +152,9 @@ files:
152
152
  - lib/fastlane/plugin/firebase_app_distribution.rb
153
153
  - lib/fastlane/plugin/firebase_app_distribution/actions/firebase_app_distribution_action.rb
154
154
  - lib/fastlane/plugin/firebase_app_distribution/actions/firebase_app_distribution_login.rb
155
+ - lib/fastlane/plugin/firebase_app_distribution/client/aab_certificate.rb
156
+ - lib/fastlane/plugin/firebase_app_distribution/client/app.rb
157
+ - lib/fastlane/plugin/firebase_app_distribution/client/error_response.rb
155
158
  - lib/fastlane/plugin/firebase_app_distribution/client/firebase_app_distribution_api_client.rb
156
159
  - lib/fastlane/plugin/firebase_app_distribution/helper/firebase_app_distribution_auth_client.rb
157
160
  - lib/fastlane/plugin/firebase_app_distribution/helper/firebase_app_distribution_error_message.rb
@@ -162,7 +165,7 @@ homepage: https://github.com/fastlane/fastlane-plugin-firebase_app_distribution
162
165
  licenses:
163
166
  - MIT
164
167
  metadata: {}
165
- post_install_message:
168
+ post_install_message:
166
169
  rdoc_options: []
167
170
  require_paths:
168
171
  - lib
@@ -177,8 +180,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
177
180
  - !ruby/object:Gem::Version
178
181
  version: 1.3.1
179
182
  requirements: []
180
- rubygems_version: 3.1.2
181
- signing_key:
183
+ rubygems_version: 3.1.4
184
+ signing_key:
182
185
  specification_version: 4
183
186
  summary: Release your beta builds to Firebase App Distribution. https://firebase.google.com/docs/app-distribution
184
187
  test_files: []