# Release: build the images both deploy targets share, assemble the # SIGNED air-gapped bundle, verify it offline, and attach everything to # the tag. The signing key lives in repo secrets (BUNDLE_SIGNING_KEY, # hex ed25519 from `clawmates-bundler keygen`); the matching public key is # published out of band so customers can verify before docker load. name: release on: push: tags: ["v*"] jobs: bundle: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - uses: dtolnay/rust-toolchain@stable - uses: Swatinem/rust-cache@v2 - name: Version from tag run: echo "VERSION=${GITHUB_REF_NAME#v}" >> "$GITHUB_ENV" - name: Build images run: | docker build -t "clawmates/server:$VERSION" -f images/server.Dockerfile . docker build -t "clawmates/frontend:$VERSION" -f images/frontend.Dockerfile . docker build -t "clawmates/broker:$VERSION" -f images/broker.Dockerfile . docker build -t "clawmates/agent-base:$VERSION" images/agent-base docker build -t "clawmates/agent-browser:$VERSION" images/agent-browser docker pull postgres:16-alpine - name: SBOMs for every shipped image run: | mkdir -p dist/sboms curl -sSfL https://raw.githubusercontent.com/anchore/syft/main/install.sh \ | sh -s -- -b /usr/local/bin for image in server frontend broker agent-base agent-browser; do syft "clawmates/$image:$VERSION" -o spdx-json \ > "dist/sboms/$image.spdx.json" done - name: Save image tarballs run: | mkdir -p dist/images docker save "clawmates/server:$VERSION" -o dist/images/server.tar docker save "clawmates/frontend:$VERSION" -o dist/images/frontend.tar docker save "clawmates/broker:$VERSION" -o dist/images/broker.tar docker pull tecnativa/docker-socket-proxy:0.3 docker save tecnativa/docker-socket-proxy:0.3 -o dist/images/socket-proxy.tar docker save "clawmates/agent-base:$VERSION" -o dist/images/agent-base.tar docker save "clawmates/agent-browser:$VERSION" -o dist/images/agent-browser.tar docker save postgres:16-alpine -o dist/images/postgres.tar - name: Build bundler run: cargo build --release -p clawmates-bundler - name: Assemble and sign the bundle env: BUNDLE_SIGNING_KEY: ${{ secrets.BUNDLE_SIGNING_KEY }} run: | printf '%s' "$BUNDLE_SIGNING_KEY" > /tmp/release.key BUNDLER=target/release/clawmates-bundler ARTIFACTS="" for tar in dist/images/*.tar; do ARTIFACTS="$ARTIFACTS $tar=images/$(basename "$tar")" done for migration in migrations/*.sql; do ARTIFACTS="$ARTIFACTS $migration=migrations/$(basename "$migration")" done # shellcheck disable=SC2086 "$BUNDLER" assemble dist/bundle "$VERSION" /tmp/release.key \ deploy/compose/docker-compose.yml=compose/docker-compose.yml \ deploy/compose/clawmates.toml=compose/clawmates.toml \ deploy/compose/.env.example=compose/.env.example \ deploy/e2e/scenarios.toml=compose/scenarios.toml \ images/seccomp/agent-profile.json=seccomp/agent-profile.json \ deploy/airgapped/install.sh=install.sh \ "$BUNDLER"=bin/clawmates-bundler \ dist/sboms/server.spdx.json=sboms/server.spdx.json \ dist/sboms/frontend.spdx.json=sboms/frontend.spdx.json \ dist/sboms/agent-base.spdx.json=sboms/agent-base.spdx.json \ dist/sboms/agent-browser.spdx.json=sboms/agent-browser.spdx.json \ $ARTIFACTS chmod +x dist/bundle/bin/clawmates-bundler dist/bundle/install.sh rm /tmp/release.key - name: Verify the bundle offline (public key only) env: BUNDLE_SIGNING_KEY: ${{ secrets.BUNDLE_SIGNING_KEY }} run: | printf '%s' "$BUNDLE_SIGNING_KEY" > /tmp/release.key target/release/clawmates-bundler pubkey /tmp/release.key dist/release.pub rm /tmp/release.key # The customer's exact procedure: only the public half — and # inside a NETWORK-DISABLED container, proving verification # needs no internet (the air-gapped contract). docker run --rm --network none \ -v "$PWD/dist:/dist:ro" \ ubuntu:24.04 \ /dist/bundle/bin/clawmates-bundler verify /dist/bundle /dist/release.pub - name: Tarball run: tar -C dist -czf "clawmates-bundle-$VERSION.tgz" bundle - name: Clean-room install rehearsal run: | docker tag "clawmates/server:$VERSION" clawmates/server:latest docker tag "clawmates/frontend:$VERSION" clawmates/frontend:latest docker tag "clawmates/broker:$VERSION" clawmates/broker:latest ./scripts/rehearse-install.sh - name: Attach to release uses: softprops/action-gh-release@v2 with: files: | clawmates-bundle-*.tgz dist/release.pub