Skip to content

chore: update dependency nanoid@<3.3.18 to v3.3.19 #683

chore: update dependency nanoid@<3.3.18 to v3.3.19

chore: update dependency nanoid@<3.3.18 to v3.3.19 #683

Workflow file for this run

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}`,
);