ci: registry login before pulling CHROMELESS_IMAGE (first real-image run found the gap) #50
Loading…
Add table
Add a link
Reference in a new issue
No description provided.
Delete branch "fix/ci-registry-login"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Setting
CHROMELESS_IMAGEflipped smoke/e2e/native-peer-gate from skip-green to real runs — and all four image consumers immediately failedno basic auth credentials(registry.triform.cloud requires auth; job-container docker daemons don't inherit the runner pod's imagePullSecrets).Login step before each pull site (ci smoke, both gate jobs, e2e before compose-up). Conditional on the
REGISTRY_PASSWORDsecret existing → forks/GitHub with a public image keep working.--password-stdin, registry host derived from the image ref.Secrets are set on the repo. This PR's own CI is the verification: smoke/gates/e2e should now pull and RUN the cr7727-0a0d28c67858 image — the first-ever real-image CI execution.
🤖 Generated with Claude Code
Setting CHROMELESS_IMAGE flipped smoke/e2e/both gates from skip-green to actually running the worker, and every one of them failed. Four independent causes, each pre-existing and invisible while the jobs skipped. All four were reproduced in-cluster inside the real ci-tools job container before fixing. 1. setup-node installs a glibc node onto a musl runner (killed 4 jobs) ubuntu-latest maps to ci-tools:latest = Alpine/musl. actions/setup-node@v4 downloads the official glibc node tarball; it extracts fine, so setup-node reports success ("Adding to the cache", "npm: 10.8.2"), and then nothing can exec it -- /lib64/ld-linux-x86-64.so.2 does not exist: .../node: cannot execute: required file not found exec .../node: no such file or directory <- post-steps, exit 255 The poisoned PATH persists, so EVERY post step of EVERY JS action 255s and the job fails even when its work passed: Lint's tsc + all 217 vitest tests PASSED, then Post-Node/Post-Go/Post-checkout each 255'd -> job failed. Both native-peer gates died with nothing of theirs having run. setup-go is unaffected only because Go ships static binaries. Fix: drop setup-node; use the node ci-tools already ships (v22, musl) and assert it exists. Verified in-container: npm ci + tsc + 15 files/217 tests green on the image's own node. 2. smoke asserted an idle-watchdog the M7 image doesn't have Step 3 polled 20s for /var/log/supervisor/idle-watchdog.log to prove IDLE_TIMEOUT_S propagated. supervisord.phase2.conf deliberately has no idle-watchdog program -- the native peer owns lifecycle -- so the log can never appear and the smoke could never pass on any cr7727-* image. Fix: probe the baked conf for the program; skip honestly when absent. 3. smoke's image fallback built a Dockerfile deleted in OSS-W1 Every resolve branch fell back to a docker build of infra/Dockerfile, removed when M7 deleted capture/streamer-page/. So "image absent" surfaced as "lstat infra: no such file or directory" / "docker build failed". That is exactly what a pruned image looks like here: dind-gc's high-water image-prune with until=30m evicts the image mid-job (observed live -- it ate the image between two assertions of one gate run). Fix: pull when a tag is named, and say so when that fails. 4. e2e ran compose subcommands without CHROMELESS_IMAGE compose.yaml declares it REQUIRED via the interpolation-error form, and every subcommand interpolates the whole file. status/log-capture/teardown each lacked it in their own env -> "required variable CHROMELESS_IMAGE is missing a value", and the always() teardown failed the job by itself. Fix: pass it to every compose step; make teardown non-fatal + add an explicit rm -f sweep (a real cbwrtc-chromium was found still running 40min after its job died, respawning chromium every 30s). Also e2e: probe whether the docker daemon shares the job's filesystem before bringing the stack up. It does not here (job container -> sibling dind over tcp), so compose's bind-mounts cannot resolve: the prometheus.yml file-mount failed dir-onto-file, and the ../client dir-mounts would have silently given an EMPTY tree -- specs failing against a stack that never contained the app. Now it skips with a notice rather than a red that says nothing. Verified: fixed smoke run end-to-end against registry.triform.cloud/chromeless/chromeless:cr7727-f9e46a1434b5 inside ci-tools on forgejo-runner-7 -- all 7 steps, real navigation to example.com, 17121-byte valid PNG, "container-boot: PASS". Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>Round 2 of the same exercise. The node fix worked -- both gates now actually RUN (node: v22.15.1) and assertions #1/#2 PASS against the real image with `mode: docker` evidence for the first time, and Container smoke test went green. What remained were three further defects, each with a different cause. 1. upload-artifact@v4 cannot work on this host (failed 3 jobs) It RESOLVES on the mirror -- so it passed the existing action lint, which only checks resolvability -- and then aborts at runtime: GHESNotSupportedError: @actions/artifact v2.0.0+, upload-artifact@v4+ and download-artifact@v4+ are not currently supported on GHES. v4 talks to GitHub's artifact backend service, which Forgejo does not implement. Because the steps carried `if: always()`, they failed jobs whose own work had PASSED: the scaffold gate printed `verdict=PASS exitCode=0` and was then reported red by its own artifact upload. Fix: print the payload into the run log (the log IS the artifact here), and teach tools/lint/workflow_actions_lint.py this second failure class -- resolvable is not the same as runnable. Checked tree-wide per the repo convention before landing: it found two MORE latent instances (harness-loopback.yml, release.yml) that had never been exercised. Both fixed rather than exempted; release.yml's SBOM now goes to the registry via cosign, which this host can actually do. 2. hadolint was unpinned and unthresholded Lint passed at 09:10Z and failed at 13:06Z with an unchanged Dockerfile. `hadolint/hadolint:latest` was rebuilt at 12:53Z -- 13 min before the failing run -- and the new version reports two more findings. No --failure-threshold was set either, so hadolint's default (info) applied and ANY info-level finding failed the job. Both findings are deliberate: DL3025 (HEALTHCHECK must be shell form -- the check is `curl … || exit 1`, which exec form cannot express) and DL3066 (`USER cbuser` is a named user on purpose). Fix: pin v2.12.0 + --failure-threshold warning. Verified on a runner: rc=0 at BOTH thresholds, confirming the pin alone is the fix and the threshold is defence in depth. 3. the strict gate is unsatisfiable by construction Strict requires every assertion PASS, and counts NOT_YET_IMPLEMENTED as FAIL. But #3 codec-cap and #4 native-peer are still M0 shims that return NYI unconditionally, and #3's real probe needs `ctx.client` from the R7 boot harness -- which runGate never constructs. So no --position and no image can make strict green until M1/M3 wire those probes. It only ever "passed" by skipping, before CHROMELESS_IMAGE was set; running it for the first time exposed that. Fix: continue-on-error, so it still reports the honest status of the M7 surface without failing PRs on its own unimplemented probes. The scaffold job remains the required check per verification/README.md; strict becomes required when M1+M3 land and the flag comes off. Also: e2e still carried its own setup-node -- same glibc-on-musl trap fixed in the other two workflows last commit. Removed. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>