151 lines
9.2 KiB
Markdown
151 lines
9.2 KiB
Markdown
> **WARNING:** This document describes only the change applied in this PR.
|
|
> It may already be outdated once the next PR is merged.
|
|
> Historic PR-documentation is not maintained along with new PRs — treat it as a snapshot, not as current documentation.
|
|
|
|
## Related Links
|
|
|
|
- Werkdock `CHANGELOG.md` 0.2.0 (repository `mi/werkdock`) — the release notes this PR adapts to: `import` replaces `load -i --name` for rootfs archives, `load` is reserved for docker/OCI image archives, image names gained docker-style tags.
|
|
- ADR 0008 — the bwrap build runtime; `docs/configuration.md`, notes on `builds.<name>.werkdock`.
|
|
|
|
## The Problem
|
|
|
|
Werkdock moves its CLI closer to `docker run --rm`.
|
|
For the rootfs archives Werkator uses, the verb is now `werkdock import ARCHIVE IMAGE` (docker import semantics).
|
|
`werkdock load -i ARCHIVE --name IMAGE` still works but is a compatibility path with a note on stderr, and is announced to go away once Werkator has switched.
|
|
Werkator ships the werkdock binary with its deployment, but nothing forces the two to be updated together: an installation may run a new Werkator against an older werkdock for a while, or the other way round.
|
|
|
|
And the shipping itself was unreliable, which is the second half of the same subject.
|
|
`tools/remote` built the werkdock binary only when the file was missing, so the first build won forever: a binary built weeks ago survived every `git pull` in the werkdock checkout and was uploaded in silence on every deployment since.
|
|
On 2026-09-10 this put werkdock 0.1.0-dev on the production instance twice in one morning while the checkout it came from had been at 0.2.0 for days.
|
|
The evidence was on screen both times — the deployment prints the version it unpacks — and was read as a fact about the host rather than as the symptom it was.
|
|
Without that fix this PR would change nothing in practice: the new code would meet the old binary, fall back, and go on using `load -i`.
|
|
|
|
## Non-Goals
|
|
|
|
- No switch of the existence check from `werkdock images` to `werkdock inspect`: `images` prints the bare name for untagged images on every werkdock version, the exact-line match keeps working, and `inspect` would tie Werkator to the new werkdock.
|
|
- No tagged image name (`werkator-buildenv:<hash>`): it would re-import every build environment on the webspace and orphan the old image, for no functional gain today.
|
|
- None of the new `run` flags (`--mount`, `--entrypoint`, `--network host`): Werkator's invocation needs none of them.
|
|
- No build of werkdock from this repository. It has been its own repository since step 21 session E; this only decides which of its builds gets uploaded.
|
|
- No checksum or provenance tracking for the binary. The question is "is this binary older than its sources", not "which commit is it from".
|
|
|
|
## The Scenarios
|
|
|
|
### Feature: rootfs archives are imported with werkdock's docker-shaped verb
|
|
|
|
#### Background
|
|
|
|
- The build environment is a rootfs archive named by `builds.<name>.werkdock.rootfs`.
|
|
- Werkator creates the werkdock image `werkator-buildenv-<hash>` from it once per source.
|
|
- Werkdock answers an unknown verb with `werkdock: unknown command "import"` and exit code 125, its code for its own errors.
|
|
|
|
#### Scenario#50.01: A missing image is imported with `werkdock import`
|
|
|
|
So that Werkator uses the verb werkdock names for rootfs archives, and the deprecated path can be removed on werkdock's side.
|
|
|
|
- **Given** `werkdock images` does not list the image
|
|
- **When** a build starts
|
|
- **Then** Werkator runs `werkdock import ARCHIVE werkator-buildenv-<hash>`
|
|
- **and** runs no `werkdock load`.
|
|
|
|
##### Verified by
|
|
|
|
- [WerkdockBuildRunnerTest "imports the image once when werkdock does not know it yet"](../../src/test/kotlin/de/hoennig/werkator/build/WerkdockBuildRunnerTest.kt)
|
|
- [WerkdockBuildRunnerTest "downloads a URL rootfs once before importing it"](../../src/test/kotlin/de/hoennig/werkator/build/WerkdockBuildRunnerTest.kt)
|
|
|
|
#### Scenario#50.02: An older werkdock without the verb still works
|
|
|
|
So that Werkator and werkdock can be updated in either order.
|
|
|
|
- **Given** the installed werkdock answers `import` with `unknown command` and exit 125
|
|
- **When** a build starts with a missing image
|
|
- **Then** Werkator falls back to `werkdock load -i ARCHIVE --name werkator-buildenv-<hash>`
|
|
- **and** logs the fallback.
|
|
|
|
##### Verified by
|
|
|
|
- [WerkdockBuildRunnerTest "falls back to load -i --name on a werkdock without the import verb"](../../src/test/kotlin/de/hoennig/werkator/build/WerkdockBuildRunnerTest.kt)
|
|
|
|
#### Scenario#50.03: A real import failure is not masked by the fallback
|
|
|
|
So that a broken archive fails the build with werkdock's message, as before.
|
|
|
|
- **Given** `werkdock import` fails for any other reason (any other exit code, or exit 125 without `unknown command`)
|
|
- **When** a build starts with a missing image
|
|
- **Then** the build fails with that command's output
|
|
- **and** no `werkdock load` runs.
|
|
|
|
##### Verified by
|
|
|
|
- [WerkdockBuildRunnerTest "propagates an import failure that is not a missing verb"](../../src/test/kotlin/de/hoennig/werkator/build/WerkdockBuildRunnerTest.kt)
|
|
|
|
#### Scenario#50.04: An existing image is neither imported nor loaded
|
|
|
|
- **Given** `werkdock images` lists the image
|
|
- **When** a build starts
|
|
- **Then** neither `import` nor `load` runs.
|
|
|
|
##### Verified by
|
|
|
|
- [WerkdockBuildRunnerTest "does not import an image werkdock already has"](../../src/test/kotlin/de/hoennig/werkator/build/WerkdockBuildRunnerTest.kt)
|
|
|
|
### Feature: the uploaded werkdock is the werkdock that is checked out
|
|
|
|
#### Background
|
|
|
|
- `WERKDOCK_REPO` is the werkdock checkout, by default a sibling of this repository.
|
|
- `WERKDOCK_BINARY` is the binary within it, by default `$WERKDOCK_REPO/dist/werkdock`.
|
|
|
|
#### Scenario#50.05: A binary older than its sources is rebuilt
|
|
|
|
- **Given** a werkdock checkout whose Go sources are newer than the built binary
|
|
- **When** any command that uploads werkdock runs
|
|
- **Then** the binary is rebuilt before the upload
|
|
- **Given** a binary newer than every source
|
|
- **Then** nothing is built — a deployment does not wait for a compiler with nothing to do
|
|
- **Given** no binary at all
|
|
- **Then** it is built, as before
|
|
|
|
#### Scenario#50.06: A binary named elsewhere is taken as given
|
|
|
|
So that a prebuilt binary stays the deployer's decision.
|
|
|
|
- **Given** `WERKDOCK_BINARY` pointing outside the default path in the checkout
|
|
- **Then** it is uploaded as it is and never overwritten
|
|
- **Given** no werkdock checkout at all
|
|
- **Then** there is nothing to build from, and the named binary is used
|
|
|
|
#### Scenario#50.07: The version is on the screen before it is on the host
|
|
|
|
- **Given** any upload of werkdock
|
|
- **Then** the version being uploaded is printed first
|
|
- **and** a binary that cannot report its version says so instead of failing the deployment
|
|
|
|
## The Solution
|
|
|
|
`WerkdockBuildRunner.ensureImage` calls the new `importImage`: `werkdock import ARCHIVE IMAGE` through the non-throwing `run`, then either returns, falls back to `load -i --name` on exactly the unknown-verb signature (exit 125 plus `unknown command` on stderr), or rethrows the import's result as a `GitCommandException` — the same exception and message the old code produced.
|
|
The fallback is keyed on werkdock's own error signature rather than on parsing `werkdock version`: the signature is what actually decides whether the verb exists, and it needs no version table in Werkator.
|
|
|
|
For the binary, timestamps decide, not hashes: a `git pull`, a branch switch and an edit all touch exactly the files that would produce a different binary, so a source newer than the binary is the honest signal that the binary is behind. `.git` is excluded from the search, or every fetch would look like a change.
|
|
Only the default path inside the checkout is ever written — a `WERKDOCK_BINARY` pointing elsewhere is a deliberate choice of a prebuilt binary, and overwriting it would be a surprise; without a `go.mod` there is nothing to build from at all.
|
|
The version is printed before the upload. The rebuild rule makes the stale case unlikely, but the line is what makes it *visible*, and this failure stayed silent for weeks precisely because nobody had to look.
|
|
|
|
## Open Questions
|
|
|
|
- When to drop the fallback: once every installation runs a werkdock with `import`, the `load -i --name` branch and its test go, and werkdock can remove the compatibility path.
|
|
- A binary newer than its sources but built from another branch still passes. The printed version is what would catch it, and only if someone reads it.
|
|
- No version bump and no release-notes entry for the deployment half: nothing in the shipped application changes there, and a release note that tells the user nothing is noise.
|
|
|
|
## Additional Changes
|
|
|
|
- `docs/configuration.md` and `docs/deployment.md` say "imported" where they said "loaded", and name the fallback and the reason the image name stays untagged.
|
|
- `tools/remote`: `ensure_werkdock_binary` rebuilds a stale binary and prints the version it uploads; its header and `docs/deployment.md` say so.
|
|
- One `PR#000` placeholder in `werkator.css` that PR#48 left behind: its rename covered Markdown, Kotlin, YAML and HTML, but not the stylesheet.
|
|
|
|
## Prerequisite PRs
|
|
|
|
- Werkdock 0.2.0 (repository `mi/werkdock`), which introduces `import`; against an older werkdock the fallback path runs. Released on werkdock's main on 2026-09-10.
|
|
|
|
## Follow-up PRs
|
|
|
|
- Remove the fallback (see Open Questions).
|