Skip to content

VERSION files + buildinfo-based VCS info (discussion #13287)#13313

Draft
shykes wants to merge 10 commits into
mainfrom
better-version-strings
Draft

VERSION files + buildinfo-based VCS info (discussion #13287)#13313
shykes wants to merge 10 commits into
mainfrom
better-version-strings

Conversation

@shykes
Copy link
Copy Markdown
Contributor

@shykes shykes commented Jun 3, 2026

Draft. Implements the foundation of discussion #13287 — replacing git-tag-walking + scattered ldflags injection with embedded VERSION files and a generic buildinfo wrapper. P1+P2 only; P3 (helm), P4 (SDKs), P5 (release-by-promotion) follow separately.

Shape

  • internal/version — embeds internal/version/VERSION via //go:embed. Exposes Version, Commit, Dirty, CommitTime, and Canonical() (`X.Y.Z+abc12345[.dirty]`). VCS fields come from `runtime/debug.ReadBuildInfo`, wrapped by `github.com/dagger/go/buildinfo`.

  • github.com/dagger/go/buildinfo — wraps `runtime/debug.ReadBuildInfo` with four `Injected*` package vars settable via `-ldflags`. Empty injected vars defer to whatever the toolchain stamped natively; non-empty ones override. Lives in the dagger/go repo so other projects (Daggerverse modules, third-party Go tools) can reuse the same mechanism.

  • `dagger version` — new output per the proposal:
    ```
    $ dagger version
    version: 0.21.3
    commit: 3059ea7
    dirty: yes

    $ dagger version -q
    0.21.3+3059ea74.dirty
    ```

  • `engine.Version` / `engine.Tag` — kept as the public API every callsite uses; `init()` populates them from `internal/version`. Zero callsite changes needed elsewhere.

  • `toolchains/go` — gains an auto-acquired `dagger.GitRepository` input (via `+defaultPath="/"`). When present, reads HEAD commit + uncommitted state via Dagger primitives (no `git` CLI shell-out) and appends `-X github.com/dagger/go/buildinfo.Injected=…` ldflags. cli-dev/engine-dev/release continue calling `dag.Go(...)` unchanged.

What's intentionally out of scope here

  • P3 — helm chart `VERSION` — separate PR.
  • P4 — SDK migrations — per-SDK, separate PRs.
  • P5 — release-by-promotion + manifest — bigger architectural change; separate.
  • Workspace.git (PR Workspace.git(): contextual information about a workspace's git repository #13074) — TODO note in `toolchains/go/main.go`. Will swap once we depend on Dagger ≥ 1.0.0-beta.2.
  • Commit time injection — waits for the GitCommit proposal (New core type: GitCommit #13086).
  • `engine.IsDevVersion` cleanup — works under the new system (dev builds report clean version strings now, so update-check kicks in for matched-version dev builds). Real cleanup belongs with the `--check` rework in P5.

Commits

  1. `chore: remove version module` — the old `/version` Dagger module deleted (was doing git-tag-walking).
  2. `add version package and switch dagger CLI to it` — `internal/version` (initially under `/version/`).
  3. `move version package under internal/` — explicit scope-limit. `runtime/debug.ReadBuildInfo` returns the main module's VCS info; the package's Version+VCS fusion is only meaningful for binaries that are themselves Dagger binaries. `internal/` enforces that at the compiler level.
  4. `populate engine.Version / engine.Tag from internal/version` — public API preserved.
  5. `drop dag.Version() callsites and ldflags injection from toolchains` — clears the `dag.Version()` FIXMEs in cli-dev/engine-dev/release. `TestLocalRelease` gains a required `version` parameter.
  6. `toolchains/go: stamp VCS info into binaries via buildinfo ldflags` — the auto-acquired `*dagger.GitRepository` input + the helper.
  7. `go.mod: drop buildinfo local replace, consume dagger/go main` — `replace` swapped for the pseudo-version against dagger/go main.

Known follow-ups before un-drafting

  • Rebase onto current main. Conflicts expected in `toolchains/cli-dev/main.go` and `toolchains/engine-dev/main.go` from upstream fix incorrect artifact caching issue when releasing #13285.
  • `dagger develop` on `toolchains/go` and each consumer module to regenerate `dagger.gen.go` with the new `repo` input on `GoOpts`. Without this, the sandbox build doesn't actually pass the workspace through and `dagger version` will still report `commit: unknown`.
  • End-to-end verification: sandbox-build cmd/dagger, confirm `dagger version` reports the real commit + dirty.
  • `go mod tidy` in a clean tree to fix the `// indirect` marker on `github.com/dagger/go/buildinfo` (internal/version imports it directly).

How `dagger version` looks today (native `go build` from this checkout)

```
$ dagger version
version: 0.21.3
commit: 3059ea7
dirty: yes

$ dagger version -q
0.21.3+3059ea74.dirty
```

Stdlib's `runtime/debug.ReadBuildInfo` already populates VCS settings when building from a real checkout. The buildinfo wrapper kicks in for sandbox builds (after `dagger develop` regenerates the consumer gen.go and the new `repo` input flows through).

Refs: discussion #13287, #13074 (Workspace.git), #13086 (GitCommit proposal).

@tiborvass tiborvass force-pushed the better-version-strings branch 3 times, most recently from 1e1f733 to dfa29b4 Compare June 4, 2026 16:41
@dagger-codex dagger-codex Bot force-pushed the better-version-strings branch 2 times, most recently from 4930c51 to 279cfcc Compare June 4, 2026 18:36
shykes added 8 commits June 4, 2026 23:02
Delete the standalone ./version module and all references to it from
dagger.json dependency lists and the helm e2e workspace includes.

The Go callsites that still invoke dag.Version() (in cli-dev, engine-dev,
release) are flagged with FIXME comments; they will not compile until
replaced with another source of version info.

Signed-off-by: Solomon Hykes <solomon@dagger.io>
New package github.com/dagger/dagger/version owns the Dagger build's
identity at runtime:

- semantic version embedded from version/VERSION via //go:embed
- commit/dirty/time read from runtime/debug build info, wrapped by
  github.com/dagger/go/buildinfo to leave room for ldflags injection
  from sandboxed builds in a follow-up
- exposes Canonical() returning "X.Y.Z+abc12345[.dirty]" per discussion #13287

dagger version output now follows the discussion format:

  $ dagger version
  version: 0.21.3
  commit:  0bbaf39
  dirty:   yes

  $ dagger version -q
  0.21.3+0bbaf39f.dirty

engine.Version / engine.Tag and the --check / update-available flow are
untouched here; they get migrated in P1.3 / P5.

buildinfo is pulled in via a local replace until it ships a tag in the
dagger/go repo.

Signed-off-by: Solomon Hykes <solomon@dagger.io>
runtime/debug.ReadBuildInfo().Settings returns the *main module's* VCS
info, never an imported module's. The version package fuses Dagger's
embedded semver with whatever vcs.* the toolchain stamped — that fusion
is only meaningful when the binary being built is itself a Dagger binary.

Marking it internal/ blocks external consumers at compile time, so any
third-party importer can't accidentally read "0.21.3 + my-own-binary's
commit hash" and mistake it for Dagger's identity.

Signed-off-by: Solomon Hykes <solomon@dagger.io>
engine.Version and engine.Tag remain the public API every callsite uses.
They're no longer ldflags-injected — init() reads from internal/version
(which embeds VERSION and wraps runtime/debug for VCS info):

  Version = "v" + iversion.Version
  Tag     = iversion.Commit if known, else Version

The "v" prefix matches the semver convention this codebase already
threads through golang.org/x/mod/semver. The Tag fallback matches the
existing convention where dev/main builds tag the engine image by
commit and releases tag it by version.

DAGGER_VERSION / DAGGER_TAG / DAGGER_MINIMUM_VERSION env-var overrides
still work for tests (unchanged). Minimum-version capping also unchanged.

cli-dev/engine-dev still pass -X engine.Version / engine.Tag ldflags —
those become no-ops now (the vars get overwritten by init from
internal/version). P1.4 removes the ldflags injection from those
toolchains. P2 wires sandbox builds to inject via buildinfo's
Injected* vars instead, so sandbox builds self-report VCS correctly.

Signed-off-by: Solomon Hykes <solomon@dagger.io>
The version module is gone (commit 0bbaf39) and engine.Version is now
populated from the embedded VERSION file via internal/version (commit
f15863e). The -X engine.Version / -X engine.Tag ldflags the toolchains
were still passing became no-ops the moment P1.3 landed — init()
overwrites those vars unconditionally.

- toolchains/cli-dev/main.go: drop dag.Version()/dag.Version().ImageTag()
  fallback, drop the ldflags. CliDev.Version is whatever the caller
  passes; publish flow callers still set it explicitly for goreleaser
  ENGINE_VERSION / semver gating / S3 paths. The built binary
  self-reports correctly from embedded VERSION regardless.

- toolchains/engine-dev/main.go: drop dag.Version() lookup in Service;
  use the existing version parameter directly for cache volume naming,
  fall back to random when empty (existing behavior).

- toolchains/engine-dev/test.go: drop the ldflags injection block
  entirely — it was already a no-op. Drop "strings" import.

- toolchains/release/tests.go: TestLocalRelease gains a required
  version parameter (was reading via dag.Version()). gen.go updated
  manually; next `dagger develop` will regenerate cleanly.

- toolchains/engine-dev/build/builder.go: drop dag.Version() and
  ldflags injection block entirely.

Sandbox-built binaries still report empty commit/dirty until P2 wires
buildinfo's Injected* vars through cli-dev's go build invocation.

Signed-off-by: Solomon Hykes <solomon@dagger.io>
Add an auto-acquired *dagger.GitRepository input to the Go builder's New
constructor. When present (default: the workspace root), read HEAD commit
and uncommitted state, and append -X github.com/dagger/go/buildinfo.Injected*=
values to the -ldflags pipeline.

Binaries that import buildinfo (transitively via
github.com/dagger/dagger/internal/version, after P1) self-report VCS info
through runtime/debug.ReadBuildInfo without needing .git inside the build
container.

GitRepository was chosen over a Directory input because it's lazier — no
full repo upload, the engine fetches only what GraphQL operations actually
need. The dirty signal uses GitRepository.uncommitted, which the engine
already implements with the right gitignore semantics.

Inspection errors are swallowed silently — builds proceed without VCS info
rather than aborting (matches the deleted version/ module's failure mode).

TODO: switch to Workspace.git (PR #13074) when Dagger >= 1.0.0-beta.2.
It's lazier still, supports nested workspaces cleanly, and will expose
commit time once the GitCommit proposal (#13086) lands.

No caller changes needed: cli-dev/engine-dev/release continue calling
dag.Go(GoOpts{...}) unchanged. The new input auto-acquires from the
workspace root at module-call time. dagger.gen.go for consumers will pick
up the new optional field next time they run `dagger develop`.

Signed-off-by: Solomon Hykes <solomon@dagger.io>
buildinfo lives at github.com/dagger/go/buildinfo on the dagger/go
default branch as of bcbbfc7. Switch from the local-checkout replace
directive (added in P1.2 as a temporary hold) to the real pseudo-version.

The // indirect marker is cosmetic — internal/version imports it
directly. `go mod tidy` errored partway on an unrelated querybuilder
snippet import before reaching the direct/indirect classification; a
follow-up tidy in a clean tree will fix the label.

Signed-off-by: Solomon Hykes <solomon@dagger.io>
Signed-off-by: Solomon Hykes <solomon@dagger.io>
@dagger-codex dagger-codex Bot force-pushed the better-version-strings branch from 279cfcc to edb9125 Compare June 4, 2026 23:08
Signed-off-by: Solomon Hykes <solomon@dagger.io>
@dagger-codex dagger-codex Bot force-pushed the better-version-strings branch from edb9125 to 0aff9e0 Compare June 4, 2026 23:16
Signed-off-by: Solomon Hykes <solomon@dagger.io>
@dagger-codex dagger-codex Bot force-pushed the better-version-strings branch from 687602c to 6eb7e0d Compare June 5, 2026 18:10
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant