--version goes to stderr and the Docker image always reports "dev" #15

Open
opened 2026-08-09 03:45:59 +02:00 by clawbot · 0 comments
Collaborator

main.go:37 declares Version and main.go:44 wires it into cobra, so -X main.Version=... does work from a local make build. Two things are still wrong.

The version string goes to stderr. main.go:54-55 routes cobra's output and error writers to os.Stderr, which is right for help and usage, but it also sends sfdupes --version there. A version string is the most machine-read output a CLI has; scripts expect it on stdout. README §Design goal 4 says everything on stdout is machine-readable data, which a version string is.

The shipped image can never report a real version. .dockerignore:1 excludes .git, so VERSION := $(shell git describe --tags --always --dirty ...) (Makefile:6) falls back to dev inside the container built at Dockerfile:52. docker run sfdupes --version therefore prints dev for every image ever built, including release images.

Also undocumented: README §Subcommands says "Three subcommands, all implemented" and the usage block lists only the three; neither --version nor -h/--help appears anywhere, nor which stream they use.

Definition of done

  1. sfdupes --version prints one line, sfdupes VERSION, to stdout, and exits 0. Help and usage stay on stderr.
  2. The Dockerfile takes ARG VERSION=dev and passes it through to the build ldflags, and script/docker supplies --build-arg VERSION=$(git describe --tags --always --dirty) so a locally built image reports the real tag. Do not un-exclude .git from .dockerignore.
  3. Per the Go styleguide the build embeds the version only — never build time or build user, which would make the image non-reproducible.
  4. README documents --version and --help, their output streams and their exit codes.
  5. Tests assert the version line goes to stdout, that stderr is empty for --version, and that the exit code is 0.
  6. make check green and make docker succeeds; docker run --rm sfdupes --version prints a real version rather than dev.
`main.go:37` declares `Version` and `main.go:44` wires it into cobra, so `-X main.Version=...` does work from a local `make build`. Two things are still wrong. **The version string goes to stderr.** `main.go:54-55` routes cobra's output and error writers to `os.Stderr`, which is right for help and usage, but it also sends `sfdupes --version` there. A version string is the most machine-read output a CLI has; scripts expect it on stdout. README §Design goal 4 says everything on stdout is machine-readable data, which a version string is. **The shipped image can never report a real version.** `.dockerignore:1` excludes `.git`, so `VERSION := $(shell git describe --tags --always --dirty ...)` (`Makefile:6`) falls back to `dev` inside the container built at `Dockerfile:52`. `docker run sfdupes --version` therefore prints `dev` for every image ever built, including release images. Also undocumented: README §Subcommands says "Three subcommands, all implemented" and the usage block lists only the three; neither `--version` nor `-h`/`--help` appears anywhere, nor which stream they use. ## Definition of done 1. `sfdupes --version` prints one line, `sfdupes VERSION`, to **stdout**, and exits 0. Help and usage stay on stderr. 2. The Dockerfile takes `ARG VERSION=dev` and passes it through to the build ldflags, and `script/docker` supplies `--build-arg VERSION=$(git describe --tags --always --dirty)` so a locally built image reports the real tag. Do not un-exclude `.git` from `.dockerignore`. 3. Per the Go styleguide the build embeds the version only — never build time or build user, which would make the image non-reproducible. 4. README documents `--version` and `--help`, their output streams and their exit codes. 5. Tests assert the version line goes to stdout, that stderr is empty for `--version`, and that the exit code is 0. 6. `make check` green and `make docker` succeeds; `docker run --rm sfdupes --version` prints a real version rather than `dev`.
clawbot added this to the 1.0.0 milestone 2026-08-09 03:45:59 +02:00
Sign in to join this conversation.