New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[deliver][pilot] use altool instead of using iTMSTransporter for Xcode 14 #20631
Changes from 17 commits
83cdc08
ec4cfee
f1e0575
c3a66db
39c919e
74f681d
6d10343
ce9b12d
40337e3
0f2a3d4
d675551
fa388c7
dcb97a8
5bd7d3f
2023aa8
7a13c48
8df9559
ed28c90
375f951
711eb3c
747178f
13afc8c
d36bf23
e4e44ac
4f677a6
9988e33
4ebf009
ddb437a
b4f5324
44bf7aa
01a5873
7490c5c
388726b
b070b58
1cdc817
c82e99a
bdbacb5
00c974c
1994860
16249e7
8ab40fa
22cfcec
3fc11fa
980f189
669bebc
98e1a88
355cc9d
0f6bfcb
f4cd020
95e7cb8
38f23ee
30839b9
a2ea2fb
874320c
ab02d6d
71e57d6
b518207
8d5bb62
5beca1e
4f4d3ee
cd2c504
4b93a81
18f460f
ac7358d
07181a7
599ab2b
8be9c7e
8e7d6f3
c005aae
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
First,Last,Email,Groups,Installed Version,Install Date | ||
Original file line number | Diff line number | Diff line change | ||||
---|---|---|---|---|---|---|
|
@@ -180,9 +180,123 @@ def additional_upload_parameters | |||||
end | ||||||
end | ||||||
|
||||||
# Generates commands and executes the altool. | ||||||
class AltoolTransporterExecutor < TransporterExecutor | ||||||
ERROR_REGEX = /\sNSLocalizedFailureReason\s=\s"+(.+)"/ | ||||||
|
||||||
private_constant :ERROR_REGEX | ||||||
|
||||||
def execute(command, hide_output) | ||||||
if Helper.test? | ||||||
yield(nil) if block_given? | ||||||
command | ||||||
end | ||||||
|
||||||
@errors = [] | ||||||
@warnings = [] | ||||||
@all_lines = [] | ||||||
|
||||||
if hide_output | ||||||
giginet marked this conversation as resolved.
Show resolved
Hide resolved
|
||||||
# Show a one time message instead | ||||||
UI.success("Waiting for App Store Connect transporter to be finished.") | ||||||
UI.success("Application Loader progress... this might take a few minutes...") | ||||||
end | ||||||
|
||||||
begin | ||||||
exit_status = FastlaneCore::FastlanePty.spawn(command) do |command_stdout, command_stdin, pid| | ||||||
begin | ||||||
giginet marked this conversation as resolved.
Show resolved
Hide resolved
|
||||||
command_stdout.each do |line| | ||||||
@all_lines << line | ||||||
parse_line(line, hide_output) # this is where the parsing happens | ||||||
end | ||||||
end | ||||||
end | ||||||
freddi-kit marked this conversation as resolved.
Show resolved
Hide resolved
|
||||||
rescue => ex | ||||||
# FastlanePty adds exit_status on to StandardError so every error will have a status code | ||||||
exit_status = ex.exit_status | ||||||
@errors << ex.to_s | ||||||
end | ||||||
|
||||||
@errors << "The call to the altool completed with a non-zero exit status: #{exit_status}. This indicates a failure." unless exit_status.zero? | ||||||
|
||||||
UI.important(@warnings.join("\n")) unless @warnings.empty? | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Nit: Given
Suggested change
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Ah, yes, currently there is no change to parse warning here, so maybe it would be better to remove this line There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 44bf7aa fixed! |
||||||
|
||||||
if @errors.count > 0 && @all_lines.count > 0 | ||||||
freddi-kit marked this conversation as resolved.
Show resolved
Hide resolved
|
||||||
# Print out the last 18 lines, this is key for non-verbose mode | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Do you have sample output or test cases? We may prefer to use regex instead. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I found good way so please wait a moment There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Ah, sorry I think this line should be leave it as is, because the main purpose is providing altool's command output itself and regex is already done at here fastlane/fastlane_core/lib/fastlane_core/itunes_transporter.rb Lines 282 to 283 in 0f2a3d4
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
This is a sample and I found I may can improve around error, let me fix it |
||||||
@all_lines.last(18).each do |line| | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Are we sure this will always be the last 18 lines? What if there are other metadata in the JSON printed in There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Honestly, I'm not sure 😅 . I only encountered and know There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. What about detecting the first line with There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Or alternatively, detecting each line starting with There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 01a5873 There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Another tip: you can index arrays using ranges and negative indices like I think using that instead of So this whole There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. ohhh.. cool! There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Also you should add those example messages you commented above … as unit tests 🙂 There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. good. let me add unit test later 🙇 (Sorry I'm busy from now so it will be later) There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. No worries, and thanks already for handling all this 👍 |
||||||
UI.important("[altool] #{line}") | ||||||
end | ||||||
UI.message("Application Loader output above ^") | ||||||
UI.error(@errors.join("\n")) | ||||||
freddi-kit marked this conversation as resolved.
Show resolved
Hide resolved
|
||||||
end | ||||||
|
||||||
yield(@all_lines) if block_given? | ||||||
exit_status.zero? | ||||||
end | ||||||
|
||||||
def build_upload_command(username, password, source = "/tmp", provider_short_name = "", jwt = nil, platform = nil, api_key = nil) | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 💄 I'd love for this method to use named parameters at some point ( There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I see, since There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Totally 👍 no need to fix this in this PR given the refactoring it will involve indeed. And not that critical if we don't do it anyway to be honest 🙃
giginet marked this conversation as resolved.
Show resolved
Hide resolved
|
||||||
[ | ||||||
("API_PRIVATE_KEYS_DIR=#{api_key[:key_filepath]}" if api_key), | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
default_platform(:ios)
platform :ios do
desc "Description of what the lane does"
lane :custom_lane do
xcode_select("/Applications/Xcode_14 beta 6.app")
api_key = app_store_connect_api_key(
key_id: "XXXXXX",
issuer_id: "XXX-XXX-XXXX",
key_content: "-----BEGIN PRIVATE KEY-----\nXXXXXXX\nXXXXXX\nXXXXXXXX\nXXX\n-----END PRIVATE KEY-----",
duration: 1200,
in_house: false
)
pilot(api_key: api_key)
end
end There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Ah... I missed this case, thank you! There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
yes! it is solution for this case, let me do so There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I noticed key_filepath is not existed and i need to do other way, I'll push commit soon There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 747178f fixed |
||||||
"xcrun altool", | ||||||
"--upload-app", | ||||||
("-u #{username.shellescape}" unless api_key), | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Hi @freddi-kit Nice work! i am just testing this PR and it got crashed in this line 3: from /Users/distiller/project/vendor/bundle/ruby/2.7.0/bundler/gems/fastlane-ed28c906445c/fastlane/lib/fastlane/actions/upload_to_testflight.rb:34:in `run'
2: from /Users/distiller/project/vendor/bundle/ruby/2.7.0/bundler/gems/fastlane-ed28c906445c/pilot/lib/pilot/build_manager.rb:50:in `upload'
1: from /Users/distiller/project/vendor/bundle/ruby/2.7.0/bundler/gems/fastlane-ed28c906445c/fastlane_core/lib/fastlane_core/itunes_transporter.rb:697:in `upload'
/Users/distiller/project/vendor/bundle/ruby/2.7.0/bundler/gems/fastlane-ed28c906445c/fastlane_core/lib/fastlane_core/itunes_transporter.rb:242:in `build_upload_command': undefined method `shellescape' for nil:NilClass (NoMethodError) There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. btw, There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Oh Thank you for reviewing! in my environment, pilot+api_key works. and here is my example Fastfile. default_platform(:ios)
platform :ios do
desc "Description of what the lane does"
lane :custom_lane do
xcode_select("/Applications/Xcode_14 beta 6.app")
api_key = app_store_connect_api_key(
key_id: "XXXXXX",
issuer_id: "XXX-XXX-XXXX",
key_filepath: "./AuthKey_XXXX.p8",
duration: 1200,
in_house: false
)
pilot(api_key: api_key)
end
end There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Ah... sorry I may found there is a issue to check password is available or not, please wait a moment There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I noticed this is simple issue comes from my less experience Ruby 🙇 I can fix it soon There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Hi @freddi-kit! Thank you so much for working on that. I second @raid5 comment. I believe there's an unhandled case when using
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I just investigated and it is bug, and it requires a little more changes to support it api_key_path. I'm happy if you wait for it 🙇 There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Fixed! 22cfcec There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Awesome this works great! I was able to successfully upload to TestFlight now. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Same! Thank you so much @freddi-kit. As others said:
|
||||||
("-p #{password.shellescape}" unless api_key), | ||||||
("--apiKey #{api_key[:key_id]}" if api_key), | ||||||
("--apiIssuer #{api_key[:issuer_id]}" if api_key), | ||||||
platform_option(platform), | ||||||
file_upload_option(source), | ||||||
additional_upload_parameters, | ||||||
"-k 100000" | ||||||
].compact.join(' ') | ||||||
end | ||||||
|
||||||
def additional_upload_parameters | ||||||
env_deliver_additional_params = ENV["DELIVER_ALTOOL_ADDITIONAL_UPLOAD_PARAMETERS"] | ||||||
if env_deliver_additional_params.to_s.strip.empty? | ||||||
return nil | ||||||
end | ||||||
giginet marked this conversation as resolved.
Show resolved
Hide resolved
freddi-kit marked this conversation as resolved.
Show resolved
Hide resolved
|
||||||
|
||||||
return env_deliver_additional_params.to_s.strip | ||||||
freddi-kit marked this conversation as resolved.
Show resolved
Hide resolved
|
||||||
end | ||||||
|
||||||
def handle_error(password) | ||||||
UI.error("Could not download/upload from App Store Connect!") | ||||||
giginet marked this conversation as resolved.
Show resolved
Hide resolved
|
||||||
end | ||||||
|
||||||
def displayable_errors | ||||||
@errors.map { |error| "[Application Loader Error Output]: #{error}" }.join("\n") | ||||||
end | ||||||
|
||||||
private | ||||||
|
||||||
def file_upload_option(source) | ||||||
"-f #{source.shellescape}" | ||||||
end | ||||||
|
||||||
def platform_option(platform) | ||||||
"-t #{platform == 'osx' ? 'macox' : platform}" | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Typo?
Suggested change
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Btw, since that typo didn't make the test suite fail… maybe that's a sign that we need to add some test case (aka spec example) to our test suite for it? 🤔 There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. also, this brings to point that are we really sure we need to make it Apple's official document is saying
i agree! There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This should be macos and it is my mistake 🙇 let me fix it There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
I just tested with new empty mac os app and it success to upload. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 9988e33 fixed |
||||||
end | ||||||
|
||||||
def parse_line(line, hide_output) | ||||||
output_done = false | ||||||
|
||||||
if line =~ ERROR_REGEX | ||||||
@errors << $1 | ||||||
output_done = true | ||||||
end | ||||||
|
||||||
unless hide_output | ||||||
# General logging for debug purposes | ||||||
unless output_done | ||||||
UI.verbose("[altool]: #{$1}") | ||||||
end | ||||||
end | ||||||
end | ||||||
end | ||||||
|
||||||
# Generates commands and executes the iTMSTransporter through the shell script it provides by the same name | ||||||
class ShellScriptTransporterExecutor < TransporterExecutor | ||||||
def build_upload_command(username, password, source = "/tmp", provider_short_name = "", jwt = nil) | ||||||
def build_upload_command(username, password, source = "/tmp", provider_short_name = "", jwt = nil, platform = nil, api_key = nil) | ||||||
use_jwt = !jwt.to_s.empty? | ||||||
[ | ||||||
'"' + Helper.transporter_path + '"', | ||||||
|
@@ -278,7 +392,7 @@ def shell_escaped_password(password) | |||||
# Generates commands and executes the iTMSTransporter by invoking its Java app directly, to avoid the crazy parameter | ||||||
# escaping problems in its accompanying shell script. | ||||||
class JavaTransporterExecutor < TransporterExecutor | ||||||
def build_upload_command(username, password, source = "/tmp", provider_short_name = "", jwt = nil) | ||||||
def build_upload_command(username, password, source = "/tmp", provider_short_name = "", jwt = nil, platform = nil, api_key = nil) | ||||||
use_jwt = !jwt.to_s.empty? | ||||||
if !Helper.user_defined_itms_path? && Helper.mac? && Helper.xcode_at_least?(11) | ||||||
[ | ||||||
|
@@ -476,7 +590,7 @@ def self.hide_transporter_output? | |||||
# see: https://github.com/fastlane/fastlane/issues/1524#issuecomment-196370628 | ||||||
# for more information about how to use the iTMSTransporter to list your provider | ||||||
# short names | ||||||
def initialize(user = nil, password = nil, use_shell_script = false, provider_short_name = nil, jwt = nil) | ||||||
def initialize(user = nil, password = nil, use_shell_script = false, provider_short_name = nil, jwt = nil, upload: false, api_key: nil) | ||||||
# Xcode 6.x doesn't have the same iTMSTransporter Java setup as later Xcode versions, so | ||||||
# we can't default to using the newer direct Java invocation strategy for those versions. | ||||||
use_shell_script ||= Helper.is_mac? && Helper.xcode_version.start_with?('6.') | ||||||
|
@@ -489,8 +603,16 @@ def initialize(user = nil, password = nil, use_shell_script = false, provider_sh | |||||
end | ||||||
|
||||||
@jwt = jwt | ||||||
@api_key = api_key | ||||||
|
||||||
if should_use_altool?(upload, use_shell_script) | ||||||
UI.verbose("Use altool as transporter.") | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Maybe
Suggested change
|
||||||
@transporter_executor = AltoolTransporterExecutor.new | ||||||
else | ||||||
UI.verbose("Use iTMSTransporter as transporter") | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||
@transporter_executor = use_shell_script ? ShellScriptTransporterExecutor.new : JavaTransporterExecutor.new | ||||||
end | ||||||
|
||||||
@transporter_executor = use_shell_script ? ShellScriptTransporterExecutor.new : JavaTransporterExecutor.new | ||||||
@provider_short_name = provider_short_name | ||||||
end | ||||||
|
||||||
|
@@ -539,7 +661,7 @@ def download(app_id, dir = nil) | |||||
# @return (Bool) True if everything worked fine | ||||||
# @raise [Deliver::TransporterTransferError] when something went wrong | ||||||
# when transferring | ||||||
def upload(app_id = nil, dir = nil, package_path: nil, asset_path: nil) | ||||||
def upload(app_id = nil, dir = nil, package_path: nil, asset_path: nil, platform: nil) | ||||||
raise "app_id and dir are required or package_path or asset_path is required" if (app_id.nil? || dir.nil?) && package_path.nil? && asset_path.nil? | ||||||
|
||||||
# Transport can upload .ipa, .dmg, and .pkg files directly with -assetFile | ||||||
|
@@ -568,9 +690,11 @@ def upload(app_id = nil, dir = nil, package_path: nil, asset_path: nil) | |||||
|
||||||
password_placeholder = @jwt.nil? ? 'YourPassword' : nil | ||||||
jwt_placeholder = @jwt.nil? ? nil : 'YourJWT' | ||||||
api_key_plaseholder = nil unless @api_key.nil? | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
please fix this spell in other places too... |
||||||
api_key_plaseholder = { key_id: "YourKeyID", issuer_id: "YourIssuerID", key_filepath: "YourKeyFilepath" } if @api_key.nil? | ||||||
|
||||||
command = @transporter_executor.build_upload_command(@user, @password, actual_dir, @provider_short_name, @jwt) | ||||||
UI.verbose(@transporter_executor.build_upload_command(@user, password_placeholder, actual_dir, @provider_short_name, jwt_placeholder)) | ||||||
command = @transporter_executor.build_upload_command(@user, @password, actual_dir, @provider_short_name, @jwt, platform, @api_key) | ||||||
UI.verbose(@transporter_executor.build_upload_command(@user, password_placeholder, actual_dir, @provider_short_name, jwt_placeholder, platform, api_key_plaseholder)) | ||||||
|
||||||
begin | ||||||
result = @transporter_executor.execute(command, ItunesTransporter.hide_transporter_output?) | ||||||
|
@@ -657,6 +781,12 @@ def provider_ids | |||||
|
||||||
TWO_FACTOR_ENV_VARIABLE = "FASTLANE_APPLE_APPLICATION_SPECIFIC_PASSWORD" | ||||||
|
||||||
# Returns whether altool should be used or ItunesTransporter should be used | ||||||
def should_use_altool?(upload, use_shell_script) | ||||||
# Xcode 14 no longer supports iTMSTransporter. Use altool instead | ||||||
!use_shell_script && upload && !Helper.user_defined_itms_path? && Helper.mac? && Helper.xcode_at_least?(14) | ||||||
end | ||||||
|
||||||
# Returns the password to be used with the transporter | ||||||
def load_password_for_transporter | ||||||
# 3 different sources for the password | ||||||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Was this file uploaded by mistake? Do we need it?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Removed! it is autogenerated file by unit tests. i will fix it properly in separate PR 13afc8c
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Shouldn't the unit tests generate such files in a temporary dir instead on generating them at the repo root? 🤔
Though I think that file is not generated by any test that you added in this PR, but has instead always been generated there, by another test that was already there even before your PR, right? In that case, fixing that is outside the scope of this PR; but maybe something to keep in mind for a later fix? 🤷