dashboard.yml
Telemetry dashboard build and publish. Lives in the redline-metrics repo.
What this is
Rebuilds the telemetry dashboard and publishes it to GitHub Pages, via scripts/build-dashboard.mjs.
How to onboard it
Copy this file into the redline-metrics repository, set REDLINE_ORG_READ_TOKEN and acknowledge Pages visibility there. Coverage additionally needs registry.json readable from the source repo — which the Redline Registry workflow now publishes nightly.
- Lives in: The redline-metrics repo — not this one.
- Trigger: Daily at 05:30 UTC, plus workflow_dispatch, plus automatically once Redline Collect or Redline Seed Canary finishes.
How to use it
Nothing, normally — it runs itself daily. Run scripts/build-dashboard.mjs locally against a copy of data/ to preview a metric or chart change.
What a run does, in order:
- Same Pages-visibility gate as the inbox.
- Counts onboarded repos by reading registry.json from the source repo, for a coverage figure. A failed read omits the figure rather than reporting zero.
- Builds dist/index.html with scripts/build-dashboard.mjs, then deploys, then opens or updates a tracking issue on failure.
Works, but only where installed: same Pages-visibility gate as the inbox, plus REDLINE_ORG_READ_TOKEN and collected telemetry to summarise. The coverage figure works again now that registry.json exists — but this workflow file lives in the metrics repo, so the fix reaches the live dashboard only once someone copies it across; redline sync would do that, but it targets registered product repositories, not the metrics repo.
Expected output
A static dashboard on GitHub Pages: acted-on rate, weekly trend, seed recall history, and the rules most worth tuning. Coverage is reported as instrumented-against-onboarded, read from registry.json; if that read fails the figure is omitted rather than shown as zero, because zero would look like a finding.
How to edit it
- Edit the YAML in workflows/ or .github/workflows/workflows/ holds files destined for other repositories; .github/workflows/ is this repository's own CI. The two are not interchangeable — check where this one lives before editing.
- actionlintCI lints .github/workflows/*.yml, workflows/*.yml and templates/redline.yml together. workflows/ is pointed at explicitly because actionlint's own discovery would skip it.
- node scripts/check-pins.mjsIf you add a third-party action, pin it to a 40-character commit SHA with a trailing # vX.Y.Z comment. First-party actions/* are referenced by tag. The pin checker re-resolves the SHA against the tag the comment claims.
- node scripts/validate.mjsAsserts the workflow files the bundle depends on still exist, and that the gate's job ids still match the required check name derived from them.
The full file
# Lives in the redline-metrics repo. Rebuilds the Redline dashboard from collected
# telemetry and publishes it to GitHub Pages.
#
# SECURITY: the page lists repo names, rule ids and finding counts. Set the Pages site to
# private or internal visibility BEFORE enabling this. The build refuses to run until
# PAGES_VISIBILITY_ACKNOWLEDGED is set as a repository variable.
name: Redline Dashboard
on:
schedule:
- cron: '30 5 * * *'
workflow_dispatch:
workflow_run:
# Rebuild as soon as new telemetry lands, so the page is never a day behind the data.
workflows: [Redline Collect, Redline Seed Canary]
types: [completed]
permissions:
contents: read
pages: write
id-token: write
concurrency:
group: dashboard
cancel-in-progress: true
jobs:
build:
runs-on: ubuntu-latest
timeout-minutes: 15
steps:
- name: Confirm Pages visibility was reviewed
env:
ACK: ${{ vars.PAGES_VISIBILITY_ACKNOWLEDGED }}
run: |
if [[ "${ACK:-}" != "private" && "${ACK:-}" != "internal" ]]; then
echo "::error::Set the repository variable PAGES_VISIBILITY_ACKNOWLEDGED to 'private' or 'internal' after confirming the Pages site is not public. This page exposes org-wide delivery detail."
exit 1
fi
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: 22
- name: Count onboarded repos
id: coverage
env:
GH_TOKEN: ${{ secrets.REDLINE_ORG_READ_TOKEN }}
SOURCE: ${{ vars.REDLINE_SOURCE || format('{0}/redline', github.repository_owner) }}
run: |
set -euo pipefail
# registry.json is the derived register of onboarded repositories, refreshed
# nightly by the source repo's Redline Registry workflow. Coverage is
# instrumented-vs-onboarded, so partial coverage cannot read as health. A
# failed read omits the figure rather than reporting zero — absent is honest,
# zero is a lie that looks like a finding.
# Written to a file rather than counted here: the dashboard reads the whole
# register for the enforcement ladder, not just its length, and a count
# alone cannot say how much of the estate is actually enforcing.
if gh api "repos/$SOURCE/contents/registry.json" --jq '.content' 2>/dev/null | base64 -d > registry.json; then
echo "found=true" >> "$GITHUB_OUTPUT"
else
rm -f registry.json
echo "found=false" >> "$GITHUB_OUTPUT"
echo "::warning::Could not read registry.json from $SOURCE — coverage and the ladder will be omitted."
fi
# No checkout of the Redline source, and no copy of its scripts in this
# repository. The command ships in the package; this repo owns its data and
# nothing else.
- name: Build dashboard
env:
STANDARDS_VERSION: ${{ vars.STANDARDS_VERSION }}
run: |
npx --yes "redlinegate@0.0.3" metrics dashboard \
--org "${{ github.repository_owner }}" \
--data data \
--days 90 \
--out dist \
--registry registry.json
- uses: actions/upload-pages-artifact@v3
with:
path: dist
deploy:
needs: build
runs-on: ubuntu-latest
environment:
name: github-pages
url: ${{ steps.deployment.outputs.page_url }}
steps:
- id: deployment
uses: actions/deploy-pages@v4
alert:
needs: [build, deploy]
if: failure()
runs-on: ubuntu-latest
permissions:
contents: read
issues: write
steps:
- name: Open or update a failure issue
env:
GH_TOKEN: ${{ github.token }}
REPO: ${{ github.repository }}
RUN: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}
run: |
set -euo pipefail
title="Redline dashboard build is failing"
existing=$(gh issue list --repo "$REPO" --state open --search "$title in:title" --json number --jq '.[0].number // ""')
if [[ -n "$existing" ]]; then
gh issue comment "$existing" --repo "$REPO" --body "Still failing: $RUN"
else
gh issue create --repo "$REPO" --title "$title" \
--body "The dashboard build failed, so the published page is now stale. Run: $RUN"
fi