github-actions/codesign-macos/action.yml
2026-03-31 13:45:04 +02:00

204 lines
7.1 KiB
YAML

name: 'Codesign, package and notarize macOS app'
description: 'Signs and notarizes macOS application with DMG creation'
inputs:
app-name:
description: 'Application name'
required: true
certificate:
description: 'Base64-encoded .p12 certificate file'
required: true
certificate-password:
description: 'Password for the .p12 certificate'
required: true
apple-id:
description: 'Apple ID for notarization'
required: true
team-id:
description: 'Apple Team ID'
required: true
notarization-password:
description: 'App-specific password for notarization'
required: true
identity:
description: 'Signing identity name'
required: false
default: 'Developer ID Application: Craig Raw (UPLVMSK9D7)'
runs:
using: "composite"
steps:
- name: Extract version from build.gradle
id: version
shell: bash
run: |
VERSION=$(grep "^version = " build.gradle | sed "s/version = '\(.*\)'/\1/")
echo "VERSION=$VERSION" >> $GITHUB_OUTPUT
echo "Extracted version: $VERSION"
- name: Set architecture variables
id: arch
shell: bash
run: |
if [[ "${{ runner.arch }}" == "X64" ]]; then
echo "ARCH=x86_64" >> $GITHUB_OUTPUT
echo "RESOURCE_ARCH=x64" >> $GITHUB_OUTPUT
else
echo "ARCH=aarch64" >> $GITHUB_OUTPUT
echo "RESOURCE_ARCH=aarch64" >> $GITHUB_OUTPUT
fi
- name: Install create-dmg
shell: bash
run: brew install create-dmg
- name: Setup signing keychain
shell: bash
env:
MACOS_CERTIFICATE: ${{ inputs.certificate }}
MACOS_CERTIFICATE_PASSWORD: ${{ inputs.certificate-password }}
run: |
# Create temporary keychain
KEYCHAIN_PATH=$RUNNER_TEMP/app-signing.keychain-db
KEYCHAIN_PASSWORD=$(openssl rand -base64 32)
# Create and unlock keychain
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
echo "$MACOS_CERTIFICATE" | base64 --decode > certificate.p12
security import certificate.p12 -k "$KEYCHAIN_PATH" -P "$MACOS_CERTIFICATE_PASSWORD" -T /usr/bin/codesign
rm certificate.p12
# Set keychain search list
security list-keychain -d user -s "$KEYCHAIN_PATH"
# Allow codesign to use the keychain
security set-key-partition-list -S apple-tool:,apple:,codesign: -s -k "$KEYCHAIN_PASSWORD" "$KEYCHAIN_PATH"
# Save keychain path for cleanup
echo "KEYCHAIN_PATH=$KEYCHAIN_PATH" >> $GITHUB_ENV
- name: Get SignPackage commit hash
id: signpackage-version
shell: bash
run: |
COMMIT_HASH=$(git ls-remote https://github.com/sparrowwallet/signpackage.git HEAD | awk '{print $1}')
echo "HASH=$COMMIT_HASH" >> $GITHUB_OUTPUT
echo "SignPackage HEAD commit: $COMMIT_HASH"
- name: Cache SignPackage.jar
id: cache-signpackage
uses: actions/cache@v5
with:
path: $HOME/signpackage-cache
key: signpackage-${{ steps.signpackage-version.outputs.HASH }}
- name: Build SignPackage.jar
if: steps.cache-signpackage.outputs.cache-hit != 'true'
shell: bash
run: |
cd $RUNNER_TEMP
git clone https://github.com/sparrowwallet/signpackage.git
cd signpackage
# Use package instead of compile to create project artifact first
mvn clean package assembly:single
cp target/SignPackage-1.0-jar-with-dependencies.jar SignPackage.jar
mkdir -p $HOME/signpackage-cache
cp SignPackage.jar $HOME/signpackage-cache/
- name: Set SignPackage.jar path
shell: bash
run: |
echo "SIGNPACKAGE_JAR=$HOME/signpackage-cache/SignPackage.jar" >> $GITHUB_ENV
- name: Sign app contents with SignPackage
shell: bash
run: |
java -jar "$SIGNPACKAGE_JAR" \
-d build/jpackage/${{ inputs.app-name }}.app \
-t -r \
-k "${{ inputs.identity }}" \
-e src/main/deploy/package/macos/${{ inputs.app-name }}.entitlements
- name: Codesign MacOS executables
shell: bash
run: |
codesign --timestamp \
--entitlements src/main/deploy/package/macos/${{ inputs.app-name }}.entitlements \
--options runtime \
-vvv -f \
--sign "${{ inputs.identity }}" \
build/jpackage/${{ inputs.app-name }}.app/Contents/MacOS/*
- name: Codesign app bundle
shell: bash
run: |
codesign --timestamp \
--entitlements src/main/deploy/package/macos/${{ inputs.app-name }}.entitlements \
--options runtime \
-vvv -f \
--sign "${{ inputs.identity }}" \
build/jpackage/${{ inputs.app-name }}.app
- name: Create DMG
shell: bash
run: |
APP_NAME_LOWER=$(echo "${{ inputs.app-name }}" | tr '[:upper:]' '[:lower:]')
create-dmg \
--volname "${{ inputs.app-name }}" \
--volicon "src/main/deploy/package/macos/${APP_NAME_LOWER}.icns" \
--background "src/main/deploy/package/macos/installer-background.png" \
--window-pos 200 140 \
--window-size 520 300 \
--icon-size 100 \
--icon "${{ inputs.app-name }}.app" 120 120 \
--hide-extension "${{ inputs.app-name }}.app" \
--app-drop-link 400 120 \
"build/jpackage/${{ inputs.app-name }}-${{ steps.version.outputs.VERSION }}-${{ steps.arch.outputs.ARCH }}.dmg" \
"build/jpackage/"
- name: Codesign DMG
shell: bash
run: |
codesign --timestamp \
--entitlements src/main/deploy/package/macos/${{ inputs.app-name }}.entitlements \
--options runtime \
-vvv -f \
--sign "${{ inputs.identity }}" \
"build/jpackage/${{ inputs.app-name }}-${{ steps.version.outputs.VERSION }}-${{ steps.arch.outputs.ARCH }}.dmg"
- name: Notarize DMG
shell: bash
env:
APPLE_ID: ${{ inputs.apple-id }}
TEAM_ID: ${{ inputs.team-id }}
PASSWORD: ${{ inputs.notarization-password }}
run: |
xcrun notarytool submit --wait \
--apple-id "$APPLE_ID" \
--team-id "$TEAM_ID" \
--password "$PASSWORD" \
"build/jpackage/${{ inputs.app-name }}-${{ steps.version.outputs.VERSION }}-${{ steps.arch.outputs.ARCH }}.dmg"
- name: Staple notarization ticket
shell: bash
run: |
xcrun stapler staple -v "build/jpackage/${{ inputs.app-name }}-${{ steps.version.outputs.VERSION }}-${{ steps.arch.outputs.ARCH }}.dmg"
- name: Verify signature
shell: bash
run: |
spctl --assess --type install --context context:primary-signature -v \
"build/jpackage/${{ inputs.app-name }}-${{ steps.version.outputs.VERSION }}-${{ steps.arch.outputs.ARCH }}.dmg"
- name: Cleanup keychain
if: always()
shell: bash
run: |
if [ -n "$KEYCHAIN_PATH" ]; then
security delete-keychain "$KEYCHAIN_PATH" || true
fi