chore(deps): Bump the actions group with 3 updates #32
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: Issue Linker | |
| on: | |
| pull_request_target: | |
| types: [opened, edited] | |
| permissions: | |
| contents: read | |
| concurrency: | |
| group: issue-linker-${{ github.event.pull_request.number }} | |
| cancel-in-progress: true | |
| jobs: | |
| check-links: | |
| name: Check issue links | |
| runs-on: ubuntu-latest | |
| timeout-minutes: 5 | |
| permissions: | |
| contents: read | |
| pull-requests: write | |
| issues: read | |
| steps: | |
| - uses: step-security/harden-runner@9af89fc71515a100421586dfdb3dc9c984fbf411 # v2.19.4 | |
| with: | |
| egress-policy: audit | |
| - uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0 | |
| with: | |
| script: | | |
| const body = context.payload.pull_request.body || ''; | |
| const prNumber = context.payload.pull_request.number; | |
| // Skip Dependabot PRs | |
| const author = context.payload.pull_request.user.login; | |
| if (author === 'dependabot[bot]' || author === 'renovate[bot]') return; | |
| // Find all #NNN references (exclude table column refs like |---|) | |
| const allRefs = [...body.matchAll(/(?<!\w)#(\d+)/g)] | |
| .map(m => parseInt(m[1])) | |
| .filter(n => n !== prNumber && n > 0 && n < 100000); | |
| const uniqueRefs = [...new Set(allRefs)]; | |
| if (uniqueRefs.length === 0) return; | |
| // Find references with closure keywords | |
| const closurePattern = /(?:closes?|fixes?|resolves?)\s+#(\d+)/gi; | |
| const closedRefs = new Set( | |
| [...body.matchAll(closurePattern)].map(m => parseInt(m[1])) | |
| ); | |
| // Check which bare refs are open issues in this repo | |
| const bareOpenIssues = []; | |
| for (const num of uniqueRefs) { | |
| if (closedRefs.has(num)) continue; | |
| try { | |
| const issue = await github.rest.issues.get({ | |
| owner: context.repo.owner, | |
| repo: context.repo.repo, | |
| issue_number: num | |
| }); | |
| if (issue.data.state === 'open' && !issue.data.pull_request) { | |
| bareOpenIssues.push({ number: num, title: issue.data.title }); | |
| } | |
| } catch { | |
| // Not a valid issue number or from another repo | |
| } | |
| } | |
| const marker = '<!-- issue-linker-check -->'; | |
| // Delete previous comment if no bare open issues remain | |
| const { data: comments } = await github.rest.issues.listComments({ | |
| owner: context.repo.owner, | |
| repo: context.repo.repo, | |
| issue_number: prNumber, | |
| per_page: 100 | |
| }); | |
| const existing = comments.find(c => c.body.includes(marker)); | |
| if (bareOpenIssues.length === 0) { | |
| if (existing) { | |
| await github.rest.issues.deleteComment({ | |
| owner: context.repo.owner, | |
| repo: context.repo.repo, | |
| comment_id: existing.id | |
| }); | |
| } | |
| return; | |
| } | |
| const issueList = bareOpenIssues | |
| .map(i => `| #${i.number} | ${i.title} |`) | |
| .join('\n'); | |
| const message = [ | |
| marker, | |
| '### Issue Link Check', | |
| '', | |
| 'This PR references open issues without a closure keyword', | |
| '(`Closes`, `Fixes`, or `Resolves`):', | |
| '', | |
| '| Issue | Title |', | |
| '|---|---|', | |
| issueList, | |
| '', | |
| 'If this PR fixes any of these, add `Closes #N` to the description', | |
| 'so they auto-close on merge. If the reference is informational,', | |
| 'no action needed.', | |
| ].join('\n'); | |
| if (existing) { | |
| await github.rest.issues.updateComment({ | |
| owner: context.repo.owner, | |
| repo: context.repo.repo, | |
| comment_id: existing.id, | |
| body: message | |
| }); | |
| } else { | |
| await github.rest.issues.createComment({ | |
| owner: context.repo.owner, | |
| repo: context.repo.repo, | |
| issue_number: prNumber, | |
| body: message | |
| }); | |
| } |