Warn in the wizard preflight when the installed scimesh version mismatches the binary

This commit is contained in:
Emil
2026-08-03 16:35:37 +03:00
parent ff1fc25d77
commit dc15e2d04b
3 changed files with 60 additions and 1 deletions
+3 -1
View File
@@ -88,8 +88,10 @@ func CheckEnvironment(ctx context.Context) CheckReport {
// execute with.
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.
//nolint:gosec // G204: python is a resolved interpreter path, the argument list is constant
cmd := exec.CommandContext(ctx, python, "-c", "import scimesh; print(scimesh.__version__ if hasattr(scimesh, '__version__') else 'installed')")
cmd := exec.CommandContext(ctx, python, "-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
@@ -454,6 +454,9 @@ func (s *Server) handleTest(w http.ResponseWriter, r *http.Request) {
// checking the bare system python3 would keep reporting scimesh as
// missing even though the worker would run with the venv.
report := agent.RunCheck(r.Context(), url, s.venvPython(), req.Token, req.WorkerKey, req.UserserviceURL)
if report.Scimesh.OK {
report.Scimesh = ensureMatchingScimeshVersion(report.Scimesh)
}
writeJSON(w, http.StatusOK, report)
}
@@ -628,3 +631,26 @@ func truncate(s string, n int) string {
}
return s[:n] + "…"
}
// ensureMatchingScimeshVersion flips a green scimesh check to a stale one when
// the installed package does not match the worker-agent's own version: a
// version-locked wheel is the only supported runtime, and a mismatch means the
// workload catalog the worker advertises is not what it executes. The wizard
// UI then offers the Install button again. Dev builds have no release wheel,
// so they skip the comparison.
func ensureMatchingScimeshVersion(item agent.CheckItem) agent.CheckItem {
if agent.Version == "" || agent.Version == "dev" {
return item
}
want := agent.NormalizePEP440(agent.Version)
got := strings.TrimSpace(item.Detail)
if got == "" || got == want {
return item
}
item.OK = false
item.Detail = fmt.Sprintf(
"installed scimesh %s, but this worker-agent (%s) needs %s — press Install to upgrade",
got, agent.Version, want,
)
return item
}
@@ -497,3 +497,34 @@ time=6 level=WARN msg="agent cycle failed" error="boom"
t.Errorf("stats = %+v, want registered claimed=2 completed=1 failed=1", stats)
}
}
func writeVenvStub(t *testing.T, server *Server, version string) string {
t.Helper()
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 "+version+"; exit 0; fi\nexit 0\n"), 0o755)
return venvPython
}
func testCheckScimeshVersion(t *testing.T, installed, binary string, wantOK bool, wantDetail string) {
t.Helper()
old := agent.Version
agent.Version = binary
t.Cleanup(func() { agent.Version = old })
item := ensureMatchingScimeshVersion(agent.CheckItem{Name: "scimesh", OK: true, Detail: installed})
if item.OK != wantOK {
t.Errorf("installed=%s binary=%s: ok=%v, want %v (%s)", installed, binary, item.OK, wantOK, item.Detail)
}
if wantDetail != "" && !strings.Contains(item.Detail, wantDetail) {
t.Errorf("detail = %q, want it to contain %q", item.Detail, wantDetail)
}
}
func TestEnsureMatchingScimeshVersion(t *testing.T) {
testCheckScimeshVersion(t, "1.1.0a20", "1.1.0-alpha.20", true, "")
testCheckScimeshVersion(t, "1.1.0a17", "1.1.0-alpha.20", false, "press Install to upgrade")
testCheckScimeshVersion(t, "1.1.0a16.dev7+gea0fb8c59.d20260803", "1.1.0-alpha.20", false, "needs 1.1.0a20")
// Dev builds and unknown versions never block.
testCheckScimeshVersion(t, "anything", "dev", true, "")
testCheckScimeshVersion(t, "1.1.0a20", "", true, "")
}