Skip to content

Commit cfdd8d2

Browse files
authored
Add action to get latest release (#252)
Add action to get latest release.
1 parent 7f402ce commit cfdd8d2

9 files changed

+245
-9
lines changed

lib/fastlane/plugin/firebase_app_distribution/actions/firebase_app_distribution_action.rb

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -89,10 +89,6 @@ def self.app_id_from_params(params)
8989
app_id
9090
end
9191

92-
def self.app_name_from_app_id(app_id)
93-
"projects/#{app_id.split(':')[1]}/apps/#{app_id}"
94-
end
95-
9692
def self.xcode_archive_path
9793
# prevents issues on cross-platform build environments where an XCode build happens within
9894
# the same lane
@@ -230,7 +226,7 @@ def self.available_options
230226
optional: true,
231227
type: String),
232228
FastlaneCore::ConfigItem.new(key: :firebase_cli_token,
233-
description: "Auth token for firebase cli",
229+
description: "Auth token generated using 'fastlane run firebase_app_distribution_login', or the Firebase CLI's login:ci command",
234230
optional: true,
235231
type: String),
236232
FastlaneCore::ConfigItem.new(key: :debug,

lib/fastlane/plugin/firebase_app_distribution/actions/firebase_app_distribution_add_testers_action.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -71,7 +71,7 @@ def self.available_options
7171
optional: true,
7272
type: String),
7373
FastlaneCore::ConfigItem.new(key: :firebase_cli_token,
74-
description: "Auth token for firebase cli",
74+
description: "Auth token generated using 'fastlane run firebase_app_distribution_login', or the Firebase CLI's login:ci command",
7575
optional: true,
7676
type: String),
7777
FastlaneCore::ConfigItem.new(key: :debug,
Lines changed: 111 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,111 @@
1+
require 'fastlane/action'
2+
require_relative '../client/firebase_app_distribution_api_client'
3+
require_relative '../helper/firebase_app_distribution_auth_client'
4+
require_relative '../helper/firebase_app_distribution_helper'
5+
6+
module Fastlane
7+
module Actions
8+
class FirebaseAppDistributionGetLatestReleaseAction < Action
9+
extend Auth::FirebaseAppDistributionAuthClient
10+
extend Helper::FirebaseAppDistributionHelper
11+
12+
def self.run(params)
13+
auth_token = fetch_auth_token(params[:service_credentials_file], params[:firebase_cli_token])
14+
fad_api_client = Client::FirebaseAppDistributionApiClient.new(auth_token, params[:debug])
15+
16+
UI.message("⏳ Fetching latest release for app #{params[:app]}...")
17+
18+
releases = fad_api_client.list_releases(app_name_from_app_id(params[:app]), 1)[:releases] || []
19+
if releases.empty?
20+
latest_release = nil
21+
UI.important("No releases for app #{params[:app]} found in App Distribution. Returning nil and setting Actions.lane_context[:FIREBASE_APP_DISTRO_LATEST_RELEASE].")
22+
else
23+
latest_release = releases[0]
24+
UI.success("✅ Latest release fetched successfully. Returning release and setting Actions.lane_context[:FIREBASE_APP_DISTRO_LATEST_RELEASE].")
25+
end
26+
Actions.lane_context[:FIREBASE_APP_DISTRO_LATEST_RELEASE] = latest_release
27+
return latest_release
28+
end
29+
30+
#####################################################
31+
# @!group Documentation
32+
#####################################################
33+
34+
def self.description
35+
"Fetches the latest release in Firebase App Distribution"
36+
end
37+
38+
def self.details
39+
[
40+
"Fetches information about the most recently created release in App Distribution, including the version and release notes. Returns nil if no releases are found."
41+
].join("\n")
42+
end
43+
44+
def self.available_options
45+
[
46+
FastlaneCore::ConfigItem.new(key: :app,
47+
env_name: "FIREBASEAPPDISTRO_APP",
48+
description: "Your app's Firebase App ID. You can find the App ID in the Firebase console, on the General Settings page",
49+
optional: false,
50+
type: String),
51+
FastlaneCore::ConfigItem.new(key: :firebase_cli_token,
52+
description: "Auth token generated using 'fastlane run firebase_app_distribution_login', or the Firebase CLI's login:ci command",
53+
optional: true,
54+
type: String),
55+
FastlaneCore::ConfigItem.new(key: :service_credentials_file,
56+
description: "Path to Google service account json",
57+
optional: true,
58+
type: String),
59+
FastlaneCore::ConfigItem.new(key: :debug,
60+
description: "Print verbose debug output",
61+
optional: true,
62+
default_value: false,
63+
is_string: false)
64+
]
65+
end
66+
67+
def self.output
68+
[
69+
['FIREBASE_APP_DISTRO_LATEST_RELEASE', 'A hash representing the lastest release created in Firebase App Distribution']
70+
]
71+
end
72+
73+
def self.return_value
74+
"Hash representation of the lastest release created in Firebase App Distribution (see https://firebase.google.com/docs/reference/app-distribution/rest/v1/projects.apps.releases#resource:-release)"
75+
end
76+
77+
def self.return_type
78+
:hash
79+
end
80+
81+
def self.authors
82+
["lkellogg@google.com"]
83+
end
84+
85+
def self.is_supported?(platform)
86+
true
87+
end
88+
89+
def self.example_code
90+
[
91+
'release = firebase_app_distribution_get_latest_release(app: "1:1234567890:ios:0a1b2c3d4e5f67890")',
92+
'increment_build_number({
93+
build_number: firebase_app_distribution_get_latest_release(app: "1:1234567890:ios:0a1b2c3d4e5f67890")[:buildVersion].to_i + 1
94+
})'
95+
]
96+
end
97+
98+
def self.sample_return_value
99+
{
100+
name: "projects/123456789/apps/1:1234567890:ios:0a1b2c3d4e5f67890/releases/0a1b2c3d4",
101+
releaseNotes: {
102+
text: "Here are some release notes!"
103+
},
104+
displayVersion: "1.2.3",
105+
buildVersion: "10",
106+
createTime: "2021-10-06T15:01:23Z"
107+
}
108+
end
109+
end
110+
end
111+
end

lib/fastlane/plugin/firebase_app_distribution/actions/firebase_app_distribution_get_udids.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -63,7 +63,7 @@ def self.available_options
6363
optional: false,
6464
type: String),
6565
FastlaneCore::ConfigItem.new(key: :firebase_cli_token,
66-
description: "Auth token for firebase cli",
66+
description: "Auth token generated using 'fastlane run firebase_app_distribution_login', or the Firebase CLI's login:ci command",
6767
optional: true,
6868
type: String),
6969
FastlaneCore::ConfigItem.new(key: :service_credentials_file,

lib/fastlane/plugin/firebase_app_distribution/actions/firebase_app_distribution_remove_testers_action.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -68,7 +68,7 @@ def self.available_options
6868
optional: true,
6969
type: String),
7070
FastlaneCore::ConfigItem.new(key: :firebase_cli_token,
71-
description: "Auth token for firebase cli",
71+
description: "Auth token generated using 'fastlane run firebase_app_distribution_login', or the Firebase CLI's login:ci command",
7272
optional: true,
7373
type: String),
7474
FastlaneCore::ConfigItem.new(key: :debug,

lib/fastlane/plugin/firebase_app_distribution/client/firebase_app_distribution_api_client.rb

Lines changed: 26 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -220,7 +220,7 @@ def add_testers(project_number, emails)
220220
rescue Faraday::ResourceNotFound
221221
UI.user_error!(ErrorMessage::INVALID_PROJECT)
222222
rescue Faraday::ClientError => e
223-
if e.response_status == 429
223+
if e.response[:status] == 429
224224
UI.user_error!(ErrorMessage::TESTER_LIMIT_VIOLATION)
225225
else
226226
raise e
@@ -247,6 +247,27 @@ def remove_testers(project_number, emails)
247247
UI.user_error!(ErrorMessage::INVALID_PROJECT)
248248
end
249249

250+
# List releases
251+
#
252+
# args
253+
# app_name - Firebase App resource name
254+
# page_size - The number of releases to return in the page
255+
# page_token - A page token, received from a previous call
256+
#
257+
# Returns the response body. Throws a user_error if the app hasn't been onboarded to App Distribution.
258+
def list_releases(app_name, page_size = 100, page_token = nil)
259+
begin
260+
response = connection.get(list_releases_url(app_name), { pageSize: page_size.to_s, pageToken: page_token }) do |request|
261+
request.headers[AUTHORIZATION] = "Bearer " + @auth_token
262+
request.headers[CLIENT_VERSION] = client_version_header_value
263+
end
264+
rescue Faraday::ResourceNotFound
265+
UI.user_error!("#{ErrorMessage::INVALID_APP_ID}: #{app_name}")
266+
end
267+
268+
return response.body
269+
end
270+
250271
private
251272

252273
def client_version_header_value
@@ -281,6 +302,10 @@ def upload_status_url(operation_name)
281302
"/v1/#{operation_name}"
282303
end
283304

305+
def list_releases_url(app_name)
306+
"#{v1_apps_url(app_name)}/releases"
307+
end
308+
284309
def get_udids_url(app_id)
285310
"#{v1alpha_apps_url(app_id)}/testers:getTesterUdids"
286311
end

lib/fastlane/plugin/firebase_app_distribution/helper/firebase_app_distribution_helper.rb

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,10 @@ def blank?(value)
5353
def present?(value)
5454
!blank?(value)
5555
end
56+
57+
def app_name_from_app_id(app_id)
58+
"projects/#{app_id.split(':')[1]}/apps/#{app_id}"
59+
end
5660
end
5761
end
5862
end

spec/firebase_app_distribution_api_client_spec.rb

Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -393,6 +393,73 @@
393393
end
394394
end
395395

396+
describe '#list_releases' do
397+
let(:release1) do
398+
{
399+
name: "#{app_name}/releases/2c3d40a1b",
400+
releaseNotes: {
401+
text: "Here are some release notes!"
402+
},
403+
displayVersion: "1.2.2",
404+
buildVersion: "9",
405+
createTime: "2021-10-05T15:01:23Z"
406+
}
407+
end
408+
let(:release2) do
409+
{
410+
name: "#{app_name}/releases/0a1b2c3d4",
411+
releaseNotes: {
412+
text: "Here are some newer release notes!"
413+
},
414+
displayVersion: "1.2.3",
415+
buildVersion: "10",
416+
createTime: "2021-10-06T15:01:23Z"
417+
}
418+
end
419+
let(:list_releases_headers) do
420+
{
421+
'Authorization' => 'Bearer auth_token',
422+
'X-Client-Version' => "fastlane/#{Fastlane::FirebaseAppDistribution::VERSION}"
423+
}
424+
end
425+
426+
it 'sets the page size and returns the response body' do
427+
stubs.get("/v1/#{app_name}/releases?pageSize=2", list_releases_headers) do |env|
428+
[
429+
200,
430+
{}, # response headers
431+
{ releases: [release2, release1] }
432+
]
433+
end
434+
result = api_client.list_releases(app_name, 2)
435+
expect(result).to eq({ releases: [release2, release1] })
436+
end
437+
438+
it 'sets the page size and token and returns the response body' do
439+
stubs.get("/v1/#{app_name}/releases?pageSize=2&pageToken=the-token", list_releases_headers) do |env|
440+
[
441+
200,
442+
{}, # response headers
443+
{ releases: [release2, release1] }
444+
]
445+
end
446+
result = api_client.list_releases(app_name, 2, 'the-token')
447+
expect(result).to eq({ releases: [release2, release1] })
448+
end
449+
450+
it 'fails and prints correct error message for a 404' do
451+
stubs.get("/v1/#{app_name}/releases?pageSize=2", list_releases_headers) do |env|
452+
[
453+
404,
454+
{}, # response headers
455+
{}
456+
]
457+
end
458+
expect { api_client.list_releases(app_name, 2) }
459+
.to raise_error("#{ErrorMessage::INVALID_APP_ID}: #{app_name}")
460+
end
461+
end
462+
396463
describe '#distribute' do
397464
it 'posts successfuly when tester emails and groupIds are defined' do
398465
payload = { testerEmails: ["testers"], groupAliases: ["groups"] }
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
require 'fastlane/action'
2+
3+
describe Fastlane::Actions::FirebaseAppDistributionGetLatestReleaseAction do
4+
let(:action) { Fastlane::Actions::FirebaseAppDistributionGetLatestReleaseAction }
5+
describe '#run' do
6+
before(:each) do
7+
allow(action).to receive(:fetch_auth_token).and_return('fake-auth-token')
8+
end
9+
10+
it 'returns nil if the app does not have any releases' do
11+
allow_any_instance_of(Fastlane::Client::FirebaseAppDistributionApiClient).to receive(:list_releases).with('projects/1234567890/apps/1:1234567890:ios:321abc456def7890', 1).and_return({})
12+
13+
expect(action.run({ app: '1:1234567890:ios:321abc456def7890' })).to eq(nil)
14+
expect(Fastlane::Actions.lane_context[:FIREBASE_APP_DISTRO_LATEST_RELEASE]).to eq(nil)
15+
end
16+
17+
it 'returns the release if the app has at least one release' do
18+
release = {
19+
name: "projects/1234567890/apps/1:1234567890:ios:321abc456def7890/releases/0a1b2c3d4",
20+
releaseNotes: {
21+
text: "Here are some release notes!"
22+
},
23+
displayVersion: "1.2.3",
24+
buildVersion: "10",
25+
createTime: "2021-10-06T15:01:23Z"
26+
}
27+
allow_any_instance_of(Fastlane::Client::FirebaseAppDistributionApiClient).to receive(:list_releases).with('projects/1234567890/apps/1:1234567890:ios:321abc456def7890', 1).and_return({ releases: [release] })
28+
29+
expect(action.run({ app: '1:1234567890:ios:321abc456def7890' })).to eq(release)
30+
expect(Fastlane::Actions.lane_context[:FIREBASE_APP_DISTRO_LATEST_RELEASE]).to eq(release)
31+
end
32+
end
33+
end

0 commit comments

Comments
 (0)