mirror of
https://github.com/okieselbach/AutopilotMonitor.git
synced 2026-10-24 12:42:38 +00:00
1b27babd17
zizmor (static analysis for GitHub Actions) now runs twice: locally before a
workflow commit (hard rule in .claude/CLAUDE.md) and as the `zizmor` job next to
the pin gate in action-pins.yml (zizmorcore/zizmor-action pinned by SHA, zizmor
1.30.1 pinned, SARIF into Code scanning, severity low and above). Like the pin
gate it reports and does not block. zizmor.yml holds the single reasoned ignore.
Baseline fixes so the gate starts green:
- persist-credentials: false on every checkout except combine-rules.yml, the one
job that pushes with the checkout credentials (ignored in zizmor.yml, with reason)
- publish-scripts.yml: id-token: write moves from the workflow to the publish job;
indexnow-notify.yml: permissions: {}
- deploy-mcp.yml: env values referenced as shell variables inside bash steps
instead of template expansion
- pester.yml / publish-scripts.yml: reusable workflow via the self-repository form
$/ (GitHub changelog 2026-07-30); the pin gate treats $/ like ./
- combine-rules.yml: ajv-cli installed with npm ci from rules/scripts/package-lock.json
(no ts-node peer, no lifecycle scripts) instead of an ad-hoc global install;
Dependabot watches /rules/scripts
- azure/login pin comment corrected to the tag the SHA actually is (v3.0.2; v3
moved on to v3.1.0), no runtime change
- Dependabot: 7-day cooldown on every ecosystem (security updates are exempt)
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
189 lines
9.1 KiB
YAML
189 lines
9.1 KiB
YAML
name: Deploy Web (Static Web App)
|
|
|
|
# Build here, upload with the pinned SWA CLI. No third-party action sees the deploy token
|
|
# (the CLI is Microsoft-published npm code at an exact version), the build runs on the same
|
|
# path ci.yml tests (setup-node 22 + npm ci + next build) instead of Oryx guessing inside the
|
|
# action's container, and the exported site is attested like the agent ZIP and the MSI.
|
|
#
|
|
# PR preview environments were dropped with the action: their per-PR URLs are never
|
|
# registered as redirect URIs, so nobody could log in on them anyway.
|
|
|
|
on:
|
|
push:
|
|
branches:
|
|
- main
|
|
paths:
|
|
- 'src/Web/autopilot-monitor-web/**'
|
|
- '.github/workflows/deploy-web.yml'
|
|
workflow_dispatch:
|
|
|
|
permissions:
|
|
contents: read
|
|
id-token: write # keyless Sigstore signing (artifact attestation)
|
|
attestations: write # store the provenance attestation
|
|
|
|
env:
|
|
PORTAL_VERSION_URL: https://portal.autopilotmonitor.com/version.json
|
|
VERSIONS_CONTAINER: https://autopilotmonitoreu.blob.core.windows.net/versions
|
|
WEB_DIR: src/Web/autopilot-monitor-web
|
|
# Exact version: `npx --package` resolves from the registry at run time, so the pin is the
|
|
# only thing that keeps the deploy step's code fixed. Bump deliberately.
|
|
SWA_CLI_VERSION: 2.0.10
|
|
|
|
jobs:
|
|
build_and_deploy_job:
|
|
runs-on: ubuntu-latest
|
|
name: Build and Deploy Job
|
|
steps:
|
|
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7
|
|
with:
|
|
persist-credentials: false
|
|
lfs: false
|
|
fetch-depth: 0 # Full history needed for git-based sitemap lastmod dates
|
|
|
|
# Only the version manifest at the very end needs Azure; logging in here rather than
|
|
# next to that step keeps a credential problem from surfacing after a live deploy.
|
|
- name: Login to Azure (OIDC)
|
|
uses: azure/login@7ddb5af1ef8758cf1353cf3b42f940aee27ba21c # v3.0.2
|
|
with:
|
|
client-id: ${{ secrets.AZURE_CLIENT_ID }}
|
|
tenant-id: ${{ secrets.AZURE_TENANT_ID }}
|
|
subscription-id: ${{ secrets.AZURE_SUBSCRIPTION_ID }}
|
|
|
|
# The customer docs repo is the single source of the "What's new" panel: the prebuild
|
|
# step (scripts/whats-new/build-whats-new.js) turns changelog/*.md into
|
|
# public/whats-new.json and dates every bullet via git blame — hence the full history.
|
|
# Public repo, no token. Editing the docs does NOT update the live panel until this
|
|
# workflow runs again (push under src/Web/** or a manual dispatch); versions/web.json
|
|
# records which docs commit is live.
|
|
- name: Check out documentation bundle
|
|
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7
|
|
with:
|
|
persist-credentials: false
|
|
repository: autopilotmonitor/autopilotmonitor-docs
|
|
path: whats-new-docs
|
|
fetch-depth: 0
|
|
|
|
- name: Resolve docs commit
|
|
id: docs
|
|
run: echo "sha=$(git -C whats-new-docs rev-parse HEAD)" >> "$GITHUB_OUTPUT"
|
|
|
|
- uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7
|
|
with:
|
|
node-version: '22'
|
|
cache: npm
|
|
cache-dependency-path: ${{ env.WEB_DIR }}/package-lock.json
|
|
|
|
- name: Install dependencies
|
|
working-directory: ${{ env.WEB_DIR }}
|
|
run: npm ci
|
|
|
|
# next.config output:'export' -- `next build` writes the static site to out/
|
|
# (prebuild: sync-error-codes, generate-lastmod, generate-version, whats-new).
|
|
- name: Build
|
|
working-directory: ${{ env.WEB_DIR }}
|
|
env:
|
|
NEXT_PUBLIC_ENTRA_CLIENT_ID: ${{ vars.NEXT_PUBLIC_ENTRA_CLIENT_ID }}
|
|
# Dual app-reg window: previous app registration (unset = single-app behaviour)
|
|
# and the default app for browsers without a stored selection (legacy|primary).
|
|
NEXT_PUBLIC_ENTRA_LEGACY_CLIENT_ID: ${{ vars.NEXT_PUBLIC_ENTRA_LEGACY_CLIENT_ID }}
|
|
NEXT_PUBLIC_ENTRA_DEFAULT_APP: ${{ vars.NEXT_PUBLIC_ENTRA_DEFAULT_APP }}
|
|
NEXT_PUBLIC_ENTRA_REDIRECT_URI: ${{ vars.NEXT_PUBLIC_ENTRA_REDIRECT_URI }}
|
|
NEXT_PUBLIC_ENTRA_POST_LOGOUT_REDIRECT_URI: ${{ vars.NEXT_PUBLIC_ENTRA_POST_LOGOUT_REDIRECT_URI }}
|
|
NEXT_PUBLIC_API_BASE_URL: ${{ vars.NEXT_PUBLIC_API_BASE_URL }}
|
|
NEXT_PUBLIC_PLATFORM_STATS_MANIFEST_URL: ${{ vars.NEXT_PUBLIC_PLATFORM_STATS_MANIFEST_URL }}
|
|
NEXT_PUBLIC_APPINSIGHTS_CONNECTION_STRING: ${{ secrets.NEXT_PUBLIC_APPINSIGHTS_CONNECTION_STRING }}
|
|
# Build stamp for public/version.json (scripts/generate-version.js in prebuild);
|
|
# the verify step below polls the deployed file for exactly this commit.
|
|
GITHUB_SHA: ${{ github.sha }}
|
|
# Docs checkout for public/whats-new.json; the prebuild fails in CI without it.
|
|
WHATS_NEW_DOCS_DIR: ${{ github.workspace }}/whats-new-docs
|
|
run: |
|
|
npm run build
|
|
test -f out/version.json || { echo "out/version.json missing - export did not run"; exit 1; }
|
|
test -f out/whats-new.json || { echo "out/whats-new.json missing - whats-new prebuild did not run"; exit 1; }
|
|
|
|
# One archive of the exact bytes that get uploaded, so the attestation has a single subject.
|
|
- name: Package export
|
|
id: package
|
|
run: |
|
|
archive="portal-${GITHUB_SHA::7}.tar.gz"
|
|
tar -C "$WEB_DIR/out" -czf "$archive" .
|
|
echo "archive=$archive" >> "$GITHUB_OUTPUT"
|
|
sha256sum "$archive"
|
|
|
|
# Sigstore keyless provenance: binds the export digest to this workflow run (repo, ref,
|
|
# commit, workflow file) in a public transparency log.
|
|
# Verify: gh attestation verify portal-<sha>.tar.gz --repo okieselbach/AutopilotMonitor
|
|
- name: Attest build provenance (portal export)
|
|
uses: actions/attest-build-provenance@4d101475d8b20a2381f78447822ac1eab6504dd8 # v4
|
|
with:
|
|
subject-path: ${{ steps.package.outputs.archive }}
|
|
|
|
- name: Upload build artifact
|
|
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7
|
|
with:
|
|
name: portal-${{ github.sha }}
|
|
path: ${{ steps.package.outputs.archive }}
|
|
|
|
# Upload only -- out/ is already built. --swa-config-location points at the app root,
|
|
# where staticwebapp.config.json lives (next export does not copy it into out/).
|
|
- name: Deploy to Static Web App
|
|
env:
|
|
SWA_CLI_DEPLOYMENT_TOKEN: ${{ secrets.AZURE_SWA_DEPLOY_TOKEN }}
|
|
run: |
|
|
npx --yes --package "@azure/static-web-apps-cli@${SWA_CLI_VERSION}" swa deploy \
|
|
--app-location "$WEB_DIR" \
|
|
--output-location out \
|
|
--swa-config-location "$WEB_DIR" \
|
|
--env production \
|
|
--no-use-keychain
|
|
|
|
# The CLI reporting success proves the upload, not that the production host serves the
|
|
# new build -- verify against the live /version.json (Cache-Control: no-store via
|
|
# staticwebapp.config.json) before publishing the version manifest.
|
|
- name: Verify deployed portal build is live
|
|
shell: pwsh
|
|
run: |
|
|
$expectedCommit = '${{ github.sha }}'.Substring(0, 7)
|
|
$deadline = [DateTime]::UtcNow.AddMinutes(5)
|
|
$attempt = 0
|
|
|
|
while ([DateTime]::UtcNow -lt $deadline) {
|
|
$attempt++
|
|
try {
|
|
$v = Invoke-RestMethod -Uri '${{ env.PORTAL_VERSION_URL }}' -Method Get -TimeoutSec 30 -Headers @{ 'Cache-Control' = 'no-cache' }
|
|
if ($v.commit -eq $expectedCommit) {
|
|
Write-Host "Live after $attempt attempt(s): web @ $($v.commit), built $($v.buildUtc)"
|
|
exit 0
|
|
}
|
|
Write-Host "Attempt ${attempt}: serving $($v.commit) -- waiting for $expectedCommit"
|
|
} catch {
|
|
Write-Host "Attempt ${attempt}: /version.json not answering yet -- $($_.Exception.Message)"
|
|
}
|
|
Start-Sleep -Seconds 15
|
|
}
|
|
|
|
throw "Portal did not serve commit $expectedCommit within 5 minutes -- deployment not verified, version manifest NOT published"
|
|
|
|
# Cross-component deploy record, written last and only on a verified deploy --
|
|
# same contract as versions/backend.json and versions/mcp.json. docsCommit says which
|
|
# docs revision the live What's new panel was generated from (as for the MCP corpus).
|
|
- name: Publish version manifest
|
|
shell: pwsh
|
|
run: |
|
|
$manifest = [ordered]@{
|
|
component = 'web'
|
|
commit = '${{ github.sha }}'.Substring(0, 7)
|
|
deployedUtc = [DateTime]::UtcNow.ToString('o')
|
|
runId = '${{ github.run_id }}'
|
|
docsCommit = '${{ steps.docs.outputs.sha }}'.Substring(0, 7)
|
|
} | ConvertTo-Json
|
|
|
|
$authHeaders = & ./.github/scripts/Get-StorageAuthHeaders.ps1
|
|
$url = "${{ env.VERSIONS_CONTAINER }}/web.json"
|
|
# no-cache: rotating pointer, must never be served stale by any fronting cache.
|
|
Invoke-RestMethod -Uri $url -Method Put -Headers ($authHeaders + @{ 'x-ms-blob-type' = 'BlockBlob'; 'x-ms-blob-cache-control' = 'no-cache' }) -Body $manifest -ContentType 'application/json' | Out-Null
|
|
Write-Host "Published versions/web.json"
|
|
Write-Host $manifest
|