From 0cc4c36302dec17263f2ee9bc6b7291605838a0a Mon Sep 17 00:00:00 2001 From: Benjamin Wester Date: Fri, 13 Mar 2026 02:38:44 +0000 Subject: [PATCH 1/3] feat: Support pseudo-versions in Go module proxy Go allows module versions to be specified as a pesudo-version string, like "v0.0.0-20191109021931-daa7c04131f5". This commit adds support to the Go private module fetcher to extract and use the git object name when handling a pseudo-version provided by a client. --- internal/strategy/gomod/private_fetcher.go | 38 +++++++++++++++++----- 1 file changed, 30 insertions(+), 8 deletions(-) diff --git a/internal/strategy/gomod/private_fetcher.go b/internal/strategy/gomod/private_fetcher.go index 80f71c8..90bf596 100644 --- a/internal/strategy/gomod/private_fetcher.go +++ b/internal/strategy/gomod/private_fetcher.go @@ -154,9 +154,10 @@ func (p *privateFetcher) modulePathToGitURL(modulePath string) string { return "https://" + modulePath } -func (p *privateFetcher) verifyCommitExists(ctx context.Context, repo *gitclone.Repository, ref string) error { +func (p *privateFetcher) verifyCommitExists(ctx context.Context, repo *gitclone.Repository, version string) error { + ref := VersionToGitRef(version) if !repo.HasCommit(ctx, ref) { - return errors.Errorf("commit %s not found in repository %s", ref, repo.UpstreamURL()) + return errors.Errorf("commit %s not found in repository %s", version, repo.UpstreamURL()) } return nil } @@ -254,7 +255,7 @@ func (p *privateFetcher) getDefaultBranchVersion(ctx context.Context, repo *gitc } func (p *privateFetcher) generateInfo(ctx context.Context, repo *gitclone.Repository, version string) (io.ReadSeekCloser, error) { - commitTime, err := p.getCommitTime(ctx, repo, version) + commitTime, err := p.getCommitTime(ctx, repo, VersionToGitRef(version)) if err != nil { return nil, err } @@ -275,12 +276,12 @@ func (p *privateFetcher) generateInfo(ctx context.Context, repo *gitclone.Reposi func (p *privateFetcher) generateMod(ctx context.Context, repo *gitclone.Repository, modulePath, version string) io.ReadSeekCloser { output, err := gitclone.WithReadLockReturn(repo, func() ([]byte, error) { // #nosec G204 - version and repo.Path() are controlled by this package, not user input - cmd := exec.CommandContext(ctx, "git", "-C", repo.Path(), "show", fmt.Sprintf("%s:go.mod", version)) + cmd := exec.CommandContext(ctx, "git", "-C", repo.Path(), "show", fmt.Sprintf("%s:go.mod", VersionToGitRef(version))) return cmd.CombinedOutput() }) if err != nil { - minimal := fmt.Sprintf("module %s\n\ngo 1.21\n", modulePath) + minimal := fmt.Sprintf("module %s\n", modulePath) return newReadSeekCloser(bytes.NewReader([]byte(minimal))) } @@ -296,12 +297,19 @@ func (p *privateFetcher) generateZip(ctx context.Context, repo *gitclone.Reposit cloneDir := filepath.Join(tmpDir, "repo") - // Local clone from the mirror at the requested version — git hardlinks objects by default. - // #nosec G204 - repo.Path(), version, and cloneDir are controlled by us - cmd := exec.CommandContext(ctx, "git", "clone", "--branch", version, repo.Path(), cloneDir) + // Local clone from the mirror at the requested ref — git hardlinks objects by default. + // We use --no-checkout then checkout the specific ref so that both tagged versions and + // raw commit hashes (from pseudo-versions) work; --branch only accepts branch/tag names. + // #nosec G204 - repo.Path(), gitRef, and cloneDir are controlled by us + gitRef := VersionToGitRef(version) + cmd := exec.CommandContext(ctx, "git", "clone", "--no-checkout", repo.Path(), cloneDir) if output, err := cmd.CombinedOutput(); err != nil { return nil, errors.Wrapf(err, "git clone: %s", string(output)) } + cmd = exec.CommandContext(ctx, "git", "-C", cloneDir, "checkout", gitRef) + if output, err := cmd.CombinedOutput(); err != nil { + return nil, errors.Wrapf(err, "git checkout %s: %s", gitRef, string(output)) + } var buf bytes.Buffer m := module.Version{Path: modulePath, Version: version} @@ -323,3 +331,17 @@ func newReadSeekCloser(r *bytes.Reader) io.ReadSeekCloser { func (r *readSeekCloser) Close() error { return nil } + +// VersionToGitRef converts a Go module version string to a git ref. For pseudo-versions +// (e.g. v0.0.0-20160603174536-ad42235f7e24), it extracts the commit hash suffix. For +// all other versions (tagged releases, pre-releases, +incompatible), it returns the +// version unchanged. +func VersionToGitRef(version string) string { + if module.IsPseudoVersion(version) { + rev, err := module.PseudoVersionRev(version) + if err == nil { + return rev + } + } + return version +} From d6d4cd040df63dad7ca16e83bee6d275b027cc62 Mon Sep 17 00:00:00 2001 From: Benjamin Wester Date: Fri, 13 Mar 2026 03:21:46 +0000 Subject: [PATCH 2/3] appease linter --- internal/strategy/gomod/private_fetcher.go | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/internal/strategy/gomod/private_fetcher.go b/internal/strategy/gomod/private_fetcher.go index 90bf596..f745dba 100644 --- a/internal/strategy/gomod/private_fetcher.go +++ b/internal/strategy/gomod/private_fetcher.go @@ -296,16 +296,17 @@ func (p *privateFetcher) generateZip(ctx context.Context, repo *gitclone.Reposit defer os.RemoveAll(tmpDir) //nolint:errcheck cloneDir := filepath.Join(tmpDir, "repo") + gitRef := VersionToGitRef(version) // Local clone from the mirror at the requested ref — git hardlinks objects by default. // We use --no-checkout then checkout the specific ref so that both tagged versions and // raw commit hashes (from pseudo-versions) work; --branch only accepts branch/tag names. - // #nosec G204 - repo.Path(), gitRef, and cloneDir are controlled by us - gitRef := VersionToGitRef(version) + // #nosec G204 - repo.Path() and cloneDir are controlled by us cmd := exec.CommandContext(ctx, "git", "clone", "--no-checkout", repo.Path(), cloneDir) if output, err := cmd.CombinedOutput(); err != nil { return nil, errors.Wrapf(err, "git clone: %s", string(output)) } + // #nosec G204 - cloneDir and gitRef are controlled by us cmd = exec.CommandContext(ctx, "git", "-C", cloneDir, "checkout", gitRef) if output, err := cmd.CombinedOutput(); err != nil { return nil, errors.Wrapf(err, "git checkout %s: %s", gitRef, string(output)) From 9fb680be5ff896400ebe28120ab0d38f193a8798 Mon Sep 17 00:00:00 2001 From: Benjamin Wester Date: Fri, 13 Mar 2026 15:24:08 +0000 Subject: [PATCH 3/3] add tests --- .../strategy/gomod/private_fetcher_test.go | 55 +++++++++++++++++++ 1 file changed, 55 insertions(+) create mode 100644 internal/strategy/gomod/private_fetcher_test.go diff --git a/internal/strategy/gomod/private_fetcher_test.go b/internal/strategy/gomod/private_fetcher_test.go new file mode 100644 index 0000000..c546882 --- /dev/null +++ b/internal/strategy/gomod/private_fetcher_test.go @@ -0,0 +1,55 @@ +package gomod_test + +import ( + "testing" + + "github.com/alecthomas/assert/v2" + + "github.com/block/cachew/internal/strategy/gomod" +) + +func TestVersionToGitRef(t *testing.T) { + tests := []struct { + name string + version string + want string + }{ + { + name: "TaggedVersion", + version: "v1.2.3", + want: "v1.2.3", + }, + { + name: "PseudoVersion", + version: "v0.0.0-20160603174536-ad42235f7e24", + want: "ad42235f7e24", + }, + { + name: "PseudoVersionWithBase", + version: "v1.2.4-0.20230101120000-abcdef123456", + want: "abcdef123456", + }, + { + name: "PreReleaseVersion", + version: "v1.0.0-rc1", + want: "v1.0.0-rc1", + }, + { + name: "IncompatibleVersion", + version: "v2.0.0+incompatible", + want: "v2.0.0+incompatible", + }, + { + name: "BareRef", + version: "HEAD", + want: "HEAD", + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + ref := gomod.VersionToGitRef(tt.version) + assert.Equal(t, tt.want, ref) + }) + } +}