From b1c4de343e46bb932a746fcc392e103437a6f699 Mon Sep 17 00:00:00 2001 From: Pieter Noordhuis Date: Thu, 27 Nov 2025 12:44:31 +0100 Subject: [PATCH 01/21] Run tool to determine which test to run --- .github/workflows/push.yml | 37 ++++++++++ Makefile | 9 ++- tools/testmask/git.go | 33 +++++++++ tools/testmask/git_test.go | 72 ++++++++++++++++++ tools/testmask/main.go | 80 ++++++++++++++++++++ tools/testmask/main_test.go | 137 +++++++++++++++++++++++++++++++++++ tools/testmask/rules.go | 79 ++++++++++++++++++++ tools/testmask/rules_test.go | 105 +++++++++++++++++++++++++++ 8 files changed, 548 insertions(+), 4 deletions(-) create mode 100644 tools/testmask/git.go create mode 100644 tools/testmask/git_test.go create mode 100644 tools/testmask/main.go create mode 100644 tools/testmask/main_test.go create mode 100644 tools/testmask/rules.go create mode 100644 tools/testmask/rules_test.go diff --git a/.github/workflows/push.yml b/.github/workflows/push.yml index b656432fa5..2145ab79cd 100644 --- a/.github/workflows/push.yml +++ b/.github/workflows/push.yml @@ -31,8 +31,45 @@ jobs: GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} run: gh cache delete --all --repo databricks/cli || true + testmask: + if: ${{ github.event_name == 'pull_request' }} + runs-on: ubuntu-latest + outputs: + packages: ${{ steps.mask.outputs.packages }} + acceptance_prefix: ${{ steps.mask.outputs.acceptance_prefix }} + steps: + - name: Checkout repository + uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 + with: + fetch-depth: 0 + + - name: Setup Go + uses: actions/setup-go@44694675825211faa026b3c33043df3e48a5fa00 # v6.0.0 + with: + go-version-file: tools/go.mod + + - name: Run testmask + id: mask + working-directory: tools/testmask + run: | + go run . > output.txt + + - name: Parse testmask output + id: parse + working-directory: tools/testmask + run: | + packages=$(head -n 1 output.txt) + acceptance_prefix=$(tail -n 1 output.txt) + rm -f output.txt + + echo "packages: \"$packages\"" + echo "acceptance_prefix: \"$acceptance_prefix\"" + echo "packages=$packages" >> $GITHUB_OUTPUT + echo "acceptance_prefix=$acceptance_prefix" >> $GITHUB_OUTPUT + tests: needs: cleanups + runs-on: ${{ matrix.os }} strategy: diff --git a/Makefile b/Makefile index bb976362a2..a8038288ac 100644 --- a/Makefile +++ b/Makefile @@ -1,7 +1,8 @@ default: checks fmt lint # gotestsum: when go test args are used with --rerun-fails the list of packages to test must be specified by the --packages flag -PACKAGES=--packages "./acceptance/... ./libs/... ./internal/... ./cmd/... ./bundle/... ./experimental/aitools/... ./experimental/ssh/... ." +PACKAGES = ./acceptance/... ./libs/... ./internal/... ./cmd/... ./bundle/... ./experimental/aitools/... ./experimental/ssh/... . +PACKAGES_ARG = --packages "${PACKAGES}" GO_TOOL ?= go tool -modfile=tools/go.mod GOTESTSUM_FORMAT ?= pkgname-and-test-fails @@ -56,10 +57,10 @@ links: checks: tidy ws links test: - ${GOTESTSUM_CMD} ${PACKAGES} -- -timeout=${LOCAL_TIMEOUT} -short + ${GOTESTSUM_CMD} ${PACKAGES_ARG} -- -timeout=${LOCAL_TIMEOUT} -short test-slow: - ${GOTESTSUM_CMD} ${PACKAGES} -- -timeout=${LOCAL_TIMEOUT} + ${GOTESTSUM_CMD} ${PACKAGES_ARG} -- -timeout=${LOCAL_TIMEOUT} # Updates acceptance test output (local tests) test-update: @@ -82,7 +83,7 @@ slowest: cover: rm -fr ./acceptance/build/cover/ - VERBOSE_TEST=1 CLI_GOCOVERDIR=build/cover ${GOTESTSUM_CMD} ${PACKAGES} -- -coverprofile=coverage.txt -timeout=${LOCAL_TIMEOUT} + VERBOSE_TEST=1 CLI_GOCOVERDIR=build/cover ${GOTESTSUM_CMD} ${PACKAGES_ARG} -- -coverprofile=coverage.txt -timeout=${LOCAL_TIMEOUT} rm -fr ./acceptance/build/cover-merged/ mkdir -p acceptance/build/cover-merged/ go tool covdata merge -i $$(printf '%s,' acceptance/build/cover/* | sed 's/,$$//') -o acceptance/build/cover-merged/ diff --git a/tools/testmask/git.go b/tools/testmask/git.go new file mode 100644 index 0000000000..b3cf950850 --- /dev/null +++ b/tools/testmask/git.go @@ -0,0 +1,33 @@ +package main + +import ( + "bufio" + "bytes" + "fmt" + "os/exec" + "strings" +) + +// GetChangedFiles returns the list of files changed between two git refs. +func GetChangedFiles(headRef, baseRef string) ([]string, error) { + cmd := exec.Command("git", "diff", "--name-only", baseRef, headRef) + output, err := cmd.Output() + if err != nil { + return nil, fmt.Errorf("failed to get diff between %s and %s: %w", baseRef, headRef, err) + } + + return parseLines(output), nil +} + +// parseLines parses command output into a slice of non-empty lines. +func parseLines(output []byte) []string { + var lines []string + scanner := bufio.NewScanner(bytes.NewReader(output)) + for scanner.Scan() { + line := strings.TrimSpace(scanner.Text()) + if line != "" { + lines = append(lines, line) + } + } + return lines +} diff --git a/tools/testmask/git_test.go b/tools/testmask/git_test.go new file mode 100644 index 0000000000..ca8cbd6ce8 --- /dev/null +++ b/tools/testmask/git_test.go @@ -0,0 +1,72 @@ +package main + +import ( + "testing" +) + +func TestParseLines(t *testing.T) { + tests := []struct { + name string + input string + expected []string + }{ + { + name: "empty input", + input: "", + expected: []string{}, + }, + { + name: "multiple lines", + input: "file1.go\nfile2.go\nfile3.go\n", + expected: []string{"file1.go", "file2.go", "file3.go"}, + }, + { + name: "whitespace trimmed and empty lines ignored", + input: " file1.go \n\nfile2.go\n\t\n", + expected: []string{"file1.go", "file2.go"}, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + result := parseLines([]byte(tt.input)) + if len(result) != len(tt.expected) { + t.Errorf("expected %d lines, got %d: %v", len(tt.expected), len(result), result) + return + } + for i, line := range result { + if line != tt.expected[i] { + t.Errorf("line[%d]: expected %q, got %q", i, tt.expected[i], line) + } + } + }) + } +} + +func TestGetChangedFiles(t *testing.T) { + // Test with HEAD to HEAD - should return empty list + result, err := GetChangedFiles("HEAD", "HEAD") + if err != nil { + t.Skipf("unable to run git: %v", err) + return + } + if len(result) > 0 { + t.Errorf("expected empty list, got %v", result) + } + + // Test with HEAD to HEAD~2 - should produce non-empty result if there are commits + result, err = GetChangedFiles("HEAD", "HEAD~2") + if err != nil { + t.Errorf("unable to run git: %v", err) + return + } + if len(result) == 0 { + t.Errorf("expected non-empty list, got %v", result) + } + + // Test with invalid refs - should error + _, err = GetChangedFiles("invalid-ref-12345", "invalid-ref-67890") + if err == nil { + t.Error("expected error for invalid refs") + } +} diff --git a/tools/testmask/main.go b/tools/testmask/main.go new file mode 100644 index 0000000000..c5d4af5224 --- /dev/null +++ b/tools/testmask/main.go @@ -0,0 +1,80 @@ +package main + +import ( + "errors" + "fmt" + "os" + "strings" +) + +// errSkip indicates that files matching this rule should be skipped. +var errSkip = errors.New("skip") + +// RuleFunc is a function that processes a list of changed files. +// Returns: +// - packages: list of packages to unit test (empty means test all) +// - acceptancePrefixes: list of acceptance test prefixes (empty means test all) +// - error: errSkip to skip these files, or nil/other error +type RuleFunc func(files []string) (packages []string, acceptancePrefixes []string, err error) + +func main() { + baseRef := os.Getenv("GITHUB_BASE_REF") + if baseRef == "" { + baseRef = "HEAD" + } + + headRef := os.Getenv("GITHUB_HEAD_REF") + if headRef == "" { + headRef = "HEAD" + } + + // Accept CLI arguments for testing + if len(os.Args) == 3 { + headRef = os.Args[1] + baseRef = os.Args[2] + } + + changedFiles, err := GetChangedFiles(headRef, baseRef) + if err != nil { + fmt.Fprintf(os.Stderr, "Error getting changed files: %v\n", err) + os.Exit(1) + } + + packages, acceptancePrefixes := applyRules(changedFiles) + + // Output packages (space-separated, or empty for all) + if len(packages) == 0 { + fmt.Println("") + } else { + fmt.Println(strings.Join(packages, " ")) + } + + // Output acceptance test prefixes (space-separated, or empty for all) + if len(acceptancePrefixes) == 0 { + fmt.Println("") + } else { + fmt.Println(strings.Join(acceptancePrefixes, " ")) + } +} + +// applyRules applies rules sequentially to all files until one returns a real error or real outputs. +func applyRules(changedFiles []string) ([]string, []string) { + rules := AllRules() + + for _, rule := range rules { + packages, prefixes, err := rule(changedFiles) + if err != nil && err != errSkip { + // Real error - exit + fmt.Fprintf(os.Stderr, "Error applying rule: %v\n", err) + os.Exit(1) + } + if err == errSkip { + // Skip this rule, continue to next rule + continue + } + return packages, prefixes + } + + // No rule matched - test everything (empty means test all) + return []string{}, []string{} +} diff --git a/tools/testmask/main_test.go b/tools/testmask/main_test.go new file mode 100644 index 0000000000..b661877ba0 --- /dev/null +++ b/tools/testmask/main_test.go @@ -0,0 +1,137 @@ +package main + +import ( + "os" + "testing" +) + +func TestApplyRules(t *testing.T) { + tests := []struct { + name string + files []string + expectedPackages []string + expectedAcceptance []string + }{ + { + name: "bundle files", + files: []string{"bundle/config.go", "bundle/deploy/deploy.go"}, + expectedPackages: []string{"./bundle", "./bundle/deploy"}, + expectedAcceptance: []string{"bundle"}, + }, + { + name: "skip testdata", + files: []string{"bundle/config.go", "bundle/testdata/test.json"}, + expectedPackages: []string{"./bundle"}, + expectedAcceptance: []string{"bundle"}, + }, + { + name: "skip acceptance tests", + files: []string{"bundle/config.go", "acceptance/bundle/test/script"}, + expectedPackages: []string{"./bundle"}, + expectedAcceptance: []string{"bundle"}, + }, + { + name: "skip experimental", + files: []string{"bundle/config.go", "experimental/apps-mcp/lib/server.go"}, + expectedPackages: []string{"./bundle"}, + expectedAcceptance: []string{"bundle"}, + }, + { + name: "all experimental skips everything", + files: []string{"experimental/apps-mcp/lib/server.go", "experimental/ssh/cmd/connect.go"}, + expectedPackages: []string{}, + expectedAcceptance: []string{}, + }, + { + name: "root level file", + files: []string{"main.go"}, + expectedPackages: []string{"."}, + expectedAcceptance: []string{"bundle"}, + }, + { + name: "non-go files", + files: []string{"README.md", "Makefile"}, + expectedPackages: []string{}, + expectedAcceptance: []string{}, + }, + { + name: "unmatched file defaults to everything", + files: []string{"unknown/path/file.go"}, + expectedPackages: []string{}, + expectedAcceptance: []string{}, + }, + { + name: "mixed matched and unmatched defaults to everything", + files: []string{"bundle/config.go", "unknown/path/file.go"}, + expectedPackages: []string{}, + expectedAcceptance: []string{}, + }, + { + name: "cmd bundle", + files: []string{"cmd/bundle/deploy.go"}, + expectedPackages: []string{"./cmd/bundle"}, + expectedAcceptance: []string{"bundle"}, + }, + { + name: "cmd workspace", + files: []string{"cmd/workspace/list.go"}, + expectedPackages: []string{"./cmd/workspace"}, + expectedAcceptance: []string{"workspace"}, + }, + { + name: "libs auth", + files: []string{"libs/auth/auth.go"}, + expectedPackages: []string{"./libs/auth"}, + expectedAcceptance: []string{"auth"}, + }, + { + name: "libs template", + files: []string{"libs/template/renderer.go"}, + expectedPackages: []string{"./libs/template"}, + expectedAcceptance: []string{"bundle/templates"}, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + packages, acceptance := applyRules(tt.files) + if len(packages) != len(tt.expectedPackages) { + t.Errorf("expected %d packages, got %d: %v", len(tt.expectedPackages), len(packages), packages) + return + } + for i, pkg := range packages { + if pkg != tt.expectedPackages[i] { + t.Errorf("package[%d]: expected %q, got %q", i, tt.expectedPackages[i], pkg) + } + } + if len(acceptance) != len(tt.expectedAcceptance) { + t.Errorf("expected %d acceptance prefixes, got %d: %v", len(tt.expectedAcceptance), len(acceptance), acceptance) + return + } + for i, prefix := range acceptance { + if prefix != tt.expectedAcceptance[i] { + t.Errorf("acceptance[%d]: expected %q, got %q", i, tt.expectedAcceptance[i], prefix) + } + } + }) + } +} + +func TestMainWithEnvVar(t *testing.T) { + // Test that GITHUB_BASE_REF is respected + originalBaseRef := os.Getenv("GITHUB_BASE_REF") + defer func() { + if originalBaseRef == "" { + os.Unsetenv("GITHUB_BASE_REF") + } else { + os.Setenv("GITHUB_BASE_REF", originalBaseRef) + } + }() + + os.Setenv("GITHUB_BASE_REF", "main") + // We can't easily test the full execution without git, but we can verify the env var is read + baseRef := os.Getenv("GITHUB_BASE_REF") + if baseRef != "main" { + t.Errorf("expected GITHUB_BASE_REF to be 'main', got %q", baseRef) + } +} diff --git a/tools/testmask/rules.go b/tools/testmask/rules.go new file mode 100644 index 0000000000..4bf6ba5002 --- /dev/null +++ b/tools/testmask/rules.go @@ -0,0 +1,79 @@ +package main + +import ( + "path/filepath" + "regexp" + "strings" +) + +// AllRules returns all rules in order of application. +func AllRules() []RuleFunc { + return []RuleFunc{ + handleExperimental, + } +} + +// longestCommonDirectoryPrefix returns the longest common directory prefix of a list of files. +// It always returns a directory path, never a file path. +func longestCommonDirectoryPrefix(files []string) string { + if len(files) == 0 { + return "" + } + if len(files) == 1 { + dir := filepath.Dir(files[0]) + if dir == "." { + return "" + } + return dir + "/" + } + prefix := files[0] + for _, file := range files[1:] { + prefix = longestCommonPrefix(prefix, file) + if prefix == "" { + return "" + } + } + // Ensure we return a directory path (remove filename if present) + if lastSlash := strings.LastIndex(prefix, "/"); lastSlash >= 0 { + return prefix[:lastSlash+1] + } + return "" +} + +// longestCommonPrefix returns the longest common prefix of two strings. +// For file paths, it stops at directory boundaries (after a "/"). +func longestCommonPrefix(a, b string) string { + minLen := len(a) + if len(b) < minLen { + minLen = len(b) + } + lastSlash := -1 + for i := range minLen { + if a[i] != b[i] { + if lastSlash >= 0 { + return a[:lastSlash+1] + } + return "" + } + if a[i] == '/' { + lastSlash = i + } + } + if lastSlash >= 0 { + return a[:lastSlash+1] + } + return a[:minLen] +} + +// handleExperimental deals with changes under the experimental/ directory. +func handleExperimental(files []string) ([]string, []string, error) { + prefix := longestCommonDirectoryPrefix(files) + + // Match this prefix to experimental and optionally a package nested under it. + pkg := regexp.MustCompile(`^experimental(/[\w-_]+)?`).FindString(prefix) + if pkg == "" { + return nil, nil, errSkip + } + + return []string{pkg + "/..."}, []string{}, nil +} diff --git a/tools/testmask/rules_test.go b/tools/testmask/rules_test.go new file mode 100644 index 0000000000..af9b89160e --- /dev/null +++ b/tools/testmask/rules_test.go @@ -0,0 +1,105 @@ +package main + +import ( + "testing" + + "github.com/stretchr/testify/assert" +) + +func TestLongestCommonDirectoryPrefix(t *testing.T) { + tests := []struct { + name string + files []string + want string + }{ + { + name: "empty_list", + files: []string{}, + want: "", + }, + { + name: "single_file", + files: []string{"foo/bar/baz.txt"}, + want: "foo/bar/", + }, + { + name: "all_same_dir", + files: []string{"foo/bar/a.go", "foo/bar/b.go"}, + want: "foo/bar/", + }, + { + name: "different_dirs", + files: []string{"foo/bar/a.go", "foo/baz/a.go"}, + want: "foo/", + }, + { + name: "top_level", + files: []string{"top/a.go", "top/b.go"}, + want: "top/", + }, + { + name: "absolute_paths_same_dir", + files: []string{"/a/b/c.go", "/a/b/d.go"}, + want: "/a/b/", + }, + { + name: "absolute_paths_diff_dirs", + files: []string{"/a/b/c.go", "/a/c/b.go"}, + want: "/a/", + }, + { + name: "no_common_prefix", + files: []string{"foo/a.go", "bar/b.go"}, + want: "", + }, + { + name: "partial_overlap", + files: []string{"foo/bar/baz/a.go", "foo/bar/qux/b.go"}, + want: "foo/bar/", + }, + { + name: "single_character_common", + files: []string{"a/b.go", "a/c.go"}, + want: "a/", + }, + { + name: "root_level_file", + files: []string{"file.go"}, + want: "", + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got := longestCommonDirectoryPrefix(tt.files) + assert.Equal(t, tt.want, got) + }) + } +} + +func TestHandleExperimental(t *testing.T) { + // Changes only in experimental/ssh/ should match. + packages, acceptance, err := handleExperimental([]string{ + "experimental/ssh/main.go", + "experimental/ssh/lib/server.go", + }) + assert.NoError(t, err) + assert.Equal(t, []string{"experimental/ssh/..."}, packages) + assert.Empty(t, acceptance) + + // Changes in experimental/ plus go.mod should not match. + _, _, err = handleExperimental([]string{ + "experimental/ssh/main.go", + "go.mod", + }) + assert.ErrorIs(t, err, errSkip) + + // Changes spanning multiple experimental subdirs should test all of experimental. + packages, acceptance, err = handleExperimental([]string{ + "experimental/ssh/main.go", + "experimental/aitools/server.go", + }) + assert.NoError(t, err) + assert.Equal(t, []string{"experimental/..."}, packages) + assert.Empty(t, acceptance) +} From 352c6187b48317a99f9508e79c3a182321caa94d Mon Sep 17 00:00:00 2001 From: Pieter Noordhuis Date: Thu, 27 Nov 2025 12:45:27 +0100 Subject: [PATCH 02/21] . --- .github/workflows/push.yml | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/.github/workflows/push.yml b/.github/workflows/push.yml index 2145ab79cd..a8a35c43f9 100644 --- a/.github/workflows/push.yml +++ b/.github/workflows/push.yml @@ -35,8 +35,8 @@ jobs: if: ${{ github.event_name == 'pull_request' }} runs-on: ubuntu-latest outputs: - packages: ${{ steps.mask.outputs.packages }} - acceptance_prefix: ${{ steps.mask.outputs.acceptance_prefix }} + packages: ${{ steps.parse.outputs.packages }} + acceptance_prefix: ${{ steps.parse.outputs.acceptance_prefix }} steps: - name: Checkout repository uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 @@ -69,7 +69,6 @@ jobs: tests: needs: cleanups - runs-on: ${{ matrix.os }} strategy: From c2af5e15c93c0dceccb8c6a6272760930b516b9c Mon Sep 17 00:00:00 2001 From: Pieter Noordhuis Date: Thu, 27 Nov 2025 12:45:48 +0100 Subject: [PATCH 03/21] Revert Makefile changes --- Makefile | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/Makefile b/Makefile index a8038288ac..bb976362a2 100644 --- a/Makefile +++ b/Makefile @@ -1,8 +1,7 @@ default: checks fmt lint # gotestsum: when go test args are used with --rerun-fails the list of packages to test must be specified by the --packages flag -PACKAGES = ./acceptance/... ./libs/... ./internal/... ./cmd/... ./bundle/... ./experimental/aitools/... ./experimental/ssh/... . -PACKAGES_ARG = --packages "${PACKAGES}" +PACKAGES=--packages "./acceptance/... ./libs/... ./internal/... ./cmd/... ./bundle/... ./experimental/aitools/... ./experimental/ssh/... ." GO_TOOL ?= go tool -modfile=tools/go.mod GOTESTSUM_FORMAT ?= pkgname-and-test-fails @@ -57,10 +56,10 @@ links: checks: tidy ws links test: - ${GOTESTSUM_CMD} ${PACKAGES_ARG} -- -timeout=${LOCAL_TIMEOUT} -short + ${GOTESTSUM_CMD} ${PACKAGES} -- -timeout=${LOCAL_TIMEOUT} -short test-slow: - ${GOTESTSUM_CMD} ${PACKAGES_ARG} -- -timeout=${LOCAL_TIMEOUT} + ${GOTESTSUM_CMD} ${PACKAGES} -- -timeout=${LOCAL_TIMEOUT} # Updates acceptance test output (local tests) test-update: @@ -83,7 +82,7 @@ slowest: cover: rm -fr ./acceptance/build/cover/ - VERBOSE_TEST=1 CLI_GOCOVERDIR=build/cover ${GOTESTSUM_CMD} ${PACKAGES_ARG} -- -coverprofile=coverage.txt -timeout=${LOCAL_TIMEOUT} + VERBOSE_TEST=1 CLI_GOCOVERDIR=build/cover ${GOTESTSUM_CMD} ${PACKAGES} -- -coverprofile=coverage.txt -timeout=${LOCAL_TIMEOUT} rm -fr ./acceptance/build/cover-merged/ mkdir -p acceptance/build/cover-merged/ go tool covdata merge -i $$(printf '%s,' acceptance/build/cover/* | sed 's/,$$//') -o acceptance/build/cover-merged/ From 647b7f417b1a036e4468c0dcdbc975b6acbe1439 Mon Sep 17 00:00:00 2001 From: Pieter Noordhuis Date: Thu, 27 Nov 2025 12:50:48 +0100 Subject: [PATCH 04/21] Format --- tools/testmask/main.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/testmask/main.go b/tools/testmask/main.go index c5d4af5224..0704893ab7 100644 --- a/tools/testmask/main.go +++ b/tools/testmask/main.go @@ -15,7 +15,7 @@ var errSkip = errors.New("skip") // - packages: list of packages to unit test (empty means test all) // - acceptancePrefixes: list of acceptance test prefixes (empty means test all) // - error: errSkip to skip these files, or nil/other error -type RuleFunc func(files []string) (packages []string, acceptancePrefixes []string, err error) +type RuleFunc func(files []string) (packages, acceptancePrefixes []string, err error) func main() { baseRef := os.Getenv("GITHUB_BASE_REF") From 194b16503fb3d5236a06e3f7a1db166a2c8e42a9 Mon Sep 17 00:00:00 2001 From: Pieter Noordhuis Date: Thu, 27 Nov 2025 12:51:57 +0100 Subject: [PATCH 05/21] Interpolate SHAs --- .github/workflows/push.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/push.yml b/.github/workflows/push.yml index a8a35c43f9..d51ef6b492 100644 --- a/.github/workflows/push.yml +++ b/.github/workflows/push.yml @@ -52,7 +52,7 @@ jobs: id: mask working-directory: tools/testmask run: | - go run . > output.txt + go run . ${{ github.event.pull_request.head.sha }} ${{ github.event.pull_request.base.sha }} > output.txt - name: Parse testmask output id: parse From a289e7fe5db3847a79f60b2637d69c28c1fffef6 Mon Sep 17 00:00:00 2001 From: Pieter Noordhuis Date: Thu, 27 Nov 2025 15:32:50 +0100 Subject: [PATCH 06/21] Make PACKAGES usable var in Makefile --- Makefile | 14 +-- acceptance/acceptance_test.go | 2 +- tools/testmask/main_test.go | 162 +++++++++------------------------- 3 files changed, 54 insertions(+), 124 deletions(-) diff --git a/Makefile b/Makefile index bb976362a2..2dc2801008 100644 --- a/Makefile +++ b/Makefile @@ -1,7 +1,10 @@ default: checks fmt lint -# gotestsum: when go test args are used with --rerun-fails the list of packages to test must be specified by the --packages flag -PACKAGES=--packages "./acceptance/... ./libs/... ./internal/... ./cmd/... ./bundle/... ./experimental/aitools/... ./experimental/ssh/... ." +# Default packages to test (all) +TEST_PACKAGES = . ./acceptance/internal ./libs/... ./internal/... ./cmd/... ./bundle/... ./experimental/... + +# Default acceptance test filter (all) +ACCEPTANCE_TEST_FILTER = TestAccept GO_TOOL ?= go tool -modfile=tools/go.mod GOTESTSUM_FORMAT ?= pkgname-and-test-fails @@ -56,10 +59,11 @@ links: checks: tidy ws links test: - ${GOTESTSUM_CMD} ${PACKAGES} -- -timeout=${LOCAL_TIMEOUT} -short + ${GOTESTSUM_CMD} --packages "${TEST_PACKAGES}" -- -timeout=${LOCAL_TIMEOUT} ${SHORT_FLAG} + ${GOTESTSUM_CMD} --packages ./acceptance/... -- -timeout=${LOCAL_TIMEOUT} ${SHORT_FLAG} -run ${ACCEPTANCE_TEST_FILTER} test-slow: - ${GOTESTSUM_CMD} ${PACKAGES} -- -timeout=${LOCAL_TIMEOUT} + make test SHORT_FLAG="-short" # Updates acceptance test output (local tests) test-update: @@ -82,7 +86,7 @@ slowest: cover: rm -fr ./acceptance/build/cover/ - VERBOSE_TEST=1 CLI_GOCOVERDIR=build/cover ${GOTESTSUM_CMD} ${PACKAGES} -- -coverprofile=coverage.txt -timeout=${LOCAL_TIMEOUT} + VERBOSE_TEST=1 CLI_GOCOVERDIR=build/cover ${GOTESTSUM_CMD} --packages ${TEST_PACKAGES} -- -coverprofile=coverage.txt -timeout=${LOCAL_TIMEOUT} rm -fr ./acceptance/build/cover-merged/ mkdir -p acceptance/build/cover-merged/ go tool covdata merge -i $$(printf '%s,' acceptance/build/cover/* | sed 's/,$$//') -o acceptance/build/cover-merged/ diff --git a/acceptance/acceptance_test.go b/acceptance/acceptance_test.go index 5949e2ebc5..041e2f0bd3 100644 --- a/acceptance/acceptance_test.go +++ b/acceptance/acceptance_test.go @@ -901,7 +901,7 @@ func BuildCLI(t *testing.T, buildDir, coverDir, osName, arch string) string { } args := []string{ - "go", "build", "-o", execPath, + "go", "build", "-o", execPath, "-buildvcs=false", } if coverDir != "" { diff --git a/tools/testmask/main_test.go b/tools/testmask/main_test.go index b661877ba0..38b42ed188 100644 --- a/tools/testmask/main_test.go +++ b/tools/testmask/main_test.go @@ -1,137 +1,63 @@ package main import ( - "os" "testing" + + "github.com/stretchr/testify/assert" ) func TestApplyRules(t *testing.T) { tests := []struct { - name string - files []string - expectedPackages []string - expectedAcceptance []string + name string + files []string + packages []string + acceptance []string }{ { - name: "bundle files", - files: []string{"bundle/config.go", "bundle/deploy/deploy.go"}, - expectedPackages: []string{"./bundle", "./bundle/deploy"}, - expectedAcceptance: []string{"bundle"}, - }, - { - name: "skip testdata", - files: []string{"bundle/config.go", "bundle/testdata/test.json"}, - expectedPackages: []string{"./bundle"}, - expectedAcceptance: []string{"bundle"}, - }, - { - name: "skip acceptance tests", - files: []string{"bundle/config.go", "acceptance/bundle/test/script"}, - expectedPackages: []string{"./bundle"}, - expectedAcceptance: []string{"bundle"}, - }, - { - name: "skip experimental", - files: []string{"bundle/config.go", "experimental/apps-mcp/lib/server.go"}, - expectedPackages: []string{"./bundle"}, - expectedAcceptance: []string{"bundle"}, - }, - { - name: "all experimental skips everything", - files: []string{"experimental/apps-mcp/lib/server.go", "experimental/ssh/cmd/connect.go"}, - expectedPackages: []string{}, - expectedAcceptance: []string{}, - }, - { - name: "root level file", - files: []string{"main.go"}, - expectedPackages: []string{"."}, - expectedAcceptance: []string{"bundle"}, - }, - { - name: "non-go files", - files: []string{"README.md", "Makefile"}, - expectedPackages: []string{}, - expectedAcceptance: []string{}, - }, - { - name: "unmatched file defaults to everything", - files: []string{"unknown/path/file.go"}, - expectedPackages: []string{}, - expectedAcceptance: []string{}, - }, - { - name: "mixed matched and unmatched defaults to everything", - files: []string{"bundle/config.go", "unknown/path/file.go"}, - expectedPackages: []string{}, - expectedAcceptance: []string{}, - }, - { - name: "cmd bundle", - files: []string{"cmd/bundle/deploy.go"}, - expectedPackages: []string{"./cmd/bundle"}, - expectedAcceptance: []string{"bundle"}, - }, - { - name: "cmd workspace", - files: []string{"cmd/workspace/list.go"}, - expectedPackages: []string{"./cmd/workspace"}, - expectedAcceptance: []string{"workspace"}, - }, - { - name: "libs auth", - files: []string{"libs/auth/auth.go"}, - expectedPackages: []string{"./libs/auth"}, - expectedAcceptance: []string{"auth"}, - }, - { - name: "libs template", - files: []string{"libs/template/renderer.go"}, - expectedPackages: []string{"./libs/template"}, - expectedAcceptance: []string{"bundle/templates"}, + name: "experimental_subdir", + files: []string{ + "experimental/ssh/main.go", + "experimental/ssh/lib/server.go", + }, + packages: []string{"experimental/ssh/..."}, + acceptance: []string{}, + }, + { + name: "multiple_experimental_subdirs", + files: []string{ + "experimental/ssh/main.go", + "experimental/aitools/server.go", + }, + packages: []string{ + "experimental/...", + }, + acceptance: []string{}, + }, + { + name: "non_experimental", + files: []string{ + "bundle/config.go", + "cmd/bundle/deploy.go", + }, + packages: []string{}, + acceptance: []string{}, + }, + { + name: "mixed_experimental_and_other", + files: []string{ + "experimental/ssh/main.go", + "go.mod", + }, + packages: []string{}, + acceptance: []string{}, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { packages, acceptance := applyRules(tt.files) - if len(packages) != len(tt.expectedPackages) { - t.Errorf("expected %d packages, got %d: %v", len(tt.expectedPackages), len(packages), packages) - return - } - for i, pkg := range packages { - if pkg != tt.expectedPackages[i] { - t.Errorf("package[%d]: expected %q, got %q", i, tt.expectedPackages[i], pkg) - } - } - if len(acceptance) != len(tt.expectedAcceptance) { - t.Errorf("expected %d acceptance prefixes, got %d: %v", len(tt.expectedAcceptance), len(acceptance), acceptance) - return - } - for i, prefix := range acceptance { - if prefix != tt.expectedAcceptance[i] { - t.Errorf("acceptance[%d]: expected %q, got %q", i, tt.expectedAcceptance[i], prefix) - } - } + assert.Equal(t, tt.packages, packages) + assert.Equal(t, tt.acceptance, acceptance) }) } } - -func TestMainWithEnvVar(t *testing.T) { - // Test that GITHUB_BASE_REF is respected - originalBaseRef := os.Getenv("GITHUB_BASE_REF") - defer func() { - if originalBaseRef == "" { - os.Unsetenv("GITHUB_BASE_REF") - } else { - os.Setenv("GITHUB_BASE_REF", originalBaseRef) - } - }() - - os.Setenv("GITHUB_BASE_REF", "main") - // We can't easily test the full execution without git, but we can verify the env var is read - baseRef := os.Getenv("GITHUB_BASE_REF") - if baseRef != "main" { - t.Errorf("expected GITHUB_BASE_REF to be 'main', got %q", baseRef) - } -} From ed8dc5b2415d7e59a65bdeabdbd092976b10cea3 Mon Sep 17 00:00:00 2001 From: Pieter Noordhuis Date: Thu, 27 Nov 2025 16:49:30 +0100 Subject: [PATCH 07/21] . --- Makefile | 12 ++++ tools/testmask/main.go | 50 +------------- tools/testmask/main_test.go | 63 ------------------ tools/testmask/rules.go | 79 ---------------------- tools/testmask/rules_test.go | 105 ----------------------------- tools/testmask/targets.go | 85 ++++++++++++++++++++++++ tools/testmask/targets_test.go | 116 +++++++++++++++++++++++++++++++++ 7 files changed, 215 insertions(+), 295 deletions(-) delete mode 100644 tools/testmask/main_test.go delete mode 100644 tools/testmask/rules.go delete mode 100644 tools/testmask/rules_test.go create mode 100644 tools/testmask/targets.go create mode 100644 tools/testmask/targets_test.go diff --git a/Makefile b/Makefile index 2dc2801008..50775d493e 100644 --- a/Makefile +++ b/Makefile @@ -155,3 +155,15 @@ generate: .PHONY: lint lintfull tidy lintcheck fmt fmtfull test cover showcover build snapshot snapshot-release schema integration integration-short acc-cover acc-showcover docs ws wsfix links checks test-update test-update-templates test-update-aws test-update-all generate-validation + +test-exp-aitools: + make test TEST_PACKAGES="./experimental/aitools/..." ACCEPTANCE_TEST_FILTER="TestAccept/idontexistyet/aitools" + +test-exp-apps-mcp: + make test TEST_PACKAGES="./experimental/apps-mcp/..." ACCEPTANCE_TEST_FILTER="TestAccept/idontexistyet/apps-mcp" + +test-exp-ssh: + make test TEST_PACKAGES="./experimental/ssh/..." ACCEPTANCE_TEST_FILTER="TestAccept/ssh" + +test-pipelines: + make test TEST_PACKAGES="./cmd/pipelines/..." ACCEPTANCE_TEST_FILTER="TestAccept/pipelines" diff --git a/tools/testmask/main.go b/tools/testmask/main.go index 0704893ab7..110c4cf54f 100644 --- a/tools/testmask/main.go +++ b/tools/testmask/main.go @@ -1,22 +1,11 @@ package main import ( - "errors" "fmt" "os" "strings" ) -// errSkip indicates that files matching this rule should be skipped. -var errSkip = errors.New("skip") - -// RuleFunc is a function that processes a list of changed files. -// Returns: -// - packages: list of packages to unit test (empty means test all) -// - acceptancePrefixes: list of acceptance test prefixes (empty means test all) -// - error: errSkip to skip these files, or nil/other error -type RuleFunc func(files []string) (packages, acceptancePrefixes []string, err error) - func main() { baseRef := os.Getenv("GITHUB_BASE_REF") if baseRef == "" { @@ -40,41 +29,6 @@ func main() { os.Exit(1) } - packages, acceptancePrefixes := applyRules(changedFiles) - - // Output packages (space-separated, or empty for all) - if len(packages) == 0 { - fmt.Println("") - } else { - fmt.Println(strings.Join(packages, " ")) - } - - // Output acceptance test prefixes (space-separated, or empty for all) - if len(acceptancePrefixes) == 0 { - fmt.Println("") - } else { - fmt.Println(strings.Join(acceptancePrefixes, " ")) - } -} - -// applyRules applies rules sequentially to all files until one returns a real error or real outputs. -func applyRules(changedFiles []string) ([]string, []string) { - rules := AllRules() - - for _, rule := range rules { - packages, prefixes, err := rule(changedFiles) - if err != nil && err != errSkip { - // Real error - exit - fmt.Fprintf(os.Stderr, "Error applying rule: %v\n", err) - os.Exit(1) - } - if err == errSkip { - // Skip this rule, continue to next rule - continue - } - return packages, prefixes - } - - // No rule matched - test everything (empty means test all) - return []string{}, []string{} + targets := GetTargets(changedFiles) + fmt.Println(strings.Join(targets, " ")) } diff --git a/tools/testmask/main_test.go b/tools/testmask/main_test.go deleted file mode 100644 index 38b42ed188..0000000000 --- a/tools/testmask/main_test.go +++ /dev/null @@ -1,63 +0,0 @@ -package main - -import ( - "testing" - - "github.com/stretchr/testify/assert" -) - -func TestApplyRules(t *testing.T) { - tests := []struct { - name string - files []string - packages []string - acceptance []string - }{ - { - name: "experimental_subdir", - files: []string{ - "experimental/ssh/main.go", - "experimental/ssh/lib/server.go", - }, - packages: []string{"experimental/ssh/..."}, - acceptance: []string{}, - }, - { - name: "multiple_experimental_subdirs", - files: []string{ - "experimental/ssh/main.go", - "experimental/aitools/server.go", - }, - packages: []string{ - "experimental/...", - }, - acceptance: []string{}, - }, - { - name: "non_experimental", - files: []string{ - "bundle/config.go", - "cmd/bundle/deploy.go", - }, - packages: []string{}, - acceptance: []string{}, - }, - { - name: "mixed_experimental_and_other", - files: []string{ - "experimental/ssh/main.go", - "go.mod", - }, - packages: []string{}, - acceptance: []string{}, - }, - } - - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - packages, acceptance := applyRules(tt.files) - assert.Equal(t, tt.packages, packages) - assert.Equal(t, tt.acceptance, acceptance) - }) - } -} diff --git a/tools/testmask/rules.go b/tools/testmask/rules.go deleted file mode 100644 index 4bf6ba5002..0000000000 --- a/tools/testmask/rules.go +++ /dev/null @@ -1,79 +0,0 @@ -package main - -import ( - "path/filepath" - "regexp" - "strings" -) - -// AllRules returns all rules in order of application. -func AllRules() []RuleFunc { - return []RuleFunc{ - handleExperimental, - } -} - -// longestCommonDirectoryPrefix returns the longest common directory prefix of a list of files. -// It always returns a directory path, never a file path. -func longestCommonDirectoryPrefix(files []string) string { - if len(files) == 0 { - return "" - } - if len(files) == 1 { - dir := filepath.Dir(files[0]) - if dir == "." { - return "" - } - return dir + "/" - } - prefix := files[0] - for _, file := range files[1:] { - prefix = longestCommonPrefix(prefix, file) - if prefix == "" { - return "" - } - } - // Ensure we return a directory path (remove filename if present) - if lastSlash := strings.LastIndex(prefix, "/"); lastSlash >= 0 { - return prefix[:lastSlash+1] - } - return "" -} - -// longestCommonPrefix returns the longest common prefix of two strings. -// For file paths, it stops at directory boundaries (after a "/"). -func longestCommonPrefix(a, b string) string { - minLen := len(a) - if len(b) < minLen { - minLen = len(b) - } - lastSlash := -1 - for i := range minLen { - if a[i] != b[i] { - if lastSlash >= 0 { - return a[:lastSlash+1] - } - return "" - } - if a[i] == '/' { - lastSlash = i - } - } - if lastSlash >= 0 { - return a[:lastSlash+1] - } - return a[:minLen] -} - -// handleExperimental deals with changes under the experimental/ directory. -func handleExperimental(files []string) ([]string, []string, error) { - prefix := longestCommonDirectoryPrefix(files) - - // Match this prefix to experimental and optionally a package nested under it. - pkg := regexp.MustCompile(`^experimental(/[\w-_]+)?`).FindString(prefix) - if pkg == "" { - return nil, nil, errSkip - } - - return []string{pkg + "/..."}, []string{}, nil -} diff --git a/tools/testmask/rules_test.go b/tools/testmask/rules_test.go deleted file mode 100644 index af9b89160e..0000000000 --- a/tools/testmask/rules_test.go +++ /dev/null @@ -1,105 +0,0 @@ -package main - -import ( - "testing" - - "github.com/stretchr/testify/assert" -) - -func TestLongestCommonDirectoryPrefix(t *testing.T) { - tests := []struct { - name string - files []string - want string - }{ - { - name: "empty_list", - files: []string{}, - want: "", - }, - { - name: "single_file", - files: []string{"foo/bar/baz.txt"}, - want: "foo/bar/", - }, - { - name: "all_same_dir", - files: []string{"foo/bar/a.go", "foo/bar/b.go"}, - want: "foo/bar/", - }, - { - name: "different_dirs", - files: []string{"foo/bar/a.go", "foo/baz/a.go"}, - want: "foo/", - }, - { - name: "top_level", - files: []string{"top/a.go", "top/b.go"}, - want: "top/", - }, - { - name: "absolute_paths_same_dir", - files: []string{"/a/b/c.go", "/a/b/d.go"}, - want: "/a/b/", - }, - { - name: "absolute_paths_diff_dirs", - files: []string{"/a/b/c.go", "/a/c/b.go"}, - want: "/a/", - }, - { - name: "no_common_prefix", - files: []string{"foo/a.go", "bar/b.go"}, - want: "", - }, - { - name: "partial_overlap", - files: []string{"foo/bar/baz/a.go", "foo/bar/qux/b.go"}, - want: "foo/bar/", - }, - { - name: "single_character_common", - files: []string{"a/b.go", "a/c.go"}, - want: "a/", - }, - { - name: "root_level_file", - files: []string{"file.go"}, - want: "", - }, - } - - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - got := longestCommonDirectoryPrefix(tt.files) - assert.Equal(t, tt.want, got) - }) - } -} - -func TestHandleExperimental(t *testing.T) { - // Changes only in experimental/ssh/ should match. - packages, acceptance, err := handleExperimental([]string{ - "experimental/ssh/main.go", - "experimental/ssh/lib/server.go", - }) - assert.NoError(t, err) - assert.Equal(t, []string{"experimental/ssh/..."}, packages) - assert.Empty(t, acceptance) - - // Changes in experimental/ plus go.mod should not match. - _, _, err = handleExperimental([]string{ - "experimental/ssh/main.go", - "go.mod", - }) - assert.ErrorIs(t, err, errSkip) - - // Changes spanning multiple experimental subdirs should test all of experimental. - packages, acceptance, err = handleExperimental([]string{ - "experimental/ssh/main.go", - "experimental/aitools/server.go", - }) - assert.NoError(t, err) - assert.Equal(t, []string{"experimental/..."}, packages) - assert.Empty(t, acceptance) -} diff --git a/tools/testmask/targets.go b/tools/testmask/targets.go new file mode 100644 index 0000000000..24478f3d3b --- /dev/null +++ b/tools/testmask/targets.go @@ -0,0 +1,85 @@ +package main + +import ( + "sort" + "strings" +) + +type targetMapping struct { + patterns []string + target string +} + +var fileTargetMappings = []targetMapping{ + { + patterns: []string{ + "experimental/aitools/", + }, + target: "test-exp-aitools", + }, + { + patterns: []string{ + "experimental/apps-mcp/", + }, + target: "test-exp-apps-mcp", + }, + { + patterns: []string{ + "experimental/ssh/", + "acceptance/ssh/", + }, + target: "test-exp-ssh", + }, + { + patterns: []string{ + "cmd/pipelines/", + "acceptance/pipelines/", + }, + target: "test-pipelines", + }, +} + +// GetTargets matches files to targets based on patterns and returns the matched targets. +func GetTargets(files []string) []string { + targetSet := make(map[string]bool) + unmatchedFiles := []string{} + + for _, file := range files { + matched := false + for _, mapping := range fileTargetMappings { + for _, pattern := range mapping.patterns { + if strings.HasPrefix(file, pattern) { + targetSet[mapping.target] = true + matched = true + break + } + } + if matched { + break + } + } + if !matched { + unmatchedFiles = append(unmatchedFiles, file) + } + } + + // If there are unmatched files, add the "test" target to run all tests. + if len(unmatchedFiles) > 0 { + targetSet["test"] = true + } + + // If there are no targets, add the "test" target to run all tests. + if len(targetSet) == 0 { + return []string{"test"} + } + + // Convert map to sorted slice + targets := make([]string, 0, len(targetSet)) + for target := range targetSet { + targets = append(targets, target) + } + + // Sort for consistent output + sort.Strings(targets) + return targets +} diff --git a/tools/testmask/targets_test.go b/tools/testmask/targets_test.go new file mode 100644 index 0000000000..c4224eaa6e --- /dev/null +++ b/tools/testmask/targets_test.go @@ -0,0 +1,116 @@ +package main + +import ( + "os" + "regexp" + "strings" + "testing" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +func TestGetTargets(t *testing.T) { + tests := []struct { + name string + files []string + targets []string + }{ + { + name: "experimental_ssh", + files: []string{ + "experimental/ssh/main.go", + "experimental/ssh/lib/server.go", + }, + targets: []string{"test-exp-ssh"}, + }, + { + name: "experimental_aitools", + files: []string{ + "experimental/aitools/server.go", + }, + targets: []string{"test-exp-aitools"}, + }, + { + name: "pipelines", + files: []string{ + "cmd/pipelines/main.go", + }, + targets: []string{"test-pipelines"}, + }, + { + name: "non_matching", + files: []string{ + "bundle/config.go", + "cmd/bundle/deploy.go", + }, + targets: []string{"test"}, + }, + { + name: "mixed_matching_and_unmatched", + files: []string{ + "experimental/ssh/main.go", + "go.mod", + }, + targets: []string{"test", "test-exp-ssh"}, + }, + { + name: "empty_files", + files: []string{}, + targets: []string{"test"}, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + targets := GetTargets(tt.files) + assert.Equal(t, tt.targets, targets) + }) + } +} + +func TestTargetsExistInMakefile(t *testing.T) { + // Collect all targets from fileTargetMappings + expectedTargets := make(map[string]bool) + for _, mapping := range fileTargetMappings { + expectedTargets[mapping.target] = true + } + + // Also include "test" since it's used in GetTargets + expectedTargets["test"] = true + + // Read and parse Makefile to extract target names + makefileTargets := parseMakefileTargets(t, "../../Makefile") + + // Verify all expected targets exist in Makefile + var missingTargets []string + for target := range expectedTargets { + if !makefileTargets[target] { + missingTargets = append(missingTargets, target) + } + } + + if len(missingTargets) > 0 { + t.Errorf("The following targets are defined in targets.go but do not exist in Makefile: %v", missingTargets) + } +} + +// parseMakefileTargets parses a Makefile and returns a set of target names +func parseMakefileTargets(t *testing.T, makefilePath string) map[string]bool { + targets := make(map[string]bool) + targetRegex := regexp.MustCompile(`^([a-zA-Z0-9_-]+):`) + + content, err := os.ReadFile(makefilePath) + require.NoError(t, err) + + lines := strings.Split(string(content), "\n") + for _, line := range lines { + // Match Makefile target pattern: target: + matches := targetRegex.FindStringSubmatch(line) + if len(matches) > 1 { + targets[matches[1]] = true + } + } + + return targets +} From edc1f999c9290bbd4c829a7373abd0759cd552ef Mon Sep 17 00:00:00 2001 From: Pieter Noordhuis Date: Thu, 27 Nov 2025 17:04:09 +0100 Subject: [PATCH 08/21] Use JSON array with targets --- .github/workflows/push.yml | 101 ++++++++++++++++++++++++++++++++----- tools/testmask/main.go | 8 ++- 2 files changed, 93 insertions(+), 16 deletions(-) diff --git a/.github/workflows/push.yml b/.github/workflows/push.yml index d51ef6b492..2c09569d59 100644 --- a/.github/workflows/push.yml +++ b/.github/workflows/push.yml @@ -35,8 +35,7 @@ jobs: if: ${{ github.event_name == 'pull_request' }} runs-on: ubuntu-latest outputs: - packages: ${{ steps.parse.outputs.packages }} - acceptance_prefix: ${{ steps.parse.outputs.acceptance_prefix }} + targets: ${{ steps.mask.outputs.targets }} steps: - name: Checkout repository uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 @@ -48,27 +47,37 @@ jobs: with: go-version-file: tools/go.mod - - name: Run testmask + - name: Run testmask (pull requests) + if: ${{ github.event_name == 'pull_request' }} id: mask working-directory: tools/testmask run: | - go run . ${{ github.event.pull_request.head.sha }} ${{ github.event.pull_request.base.sha }} > output.txt + go run . ${{ github.event.pull_request.head.sha }} ${{ github.event.pull_request.base.sha }} | tee output.json + echo "targets=$(jq -r '.' output.json)" >> $GITHUB_OUTPUT - - name: Parse testmask output - id: parse + - name: Run testmask (merge group) + if: ${{ github.event_name == 'merge_group' }} + id: mask working-directory: tools/testmask run: | - packages=$(head -n 1 output.txt) - acceptance_prefix=$(tail -n 1 output.txt) - rm -f output.txt + go run . ${{ github.event.merge_group.head.sha }} ${{ github.event.merge_group.base.sha }} | tee output.json + echo "targets=$(jq -r '.' output.json)" >> $GITHUB_OUTPUT - echo "packages: \"$packages\"" - echo "acceptance_prefix: \"$acceptance_prefix\"" - echo "packages=$packages" >> $GITHUB_OUTPUT - echo "acceptance_prefix=$acceptance_prefix" >> $GITHUB_OUTPUT + - name: Run testmask (other events) + if: ${{ github.event_name != 'pull_request' && github.event_name != 'merge_group' }} + id: mask + working-directory: tools/testmask + run: | + # Always run all tests + echo "targets=[\"test\"]" >> $GITHUB_OUTPUT tests: - needs: cleanups + needs: + - cleanups + - testmask + + # Only run if the target is in the list of targets from testmask + if: ${{ contains(needs.testmask.outputs.targets, matrix.target) }} runs-on: ${{ matrix.os }} strategy: @@ -140,6 +149,70 @@ jobs: - name: Analyze slow tests run: make slowest + tests-selective: + needs: + - cleanups + - testmask + + # Only run if the target is in the list of targets from testmask + if: ${{ contains(needs.testmask.outputs.targets, matrix.target) }} + name: "make ${{ matrix.target }} (${{ matrix.os }})" + runs-on: ${{ matrix.os }} + + strategy: + fail-fast: false + matrix: + target: + - test-exp-aitools + - test-exp-apps-mcp + - test-exp-ssh + - test-pipelines + os: + - macos-latest + - ubuntu-latest + - windows-latest + + steps: + - name: Checkout repository and submodules + uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 + + - name: Create cache identifier + run: echo "${{ matrix.target }}" > target.txt + + - name: Setup Go + uses: actions/setup-go@44694675825211faa026b3c33043df3e48a5fa00 # v6.0.0 + with: + go-version-file: go.mod + cache-dependency-path: | + go.sum + target.txt + + - name: Setup Python + uses: actions/setup-python@e797f83bcb11b83ae66e0230d6156d7c80228e7c # v6.0.0 + with: + python-version: '3.13' + + - name: Install uv + uses: astral-sh/setup-uv@85856786d1ce8acfbcc2f13a5f3fbd6b938f9f41 # v7.1.2 + with: + version: "0.8.9" + + - name: Install ruff (Python linter and formatter) + uses: astral-sh/ruff-action@57714a7c8a2e59f32539362ba31877a1957dded1 # v3.5.1 + with: + version: "0.9.1" + args: "--version" + + - name: Pull external libraries + run: | + go mod download + pip3 install wheel==0.45.1 + + - name: Run tests + run: | + make ${{ matrix.target }} + + validate-generated-is-up-to-date: needs: cleanups runs-on: ubuntu-latest diff --git a/tools/testmask/main.go b/tools/testmask/main.go index 110c4cf54f..028e7199c1 100644 --- a/tools/testmask/main.go +++ b/tools/testmask/main.go @@ -1,9 +1,9 @@ package main import ( + "encoding/json" "fmt" "os" - "strings" ) func main() { @@ -30,5 +30,9 @@ func main() { } targets := GetTargets(changedFiles) - fmt.Println(strings.Join(targets, " ")) + err = json.NewEncoder(os.Stdout).Encode(targets) + if err != nil { + fmt.Fprintf(os.Stderr, "Error encoding targets: %v\n", err) + os.Exit(1) + } } From e166105b4be99daa5d910379c2ac5cf52f1a9487 Mon Sep 17 00:00:00 2001 From: Pieter Noordhuis Date: Thu, 27 Nov 2025 17:04:55 +0100 Subject: [PATCH 09/21] Use fromJSON --- .github/workflows/push.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/push.yml b/.github/workflows/push.yml index 2c09569d59..5a31a66c52 100644 --- a/.github/workflows/push.yml +++ b/.github/workflows/push.yml @@ -77,7 +77,7 @@ jobs: - testmask # Only run if the target is in the list of targets from testmask - if: ${{ contains(needs.testmask.outputs.targets, matrix.target) }} + if: ${{ contains(fromJSON(needs.testmask.outputs.targets), "test") }} runs-on: ${{ matrix.os }} strategy: @@ -155,7 +155,7 @@ jobs: - testmask # Only run if the target is in the list of targets from testmask - if: ${{ contains(needs.testmask.outputs.targets, matrix.target) }} + if: ${{ contains(fromJSON(needs.testmask.outputs.targets), matrix.target) }} name: "make ${{ matrix.target }} (${{ matrix.os }})" runs-on: ${{ matrix.os }} From 8d97d8f7762e406ac655f637ca9e5618424d5148 Mon Sep 17 00:00:00 2001 From: Pieter Noordhuis Date: Thu, 27 Nov 2025 17:06:12 +0100 Subject: [PATCH 10/21] Single quotes --- .github/workflows/push.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/push.yml b/.github/workflows/push.yml index 5a31a66c52..b968dc66d6 100644 --- a/.github/workflows/push.yml +++ b/.github/workflows/push.yml @@ -77,7 +77,7 @@ jobs: - testmask # Only run if the target is in the list of targets from testmask - if: ${{ contains(fromJSON(needs.testmask.outputs.targets), "test") }} + if: ${{ contains(fromJSON(needs.testmask.outputs.targets), 'test') }} runs-on: ${{ matrix.os }} strategy: From 0fe377f63c3368af3710f9428653c285f7e3690d Mon Sep 17 00:00:00 2001 From: Pieter Noordhuis Date: Thu, 27 Nov 2025 17:25:37 +0100 Subject: [PATCH 11/21] Reusable axction to setup job; split out jobs per target --- .../setup-build-environment/action.yml | 48 ++++++ .github/workflows/push.yml | 154 ++++++++++-------- 2 files changed, 134 insertions(+), 68 deletions(-) create mode 100644 .github/actions/setup-build-environment/action.yml diff --git a/.github/actions/setup-build-environment/action.yml b/.github/actions/setup-build-environment/action.yml new file mode 100644 index 0000000000..cee2cda36f --- /dev/null +++ b/.github/actions/setup-build-environment/action.yml @@ -0,0 +1,48 @@ +name: 'Setup Build Environment' +description: 'Sets up the build environment with Go, Python, uv, and ruff' + +inputs: + cache-key: + description: 'Cache key identifier for Go cache' + required: true + +runs: + using: 'composite' + steps: + - name: Checkout repository and submodules + uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 + shell: bash + + - name: Create cache identifier + run: echo "${{ inputs.cache-key }}" > target.txt + shell: bash + + - name: Setup Go + uses: actions/setup-go@44694675825211faa026b3c33043df3e48a5fa00 # v6.0.0 + with: + go-version-file: go.mod + cache-dependency-path: | + go.sum + target.txt + + - name: Setup Python + uses: actions/setup-python@e797f83bcb11b83ae66e0230d6156d7c80228e7c # v6.0.0 + with: + python-version: '3.13' + + - name: Install uv + uses: astral-sh/setup-uv@85856786d1ce8acfbcc2f13a5f3fbd6b938f9f41 # v7.1.2 + with: + version: "0.8.9" + + - name: Install ruff (Python linter and formatter) + uses: astral-sh/ruff-action@57714a7c8a2e59f32539362ba31877a1957dded1 # v3.5.1 + with: + version: "0.9.1" + args: "--version" + + - name: Pull external libraries + run: | + go mod download + pip3 install wheel==0.45.1 + shell: bash diff --git a/.github/workflows/push.yml b/.github/workflows/push.yml index b968dc66d6..cb30541ee6 100644 --- a/.github/workflows/push.yml +++ b/.github/workflows/push.yml @@ -95,40 +95,10 @@ jobs: - os: null steps: - - name: Checkout repository and submodules - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 - - - name: Create deployment-specific cache identifier - run: echo "${{ matrix.deployment }}" > deployment-type.txt - - - name: Setup Go - uses: actions/setup-go@44694675825211faa026b3c33043df3e48a5fa00 # v6.0.0 + - name: Setup build environment + uses: ./.github/actions/setup-build-environment with: - go-version-file: go.mod - cache-dependency-path: | - go.sum - deployment-type.txt - - - name: Setup Python - uses: actions/setup-python@e797f83bcb11b83ae66e0230d6156d7c80228e7c # v6.0.0 - with: - python-version: '3.13' - - - name: Install uv - uses: astral-sh/setup-uv@85856786d1ce8acfbcc2f13a5f3fbd6b938f9f41 # v7.1.2 - with: - version: "0.8.9" - - - name: Install ruff (Python linter and formatter) - uses: astral-sh/ruff-action@57714a7c8a2e59f32539362ba31877a1957dded1 # v3.5.1 - with: - version: "0.9.1" - args: "--version" - - - name: Pull external libraries - run: | - go mod download - pip3 install wheel==0.45.1 + cache-key: ${{matrix.target}}-${{ matrix.deployment }} - name: Run tests without coverage # We run tests without coverage on PR, merge_group, and schedule because we don't make use of coverage information @@ -149,69 +119,117 @@ jobs: - name: Analyze slow tests run: make slowest - tests-selective: + test-exp-aitools: needs: - cleanups - testmask # Only run if the target is in the list of targets from testmask - if: ${{ contains(fromJSON(needs.testmask.outputs.targets), matrix.target) }} - name: "make ${{ matrix.target }} (${{ matrix.os }})" + if: ${{ contains(fromJSON(needs.testmask.outputs.targets), 'test-exp-aitools') }} + name: "make test-exp-aitools (${{ matrix.os }})" runs-on: ${{ matrix.os }} strategy: fail-fast: false matrix: - target: - - test-exp-aitools - - test-exp-apps-mcp - - test-exp-ssh - - test-pipelines os: - macos-latest - ubuntu-latest - windows-latest steps: - - name: Checkout repository and submodules - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 + - name: Setup build environment + uses: ./.github/actions/setup-build-environment + with: + cache-key: test-exp-aitools - - name: Create cache identifier - run: echo "${{ matrix.target }}" > target.txt + - name: Run tests + run: | + make test-exp-aitools - - name: Setup Go - uses: actions/setup-go@44694675825211faa026b3c33043df3e48a5fa00 # v6.0.0 - with: - go-version-file: go.mod - cache-dependency-path: | - go.sum - target.txt + test-exp-apps-mcp: + needs: + - cleanups + - testmask - - name: Setup Python - uses: actions/setup-python@e797f83bcb11b83ae66e0230d6156d7c80228e7c # v6.0.0 - with: - python-version: '3.13' + # Only run if the target is in the list of targets from testmask + if: ${{ contains(fromJSON(needs.testmask.outputs.targets), 'test-exp-apps-mcp') }} + name: "make test-exp-apps-mcp (${{ matrix.os }})" + runs-on: ${{ matrix.os }} - - name: Install uv - uses: astral-sh/setup-uv@85856786d1ce8acfbcc2f13a5f3fbd6b938f9f41 # v7.1.2 - with: - version: "0.8.9" + strategy: + fail-fast: false + matrix: + os: + - macos-latest + - ubuntu-latest + - windows-latest - - name: Install ruff (Python linter and formatter) - uses: astral-sh/ruff-action@57714a7c8a2e59f32539362ba31877a1957dded1 # v3.5.1 + steps: + - name: Setup build environment + uses: ./.github/actions/setup-build-environment with: - version: "0.9.1" - args: "--version" + cache-key: test-exp-apps-mcp - - name: Pull external libraries + - name: Run tests run: | - go mod download - pip3 install wheel==0.45.1 + make test-exp-apps-mcp + + test-exp-ssh: + needs: + - cleanups + - testmask + + # Only run if the target is in the list of targets from testmask + if: ${{ contains(fromJSON(needs.testmask.outputs.targets), 'test-exp-ssh') }} + name: "make test-exp-ssh (${{ matrix.os }})" + runs-on: ${{ matrix.os }} + + strategy: + fail-fast: false + matrix: + os: + - macos-latest + - ubuntu-latest + - windows-latest + + steps: + - name: Setup build environment + uses: ./.github/actions/setup-build-environment + with: + cache-key: test-exp-ssh - name: Run tests run: | - make ${{ matrix.target }} + make test-exp-ssh + + test-pipelines: + needs: + - cleanups + - testmask + # Only run if the target is in the list of targets from testmask + if: ${{ contains(fromJSON(needs.testmask.outputs.targets), 'test-pipelines') }} + name: "make test-pipelines (${{ matrix.os }})" + runs-on: ${{ matrix.os }} + + strategy: + fail-fast: false + matrix: + os: + - macos-latest + - ubuntu-latest + - windows-latest + + steps: + - name: Setup build environment + uses: ./.github/actions/setup-build-environment + with: + cache-key: test-pipelines + + - name: Run tests + run: | + make test-pipelines validate-generated-is-up-to-date: needs: cleanups From cc0c351bff0050588fc82c82d369182f20b37a7a Mon Sep 17 00:00:00 2001 From: Pieter Noordhuis Date: Thu, 27 Nov 2025 17:26:52 +0100 Subject: [PATCH 12/21] Multiple step ids --- .github/workflows/push.yml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/push.yml b/.github/workflows/push.yml index cb30541ee6..8c35046feb 100644 --- a/.github/workflows/push.yml +++ b/.github/workflows/push.yml @@ -35,7 +35,7 @@ jobs: if: ${{ github.event_name == 'pull_request' }} runs-on: ubuntu-latest outputs: - targets: ${{ steps.mask.outputs.targets }} + targets: ${{ steps.mask1.outputs.targets | steps.mask2.outputs.targets | steps.mask3.outputs.targets }} steps: - name: Checkout repository uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 @@ -49,7 +49,7 @@ jobs: - name: Run testmask (pull requests) if: ${{ github.event_name == 'pull_request' }} - id: mask + id: mask1 working-directory: tools/testmask run: | go run . ${{ github.event.pull_request.head.sha }} ${{ github.event.pull_request.base.sha }} | tee output.json @@ -57,7 +57,7 @@ jobs: - name: Run testmask (merge group) if: ${{ github.event_name == 'merge_group' }} - id: mask + id: mask2 working-directory: tools/testmask run: | go run . ${{ github.event.merge_group.head.sha }} ${{ github.event.merge_group.base.sha }} | tee output.json @@ -65,7 +65,7 @@ jobs: - name: Run testmask (other events) if: ${{ github.event_name != 'pull_request' && github.event_name != 'merge_group' }} - id: mask + id: mask3 working-directory: tools/testmask run: | # Always run all tests From 72275e3927ccd82696c83a9d950d402b91707848 Mon Sep 17 00:00:00 2001 From: Pieter Noordhuis Date: Thu, 27 Nov 2025 17:28:00 +0100 Subject: [PATCH 13/21] Fix --- .github/workflows/push.yml | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/.github/workflows/push.yml b/.github/workflows/push.yml index 8c35046feb..ac21ebe793 100644 --- a/.github/workflows/push.yml +++ b/.github/workflows/push.yml @@ -32,10 +32,9 @@ jobs: run: gh cache delete --all --repo databricks/cli || true testmask: - if: ${{ github.event_name == 'pull_request' }} runs-on: ubuntu-latest outputs: - targets: ${{ steps.mask1.outputs.targets | steps.mask2.outputs.targets | steps.mask3.outputs.targets }} + targets: ${{ steps.mask1.outputs.targets || steps.mask2.outputs.targets || steps.mask3.outputs.targets }} steps: - name: Checkout repository uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 From 08501ccaaf5fdc3af78ab1582276963a05308c2e Mon Sep 17 00:00:00 2001 From: Pieter Noordhuis Date: Thu, 27 Nov 2025 17:29:43 +0100 Subject: [PATCH 14/21] Fix --- .github/workflows/push.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/push.yml b/.github/workflows/push.yml index ac21ebe793..9615280fb4 100644 --- a/.github/workflows/push.yml +++ b/.github/workflows/push.yml @@ -52,7 +52,7 @@ jobs: working-directory: tools/testmask run: | go run . ${{ github.event.pull_request.head.sha }} ${{ github.event.pull_request.base.sha }} | tee output.json - echo "targets=$(jq -r '.' output.json)" >> $GITHUB_OUTPUT + echo "targets=$(jq -c '.' output.json)" >> $GITHUB_OUTPUT - name: Run testmask (merge group) if: ${{ github.event_name == 'merge_group' }} @@ -60,7 +60,7 @@ jobs: working-directory: tools/testmask run: | go run . ${{ github.event.merge_group.head.sha }} ${{ github.event.merge_group.base.sha }} | tee output.json - echo "targets=$(jq -r '.' output.json)" >> $GITHUB_OUTPUT + echo "targets=$(jq -c '.' output.json)" >> $GITHUB_OUTPUT - name: Run testmask (other events) if: ${{ github.event_name != 'pull_request' && github.event_name != 'merge_group' }} From 5bd2beb9b07fd917f18aeb54ce520dde5fda5949 Mon Sep 17 00:00:00 2001 From: Pieter Noordhuis Date: Thu, 27 Nov 2025 17:33:24 +0100 Subject: [PATCH 15/21] Fixes --- .github/workflows/push.yml | 23 +++++++++++++++++++---- 1 file changed, 19 insertions(+), 4 deletions(-) diff --git a/.github/workflows/push.yml b/.github/workflows/push.yml index 9615280fb4..f6fa713e58 100644 --- a/.github/workflows/push.yml +++ b/.github/workflows/push.yml @@ -94,6 +94,9 @@ jobs: - os: null steps: + - name: Checkout repository and submodules + uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 + - name: Setup build environment uses: ./.github/actions/setup-build-environment with: @@ -125,7 +128,7 @@ jobs: # Only run if the target is in the list of targets from testmask if: ${{ contains(fromJSON(needs.testmask.outputs.targets), 'test-exp-aitools') }} - name: "make test-exp-aitools (${{ matrix.os }})" + name: "make test-exp-aitools" runs-on: ${{ matrix.os }} strategy: @@ -137,6 +140,9 @@ jobs: - windows-latest steps: + - name: Checkout repository and submodules + uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 + - name: Setup build environment uses: ./.github/actions/setup-build-environment with: @@ -153,7 +159,7 @@ jobs: # Only run if the target is in the list of targets from testmask if: ${{ contains(fromJSON(needs.testmask.outputs.targets), 'test-exp-apps-mcp') }} - name: "make test-exp-apps-mcp (${{ matrix.os }})" + name: "make test-exp-apps-mcp" runs-on: ${{ matrix.os }} strategy: @@ -165,6 +171,9 @@ jobs: - windows-latest steps: + - name: Checkout repository and submodules + uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 + - name: Setup build environment uses: ./.github/actions/setup-build-environment with: @@ -181,7 +190,7 @@ jobs: # Only run if the target is in the list of targets from testmask if: ${{ contains(fromJSON(needs.testmask.outputs.targets), 'test-exp-ssh') }} - name: "make test-exp-ssh (${{ matrix.os }})" + name: "make test-exp-ssh" runs-on: ${{ matrix.os }} strategy: @@ -193,6 +202,9 @@ jobs: - windows-latest steps: + - name: Checkout repository and submodules + uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 + - name: Setup build environment uses: ./.github/actions/setup-build-environment with: @@ -209,7 +221,7 @@ jobs: # Only run if the target is in the list of targets from testmask if: ${{ contains(fromJSON(needs.testmask.outputs.targets), 'test-pipelines') }} - name: "make test-pipelines (${{ matrix.os }})" + name: "make test-pipelines" runs-on: ${{ matrix.os }} strategy: @@ -221,6 +233,9 @@ jobs: - windows-latest steps: + - name: Checkout repository and submodules + uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 + - name: Setup build environment uses: ./.github/actions/setup-build-environment with: From 77679fdc08fe9b18330b79f71a78e36dade63b54 Mon Sep 17 00:00:00 2001 From: Pieter Noordhuis Date: Thu, 27 Nov 2025 17:35:16 +0100 Subject: [PATCH 16/21] Remove shell --- .github/actions/setup-build-environment/action.yml | 3 --- 1 file changed, 3 deletions(-) diff --git a/.github/actions/setup-build-environment/action.yml b/.github/actions/setup-build-environment/action.yml index cee2cda36f..af6de46e4b 100644 --- a/.github/actions/setup-build-environment/action.yml +++ b/.github/actions/setup-build-environment/action.yml @@ -11,11 +11,9 @@ runs: steps: - name: Checkout repository and submodules uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 - shell: bash - name: Create cache identifier run: echo "${{ inputs.cache-key }}" > target.txt - shell: bash - name: Setup Go uses: actions/setup-go@44694675825211faa026b3c33043df3e48a5fa00 # v6.0.0 @@ -45,4 +43,3 @@ runs: run: | go mod download pip3 install wheel==0.45.1 - shell: bash From d1d7c0bbaa5be8d38c82e5fa02f1a2282c3044db Mon Sep 17 00:00:00 2001 From: Pieter Noordhuis Date: Thu, 27 Nov 2025 17:36:45 +0100 Subject: [PATCH 17/21] Bring back shell --- .github/actions/setup-build-environment/action.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/actions/setup-build-environment/action.yml b/.github/actions/setup-build-environment/action.yml index af6de46e4b..b58e954e20 100644 --- a/.github/actions/setup-build-environment/action.yml +++ b/.github/actions/setup-build-environment/action.yml @@ -14,6 +14,7 @@ runs: - name: Create cache identifier run: echo "${{ inputs.cache-key }}" > target.txt + shell: bash - name: Setup Go uses: actions/setup-go@44694675825211faa026b3c33043df3e48a5fa00 # v6.0.0 @@ -43,3 +44,4 @@ runs: run: | go mod download pip3 install wheel==0.45.1 + shell: bash From 2a985f43fc66f2175c75b0a6ed090995d1a101ce Mon Sep 17 00:00:00 2001 From: Pieter Noordhuis Date: Thu, 27 Nov 2025 17:37:41 +0100 Subject: [PATCH 18/21] Rename --- .github/actions/setup-build-environment/action.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/actions/setup-build-environment/action.yml b/.github/actions/setup-build-environment/action.yml index b58e954e20..500f9f6d3a 100644 --- a/.github/actions/setup-build-environment/action.yml +++ b/.github/actions/setup-build-environment/action.yml @@ -13,7 +13,7 @@ runs: uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 - name: Create cache identifier - run: echo "${{ inputs.cache-key }}" > target.txt + run: echo "${{ inputs.cache-key }}" > cache.txt shell: bash - name: Setup Go @@ -22,7 +22,7 @@ runs: go-version-file: go.mod cache-dependency-path: | go.sum - target.txt + cache.txt - name: Setup Python uses: actions/setup-python@e797f83bcb11b83ae66e0230d6156d7c80228e7c # v6.0.0 From f5364e4fdcf841a78849a5b38e3941e45b206874 Mon Sep 17 00:00:00 2001 From: Pieter Noordhuis Date: Fri, 28 Nov 2025 08:30:54 +0100 Subject: [PATCH 19/21] Drop apps-mcp from main test target; Windows is broken --- Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile b/Makefile index 50775d493e..6d83210a7b 100644 --- a/Makefile +++ b/Makefile @@ -1,7 +1,7 @@ default: checks fmt lint # Default packages to test (all) -TEST_PACKAGES = . ./acceptance/internal ./libs/... ./internal/... ./cmd/... ./bundle/... ./experimental/... +TEST_PACKAGES = ./acceptance/internal ./libs/... ./internal/... ./cmd/... ./bundle/... ./experimental/aitools/... ./experimental/ssh/... . # Default acceptance test filter (all) ACCEPTANCE_TEST_FILTER = TestAccept From 1d4dfb930a8bb686489acab1ec09389a3264c888 Mon Sep 17 00:00:00 2001 From: Pieter Noordhuis Date: Fri, 28 Nov 2025 08:31:37 +0100 Subject: [PATCH 20/21] Disable Windows tests for apps-mcp --- .github/workflows/push.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/workflows/push.yml b/.github/workflows/push.yml index f6fa713e58..ad688a1c3b 100644 --- a/.github/workflows/push.yml +++ b/.github/workflows/push.yml @@ -168,7 +168,8 @@ jobs: os: - macos-latest - ubuntu-latest - - windows-latest + # The Windows tests are broken; see https://github.com/databricks/cli/pull/4024. + # - windows-latest steps: - name: Checkout repository and submodules From c145f24d4aed66b531264ca77ed24a0ff2adf4d4 Mon Sep 17 00:00:00 2001 From: Pieter Noordhuis Date: Fri, 28 Nov 2025 08:12:44 +0100 Subject: [PATCH 21/21] Add trigger for test-exp-apps-mcp --- experimental/apps-mcp/trigger.txt | 1 + 1 file changed, 1 insertion(+) create mode 100644 experimental/apps-mcp/trigger.txt diff --git a/experimental/apps-mcp/trigger.txt b/experimental/apps-mcp/trigger.txt new file mode 100644 index 0000000000..cb5fcdfd87 --- /dev/null +++ b/experimental/apps-mcp/trigger.txt @@ -0,0 +1 @@ +Trigger test-exp-apps-mcp