Skip to content

add placeholder generated version #93

add placeholder generated version

add placeholder generated version #93

Workflow file for this run

name: Build and Sign
on:
push:
branches:
- master
tags:
- 'v*'
permissions:
contents: write
jobs:
build-sign:
runs-on: macos-14
steps:
- name: Checkout
uses: actions/checkout@v4
with:
submodules: recursive
fetch-tags: true
- name: Select Xcode
uses: maxim-lobanov/setup-xcode@v1
with:
xcode-version: "16.1"
- name: Cache DerivedData
uses: actions/cache@v4
with:
path: |
~/Library/Developer/Xcode/DerivedData
~/.swiftpm
key: ${{ runner.os }}-spm-${{ hashFiles('**/Package.resolved') }}
- name: Cache BoringSSL build
uses: actions/cache@v4
with:
path: pkcs11/vendor/boringssl/build
key: ${{ runner.os }}-boringssl-${{ hashFiles('pkcs11/vendor/boringssl/CMakeLists.txt', 'pkcs11/vendor/boringssl/crypto/**') }}
- name: Install certificate and provisioning profile
env:
MACOS_CERTIFICATE: ${{ secrets.MACOS_CERTIFICATE }}
MACOS_CERTIFICATE_PASSWORD: ${{ secrets.MACOS_CERTIFICATE_PASSWORD }}
PROVISIONING_PROFILE_BASE64: ${{ secrets.PROVISIONING_PROFILE_BASE64 }}
INSTALLER_CERTIFICATE: ${{ secrets.INSTALLER_CERTIFICATE }}
INSTALLER_CERTIFICATE_PASSWORD: ${{ secrets.INSTALLER_CERTIFICATE_PASSWORD }}
run: |
# Install Developer ID Application certificate
echo "$MACOS_CERTIFICATE" | base64 --decode > signing.p12
security create-keychain -p "" build.keychain
security import signing.p12 -k build.keychain -P "$MACOS_CERTIFICATE_PASSWORD" -T /usr/bin/codesign -T /usr/bin/productsign
rm signing.p12
# Install Developer ID Installer certificate (for pkg signing)
if [ -n "$INSTALLER_CERTIFICATE" ]; then
echo "$INSTALLER_CERTIFICATE" | base64 --decode > installer.p12
security import installer.p12 -k build.keychain -P "$INSTALLER_CERTIFICATE_PASSWORD" -T /usr/bin/productsign
rm installer.p12
fi
security set-key-partition-list -S apple-tool:,apple: -s -k "" build.keychain
security default-keychain -s build.keychain
security unlock-keychain -p "" build.keychain
# Install provisioning profile
mkdir -p ~/Library/MobileDevice/Provisioning\ Profiles
echo "$PROVISIONING_PROFILE_BASE64" | base64 --decode > ~/Library/MobileDevice/Provisioning\ Profiles/nailed.provisionprofile
# Also save for embedding
echo "$PROVISIONING_PROFILE_BASE64" | base64 --decode > build_profile.provisionprofile
- name: Build PKCS#11 dylib
run: |
cd pkcs11
make clean || true
make -j$(sysctl -n hw.ncpu)
echo "Built dylib:"
ls -la libnailed_pkcs11.dylib
file libnailed_pkcs11.dylib
- name: Sign PKCS#11 dylib
env:
CODE_SIGN_IDENTITY: ${{ vars.CODE_SIGN_IDENTITY }}
run: |
DYLIB="pkcs11/libnailed_pkcs11.dylib"
# Sign the dylib with hardened runtime
codesign --force --options runtime --timestamp \
--sign "$CODE_SIGN_IDENTITY" \
"$DYLIB"
# Verify signature
codesign --verify --verbose=2 "$DYLIB"
echo "Dylib signature info:"
codesign -dvv "$DYLIB"
- name: Build app (Release, unsigned)
run: |
set -e
xcodebuild \
-project nailed.xcodeproj \
-scheme nailed \
-configuration Release \
-archivePath $PWD/build/nailed.xcarchive \
CODE_SIGN_IDENTITY="-" \
CODE_SIGNING_REQUIRED=NO \
CODE_SIGNING_ALLOWED=NO \
clean archive | xcpretty && exit ${PIPESTATUS[0]}
- name: Export and sign .app
env:
CODE_SIGN_IDENTITY: ${{ vars.CODE_SIGN_IDENTITY }}
DEVELOPMENT_TEAM: ${{ vars.DEVELOPMENT_TEAM }}
run: |
mkdir -p build/export
ditto build/nailed.xcarchive/Products/Applications/nailed.app build/export/nailed.app
APP="build/export/nailed.app"
# Embed provisioning profile
cp build_profile.provisionprofile "$APP/Contents/embedded.provisionprofile"
# Create entitlements with resolved variables
sed "s/\$(AppIdentifierPrefix)/${DEVELOPMENT_TEAM}./g" \
nailed/nailed.entitlements > build/entitlements.plist
echo "Resolved entitlements:"
cat build/entitlements.plist
# Sign nested bundles first (inside-out signing)
find "$APP/Contents/Resources" -name "*.bundle" -type d | while read bundle; do
echo "Signing bundle: $bundle"
codesign --force --options runtime --timestamp \
--sign "$CODE_SIGN_IDENTITY" \
"$bundle"
done
# Sign any frameworks
if [ -d "$APP/Contents/Frameworks" ]; then
find "$APP/Contents/Frameworks" -name "*.framework" -type d | while read framework; do
echo "Signing framework: $framework"
codesign --force --options runtime --timestamp \
--sign "$CODE_SIGN_IDENTITY" \
"$framework"
done
fi
# Sign the main app with entitlements
codesign --force --options runtime --timestamp \
--entitlements build/entitlements.plist \
--sign "$CODE_SIGN_IDENTITY" \
"$APP"
# Verify signature
codesign --verify --verbose=2 "$APP"
# Show signature info
echo "Signature info:"
codesign -dvv "$APP"
# Show embedded entitlements
echo "Embedded entitlements:"
codesign -d --entitlements - "$APP" 2>/dev/null || true
echo "Signature verification passed"
- name: Create installer package
env:
INSTALLER_SIGN_IDENTITY: ${{ vars.INSTALLER_SIGN_IDENTITY }}
run: |
chmod +x scripts/build-pkg.sh
# Extract version from tag or use default
if [[ "$GITHUB_REF" == refs/tags/v* ]]; then
VERSION="${GITHUB_REF#refs/tags/v}"
else
VERSION="0.0.0-dev"
fi
scripts/build-pkg.sh \
"build/export/nailed.app" \
"pkcs11/libnailed_pkcs11.dylib" \
"build/nailed.pkg" \
"$INSTALLER_SIGN_IDENTITY" \
"$VERSION"
echo "Package created:"
ls -la build/nailed.pkg
# Verify package signature
if [ -n "$INSTALLER_SIGN_IDENTITY" ]; then
pkgutil --check-signature build/nailed.pkg
fi
- name: Upload PKG artifact
uses: actions/upload-artifact@v4
with:
name: nailed-${{ github.run_id }}
path: build/nailed.pkg
- name: Submit for notarization
if: startsWith(github.ref, 'refs/tags/v')
env:
APPLE_API_KEY_ID: ${{ secrets.APPLE_API_KEY_ID }}
APPLE_API_KEY_ISSUER: ${{ secrets.APPLE_API_KEY_ISSUER }}
APPLE_API_KEY_BASE64: ${{ secrets.APPLE_API_KEY_BASE64 }}
run: |
mkdir -p ~/private_keys
echo "$APPLE_API_KEY_BASE64" | base64 --decode > ~/private_keys/AuthKey_${APPLE_API_KEY_ID}.p8
xcrun notarytool submit build/nailed.pkg \
--key ~/private_keys/AuthKey_${APPLE_API_KEY_ID}.p8 \
--key-id "$APPLE_API_KEY_ID" \
--issuer "$APPLE_API_KEY_ISSUER" \
--output-format json | tee notarization-result.json
SUBMISSION_ID=$(cat notarization-result.json | python3 -c "import sys,json; print(json.load(sys.stdin)['id'])")
echo "Submission ID: $SUBMISSION_ID"
echo "$SUBMISSION_ID" > build/submission-id.txt
- name: Wait for notarization and staple
if: startsWith(github.ref, 'refs/tags/v')
env:
APPLE_API_KEY_ID: ${{ secrets.APPLE_API_KEY_ID }}
APPLE_API_KEY_ISSUER: ${{ secrets.APPLE_API_KEY_ISSUER }}
run: |
SUBMISSION_ID=$(cat build/submission-id.txt)
echo "Waiting for notarization (up to 2 minutes)..."
# Wait for notarization to complete (timeout after 2 minutes)
if xcrun notarytool wait "$SUBMISSION_ID" \
--key ~/private_keys/AuthKey_${APPLE_API_KEY_ID}.p8 \
--key-id "$APPLE_API_KEY_ID" \
--issuer "$APPLE_API_KEY_ISSUER" \
--timeout 2m; then
echo "Notarization completed, stapling..."
xcrun stapler staple build/nailed.pkg
xcrun stapler validate build/nailed.pkg
echo "STAPLED=true" >> $GITHUB_ENV
else
echo "Notarization not completed within 2 minutes, use staple.yml workflow later"
echo "STAPLED=false" >> $GITHUB_ENV
fi
rm -rf ~/private_keys
- name: Upload submission ID
if: startsWith(github.ref, 'refs/tags/v') && env.STAPLED == 'false'
uses: actions/upload-artifact@v4
with:
name: notarization-${{ github.run_id }}
path: build/submission-id.txt
- name: Upload stapled PKG
if: startsWith(github.ref, 'refs/tags/v') && env.STAPLED == 'true'
uses: actions/upload-artifact@v4
with:
name: nailed-${{ github.run_id }}-stapled
path: build/nailed.pkg
- name: Create release
if: startsWith(github.ref, 'refs/tags/v') && env.STAPLED == 'true'
env:
GH_TOKEN: ${{ github.token }}
run: |
VERSION="${GITHUB_REF#refs/tags/}"
# Rename pkg to include version
cp build/nailed.pkg "build/nailed-${VERSION}.pkg"
# Create release with the stapled pkg
gh release create "$VERSION" \
--title "Nailed $VERSION" \
--generate-notes \
"build/nailed-${VERSION}.pkg"