From 559dc03bb5698ada08f6e72b6b97b0875f1d398c Mon Sep 17 00:00:00 2001 From: claude Date: Sun, 19 Jul 2026 21:43:39 +0000 Subject: [PATCH] ci(infra): build images locally in test jobs; publish post-merge The test jobs were pulling a pre-published infra rootfs artifact from the registry, which made CI dependent on an out-of-band publish step. Test suites should be self-contained. - Set BOT_BOTTLE_INFRA_BUILD=local in integration-firecracker and coverage so both jobs build agent and infra images from the checked-out source. - Add publish-infra.yml: a separate workflow that fires after the test workflow succeeds on main, builds the same rootfs, and uploads it to the Gitea generic-package registry for operators who want the pull path. - Registry credentials (BOT_BOTTLE_INFRA_ARTIFACT_TOKEN) are only needed by the post-merge publish job, never by test jobs. Co-Authored-By: Claude Sonnet 4.6 --- .gitea/workflows/publish-infra.yml | 31 ++++++++++++++++++++++++++++++ .gitea/workflows/test.yml | 16 +++++++++++---- 2 files changed, 43 insertions(+), 4 deletions(-) create mode 100644 .gitea/workflows/publish-infra.yml diff --git a/.gitea/workflows/publish-infra.yml b/.gitea/workflows/publish-infra.yml new file mode 100644 index 0000000..401d95d --- /dev/null +++ b/.gitea/workflows/publish-infra.yml @@ -0,0 +1,31 @@ +# Build and publish the Firecracker infra rootfs artifact after tests pass on main. +# +# The test suite builds images locally (BOT_BOTTLE_INFRA_BUILD=local). This +# workflow is the separate publish step: it runs AFTER the test workflow +# completes on main pushes, builds the same rootfs, and uploads it to the +# Gitea generic-package registry so operators can pull it without Docker. +# +# Auth: BOT_BOTTLE_INFRA_ARTIFACT_TOKEN must be a Gitea token with +# write:package on the target owner (set in repo secrets). + +name: publish-infra + +on: + workflow_run: + workflows: [test] + types: [completed] + branches: [main] + +jobs: + publish: + runs-on: [self-hosted, kvm] + # Only publish when the test workflow succeeded — never on a failing main. + if: github.event.workflow_run.conclusion == 'success' + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Build and publish infra rootfs artifact + env: + BOT_BOTTLE_INFRA_ARTIFACT_TOKEN: ${{ secrets.BOT_BOTTLE_INFRA_ARTIFACT_TOKEN }} + run: python3 -m bot_bottle.backend.firecracker.publish_infra diff --git a/.gitea/workflows/test.yml b/.gitea/workflows/test.yml index 4027ef6..6fc341b 100644 --- a/.gitea/workflows/test.yml +++ b/.gitea/workflows/test.yml @@ -25,10 +25,8 @@ on: - '.gitea/workflows/**.yml' - 'scripts/**' - 'README.md' - # The Firecracker infra artifact the integration/coverage jobs pull is - # versioned by a content hash of the Dockerfiles (+ bot_bottle/, init); - # pyproject.toml drives the in-image package install. Changes here alter - # what those jobs build/pull, so they must re-run the suite. + # Dockerfiles and pyproject.toml are baked into the infra rootfs; a + # change here alters what the integration/coverage jobs build locally. - 'Dockerfile*' - 'pyproject.toml' pull_request: @@ -120,6 +118,10 @@ jobs: - name: Run integration tests (firecracker) env: BOT_BOTTLE_BACKEND: firecracker + # Build images locally from the checked-out source — never pull a + # pre-published artifact during tests. Publishing happens post-merge + # in publish-infra.yml. + BOT_BOTTLE_INFRA_BUILD: local run: python3 -m unittest discover -t . -s tests/integration -v # Combined unit+integration coverage + the diff-coverage gate (the hard @@ -138,6 +140,10 @@ jobs: # # See #414 for the planned follow-up: artifact-based coverage combination # (run tests once in their respective jobs, combine .coverage files here). + # + # BOT_BOTTLE_INFRA_BUILD=local: build images from source here, never pull a + # pre-published artifact. Registry publishing runs post-merge in + # publish-infra.yml, gated on tests passing. coverage: timeout-minutes: 15 runs-on: [self-hosted, kvm] @@ -166,6 +172,8 @@ jobs: # module to install into anyway. `scripts/coverage.sh` + # `diff_coverage.py` need only `coverage` (not pylint/pyright). - name: Combined coverage (unit + integration, incl. firecracker) + env: + BOT_BOTTLE_INFRA_BUILD: local run: PYTHON=python3 bash scripts/coverage.sh critical - name: Diff-coverage gate (changed lines >= 90%)