mirror of
https://github.com/anthropics/claude-code-action.git
synced 2026-01-23 23:14:13 +08:00
Compare commits
7 Commits
demo/subtl
...
v1.0.5
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
fd2c17f101 | ||
|
|
a4a723b927 | ||
|
|
d22fa6061b | ||
|
|
63f1c772bd | ||
|
|
fb823f6dd6 | ||
|
|
9e9123239f | ||
|
|
791fcb9fd1 |
5
.github/workflows/issue-triage.yml
vendored
5
.github/workflows/issue-triage.yml
vendored
@@ -11,6 +11,7 @@ jobs:
|
||||
permissions:
|
||||
contents: read
|
||||
issues: write
|
||||
id-token: write
|
||||
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
@@ -78,7 +79,7 @@ jobs:
|
||||
4. Select appropriate labels from the available labels list provided above:
|
||||
- Choose labels that accurately reflect the issue's nature
|
||||
- Be specific but comprehensive
|
||||
- Select priority labels if you can determine urgency (high-priority, med-priority, or low-priority)
|
||||
- IMPORTANT: Add a priority label (P1, P2, or P3) based on the label descriptions from gh label list
|
||||
- Consider platform labels (android, ios) if applicable
|
||||
- If you find similar issues using mcp__github__search_issues, consider using a "duplicate" label if appropriate. Only do so if the issue is a duplicate of another OPEN issue.
|
||||
|
||||
@@ -97,7 +98,7 @@ jobs:
|
||||
EOF
|
||||
|
||||
- name: Run Claude Code for Issue Triage
|
||||
uses: anthropics/claude-code-base-action@v1
|
||||
uses: anthropics/claude-code-action@v1
|
||||
with:
|
||||
prompt: $(cat /tmp/claude-prompts/triage-prompt.txt)
|
||||
anthropic_api_key: ${{ secrets.ANTHROPIC_API_KEY }}
|
||||
|
||||
12
action.yml
12
action.yml
@@ -73,6 +73,14 @@ inputs:
|
||||
description: "Enable commit signing using GitHub's commit signature verification. When false, Claude uses standard git commands"
|
||||
required: false
|
||||
default: "false"
|
||||
bot_id:
|
||||
description: "GitHub user ID to use for git operations (defaults to Claude's bot ID)"
|
||||
required: false
|
||||
default: "41898282" # Claude's bot ID - see src/github/constants.ts
|
||||
bot_name:
|
||||
description: "GitHub username to use for git operations (defaults to Claude's bot name)"
|
||||
required: false
|
||||
default: "claude[bot]"
|
||||
track_progress:
|
||||
description: "Force tag mode with tracking comments for pull_request and issue events. Only applicable to pull_request (opened, synchronize, ready_for_review, reopened) and issue (opened, edited, labeled, assigned) events."
|
||||
required: false
|
||||
@@ -144,6 +152,8 @@ runs:
|
||||
USE_STICKY_COMMENT: ${{ inputs.use_sticky_comment }}
|
||||
DEFAULT_WORKFLOW_TOKEN: ${{ github.token }}
|
||||
USE_COMMIT_SIGNING: ${{ inputs.use_commit_signing }}
|
||||
BOT_ID: ${{ inputs.bot_id }}
|
||||
BOT_NAME: ${{ inputs.bot_name }}
|
||||
TRACK_PROGRESS: ${{ inputs.track_progress }}
|
||||
ADDITIONAL_PERMISSIONS: ${{ inputs.additional_permissions }}
|
||||
CLAUDE_ARGS: ${{ inputs.claude_args }}
|
||||
@@ -162,7 +172,7 @@ runs:
|
||||
# Install Claude Code if no custom executable is provided
|
||||
if [ -z "${{ inputs.path_to_claude_code_executable }}" ]; then
|
||||
echo "Installing Claude Code..."
|
||||
curl -fsSL https://claude.ai/install.sh | bash -s 1.0.103
|
||||
curl -fsSL https://claude.ai/install.sh | bash -s 1.0.107
|
||||
echo "$HOME/.local/bin" >> "$GITHUB_PATH"
|
||||
else
|
||||
echo "Using custom Claude Code executable: ${{ inputs.path_to_claude_code_executable }}"
|
||||
|
||||
@@ -99,7 +99,7 @@ runs:
|
||||
run: |
|
||||
if [ -z "${{ inputs.path_to_claude_code_executable }}" ]; then
|
||||
echo "Installing Claude Code..."
|
||||
curl -fsSL https://claude.ai/install.sh | bash -s 1.0.103
|
||||
curl -fsSL https://claude.ai/install.sh | bash -s 1.0.107
|
||||
else
|
||||
echo "Using custom Claude Code executable: ${{ inputs.path_to_claude_code_executable }}"
|
||||
# Add the directory containing the custom executable to PATH
|
||||
|
||||
27
docs/faq.md
27
docs/faq.md
@@ -28,6 +28,33 @@ permissions:
|
||||
|
||||
The OIDC token is required in order for the Claude GitHub app to function. If you wish to not use the GitHub app, you can instead provide a `github_token` input to the action for Claude to operate with. See the [Claude Code permissions documentation][perms] for more.
|
||||
|
||||
### Why am I getting '403 Resource not accessible by integration' errors?
|
||||
|
||||
This error occurs when the action tries to fetch the authenticated user information using a GitHub App installation token. GitHub App tokens have limited access and cannot access the `/user` endpoint, which causes this 403 error.
|
||||
|
||||
**Solution**: The action now includes `bot_id` and `bot_name` inputs that default to Claude's bot credentials. This avoids the need to fetch user information from the API.
|
||||
|
||||
For the default claude[bot]:
|
||||
|
||||
```yaml
|
||||
- uses: anthropics/claude-code-action@v1
|
||||
with:
|
||||
anthropic_api_key: ${{ secrets.ANTHROPIC_API_KEY }}
|
||||
# bot_id and bot_name have sensible defaults, no need to specify
|
||||
```
|
||||
|
||||
For custom bots, specify both:
|
||||
|
||||
```yaml
|
||||
- uses: anthropics/claude-code-action@v1
|
||||
with:
|
||||
anthropic_api_key: ${{ secrets.ANTHROPIC_API_KEY }}
|
||||
bot_id: "12345678" # Your bot's GitHub user ID
|
||||
bot_name: "my-bot" # Your bot's username
|
||||
```
|
||||
|
||||
This issue typically only affects agent/automation mode workflows. Interactive workflows (with @claude mentions) don't encounter this issue as they use the comment author's information.
|
||||
|
||||
## Claude's Capabilities and Limitations
|
||||
|
||||
### Why won't Claude update workflow files when I ask it to?
|
||||
|
||||
@@ -15,7 +15,7 @@ This guide helps you migrate from Claude Code Action v0.x to v1.0. The new versi
|
||||
The following inputs have been deprecated and replaced:
|
||||
|
||||
| Deprecated Input | Replacement | Notes |
|
||||
| --------------------- | -------------------------------- | --------------------------------------------- |
|
||||
| --------------------- | ------------------------------------ | --------------------------------------------- |
|
||||
| `mode` | Auto-detected | Action automatically chooses based on context |
|
||||
| `direct_prompt` | `prompt` | Direct drop-in replacement |
|
||||
| `override_prompt` | `prompt` | Use GitHub context variables instead |
|
||||
@@ -26,6 +26,7 @@ The following inputs have been deprecated and replaced:
|
||||
| `disallowed_tools` | `claude_args: --disallowedTools` | Use CLI format |
|
||||
| `claude_env` | `settings` with env object | Use settings JSON |
|
||||
| `mcp_config` | `claude_args: --mcp-config` | Pass MCP config via CLI arguments |
|
||||
| `timeout_minutes` | Use GitHub Actions `timeout-minutes` | Configure at job level instead of input level |
|
||||
|
||||
## Migration Examples
|
||||
|
||||
@@ -198,6 +199,30 @@ The `track_progress` input only works with these GitHub events:
|
||||
}
|
||||
```
|
||||
|
||||
### Timeout Configuration
|
||||
|
||||
**Before (v0.x):**
|
||||
|
||||
```yaml
|
||||
- uses: anthropics/claude-code-action@beta
|
||||
with:
|
||||
timeout_minutes: 30
|
||||
anthropic_api_key: ${{ secrets.ANTHROPIC_API_KEY }}
|
||||
```
|
||||
|
||||
**After (v1.0):**
|
||||
|
||||
```yaml
|
||||
jobs:
|
||||
claude-task:
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 30 # Moved to job level
|
||||
steps:
|
||||
- uses: anthropics/claude-code-action@v1
|
||||
with:
|
||||
anthropic_api_key: ${{ secrets.ANTHROPIC_API_KEY }}
|
||||
```
|
||||
|
||||
## How Mode Detection Works
|
||||
|
||||
The action now automatically detects the appropriate mode:
|
||||
@@ -312,6 +337,7 @@ You can also pass MCP configuration from a file:
|
||||
- [ ] Convert `disallowed_tools` to `claude_args` with `--disallowedTools`
|
||||
- [ ] Move `claude_env` to `settings` JSON format
|
||||
- [ ] Move `mcp_config` to `claude_args` with `--mcp-config`
|
||||
- [ ] Replace `timeout_minutes` with GitHub Actions `timeout-minutes` at job level
|
||||
- [ ] **Optional**: Add `track_progress: true` if you need tracking comments in automation mode
|
||||
- [ ] Test workflow in a non-production environment
|
||||
|
||||
|
||||
@@ -48,7 +48,7 @@ jobs:
|
||||
## Inputs
|
||||
|
||||
| Input | Description | Required | Default |
|
||||
| ------------------------------ | -------------------------------------------------------------------------------------------------------------------- | -------- | --------- |
|
||||
| ------------------------------ | -------------------------------------------------------------------------------------------------------------------- | -------- | ------------- |
|
||||
| `anthropic_api_key` | Anthropic API key (required for direct API, not needed for Bedrock/Vertex) | No\* | - |
|
||||
| `claude_code_oauth_token` | Claude Code OAuth token (alternative to anthropic_api_key) | No\* | - |
|
||||
| `prompt` | Instructions for Claude. Can be a direct prompt or custom template for automation workflows | No | - |
|
||||
@@ -68,6 +68,8 @@ jobs:
|
||||
| `additional_permissions` | Additional permissions to enable. Currently supports 'actions: read' for viewing workflow results | No | "" |
|
||||
| `experimental_allowed_domains` | Restrict network access to these domains only (newline-separated). | No | "" |
|
||||
| `use_commit_signing` | Enable commit signing using GitHub's commit signature verification. When false, Claude uses standard git commands | No | `false` |
|
||||
| `bot_id` | GitHub user ID to use for git operations (defaults to Claude's bot ID) | No | `41898282` |
|
||||
| `bot_name` | GitHub username to use for git operations (defaults to Claude's bot name) | No | `claude[bot]` |
|
||||
| `allowed_bots` | Comma-separated list of allowed bot usernames, or '\*' to allow all bots. Empty string (default) allows no bots | No | "" |
|
||||
|
||||
### Deprecated Inputs
|
||||
|
||||
13
src/github/constants.ts
Normal file
13
src/github/constants.ts
Normal file
@@ -0,0 +1,13 @@
|
||||
/**
|
||||
* GitHub-related constants used throughout the application
|
||||
*/
|
||||
|
||||
/**
|
||||
* Claude App bot user ID
|
||||
*/
|
||||
export const CLAUDE_APP_BOT_ID = 41898282;
|
||||
|
||||
/**
|
||||
* Claude bot username
|
||||
*/
|
||||
export const CLAUDE_BOT_LOGIN = "claude[bot]";
|
||||
@@ -8,6 +8,7 @@ import type {
|
||||
PullRequestReviewCommentEvent,
|
||||
WorkflowRunEvent,
|
||||
} from "@octokit/webhooks-types";
|
||||
import { CLAUDE_APP_BOT_ID, CLAUDE_BOT_LOGIN } from "./constants";
|
||||
// Custom types for GitHub Actions events that aren't webhooks
|
||||
export type WorkflowDispatchEvent = {
|
||||
action?: never;
|
||||
@@ -74,6 +75,8 @@ type BaseContext = {
|
||||
branchPrefix: string;
|
||||
useStickyComment: boolean;
|
||||
useCommitSigning: boolean;
|
||||
botId: string;
|
||||
botName: string;
|
||||
allowedBots: string;
|
||||
trackProgress: boolean;
|
||||
};
|
||||
@@ -122,6 +125,8 @@ export function parseGitHubContext(): GitHubContext {
|
||||
branchPrefix: process.env.BRANCH_PREFIX ?? "claude/",
|
||||
useStickyComment: process.env.USE_STICKY_COMMENT === "true",
|
||||
useCommitSigning: process.env.USE_COMMIT_SIGNING === "true",
|
||||
botId: process.env.BOT_ID ?? String(CLAUDE_APP_BOT_ID),
|
||||
botName: process.env.BOT_NAME ?? CLAUDE_BOT_LOGIN,
|
||||
allowedBots: process.env.ALLOWED_BOTS ?? "",
|
||||
trackProgress: process.env.TRACK_PROGRESS === "true",
|
||||
},
|
||||
|
||||
@@ -17,7 +17,7 @@ type GitUser = {
|
||||
export async function configureGitAuth(
|
||||
githubToken: string,
|
||||
context: GitHubContext,
|
||||
user: GitUser | null,
|
||||
user: GitUser,
|
||||
) {
|
||||
console.log("Configuring git authentication for non-signing mode");
|
||||
|
||||
@@ -28,20 +28,14 @@ export async function configureGitAuth(
|
||||
? "users.noreply.github.com"
|
||||
: `users.noreply.${serverUrl.hostname}`;
|
||||
|
||||
// Configure git user based on the comment creator
|
||||
// Configure git user
|
||||
console.log("Configuring git user...");
|
||||
if (user) {
|
||||
const botName = user.login;
|
||||
const botId = user.id;
|
||||
console.log(`Setting git user as ${botName}...`);
|
||||
await $`git config user.name "${botName}"`;
|
||||
await $`git config user.email "${botId}+${botName}@${noreplyDomain}"`;
|
||||
console.log(`✓ Set git user as ${botName}`);
|
||||
} else {
|
||||
console.log("No user data in comment, using default bot user");
|
||||
await $`git config user.name "github-actions[bot]"`;
|
||||
await $`git config user.email "41898282+github-actions[bot]@${noreplyDomain}"`;
|
||||
}
|
||||
|
||||
// Remove the authorization header that actions/checkout sets
|
||||
console.log("Removing existing git authentication headers...");
|
||||
|
||||
@@ -1,259 +0,0 @@
|
||||
import { describe, test, expect } from "bun:test";
|
||||
import { checkContainsTrigger } from "../trigger";
|
||||
import type { ParsedGitHubContext } from "../../context";
|
||||
|
||||
describe("Trigger Validation", () => {
|
||||
const createMockContext = (overrides = {}): ParsedGitHubContext => ({
|
||||
eventName: "issue_comment",
|
||||
eventAction: "created",
|
||||
repository: {
|
||||
owner: "test-owner",
|
||||
repo: "test-repo",
|
||||
full_name: "test-owner/test-repo",
|
||||
},
|
||||
actor: "testuser",
|
||||
entityNumber: 42,
|
||||
isPR: false,
|
||||
runId: "test-run-id",
|
||||
inputs: {
|
||||
triggerPhrase: "@claude",
|
||||
assigneeTrigger: "",
|
||||
labelTrigger: "",
|
||||
prompt: "",
|
||||
trackProgress: false,
|
||||
},
|
||||
payload: {
|
||||
comment: {
|
||||
body: "Test comment",
|
||||
id: 12345,
|
||||
},
|
||||
},
|
||||
...overrides,
|
||||
} as ParsedGitHubContext);
|
||||
|
||||
describe("checkContainsTrigger", () => {
|
||||
test("should detect @claude mentions", () => {
|
||||
const context = createMockContext({
|
||||
payload: {
|
||||
comment: { body: "Hey @claude can you fix this?", id: 12345 },
|
||||
},
|
||||
});
|
||||
|
||||
const result = checkContainsTrigger(context);
|
||||
expect(result).toBe(true);
|
||||
});
|
||||
|
||||
test("should detect Claude mentions case-insensitively", () => {
|
||||
// Testing multiple case variations
|
||||
const contexts = [
|
||||
createMockContext({
|
||||
payload: { comment: { body: "Hey @Claude please help", id: 12345 } },
|
||||
}),
|
||||
createMockContext({
|
||||
payload: { comment: { body: "Hey @CLAUDE please help", id: 12345 } },
|
||||
}),
|
||||
createMockContext({
|
||||
payload: { comment: { body: "Hey @ClAuDe please help", id: 12345 } },
|
||||
}),
|
||||
];
|
||||
|
||||
// Note: The actual function is case-sensitive, it looks for exact match
|
||||
contexts.forEach(context => {
|
||||
const result = checkContainsTrigger(context);
|
||||
expect(result).toBe(false); // @claude is case-sensitive
|
||||
});
|
||||
});
|
||||
|
||||
test("should not trigger on partial matches", () => {
|
||||
const context = createMockContext({
|
||||
payload: {
|
||||
comment: { body: "Emailed @claudette about this", id: 12345 },
|
||||
},
|
||||
});
|
||||
|
||||
const result = checkContainsTrigger(context);
|
||||
expect(result).toBe(false);
|
||||
});
|
||||
|
||||
test("should handle claude mentions in code blocks", () => {
|
||||
// Testing mentions inside code blocks - they SHOULD trigger
|
||||
// The regex checks for word boundaries, not markdown context
|
||||
const context = createMockContext({
|
||||
payload: {
|
||||
comment: {
|
||||
body: "Here's an example:\n```\n@claude fix this\n```",
|
||||
id: 12345
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
const result = checkContainsTrigger(context);
|
||||
expect(result).toBe(true); // Mentions in code blocks do trigger
|
||||
});
|
||||
|
||||
test("should detect trigger in issue body for opened events", () => {
|
||||
const context = createMockContext({
|
||||
eventName: "issues",
|
||||
eventAction: "opened",
|
||||
payload: {
|
||||
action: "opened",
|
||||
issue: {
|
||||
body: "@claude implement this feature",
|
||||
title: "New feature",
|
||||
number: 42
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
const result = checkContainsTrigger(context);
|
||||
expect(result).toBe(true);
|
||||
});
|
||||
|
||||
test("should handle multiple mentions", () => {
|
||||
const context = createMockContext({
|
||||
payload: {
|
||||
comment: { body: "@claude and @claude should both work", id: 12345 },
|
||||
},
|
||||
});
|
||||
|
||||
// Multiple mentions in same comment should trigger (only needs one match)
|
||||
const result = checkContainsTrigger(context);
|
||||
expect(result).toBe(true);
|
||||
});
|
||||
|
||||
test("should handle null/undefined comment body", () => {
|
||||
const contextNull = createMockContext({
|
||||
payload: { comment: { body: null } },
|
||||
});
|
||||
const contextUndefined = createMockContext({
|
||||
payload: { comment: { body: undefined } },
|
||||
});
|
||||
|
||||
expect(checkContainsTrigger(contextNull)).toBe(false);
|
||||
expect(checkContainsTrigger(contextUndefined)).toBe(false);
|
||||
});
|
||||
|
||||
test("should handle empty comment body", () => {
|
||||
const context = createMockContext({
|
||||
payload: { comment: { body: "" } },
|
||||
});
|
||||
|
||||
const result = checkContainsTrigger(context);
|
||||
expect(result).toBe(false);
|
||||
});
|
||||
|
||||
test("should detect trigger in pull request body", () => {
|
||||
const context = createMockContext({
|
||||
eventName: "pull_request",
|
||||
eventAction: "opened",
|
||||
isPR: true,
|
||||
payload: {
|
||||
action: "opened",
|
||||
pull_request: {
|
||||
body: "@claude please review this PR",
|
||||
title: "Feature update",
|
||||
number: 42
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
const result = checkContainsTrigger(context);
|
||||
expect(result).toBe(true);
|
||||
});
|
||||
|
||||
test("should detect trigger in pull request review", () => {
|
||||
const context = createMockContext({
|
||||
eventName: "pull_request_review",
|
||||
eventAction: "submitted",
|
||||
isPR: true,
|
||||
payload: {
|
||||
action: "submitted",
|
||||
review: {
|
||||
body: "@claude can you fix this issue?",
|
||||
id: 999
|
||||
},
|
||||
pull_request: {
|
||||
number: 42
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
const result = checkContainsTrigger(context);
|
||||
expect(result).toBe(true);
|
||||
});
|
||||
|
||||
test("should detect trigger when assigned to specified user", () => {
|
||||
const context = createMockContext({
|
||||
eventName: "issues",
|
||||
eventAction: "assigned",
|
||||
inputs: {
|
||||
triggerPhrase: "@claude",
|
||||
assigneeTrigger: "claude-bot",
|
||||
labelTrigger: "",
|
||||
prompt: "",
|
||||
trackProgress: false,
|
||||
},
|
||||
payload: {
|
||||
action: "assigned",
|
||||
issue: {
|
||||
number: 42,
|
||||
body: "Some issue",
|
||||
title: "Title"
|
||||
},
|
||||
assignee: {
|
||||
login: "claude-bot"
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
const result = checkContainsTrigger(context);
|
||||
expect(result).toBe(true);
|
||||
});
|
||||
|
||||
test("should detect trigger when labeled with specified label", () => {
|
||||
const context = createMockContext({
|
||||
eventName: "issues",
|
||||
eventAction: "labeled",
|
||||
inputs: {
|
||||
triggerPhrase: "@claude",
|
||||
assigneeTrigger: "",
|
||||
labelTrigger: "needs-claude",
|
||||
prompt: "",
|
||||
trackProgress: false,
|
||||
},
|
||||
payload: {
|
||||
action: "labeled",
|
||||
issue: {
|
||||
number: 42,
|
||||
body: "Some issue",
|
||||
title: "Title"
|
||||
},
|
||||
label: {
|
||||
name: "needs-claude"
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
const result = checkContainsTrigger(context);
|
||||
expect(result).toBe(true);
|
||||
});
|
||||
|
||||
test("should always trigger when prompt is provided", () => {
|
||||
const context = createMockContext({
|
||||
inputs: {
|
||||
triggerPhrase: "@claude",
|
||||
assigneeTrigger: "",
|
||||
labelTrigger: "",
|
||||
prompt: "Fix all the bugs",
|
||||
trackProgress: false,
|
||||
},
|
||||
payload: {
|
||||
comment: { body: "No trigger phrase here", id: 12345 },
|
||||
},
|
||||
});
|
||||
|
||||
const result = checkContainsTrigger(context);
|
||||
expect(result).toBe(true);
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -77,22 +77,16 @@ export const agentMode: Mode = {
|
||||
return false;
|
||||
},
|
||||
|
||||
async prepare({
|
||||
context,
|
||||
githubToken,
|
||||
octokit,
|
||||
}: ModeOptions): Promise<ModeResult> {
|
||||
async prepare({ context, githubToken }: ModeOptions): Promise<ModeResult> {
|
||||
// Configure git authentication for agent mode (same as tag mode)
|
||||
if (!context.inputs.useCommitSigning) {
|
||||
try {
|
||||
// Get the authenticated user (will be claude[bot] when using Claude App token)
|
||||
const { data: authenticatedUser } =
|
||||
await octokit.rest.users.getAuthenticated();
|
||||
// Use bot_id and bot_name from inputs directly
|
||||
const user = {
|
||||
login: authenticatedUser.login,
|
||||
id: authenticatedUser.id,
|
||||
login: context.inputs.botName,
|
||||
id: parseInt(context.inputs.botId),
|
||||
};
|
||||
|
||||
try {
|
||||
// Use the shared git configuration function
|
||||
await configureGitAuth(githubToken, context, user);
|
||||
} catch (error) {
|
||||
|
||||
@@ -89,8 +89,14 @@ export const tagMode: Mode = {
|
||||
|
||||
// Configure git authentication if not using commit signing
|
||||
if (!context.inputs.useCommitSigning) {
|
||||
// Use bot_id and bot_name from inputs directly
|
||||
const user = {
|
||||
login: context.inputs.botName,
|
||||
id: parseInt(context.inputs.botId),
|
||||
};
|
||||
|
||||
try {
|
||||
await configureGitAuth(githubToken, context, commentData.user);
|
||||
await configureGitAuth(githubToken, context, user);
|
||||
} catch (error) {
|
||||
console.error("Failed to configure git authentication:", error);
|
||||
throw error;
|
||||
|
||||
@@ -2,6 +2,7 @@ import { describe, test, expect, beforeEach, afterEach, spyOn } from "bun:test";
|
||||
import { prepareMcpConfig } from "../src/mcp/install-mcp-server";
|
||||
import * as core from "@actions/core";
|
||||
import type { ParsedGitHubContext } from "../src/github/context";
|
||||
import { CLAUDE_APP_BOT_ID, CLAUDE_BOT_LOGIN } from "../src/github/constants";
|
||||
|
||||
describe("prepareMcpConfig", () => {
|
||||
let consoleInfoSpy: any;
|
||||
@@ -31,6 +32,8 @@ describe("prepareMcpConfig", () => {
|
||||
branchPrefix: "",
|
||||
useStickyComment: false,
|
||||
useCommitSigning: false,
|
||||
botId: String(CLAUDE_APP_BOT_ID),
|
||||
botName: CLAUDE_BOT_LOGIN,
|
||||
allowedBots: "",
|
||||
trackProgress: false,
|
||||
},
|
||||
|
||||
@@ -9,6 +9,7 @@ import type {
|
||||
PullRequestReviewEvent,
|
||||
PullRequestReviewCommentEvent,
|
||||
} from "@octokit/webhooks-types";
|
||||
import { CLAUDE_APP_BOT_ID, CLAUDE_BOT_LOGIN } from "../src/github/constants";
|
||||
|
||||
const defaultInputs = {
|
||||
prompt: "",
|
||||
@@ -18,6 +19,8 @@ const defaultInputs = {
|
||||
branchPrefix: "claude/",
|
||||
useStickyComment: false,
|
||||
useCommitSigning: false,
|
||||
botId: String(CLAUDE_APP_BOT_ID),
|
||||
botName: CLAUDE_BOT_LOGIN,
|
||||
allowedBots: "",
|
||||
trackProgress: false,
|
||||
};
|
||||
|
||||
@@ -1,13 +1,23 @@
|
||||
import { describe, test, expect, beforeEach, afterEach, spyOn } from "bun:test";
|
||||
import {
|
||||
describe,
|
||||
test,
|
||||
expect,
|
||||
beforeEach,
|
||||
afterEach,
|
||||
spyOn,
|
||||
mock,
|
||||
} from "bun:test";
|
||||
import { agentMode } from "../../src/modes/agent";
|
||||
import type { GitHubContext } from "../../src/github/context";
|
||||
import { createMockContext, createMockAutomationContext } from "../mockContext";
|
||||
import * as core from "@actions/core";
|
||||
import * as gitConfig from "../../src/github/operations/git-config";
|
||||
|
||||
describe("Agent Mode", () => {
|
||||
let mockContext: GitHubContext;
|
||||
let exportVariableSpy: any;
|
||||
let setOutputSpy: any;
|
||||
let configureGitAuthSpy: any;
|
||||
|
||||
beforeEach(() => {
|
||||
mockContext = createMockAutomationContext({
|
||||
@@ -17,13 +27,22 @@ describe("Agent Mode", () => {
|
||||
() => {},
|
||||
);
|
||||
setOutputSpy = spyOn(core, "setOutput").mockImplementation(() => {});
|
||||
// Mock configureGitAuth to prevent actual git commands from running
|
||||
configureGitAuthSpy = spyOn(
|
||||
gitConfig,
|
||||
"configureGitAuth",
|
||||
).mockImplementation(async () => {
|
||||
// Do nothing - prevent actual git config modifications
|
||||
});
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
exportVariableSpy?.mockClear();
|
||||
setOutputSpy?.mockClear();
|
||||
configureGitAuthSpy?.mockClear();
|
||||
exportVariableSpy?.mockRestore();
|
||||
setOutputSpy?.mockRestore();
|
||||
configureGitAuthSpy?.mockRestore();
|
||||
});
|
||||
|
||||
test("agent mode has correct properties", () => {
|
||||
@@ -113,7 +132,22 @@ describe("Agent Mode", () => {
|
||||
// Set CLAUDE_ARGS environment variable
|
||||
process.env.CLAUDE_ARGS = "--model claude-sonnet-4 --max-turns 10";
|
||||
|
||||
const mockOctokit = {} as any;
|
||||
const mockOctokit = {
|
||||
rest: {
|
||||
users: {
|
||||
getAuthenticated: mock(() =>
|
||||
Promise.resolve({
|
||||
data: { login: "test-user", id: 12345 },
|
||||
}),
|
||||
),
|
||||
getByUsername: mock(() =>
|
||||
Promise.resolve({
|
||||
data: { login: "test-user", id: 12345 },
|
||||
}),
|
||||
),
|
||||
},
|
||||
},
|
||||
} as any;
|
||||
const result = await agentMode.prepare({
|
||||
context: contextWithCustomArgs,
|
||||
octokit: mockOctokit,
|
||||
@@ -152,7 +186,22 @@ describe("Agent Mode", () => {
|
||||
// In v1-dev, we only have the unified prompt field
|
||||
contextWithPrompts.inputs.prompt = "Custom prompt content";
|
||||
|
||||
const mockOctokit = {} as any;
|
||||
const mockOctokit = {
|
||||
rest: {
|
||||
users: {
|
||||
getAuthenticated: mock(() =>
|
||||
Promise.resolve({
|
||||
data: { login: "test-user", id: 12345 },
|
||||
}),
|
||||
),
|
||||
getByUsername: mock(() =>
|
||||
Promise.resolve({
|
||||
data: { login: "test-user", id: 12345 },
|
||||
}),
|
||||
),
|
||||
},
|
||||
},
|
||||
} as any;
|
||||
await agentMode.prepare({
|
||||
context: contextWithPrompts,
|
||||
octokit: mockOctokit,
|
||||
|
||||
@@ -2,6 +2,7 @@ import { describe, expect, test, spyOn, beforeEach, afterEach } from "bun:test";
|
||||
import * as core from "@actions/core";
|
||||
import { checkWritePermissions } from "../src/github/validation/permissions";
|
||||
import type { ParsedGitHubContext } from "../src/github/context";
|
||||
import { CLAUDE_APP_BOT_ID, CLAUDE_BOT_LOGIN } from "../src/github/constants";
|
||||
|
||||
describe("checkWritePermissions", () => {
|
||||
let coreInfoSpy: any;
|
||||
@@ -67,6 +68,8 @@ describe("checkWritePermissions", () => {
|
||||
branchPrefix: "claude/",
|
||||
useStickyComment: false,
|
||||
useCommitSigning: false,
|
||||
botId: String(CLAUDE_APP_BOT_ID),
|
||||
botName: CLAUDE_BOT_LOGIN,
|
||||
allowedBots: "",
|
||||
trackProgress: false,
|
||||
},
|
||||
|
||||
Reference in New Issue
Block a user