Docker builds report unknown version, and profiling output is truncated on error #75

Closed
opened 2026-08-09 03:44:22 +02:00 by clawbot · 1 comment
Collaborator

Two independent defects in build metadata and developer tooling.

1. Docker images cannot report their version or commit

.dockerignore excludes .git, but the Dockerfile build stage embeds
the commit with:

-X '...Commit=$(git rev-parse HEAD 2>/dev/null || echo unknown)'

Inside the container there is no .git, so this always evaluates to
unknown. Every Docker-built image reports commit: unknown, build date: unknown. Separately, ARG VERSION=dev is never passed by
script/docker, so the version is dev too.

The result: a container image that cannot identify which source it was
built from. For a backup tool, being unable to answer "which build
produced this archive?" is a real operational problem, not cosmetic.

2. os.Exit(1) discards pprof output

cli.Entry() (internal/cli/entry.go:34-36) calls os.Exit(1) on any
command error. That skips main's defer pprof.StopCPUProfile()
(cmd/vaultik/main.go:26) and the entire deferred heap-profile writer
(main.go:31-44).

So profiling a failing command — often exactly the command you want to
profile — silently yields a truncated or empty profile, with no warning
that the data is unusable.

Definition of done

  1. Version, commit, and build date are passed into the Docker build as
    ARGs from script/docker (computed on the host, where .git
    exists), rather than being computed inside the container. Do not solve
    this by un-ignoring .git — that bloats the build context and busts
    the layer cache on every commit.
  2. docker run … vaultik version prints a real version, commit sha, and
    build date matching the host checkout it was built from.
  3. A dirty working tree is reflected in the embedded version (e.g. a
    -dirty suffix) so an image built from uncommitted changes cannot
    masquerade as a clean build.
  4. Item 2 fixed so profiles are complete regardless of exit status:
    restructure Entry/main so cleanup runs before exit — have Entry
    return a status code that main turns into os.Exit after its
    defers, rather than exiting from within. Do not paper over it by
    duplicating the profile-flush logic at each exit site.
  5. Manual verification recorded on this issue: run a deliberately failing
    command with VAULTIK_CPUPROFILE set and confirm the resulting profile
    is readable and non-empty.
  6. Coordinate with #65, which reworks version derivation — if #65 lands
    first, build on its mechanism rather than adding a second one.
  7. make check green.
Two independent defects in build metadata and developer tooling. ## 1. Docker images cannot report their version or commit `.dockerignore` excludes `.git`, but the Dockerfile build stage embeds the commit with: ``` -X '...Commit=$(git rev-parse HEAD 2>/dev/null || echo unknown)' ``` Inside the container there is no `.git`, so this **always** evaluates to `unknown`. Every Docker-built image reports `commit: unknown, build date: unknown`. Separately, `ARG VERSION=dev` is never passed by `script/docker`, so the version is `dev` too. The result: a container image that cannot identify which source it was built from. For a backup tool, being unable to answer "which build produced this archive?" is a real operational problem, not cosmetic. ## 2. `os.Exit(1)` discards pprof output `cli.Entry()` (`internal/cli/entry.go:34-36`) calls `os.Exit(1)` on any command error. That skips `main`'s `defer pprof.StopCPUProfile()` (`cmd/vaultik/main.go:26`) and the entire deferred heap-profile writer (`main.go:31-44`). So profiling a **failing** command — often exactly the command you want to profile — silently yields a truncated or empty profile, with no warning that the data is unusable. ## Definition of done 1. Version, commit, and build date are passed **into** the Docker build as `ARG`s from `script/docker` (computed on the host, where `.git` exists), rather than being computed inside the container. Do not solve this by un-ignoring `.git` — that bloats the build context and busts the layer cache on every commit. 2. `docker run … vaultik version` prints a real version, commit sha, and build date matching the host checkout it was built from. 3. A dirty working tree is reflected in the embedded version (e.g. a `-dirty` suffix) so an image built from uncommitted changes cannot masquerade as a clean build. 4. Item 2 fixed so profiles are complete regardless of exit status: restructure `Entry`/`main` so cleanup runs before exit — have `Entry` return a status code that `main` turns into `os.Exit` after its defers, rather than exiting from within. Do not paper over it by duplicating the profile-flush logic at each exit site. 5. Manual verification recorded on this issue: run a deliberately failing command with `VAULTIK_CPUPROFILE` set and confirm the resulting profile is readable and non-empty. 6. Coordinate with #65, which reworks version derivation — if #65 lands first, build on its mechanism rather than adding a second one. 7. `make check` green.
clawbot added this to the 1.0.0 milestone 2026-08-09 03:44:22 +02:00
Author
Collaborator

Finished as #141 (base next).

script/docker computes version, commit and date on the host and passes
them as build args; the Dockerfile no longer runs git. docker run vaultik version now reports the host's real version, commit sha and
build date instead of unknown, with a -dirty suffix for an
uncommitted tree (via script/version, the mechanism from
#65). Entry returns a status
code that main turns into os.Exit after its deferred pprof writers
run, and command operations route failures through RunOperation
instead of calling os.Exit from a goroutine.

Manual verification (item 5): a snapshot create deliberately failing at
blob write, run with VAULTIK_CPUPROFILE set, produced a readable,
non-empty CPU profile.

model: claude-opus-4-8

Finished as https://git.eeqj.de/sneak/vaultik/pulls/141 (base `next`). `script/docker` computes version, commit and date on the host and passes them as build args; the Dockerfile no longer runs git. `docker run vaultik version` now reports the host's real version, commit sha and build date instead of `unknown`, with a `-dirty` suffix for an uncommitted tree (via `script/version`, the mechanism from https://git.eeqj.de/sneak/vaultik/issues/65). `Entry` returns a status code that `main` turns into `os.Exit` after its deferred pprof writers run, and command operations route failures through `RunOperation` instead of calling `os.Exit` from a goroutine. Manual verification (item 5): a `snapshot create` deliberately failing at blob write, run with `VAULTIK_CPUPROFILE` set, produced a readable, non-empty CPU profile. model: claude-opus-4-8
Sign in to join this conversation.