Skip to content

Commit 4acef74

Browse files
committed
fix: address whichtests review feedback
1 parent 38707a6 commit 4acef74

28 files changed

Lines changed: 491 additions & 392 deletions

README.md

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -33,18 +33,22 @@ go run ./ \
3333
--out-matrix "$RUNNER_TEMP/flake-matrix.json"
3434
```
3535

36+
For `pull_request` events, checkout must use the PR head SHA, for example `github.event.pull_request.head.sha`. The default synthetic merge ref is rejected because the checked-out `HEAD` must match `pull_request.head.sha`.
37+
38+
The matrix JSON contains `include` rows with `package`, `run_regex`, and `test_count`. `package` is normally one safe Go package pattern. If the matrix cap is hit, the final overflow row stores a space-separated list of safe package tokens in `package`, leaves `run_regex` empty, and sets `test_count` to `1`; this is the contract consumed by the current `flake-go` workflow.
39+
3640
## File layout
3741

3842
The binary is a single `package main`, split into focused files:
3943

4044
| File | Responsibility |
4145
| --------------- | ------------------------------------------------------------------- |
42-
| `cli.go` | `main`, flag parsing, command orchestration (`run`, `runCommand`). |
46+
| `cli.go` | `main`, flag parsing, command orchestration (`runCommand`). |
4347
| `config.go` | `config` / `commandConfig` types and defaults. |
4448
| `request.go` | `runRequest`, `diffRange`, revision validation. |
4549
| `gitexec.go` | `gitRunner` / `gitFetcher` types and the real `exec.Command` impl. |
4650
| `diff.go` | Reading and parsing `git diff`, change kinds, hunks, line ranges. |
47-
| `snapshot.go` | AST snapshot parsing, `fileSnapshot`, `sharedDecl`, fallbacks. |
51+
| `snapshot.go` | AST snapshot parsing, `fileSnapshot`, and `sharedDecl`. |
4852
| `broadening.go` | Per-kind broadening rules (`broadeningScope`). |
4953
| `selection.go` | Per-change selection logic (`selectChange`, broaden vs narrow). |
5054
| `inventory.go` | `inventoryCache` for package/directory test discovery. |

broadening.go

Lines changed: 11 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -9,24 +9,25 @@ const (
99
)
1010

1111
func broadeningScopeForOldHunk(decls []sharedDecl, candidate lineRange) broadeningScope {
12+
scope := broadeningNone
1213
for _, decl := range decls {
13-
if decl.Range.overlaps(candidate) {
14-
return decl.broadeningScope()
14+
if !decl.Range.overlaps(candidate) {
15+
continue
1516
}
17+
scope = max(scope, decl.broadeningScope())
1618
}
17-
return broadeningNone
19+
return scope
1820
}
1921

2022
func broadeningScopeForNewHunk(decls []sharedDecl, oldSnapshot *fileSnapshot, candidate lineRange) broadeningScope {
23+
scope := broadeningNone
2124
for _, decl := range decls {
2225
if !decl.Range.overlaps(candidate) {
2326
continue
2427
}
25-
if scope := decl.broadeningScopeOnNewSide(oldSnapshot); scope != broadeningNone {
26-
return scope
27-
}
28+
scope = max(scope, decl.broadeningScopeOnNewSide(oldSnapshot))
2829
}
29-
return broadeningNone
30+
return scope
3031
}
3132

3233
func (decl sharedDecl) broadeningScope() broadeningScope {
@@ -43,6 +44,9 @@ func (decl sharedDecl) broadeningScope() broadeningScope {
4344

4445
func (decl sharedDecl) broadeningScopeOnNewSide(oldSnapshot *fileSnapshot) broadeningScope {
4546
switch decl.Kind {
47+
// TODO: Decide whether new imports should narrow to tests that still
48+
// reference package-local declarations. Today any import edit broadens
49+
// the package.
4650
case sharedDeclImport:
4751
return broadeningPackage
4852
case sharedDeclInit, sharedDeclTestMain:

broadening_test.go

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
package main
2+
3+
import (
4+
"testing"
5+
6+
"github.com/stretchr/testify/require"
7+
)
8+
9+
func TestBroadeningScopeForOldHunkChoosesMaxOverlappingScope(t *testing.T) {
10+
t.Parallel()
11+
12+
data := []byte(`package sample
13+
14+
import "testing"
15+
16+
func init() {
17+
register()
18+
}
19+
20+
func TestAlpha(t *testing.T) {}
21+
`)
22+
snapshot, err := parseFileSnapshot(data)
23+
require.NoError(t, err)
24+
candidate := rangeSpan(
25+
singleLineRange(t, string(data), `import "testing"`),
26+
singleLineRange(t, string(data), "register()"),
27+
)
28+
require.Equal(t, broadeningDirectory, broadeningScopeForOldHunk(snapshot.shared, candidate))
29+
}
30+
31+
func TestBroadeningScopeForNewHunkChoosesMaxOverlappingScope(t *testing.T) {
32+
t.Parallel()
33+
34+
data := []byte(`package sample
35+
36+
import "testing"
37+
38+
func TestMain(m *testing.M) {
39+
m.Run()
40+
}
41+
42+
func TestAlpha(t *testing.T) {}
43+
`)
44+
snapshot, err := parseFileSnapshot(data)
45+
require.NoError(t, err)
46+
candidate := rangeSpan(
47+
singleLineRange(t, string(data), `import "testing"`),
48+
singleLineRange(t, string(data), "m.Run()"),
49+
)
50+
require.Equal(t, broadeningDirectory, broadeningScopeForNewHunk(snapshot.shared, nil, candidate))
51+
}

cli.go

Lines changed: 6 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -4,12 +4,11 @@ package main
44

55
import (
66
"context"
7+
"errors"
78
"flag"
89
"fmt"
910
"io"
1011
"os"
11-
12-
"golang.org/x/xerrors"
1312
)
1413

1514
func main() {
@@ -21,11 +20,6 @@ func main() {
2120
flags.StringVar(&cfg.OutMatrix, "out-matrix", cfg.OutMatrix, "path to write workflow matrix JSON")
2221
flags.StringVar(&cfg.OutSummary, "out-summary", cfg.OutSummary, "path to write Markdown summary, or - for stdout")
2322
flags.BoolVar(&cfg.GitHubActions, "github-actions", cfg.GitHubActions, "read diff range and output paths from GitHub Actions environment")
24-
flags.StringVar(&cfg.GitHubEventName, "github-event-name", cfg.GitHubEventName, "override GITHUB_EVENT_NAME")
25-
flags.StringVar(&cfg.GitHubEventPath, "github-event-path", cfg.GitHubEventPath, "override GITHUB_EVENT_PATH")
26-
flags.StringVar(&cfg.GitHubRepository, "github-repository", cfg.GitHubRepository, "override GITHUB_REPOSITORY")
27-
flags.StringVar(&cfg.GitHubOutput, "github-output", cfg.GitHubOutput, "override GITHUB_OUTPUT")
28-
flags.StringVar(&cfg.GitHubStepSummary, "github-step-summary", cfg.GitHubStepSummary, "override GITHUB_STEP_SUMMARY")
2923
if err := flags.Parse(os.Args[1:]); err != nil {
3024
_, _ = fmt.Fprintln(os.Stderr, err)
3125
os.Exit(2)
@@ -36,14 +30,6 @@ func main() {
3630
}
3731
}
3832

39-
func run(ctx context.Context, cfg config, stdout, stderr io.Writer, git gitRunner) error {
40-
req, err := explicitRunRequest(cfg)
41-
if err != nil {
42-
return err
43-
}
44-
return executeRunRequest(ctx, req, stdout, stderr, git, nil)
45-
}
46-
4733
func runCommand(ctx context.Context, cfg commandConfig, stdout, stderr io.Writer, git gitRunner, fetch gitFetcher) error {
4834
var (
4935
req runRequest
@@ -63,15 +49,15 @@ func runCommand(ctx context.Context, cfg commandConfig, stdout, stderr io.Writer
6349
func explicitRunRequest(cfg config) (runRequest, error) {
6450
cfg = cfg.withDefaults()
6551
if cfg.BaseSHA == "" {
66-
return runRequest{}, xerrors.New("--base-sha is required")
52+
return runRequest{}, errors.New("--base-sha is required")
6753
}
6854
if cfg.OutMatrix == "" {
69-
return runRequest{}, xerrors.New("--out-matrix is required")
55+
return runRequest{}, errors.New("--out-matrix is required")
7056
}
71-
if err := validateRevision("--base-sha", cfg.BaseSHA); err != nil {
57+
if err := validateRevisionArg("--base-sha", cfg.BaseSHA); err != nil {
7258
return runRequest{}, err
7359
}
74-
if err := validateRevision("--head-sha", cfg.HeadSHA); err != nil {
60+
if err := validateRevisionArg("--head-sha", cfg.HeadSHA); err != nil {
7561
return runRequest{}, err
7662
}
7763
return runRequest{
@@ -101,7 +87,7 @@ func executeRunRequest(ctx context.Context, req runRequest, stdout, stderr io.Wr
10187
return err
10288
}
10389
summary := renderSummary(changedFiles, result.Summary)
104-
if err := publishPlan(req.Sinks, result.Matrix, summary, stdout, req.OutputSizeLimit); err != nil {
90+
if err := publishPlan(req.Sinks, result.Matrix, summary, stdout); err != nil {
10591
return err
10692
}
10793
_, _ = fmt.Fprintf(stderr, "selected %d package targets from %d changed test files\n", len(result.Matrix.Include), len(changedFiles))

cli_test.go

Lines changed: 18 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -4,12 +4,12 @@ import (
44
"bytes"
55
"context"
66
"encoding/json"
7+
"errors"
78
"os"
89
"path/filepath"
910
"testing"
1011

1112
"github.com/stretchr/testify/require"
12-
"golang.org/x/xerrors"
1313
)
1414

1515
func TestRunValidationErrors(t *testing.T) {
@@ -18,22 +18,22 @@ func TestRunValidationErrors(t *testing.T) {
1818
var stdout bytes.Buffer
1919
var stderr bytes.Buffer
2020
neverGit := func(_ context.Context, _ string, _ ...string) (gitResult, error) {
21-
return gitResult{}, xerrors.New("git should not be called")
21+
return gitResult{}, errors.New("git should not be called")
2222
}
2323

24-
err := run(t.Context(), config{OutMatrix: "matrix.json"}, &stdout, &stderr, neverGit)
24+
err := runCommand(t.Context(), commandConfig{config: config{OutMatrix: "matrix.json"}}, &stdout, &stderr, neverGit, nil)
2525
require.EqualError(t, err, "--base-sha is required")
2626

27-
err = run(t.Context(), config{BaseSHA: "base"}, &stdout, &stderr, neverGit)
27+
err = runCommand(t.Context(), commandConfig{config: config{BaseSHA: "base"}}, &stdout, &stderr, neverGit, nil)
2828
require.EqualError(t, err, "--out-matrix is required")
2929

30-
err = run(t.Context(), config{BaseSHA: "-bad", OutMatrix: "matrix.json"}, &stdout, &stderr, neverGit)
30+
err = runCommand(t.Context(), commandConfig{config: config{BaseSHA: "-bad", OutMatrix: "matrix.json"}}, &stdout, &stderr, neverGit, nil)
3131
require.ErrorContains(t, err, "must not start with '-'")
3232

33-
err = run(t.Context(), config{BaseSHA: "base:bad", OutMatrix: "matrix.json"}, &stdout, &stderr, neverGit)
33+
err = runCommand(t.Context(), commandConfig{config: config{BaseSHA: "base:bad", OutMatrix: "matrix.json"}}, &stdout, &stderr, neverGit, nil)
3434
require.ErrorContains(t, err, "must not contain ':'")
3535

36-
err = run(t.Context(), config{BaseSHA: "base\x00bad", OutMatrix: "matrix.json"}, &stdout, &stderr, neverGit)
36+
err = runCommand(t.Context(), commandConfig{config: config{BaseSHA: "base\x00bad", OutMatrix: "matrix.json"}}, &stdout, &stderr, neverGit, nil)
3737
require.ErrorContains(t, err, "must not contain NUL bytes")
3838
}
3939

@@ -99,7 +99,7 @@ func TestShared(t *testing.T) {
9999
summaryPath := filepath.Join(repoRoot, "summary.md")
100100
var stdout bytes.Buffer
101101
var stderr bytes.Buffer
102-
err := run(t.Context(), config{RepoRoot: repoRoot, BaseSHA: "base", HeadSHA: "head", OutMatrix: matrixPath, OutSummary: summaryPath}, &stdout, &stderr, repo.runner(t))
102+
err := runCommand(t.Context(), commandConfig{config: config{RepoRoot: repoRoot, BaseSHA: "base", HeadSHA: "head", OutMatrix: matrixPath, OutSummary: summaryPath}}, &stdout, &stderr, repo.runner(t), nil)
103103
require.NoError(t, err)
104104
require.Empty(t, stdout.String())
105105
require.Contains(t, stderr.String(), "selected 2 package targets")
@@ -159,7 +159,7 @@ func TestAlpha(t *testing.T) {
159159
matrixPath := filepath.Join(repoRoot, "matrix.json")
160160
var stdout bytes.Buffer
161161
var stderr bytes.Buffer
162-
err := run(t.Context(), config{RepoRoot: repoRoot, BaseSHA: "base", HeadSHA: "head", OutMatrix: matrixPath, OutSummary: "-"}, &stdout, &stderr, repo.runner(t))
162+
err := runCommand(t.Context(), commandConfig{config: config{RepoRoot: repoRoot, BaseSHA: "base", HeadSHA: "head", OutMatrix: matrixPath, OutSummary: "-"}}, &stdout, &stderr, repo.runner(t), nil)
163163
require.NoError(t, err)
164164
require.Contains(t, stdout.String(), "## Go test flake detector selection")
165165
require.Contains(t, stdout.String(), "### `./pkg`")
@@ -231,7 +231,7 @@ func TestMain(m *testing.M) {
231231
summaryPath := filepath.Join(repoRoot, "summary.md")
232232
var stdout bytes.Buffer
233233
var stderr bytes.Buffer
234-
err := run(t.Context(), config{RepoRoot: repoRoot, BaseSHA: "base", HeadSHA: "head", OutMatrix: matrixPath, OutSummary: summaryPath}, &stdout, &stderr, repo.runner(t))
234+
err := runCommand(t.Context(), commandConfig{config: config{RepoRoot: repoRoot, BaseSHA: "base", HeadSHA: "head", OutMatrix: matrixPath, OutSummary: summaryPath}}, &stdout, &stderr, repo.runner(t), nil)
235235
require.NoError(t, err)
236236

237237
var matrix matrixOutput
@@ -289,7 +289,7 @@ func TestRenamed(t *testing.T) {
289289
summaryPath := filepath.Join(repoRoot, "summary.md")
290290
var stdout bytes.Buffer
291291
var stderr bytes.Buffer
292-
err := run(t.Context(), config{RepoRoot: repoRoot, BaseSHA: "base", HeadSHA: "head", OutMatrix: matrixPath, OutSummary: summaryPath}, &stdout, &stderr, repo.runner(t))
292+
err := runCommand(t.Context(), commandConfig{config: config{RepoRoot: repoRoot, BaseSHA: "base", HeadSHA: "head", OutMatrix: matrixPath, OutSummary: summaryPath}}, &stdout, &stderr, repo.runner(t), nil)
293293
require.NoError(t, err)
294294

295295
var matrix matrixOutput
@@ -352,7 +352,7 @@ func TestHead(t *testing.T) {
352352
matrixPath := filepath.Join(repoRoot, "matrix.json")
353353
var stdout bytes.Buffer
354354
var stderr bytes.Buffer
355-
err := run(t.Context(), config{RepoRoot: repoRoot, BaseSHA: "base", HeadSHA: "head", OutMatrix: matrixPath}, &stdout, &stderr, repo.runner(t))
355+
err := runCommand(t.Context(), commandConfig{config: config{RepoRoot: repoRoot, BaseSHA: "base", HeadSHA: "head", OutMatrix: matrixPath}}, &stdout, &stderr, repo.runner(t), nil)
356356
require.NoError(t, err)
357357

358358
var matrix matrixOutput
@@ -408,7 +408,7 @@ func TestHiddenIgnored(t *testing.T) {
408408
summaryPath := filepath.Join(repoRoot, "summary.md")
409409
var stdout bytes.Buffer
410410
var stderr bytes.Buffer
411-
err := run(t.Context(), config{RepoRoot: repoRoot, BaseSHA: "base", HeadSHA: "head", OutMatrix: matrixPath, OutSummary: summaryPath}, &stdout, &stderr, repo.runner(t))
411+
err := runCommand(t.Context(), commandConfig{config: config{RepoRoot: repoRoot, BaseSHA: "base", HeadSHA: "head", OutMatrix: matrixPath, OutSummary: summaryPath}}, &stdout, &stderr, repo.runner(t), nil)
412412
require.NoError(t, err)
413413

414414
var matrix matrixOutput
@@ -477,7 +477,7 @@ func TestPlatform(t *testing.T) {
477477
matrixPath := filepath.Join(repoRoot, "matrix.json")
478478
var stdout bytes.Buffer
479479
var stderr bytes.Buffer
480-
err := run(t.Context(), config{RepoRoot: repoRoot, BaseSHA: "base", HeadSHA: "head", OutMatrix: matrixPath}, &stdout, &stderr, repo.runner(t))
480+
err := runCommand(t.Context(), commandConfig{config: config{RepoRoot: repoRoot, BaseSHA: "base", HeadSHA: "head", OutMatrix: matrixPath}}, &stdout, &stderr, repo.runner(t), nil)
481481
require.NoError(t, err)
482482

483483
var matrix matrixOutput
@@ -531,7 +531,7 @@ func TestAlpha(t *testing.T) {
531531
summaryPath := filepath.Join(repoRoot, "summary.md")
532532
var stdout bytes.Buffer
533533
var stderr bytes.Buffer
534-
err := run(t.Context(), config{RepoRoot: repoRoot, BaseSHA: "base", HeadSHA: "head", OutMatrix: matrixPath, OutSummary: summaryPath}, &stdout, &stderr, repo.runner(t))
534+
err := runCommand(t.Context(), commandConfig{config: config{RepoRoot: repoRoot, BaseSHA: "base", HeadSHA: "head", OutMatrix: matrixPath, OutSummary: summaryPath}}, &stdout, &stderr, repo.runner(t), nil)
535535
require.NoError(t, err)
536536

537537
var matrix matrixOutput
@@ -600,7 +600,7 @@ func init() {
600600
matrixPath := filepath.Join(repoRoot, "matrix.json")
601601
var stdout bytes.Buffer
602602
var stderr bytes.Buffer
603-
err := run(t.Context(), config{RepoRoot: repoRoot, BaseSHA: "base", HeadSHA: "head", OutMatrix: matrixPath}, &stdout, &stderr, repo.runner(t))
603+
err := runCommand(t.Context(), commandConfig{config: config{RepoRoot: repoRoot, BaseSHA: "base", HeadSHA: "head", OutMatrix: matrixPath}}, &stdout, &stderr, repo.runner(t), nil)
604604
require.NoError(t, err)
605605

606606
var matrix matrixOutput
@@ -660,7 +660,7 @@ func TestMoved(t *testing.T) {
660660
matrixPath := filepath.Join(repoRoot, "matrix.json")
661661
var stdout bytes.Buffer
662662
var stderr bytes.Buffer
663-
err := run(t.Context(), config{RepoRoot: repoRoot, BaseSHA: "base", HeadSHA: "head", OutMatrix: matrixPath}, &stdout, &stderr, repo.runner(t))
663+
err := runCommand(t.Context(), commandConfig{config: config{RepoRoot: repoRoot, BaseSHA: "base", HeadSHA: "head", OutMatrix: matrixPath}}, &stdout, &stderr, repo.runner(t), nil)
664664
require.NoError(t, err)
665665

666666
var matrix matrixOutput
@@ -731,7 +731,7 @@ func TestNewStable(t *testing.T) {
731731
matrixPath := filepath.Join(repoRoot, "matrix.json")
732732
var stdout bytes.Buffer
733733
var stderr bytes.Buffer
734-
err := run(t.Context(), config{RepoRoot: repoRoot, BaseSHA: "base", HeadSHA: "head", OutMatrix: matrixPath}, &stdout, &stderr, repo.runner(t))
734+
err := runCommand(t.Context(), commandConfig{config: config{RepoRoot: repoRoot, BaseSHA: "base", HeadSHA: "head", OutMatrix: matrixPath}}, &stdout, &stderr, repo.runner(t), nil)
735735
require.NoError(t, err)
736736

737737
var matrix matrixOutput

config.go

Lines changed: 6 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -5,11 +5,11 @@ import (
55
)
66

77
const (
8-
defaultRepoRoot = "."
9-
defaultHeadSHA = "HEAD"
10-
defaultOutSummary = "-"
11-
defaultTargetCount = "10"
12-
runOnceTargetCount = "1"
8+
defaultRepoRoot = "."
9+
defaultHeadSHA = "HEAD"
10+
defaultOutSummary = "-"
11+
defaultTestCount = "10"
12+
runOnceTestCount = "1"
1313

1414
// Package-wide and matrix-wide caps keep the detector cheap by
1515
// running broad fallback targets once instead of repeatedly.
@@ -44,13 +44,7 @@ func (cfg config) withDefaults() config {
4444
type commandConfig struct {
4545
config
4646

47-
GitHubActions bool
48-
GitHubEventName string
49-
GitHubEventPath string
50-
GitHubRepository string
51-
GitHubOutput string
52-
GitHubStepSummary string
53-
Env map[string]string
47+
GitHubActions bool
5448
}
5549

5650
func defaultCommandConfig() commandConfig {

0 commit comments

Comments
 (0)