Merge ac20e355748a11b818619bf1692eabf8b946223a into 3f3b0175e8c66fb49b9a6d5a0cd1f8436d4c3ab6
This commit is contained in:
commit
8182a0d5e7
@ -96,6 +96,7 @@ Every argument is optional.
|
||||
| [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 | |
|
||||
| [ignore-reactions](#ignore-reactions) | Any reaction can reset the stale idle time on the issues/PRs | |
|
||||
| [include-only-assigned](#include-only-assigned) | Process only assigned issues | `false` |
|
||||
|
||||
### List of output options
|
||||
@ -541,6 +542,12 @@ Useful to override [ignore-updates](#ignore-updates) but only to ignore the upda
|
||||
|
||||
Default value: unset
|
||||
|
||||
#### ignore-reactions
|
||||
|
||||
If set to `false`, any reaction to an issue will be considered an update.
|
||||
|
||||
Default value: unset
|
||||
|
||||
#### include-only-assigned
|
||||
|
||||
If set to `true`, only the issues or the pull requests with an assignee will be marked as stale automatically.
|
||||
|
||||
@ -1144,6 +1144,7 @@ class IssuesProcessorBuilder {
|
||||
new StateMock(),
|
||||
async p => (p === 1 ? this._issues : []),
|
||||
async () => [],
|
||||
async () => [],
|
||||
async () => new Date().toDateString()
|
||||
);
|
||||
}
|
||||
|
||||
@ -53,6 +53,7 @@ describe('assignees options', (): void => {
|
||||
alwaysFalseStateMock,
|
||||
async p => (p === 1 ? testIssueList : []),
|
||||
async () => [],
|
||||
async () => [],
|
||||
async () => new Date().toDateString()
|
||||
);
|
||||
};
|
||||
|
||||
@ -1,6 +1,7 @@
|
||||
import {Issue} from '../../src/classes/issue';
|
||||
import {IssuesProcessor} from '../../src/classes/issues-processor';
|
||||
import {IComment} from '../../src/interfaces/comment';
|
||||
import {IReaction} from '../../src/interfaces/reaction';
|
||||
import {IIssuesProcessorOptions} from '../../src/interfaces/issues-processor-options';
|
||||
import {IPullRequest} from '../../src/interfaces/pull-request';
|
||||
import {IState} from '../../src/interfaces/state/state';
|
||||
@ -14,6 +15,10 @@ export class IssuesProcessorMock extends IssuesProcessor {
|
||||
issue: Issue,
|
||||
sinceDate: string
|
||||
) => Promise<IComment[]>,
|
||||
listIssueReactions?: (
|
||||
issue: Issue,
|
||||
sinceDate: string
|
||||
) => Promise<IReaction[]>,
|
||||
getLabelCreationDate?: (
|
||||
issue: Issue,
|
||||
label: string
|
||||
@ -30,6 +35,10 @@ export class IssuesProcessorMock extends IssuesProcessor {
|
||||
this.listIssueComments = listIssueComments;
|
||||
}
|
||||
|
||||
if (listIssueReactions) {
|
||||
this.listIssueReactions = listIssueReactions;
|
||||
}
|
||||
|
||||
if (getLabelCreationDate) {
|
||||
this.getLabelCreationDate = getLabelCreationDate;
|
||||
}
|
||||
|
||||
@ -53,6 +53,7 @@ export const DefaultProcessorOptions: IIssuesProcessorOptions = Object.freeze({
|
||||
ignoreUpdates: false,
|
||||
ignoreIssueUpdates: undefined,
|
||||
ignorePrUpdates: undefined,
|
||||
ignoreReactions: undefined,
|
||||
exemptDraftPr: false,
|
||||
closeIssueReason: 'not_planned',
|
||||
includeOnlyAssigned: false
|
||||
|
||||
@ -128,6 +128,7 @@ class IssuesProcessorBuilder {
|
||||
alwaysFalseStateMock,
|
||||
async p => (p === 1 ? this._issues : []),
|
||||
async () => [],
|
||||
async () => [],
|
||||
async () => new Date().toDateString(),
|
||||
async (): Promise<IPullRequest> => {
|
||||
return Promise.resolve({
|
||||
|
||||
@ -20,6 +20,7 @@ test('processing an issue with no label will make it stale and close it, if it i
|
||||
alwaysFalseStateMock,
|
||||
async p => (p === 1 ? TestIssueList : []),
|
||||
async () => [],
|
||||
async () => [],
|
||||
async () => new Date().toDateString()
|
||||
);
|
||||
|
||||
@ -52,6 +53,7 @@ test('processing an issue with no label and a start date as ECMAScript epoch in
|
||||
alwaysFalseStateMock,
|
||||
async p => (p === 1 ? TestIssueList : []),
|
||||
async () => [],
|
||||
async () => [],
|
||||
async () => new Date().toDateString()
|
||||
);
|
||||
|
||||
@ -84,6 +86,7 @@ test('processing an issue with no label and a start date as ECMAScript epoch in
|
||||
alwaysFalseStateMock,
|
||||
async p => (p === 1 ? TestIssueList : []),
|
||||
async () => [],
|
||||
async () => [],
|
||||
async () => new Date().toDateString()
|
||||
);
|
||||
|
||||
@ -116,6 +119,7 @@ test('processing an issue with no label and a start date as ECMAScript epoch in
|
||||
alwaysFalseStateMock,
|
||||
async p => (p === 1 ? TestIssueList : []),
|
||||
async () => [],
|
||||
async () => [],
|
||||
async () => new Date().toDateString()
|
||||
);
|
||||
|
||||
@ -148,6 +152,7 @@ test('processing an issue with no label and a start date as ECMAScript epoch in
|
||||
alwaysFalseStateMock,
|
||||
async p => (p === 1 ? TestIssueList : []),
|
||||
async () => [],
|
||||
async () => [],
|
||||
async () => new Date().toDateString()
|
||||
);
|
||||
|
||||
@ -180,6 +185,7 @@ test('processing an issue with no label and a start date as ISO 8601 being befor
|
||||
alwaysFalseStateMock,
|
||||
async p => (p === 1 ? TestIssueList : []),
|
||||
async () => [],
|
||||
async () => [],
|
||||
async () => new Date().toDateString()
|
||||
);
|
||||
|
||||
@ -212,6 +218,7 @@ test('processing an issue with no label and a start date as ISO 8601 being after
|
||||
alwaysFalseStateMock,
|
||||
async p => (p === 1 ? TestIssueList : []),
|
||||
async () => [],
|
||||
async () => [],
|
||||
async () => new Date().toDateString()
|
||||
);
|
||||
|
||||
@ -244,6 +251,7 @@ test('processing an issue with no label and a start date as RFC 2822 being befor
|
||||
alwaysFalseStateMock,
|
||||
async p => (p === 1 ? TestIssueList : []),
|
||||
async () => [],
|
||||
async () => [],
|
||||
async () => new Date().toDateString()
|
||||
);
|
||||
|
||||
@ -276,6 +284,7 @@ test('processing an issue with no label and a start date as RFC 2822 being after
|
||||
alwaysFalseStateMock,
|
||||
async p => (p === 1 ? TestIssueList : []),
|
||||
async () => [],
|
||||
async () => [],
|
||||
async () => new Date().toDateString()
|
||||
);
|
||||
|
||||
@ -300,6 +309,7 @@ test('processing an issue with no label will make it stale and close it, if it i
|
||||
alwaysFalseStateMock,
|
||||
async p => (p === 1 ? TestIssueList : []),
|
||||
async () => [],
|
||||
async () => [],
|
||||
async () => new Date().toDateString()
|
||||
);
|
||||
|
||||
@ -325,6 +335,7 @@ test('processing an issue with no label will make it stale and not close it, if
|
||||
alwaysFalseStateMock,
|
||||
async p => (p === 1 ? TestIssueList : []),
|
||||
async () => [],
|
||||
async () => [],
|
||||
async () => new Date().toDateString()
|
||||
);
|
||||
|
||||
@ -348,6 +359,7 @@ test('processing an issue with no label will make it stale and not close it if d
|
||||
alwaysFalseStateMock,
|
||||
async p => (p === 1 ? TestIssueList : []),
|
||||
async () => [],
|
||||
async () => [],
|
||||
async () => new Date().toDateString()
|
||||
);
|
||||
|
||||
@ -372,6 +384,7 @@ test('processing an issue with no label will make it stale and not close it if d
|
||||
alwaysFalseStateMock,
|
||||
async p => (p === 1 ? TestIssueList : []),
|
||||
async () => [],
|
||||
async () => [],
|
||||
async () => new Date().toDateString()
|
||||
);
|
||||
|
||||
@ -396,6 +409,7 @@ test('processing an issue with no label will not make it stale if days-before-st
|
||||
alwaysFalseStateMock,
|
||||
async p => (p === 1 ? TestIssueList : []),
|
||||
async () => [],
|
||||
async () => [],
|
||||
async () => new Date().toDateString()
|
||||
);
|
||||
|
||||
@ -421,6 +435,7 @@ test('processing an issue with no label will not make it stale if days-before-st
|
||||
alwaysFalseStateMock,
|
||||
async p => (p === 1 ? TestIssueList : []),
|
||||
async () => [],
|
||||
async () => [],
|
||||
async () => new Date().toDateString()
|
||||
);
|
||||
|
||||
@ -449,6 +464,7 @@ test('processing an issue with no label will make it stale but not close it', as
|
||||
alwaysFalseStateMock,
|
||||
async p => (p === 1 ? TestIssueList : []),
|
||||
async () => [],
|
||||
async () => [],
|
||||
async () => new Date().toDateString()
|
||||
);
|
||||
|
||||
@ -481,6 +497,7 @@ test('processing a stale issue will close it', async () => {
|
||||
alwaysFalseStateMock,
|
||||
async p => (p === 1 ? TestIssueList : []),
|
||||
async () => [],
|
||||
async () => [],
|
||||
async () => new Date().toDateString()
|
||||
);
|
||||
|
||||
@ -513,6 +530,7 @@ test('processing a stale issue containing a space in the label will close it', a
|
||||
alwaysFalseStateMock,
|
||||
async p => (p === 1 ? TestIssueList : []),
|
||||
async () => [],
|
||||
async () => [],
|
||||
async () => new Date().toDateString()
|
||||
);
|
||||
|
||||
@ -545,6 +563,7 @@ test('processing a stale issue containing a slash in the label will close it', a
|
||||
alwaysFalseStateMock,
|
||||
async p => (p === 1 ? TestIssueList : []),
|
||||
async () => [],
|
||||
async () => [],
|
||||
async () => new Date().toDateString()
|
||||
);
|
||||
|
||||
@ -578,6 +597,7 @@ test('processing a stale issue will close it when days-before-issue-stale overri
|
||||
alwaysFalseStateMock,
|
||||
async p => (p === 1 ? TestIssueList : []),
|
||||
async () => [],
|
||||
async () => [],
|
||||
async () => new Date().toDateString()
|
||||
);
|
||||
|
||||
@ -610,6 +630,7 @@ test('processing a stale PR will close it', async () => {
|
||||
alwaysFalseStateMock,
|
||||
async p => (p === 1 ? TestIssueList : []),
|
||||
async () => [],
|
||||
async () => [],
|
||||
async () => new Date().toDateString()
|
||||
);
|
||||
|
||||
@ -643,6 +664,7 @@ test('processing a stale PR will close it when days-before-pr-stale override day
|
||||
alwaysFalseStateMock,
|
||||
async p => (p === 1 ? TestIssueList : []),
|
||||
async () => [],
|
||||
async () => [],
|
||||
async () => new Date().toDateString()
|
||||
);
|
||||
|
||||
@ -676,6 +698,7 @@ test('processing a stale issue will close it even if configured not to mark as s
|
||||
alwaysFalseStateMock,
|
||||
async p => (p === 1 ? TestIssueList : []),
|
||||
async () => [],
|
||||
async () => [],
|
||||
async () => new Date().toDateString()
|
||||
);
|
||||
|
||||
@ -710,6 +733,7 @@ test('processing a stale issue will close it even if configured not to mark as s
|
||||
alwaysFalseStateMock,
|
||||
async p => (p === 1 ? TestIssueList : []),
|
||||
async () => [],
|
||||
async () => [],
|
||||
async () => new Date().toDateString()
|
||||
);
|
||||
|
||||
@ -743,6 +767,7 @@ test('processing a stale PR will close it even if configured not to mark as stal
|
||||
alwaysFalseStateMock,
|
||||
async p => (p === 1 ? TestIssueList : []),
|
||||
async () => [],
|
||||
async () => [],
|
||||
async () => new Date().toDateString()
|
||||
);
|
||||
|
||||
@ -777,6 +802,7 @@ test('processing a stale PR will close it even if configured not to mark as stal
|
||||
alwaysFalseStateMock,
|
||||
async p => (p === 1 ? TestIssueList : []),
|
||||
async () => [],
|
||||
async () => [],
|
||||
async () => new Date().toDateString()
|
||||
);
|
||||
|
||||
@ -834,6 +860,7 @@ test('stale closed issues will not be closed', async () => {
|
||||
alwaysFalseStateMock,
|
||||
async p => (p === 1 ? TestIssueList : []),
|
||||
async () => [],
|
||||
async () => [],
|
||||
async () => new Date().toDateString()
|
||||
);
|
||||
|
||||
@ -863,6 +890,7 @@ test('closed prs will not be marked stale', async () => {
|
||||
alwaysFalseStateMock,
|
||||
async p => (p === 1 ? TestIssueList : []),
|
||||
async () => [],
|
||||
async () => [],
|
||||
async () => new Date().toDateString()
|
||||
);
|
||||
|
||||
@ -892,6 +920,7 @@ test('stale closed prs will not be closed', async () => {
|
||||
alwaysFalseStateMock,
|
||||
async p => (p === 1 ? TestIssueList : []),
|
||||
async () => [],
|
||||
async () => [],
|
||||
async () => new Date().toDateString()
|
||||
);
|
||||
|
||||
@ -950,6 +979,7 @@ test('stale locked issues will not be closed', async () => {
|
||||
alwaysFalseStateMock,
|
||||
async p => (p === 1 ? TestIssueList : []),
|
||||
async () => [],
|
||||
async () => [],
|
||||
async () => new Date().toDateString()
|
||||
);
|
||||
|
||||
@ -1008,6 +1038,7 @@ test('stale locked prs will not be closed', async () => {
|
||||
alwaysFalseStateMock,
|
||||
async p => (p === 1 ? TestIssueList : []),
|
||||
async () => [],
|
||||
async () => [],
|
||||
async () => new Date().toDateString()
|
||||
);
|
||||
|
||||
@ -1039,6 +1070,7 @@ test('exempt issue labels will not be marked stale', async () => {
|
||||
alwaysFalseStateMock,
|
||||
async p => (p === 1 ? TestIssueList : []),
|
||||
async () => [],
|
||||
async () => [],
|
||||
async () => new Date().toDateString()
|
||||
);
|
||||
|
||||
@ -1070,6 +1102,7 @@ test('exempt issue labels will not be marked stale (multi issue label with space
|
||||
alwaysFalseStateMock,
|
||||
async p => (p === 1 ? TestIssueList : []),
|
||||
async () => [],
|
||||
async () => [],
|
||||
async () => new Date().toDateString()
|
||||
);
|
||||
|
||||
@ -1100,6 +1133,7 @@ test('exempt issue labels will not be marked stale (multi issue label)', async (
|
||||
alwaysFalseStateMock,
|
||||
async p => (p === 1 ? TestIssueList : []),
|
||||
async () => [],
|
||||
async () => [],
|
||||
async () => new Date().toDateString()
|
||||
);
|
||||
|
||||
@ -1150,6 +1184,7 @@ test('exempt pr labels will not be marked stale', async () => {
|
||||
alwaysFalseStateMock,
|
||||
async p => (p === 1 ? TestIssueList : []),
|
||||
async () => [],
|
||||
async () => [],
|
||||
async () => new Date().toDateString()
|
||||
);
|
||||
|
||||
@ -1199,6 +1234,7 @@ test('stale issues should not be closed if days is set to -1', async () => {
|
||||
alwaysFalseStateMock,
|
||||
async p => (p === 1 ? TestIssueList : []),
|
||||
async () => [],
|
||||
async () => [],
|
||||
async () => new Date().toDateString()
|
||||
);
|
||||
|
||||
@ -1236,6 +1272,7 @@ test('stale label should be removed if a comment was added to a stale issue', as
|
||||
body: 'Body'
|
||||
}
|
||||
], // return a fake comment to indicate there was an update
|
||||
async () => [],
|
||||
async () => new Date().toDateString()
|
||||
);
|
||||
|
||||
@ -1247,6 +1284,122 @@ test('stale label should be removed if a comment was added to a stale issue', as
|
||||
expect(processor.removedLabelIssues).toHaveLength(1);
|
||||
});
|
||||
|
||||
test('when the option "ignoreReactions" is set to false, stale label should be removed if a reaction was added to a stale issue', async () => {
|
||||
const opts = {
|
||||
...DefaultProcessorOptions,
|
||||
removeStaleWhenUpdated: true,
|
||||
ignoreReactions: false
|
||||
};
|
||||
const TestIssueList: Issue[] = [
|
||||
generateIssue(
|
||||
opts,
|
||||
1,
|
||||
'An issue that should un-stale',
|
||||
'2020-01-01T17:00:00Z',
|
||||
'2020-01-01T17:00:00Z',
|
||||
false,
|
||||
false,
|
||||
['Stale']
|
||||
)
|
||||
];
|
||||
const processor = new IssuesProcessorMock(
|
||||
opts,
|
||||
alwaysFalseStateMock,
|
||||
async p => (p === 1 ? TestIssueList : []),
|
||||
async () => [],
|
||||
async () => [
|
||||
{
|
||||
content: '+1',
|
||||
created_at: '2020-01-07T17:00:00Z'
|
||||
}
|
||||
], // return a fake reaction to indicate there was an update
|
||||
async () => new Date().toDateString()
|
||||
);
|
||||
|
||||
// process our fake issue list
|
||||
await processor.processIssues(1);
|
||||
|
||||
expect(processor.closedIssues).toHaveLength(0);
|
||||
expect(processor.staleIssues).toHaveLength(0);
|
||||
expect(processor.removedLabelIssues).toHaveLength(1);
|
||||
});
|
||||
|
||||
test('when the option "ignoreReactions" is not set, stale label should not be removed if a reaction was added to a stale issue', async () => {
|
||||
const opts = {...DefaultProcessorOptions, removeStaleWhenUpdated: true};
|
||||
const TestIssueList: Issue[] = [
|
||||
generateIssue(
|
||||
opts,
|
||||
1,
|
||||
'An issue that should un-stale',
|
||||
'2020-01-01T17:00:00Z',
|
||||
'2020-01-01T17:00:00Z',
|
||||
false,
|
||||
false,
|
||||
['Stale']
|
||||
)
|
||||
];
|
||||
const processor = new IssuesProcessorMock(
|
||||
opts,
|
||||
alwaysFalseStateMock,
|
||||
async p => (p === 1 ? TestIssueList : []),
|
||||
async () => [],
|
||||
async () => [
|
||||
{
|
||||
content: '+1',
|
||||
created_at: '2020-01-07T17:00:00Z'
|
||||
}
|
||||
], // return a fake reaction to indicate there was an update
|
||||
async () => new Date().toDateString()
|
||||
);
|
||||
|
||||
// process our fake issue list
|
||||
await processor.processIssues(1);
|
||||
|
||||
expect(processor.closedIssues).toHaveLength(1);
|
||||
expect(processor.staleIssues).toHaveLength(0);
|
||||
expect(processor.removedLabelIssues).toHaveLength(0);
|
||||
});
|
||||
|
||||
test('when the option "ignoreReactions" is set to true, stale label should not be removed if a reaction was added to a stale issue', async () => {
|
||||
const opts = {
|
||||
...DefaultProcessorOptions,
|
||||
removeStaleWhenUpdated: true,
|
||||
ignoreReactions: true
|
||||
};
|
||||
const TestIssueList: Issue[] = [
|
||||
generateIssue(
|
||||
opts,
|
||||
1,
|
||||
'An issue that should un-stale',
|
||||
'2020-01-01T17:00:00Z',
|
||||
'2020-01-01T17:00:00Z',
|
||||
false,
|
||||
false,
|
||||
['Stale']
|
||||
)
|
||||
];
|
||||
const processor = new IssuesProcessorMock(
|
||||
opts,
|
||||
alwaysFalseStateMock,
|
||||
async p => (p === 1 ? TestIssueList : []),
|
||||
async () => [],
|
||||
async () => [
|
||||
{
|
||||
content: '+1',
|
||||
created_at: '2020-01-07T17:00:00Z'
|
||||
}
|
||||
], // return a fake reaction to indicate there was an update
|
||||
async () => new Date().toDateString()
|
||||
);
|
||||
|
||||
// process our fake issue list
|
||||
await processor.processIssues(1);
|
||||
|
||||
expect(processor.closedIssues).toHaveLength(1);
|
||||
expect(processor.staleIssues).toHaveLength(0);
|
||||
expect(processor.removedLabelIssues).toHaveLength(0);
|
||||
});
|
||||
|
||||
test('when the option "labelsToAddWhenUnstale" is set, the labels should be added when unstale', async () => {
|
||||
expect.assertions(4);
|
||||
const opts = {
|
||||
@ -1279,6 +1432,7 @@ test('when the option "labelsToAddWhenUnstale" is set, the labels should be adde
|
||||
body: 'Body'
|
||||
}
|
||||
], // return a fake comment to indicate there was an update
|
||||
async () => [],
|
||||
async () => new Date().toDateString()
|
||||
);
|
||||
|
||||
@ -1325,6 +1479,7 @@ test('when the option "labelsToRemoveWhenStale" is set, the labels should be rem
|
||||
body: 'Body'
|
||||
}
|
||||
], // return a fake comment to indicate there was an update
|
||||
async () => [],
|
||||
async () => new Date().toDateString()
|
||||
);
|
||||
|
||||
@ -1365,6 +1520,7 @@ test('stale label should not be removed if a comment was added by the bot (and t
|
||||
body: 'This issue is stale'
|
||||
}
|
||||
], // return a fake comment to indicate there was an update by the bot
|
||||
async () => [],
|
||||
async () => new Date().toDateString()
|
||||
);
|
||||
|
||||
@ -1399,6 +1555,7 @@ test('stale label containing a space should be removed if a comment was added to
|
||||
alwaysFalseStateMock,
|
||||
async p => (p === 1 ? TestIssueList : []),
|
||||
async () => [{user: {login: 'notme', type: 'User'}, body: 'Body'}], // return a fake comment to indicate there was an update
|
||||
async () => [],
|
||||
async () => new Date().toDateString()
|
||||
);
|
||||
|
||||
@ -1431,6 +1588,7 @@ test('stale issues should not be closed until after the closed number of days',
|
||||
alwaysFalseStateMock,
|
||||
async p => (p === 1 ? TestIssueList : []),
|
||||
async () => [],
|
||||
async () => [],
|
||||
async () => new Date().toDateString()
|
||||
);
|
||||
|
||||
@ -1465,6 +1623,7 @@ test('stale issues should be closed if the closed nubmer of days (additive) is a
|
||||
alwaysFalseStateMock,
|
||||
async p => (p === 1 ? TestIssueList : []),
|
||||
async () => [],
|
||||
async () => [],
|
||||
async () => new Date().toDateString()
|
||||
);
|
||||
|
||||
@ -1497,6 +1656,7 @@ test('stale issues should not be closed until after the closed number of days (l
|
||||
alwaysFalseStateMock,
|
||||
async p => (p === 1 ? TestIssueList : []),
|
||||
async () => [],
|
||||
async () => [],
|
||||
async () => new Date().toDateString()
|
||||
);
|
||||
|
||||
@ -1530,6 +1690,7 @@ test('skips stale message on issues when stale-issue-message is empty', async ()
|
||||
alwaysFalseStateMock,
|
||||
async p => (p === 1 ? TestIssueList : []),
|
||||
async () => [],
|
||||
async () => [],
|
||||
async () => new Date().toDateString()
|
||||
);
|
||||
|
||||
@ -1575,6 +1736,7 @@ test('send stale message on issues when stale-issue-message is not empty', async
|
||||
alwaysFalseStateMock,
|
||||
async p => (p === 1 ? TestIssueList : []),
|
||||
async () => [],
|
||||
async () => [],
|
||||
async () => new Date().toDateString()
|
||||
);
|
||||
|
||||
@ -1621,6 +1783,7 @@ test('skips stale message on prs when stale-pr-message is empty', async () => {
|
||||
alwaysFalseStateMock,
|
||||
async p => (p === 1 ? TestIssueList : []),
|
||||
async () => [],
|
||||
async () => [],
|
||||
async () => new Date().toDateString()
|
||||
);
|
||||
|
||||
@ -1667,6 +1830,7 @@ test('send stale message on prs when stale-pr-message is not empty', async () =>
|
||||
alwaysFalseStateMock,
|
||||
async p => (p === 1 ? TestIssueList : []),
|
||||
async () => [],
|
||||
async () => [],
|
||||
async () => new Date().toDateString()
|
||||
);
|
||||
|
||||
@ -1710,6 +1874,7 @@ test('git branch is deleted when option is enabled', async () => {
|
||||
alwaysFalseStateMock,
|
||||
async p => (p === 1 ? TestIssueList : []),
|
||||
async () => [],
|
||||
async () => [],
|
||||
async () => new Date().toDateString()
|
||||
);
|
||||
|
||||
@ -1741,6 +1906,7 @@ test('git branch is not deleted when issue is not pull request', async () => {
|
||||
alwaysFalseStateMock,
|
||||
async p => (p === 1 ? TestIssueList : []),
|
||||
async () => [],
|
||||
async () => [],
|
||||
async () => new Date().toDateString()
|
||||
);
|
||||
|
||||
@ -1774,6 +1940,7 @@ test('an issue without a milestone will be marked as stale', async () => {
|
||||
alwaysFalseStateMock,
|
||||
async p => (p === 1 ? TestIssueList : []),
|
||||
async () => [],
|
||||
async () => [],
|
||||
async () => new Date().toDateString()
|
||||
);
|
||||
|
||||
@ -1809,6 +1976,7 @@ test('an issue without an exempted milestone will be marked as stale', async ()
|
||||
alwaysFalseStateMock,
|
||||
async p => (p === 1 ? TestIssueList : []),
|
||||
async () => [],
|
||||
async () => [],
|
||||
async () => new Date().toDateString()
|
||||
);
|
||||
|
||||
@ -1844,6 +2012,7 @@ test('an issue with an exempted milestone will not be marked as stale', async ()
|
||||
alwaysFalseStateMock,
|
||||
async p => (p === 1 ? TestIssueList : []),
|
||||
async () => [],
|
||||
async () => [],
|
||||
async () => new Date().toDateString()
|
||||
);
|
||||
|
||||
@ -1879,6 +2048,7 @@ test('an issue with an exempted milestone will not be marked as stale (multi mil
|
||||
alwaysFalseStateMock,
|
||||
async p => (p === 1 ? TestIssueList : []),
|
||||
async () => [],
|
||||
async () => [],
|
||||
async () => new Date().toDateString()
|
||||
);
|
||||
|
||||
@ -1914,6 +2084,7 @@ test('an issue with an exempted milestone will not be marked as stale (multi mil
|
||||
alwaysFalseStateMock,
|
||||
async p => (p === 1 ? TestIssueList : []),
|
||||
async () => [],
|
||||
async () => [],
|
||||
async () => new Date().toDateString()
|
||||
);
|
||||
|
||||
@ -1950,6 +2121,7 @@ test('an issue with an exempted milestone but without an exempted issue mileston
|
||||
alwaysFalseStateMock,
|
||||
async p => (p === 1 ? TestIssueList : []),
|
||||
async () => [],
|
||||
async () => [],
|
||||
async () => new Date().toDateString()
|
||||
);
|
||||
|
||||
@ -1986,6 +2158,7 @@ test('an issue with an exempted milestone but with another exempted issue milest
|
||||
alwaysFalseStateMock,
|
||||
async p => (p === 1 ? TestIssueList : []),
|
||||
async () => [],
|
||||
async () => [],
|
||||
async () => new Date().toDateString()
|
||||
);
|
||||
|
||||
@ -2022,6 +2195,7 @@ test('an issue with an exempted milestone and with an exempted issue milestone w
|
||||
alwaysFalseStateMock,
|
||||
async p => (p === 1 ? TestIssueList : []),
|
||||
async () => [],
|
||||
async () => [],
|
||||
async () => new Date().toDateString()
|
||||
);
|
||||
|
||||
@ -2050,6 +2224,7 @@ test('processing an issue opened since 2 days and with the option "daysBeforeIss
|
||||
alwaysFalseStateMock,
|
||||
async p => (p === 1 ? TestIssueList : []),
|
||||
async () => [],
|
||||
async () => [],
|
||||
async () => new Date().toDateString()
|
||||
);
|
||||
|
||||
@ -2077,6 +2252,7 @@ test('processing an issue opened since 2 days and with the option "daysBeforeIss
|
||||
alwaysFalseStateMock,
|
||||
async p => (p === 1 ? TestIssueList : []),
|
||||
async () => [],
|
||||
async () => [],
|
||||
async () => new Date().toDateString()
|
||||
);
|
||||
|
||||
@ -2104,6 +2280,7 @@ test('processing an issue opened since 2 days and with the option "daysBeforeIss
|
||||
alwaysFalseStateMock,
|
||||
async p => (p === 1 ? TestIssueList : []),
|
||||
async () => [],
|
||||
async () => [],
|
||||
async () => new Date().toDateString()
|
||||
);
|
||||
|
||||
@ -2131,6 +2308,7 @@ test('processing an issue opened since 1 hour and with the option "daysBeforeIss
|
||||
alwaysFalseStateMock,
|
||||
async p => (p === 1 ? TestIssueList : []),
|
||||
async () => [],
|
||||
async () => [],
|
||||
async () => new Date().toISOString()
|
||||
);
|
||||
|
||||
@ -2158,6 +2336,7 @@ test('processing an issue opened since 4 hours and with the option "daysBeforeIs
|
||||
alwaysFalseStateMock,
|
||||
async p => (p === 1 ? TestIssueList : []),
|
||||
async () => [],
|
||||
async () => [],
|
||||
async () => new Date().toISOString()
|
||||
);
|
||||
|
||||
@ -2185,6 +2364,7 @@ test('processing an issue opened since 5 hours and with the option "daysBeforeIs
|
||||
alwaysFalseStateMock,
|
||||
async p => (p === 1 ? TestIssueList : []),
|
||||
async () => [],
|
||||
async () => [],
|
||||
async () => new Date().toISOString()
|
||||
);
|
||||
|
||||
@ -2220,6 +2400,7 @@ test('processing a pull request opened since 2 days and with the option "daysBef
|
||||
alwaysFalseStateMock,
|
||||
async p => (p === 1 ? TestIssueList : []),
|
||||
async () => [],
|
||||
async () => [],
|
||||
async () => new Date().toDateString()
|
||||
);
|
||||
|
||||
@ -2255,6 +2436,7 @@ test('processing a pull request opened since 2 days and with the option "daysBef
|
||||
alwaysFalseStateMock,
|
||||
async p => (p === 1 ? TestIssueList : []),
|
||||
async () => [],
|
||||
async () => [],
|
||||
async () => new Date().toDateString()
|
||||
);
|
||||
|
||||
@ -2290,6 +2472,7 @@ test('processing a pull request opened since 2 days and with the option "daysBef
|
||||
alwaysFalseStateMock,
|
||||
async p => (p === 1 ? TestIssueList : []),
|
||||
async () => [],
|
||||
async () => [],
|
||||
async () => new Date().toDateString()
|
||||
);
|
||||
|
||||
@ -2325,6 +2508,7 @@ test('processing a pull request opened since 1 hour and with the option "daysBef
|
||||
alwaysFalseStateMock,
|
||||
async p => (p === 1 ? TestIssueList : []),
|
||||
async () => [],
|
||||
async () => [],
|
||||
async () => new Date().toISOString()
|
||||
);
|
||||
|
||||
@ -2360,6 +2544,7 @@ test('processing a pull request opened since 4 hours and with the option "daysBe
|
||||
alwaysFalseStateMock,
|
||||
async p => (p === 1 ? TestIssueList : []),
|
||||
async () => [],
|
||||
async () => [],
|
||||
async () => new Date().toISOString()
|
||||
);
|
||||
|
||||
@ -2395,6 +2580,7 @@ test('processing a pull request opened since 5 hours and with the option "daysBe
|
||||
alwaysFalseStateMock,
|
||||
async p => (p === 1 ? TestIssueList : []),
|
||||
async () => [],
|
||||
async () => [],
|
||||
async () => new Date().toISOString()
|
||||
);
|
||||
|
||||
@ -2433,6 +2619,7 @@ test('processing a previously closed issue with a close label will remove the cl
|
||||
alwaysFalseStateMock,
|
||||
async p => (p === 1 ? TestIssueList : []),
|
||||
async () => [],
|
||||
async () => [],
|
||||
async () => new Date().toDateString()
|
||||
);
|
||||
|
||||
@ -2470,6 +2657,7 @@ test('processing a closed issue with a close label will not remove the close lab
|
||||
alwaysFalseStateMock,
|
||||
async p => (p === 1 ? TestIssueList : []),
|
||||
async () => [],
|
||||
async () => [],
|
||||
async () => new Date().toDateString()
|
||||
);
|
||||
|
||||
@ -2507,6 +2695,7 @@ test('processing a locked issue with a close label will not remove the close lab
|
||||
alwaysFalseStateMock,
|
||||
async p => (p === 1 ? TestIssueList : []),
|
||||
async () => [],
|
||||
async () => [],
|
||||
async () => new Date().toDateString()
|
||||
);
|
||||
|
||||
@ -2548,6 +2737,7 @@ test('processing an issue stale since less than the daysBeforeStale with a stale
|
||||
alwaysFalseStateMock,
|
||||
async p => (p === 1 ? TestIssueList : []),
|
||||
async (): Promise<IComment[]> => Promise.resolve([]),
|
||||
async () => [],
|
||||
async () => labelCreatedAt.toDateString()
|
||||
);
|
||||
|
||||
@ -2590,6 +2780,7 @@ test('processing an issue stale since less than the daysBeforeStale without a st
|
||||
alwaysFalseStateMock,
|
||||
async p => (p === 1 ? TestIssueList : []),
|
||||
async (): Promise<IComment[]> => Promise.resolve([]),
|
||||
async () => [],
|
||||
async () => new Date().toDateString()
|
||||
);
|
||||
|
||||
@ -2627,6 +2818,7 @@ test('processing a pull request to be stale with the "stalePrMessage" option set
|
||||
alwaysFalseStateMock,
|
||||
async p => (p === 1 ? TestIssueList : []),
|
||||
async () => [],
|
||||
async () => [],
|
||||
async () => new Date().toDateString()
|
||||
);
|
||||
|
||||
@ -2664,6 +2856,7 @@ test('processing a pull request to be stale with the "stalePrMessage" option set
|
||||
alwaysFalseStateMock,
|
||||
async p => (p === 1 ? TestIssueList : []),
|
||||
async () => [],
|
||||
async () => [],
|
||||
async () => new Date().toDateString()
|
||||
);
|
||||
|
||||
@ -2706,6 +2899,7 @@ test('processing an issue with the "includeOnlyAssigned" option and nonempty ass
|
||||
alwaysFalseStateMock,
|
||||
async p => (p === 1 ? TestIssueList : []),
|
||||
async () => [],
|
||||
async () => [],
|
||||
async () => new Date().toDateString()
|
||||
);
|
||||
|
||||
@ -2734,6 +2928,7 @@ test('processing an issue with the "includeOnlyAssigned" option set and no assig
|
||||
alwaysFalseStateMock,
|
||||
async p => (p === 1 ? TestIssueList : []),
|
||||
async () => [],
|
||||
async () => [],
|
||||
async () => new Date().toDateString()
|
||||
);
|
||||
|
||||
|
||||
@ -44,6 +44,7 @@ describe('milestones options', (): void => {
|
||||
alwaysFalseStateMock,
|
||||
async p => (p === 1 ? testIssueList : []),
|
||||
async () => [],
|
||||
async () => [],
|
||||
async () => new Date().toDateString()
|
||||
);
|
||||
};
|
||||
|
||||
@ -1144,6 +1144,7 @@ class IssuesProcessorBuilder {
|
||||
alwaysFalseStateMock,
|
||||
async p => (p === 1 ? this._issues : []),
|
||||
async () => [],
|
||||
async () => [],
|
||||
async () => new Date().toDateString()
|
||||
);
|
||||
}
|
||||
|
||||
@ -209,6 +209,7 @@ class SUT {
|
||||
alwaysFalseStateMock,
|
||||
async p => (p === 1 ? this._testIssueList : []),
|
||||
async () => [],
|
||||
async () => [],
|
||||
async () => new Date().toDateString()
|
||||
);
|
||||
|
||||
|
||||
@ -555,6 +555,7 @@ class IssuesProcessorBuilder {
|
||||
body: 'body'
|
||||
}
|
||||
],
|
||||
async () => [],
|
||||
async () => new Date().toDateString()
|
||||
);
|
||||
}
|
||||
|
||||
@ -51,6 +51,7 @@ describe('state', (): void => {
|
||||
state,
|
||||
async p => (p === 1 ? TestIssueList : []),
|
||||
async () => [],
|
||||
async () => [],
|
||||
async () => new Date().toDateString()
|
||||
);
|
||||
|
||||
@ -78,6 +79,7 @@ describe('state', (): void => {
|
||||
state,
|
||||
async p => (p === 1 ? TestIssueList : []),
|
||||
async () => [],
|
||||
async () => [],
|
||||
async () => new Date().toDateString()
|
||||
);
|
||||
|
||||
@ -117,6 +119,7 @@ describe('state', (): void => {
|
||||
state,
|
||||
async p => (p === 1 ? TestIssueList : []),
|
||||
async () => [],
|
||||
async () => [],
|
||||
async () => new Date().toDateString()
|
||||
);
|
||||
|
||||
@ -159,6 +162,7 @@ describe('state', (): void => {
|
||||
state,
|
||||
async p => (p === 1 ? TestIssueList : []),
|
||||
async () => [],
|
||||
async () => [],
|
||||
async () => new Date().toDateString()
|
||||
);
|
||||
|
||||
@ -197,12 +201,13 @@ describe('state', (): void => {
|
||||
state,
|
||||
async p => (p === 1 ? TestIssueList : []),
|
||||
async () => [],
|
||||
async () => [],
|
||||
async () => new Date().toDateString()
|
||||
);
|
||||
|
||||
await processor.processIssues(1);
|
||||
// make sure all issues are proceeded
|
||||
expect(infoSpy.mock.calls[71][0]).toContain(
|
||||
expect(infoSpy.mock.calls[73][0]).toContain(
|
||||
'No more issues found to process. Exiting...'
|
||||
);
|
||||
|
||||
|
||||
@ -691,6 +691,7 @@ class SUT {
|
||||
alwaysFalseStateMock,
|
||||
async p => (p === 1 ? this._testIssueList : []),
|
||||
async () => [],
|
||||
async () => [],
|
||||
async () => new Date().toDateString()
|
||||
);
|
||||
|
||||
|
||||
@ -200,6 +200,10 @@ inputs:
|
||||
description: 'Any update (update/comment) can reset the stale idle time on the pull requests. Override "ignore-updates" option regarding only the pull requests.'
|
||||
default: ''
|
||||
required: false
|
||||
ignore-reactions:
|
||||
description: 'Any reaction can reset the stale idle time on the issues/PRs.'
|
||||
default: ''
|
||||
required: false
|
||||
include-only-assigned:
|
||||
description: 'Only the issues or the pull requests with an assignee will be marked as stale automatically.'
|
||||
default: 'false'
|
||||
|
||||
72
dist/index.js
vendored
72
dist/index.js
vendored
@ -672,6 +672,38 @@ class IssuesProcessor {
|
||||
}
|
||||
});
|
||||
}
|
||||
// Grab reactions for an issue since a given date
|
||||
listIssueReactions(issue, sinceDate) {
|
||||
var _a;
|
||||
return __awaiter(this, void 0, void 0, function* () {
|
||||
try {
|
||||
this._consumeIssueOperation(issue);
|
||||
(_a = this.statistics) === null || _a === void 0 ? void 0 : _a.incrementFetchedItemsReactionsCount();
|
||||
let reactions = [];
|
||||
let currentReactions = [];
|
||||
let iterator = 1;
|
||||
const daysSinceLastUpdated = (new Date().getTime() - new Date(sinceDate).getTime()) /
|
||||
(1000 * 60 * 60 * 24);
|
||||
do {
|
||||
const response = yield this.client.rest.reactions.listForIssue({
|
||||
owner: github_1.context.repo.owner,
|
||||
repo: github_1.context.repo.repo,
|
||||
issue_number: issue.number,
|
||||
per_page: 100,
|
||||
page: iterator
|
||||
});
|
||||
currentReactions = response.data;
|
||||
reactions = [...reactions, ...currentReactions];
|
||||
iterator++;
|
||||
} while (currentReactions.length !== 0);
|
||||
return reactions.filter(reaction => IssuesProcessor._updatedSince(reaction.created_at, daysSinceLastUpdated));
|
||||
}
|
||||
catch (error) {
|
||||
this._logger.error(`List issue reactions error: ${error.message}`);
|
||||
return Promise.resolve([]);
|
||||
}
|
||||
});
|
||||
}
|
||||
// grab issues from github in batches of 100
|
||||
getIssues(page) {
|
||||
var _a;
|
||||
@ -759,6 +791,8 @@ class IssuesProcessor {
|
||||
issueLogger.info(`$$type marked stale on: ${logger_service_1.LoggerService.cyan(markedStaleOn)}`);
|
||||
const issueHasCommentsSinceStale = yield this._hasCommentsSince(issue, markedStaleOn, staleMessage);
|
||||
issueLogger.info(`$$type has been commented on: ${logger_service_1.LoggerService.cyan(issueHasCommentsSinceStale)}`);
|
||||
const issueHasReactionsSinceStale = yield this._hasReactionsSince(issue, markedStaleOn, this.options.ignoreReactions);
|
||||
issueLogger.info(`$$type had a reaction: ${logger_service_1.LoggerService.cyan(issueHasReactionsSinceStale)}`);
|
||||
const daysBeforeClose = issue.isPullRequest
|
||||
? this._getDaysBeforePrClose()
|
||||
: this._getDaysBeforeIssueClose();
|
||||
@ -781,7 +815,11 @@ class IssuesProcessor {
|
||||
issueLogger.info(`$$type has been updated since it was marked stale: ${logger_service_1.LoggerService.cyan(issueHasUpdateSinceStale)}`);
|
||||
// Should we un-stale this issue?
|
||||
if (shouldRemoveStaleWhenUpdated &&
|
||||
(issueHasUpdateSinceStale || issueHasCommentsSinceStale) &&
|
||||
(issueHasUpdateSinceStale ||
|
||||
issueHasCommentsSinceStale ||
|
||||
(this.options.ignoreReactions === false
|
||||
? issueHasReactionsSinceStale
|
||||
: false)) &&
|
||||
!issue.markedStaleThisRun) {
|
||||
issueLogger.info(`Remove the stale label since the $$type has been updated and the workflow should remove the stale label when updated`);
|
||||
yield this._removeStaleLabel(issue, staleLabel);
|
||||
@ -797,7 +835,11 @@ class IssuesProcessor {
|
||||
}
|
||||
const issueHasUpdateInCloseWindow = IssuesProcessor._updatedSince(issue.updated_at, daysBeforeClose);
|
||||
issueLogger.info(`$$type has been updated in the last ${daysBeforeClose} days: ${logger_service_1.LoggerService.cyan(issueHasUpdateInCloseWindow)}`);
|
||||
if (!issueHasCommentsSinceStale && !issueHasUpdateInCloseWindow) {
|
||||
if (!issueHasCommentsSinceStale &&
|
||||
!issueHasUpdateInCloseWindow &&
|
||||
(this.options.ignoreReactions === false
|
||||
? !issueHasReactionsSinceStale
|
||||
: true)) {
|
||||
issueLogger.info(`Closing $$type because it was last updated on: ${logger_service_1.LoggerService.cyan(issue.updated_at)}`);
|
||||
yield this._closeIssue(issue, closeMessage, closeLabel);
|
||||
if (this.options.deleteBranch && issue.pull_request) {
|
||||
@ -831,6 +873,21 @@ class IssuesProcessor {
|
||||
return filteredComments.length > 0;
|
||||
});
|
||||
}
|
||||
// find any reactions since the date
|
||||
_hasReactionsSince(issue, sinceDate, ignoreReactions) {
|
||||
return __awaiter(this, void 0, void 0, function* () {
|
||||
const issueLogger = new issue_logger_1.IssueLogger(issue);
|
||||
if (!sinceDate) {
|
||||
return true;
|
||||
}
|
||||
if (ignoreReactions === true || ignoreReactions === undefined) {
|
||||
return false;
|
||||
}
|
||||
issueLogger.info(`Checking for reactions on $$type since: ${logger_service_1.LoggerService.cyan(sinceDate)}`);
|
||||
const reactions = yield this.listIssueReactions(issue, sinceDate);
|
||||
return reactions.length > 0;
|
||||
});
|
||||
}
|
||||
// Mark an issue as stale with a comment and a label
|
||||
_markStale(issue, staleMessage, staleLabel, skipMessage) {
|
||||
var _a, _b, _c;
|
||||
@ -1830,6 +1887,7 @@ class Statistics {
|
||||
this.fetchedItemsCount = 0;
|
||||
this.fetchedItemsEventsCount = 0;
|
||||
this.fetchedItemsCommentsCount = 0;
|
||||
this.fetchedItemsReactionsCount = 0;
|
||||
this.fetchedPullRequestsCount = 0;
|
||||
}
|
||||
incrementProcessedItemsCount(issue, increment = 1) {
|
||||
@ -1900,6 +1958,10 @@ class Statistics {
|
||||
this.fetchedItemsCommentsCount += increment;
|
||||
return this;
|
||||
}
|
||||
incrementFetchedItemsReactionsCount(increment = 1) {
|
||||
this.fetchedItemsReactionsCount += increment;
|
||||
return this;
|
||||
}
|
||||
incrementFetchedPullRequestsCount(increment = 1) {
|
||||
this.fetchedPullRequestsCount += increment;
|
||||
return this;
|
||||
@ -1918,6 +1980,7 @@ class Statistics {
|
||||
this._logFetchedItemsCount();
|
||||
this._logFetchedItemsEventsCount();
|
||||
this._logFetchedItemsCommentsCount();
|
||||
this._logFetchedItemsReactionsCount();
|
||||
this._logFetchedPullRequestsCount();
|
||||
this._logOperationsCount();
|
||||
return this;
|
||||
@ -2094,6 +2157,9 @@ class Statistics {
|
||||
_logFetchedItemsCommentsCount() {
|
||||
this._logCount('Fetched items comments', this.fetchedItemsCommentsCount);
|
||||
}
|
||||
_logFetchedItemsReactionsCount() {
|
||||
this._logCount('Fetched items reactions', this.fetchedItemsReactionsCount);
|
||||
}
|
||||
_logFetchedPullRequestsCount() {
|
||||
this._logCount('Fetched pull requests', this.fetchedPullRequestsCount);
|
||||
}
|
||||
@ -2220,6 +2286,7 @@ var Option;
|
||||
Option["IgnoreUpdates"] = "ignore-updates";
|
||||
Option["IgnoreIssueUpdates"] = "ignore-issue-updates";
|
||||
Option["IgnorePrUpdates"] = "ignore-pr-updates";
|
||||
Option["IgnoreReactions"] = "ignore-reactions";
|
||||
Option["ExemptDraftPr"] = "exempt-draft-pr";
|
||||
Option["CloseIssueReason"] = "close-issue-reason";
|
||||
})(Option || (exports.Option = Option = {}));
|
||||
@ -2565,6 +2632,7 @@ function _getAndValidateArgs() {
|
||||
ignoreUpdates: core.getInput('ignore-updates') === 'true',
|
||||
ignoreIssueUpdates: _toOptionalBoolean('ignore-issue-updates'),
|
||||
ignorePrUpdates: _toOptionalBoolean('ignore-pr-updates'),
|
||||
ignoreReactions: _toOptionalBoolean('ignore-reactions'),
|
||||
exemptDraftPr: core.getInput('exempt-draft-pr') === 'true',
|
||||
closeIssueReason: core.getInput('close-issue-reason'),
|
||||
includeOnlyAssigned: core.getInput('include-only-assigned') === 'true'
|
||||
|
||||
@ -62,6 +62,7 @@ describe('Issue', (): void => {
|
||||
ignoreUpdates: false,
|
||||
ignoreIssueUpdates: undefined,
|
||||
ignorePrUpdates: undefined,
|
||||
ignoreReactions: undefined,
|
||||
exemptDraftPr: false,
|
||||
closeIssueReason: '',
|
||||
includeOnlyAssigned: false
|
||||
|
||||
@ -14,6 +14,7 @@ import {IComment} from '../interfaces/comment';
|
||||
import {IIssueEvent} from '../interfaces/issue-event';
|
||||
import {IIssuesProcessorOptions} from '../interfaces/issues-processor-options';
|
||||
import {IPullRequest} from '../interfaces/pull-request';
|
||||
import {IReaction} from '../interfaces/reaction';
|
||||
import {Assignees} from './assignees';
|
||||
import {IgnoreUpdates} from './ignore-updates';
|
||||
import {ExemptDraftPullRequest} from './exempt-draft-pull-request';
|
||||
@ -561,6 +562,41 @@ export class IssuesProcessor {
|
||||
}
|
||||
}
|
||||
|
||||
// Grab reactions for an issue since a given date
|
||||
async listIssueReactions(
|
||||
issue: Readonly<Issue>,
|
||||
sinceDate: Readonly<string>
|
||||
): Promise<IReaction[]> {
|
||||
try {
|
||||
this._consumeIssueOperation(issue);
|
||||
this.statistics?.incrementFetchedItemsReactionsCount();
|
||||
let reactions: IReaction[] = [];
|
||||
let currentReactions: IReaction[] = [];
|
||||
let iterator = 1;
|
||||
const daysSinceLastUpdated =
|
||||
(new Date().getTime() - new Date(sinceDate).getTime()) /
|
||||
(1000 * 60 * 60 * 24);
|
||||
do {
|
||||
const response = await this.client.rest.reactions.listForIssue({
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
issue_number: issue.number,
|
||||
per_page: 100,
|
||||
page: iterator
|
||||
});
|
||||
currentReactions = response.data;
|
||||
reactions = [...reactions, ...currentReactions];
|
||||
iterator++;
|
||||
} while (currentReactions.length !== 0);
|
||||
return reactions.filter(reaction =>
|
||||
IssuesProcessor._updatedSince(reaction.created_at, daysSinceLastUpdated)
|
||||
);
|
||||
} catch (error) {
|
||||
this._logger.error(`List issue reactions error: ${error.message}`);
|
||||
return Promise.resolve([]);
|
||||
}
|
||||
}
|
||||
|
||||
// grab issues from github in batches of 100
|
||||
async getIssues(page: number): Promise<Issue[]> {
|
||||
try {
|
||||
@ -678,6 +714,17 @@ export class IssuesProcessor {
|
||||
)}`
|
||||
);
|
||||
|
||||
const issueHasReactionsSinceStale: boolean = await this._hasReactionsSince(
|
||||
issue,
|
||||
markedStaleOn,
|
||||
this.options.ignoreReactions
|
||||
);
|
||||
issueLogger.info(
|
||||
`$$type had a reaction: ${LoggerService.cyan(
|
||||
issueHasReactionsSinceStale
|
||||
)}`
|
||||
);
|
||||
|
||||
const daysBeforeClose: number = issue.isPullRequest
|
||||
? this._getDaysBeforePrClose()
|
||||
: this._getDaysBeforeIssueClose();
|
||||
@ -729,7 +776,11 @@ export class IssuesProcessor {
|
||||
// Should we un-stale this issue?
|
||||
if (
|
||||
shouldRemoveStaleWhenUpdated &&
|
||||
(issueHasUpdateSinceStale || issueHasCommentsSinceStale) &&
|
||||
(issueHasUpdateSinceStale ||
|
||||
issueHasCommentsSinceStale ||
|
||||
(this.options.ignoreReactions === false
|
||||
? issueHasReactionsSinceStale
|
||||
: false)) &&
|
||||
!issue.markedStaleThisRun
|
||||
) {
|
||||
issueLogger.info(
|
||||
@ -765,7 +816,13 @@ export class IssuesProcessor {
|
||||
)}`
|
||||
);
|
||||
|
||||
if (!issueHasCommentsSinceStale && !issueHasUpdateInCloseWindow) {
|
||||
if (
|
||||
!issueHasCommentsSinceStale &&
|
||||
!issueHasUpdateInCloseWindow &&
|
||||
(this.options.ignoreReactions === false
|
||||
? !issueHasReactionsSinceStale
|
||||
: true)
|
||||
) {
|
||||
issueLogger.info(
|
||||
`Closing $$type because it was last updated on: ${LoggerService.cyan(
|
||||
issue.updated_at
|
||||
@ -824,6 +881,30 @@ export class IssuesProcessor {
|
||||
return filteredComments.length > 0;
|
||||
}
|
||||
|
||||
// find any reactions since the date
|
||||
private async _hasReactionsSince(
|
||||
issue: Issue,
|
||||
sinceDate: string,
|
||||
ignoreReactions: boolean | undefined
|
||||
): Promise<boolean> {
|
||||
const issueLogger: IssueLogger = new IssueLogger(issue);
|
||||
|
||||
if (!sinceDate) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (ignoreReactions === true || ignoreReactions === undefined) {
|
||||
return false;
|
||||
}
|
||||
|
||||
issueLogger.info(
|
||||
`Checking for reactions on $$type since: ${LoggerService.cyan(sinceDate)}`
|
||||
);
|
||||
const reactions = await this.listIssueReactions(issue, sinceDate);
|
||||
|
||||
return reactions.length > 0;
|
||||
}
|
||||
|
||||
// Mark an issue as stale with a comment and a label
|
||||
private async _markStale(
|
||||
issue: Issue,
|
||||
|
||||
@ -30,6 +30,7 @@ export class Statistics {
|
||||
fetchedItemsCount = 0;
|
||||
fetchedItemsEventsCount = 0;
|
||||
fetchedItemsCommentsCount = 0;
|
||||
fetchedItemsReactionsCount = 0;
|
||||
fetchedPullRequestsCount = 0;
|
||||
|
||||
incrementProcessedItemsCount(
|
||||
@ -154,6 +155,14 @@ export class Statistics {
|
||||
return this;
|
||||
}
|
||||
|
||||
incrementFetchedItemsReactionsCount(
|
||||
increment: Readonly<number> = 1
|
||||
): Statistics {
|
||||
this.fetchedItemsReactionsCount += increment;
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
incrementFetchedPullRequestsCount(
|
||||
increment: Readonly<number> = 1
|
||||
): Statistics {
|
||||
@ -176,6 +185,7 @@ export class Statistics {
|
||||
this._logFetchedItemsCount();
|
||||
this._logFetchedItemsEventsCount();
|
||||
this._logFetchedItemsCommentsCount();
|
||||
this._logFetchedItemsReactionsCount();
|
||||
this._logFetchedPullRequestsCount();
|
||||
this._logOperationsCount();
|
||||
|
||||
@ -430,6 +440,10 @@ export class Statistics {
|
||||
this._logCount('Fetched items comments', this.fetchedItemsCommentsCount);
|
||||
}
|
||||
|
||||
private _logFetchedItemsReactionsCount(): void {
|
||||
this._logCount('Fetched items reactions', this.fetchedItemsReactionsCount);
|
||||
}
|
||||
|
||||
private _logFetchedPullRequestsCount(): void {
|
||||
this._logCount('Fetched pull requests', this.fetchedPullRequestsCount);
|
||||
}
|
||||
|
||||
@ -47,6 +47,7 @@ export enum Option {
|
||||
IgnoreUpdates = 'ignore-updates',
|
||||
IgnoreIssueUpdates = 'ignore-issue-updates',
|
||||
IgnorePrUpdates = 'ignore-pr-updates',
|
||||
IgnoreReactions = 'ignore-reactions',
|
||||
ExemptDraftPr = 'exempt-draft-pr',
|
||||
CloseIssueReason = 'close-issue-reason'
|
||||
}
|
||||
|
||||
@ -2,6 +2,7 @@ import {IsoDateString} from '../types/iso-date-string';
|
||||
import {Assignee} from './assignee';
|
||||
import {ILabel} from './label';
|
||||
import {IMilestone} from './milestone';
|
||||
import {IReaction} from './reaction';
|
||||
import {components} from '@octokit/openapi-types';
|
||||
export interface IIssue {
|
||||
title: string;
|
||||
@ -15,6 +16,7 @@ export interface IIssue {
|
||||
locked: boolean;
|
||||
milestone?: IMilestone | null;
|
||||
assignees?: Assignee[] | null;
|
||||
reactions?: IReaction[] | null;
|
||||
}
|
||||
|
||||
export type OctokitIssue = components['schemas']['issue'];
|
||||
|
||||
@ -51,6 +51,7 @@ export interface IIssuesProcessorOptions {
|
||||
ignoreUpdates: boolean;
|
||||
ignoreIssueUpdates: boolean | undefined;
|
||||
ignorePrUpdates: boolean | undefined;
|
||||
ignoreReactions: boolean | undefined;
|
||||
exemptDraftPr: boolean;
|
||||
closeIssueReason: string;
|
||||
includeOnlyAssigned: boolean;
|
||||
|
||||
4
src/interfaces/reaction.ts
Normal file
4
src/interfaces/reaction.ts
Normal file
@ -0,0 +1,4 @@
|
||||
export interface IReaction {
|
||||
content: string;
|
||||
created_at: string;
|
||||
}
|
||||
@ -121,6 +121,7 @@ function _getAndValidateArgs(): IIssuesProcessorOptions {
|
||||
ignoreUpdates: core.getInput('ignore-updates') === 'true',
|
||||
ignoreIssueUpdates: _toOptionalBoolean('ignore-issue-updates'),
|
||||
ignorePrUpdates: _toOptionalBoolean('ignore-pr-updates'),
|
||||
ignoreReactions: _toOptionalBoolean('ignore-reactions'),
|
||||
exemptDraftPr: core.getInput('exempt-draft-pr') === 'true',
|
||||
closeIssueReason: core.getInput('close-issue-reason'),
|
||||
includeOnlyAssigned: core.getInput('include-only-assigned') === 'true'
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user