Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .github/workflows/auto-assign-issues.yml
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@ jobs:

- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: 22 # need this version for `Set` methods

- name: Install pnpm
uses: pnpm/action-setup@v4
Expand Down
84 changes: 69 additions & 15 deletions tools/github-workflow-helpers/auto-assign-issues.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,47 @@ import type { IssuesLabeledEvent } from "@octokit/webhooks-types";
* The mapping of github issue labels to team members for assignment.
*/
const TEAM_ASSIGNMENTS: { [label: string]: { [jobRole: string]: string } } = {
miniflare: { em: "lrapoport", pm: "mikenomitch" },
agents: {},
"analytics engine": {},
api: {},
auth: {},
"auto-provisioning": { em: "lrapoport-cf", pm: "mattietk" },
c3: { em: "lrapoport-cf", pm: "mattietk" },
constellation: {},
containers: {},
d1: {},
documentation: {},
"durable objects": {},
hyperdrive: {},
"kv-asset-handler": { em: "lrapoport-cf", pm: "mattietk" },
miniflare: { em: "lrapoport-cf", pm: "mattietk" },
multiworker: { em: "lrapoport-cf", pm: "mattietk" },
"node compat": { em: "lrapoport-cf", pm: "mattietk" },
"nodejs compat": { em: "lrapoport-cf", pm: "mattietk" },
observability: {},
opennext: { em: "lrapoport-cf", pm: "mattietk", tl: "vicb" },
pages: { em: "lrapoport-cf", pm: "mattietk" },
"pages-shared": {},
pipelines: {},
"playground-worker": { em: "lrapoport-cf", pm: "mattietk" },
python: {},
queues: {},
r2: {},
"remote-bindings": { em: "lrapoport-cf", pm: "mattietk" },
"secrets-store": {},
"start-dev-worker": { em: "lrapoport-cf", pm: "mattietk" },
templates: { em: "lrapoport-cf", pm: "mattietk" },
types: { em: "lrapoport-cf", pm: "mattietk" },
unenv: { em: "lrapoport-cf", pm: "mattietk" },
unstable_dev: { em: "lrapoport-cf", pm: "mattietk" },
vectorize: {},
"vite-plugin": { em: "lrapoport-cf", pm: "mattietk", tl: "jamesopstad" },
vitest: { em: "lrapoport-cf", pm: "mattietk" },
"Workers + Assets": {},
"workers ai": {},
"workers-builds": {},
workflows: {},
wrangler: { em: "lrapoport-cf", pm: "mattietk" },
};

if (require.main === module) {
Expand All @@ -23,9 +63,10 @@ async function run() {
`);

if (isIssuesLabeledEvent(context.payload)) {
const issue = context.payload.issue;
const label = context.payload.label;
const labelName = label.name;
const {
issue,
label: { name: labelName },
} = context.payload;

core.info(`Processing new label: ${labelName} for issue #${issue.number}`);

Expand All @@ -35,31 +76,44 @@ async function run() {
return;
}

const assignees = new Set(Object.values(teamConfig));
for (const assignee of issue.assignees ?? []) {
assignees.delete(assignee.login);
}
if (assignees.size === 0) {
const teamAssignees = new Set(Object.values(teamConfig));
const currentAssignees = new Set(issue.assignees?.map((a) => a.login));
const toBeAssigned = teamAssignees.difference(currentAssignees);

if (toBeAssigned.size === 0) {
core.info(
`All potential assignees are already assigned to issue #${issue.number}. Skipping auto-assignment.`
`All potential assignees are already assigned to issue. Skipping auto-assignment.`
);
return;
}

const assigneeList = new Intl.ListFormat("en").format(assignees);
core.info(`Assigning issue #${issue.number} to: ${assigneeList}`);
core.info(
`Assigning to: ${new Intl.ListFormat("en").format(toBeAssigned)}`
);

const token = core.getInput("github_token", { required: true });
const octokit = getOctokit(token);
await octokit.rest.issues.addAssignees({
const result = await octokit.rest.issues.addAssignees({
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: issue.number,
assignees: Array.from(assignees),
assignees: Array.from(toBeAssigned),
});

const assigned = new Set(result.data.assignees?.map((a) => a.login));
const missing = toBeAssigned.difference(assigned);

if (missing.size > 0) {
core.warning(
dedent`
Not all assignees were added to the issue. They may not be collaborators on the repository.
Missing assignees: ${new Intl.ListFormat("en").format(missing)}
`
);
}

core.info(
`Successfully assigned issue #${issue.number} to ${assigneeList}`
`Issue assigned to ${new Intl.ListFormat("en").format(assigned)}`
);
}
}
Expand Down
Loading