diff --git a/lib/fastlane/plugin/firebase_app_distribution/actions/firebase_app_distribution_action.rb b/lib/fastlane/plugin/firebase_app_distribution/actions/firebase_app_distribution_action.rb index 4660703..43d1a52 100644 --- a/lib/fastlane/plugin/firebase_app_distribution/actions/firebase_app_distribution_action.rb +++ b/lib/fastlane/plugin/firebase_app_distribution/actions/firebase_app_distribution_action.rb @@ -142,34 +142,6 @@ def self.test_password_from_params(params) test_password && test_password.sub(/\r?\n$/, "") end - def self.app_id_from_params(params) - if params[:app] - app_id = params[:app] - elsif xcode_archive_path - plist_path = params[:googleservice_info_plist_path] - app_id = get_ios_app_id_from_archive_plist(xcode_archive_path, plist_path) - end - if app_id.nil? - UI.crash!(ErrorMessage::MISSING_APP_ID) - end - app_id - end - - def self.xcode_archive_path - # prevents issues on cross-platform build environments where an XCode build happens within - # the same lane - return nil if lane_platform == :android - - # rubocop:disable Require/MissingRequireStatement - Actions.lane_context[SharedValues::XCODEBUILD_ARCHIVE] - # rubocop:enable Require/MissingRequireStatement - end - - def self.lane_platform - # to_sym shouldn't be necessary, but possibly fixes #376 - Actions.lane_context[Actions::SharedValues::PLATFORM_NAME]&.to_sym - end - def self.platform_from_app_id(app_id) if app_id.include?(':ios:') :ios @@ -492,7 +464,7 @@ def self.available_options optional: true), FastlaneCore::ConfigItem.new(key: :googleservice_info_plist_path, env_name: "GOOGLESERVICE_INFO_PLIST_PATH", - description: "Path to your GoogleService-Info.plist file, relative to the archived product path", + description: "Path to your GoogleService-Info.plist file, relative to the archived product path (or directly, if no archived product path is found)", default_value: "GoogleService-Info.plist", optional: true, type: String), diff --git a/lib/fastlane/plugin/firebase_app_distribution/actions/firebase_app_distribution_get_latest_release.rb b/lib/fastlane/plugin/firebase_app_distribution/actions/firebase_app_distribution_get_latest_release.rb index db1de93..5224430 100644 --- a/lib/fastlane/plugin/firebase_app_distribution/actions/firebase_app_distribution_get_latest_release.rb +++ b/lib/fastlane/plugin/firebase_app_distribution/actions/firebase_app_distribution_get_latest_release.rb @@ -19,15 +19,15 @@ def self.run(params) client = Google::Apis::FirebaseappdistributionV1::FirebaseAppDistributionService.new client.authorization = get_authorization(params[:service_credentials_file], params[:firebase_cli_token], params[:service_credentials_json_data], params[:debug]) - UI.message("⏳ Fetching latest release for app #{params[:app]}...") - - parent = app_name_from_app_id(params[:app]) + app_id = app_id_from_params(params) + UI.message("⏳ Fetching latest release for app #{app_id}...") + parent = app_name_from_app_id(app_id) begin releases = client.list_project_app_releases(parent, page_size: 1).releases rescue Google::Apis::Error => err if err.status_code.to_i == 404 - UI.user_error!("#{ErrorMessage::INVALID_APP_ID}: #{params[:app]}") + UI.user_error!("#{ErrorMessage::INVALID_APP_ID}: #{app_id}") else UI.crash!(err) end @@ -35,7 +35,7 @@ def self.run(params) if releases.nil? || releases.empty? latest_release = nil - UI.important("No releases for app #{params[:app]} found in App Distribution. Returning nil and setting Actions.lane_context[SharedValues::FIREBASE_APP_DISTRO_LATEST_RELEASE].") + UI.important("No releases for app #{app_id} found in App Distribution. Returning nil and setting Actions.lane_context[SharedValues::FIREBASE_APP_DISTRO_LATEST_RELEASE].") else # latest_release = append_json_style_fields(response.releases[0].to_h) latest_release = map_release_hash(releases[0]) @@ -80,10 +80,19 @@ def self.details def self.available_options [ + # iOS Specific + FastlaneCore::ConfigItem.new(key: :googleservice_info_plist_path, + env_name: "GOOGLESERVICE_INFO_PLIST_PATH", + description: "Path to your GoogleService-Info.plist file, relative to the archived product path (or directly, if no archived product path is found)", + default_value: "GoogleService-Info.plist", + optional: true, + type: String), + + # General FastlaneCore::ConfigItem.new(key: :app, env_name: "FIREBASEAPPDISTRO_APP", description: "Your app's Firebase App ID. You can find the App ID in the Firebase console, on the General Settings page", - optional: false, + optional: true, type: String), FastlaneCore::ConfigItem.new(key: :firebase_cli_token, description: "Auth token generated using Firebase CLI's login:ci command", diff --git a/lib/fastlane/plugin/firebase_app_distribution/helper/firebase_app_distribution_helper.rb b/lib/fastlane/plugin/firebase_app_distribution/helper/firebase_app_distribution_helper.rb index 4870224..3be033b 100644 --- a/lib/fastlane/plugin/firebase_app_distribution/helper/firebase_app_distribution_helper.rb +++ b/lib/fastlane/plugin/firebase_app_distribution/helper/firebase_app_distribution_helper.rb @@ -1,3 +1,4 @@ +require 'fastlane/action' require 'fastlane_core/ui/ui' require 'cfpropertylist' require 'google/apis/core' @@ -36,15 +37,52 @@ def string_to_array(string, delimiter = /[,\n]/) string.strip.split(delimiter).map(&:strip) end + def app_id_from_params(params) + plist_path = params[:googleservice_info_plist_path] + if params[:app] + UI.message("Using app ID from 'app' parameter") + app_id = params[:app] + elsif xcode_archive_path + UI.message("Using app ID from the GoogleService-Info.plist file located using the 'googleservice_info_plist_path' parameter, relative to the archive path: #{xcode_archive_path}") + app_id = get_ios_app_id_from_archive_plist(xcode_archive_path, plist_path) + elsif plist_path + UI.message("Using app ID from the GoogleService-Info.plist file located using the 'googleservice_info_plist_path' parameter directly since no archive path was found") + app_id = get_ios_app_id_from_plist(plist_path) + end + if app_id.nil? + UI.crash!(ErrorMessage::MISSING_APP_ID) + end + app_id + end + + def lane_platform + # to_sym shouldn't be necessary, but possibly fixes #376 + Actions.lane_context[Actions::SharedValues::PLATFORM_NAME]&.to_sym + end + + def xcode_archive_path + # prevents issues on cross-platform build environments where an XCode build happens within + # the same lane + return nil if lane_platform == :android + + # rubocop:disable Require/MissingRequireStatement + Actions.lane_context[Actions::SharedValues::XCODEBUILD_ARCHIVE] + # rubocop:enable Require/MissingRequireStatement + end + def parse_plist(path) CFPropertyList.native_types(CFPropertyList::List.new(file: path).value) end - def get_ios_app_id_from_archive_plist(archive_path, plist_path) + def get_ios_app_id_from_archive_plist(archive_path, relative_plist_path) app_path = parse_plist("#{archive_path}/Info.plist")["ApplicationProperties"]["ApplicationPath"] UI.shell_error!("can't extract application path from Info.plist at #{archive_path}") if app_path.empty? - identifier = parse_plist("#{archive_path}/Products/#{app_path}/#{plist_path}")["GOOGLE_APP_ID"] - UI.shell_error!("can't extract GOOGLE_APP_ID") if identifier.empty? + return get_ios_app_id_from_plist("#{archive_path}/Products/#{app_path}/#{relative_plist_path}") + end + + def get_ios_app_id_from_plist(plist_path) + identifier = parse_plist(plist_path)["GOOGLE_APP_ID"] + UI.shell_error!("can't extract GOOGLE_APP_ID from #{plist_path}") if identifier.empty? return identifier end diff --git a/spec/firebase_app_distribution_action_spec.rb b/spec/firebase_app_distribution_action_spec.rb index 1c652e7..4a6be7b 100644 --- a/spec/firebase_app_distribution_action_spec.rb +++ b/spec/firebase_app_distribution_action_spec.rb @@ -107,70 +107,6 @@ end end - describe '#xcode_archive_path' do - it 'returns the archive path is set, and platform is not Android' do - allow(Fastlane::Actions).to receive(:lane_context).and_return({ - XCODEBUILD_ARCHIVE: '/path/to/archive' - }) - expect(action.xcode_archive_path).to eq('/path/to/archive') - end - - it 'returns nil if platform is Android' do - allow(Fastlane::Actions).to receive(:lane_context).and_return({ - XCODEBUILD_ARCHIVE: '/path/to/archive', - PLATFORM_NAME: :android - }) - expect(action.xcode_archive_path).to be_nil - end - - it 'returns nil if the archive path is not set' do - allow(Fastlane::Actions).to receive(:lane_context).and_return({}) - expect(action.xcode_archive_path).to be_nil - end - end - - describe '#app_id_from_params' do - it 'returns the app id from the app parameter if set' do - expect(action).not_to(receive(:xcode_archive_path)) - - params = { app: 'app-id' } - result = action.app_id_from_params(params) - - expect(result).to eq('app-id') - end - - it 'raises if the app parameter is not set and there is no archive path' do - allow(action).to receive(:xcode_archive_path).and_return(nil) - - params = {} - expect { action.app_id_from_params(params) } - .to raise_error(ErrorMessage::MISSING_APP_ID) - end - - it 'returns the app id from the plist if the archive path is set' do - allow(action).to receive(:xcode_archive_path).and_return('/path/to/archive') - allow(action).to receive(:get_ios_app_id_from_archive_plist) - .with('/path/to/archive', '/path/to/plist') - .and_return('app-id-from-plist') - - params = { googleservice_info_plist_path: '/path/to/plist' } - result = action.app_id_from_params(params) - - expect(result).to eq('app-id-from-plist') - end - - it 'raises if the app parameter is not set and the plist is empty' do - allow(action).to receive(:xcode_archive_path).and_return('/path/to/archive') - allow(action).to receive(:get_ios_app_id_from_archive_plist) - .with('/path/to/archive', '/path/to/plist') - .and_return(nil) - - params = { googleservice_info_plist_path: '/path/to/plist' } - expect { action.app_id_from_params(params) } - .to raise_error(ErrorMessage::MISSING_APP_ID) - end - end - describe '#release_notes' do before do allow(Fastlane::Actions.lane_context).to receive('[]') diff --git a/spec/firebase_app_distribution_helper_spec.rb b/spec/firebase_app_distribution_helper_spec.rb index c6250af..fb9b698 100644 --- a/spec/firebase_app_distribution_helper_spec.rb +++ b/spec/firebase_app_distribution_helper_spec.rb @@ -136,4 +136,80 @@ end.to raise_error("Unsupported distribution file format, should be .ipa, .apk or .aab") end end + + describe '#xcode_archive_path' do + it 'returns the archive path is set, and platform is not Android' do + allow(Fastlane::Actions).to receive(:lane_context).and_return({ + XCODEBUILD_ARCHIVE: '/path/to/archive' + }) + expect(helper.xcode_archive_path).to eq('/path/to/archive') + end + + it 'returns nil if platform is Android' do + allow(Fastlane::Actions).to receive(:lane_context).and_return({ + XCODEBUILD_ARCHIVE: '/path/to/archive', + PLATFORM_NAME: :android + }) + expect(helper.xcode_archive_path).to be_nil + end + + it 'returns nil if the archive path is not set' do + allow(Fastlane::Actions).to receive(:lane_context).and_return({}) + expect(helper.xcode_archive_path).to be_nil + end + end + + describe '#app_id_from_params' do + it 'returns the app id from the app parameter if set' do + expect(helper).not_to(receive(:xcode_archive_path)) + + params = { app: 'app-id' } + result = helper.app_id_from_params(params) + + expect(result).to eq('app-id') + end + + it 'raises if the app parameter is not set and there is no archive path' do + allow(helper).to receive(:xcode_archive_path).and_return(nil) + + params = {} + expect { helper.app_id_from_params(params) } + .to raise_error(ErrorMessage::MISSING_APP_ID) + end + + it 'returns the app id from the plist if the archive path is set' do + allow(helper).to receive(:xcode_archive_path).and_return('/path/to/archive') + allow(helper).to receive(:get_ios_app_id_from_archive_plist) + .with('/path/to/archive', '/path/to/plist') + .and_return('app-id-from-plist') + + params = { googleservice_info_plist_path: '/path/to/plist' } + result = helper.app_id_from_params(params) + + expect(result).to eq('app-id-from-plist') + end + + it 'returns the app id from the plist if there is no archive path and it is found directly at the given path' do + allow(helper).to receive(:xcode_archive_path).and_return(nil) + allow(helper).to receive(:get_ios_app_id_from_plist) + .with('/path/to/plist') + .and_return('app-id-from-plist') + + params = { googleservice_info_plist_path: '/path/to/plist' } + result = helper.app_id_from_params(params) + + expect(result).to eq('app-id-from-plist') + end + + it 'raises if the app parameter is not set and the plist is empty' do + allow(helper).to receive(:xcode_archive_path).and_return('/path/to/archive') + allow(helper).to receive(:get_ios_app_id_from_archive_plist) + .with('/path/to/archive', '/path/to/plist') + .and_return(nil) + + params = { googleservice_info_plist_path: '/path/to/plist' } + expect { helper.app_id_from_params(params) } + .to raise_error(ErrorMessage::MISSING_APP_ID) + end + end end