Files
meshcore-analyzer/.github/workflows
efitenandClaude Opus 5.5 59708ee72e ci: publish the README badges to a badges branch with GITHUB_TOKEN (#2202)
Fixes #2136.

## Problem

The README badges are stale: `e2e-tests` shows 821 passed (1161 when
#2136 was filed), both Go coverage badges have not changed since
2026-03-30 and frontend coverage since June. There are two causes.

1. **The PAT.** The `publish` job wrote the badge JSON to `master`
through the Contents API with `BADGE_PUSH_TOKEN`, a PAT that could push
past the branch protection on `master`. That token returns 401: run
38360160932 on 2026-10-13 logs `gh: Bad credentials (HTTP 401)` for
every badge. The step is `continue-on-error` and only echoed `Failed to
update`, so every run stayed green.
2. **Old badges inside `e2e-badges`.** `.badges/` is tracked on
`master`, so every e2e shard checkout and the aggregate checkout already
hold the old files. The shard upload and the aggregate `cp` shipped them
in `e2e-badges`, and `publish` downloaded `e2e-badges` after
`go-badges`, so the old values overwrote the fresh ones. In run
38407143126, `go-badges` holds 85.8% and 77.1%, while `e2e-badges` holds
85.4%, 70.2% and frontend 36.64%, the same as `master`'s `.badges/`.
Fixing only the token would have left three of the four README badges
frozen.

## Change

1. `5f430966` `ci:` the `publish` job pushes the badges to an
unprotected `badges` branch with its own `GITHUB_TOKEN`.
- Job-level `permissions: contents: write`. No other job gains a
permission. The checkout uses `persist-credentials: false`, since the
push passes its own token.
- The branch is a single orphan commit, force-pushed, so it does not
grow. Master push runs are already serialised by the workflow
`concurrency` group.
- Each run starts from the branch as it is and overwrites only the
badges it downloaded, so a missing artifact keeps those badges at their
previous value. The first time, when `git ls-remote --exit-code` reports
the branch absent (exit 2), it starts from `master`'s `.badges/`.
- No downloaded artifacts, or any other `ls-remote`, fetch, extract,
commit or push failure, ends in a `::warning::` in the run summary and
leaves the branch alone. The step stays `continue-on-error`, so a badge
problem never turns the pipeline red.
   - `go-badges` is downloaded after `e2e-badges`, so its files win.
2. `2134c1e5` `docs:` the four README badge URLs read
`.../CoreScope/badges/<name>.json` instead of
`.../master/.badges/<name>.json`.
3. `47e57482` `ci:` the frontend-coverage step in the e2e shards and the
aggregate badge step start from an empty `.badges/`, so `e2e-badges`
carries only what the run produced. This also stops shard 3's old
`frontend-coverage.json` from replacing the one shard 2 generates.

No PAT is involved any more, so nothing expires.

## Testing

- The publish step script, taken from the YAML, run against a local bare
repository standing in for `origin`:

| Run | Downloaded | Result on `badges` |
|---|---|---|
| 1, branch absent | a, b | a, b new; c from `.badges/`; one commit |
| 2 | c only | a, b kept from run 1; c new; still one commit |
| 3 | none | `::warning::`, branch unchanged, step exit 0 |
| 4, `origin` unreachable for the read | a | `::warning::`, branch
unchanged, step exit 0 |
| 5, push to a missing remote | a | `::warning::`, branch unchanged,
step exit 0 |

- The artifacts of run 38407143126 land flat in the download path, so
`badges-new/*.json` matches them.
- `shellcheck` at style level on the step script: no findings.
- `actionlint` 1.7.7 (shellcheck off): one finding, the self-hosted
runner label `meshcore-runner-2`, which `master` reports as well.

**Not tested:** the e2e change and the push with a real `GITHUB_TOKEN`.
Both only run on a push to `master`, so the first master run after merge
is the real test. The `badges` branch already exists (`80fda6c0`, a copy
of `master`'s `.badges/`), so the README badges keep working from the
merge on, with the old values until that run. After it, I will check the
`badges` branch contents against that run's go and frontend numbers, the
step log and the rendered README.

## Not in this PR

- Removing the now unused `.badges/` from `master`: the first run seeds
the branch from it, so that goes in a follow-up once the branch exists.
- Deleting the `BADGE_PUSH_TOKEN` secret: that needs a repository admin.
- `frontend-tests.json`: no CI step produces it and no README badge uses
it. It is carried over to the branch unchanged.

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-13 16:25:42 +02:00
..