285 lines
15 KiB
Markdown
285 lines
15 KiB
Markdown
# Changelog
|
|
|
|
Release notes for consumers of the `werkdock` CLI, Werkator first among them.
|
|
Each entry says what changed, what still works unchanged, and what a consumer
|
|
should adapt — and by when.
|
|
|
|
A release is a merge to main; there is no separate release step. So a branch
|
|
that changes behavior sets the version in `internal/cli/cli.go` (what
|
|
`werkdock version` prints) and titles its section here with that version and
|
|
the merge date, before it is merged. Nothing on main is ever "unreleased".
|
|
|
|
## 0.6.0 (2026-09-10)
|
|
|
|
Theme: the API socket — RFC 0005 milestone 3, and with it milestone 4:
|
|
Testcontainers starts a real PostgreSQL in werkdock. Purely additive;
|
|
no CLI verb changes behaviour.
|
|
|
|
### New
|
|
|
|
- `werkdock system service [--time D] [unix://PATH]` serves the Docker
|
|
Engine API subset a client actually uses, on
|
|
`$XDG_RUNTIME_DIR/werkdock.sock` by default (podman's verb and
|
|
podman's manners: optional, started when needed, `--time` to exit
|
|
when idle). A client is pointed at it with
|
|
`DOCKER_HOST=unix://<path>`.
|
|
- Testcontainers (validated with 1.19.3, `PostgreSQLContainer`) works
|
|
against it: pull, create, start, logs with the wait strategy, exec,
|
|
kill, remove, and the session cleanup. It needs
|
|
`TESTCONTAINERS_RYUK_DISABLED=true`, because Ryuk wants the API
|
|
socket inside a container of its own.
|
|
- The endpoints are those measured from a real run, written down in
|
|
`docs/testcontainers-api.md`. Everything outside the subset answers
|
|
**501 with the reason**, never 404, so a client that needs more says
|
|
so plainly.
|
|
- Instances carry docker labels (`config.json`), and the instance list
|
|
filters on them, which is how a client finds what it left behind.
|
|
- An exec is a record of its own with an id and an exit code, because
|
|
the API creates, starts, and inspects it in three calls.
|
|
|
|
### Refused rather than ignored
|
|
|
|
- `logs` parameters werkdock cannot honour say so with 501 and the
|
|
reason: `tail` (the output is streamed whole), `timestamps` and a
|
|
non-zero `since`/`until` (no clock is recorded beside the output).
|
|
What a client actually sends — `stdout`, `stderr`, `follow`,
|
|
`since=0`, `tail=all` — is served.
|
|
|
|
### Ports, the deliberate deviation
|
|
|
|
Werkdock shares the host's network and cannot map a port. Where a
|
|
client asks for a published port, werkdock therefore picks a free one
|
|
and tells the *service* to bind it, through the environment variable
|
|
the image honours (`PGPORT`, `MARIADB_TCP_PORT`, `MYSQL_TCP_PORT`,
|
|
`REDIS_PORT`). An image outside that small table keeps its own port,
|
|
which is right as long as one instance of it runs at a time; asking to
|
|
move it fails with the reason instead of silently reporting a port
|
|
nothing listens on. The same table carries the user an image needs when
|
|
its entrypoint expects to drop privileges itself (`postgres`), which
|
|
werkdock cannot do with one uid.
|
|
|
|
### Version reporting
|
|
|
|
`/version` and `/info` report the Docker Engine version whose API is
|
|
served (25.0.0), not werkdock's: clients compare that field against a
|
|
minimum, and 0.x fails it forever. Werkdock's own version is beside it
|
|
in `Components` and in `/info`'s `OperatingSystem`.
|
|
|
|
### For Werkator
|
|
|
|
- Nothing to change. The service is not started unless someone asks
|
|
for it, and no CLI verb behaves differently.
|
|
- New possibility: an integration test in a build sandbox can get a
|
|
real database. Bind the socket into the sandbox
|
|
(`-v $XDG_RUNTIME_DIR/werkdock.sock:/run/docker.sock`) and set
|
|
`DOCKER_HOST` and `TESTCONTAINERS_RYUK_DISABLED` in the build.
|
|
|
|
## 0.5.0 (2026-09-10)
|
|
|
|
Theme: the instance lifecycle — RFC 0005 milestone 2. A service image
|
|
runs in the background, can be stopped, restarted, queried, and talked
|
|
to. Additive for `run --rm`; two small semantic changes below.
|
|
|
|
### New
|
|
|
|
- `run -d` starts the instance under a background supervisor and prints
|
|
its 64-hex id; `--name NAME` names it (default: image basename plus six
|
|
characters of the id); `-i` passes stdin (no pty: `-t` is refused).
|
|
- `ps [-a] [-q] [--format FMT]` lists instances with docker's columns
|
|
(`CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES`; PORTS stays
|
|
empty, the network is the host's); `stop [-t SECONDS]`, `kill [-s SIG]`,
|
|
`rm [-f]`, `wait`, `logs [-f]`, `create`, `start`, and `inspect` on an
|
|
instance id, unique prefix, or name. Each prints what docker prints
|
|
(the argument on success, `werkdock: …` and exit 125 per failure).
|
|
- `exec [-e K=V] [-w DIR] [-u USER] [-i] INSTANCE CMD...` runs a second
|
|
command in the instance's sandbox: same rootfs, mounts, uid, env,
|
|
`/run` and volumes; not the same pid namespace.
|
|
- docker's management form is accepted: `container ls|start|stop|...`,
|
|
`image ls|rm|pull|...`.
|
|
- State lives in `$WERKDOCK_HOME/instances/<id>/`: `config.json`
|
|
(`version: 1`), `state.json`, `stdout`, `stderr`, `run/`, `volumes/`.
|
|
A state left "running" by a dead supervisor (reboot, `kill -9`) is
|
|
repaired to exited 255 on the next read; an exited `--rm` instance
|
|
found that way is removed.
|
|
|
|
### Changed
|
|
|
|
- A `run` without `--rm` no longer fails ("persistent instances are not
|
|
implemented yet"); the instance stays for `ps -a`, `logs`, `start`, `rm`.
|
|
- The command is pid 1 of its sandbox (`bwrap --as-pid-1`), as in
|
|
docker, so `stop`'s SIGTERM can reach it. As in docker, an entrypoint
|
|
without a SIGTERM handler (`sh -c '…'`) is only ended by the SIGKILL
|
|
after the timeout, and orphaned children are its own to reap.
|
|
- A foreground `run` forwards the first SIGINT/SIGTERM/SIGHUP to the
|
|
command and kills it on the second, or after 10 seconds; bwrap runs in its own process
|
|
group, so Ctrl-C no longer kills bwrap (and with it the command)
|
|
outright. Werkator's builds see the same exit codes as before; a
|
|
`sh -c` build interrupted with SIGTERM now ends 10 seconds later, on
|
|
the SIGKILL, unless the script traps TERM.
|
|
- Anonymous volumes (the image's `VOLUME`s) and `/run` are directories
|
|
of the instance instead of tmpfs, so they survive `stop`/`start` and
|
|
are visible to `exec`. For a `--rm` run the difference is disk instead
|
|
of RAM for those paths, and nothing else.
|
|
|
|
### For Werkator
|
|
|
|
- Nothing to change: `run --rm …` behaves as before. The `ps` output
|
|
is new and Werkator does not parse it.
|
|
|
|
## 0.4.0 (2026-09-10)
|
|
|
|
Theme: service images — the first milestone of RFC 0005 (PostgreSQL in the
|
|
sandbox, Testcontainers as the destination). Additive; a `run` without the
|
|
new flags on an image without `USER`/`VOLUME` behaves exactly as before.
|
|
|
|
### New
|
|
|
|
- `run --user USER[:GROUP]` (`-u`) makes the caller appear as that user inside
|
|
the sandbox: a name is resolved in the image's `/etc/passwd` and `/etc/group`,
|
|
a number needs no entry. `HOME` follows the passwd entry when there is one.
|
|
Still one uid only — the caller is mapped to it; there is no second user.
|
|
- `run --userns=keep-id` (podman's spelling) maps the caller to their own uid
|
|
and gid. Other `--userns` modes are refused with the reason.
|
|
- An image's `USER` (OCI config) applies when neither flag is given, as with
|
|
podman; unknown names fail with `image USER: …`. Default stays uid 0.
|
|
- An image's `VOLUME` paths are mounted as anonymous tmpfs unless a `-v` or
|
|
`--mount` covers them, so `postgres:16` starts without knowing its data and
|
|
socket directories by heart.
|
|
- `/run` is a tmpfs in every sandbox, beside `/tmp` and `/root`. `/var/run`
|
|
points there in practically every image, and services keep pid files, locks
|
|
and sockets under it without declaring a `VOLUME` (`postgres` needs
|
|
`/var/run/postgresql` writable). A `--tmpfs`/`-v` on a path below `/run`
|
|
still works, it just lands inside the tmpfs.
|
|
- `import -c` accepts `USER user[:group]` and `VOLUME /path` (or a JSON array);
|
|
`inspect` shows `User` and `Volumes` in `Config`.
|
|
|
|
### For Werkator
|
|
|
|
- Nothing to change. Builds keep running as uid 0. If a build image ever sets
|
|
`USER`, that user applies; add `--user 0` to keep root.
|
|
|
|
### Podman before Docker
|
|
|
|
- Where the two differ, werkdock follows podman from now on (RFC 0005):
|
|
`--userns=keep-id` is the first case.
|
|
|
|
## 0.3.0 (2026-09-10)
|
|
|
|
Theme: `werkdock pull` — images come straight from a registry, no docker
|
|
on any machine needed (RFC 0002 level 2, RFC 0004). Additive; nothing a
|
|
consumer calls today changes.
|
|
|
|
### New
|
|
|
|
- `werkdock pull [--platform OS/ARCH] [REGISTRY/]REPOSITORY[:TAG]` fetches an
|
|
image and stores it exactly as `load -i` would: layers flattened, OCI
|
|
config kept, listed by `images` under the reference given. Prints
|
|
`Pulled image: REF`; exit 125 with the reason if the registry refuses,
|
|
the image already exists, or crane is missing. `--platform` defaults to
|
|
`linux/<host arch>`.
|
|
- The registry side is the `crane` binary
|
|
(github.com/google/go-containerregistry, one static executable, no
|
|
privileges). werkdock looks for it in `PATH`, then beside its own
|
|
binary, or at `WERKDOCK_CRANE`. A deployment on a managed webspace ships
|
|
it next to `werkdock`. Credentials come from `~/.docker/config.json`
|
|
(`DOCKER_CONFIG`), as with docker.
|
|
- `werkdock rmi IMAGE...` removes images (`docker rmi`): prints `Untagged: REF`
|
|
per image, tries every argument and exits 125 if one failed. Works on the
|
|
legacy flat layout too, so Werkator's pre-0.2.0 build image can be removed
|
|
with it. `pull` and `load` now name `rmi` in their "already exists" message.
|
|
- `werkdock images` takes docker's `--format`: a Go template over
|
|
`.Repository`, `.Tag`, `.ID`, `.CreatedAt`, `.CreatedSince`, `.Size`, or
|
|
`table [TEMPLATE]` for docker's column layout (`--format table` prints
|
|
`REPOSITORY TAG IMAGE ID CREATED SIZE`); `-q` prints IDs; an optional
|
|
`REPOSITORY[:TAG]` argument filters. Without flags the output is unchanged:
|
|
one short reference per line. `inspect` gains an `Id` field. The ID is
|
|
a hash of the image metadata, shared by tags of the same image; it is
|
|
not docker's config digest.
|
|
- `werkdock doctor` warns when crane is not in `PATH` (a warning, not a
|
|
failure: everything but `pull` works without it).
|
|
|
|
### For Werkator
|
|
|
|
- Optional: replace "download archive, `werkdock load -i`" with a single
|
|
`werkdock pull IMAGE` where the image is a public registry image; the
|
|
self-built archives keep going through `import`/`load`.
|
|
- Deployment: put `crane` beside `werkdock` if `pull` is to be used.
|
|
- Optional: retire old build images with `werkdock rmi` instead of deleting
|
|
store directories.
|
|
|
|
## 0.2.0 (2026-09-10)
|
|
|
|
Theme: the `run` surface, image names, and the image store move closer to
|
|
`docker run --rm` (RFC 0002, RFC 0004). Everything Werkator invokes today keeps working; two of its calls
|
|
now have a preferred docker-shaped form.
|
|
|
|
### Compatible — nothing to change
|
|
|
|
- `werkdock images` still prints one image per line and still prints a bare
|
|
name for images loaded without a tag. Werkator's exact-line existence check
|
|
(`image in loaded`) matches as before. Images that carry a tag other than
|
|
`latest` print as `repository:tag`.
|
|
- `werkdock load -i ARCHIVE --name NAME` still imports a rootfs archive; it
|
|
now additionally prints one note on stderr pointing to `werkdock import`.
|
|
Exit code and stdout are unchanged (`Loaded image: NAME` became
|
|
`Imported image: NAME` — adjust if the stdout line is parsed).
|
|
- `werkdock run --rm -v ... --tmpfs ... -e ... -w ... IMAGE CMD...` is
|
|
unchanged in syntax and mount order. Two semantic corrections match docker:
|
|
a bare `-e KEY` whose variable the host does not have is no longer passed as
|
|
`KEY=` (it is omitted); a missing `-w DIR` is created when it lies in a
|
|
writable area (tmpfs or a read-write bind) instead of failing with bwrap's
|
|
"Can't chdir".
|
|
- Existing stores: the flat layout `images/NAME/` written by earlier versions
|
|
is still read as `NAME:latest`. No reload after the upgrade; new imports land
|
|
in `images/REPOSITORY/TAG/`. The pre-existing `image.json` files lack the new
|
|
metadata fields and read back as an image without ENTRYPOINT/CMD/Env, which
|
|
is exactly what they were.
|
|
- `werkdock doctor`, `version`, exit codes (125 for werkdock's own errors,
|
|
the command's code otherwise): unchanged.
|
|
|
|
### Preferred new forms — adapt at the next convenient change
|
|
|
|
- `werkdock import ARCHIVE [REPOSITORY[:TAG]]` replaces `load -i --name` for
|
|
rootfs archives (`docker import` semantics). Plan: `load -i --name` stays
|
|
accepted until Werkator has switched, then it is removed together with the
|
|
stderr note.
|
|
- `werkdock load -i ARCHIVE` loads a `docker save` archive (legacy and OCI
|
|
layout) or a plain OCI image layout: layers flattened into one rootfs,
|
|
whiteouts applied, the OCI config kept as image metadata, the image named by
|
|
its RepoTags (every tag becomes a hardlinked copy). A multi-arch index picks
|
|
the host platform. This is the intended path for build environments from now
|
|
on: `docker build`, `docker save`, upload, `werkdock load` — with ENTRYPOINT,
|
|
ENV, and WORKDIR intact (RFC 0004). Layers compressed with zstd need the
|
|
`zstd` binary, as `import` does.
|
|
- Image names may carry a tag: `werkator-buildenv-<hash>` keeps working and
|
|
equals `werkator-buildenv-<hash>:latest`; a consumer may also use
|
|
`werkator-buildenv:<hash>` or a registry-style `git.javagil.de/mi/buildenv:<hash>`.
|
|
Name grammar: `[registry/]repository[:tag]`, repository components lowercase
|
|
`[a-z0-9]` with `.`, `_`, `-` between them, tag `[A-Za-z0-9_][A-Za-z0-9_.-]*`,
|
|
max 128 characters. The `.tmp` tag suffix is reserved.
|
|
- `import -c 'ENTRYPOINT [...]'`, `-c 'CMD ...'`, `-c 'ENV K=V'`,
|
|
`-c 'WORKDIR /d'`, `-c 'LABEL k=v'` store OCI metadata with the image;
|
|
`werkdock inspect IMAGE` prints it as a docker-shaped JSON array
|
|
(`RepoTags`, `Created`, `Architecture`, `Os`, `Config`, plus a `Werkdock`
|
|
section with `Source` and `RootFS`). A consumer that wants to check
|
|
existence like `docker image inspect` can use `inspect` (exit 125 when the
|
|
image is missing) instead of scanning `images`.
|
|
- `werkdock run IMAGE` without a command runs the image's ENTRYPOINT+CMD;
|
|
`--entrypoint PROGRAM` overrides ENTRYPOINT and drops CMD; positional
|
|
arguments replace CMD; `-e` and `-w` override the image's Env and WorkingDir.
|
|
- `--mount type=bind,src=SRC,dst=DEST[,readonly]` and
|
|
`--mount type=tmpfs,dst=DEST` are accepted beside `-v` and `--tmpfs`; all
|
|
three feed one ordered mount list (the git-metadata mask ordering still
|
|
holds across them).
|
|
- `--network host` (and `--net host`) is accepted as a no-op, so a build
|
|
script can pass a docker-compatible command line unchanged. Other modes fail
|
|
with: `network mode "X" is not available in unprivileged shared-webspace
|
|
environments; only "host" is supported`. `-p/--publish` remain refused.
|
|
|
|
### Incompatible
|
|
|
|
- None for the calls Werkator makes. For everyone else: an image name with an
|
|
uppercase letter or a leading `.`/`-`/`_` in the repository was accepted by
|
|
the old name check and is refused now (docker's grammar); `..`-style names
|
|
were refused before and still are.
|