Serenity Operating System
at master 145 lines 5.4 kB view raw
1/* 2 * Copyright (c) 2022, Luke Wilde <lukew@serenityos.org> 3 * Copyright (c) 2023, kleines Filmröllchen <filmroellchen@serenityos.org> 4 * 5 * SPDX-License-Identifier: BSD-2-Clause 6 */ 7 8const Label = { 9 CommunityApproved: "✅ pr-community-approved", 10 HasConflicts: "⚠️ pr-has-conflicts", 11 IsBlocked: "⛔️ pr-is-blocked", 12 MaintainerApprovedButAwaitingCi: "✅ pr-maintainer-approved-but-awaiting-ci", 13 NeedsReview: "👀 pr-needs-review", 14 Unclear: "🤔 pr-unclear", 15 WaitingForAuthor: "⏳ pr-waiting-for-author", 16}; 17 18const subjectiveLabels = [Label.IsBlocked, Label.Unclear]; 19 20function removeExistingPrLabels(currentLabels, keepSubjectiveLabels) { 21 return currentLabels.filter( 22 label => 23 !label.includes("pr-") || 24 label === Label.HasConflicts || 25 (keepSubjectiveLabels && subjectiveLabels.includes(label)) 26 ); 27} 28 29async function labelsForGenericPullRequestChange(currentLabels) { 30 const filteredLabels = removeExistingPrLabels(currentLabels, true); 31 filteredLabels.push(Label.NeedsReview); 32 return filteredLabels; 33} 34 35async function labelsForPullRequestEffectivelyClosed(currentLabels) { 36 return removeExistingPrLabels(currentLabels, false); 37} 38 39function apiErrorHandler(error) { 40 console.log( 41 "::warning::Encountered error during event handling, not updating labels. Error:", 42 error 43 ); 44} 45 46module.exports = ({ github, context }) => { 47 async function labelsForPullRequestReviewSubmitted(currentLabels, { pull_request, review }) { 48 let newLabels = currentLabels; 49 const isBlocked = currentLabels.some(label => label === Label.IsBlocked); 50 51 if (review.state.toLowerCase() === "approved") { 52 const maintainers = ( 53 await github.rest.teams.listMembersInOrg({ 54 org: "SerenityOS", 55 team_slug: "maintainers", 56 }) 57 ).data; 58 59 const approvedByMaintainer = maintainers.some( 60 maintainerInArray => maintainerInArray.login === review.user.login 61 ); 62 63 if (approvedByMaintainer) { 64 newLabels = newLabels.filter( 65 label => !(label === Label.NeedsReview || label === Label.WaitingForAuthor) 66 ); 67 68 if (!newLabels.includes(Label.MaintainerApprovedButAwaitingCi)) 69 newLabels.push(Label.MaintainerApprovedButAwaitingCi); 70 } else { 71 if (!newLabels.includes(Label.CommunityApproved)) 72 newLabels.push(Label.CommunityApproved); 73 } 74 } else if (!isBlocked) { 75 // Remove approval labels. 76 newLabels = newLabels.filter( 77 label => 78 !( 79 label === Label.CommunityApproved || 80 label === Label.MaintainerApprovedButAwaitingCi 81 ) 82 ); 83 84 if (review.user.login === pull_request.user.login) newLabels.push(Label.NeedsReview); 85 else newLabels.push(Label.WaitingForAuthor); 86 } 87 88 return newLabels; 89 } 90 91 const eventHandlers = { 92 opened: labelsForGenericPullRequestChange, 93 reopened: labelsForGenericPullRequestChange, 94 submitted: labelsForPullRequestReviewSubmitted, 95 dismissed: labelsForGenericPullRequestChange, 96 converted_to_draft: labelsForPullRequestEffectivelyClosed, 97 ready_for_review: labelsForGenericPullRequestChange, 98 synchronize: labelsForGenericPullRequestChange, // synchronize is triggered when the branch is changed 99 edited: labelsForGenericPullRequestChange, 100 review_requested: labelsForGenericPullRequestChange, 101 closed: labelsForPullRequestEffectivelyClosed, 102 }; 103 104 const eventName = context.payload.action; 105 const handlerForCurrentEvent = eventHandlers[eventName]; 106 107 async function updateLabels(currentLabelsAsObjects) { 108 const currentLabels = []; 109 currentLabelsAsObjects.forEach(labelObject => currentLabels.push(labelObject.name)); 110 111 const isEffectivelyClosed = 112 context.payload.pull_request.draft || 113 context.payload.pull_request.state.toLowerCase() === "closed"; 114 115 const newLabels = await (isEffectivelyClosed 116 ? labelsForPullRequestEffectivelyClosed(currentLabels, context.payload) 117 : handlerForCurrentEvent(currentLabels, context.payload)); 118 119 console.log( 120 `Received '${eventName}' event for ${ 121 isEffectivelyClosed ? "draft/closed" : "open" 122 } pull request, changing labels from '${currentLabels}' to '${newLabels}'` 123 ); 124 125 return github.rest.issues.setLabels({ 126 issue_number: context.payload.pull_request.number, 127 owner: context.repo.owner, 128 repo: context.repo.repo, 129 labels: newLabels, 130 }); 131 } 132 133 if (handlerForCurrentEvent) { 134 github.rest.issues 135 .listLabelsOnIssue({ 136 issue_number: context.payload.pull_request.number, 137 owner: context.repo.owner, 138 repo: context.repo.repo, 139 }) 140 .then(result => updateLabels(result.data)) 141 .catch(apiErrorHandler); 142 } else { 143 console.log(`::warning::No handler for the '${eventName}' event, not updating labels.`); 144 } 145};