Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 15 additions & 5 deletions .github/actions/resolve-config/callers.test.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -1367,12 +1367,20 @@ test("every job that reads a Claude secret is a catalog lane, and every catalog
});

// run-claude-cli names the lane record's credential from `role:`, never from
// the secret passed, so the role and the secret are checked together.
// the secret passed, so the role and the secret are checked together. The one
// exception is the central fallback token, the same org secret for every role,
// which only a call of a ci-workflows reusable with a fallback job may pass
// (intake-triage.yml and pr-review-cli.yml from v0.65.12, ci-workflows#890).
const FALLBACK_SECRET = "FALLBACK_CLAUDE_CODE_OAUTH_TOKEN";
const FALLBACK_REUSABLE = /^melodic-software\/ci-workflows\/\.github\/workflows\/(?:intake-triage|pr-review-cli)\.yml@[0-9a-f]{40}$/;
test("every catalog lane reads only its role's secrets, and every role: it passes is its role", () => {
const jobs = claudeSecretJobs();
for (const { role, lane, key } of catalogLanes()) {
for (const name of lane.currentSecrets) assert.ok([role.keySecret, role.tokenSecret].includes(name), `${key} reads ${name}`);
const { job } = jobs.get(key);
const fallback = FALLBACK_REUSABLE.test(String(job.uses ?? "")) && job.secrets?.[FALLBACK_SECRET] === `\${{ secrets.${FALLBACK_SECRET} }}`;
for (const name of lane.currentSecrets) {
assert.ok([role.keySecret, role.tokenSecret, ...(fallback ? [FALLBACK_SECRET] : [])].includes(name), `${key} reads ${name}`);
}
const roles = [job.with?.role, ...(job.steps ?? []).map((step) => step.with?.role)].filter((value) => value !== undefined);
for (const value of roles) assert.equal(value, role.id, key);
}
Expand Down Expand Up @@ -1537,7 +1545,7 @@ test("pr-run-activity-read.yml: the run job can only read, and each launcher ste
const { jobs } = workflow("pr-run-activity-read.yml");
assert.deepEqual(jobs.run.permissions, { contents: "read", "pull-requests": "read" }, "read-only, no id-token");
const step = (id) => jobs.run.steps.find((s) => s.id === id);
const launcher = "melodic-software/ci-workflows/.github/actions/run-claude-cli@e5167fc29d948ab7d0923cf62f5ea9eeeef8e9e8";
const launcher = "melodic-software/ci-workflows/.github/actions/run-claude-cli@ba7af78d291f10bd75651857a0db2357c23df0fe";
for (const [id, input, secret] of [
["act-key", "api-key", "claude-api-key"],
["act-token", "subscription-token", "claude-code-oauth-token"],
Expand Down Expand Up @@ -1594,13 +1602,15 @@ test("no run-claude-cli plugin-dirs or add-dirs value names runner.temp, RUNNER_
// Launcher SHAs checked to pass `--permission-prompts none` unconditionally,
// under which the default mode denies every call no rule allows, as dontAsk
// does; check-permission-mode.sh accepts default for dontAsk on that basis.
// 7b362428: lane.cjs:311-312; 164c2809: lane.cjs:355-358; d02b8697 and e5167fc2: lane.cjs:357-358; in cliArgs. A pin
// 7b362428: lane.cjs:311-312; 164c2809: lane.cjs:355-358; d02b8697 and e5167fc2: lane.cjs:357-358;
// ba7af78d291f10bd75651857a0db2357c23df0fe: lane.cjs:404-405; in cliArgs. A pin
Comment thread
kyle-sexton marked this conversation as resolved.
// bump fails here until its lane.cjs is read and its SHA added.
const PERMISSION_PROMPTS_NONE_LAUNCHERS = new Set([
"7b362428c76085a160e28656cd2c75c3147a69ff",
"164c2809f6150429b6f24d4a2777bb377af953d9",
"d02b8697e092705153f0996549b05f95a446bb52",
"e5167fc29d948ab7d0923cf62f5ea9eeeef8e9e8",
"ba7af78d291f10bd75651857a0db2357c23df0fe",
]);

test("pr-run-activity-read.yml: every run-claude-cli pin is a launcher checked to pass --permission-prompts none", () => {
Expand Down Expand Up @@ -3703,7 +3713,7 @@ test("scan job: every gate, skip and launcher class value the runner sets passes
const schema = JSON.parse(read("../../../docs/conventions/pr-pipeline/pr-pipeline.schema.json"));
const gateReasons = Object.keys(JSON.parse(read("../report-check-run/gate-skip-reasons.json")));
// run-claude-cli's failure classes, the keys of SUMMARY_NOTES in lane.cjs at
// the pinned e5167fc2. cant-fail-ok: an external contract's value list.
// the pinned ba7af78d291f10bd75651857a0db2357c23df0fe. cant-fail-ok: an external contract's value list.
const laneClasses = ["credit-stop", "rate-limit", "auth", "overloaded", "max-turns", "timeout", "other"];
const cases = [
...schema.$defs["skip-reason"].enum.map((value) => ["skip-reason", value]),
Expand Down
6 changes: 3 additions & 3 deletions .github/lane-funding.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,8 @@
"keyed": true,
"category": "review",
"lanes": [
{ "id": "pr-review", "workflow": "pr-review-hosted.yml", "launcher": "run-claude-cli", "triggers": ["pull_request"], "currentSecrets": ["REVIEWS_CLAUDE_API_KEY", "REVIEWS_CLAUDE_CODE_OAUTH_TOKEN"] },
{ "id": "pr-review-security", "workflow": "pr-review-security-hosted.yml", "launcher": "run-claude-cli", "triggers": ["pull_request"], "currentSecrets": ["REVIEWS_CLAUDE_API_KEY", "REVIEWS_CLAUDE_CODE_OAUTH_TOKEN"] }
{ "id": "pr-review", "workflow": "pr-review-hosted.yml", "launcher": "run-claude-cli", "triggers": ["pull_request"], "currentSecrets": ["REVIEWS_CLAUDE_API_KEY", "REVIEWS_CLAUDE_CODE_OAUTH_TOKEN", "FALLBACK_CLAUDE_CODE_OAUTH_TOKEN"] },
{ "id": "pr-review-security", "workflow": "pr-review-security-hosted.yml", "launcher": "run-claude-cli", "triggers": ["pull_request"], "currentSecrets": ["REVIEWS_CLAUDE_API_KEY", "REVIEWS_CLAUDE_CODE_OAUTH_TOKEN", "FALLBACK_CLAUDE_CODE_OAUTH_TOKEN"] }
]
},
{
Expand All @@ -34,7 +34,7 @@
"keyed": true,
"category": "intake",
"lanes": [
{ "id": "intake-triage", "workflow": "intake-triage.yml", "launcher": "run-claude-cli", "triggers": ["issues", "workflow_dispatch"], "gate": "author", "currentSecrets": ["INTAKE_CLAUDE_API_KEY", "INTAKE_CLAUDE_CODE_OAUTH_TOKEN"] },
{ "id": "intake-triage", "workflow": "intake-triage.yml", "launcher": "run-claude-cli", "triggers": ["issues", "workflow_dispatch"], "gate": "author", "currentSecrets": ["INTAKE_CLAUDE_API_KEY", "INTAKE_CLAUDE_CODE_OAUTH_TOKEN", "FALLBACK_CLAUDE_CODE_OAUTH_TOKEN"] },
{ "id": "refine-issue", "workflow": "intake-triage.yml", "launcher": "claude-code-action", "triggers": ["issues", "workflow_dispatch"], "gate": "author", "currentSecrets": ["INTAKE_CLAUDE_CODE_OAUTH_TOKEN"] }
]
},
Expand Down
2 changes: 1 addition & 1 deletion .github/scripts/pr-verify/probe/replay.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ const REPOSITORY = "melodic-software/claude-code-plugins";
// apply-claude-managed-settings to. cliArgs, LEG_ENV, CLI_VERSION and
// MANAGED_SETTINGS were read from that SHA; the parity test fails when the
// pin moves, so they are read again.
export const RUN_CLAUDE_CLI_PIN = "e5167fc29d948ab7d0923cf62f5ea9eeeef8e9e8";
export const RUN_CLAUDE_CLI_PIN = "ba7af78d291f10bd75651857a0db2357c23df0fe";
export const CLI_VERSION = "latest";
export const MANAGED_SETTINGS = Object.freeze({
apiKeyHelper: "",
Expand Down
5 changes: 4 additions & 1 deletion .github/workflows/intake-triage.yml
Original file line number Diff line number Diff line change
Expand Up @@ -201,7 +201,7 @@ jobs:
concurrency:
group: intake-triage-${{ github.event.issue.number || inputs.issue-number || github.run_id }}
cancel-in-progress: false
uses: melodic-software/ci-workflows/.github/workflows/intake-triage.yml@e5167fc29d948ab7d0923cf62f5ea9eeeef8e9e8 # v0.65.11
uses: melodic-software/ci-workflows/.github/workflows/intake-triage.yml@ba7af78d291f10bd75651857a0db2357c23df0fe # v0.65.13
with:
runner: ubuntu-24.04
role: intake
Expand Down Expand Up @@ -330,9 +330,12 @@ jobs:
e. When the decision settles every open question and no person is needed, apply no status
label, quote the decision, say in one sentence that it settles the open questions, and ask
nothing again. Triage the rest as usual.
# Named secrets only, never `secrets: inherit`. The central fallback token
# runs only after the subscription token hands off.
secrets:
CLAUDE_API_KEY: ${{ secrets.INTAKE_CLAUDE_API_KEY }}
CLAUDE_CODE_OAUTH_TOKEN: ${{ secrets.INTAKE_CLAUDE_CODE_OAUTH_TOKEN }}
FALLBACK_CLAUDE_CODE_OAUTH_TOKEN: ${{ secrets.FALLBACK_CLAUDE_CODE_OAUTH_TOKEN }}

# Writes the triage run's verdict on the issue as the lanes App, so the work
# lanes count it: the work-class label, the Size, Complexity and Uncertainty
Expand Down
6 changes: 4 additions & 2 deletions .github/workflows/pr-review-hosted.yml
Original file line number Diff line number Diff line change
Expand Up @@ -169,7 +169,7 @@ jobs:
contents: read
pull-requests: write # the review, its inline comments and the lane's
# reviewed-head marker comment
uses: melodic-software/ci-workflows/.github/workflows/pr-review-cli.yml@e5167fc29d948ab7d0923cf62f5ea9eeeef8e9e8 # v0.65.11
uses: melodic-software/ci-workflows/.github/workflows/pr-review-cli.yml@ba7af78d291f10bd75651857a0db2357c23df0fe # v0.65.13
with:
lane: code
publish: comments
Expand Down Expand Up @@ -203,7 +203,9 @@ jobs:
**/CODEOWNERS
**/.gitmodules
# The reviews role's key runs first; its subscription token only after the
# key hands off. Named secrets only, never `secrets: inherit`.
# key hands off, and the central fallback token only after the
# subscription token hands off. Named secrets only, never `secrets: inherit`.
secrets:
CLAUDE_API_KEY: ${{ secrets.REVIEWS_CLAUDE_API_KEY }}
CLAUDE_CODE_OAUTH_TOKEN: ${{ secrets.REVIEWS_CLAUDE_CODE_OAUTH_TOKEN }}
FALLBACK_CLAUDE_CODE_OAUTH_TOKEN: ${{ secrets.FALLBACK_CLAUDE_CODE_OAUTH_TOKEN }}
6 changes: 4 additions & 2 deletions .github/workflows/pr-review-security-hosted.yml
Original file line number Diff line number Diff line change
Expand Up @@ -174,7 +174,7 @@ jobs:
contents: read
pull-requests: write # the review, its inline comments and the lane's
# reviewed-head marker comment
uses: melodic-software/ci-workflows/.github/workflows/pr-review-cli.yml@e5167fc29d948ab7d0923cf62f5ea9eeeef8e9e8 # v0.65.11
uses: melodic-software/ci-workflows/.github/workflows/pr-review-cli.yml@ba7af78d291f10bd75651857a0db2357c23df0fe # v0.65.13
with:
lane: security
publish: comments
Expand Down Expand Up @@ -203,7 +203,9 @@ jobs:
**/CODEOWNERS
**/.gitmodules
# The reviews role's key runs first; its subscription token only after the
# key hands off. Named secrets only, never `secrets: inherit`.
# key hands off, and the central fallback token only after the
# subscription token hands off. Named secrets only, never `secrets: inherit`.
secrets:
CLAUDE_API_KEY: ${{ secrets.REVIEWS_CLAUDE_API_KEY }}
CLAUDE_CODE_OAUTH_TOKEN: ${{ secrets.REVIEWS_CLAUDE_CODE_OAUTH_TOKEN }}
FALLBACK_CLAUDE_CODE_OAUTH_TOKEN: ${{ secrets.FALLBACK_CLAUDE_CODE_OAUTH_TOKEN }}
14 changes: 7 additions & 7 deletions .github/workflows/pr-run-activity-read.yml
Original file line number Diff line number Diff line change
Expand Up @@ -1059,7 +1059,7 @@ jobs:
# need sudo, so they are written before the runner is isolated.
- name: Apply the Claude managed settings
if: steps.launch.outcome == 'success' && steps.go.outputs.run == 'true'
uses: melodic-software/ci-workflows/.github/actions/apply-claude-managed-settings@e5167fc29d948ab7d0923cf62f5ea9eeeef8e9e8 # v0.65.11
uses: melodic-software/ci-workflows/.github/actions/apply-claude-managed-settings@ba7af78d291f10bd75651857a0db2357c23df0fe # v0.65.13

# Nothing after this step needs sudo or docker.
- name: Isolate the runner
Expand Down Expand Up @@ -1344,7 +1344,7 @@ jobs:
if: steps.key-present.outputs.present == 'true'
continue-on-error: true
timeout-minutes: ${{ fromJSON(steps.launch.outputs.step-minutes || '50') }}
uses: melodic-software/ci-workflows/.github/actions/run-claude-cli@e5167fc29d948ab7d0923cf62f5ea9eeeef8e9e8 # v0.65.11
uses: melodic-software/ci-workflows/.github/actions/run-claude-cli@ba7af78d291f10bd75651857a0db2357c23df0fe # v0.65.13
env:
# No work may outlive the result message the launcher reads.
CLAUDE_CODE_DISABLE_BACKGROUND_TASKS: "1"
Expand Down Expand Up @@ -1396,7 +1396,7 @@ jobs:
if: steps.token-present.outputs.present == 'true'
continue-on-error: true
timeout-minutes: ${{ fromJSON(steps.launch.outputs.step-minutes || '50') }}
uses: melodic-software/ci-workflows/.github/actions/run-claude-cli@e5167fc29d948ab7d0923cf62f5ea9eeeef8e9e8 # v0.65.11
uses: melodic-software/ci-workflows/.github/actions/run-claude-cli@ba7af78d291f10bd75651857a0db2357c23df0fe # v0.65.13
env:
CLAUDE_CODE_DISABLE_BACKGROUND_TASKS: "1"
LD_LIBRARY_PATH: ""
Expand Down Expand Up @@ -1611,7 +1611,7 @@ jobs:
always() && steps.key-present.outputs.present != 'false' && steps.act-key.outputs.handoff != 'true'
&& (steps.act-key.outcome == 'success' || steps.act-key.outcome == 'failure'
|| steps.act-token.outcome == 'success' || steps.act-token.outcome == 'failure')
uses: melodic-software/ci-workflows/.github/actions/report-lane-outcome@e5167fc29d948ab7d0923cf62f5ea9eeeef8e9e8 # v0.65.11
uses: melodic-software/ci-workflows/.github/actions/report-lane-outcome@ba7af78d291f10bd75651857a0db2357c23df0fe # v0.65.13
with:
outcome: ${{ steps.act-key.outcome != 'skipped' && steps.act-key.outcome || steps.act-token.outcome }}
execution-file: ${{ steps.act-key.outputs.execution-file || steps.act-token.outputs.execution-file }}
Expand Down Expand Up @@ -2958,7 +2958,7 @@ jobs:
id: summary-settings
if: steps.summary-prompt.outcome == 'success'
continue-on-error: true
uses: melodic-software/ci-workflows/.github/actions/apply-claude-managed-settings@e5167fc29d948ab7d0923cf62f5ea9eeeef8e9e8 # v0.65.11
uses: melodic-software/ci-workflows/.github/actions/apply-claude-managed-settings@ba7af78d291f10bd75651857a0db2357c23df0fe # v0.65.13

- name: Isolate the runner
id: isolate
Expand All @@ -2977,7 +2977,7 @@ jobs:
if: steps.isolate.outcome == 'success' && inputs.fallback == ''
continue-on-error: true
timeout-minutes: 15
uses: melodic-software/ci-workflows/.github/actions/run-claude-cli@e5167fc29d948ab7d0923cf62f5ea9eeeef8e9e8 # v0.65.11
uses: melodic-software/ci-workflows/.github/actions/run-claude-cli@ba7af78d291f10bd75651857a0db2357c23df0fe # v0.65.13
env:
CLAUDE_CODE_DISABLE_BACKGROUND_TASKS: "1"
with:
Expand All @@ -3003,7 +3003,7 @@ jobs:
if: steps.isolate.outcome == 'success' && inputs.fallback != ''
continue-on-error: true
timeout-minutes: 15
uses: melodic-software/ci-workflows/.github/actions/run-claude-cli@e5167fc29d948ab7d0923cf62f5ea9eeeef8e9e8 # v0.65.11
uses: melodic-software/ci-workflows/.github/actions/run-claude-cli@ba7af78d291f10bd75651857a0db2357c23df0fe # v0.65.13
env:
CLAUDE_CODE_DISABLE_BACKGROUND_TASKS: "1"
with:
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/pr-run-activity-write.yml
Original file line number Diff line number Diff line change
Expand Up @@ -1644,7 +1644,7 @@ jobs:
- name: Record the lane outcome
id: lane-record
if: always() && (steps.act-skill.outcome == 'success' || steps.act-skill.outcome == 'failure')
uses: melodic-software/ci-workflows/.github/actions/report-lane-outcome@e5167fc29d948ab7d0923cf62f5ea9eeeef8e9e8 # v0.65.11
uses: melodic-software/ci-workflows/.github/actions/report-lane-outcome@ba7af78d291f10bd75651857a0db2357c23df0fe # v0.65.13
with:
outcome: ${{ steps.act-result.outputs.outcome || steps.act-skill.outcome }}
execution-file: >-
Expand Down
8 changes: 4 additions & 4 deletions .github/workflows/pr-unblock.yml
Original file line number Diff line number Diff line change
Expand Up @@ -935,7 +935,7 @@ jobs:
# need sudo, so they are written before the runner is isolated.
- name: Apply the Claude managed settings
if: steps.key.outputs.present == 'true'
uses: melodic-software/ci-workflows/.github/actions/apply-claude-managed-settings@e5167fc29d948ab7d0923cf62f5ea9eeeef8e9e8 # v0.65.11
uses: melodic-software/ci-workflows/.github/actions/apply-claude-managed-settings@ba7af78d291f10bd75651857a0db2357c23df0fe # v0.65.13

# Nothing after this step needs sudo or docker.
- name: Isolate the runner
Expand Down Expand Up @@ -968,7 +968,7 @@ jobs:
if: steps.key.outputs.present == 'true' && fromJSON(steps.prepare.outputs.count || '0') > 0
continue-on-error: true
timeout-minutes: 20
uses: melodic-software/ci-workflows/.github/actions/run-claude-cli@e5167fc29d948ab7d0923cf62f5ea9eeeef8e9e8 # v0.65.11
uses: melodic-software/ci-workflows/.github/actions/run-claude-cli@ba7af78d291f10bd75651857a0db2357c23df0fe # v0.65.13
env:
CLAUDE_CODE_DISABLE_BACKGROUND_TASKS: "1"
LD_LIBRARY_PATH: ""
Expand Down Expand Up @@ -1029,7 +1029,7 @@ jobs:
if: steps.key.outputs.present == 'true' && fromJSON(steps.prepare.outputs.count || '0') > 1
continue-on-error: true
timeout-minutes: 20
uses: melodic-software/ci-workflows/.github/actions/run-claude-cli@e5167fc29d948ab7d0923cf62f5ea9eeeef8e9e8 # v0.65.11
uses: melodic-software/ci-workflows/.github/actions/run-claude-cli@ba7af78d291f10bd75651857a0db2357c23df0fe # v0.65.13
env:
CLAUDE_CODE_DISABLE_BACKGROUND_TASKS: "1"
LD_LIBRARY_PATH: ""
Expand Down Expand Up @@ -1090,7 +1090,7 @@ jobs:
if: steps.key.outputs.present == 'true' && fromJSON(steps.prepare.outputs.count || '0') > 2
continue-on-error: true
timeout-minutes: 20
uses: melodic-software/ci-workflows/.github/actions/run-claude-cli@e5167fc29d948ab7d0923cf62f5ea9eeeef8e9e8 # v0.65.11
uses: melodic-software/ci-workflows/.github/actions/run-claude-cli@ba7af78d291f10bd75651857a0db2357c23df0fe # v0.65.13
env:
CLAUDE_CODE_DISABLE_BACKGROUND_TASKS: "1"
LD_LIBRARY_PATH: ""
Expand Down
Loading
Loading