From 9f54fcc086376b8b59f1720b65ed75e61a72f186 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fabian=20Spie=C3=9F?= Date: Fri, 13 Nov 2026 15:37:42 +0100 Subject: [PATCH 1/2] ci(OP-3891): authenticate Claude with workload identity federation The reusable Claude workflow takes the four ANTHROPIC_* repository variables instead of the ANTHROPIC_API_KEY secret. Public repositories do not see the org variables, so the values are set on this repository. Co-Authored-By: Claude Opus 5.5 --- .github/CLAUDE.md | 10 ++++---- .github/workflows/_claude-code.yml | 25 ++++++++++++++----- ...tomation-operational-excellence-weekly.yml | 5 +--- .../claude-code-automation-pr-review.yml | 4 +-- .github/workflows/claude-code-interactive.yml | 4 +-- 5 files changed, 27 insertions(+), 21 deletions(-) diff --git a/.github/CLAUDE.md b/.github/CLAUDE.md index 95d7f535f..9ea94ae51 100644 --- a/.github/CLAUDE.md +++ b/.github/CLAUDE.md @@ -141,9 +141,10 @@ than copying), `track_progress`, `use_sticky_comment`. Model is `claude-sonnet-4-5-20250929`. **Setup**: installs `uv`, dev tools (`_install_dev_tools.bash`), -`uv sync --all-extras --locked`, headless display. Only secret available is -`ANTHROPIC_API_KEY` — Claude Code workflows intentionally have **no** platform -or GCP credentials. +`uv sync --all-extras --locked`, headless display. Claude authenticates with +workload identity federation through the repository-level `ANTHROPIC_*` Actions +variables; no secret is passed. Claude Code workflows intentionally have **no** +platform or GCP credentials. ## Scheduled jobs @@ -171,8 +172,7 @@ Secrets: `BETTERSTACK_AUDIT_HEARTBEAT_URL`, E2E. **Production** (`platform.aignostics.com`) is used only by scheduled jobs and release validation — never in PR CI. -GitHub secrets: `ANTHROPIC_API_KEY`, -`AIGNOSTICS_CLIENT_ID_DEVICE_{STAGING,PRODUCTION}`, +GitHub secrets: `AIGNOSTICS_CLIENT_ID_DEVICE_{STAGING,PRODUCTION}`, `AIGNOSTICS_REFRESH_TOKEN_{STAGING,PRODUCTION}`, the BetterStack URLs above, `CODECOV_TOKEN`, `SONAR_TOKEN`, `SENTRY_DSN`/`SENTRY_AUTH_TOKEN`, `UV_PUBLISH_TOKEN`, `KETRYX_PROJECT`/`KETRYX_API_KEY`, `SLACK_*_RELEASE_ANNOUNCEMENT`. diff --git a/.github/workflows/_claude-code.yml b/.github/workflows/_claude-code.yml index 4cc90ae41..427abf585 100644 --- a/.github/workflows/_claude-code.yml +++ b/.github/workflows/_claude-code.yml @@ -31,9 +31,6 @@ on: required: false default: false type: boolean - secrets: - ANTHROPIC_API_KEY: - required: true jobs: claude-code: @@ -41,7 +38,7 @@ jobs: permissions: actions: read # github_ci MCP server of claude-code-action reads workflow runs and job logs contents: read - id-token: write # anthropics/claude-code-action exchanges the OIDC token for the Claude App token + id-token: write # anthropics/claude-code-action exchanges the OIDC token for the Claude App token and the Anthropic API token pull-requests: read # github_ci MCP server of claude-code-action reads the pull request (pulls.get) steps: - name: Checkout repository @@ -78,7 +75,15 @@ jobs: if: inputs.mode == 'interactive' uses: anthropics/claude-code-action@db77d4d3c98f3f6380e7cd45ffba2342a3dbde3c # v1.0.254 with: - anthropic_api_key: ${{ secrets.ANTHROPIC_API_KEY }} + # Workload identity federation: the job's GitHub OIDC token is exchanged + # for a short-lived Anthropic token. Values are repository-level Actions + # variables (identifiers, not secrets): a public repository cannot see the + # org-level ones. Do not also set anthropic_api_key: a static key takes + # precedence and federation is silently skipped. + anthropic_federation_rule_id: ${{ vars.ANTHROPIC_FEDERATION_RULE_ID }} + anthropic_organization_id: ${{ vars.ANTHROPIC_ORGANIZATION_ID }} + anthropic_service_account_id: ${{ vars.ANTHROPIC_SERVICE_ACCOUNT_ID }} + anthropic_workspace_id: ${{ vars.ANTHROPIC_WORKSPACE_ID }} track_progress: ${{ inputs.track_progress }} additional_permissions: | actions: read @@ -93,7 +98,15 @@ jobs: if: inputs.mode == 'automation' uses: anthropics/claude-code-action@db77d4d3c98f3f6380e7cd45ffba2342a3dbde3c # v1.0.254 with: - anthropic_api_key: ${{ secrets.ANTHROPIC_API_KEY }} + # Workload identity federation: the job's GitHub OIDC token is exchanged + # for a short-lived Anthropic token. Values are repository-level Actions + # variables (identifiers, not secrets): a public repository cannot see the + # org-level ones. Do not also set anthropic_api_key: a static key takes + # precedence and federation is silently skipped. + anthropic_federation_rule_id: ${{ vars.ANTHROPIC_FEDERATION_RULE_ID }} + anthropic_organization_id: ${{ vars.ANTHROPIC_ORGANIZATION_ID }} + anthropic_service_account_id: ${{ vars.ANTHROPIC_SERVICE_ACCOUNT_ID }} + anthropic_workspace_id: ${{ vars.ANTHROPIC_WORKSPACE_ID }} track_progress: ${{ inputs.track_progress }} use_sticky_comment: ${{ inputs.use_sticky_comment }} additional_permissions: | diff --git a/.github/workflows/claude-code-automation-operational-excellence-weekly.yml b/.github/workflows/claude-code-automation-operational-excellence-weekly.yml index 343f058c7..41fcda8a7 100644 --- a/.github/workflows/claude-code-automation-operational-excellence-weekly.yml +++ b/.github/workflows/claude-code-automation-operational-excellence-weekly.yml @@ -16,7 +16,7 @@ jobs: permissions: actions: read # github_ci MCP server of claude-code-action in _claude-code.yml reads workflow runs and job logs contents: read - id-token: write # anthropics/claude-code-action in _claude-code.yml exchanges the OIDC token for the Claude App token + id-token: write # anthropics/claude-code-action in _claude-code.yml exchanges the OIDC token for the Claude App token and the Anthropic API token pull-requests: read # github_ci MCP server of claude-code-action in _claude-code.yml reads the pull request (pulls.get) with: mode: 'automation' @@ -291,6 +291,3 @@ jobs: ## Meta Suggest improvements to this workflow! You're continuously learning what works. - - secrets: - ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }} diff --git a/.github/workflows/claude-code-automation-pr-review.yml b/.github/workflows/claude-code-automation-pr-review.yml index 05b428db4..58581ce13 100644 --- a/.github/workflows/claude-code-automation-pr-review.yml +++ b/.github/workflows/claude-code-automation-pr-review.yml @@ -19,7 +19,7 @@ jobs: permissions: actions: read # github_ci MCP server of claude-code-action in _claude-code.yml reads workflow runs and job logs contents: read - id-token: write # anthropics/claude-code-action in _claude-code.yml exchanges the OIDC token for the Claude App token + id-token: write # anthropics/claude-code-action in _claude-code.yml exchanges the OIDC token for the Claude App token and the Anthropic API token pull-requests: read # github_ci MCP server of claude-code-action in _claude-code.yml reads the pull request (pulls.get) with: mode: 'automation' @@ -266,5 +266,3 @@ jobs: --- **Remember**: This is medical device software. Insist on highest standards. Be thorough, actionable, and kind. - secrets: - ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }} diff --git a/.github/workflows/claude-code-interactive.yml b/.github/workflows/claude-code-interactive.yml index f1ab3078b..c061b5f36 100644 --- a/.github/workflows/claude-code-interactive.yml +++ b/.github/workflows/claude-code-interactive.yml @@ -26,10 +26,8 @@ jobs: permissions: actions: read # github_ci MCP server of claude-code-action in _claude-code.yml reads workflow runs and job logs contents: read - id-token: write # anthropics/claude-code-action in _claude-code.yml exchanges the OIDC token for the Claude App token + id-token: write # anthropics/claude-code-action in _claude-code.yml exchanges the OIDC token for the Claude App token and the Anthropic API token pull-requests: read # github_ci MCP server of claude-code-action in _claude-code.yml reads the pull request (pulls.get) with: mode: 'interactive' track_progress: ${{ github.event_name != 'workflow_dispatch' && true || false }} - secrets: - ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }} From 2bc3c380b42ce6bc55b55ec0e18bc8674c986456 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fabian=20Spie=C3=9F?= Date: Fri, 13 Nov 2026 16:00:39 +0100 Subject: [PATCH 2/2] ci(OP-3891): drop the WIF comment block Co-Authored-By: Claude Opus 5.5 --- .github/workflows/_claude-code.yml | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/.github/workflows/_claude-code.yml b/.github/workflows/_claude-code.yml index 427abf585..1ac730e77 100644 --- a/.github/workflows/_claude-code.yml +++ b/.github/workflows/_claude-code.yml @@ -75,11 +75,6 @@ jobs: if: inputs.mode == 'interactive' uses: anthropics/claude-code-action@db77d4d3c98f3f6380e7cd45ffba2342a3dbde3c # v1.0.254 with: - # Workload identity federation: the job's GitHub OIDC token is exchanged - # for a short-lived Anthropic token. Values are repository-level Actions - # variables (identifiers, not secrets): a public repository cannot see the - # org-level ones. Do not also set anthropic_api_key: a static key takes - # precedence and federation is silently skipped. anthropic_federation_rule_id: ${{ vars.ANTHROPIC_FEDERATION_RULE_ID }} anthropic_organization_id: ${{ vars.ANTHROPIC_ORGANIZATION_ID }} anthropic_service_account_id: ${{ vars.ANTHROPIC_SERVICE_ACCOUNT_ID }} @@ -98,11 +93,6 @@ jobs: if: inputs.mode == 'automation' uses: anthropics/claude-code-action@db77d4d3c98f3f6380e7cd45ffba2342a3dbde3c # v1.0.254 with: - # Workload identity federation: the job's GitHub OIDC token is exchanged - # for a short-lived Anthropic token. Values are repository-level Actions - # variables (identifiers, not secrets): a public repository cannot see the - # org-level ones. Do not also set anthropic_api_key: a static key takes - # precedence and federation is silently skipped. anthropic_federation_rule_id: ${{ vars.ANTHROPIC_FEDERATION_RULE_ID }} anthropic_organization_id: ${{ vars.ANTHROPIC_ORGANIZATION_ID }} anthropic_service_account_id: ${{ vars.ANTHROPIC_SERVICE_ACCOUNT_ID }}