- Add Gitea Actions workflow for automated releases - Builds release binary - Signs app with Developer ID - Creates and signs DMG - Notarizes with Apple - Uploads to release - Add documentation: - macos-runner-setup.md: Self-hosted runner setup guide - pipeline-configuration.md: Secrets and pipeline config guide 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
7.2 KiB
CI/CD Pipeline Configuration Guide
This guide explains how to configure the Gitea Actions pipeline for automated building, signing, and notarizing the Apple Intelligence Server app.
Overview
The pipeline automatically:
- Builds the app when a release is created
- Creates a signed
.appbundle - Packages it into a DMG
- Signs and notarizes the DMG with Apple
- Uploads the DMG to the release
Prerequisites
- A macOS self-hosted runner (see macos-runner-setup.md)
- Apple Developer Program membership ($99/year)
- Developer ID Application certificate
Step 1: Export Your Signing Certificate
Using Keychain Access (GUI)
- Open Keychain Access
- Find "Developer ID Application: Your Name"
- Right-click → Export
- Save as
.p12file - Set a strong password
Using Command Line
security find-identity -v -p codesigning | grep "Developer ID"
# Note the certificate name
security export -k ~/Library/Keychains/login.keychain-db \
-t identities \
-f pkcs12 \
-P "YOUR_SECURE_PASSWORD" \
-o ~/Desktop/developer-id.p12
Convert to Base64
base64 -i ~/Desktop/developer-id.p12 | tr -d '\n' > ~/Desktop/certificate-base64.txt
Copy the contents of certificate-base64.txt for the next step.
Step 2: Create an App-Specific Password
Apple requires an app-specific password for notarization:
- Go to appleid.apple.com
- Sign in with your Apple ID
- Go to Sign-In and Security → App-Specific Passwords
- Click Generate an app-specific password
- Name it "Gitea CI" or similar
- Copy the generated password (format:
xxxx-xxxx-xxxx-xxxx)
Step 3: Configure Gitea Secrets
Go to your Gitea repository → Settings → Actions → Secrets
Add the following secrets:
| Secret Name | Description | Example |
|---|---|---|
APPLE_CERTIFICATE_BASE64 |
Base64-encoded .p12 certificate | MIIKYgIBAzCCCh... |
APPLE_CERTIFICATE_PASSWORD |
Password for the .p12 file | your-p12-password |
APPLE_ID |
Your Apple ID email | you@example.com |
APPLE_APP_PASSWORD |
App-specific password | xxxx-xxxx-xxxx-xxxx |
APPLE_TEAM_ID |
Your Apple Developer Team ID | LD76P8L42W |
Finding Your Team ID
Your Team ID is shown in:
- Apple Developer portal → Membership → Team ID
- Or in your certificate name: "Developer ID Application: Name (TEAM_ID)"
Step 4: Workflow File
The workflow file is located at .gitea/workflows/release.yml.
Workflow Triggers
The workflow triggers when a release is created:
on:
release:
types: [created]
Key Steps Explained
Install Certificate
Creates a temporary keychain and imports the signing certificate:
- name: Install Certificate
env:
APPLE_CERTIFICATE_BASE64: ${{ secrets.APPLE_CERTIFICATE_BASE64 }}
APPLE_CERTIFICATE_PASSWORD: ${{ secrets.APPLE_CERTIFICATE_PASSWORD }}
run: |
# Creates temporary keychain
# Imports certificate
# Configures codesign access
Build Release Binary
Compiles the Swift project in release mode:
- name: Build Release Binary
run: swift build -c release --product AppleIntelligenceApp
Sign App
Signs the app bundle with hardened runtime:
- name: Sign App
run: |
codesign --deep --force --verify --verbose \
--options runtime \
--sign "Developer ID Application: Your Name (TEAM_ID)" \
"dist/Apple Intelligence Server.app"
The --options runtime flag enables hardened runtime, required for notarization.
Notarize DMG
Submits the DMG to Apple for notarization:
- name: Notarize DMG
run: |
xcrun notarytool submit "dist/AppleIntelligenceServer-$VERSION.dmg" \
--apple-id "$APPLE_ID" \
--password "$APPLE_APP_PASSWORD" \
--team-id "$APPLE_TEAM_ID" \
--wait
The --wait flag makes the command wait until notarization completes.
Staple DMG
Attaches the notarization ticket to the DMG:
- name: Staple DMG
run: xcrun stapler staple "dist/AppleIntelligenceServer-$VERSION.dmg"
Step 5: Creating a Release
To trigger the pipeline:
- Go to your Gitea repository
- Click Releases → New Release
- Create a tag (e.g.,
v1.0.0) - Fill in release title and description
- Click Publish Release
The pipeline will automatically:
- Build the app
- Sign and notarize
- Upload the DMG to the release
Version Numbering
The workflow extracts the version from the Git tag:
VERSION="${GITHUB_REF_NAME#v}" # v1.0.0 → 1.0.0
Tag your releases as v1.0.0, v1.1.0, etc.
Monitoring Pipeline Runs
- Go to your Gitea repository
- Click Actions
- View the running or completed workflows
- Click on a run to see detailed logs
Troubleshooting
Certificate Import Fails
Error: security: SecKeychainItemImport: The specified item already exists in the keychain
Solution: The temporary keychain may not be cleaning up. Check the workflow logs.
Notarization Fails
Error: Error: Unable to upload your app for notarization
Solutions:
- Verify Apple ID credentials are correct
- Ensure app-specific password is valid
- Check that the app is properly signed with hardened runtime
Notarization Rejected
Error: Package Invalid
Solutions:
- Run
xcrun notarytool log <submission-id>to see details - Common issues:
- Missing hardened runtime (
--options runtime) - Unsigned nested code
- Invalid entitlements
- Missing hardened runtime (
Code Signing Fails
Error: No identity found
Solutions:
- Verify certificate is correctly base64-encoded
- Check certificate password is correct
- Ensure certificate hasn't expired
Security Best Practices
- Rotate Secrets Regularly: Update your app-specific password periodically
- Certificate Expiration: Developer ID certificates expire after 5 years
- Limit Access: Only admins should have access to repository secrets
- Audit Logs: Monitor pipeline runs for unauthorized activity
Customizing the Pipeline
Adding Tests
- name: Run Tests
run: swift test
Building for Multiple Architectures
- name: Build Universal Binary
run: |
swift build -c release --arch arm64 --arch x86_64
Custom DMG Background
Add a background image to make the DMG look professional:
- name: Create DMG with Background
run: |
# Use create-dmg tool for custom styling
brew install create-dmg
create-dmg \
--volname "Apple Intelligence Server" \
--background "assets/dmg-background.png" \
--window-pos 200 120 \
--window-size 600 400 \
--icon-size 100 \
--icon "Apple Intelligence Server.app" 150 190 \
--app-drop-link 450 185 \
"dist/AppleIntelligenceServer-$VERSION.dmg" \
"dist/Apple Intelligence Server.app"
Quick Reference
Secrets Checklist
APPLE_CERTIFICATE_BASE64APPLE_CERTIFICATE_PASSWORDAPPLE_IDAPPLE_APP_PASSWORDAPPLE_TEAM_ID
Release Checklist
- Code is tested and ready
- Version number updated (if applicable)
- Create Git tag:
git tag v1.0.0 - Push tag:
git push origin v1.0.0 - Create release on Gitea
- Monitor pipeline completion
- Verify DMG is uploaded to release