From d5cd729bd73cf0d910dd664b36814402ed2ca63b Mon Sep 17 00:00:00 2001 From: Rynaard Burger Date: Wed, 5 Apr 2023 18:42:03 +0200 Subject: [PATCH 01/12] Add bundletool_generate_signed_apk action --- ...ol_generate_universal_signed_apk_action.rb | 104 ++++++++++++++++++ 1 file changed, 104 insertions(+) create mode 100644 lib/fastlane/plugin/wpmreleasetoolkit/actions/bundletool/bundletool_generate_universal_signed_apk_action.rb diff --git a/lib/fastlane/plugin/wpmreleasetoolkit/actions/bundletool/bundletool_generate_universal_signed_apk_action.rb b/lib/fastlane/plugin/wpmreleasetoolkit/actions/bundletool/bundletool_generate_universal_signed_apk_action.rb new file mode 100644 index 000000000..a14ed2e1f --- /dev/null +++ b/lib/fastlane/plugin/wpmreleasetoolkit/actions/bundletool/bundletool_generate_universal_signed_apk_action.rb @@ -0,0 +1,104 @@ +module Fastlane + module Actions + class BundletoolGenerateUniversalSignedApkAction < Action + def self.run(params) + aab_path = params[:aab_path] + apk_output_path = params[:apk_output_path] + keystore_path = params[:keystore_path] + keystore_password = params[:keystore_password] + keystore_key_alias = params[:keystore_key_alias] + signing_key_password = params[:signing_key_password] + + begin + sh('command -v bundletool > /dev/null') + rescue StandardError + UI.user_error!('bundletool is not installed. Please install it using the instructions at https://developer.android.com/studio/command-line/bundletool') + raise + end + + sh('bundletool', 'build-apks', + '--mode', 'universal', + '--bundle', aab_path, + '--output-format', 'DIRECTORY', + '--output', apk_output_path, + '--ks', keystore_path, + '--ks-pass', keystore_password, + '--ks-key-alias', keystore_key_alias, + '--key-pass', signing_key_password) + end + + ##################################################### + # @!group Documentation + ##################################################### + + def self.description + 'Generates a signed universal APK from the specified AAB' + end + + def self.details + 'Generates a signed universal APK from the specified AAB' + end + + def self.available_options + [ + FastlaneCore::ConfigItem.new( + key: :aab_path, + env_name: 'BUNDLETOOL_AAB_PATH', + description: 'The path to the AAB file', + type: String, + optional: false, + default_value: nil + ), + FastlaneCore::ConfigItem.new( + key: :apk_output_path, + env_name: 'BUNDLETOOL_APK_OUTPUT_PATH', + description: 'The path to the output directory where the APK will be generated', + type: String, + optional: false, + default_value: nil + ), + FastlaneCore::ConfigItem.new( + key: :keystore_path, + env_name: 'BUNDLETOOL_KEYSTORE_PATH', + description: 'The path to the keystore file', + type: String, + optional: false, + default_value: nil + ), + FastlaneCore::ConfigItem.new( + key: :keystore_password, + env_name: 'BUNDLETOOL_KEYSTORE_PASSWORD', + description: 'The password for the keystore', + type: String, + optional: false, + default_value: nil + ), + FastlaneCore::ConfigItem.new( + key: :keystore_key_alias, + env_name: 'BUNDLETOOL_KEYSTORE_KEY_ALIAS', + description: 'The alias of the key in the keystore', + type: String, + optional: false, + default_value: nil + ), + FastlaneCore::ConfigItem.new( + key: :signing_key_password, + env_name: 'BUNDLETOOL_SIGNING_KEY_PASSWORD', + description: 'The password for the signing key', + type: String, + optional: false, + default_value: nil + ), + ] + end + + def self.authors + ['Automattic'] + end + + def self.is_supported?(platform) + true + end + end + end +end From f9b2fc0ef364cc6c56faedb490314114a15bf060 Mon Sep 17 00:00:00 2001 From: Rynaard Burger Date: Mon, 24 Apr 2023 13:15:11 +0200 Subject: [PATCH 02/12] Add unit test for action --- ...tool_generate_universal_signed_apk_spec.rb | 24 +++++++++++++++++++ 1 file changed, 24 insertions(+) create mode 100644 spec/bundletool_generate_universal_signed_apk_spec.rb diff --git a/spec/bundletool_generate_universal_signed_apk_spec.rb b/spec/bundletool_generate_universal_signed_apk_spec.rb new file mode 100644 index 000000000..78629358d --- /dev/null +++ b/spec/bundletool_generate_universal_signed_apk_spec.rb @@ -0,0 +1,24 @@ +require 'spec_helper' + +describe Fastlane::Actions::BundletoolGenerateUniversalSignedApkAction do + let(:aab_path) { 'path/to/app.aab' } + let(:apk_output_path) { 'path/to/output' } + let(:keystore_path) { 'path/to/keystore' } + let(:keystore_password) { 'keystore_password' } + let(:keystore_key_alias) { 'keystore_key_alias' } + let(:signing_key_password) { 'signing_key_password' } + + describe 'bundletool_generate_universal_signed_apk' do + it 'calls the `bundletool` command with the correct arguments' do + cmd = run_described_fastlane_action( + aab_path: aab_path, + apk_output_path: apk_output_path, + keystore_path: keystore_path, + keystore_password: keystore_password, + keystore_key_alias: keystore_key_alias, + signing_key_password: signing_key_password + ) + expect(cmd).to eq("bundletool build-apks --mode universal --bundle #{aab_path} --output-format DIRECTORY --output #{apk_output_path} --ks #{keystore_path} --ks-pass #{keystore_password} --ks-key-alias #{keystore_key_alias} --key-pass #{signing_key_password}") + end + end +end From d27847db7cbe1af7fee60a8c44b7c8aaeb60d1a9 Mon Sep 17 00:00:00 2001 From: Oguz Kocer Date: Tue, 11 Apr 2023 18:33:17 -0400 Subject: [PATCH 03/12] Add the branch information to the 'this is not a release branch' error --- .../actions/android/android_completecodefreeze_prechecks.rb | 2 +- .../actions/ios/ios_completecodefreeze_prechecks.rb | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/fastlane/plugin/wpmreleasetoolkit/actions/android/android_completecodefreeze_prechecks.rb b/lib/fastlane/plugin/wpmreleasetoolkit/actions/android/android_completecodefreeze_prechecks.rb index b57f911ce..c40c3fb99 100644 --- a/lib/fastlane/plugin/wpmreleasetoolkit/actions/android/android_completecodefreeze_prechecks.rb +++ b/lib/fastlane/plugin/wpmreleasetoolkit/actions/android/android_completecodefreeze_prechecks.rb @@ -7,7 +7,7 @@ def self.run(params) require_relative '../../helper/android/android_version_helper' require_relative '../../helper/android/android_git_helper' - UI.user_error!('This is not a release branch. Abort.') unless other_action.git_branch.start_with?('release/') + UI.user_error!("Current branch - '#{other_action.git_branch}' - is not a release branch. Abort.") unless other_action.git_branch.start_with?('release/') version = Fastlane::Helper::Android::VersionHelper.get_public_version message = "Completing code freeze for: #{version}\n" diff --git a/lib/fastlane/plugin/wpmreleasetoolkit/actions/ios/ios_completecodefreeze_prechecks.rb b/lib/fastlane/plugin/wpmreleasetoolkit/actions/ios/ios_completecodefreeze_prechecks.rb index 3d636a6fa..f340a8771 100644 --- a/lib/fastlane/plugin/wpmreleasetoolkit/actions/ios/ios_completecodefreeze_prechecks.rb +++ b/lib/fastlane/plugin/wpmreleasetoolkit/actions/ios/ios_completecodefreeze_prechecks.rb @@ -7,7 +7,7 @@ def self.run(params) require_relative '../../helper/ios/ios_version_helper' require_relative '../../helper/ios/ios_git_helper' - UI.user_error!('This is not a release branch. Abort.') unless other_action.git_branch.start_with?('release/') + UI.user_error!("Current branch - '#{other_action.git_branch}' - is not a release branch. Abort.") unless other_action.git_branch.start_with?('release/') version = Fastlane::Helper::Ios::VersionHelper.get_public_version message = "Completing code freeze for: #{version}\n" From 96df190bf4b3714e7bb6b48330b7d3b946cf5bc5 Mon Sep 17 00:00:00 2001 From: Oguz Kocer Date: Tue, 11 Apr 2023 18:36:17 -0400 Subject: [PATCH 04/12] Add changelog entry for the debug information addition for 'this is not a release branch' --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 8fe2d0690..2028d1317 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -20,6 +20,7 @@ _None_ - Add "Mobile Secrets" to `configure_update` current branch message to clarify which repo it's referring to. [#455] - `buildkite_trigger_build` now prints the web URL of the newly scheduled build, to allow you to easily open it via cmd-click. [#460] +- Add the branch information to the 'This is not a release branch' error that's thrown from complete code freeze lane. [#461] ## 7.0.0 From dec39d884eaeec1b7ce82cf9884f92d68aec66bd Mon Sep 17 00:00:00 2001 From: Rynaard Burger Date: Mon, 24 Apr 2023 16:11:25 +0200 Subject: [PATCH 05/12] Update CHANGELOG.md to include new action --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 2028d1317..a562d4950 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,7 +10,7 @@ _None_ ### New Features -_None_ +- Add new `android_generate_apk_from_aab` action to generate an APK from a given AAB. ### Bug Fixes From 16e5050557dcad2312d1b7db19c7b1ddfa71119b Mon Sep 17 00:00:00 2001 From: Rynaard Burger Date: Tue, 25 Apr 2023 12:16:39 +0200 Subject: [PATCH 06/12] Move and rename action to the android namespace --- .../android/android_generate_apk_from_aab.rb | 132 ++++++++++++++++++ ...ol_generate_universal_signed_apk_action.rb | 104 -------------- 2 files changed, 132 insertions(+), 104 deletions(-) create mode 100644 lib/fastlane/plugin/wpmreleasetoolkit/actions/android/android_generate_apk_from_aab.rb delete mode 100644 lib/fastlane/plugin/wpmreleasetoolkit/actions/bundletool/bundletool_generate_universal_signed_apk_action.rb diff --git a/lib/fastlane/plugin/wpmreleasetoolkit/actions/android/android_generate_apk_from_aab.rb b/lib/fastlane/plugin/wpmreleasetoolkit/actions/android/android_generate_apk_from_aab.rb new file mode 100644 index 000000000..6e01723c5 --- /dev/null +++ b/lib/fastlane/plugin/wpmreleasetoolkit/actions/android/android_generate_apk_from_aab.rb @@ -0,0 +1,132 @@ +module Fastlane + module Actions + class AndroidGenerateApkFromAabAction < Action + def self.generate_command(aab_file_path, apk_output_file_path, keystore_path, keystore_password, keystore_key_alias, signing_key_password) + command = "bundletool build-apks --mode universal --bundle #{aab_file_path} --output-format DIRECTORY --output #{apk_output_file_path} " + code_sign_arguments = "--ks #{keystore_path} --ks-pass #{keystore_password} --ks-key-alias #{keystore_key_alias} --key-pass #{signing_key_password} " + move_and_cleanup_command = "&& mv #{apk_output_file_path}/universal.apk #{apk_output_file_path}_tmp && rm -rf #{apk_output_file_path} && mv #{apk_output_file_path}_tmp #{apk_output_file_path}" + + # Attempt to code sign the APK if a keystore_path parameter is specified + command += code_sign_arguments unless keystore_path.nil? + + # Move and rename the universal.apk file to the specified output path and cleanup the directory created by bundletool + command += move_and_cleanup_command + + return command + end + + def self.run(params) + begin + sh('command -v bundletool > /dev/null') + rescue StandardError + UI.user_error!('bundletool is not installed. Please install it using the instructions at https://developer.android.com/studio/command-line/bundletool.') + raise + end + + # If no AAB param was provided, attempt to get it from the lane context + # First GRADLE_ALL_AAB_OUTPUT_PATHS if only one + # Second GRADLE_AAB_OUTPUT_PATH if it is set + # Else use the specified parameter value + if params[:aab_file_path].nil? + all_aab_paths = Actions.lane_context[SharedValues::GRADLE_ALL_AAB_OUTPUT_PATHS] || [] + aab_file_path = if all_aab_paths.count == 1 + all_aab_paths.first + else + Actions.lane_context[SharedValues::GRADLE_AAB_OUTPUT_PATH] + end + else + aab_file_path = params[:aab_file_path] + end + + # If no AAB file path was found, raise an error + if aab_file_path.nil? + UI.user_error!('No AAB file path was specified and none was found in the lane context. Please specify the `aab_file_path` parameter or ensure that the relevant build action has been run prior to this action.') + raise + end + + apk_output_file_path = params[:apk_output_file_path] + keystore_path = params[:keystore_path] + keystore_password = params[:keystore_password] + keystore_key_alias = params[:keystore_key_alias] + signing_key_password = params[:signing_key_password] + + sh(generate_command(aab_file_path, apk_output_file_path, keystore_path, keystore_password, keystore_key_alias, signing_key_password)) + end + + ##################################################### + # @!group Documentation + ##################################################### + + def self.description + 'Generates an APK from the specified AAB' + end + + def self.details + 'Generates an APK from the specified AAB' + end + + def self.available_options + [ + FastlaneCore::ConfigItem.new( + key: :aab_file_path, + env_name: 'ANDROID_AAB_FILE_PATH', + description: 'The path to the AAB file. If not speicified, the action will attempt to read from the lane context using the `SharedValues::GRADLE_ALL_AAB_OUTPUT_PATHS` and `SharedValues::GRADLE_AAB_OUTPUT_PATH` keys', + type: String, + optional: true, + default_value: nil, + verify_block: proc { |p| UI.user_error!("AAB path `#{p}` is not a valid file path.") unless File.file?(p) } + ), + FastlaneCore::ConfigItem.new( + key: :apk_output_file_path, + env_name: 'ANDROID_APK_OUTPUT_PATH', + description: 'The output path where the APK file will be generated. The directory will be created if it does not yet exist', + type: String, + optional: false, + default_value: nil + ), + FastlaneCore::ConfigItem.new( + key: :keystore_path, + env_name: 'ANDROID_KEYSTORE_PATH', + description: 'The path to the keystore file', + type: String, + optional: true, + default_value: nil, + verify_block: proc { |p| UI.user_error!("Keystore file path `#{p}` is not a valid file path.") unless File.file?(p) || p.nil } + ), + FastlaneCore::ConfigItem.new( + key: :keystore_password, + env_name: 'ANDROID_KEYSTORE_PASSWORD', + description: 'The password for the keystore', + type: String, + optional: true, + default_value: nil + ), + FastlaneCore::ConfigItem.new( + key: :keystore_key_alias, + env_name: 'ANDROID_KEYSTORE_KEY_ALIAS', + description: 'The alias of the key in the keystore', + type: String, + optional: true, + default_value: nil + ), + FastlaneCore::ConfigItem.new( + key: :signing_key_password, + env_name: 'ANDROID_SIGNING_KEY_PASSWORD', + description: 'The password for the signing key', + type: String, + optional: true, + default_value: nil + ), + ] + end + + def self.authors + ['Automattic'] + end + + def self.is_supported?(platform) + true + end + end + end +end diff --git a/lib/fastlane/plugin/wpmreleasetoolkit/actions/bundletool/bundletool_generate_universal_signed_apk_action.rb b/lib/fastlane/plugin/wpmreleasetoolkit/actions/bundletool/bundletool_generate_universal_signed_apk_action.rb deleted file mode 100644 index a14ed2e1f..000000000 --- a/lib/fastlane/plugin/wpmreleasetoolkit/actions/bundletool/bundletool_generate_universal_signed_apk_action.rb +++ /dev/null @@ -1,104 +0,0 @@ -module Fastlane - module Actions - class BundletoolGenerateUniversalSignedApkAction < Action - def self.run(params) - aab_path = params[:aab_path] - apk_output_path = params[:apk_output_path] - keystore_path = params[:keystore_path] - keystore_password = params[:keystore_password] - keystore_key_alias = params[:keystore_key_alias] - signing_key_password = params[:signing_key_password] - - begin - sh('command -v bundletool > /dev/null') - rescue StandardError - UI.user_error!('bundletool is not installed. Please install it using the instructions at https://developer.android.com/studio/command-line/bundletool') - raise - end - - sh('bundletool', 'build-apks', - '--mode', 'universal', - '--bundle', aab_path, - '--output-format', 'DIRECTORY', - '--output', apk_output_path, - '--ks', keystore_path, - '--ks-pass', keystore_password, - '--ks-key-alias', keystore_key_alias, - '--key-pass', signing_key_password) - end - - ##################################################### - # @!group Documentation - ##################################################### - - def self.description - 'Generates a signed universal APK from the specified AAB' - end - - def self.details - 'Generates a signed universal APK from the specified AAB' - end - - def self.available_options - [ - FastlaneCore::ConfigItem.new( - key: :aab_path, - env_name: 'BUNDLETOOL_AAB_PATH', - description: 'The path to the AAB file', - type: String, - optional: false, - default_value: nil - ), - FastlaneCore::ConfigItem.new( - key: :apk_output_path, - env_name: 'BUNDLETOOL_APK_OUTPUT_PATH', - description: 'The path to the output directory where the APK will be generated', - type: String, - optional: false, - default_value: nil - ), - FastlaneCore::ConfigItem.new( - key: :keystore_path, - env_name: 'BUNDLETOOL_KEYSTORE_PATH', - description: 'The path to the keystore file', - type: String, - optional: false, - default_value: nil - ), - FastlaneCore::ConfigItem.new( - key: :keystore_password, - env_name: 'BUNDLETOOL_KEYSTORE_PASSWORD', - description: 'The password for the keystore', - type: String, - optional: false, - default_value: nil - ), - FastlaneCore::ConfigItem.new( - key: :keystore_key_alias, - env_name: 'BUNDLETOOL_KEYSTORE_KEY_ALIAS', - description: 'The alias of the key in the keystore', - type: String, - optional: false, - default_value: nil - ), - FastlaneCore::ConfigItem.new( - key: :signing_key_password, - env_name: 'BUNDLETOOL_SIGNING_KEY_PASSWORD', - description: 'The password for the signing key', - type: String, - optional: false, - default_value: nil - ), - ] - end - - def self.authors - ['Automattic'] - end - - def self.is_supported?(platform) - true - end - end - end -end From f99c55e69766bdf81cb9780135da48f6287675b1 Mon Sep 17 00:00:00 2001 From: Rynaard Burger Date: Tue, 25 Apr 2023 12:36:35 +0200 Subject: [PATCH 07/12] Rename and update spec --- spec/android_generate_apk_from_aab_spec.rb | 87 +++++++++++++++++++ ...tool_generate_universal_signed_apk_spec.rb | 24 ----- 2 files changed, 87 insertions(+), 24 deletions(-) create mode 100644 spec/android_generate_apk_from_aab_spec.rb delete mode 100644 spec/bundletool_generate_universal_signed_apk_spec.rb diff --git a/spec/android_generate_apk_from_aab_spec.rb b/spec/android_generate_apk_from_aab_spec.rb new file mode 100644 index 000000000..4dc4ad718 --- /dev/null +++ b/spec/android_generate_apk_from_aab_spec.rb @@ -0,0 +1,87 @@ +require 'spec_helper' + +describe Fastlane::Actions::AndroidGenerateApkFromAabAction do + before do + allow(File).to receive(:file?).with(aab_file_path).and_return('mocked file data') + allow(File).to receive(:file?).with(apk_output_file_path).and_return('mocked file data') + allow(File).to receive(:file?).with(keystore_path).and_return('mocked file data') + end + + let(:aab_file_path) { 'path/to/app.aab' } + let(:apk_output_file_path) { 'path/to/app.apk' } + let(:keystore_path) { 'path/to/keystore' } + let(:keystore_password) { 'keystore_password' } + let(:keystore_key_alias) { 'keystore_key_alias' } + let(:signing_key_password) { 'signing_key_password' } + + def generate_command(apk_output_file_path:, aab_file_path: nil, keystore_path: nil, keystore_password: nil, keystore_key_alias: nil, signing_key_password: nil) + command = "bundletool build-apks --mode universal --bundle #{aab_file_path} --output-format DIRECTORY --output #{apk_output_file_path} " + code_sign_arguments = "--ks #{keystore_path} --ks-pass #{keystore_password} --ks-key-alias #{keystore_key_alias} --key-pass #{signing_key_password} " + move_and_cleanup_command = "&& mv #{apk_output_file_path}/universal.apk #{apk_output_file_path}_tmp && rm -rf #{apk_output_file_path} && mv #{apk_output_file_path}_tmp #{apk_output_file_path}" + + # Append the code signing arguments + command += code_sign_arguments unless keystore_path.nil? + + # Append the move and cleanup command + command += move_and_cleanup_command + return command + end + + describe 'android_generate_apk_from_aab' do + it 'calls the `bundletool` command with the correct arguments when generating a signed APK' do + cmd = run_described_fastlane_action( + aab_file_path: aab_file_path, + apk_output_file_path: apk_output_file_path, + keystore_path: keystore_path, + keystore_password: keystore_password, + keystore_key_alias: keystore_key_alias, + signing_key_password: signing_key_password + ) + expected_command = generate_command(aab_file_path: aab_file_path, + apk_output_file_path: apk_output_file_path, + keystore_path: keystore_path, + keystore_password: keystore_password, + keystore_key_alias: keystore_key_alias, + signing_key_password: signing_key_password) + expect(cmd).to eq(expected_command) + end + + it 'calls the `bundletool` command with the correct arguments when generating an unsigned APK' do + cmd = run_described_fastlane_action( + aab_file_path: aab_file_path, + apk_output_file_path: apk_output_file_path + ) + expected_command = generate_command(aab_file_path: aab_file_path, + apk_output_file_path: apk_output_file_path) + expect(cmd).to eq(expected_command) + end + + it 'calls the `bundletool` command with the correct arguments and use the path to the AAB from the lane context if the SharedValues::GRADLE_AAB_OUTPUT_PATH key is set' do + aab_path_from_context = 'path/from/context/app.aab' + + Fastlane::Actions.lane_context[Fastlane::Actions::SharedValues::GRADLE_AAB_OUTPUT_PATH] = aab_path_from_context + + cmd = run_described_fastlane_action( + apk_output_file_path: apk_output_file_path + ) + + expected_command = generate_command(aab_file_path: aab_path_from_context, + apk_output_file_path: apk_output_file_path) + expect(cmd).to eq(expected_command) + end + + it 'calls the `bundletool` command with the correct arguments and use the path to the AAB from the lane context if the SharedValues::GRADLE_ALL_AAB_OUTPUT_PATHS key is set' do + all_aab_paths_from_context = ['first/path/from/context/app.aab'] + + Fastlane::Actions.lane_context[Fastlane::Actions::SharedValues::GRADLE_ALL_AAB_OUTPUT_PATHS] = all_aab_paths_from_context + + cmd = run_described_fastlane_action( + apk_output_file_path: apk_output_file_path + ) + + expected_command = generate_command(aab_file_path: all_aab_paths_from_context.first, + apk_output_file_path: apk_output_file_path) + expect(cmd).to eq(expected_command) + end + end +end diff --git a/spec/bundletool_generate_universal_signed_apk_spec.rb b/spec/bundletool_generate_universal_signed_apk_spec.rb deleted file mode 100644 index 78629358d..000000000 --- a/spec/bundletool_generate_universal_signed_apk_spec.rb +++ /dev/null @@ -1,24 +0,0 @@ -require 'spec_helper' - -describe Fastlane::Actions::BundletoolGenerateUniversalSignedApkAction do - let(:aab_path) { 'path/to/app.aab' } - let(:apk_output_path) { 'path/to/output' } - let(:keystore_path) { 'path/to/keystore' } - let(:keystore_password) { 'keystore_password' } - let(:keystore_key_alias) { 'keystore_key_alias' } - let(:signing_key_password) { 'signing_key_password' } - - describe 'bundletool_generate_universal_signed_apk' do - it 'calls the `bundletool` command with the correct arguments' do - cmd = run_described_fastlane_action( - aab_path: aab_path, - apk_output_path: apk_output_path, - keystore_path: keystore_path, - keystore_password: keystore_password, - keystore_key_alias: keystore_key_alias, - signing_key_password: signing_key_password - ) - expect(cmd).to eq("bundletool build-apks --mode universal --bundle #{aab_path} --output-format DIRECTORY --output #{apk_output_path} --ks #{keystore_path} --ks-pass #{keystore_password} --ks-key-alias #{keystore_key_alias} --key-pass #{signing_key_password}") - end - end -end From 0e801e1be5b514e1b8c8c7717881991e916021a0 Mon Sep 17 00:00:00 2001 From: Olivier Halligon Date: Fri, 5 May 2023 19:57:46 +0200 Subject: [PATCH 08/12] Fix security issues and potential crashes MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This new implementation fixes the following issues that we had in the old implementation: - Issues and security risk with paths containing spaces or special parameters - This would not only have made the command fail - But it also created a security issue for parameter injection (e.g. `apk_output_file_path: 'foo --inject this && echo print the rest'`) - Risk of deleting any arbitrary folder on disk (due to use of unchecked `rm -rf`) - Calling the action with `apk_output_file_path: '.'` for example (or a path to any directory) would have the side effect of… deleting the whole content of the current directory before failing - This was even more problematic that this could be an easy genuine accidental error to make—especially if the caller misread the documentation and thought that the command accepted a folder for the output path instead of the path to the apk file. In addition, this commit adds the following features to the action's behavior: - The action will detect if the parent directory of the `apk_output_file_path` does not exist, and exit early with a nice error if so - We now allow `apk_output_file_path` to be a directory, and if so the output file will be put in that directory and use the same basename as the input aab_file_path (but with `.apk` extension instead of `.aab`) - The action now returns the path to the generated file (which is especially useful if you didn't provide an explicit value for `apk_output_file_path` and let the action infer it, or if you provided a directory for this output path) --- .../android/android_generate_apk_from_aab.rb | 100 +++++++++++------- 1 file changed, 60 insertions(+), 40 deletions(-) diff --git a/lib/fastlane/plugin/wpmreleasetoolkit/actions/android/android_generate_apk_from_aab.rb b/lib/fastlane/plugin/wpmreleasetoolkit/actions/android/android_generate_apk_from_aab.rb index 6e01723c5..853ed00aa 100644 --- a/lib/fastlane/plugin/wpmreleasetoolkit/actions/android/android_generate_apk_from_aab.rb +++ b/lib/fastlane/plugin/wpmreleasetoolkit/actions/android/android_generate_apk_from_aab.rb @@ -1,32 +1,47 @@ module Fastlane module Actions class AndroidGenerateApkFromAabAction < Action - def self.generate_command(aab_file_path, apk_output_file_path, keystore_path, keystore_password, keystore_key_alias, signing_key_password) - command = "bundletool build-apks --mode universal --bundle #{aab_file_path} --output-format DIRECTORY --output #{apk_output_file_path} " - code_sign_arguments = "--ks #{keystore_path} --ks-pass #{keystore_password} --ks-key-alias #{keystore_key_alias} --key-pass #{signing_key_password} " - move_and_cleanup_command = "&& mv #{apk_output_file_path}/universal.apk #{apk_output_file_path}_tmp && rm -rf #{apk_output_file_path} && mv #{apk_output_file_path}_tmp #{apk_output_file_path}" + def self.run(params) + begin + sh('command', '-v', 'bundletool', print_command: false, print_command_output: false) + rescue StandardError + UI.user_error!(MISSING_BUNDLETOOL_ERROR_MESSAGE) + end - # Attempt to code sign the APK if a keystore_path parameter is specified - command += code_sign_arguments unless keystore_path.nil? + # Parse input parameters + aab_file_path = parse_aab_param(params) + apk_output_file_path = params[:apk_output_file_path] || Pathname(aab_file_path).sub_ext('.apk').to_s + code_sign_arguments = { + '--ks': params[:keystore_path], + '--ks-pass': params[:keystore_password], + '--ks-key-alias': params[:keystore_key_alias], + '--key-pass': params[:signing_key_password] + }.compact.flatten.map(&:to_s) - # Move and rename the universal.apk file to the specified output path and cleanup the directory created by bundletool - command += move_and_cleanup_command + if File.directory?(apk_output_file_path) + apk_output_file_path = File.join(apk_output_file_path, "#{File.basename(aab_file_path, '.aab')}.apk") + end - return command + Dir.mktmpdir('a8c-release-toolkit-bundletool-') do |tmpdir| + sh( + 'bundletool', 'build-apks', + '--mode', 'universal', + '--bundle', aab_file_path, + '--output-format', 'DIRECTORY', + '--output', tmpdir, + *code_sign_arguments + ) + FileUtils.mkdir_p(File.dirname(apk_output_file_path)) # Create destination directory if it doesn't exist yet + FileUtils.mv(File.join(tmpdir, 'universal.apk'), apk_output_file_path) + end + + apk_output_file_path end - def self.run(params) - begin - sh('command -v bundletool > /dev/null') - rescue StandardError - UI.user_error!('bundletool is not installed. Please install it using the instructions at https://developer.android.com/studio/command-line/bundletool.') - raise - end + ##################################################### + def self.parse_aab_param(params) # If no AAB param was provided, attempt to get it from the lane context - # First GRADLE_ALL_AAB_OUTPUT_PATHS if only one - # Second GRADLE_AAB_OUTPUT_PATH if it is set - # Else use the specified parameter value if params[:aab_file_path].nil? all_aab_paths = Actions.lane_context[SharedValues::GRADLE_ALL_AAB_OUTPUT_PATHS] || [] aab_file_path = if all_aab_paths.count == 1 @@ -40,19 +55,17 @@ def self.run(params) # If no AAB file path was found, raise an error if aab_file_path.nil? - UI.user_error!('No AAB file path was specified and none was found in the lane context. Please specify the `aab_file_path` parameter or ensure that the relevant build action has been run prior to this action.') - raise + UI.user_error!(NO_AAB_ERROR_MESSAGE) + elsif !File.file?(aab_file_path) + UI.user_error!("The file `#{aab_file_path}` was not found. Please provide a path to an existing file.") end - apk_output_file_path = params[:apk_output_file_path] - keystore_path = params[:keystore_path] - keystore_password = params[:keystore_password] - keystore_key_alias = params[:keystore_key_alias] - signing_key_password = params[:signing_key_password] - - sh(generate_command(aab_file_path, apk_output_file_path, keystore_path, keystore_password, keystore_key_alias, signing_key_password)) + aab_file_path end + MISSING_BUNDLETOOL_ERROR_MESSAGE = 'bundletool is not installed. Please install it using the instructions at https://developer.android.com/studio/command-line/bundletool.'.freeze + NO_AAB_ERROR_MESSAGE = 'No AAB file path was specified and none was found in the lane context. Please specify the `aab_file_path` parameter or ensure that the `gradle` action has been run prior to this action.'.freeze + ##################################################### # @!group Documentation ##################################################### @@ -62,7 +75,7 @@ def self.description end def self.details - 'Generates an APK from the specified AAB' + 'Generates an APK file from the specified AAB file using `bundletool`' end def self.available_options @@ -70,33 +83,32 @@ def self.available_options FastlaneCore::ConfigItem.new( key: :aab_file_path, env_name: 'ANDROID_AAB_FILE_PATH', - description: 'The path to the AAB file. If not speicified, the action will attempt to read from the lane context using the `SharedValues::GRADLE_ALL_AAB_OUTPUT_PATHS` and `SharedValues::GRADLE_AAB_OUTPUT_PATH` keys', + description: 'The path to the AAB file. If not specified, the action will attempt to read from the lane context using the `SharedValues::GRADLE_ALL_AAB_OUTPUT_PATHS` and `SharedValues::GRADLE_AAB_OUTPUT_PATH` keys', type: String, optional: true, - default_value: nil, - verify_block: proc { |p| UI.user_error!("AAB path `#{p}` is not a valid file path.") unless File.file?(p) } + default_value: nil ), FastlaneCore::ConfigItem.new( key: :apk_output_file_path, env_name: 'ANDROID_APK_OUTPUT_PATH', - description: 'The output path where the APK file will be generated. The directory will be created if it does not yet exist', + description: 'The path of the output APK file to generate. If not specified, will use the same path and basename as the `aab_file_path` but with an `.apk` file extension', type: String, - optional: false, + optional: true, default_value: nil ), FastlaneCore::ConfigItem.new( key: :keystore_path, env_name: 'ANDROID_KEYSTORE_PATH', - description: 'The path to the keystore file', + description: 'The path to the keystore file (if you want to codesign the APK)', type: String, optional: true, default_value: nil, - verify_block: proc { |p| UI.user_error!("Keystore file path `#{p}` is not a valid file path.") unless File.file?(p) || p.nil } + verify_block: proc { |p| UI.user_error!("Keystore file path `#{p}` is not a valid file path.") unless p.nil? || File.file?(p) } ), FastlaneCore::ConfigItem.new( key: :keystore_password, env_name: 'ANDROID_KEYSTORE_PASSWORD', - description: 'The password for the keystore', + description: 'The password for the keystore (if you want to codesign the APK)', type: String, optional: true, default_value: nil @@ -104,7 +116,7 @@ def self.available_options FastlaneCore::ConfigItem.new( key: :keystore_key_alias, env_name: 'ANDROID_KEYSTORE_KEY_ALIAS', - description: 'The alias of the key in the keystore', + description: 'The alias of the key in the keystore (if you want to codesign the APK)', type: String, optional: true, default_value: nil @@ -112,7 +124,7 @@ def self.available_options FastlaneCore::ConfigItem.new( key: :signing_key_password, env_name: 'ANDROID_SIGNING_KEY_PASSWORD', - description: 'The password for the signing key', + description: 'The password for the signing key (if you want to codesign the APK)', type: String, optional: true, default_value: nil @@ -120,12 +132,20 @@ def self.available_options ] end + def self.return_type + :string + end + + def self.return_value + 'The path to the APK that has been generated' + end + def self.authors ['Automattic'] end def self.is_supported?(platform) - true + platform == 'android' end end end From b28c1f02ecfa2ec49bc777ee072b52dbd0276219 Mon Sep 17 00:00:00 2001 From: Olivier Halligon Date: Fri, 5 May 2023 19:58:01 +0200 Subject: [PATCH 09/12] Fix Unit Tests (specs) and add some more --- spec/android_generate_apk_from_aab_spec.rb | 176 +++++++++++++++------ 1 file changed, 124 insertions(+), 52 deletions(-) diff --git a/spec/android_generate_apk_from_aab_spec.rb b/spec/android_generate_apk_from_aab_spec.rb index 4dc4ad718..9d3f174bb 100644 --- a/spec/android_generate_apk_from_aab_spec.rb +++ b/spec/android_generate_apk_from_aab_spec.rb @@ -1,35 +1,35 @@ require 'spec_helper' describe Fastlane::Actions::AndroidGenerateApkFromAabAction do - before do - allow(File).to receive(:file?).with(aab_file_path).and_return('mocked file data') - allow(File).to receive(:file?).with(apk_output_file_path).and_return('mocked file data') - allow(File).to receive(:file?).with(keystore_path).and_return('mocked file data') - end - - let(:aab_file_path) { 'path/to/app.aab' } - let(:apk_output_file_path) { 'path/to/app.apk' } - let(:keystore_path) { 'path/to/keystore' } - let(:keystore_password) { 'keystore_password' } - let(:keystore_key_alias) { 'keystore_key_alias' } - let(:signing_key_password) { 'signing_key_password' } + let(:aab_file_path) { 'Dev/My App/my-app.aab' } + let(:apk_output_file_path) { 'Dev/My App/build/artifacts/my-universal-app.apk' } - def generate_command(apk_output_file_path:, aab_file_path: nil, keystore_path: nil, keystore_password: nil, keystore_key_alias: nil, signing_key_password: nil) - command = "bundletool build-apks --mode universal --bundle #{aab_file_path} --output-format DIRECTORY --output #{apk_output_file_path} " - code_sign_arguments = "--ks #{keystore_path} --ks-pass #{keystore_password} --ks-key-alias #{keystore_key_alias} --key-pass #{signing_key_password} " - move_and_cleanup_command = "&& mv #{apk_output_file_path}/universal.apk #{apk_output_file_path}_tmp && rm -rf #{apk_output_file_path} && mv #{apk_output_file_path}_tmp #{apk_output_file_path}" + def expect_bundletool_call(aab, apk, *options) + expect(Fastlane::Action).to receive('sh').with('command', '-v', 'bundletool', { print_command: false, print_command_output: false }) + allow(File).to receive(:file?).with(aab).and_return(true) + expect(Fastlane::Action).to receive('sh').with( + 'bundletool', 'build-apks', '--mode', 'universal', '--bundle', aab, + '--output-format', 'DIRECTORY', '--output', anything, + *options + ) + expect(FileUtils).to receive(:mkdir_p).with(File.dirname(apk)) + expect(FileUtils).to receive(:mv).with(anything, apk) + end - # Append the code signing arguments - command += code_sign_arguments unless keystore_path.nil? + context 'when generating a signed APK' do + let(:keystore_path) { 'Dev/My App/secrets/path/to/keystore' } + let(:keystore_password) { 'keystore_password' } + let(:keystore_key_alias) { 'keystore_key_alias' } + let(:signing_key_password) { 'signing_key_password' } - # Append the move and cleanup command - command += move_and_cleanup_command - return command - end + it 'calls the `bundletool` command with the correct arguments' do + allow(File).to receive(:file?).with(keystore_path).and_return(true) + expect_bundletool_call( + aab_file_path, apk_output_file_path, + '--ks', keystore_path, '--ks-pass', keystore_password, '--ks-key-alias', keystore_key_alias, '--key-pass', signing_key_password + ) - describe 'android_generate_apk_from_aab' do - it 'calls the `bundletool` command with the correct arguments when generating a signed APK' do - cmd = run_described_fastlane_action( + output = run_described_fastlane_action( aab_file_path: aab_file_path, apk_output_file_path: apk_output_file_path, keystore_path: keystore_path, @@ -37,51 +37,123 @@ def generate_command(apk_output_file_path:, aab_file_path: nil, keystore_path: n keystore_key_alias: keystore_key_alias, signing_key_password: signing_key_password ) - expected_command = generate_command(aab_file_path: aab_file_path, - apk_output_file_path: apk_output_file_path, - keystore_path: keystore_path, - keystore_password: keystore_password, - keystore_key_alias: keystore_key_alias, - signing_key_password: signing_key_password) - expect(cmd).to eq(expected_command) + + expect(output).to eq(apk_output_file_path) end + end - it 'calls the `bundletool` command with the correct arguments when generating an unsigned APK' do - cmd = run_described_fastlane_action( + context 'when generating an unsigned APK' do + it 'calls the `bundletool` command with the correct arguments' do + expect_bundletool_call(aab_file_path, apk_output_file_path) + + output = run_described_fastlane_action( aab_file_path: aab_file_path, apk_output_file_path: apk_output_file_path ) - expected_command = generate_command(aab_file_path: aab_file_path, - apk_output_file_path: apk_output_file_path) - expect(cmd).to eq(expected_command) - end - it 'calls the `bundletool` command with the correct arguments and use the path to the AAB from the lane context if the SharedValues::GRADLE_AAB_OUTPUT_PATH key is set' do - aab_path_from_context = 'path/from/context/app.aab' + expect(output).to eq(apk_output_file_path) + end + end + describe 'parameter inference' do + it 'infers the AAB path from lane context if `SharedValues::GRADLE_AAB_OUTPUT_PATH` is set' do + aab_path_from_context = 'path/from/context/my-app.aab' Fastlane::Actions.lane_context[Fastlane::Actions::SharedValues::GRADLE_AAB_OUTPUT_PATH] = aab_path_from_context - cmd = run_described_fastlane_action( + expect_bundletool_call(aab_path_from_context, apk_output_file_path) + run_described_fastlane_action( apk_output_file_path: apk_output_file_path ) - expected_command = generate_command(aab_file_path: aab_path_from_context, - apk_output_file_path: apk_output_file_path) - expect(cmd).to eq(expected_command) + Fastlane::Actions.lane_context[Fastlane::Actions::SharedValues::GRADLE_AAB_OUTPUT_PATH] = nil end - it 'calls the `bundletool` command with the correct arguments and use the path to the AAB from the lane context if the SharedValues::GRADLE_ALL_AAB_OUTPUT_PATHS key is set' do - all_aab_paths_from_context = ['first/path/from/context/app.aab'] - - Fastlane::Actions.lane_context[Fastlane::Actions::SharedValues::GRADLE_ALL_AAB_OUTPUT_PATHS] = all_aab_paths_from_context + it 'infers the AAB path from lane context if `SharedValues::GRADLE_ALL_AAB_OUTPUT_PATHS` is set with only one value' do + aab_paths_from_context = ['first/path/from/context/app.aab'] + Fastlane::Actions.lane_context[Fastlane::Actions::SharedValues::GRADLE_ALL_AAB_OUTPUT_PATHS] = aab_paths_from_context - cmd = run_described_fastlane_action( + expect_bundletool_call(aab_paths_from_context.first, apk_output_file_path) + run_described_fastlane_action( apk_output_file_path: apk_output_file_path ) - expected_command = generate_command(aab_file_path: all_aab_paths_from_context.first, - apk_output_file_path: apk_output_file_path) - expect(cmd).to eq(expected_command) + Fastlane::Actions.lane_context[Fastlane::Actions::SharedValues::GRADLE_ALL_AAB_OUTPUT_PATHS] = nil + end + + it 'does not infer the AAB path from lane context if `SharedValues::GRADLE_AAB_OUTPUT_PATHS` has more than one value' do + aab_paths_from_context = ['first/path/from/context/app.aab', 'second/path/from/context/app.aab'] + Fastlane::Actions.lane_context[Fastlane::Actions::SharedValues::GRADLE_ALL_AAB_OUTPUT_PATHS] = aab_paths_from_context + + expect do + run_described_fastlane_action( + apk_output_file_path: apk_output_file_path + ) + end.to raise_error(described_class::NO_AAB_ERROR_MESSAGE) + + Fastlane::Actions.lane_context[Fastlane::Actions::SharedValues::GRADLE_ALL_AAB_OUTPUT_PATHS] = nil + end + + it 'infers the output path if none is provided' do + inferred_apk_path = File.join(File.dirname(aab_file_path), "#{File.basename(aab_file_path, '.aab')}.apk") + expect_bundletool_call(aab_file_path, inferred_apk_path) + + output = run_described_fastlane_action( + aab_file_path: aab_file_path + ) + + expect(output).to eq(inferred_apk_path) + end + + it 'infers the output file name if output path is a directory' do + in_tmp_dir do |output_dir| + inferred_apk_path = File.join(output_dir, "#{File.basename(aab_file_path, '.aab')}.apk") + expect_bundletool_call(aab_file_path, inferred_apk_path) + + output = run_described_fastlane_action( + aab_file_path: aab_file_path, + apk_output_file_path: output_dir + ) + + expect(output).to eq(inferred_apk_path) + end + end + end + + describe 'error handling' do + it 'errors if bundletool is not installed' do + allow(Fastlane::Action).to receive('sh').with('command', '-v', 'bundletool', any_args).and_raise + expect(Fastlane::UI).to receive(:user_error!).with(described_class::MISSING_BUNDLETOOL_ERROR_MESSAGE).and_raise + + expect do + run_described_fastlane_action( + aab_file_path: aab_file_path, + apk_output_file_path: apk_output_file_path + ) + end.to raise_error(RuntimeError) + end + + it 'errors if no input AAB file was provided nor can be inferred' do + expect(Fastlane::Action).to receive('sh').with('command', '-v', 'bundletool', any_args) + expect(Fastlane::UI).to receive(:user_error!).with(described_class::NO_AAB_ERROR_MESSAGE).and_raise + + expect do + run_described_fastlane_action( + apk_output_file_path: apk_output_file_path + ) + end.to raise_error(RuntimeError) + end + + it 'errors if the provided input AAB file does not exist' do + expect(Fastlane::Action).to receive('sh').with('command', '-v', 'bundletool', any_args) + allow(File).to receive(:file?).with(aab_file_path).and_return(false) + expect(Fastlane::UI).to receive(:user_error!).with("The file `#{aab_file_path}` was not found. Please provide a path to an existing file.").and_raise + + expect do + run_described_fastlane_action( + aab_file_path: aab_file_path, + apk_output_file_path: apk_output_file_path + ) + end.to raise_error(RuntimeError) end end end From 1d566d1e235dad84f79783a82e70378e6cbb865a Mon Sep 17 00:00:00 2001 From: Olivier Halligon Date: Fri, 5 May 2023 20:09:49 +0200 Subject: [PATCH 10/12] code cleanup Group the code related to parsing the `apk_output_file_path` parameter together --- .../actions/android/android_generate_apk_from_aab.rb | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/lib/fastlane/plugin/wpmreleasetoolkit/actions/android/android_generate_apk_from_aab.rb b/lib/fastlane/plugin/wpmreleasetoolkit/actions/android/android_generate_apk_from_aab.rb index 853ed00aa..2f54a71ee 100644 --- a/lib/fastlane/plugin/wpmreleasetoolkit/actions/android/android_generate_apk_from_aab.rb +++ b/lib/fastlane/plugin/wpmreleasetoolkit/actions/android/android_generate_apk_from_aab.rb @@ -11,6 +11,7 @@ def self.run(params) # Parse input parameters aab_file_path = parse_aab_param(params) apk_output_file_path = params[:apk_output_file_path] || Pathname(aab_file_path).sub_ext('.apk').to_s + apk_output_file_path = File.join(apk_output_file_path, "#{File.basename(aab_file_path, '.aab')}.apk") if File.directory?(apk_output_file_path) code_sign_arguments = { '--ks': params[:keystore_path], '--ks-pass': params[:keystore_password], @@ -18,10 +19,6 @@ def self.run(params) '--key-pass': params[:signing_key_password] }.compact.flatten.map(&:to_s) - if File.directory?(apk_output_file_path) - apk_output_file_path = File.join(apk_output_file_path, "#{File.basename(aab_file_path, '.aab')}.apk") - end - Dir.mktmpdir('a8c-release-toolkit-bundletool-') do |tmpdir| sh( 'bundletool', 'build-apks', From 2d499f9de54003407a27fedb14df5c1636489079 Mon Sep 17 00:00:00 2001 From: Olivier Halligon Date: Fri, 5 May 2023 20:29:26 +0200 Subject: [PATCH 11/12] Simplify error handling tests a bit --- spec/android_generate_apk_from_aab_spec.rb | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/spec/android_generate_apk_from_aab_spec.rb b/spec/android_generate_apk_from_aab_spec.rb index 9d3f174bb..2816b9883 100644 --- a/spec/android_generate_apk_from_aab_spec.rb +++ b/spec/android_generate_apk_from_aab_spec.rb @@ -88,7 +88,7 @@ def expect_bundletool_call(aab, apk, *options) run_described_fastlane_action( apk_output_file_path: apk_output_file_path ) - end.to raise_error(described_class::NO_AAB_ERROR_MESSAGE) + end.to raise_error(FastlaneCore::Interface::FastlaneError, described_class::NO_AAB_ERROR_MESSAGE) Fastlane::Actions.lane_context[Fastlane::Actions::SharedValues::GRADLE_ALL_AAB_OUTPUT_PATHS] = nil end @@ -122,38 +122,35 @@ def expect_bundletool_call(aab, apk, *options) describe 'error handling' do it 'errors if bundletool is not installed' do allow(Fastlane::Action).to receive('sh').with('command', '-v', 'bundletool', any_args).and_raise - expect(Fastlane::UI).to receive(:user_error!).with(described_class::MISSING_BUNDLETOOL_ERROR_MESSAGE).and_raise expect do run_described_fastlane_action( aab_file_path: aab_file_path, apk_output_file_path: apk_output_file_path ) - end.to raise_error(RuntimeError) + end.to raise_error(FastlaneCore::Interface::FastlaneError, described_class::MISSING_BUNDLETOOL_ERROR_MESSAGE) end it 'errors if no input AAB file was provided nor can be inferred' do expect(Fastlane::Action).to receive('sh').with('command', '-v', 'bundletool', any_args) - expect(Fastlane::UI).to receive(:user_error!).with(described_class::NO_AAB_ERROR_MESSAGE).and_raise expect do run_described_fastlane_action( apk_output_file_path: apk_output_file_path ) - end.to raise_error(RuntimeError) + end.to raise_error(FastlaneCore::Interface::FastlaneError, described_class::NO_AAB_ERROR_MESSAGE) end it 'errors if the provided input AAB file does not exist' do expect(Fastlane::Action).to receive('sh').with('command', '-v', 'bundletool', any_args) allow(File).to receive(:file?).with(aab_file_path).and_return(false) - expect(Fastlane::UI).to receive(:user_error!).with("The file `#{aab_file_path}` was not found. Please provide a path to an existing file.").and_raise expect do run_described_fastlane_action( aab_file_path: aab_file_path, apk_output_file_path: apk_output_file_path ) - end.to raise_error(RuntimeError) + end.to raise_error(FastlaneCore::Interface::FastlaneError, "The file `#{aab_file_path}` was not found. Please provide a path to an existing file.") end end end From 658e8f61f24905148867db24b06e49920de60a22 Mon Sep 17 00:00:00 2001 From: Olivier Halligon Date: Tue, 9 May 2023 08:12:09 +0200 Subject: [PATCH 12/12] Update PR link in CHANGELOG.md --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index dc26bf93e..2c4307393 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,7 +11,7 @@ _None_ ### New Features - Add `ios_get_build_number` action to get the current build number from an `xcconfig` file. [#458] -- Add new `android_generate_apk_from_aab` action to generate an APK from a given AAB. +- Add new `android_generate_apk_from_aab` action to generate an APK from a given AAB. [#467] ### Bug Fixes