diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml new file mode 100644 index 0000000..30e7de5 --- /dev/null +++ b/.github/workflows/release.yml @@ -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 diff --git a/ExportOptions.plist b/ExportOptions.plist new file mode 100644 index 0000000..bf58bd5 --- /dev/null +++ b/ExportOptions.plist @@ -0,0 +1,14 @@ + + + + + method + developer-id + teamID + $(APPLE_TEAM_ID) + signingStyle + manual + signingCertificate + Developer ID Application + +