From f04e106b13699b5239abfecec9bfced9040cf9ff Mon Sep 17 00:00:00 2001 From: Neko Ayaka Date: Thu, 26 Feb 2026 23:11:59 +0800 Subject: [PATCH] chore(ci): macOS app code signing --- .github/workflows/release-tamagotchi.yml | 40 +++++-------------- .../electron-builder.config.ts | 34 +++++++++++++++- apps/stage-tamagotchi/package.json | 1 + pnpm-lock.yaml | 23 +++++++++-- pnpm-workspace.yaml | 2 + 5 files changed, 65 insertions(+), 35 deletions(-) diff --git a/.github/workflows/release-tamagotchi.yml b/.github/workflows/release-tamagotchi.yml index 9d2809e7c..30dbecf63 100644 --- a/.github/workflows/release-tamagotchi.yml +++ b/.github/workflows/release-tamagotchi.yml @@ -104,31 +104,6 @@ jobs: - uses: actions/checkout@v6 - # https://docs.github.com/en/actions/how-tos/deploy/deploy-to-third-party-platforms/sign-xcode-applications - # - name: Install the Apple certificate (macOS Only) - # if: matrix.os == 'macos-15-intel' || matrix.os == 'macos-26' - # env: - # BUILD_CERTIFICATE_BASE64: ${{ secrets.BUILD_CERTIFICATE_BASE64 }} - # P12_PASSWORD: ${{ secrets.P12_PASSWORD }} - # KEYCHAIN_PASSWORD: ${{ secrets.KEYCHAIN_PASSWORD }} - # run: | - # # create variables - # CERTIFICATE_PATH=$RUNNER_TEMP/build_certificate.p12 - # KEYCHAIN_PATH=$RUNNER_TEMP/app-signing.keychain-db - - # # import certificate from secrets - # echo -n "$BUILD_CERTIFICATE_BASE64" | base64 --decode -o $CERTIFICATE_PATH - - # # create temporary 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 to keychain - # security import $CERTIFICATE_PATH -P "$P12_PASSWORD" -A -t cert -f pkcs12 -k $KEYCHAIN_PATH - # security set-key-partition-list -S apple-tool:,apple: -k "$KEYCHAIN_PASSWORD" $KEYCHAIN_PATH - # security list-keychain -d user -s $KEYCHAIN_PATH - - name: macOS Select Xcode 26.2 if: matrix.os == 'macos-15-intel' || matrix.os == 'macos-26' run: | @@ -164,9 +139,17 @@ jobs: - name: Build (macOS Only) # macOS if: matrix.os == 'macos-15-intel' || matrix.os == 'macos-26' - run: pnpm run -F @proj-airi/stage-tamagotchi build && pnpm -F @proj-airi/stage-tamagotchi exec electron-builder build ${{ matrix.builder-args }} --publish=${{ (inputs.build_only || inputs.artifacts_only) && 'never' || 'onTagOrDraft' }} + run: | + echo "$CSC_CONTENT" | base64 --decode > apple-developer-code-signing.p12 + export CSC_LINK="./apple-developer-code-signing.p12" + pnpm run -F @proj-airi/stage-tamagotchi build && pnpm -F @proj-airi/stage-tamagotchi exec electron-builder build ${{ matrix.builder-args }} --publish=${{ (inputs.build_only || inputs.artifacts_only) && 'never' || 'onTagOrDraft' }} env: GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + CSC_CONTENT: ${{ secrets.CSC_CONTENT }} + CSC_KEY_PASSWORD: ${{ secrets.CSC_KEY_PASSWORD }} + APPLE_DEVELOPER_TEAM_ID: ${{ secrets.APPLE_DEVELOPER_TEAM_ID }} + APPLE_DEVELOPER_APPLE_ID: ${{ secrets.APPLE_DEVELOPER_APPLE_ID }} + APPLE_DEVELOPER_APPLE_APP_SPECIFIC_PASSWORD: ${{ secrets.APPLE_DEVELOPER_APPLE_APP_SPECIFIC_PASSWORD }} - name: Build (Linux Only) # Linux if: matrix.os == 'ubuntu-latest' || matrix.os == 'ubuntu-24.04-arm' @@ -365,11 +348,6 @@ jobs: apps/stage-tamagotchi/bundle/latest-*.yml append_body: true - # - name: Clean up keychain and provisioning profile (macOS Only) - # if: ${{ always() && (matrix.os == 'macos-15-intel' || matrix.os == 'macos-26') }} - # run: | - # security delete-keychain $RUNNER_TEMP/app-signing.keychain-db - merge-mac-latest: name: Merge macOS latest-mac.yml if: ${{ github.event_name == 'release' }} # Skipping will only exist when triggered by workflow_dispatch. No need to handle here diff --git a/apps/stage-tamagotchi/electron-builder.config.ts b/apps/stage-tamagotchi/electron-builder.config.ts index 8d9c360c6..1253e46b6 100644 --- a/apps/stage-tamagotchi/electron-builder.config.ts +++ b/apps/stage-tamagotchi/electron-builder.config.ts @@ -3,7 +3,9 @@ import type { Configuration } from 'electron-builder' import { execSync } from 'node:child_process' +import { env } from 'node:process' +import { notarize } from '@electron/notarize' import { isMacOS } from 'std-env' function hasXcode26OrAbove() { @@ -42,6 +44,30 @@ export default { output: 'dist', buildResources: 'build', }, + // For self-publishing, testing, and distribution after modified the code without access to + // an Apple Developer account, comment and uncomment the following 4 lines. + // Later on when you obtained one, you can set up the necessary certificates and provisioning + // profiles to enable these security features. + // + // https://www.bigbinary.com/blog/code-sign-notorize-mac-desktop-app + // https://kilianvalkhof.com/2019/electron/notarizing-your-electron-application/ + afterSign: async (context) => { + const { electronPlatformName, appOutDir } = context + if (electronPlatformName !== 'darwin') + return + if (env.CI !== 'true') { + console.warn('Skipping notarizing step. Packaging is not running in CI') + return + } + + const appName = context.packager.appInfo.productFilename + await notarize({ + appPath: `${appOutDir}/${appName}.app`, + teamId: env.APPLE_DEVELOPER_TEAM_ID!, + appleId: env.APPLE_DEVELOPER_APPLE_ID!, + appleIdPassword: env.APPLE_DEVELOPER_APPLE_APP_SPECIFIC_PASSWORD!, + }) + }, files: [ 'out/**', 'resources/**', @@ -91,8 +117,14 @@ export default { NSCameraUsageDescription: 'AIRI requires camera access for vision understanding', }, ], + // We have customized the notarization step in the `afterSign` hook. notarize: false, - hardenedRuntime: false, + // For self-publishing, testing, and distribution after modified the code without access to + // an Apple Developer account, comment and uncomment the following 4 lines. + // Later on when you obtained one, you can set up the necessary certificates and provisioning + // profiles to enable these security features. + // hardenedRuntime: false, + hardenedRuntime: true, executableName: 'airi', icon: useIconFormattedMacAppIcon ? 'icon.icon' : 'icon.icns', }, diff --git a/apps/stage-tamagotchi/package.json b/apps/stage-tamagotchi/package.json index 7839eb265..8bffe49cf 100644 --- a/apps/stage-tamagotchi/package.json +++ b/apps/stage-tamagotchi/package.json @@ -129,6 +129,7 @@ }, "devDependencies": { "@electron-toolkit/tsconfig": "^2.0.0", + "@electron/notarize": "catalog:", "@iconify-json/carbon": "^1.2.18", "@iconify-json/eos-icons": "^1.2.4", "@iconify-json/lucide": "^1.2.94", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index d707afd63..261f31b39 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -24,6 +24,9 @@ catalogs: '@electron-toolkit/preload': specifier: ^3.0.2 version: 3.0.2 + '@electron/notarize': + specifier: ^3.1.1 + version: 3.1.1 '@guiiai/logg': specifier: ^1.2.11 version: 1.2.11 @@ -1152,6 +1155,9 @@ importers: '@electron-toolkit/tsconfig': specifier: ^2.0.0 version: 2.0.0(@types/node@24.10.14) + '@electron/notarize': + specifier: 'catalog:' + version: 3.1.1 '@iconify-json/carbon': specifier: ^1.2.18 version: 1.2.18 @@ -3087,7 +3093,7 @@ importers: version: 14.1.0(vue@3.5.29(typescript@5.9.3)) '@wxt-dev/module-vue': specifier: ^1.0.3 - version: 1.0.3(vite@7.3.1(@types/node@24.10.14)(jiti@2.6.1)(less@4.5.1)(lightningcss@1.31.1)(terser@5.44.1)(tsx@4.21.0)(yaml@2.8.2))(vue@3.5.29(typescript@5.9.3))(wxt@0.20.18(@types/node@24.10.14)(canvas@3.2.1)(eslint@9.39.3(jiti@2.6.1))(jiti@2.6.1)(less@4.5.1)(lightningcss@1.31.1)(rollup@4.59.0)(terser@5.44.1)(tsx@4.21.0)(yaml@2.8.2)) + version: 1.0.3(vite@8.0.0-beta.15(@types/node@24.10.14)(esbuild@0.27.2)(jiti@2.6.1)(less@4.5.1)(terser@5.44.1)(tsx@4.21.0)(yaml@2.8.2))(vue@3.5.29(typescript@5.9.3))(wxt@0.20.18(@types/node@24.10.14)(canvas@3.2.1)(eslint@9.39.3(jiti@2.6.1))(jiti@2.6.1)(less@4.5.1)(lightningcss@1.31.1)(rollup@4.59.0)(terser@5.44.1)(tsx@4.21.0)(yaml@2.8.2)) nanoid: specifier: ^5.1.6 version: 5.1.6 @@ -4600,6 +4606,10 @@ packages: resolution: {integrity: sha512-jNT8nwH1f9X5GEITXaQ8IF/KdskvIkOFfB2CvwumsveVidzpSc+mvhhTMdAGSYF3O+Nq49lJ7y+ssODRXu06+A==} engines: {node: '>= 10.0.0'} + '@electron/notarize@3.1.1': + resolution: {integrity: sha512-uQQSlOiJnqRkTL1wlEBAxe90nVN/Fc/hEmk0bqpKk8nKjV1if/tXLHKUPePtv9Xsx90PtZU8aidx5lAiOpjkQQ==} + engines: {node: '>= 22.12.0'} + '@electron/osx-sign@1.3.3': resolution: {integrity: sha512-KZ8mhXvWv2rIEgMbWZ4y33bDHyUKMXnx4M0sTyPNK/vcB81ImdeY9Ggdqy0SWbMDgmbqyQ+phgejh6V3R2QuSg==} engines: {node: '>=12.0.0'} @@ -18350,6 +18360,13 @@ snapshots: transitivePeerDependencies: - supports-color + '@electron/notarize@3.1.1': + dependencies: + debug: 4.4.3 + promise-retry: 2.0.1 + transitivePeerDependencies: + - supports-color + '@electron/osx-sign@1.3.3': dependencies: compare-version: 0.1.2 @@ -23233,9 +23250,9 @@ snapshots: '@types/filesystem': 0.0.36 '@types/har-format': 1.2.16 - '@wxt-dev/module-vue@1.0.3(vite@7.3.1(@types/node@24.10.14)(jiti@2.6.1)(less@4.5.1)(lightningcss@1.31.1)(terser@5.44.1)(tsx@4.21.0)(yaml@2.8.2))(vue@3.5.29(typescript@5.9.3))(wxt@0.20.18(@types/node@24.10.14)(canvas@3.2.1)(eslint@9.39.3(jiti@2.6.1))(jiti@2.6.1)(less@4.5.1)(lightningcss@1.31.1)(rollup@4.59.0)(terser@5.44.1)(tsx@4.21.0)(yaml@2.8.2))': + '@wxt-dev/module-vue@1.0.3(vite@8.0.0-beta.15(@types/node@24.10.14)(esbuild@0.27.2)(jiti@2.6.1)(less@4.5.1)(terser@5.44.1)(tsx@4.21.0)(yaml@2.8.2))(vue@3.5.29(typescript@5.9.3))(wxt@0.20.18(@types/node@24.10.14)(canvas@3.2.1)(eslint@9.39.3(jiti@2.6.1))(jiti@2.6.1)(less@4.5.1)(lightningcss@1.31.1)(rollup@4.59.0)(terser@5.44.1)(tsx@4.21.0)(yaml@2.8.2))': dependencies: - '@vitejs/plugin-vue': 6.0.4(vite@7.3.1(@types/node@24.10.14)(jiti@2.6.1)(less@4.5.1)(lightningcss@1.31.1)(terser@5.44.1)(tsx@4.21.0)(yaml@2.8.2))(vue@3.5.29(typescript@5.9.3)) + '@vitejs/plugin-vue': 6.0.4(vite@8.0.0-beta.15(@types/node@24.10.14)(esbuild@0.27.2)(jiti@2.6.1)(less@4.5.1)(terser@5.44.1)(tsx@4.21.0)(yaml@2.8.2))(vue@3.5.29(typescript@5.9.3)) wxt: 0.20.18(@types/node@24.10.14)(canvas@3.2.1)(eslint@9.39.3(jiti@2.6.1))(jiti@2.6.1)(less@4.5.1)(lightningcss@1.31.1)(rollup@4.59.0)(terser@5.44.1)(tsx@4.21.0)(yaml@2.8.2) transitivePeerDependencies: - vite diff --git a/pnpm-workspace.yaml b/pnpm-workspace.yaml index e90e93db6..b5a5ee027 100644 --- a/pnpm-workspace.yaml +++ b/pnpm-workspace.yaml @@ -26,6 +26,7 @@ patchedDependencies: mineflayer-pathfinder: patches/mineflayer-pathfinder.patch mineflayer@4.33.0: patches/mineflayer@4.33.0.patch srvx@0.9.8: patches/srvx@0.9.8.patch + catalog: '@capacitor/cli': ^8.1.0 '@capacitor/core': ^8.1.0 @@ -33,6 +34,7 @@ catalog: '@capacitor/local-notifications': ^8.0.1 '@electric-sql/pglite': ^0.3.15 '@electron-toolkit/preload': ^3.0.2 + '@electron/notarize': ^3.1.1 '@guiiai/logg': ^1.2.11 '@histoire/plugin-vue': 1.0.0-beta.1 '@iconify-json/logos': ^1.2.10