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

feat(update-issues): Process PRs as well as issues #25

Merged
merged 3 commits into from
Jul 24, 2023
Merged
Show file tree
Hide file tree
Changes from 1 commit
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
4 changes: 3 additions & 1 deletion update-issues/issues.js
Original file line number Diff line number Diff line change
Expand Up @@ -307,6 +307,8 @@ class Issue extends GitHubObject {
this.milestone = obj.milestone ? new Milestone(obj.milestone) : null;
/** @type {boolean} */
this.isPR = !!obj.pull_request;
/** @type {boolean} */
this.merged = obj.merged_at != null;
}

/**
Expand Down Expand Up @@ -527,7 +529,7 @@ class Issue extends GitHubObject {
octokit.rest.issues.listForRepo, Issue, {
state: 'all',
});
return all.filter(issue => !issue.isPR);
return all;
}
}

Expand Down
55 changes: 35 additions & 20 deletions update-issues/main.js
Original file line number Diff line number Diff line change
Expand Up @@ -60,18 +60,22 @@ const PING_QUESTION_TEXT =
'Does this answer all your questions? ' +
'If so, would you please close the issue?';

const CLOSE_STALE_TEXT =
const CLOSE_STALE_ISSUE_TEXT =
'Closing due to inactivity. If this is still an issue for you or if you ' +
'have further questions, the OP can ask shaka-bot to reopen it by ' +
'including `@shaka-bot reopen` in a comment.';

const CLOSE_STALE_PR_TEXT =
'Closing due to inactivity. If the author would like to continue this ' +
'PR, they can reopen it (preferred) or start a new one (if needed).';

const PING_INACTIVE_QUESTION_DAYS = 4;
const CLOSE_AFTER_WAITING_DAYS = 7;
const ARCHIVE_AFTER_CLOSED_DAYS = 60;


async function archiveOldIssues(issue) {
// If the issue has been closed for a while, archive it.
async function archiveOldIssuesAndPRs(issue) {
// If the issue or PR has been closed for a while, archive it.
// Exclude locked issues, so that this doesn't conflict with unarchiveIssues
// below.
if (!issue.locked && issue.closed &&
Expand All @@ -81,17 +85,21 @@ async function archiveOldIssues(issue) {
}
}

async function unarchiveIssues(issue) {
// If the archive label is removed from an archived issue, unarchive it.
async function unarchiveIssuesAndPRs(issue) {
joeyparrish marked this conversation as resolved.
Show resolved Hide resolved
// If the archive label is removed from an archived issue or PR, unarchive it.
if (issue.locked && !issue.hasLabel(STATUS_ARCHIVED)) {
await issue.unlock();
await issue.reopen();
// If it's not a PR, reopen it. Unlike issues, PRs cannot always be
// reopened automatically.
if (!issue.isPR) {
await issue.reopen();
}
}
}

async function reopenIssues(issue) {
// If the original author wants an issue reopened, reopen it.
if (issue.closed && !issue.hasLabel(STATUS_ARCHIVED)) {
if (!issue.isPR && issue.closed && !issue.hasLabel(STATUS_ARCHIVED)) {
// Important: only load comments if prior filters pass!
// If we loaded them on every issue, we could exceed our query quota!
await issue.loadComments();
Expand All @@ -110,8 +118,8 @@ async function reopenIssues(issue) {
}
}

async function manageWaitingIssues(issue) {
// Filter for waiting issues.
async function manageWaitingIssuesAndPRs(issue) {
// Filter for waiting issues and PRs.
if (!issue.closed && issue.hasLabel(STATUS_WAITING)) {
const labelAgeInDays = await issue.getLabelAgeInDays(STATUS_WAITING);

Expand All @@ -129,22 +137,29 @@ async function manageWaitingIssues(issue) {

// If an issue has been in a waiting state for too long, close it as stale.
if (labelAgeInDays >= CLOSE_AFTER_WAITING_DAYS) {
await issue.postComment(CLOSE_STALE_TEXT);
// PRs and issues get slightly different messages because reopening them
// works differently.
if (issue.isPR) {
await issue.postComment(CLOSE_STALE_PR_TEXT);
} else {
await issue.postComment(CLOSE_STALE_ISSUE_TEXT);
}

await issue.close();
}
}
}

async function cleanUpIssueTags(issue) {
// If an issue with the waiting tag was closed, remove the tag.
async function cleanUpIssueAndPRTags(issue) {
// If an issue or PR with the waiting tag was closed, remove the tag.
if (issue.closed && issue.hasLabel(STATUS_WAITING)) {
await issue.removeLabel(STATUS_WAITING);
}
}

async function pingQuestions(issue) {
// If a question hasn't been responded to recently, ping it.
if (!issue.closed &&
if (!issue.isPR && !issue.closed &&
issue.hasLabel(TYPE_QUESTION) &&
!issue.hasLabel(STATUS_WAITING)) {
// Important: only load comments if prior filters pass!
Expand All @@ -165,9 +180,9 @@ async function pingQuestions(issue) {
}
}

async function maintainMilestones(issue, nextMilestone, backlog) {
async function maintainIssueMilestones(issue, nextMilestone, backlog) {
// Set or remove milestones based on type labels.
if (!issue.closed) {
if (!issue.isPR && !issue.closed) {
if (issue.hasAnyLabel(LABELS_FOR_NEXT_MILESTONE)) {
if (!issue.milestone) {
// Some (low) priority flags will indicate that an issue should go to
Expand All @@ -193,12 +208,12 @@ async function maintainMilestones(issue, nextMilestone, backlog) {

const ALL_ISSUE_TASKS = [
reopenIssues,
archiveOldIssues,
unarchiveIssues,
manageWaitingIssues,
cleanUpIssueTags,
archiveOldIssuesAndPRs,
unarchiveIssuesAndPRs,
manageWaitingIssuesAndPRs,
cleanUpIssueAndPRTags,
pingQuestions,
maintainMilestones,
maintainIssueMilestones,
];

async function processIssues(issues, nextMilestone, backlog) {
joeyparrish marked this conversation as resolved.
Show resolved Hide resolved
Expand Down
11 changes: 11 additions & 0 deletions update-issues/mocks.js
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,8 @@ class MockIssue extends MockGitHubObject {
locked: false,
milestone: null,
comments: [],
isPR: false,
merged: false,
};

super(defaults, params);
Expand All @@ -83,32 +85,41 @@ class MockIssue extends MockGitHubObject {
.and.returnValue(params.labelAgeInDays || 0);
this.addLabel = jasmine.createSpy('addLabel').and.callFake((name) => {
console.log(`Adding label ${name}`);
this.labels.push(name);
});
this.removeLabel = jasmine.createSpy('removeLabel').and.callFake((name) => {
console.log(`Removing label ${name}`);
this.labels = this.labels.filter(l => l != name);
});
this.lock = jasmine.createSpy('lock').and.callFake(() => {
console.log('Locking');
this.locked = true;
});
this.unlock = jasmine.createSpy('unlock').and.callFake(() => {
console.log('Unlocking');
this.locked = false;
});
this.close = jasmine.createSpy('close').and.callFake(() => {
console.log('Closing');
this.closed = true;
});
this.reopen = jasmine.createSpy('reopen').and.callFake(() => {
console.log('Reopening');
this.closed = false;
});
this.setMilestone =
jasmine.createSpy('setMilestone').and.callFake((milestone) => {
console.log(`Setting milestone to "${milestone.title}"`);
this.milestone = milestone;
});
this.removeMilestone =
jasmine.createSpy('removeMilestone').and.callFake(() => {
console.log('Removing milestone.');
this.milestone = null;
});
this.postComment = jasmine.createSpy('postComment').and.callFake((body) => {
console.log(`Posting comment: ${body}`);
this.comments.push(new MockComment({body}));
});
this.loadComments = jasmine.createSpy('loadComments');
}
Expand Down
Loading
Loading