Skip to content

Commit 2562e8e

Browse files
authored
Merge pull request #194 from fastlane/get-udids
Add firebase_app_distribution_get_udids action
2 parents 7d9c7d1 + de1aa4c commit 2562e8e

File tree

5 files changed

+221
-1
lines changed

5 files changed

+221
-1
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,97 @@
1+
require 'fastlane/action'
2+
require 'open3'
3+
require 'shellwords'
4+
require 'googleauth'
5+
require_relative '../helper/firebase_app_distribution_helper'
6+
require_relative '../helper/firebase_app_distribution_error_message'
7+
require_relative '../client/firebase_app_distribution_api_client'
8+
require_relative '../helper/firebase_app_distribution_auth_client'
9+
10+
module Fastlane
11+
module Actions
12+
class FirebaseAppDistributionGetUdidsAction < Action
13+
extend Auth::FirebaseAppDistributionAuthClient
14+
extend Helper::FirebaseAppDistributionHelper
15+
16+
def self.run(params)
17+
auth_token = fetch_auth_token(params[:service_credentials_file], params[:firebase_cli_token])
18+
fad_api_client = Client::FirebaseAppDistributionApiClient.new(auth_token, params[:debug])
19+
20+
app_id = params[:app]
21+
udids = fad_api_client.get_udids(app_id)
22+
23+
if udids.empty?
24+
UI.important("App Distribution fetched 0 tester UDIDs. Nothing written to output file.")
25+
else
26+
write_udids_to_file(udids, params[:output_file])
27+
UI.success("🎉 App Distribution tester UDIDs written to: #{params[:output_file]}")
28+
end
29+
end
30+
31+
def self.write_udids_to_file(udids, output_file)
32+
File.open(output_file, 'w') do |f|
33+
f.write("Device ID\tDevice Name\tDevice Platform\n")
34+
udids.each do |tester_udid|
35+
f.write("#{tester_udid[:udid]}\t#{tester_udid[:name]}\t#{tester_udid[:platform]}\n")
36+
end
37+
end
38+
end
39+
40+
def self.description
41+
"Download the UDIDs of your Firebase App Distribution testers"
42+
end
43+
44+
def self.authors
45+
["Lee Kellogg"]
46+
end
47+
48+
# supports markdown.
49+
def self.details
50+
"Export your testers' device identifiers in a CSV file, so you can add them your provisioning profile. This file can be imported into your Apple developer account using the Register Multiple Devices option. See the [App Distribution docs](https://firebase.google.com/docs/app-distribution/ios/distribute-console#register-tester-devices) for more info."
51+
end
52+
53+
def self.available_options
54+
[
55+
FastlaneCore::ConfigItem.new(key: :app,
56+
env_name: "FIREBASEAPPDISTRO_APP",
57+
description: "Your app's Firebase App ID. You can find the App ID in the Firebase console, on the General Settings page",
58+
optional: false,
59+
type: String),
60+
FastlaneCore::ConfigItem.new(key: :output_file,
61+
env_name: "FIREBASEAPPDISTRO_OUTPUT_FILE",
62+
description: "The path to the file where the tester UDIDs will be written",
63+
optional: false,
64+
type: String),
65+
FastlaneCore::ConfigItem.new(key: :firebase_cli_token,
66+
description: "Auth token for firebase cli",
67+
optional: true,
68+
type: String),
69+
FastlaneCore::ConfigItem.new(key: :service_credentials_file,
70+
description: "Path to Google service account json",
71+
optional: true,
72+
type: String),
73+
FastlaneCore::ConfigItem.new(key: :debug,
74+
description: "Print verbose debug output",
75+
optional: true,
76+
default_value: false,
77+
is_string: false)
78+
]
79+
end
80+
81+
def self.is_supported?(platform)
82+
[:ios].include?(platform)
83+
end
84+
85+
def self.example_code
86+
[
87+
<<-CODE
88+
firebase_app_distribution_get_udids(
89+
app: "1:1234567890:ios:0a1b2c3d4e5f67890",
90+
output_file: "tester_udids.txt",
91+
)
92+
CODE
93+
]
94+
end
95+
end
96+
end
97+
end

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

+21
Original file line numberDiff line numberDiff line change
@@ -180,6 +180,23 @@ def get_upload_status(app_id, upload_token)
180180
return UploadStatusResponse.new(response.body)
181181
end
182182

183+
# Get tester UDIDs
184+
#
185+
# args
186+
# app_id - Firebase App ID
187+
#
188+
# Returns a list of hashes containing tester device info
189+
def get_udids(app_id)
190+
begin
191+
response = connection.get(get_udids_url(app_id)) do |request|
192+
request.headers[AUTHORIZATION] = "Bearer " + @auth_token
193+
end
194+
rescue Faraday::ResourceNotFound
195+
UI.user_error!("#{ErrorMessage::INVALID_APP_ID}: #{app_id}")
196+
end
197+
response.body[:testerUdids] || []
198+
end
199+
183200
private
184201

185202
def v1_apps_url(app_id)
@@ -202,6 +219,10 @@ def upload_status_url(app_id, app_token)
202219
"#{v1_apps_url(app_id)}/upload_status/#{app_token}"
203220
end
204221

222+
def get_udids_url(app_id)
223+
"#{v1_apps_url(app_id)}/testers:getTesterUdids"
224+
end
225+
205226
def get_upload_token(project_number, app_id, binary_path)
206227
binary_hash = Digest::SHA256.hexdigest(read_binary(binary_path))
207228
CGI.escape("projects/#{project_number}/apps/#{app_id}/releases/-/binaries/#{binary_hash}")
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
module Fastlane
22
module FirebaseAppDistribution
3-
VERSION = "0.2.6.pre.1"
3+
VERSION = "0.2.7.pre.1"
44
end
55
end

spec/firebase_app_distribution_api_client_spec.rb

+33
Original file line numberDiff line numberDiff line change
@@ -345,6 +345,39 @@
345345
end
346346
end
347347

348+
describe '#get_udids' do
349+
let(:udids) do
350+
[
351+
{ udid: 'device-udid-1', name: 'device-name-1', platform: 'ios' },
352+
{ udid: 'device-udid-1', name: 'device-name-1', platform: 'ios' }
353+
]
354+
end
355+
356+
it 'returns the list of UDIDs when the get call is successfull' do
357+
stubs.get("/v1alpha/apps/app_id/testers:getTesterUdids", headers) do |env|
358+
[
359+
200,
360+
{},
361+
{ testerUdids: udids }
362+
]
363+
end
364+
result = api_client.get_udids("app_id")
365+
expect(result).to eq(udids)
366+
end
367+
368+
it 'returns an empty list UDIDs when there are no udids' do
369+
stubs.get("/v1alpha/apps/app_id/testers:getTesterUdids", headers) do |env|
370+
[
371+
200,
372+
{},
373+
{}
374+
]
375+
end
376+
result = api_client.get_udids("app_id")
377+
expect(result).to eq([])
378+
end
379+
end
380+
348381
describe '#enable_access' do
349382
it 'posts successfully when tester emails and groupIds are defined' do
350383
payload = { emails: ["testers"], groupIds: ["groups"] }
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
require 'fastlane/action'
2+
3+
describe Fastlane::Actions::FirebaseAppDistributionGetUdidsAction do
4+
let(:action) { Fastlane::Actions::FirebaseAppDistributionGetUdidsAction }
5+
let(:app_id) { '1:1234567890:android:321abc456def7890' }
6+
let(:mock_file) { StringIO.new }
7+
let(:output_file_path) { '/path/to/output/file.txt' }
8+
9+
describe '#run' do
10+
describe 'when there are testers with udids' do
11+
before(:each) do
12+
allow(action).to receive(:fetch_auth_token).and_return('fake-auth-token')
13+
allow_any_instance_of(Fastlane::Client::FirebaseAppDistributionApiClient)
14+
.to receive(:get_udids)
15+
.with(app_id)
16+
.and_return(
17+
[
18+
{
19+
udid: 'device-udid-1',
20+
name: 'device-name-1',
21+
platform: 'ios'
22+
},
23+
{
24+
udid: 'device-udid-2',
25+
name: 'device-name-2',
26+
platform: 'ios'
27+
}
28+
]
29+
)
30+
end
31+
32+
let(:params) do
33+
{
34+
app: app_id,
35+
output_file: output_file_path
36+
}
37+
end
38+
39+
it 'writes UDIDs to file' do
40+
expect(File).to receive(:open).with(output_file_path, 'w').and_yield(mock_file)
41+
action.run(params)
42+
expect(mock_file.string).to eq("Device ID\tDevice Name\tDevice Platform\ndevice-udid-1\tdevice-name-1\tios\ndevice-udid-2\tdevice-name-2\tios\n")
43+
end
44+
end
45+
end
46+
47+
describe 'when there are no testers with udids' do
48+
before(:each) do
49+
allow(action).to receive(:fetch_auth_token).and_return('fake-auth-token')
50+
allow_any_instance_of(Fastlane::Client::FirebaseAppDistributionApiClient)
51+
.to receive(:get_udids)
52+
.with(app_id)
53+
.and_return([])
54+
end
55+
56+
let(:params) do
57+
{
58+
app: app_id,
59+
output_file: output_file_path
60+
}
61+
end
62+
63+
it 'does not write to file' do
64+
allow(File).to receive(:open).and_yield(mock_file)
65+
action.run(params)
66+
expect(File).not_to(have_received(:open))
67+
end
68+
end
69+
end

0 commit comments

Comments
 (0)