Voice input #32
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| name: Release macOS Apple Silicon | |
| on: | |
| push: | |
| branches: | |
| - main | |
| tags: | |
| - 'v*' | |
| workflow_dispatch: | |
| permissions: | |
| contents: write | |
| jobs: | |
| build-macos-arm64: | |
| runs-on: macos-14 | |
| env: | |
| APPLE_SIGNING_IDENTITY: ${{ secrets.APPLE_SIGNING_IDENTITY }} | |
| APPLE_ID: ${{ secrets.APPLE_ID }} | |
| APPLE_APP_SPECIFIC_PASSWORD: ${{ secrets.APPLE_APP_SPECIFIC_PASSWORD }} | |
| APPLE_TEAM_ID: ${{ secrets.APPLE_TEAM_ID }} | |
| HAS_APPLE_SIGNING_CERTIFICATE: ${{ secrets.APPLE_SIGNING_CERTIFICATE_P12 != '' && secrets.APPLE_SIGNING_CERTIFICATE_PASSWORD != '' }} | |
| steps: | |
| - name: Checkout | |
| uses: actions/checkout@v4 | |
| - name: Setup Node.js | |
| uses: actions/setup-node@v4 | |
| with: | |
| node-version: 20 | |
| cache: npm | |
| - name: Install dependencies | |
| run: npm ci | |
| - name: Lint | |
| run: npm run lint | |
| - name: Import Apple signing certificate | |
| if: ${{ env.HAS_APPLE_SIGNING_CERTIFICATE == 'true' }} | |
| env: | |
| APPLE_SIGNING_CERTIFICATE_P12: ${{ secrets.APPLE_SIGNING_CERTIFICATE_P12 }} | |
| APPLE_SIGNING_CERTIFICATE_PASSWORD: ${{ secrets.APPLE_SIGNING_CERTIFICATE_PASSWORD }} | |
| shell: bash | |
| run: | | |
| set -euo pipefail | |
| KEYCHAIN_PATH="${RUNNER_TEMP}/app-signing.keychain-db" | |
| CERT_PATH="${RUNNER_TEMP}/app-signing-cert.p12" | |
| echo "${APPLE_SIGNING_CERTIFICATE_P12}" | base64 --decode > "${CERT_PATH}" | |
| security create-keychain -p "${APPLE_SIGNING_CERTIFICATE_PASSWORD}" "${KEYCHAIN_PATH}" | |
| security set-keychain-settings -lut 21600 "${KEYCHAIN_PATH}" | |
| security unlock-keychain -p "${APPLE_SIGNING_CERTIFICATE_PASSWORD}" "${KEYCHAIN_PATH}" | |
| security import "${CERT_PATH}" \ | |
| -k "${KEYCHAIN_PATH}" \ | |
| -P "${APPLE_SIGNING_CERTIFICATE_PASSWORD}" \ | |
| -T /usr/bin/codesign \ | |
| -T /usr/bin/security | |
| security set-key-partition-list \ | |
| -S apple-tool:,apple:,codesign: \ | |
| -s \ | |
| -k "${APPLE_SIGNING_CERTIFICATE_PASSWORD}" \ | |
| "${KEYCHAIN_PATH}" | |
| EXISTING_KEYCHAINS="$(security list-keychains -d user | tr -d '"')" | |
| security list-keychains -d user -s "${KEYCHAIN_PATH}" ${EXISTING_KEYCHAINS} | |
| security default-keychain -d user -s "${KEYCHAIN_PATH}" | |
| security find-identity -v -p codesigning "${KEYCHAIN_PATH}" | |
| - name: Build macOS arm64 package | |
| run: npm run make:mac:arm64 | |
| - name: Smoke test packaged app | |
| shell: bash | |
| run: | | |
| set -euo pipefail | |
| APP_PATH="$(find out -type d -path '*/Zero-darwin-arm64/Zero.app' | head -n 1)" | |
| if [[ -z "${APP_PATH}" ]]; then | |
| APP_PATH="$(find out -type d -name 'Zero.app' | head -n 1)" | |
| fi | |
| if [[ -z "${APP_PATH}" ]]; then | |
| echo "Packaged Zero.app not found under out/." >&2 | |
| exit 1 | |
| fi | |
| APP_BIN="${APP_PATH}/Contents/MacOS/Zero" | |
| RESOURCES_DIR="${APP_PATH}/Contents/Resources" | |
| CODEX_BIN="${RESOURCES_DIR}/app.asar.unpacked/node_modules/@zed-industries/codex-acp-darwin-arm64/bin/codex-acp" | |
| CLAUDE_ENTRY="${RESOURCES_DIR}/app.asar.unpacked/node_modules/@zed-industries/claude-agent-acp/dist/index.js" | |
| NODE_PTY_BIN="${RESOURCES_DIR}/app.asar.unpacked/node_modules/node-pty/build/Release/pty.node" | |
| REQUIRED_PATHS=( | |
| "${APP_BIN}" | |
| "${RESOURCES_DIR}/app.asar" | |
| "${CODEX_BIN}" | |
| "${CLAUDE_ENTRY}" | |
| "${NODE_PTY_BIN}" | |
| ) | |
| for required_path in "${REQUIRED_PATHS[@]}"; do | |
| if [[ ! -e "${required_path}" ]]; then | |
| echo "Missing required runtime file: ${required_path}" >&2 | |
| exit 1 | |
| fi | |
| done | |
| if [[ ! -x "${APP_BIN}" ]]; then | |
| echo "App binary is not executable: ${APP_BIN}" >&2 | |
| exit 1 | |
| fi | |
| if [[ ! -x "${CODEX_BIN}" ]]; then | |
| echo "Codex ACP binary is not executable: ${CODEX_BIN}" >&2 | |
| exit 1 | |
| fi | |
| "${CODEX_BIN}" --help >/dev/null | |
| ELECTRON_RUN_AS_NODE=1 "${APP_BIN}" -e "console.log('smoke-node-ok')" | grep -q "smoke-node-ok" | |
| ELECTRON_RUN_AS_NODE=1 "${APP_BIN}" "${CLAUDE_ENTRY}" --help >/dev/null | |
| if [[ -n "${APPLE_SIGNING_IDENTITY:-}" ]]; then | |
| codesign --verify --deep --strict --verbose=2 "${APP_PATH}" | |
| codesign --display --verbose=2 "${APP_PATH}" >/dev/null | |
| spctl --assess --type execute --verbose=2 "${APP_PATH}" | |
| fi | |
| - name: Prepare release artifacts | |
| id: prepare | |
| shell: bash | |
| run: | | |
| set -euo pipefail | |
| ZIP_PATH="$(find out/make -type f -path '*/zip/darwin/arm64/*.zip' | head -n 1)" | |
| if [[ -z "${ZIP_PATH}" ]]; then | |
| echo "No macOS arm64 zip artifact found under out/make." >&2 | |
| exit 1 | |
| fi | |
| DMG_PATH="$(find out/make -type f -path '*/dmg/*arm64*.dmg' | head -n 1)" | |
| if [[ -z "${DMG_PATH}" ]]; then | |
| DMG_PATH="$(find out/make -type f -path '*/dmg/*.dmg' | head -n 1)" | |
| fi | |
| if [[ -z "${DMG_PATH}" ]]; then | |
| echo "No macOS arm64 dmg artifact found under out/make; creating fallback DMG from packaged app." | |
| APP_PATH="$(find out -type d -path '*/Zero-darwin-arm64/Zero.app' | head -n 1)" | |
| if [[ -z "${APP_PATH}" ]]; then | |
| APP_PATH="$(find out -type d -name 'Zero.app' | head -n 1)" | |
| fi | |
| if [[ -z "${APP_PATH}" ]]; then | |
| echo "Packaged Zero.app not found under out/ for fallback DMG creation." >&2 | |
| exit 1 | |
| fi | |
| FALLBACK_DMG_DIR="out/make/dmg-fallback" | |
| FALLBACK_DMG_PATH="${FALLBACK_DMG_DIR}/Zero-darwin-arm64.dmg" | |
| mkdir -p "${FALLBACK_DMG_DIR}" | |
| hdiutil create \ | |
| -volname "Zero" \ | |
| -srcfolder "${APP_PATH}" \ | |
| -ov \ | |
| -format UDZO \ | |
| "${FALLBACK_DMG_PATH}" | |
| DMG_PATH="${FALLBACK_DMG_PATH}" | |
| fi | |
| RELEASE_DIR="out/release-assets" | |
| mkdir -p "${RELEASE_DIR}" | |
| STABLE_ZIP="${RELEASE_DIR}/Zero-darwin-arm64.zip" | |
| STABLE_DMG="${RELEASE_DIR}/Zero-darwin-arm64.dmg" | |
| cp "${ZIP_PATH}" "${STABLE_ZIP}" | |
| cp "${DMG_PATH}" "${STABLE_DMG}" | |
| VERSION="$(node -p "require('./package.json').version")" | |
| node scripts/generate-latest-mac-yml.mjs "${STABLE_ZIP}" "${VERSION}" latest-mac.yml | |
| echo "version=${VERSION}" >> "${GITHUB_OUTPUT}" | |
| echo "zip_file=${STABLE_ZIP}" >> "${GITHUB_OUTPUT}" | |
| echo "dmg_file=${STABLE_DMG}" >> "${GITHUB_OUTPUT}" | |
| echo "update_file=${RELEASE_DIR}/latest-mac.yml" >> "${GITHUB_OUTPUT}" | |
| - name: Upload workflow artifacts | |
| uses: actions/upload-artifact@v4 | |
| with: | |
| name: zero-macos-arm64 | |
| path: out/release-assets | |
| if-no-files-found: error | |
| - name: Publish GitHub release assets | |
| if: startsWith(github.ref, 'refs/tags/v') | |
| uses: softprops/action-gh-release@v2 | |
| with: | |
| tag_name: ${{ github.ref_name }} | |
| name: Release ${{ github.ref_name }} | |
| prerelease: false | |
| make_latest: true | |
| generate_release_notes: true | |
| fail_on_unmatched_files: true | |
| target_commitish: ${{ github.sha }} | |
| files: | | |
| ${{ steps.prepare.outputs.dmg_file }} | |
| ${{ steps.prepare.outputs.zip_file }} | |
| ${{ steps.prepare.outputs.update_file }} |