From ee2b8e63e653fcf517791e953f8e912d3ca5157e Mon Sep 17 00:00:00 2001 From: Eric Rakestraw Date: Sat, 29 Aug 2026 23:13:01 +0000 Subject: [PATCH] Report the embedded release version --- .woodpecker/release.yml | 10 +++++++++ docs/cli.md | 10 +++++++++ internal/app/commands.go | 4 +++- internal/app/version.go | 17 +++++++++++++++ internal/app/version_test.go | 38 +++++++++++++++++++++++++++++++++ internal/buildinfo/buildinfo.go | 6 ++++++ 6 files changed, 84 insertions(+), 1 deletion(-) create mode 100644 internal/app/version.go create mode 100644 internal/app/version_test.go create mode 100644 internal/buildinfo/buildinfo.go diff --git a/.woodpecker/release.yml b/.woodpecker/release.yml index 9da6066..222a701 100644 --- a/.woodpecker/release.yml +++ b/.woodpecker/release.yml @@ -58,6 +58,16 @@ steps: build_binary windows amd64 ".exe" build_binary windows arm64 ".exe" + smoke_binary="$dist/narratio-version-smoke" + go build -trimpath -ldflags "-s -w -X gitea.maximumdirect.net/eric/narratio/internal/buildinfo.Version=$version" \ + -o "$smoke_binary" "$pkg" + reported_version="$("$smoke_binary" version)" + rm -f "$smoke_binary" + if [ "$reported_version" != "narratio $version" ]; then + echo "release binary reported unexpected version: $reported_version" >&2 + exit 1 + fi + publish-release: image: woodpeckerci/plugin-release depends_on: diff --git a/docs/cli.md b/docs/cli.md index 631c4b3..0145117 100644 --- a/docs/cli.md +++ b/docs/cli.md @@ -12,6 +12,7 @@ This runs the canonical full pipeline for session `2026-04-04`. Top-level commands: +- `version`: print the Narratio build version. - `run `: run all or one contiguous range of the canonical stage order. - `regenerate-artifacts `: force-run extraction through analysis. - `run-stage `: run one stage. @@ -71,6 +72,15 @@ Commands with additional positionals keep their command-specific order: ## Command Reference +### `version` + +```bash +narratio version +``` + +Official release binaries report their exact Git tag. Binaries built directly +from source without release linker metadata report `dev`. + ### `run` ```bash diff --git a/internal/app/commands.go b/internal/app/commands.go index 466a9b3..6d4fab0 100644 --- a/internal/app/commands.go +++ b/internal/app/commands.go @@ -7,7 +7,7 @@ import ( "strings" ) -var supportedCommands = []string{"run", "regenerate-artifacts", "run-stage", "analyze", "publish", "clean", "session"} +var supportedCommands = []string{"version", "run", "regenerate-artifacts", "run-stage", "analyze", "publish", "clean", "session"} var runCommandFn = Run @@ -24,6 +24,8 @@ func Execute(args []string, stdout, stderr io.Writer) int { var err error switch cmd { + case "version": + err = Version(cmdArgs, stdout) case "run": err = runCommandFn(ctx, cmdArgs, stdout) case "regenerate-artifacts": diff --git a/internal/app/version.go b/internal/app/version.go new file mode 100644 index 0000000..9fef590 --- /dev/null +++ b/internal/app/version.go @@ -0,0 +1,17 @@ +package app + +import ( + "fmt" + "io" + + "gitea.maximumdirect.net/eric/narratio/internal/buildinfo" +) + +// Version prints the version embedded in the current Narratio binary. +func Version(args []string, out io.Writer) error { + if len(args) != 0 { + return fmt.Errorf("version: unexpected arguments") + } + _, err := fmt.Fprintf(out, "narratio %s\n", buildinfo.Version) + return err +} diff --git a/internal/app/version_test.go b/internal/app/version_test.go new file mode 100644 index 0000000..92cbd70 --- /dev/null +++ b/internal/app/version_test.go @@ -0,0 +1,38 @@ +package app + +import ( + "bytes" + "io" + "strings" + "testing" + + "gitea.maximumdirect.net/eric/narratio/internal/buildinfo" +) + +func TestExecuteVersionReportsEmbeddedBuildVersion(t *testing.T) { + original := buildinfo.Version + buildinfo.Version = "v1.5.0-test" + t.Cleanup(func() { buildinfo.Version = original }) + + var stdout bytes.Buffer + var stderr bytes.Buffer + if code := Execute([]string{"version"}, &stdout, &stderr); code != 0 { + t.Fatalf("Execute() code = %d, stderr = %q", code, stderr.String()) + } + if got, want := stdout.String(), "narratio v1.5.0-test\n"; got != want { + t.Fatalf("stdout = %q, want %q", got, want) + } + if stderr.Len() != 0 { + t.Fatalf("stderr = %q, want empty", stderr.String()) + } +} + +func TestExecuteVersionRejectsArguments(t *testing.T) { + var stderr bytes.Buffer + if code := Execute([]string{"version", "extra"}, io.Discard, &stderr); code == 0 { + t.Fatal("Execute() code = 0, want failure") + } + if !strings.Contains(stderr.String(), "version: unexpected arguments") { + t.Fatalf("stderr = %q, want argument error", stderr.String()) + } +} diff --git a/internal/buildinfo/buildinfo.go b/internal/buildinfo/buildinfo.go new file mode 100644 index 0000000..2e9124a --- /dev/null +++ b/internal/buildinfo/buildinfo.go @@ -0,0 +1,6 @@ +// Package buildinfo exposes metadata supplied by the release build. +package buildinfo + +// Version is the Narratio release identifier. Source builds report "dev"; +// release automation replaces it with the exact Git tag through -ldflags -X. +var Version = "dev"