▍ humdrum codex / custard v0.3.0
license AGPL-3.0

feat(release): archive an arbitrary ref, decoupled from the version

bbac80daf500efc7f54a4e0662c9bd4cc51b6963
humdrum <me@humdrum.me> · 2026-06-19 08:24

parent 1dd04bc8

2 files changed

internal/release/release.go +8 −3
@@ -113,7 +113,8 @@ 	DLPath    string // tarball output dir
 	TapRepo   string // tap bare repo name (e.g. homebrew-tap)
 	BaseURL   string // e.g. https://codex.humdrum.me
 	Repo      string // repo name
-	Tag       string // vX.Y.Z
+	Ref       string // git ref to archive (tag or release branch); defaults to Tag
+	Tag       string // vX.Y.Z — the version (tarball name, formula, commit msg)
 	Desc      string // repo description (from webhook payload)
 	Package   string // go build path; default "."
 }
@@ -122,6 +123,10 @@ // Publish archives the tag, writes the tarball, renders the formula, and commits
 // it to the tap. Returns the published version on success.
 func Publish(o Options) (string, error) {
 	ver := strings.TrimPrefix(o.Tag, "v")
+	archiveRef := o.Ref
+	if archiveRef == "" {
+		archiveRef = o.Tag
+	}
 	pkg := o.Package
 	if pkg == "" {
 		pkg = "."
@@ -133,7 +138,7 @@ 	if err := os.MkdirAll(o.DLPath, 0o755); err != nil {
 		return "", err
 	}
 	tarPath := filepath.Join(o.DLPath, tarName)
-	if err := gitArchive(bare, o.Repo+"-"+ver, o.Tag, tarPath); err != nil {
+	if err := gitArchive(bare, o.Repo+"-"+ver, archiveRef, tarPath); err != nil {
 		return "", err
 	}
 	sha, err := sha256File(tarPath)
@@ -148,7 +153,7 @@ 		Desc:     formulaDesc(o.Desc),
 		Homepage: o.BaseURL + "/r/" + o.Repo,
 		URL:      o.BaseURL + "/dl/" + tarName,
 		SHA:      sha,
-		License:  detectLicense(o.Store, o.Repo, o.Tag),
+		License:  detectLicense(o.Store, o.Repo, archiveRef),
 		Package:  pkg,
 	}
 	rendered, err := f.render()
internal/release/release_test.go +88 −1
@@ -1,6 +1,14 @@
 package release
 
-import "testing"
+import (
+	"os"
+	"os/exec"
+	"path/filepath"
+	"strings"
+	"testing"
+
+	"custard/internal/gitread"
+)
 
 func TestReleaseSourceAndPrefix(t *testing.T) {
 	var c RepoConfig
@@ -39,3 +47,82 @@ 			t.Errorf("BookmarkVersion(%q,%q) = %q,%v; want %q,%v", c.short, c.prefix, got, ok, c.want, c.ok)
 		}
 	}
 }
+
+func gitRun(t *testing.T, dir string, args ...string) {
+	t.Helper()
+	c := exec.Command("git", args...)
+	c.Dir = dir
+	c.Env = append(c.Environ(),
+		"GIT_AUTHOR_NAME=t", "GIT_AUTHOR_EMAIL=t@x", "GIT_AUTHOR_DATE=2026-01-01T00:00:00",
+		"GIT_COMMITTER_NAME=t", "GIT_COMMITTER_EMAIL=t@x", "GIT_COMMITTER_DATE=2026-01-01T00:00:00")
+	if out, err := c.CombinedOutput(); err != nil {
+		t.Fatalf("git %v: %v: %s", args, err, out)
+	}
+}
+
+// TestPublishArchivesBranchRef verifies bookmark mode: archive the release
+// BRANCH while naming the tarball/formula by the VERSION.
+func TestPublishArchivesBranchRef(t *testing.T) {
+	root := t.TempDir()
+	work := filepath.Join(root, "work")
+	gitRun(t, root, "init", "-q", "-b", "main", "work")
+	if err := os.WriteFile(filepath.Join(work, "go.mod"), []byte("module proj\n"), 0o644); err != nil {
+		t.Fatal(err)
+	}
+	gitRun(t, work, "add", ".")
+	gitRun(t, work, "commit", "-q", "-m", "init")
+	gitRun(t, work, "checkout", "-q", "-b", "release/v1.2.3")
+	gitRun(t, work, "commit", "-q", "--allow-empty", "-m", "ship v1.2.3")
+	gitRun(t, work, "checkout", "-q", "main")
+	gitRun(t, work, "clone", "-q", "--bare", work, filepath.Join(root, "proj.git"))
+
+	// A bare tap repo to receive the formula.
+	tap := filepath.Join(root, "homebrew-tap.git")
+	gitRun(t, root, "init", "-q", "--bare", "homebrew-tap.git")
+	// Seed the tap with one commit so HEAD exists for cloning.
+	tapWork := filepath.Join(root, "tapwork")
+	gitRun(t, root, "clone", "-q", "homebrew-tap.git", "tapwork")
+	if err := os.WriteFile(filepath.Join(tapWork, "README"), []byte("tap\n"), 0o644); err != nil {
+		t.Fatal(err)
+	}
+	gitRun(t, tapWork, "add", ".")
+	gitRun(t, tapWork, "commit", "-q", "-m", "seed")
+	gitRun(t, tapWork, "push", "-q", "origin", "HEAD:master")
+
+	dl := filepath.Join(root, "dl")
+	ver, err := Publish(Options{
+		Store:     gitread.New(root),
+		ReposPath: root,
+		DLPath:    dl,
+		TapRepo:   "homebrew-tap",
+		BaseURL:   "https://example.test",
+		Repo:      "proj",
+		Ref:       "release/v1.2.3", // archive the branch
+		Tag:       "v1.2.3",         // version
+		Desc:      "a thing",
+		Package:   ".",
+	})
+	if err != nil {
+		t.Fatalf("Publish: %v", err)
+	}
+	if ver != "1.2.3" {
+		t.Errorf("ver = %q, want 1.2.3", ver)
+	}
+	if _, err := os.Stat(filepath.Join(dl, "proj-1.2.3.tar.gz")); err != nil {
+		t.Errorf("tarball not written: %v", err)
+	}
+	_ = tap
+}
+
+func TestPublishDefaultsRefToTag(t *testing.T) {
+	// With Ref empty, Publish must archive Tag (tag-mode back-compat). A bogus
+	// repo path makes git archive fail; assert the error names the tag, proving
+	// Tag was used as the archive ref.
+	_, err := Publish(Options{
+		Store: gitread.New(t.TempDir()), ReposPath: t.TempDir(), DLPath: t.TempDir(),
+		Repo: "nope", Tag: "v9.9.9",
+	})
+	if err == nil || !strings.Contains(err.Error(), "v9.9.9") {
+		t.Fatalf("expected archive error naming v9.9.9, got %v", err)
+	}
+}