Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
156 changes: 131 additions & 25 deletions .github/workflows/bundle-desktop-intel.yml
Original file line number Diff line number Diff line change
Expand Up @@ -34,10 +34,9 @@ on:
name: Reusable workflow to bundle desktop app for Intel Mac

jobs:
bundle-desktop-intel:
build-desktop-intel:
runs-on: macos-latest
name: Bundle Desktop App on Intel macOS
environment: ${{ inputs.environment || '' }}
name: Build Desktop App on Intel macOS
env:
MACOSX_DEPLOYMENT_TARGET: "12.0"
permissions:
Expand Down Expand Up @@ -120,22 +119,12 @@ jobs:
jq '.build.mac.target[0].arch = "x64"' package.json > package.json.tmp && mv package.json.tmp package.json
working-directory: ui/desktop

- name: Import Apple signing certificate
if: ${{ inputs.signing }}
uses: ./.github/actions/apple-codesign
with:
certificate-base64: ${{ secrets.APPLE_CERTIFICATE_BASE64 }}
certificate-password: ${{ secrets.APPLE_CERTIFICATE_PASSWORD }}

# Check disk space before bundling
- name: Check disk space before bundling
run: df -h

- name: Build App
env:
APPLE_ID: ${{ inputs.signing && secrets.APPLE_ID || '' }}
APPLE_ID_PASSWORD: ${{ inputs.signing && secrets.APPLE_ID_PASSWORD || '' }}
APPLE_TEAM_ID: ${{ inputs.signing && secrets.APPLE_TEAM_ID || '' }}
# Build without signing — signing env vars are intentionally omitted
- name: Build App (unsigned)
run: |
source ../../bin/activate-hermit
attempt=0
Expand All @@ -152,13 +141,6 @@ jobs:
fi
working-directory: ui/desktop

- name: Clean up signing keychain
if: always()
run: |
if [ -n "$KEYCHAIN_PATH" ] && [ -f "$KEYCHAIN_PATH" ]; then
security delete-keychain "$KEYCHAIN_PATH" || true
fi

- name: Final cleanup before artifact upload
run: |
echo "Performing final cleanup..."
Expand All @@ -167,11 +149,11 @@ jobs:
# Check disk space after cleanup
df -h

- name: Upload Desktop artifact
- name: Upload unsigned Desktop artifact
uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6.0.0
with:
name: Goose-darwin-x64
path: ui/desktop/out/Goose-darwin-x64/Goose_intel_mac.zip
name: Goose-darwin-x64-unsigned
path: ui/desktop/out/Goose-darwin-x64/
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Prevent unsigned Intel zip from colliding with release artifact

This intermediate upload now includes Goose_intel_mac.zip (unsigned) because it archives the whole output directory, while sign-desktop-intel later uploads a signed Goose_intel_mac.zip. When release jobs merge all artifacts into one directory (download-artifact with merge-multiple: true), those same-named files conflict, so the final published Intel zip can be the unsigned one depending on extraction order. Exclude/rename the unsigned zip in the build artifact so only the signed zip is eligible for publication.

Useful? React with 👍 / 👎.


- name: Quick launch test (macOS)
if: ${{ inputs.quick_test }}
Expand All @@ -193,3 +175,127 @@ jobs:
fi
# Kill the app to clean up
pkill -f "Goose.app/Contents/MacOS/Goose"

sign-desktop-intel:
needs: build-desktop-intel
if: ${{ inputs.signing }}
runs-on: macos-latest
name: Sign and Notarize Desktop App (Intel macOS)
environment: ${{ inputs.environment || '' }}
permissions:
id-token: write
contents: read
steps:
- name: Checkout code
uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
with:
ref: ${{ inputs.ref != '' && inputs.ref || '' }}
sparse-checkout: |
.github/actions/apple-codesign
ui/desktop/entitlements.plist

- name: Download unsigned artifact
uses: actions/download-artifact@37930b1c2abaa49bbe596cd826c3c89aef350131 # v7.0.0
with:
name: Goose-darwin-x64-unsigned
path: app-bundle/

- name: Import Apple signing certificate
uses: ./.github/actions/apple-codesign
with:
certificate-base64: ${{ secrets.APPLE_CERTIFICATE_BASE64 }}
certificate-password: ${{ secrets.APPLE_CERTIFICATE_PASSWORD }}

- name: Setup Node.js
uses: actions/setup-node@6044e13b5dc448c55e2357c09f80417699197238 # v6.2.0
with:
node-version: 22

- name: Install signing tools
run: npm install @electron/osx-sign @electron/notarize

- name: Sign app bundle
env:
KEYCHAIN_PATH: ${{ env.KEYCHAIN_PATH }}
run: |
node -e "
const { signAsync } = require('@electron/osx-sign');
signAsync({
app: 'app-bundle/Goose.app',
keychain: process.env.KEYCHAIN_PATH || undefined,
identity: 'Developer ID Application',
entitlements: 'ui/desktop/entitlements.plist',
entitlementsInherit: 'ui/desktop/entitlements.plist',
}).then(() => {
console.log('Code signing complete.');
}).catch((err) => {
console.error('Code signing failed:', err);
process.exit(1);
});
"

- name: Verify code signature
run: |
codesign --verify --deep --strict --verbose=2 app-bundle/Goose.app
echo "Signature verification passed."

- name: Notarize app bundle
env:
APPLE_ID: ${{ secrets.APPLE_ID }}
APPLE_ID_PASSWORD: ${{ secrets.APPLE_ID_PASSWORD }}
APPLE_TEAM_ID: ${{ secrets.APPLE_TEAM_ID }}
run: |
node -e "
const { notarize } = require('@electron/notarize');
notarize({
appPath: 'app-bundle/Goose.app',
appleId: process.env.APPLE_ID,
appleIdPassword: process.env.APPLE_ID_PASSWORD,
teamId: process.env.APPLE_TEAM_ID,
}).then(() => {
console.log('Notarization complete.');
}).catch((err) => {
console.error('Notarization failed:', err);
process.exit(1);
});
"

- name: Staple notarization ticket
run: xcrun stapler staple app-bundle/Goose.app

- name: Create zip for distribution
run: |
cd app-bundle
ditto -c -k --sequesterRsrc --keepParent Goose.app Goose_intel_mac.zip

- name: Upload signed Desktop artifact
uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6.0.0
with:
name: Goose-darwin-x64
path: app-bundle/Goose_intel_mac.zip

- name: Clean up signing keychain
if: always()
run: |
if [ -n "$KEYCHAIN_PATH" ] && [ -f "$KEYCHAIN_PATH" ]; then
security delete-keychain "$KEYCHAIN_PATH" || true
fi

# When signing is disabled, publish the unsigned build with the expected artifact name
package-desktop-intel:
needs: build-desktop-intel
if: ${{ !inputs.signing }}
runs-on: ubuntu-latest
name: Package Desktop App (Intel, unsigned)
steps:
- name: Download unsigned artifact
uses: actions/download-artifact@37930b1c2abaa49bbe596cd826c3c89aef350131 # v7.0.0
with:
name: Goose-darwin-x64-unsigned
path: app-bundle/

- name: Upload Desktop artifact
uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6.0.0
with:
name: Goose-darwin-x64
path: app-bundle/Goose_intel_mac.zip
Loading
Loading