Repository navigation
Add pull request size check #12974
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Add pull request size check #12974
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,11 @@ | ||
| issuer: https://token.actions.githubusercontent.com | ||
|
|
||
| subject: repo:DataDog/dd-trace-java:pull_request | ||
|
|
||
| claim_pattern: | ||
| event_name: pull_request | ||
| job_workflow_ref: DataDog/dd-trace-java/\.github/workflows/check-pull-request-size\.yaml@refs/(pull/[0-9]+/merge|heads/.+) | ||
|
|
||
| permissions: | ||
| issues: write | ||
| pull_requests: write |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,27 @@ | ||
| #!/usr/bin/env bash | ||
| # Counts the lines added from the merge base of two revisions, excluding moved lines and lock files. | ||
| # Usage: count-added-lines.sh <base> [head] | ||
| # | ||
| # Moved lines (blocks of at least 20 alphanumeric characters deleted and added elsewhere, even | ||
| # re-indented or in another file) are told apart from added lines using diff colors: | ||
| # added lines are painted green and moved lines blue, then only the green lines are counted. | ||
| set -euo pipefail | ||
|
|
||
| BASE=${1:?Usage: $0 <base> [head]} | ||
| HEAD=${2:-HEAD} | ||
|
|
||
| git \ | ||
| -c color.diff.meta=bold \ | ||
| -c color.diff.new=green \ | ||
| -c color.diff.newMoved=blue \ | ||
| diff \ | ||
| --color=always \ | ||
| --color-moved=blocks \ | ||
| --color-moved-ws=allow-indentation-change \ | ||
| --ws-error-highlight=none \ | ||
| --merge-base \ | ||
| --find-renames \ | ||
| --no-ext-diff \ | ||
| "$BASE" "$HEAD" \ | ||
| -- . ':(exclude,glob)**/gradle.lockfile' | | ||
| awk '/^\033\[32m\+/ { count++ } END { print count + 0 }' |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,110 @@ | ||
| name: Check pull request size | ||
| on: | ||
| pull_request: | ||
| types: [opened, reopened, edited, ready_for_review, labeled, unlabeled, synchronize] | ||
| branches: | ||
| - master | ||
| - "release/v*" | ||
|
|
||
| concurrency: | ||
| group: ${{ github.workflow }}-${{ github.ref }} | ||
| cancel-in-progress: true | ||
|
|
||
| jobs: | ||
| check_pull_request_size: | ||
| name: Check pull request size | ||
| # Skip draft pull requests | ||
| if: ${{ !github.event.pull_request.draft }} | ||
| permissions: | ||
| contents: read # Required to check out the pull request changes | ||
| id-token: write # Required for OIDC token federation | ||
| runs-on: ubuntu-latest | ||
| steps: | ||
| - name: Checkout pull request changes | ||
| uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # 7.0.1 | ||
| with: | ||
| fetch-depth: 2 # Fetch the pull request merge commit and its parents | ||
| filter: blob:none | ||
| sparse-checkout: .github/scripts | ||
| persist-credentials: false | ||
| - name: Count added lines | ||
| id: count-added-lines | ||
| run: | | ||
| # The first parent of the pull request merge commit is the base branch | ||
| ADDED_LINES=$(.github/scripts/count-added-lines.sh HEAD^1 HEAD) | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
On any PR that modifies AGENTS.md reference: AGENTS.md:L74-L74 Useful? React with 👍 / 👎.
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
A PR modifying count-added-lines.sh executes author-controlled shell code in a job with id-token: write. That code can request an OIDC token and exchange it under the new policy, which accepts PR merge refs and grants repository-wide issue and pull-request write permissions. Disabling persisted checkout credentials does not restrict OIDC access. Separate untrusted counting from credentialed commenting and bind token issuance to a trusted workflow revision. React: 👍 useful · 👎 wrong · 😕 unclear · 👀 right, but not acting on it What each reaction means
🤖 Bits Code Review · Open Bits AI session |
||
| echo "Added lines: $ADDED_LINES" | ||
| echo "added_lines=$ADDED_LINES" >> "$GITHUB_OUTPUT" | ||
| - name: Get GitHub Token via dd-octo-sts | ||
| id: generate-token | ||
| uses: DataDog/dd-octo-sts-action@c85802a58e202b7e764b5441e4378c76043c8108 # v1.0.5 | ||
| with: | ||
| scope: DataDog/dd-trace-java | ||
| policy: self.check-pull-request-size | ||
| - name: Check pull request size | ||
| uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # 9.0.0 | ||
| env: | ||
| ADDED_LINES: ${{ steps.count-added-lines.outputs.added_lines }} | ||
| MAX_ADDED_LINES: 1000 | ||
| with: | ||
| github-token: ${{ steps.generate-token.outputs.token }} | ||
| script: | | ||
| const managedMarker = '<!-- dd-trace-java-check-pull-request-size -->' | ||
| const overrideLabel = 'tag: override pr size check' | ||
| const addedLines = parseInt(process.env.ADDED_LINES, 10) | ||
| const maxAddedLines = parseInt(process.env.MAX_ADDED_LINES, 10) | ||
|
|
||
| // Look for the previous managed comment | ||
| const comments = await github.paginate(github.rest.issues.listComments, { | ||
| issue_number: context.payload.pull_request.number, | ||
| owner: context.repo.owner, | ||
| repo: context.repo.repo | ||
| }) | ||
| const existingComment = comments.find(comment => comment.body?.includes(managedMarker)) | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
If a contributor quotes a warning containing the managed marker, this lookup can select their comment instead of the bot's. Subsequent runs target it for overwriting when oversized or deletion when below the limit or overridden, leaving the actual warning untouched. Focused mocked execution confirmed all three selection paths. Require the expected issuing app's identity together with the marker before modifying a comment. React: 👍 useful · 👎 wrong · 😕 unclear · 👀 right, but not acting on it What each reaction means
🤖 Bits Code Review · Open Bits AI session |
||
|
|
||
| const deleteManagedComment = async () => { | ||
| if (existingComment) { | ||
| await github.rest.issues.deleteComment({ | ||
| comment_id: existingComment.id, | ||
| owner: context.repo.owner, | ||
| repo: context.repo.repo | ||
| }) | ||
| } | ||
| } | ||
|
|
||
| // Check for override label — skip the check if label present | ||
| const labels = context.payload.pull_request.labels.map(l => l.name) | ||
| if (labels.includes(overrideLabel)) { | ||
| await deleteManagedComment() | ||
| console.log(`${overrideLabel} label detected — skipping the check.`) | ||
| return | ||
| } | ||
|
|
||
| if (addedLines < maxAddedLines) { | ||
| await deleteManagedComment() | ||
| console.log(`Pull request adds ${addedLines} lines, below the ${maxAddedLines} lines limit.`) | ||
| return | ||
| } | ||
|
|
||
| const body = `**❌ Pull Request Too Large**\n\n` + | ||
| `This pull request adds **${addedLines}** lines while the limit is **${maxAddedLines}** lines.\n` + | ||
| `Deleted lines, moved code, and \`gradle.lockfile\` files are not counted.\n\n` + | ||
| `Please split it into smaller stacked pull requests, each one targeting the branch of the previous one. ` + | ||
| `See the [pull request size guidelines](https://github.com/DataDog/dd-trace-java/blob/master/CONTRIBUTING.md#pull-request-size) for more details.\n\n` + | ||
| `If this PR cannot be split, exceptionnaly add the \`${overrideLabel}\` label to bypass this workflow.\n\n` + | ||
| managedMarker | ||
| if (existingComment) { | ||
| await github.rest.issues.updateComment({ | ||
| comment_id: existingComment.id, | ||
| owner: context.repo.owner, | ||
| repo: context.repo.repo, | ||
| body | ||
| }) | ||
| } else { | ||
| await github.rest.issues.createComment({ | ||
| issue_number: context.payload.pull_request.number, | ||
| owner: context.repo.owner, | ||
| repo: context.repo.repo, | ||
| body | ||
| }) | ||
| } | ||
| core.setFailed(`Pull request adds ${addedLines} lines, above the ${maxAddedLines} lines limit. See PR comment for details. To bypass this workflow, add the '${overrideLabel}' label.`) | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -71,6 +71,7 @@ docs/ Developer documentation (see below) | |
| ## PR conventions | ||
|
|
||
| - Title: imperative verb sentence describing user-visible change (e.g. "Fix span sampling rule parsing") | ||
| - Size: keep PRs small (under 1000 added lines), split larger changes into stacked PRs. | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. note: I suggest to explicitly write that agents are not allowed to add this tag. I.e. Only human judgment should be involved in this tag!
Collaborator
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Do you think so? I would be fine having a bot adding the label on mechanical changes for example. |
||
| - Labels: always add `tag: ai generated` and at least one `comp:` or `inst:` label + one `type:` label | ||
| - Use `tag: no release notes` for internal/refactoring changes | ||
| - Open as draft first, convert to ready when reviewable | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
On every workflow run,
filter: blob:nonecauses this pinned version ofactions/checkoutto ignore the followingsparse-checkoutinput—the action explicitly documents thatfilteroverrides sparse checkout. Consequently the checkout materializes the repository's entire roughly 12,000-file, 111 MiB snapshot instead of only.github/scripts, adding avoidable I/O to a check triggered for every PR update and label change. Remove the conflicting filter or configure partial and sparse checkout manually so the intended path restriction is honored.Useful? React with 👍 / 👎.