Skip to content

ci-metrics

ci-metrics #1008

Workflow file for this run

# ci-metrics — the recorder (ADR-0047). Every time a tracked workflow
# completes, this sweeps the recent completed runs, appends anything not yet
# recorded to the orphan `ci-metrics` branch (NDJSON, one shard per month),
# and re-renders DASHBOARD.md + site/summary.json there. phux-site's /ci
# page reads summary.json straight off raw.githubusercontent.com, so the
# public dashboard updates the moment this pushes — no site rebuild.
#
# SINGLE WRITER: nothing else may push to `ci-metrics`. The concurrency
# group serializes collectors; GitHub keeps only the newest queued run when
# several pile up, and that is fine BY DESIGN — collect-runs.sh sweeps a
# window and diffs against what is recorded, so a cancelled collector's runs
# are picked up by the next one. The weekly scheduled sweep is the backstop
# that also refreshes the dashboard through quiet stretches.
#
# SECURITY: workflow_run executes with repo permissions, so this must never
# run code from the triggering ref. It checks out main only, treats
# artifact contents as data (schema-validated JSON lines, size-capped), and
# needs just contents:write (the metrics branch) + actions:read (the API
# sweep). GITHUB_TOKEN pushes do not re-trigger workflows, so no loops.
name: ci-metrics
on:
workflow_run:
workflows:
- ci
- stress
- observatory
- release
- release-please
- publish-crate
- conventional-commits
types: [completed]
schedule:
- cron: "45 6 * * 1" # Mondays 06:45 UTC, after the observatory
workflow_dispatch:
inputs:
window:
description: "How many recent completed runs to sweep (backfill knob)."
type: string
default: "40"
permissions:
contents: write
actions: read
concurrency:
group: ci-metrics-writer
cancel-in-progress: false
jobs:
record:
name: record + render
runs-on: ubuntu-latest
steps:
# The scripts, from main — never from the triggering ref.
- uses: actions/checkout@v7
# The data branch. Seeded once by hand (see ADR-0047); this fails
# loudly if it ever disappears rather than silently re-rooting history.
- uses: actions/checkout@v7
with:
ref: ci-metrics
path: metrics
- name: Sweep runs + render dashboard
env:
GH_TOKEN: ${{ github.token }}
GH_REPO: ${{ github.repository }}
WINDOW: ${{ inputs.window || '40' }}
run: |
set -euo pipefail
bash scripts/ci/collect-runs.sh --out metrics --window "$WINDOW"
bash scripts/ci/render-dashboard.sh metrics
- name: Push to ci-metrics
run: |
set -euo pipefail
cd metrics
git config user.name "github-actions[bot]"
git config user.email "github-actions[bot]@users.noreply.github.com"
git add -A
if git diff --cached --quiet; then
echo "nothing new to record"
exit 0
fi
git commit -m "record ci metrics ($(date -u +%Y-%m-%dT%H:%MZ))"
# The concurrency group makes a competing writer near-impossible,
# but a rebase-retry costs nothing and makes it actually impossible
# to clobber (NDJSON appends + regenerated derived files rebase
# cleanly; the derived files are re-rendered next sweep anyway).
for _ in 1 2 3; do
git push origin HEAD:ci-metrics && exit 0
git pull --rebase -X theirs origin ci-metrics || true
done
echo "::error::could not push ci-metrics after 3 attempts"
exit 1
- name: Dashboard excerpt
if: always()
run: |
if [ -f metrics/DASHBOARD.md ]; then
head -60 metrics/DASHBOARD.md >>"$GITHUB_STEP_SUMMARY"
fi