swift-apple-intelligence-grpc/docs/pipeline-configuration.md
Mathias Beaulieu-Duncan 8e53dee03c 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>
2025-12-30 04:35:36 -05:00

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:

  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)
  • 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

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:

  1. Go to appleid.apple.com
  2. Sign in with your Apple ID
  3. Go to Sign-In and SecurityApp-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 → SettingsActionsSecrets

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:

  1. Go to your Gitea repository
  2. Click ReleasesNew 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:

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

- 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_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