diff --git a/.github/workflows/apidiff.yaml b/.github/workflows/apidiff.yaml deleted file mode 100644 index 7bb51ed37891..000000000000 --- a/.github/workflows/apidiff.yaml +++ /dev/null @@ -1,178 +0,0 @@ -name: API Diff Check - -on: - # SECURITY: Using pull_request_target to support fork PRs with write permissions. - # PR code is checked out but only for static analysis - it is never executed. - # If modifying this workflow, ensure PR code is never executed and user inputs are not used unsafely. - pull_request_target: - types: [opened, synchronize] - paths: - - 'pkg/**/*.go' - - 'rpc/**/*.go' - -permissions: - contents: read - pull-requests: write - issues: write - -jobs: - apidiff: - runs-on: ubuntu-24.04 - name: API Diff Check - steps: - # Check if PR has conflicts. When conflicts exist, the merge commit becomes - # frozen at an old state and apidiff cannot run correctly. - - name: Check for merge conflicts - env: - GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} - PR_NUMBER: ${{ github.event.pull_request.number }} - # pull_request_target and mergeability are processed asynchronously. - # As a result, it’s possible that we start the check before GitHub has finished calculating the mergeability. - # To handle this, a retry mechanism has been added — it waits for 2 seconds after each attempt. - # If mergeable_state isn’t obtained after 5 attempts, an error is returned. - run: | - MAX=5 - for i in $(seq 1 "$MAX"); do - state=$(gh api "repos/$GITHUB_REPOSITORY/pulls/$PR_NUMBER" --jq .mergeable_state) - echo "mergeable_state=$state" - - if [ "$state" = "dirty" ]; then - echo "::error::This PR has merge conflicts. Please resolve conflicts before running apidiff." - exit 1 - fi - - if [ -n "$state" ] && [ "$state" != "unknown" ] && [ "$state" != "null" ]; then - break - fi - - if [ "$i" -lt "$MAX" ] && { [ -z "$state" ] || [ "$state" = "unknown" ] || [ "$state" = "null" ]; }; then - echo "::error::Could not determine mergeability after $i tries." - exit 1 - fi - - sleep 2 - done - - # Checkout PR merge commit to compare against base branch - # This ensures we compare the actual merge result with the base branch, - # avoiding false positives when PR is not rebased with latest main - - name: Checkout - uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 - with: - ref: refs/pull/${{ github.event.pull_request.number }}/merge - - - name: Set up Go - uses: ./.github/actions/setup-go - - # Ensure the base commit exists locally for go-apidiff to compare against. - # Even though we checkout the merge commit, go-apidiff needs the base ref to exist. - # Use base.ref instead of base.sha, since base.sha is outdated (not updated after every commit). - # cf. https://github.com/orgs/community/discussions/59677 - - name: Fetch base commit - id: fetch_base - run: | - set -euo pipefail - BASE_REF="${{ github.event.pull_request.base.ref || github.event.merge_group.base_ref }}" - if [ -z "${BASE_REF:-}" ]; then - echo "::error::BASE_REF is empty (no base ref in event payload)"; exit 1 - fi - - git fetch --depth=1 origin "$BASE_REF" - - BASE_SHA="$(git rev-parse "origin/$BASE_REF")" - if [ -z "${BASE_SHA:-}" ]; then - echo "::error::BASE_SHA is empty (failed to resolve origin/$BASE_REF)"; exit 1 - fi - echo "base_sha=$BASE_SHA" >> "$GITHUB_OUTPUT" - - # NOTE: go-apidiff is not managed in go.mod because installing it via `go get -tool` - # would cause `mage tool:install` to attempt building it on Windows, which currently - # fails due to platform-specific issues. - - name: Run go-apidiff - id: apidiff - continue-on-error: true - uses: joelanford/go-apidiff@60c4206be8f84348ebda2a3e0c3ac9cb54b8f685 # v0.8.3 - with: - base-ref: ${{ steps.fetch_base.outputs.base_sha }} - version: v0.8.3 - - - name: Add apidiff label - if: ${{ steps.apidiff.outputs.semver-type == 'major' }} - uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8.0.0 - with: - script: | - const label = 'apidiff'; - await github.rest.issues.addLabels({ - owner: context.repo.owner, - repo: context.repo.repo, - issue_number: context.issue.number, - labels: [label], - }); - - - name: Comment API diff - if: ${{ steps.apidiff.outputs.semver-type == 'major' }} - uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8.0.0 - env: - APIDIFF_OUTPUT: ${{ steps.apidiff.outputs.output }} - SEMVER_TYPE: ${{ steps.apidiff.outputs.semver-type }} - with: - script: | - const header = '## 📊 API Changes Detected'; - const diff = process.env.APIDIFF_OUTPUT.trim(); - const semver = process.env.SEMVER_TYPE || 'unknown'; - const body = [ - header, - '', - `Semver impact: \`${semver}\``, - '', - '```', - diff, - '```', - ].join('\n'); - - const { data: comments } = await github.rest.issues.listComments({ - owner: context.repo.owner, - repo: context.repo.repo, - issue_number: context.issue.number, - }); - - const existing = comments.find(comment => - comment.user.type === 'Bot' && - comment.body.startsWith(header), - ); - - if (existing) { - await github.rest.issues.updateComment({ - owner: context.repo.owner, - repo: context.repo.repo, - comment_id: existing.id, - body, - }); - } else { - await github.rest.issues.createComment({ - owner: context.repo.owner, - repo: context.repo.repo, - issue_number: context.issue.number, - body, - }); - } - - # Attempt to request the premium reviewers; needs org-scoped token because GITHUB_TOKEN lacks read:org. - - name: Request trivy-premium review - if: ${{ steps.apidiff.outputs.semver-type == 'major' }} - uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8.0.0 - with: - github-token: ${{ secrets.ORG_REPO_TOKEN }} - script: | - try { - await github.rest.pulls.requestReviewers({ - owner: context.repo.owner, - repo: context.repo.repo, - pull_number: context.issue.number, - team_reviewers: ['trivy-premium'], - }); - console.log('Requested review from aquasecurity/trivy-premium team'); - } catch (error) { - core.error(`Failed to request trivy-premium reviewers: ${error.message}`); - throw error; - }