// Shared by the assessment's preparation and its workflow_run completion hook. const subjectQuery = `query($id: ID!) { node(id: $id) { id __typename ... on Reactable { reactionGroups { content viewerHasReacted } } ... on Issue { repository { nameWithOwner } } ... on Discussion { repository { nameWithOwner } } ... on IssueComment { updatedAt repository { nameWithOwner } } ... on DiscussionComment { updatedAt discussion { repository { nameWithOwner } } } } }`; async function readSubject(github, context, target) { if (typeof target?.subjectId !== "string" || !target.subjectId) { throw new Error("An assessment subject is required"); } const { node } = await github.graphql(subjectQuery, { id: target.subjectId }); if (!node) return null; // A deleted comment no longer needs a reaction. const repository = node.repository || node.discussion?.repository; if (!["Issue", "Discussion", "IssueComment", "DiscussionComment"].includes(node.__typename) || repository?.nameWithOwner !== `${context.repo.owner}/${context.repo.repo}`) { throw new Error("The assessment subject must belong to this repository"); } if (node.__typename.endsWith("Comment") && (!target.updatedAt || Date.parse(node.updatedAt) !== Date.parse(target.updatedAt))) { return null; // An edit has its own assessment. Do not mark an older version. } return node; } async function prepare({ github, context }) { const { issue, discussion, comment, sender } = context.payload; const isBot = user => user?.type === "Bot" || user?.login?.endsWith("[bot]"); // A maintainer closing or discussing an alert must not trigger another // failed assessment and another alert, even though their comment is human. const isWorkflowFailure = item => isBot(item?.user) && (item.title?.startsWith('[aw] Failed jobs:') || item.body?.includes('