From b2cc4482f6c1a2c6f36d72fbc1ccc203aaa8659e Mon Sep 17 00:00:00 2001 From: Cole Speelman Date: Fri, 14 Aug 2026 14:19:01 -0400 Subject: [PATCH] fix: prepare Tend preview 2 Publish the reviewed version identity fix and mark preview 1 withdrawn.\n\nPrivate-Source-Commit: 9907afdfa18099ac488fe4011291ea454036435a\nPrivate-Source-Tree: 3a5e666dd6a0c69a07f7804612c8e3dc3474966a\nHimesan-Output-Permission: v1.0\nAI-Assistance: OpenAI Codex assisted implementation and review. Signed-off-by: Cole Speelman --- README.md | 2 +- RELEASE.md | 8 +++- docs/DOGFOOD_EVIDENCE.md | 7 +++ internal/version/version.go | 75 +++++++++++++++++++++++++++++++- internal/version/version_test.go | 33 ++++++++++++++ scripts/public-snapshot.allow | 1 + 6 files changed, 123 insertions(+), 3 deletions(-) create mode 100644 internal/version/version_test.go diff --git a/README.md b/README.md index b495d0e..2f597dd 100644 --- a/README.md +++ b/README.md @@ -17,7 +17,7 @@ shell hooks. Application-specific data activation remains application-specific. ```text tend check --config tend.json -tend package --config tend.json --version v0.1.0-preview.1 --out dist +tend package --config tend.json --version v0.1.0-preview.2 --out dist tend deploy --config /etc/example/tend.json --artifact FILE --sha256 HEX --approve-sha256 HEX tend status --config /etc/example/tend.json tend rollback --config /etc/example/tend.json diff --git a/RELEASE.md b/RELEASE.md index 6c3edbc..a70698e 100644 --- a/RELEASE.md +++ b/RELEASE.md @@ -1,6 +1,6 @@ # Preview release policy -`v0.1.0-preview.1` may be published only after one identical candidate Tend +`v0.1.0-preview.2` may be published only after one identical candidate Tend binary has successfully completed maintenance releases for Gamertan and the Sandwich Hime website, including injected-failure restoration and explicit rollback proof. The August 14, 2026 campaign met that gate; the scoped, @@ -19,6 +19,12 @@ The release tag and attached candidate must be built from the final reviewed source commit. Documentation-only changes after the recorded campaign require one final identical-candidate maintenance pass before tagging. +`v0.1.0-preview.1` is immutable but withdrawn: its source and module checksums +are valid, while a fresh `go install` reports the development identity because +the CLI did not yet adopt the tagged module version from Go build information. +Preview 2 adds that identity path and its regression tests; preview 1 is never +retagged or rewritten. + The canonical public origin is `ssh://git@gitea.speelman.ca:2222/gamertan/tend.git`. GitHub is a read-only discovery snapshot. Private development history is not published or merged into either public history. diff --git a/docs/DOGFOOD_EVIDENCE.md b/docs/DOGFOOD_EVIDENCE.md index 756851a..6cb56b9 100644 --- a/docs/DOGFOOD_EVIDENCE.md +++ b/docs/DOGFOOD_EVIDENCE.md @@ -22,6 +22,13 @@ The release tag and attached assets must still identify their own exact source commit and digests. Any code change after this campaign requires the dogfood sequence to be repeated. +A fresh-install check after the first immutable source tag found that the CLI +reported its development identity instead of the tagged module version. No +deployment logic or artifact content was ambiguous, but the distribution +identity was not acceptable. Preview 1 remains immutable and withdrawn; +Preview 2 adds Go build-information version selection and repeats the release +gates rather than retagging old content. + ## Sandwich Hime website The singleton-candidate strategy packaged and activated website preview 25: diff --git a/internal/version/version.go b/internal/version/version.go index 8613d6c..a0c90a8 100644 --- a/internal/version/version.go +++ b/internal/version/version.go @@ -2,8 +2,81 @@ package version +import ( + "regexp" + "runtime/debug" + "strings" +) + +const developmentVersion = "v0.1.0-dev" + var ( - Version = "v0.1.0-dev" + Version = developmentVersion Commit = "unknown" Date = "unknown" ) + +var ( + taggedVersionPattern = regexp.MustCompile(`^v(?:0|[1-9][0-9]*)\.(?:0|[1-9][0-9]*)\.(?:0|[1-9][0-9]*)(?:-([0-9A-Za-z-]+(?:\.[0-9A-Za-z-]+)*))?$`) + pseudoVersionSuffixPattern = regexp.MustCompile(`(?:^|[.-])(?:0\.)?[0-9]{14}-[0-9a-f]{12,}$`) +) + +func init() { + information, ok := debug.ReadBuildInfo() + if !ok { + return + } + Version = selectVersion(Version, information.Main.Version) + for _, setting := range information.Settings { + switch setting.Key { + case "vcs.revision": + if Commit == "unknown" && setting.Value != "" { + Commit = setting.Value + } + case "vcs.time": + if Date == "unknown" && setting.Value != "" { + Date = setting.Value + } + } + } +} + +func selectVersion(linkerValue, moduleVersion string) string { + if linkerValue != developmentVersion { + return linkerValue + } + moduleVersion = strings.TrimSpace(moduleVersion) + if !isTaggedVersion(moduleVersion) { + return linkerValue + } + return moduleVersion +} + +func isTaggedVersion(value string) bool { + matches := taggedVersionPattern.FindStringSubmatch(value) + if matches == nil { + return false + } + prerelease := matches[1] + if pseudoVersionSuffixPattern.MatchString(prerelease) { + return false + } + for _, identifier := range strings.Split(prerelease, ".") { + if len(identifier) > 1 && identifier[0] == '0' && allDecimal(identifier) { + return false + } + } + return true +} + +func allDecimal(value string) bool { + if value == "" { + return false + } + for _, character := range value { + if character < '0' || character > '9' { + return false + } + } + return true +} diff --git a/internal/version/version_test.go b/internal/version/version_test.go new file mode 100644 index 0000000..36a3bad --- /dev/null +++ b/internal/version/version_test.go @@ -0,0 +1,33 @@ +// SPDX-License-Identifier: AGPL-3.0-only + +package version + +import "testing" + +func TestSelectVersion(t *testing.T) { + t.Parallel() + tests := []struct { + name, linker, module, want string + }{ + {"local build", developmentVersion, "(devel)", developmentVersion}, + {"missing build info", developmentVersion, "", developmentVersion}, + {"preview one install", developmentVersion, "v0.1.0-preview.1", "v0.1.0-preview.1"}, + {"preview two install", developmentVersion, "v0.1.0-preview.2", "v0.1.0-preview.2"}, + {"release install", developmentVersion, "v1.0.0", "v1.0.0"}, + {"linker override", "v0.1.0-preview.2", "(devel)", "v0.1.0-preview.2"}, + {"pseudo version", developmentVersion, "v0.0.0-20260814120000-0123456789ab", developmentVersion}, + {"pseudo after release", developmentVersion, "v0.1.1-0.20260814120000-0123456789ab", developmentVersion}, + {"build metadata", developmentVersion, "v0.1.0+dirty", developmentVersion}, + {"leading zero release", developmentVersion, "v00.1.0", developmentVersion}, + {"leading zero prerelease", developmentVersion, "v0.1.0-preview.02", developmentVersion}, + } + for _, test := range tests { + test := test + t.Run(test.name, func(t *testing.T) { + t.Parallel() + if got := selectVersion(test.linker, test.module); got != test.want { + t.Fatalf("selectVersion(%q, %q)=%q want %q", test.linker, test.module, got, test.want) + } + }) + } +} diff --git a/scripts/public-snapshot.allow b/scripts/public-snapshot.allow index 11c5da6..993d5c8 100644 --- a/scripts/public-snapshot.allow +++ b/scripts/public-snapshot.allow @@ -36,6 +36,7 @@ internal/provenance/git.go internal/state/state.go internal/state/state_test.go internal/version/version.go +internal/version/version_test.go release/tend.json scripts/check-licenses.sh scripts/export-public.sh