The past few days I was attempting to auto deploy a react native app to App Center using Github actions. This process is the exact same for a fully native app, since we are running the normal xcode / gradle commands. I'll walk you through all the annoying things Apple put me through, so you don't have to. If you only want to build for Android, you can skip to that and be done in a few minutes.. iOS setup is much more involved
iOS
There's not much out there when you search for help with iOS builds, and the only ones you find tell you to use fastlane for cert management. I knew there had to be a way though, even with my naive knowledge of the profiles and certificate logic that iOS app builds have, I know I only needed to download two files in order to build an app on my local machine, so it couldn't be too hard, right?
Before I start, let me quickly rant about Apple. I was able to get it mostly working, then ran into the code signing process prompting for a password and hanging indefinitely inside the build container.. The fix for this is a command that makes no sense, and someone had to reverse engineer Apple's tooling just to find it. It's been almost 4 years and Apple has yet to improve on any of these command line utilities. It's honestly a miracle we are able to make this work at all.
Getting Started
Instead of stepping through each little piece of the config, I'm going to share the final piece of the main config, then explain the parts around it. If you are not using react native, you will need to remove the
ios
folder prefix from my commands. Place this inside .github/workflows/ios-build.yaml
This tutorial is assuming that you have a signing certificate and provisioning profile for the app on you machine already. Also, I'm doing an ad-hoc build.
Install GPG
I followed the official guide from github on how to create secrets that are too large to be stored as plain text. Unfortunately, they fail to mention that gpg isn't installed on the Mac OS images, but it is on all the others. There's an open issue about getting this added to the Mac OS image itself. This would be great because it takes about 2.5 minutes just to install gpg. We will be using it to encrypt our provisioning profile and signing certificate.
Add Cert / Profile to the repo
Jun 10, 2019 There are plenty of ways to use GitHub: the website, the app, or the terminal. The terminal is the best way to sync local projects to a GitHub repository, and once you learn the basics, you'll be able to push local projects to remote GitHub repositories in seconds. Stockfish for Mac. Stockfish for Mac is a powerful chess analysis app. Play two-player games on the beautiful chess board, or get instant accurate analysis of any game. Stockfish for Mac web site. Mac App Store download link. Stockfish for Mac has many of the essential features one would expect in a professional chess analysis.
Here's what I did first. Open up your app in xcode, go to Build Settings under your app target, and find
Signing
Hover over the release profile, and to the right of it you should see
Profile Name (uuid)
Copy this uuid. We need it to get the profile and it's also needed in a later step.
Open up a terminal window inside of your app's directory. Make a folder called
.github
inside it create workflows
and secrets
How to make apps on mac invisible.Next, run this:
I know, so weird that I didn't use
cp
. I was thinking the same thing. Anyways, be sure to paste the UUID from xcode so that you get the right one.Next, we need to encrypt it, this way other people can't access the profile. But first, we need to grab the signing certificate as well.
Inside xcode, go to preferences -> accounts -> click the account you're using -> manage certificates -> right click on the cert you are using, and click export.
Don't give it a password. Put it inside the app folder as well, it should be named
Certificates.p12
Encrypt Certificate and Profile
Encrypting them is easy, make sure you have
gpg
installed. On Mac, you can run brew install gpg
, linux machines should have it already installed. From a terminal window inside your app:For both commands, I used a randomly generated password. I recommend using the same password for both files.
Immediately after you finish running the command for both files, open up the settings page for your repo on github, go to secrets, and create a new one called
IOS_PROFILE_KEY
and the value is the password you just used for gpg. Next, delete Certificates.p12
and profile.mobileprovision
. We don't want to accidentally commit these since they are unencrypted. Lastly, move
Certificates.p12.gpg
and profile.mobileprovision.gpg
to the .github/secrets
folder.Decrypt and Install Certs on the server
We're almost there with this gpg stuff, I promise. Create
.github/secrets/decrypt_secrets.sh
and put the following inside it:First off, we decrypt both files using the secret key we just added to github. Next, we copy the mobile provision into the same location that it existed on our local machine. Funny enough.. I never saw this anywhere because I could only find people using fastlane. It was just a lucky guess that this is the correct procedure.
After that is the weird stuff.. We create a new keychain on the build server. This might not be necessary, but through my trials and errors I ended up doing it. The problem I initially ran into was xcode building correctly, but hanging forever because the codesign tool would prompt asking for a password, clearly we can't go into the UI and hit the enter key (the password is blank).
The final line, set-key-partition-list, is the most important part that fixes the issue I described above. Even though nobody seems to really understand the meaning of this command. You can read more about it on this SO post I found. The commenter seemed to give more info than Apple does about this whole process, so I followed them fully, by including the new keychain part. Do as you wish, but the config I have works :)
Make sure you run
chmod +x .github/secrets/decrypt_secrets.sh
before you commit any of this.Back to the actions
With most of the setup out of the way, let me continue explaining the actions file I posted above. Please have it open while reading along below.
Reference
Selecting xcode version
After installing gpg, we can select an xcode version. This may be really outdated by the time you're reading it. Github has a nifty docs page that is about a mile long. Here's the section on xcode. This way you can adjust the config if you need to use a different version of xcode or the iOS sdk.
npm / yarn
Next step is caching the npm dependencies then running yarn install. You can remove it if you're doing a fully native app. If you are using React Native, this will cache the
node_modules
folder until the yarn.lock
changes. If you use npm, simply change yarn.lock
to package.json
You'll notice my yarn install step is setting an NPM_TOKEN value. My app uses private npm packages so this piece of code is needed for it to download them.
Pods
Very similar to npm / yarn step, this will cache all the pods until the lock file changes. Very self explanatory.
Build app
Okay, our last couple things to configure..
Replace
appname
with the actual app name you see in xcode. Replace UUID
with the mobile provision uuid that we copied over in the gpg encryption step. There may be an easier way, but here's how I got the CODE_SIGN_IDENTITY
value.. Inside xcode, with the project open, go to General
and click the little i
icon next to the Signing (Release)
section. It should be spelled out under the Certificates included
section. I hope this helps. If you still can't find it, the next step will have you do a build inside xcode, and the outputted plist file should have it inside.Exporting
Replace appname with the app's name one more time, this command actually spits out the
.ipa
file. To get the exportOptionsPlist
, inside xcode, do a product -> archive of your app. Choose distribute, choose ad-hoc. And then export it to your desktop. Now copy the file inside it, ExportOptions.plist
to appname/ios/ci.plist
. It's almost all set for you, but we have to add the following BEFORE ApplicationPropertiesNow it should be good to go and successfully build..
Deploy it!
The ipa is here:
ios/build/cnnrnv2.ipa
Which means it's up to you what to do with it. The Mac OS environment doesn't support docker actions on github, most actions won't work. If there's not an existing action for the platform you're using, you will have to make a node module and deploy it to npm like appcenter does.
You'll see that I am deploying to app center. They offer a node cli, so I install it, and then run the distribute command with the path to the ipa file. Take note of this part:
--release-notes '$(git log -1 --pretty=format:%s)'
this attaches the commit to the release notes. Might be useful to pass this git log command for any service you may be using. This is much quicker than iOS. Here's the config. Place it in
.github/workflows/android-deploy.yaml
Thats.. seriously it. So much easier than iOS. Not much setup is required here, but I'll go over the file. Run mean app on mac.
Setup
The first three actions are setup. Checking out the code, adding in node and java. If you are not using react native you may remove the node action and yarn install step.
Caching npm dependencies
As explained in the iOS tutorial, this will hash the yarn.lock file (change to package-lock.json if using npm) and restore the
node_modules
each time it is ran, if the lock hasn't changed.Install yarn dependencies
GitHub - Git-it-App/git-it-electron: ? ? Git-it Is A (Mac ..
![Github desktop app mac Github desktop app mac](https://www.androidcentral.com/sites/androidcentral.com/files/styles/large/public/article_images/2016/08/Simplenote-Foss.jpg?itok=J24wTZTZ)
This step adds my NPM_TOKEN secret to let yarn access private npm packages, otherwise its a basic yarn install command.
Build for Android
Builds and spits out the apk for us! Standard gradle.
Deploy
Please reference the deploy section for iOS. The only difference is the nicely built action that we are able to use, because linux supports all docker based actions.
Conclusion
I hope I didn't miss anything. I tried my best to reference issues and articles I found along the way. If you get stuck during any of this, or have other solutions, please reach out on twitter @zachcodes so I can update the article. I really wish Apple would invest in making their command line tooling better. I don't mind having to encrypt the cert and profile, but let me pass them in to the build command as arguments.. Also, why do we need to create this extra plist file just to export? Give me an ad-hoc build command and remove the duplication of setting the profile uuid in every command.. Oh well..
At least we have a way to make it work! Overall I'm happy with the outcome, just shocked at how Apple doesn't bother making any of this an easy process. Github has definitely taken away much of the pain.
Installation
Homebrew is package manager for Macs which makes installing lots of different software like Git, Ruby, and Node simpler. Homebrew lets you avoid possible security problems associated with using the
sudo
command to install software like Node.Prerequisites
- You should have some familiarity with the Mac Terminal application since you’ll need to use it to install Homebrew. The Terminal application is located in the Utilities folder in the Applications folder.
- Dependencies. You need to install one other piece of software before you can install Homebew:
- Xcode. Install Apple’s Xcode development software: Xcode in the Apple App Store.
Installation Overview
Installing Homebrew is straightforward as long as you understand the Mac Terminal. The Homebrew installation process guides through each step.
Installation Steps
- Open the Terminal app.
- Type
ruby -e '$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install)'
You’ll see messages in the Terminal explaining what you need to do to complete the installation process. You can learn more about Homebrew at the Homebrew website.
How to Update Homebrew
New versions of Homebrew come out frequently, so make sure you update it before updating any of the other software components that you’ve installed using Homebrew.* In Terminal type
brew update
How to Uninstall Homebrew
- Open the Terminal app
- Type
ruby -e '$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/uninstall)'
This downloads and runs the uninstaller script. Follow the instructions and Homebrew will be removed from your computer.