Merge acfe4b9de04eadf6071caa3a071a63e28eb7c00e into 3f3b0175e8c66fb49b9a6d5a0cd1f8436d4c3ab6

This commit is contained in:
Sergey Dolin 2023-12-14 14:24:17 -08:00 committed by GitHub
commit b9ffbcdb43
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
10 changed files with 245 additions and 74 deletions

109
README.md
View File

@ -42,61 +42,62 @@ configured for the repo.
Every argument is optional.
| Input | Description | Default |
| ------------------------------------------------------------------- | --------------------------------------------------------------------------- | --------------------- |
| [repo-token](#repo-token) | PAT for GitHub API authentication | `${{ github.token }}` |
| [days-before-stale](#days-before-stale) | Idle number of days before marking issues/PRs stale | `60` |
| [days-before-issue-stale](#days-before-issue-stale) | Override [days-before-stale](#days-before-stale) for issues only | |
| [days-before-pr-stale](#days-before-pr-stale) | Override [days-before-stale](#days-before-stale) for PRs only | |
| [days-before-close](#days-before-close) | Idle number of days before closing stale issues/PRs | `7` |
| [days-before-issue-close](#days-before-issue-close) | Override [days-before-close](#days-before-close) for issues only | |
| [days-before-pr-close](#days-before-pr-close) | Override [days-before-close](#days-before-close) for PRs only | |
| [stale-issue-message](#stale-issue-message) | Comment on the staled issues | |
| [stale-pr-message](#stale-pr-message) | Comment on the staled PRs | |
| [close-issue-message](#close-issue-message) | Comment on the staled issues while closed | |
| [close-pr-message](#close-pr-message) | Comment on the staled PRs while closed | |
| [stale-issue-label](#stale-issue-label) | Label to apply on staled issues | `Stale` |
| [close-issue-label](#close-issue-label) | Label to apply on closed issues | |
| [close-issue-reason](#close-issue-reason) | Reason to use when closing issues | `not_planned` |
| [stale-pr-label](#stale-pr-label) | Label to apply on staled PRs | `Stale` |
| [close-pr-label](#close-pr-label) | Label to apply on closed PRs | |
| [exempt-issue-labels](#exempt-issue-labels) | Labels on issues exempted from stale | |
| [exempt-pr-labels](#exempt-pr-labels) | Labels on PRs exempted from stale | |
| [only-labels](#only-labels) | Only issues/PRs with ALL these labels are checked | |
| [only-issue-labels](#only-issue-labels) | Override [only-labels](#only-labels) for issues only | |
| [only-pr-labels](#only-pr-labels) | Override [only-labels](#only-labels) for PRs only | |
| [any-of-labels](#any-of-labels) | Only issues/PRs with ANY of these labels are checked | |
| [any-of-issue-labels](#any-of-issue-labels) | Override [any-of-labels](#any-of-labels) for issues only | |
| [any-of-pr-labels](#any-of-pr-labels) | Override [any-of-labels](#any-of-labels) for PRs only | |
| [operations-per-run](#operations-per-run) | Max number of operations per run | `30` |
| [remove-stale-when-updated](#remove-stale-when-updated) | Remove stale label from issues/PRs on updates | `true` |
| [remove-issue-stale-when-updated](#remove-issue-stale-when-updated) | Remove stale label from issues on updates/comments | |
| [remove-pr-stale-when-updated](#remove-pr-stale-when-updated) | Remove stale label from PRs on updates/comments | |
| [labels-to-add-when-unstale](#labels-to-add-when-unstale) | Add specified labels from issues/PRs when they become unstale | |
| [labels-to-remove-when-stale](#labels-to-remove-when-stale) | Remove specified labels from issues/PRs when they become stale | |
| [labels-to-remove-when-unstale](#labels-to-remove-when-unstale) | Remove specified labels from issues/PRs when they become unstale | |
| [debug-only](#debug-only) | Dry-run | `false` |
| [ascending](#ascending) | Order to get issues/PRs | `false` |
| [start-date](#start-date) | Skip stale action for issues/PRs created before it | |
| [delete-branch](#delete-branch) | Delete branch after closing a stale PR | `false` |
| [exempt-milestones](#exempt-milestones) | Milestones on issues/PRs exempted from stale | |
| [exempt-issue-milestones](#exempt-issue-milestones) | Override [exempt-milestones](#exempt-milestones) for issues only | |
| [exempt-pr-milestones](#exempt-pr-milestones) | Override [exempt-milestones](#exempt-milestones) for PRs only | |
| [exempt-all-milestones](#exempt-all-milestones) | Exempt all issues/PRs with milestones from stale | `false` |
| [exempt-all-issue-milestones](#exempt-all-issue-milestones) | Override [exempt-all-milestones](#exempt-all-milestones) for issues only | |
| [exempt-all-pr-milestones](#exempt-all-pr-milestones) | Override [exempt-all-milestones](#exempt-all-milestones) for PRs only | |
| [exempt-assignees](#exempt-assignees) | Assignees on issues/PRs exempted from stale | |
| [exempt-issue-assignees](#exempt-issue-assignees) | Override [exempt-assignees](#exempt-assignees) for issues only | |
| [exempt-pr-assignees](#exempt-pr-assignees) | Override [exempt-assignees](#exempt-assignees) for PRs only | |
| [exempt-all-assignees](#exempt-all-assignees) | Exempt all issues/PRs with assignees from stale | `false` |
| [exempt-all-issue-assignees](#exempt-all-issue-assignees) | Override [exempt-all-assignees](#exempt-all-assignees) for issues only | |
| [exempt-all-pr-assignees](#exempt-all-pr-assignees) | Override [exempt-all-assignees](#exempt-all-assignees) for PRs only | |
| [exempt-draft-pr](#exempt-draft-pr) | Skip the stale action for draft PRs | `false` |
| [enable-statistics](#enable-statistics) | Display statistics in the logs | `true` |
| Input | Description | Default |
|---------------------------------------------------------------------|----------------------------------------------------------------------------|-----------------------|
| [repo-token](#repo-token) | PAT for GitHub API authentication | `${{ github.token }}` |
| [days-before-stale](#days-before-stale) | Idle number of days before marking issues/PRs stale | `60` |
| [days-before-issue-stale](#days-before-issue-stale) | Override [days-before-stale](#days-before-stale) for issues only | |
| [days-before-pr-stale](#days-before-pr-stale) | Override [days-before-stale](#days-before-stale) for PRs only | |
| [days-before-close](#days-before-close) | Idle number of days before closing stale issues/PRs | `7` |
| [days-before-issue-close](#days-before-issue-close) | Override [days-before-close](#days-before-close) for issues only | |
| [days-before-pr-close](#days-before-pr-close) | Override [days-before-close](#days-before-close) for PRs only | |
| [stale-issue-message](#stale-issue-message) | Comment on the staled issues | |
| [stale-pr-message](#stale-pr-message) | Comment on the staled PRs | |
| [close-issue-message](#close-issue-message) | Comment on the staled issues while closed | |
| [close-pr-message](#close-pr-message) | Comment on the staled PRs while closed | |
| [stale-issue-label](#stale-issue-label) | Label to apply on staled issues | `Stale` |
| [close-issue-label](#close-issue-label) | Label to apply on closed issues | |
| [close-issue-reason](#close-issue-reason) | Reason to use when closing issues | `not_planned` |
| [stale-pr-label](#stale-pr-label) | Label to apply on staled PRs | `Stale` |
| [close-pr-label](#close-pr-label) | Label to apply on closed PRs | |
| [exempt-issue-labels](#exempt-issue-labels) | Labels on issues exempted from stale | |
| [exempt-pr-labels](#exempt-pr-labels) | Labels on PRs exempted from stale | |
| [only-labels](#only-labels) | Only issues/PRs with ALL these labels are checked | |
| [only-issue-labels](#only-issue-labels) | Override [only-labels](#only-labels) for issues only | |
| [only-pr-labels](#only-pr-labels) | Override [only-labels](#only-labels) for PRs only | |
| [any-of-labels](#any-of-labels) | Only issues/PRs with ANY of these labels are checked | |
| [any-of-issue-labels](#any-of-issue-labels) | Override [any-of-labels](#any-of-labels) for issues only | |
| [any-of-pr-labels](#any-of-pr-labels) | Override [any-of-labels](#any-of-labels) for PRs only | |
| [operations-per-run](#operations-per-run) | Max number of operations per run | `30` |
| [remove-stale-when-updated](#remove-stale-when-updated) | Remove stale label from issues/PRs on updates | `true` |
| [remove-issue-stale-when-updated](#remove-issue-stale-when-updated) | Remove stale label from issues on updates/comments | |
| [remove-pr-stale-when-updated](#remove-pr-stale-when-updated) | Remove stale label from PRs on updates/comments | |
| [labels-to-add-when-unstale](#labels-to-add-when-unstale) | Add specified labels from issues/PRs when they become unstale | |
| [labels-to-remove-when-stale](#labels-to-remove-when-stale) | Remove specified labels from issues/PRs when they become stale | |
| [labels-to-remove-when-unstale](#labels-to-remove-when-unstale) | Remove specified labels from issues/PRs when they become unstale | |
| [debug-only](#debug-only) | Dry-run | `false` |
| [ascending](#ascending) | Order to get issues/PRs | `false` |
| [start-date](#start-date) | Skip stale action for issues/PRs created before it | |
| [delete-branch](#delete-branch) | Delete branch after closing a stale PR | `false` |
| [exempt-milestones](#exempt-milestones) | Milestones on issues/PRs exempted from stale | |
| [exempt-issue-milestones](#exempt-issue-milestones) | Override [exempt-milestones](#exempt-milestones) for issues only | |
| [exempt-pr-milestones](#exempt-pr-milestones) | Override [exempt-milestones](#exempt-milestones) for PRs only | |
| [exempt-all-milestones](#exempt-all-milestones) | Exempt all issues/PRs with milestones from stale | `false` |
| [exempt-all-issue-milestones](#exempt-all-issue-milestones) | Override [exempt-all-milestones](#exempt-all-milestones) for issues only | |
| [exempt-all-pr-milestones](#exempt-all-pr-milestones) | Override [exempt-all-milestones](#exempt-all-milestones) for PRs only | |
| [exempt-assignees](#exempt-assignees) | Assignees on issues/PRs exempted from stale | |
| [exempt-issue-assignees](#exempt-issue-assignees) | Override [exempt-assignees](#exempt-assignees) for issues only | |
| [exempt-pr-assignees](#exempt-pr-assignees) | Override [exempt-assignees](#exempt-assignees) for PRs only | |
| [exempt-pinned-issues](#exempt-pinned-issues) | Exempt pinned issues from stale | `false` |
| [exempt-all-assignees](#exempt-all-assignees) | Exempt all issues/PRs with assignees from stale | `false` |
| [exempt-all-issue-assignees](#exempt-all-issue-assignees) | Override [exempt-all-assignees](#exempt-all-assignees) for issues only | |
| [exempt-all-pr-assignees](#exempt-all-pr-assignees) | Override [exempt-all-assignees](#exempt-all-assignees) for PRs only | |
| [exempt-draft-pr](#exempt-draft-pr) | Skip the stale action for draft PRs | `false` |
| [enable-statistics](#enable-statistics) | Display statistics in the logs | `true` |
| [ignore-updates](#ignore-updates) | Any update (update/comment) can reset the stale idle time on the issues/PRs | `false` |
| [ignore-issue-updates](#ignore-issue-updates) | Override [ignore-updates](#ignore-updates) for issues only | |
| [ignore-pr-updates](#ignore-pr-updates) | Override [ignore-updates](#ignore-updates) for PRs only | |
| [include-only-assigned](#include-only-assigned) | Process only assigned issues | `false` |
| [ignore-issue-updates](#ignore-issue-updates) | Override [ignore-updates](#ignore-updates) for issues only | |
| [ignore-pr-updates](#ignore-pr-updates) | Override [ignore-updates](#ignore-updates) for PRs only | |
| [include-only-assigned](#include-only-assigned) | Process only assigned issues | `false` |
### List of output options

View File

@ -4,6 +4,7 @@ import {IComment} from '../../src/interfaces/comment';
import {IIssuesProcessorOptions} from '../../src/interfaces/issues-processor-options';
import {IPullRequest} from '../../src/interfaces/pull-request';
import {IState} from '../../src/interfaces/state/state';
import {IIssueEvent} from '../../src/interfaces/issue-event';
export class IssuesProcessorMock extends IssuesProcessor {
constructor(
@ -18,7 +19,8 @@ export class IssuesProcessorMock extends IssuesProcessor {
issue: Issue,
label: string
) => Promise<string | undefined>,
getPullRequest?: (issue: Issue) => Promise<IPullRequest | undefined | void>
getPullRequest?: (issue: Issue) => Promise<IPullRequest | undefined | void>,
getIssueEvents?: (issue: Issue) => Promise<IIssueEvent[]>
) {
super(options, state);
@ -37,5 +39,9 @@ export class IssuesProcessorMock extends IssuesProcessor {
if (getPullRequest) {
this.getPullRequest = getPullRequest;
}
if (getIssueEvents) {
this.getIssueEvents = getIssueEvents;
}
}
}

View File

@ -17,6 +17,7 @@ export const DefaultProcessorOptions: IIssuesProcessorOptions = Object.freeze({
staleIssueLabel: 'Stale',
closeIssueLabel: '',
exemptIssueLabels: '',
exemptPinnedIssues: false,
stalePrLabel: 'Stale',
closePrLabel: '',
exemptPrLabels: '',

89
__tests__/pinned.spec.ts Normal file
View File

@ -0,0 +1,89 @@
import {IIssuesProcessorOptions} from '../src/interfaces/issues-processor-options';
import {DefaultProcessorOptions} from './constants/default-processor-options';
import {Issue} from '../src/classes/issue';
import {generateIssue} from './functions/generate-issue';
import {IssuesProcessorMock} from './classes/issues-processor-mock';
import {alwaysFalseStateMock} from './classes/state-mock';
import {IIssueEvent} from '../src/interfaces/issue-event';
const opts: IIssuesProcessorOptions = {
...DefaultProcessorOptions,
daysBeforeClose: 0
};
const testIssueList = async (page: number): Promise<Issue[]> => {
return page == 1
? [
generateIssue(opts, 1, 'Issue 1', '2020-01-01T17:00:00Z'),
generateIssue(opts, 2, 'Issue 2', '2020-01-01T17:00:00Z')
]
: [];
};
const pinnedEvent: IIssueEvent = {
created_at: '2020-01-01T17:00:00Z',
event: 'pinned',
label: {}
};
const unpinnedEvent: IIssueEvent = {
created_at: '2020-01-01T17:00:00Z',
event: 'unpinned',
label: {}
};
describe('exempt-pinned-issues options', (): void => {
it('pinned issues should be skipped if exemptPinnedIssues true', async () => {
const processor = new IssuesProcessorMock(
{...opts, exemptPinnedIssues: true},
alwaysFalseStateMock,
testIssueList,
async () => [],
async () => new Date().toDateString(),
async (issue: Issue) => undefined,
async (issue: Issue) => (issue.number === 1 ? [pinnedEvent] : [])
);
await processor.processIssues(1);
expect(processor.staleIssues).toHaveLength(1);
});
it('pinned issues should not be skipped if exemptPinnedIssues false', async () => {
const processor = new IssuesProcessorMock(
{...opts, exemptPinnedIssues: false},
alwaysFalseStateMock,
testIssueList,
async () => [],
async () => new Date().toDateString(),
async (issue: Issue) => undefined,
async (issue: Issue) => (issue.number === 1 ? [pinnedEvent] : [])
);
await processor.processIssues(1);
expect(processor.staleIssues).toHaveLength(2);
});
it('pinned issues should not be skipped if exemptPinnedIssues true but it was unpinned', async () => {
const processor = new IssuesProcessorMock(
{...opts, exemptPinnedIssues: true},
alwaysFalseStateMock,
testIssueList,
async () => [],
async () => new Date().toDateString(),
async (issue: Issue) => undefined,
async (issue: Issue) =>
issue.number === 1 ? [unpinnedEvent, pinnedEvent] : []
);
await processor.processIssues(1);
expect(processor.staleIssues).toHaveLength(2);
});
it('pinned issues should not be skipped if exemptPinnedIssues true and it was unpinned and pinned', async () => {
const processor = new IssuesProcessorMock(
{...opts, exemptPinnedIssues: true},
alwaysFalseStateMock,
testIssueList,
async () => [],
async () => new Date().toDateString(),
async (issue: Issue) => undefined,
async (issue: Issue) =>
issue.number === 1 ? [pinnedEvent, unpinnedEvent, pinnedEvent] : []
);
await processor.processIssues(1);
expect(processor.staleIssues).toHaveLength(1);
});
});

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

@ -409,6 +409,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);
@ -465,6 +467,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* () {
@ -505,6 +535,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)})`);
@ -703,15 +738,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
@ -1038,6 +1066,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 !== '') {
@ -2527,6 +2560,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

@ -26,6 +26,7 @@ describe('Issue', (): void => {
debugOnly: false,
deleteBranch: false,
exemptIssueLabels: '',
exemptPinnedIssues: false,
exemptPrLabels: '',
onlyLabels: '',
onlyIssueLabels: '',

View File

@ -196,6 +196,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>[],
@ -251,6 +280,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) {
@ -596,17 +631,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)
@ -1087,6 +1114,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

@ -74,6 +74,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'),