Commit Graph
11 Commits
Author SHA1 Message Date
Omar SobhandClaude Opus 5 4d07868410 ci: stop leaking a 2.8 GB postgres volume every run
deploy / test (push) Successful in 4m43s
deploy / build (push) Successful in 4m0s
`docker rm -f` without -v orphans the anonymous volume the postgres image
declares. cm-testkit creates a database per test, so each CI run left ~2.8 GB
behind: 38 GB of dangling volumes had accumulated on gw-04, most of the 99 GB
-> 23 GB drop in free space over one day.

Note for anyone reaching for `docker volume prune` to clean this up: don't. On
gw-04 the dangling set also contained traefik-acme (Let's Encrypt certificates)
and all three CI cargo caches. Only the anonymous 64-hex volumes were safe to
remove.

Co-Authored-By: Claude Opus 5 <[email protected]>
2026-08-13 21:16:38 -07:00
Omar SobhandClaude Opus 5 25a3d6902a ci: only publish a release for an actual tag
deploy / test (push) Successful in 3m55s
deploy / build (push) Successful in 57s
On workflow_dispatch GITHUB_REF_NAME is the BRANCH, so the upload step created a
Gitea release AND a git tag both named "main" — a tag sharing the branch name,
from a run that was only meant to be a smoke test. Both have been deleted.

Gated on github.ref_type == 'tag'. A dispatch now exercises build, SBOM, sign
and offline verify, and stops there.

Co-Authored-By: Claude Opus 5 <[email protected]>
2026-08-13 21:06:44 -07:00
Omar SobhandClaude Opus 5 837a3d3ff0 ci: don't run the install rehearsal on the production gateway
deploy / test (push) Successful in 3m56s
deploy / build (push) Successful in 56s
Every other step in the release job is inert with respect to prod — build,
SBOM, sign, offline verify. The rehearsal is the only one whose purpose is to
stand a full stack up and tear it down with `down -v`, and it was doing that on
the machine serving production. On 2026-08-13 it adopted the live compose
project and destroyed clawmates_pgdata.

The script itself is now safe (unique -p, a guard against the production project
name, and a health probe pointing at the port the bundle actually publishes) and
is kept for use on a build box or throwaway VM. What changes here is only WHERE
it runs, which was the real problem: a destructive verification step does not
belong on the host it can destroy.

Releases still build, sign, verify offline in a --network none container, and
upload to Gitea.

Co-Authored-By: Claude Opus 5 <[email protected]>
2026-08-13 20:56:30 -07:00
Omar SobhandClaude Opus 5 41854c70e1 ci: the install rehearsal needs compose v2, and says so
deploy / test (push) Successful in 4m23s
deploy / build (push) Successful in 57s
The v1 fallback I added a commit ago cannot work: deploy/compose/docker-compose.yml
uses v2-only syntax — a top-level `name:` and long-form
`env_file: {path, required}` — so docker-compose 1.29 rejects the file outright
("'name' does not match any of the regexes"). A fallback that always fails is
worse than no fallback, so the script now requires v2 and fails immediately with
what to do about it.

$COMPOSE overrides the detection. gw-04 is deliberately left WITHOUT a
`docker compose` plugin: installing one system-wide would flip the production
rolling deploy (clawmates-deploy.sh prefers v2 when present) off docker-compose
v1 as an invisible side effect of a release change. The runner gets a standalone
v2 binary at /opt/act-runner/bin/docker-compose and the workflow passes it in,
so prod keeps rolling exactly as it did.

Verified on gw-04: standalone v2.32.4 runs, and `docker compose` still resolves
to nothing, so clawmates-deploy.sh takes its v1 branch unchanged.

Co-Authored-By: Claude Opus 5 <[email protected]>
2026-08-13 15:22:29 -07:00
Omar SobhandClaude Opus 5 bd1c970577 ci: let the install rehearsal use a pre-built bundler
deploy / test (push) Failing after 2m56s
deploy / build (push) Skipped
The rehearsal hardcoded `cargo build -p clawmates-bundler`, so it died with
"cargo: command not found" on the release runner — gw-04 builds Rust inside a
container and has no toolchain of its own. The release job had already built the
bundler two steps earlier, so it was also redundant work.

CLAWMATES_BUNDLER now short-circuits that build when it points at an executable,
falling back to cargo otherwise, so running the script by hand is unchanged.

Everything before this step already passed on the runner: images built, SBOMs
generated, bundle assembled and signed, and "bundle OK: 94 artifacts verified
offline" inside a --network none container.

Co-Authored-By: Claude Opus 5 <[email protected]>
2026-08-13 15:02:05 -07:00
Omar SobhandClaude Opus 5 c1642a7004 ci: copy the bundler out of the target volume
deploy / test (push) Successful in 4m24s
deploy / build (push) Successful in 58s
First dispatch failed at exit 127, "target/release/clawmates-bundler: No such
file or directory". The bundler builds inside a container where /w/target is a
NAMED VOLUME, so the binary was written somewhere no later host step can see —
the workspace's target/ stays empty. Copy it to .tools/ (bind-mounted) and
assert it landed, so the next occurrence fails at the build step with a clear
message instead of two steps later as a missing file.

deploy.yml does not hit this because it copies clawmates-node into
frontend/public/dl/ from inside the same container.

Co-Authored-By: Claude Opus 5 <[email protected]>
2026-08-13 14:51:50 -07:00
Omar SobhandClaude Opus 5 de8736c16b ci: move release.yml to Gitea and make it actually runnable
deploy / test (push) Successful in 4m37s
deploy / build (push) Successful in 56s
It could never have run as written: `runs-on: ubuntu-latest` matches no runner
on this forge, and `softprops/action-gh-release` talks to GitHub's API. There
are zero tags and zero releases, which is consistent with it never having fired.

Rewritten for this runner:
- runs-on: gw04 (the only reachable x86_64 host; prod artifacts must be amd64)
- the bundler builds in a rust container with the shared cargo cache volumes —
  gw-04 has no cargo, and installing a toolchain onto the production gateway to
  build a release is the wrong trade
- release creation + asset upload go to Gitea's own API, create-or-reuse so a
  re-run of a tag updates rather than 409s
- syft installs into the workspace, not /usr/local/bin: the host executor runs
  as root on the gateway and a release should leave nothing behind
- a disk-reclaim step, because the artifacts are GBs of image tarballs on a box
  that is also serving production. It removes only the versioned images it
  created — never a blanket prune, since clawmates/agent-*:dev exist in no
  registry and are the source of the microVM rootfs files
- workflow_dispatch added so the pipeline can be exercised without minting a tag

BUNDLE_SIGNING_KEY now exists as a repo secret (fresh ed25519 keypair; nothing
depended on a previous one). The signing and offline-verify steps are unchanged:
verification still runs inside a --network none container, which is the whole
air-gapped contract.

.github/ is now empty and removed.

Co-Authored-By: Claude Opus 5 <[email protected]>
2026-08-13 14:43:33 -07:00
Omar SobhandClaude Opus 5 af89020dfd ci: put the docker CLI on PATH for the sandbox integration tests
deploy / test (push) Successful in 4m34s
deploy / build (push) Successful in 8m42s
cm-runtime/cm-sandbox tests shell out to `docker` via std::process, so the
mounted socket alone was not enough — browser_tool failed with
`docker available: NotFound`. Mount the host binary rather than apt-installing
docker.io: the container is fresh every run, so an install would re-download
~100 MB each time and cache nothing.

Verified on gw-04 that a mounted /usr/bin/docker talks to the host daemon
(client=29.1.3 server=29.1.3), and that the agent-*:dev images these tests need
are already present there.

Co-Authored-By: Claude Opus 5 <[email protected]>
2026-08-13 10:18:01 -07:00
Omar SobhandClaude Opus 5 ee1cea72d9 ci: mount the docker socket so the testcontainers suite can run
deploy / test (push) Failing after 2m54s
deploy / build (push) Skipped
cm-files' s3_store test starts a real MinIO via testcontainers. Without the
socket it does not skip — it fails with
`Client(Init(SocketNotFoundError("/var/run/docker.sock")))`, which looks like a
broken test rather than a missing capability. It passed locally only because the
Mac's docker socket was visible to the test process.

Sibling containers testcontainers starts are reachable because the test
container already shares the host network.

Co-Authored-By: Claude Opus 5 <[email protected]>
2026-08-13 10:13:26 -07:00
Omar SobhandClaude Opus 5 8129f58845 ci: give cargo the credential for the private clawhdf5 git dep
deploy / test (push) Failing after 5m38s
deploy / build (push) Skipped
First run failed in `cargo test --workspace`: "failed to load source for
dependency clawhdf5", preceded by three "spurious network error: invalid packet
line" retries. Two separate causes, both needed:

- libgit2 cannot fetch from Gitea's smart-HTTP. images/server.Dockerfile already
  sets CARGO_NET_GIT_FETCH_WITH_CLI for exactly this; the test step did not.
- quantumclaw/clawhdf5 is private (401 anonymous), so the CLI fetch needs a
  credential. Supplied via an insteadOf rewrite from a repo secret, so the token
  is masked in logs and never committed.

The server image build does not hit this — it builds only clawmates-server,
which does not pull cm-brain's clawhdf5 path.

Co-Authored-By: Claude Opus 5 <[email protected]>
2026-08-13 10:06:34 -07:00
Omar SobhandClaude Opus 5 b1bf50160a ci: build and deploy to production from a push to main
deploy / test (push) Failing after 42s
deploy / build (push) Skipped
Closes the one manual step left in the pipeline. gw-04 has run
clawmates-deploy.timer every minute since July, pulling :latest and rolling on
drift — the CD half already worked. What was missing was anything that moved
:latest, since the old build host (tank) is packed for the move.

The runner lives on gw-04 because it is the only reachable x86_64 host and prod
images must be linux/amd64: web-01 is aarch64 and the fleet build boxes are
offline. Host executor, capacity 1, so builds serialize rather than competing
with production traffic.

Three details that are not obvious:
- `docker push :latest` does NOT move the tag on this registry once the manifest
  exists under another tag. The PUT-the-manifest step is what actually moves it,
  and its absence is how a "successful" deploy could leave prod on a stale image.
- The final step verifies the image prod is RUNNING, not the one we pushed. A
  green edge on the old image is the failure this pipeline exists to prevent.
- broker is built here too. It had no :latest tag at all, so gw-04's deploy loop
  logged a pull failure every single cycle since 2026-08-11.

Also ignore the local env backups: `.env` was ignored but `.env.bak.*` was not,
and those copies hold real credentials.

Co-Authored-By: Claude Opus 5 <[email protected]>
2026-08-13 10:03:35 -07:00