Skip to content

Commit c281e17

Browse files
authored
fix: fall back to repo default_branch instead of hardcoded "main" (#1143)
* fix: fall back to repo default_branch instead of hardcoded "main" When no explicit base_branch input is provided, the action previously fell back to a hardcoded "main", which fails on repositories whose default branch is named differently (e.g. "master", "develop"). This reads repository.default_branch from the GitHub event payload (populated once in parseGitHubContext) and uses it as the fallback in all three callsites: agent/index.ts, run.ts, and update-comment-link.ts. Explicit env/input precedence is preserved; "main" remains only as a last-resort defensive fallback if the payload somehow lacks the field. * test: drop unused BASE_BRANCH env handling from default_branch test agent/index.ts no longer reads process.env.BASE_BRANCH directly (it now goes through context.inputs.baseBranch which is set on the mock context), so saving/clearing/restoring that env var in the regression test is dead code.
1 parent 408a40e commit c281e17

File tree

6 files changed

+66
-8
lines changed

6 files changed

+66
-8
lines changed

src/entrypoints/run.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -229,8 +229,8 @@ async function run() {
229229
// Restore them from the base branch before the CLI reads them.
230230
//
231231
// We read pull_request.base.ref from the payload directly because agent
232-
// mode's branchInfo.baseBranch defaults to "main" rather than the PR's
233-
// actual target (agent/index.ts). For issue_comment on a PR the payload
232+
// mode's branchInfo.baseBranch defaults to the repo's default branch rather
233+
// than the PR's actual target (agent/index.ts). For issue_comment on a PR the payload
234234
// lacks base.ref, so we fall back to the mode-provided value — tag mode
235235
// fetches it from GraphQL; agent mode on issue_comment is an edge case
236236
// that at worst restores from the wrong trusted branch (still secure).
@@ -312,7 +312,7 @@ async function run() {
312312
commentId,
313313
githubToken,
314314
claudeBranch,
315-
baseBranch: baseBranch || "main",
315+
baseBranch: baseBranch || context.repository.default_branch || "main",
316316
triggerUsername: context.actor,
317317
context,
318318
octokit,

src/entrypoints/update-comment-link.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -253,7 +253,8 @@ async function run() {
253253
commentId: parseInt(process.env.CLAUDE_COMMENT_ID!),
254254
githubToken,
255255
claudeBranch: process.env.CLAUDE_BRANCH,
256-
baseBranch: process.env.BASE_BRANCH || "main",
256+
baseBranch:
257+
process.env.BASE_BRANCH || context.repository.default_branch || "main",
257258
triggerUsername: process.env.TRIGGER_USERNAME,
258259
context,
259260
octokit,

src/github/context.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,7 @@ type BaseContext = {
7979
owner: string;
8080
repo: string;
8181
full_name: string;
82+
default_branch?: string;
8283
};
8384
actor: string;
8485
inputs: {
@@ -140,6 +141,7 @@ export function parseGitHubContext(): GitHubContext {
140141
owner: context.repo.owner,
141142
repo: context.repo.repo,
142143
full_name: `${context.repo.owner}/${context.repo.repo}`,
144+
default_branch: context.payload.repository?.default_branch,
143145
},
144146
actor: context.actor,
145147
inputs: {

src/modes/agent/index.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -85,15 +85,15 @@ export async function prepareAgentMode({
8585

8686
// Check for branch info from environment variables (useful for auto-fix workflows)
8787
const claudeBranch = process.env.CLAUDE_BRANCH || undefined;
88-
const baseBranch =
89-
process.env.BASE_BRANCH || context.inputs.baseBranch || "main";
88+
const defaultBranch = context.repository.default_branch || "main";
89+
const baseBranch = context.inputs.baseBranch || defaultBranch;
9090

9191
// Detect current branch from GitHub environment
9292
const currentBranch =
9393
claudeBranch ||
9494
process.env.GITHUB_HEAD_REF ||
9595
process.env.GITHUB_REF_NAME ||
96-
"main";
96+
defaultBranch;
9797

9898
// Get our GitHub MCP servers config
9999
const ourMcpConfig = await prepareMcpConfig({

test/mockContext.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ const defaultRepository = {
3636
owner: "test-owner",
3737
repo: "test-repo",
3838
full_name: "test-owner/test-repo",
39+
default_branch: "main",
3940
};
4041

4142
type MockContextOverrides = Omit<Partial<ParsedGitHubContext>, "inputs"> & {

test/modes/agent.test.ts

Lines changed: 55 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -88,7 +88,7 @@ describe("Agent Mode", () => {
8888
expect(result.claudeArgs).toBe("--model claude-sonnet-4 --max-turns 10");
8989
expect(result.claudeArgs).not.toContain("--mcp-config");
9090

91-
// Verify return structure - should use "main" as fallback when no env vars set
91+
// Verify return structure - should fall back to repository.default_branch when no env vars set
9292
expect(result).toEqual({
9393
commentId: undefined,
9494
branchInfo: {
@@ -108,6 +108,60 @@ describe("Agent Mode", () => {
108108
process.env.GITHUB_REF_NAME = originalRefName;
109109
});
110110

111+
test("prepare falls back to repository.default_branch when not 'main'", async () => {
112+
const contextWithDevelop = createMockAutomationContext({
113+
eventName: "workflow_dispatch",
114+
repository: {
115+
owner: "test-owner",
116+
repo: "test-repo",
117+
full_name: "test-owner/test-repo",
118+
default_branch: "develop",
119+
},
120+
});
121+
122+
// Save and clear env vars that would otherwise override the fallback
123+
const originalClaudeBranch = process.env.CLAUDE_BRANCH;
124+
const originalHeadRef = process.env.GITHUB_HEAD_REF;
125+
const originalRefName = process.env.GITHUB_REF_NAME;
126+
delete process.env.CLAUDE_BRANCH;
127+
delete process.env.GITHUB_HEAD_REF;
128+
delete process.env.GITHUB_REF_NAME;
129+
130+
const mockOctokit = {
131+
rest: {
132+
users: {
133+
getAuthenticated: mock(() =>
134+
Promise.resolve({
135+
data: { login: "test-user", id: 12345, type: "User" },
136+
}),
137+
),
138+
getByUsername: mock(() =>
139+
Promise.resolve({
140+
data: { login: "test-user", id: 12345, type: "User" },
141+
}),
142+
),
143+
},
144+
},
145+
} as any;
146+
147+
const result = await prepareAgentMode({
148+
context: contextWithDevelop,
149+
octokit: mockOctokit,
150+
githubToken: "test-token",
151+
});
152+
153+
expect(result.branchInfo.baseBranch).toBe("develop");
154+
expect(result.branchInfo.currentBranch).toBe("develop");
155+
156+
// Restore env vars
157+
if (originalClaudeBranch !== undefined)
158+
process.env.CLAUDE_BRANCH = originalClaudeBranch;
159+
if (originalHeadRef !== undefined)
160+
process.env.GITHUB_HEAD_REF = originalHeadRef;
161+
if (originalRefName !== undefined)
162+
process.env.GITHUB_REF_NAME = originalRefName;
163+
});
164+
111165
test("prepare rejects bot actors without allowed_bots", async () => {
112166
const contextWithPrompts = createMockAutomationContext({
113167
eventName: "workflow_dispatch",

0 commit comments

Comments
 (0)