mirror of
https://github.com/okieselbach/AutopilotMonitor.git
synced 2026-10-24 12:42:38 +00:00
c26542776e
Every job that runs third-party code (restore, build, tests, npm ci, next build, wix) now holds only a read-only token and hands its output on as one archive. Signing, publishing and deploying happen in separate jobs on a fresh runner that run only this repository's scripts and pinned actions, unpack artifacts outside the checkout and take no build-job output into a script. - build-agent: reserve -> build -> release - build-msi: sign-loader -> build -> release - deploy-backend: reserve -> build -> deploy (publish output as one tar.gz) - deploy-web: build -> deploy (SWA token only, no id-token) -> record - combine-rules: validate (read-only, PRs too) -> publish (push to main only) - deploy-mcp: same shape; documents docker build as its boundary Deploy-only jobs run in the production environment. The jobs that sign stay without an environment for now: a job carries one OIDC subject, and the signing identity still trusts the ref. oidc-login-test.yml is removed; combine-rules no longer triggers on the dev branch. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
294 lines
11 KiB
YAML
294 lines
11 KiB
YAML
name: CI
|
|
|
|
# Tests, typechecks and drift-checks every push to main and every pull request:
|
|
# the .NET suites, the web and MCP suites, a guard that the generated rule
|
|
# files still match rules/guardrails.json, and the duplicate-code check.
|
|
#
|
|
# The deploy workflows build but never test, so this is the only place the suites
|
|
# actually run. It does not gate anything — main is deliberately unprotected, so a
|
|
# red run reports rather than blocks. Read the result and follow up.
|
|
#
|
|
# Path filters: commits that only bump a private submodule gitlink (internal/, infra/,
|
|
# .claude/commands), touch prose or edit another workflow cannot change anything the
|
|
# suites cover, so they are skipped instead of burning ~15 runner-minutes each. A gitlink bump reports the bare
|
|
# path ('internal', no trailing slash), which 'internal/**' does NOT match — both forms
|
|
# are listed. GitHub skips only when EVERY changed path matches; a commit that touches
|
|
# docs AND code still runs.
|
|
on:
|
|
push:
|
|
branches: [main]
|
|
paths-ignore:
|
|
- 'internal'
|
|
- 'internal/**'
|
|
- 'infra'
|
|
- 'infra/**'
|
|
- '.claude/**'
|
|
- '.gitmodules'
|
|
- 'zizmor.yml'
|
|
- '**/*.md'
|
|
- 'LICENSE'
|
|
- '.github/ISSUE_TEMPLATE/**'
|
|
- '.github/FUNDING.yml'
|
|
- '.github/dependabot.yml'
|
|
- '.github/assets/**'
|
|
- '.github/codeql/**'
|
|
- '.github/scripts/**'
|
|
# Every other workflow: nothing here builds or tests anything CI covers. paths-ignore
|
|
# cannot negate, so they are listed one by one; ci.yml itself stays out of the list
|
|
# so a change to CI runs CI. A new workflow file is opt-in — add it here too.
|
|
- '.github/workflows/action-pins.yml'
|
|
- '.github/workflows/bootstrap-script-gates.yml'
|
|
- '.github/workflows/build-agent.yml'
|
|
- '.github/workflows/build-msi.yml'
|
|
- '.github/workflows/codeql.yml'
|
|
- '.github/workflows/combine-rules.yml'
|
|
- '.github/workflows/deploy-backend.yml'
|
|
- '.github/workflows/deploy-mcp.yml'
|
|
- '.github/workflows/deploy-web.yml'
|
|
- '.github/workflows/indexnow-notify.yml'
|
|
- '.github/workflows/pester.yml'
|
|
- '.github/workflows/publish-scripts.yml'
|
|
pull_request:
|
|
branches: [main]
|
|
paths-ignore:
|
|
- 'internal'
|
|
- 'internal/**'
|
|
- 'infra'
|
|
- 'infra/**'
|
|
- '.claude/**'
|
|
- '.gitmodules'
|
|
- 'zizmor.yml'
|
|
- '**/*.md'
|
|
- 'LICENSE'
|
|
- '.github/ISSUE_TEMPLATE/**'
|
|
- '.github/FUNDING.yml'
|
|
- '.github/dependabot.yml'
|
|
- '.github/assets/**'
|
|
- '.github/codeql/**'
|
|
- '.github/scripts/**'
|
|
# Every other workflow: nothing here builds or tests anything CI covers. paths-ignore
|
|
# cannot negate, so they are listed one by one; ci.yml itself stays out of the list
|
|
# so a change to CI runs CI. A new workflow file is opt-in — add it here too.
|
|
- '.github/workflows/action-pins.yml'
|
|
- '.github/workflows/bootstrap-script-gates.yml'
|
|
- '.github/workflows/build-agent.yml'
|
|
- '.github/workflows/build-msi.yml'
|
|
- '.github/workflows/codeql.yml'
|
|
- '.github/workflows/combine-rules.yml'
|
|
- '.github/workflows/deploy-backend.yml'
|
|
- '.github/workflows/deploy-mcp.yml'
|
|
- '.github/workflows/deploy-web.yml'
|
|
- '.github/workflows/indexnow-notify.yml'
|
|
- '.github/workflows/pester.yml'
|
|
- '.github/workflows/publish-scripts.yml'
|
|
workflow_dispatch:
|
|
|
|
permissions:
|
|
contents: read
|
|
|
|
concurrency:
|
|
group: ci-${{ github.ref }}
|
|
cancel-in-progress: true
|
|
|
|
jobs:
|
|
dotnet:
|
|
name: .NET tests (backend + agent)
|
|
# windows-latest is required: the agent test projects target net48.
|
|
runs-on: windows-latest
|
|
steps:
|
|
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
|
with:
|
|
persist-credentials: false
|
|
|
|
- uses: actions/setup-dotnet@a98b56852c35b8e3190ac28c8c2271da59106c68 # v6.0.0
|
|
with:
|
|
dotnet-version: '10.0.x'
|
|
|
|
- name: Restore
|
|
run: dotnet restore AutopilotMonitor.sln
|
|
|
|
- name: Build
|
|
run: dotnet build AutopilotMonitor.sln --configuration Release --no-restore
|
|
|
|
- name: Backend tests
|
|
run: dotnet test src/Backend/AutopilotMonitor.Functions.Tests --configuration Release --no-build --verbosity quiet --logger "console;verbosity=minimal"
|
|
|
|
- name: Agent V2 Core tests
|
|
run: dotnet test src/Agent/AutopilotMonitor.Agent.V2.Core.Tests --configuration Release --no-build --verbosity quiet --logger "console;verbosity=minimal"
|
|
|
|
- name: Decision Core tests
|
|
run: dotnet test src/Agent/AutopilotMonitor.DecisionCore.Tests --configuration Release --no-build --verbosity quiet --logger "console;verbosity=minimal"
|
|
|
|
- name: Summary Dialog tests
|
|
run: dotnet test src/Agent/AutopilotMonitor.SummaryDialog.Tests --configuration Release --no-build --verbosity quiet --logger "console;verbosity=minimal"
|
|
|
|
- name: Push tests
|
|
run: dotnet test src/Push/AutopilotMonitor.Push.Tests --configuration Release --no-build --verbosity quiet --logger "console;verbosity=minimal"
|
|
|
|
web:
|
|
name: Web (typecheck + tests)
|
|
runs-on: ubuntu-latest
|
|
defaults:
|
|
run:
|
|
working-directory: src/Web/autopilot-monitor-web
|
|
steps:
|
|
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
|
with:
|
|
persist-credentials: false
|
|
|
|
- uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0
|
|
with:
|
|
node-version: '22'
|
|
cache: npm
|
|
cache-dependency-path: src/Web/autopilot-monitor-web/package-lock.json
|
|
|
|
- run: npm ci
|
|
|
|
# next-env.d.ts + .next type stubs are gitignored (Next-managed since
|
|
# v16); typegen generates them so tsc sees the same types as a build.
|
|
- name: Generate Next.js types
|
|
run: npx next typegen
|
|
|
|
- name: Typecheck
|
|
run: npx tsc --noEmit
|
|
|
|
# Errors fail outright, and so does ANY warning: the codebase is
|
|
# warning-clean and must stay that way — fix new findings, never
|
|
# raise the cap.
|
|
- name: Lint
|
|
run: npm run lint -- --max-warnings 0
|
|
|
|
# Unused files, exports and dependencies (knip.json). Its own script on
|
|
# purpose: chained into `lint`, the `-- --max-warnings 0` above would be
|
|
# handed to knip and ESLint would lose its cap.
|
|
- name: Unused code
|
|
run: npm run knip
|
|
|
|
- name: Tests
|
|
run: npm test
|
|
|
|
mcp:
|
|
name: MCP server tests
|
|
runs-on: ubuntu-latest
|
|
defaults:
|
|
run:
|
|
working-directory: src/McpServer/autopilot-monitor-mcp
|
|
steps:
|
|
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
|
with:
|
|
persist-credentials: false
|
|
|
|
- uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0
|
|
with:
|
|
node-version: '22'
|
|
cache: npm
|
|
cache-dependency-path: src/McpServer/autopilot-monitor-mcp/package-lock.json
|
|
|
|
- run: npm ci
|
|
|
|
# tsconfig.json covers sources, tests and scripts; the build emits via
|
|
# tsconfig.build.json.
|
|
- name: Typecheck
|
|
run: npx tsc --noEmit
|
|
|
|
# Same rule as the web job: any warning fails — fix new findings, never
|
|
# raise the cap.
|
|
- name: Lint
|
|
run: npm run lint -- --max-warnings 0
|
|
|
|
- name: Unused code
|
|
run: npm run knip
|
|
|
|
# The vector-search tests tokenize with the real all-MiniLM-L6-v2 model
|
|
# (~23 MB tokenizer + ONNX weights). transformers.js caches under
|
|
# node_modules by default, which npm ci recreates — so every CI run
|
|
# re-downloaded from huggingface.co and rate-limits (429) failed the job
|
|
# (run 31252869254). Persist the model across runs instead; HF_CACHE_DIR
|
|
# is honored by vector-search-provider.ts.
|
|
- name: Cache embedding model
|
|
uses: actions/cache@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0
|
|
with:
|
|
path: ${{ github.workspace }}/.hf-cache
|
|
key: hf-model-Xenova-all-MiniLM-L6-v2
|
|
|
|
- name: Tests
|
|
run: npm test
|
|
env:
|
|
HF_CACHE_DIR: ${{ github.workspace }}/.hf-cache
|
|
|
|
guardrails-in-sync:
|
|
name: Generated guardrails match rules/guardrails.json
|
|
runs-on: ubuntu-latest
|
|
steps:
|
|
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
|
with:
|
|
persist-credentials: false
|
|
|
|
- uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0
|
|
with:
|
|
node-version: '22'
|
|
|
|
- name: Regenerate
|
|
run: node rules/scripts/combine.js
|
|
|
|
- name: Fail if the generated files drifted
|
|
run: |
|
|
if ! git diff --quiet; then
|
|
echo "::error::Generated files are out of date. Run 'node rules/scripts/combine.js' and commit the result."
|
|
git diff --stat
|
|
exit 1
|
|
fi
|
|
|
|
shared-manifests-in-sync:
|
|
name: Generated wire types match shared-manifests.json
|
|
runs-on: ubuntu-latest
|
|
steps:
|
|
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
|
with:
|
|
persist-credentials: false
|
|
|
|
- uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0
|
|
with:
|
|
node-version: '22'
|
|
|
|
# JSON ↔ C# parity is the dotnet job's SharedManifestParityTests; this job pins
|
|
# JSON ↔ generated TS (shared-manifests.generated.ts, wire-types.generated.ts and the
|
|
# MCP's wire-types + wire-vocabularies copies - the vocabularies are what the MCP
|
|
# tool input schemas derive their z.enum() from).
|
|
- name: Regenerate
|
|
run: node scripts/generate-shared-manifest-types.js
|
|
working-directory: src/Web/autopilot-monitor-web
|
|
|
|
- name: Fail if the generated files drifted
|
|
run: |
|
|
if ! git diff --quiet -- src/Web/autopilot-monitor-web/lib/generated/shared-manifests.generated.ts src/Web/autopilot-monitor-web/lib/generated/wire-types.generated.ts src/McpServer/autopilot-monitor-mcp/src/generated/wire-types.generated.ts src/McpServer/autopilot-monitor-mcp/src/generated/wire-vocabularies.generated.ts; then
|
|
echo "::error::Generated wire types are out of date. Run 'npm run generate:manifests' in src/Web/autopilot-monitor-web and commit the result."
|
|
git diff --stat
|
|
exit 1
|
|
fi
|
|
|
|
duplicates:
|
|
name: Duplicate code
|
|
runs-on: ubuntu-latest
|
|
defaults:
|
|
run:
|
|
working-directory: tools/quality
|
|
steps:
|
|
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
|
with:
|
|
persist-credentials: false
|
|
|
|
- uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0
|
|
with:
|
|
node-version: '22'
|
|
cache: npm
|
|
cache-dependency-path: tools/quality/package-lock.json
|
|
|
|
- run: npm ci --ignore-scripts
|
|
|
|
# jscpd over the C# and TypeScript sources (.jscpd.json). A clone missing from
|
|
# jscpd-baseline.json fails, and so does a baseline entry whose clone is gone,
|
|
# so the baseline only shrinks. Locally: npm run dup in tools/quality.
|
|
- name: Duplicate check
|
|
run: npm run dup
|