-
Notifications
You must be signed in to change notification settings - Fork 12
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
✨ reconcile issue reusable workflow (#58)
Introduce a reusable workflow that can be used by Konveyor projects to: 1. look for `triage/accepted` label. Adds `needs-triage` if missing. 1. look for `kind/` label. Add `needs-kind` if missing. 1. look for `priority/` label. Add `needs-priority` if missing. When the event type is created/opened, then add it to the Planning board. This PR also introduces a reusable workflow to handle issue comments. In much the same way the kube prow `label` plugin can handle `/kind bug` or `/triage accepted` commands (and their negatives `/remove-kind bug` or `/remove-triage accepted`), this workflow attempts to handle those commands that could be put in issue comments to make triaging a little more straightforward. Fixes #57 Signed-off-by: David Zager <dzager@redhat.com>
- Loading branch information
Showing
5 changed files
with
315 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,12 @@ | ||
name: Handle Issue Comment | ||
|
||
on: | ||
issue_comment: | ||
|
||
jobs: | ||
reconcile-issue-comment: | ||
permissions: | ||
contents: write | ||
issues: write | ||
pull-requests: write | ||
uses: ./.github/workflows/reconcile-issue-comment.yaml |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,25 @@ | ||
name: Reconcile GitHub Issue | ||
|
||
on: | ||
issues: | ||
types: | ||
- opened | ||
- edited | ||
- closed | ||
- reopened | ||
- labeled | ||
- unlabeled | ||
|
||
# This prevents potential race conditions by only allowing this action to handle | ||
# one update at a time for a given issue. | ||
concurrency: | ||
group: reconcile-issue-${{ github.event.issue.number }} | ||
cancel-in-progress: true | ||
|
||
jobs: | ||
reconcile-issue: | ||
permissions: | ||
contents: write | ||
issues: write | ||
pull-requests: write | ||
uses: ./.github/workflows/reconcile-issue.yaml |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,116 @@ | ||
name: Reusable Reconcile Issue Comment | ||
|
||
on: | ||
workflow_call: | ||
|
||
jobs: | ||
event_type: | ||
name: Check event type | ||
runs-on: ubuntu-latest | ||
if: > | ||
github.event_name == 'issue_comment' && | ||
( | ||
github.event.action == 'created' || | ||
github.event.action == 'edited' | ||
) | ||
steps: | ||
- name: ok | ||
run: /bin/true | ||
|
||
addLabels: | ||
needs: event_type | ||
name: Add Labels | ||
runs-on: ubuntu-latest | ||
steps: | ||
- name: ok | ||
uses: actions/github-script@v7 | ||
with: | ||
script: | | ||
const commentRegex = /(<!--.*?-->)|(<!--[\S\s]+?-->)|(<!--[\S\s]*?$)/g; | ||
const labelRegex = /^(\/(area|kind|priority|sig|triage|wg))\s*(.*?)\s*$/gm; | ||
const removeLabelRegex = /^\/remove-(area|committee|kind|language|priority|sig|triage|wg)\s*(.*?)\s*$/gm; | ||
const labelsToAdd = []; | ||
const labelsToRemove = []; | ||
let match; | ||
const { data: comment } = await github.rest.issues.getComment({ | ||
owner: context.repo.owner, | ||
repo: context.repo.repo, | ||
comment_id: context.payload.comment.id, | ||
}); | ||
const bodyWithoutComments = comment.body.replaceAll(commentRegex, ''); | ||
while ((match = labelRegex.exec(bodyWithoutComments)) !== null) { | ||
const keyword = match[2]; | ||
const text = match[3]; | ||
const labelToAdd = `${keyword}/${text}`; | ||
const labelExists = await github.rest.issues.getLabel({ | ||
owner: context.repo.owner, | ||
repo: context.repo.repo, | ||
name: labelToAdd, | ||
}).catch(() => false); | ||
if (labelExists) { | ||
labelsToAdd.push(labelToAdd); | ||
} else { | ||
console.log(`label ${labelToAdd} does not exist on this repo`); | ||
} | ||
} | ||
console.log(labelsToAdd); | ||
while ((match = removeLabelRegex.exec(bodyWithoutComments)) !== null) { | ||
const keyword = match[1]; | ||
const text = match[2]; | ||
const labelToRemove = `${keyword}/${text}`; | ||
labelsToRemove.push(labelToRemove); | ||
} | ||
console.log(labelsToRemove); | ||
if (labelsToAdd.length == 0 && labelsToRemove.length == 0) { | ||
console.log("Nothing to do!"); | ||
return; | ||
} | ||
// If we make it this far, we should verify the commenter is at | ||
// least a collaborator. | ||
// TODO(djzager): Is this enough? | ||
const commenterLogin = comment.user.login; | ||
try { | ||
await github.rest.repos.checkCollaborator({ | ||
owner: context.repo.owner, | ||
repo: context.repo.repo, | ||
username: commenterLogin, | ||
}); | ||
console.log(`Commenter ${commenterLogin} is a collaborator.`); | ||
} catch (error) { | ||
console.log(`Commenter ${commenterLogin} is not a collaborator.`); | ||
await github.rest.issues.createComment({ | ||
issue_number: context.issue.number, | ||
owner: context.repo.owner, | ||
repo: context.repo.repo, | ||
body: "Only collaborators can add/remove labels", | ||
}); | ||
return; | ||
} | ||
// Add the labels | ||
if (labelsToAdd.length > 0) { | ||
await github.rest.issues.addLabels({ | ||
issue_number: context.issue.number, | ||
owner: context.repo.owner, | ||
repo: context.repo.repo, | ||
labels: labelsToAdd | ||
}); | ||
} | ||
// Remove the labels | ||
if (labelsToRemove.length > 0) { | ||
for (const labelToRemove of labelsToRemove) { | ||
await github.rest.issues.removeLabel({ | ||
issue_number: context.issue.number, | ||
owner: context.repo.owner, | ||
repo: context.repo.repo, | ||
name: labelToRemove | ||
}); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,156 @@ | ||
name: Reusable Reconcile Issue | ||
|
||
on: | ||
workflow_call: | ||
|
||
jobs: | ||
event_type: | ||
name: Check event type | ||
runs-on: ubuntu-latest | ||
if: github.event_name == 'issues' || github.event_name == 'pull_request' | ||
steps: | ||
- name: ok | ||
run: /bin/true | ||
|
||
labels: | ||
needs: event_type | ||
name: Check labels | ||
runs-on: ubuntu-latest | ||
if: github.event.action != 'closed' | ||
steps: | ||
- name: triage | ||
uses: actions/github-script@v7 | ||
with: | ||
script: | | ||
// begin helper function for adding comments | ||
async function ensureComment(body, shouldExist) { | ||
// Try to find the comment | ||
for await (const { data: comments } of github.paginate.iterator( | ||
github.rest.issues.listComments, | ||
{ | ||
owner: context.repo.owner, | ||
repo: context.repo.repo, | ||
issue_number: context.issue.number, | ||
} | ||
)) { | ||
const comment = comments.find( | ||
(comment) => | ||
comment.user && | ||
comment.user.login == "github-actions[bot]" && | ||
comment.body && | ||
comment.body.includes(body) | ||
); | ||
// If we find the comment...we are done | ||
if (comment) { | ||
if (!shouldExist) { | ||
await github.rest.issues.deleteComment({ | ||
owner: context.repo.owner, | ||
repo: context.repo.repo, | ||
comment_id: comment.id, | ||
}); | ||
} | ||
return; | ||
} | ||
} | ||
// Create the comment if it doesn't exist | ||
if (shouldExist) { | ||
await github.rest.issues.createComment({ | ||
issue_number: context.issue.number, | ||
owner: context.repo.owner, | ||
repo: context.repo.repo, | ||
body: body, | ||
}); | ||
} | ||
} | ||
// end helper function for adding comments | ||
const { data: issue } = await github.rest.issues.get({ | ||
issue_number: context.issue.number, | ||
owner: context.repo.owner, | ||
repo: context.repo.repo, | ||
}); | ||
const labels = issue.labels.map(label => label.name); | ||
console.log(`Labels: ${labels}`); | ||
// triage label | ||
if (labels.some(label => label.match('^triage/accepted$'))) { | ||
if (labels.includes('needs-triage')) { | ||
await github.rest.issues.removeLabel({ | ||
issue_number: context.issue.number, | ||
owner: context.repo.owner, | ||
repo: context.repo.repo, | ||
name: 'needs-triage' | ||
}); | ||
} | ||
} else { | ||
let comment = "This issue is currently awaiting triage.\n"; | ||
comment += "If contributors determine this is a relevant issue, they will accept it by applying the `triage/accepted` label and provide further guidance.\n"; | ||
comment += "The `triage/accepted` label can be added by org members."; | ||
await github.rest.issues.addLabels({ | ||
issue_number: context.issue.number, | ||
owner: context.repo.owner, | ||
repo: context.repo.repo, | ||
labels: ['needs-triage'] | ||
}); | ||
await ensureComment(comment, true); | ||
} | ||
// kind label | ||
if (labels.some(label => label.match('^kind/'))) { | ||
if (labels.includes('needs-kind')) { | ||
await github.rest.issues.removeLabel({ | ||
issue_number: context.issue.number, | ||
owner: context.repo.owner, | ||
repo: context.repo.repo, | ||
name: 'needs-kind' | ||
}); | ||
} | ||
} else { | ||
await github.rest.issues.addLabels({ | ||
issue_number: context.issue.number, | ||
owner: context.repo.owner, | ||
repo: context.repo.repo, | ||
labels: ['needs-kind'] | ||
}); | ||
} | ||
// priority label | ||
if (labels.some(label => label.match('^priority/'))) { | ||
if (labels.includes('needs-priority')) { | ||
await github.rest.issues.removeLabel({ | ||
issue_number: context.issue.number, | ||
owner: context.repo.owner, | ||
repo: context.repo.repo, | ||
name: 'needs-priority' | ||
}); | ||
} | ||
} else { | ||
await github.rest.issues.addLabels({ | ||
issue_number: context.issue.number, | ||
owner: context.repo.owner, | ||
repo: context.repo.repo, | ||
labels: ['needs-priority'] | ||
}); | ||
} | ||
// good first issue | ||
let firstIssueComment = "This issue has been marked 'good first issue'\n"; | ||
firstIssueComment += "Please, make sure it aligns with the criteria found [here](https://contribute.cncf.io/maintainers/templates/issue-labels/#good-first-issue)\n"; | ||
if (labels.some(label => label.match('^good first issue$'))) { | ||
await ensureComment(firstIssueComment, true); | ||
} else { | ||
await ensureComment(firstIssueComment, false); | ||
} | ||
project: | ||
needs: event_type | ||
name: Add new issues to planning project | ||
runs-on: ubuntu-latest | ||
if: github.event.action == 'opened' | ||
steps: | ||
- uses: actions/add-to-project@v0.5.0 | ||
with: | ||
project-url: https://github.com/orgs/konveyor/projects/67 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters