chore(.gemini): add GEMINI.md context file #27127
Workflow file for this run
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| --- | |
| name: apidiff | |
| on: | |
| pull_request: | |
| permissions: | |
| contents: read | |
| env: | |
| GOTOOLCHAIN: local | |
| jobs: | |
| scan_changes: | |
| runs-on: ubuntu-latest | |
| steps: | |
| - uses: actions/checkout@v4 | |
| with: | |
| ref: main | |
| - name: Get main commit | |
| id: main | |
| run: echo "hash=$(git rev-parse HEAD)" >> $GITHUB_OUTPUT | |
| - uses: actions/checkout@v4 | |
| - uses: actions/setup-go@v5 | |
| with: | |
| go-version: '1.25.x' | |
| - name: Get changed directories | |
| id: changed_dirs | |
| # Ignore changes to the internal and root directories. | |
| # Ignore added files with --diff-filter=a. | |
| run: | | |
| dirs=$(go run ./internal/actions/cmd/changefinder -q --diff-filter=a) | |
| if [ -z "$dirs" ] | |
| then | |
| echo "skip=1" >> $GITHUB_OUTPUT | |
| echo "No changes worth diffing!" | |
| else | |
| for d in $dirs; do list=${list},\"${d}\"; done | |
| echo "changed={\"changed\":[${list#,}]}" >> $GITHUB_OUTPUT | |
| echo "skip=" >> $GITHUB_OUTPUT | |
| fi | |
| outputs: | |
| changed_dirs: ${{ steps.changed_dirs.outputs.changed }} | |
| skip: ${{ steps.changed_dirs.outputs.skip }} | |
| apidiff: | |
| needs: scan_changes | |
| runs-on: ubuntu-latest | |
| # This job runs if there are changed directories and the PR is not explicitly | |
| # marked as allowing breaking changes. | |
| if: ${{ !needs.scan_changes.outputs.skip && !contains(github.event.pull_request.labels.*.name, 'breaking change allowed') }} | |
| permissions: | |
| pull-requests: write | |
| strategy: | |
| matrix: ${{ fromJson(needs.scan_changes.outputs.changed_dirs) }} | |
| steps: | |
| - uses: actions/setup-go@v5 | |
| with: | |
| go-version: '1.25.x' | |
| - name: Install latest apidiff | |
| run: go install golang.org/x/exp/cmd/apidiff@latest | |
| - uses: actions/checkout@v4 | |
| with: | |
| ref: main | |
| - name: Create baseline | |
| id: baseline | |
| run: | | |
| export CHANGED=${{ matrix.changed }} | |
| echo pkg="${CHANGED//\//_}_pkg.main" >> $GITHUB_OUTPUT | |
| - name: Create Go package baseline | |
| run: cd ${{ matrix.changed }} && apidiff -m -w ${{ steps.baseline.outputs.pkg }} . | |
| - name: Upload baseline package data | |
| uses: actions/upload-artifact@v5 | |
| with: | |
| name: ${{ steps.baseline.outputs.pkg }} | |
| path: ${{ matrix.changed }}/${{ steps.baseline.outputs.pkg }} | |
| retention-days: 1 | |
| - uses: actions/checkout@v4 | |
| - name: Download baseline package data | |
| uses: actions/download-artifact@v6 | |
| with: | |
| name: ${{ steps.baseline.outputs.pkg }} | |
| path: ${{ matrix.changed }} | |
| # Step 1: Run apidiff to detect breaking changes. | |
| # Instead of failing the job immediately, we capture the output and set a | |
| # flag (`breaking_change_found`) to be used in subsequent steps. | |
| - name: Detect breaking changes | |
| id: detect | |
| # Only ignore Go interface additions when the PR is from Librarian (e.g., librarian-20251016T095208Z) | |
| # as it is likely a new method added to the gRPC client stub interface, which is non-breaking. | |
| env: | |
| PR_HEAD_REF: ${{ github.event.pull_request.head.ref }} | |
| run: | | |
| cd ${{ matrix.changed }} | |
| apidiff -m -incompatible ${{ steps.baseline.outputs.pkg }} . > diff.txt | |
| if [[ "$PR_HEAD_REF" == librarian-* ]]; then | |
| sed -i '/: added/d' ./diff.txt | |
| fi | |
| cat diff.txt | |
| if [ -s diff.txt ]; then | |
| echo "breaking_change_found=true" >> $GITHUB_OUTPUT | |
| else | |
| echo "breaking_change_found=false" >> $GITHUB_OUTPUT | |
| fi | |
| # Step 2: Conditionally fail the job for GA APIs. | |
| # This step runs only if a breaking change was found in the previous step. | |
| # It checks if the changed module is a GA version (e.g., "apiv1", "apiv2"). | |
| # If it is, the `exit 1` command fails the job, blocking the PR. | |
| # For non-GA versions (e.g., "apiv1beta1"), this step is skipped, and the | |
| # job continues. | |
| - name: Conditionally fail for GA APIs | |
| id: ga_check # Add an ID to this step to check its outcome later. | |
| if: steps.detect.outputs.breaking_change_found == 'true' | |
| run: | | |
| if [[ "${{ matrix.changed }}" =~ apiv[0-9]+$ ]]; then | |
| echo "Error: Breaking change detected in GA API: ${{ matrix.changed }}" | |
| exit 1 | |
| fi | |
| echo "Breaking change in non-GA API, proceeding." | |
| # Step 3: Add a "breaking change" label. | |
| # This step runs if a breaking change was found, regardless of whether the | |
| # API is GA or not. This ensures that all breaking changes are labeled for | |
| # review, even if they don't block the PR. | |
| - name: Add breaking change label | |
| if: ${{ steps.detect.outputs.breaking_change_found == 'true' && !github.event.pull_request.head.repo.fork }} | |
| uses: actions/github-script@v7 | |
| with: | |
| script: | | |
| github.rest.issues.addLabels({ | |
| issue_number: ${{ github.event.number }}, | |
| owner: context.repo.owner, | |
| repo: context.repo.repo, | |
| labels: ['breaking change'] | |
| }) | |
| # Step 4: For non-GA APIs, post the breaking change details as a comment. | |
| # This step only runs if the ga_check step succeeded, meaning a breaking | |
| # change was found in a non-GA API. This provides high-visibility feedback | |
| # without blocking the PR, and avoids redundant comments for GA APIs where | |
| # the failed presubmit check is the primary signal. | |
| - name: Post breaking change details as a comment | |
| if: ${{ steps.ga_check.outcome == 'success' && !github.event.pull_request.head.repo.fork }} | |
| uses: actions/github-script@v7 | |
| with: | |
| script: | | |
| const fs = require('fs'); | |
| const diff = fs.readFileSync('${{ matrix.changed }}/diff.txt', 'utf8'); | |
| const moduleName = '${{ matrix.changed }}'; | |
| const commentHeader = `### ⚠️ Breaking change detected in pre-GA module: \`${moduleName}\``; | |
| const body = `${commentHeader}\n\nThe apidiff check has detected one or more breaking changes in this module for pre-GA APIs.\n\nPre-GA breaking changes do not block the pull request, but are flagged here for review.\n\nFor any pre-GA breaking change that needs investigation, open a new issue containing a link to this comment, then you may proceed with reviewing and merging this PR.\n\n\`\`\`\n${diff}\n\`\`\``; | |
| const { data: comments } = await github.rest.issues.listComments({ | |
| owner: context.repo.owner, | |
| repo: context.repo.repo, | |
| issue_number: context.issue.number, | |
| }); | |
| const existingComment = comments.find(comment => comment.body.startsWith(commentHeader)); | |
| if (existingComment) { | |
| await github.rest.issues.updateComment({ | |
| owner: context.repo.owner, | |
| repo: context.repo.repo, | |
| comment_id: existingComment.id, | |
| body: body, | |
| }); | |
| } else { | |
| await github.rest.issues.createComment({ | |
| owner: context.repo.owner, | |
| repo: context.repo.repo, | |
| issue_number: context.issue.number, | |
| body: body, | |
| }); | |
| } |