slawekjaranowski commented on code in PR #326: URL: https://github.com/apache/maven-gh-actions-shared/pull/326#discussion_r4130275891
########## .github/workflows/pr-check.yml: ########## @@ -0,0 +1,154 @@ +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. + +# Reusable workflow grouping the checks run against a pull request. +name: PR check + +on: + workflow_call: + +# clear all permissions for GITHUB_TOKEN +permissions: {} + +jobs: + # The PR description must contain at least one CHECKED Apache license declaration + # ("- [x] I hereby declare ..."). + # - Missing -> adds ONE comment and fails. + # - Fixed -> hides the comment as outdated and passes. + # - Removed again -> unhides the same comment (no duplicates). + license-declaration: + permissions: + pull-requests: write + issues: write # PR comments are "issue comments" in the API + runs-on: ubuntu-slim + # Skip (a skipped job counts as successful, so a required check doesn't block the merge): + # - PRs opened by bots (Dependabot, Renovate, other GitHub Apps), + # - PRs opened by members of the organization owning the repository (apache). + if: >- + github.event.pull_request.user.type != 'Bot' && + !contains(fromJSON('["OWNER", "MEMBER"]'), github.event.pull_request.author_association) + # Prevents races (e.g. opened + edited in quick succession => two comments). + # Do NOT define the same concurrency group in the caller – it would deadlock. + concurrency: + group: license-declaration-check-${{ github.repository }}-${{ github.event.pull_request.number }} + cancel-in-progress: false + steps: + - name: Check license declaration + uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0 + with: + script: | + const MARKER = '<!-- license-declaration-check -->'; + + // At least one of these lines must be present with a checked box + const DECLARATIONS = [ + 'I hereby declare this contribution to be licensed under the [Apache License Version 2.0, January 2004](https://www.apache.org/licenses/LICENSE-2.0)', + 'I hereby declare that I have filed an [Apache Individual Contributor License Agreement](https://www.apache.org/licenses/icla.pdf).', + ]; + + const { owner, repo } = context.repo; + + // The workflow only makes sense for pull request events + const prNumber = context.payload.pull_request?.number; + if (!prNumber) { + core.setFailed(`This workflow must be called from pull_request or pull_request_target events (got "${context.eventName}").`); + return; + } + + // "- [x] <declaration>" or "* [X] <declaration>", one per line, optional indentation/trailing spaces. + // Links may use either http:// or https://. + const escape = s => s + .replace(/[.*+?^${}()|[\]\\]/g, '\\$&') + .replace(/https?:\/\//g, 'https?://'); + const regex = new RegExp( + `^[ \\t]*[-*][ \\t]+\\[[xX]\\][ \\t]+(?:${DECLARATIONS.map(escape).join('|')})[ \\t]*\\r?$`, + 'm' + ); + + // Fetch the current description from the API (the event payload may be stale on "Re-run jobs") + const { data: pr } = await github.rest.pulls.get({ owner, repo, pull_number: prNumber }); + const hasDeclaration = regex.test(pr.body || ''); + + // Find our comment: author must be a bot and the comment must contain the marker + const comments = await github.paginate(github.rest.issues.listComments, { + owner, repo, issue_number: prNumber, per_page: 100, + }); + const existing = comments.find( + c => c.user?.type === 'Bot' && c.body?.includes(MARKER) + ); + + // Check whether the comment is already hidden + let isMinimized = false; + if (existing) { + const res = await github.graphql( + `query($id: ID!) { Review Comment: Applied in 00507a6 — switched to `github.paginate.iterator` with an early `break`. One note on the rationale: rate limiting was not really the issue. At `per_page: 100`, even a PR with 300 comments is 3 requests against a 1000/h per-repo budget for `GITHUB_TOKEN`. The change is worth it for a different reason: the bot comment is created on the first failing run, `issues.listComments` returns comments oldest-first, so the marker is almost always on page 1 — one request instead of one per 100 comments, at no cost in readability. -- This is an automated message from the Apache Git Service. To respond to the message, please log on to GitHub and use the URL above to go to the specific comment. To unsubscribe, e-mail: [email protected] For queries about this service, please contact Infrastructure at: [email protected]
