Add skipPinned

This commit is contained in:
Sergey Dolin 2023-07-06 19:34:46 +02:00
parent 465330b7e2
commit f6f48c4485
5 changed files with 92 additions and 19 deletions

View File

@ -164,6 +164,10 @@ inputs:
description: 'Exempt all issues with assignees from being marked as stale. Override "exempt-all-assignees" option regarding only the issues.' description: 'Exempt all issues with assignees from being marked as stale. Override "exempt-all-assignees" option regarding only the issues.'
default: '' default: ''
required: false required: false
exempt-pinned-issues:
description: 'Exempt pinned issues from being marked as stale. Default to false.'
default: 'false'
required: false
exempt-all-pr-assignees: exempt-all-pr-assignees:
description: 'Exempt all pull requests with assignees from being marked as stale. Override "exempt-all-assignees" option regarding only the pull requests.' description: 'Exempt all pull requests with assignees from being marked as stale. Override "exempt-all-assignees" option regarding only the pull requests.'
default: '' default: ''

52
dist/index.js vendored
View File

@ -408,6 +408,8 @@ class IssuesProcessor {
this.addedLabelIssues = []; this.addedLabelIssues = [];
this.addedCloseCommentIssues = []; this.addedCloseCommentIssues = [];
this._logger = new logger_1.Logger(); this._logger = new logger_1.Logger();
this._lastIssueEvents = [];
this._lastIssueEventsIssueId = -1;
this.options = options; this.options = options;
this.state = state; this.state = state;
this.client = (0, github_1.getOctokit)(this.options.repoToken, undefined, plugin_retry_1.retry); this.client = (0, github_1.getOctokit)(this.options.repoToken, undefined, plugin_retry_1.retry);
@ -464,6 +466,34 @@ class IssuesProcessor {
return this.processIssues(page + 1); return this.processIssues(page + 1);
}); });
} }
getIssueEvents(issue) {
return __awaiter(this, void 0, void 0, function* () {
if (issue.number !== this._lastIssueEventsIssueId) {
const options = this.client.rest.issues.listEvents.endpoint.merge({
owner: github_1.context.repo.owner,
repo: github_1.context.repo.repo,
per_page: 100,
issue_number: issue.number
});
const events = yield this.client.paginate(options);
this._lastIssueEvents = events.reverse();
this._lastIssueEventsIssueId = issue.number;
}
return this._lastIssueEvents;
});
}
getPinnedStatus(issue) {
return __awaiter(this, void 0, void 0, function* () {
const events = yield this.getIssueEvents(issue);
const pinnedEvent = events.findIndex(event => event.event === 'pinned');
if (pinnedEvent == -1)
return false;
const unpinnedEvent = events.findIndex(event => event.event === 'unpinned');
if (unpinnedEvent == -1)
return true;
return pinnedEvent < unpinnedEvent;
});
}
processIssue(issue, labelsToAddWhenUnstale, labelsToRemoveWhenUnstale, labelsToRemoveWhenStale) { processIssue(issue, labelsToAddWhenUnstale, labelsToRemoveWhenUnstale, labelsToRemoveWhenStale) {
var _a; var _a;
return __awaiter(this, void 0, void 0, function* () { return __awaiter(this, void 0, void 0, function* () {
@ -504,6 +534,11 @@ class IssuesProcessor {
IssuesProcessor._endIssueProcessing(issue); IssuesProcessor._endIssueProcessing(issue);
return; // If the issue has an 'include-only-assigned' option set, process only issues with nonempty assignees list return; // If the issue has an 'include-only-assigned' option set, process only issues with nonempty assignees list
} }
if (yield this._isSkipPinned(issue)) {
issueLogger.info('Skipping this issue because it is pinned');
IssuesProcessor._endIssueProcessing(issue);
return; // Don't process pinned issues
}
const onlyLabels = (0, words_to_list_1.wordsToList)(this._getOnlyLabels(issue)); const onlyLabels = (0, words_to_list_1.wordsToList)(this._getOnlyLabels(issue));
if (onlyLabels.length > 0) { 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)})`); 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)})`);
@ -702,15 +737,8 @@ class IssuesProcessor {
issueLogger.info(`Checking for label on this $$type`); issueLogger.info(`Checking for label on this $$type`);
this._consumeIssueOperation(issue); this._consumeIssueOperation(issue);
(_a = this.statistics) === null || _a === void 0 ? void 0 : _a.incrementFetchedItemsEventsCount(); (_a = this.statistics) === null || _a === void 0 ? void 0 : _a.incrementFetchedItemsEventsCount();
const options = this.client.rest.issues.listEvents.endpoint.merge({ const events = yield this.getIssueEvents(issue);
owner: github_1.context.repo.owner, const staleLabeledEvent = events.find(event => event.event === 'labeled' &&
repo: github_1.context.repo.repo,
per_page: 100,
issue_number: issue.number
});
const events = yield this.client.paginate(options);
const reversedEvents = events.reverse();
const staleLabeledEvent = reversedEvents.find(event => event.event === 'labeled' &&
(0, clean_label_1.cleanLabel)(event.label.name) === (0, clean_label_1.cleanLabel)(label)); (0, clean_label_1.cleanLabel)(event.label.name) === (0, clean_label_1.cleanLabel)(label));
if (!staleLabeledEvent) { if (!staleLabeledEvent) {
// Must be old rather than labeled // Must be old rather than labeled
@ -1025,6 +1053,11 @@ class IssuesProcessor {
_isIncludeOnlyAssigned(issue) { _isIncludeOnlyAssigned(issue) {
return this.options.includeOnlyAssigned && !issue.hasAssignees; return this.options.includeOnlyAssigned && !issue.hasAssignees;
} }
_isSkipPinned(issue) {
return __awaiter(this, void 0, void 0, function* () {
return (this.options.exemptPinnedIssues && (yield this.getPinnedStatus(issue)));
});
}
_getAnyOfLabels(issue) { _getAnyOfLabels(issue) {
if (issue.isPullRequest) { if (issue.isPullRequest) {
if (this.options.anyOfPrLabels !== '') { if (this.options.anyOfPrLabels !== '') {
@ -2461,6 +2494,7 @@ function _getAndValidateArgs() {
staleIssueLabel: core.getInput('stale-issue-label', { required: true }), staleIssueLabel: core.getInput('stale-issue-label', { required: true }),
closeIssueLabel: core.getInput('close-issue-label'), closeIssueLabel: core.getInput('close-issue-label'),
exemptIssueLabels: core.getInput('exempt-issue-labels'), exemptIssueLabels: core.getInput('exempt-issue-labels'),
exemptPinnedIssues: core.getInput('exempt-pinned-issues') === 'true',
stalePrLabel: core.getInput('stale-pr-label', { required: true }), stalePrLabel: core.getInput('stale-pr-label', { required: true }),
closePrLabel: core.getInput('close-pr-label'), closePrLabel: core.getInput('close-pr-label'),
exemptPrLabels: core.getInput('exempt-pr-labels'), exemptPrLabels: core.getInput('exempt-pr-labels'),

View File

@ -193,6 +193,35 @@ export class IssuesProcessor {
return this.processIssues(page + 1); return this.processIssues(page + 1);
} }
private _lastIssueEvents: IIssueEvent[] = [];
private _lastIssueEventsIssueId = -1;
async getIssueEvents(issue: Issue): Promise<IIssueEvent[]> {
if (issue.number !== this._lastIssueEventsIssueId) {
const options = this.client.rest.issues.listEvents.endpoint.merge({
owner: context.repo.owner,
repo: context.repo.repo,
per_page: 100,
issue_number: issue.number
});
const events: IIssueEvent[] = await this.client.paginate(options);
this._lastIssueEvents = events.reverse();
this._lastIssueEventsIssueId = issue.number;
}
return this._lastIssueEvents;
}
async getPinnedStatus(issue: Issue): Promise<boolean> {
const events = await this.getIssueEvents(issue);
const pinnedEvent = events.findIndex(event => event.event === 'pinned');
if (pinnedEvent == -1) return false;
const unpinnedEvent = events.findIndex(event => event.event === 'unpinned');
if (unpinnedEvent == -1) return true;
return pinnedEvent < unpinnedEvent;
}
async processIssue( async processIssue(
issue: Issue, issue: Issue,
labelsToAddWhenUnstale: Readonly<string>[], labelsToAddWhenUnstale: Readonly<string>[],
@ -248,6 +277,12 @@ export class IssuesProcessor {
return; // If the issue has an 'include-only-assigned' option set, process only issues with nonempty assignees list return; // If the issue has an 'include-only-assigned' option set, process only issues with nonempty assignees list
} }
if (await this._isSkipPinned(issue)) {
issueLogger.info('Skipping this issue because it is pinned');
IssuesProcessor._endIssueProcessing(issue);
return; // Don't process pinned issues
}
const onlyLabels: string[] = wordsToList(this._getOnlyLabels(issue)); const onlyLabels: string[] = wordsToList(this._getOnlyLabels(issue));
if (onlyLabels.length > 0) { if (onlyLabels.length > 0) {
@ -593,17 +628,9 @@ export class IssuesProcessor {
this._consumeIssueOperation(issue); this._consumeIssueOperation(issue);
this.statistics?.incrementFetchedItemsEventsCount(); this.statistics?.incrementFetchedItemsEventsCount();
const options = this.client.rest.issues.listEvents.endpoint.merge({
owner: context.repo.owner,
repo: context.repo.repo,
per_page: 100,
issue_number: issue.number
});
const events: IIssueEvent[] = await this.client.paginate(options); const events = await this.getIssueEvents(issue);
const reversedEvents = events.reverse(); const staleLabeledEvent = events.find(
const staleLabeledEvent = reversedEvents.find(
event => event =>
event.event === 'labeled' && event.event === 'labeled' &&
cleanLabel(event.label.name) === cleanLabel(label) cleanLabel(event.label.name) === cleanLabel(label)
@ -1074,6 +1101,12 @@ export class IssuesProcessor {
return this.options.includeOnlyAssigned && !issue.hasAssignees; return this.options.includeOnlyAssigned && !issue.hasAssignees;
} }
private async _isSkipPinned(issue: Issue): Promise<boolean> {
return (
this.options.exemptPinnedIssues && (await this.getPinnedStatus(issue))
);
}
private _getAnyOfLabels(issue: Issue): string { private _getAnyOfLabels(issue: Issue): string {
if (issue.isPullRequest) { if (issue.isPullRequest) {
if (this.options.anyOfPrLabels !== '') { if (this.options.anyOfPrLabels !== '') {

View File

@ -15,6 +15,7 @@ export interface IIssuesProcessorOptions {
staleIssueLabel: string; staleIssueLabel: string;
closeIssueLabel: string; closeIssueLabel: string;
exemptIssueLabels: string; exemptIssueLabels: string;
exemptPinnedIssues: boolean;
stalePrLabel: string; stalePrLabel: string;
closePrLabel: string; closePrLabel: string;
exemptPrLabels: string; exemptPrLabels: string;

View File

@ -47,6 +47,7 @@ function _getAndValidateArgs(): IIssuesProcessorOptions {
staleIssueLabel: core.getInput('stale-issue-label', {required: true}), staleIssueLabel: core.getInput('stale-issue-label', {required: true}),
closeIssueLabel: core.getInput('close-issue-label'), closeIssueLabel: core.getInput('close-issue-label'),
exemptIssueLabels: core.getInput('exempt-issue-labels'), exemptIssueLabels: core.getInput('exempt-issue-labels'),
exemptPinnedIssues: core.getInput('exempt-pinned-issues') === 'true',
stalePrLabel: core.getInput('stale-pr-label', {required: true}), stalePrLabel: core.getInput('stale-pr-label', {required: true}),
closePrLabel: core.getInput('close-pr-label'), closePrLabel: core.getInput('close-pr-label'),
exemptPrLabels: core.getInput('exempt-pr-labels'), exemptPrLabels: core.getInput('exempt-pr-labels'),