It hurts to keep the whole team on the same building as your current app. There are a handful of automated steps and long time runs while waiting for the Apple servers to convey your data. Let's automate this process so you can set it once and forget it.
Our goal is to take our existing process and automate it with CircleCI. For this example, our current process is:
agvtool bump -allto update the building number. What is
- Obligate and push the version hump
- In Xcode, archive the project and send to TestFlight
- Wait for the building to be uploaded and processed
- In the App Store Connect, distribute the building to our test team.
How is this translated into an automated system? We have some kinks to train.
Adding commitments triggers a build loop
We want this build to run on all obligations to
master . Part of our process is to add a git commit to the master (the commitment that increases the build number by one). This is a recipe for infinitely increasing your engagement rate. #protip
We plan to check the last commitment to make sure it is not a build commitment before adding a new one. We will also add a tag to this commitment, which will be useful later.
We cannot submit a given build number more than once.
Our CI process is called twice – once for the change to master, and once for build bull commit. If we leave to TestFlight on both of these buildings, the other will always fail. It's bad. The last thing we want to do is make the team comfortable with the CI task errors.
We can use some of the job filtering that CircleCI gives us to make sure we just try to submit a building once, after we commit ourselves to build bump.
We need Apple signing credentials to submit to TestFlight
fastlane handles much of this work for us, it means the CI system needs access to two storage locations – one with the code and one with our
fastlane – was able to sign credentials. Since GitHub prevents you from using a single distribution key more than once, this is not as simple as it sounds.
We plan to create two distribution keys, one for each repo, and configure the CI system to use the right one at the right time.
Distributing a build requires build tags
Obviously you can only hard code "bug fixes and enhancements", but we can do better. So we want to! Let's collect the engagement messages since our last introduction to a short list as the notes to be sent to TestFlight.
We can re-use the building codes we need above to ensure we get an accurate description of what has changed since the last building we sent out.
First Steps: Take Major Measures with Fastlane
Let's pause the CI ambitions for a moment and focus on reducing this process with multiple applications and sites in a sequence of terminal commands. Fastlane can handle most of the heavy lifting here.
Lock the building number
Let's look at how we build steadily first. In our
Fastfile we added a new path with the name
desc "Bump and tag version" job: bump do ensure_git_status_clean bump_message = "Bump build number." # If our last commitment is not a build shock, then steady build. build_is_already_bumped = last_git_commit [:message]. include? bump_message next if build_is_already_bumped increment_build_number commit_version_bump ( message: bump_message, xcodeproj: "Project.xcodeproj" ) add_git_tag push_to_git_remote the end
The difficult thing here is to determine if the last commitment was a building block. We had to do
include? rather than a right equality check, since the value of
last_message includes a new line at the end of it for some reason. Then we tag the engagement with the build number and push to remote control.
Send to TestFlight
Let's see how we got the thing to send to TestFlight.
desc "Send the latest version of build to testflight" lane: send_to_testflow do | options | # 1. Do some math to get building tags build_number = get_build_number (xcodeproj: PROJECT_PATH) last_build_number = build_number.to_i - 1 build_tag = "builds / iosbump /" + build_number last_build_tag = "builds / iosbump /" + last_build_number.to_s # 2. Generate a change log comments = changelog_from_git_commits ( between: [last_build_tag, build_tag], pen: "-% s", date_format: "short", match_lightweight_tag: false, merge_commit_filtering: "exclude_merges" ) # 3. Build the app compliance (type: "appstore", readonly: true, skip_docs: true) target_scheme = options [:scheme] || "MyApp-Debug-Development" build_app (form: target_scheme) # 4. Upload it to test aircraft groups = options [:groups] || "All build" upload_to_testflight ( changelog: comments, distribute_external: true, groups: groups ) the end
Let's look into each of these four sections:
- Here we make
build_tag based on the codes we generated in the path above. Note that this is the default output from
add_git_tagfrom that script. Although everything else can be configured, the
filepart of the tag cannot be the most important. So if you have called the above field
bumpas we did, then the code will have
iosbumpin the middle.
- This compiles the obligations between these two codes into a nicely formatted list. For example:
- Bump build number. - Fix errors and warnings caused by pod update. - Updates MyPodDependency pod from 0.4.1 to 3.0.0.
- Here we get credentials, do the building. Note that our form must match the profile type configured in the
- Upload build to TestFlight in the end. The value of the
groupscorresponds to a previously added group in the App Store Connect.
One more thing to
This will have no effect yet, but when CircleCI does these commands run later, it will complain. Although it can download the credentials, it has nowhere to store them. You must add a line to run before each path:
`` rubin before everyone does | job | setup_circle_ci end ``
"But wait," do you ask, "can't I just do it at the top of our
submit_to_testflight course, since that's the only place we need it?" Maybe. But you can write other tracks and forget this step later. The documents say to do it before each lane. I would do as they say.
Good to go
At this time, you can distribute buildings from the terminal with two commands:
Hooray! That's better!
However, our goal is to avoid having to write these two commands. To achieve this, we must configure CircleCI to run these commands in Part 2.