Skip to content

macOS Build, Sign, Notarize & Release #21

macOS Build, Sign, Notarize & Release

macOS Build, Sign, Notarize & Release #21

name: macOS Build, Sign, Notarize & Release
on:
release:
types: [published]
workflow_dispatch:
jobs:
build-sign-notarize:
runs-on: macos-26
permissions:
contents: write
steps:
- name: Checkout code
uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Set version info
run: |
VERSION="${{ github.event.release.tag_name }}"
SHORT_VERSION="${VERSION#v}"
BUILD_NUMBER=$(grep 'CURRENT_PROJECT_VERSION' Nook.xcodeproj/project.pbxproj \
| head -n1 | grep -oE '[0-9]+' | tail -n1)
echo "VERSION=$VERSION" >> $GITHUB_ENV
echo "SHORT_VERSION=$SHORT_VERSION" >> $GITHUB_ENV
echo "BUILD_NUMBER=$BUILD_NUMBER" >> $GITHUB_ENV
echo "Building version $SHORT_VERSION (build $BUILD_NUMBER)"
- name: Import Developer ID certificate
env:
APPLE_CERTIFICATE_P12_BASE64: ${{ secrets.APPLE_CERTIFICATE_P12_BASE64 }}
APPLE_CERTIFICATE_PASSWORD: ${{ secrets.APPLE_CERTIFICATE_PASSWORD }}
run: |
printf "%s" "$APPLE_CERTIFICATE_P12_BASE64" | base64 --decode > signing_certificate.p12
security create-keychain -p "" build.keychain
security default-keychain -s build.keychain
security unlock-keychain -p "" build.keychain
security import signing_certificate.p12 \
-k build.keychain \
-P "$APPLE_CERTIFICATE_PASSWORD" \
-T /usr/bin/codesign
security list-keychains -d user -s build.keychain login.keychain
security set-key-partition-list -S apple-tool:,apple:,codesign: -s -k "" build.keychain
IDENTITY=$(security find-identity -v -p codesigning build.keychain \
| grep "Developer ID Application" | head -n1 | awk '{print $2}')
echo "SIGNING_IDENTITY=$IDENTITY" >> $GITHUB_ENV
echo "Using signing identity: $IDENTITY"
- name: Build app
env:
APPLE_TEAM_ID: ${{ secrets.APPLE_TEAM_ID }}
run: |
set -e
mkdir -p build
echo "Attempting universal build (arm64 + x86_64)..."
if ! xcodebuild -scheme Nook -configuration Release \
-arch arm64 -arch x86_64 \
-derivedDataPath build \
CODE_SIGN_IDENTITY="$SIGNING_IDENTITY" \
CODE_SIGN_STYLE=Manual \
DEVELOPMENT_TEAM="$APPLE_TEAM_ID" \
CODE_SIGN_ENTITLEMENTS="Nook/Nook.entitlements" \
PROVISIONING_PROFILE_SPECIFIER=""; then
echo "Universal build failed, retrying Apple Silicon only..."
xcodebuild -scheme Nook -configuration Release \
-arch arm64 \
-derivedDataPath build \
CODE_SIGN_IDENTITY="$SIGNING_IDENTITY" \
CODE_SIGN_STYLE=Manual \
DEVELOPMENT_TEAM="$APPLE_TEAM_ID" \
CODE_SIGN_ENTITLEMENTS="Nook/Nook.entitlements" \
PROVISIONING_PROFILE_SPECIFIER=""
fi
cp -R "build/Build/Products/Release/Nook.app" ./Nook.app
- name: Notarize app
env:
APPLE_ID: ${{ secrets.APPLE_ID }}
APPLE_TEAM_ID: ${{ secrets.APPLE_TEAM_ID }}
APPLE_APP_SPECIFIC_PASSWORD: ${{ secrets.APPLE_APP_SPECIFIC_PASSWORD }}
run: |
xcrun notarytool store-credentials "nook-notary" \
--apple-id "$APPLE_ID" \
--team-id "$APPLE_TEAM_ID" \
--password "$APPLE_APP_SPECIFIC_PASSWORD"
zip -r Nook.zip "Nook.app"
xcrun notarytool submit "Nook.zip" \
--keychain-profile "nook-notary" \
--wait
- name: Staple notarization ticket
run: xcrun stapler staple "Nook.app"
- name: Verify signature
run: |
codesign --verify --deep --strict --verbose=2 "Nook.app"
spctl --assess --type execute --verbose "Nook.app"
- name: Create DMG
run: |
hdiutil create -volname "Nook $SHORT_VERSION" \
-srcfolder "Nook.app" \
-ov -format UDZO "Nook-${VERSION}.dmg"
- name: Upload DMG to release
uses: softprops/action-gh-release@v2
with:
files: Nook-*.dmg
fail_on_unmatched_files: false
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: Update appcast on gh-pages
run: |
DMG_URL="https://github.com/${{ github.repository }}/releases/download/${VERSION}/Nook-${VERSION}.dmg"
DATE=$(date -R)
git fetch origin gh-pages
git checkout gh-pages
ENTRY=$(cat << XMLEOF
<item>
<title>Version ${SHORT_VERSION}</title>
<sparkle:releaseNotesLink>https://github.com/${{ github.repository }}/releases/tag/${VERSION}</sparkle:releaseNotesLink>
<pubDate>${DATE}</pubDate>
<enclosure
url="${DMG_URL}"
sparkle:version="${BUILD_NUMBER}"
sparkle:shortVersionString="${SHORT_VERSION}"
type="application/octet-stream"/>
</item>
XMLEOF
)
python3 -c "import sys; content = open('appcast.xml').read(); entry = sys.argv[1]; content = content.replace('</channel>', entry + '\n</channel>', 1); open('appcast.xml', 'w').write(content)" "$ENTRY"
git config user.name "github-actions[bot]"
git config user.email "github-actions[bot]@users.noreply.github.com"
git add appcast.xml
git commit -m "Release ${VERSION}"
git push origin gh-pages