mirror of
https://github.com/anthropics/claude-code-action.git
synced 2026-01-23 23:14:13 +08:00
Compare commits
1 Commits
ashwin/err
...
ashwin/tri
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
2f766f1151 |
36
.github/ISSUE_TEMPLATE/bug_report.md
vendored
36
.github/ISSUE_TEMPLATE/bug_report.md
vendored
@@ -1,36 +0,0 @@
|
|||||||
---
|
|
||||||
name: Bug report
|
|
||||||
about: Create a report to help us improve
|
|
||||||
title: ""
|
|
||||||
labels: bug
|
|
||||||
assignees: ""
|
|
||||||
---
|
|
||||||
|
|
||||||
**Describe the bug**
|
|
||||||
A clear and concise description of what the bug is.
|
|
||||||
|
|
||||||
**To Reproduce**
|
|
||||||
Steps to reproduce the behavior:
|
|
||||||
|
|
||||||
1. Go to '...'
|
|
||||||
2. Click on '....'
|
|
||||||
3. Scroll down to '....'
|
|
||||||
4. See error
|
|
||||||
|
|
||||||
**Expected behavior**
|
|
||||||
A clear and concise description of what you expected to happen.
|
|
||||||
|
|
||||||
**Screenshots**
|
|
||||||
If applicable, add screenshots to help explain your problem.
|
|
||||||
|
|
||||||
**Workflow yml file**
|
|
||||||
If it's not sensitive, consider including a paste of your full Claude workflow.yml file.
|
|
||||||
|
|
||||||
**API Provider**
|
|
||||||
|
|
||||||
[ ] Anthropic First-Party API (default)
|
|
||||||
[ ] AWS Bedrock
|
|
||||||
[ ] GCP Vertex
|
|
||||||
|
|
||||||
**Additional context**
|
|
||||||
Add any other context about the problem here.
|
|
||||||
1
.gitignore
vendored
1
.gitignore
vendored
@@ -1,4 +1,3 @@
|
|||||||
.DS_Store
|
|
||||||
node_modules
|
node_modules
|
||||||
|
|
||||||
**/.claude/settings.local.json
|
**/.claude/settings.local.json
|
||||||
|
|||||||
@@ -446,7 +446,7 @@ anthropic_api_key: ${{ secrets.ANTHROPIC_API_KEY }}
|
|||||||
```
|
```
|
||||||
|
|
||||||
This applies to all sensitive values including API keys, access tokens, and credentials.
|
This applies to all sensitive values including API keys, access tokens, and credentials.
|
||||||
We also recommend that you always use short-lived tokens when possible
|
We also reccomend that you always use short-lived tokens when possible
|
||||||
|
|
||||||
## License
|
## License
|
||||||
|
|
||||||
|
|||||||
@@ -94,7 +94,7 @@ runs:
|
|||||||
- name: Run Claude Code
|
- name: Run Claude Code
|
||||||
id: claude-code
|
id: claude-code
|
||||||
if: steps.prepare.outputs.contains_trigger == 'true'
|
if: steps.prepare.outputs.contains_trigger == 'true'
|
||||||
uses: anthropics/claude-code-base-action@266585c92dd90d61d3806a3367582c4f6224e892 # https://github.com/anthropics/claude-code-base-action/releases/tag/v0.0.6
|
uses: anthropics/claude-code-base-action@5097b6cdfe5fc5a3ac0166cc344c34ed23c93982 # https://github.com/anthropics/claude-code-base-action/releases/tag/v0.0.5
|
||||||
with:
|
with:
|
||||||
prompt_file: /tmp/claude-prompts/claude-prompt.txt
|
prompt_file: /tmp/claude-prompts/claude-prompt.txt
|
||||||
allowed_tools: ${{ env.ALLOWED_TOOLS }}
|
allowed_tools: ${{ env.ALLOWED_TOOLS }}
|
||||||
@@ -147,8 +147,6 @@ runs:
|
|||||||
CLAUDE_SUCCESS: ${{ steps.claude-code.outputs.conclusion == 'success' }}
|
CLAUDE_SUCCESS: ${{ steps.claude-code.outputs.conclusion == 'success' }}
|
||||||
OUTPUT_FILE: ${{ steps.claude-code.outputs.execution_file || '' }}
|
OUTPUT_FILE: ${{ steps.claude-code.outputs.execution_file || '' }}
|
||||||
TRIGGER_USERNAME: ${{ github.event.comment.user.login || github.event.issue.user.login || github.event.pull_request.user.login || github.event.sender.login || github.triggering_actor || github.actor || '' }}
|
TRIGGER_USERNAME: ${{ github.event.comment.user.login || github.event.issue.user.login || github.event.pull_request.user.login || github.event.sender.login || github.triggering_actor || github.actor || '' }}
|
||||||
PREPARE_SUCCESS: ${{ steps.prepare.outcome == 'success' }}
|
|
||||||
PREPARE_ERROR: ${{ steps.prepare.outputs.prepare_error || '' }}
|
|
||||||
|
|
||||||
- name: Display Claude Code Report
|
- name: Display Claude Code Report
|
||||||
if: steps.prepare.outputs.contains_trigger == 'true' && steps.claude-code.outputs.execution_file != ''
|
if: steps.prepare.outputs.contains_trigger == 'true' && steps.claude-code.outputs.execution_file != ''
|
||||||
|
|||||||
BIN
src/.DS_Store
vendored
Normal file
BIN
src/.DS_Store
vendored
Normal file
Binary file not shown.
@@ -92,10 +92,7 @@ async function run() {
|
|||||||
);
|
);
|
||||||
core.setOutput("mcp_config", mcpConfig);
|
core.setOutput("mcp_config", mcpConfig);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
const errorMessage = error instanceof Error ? error.message : String(error);
|
core.setFailed(`Prepare step failed with error: ${error}`);
|
||||||
core.setFailed(`Prepare step failed with error: ${errorMessage}`);
|
|
||||||
// Also output the clean error message for the action to capture
|
|
||||||
core.setOutput("prepare_error", errorMessage);
|
|
||||||
process.exit(1);
|
process.exit(1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -145,48 +145,38 @@ async function run() {
|
|||||||
duration_api_ms?: number;
|
duration_api_ms?: number;
|
||||||
} | null = null;
|
} | null = null;
|
||||||
let actionFailed = false;
|
let actionFailed = false;
|
||||||
let errorDetails: string | undefined;
|
|
||||||
|
|
||||||
// First check if prepare step failed
|
// Check for existence of output file and parse it if available
|
||||||
const prepareSuccess = process.env.PREPARE_SUCCESS !== "false";
|
try {
|
||||||
const prepareError = process.env.PREPARE_ERROR;
|
const outputFile = process.env.OUTPUT_FILE;
|
||||||
|
if (outputFile) {
|
||||||
|
const fileContent = await fs.readFile(outputFile, "utf8");
|
||||||
|
const outputData = JSON.parse(fileContent);
|
||||||
|
|
||||||
if (!prepareSuccess && prepareError) {
|
// Output file is an array, get the last element which contains execution details
|
||||||
actionFailed = true;
|
if (Array.isArray(outputData) && outputData.length > 0) {
|
||||||
errorDetails = prepareError;
|
const lastElement = outputData[outputData.length - 1];
|
||||||
} else {
|
if (
|
||||||
// Check for existence of output file and parse it if available
|
lastElement.role === "system" &&
|
||||||
try {
|
"cost_usd" in lastElement &&
|
||||||
const outputFile = process.env.OUTPUT_FILE;
|
"duration_ms" in lastElement
|
||||||
if (outputFile) {
|
) {
|
||||||
const fileContent = await fs.readFile(outputFile, "utf8");
|
executionDetails = {
|
||||||
const outputData = JSON.parse(fileContent);
|
cost_usd: lastElement.cost_usd,
|
||||||
|
duration_ms: lastElement.duration_ms,
|
||||||
// Output file is an array, get the last element which contains execution details
|
duration_api_ms: lastElement.duration_api_ms,
|
||||||
if (Array.isArray(outputData) && outputData.length > 0) {
|
};
|
||||||
const lastElement = outputData[outputData.length - 1];
|
|
||||||
if (
|
|
||||||
lastElement.role === "system" &&
|
|
||||||
"cost_usd" in lastElement &&
|
|
||||||
"duration_ms" in lastElement
|
|
||||||
) {
|
|
||||||
executionDetails = {
|
|
||||||
cost_usd: lastElement.cost_usd,
|
|
||||||
duration_ms: lastElement.duration_ms,
|
|
||||||
duration_api_ms: lastElement.duration_api_ms,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check if the Claude action failed
|
|
||||||
const claudeSuccess = process.env.CLAUDE_SUCCESS !== "false";
|
|
||||||
actionFailed = !claudeSuccess;
|
|
||||||
} catch (error) {
|
|
||||||
console.error("Error reading output file:", error);
|
|
||||||
// If we can't read the file, check for any failure markers
|
|
||||||
actionFailed = process.env.CLAUDE_SUCCESS === "false";
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Check if the action failed by looking at the exit code or error marker
|
||||||
|
const claudeSuccess = process.env.CLAUDE_SUCCESS !== "false";
|
||||||
|
actionFailed = !claudeSuccess;
|
||||||
|
} catch (error) {
|
||||||
|
console.error("Error reading output file:", error);
|
||||||
|
// If we can't read the file, check for any failure markers
|
||||||
|
actionFailed = process.env.CLAUDE_SUCCESS === "false";
|
||||||
}
|
}
|
||||||
|
|
||||||
// Prepare input for updateCommentBody function
|
// Prepare input for updateCommentBody function
|
||||||
@@ -199,7 +189,6 @@ async function run() {
|
|||||||
prLink,
|
prLink,
|
||||||
branchName: shouldDeleteBranch ? undefined : claudeBranch,
|
branchName: shouldDeleteBranch ? undefined : claudeBranch,
|
||||||
triggerUsername,
|
triggerUsername,
|
||||||
errorDetails,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const updatedBody = updateCommentBody(commentInput);
|
const updatedBody = updateCommentBody(commentInput);
|
||||||
|
|||||||
@@ -51,9 +51,8 @@ export async function setupBranch(
|
|||||||
|
|
||||||
const branchName = prData.headRefName;
|
const branchName = prData.headRefName;
|
||||||
|
|
||||||
// Execute git commands to checkout PR branch (shallow fetch for performance)
|
// Execute git commands to checkout PR branch
|
||||||
// Fetch the branch with a depth of 20 to avoid fetching too much history, while still allowing for some context
|
await $`git fetch origin ${branchName}`;
|
||||||
await $`git fetch origin --depth=20 ${branchName}`;
|
|
||||||
await $`git checkout ${branchName}`;
|
await $`git checkout ${branchName}`;
|
||||||
|
|
||||||
console.log(`Successfully checked out PR branch for PR #${entityNumber}`);
|
console.log(`Successfully checked out PR branch for PR #${entityNumber}`);
|
||||||
@@ -99,8 +98,8 @@ export async function setupBranch(
|
|||||||
sha: currentSHA,
|
sha: currentSHA,
|
||||||
});
|
});
|
||||||
|
|
||||||
// Checkout the new branch (shallow fetch for performance)
|
// Checkout the new branch
|
||||||
await $`git fetch origin --depth=1 ${newBranch}`;
|
await $`git fetch origin ${newBranch}`;
|
||||||
await $`git checkout ${newBranch}`;
|
await $`git checkout ${newBranch}`;
|
||||||
|
|
||||||
console.log(
|
console.log(
|
||||||
|
|||||||
@@ -15,7 +15,6 @@ export type CommentUpdateInput = {
|
|||||||
prLink?: string;
|
prLink?: string;
|
||||||
branchName?: string;
|
branchName?: string;
|
||||||
triggerUsername?: string;
|
triggerUsername?: string;
|
||||||
errorDetails?: string;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
export function ensureProperlyEncodedUrl(url: string): string | null {
|
export function ensureProperlyEncodedUrl(url: string): string | null {
|
||||||
@@ -76,7 +75,6 @@ export function updateCommentBody(input: CommentUpdateInput): string {
|
|||||||
actionFailed,
|
actionFailed,
|
||||||
branchName,
|
branchName,
|
||||||
triggerUsername,
|
triggerUsername,
|
||||||
errorDetails,
|
|
||||||
} = input;
|
} = input;
|
||||||
|
|
||||||
// Extract content from the original comment body
|
// Extract content from the original comment body
|
||||||
@@ -179,14 +177,7 @@ export function updateCommentBody(input: CommentUpdateInput): string {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Build the new body with blank line between header and separator
|
// Build the new body with blank line between header and separator
|
||||||
let newBody = `${header}${links}`;
|
let newBody = `${header}${links}\n\n---\n`;
|
||||||
|
|
||||||
// Add error details if available
|
|
||||||
if (actionFailed && errorDetails) {
|
|
||||||
newBody += `\n\n\`\`\`\n${errorDetails}\n\`\`\``;
|
|
||||||
}
|
|
||||||
|
|
||||||
newBody += `\n\n---\n`;
|
|
||||||
|
|
||||||
// Clean up the body content
|
// Clean up the body content
|
||||||
// Remove any existing View job run, branch links from the bottom
|
// Remove any existing View job run, branch links from the bottom
|
||||||
|
|||||||
@@ -39,25 +39,6 @@ describe("updateCommentBody", () => {
|
|||||||
expect(result).toContain("**Claude encountered an error after 45s**");
|
expect(result).toContain("**Claude encountered an error after 45s**");
|
||||||
});
|
});
|
||||||
|
|
||||||
it("includes error details when provided", () => {
|
|
||||||
const input = {
|
|
||||||
...baseInput,
|
|
||||||
currentBody: "Claude Code is working...",
|
|
||||||
actionFailed: true,
|
|
||||||
executionDetails: { duration_ms: 45000 },
|
|
||||||
errorDetails: "Failed to fetch issue data",
|
|
||||||
};
|
|
||||||
|
|
||||||
const result = updateCommentBody(input);
|
|
||||||
expect(result).toContain("**Claude encountered an error after 45s**");
|
|
||||||
expect(result).toContain("[View job]");
|
|
||||||
expect(result).toContain("```\nFailed to fetch issue data\n```");
|
|
||||||
// Ensure error details come after the header/links
|
|
||||||
const errorIndex = result.indexOf("```");
|
|
||||||
const headerIndex = result.indexOf("**Claude encountered an error");
|
|
||||||
expect(errorIndex).toBeGreaterThan(headerIndex);
|
|
||||||
});
|
|
||||||
|
|
||||||
it("handles username extraction from content when not provided", () => {
|
it("handles username extraction from content when not provided", () => {
|
||||||
const input = {
|
const input = {
|
||||||
...baseInput,
|
...baseInput,
|
||||||
|
|||||||
Reference in New Issue
Block a user