Automating 2FA using Fastlane/CircleCI - ios

What is the correct way to setup 2FA for beta uploads to iTunesConnect/TestFlight?
There are so many links and forums answers but none solve the issue.
Currently I have added the environment variables into CircleCI, including the Application Specific Password generated on the AppleID.
I have a lane in Fastlane that looks like this
desc "Alpha build"
lane :alpha do
match(type: "adhoc")
gym(export_method: "ad-hoc")
upload_to_testflight(skip_submission: true)
end
I run the preauth command below before the alpha lane
- run:
name: Spaceship pre-auth for 2FA
command: bundle exec fastlane spaceauth -u [redacted].com
Fastlane seems to be failing on auth even if the password is correct
Please check your credentials and try again.
This could be an issue with App Store Connect,
Please try unsetting the FASTLANE_SESSION environment variable
If I remove the spaceauth command circleci is failing by timing out waiting for 2FA.
The Fastlane site says that there is no need to use spaceauth unless additional app store connect APIs are being used such as uploading metadata etc. It states that for uploads to testflight only the Application Specific Password should be enough, although this doesnt work either.
Has anyone solved this issue that can advise please?

You can authenticate with Apple through API key. You can generate the key here: https://appstoreconnect.apple.com/access/api.
Once you have the key.p8 file, you can used to auth with the fastlane command: app_store_connect_api_key as follow:
app_store_connect_api_key(
key_id: "ABCDEFG",
issuer_id: "Your_issuer_id",
key_content: File.read("./key.p8").chomp,
duration: 1200,
in_house: false
)
After the execution of the command, the key session is store in the following env var: APP_STORE_CONNECT_API_KEY so your upload to testflight should look like this:
upload_to_testflight(
groups: ["Friends & Family","Mytest-Group"],
ipa: "./build/myapp.ipa",
api_key: Actions.lane_context[SharedValues::APP_STORE_CONNECT_API_KEY]
)

Use CI server's REST API to update the value of FASTLANE_SESSION configuration parameter. In our case we have it defined in one place and it gets reused by all jobs that need to auth to Dev Center / App Store Connect. I haven't looked into details, but I'm sure there's a way to update the job/project parameter via REST call. Then again, have a scheduled job that runs spaceauth and uses REST API to set the new value.

The docs are wrong there - and it's my fault. I updated the docs article before the actual code enabling this is merged.
Your best best currently is to create a second account that doesn't have 2FA enabled, or using spaceauth locally and then copying the returned value into a ENV variable on your CI provider, although it probably will only work 24 hours. We are currently investigating how to improve this.

Related

Fastlane failing with error "Cannot obtain the content provider public id. Please specify a provider short name using the -asc_provider option."

I created an iOS test flight build using Fastlane, and I got this strange error, not sure why because it was working fine yesterday and now without any change in Fastlane configuration it gives me an error while uploading the build to the Apple App store.
errors wordings are as below
[21:50:01]: Transporter transfer failed.
[21:50:01]:
[21:50:01]: Cannot obtain the content provider public id. Please specify a provider short name using the -asc_provider option.
[21:50:02]: Cannot obtain the content provider public id. Please specify a provider short name using the -asc_provider option.
Return status of iTunes Transporter was 1: Cannot obtain the content provider public id. Please specify a provider short name using the -asc_provider option.
The call to the iTMSTransporter completed with a non-zero exit status: 1. This indicates a failure.
[21:50:02]: Error uploading ipa file:
[21:50:02]: fastlane finished with errors
[!] Error uploading ipa file:
Refer below logs
For those who are suffering with this on Azure Devops's AppStoreRelease task. Using #user20291554 solution it can be fixed as follows
- job: ios
pool:
vmImage: macOS-latest
variables:
DELIVER_ITMSTRANSPORTER_ADDITIONAL_UPLOAD_PARAMETERS: "-asc_provider <your team ID or short name if different>"
steps:
...
- task: AppStoreRelease#1
inputs:
...
Please add the itc_provider along with the apple_id on the below line of code.
upload_to_testflight(
skip_waiting_for_build_processing: true,
apple_id: "APPLE_ID",
itc_provider:"ID" #example: W4A0P2BYMN
)
If you are on multiple App Store Connect teams, deliver needs a provider short name to know where to upload your binary. deliver will try to use the long name of the selected team to detect the provider short name. To override the detected value with an explicit one, use the itc_provider option.
Fastlane reference doc for itc_provider
Stackoverflow: To get provider short name(itc_provider)
Fastlane Github: related ticket
I had the same.
This comment from github helped me.
Add ENV variable to your deployment (or local machine 🥇, or Fastfile
directly) With DELIVER_ITMSTRANSPORTER_ADDITIONAL_UPLOAD_PARAMETERS we
can add the "missing" -asc_provider variable.
ENV["DELIVER_ITMSTRANSPORTER_ADDITIONAL_UPLOAD_PARAMETERS"] =
"-asc_provider YourShortName" Just deployed and it works for those who
can't wait.
For me adding the environment variable works perfectly:
ITMSTRANSPORTER_FORCE_ITMS_PACKAGE_UPLOAD: true
For my case, here is an example of Azure DevOps pipelines:
- task: AppStoreRelease#1
env:
ITMSTRANSPORTER_FORCE_ITMS_PACKAGE_UPLOAD: true
...
Source Fastlane GitHub issue
This is how I solved it!
deliver(
app_identifier: '{{YOUR_APP_ID}}',
submit_for_review: false,
skip_screenshots: true,
force: true,
itc_provider: "{{YOUR_TEAM_ID}}" // <- added!
)
To get itc_provider run command
/Applications/Xcode.app/Contents/SharedFrameworks/ContentDeliveryServices.framework/Versions/A/itms/bin/iTMSTransporter -m provider -u 'appleid#xxx.xx' -p 'xxxx-xxxx-xxxx-xxxx' -account_type itunes_connect -v off
where
appleid#xxx.xx your appleid
xxxx-xxxx-xxxx-xxxx - password for your app
How to generate an app-specific password
Sign in to appleid.apple.com.
In the Sign-In and Security section, select App-Specific Passwords.
Select Generate an app-specific password or select the Add button Blue plus sign icon., then follow the steps on your screen.
Enter or paste the app-specific password into the password field of the app.
Im using the fastlane deliver to upload my apps
The solution for me was:
Add new tag/flag for command fastlane deliver
Example: fastlane deliver --username xxx#xxx.com....
And new tag added was --itc-provider my_team_id
You can found your team_id here: page
So, the command at the end was:
fastlane deliver --verbose --ipa xxx --username xxx --app_identifier xxx --itc_provider team_id
xxx => corresponds about your project
team_id => corresponds about Team ID, that you can get on page above

Connecting to different ARN/Role/Amazon Account when trying to deploy

I have previously had Serverless installed on a server, and then when I tried to edit the function and package it back up to edit the zip file I broke it, so I have to start all over. So to begin this issue: I had Serverless running and was using it with this package - https://github.com/adieuadieu/serverless-chrome/tree/master/examples/serverless-framework/aws
When I sudo npm run deploy, I get the ServerlessError:
ServerlessError: User: arn:aws:sts::XXX:assumed-role/EC2CodeDeploy/i-268b1acf is not authorized to perform: cloudformation:DescribeStackResources on resource: arn:aws:cloudformation:us-east-1:YYY:stack/aws-dev/*
I'm not sure why it is trying to connect to a Role and not an IAM. So I check the Role, and it is in an entirely different AWS account than the account I've configured. Let's call this Account B.
When it comes to configuration, I've installed AWS CLI and entered in the key, id, and region in my Account A in AWS. Not touching Account B whatsoever. When I run aws s3 ls I see the correct s3 buckets of the account with the key/id/regioin, so I know CLI is working with the correct account. Sounds good. I check the ~/.aws/creditionals file and just has one profile [default] which seems normal. No other profiles are in here. I copied this over to the ~/.aws/config file so now both files are same. Works great.
I then go into my SSH where I've installed serverless, and run npm run deploy and it gives me the same message above. I think maybe somehow it is not using the correct account for whatever reason. So I manually set the access key and secret with the following commands:
serverless config credentials --provider aws --key XXX --secret YYY
It tells me there already is a profile in the aws creds file, so I then add --o to the end to overwrite. I run sudo npm run deploy and still same error.
I then run this command to manually set a profile in the creds for serverless, with the profile name matching the IAM user name:
serverless config credentials --provider aws --key XXX --secret YYY --profile serverless-agent
Where "serverless-agent" is the name of my IAM user I've been trying to use to deploy. I run this, it tells me there already is an existing profile in the aws creds file so I run it with --o and it tells me the aws file is now updated. In bash I go to Vim the file and I only see the single "[default]" settings, as if nothing has changed. I run sudo npm run deploy and it gives me the same Error.
I then go and manually set the access and secret:
export AWS_ACCESS_KEY_ID=XXX
export AWS_SECRET_ACCESS_KEY=YYY
I run sudo npm run deploy and it gives me the same Error.
I even removed AWS CLI, and the directory that holds the creditionals and config files - and when I manually set my account creds via serverless config it tells me there already is a profile set up in my aws file, prompting me to use the overwrite command - how is this possible when the file is literally not on my computer?
So I then think that serverless itself has a cache or something, calling the wrong file or whatever for creds, so I uninstall serverless via sudo npm uninstall -g serverless so that I can start from zero again. I then do all of the above steps and more all over again, and nothing has changed. Same error message.
I do have Apex.run set up, but that should be using my AWS CLI config file so I'm not sure if that is causing any problems. But then again I've no clue of anything deep on this subject, and I can't find any ability to remove Apex itself in their docs.
In the package I am trying to deploy, I do not have a profile:XXX set in the serverless.yml file, because I've read if you do not then it just defaults to the [default] profile you have set in the aws creds file on your computer. Just to check, I go into the serverless.yml file and set the profile: default, and the error I now get when I run npm run deploy is
Profile default does not exist
How is that possible when I have the "default" profile set in my creds file? So I remember that previously I ran the serverless config creditionals command and added the profile name of serverless-agent to it (yet didn't save in the aws creds file as I mentioned above), so I add that profile name to the serverless.yml file just to see if this works, and same error of "Profile default does not exist".
So back to the error message. The Role is an account not even related to the IAM user I'm using in my aws creds. Without knowing a lot about this, it's as if the config in serverless via ssh isn't correct or something. Is it using old creds I had set up in Apex.run? Why is the aws creds file not updated with the profile when I manually set it in serverless config command? I am using the same user account (but with new key and secret) that I used a few weeks ago when I correctly deployed and my Lambda and API was set up for me on AWS. Boy do I miss those time and wish I didn't mess up my existing Lambda functions, without setting version number prior, forcing me to start all over.
I am so confused. Any help would be greatly appreciated.
If you are using IAM role then you have to use that IAM role through assume role using powershell.
I was also facing same issue earlier, when we moved from from user to role.

How to make deliver (fastlane) download metadata for multiple targets?

I have an Xcode project with six targets, each target is made to build a separate app. I'm trying to setup fastlane to assist me in publication of these apps.
Fastlane docs suggest using .env files in order to handle multiple targets (you can specify app_identifier, team_name, etc. in different .env files, and then, for instance, call fastlane appstore --env ENV_NAME_HERE). However I can't figure out how to set up deliver properly.
deliver init downloads metadata for one target only by default. I need to download metadata for all my targets to different directories (and then use those directories to upload data, obviously).
deliver download_metadata doesn't accept the --env parameter (my Deliverfile depends on env files). I've tried fastlane deliver --env, but it seems to be just a shorthand for deliver, so it doesn't work either.
I guess I could just manually run deliver with different --metadata_path parameters (and all the other parameters since my Deliverfile is invalid, because it depends on env files), and then later specify directories using Deliverfile + .env files. But since I have Deliverfile and .env files already set up (now I use deliver to upload the binary only), I hoped that there is a better way. Is there?
P.S. This is a large legacy project, so splitting it into six different projects would be great, but it's not an option, unfortunately.
I've been struggling with this as well and setting up the submit is easy using the .env files.
But retrieving the initial data is difficult, but not impossible.
To grab the metadata it ran this command:
fastlane deliver download_metadata -m "./Targets/Release/Metadata" -u "itunes#username" -a "com.example.ios"
And for the screenshots:
fastlane deliver download_screenshots -w "./Targets/Release/Screenshots" -u "itunes#username" -a "com.example.ios"
Adding up to #rckoenes answer:
1) Create an .env.yourEnvName file with this info (as an example):
DLV_METADATA_PATH="../Targets/Your_Target/Metadata"
DLV_ITUNESCONNECT_USERNAME="yourItunesUser#something.com"
DLV_BUNDLE_ID="com.yourCompany.yourTarget"
2) Create a lane like this:
desc "Download metadata"
lane :metadata do
sh('fastlane deliver download_metadata -m "$DLV_METADATA_PATH" -u $DLV_ITUNESCONNECT_USERNAME -a $DLV_BUNDLE_ID')
end
3) Call fastlane like this:
fastlane metadata --env yourEnvName
That way it's a little bit cleaner, and you keep the vars in the .env file.
For automating this call for multiple targets, please refer to: https://docs.fastlane.tools/faqs/#multiple-targets-of-the-same-underlying-app
This is a combination of #rckoenes, #Riddick's answer and this fastlane github issue submission.
I was trying #Riddick's answer to have a cleaner workflow but I couldn't make it work to download metadata. For some reason, it only makes the metadata path folder but no metadata downloaded from iTunesConnect. I did some trial and error and found this line of code from the link above this:
ENV["DELIVER_FORCE_OVERWRITE"] = "1"
added it to the lane and worked!
1) Create an .env.yourEnvName file with this info (as an example):
METADATA_PATH="../Targets/Your_Target/Metadata"
APP_IDENTIFIER="com.yourCompany.yourTarget"
2) Create a lane like this:
desc "Download metadata"
lane :metadata do
ENV["DELIVER_FORCE_OVERWRITE"] = "1" # This is the additional line from Riddick's code
sh "fastlane deliver download_metadata --app_identifier #{ENV['APP_IDENTIFIER'] --metadata_path #{ENV['METADATA_PATH']}"
end
3) Call fastlane like this:
fastlane metadata --env yourEnvName
***I did not use the username parameter because I had it on my Deliver File.

Can't get sauce labs and travis CI to work

I'm trying to run a test with sauce labs + travis CI but no luck.
Here's where I think the issue is:
https://travis-ci.org/angulytics/angular-snitch/builds/50894194#L402
UnknownError: Sauce Labs Authentication Error.
You used username 'None' and access key 'None' to authenticate, which are not valid Sauce Labs credentials.
However, I have added the keys to my travis settings page. Also, it looks like Travis is in fact exporting them
https://travis-ci.org/angulytics/angular-snitch/builds/50894194#L80
$ export SAUCE_USERNAME=[secure]
$ export SAUCE_ACCESS_KEY=[secure]
So what's going on?
If it helps, you can check the rest of the protractor.conf here
https://github.com/angulytics/angular-snitch/blob/master/protractor.conf.js
I can't remember where exactly I found the answer, but I had to remove the seleniumAddress config option for reasons I don't really understand.

Jenkins iOS build using credentials and developer profile

We are using Jenkins as our CI server for our iOS team with the following setup:
Master server on OSX, not running any job
2 slaves on OSX running our integration jobs + UI Testing
Currently all signing identities and provisioning profiles for the apps are uploaded each slave which makes the administration a tad tedious and adding a new node to the cluster even more painful.
To work around this we've looked into using the credentials plugin with Developer profiles and import the profile as the first build step on all iOS jobs but are faced with to main issues:
The import developer profile seems to work the first time (at least for creating the keychain entries) but the job fails with a "no matching provisioning profile" error, even if the developer profile contains all the provisioning profiles required by the target.
Second run on the same job always fail with a "Keychain already exist" error
We've tried some work arounds for the second issue adding a shell build step removing the particular keychain but are still faced with the first error. If we manually install the profile on the slave the build passes but this defeat the purpose of using the credentials plugin.
What do you guys think?
I think the newest version of the credentials plugin now first removes any existing keychains with a matching name before importing, as seen in the log output below.
$ security delete-keychain jenkins-MyAppsBuildName-iOS
$ security create-keychain -p ******** jenkins-MyAppsBuildName-iOS
$ security unlock-keychain -p ******** jenkins-MyAppsBuildName-iOS
Because of this fact, I don't think you will have an issue anymore with duplicate keychain errors on the second run.
As far as the issue relating to the provisioning profile not being found, add the following line inside the execute shell command and run a build on jenkins.
security list-keychains
Take a look at the console for that specific build and you should see a list of all the keychains that are currently in the scope of the shell.
If you do not see "jenkins-MyAppsBuildName-iOS" as a listed keychain, this is why you are having the signing issue. Because the keychain is not listed, it is never even being searched through to find the proper signing identity/profile.
Solution: Warning: it's hacky
I'm not 100% sure why this is happening, but from other threads it appears to be a permissions issue.
Luckily there is an easy way around this.
In the execute shell command add the following:
security list-keychain -s jenkins-${JOB_NAME}
This will reset the keychain list to include the keychain needed to successfully build the project.
To verify that this now lists the proper keychain, you can add the following lines to the shell command:
security list-keychain
security list-keychain -s jenkins-${JOB_NAME}
security list-keychain
Now compare the output of the first list-keychain command with the second list-keychain command in the console. Make sure that the jenkin's build keychain is listed after the second security list-keychain output.
Warning: This will permanently change the keychain list on the system, so it is probably a good idea to reset the keychain after the build completes. You can accomplish this by settings the default desired keychain values in the xcode configuration inside of Jenkin's System Configuration section. After doing so, make sure to tick the check box "Restore OS X keychains after build process as defined in global configuration" under build environment inside of the Jenkins job's page.
Additional info: In my example I set the keychain-list to only include the keychain generated from Jenkins, but you may decide to also include the standard system and login keychain's by modifying the line as such:
security list-keychain -s jenkins-${JOB_NAME} login.keychain System.keychain
Keywords: Jenkins, iOS, slave, node, Xcode, plugin, credentials, .developerprofile

Resources