nigig-org/.forgejo/RUNNER.md
nigig-ci 422a0e3388
All checks were successful
repo hygiene / hygiene (push) Successful in 3s
docs(ci): all jobs green; note that this is a starting line
cad-module was the last red job and now passes. Board updated.

Also records that pdf.yml/fuzz reporting "skipped" is correct -- it is
gated on schedule || workflow_dispatch -- so nobody spends time
investigating it as a failure.

The caveat stays prominent: several jobs are green because their gate
is deliberately loose (the nigig-map unit-test ratchet sits at 9 real
failures, and its fmt/clippy steps are report-only). Those are listed
under Known-not-gated so a full green board is not mistaken for a
healthy codebase.
2026-08-04 06:57:21 +00:00

225 lines
7.4 KiB
Markdown

# CI runners
Until 2026-08-02 this repository had **zero registered runners and zero
workflow runs**. Seven workflow files existed and none of them had ever
executed — for any commit, on any branch, ever:
```
GET /api/v1/repos/andodeki/nigig-org/actions/runners -> []
GET /api/v1/repos/andodeki/nigig-org/actions/tasks -> {"total_count": 0}
```
A gate that never runs is not a gate. Registering the first runner
immediately turned up four defects that had been sitting on `main`,
described at the bottom of this file.
## Registering a runner
`gitdab.com` is Gitea 1.22 (Actions API compatible with
`forgejo-runner`). Any Linux host with Docker, or without it in host
mode, will do.
### 1. Get a registration token
Repo → Settings → Actions → Runners → "Create new runner", or:
```bash
curl -H "Authorization: token $GITEA_TOKEN" \
https://gitdab.com/api/v1/repos/andodeki/nigig-org/actions/runners/registration-token
```
Note this instance answers `GET` on that endpoint; `POST` returns 405.
### 2. Install and register
```bash
VERSION=6.3.1
curl -sSLo forgejo-runner \
"https://code.forgejo.org/forgejo/runner/releases/download/v${VERSION}/forgejo-runner-${VERSION}-linux-amd64"
chmod +x forgejo-runner
./forgejo-runner register --no-interactive \
--instance https://gitdab.com \
--token "<registration-token>" \
--name "<runner-name>" \
--labels 'ubuntu-latest:docker://node:20-bookworm'
```
Every job in `.forgejo/workflows/` uses `runs-on: ubuntu-latest`, so
that label must exist or nothing is ever scheduled.
**Host mode** (no Docker) also works and is what the first runs used:
```bash
--labels 'ubuntu-latest:host://-self-hosted'
```
Host mode runs jobs directly on the machine as the runner user. Jobs
`sudo apt-get install` native dependencies and write to `$HOME/.cargo`,
so use a disposable machine or a dedicated unprivileged user. Docker
mode is preferable for anything long-lived.
### 3. Run it
```bash
./forgejo-runner generate-config > config.yml
./forgejo-runner daemon --config config.yml
```
Confirm with:
```bash
curl -H "Authorization: token $GITEA_TOKEN" \
https://gitdab.com/api/v1/repos/andodeki/nigig-org/actions/runners
```
`"status": "active"` means it is polling.
## Action resolution — read this before adding `uses:`
Forgejo resolves `uses: owner/name@ref` against **`data.forgejo.org`**,
not `github.com`, and there is **no fallback**. An action that does not
exist there fails the job in `Set up job`, which cancels every
subsequent step — so the job reports failure having compiled nothing,
and the log looks like an infrastructure blip rather than a config bug.
Verify before committing:
```bash
git ls-remote --heads https://data.forgejo.org/<owner>/<name>
```
Known state:
| Action | Resolves? |
|---|---|
| `actions/checkout@v4` | yes |
| `actions/setup-java@v4` | yes |
| `actions/cache@v3` | yes |
| `actions/setup-rust@v1` | **no** |
| `android-actions/setup-android@v3` | **no** |
Prefer an inline `run:` step over a third-party action. `sms.yml`
installs the Android SDK with `cmdline-tools` directly for this reason.
## Reading job logs
The REST API on this instance returns 404 for
`/api/v1/repos/{owner}/{repo}/actions/tasks/{id}/logs`. The web UI's
JSON endpoint works:
```
POST /andodeki/nigig-org/actions/runs/<run_number>/jobs/<job_index>/attempt/1
Content-Type: application/json
{"logCursors":[{"step":0,"cursor":null,"expanded":true}, ...]}
```
`<job_index>` is the 0-based position of the job in the workflow file,
and `<run_number>` is `run_number` from the tasks API — not the task id.
Repeat with the returned `cursor` per step to page through output.
## What the first real runs found
All were invisible defects on `main`, each fixed in its own commit:
1. **`android-actions/setup-android@v3` does not exist on
data.forgejo.org.** The `android` job in `sms.yml` — the only job
that compiles the ~600 lines of JNI behind
`#[cfg(target_os = "android")]` — died in `Set up job` and cancelled
all seven of its steps.
2. **Five of six `tools/*.sh` were committed mode 100644.** Every one is
invoked as `./tools/<name>.sh`; both `pay-domain.yml` jobs died with
`Permission denied` at their first substantive step. Now gated in
`repo-hygiene.yml`.
3. **`cargo-deny --config` was passed to the binary instead of to the
`check` subcommand** in `pay-domain.yml`, so the dependency audit
exited 2 without auditing anything, skipping the three boundary gates
behind it.
4. **`repo-hygiene.yml` was never scheduled.** It was written as
```yaml
on:
push:
pull_request:
```
Valid YAML, and the spelling GitHub documents for "all branches" —
but this instance does not schedule it. The one workflow with no path
filter, whose entire purpose is to run on every commit, had never run
once. `on: [push, pull_request]` fixes it. Use the list form here.
5. **`actions/setup-rust@v1` does not exist** either, so `nigig-map.yml`
fails in `Set up job`. Not fixed — the `map` crate does not compile
on `main` regardless, so that workflow has more than a CI problem.
## Current state
Latest result per job, all on a real runner:
| Workflow | Job | Status |
|---|---|---|
| doc-engine.yml | engine, consumer | pass |
| nigig-build.yml | supply-chain, full-crate-check | pass |
| nigig-build.yml | cad-module, full-crate-check, supply-chain | pass |
| nigig-map.yml | test | pass |
| pay-domain.yml | isolated-payment-tests, payment-ui-tests | pass |
| pdf.yml | engine, makepad-integration | pass |
| pdf.yml | fuzz | skipped (schedule / workflow_dispatch only) |
| repo-hygiene.yml | hygiene | pass |
| sms.yml | gates, robius-sms, android, nigig-sms, supply-chain | pass |
**Every job passes.** `pdf.yml/fuzz` is gated on
`schedule || workflow_dispatch`, so `skipped` is its correct result on
a push.
That is a starting line, not a finish line — see below for what is
green because it is genuinely healthy versus green because the gate is
deliberately loose.
### Known-not-gated
Things that are broken but deliberately not failing a build, so nobody
mistakes silence for health:
- **`nigig-map` unit tests**: ratchet at 9 failures, not a hard gate.
530 pass; the 9 are real logic bugs (4 `mvt_parser`, 1
`overpass_parser`, 4 `sprite` classification) that were invisible
until the crate compiled.
- **`nigig-map` test targets**: `tests/ui.rs` imports
`makepad_widgets::makepad_test`; `tests/makepad_visual_tests.rs` and
`benches/tile_decode_bench.rs` import `pub(crate)` modules, and
`criterion` is not a declared dev-dependency. The workflow runs
`--lib` only for that reason.
- **`nigig-map` fmt/clippy**: report-only. 392 pre-existing fmt diffs in
`src/` (rustfmt could not parse `view.rs` before, so it silently
skipped the whole directory) and 132 clippy warnings.
### Runner capacity
One runner with `capacity: 1` serialises every job; a push touching
several filtered paths takes a while to drain. Raise `capacity`, or add
runners, if that becomes annoying.
### Writing a ratchet step
The runner executes `run:` blocks under `bash -e`. A bare
```bash
out="$(cargo test ...)"
```
aborts the step the moment the command exits non-zero — which is always
true while a ratchet baseline is above zero — so the comparison never
runs and the step fails at exactly the count it was meant to allow.
This bit nigig-map.yml on its first run. Use:
```bash
status=0
out="$(cargo test ... 2>&1)" || status=$?
```
`|| status=$?` makes it a tested compound command, which `-e` exempts.