From d92f49bd367dde8b507cab63ee03e207e76b2225 Mon Sep 17 00:00:00 2001 From: Matt Andreko Date: Mon, 6 Oct 2025 13:00:16 -0400 Subject: [PATCH 1/7] Fix variable naming from recent update (#452) --- .github/workflows/_sonar.yml | 48 ++++++++++++++++++------------------ 1 file changed, 24 insertions(+), 24 deletions(-) diff --git a/.github/workflows/_sonar.yml b/.github/workflows/_sonar.yml index 8ad7c7c1..8baccb25 100644 --- a/.github/workflows/_sonar.yml +++ b/.github/workflows/_sonar.yml @@ -103,30 +103,30 @@ jobs: run: | set -euo pipefail ARGS=() - if [ -n "$_PULL_REQUEST_KEY" ]; then - ARGS+=("/d:sonar.pullrequest.key=$_PULL_REQUEST_KEY") + if [ -n "${_PULL_REQUEST_KEY}" ]; then + ARGS+=("/d:sonar.pullrequest.key=${_PULL_REQUEST_KEY}") fi - if [ -n "$_SONAR_TEST_INCLUSIONS" ]; then - ARGS+=("/d:sonar.test.inclusions=$_SONAR_TEST_INCLUSIONS") + if [ -n "${_SONAR_TEST_INCLUSIONS}" ]; then + ARGS+=("/d:sonar.test.inclusions=${_SONAR_TEST_INCLUSIONS}") fi - if [ -n "$_SONAR_EXCLUSIONS" ]; then - ARGS+=("/d:sonar.exclusions=$_SONAR_EXCLUSIONS") + if [ -n "${_SONAR_EXCLUSIONS}" ]; then + ARGS+=("/d:sonar.exclusions=${_SONAR_EXCLUSIONS}") fi - if [ -n "$_SONAR_SOURCES" ]; then - ARGS+=("-Dsonar.sources=$_SONAR_SOURCES") + if [ -n "${_SONAR_SOURCES}" ]; then + ARGS+=("-Dsonar.sources=${_SONAR_SOURCES}") fi - if [ -n "$_SONAR_TESTS" ]; then - ARGS+=("-Dsonar.tests=$_SONAR_TESTS") + if [ -n "${_SONAR_TESTS}" ]; then + ARGS+=("-Dsonar.tests=${_SONAR_TESTS}") fi dotnet-sonarscanner begin \ - /k:"${REPOSITORY_OWNER}_${REPOSITORY_NAME}" \ - /o:"$REPOSITORY_OWNER" \ - /d:sonar.token="$SONAR_TOKEN" \ + /k:"${_REPOSITORY_OWNER}_${_REPOSITORY_NAME}" \ + /o:"${_REPOSITORY_OWNER}" \ + /d:sonar.token="${_SONAR_TOKEN}" \ /d:sonar.host.url="https://sonarcloud.io" \ "${ARGS[@]}" dotnet build - dotnet-sonarscanner end /d:sonar.token="$_SONAR_TOKEN" + dotnet-sonarscanner end /d:sonar.token="${_SONAR_TOKEN}" - name: Scan with Sonar if: inputs.sonar-config == 'maven' @@ -141,20 +141,20 @@ jobs: set -euo pipefail ARGS=() - if [ -n "$_SONAR_TEST_INCLUSIONS" ]; then - ARGS+=("-Dsonar.test.inclusions=$_SONAR_TEST_INCLUSIONS") + if [ -n "${_SONAR_TEST_INCLUSIONS}" ]; then + ARGS+=("-Dsonar.test.inclusions=${_SONAR_TEST_INCLUSIONS}") fi - if [ -n "$_SONAR_EXCLUSIONS" ]; then - ARGS+=("-Dsonar.exclusions=$_SONAR_EXCLUSIONS") + if [ -n "${_SONAR_EXCLUSIONS}" ]; then + ARGS+=("-Dsonar.exclusions=${_SONAR_EXCLUSIONS}") fi - if [ -n "$_SONAR_SOURCES" ]; then - ARGS+=("-Dsonar.sources=$_SONAR_SOURCES") + if [ -n "${_SONAR_SOURCES}" ]; then + ARGS+=("-Dsonar.sources=${_SONAR_SOURCES}") fi - if [ -n "$_SONAR_TESTS" ]; then - ARGS+=("-Dsonar.tests=$_SONAR_TESTS") + if [ -n "${_SONAR_TESTS}" ]; then + ARGS+=("-Dsonar.tests=${_SONAR_TESTS}") fi - if [ -n "$_PULL_REQUEST_KEY" ]; then - ARGS+=("-Dsonar.pullrequest.key=$_PULL_REQUEST_KEY") + if [ -n "${_PULL_REQUEST_KEY}" ]; then + ARGS+=("-Dsonar.pullrequest.key=${_PULL_REQUEST_KEY}") fi mvn clean install -Dgpg.skip=true sonar:sonar "${ARGS[@]}" From 7df2c173606149db6e8d27bb1893c35cdd3e90c9 Mon Sep 17 00:00:00 2001 From: Matt Andreko Date: Tue, 7 Oct 2025 14:54:01 -0400 Subject: [PATCH 2/7] Add test inputs to Sonar tests (#453) --- .github/workflows/_sonar.yml | 2 +- .github/workflows/test-sonar.yml | 15 +++++++++++++++ 2 files changed, 16 insertions(+), 1 deletion(-) diff --git a/.github/workflows/_sonar.yml b/.github/workflows/_sonar.yml index 8baccb25..3bba6590 100644 --- a/.github/workflows/_sonar.yml +++ b/.github/workflows/_sonar.yml @@ -68,7 +68,7 @@ jobs: args: > "-Dsonar.organization=${{ github.repository_owner }}" "-Dsonar.projectKey=${{ github.repository_owner }}_${{ github.event.repository.name }}" - "-Dsonar.pullrequest.key=${{ github.event.pull_request.number }}" + ${{ contains(github.event_name, 'pull_request') && format('"-Dsonar.pullrequest.key={0}"', github.event.pull_request.number) || '' }} ${{ inputs.sonar-test-inclusions != '' && format('"-Dsonar.test.inclusions={0}"', inputs.sonar-test-inclusions) || '' }} ${{ inputs.sonar-exclusions != '' && format('"-Dsonar.exclusions={0}"', inputs.sonar-exclusions) || '' }} ${{ inputs.sonar-sources != '' && format('"-Dsonar.sources={0}"', inputs.sonar-sources) || '' }} diff --git a/.github/workflows/test-sonar.yml b/.github/workflows/test-sonar.yml index dd551957..16467097 100644 --- a/.github/workflows/test-sonar.yml +++ b/.github/workflows/test-sonar.yml @@ -10,6 +10,17 @@ on: branches: - "main" workflow_dispatch: + inputs: + sonar-config: + description: "Configuration for Sonar" + type: string + default: "default" + sonar-test-inclusions: + description: "Glob pattern(s) for test files to include in Sonar analysis" + type: string + sonar-exclusions: + description: "Glob pattern(s) for files to exclude from Sonar analysis" + type: string permissions: {} @@ -25,3 +36,7 @@ jobs: contents: read pull-requests: write id-token: write + with: + sonar-config: ${{ github.event.inputs.sonar-config }} + sonar-test-inclusions: ${{ github.event.inputs.sonar-test-inclusions }} + sonar-exclusions: ${{ github.event.inputs.sonar-exclusions }} From 3220eb4b166c863d4e8c573a9c7c0cc4ca91ea03 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Mon, 13 Oct 2025 11:45:12 -0400 Subject: [PATCH 3/7] [deps] example-references: Update minor (#454) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- .../workflow-templates/example-references/_build.yml | 6 +++--- .../workflow-templates/example-references/_docker.yml | 2 +- .../workflow-templates/example-references/_test.yml | 4 ++-- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/.github/templates/workflow-templates/example-references/_build.yml b/.github/templates/workflow-templates/example-references/_build.yml index d82b8214..835aa62b 100644 --- a/.github/templates/workflow-templates/example-references/_build.yml +++ b/.github/templates/workflow-templates/example-references/_build.yml @@ -25,10 +25,10 @@ jobs: fetch-depth: 0 - name: Set up .NET - uses: actions/setup-dotnet@3e891b0cb619bf60e2c25674b222b8940e2c1c25 # v4.1.0 + uses: actions/setup-dotnet@67a3573c9a986a3f9c594539f4ab511d57bb3ce9 # v4.3.1 - name: Cache NuGet packages - uses: actions/cache@1bd1e32a3bdc45362d1e726936510720a7c30a57 # v4.2.0 + uses: actions/cache@0057852bfaa89a56745cba8c7296529d2fc39830 # v4.3.0 with: path: ~/.nuget/packages key: ${{ runner.os }}-nuget-${{ hashFiles('**/packages.lock.json') }} @@ -57,7 +57,7 @@ jobs: ls -atlh ../../ - name: Upload artifact - uses: actions/upload-artifact@b4b15b8c7c6ac21ea08fcf65892d2ee8f75cf882 # v4.4.3 + uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2 with: name: ${{ inputs.project-name }}.zip path: ./${{ inputs.project-name }}.zip diff --git a/.github/templates/workflow-templates/example-references/_docker.yml b/.github/templates/workflow-templates/example-references/_docker.yml index 34a20a49..2954bb62 100644 --- a/.github/templates/workflow-templates/example-references/_docker.yml +++ b/.github/templates/workflow-templates/example-references/_docker.yml @@ -72,7 +72,7 @@ jobs: -d ${{ inputs.project-path }}/obj/build-output/publish - name: Build Docker image - uses: docker/build-push-action@48aba3b46d1b1fec4febb7c5d0c644b249a11355 # v6.10.0 + uses: docker/build-push-action@263435318d21b8e681c14492fe198d362a7d2c83 # v6.18.0 with: context: ${{ inputs.project-path }} file: ${{ inputs.project-path }}/Dockerfile diff --git a/.github/templates/workflow-templates/example-references/_test.yml b/.github/templates/workflow-templates/example-references/_test.yml index 18a37564..b8744226 100644 --- a/.github/templates/workflow-templates/example-references/_test.yml +++ b/.github/templates/workflow-templates/example-references/_test.yml @@ -27,10 +27,10 @@ jobs: fetch-depth: 0 - name: Set up .NET - uses: actions/setup-dotnet@3e891b0cb619bf60e2c25674b222b8940e2c1c25 # v4.1.0 + uses: actions/setup-dotnet@67a3573c9a986a3f9c594539f4ab511d57bb3ce9 # v4.3.1 - name: Cache NuGet packages - uses: actions/cache@1bd1e32a3bdc45362d1e726936510720a7c30a57 # v4.2.0 + uses: actions/cache@0057852bfaa89a56745cba8c7296529d2fc39830 # v4.3.0 with: path: ~/.nuget/packages key: ${{ runner.os }}-nuget-${{ hashFiles('**/packages.lock.json') }} From 6732eec467c6767dfd13a23679034a215276ae81 Mon Sep 17 00:00:00 2001 From: Amy Galles <9685081+AmyLGalles@users.noreply.github.com> Date: Tue, 14 Oct 2025 14:33:34 -0700 Subject: [PATCH 4/7] [BRE-1194] add step to disable publish github release (#446) * add step to disable publish github release * remove abandoned idea * refine dry run * fix errors with slack notification * expand scope of dry run defnition * adding default dry run * fix dry-run typo * fix tag typo * Update .github/workflows/_publish-mobile-github-release.yml Co-authored-by: Andy Pixley <3723676+pixman20@users.noreply.github.com> * Update .github/workflows/_publish-mobile-github-release.yml Co-authored-by: Andy Pixley <3723676+pixman20@users.noreply.github.com> * remove user input from run blocks * add underscore to global variable * remove default dry run * don't tag authenticator as latest * Apply suggestions from code review Co-authored-by: Andy Pixley <3723676+pixman20@users.noreply.github.com> * move criteria for successful run * remove unused id * fix workflow name variable * fix workflow name variable * fix workflow name variable * remove testing option" * Apply suggestions from code review Co-authored-by: Vince Grassia <593223+vgrassia@users.noreply.github.com> * remove unused github output --------- Co-authored-by: Andy Pixley <3723676+pixman20@users.noreply.github.com> Co-authored-by: Vince Grassia <593223+vgrassia@users.noreply.github.com> --- .../_publish-mobile-github-release.yml | 43 ++++++++++++++++--- 1 file changed, 36 insertions(+), 7 deletions(-) diff --git a/.github/workflows/_publish-mobile-github-release.yml b/.github/workflows/_publish-mobile-github-release.yml index 9b6f7afe..991930e6 100644 --- a/.github/workflows/_publish-mobile-github-release.yml +++ b/.github/workflows/_publish-mobile-github-release.yml @@ -26,8 +26,11 @@ on: description: 'Type of the project (e.g. "android" or "ios")' type: string required: true - - + dry_run: + type: boolean + description: 'Run the workflow in dry-run mode without making any changes' + required: false + default: false jobs: publish-release: @@ -36,7 +39,10 @@ jobs: permissions: contents: write id-token: write - actions: read + actions: write + env: + _DRY_RUN: ${{ inputs.dry_run }} + _WORKFLOW_NAME: ${{ inputs.workflow_name }} steps: - name: Check out repository @@ -61,6 +67,10 @@ jobs: if [ "$is_latest_draft" != "true" ]; then echo "No draft found" + if [ "$_DRY_RUN" != "true" ]; then + echo "Disabling workflow to prevent further runs" + gh workflow disable "$_WORKFLOW_NAME" + fi exit 0 fi @@ -84,9 +94,8 @@ jobs: id: get_previous_run env: GH_TOKEN: ${{ github.token }} - WORKFLOW_NAME: ${{ inputs.workflow_name }} run: | - previous_run_id=$(gh run list --workflow=$WORKFLOW_NAME --status=success --limit 1 --json databaseId --jq '.[0].databaseId // empty') + previous_run_id=$(gh run list --workflow=$_WORKFLOW_NAME --status=success --limit 1 --json databaseId --jq '.[0].databaseId // empty') if [ -n "$previous_run_id" ] && [ "$previous_run_id" != "null" ]; then echo "Found previous successful scheduled run: $previous_run_id" @@ -243,11 +252,31 @@ jobs: fi - name: Make GitHub release latest and non-pre-release - if: steps.check_version.outputs.version_released == 'true' + id: make_release_latest + if: steps.check_version.outputs.version_released == 'true' && inputs.dry_run != true env: TAG: ${{ steps.get_latest_draft.outputs.latest_draft_tag }} GH_TOKEN: ${{ github.token }} - run: gh release edit $TAG --prerelease=false --latest --draft=false + PRODUCT: ${{ inputs.release_name }} + run: | + if [ "$PRODUCT" = "Password Manager" ]; then + gh release edit $TAG --prerelease=false --latest --draft=false + else + gh release edit $TAG --prerelease=false --draft=false + fi + + - name: Disable workflow if published + if: ${{ steps.make_release_latest.conclusion == 'success' }} + env: + GH_TOKEN: ${{ github.token }} + run: | + if [ "$_DRY_RUN" = "true" ]; then + echo "Dry run mode - skipping gh workflow disable command" + gh workflow list "$_WORKFLOW_NAME" + else + echo "Disabling workflow to prevent further runs" + gh workflow disable "$_WORKFLOW_NAME" + fi - name: Create workflow state artifact run: | From 4b8449ea9d9fa493450e876ba252111e5e8f6e7f Mon Sep 17 00:00:00 2001 From: Mick Letofsky Date: Fri, 17 Oct 2025 15:46:18 +0200 Subject: [PATCH 5/7] [PM-26935] Create a workflow for Claude code reviews (#456) * Create an org workflow for Claude code reviews --- .../CLAUDE.md | 33 +++- .claude/README.md | 62 +++++++ .claude/commands/review-pr.md | 78 +++++++++ .claude/prompts/review-code.md | 22 +++ .github/workflows/review-code.yml | 155 ++++++++++++++++++ 5 files changed, 348 insertions(+), 2 deletions(-) rename .github/copilot-instructions.md => .claude/CLAUDE.md (98%) create mode 100644 .claude/README.md create mode 100644 .claude/commands/review-pr.md create mode 100644 .claude/prompts/review-code.md create mode 100644 .github/workflows/review-code.yml diff --git a/.github/copilot-instructions.md b/.claude/CLAUDE.md similarity index 98% rename from .github/copilot-instructions.md rename to .claude/CLAUDE.md index 08f8ab05..8efb8698 100644 --- a/.github/copilot-instructions.md +++ b/.claude/CLAUDE.md @@ -1,10 +1,11 @@ -# GitHub Copilot Instructions for Bitwarden GitHub Actions +# Bitwarden GitHub Actions - Claude Code Configuration ## Repository Overview This repository contains a collection of custom GitHub Actions used by Bitwarden to simplify and standardize CI/CD pipelines across their projects. The repository follows a modular structure where each action is self-contained in its own directory with its own `action.yml` file. **Repository Details:** + - **Type**: GitHub Actions collection - **Size**: Medium-sized repository with ~20 custom actions - **Languages**: TypeScript, Python, JavaScript, Shell scripts, YAML @@ -13,14 +14,17 @@ This repository contains a collection of custom GitHub Actions used by Bitwarden ## Build and Validation Instructions ### Prerequisites + - Node.js (version 22+ for TypeScript actions) - Python 3.13+ (for Python-based actions) - Docker (for containerized actions) ### Initial Setup + 1. **Always run `npm install` first** in the repository root to install development dependencies including Prettier, Husky, and lint-staged. ### Code Formatting and Linting + - **Code formatting**: This repository uses Prettier for all file types - **Pre-commit hooks**: Husky is configured to run lint-staged on commit - **Command**: `npx prettier --cache --write --ignore-unknown .` (formats all files) @@ -51,9 +55,11 @@ Refer to the [rules directory](https://github.com/bitwarden/workflow-linter/tree **Python requirement**: Python 3.13+ with `pip install bitwarden_workflow_linter` ### Testing Individual Actions + Each action has its own test workflow in `.github/workflows/test-*.yml`: + - `test-version-bump.yml` - Tests version bumping functionality -- `test-get-secrets.yml` - Tests Azure Key Vault integration +- `test-get-secrets.yml` - Tests Azure Key Vault integration - `test-download-artifacts.yml` - Tests artifact downloading - `test-release-version-check.yml` - Tests release version validation - And others... @@ -61,7 +67,9 @@ Each action has its own test workflow in `.github/workflows/test-*.yml`: **Test execution**: Tests run automatically on PRs affecting specific action directories. ### TypeScript Actions Build Process + For TypeScript-based actions (e.g., `get-keyvault-secrets`): + 1. Source files are in `src/` directory 2. **Always run `npm run build`** or `tsc` to compile TypeScript to JavaScript 3. Compiled output goes to `lib/` directory @@ -69,7 +77,9 @@ For TypeScript-based actions (e.g., `get-keyvault-secrets`): 5. **Critical**: Always commit both source AND compiled files ### Docker-based Actions + For containerized actions (e.g., `version-bump`, `get-checksum`): + - Use `Dockerfile` in action directory - Python scripts use `main.py` as entry point - **No local build required** - Docker builds during action execution @@ -77,6 +87,7 @@ For containerized actions (e.g., `version-bump`, `get-checksum`): ## Project Layout and Architecture ### Repository Structure + ``` / ├── package.json # Root dependencies (Prettier, Husky, lint-staged) @@ -101,15 +112,18 @@ For containerized actions (e.g., `version-bump`, `get-checksum`): ### Key Actions by Type **TypeScript/Node.js Actions:** + - `get-keyvault-secrets/` - Azure Key Vault integration - `download-artifacts/` - Artifact management **Python/Docker Actions:** + - `version-bump/` - File version updating (JSON, XML, PLIST, YAML) - `get-checksum/` - SHA256 checksum generation - `crowdin/` - Translation management **Shell/YAML Actions:** + - `azure-login/`, `azure-logout/` - Azure authentication - `setup-docker-trust/` - Docker configuration - Various reporting and utility actions @@ -117,15 +131,18 @@ For containerized actions (e.g., `version-bump`, `get-checksum`): ### CI/CD Validation Pipeline **Pre-commit Checks:** + 1. Prettier formatting (automatic via Husky) 2. Lint-staged validation **Pull Request Checks:** + 1. Workflow linting (if `.github/workflows/` changed) 2. Action-specific tests (if action directory changed) 3. Various security and quality scans **Critical Validation Steps:** + - **Workflow files**: Must pass `bitwarden_workflow_linter` validation - **TypeScript actions**: Must have compiled `lib/` output committed - **All files**: Must pass Prettier formatting @@ -134,32 +151,38 @@ For containerized actions (e.g., `version-bump`, `get-checksum`): ### Dependencies and Requirements **Development Dependencies (root):** + - `prettier` - Code formatting - `husky` - Git hooks - `lint-staged` - Staged file processing **Action-specific Dependencies:** + - TypeScript actions: `@actions/core`, `@actions/exec`, type definitions - Python actions: Standard library primarily, some use external packages ### File Patterns and Conventions **Action Definition Files:** + - Every action directory MUST have an `action.yml` file - Branding should include `icon` and `color` properties - Input/output definitions follow GitHub Actions schema **TypeScript Actions:** + - Source in `src/`, compiled output in `lib/` - Use `@actions/core` for GitHub Actions integration - `tsconfig.json` for TypeScript configuration **Python Actions:** + - Main script typically named `main.py` - Use environment variables for input (`os.getenv("INPUT_*")`) - Docker-based execution via `Dockerfile` **Testing:** + - Test workflows named `test-[action-name].yml` - Test fixtures in `tests/fixtures/` subdirectories - Tests validate action outputs and side effects @@ -169,6 +192,7 @@ For containerized actions (e.g., `version-bump`, `get-checksum`): All code changes and action development must follow security best practices relevant to GitHub Actions and Bitwarden's standards: **GitHub Actions Security:** + - **No hard-coded secrets or credentials** - Use secure parameter passing - **Validate all action inputs** - Sanitize and validate user-provided inputs to prevent injection attacks - **Use pinned action versions** - All external actions must be pinned to specific commit hashes (enforced by workflow linter) @@ -176,16 +200,19 @@ All code changes and action development must follow security best practices rele - **Secure output handling** - Avoid exposing sensitive data in action outputs or logs **Secret and Credential Management:** + - Use Azure Key Vault integration properly via `get-keyvault-secrets` action - Never log or expose secret values in action outputs - Use GitHub's secret masking capabilities (`core.setSecret()` in TypeScript actions) **Supply Chain Security:** + - Only use approved actions listed in the workflow linter's approved actions list - Pin all dependencies to specific versions in `package.json` and `requirements.txt` - Validate Docker base images and use official, minimal images when possible **Input Validation:** + - Validate file paths to prevent directory traversal attacks - Sanitize version strings and other user inputs - Use proper escaping when constructing shell commands @@ -195,6 +222,7 @@ All code changes and action development must follow security best practices rele **Trust these instructions** and only perform additional searching if the information provided is incomplete or found to be incorrect. The repository follows consistent patterns, and the validation processes are well-established. **When making changes:** + 1. Always format code with Prettier before committing 2. For TypeScript actions, always compile and commit the `lib/` output 3. Test changes using the existing test workflows when possible @@ -203,6 +231,7 @@ All code changes and action development must follow security best practices rele 6. Apply security best practices and validate all inputs **Common pitfalls to avoid:** + - Forgetting to compile TypeScript actions - Not running Prettier formatting - Missing required properties in `action.yml` files diff --git a/.claude/README.md b/.claude/README.md new file mode 100644 index 00000000..76f6f95c --- /dev/null +++ b/.claude/README.md @@ -0,0 +1,62 @@ +# Claude Code Configuration + +This directory contains Claude Code configuration files for the gh-actions repository. + +## Directory Structure + +``` +.claude/ +├── CLAUDE.md # General project context and guidelines +├── commands/ # Custom slash commands +│ └── review-pr.md # /review-pr command for PR reviews +└── prompts/ # Workflow-specific prompts + └── review-code.md # Used by review-code.yml workflow +``` + +## Custom Commands + +### `/review-pr` - Pull Request Review + +Triggers a comprehensive PR code review in your current Claude Code session. + +**Usage:** + +1. Open Claude Code in this repository +2. Check out the PR branch you want to review +3. Tag @claude and type `/review-pr` + +**What it does:** + +- Analyzes code quality and best practices +- Checks for security vulnerabilities +- Validates workflow linter compliance +- Reviews performance and efficiency +- Provides structured feedback with action items + +**Example:** + +``` +@claude /review-pr +``` + +## Automated Workflow Reviews + +The `review-code.yml` workflow uses the `.claude/prompts/review-code.md` to automatically review PRs via GitHub Actions in each Bitwarden repo. The `review-code.md` is used as a gate to execute the `review-code.yml` workflow. Repos without this file will not see Claude code reviews performed on each pull request. + +**How it works:** + +1. Workflow triggers on non-draft PRs +2. Reads `.claude/prompts/review-code.md` from the PR's branch +3. Posts review as a sticky comment +4. Updates comment on new commits + +**To enable in our repos:** + +1. Create `.claude/prompts/review-code.md` with review criteria +2. Workflow runs automatically on subsequent pull requests + +## Best Practices + +- **Commands** (`.claude/commands/`): For interactive Claude Code sessions +- **Prompts** (`.claude/prompts/`): For automated GitHub Actions workflows +- **CLAUDE.md**: General project context available in all Claude interactions diff --git a/.claude/commands/review-pr.md b/.claude/commands/review-pr.md new file mode 100644 index 00000000..2571dadb --- /dev/null +++ b/.claude/commands/review-pr.md @@ -0,0 +1,78 @@ +--- +description: Review the current pull request with comprehensive code analysis +--- + +You are conducting a thorough pull request code review for the Bitwarden gh-actions repository. + +## Current Context +- Repository: bitwarden/gh-actions +- This is a collection of reusable GitHub Actions workflows and custom actions +- The code must follow Bitwarden's workflow linter rules +- Security and reliability are paramount + +## Review Instructions + +Perform a comprehensive review of the current PR with focus on: + +### 1. **Code Quality & Best Practices** +- Adherence to GitHub Actions best practices +- Proper error handling and validation +- Code maintainability and clarity +- Appropriate use of GitHub Actions syntax + +### 2. **Security Implications** +- No hardcoded secrets or credentials +- Proper permission scoping +- Input validation and sanitization +- Protection against command injection +- Safe handling of user-provided data + +### 3. **Workflow Linter Compliance** +Verify compliance with Bitwarden workflow linter rules: +- Actions pinned to commit SHA with version comment +- Permissions explicitly defined +- Runner versions pinned (e.g., ubuntu-24.04) +- Proper naming conventions (capitalized) +- Only approved actions are used + +### 4. **Performance & Efficiency** +- Appropriate caching strategies +- Parallel job execution where possible +- Minimal redundant operations +- Efficient use of GitHub Actions resources + +### 5. **Testing & Validation** +- Adequate test coverage for new features +- Test workflows follow established patterns +- Integration with existing test infrastructure + +## Output Format + +Provide a structured review with: + +1. **Summary of Changes** + - High-level overview of what this PR accomplishes + - Key files modified and their impact + +2. **Critical Issues** (if any) + - Security vulnerabilities + - Breaking changes + - Non-compliant code that must be fixed + +3. **Suggested Improvements** + - Optimization opportunities + - Better patterns or approaches + - Documentation enhancements + +4. **Good Practices Observed** + - Notable positive aspects (be concise) + - Correct security implementations + - Well-structured code + +5. **Action Items** + - Specific tasks for the author + - Priority level (Critical/High/Medium/Low) + +Use collapsible `
` sections for lengthy explanations to keep the review readable. + +**Important**: Focus on being thorough about issues and improvements. For good practices, be brief and just note what was done well. diff --git a/.claude/prompts/review-code.md b/.claude/prompts/review-code.md new file mode 100644 index 00000000..72905852 --- /dev/null +++ b/.claude/prompts/review-code.md @@ -0,0 +1,22 @@ +Please review this pull request with a focus on: +- Code quality and best practices +- Potential bugs or issues +- Security implications +- Performance considerations + +Note: The PR branch is already checked out in the current working directory. + +Provide a comprehensive review including: +- Summary of changes since last review +- Critical issues found (be thorough) +- Suggested improvements (be thorough) +- Good practices observed (be concise - list only the most notable items without elaboration) +- Action items for the author +- Leverage collapsible
sections where appropriate for lengthy explanations or code snippets to enhance human readability + +When reviewing subsequent commits: +- Track status of previously identified issues (fixed/unfixed/reopened) +- Identify NEW problems introduced since last review +- Note if fixes introduced new issues + +IMPORTANT: Be comprehensive about issues and improvements. For good practices, be brief - just note what was done well without explaining why or praising excessively. diff --git a/.github/workflows/review-code.yml b/.github/workflows/review-code.yml new file mode 100644 index 00000000..8decdb56 --- /dev/null +++ b/.github/workflows/review-code.yml @@ -0,0 +1,155 @@ +name: Code Review + +on: + pull_request: + types: [opened, synchronize, reopened, ready_for_review] + +permissions: + contents: read + pull-requests: write + +jobs: + validation: + name: Validation + runs-on: ubuntu-24.04 + permissions: + contents: read + outputs: + should_review: ${{ steps.validate.outputs.should_review }} + + steps: + - name: Check PR requirements + id: check-pr + run: | + if [ "${{ github.event.pull_request.draft }}" == "true" ]; then + echo "⚠️ Validation: PR is a draft - skipping review" + echo "pr_valid=false" >> $GITHUB_OUTPUT + else + echo "✅ Validation: PR is ready for review" + echo "pr_valid=true" >> $GITHUB_OUTPUT + fi + + - name: Check for Azure credentials + id: check-azure-secret + env: + _AZURE_SUBSCRIPTION_ID: ${{ secrets.AZURE_SUBSCRIPTION_ID }} + _AZURE_TENANT_ID: ${{ secrets.AZURE_TENANT_ID }} + _AZURE_CLIENT_ID: ${{ secrets.AZURE_CLIENT_ID }} + run: | + if [ -n "$_AZURE_SUBSCRIPTION_ID" ] && [ -n "$_AZURE_TENANT_ID" ] && [ -n "$_AZURE_CLIENT_ID" ]; then + echo "credentials_valid=true" >> $GITHUB_OUTPUT + echo "✅ Validation: Azure credentials available" + else + echo "credentials_valid=false" >> $GITHUB_OUTPUT + echo "⚠️ Validation: Azure credentials not available" + echo "This is expected for external contributors or forks" + fi + + - name: Check if prompt file exists + id: check-prompt + env: + GH_TOKEN: ${{ github.token }} + run: | + # Use GitHub API to check file existence WITHOUT checkout + FILE_PATH=".claude/prompts/review-code.md" + REPO="${{ github.repository }}" + REF="${{ github.event.pull_request.head.sha }}" + + # Check if file exists via API + if gh api "repos/$REPO/contents/$FILE_PATH?ref=$REF" --silent 2>/dev/null; then + echo "prompt_exists=true" >> $GITHUB_OUTPUT + echo "✅ Found $FILE_PATH in $REPO" + else + echo "prompt_exists=false" >> $GITHUB_OUTPUT + echo "⚠️ Validation: No $FILE_PATH found - skipping Claude review" + fi + + - name: Set validation result + id: validate + run: | + if [ "${{ steps.check-pr.outputs.pr_valid }}" == "true" ] && \ + [ "${{ steps.check-azure-secret.outputs.credentials_valid }}" == "true" ] && \ + [ "${{ steps.check-prompt.outputs.prompt_exists }}" == "true" ]; then + echo "should_review=true" >> $GITHUB_OUTPUT + echo "✅ Validation passed - code review will proceed" + else + echo "should_review=false" >> $GITHUB_OUTPUT + echo "⚠️ Validation failed - code review will be skipped" + fi + + review: + name: Review + runs-on: ubuntu-24.04 + needs: validation + if: needs.validation.outputs.should_review == 'true' + permissions: + contents: read + id-token: write + pull-requests: write + + steps: + - name: Check out repo + uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 + with: + fetch-depth: 0 + persist-credentials: false + + - name: Log in to Azure + uses: bitwarden/gh-actions/azure-login@main + with: + subscription_id: ${{ secrets.AZURE_SUBSCRIPTION_ID }} + tenant_id: ${{ secrets.AZURE_TENANT_ID }} + client_id: ${{ secrets.AZURE_CLIENT_ID }} + + - name: Get Azure Key Vault secrets + id: get-kv-secrets + uses: bitwarden/gh-actions/get-keyvault-secrets@main + with: + keyvault: gh-org-bitwarden + secrets: "ANTHROPIC-API-KEY" + + - name: Log out from Azure + uses: bitwarden/gh-actions/azure-logout@main + + - name: Build review prompt + id: build-prompt + env: + PR_REPO: ${{ github.repository }} + PR_NUMBER: ${{ github.event.pull_request.number }} + PR_TITLE: ${{ github.event.pull_request.title }} + PR_BODY: ${{ github.event.pull_request.body }} + PR_AUTHOR: ${{ github.event.pull_request.user.login }} + PR_COMMIT: ${{ github.event.pull_request.head.sha }} + run: | + PROMPT_FILE=".claude/prompts/review-code.md" + + # Build the full prompt with GitHub context + repo's prompt + { + printf "REPO: %s\n" "$PR_REPO" + printf "PR NUMBER: %s\n" "$PR_NUMBER" + printf "TITLE: %s\n" "$PR_TITLE" + printf "BODY: %s\n" "$PR_BODY" + printf "AUTHOR: %s\n" "$PR_AUTHOR" + printf "COMMIT: %s\n" "$PR_COMMIT" + printf "\n" + printf "Note: The PR branch is already checked out in the current working directory.\n" + printf "\n---\n\n" + cat "$PROMPT_FILE" + } > /tmp/review-prompt.md + + # Output the prompt + { + echo 'FINAL_PROMPT<> "$GITHUB_OUTPUT" + + - name: Review with Claude Code + uses: anthropics/claude-code-action@e8bad572273ce919ba15fec95aef0ce974464753 # v1.0.13 + with: + anthropic_api_key: ${{ steps.get-kv-secrets.outputs.ANTHROPIC-API-KEY }} + track_progress: true + use_sticky_comment: true + prompt: ${{ steps.build-prompt.outputs.FINAL_PROMPT }} + claude_args: | + --allowedTools "mcp__github_comment__update_claude_comment,mcp__github_inline_comment__create_inline_comment,Bash(gh pr diff:*),Bash(gh pr view:*)" From be38f1037514712b8dfab6beaa679bf3a6caa439 Mon Sep 17 00:00:00 2001 From: Mick Letofsky Date: Fri, 17 Oct 2025 16:13:11 +0200 Subject: [PATCH 6/7] Create a Claude comment workflow (#457) * Create a Claude comment workflow to allow teammates to interact with Claude via comments and issues. --- .github/workflows/respond.yml | 119 ++++++++++++++++++++++++++++++++++ 1 file changed, 119 insertions(+) create mode 100644 .github/workflows/respond.yml diff --git a/.github/workflows/respond.yml b/.github/workflows/respond.yml new file mode 100644 index 00000000..8e4e8762 --- /dev/null +++ b/.github/workflows/respond.yml @@ -0,0 +1,119 @@ +name: Claude Review + +on: + issue_comment: + types: [created] + pull_request_review_comment: + types: [created] + issues: + types: [opened, assigned] + pull_request_review: + types: [submitted] + +jobs: + validation: + name: Validation + runs-on: ubuntu-24.04 + permissions: + contents: read + outputs: + should_comment: ${{ steps.validate.outputs.should_comment }} + + steps: + - name: Check GitHub event + id: check-github-event + env: + _EVENT_NAME: ${{ github.event_name }} + _COMMENT_BODY: ${{ github.event.comment.body }} + _REVIEW_BODY: ${{ github.event.review.body }} + _ISSUE_BODY: ${{ github.event.issue.body }} + run: | + # Check if @claude is mentioned in the event + MENTIONED=false + + if [ "$_EVENT_NAME" == "issue_comment" ] && echo "$_COMMENT_BODY" | grep -qF "@claude"; then + MENTIONED=true + elif [ "$_EVENT_NAME" == "pull_request_review_comment" ] && echo "$_COMMENT_BODY" | grep -qF "@claude"; then + MENTIONED=true + elif [ "$_EVENT_NAME" == "pull_request_review" ] && echo "$_REVIEW_BODY" | grep -qF "@claude"; then + MENTIONED=true + elif [ "$_EVENT_NAME" == "issues" ] && echo "$_ISSUE_BODY" | grep -qF "@claude"; then + MENTIONED=true + fi + + if [ "$MENTIONED" = "true" ]; then + echo "claude_mentioned=true" >> $GITHUB_OUTPUT + echo "✅ Validation: @claude mentioned in event" + else + echo "claude_mentioned=false" >> $GITHUB_OUTPUT + echo "⏭️ Validation: @claude not mentioned - skipping" + fi + + - name: Check for Azure credentials + id: check-azure-secret + env: + _AZURE_SUBSCRIPTION_ID: ${{ secrets.AZURE_SUBSCRIPTION_ID }} + _AZURE_TENANT_ID: ${{ secrets.AZURE_TENANT_ID }} + _AZURE_CLIENT_ID: ${{ secrets.AZURE_CLIENT_ID }} + run: | + if [ -n "$_AZURE_SUBSCRIPTION_ID" ] && [ -n "$_AZURE_TENANT_ID" ] && [ -n "$_AZURE_CLIENT_ID" ]; then + echo "credentials_valid=true" >> $GITHUB_OUTPUT + echo "✅ Validation: Azure credentials available" + else + echo "credentials_valid=false" >> $GITHUB_OUTPUT + echo "⚠️ Validation: Azure credentials not available" + echo "This is expected for external contributors or forks" + fi + + - name: Set validation result + id: validate + run: | + if [ "${{ steps.check-github-event.outputs.claude_mentioned }}" == "true" ] && \ + [ "${{ steps.check-azure-secret.outputs.credentials_valid }}" == "true" ]; then + echo "should_comment=true" >> $GITHUB_OUTPUT + echo "✅ Validation passed - comment will proceed" + else + echo "should_comment=false" >> $GITHUB_OUTPUT + echo "⚠️ Validation failed - comment will be skipped" + fi + + comment: + name: Claude comment + runs-on: ubuntu-24.04 + needs: validation + if: needs.validation.outputs.should_comment == 'true' + permissions: + contents: write + pull-requests: write + issues: write + id-token: write + actions: read + + steps: + - name: Check out repo + uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 + with: + fetch-depth: 1 + + - name: Log in to Azure + uses: bitwarden/gh-actions/azure-login@main + with: + subscription_id: ${{ secrets.AZURE_SUBSCRIPTION_ID }} + tenant_id: ${{ secrets.AZURE_TENANT_ID }} + client_id: ${{ secrets.AZURE_CLIENT_ID }} + + - name: Get Azure Key Vault secrets + id: get-kv-secrets + uses: bitwarden/gh-actions/get-keyvault-secrets@main + with: + keyvault: gh-org-bitwarden + secrets: "ANTHROPIC-API-KEY" + + - name: Log out from Azure + uses: bitwarden/gh-actions/azure-logout@main + + - name: Run Claude Code + id: claude + uses: anthropics/claude-code-action@e8bad572273ce919ba15fec95aef0ce974464753 # v1.0.13 + with: + anthropic_api_key: ${{ steps.get-kv-secrets.outputs.ANTHROPIC-API-KEY }} From dafe7e5106a5755ac5f5342ab7658a596534444d Mon Sep 17 00:00:00 2001 From: Mick Letofsky Date: Mon, 20 Oct 2025 22:28:54 +0200 Subject: [PATCH 7/7] [PM-26935] Craft a reusable workflow to consolidating Claude business logic into one place --- .github/workflows/_review-code.yml | 143 +++++++++++++++++++++++++++ .github/workflows/review-code.yml | 149 ++--------------------------- 2 files changed, 149 insertions(+), 143 deletions(-) create mode 100644 .github/workflows/_review-code.yml diff --git a/.github/workflows/_review-code.yml b/.github/workflows/_review-code.yml new file mode 100644 index 00000000..76ef42a7 --- /dev/null +++ b/.github/workflows/_review-code.yml @@ -0,0 +1,143 @@ +name: Code Review + +on: + workflow_call: + secrets: + AZURE_SUBSCRIPTION_ID: + required: true + AZURE_TENANT_ID: + required: true + AZURE_CLIENT_ID: + required: true + +jobs: + validation: + name: Validation + runs-on: ubuntu-24.04 + permissions: + contents: read + outputs: + should_review: ${{ steps.validate.outputs.should_review }} + + steps: + - name: Check PR requirements + id: check-pr + env: + IS_DRAFT: ${{ github.event.pull_request.draft }} + run: | + if [ "$IS_DRAFT" == "true" ]; then + echo "⚠️ Validation: PR is a draft - skipping review" + echo "pr_valid=false" >> $GITHUB_OUTPUT + else + echo "✅ Validation: PR is ready for review" + echo "pr_valid=true" >> $GITHUB_OUTPUT + fi + + - name: Check if prompt file exists using GitHub CLI + id: check-prompt + env: + GH_TOKEN: ${{ github.token }} + REPO: ${{ github.repository }} + REF: ${{ github.event.pull_request.head.sha }} + FILE_PATH: ".claude/prompts/review-code.md" + run: | + if gh api "repos/$REPO/contents/$FILE_PATH?ref=$REF" --silent 2>/dev/null; then + echo "prompt_exists=true" >> $GITHUB_OUTPUT + echo "✅ Found $FILE_PATH in $REPO" + else + echo "prompt_exists=false" >> $GITHUB_OUTPUT + echo "⚠️ Validation: No $FILE_PATH found - skipping Claude review" + fi + + - name: Set validation result + id: validate + env: + PR_VALID: ${{ steps.check-pr.outputs.pr_valid }} + PROMPT_EXISTS: ${{ steps.check-prompt.outputs.prompt_exists }} + run: | + if [ "$PR_VALID" == "true" ] && \ + [ "$PROMPT_EXISTS" == "true" ]; then + echo "should_review=true" >> $GITHUB_OUTPUT + echo "✅ Validation passed - code review will proceed" + else + echo "should_review=false" >> $GITHUB_OUTPUT + echo "⚠️ Validation failed - code review will be skipped" + fi + + review: + name: Review + runs-on: ubuntu-24.04 + needs: validation + if: needs.validation.outputs.should_review == 'true' + permissions: + contents: read + id-token: write + pull-requests: write + + steps: + - name: Check out repo + uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 + with: + fetch-depth: 0 + ref: ${{ github.event.pull_request.head.sha }} + persist-credentials: false + + - name: Log in to Azure + uses: bitwarden/gh-actions/azure-login@main + with: + subscription_id: ${{ secrets.AZURE_SUBSCRIPTION_ID }} + tenant_id: ${{ secrets.AZURE_TENANT_ID }} + client_id: ${{ secrets.AZURE_CLIENT_ID }} + + - name: Get Azure Key Vault secrets + id: get-kv-secrets + uses: bitwarden/gh-actions/get-keyvault-secrets@main + with: + keyvault: gh-org-bitwarden + secrets: "ANTHROPIC-API-KEY" + + - name: Log out from Azure + uses: bitwarden/gh-actions/azure-logout@main + + - name: Build review prompt + id: build-prompt + env: + PR_REPO: ${{ github.repository }} + PR_NUMBER: ${{ github.event.pull_request.number }} + PR_TITLE: ${{ github.event.pull_request.title }} + PR_BODY: ${{ github.event.pull_request.body }} + PR_AUTHOR: ${{ github.event.pull_request.user.login }} + PR_COMMIT: ${{ github.event.pull_request.head.sha }} + run: | + PROMPT_FILE=".claude/prompts/review-code.md" + + # Build the full prompt with GitHub context + repo's prompt + { + printf "REPO: %s\n" "$PR_REPO" + printf "PR NUMBER: %s\n" "$PR_NUMBER" + printf "TITLE: %s\n" "$PR_TITLE" + printf "BODY: %s\n" "$PR_BODY" + printf "AUTHOR: %s\n" "$PR_AUTHOR" + printf "COMMIT: %s\n" "$PR_COMMIT" + printf "\n" + printf "Note: The PR branch is already checked out in the current working directory.\n" + printf "\n---\n\n" + cat "$PROMPT_FILE" + } > /tmp/review-prompt.md + + # Output the prompt + { + echo 'FINAL_PROMPT<> "$GITHUB_OUTPUT" + + - name: Review with Claude Code + uses: anthropics/claude-code-action@e8bad572273ce919ba15fec95aef0ce974464753 # v1.0.13 + with: + anthropic_api_key: ${{ steps.get-kv-secrets.outputs.ANTHROPIC-API-KEY }} + track_progress: true + use_sticky_comment: true + prompt: ${{ steps.build-prompt.outputs.FINAL_PROMPT }} + claude_args: | + --allowedTools "mcp__github_comment__update_claude_comment,mcp__github_inline_comment__create_inline_comment,Bash(gh pr diff:*),Bash(gh pr view:*)" diff --git a/.github/workflows/review-code.yml b/.github/workflows/review-code.yml index 8decdb56..4469a2c6 100644 --- a/.github/workflows/review-code.yml +++ b/.github/workflows/review-code.yml @@ -4,152 +4,15 @@ on: pull_request: types: [opened, synchronize, reopened, ready_for_review] -permissions: - contents: read - pull-requests: write - jobs: - validation: - name: Validation - runs-on: ubuntu-24.04 - permissions: - contents: read - outputs: - should_review: ${{ steps.validate.outputs.should_review }} - - steps: - - name: Check PR requirements - id: check-pr - run: | - if [ "${{ github.event.pull_request.draft }}" == "true" ]; then - echo "⚠️ Validation: PR is a draft - skipping review" - echo "pr_valid=false" >> $GITHUB_OUTPUT - else - echo "✅ Validation: PR is ready for review" - echo "pr_valid=true" >> $GITHUB_OUTPUT - fi - - - name: Check for Azure credentials - id: check-azure-secret - env: - _AZURE_SUBSCRIPTION_ID: ${{ secrets.AZURE_SUBSCRIPTION_ID }} - _AZURE_TENANT_ID: ${{ secrets.AZURE_TENANT_ID }} - _AZURE_CLIENT_ID: ${{ secrets.AZURE_CLIENT_ID }} - run: | - if [ -n "$_AZURE_SUBSCRIPTION_ID" ] && [ -n "$_AZURE_TENANT_ID" ] && [ -n "$_AZURE_CLIENT_ID" ]; then - echo "credentials_valid=true" >> $GITHUB_OUTPUT - echo "✅ Validation: Azure credentials available" - else - echo "credentials_valid=false" >> $GITHUB_OUTPUT - echo "⚠️ Validation: Azure credentials not available" - echo "This is expected for external contributors or forks" - fi - - - name: Check if prompt file exists - id: check-prompt - env: - GH_TOKEN: ${{ github.token }} - run: | - # Use GitHub API to check file existence WITHOUT checkout - FILE_PATH=".claude/prompts/review-code.md" - REPO="${{ github.repository }}" - REF="${{ github.event.pull_request.head.sha }}" - - # Check if file exists via API - if gh api "repos/$REPO/contents/$FILE_PATH?ref=$REF" --silent 2>/dev/null; then - echo "prompt_exists=true" >> $GITHUB_OUTPUT - echo "✅ Found $FILE_PATH in $REPO" - else - echo "prompt_exists=false" >> $GITHUB_OUTPUT - echo "⚠️ Validation: No $FILE_PATH found - skipping Claude review" - fi - - - name: Set validation result - id: validate - run: | - if [ "${{ steps.check-pr.outputs.pr_valid }}" == "true" ] && \ - [ "${{ steps.check-azure-secret.outputs.credentials_valid }}" == "true" ] && \ - [ "${{ steps.check-prompt.outputs.prompt_exists }}" == "true" ]; then - echo "should_review=true" >> $GITHUB_OUTPUT - echo "✅ Validation passed - code review will proceed" - else - echo "should_review=false" >> $GITHUB_OUTPUT - echo "⚠️ Validation failed - code review will be skipped" - fi - review: name: Review - runs-on: ubuntu-24.04 - needs: validation - if: needs.validation.outputs.should_review == 'true' + uses: bitwarden/gh-actions/.github/workflows/_review-code.yml@main + secrets: + AZURE_SUBSCRIPTION_ID: ${{ secrets.AZURE_SUBSCRIPTION_ID }} + AZURE_TENANT_ID: ${{ secrets.AZURE_TENANT_ID }} + AZURE_CLIENT_ID: ${{ secrets.AZURE_CLIENT_ID }} permissions: contents: read - id-token: write pull-requests: write - - steps: - - name: Check out repo - uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 - with: - fetch-depth: 0 - persist-credentials: false - - - name: Log in to Azure - uses: bitwarden/gh-actions/azure-login@main - with: - subscription_id: ${{ secrets.AZURE_SUBSCRIPTION_ID }} - tenant_id: ${{ secrets.AZURE_TENANT_ID }} - client_id: ${{ secrets.AZURE_CLIENT_ID }} - - - name: Get Azure Key Vault secrets - id: get-kv-secrets - uses: bitwarden/gh-actions/get-keyvault-secrets@main - with: - keyvault: gh-org-bitwarden - secrets: "ANTHROPIC-API-KEY" - - - name: Log out from Azure - uses: bitwarden/gh-actions/azure-logout@main - - - name: Build review prompt - id: build-prompt - env: - PR_REPO: ${{ github.repository }} - PR_NUMBER: ${{ github.event.pull_request.number }} - PR_TITLE: ${{ github.event.pull_request.title }} - PR_BODY: ${{ github.event.pull_request.body }} - PR_AUTHOR: ${{ github.event.pull_request.user.login }} - PR_COMMIT: ${{ github.event.pull_request.head.sha }} - run: | - PROMPT_FILE=".claude/prompts/review-code.md" - - # Build the full prompt with GitHub context + repo's prompt - { - printf "REPO: %s\n" "$PR_REPO" - printf "PR NUMBER: %s\n" "$PR_NUMBER" - printf "TITLE: %s\n" "$PR_TITLE" - printf "BODY: %s\n" "$PR_BODY" - printf "AUTHOR: %s\n" "$PR_AUTHOR" - printf "COMMIT: %s\n" "$PR_COMMIT" - printf "\n" - printf "Note: The PR branch is already checked out in the current working directory.\n" - printf "\n---\n\n" - cat "$PROMPT_FILE" - } > /tmp/review-prompt.md - - # Output the prompt - { - echo 'FINAL_PROMPT<> "$GITHUB_OUTPUT" - - - name: Review with Claude Code - uses: anthropics/claude-code-action@e8bad572273ce919ba15fec95aef0ce974464753 # v1.0.13 - with: - anthropic_api_key: ${{ steps.get-kv-secrets.outputs.ANTHROPIC-API-KEY }} - track_progress: true - use_sticky_comment: true - prompt: ${{ steps.build-prompt.outputs.FINAL_PROMPT }} - claude_args: | - --allowedTools "mcp__github_comment__update_claude_comment,mcp__github_inline_comment__create_inline_comment,Bash(gh pr diff:*),Bash(gh pr view:*)" + id-token: write