diff --git a/__tests__/any-of-labels.spec.ts b/__tests__/any-of-labels.spec.ts index cfed44bd..6aa15601 100644 --- a/__tests__/any-of-labels.spec.ts +++ b/__tests__/any-of-labels.spec.ts @@ -1112,14 +1112,12 @@ class IssuesProcessorBuilder { issues(issues: Partial[]): IssuesProcessorBuilder { this.issuesOrPrs( - issues.map( - (issue: Readonly>): Partial => { - return { - ...issue, - pull_request: null - }; - } - ) + issues.map((issue: Readonly>): Partial => { + return { + ...issue, + pull_request: null + }; + }) ); return this; @@ -1127,14 +1125,12 @@ class IssuesProcessorBuilder { prs(issues: Partial[]): IssuesProcessorBuilder { this.issuesOrPrs( - issues.map( - (issue: Readonly>): Partial => { - return { - ...issue, - pull_request: {key: 'value'} - }; - } - ) + issues.map((issue: Readonly>): Partial => { + return { + ...issue, + pull_request: {key: 'value'} + }; + }) ); return this; diff --git a/__tests__/functions/generate-issue.ts b/__tests__/functions/generate-issue.ts index 3b3fe433..6817d8e4 100644 --- a/__tests__/functions/generate-issue.ts +++ b/__tests__/functions/generate-issue.ts @@ -32,12 +32,10 @@ export function generateIssue( title: milestone } : undefined, - assignees: assignees.map( - (assignee: Readonly): IAssignee => { - return { - login: assignee - }; - } - ) + assignees: assignees.map((assignee: Readonly): IAssignee => { + return { + login: assignee + }; + }) }); } diff --git a/__tests__/only-labels.spec.ts b/__tests__/only-labels.spec.ts index d8ceddc9..042642ba 100644 --- a/__tests__/only-labels.spec.ts +++ b/__tests__/only-labels.spec.ts @@ -1112,14 +1112,12 @@ class IssuesProcessorBuilder { issues(issues: Partial[]): IssuesProcessorBuilder { this.issuesOrPrs( - issues.map( - (issue: Readonly>): Partial => { - return { - ...issue, - pull_request: null - }; - } - ) + issues.map((issue: Readonly>): Partial => { + return { + ...issue, + pull_request: null + }; + }) ); return this; @@ -1127,14 +1125,12 @@ class IssuesProcessorBuilder { prs(issues: Partial[]): IssuesProcessorBuilder { this.issuesOrPrs( - issues.map( - (issue: Readonly>): Partial => { - return { - ...issue, - pull_request: {key: 'value'} - }; - } - ) + issues.map((issue: Readonly>): Partial => { + return { + ...issue, + pull_request: {key: 'value'} + }; + }) ); return this; diff --git a/__tests__/operations-per-run.spec.ts b/__tests__/operations-per-run.spec.ts index 46035cb5..e73593c7 100644 --- a/__tests__/operations-per-run.spec.ts +++ b/__tests__/operations-per-run.spec.ts @@ -188,18 +188,16 @@ class SUT { } private _setTestIssueList(): SUT { - this._testIssueList = this._sutIssues.map( - (sutIssue: SUTIssue): Issue => { - return generateIssue( - this._opts, - 1, - 'My first issue', - sutIssue.updatedAt, - sutIssue.updatedAt, - false - ); - } - ); + this._testIssueList = this._sutIssues.map((sutIssue: SUTIssue): Issue => { + return generateIssue( + this._opts, + 1, + 'My first issue', + sutIssue.updatedAt, + sutIssue.updatedAt, + false + ); + }); return this; } diff --git a/__tests__/remove-stale-when-updated.spec.ts b/__tests__/remove-stale-when-updated.spec.ts index 24eeef59..91b5bef6 100644 --- a/__tests__/remove-stale-when-updated.spec.ts +++ b/__tests__/remove-stale-when-updated.spec.ts @@ -464,14 +464,12 @@ class IssuesProcessorBuilder { issues(issues: Partial[]): IssuesProcessorBuilder { this.issuesOrPrs( - issues.map( - (issue: Readonly>): Partial => { - return { - ...issue, - pull_request: null - }; - } - ) + issues.map((issue: Readonly>): Partial => { + return { + ...issue, + pull_request: null + }; + }) ); return this; @@ -479,27 +477,23 @@ class IssuesProcessorBuilder { staleIssues(issues: Partial[]): IssuesProcessorBuilder { this.issues( - issues.map( - (issue: Readonly>): Partial => { - return { - ...issue, - updated_at: '2020-01-01T17:00:00Z', - created_at: '2020-01-01T17:00:00Z', - labels: issue.labels?.map( - (label: Readonly): ILabel => { - return { - ...label, - name: 'Stale' - }; - } - ) ?? [ - { - name: 'Stale' - } - ] - }; - } - ) + issues.map((issue: Readonly>): Partial => { + return { + ...issue, + updated_at: '2020-01-01T17:00:00Z', + created_at: '2020-01-01T17:00:00Z', + labels: issue.labels?.map((label: Readonly): ILabel => { + return { + ...label, + name: 'Stale' + }; + }) ?? [ + { + name: 'Stale' + } + ] + }; + }) ); return this; @@ -507,14 +501,12 @@ class IssuesProcessorBuilder { prs(issues: Partial[]): IssuesProcessorBuilder { this.issuesOrPrs( - issues.map( - (issue: Readonly>): Partial => { - return { - ...issue, - pull_request: {key: 'value'} - }; - } - ) + issues.map((issue: Readonly>): Partial => { + return { + ...issue, + pull_request: {key: 'value'} + }; + }) ); return this; @@ -522,27 +514,23 @@ class IssuesProcessorBuilder { stalePrs(issues: Partial[]): IssuesProcessorBuilder { this.prs( - issues.map( - (issue: Readonly>): Partial => { - return { - ...issue, - updated_at: '2020-01-01T17:00:00Z', - created_at: '2020-01-01T17:00:00Z', - labels: issue.labels?.map( - (label: Readonly): ILabel => { - return { - ...label, - name: 'Stale' - }; - } - ) ?? [ - { - name: 'Stale' - } - ] - }; - } - ) + issues.map((issue: Readonly>): Partial => { + return { + ...issue, + updated_at: '2020-01-01T17:00:00Z', + created_at: '2020-01-01T17:00:00Z', + labels: issue.labels?.map((label: Readonly): ILabel => { + return { + ...label, + name: 'Stale' + }; + }) ?? [ + { + name: 'Stale' + } + ] + }; + }) ); return this; diff --git a/dist/index.js b/dist/index.js index f31a6a81..aeedcb77 100644 --- a/dist/index.js +++ b/dist/index.js @@ -286,7 +286,7 @@ class IssuesProcessor { return issue.isPullRequest ? option_1.Option.ClosePrLabel : option_1.Option.CloseIssueLabel; } processIssues(page = 1) { - var _a, _b, _c; + var _a, _b; return __awaiter(this, void 0, void 0, function* () { // get the next batch of issues const issues = yield this.getIssues(page); @@ -305,159 +305,14 @@ class IssuesProcessor { break; } const issueLogger = new issue_logger_1.IssueLogger(issue); - (_b = this._statistics) === null || _b === void 0 ? void 0 : _b.incrementProcessedItemsCount(issue); - issueLogger.info(`Found this $$type last updated at: ${logger_service_1.LoggerService.cyan(issue.updated_at)}`); - // calculate string based messages for this issue - const staleMessage = issue.isPullRequest - ? this.options.stalePrMessage - : this.options.staleIssueMessage; - const closeMessage = issue.isPullRequest - ? this.options.closePrMessage - : this.options.closeIssueMessage; - const staleLabel = issue.isPullRequest - ? this.options.stalePrLabel - : this.options.staleIssueLabel; - const closeLabel = issue.isPullRequest - ? this.options.closePrLabel - : this.options.closeIssueLabel; - const skipMessage = issue.isPullRequest - ? this.options.stalePrMessage.length === 0 - : this.options.staleIssueMessage.length === 0; - const daysBeforeStale = issue.isPullRequest - ? this._getDaysBeforePrStale() - : this._getDaysBeforeIssueStale(); - const onlyLabels = words_to_list_1.wordsToList(this._getOnlyLabels(issue)); - if (onlyLabels.length > 0) { - issueLogger.info(`The option ${issueLogger.createOptionLink(option_1.Option.OnlyLabels)} was specified to only process issues and pull requests with all those labels (${logger_service_1.LoggerService.cyan(onlyLabels.length)})`); - const hasAllWhitelistedLabels = onlyLabels.every((label) => { - return is_labeled_1.isLabeled(issue, label); - }); - if (!hasAllWhitelistedLabels) { - issueLogger.info(logger_service_1.LoggerService.white('└──'), `Skipping this $$type because it doesn't have all the required labels`); - IssuesProcessor._endIssueProcessing(issue); - continue; // Don't process issues without all of the required labels - } - else { - issueLogger.info(logger_service_1.LoggerService.white('├──'), `All the required labels are present on this $$type`); - issueLogger.info(logger_service_1.LoggerService.white('└──'), `Continuing the process for this $$type`); - } - } - else { - issueLogger.info(`The option ${issueLogger.createOptionLink(option_1.Option.OnlyLabels)} was not specified`); - issueLogger.info(logger_service_1.LoggerService.white('└──'), `Continuing the process for this $$type`); - } - issueLogger.info(`Days before $$type stale: ${logger_service_1.LoggerService.cyan(daysBeforeStale)}`); - const shouldMarkAsStale = should_mark_when_stale_1.shouldMarkWhenStale(daysBeforeStale); - if (issue.state === 'closed') { - issueLogger.info(`Skipping this $$type because it is closed`); - IssuesProcessor._endIssueProcessing(issue); - continue; // Don't process closed issues - } - if (issue.locked) { - issueLogger.info(`Skipping this $$type because it is locked`); - IssuesProcessor._endIssueProcessing(issue); - continue; // Don't process locked issues - } - // Try to remove the close label when not close/locked issue or PR - yield this._removeCloseLabel(issue, closeLabel); - if (this.options.startDate) { - const startDate = new Date(this.options.startDate); - const createdAt = new Date(issue.created_at); - issueLogger.info(`A start date was specified for the ${get_humanized_date_1.getHumanizedDate(startDate)} (${logger_service_1.LoggerService.cyan(this.options.startDate)})`); - // Expecting that GitHub will always set a creation date on the issues and PRs - // But you never know! - if (!is_valid_date_1.isValidDate(createdAt)) { - IssuesProcessor._endIssueProcessing(issue); - core.setFailed(new Error(`Invalid issue field: "created_at". Expected a valid date`)); - } - issueLogger.info(`$$type created the ${get_humanized_date_1.getHumanizedDate(createdAt)} (${logger_service_1.LoggerService.cyan(issue.created_at)})`); - if (!is_date_more_recent_than_1.isDateMoreRecentThan(createdAt, startDate)) { - issueLogger.info(`Skipping this $$type because it was created before the specified start date`); - IssuesProcessor._endIssueProcessing(issue); - continue; // Don't process issues which were created before the start date - } - } - if (issue.isStale) { - issueLogger.info(`This $$type has a stale label`); - } - else { - issueLogger.info(`This $$type hasn't a stale label`); - } - const exemptLabels = words_to_list_1.wordsToList(issue.isPullRequest - ? this.options.exemptPrLabels - : this.options.exemptIssueLabels); - if (exemptLabels.some((exemptLabel) => is_labeled_1.isLabeled(issue, exemptLabel))) { - if (issue.isStale) { - issueLogger.info(`An exempt label was added after the stale label.`); - yield this._removeStaleLabel(issue, staleLabel); - } - issueLogger.info(`Skipping this $$type because it has an exempt label`); - IssuesProcessor._endIssueProcessing(issue); - continue; // Don't process exempt issues - } - const anyOfLabels = words_to_list_1.wordsToList(this._getAnyOfLabels(issue)); - if (anyOfLabels.length > 0) { - issueLogger.info(`The option ${issueLogger.createOptionLink(option_1.Option.AnyOfLabels)} was specified to only process the issues and pull requests with one of those labels (${logger_service_1.LoggerService.cyan(anyOfLabels.length)})`); - const hasOneOfWhitelistedLabels = anyOfLabels.some((label) => { - return is_labeled_1.isLabeled(issue, label); - }); - if (!hasOneOfWhitelistedLabels) { - issueLogger.info(logger_service_1.LoggerService.white('└──'), `Skipping this $$type because it doesn't have one of the required labels`); - IssuesProcessor._endIssueProcessing(issue); - continue; // Don't process issues without any of the required labels - } - else { - issueLogger.info(logger_service_1.LoggerService.white('├──'), `One of the required labels is present on this $$type`); - issueLogger.info(logger_service_1.LoggerService.white('└──'), `Continuing the process for this $$type`); - } - } - else { - issueLogger.info(`The option ${issueLogger.createOptionLink(option_1.Option.AnyOfLabels)} was not specified`); - issueLogger.info(logger_service_1.LoggerService.white('└──'), `Continuing the process for this $$type`); - } - const milestones = new milestones_1.Milestones(this.options, issue); - if (milestones.shouldExemptMilestones()) { - IssuesProcessor._endIssueProcessing(issue); - continue; // Don't process exempt milestones - } - const assignees = new assignees_1.Assignees(this.options, issue); - if (assignees.shouldExemptAssignees()) { - IssuesProcessor._endIssueProcessing(issue); - continue; // Don't process exempt assignees - } - // Should this issue be marked stale? - const shouldBeStale = !IssuesProcessor._updatedSince(issue.updated_at, daysBeforeStale); - // Determine if this issue needs to be marked stale first - if (!issue.isStale) { - issueLogger.info(`This $$type is not stale`); - const updatedAtDate = new Date(issue.updated_at); - if (shouldBeStale) { - issueLogger.info(`This $$type should be stale based on the last update date the ${get_humanized_date_1.getHumanizedDate(updatedAtDate)} (${logger_service_1.LoggerService.cyan(issue.updated_at)})`); - if (shouldMarkAsStale) { - issueLogger.info(`This $$type should be marked as stale based on the option ${issueLogger.createOptionLink(this._getDaysBeforeStaleUsedOptionName(issue))} (${logger_service_1.LoggerService.cyan(daysBeforeStale)})`); - yield this._markStale(issue, staleMessage, staleLabel, skipMessage); - issue.isStale = true; // This issue is now considered stale - issueLogger.info(`This $$type is now stale`); - } - else { - issueLogger.info(`This $$type should not be marked as stale based on the option ${issueLogger.createOptionLink(this._getDaysBeforeStaleUsedOptionName(issue))} (${logger_service_1.LoggerService.cyan(daysBeforeStale)})`); - } - } - else { - issueLogger.info(`This $$type should not be stale based on the last update date the ${get_humanized_date_1.getHumanizedDate(updatedAtDate)} (${logger_service_1.LoggerService.cyan(issue.updated_at)})`); - } - } - // Process the issue if it was marked stale - if (issue.isStale) { - issueLogger.info(`This $$type is already stale`); - yield this._processStaleIssue(issue, staleLabel, actor, closeMessage, closeLabel); - } - IssuesProcessor._endIssueProcessing(issue); + yield issueLogger.grouping(`$$type #${issue.number}`, () => __awaiter(this, void 0, void 0, function* () { + yield this.processIssue(issue, actor); + })); } if (!this.operations.hasRemainingOperations()) { this._logger.warning(logger_service_1.LoggerService.yellowBright(`No more operations left! Exiting...`)); this._logger.warning(`${logger_service_1.LoggerService.yellowBright('If you think that not enough issues were processed you could try to increase the quantity related to the')} ${this._logger.createOptionLink(option_1.Option.OperationsPerRun)} ${logger_service_1.LoggerService.yellowBright('option which is currently set to')} ${logger_service_1.LoggerService.cyan(this.options.operationsPerRun)}`); - (_c = this._statistics) === null || _c === void 0 ? void 0 : _c.setOperationsCount(this.operations.getConsumedOperationsCount()).logStats(); + (_b = this._statistics) === null || _b === void 0 ? void 0 : _b.setOperationsCount(this.operations.getConsumedOperationsCount()).logStats(); return 0; } this._logger.info(`${logger_service_1.LoggerService.green('Batch')} ${logger_service_1.LoggerService.cyan(`#${page}`)} ${logger_service_1.LoggerService.green('processed.')}`); @@ -465,6 +320,160 @@ class IssuesProcessor { return this.processIssues(page + 1); }); } + processIssue(issue, actor) { + var _a; + return __awaiter(this, void 0, void 0, function* () { + (_a = this._statistics) === null || _a === void 0 ? void 0 : _a.incrementProcessedItemsCount(issue); + const issueLogger = new issue_logger_1.IssueLogger(issue); + issueLogger.info(`Found this $$type last updated at: ${logger_service_1.LoggerService.cyan(issue.updated_at)}`); + // calculate string based messages for this issue + const staleMessage = issue.isPullRequest + ? this.options.stalePrMessage + : this.options.staleIssueMessage; + const closeMessage = issue.isPullRequest + ? this.options.closePrMessage + : this.options.closeIssueMessage; + const staleLabel = issue.isPullRequest + ? this.options.stalePrLabel + : this.options.staleIssueLabel; + const closeLabel = issue.isPullRequest + ? this.options.closePrLabel + : this.options.closeIssueLabel; + const skipMessage = issue.isPullRequest + ? this.options.stalePrMessage.length === 0 + : this.options.staleIssueMessage.length === 0; + const daysBeforeStale = issue.isPullRequest + ? this._getDaysBeforePrStale() + : this._getDaysBeforeIssueStale(); + const onlyLabels = words_to_list_1.wordsToList(this._getOnlyLabels(issue)); + if (onlyLabels.length > 0) { + issueLogger.info(`The option ${issueLogger.createOptionLink(option_1.Option.OnlyLabels)} was specified to only process issues and pull requests with all those labels (${logger_service_1.LoggerService.cyan(onlyLabels.length)})`); + const hasAllWhitelistedLabels = onlyLabels.every((label) => { + return is_labeled_1.isLabeled(issue, label); + }); + if (!hasAllWhitelistedLabels) { + issueLogger.info(logger_service_1.LoggerService.white('└──'), `Skipping this $$type because it doesn't have all the required labels`); + IssuesProcessor._endIssueProcessing(issue); + return; // Don't process issues without all of the required labels + } + else { + issueLogger.info(logger_service_1.LoggerService.white('├──'), `All the required labels are present on this $$type`); + issueLogger.info(logger_service_1.LoggerService.white('└──'), `Continuing the process for this $$type`); + } + } + else { + issueLogger.info(`The option ${issueLogger.createOptionLink(option_1.Option.OnlyLabels)} was not specified`); + issueLogger.info(logger_service_1.LoggerService.white('└──'), `Continuing the process for this $$type`); + } + issueLogger.info(`Days before $$type stale: ${logger_service_1.LoggerService.cyan(daysBeforeStale)}`); + const shouldMarkAsStale = should_mark_when_stale_1.shouldMarkWhenStale(daysBeforeStale); + if (issue.state === 'closed') { + issueLogger.info(`Skipping this $$type because it is closed`); + IssuesProcessor._endIssueProcessing(issue); + return; // Don't process closed issues + } + if (issue.locked) { + issueLogger.info(`Skipping this $$type because it is locked`); + IssuesProcessor._endIssueProcessing(issue); + return; // Don't process locked issues + } + // Try to remove the close label when not close/locked issue or PR + yield this._removeCloseLabel(issue, closeLabel); + if (this.options.startDate) { + const startDate = new Date(this.options.startDate); + const createdAt = new Date(issue.created_at); + issueLogger.info(`A start date was specified for the ${get_humanized_date_1.getHumanizedDate(startDate)} (${logger_service_1.LoggerService.cyan(this.options.startDate)})`); + // Expecting that GitHub will always set a creation date on the issues and PRs + // But you never know! + if (!is_valid_date_1.isValidDate(createdAt)) { + IssuesProcessor._endIssueProcessing(issue); + core.setFailed(new Error(`Invalid issue field: "created_at". Expected a valid date`)); + } + issueLogger.info(`$$type created the ${get_humanized_date_1.getHumanizedDate(createdAt)} (${logger_service_1.LoggerService.cyan(issue.created_at)})`); + if (!is_date_more_recent_than_1.isDateMoreRecentThan(createdAt, startDate)) { + issueLogger.info(`Skipping this $$type because it was created before the specified start date`); + IssuesProcessor._endIssueProcessing(issue); + return; // Don't process issues which were created before the start date + } + } + if (issue.isStale) { + issueLogger.info(`This $$type has a stale label`); + } + else { + issueLogger.info(`This $$type hasn't a stale label`); + } + const exemptLabels = words_to_list_1.wordsToList(issue.isPullRequest + ? this.options.exemptPrLabels + : this.options.exemptIssueLabels); + if (exemptLabels.some((exemptLabel) => is_labeled_1.isLabeled(issue, exemptLabel))) { + if (issue.isStale) { + issueLogger.info(`An exempt label was added after the stale label.`); + yield this._removeStaleLabel(issue, staleLabel); + } + issueLogger.info(`Skipping this $$type because it has an exempt label`); + IssuesProcessor._endIssueProcessing(issue); + return; // Don't process exempt issues + } + const anyOfLabels = words_to_list_1.wordsToList(this._getAnyOfLabels(issue)); + if (anyOfLabels.length > 0) { + issueLogger.info(`The option ${issueLogger.createOptionLink(option_1.Option.AnyOfLabels)} was specified to only process the issues and pull requests with one of those labels (${logger_service_1.LoggerService.cyan(anyOfLabels.length)})`); + const hasOneOfWhitelistedLabels = anyOfLabels.some((label) => { + return is_labeled_1.isLabeled(issue, label); + }); + if (!hasOneOfWhitelistedLabels) { + issueLogger.info(logger_service_1.LoggerService.white('└──'), `Skipping this $$type because it doesn't have one of the required labels`); + IssuesProcessor._endIssueProcessing(issue); + return; // Don't process issues without any of the required labels + } + else { + issueLogger.info(logger_service_1.LoggerService.white('├──'), `One of the required labels is present on this $$type`); + issueLogger.info(logger_service_1.LoggerService.white('└──'), `Continuing the process for this $$type`); + } + } + else { + issueLogger.info(`The option ${issueLogger.createOptionLink(option_1.Option.AnyOfLabels)} was not specified`); + issueLogger.info(logger_service_1.LoggerService.white('└──'), `Continuing the process for this $$type`); + } + const milestones = new milestones_1.Milestones(this.options, issue); + if (milestones.shouldExemptMilestones()) { + IssuesProcessor._endIssueProcessing(issue); + return; // Don't process exempt milestones + } + const assignees = new assignees_1.Assignees(this.options, issue); + if (assignees.shouldExemptAssignees()) { + IssuesProcessor._endIssueProcessing(issue); + return; // Don't process exempt assignees + } + // Should this issue be marked stale? + const shouldBeStale = !IssuesProcessor._updatedSince(issue.updated_at, daysBeforeStale); + // Determine if this issue needs to be marked stale first + if (!issue.isStale) { + issueLogger.info(`This $$type is not stale`); + const updatedAtDate = new Date(issue.updated_at); + if (shouldBeStale) { + issueLogger.info(`This $$type should be stale based on the last update date the ${get_humanized_date_1.getHumanizedDate(updatedAtDate)} (${logger_service_1.LoggerService.cyan(issue.updated_at)})`); + if (shouldMarkAsStale) { + issueLogger.info(`This $$type should be marked as stale based on the option ${issueLogger.createOptionLink(this._getDaysBeforeStaleUsedOptionName(issue))} (${logger_service_1.LoggerService.cyan(daysBeforeStale)})`); + yield this._markStale(issue, staleMessage, staleLabel, skipMessage); + issue.isStale = true; // This issue is now considered stale + issueLogger.info(`This $$type is now stale`); + } + else { + issueLogger.info(`This $$type should not be marked as stale based on the option ${issueLogger.createOptionLink(this._getDaysBeforeStaleUsedOptionName(issue))} (${logger_service_1.LoggerService.cyan(daysBeforeStale)})`); + } + } + else { + issueLogger.info(`This $$type should not be stale based on the last update date the ${get_humanized_date_1.getHumanizedDate(updatedAtDate)} (${logger_service_1.LoggerService.cyan(issue.updated_at)})`); + } + } + // Process the issue if it was marked stale + if (issue.isStale) { + issueLogger.info(`This $$type is already stale`); + yield this._processStaleIssue(issue, staleLabel, actor, closeMessage, closeLabel); + } + IssuesProcessor._endIssueProcessing(issue); + }); + } // Grab comments for an issue since a given date listIssueComments(issueNumber, sinceDate) { var _a; @@ -913,10 +922,19 @@ exports.IssuesProcessor = IssuesProcessor; /***/ }), /***/ 2984: -/***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { +/***/ (function(__unused_webpack_module, exports, __nccwpck_require__) { "use strict"; +var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { + function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } + return new (P || (P = Promise))(function (resolve, reject) { + function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } + function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } + function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } + step((generator = generator.apply(thisArg, _arguments || [])).next()); + }); +}; Object.defineProperty(exports, "__esModule", ({ value: true })); exports.IssueLogger = void 0; const logger_1 = __nccwpck_require__(6212); @@ -948,6 +966,14 @@ class IssueLogger extends logger_1.Logger { error(...message) { super.error(this._format(...message)); } + grouping(message, fn) { + const _super = Object.create(null, { + grouping: { get: () => super.grouping } + }); + return __awaiter(this, void 0, void 0, function* () { + return _super.grouping.call(this, this._format(message), fn); + }); + } _replaceTokens(message) { return this._replaceTypeToken(message); } @@ -1006,6 +1032,15 @@ var __importStar = (this && this.__importStar) || function (mod) { __setModuleDefault(result, mod); return result; }; +var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { + function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } + return new (P || (P = Promise))(function (resolve, reject) { + function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } + function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } + function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } + step((generator = generator.apply(thisArg, _arguments || [])).next()); + }); +}; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; @@ -1024,6 +1059,11 @@ class Logger { error(...message) { core.error(logger_service_1.LoggerService.whiteBright(message.join(' '))); } + grouping(message, fn) { + return __awaiter(this, void 0, void 0, function* () { + return core.group(logger_service_1.LoggerService.whiteBright(message), fn); + }); + } createLink(name, link) { return terminal_link_1.default(name, link); } @@ -8897,4 +8937,4 @@ module.exports = require("zlib");; /******/ // Load entry module and return exports /******/ return __nccwpck_require__(3109); /******/ })() -; +; \ No newline at end of file diff --git a/package.json b/package.json index 7a93c0b0..d52788f1 100644 --- a/package.json +++ b/package.json @@ -30,6 +30,10 @@ "node", "stale" ], + "engines": { + "node": "12", + "npm": "6" + }, "author": "GitHub", "license": "MIT", "dependencies": { diff --git a/src/classes/issues-processor.ts b/src/classes/issues-processor.ts index 98f72178..e5e963aa 100644 --- a/src/classes/issues-processor.ts +++ b/src/classes/issues-processor.ts @@ -37,7 +37,8 @@ export class IssuesProcessor { } private static _endIssueProcessing(issue: Issue): void { - const consumedOperationsCount: number = issue.operations.getConsumedOperationsCount(); + const consumedOperationsCount: number = + issue.operations.getConsumedOperationsCount(); if (consumedOperationsCount > 0) { const issueLogger: IssueLogger = new IssueLogger(issue); @@ -133,281 +134,9 @@ export class IssuesProcessor { } const issueLogger: IssueLogger = new IssueLogger(issue); - this._statistics?.incrementProcessedItemsCount(issue); - - issueLogger.info( - `Found this $$type last updated at: ${LoggerService.cyan( - issue.updated_at - )}` - ); - - // calculate string based messages for this issue - const staleMessage: string = issue.isPullRequest - ? this.options.stalePrMessage - : this.options.staleIssueMessage; - const closeMessage: string = issue.isPullRequest - ? this.options.closePrMessage - : this.options.closeIssueMessage; - const staleLabel: string = issue.isPullRequest - ? this.options.stalePrLabel - : this.options.staleIssueLabel; - const closeLabel: string = issue.isPullRequest - ? this.options.closePrLabel - : this.options.closeIssueLabel; - const skipMessage = issue.isPullRequest - ? this.options.stalePrMessage.length === 0 - : this.options.staleIssueMessage.length === 0; - const daysBeforeStale: number = issue.isPullRequest - ? this._getDaysBeforePrStale() - : this._getDaysBeforeIssueStale(); - const onlyLabels: string[] = wordsToList(this._getOnlyLabels(issue)); - - if (onlyLabels.length > 0) { - issueLogger.info( - `The option ${issueLogger.createOptionLink( - Option.OnlyLabels - )} was specified to only process issues and pull requests with all those labels (${LoggerService.cyan( - onlyLabels.length - )})` - ); - - const hasAllWhitelistedLabels: boolean = onlyLabels.every( - (label: Readonly): boolean => { - return isLabeled(issue, label); - } - ); - - if (!hasAllWhitelistedLabels) { - issueLogger.info( - LoggerService.white('└──'), - `Skipping this $$type because it doesn't have all the required labels` - ); - - IssuesProcessor._endIssueProcessing(issue); - continue; // Don't process issues without all of the required labels - } else { - issueLogger.info( - LoggerService.white('├──'), - `All the required labels are present on this $$type` - ); - issueLogger.info( - LoggerService.white('└──'), - `Continuing the process for this $$type` - ); - } - } else { - issueLogger.info( - `The option ${issueLogger.createOptionLink( - Option.OnlyLabels - )} was not specified` - ); - issueLogger.info( - LoggerService.white('└──'), - `Continuing the process for this $$type` - ); - } - - issueLogger.info( - `Days before $$type stale: ${LoggerService.cyan(daysBeforeStale)}` - ); - - const shouldMarkAsStale: boolean = shouldMarkWhenStale(daysBeforeStale); - - if (issue.state === 'closed') { - issueLogger.info(`Skipping this $$type because it is closed`); - IssuesProcessor._endIssueProcessing(issue); - continue; // Don't process closed issues - } - - if (issue.locked) { - issueLogger.info(`Skipping this $$type because it is locked`); - IssuesProcessor._endIssueProcessing(issue); - continue; // Don't process locked issues - } - - // Try to remove the close label when not close/locked issue or PR - await this._removeCloseLabel(issue, closeLabel); - - 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 - )} (${LoggerService.cyan(this.options.startDate)})` - ); - - // Expecting that GitHub will always set a creation date on the issues and PRs - // But you never know! - if (!isValidDate(createdAt)) { - IssuesProcessor._endIssueProcessing(issue); - core.setFailed( - new Error( - `Invalid issue field: "created_at". Expected a valid date` - ) - ); - } - - issueLogger.info( - `$$type created the ${getHumanizedDate( - createdAt - )} (${LoggerService.cyan(issue.created_at)})` - ); - - if (!isDateMoreRecentThan(createdAt, startDate)) { - issueLogger.info( - `Skipping this $$type because it was created before the specified start date` - ); - - IssuesProcessor._endIssueProcessing(issue); - continue; // Don't process issues which were created before the start date - } - } - - if (issue.isStale) { - issueLogger.info(`This $$type has a stale label`); - } else { - issueLogger.info(`This $$type hasn't a stale label`); - } - - const exemptLabels: string[] = wordsToList( - issue.isPullRequest - ? this.options.exemptPrLabels - : this.options.exemptIssueLabels - ); - - if ( - exemptLabels.some((exemptLabel: Readonly): boolean => - isLabeled(issue, exemptLabel) - ) - ) { - if (issue.isStale) { - issueLogger.info(`An exempt label was added after the stale label.`); - await this._removeStaleLabel(issue, staleLabel); - } - - issueLogger.info(`Skipping this $$type because it has an exempt label`); - IssuesProcessor._endIssueProcessing(issue); - continue; // Don't process exempt issues - } - - const anyOfLabels: string[] = wordsToList(this._getAnyOfLabels(issue)); - - if (anyOfLabels.length > 0) { - issueLogger.info( - `The option ${issueLogger.createOptionLink( - Option.AnyOfLabels - )} was specified to only process the issues and pull requests with one of those labels (${LoggerService.cyan( - anyOfLabels.length - )})` - ); - - const hasOneOfWhitelistedLabels: boolean = anyOfLabels.some( - (label: Readonly): boolean => { - return isLabeled(issue, label); - } - ); - - if (!hasOneOfWhitelistedLabels) { - issueLogger.info( - LoggerService.white('└──'), - `Skipping this $$type because it doesn't have one of the required labels` - ); - IssuesProcessor._endIssueProcessing(issue); - continue; // Don't process issues without any of the required labels - } else { - issueLogger.info( - LoggerService.white('├──'), - `One of the required labels is present on this $$type` - ); - issueLogger.info( - LoggerService.white('└──'), - `Continuing the process for this $$type` - ); - } - } else { - issueLogger.info( - `The option ${issueLogger.createOptionLink( - Option.AnyOfLabels - )} was not specified` - ); - issueLogger.info( - LoggerService.white('└──'), - `Continuing the process for this $$type` - ); - } - - const milestones: Milestones = new Milestones(this.options, issue); - - if (milestones.shouldExemptMilestones()) { - IssuesProcessor._endIssueProcessing(issue); - continue; // Don't process exempt milestones - } - - const assignees: Assignees = new Assignees(this.options, issue); - - if (assignees.shouldExemptAssignees()) { - IssuesProcessor._endIssueProcessing(issue); - continue; // Don't process exempt assignees - } - - // Should this issue be marked stale? - const shouldBeStale = !IssuesProcessor._updatedSince( - issue.updated_at, - daysBeforeStale - ); - - // Determine if this issue needs to be marked stale first - if (!issue.isStale) { - issueLogger.info(`This $$type is not stale`); - const updatedAtDate: Date = new Date(issue.updated_at); - - if (shouldBeStale) { - issueLogger.info( - `This $$type should be stale based on the last update date the ${getHumanizedDate( - updatedAtDate - )} (${LoggerService.cyan(issue.updated_at)})` - ); - - if (shouldMarkAsStale) { - issueLogger.info( - `This $$type should be marked as stale based on the option ${issueLogger.createOptionLink( - this._getDaysBeforeStaleUsedOptionName(issue) - )} (${LoggerService.cyan(daysBeforeStale)})` - ); - await this._markStale(issue, staleMessage, staleLabel, skipMessage); - issue.isStale = true; // This issue is now considered stale - issueLogger.info(`This $$type is now stale`); - } else { - issueLogger.info( - `This $$type should not be marked as stale based on the option ${issueLogger.createOptionLink( - this._getDaysBeforeStaleUsedOptionName(issue) - )} (${LoggerService.cyan(daysBeforeStale)})` - ); - } - } else { - issueLogger.info( - `This $$type should not be stale based on the last update date the ${getHumanizedDate( - updatedAtDate - )} (${LoggerService.cyan(issue.updated_at)})` - ); - } - } - - // Process the issue if it was marked stale - if (issue.isStale) { - issueLogger.info(`This $$type is already stale`); - await this._processStaleIssue( - issue, - staleLabel, - actor, - closeMessage, - closeLabel - ); - } - - IssuesProcessor._endIssueProcessing(issue); + await issueLogger.grouping(`$$type #${issue.number}`, async () => { + await this.processIssue(issue, actor); + }); } if (!this.operations.hasRemainingOperations()) { @@ -440,6 +169,283 @@ export class IssuesProcessor { return this.processIssues(page + 1); } + async processIssue(issue: Issue, actor: string): Promise { + this._statistics?.incrementProcessedItemsCount(issue); + + const issueLogger: IssueLogger = new IssueLogger(issue); + issueLogger.info( + `Found this $$type last updated at: ${LoggerService.cyan( + issue.updated_at + )}` + ); + + // calculate string based messages for this issue + const staleMessage: string = issue.isPullRequest + ? this.options.stalePrMessage + : this.options.staleIssueMessage; + const closeMessage: string = issue.isPullRequest + ? this.options.closePrMessage + : this.options.closeIssueMessage; + const staleLabel: string = issue.isPullRequest + ? this.options.stalePrLabel + : this.options.staleIssueLabel; + const closeLabel: string = issue.isPullRequest + ? this.options.closePrLabel + : this.options.closeIssueLabel; + const skipMessage = issue.isPullRequest + ? this.options.stalePrMessage.length === 0 + : this.options.staleIssueMessage.length === 0; + const daysBeforeStale: number = issue.isPullRequest + ? this._getDaysBeforePrStale() + : this._getDaysBeforeIssueStale(); + const onlyLabels: string[] = wordsToList(this._getOnlyLabels(issue)); + + if (onlyLabels.length > 0) { + issueLogger.info( + `The option ${issueLogger.createOptionLink( + Option.OnlyLabels + )} was specified to only process issues and pull requests with all those labels (${LoggerService.cyan( + onlyLabels.length + )})` + ); + + const hasAllWhitelistedLabels: boolean = onlyLabels.every( + (label: Readonly): boolean => { + return isLabeled(issue, label); + } + ); + + if (!hasAllWhitelistedLabels) { + issueLogger.info( + LoggerService.white('└──'), + `Skipping this $$type because it doesn't have all the required labels` + ); + + IssuesProcessor._endIssueProcessing(issue); + return; // Don't process issues without all of the required labels + } else { + issueLogger.info( + LoggerService.white('├──'), + `All the required labels are present on this $$type` + ); + issueLogger.info( + LoggerService.white('└──'), + `Continuing the process for this $$type` + ); + } + } else { + issueLogger.info( + `The option ${issueLogger.createOptionLink( + Option.OnlyLabels + )} was not specified` + ); + issueLogger.info( + LoggerService.white('└──'), + `Continuing the process for this $$type` + ); + } + + issueLogger.info( + `Days before $$type stale: ${LoggerService.cyan(daysBeforeStale)}` + ); + + const shouldMarkAsStale: boolean = shouldMarkWhenStale(daysBeforeStale); + + if (issue.state === 'closed') { + issueLogger.info(`Skipping this $$type because it is closed`); + IssuesProcessor._endIssueProcessing(issue); + return; // Don't process closed issues + } + + if (issue.locked) { + issueLogger.info(`Skipping this $$type because it is locked`); + IssuesProcessor._endIssueProcessing(issue); + return; // Don't process locked issues + } + + // Try to remove the close label when not close/locked issue or PR + await this._removeCloseLabel(issue, closeLabel); + + 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 + )} (${LoggerService.cyan(this.options.startDate)})` + ); + + // Expecting that GitHub will always set a creation date on the issues and PRs + // But you never know! + if (!isValidDate(createdAt)) { + IssuesProcessor._endIssueProcessing(issue); + core.setFailed( + new Error(`Invalid issue field: "created_at". Expected a valid date`) + ); + } + + issueLogger.info( + `$$type created the ${getHumanizedDate( + createdAt + )} (${LoggerService.cyan(issue.created_at)})` + ); + + if (!isDateMoreRecentThan(createdAt, startDate)) { + issueLogger.info( + `Skipping this $$type because it was created before the specified start date` + ); + + IssuesProcessor._endIssueProcessing(issue); + return; // Don't process issues which were created before the start date + } + } + + if (issue.isStale) { + issueLogger.info(`This $$type has a stale label`); + } else { + issueLogger.info(`This $$type hasn't a stale label`); + } + + const exemptLabels: string[] = wordsToList( + issue.isPullRequest + ? this.options.exemptPrLabels + : this.options.exemptIssueLabels + ); + + if ( + exemptLabels.some((exemptLabel: Readonly): boolean => + isLabeled(issue, exemptLabel) + ) + ) { + if (issue.isStale) { + issueLogger.info(`An exempt label was added after the stale label.`); + await this._removeStaleLabel(issue, staleLabel); + } + + issueLogger.info(`Skipping this $$type because it has an exempt label`); + IssuesProcessor._endIssueProcessing(issue); + return; // Don't process exempt issues + } + + const anyOfLabels: string[] = wordsToList(this._getAnyOfLabels(issue)); + + if (anyOfLabels.length > 0) { + issueLogger.info( + `The option ${issueLogger.createOptionLink( + Option.AnyOfLabels + )} was specified to only process the issues and pull requests with one of those labels (${LoggerService.cyan( + anyOfLabels.length + )})` + ); + + const hasOneOfWhitelistedLabels: boolean = anyOfLabels.some( + (label: Readonly): boolean => { + return isLabeled(issue, label); + } + ); + + if (!hasOneOfWhitelistedLabels) { + issueLogger.info( + LoggerService.white('└──'), + `Skipping this $$type because it doesn't have one of the required labels` + ); + IssuesProcessor._endIssueProcessing(issue); + return; // Don't process issues without any of the required labels + } else { + issueLogger.info( + LoggerService.white('├──'), + `One of the required labels is present on this $$type` + ); + issueLogger.info( + LoggerService.white('└──'), + `Continuing the process for this $$type` + ); + } + } else { + issueLogger.info( + `The option ${issueLogger.createOptionLink( + Option.AnyOfLabels + )} was not specified` + ); + issueLogger.info( + LoggerService.white('└──'), + `Continuing the process for this $$type` + ); + } + + const milestones: Milestones = new Milestones(this.options, issue); + + if (milestones.shouldExemptMilestones()) { + IssuesProcessor._endIssueProcessing(issue); + return; // Don't process exempt milestones + } + + const assignees: Assignees = new Assignees(this.options, issue); + + if (assignees.shouldExemptAssignees()) { + IssuesProcessor._endIssueProcessing(issue); + return; // Don't process exempt assignees + } + + // Should this issue be marked stale? + const shouldBeStale = !IssuesProcessor._updatedSince( + issue.updated_at, + daysBeforeStale + ); + + // Determine if this issue needs to be marked stale first + if (!issue.isStale) { + issueLogger.info(`This $$type is not stale`); + const updatedAtDate: Date = new Date(issue.updated_at); + + if (shouldBeStale) { + issueLogger.info( + `This $$type should be stale based on the last update date the ${getHumanizedDate( + updatedAtDate + )} (${LoggerService.cyan(issue.updated_at)})` + ); + + if (shouldMarkAsStale) { + issueLogger.info( + `This $$type should be marked as stale based on the option ${issueLogger.createOptionLink( + this._getDaysBeforeStaleUsedOptionName(issue) + )} (${LoggerService.cyan(daysBeforeStale)})` + ); + await this._markStale(issue, staleMessage, staleLabel, skipMessage); + issue.isStale = true; // This issue is now considered stale + issueLogger.info(`This $$type is now stale`); + } else { + issueLogger.info( + `This $$type should not be marked as stale based on the option ${issueLogger.createOptionLink( + this._getDaysBeforeStaleUsedOptionName(issue) + )} (${LoggerService.cyan(daysBeforeStale)})` + ); + } + } else { + issueLogger.info( + `This $$type should not be stale based on the last update date the ${getHumanizedDate( + updatedAtDate + )} (${LoggerService.cyan(issue.updated_at)})` + ); + } + } + + // Process the issue if it was marked stale + if (issue.isStale) { + issueLogger.info(`This $$type is already stale`); + await this._processStaleIssue( + issue, + staleLabel, + actor, + closeMessage, + closeLabel + ); + } + + IssuesProcessor._endIssueProcessing(issue); + } + // Grab comments for an issue since a given date async listIssueComments( issueNumber: Readonly, @@ -484,16 +490,15 @@ export class IssuesProcessor { try { this.operations.consumeOperation(); - const issueResult: OctoKitIssueList = await this.client.issues.listForRepo( - { + 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 - } - ); + }); this._statistics?.incrementFetchedItemsCount(issueResult.data.length); return issueResult.data.map( @@ -579,9 +584,8 @@ export class IssuesProcessor { `$$type has been updated: ${LoggerService.cyan(issueHasUpdate)}` ); - const shouldRemoveStaleWhenUpdated: boolean = this._shouldRemoveStaleWhenUpdated( - issue - ); + const shouldRemoveStaleWhenUpdated: boolean = + this._shouldRemoveStaleWhenUpdated(issue); issueLogger.info( `The option ${issueLogger.createOptionLink( diff --git a/src/classes/loggers/issue-logger.ts b/src/classes/loggers/issue-logger.ts index 1dd6c793..7a58de23 100644 --- a/src/classes/loggers/issue-logger.ts +++ b/src/classes/loggers/issue-logger.ts @@ -35,6 +35,10 @@ export class IssueLogger extends Logger { super.error(this._format(...message)); } + async grouping(message: string, fn: () => Promise): Promise { + return super.grouping(this._format(message), fn); + } + private _replaceTokens(message: Readonly): string { return this._replaceTypeToken(message); } diff --git a/src/classes/loggers/logger.ts b/src/classes/loggers/logger.ts index 4d10d76b..3c8ed81c 100644 --- a/src/classes/loggers/logger.ts +++ b/src/classes/loggers/logger.ts @@ -16,6 +16,10 @@ export class Logger { core.error(LoggerService.whiteBright(message.join(' '))); } + async grouping(message: string, fn: () => Promise): Promise { + return core.group(LoggerService.whiteBright(message), fn); + } + createLink(name: Readonly, link: Readonly): string { return terminalLink(name, link); } diff --git a/src/classes/milestones.ts b/src/classes/milestones.ts index af804d5d..db002ea4 100644 --- a/src/classes/milestones.ts +++ b/src/classes/milestones.ts @@ -195,9 +195,8 @@ export class Milestones { return false; } - const cleanMilestone: CleanMilestone = Milestones._cleanMilestone( - milestone - ); + const cleanMilestone: CleanMilestone = + Milestones._cleanMilestone(milestone); const isSameMilestone: boolean = cleanMilestone === diff --git a/src/functions/is-labeled.spec.ts b/src/functions/is-labeled.spec.ts index 249fcd08..30719f94 100644 --- a/src/functions/is-labeled.spec.ts +++ b/src/functions/is-labeled.spec.ts @@ -7,9 +7,9 @@ describe('isLabeled()', (): void => { describe('when the given issue contains no label', (): void => { beforeEach((): void => { - issue = ({ + issue = { labels: [] - } as unknown) as Issue; + } as unknown as Issue; }); describe('when the given label is a simple label', (): void => {