Two fixes. The first is a live regression I caused today; the second stops one
before it arms.
1. `label_id()` looked the label up in `repos/{repo}/labels` only. Labels moved
to the organisation today (weblib-archive#63) and that endpoint now returns
`[]` in all five repos, so it returned None everywhere and the whole script
became a silent no-op:
$ sync_blocked_label.py --repo weblib/weblib-archive --dry-run
skipped: no 'Status/Blocked' label in this repo
--> 0 change(s)
It failed *safe* - skipping rather than mislabelling, which is what that
docstring was written for - but a job that runs every 15 minutes reported
success while doing nothing. It now tries the repo, then the org, so it does
not care how an instance is arranged.
2. bit, 2026-09-07: "the reconciler must not touch issues that are already on
hold or abandoned". Implemented literally - neither add nor remove.
This matters because `Status/*` is becoming exclusive again. Under that,
adding `Status/Blocked` does not sit beside an existing status, it
*replaces* it - so the reconciler would silently delete a deliberate
`Status/On Hold` on its next pass. And since On Hold is exactly what makes
the backlog sweep skip an issue, a parked issue would quietly become an
available one, with nothing in the log to say why.
Verified against the live forge rather than by reading:
* add path, org-resolved: stripped Status/Blocked off cfbypass#8, dry-run
said "would add", the real run added it back
* hands-off, as a control on ONE issue with ONE open blocker, changing only
the label:
without Status/On Hold -> "would add Status/Blocked ... (blocked by #54)"
with Status/On Hold -> "hands off", 0 changes, label intact
Closes #3
Co-authored-by: bit <bit@das-labor.org>
weblib-ci
The CI scripts shared by cfbypass, weblib-archive, weblib-fs and weblib-viewer. Split out per weblib-archive#44, where they had been hand-copied into each repo and had already drifted once.
Public deliberately. Nothing here is a secret or specific to the archive's contents: a nixpkgs-pinning wrapper, a log poster and a label reconciler. Public means a consumer needs no deploy key, no ssh setup and no secret to fetch it — which was measured to be the difference between one step and three.
What is here
| file | what it does |
|---|---|
with-nixpkgs.sh |
Runs a command with one nixpkgs package on PATH, pinned to the consuming repo's flake.lock. Avoids nix shell nixpkgs#x, which re-resolves the registry and refetches a channel tarball whenever the branch moves. |
report_job_log.py |
Posts the tail of a build log as a PR comment. Exists because actions/jobs/{id}/logs returns 500 for every id on Gitea 1.25.2, so a red job otherwise says only that it failed. |
sync_blocked_label.py |
Keeps Status/Blocked in step with Gitea's dependency graph. |
All three are standard library / plain bash only. They are run, not built, so this repo has no flake.
Using it
- uses: actions/checkout@v4
- id: ci
uses: https://git.chaosbit.de/weblib/weblib-ci@main
- run: bash ${{ steps.ci.outputs.path }}/with-nixpkgs.sh python3 \
python3 ${{ steps.ci.outputs.path }}/report_job_log.py /tmp/build.log
No credentials anywhere: the repo is public, which is the point of it being so.
with-nixpkgs.sh reads the consuming repo's flake.lock relative to the
working directory, so it keeps working when invoked by absolute path from
outside the checkout.
Use the full URL, not weblib/weblib-ci@main
Measured on weblib-archive#44 (2026-09-07), one job per form because Gitea posts one commit status per job and job logs return 500:
| form | result |
|---|---|
uses: https://git.chaosbit.de/weblib/weblib-ci@main |
works |
uses: weblib/weblib-ci@main |
fails |
git clone https://…/weblib-ci.git with no credentials |
works |
steps.<id>.outputs.path, then running a tool through it |
works |
The bare owner/repo form resolves against the instance's default actions URL
rather than this host, so it has to be the full URL. Both forms failed while
this repo was private, which is the other half of why it is public — the
alternative was a deploy key and an ssh setup step in four repos.
The outputs.path row is listed separately on purpose: the action running and
its output reaching the caller are different claims, and a composite action
returning an empty string is exactly the sort of thing that looks green.
Why not a flake input
These are scripts a workflow runs, not derivations. A flake input would cost a
flake.lock bump in four repos every time one changes, and buys nothing.