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.'
default: ''
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:
description: 'Exempt all pull requests with assignees from being marked as stale. Override "exempt-all-assignees" option regarding only the pull requests.'
default: ''

52
dist/index.js vendored
View File

@ -408,6 +408,8 @@ class IssuesProcessor {
this.addedLabelIssues = [];
this.addedCloseCommentIssues = [];
this._logger = new logger_1.Logger();
this._lastIssueEvents = [];
this._lastIssueEventsIssueId = -1;
this.options = options;
this.state = state;
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);
});
}
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) {
var _a;
return __awaiter(this, void 0, void 0, function* () {
@ -504,6 +534,11 @@ class IssuesProcessor {
IssuesProcessor._endIssueProcessing(issue);
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));
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)})`);
@ -702,15 +737,8 @@ class IssuesProcessor {
issueLogger.info(`Checking for label on this $$type`);
this._consumeIssueOperation(issue);
(_a = this.statistics) === null || _a === void 0 ? void 0 : _a.incrementFetchedItemsEventsCount();
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);
const reversedEvents = events.reverse();
const staleLabeledEvent = reversedEvents.find(event => event.event === 'labeled' &&
const events = yield this.getIssueEvents(issue);
const staleLabeledEvent = events.find(event => event.event === 'labeled' &&
(0, clean_label_1.cleanLabel)(event.label.name) === (0, clean_label_1.cleanLabel)(label));
if (!staleLabeledEvent) {
// Must be old rather than labeled
@ -1025,6 +1053,11 @@ class IssuesProcessor {
_isIncludeOnlyAssigned(issue) {
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) {
if (issue.isPullRequest) {
if (this.options.anyOfPrLabels !== '') {
@ -2461,6 +2494,7 @@ function _getAndValidateArgs() {
staleIssueLabel: core.getInput('stale-issue-label', { required: true }),
closeIssueLabel: core.getInput('close-issue-label'),
exemptIssueLabels: core.getInput('exempt-issue-labels'),
exemptPinnedIssues: core.getInput('exempt-pinned-issues') === 'true',
stalePrLabel: core.getInput('stale-pr-label', { required: true }),
closePrLabel: core.getInput('close-pr-label'),
exemptPrLabels: core.getInput('exempt-pr-labels'),

View File

@ -193,6 +193,35 @@ export class IssuesProcessor {
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(
issue: Issue,
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
}
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));
if (onlyLabels.length > 0) {
@ -593,17 +628,9 @@ export class IssuesProcessor {
this._consumeIssueOperation(issue);
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 reversedEvents = events.reverse();
const staleLabeledEvent = reversedEvents.find(
const events = await this.getIssueEvents(issue);
const staleLabeledEvent = events.find(
event =>
event.event === 'labeled' &&
cleanLabel(event.label.name) === cleanLabel(label)
@ -1074,6 +1101,12 @@ export class IssuesProcessor {
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 {
if (issue.isPullRequest) {
if (this.options.anyOfPrLabels !== '') {

View File

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

View File

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