Add rotten functionality
Add processRottenIssue Add Options to support rotten issues Update main.ts to process rotten related inputs Update the Option enum to include rotten related variables Update issue.ts to include function to get the rotten label Add helper functions for rotten related options
This commit is contained in:
parent
3f3b0175e8
commit
9e2995bba5
|
@ -6,18 +6,25 @@ export const DefaultProcessorOptions: IIssuesProcessorOptions = Object.freeze({
|
||||||
repoToken: 'none',
|
repoToken: 'none',
|
||||||
staleIssueMessage: 'This issue is stale',
|
staleIssueMessage: 'This issue is stale',
|
||||||
stalePrMessage: 'This PR is stale',
|
stalePrMessage: 'This PR is stale',
|
||||||
|
rottenIssueMessage: 'This issue is rotten',
|
||||||
|
rottenPrMessage: 'This PR is rotten',
|
||||||
closeIssueMessage: 'This issue is being closed',
|
closeIssueMessage: 'This issue is being closed',
|
||||||
closePrMessage: 'This PR is being closed',
|
closePrMessage: 'This PR is being closed',
|
||||||
daysBeforeStale: 1,
|
daysBeforeStale: 1,
|
||||||
|
daysBeforeRotten: 0,
|
||||||
daysBeforeIssueStale: NaN,
|
daysBeforeIssueStale: NaN,
|
||||||
daysBeforePrStale: NaN,
|
daysBeforePrStale: NaN,
|
||||||
|
daysBeforeIssueRotten: NaN,
|
||||||
|
daysBeforePrRotten: NaN,
|
||||||
daysBeforeClose: 30,
|
daysBeforeClose: 30,
|
||||||
daysBeforeIssueClose: NaN,
|
daysBeforeIssueClose: NaN,
|
||||||
daysBeforePrClose: NaN,
|
daysBeforePrClose: NaN,
|
||||||
staleIssueLabel: 'Stale',
|
staleIssueLabel: 'Stale',
|
||||||
|
rottenIssueLabel: 'Rotten',
|
||||||
closeIssueLabel: '',
|
closeIssueLabel: '',
|
||||||
exemptIssueLabels: '',
|
exemptIssueLabels: '',
|
||||||
stalePrLabel: 'Stale',
|
stalePrLabel: 'Stale',
|
||||||
|
rottenPrLabel: 'Rotten',
|
||||||
closePrLabel: '',
|
closePrLabel: '',
|
||||||
exemptPrLabels: '',
|
exemptPrLabels: '',
|
||||||
onlyLabels: '',
|
onlyLabels: '',
|
||||||
|
@ -31,6 +38,9 @@ export const DefaultProcessorOptions: IIssuesProcessorOptions = Object.freeze({
|
||||||
removeStaleWhenUpdated: false,
|
removeStaleWhenUpdated: false,
|
||||||
removeIssueStaleWhenUpdated: undefined,
|
removeIssueStaleWhenUpdated: undefined,
|
||||||
removePrStaleWhenUpdated: undefined,
|
removePrStaleWhenUpdated: undefined,
|
||||||
|
removeRottenWhenUpdated: false,
|
||||||
|
removeIssueRottenWhenUpdated: undefined,
|
||||||
|
removePrRottenWhenUpdated: undefined,
|
||||||
ascending: false,
|
ascending: false,
|
||||||
deleteBranch: false,
|
deleteBranch: false,
|
||||||
startDate: '',
|
startDate: '',
|
||||||
|
@ -50,6 +60,9 @@ export const DefaultProcessorOptions: IIssuesProcessorOptions = Object.freeze({
|
||||||
labelsToRemoveWhenStale: '',
|
labelsToRemoveWhenStale: '',
|
||||||
labelsToRemoveWhenUnstale: '',
|
labelsToRemoveWhenUnstale: '',
|
||||||
labelsToAddWhenUnstale: '',
|
labelsToAddWhenUnstale: '',
|
||||||
|
labelsToRemoveWhenRotten: '',
|
||||||
|
labelsToRemoveWhenUnrotten: '',
|
||||||
|
labelsToAddWhenUnrotten: '',
|
||||||
ignoreUpdates: false,
|
ignoreUpdates: false,
|
||||||
ignoreIssueUpdates: undefined,
|
ignoreIssueUpdates: undefined,
|
||||||
ignorePrUpdates: undefined,
|
ignorePrUpdates: undefined,
|
||||||
|
|
|
@ -159,11 +159,12 @@ test('processing an issue with no label and a start date as ECMAScript epoch in
|
||||||
});
|
});
|
||||||
|
|
||||||
test('processing an issue with no label and a start date as ISO 8601 being before the issue creation date will make it stale and close it when it is old enough and days-before-close is set to 0', async () => {
|
test('processing an issue with no label and a start date as ISO 8601 being before the issue creation date will make it stale and close it when it is old enough and days-before-close is set to 0', async () => {
|
||||||
expect.assertions(2);
|
expect.assertions(3);
|
||||||
const january2000 = '2000-01-01T00:00:00Z';
|
const january2000 = '2000-01-01T00:00:00Z';
|
||||||
const opts: IIssuesProcessorOptions = {
|
const opts: IIssuesProcessorOptions = {
|
||||||
...DefaultProcessorOptions,
|
...DefaultProcessorOptions,
|
||||||
daysBeforeClose: 0,
|
daysBeforeClose: 0,
|
||||||
|
daysBeforeRotten: 0,
|
||||||
startDate: january2000.toString()
|
startDate: january2000.toString()
|
||||||
};
|
};
|
||||||
const TestIssueList: Issue[] = [
|
const TestIssueList: Issue[] = [
|
||||||
|
@ -187,6 +188,7 @@ test('processing an issue with no label and a start date as ISO 8601 being befor
|
||||||
await processor.processIssues(1);
|
await processor.processIssues(1);
|
||||||
|
|
||||||
expect(processor.staleIssues.length).toStrictEqual(1);
|
expect(processor.staleIssues.length).toStrictEqual(1);
|
||||||
|
expect(processor.rottenIssues.length).toStrictEqual(1);
|
||||||
expect(processor.closedIssues.length).toStrictEqual(1);
|
expect(processor.closedIssues.length).toStrictEqual(1);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -222,6 +224,39 @@ test('processing an issue with no label and a start date as ISO 8601 being after
|
||||||
expect(processor.closedIssues.length).toStrictEqual(0);
|
expect(processor.closedIssues.length).toStrictEqual(0);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test('processing an issue with no label and a start date as ISO 8601 being after the issue creation date will not make it stale , rotten or close it when it is old enough and days-before-close is set to 0', async () => {
|
||||||
|
expect.assertions(3);
|
||||||
|
const january2021 = '2021-01-01T00:00:00Z';
|
||||||
|
const opts: IIssuesProcessorOptions = {
|
||||||
|
...DefaultProcessorOptions,
|
||||||
|
daysBeforeClose: 0,
|
||||||
|
startDate: january2021.toString()
|
||||||
|
};
|
||||||
|
const TestIssueList: Issue[] = [
|
||||||
|
generateIssue(
|
||||||
|
opts,
|
||||||
|
1,
|
||||||
|
'An issue with no label',
|
||||||
|
'2020-01-01T17:00:00Z',
|
||||||
|
'2020-01-01T17:00:00Z'
|
||||||
|
)
|
||||||
|
];
|
||||||
|
const processor = new IssuesProcessorMock(
|
||||||
|
opts,
|
||||||
|
alwaysFalseStateMock,
|
||||||
|
async p => (p === 1 ? TestIssueList : []),
|
||||||
|
async () => [],
|
||||||
|
async () => new Date().toDateString()
|
||||||
|
);
|
||||||
|
|
||||||
|
// process our fake issue list
|
||||||
|
await processor.processIssues(1);
|
||||||
|
|
||||||
|
expect(processor.staleIssues.length).toStrictEqual(0);
|
||||||
|
expect(processor.rottenIssues.length).toStrictEqual(0);
|
||||||
|
expect(processor.closedIssues.length).toStrictEqual(0);
|
||||||
|
});
|
||||||
|
|
||||||
test('processing an issue with no label and a start date as RFC 2822 being before the issue creation date will make it stale and close it when it is old enough and days-before-close is set to 0', async () => {
|
test('processing an issue with no label and a start date as RFC 2822 being before the issue creation date will make it stale and close it when it is old enough and days-before-close is set to 0', async () => {
|
||||||
expect.assertions(2);
|
expect.assertions(2);
|
||||||
const january2000 = 'January 1, 2000 00:00:00';
|
const january2000 = 'January 1, 2000 00:00:00';
|
||||||
|
@ -290,6 +325,7 @@ test('processing an issue with no label will make it stale and close it, if it i
|
||||||
const opts: IIssuesProcessorOptions = {
|
const opts: IIssuesProcessorOptions = {
|
||||||
...DefaultProcessorOptions,
|
...DefaultProcessorOptions,
|
||||||
daysBeforeClose: 1,
|
daysBeforeClose: 1,
|
||||||
|
daysBeforeRotten: 0,
|
||||||
daysBeforeIssueClose: 0
|
daysBeforeIssueClose: 0
|
||||||
};
|
};
|
||||||
const TestIssueList: Issue[] = [
|
const TestIssueList: Issue[] = [
|
||||||
|
@ -307,6 +343,7 @@ test('processing an issue with no label will make it stale and close it, if it i
|
||||||
await processor.processIssues(1);
|
await processor.processIssues(1);
|
||||||
|
|
||||||
expect(processor.staleIssues).toHaveLength(1);
|
expect(processor.staleIssues).toHaveLength(1);
|
||||||
|
expect(processor.rottenIssues).toHaveLength(1);
|
||||||
expect(processor.closedIssues).toHaveLength(1);
|
expect(processor.closedIssues).toHaveLength(1);
|
||||||
expect(processor.deletedBranchIssues).toHaveLength(0);
|
expect(processor.deletedBranchIssues).toHaveLength(0);
|
||||||
});
|
});
|
||||||
|
@ -488,6 +525,7 @@ test('processing a stale issue will close it', async () => {
|
||||||
await processor.processIssues(1);
|
await processor.processIssues(1);
|
||||||
|
|
||||||
expect(processor.staleIssues).toHaveLength(0);
|
expect(processor.staleIssues).toHaveLength(0);
|
||||||
|
expect(processor.rottenIssues).toHaveLength(1);
|
||||||
expect(processor.closedIssues).toHaveLength(1);
|
expect(processor.closedIssues).toHaveLength(1);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
56
action.yml
56
action.yml
|
@ -1,6 +1,6 @@
|
||||||
name: 'Close Stale Issues'
|
name: 'Close, Rotten and Stale Issues'
|
||||||
description: 'Close issues and pull requests with no recent activity'
|
description: 'Close issues and pull requests with no recent activity'
|
||||||
author: 'GitHub'
|
author: 'M Viswanath Sai'
|
||||||
inputs:
|
inputs:
|
||||||
repo-token:
|
repo-token:
|
||||||
description: 'Token for the repository. Can be passed in using `{{ secrets.GITHUB_TOKEN }}`.'
|
description: 'Token for the repository. Can be passed in using `{{ secrets.GITHUB_TOKEN }}`.'
|
||||||
|
@ -12,6 +12,12 @@ inputs:
|
||||||
stale-pr-message:
|
stale-pr-message:
|
||||||
description: 'The message to post on the pull request when tagging it. If none provided, will not mark pull requests stale.'
|
description: 'The message to post on the pull request when tagging it. If none provided, will not mark pull requests stale.'
|
||||||
required: false
|
required: false
|
||||||
|
rotten-issue-message:
|
||||||
|
description: 'The message to post on the issue when tagging it. If none provided, will not mark issues rotten.'
|
||||||
|
required: false
|
||||||
|
rotten-pr-message:
|
||||||
|
description: 'The message to post on the pull request when tagging it. If none provided, will not mark pull requests rotten.'
|
||||||
|
required: false
|
||||||
close-issue-message:
|
close-issue-message:
|
||||||
description: 'The message to post on the issue when closing it. If none provided, will not comment when closing an issue.'
|
description: 'The message to post on the issue when closing it. If none provided, will not comment when closing an issue.'
|
||||||
required: false
|
required: false
|
||||||
|
@ -21,17 +27,27 @@ inputs:
|
||||||
days-before-stale:
|
days-before-stale:
|
||||||
description: 'The number of days old an issue or a pull request can be before marking it stale. Set to -1 to never mark issues or pull requests as stale automatically.'
|
description: 'The number of days old an issue or a pull request can be before marking it stale. Set to -1 to never mark issues or pull requests as stale automatically.'
|
||||||
required: false
|
required: false
|
||||||
default: '60'
|
default: '90'
|
||||||
days-before-issue-stale:
|
days-before-issue-stale:
|
||||||
description: 'The number of days old an issue can be before marking it stale. Set to -1 to never mark issues as stale automatically. Override "days-before-stale" option regarding only the issues.'
|
description: 'The number of days old an issue can be before marking it stale. Set to -1 to never mark issues as stale automatically. Override "days-before-stale" option regarding only the issues.'
|
||||||
required: false
|
required: false
|
||||||
days-before-pr-stale:
|
days-before-pr-stale:
|
||||||
description: 'The number of days old a pull request can be before marking it stale. Set to -1 to never mark pull requests as stale automatically. Override "days-before-stale" option regarding only the pull requests.'
|
description: 'The number of days old a pull request can be before marking it stale. Set to -1 to never mark pull requests as stale automatically. Override "days-before-stale" option regarding only the pull requests.'
|
||||||
required: false
|
required: false
|
||||||
|
days-before-rotten:
|
||||||
|
description: 'The number of days old an issue or a pull request can be before marking it rotten. Set to -1 to never mark issues or pull requests as rotten automatically.'
|
||||||
|
required: false
|
||||||
|
default: '30'
|
||||||
|
days-before-issue-rotten:
|
||||||
|
description: 'The number of days old an issue can be before marking it rotten. Set to -1 to never mark issues as rotten automatically. Override "days-before-rotten" option regarding only the issues.'
|
||||||
|
required: false
|
||||||
|
days-before-pr-rotten:
|
||||||
|
description: 'The number of days old a pull request can be before marking it rotten. Set to -1 to never mark pull requests as rotten automatically. Override "days-before-rotten" option regarding only the pull requests.'
|
||||||
|
required: false
|
||||||
days-before-close:
|
days-before-close:
|
||||||
description: 'The number of days to wait to close an issue or a pull request after it being marked stale. Set to -1 to never close stale issues or pull requests.'
|
description: 'The number of days to wait to close an issue or a pull request after it being marked stale. Set to -1 to never close stale issues or pull requests.'
|
||||||
required: false
|
required: false
|
||||||
default: '7'
|
default: '30'
|
||||||
days-before-issue-close:
|
days-before-issue-close:
|
||||||
description: 'The number of days to wait to close an issue after it being marked stale. Set to -1 to never close stale issues. Override "days-before-close" option regarding only the issues.'
|
description: 'The number of days to wait to close an issue after it being marked stale. Set to -1 to never close stale issues. Override "days-before-close" option regarding only the issues.'
|
||||||
required: false
|
required: false
|
||||||
|
@ -42,6 +58,10 @@ inputs:
|
||||||
description: 'The label to apply when an issue is stale.'
|
description: 'The label to apply when an issue is stale.'
|
||||||
required: false
|
required: false
|
||||||
default: 'Stale'
|
default: 'Stale'
|
||||||
|
rotten-issue-label:
|
||||||
|
description: 'The label to apply when an issue is rotten.'
|
||||||
|
required: false
|
||||||
|
default: 'Rotten'
|
||||||
close-issue-label:
|
close-issue-label:
|
||||||
description: 'The label to apply when an issue is closed.'
|
description: 'The label to apply when an issue is closed.'
|
||||||
required: false
|
required: false
|
||||||
|
@ -57,6 +77,10 @@ inputs:
|
||||||
description: 'The label to apply when a pull request is stale.'
|
description: 'The label to apply when a pull request is stale.'
|
||||||
default: 'Stale'
|
default: 'Stale'
|
||||||
required: false
|
required: false
|
||||||
|
rotten-pr-label:
|
||||||
|
description: 'The label to apply when a pull request is rotten.'
|
||||||
|
default: 'Rotten'
|
||||||
|
required: false
|
||||||
close-pr-label:
|
close-pr-label:
|
||||||
description: 'The label to apply when a pull request is closed.'
|
description: 'The label to apply when a pull request is closed.'
|
||||||
required: false
|
required: false
|
||||||
|
@ -128,6 +152,18 @@ inputs:
|
||||||
description: 'Remove stale labels from pull requests when they are updated or commented on. Override "remove-stale-when-updated" option regarding only the pull requests.'
|
description: 'Remove stale labels from pull requests when they are updated or commented on. Override "remove-stale-when-updated" option regarding only the pull requests.'
|
||||||
default: ''
|
default: ''
|
||||||
required: false
|
required: false
|
||||||
|
remove-rotten-when-updated:
|
||||||
|
description: 'Remove rotten labels from issues and pull requests when they are updated or commented on.'
|
||||||
|
default: 'true'
|
||||||
|
required: false
|
||||||
|
remove-issue-rotten-when-updated:
|
||||||
|
description: 'Remove rotten labels from issues when they are updated or commented on. Override "remove-rotten-when-updated" option regarding only the issues.'
|
||||||
|
default: ''
|
||||||
|
required: false
|
||||||
|
remove-pr-rotten-when-updated:
|
||||||
|
description: 'Remove rotten labels from pull requests when they are updated or commented on. Override "remove-rotten-when-updated" option regarding only the pull requests.'
|
||||||
|
default: ''
|
||||||
|
required: false
|
||||||
debug-only:
|
debug-only:
|
||||||
description: 'Run the processor in debug mode without actually performing any operations on live issues.'
|
description: 'Run the processor in debug mode without actually performing any operations on live issues.'
|
||||||
default: 'false'
|
default: 'false'
|
||||||
|
@ -188,6 +224,18 @@ inputs:
|
||||||
description: 'A comma delimited list of labels to remove when an issue or pull request becomes unstale.'
|
description: 'A comma delimited list of labels to remove when an issue or pull request becomes unstale.'
|
||||||
default: ''
|
default: ''
|
||||||
required: false
|
required: false
|
||||||
|
labels-to-add-when-unrotten:
|
||||||
|
description: 'A comma delimited list of labels to add when an issue or pull request becomes unrotten.'
|
||||||
|
default: ''
|
||||||
|
required: false
|
||||||
|
labels-to-remove-when-rotten:
|
||||||
|
description: 'A comma delimited list of labels to remove when an issue or pull request becomes rotten.'
|
||||||
|
default: ''
|
||||||
|
required: false
|
||||||
|
labels-to-remove-when-unrotten:
|
||||||
|
description: 'A comma delimited list of labels to remove when an issue or pull request becomes unrotten.'
|
||||||
|
default: ''
|
||||||
|
required: false
|
||||||
ignore-updates:
|
ignore-updates:
|
||||||
description: 'Any update (update/comment) can reset the stale idle time on the issues and pull requests.'
|
description: 'Any update (update/comment) can reset the stale idle time on the issues and pull requests.'
|
||||||
default: 'false'
|
default: 'false'
|
||||||
|
|
|
@ -1,12 +1,12 @@
|
||||||
import {isLabeled} from '../functions/is-labeled';
|
import { isLabeled } from '../functions/is-labeled';
|
||||||
import {isPullRequest} from '../functions/is-pull-request';
|
import { isPullRequest } from '../functions/is-pull-request';
|
||||||
import {Assignee} from '../interfaces/assignee';
|
import { Assignee } from '../interfaces/assignee';
|
||||||
import {IIssue, OctokitIssue} from '../interfaces/issue';
|
import { IIssue, OctokitIssue } from '../interfaces/issue';
|
||||||
import {IIssuesProcessorOptions} from '../interfaces/issues-processor-options';
|
import { IIssuesProcessorOptions } from '../interfaces/issues-processor-options';
|
||||||
import {ILabel} from '../interfaces/label';
|
import { ILabel } from '../interfaces/label';
|
||||||
import {IMilestone} from '../interfaces/milestone';
|
import { IMilestone } from '../interfaces/milestone';
|
||||||
import {IsoDateString} from '../types/iso-date-string';
|
import { IsoDateString } from '../types/iso-date-string';
|
||||||
import {Operations} from './operations';
|
import { Operations } from './operations';
|
||||||
|
|
||||||
export class Issue implements IIssue {
|
export class Issue implements IIssue {
|
||||||
readonly title: string;
|
readonly title: string;
|
||||||
|
@ -21,7 +21,9 @@ export class Issue implements IIssue {
|
||||||
readonly milestone?: IMilestone | null;
|
readonly milestone?: IMilestone | null;
|
||||||
readonly assignees: Assignee[];
|
readonly assignees: Assignee[];
|
||||||
isStale: boolean;
|
isStale: boolean;
|
||||||
|
isRotten: boolean;
|
||||||
markedStaleThisRun: boolean;
|
markedStaleThisRun: boolean;
|
||||||
|
markedRottenThisRun: boolean;
|
||||||
operations = new Operations();
|
operations = new Operations();
|
||||||
private readonly _options: IIssuesProcessorOptions;
|
private readonly _options: IIssuesProcessorOptions;
|
||||||
|
|
||||||
|
@ -42,7 +44,9 @@ export class Issue implements IIssue {
|
||||||
this.milestone = issue.milestone;
|
this.milestone = issue.milestone;
|
||||||
this.assignees = issue.assignees || [];
|
this.assignees = issue.assignees || [];
|
||||||
this.isStale = isLabeled(this, this.staleLabel);
|
this.isStale = isLabeled(this, this.staleLabel);
|
||||||
|
this.isRotten = isLabeled(this, this.rottenLabel);
|
||||||
this.markedStaleThisRun = false;
|
this.markedStaleThisRun = false;
|
||||||
|
this.markedRottenThisRun = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
get isPullRequest(): boolean {
|
get isPullRequest(): boolean {
|
||||||
|
@ -52,6 +56,9 @@ export class Issue implements IIssue {
|
||||||
get staleLabel(): string {
|
get staleLabel(): string {
|
||||||
return this._getStaleLabel();
|
return this._getStaleLabel();
|
||||||
}
|
}
|
||||||
|
get rottenLabel(): string {
|
||||||
|
return this._getRottenLabel();
|
||||||
|
}
|
||||||
|
|
||||||
get hasAssignees(): boolean {
|
get hasAssignees(): boolean {
|
||||||
return this.assignees.length > 0;
|
return this.assignees.length > 0;
|
||||||
|
@ -62,6 +69,11 @@ export class Issue implements IIssue {
|
||||||
? this._options.stalePrLabel
|
? this._options.stalePrLabel
|
||||||
: this._options.staleIssueLabel;
|
: this._options.staleIssueLabel;
|
||||||
}
|
}
|
||||||
|
private _getRottenLabel(): string {
|
||||||
|
return this.isPullRequest
|
||||||
|
? this._options.rottenPrLabel
|
||||||
|
: this._options.rottenIssueLabel;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function mapLabels(labels: (string | ILabel)[] | ILabel[]): ILabel[] {
|
function mapLabels(labels: (string | ILabel)[] | ILabel[]): ILabel[] {
|
||||||
|
|
|
@ -1,34 +1,34 @@
|
||||||
import * as core from '@actions/core';
|
import * as core from '@actions/core';
|
||||||
import {context, getOctokit} from '@actions/github';
|
import { context, getOctokit } from '@actions/github';
|
||||||
import {GitHub} from '@actions/github/lib/utils';
|
import { GitHub } from '@actions/github/lib/utils';
|
||||||
import {Option} from '../enums/option';
|
import { Option } from '../enums/option';
|
||||||
import {getHumanizedDate} from '../functions/dates/get-humanized-date';
|
import { getHumanizedDate } from '../functions/dates/get-humanized-date';
|
||||||
import {isDateMoreRecentThan} from '../functions/dates/is-date-more-recent-than';
|
import { isDateMoreRecentThan } from '../functions/dates/is-date-more-recent-than';
|
||||||
import {isValidDate} from '../functions/dates/is-valid-date';
|
import { isValidDate } from '../functions/dates/is-valid-date';
|
||||||
import {isBoolean} from '../functions/is-boolean';
|
import { isBoolean } from '../functions/is-boolean';
|
||||||
import {isLabeled} from '../functions/is-labeled';
|
import { isLabeled } from '../functions/is-labeled';
|
||||||
import {cleanLabel} from '../functions/clean-label';
|
import { cleanLabel } from '../functions/clean-label';
|
||||||
import {shouldMarkWhenStale} from '../functions/should-mark-when-stale';
|
import { shouldMarkWhenStale } from '../functions/should-mark-when-stale';
|
||||||
import {wordsToList} from '../functions/words-to-list';
|
import { wordsToList } from '../functions/words-to-list';
|
||||||
import {IComment} from '../interfaces/comment';
|
import { IComment } from '../interfaces/comment';
|
||||||
import {IIssueEvent} from '../interfaces/issue-event';
|
import { IIssueEvent } from '../interfaces/issue-event';
|
||||||
import {IIssuesProcessorOptions} from '../interfaces/issues-processor-options';
|
import { IIssuesProcessorOptions } from '../interfaces/issues-processor-options';
|
||||||
import {IPullRequest} from '../interfaces/pull-request';
|
import { IPullRequest } from '../interfaces/pull-request';
|
||||||
import {Assignees} from './assignees';
|
import { Assignees } from './assignees';
|
||||||
import {IgnoreUpdates} from './ignore-updates';
|
import { IgnoreUpdates } from './ignore-updates';
|
||||||
import {ExemptDraftPullRequest} from './exempt-draft-pull-request';
|
import { ExemptDraftPullRequest } from './exempt-draft-pull-request';
|
||||||
import {Issue} from './issue';
|
import { Issue } from './issue';
|
||||||
import {IssueLogger} from './loggers/issue-logger';
|
import { IssueLogger } from './loggers/issue-logger';
|
||||||
import {Logger} from './loggers/logger';
|
import { Logger } from './loggers/logger';
|
||||||
import {Milestones} from './milestones';
|
import { Milestones } from './milestones';
|
||||||
import {StaleOperations} from './stale-operations';
|
import { StaleOperations } from './stale-operations';
|
||||||
import {Statistics} from './statistics';
|
import { Statistics } from './statistics';
|
||||||
import {LoggerService} from '../services/logger.service';
|
import { LoggerService } from '../services/logger.service';
|
||||||
import {OctokitIssue} from '../interfaces/issue';
|
import { OctokitIssue } from '../interfaces/issue';
|
||||||
import {retry} from '@octokit/plugin-retry';
|
import { retry } from '@octokit/plugin-retry';
|
||||||
import {IState} from '../interfaces/state/state';
|
import { IState } from '../interfaces/state/state';
|
||||||
import {IRateLimit} from '../interfaces/rate-limit';
|
import { IRateLimit } from '../interfaces/rate-limit';
|
||||||
import {RateLimit} from './rate-limit';
|
import { RateLimit } from './rate-limit';
|
||||||
|
|
||||||
/***
|
/***
|
||||||
* Handle processing of issues for staleness/closure.
|
* Handle processing of issues for staleness/closure.
|
||||||
|
@ -52,8 +52,7 @@ export class IssuesProcessor {
|
||||||
|
|
||||||
issueLogger.info(
|
issueLogger.info(
|
||||||
LoggerService.cyan(consumedOperationsCount),
|
LoggerService.cyan(consumedOperationsCount),
|
||||||
`operation${
|
`operation${consumedOperationsCount > 1 ? 's' : ''
|
||||||
consumedOperationsCount > 1 ? 's' : ''
|
|
||||||
} consumed for this $$type`
|
} consumed for this $$type`
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -69,6 +68,7 @@ export class IssuesProcessor {
|
||||||
readonly client: InstanceType<typeof GitHub>;
|
readonly client: InstanceType<typeof GitHub>;
|
||||||
readonly options: IIssuesProcessorOptions;
|
readonly options: IIssuesProcessorOptions;
|
||||||
readonly staleIssues: Issue[] = [];
|
readonly staleIssues: Issue[] = [];
|
||||||
|
readonly rottenIssues: Issue[] = [];
|
||||||
readonly closedIssues: Issue[] = [];
|
readonly closedIssues: Issue[] = [];
|
||||||
readonly deletedBranchIssues: Issue[] = [];
|
readonly deletedBranchIssues: Issue[] = [];
|
||||||
readonly removedLabelIssues: Issue[] = [];
|
readonly removedLabelIssues: Issue[] = [];
|
||||||
|
@ -141,6 +141,16 @@ export class IssuesProcessor {
|
||||||
const labelsToRemoveWhenUnstale: string[] = wordsToList(
|
const labelsToRemoveWhenUnstale: string[] = wordsToList(
|
||||||
this.options.labelsToRemoveWhenUnstale
|
this.options.labelsToRemoveWhenUnstale
|
||||||
);
|
);
|
||||||
|
const labelsToRemoveWhenRotten: string[] = wordsToList(
|
||||||
|
this.options.labelsToRemoveWhenRotten
|
||||||
|
);
|
||||||
|
|
||||||
|
const labelsToAddWhenUnrotten: string[] = wordsToList(
|
||||||
|
this.options.labelsToAddWhenUnrotten
|
||||||
|
);
|
||||||
|
const labelsToRemoveWhenUnrotten: string[] = wordsToList(
|
||||||
|
this.options.labelsToRemoveWhenUnrotten
|
||||||
|
);
|
||||||
|
|
||||||
for (const issue of issues.values()) {
|
for (const issue of issues.values()) {
|
||||||
// Stop the processing if no more operations remains
|
// Stop the processing if no more operations remains
|
||||||
|
@ -160,7 +170,10 @@ export class IssuesProcessor {
|
||||||
issue,
|
issue,
|
||||||
labelsToAddWhenUnstale,
|
labelsToAddWhenUnstale,
|
||||||
labelsToRemoveWhenUnstale,
|
labelsToRemoveWhenUnstale,
|
||||||
labelsToRemoveWhenStale
|
labelsToRemoveWhenStale,
|
||||||
|
labelsToAddWhenUnrotten,
|
||||||
|
labelsToRemoveWhenUnrotten,
|
||||||
|
labelsToRemoveWhenRotten
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
this.state.addIssueToProcessed(issue);
|
this.state.addIssueToProcessed(issue);
|
||||||
|
@ -200,7 +213,10 @@ export class IssuesProcessor {
|
||||||
issue: Issue,
|
issue: Issue,
|
||||||
labelsToAddWhenUnstale: Readonly<string>[],
|
labelsToAddWhenUnstale: Readonly<string>[],
|
||||||
labelsToRemoveWhenUnstale: Readonly<string>[],
|
labelsToRemoveWhenUnstale: Readonly<string>[],
|
||||||
labelsToRemoveWhenStale: Readonly<string>[]
|
labelsToRemoveWhenStale: Readonly<string>[],
|
||||||
|
labelsToAddWhenUnrotten: Readonly<string>[],
|
||||||
|
labelsToRemoveWhenUnrotten: Readonly<string>[],
|
||||||
|
labelsToRemoveWhenRotten: Readonly<string>[]
|
||||||
): Promise<void> {
|
): Promise<void> {
|
||||||
this.statistics?.incrementProcessedItemsCount(issue);
|
this.statistics?.incrementProcessedItemsCount(issue);
|
||||||
|
|
||||||
|
@ -215,12 +231,21 @@ export class IssuesProcessor {
|
||||||
const staleMessage: string = issue.isPullRequest
|
const staleMessage: string = issue.isPullRequest
|
||||||
? this.options.stalePrMessage
|
? this.options.stalePrMessage
|
||||||
: this.options.staleIssueMessage;
|
: this.options.staleIssueMessage;
|
||||||
|
const rottenMessage: string = issue.isPullRequest
|
||||||
|
? this.options.rottenPrMessage
|
||||||
|
: this.options.rottenIssueMessage;
|
||||||
const closeMessage: string = issue.isPullRequest
|
const closeMessage: string = issue.isPullRequest
|
||||||
? this.options.closePrMessage
|
? this.options.closePrMessage
|
||||||
: this.options.closeIssueMessage;
|
: this.options.closeIssueMessage;
|
||||||
|
const skipRottenMessage = issue.isPullRequest
|
||||||
|
? this.options.rottenPrMessage.length === 0
|
||||||
|
: this.options.rottenIssueMessage.length === 0;
|
||||||
const staleLabel: string = issue.isPullRequest
|
const staleLabel: string = issue.isPullRequest
|
||||||
? this.options.stalePrLabel
|
? this.options.stalePrLabel
|
||||||
: this.options.staleIssueLabel;
|
: this.options.staleIssueLabel;
|
||||||
|
const rottenLabel: string = issue.isPullRequest
|
||||||
|
? this.options.rottenPrLabel
|
||||||
|
: this.options.rottenIssueLabel;
|
||||||
const closeLabel: string = issue.isPullRequest
|
const closeLabel: string = issue.isPullRequest
|
||||||
? this.options.closePrLabel
|
? this.options.closePrLabel
|
||||||
: this.options.closeIssueLabel;
|
: this.options.closeIssueLabel;
|
||||||
|
@ -231,6 +256,7 @@ export class IssuesProcessor {
|
||||||
? this._getDaysBeforePrStale()
|
? this._getDaysBeforePrStale()
|
||||||
: this._getDaysBeforeIssueStale();
|
: this._getDaysBeforeIssueStale();
|
||||||
|
|
||||||
|
|
||||||
if (issue.state === 'closed') {
|
if (issue.state === 'closed') {
|
||||||
issueLogger.info(`Skipping this $$type because it is closed`);
|
issueLogger.info(`Skipping this $$type because it is closed`);
|
||||||
IssuesProcessor._endIssueProcessing(issue);
|
IssuesProcessor._endIssueProcessing(issue);
|
||||||
|
@ -342,10 +368,18 @@ export class IssuesProcessor {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Check if the issue is stale, if not, check if it is rotten and then log the findings.
|
||||||
if (issue.isStale) {
|
if (issue.isStale) {
|
||||||
issueLogger.info(`This $$type includes a stale label`);
|
issueLogger.info(`This $$type includes a stale label`);
|
||||||
} else {
|
} else {
|
||||||
issueLogger.info(`This $$type does not include a stale label`);
|
issueLogger.info(`This $$type does not include a stale label`);
|
||||||
|
if (issue.isRotten) {
|
||||||
|
issueLogger.info(`This $$type includes a rotten label`);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
issueLogger.info(`This $$type does not include a rotten label`);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const exemptLabels: string[] = wordsToList(
|
const exemptLabels: string[] = wordsToList(
|
||||||
|
@ -445,10 +479,24 @@ export class IssuesProcessor {
|
||||||
return; // Don't process draft PR
|
return; // Don't process draft PR
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Here we are looking into if the issue is stale or not, and then adding the label. This same code will also be used for the rotten label.
|
||||||
// Determine if this issue needs to be marked stale first
|
// Determine if this issue needs to be marked stale first
|
||||||
if (!issue.isStale) {
|
if (!issue.isStale) {
|
||||||
issueLogger.info(`This $$type is not stale`);
|
issueLogger.info(`This $$type is not stale`);
|
||||||
|
|
||||||
|
if (issue.isRotten) {
|
||||||
|
await this._processRottenIssue(
|
||||||
|
issue,
|
||||||
|
rottenLabel,
|
||||||
|
rottenMessage,
|
||||||
|
labelsToAddWhenUnrotten,
|
||||||
|
labelsToRemoveWhenUnrotten,
|
||||||
|
labelsToRemoveWhenRotten,
|
||||||
|
closeMessage,
|
||||||
|
closeLabel
|
||||||
|
);
|
||||||
|
}
|
||||||
|
else {
|
||||||
const shouldIgnoreUpdates: boolean = new IgnoreUpdates(
|
const shouldIgnoreUpdates: boolean = new IgnoreUpdates(
|
||||||
this.options,
|
this.options,
|
||||||
issue
|
issue
|
||||||
|
@ -520,6 +568,7 @@ export class IssuesProcessor {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Process the issue if it was marked stale
|
// Process the issue if it was marked stale
|
||||||
if (issue.isStale) {
|
if (issue.isStale) {
|
||||||
|
@ -528,11 +577,17 @@ export class IssuesProcessor {
|
||||||
issue,
|
issue,
|
||||||
staleLabel,
|
staleLabel,
|
||||||
staleMessage,
|
staleMessage,
|
||||||
|
rottenLabel,
|
||||||
|
rottenMessage,
|
||||||
|
closeLabel,
|
||||||
|
closeMessage,
|
||||||
labelsToAddWhenUnstale,
|
labelsToAddWhenUnstale,
|
||||||
labelsToRemoveWhenUnstale,
|
labelsToRemoveWhenUnstale,
|
||||||
labelsToRemoveWhenStale,
|
labelsToRemoveWhenStale,
|
||||||
closeMessage,
|
labelsToAddWhenUnrotten,
|
||||||
closeLabel
|
labelsToRemoveWhenUnrotten,
|
||||||
|
labelsToRemoveWhenRotten,
|
||||||
|
skipRottenMessage,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -650,17 +705,26 @@ export class IssuesProcessor {
|
||||||
}
|
}
|
||||||
|
|
||||||
// handle all of the stale issue logic when we find a stale issue
|
// handle all of the stale issue logic when we find a stale issue
|
||||||
|
// This whole thing needs to be altered, to be calculated based on the days to rotten, rather than days to close or whatever
|
||||||
private async _processStaleIssue(
|
private async _processStaleIssue(
|
||||||
issue: Issue,
|
issue: Issue,
|
||||||
staleLabel: string,
|
staleLabel: string,
|
||||||
staleMessage: string,
|
staleMessage: string,
|
||||||
|
rottenLabel: string,
|
||||||
|
rottenMessage: string,
|
||||||
|
closeLabel: string,
|
||||||
|
closeMessage: string,
|
||||||
labelsToAddWhenUnstale: Readonly<string>[],
|
labelsToAddWhenUnstale: Readonly<string>[],
|
||||||
labelsToRemoveWhenUnstale: Readonly<string>[],
|
labelsToRemoveWhenUnstale: Readonly<string>[],
|
||||||
labelsToRemoveWhenStale: Readonly<string>[],
|
labelsToRemoveWhenStale: Readonly<string>[],
|
||||||
closeMessage?: string,
|
labelsToAddWhenUnrotten: Readonly<string>[],
|
||||||
closeLabel?: string
|
labelsToRemoveWhenUnrotten: Readonly<string>[],
|
||||||
|
labelsToRemoveWhenRotten: Readonly<string>[],
|
||||||
|
skipMessage: boolean
|
||||||
) {
|
) {
|
||||||
const issueLogger: IssueLogger = new IssueLogger(issue);
|
const issueLogger: IssueLogger = new IssueLogger(issue);
|
||||||
|
|
||||||
|
// We can get the label creation date from the getLableCreationDate function
|
||||||
const markedStaleOn: string =
|
const markedStaleOn: string =
|
||||||
(await this.getLabelCreationDate(issue, staleLabel)) || issue.updated_at;
|
(await this.getLabelCreationDate(issue, staleLabel)) || issue.updated_at;
|
||||||
issueLogger.info(
|
issueLogger.info(
|
||||||
|
@ -678,12 +742,15 @@ export class IssuesProcessor {
|
||||||
)}`
|
)}`
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const daysBeforeRotten: number = issue.isPullRequest
|
||||||
|
? this._getDaysBeforePrRotten()
|
||||||
|
: this._getDaysBeforeIssueRotten();
|
||||||
|
|
||||||
const daysBeforeClose: number = issue.isPullRequest
|
const daysBeforeClose: number = issue.isPullRequest
|
||||||
? this._getDaysBeforePrClose()
|
? this._getDaysBeforePrClose()
|
||||||
: this._getDaysBeforeIssueClose();
|
: this._getDaysBeforeIssueClose();
|
||||||
|
|
||||||
issueLogger.info(
|
issueLogger.info(
|
||||||
`Days before $$type close: ${LoggerService.cyan(daysBeforeClose)}`
|
`Days before $$type rotten: ${LoggerService.cyan(daysBeforeRotten)}`
|
||||||
);
|
);
|
||||||
|
|
||||||
const shouldRemoveStaleWhenUpdated: boolean =
|
const shouldRemoveStaleWhenUpdated: boolean =
|
||||||
|
@ -703,6 +770,7 @@ export class IssuesProcessor {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// we will need to use a variation of this for the rotten state
|
||||||
if (issue.markedStaleThisRun) {
|
if (issue.markedStaleThisRun) {
|
||||||
issueLogger.info(`marked stale this run, so don't check for updates`);
|
issueLogger.info(`marked stale this run, so don't check for updates`);
|
||||||
await this._removeLabelsOnStatusTransition(
|
await this._removeLabelsOnStatusTransition(
|
||||||
|
@ -750,22 +818,17 @@ export class IssuesProcessor {
|
||||||
return; // Nothing to do because it is no longer stale
|
return; // Nothing to do because it is no longer stale
|
||||||
}
|
}
|
||||||
|
|
||||||
// Now start closing logic
|
if (daysBeforeRotten < 0) {
|
||||||
if (daysBeforeClose < 0) {
|
if (daysBeforeClose < 0) {
|
||||||
return; // Nothing to do because we aren't closing stale issues
|
return;
|
||||||
}
|
}
|
||||||
|
else {
|
||||||
const issueHasUpdateInCloseWindow: boolean = IssuesProcessor._updatedSince(
|
let issueHasUpdateInCloseWindow: boolean
|
||||||
|
issueHasUpdateInCloseWindow = !IssuesProcessor._updatedSince(
|
||||||
issue.updated_at,
|
issue.updated_at,
|
||||||
daysBeforeClose
|
daysBeforeClose
|
||||||
);
|
);
|
||||||
issueLogger.info(
|
if (!issueHasUpdateInCloseWindow && !issueHasCommentsSinceStale) {
|
||||||
`$$type has been updated in the last ${daysBeforeClose} days: ${LoggerService.cyan(
|
|
||||||
issueHasUpdateInCloseWindow
|
|
||||||
)}`
|
|
||||||
);
|
|
||||||
|
|
||||||
if (!issueHasCommentsSinceStale && !issueHasUpdateInCloseWindow) {
|
|
||||||
issueLogger.info(
|
issueLogger.info(
|
||||||
`Closing $$type because it was last updated on: ${LoggerService.cyan(
|
`Closing $$type because it was last updated on: ${LoggerService.cyan(
|
||||||
issue.updated_at
|
issue.updated_at
|
||||||
|
@ -788,6 +851,229 @@ export class IssuesProcessor {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: make a function for shouldMarkWhenRotten
|
||||||
|
const shouldMarkAsRotten: boolean = shouldMarkWhenStale(daysBeforeRotten);
|
||||||
|
|
||||||
|
if (!issue.isRotten) {
|
||||||
|
issueLogger.info(`This $$type is not rotten`);
|
||||||
|
|
||||||
|
const shouldIgnoreUpdates: boolean = new IgnoreUpdates(
|
||||||
|
this.options,
|
||||||
|
issue
|
||||||
|
).shouldIgnoreUpdates();
|
||||||
|
|
||||||
|
let shouldBeRotten: boolean;
|
||||||
|
shouldBeRotten = !IssuesProcessor._updatedSince(
|
||||||
|
issue.updated_at,
|
||||||
|
daysBeforeRotten
|
||||||
|
);
|
||||||
|
|
||||||
|
if (shouldBeRotten) {
|
||||||
|
if (shouldIgnoreUpdates) {
|
||||||
|
issueLogger.info(
|
||||||
|
`This $$type should be rotten based on the creation date the ${getHumanizedDate(
|
||||||
|
new Date(issue.created_at)
|
||||||
|
)} (${LoggerService.cyan(issue.created_at)})`
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
issueLogger.info(
|
||||||
|
`This $$type should be rotten based on the last update date the ${getHumanizedDate(
|
||||||
|
new Date(issue.updated_at)
|
||||||
|
)} (${LoggerService.cyan(issue.updated_at)})`
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (shouldMarkAsRotten) {
|
||||||
|
issueLogger.info(
|
||||||
|
`This $$type should be marked as rotten based on the option ${issueLogger.createOptionLink(
|
||||||
|
this._getDaysBeforeRottenUsedOptionName(issue)
|
||||||
|
)} (${LoggerService.cyan(daysBeforeRotten)})`
|
||||||
|
);
|
||||||
|
await this._markRotten(issue, rottenMessage, rottenLabel, skipMessage);
|
||||||
|
issue.isRotten = true; // This issue is now considered rotten
|
||||||
|
issue.markedRottenThisRun = true;
|
||||||
|
issueLogger.info(`This $$type is now rotten`);
|
||||||
|
} else {
|
||||||
|
issueLogger.info(
|
||||||
|
`This $$type should not be marked as rotten based on the option ${issueLogger.createOptionLink(
|
||||||
|
this._getDaysBeforeStaleUsedOptionName(issue)
|
||||||
|
)} (${LoggerService.cyan(daysBeforeRotten)})`
|
||||||
|
);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (shouldIgnoreUpdates) {
|
||||||
|
issueLogger.info(
|
||||||
|
`This $$type is not old enough to be rotten based on the creation date the ${getHumanizedDate(
|
||||||
|
new Date(issue.created_at)
|
||||||
|
)} (${LoggerService.cyan(issue.created_at)})`
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
issueLogger.info(
|
||||||
|
`This $$type is not old enough to be rotten based on the creation date the ${getHumanizedDate(
|
||||||
|
new Date(issue.updated_at)
|
||||||
|
)} (${LoggerService.cyan(issue.updated_at)})`
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if(issue.isRotten){
|
||||||
|
issueLogger.info(`This $$type is already rotten`);
|
||||||
|
// process the rotten issues
|
||||||
|
this._processRottenIssue(
|
||||||
|
issue,
|
||||||
|
rottenLabel,
|
||||||
|
rottenMessage,
|
||||||
|
labelsToAddWhenUnrotten,
|
||||||
|
labelsToRemoveWhenUnrotten,
|
||||||
|
labelsToRemoveWhenRotten,
|
||||||
|
closeMessage,
|
||||||
|
closeLabel,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
private async _processRottenIssue(
|
||||||
|
issue: Issue,
|
||||||
|
rottenLabel: string,
|
||||||
|
rottenMessage: string,
|
||||||
|
labelsToAddWhenUnrotten: Readonly<string>[],
|
||||||
|
labelsToRemoveWhenUnrotten: Readonly<string>[],
|
||||||
|
labelsToRemoveWhenRotten: Readonly<string>[],
|
||||||
|
closeMessage?: string,
|
||||||
|
closeLabel?: string
|
||||||
|
) {
|
||||||
|
const issueLogger: IssueLogger = new IssueLogger(issue);
|
||||||
|
// We can get the label creation date from the getLableCreationDate function
|
||||||
|
const markedRottenOn: string =
|
||||||
|
(await this.getLabelCreationDate(issue, rottenLabel)) || issue.updated_at;
|
||||||
|
issueLogger.info(
|
||||||
|
`$$type marked rotten on: ${LoggerService.cyan(markedRottenOn)}`
|
||||||
|
);
|
||||||
|
|
||||||
|
const issueHasCommentsSinceRotten: boolean = await this._hasCommentsSince(
|
||||||
|
issue,
|
||||||
|
markedRottenOn,
|
||||||
|
rottenMessage
|
||||||
|
);
|
||||||
|
issueLogger.info(
|
||||||
|
`$$type has been commented on: ${LoggerService.cyan(
|
||||||
|
issueHasCommentsSinceRotten
|
||||||
|
)}`
|
||||||
|
);
|
||||||
|
|
||||||
|
const daysBeforeClose: number = issue.isPullRequest
|
||||||
|
? this._getDaysBeforePrClose()
|
||||||
|
: this._getDaysBeforeIssueClose();
|
||||||
|
|
||||||
|
issueLogger.info(
|
||||||
|
`Days before $$type close: ${LoggerService.cyan(daysBeforeClose)}`
|
||||||
|
);
|
||||||
|
|
||||||
|
const shouldRemoveRottenWhenUpdated: boolean =
|
||||||
|
this._shouldRemoveRottenWhenUpdated(issue);
|
||||||
|
|
||||||
|
issueLogger.info(
|
||||||
|
`The option ${issueLogger.createOptionLink(
|
||||||
|
this._getRemoveRottenWhenUpdatedUsedOptionName(issue)
|
||||||
|
)} is: ${LoggerService.cyan(shouldRemoveRottenWhenUpdated)}`
|
||||||
|
);
|
||||||
|
|
||||||
|
if (shouldRemoveRottenWhenUpdated) {
|
||||||
|
issueLogger.info(`The rotten label should not be removed`);
|
||||||
|
} else {
|
||||||
|
issueLogger.info(
|
||||||
|
`The rotten label should be removed if all conditions met`
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (issue.markedRottenThisRun) {
|
||||||
|
issueLogger.info(`marked rotten this run, so don't check for updates`);
|
||||||
|
await this._removeLabelsOnStatusTransition(
|
||||||
|
issue,
|
||||||
|
labelsToRemoveWhenRotten,
|
||||||
|
Option.LabelsToRemoveWhenRotten
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// The issue.updated_at and markedRottenOn are not always exactly in sync (they can be off by a second or 2)
|
||||||
|
// isDateMoreRecentThan makes sure they are not the same date within a certain tolerance (15 seconds in this case)
|
||||||
|
const issueHasUpdateSinceRotten = isDateMoreRecentThan(
|
||||||
|
new Date(issue.updated_at),
|
||||||
|
new Date(markedRottenOn),
|
||||||
|
15
|
||||||
|
);
|
||||||
|
|
||||||
|
issueLogger.info(
|
||||||
|
`$$type has been updated since it was marked rotten: ${LoggerService.cyan(
|
||||||
|
issueHasUpdateSinceRotten
|
||||||
|
)}`
|
||||||
|
);
|
||||||
|
|
||||||
|
// Should we un-rotten this issue?
|
||||||
|
if (
|
||||||
|
shouldRemoveRottenWhenUpdated &&
|
||||||
|
(issueHasUpdateSinceRotten || issueHasCommentsSinceRotten) &&
|
||||||
|
!issue.markedRottenThisRun
|
||||||
|
) {
|
||||||
|
issueLogger.info(
|
||||||
|
`Remove the rotten label since the $$type has been updated and the workflow should remove the stale label when updated`
|
||||||
|
);
|
||||||
|
await this._removeRottenLabel(issue, rottenLabel);
|
||||||
|
|
||||||
|
// Are there labels to remove or add when an issue is no longer rotten?
|
||||||
|
// This logic takes care of removing labels when unrotten
|
||||||
|
await this._removeLabelsOnStatusTransition(
|
||||||
|
issue,
|
||||||
|
labelsToRemoveWhenUnrotten,
|
||||||
|
Option.LabelsToRemoveWhenUnrotten
|
||||||
|
);
|
||||||
|
await this._addLabelsWhenUnrotten(issue, labelsToAddWhenUnrotten);
|
||||||
|
|
||||||
|
issueLogger.info(`Skipping the process since the $$type is now un-rotten`);
|
||||||
|
|
||||||
|
return; // Nothing to do because it is no longer rotten
|
||||||
|
}
|
||||||
|
|
||||||
|
// Now start closing logic
|
||||||
|
if (daysBeforeClose < 0) {
|
||||||
|
return; // Nothing to do because we aren't closing rotten issues
|
||||||
|
}
|
||||||
|
|
||||||
|
const issueHasUpdateInCloseWindow: boolean = IssuesProcessor._updatedSince(
|
||||||
|
issue.updated_at,
|
||||||
|
daysBeforeClose
|
||||||
|
);
|
||||||
|
issueLogger.info(
|
||||||
|
`$$type has been updated in the last ${daysBeforeClose} days: ${LoggerService.cyan(
|
||||||
|
issueHasUpdateInCloseWindow
|
||||||
|
)}`
|
||||||
|
);
|
||||||
|
|
||||||
|
if (!issueHasCommentsSinceRotten && !issueHasUpdateInCloseWindow) {
|
||||||
|
issueLogger.info(
|
||||||
|
`Closing $$type because it was last updated on: ${LoggerService.cyan(
|
||||||
|
issue.updated_at
|
||||||
|
)}`
|
||||||
|
);
|
||||||
|
await this._closeIssue(issue, closeMessage, closeLabel);
|
||||||
|
|
||||||
|
if (this.options.deleteBranch && issue.pull_request) {
|
||||||
|
issueLogger.info(
|
||||||
|
`Deleting the branch since the option ${issueLogger.createOptionLink(
|
||||||
|
Option.DeleteBranch
|
||||||
|
)} is enabled`
|
||||||
|
);
|
||||||
|
await this._deleteBranch(issue);
|
||||||
|
this.deletedBranchIssues.push(issue);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
issueLogger.info(
|
||||||
|
`Rotten $$type is not old enough to close yet (hasComments? ${issueHasCommentsSinceRotten}, hasUpdate? ${issueHasUpdateInCloseWindow})`
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// checks to see if a given issue is still stale (has had activity on it)
|
// checks to see if a given issue is still stale (has had activity on it)
|
||||||
private async _hasCommentsSince(
|
private async _hasCommentsSince(
|
||||||
|
@ -876,6 +1162,58 @@ export class IssuesProcessor {
|
||||||
issueLogger.error(`Error when adding a label: ${error.message}`);
|
issueLogger.error(`Error when adding a label: ${error.message}`);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
private async _markRotten(
|
||||||
|
issue: Issue,
|
||||||
|
rottenMessage: string,
|
||||||
|
rottenLabel: string,
|
||||||
|
skipMessage: boolean
|
||||||
|
): Promise<void> {
|
||||||
|
const issueLogger: IssueLogger = new IssueLogger(issue);
|
||||||
|
|
||||||
|
issueLogger.info(`Marking this $$type as rotten`);
|
||||||
|
this.rottenIssues.push(issue);
|
||||||
|
|
||||||
|
// if the issue is being marked rotten, the updated date should be changed to right now
|
||||||
|
// so that close calculations work correctly
|
||||||
|
const newUpdatedAtDate: Date = new Date();
|
||||||
|
issue.updated_at = newUpdatedAtDate.toString();
|
||||||
|
|
||||||
|
if (!skipMessage) {
|
||||||
|
try {
|
||||||
|
this._consumeIssueOperation(issue);
|
||||||
|
this.statistics?.incrementAddedItemsComment(issue);
|
||||||
|
|
||||||
|
if (!this.options.debugOnly) {
|
||||||
|
await this.client.rest.issues.createComment({
|
||||||
|
owner: context.repo.owner,
|
||||||
|
repo: context.repo.repo,
|
||||||
|
issue_number: issue.number,
|
||||||
|
body: rottenMessage
|
||||||
|
});
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
issueLogger.error(`Error when creating a comment: ${error.message}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
this._consumeIssueOperation(issue);
|
||||||
|
this.statistics?.incrementAddedItemsLabel(issue);
|
||||||
|
this.statistics?.incrementStaleItemsCount(issue);
|
||||||
|
|
||||||
|
if (!this.options.debugOnly) {
|
||||||
|
await this.client.rest.issues.addLabels({
|
||||||
|
owner: context.repo.owner,
|
||||||
|
repo: context.repo.repo,
|
||||||
|
issue_number: issue.number,
|
||||||
|
labels: [rottenLabel]
|
||||||
|
});
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
issueLogger.error(`Error when adding a label: ${error.message}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
// Close an issue based on staleness
|
// Close an issue based on staleness
|
||||||
private async _closeIssue(
|
private async _closeIssue(
|
||||||
|
@ -996,8 +1334,7 @@ export class IssuesProcessor {
|
||||||
issueLogger.warning(
|
issueLogger.warning(
|
||||||
`Deleting the branch "${LoggerService.cyan(
|
`Deleting the branch "${LoggerService.cyan(
|
||||||
branch
|
branch
|
||||||
)}" has skipped because it belongs to other repo ${
|
)}" has skipped because it belongs to other repo ${pullRequest.head.repo.full_name
|
||||||
pullRequest.head.repo.full_name
|
|
||||||
}`
|
}`
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -1012,8 +1349,7 @@ export class IssuesProcessor {
|
||||||
const issueLogger: IssueLogger = new IssueLogger(issue);
|
const issueLogger: IssueLogger = new IssueLogger(issue);
|
||||||
|
|
||||||
issueLogger.info(
|
issueLogger.info(
|
||||||
`${
|
`${isSubStep ? LoggerService.white('├── ') : ''
|
||||||
isSubStep ? LoggerService.white('├── ') : ''
|
|
||||||
}Removing the label "${LoggerService.cyan(label)}" from this $$type...`
|
}Removing the label "${LoggerService.cyan(label)}" from this $$type...`
|
||||||
);
|
);
|
||||||
this.removedLabelIssues.push(issue);
|
this.removedLabelIssues.push(issue);
|
||||||
|
@ -1032,14 +1368,12 @@ export class IssuesProcessor {
|
||||||
}
|
}
|
||||||
|
|
||||||
issueLogger.info(
|
issueLogger.info(
|
||||||
`${
|
`${isSubStep ? LoggerService.white('└── ') : ''
|
||||||
isSubStep ? LoggerService.white('└── ') : ''
|
|
||||||
}The label "${LoggerService.cyan(label)}" was removed`
|
}The label "${LoggerService.cyan(label)}" was removed`
|
||||||
);
|
);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
issueLogger.error(
|
issueLogger.error(
|
||||||
`${
|
`${isSubStep ? LoggerService.white('└── ') : ''
|
||||||
isSubStep ? LoggerService.white('└── ') : ''
|
|
||||||
}Error when removing the label: "${LoggerService.cyan(error.message)}"`
|
}Error when removing the label: "${LoggerService.cyan(error.message)}"`
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -1056,6 +1390,17 @@ export class IssuesProcessor {
|
||||||
? this.options.daysBeforeStale
|
? this.options.daysBeforeStale
|
||||||
: this.options.daysBeforePrStale;
|
: this.options.daysBeforePrStale;
|
||||||
}
|
}
|
||||||
|
private _getDaysBeforeIssueRotten(): number {
|
||||||
|
return isNaN(this.options.daysBeforeIssueRotten)
|
||||||
|
? this.options.daysBeforeRotten
|
||||||
|
: this.options.daysBeforeIssueRotten;
|
||||||
|
}
|
||||||
|
|
||||||
|
private _getDaysBeforePrRotten(): number {
|
||||||
|
return isNaN(this.options.daysBeforePrStale)
|
||||||
|
? this.options.daysBeforeStale
|
||||||
|
: this.options.daysBeforePrStale;
|
||||||
|
}
|
||||||
|
|
||||||
private _getDaysBeforeIssueClose(): number {
|
private _getDaysBeforeIssueClose(): number {
|
||||||
return isNaN(this.options.daysBeforeIssueClose)
|
return isNaN(this.options.daysBeforeIssueClose)
|
||||||
|
@ -1069,6 +1414,7 @@ export class IssuesProcessor {
|
||||||
: this.options.daysBeforePrClose;
|
: this.options.daysBeforePrClose;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
private _getOnlyLabels(issue: Issue): string {
|
private _getOnlyLabels(issue: Issue): string {
|
||||||
if (issue.isPullRequest) {
|
if (issue.isPullRequest) {
|
||||||
if (this.options.onlyPrLabels !== '') {
|
if (this.options.onlyPrLabels !== '') {
|
||||||
|
@ -1116,6 +1462,21 @@ export class IssuesProcessor {
|
||||||
|
|
||||||
return this.options.removeStaleWhenUpdated;
|
return this.options.removeStaleWhenUpdated;
|
||||||
}
|
}
|
||||||
|
private _shouldRemoveRottenWhenUpdated(issue: Issue): boolean {
|
||||||
|
if (issue.isPullRequest) {
|
||||||
|
if (isBoolean(this.options.removePrRottenWhenUpdated)) {
|
||||||
|
return this.options.removePrRottenWhenUpdated;
|
||||||
|
}
|
||||||
|
|
||||||
|
return this.options.removeRottenWhenUpdated;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isBoolean(this.options.removeIssueRottenWhenUpdated)) {
|
||||||
|
return this.options.removeIssueRottenWhenUpdated;
|
||||||
|
}
|
||||||
|
|
||||||
|
return this.options.removeRottenWhenUpdated;
|
||||||
|
}
|
||||||
|
|
||||||
private async _removeLabelsOnStatusTransition(
|
private async _removeLabelsOnStatusTransition(
|
||||||
issue: Issue,
|
issue: Issue,
|
||||||
|
@ -1139,6 +1500,7 @@ export class IssuesProcessor {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
private async _addLabelsWhenUnstale(
|
private async _addLabelsWhenUnstale(
|
||||||
issue: Issue,
|
issue: Issue,
|
||||||
labelsToAdd: Readonly<string>[]
|
labelsToAdd: Readonly<string>[]
|
||||||
|
@ -1175,6 +1537,42 @@ export class IssuesProcessor {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private async _addLabelsWhenUnrotten(
|
||||||
|
issue: Issue,
|
||||||
|
labelsToAdd: Readonly<string>[]
|
||||||
|
): Promise<void> {
|
||||||
|
if (!labelsToAdd.length) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const issueLogger: IssueLogger = new IssueLogger(issue);
|
||||||
|
|
||||||
|
issueLogger.info(
|
||||||
|
`Adding all the labels specified via the ${this._logger.createOptionLink(
|
||||||
|
Option.LabelsToAddWhenUnrotten
|
||||||
|
)} option.`
|
||||||
|
);
|
||||||
|
|
||||||
|
// TODO: this might need to be changed to a set to avoiod repetition
|
||||||
|
this.addedLabelIssues.push(issue);
|
||||||
|
|
||||||
|
try {
|
||||||
|
this._consumeIssueOperation(issue);
|
||||||
|
this.statistics?.incrementAddedItemsLabel(issue);
|
||||||
|
if (!this.options.debugOnly) {
|
||||||
|
await this.client.rest.issues.addLabels({
|
||||||
|
owner: context.repo.owner,
|
||||||
|
repo: context.repo.repo,
|
||||||
|
issue_number: issue.number,
|
||||||
|
labels: labelsToAdd
|
||||||
|
});
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
this._logger.error(
|
||||||
|
`Error when adding labels after updated from rotten: ${error.message}`
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
private async _removeStaleLabel(
|
private async _removeStaleLabel(
|
||||||
issue: Issue,
|
issue: Issue,
|
||||||
staleLabel: Readonly<string>
|
staleLabel: Readonly<string>
|
||||||
|
@ -1188,6 +1586,19 @@ export class IssuesProcessor {
|
||||||
await this._removeLabel(issue, staleLabel);
|
await this._removeLabel(issue, staleLabel);
|
||||||
this.statistics?.incrementUndoStaleItemsCount(issue);
|
this.statistics?.incrementUndoStaleItemsCount(issue);
|
||||||
}
|
}
|
||||||
|
private async _removeRottenLabel(
|
||||||
|
issue: Issue,
|
||||||
|
rottenLabel: Readonly<string>
|
||||||
|
): Promise<void> {
|
||||||
|
const issueLogger: IssueLogger = new IssueLogger(issue);
|
||||||
|
|
||||||
|
issueLogger.info(
|
||||||
|
`The $$type is no longer rotten. Removing the rotten label...`
|
||||||
|
);
|
||||||
|
|
||||||
|
await this._removeLabel(issue, rottenLabel);
|
||||||
|
this.statistics?.incrementUndoRottenItemsCount(issue);
|
||||||
|
}
|
||||||
|
|
||||||
private async _removeCloseLabel(
|
private async _removeCloseLabel(
|
||||||
issue: Issue,
|
issue: Issue,
|
||||||
|
@ -1266,6 +1677,32 @@ export class IssuesProcessor {
|
||||||
: Option.DaysBeforePrStale;
|
: Option.DaysBeforePrStale;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private _getDaysBeforeRottenUsedOptionName(
|
||||||
|
issue: Readonly<Issue>
|
||||||
|
):
|
||||||
|
| Option.DaysBeforeRotten
|
||||||
|
| Option.DaysBeforeIssueRotten
|
||||||
|
| Option.DaysBeforePrRotten {
|
||||||
|
return issue.isPullRequest
|
||||||
|
? this._getDaysBeforePrRottenUsedOptionName()
|
||||||
|
: this._getDaysBeforeIssueRottenUsedOptionName();
|
||||||
|
}
|
||||||
|
|
||||||
|
private _getDaysBeforeIssueRottenUsedOptionName():
|
||||||
|
| Option.DaysBeforeRotten
|
||||||
|
| Option.DaysBeforeIssueRotten {
|
||||||
|
return isNaN(this.options.daysBeforeIssueRotten)
|
||||||
|
? Option.DaysBeforeRotten
|
||||||
|
: Option.DaysBeforeIssueRotten;
|
||||||
|
}
|
||||||
|
|
||||||
|
private _getDaysBeforePrRottenUsedOptionName():
|
||||||
|
| Option.DaysBeforeRotten
|
||||||
|
| Option.DaysBeforePrRotten {
|
||||||
|
return isNaN(this.options.daysBeforePrRotten)
|
||||||
|
? Option.DaysBeforeRotten
|
||||||
|
: Option.DaysBeforePrRotten;
|
||||||
|
}
|
||||||
private _getRemoveStaleWhenUpdatedUsedOptionName(
|
private _getRemoveStaleWhenUpdatedUsedOptionName(
|
||||||
issue: Readonly<Issue>
|
issue: Readonly<Issue>
|
||||||
):
|
):
|
||||||
|
@ -1286,4 +1723,24 @@ export class IssuesProcessor {
|
||||||
|
|
||||||
return Option.RemoveStaleWhenUpdated;
|
return Option.RemoveStaleWhenUpdated;
|
||||||
}
|
}
|
||||||
|
private _getRemoveRottenWhenUpdatedUsedOptionName(
|
||||||
|
issue: Readonly<Issue>
|
||||||
|
):
|
||||||
|
| Option.RemovePrRottenWhenUpdated
|
||||||
|
| Option.RemoveRottenWhenUpdated
|
||||||
|
| Option.RemoveIssueRottenWhenUpdated {
|
||||||
|
if (issue.isPullRequest) {
|
||||||
|
if (isBoolean(this.options.removePrRottenWhenUpdated)) {
|
||||||
|
return Option.RemovePrRottenWhenUpdated;
|
||||||
|
}
|
||||||
|
|
||||||
|
return Option.RemoveRottenWhenUpdated;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isBoolean(this.options.removeIssueRottenWhenUpdated)) {
|
||||||
|
return Option.RemoveIssueRottenWhenUpdated;
|
||||||
|
}
|
||||||
|
|
||||||
|
return Option.RemoveRottenWhenUpdated;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -15,6 +15,10 @@ export class Statistics {
|
||||||
stalePullRequestsCount = 0;
|
stalePullRequestsCount = 0;
|
||||||
undoStaleIssuesCount = 0;
|
undoStaleIssuesCount = 0;
|
||||||
undoStalePullRequestsCount = 0;
|
undoStalePullRequestsCount = 0;
|
||||||
|
rottenIssuesCount = 0;
|
||||||
|
rottenPullRequestsCount = 0;
|
||||||
|
undoRottenIssuesCount = 0;
|
||||||
|
undoRottenPullRequestsCount = 0;
|
||||||
operationsCount = 0;
|
operationsCount = 0;
|
||||||
closedIssuesCount = 0;
|
closedIssuesCount = 0;
|
||||||
closedPullRequestsCount = 0;
|
closedPullRequestsCount = 0;
|
||||||
|
@ -65,6 +69,18 @@ export class Statistics {
|
||||||
return this._incrementUndoStaleIssuesCount(increment);
|
return this._incrementUndoStaleIssuesCount(increment);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
incrementUndoRottenItemsCount(
|
||||||
|
issue: Readonly<Issue>,
|
||||||
|
increment: Readonly<number> = 1
|
||||||
|
): Statistics {
|
||||||
|
if (issue.isPullRequest) {
|
||||||
|
return this._incrementUndoRottenPullRequestsCount(increment);
|
||||||
|
}
|
||||||
|
|
||||||
|
return this._incrementUndoRottenIssuesCount(increment);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
setOperationsCount(operationsCount: Readonly<number>): Statistics {
|
setOperationsCount(operationsCount: Readonly<number>): Statistics {
|
||||||
this.operationsCount = operationsCount;
|
this.operationsCount = operationsCount;
|
||||||
|
|
||||||
|
@ -222,6 +238,21 @@ export class Statistics {
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private _incrementUndoRottenPullRequestsCount(
|
||||||
|
increment: Readonly<number> = 1
|
||||||
|
): Statistics {
|
||||||
|
this.undoRottenPullRequestsCount += increment;
|
||||||
|
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
private _incrementUndoRottenIssuesCount(
|
||||||
|
increment: Readonly<number> = 1
|
||||||
|
): Statistics {
|
||||||
|
this.undoRottenIssuesCount += increment;
|
||||||
|
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
private _incrementUndoStalePullRequestsCount(
|
private _incrementUndoStalePullRequestsCount(
|
||||||
increment: Readonly<number> = 1
|
increment: Readonly<number> = 1
|
||||||
): Statistics {
|
): Statistics {
|
||||||
|
|
|
@ -2,18 +2,25 @@ export enum Option {
|
||||||
RepoToken = 'repo-token',
|
RepoToken = 'repo-token',
|
||||||
StaleIssueMessage = 'stale-issue-message',
|
StaleIssueMessage = 'stale-issue-message',
|
||||||
StalePrMessage = 'stale-pr-message',
|
StalePrMessage = 'stale-pr-message',
|
||||||
|
RottenIssueMessage = 'rotten-issue-message',
|
||||||
|
RottenPrMessage = 'rotten-pr-message',
|
||||||
CloseIssueMessage = 'close-issue-message',
|
CloseIssueMessage = 'close-issue-message',
|
||||||
ClosePrMessage = 'close-pr-message',
|
ClosePrMessage = 'close-pr-message',
|
||||||
DaysBeforeStale = 'days-before-stale',
|
DaysBeforeStale = 'days-before-stale',
|
||||||
DaysBeforeIssueStale = 'days-before-issue-stale',
|
DaysBeforeIssueStale = 'days-before-issue-stale',
|
||||||
DaysBeforePrStale = 'days-before-pr-stale',
|
DaysBeforePrStale = 'days-before-pr-stale',
|
||||||
|
DaysBeforeRotten = 'days-before-rotten',
|
||||||
|
DaysBeforeIssueRotten = 'days-before-issue-rotten',
|
||||||
|
DaysBeforePrRotten = 'days-before-pr-rotten',
|
||||||
DaysBeforeClose = 'days-before-close',
|
DaysBeforeClose = 'days-before-close',
|
||||||
DaysBeforeIssueClose = 'days-before-issue-close',
|
DaysBeforeIssueClose = 'days-before-issue-close',
|
||||||
DaysBeforePrClose = 'days-before-pr-close',
|
DaysBeforePrClose = 'days-before-pr-close',
|
||||||
StaleIssueLabel = 'stale-issue-label',
|
StaleIssueLabel = 'stale-issue-label',
|
||||||
|
RottenIssueLabel = 'rotten-issue-label',
|
||||||
CloseIssueLabel = 'close-issue-label',
|
CloseIssueLabel = 'close-issue-label',
|
||||||
ExemptIssueLabels = 'exempt-issue-labels',
|
ExemptIssueLabels = 'exempt-issue-labels',
|
||||||
StalePrLabel = 'stale-pr-label',
|
StalePrLabel = 'stale-pr-label',
|
||||||
|
RottenPrLabel = 'rotten-pr-label',
|
||||||
ClosePrLabel = 'close-pr-label',
|
ClosePrLabel = 'close-pr-label',
|
||||||
ExemptPrLabels = 'exempt-pr-labels',
|
ExemptPrLabels = 'exempt-pr-labels',
|
||||||
OnlyLabels = 'only-labels',
|
OnlyLabels = 'only-labels',
|
||||||
|
@ -24,6 +31,9 @@ export enum Option {
|
||||||
RemoveStaleWhenUpdated = 'remove-stale-when-updated',
|
RemoveStaleWhenUpdated = 'remove-stale-when-updated',
|
||||||
RemoveIssueStaleWhenUpdated = 'remove-issue-stale-when-updated',
|
RemoveIssueStaleWhenUpdated = 'remove-issue-stale-when-updated',
|
||||||
RemovePrStaleWhenUpdated = 'remove-pr-stale-when-updated',
|
RemovePrStaleWhenUpdated = 'remove-pr-stale-when-updated',
|
||||||
|
RemoveRottenWhenUpdated = 'remove-rotten-when-updated',
|
||||||
|
RemoveIssueRottenWhenUpdated = 'remove-issue-rotten-when-updated',
|
||||||
|
RemovePrRottenWhenUpdated = 'remove-pr-rotten-when-updated',
|
||||||
DebugOnly = 'debug-only',
|
DebugOnly = 'debug-only',
|
||||||
Ascending = 'ascending',
|
Ascending = 'ascending',
|
||||||
DeleteBranch = 'delete-branch',
|
DeleteBranch = 'delete-branch',
|
||||||
|
@ -44,6 +54,9 @@ export enum Option {
|
||||||
LabelsToRemoveWhenStale = 'labels-to-remove-when-stale',
|
LabelsToRemoveWhenStale = 'labels-to-remove-when-stale',
|
||||||
LabelsToRemoveWhenUnstale = 'labels-to-remove-when-unstale',
|
LabelsToRemoveWhenUnstale = 'labels-to-remove-when-unstale',
|
||||||
LabelsToAddWhenUnstale = 'labels-to-add-when-unstale',
|
LabelsToAddWhenUnstale = 'labels-to-add-when-unstale',
|
||||||
|
LabelsToRemoveWhenRotten = 'labels-to-remove-when-rotten',
|
||||||
|
LabelsToRemoveWhenUnrotten = 'labels-to-remove-when-unstale',
|
||||||
|
LabelsToAddWhenUnrotten = 'labels-to-add-when-unstale',
|
||||||
IgnoreUpdates = 'ignore-updates',
|
IgnoreUpdates = 'ignore-updates',
|
||||||
IgnoreIssueUpdates = 'ignore-issue-updates',
|
IgnoreIssueUpdates = 'ignore-issue-updates',
|
||||||
IgnorePrUpdates = 'ignore-pr-updates',
|
IgnorePrUpdates = 'ignore-pr-updates',
|
||||||
|
|
|
@ -1,21 +1,28 @@
|
||||||
import {IsoOrRfcDateString} from '../types/iso-or-rfc-date-string';
|
import { IsoOrRfcDateString } from '../types/iso-or-rfc-date-string';
|
||||||
|
|
||||||
export interface IIssuesProcessorOptions {
|
export interface IIssuesProcessorOptions {
|
||||||
repoToken: string;
|
repoToken: string;
|
||||||
staleIssueMessage: string;
|
staleIssueMessage: string;
|
||||||
stalePrMessage: string;
|
stalePrMessage: string;
|
||||||
|
rottenIssueMessage: string;
|
||||||
|
rottenPrMessage: string;
|
||||||
closeIssueMessage: string;
|
closeIssueMessage: string;
|
||||||
closePrMessage: string;
|
closePrMessage: string;
|
||||||
daysBeforeStale: number;
|
daysBeforeStale: number;
|
||||||
daysBeforeIssueStale: number; // Could be NaN
|
daysBeforeIssueStale: number; // Could be NaN
|
||||||
daysBeforePrStale: number; // Could be NaN
|
daysBeforePrStale: number; // Could be NaN
|
||||||
|
daysBeforeRotten: number;
|
||||||
|
daysBeforeIssueRotten: number; // Could be NaN
|
||||||
|
daysBeforePrRotten: number; // Could be NaN
|
||||||
daysBeforeClose: number;
|
daysBeforeClose: number;
|
||||||
daysBeforeIssueClose: number; // Could be NaN
|
daysBeforeIssueClose: number; // Could be NaN
|
||||||
daysBeforePrClose: number; // Could be NaN
|
daysBeforePrClose: number; // Could be NaN
|
||||||
staleIssueLabel: string;
|
staleIssueLabel: string;
|
||||||
|
rottenIssueLabel: string;
|
||||||
closeIssueLabel: string;
|
closeIssueLabel: string;
|
||||||
exemptIssueLabels: string;
|
exemptIssueLabels: string;
|
||||||
stalePrLabel: string;
|
stalePrLabel: string;
|
||||||
|
rottenPrLabel: string;
|
||||||
closePrLabel: string;
|
closePrLabel: string;
|
||||||
exemptPrLabels: string;
|
exemptPrLabels: string;
|
||||||
onlyLabels: string;
|
onlyLabels: string;
|
||||||
|
@ -28,6 +35,9 @@ export interface IIssuesProcessorOptions {
|
||||||
removeStaleWhenUpdated: boolean;
|
removeStaleWhenUpdated: boolean;
|
||||||
removeIssueStaleWhenUpdated: boolean | undefined;
|
removeIssueStaleWhenUpdated: boolean | undefined;
|
||||||
removePrStaleWhenUpdated: boolean | undefined;
|
removePrStaleWhenUpdated: boolean | undefined;
|
||||||
|
removeRottenWhenUpdated: boolean;
|
||||||
|
removeIssueRottenWhenUpdated: boolean | undefined;
|
||||||
|
removePrRottenWhenUpdated: boolean | undefined;
|
||||||
debugOnly: boolean;
|
debugOnly: boolean;
|
||||||
ascending: boolean;
|
ascending: boolean;
|
||||||
deleteBranch: boolean;
|
deleteBranch: boolean;
|
||||||
|
@ -48,6 +58,9 @@ export interface IIssuesProcessorOptions {
|
||||||
labelsToRemoveWhenStale: string;
|
labelsToRemoveWhenStale: string;
|
||||||
labelsToRemoveWhenUnstale: string;
|
labelsToRemoveWhenUnstale: string;
|
||||||
labelsToAddWhenUnstale: string;
|
labelsToAddWhenUnstale: string;
|
||||||
|
labelsToRemoveWhenRotten: string;
|
||||||
|
labelsToRemoveWhenUnrotten: string;
|
||||||
|
labelsToAddWhenUnrotten: string;
|
||||||
ignoreUpdates: boolean;
|
ignoreUpdates: boolean;
|
||||||
ignoreIssueUpdates: boolean | undefined;
|
ignoreIssueUpdates: boolean | undefined;
|
||||||
ignorePrUpdates: boolean | undefined;
|
ignorePrUpdates: boolean | undefined;
|
||||||
|
|
31
src/main.ts
31
src/main.ts
|
@ -46,6 +46,7 @@ async function _run(): Promise<void> {
|
||||||
|
|
||||||
await processOutput(
|
await processOutput(
|
||||||
issueProcessor.staleIssues,
|
issueProcessor.staleIssues,
|
||||||
|
issueProcessor.rottenIssues,
|
||||||
issueProcessor.closedIssues
|
issueProcessor.closedIssues
|
||||||
);
|
);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
|
@ -59,22 +60,31 @@ function _getAndValidateArgs(): IIssuesProcessorOptions {
|
||||||
repoToken: core.getInput('repo-token'),
|
repoToken: core.getInput('repo-token'),
|
||||||
staleIssueMessage: core.getInput('stale-issue-message'),
|
staleIssueMessage: core.getInput('stale-issue-message'),
|
||||||
stalePrMessage: core.getInput('stale-pr-message'),
|
stalePrMessage: core.getInput('stale-pr-message'),
|
||||||
|
rottenIssueMessage: core.getInput('rotten-issue-message'),
|
||||||
|
rottenPrMessage: core.getInput('rotten-pr-message'),
|
||||||
closeIssueMessage: core.getInput('close-issue-message'),
|
closeIssueMessage: core.getInput('close-issue-message'),
|
||||||
closePrMessage: core.getInput('close-pr-message'),
|
closePrMessage: core.getInput('close-pr-message'),
|
||||||
daysBeforeStale: parseFloat(
|
daysBeforeStale: parseFloat(
|
||||||
core.getInput('days-before-stale', {required: true})
|
core.getInput('days-before-stale', {required: true})
|
||||||
),
|
),
|
||||||
|
daysBeforeRotten: parseFloat(
|
||||||
|
core.getInput('days-before-rotten', {required: true})
|
||||||
|
),
|
||||||
daysBeforeIssueStale: parseFloat(core.getInput('days-before-issue-stale')),
|
daysBeforeIssueStale: parseFloat(core.getInput('days-before-issue-stale')),
|
||||||
daysBeforePrStale: parseFloat(core.getInput('days-before-pr-stale')),
|
daysBeforePrStale: parseFloat(core.getInput('days-before-pr-stale')),
|
||||||
|
daysBeforeIssueRotten: parseFloat(core.getInput('days-before-issue-rotten')),
|
||||||
|
daysBeforePrRotten: parseFloat(core.getInput('days-before-pr-rotten')),
|
||||||
daysBeforeClose: parseInt(
|
daysBeforeClose: parseInt(
|
||||||
core.getInput('days-before-close', {required: true})
|
core.getInput('days-before-close', {required: true})
|
||||||
),
|
),
|
||||||
daysBeforeIssueClose: parseInt(core.getInput('days-before-issue-close')),
|
daysBeforeIssueClose: parseInt(core.getInput('days-before-issue-close')),
|
||||||
daysBeforePrClose: parseInt(core.getInput('days-before-pr-close')),
|
daysBeforePrClose: parseInt(core.getInput('days-before-pr-close')),
|
||||||
staleIssueLabel: core.getInput('stale-issue-label', {required: true}),
|
staleIssueLabel: core.getInput('stale-issue-label', {required: true}),
|
||||||
|
rottenIssueLabel: core.getInput('rotten-issue-label', {required: true}),
|
||||||
closeIssueLabel: core.getInput('close-issue-label'),
|
closeIssueLabel: core.getInput('close-issue-label'),
|
||||||
exemptIssueLabels: core.getInput('exempt-issue-labels'),
|
exemptIssueLabels: core.getInput('exempt-issue-labels'),
|
||||||
stalePrLabel: core.getInput('stale-pr-label', {required: true}),
|
stalePrLabel: core.getInput('stale-pr-label', {required: true}),
|
||||||
|
rottenPrLabel: core.getInput('rotten-pr-label', {required: true}),
|
||||||
closePrLabel: core.getInput('close-pr-label'),
|
closePrLabel: core.getInput('close-pr-label'),
|
||||||
exemptPrLabels: core.getInput('exempt-pr-labels'),
|
exemptPrLabels: core.getInput('exempt-pr-labels'),
|
||||||
onlyLabels: core.getInput('only-labels'),
|
onlyLabels: core.getInput('only-labels'),
|
||||||
|
@ -95,6 +105,15 @@ function _getAndValidateArgs(): IIssuesProcessorOptions {
|
||||||
removePrStaleWhenUpdated: _toOptionalBoolean(
|
removePrStaleWhenUpdated: _toOptionalBoolean(
|
||||||
'remove-pr-stale-when-updated'
|
'remove-pr-stale-when-updated'
|
||||||
),
|
),
|
||||||
|
removeRottenWhenUpdated: !(
|
||||||
|
core.getInput('remove-rotten-when-updated') === 'false'
|
||||||
|
),
|
||||||
|
removeIssueRottenWhenUpdated: _toOptionalBoolean(
|
||||||
|
'remove-issue-rotten-when-updated'
|
||||||
|
),
|
||||||
|
removePrRottenWhenUpdated: _toOptionalBoolean(
|
||||||
|
'remove-pr-rotten-when-updated'
|
||||||
|
),
|
||||||
debugOnly: core.getInput('debug-only') === 'true',
|
debugOnly: core.getInput('debug-only') === 'true',
|
||||||
ascending: core.getInput('ascending') === 'true',
|
ascending: core.getInput('ascending') === 'true',
|
||||||
deleteBranch: core.getInput('delete-branch') === 'true',
|
deleteBranch: core.getInput('delete-branch') === 'true',
|
||||||
|
@ -118,6 +137,9 @@ function _getAndValidateArgs(): IIssuesProcessorOptions {
|
||||||
labelsToRemoveWhenStale: core.getInput('labels-to-remove-when-stale'),
|
labelsToRemoveWhenStale: core.getInput('labels-to-remove-when-stale'),
|
||||||
labelsToRemoveWhenUnstale: core.getInput('labels-to-remove-when-unstale'),
|
labelsToRemoveWhenUnstale: core.getInput('labels-to-remove-when-unstale'),
|
||||||
labelsToAddWhenUnstale: core.getInput('labels-to-add-when-unstale'),
|
labelsToAddWhenUnstale: core.getInput('labels-to-add-when-unstale'),
|
||||||
|
labelsToRemoveWhenRotten: core.getInput('labels-to-remove-when-rotten'),
|
||||||
|
labelsToRemoveWhenUnrotten: core.getInput('labels-to-remove-when-unrotten'),
|
||||||
|
labelsToAddWhenUnrotten: core.getInput('labels-to-add-when-unrotten'),
|
||||||
ignoreUpdates: core.getInput('ignore-updates') === 'true',
|
ignoreUpdates: core.getInput('ignore-updates') === 'true',
|
||||||
ignoreIssueUpdates: _toOptionalBoolean('ignore-issue-updates'),
|
ignoreIssueUpdates: _toOptionalBoolean('ignore-issue-updates'),
|
||||||
ignorePrUpdates: _toOptionalBoolean('ignore-pr-updates'),
|
ignorePrUpdates: _toOptionalBoolean('ignore-pr-updates'),
|
||||||
|
@ -133,6 +155,13 @@ function _getAndValidateArgs(): IIssuesProcessorOptions {
|
||||||
throw new Error(errorMessage);
|
throw new Error(errorMessage);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
for (const numberInput of ['days-before-rotten']) {
|
||||||
|
if (isNaN(parseFloat(core.getInput(numberInput)))) {
|
||||||
|
const errorMessage = `Option "${numberInput}" did not parse to a valid float`;
|
||||||
|
core.setFailed(errorMessage);
|
||||||
|
throw new Error(errorMessage);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
for (const numberInput of ['days-before-close', 'operations-per-run']) {
|
for (const numberInput of ['days-before-close', 'operations-per-run']) {
|
||||||
if (isNaN(parseInt(core.getInput(numberInput)))) {
|
if (isNaN(parseInt(core.getInput(numberInput)))) {
|
||||||
|
@ -167,9 +196,11 @@ function _getAndValidateArgs(): IIssuesProcessorOptions {
|
||||||
|
|
||||||
async function processOutput(
|
async function processOutput(
|
||||||
staledIssues: Issue[],
|
staledIssues: Issue[],
|
||||||
|
rottenIssues: Issue[],
|
||||||
closedIssues: Issue[]
|
closedIssues: Issue[]
|
||||||
): Promise<void> {
|
): Promise<void> {
|
||||||
core.setOutput('staled-issues-prs', JSON.stringify(staledIssues));
|
core.setOutput('staled-issues-prs', JSON.stringify(staledIssues));
|
||||||
|
core.setOutput('rotten-issues-prs', JSON.stringify(rottenIssues));
|
||||||
core.setOutput('closed-issues-prs', JSON.stringify(closedIssues));
|
core.setOutput('closed-issues-prs', JSON.stringify(closedIssues));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue