Skip to content

Build & Package

Build & Package #285

Workflow file for this run

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