Add CI/CD pipeline and documentation
- 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>
This commit is contained in:
parent
e0bf17da3d
commit
8e53dee03c
150
.gitea/workflows/release.yml
Normal file
150
.gitea/workflows/release.yml
Normal file
@ -0,0 +1,150 @@
|
|||||||
|
name: Build and Release
|
||||||
|
|
||||||
|
on:
|
||||||
|
release:
|
||||||
|
types: [created]
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
build:
|
||||||
|
runs-on: macos-latest
|
||||||
|
steps:
|
||||||
|
- name: Checkout
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
|
||||||
|
- name: Setup Swift
|
||||||
|
uses: swift-actions/setup-swift@v2
|
||||||
|
with:
|
||||||
|
swift-version: "6.0"
|
||||||
|
|
||||||
|
- name: Install Certificate
|
||||||
|
env:
|
||||||
|
APPLE_CERTIFICATE_BASE64: ${{ secrets.APPLE_CERTIFICATE_BASE64 }}
|
||||||
|
APPLE_CERTIFICATE_PASSWORD: ${{ secrets.APPLE_CERTIFICATE_PASSWORD }}
|
||||||
|
run: |
|
||||||
|
# Create temporary keychain
|
||||||
|
KEYCHAIN_PATH=$RUNNER_TEMP/app-signing.keychain-db
|
||||||
|
KEYCHAIN_PASSWORD=$(openssl rand -base64 32)
|
||||||
|
|
||||||
|
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 certificate
|
||||||
|
CERTIFICATE_PATH=$RUNNER_TEMP/certificate.p12
|
||||||
|
echo "$APPLE_CERTIFICATE_BASE64" | base64 --decode > "$CERTIFICATE_PATH"
|
||||||
|
|
||||||
|
security import "$CERTIFICATE_PATH" \
|
||||||
|
-P "$APPLE_CERTIFICATE_PASSWORD" \
|
||||||
|
-A -t cert -f pkcs12 \
|
||||||
|
-k "$KEYCHAIN_PATH"
|
||||||
|
|
||||||
|
security list-keychain -d user -s "$KEYCHAIN_PATH"
|
||||||
|
|
||||||
|
# Allow codesign to access keychain
|
||||||
|
security set-key-partition-list -S apple-tool:,apple:,codesign: \
|
||||||
|
-s -k "$KEYCHAIN_PASSWORD" "$KEYCHAIN_PATH"
|
||||||
|
|
||||||
|
- name: Build Release Binary
|
||||||
|
run: swift build -c release --product AppleIntelligenceApp
|
||||||
|
|
||||||
|
- name: Create App Bundle
|
||||||
|
run: |
|
||||||
|
APP_NAME="Apple Intelligence Server"
|
||||||
|
VERSION="${GITHUB_REF_NAME#v}"
|
||||||
|
|
||||||
|
mkdir -p "dist/$APP_NAME.app/Contents/MacOS"
|
||||||
|
mkdir -p "dist/$APP_NAME.app/Contents/Resources"
|
||||||
|
|
||||||
|
cp .build/release/AppleIntelligenceApp "dist/$APP_NAME.app/Contents/MacOS/$APP_NAME"
|
||||||
|
|
||||||
|
cat > "dist/$APP_NAME.app/Contents/Info.plist" << EOF
|
||||||
|
<?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>CFBundleExecutable</key>
|
||||||
|
<string>$APP_NAME</string>
|
||||||
|
<key>CFBundleIdentifier</key>
|
||||||
|
<string>com.svrnty.apple-intelligence-server</string>
|
||||||
|
<key>CFBundleName</key>
|
||||||
|
<string>$APP_NAME</string>
|
||||||
|
<key>CFBundlePackageType</key>
|
||||||
|
<string>APPL</string>
|
||||||
|
<key>CFBundleShortVersionString</key>
|
||||||
|
<string>$VERSION</string>
|
||||||
|
<key>CFBundleVersion</key>
|
||||||
|
<string>1</string>
|
||||||
|
<key>LSMinimumSystemVersion</key>
|
||||||
|
<string>26.0</string>
|
||||||
|
<key>LSUIElement</key>
|
||||||
|
<true/>
|
||||||
|
<key>NSHighResolutionCapable</key>
|
||||||
|
<true/>
|
||||||
|
<key>NSLocalNetworkUsageDescription</key>
|
||||||
|
<string>Apple Intelligence Server needs local network access to accept connections from other devices.</string>
|
||||||
|
<key>NSPrincipalClass</key>
|
||||||
|
<string>NSApplication</string>
|
||||||
|
</dict>
|
||||||
|
</plist>
|
||||||
|
EOF
|
||||||
|
|
||||||
|
echo -n "APPL????" > "dist/$APP_NAME.app/Contents/PkgInfo"
|
||||||
|
|
||||||
|
- name: Sign App
|
||||||
|
run: |
|
||||||
|
codesign --deep --force --verify --verbose \
|
||||||
|
--options runtime \
|
||||||
|
--sign "Developer ID Application: Mathias Beaulieu-Duncan (LD76P8L42W)" \
|
||||||
|
"dist/Apple Intelligence Server.app"
|
||||||
|
|
||||||
|
- name: Create DMG
|
||||||
|
run: |
|
||||||
|
VERSION="${GITHUB_REF_NAME#v}"
|
||||||
|
|
||||||
|
mkdir -p dist/dmg-temp
|
||||||
|
cp -R "dist/Apple Intelligence Server.app" dist/dmg-temp/
|
||||||
|
ln -s /Applications dist/dmg-temp/Applications
|
||||||
|
|
||||||
|
hdiutil create -volname "Apple Intelligence Server" \
|
||||||
|
-srcfolder dist/dmg-temp \
|
||||||
|
-ov -format UDRW dist/temp.dmg
|
||||||
|
|
||||||
|
hdiutil convert dist/temp.dmg -format UDZO \
|
||||||
|
-o "dist/AppleIntelligenceServer-$VERSION.dmg"
|
||||||
|
|
||||||
|
rm -rf dist/dmg-temp dist/temp.dmg
|
||||||
|
|
||||||
|
- name: Sign DMG
|
||||||
|
run: |
|
||||||
|
VERSION="${GITHUB_REF_NAME#v}"
|
||||||
|
codesign --force \
|
||||||
|
--sign "Developer ID Application: Mathias Beaulieu-Duncan (LD76P8L42W)" \
|
||||||
|
"dist/AppleIntelligenceServer-$VERSION.dmg"
|
||||||
|
|
||||||
|
- name: Notarize DMG
|
||||||
|
env:
|
||||||
|
APPLE_ID: ${{ secrets.APPLE_ID }}
|
||||||
|
APPLE_APP_PASSWORD: ${{ secrets.APPLE_APP_PASSWORD }}
|
||||||
|
APPLE_TEAM_ID: ${{ secrets.APPLE_TEAM_ID }}
|
||||||
|
run: |
|
||||||
|
VERSION="${GITHUB_REF_NAME#v}"
|
||||||
|
|
||||||
|
xcrun notarytool submit "dist/AppleIntelligenceServer-$VERSION.dmg" \
|
||||||
|
--apple-id "$APPLE_ID" \
|
||||||
|
--password "$APPLE_APP_PASSWORD" \
|
||||||
|
--team-id "$APPLE_TEAM_ID" \
|
||||||
|
--wait
|
||||||
|
|
||||||
|
- name: Staple DMG
|
||||||
|
run: |
|
||||||
|
VERSION="${GITHUB_REF_NAME#v}"
|
||||||
|
xcrun stapler staple "dist/AppleIntelligenceServer-$VERSION.dmg"
|
||||||
|
|
||||||
|
- name: Upload to Release
|
||||||
|
env:
|
||||||
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
run: |
|
||||||
|
VERSION="${GITHUB_REF_NAME#v}"
|
||||||
|
gh release upload "$GITHUB_REF_NAME" \
|
||||||
|
"dist/AppleIntelligenceServer-$VERSION.dmg" \
|
||||||
|
--clobber
|
||||||
187
docs/macos-runner-setup.md
Normal file
187
docs/macos-runner-setup.md
Normal file
@ -0,0 +1,187 @@
|
|||||||
|
# Setting Up a macOS Self-Hosted Runner for Gitea Actions
|
||||||
|
|
||||||
|
This guide explains how to set up a self-hosted Gitea Actions runner on macOS for building, signing, and notarizing the Apple Intelligence Server app.
|
||||||
|
|
||||||
|
## Prerequisites
|
||||||
|
|
||||||
|
- A Mac (Intel or Apple Silicon) running macOS 13+
|
||||||
|
- Admin access to your Gitea instance
|
||||||
|
- Xcode Command Line Tools installed
|
||||||
|
|
||||||
|
## Step 1: Install Xcode Command Line Tools
|
||||||
|
|
||||||
|
```bash
|
||||||
|
xcode-select --install
|
||||||
|
```
|
||||||
|
|
||||||
|
## Step 2: Install Swift
|
||||||
|
|
||||||
|
Ensure Swift 6.0+ is installed:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
swift --version
|
||||||
|
```
|
||||||
|
|
||||||
|
If not installed, download from [swift.org](https://swift.org/download/) or install via Xcode.
|
||||||
|
|
||||||
|
## Step 3: Download Gitea Act Runner
|
||||||
|
|
||||||
|
Download the latest release from [gitea/act_runner](https://gitea.com/gitea/act_runner/releases):
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# For Apple Silicon (M1/M2/M3)
|
||||||
|
curl -L -o act_runner https://gitea.com/gitea/act_runner/releases/download/v0.2.11/act_runner-0.2.11-darwin-arm64
|
||||||
|
|
||||||
|
# For Intel Mac
|
||||||
|
curl -L -o act_runner https://gitea.com/gitea/act_runner/releases/download/v0.2.11/act_runner-0.2.11-darwin-amd64
|
||||||
|
|
||||||
|
# Make executable
|
||||||
|
chmod +x act_runner
|
||||||
|
```
|
||||||
|
|
||||||
|
## Step 4: Get Registration Token
|
||||||
|
|
||||||
|
1. Go to your Gitea repository
|
||||||
|
2. Navigate to **Settings** → **Actions** → **Runners**
|
||||||
|
3. Click **Create new Runner**
|
||||||
|
4. Copy the registration token
|
||||||
|
|
||||||
|
## Step 5: Register the Runner
|
||||||
|
|
||||||
|
```bash
|
||||||
|
./act_runner register \
|
||||||
|
--instance https://your-gitea-instance.com \
|
||||||
|
--token YOUR_REGISTRATION_TOKEN \
|
||||||
|
--name "macos-runner" \
|
||||||
|
--labels "macos,macos-latest,self-hosted"
|
||||||
|
```
|
||||||
|
|
||||||
|
When prompted:
|
||||||
|
- **Runner name:** `macos-runner` (or any name you prefer)
|
||||||
|
- **Labels:** `macos,macos-latest,self-hosted`
|
||||||
|
|
||||||
|
This creates a `.runner` configuration file in the current directory.
|
||||||
|
|
||||||
|
## Step 6: Run the Runner
|
||||||
|
|
||||||
|
### Option A: Run in Foreground (for testing)
|
||||||
|
|
||||||
|
```bash
|
||||||
|
./act_runner daemon
|
||||||
|
```
|
||||||
|
|
||||||
|
### Option B: Run as a Background Service (recommended)
|
||||||
|
|
||||||
|
Create a LaunchAgent to run the runner automatically:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
mkdir -p ~/Library/LaunchAgents
|
||||||
|
```
|
||||||
|
|
||||||
|
Create the plist file:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cat > ~/Library/LaunchAgents/com.gitea.act_runner.plist << 'EOF'
|
||||||
|
<?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>Label</key>
|
||||||
|
<string>com.gitea.act_runner</string>
|
||||||
|
<key>ProgramArguments</key>
|
||||||
|
<array>
|
||||||
|
<string>/Users/YOUR_USERNAME/act_runner/act_runner</string>
|
||||||
|
<string>daemon</string>
|
||||||
|
</array>
|
||||||
|
<key>WorkingDirectory</key>
|
||||||
|
<string>/Users/YOUR_USERNAME/act_runner</string>
|
||||||
|
<key>RunAtLoad</key>
|
||||||
|
<true/>
|
||||||
|
<key>KeepAlive</key>
|
||||||
|
<true/>
|
||||||
|
<key>StandardOutPath</key>
|
||||||
|
<string>/Users/YOUR_USERNAME/act_runner/runner.log</string>
|
||||||
|
<key>StandardErrorPath</key>
|
||||||
|
<string>/Users/YOUR_USERNAME/act_runner/runner.error.log</string>
|
||||||
|
</dict>
|
||||||
|
</plist>
|
||||||
|
EOF
|
||||||
|
```
|
||||||
|
|
||||||
|
**Important:** Replace `YOUR_USERNAME` with your actual macOS username.
|
||||||
|
|
||||||
|
Load the service:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
launchctl load ~/Library/LaunchAgents/com.gitea.act_runner.plist
|
||||||
|
```
|
||||||
|
|
||||||
|
## Step 7: Verify Runner is Connected
|
||||||
|
|
||||||
|
1. Go to your Gitea repository
|
||||||
|
2. Navigate to **Settings** → **Actions** → **Runners**
|
||||||
|
3. Your runner should appear as **Online**
|
||||||
|
|
||||||
|
## Managing the Runner
|
||||||
|
|
||||||
|
### Check Status
|
||||||
|
|
||||||
|
```bash
|
||||||
|
launchctl list | grep act_runner
|
||||||
|
```
|
||||||
|
|
||||||
|
### Stop the Runner
|
||||||
|
|
||||||
|
```bash
|
||||||
|
launchctl unload ~/Library/LaunchAgents/com.gitea.act_runner.plist
|
||||||
|
```
|
||||||
|
|
||||||
|
### Start the Runner
|
||||||
|
|
||||||
|
```bash
|
||||||
|
launchctl load ~/Library/LaunchAgents/com.gitea.act_runner.plist
|
||||||
|
```
|
||||||
|
|
||||||
|
### View Logs
|
||||||
|
|
||||||
|
```bash
|
||||||
|
tail -f ~/act_runner/runner.log
|
||||||
|
tail -f ~/act_runner/runner.error.log
|
||||||
|
```
|
||||||
|
|
||||||
|
## Security Considerations
|
||||||
|
|
||||||
|
1. **Dedicated User:** Consider creating a dedicated macOS user for the runner
|
||||||
|
2. **Keychain Access:** The runner needs access to the keychain for code signing
|
||||||
|
3. **Network:** Ensure the Mac has reliable network access to your Gitea instance
|
||||||
|
4. **Updates:** Keep the runner updated to the latest version
|
||||||
|
|
||||||
|
## Troubleshooting
|
||||||
|
|
||||||
|
### Runner Not Connecting
|
||||||
|
|
||||||
|
- Check firewall settings
|
||||||
|
- Verify the Gitea instance URL is correct
|
||||||
|
- Ensure the registration token hasn't expired
|
||||||
|
|
||||||
|
### Code Signing Fails
|
||||||
|
|
||||||
|
- Ensure certificates are installed in the login keychain
|
||||||
|
- Check that the runner has keychain access
|
||||||
|
- Verify certificate names match the workflow
|
||||||
|
|
||||||
|
### Build Fails with Swift Errors
|
||||||
|
|
||||||
|
- Ensure Xcode Command Line Tools are installed
|
||||||
|
- Check Swift version compatibility
|
||||||
|
- Clear the build cache: `swift package clean`
|
||||||
|
|
||||||
|
## Recommended Directory Structure
|
||||||
|
|
||||||
|
```
|
||||||
|
~/act_runner/
|
||||||
|
├── act_runner # The runner binary
|
||||||
|
├── .runner # Runner configuration
|
||||||
|
├── runner.log # Standard output log
|
||||||
|
└── runner.error.log # Error log
|
||||||
|
```
|
||||||
294
docs/pipeline-configuration.md
Normal file
294
docs/pipeline-configuration.md
Normal file
@ -0,0 +1,294 @@
|
|||||||
|
# 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:
|
||||||
|
1. Builds the app when a release is created
|
||||||
|
2. Creates a signed `.app` bundle
|
||||||
|
3. Packages it into a DMG
|
||||||
|
4. Signs and notarizes the DMG with Apple
|
||||||
|
5. Uploads the DMG to the release
|
||||||
|
|
||||||
|
## Prerequisites
|
||||||
|
|
||||||
|
- A macOS self-hosted runner (see [macos-runner-setup.md](./macos-runner-setup.md))
|
||||||
|
- Apple Developer Program membership ($99/year)
|
||||||
|
- Developer ID Application certificate
|
||||||
|
|
||||||
|
## Step 1: Export Your Signing Certificate
|
||||||
|
|
||||||
|
### Using Keychain Access (GUI)
|
||||||
|
|
||||||
|
1. Open **Keychain Access**
|
||||||
|
2. Find "Developer ID Application: Your Name"
|
||||||
|
3. Right-click → **Export**
|
||||||
|
4. Save as `.p12` file
|
||||||
|
5. Set a strong password
|
||||||
|
|
||||||
|
### Using Command Line
|
||||||
|
|
||||||
|
```bash
|
||||||
|
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
|
||||||
|
|
||||||
|
```bash
|
||||||
|
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:
|
||||||
|
|
||||||
|
1. Go to [appleid.apple.com](https://appleid.apple.com/account/manage)
|
||||||
|
2. Sign in with your Apple ID
|
||||||
|
3. Go to **Sign-In and Security** → **App-Specific Passwords**
|
||||||
|
4. Click **Generate an app-specific password**
|
||||||
|
5. Name it "Gitea CI" or similar
|
||||||
|
6. 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:
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
on:
|
||||||
|
release:
|
||||||
|
types: [created]
|
||||||
|
```
|
||||||
|
|
||||||
|
### Key Steps Explained
|
||||||
|
|
||||||
|
#### Install Certificate
|
||||||
|
|
||||||
|
Creates a temporary keychain and imports the signing certificate:
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
- 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:
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
- name: Build Release Binary
|
||||||
|
run: swift build -c release --product AppleIntelligenceApp
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Sign App
|
||||||
|
|
||||||
|
Signs the app bundle with hardened runtime:
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
- 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:
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
- 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:
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
- name: Staple DMG
|
||||||
|
run: xcrun stapler staple "dist/AppleIntelligenceServer-$VERSION.dmg"
|
||||||
|
```
|
||||||
|
|
||||||
|
## Step 5: Creating a Release
|
||||||
|
|
||||||
|
To trigger the pipeline:
|
||||||
|
|
||||||
|
1. Go to your Gitea repository
|
||||||
|
2. Click **Releases** → **New Release**
|
||||||
|
3. Create a tag (e.g., `v1.0.0`)
|
||||||
|
4. Fill in release title and description
|
||||||
|
5. 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:
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
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
|
||||||
|
|
||||||
|
1. Go to your Gitea repository
|
||||||
|
2. Click **Actions**
|
||||||
|
3. View the running or completed workflows
|
||||||
|
4. 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
|
||||||
|
|
||||||
|
### 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
|
||||||
|
|
||||||
|
1. **Rotate Secrets Regularly:** Update your app-specific password periodically
|
||||||
|
2. **Certificate Expiration:** Developer ID certificates expire after 5 years
|
||||||
|
3. **Limit Access:** Only admins should have access to repository secrets
|
||||||
|
4. **Audit Logs:** Monitor pipeline runs for unauthorized activity
|
||||||
|
|
||||||
|
## Customizing the Pipeline
|
||||||
|
|
||||||
|
### Adding Tests
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
- name: Run Tests
|
||||||
|
run: swift test
|
||||||
|
```
|
||||||
|
|
||||||
|
### Building for Multiple Architectures
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
- 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:
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
- 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_BASE64`
|
||||||
|
- [ ] `APPLE_CERTIFICATE_PASSWORD`
|
||||||
|
- [ ] `APPLE_ID`
|
||||||
|
- [ ] `APPLE_APP_PASSWORD`
|
||||||
|
- [ ] `APPLE_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
|
||||||
Loading…
Reference in New Issue
Block a user