From 8dd97890ff36179ea30f3ddd74eb69abb0f83a2a Mon Sep 17 00:00:00 2001 From: Novus Nota <68142933+novusnota@users.noreply.github.com> Date: Fri, 28 Nov 2025 18:19:29 +0100 Subject: [PATCH 1/9] feat(ci): add workflow to validate size of pull requests Sets the upper bound of additions to 500 in pull requests from forks, and to 1000 in PRs from direct branches. Not all additions are equal, as many generated, API or script files are ignored for clarity. --- .github/workflows/bouncer.yml | 178 ++++++++++++++++++++++++++++++++++ 1 file changed, 178 insertions(+) create mode 100644 .github/workflows/bouncer.yml diff --git a/.github/workflows/bouncer.yml b/.github/workflows/bouncer.yml new file mode 100644 index 000000000..e1527a795 --- /dev/null +++ b/.github/workflows/bouncer.yml @@ -0,0 +1,178 @@ +name: 🏀 Bouncer +# aka 🚪 Supervisor + +env: + # additions only + MAX_ADDITIONS_FORKS: 500 + # on rare occasions, when maintainers need to edit a lot of things at once + MAX_ADDITIONS_DIRECT_BRANCHES: 1000 + # the name of this workflow file wrapped in backticks + MSG_PREFIX: "`bouncer.yml`" + +on: + # pull_request: # FIXME: for testing purposes only, delete this and uncomment pull_request_target before opening for review + # branches: ["**"] + # types: [opened, synchronize] + pull_request_target: # do NOT use actions/checkout + branches: ["**"] # any branches + types: [opened, synchronize] # on creation and on new commits + +concurrency: + group: ${{ github.workflow }}-${{ github.ref }}-bouncer + cancel-in-progress: true + +permissions: + contents: read + pull-requests: write + +jobs: + enforce-smaller-requests: + name: "Bloat" + runs-on: ubuntu-latest + steps: + - name: Set MAX_ADDITIONS for forks + if: | + github.event_name == 'pull_request_target' && + github.event.pull_request.head.repo.fork == true || + github.event.pull_request.head.repo.full_name != github.repository + run: echo 'MAX_ADDITIONS=${{ env.MAX_ADDITIONS_FORKS }}' >> "$GITHUB_ENV" + + - name: Set MAX_ADDITIONS for direct branches + if: | + github.event.pull_request.head.repo.fork != true || + github.event.pull_request.head.repo.full_name == github.repository + run: echo 'MAX_ADDITIONS=${{ env.MAX_ADDITIONS_DIRECT_BRANCHES }}' >> "$GITHUB_ENV" + + - name: Remove prior comments + if: github.event.action == 'synchronize' + uses: actions/github-script@f28e40c7f34bde8b3046d885e986cb6290c5673b # v7.1.0 + with: + script: | + const comments = await github.rest.issues.listComments({ + owner: context.repo.owner, + repo: context.repo.repo, + issue_number: context.payload.pull_request.number, + }); + for (const comment of comments.data) { + const isHidden = (await github.graphql(` + query($nodeId: ID!) { + node(id: $nodeId) { + ... on IssueComment { + isMinimized + } + } + } + `, { nodeId: comment.node_id }))?.node?.isMinimized; + await exec.exec('sleep 0.5s'); + if (isHidden) { continue; } + if ( + comment.user.login === 'github-actions[bot]' && + comment.body.startsWith('${{ env.MSG_PREFIX }}') + ) { + console.log('Comment node_id:', comment.node_id); + console.log(await github.graphql(` + mutation($subjectId: ID!) { + minimizeComment(input: { + subjectId: $subjectId, + classifier: OUTDATED + }) { + minimizedComment { + isMinimized + minimizedReason + } + } + } + `, { + subjectId: comment.node_id, + })); + await exec.exec('sleep 0.5s'); + } + } + + - name: Check if a number of additions modulo filtered files is within the threshold + id: stats + uses: actions/github-script@f28e40c7f34bde8b3046d885e986cb6290c5673b # v7.1.0 + with: + # This JavaScript code cannot be moved to a separate file because + # this workflow must NOT checkout the repository for security reasons + # + # The same note applies to all JS code in this file + script: | + const maxAdditions = Number(process.env.MAX_ADDITIONS ?? '500'); + const { data: files } = await github.rest.pulls.listFiles({ + owner: context.repo.owner, + repo: context.repo.repo, + pull_number: context.payload.pull_request.number, + per_page: 100, + }); + const filtered = files.filter( + (f) => + ![ + 'package-lock.json', + 'ecosystem/api/toncenter/v2.json', + 'ecosystem/api/toncenter/v3.yaml', + 'ecosystem/api/toncenter/smc-index.json', + 'tvm/instructions.mdx', + ].includes(f.filename) && !f.filename.endsWith('.py'), + ); + // NOTE: consider looking for .changes + const additions = filtered.reduce((acc, it) => acc + it.additions, 0); + if (additions > maxAdditions) { + core.setOutput('trigger', 'true'); + } else { + core.setOutput('trigger', 'false'); + } + + - name: ${{ steps.stats.outputs.trigger == 'true' && 'An opened PR is too big to be reviewed at once!' || 'No comments' }} + if: github.event.action == 'opened' && steps.stats.outputs.trigger == 'true' + uses: actions/github-script@f28e40c7f34bde8b3046d885e986cb6290c5673b # v7.1.0 + with: + script: | + await github.rest.issues.createComment({ + owner: context.repo.owner, + repo: context.repo.repo, + issue_number: context.payload.pull_request.number, + body: [ + '${{ env.MSG_PREFIX }}', + 'Thank you for the contribution!', + [ + 'Unfortunately, it is too large, with over ${{ env.MAX_ADDITIONS }} added lines,', + 'excluding some generated or otherwise special files.', + 'Thus, this pull request is challenging to review and iterate on.', + ].join(' '), + [ + 'Please split the PR into several smaller ones and consider', + 'reverting any unrelated changes, writing less, or approaching', + 'the problem in the issue from a different angle.', + ].join(' '), + 'I will close this PR and will be looking forward to your next submissions. If there is a mistake and you still intend to proceed, do reopen it.', + ].join('\n\n'), + }); + await github.rest.pulls.update({ + owner: context.repo.owner, + repo: context.repo.repo, + pull_number: context.payload.pull_request.number, + // This is fun + state: 'closed', + }); + process.exit(1); + + - name: ${{ steps.stats.outputs.trigger == 'true' && 'Some change in the PR made it too big!' || 'No comments' }} + if: github.event.action == 'synchronize' && steps.stats.outputs.trigger == 'true' + uses: actions/github-script@f28e40c7f34bde8b3046d885e986cb6290c5673b # v7.1.0 + with: + script: | + await github.rest.issues.createComment({ + owner: context.repo.owner, + repo: context.repo.repo, + issue_number: context.payload.pull_request.number, + body: [ + '${{ env.MSG_PREFIX }}', + [ + 'The most recent commit has made this PR go over ${{ env.MAX_ADDITIONS }} added lines.', + 'Please, decrease the size of this pull request or consider splitting it into several smaller requests.' + ].join(' '), + 'Until then, the CI will be marked as failed.', + ].join('\n\n'), + }); + process.exit(1); From cd528e1f975e1264ef1ea7bdb33207b9115c44e6 Mon Sep 17 00:00:00 2001 From: Novus Nota <68142933+novusnota@users.noreply.github.com> Date: Sat, 29 Nov 2025 06:26:57 +0100 Subject: [PATCH 2/9] remove `pull_request`, keeping only `pull_request_target` --- .github/workflows/bouncer.yml | 3 --- 1 file changed, 3 deletions(-) diff --git a/.github/workflows/bouncer.yml b/.github/workflows/bouncer.yml index e1527a795..47a348cb0 100644 --- a/.github/workflows/bouncer.yml +++ b/.github/workflows/bouncer.yml @@ -10,9 +10,6 @@ env: MSG_PREFIX: "`bouncer.yml`" on: - # pull_request: # FIXME: for testing purposes only, delete this and uncomment pull_request_target before opening for review - # branches: ["**"] - # types: [opened, synchronize] pull_request_target: # do NOT use actions/checkout branches: ["**"] # any branches types: [opened, synchronize] # on creation and on new commits From c107d734fc35e8603faed6d0aac52e8c449077b2 Mon Sep 17 00:00:00 2001 From: Novus Nota <68142933+novusnota@users.noreply.github.com> Date: Sat, 29 Nov 2025 07:48:22 +0100 Subject: [PATCH 3/9] check the title and the linked issues in the description --- .github/workflows/bouncer.yml | 69 +++++++++++++++++++++++++++++++---- 1 file changed, 62 insertions(+), 7 deletions(-) diff --git a/.github/workflows/bouncer.yml b/.github/workflows/bouncer.yml index 47a348cb0..ef9ddff36 100644 --- a/.github/workflows/bouncer.yml +++ b/.github/workflows/bouncer.yml @@ -4,15 +4,24 @@ name: 🏀 Bouncer env: # additions only MAX_ADDITIONS_FORKS: 500 - # on rare occasions, when maintainers need to edit a lot of things at once - MAX_ADDITIONS_DIRECT_BRANCHES: 1000 + # on rare occasions maintainers need to edit a lot of things at once + MAX_ADDITIONS_DIRECT_BRANCHES: 800 + # many target issues usually mean bigger pull requests + MAX_ISSUES_PER_PR: 3 # the name of this workflow file wrapped in backticks MSG_PREFIX: "`bouncer.yml`" on: - pull_request_target: # do NOT use actions/checkout - branches: ["**"] # any branches - types: [opened, synchronize] # on creation and on new commits + pull_request: # FIXME: re-enabled only to check some jobs + # any branches + branches: ["**"] + # on creation, on new commits, and description edits + types: [opened, synchronize, edited] + pull_request_target: # do NOT use actions/checkout! + # any branches + branches: ["**"] + # on creation, on new commits, and description edits + types: [opened, synchronize, edited] concurrency: group: ${{ github.workflow }}-${{ github.ref }}-bouncer @@ -25,6 +34,7 @@ permissions: jobs: enforce-smaller-requests: name: "Bloat" + if: github.event.action != 'edited' runs-on: ubuntu-latest steps: - name: Set MAX_ADDITIONS for forks @@ -45,12 +55,14 @@ jobs: uses: actions/github-script@f28e40c7f34bde8b3046d885e986cb6290c5673b # v7.1.0 with: script: | + await exec.exec('sleep 0.5s'); const comments = await github.rest.issues.listComments({ owner: context.repo.owner, repo: context.repo.repo, issue_number: context.payload.pull_request.number, }); for (const comment of comments.data) { + await exec.exec('sleep 0.5s'); const isHidden = (await github.graphql(` query($nodeId: ID!) { node(id: $nodeId) { @@ -60,13 +72,13 @@ jobs: } } `, { nodeId: comment.node_id }))?.node?.isMinimized; - await exec.exec('sleep 0.5s'); if (isHidden) { continue; } if ( comment.user.login === 'github-actions[bot]' && comment.body.startsWith('${{ env.MSG_PREFIX }}') ) { console.log('Comment node_id:', comment.node_id); + await exec.exec('sleep 0.5s'); console.log(await github.graphql(` mutation($subjectId: ID!) { minimizeComment(input: { @@ -82,7 +94,6 @@ jobs: `, { subjectId: comment.node_id, })); - await exec.exec('sleep 0.5s'); } } @@ -96,6 +107,7 @@ jobs: # The same note applies to all JS code in this file script: | const maxAdditions = Number(process.env.MAX_ADDITIONS ?? '500'); + await exec.exec('sleep 0.5s'); const { data: files } = await github.rest.pulls.listFiles({ owner: context.repo.owner, repo: context.repo.repo, @@ -125,6 +137,7 @@ jobs: uses: actions/github-script@f28e40c7f34bde8b3046d885e986cb6290c5673b # v7.1.0 with: script: | + await exec.exec('sleep 0.5s'); await github.rest.issues.createComment({ owner: context.repo.owner, repo: context.repo.repo, @@ -145,6 +158,7 @@ jobs: 'I will close this PR and will be looking forward to your next submissions. If there is a mistake and you still intend to proceed, do reopen it.', ].join('\n\n'), }); + await exec.exec('sleep 0.5s'); await github.rest.pulls.update({ owner: context.repo.owner, repo: context.repo.repo, @@ -159,6 +173,7 @@ jobs: uses: actions/github-script@f28e40c7f34bde8b3046d885e986cb6290c5673b # v7.1.0 with: script: | + await exec.exec('sleep 0.5s'); await github.rest.issues.createComment({ owner: context.repo.owner, repo: context.repo.repo, @@ -173,3 +188,43 @@ jobs: ].join('\n\n'), }); process.exit(1); + + enforce-better-descriptions: + name: "Title and description" + if: github.event.action == 'opened' || github.event.action == 'edited' + runs-on: ubuntu-latest + steps: + # issue title check + - name: "Check that the title conforms to the simplified version of Conventional Commits" + uses: actions/github-script@f28e40c7f34bde8b3046d885e986cb6290c5673b # v7.1.0 + with: + script: | + const title = context.payload.pull_request.title; + const pattern = /^(revert: )?(feat|fix|chore|refactor|test)(?:\/(feat|fix|chore|refactor|test))?!?(\(.+?\))?!?: [a-z].{1,200}/; + const matches = title.match(pattern) !== null; + if (!matches) { + core.setFailed([ + 'Title of this pull request does not conform to the simplified version of Conventional Commits (https://www.conventionalcommits.org/en/v1.0.0/)', + 'Received: ' + title, + 'Expected to find a type of feat/fix/chore/refactor/test, followed by an optional scope: description', + ].join('\n')); + process.exit(1); + } + + # issue close limits + - name: "Check that there is no more than ${{ env.MAX_ISSUES_PER_PR }} linked issues" + uses: actions/github-script@f28e40c7f34bde8b3046d885e986cb6290c5673b # v7.1.0 + with: + script: | + const maxIssuesAllowed = Number(process.env.MAX_ISSUES_PER_PR ?? '3'); + const body = context.payload.pull_request.body || ''; + const closePatterns = /(?:close[sd]?|fixes|fixed|resolve[sd]?)\s+(?:[a-z0-9_\-\/\:\.]+)?#\d+/gi; + const issueCount = [...body.matchAll(closePatterns)].length; + if (issueCount > maxIssuesAllowed) { + core.setFailed(`This pull request attempts to close ${issueCount} issues, while the maximum number allowed is ${maxIssuesAllowed}!`); + process.exit(1); + } + if (issueCount === 0) { + core.setFailed(`This pull request does not resolve any issues — no close patterns found in the description. Please, specify an issue by writing \`Closes #that-issue-number\` in the description of this PR. If there is none, create a new one!`); + process.exit(1); + } From 016bcd7224afb094aef41a97a72815819ba06958 Mon Sep 17 00:00:00 2001 From: Novus Nota <68142933+novusnota@users.noreply.github.com> Date: Sat, 29 Nov 2025 08:04:42 +0100 Subject: [PATCH 4/9] no more bash --- .github/workflows/bouncer.yml | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/.github/workflows/bouncer.yml b/.github/workflows/bouncer.yml index ef9ddff36..c8f104270 100644 --- a/.github/workflows/bouncer.yml +++ b/.github/workflows/bouncer.yml @@ -42,18 +42,26 @@ jobs: github.event_name == 'pull_request_target' && github.event.pull_request.head.repo.fork == true || github.event.pull_request.head.repo.full_name != github.repository - run: echo 'MAX_ADDITIONS=${{ env.MAX_ADDITIONS_FORKS }}' >> "$GITHUB_ENV" + uses: actions/github-script@f28e40c7f34bde8b3046d885e986cb6290c5673b # v7.1.0 + with: + script: core.exportVariable('MAX_ADDITIONS', '${{ env.MAX_ADDITIONS_FORKS }}') - name: Set MAX_ADDITIONS for direct branches if: | github.event.pull_request.head.repo.fork != true || github.event.pull_request.head.repo.full_name == github.repository - run: echo 'MAX_ADDITIONS=${{ env.MAX_ADDITIONS_DIRECT_BRANCHES }}' >> "$GITHUB_ENV" + uses: actions/github-script@f28e40c7f34bde8b3046d885e986cb6290c5673b # v7.1.0 + with: + script: core.exportVariable('MAX_ADDITIONS', '${{ env.MAX_ADDITIONS_DIRECT_BRANCHES }}') - - name: Remove prior comments + - name: Remove prior comments by their common prefix if: github.event.action == 'synchronize' uses: actions/github-script@f28e40c7f34bde8b3046d885e986cb6290c5673b # v7.1.0 with: + # This JavaScript code cannot be moved to a separate file because + # this workflow must NOT checkout the repository for security reasons + # + # The same note applies to all JS code in this file script: | await exec.exec('sleep 0.5s'); const comments = await github.rest.issues.listComments({ @@ -101,10 +109,6 @@ jobs: id: stats uses: actions/github-script@f28e40c7f34bde8b3046d885e986cb6290c5673b # v7.1.0 with: - # This JavaScript code cannot be moved to a separate file because - # this workflow must NOT checkout the repository for security reasons - # - # The same note applies to all JS code in this file script: | const maxAdditions = Number(process.env.MAX_ADDITIONS ?? '500'); await exec.exec('sleep 0.5s'); From 81cd1a7615403185664f153a6e64e6740e5df45d Mon Sep 17 00:00:00 2001 From: Novus Nota <68142933+novusnota@users.noreply.github.com> Date: Sat, 29 Nov 2025 08:04:59 +0100 Subject: [PATCH 5/9] run all checks --- .github/workflows/bouncer.yml | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/.github/workflows/bouncer.yml b/.github/workflows/bouncer.yml index c8f104270..0b3ccded0 100644 --- a/.github/workflows/bouncer.yml +++ b/.github/workflows/bouncer.yml @@ -33,7 +33,7 @@ permissions: jobs: enforce-smaller-requests: - name: "Bloat" + name: "PR is manageable" if: github.event.action != 'edited' runs-on: ubuntu-latest steps: @@ -198,8 +198,9 @@ jobs: if: github.event.action == 'opened' || github.event.action == 'edited' runs-on: ubuntu-latest steps: - # issue title check + # pr title check - name: "Check that the title conforms to the simplified version of Conventional Commits" + if: ${{ !cancelled() }} uses: actions/github-script@f28e40c7f34bde8b3046d885e986cb6290c5673b # v7.1.0 with: script: | @@ -208,15 +209,16 @@ jobs: const matches = title.match(pattern) !== null; if (!matches) { core.setFailed([ - 'Title of this pull request does not conform to the simplified version of Conventional Commits (https://www.conventionalcommits.org/en/v1.0.0/)', + 'Title of this pull request does not conform to the simplified version of Conventional Commits (https://www.conventionalcommits.org/en/v1.0.0/) used in the documentation', 'Received: ' + title, 'Expected to find a type of feat/fix/chore/refactor/test, followed by an optional scope: description', ].join('\n')); process.exit(1); } - # issue close limits + # pr close issue limits - name: "Check that there is no more than ${{ env.MAX_ISSUES_PER_PR }} linked issues" + if: ${{ !cancelled() }} uses: actions/github-script@f28e40c7f34bde8b3046d885e986cb6290c5673b # v7.1.0 with: script: | @@ -229,6 +231,11 @@ jobs: process.exit(1); } if (issueCount === 0) { - core.setFailed(`This pull request does not resolve any issues — no close patterns found in the description. Please, specify an issue by writing \`Closes #that-issue-number\` in the description of this PR. If there is none, create a new one!`); + core.setFailed([ + 'This pull request does not resolve any issues — no close patterns found in the description.', + 'Please, specify an issue by writing `Closes #that-issue-number` in the description of this PR.', + 'If there is no such issue, create a new one: https://github.com/ton-org/docs/issues/1366#issuecomment-3560650817', + ].join(' ')); + process.exit(1); } From e3b6e6a143d48730759009a3f1846bd67268462c Mon Sep 17 00:00:00 2001 From: Novus Nota <68142933+novusnota@users.noreply.github.com> Date: Sat, 29 Nov 2025 09:10:02 +0100 Subject: [PATCH 6/9] do not ~~redeem~~ close --- .github/workflows/bouncer.yml | 24 +++++------------------- 1 file changed, 5 insertions(+), 19 deletions(-) diff --git a/.github/workflows/bouncer.yml b/.github/workflows/bouncer.yml index 0b3ccded0..587c345ec 100644 --- a/.github/workflows/bouncer.yml +++ b/.github/workflows/bouncer.yml @@ -12,11 +12,6 @@ env: MSG_PREFIX: "`bouncer.yml`" on: - pull_request: # FIXME: re-enabled only to check some jobs - # any branches - branches: ["**"] - # on creation, on new commits, and description edits - types: [opened, synchronize, edited] pull_request_target: # do NOT use actions/checkout! # any branches branches: ["**"] @@ -159,17 +154,9 @@ jobs: 'reverting any unrelated changes, writing less, or approaching', 'the problem in the issue from a different angle.', ].join(' '), - 'I will close this PR and will be looking forward to your next submissions. If there is a mistake and you still intend to proceed, do reopen it.', + 'I look forward to your next submissions. If you still intend to proceed as is, then you are at the mercy of the reviewers.', ].join('\n\n'), }); - await exec.exec('sleep 0.5s'); - await github.rest.pulls.update({ - owner: context.repo.owner, - repo: context.repo.repo, - pull_number: context.payload.pull_request.number, - // This is fun - state: 'closed', - }); process.exit(1); - name: ${{ steps.stats.outputs.trigger == 'true' && 'Some change in the PR made it too big!' || 'No comments' }} @@ -209,9 +196,9 @@ jobs: const matches = title.match(pattern) !== null; if (!matches) { core.setFailed([ - 'Title of this pull request does not conform to the simplified version of Conventional Commits (https://www.conventionalcommits.org/en/v1.0.0/) used in the documentation', - 'Received: ' + title, - 'Expected to find a type of feat/fix/chore/refactor/test, followed by an optional scope: description', + 'Title of this pull request does not conform to the simplified version of Conventional Commits used in the documentation', + `Received: ${title}`, + 'Expected to find a type of feat/fix/chore/refactor/test, followed by the parts outlined here: https://www.conventionalcommits.org/en/v1.0.0/', ].join('\n')); process.exit(1); } @@ -227,7 +214,7 @@ jobs: const closePatterns = /(?:close[sd]?|fixes|fixed|resolve[sd]?)\s+(?:[a-z0-9_\-\/\:\.]+)?#\d+/gi; const issueCount = [...body.matchAll(closePatterns)].length; if (issueCount > maxIssuesAllowed) { - core.setFailed(`This pull request attempts to close ${issueCount} issues, while the maximum number allowed is ${maxIssuesAllowed}!`); + core.setFailed(`This pull request attempts to close ${issueCount} issues, while the maximum number allowed is ${maxIssuesAllowed}.`); process.exit(1); } if (issueCount === 0) { @@ -236,6 +223,5 @@ jobs: 'Please, specify an issue by writing `Closes #that-issue-number` in the description of this PR.', 'If there is no such issue, create a new one: https://github.com/ton-org/docs/issues/1366#issuecomment-3560650817', ].join(' ')); - process.exit(1); } From c5fb1a333d970cfe8364e64c2624ea10bd116914 Mon Sep 17 00:00:00 2001 From: Novus Nota <68142933+novusnota@users.noreply.github.com> Date: Sat, 29 Nov 2025 09:12:55 +0100 Subject: [PATCH 7/9] word boundary --- .github/workflows/bouncer.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/bouncer.yml b/.github/workflows/bouncer.yml index 587c345ec..06d74fd14 100644 --- a/.github/workflows/bouncer.yml +++ b/.github/workflows/bouncer.yml @@ -211,7 +211,7 @@ jobs: script: | const maxIssuesAllowed = Number(process.env.MAX_ISSUES_PER_PR ?? '3'); const body = context.payload.pull_request.body || ''; - const closePatterns = /(?:close[sd]?|fixes|fixed|resolve[sd]?)\s+(?:[a-z0-9_\-\/\:\.]+)?#\d+/gi; + const closePatterns = /\b(?:close[sd]?|fixes|fixed|resolve[sd]?)\s+(?:[a-z0-9_\-\/\:\.]+)?#\d+/gi; const issueCount = [...body.matchAll(closePatterns)].length; if (issueCount > maxIssuesAllowed) { core.setFailed(`This pull request attempts to close ${issueCount} issues, while the maximum number allowed is ${maxIssuesAllowed}.`); From 3f5702f677e24d5d957a17841fece08bb42ec6b9 Mon Sep 17 00:00:00 2001 From: Novus Nota <68142933+novusnota@users.noreply.github.com> Date: Sun, 30 Nov 2025 20:16:26 +0100 Subject: [PATCH 8/9] enhance regex --- .github/workflows/bouncer.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/bouncer.yml b/.github/workflows/bouncer.yml index 06d74fd14..6ad7686a1 100644 --- a/.github/workflows/bouncer.yml +++ b/.github/workflows/bouncer.yml @@ -198,7 +198,7 @@ jobs: core.setFailed([ 'Title of this pull request does not conform to the simplified version of Conventional Commits used in the documentation', `Received: ${title}`, - 'Expected to find a type of feat/fix/chore/refactor/test, followed by the parts outlined here: https://www.conventionalcommits.org/en/v1.0.0/', + 'Expected to find a type of: feat, fix, chore, refactor, or test, followed by the parts outlined here: https://www.conventionalcommits.org/en/v1.0.0/', ].join('\n')); process.exit(1); } @@ -211,7 +211,7 @@ jobs: script: | const maxIssuesAllowed = Number(process.env.MAX_ISSUES_PER_PR ?? '3'); const body = context.payload.pull_request.body || ''; - const closePatterns = /\b(?:close[sd]?|fixes|fixed|resolve[sd]?)\s+(?:[a-z0-9_\-\/\:\.]+)?#\d+/gi; + const closePatterns = /\b(?:close[sd]?|fixes|fixed|resolve[sd]?)\s+(?:https?:\/\/github\.com\/|[a-z0-9\-\_\/]*#\d+)/gi; const issueCount = [...body.matchAll(closePatterns)].length; if (issueCount > maxIssuesAllowed) { core.setFailed(`This pull request attempts to close ${issueCount} issues, while the maximum number allowed is ${maxIssuesAllowed}.`); From ef196cb928fff145adf88b251acb61f828288fb2 Mon Sep 17 00:00:00 2001 From: Novus Nota <68142933+novusnota@users.noreply.github.com> Date: Mon, 1 Dec 2025 01:16:42 +0100 Subject: [PATCH 9/9] allow "towards #..." --- .github/workflows/bouncer.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/bouncer.yml b/.github/workflows/bouncer.yml index 6ad7686a1..3211459de 100644 --- a/.github/workflows/bouncer.yml +++ b/.github/workflows/bouncer.yml @@ -211,7 +211,7 @@ jobs: script: | const maxIssuesAllowed = Number(process.env.MAX_ISSUES_PER_PR ?? '3'); const body = context.payload.pull_request.body || ''; - const closePatterns = /\b(?:close[sd]?|fixes|fixed|resolve[sd]?)\s+(?:https?:\/\/github\.com\/|[a-z0-9\-\_\/]*#\d+)/gi; + const closePatterns = /\b(?:close[sd]?|fixes|fixed|resolve[sd]?|towards)\s+(?:https?:\/\/github\.com\/|[a-z0-9\-\_\/]*#\d+)/gi; const issueCount = [...body.matchAll(closePatterns)].length; if (issueCount > maxIssuesAllowed) { core.setFailed(`This pull request attempts to close ${issueCount} issues, while the maximum number allowed is ${maxIssuesAllowed}.`);