Skip to content

Build and Notarize SwiftFormat for Xcode #42

Build and Notarize SwiftFormat for Xcode

Build and Notarize SwiftFormat for Xcode #42

Workflow file for this run

name: Build and Notarize SwiftFormat for Xcode
on:
release:
types: [published]
workflow_dispatch:
inputs:
tag:
description: 'Release tag (e.g. v1.2.3)'
required: true
jobs:
build-and-notarize:
name: Build and Notarize SwiftFormat for Xcode
runs-on: macos-15
steps:
- name: Select Xcode version
uses: maxim-lobanov/setup-xcode@v1
with:
xcode-version: '16.3'
- name: Checkout
uses: actions/checkout@v5
with:
ref: ${{ github.event.release.tag_name || inputs.tag }}
- name: Import Code Signing Certificate
run: |
# Debug: Check if secrets exist (without revealing them)
if [ -z "${{ secrets.DEVELOPER_ID_CERTIFICATE_BASE64 }}" ]; then
echo "ERROR: DEVELOPER_ID_CERTIFICATE_BASE64 secret is empty or not set"
exit 1
fi
if [ -z "${{ secrets.DEVELOPER_ID_CERTIFICATE_PASSWORD }}" ]; then
echo "ERROR: DEVELOPER_ID_CERTIFICATE_PASSWORD secret is empty or not set"
exit 1
fi
echo "Secrets are present, proceeding with certificate import..."
# Create keychain
security create-keychain -p "" build.keychain
security default-keychain -s build.keychain
security unlock-keychain -p "" build.keychain
security set-keychain-settings -t 3600 -l build.keychain
# Import certificate
echo "${{ secrets.DEVELOPER_ID_CERTIFICATE_BASE64 }}" | base64 --decode > certificate.p12
security import certificate.p12 -k build.keychain -P "${{ secrets.DEVELOPER_ID_CERTIFICATE_PASSWORD }}" -T /usr/bin/codesign
security set-key-partition-list -S apple-tool:,apple: -s -k "" build.keychain
# Clean up
rm certificate.p12
- name: Archive SwiftFormat for Xcode App
run: |
ARCHIVE_PATH="build/SwiftFormatForXcode.xcarchive"
xcodebuild \
-project SwiftFormat.xcodeproj \
-scheme "SwiftFormat for Xcode" \
-configuration Release \
-archivePath "$ARCHIVE_PATH" \
CODE_SIGN_STYLE=Manual \
CODE_SIGN_IDENTITY="Developer ID Application" \
DEVELOPMENT_TEAM="8VQKF583ED" \
PROVISIONING_PROFILE_SPECIFIER="" \
archive
- name: Copy and Sign App from Archive
id: export-app
run: |
ARCHIVE_PATH="build/SwiftFormatForXcode.xcarchive"
EXPORT_PATH="build/Export"
mkdir -p "$EXPORT_PATH"
# Copy app from archive
cp -R "$ARCHIVE_PATH/Products/Applications/SwiftFormat for Xcode.app" "$EXPORT_PATH/"
APP_PATH="$EXPORT_PATH/SwiftFormat for Xcode.app"
echo "app-path=$APP_PATH" >> $GITHUB_OUTPUT
echo "Copied app to: $APP_PATH"
# Use app from archive as-is without additional signing
echo "Using app from archive without modification"
# Check and fix XcodeKit.framework structure
XCODE_KIT_PATH="$APP_PATH/Contents/PlugIns/SwiftFormat.appex/Contents/Frameworks/XcodeKit.framework"
echo "Checking XcodeKit.framework structure..."
ls -la "$XCODE_KIT_PATH/Versions/"
file "$XCODE_KIT_PATH/Versions/Current"
# Validate the signature from archive
echo "Validating archive signature..."
if ! codesign --verify --deep --strict "$APP_PATH"; then
echo "ERROR: Archive produced invalid signature!"
exit 1
fi
echo "Archive signature is valid"
# Check Gatekeeper assessment
echo "Checking Gatekeeper assessment..."
if ! spctl -a -v "$APP_PATH"; then
echo "ERROR: Gatekeeper would reject this app!"
exit 1
fi
echo "Gatekeeper assessment passed"
- name: Notarize App
uses: lando/notarize-action@v2
with:
product-path: ${{ steps.export-app.outputs.app-path }}
appstore-connect-username: ${{ secrets.NOTARIZATION_USERNAME }}
appstore-connect-password: ${{ secrets.NOTARIZATION_PASSWORD }}
appstore-connect-team-id: 8VQKF583ED
primary-bundle-id: com.nicklockwood.SwiftFormat-for-Xcode
verbose: true
- name: Staple Notarization
id: staple
run: |
xcrun stapler staple "${{ steps.export-app.outputs.app-path }}"
- name: Get Notarization Logs
if: failure()
run: |
echo "Getting notarization history and logs..."
# Show recent history
echo "=== Recent Notarization History ==="
xcrun notarytool history --team-id 8VQKF583ED --apple-id "${{ secrets.NOTARIZATION_USERNAME }}" --password "${{ secrets.NOTARIZATION_PASSWORD }}"
# Get the most recent request UUID and its detailed logs
echo ""
echo "=== Getting detailed logs for most recent submission ==="
REQUEST_UUID=$(xcrun notarytool history --team-id 8VQKF583ED --apple-id "${{ secrets.NOTARIZATION_USERNAME }}" --password "${{ secrets.NOTARIZATION_PASSWORD }}" | grep -E "id:\s*[a-f0-9-]{36}" | head -1 | sed 's/.*id: //')
if [ -n "$REQUEST_UUID" ]; then
echo "Getting logs for request: $REQUEST_UUID"
xcrun notarytool log "$REQUEST_UUID" --team-id 8VQKF583ED --apple-id "${{ secrets.NOTARIZATION_USERNAME }}" --password "${{ secrets.NOTARIZATION_PASSWORD }}"
else
echo "Could not extract request UUID from history"
fi
- name: Zip App
run: |
cd "$(dirname "${{ steps.export-app.outputs.app-path }}")"
zip -r --symlinks SwiftFormat.for.Xcode.app.zip "SwiftFormat for Xcode.app"
echo "ZIP_DIR=$(pwd)" >> $GITHUB_ENV
- name: Upload App Artifact
uses: actions/upload-artifact@v4
with:
name: SwiftFormat-for-Xcode
path: ${{ env.ZIP_DIR }}/SwiftFormat.for.Xcode.app.zip
upload:
name: Upload release artifacts
runs-on: ubuntu-latest
needs: [build-and-notarize]
steps:
- name: Checkout the repository
uses: actions/checkout@v5
with:
ref: ${{ github.event.release.tag_name || inputs.tag }}
- uses: actions/download-artifact@v5
with:
path: downloaded_artifacts
pattern: SwiftFormat-for-Xcode
- name: Display structure of downloaded files
run: ls -R downloaded_artifacts
- name: Get Release ID
id: get-release-id
run: |
if [ -n "${{ github.event.release.id }}" ]; then
echo "release-id=${{ github.event.release.id }}" >> $GITHUB_OUTPUT
else
TAG="${{ inputs.tag }}"
RELEASE_ID=$(gh api repos/${{ github.repository }}/releases/tags/$TAG --jq '.id')
echo "release-id=$RELEASE_ID" >> $GITHUB_OUTPUT
fi
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: Upload release assets
uses: skx/github-action-publish-binaries@master
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
releaseId: ${{ steps.get-release-id.outputs.release-id }}
args: 'downloaded_artifacts/SwiftFormat.for.Xcode.app.zip'