chore: update dependency nanoid@<3.3.18 to v3.3.19 #683
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: PR label | |
| # The work-type label comes from the title, the size label comes from the current | |
| # diff, and good first issue comes from a linked closing issue. Each source is | |
| # observable, so none of these labels needs a hand-maintained estimate. | |
| # | |
| # pull_request_target rather than pull_request, because a pull_request run from | |
| # a fork gets a read-only token and could not label at all, which would fail | |
| # every outside contribution. Nothing here checks out or runs the pull request's | |
| # code. It reads the title out of the event payload, so the elevated token never | |
| # meets anything the author controls beyond that string. | |
| on: | |
| pull_request_target: | |
| types: [opened, edited, reopened, synchronize] | |
| permissions: | |
| contents: read | |
| issues: read | |
| pull-requests: write | |
| jobs: | |
| label: | |
| name: label from title | |
| runs-on: ubuntu-latest | |
| steps: | |
| - uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9 | |
| with: | |
| script: | | |
| // The commit types in CONTRIBUTING.md, mapped to the work-type | |
| // labels in docs/labels.md. The two lists are named differently, | |
| // since the labels predate the type table, so the mapping is | |
| // spelled out rather than assumed to be identity. | |
| const LABELS = { | |
| feat: 'enhancement', | |
| fix: 'bug', | |
| docs: 'documentation', | |
| refactor: 'refactor', | |
| test: 'test', | |
| chore: 'chore', | |
| }; | |
| const SIZE_LABELS = [ | |
| { name: 'π xs', max: 9 }, | |
| { name: 'π s', max: 49 }, | |
| { name: 'π m', max: 199 }, | |
| { name: 'π l', max: 999 }, | |
| { name: 'π xl', max: Infinity }, | |
| ]; | |
| // Fetch the pull request instead of trusting the event payload so a | |
| // synchronize event sees the latest additions and deletions. | |
| const { data: pr } = await github.rest.pulls.get({ | |
| ...context.repo, | |
| pull_number: context.payload.pull_request.number, | |
| }); | |
| // `!` marks a breaking change in Conventional Commits. Scopes are | |
| // not used here, so `feat(core):` is rejected along with anything | |
| // else that does not match the documented `<type>: <description>`. | |
| const match = /^([a-z]+)(!)?: .+/.exec(pr.title); | |
| const type = match?.[1]; | |
| const wanted = type ? LABELS[type] : undefined; | |
| if (!wanted) { | |
| core.setFailed( | |
| `The title "${pr.title}" does not start with a known commit type. ` + | |
| `Use "<type>: <description>" with one of: ${Object.keys(LABELS).join(', ')}. ` + | |
| `See the Commits section of CONTRIBUTING.md.`, | |
| ); | |
| return; | |
| } | |
| // Exactly one work-type label, so a corrected title does not leave | |
| // the old one behind. | |
| const workType = new Set(Object.values(LABELS)); | |
| const current = pr.labels.map((label) => label.name); | |
| const stale = current.filter((name) => workType.has(name) && name !== wanted); | |
| for (const name of stale) { | |
| await github.rest.issues.removeLabel({ | |
| ...context.repo, | |
| issue_number: pr.number, | |
| name, | |
| }); | |
| } | |
| if (!current.includes(wanted)) { | |
| await github.rest.issues.addLabels({ | |
| ...context.repo, | |
| issue_number: pr.number, | |
| labels: [wanted], | |
| }); | |
| } | |
| // Size is PR-only and always comes from additions + deletions. Keep | |
| // exactly one so a synchronize event replaces a stale size. | |
| const changedLines = pr.additions + pr.deletions; | |
| const wantedSize = SIZE_LABELS.find(({ max }) => changedLines <= max).name; | |
| const sizeNames = new Set(SIZE_LABELS.map(({ name }) => name)); | |
| const staleSizes = current.filter( | |
| (name) => sizeNames.has(name) && name !== wantedSize, | |
| ); | |
| for (const name of staleSizes) { | |
| await github.rest.issues.removeLabel({ | |
| ...context.repo, | |
| issue_number: pr.number, | |
| name, | |
| }); | |
| } | |
| if (!current.includes(wantedSize)) { | |
| await github.rest.issues.addLabels({ | |
| ...context.repo, | |
| issue_number: pr.number, | |
| labels: [wantedSize], | |
| }); | |
| } | |
| // GitHub resolves closingIssuesReferences from closing keywords and | |
| // linked development work. Copy good first issue only when at least | |
| // one of those issues carries it. | |
| const linked = await github.graphql( | |
| `query($owner: String!, $repo: String!, $number: Int!) { | |
| repository(owner: $owner, name: $repo) { | |
| pullRequest(number: $number) { | |
| closingIssuesReferences(first: 50) { | |
| nodes { | |
| labels(first: 100) { nodes { name } } | |
| } | |
| } | |
| } | |
| } | |
| }`, | |
| { ...context.repo, number: pr.number }, | |
| ); | |
| const closingIssues = | |
| linked.repository.pullRequest.closingIssuesReferences.nodes; | |
| const shouldBeGoodFirst = closingIssues.some((issue) => | |
| issue.labels.nodes.some((label) => label.name === 'good first issue'), | |
| ); | |
| const isGoodFirst = current.includes('good first issue'); | |
| if (shouldBeGoodFirst && !isGoodFirst) { | |
| await github.rest.issues.addLabels({ | |
| ...context.repo, | |
| issue_number: pr.number, | |
| labels: ['good first issue'], | |
| }); | |
| } else if (!shouldBeGoodFirst && isGoodFirst) { | |
| await github.rest.issues.removeLabel({ | |
| ...context.repo, | |
| issue_number: pr.number, | |
| name: 'good first issue', | |
| }); | |
| } | |
| core.info( | |
| `${pr.title} -> ${wanted}, ${wantedSize}, good first issue: ${shouldBeGoodFirst}`, | |
| ); |