fastlane-plugin-firebase_app_distribution 0.2.0.pre.1 → 0.2.0.pre.2
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 +5 -5
- data/lib/fastlane/plugin/firebase_app_distribution/actions/firebase_app_distribution_action.rb +7 -6
- data/lib/fastlane/plugin/firebase_app_distribution/client/firebase_app_distribution_api_client.rb +32 -15
- data/lib/fastlane/plugin/firebase_app_distribution/helper/firebase_app_distribution_auth_client.rb +61 -18
- data/lib/fastlane/plugin/firebase_app_distribution/helper/firebase_app_distribution_error_message.rb +19 -6
- data/lib/fastlane/plugin/firebase_app_distribution/helper/firebase_app_distribution_helper.rb +5 -0
- data/lib/fastlane/plugin/firebase_app_distribution/helper/upload_status_response.rb +13 -1
- data/lib/fastlane/plugin/firebase_app_distribution/version.rb +1 -1
- metadata +3 -4
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
|
-
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: 7387fdbb5db28a63ea91ef6e4feb290f4cdb71b93e8c7132595098f94f1ffc16
|
4
|
+
data.tar.gz: d24c551ec1c81f90ca38307aa75f7c3884982fdc58de2911f54b5971056a4a99
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: fe83dbc6eaca609c5471732598f004775ff2139d9d24487bb5021d821a909dfce61f955166c3ed1e115186909c7b455a0578237923845a5015d5ac776c57fe5c
|
7
|
+
data.tar.gz: 440ac81fb3b05743c511d22f2096c099a636cfc6df06a8d4a8703f9dff8d727c4841f6218e4503cec22d765d2379dc5bbca4ae76bc2b98dcb79007d2b45533ce
|
data/lib/fastlane/plugin/firebase_app_distribution/actions/firebase_app_distribution_action.rb
CHANGED
@@ -21,9 +21,8 @@ module Fastlane
|
|
21
21
|
|
22
22
|
def self.run(params)
|
23
23
|
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]
|
24
|
+
auth_token = fetch_auth_token(params[:service_credentials_file], params[:firebase_cli_token])
|
25
|
+
fad_api_client = Client::FirebaseAppDistributionApiClient.new(auth_token, platform)
|
27
26
|
binary_path = params[:ipa_path] || params[:apk_path]
|
28
27
|
|
29
28
|
if params[:app] # Set app_id if it is specified as a parameter
|
@@ -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
|
@@ -67,9 +66,11 @@ module Fastlane
|
|
67
66
|
"Release your beta builds with Firebase App Distribution"
|
68
67
|
end
|
69
68
|
|
70
|
-
def self.
|
71
|
-
platform
|
69
|
+
def self.platform
|
70
|
+
@platform ||= Actions.lane_context[Actions::SharedValues::PLATFORM_NAME]
|
71
|
+
end
|
72
72
|
|
73
|
+
def self.available_options
|
73
74
|
if platform == :ios || platform.nil?
|
74
75
|
ipa_path_default = Dir["*.ipa"].sort_by { |x| File.mtime(x) }.last
|
75
76
|
end
|
data/lib/fastlane/plugin/firebase_app_distribution/client/firebase_app_distribution_api_client.rb
CHANGED
@@ -9,8 +9,13 @@ 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
|
+
if platform == :ios || platform.nil?
|
15
|
+
@binary_type = "IPA"
|
16
|
+
else
|
17
|
+
@binary_type = "APK"
|
18
|
+
end
|
14
19
|
end
|
15
20
|
|
16
21
|
# Enables tester access to the specified app release. Skips this
|
@@ -38,6 +43,7 @@ module Fastlane
|
|
38
43
|
rescue Faraday::ClientError
|
39
44
|
UI.user_error!("#{ErrorMessage::INVALID_TESTERS} \nEmails: #{emails} \nGroups: #{group_ids}")
|
40
45
|
end
|
46
|
+
UI.success("Added testers/groups successfully.")
|
41
47
|
end
|
42
48
|
|
43
49
|
# Posts notes for the specified app release. Skips this
|
@@ -71,7 +77,7 @@ module Fastlane
|
|
71
77
|
begin
|
72
78
|
binary_hash = Digest::SHA256.hexdigest(File.open(binary_path).read)
|
73
79
|
rescue Errno::ENOENT
|
74
|
-
UI.crash!("#{ErrorMessage
|
80
|
+
UI.crash!("#{ErrorMessage.binary_not_found(@binary_type)}: #{binary_path}")
|
75
81
|
end
|
76
82
|
|
77
83
|
begin
|
@@ -88,14 +94,17 @@ module Fastlane
|
|
88
94
|
return upload_token_format(response.body[:appId], response.body[:projectNumber], binary_hash)
|
89
95
|
end
|
90
96
|
|
91
|
-
def upload_binary(app_id, binary_path)
|
97
|
+
def upload_binary(app_id, binary_path, platform)
|
92
98
|
connection.post(binary_upload_url(app_id), File.open(binary_path).read) do |request|
|
93
99
|
request.headers["Authorization"] = "Bearer " + @auth_token
|
100
|
+
request.headers["X-APP-DISTRO-API-CLIENT-ID"] = "fastlane"
|
101
|
+
request.headers["X-APP-DISTRO-API-CLIENT-TYPE"] = platform
|
102
|
+
request.headers["X-APP-DISTRO-API-CLIENT-VERSION"] = Fastlane::FirebaseAppDistribution::VERSION
|
94
103
|
end
|
95
104
|
rescue Faraday::ResourceNotFound
|
96
105
|
UI.crash!("#{ErrorMessage::INVALID_APP_ID}: #{app_id}")
|
97
106
|
rescue Errno::ENOENT
|
98
|
-
UI.crash!("#{ErrorMessage
|
107
|
+
UI.crash!("#{ErrorMessage.binary_not_found(@binary_type)}: #{binary_path}")
|
99
108
|
end
|
100
109
|
|
101
110
|
# Uploads the binary file if it has not already been uploaded
|
@@ -107,28 +116,36 @@ module Fastlane
|
|
107
116
|
#
|
108
117
|
# Returns the release_id on a successful release, otherwise returns nil.
|
109
118
|
#
|
110
|
-
# Throws
|
111
|
-
|
119
|
+
# Throws a UI error if the number of polling retries exceeds MAX_POLLING_RETRIES
|
120
|
+
# Crashes if not able to upload the binary
|
121
|
+
def upload(app_id, binary_path, platform)
|
112
122
|
upload_token = get_upload_token(app_id, binary_path)
|
113
123
|
upload_status_response = get_upload_status(app_id, upload_token)
|
114
|
-
if upload_status_response.success?
|
115
|
-
UI.success("This
|
124
|
+
if upload_status_response.success? || upload_status_response.already_uploaded?
|
125
|
+
UI.success("This #{@binary_type} has been uploaded before. Skipping upload step.")
|
116
126
|
else
|
117
|
-
UI.message("This
|
127
|
+
UI.message("This #{@binary_type} has not been uploaded before")
|
128
|
+
UI.message("Uploading the #{@binary_type}.")
|
129
|
+
unless upload_status_response.in_progress?
|
130
|
+
upload_binary(app_id, binary_path, platform)
|
131
|
+
end
|
118
132
|
MAX_POLLING_RETRIES.times do
|
119
|
-
|
120
|
-
|
133
|
+
upload_status_response = get_upload_status(app_id, upload_token)
|
134
|
+
if upload_status_response.success? || upload_status_response.already_uploaded?
|
135
|
+
UI.success("Uploaded #{@binary_type} Successfully!")
|
121
136
|
break
|
122
137
|
elsif upload_status_response.in_progress?
|
123
138
|
sleep(POLLING_INTERVAL_SECONDS)
|
124
139
|
else
|
125
|
-
|
126
|
-
|
140
|
+
if !upload_status_response.message.nil?
|
141
|
+
UI.user_error!("#{ErrorMessage.upload_binary_error(@binary_type)}: #{upload_status_response.message}")
|
142
|
+
else
|
143
|
+
UI.user_error!(ErrorMessage.upload_binary_error(@binary_type))
|
144
|
+
end
|
127
145
|
end
|
128
|
-
upload_status_response = get_upload_status(app_id, upload_token)
|
129
146
|
end
|
130
147
|
unless upload_status_response.success?
|
131
|
-
UI.error("It took longer than expected to process your
|
148
|
+
UI.error("It took longer than expected to process your #{@binary_type}, please try again.")
|
132
149
|
return nil
|
133
150
|
end
|
134
151
|
end
|
data/lib/fastlane/plugin/firebase_app_distribution/helper/firebase_app_distribution_auth_client.rb
CHANGED
@@ -5,33 +5,74 @@ module Fastlane
|
|
5
5
|
module FirebaseAppDistributionAuthClient
|
6
6
|
TOKEN_CREDENTIAL_URI = "https://oauth2.googleapis.com/token"
|
7
7
|
|
8
|
-
|
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
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
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.
|
39
|
+
UI.user_error!(ErrorMessage::MISSING_CREDENTIALS)
|
17
40
|
end
|
41
|
+
UI.success("Successfully authenticated!")
|
42
|
+
token
|
18
43
|
end
|
19
44
|
|
20
45
|
private
|
21
46
|
|
22
|
-
def
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
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.
|
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
|
data/lib/fastlane/plugin/firebase_app_distribution/helper/firebase_app_distribution_error_message.rb
CHANGED
@@ -1,16 +1,12 @@
|
|
1
1
|
module ErrorMessage
|
2
|
-
MISSING_CREDENTIALS = "Missing credentials.
|
3
|
-
APK_NOT_FOUND = "Could not find the APK/IPA. Make sure you set the apk_path parameter to point to your APK/IPA"
|
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."
|
4
3
|
MISSING_APP_ID = "Missing app id. Please check that it was passed in 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 = "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"
|
@@ -18,4 +14,21 @@ module ErrorMessage
|
|
18
14
|
INVALID_PATH = "Could not read content from"
|
19
15
|
INVALID_TESTERS = "Could not enable access for testers. Ensure 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 = "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
|
data/lib/fastlane/plugin/firebase_app_distribution/helper/firebase_app_distribution_helper.rb
CHANGED
@@ -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 ==
|
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
|
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.2.0.pre.
|
4
|
+
version: 0.2.0.pre.2
|
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: 2020-
|
13
|
+
date: 2020-08-06 00:00:00.000000000 Z
|
14
14
|
dependencies:
|
15
15
|
- !ruby/object:Gem::Dependency
|
16
16
|
name: pry
|
@@ -177,8 +177,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
177
177
|
- !ruby/object:Gem::Version
|
178
178
|
version: 1.3.1
|
179
179
|
requirements: []
|
180
|
-
|
181
|
-
rubygems_version: 2.2.5
|
180
|
+
rubygems_version: 3.1.4
|
182
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
|