[chore](trx-rs): shared SDK image for CI and developers #5

Merged
sjg merged 8 commits from ci/sdk-image into main 2026-08-02 17:23:58 +02:00
Owner

Summary

Introduce a single SDK image as the canonical build environment for
trx-rs, used both by CI and by developers, and move CI onto a Docker-executor
runner. This replaces the host-executor Podman approach, whose repeated
failures (Docker actions unsupported, reuse packaging, dpkg-lock collisions)
were all executor artifacts rather than real CI problems.

What changed

  • container/Containerfile — reworked from an act_runner image into the
    SDK build image: Debian + build deps (opus, alsa, soapysdr, clang) + a
    pinned Rust toolchain (rustfmt/clippy) + Node (for JS actions).
  • rust-toolchain.toml — pins the toolchain to match the image. Besides
    reproducibility, this ends the "CI clippy is newer than local" version skew
    that caused several earlier rounds of churn.
  • .gitea/workflows/ci.ymllint/test now run inside the SDK image
    via container: (straight to cargo, no setup). reuse returns to
    fsfe/reuse-action — under the Docker executor it runs as a sibling
    container, so nothing REUSE-related is baked into the SDK (this removes the
    NoEncodingModuleError problem entirely).
  • .devcontainer/devcontainer.json — developers get the same image via
    "Reopen in Container".
  • container/runner-config.example.yaml — Docker-executor act_runner
    config for the CI VM, capped (capacity: 1, --cpus=2) for a 2-thread
    budget.
  • Removed the obsolete host-executor entrypoint/config/Quadlet files.

Rollout

  1. Build & publish the SDK image (public, so pulls need no creds):
    podman build -t git.haxx.space/sjg/trx-rs-sdk:latest container && podman push …
  2. Stand up the CI VM (2 pinned vCPUs), install Docker + act_runner, register a
    Docker-executor runner per project (see container/README.md).
  3. Merge this PR.

Notes

  • Supersedes the host-executor direction that landed via PR #4.
  • The SDK image ties CI to a maintained image; that is the intended trade for
    a shared, reproducible toolchain.

🤖 Generated with Claude Code

https://claude.ai/code/session_01NFpGtGTWUEYXLwZeZs2RAV

## Summary Introduce a single **SDK image** as the canonical build environment for trx-rs, used both by CI and by developers, and move CI onto a Docker-executor runner. This replaces the host-executor Podman approach, whose repeated failures (Docker actions unsupported, `reuse` packaging, dpkg-lock collisions) were all executor artifacts rather than real CI problems. ## What changed - **`container/Containerfile`** — reworked from an act_runner image into the SDK build image: Debian + build deps (opus, alsa, soapysdr, clang) + a **pinned** Rust toolchain (rustfmt/clippy) + Node (for JS actions). - **`rust-toolchain.toml`** — pins the toolchain to match the image. Besides reproducibility, this ends the "CI clippy is newer than local" version skew that caused several earlier rounds of churn. - **`.gitea/workflows/ci.yml`** — `lint`/`test` now run *inside* the SDK image via `container:` (straight to cargo, no setup). `reuse` returns to `fsfe/reuse-action` — under the Docker executor it runs as a sibling container, so nothing REUSE-related is baked into the SDK (this removes the `NoEncodingModuleError` problem entirely). - **`.devcontainer/devcontainer.json`** — developers get the same image via "Reopen in Container". - **`container/runner-config.example.yaml`** — Docker-executor act_runner config for the CI VM, capped (`capacity: 1`, `--cpus=2`) for a 2-thread budget. - Removed the obsolete host-executor entrypoint/config/Quadlet files. ## Rollout 1. Build & publish the SDK image (public, so pulls need no creds): `podman build -t git.haxx.space/sjg/trx-rs-sdk:latest container && podman push …` 2. Stand up the CI VM (2 pinned vCPUs), install Docker + act_runner, register a Docker-executor runner per project (see `container/README.md`). 3. Merge this PR. ## Notes - Supersedes the host-executor direction that landed via PR #4. - The SDK image ties CI to a maintained image; that is the intended trade for a shared, reproducible toolchain. 🤖 Generated with [Claude Code](https://claude.com/claude-code) https://claude.ai/code/session_01NFpGtGTWUEYXLwZeZs2RAV
sjg added 5 commits 2026-08-02 11:33:30 +02:00
Rework container/ from a host-executor act_runner image into a single
"SDK" build image used everywhere: as the CI job container (Docker
executor) and by developers locally / via .devcontainer. It bakes in a
pinned Rust toolchain and all build dependencies, so CI and every
developer share the exact same rustc/clippy.

- container/Containerfile: SDK image (Debian + deps + pinned Rust + Node).
- rust-toolchain.toml: pin the toolchain to match the image; also ends the
  "CI clippy newer than local" version skew.
- .gitea/workflows/ci.yml: lint/test run inside the SDK image via
  `container:`; reuse returns to fsfe/reuse-action (Docker executor runs
  it as a sibling container, so nothing REUSE-related is baked in).
- .devcontainer/devcontainer.json: dev use of the same image.
- container/runner-config.example.yaml: Docker-executor runner config for
  the CI VM, capped for a 2-thread budget.
- Drop the obsolete host-executor entrypoint/config/Quadlet units.

Assisted-By: Claude Code (claude-opus-4)
Claude-Session: https://claude.ai/code/session_01NFpGtGTWUEYXLwZeZs2RAV
Signed-off-by: Stan Grams <sjg@haxx.space>
Match the image name that was pushed to the registry
(git.haxx.space/sjg/trx-rs/sdk) across the workflow, devcontainer and
README.

Assisted-By: Claude Code (claude-opus-4)
Claude-Session: https://claude.ai/code/session_01NFpGtGTWUEYXLwZeZs2RAV
Signed-off-by: Stan Grams <sjg@haxx.space>
The runner host is Alpine (OpenRC, no systemd). Add an OpenRC init script
for act_runner (supervise-daemon, depends on docker) plus a conf.d
example for running one instance per project, and rewrite the runner
section of the README with Alpine setup steps (apk docker, dedicated user
in the docker group, register, service install).

Assisted-By: Claude Code (claude-opus-4)
Claude-Session: https://claude.ai/code/session_01NFpGtGTWUEYXLwZeZs2RAV
Signed-off-by: Stan Grams <sjg@haxx.space>
Bake sccache into the SDK image and enable it via RUSTC_WRAPPER in CI and
the devcontainer (not repo-wide, so non-SDK builds are unaffected).

- container/Containerfile: install the sccache musl binary.
- ci.yml: RUSTC_WRAPPER=sccache, CARGO_INCREMENTAL=0, SCCACHE_DIR=/sccache,
  cache size cap, plus a `sccache --show-stats` step per job.
- runner-config.example.yaml: bind-mount /var/cache/sccache into job
  containers so the cache persists across runs and is shared between jobs.
- .devcontainer: enable sccache with a named cache volume.

Assisted-By: Claude Code (claude-opus-4)
Claude-Session: https://claude.ai/code/session_01NFpGtGTWUEYXLwZeZs2RAV
Signed-off-by: Stan Grams <sjg@haxx.space>
[chore](trx-rs): run the frontend job in the SDK image
CI / lint (pull_request) Failing after 4s
CI / test (pull_request) Failing after 2s
CI / frontend (pull_request) Failing after 27s
CI / reuse (pull_request) Successful in 5s
22ff1349f3
The frontend job was added while CI still targeted host-executor runners,
so it never gained the `container:` key the lint and test jobs use.  On
the Docker executor it lands on a bare job container and fails the same
way the Rust jobs did before this branch: `npm` is missing, the Chromium
install shells out to `sudo apt-get`, and `npm run verify-generated`
regenerates the Rust wire contracts, so it needs `cargo` too.

Run it in the SDK image, which already ships Node.js, Chromium at the
path the browser smoke test defaults to, and the pinned Rust toolchain.
Installing Chromium per run is then redundant.

Drop the job's trailing `reuse lint`.  The SDK image deliberately carries
nothing REUSE-related, and the separate `reuse` job lints the whole
repository with the upstream action, generated assets included.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01GdyUjuXejCEfiub675z6cz
Signed-off-by: Stan Grams <sjg@haxx.space>
sjg force-pushed ci/sdk-image from 99ae2a5fd7 to 22ff1349f3 2026-08-02 11:33:30 +02:00 Compare
sjg added 1 commit 2026-08-02 12:14:48 +02:00
[chore](trx-rs): build the SDK image natively on x86_64 and arm64
CI / reuse (pull_request) Successful in 3s
CI / lint (pull_request) Has been cancelled
CI / test (pull_request) Has been cancelled
CI / frontend (pull_request) Has been cancelled
c2455bb08c
The sccache release asset is per-architecture and the Containerfile
hardcoded the x86_64 triple, so an arm64 build produced an image whose
sccache binary could not execute.  Everything else in the image — the
Debian base, the build dependencies, Node.js and rustup — already
resolves per architecture, so that one URL was what pinned the image to
amd64 and forced Rosetta or qemu on Apple Silicon.

Resolve the triple from `uname -m`, which reflects the build platform
under plain docker/podman build as well as buildx, unlike the
BuildKit-only TARGETARCH.

Document publishing `:latest` as a manifest list built natively on a host
of each architecture, since a single-architecture tag sends the other
side back to emulation, and note that Apple's `container` CLI needs
Rosetta for its BuildKit helper VM regardless of the target.

Pick the act_runner download by architecture for the same reason.

Verified on arm64: the case arm selects
sccache-v0.8.2-aarch64-unknown-linux-musl, and the installed binary
reports `sccache 0.8.2` running natively.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01GdyUjuXejCEfiub675z6cz
Signed-off-by: Stan Grams <sjg@haxx.space>
sjg added 1 commit 2026-08-02 16:49:50 +02:00
[chore](trx-rs): force-pull the SDK image on the CI runner
CI / lint (pull_request) Failing after 3s
CI / test (pull_request) Failing after 2s
CI / frontend (pull_request) Failing after 28s
CI / reuse (pull_request) Successful in 3s
26b00608b2
The workflow references the SDK image by the moving `:latest` tag, and
act_runner skips the pull when a local copy of that tag already exists:
the job log reports `docker pull ... forcePull=false` followed by
`Image exists? true`.  Pushing a rebuilt image therefore changes nothing
until someone pulls on the VM by hand, and the run fails as though the
image never gained the tool that was added to the Containerfile —
`sccache` resolving as "No such file or directory" while the pinned
toolchain from an earlier build of the same tag resolves fine.

Set `force_pull: true` so a pushed image is what actually runs, and
document the manual refresh for runners configured before this change.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01GdyUjuXejCEfiub675z6cz
Signed-off-by: Stan Grams <sjg@haxx.space>
sjg added 1 commit 2026-08-02 16:54:19 +02:00
[chore](trx-rs): allow the sccache bind mount on the CI runner
CI / test (pull_request) Successful in 13m51s
CI / frontend (pull_request) Successful in 5m1s
CI / test (push) Successful in 7m43s
CI / frontend (push) Successful in 2m18s
CI / reuse (pull_request) Successful in 4s
CI / lint (pull_request) Successful in 4m22s
CI / lint (push) Successful in 2m23s
CI / reuse (push) Successful in 1m18s
2f4973ed70
act_runner validates every bind mount against `valid_volumes`, which
defaults to an empty allowlist, so the `-v /var/cache/sccache:/sccache`
in `container.options` was dropped on every job.  The only trace is one
line in the job log — "[/var/cache/sccache] is not a valid volume, will
be ignored" — after which SCCACHE_DIR points at a path that does not
outlive the container, so the shared compilation cache never persisted.

Allow that one path rather than the `**` wildcard: the runner is the only
thing mounting host directories here, and a narrow allowlist keeps a
workflow from mounting arbitrary host paths into a job container.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01GdyUjuXejCEfiub675z6cz
Signed-off-by: Stan Grams <sjg@haxx.space>
sjg merged commit 2f4973ed70 into main 2026-08-02 17:23:58 +02:00
sjg deleted branch ci/sdk-image 2026-08-02 17:23:58 +02:00
Sign in to join this conversation.
No Reviewers
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: sjg/trx-rs#5