From 0f45be3f67cd05b1293cef33846f1e9d9862598f Mon Sep 17 00:00:00 2001 From: Ashwin Bhat Date: Tue, 3 Feb 2026 18:50:20 -0800 Subject: [PATCH] fix: address PR review feedback - Use path.dirname() instead of manual string slicing for executable path - Differentiate prepare vs execution errors in catch block so tracking comment accurately reflects which phase failed - Update CLAUDE.md architecture docs to reflect unified run.ts entrypoint and four-phase design --- CLAUDE.md | 31 +++++++++++++++++++------------ src/entrypoints/run.ts | 17 +++++++++++------ 2 files changed, 30 insertions(+), 18 deletions(-) diff --git a/CLAUDE.md b/CLAUDE.md index 7834fc2..7ff112f 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -25,28 +25,35 @@ bun run typecheck # Run TypeScript type checker ## Architecture Overview -This is a GitHub Action that enables Claude to interact with GitHub PRs and issues. The action operates in two main phases: +This is a GitHub Action that enables Claude to interact with GitHub PRs and issues. The action runs through a unified entrypoint (`src/entrypoints/run.ts`) that orchestrates four internal phases within a single composite step: -### Phase 1: Preparation (`src/entrypoints/prepare.ts`) +### Phase 1: Prepare 1. **Authentication Setup**: Establishes GitHub token via OIDC or GitHub App 2. **Permission Validation**: Verifies actor has write permissions 3. **Trigger Detection**: Uses mode-specific logic to determine if Claude should respond -4. **Context Creation**: Prepares GitHub context and initial tracking comment +4. **Context Creation**: Prepares GitHub context, initial tracking comment, and branch -### Phase 2: Execution (`base-action/`) +### Phase 2: Install -The `base-action/` directory contains the core Claude Code execution logic, which serves a dual purpose: +1. **Claude Code CLI**: Installs the CLI (with retry logic), or uses a custom executable path -- **Standalone Action**: Published separately as `@anthropic-ai/claude-code-base-action` for direct use -- **Inner Logic**: Used internally by this GitHub Action after preparation phase completes +### Phase 3: Execute -Execution steps: +Imports `base-action/` functions directly (no subprocess) to run Claude: -1. **MCP Server Setup**: Installs and configures GitHub MCP server for tool access -2. **Prompt Generation**: Creates context-rich prompts from GitHub data +1. **Environment Setup**: Validates env vars, configures Claude Code settings, installs plugins +2. **Prompt Preparation**: Writes the context-rich prompt to a temp file 3. **Claude Integration**: Executes via multiple providers (Anthropic API, AWS Bedrock, Google Vertex AI) -4. **Result Processing**: Updates comments and creates branches/PRs as needed + +The `base-action/` directory is also published separately as `@anthropic-ai/claude-code-base-action` for standalone use. + +### Phase 4: Cleanup (always runs) + +1. **Comment Update**: Updates the tracking comment with job link, branch, and status +2. **Step Summary**: Formats Claude's output into the GitHub Actions step summary +3. **SSH Signing Cleanup**: Removes SSH signing key (separate composite step, runs with `always()`) +4. **Token Revocation**: Revokes the GitHub App installation token (separate composite step, runs with `always()`) ### Key Architectural Components @@ -82,7 +89,7 @@ Execution steps: ``` src/ ├── entrypoints/ # Action entry points -│ ├── prepare.ts # Main preparation logic +│ ├── run.ts # Unified entrypoint (orchestrates all phases) │ ├── update-comment-link.ts # Post-execution comment updates │ └── format-turns.ts # Claude conversation formatting ├── github/ # GitHub integration layer diff --git a/src/entrypoints/run.ts b/src/entrypoints/run.ts index b32d399..871fa19 100644 --- a/src/entrypoints/run.ts +++ b/src/entrypoints/run.ts @@ -7,6 +7,7 @@ */ import * as core from "@actions/core"; +import { dirname } from "path"; import { spawn } from "child_process"; import { appendFile } from "fs/promises"; import { existsSync, readFileSync } from "fs"; @@ -37,10 +38,7 @@ async function installClaudeCode(): Promise { const customExecutable = process.env.PATH_TO_CLAUDE_CODE_EXECUTABLE; if (customExecutable) { console.log(`Using custom Claude Code executable: ${customExecutable}`); - const claudeDir = customExecutable.substring( - 0, - customExecutable.lastIndexOf("/"), - ); + const claudeDir = dirname(customExecutable); // Add to PATH by appending to GITHUB_PATH const githubPath = process.env.GITHUB_PATH; if (githubPath) { @@ -134,6 +132,8 @@ async function run() { let prepareError: string | undefined; let context: GitHubContext | undefined; let octokit: Octokits | undefined; + // Track whether we've completed prepare phase, so we can attribute errors correctly + let prepareCompleted = false; try { // Phase 1: Prepare const actionInputsPresent = collectActionInputsPresence(); @@ -195,6 +195,8 @@ async function run() { commentId = prepareResult.commentId; claudeBranch = prepareResult.branchInfo.claudeBranch; baseBranch = prepareResult.branchInfo.baseBranch; + prepareCompleted = true; + // Set system prompt if available if (mode.getSystemPrompt) { const modeContext = mode.prepareContext(context, { @@ -260,8 +262,11 @@ async function run() { core.setOutput("conclusion", claudeResult.conclusion); } catch (error) { const errorMessage = error instanceof Error ? error.message : String(error); - prepareSuccess = false; - prepareError = errorMessage; + // Only mark as prepare failure if we haven't completed the prepare phase + if (!prepareCompleted) { + prepareSuccess = false; + prepareError = errorMessage; + } core.setFailed(`Action failed with error: ${errorMessage}`); } finally { // Phase 4: Cleanup (always runs)