2021-03-08 11:56:52 +01:00
|
|
|
import * as core from '@actions/core';
|
2020-07-24 08:44:53 -04:00
|
|
|
import {context, getOctokit} from '@actions/github';
|
2021-01-15 12:51:24 +01:00
|
|
|
import {GitHub} from '@actions/github/lib/utils';
|
2020-07-24 08:44:53 -04:00
|
|
|
import {GetResponseTypeFromEndpointMethod} from '@octokit/types';
|
2021-03-08 11:56:52 +01:00
|
|
|
import chalk from 'chalk';
|
|
|
|
|
import {Option} from '../enums/option';
|
2021-02-13 12:09:37 +01:00
|
|
|
import {getHumanizedDate} from '../functions/dates/get-humanized-date';
|
|
|
|
|
import {isDateMoreRecentThan} from '../functions/dates/is-date-more-recent-than';
|
|
|
|
|
import {isValidDate} from '../functions/dates/is-valid-date';
|
2021-04-30 15:14:51 +02:00
|
|
|
import {isBoolean} from '../functions/is-boolean';
|
2021-02-13 12:09:37 +01:00
|
|
|
import {isLabeled} from '../functions/is-labeled';
|
|
|
|
|
import {shouldMarkWhenStale} from '../functions/should-mark-when-stale';
|
|
|
|
|
import {wordsToList} from '../functions/words-to-list';
|
|
|
|
|
import {IComment} from '../interfaces/comment';
|
|
|
|
|
import {IIssue} from '../interfaces/issue';
|
|
|
|
|
import {IIssueEvent} from '../interfaces/issue-event';
|
|
|
|
|
import {IIssuesProcessorOptions} from '../interfaces/issues-processor-options';
|
|
|
|
|
import {IPullRequest} from '../interfaces/pull-request';
|
2021-02-28 12:15:08 +01:00
|
|
|
import {Assignees} from './assignees';
|
2021-02-13 12:09:37 +01:00
|
|
|
import {Issue} from './issue';
|
|
|
|
|
import {IssueLogger} from './loggers/issue-logger';
|
|
|
|
|
import {Logger} from './loggers/logger';
|
|
|
|
|
import {Milestones} from './milestones';
|
2021-03-08 11:56:52 +01:00
|
|
|
import {Operations} from './operations';
|
2021-03-01 21:34:35 +01:00
|
|
|
import {Statistics} from './statistics';
|
2020-04-16 13:57:59 -04:00
|
|
|
|
|
|
|
|
/***
|
|
|
|
|
* Handle processing of issues for staleness/closure.
|
|
|
|
|
*/
|
2021-02-13 12:09:37 +01:00
|
|
|
export class IssuesProcessor {
|
2021-01-17 02:13:19 +01:00
|
|
|
private static _updatedSince(timestamp: string, num_days: number): boolean {
|
2021-01-16 14:28:29 +01:00
|
|
|
const daysInMillis = 1000 * 60 * 60 * 24 * num_days;
|
|
|
|
|
const millisSinceLastUpdated =
|
|
|
|
|
new Date().getTime() - new Date(timestamp).getTime();
|
|
|
|
|
|
|
|
|
|
return millisSinceLastUpdated <= daysInMillis;
|
|
|
|
|
}
|
|
|
|
|
|
2021-01-19 11:54:16 +01:00
|
|
|
private readonly _logger: Logger = new Logger();
|
2021-03-08 11:56:52 +01:00
|
|
|
private readonly _operations: Operations;
|
2021-03-01 21:34:35 +01:00
|
|
|
private readonly _statistics: Statistics | undefined;
|
2021-01-15 12:51:24 +01:00
|
|
|
readonly client: InstanceType<typeof GitHub>;
|
2021-02-13 12:09:37 +01:00
|
|
|
readonly options: IIssuesProcessorOptions;
|
2020-04-16 13:57:59 -04:00
|
|
|
readonly staleIssues: Issue[] = [];
|
|
|
|
|
readonly closedIssues: Issue[] = [];
|
2021-01-15 11:49:38 +00:00
|
|
|
readonly deletedBranchIssues: Issue[] = [];
|
2020-05-11 10:46:03 -04:00
|
|
|
readonly removedLabelIssues: Issue[] = [];
|
2020-04-16 13:57:59 -04:00
|
|
|
|
2021-03-01 21:34:35 +01:00
|
|
|
constructor(options: IIssuesProcessorOptions) {
|
2020-04-16 13:57:59 -04:00
|
|
|
this.options = options;
|
2021-03-01 21:34:35 +01:00
|
|
|
this.client = getOctokit(this.options.repoToken);
|
2021-03-08 11:56:52 +01:00
|
|
|
this._operations = new Operations(this.options);
|
|
|
|
|
|
|
|
|
|
this._logger.info(chalk.yellow('Starting the stale action process...'));
|
2020-05-11 10:46:03 -04:00
|
|
|
|
2020-04-16 13:57:59 -04:00
|
|
|
if (this.options.debugOnly) {
|
2021-03-08 11:56:52 +01:00
|
|
|
this._logger.warning(chalk.yellowBright('Executing in debug mode!'));
|
2021-01-19 11:54:16 +01:00
|
|
|
this._logger.warning(
|
2021-03-08 11:56:52 +01:00
|
|
|
chalk.yellowBright(
|
|
|
|
|
'The debug output will be written but no issues/PRs will be processed.'
|
|
|
|
|
)
|
2020-04-16 13:57:59 -04:00
|
|
|
);
|
|
|
|
|
}
|
2021-03-01 21:34:35 +01:00
|
|
|
|
|
|
|
|
if (this.options.enableStatistics) {
|
2021-03-08 11:56:52 +01:00
|
|
|
this._statistics = new Statistics();
|
2021-03-01 21:34:35 +01:00
|
|
|
}
|
2020-04-16 13:57:59 -04:00
|
|
|
}
|
|
|
|
|
|
2021-03-08 11:56:52 +01:00
|
|
|
async processIssues(page: Readonly<number> = 1): Promise<number> {
|
2020-04-16 13:57:59 -04:00
|
|
|
// get the next batch of issues
|
2021-03-01 21:34:35 +01:00
|
|
|
const issues: Issue[] = await this.getIssues(page);
|
|
|
|
|
const actor: string = await this.getActor();
|
2021-01-15 07:20:32 -05:00
|
|
|
|
2020-04-16 13:57:59 -04:00
|
|
|
if (issues.length <= 0) {
|
2021-03-08 11:56:52 +01:00
|
|
|
this._logger.info(
|
|
|
|
|
chalk.green('No more issues found to process. Exiting...')
|
|
|
|
|
);
|
|
|
|
|
this._statistics
|
|
|
|
|
?.setOperationsLeft(this._operations.getUnconsumedOperationsCount())
|
|
|
|
|
.logStats();
|
2021-03-01 21:34:35 +01:00
|
|
|
|
2021-03-08 11:56:52 +01:00
|
|
|
return this._operations.getOperationsLeftCount();
|
|
|
|
|
} else {
|
|
|
|
|
this._logger.info(
|
|
|
|
|
chalk.yellow(
|
|
|
|
|
`Processing the batch of issues ${chalk.cyan(
|
|
|
|
|
`#${page}`
|
|
|
|
|
)} containing ${chalk.cyan(issues.length)} issue${
|
|
|
|
|
issues.length > 1 ? 's' : ''
|
|
|
|
|
}...`
|
|
|
|
|
)
|
|
|
|
|
);
|
2020-04-16 13:57:59 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
for (const issue of issues.values()) {
|
2021-01-16 15:49:50 +01:00
|
|
|
const issueLogger: IssueLogger = new IssueLogger(issue);
|
2021-04-27 20:47:02 +02:00
|
|
|
this._statistics?.incrementProcessedItemsCount(issue);
|
2020-04-16 13:57:59 -04:00
|
|
|
|
2021-02-28 12:15:08 +01:00
|
|
|
issueLogger.info(`Found this $$type last updated ${issue.updated_at}`);
|
2020-04-16 13:57:59 -04:00
|
|
|
|
|
|
|
|
// calculate string based messages for this issue
|
2021-01-19 11:54:16 +01:00
|
|
|
const staleMessage: string = issue.isPullRequest
|
2020-04-16 13:57:59 -04:00
|
|
|
? this.options.stalePrMessage
|
|
|
|
|
: this.options.staleIssueMessage;
|
2021-01-19 11:54:16 +01:00
|
|
|
const closeMessage: string = issue.isPullRequest
|
2020-07-13 18:05:59 +01:00
|
|
|
? this.options.closePrMessage
|
|
|
|
|
: this.options.closeIssueMessage;
|
2021-01-19 11:54:16 +01:00
|
|
|
const staleLabel: string = issue.isPullRequest
|
2020-04-16 13:57:59 -04:00
|
|
|
? this.options.stalePrLabel
|
|
|
|
|
: this.options.staleIssueLabel;
|
2021-01-19 11:54:16 +01:00
|
|
|
const closeLabel: string = issue.isPullRequest
|
2020-09-08 21:32:42 +02:00
|
|
|
? this.options.closePrLabel
|
|
|
|
|
: this.options.closeIssueLabel;
|
2021-01-19 11:54:16 +01:00
|
|
|
const skipMessage = issue.isPullRequest
|
2020-07-24 17:38:48 +05:30
|
|
|
? this.options.skipStalePrMessage
|
|
|
|
|
: this.options.skipStaleIssueMessage;
|
2021-01-19 11:54:16 +01:00
|
|
|
const daysBeforeStale: number = issue.isPullRequest
|
2021-01-16 14:28:29 +01:00
|
|
|
? this._getDaysBeforePrStale()
|
|
|
|
|
: this._getDaysBeforeIssueStale();
|
2021-03-01 01:08:33 +01:00
|
|
|
const onlyLabels: string[] = wordsToList(this._getOnlyLabels(issue));
|
|
|
|
|
|
|
|
|
|
if (onlyLabels.length > 0) {
|
|
|
|
|
issueLogger.info(
|
|
|
|
|
`The option "onlyLabels" was specified to only processed the issues and pull requests with all those labels (${onlyLabels.length})`
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
const hasAllWhitelistedLabels: boolean = onlyLabels.every(
|
|
|
|
|
(label: Readonly<string>): boolean => {
|
|
|
|
|
return isLabeled(issue, label);
|
|
|
|
|
}
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
if (!hasAllWhitelistedLabels) {
|
|
|
|
|
issueLogger.info(
|
2021-04-28 22:33:42 +02:00
|
|
|
chalk.white('└──'),
|
2021-03-01 01:08:33 +01:00
|
|
|
`Skipping this $$type because it doesn't have all the required labels`
|
|
|
|
|
);
|
|
|
|
|
continue; // Don't process issues without all of the required labels
|
|
|
|
|
} else {
|
|
|
|
|
issueLogger.info(
|
2021-04-28 22:33:42 +02:00
|
|
|
chalk.white('├──'),
|
|
|
|
|
`All the required labels are present on this $$type`
|
|
|
|
|
);
|
|
|
|
|
issueLogger.info(
|
|
|
|
|
chalk.white('└──'),
|
|
|
|
|
`Continuing the process for this $$type`
|
2021-03-01 01:08:33 +01:00
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
} else {
|
2021-04-28 22:33:42 +02:00
|
|
|
issueLogger.info(`The option "onlyLabels" was not specified`);
|
2021-03-01 01:08:33 +01:00
|
|
|
issueLogger.info(
|
2021-04-28 22:33:42 +02:00
|
|
|
chalk.white('└──'),
|
|
|
|
|
`Continuing the process for this $$type`
|
2021-03-01 01:08:33 +01:00
|
|
|
);
|
|
|
|
|
}
|
2021-01-16 14:28:29 +01:00
|
|
|
|
2021-02-28 12:15:08 +01:00
|
|
|
issueLogger.info(`Days before $$type stale: ${daysBeforeStale}`);
|
2021-01-16 14:28:29 +01:00
|
|
|
|
|
|
|
|
const shouldMarkAsStale: boolean = shouldMarkWhenStale(daysBeforeStale);
|
2020-04-16 13:57:59 -04:00
|
|
|
|
2021-01-16 14:28:29 +01:00
|
|
|
if (!staleMessage && shouldMarkAsStale) {
|
2021-02-28 12:15:08 +01:00
|
|
|
issueLogger.info(`Skipping $$type due to empty stale message`);
|
2020-04-16 13:57:59 -04:00
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
2020-04-27 06:53:58 -05:00
|
|
|
if (issue.state === 'closed') {
|
2021-02-28 12:15:08 +01:00
|
|
|
issueLogger.info(`Skipping $$type because it is closed`);
|
2020-04-27 06:53:58 -05:00
|
|
|
continue; // don't process closed issues
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (issue.locked) {
|
2021-02-28 12:15:08 +01:00
|
|
|
issueLogger.info(`Skipping $$type because it is locked`);
|
2020-04-27 06:53:58 -05:00
|
|
|
continue; // don't process locked issues
|
|
|
|
|
}
|
|
|
|
|
|
2021-03-01 01:07:54 +01:00
|
|
|
// Try to remove the close label when not close/locked issue or PR
|
|
|
|
|
await this._removeCloseLabel(issue, closeLabel);
|
|
|
|
|
|
2021-01-18 02:22:36 +01:00
|
|
|
if (this.options.startDate) {
|
|
|
|
|
const startDate: Date = new Date(this.options.startDate);
|
|
|
|
|
const createdAt: Date = new Date(issue.created_at);
|
|
|
|
|
|
|
|
|
|
issueLogger.info(
|
|
|
|
|
`A start date was specified for the ${getHumanizedDate(startDate)} (${
|
|
|
|
|
this.options.startDate
|
|
|
|
|
})`
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
// Expecting that GitHub will always set a creation date on the issues and PRs
|
|
|
|
|
// But you never know!
|
|
|
|
|
if (!isValidDate(createdAt)) {
|
2021-03-08 11:56:52 +01:00
|
|
|
core.setFailed(
|
|
|
|
|
new Error(
|
|
|
|
|
`Invalid issue field: "created_at". Expected a valid date`
|
|
|
|
|
)
|
2021-01-18 02:22:36 +01:00
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
issueLogger.info(
|
2021-02-28 12:15:08 +01:00
|
|
|
`$$type created the ${getHumanizedDate(createdAt)} (${
|
2021-01-18 02:22:36 +01:00
|
|
|
issue.created_at
|
|
|
|
|
})`
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
if (!isDateMoreRecentThan(createdAt, startDate)) {
|
|
|
|
|
issueLogger.info(
|
2021-02-28 12:15:08 +01:00
|
|
|
`Skipping $$type because it was created before the specified start date`
|
2021-01-18 02:22:36 +01:00
|
|
|
);
|
|
|
|
|
|
|
|
|
|
continue; // don't process issues which were created before the start date
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2021-01-19 11:54:16 +01:00
|
|
|
if (issue.isStale) {
|
2021-02-28 12:15:08 +01:00
|
|
|
issueLogger.info(`This $$type has a stale label`);
|
2021-01-17 02:13:19 +01:00
|
|
|
} else {
|
2021-02-28 12:15:08 +01:00
|
|
|
issueLogger.info(`This $$type hasn't a stale label`);
|
2021-01-17 02:13:19 +01:00
|
|
|
}
|
|
|
|
|
|
2021-01-19 11:54:16 +01:00
|
|
|
const exemptLabels: string[] = wordsToList(
|
|
|
|
|
issue.isPullRequest
|
|
|
|
|
? this.options.exemptPrLabels
|
|
|
|
|
: this.options.exemptIssueLabels
|
|
|
|
|
);
|
|
|
|
|
|
2020-04-16 13:57:59 -04:00
|
|
|
if (
|
2021-01-17 02:13:19 +01:00
|
|
|
exemptLabels.some((exemptLabel: Readonly<string>): boolean =>
|
2020-11-20 12:48:33 +01:00
|
|
|
isLabeled(issue, exemptLabel)
|
2020-04-16 13:57:59 -04:00
|
|
|
)
|
|
|
|
|
) {
|
2021-01-19 11:54:16 +01:00
|
|
|
if (issue.isStale) {
|
2021-01-17 02:13:19 +01:00
|
|
|
issueLogger.info(`An exempt label was added after the stale label.`);
|
|
|
|
|
await this._removeStaleLabel(issue, staleLabel);
|
|
|
|
|
}
|
|
|
|
|
|
2021-02-28 12:15:08 +01:00
|
|
|
issueLogger.info(`Skipping $$type because it has an exempt label`);
|
2020-04-16 13:57:59 -04:00
|
|
|
continue; // don't process exempt issues
|
|
|
|
|
}
|
2021-03-01 11:05:53 -05:00
|
|
|
|
2021-04-28 22:33:42 +02:00
|
|
|
const anyOfLabels: string[] = wordsToList(this._getAnyOfLabels(issue));
|
2021-03-08 11:56:52 +01:00
|
|
|
|
2021-04-28 22:33:42 +02:00
|
|
|
if (anyOfLabels.length > 0) {
|
|
|
|
|
issueLogger.info(
|
|
|
|
|
`The option "anyOfLabels" was specified to only processed the issues and pull requests with one of those labels (${anyOfLabels.length})`
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
const hasOneOfWhitelistedLabels: boolean = anyOfLabels.some(
|
|
|
|
|
(label: Readonly<string>): boolean => {
|
|
|
|
|
return isLabeled(issue, label);
|
|
|
|
|
}
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
if (!hasOneOfWhitelistedLabels) {
|
|
|
|
|
issueLogger.info(
|
|
|
|
|
chalk.white('└──'),
|
|
|
|
|
`Skipping this $$type because it doesn't have one of the required labels`
|
|
|
|
|
);
|
|
|
|
|
continue; // Don't process issues without any of the required labels
|
|
|
|
|
} else {
|
|
|
|
|
issueLogger.info(
|
|
|
|
|
chalk.white('├──'),
|
|
|
|
|
`One of the required labels is present on this $$type`
|
|
|
|
|
);
|
|
|
|
|
issueLogger.info(
|
|
|
|
|
chalk.white('└──'),
|
|
|
|
|
`Continuing the process for this $$type`
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
issueLogger.info(`The option "anyOfLabels" was not specified`);
|
2021-03-01 11:05:53 -05:00
|
|
|
issueLogger.info(
|
2021-04-28 22:33:42 +02:00
|
|
|
chalk.white('└──'),
|
|
|
|
|
`Continuing the process for this $$type`
|
2021-03-01 11:05:53 -05:00
|
|
|
);
|
|
|
|
|
}
|
2020-04-16 13:57:59 -04:00
|
|
|
|
2021-01-19 11:54:16 +01:00
|
|
|
const milestones: Milestones = new Milestones(this.options, issue);
|
|
|
|
|
|
|
|
|
|
if (milestones.shouldExemptMilestones()) {
|
|
|
|
|
continue; // don't process exempt milestones
|
|
|
|
|
}
|
|
|
|
|
|
2021-02-28 12:15:08 +01:00
|
|
|
const assignees: Assignees = new Assignees(this.options, issue);
|
|
|
|
|
|
|
|
|
|
if (assignees.shouldExemptAssignees()) {
|
|
|
|
|
continue; // don't process exempt assignees
|
|
|
|
|
}
|
|
|
|
|
|
2020-05-26 09:16:38 -04:00
|
|
|
// should this issue be marked stale?
|
2021-02-13 12:09:37 +01:00
|
|
|
const shouldBeStale = !IssuesProcessor._updatedSince(
|
2020-05-26 09:16:38 -04:00
|
|
|
issue.updated_at,
|
2021-02-05 12:52:44 +01:00
|
|
|
daysBeforeStale
|
2020-05-26 09:16:38 -04:00
|
|
|
);
|
|
|
|
|
|
2020-05-11 11:15:05 -04:00
|
|
|
// determine if this issue needs to be marked stale first
|
2021-01-19 11:54:16 +01:00
|
|
|
if (!issue.isStale && shouldBeStale && shouldMarkAsStale) {
|
2021-01-16 15:49:50 +01:00
|
|
|
issueLogger.info(
|
2021-02-28 12:15:08 +01:00
|
|
|
`Marking $$type stale because it was last updated on ${issue.updated_at} and it does not have a stale label`
|
2020-04-16 13:57:59 -04:00
|
|
|
);
|
2021-01-17 02:13:19 +01:00
|
|
|
await this._markStale(issue, staleMessage, staleLabel, skipMessage);
|
2021-01-19 11:54:16 +01:00
|
|
|
issue.isStale = true; // this issue is now considered stale
|
2021-02-05 12:53:23 +01:00
|
|
|
} else if (!issue.isStale) {
|
|
|
|
|
issueLogger.info(
|
|
|
|
|
`Not marking as stale: shouldBeStale=${shouldBeStale}, shouldMarkAsStale=${shouldMarkAsStale}`
|
|
|
|
|
);
|
2020-05-11 11:15:05 -04:00
|
|
|
}
|
|
|
|
|
|
2020-05-26 09:16:38 -04:00
|
|
|
// process the issue if it was marked stale
|
2021-01-19 11:54:16 +01:00
|
|
|
if (issue.isStale) {
|
2021-02-28 12:15:08 +01:00
|
|
|
issueLogger.info(`Found a stale $$type`);
|
2021-01-17 02:13:19 +01:00
|
|
|
await this._processStaleIssue(
|
2020-07-13 18:05:59 +01:00
|
|
|
issue,
|
|
|
|
|
staleLabel,
|
2021-01-15 07:20:32 -05:00
|
|
|
actor,
|
2020-09-08 21:32:42 +02:00
|
|
|
closeMessage,
|
|
|
|
|
closeLabel
|
2020-07-13 18:05:59 +01:00
|
|
|
);
|
2020-04-16 13:57:59 -04:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2021-03-08 11:56:52 +01:00
|
|
|
if (this._operations.hasOperationsLeft()) {
|
|
|
|
|
this._logger.warning(
|
|
|
|
|
chalk.yellowBright('No more operations left! Exiting...')
|
|
|
|
|
);
|
2021-01-19 11:54:16 +01:00
|
|
|
this._logger.warning(
|
2021-03-08 11:56:52 +01:00
|
|
|
chalk.yellowBright(
|
|
|
|
|
`If you think that not enough issues were processed you could try to increase the quantity related to the ${this._logger.createOptionLink(
|
|
|
|
|
Option.OperationsPerRun
|
|
|
|
|
)} option which is currently set to ${chalk.cyan(
|
|
|
|
|
this.options.operationsPerRun
|
|
|
|
|
)}`
|
|
|
|
|
)
|
2021-01-19 11:54:16 +01:00
|
|
|
);
|
2021-03-08 11:56:52 +01:00
|
|
|
|
2020-05-26 09:16:38 -04:00
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
2021-03-08 11:56:52 +01:00
|
|
|
this._logger.info(
|
|
|
|
|
chalk.green(`Batch ${chalk.cyan(`#${page}`)} processed.`)
|
|
|
|
|
);
|
|
|
|
|
|
2020-04-16 13:57:59 -04:00
|
|
|
// do the next batch
|
|
|
|
|
return this.processIssues(page + 1);
|
|
|
|
|
}
|
|
|
|
|
|
2021-03-01 21:34:35 +01:00
|
|
|
// grab comments for an issue since a given date
|
|
|
|
|
async listIssueComments(
|
|
|
|
|
issueNumber: number,
|
|
|
|
|
sinceDate: string
|
|
|
|
|
): Promise<IComment[]> {
|
|
|
|
|
// find any comments since date on the given issue
|
|
|
|
|
try {
|
2021-03-08 11:56:52 +01:00
|
|
|
this._operations.consumeOperation();
|
2021-04-27 20:47:02 +02:00
|
|
|
this._statistics?.incrementFetchedItemsCommentsCount();
|
2021-03-01 21:34:35 +01:00
|
|
|
const comments = await this.client.issues.listComments({
|
|
|
|
|
owner: context.repo.owner,
|
|
|
|
|
repo: context.repo.repo,
|
|
|
|
|
issue_number: issueNumber,
|
|
|
|
|
since: sinceDate
|
|
|
|
|
});
|
|
|
|
|
return comments.data;
|
|
|
|
|
} catch (error) {
|
|
|
|
|
this._logger.error(`List issue comments error: ${error.message}`);
|
|
|
|
|
return Promise.resolve([]);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// get the actor from the GitHub token or context
|
|
|
|
|
async getActor(): Promise<string> {
|
|
|
|
|
let actor;
|
|
|
|
|
|
|
|
|
|
try {
|
2021-03-08 11:56:52 +01:00
|
|
|
this._operations.consumeOperation();
|
2021-03-01 21:34:35 +01:00
|
|
|
actor = await this.client.users.getAuthenticated();
|
|
|
|
|
} catch (error) {
|
|
|
|
|
return context.actor;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return actor.data.login;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// grab issues from github in batches of 100
|
|
|
|
|
async getIssues(page: number): Promise<Issue[]> {
|
|
|
|
|
// generate type for response
|
|
|
|
|
const endpoint = this.client.issues.listForRepo;
|
|
|
|
|
type OctoKitIssueList = GetResponseTypeFromEndpointMethod<typeof endpoint>;
|
|
|
|
|
|
|
|
|
|
try {
|
2021-03-08 11:56:52 +01:00
|
|
|
this._operations.consumeOperation();
|
2021-03-01 21:34:35 +01:00
|
|
|
const issueResult: OctoKitIssueList = await this.client.issues.listForRepo(
|
|
|
|
|
{
|
|
|
|
|
owner: context.repo.owner,
|
|
|
|
|
repo: context.repo.repo,
|
|
|
|
|
state: 'open',
|
|
|
|
|
per_page: 100,
|
|
|
|
|
direction: this.options.ascending ? 'asc' : 'desc',
|
|
|
|
|
page
|
|
|
|
|
}
|
|
|
|
|
);
|
2021-04-27 20:47:02 +02:00
|
|
|
this._statistics?.incrementFetchedItemsCount(issueResult.data.length);
|
2021-03-01 21:34:35 +01:00
|
|
|
|
|
|
|
|
return issueResult.data.map(
|
|
|
|
|
(issue: Readonly<IIssue>): Issue => new Issue(this.options, issue)
|
|
|
|
|
);
|
|
|
|
|
} catch (error) {
|
|
|
|
|
this._logger.error(`Get issues for repo error: ${error.message}`);
|
|
|
|
|
return Promise.resolve([]);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// returns the creation date of a given label on an issue (or nothing if no label existed)
|
|
|
|
|
///see https://developer.github.com/v3/activity/events/
|
|
|
|
|
async getLabelCreationDate(
|
|
|
|
|
issue: Issue,
|
|
|
|
|
label: string
|
|
|
|
|
): Promise<string | undefined> {
|
|
|
|
|
const issueLogger: IssueLogger = new IssueLogger(issue);
|
|
|
|
|
|
|
|
|
|
issueLogger.info(`Checking for label on $$type`);
|
|
|
|
|
|
2021-03-08 11:56:52 +01:00
|
|
|
this._operations.consumeOperation();
|
2021-04-27 20:47:02 +02:00
|
|
|
this._statistics?.incrementFetchedItemsEventsCount();
|
2021-03-01 21:34:35 +01:00
|
|
|
const options = this.client.issues.listEvents.endpoint.merge({
|
|
|
|
|
owner: context.repo.owner,
|
|
|
|
|
repo: context.repo.repo,
|
|
|
|
|
per_page: 100,
|
|
|
|
|
issue_number: issue.number
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
const events: IIssueEvent[] = await this.client.paginate(options);
|
|
|
|
|
const reversedEvents = events.reverse();
|
|
|
|
|
|
|
|
|
|
const staleLabeledEvent = reversedEvents.find(
|
|
|
|
|
event => event.event === 'labeled' && event.label.name === label
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
if (!staleLabeledEvent) {
|
|
|
|
|
// Must be old rather than labeled
|
|
|
|
|
return undefined;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return staleLabeledEvent.created_at;
|
|
|
|
|
}
|
|
|
|
|
|
2020-05-11 10:46:03 -04:00
|
|
|
// handle all of the stale issue logic when we find a stale issue
|
2021-01-17 02:13:19 +01:00
|
|
|
private async _processStaleIssue(
|
2020-05-11 10:46:03 -04:00
|
|
|
issue: Issue,
|
2020-07-13 18:05:59 +01:00
|
|
|
staleLabel: string,
|
2021-01-15 07:20:32 -05:00
|
|
|
actor: string,
|
2020-09-08 21:32:42 +02:00
|
|
|
closeMessage?: string,
|
|
|
|
|
closeLabel?: string
|
2020-05-11 10:46:03 -04:00
|
|
|
) {
|
2021-01-16 15:49:50 +01:00
|
|
|
const issueLogger: IssueLogger = new IssueLogger(issue);
|
2020-05-26 09:16:38 -04:00
|
|
|
const markedStaleOn: string =
|
2021-03-01 21:34:35 +01:00
|
|
|
(await this.getLabelCreationDate(issue, staleLabel)) || issue.updated_at;
|
2021-04-30 15:14:51 +02:00
|
|
|
issueLogger.info(`$$type marked stale on: ${chalk.cyan(markedStaleOn)}`);
|
2020-05-11 10:46:03 -04:00
|
|
|
|
2021-01-17 02:13:19 +01:00
|
|
|
const issueHasComments: boolean = await this._hasCommentsSince(
|
2020-05-11 10:46:03 -04:00
|
|
|
issue,
|
2021-01-15 07:20:32 -05:00
|
|
|
markedStaleOn,
|
|
|
|
|
actor
|
2020-05-11 10:46:03 -04:00
|
|
|
);
|
2021-04-30 15:14:51 +02:00
|
|
|
issueLogger.info(
|
|
|
|
|
`$$type has been commented on: ${chalk.cyan(issueHasComments)}`
|
|
|
|
|
);
|
2020-05-26 09:16:38 -04:00
|
|
|
|
2021-03-08 11:56:52 +01:00
|
|
|
const daysBeforeClose: number = issue.isPullRequest
|
2021-01-16 14:28:29 +01:00
|
|
|
? this._getDaysBeforePrClose()
|
|
|
|
|
: this._getDaysBeforeIssueClose();
|
|
|
|
|
|
2021-02-28 12:15:08 +01:00
|
|
|
issueLogger.info(`Days before $$type close: ${daysBeforeClose}`);
|
2021-01-16 14:28:29 +01:00
|
|
|
|
2021-02-13 12:09:37 +01:00
|
|
|
const issueHasUpdate: boolean = IssuesProcessor._updatedSince(
|
2020-05-11 10:46:03 -04:00
|
|
|
issue.updated_at,
|
2021-01-16 14:28:29 +01:00
|
|
|
daysBeforeClose
|
2020-05-11 10:46:03 -04:00
|
|
|
);
|
2021-04-30 15:14:51 +02:00
|
|
|
issueLogger.info(`$$type has been updated: ${chalk.cyan(issueHasUpdate)}`);
|
2020-05-11 10:46:03 -04:00
|
|
|
|
2020-05-26 09:16:38 -04:00
|
|
|
// should we un-stale this issue?
|
2021-04-30 15:14:51 +02:00
|
|
|
if (this._shouldRemoveStaleWhenUpdated(issue) && issueHasComments) {
|
2021-01-17 02:13:19 +01:00
|
|
|
await this._removeStaleLabel(issue, staleLabel);
|
2021-04-30 15:14:51 +02:00
|
|
|
|
|
|
|
|
issueLogger.info(`Skipping the process since the $$type is now un-stale`);
|
|
|
|
|
|
|
|
|
|
return; // nothing to do because it is no longer stale
|
2020-05-26 09:16:38 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// now start closing logic
|
2021-01-16 14:28:29 +01:00
|
|
|
if (daysBeforeClose < 0) {
|
2020-05-26 09:16:38 -04:00
|
|
|
return; // nothing to do because we aren't closing stale issues
|
2020-05-12 15:23:00 -04:00
|
|
|
}
|
2020-05-11 10:46:03 -04:00
|
|
|
|
|
|
|
|
if (!issueHasComments && !issueHasUpdate) {
|
2021-01-16 15:49:50 +01:00
|
|
|
issueLogger.info(
|
2021-02-28 12:15:08 +01:00
|
|
|
`Closing $$type because it was last updated on ${issue.updated_at}`
|
2020-05-11 10:46:03 -04:00
|
|
|
);
|
2021-01-17 02:13:19 +01:00
|
|
|
await this._closeIssue(issue, closeMessage, closeLabel);
|
2021-01-15 11:49:38 +00:00
|
|
|
|
|
|
|
|
if (this.options.deleteBranch && issue.pull_request) {
|
2021-01-16 15:49:50 +01:00
|
|
|
issueLogger.info(
|
2021-02-28 12:15:08 +01:00
|
|
|
`Deleting branch for as delete-branch option was specified`
|
2021-01-15 11:49:38 +00:00
|
|
|
);
|
2021-01-17 02:13:19 +01:00
|
|
|
await this._deleteBranch(issue);
|
2021-01-15 11:49:38 +00:00
|
|
|
this.deletedBranchIssues.push(issue);
|
|
|
|
|
}
|
2020-05-11 10:46:03 -04:00
|
|
|
} else {
|
2021-01-16 15:49:50 +01:00
|
|
|
issueLogger.info(
|
2021-02-28 12:15:08 +01:00
|
|
|
`Stale $$type is not old enough to close yet (hasComments? ${issueHasComments}, hasUpdate? ${issueHasUpdate})`
|
2020-05-26 09:16:38 -04:00
|
|
|
);
|
2020-05-11 10:46:03 -04:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// checks to see if a given issue is still stale (has had activity on it)
|
2021-01-17 02:13:19 +01:00
|
|
|
private async _hasCommentsSince(
|
2020-05-11 10:46:03 -04:00
|
|
|
issue: Issue,
|
2021-01-15 07:20:32 -05:00
|
|
|
sinceDate: string,
|
|
|
|
|
actor: string
|
2020-05-11 10:46:03 -04:00
|
|
|
): Promise<boolean> {
|
2021-01-16 15:49:50 +01:00
|
|
|
const issueLogger: IssueLogger = new IssueLogger(issue);
|
|
|
|
|
|
2021-02-28 12:15:08 +01:00
|
|
|
issueLogger.info(`Checking for comments on $$type since ${sinceDate}`);
|
2020-05-11 10:46:03 -04:00
|
|
|
|
|
|
|
|
if (!sinceDate) {
|
2020-05-26 09:16:38 -04:00
|
|
|
return true;
|
2020-05-11 10:46:03 -04:00
|
|
|
}
|
|
|
|
|
|
2020-05-26 09:16:38 -04:00
|
|
|
// find any comments since the date
|
2021-03-01 21:34:35 +01:00
|
|
|
const comments = await this.listIssueComments(issue.number, sinceDate);
|
2020-05-11 10:46:03 -04:00
|
|
|
|
2020-05-26 09:16:38 -04:00
|
|
|
const filteredComments = comments.filter(
|
2021-01-15 07:35:41 -05:00
|
|
|
comment => comment.user.type === 'User' && comment.user.login !== actor
|
2020-05-26 09:16:38 -04:00
|
|
|
);
|
|
|
|
|
|
2021-01-16 15:49:50 +01:00
|
|
|
issueLogger.info(
|
2020-11-02 16:43:44 -05:00
|
|
|
`Comments not made by actor or another bot: ${filteredComments.length}`
|
2020-05-18 20:33:59 -04:00
|
|
|
);
|
2020-05-26 09:16:38 -04:00
|
|
|
|
|
|
|
|
// if there are any user comments returned
|
|
|
|
|
return filteredComments.length > 0;
|
2020-05-11 10:46:03 -04:00
|
|
|
}
|
|
|
|
|
|
2020-04-16 13:57:59 -04:00
|
|
|
// Mark an issue as stale with a comment and a label
|
2021-01-17 02:13:19 +01:00
|
|
|
private async _markStale(
|
2020-04-16 13:57:59 -04:00
|
|
|
issue: Issue,
|
|
|
|
|
staleMessage: string,
|
2020-07-24 17:38:48 +05:30
|
|
|
staleLabel: string,
|
|
|
|
|
skipMessage: boolean
|
2020-04-16 13:57:59 -04:00
|
|
|
): Promise<void> {
|
2021-01-16 15:49:50 +01:00
|
|
|
const issueLogger: IssueLogger = new IssueLogger(issue);
|
|
|
|
|
|
2021-02-28 12:15:08 +01:00
|
|
|
issueLogger.info(`Marking $$type as stale`);
|
2020-04-16 13:57:59 -04:00
|
|
|
this.staleIssues.push(issue);
|
|
|
|
|
|
2020-06-08 12:45:13 -04:00
|
|
|
// if the issue is being marked stale, the updated date should be changed to right now
|
|
|
|
|
// so that close calculations work correctly
|
2020-05-29 09:32:20 -04:00
|
|
|
const newUpdatedAtDate: Date = new Date();
|
|
|
|
|
issue.updated_at = newUpdatedAtDate.toString();
|
|
|
|
|
|
2020-04-16 13:57:59 -04:00
|
|
|
if (this.options.debugOnly) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
2020-07-24 17:38:48 +05:30
|
|
|
if (!skipMessage) {
|
|
|
|
|
try {
|
2021-03-08 11:56:52 +01:00
|
|
|
this._operations.consumeOperation();
|
2021-04-27 20:47:02 +02:00
|
|
|
this._statistics?.incrementAddedItemsComment(issue);
|
2020-07-24 17:38:48 +05:30
|
|
|
await this.client.issues.createComment({
|
2020-07-24 08:44:53 -04:00
|
|
|
owner: context.repo.owner,
|
|
|
|
|
repo: context.repo.repo,
|
2020-07-24 17:38:48 +05:30
|
|
|
issue_number: issue.number,
|
|
|
|
|
body: staleMessage
|
|
|
|
|
});
|
|
|
|
|
} catch (error) {
|
2021-04-30 15:14:51 +02:00
|
|
|
issueLogger.error(`Error when creating a comment: ${error.message}`);
|
2020-07-24 17:38:48 +05:30
|
|
|
}
|
2020-06-08 14:18:22 -04:00
|
|
|
}
|
2020-04-16 13:57:59 -04:00
|
|
|
|
2020-06-08 14:18:22 -04:00
|
|
|
try {
|
2021-03-08 11:56:52 +01:00
|
|
|
this._operations.consumeOperation();
|
2021-04-27 20:47:02 +02:00
|
|
|
this._statistics?.incrementAddedItemsLabel(issue);
|
|
|
|
|
this._statistics?.incrementStaleItemsCount(issue);
|
2020-06-08 14:18:22 -04:00
|
|
|
await this.client.issues.addLabels({
|
2020-07-24 08:44:53 -04:00
|
|
|
owner: context.repo.owner,
|
|
|
|
|
repo: context.repo.repo,
|
2020-06-08 14:18:22 -04:00
|
|
|
issue_number: issue.number,
|
|
|
|
|
labels: [staleLabel]
|
|
|
|
|
});
|
|
|
|
|
} catch (error) {
|
2021-04-30 15:14:51 +02:00
|
|
|
issueLogger.error(`Error when adding a label: ${error.message}`);
|
2020-06-08 14:18:22 -04:00
|
|
|
}
|
2020-04-16 13:57:59 -04:00
|
|
|
}
|
|
|
|
|
|
2020-05-11 10:46:03 -04:00
|
|
|
// Close an issue based on staleness
|
2021-01-17 02:13:19 +01:00
|
|
|
private async _closeIssue(
|
2020-09-08 21:32:42 +02:00
|
|
|
issue: Issue,
|
|
|
|
|
closeMessage?: string,
|
|
|
|
|
closeLabel?: string
|
|
|
|
|
): Promise<void> {
|
2021-01-16 15:49:50 +01:00
|
|
|
const issueLogger: IssueLogger = new IssueLogger(issue);
|
|
|
|
|
|
2021-02-28 12:15:08 +01:00
|
|
|
issueLogger.info(`Closing $$type for being stale`);
|
2020-04-16 13:57:59 -04:00
|
|
|
this.closedIssues.push(issue);
|
|
|
|
|
|
|
|
|
|
if (this.options.debugOnly) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
2020-07-13 18:05:59 +01:00
|
|
|
if (closeMessage) {
|
|
|
|
|
try {
|
2021-03-08 11:56:52 +01:00
|
|
|
this._operations.consumeOperation();
|
2021-04-27 20:47:02 +02:00
|
|
|
this._statistics?.incrementAddedItemsComment(issue);
|
2020-07-13 18:05:59 +01:00
|
|
|
await this.client.issues.createComment({
|
2020-07-24 08:44:53 -04:00
|
|
|
owner: context.repo.owner,
|
|
|
|
|
repo: context.repo.repo,
|
2020-07-13 18:05:59 +01:00
|
|
|
issue_number: issue.number,
|
|
|
|
|
body: closeMessage
|
|
|
|
|
});
|
|
|
|
|
} catch (error) {
|
2021-04-30 15:14:51 +02:00
|
|
|
issueLogger.error(`Error when creating a comment: ${error.message}`);
|
2020-07-13 18:05:59 +01:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2020-09-08 21:32:42 +02:00
|
|
|
if (closeLabel) {
|
|
|
|
|
try {
|
2021-03-08 11:56:52 +01:00
|
|
|
this._operations.consumeOperation();
|
2021-04-27 20:47:02 +02:00
|
|
|
this._statistics?.incrementAddedItemsLabel(issue);
|
2020-09-08 21:32:42 +02:00
|
|
|
await this.client.issues.addLabels({
|
|
|
|
|
owner: context.repo.owner,
|
|
|
|
|
repo: context.repo.repo,
|
|
|
|
|
issue_number: issue.number,
|
|
|
|
|
labels: [closeLabel]
|
|
|
|
|
});
|
|
|
|
|
} catch (error) {
|
2021-04-30 15:14:51 +02:00
|
|
|
issueLogger.error(`Error when adding a label: ${error.message}`);
|
2020-09-08 21:32:42 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2020-06-08 14:18:22 -04:00
|
|
|
try {
|
2021-03-08 11:56:52 +01:00
|
|
|
this._operations.consumeOperation();
|
2021-04-27 20:47:02 +02:00
|
|
|
this._statistics?.incrementClosedItemsCount(issue);
|
2020-06-08 14:18:22 -04:00
|
|
|
await this.client.issues.update({
|
2020-07-24 08:44:53 -04:00
|
|
|
owner: context.repo.owner,
|
|
|
|
|
repo: context.repo.repo,
|
2020-06-08 14:18:22 -04:00
|
|
|
issue_number: issue.number,
|
|
|
|
|
state: 'closed'
|
|
|
|
|
});
|
|
|
|
|
} catch (error) {
|
2021-04-30 15:14:51 +02:00
|
|
|
issueLogger.error(`Error when updating this $$type: ${error.message}`);
|
2020-06-08 14:18:22 -04:00
|
|
|
}
|
2020-04-16 13:57:59 -04:00
|
|
|
}
|
|
|
|
|
|
2021-01-17 02:13:19 +01:00
|
|
|
private async _getPullRequest(
|
|
|
|
|
issue: Issue
|
2021-03-01 21:34:35 +01:00
|
|
|
): Promise<IPullRequest | undefined | void> {
|
2021-01-16 15:49:50 +01:00
|
|
|
const issueLogger: IssueLogger = new IssueLogger(issue);
|
2021-03-01 21:34:35 +01:00
|
|
|
|
|
|
|
|
if (this.options.debugOnly) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
2021-01-15 11:49:38 +00:00
|
|
|
|
|
|
|
|
try {
|
2021-03-08 11:56:52 +01:00
|
|
|
this._operations.consumeOperation();
|
2021-03-01 21:34:35 +01:00
|
|
|
this._statistics?.incrementFetchedPullRequestsCount();
|
2021-01-15 11:49:38 +00:00
|
|
|
const pullRequest = await this.client.pulls.get({
|
|
|
|
|
owner: context.repo.owner,
|
|
|
|
|
repo: context.repo.repo,
|
2021-01-16 15:49:50 +01:00
|
|
|
pull_number: issue.number
|
2021-01-15 11:49:38 +00:00
|
|
|
});
|
|
|
|
|
|
|
|
|
|
return pullRequest.data;
|
|
|
|
|
} catch (error) {
|
2021-04-30 15:14:51 +02:00
|
|
|
issueLogger.error(`Error when getting this $$type: ${error.message}`);
|
2021-01-15 11:49:38 +00:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Delete the branch on closed pull request
|
2021-01-17 02:13:19 +01:00
|
|
|
private async _deleteBranch(issue: Issue): Promise<void> {
|
2021-01-16 15:49:50 +01:00
|
|
|
const issueLogger: IssueLogger = new IssueLogger(issue);
|
|
|
|
|
|
2021-02-28 12:15:08 +01:00
|
|
|
issueLogger.info(`Delete branch from closed $$type - ${issue.title}`);
|
2021-01-15 11:49:38 +00:00
|
|
|
|
2021-01-17 02:13:19 +01:00
|
|
|
const pullRequest = await this._getPullRequest(issue);
|
2021-01-15 11:49:38 +00:00
|
|
|
|
|
|
|
|
if (!pullRequest) {
|
2021-01-16 15:49:50 +01:00
|
|
|
issueLogger.info(
|
2021-02-28 12:15:08 +01:00
|
|
|
`Not deleting branch as pull request not found for this $$type`
|
2021-01-15 11:49:38 +00:00
|
|
|
);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
2021-03-01 21:34:35 +01:00
|
|
|
if (this.options.debugOnly) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
2021-01-15 11:49:38 +00:00
|
|
|
const branch = pullRequest.head.ref;
|
2021-04-30 15:14:51 +02:00
|
|
|
issueLogger.info(
|
|
|
|
|
`Deleting the branch "${chalk.cyan(branch)}" from closed $$type`
|
|
|
|
|
);
|
2021-01-15 11:49:38 +00:00
|
|
|
|
|
|
|
|
try {
|
2021-03-08 11:56:52 +01:00
|
|
|
this._operations.consumeOperation();
|
2021-03-01 21:34:35 +01:00
|
|
|
this._statistics?.incrementDeletedBranchesCount();
|
2021-01-15 11:49:38 +00:00
|
|
|
await this.client.git.deleteRef({
|
|
|
|
|
owner: context.repo.owner,
|
|
|
|
|
repo: context.repo.repo,
|
|
|
|
|
ref: `heads/${branch}`
|
|
|
|
|
});
|
|
|
|
|
} catch (error) {
|
2021-01-16 15:49:50 +01:00
|
|
|
issueLogger.error(
|
2021-04-30 15:14:51 +02:00
|
|
|
`Error when deleting the branch "${chalk.cyan(branch)}" from $$type: ${
|
|
|
|
|
error.message
|
|
|
|
|
}`
|
2021-01-15 11:49:38 +00:00
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2021-04-27 20:47:02 +02:00
|
|
|
// Remove a label from an issue or a pull request
|
2021-01-17 02:13:19 +01:00
|
|
|
private async _removeLabel(issue: Issue, label: string): Promise<void> {
|
2021-01-16 15:49:50 +01:00
|
|
|
const issueLogger: IssueLogger = new IssueLogger(issue);
|
|
|
|
|
|
2021-04-30 15:14:51 +02:00
|
|
|
issueLogger.info(
|
|
|
|
|
`Removing the label "${chalk.cyan(label)}" from the $$type...`
|
|
|
|
|
);
|
2020-05-11 10:46:03 -04:00
|
|
|
this.removedLabelIssues.push(issue);
|
|
|
|
|
|
|
|
|
|
if (this.options.debugOnly) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
2020-06-08 14:18:22 -04:00
|
|
|
try {
|
2021-03-08 11:56:52 +01:00
|
|
|
this._operations.consumeOperation();
|
2021-04-27 20:47:02 +02:00
|
|
|
this._statistics?.incrementDeletedItemsLabelsCount(issue);
|
2020-06-08 14:18:22 -04:00
|
|
|
await this.client.issues.removeLabel({
|
2020-07-24 08:44:53 -04:00
|
|
|
owner: context.repo.owner,
|
|
|
|
|
repo: context.repo.repo,
|
2020-06-08 14:18:22 -04:00
|
|
|
issue_number: issue.number,
|
2021-01-15 12:51:24 +01:00
|
|
|
name: label
|
2020-06-08 14:18:22 -04:00
|
|
|
});
|
2021-04-30 15:14:51 +02:00
|
|
|
issueLogger.info(`The label "${chalk.cyan(label)}" was removed`);
|
2020-06-08 14:18:22 -04:00
|
|
|
} catch (error) {
|
2021-04-30 15:14:51 +02:00
|
|
|
issueLogger.error(
|
|
|
|
|
`Error when removing the label: "${chalk.cyan(error.message)}"`
|
|
|
|
|
);
|
2020-06-08 14:18:22 -04:00
|
|
|
}
|
2020-05-11 10:46:03 -04:00
|
|
|
}
|
|
|
|
|
|
2021-01-16 14:28:29 +01:00
|
|
|
private _getDaysBeforeIssueStale(): number {
|
|
|
|
|
return isNaN(this.options.daysBeforeIssueStale)
|
|
|
|
|
? this.options.daysBeforeStale
|
|
|
|
|
: this.options.daysBeforeIssueStale;
|
|
|
|
|
}
|
2020-05-11 10:46:03 -04:00
|
|
|
|
2021-01-16 14:28:29 +01:00
|
|
|
private _getDaysBeforePrStale(): number {
|
|
|
|
|
return isNaN(this.options.daysBeforePrStale)
|
|
|
|
|
? this.options.daysBeforeStale
|
|
|
|
|
: this.options.daysBeforePrStale;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private _getDaysBeforeIssueClose(): number {
|
|
|
|
|
return isNaN(this.options.daysBeforeIssueClose)
|
|
|
|
|
? this.options.daysBeforeClose
|
|
|
|
|
: this.options.daysBeforeIssueClose;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private _getDaysBeforePrClose(): number {
|
|
|
|
|
return isNaN(this.options.daysBeforePrClose)
|
|
|
|
|
? this.options.daysBeforeClose
|
|
|
|
|
: this.options.daysBeforePrClose;
|
2020-04-16 13:57:59 -04:00
|
|
|
}
|
2021-01-17 02:13:19 +01:00
|
|
|
|
2021-03-01 01:08:33 +01:00
|
|
|
private _getOnlyLabels(issue: Issue): string {
|
|
|
|
|
if (issue.isPullRequest) {
|
|
|
|
|
if (this.options.onlyPrLabels !== '') {
|
|
|
|
|
return this.options.onlyPrLabels;
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
if (this.options.onlyIssueLabels !== '') {
|
|
|
|
|
return this.options.onlyIssueLabels;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return this.options.onlyLabels;
|
|
|
|
|
}
|
|
|
|
|
|
2021-04-28 22:33:42 +02:00
|
|
|
private _getAnyOfLabels(issue: Issue): string {
|
|
|
|
|
if (issue.isPullRequest) {
|
|
|
|
|
if (this.options.anyOfPrLabels !== '') {
|
|
|
|
|
return this.options.anyOfPrLabels;
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
if (this.options.anyOfIssueLabels !== '') {
|
|
|
|
|
return this.options.anyOfIssueLabels;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return this.options.anyOfLabels;
|
|
|
|
|
}
|
|
|
|
|
|
2021-04-30 15:14:51 +02:00
|
|
|
private _shouldRemoveStaleWhenUpdated(issue: Issue): boolean {
|
|
|
|
|
if (issue.isPullRequest) {
|
|
|
|
|
if (isBoolean(this.options.removePrStaleWhenUpdated)) {
|
|
|
|
|
return this.options.removePrStaleWhenUpdated;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return this.options.removeStaleWhenUpdated;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (isBoolean(this.options.removeIssueStaleWhenUpdated)) {
|
|
|
|
|
return this.options.removeIssueStaleWhenUpdated;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return this.options.removeStaleWhenUpdated;
|
|
|
|
|
}
|
|
|
|
|
|
2021-01-17 02:13:19 +01:00
|
|
|
private async _removeStaleLabel(
|
2021-01-19 11:54:16 +01:00
|
|
|
issue: Issue,
|
2021-01-17 02:13:19 +01:00
|
|
|
staleLabel: Readonly<string>
|
|
|
|
|
): Promise<void> {
|
|
|
|
|
const issueLogger: IssueLogger = new IssueLogger(issue);
|
|
|
|
|
|
2021-03-01 01:07:54 +01:00
|
|
|
issueLogger.info(
|
|
|
|
|
`The $$type is no longer stale. Removing the stale label...`
|
|
|
|
|
);
|
2021-01-17 02:13:19 +01:00
|
|
|
|
2021-03-01 21:34:35 +01:00
|
|
|
await this._removeLabel(issue, staleLabel);
|
2021-04-27 20:47:02 +02:00
|
|
|
this._statistics?.incrementUndoStaleItemsCount(issue);
|
2021-01-17 02:13:19 +01:00
|
|
|
}
|
2021-03-01 01:07:54 +01:00
|
|
|
|
|
|
|
|
private async _removeCloseLabel(
|
|
|
|
|
issue: Issue,
|
|
|
|
|
closeLabel: Readonly<string | undefined>
|
|
|
|
|
): Promise<void> {
|
|
|
|
|
const issueLogger: IssueLogger = new IssueLogger(issue);
|
|
|
|
|
|
|
|
|
|
issueLogger.info(
|
|
|
|
|
`The $$type is not closed nor locked. Trying to remove the close label...`
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
if (!closeLabel) {
|
|
|
|
|
issueLogger.info(`There is no close label on this $$type. Skip`);
|
|
|
|
|
|
|
|
|
|
return Promise.resolve();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (isLabeled(issue, closeLabel)) {
|
|
|
|
|
issueLogger.info(
|
2021-04-30 15:14:51 +02:00
|
|
|
`The $$type has a close label "${chalk.cyan(
|
|
|
|
|
closeLabel
|
|
|
|
|
)}". Removing the close label...`
|
2021-03-01 01:07:54 +01:00
|
|
|
);
|
|
|
|
|
|
2021-03-01 21:34:35 +01:00
|
|
|
await this._removeLabel(issue, closeLabel);
|
2021-04-27 20:47:02 +02:00
|
|
|
this._statistics?.incrementDeletedCloseItemsLabelsCount(issue);
|
2021-03-01 01:07:54 +01:00
|
|
|
}
|
|
|
|
|
}
|
2020-04-16 13:57:59 -04:00
|
|
|
}
|