From 9162b87915f7d87141c5cee07f076383ababfffb Mon Sep 17 00:00:00 2001 From: Alessandro Pogliaghi Date: Tue, 21 Jul 2026 19:46:31 +0100 Subject: [PATCH] fix(cloud-agent): fall back to legacy env file for github token MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit During a mixed-version rollout a new agent can run against an older backend that still refreshes GitHub credentials only into the legacy /tmp/agent-env. The reader looked only at the dedicated /tmp/agent-github-env and, when absent, dropped straight to the process env frozen at launch — signing commits with an expired or previous actor's token. Chain a fallback to the legacy live file before the frozen process env, so a refreshed token is used under either backend version. Generated-By: PostHog Code Task-Id: 5d51048c-d903-459e-a770-028ab37f9099 --- packages/agent/src/utils/github-token.test.ts | 25 ++++++++++++++++--- packages/agent/src/utils/github-token.ts | 22 ++++++++++++---- 2 files changed, 38 insertions(+), 9 deletions(-) diff --git a/packages/agent/src/utils/github-token.test.ts b/packages/agent/src/utils/github-token.test.ts index 08ef9428b9..97d9310f1a 100644 --- a/packages/agent/src/utils/github-token.test.ts +++ b/packages/agent/src/utils/github-token.test.ts @@ -66,11 +66,28 @@ describe("github-token", () => { expect(resolveGithubToken(path)).toBe("ghs_fromfile"); }); - it("falls back to the process env when the sandbox file is absent", () => { + it("prefers the github env file over the legacy env file", () => { + const githubPath = writeEnvFile("GH_TOKEN=ghs_github\0"); + const legacyPath = writeEnvFile("GH_TOKEN=ghs_legacy\0"); + expect(resolveGithubToken(githubPath, legacyPath)).toBe("ghs_github"); + }); + + it("falls back to the legacy env file when the github env file is absent", () => { vi.stubEnv("GH_TOKEN", "ghs_fromprocess"); - expect(resolveGithubToken("/nonexistent/agent-env")).toBe( - "ghs_fromprocess", - ); + const legacyPath = writeEnvFile("GH_TOKEN=ghs_legacy\0"); + expect( + resolveGithubToken("/nonexistent/agent-github-env", legacyPath), + ).toBe("ghs_legacy"); + }); + + it("falls back to the process env when both sandbox files are absent", () => { + vi.stubEnv("GH_TOKEN", "ghs_fromprocess"); + expect( + resolveGithubToken( + "/nonexistent/agent-github-env", + "/nonexistent/agent-env", + ), + ).toBe("ghs_fromprocess"); }); }); }); diff --git a/packages/agent/src/utils/github-token.ts b/packages/agent/src/utils/github-token.ts index 1e1ae73228..d29d129c9d 100644 --- a/packages/agent/src/utils/github-token.ts +++ b/packages/agent/src/utils/github-token.ts @@ -8,6 +8,11 @@ import { readGithubTokenFromEnv } from "@posthog/git/signed-commit"; // this live file is how in-process tools pick up a refreshed token without a // process restart. const SANDBOX_GITHUB_ENV_FILE = "/tmp/agent-github-env"; +// Legacy pre-split file. Before GitHub credentials got their own file, the +// backend refreshed them in place here. Kept as a fallback so that during a +// mixed-version rollout (new agent, old backend that only writes this file) we +// still read a live, refreshed token instead of the frozen process env. +const SANDBOX_LEGACY_ENV_FILE = "/tmp/agent-env"; export function readGithubTokenFromSandboxEnvFile( envFilePath: string = SANDBOX_GITHUB_ENV_FILE, @@ -31,14 +36,21 @@ export function readGithubTokenFromSandboxEnvFile( /** The GitHub token available to the sandbox, if any. * - * Prefers the live agentsh env file (refreshed in place mid-session) over the - * process env (frozen at launch) so long-running in-process tools — e.g. the - * signed-commit tool — pick up a refreshed token without a restart. + * Precedence: the dedicated live credential file, then the legacy live file, + * then the process env (frozen at launch). Reading a live file first is how + * long-running in-process tools — e.g. the signed-commit tool — pick up a + * refreshed token without a restart. The legacy fallback covers a mixed-version + * rollout where an older backend still refreshes credentials only into + * `/tmp/agent-env`; without it the reader would drop straight to the frozen + * process env and sign commits with an expired or previous actor's token. */ export function resolveGithubToken( - envFilePath: string = SANDBOX_GITHUB_ENV_FILE, + githubEnvFilePath: string = SANDBOX_GITHUB_ENV_FILE, + legacyEnvFilePath: string = SANDBOX_LEGACY_ENV_FILE, ): string | undefined { return ( - readGithubTokenFromSandboxEnvFile(envFilePath) ?? readGithubTokenFromEnv() + readGithubTokenFromSandboxEnvFile(githubEnvFilePath) ?? + readGithubTokenFromSandboxEnvFile(legacyEnvFilePath) ?? + readGithubTokenFromEnv() ); }