** ARCHIVE FAILED ** — Exit Status 65 — Here we go again

Margaryta Chepiga
Tumiya
Published in
7 min readMay 19, 2021

So recently, I was working on a project that involved Codemagic.

While working on my project, I ran into the error that I’ve not only seen before, but that I’ve wrote an article on how to resolve it. So I’ve followed my own article and was very surprised to discover that none of the solutions worked for me.

Eventually, I was able to find a solution, hence I’m writing this article and hope that if previous article didn’t help you, this one might.

First thing first, in my previous article I didn’t mention one very small and simple solution that I think is worth mentioning.

If you are running your build on a CI build machine and you are using pods in your project, don’t forget to run pod install.

As it turns out, you can see the archived failed error if you are using pods in your project, but forget to install them. So something simple like the following might save you tons of time:

$ bundle exec pod install

The problem I had, was a little bit more complex than that though.

Problem Stament

My problem statement was actually quite similar to the one I had before. The only difference was that now I was using Codemagic to run my jobs and that the error message was a bit different.

There were an error message raised during the match step, but it didn’t cause a failure in that step:

Instead, the pipeline failed while running gym, with the following error message:

looks familiar, huh? :)

Please note, that the following solution will work for you if you’re:

  • Using Codemagic for your iOS apps
  • Using Fastlane, specifically running the match command

Now, the solution might be helpful to you if you’re not using the above, but I can’t guarantee that since this solution is very specific to the platform.

Details

I’m going to share some details on my project setup and why the problem occurred here, so feel free to skip this part if you want to go straight to the solution.

As I already mentioned, the problem statement was very similar to the one in previous article. However, in this project I had:

  • An iOS project that is hosted at Gitlab
  • Used Fastlane to automate testing, building, code signing and deployments
  • Used Codemagic as a PaaS provider for iOS builds

When I ran into the issue, I already had tested and working Fastlane actions, and already had some experience writing Codemagic workflows (like for running unit and UI tests). However, I hadn’t done any code signing yet.

And the reason I’m mentioning this is because Codemagic has documentation on how to code sign your iOS apps, but none of them seem to involve Fastlane match (which I used):

The thing is, I already had a tested solution to code sign my app using match. Following Codemagic documentation meant that I would have to change a lot of my current configuration as well as storing information I don’t want to in my Codemagic workflow. So I started looking for a solution that would let me keep using match while also fixing the error.

Now, there are several things we should know in order to fix this error.

First, If we look at match documentation, there is a keychain_name option available to specify the keychain in which the certificates should be imported.

Fun fact, is that this option has a default value of login.keychain. Which worked perfectly for us before.

So the reason we are getting this error is that we are trying to import the certificates to the default keychain (login.keychain), but we are unable to due to the absence of the keychain password.

Now, where do we get the password? Spoiler alert, for the default keychain (login.keychain) — we can’t! At least I couldn’t find the way to do so.

And here is when, another important piece comes into play…

Codemagic is using their own keychain utility. This keychain utility is used to manage macOS keychains and certificates. Since it’s consistently used in Codemagic’s code signing examples, this became my first clue:

So what does keychain initialize do?

keychain initialize — Set up the keychain to be used for code signing. Create the keychain at specified path with specified password with given timeout. Make it default and unlock it for upcoming use.

Essentially, in order to fix this error, we need to provide our own keychain and password to it in our match lane. Where keychain utility is used to create the new keychain and password to it.

Solution

So essentially what we need to do is to add keychain initialize to our workflow before running Fastlane, so that our workflow looks like this:

It will generate a new keychain, set it to default and unlock it. If you don’t provide the password, it get’s set to “” by default:

Now, you might be wondering whether we can provide the path and password as options to the keychain initialize command.

According to the command documentation, we can, but in reality when I tried passing my own parameters the command kept on failing.

I think that there is some kind of error in the command itself because I tested on both the pipeline and on the VM itself (by testing on the VM, I mean that I connected to the VM via ssh to run the command and debug the failure), and essentially I could not get a successful result while passing the parameters. The reason why I wanted to pass my own parameters to the keychain initialize command is to be able to specify the keychain path and easily pass it to the match lane.

The weird thing is that command would fail resulting in error, while the keychain would still get created. This is how it looked like:

Now we need to get the keychain path that we will later use in our match command. In order to do that, we need to do this:

keychain get-default > keychain_name.txt && temp=`grep '/private' keychain_name.txt` && echo $temp

and then combine it with the Fastlane command, which results in the following:

- keychain get-default > keychain_name.txt && temp=`grep '/private' keychain_name.txt` && echo $temp && bundle exec fastlane ios build configuration:"Enterprise" api:"prod" upload_to:"firebase" keychain_name:$temp

Essentially what we do here is:

  • keychain get-default — would show us the system default keychain info, which includes the keychain path
  • keychain get-default > keychain_name.txt — would store that information in a txt file
  • temp=`grep '/private' keychain_name.txt` — here we are getting the path of the keychain from the txt file
  • echo $temp — display what we got
  • keychain_name:$temp — pass it to the lane

Now let’s update the Fastlane files so that we could pass the keychain path to the match:

Fastfile update:

lane :build do |options|  cocoapods unless is_ci?

make_build(
api: options[:api],
configuration: options[:configuration],
platform: options[:upload_to],
keychain_name: options[:keychain_name] // line added
)
end

My make_build is located in a file called build_support, and it had a call to run_code_signing lane which was updated to this:

def make_build(api: nil, configuration: nil, tags: [], platform: nil, keychain_name: nil)  ...  run_code_signing(readonly: true, force_update: false, build_type: configuration, keychain_name: keychain_name)

run_code_signing is located in a separate file called match_support:

private_lane :run_code_signing do |opts|  build_type = opts[:build_type]
readonly = opts[:readonly]
device_update = opts[:force_update]
keychain_name = opts[:keychain_name]
if build_type == 'All'
builds = ['Enterprise', 'Release', 'Debug']
builds.each do |type|
run_match(type, readonly, device_update, keychain_name)
end
else
run_match(build_type, readonly, device_update, keychain_name)
end
end
def run_match(build_type, readonly, device_update, keychain_name) configuration = get_config(build_type)
team_id = configuration[:team_id]
profile = configuration[:profile]
bundle_id = ENV["#{build_type.upcase}_BUNDLE_ID"]
match(
git_branch: team_id,
type: profile,
app_identifier: bundle_id,
team_id: team_id,
readonly: readonly,
skip_docs: true,
force_for_new_devices: device_update,
keychain_name: keychain_name,
keychain_password: ""
)
end

Essentially, the majority of changes in the Fastlane files were simply to pass an additional parameter that would be used in match. The only additional thing I did was adding keychain_password: "" to the match.

and…

https://giphy.com/gifs/zCME2Cd20Czvy/links

--

--

Margaryta Chepiga
Tumiya
Writer for

Software Developer, known as overexcited girl who is passionate about technology