mirror of
https://github.com/bherbruck/solidcraft.git
synced 2026-11-04 19:15:57 +00:00
97336ccfe7
A pull request outside crates/kernel, crates/geom and vendor/truck* is no longer labelled for a kernel word in its title. Issues and discussions keep the title check. Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
92 lines
4.1 KiB
JavaScript
92 lines
4.1 KiB
JavaScript
'use strict';
|
|
// While a new geometry kernel is evaluated, new issues and discussions about kernel work, and new
|
|
// pull requests that change the kernel directories, get the `kernel-paused` label, so a maintainer
|
|
// finds them and answers in person (CONTRIBUTING.md, "Kernel work is paused"). Reports of crashes
|
|
// and data loss are left alone, and so is anyone who can push to the repository.
|
|
|
|
const LABEL = 'kernel-paused';
|
|
const LABEL_COLOR = 'b60205';
|
|
const LABEL_DESCRIPTION = 'Kernel work is paused while a new geometry kernel is evaluated';
|
|
|
|
// Kernel operations named in the title of an issue or discussion. "Hole" and "shell" are left out
|
|
// on purpose: they name UI work too. A pull request is judged by its files, never by its title.
|
|
const TOPIC =
|
|
/\b(booleans?|fillets?|chamfers?|offsets?|sweeps?|lofts?|threads?|step|stp|iges|igs|tessellat\w*|kernel|truck|b-?rep)\b/i;
|
|
// Crashes and lost work come first whatever the kernel is.
|
|
const URGENT = /\b(crash\w*|panic\w*|data loss|lost (my )?work|corrupt\w*)\b/i;
|
|
// What a pull request changes that makes it kernel work.
|
|
const KERNEL_PATHS = ['crates/kernel/', 'crates/geom/', 'vendor/truck'];
|
|
|
|
function isTopic(title) {
|
|
return TOPIC.test(title || '');
|
|
}
|
|
|
|
function isUrgent(title, body) {
|
|
return URGENT.test(`${title || ''}\n${body || ''}`);
|
|
}
|
|
|
|
function touchesKernel(files) {
|
|
return files.some((f) => KERNEL_PATHS.some((p) => f.startsWith(p)));
|
|
}
|
|
|
|
// Whether the author can push to the repository (maintainers and their agents are not paused).
|
|
async function canPush({ github, context }, username) {
|
|
try {
|
|
const { data } = await github.rest.repos.getCollaboratorPermissionLevel({ ...context.repo, username });
|
|
return ['admin', 'maintain', 'write'].includes(data.permission);
|
|
} catch {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
async function ensureLabel({ github, context }) {
|
|
try {
|
|
await github.rest.issues.getLabel({ ...context.repo, name: LABEL });
|
|
} catch (e) {
|
|
if (e.status !== 404) throw e;
|
|
await github.rest.issues.createLabel({ ...context.repo, name: LABEL, color: LABEL_COLOR, description: LABEL_DESCRIPTION });
|
|
}
|
|
}
|
|
|
|
// A new issue or pull request: returns what was done, for the log.
|
|
async function handleOpened({ github, context }) {
|
|
const item = context.payload.pull_request || context.payload.issue;
|
|
if (!item) return 'nothing to do';
|
|
const pull = Boolean(context.payload.pull_request);
|
|
if (isUrgent(item.title, item.body)) return `#${item.number}: a crash or data-loss report, left alone`;
|
|
if (await canPush({ github, context }, item.user.login)) return `#${item.number}: by a maintainer, left alone`;
|
|
let kernel;
|
|
if (pull) {
|
|
const files = await github.paginate(github.rest.pulls.listFiles, { ...context.repo, pull_number: item.number, per_page: 100 });
|
|
kernel = touchesKernel(files.map((f) => f.filename));
|
|
} else {
|
|
kernel = isTopic(item.title);
|
|
}
|
|
if (!kernel) return `#${item.number}: not kernel work`;
|
|
await ensureLabel({ github, context });
|
|
await github.rest.issues.addLabels({ ...context.repo, issue_number: item.number, labels: [LABEL] });
|
|
return `#${item.number}: labelled ${LABEL}`;
|
|
}
|
|
|
|
// A new discussion.
|
|
async function handleDiscussion({ github, context }) {
|
|
const d = context.payload.discussion;
|
|
if (!d) return 'nothing to do';
|
|
if (!isTopic(d.title) || isUrgent(d.title, d.body)) return `discussion #${d.number}: left alone`;
|
|
if (await canPush({ github, context }, d.user.login)) return `discussion #${d.number}: by a maintainer, left alone`;
|
|
await ensureLabel({ github, context });
|
|
const { repository } = await github.graphql(
|
|
'query($owner: String!, $repo: String!, $name: String!) { repository(owner: $owner, name: $repo) { label(name: $name) { id } } }',
|
|
{ ...context.repo, name: LABEL },
|
|
);
|
|
if (repository && repository.label) {
|
|
await github.graphql('mutation($id: ID!, $labels: [ID!]!) { addLabelsToLabelable(input: {labelableId: $id, labelIds: $labels}) { clientMutationId } }', {
|
|
id: d.node_id,
|
|
labels: [repository.label.id],
|
|
});
|
|
}
|
|
return `discussion #${d.number}: labelled ${LABEL}`;
|
|
}
|
|
|
|
module.exports = { LABEL, isTopic, isUrgent, touchesKernel, handleOpened, handleDiscussion };
|