diff --git a/.github/workflows/test-mcp-servers.yml b/.github/workflows/test-mcp-servers.yml index 3f20eee..33dd334 100644 --- a/.github/workflows/test-mcp-servers.yml +++ b/.github/workflows/test-mcp-servers.yml @@ -27,7 +27,7 @@ jobs: with: prompt: "List all available tools" anthropic_api_key: ${{ secrets.ANTHROPIC_API_KEY }} - # base-action defaults setting_sources to "user"; opt in to project so .mcp.json is discovered + # Explicitly include project so .mcp.json is discovered regardless of the event-gated default setting_sources: "user,project" env: # Change to test directory so it finds .mcp.json @@ -110,8 +110,9 @@ jobs: with: prompt: "List all available tools" anthropic_api_key: ${{ secrets.ANTHROPIC_API_KEY }} - # mcp_config input was removed; pass via claude_args. setting_sources stays at the - # default ("user") so .mcp.json is NOT auto-discovered — this proves the flag works. + # mcp_config input was removed; pass via claude_args. Pin setting_sources to "user" + # so .mcp.json is NOT auto-discovered — this proves the flag itself works. + setting_sources: "user" claude_args: >- --mcp-config '{"mcpServers":{"test-server":{"type":"stdio","command":"bun","args":["simple-mcp-server.ts"],"env":{}}}}' env: diff --git a/base-action/README.md b/base-action/README.md index f1f89d2..8fccdb8 100644 --- a/base-action/README.md +++ b/base-action/README.md @@ -94,7 +94,7 @@ Add the following to your workflow file: | `max_turns` | Maximum number of conversation turns (default: no limit) | No | '' | | `mcp_config` | Path to the MCP configuration JSON file, or MCP configuration JSON string | No | '' | | `settings` | Path to Claude Code settings JSON file, or settings JSON string | No | '' | -| `setting_sources` | Comma-separated setting sources to load (`user`, `project`, `local`). Project/local merge permissions additively. | No | 'user' | +| `setting_sources` | Comma-separated setting sources to load (`user`, `project`, `local`). Project/local merge permissions additively. | No | event-dependent (see below) | | `system_prompt` | Override system prompt | No | '' | | `append_system_prompt` | Append to system prompt | No | '' | | `claude_env` | Custom environment variables to pass to Claude Code execution (YAML multiline format) | No | '' | diff --git a/base-action/action.yml b/base-action/action.yml index 4d78acf..24d0949 100644 --- a/base-action/action.yml +++ b/base-action/action.yml @@ -19,7 +19,7 @@ inputs: required: false default: "" setting_sources: - description: "Comma-separated list of setting sources to load (user, project, local). Defaults to 'user' only. Project/local settings additively merge permissions with allowed_tools — set to 'user,project,local' to opt in." + description: "Comma-separated list of setting sources to load (user, project, local). Defaults to 'user,project,local'; under pull_request_target/workflow_run, defaults to 'user' only. Project/local settings additively merge permissions with allowed_tools — set explicitly to control which sources load." required: false default: "" diff --git a/base-action/src/parse-sdk-options.ts b/base-action/src/parse-sdk-options.ts index 7aab9b7..35d012a 100644 --- a/base-action/src/parse-sdk-options.ts +++ b/base-action/src/parse-sdk-options.ts @@ -272,16 +272,20 @@ export function parseSdkOptions(options: ClaudeOptions): ParsedSdkOptions { env, // Setting sources precedence: direct input > --setting-sources in claude_args > default. - // The default is supplied by the caller (base-action uses ["user"]; the wrapper action - // uses ["user","project","local"]). Both action.yml files leave the YAML default empty - // so that --setting-sources in claude_args is reachable when the input is not set. + // The default is supplied by the caller (the wrapper action passes + // ["user","project","local"]); base-action applies an event-gated default of ["user"] + // under pull_request_target/workflow_run and ["user","project","local"] otherwise. + // Both action.yml files leave the YAML default empty so that --setting-sources in + // claude_args is reachable when the input is not set. settingSources: (options.settingSources ? options.settingSources.split(",").map((s) => s.trim()) : extraArgs["setting-sources"] ? extraArgs["setting-sources"].split(",").map((s) => s.trim()) - : (options.defaultSettingSources ?? [ - "user", - ])) as SdkOptions["settingSources"], + : (options.defaultSettingSources ?? + (process.env.GITHUB_EVENT_NAME === "pull_request_target" || + process.env.GITHUB_EVENT_NAME === "workflow_run" + ? ["user"] + : ["user", "project", "local"]))) as SdkOptions["settingSources"], }; // Remove setting-sources from extraArgs to avoid passing it twice diff --git a/base-action/test/parse-sdk-options.test.ts b/base-action/test/parse-sdk-options.test.ts index 898379b..ea9cd65 100644 --- a/base-action/test/parse-sdk-options.test.ts +++ b/base-action/test/parse-sdk-options.test.ts @@ -1,6 +1,6 @@ #!/usr/bin/env bun -import { describe, test, expect } from "bun:test"; +import { describe, test, expect, afterEach } from "bun:test"; import { parseSdkOptions } from "../src/parse-sdk-options"; import type { ClaudeOptions } from "../src/run-claude"; @@ -424,9 +424,36 @@ describe("parseSdkOptions", () => { }); describe("settingSources", () => { - test("should default to ['user'] when not specified", () => { - const options: ClaudeOptions = {}; - const result = parseSdkOptions(options); + const originalEventName = process.env.GITHUB_EVENT_NAME; + afterEach(() => { + if (originalEventName === undefined) { + delete process.env.GITHUB_EVENT_NAME; + } else { + process.env.GITHUB_EVENT_NAME = originalEventName; + } + }); + + test("should default to ['user','project','local'] for non-gated events", () => { + process.env.GITHUB_EVENT_NAME = "push"; + const result = parseSdkOptions({}); + + expect(result.sdkOptions.settingSources).toEqual([ + "user", + "project", + "local", + ]); + }); + + test("should default to ['user'] under pull_request_target", () => { + process.env.GITHUB_EVENT_NAME = "pull_request_target"; + const result = parseSdkOptions({}); + + expect(result.sdkOptions.settingSources).toEqual(["user"]); + }); + + test("should default to ['user'] under workflow_run", () => { + process.env.GITHUB_EVENT_NAME = "workflow_run"; + const result = parseSdkOptions({}); expect(result.sdkOptions.settingSources).toEqual(["user"]); }); @@ -477,7 +504,8 @@ describe("parseSdkOptions", () => { ]); }); - test("should use defaultSettingSources when nothing else is set", () => { + test("explicit defaultSettingSources overrides the event-gated default", () => { + process.env.GITHUB_EVENT_NAME = "pull_request_target"; const options: ClaudeOptions = { defaultSettingSources: ["user", "project", "local"], };