The agent as a container image (M1.5) #115

Merged
dev-agent merged 4 commits from image into main 2026-10-07 21:48:44 +00:00
Collaborator

M1.5 of the Development Plan (X4), as planned.

What changes

  • Dockerfile:
    • Build: with uv into /opt/nisse, installed rather than editable.
    • Runtime: python:3.12-slim plus openssh-client, as user nisse (uid 10001). The image is 202 MB.
    • Stateless:
      • state goes in a /var/lib/nisse-dev volume;
      • config is mounted at /etc/nisse-dev/nisse-dev.yaml;
      • secrets come in as environment variables (--env-file);
      • SSH and deploy keys are mounted read-only wherever the config says.
    • Other stages: the default target is the agent. A test stage adds the tests and their tools over the installed package. UV_NO_SYNC=1 stops uv run from swapping in the source tree.
  • scripts/e2e.sh --image REF runs the e2e suite inside an image's test stage, on its own sshd, so a release can be checked as built.
  • CI:
    • a new image job builds both targets on every PR and checks the agent refuses to start without config. Nothing is pushed.
    • this PR's run is also the first test of whether the runner can build images. If it can't, the runner needs Docker access in its job containers (e.g. the socket mounted).
  • release.yml: on a v* tag, it builds and pushes gitea.dongar.net/dongar/nisse-dev:<tag> and :latest. It logs in as dongar with the REGISTRY_TOKEN secret, which needs write:package. That secret needs creating.
  • scripts/bringup.sh:
    • start builds the code under test into an image and runs it in the systemd unit, as root, with $E2E mounted at its own path so the config's paths still hold;
    • plan prints what it would run;
    • the build-VM set-aside reads the projects: config. The old code read the flat keys that M1.4 removed.

Checked

  • The image refuses to start without config, and runs as uid 10001.
  • scripts/e2e.sh --image nisse-dev:test -k plain_yes passed from the test stage in 2m 45s for $0.0000.

Still to do for acceptance

  1. You: create the REGISTRY_TOKEN CI secret.
  2. You (or me, if you say so): push a tag, e.g. v0.1.0.
  3. Me: scripts/e2e.sh --image gitea.dongar.net/dongar/nisse-dev:v0.1.0 (the test stage, built from the tag), as acceptance.

Deferred: the live bring-up's switch to the image, until the next live test (it also needs its own test repository).

🤖 Generated with Claude Code

https://claude.ai/code/session_01PPQ14L3LikyefCMFBqEbKE

M1.5 of the Development Plan (X4), as planned. ## What changes - **`Dockerfile`:** - **Build:** with `uv` into `/opt/nisse`, installed rather than editable. - **Runtime:** `python:3.12-slim` plus `openssh-client`, as user `nisse` (uid 10001). The image is 202 MB. - **Stateless:** - state goes in a `/var/lib/nisse-dev` volume; - config is mounted at `/etc/nisse-dev/nisse-dev.yaml`; - secrets come in as environment variables (`--env-file`); - SSH and deploy keys are mounted read-only wherever the config says. - **Other stages:** the default target is the agent. A `test` stage adds the tests and their tools over the *installed* package. `UV_NO_SYNC=1` stops `uv run` from swapping in the source tree. - **`scripts/e2e.sh --image REF`** runs the e2e suite inside an image's `test` stage, on its own sshd, so a release can be checked as built. - **CI:** - a new `image` job builds both targets on every PR and checks the agent refuses to start without config. Nothing is pushed. - this PR's run is also the first test of whether the runner can build images. If it can't, the runner needs Docker access in its job containers (e.g. the socket mounted). - **`release.yml`:** on a `v*` tag, it builds and pushes `gitea.dongar.net/dongar/nisse-dev:<tag>` and `:latest`. It logs in as `dongar` with the **`REGISTRY_TOKEN`** secret, which needs `write:package`. That secret needs creating. - **`scripts/bringup.sh`:** - `start` builds the code under test into an image and runs it in the systemd unit, as root, with `$E2E` mounted at its own path so the config's paths still hold; - `plan` prints what it would run; - the build-VM set-aside reads the `projects:` config. The old code read the flat keys that M1.4 removed. ## Checked - The image refuses to start without config, and runs as uid 10001. - `scripts/e2e.sh --image nisse-dev:test -k plain_yes` passed from the test stage in 2m 45s for $0.0000. ## Still to do for acceptance 1. **You:** create the `REGISTRY_TOKEN` CI secret. 2. **You (or me, if you say so):** push a tag, e.g. `v0.1.0`. 3. **Me:** `scripts/e2e.sh --image gitea.dongar.net/dongar/nisse-dev:v0.1.0` (the test stage, built from the tag), as acceptance. **Deferred:** the live bring-up's switch to the image, until the next live test (it also needs its own test repository). 🤖 Generated with [Claude Code](https://claude.com/claude-code) https://claude.ai/code/session_01PPQ14L3LikyefCMFBqEbKE
dev-agent added 1 commit 2026-10-07 19:51:00 +00:00
The agent as a container image (M1.5)
CI / checks (push) Skipped
CI / e2e (pull_request) Failing after 49m48s
CI / checks (pull_request) Successful in 10m0s
CI / image (pull_request) Successful in 27s
0660fa3c51
- Dockerfile: built with uv into /opt/nisse (installed, not editable),
  run on python:3.12-slim with openssh-client as user nisse; stateless,
  with state in a /var/lib/nisse-dev volume, config mounted at
  /etc/nisse-dev/nisse-dev.yaml, secrets as environment variables, keys
  mounted read-only. 202 MB. Its `test` stage adds the tests and their
  tools over the installed package (UV_NO_SYNC, so `uv run` never swaps
  in the source tree); the default target is the agent.
- scripts/e2e.sh --image REF runs the suite inside an image's test stage,
  on its own sshd.
- CI builds both images on every PR (nothing pushed); release.yml pushes
  gitea.dongar.net/dongar/nisse-dev:<tag> and :latest on a v* tag, with
  the REGISTRY_TOKEN secret.
- scripts/bringup.sh builds the code under test into an image and runs it
  in the systemd unit (root, $E2E mounted at its own path); `plan` prints
  what it would run; the build-VM set-aside reads the projects: config.

Checked: the image refuses to start without a config, runs as uid 10001;
one e2e test passed from the test stage (2m45s, $0).

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PPQ14L3LikyefCMFBqEbKE
dongar approved these changes 2026-10-07 20:39:33 +00:00
Dismissed
dev-agent added 2 commits 2026-10-07 20:52:07 +00:00
- on: release (published), not on any v* tag: only a deliberate release
  builds an image; the version is its tag without the v, plus latest.
- docker/login-action, setup-buildx and build-push-action, as the runner
  already serves them for inlaw_outlaw; the version as a build argument
  and image label.
- Comments on why the registry needs a personal token (the job token's
  empty package scope) and that packages belong to the repository owner.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PPQ14L3LikyefCMFBqEbKE
Release: the dev organization's registry, pushed by registry-push
CI / checks (push) Skipped
CI / checks (pull_request) Successful in 6m28s
CI / image (pull_request) Successful in 21s
CI / e2e (pull_request) Canceled after 1m27s
2766cf1e8e
The repository moved to the dev organization: images go to
gitea.dongar.net/dev/nisse-dev, pushed by the registry-push bot with the
organization's REGISTRY_TOKEN.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PPQ14L3LikyefCMFBqEbKE
dev-agent dismissed dongar's review 2026-10-07 20:52:07 +00:00
Reason:

New commits pushed, approval review dismissed automatically according to repository settings

dongar approved these changes 2026-10-07 20:55:33 +00:00
Dismissed
dev-agent added 1 commit 2026-10-07 21:00:21 +00:00
CI: no image build on PRs; images are built only on release
CI / checks (push) Skipped
CI / checks (pull_request) Successful in 12m10s
CI / e2e (pull_request) Successful in 34m0s
6af54790a9
The build-only image job on every PR goes: images are built when a
release is published (release.yml), so PRs don't wait on it.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PPQ14L3LikyefCMFBqEbKE
dev-agent dismissed dongar's review 2026-10-07 21:00:21 +00:00
Reason:

New commits pushed, approval review dismissed automatically according to repository settings

dongar approved these changes 2026-10-07 21:10:47 +00:00
Dismissed
dongar approved these changes 2026-10-07 21:48:13 +00:00
dev-agent merged commit cd9eb3fd76 into main 2026-10-07 21:48:44 +00:00
dev-agent deleted branch image 2026-10-07 21:48:44 +00:00
Sign in to join this conversation.
No Reviewers
2 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: dev/nisse#115