mirror of
https://github.com/anthropics/claude-code-action.git
synced 2026-01-24 07:24:12 +08:00
Compare commits
11 Commits
inigo/add-
...
v1.0.19
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
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:
|
on:
|
||||||
pull_request:
|
pull_request:
|
||||||
types: [opened, synchronize, ready_for_review, reopened]
|
types: [opened]
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
review:
|
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
|
- number_field: 42
|
||||||
- boolean_true: true
|
- boolean_true: true
|
||||||
- boolean_false: false
|
- 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 }}
|
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
|
- name: Verify outputs
|
||||||
run: |
|
run: |
|
||||||
@@ -97,21 +88,10 @@ jobs:
|
|||||||
- items: ["apple", "banana", "cherry"]
|
- items: ["apple", "banana", "cherry"]
|
||||||
- config: {"key": "value", "count": 3}
|
- config: {"key": "value", "count": 3}
|
||||||
- empty_array: []
|
- 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 }}
|
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
|
- name: Verify JSON stringification
|
||||||
run: |
|
run: |
|
||||||
@@ -160,19 +140,10 @@ jobs:
|
|||||||
- empty_string: ""
|
- empty_string: ""
|
||||||
- negative: -5
|
- negative: -5
|
||||||
- decimal: 3.14
|
- 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 }}
|
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
|
- name: Verify edge cases
|
||||||
run: |
|
run: |
|
||||||
@@ -223,17 +194,10 @@ jobs:
|
|||||||
prompt: |
|
prompt: |
|
||||||
Run: echo "test"
|
Run: echo "test"
|
||||||
Return EXACTLY: {test-result: "passed", item_count: 10}
|
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 }}
|
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
|
- name: Verify sanitized names work
|
||||||
run: |
|
run: |
|
||||||
@@ -268,16 +232,10 @@ jobs:
|
|||||||
uses: ./base-action
|
uses: ./base-action
|
||||||
with:
|
with:
|
||||||
prompt: "Run: echo 'complete'. Return: {done: true}"
|
prompt: "Run: echo 'complete'. Return: {done: true}"
|
||||||
json_schema: |
|
|
||||||
{
|
|
||||||
"type": "object",
|
|
||||||
"properties": {
|
|
||||||
"done": {"type": "boolean"}
|
|
||||||
},
|
|
||||||
"required": ["done"]
|
|
||||||
}
|
|
||||||
anthropic_api_key: ${{ secrets.ANTHROPIC_API_KEY }}
|
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
|
- name: Verify execution file contains structured_output
|
||||||
run: |
|
run: |
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
# Claude Code Action
|
# 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
|
## Features
|
||||||
|
|
||||||
@@ -30,7 +30,7 @@ This command will guide you through setting up the GitHub app and required secre
|
|||||||
**Note**:
|
**Note**:
|
||||||
|
|
||||||
- You must be a repository admin to install the GitHub app and add secrets
|
- 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
|
## 📚 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
|
- [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
|
- [Configuration](./docs/configuration.md) - MCP servers, permissions, environment variables, and advanced settings
|
||||||
- [Experimental Features](./docs/experimental.md) - Execution modes and network restrictions
|
- [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
|
- [Capabilities & Limitations](./docs/capabilities-and-limitations.md) - What Claude can and cannot do
|
||||||
- [Security](./docs/security.md) - Access control, permissions, and commit signing
|
- [Security](./docs/security.md) - Access control, permissions, and commit signing
|
||||||
- [FAQ](./docs/faq.md) - Common questions and troubleshooting
|
- [FAQ](./docs/faq.md) - Common questions and troubleshooting
|
||||||
|
|||||||
26
action.yml
26
action.yml
@@ -44,7 +44,7 @@ inputs:
|
|||||||
|
|
||||||
# Auth configuration
|
# Auth configuration
|
||||||
anthropic_api_key:
|
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
|
required: false
|
||||||
claude_code_oauth_token:
|
claude_code_oauth_token:
|
||||||
description: "Claude Code OAuth token (alternative to anthropic_api_key)"
|
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"
|
description: "Use Google Vertex AI with OIDC authentication instead of direct Anthropic API"
|
||||||
required: false
|
required: false
|
||||||
default: "false"
|
default: "false"
|
||||||
|
use_foundry:
|
||||||
|
description: "Use Microsoft Foundry with OIDC authentication instead of direct Anthropic API"
|
||||||
|
required: false
|
||||||
|
default: "false"
|
||||||
|
|
||||||
claude_args:
|
claude_args:
|
||||||
description: "Additional arguments to pass directly to Claude CLI"
|
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')"
|
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
|
required: false
|
||||||
default: ""
|
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:
|
outputs:
|
||||||
execution_file:
|
execution_file:
|
||||||
@@ -128,6 +128,9 @@ outputs:
|
|||||||
github_token:
|
github_token:
|
||||||
description: "The GitHub token used by the action (Claude App token if available)"
|
description: "The GitHub token used by the action (Claude App token if available)"
|
||||||
value: ${{ steps.prepare.outputs.github_token }}
|
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:
|
runs:
|
||||||
using: "composite"
|
using: "composite"
|
||||||
@@ -178,7 +181,6 @@ runs:
|
|||||||
TRACK_PROGRESS: ${{ inputs.track_progress }}
|
TRACK_PROGRESS: ${{ inputs.track_progress }}
|
||||||
ADDITIONAL_PERMISSIONS: ${{ inputs.additional_permissions }}
|
ADDITIONAL_PERMISSIONS: ${{ inputs.additional_permissions }}
|
||||||
CLAUDE_ARGS: ${{ inputs.claude_args }}
|
CLAUDE_ARGS: ${{ inputs.claude_args }}
|
||||||
JSON_SCHEMA: ${{ inputs.json_schema }}
|
|
||||||
ALL_INPUTS: ${{ toJson(inputs) }}
|
ALL_INPUTS: ${{ toJson(inputs) }}
|
||||||
|
|
||||||
- name: Install Base Action Dependencies
|
- name: Install Base Action Dependencies
|
||||||
@@ -194,7 +196,7 @@ runs:
|
|||||||
# Install Claude Code if no custom executable is provided
|
# Install Claude Code if no custom executable is provided
|
||||||
if [ -z "${{ inputs.path_to_claude_code_executable }}" ]; then
|
if [ -z "${{ inputs.path_to_claude_code_executable }}" ]; then
|
||||||
echo "Installing Claude Code..."
|
echo "Installing Claude Code..."
|
||||||
curl -fsSL https://claude.ai/install.sh | bash -s 2.0.42
|
curl -fsSL https://claude.ai/install.sh | bash -s 2.0.49
|
||||||
echo "$HOME/.local/bin" >> "$GITHUB_PATH"
|
echo "$HOME/.local/bin" >> "$GITHUB_PATH"
|
||||||
else
|
else
|
||||||
echo "Using custom Claude Code executable: ${{ inputs.path_to_claude_code_executable }}"
|
echo "Using custom Claude Code executable: ${{ inputs.path_to_claude_code_executable }}"
|
||||||
@@ -233,7 +235,6 @@ runs:
|
|||||||
INPUT_SHOW_FULL_OUTPUT: ${{ inputs.show_full_output }}
|
INPUT_SHOW_FULL_OUTPUT: ${{ inputs.show_full_output }}
|
||||||
INPUT_PLUGINS: ${{ inputs.plugins }}
|
INPUT_PLUGINS: ${{ inputs.plugins }}
|
||||||
INPUT_PLUGIN_MARKETPLACES: ${{ inputs.plugin_marketplaces }}
|
INPUT_PLUGIN_MARKETPLACES: ${{ inputs.plugin_marketplaces }}
|
||||||
JSON_SCHEMA: ${{ inputs.json_schema }}
|
|
||||||
|
|
||||||
# Model configuration
|
# Model configuration
|
||||||
GITHUB_TOKEN: ${{ steps.prepare.outputs.GITHUB_TOKEN }}
|
GITHUB_TOKEN: ${{ steps.prepare.outputs.GITHUB_TOKEN }}
|
||||||
@@ -247,12 +248,14 @@ runs:
|
|||||||
ANTHROPIC_CUSTOM_HEADERS: ${{ env.ANTHROPIC_CUSTOM_HEADERS }}
|
ANTHROPIC_CUSTOM_HEADERS: ${{ env.ANTHROPIC_CUSTOM_HEADERS }}
|
||||||
CLAUDE_CODE_USE_BEDROCK: ${{ inputs.use_bedrock == 'true' && '1' || '' }}
|
CLAUDE_CODE_USE_BEDROCK: ${{ inputs.use_bedrock == 'true' && '1' || '' }}
|
||||||
CLAUDE_CODE_USE_VERTEX: ${{ inputs.use_vertex == 'true' && '1' || '' }}
|
CLAUDE_CODE_USE_VERTEX: ${{ inputs.use_vertex == 'true' && '1' || '' }}
|
||||||
|
CLAUDE_CODE_USE_FOUNDRY: ${{ inputs.use_foundry == 'true' && '1' || '' }}
|
||||||
|
|
||||||
# AWS configuration
|
# AWS configuration
|
||||||
AWS_REGION: ${{ env.AWS_REGION }}
|
AWS_REGION: ${{ env.AWS_REGION }}
|
||||||
AWS_ACCESS_KEY_ID: ${{ env.AWS_ACCESS_KEY_ID }}
|
AWS_ACCESS_KEY_ID: ${{ env.AWS_ACCESS_KEY_ID }}
|
||||||
AWS_SECRET_ACCESS_KEY: ${{ env.AWS_SECRET_ACCESS_KEY }}
|
AWS_SECRET_ACCESS_KEY: ${{ env.AWS_SECRET_ACCESS_KEY }}
|
||||||
AWS_SESSION_TOKEN: ${{ env.AWS_SESSION_TOKEN }}
|
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)) }}
|
ANTHROPIC_BEDROCK_BASE_URL: ${{ env.ANTHROPIC_BEDROCK_BASE_URL || (env.AWS_REGION && format('https://bedrock-runtime.{0}.amazonaws.com', env.AWS_REGION)) }}
|
||||||
|
|
||||||
# GCP configuration
|
# GCP configuration
|
||||||
@@ -266,6 +269,13 @@ runs:
|
|||||||
VERTEX_REGION_CLAUDE_3_5_SONNET: ${{ env.VERTEX_REGION_CLAUDE_3_5_SONNET }}
|
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 }}
|
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
|
- name: Update comment with job link
|
||||||
if: steps.prepare.outputs.contains_trigger == 'true' && steps.prepare.outputs.claude_comment_id && always()
|
if: steps.prepare.outputs.contains_trigger == 'true' && steps.prepare.outputs.claude_comment_id && always()
|
||||||
shell: bash
|
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')"
|
description: "Additional arguments to pass directly to Claude CLI (e.g., '--max-turns 3 --mcp-config /path/to/config.json')"
|
||||||
required: false
|
required: false
|
||||||
default: ""
|
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
|
# Authentication settings
|
||||||
anthropic_api_key:
|
anthropic_api_key:
|
||||||
@@ -46,6 +42,10 @@ inputs:
|
|||||||
description: "Use Google Vertex AI with OIDC authentication instead of direct Anthropic API"
|
description: "Use Google Vertex AI with OIDC authentication instead of direct Anthropic API"
|
||||||
required: false
|
required: false
|
||||||
default: "false"
|
default: "false"
|
||||||
|
use_foundry:
|
||||||
|
description: "Use Microsoft Foundry with OIDC authentication instead of direct Anthropic API"
|
||||||
|
required: false
|
||||||
|
default: "false"
|
||||||
|
|
||||||
use_node_cache:
|
use_node_cache:
|
||||||
description: "Whether to use Node.js dependency caching (set to true only for Node.js projects with lock files)"
|
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')"
|
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
|
required: false
|
||||||
default: ""
|
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:
|
outputs:
|
||||||
conclusion:
|
conclusion:
|
||||||
@@ -88,7 +80,7 @@ outputs:
|
|||||||
description: "Path to the JSON file containing Claude Code execution log"
|
description: "Path to the JSON file containing Claude Code execution log"
|
||||||
value: ${{ steps.run_claude.outputs.execution_file }}
|
value: ${{ steps.run_claude.outputs.execution_file }}
|
||||||
structured_output:
|
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 }}
|
value: ${{ steps.run_claude.outputs.structured_output }}
|
||||||
|
|
||||||
runs:
|
runs:
|
||||||
@@ -126,7 +118,7 @@ runs:
|
|||||||
run: |
|
run: |
|
||||||
if [ -z "${{ inputs.path_to_claude_code_executable }}" ]; then
|
if [ -z "${{ inputs.path_to_claude_code_executable }}" ]; then
|
||||||
echo "Installing Claude Code..."
|
echo "Installing Claude Code..."
|
||||||
curl -fsSL https://claude.ai/install.sh | bash -s 2.0.45
|
curl -fsSL https://claude.ai/install.sh | bash -s 2.0.49
|
||||||
else
|
else
|
||||||
echo "Using custom Claude Code executable: ${{ inputs.path_to_claude_code_executable }}"
|
echo "Using custom Claude Code executable: ${{ inputs.path_to_claude_code_executable }}"
|
||||||
# Add the directory containing the custom executable to PATH
|
# Add the directory containing the custom executable to PATH
|
||||||
@@ -156,8 +148,6 @@ runs:
|
|||||||
INPUT_SHOW_FULL_OUTPUT: ${{ inputs.show_full_output }}
|
INPUT_SHOW_FULL_OUTPUT: ${{ inputs.show_full_output }}
|
||||||
INPUT_PLUGINS: ${{ inputs.plugins }}
|
INPUT_PLUGINS: ${{ inputs.plugins }}
|
||||||
INPUT_PLUGIN_MARKETPLACES: ${{ inputs.plugin_marketplaces }}
|
INPUT_PLUGIN_MARKETPLACES: ${{ inputs.plugin_marketplaces }}
|
||||||
INPUT_ALLOWED_TOOLS: ${{ inputs.allowed_tools }}
|
|
||||||
JSON_SCHEMA: ${{ inputs.json_schema }}
|
|
||||||
|
|
||||||
# Provider configuration
|
# Provider configuration
|
||||||
ANTHROPIC_API_KEY: ${{ inputs.anthropic_api_key }}
|
ANTHROPIC_API_KEY: ${{ inputs.anthropic_api_key }}
|
||||||
@@ -167,12 +157,14 @@ runs:
|
|||||||
# Only set provider flags if explicitly true, since any value (including "false") is truthy
|
# 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_BEDROCK: ${{ inputs.use_bedrock == 'true' && '1' || '' }}
|
||||||
CLAUDE_CODE_USE_VERTEX: ${{ inputs.use_vertex == 'true' && '1' || '' }}
|
CLAUDE_CODE_USE_VERTEX: ${{ inputs.use_vertex == 'true' && '1' || '' }}
|
||||||
|
CLAUDE_CODE_USE_FOUNDRY: ${{ inputs.use_foundry == 'true' && '1' || '' }}
|
||||||
|
|
||||||
# AWS configuration
|
# AWS configuration
|
||||||
AWS_REGION: ${{ env.AWS_REGION }}
|
AWS_REGION: ${{ env.AWS_REGION }}
|
||||||
AWS_ACCESS_KEY_ID: ${{ env.AWS_ACCESS_KEY_ID }}
|
AWS_ACCESS_KEY_ID: ${{ env.AWS_ACCESS_KEY_ID }}
|
||||||
AWS_SECRET_ACCESS_KEY: ${{ env.AWS_SECRET_ACCESS_KEY }}
|
AWS_SECRET_ACCESS_KEY: ${{ env.AWS_SECRET_ACCESS_KEY }}
|
||||||
AWS_SESSION_TOKEN: ${{ env.AWS_SESSION_TOKEN }}
|
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)) }}
|
ANTHROPIC_BEDROCK_BASE_URL: ${{ env.ANTHROPIC_BEDROCK_BASE_URL || (env.AWS_REGION && format('https://bedrock-runtime.{0}.amazonaws.com', env.AWS_REGION)) }}
|
||||||
|
|
||||||
# GCP configuration
|
# GCP configuration
|
||||||
@@ -180,3 +172,10 @@ runs:
|
|||||||
CLOUD_ML_REGION: ${{ env.CLOUD_ML_REGION }}
|
CLOUD_ML_REGION: ${{ env.CLOUD_ML_REGION }}
|
||||||
GOOGLE_APPLICATION_CREDENTIALS: ${{ env.GOOGLE_APPLICATION_CREDENTIALS }}
|
GOOGLE_APPLICATION_CREDENTIALS: ${{ env.GOOGLE_APPLICATION_CREDENTIALS }}
|
||||||
ANTHROPIC_VERTEX_BASE_URL: ${{ env.ANTHROPIC_VERTEX_BASE_URL }}
|
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 || "",
|
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, {
|
await runClaude(promptConfig.path, {
|
||||||
claudeArgs: claudeArgs.trim(),
|
claudeArgs: process.env.INPUT_CLAUDE_ARGS,
|
||||||
allowedTools: process.env.INPUT_ALLOWED_TOOLS,
|
allowedTools: process.env.INPUT_ALLOWED_TOOLS,
|
||||||
disallowedTools: process.env.INPUT_DISALLOWED_TOOLS,
|
disallowedTools: process.env.INPUT_DISALLOWED_TOOLS,
|
||||||
maxTurns: process.env.INPUT_MAX_TURNS,
|
maxTurns: process.env.INPUT_MAX_TURNS,
|
||||||
|
|||||||
@@ -124,7 +124,7 @@ export function prepareRunConfig(
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Parses structured_output from execution file and sets GitHub Action outputs
|
* 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
|
* Exported for testing
|
||||||
*/
|
*/
|
||||||
export async function parseAndSetStructuredOutputs(
|
export async function parseAndSetStructuredOutputs(
|
||||||
@@ -144,7 +144,7 @@ export async function parseAndSetStructuredOutputs(
|
|||||||
|
|
||||||
if (!result?.structured_output) {
|
if (!result?.structured_output) {
|
||||||
throw new Error(
|
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`,
|
`Found ${messages.length} messages. Result exists: ${!!result}\n`,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -167,6 +167,9 @@ export async function parseAndSetStructuredOutputs(
|
|||||||
export async function runClaude(promptPath: string, options: ClaudeOptions) {
|
export async function runClaude(promptPath: string, options: ClaudeOptions) {
|
||||||
const config = prepareRunConfig(promptPath, options);
|
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
|
// Create a named pipe
|
||||||
try {
|
try {
|
||||||
await unlink(PIPE_PATH);
|
await unlink(PIPE_PATH);
|
||||||
@@ -352,8 +355,8 @@ export async function runClaude(promptPath: string, options: ClaudeOptions) {
|
|||||||
|
|
||||||
core.setOutput("execution_file", EXECUTION_FILE);
|
core.setOutput("execution_file", EXECUTION_FILE);
|
||||||
|
|
||||||
// Parse and set structured outputs only if user provided json_schema
|
// Parse and set structured outputs only if user provided --json-schema in claude_args
|
||||||
if (process.env.JSON_SCHEMA) {
|
if (hasJsonSchema) {
|
||||||
try {
|
try {
|
||||||
await parseAndSetStructuredOutputs(EXECUTION_FILE);
|
await parseAndSetStructuredOutputs(EXECUTION_FILE);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
|
|||||||
@@ -1,39 +1,50 @@
|
|||||||
/**
|
/**
|
||||||
* Validates the environment variables required for running Claude Code
|
* 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() {
|
export function validateEnvironmentVariables() {
|
||||||
const useBedrock = process.env.CLAUDE_CODE_USE_BEDROCK === "1";
|
const useBedrock = process.env.CLAUDE_CODE_USE_BEDROCK === "1";
|
||||||
const useVertex = process.env.CLAUDE_CODE_USE_VERTEX === "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 anthropicApiKey = process.env.ANTHROPIC_API_KEY;
|
||||||
const claudeCodeOAuthToken = process.env.CLAUDE_CODE_OAUTH_TOKEN;
|
const claudeCodeOAuthToken = process.env.CLAUDE_CODE_OAUTH_TOKEN;
|
||||||
|
|
||||||
const errors: string[] = [];
|
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(
|
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) {
|
if (!anthropicApiKey && !claudeCodeOAuthToken) {
|
||||||
errors.push(
|
errors.push(
|
||||||
"Either ANTHROPIC_API_KEY or CLAUDE_CODE_OAUTH_TOKEN is required when using direct Anthropic API.",
|
"Either ANTHROPIC_API_KEY or CLAUDE_CODE_OAUTH_TOKEN is required when using direct Anthropic API.",
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
} else if (useBedrock) {
|
} else if (useBedrock) {
|
||||||
const requiredBedrockVars = {
|
const awsRegion = process.env.AWS_REGION;
|
||||||
AWS_REGION: process.env.AWS_REGION,
|
const awsAccessKeyId = process.env.AWS_ACCESS_KEY_ID;
|
||||||
AWS_ACCESS_KEY_ID: process.env.AWS_ACCESS_KEY_ID,
|
const awsSecretAccessKey = process.env.AWS_SECRET_ACCESS_KEY;
|
||||||
AWS_SECRET_ACCESS_KEY: process.env.AWS_SECRET_ACCESS_KEY,
|
const awsBearerToken = process.env.AWS_BEARER_TOKEN_BEDROCK;
|
||||||
};
|
|
||||||
|
|
||||||
Object.entries(requiredBedrockVars).forEach(([key, value]) => {
|
// AWS_REGION is always required for Bedrock
|
||||||
if (!value) {
|
if (!awsRegion) {
|
||||||
errors.push(`${key} is required when using AWS Bedrock.`);
|
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) {
|
} else if (useVertex) {
|
||||||
const requiredVertexVars = {
|
const requiredVertexVars = {
|
||||||
ANTHROPIC_VERTEX_PROJECT_ID: process.env.ANTHROPIC_VERTEX_PROJECT_ID,
|
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.`);
|
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) {
|
if (errors.length > 0) {
|
||||||
|
|||||||
@@ -113,7 +113,7 @@ describe("parseAndSetStructuredOutputs", () => {
|
|||||||
await expect(
|
await expect(
|
||||||
parseAndSetStructuredOutputs(TEST_EXECUTION_FILE),
|
parseAndSetStructuredOutputs(TEST_EXECUTION_FILE),
|
||||||
).rejects.toThrow(
|
).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(
|
await expect(
|
||||||
parseAndSetStructuredOutputs(TEST_EXECUTION_FILE),
|
parseAndSetStructuredOutputs(TEST_EXECUTION_FILE),
|
||||||
).rejects.toThrow(
|
).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.ANTHROPIC_API_KEY;
|
||||||
delete process.env.CLAUDE_CODE_USE_BEDROCK;
|
delete process.env.CLAUDE_CODE_USE_BEDROCK;
|
||||||
delete process.env.CLAUDE_CODE_USE_VERTEX;
|
delete process.env.CLAUDE_CODE_USE_VERTEX;
|
||||||
|
delete process.env.CLAUDE_CODE_USE_FOUNDRY;
|
||||||
delete process.env.AWS_REGION;
|
delete process.env.AWS_REGION;
|
||||||
delete process.env.AWS_ACCESS_KEY_ID;
|
delete process.env.AWS_ACCESS_KEY_ID;
|
||||||
delete process.env.AWS_SECRET_ACCESS_KEY;
|
delete process.env.AWS_SECRET_ACCESS_KEY;
|
||||||
delete process.env.AWS_SESSION_TOKEN;
|
delete process.env.AWS_SESSION_TOKEN;
|
||||||
|
delete process.env.AWS_BEARER_TOKEN_BEDROCK;
|
||||||
delete process.env.ANTHROPIC_BEDROCK_BASE_URL;
|
delete process.env.ANTHROPIC_BEDROCK_BASE_URL;
|
||||||
delete process.env.ANTHROPIC_VERTEX_PROJECT_ID;
|
delete process.env.ANTHROPIC_VERTEX_PROJECT_ID;
|
||||||
delete process.env.CLOUD_ML_REGION;
|
delete process.env.CLOUD_ML_REGION;
|
||||||
delete process.env.GOOGLE_APPLICATION_CREDENTIALS;
|
delete process.env.GOOGLE_APPLICATION_CREDENTIALS;
|
||||||
delete process.env.ANTHROPIC_VERTEX_BASE_URL;
|
delete process.env.ANTHROPIC_VERTEX_BASE_URL;
|
||||||
|
delete process.env.ANTHROPIC_FOUNDRY_RESOURCE;
|
||||||
|
delete process.env.ANTHROPIC_FOUNDRY_BASE_URL;
|
||||||
});
|
});
|
||||||
|
|
||||||
afterEach(() => {
|
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.CLAUDE_CODE_USE_BEDROCK = "1";
|
||||||
process.env.AWS_REGION = "us-east-1";
|
process.env.AWS_REGION = "us-east-1";
|
||||||
process.env.AWS_SECRET_ACCESS_KEY = "test-secret-key";
|
process.env.AWS_SECRET_ACCESS_KEY = "test-secret-key";
|
||||||
|
|
||||||
expect(() => validateEnvironmentVariables()).toThrow(
|
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.CLAUDE_CODE_USE_BEDROCK = "1";
|
||||||
process.env.AWS_REGION = "us-east-1";
|
process.env.AWS_REGION = "us-east-1";
|
||||||
process.env.AWS_ACCESS_KEY_ID = "test-access-key";
|
process.env.AWS_ACCESS_KEY_ID = "test-access-key";
|
||||||
|
|
||||||
expect(() => validateEnvironmentVariables()).toThrow(
|
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";
|
process.env.CLAUDE_CODE_USE_BEDROCK = "1";
|
||||||
|
|
||||||
expect(() => validateEnvironmentVariables()).toThrow(
|
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", () => {
|
describe("Multiple providers", () => {
|
||||||
test("should fail when both Bedrock and Vertex are enabled", () => {
|
test("should fail when both Bedrock and Vertex are enabled", () => {
|
||||||
process.env.CLAUDE_CODE_USE_BEDROCK = "1";
|
process.env.CLAUDE_CODE_USE_BEDROCK = "1";
|
||||||
@@ -179,7 +260,51 @@ describe("validateEnvironmentVariables", () => {
|
|||||||
process.env.CLOUD_ML_REGION = "us-central1";
|
process.env.CLOUD_ML_REGION = "us-central1";
|
||||||
|
|
||||||
expect(() => validateEnvironmentVariables()).toThrow(
|
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.",
|
" - AWS_REGION is required when using AWS Bedrock.",
|
||||||
);
|
);
|
||||||
expect(error!.message).toContain(
|
expect(error!.message).toContain(
|
||||||
" - 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.",
|
||||||
);
|
|
||||||
expect(error!.message).toContain(
|
|
||||||
" - AWS_SECRET_ACCESS_KEY is required when using AWS Bedrock.",
|
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -1,16 +1,17 @@
|
|||||||
# Cloud Providers
|
# 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)
|
1. Direct Anthropic API (default)
|
||||||
2. Amazon Bedrock with OIDC authentication
|
2. Amazon Bedrock with OIDC authentication
|
||||||
3. Google Vertex AI 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).
|
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**:
|
**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
|
- 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
|
- 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: |
|
claude_args: |
|
||||||
--model claude-4-0-sonnet@20250805
|
--model claude-4-0-sonnet@20250805
|
||||||
# ... other inputs
|
# ... 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
|
```yaml
|
||||||
# For AWS Bedrock with OIDC
|
# For AWS Bedrock with OIDC
|
||||||
@@ -97,3 +106,36 @@ Both AWS Bedrock and GCP Vertex AI require OIDC authentication.
|
|||||||
permissions:
|
permissions:
|
||||||
id-token: write # Required for OIDC
|
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
|
## 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
|
## ⚠️ 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 | "" |
|
| `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 | "" |
|
| `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 | "" |
|
| `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
|
### Deprecated Inputs
|
||||||
|
|
||||||
@@ -201,16 +200,8 @@ Get validated JSON results from Claude that automatically become GitHub Action o
|
|||||||
prompt: |
|
prompt: |
|
||||||
Check the CI logs and determine if this is a flaky test.
|
Check the CI logs and determine if this is a flaky test.
|
||||||
Return: is_flaky (boolean), confidence (0-1), summary (string)
|
Return: is_flaky (boolean), confidence (0-1), summary (string)
|
||||||
json_schema: |
|
claude_args: |
|
||||||
{
|
--json-schema '{"type":"object","properties":{"is_flaky":{"type":"boolean"},"confidence":{"type":"number"},"summary":{"type":"string"}},"required":["is_flaky"]}'
|
||||||
"type": "object",
|
|
||||||
"properties": {
|
|
||||||
"is_flaky": {"type": "boolean"},
|
|
||||||
"confidence": {"type": "number"},
|
|
||||||
"summary": {"type": "string"}
|
|
||||||
},
|
|
||||||
"required": ["is_flaky"]
|
|
||||||
}
|
|
||||||
|
|
||||||
- name: Retry if flaky
|
- name: Retry if flaky
|
||||||
if: fromJSON(steps.analyze.outputs.structured_output).is_flaky == true
|
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
|
### 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
|
2. **Claude Executes**: Claude uses tools to complete your task
|
||||||
3. **Validated Output**: Result is validated against your schema
|
3. **Validated Output**: Result is validated against your schema
|
||||||
4. **JSON Output**: All fields are returned in a single `structured_output` JSON string
|
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
|
- is_flaky: true if likely flaky, false if real bug
|
||||||
- confidence: number 0-1 indicating confidence level
|
- confidence: number 0-1 indicating confidence level
|
||||||
- summary: brief one-sentence explanation
|
- summary: brief one-sentence explanation
|
||||||
json_schema: |
|
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"]}'
|
||||||
"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)
|
# Auto-retry only if flaky AND high confidence (>= 0.7)
|
||||||
- name: Retry flaky tests
|
- name: Retry flaky tests
|
||||||
|
|||||||
@@ -7,7 +7,6 @@ import { parseAllowedTools } from "./parse-tools";
|
|||||||
import { configureGitAuth } from "../../github/operations/git-config";
|
import { configureGitAuth } from "../../github/operations/git-config";
|
||||||
import type { GitHubContext } from "../../github/context";
|
import type { GitHubContext } from "../../github/context";
|
||||||
import { isEntityContext } from "../../github/context";
|
import { isEntityContext } from "../../github/context";
|
||||||
import { appendJsonSchemaArg } from "../../utils/json-schema";
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Extract GitHub context as environment variables for agent mode
|
* Extract GitHub context as environment variables for agent mode
|
||||||
@@ -150,9 +149,6 @@ export const agentMode: Mode = {
|
|||||||
claudeArgs = `--mcp-config '${escapedOurConfig}'`;
|
claudeArgs = `--mcp-config '${escapedOurConfig}'`;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add JSON schema if provided
|
|
||||||
claudeArgs = appendJsonSchemaArg(claudeArgs);
|
|
||||||
|
|
||||||
// Append user's claude_args (which may have more --mcp-config flags)
|
// Append user's claude_args (which may have more --mcp-config flags)
|
||||||
claudeArgs = `${claudeArgs} ${userClaudeArgs}`.trim();
|
claudeArgs = `${claudeArgs} ${userClaudeArgs}`.trim();
|
||||||
|
|
||||||
|
|||||||
@@ -15,7 +15,6 @@ import { isEntityContext } from "../../github/context";
|
|||||||
import type { PreparedContext } from "../../create-prompt/types";
|
import type { PreparedContext } from "../../create-prompt/types";
|
||||||
import type { FetchDataResult } from "../../github/data/fetcher";
|
import type { FetchDataResult } from "../../github/data/fetcher";
|
||||||
import { parseAllowedTools } from "../agent/parse-tools";
|
import { parseAllowedTools } from "../agent/parse-tools";
|
||||||
import { appendJsonSchemaArg } from "../../utils/json-schema";
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Tag mode implementation.
|
* Tag mode implementation.
|
||||||
@@ -178,9 +177,6 @@ export const tagMode: Mode = {
|
|||||||
// Add required tools for tag mode
|
// Add required tools for tag mode
|
||||||
claudeArgs += ` --allowedTools "${tagModeTools.join(",")}"`;
|
claudeArgs += ` --allowedTools "${tagModeTools.join(",")}"`;
|
||||||
|
|
||||||
// Add JSON schema if provided
|
|
||||||
claudeArgs = appendJsonSchemaArg(claudeArgs);
|
|
||||||
|
|
||||||
// Append user's claude_args (which may have more --mcp-config flags)
|
// Append user's claude_args (which may have more --mcp-config flags)
|
||||||
if (userClaudeArgs) {
|
if (userClaudeArgs) {
|
||||||
claudeArgs += ` ${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