Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

✨ reconcile issue reusable workflow #58

Merged
merged 1 commit into from
Feb 5, 2024
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
12 changes: 12 additions & 0 deletions .github/workflows/_issue_comment.yaml
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
25 changes: 25 additions & 0 deletions .github/workflows/_issues.yaml
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
116 changes: 116 additions & 0 deletions .github/workflows/reconcile-issue-comment.yaml
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
});
}
}
156 changes: 156 additions & 0 deletions .github/workflows/reconcile-issue.yaml
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
Copy link
Contributor

@mateusoliveira43 mateusoliveira43 Feb 2, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

can this be a separate file? As inline YAML, it is harder to review the logic within it, because we lose syntax highlighting (which can make hard to maintain this in the future)

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I agree. I think what I will do is create an issue to rewrite it in golang to expand those that can help us maintain/fix it when it breaks.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'll write the issue for that after this merges so I can link to the source lines where we have javascript as inline YAML.

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
6 changes: 6 additions & 0 deletions pkg/config/config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,9 @@ labels:
description: Indicates an issue that is a support question.
name: triage/support
# Kind
- color: ededed
description: Indicates an issue or PR lacks a `kind/foo` label and requires one.
name: needs-kind
- color: e11d21
description: Categorizes issue or PR as related to a bug.
name: kind/bug
Expand All @@ -76,6 +79,9 @@ labels:
description: Categorizes issue or PR as related to a new feature.
name: kind/feature
# Priority
- color: ededed
description: Indicates an issue or PR lacks a `priority/foo` label and requires one.
name: needs-priority
- color: fef2c0
description: Lowest priority. Possibly useful, but not yet enough support to actually get it done. # These are mostly place-holders for potentially good ideas, so that they don't get completely forgotten, and can be referenced /deduped every time they come up.
name: priority/awaiting-more-evidence
Expand Down
Loading