Files
2026-09-11 09:07:01 -07:00

207 lines
8.3 KiB
YAML

# CI Gate — the authoritative status checks for pull requests.
#
# Both jobs ALWAYS run on every PR to `main` (no path filters), so they can be
# safely marked as required status checks without the "required check never
# runs on path-filtered PRs" deadlock.
#
# `Docs Site` builds and audits the MkDocs website. `CI Gate` ports the
# Excel-free pre-commit gates (scripts/pre-commit.ps1) into CI so
# that the correctness checks are enforced server-side even if a contributor
# does not have the local hook installed. The Excel-dependent gates — the CLI
# workflow smoke test and the MCP smoke test — are intentionally NOT run here:
# this job uses the GitHub-hosted `windows-latest` runner, which does not have
# Microsoft Excel installed. Those two gates remain local prerequisites and are
# explicitly attested in the pull request testing checklist.
name: CI Gate
on:
pull_request:
branches: [ main ]
push:
branches: [ main ]
workflow_dispatch:
permissions:
contents: read
concurrency:
group: ci-gate-${{ github.ref }}
cancel-in-progress: true
jobs:
# The documentation site is the primary distribution channel for this project
# (README.md is a landing page that links to excelmcpserver.dev), but until
# this job existed nothing validated it before merge: the Pages workflow only
# builds on push to `main`, and scripts/pre-commit.ps1 classifies `docs/`,
# `gh-pages/` and `*.md` as a docs-only fast path that skips almost every
# gate. A broken --strict build or audit regression therefore surfaced only
# after merge, on the deploy run. Runs unfiltered like the CI Gate job below
# so it can safely be a required check.
docs:
name: Docs Site
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v7
with:
# hooks.py derives each sitemap <lastmod> from `git log`, so the full
# history is required. A shallow clone would date every page to the
# tip commit; audit_site.py fails the build if it detects that.
fetch-depth: 0
- name: Setup Python
uses: actions/setup-python@v7
with:
python-version: '3.13'
cache: pip
cache-dependency-path: gh-pages/requirements.txt
- name: Install dependencies
run: pip install -r gh-pages/requirements.txt
- name: Build with MkDocs
working-directory: gh-pages
run: mkdocs build --strict --clean
- name: Audit built site
working-directory: gh-pages
run: python audit_site.py
# Guards against the deploy workflow's hand-written paths: filter drifting
# from the canonical sources hooks.py mirrors, which would publish a
# silently stale site until the nightly cron caught up.
- name: Verify deploy path filter covers every mirrored source
working-directory: gh-pages
run: python check_deploy_paths.py
ci-gate:
name: CI Gate
runs-on: windows-latest
steps:
- name: Checkout
uses: actions/checkout@v7
- name: Npm lockfile portability regression tests
timeout-minutes: 2
shell: pwsh
run: ./scripts/Test-NpmLockfiles.ps1
- name: Npm lockfile portability
timeout-minutes: 2
shell: pwsh
run: ./scripts/check-npm-lockfiles.ps1
- name: Setup .NET
uses: actions/setup-dotnet@v6
with:
dotnet-version: 10.0.x
- name: Restore
run: dotnet restore Sbroenne.ExcelMcp.sln
# Release build validates compilation and runs the source generators, so
# the audit scripts below see the generated CLI/MCP surface. Mirrors the
# pre-commit Release build gate (NuGetAudit disabled here — the Dependency
# Review workflow owns vulnerability scanning).
- name: Build (Release)
run: dotnet build Sbroenne.ExcelMcp.sln --configuration Release --no-restore -p:NuGetAudit=false
- name: Pre-commit hook regression tests
run: dotnet test tests/ExcelMcp.SkillGeneration.Tests/ExcelMcp.SkillGeneration.Tests.csproj --configuration Release --no-build --no-restore --filter "Feature=PreCommit" --blame-hang-timeout 60s
# --- Excel-free correctness gates (ported from scripts/pre-commit.ps1) ---
- name: COM object leak check
shell: pwsh
run: |
& scripts/check-com-leaks.ps1
if ($LASTEXITCODE -ne 0) { throw "COM object leaks detected. All dynamic COM objects must be released in finally blocks." }
- name: Core coverage + naming audit
shell: pwsh
run: |
& scripts/audit-core-coverage.ps1 -CheckNaming -FailOnGaps
if ($LASTEXITCODE -ne 0) { throw "Coverage or naming gaps detected. Every Core method must be exposed via the MCP Server with a matching name." }
- name: MCP actions have Core implementations
shell: pwsh
run: |
& scripts/check-mcp-core-implementations.ps1
if ($LASTEXITCODE -ne 0) { throw "MCP enum actions without a matching Core Command implementation were found." }
- name: Success flag check
shell: pwsh
run: |
& scripts/check-success-flag.ps1
if ($LASTEXITCODE -ne 0) { throw "Success flag violations detected (Success=true with an ErrorMessage set)." }
- name: Shared bootstrap template drift check
shell: pwsh
run: |
& scripts/Build-BootstrapScripts.ps1 -Check
if ($LASTEXITCODE -ne 0) { throw "Bootstrap drift detected. Regenerate the canonical download.ps1 template output before committing." }
- name: Documentation tool/operation counts
shell: pwsh
run: |
& scripts/check-doc-counts.ps1 -SkipBuild
if ($LASTEXITCODE -ne 0) { throw "A doc advertises a tool/operation count that does not match the code-derived canonical count." }
- name: Dynamic cast audit (documented ((dynamic)) casts)
shell: pwsh
run: |
& scripts/check-dynamic-casts.ps1
if ($LASTEXITCODE -ne 0) { throw "Undocumented ((dynamic)) casts detected. Each cast needs a justification comment." }
- name: Plugin README check
shell: pwsh
run: |
& scripts/check-plugin-readmes.ps1
if ($LASTEXITCODE -ne 0) { throw "Plugin README validation failed." }
# --- Excel-free build smoke checks (absorbed from the old Build CLI /
# Build MCP Server workflows) ---
- name: Verify CLI build + help (no Excel required)
shell: pwsh
run: |
$exe = "src/ExcelMcp.CLI/bin/Release/net10.0-windows/excelcli.exe"
if (-not (Test-Path $exe)) { throw "excelcli.exe not found at $exe" }
$help = & $exe --help 2>&1
if ($LASTEXITCODE -ne 0) { throw "excelcli --help failed with exit code $LASTEXITCODE" }
Write-Host "excelcli --help OK"
- name: Verify MCP Server build
shell: pwsh
run: |
$exe = "src/ExcelMcp.McpServer/bin/Release/net10.0-windows/Sbroenne.ExcelMcp.McpServer.exe"
if (-not (Test-Path $exe)) { throw "MCP Server exe not found at $exe" }
if (-not (Test-Path "src/ExcelMcp.McpServer/bin/Release/net10.0-windows/.mcp/server.json")) {
Write-Warning "MCP server.json configuration not found"
}
Write-Host "MCP Server build OK"
- name: Integration tests note
shell: pwsh
run: |
Write-Host "Full Excel integration tests, the CLI workflow smoke test, and the MCP smoke test all require Microsoft Excel."
Write-Host "This GitHub-hosted 'windows-latest' runner has no Excel, so this job does not run them."
Write-Host "Run '& .\scripts\Test-E2E.ps1' locally and attest the result in the pull request testing checklist."
- name: Upload CLI build artifacts
if: ${{ github.event_name == 'pull_request' || github.event_name == 'push' }}
uses: actions/upload-artifact@v7
with:
name: ExcelMcp-CLI-${{ github.sha }}
path: src/ExcelMcp.CLI/bin/Release/net10.0-windows/
if-no-files-found: error
- name: Upload MCP Server build artifacts
if: ${{ github.event_name == 'pull_request' || github.event_name == 'push' }}
uses: actions/upload-artifact@v7
with:
name: ExcelMcp-MCP-Server-${{ github.sha }}
path: src/ExcelMcp.McpServer/bin/Release/net10.0-windows/
if-no-files-found: error