From fdd8e1519913b2747e39a053a22d1a3259e51669 Mon Sep 17 00:00:00 2001 From: Michael Brooks Date: Fri, 20 Feb 2026 13:53:11 -0800 Subject: [PATCH 1/6] feat(python): auto-activate venv before hook execution Set VIRTUAL_ENV, prepend venv bin to PATH, and unset PYTHONHOME on the CLI process before any hooks run. This lets hook scripts (e.g. get-hooks, start, deploy) resolve the venv's Python and installed packages without requiring developers to manually activate the venv. Activation happens in InitSDKConfig after finding the project root and before reading/executing hooks. It is a no-op when no .venv exists, so non-Python projects are unaffected. Failure is non-fatal (debug log). --- internal/runtime/python/python.go | 38 +++++++++++++ internal/runtime/python/python_test.go | 77 ++++++++++++++++++++++++-- internal/runtime/runtime.go | 8 +++ internal/shared/clients.go | 6 ++ 4 files changed, 123 insertions(+), 6 deletions(-) diff --git a/internal/runtime/python/python.go b/internal/runtime/python/python.go index 9150d0d6..ee453440 100644 --- a/internal/runtime/python/python.go +++ b/internal/runtime/python/python.go @@ -18,6 +18,7 @@ import ( "context" _ "embed" "fmt" + "os" "os/exec" "path/filepath" "regexp" @@ -76,6 +77,43 @@ func getPythonExecutable() string { return "python3" } +// getVenvBinDir returns the platform-specific bin directory inside a virtual environment +func getVenvBinDir(venvPath string) string { + if runtime.GOOS == "windows" { + return filepath.Join(venvPath, "Scripts") + } + return filepath.Join(venvPath, "bin") +} + +// ActivateVenvIfPresent sets the process environment variables that a Python +// virtual environment's activate script would set, so that child processes +// (hook scripts) inherit the activated venv. This is a no-op when no .venv +// directory exists in projectDir. +// +// The three env vars (VIRTUAL_ENV, PATH, PYTHONHOME) are the stable contract +// defined by PEP 405 (Python 3.3, 2012). Other tools (Poetry, tox, pipx) use +// the same approach. Sourcing the shell-specific activate script (activate, +// activate.fish, Activate.ps1) would be higher maintenance because it varies +// by shell. +func ActivateVenvIfPresent(fs afero.Fs, projectDir string) error { + venvPath := getVenvPath(projectDir) + if !venvExists(fs, venvPath) { + return nil + } + + binDir := getVenvBinDir(venvPath) + + if err := os.Setenv("VIRTUAL_ENV", venvPath); err != nil { + return err + } + if err := os.Setenv("PATH", binDir+string(filepath.ListSeparator)+os.Getenv("PATH")); err != nil { + return err + } + os.Unsetenv("PYTHONHOME") + + return nil +} + // getPipExecutable returns the path to the pip executable in the virtual environment func getPipExecutable(venvPath string) string { if runtime.GOOS == "windows" { diff --git a/internal/runtime/python/python_test.go b/internal/runtime/python/python_test.go index 28672590..cdace49d 100644 --- a/internal/runtime/python/python_test.go +++ b/internal/runtime/python/python_test.go @@ -17,7 +17,9 @@ package python import ( "encoding/json" "fmt" + "os" "path/filepath" + "runtime" "testing" "github.com/slackapi/slack-cli/internal/config" @@ -85,19 +87,19 @@ func Test_getVenvPath(t *testing.T) { func Test_getPythonExecutable(t *testing.T) { tests := []struct { - name string + name string expectedExecutable string - skipOnOS string + skipOnOS string }{ { - name: "Get python executable on Unix", + name: "Get python executable on Unix", expectedExecutable: "python3", - skipOnOS: "windows", + skipOnOS: "windows", }, { - name: "Get python executable on Windows", + name: "Get python executable on Windows", expectedExecutable: "python", - skipOnOS: "linux", + skipOnOS: "linux", }, } for _, tt := range tests { @@ -314,6 +316,69 @@ func Test_venvExists(t *testing.T) { } } +func Test_getVenvBinDir(t *testing.T) { + result := getVenvBinDir("/path/to/.venv") + if runtime.GOOS == "windows" { + require.Equal(t, filepath.Join("/path/to/.venv", "Scripts"), result) + } else { + require.Equal(t, filepath.Join("/path/to/.venv", "bin"), result) + } +} + +func Test_ActivateVenvIfPresent(t *testing.T) { + tests := map[string]struct { + createVenv bool + expectedActivated bool + }{ + "activates venv when it exists": { + createVenv: true, + expectedActivated: true, + }, + "no-op when venv does not exist": { + createVenv: false, + expectedActivated: false, + }, + } + for name, tc := range tests { + t.Run(name, func(t *testing.T) { + fs := slackdeps.NewFsMock() + projectDir := "/path/to/project" + venvPath := filepath.Join(projectDir, ".venv") + + // Save and restore env vars + t.Setenv("VIRTUAL_ENV", "") + t.Setenv("PYTHONHOME", "original-pythonhome") + originalPath := os.Getenv("PATH") + t.Setenv("PATH", originalPath) + + if tc.createVenv { + // Create the pip executable so venvExists returns true + pipPath := getPipExecutable(venvPath) + err := fs.MkdirAll(filepath.Dir(pipPath), 0755) + require.NoError(t, err) + err = afero.WriteFile(fs, pipPath, []byte(""), 0755) + require.NoError(t, err) + } + + err := ActivateVenvIfPresent(fs, projectDir) + require.NoError(t, err) + + if tc.expectedActivated { + require.Equal(t, venvPath, os.Getenv("VIRTUAL_ENV")) + expectedBinDir := getVenvBinDir(venvPath) + require.Contains(t, os.Getenv("PATH"), expectedBinDir+string(filepath.ListSeparator)) + // PYTHONHOME should be unset (empty) + _, pythonHomeSet := os.LookupEnv("PYTHONHOME") + require.False(t, pythonHomeSet) + } else { + require.Equal(t, "", os.Getenv("VIRTUAL_ENV")) + require.Equal(t, originalPath, os.Getenv("PATH")) + require.Equal(t, "original-pythonhome", os.Getenv("PYTHONHOME")) + } + }) + } +} + // Test_Python_InstallProjectDependencies tests the main function // NOTE: These tests require Python 3 and pip to be installed for full integration testing. // The function creates real virtual environments and runs real pip commands. diff --git a/internal/runtime/runtime.go b/internal/runtime/runtime.go index 11d35492..cfa5adc0 100644 --- a/internal/runtime/runtime.go +++ b/internal/runtime/runtime.go @@ -66,6 +66,14 @@ func New(runtimeName string) (Runtime, error) { return rt, nil } +// ActivatePythonVenvIfPresent activates a Python virtual environment if one +// exists in the given project directory. This sets VIRTUAL_ENV, prepends the +// venv bin directory to PATH, and unsets PYTHONHOME on the current process so +// that child processes (hook scripts) inherit the activated venv. +func ActivatePythonVenvIfPresent(fs afero.Fs, projectDir string) error { + return python.ActivateVenvIfPresent(fs, projectDir) +} + // NewDetectProject returns a new Runtime based on the project type of dirPath func NewDetectProject(ctx context.Context, fs afero.Fs, dirPath string, sdkConfig hooks.SDKCLIConfig) (Runtime, error) { var rt Runtime diff --git a/internal/shared/clients.go b/internal/shared/clients.go index ee51b049..122a44f5 100644 --- a/internal/shared/clients.go +++ b/internal/shared/clients.go @@ -224,6 +224,12 @@ func (c *ClientFactory) InitSDKConfig(ctx context.Context, dirPath string) error // Move upward one directory level dirPath = filepath.Dir(dirPath) } + // Activate Python virtual environment if present, so hook scripts + // can resolve the venv's Python and installed packages. + if err := runtime.ActivatePythonVenvIfPresent(c.Fs, dirPath); err != nil { + c.IO.PrintDebug(ctx, "failed to activate Python virtual environment: %s", err) + } + configFileBytes, err := afero.ReadFile(c.Fs, hooksJSONFilePath) if err != nil { return err // Fixes regression: do not wrap this error, so that the caller can use `os.IsNotExists` From adbda1b5e1ad95049765d102d691fa93c4c449b0 Mon Sep 17 00:00:00 2001 From: Michael Brooks Date: Fri, 20 Feb 2026 17:11:56 -0800 Subject: [PATCH 2/6] feat(python): log debug message when venv is activated Return a boolean from ActivateVenvIfPresent to indicate whether activation occurred, and log a debug message on success. --- internal/runtime/python/python.go | 10 +++++----- internal/runtime/python/python_test.go | 3 ++- internal/runtime/runtime.go | 2 +- internal/shared/clients.go | 4 +++- 4 files changed, 11 insertions(+), 8 deletions(-) diff --git a/internal/runtime/python/python.go b/internal/runtime/python/python.go index 62b4585c..8a2fd93d 100644 --- a/internal/runtime/python/python.go +++ b/internal/runtime/python/python.go @@ -95,23 +95,23 @@ func getVenvBinDir(venvPath string) string { // the same approach. Sourcing the shell-specific activate script (activate, // activate.fish, Activate.ps1) would be higher maintenance because it varies // by shell. -func ActivateVenvIfPresent(fs afero.Fs, projectDir string) error { +func ActivateVenvIfPresent(fs afero.Fs, projectDir string) (bool, error) { venvPath := getVenvPath(projectDir) if !venvExists(fs, venvPath) { - return nil + return false, nil } binDir := getVenvBinDir(venvPath) if err := os.Setenv("VIRTUAL_ENV", venvPath); err != nil { - return err + return false, err } if err := os.Setenv("PATH", binDir+string(filepath.ListSeparator)+os.Getenv("PATH")); err != nil { - return err + return false, err } os.Unsetenv("PYTHONHOME") - return nil + return true, nil } // getPipExecutable returns the path to the pip executable in the virtual environment diff --git a/internal/runtime/python/python_test.go b/internal/runtime/python/python_test.go index 20e90222..b5c8c4b5 100644 --- a/internal/runtime/python/python_test.go +++ b/internal/runtime/python/python_test.go @@ -360,8 +360,9 @@ func Test_ActivateVenvIfPresent(t *testing.T) { require.NoError(t, err) } - err := ActivateVenvIfPresent(fs, projectDir) + activated, err := ActivateVenvIfPresent(fs, projectDir) require.NoError(t, err) + require.Equal(t, tc.expectedActivated, activated) if tc.expectedActivated { require.Equal(t, venvPath, os.Getenv("VIRTUAL_ENV")) diff --git a/internal/runtime/runtime.go b/internal/runtime/runtime.go index cfa5adc0..a011200f 100644 --- a/internal/runtime/runtime.go +++ b/internal/runtime/runtime.go @@ -70,7 +70,7 @@ func New(runtimeName string) (Runtime, error) { // exists in the given project directory. This sets VIRTUAL_ENV, prepends the // venv bin directory to PATH, and unsets PYTHONHOME on the current process so // that child processes (hook scripts) inherit the activated venv. -func ActivatePythonVenvIfPresent(fs afero.Fs, projectDir string) error { +func ActivatePythonVenvIfPresent(fs afero.Fs, projectDir string) (bool, error) { return python.ActivateVenvIfPresent(fs, projectDir) } diff --git a/internal/shared/clients.go b/internal/shared/clients.go index 122a44f5..1b393de1 100644 --- a/internal/shared/clients.go +++ b/internal/shared/clients.go @@ -226,8 +226,10 @@ func (c *ClientFactory) InitSDKConfig(ctx context.Context, dirPath string) error } // Activate Python virtual environment if present, so hook scripts // can resolve the venv's Python and installed packages. - if err := runtime.ActivatePythonVenvIfPresent(c.Fs, dirPath); err != nil { + if activated, err := runtime.ActivatePythonVenvIfPresent(c.Fs, dirPath); err != nil { c.IO.PrintDebug(ctx, "failed to activate Python virtual environment: %s", err) + } else if activated { + c.IO.PrintDebug(ctx, "Activated Python virtual environment .venv") } configFileBytes, err := afero.ReadFile(c.Fs, hooksJSONFilePath) From 91a07bf51d1685edf2e03de649a62ec128fc0514 Mon Sep 17 00:00:00 2001 From: Michael Brooks Date: Fri, 20 Feb 2026 18:40:16 -0800 Subject: [PATCH 3/6] refactor(python): use Os interface in ActivateVenvIfPresent for testability Add Unsetenv to the Os interface/implementation/mock and refactor ActivateVenvIfPresent to accept types.Os instead of calling the os package directly. This allows the test to use mocks instead of mutating the real process environment. --- internal/runtime/python/python.go | 11 ++++++----- internal/runtime/python/python_test.go | 25 ++++++++++--------------- internal/runtime/runtime.go | 4 ++-- internal/shared/clients.go | 2 +- internal/shared/types/slackdeps.go | 3 +++ internal/slackdeps/os.go | 5 +++++ internal/slackdeps/os_mock.go | 9 +++++++++ 7 files changed, 36 insertions(+), 23 deletions(-) diff --git a/internal/runtime/python/python.go b/internal/runtime/python/python.go index 8a2fd93d..fb82e116 100644 --- a/internal/runtime/python/python.go +++ b/internal/runtime/python/python.go @@ -19,7 +19,6 @@ import ( "context" _ "embed" "fmt" - "os" "path/filepath" "regexp" "runtime" @@ -95,7 +94,7 @@ func getVenvBinDir(venvPath string) string { // the same approach. Sourcing the shell-specific activate script (activate, // activate.fish, Activate.ps1) would be higher maintenance because it varies // by shell. -func ActivateVenvIfPresent(fs afero.Fs, projectDir string) (bool, error) { +func ActivateVenvIfPresent(fs afero.Fs, osClient types.Os, projectDir string) (bool, error) { venvPath := getVenvPath(projectDir) if !venvExists(fs, venvPath) { return false, nil @@ -103,13 +102,15 @@ func ActivateVenvIfPresent(fs afero.Fs, projectDir string) (bool, error) { binDir := getVenvBinDir(venvPath) - if err := os.Setenv("VIRTUAL_ENV", venvPath); err != nil { + if err := osClient.Setenv("VIRTUAL_ENV", venvPath); err != nil { return false, err } - if err := os.Setenv("PATH", binDir+string(filepath.ListSeparator)+os.Getenv("PATH")); err != nil { + if err := osClient.Setenv("PATH", binDir+string(filepath.ListSeparator)+osClient.Getenv("PATH")); err != nil { + return false, err + } + if err := osClient.Unsetenv("PYTHONHOME"); err != nil { return false, err } - os.Unsetenv("PYTHONHOME") return true, nil } diff --git a/internal/runtime/python/python_test.go b/internal/runtime/python/python_test.go index b5c8c4b5..070fe1bf 100644 --- a/internal/runtime/python/python_test.go +++ b/internal/runtime/python/python_test.go @@ -17,7 +17,6 @@ package python import ( "encoding/json" "fmt" - "os" "path/filepath" "runtime" "testing" @@ -342,14 +341,13 @@ func Test_ActivateVenvIfPresent(t *testing.T) { for name, tc := range tests { t.Run(name, func(t *testing.T) { fs := slackdeps.NewFsMock() + osMock := slackdeps.NewOsMock() projectDir := "/path/to/project" venvPath := filepath.Join(projectDir, ".venv") - // Save and restore env vars - t.Setenv("VIRTUAL_ENV", "") - t.Setenv("PYTHONHOME", "original-pythonhome") - originalPath := os.Getenv("PATH") - t.Setenv("PATH", originalPath) + originalPath := "/usr/bin:/bin" + osMock.On("Getenv", "PATH").Return(originalPath) + osMock.AddDefaultMocks() if tc.createVenv { // Create the pip executable so venvExists returns true @@ -360,21 +358,18 @@ func Test_ActivateVenvIfPresent(t *testing.T) { require.NoError(t, err) } - activated, err := ActivateVenvIfPresent(fs, projectDir) + activated, err := ActivateVenvIfPresent(fs, osMock, projectDir) require.NoError(t, err) require.Equal(t, tc.expectedActivated, activated) if tc.expectedActivated { - require.Equal(t, venvPath, os.Getenv("VIRTUAL_ENV")) expectedBinDir := getVenvBinDir(venvPath) - require.Contains(t, os.Getenv("PATH"), expectedBinDir+string(filepath.ListSeparator)) - // PYTHONHOME should be unset (empty) - _, pythonHomeSet := os.LookupEnv("PYTHONHOME") - require.False(t, pythonHomeSet) + osMock.AssertCalled(t, "Setenv", "VIRTUAL_ENV", venvPath) + osMock.AssertCalled(t, "Setenv", "PATH", expectedBinDir+string(filepath.ListSeparator)+originalPath) + osMock.AssertCalled(t, "Unsetenv", "PYTHONHOME") } else { - require.Equal(t, "", os.Getenv("VIRTUAL_ENV")) - require.Equal(t, originalPath, os.Getenv("PATH")) - require.Equal(t, "original-pythonhome", os.Getenv("PYTHONHOME")) + osMock.AssertNotCalled(t, "Setenv", mock.Anything, mock.Anything) + osMock.AssertNotCalled(t, "Unsetenv", mock.Anything) } }) } diff --git a/internal/runtime/runtime.go b/internal/runtime/runtime.go index a011200f..e6098245 100644 --- a/internal/runtime/runtime.go +++ b/internal/runtime/runtime.go @@ -70,8 +70,8 @@ func New(runtimeName string) (Runtime, error) { // exists in the given project directory. This sets VIRTUAL_ENV, prepends the // venv bin directory to PATH, and unsets PYTHONHOME on the current process so // that child processes (hook scripts) inherit the activated venv. -func ActivatePythonVenvIfPresent(fs afero.Fs, projectDir string) (bool, error) { - return python.ActivateVenvIfPresent(fs, projectDir) +func ActivatePythonVenvIfPresent(fs afero.Fs, os types.Os, projectDir string) (bool, error) { + return python.ActivateVenvIfPresent(fs, os, projectDir) } // NewDetectProject returns a new Runtime based on the project type of dirPath diff --git a/internal/shared/clients.go b/internal/shared/clients.go index 1b393de1..55f2d3c9 100644 --- a/internal/shared/clients.go +++ b/internal/shared/clients.go @@ -226,7 +226,7 @@ func (c *ClientFactory) InitSDKConfig(ctx context.Context, dirPath string) error } // Activate Python virtual environment if present, so hook scripts // can resolve the venv's Python and installed packages. - if activated, err := runtime.ActivatePythonVenvIfPresent(c.Fs, dirPath); err != nil { + if activated, err := runtime.ActivatePythonVenvIfPresent(c.Fs, c.Os, dirPath); err != nil { c.IO.PrintDebug(ctx, "failed to activate Python virtual environment: %s", err) } else if activated { c.IO.PrintDebug(ctx, "Activated Python virtual environment .venv") diff --git a/internal/shared/types/slackdeps.go b/internal/shared/types/slackdeps.go index f2dd1ae0..858c5201 100644 --- a/internal/shared/types/slackdeps.go +++ b/internal/shared/types/slackdeps.go @@ -38,6 +38,9 @@ type Os interface { // Setenv defaults to `os.Setenv` and can be mocked to test Setenv(key string, value string) error + // Unsetenv defaults to `os.Unsetenv` and can be mocked to test + Unsetenv(key string) error + // Getwd defaults to `os.Getwd` and can be mocked to test Getwd() (dir string, err error) diff --git a/internal/slackdeps/os.go b/internal/slackdeps/os.go index d61c3f1b..4a353235 100644 --- a/internal/slackdeps/os.go +++ b/internal/slackdeps/os.go @@ -53,6 +53,11 @@ func (c *Os) Setenv(key string, value string) error { return os.Setenv(key, value) } +// Unsetenv defaults to `os.Unsetenv` and can be mocked to test +func (c *Os) Unsetenv(key string) error { + return os.Unsetenv(key) +} + // Getwd defaults to `os.Getwd` and can be mocked to test func (c *Os) Getwd() (dir string, err error) { return os.Getwd() diff --git a/internal/slackdeps/os_mock.go b/internal/slackdeps/os_mock.go index ec19f156..f8f1620f 100644 --- a/internal/slackdeps/os_mock.go +++ b/internal/slackdeps/os_mock.go @@ -46,6 +46,7 @@ func (m *OsMock) AddDefaultMocks() { m.On("LookPath", mock.Anything).Return("", nil) m.On("LookupEnv", mock.Anything).Return("", false) m.On("Setenv", mock.Anything, mock.Anything).Return(nil) + m.On("Unsetenv", mock.Anything).Return(nil) m.On("Getwd").Return(MockWorkingDirectory, nil) m.On("UserHomeDir").Return(MockHomeDirectory, nil) m.On("GetExecutionDir").Return(MockHomeDirectory, nil) @@ -83,6 +84,14 @@ func (m *OsMock) Setenv(key string, value string) error { return args.Error(0) } +// Unsetenv mocks the unsetting of an environment variable +func (m *OsMock) Unsetenv(key string) error { + m.On("Getenv", key).Return("") + m.On("LookupEnv", key).Return("", false) + args := m.Called(key) + return args.Error(0) +} + // Getwd mocks returning the working directory. func (m *OsMock) Getwd() (_dir string, _err error) { args := m.Called() From b75edb94b721d8533b1c38ef96fc767d2cacd9c2 Mon Sep 17 00:00:00 2001 From: Michael Brooks Date: Fri, 20 Feb 2026 18:51:52 -0800 Subject: [PATCH 4/6] test: add tests for ActivatePythonVenvIfPresent in runtime package --- internal/runtime/runtime_test.go | 53 ++++++++++++++++++++++++++++++++ 1 file changed, 53 insertions(+) diff --git a/internal/runtime/runtime_test.go b/internal/runtime/runtime_test.go index 932c1698..dc032586 100644 --- a/internal/runtime/runtime_test.go +++ b/internal/runtime/runtime_test.go @@ -15,6 +15,7 @@ package runtime import ( + "path/filepath" "testing" "github.com/slackapi/slack-cli/internal/hooks" @@ -22,7 +23,9 @@ import ( "github.com/slackapi/slack-cli/internal/runtime/node" "github.com/slackapi/slack-cli/internal/runtime/python" "github.com/slackapi/slack-cli/internal/slackcontext" + "github.com/slackapi/slack-cli/internal/slackdeps" "github.com/spf13/afero" + "github.com/stretchr/testify/mock" "github.com/stretchr/testify/require" ) @@ -57,6 +60,56 @@ func Test_Runtime_New(t *testing.T) { } } +func Test_ActivatePythonVenvIfPresent(t *testing.T) { + tests := map[string]struct { + createVenv bool + expectedActivated bool + }{ + "activates venv when it exists": { + createVenv: true, + expectedActivated: true, + }, + "no-op when venv does not exist": { + createVenv: false, + expectedActivated: false, + }, + } + for name, tc := range tests { + t.Run(name, func(t *testing.T) { + fs := slackdeps.NewFsMock() + osMock := slackdeps.NewOsMock() + projectDir := "/path/to/project" + venvPath := filepath.Join(projectDir, ".venv") + + osMock.On("Getenv", "PATH").Return("/usr/bin:/bin") + osMock.AddDefaultMocks() + + if tc.createVenv { + // Create the pip executable so venvExists returns true + var pipPath string + pipPath = filepath.Join(venvPath, "bin", "pip") + err := fs.MkdirAll(filepath.Dir(pipPath), 0755) + require.NoError(t, err) + err = afero.WriteFile(fs, pipPath, []byte(""), 0755) + require.NoError(t, err) + } + + activated, err := ActivatePythonVenvIfPresent(fs, osMock, projectDir) + require.NoError(t, err) + require.Equal(t, tc.expectedActivated, activated) + + if tc.expectedActivated { + osMock.AssertCalled(t, "Setenv", "VIRTUAL_ENV", venvPath) + osMock.AssertCalled(t, "Setenv", "PATH", mock.Anything) + osMock.AssertCalled(t, "Unsetenv", "PYTHONHOME") + } else { + osMock.AssertNotCalled(t, "Setenv", mock.Anything, mock.Anything) + osMock.AssertNotCalled(t, "Unsetenv", mock.Anything) + } + }) + } +} + func Test_Runtime_NewDetectProject(t *testing.T) { tests := map[string]struct { sdkConfig hooks.SDKCLIConfig From ce152972835b6cb9c56549d2a3d9c2eb886ac8f6 Mon Sep 17 00:00:00 2001 From: Michael Brooks Date: Fri, 20 Feb 2026 18:54:45 -0800 Subject: [PATCH 5/6] test: remove test comment --- internal/runtime/python/python_test.go | 4 ---- 1 file changed, 4 deletions(-) diff --git a/internal/runtime/python/python_test.go b/internal/runtime/python/python_test.go index 070fe1bf..4f6355ff 100644 --- a/internal/runtime/python/python_test.go +++ b/internal/runtime/python/python_test.go @@ -375,10 +375,6 @@ func Test_ActivateVenvIfPresent(t *testing.T) { } } -// Test_Python_InstallProjectDependencies tests the main function -// NOTE: These tests require Python 3 and pip to be installed for full integration testing. -// The function creates real virtual environments and runs real pip commands. -// Unit tests below focus on testing individual components. func Test_Python_InstallProjectDependencies(t *testing.T) { tests := map[string]struct { existingFiles map[string]string From f8755c9c6208da4f6578a195dfdbb0ef809b5b77 Mon Sep 17 00:00:00 2001 From: Michael Brooks Date: Fri, 20 Feb 2026 18:56:03 -0800 Subject: [PATCH 6/6] fix: merge variable declaration with assignment to satisfy linter --- internal/runtime/runtime_test.go | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/internal/runtime/runtime_test.go b/internal/runtime/runtime_test.go index dc032586..8a2c29a2 100644 --- a/internal/runtime/runtime_test.go +++ b/internal/runtime/runtime_test.go @@ -86,8 +86,7 @@ func Test_ActivatePythonVenvIfPresent(t *testing.T) { if tc.createVenv { // Create the pip executable so venvExists returns true - var pipPath string - pipPath = filepath.Join(venvPath, "bin", "pip") + pipPath := filepath.Join(venvPath, "bin", "pip") err := fs.MkdirAll(filepath.Dir(pipPath), 0755) require.NoError(t, err) err = afero.WriteFile(fs, pipPath, []byte(""), 0755)