From b9ead37031afa7346b7bde88bc3ecc7ddf923ca1 Mon Sep 17 00:00:00 2001 From: simonfaltum Date: Tue, 9 Jun 2026 23:04:40 +0200 Subject: [PATCH 1/2] Clean up Python mutator temp dir after run Without DATABRICKS_BUNDLE_TMP set, every run of the Python mutator left a -python* temp dir containing the full serialized bundle config in the system temp dir. Remove it once the subprocess output has been consumed. User-specified DATABRICKS_BUNDLE_TMP locations are kept for inspection. Co-authored-by: Isaac --- .../config/mutator/python/python_mutator.go | 21 ++++++++++--- .../mutator/python/python_mutator_test.go | 31 ++++++++++++++++++- 2 files changed, 46 insertions(+), 6 deletions(-) diff --git a/bundle/config/mutator/python/python_mutator.go b/bundle/config/mutator/python/python_mutator.go index ed221c00c6b..da3ca9965b2 100644 --- a/bundle/config/mutator/python/python_mutator.go +++ b/bundle/config/mutator/python/python_mutator.go @@ -238,10 +238,11 @@ func (m *pythonMutator) Apply(ctx context.Context, b *bundle.Bundle) diag.Diagno return dyn.InvalidValue, fmt.Errorf("failed to get Python interpreter path: %w", err) } - cacheDir, err := createCacheDir(ctx) + cacheDir, cleanup, err := createCacheDir(ctx) if err != nil { return dyn.InvalidValue, fmt.Errorf("failed to create cache dir: %w", err) } + defer cleanup() rightRoot, diags := m.runPythonMutator(ctx, leftRoot, runPythonMutatorOpts{ cacheDir: cacheDir, @@ -315,7 +316,9 @@ func (m *pythonMutator) Apply(ctx context.Context, b *bundle.Bundle) diag.Diagno return mutateDiags } -func createCacheDir(ctx context.Context) (string, error) { +// createCacheDir returns the directory for input/output files of the Python +// subprocess, and a cleanup function to call once they are no longer needed. +func createCacheDir(ctx context.Context) (string, func(), error) { // b.LocalStateDir doesn't work because target isn't yet selected // support the same env variable as in b.LocalStateDir @@ -325,13 +328,21 @@ func createCacheDir(ctx context.Context) (string, error) { err := os.MkdirAll(cacheDir, 0o700) if err != nil { - return "", err + return "", nil, err } - return cacheDir, nil + // the user picked this location explicitly, keep the files for inspection + return cacheDir, func() {}, nil + } + + cacheDir, err := os.MkdirTemp("", "-python") + if err != nil { + return "", nil, err } - return os.MkdirTemp("", "-python") + // input.json contains the full serialized bundle configuration; + // don't leave it behind in the system temp dir + return cacheDir, func() { _ = os.RemoveAll(cacheDir) }, nil } func (m *pythonMutator) runPythonMutator(ctx context.Context, root dyn.Value, opts runPythonMutatorOpts) (dyn.Value, diag.Diagnostics) { diff --git a/bundle/config/mutator/python/python_mutator_test.go b/bundle/config/mutator/python/python_mutator_test.go index 1e34c420288..9b106f727bf 100644 --- a/bundle/config/mutator/python/python_mutator_test.go +++ b/bundle/config/mutator/python/python_mutator_test.go @@ -20,6 +20,7 @@ import ( "github.com/databricks/cli/bundle" "github.com/databricks/cli/bundle/config" + "github.com/databricks/cli/internal/testutil" "github.com/databricks/cli/libs/cmdio" "github.com/databricks/cli/libs/process" "github.com/stretchr/testify/assert" @@ -471,6 +472,34 @@ func TestStrictNormalize(t *testing.T) { assert.True(t, strictDiags.HasError()) } +func TestCreateCacheDir(t *testing.T) { + testutil.CleanupEnvironment(t) + + t.Run("DATABRICKS_BUNDLE_TMP is set", func(t *testing.T) { + tempDir := t.TempDir() + t.Setenv(env.TempDirVariable, tempDir) + + cacheDir, cleanup, err := createCacheDir(t.Context()) + require.NoError(t, err) + require.Equal(t, filepath.Join(tempDir, "default", "python"), cacheDir) + + cleanup() + + // user-specified directories are kept for inspection + assert.DirExists(t, cacheDir) + }) + + t.Run("DATABRICKS_BUNDLE_TMP is not set", func(t *testing.T) { + cacheDir, cleanup, err := createCacheDir(t.Context()) + require.NoError(t, err) + require.DirExists(t, cacheDir) + + cleanup() + + assert.NoDirExists(t, cacheDir) + }) +} + func TestExplainProcessErr(t *testing.T) { stderr := "/home/test/.venv/bin/python3: Error while finding module specification for 'databricks.bundles.build' (ModuleNotFoundError: No module named 'databricks')\n" expected := `/home/test/.venv/bin/python3: Error while finding module specification for 'databricks.bundles.build' (ModuleNotFoundError: No module named 'databricks') @@ -501,7 +530,7 @@ func withProcessStub(t *testing.T, args []string, output, diagnostics, locations t.Setenv(env.TempDirVariable, t.TempDir()) // after we override env variable, we always get the same cache dir as mutator - cacheDir, err := createCacheDir(ctx) + cacheDir, _, err := createCacheDir(ctx) require.NoError(t, err) inputPath := filepath.Join(cacheDir, "input.json") From 7ab2e9fb9166cc1192341d18974578bbd909966c Mon Sep 17 00:00:00 2001 From: simonfaltum Date: Wed, 10 Jun 2026 07:23:55 +0200 Subject: [PATCH 2/2] Remove redundant comments Co-authored-by: Isaac --- bundle/config/mutator/python/python_mutator.go | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/bundle/config/mutator/python/python_mutator.go b/bundle/config/mutator/python/python_mutator.go index da3ca9965b2..9112aaa808d 100644 --- a/bundle/config/mutator/python/python_mutator.go +++ b/bundle/config/mutator/python/python_mutator.go @@ -316,8 +316,7 @@ func (m *pythonMutator) Apply(ctx context.Context, b *bundle.Bundle) diag.Diagno return mutateDiags } -// createCacheDir returns the directory for input/output files of the Python -// subprocess, and a cleanup function to call once they are no longer needed. +// createCacheDir returns the directory for input/output files of the Python subprocess, and a cleanup function. func createCacheDir(ctx context.Context) (string, func(), error) { // b.LocalStateDir doesn't work because target isn't yet selected @@ -340,8 +339,7 @@ func createCacheDir(ctx context.Context) (string, func(), error) { return "", nil, err } - // input.json contains the full serialized bundle configuration; - // don't leave it behind in the system temp dir + // input.json contains the full serialized bundle configuration; don't leave it behind in the system temp dir return cacheDir, func() { _ = os.RemoveAll(cacheDir) }, nil }