diff --git a/coordinator/cmd/coordinator/agent_cmd.go b/coordinator/cmd/coordinator/agent_cmd.go index eb6edc3..027eda9 100644 --- a/coordinator/cmd/coordinator/agent_cmd.go +++ b/coordinator/cmd/coordinator/agent_cmd.go @@ -45,7 +45,7 @@ func runAgent(args []string) error { return fmt.Errorf("--coordinator-url, --token, and --work-dir are required") } if *taskRunner == "" { - *taskRunner = "python -m scimesh.worker.task" + *taskRunner = "python -I -m scimesh.worker.task" } logger := slog.New(slog.NewTextHandler(os.Stderr, nil)) diff --git a/coordinator/cmd/coordinator/serve_cmd.go b/coordinator/cmd/coordinator/serve_cmd.go index 9cefe73..1478930 100644 --- a/coordinator/cmd/coordinator/serve_cmd.go +++ b/coordinator/cmd/coordinator/serve_cmd.go @@ -236,9 +236,9 @@ func stopAgents(agents []*exec.Cmd) { // system `python`. func defaultTaskRunner(venvPython string) string { if runtimeStatus(venvPython) { - return venvPython + " -m scimesh.worker.task" + return venvPython + " -I -m scimesh.worker.task" } - return "python -m scimesh.worker.task" + return "python -I -m scimesh.worker.task" } // ensureRuntime creates the managed venv and installs scimesh into it, unless diff --git a/coordinator/internal/agent/check.go b/coordinator/internal/agent/check.go index c4c6d38..306d9b9 100644 --- a/coordinator/internal/agent/check.go +++ b/coordinator/internal/agent/check.go @@ -89,9 +89,11 @@ func CheckEnvironment(ctx context.Context) CheckReport { func CheckEnvironmentWithPython(ctx context.Context, python string) CheckReport { report := CheckReport{Agent: Version, Python: CheckItem{Name: "python", OK: true, Detail: python}} // The version comes from importlib.metadata, so the wizard can compare the - // installed package with the binary version and offer an upgrade. + // installed package with the binary version and offer an upgrade. -I keeps + // the working directory out of sys.path, so a scimesh checkout in the + // wizard's cwd can never shadow the venv installation. //nolint:gosec // G204: python is a resolved interpreter path, the argument list is constant - cmd := exec.CommandContext(ctx, python, "-c", "import importlib.metadata as m; print(m.version('scimesh'))") + cmd := exec.CommandContext(ctx, python, "-I", "-c", "import importlib.metadata as m; print(m.version('scimesh'))") out, err := cmd.Output() if err != nil { // The worker executes workloads by spawning scimesh's task runner, so diff --git a/coordinator/internal/agent/config.go b/coordinator/internal/agent/config.go index 4b03ab4..b6900f0 100644 --- a/coordinator/internal/agent/config.go +++ b/coordinator/internal/agent/config.go @@ -112,7 +112,7 @@ func LoadConfig() (*Config, error) { return nil, err } if len(runner) == 0 { - runner = []string{"python", "-m", "scimesh.worker.task"} + runner = []string{"python", "-I", "-m", "scimesh.worker.task"} } maxTasks := 0 if raw := os.Getenv("MAX_TASKS"); raw != "" { diff --git a/coordinator/internal/agent/configfile.go b/coordinator/internal/agent/configfile.go index 2a39b7b..c71c653 100644 --- a/coordinator/internal/agent/configfile.go +++ b/coordinator/internal/agent/configfile.go @@ -112,7 +112,7 @@ func (f *ConfigFile) Config() (*Config, error) { config.TaskRunner = f.TaskRunner } if len(config.TaskRunner) == 0 { - config.TaskRunner = []string{"python", "-m", "scimesh.worker.task"} + config.TaskRunner = []string{"python", "-I", "-m", "scimesh.worker.task"} } config.PollInterval = 2 * time.Second config.RequestTimeout = 30 * time.Second diff --git a/coordinator/internal/agent/setupui/server.go b/coordinator/internal/agent/setupui/server.go index 0606ceb..a1e1bd8 100644 --- a/coordinator/internal/agent/setupui/server.go +++ b/coordinator/internal/agent/setupui/server.go @@ -323,7 +323,7 @@ func (s *Server) ensureVenvTaskRunner() { return } if venv := s.venvPython(); venv != "" { - file.TaskRunner = []string{venv, "-m", "scimesh.worker.task"} + file.TaskRunner = []string{venv, "-I", "-m", "scimesh.worker.task"} if payload, err := json.MarshalIndent(file, "", " "); err == nil { _ = os.WriteFile(s.cfgPath, append(payload, '\n'), 0o600) } @@ -428,7 +428,7 @@ func (s *Server) handleSaveConfig(w http.ResponseWriter, r *http.Request) { // workloads execute through scimesh's task runner, which lives in the venv. if len(file.TaskRunner) == 0 { if venv := s.venvPython(); venv != "" { - file.TaskRunner = []string{venv, "-m", "scimesh.worker.task"} + file.TaskRunner = []string{venv, "-I", "-m", "scimesh.worker.task"} } } if err := agent.SaveConfigFile(s.cfgPath, file); err != nil { diff --git a/coordinator/internal/agent/setupui/server_test.go b/coordinator/internal/agent/setupui/server_test.go index d3103e1..d8a7368 100644 --- a/coordinator/internal/agent/setupui/server_test.go +++ b/coordinator/internal/agent/setupui/server_test.go @@ -429,8 +429,8 @@ func TestStartPinsTheVenvTaskRunner(t *testing.T) { if err != nil { t.Fatal(err) } - if len(config.TaskRunner) != 3 || config.TaskRunner[0] != venvPython || config.TaskRunner[1] != "-m" || config.TaskRunner[2] != "scimesh.worker.task" { - t.Errorf("task runner = %v, want the venv python runner", config.TaskRunner) + if len(config.TaskRunner) != 4 || config.TaskRunner[0] != venvPython || config.TaskRunner[1] != "-I" || config.TaskRunner[2] != "-m" || config.TaskRunner[3] != "scimesh.worker.task" { + t.Errorf("task runner = %v, want the venv python runner with -I", config.TaskRunner) } } @@ -450,8 +450,8 @@ func TestSaveConfigPinsVenvRunnerWhenPresent(t *testing.T) { if err != nil { t.Fatal(err) } - if len(config.TaskRunner) != 3 || config.TaskRunner[0] != venvPython { - t.Errorf("task runner = %v, want the venv python", config.TaskRunner) + if len(config.TaskRunner) != 4 || config.TaskRunner[0] != venvPython || config.TaskRunner[1] != "-I" { + t.Errorf("task runner = %v, want the venv python with -I", config.TaskRunner) } } @@ -462,7 +462,7 @@ func TestTestProbesTheVenvPythonAfterInstall(t *testing.T) { // a fake scimesh version so the preflight goes green through the venv. venvPython := filepath.Join(server.dir, "venv", "bin", "python") _ = os.MkdirAll(filepath.Dir(venvPython), 0o755) - _ = os.WriteFile(venvPython, []byte("#!/bin/sh\nif [ \"$1\" = \"-c\" ]; then echo 9.9.9-test; exit 0; fi\nexit 0\n"), 0o755) + _ = os.WriteFile(venvPython, []byte("#!/bin/sh\nfor a in \"$@\"; do if [ \"$a\" = \"-c\" ]; then echo 9.9.9-test; exit 0; fi; done\nexit 0\n"), 0o755) req, _ := http.NewRequestWithContext(context.Background(), http.MethodPost, base+"/api/test", strings.NewReader(`{"coordinator_url":"http://127.0.0.1:1"}`)) req.Header.Set("Content-Type", "application/json") diff --git a/coordinator/internal/agent/setupui/template.html b/coordinator/internal/agent/setupui/template.html index 3cc4fae..50f60aa 100644 --- a/coordinator/internal/agent/setupui/template.html +++ b/coordinator/internal/agent/setupui/template.html @@ -232,7 +232,7 @@ function draftConfig(){ cpu_count:state.cpu==='custom'?parseInt($('in-cpu').value||'1',10):0, concurrency:parseInt($('in-conc').value||'1',10) }; - if(state.venvPython)cfg.task_runner=[state.venvPython,'-m','scimesh.worker.task']; + if(state.venvPython)cfg.task_runner=[state.venvPython,'-I','-m','scimesh.worker.task']; return cfg; }