Files
firezone/.github/workflows/_swift.yml
Jamil 9c2c719424 Remove duplicate ios/macos builds (#2851)
We're uploading dupe builds of the client on macOS and iOS because we're
testing the build on Xcode 14 and 15.

Since Xcode 15 is stable now, builds for 14 can be removed.
2023-12-10 17:30:28 +00:00

189 lines
8.5 KiB
YAML

name: Swift
on:
workflow_call:
workflow_dispatch:
jobs:
build:
runs-on: ${{ matrix.runs-on }}
strategy:
fail-fast: false
matrix:
include:
- sdk: macosx
runs-on: macos-13
platform: macOS
destination: platform=macOS
xcode: "15.0"
- sdk: iphoneos
runs-on: macos-13
platform: iOS
destination: generic/platform=iOS
xcode: "15.0"
# TODO: Enable when GH Actions has macos-14 runners. Until this, this depends
# on self-hosted runners which can be less reliable and have issues with things
# like sccache.
# See https://github.com/firezone/firezone/actions/runs/6608338431/job/17946908445
# - sdk: macosx
# runs-on: macos-14
# platform: macOS
# destination: platform=macOS
# - sdk: iphoneos
# runs-on: macos-14
# platform: iOS
# destination: generic/platform=iOS
permissions:
contents: read
id-token: 'write'
defaults:
run:
working-directory: ./swift/apple
steps:
- uses: actions/checkout@v4
- uses: ./.github/actions/setup-rust
with:
targets: aarch64-apple-darwin aarch64-apple-ios
- uses: actions/cache/restore@v3
name: Restore Swift DerivedData Cache
with:
path: ~/Library/Developer/Xcode/DerivedData
key: ${{ matrix.runs-on }}-${{ runner.arch }}-swift-${{ hashFiles('swift/*', 'rust/**/*.rs', 'rust/**/*.toml', 'rust/**/*.lock}') }}
restore-keys: |
${{ matrix.runs-on }}-${{ runner.arch }}-swift-
- name: Install the Apple build certificate and provisioning profile
env:
BUILD_CERT: ${{ secrets.APPLE_BUILD_CERTIFICATE_BASE64 }}
BUILD_CERT_PASS: ${{ secrets.APPLE_BUILD_CERTIFICATE_P12_PASSWORD }}
INSTALLER_CERT: ${{ secrets.APPLE_MAC_INSTALLER_CERTIFICATE_BASE64 }}
INSTALLER_CERT_PASS: ${{ secrets.APPLE_MAC_INSTALLER_CERTIFICATE_P12_PASSWORD }}
KEYCHAIN_PASS: ${{ secrets.APPLE_RUNNER_KEYCHAIN_PASSWORD }}
IOS_APP_PP: ${{ secrets.APPLE_IOS_APP_PROVISIONING_PROFILE }}
IOS_NE_PP: ${{ secrets.APPLE_IOS_NE_PROVISIONING_PROFILE }}
MACOS_APP_PP: ${{ secrets.APPLE_MACOS_APP_PROVISIONING_PROFILE }}
MACOS_NE_PP: ${{ secrets.APPLE_MACOS_NE_PROVISIONING_PROFILE }}
run: |
BUILD_CERT_PATH=$RUNNER_TEMP/build_certificate.p12
INSTALLER_CERT_PATH=$RUNNER_TEMP/installer_certificate.cer
KEYCHAIN_PATH=$RUNNER_TEMP/app-signing.keychain-db
PP_PATH=~/Library/MobileDevice/Provisioning\ Profiles
mkdir -p "$PP_PATH"
# import certificate and provisioning profiles from secrets
echo -n "$BUILD_CERT" | base64 --decode -o $BUILD_CERT_PATH
# Matrix won't let us access secrets (for good reason), so use an explicit conditional here instead
if [ "${{ matrix.platform }}" = "iOS" ]; then
echo -n "$IOS_APP_PP" | base64 --decode -o "$PP_PATH"/app.mobileprovision
echo -n "$IOS_NE_PP" | base64 --decode -o "$PP_PATH"/ne.mobileprovision
elif [ "${{ matrix.platform }}" = "macOS" ]; then
echo -n "$MACOS_APP_PP" | base64 --decode -o "$PP_PATH"/app.provisionprofile
echo -n "$MACOS_NE_PP" | base64 --decode -o "$PP_PATH"/ne.provisionprofile
# Submission to the macOS app store requires an installer package
# which must be signed separately.
echo -n "$INSTALLER_CERT" | base64 --decode -o $INSTALLER_CERT_PATH
else
echo "Platform not supported"
exit 1
fi
# create temporary keychain
security create-keychain -p "$KEYCHAIN_PASS" $KEYCHAIN_PATH
security set-keychain-settings -lut 21600 $KEYCHAIN_PATH
security unlock-keychain -p "$KEYCHAIN_PASS" $KEYCHAIN_PATH
# import certificate to keychain
security import $BUILD_CERT_PATH -P "$BUILD_CERT_PASS" -A -t cert -f pkcs12 -k $KEYCHAIN_PATH
if [ "${{ matrix.platform }}" = "macOS" ]; then
security import $INSTALLER_CERT_PATH -P "$INSTALLER_CERT_PASS" -A -t cert -f pkcs12 -k $KEYCHAIN_PATH
fi
security list-keychain -d user -s $KEYCHAIN_PATH
- name: Build and sign app
id: build
env:
# Build universal binary
ONLY_ACTIVE_ARCH: no
# Needed because `productbuild` doesn't support picking this up automatically like Xcode does
INSTALLER_CODE_SIGN_IDENTITY: "3rd Party Mac Developer Installer: Firezone, Inc. (47R2M6779T)"
REQUESTED_XCODE_VERSION: ${{ matrix.xcode }}
run: |
# Set Xcode version to use if provided
[[ ! -z "$REQUESTED_XCODE_VERSION" ]] && sudo xcode-select -s /Applications/Xcode_$REQUESTED_XCODE_VERSION.app
# Copy xcconfig
cp Firezone/xcconfig/release.xcconfig Firezone/xcconfig/config.xcconfig
# App Store Connect requires a new build version on each upload and it must be an integer.
# See https://developer.apple.com/documentation/xcode/build-settings-reference#Current-Project-Version
seconds_since_epoch=$(date +%s)
sed -i '' "s/CURRENT_PROJECT_VERSION = [0-9]/CURRENT_PROJECT_VERSION = $seconds_since_epoch/" \
Firezone.xcodeproj/project.pbxproj
# Unfortunately the macOS app requires an installer package to make it into the App Store,
# while iOS requires an ipa. The process for building each of these is slightly different.
if [ "${{ matrix.platform }}" = "iOS" ]; then
# Build archive
xcodebuild archive \
-archivePath $RUNNER_TEMP/Firezone.xcarchive \
-configuration Release \
-scheme Firezone \
-sdk ${{ matrix.sdk }} \
-destination '${{ matrix.destination }}'
# Export IPA
xcodebuild \
-exportArchive \
-archivePath $RUNNER_TEMP/Firezone.xcarchive \
-exportPath $RUNNER_TEMP/ \
-exportOptionsPlist Firezone/ExportOptions.plist
# Save resulting file to use for upload
echo "app_bundle=$RUNNER_TEMP/Firezone.ipa" >> "$GITHUB_OUTPUT"
elif [ "${{ matrix.platform }}" = "macOS" ]; then
# Build app bundle
xcodebuild build \
-configuration Release \
-scheme Firezone \
-sdk ${{ matrix.sdk }} \
-destination '${{ matrix.destination }}'
# Move it from randomized build output dir to somewhere we can find it
mv ~/Library/Developer/Xcode/DerivedData/Firezone-*/Build/Products/Release/Firezone.app $RUNNER_TEMP/.
# Create signed installer pkg
productbuild \
--sign "${{ env.INSTALLER_CODE_SIGN_IDENTITY }}" \
--component $RUNNER_TEMP/Firezone.app /Applications $RUNNER_TEMP/Firezone.pkg
# Save resulting file to use for upload
echo "app_bundle=$RUNNER_TEMP/Firezone.pkg" >> "$GITHUB_OUTPUT"
else
echo "Unsupported platform"
exit 1
fi
- name: Upload build to App Store Connect
if: ${{ github.event_name == 'workflow_dispatch' || (github.ref == 'refs/heads/main' && contains(github.event.head_commit.modified, 'elixir/VERSION')) }}
env:
ISSUER_ID: ${{ secrets.APPLE_APP_STORE_CONNECT_ISSUER_ID }}
API_KEY_ID: ${{ secrets.APPLE_APP_STORE_CONNECT_API_KEY_ID }}
API_KEY: ${{ secrets.APPLE_APP_STORE_CONNECT_API_KEY }}
run: |
# set up private key from env
mkdir -p ~/private_keys
echo "$API_KEY" > ~/private_keys/AuthKey_$API_KEY_ID.p8
# Submit app to App Store Connect
xcrun altool \
--upload-app \
-f ${{ steps.build.outputs.app_bundle }} \
-t ${{ matrix.platform }} \
--apiKey $API_KEY_ID \
--apiIssuer $ISSUER_ID
- uses: actions/cache/save@v3
if: ${{ github.ref == 'refs/heads/main' }}
name: Save Swift DerivedData Cache
with:
path: ~/Library/Developer/Xcode/DerivedData
# Swift benefits heavily from build cache, so aggressively write a new one
# on each build on `main` and attempt to restore it in PR builds with broader restore-key.
key: ${{ matrix.runs-on }}-${{ runner.arch }}-swift-${{ hashFiles('swift/*', 'rust/**/*.rs', 'rust/**/*.toml', 'rust/**/*.lock}') }}