ci: Add standalone macOS build support (#7581)

The CI swift workflow needs to be updated to accommodate the macOS
standalone build. This required a decent amount of refactoring to make
the Apple build process more maintainable.

Unfortunately this PR ended up being a giant ball of yarn where pulling
on one thread tended to unravel things elsewhere, since building the
Apple artifacts involve multiple interconnected systems. Combined with
the slow iteration of running in CI, I wasn't able to split this PR into
easier to digest commits, so I've annotated the PR as much as I can to
explain what's changed.

The good news is that Apple release artifacts can now be easily built
from a developer's machine with simply
`scripts/build/macos-standalone.sh`. The only thing needed is the proper
provisioning profiles and signing certs installed.

Since this PR is so big already, I'll save the swift/apple/README.md
updates for another PR.
This commit is contained in:
Jamil
2024-12-28 16:28:09 -06:00
committed by GitHub
parent e2b7f3103b
commit 0c38409588
14 changed files with 477 additions and 202 deletions

View File

@@ -5,172 +5,81 @@ on:
jobs:
build:
name: build-${{ matrix.sdk }}
runs-on: ${{ matrix.runs-on }}
name: ${{ matrix.job_name }}
runs-on: macos-15
permissions:
contents: read
id-token: "write"
env:
XCODE_VERSION: "16.2"
strategy:
fail-fast: false
matrix:
include:
- sdk: macosx
runs-on: macos-14
platform: macOS
xcode: "15.2"
destination: platform=macOS
- sdk: iphoneos
runs-on: macos-14
- job_name: build-ios
rust-targets: aarch64-apple-ios
build-script: scripts/build/ios-appstore.sh
upload-script: scripts/upload/app-store-connect.sh
artifact-file: "Firezone.ipa"
platform: iOS
xcode: "15.2"
destination: generic/platform=iOS
permissions:
contents: read
id-token: "write"
defaults:
run:
working-directory: ./swift/apple
- job_name: build-macos-appstore
rust-targets: aarch64-apple-darwin x86_64-apple-darwin
build-script: scripts/build/macos-appstore.sh
upload-script: scripts/upload/app-store-connect.sh
artifact-file: "Firezone.pkg"
platform: macOS
- job_name: build-macos-standalone
rust-targets: aarch64-apple-darwin x86_64-apple-darwin
build-script: scripts/build/macos-standalone.sh
upload-script: scripts/upload/github-release.sh
# mark:next-apple-version
artifact-file: "firezone-macos-client-1.4.0.dmg"
# mark:next-apple-version
release-name: macos-client-1.4.0
steps:
- uses: actions/checkout@v4
with:
fetch-tags: true # Otherwise we cannot embed the correct version into the build.
- uses: ./.github/actions/setup-rust
with:
targets: aarch64-apple-darwin aarch64-apple-ios x86_64-apple-darwin
targets: ${{ matrix.rust-targets }}
- uses: actions/cache/restore@v4
name: Restore Swift DerivedData Cache
id: cache
with:
path: ~/Library/Developer/Xcode/DerivedData
key: ${{ matrix.runs-on }}-${{ hashFiles('swift/*', 'rust/**/*.rs', 'rust/**/*.toml', 'rust/**/*.lock}') }}
- name: Install the Apple build certificate and provisioning profile
key: ${{ runner.os }}-${{ hashFiles('swift/*', 'rust/**/*.rs', 'rust/**/*.toml', 'rust/**/*.lock}') }}
- run: ${{ matrix.build-script }}
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)"
run: |
# Use the same Xcode version as development
sudo xcode-select -s /Applications/Xcode_${{ matrix.xcode }}.app
# Copy xcconfig
cp Firezone/xcconfig/app_store.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 \
GIT_SHA=${GITHUB_SHA} \
-skipMacroValidation \
-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 \
GIT_SHA=${GITHUB_SHA} \
-skipMacroValidation \
-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
IOS_APP_PROVISIONING_PROFILE: "${{ secrets.APPLE_IOS_APP_PROVISIONING_PROFILE }}"
IOS_NE_PROVISIONING_PROFILE: "${{ secrets.APPLE_IOS_NE_PROVISIONING_PROFILE }}"
MACOS_APP_PROVISIONING_PROFILE: "${{ secrets.APPLE_MACOS_APP_PROVISIONING_PROFILE }}"
MACOS_NE_PROVISIONING_PROFILE: "${{ secrets.APPLE_MACOS_NE_PROVISIONING_PROFILE }}"
STANDALONE_MACOS_APP_PROVISIONING_PROFILE: "${{ secrets.APPLE_STANDALONE_MACOS_APP_PROVISIONING_PROFILE }}"
STANDALONE_MACOS_NE_PROVISIONING_PROFILE: "${{ secrets.APPLE_STANDALONE_MACOS_NE_PROVISIONING_PROFILE }}"
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 }}"
STANDALONE_BUILD_CERT: "${{ secrets.APPLE_STANDALONE_BUILD_CERTIFICATE_BASE64 }}"
STANDALONE_BUILD_CERT_PASS: "${{ secrets.APPLE_STANDALONE_BUILD_CERTIFICATE_P12_PASSWORD }}"
ARTIFACT_PATH: "${{ runner.temp }}/${{ matrix.artifact-file }}"
NOTARIZE: "${{ (github.event_name == 'workflow_dispatch' || github.ref == 'refs/heads/main') }}"
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: ${{ matrix.upload-script }}
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
ARTIFACT_PATH: "${{ runner.temp }}/${{ matrix.artifact-file }}"
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 }}"
GITHUB_TOKEN: "${{ secrets.GITHUB_TOKEN }}"
RELEASE_NAME: "${{ matrix.release-name }}"
PLATFORM: "${{ matrix.platform }}"
- uses: actions/cache/save@v4
if: ${{ steps.cache.outputs.cache-hit != 'true'}}
name: Save Swift DerivedData Cache

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

82
scripts/build/ios-appstore.sh Executable file
View File

@@ -0,0 +1,82 @@
#!/usr/bin/env bash
# Builds the Firezone iOS client for submitting to the App Store
set -euo pipefail
source "./scripts/build/lib.sh"
# Define needed variables
app_profile_id=8da59aa3-e8da-4a8c-9902-2d540324d92c
ne_profile_id=0fccb78a-97c0-41b9-8c54-9c995280ea8e
temp_dir=$(mktemp -d)
archive_path="$temp_dir/Firezone.xcarchive"
export_options_plist_path="$temp_dir/ExportOptions.plist"
git_sha=${GITHUB_SHA:-$(git rev-parse HEAD)}
project_file=swift/apple/Firezone.xcodeproj
code_sign_identity="Apple Distribution: Firezone, Inc. (47R2M6779T)"
if [ "${CI:-}" = "true" ]; then
# Configure the environment for building, signing, and packaging in CI
setup_runner \
"$IOS_APP_PROVISIONING_PROFILE" \
"$app_profile_id.mobileprovision" \
"$IOS_NE_PROVISIONING_PROFILE" \
"$ne_profile_id.mobileprovision"
fi
# Build and sign app
set_project_build_version "$project_file/project.pbxproj"
echo "Building and signing app..."
xcodebuild archive \
GIT_SHA="$git_sha" \
CODE_SIGN_STYLE=Manual \
CODE_SIGN_IDENTITY="$code_sign_identity" \
APP_PROFILE_ID="$app_profile_id" \
NE_PROFILE_ID="$ne_profile_id" \
-project "$project_file" \
-skipMacroValidation \
-archivePath "$archive_path" \
-configuration Release \
-scheme Firezone \
-sdk iphoneos \
-destination 'generic/platform=iOS'
# iOS requires a separate export step; write out the export options plist
# here so we can inject the provisioning profile IDs
cat <<EOF >"$export_options_plist_path"
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>method</key>
<string>app-store</string>
<key>provisioningProfiles</key>
<dict>
<key>dev.firezone.firezone</key>
<string>$app_profile_id</string>
<key>dev.firezone.firezone.network-extension</key>
<string>$ne_profile_id</string>
</dict>
</dict>
</plist>
EOF
# Export the archive
# -exportPath MUST be a directory; the Firezone.ipa will be written here
xcodebuild \
-exportArchive \
-archivePath "$archive_path" \
-exportPath "$temp_dir" \
-exportOptionsPlist "$export_options_plist_path"
package_path="$temp_dir/Firezone.ipa"
echo "Package created at $package_path"
# Move to final location the uploader expects
if [[ -n "${ARTIFACT_PATH:-}" ]]; then
mv "$package_path" "$ARTIFACT_PATH"
fi

95
scripts/build/lib.sh Executable file
View File

@@ -0,0 +1,95 @@
#!/usr/bin/env bash
set -e
# See https://docs.github.com/en/actions/use-cases-and-examples/deploying/installing-an-apple-certificate-on-macos-runners-for-xcode-development
function setup_runner() {
local app_profile="$1"
local app_profile_file="$2"
local ne_profile="$3"
local ne_profile_file="$4"
profiles_path="$HOME/Library/Developer/Xcode/UserData/Provisioning Profiles"
keychain_pass=$(openssl rand -base64 32)
keychain_path="$(mktemp -d)/app-signing.keychain-db"
# Select Xcode specified by the workflow
sudo xcode-select -s "/Applications/Xcode_$XCODE_VERSION.app"
# Install provisioning profiles
mkdir -p "$profiles_path"
base64_decode "$app_profile" "$profiles_path/$app_profile_file"
base64_decode "$ne_profile" "$profiles_path/$ne_profile_file"
# Create a keychain to use for signing
security create-keychain -p "$keychain_pass" "$keychain_path"
# Set it as the default keychain so Xcode can find the signing certs
security default-keychain -s "$keychain_path"
# Ensure it stays unlocked during the build
security set-keychain-settings -lut 21600 "$keychain_path"
# Unlock the keychain for use
security unlock-keychain -p "$keychain_pass" "$keychain_path"
# Install signing certs
install_cert \
"$BUILD_CERT" \
"$BUILD_CERT_PASS" \
"$keychain_pass" \
"$keychain_path"
install_cert \
"$INSTALLER_CERT" \
"$INSTALLER_CERT_PASS" \
"$keychain_pass" \
"$keychain_path"
install_cert \
"$STANDALONE_BUILD_CERT" \
"$STANDALONE_BUILD_CERT_PASS" \
"$keychain_pass" \
"$keychain_path"
}
function base64_decode() {
local input_stdin="$1"
local output_path="$2"
echo -n "$input_stdin" | base64 --decode -o "$output_path"
}
function install_cert() {
local cert_path
local cert="$1"
local pass="$2"
local keychain_pass="$3"
local keychain_path="$4"
cert_path="$(mktemp -d)/cert.p12"
base64_decode "$cert" "$cert_path"
# Import cert into keychain
security import "$cert_path" \
-P "$pass" \
-A \
-t cert \
-f pkcs12 \
-k "$keychain_path"
# Prevent the keychain from asking for password to access the cert
security set-key-partition-list \
-S apple-tool:,apple: \
-k "$keychain_pass" \
"$keychain_path"
# Clean up
rm "$cert_path"
}
function set_project_build_version() {
local project_file="$1"
seconds_since_epoch=$(date +%s)
sed -i '' "s/CURRENT_PROJECT_VERSION = [0-9]/CURRENT_PROJECT_VERSION = $seconds_since_epoch/" \
"$project_file"
}

59
scripts/build/macos-appstore.sh Executable file
View File

@@ -0,0 +1,59 @@
#!/usr/bin/env bash
# Builds the Firezone macOS client for submitting to the App Store
set -euo pipefail
source "./scripts/build/lib.sh"
# Define needed variables
app_profile_id=2bf20e38-81ea-40d0-91e5-330cf58f52d9
ne_profile_id=2c683d1a-4479-451c-9ee6-ae7d4aca5c93
temp_dir=$(mktemp -d)
package_path="$temp_dir/Firezone.pkg"
git_sha=${GITHUB_SHA:-$(git rev-parse HEAD)}
project_file=swift/apple/Firezone.xcodeproj
code_sign_identity="Apple Distribution: Firezone, Inc. (47R2M6779T)"
installer_code_sign_identity="3rd Party Mac Developer Installer: Firezone, Inc. (47R2M6779T)"
if [ "${CI:-}" = "true" ]; then
# Configure the environment for building, signing, and packaging in CI
setup_runner \
"$MACOS_APP_PROVISIONING_PROFILE" \
"$app_profile_id.provisionprofile" \
"$MACOS_NE_PROVISIONING_PROFILE" \
"$ne_profile_id.provisionprofile"
fi
# Build and sign
set_project_build_version "$project_file/project.pbxproj"
echo "Building and signing app..."
xcodebuild build \
GIT_SHA="$git_sha" \
CODE_SIGN_STYLE=Manual \
CODE_SIGN_IDENTITY="$code_sign_identity" \
CONFIGURATION_BUILD_DIR="$temp_dir" \
APP_PROFILE_ID="$app_profile_id" \
NE_PROFILE_ID="$ne_profile_id" \
ONLY_ACTIVE_ARCH=NO \
-project "$project_file" \
-skipMacroValidation \
-configuration Release \
-scheme Firezone \
-sdk macosx \
-destination 'platform=macOS'
# Mac App Store requires a signed installer package
productbuild \
--sign "$installer_code_sign_identity" \
--component "$temp_dir/Firezone.app" \
/Applications \
"$package_path"
echo "Installer package created at $package_path"
# Move to final location the uploader expects
if [[ -n "${ARTIFACT_PATH:-}" ]]; then
mv "$package_path" "$ARTIFACT_PATH"
fi

127
scripts/build/macos-standalone.sh Executable file
View File

@@ -0,0 +1,127 @@
#!/usr/bin/env bash
# Builds the Firezone macOS client for standalone distribution
set -euo pipefail
source "./scripts/build/lib.sh"
# Define needed variables
app_profile_id=c5d97f71-de80-4dfc-80f8-d0a4393ff082
ne_profile_id=153db941-2136-4d6c-96ef-52f748521e78
notarize=${NOTARIZE:-"false"}
temp_dir=$(mktemp -d)
dmg_dir="$temp_dir/dmg"
dmg_path="$temp_dir/Firezone.dmg"
package_path="$temp_dir/package.dmg"
git_sha=${GITHUB_SHA:-$(git rev-parse HEAD)}
project_file=swift/apple/Firezone.xcodeproj
codesign_identity="Developer ID Application: Firezone, Inc. (47R2M6779T)"
if [ "${CI:-}" = "true" ]; then
# Configure the environment for building, signing, and packaging in CI
setup_runner \
"$STANDALONE_MACOS_APP_PROVISIONING_PROFILE" \
"$app_profile_id.provisionprofile" \
"$STANDALONE_MACOS_NE_PROVISIONING_PROFILE" \
"$ne_profile_id.provisionprofile"
fi
# Build and sign
set_project_build_version "$project_file/project.pbxproj"
echo "Building and signing app..."
xcodebuild build \
GIT_SHA="$git_sha" \
CODE_SIGN_STYLE=Manual \
CODE_SIGN_IDENTITY="$codesign_identity" \
PACKET_TUNNEL_PROVIDER_SUFFIX=-systemextension \
OTHER_CODE_SIGN_FLAGS="--timestamp" \
CODE_SIGN_INJECT_BASE_ENTITLEMENTS=NO \
CONFIGURATION_BUILD_DIR="$temp_dir" \
APP_PROFILE_ID="$app_profile_id" \
NE_PROFILE_ID="$ne_profile_id" \
ONLY_ACTIVE_ARCH=NO \
-project "$project_file" \
-skipMacroValidation \
-configuration Release \
-scheme Firezone \
-sdk macosx \
-destination 'platform=macOS'
# Notarize app before embedding within disk image
if [ "$notarize" = "true" ]; then
# Notary service expects a single file, not app bundle
ditto -c -k "$temp_dir/Firezone.app" "$temp_dir/Firezone.zip"
private_key_path="$temp_dir/firezone-api-key.p8"
base64_decode "$API_KEY" "$private_key_path"
# Submit app bundle to be notarized. Can take a few minutes.
# Notarizes embedded app bundle as well.
xcrun notarytool submit "$temp_dir/Firezone.zip" \
--key "$private_key_path" \
--key-id "$API_KEY_ID" \
--issuer "$ISSUER_ID" \
--wait
# Clean up private key
rm "$private_key_path"
# Staple notarization ticket to app bundle
xcrun stapler staple "$temp_dir/Firezone.app"
fi
# Create disk image
mkdir -p "$dmg_dir/.background"
mv "$temp_dir/Firezone.app" "$dmg_dir/Firezone.app"
cp "scripts/build/dmg_background.png" "$dmg_dir/.background/background.png"
ln -s /Applications "$dmg_dir/Applications"
hdiutil create \
-volname "Firezone Installer" \
-srcfolder "$dmg_dir" \
-ov \
-format UDRW \
"$package_path"
# Mount disk image for customization
mount_dir=$(hdiutil attach "$package_path" -readwrite -noverify -noautoopen | grep -o "/Volumes/.*")
# Embed background image to instruct user to drag app to /Applications
osascript <<EOF
tell application "Finder"
tell disk "Firezone Installer"
open
set current view of container window to icon view
set toolbar visible of container window to false
set statusbar visible of container window to false
set bounds of container window to {100, 100, 800, 400}
set viewOptions to the icon view options of container window
set arrangement of viewOptions to not arranged
set icon size of viewOptions to 128
set background picture of viewOptions to file ".background:background.png"
set position of item "Firezone.app" of container window to {200, 128}
set position of item "Applications" of container window to {500, 128}
close
open
update without registering applications
delay 2
end tell
end tell
EOF
# Unmount disk image
hdiutil detach "$mount_dir"
# Convert to read-only
hdiutil convert "$package_path" -format UDZO -o "$dmg_path"
# Sign disk image
codesign --force --sign "$codesign_identity" "$dmg_path"
echo "Disk image created at $dmg_path"
# Move to final location the uploader expects
if [[ -n "${ARTIFACT_PATH:-}" ]]; then
mv "$package_path" "$ARTIFACT_PATH"
fi

View File

@@ -0,0 +1,31 @@
#!/usr/bin/env bash
# Pushes iOS and macOS builds to App Store Connect
set -euo pipefail
source "./scripts/build/lib.sh"
# xcrun altool requires private keys to be files in a specific naming format
temp_dir=$(mktemp -d)
private_key_dir="$temp_dir/private_keys"
private_key_file="AuthKey_$API_KEY_ID.p8"
mkdir -p "$private_key_dir"
base64_decode "$API_KEY" "$private_key_dir/$private_key_file"
cur_dir=$(pwd)
cd "$temp_dir"
# Submit app to App Store Connect
xcrun altool \
--upload-app \
-f "$ARTIFACT_PATH" \
-t "$PLATFORM" \
--apiKey "$API_KEY_ID" \
--apiIssuer "$ISSUER_ID"
# Clean up private key
rm -rf "$private_key_dir"
cd "$cur_dir"

View File

@@ -0,0 +1,21 @@
#!/usr/bin/env bash
# Uploads built packages to a GitHub release
set -euo pipefail
# Only clobber existing release assets if the release is a draft
is_draft=$(gh release view "$RELEASE_NAME" --json isDraft --jq '.isDraft' | tr -d '\n')
if [[ "$is_draft" == "true" ]]; then
clobber="--clobber"
else
clobber=""
fi
sha256sum "$ARTIFACT_PATH" >"$ARTIFACT_PATH.sha256sum.txt"
gh release upload "$RELEASE_NAME" \
"$ARTIFACT_PATH" \
"$ARTIFACT_PATH.sha256sum.txt" \
$clobber \
--repo "$GITHUB_REPOSITORY"

View File

@@ -5,5 +5,4 @@ DerivedData/
xcuserdata/
**/*.xcuserstate
Firezone/xcconfig/config.xcconfig
FirezoneNetworkExtension/Connlib

View File

@@ -595,8 +595,7 @@
OTHER_LDFLAGS = "-lconnlib";
PRODUCT_BUNDLE_IDENTIFIER = "$(inherited).network-extension";
PRODUCT_NAME = "$(PRODUCT_BUNDLE_IDENTIFIER)";
PROVISIONING_PROFILE_SPECIFIER = "";
"PROVISIONING_PROFILE_SPECIFIER[sdk=iphoneos*]" = "$(IOS_NE_PROVISIONING_PROFILE_IDENTIFIER)";
PROVISIONING_PROFILE_SPECIFIER = "$(NE_PROFILE_ID)";
SDKROOT = iphoneos;
SKIP_INSTALL = YES;
SUPPORTED_PLATFORMS = iphoneos;
@@ -637,8 +636,7 @@
OTHER_LDFLAGS = "-lconnlib";
PRODUCT_BUNDLE_IDENTIFIER = "$(inherited).network-extension";
PRODUCT_NAME = "$(PRODUCT_BUNDLE_IDENTIFIER)";
PROVISIONING_PROFILE_SPECIFIER = "";
"PROVISIONING_PROFILE_SPECIFIER[sdk=iphoneos*]" = "$(IOS_NE_PROVISIONING_PROFILE_IDENTIFIER)";
PROVISIONING_PROFILE_SPECIFIER = "$(NE_PROFILE_ID)";
SDKROOT = iphoneos;
SKIP_INSTALL = YES;
SUPPORTED_PLATFORMS = iphoneos;
@@ -679,7 +677,7 @@
OTHER_LDFLAGS = "-lconnlib";
PRODUCT_BUNDLE_IDENTIFIER = "$(inherited).network-extension";
PRODUCT_NAME = "$(PRODUCT_BUNDLE_IDENTIFIER)";
PROVISIONING_PROFILE_SPECIFIER = "";
PROVISIONING_PROFILE_SPECIFIER = "$(NE_PROFILE_ID)";
SDKROOT = macosx;
SKIP_INSTALL = YES;
SUPPORTED_PLATFORMS = macosx;
@@ -717,8 +715,7 @@
OTHER_LDFLAGS = "-lconnlib";
PRODUCT_BUNDLE_IDENTIFIER = "$(inherited).network-extension";
PRODUCT_NAME = "$(PRODUCT_BUNDLE_IDENTIFIER)";
PROVISIONING_PROFILE_SPECIFIER = "";
"PROVISIONING_PROFILE_SPECIFIER[sdk=macosx*]" = "$(MACOS_NE_PROVISIONING_PROFILE_IDENTIFIER)";
PROVISIONING_PROFILE_SPECIFIER = "$(NE_PROFILE_ID)";
SDKROOT = macosx;
SKIP_INSTALL = YES;
SUPPORTED_PLATFORMS = macosx;
@@ -883,9 +880,7 @@
OTHER_LDFLAGS = "";
PRODUCT_BUNDLE_IDENTIFIER = "$(inherited)";
PRODUCT_NAME = "$(TARGET_NAME)";
PROVISIONING_PROFILE_SPECIFIER = "";
"PROVISIONING_PROFILE_SPECIFIER[sdk=iphoneos*]" = "$(IOS_APP_PROVISIONING_PROFILE_IDENTIFIER)";
"PROVISIONING_PROFILE_SPECIFIER[sdk=macosx*]" = "$(MACOS_APP_PROVISIONING_PROFILE_IDENTIFIER)";
PROVISIONING_PROFILE_SPECIFIER = "$(APP_PROFILE_ID)";
SDKROOT = auto;
SUPPORTED_PLATFORMS = "iphoneos macosx";
SWIFT_EMIT_LOC_STRINGS = YES;
@@ -934,9 +929,7 @@
PRODUCT_BUNDLE_IDENTIFIER = "$(inherited)";
PRODUCT_MODULE_NAME = "$(PRODUCT_NAME:c99extidentifier)";
PRODUCT_NAME = "$(TARGET_NAME)";
PROVISIONING_PROFILE_SPECIFIER = "";
"PROVISIONING_PROFILE_SPECIFIER[sdk=iphoneos*]" = "$(IOS_APP_PROVISIONING_PROFILE_IDENTIFIER)";
"PROVISIONING_PROFILE_SPECIFIER[sdk=macosx*]" = "$(MACOS_APP_PROVISIONING_PROFILE_IDENTIFIER)";
PROVISIONING_PROFILE_SPECIFIER = "$(APP_PROFILE_ID)";
SDKROOT = auto;
SUPPORTED_PLATFORMS = "iphoneos macosx";
SWIFT_EMIT_LOC_STRINGS = YES;

View File

@@ -1,16 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>method</key>
<string>app-store</string>
<key>provisioningProfiles</key>
<dict>
<key>dev.firezone.firezone</key>
<string>07102026-065f-4cc0-800b-5f8595c50ce8</string>
<key>dev.firezone.firezone.network-extension</key>
<string>c6feb05e-063a-4429-8563-57c1d2755067</string>
</dict>
</dict>
</plist>

View File

@@ -1,13 +0,0 @@
// Apple Developer account-specific configuration for App Store distribution
DEVELOPMENT_TEAM = 47R2M6779T
PRODUCT_BUNDLE_IDENTIFIER = dev.firezone.firezone
APP_GROUP_ID[sdk=macosx*] = 47R2M6779T.dev.firezone.firezone
APP_GROUP_ID_PRE_1_4_0[sdk=macosx*] = 47R2M6779T.group.dev.firezone.firezone
APP_GROUP_ID[sdk=iphoneos*] = group.dev.firezone.firezone
APP_GROUP_ID_PRE_1_4_0[sdk=iphoneos*] = group.dev.firezone.firezone
CODE_SIGN_STYLE = Manual
CODE_SIGN_IDENTITY = Apple Distribution: Firezone, Inc. (47R2M6779T)
IOS_APP_PROVISIONING_PROFILE_IDENTIFIER = 07102026-065f-4cc0-800b-5f8595c50ce8
MACOS_APP_PROVISIONING_PROFILE_IDENTIFIER = 9933ad98-3698-4782-ba8c-7e2da4c9835a
IOS_NE_PROVISIONING_PROFILE_IDENTIFIER = c6feb05e-063a-4429-8563-57c1d2755067
MACOS_NE_PROVISIONING_PROFILE_IDENTIFIER = 789f5daf-bc9a-49fd-befd-bcfc88dd97a1

View File

@@ -1,12 +0,0 @@
// Apple Developer account-specific configuration for Standalone distribution
DEVELOPMENT_TEAM = 47R2M6779T
PRODUCT_BUNDLE_IDENTIFIER = dev.firezone.firezone
APP_GROUP_ID[sdk=macosx*] = 47R2M6779T.dev.firezone.firezone
APP_GROUP_ID_PRE_1_4_0[sdk=macosx*] = 47R2M6779T.group.dev.firezone.firezone
APP_GROUP_ID[sdk=iphoneos*] = group.dev.firezone.firezone
APP_GROUP_ID_PRE_1_4_0[sdk=iphoneos*] = group.dev.firezone.firezone
CODE_SIGN_STYLE = Manual
CODE_SIGN_IDENTITY = Developer ID Application: Firezone, Inc. (47R2M6779T)
MACOS_APP_PROVISIONING_PROFILE_IDENTIFIER = 734b5163-46a4-4676-9ee7-01f25ec968e7
MACOS_NE_PROVISIONING_PROFILE_IDENTIFIER = 6c0eee5f-00fd-40ab-ba6c-6ae83c59d19d
PACKET_TUNNEL_PROVIDER_SUFFIX = -systemextension