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>
trx-rs SDK image
A single container image that is the canonical build environment for trx-rs,
used both by CI and by developers. It bakes in the pinned Rust toolchain
(matching rust-toolchain.toml) and every build dependency, so the compiler
and clippy are identical everywhere — no "works on my machine".
| File | Purpose |
|---|---|
Containerfile |
The SDK image (Debian + build deps + pinned Rust + Node + git). |
runner-config.example.yaml |
Example act_runner config for the CI VM (Docker executor). |
Build and publish
# from the repo root
podman build -t git.haxx.space/sjg/trx-rs/sdk:latest container
podman login git.haxx.space
podman push git.haxx.space/sjg/trx-rs/sdk:latest
Tag with the Rust version too (e.g. :1.97.1) if you want reproducible pins.
Make the package public (Gitea → Packages → the image → Settings) so the CI
runner and developers can pull it without credentials. If you keep it private,
add credentials: under the workflow's container: and log the runner into the
registry.
Developer use
Reproducible one-off build, no local toolchain needed:
podman run --rm -it -v "$PWD":/work -w /work \
git.haxx.space/sjg/trx-rs/sdk:latest \
cargo build --release
Or open the repo in the image via VS Code / JetBrains "Reopen in Container"
(.devcontainer/devcontainer.json points at the same image).
Building outside the container? rust-toolchain.toml pins the same rustc, so
rustup installs the matching toolchain automatically.
CI use
.gitea/workflows/ci.yml runs the lint and test jobs inside this image
via the container: key, so they skip all setup and go straight to cargo.
The reuse job stays on the upstream fsfe/reuse-action (a Docker action the
Docker executor launches as a sibling container) — nothing REUSE-related is
baked into the SDK.
Compilation cache (sccache)
The SDK image ships sccache. It is
enabled via RUSTC_WRAPPER=sccache in CI and the devcontainer (not repo-wide,
so plain cargo builds outside the SDK are unaffected).
- CI persists the cache on the runner host — create the dir once:
mkdir -p /var/cache/sccache. It is bind-mounted into each job container at/sccache(seerunner-config.example.yaml), so cache survives across runs and is shared between the lint/test jobs and both projects. - Devcontainer uses a named volume (
trx-rs-sccache). - Check effectiveness with
sccache --show-stats(the CI jobs print it).
CARGO_INCREMENTAL=0 is set wherever sccache is on, since sccache cannot cache
incremental artifacts.
CI runner (Alpine / OpenRC)
The runner uses the Docker executor (not the host executor): per-job
container isolation and standard ubuntu-latest semantics. act_runner runs
as an OpenRC service. Files provided:
| File | Purpose |
|---|---|
act_runner.openrc |
OpenRC init script (supervise-daemon, depends on docker). |
act_runner.confd.example |
Per-instance conf.d settings for multi-runner hosts. |
Cap the thread budget. In a VM, pin its vCPUs to specific host threads (libvirt/KVM):
<vcpu placement='static'>2</vcpu>
<cputune>
<vcpupin vcpu='0' cpuset='4'/>
<vcpupin vcpu='1' cpuset='5'/>
</cputune>
On bare metal, the container.options: "--cpus=2" and capacity: 1 in
runner-config.example.yaml already bound each runner.
Set it up:
# 1. Docker + a dedicated user with socket access
apk add docker docker-cli
rc-update add docker default && rc-service docker start
adduser -S -D -H -h /var/lib/act_runner act
addgroup act docker
# 2. act_runner binary (static Go build, works on musl)
curl -fsSL -o /usr/local/bin/act_runner \
https://gitea.com/gitea/act_runner/releases/download/v0.2.11/act_runner-0.2.11-linux-amd64
chmod +x /usr/local/bin/act_runner
# 3. Config + register one runner per project (scope keeps their jobs apart)
install -Dm644 container/runner-config.example.yaml /etc/act_runner/trx-rs.yaml
install -d -o act /var/lib/act_runner/trx-rs
su act -s /bin/sh -c 'cd /var/lib/act_runner/trx-rs && \
act_runner register --no-interactive \
--instance https://git.haxx.space --token <TOKEN> \
--name trx-rs-ci \
--labels "ubuntu-latest:docker://catthehacker/ubuntu:act-latest"'
# 4. OpenRC service (repeat the symlink+conf.d for the second project)
install -m755 container/act_runner.openrc /etc/init.d/act_runner
ln -s act_runner /etc/init.d/act_runner.trx-rs
install -m644 container/act_runner.confd.example /etc/conf.d/act_runner.trx-rs
rc-update add act_runner.trx-rs default
rc-service act_runner.trx-rs start
Check it with rc-service act_runner.trx-rs status and
tail -f /var/log/act_runner.trx-rs.log.