diff --git a/__tests__/any-of-labels.spec.ts b/__tests__/any-of-labels.spec.ts index cfed44bde..6aa15601b 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 3b3fe433d..6817d8e4e 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 d8ceddc9d..042642bad 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 46035cb5d..e73593c77 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 24eeef592..91b5bef69 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 f31a6a812..aeedcb77f 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,164 +305,173 @@ 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`); - } + 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)}`); + (_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.')}`); + // Do the next batch + 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(`The option ${issueLogger.createOptionLink(option_1.Option.OnlyLabels)} was not specified`); + 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`); } - 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`); + } + 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); - continue; // Don't process closed issues + core.setFailed(new Error(`Invalid issue field: "created_at". Expected a valid date`)); } - if (issue.locked) { - issueLogger.info(`Skipping this $$type because it is locked`); + 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 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 - } + 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(`This $$type has a stale label`); - } - else { - issueLogger.info(`This $$type hasn't a stale label`); + issueLogger.info(`An exempt label was added after the stale label.`); + yield this._removeStaleLabel(issue, staleLabel); } - 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`); + 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); - 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`); - } + return; // Don't process issues without any of the required labels } else { - issueLogger.info(`The option ${issueLogger.createOptionLink(option_1.Option.AnyOfLabels)} was not specified`); + 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`); } - 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(`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 stale based on the last update date the ${get_humanized_date_1.getHumanizedDate(updatedAtDate)} (${logger_service_1.LoggerService.cyan(issue.updated_at)})`); + 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)})`); } } - // 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); + 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)})`); } - IssuesProcessor._endIssueProcessing(issue); } - 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(); - return 0; + // 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); } - this._logger.info(`${logger_service_1.LoggerService.green('Batch')} ${logger_service_1.LoggerService.cyan(`#${page}`)} ${logger_service_1.LoggerService.green('processed.')}`); - // Do the next batch - return this.processIssues(page + 1); + IssuesProcessor._endIssueProcessing(issue); }); } // Grab comments for an issue since a given date @@ -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 7a93c0b00..d52788f1d 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 98f721780..e5e963aa7 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,311 +134,316 @@ export class IssuesProcessor { } const issueLogger: IssueLogger = new IssueLogger(issue); - this._statistics?.incrementProcessedItemsCount(issue); + await issueLogger.grouping(`$$type #${issue.number}`, async () => { + await this.processIssue(issue, actor); + }); + } - issueLogger.info( - `Found this $$type last updated at: ${LoggerService.cyan( - issue.updated_at - )}` + if (!this.operations.hasRemainingOperations()) { + this._logger.warning( + LoggerService.yellowBright(`No more operations left! Exiting...`) ); + this._logger.warning( + `${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.OperationsPerRun + )} ${LoggerService.yellowBright( + 'option which is currently set to' + )} ${LoggerService.cyan(this.options.operationsPerRun)}` + ); + this._statistics + ?.setOperationsCount(this.operations.getConsumedOperationsCount()) + .logStats(); - // 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 - )})` - ); + return 0; + } - const hasAllWhitelistedLabels: boolean = onlyLabels.every( - (label: Readonly): boolean => { - return isLabeled(issue, label); - } - ); + this._logger.info( + `${LoggerService.green('Batch')} ${LoggerService.cyan( + `#${page}` + )} ${LoggerService.green('processed.')}` + ); - if (!hasAllWhitelistedLabels) { - issueLogger.info( - LoggerService.white('└──'), - `Skipping this $$type because it doesn't have all the required labels` - ); + // Do the next batch + return this.processIssues(page + 1); + } - 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` - ); + 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( - `The option ${issueLogger.createOptionLink( - Option.OnlyLabels - )} was not specified` + 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( - `Days before $$type stale: ${LoggerService.cyan(daysBeforeStale)}` + LoggerService.white('└──'), + `Continuing the process for this $$type` ); + } - const shouldMarkAsStale: boolean = shouldMarkWhenStale(daysBeforeStale); + issueLogger.info( + `Days before $$type stale: ${LoggerService.cyan(daysBeforeStale)}` + ); - if (issue.state === 'closed') { - issueLogger.info(`Skipping this $$type because it is closed`); - IssuesProcessor._endIssueProcessing(issue); - continue; // Don't process closed issues - } + const shouldMarkAsStale: boolean = shouldMarkWhenStale(daysBeforeStale); - if (issue.locked) { - issueLogger.info(`Skipping this $$type because it is locked`); - IssuesProcessor._endIssueProcessing(issue); - continue; // Don't process locked issues - } + if (issue.state === 'closed') { + issueLogger.info(`Skipping this $$type because it is closed`); + IssuesProcessor._endIssueProcessing(issue); + return; // Don't process closed issues + } - // Try to remove the close label when not close/locked issue or PR - await this._removeCloseLabel(issue, closeLabel); + if (issue.locked) { + issueLogger.info(`Skipping this $$type because it is locked`); + IssuesProcessor._endIssueProcessing(issue); + return; // Don't process locked issues + } - if (this.options.startDate) { - const startDate: Date = new Date(this.options.startDate); - const createdAt: Date = new Date(issue.created_at); + // Try to remove the close label when not close/locked issue or PR + await this._removeCloseLabel(issue, closeLabel); - issueLogger.info( - `A start date was specified for the ${getHumanizedDate( - startDate - )} (${LoggerService.cyan(this.options.startDate)})` + 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`) ); + } - // 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( - `$$type created the ${getHumanizedDate( - createdAt - )} (${LoggerService.cyan(issue.created_at)})` + `Skipping this $$type because it was created before the specified start date` ); - 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 - } + 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(`This $$type has a stale label`); - } else { - issueLogger.info(`This $$type hasn't a stale label`); + issueLogger.info(`An exempt label was added after the stale label.`); + await this._removeStaleLabel(issue, staleLabel); } - const exemptLabels: string[] = wordsToList( - issue.isPullRequest - ? this.options.exemptPrLabels - : this.options.exemptIssueLabels - ); + issueLogger.info(`Skipping this $$type because it has an exempt label`); + IssuesProcessor._endIssueProcessing(issue); + return; // Don't process exempt issues + } - 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); - } + const anyOfLabels: string[] = wordsToList(this._getAnyOfLabels(issue)); - issueLogger.info(`Skipping this $$type because it has an exempt label`); - IssuesProcessor._endIssueProcessing(issue); - continue; // Don't process exempt issues - } + 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 anyOfLabels: string[] = wordsToList(this._getAnyOfLabels(issue)); + const hasOneOfWhitelistedLabels: boolean = anyOfLabels.some( + (label: Readonly): boolean => { + return isLabeled(issue, label); + } + ); - if (anyOfLabels.length > 0) { + if (!hasOneOfWhitelistedLabels) { 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); - } + LoggerService.white('└──'), + `Skipping this $$type because it doesn't have one of the required labels` ); - - 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` - ); - } + IssuesProcessor._endIssueProcessing(issue); + return; // Don't process issues without any of the required labels } else { issueLogger.info( - `The option ${issueLogger.createOptionLink( - Option.AnyOfLabels - )} was not specified` + 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); + const milestones: Milestones = new Milestones(this.options, issue); - if (milestones.shouldExemptMilestones()) { - IssuesProcessor._endIssueProcessing(issue); - continue; // Don't process exempt milestones - } + if (milestones.shouldExemptMilestones()) { + IssuesProcessor._endIssueProcessing(issue); + return; // Don't process exempt milestones + } - const assignees: Assignees = new Assignees(this.options, issue); + const assignees: Assignees = new Assignees(this.options, issue); - if (assignees.shouldExemptAssignees()) { - IssuesProcessor._endIssueProcessing(issue); - continue; // Don't process exempt assignees - } + 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 - ); + // 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); + // 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 (shouldBeStale) { + if (shouldMarkAsStale) { issueLogger.info( - `This $$type should be stale based on the last update date the ${getHumanizedDate( - updatedAtDate - )} (${LoggerService.cyan(issue.updated_at)})` + `This $$type should be marked as stale based on the option ${issueLogger.createOptionLink( + this._getDaysBeforeStaleUsedOptionName(issue) + )} (${LoggerService.cyan(daysBeforeStale)})` ); - - 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)})` - ); - } + 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 stale based on the last update date the ${getHumanizedDate( - updatedAtDate - )} (${LoggerService.cyan(issue.updated_at)})` + `This $$type should not be marked as stale based on the option ${issueLogger.createOptionLink( + this._getDaysBeforeStaleUsedOptionName(issue) + )} (${LoggerService.cyan(daysBeforeStale)})` ); } - } - - // 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 + } else { + issueLogger.info( + `This $$type should not be stale based on the last update date the ${getHumanizedDate( + updatedAtDate + )} (${LoggerService.cyan(issue.updated_at)})` ); } - - IssuesProcessor._endIssueProcessing(issue); } - if (!this.operations.hasRemainingOperations()) { - this._logger.warning( - LoggerService.yellowBright(`No more operations left! Exiting...`) - ); - this._logger.warning( - `${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.OperationsPerRun - )} ${LoggerService.yellowBright( - 'option which is currently set to' - )} ${LoggerService.cyan(this.options.operationsPerRun)}` + // 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 ); - this._statistics - ?.setOperationsCount(this.operations.getConsumedOperationsCount()) - .logStats(); - - return 0; } - this._logger.info( - `${LoggerService.green('Batch')} ${LoggerService.cyan( - `#${page}` - )} ${LoggerService.green('processed.')}` - ); - - // Do the next batch - return this.processIssues(page + 1); + IssuesProcessor._endIssueProcessing(issue); } // Grab comments for an issue since a given date @@ -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 1dd6c793a..7a58de23d 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 4d10d76bb..3c8ed81c9 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 af804d5de..db002ea4a 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 249fcd08a..30719f94d 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 => {