feat: publish client images and pull them on install #53

Merged
claudiomaradonna merged 5 commits from feat/published-images into master 2026-10-26 18:06:49 +01:00
Collaborator

Why

Every install or update of azkaban builds each client image locally from
containers/Dockerfile: apt, Node.js, gh, fj and the client itself, on
every machine, at every client release. Now that a Forgejo runner is
available, the forge can build the images once and hosts can pull them.
Pulling with an automatic fallback to a build was set aside because it
hides registry failures; multi-arch images were set aside because the
runner is amd64 and QEMU emulation is slow.

What changed

A new publish-images workflow runs nightly, on pushes to master that
change the Dockerfile, the client registry, the publish script, or the
workflow, and on demand. It builds every registered client for
linux/amd64 on the runner's Docker-in-Docker daemon and pushes
git.unitoo.it/claudiomaradonna/<image>:latest and :<client version>,
only after every image has built.

On a linux/amd64 host, ./install.sh now pulls those images and gives
them the same local names as before (codex-cli:latest, and so
localhost/codex-cli:latest for Dockerfile.azkaban and custom images),
so the launcher and custom images are unchanged. --build keeps the
local build, --tz and --no-cache imply it, and macOS and non-amd64
Linux hosts build as before. A failed pull leaves the installation as it
was. The README, the development guide and the client guide describe
the new behavior and the publishing setup.

Before the first run, the repository needs the secret REGISTRY_TOKEN:
an access token of claudiomaradonna with the write:package scope.
The repository and its packages must stay public.

How to verify

  1. With REGISTRY_TOKEN set, run publish-images from the Actions tab
    on this branch. It should end green and show codex-cli,
    claude-code and opencode-cli under the owner's packages, each with
    latest and a version tag.
  2. On a linux/amd64 host with rootless Podman, run ./install.sh claude
    from this branch. The output should show
    Pulling claude image git.unitoo.it/claudiomaradonna/claude-code:latest...
    with no build, and podman images should list
    localhost/claude-code:latest. On master the same command builds
    the image.
  3. ./install.sh --tz Europe/Rome claude builds locally, as before.

make lint && .agents/hooks/shellcheck.sh repo && make test passes on
the tip. The new tests/unit/test_publish.sh,
tests/integration/test_install_pulls.sh and the --build help check
fail on master. Not tried: the workflow on the real runner, which needs
the secret, and make test-e2e, which needs real rootless Podman.

Considerations

Hosts now trust whatever was last pushed to the registry: there is no
digest pin or signature check, so a write:package token for the
account controls what every amd64 install runs. This assumes the
runner's dind daemon is not shared with untrusted jobs, which could
retag an image between build and push.

The push triggers also include the publish script and the workflow
file, beyond the Dockerfile and registry that were agreed, since they
too change the published images. One low review finding was set
aside: the --tz/--no-cache pin was folded into the feature commit
rather than committed first.

## Why Every install or update of azkaban builds each client image locally from `containers/Dockerfile`: apt, Node.js, gh, fj and the client itself, on every machine, at every client release. Now that a Forgejo runner is available, the forge can build the images once and hosts can pull them. Pulling with an automatic fallback to a build was set aside because it hides registry failures; multi-arch images were set aside because the runner is amd64 and QEMU emulation is slow. ## What changed A new `publish-images` workflow runs nightly, on pushes to `master` that change the Dockerfile, the client registry, the publish script, or the workflow, and on demand. It builds every registered client for linux/amd64 on the runner's Docker-in-Docker daemon and pushes `git.unitoo.it/claudiomaradonna/<image>:latest` and `:<client version>`, only after every image has built. On a linux/amd64 host, `./install.sh` now pulls those images and gives them the same local names as before (`codex-cli:latest`, and so `localhost/codex-cli:latest` for `Dockerfile.azkaban` and custom images), so the launcher and custom images are unchanged. `--build` keeps the local build, `--tz` and `--no-cache` imply it, and macOS and non-amd64 Linux hosts build as before. A failed pull leaves the installation as it was. The README, the development guide and the client guide describe the new behavior and the publishing setup. Before the first run, the repository needs the secret `REGISTRY_TOKEN`: an access token of `claudiomaradonna` with the `write:package` scope. The repository and its packages must stay public. ## How to verify 1. With `REGISTRY_TOKEN` set, run `publish-images` from the Actions tab on this branch. It should end green and show `codex-cli`, `claude-code` and `opencode-cli` under the owner's packages, each with `latest` and a version tag. 2. On a linux/amd64 host with rootless Podman, run `./install.sh claude` from this branch. The output should show `Pulling claude image git.unitoo.it/claudiomaradonna/claude-code:latest...` with no build, and `podman images` should list `localhost/claude-code:latest`. On `master` the same command builds the image. 3. `./install.sh --tz Europe/Rome claude` builds locally, as before. `make lint && .agents/hooks/shellcheck.sh repo && make test` passes on the tip. The new `tests/unit/test_publish.sh`, `tests/integration/test_install_pulls.sh` and the `--build` help check fail on `master`. Not tried: the workflow on the real runner, which needs the secret, and `make test-e2e`, which needs real rootless Podman. ## Considerations Hosts now trust whatever was last pushed to the registry: there is no digest pin or signature check, so a `write:package` token for the account controls what every amd64 install runs. This assumes the runner's dind daemon is not shared with untrusted jobs, which could retag an image between build and push. The push triggers also include the publish script and the workflow file, beyond the Dockerfile and registry that were agreed, since they too change the published images. One low review finding was set aside: the `--tz`/`--no-cache` pin was folded into the feature commit rather than committed first.
Take the source's Codex changes since d5d69df: subagent adapters drop
their sandbox_mode so they inherit the session's permissions, the
reviewers turn off apps, plugins, and bundled skills to trim their
context, and execute-plan schedules parallel tasks by the session's
subagent capacity in Codex.
Pull request bodies now come from .agents/templates/pull-request.md,
which replaces deliver's pull-request.md, and fj.md records fj's
template lookup. As the forge is Forgejo, the template is also copied
to .forgejo/pull_request_template.md, so fj makes one lookup instead
of 24 before each pr create.
A nightly, push, and manual Forgejo workflow builds every registered
client target on the runner's Docker-in-Docker daemon and pushes it to
the forge's container registry as latest and as the client's version,
so hosts can pull the images instead of building them.
The installer now pulls each selected client image from the forge's
registry and names it as the local image the launcher and custom images
use, instead of building it. --build keeps the local build, which
--no-cache and --tz imply, and hosts other than linux/amd64, for which
no image is published, build locally as before.
docs: describe published client images
Some checks failed
publish-images / publish (push) Failing after 1m14s
ce7ae7ca96
The README tells which hosts pull the published images and which
build them, and the development guide covers the publishing workflow,
its runner, and its registry secret.
claudiomaradonna deleted branch feat/published-images 2026-10-26 18:06:54 +01:00
Sign in to join this conversation.
No reviewers
No labels
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
claudiomaradonna/azkaban!53
No description provided.