Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
61 commits
Select commit Hold shift + click to select a range
66c3562
Add --report-jsonl to capture raw per-test outcomes for the dashboard
gaurav Aug 26, 2026
d3f0552
Generate the dashboard report.json and history.jsonl from raw outcomes
gaurav Aug 26, 2026
9605a26
Replace the website's superseded tools with a test-report dashboard
gaurav Aug 26, 2026
fe29aa0
Regenerate and deploy the dashboard daily instead of on releases
gaurav Aug 26, 2026
714447d
Read the Google Sheet ID from BABEL_VALIDATION_SHEET_ID instead of ch…
gaurav Aug 27, 2026
f22b926
Read the blocklist sheet ID from the environment too
gaurav Aug 27, 2026
725aaca
Redesign the dashboard: status table in deployment order, filter pane…
gaurav Aug 27, 2026
c21b87a
Temporarily trigger the dashboard workflow on pushes to this branch
gaurav Aug 27, 2026
eb9ea05
Fix the CLAUDE.md example for running a single Google Sheet test row
gaurav Aug 27, 2026
b38c78f
Move deploymentOrder.js out of src/lib/, which the root .gitignore sw…
gaurav Aug 27, 2026
7aa38b3
Pass the tests path explicitly so xdist workers see the pytest options
gaurav Aug 27, 2026
ccf5694
Normalize node-ID keys so issue tests and service links survive 'pyte…
gaurav Aug 27, 2026
80f750a
Note the xdist-path and gitignore-lib traps in CLAUDE.md
gaurav Aug 27, 2026
8029211
Keep shared dashboard links pointing where they say
gaurav Aug 27, 2026
95b12c6
Stop one odd record or half-configured target from sinking the report
gaurav Aug 27, 2026
9de751f
Create the --report-jsonl parent directory, and truncate it per run
gaurav Aug 27, 2026
ac880f5
Remove the pre-merge push trigger from the dashboard workflow
gaurav Aug 27, 2026
a961187
Add 'npm run fetch-data' to pull the published dashboard data
gaurav Aug 27, 2026
d6947d2
Drop history runs that recorded no test results at all
gaurav Aug 27, 2026
da0b68d
Test the dashboard's URL, filter and pagination logic with vitest
gaurav Aug 27, 2026
b34aff4
Give the dashboard a page shell: nav bar, cards, footer, dark mode
gaurav Aug 27, 2026
40a6c13
Move the report's non-rendering logic into src/reportData.js
gaurav Aug 27, 2026
14900d5
Split the dashboard into an overview and a results page
gaurav Aug 27, 2026
8e25f2e
Group failures by the shape they make across the pipeline
gaurav Aug 27, 2026
b039c7f
Lead the run history with what changed since the previous run
gaurav Aug 27, 2026
4e5c273
Shade only the status rows where environments should agree
gaurav Aug 27, 2026
203860e
Run the dashboard workflow from this branch instead of #118's
gaurav Aug 27, 2026
4f3c9b9
Regenerate package-lock.json so npm ci can install it
gaurav Aug 27, 2026
2a2e895
Build the website on Node 24 in CI, matching the lockfile's npm
gaurav Aug 27, 2026
a099561
Cover the overview page and the filter chips, and note the npm ci trap
gaurav Aug 27, 2026
92db930
Merge branch 'main' into redesign-dashboard
gaurav Aug 31, 2026
133cdfc
Keep the address bar on /results/, and share only pages that exist
gaurav Aug 31, 2026
696ca8f
Drop only the unparseable line from the run history, not the whole file
gaurav Aug 31, 2026
37994c3
Shade nothing when environments have no majority value
gaurav Aug 31, 2026
d5a9157
Set scroll-padding-top on the root element, where it has an effect
gaurav Aug 31, 2026
7f38485
Drop the test-redis target now that NodeNorm Redis in Test is switche…
gaurav Aug 31, 2026
42bc4a5
Correct the dashboard workflow's own estimate of how long it takes
gaurav Aug 31, 2026
184eec7
Take the workflow's target list from targets.ini, not a second copy o…
gaurav Aug 31, 2026
d15eb45
Label a sheet row by its number alone when it has nothing else
gaurav Aug 31, 2026
2fd3fff
Replace the Astro starter README with one about this site
gaurav Aug 31, 2026
77971a1
Record the two traps this round cost time on
gaurav Aug 31, 2026
689ad61
Pin read_targets, now that the workflow's run loop comes from it
gaurav Aug 31, 2026
521c461
Say so when an environment is missing from the promotion order
gaurav Aug 31, 2026
ced7bd7
Let a manual dispatch supersede a running dashboard build
gaurav Aug 31, 2026
c38c992
Document the environment with env.default, and tell agents not to rea…
gaurav Sep 1, 2026
74f83fb
Trigger a dashboard run against the rotated sheet secrets
gaurav Sep 1, 2026
b3fdac2
Point the sequential-loop note at #122
gaurav Sep 1, 2026
611b2bd
Remove the temporary push trigger now the redesign is verified
gaurav Sep 1, 2026
5f90b3b
Tell a broken test run apart from a failing one
gaurav Sep 1, 2026
a8af4c1
Fail a target that exited cleanly without writing any results
gaurav Sep 1, 2026
cccc6ba
Stop one raw record with a non-string msg from sinking the whole report
gaurav Sep 1, 2026
e591185
Keep unattributed tests out of the results table
gaurav Sep 1, 2026
a3511a4
Make ?env= reject an Object.prototype key instead of matching every row
gaurav Sep 1, 2026
bfb90e9
Give the pinned ?test= row its own heading rather than repeating a kind
gaurav Sep 1, 2026
d989d24
Colour a drop in passing tests as a regression, not an improvement
gaurav Sep 1, 2026
487b111
Record that a URL parameter must not index a JSON-parsed object
gaurav Sep 1, 2026
3abd90e
Drop non-finite numbers from /status rather than writing invalid JSON
gaurav Sep 1, 2026
ba0dd00
Count errors as a change on the History page
gaurav Sep 1, 2026
19c97e7
Report a target that appeared or disappeared between runs
gaurav Sep 1, 2026
8480db1
Actually disable the pagination arrows at each end
gaurav Sep 1, 2026
c8e8777
Show an errored count on the environment cards that have one
gaurav Sep 1, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
164 changes: 164 additions & 0 deletions .github/workflows/dashboard.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,164 @@
# Runs the full validation suite against every environment daily, then builds
# and deploys the dashboard website to GitHub Pages.
name: Test dashboard

on:
schedule:
- cron: '30 6 * * *'
workflow_dispatch:

permissions:
contents: write # the deploy action pushes to the gh-pages branch
issues: read # the GitHub issue tests read this repo's issues

concurrency:
group: dashboard-deploy
# Only a manual dispatch supersedes what is already running: you press Run
# workflow because you want fresh output now, and the run it cancels would have
# produced the same two files, so nothing is lost by killing it. A scheduled run
# never cancels anything — a cron run must not be able to kill a slower one that
# is mid-flight, because that is the run that appends the day's history line —
# and neither does a push, which queues behind whatever is deploying rather than
# racing it.
cancel-in-progress: ${{ github.event_name == 'workflow_dispatch' }}

jobs:
test-and-deploy:
runs-on: ubuntu-latest
timeout-minutes: 350
steps:
- uses: actions/checkout@v4

- name: Install uv
uses: astral-sh/setup-uv@v6

- name: Run the test suite against every target
env:
GITHUB_TOKEN: ${{ github.token }}
# The Google Sheet IDs are secrets: they must never be checked in or
# appear in anything this workflow publishes.
BABEL_VALIDATION_SHEET_ID: ${{ secrets.BABEL_VALIDATION_SHEET_ID }}
BABEL_VALIDATION_BLOCKLIST_SHEET_ID: ${{ secrets.BABEL_VALIDATION_BLOCKLIST_SHEET_ID }}
# A nonzero pytest exit (failing tests) is normal here: the report is
# the artifact. `timeout 45m` caps the damage from a hung or down
# environment, whose per-test timeouts would otherwise add up.
# ponytail: sequential loop, and ~25 of the run's ~26 minutes. Splitting it
# into a matrix job with artifact merging is #122; the 6h job limit is not
# the reason to do it, the feedback loop is.
# The explicit `tests` path is required: without a path argument,
# pytest-xdist workers do not load tests/conftest.py early enough to
# know --target/--report-jsonl, and every worker dies at argparse.
run: |
mkdir -p raw
# Ask the report generator which targets exist rather than keeping a
# second list here. The two silently disagreed once already: adding
# [test-redis] to targets.ini put it in every table on the site — as a
# permanently unreachable environment, sorted after prod, and as an
# always-empty column in every ?sig= signature — because read_targets
# takes every section but localhost while this loop did not run it.
targets=$(uv run python -c "from src.babel_validation.tools.generate_report import read_targets; print(' '.join(read_targets('tests/targets.ini')[0]))")
echo "Running against: $targets"
broken=""
for t in $targets; do
# `|| status=$?` rather than a bare call: steps run under `bash -e`,
# so an unguarded nonzero exit would abort the loop at the first
# target with a failing test — which is most of them.
status=0
timeout 45m uv run pytest tests --target "$t" -n 8 -m "not unit" \
--report-jsonl "raw/$t.jsonl" || status=$?
case "$status" in
0|1)
# 0 = everything passed, 1 = tests failed. Failing tests are the
# artifact this workflow exists to publish, so neither is a
# problem with the run.
#
# But the command is `uv run pytest`, and uv exits 1 for its own
# failures too — a failed dependency sync, a resolution error —
# before pytest ever starts. That target then has no records, the
# report renders it as unreachable during the run, and the job
# goes green: exactly the silent partial failure this triage
# exists to catch. pytest_configure truncates raw/$t.jsonl before
# collection, so an empty file means pytest never got that far.
if [ ! -s "raw/$t.jsonl" ]; then
echo "::error title=Target run broke::pytest for $t exited $status without writing any results"
broken="$broken $t"
fi
;;
124)
# `timeout` killed it: the environment hung. That is a finding
# about the deployment, not about us, and the report already
# renders the target as unreachable — so say it loudly and carry
# on rather than going red every day an environment is down.
echo "::warning title=Target timed out::$t exceeded 45m; it has no results in this report"
;;
*)
# 2 interrupted (a collection error — a missing sheet ID lands
# here), 3 internal error, 4 usage error, 5 nothing collected.
# None of these are findings about a deployment: they mean the
# run did not do what its green tick suggests.
echo "::error title=Target run broke::pytest for $t exited $status; no usable results"
broken="$broken $t"
;;
esac
done
# Recorded, not raised here: the report is still worth generating and
# publishing for the targets that did work. The last step turns this
# into a failed job, after the deploy.
echo "BROKEN_TARGETS=$broken" >> "$GITHUB_ENV"

- name: Fetch the previous run history
# The published file is the source of truth: the deploy action
# force-pushes gh-pages as a single commit, so the branch is not an
# append log. The Pages CDN caches for ~10 minutes, so a manual
# dispatch right after a deploy could drop one history line — fine at
# a daily cadence.
run: |
curl -fsSL -o old_history.jsonl \
https://translatorsri.github.io/babel-validation/data/history.jsonl \
|| : > old_history.jsonl

- name: Generate the report
run: |
uv run python -m src.babel_validation.tools.generate_report \
--raw-dir raw --targets-ini tests/targets.ini \
--history-in old_history.jsonl --out-dir website/public/data

# Node 24 for npm 11 — see the note in tests.yaml. Without this the job
# gets the runner's default Node and fails at npm ci, four hours into the
# run, after every test has already been paid for.
- uses: actions/setup-node@v4
with:
node-version: 24
cache: npm
cache-dependency-path: website/package-lock.json

- name: Build the website
working-directory: website
run: |
npm ci
npm run build
touch dist/.nojekyll

- name: Deploy to GitHub Pages
uses: JamesIves/github-pages-deploy-action@v4
with:
folder: website/dist

- name: Fail if any target's run broke
# Last, so the report for the targets that worked is already published.
# `all_targets_unreachable` in generate_report already stops a run where
# *everything* died; this catches the partial case, which otherwise
# renders as "environment unreachable" and reads as someone else's fault.
if: env.BROKEN_TARGETS != ''
run: |
echo "These targets produced no usable results:${BROKEN_TARGETS}" >&2
echo "See the annotations above for each one's pytest exit code." >&2
exit 1

- name: Upload raw outcomes for debugging
if: always()
uses: actions/upload-artifact@v4
with:
name: raw-jsonl
path: raw/
retention-days: 14
29 changes: 0 additions & 29 deletions .github/workflows/deploy-website-to-gh-pages.yaml

This file was deleted.

24 changes: 24 additions & 0 deletions .github/workflows/tests.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -35,3 +35,27 @@ jobs:
# secret instead.
- name: Run unit tests
run: uv run pytest -m unit -v

website-tests:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4

# Node 24 for npm 11: the lockfile npm 11 writes omits `optional: true` on
# platform-specific packages, and npm 10 then refuses to install it
# (EBADPLATFORM on lightningcss-android-arm64). Keep this in step with the
# same block in dashboard.yaml.
- uses: actions/setup-node@v4
with:
node-version: 24
cache: npm
cache-dependency-path: website/package-lock.json

# The dashboard's URL round-tripping, filtering and pagination: logic the
# Python tests cannot see, and where a shared link quietly losing its page
# looks like nothing is wrong.
- name: Run the website unit tests
working-directory: website
run: |
npm ci
npm test
Loading
Loading