# Explicit review identities share workflow concurrency; legacy names use ordered cancellation. name: Pullfrog run-name: ${{ inputs.name || github.workflow }}${{ inputs.pull_request_number && format(' | PR {0}', inputs.pull_request_number) || '' }} on: workflow_dispatch: inputs: prompt: type: string description: Agent prompt name: type: string description: Run name pull_request_number: type: string description: Optional PR identity for cancelling superseded reviews head_sha: type: string description: Optional expected PR head; stale reviews are skipped permissions: contents: read pull-requests: read concurrency: group: ${{ inputs.pull_request_number && format('pullfrog-pr-{0}', inputs.pull_request_number) || format('pullfrog-run-{0}', github.run_id) }} cancel-in-progress: true jobs: review_scope: runs-on: ubuntu-slim permissions: contents: read pull-requests: read actions: write outputs: current: ${{ steps.scope.outputs.current }} number: ${{ steps.scope.outputs.number }} head: ${{ steps.scope.outputs.head }} steps: - uses: actions/checkout@v6 with: sparse-checkout: config/scripts/pullfrog-review-scope.cjs sparse-checkout-cone-mode: false persist-credentials: false - uses: actions/github-script@v8 id: scope with: script: | const { reviewScope } = require('./config/scripts/pullfrog-review-scope.cjs') await reviewScope({ github, context, core }) pullfrog: needs: review_scope if: needs.review_scope.outputs.current == 'true' runs-on: ubuntu-latest permissions: id-token: write contents: read pull-requests: read steps: - name: Checkout code uses: actions/checkout@v6 with: fetch-depth: 1 - name: Recheck review head before starting agent id: freshness if: needs.review_scope.outputs.number != '' uses: actions/github-script@v8 env: REVIEW_NUMBER: ${{ needs.review_scope.outputs.number }} REVIEW_HEAD: ${{ needs.review_scope.outputs.head }} with: script: | const { data: pr } = await github.rest.pulls.get({ ...context.repo, pull_number: Number(process.env.REVIEW_NUMBER) }) core.setOutput('current', pr.state === 'open' && pr.head.sha === process.env.REVIEW_HEAD) - name: Run agent if: needs.review_scope.outputs.number == '' || steps.freshness.outputs.current == 'true' uses: pullfrog/pullfrog@v0 with: prompt: ${{ inputs.prompt }} env: # add at least one provider API key ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }} CLAUDE_CODE_OAUTH_TOKEN: ${{ secrets.CLAUDE_CODE_OAUTH_TOKEN }} OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }} GOOGLE_GENERATIVE_AI_API_KEY: ${{ secrets.GOOGLE_GENERATIVE_AI_API_KEY }} GEMINI_API_KEY: ${{ secrets.GEMINI_API_KEY }} XAI_API_KEY: ${{ secrets.XAI_API_KEY }} DEEPSEEK_API_KEY: ${{ secrets.DEEPSEEK_API_KEY }} MOONSHOT_API_KEY: ${{ secrets.MOONSHOT_API_KEY }} OPENROUTER_API_KEY: ${{ secrets.OPENROUTER_API_KEY }} OPENCODE_API_KEY: ${{ secrets.OPENCODE_API_KEY }} # for Amazon Bedrock (https://docs.pullfrog.com/bedrock) # AWS_BEARER_TOKEN_BEDROCK: ${{ secrets.AWS_BEARER_TOKEN_BEDROCK }} # AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }} # AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }} # AWS_REGION: us-east-1 # BEDROCK_MODEL_ID: # for Google Vertex AI (https://docs.pullfrog.com/vertex) # VERTEX_SERVICE_ACCOUNT_JSON: ${{ secrets.VERTEX_SERVICE_ACCOUNT_JSON }} # GOOGLE_CLOUD_PROJECT: my-project # VERTEX_LOCATION: global # VERTEX_MODEL_ID: