Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
139 changes: 139 additions & 0 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,139 @@
name: Release

# Triggered when a version tag is pushed (e.g. v1.2.3).
# Builds a fully code-signed, notarized macOS app and attaches it to a GitHub Release.
#
# Required GitHub Actions secrets:
# DEVELOPER_ID_APPLICATION_CERT_P12_BASE64
# Base64-encoded Developer ID Application certificate (.p12).
# Export from Keychain Access and encode with:
# base64 -i cert.p12 | pbcopy
# DEVELOPER_ID_APPLICATION_CERT_PASSWORD
# Password used to protect the exported .p12 file.
# APPLE_TEAM_ID
# 10-character Apple Developer Team ID (visible in developer.apple.com).
# NOTARIZATION_APPLE_ID
# Apple ID (email) used to submit the notarization request.
# NOTARIZATION_PASSWORD
# App-specific password for the Apple ID above.
# Create one at appleid.apple.com under "Sign-In and Security".

on:
push:
tags:
- 'v*'

jobs:
release:
name: Build, Sign, Notarize & Release
runs-on: macos-14
permissions:
contents: write

steps:
- name: Checkout
uses: actions/checkout@v4

- name: Show Xcode version
run: xcodebuild -version

# ── Code-signing setup ────────────────────────────────────────────────
- name: Import Developer ID certificate
env:
P12_BASE64: ${{ secrets.DEVELOPER_ID_APPLICATION_CERT_P12_BASE64 }}
P12_PASSWORD: ${{ secrets.DEVELOPER_ID_APPLICATION_CERT_PASSWORD }}
run: |
# Write the p12 to a temp file
P12_PATH="$RUNNER_TEMP/developer_id.p12"
echo "$P12_BASE64" | base64 --decode -o "$P12_PATH"

# Create a temporary keychain so the cert is isolated from the login keychain
KEYCHAIN_PATH="$RUNNER_TEMP/signing.keychain-db"
KEYCHAIN_PASSWORD="$(openssl rand -hex 16)"
security create-keychain -p "$KEYCHAIN_PASSWORD" "$KEYCHAIN_PATH"
security set-keychain-settings -lut 21600 "$KEYCHAIN_PATH"
security unlock-keychain -p "$KEYCHAIN_PASSWORD" "$KEYCHAIN_PATH"

# Import the certificate
security import "$P12_PATH" \
-k "$KEYCHAIN_PATH" \
-P "$P12_PASSWORD" \
-T /usr/bin/codesign \
-T /usr/bin/security

# Allow codesign to access the key without a UI prompt
security set-key-partition-list \
-S apple-tool:,apple: \
-k "$KEYCHAIN_PASSWORD" \
"$KEYCHAIN_PATH"

# Make the temporary keychain the default search list
security list-keychains -d user -s "$KEYCHAIN_PATH" $(security list-keychains -d user | tr -d '"')

# ── Archive ───────────────────────────────────────────────────────────
- name: Archive
run: |
set -o pipefail
xcodebuild archive \
-project "Table Tool.xcodeproj" \
-scheme "Table Tool" \
-configuration Release \
-archivePath "$RUNNER_TEMP/TableTool.xcarchive" \
ONLY_ACTIVE_ARCH=NO \
MACOSX_DEPLOYMENT_TARGET=10.13 \
CODE_SIGN_STYLE=Manual \
CODE_SIGN_IDENTITY="Developer ID Application" \
DEVELOPMENT_TEAM="${{ secrets.APPLE_TEAM_ID }}" \
| xcpretty

# ── Export (produces the signed .app) ─────────────────────────────────
- name: Export archive
run: |
# Substitute the Team ID placeholder in ExportOptions.plist
sed "s/\$(APPLE_TEAM_ID)/${{ secrets.APPLE_TEAM_ID }}/g" \
ExportOptions.plist > "$RUNNER_TEMP/ExportOptions.plist"

set -o pipefail
xcodebuild -exportArchive \
-archivePath "$RUNNER_TEMP/TableTool.xcarchive" \
-exportPath "$RUNNER_TEMP/export" \
-exportOptionsPlist "$RUNNER_TEMP/ExportOptions.plist" \
| xcpretty

# ── Notarize ──────────────────────────────────────────────────────────
- name: Notarize app
env:
NOTARIZATION_APPLE_ID: ${{ secrets.NOTARIZATION_APPLE_ID }}
NOTARIZATION_PASSWORD: ${{ secrets.NOTARIZATION_PASSWORD }}
APPLE_TEAM_ID: ${{ secrets.APPLE_TEAM_ID }}
run: |
APP_PATH="$RUNNER_TEMP/export/Table Tool.app"

# Zip the app for submission (notarytool accepts a zip)
ditto -c -k --sequesterRsrc --keepParent \
"$APP_PATH" \
"$RUNNER_TEMP/TableTool-notarize.zip"

# Submit and wait for Apple's notarization service
xcrun notarytool submit "$RUNNER_TEMP/TableTool-notarize.zip" \
--apple-id "$NOTARIZATION_APPLE_ID" \
--password "$NOTARIZATION_PASSWORD" \
--team-id "$APPLE_TEAM_ID" \
--wait

# Attach the notarization ticket to the app bundle
xcrun stapler staple "$APP_PATH"

# ── Package the final artifact ────────────────────────────────────────
- name: Package signed app
run: |
ditto -c -k --sequesterRsrc --keepParent \
"$RUNNER_TEMP/export/Table Tool.app" \
"$RUNNER_TEMP/TableTool.zip"

# ── GitHub Release ────────────────────────────────────────────────────
- name: Create GitHub Release
uses: softprops/action-gh-release@v2
with:
files: ${{ runner.temp }}/TableTool.zip
generate_release_notes: true
14 changes: 14 additions & 0 deletions ExportOptions.plist
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
<?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>developer-id</string>
<key>teamID</key>
<string>$(APPLE_TEAM_ID)</string>
<key>signingStyle</key>
<string>manual</string>
<key>signingCertificate</key>
<string>Developer ID Application</string>
</dict>
</plist>