From 0ce1d9fd7b38fe1a47d0d4d7eba548704101fd62 Mon Sep 17 00:00:00 2001 From: Colin McDonnell Date: Thu, 20 Nov 2025 16:31:00 -0800 Subject: [PATCH] deterministically set up working branch --- agents/instructions.ts | 1 + external.ts | 6 ++++++ main.ts | 3 ++- utils/setup.ts | 40 ++++++++++++++++++++++++++++++++++++++++ 4 files changed, 49 insertions(+), 1 deletion(-) diff --git a/agents/instructions.ts b/agents/instructions.ts index cec07ce..785fa71 100644 --- a/agents/instructions.ts +++ b/agents/instructions.ts @@ -16,6 +16,7 @@ You do not add unecessary comments, tests, or documentation unless explicitly pr You adapt your writing style to the style of your coworkers, while never being unprofessional. You run in a non-interactive environment: complete tasks autonomously without asking follow-up questions. You make reasonable assumptions when details are missing, but fail with an explicit error if critical information is missing (e.g. user asks to review a PR but does not provide a link or ID). +Never push commits directly to protected branches: main, master, production. Always create a feature branch. ## SECURITY diff --git a/external.ts b/external.ts index 29cf583..efda803 100644 --- a/external.ts +++ b/external.ts @@ -46,6 +46,7 @@ export type PayloadEvent = pr_number: number; pr_title: string; pr_body: string | null; + branch: string; [key: string]: any; } | { @@ -53,6 +54,7 @@ export type PayloadEvent = pr_number: number; pr_title: string; pr_body: string | null; + branch: string; [key: string]: any; } | { @@ -63,6 +65,7 @@ export type PayloadEvent = review_state: string; review_comments: any[]; context: any; + branch: string; [key: string]: any; } | { @@ -72,6 +75,7 @@ export type PayloadEvent = comment_id: number; comment_body: string; thread?: any; + branch: string; [key: string]: any; } | { @@ -100,6 +104,7 @@ export type PayloadEvent = comment_id: number; comment_body: string; issue_number: number; + branch?: string; [key: string]: any; } | { @@ -108,6 +113,7 @@ export type PayloadEvent = pr_title: string; pr_body: string | null; pull_request: any; + branch: string; check_suite: { id: number; head_sha: string; diff --git a/main.ts b/main.ts index cd79f3c..72ef58c 100644 --- a/main.ts +++ b/main.ts @@ -17,7 +17,7 @@ import { revokeInstallationToken, setupGitHubInstallationToken, } from "./utils/github.ts"; -import { setupGitAuth, setupGitConfig } from "./utils/setup.ts"; +import { setupGitAuth, setupGitBranch, setupGitConfig } from "./utils/setup.ts"; // runtime validation using agents (needed for ArkType) // Note: The AgentName type is defined in external.ts, this is the runtime validator @@ -57,6 +57,7 @@ export async function main(inputs: Inputs): Promise { setupMcpLogPolling(ctx); ctx.payload = parsePayload(inputs); + setupGitBranch(ctx.payload); setupMcpServers(ctx); await installAgentCli(ctx); validateApiKey(ctx); diff --git a/utils/setup.ts b/utils/setup.ts index e204569..985d38a 100644 --- a/utils/setup.ts +++ b/utils/setup.ts @@ -1,5 +1,6 @@ import { execSync } from "node:child_process"; import { existsSync, rmSync } from "node:fs"; +import type { Payload } from "../external.ts"; import { log } from "./cli.ts"; import type { RepoContext } from "./github.ts"; import { $ } from "./shell.ts"; @@ -91,3 +92,42 @@ export function setupGitAuth(githubToken: string, repoContext: RepoContext): voi $("git", ["remote", "set-url", "origin", remoteUrl]); log.info("✓ Updated remote URL with authentication token"); } + +/** + * Setup git branch based on payload event context + * Automatically checks out the appropriate branch before agent execution + */ +export function setupGitBranch(payload: Payload): void { + // Only set up git branch in GitHub Actions environment + // In local testing, this might interfere with local git state + if (!process.env.GITHUB_ACTIONS) { + return; + } + + const branch = payload.event.branch; + + if (!branch) { + log.debug("No branch specified in payload, using default branch"); + return; + } + + log.info(`🌿 Setting up git branch: ${branch}`); + + try { + // Fetch the branch from origin + log.debug(`Fetching branch from origin: ${branch}`); + execSync(`git fetch origin ${branch}`, { stdio: "pipe" }); + + // Checkout the branch, creating local tracking branch + log.debug(`Checking out branch: ${branch}`); + execSync(`git checkout -B ${branch} origin/${branch}`, { stdio: "pipe" }); + + log.info(`✓ Successfully checked out branch: ${branch}`); + } catch (error) { + // If git operations fail, log warning but don't fail the action + // The agent might still be able to work with the default branch + log.warning( + `Failed to checkout branch ${branch}: ${error instanceof Error ? error.message : String(error)}` + ); + } +}