Watch
1
0
Fork
You've already forked SouveraineOS
0
SouveraineOS/tools/README.md
Fimeg f7b4b04525 tools: read CI outcomes from the API, and do it automatically
A rotted action pin skipped the publish job rather than failing it, so
edge stopped publishing and the phone sat on an old build for a working
day with nothing saying why. The runner journal logs task pickup and
never outcome, which is what was being read.

ci-status.sh asks Gitea instead, drilling run -> job -> log tail on a
failure. claude-hook-push-ci.sh runs it after every git push so the
answer arrives without anyone choosing to look. TASK-42 has the
three-layer plan; this is layer 1.
2026-07-28 17:38:44 -04:00

69 lines
2.9 KiB
Markdown

# tools
Cross-repo operational tooling. Lives here rather than in a component repo
because it reaches across all of them.
## `ci-status.sh` — what did CI actually do
./ci-status.sh # infer the repo from the cwd's git remote
./ci-status.sh souveraine # name it
./ci-status.sh souveraine 10 # last 10 runs instead of 3
./ci-status.sh --all # newest run for every repo we own
Exits 0 when the newest run is green or still going, 1 when it failed, so it
works as a gate as well as a report. On a failure it drills down by itself: the
run, then which job, then the tail of that job's log with the docker noise
stripped.
Token comes from `$GITEA_TOKEN`, else `~/.git-credentials`, else the Pixel3Arch
remote URL. No new copy of a secret in the tree.
### Read this before trusting anything else for build status
**`journalctl -u gitea-runner` is not a status source.** It logs task *pickup*
and never *outcome*, so a run that died thirty seconds in still shows as
activity. That habit produced three confident wrong diagnoses in a row on
2026-07-28 while the correct answer sat in the API untouched.
The failure that prompted all this: a pinned action SHA rotted, `rust-test` died
before compiling, and so `aarch64-artifact` was **skipped** — not failed. The run
looked unremarkable, `edge` silently stopped publishing, and the phone kept
installing a build from days earlier for a working day. Full write-up and the
three-layer fix in `../docs/tasks/42-build-outcomes-are-invisible.md`.
## `claude-hook-push-ci.sh` — the same thing, without having to remember
A Claude Code `PostToolUse` hook on `Bash`. It ignores everything that is not a
`git push`, and after one it prints that repo's CI status.
Wired in `~/.claude/settings.json`:
```json
"hooks": {
"PostToolUse": [
{ "matcher": "Bash",
"hooks": [ { "type": "command",
"command": "/home/casey/Projects/SouveraineOS/tools/claude-hook-push-ci.sh" } ] }
]
}
```
It always exits 0 — a status reporter that can block a push is a worse problem
than the one it solves.
**What it cannot do.** A cross build takes ~20 minutes, so straight after a push
the honest answer is usually "running". It reliably catches the fail-fast class
— pin rot, lint, attribution, a broken workflow edit — which is what actually
bites. Catching "publishing quietly stopped" needs the staleness comparison in
TASK-42 layer 2, which belongs to `souveraine-updater`, not to a longer sleep
in a hook.
## Pinning actions, since this is where it cost us
- A **tag's** SHA is immutable. `actions/checkout@34e1148` (v4) survived
untouched.
- A **branch tip's** SHA is not. `dtolnay/rust-toolchain` force-moves `stable`
every Rust release, which orphaned the pin — the commit still exists as a
GitHub object but is reachable from no ref, so act cannot resolve it.
- If an action must track a branch, pin to a commit on an **append-only**
branch and say in a comment why that branch was chosen.