From f9ae28e854e943088c5122a7f4b1157c4037cb80 Mon Sep 17 00:00:00 2001 From: Taras Yemets Date: Fri, 29 May 2026 13:47:03 +0300 Subject: [PATCH 1/3] fix/batch-changes: ignore per-job env vars in cache key The two SRC_BATCHES_* env vars declared by codingAgent steps (see sourcegraph/sourcegraph#12503) resolve to different values on every dequeue. Strip them from cache-key inputs so the key stays stable across runs. --- lib/batches/execution/cache/cache.go | 10 ++++++++ lib/batches/execution/cache/cache_test.go | 30 +++++++++++++++++++++++ 2 files changed, 40 insertions(+) create mode 100644 lib/batches/execution/cache/cache_test.go diff --git a/lib/batches/execution/cache/cache.go b/lib/batches/execution/cache/cache.go index 9392cd2c31..672b04bab4 100644 --- a/lib/batches/execution/cache/cache.go +++ b/lib/batches/execution/cache/cache.go @@ -47,6 +47,13 @@ func (key CacheKey) mountsMetadata() ([]MountMetadata, error) { return nil, nil } +// perJobEnvVars resolve to per-job values and must be stripped from +// cache keys. Mirrored in sourcegraph/sourcegraph/lib/batches/execution/cache/cache.go. +var perJobEnvVars = []string{ + "SRC_BATCHES_MODEL_PROVIDER_TOKEN", + "SRC_BATCHES_JOB_ID", +} + // resolveStepsEnvironment returns a slice of environments for each of the steps, // containing only the env vars that are actually used. func resolveStepsEnvironment(globalEnv []string, steps []batches.Step) ([]map[string]string, error) { @@ -64,6 +71,9 @@ func resolveStepsEnvironment(globalEnv []string, steps []batches.Step) ([]map[st if err != nil { return nil, errors.Wrapf(err, "resolving environment for step %d", i) } + for _, name := range perJobEnvVars { + delete(env, name) + } envs[i] = env } return envs, nil diff --git a/lib/batches/execution/cache/cache_test.go b/lib/batches/execution/cache/cache_test.go new file mode 100644 index 0000000000..75e170f7e8 --- /dev/null +++ b/lib/batches/execution/cache/cache_test.go @@ -0,0 +1,30 @@ +package cache + +import ( + "encoding/json" + "testing" + + "github.com/stretchr/testify/require" + + "github.com/sourcegraph/sourcegraph/lib/batches" + "github.com/sourcegraph/sourcegraph/lib/batches/env" +) + +func TestKeyer_Key_PerJobEnvVarsIgnored(t *testing.T) { + var stepEnv env.Environment + require.NoError(t, json.Unmarshal( + []byte(`["SRC_BATCHES_MODEL_PROVIDER_TOKEN", "SRC_BATCHES_JOB_ID"]`), + &stepEnv, + )) + step := batches.Step{Run: "foo", Env: stepEnv} + repo := batches.Repository{ID: "r", Name: "r"} + + unset, err := (&CacheKey{Repository: repo, Steps: []batches.Step{step}, StepIndex: 0}).Key() + require.NoError(t, err) + resolved, err := (&CacheKey{Repository: repo, Steps: []batches.Step{step}, StepIndex: 0, GlobalEnv: []string{ + "SRC_BATCHES_MODEL_PROVIDER_TOKEN=tok", + "SRC_BATCHES_JOB_ID=42", + }}).Key() + require.NoError(t, err) + require.Equal(t, unset, resolved) +} From 1257e39e5c7019ea9665e902b40234646e6b551c Mon Sep 17 00:00:00 2001 From: Taras Yemets Date: Fri, 29 May 2026 15:51:50 +0300 Subject: [PATCH 2/3] fix/batch-changes: rename per-job env vars to SRC_EXECUTOR_* and strip SRC_EXECUTOR_NAME The per-job env vars declared by codingAgent steps were renamed in sourcegraph/sourcegraph#12503 (review feedback) from SRC_BATCHES_* to SRC_EXECUTOR_*, and a third var SRC_EXECUTOR_NAME was added so the in-sandbox coding-agent CLI can pass X-Sourcegraph-Executor-Name to the model-provider proxy (lets the server reuse jobAuthMiddleware instead of a bespoke auth path). Mirror those changes in src-cli's cache-key stripping list so cached runs aren't invalidated by the per-dequeue token/ID/executor-name values that get injected onto step.Env. --- lib/batches/execution/cache/cache.go | 9 +++++---- lib/batches/execution/cache/cache_test.go | 7 ++++--- lib/go.mod | 3 +++ 3 files changed, 12 insertions(+), 7 deletions(-) diff --git a/lib/batches/execution/cache/cache.go b/lib/batches/execution/cache/cache.go index 672b04bab4..734f90a3cd 100644 --- a/lib/batches/execution/cache/cache.go +++ b/lib/batches/execution/cache/cache.go @@ -47,11 +47,12 @@ func (key CacheKey) mountsMetadata() ([]MountMetadata, error) { return nil, nil } -// perJobEnvVars resolve to per-job values and must be stripped from -// cache keys. Mirrored in sourcegraph/sourcegraph/lib/batches/execution/cache/cache.go. +// perJobEnvVars resolve to per-job or per-executor values and must be +// stripped from cache keys. Mirrored in sourcegraph/sourcegraph/lib/batches/execution/cache/cache.go. var perJobEnvVars = []string{ - "SRC_BATCHES_MODEL_PROVIDER_TOKEN", - "SRC_BATCHES_JOB_ID", + "SRC_EXECUTOR_JOB_TOKEN", + "SRC_EXECUTOR_JOB_ID", + "SRC_EXECUTOR_NAME", } // resolveStepsEnvironment returns a slice of environments for each of the steps, diff --git a/lib/batches/execution/cache/cache_test.go b/lib/batches/execution/cache/cache_test.go index 75e170f7e8..55deb4cd4e 100644 --- a/lib/batches/execution/cache/cache_test.go +++ b/lib/batches/execution/cache/cache_test.go @@ -13,7 +13,7 @@ import ( func TestKeyer_Key_PerJobEnvVarsIgnored(t *testing.T) { var stepEnv env.Environment require.NoError(t, json.Unmarshal( - []byte(`["SRC_BATCHES_MODEL_PROVIDER_TOKEN", "SRC_BATCHES_JOB_ID"]`), + []byte(`["SRC_EXECUTOR_JOB_TOKEN", "SRC_EXECUTOR_JOB_ID", "SRC_EXECUTOR_NAME"]`), &stepEnv, )) step := batches.Step{Run: "foo", Env: stepEnv} @@ -22,8 +22,9 @@ func TestKeyer_Key_PerJobEnvVarsIgnored(t *testing.T) { unset, err := (&CacheKey{Repository: repo, Steps: []batches.Step{step}, StepIndex: 0}).Key() require.NoError(t, err) resolved, err := (&CacheKey{Repository: repo, Steps: []batches.Step{step}, StepIndex: 0, GlobalEnv: []string{ - "SRC_BATCHES_MODEL_PROVIDER_TOKEN=tok", - "SRC_BATCHES_JOB_ID=42", + "SRC_EXECUTOR_JOB_TOKEN=tok", + "SRC_EXECUTOR_JOB_ID=42", + "SRC_EXECUTOR_NAME=executor-abc", }}).Key() require.NoError(t, err) require.Equal(t, unset, resolved) diff --git a/lib/go.mod b/lib/go.mod index 44fdc19356..00f3d2d6df 100644 --- a/lib/go.mod +++ b/lib/go.mod @@ -22,6 +22,7 @@ require ( github.com/sourcegraph/conc v0.3.0 github.com/sourcegraph/go-diff v0.7.0 github.com/sourcegraph/log v0.0.0-20250923023806-517b6960b55b + github.com/stretchr/testify v1.11.1 github.com/urfave/cli/v3 v3.8.0 github.com/xeipuuv/gojsonschema v1.2.0 github.com/xlab/treeprint v1.2.0 @@ -46,6 +47,7 @@ require ( github.com/charmbracelet/x/term v0.2.1 // indirect github.com/clipperhouse/uax29/v2 v2.2.0 // indirect github.com/cockroachdb/logtags v0.0.0-20230118201751-21c54148d20b // indirect + github.com/davecgh/go-spew v1.1.1 // indirect github.com/dlclark/regexp2 v1.11.0 // indirect github.com/fatih/color v1.18.0 // indirect github.com/getsentry/sentry-go v0.27.0 // indirect @@ -64,6 +66,7 @@ require ( github.com/microcosm-cc/bluemonday v1.0.27 // indirect github.com/muesli/reflow v0.3.0 // indirect github.com/pkg/errors v0.9.1 // indirect + github.com/pmezard/go-difflib v1.0.0 // indirect github.com/rivo/uniseg v0.4.7 // indirect github.com/rogpeppe/go-internal v1.14.1 // indirect github.com/sourcegraph/beaut v0.0.0-20240611013027-627e4c25335a // indirect From ee8d1982697426b3326981890fa0c71d0764825b Mon Sep 17 00:00:00 2001 From: Taras Yemets Date: Fri, 29 May 2026 15:56:33 +0300 Subject: [PATCH 3/3] fix/batch-changes: rename perJobEnvVars to perRunEnvVars One of the three stripped env vars (SRC_EXECUTOR_NAME) is per-executor, not per-job, so the perJob name is technically wrong. Renaming to perRunEnvVars accurately captures that all three change on every dequeue (job-token + job-ID per job, executor-name per executor). --- lib/batches/execution/cache/cache.go | 9 +++++---- lib/batches/execution/cache/cache_test.go | 2 +- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/lib/batches/execution/cache/cache.go b/lib/batches/execution/cache/cache.go index 734f90a3cd..f77d5ab92d 100644 --- a/lib/batches/execution/cache/cache.go +++ b/lib/batches/execution/cache/cache.go @@ -47,9 +47,10 @@ func (key CacheKey) mountsMetadata() ([]MountMetadata, error) { return nil, nil } -// perJobEnvVars resolve to per-job or per-executor values and must be -// stripped from cache keys. Mirrored in sourcegraph/sourcegraph/lib/batches/execution/cache/cache.go. -var perJobEnvVars = []string{ +// perRunEnvVars resolve to per-job or per-executor values that change on +// every dequeue and must be stripped from cache keys. Mirrored in +// sourcegraph/sourcegraph/lib/batches/execution/cache/cache.go. +var perRunEnvVars = []string{ "SRC_EXECUTOR_JOB_TOKEN", "SRC_EXECUTOR_JOB_ID", "SRC_EXECUTOR_NAME", @@ -72,7 +73,7 @@ func resolveStepsEnvironment(globalEnv []string, steps []batches.Step) ([]map[st if err != nil { return nil, errors.Wrapf(err, "resolving environment for step %d", i) } - for _, name := range perJobEnvVars { + for _, name := range perRunEnvVars { delete(env, name) } envs[i] = env diff --git a/lib/batches/execution/cache/cache_test.go b/lib/batches/execution/cache/cache_test.go index 55deb4cd4e..c416fe024f 100644 --- a/lib/batches/execution/cache/cache_test.go +++ b/lib/batches/execution/cache/cache_test.go @@ -10,7 +10,7 @@ import ( "github.com/sourcegraph/sourcegraph/lib/batches/env" ) -func TestKeyer_Key_PerJobEnvVarsIgnored(t *testing.T) { +func TestKeyer_Key_PerRunEnvVarsIgnored(t *testing.T) { var stepEnv env.Environment require.NoError(t, json.Unmarshal( []byte(`["SRC_EXECUTOR_JOB_TOKEN", "SRC_EXECUTOR_JOB_ID", "SRC_EXECUTOR_NAME"]`),