mirror of
https://github.com/anthropics/claude-code-action.git
synced 2026-07-27 22:38:30 +08:00
* Add workload identity federation support to base-action Move the workload identity module into base-action so the standalone action can fetch and refresh the GitHub OIDC identity token itself, and expose the same federation inputs as the outer action. Switch the base-action test workflows from the anthropic_api_key secret to the federation repo variables and grant them id-token: write. * Verify MCP test tool invocation instead of init connection status MCP servers can connect asynchronously, so the init event may report a server as pending. Check that the server is registered at init, then assert the test tool was actually called and returned its response. Also pass the MCP config through claude_args --mcp-config, replacing the removed mcp_config input.
241 lines
12 KiB
YAML
241 lines
12 KiB
YAML
name: "Claude Code Base Action"
|
|
description: "Run Claude Code in GitHub Actions workflows"
|
|
branding:
|
|
icon: "code"
|
|
color: "orange"
|
|
|
|
inputs:
|
|
# Claude Code arguments
|
|
prompt:
|
|
description: "The prompt to send to Claude Code (mutually exclusive with prompt_file)"
|
|
required: false
|
|
default: ""
|
|
prompt_file:
|
|
description: "Path to a file containing the prompt to send to Claude Code (mutually exclusive with prompt)"
|
|
required: false
|
|
default: ""
|
|
settings:
|
|
description: "Claude Code settings as JSON string or path to settings JSON file"
|
|
required: false
|
|
default: ""
|
|
|
|
# Action settings
|
|
claude_args:
|
|
description: "Additional arguments to pass directly to Claude CLI (e.g., '--max-turns 3 --mcp-config /path/to/config.json')"
|
|
required: false
|
|
default: ""
|
|
|
|
# Authentication settings
|
|
anthropic_api_key:
|
|
description: "Anthropic API key (required for direct Anthropic API)"
|
|
required: false
|
|
default: ""
|
|
claude_code_oauth_token:
|
|
description: "Claude Code OAuth token (alternative to anthropic_api_key)"
|
|
required: false
|
|
default: ""
|
|
anthropic_federation_rule_id:
|
|
description: "Workload identity federation rule ID (fdrl_...). When set with anthropic_organization_id, the action authenticates to the Claude API by exchanging the workflow's GitHub OIDC token instead of using a static API key. Requires `id-token: write` permission."
|
|
required: false
|
|
default: ""
|
|
anthropic_organization_id:
|
|
description: "Anthropic organization UUID used for workload identity federation"
|
|
required: false
|
|
default: ""
|
|
anthropic_service_account_id:
|
|
description: "Service account ID (svac_...) the federated token acts as (optional, used with workload identity federation)"
|
|
required: false
|
|
default: ""
|
|
anthropic_workspace_id:
|
|
description: "Workspace ID (wrkspc_...) for workload identity federation. Optional when the federation rule targets a single workspace."
|
|
required: false
|
|
default: ""
|
|
anthropic_oidc_audience:
|
|
description: "Audience to request on the GitHub OIDC token used for workload identity federation. Defaults to https://api.anthropic.com."
|
|
required: false
|
|
default: ""
|
|
use_bedrock:
|
|
description: "Use Amazon Bedrock with OIDC authentication instead of direct Anthropic API"
|
|
required: false
|
|
default: "false"
|
|
use_vertex:
|
|
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)"
|
|
required: false
|
|
default: "false"
|
|
path_to_claude_code_executable:
|
|
description: "Optional path to a custom Claude Code executable. If provided, skips automatic installation and uses this executable instead. WARNING: Using an older version may cause problems if the action begins taking advantage of new Claude Code features. This input is typically not needed unless you're debugging something specific or have unique needs in your environment."
|
|
required: false
|
|
default: ""
|
|
path_to_bun_executable:
|
|
description: "Optional path to a custom Bun executable. If provided, skips automatic Bun installation and uses this executable instead. WARNING: Using an incompatible version may cause problems if the action requires specific Bun features. This input is typically not needed unless you're debugging something specific or have unique needs in your environment."
|
|
required: false
|
|
default: ""
|
|
show_full_output:
|
|
description: "Show full JSON output from Claude Code. WARNING: This outputs ALL Claude messages including tool execution results which may contain secrets, API keys, or other sensitive information. These logs are publicly visible in GitHub Actions. Only enable for debugging in non-sensitive environments."
|
|
required: false
|
|
default: "false"
|
|
plugins:
|
|
description: "Newline-separated list of Claude Code plugin names to install (e.g., 'code-review@claude-code-plugins\nfeature-dev@claude-code-plugins')"
|
|
required: false
|
|
default: ""
|
|
plugin_marketplaces:
|
|
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: ""
|
|
|
|
outputs:
|
|
conclusion:
|
|
description: "Execution status of Claude Code ('success' or 'failure')"
|
|
value: ${{ steps.run_claude.outputs.conclusion }}
|
|
execution_file:
|
|
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 when --json-schema is provided in claude_args (use fromJSON() or jq to parse)"
|
|
value: ${{ steps.run_claude.outputs.structured_output }}
|
|
session_id:
|
|
description: "The Claude Code session ID that can be used with --resume to continue this conversation"
|
|
value: ${{ steps.run_claude.outputs.session_id }}
|
|
|
|
runs:
|
|
using: "composite"
|
|
steps:
|
|
- name: Setup Node.js
|
|
uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # https://github.com/actions/setup-node/releases/tag/v4.4.0
|
|
with:
|
|
node-version: ${{ env.NODE_VERSION || '18.x' }}
|
|
cache: ${{ inputs.use_node_cache == 'true' && 'npm' || '' }}
|
|
|
|
- name: Install Bun
|
|
if: inputs.path_to_bun_executable == ''
|
|
uses: oven-sh/setup-bun@0c5077e51419868618aeaa5fe8019c62421857d6 # https://github.com/oven-sh/setup-bun/releases/tag/v2.2.0
|
|
with:
|
|
bun-version: 1.3.14
|
|
|
|
- name: Setup Custom Bun Path
|
|
if: inputs.path_to_bun_executable != ''
|
|
shell: bash
|
|
env:
|
|
PATH_TO_BUN_EXECUTABLE: ${{ inputs.path_to_bun_executable }}
|
|
run: |
|
|
echo "Using custom Bun executable: $PATH_TO_BUN_EXECUTABLE"
|
|
# Add the directory containing the custom executable to PATH
|
|
BUN_DIR=$(dirname "$PATH_TO_BUN_EXECUTABLE")
|
|
echo "$BUN_DIR" >> "$GITHUB_PATH"
|
|
|
|
- name: Install Dependencies
|
|
shell: bash
|
|
run: |
|
|
cd ${GITHUB_ACTION_PATH}
|
|
bun install --production
|
|
|
|
- name: Install Claude Code
|
|
shell: bash
|
|
env:
|
|
PATH_TO_CLAUDE_CODE_EXECUTABLE: ${{ inputs.path_to_claude_code_executable }}
|
|
run: |
|
|
if [ -z "$PATH_TO_CLAUDE_CODE_EXECUTABLE" ]; then
|
|
CLAUDE_CODE_VERSION="2.1.160"
|
|
echo "Installing Claude Code v${CLAUDE_CODE_VERSION}..."
|
|
for attempt in 1 2 3; do
|
|
echo "Installation attempt $attempt..."
|
|
if command -v timeout &> /dev/null; then
|
|
# Use --foreground to kill entire process group on timeout, --kill-after to send SIGKILL if SIGTERM fails
|
|
timeout --foreground --kill-after=10 120 bash -c "curl -fsSL https://claude.ai/install.sh | bash -s -- $CLAUDE_CODE_VERSION" && break
|
|
else
|
|
curl -fsSL https://claude.ai/install.sh | bash -s -- "$CLAUDE_CODE_VERSION" && break
|
|
fi
|
|
if [ $attempt -eq 3 ]; then
|
|
echo "Failed to install Claude Code after 3 attempts"
|
|
exit 1
|
|
fi
|
|
echo "Installation failed, retrying..."
|
|
sleep 5
|
|
done
|
|
echo "Claude Code installed successfully"
|
|
else
|
|
echo "Using custom Claude Code executable: $PATH_TO_CLAUDE_CODE_EXECUTABLE"
|
|
# Add the directory containing the custom executable to PATH
|
|
CLAUDE_DIR=$(dirname "$PATH_TO_CLAUDE_CODE_EXECUTABLE")
|
|
echo "$CLAUDE_DIR" >> "$GITHUB_PATH"
|
|
fi
|
|
|
|
- name: Run Claude Code Action
|
|
shell: bash
|
|
id: run_claude
|
|
run: |
|
|
# Change to CLAUDE_WORKING_DIR if set (for running in custom directories)
|
|
if [ -n "$CLAUDE_WORKING_DIR" ]; then
|
|
echo "Changing directory to CLAUDE_WORKING_DIR: $CLAUDE_WORKING_DIR"
|
|
cd "$CLAUDE_WORKING_DIR"
|
|
fi
|
|
bun run ${GITHUB_ACTION_PATH}/src/index.ts
|
|
env:
|
|
# Model configuration
|
|
CLAUDE_CODE_ACTION: "1"
|
|
INPUT_PROMPT: ${{ inputs.prompt }}
|
|
INPUT_PROMPT_FILE: ${{ inputs.prompt_file }}
|
|
INPUT_SETTINGS: ${{ inputs.settings }}
|
|
INPUT_CLAUDE_ARGS: ${{ inputs.claude_args }}
|
|
INPUT_PATH_TO_CLAUDE_CODE_EXECUTABLE: ${{ inputs.path_to_claude_code_executable }}
|
|
INPUT_PATH_TO_BUN_EXECUTABLE: ${{ inputs.path_to_bun_executable }}
|
|
INPUT_SHOW_FULL_OUTPUT: ${{ inputs.show_full_output }}
|
|
INPUT_PLUGINS: ${{ inputs.plugins }}
|
|
INPUT_PLUGIN_MARKETPLACES: ${{ inputs.plugin_marketplaces }}
|
|
|
|
# Provider configuration
|
|
ANTHROPIC_API_KEY: ${{ inputs.anthropic_api_key }}
|
|
CLAUDE_CODE_OAUTH_TOKEN: ${{ inputs.claude_code_oauth_token }}
|
|
ANTHROPIC_FEDERATION_RULE_ID: ${{ inputs.anthropic_federation_rule_id }}
|
|
ANTHROPIC_ORGANIZATION_ID: ${{ inputs.anthropic_organization_id }}
|
|
ANTHROPIC_SERVICE_ACCOUNT_ID: ${{ inputs.anthropic_service_account_id }}
|
|
ANTHROPIC_WORKSPACE_ID: ${{ inputs.anthropic_workspace_id }}
|
|
ANTHROPIC_OIDC_AUDIENCE: ${{ inputs.anthropic_oidc_audience }}
|
|
ANTHROPIC_BASE_URL: ${{ env.ANTHROPIC_BASE_URL }}
|
|
ANTHROPIC_CUSTOM_HEADERS: ${{ env.ANTHROPIC_CUSTOM_HEADERS }}
|
|
# 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
|
|
ANTHROPIC_VERTEX_PROJECT_ID: ${{ env.ANTHROPIC_VERTEX_PROJECT_ID }}
|
|
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 }}
|
|
|
|
# Telemetry configuration
|
|
CLAUDE_CODE_ENABLE_TELEMETRY: ${{ env.CLAUDE_CODE_ENABLE_TELEMETRY }}
|
|
OTEL_METRICS_EXPORTER: ${{ env.OTEL_METRICS_EXPORTER }}
|
|
OTEL_LOGS_EXPORTER: ${{ env.OTEL_LOGS_EXPORTER }}
|
|
OTEL_EXPORTER_OTLP_PROTOCOL: ${{ env.OTEL_EXPORTER_OTLP_PROTOCOL }}
|
|
OTEL_EXPORTER_OTLP_ENDPOINT: ${{ env.OTEL_EXPORTER_OTLP_ENDPOINT }}
|
|
OTEL_EXPORTER_OTLP_HEADERS: ${{ env.OTEL_EXPORTER_OTLP_HEADERS }}
|
|
OTEL_METRIC_EXPORT_INTERVAL: ${{ env.OTEL_METRIC_EXPORT_INTERVAL }}
|
|
OTEL_LOGS_EXPORT_INTERVAL: ${{ env.OTEL_LOGS_EXPORT_INTERVAL }}
|
|
OTEL_RESOURCE_ATTRIBUTES: ${{ env.OTEL_RESOURCE_ATTRIBUTES }}
|