diff --git a/internal/builder/check.go b/internal/builder/check.go index 9d9547ae..f49cc160 100644 --- a/internal/builder/check.go +++ b/internal/builder/check.go @@ -263,9 +263,12 @@ const ( // EnvChangedFile names a file holding every changed path, one per line, in the order given: the whole // list however long, which one environment string cannot carry (novox/hq issue 1247). EnvChangedFile = "MESH_CHECK_CHANGED_FILE" - EnvVerdict = "MESH_CHECK_VERDICT" - EnvBeside = "MESH_CHECK_BESIDE" - EnvModules = "MESH_CHECK_MODULES" + // EnvChangedCut is 1 when MESH_CHECK_CHANGED was cut to directories, so the gate reads the file; unset + // otherwise, and the gate is given the list as before, which a judge older than --changed-file understands. + EnvChangedCut = "MESH_CHECK_CHANGED_CUT" + EnvVerdict = "MESH_CHECK_VERDICT" + EnvBeside = "MESH_CHECK_BESIDE" + EnvModules = "MESH_CHECK_MODULES" // EnvGroup is a delivery group's other heads, as JSON, for the gate (novox/hq ADR 0239); empty for a // pull request checked alone. EnvGroup = "MESH_CHECK_GROUP" @@ -1096,7 +1099,7 @@ func gateLayer(ctx context.Context, spec CheckSpec, tree, root, gate, verdictFil // 2. Every machine composed with the change. if err := running(inToolchain(tree, append(env, EnvVerdict+"="+verdictFile), "sh", "-c", `"$MESH_GATE" merge-gate --facts "$MESH_FACTS" --store "$MESH_GATE_POSTGRES" `+ - `--repository "$MESH_CHECK_REPOSITORY" --tree . --changed-file "$MESH_CHECK_CHANGED_FILE" `+ + `--repository "$MESH_CHECK_REPOSITORY" --tree . `+gateChangedFlag(env)+` `+ `${MESH_CHECK_GROUP:+--group "$MESH_CHECK_GROUP"} --json > "$MESH_CHECK_VERDICT"`)); err != nil { if ctx.Err() != nil { return "error", "the check was ended during the merge gate" @@ -1926,6 +1929,7 @@ func changedEnvironment(root string, paths []string, say func(string)) ([]string return nil, err } joined := strings.Join(paths, ",") + cut := false if len(joined) >= changedEnvLimit { var short []string seen := map[string]bool{} @@ -1941,8 +1945,25 @@ func changedEnvironment(root string, paths []string, say func(string)) ([]string } } joined = strings.Join(short, ",") + cut = true say(fmt.Sprintf("--- %d changed paths are too long for one environment string: %s holds all of them, one per "+ "line, and %s only each path's first two directories", len(paths), EnvChangedFile, EnvChanged)) } - return []string{EnvChanged + "=" + joined, EnvChangedFile + "=" + file}, nil + env := []string{EnvChanged + "=" + joined, EnvChangedFile + "=" + file} + if cut { + env = append(env, EnvChangedCut+"=1") + } + return env, nil +} + +// gateChangedFlag is how the gate is given the changed paths: as the argument it always was while the list was not +// cut, so a judge from before --changed-file still runs; from the file only when it was cut, which that judge +// could never have started anyway. +func gateChangedFlag(env []string) string { + for _, e := range env { + if e == EnvChangedCut+"=1" { + return `--changed-file "$MESH_CHECK_CHANGED_FILE"` + } + } + return `--changed "$MESH_CHECK_CHANGED"` } diff --git a/internal/builder/check_changed_test.go b/internal/builder/check_changed_test.go index e85f7e62..048d0851 100644 --- a/internal/builder/check_changed_test.go +++ b/internal/builder/check_changed_test.go @@ -81,15 +81,36 @@ func TestAShortListOfChangedPathsStaysAsItWasAndIsInTheFileToo(t *testing.T) { / } } -func TestTheGateReadsTheChangedPathsFromTheFile(t *testing.T) { // 1247/AC3 +// theGatesCommand is the command gateLayer hands the toolchain for the merge gate, given the environment of the check. +func theGatesCommand(t *testing.T, env []string) string { + t.Helper() var args []string - gateLayer(t.Context(), CheckSpec{}, t.TempDir(), t.TempDir(), "gate", filepath.Join(t.TempDir(), "verdict.json"), nil, + gateLayer(t.Context(), CheckSpec{}, t.TempDir(), t.TempDir(), "gate", filepath.Join(t.TempDir(), "verdict.json"), env, func(dir string, env []string, command ...string) []string { return command }, func(a []string) error { args = a; return errors.New("stop here") }, &tail{}, "bus", "name", false, "", nil, func(string, string, ...any) {}) - command := strings.Join(args, " ") + return strings.Join(args, " ") +} + +func TestTheGateIsGivenAShortListAsBeforeSoAnOlderJudgeStillRuns(t *testing.T) { // 1247/AC3 + env, err := changedEnvironment(t.TempDir(), []string{"a.go", "b.go"}, func(string) {}) + if err != nil { + t.Fatal(err) + } + command := theGatesCommand(t, env) + if !strings.Contains(command, `--changed "$MESH_CHECK_CHANGED"`) || strings.Contains(command, "--changed-file") { + t.Errorf("a short list is not given to the gate as --changed alone: %s", command) + } +} + +func TestTheGateReadsACutListFromTheFile(t *testing.T) { // 1247/AC3 + env, err := changedEnvironment(t.TempDir(), manyPaths(), func(string) {}) + if err != nil { + t.Fatal(err) + } + command := theGatesCommand(t, env) if !strings.Contains(command, `--changed-file "$MESH_CHECK_CHANGED_FILE"`) || strings.Contains(command, `--changed "`) { - t.Errorf("the gate's command does not read the paths from the file: %s", command) + t.Errorf("a cut list is not given to the gate through the file alone: %s", command) } }