mirror of
https://github.com/okieselbach/AutopilotMonitor.git
synced 2026-10-24 12:42:38 +00:00
f5efadfd07
A tag is a mutable pointer owned by the action's publisher. These jobs are not ordinary CI: they hold the code-signing Key Vault token (D-223), the federated storage credentials (D-222), and combine-rules.yml holds a contents:write token that pushes to main. Whoever could repoint v7 would run code with all of that. azure/login and Azure/functions-action were already pinned; the actions/* and github/codeql-action references were not, on the usual argument that GitHub owns them and shares the runner's trust boundary. That argument does not survive the fact that they run in the same jobs as the signing identity. All seven are now pinned by SHA with the version in a trailing comment -- the comment is what keeps the line reviewable, and Dependabot rewrites both together. action-pins.yml is the guard: pins nobody enforces rot, and no review reliably catches one @v4 added to a 200-line workflow. It lives in its own workflow because ci.yml deliberately ignores changes to the other workflow files, so a job placed there would stay silent for exactly the commits it must inspect. Like CI it reports rather than blocks -- main is unprotected; making it binding would mean adding it as a required status check, which is a branch-protection decision. Verified both directions: 49 references pass against the repo, and a fixture with @v7, @main and a bare SHA fails with three annotations while the local reusable workflow refs are skipped. Two floating tool versions closed while here. wix went from the range 5.* to 5.0.2 -- it writes the bytes of the MSI customers install, where AzureSignTool only signs them. ajv was the worse one: unpinned, and npm runs lifecycle scripts at install, so a hijacked release would have executed with the push token before a single rule was validated; now exact versions with --ignore-scripts. Line endings: sed -i rewrote the workflow files as LF. Git normalises on commit, so the content diff is unaffected and indexnow-notify.yml carries no change at all. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
240 lines
9.1 KiB
YAML
240 lines
9.1 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, and a guard that the generated rule
|
|
# files still match rules/guardrails.json.
|
|
#
|
|
# 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'
|
|
- '**/*.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/oidc-login-test.yml'
|
|
- '.github/workflows/pester.yml'
|
|
- '.github/workflows/publish-scripts.yml'
|
|
pull_request:
|
|
branches: [main]
|
|
paths-ignore:
|
|
- 'internal'
|
|
- 'internal/**'
|
|
- 'infra'
|
|
- 'infra/**'
|
|
- '.claude/**'
|
|
- '.gitmodules'
|
|
- '**/*.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/oidc-login-test.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
|
|
|
|
- uses: actions/setup-dotnet@a98b56852c35b8e3190ac28c8c2271da59106c68 # v6
|
|
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"
|
|
|
|
web:
|
|
name: Web (typecheck + tests)
|
|
runs-on: ubuntu-latest
|
|
defaults:
|
|
run:
|
|
working-directory: src/Web/autopilot-monitor-web
|
|
steps:
|
|
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7
|
|
|
|
- uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7
|
|
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
|
|
|
|
- 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
|
|
|
|
- uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7
|
|
with:
|
|
node-version: '22'
|
|
cache: npm
|
|
cache-dependency-path: src/McpServer/autopilot-monitor-mcp/package-lock.json
|
|
|
|
- run: npm ci
|
|
|
|
- name: Typecheck
|
|
run: npx tsc --noEmit
|
|
|
|
# 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
|
|
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
|
|
|
|
- uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7
|
|
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
|
|
|
|
- uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7
|
|
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/utils/shared-manifests.generated.ts src/Web/autopilot-monitor-web/utils/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
|