feat: publish releases with this repository's own SDK code
There is no CI: the instance has no act_runner and none is planned, so releases are cut by hand. That makes `make check` the only thing standing between a mistake and the tracker, and it is one command: gofmt, vet, the suite with the cache defeated, `go mod verify`, a vendored build, and `kettle gen skills --check`. The last one is the invariant worth having — the plugin's SKILL.md command reference is generated from the binary's registry, so a flag that changed cannot ship with documentation that recommends the old one. `cli/cmd/release` publishes to Gitea using the same SDK the binary already vendors, which is a pleasing thing to be able to say: nothing third-party handles the artifacts. It is a second binary rather than a `kettle` subcommand on purpose — `kettle`'s command tree is what generates the plugin's skills, so a verb there ships to every operator, and publishing a release is build infrastructure. It is idempotent end to end: an existing release for the tag is reused, an asset of the same name is replaced rather than doubled, and a retried run converges instead of duplicating. `make release` refuses three things, each with its own message: a dirty working tree, a TAG that is not what `git describe` reports, and a tag the remote does not have. A release built from uncommitted code is unreproducible and nobody finds out until they need to reproduce it. `kettle version` reports the stamp, the toolchain and the VCS revision. The default is `dev`, and a hand build says so and means it — a binary out of somebody's working tree is not a release and must not claim to be one. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -491,6 +491,44 @@ func TestAMissingLoginIsExplained(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
// The version is "dev" until a build stamps it, and the STAMPING is what is
|
||||
// tested here rather than the printing.
|
||||
//
|
||||
// A `-X` whose symbol path is one character wrong is not an error: the linker
|
||||
// ignores it and the binary goes on reporting "dev" for the rest of its life,
|
||||
// which is discovered by an operator holding a release that will not say what
|
||||
// it is. So this builds with the flag the Makefile uses and reads the answer
|
||||
// back out of the binary.
|
||||
func TestVersionSaysDevUntilABuildStampsIt(t *testing.T) {
|
||||
dir := t.TempDir()
|
||||
|
||||
r := mustRun(t, dir, "version")
|
||||
if !strings.Contains(r.stdout, "dev") || !strings.Contains(r.stdout, "built") {
|
||||
t.Errorf("a build from source must say what it is:\n%s", r.out())
|
||||
}
|
||||
// A version needs no project: it is a fact about the binary, and the
|
||||
// question is asked most often by somebody whose project is not resolving.
|
||||
if short := mustRun(t, dir, "version", "--short"); strings.TrimSpace(short.stdout) != "dev" {
|
||||
t.Errorf("--short printed %q, want dev", short.stdout)
|
||||
}
|
||||
|
||||
const stamp = "v9.9.9-from-the-test"
|
||||
stamped := filepath.Join(t.TempDir(), "kettle")
|
||||
build := exec.Command("go", "build",
|
||||
"-ldflags", "-X git.noodles.cam/claude-skills/marketplace/cli/internal/cmd.Version="+stamp,
|
||||
"-o", stamped, "../../cmd/kettle")
|
||||
if out, err := build.CombinedOutput(); err != nil {
|
||||
t.Fatalf("building a stamped binary: %v\n%s", err, out)
|
||||
}
|
||||
out, err := exec.Command(stamped, "version", "--short").Output()
|
||||
if err != nil {
|
||||
t.Fatalf("running the stamped binary: %v", err)
|
||||
}
|
||||
if got := strings.TrimSpace(string(out)); got != stamp {
|
||||
t.Errorf("the stamped binary reports %q, want %q — the -X symbol path is wrong", got, stamp)
|
||||
}
|
||||
}
|
||||
|
||||
func closeIssue(t *testing.T, path string) {
|
||||
t.Helper()
|
||||
setField(t, path, "state", "closed")
|
||||
|
||||
Reference in New Issue
Block a user