Skip to content

Commit e1cb26d

Browse files
committed
chore: retrigger checks
1 parent fdafcf2 commit e1cb26d

2 files changed

Lines changed: 65 additions & 22 deletions

File tree

cmd/tools/validate-api-go-version/main.go

Lines changed: 61 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import (
1010
"io"
1111
"os"
1212
"os/exec"
13+
"path/filepath"
1314
"strings"
1415

1516
"golang.org/x/mod/modfile"
@@ -190,32 +191,30 @@ func validateMainModule(
190191
return fmt.Sprintf("%s@%s: failed to resolve module origin: %v", mod.modulePath, version, err)
191192
}
192193

193-
expectedRef := "refs/heads/" + origin.defaultBranch
194-
if origin.ref != expectedRef {
195-
return fmt.Sprintf("%s@%s: pseudo-version resolved to branch %q but must be on the default branch %q; use a tagged release or a pseudo-version from %s",
196-
mod.modulePath, version, origin.ref, expectedRef, origin.defaultBranch)
194+
if !origin.onDefault {
195+
return fmt.Sprintf("%s@%s: commit %s is not on the default branch (%s) of %s",
196+
mod.modulePath, version, origin.hash, origin.defaultBranch, origin.url)
197197
}
198198

199199
_, _ = fmt.Fprintf(out, " - %s@%s is on %s (ok)\n", mod.modulePath, version, origin.defaultBranch)
200200
return ""
201201
}
202202

203203
type moduleOrigin struct {
204-
ref string // e.g. "refs/heads/master"
204+
hash string // full commit hash
205+
url string // VCS repository URL
205206
defaultBranch string // e.g. "master"
207+
onDefault bool // whether the commit is on the default branch
206208
}
207209

208-
// resolveModuleOrigin uses go mod download with GOPROXY=direct to get the
209-
// Origin.Ref and Origin.URL from the VCS repository directly, then uses
210-
// git ls-remote to determine the default branch.
210+
// resolveModuleOrigin uses go mod download with GOPROXY=direct to clone the
211+
// VCS repository, then checks if the pseudo-version commit is on the default branch.
211212
func resolveModuleOrigin(ctx context.Context, modulePath string, version string) (*moduleOrigin, error) {
212213
if !module.IsPseudoVersion(version) {
213214
return nil, fmt.Errorf("version %q is not a pseudo-version", version)
214215
}
215216

216-
// Use a temporary module cache so Go fetches directly from VCS.
217-
// The Origin.Ref field is only populated on a fresh git fetch;
218-
// a warm cache omits it.
217+
// Use a temporary module cache so go mod download clones the VCS repo fresh.
219218
tmpCache, err := os.MkdirTemp("", "validate-api-go-version-*")
220219
if err != nil {
221220
return nil, fmt.Errorf("failed to create temp cache dir: %w", err)
@@ -231,31 +230,76 @@ func resolveModuleOrigin(ctx context.Context, modulePath string, version string)
231230

232231
var payload struct {
233232
Origin struct {
234-
URL string `json:"URL"`
235-
Ref string `json:"Ref"`
233+
URL string `json:"URL"`
234+
Hash string `json:"Hash"`
236235
} `json:"Origin"`
237236
}
238237
if err := json.Unmarshal(out, &payload); err != nil {
239238
return nil, fmt.Errorf("failed to decode go mod download output: %w", err)
240239
}
241-
if payload.Origin.Ref == "" {
242-
return nil, fmt.Errorf("go mod download did not return an origin ref for %s@%s", modulePath, version)
243-
}
244240
if payload.Origin.URL == "" {
245241
return nil, fmt.Errorf("go mod download did not return an origin URL for %s@%s", modulePath, version)
246242
}
243+
if payload.Origin.Hash == "" {
244+
return nil, fmt.Errorf("go mod download did not return an origin hash for %s@%s", modulePath, version)
245+
}
247246

248247
defaultBranch, err := gitDefaultBranch(ctx, payload.Origin.URL)
249248
if err != nil {
250249
return nil, fmt.Errorf("failed to determine default branch for %s: %w", payload.Origin.URL, err)
251250
}
252251

252+
// The VCS cache from go mod download is a bare git repo. Find it and
253+
// use git branch --contains to check if the commit is on the default branch.
254+
vcsDir, err := findVCSCache(tmpCache)
255+
if err != nil {
256+
return nil, fmt.Errorf("failed to find VCS cache: %w", err)
257+
}
258+
259+
onDefault, err := gitCommitOnBranch(ctx, vcsDir, payload.Origin.Hash, defaultBranch)
260+
if err != nil {
261+
return nil, fmt.Errorf("failed to check branch containment: %w", err)
262+
}
263+
253264
return &moduleOrigin{
254-
ref: payload.Origin.Ref,
265+
hash: payload.Origin.Hash,
266+
url: payload.Origin.URL,
255267
defaultBranch: defaultBranch,
268+
onDefault: onDefault,
256269
}, nil
257270
}
258271

272+
// findVCSCache finds the bare git repo directory inside the go module VCS cache.
273+
func findVCSCache(modCache string) (string, error) {
274+
vcsRoot := filepath.Join(modCache, "cache", "vcs")
275+
entries, err := os.ReadDir(vcsRoot)
276+
if err != nil {
277+
return "", fmt.Errorf("failed to read VCS cache dir: %w", err)
278+
}
279+
for _, entry := range entries {
280+
if entry.IsDir() {
281+
return filepath.Join(vcsRoot, entry.Name()), nil
282+
}
283+
}
284+
return "", errors.New("no VCS cache directory found")
285+
}
286+
287+
// gitCommitOnBranch checks if a commit is reachable from a branch in a bare git repo.
288+
func gitCommitOnBranch(ctx context.Context, repoDir string, commitHash string, branch string) (bool, error) {
289+
cmd := exec.CommandContext(ctx, "git", "-C", repoDir, "branch", "--contains", commitHash)
290+
out, err := cmd.Output()
291+
if err != nil {
292+
return false, fmt.Errorf("git branch --contains failed: %w", err)
293+
}
294+
for _, line := range strings.Split(string(out), "\n") {
295+
name := strings.TrimSpace(strings.TrimPrefix(strings.TrimSpace(line), "* "))
296+
if name == branch {
297+
return true, nil
298+
}
299+
}
300+
return false, nil
301+
}
302+
259303
// gitDefaultBranch uses git ls-remote to determine the default branch of a
260304
// remote repository.
261305
func gitDefaultBranch(ctx context.Context, repoURL string) (string, error) {

cmd/tools/validate-api-go-version/main_test.go

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -102,7 +102,7 @@ func TestValidateMainBranchPseudoVersionOnDefault(t *testing.T) {
102102
[]moduleSpec{testAPIModule},
103103
func(context.Context, string, string) (bool, error) { return false, nil },
104104
func(context.Context, string, string) (*moduleOrigin, error) {
105-
return &moduleOrigin{ref: "refs/heads/master", defaultBranch: "master"}, nil
105+
return &moduleOrigin{hash: "dbc016f3811d", url: "https://github.com/temporalio/api-go", defaultBranch: "master", onDefault: true}, nil
106106
},
107107
)
108108
require.NoError(t, err)
@@ -124,13 +124,12 @@ func TestValidateMainBranchPseudoVersionNotOnDefault(t *testing.T) {
124124
[]moduleSpec{testAPIModule},
125125
func(context.Context, string, string) (bool, error) { return false, nil },
126126
func(context.Context, string, string) (*moduleOrigin, error) {
127-
return &moduleOrigin{ref: "refs/heads/serverless", defaultBranch: "master"}, nil
127+
return &moduleOrigin{hash: "dbc016f3811d", url: "https://github.com/temporalio/api-go", defaultBranch: "master", onDefault: false}, nil
128128
},
129129
)
130130
require.Error(t, err)
131131
require.Contains(t, err.Error(), "main branch dependency validation failed")
132-
require.Contains(t, err.Error(), "must be on the default branch")
133-
require.Contains(t, err.Error(), "refs/heads/serverless")
132+
require.Contains(t, err.Error(), "is not on the default branch")
134133
}
135134

136135
func TestValidateMainBranchSDKPseudoVersionOnDefault(t *testing.T) {
@@ -151,7 +150,7 @@ func TestValidateMainBranchSDKPseudoVersionOnDefault(t *testing.T) {
151150
return modulePath == "go.temporal.io/api", nil
152151
},
153152
func(context.Context, string, string) (*moduleOrigin, error) {
154-
return &moduleOrigin{ref: "refs/heads/master", defaultBranch: "master"}, nil
153+
return &moduleOrigin{hash: "abcdef123456", url: "https://github.com/temporalio/sdk-go", defaultBranch: "master", onDefault: true}, nil
155154
},
156155
)
157156
require.NoError(t, err)

0 commit comments

Comments
 (0)