authorgravatar for 76947376+Andrew-Forster@users.noreply.github.comAndrew Forster <76947376+Andrew-Forster@users.noreply.github.com> 2026-06-17 15:49:09-07:00
committergravatar for noreply@github.comGitHub <noreply@github.com> 2026-06-17 22:49:09+00:00
log75b3b3c48aa51345f712f979a670cc17f2b41b26
tree65a0671b7174142cc9c462cd67a2192485ef88b9
parentb489ea52a85b6e6d698523a696f42d7fc730015f
signature Signed by PGP key B5690EEEBB952194

[codex] Make background VCS fetch non-interactive (#3133)


2 files changed, 38 insertions(+), 20 deletions(-)

apps/server/src/vcs/GitVcsDriverCore.test.ts+34-20
...@@ -216,7 +216,7 @@ it.layer(TestLayer)("GitVcsDriver core integration", (it) => {...@@ -216,7 +216,7 @@ it.layer(TestLayer)("GitVcsDriver core integration", (it) => {
216 }),216 }),
217 );217 );
218218
219 it.effect("disables SSH askpass for background upstream status fetches", () =>219 it.effect("makes background upstream status fetches non-interactive", () =>
220 Effect.gen(function* () {220 Effect.gen(function* () {
221 const cwd = yield* makeTmpDir();221 const cwd = yield* makeTmpDir();
222 const tempDir = yield* makeTmpDir("git-vcs-driver-ssh-env-");222 const tempDir = yield* makeTmpDir("git-vcs-driver-ssh-env-");
...@@ -225,15 +225,26 @@ it.layer(TestLayer)("GitVcsDriver core integration", (it) => {...@@ -225,15 +225,26 @@ it.layer(TestLayer)("GitVcsDriver core integration", (it) => {
225 const pathService = yield* Path.Path;225 const pathService = yield* Path.Path;
226 const sshLogPath = pathService.join(tempDir, "ssh-env.txt");226 const sshLogPath = pathService.join(tempDir, "ssh-env.txt");
227 const sshWrapperPath = pathService.join(tempDir, "ssh-wrapper.sh");227 const sshWrapperPath = pathService.join(tempDir, "ssh-wrapper.sh");
228 const previousGitSsh = process.env.GIT_SSH;228 const envKeys = [
229 const previousAskpassRequire = process.env.SSH_ASKPASS_REQUIRE;229 "GCM_INTERACTIVE",
230 const previousAskpassLog = process.env.T3_TEST_SSH_ASKPASS_LOG;230 "GIT_ASKPASS",
231 "GIT_SSH",
232 "GIT_TERMINAL_PROMPT",
233 "SSH_ASKPASS",
234 "SSH_ASKPASS_REQUIRE",
235 "T3_TEST_SSH_ASKPASS_LOG",
236 ] as const;
237 const previousEnv = new Map(envKeys.map((key) => [key, process.env[key]]));
231238
232 yield* fileSystem.writeFileString(239 yield* fileSystem.writeFileString(
233 sshWrapperPath,240 sshWrapperPath,
234 [241 [
235 "#!/bin/sh",242 "#!/bin/sh",
236 'printf "%s\\n" "${SSH_ASKPASS_REQUIRE:-}" > "$T3_TEST_SSH_ASKPASS_LOG"',243 'printf "GCM_INTERACTIVE=%s\\n" "${GCM_INTERACTIVE:-}" > "$T3_TEST_SSH_ASKPASS_LOG"',
244 'printf "GIT_ASKPASS=%s\\n" "${GIT_ASKPASS:-}" >> "$T3_TEST_SSH_ASKPASS_LOG"',
245 'printf "GIT_TERMINAL_PROMPT=%s\\n" "${GIT_TERMINAL_PROMPT:-}" >> "$T3_TEST_SSH_ASKPASS_LOG"',
246 'printf "SSH_ASKPASS=%s\\n" "${SSH_ASKPASS:-}" >> "$T3_TEST_SSH_ASKPASS_LOG"',
247 'printf "SSH_ASKPASS_REQUIRE=%s\\n" "${SSH_ASKPASS_REQUIRE:-}" >> "$T3_TEST_SSH_ASKPASS_LOG"',
237 "exit 1",248 "exit 1",
238 "",249 "",
239 ].join("\n"),250 ].join("\n"),
...@@ -245,29 +256,32 @@ it.layer(TestLayer)("GitVcsDriver core integration", (it) => {...@@ -245,29 +256,32 @@ it.layer(TestLayer)("GitVcsDriver core integration", (it) => {
245256
246 yield* Effect.gen(function* () {257 yield* Effect.gen(function* () {
247 process.env.GIT_SSH = sshWrapperPath;258 process.env.GIT_SSH = sshWrapperPath;
259 process.env.GCM_INTERACTIVE = "always";
260 process.env.GIT_ASKPASS = "git-askpass";
261 process.env.GIT_TERMINAL_PROMPT = "1";
262 process.env.SSH_ASKPASS = "ssh-askpass";
248 process.env.SSH_ASKPASS_REQUIRE = "force";263 process.env.SSH_ASKPASS_REQUIRE = "force";
249 process.env.T3_TEST_SSH_ASKPASS_LOG = sshLogPath;264 process.env.T3_TEST_SSH_ASKPASS_LOG = sshLogPath;
250265
251 yield* (yield* GitVcsDriver.GitVcsDriver).statusDetails(cwd);266 yield* (yield* GitVcsDriver.GitVcsDriver).statusDetails(cwd);
252267
253 assert.equal((yield* fileSystem.readFileString(sshLogPath)).trim(), "never");268 assert.deepEqual((yield* fileSystem.readFileString(sshLogPath)).trim().split(/\r?\n/), [
269 "GCM_INTERACTIVE=never",
270 "GIT_ASKPASS=",
271 "GIT_TERMINAL_PROMPT=0",
272 "SSH_ASKPASS=",
273 "SSH_ASKPASS_REQUIRE=never",
274 ]);
254 }).pipe(275 }).pipe(
255 Effect.ensuring(276 Effect.ensuring(
256 Effect.sync(() => {277 Effect.sync(() => {
257 if (previousGitSsh === undefined) {278 for (const key of envKeys) {
258 delete process.env.GIT_SSH;279 const previous = previousEnv.get(key);
259 } else {280 if (previous === undefined) {
260 process.env.GIT_SSH = previousGitSsh;281 delete process.env[key];
261 }282 } else {
262 if (previousAskpassRequire === undefined) {283 process.env[key] = previous;
263 delete process.env.SSH_ASKPASS_REQUIRE;284 }
264 } else {
265 process.env.SSH_ASKPASS_REQUIRE = previousAskpassRequire;
266 }
267 if (previousAskpassLog === undefined) {
268 delete process.env.T3_TEST_SSH_ASKPASS_LOG;
269 } else {
270 process.env.T3_TEST_SSH_ASKPASS_LOG = previousAskpassLog;
271 }285 }
272 }),286 }),
273 ),287 ),
apps/server/src/vcs/GitVcsDriverCore.ts+4
...@@ -54,6 +54,10 @@ const STATUS_UPSTREAM_REFRESH_TIMEOUT = Duration.seconds(5);...@@ -54,6 +54,10 @@ const STATUS_UPSTREAM_REFRESH_TIMEOUT = Duration.seconds(5);
54const STATUS_UPSTREAM_REFRESH_FAILURE_COOLDOWN = Duration.seconds(5);54const STATUS_UPSTREAM_REFRESH_FAILURE_COOLDOWN = Duration.seconds(5);
55const STATUS_UPSTREAM_REFRESH_CACHE_CAPACITY = 2_048;55const STATUS_UPSTREAM_REFRESH_CACHE_CAPACITY = 2_048;
56const STATUS_UPSTREAM_REFRESH_ENV = Object.freeze({56const STATUS_UPSTREAM_REFRESH_ENV = Object.freeze({
57 GCM_INTERACTIVE: "never",
58 GIT_ASKPASS: "",
59 GIT_TERMINAL_PROMPT: "0",
60 SSH_ASKPASS: "",
57 SSH_ASKPASS_REQUIRE: "never",61 SSH_ASKPASS_REQUIRE: "never",
58} satisfies NodeJS.ProcessEnv);62} satisfies NodeJS.ProcessEnv);
59const DEFAULT_BASE_BRANCH_CANDIDATES = ["main", "master"] as const;63const DEFAULT_BASE_BRANCH_CANDIDATES = ["main", "master"] as const;