Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 11 additions & 6 deletions cmd/entire/cli/checkpoint/checkpoint_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -221,6 +221,11 @@ func TestWriteCommitted_AgentField(t *testing.T) {
t.Errorf("commit message should contain %s trailer with value %q, got:\n%s",
trailers.AgentTrailerKey, agentType, commit.Message)
}

// metadata.json records the enriched build identity in cli_version.
if want := versioninfo.CheckpointVersion(); summary.CLIVersion != want {
t.Errorf("summary.CLIVersion = %q, want %q", summary.CLIVersion, want)
}
}

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

// TestWriteCommitted_CLIVersionField verifies that versioninfo.Version is written
// to both the root CheckpointSummary and session-level CommittedMetadata.
// TestWriteCommitted_CLIVersionField verifies that versioninfo.CheckpointVersion is
// written to both the root CheckpointSummary and session-level CommittedMetadata.
func TestWriteCommitted_CLIVersionField(t *testing.T) {
t.Parallel()

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

if summary.CLIVersion != versioninfo.Version {
t.Errorf("CheckpointSummary.CLIVersion = %q, want %q", summary.CLIVersion, versioninfo.Version)
if want := versioninfo.CheckpointVersion(); summary.CLIVersion != want {
t.Errorf("CheckpointSummary.CLIVersion = %q, want %q", summary.CLIVersion, want)
}

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

if sessionMetadata.CLIVersion != versioninfo.Version {
t.Errorf("CommittedMetadata.CLIVersion = %q, want %q", sessionMetadata.CLIVersion, versioninfo.Version)
if want := versioninfo.CheckpointVersion(); sessionMetadata.CLIVersion != want {
t.Errorf("CommittedMetadata.CLIVersion = %q, want %q", sessionMetadata.CLIVersion, want)
}
}

Expand Down
4 changes: 2 additions & 2 deletions cmd/entire/cli/checkpoint/committed.go
Original file line number Diff line number Diff line change
Expand Up @@ -454,7 +454,7 @@ func (s *GitStore) writeSessionToSubdirectory(ctx context.Context, opts WriteCom
InitialAttribution: opts.InitialAttribution,
PromptAttributions: opts.PromptAttributionsJSON,
Summary: redactSummary(opts.Summary),
CLIVersion: versioninfo.Version,
CLIVersion: versioninfo.CheckpointVersion(),
Kind: opts.Kind,
ReviewSkills: opts.ReviewSkills,
ReviewPrompt: opts.ReviewPrompt,
Expand Down Expand Up @@ -503,7 +503,7 @@ func (s *GitStore) writeCheckpointSummary(opts WriteCommittedOptions, basePath s

summary := CheckpointSummary{
CheckpointID: opts.CheckpointID,
CLIVersion: versioninfo.Version,
CLIVersion: versioninfo.CheckpointVersion(),
Strategy: opts.Strategy,
Branch: opts.Branch,
CheckpointsCount: checkpointsCount,
Expand Down
3 changes: 1 addition & 2 deletions cmd/entire/cli/fetch_no_config_pollution_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@ import (
"strings"
"testing"

"github.com/entireio/cli/cmd/entire/cli/checkpoint"
"github.com/entireio/cli/cmd/entire/cli/checkpoint/id"
"github.com/entireio/cli/cmd/entire/cli/paths"
"github.com/entireio/cli/cmd/entire/cli/testutil"
Expand Down Expand Up @@ -124,7 +123,7 @@ func TestFetchV2MainTreeOnly_DoesNotCreateShallowRepository(t *testing.T) {
if err != nil {
t.Fatalf("failed to open producer repo: %v", err)
}
writeV2CheckpointForExport(t, producerRepo, id.MustCheckpointID("121212121212"), checkpoint.WriteCommittedOptions{
writeV2CheckpointForExport(t, producerRepo, id.MustCheckpointID("121212121212"), v2CheckpointFixtureOptions{
SessionID: "fetch-v2-shallow-guard",
Transcript: redact.AlreadyRedacted([]byte(`{"type":"user","message":{"content":[{"type":"text","text":"hello"}]}}` + "\n")),
})
Expand Down
68 changes: 67 additions & 1 deletion cmd/entire/cli/versioninfo/versioninfo.go
Original file line number Diff line number Diff line change
@@ -1,7 +1,73 @@
package versioninfo

// Version and Commit are set at build time via ldflags.
import (
"runtime/debug"
"strings"
)

// Version and Commit are set at build time via ldflags for release, nightly,
// and `mise run build` binaries. Plain `go build`/`go install` binaries leave
// these at their defaults and rely on the VCS metadata the Go toolchain embeds.
var (
Version = "dev"
Commit = "unknown"
)

// dirtySuffix marks a build produced from a modified working tree. It matches
// the suffix the Go toolchain appends to a module pseudo-version.
const dirtySuffix = "+dirty"

// CheckpointVersion returns the build identity recorded in checkpoint metadata
// (the cli_version field on entire/checkpoints/v1). It mirrors Go's
// pseudo-version scheme so a checkpoint can be traced to the last known tag,
// the originating commit, and whether the working tree was dirty at build time.
//
// Release, nightly, and mise builds stamp Version via ldflags, so that value is
// authoritative; only a "+dirty" marker is added when the build tree was
// modified. Plain `go build` binaries leave Version at "dev"; for those we fall
// back to the module pseudo-version the Go toolchain embeds (e.g.
// "v0.6.3-...-15d80761c74b+dirty"), which already carries the tag, commit, and
// dirty marker.
func CheckpointVersion() string {
return describe(Version, readBuildInfo())
}

// buildInfo is the subset of debug.BuildInfo that describe needs, extracted so
// the formatting logic can be exercised in tests without a real build.
type buildInfo struct {
// pseudoVersion is debug.BuildInfo.Main.Version. It is empty or "(devel)"
// when no module pseudo-version is available (e.g. test binaries).
pseudoVersion string
// modified reports whether vcs.modified was "true" at build time.
modified bool
}

func readBuildInfo() buildInfo {
bi, ok := debug.ReadBuildInfo()
if !ok {
return buildInfo{}
}
info := buildInfo{pseudoVersion: bi.Main.Version}
for _, s := range bi.Settings {
if s.Key == "vcs.modified" {
info.modified = s.Value == "true"
}
}
return info
}

func describe(version string, bi buildInfo) string {
// Without an ldflags stamp, defer to the Go-embedded pseudo-version: it
// already encodes the last tag, the commit, and the +dirty marker.
if version == "dev" && hasPseudoVersion(bi.pseudoVersion) {
return bi.pseudoVersion
}
if bi.modified && !strings.HasSuffix(version, dirtySuffix) {
return version + dirtySuffix
}
return version
}

func hasPseudoVersion(v string) bool {
return v != "" && v != "(devel)"
}
79 changes: 79 additions & 0 deletions cmd/entire/cli/versioninfo/versioninfo_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
package versioninfo

import "testing"

func TestDescribe(t *testing.T) {
t.Parallel()

tests := []struct {
name string
version string
info buildInfo
want string
}{
{
name: "release passes through clean",
version: "v0.6.3",
info: buildInfo{pseudoVersion: "v0.6.3", modified: false},
want: "v0.6.3",
},
{
name: "nightly passes through clean",
version: "v0.6.3-nightly.202605270736.c94e9573",
info: buildInfo{pseudoVersion: "v0.6.3-...", modified: false},
want: "v0.6.3-nightly.202605270736.c94e9573",
},
{
name: "ldflags version gains dirty marker when build tree modified",
version: "v0.6.3-nightly.202605270736.c94e9573-dev-15d80761",
info: buildInfo{pseudoVersion: "v0.6.3-...+dirty", modified: true},
want: "v0.6.3-nightly.202605270736.c94e9573-dev-15d80761+dirty",
},
{
name: "dirty marker not duplicated",
version: "v0.6.3+dirty",
info: buildInfo{modified: true},
want: "v0.6.3+dirty",
},
{
name: "dev falls back to embedded pseudo-version",
version: "dev",
info: buildInfo{pseudoVersion: "v0.6.3-0.20260527133156-15d80761c74b", modified: false},
want: "v0.6.3-0.20260527133156-15d80761c74b",
},
{
name: "dev falls back to dirty pseudo-version verbatim",
version: "dev",
info: buildInfo{pseudoVersion: "v0.6.3-0.20260527133156-15d80761c74b+dirty", modified: true},
want: "v0.6.3-0.20260527133156-15d80761c74b+dirty",
},
{
name: "dev with no pseudo-version stays bare when clean",
version: "dev",
info: buildInfo{pseudoVersion: "(devel)", modified: false},
want: "dev",
},
{
name: "dev with no pseudo-version gains dirty marker",
version: "dev",
info: buildInfo{pseudoVersion: "(devel)", modified: true},
want: "dev+dirty",
},
{
name: "dev with no build info stays bare",
version: "dev",
info: buildInfo{},
want: "dev",
},
}

for i := range tests {
tt := tests[i]
t.Run(tt.name, func(t *testing.T) {
t.Parallel()
if got := describe(tt.version, tt.info); got != tt.want {
t.Errorf("describe(%q, %+v) = %q, want %q", tt.version, tt.info, got, tt.want)
}
})
}
}
Loading