Skip to content

Commit 314c4c6

Browse files
committed
Enrich checkpoint cli_version with tag and dirty state
Pivot away from a separate Entire-Version commit trailer: the build identity already lives in metadata.json's cli_version on entire/checkpoints/v1, so enrich that field instead of duplicating it in the commit message. versioninfo.CheckpointVersion() now mirrors Go's pseudo-version scheme via runtime/debug.ReadBuildInfo(): release/nightly/mise builds keep their ldflags Version and gain a "+dirty" marker when vcs.modified was set at build time; plain `go build` binaries fall back to the embedded module pseudo-version (e.g. "v0.6.3-...-15d80761c74b+dirty"), which already carries the last known tag, the originating commit, and the dirty marker. Assisted-by: Claude Opus 4.7 <noreply@anthropic.com> Signed-off-by: Paulo Gomes <paulo@entire.io> Entire-Checkpoint: 9f2af0703975
1 parent 5b6cc62 commit 314c4c6

4 files changed

Lines changed: 158 additions & 9 deletions

File tree

cmd/entire/cli/checkpoint/checkpoint_test.go

Lines changed: 11 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -221,6 +221,11 @@ func TestWriteCommitted_AgentField(t *testing.T) {
221221
t.Errorf("commit message should contain %s trailer with value %q, got:\n%s",
222222
trailers.AgentTrailerKey, agentType, commit.Message)
223223
}
224+
225+
// metadata.json records the enriched build identity in cli_version.
226+
if want := versioninfo.CheckpointVersion(); summary.CLIVersion != want {
227+
t.Errorf("summary.CLIVersion = %q, want %q", summary.CLIVersion, want)
228+
}
224229
}
225230

226231
// readLatestSessionMetadata reads the session-specific metadata from the latest session subdirectory.
@@ -3447,8 +3452,8 @@ func TestCopyMetadataDir_RedactsSecrets(t *testing.T) {
34473452
}
34483453
}
34493454

3450-
// TestWriteCommitted_CLIVersionField verifies that versioninfo.Version is written
3451-
// to both the root CheckpointSummary and session-level CommittedMetadata.
3455+
// TestWriteCommitted_CLIVersionField verifies that versioninfo.CheckpointVersion is
3456+
// written to both the root CheckpointSummary and session-level CommittedMetadata.
34523457
func TestWriteCommitted_CLIVersionField(t *testing.T) {
34533458
t.Parallel()
34543459

@@ -3532,8 +3537,8 @@ func TestWriteCommitted_CLIVersionField(t *testing.T) {
35323537
t.Fatalf("failed to parse root metadata.json: %v", err)
35333538
}
35343539

3535-
if summary.CLIVersion != versioninfo.Version {
3536-
t.Errorf("CheckpointSummary.CLIVersion = %q, want %q", summary.CLIVersion, versioninfo.Version)
3540+
if want := versioninfo.CheckpointVersion(); summary.CLIVersion != want {
3541+
t.Errorf("CheckpointSummary.CLIVersion = %q, want %q", summary.CLIVersion, want)
35373542
}
35383543

35393544
// Verify session-level metadata.json (CommittedMetadata) has CLIVersion
@@ -3557,8 +3562,8 @@ func TestWriteCommitted_CLIVersionField(t *testing.T) {
35573562
t.Fatalf("failed to parse session metadata.json: %v", err)
35583563
}
35593564

3560-
if sessionMetadata.CLIVersion != versioninfo.Version {
3561-
t.Errorf("CommittedMetadata.CLIVersion = %q, want %q", sessionMetadata.CLIVersion, versioninfo.Version)
3565+
if want := versioninfo.CheckpointVersion(); sessionMetadata.CLIVersion != want {
3566+
t.Errorf("CommittedMetadata.CLIVersion = %q, want %q", sessionMetadata.CLIVersion, want)
35623567
}
35633568
}
35643569

cmd/entire/cli/checkpoint/committed.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -454,7 +454,7 @@ func (s *GitStore) writeSessionToSubdirectory(ctx context.Context, opts WriteCom
454454
InitialAttribution: opts.InitialAttribution,
455455
PromptAttributions: opts.PromptAttributionsJSON,
456456
Summary: redactSummary(opts.Summary),
457-
CLIVersion: versioninfo.Version,
457+
CLIVersion: versioninfo.CheckpointVersion(),
458458
Kind: opts.Kind,
459459
ReviewSkills: opts.ReviewSkills,
460460
ReviewPrompt: opts.ReviewPrompt,
@@ -503,7 +503,7 @@ func (s *GitStore) writeCheckpointSummary(opts WriteCommittedOptions, basePath s
503503

504504
summary := CheckpointSummary{
505505
CheckpointID: opts.CheckpointID,
506-
CLIVersion: versioninfo.Version,
506+
CLIVersion: versioninfo.CheckpointVersion(),
507507
Strategy: opts.Strategy,
508508
Branch: opts.Branch,
509509
CheckpointsCount: checkpointsCount,
Lines changed: 67 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,73 @@
11
package versioninfo
22

3-
// Version and Commit are set at build time via ldflags.
3+
import (
4+
"runtime/debug"
5+
"strings"
6+
)
7+
8+
// Version and Commit are set at build time via ldflags for release, nightly,
9+
// and `mise run build` binaries. Plain `go build`/`go install` binaries leave
10+
// these at their defaults and rely on the VCS metadata the Go toolchain embeds.
411
var (
512
Version = "dev"
613
Commit = "unknown"
714
)
15+
16+
// dirtySuffix marks a build produced from a modified working tree. It matches
17+
// the suffix the Go toolchain appends to a module pseudo-version.
18+
const dirtySuffix = "+dirty"
19+
20+
// CheckpointVersion returns the build identity recorded in checkpoint metadata
21+
// (the cli_version field on entire/checkpoints/v1). It mirrors Go's
22+
// pseudo-version scheme so a checkpoint can be traced to the last known tag,
23+
// the originating commit, and whether the working tree was dirty at build time.
24+
//
25+
// Release, nightly, and mise builds stamp Version via ldflags, so that value is
26+
// authoritative; only a "+dirty" marker is added when the build tree was
27+
// modified. Plain `go build` binaries leave Version at "dev"; for those we fall
28+
// back to the module pseudo-version the Go toolchain embeds (e.g.
29+
// "v0.6.3-...-15d80761c74b+dirty"), which already carries the tag, commit, and
30+
// dirty marker.
31+
func CheckpointVersion() string {
32+
return describe(Version, readBuildInfo())
33+
}
34+
35+
// buildInfo is the subset of debug.BuildInfo that describe needs, extracted so
36+
// the formatting logic can be exercised in tests without a real build.
37+
type buildInfo struct {
38+
// pseudoVersion is debug.BuildInfo.Main.Version. It is empty or "(devel)"
39+
// when no module pseudo-version is available (e.g. test binaries).
40+
pseudoVersion string
41+
// modified reports whether vcs.modified was "true" at build time.
42+
modified bool
43+
}
44+
45+
func readBuildInfo() buildInfo {
46+
bi, ok := debug.ReadBuildInfo()
47+
if !ok {
48+
return buildInfo{}
49+
}
50+
info := buildInfo{pseudoVersion: bi.Main.Version}
51+
for _, s := range bi.Settings {
52+
if s.Key == "vcs.modified" {
53+
info.modified = s.Value == "true"
54+
}
55+
}
56+
return info
57+
}
58+
59+
func describe(version string, bi buildInfo) string {
60+
// Without an ldflags stamp, defer to the Go-embedded pseudo-version: it
61+
// already encodes the last tag, the commit, and the +dirty marker.
62+
if version == "dev" && hasPseudoVersion(bi.pseudoVersion) {
63+
return bi.pseudoVersion
64+
}
65+
if bi.modified && !strings.HasSuffix(version, dirtySuffix) {
66+
return version + dirtySuffix
67+
}
68+
return version
69+
}
70+
71+
func hasPseudoVersion(v string) bool {
72+
return v != "" && v != "(devel)"
73+
}
Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
package versioninfo
2+
3+
import "testing"
4+
5+
func TestDescribe(t *testing.T) {
6+
t.Parallel()
7+
8+
tests := []struct {
9+
name string
10+
version string
11+
info buildInfo
12+
want string
13+
}{
14+
{
15+
name: "release passes through clean",
16+
version: "v0.6.3",
17+
info: buildInfo{pseudoVersion: "v0.6.3", modified: false},
18+
want: "v0.6.3",
19+
},
20+
{
21+
name: "nightly passes through clean",
22+
version: "v0.6.3-nightly.202605270736.c94e9573",
23+
info: buildInfo{pseudoVersion: "v0.6.3-...", modified: false},
24+
want: "v0.6.3-nightly.202605270736.c94e9573",
25+
},
26+
{
27+
name: "ldflags version gains dirty marker when build tree modified",
28+
version: "v0.6.3-nightly.202605270736.c94e9573-dev-15d80761",
29+
info: buildInfo{pseudoVersion: "v0.6.3-...+dirty", modified: true},
30+
want: "v0.6.3-nightly.202605270736.c94e9573-dev-15d80761+dirty",
31+
},
32+
{
33+
name: "dirty marker not duplicated",
34+
version: "v0.6.3+dirty",
35+
info: buildInfo{modified: true},
36+
want: "v0.6.3+dirty",
37+
},
38+
{
39+
name: "dev falls back to embedded pseudo-version",
40+
version: "dev",
41+
info: buildInfo{pseudoVersion: "v0.6.3-0.20260527133156-15d80761c74b", modified: false},
42+
want: "v0.6.3-0.20260527133156-15d80761c74b",
43+
},
44+
{
45+
name: "dev falls back to dirty pseudo-version verbatim",
46+
version: "dev",
47+
info: buildInfo{pseudoVersion: "v0.6.3-0.20260527133156-15d80761c74b+dirty", modified: true},
48+
want: "v0.6.3-0.20260527133156-15d80761c74b+dirty",
49+
},
50+
{
51+
name: "dev with no pseudo-version stays bare when clean",
52+
version: "dev",
53+
info: buildInfo{pseudoVersion: "(devel)", modified: false},
54+
want: "dev",
55+
},
56+
{
57+
name: "dev with no pseudo-version gains dirty marker",
58+
version: "dev",
59+
info: buildInfo{pseudoVersion: "(devel)", modified: true},
60+
want: "dev+dirty",
61+
},
62+
{
63+
name: "dev with no build info stays bare",
64+
version: "dev",
65+
info: buildInfo{},
66+
want: "dev",
67+
},
68+
}
69+
70+
for _, tt := range tests {
71+
t.Run(tt.name, func(t *testing.T) {
72+
t.Parallel()
73+
if got := describe(tt.version, tt.info); got != tt.want {
74+
t.Errorf("describe(%q, %+v) = %q, want %q", tt.version, tt.info, got, tt.want)
75+
}
76+
})
77+
}
78+
}

0 commit comments

Comments
 (0)