Build & Package #285
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: Build & Package | |
| # Branch-preview override for worktree-product-refactor-research. | |
| # | |
| # The default branch only knows this workflow file name, so GitHub can dispatch | |
| # `build.yml` on a branch ref. The branch version below intentionally mirrors | |
| # the reviewed preview-build workflow: artifacts only, no Release, no tag, no | |
| # stable update channel. | |
| on: | |
| workflow_dispatch: | |
| inputs: | |
| platform: | |
| description: "Target platform" | |
| required: true | |
| default: "all" | |
| type: choice | |
| options: | |
| - all | |
| - windows | |
| - macos | |
| permissions: | |
| contents: read | |
| env: | |
| PREVIEW_VERSION: 0.55.0-preview.4 | |
| jobs: | |
| verify-source: | |
| runs-on: ubuntu-latest | |
| steps: | |
| - uses: actions/checkout@v4 | |
| with: | |
| fetch-depth: 0 | |
| - name: Fail-closed gate A — required preview fixes are present | |
| run: | | |
| set -euo pipefail | |
| if ! git merge-base --is-ancestor 6923f13 HEAD; then | |
| echo "::error::checkout does not include 6923f13 (preview startup fixes)" | |
| exit 1 | |
| fi | |
| if grep -RIn -- "--listen" src/lib/codex/app-server-manager.ts; then | |
| echo "::error::Codex app-server still uses --listen" | |
| exit 1 | |
| fi | |
| node -e ' | |
| const v = require("./package.json").version; | |
| if (v !== process.env.PREVIEW_VERSION) { | |
| console.error(`::error::package.json version ${v} != ${process.env.PREVIEW_VERSION}`); | |
| process.exit(1); | |
| } | |
| console.log(`✓ package version ${v}`); | |
| ' | |
| - uses: actions/setup-node@v4 | |
| with: | |
| node-version: 20 | |
| cache: npm | |
| - run: npm ci | |
| - name: Typecheck | |
| run: npm run typecheck | |
| - name: Fail-closed gate B — ClaudeCode canonicalizes bare sonnet | |
| run: | | |
| set -euo pipefail | |
| trap 'rm -f preview-gate-sonnet.ts' EXIT | |
| cat > preview-gate-sonnet.ts <<'TS' | |
| import { toClaudeCodeEnv } from './src/lib/provider-resolver'; | |
| const resolved: any = { | |
| provider: { | |
| id: 'gate', | |
| name: 'Legacy Gateway', | |
| provider_type: 'anthropic', | |
| protocol: 'anthropic', | |
| base_url: 'https://gateway.example.com/anthropic', | |
| api_key: 'key', | |
| is_active: 1, | |
| sort_order: 0, | |
| extra_env: '{}', | |
| headers_json: '{}', | |
| env_overrides_json: '', | |
| role_models_json: JSON.stringify({ default: 'sonnet', sonnet: 'sonnet', haiku: 'haiku', opus: 'opus' }), | |
| notes: '', | |
| created_at: '', | |
| updated_at: '', | |
| options_json: '{}', | |
| }, | |
| protocol: 'anthropic', | |
| authStyle: 'api_key', | |
| model: 'sonnet', | |
| upstreamModel: 'claude-sonnet-4-6', | |
| modelDisplayName: 'Sonnet 4.6', | |
| headers: {}, | |
| envOverrides: {}, | |
| roleModels: { default: 'sonnet', sonnet: 'sonnet', haiku: 'haiku', opus: 'opus' }, | |
| hasCredentials: true, | |
| availableModels: [ | |
| { modelId: 'sonnet', upstreamModelId: 'claude-sonnet-4-6', displayName: 'Sonnet 4.6' }, | |
| { modelId: 'haiku', upstreamModelId: 'claude-haiku-4-5-20251001', displayName: 'Haiku 4.5' }, | |
| { modelId: 'opus', upstreamModelId: 'claude-opus-4-7', displayName: 'Opus 4.7' }, | |
| ], | |
| settingSources: ['user'], | |
| }; | |
| const env = toClaudeCodeEnv({}, resolved); | |
| if (env.ANTHROPIC_MODEL !== 'claude-sonnet-4-6') { | |
| console.error(`::error::expected claude-sonnet-4-6, got ${env.ANTHROPIC_MODEL}`); | |
| process.exit(1); | |
| } | |
| console.log(`✓ sonnet -> ${env.ANTHROPIC_MODEL}`); | |
| TS | |
| CODEX_DISABLED=1 npx tsx preview-gate-sonnet.ts | |
| - name: P0 regression pins | |
| run: | | |
| CODEX_DISABLED=1 npx tsx --test --import ./src/__tests__/db-isolation.setup.ts \ | |
| src/__tests__/unit/codex-binary-discovery.test.ts \ | |
| src/__tests__/unit/provider-resolver.test.ts | |
| # Full unit coverage is enforced by the local pre-commit hook on the | |
| # pushed commit. The branch-preview CI keeps the remote gate focused on | |
| # source identity + P0 startup regressions so artifact packaging cannot be | |
| # blocked by unrelated Linux runner test hangs. | |
| build-macos-arm64: | |
| needs: verify-source | |
| if: ${{ inputs.platform == 'all' || inputs.platform == 'macos' }} | |
| runs-on: macos-15 | |
| steps: | |
| - uses: actions/checkout@v4 | |
| - uses: actions/setup-node@v4 | |
| with: | |
| node-version: 20 | |
| cache: npm | |
| - run: npm ci | |
| - name: Build + package macOS arm64 | |
| timeout-minutes: 30 | |
| env: | |
| CSC_IDENTITY_AUTO_DISCOVERY: "false" | |
| run: | | |
| npm run electron:build | |
| npx electron-builder --mac --arm64 --config electron-builder.yml --publish never | |
| - name: Verify packaged version and native ABI | |
| run: | | |
| set -euo pipefail | |
| DMG=$(find release -name "CodePilot-*-arm64.dmg" | head -1) | |
| [ -n "$DMG" ] || { echo "::error::no arm64 DMG produced"; exit 1; } | |
| case "$DMG" in *"$PREVIEW_VERSION"*) ;; *) echo "::error::DMG name lacks $PREVIEW_VERSION"; exit 1 ;; esac | |
| APP=$(find release -maxdepth 4 -name "CodePilot.app" -type d | head -1) | |
| PLIST_VER=$(plutil -extract CFBundleShortVersionString raw "$APP/Contents/Info.plist") | |
| [ "$PLIST_VER" = "$PREVIEW_VERSION" ] || { echo "::error::Info.plist $PLIST_VER != $PREVIEW_VERSION"; exit 1; } | |
| SQLITE=$(find "$APP/Contents/Resources" -path "*better-sqlite3*" -name "*.node" | head -1) | |
| [ -n "$SQLITE" ] || { echo "::error::better-sqlite3 .node not found"; exit 1; } | |
| ELECTRON_RUN_AS_NODE=1 "$APP/Contents/MacOS/CodePilot" -e "const path = require('node:path'); require(path.resolve(process.argv[1])); console.log('better-sqlite3 OK, ABI=' + process.versions.modules)" "$SQLITE" | |
| - name: Checksums | |
| run: cd release && shasum -a 256 CodePilot-*-arm64.dmg CodePilot-*-arm64*.zip 2>/dev/null | tee checksums-macos-arm64.sha256 | |
| - uses: actions/upload-artifact@v4 | |
| with: | |
| name: preview-macos-arm64 | |
| path: | | |
| release/CodePilot-*-arm64.dmg | |
| release/CodePilot-*-arm64*.zip | |
| release/checksums-macos-arm64.sha256 | |
| retention-days: 7 | |
| build-windows-x64: | |
| needs: verify-source | |
| if: ${{ inputs.platform == 'all' || inputs.platform == 'windows' }} | |
| runs-on: windows-latest | |
| steps: | |
| - uses: actions/checkout@v4 | |
| - uses: actions/setup-node@v4 | |
| with: | |
| node-version: 20 | |
| cache: npm | |
| - run: npm ci | |
| - name: Build + package Windows x64 | |
| timeout-minutes: 35 | |
| run: | | |
| npm run electron:build | |
| npx electron-builder --win --x64 --config electron-builder.yml --publish never | |
| - name: Verify packaged version and native ABI | |
| shell: bash | |
| run: | | |
| set -euo pipefail | |
| EXE=$(find release -name "CodePilot.Setup.*.exe" | head -1) | |
| [ -n "$EXE" ] || { echo "::error::no NSIS installer produced"; exit 1; } | |
| case "$EXE" in *"$PREVIEW_VERSION"*) ;; *) echo "::error::installer name lacks $PREVIEW_VERSION"; exit 1 ;; esac | |
| SQLITE=$(find release/win-unpacked -path "*better-sqlite3*" -name "*.node" 2>/dev/null | head -1) | |
| [ -n "$SQLITE" ] || { echo "::error::better-sqlite3 .node not found"; exit 1; } | |
| BIN=$(find release/win-unpacked -maxdepth 1 -name "CodePilot.exe" | head -1) | |
| ELECTRON_RUN_AS_NODE=1 "$BIN" -e "const path = require('node:path'); require(path.resolve(process.argv[1])); console.log('better-sqlite3 OK, ABI=' + process.versions.modules)" "$SQLITE" | |
| - name: Checksums | |
| shell: bash | |
| run: cd release && sha256sum CodePilot.Setup.*.exe 2>/dev/null | tee checksums-windows-x64.sha256 | |
| - uses: actions/upload-artifact@v4 | |
| with: | |
| name: preview-windows-x64 | |
| path: | | |
| release/CodePilot.Setup.*.exe | |
| release/checksums-windows-x64.sha256 | |
| retention-days: 7 |