mirror of
https://github.com/anthropics/claude-code-action.git
synced 2026-01-23 15:04:13 +08:00
Compare commits
15 Commits
inigo/add-
...
ashwin/tim
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
6626337d20 | ||
|
|
ed4a4d26bc | ||
|
|
798cf0988d | ||
|
|
8458f4399d | ||
|
|
f9b2917716 | ||
|
|
f092d4cefd | ||
|
|
c2edeab4c3 | ||
|
|
4318310481 | ||
|
|
11571151c4 | ||
|
|
70193f466c | ||
|
|
9db20ef677 | ||
|
|
6902c227aa | ||
|
|
e45f28fae7 | ||
|
|
8c4e1e7eb1 | ||
|
|
906bd89c74 |
2
.github/workflows/claude-review.yml
vendored
2
.github/workflows/claude-review.yml
vendored
@@ -2,7 +2,7 @@ name: PR Review
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
types: [opened, synchronize, ready_for_review, reopened]
|
||||
types: [opened]
|
||||
|
||||
jobs:
|
||||
review:
|
||||
|
||||
72
.github/workflows/test-structured-output.yml
vendored
72
.github/workflows/test-structured-output.yml
vendored
@@ -30,19 +30,10 @@ jobs:
|
||||
- number_field: 42
|
||||
- boolean_true: true
|
||||
- boolean_false: false
|
||||
json_schema: |
|
||||
{
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"text_field": {"type": "string"},
|
||||
"number_field": {"type": "number"},
|
||||
"boolean_true": {"type": "boolean"},
|
||||
"boolean_false": {"type": "boolean"}
|
||||
},
|
||||
"required": ["text_field", "number_field", "boolean_true", "boolean_false"]
|
||||
}
|
||||
anthropic_api_key: ${{ secrets.ANTHROPIC_API_KEY }}
|
||||
allowed_tools: "Bash"
|
||||
claude_args: |
|
||||
--allowedTools Bash
|
||||
--json-schema '{"type":"object","properties":{"text_field":{"type":"string"},"number_field":{"type":"number"},"boolean_true":{"type":"boolean"},"boolean_false":{"type":"boolean"}},"required":["text_field","number_field","boolean_true","boolean_false"]}'
|
||||
|
||||
- name: Verify outputs
|
||||
run: |
|
||||
@@ -97,21 +88,10 @@ jobs:
|
||||
- items: ["apple", "banana", "cherry"]
|
||||
- config: {"key": "value", "count": 3}
|
||||
- empty_array: []
|
||||
json_schema: |
|
||||
{
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"items": {
|
||||
"type": "array",
|
||||
"items": {"type": "string"}
|
||||
},
|
||||
"config": {"type": "object"},
|
||||
"empty_array": {"type": "array"}
|
||||
},
|
||||
"required": ["items", "config", "empty_array"]
|
||||
}
|
||||
anthropic_api_key: ${{ secrets.ANTHROPIC_API_KEY }}
|
||||
allowed_tools: "Bash"
|
||||
claude_args: |
|
||||
--allowedTools Bash
|
||||
--json-schema '{"type":"object","properties":{"items":{"type":"array","items":{"type":"string"}},"config":{"type":"object"},"empty_array":{"type":"array"}},"required":["items","config","empty_array"]}'
|
||||
|
||||
- name: Verify JSON stringification
|
||||
run: |
|
||||
@@ -160,19 +140,10 @@ jobs:
|
||||
- empty_string: ""
|
||||
- negative: -5
|
||||
- decimal: 3.14
|
||||
json_schema: |
|
||||
{
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"zero": {"type": "number"},
|
||||
"empty_string": {"type": "string"},
|
||||
"negative": {"type": "number"},
|
||||
"decimal": {"type": "number"}
|
||||
},
|
||||
"required": ["zero", "empty_string", "negative", "decimal"]
|
||||
}
|
||||
anthropic_api_key: ${{ secrets.ANTHROPIC_API_KEY }}
|
||||
allowed_tools: "Bash"
|
||||
claude_args: |
|
||||
--allowedTools Bash
|
||||
--json-schema '{"type":"object","properties":{"zero":{"type":"number"},"empty_string":{"type":"string"},"negative":{"type":"number"},"decimal":{"type":"number"}},"required":["zero","empty_string","negative","decimal"]}'
|
||||
|
||||
- name: Verify edge cases
|
||||
run: |
|
||||
@@ -223,17 +194,10 @@ jobs:
|
||||
prompt: |
|
||||
Run: echo "test"
|
||||
Return EXACTLY: {test-result: "passed", item_count: 10}
|
||||
json_schema: |
|
||||
{
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"test-result": {"type": "string"},
|
||||
"item_count": {"type": "number"}
|
||||
},
|
||||
"required": ["test-result", "item_count"]
|
||||
}
|
||||
anthropic_api_key: ${{ secrets.ANTHROPIC_API_KEY }}
|
||||
allowed_tools: "Bash"
|
||||
claude_args: |
|
||||
--allowedTools Bash
|
||||
--json-schema '{"type":"object","properties":{"test-result":{"type":"string"},"item_count":{"type":"number"}},"required":["test-result","item_count"]}'
|
||||
|
||||
- name: Verify sanitized names work
|
||||
run: |
|
||||
@@ -268,16 +232,10 @@ jobs:
|
||||
uses: ./base-action
|
||||
with:
|
||||
prompt: "Run: echo 'complete'. Return: {done: true}"
|
||||
json_schema: |
|
||||
{
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"done": {"type": "boolean"}
|
||||
},
|
||||
"required": ["done"]
|
||||
}
|
||||
anthropic_api_key: ${{ secrets.ANTHROPIC_API_KEY }}
|
||||
allowed_tools: "Bash"
|
||||
claude_args: |
|
||||
--allowedTools Bash
|
||||
--json-schema '{"type":"object","properties":{"done":{"type":"boolean"}},"required":["done"]}'
|
||||
|
||||
- name: Verify execution file contains structured_output
|
||||
run: |
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
# Claude Code Action
|
||||
|
||||
A general-purpose [Claude Code](https://claude.ai/code) action for GitHub PRs and issues that can answer questions and implement code changes. This action intelligently detects when to activate based on your workflow context—whether responding to @claude mentions, issue assignments, or executing automation tasks with explicit prompts. It supports multiple authentication methods including Anthropic direct API, Amazon Bedrock, and Google Vertex AI.
|
||||
A general-purpose [Claude Code](https://claude.ai/code) action for GitHub PRs and issues that can answer questions and implement code changes. This action intelligently detects when to activate based on your workflow context—whether responding to @claude mentions, issue assignments, or executing automation tasks with explicit prompts. It supports multiple authentication methods including Anthropic direct API, Amazon Bedrock, Google Vertex AI, and Microsoft Foundry.
|
||||
|
||||
## Features
|
||||
|
||||
@@ -30,7 +30,7 @@ This command will guide you through setting up the GitHub app and required secre
|
||||
**Note**:
|
||||
|
||||
- You must be a repository admin to install the GitHub app and add secrets
|
||||
- This quickstart method is only available for direct Anthropic API users. For AWS Bedrock or Google Vertex AI setup, see [docs/cloud-providers.md](./docs/cloud-providers.md).
|
||||
- This quickstart method is only available for direct Anthropic API users. For AWS Bedrock, Google Vertex AI, or Microsoft Foundry setup, see [docs/cloud-providers.md](./docs/cloud-providers.md).
|
||||
|
||||
## 📚 Solutions & Use Cases
|
||||
|
||||
@@ -57,7 +57,7 @@ Each solution includes complete working examples, configuration details, and exp
|
||||
- [Custom Automations](./docs/custom-automations.md) - Examples of automated workflows and custom prompts
|
||||
- [Configuration](./docs/configuration.md) - MCP servers, permissions, environment variables, and advanced settings
|
||||
- [Experimental Features](./docs/experimental.md) - Execution modes and network restrictions
|
||||
- [Cloud Providers](./docs/cloud-providers.md) - AWS Bedrock and Google Vertex AI setup
|
||||
- [Cloud Providers](./docs/cloud-providers.md) - AWS Bedrock, Google Vertex AI, and Microsoft Foundry setup
|
||||
- [Capabilities & Limitations](./docs/capabilities-and-limitations.md) - What Claude can and cannot do
|
||||
- [Security](./docs/security.md) - Access control, permissions, and commit signing
|
||||
- [FAQ](./docs/faq.md) - Common questions and troubleshooting
|
||||
|
||||
50
action.yml
50
action.yml
@@ -44,7 +44,7 @@ inputs:
|
||||
|
||||
# Auth configuration
|
||||
anthropic_api_key:
|
||||
description: "Anthropic API key (required for direct API, not needed for Bedrock/Vertex)"
|
||||
description: "Anthropic API key (required for direct API, not needed for Bedrock/Vertex/Foundry)"
|
||||
required: false
|
||||
claude_code_oauth_token:
|
||||
description: "Claude Code OAuth token (alternative to anthropic_api_key)"
|
||||
@@ -60,6 +60,10 @@ inputs:
|
||||
description: "Use Google Vertex AI with OIDC authentication instead of direct Anthropic API"
|
||||
required: false
|
||||
default: "false"
|
||||
use_foundry:
|
||||
description: "Use Microsoft Foundry with OIDC authentication instead of direct Anthropic API"
|
||||
required: false
|
||||
default: "false"
|
||||
|
||||
claude_args:
|
||||
description: "Additional arguments to pass directly to Claude CLI"
|
||||
@@ -113,10 +117,6 @@ inputs:
|
||||
description: "Newline-separated list of Claude Code plugin marketplace Git URLs to install from (e.g., 'https://github.com/user/marketplace1.git\nhttps://github.com/user/marketplace2.git')"
|
||||
required: false
|
||||
default: ""
|
||||
json_schema:
|
||||
description: "JSON schema for structured output validation. When provided, Claude will return validated JSON matching this schema. All fields are available in the structured_output output as a JSON string (use fromJSON() or jq to access fields)."
|
||||
required: false
|
||||
default: ""
|
||||
|
||||
outputs:
|
||||
execution_file:
|
||||
@@ -128,6 +128,9 @@ outputs:
|
||||
github_token:
|
||||
description: "The GitHub token used by the action (Claude App token if available)"
|
||||
value: ${{ steps.prepare.outputs.github_token }}
|
||||
structured_output:
|
||||
description: "JSON string containing all structured output fields when --json-schema is provided in claude_args. Use fromJSON() to parse: fromJSON(steps.id.outputs.structured_output).field_name"
|
||||
value: ${{ steps.claude-code.outputs.structured_output }}
|
||||
|
||||
runs:
|
||||
using: "composite"
|
||||
@@ -178,7 +181,6 @@ runs:
|
||||
TRACK_PROGRESS: ${{ inputs.track_progress }}
|
||||
ADDITIONAL_PERMISSIONS: ${{ inputs.additional_permissions }}
|
||||
CLAUDE_ARGS: ${{ inputs.claude_args }}
|
||||
JSON_SCHEMA: ${{ inputs.json_schema }}
|
||||
ALL_INPUTS: ${{ toJson(inputs) }}
|
||||
|
||||
- name: Install Base Action Dependencies
|
||||
@@ -193,8 +195,30 @@ 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 2.0.42
|
||||
CLAUDE_CODE_VERSION="2.0.50"
|
||||
echo "Installing Claude Code v${CLAUDE_CODE_VERSION}..."
|
||||
for attempt in 1 2 3; do
|
||||
echo "Installation attempt $attempt..."
|
||||
# Cross-platform timeout (GNU timeout not available on macOS)
|
||||
(curl -fsSL https://claude.ai/install.sh | bash -s -- "$CLAUDE_CODE_VERSION") &
|
||||
install_pid=$!
|
||||
( sleep 120; kill $install_pid 2>/dev/null ) &
|
||||
timeout_pid=$!
|
||||
if wait $install_pid 2>/dev/null; then
|
||||
kill $timeout_pid 2>/dev/null || true
|
||||
wait $timeout_pid 2>/dev/null || true
|
||||
echo "Claude Code installed successfully"
|
||||
break
|
||||
fi
|
||||
kill $timeout_pid 2>/dev/null || true
|
||||
wait $timeout_pid 2>/dev/null || true
|
||||
if [ $attempt -eq 3 ]; then
|
||||
echo "Failed to install Claude Code after 3 attempts"
|
||||
exit 1
|
||||
fi
|
||||
echo "Installation timed out or failed, retrying..."
|
||||
sleep 5
|
||||
done
|
||||
echo "$HOME/.local/bin" >> "$GITHUB_PATH"
|
||||
else
|
||||
echo "Using custom Claude Code executable: ${{ inputs.path_to_claude_code_executable }}"
|
||||
@@ -233,7 +257,6 @@ runs:
|
||||
INPUT_SHOW_FULL_OUTPUT: ${{ inputs.show_full_output }}
|
||||
INPUT_PLUGINS: ${{ inputs.plugins }}
|
||||
INPUT_PLUGIN_MARKETPLACES: ${{ inputs.plugin_marketplaces }}
|
||||
JSON_SCHEMA: ${{ inputs.json_schema }}
|
||||
|
||||
# Model configuration
|
||||
GITHUB_TOKEN: ${{ steps.prepare.outputs.GITHUB_TOKEN }}
|
||||
@@ -247,12 +270,14 @@ runs:
|
||||
ANTHROPIC_CUSTOM_HEADERS: ${{ env.ANTHROPIC_CUSTOM_HEADERS }}
|
||||
CLAUDE_CODE_USE_BEDROCK: ${{ inputs.use_bedrock == 'true' && '1' || '' }}
|
||||
CLAUDE_CODE_USE_VERTEX: ${{ inputs.use_vertex == 'true' && '1' || '' }}
|
||||
CLAUDE_CODE_USE_FOUNDRY: ${{ inputs.use_foundry == 'true' && '1' || '' }}
|
||||
|
||||
# AWS configuration
|
||||
AWS_REGION: ${{ env.AWS_REGION }}
|
||||
AWS_ACCESS_KEY_ID: ${{ env.AWS_ACCESS_KEY_ID }}
|
||||
AWS_SECRET_ACCESS_KEY: ${{ env.AWS_SECRET_ACCESS_KEY }}
|
||||
AWS_SESSION_TOKEN: ${{ env.AWS_SESSION_TOKEN }}
|
||||
AWS_BEARER_TOKEN_BEDROCK: ${{ env.AWS_BEARER_TOKEN_BEDROCK }}
|
||||
ANTHROPIC_BEDROCK_BASE_URL: ${{ env.ANTHROPIC_BEDROCK_BASE_URL || (env.AWS_REGION && format('https://bedrock-runtime.{0}.amazonaws.com', env.AWS_REGION)) }}
|
||||
|
||||
# GCP configuration
|
||||
@@ -266,6 +291,13 @@ runs:
|
||||
VERTEX_REGION_CLAUDE_3_5_SONNET: ${{ env.VERTEX_REGION_CLAUDE_3_5_SONNET }}
|
||||
VERTEX_REGION_CLAUDE_3_7_SONNET: ${{ env.VERTEX_REGION_CLAUDE_3_7_SONNET }}
|
||||
|
||||
# Microsoft Foundry configuration
|
||||
ANTHROPIC_FOUNDRY_RESOURCE: ${{ env.ANTHROPIC_FOUNDRY_RESOURCE }}
|
||||
ANTHROPIC_FOUNDRY_BASE_URL: ${{ env.ANTHROPIC_FOUNDRY_BASE_URL }}
|
||||
ANTHROPIC_DEFAULT_SONNET_MODEL: ${{ env.ANTHROPIC_DEFAULT_SONNET_MODEL }}
|
||||
ANTHROPIC_DEFAULT_HAIKU_MODEL: ${{ env.ANTHROPIC_DEFAULT_HAIKU_MODEL }}
|
||||
ANTHROPIC_DEFAULT_OPUS_MODEL: ${{ env.ANTHROPIC_DEFAULT_OPUS_MODEL }}
|
||||
|
||||
- name: Update comment with job link
|
||||
if: steps.prepare.outputs.contains_trigger == 'true' && steps.prepare.outputs.claude_comment_id && always()
|
||||
shell: bash
|
||||
|
||||
@@ -24,10 +24,6 @@ inputs:
|
||||
description: "Additional arguments to pass directly to Claude CLI (e.g., '--max-turns 3 --mcp-config /path/to/config.json')"
|
||||
required: false
|
||||
default: ""
|
||||
allowed_tools:
|
||||
description: "Comma-separated list of allowed tools (e.g., 'Read,Write,Bash'). Passed as --allowedTools to Claude CLI"
|
||||
required: false
|
||||
default: ""
|
||||
|
||||
# Authentication settings
|
||||
anthropic_api_key:
|
||||
@@ -46,6 +42,10 @@ inputs:
|
||||
description: "Use Google Vertex AI with OIDC authentication instead of direct Anthropic API"
|
||||
required: false
|
||||
default: "false"
|
||||
use_foundry:
|
||||
description: "Use Microsoft Foundry with OIDC authentication instead of direct Anthropic API"
|
||||
required: false
|
||||
default: "false"
|
||||
|
||||
use_node_cache:
|
||||
description: "Whether to use Node.js dependency caching (set to true only for Node.js projects with lock files)"
|
||||
@@ -71,14 +71,6 @@ inputs:
|
||||
description: "Newline-separated list of Claude Code plugin marketplace Git URLs to install from (e.g., 'https://github.com/user/marketplace1.git\nhttps://github.com/user/marketplace2.git')"
|
||||
required: false
|
||||
default: ""
|
||||
json_schema:
|
||||
description: |
|
||||
JSON schema for structured output validation. Claude must return JSON matching this schema
|
||||
or the action will fail. All fields are returned in a single structured_output JSON string.
|
||||
|
||||
Access outputs via: fromJSON(steps.<step-id>.outputs.structured_output).<field_name>
|
||||
required: false
|
||||
default: ""
|
||||
|
||||
outputs:
|
||||
conclusion:
|
||||
@@ -88,7 +80,7 @@ outputs:
|
||||
description: "Path to the JSON file containing Claude Code execution log"
|
||||
value: ${{ steps.run_claude.outputs.execution_file }}
|
||||
structured_output:
|
||||
description: "JSON string containing all structured output fields (use fromJSON() or jq to parse)"
|
||||
description: "JSON string containing all structured output fields when --json-schema is provided in claude_args (use fromJSON() or jq to parse)"
|
||||
value: ${{ steps.run_claude.outputs.structured_output }}
|
||||
|
||||
runs:
|
||||
@@ -125,8 +117,30 @@ runs:
|
||||
shell: bash
|
||||
run: |
|
||||
if [ -z "${{ inputs.path_to_claude_code_executable }}" ]; then
|
||||
echo "Installing Claude Code..."
|
||||
curl -fsSL https://claude.ai/install.sh | bash -s 2.0.45
|
||||
CLAUDE_CODE_VERSION="2.0.50"
|
||||
echo "Installing Claude Code v${CLAUDE_CODE_VERSION}..."
|
||||
for attempt in 1 2 3; do
|
||||
echo "Installation attempt $attempt..."
|
||||
# Cross-platform timeout (GNU timeout not available on macOS)
|
||||
(curl -fsSL https://claude.ai/install.sh | bash -s -- "$CLAUDE_CODE_VERSION") &
|
||||
install_pid=$!
|
||||
( sleep 120; kill $install_pid 2>/dev/null ) &
|
||||
timeout_pid=$!
|
||||
if wait $install_pid 2>/dev/null; then
|
||||
kill $timeout_pid 2>/dev/null || true
|
||||
wait $timeout_pid 2>/dev/null || true
|
||||
echo "Claude Code installed successfully"
|
||||
break
|
||||
fi
|
||||
kill $timeout_pid 2>/dev/null || true
|
||||
wait $timeout_pid 2>/dev/null || true
|
||||
if [ $attempt -eq 3 ]; then
|
||||
echo "Failed to install Claude Code after 3 attempts"
|
||||
exit 1
|
||||
fi
|
||||
echo "Installation timed out or failed, retrying..."
|
||||
sleep 5
|
||||
done
|
||||
else
|
||||
echo "Using custom Claude Code executable: ${{ inputs.path_to_claude_code_executable }}"
|
||||
# Add the directory containing the custom executable to PATH
|
||||
@@ -156,8 +170,6 @@ runs:
|
||||
INPUT_SHOW_FULL_OUTPUT: ${{ inputs.show_full_output }}
|
||||
INPUT_PLUGINS: ${{ inputs.plugins }}
|
||||
INPUT_PLUGIN_MARKETPLACES: ${{ inputs.plugin_marketplaces }}
|
||||
INPUT_ALLOWED_TOOLS: ${{ inputs.allowed_tools }}
|
||||
JSON_SCHEMA: ${{ inputs.json_schema }}
|
||||
|
||||
# Provider configuration
|
||||
ANTHROPIC_API_KEY: ${{ inputs.anthropic_api_key }}
|
||||
@@ -167,12 +179,14 @@ runs:
|
||||
# Only set provider flags if explicitly true, since any value (including "false") is truthy
|
||||
CLAUDE_CODE_USE_BEDROCK: ${{ inputs.use_bedrock == 'true' && '1' || '' }}
|
||||
CLAUDE_CODE_USE_VERTEX: ${{ inputs.use_vertex == 'true' && '1' || '' }}
|
||||
CLAUDE_CODE_USE_FOUNDRY: ${{ inputs.use_foundry == 'true' && '1' || '' }}
|
||||
|
||||
# AWS configuration
|
||||
AWS_REGION: ${{ env.AWS_REGION }}
|
||||
AWS_ACCESS_KEY_ID: ${{ env.AWS_ACCESS_KEY_ID }}
|
||||
AWS_SECRET_ACCESS_KEY: ${{ env.AWS_SECRET_ACCESS_KEY }}
|
||||
AWS_SESSION_TOKEN: ${{ env.AWS_SESSION_TOKEN }}
|
||||
AWS_BEARER_TOKEN_BEDROCK: ${{ env.AWS_BEARER_TOKEN_BEDROCK }}
|
||||
ANTHROPIC_BEDROCK_BASE_URL: ${{ env.ANTHROPIC_BEDROCK_BASE_URL || (env.AWS_REGION && format('https://bedrock-runtime.{0}.amazonaws.com', env.AWS_REGION)) }}
|
||||
|
||||
# GCP configuration
|
||||
@@ -180,3 +194,10 @@ runs:
|
||||
CLOUD_ML_REGION: ${{ env.CLOUD_ML_REGION }}
|
||||
GOOGLE_APPLICATION_CREDENTIALS: ${{ env.GOOGLE_APPLICATION_CREDENTIALS }}
|
||||
ANTHROPIC_VERTEX_BASE_URL: ${{ env.ANTHROPIC_VERTEX_BASE_URL }}
|
||||
|
||||
# Microsoft Foundry configuration
|
||||
ANTHROPIC_FOUNDRY_RESOURCE: ${{ env.ANTHROPIC_FOUNDRY_RESOURCE }}
|
||||
ANTHROPIC_FOUNDRY_BASE_URL: ${{ env.ANTHROPIC_FOUNDRY_BASE_URL }}
|
||||
ANTHROPIC_DEFAULT_SONNET_MODEL: ${{ env.ANTHROPIC_DEFAULT_SONNET_MODEL }}
|
||||
ANTHROPIC_DEFAULT_HAIKU_MODEL: ${{ env.ANTHROPIC_DEFAULT_HAIKU_MODEL }}
|
||||
ANTHROPIC_DEFAULT_OPUS_MODEL: ${{ env.ANTHROPIC_DEFAULT_OPUS_MODEL }}
|
||||
|
||||
@@ -28,22 +28,8 @@ async function run() {
|
||||
promptFile: process.env.INPUT_PROMPT_FILE || "",
|
||||
});
|
||||
|
||||
// Build claudeArgs with JSON schema if provided
|
||||
let claudeArgs = process.env.INPUT_CLAUDE_ARGS || "";
|
||||
|
||||
// Add allowed tools if specified
|
||||
if (process.env.INPUT_ALLOWED_TOOLS) {
|
||||
claudeArgs += ` --allowedTools "${process.env.INPUT_ALLOWED_TOOLS}"`;
|
||||
}
|
||||
|
||||
// Add JSON schema if specified (no escaping - parseShellArgs handles it)
|
||||
if (process.env.JSON_SCHEMA) {
|
||||
// Wrap in single quotes for parseShellArgs
|
||||
claudeArgs += ` --json-schema '${process.env.JSON_SCHEMA}'`;
|
||||
}
|
||||
|
||||
await runClaude(promptConfig.path, {
|
||||
claudeArgs: claudeArgs.trim(),
|
||||
claudeArgs: process.env.INPUT_CLAUDE_ARGS,
|
||||
allowedTools: process.env.INPUT_ALLOWED_TOOLS,
|
||||
disallowedTools: process.env.INPUT_DISALLOWED_TOOLS,
|
||||
maxTurns: process.env.INPUT_MAX_TURNS,
|
||||
|
||||
@@ -124,7 +124,7 @@ export function prepareRunConfig(
|
||||
|
||||
/**
|
||||
* Parses structured_output from execution file and sets GitHub Action outputs
|
||||
* Only runs if json_schema was explicitly provided by the user
|
||||
* Only runs if --json-schema was explicitly provided in claude_args
|
||||
* Exported for testing
|
||||
*/
|
||||
export async function parseAndSetStructuredOutputs(
|
||||
@@ -144,7 +144,7 @@ export async function parseAndSetStructuredOutputs(
|
||||
|
||||
if (!result?.structured_output) {
|
||||
throw new Error(
|
||||
`json_schema was provided but Claude did not return structured_output.\n` +
|
||||
`--json-schema was provided but Claude did not return structured_output.\n` +
|
||||
`Found ${messages.length} messages. Result exists: ${!!result}\n`,
|
||||
);
|
||||
}
|
||||
@@ -167,6 +167,9 @@ export async function parseAndSetStructuredOutputs(
|
||||
export async function runClaude(promptPath: string, options: ClaudeOptions) {
|
||||
const config = prepareRunConfig(promptPath, options);
|
||||
|
||||
// Detect if --json-schema is present in claude args
|
||||
const hasJsonSchema = options.claudeArgs?.includes("--json-schema") ?? false;
|
||||
|
||||
// Create a named pipe
|
||||
try {
|
||||
await unlink(PIPE_PATH);
|
||||
@@ -352,8 +355,8 @@ export async function runClaude(promptPath: string, options: ClaudeOptions) {
|
||||
|
||||
core.setOutput("execution_file", EXECUTION_FILE);
|
||||
|
||||
// Parse and set structured outputs only if user provided json_schema
|
||||
if (process.env.JSON_SCHEMA) {
|
||||
// Parse and set structured outputs only if user provided --json-schema in claude_args
|
||||
if (hasJsonSchema) {
|
||||
try {
|
||||
await parseAndSetStructuredOutputs(EXECUTION_FILE);
|
||||
} catch (error) {
|
||||
|
||||
@@ -1,39 +1,50 @@
|
||||
/**
|
||||
* Validates the environment variables required for running Claude Code
|
||||
* based on the selected provider (Anthropic API, AWS Bedrock, or Google Vertex AI)
|
||||
* based on the selected provider (Anthropic API, AWS Bedrock, Google Vertex AI, or Microsoft Foundry)
|
||||
*/
|
||||
export function validateEnvironmentVariables() {
|
||||
const useBedrock = process.env.CLAUDE_CODE_USE_BEDROCK === "1";
|
||||
const useVertex = process.env.CLAUDE_CODE_USE_VERTEX === "1";
|
||||
const useFoundry = process.env.CLAUDE_CODE_USE_FOUNDRY === "1";
|
||||
const anthropicApiKey = process.env.ANTHROPIC_API_KEY;
|
||||
const claudeCodeOAuthToken = process.env.CLAUDE_CODE_OAUTH_TOKEN;
|
||||
|
||||
const errors: string[] = [];
|
||||
|
||||
if (useBedrock && useVertex) {
|
||||
// Check for mutual exclusivity between providers
|
||||
const activeProviders = [useBedrock, useVertex, useFoundry].filter(Boolean);
|
||||
if (activeProviders.length > 1) {
|
||||
errors.push(
|
||||
"Cannot use both Bedrock and Vertex AI simultaneously. Please set only one provider.",
|
||||
"Cannot use multiple providers simultaneously. Please set only one of: CLAUDE_CODE_USE_BEDROCK, CLAUDE_CODE_USE_VERTEX, or CLAUDE_CODE_USE_FOUNDRY.",
|
||||
);
|
||||
}
|
||||
|
||||
if (!useBedrock && !useVertex) {
|
||||
if (!useBedrock && !useVertex && !useFoundry) {
|
||||
if (!anthropicApiKey && !claudeCodeOAuthToken) {
|
||||
errors.push(
|
||||
"Either ANTHROPIC_API_KEY or CLAUDE_CODE_OAUTH_TOKEN is required when using direct Anthropic API.",
|
||||
);
|
||||
}
|
||||
} else if (useBedrock) {
|
||||
const requiredBedrockVars = {
|
||||
AWS_REGION: process.env.AWS_REGION,
|
||||
AWS_ACCESS_KEY_ID: process.env.AWS_ACCESS_KEY_ID,
|
||||
AWS_SECRET_ACCESS_KEY: process.env.AWS_SECRET_ACCESS_KEY,
|
||||
};
|
||||
const awsRegion = process.env.AWS_REGION;
|
||||
const awsAccessKeyId = process.env.AWS_ACCESS_KEY_ID;
|
||||
const awsSecretAccessKey = process.env.AWS_SECRET_ACCESS_KEY;
|
||||
const awsBearerToken = process.env.AWS_BEARER_TOKEN_BEDROCK;
|
||||
|
||||
Object.entries(requiredBedrockVars).forEach(([key, value]) => {
|
||||
if (!value) {
|
||||
errors.push(`${key} is required when using AWS Bedrock.`);
|
||||
}
|
||||
});
|
||||
// AWS_REGION is always required for Bedrock
|
||||
if (!awsRegion) {
|
||||
errors.push("AWS_REGION is required when using AWS Bedrock.");
|
||||
}
|
||||
|
||||
// Either bearer token OR access key credentials must be provided
|
||||
const hasAccessKeyCredentials = awsAccessKeyId && awsSecretAccessKey;
|
||||
const hasBearerToken = awsBearerToken;
|
||||
|
||||
if (!hasAccessKeyCredentials && !hasBearerToken) {
|
||||
errors.push(
|
||||
"Either AWS_BEARER_TOKEN_BEDROCK or both AWS_ACCESS_KEY_ID and AWS_SECRET_ACCESS_KEY are required when using AWS Bedrock.",
|
||||
);
|
||||
}
|
||||
} else if (useVertex) {
|
||||
const requiredVertexVars = {
|
||||
ANTHROPIC_VERTEX_PROJECT_ID: process.env.ANTHROPIC_VERTEX_PROJECT_ID,
|
||||
@@ -45,6 +56,16 @@ export function validateEnvironmentVariables() {
|
||||
errors.push(`${key} is required when using Google Vertex AI.`);
|
||||
}
|
||||
});
|
||||
} else if (useFoundry) {
|
||||
const foundryResource = process.env.ANTHROPIC_FOUNDRY_RESOURCE;
|
||||
const foundryBaseUrl = process.env.ANTHROPIC_FOUNDRY_BASE_URL;
|
||||
|
||||
// Either resource name or base URL is required
|
||||
if (!foundryResource && !foundryBaseUrl) {
|
||||
errors.push(
|
||||
"Either ANTHROPIC_FOUNDRY_RESOURCE or ANTHROPIC_FOUNDRY_BASE_URL is required when using Microsoft Foundry.",
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
if (errors.length > 0) {
|
||||
|
||||
@@ -113,7 +113,7 @@ describe("parseAndSetStructuredOutputs", () => {
|
||||
await expect(
|
||||
parseAndSetStructuredOutputs(TEST_EXECUTION_FILE),
|
||||
).rejects.toThrow(
|
||||
"json_schema was provided but Claude did not return structured_output",
|
||||
"--json-schema was provided but Claude did not return structured_output",
|
||||
);
|
||||
});
|
||||
|
||||
@@ -127,7 +127,7 @@ describe("parseAndSetStructuredOutputs", () => {
|
||||
await expect(
|
||||
parseAndSetStructuredOutputs(TEST_EXECUTION_FILE),
|
||||
).rejects.toThrow(
|
||||
"json_schema was provided but Claude did not return structured_output",
|
||||
"--json-schema was provided but Claude did not return structured_output",
|
||||
);
|
||||
});
|
||||
|
||||
|
||||
@@ -13,15 +13,19 @@ describe("validateEnvironmentVariables", () => {
|
||||
delete process.env.ANTHROPIC_API_KEY;
|
||||
delete process.env.CLAUDE_CODE_USE_BEDROCK;
|
||||
delete process.env.CLAUDE_CODE_USE_VERTEX;
|
||||
delete process.env.CLAUDE_CODE_USE_FOUNDRY;
|
||||
delete process.env.AWS_REGION;
|
||||
delete process.env.AWS_ACCESS_KEY_ID;
|
||||
delete process.env.AWS_SECRET_ACCESS_KEY;
|
||||
delete process.env.AWS_SESSION_TOKEN;
|
||||
delete process.env.AWS_BEARER_TOKEN_BEDROCK;
|
||||
delete process.env.ANTHROPIC_BEDROCK_BASE_URL;
|
||||
delete process.env.ANTHROPIC_VERTEX_PROJECT_ID;
|
||||
delete process.env.CLOUD_ML_REGION;
|
||||
delete process.env.GOOGLE_APPLICATION_CREDENTIALS;
|
||||
delete process.env.ANTHROPIC_VERTEX_BASE_URL;
|
||||
delete process.env.ANTHROPIC_FOUNDRY_RESOURCE;
|
||||
delete process.env.ANTHROPIC_FOUNDRY_BASE_URL;
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
@@ -92,31 +96,58 @@ describe("validateEnvironmentVariables", () => {
|
||||
);
|
||||
});
|
||||
|
||||
test("should fail when AWS_ACCESS_KEY_ID is missing", () => {
|
||||
test("should fail when only AWS_SECRET_ACCESS_KEY is provided without bearer token", () => {
|
||||
process.env.CLAUDE_CODE_USE_BEDROCK = "1";
|
||||
process.env.AWS_REGION = "us-east-1";
|
||||
process.env.AWS_SECRET_ACCESS_KEY = "test-secret-key";
|
||||
|
||||
expect(() => validateEnvironmentVariables()).toThrow(
|
||||
"AWS_ACCESS_KEY_ID is required when using AWS Bedrock.",
|
||||
"Either AWS_BEARER_TOKEN_BEDROCK or both AWS_ACCESS_KEY_ID and AWS_SECRET_ACCESS_KEY are required when using AWS Bedrock.",
|
||||
);
|
||||
});
|
||||
|
||||
test("should fail when AWS_SECRET_ACCESS_KEY is missing", () => {
|
||||
test("should fail when only AWS_ACCESS_KEY_ID is provided without bearer token", () => {
|
||||
process.env.CLAUDE_CODE_USE_BEDROCK = "1";
|
||||
process.env.AWS_REGION = "us-east-1";
|
||||
process.env.AWS_ACCESS_KEY_ID = "test-access-key";
|
||||
|
||||
expect(() => validateEnvironmentVariables()).toThrow(
|
||||
"AWS_SECRET_ACCESS_KEY is required when using AWS Bedrock.",
|
||||
"Either AWS_BEARER_TOKEN_BEDROCK or both AWS_ACCESS_KEY_ID and AWS_SECRET_ACCESS_KEY are required when using AWS Bedrock.",
|
||||
);
|
||||
});
|
||||
|
||||
test("should report all missing Bedrock variables", () => {
|
||||
test("should pass when AWS_BEARER_TOKEN_BEDROCK is provided instead of access keys", () => {
|
||||
process.env.CLAUDE_CODE_USE_BEDROCK = "1";
|
||||
process.env.AWS_REGION = "us-east-1";
|
||||
process.env.AWS_BEARER_TOKEN_BEDROCK = "test-bearer-token";
|
||||
|
||||
expect(() => validateEnvironmentVariables()).not.toThrow();
|
||||
});
|
||||
|
||||
test("should pass when both bearer token and access keys are provided", () => {
|
||||
process.env.CLAUDE_CODE_USE_BEDROCK = "1";
|
||||
process.env.AWS_REGION = "us-east-1";
|
||||
process.env.AWS_BEARER_TOKEN_BEDROCK = "test-bearer-token";
|
||||
process.env.AWS_ACCESS_KEY_ID = "test-access-key";
|
||||
process.env.AWS_SECRET_ACCESS_KEY = "test-secret-key";
|
||||
|
||||
expect(() => validateEnvironmentVariables()).not.toThrow();
|
||||
});
|
||||
|
||||
test("should fail when no authentication method is provided", () => {
|
||||
process.env.CLAUDE_CODE_USE_BEDROCK = "1";
|
||||
process.env.AWS_REGION = "us-east-1";
|
||||
|
||||
expect(() => validateEnvironmentVariables()).toThrow(
|
||||
"Either AWS_BEARER_TOKEN_BEDROCK or both AWS_ACCESS_KEY_ID and AWS_SECRET_ACCESS_KEY are required when using AWS Bedrock.",
|
||||
);
|
||||
});
|
||||
|
||||
test("should report missing region and authentication", () => {
|
||||
process.env.CLAUDE_CODE_USE_BEDROCK = "1";
|
||||
|
||||
expect(() => validateEnvironmentVariables()).toThrow(
|
||||
/AWS_REGION is required when using AWS Bedrock.*AWS_ACCESS_KEY_ID is required when using AWS Bedrock.*AWS_SECRET_ACCESS_KEY is required when using AWS Bedrock/s,
|
||||
/AWS_REGION is required when using AWS Bedrock.*Either AWS_BEARER_TOKEN_BEDROCK or both AWS_ACCESS_KEY_ID and AWS_SECRET_ACCESS_KEY are required when using AWS Bedrock/s,
|
||||
);
|
||||
});
|
||||
});
|
||||
@@ -167,6 +198,56 @@ describe("validateEnvironmentVariables", () => {
|
||||
});
|
||||
});
|
||||
|
||||
describe("Microsoft Foundry", () => {
|
||||
test("should pass when ANTHROPIC_FOUNDRY_RESOURCE is provided", () => {
|
||||
process.env.CLAUDE_CODE_USE_FOUNDRY = "1";
|
||||
process.env.ANTHROPIC_FOUNDRY_RESOURCE = "test-resource";
|
||||
|
||||
expect(() => validateEnvironmentVariables()).not.toThrow();
|
||||
});
|
||||
|
||||
test("should pass when ANTHROPIC_FOUNDRY_BASE_URL is provided", () => {
|
||||
process.env.CLAUDE_CODE_USE_FOUNDRY = "1";
|
||||
process.env.ANTHROPIC_FOUNDRY_BASE_URL =
|
||||
"https://test-resource.services.ai.azure.com";
|
||||
|
||||
expect(() => validateEnvironmentVariables()).not.toThrow();
|
||||
});
|
||||
|
||||
test("should pass when both resource and base URL are provided", () => {
|
||||
process.env.CLAUDE_CODE_USE_FOUNDRY = "1";
|
||||
process.env.ANTHROPIC_FOUNDRY_RESOURCE = "test-resource";
|
||||
process.env.ANTHROPIC_FOUNDRY_BASE_URL =
|
||||
"https://custom.services.ai.azure.com";
|
||||
|
||||
expect(() => validateEnvironmentVariables()).not.toThrow();
|
||||
});
|
||||
|
||||
test("should construct Foundry base URL from resource name when ANTHROPIC_FOUNDRY_BASE_URL is not provided", () => {
|
||||
// This test verifies our action.yml change, which constructs:
|
||||
// ANTHROPIC_FOUNDRY_BASE_URL: ${{ env.ANTHROPIC_FOUNDRY_BASE_URL || (env.ANTHROPIC_FOUNDRY_RESOURCE && format('https://{0}.services.ai.azure.com', env.ANTHROPIC_FOUNDRY_RESOURCE)) }}
|
||||
|
||||
process.env.CLAUDE_CODE_USE_FOUNDRY = "1";
|
||||
process.env.ANTHROPIC_FOUNDRY_RESOURCE = "my-foundry-resource";
|
||||
// ANTHROPIC_FOUNDRY_BASE_URL is intentionally not set
|
||||
|
||||
// The actual URL construction happens in the composite action in action.yml
|
||||
// This test is a placeholder to document the behavior
|
||||
expect(() => validateEnvironmentVariables()).not.toThrow();
|
||||
|
||||
// In the actual action, ANTHROPIC_FOUNDRY_BASE_URL would be:
|
||||
// https://my-foundry-resource.services.ai.azure.com
|
||||
});
|
||||
|
||||
test("should fail when neither ANTHROPIC_FOUNDRY_RESOURCE nor ANTHROPIC_FOUNDRY_BASE_URL is provided", () => {
|
||||
process.env.CLAUDE_CODE_USE_FOUNDRY = "1";
|
||||
|
||||
expect(() => validateEnvironmentVariables()).toThrow(
|
||||
"Either ANTHROPIC_FOUNDRY_RESOURCE or ANTHROPIC_FOUNDRY_BASE_URL is required when using Microsoft Foundry.",
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
describe("Multiple providers", () => {
|
||||
test("should fail when both Bedrock and Vertex are enabled", () => {
|
||||
process.env.CLAUDE_CODE_USE_BEDROCK = "1";
|
||||
@@ -179,7 +260,51 @@ describe("validateEnvironmentVariables", () => {
|
||||
process.env.CLOUD_ML_REGION = "us-central1";
|
||||
|
||||
expect(() => validateEnvironmentVariables()).toThrow(
|
||||
"Cannot use both Bedrock and Vertex AI simultaneously. Please set only one provider.",
|
||||
"Cannot use multiple providers simultaneously. Please set only one of: CLAUDE_CODE_USE_BEDROCK, CLAUDE_CODE_USE_VERTEX, or CLAUDE_CODE_USE_FOUNDRY.",
|
||||
);
|
||||
});
|
||||
|
||||
test("should fail when both Bedrock and Foundry are enabled", () => {
|
||||
process.env.CLAUDE_CODE_USE_BEDROCK = "1";
|
||||
process.env.CLAUDE_CODE_USE_FOUNDRY = "1";
|
||||
// Provide all required vars to isolate the mutual exclusion error
|
||||
process.env.AWS_REGION = "us-east-1";
|
||||
process.env.AWS_ACCESS_KEY_ID = "test-access-key";
|
||||
process.env.AWS_SECRET_ACCESS_KEY = "test-secret-key";
|
||||
process.env.ANTHROPIC_FOUNDRY_RESOURCE = "test-resource";
|
||||
|
||||
expect(() => validateEnvironmentVariables()).toThrow(
|
||||
"Cannot use multiple providers simultaneously. Please set only one of: CLAUDE_CODE_USE_BEDROCK, CLAUDE_CODE_USE_VERTEX, or CLAUDE_CODE_USE_FOUNDRY.",
|
||||
);
|
||||
});
|
||||
|
||||
test("should fail when both Vertex and Foundry are enabled", () => {
|
||||
process.env.CLAUDE_CODE_USE_VERTEX = "1";
|
||||
process.env.CLAUDE_CODE_USE_FOUNDRY = "1";
|
||||
// Provide all required vars to isolate the mutual exclusion error
|
||||
process.env.ANTHROPIC_VERTEX_PROJECT_ID = "test-project";
|
||||
process.env.CLOUD_ML_REGION = "us-central1";
|
||||
process.env.ANTHROPIC_FOUNDRY_RESOURCE = "test-resource";
|
||||
|
||||
expect(() => validateEnvironmentVariables()).toThrow(
|
||||
"Cannot use multiple providers simultaneously. Please set only one of: CLAUDE_CODE_USE_BEDROCK, CLAUDE_CODE_USE_VERTEX, or CLAUDE_CODE_USE_FOUNDRY.",
|
||||
);
|
||||
});
|
||||
|
||||
test("should fail when all three providers are enabled", () => {
|
||||
process.env.CLAUDE_CODE_USE_BEDROCK = "1";
|
||||
process.env.CLAUDE_CODE_USE_VERTEX = "1";
|
||||
process.env.CLAUDE_CODE_USE_FOUNDRY = "1";
|
||||
// Provide all required vars to isolate the mutual exclusion error
|
||||
process.env.AWS_REGION = "us-east-1";
|
||||
process.env.AWS_ACCESS_KEY_ID = "test-access-key";
|
||||
process.env.AWS_SECRET_ACCESS_KEY = "test-secret-key";
|
||||
process.env.ANTHROPIC_VERTEX_PROJECT_ID = "test-project";
|
||||
process.env.CLOUD_ML_REGION = "us-central1";
|
||||
process.env.ANTHROPIC_FOUNDRY_RESOURCE = "test-resource";
|
||||
|
||||
expect(() => validateEnvironmentVariables()).toThrow(
|
||||
"Cannot use multiple providers simultaneously. Please set only one of: CLAUDE_CODE_USE_BEDROCK, CLAUDE_CODE_USE_VERTEX, or CLAUDE_CODE_USE_FOUNDRY.",
|
||||
);
|
||||
});
|
||||
});
|
||||
@@ -204,10 +329,7 @@ describe("validateEnvironmentVariables", () => {
|
||||
" - AWS_REGION is required when using AWS Bedrock.",
|
||||
);
|
||||
expect(error!.message).toContain(
|
||||
" - AWS_ACCESS_KEY_ID is required when using AWS Bedrock.",
|
||||
);
|
||||
expect(error!.message).toContain(
|
||||
" - AWS_SECRET_ACCESS_KEY is required when using AWS Bedrock.",
|
||||
" - Either AWS_BEARER_TOKEN_BEDROCK or both AWS_ACCESS_KEY_ID and AWS_SECRET_ACCESS_KEY are required when using AWS Bedrock.",
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,16 +1,17 @@
|
||||
# Cloud Providers
|
||||
|
||||
You can authenticate with Claude using any of these three methods:
|
||||
You can authenticate with Claude using any of these four methods:
|
||||
|
||||
1. Direct Anthropic API (default)
|
||||
2. Amazon Bedrock with OIDC authentication
|
||||
3. Google Vertex AI with OIDC authentication
|
||||
4. Microsoft Foundry with OIDC authentication
|
||||
|
||||
For detailed setup instructions for AWS Bedrock and Google Vertex AI, see the [official documentation](https://docs.anthropic.com/en/docs/claude-code/github-actions#using-with-aws-bedrock-%26-google-vertex-ai).
|
||||
|
||||
**Note**:
|
||||
|
||||
- Bedrock and Vertex use OIDC authentication exclusively
|
||||
- Bedrock, Vertex, and Microsoft Foundry use OIDC authentication exclusively
|
||||
- AWS Bedrock automatically uses cross-region inference profiles for certain models
|
||||
- For cross-region inference profile models, you need to request and be granted access to the Claude models in all regions that the inference profile uses
|
||||
|
||||
@@ -40,11 +41,19 @@ Use provider-specific model names based on your chosen provider:
|
||||
claude_args: |
|
||||
--model claude-4-0-sonnet@20250805
|
||||
# ... other inputs
|
||||
|
||||
# For Microsoft Foundry with OIDC
|
||||
- uses: anthropics/claude-code-action@v1
|
||||
with:
|
||||
use_foundry: "true"
|
||||
claude_args: |
|
||||
--model claude-sonnet-4-5
|
||||
# ... other inputs
|
||||
```
|
||||
|
||||
## OIDC Authentication for Bedrock and Vertex
|
||||
## OIDC Authentication for Cloud Providers
|
||||
|
||||
Both AWS Bedrock and GCP Vertex AI require OIDC authentication.
|
||||
AWS Bedrock, GCP Vertex AI, and Microsoft Foundry all support OIDC authentication.
|
||||
|
||||
```yaml
|
||||
# For AWS Bedrock with OIDC
|
||||
@@ -97,3 +106,36 @@ Both AWS Bedrock and GCP Vertex AI require OIDC authentication.
|
||||
permissions:
|
||||
id-token: write # Required for OIDC
|
||||
```
|
||||
|
||||
```yaml
|
||||
# For Microsoft Foundry with OIDC
|
||||
- name: Authenticate to Azure
|
||||
uses: azure/login@v2
|
||||
with:
|
||||
client-id: ${{ secrets.AZURE_CLIENT_ID }}
|
||||
tenant-id: ${{ secrets.AZURE_TENANT_ID }}
|
||||
subscription-id: ${{ secrets.AZURE_SUBSCRIPTION_ID }}
|
||||
|
||||
- name: Generate GitHub App token
|
||||
id: app-token
|
||||
uses: actions/create-github-app-token@v2
|
||||
with:
|
||||
app-id: ${{ secrets.APP_ID }}
|
||||
private-key: ${{ secrets.APP_PRIVATE_KEY }}
|
||||
|
||||
- uses: anthropics/claude-code-action@v1
|
||||
with:
|
||||
use_foundry: "true"
|
||||
claude_args: |
|
||||
--model claude-sonnet-4-5
|
||||
# ... other inputs
|
||||
env:
|
||||
ANTHROPIC_FOUNDRY_BASE_URL: https://my-resource.services.ai.azure.com
|
||||
|
||||
permissions:
|
||||
id-token: write # Required for OIDC
|
||||
```
|
||||
|
||||
## Microsoft Foundry Setup
|
||||
|
||||
For detailed setup instructions for Microsoft Foundry, see the [official documentation](https://docs.anthropic.com/en/docs/claude-code/microsoft-foundry).
|
||||
|
||||
@@ -38,7 +38,7 @@ The following permissions are requested but not yet actively used. These will en
|
||||
|
||||
## Commit Signing
|
||||
|
||||
All commits made by Claude through this action are automatically signed with commit signatures. This ensures the authenticity and integrity of commits, providing a verifiable trail of changes made by the action.
|
||||
Commits made by Claude through this action are no longer automatically signed with commit signatures. To enable commit signing set `use_commit_signing: True` in the workflow(s). This ensures the authenticity and integrity of commits, providing a verifiable trail of changes made by the action.
|
||||
|
||||
## ⚠️ Authentication Protection
|
||||
|
||||
|
||||
@@ -80,7 +80,6 @@ jobs:
|
||||
| `path_to_bun_executable` | Optional path to a custom Bun executable. Skips automatic Bun installation. Useful for Nix, custom containers, or specialized environments | No | "" |
|
||||
| `plugin_marketplaces` | Newline-separated list of Claude Code plugin marketplace Git URLs to install from (e.g., see example in workflow above). Marketplaces are added before plugin installation | No | "" |
|
||||
| `plugins` | Newline-separated list of Claude Code plugin names to install (e.g., see example in workflow above). Plugins are installed before Claude Code execution | No | "" |
|
||||
| `json_schema` | JSON schema for structured output validation. Automatically sets GitHub Action outputs for each field. See [Structured Outputs](#structured-outputs) section below | No | "" |
|
||||
|
||||
### Deprecated Inputs
|
||||
|
||||
@@ -201,16 +200,8 @@ Get validated JSON results from Claude that automatically become GitHub Action o
|
||||
prompt: |
|
||||
Check the CI logs and determine if this is a flaky test.
|
||||
Return: is_flaky (boolean), confidence (0-1), summary (string)
|
||||
json_schema: |
|
||||
{
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"is_flaky": {"type": "boolean"},
|
||||
"confidence": {"type": "number"},
|
||||
"summary": {"type": "string"}
|
||||
},
|
||||
"required": ["is_flaky"]
|
||||
}
|
||||
claude_args: |
|
||||
--json-schema '{"type":"object","properties":{"is_flaky":{"type":"boolean"},"confidence":{"type":"number"},"summary":{"type":"string"}},"required":["is_flaky"]}'
|
||||
|
||||
- name: Retry if flaky
|
||||
if: fromJSON(steps.analyze.outputs.structured_output).is_flaky == true
|
||||
@@ -219,7 +210,7 @@ Get validated JSON results from Claude that automatically become GitHub Action o
|
||||
|
||||
### How It Works
|
||||
|
||||
1. **Define Schema**: Provide a JSON schema in the `json_schema` input
|
||||
1. **Define Schema**: Provide a JSON schema via `--json-schema` flag in `claude_args`
|
||||
2. **Claude Executes**: Claude uses tools to complete your task
|
||||
3. **Validated Output**: Result is validated against your schema
|
||||
4. **JSON Output**: All fields are returned in a single `structured_output` JSON string
|
||||
|
||||
@@ -43,27 +43,8 @@ jobs:
|
||||
- is_flaky: true if likely flaky, false if real bug
|
||||
- confidence: number 0-1 indicating confidence level
|
||||
- summary: brief one-sentence explanation
|
||||
json_schema: |
|
||||
{
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"is_flaky": {
|
||||
"type": "boolean",
|
||||
"description": "Whether this appears to be a flaky test failure"
|
||||
},
|
||||
"confidence": {
|
||||
"type": "number",
|
||||
"minimum": 0,
|
||||
"maximum": 1,
|
||||
"description": "Confidence level in the determination"
|
||||
},
|
||||
"summary": {
|
||||
"type": "string",
|
||||
"description": "One-sentence explanation of the failure"
|
||||
}
|
||||
},
|
||||
"required": ["is_flaky", "confidence", "summary"]
|
||||
}
|
||||
claude_args: |
|
||||
--json-schema '{"type":"object","properties":{"is_flaky":{"type":"boolean","description":"Whether this appears to be a flaky test failure"},"confidence":{"type":"number","minimum":0,"maximum":1,"description":"Confidence level in the determination"},"summary":{"type":"string","description":"One-sentence explanation of the failure"}},"required":["is_flaky","confidence","summary"]}'
|
||||
|
||||
# Auto-retry only if flaky AND high confidence (>= 0.7)
|
||||
- name: Retry flaky tests
|
||||
|
||||
@@ -7,7 +7,6 @@ import { parseAllowedTools } from "./parse-tools";
|
||||
import { configureGitAuth } from "../../github/operations/git-config";
|
||||
import type { GitHubContext } from "../../github/context";
|
||||
import { isEntityContext } from "../../github/context";
|
||||
import { appendJsonSchemaArg } from "../../utils/json-schema";
|
||||
|
||||
/**
|
||||
* Extract GitHub context as environment variables for agent mode
|
||||
@@ -150,9 +149,6 @@ export const agentMode: Mode = {
|
||||
claudeArgs = `--mcp-config '${escapedOurConfig}'`;
|
||||
}
|
||||
|
||||
// Add JSON schema if provided
|
||||
claudeArgs = appendJsonSchemaArg(claudeArgs);
|
||||
|
||||
// Append user's claude_args (which may have more --mcp-config flags)
|
||||
claudeArgs = `${claudeArgs} ${userClaudeArgs}`.trim();
|
||||
|
||||
|
||||
@@ -15,7 +15,6 @@ import { isEntityContext } from "../../github/context";
|
||||
import type { PreparedContext } from "../../create-prompt/types";
|
||||
import type { FetchDataResult } from "../../github/data/fetcher";
|
||||
import { parseAllowedTools } from "../agent/parse-tools";
|
||||
import { appendJsonSchemaArg } from "../../utils/json-schema";
|
||||
|
||||
/**
|
||||
* Tag mode implementation.
|
||||
@@ -178,9 +177,6 @@ export const tagMode: Mode = {
|
||||
// Add required tools for tag mode
|
||||
claudeArgs += ` --allowedTools "${tagModeTools.join(",")}"`;
|
||||
|
||||
// Add JSON schema if provided
|
||||
claudeArgs = appendJsonSchemaArg(claudeArgs);
|
||||
|
||||
// Append user's claude_args (which may have more --mcp-config flags)
|
||||
if (userClaudeArgs) {
|
||||
claudeArgs += ` ${userClaudeArgs}`;
|
||||
|
||||
@@ -1,17 +0,0 @@
|
||||
/**
|
||||
* Appends JSON schema CLI argument if json_schema is provided
|
||||
* Escapes schema for safe shell passing
|
||||
*/
|
||||
export function appendJsonSchemaArg(
|
||||
claudeArgs: string,
|
||||
jsonSchemaStr?: string,
|
||||
): string {
|
||||
const schema = jsonSchemaStr || process.env.JSON_SCHEMA || "";
|
||||
if (!schema) {
|
||||
return claudeArgs;
|
||||
}
|
||||
|
||||
// CLI validates schema - just escape for safe shell passing
|
||||
const escapedSchema = schema.replace(/'/g, "'\\''");
|
||||
return `${claudeArgs} --json-schema '${escapedSchema}'`;
|
||||
}
|
||||
Reference in New Issue
Block a user