diff --git a/.prettierignore b/.prettierignore index 91c870c48..f150cff51 100644 --- a/.prettierignore +++ b/.prettierignore @@ -2,5 +2,6 @@ .licenses .vscode dist +lib node_modules package-lock.json diff --git a/README.md b/README.md index 869d8b00b..dd19ebf45 100644 --- a/README.md +++ b/README.md @@ -24,32 +24,33 @@ $ npm test ### Arguments -| Input | Description | Usage | -| --------------------------- | ------------------------------------------------------------------------------------ | -------- | -| `repo-token` | PAT(Personal Access Token) for authorizing repository. | Optional | -| `days-before-stale` | Idle number of days before marking an issue/pr as stale. _Defaults to **60**_ | Optional | -| `days-before-issue-stale` | Idle number of days before marking an issue as stale (override `days-before-stale`). | Optional | -| `days-before-pr-stale` | Idle number of days before marking an pr as stale (override `days-before-stale`). | Optional | -| `days-before-close` | Idle number of days before closing an stale issue/pr. _Defaults to **7**_ | Optional | -| `days-before-issue-close` | Idle number of days before closing an stale issue (override `days-before-close`). | Optional | -| `days-before-pr-close` | Idle number of days before closing an stale pr (override `days-before-close`). | Optional | -| `stale-issue-message` | Message to post on the stale issue. | Optional | -| `stale-pr-message` | Message to post on the stale pr. | Optional | -| `close-issue-message` | Message to post on the stale issue while closing it. | Optional | -| `close-pr-message` | Message to post on the stale pr while closing it. | Optional | -| `stale-issue-label` | Label to apply on the stale issue. _Defaults to **stale**_ | Optional | -| `close-issue-label` | Label to apply on closing issue. | Optional | -| `stale-pr-label` | Label to apply on the stale pr. | Optional | -| `close-pr-label` | Label to apply on the closing pr. | Optional | -| `exempt-issue-labels` | Labels on an issue exempted from being marked as stale. | Optional | -| `exempt-pr-labels` | Labels on the pr exempted from being marked as stale. | Optional | -| `only-labels` | Only labels checked for stale issue/pr. | Optional | -| `operations-per-run` | Maximum number of operations per run. _Defaults to **30**_ | Optional | -| `remove-stale-when-updated` | Remove stale label from issue/pr on updates or comments. _Defaults to **true**_ | Optional | -| `debug-only` | Dry-run on action. _Defaults to **false**_ | Optional | -| `ascending` | Order to get issues/pr. _Defaults to **false**_ | Optional | -| `skip-stale-issue-message` | Skip adding stale message on stale issue. _Defaults to **false**_ | Optional | -| `skip-stale-pr-message` | Skip adding stale message on stale pr. _Defaults to **false**_ | Optional | +| Input | Description | Usage | +| --------------------------- | -------------------------------------------------------------------------------------------- | -------- | +| `repo-token` | PAT(Personal Access Token) for authorizing repository. _Defaults to **${{ github.token }}**_ | Optional | +| `days-before-stale` | Idle number of days before marking an issue/pr as stale. _Defaults to **60**_ | Optional | +| `days-before-issue-stale` | Idle number of days before marking an issue as stale (override `days-before-stale`). | Optional | +| `days-before-pr-stale` | Idle number of days before marking an pr as stale (override `days-before-stale`). | Optional | +| `days-before-close` | Idle number of days before closing an stale issue/pr. _Defaults to **7**_ | Optional | +| `days-before-issue-close` | Idle number of days before closing an stale issue (override `days-before-close`). | Optional | +| `days-before-pr-close` | Idle number of days before closing an stale pr (override `days-before-close`). | Optional | +| `stale-issue-message` | Message to post on the stale issue. | Optional | +| `stale-pr-message` | Message to post on the stale pr. | Optional | +| `close-issue-message` | Message to post on the stale issue while closing it. | Optional | +| `close-pr-message` | Message to post on the stale pr while closing it. | Optional | +| `stale-issue-label` | Label to apply on the stale issue. _Defaults to **stale**_ | Optional | +| `close-issue-label` | Label to apply on closing issue. | Optional | +| `stale-pr-label` | Label to apply on the stale pr. | Optional | +| `close-pr-label` | Label to apply on the closing pr. | Optional | +| `exempt-issue-labels` | Labels on an issue exempted from being marked as stale. | Optional | +| `exempt-pr-labels` | Labels on the pr exempted from being marked as stale. | Optional | +| `only-labels` | Only labels checked for stale issue/pr. | Optional | +| `operations-per-run` | Maximum number of operations per run (GitHub API CRUD related). _Defaults to **30**_ | Optional | +| `remove-stale-when-updated` | Remove stale label from issue/pr on updates or comments. _Defaults to **true**_ | Optional | +| `debug-only` | Dry-run on action. _Defaults to **false**_ | Optional | +| `ascending` | Order to get issues/pr. _Defaults to **false**_ | Optional | +| `skip-stale-issue-message` | Skip adding stale message on stale issue. _Defaults to **false**_ | Optional | +| `skip-stale-pr-message` | Skip adding stale message on stale pr. _Defaults to **false**_ | Optional | +| `start-date` | The date used to skip the stale action on issue/pr created before it (ISO 8601 or RFC 2822). | Optional | ### Usage @@ -163,6 +164,23 @@ jobs: only-labels: 'awaiting-feedback,awaiting-answers' ``` +Configure the stale action to only stale issue/pr created after the 18th april 2020: + +```yaml +name: 'Close stale issues and PRs' +on: + schedule: + - cron: '30 1 * * *' + +jobs: + stale: + runs-on: ubuntu-latest + steps: + - uses: actions/stale@v3 + with: + start-date: '2020-18-04T00:00:00Z' // ISO 8601 or RFC 2822 +``` + ### Debugging To see debug output from this action, you must set the secret `ACTIONS_STEP_DEBUG` to `true` in your repository. You can run this action in debug only mode (no actions will be taken on your issues) by passing `debug-only` `true` as an argument to the action. diff --git a/__tests__/main.test.ts b/__tests__/main.test.ts index ee388b6fa..55bdb163d 100644 --- a/__tests__/main.test.ts +++ b/__tests__/main.test.ts @@ -1,15 +1,16 @@ import * as github from '@actions/github'; - import { Issue, IssueProcessor, IssueProcessorOptions } from '../src/IssueProcessor'; +import {IsoDateString} from '../src/types/iso-date-string'; function generateIssue( id: number, title: string, - updatedAt: string, + updatedAt: IsoDateString, + createdAt: IsoDateString = updatedAt, isPullRequest: boolean = false, labels: string[] = [], isClosed: boolean = false, @@ -21,6 +22,7 @@ function generateIssue( return {name: l}; }), title: title, + created_at: createdAt, updated_at: updatedAt, pull_request: isPullRequest ? {} : null, state: isClosed ? 'closed' : 'open', @@ -53,7 +55,8 @@ const DefaultProcessorOptions: IssueProcessorOptions = Object.freeze({ ascending: false, skipStaleIssueMessage: false, skipStalePrMessage: false, - deleteBranch: false + deleteBranch: false, + startDate: '' }); test('empty issue list results in 1 operation', async () => { @@ -97,6 +100,254 @@ test('processing an issue with no label will make it stale and close it, if it i expect(processor.closedIssues.length).toEqual(1); }); +test('processing an issue with no label and a start date as ECMAScript epoch in seconds being before the issue creation date will not make it stale nor close it when it is old enough and days-before-close is set to 0', async () => { + expect.assertions(2); + const TestIssueList: Issue[] = [ + generateIssue( + 1, + 'An issue with no label', + '2020-01-01T17:00:00Z', + '2020-01-01T17:00:00Z' + ) + ]; + const january2000 = 946681200000; + const opts: IssueProcessorOptions = { + ...DefaultProcessorOptions, + daysBeforeClose: 0, + startDate: january2000.toString() + }; + const processor = new IssueProcessor( + opts, + async () => 'abot', + async p => (p == 1 ? TestIssueList : []), + async (num, dt) => [], + async (issue, label) => new Date().toDateString() + ); + + // process our fake issue list + await processor.processIssues(1); + + expect(processor.staleIssues.length).toStrictEqual(0); + expect(processor.closedIssues.length).toStrictEqual(0); +}); + +test('processing an issue with no label and a start date as ECMAScript epoch in seconds being after the issue creation date will not make it stale nor close it when it is old enough and days-before-close is set to 0', async () => { + expect.assertions(2); + const TestIssueList: Issue[] = [ + generateIssue( + 1, + 'An issue with no label', + '2020-01-01T17:00:00Z', + '2020-01-01T17:00:00Z' + ) + ]; + const january2021 = 1609455600000; + const opts: IssueProcessorOptions = { + ...DefaultProcessorOptions, + daysBeforeClose: 0, + startDate: january2021.toString() + }; + const processor = new IssueProcessor( + opts, + async () => 'abot', + async p => (p == 1 ? TestIssueList : []), + async (num, dt) => [], + async (issue, label) => new Date().toDateString() + ); + + // process our fake issue list + await processor.processIssues(1); + + expect(processor.staleIssues.length).toStrictEqual(0); + expect(processor.closedIssues.length).toStrictEqual(0); +}); + +test('processing an issue with no label and a start date as ECMAScript epoch in milliseconds being before the issue creation date will not make it stale nor close it when it is old enough and days-before-close is set to 0', async () => { + expect.assertions(2); + const TestIssueList: Issue[] = [ + generateIssue( + 1, + 'An issue with no label', + '2020-01-01T17:00:00Z', + '2020-01-01T17:00:00Z' + ) + ]; + const january2000 = 946681200000000; + const opts: IssueProcessorOptions = { + ...DefaultProcessorOptions, + daysBeforeClose: 0, + startDate: january2000.toString() + }; + const processor = new IssueProcessor( + opts, + async () => 'abot', + async p => (p == 1 ? TestIssueList : []), + async (num, dt) => [], + async (issue, label) => new Date().toDateString() + ); + + // process our fake issue list + await processor.processIssues(1); + + expect(processor.staleIssues.length).toStrictEqual(0); + expect(processor.closedIssues.length).toStrictEqual(0); +}); + +test('processing an issue with no label and a start date as ECMAScript epoch in milliseconds being after the issue creation date will not make it stale nor close it when it is old enough and days-before-close is set to 0', async () => { + expect.assertions(2); + const TestIssueList: Issue[] = [ + generateIssue( + 1, + 'An issue with no label', + '2020-01-01T17:00:00Z', + '2020-01-01T17:00:00Z' + ) + ]; + const january2021 = 1609455600000; + const opts: IssueProcessorOptions = { + ...DefaultProcessorOptions, + daysBeforeClose: 0, + startDate: january2021.toString() + }; + const processor = new IssueProcessor( + opts, + async () => 'abot', + async p => (p == 1 ? TestIssueList : []), + async (num, dt) => [], + async (issue, label) => new Date().toDateString() + ); + + // process our fake issue list + await processor.processIssues(1); + + expect(processor.staleIssues.length).toStrictEqual(0); + expect(processor.closedIssues.length).toStrictEqual(0); +}); + +test('processing an issue with no label and a start date as ISO 8601 being before the issue creation date will make it stale and close it when it is old enough and days-before-close is set to 0', async () => { + expect.assertions(2); + const TestIssueList: Issue[] = [ + generateIssue( + 1, + 'An issue with no label', + '2020-01-01T17:00:00Z', + '2020-01-01T17:00:00Z' + ) + ]; + const january2000 = '2000-01-01T00:00:00Z'; + const opts: IssueProcessorOptions = { + ...DefaultProcessorOptions, + daysBeforeClose: 0, + startDate: january2000.toString() + }; + const processor = new IssueProcessor( + opts, + async () => 'abot', + async p => (p == 1 ? TestIssueList : []), + async (num, dt) => [], + async (issue, label) => new Date().toDateString() + ); + + // process our fake issue list + await processor.processIssues(1); + + expect(processor.staleIssues.length).toStrictEqual(1); + expect(processor.closedIssues.length).toStrictEqual(1); +}); + +test('processing an issue with no label and a start date as ISO 8601 being after the issue creation date will not make it stale nor close it when it is old enough and days-before-close is set to 0', async () => { + expect.assertions(2); + const TestIssueList: Issue[] = [ + generateIssue( + 1, + 'An issue with no label', + '2020-01-01T17:00:00Z', + '2020-01-01T17:00:00Z' + ) + ]; + const january2021 = '2021-01-01T00:00:00Z'; + const opts: IssueProcessorOptions = { + ...DefaultProcessorOptions, + daysBeforeClose: 0, + startDate: january2021.toString() + }; + const processor = new IssueProcessor( + opts, + async () => 'abot', + async p => (p == 1 ? TestIssueList : []), + async (num, dt) => [], + async (issue, label) => new Date().toDateString() + ); + + // process our fake issue list + await processor.processIssues(1); + + expect(processor.staleIssues.length).toStrictEqual(0); + expect(processor.closedIssues.length).toStrictEqual(0); +}); + +test('processing an issue with no label and a start date as RFC 2822 being before the issue creation date will make it stale and close it when it is old enough and days-before-close is set to 0', async () => { + expect.assertions(2); + const TestIssueList: Issue[] = [ + generateIssue( + 1, + 'An issue with no label', + '2020-01-01T17:00:00Z', + '2020-01-01T17:00:00Z' + ) + ]; + const january2000 = 'January 1, 2000 00:00:00'; + const opts: IssueProcessorOptions = { + ...DefaultProcessorOptions, + daysBeforeClose: 0, + startDate: january2000.toString() + }; + const processor = new IssueProcessor( + opts, + async () => 'abot', + async p => (p == 1 ? TestIssueList : []), + async (num, dt) => [], + async (issue, label) => new Date().toDateString() + ); + + // process our fake issue list + await processor.processIssues(1); + + expect(processor.staleIssues.length).toStrictEqual(1); + expect(processor.closedIssues.length).toStrictEqual(1); +}); + +test('processing an issue with no label and a start date as RFC 2822 being after the issue creation date will not make it stale nor close it when it is old enough and days-before-close is set to 0', async () => { + expect.assertions(2); + const TestIssueList: Issue[] = [ + generateIssue( + 1, + 'An issue with no label', + '2020-01-01T17:00:00Z', + '2020-01-01T17:00:00Z' + ) + ]; + const january2021 = 'January 1, 2021 00:00:00'; + const opts: IssueProcessorOptions = { + ...DefaultProcessorOptions, + daysBeforeClose: 0, + startDate: january2021.toString() + }; + const processor = new IssueProcessor( + opts, + async () => 'abot', + async p => (p == 1 ? TestIssueList : []), + async (num, dt) => [], + async (issue, label) => new Date().toDateString() + ); + + // process our fake issue list + await processor.processIssues(1); + + expect(processor.staleIssues.length).toStrictEqual(0); + expect(processor.closedIssues.length).toStrictEqual(0); +}); + test('processing an issue with no label will make it stale and close it, if it is old enough only if days-before-close is set to > 0 and days-before-issue-close is set to 0', async () => { const TestIssueList: Issue[] = [ generateIssue(1, 'An issue with no label', '2020-01-01T17:00:00Z') @@ -285,6 +536,7 @@ test('processing a stale issue will close it', async () => { 1, 'A stale issue that should be closed', '2020-01-01T17:00:00Z', + '2020-01-01T17:00:00Z', false, ['Stale'] ) @@ -316,6 +568,7 @@ test('processing a stale issue containing a space in the label will close it', a 1, 'A stale issue that should be closed', '2020-01-01T17:00:00Z', + '2020-01-01T17:00:00Z', false, ['state: stale'] ) @@ -347,6 +600,7 @@ test('processing a stale issue containing a slash in the label will close it', a 1, 'A stale issue that should be closed', '2020-01-01T17:00:00Z', + '2020-01-01T17:00:00Z', false, ['lifecycle/stale'] ) @@ -378,6 +632,7 @@ test('processing a stale issue will close it when days-before-issue-stale overri 1, 'A stale issue that should be closed', '2020-01-01T17:00:00Z', + '2020-01-01T17:00:00Z', false, ['Stale'] ) @@ -410,6 +665,7 @@ test('processing a stale PR will close it', async () => { 1, 'A stale PR that should be closed', '2020-01-01T17:00:00Z', + '2020-01-01T17:00:00Z', true, ['Stale'] ) @@ -441,6 +697,7 @@ test('processing a stale PR will close it when days-before-pr-stale override day 1, 'A stale PR that should be closed', '2020-01-01T17:00:00Z', + '2020-01-01T17:00:00Z', true, ['Stale'] ) @@ -469,9 +726,14 @@ test('processing a stale PR will close it when days-before-pr-stale override day test('processing a stale issue will close it even if configured not to mark as stale', async () => { const TestIssueList: Issue[] = [ - generateIssue(1, 'An issue with no label', '2020-01-01T17:00:00Z', false, [ - 'Stale' - ]) + generateIssue( + 1, + 'An issue with no label', + '2020-01-01T17:00:00Z', + '2020-01-01T17:00:00Z', + false, + ['Stale'] + ) ]; const opts = { @@ -497,9 +759,14 @@ test('processing a stale issue will close it even if configured not to mark as s test('processing a stale issue will close it even if configured not to mark as stale when days-before-issue-stale override days-before-stale', async () => { const TestIssueList: Issue[] = [ - generateIssue(1, 'An issue with no label', '2020-01-01T17:00:00Z', false, [ - 'Stale' - ]) + generateIssue( + 1, + 'An issue with no label', + '2020-01-01T17:00:00Z', + '2020-01-01T17:00:00Z', + false, + ['Stale'] + ) ]; const opts = { @@ -526,9 +793,14 @@ test('processing a stale issue will close it even if configured not to mark as s test('processing a stale PR will close it even if configured not to mark as stale', async () => { const TestIssueList: Issue[] = [ - generateIssue(1, 'An issue with no label', '2020-01-01T17:00:00Z', true, [ - 'Stale' - ]) + generateIssue( + 1, + 'An issue with no label', + '2020-01-01T17:00:00Z', + '2020-01-01T17:00:00Z', + true, + ['Stale'] + ) ]; const opts = { @@ -554,9 +826,14 @@ test('processing a stale PR will close it even if configured not to mark as stal test('processing a stale PR will close it even if configured not to mark as stale when days-before-pr-stale override days-before-stale', async () => { const TestIssueList: Issue[] = [ - generateIssue(1, 'An issue with no label', '2020-01-01T17:00:00Z', true, [ - 'Stale' - ]) + generateIssue( + 1, + 'An issue with no label', + '2020-01-01T17:00:00Z', + '2020-01-01T17:00:00Z', + true, + ['Stale'] + ) ]; const opts = { @@ -587,6 +864,7 @@ test('closed issues will not be marked stale', async () => { 1, 'A closed issue that will not be marked', '2020-01-01T17:00:00Z', + '2020-01-01T17:00:00Z', false, [], true @@ -613,6 +891,7 @@ test('stale closed issues will not be closed', async () => { 1, 'A stale closed issue', '2020-01-01T17:00:00Z', + '2020-01-01T17:00:00Z', false, ['Stale'], true @@ -640,6 +919,7 @@ test('closed prs will not be marked stale', async () => { 1, 'A closed PR that will not be marked', '2020-01-01T17:00:00Z', + '2020-01-01T17:00:00Z', true, [], true @@ -667,6 +947,7 @@ test('stale closed prs will not be closed', async () => { 1, 'A stale closed PR that will not be closed again', '2020-01-01T17:00:00Z', + '2020-01-01T17:00:00Z', true, ['Stale'], true @@ -694,6 +975,7 @@ test('locked issues will not be marked stale', async () => { 1, 'A locked issue that will not be stale', '2020-01-01T17:00:00Z', + '2020-01-01T17:00:00Z', false, [], false, @@ -720,6 +1002,7 @@ test('stale locked issues will not be closed', async () => { 1, 'A stale locked issue that will not be closed', '2020-01-01T17:00:00Z', + '2020-01-01T17:00:00Z', false, ['Stale'], false, @@ -748,6 +1031,7 @@ test('locked prs will not be marked stale', async () => { 1, 'A locked PR that will not be marked stale', '2020-01-01T17:00:00Z', + '2020-01-01T17:00:00Z', true, [], false, @@ -774,6 +1058,7 @@ test('stale locked prs will not be closed', async () => { 1, 'A stale locked PR that will not be closed', '2020-01-01T17:00:00Z', + '2020-01-01T17:00:00Z', true, ['Stale'], false, @@ -799,9 +1084,14 @@ test('stale locked prs will not be closed', async () => { test('exempt issue labels will not be marked stale', async () => { expect.assertions(3); const TestIssueList: Issue[] = [ - generateIssue(1, 'My first issue', '2020-01-01T17:00:00Z', false, [ - 'Exempt' - ]) + generateIssue( + 1, + 'My first issue', + '2020-01-01T17:00:00Z', + '2020-01-01T17:00:00Z', + false, + ['Exempt'] + ) ]; const opts = {...DefaultProcessorOptions}; @@ -825,7 +1115,14 @@ test('exempt issue labels will not be marked stale', async () => { test('exempt issue labels will not be marked stale (multi issue label with spaces)', async () => { const TestIssueList: Issue[] = [ - generateIssue(1, 'My first issue', '2020-01-01T17:00:00Z', false, ['Cool']) + generateIssue( + 1, + 'My first issue', + '2020-01-01T17:00:00Z', + '2020-01-01T17:00:00Z', + false, + ['Cool'] + ) ]; const opts = {...DefaultProcessorOptions}; @@ -848,7 +1145,14 @@ test('exempt issue labels will not be marked stale (multi issue label with space test('exempt issue labels will not be marked stale (multi issue label)', async () => { const TestIssueList: Issue[] = [ - generateIssue(1, 'My first issue', '2020-01-01T17:00:00Z', false, ['Cool']) + generateIssue( + 1, + 'My first issue', + '2020-01-01T17:00:00Z', + '2020-01-01T17:00:00Z', + false, + ['Cool'] + ) ]; const opts = {...DefaultProcessorOptions}; @@ -872,9 +1176,29 @@ test('exempt issue labels will not be marked stale (multi issue label)', async ( test('exempt pr labels will not be marked stale', async () => { const TestIssueList: Issue[] = [ - generateIssue(1, 'My first issue', '2020-01-01T17:00:00Z', false, ['Cool']), - generateIssue(2, 'My first PR', '2020-01-01T17:00:00Z', true, ['Cool']), - generateIssue(3, 'Another issue', '2020-01-01T17:00:00Z', false) + generateIssue( + 1, + 'My first issue', + '2020-01-01T17:00:00Z', + '2020-01-01T17:00:00Z', + false, + ['Cool'] + ), + generateIssue( + 2, + 'My first PR', + '2020-01-01T17:00:00Z', + '2020-01-01T17:00:00Z', + true, + ['Cool'] + ), + generateIssue( + 3, + 'Another issue', + '2020-01-01T17:00:00Z', + '2020-01-01T17:00:00Z', + false + ) ]; const opts = {...DefaultProcessorOptions}; @@ -896,17 +1220,18 @@ test('exempt pr labels will not be marked stale', async () => { test('exempt issue labels will not be marked stale and will remove the existing stale label', async () => { expect.assertions(3); - const TestIssueList: Issue[] = [ - generateIssue(1, 'My first issue', '2020-01-01T17:00:00Z', false, [ - 'Exempt', - 'Stale' - ]) + generateIssue( + 1, + 'My first issue', + '2020-01-01T17:00:00Z', + '2020-01-01T17:00:00Z', + false, + ['Exempt', 'Stale'] + ) ]; - const opts = {...DefaultProcessorOptions}; opts.exemptIssueLabels = 'Exempt'; - const processor = new IssueProcessor( opts, async () => 'abot', @@ -932,11 +1257,30 @@ test('exempt issue labels will not be marked stale and will remove the existing test('stale issues should not be closed if days is set to -1', async () => { const TestIssueList: Issue[] = [ - generateIssue(1, 'My first issue', '2020-01-01T17:00:00Z', false, [ - 'Stale' - ]), - generateIssue(2, 'My first PR', '2020-01-01T17:00:00Z', true, ['Stale']), - generateIssue(3, 'Another issue', '2020-01-01T17:00:00Z', false, ['Stale']) + generateIssue( + 1, + 'My first issue', + '2020-01-01T17:00:00Z', + '2020-01-01T17:00:00Z', + false, + ['Stale'] + ), + generateIssue( + 2, + 'My first PR', + '2020-01-01T17:00:00Z', + '2020-01-01T17:00:00Z', + true, + ['Stale'] + ), + generateIssue( + 3, + 'Another issue', + '2020-01-01T17:00:00Z', + '2020-01-01T17:00:00Z', + false, + ['Stale'] + ) ]; const opts = {...DefaultProcessorOptions}; @@ -963,6 +1307,7 @@ test('stale label should be removed if a comment was added to a stale issue', as 1, 'An issue that should un-stale', '2020-01-01T17:00:00Z', + '2020-01-01T17:00:00Z', false, ['Stale'] ) @@ -1001,6 +1346,7 @@ test('stale label should not be removed if a comment was added by the bot (and t 1, 'An issue that should stay stale', '2020-01-01T17:00:00Z', + '2020-01-01T17:00:00Z', false, ['Stale'] ) @@ -1038,6 +1384,7 @@ test('stale label containing a space should be removed if a comment was added to 1, 'An issue that should un-stale', '2020-01-01T17:00:00Z', + '2020-01-01T17:00:00Z', false, ['stat: stale'] ) @@ -1073,6 +1420,7 @@ test('stale issues should not be closed until after the closed number of days', 1, 'An issue that should be marked stale but not closed', lastUpdate.toString(), + lastUpdate.toString(), false ) ]; @@ -1105,6 +1453,7 @@ test('stale issues should be closed if the closed nubmer of days (additive) is a 1, 'An issue that should be stale and closed', lastUpdate.toString(), + lastUpdate.toString(), false, ['Stale'] ) @@ -1138,6 +1487,7 @@ test('stale issues should not be closed until after the closed number of days (l 1, 'An issue that should be marked stale but not closed', lastUpdate.toString(), + lastUpdate.toString(), false ) ]; @@ -1170,6 +1520,7 @@ test('skips stale message on issues when skip-stale-issue-message is set', async 1, 'An issue that should be marked stale but not closed', lastUpdate.toString(), + lastUpdate.toString(), false ) ]; @@ -1215,6 +1566,7 @@ test('skips stale message on prs when skip-stale-pr-message is set', async () => 1, 'An issue that should be marked stale but not closed', lastUpdate.toString(), + lastUpdate.toString(), true ) ]; @@ -1260,6 +1612,7 @@ test('not providing state takes precedence over skipStaleIssueMessage', async () 1, 'An issue that should be marked stale but not closed', lastUpdate.toString(), + lastUpdate.toString(), false ) ]; @@ -1294,6 +1647,7 @@ test('not providing stalePrMessage takes precedence over skipStalePrMessage', as 1, 'An issue that should be marked stale but not closed', lastUpdate.toString(), + lastUpdate.toString(), true ) ]; @@ -1328,6 +1682,7 @@ test('git branch is deleted when option is enabled', async () => { 1, 'An issue that should have its branch deleted', '2020-01-01T17:00:00Z', + '2020-01-01T17:00:00Z', isPullRequest, ['Stale'] ) @@ -1357,6 +1712,7 @@ test('git branch is not deleted when issue is not pull request', async () => { 1, 'An issue that should not have its branch deleted', '2020-01-01T17:00:00Z', + '2020-01-01T17:00:00Z', isPullRequest, ['Stale'] ) diff --git a/action.yml b/action.yml index 6fd705494..1c90b6191 100644 --- a/action.yml +++ b/action.yml @@ -4,6 +4,7 @@ author: 'GitHub' inputs: repo-token: description: 'Token for the repository. Can be passed in using `{{ secrets.GITHUB_TOKEN }}`.' + required: false default: ${{ github.token }} stale-issue-message: description: 'The message to post on the issue when tagging it. If none provided, will not mark issues stale.' @@ -64,7 +65,7 @@ inputs: default: '' required: false operations-per-run: - description: 'The maximum number of operations per run, used to control rate limiting.' + description: 'The maximum number of operations per run, used to control rate limiting (GitHub API CRUD related).' default: '30' required: false remove-stale-when-updated: @@ -91,6 +92,10 @@ inputs: description: 'Delete the git branch after closing a stale pull request.' default: 'false' required: false + start-date: + description: 'The date used to skip the stale action on issue/pr created before it (ISO 8601 or RFC 2822).' + default: '' + required: false runs: using: 'node12' main: 'dist/index.js' diff --git a/dist/index.js b/dist/index.js index 18f9a4485..19c002276 100644 --- a/dist/index.js +++ b/dist/index.js @@ -2,30 +2,11 @@ module.exports = /******/ (() => { // webpackBootstrap /******/ var __webpack_modules__ = ({ -/***/ 407: +/***/ 4407: /***/ (function(__unused_webpack_module, exports, __nccwpck_require__) { "use strict"; -var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { - if (k2 === undefined) k2 = k; - Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } }); -}) : (function(o, m, k, k2) { - if (k2 === undefined) k2 = k; - o[k2] = m[k]; -})); -var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { - Object.defineProperty(o, "default", { enumerable: true, value: v }); -}) : function(o, v) { - o["default"] = v; -}); -var __importStar = (this && this.__importStar) || function (mod) { - if (mod && mod.__esModule) return mod; - var result = {}; - if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k); - __setModuleDefault(result, mod); - return result; -}; var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } return new (P || (P = Promise))(function (resolve, reject) { @@ -37,52 +18,67 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge }; Object.defineProperty(exports, "__esModule", ({ value: true })); exports.IssueProcessor = void 0; -const core = __importStar(__nccwpck_require__(186)); -const github_1 = __nccwpck_require__(438); -const is_labeled_1 = __nccwpck_require__(792); -const labels_to_list_1 = __nccwpck_require__(107); +const github_1 = __nccwpck_require__(5438); +const get_humanized_date_1 = __nccwpck_require__(965); +const is_date_more_recent_than_1 = __nccwpck_require__(1473); +const is_valid_date_1 = __nccwpck_require__(891); +const get_issue_type_1 = __nccwpck_require__(5153); +const issue_logger_1 = __nccwpck_require__(1699); +const logger_1 = __nccwpck_require__(8236); +const is_labeled_1 = __nccwpck_require__(6792); +const is_pull_request_1 = __nccwpck_require__(5400); +const labels_to_list_1 = __nccwpck_require__(9107); +const should_mark_when_stale_1 = __nccwpck_require__(2461); +const logger = new logger_1.Logger(); /*** * Handle processing of issues for staleness/closure. */ class IssueProcessor { constructor(options, getActor, getIssues, listIssueComments, getLabelCreationDate) { - this.operationsLeft = 0; this.staleIssues = []; this.closedIssues = []; this.deletedBranchIssues = []; this.removedLabelIssues = []; + this.operationsLeft = 0; this.options = options; this.operationsLeft = options.operationsPerRun; this.client = github_1.getOctokit(options.repoToken); if (getActor) { - this.getActor = getActor; + this._getActor = getActor; } if (getIssues) { - this.getIssues = getIssues; + this._getIssues = getIssues; } if (listIssueComments) { - this.listIssueComments = listIssueComments; + this._listIssueComments = listIssueComments; } if (getLabelCreationDate) { - this.getLabelCreationDate = getLabelCreationDate; + this._getLabelCreationDate = getLabelCreationDate; } if (this.options.debugOnly) { - core.warning('Executing in debug mode. Debug output will be written but no issues will be processed.'); + logger.warning('Executing in debug mode. Debug output will be written but no issues will be processed.'); } } + static _updatedSince(timestamp, num_days) { + const daysInMillis = 1000 * 60 * 60 * 24 * num_days; + const millisSinceLastUpdated = new Date().getTime() - new Date(timestamp).getTime(); + return millisSinceLastUpdated <= daysInMillis; + } processIssues(page = 1) { return __awaiter(this, void 0, void 0, function* () { // get the next batch of issues - const issues = yield this.getIssues(page); + const issues = yield this._getIssues(page); this.operationsLeft -= 1; - const actor = yield this.getActor(); + const actor = yield this._getActor(); if (issues.length <= 0) { - core.info('No more issues found to process. Exiting.'); + logger.info('---'); + logger.info('No more issues found to process. Exiting.'); return this.operationsLeft; } for (const issue of issues.values()) { - const isPr = !!issue.pull_request; - core.info(`Found issue: issue #${issue.number} last updated ${issue.updated_at} (is pr? ${isPr})`); + const issueLogger = new issue_logger_1.IssueLogger(issue); + const isPr = is_pull_request_1.isPullRequest(issue); + issueLogger.info(`Found issue: issue #${issue.number} last updated ${issue.updated_at} (is pr? ${isPr})`); // calculate string based messages for this issue const staleMessage = isPr ? this.options.stalePrMessage @@ -100,48 +96,76 @@ class IssueProcessor { const skipMessage = isPr ? this.options.skipStalePrMessage : this.options.skipStaleIssueMessage; - const issueType = isPr ? 'pr' : 'issue'; - const shouldMarkWhenStale = this.options.daysBeforeStale > -1; - if (!staleMessage && shouldMarkWhenStale) { - core.info(`Skipping ${issueType} due to empty stale message`); + const issueType = get_issue_type_1.getIssueType(isPr); + const daysBeforeStale = isPr + ? this._getDaysBeforePrStale() + : this._getDaysBeforeIssueStale(); + if (isPr) { + issueLogger.info(`Days before pull request stale: ${daysBeforeStale}`); + } + else { + issueLogger.info(`Days before issue stale: ${daysBeforeStale}`); + } + const shouldMarkAsStale = should_mark_when_stale_1.shouldMarkWhenStale(daysBeforeStale); + if (!staleMessage && shouldMarkAsStale) { + issueLogger.info(`Skipping ${issueType} due to empty stale message`); continue; } if (issue.state === 'closed') { - core.info(`Skipping ${issueType} because it is closed`); + issueLogger.info(`Skipping ${issueType} because it is closed`); continue; // don't process closed issues } if (issue.locked) { - core.info(`Skipping ${issueType} because it is locked`); + issueLogger.info(`Skipping ${issueType} because it is locked`); continue; // don't process locked issues } - if (exemptLabels.some((exemptLabel) => is_labeled_1.isLabeled(issue, exemptLabel))) { - core.info(`Skipping ${issueType} because it has an exempt label`); - continue; // don't process exempt issues + if (this.options.startDate) { + const startDate = new Date(this.options.startDate); + const createdAt = new Date(issue.created_at); + issueLogger.info(`A start date was specified for the ${get_humanized_date_1.getHumanizedDate(startDate)} (${this.options.startDate})`); + // Expecting that GitHub will always set a creation date on the issues and PRs + // But you never know! + if (!is_valid_date_1.isValidDate(createdAt)) { + throw new Error(`Invalid issue field: "created_at". Expected a valid date`); + } + issueLogger.info(`Issue created the ${get_humanized_date_1.getHumanizedDate(createdAt)} (${issue.created_at})`); + if (!is_date_more_recent_than_1.isDateMoreRecentThan(createdAt, startDate)) { + issueLogger.info(`Skipping ${issueType} because it was created before the specified start date`); + continue; // don't process issues which were created before the start date + } } - // does this issue have a stale label? + // Does this issue have a stale label? let isStale = is_labeled_1.isLabeled(issue, staleLabel); if (isStale) { - core.info(`This issue has a stale label`); + issueLogger.info(`This issue has a stale label`); } else { - core.info(`This issue hasn't a stale label`); + issueLogger.info(`This issue hasn't a stale label`); + } + if (exemptLabels.some((exemptLabel) => is_labeled_1.isLabeled(issue, exemptLabel))) { + if (isStale) { + issueLogger.info(`An exempt label was added after the stale label.`); + yield this._removeStaleLabel(issue, staleLabel); + } + issueLogger.info(`Skipping ${issueType} because it has an exempt label`); + continue; // don't process exempt issues } // should this issue be marked stale? - const shouldBeStale = !IssueProcessor.updatedSince(issue.updated_at, this.options.daysBeforeStale); + const shouldBeStale = !IssueProcessor._updatedSince(issue.updated_at, this.options.daysBeforeStale); // determine if this issue needs to be marked stale first - if (!isStale && shouldBeStale && shouldMarkWhenStale) { - core.info(`Marking ${issueType} stale because it was last updated on ${issue.updated_at} and it does not have a stale label`); - yield this.markStale(issue, staleMessage, staleLabel, skipMessage); + if (!isStale && shouldBeStale && shouldMarkAsStale) { + issueLogger.info(`Marking ${issueType} stale because it was last updated on ${issue.updated_at} and it does not have a stale label`); + yield this._markStale(issue, staleMessage, staleLabel, skipMessage); isStale = true; // this issue is now considered stale } // process the issue if it was marked stale if (isStale) { - core.info(`Found a stale ${issueType}`); - yield this.processStaleIssue(issue, issueType, staleLabel, actor, closeMessage, closeLabel); + issueLogger.info(`Found a stale ${issueType}`); + yield this._processStaleIssue(issue, issueType, staleLabel, actor, closeMessage, closeLabel); } } if (this.operationsLeft <= 0) { - core.warning('Reached max number of operations to process. Exiting.'); + logger.warning('Reached max number of operations to process. Exiting.'); return 0; } // do the next batch @@ -149,54 +173,65 @@ class IssueProcessor { }); } // handle all of the stale issue logic when we find a stale issue - processStaleIssue(issue, issueType, staleLabel, actor, closeMessage, closeLabel) { + _processStaleIssue(issue, issueType, staleLabel, actor, closeMessage, closeLabel) { return __awaiter(this, void 0, void 0, function* () { - const markedStaleOn = (yield this.getLabelCreationDate(issue, staleLabel)) || issue.updated_at; - core.info(`Issue #${issue.number} marked stale on: ${markedStaleOn}`); - const issueHasComments = yield this.hasCommentsSince(issue, markedStaleOn, actor); - core.info(`Issue #${issue.number} has been commented on: ${issueHasComments}`); - const issueHasUpdate = IssueProcessor.updatedSince(issue.updated_at, this.options.daysBeforeClose); - core.info(`Issue #${issue.number} has been updated: ${issueHasUpdate}`); + const issueLogger = new issue_logger_1.IssueLogger(issue); + const markedStaleOn = (yield this._getLabelCreationDate(issue, staleLabel)) || issue.updated_at; + issueLogger.info(`Issue #${issue.number} marked stale on: ${markedStaleOn}`); + const issueHasComments = yield this._hasCommentsSince(issue, markedStaleOn, actor); + issueLogger.info(`Issue #${issue.number} has been commented on: ${issueHasComments}`); + const isPr = is_pull_request_1.isPullRequest(issue); + const daysBeforeClose = isPr + ? this._getDaysBeforePrClose() + : this._getDaysBeforeIssueClose(); + if (isPr) { + issueLogger.info(`Days before pull request close: ${daysBeforeClose}`); + } + else { + issueLogger.info(`Days before issue close: ${daysBeforeClose}`); + } + const issueHasUpdate = IssueProcessor._updatedSince(issue.updated_at, daysBeforeClose); + issueLogger.info(`Issue #${issue.number} has been updated: ${issueHasUpdate}`); // should we un-stale this issue? if (this.options.removeStaleWhenUpdated && issueHasComments) { - core.info(`Issue #${issue.number} is no longer stale. Removing stale label.`); - yield this.removeLabel(issue, staleLabel); + yield this._removeStaleLabel(issue, staleLabel); } // now start closing logic - if (this.options.daysBeforeClose < 0) { + if (daysBeforeClose < 0) { return; // nothing to do because we aren't closing stale issues } if (!issueHasComments && !issueHasUpdate) { - core.info(`Closing ${issueType} because it was last updated on ${issue.updated_at}`); - yield this.closeIssue(issue, closeMessage, closeLabel); + issueLogger.info(`Closing ${issueType} because it was last updated on ${issue.updated_at}`); + yield this._closeIssue(issue, closeMessage, closeLabel); if (this.options.deleteBranch && issue.pull_request) { - core.info(`Deleting branch for #${issue.number} as delete-branch option was specified`); - yield this.deleteBranch(issue); + issueLogger.info(`Deleting branch for #${issue.number} as delete-branch option was specified`); + yield this._deleteBranch(issue); this.deletedBranchIssues.push(issue); } } else { - core.info(`Stale ${issueType} is not old enough to close yet (hasComments? ${issueHasComments}, hasUpdate? ${issueHasUpdate})`); + issueLogger.info(`Stale ${issueType} is not old enough to close yet (hasComments? ${issueHasComments}, hasUpdate? ${issueHasUpdate})`); } }); } // checks to see if a given issue is still stale (has had activity on it) - hasCommentsSince(issue, sinceDate, actor) { + _hasCommentsSince(issue, sinceDate, actor) { return __awaiter(this, void 0, void 0, function* () { - core.info(`Checking for comments on issue #${issue.number} since ${sinceDate}`); + const issueLogger = new issue_logger_1.IssueLogger(issue); + issueLogger.info(`Checking for comments on issue #${issue.number} since ${sinceDate}`); if (!sinceDate) { return true; } // find any comments since the date - const comments = yield this.listIssueComments(issue.number, sinceDate); + const comments = yield this._listIssueComments(issue.number, sinceDate); const filteredComments = comments.filter(comment => comment.user.type === 'User' && comment.user.login !== actor); - core.info(`Comments not made by actor or another bot: ${filteredComments.length}`); + issueLogger.info(`Comments not made by actor or another bot: ${filteredComments.length}`); // if there are any user comments returned return filteredComments.length > 0; }); } // grab comments for an issue since a given date - listIssueComments(issueNumber, sinceDate) { + _listIssueComments(issueNumber, sinceDate) { return __awaiter(this, void 0, void 0, function* () { // find any comments since date on the given issue try { @@ -209,13 +244,13 @@ class IssueProcessor { return comments.data; } catch (error) { - core.error(`List issue comments error: ${error.message}`); + logger.error(`List issue comments error: ${error.message}`); return Promise.resolve([]); } }); } // get the actor from the GitHub token or context - getActor() { + _getActor() { return __awaiter(this, void 0, void 0, function* () { let actor; try { @@ -228,7 +263,7 @@ class IssueProcessor { }); } // grab issues from github in baches of 100 - getIssues(page) { + _getIssues(page) { return __awaiter(this, void 0, void 0, function* () { // generate type for response const endpoint = this.client.issues.listForRepo; @@ -245,15 +280,16 @@ class IssueProcessor { return issueResult.data; } catch (error) { - core.error(`Get issues for repo error: ${error.message}`); + logger.error(`Get issues for repo error: ${error.message}`); return Promise.resolve([]); } }); } // Mark an issue as stale with a comment and a label - markStale(issue, staleMessage, staleLabel, skipMessage) { + _markStale(issue, staleMessage, staleLabel, skipMessage) { return __awaiter(this, void 0, void 0, function* () { - core.info(`Marking issue #${issue.number} as stale`); + const issueLogger = new issue_logger_1.IssueLogger(issue); + issueLogger.info(`Marking issue #${issue.number} as stale`); this.staleIssues.push(issue); this.operationsLeft -= 2; // if the issue is being marked stale, the updated date should be changed to right now @@ -273,7 +309,7 @@ class IssueProcessor { }); } catch (error) { - core.error(`Error creating a comment: ${error.message}`); + issueLogger.error(`Error creating a comment: ${error.message}`); } } try { @@ -285,14 +321,15 @@ class IssueProcessor { }); } catch (error) { - core.error(`Error adding a label: ${error.message}`); + issueLogger.error(`Error adding a label: ${error.message}`); } }); } // Close an issue based on staleness - closeIssue(issue, closeMessage, closeLabel) { + _closeIssue(issue, closeMessage, closeLabel) { return __awaiter(this, void 0, void 0, function* () { - core.info(`Closing issue #${issue.number} for being stale`); + const issueLogger = new issue_logger_1.IssueLogger(issue); + issueLogger.info(`Closing issue #${issue.number} for being stale`); this.closedIssues.push(issue); this.operationsLeft -= 1; if (this.options.debugOnly) { @@ -308,7 +345,7 @@ class IssueProcessor { }); } catch (error) { - core.error(`Error creating a comment: ${error.message}`); + issueLogger.error(`Error creating a comment: ${error.message}`); } } if (closeLabel) { @@ -321,7 +358,7 @@ class IssueProcessor { }); } catch (error) { - core.error(`Error adding a label: ${error.message}`); + issueLogger.error(`Error adding a label: ${error.message}`); } } try { @@ -333,40 +370,42 @@ class IssueProcessor { }); } catch (error) { - core.error(`Error updating an issue: ${error.message}`); + issueLogger.error(`Error updating an issue: ${error.message}`); } }); } - getPullRequest(pullNumber) { + _getPullRequest(issue) { return __awaiter(this, void 0, void 0, function* () { + const issueLogger = new issue_logger_1.IssueLogger(issue); this.operationsLeft -= 1; try { const pullRequest = yield this.client.pulls.get({ owner: github_1.context.repo.owner, repo: github_1.context.repo.repo, - pull_number: pullNumber + pull_number: issue.number }); return pullRequest.data; } catch (error) { - core.error(`Error getting pull request ${pullNumber}: ${error.message}`); + issueLogger.error(`Error getting pull request ${issue.number}: ${error.message}`); } }); } // Delete the branch on closed pull request - deleteBranch(issue) { + _deleteBranch(issue) { return __awaiter(this, void 0, void 0, function* () { - core.info(`Delete branch from closed issue #${issue.number} - ${issue.title}`); + const issueLogger = new issue_logger_1.IssueLogger(issue); + issueLogger.info(`Delete branch from closed issue #${issue.number} - ${issue.title}`); if (this.options.debugOnly) { return; } - const pullRequest = yield this.getPullRequest(issue.number); + const pullRequest = yield this._getPullRequest(issue); if (!pullRequest) { - core.info(`Not deleting branch as pull request not found for issue ${issue.number}`); + issueLogger.info(`Not deleting branch as pull request not found for issue ${issue.number}`); return; } const branch = pullRequest.head.ref; - core.info(`Deleting branch ${branch} from closed issue #${issue.number}`); + issueLogger.info(`Deleting branch ${branch} from closed issue #${issue.number}`); this.operationsLeft -= 1; try { yield this.client.git.deleteRef({ @@ -376,14 +415,15 @@ class IssueProcessor { }); } catch (error) { - core.error(`Error deleting branch ${branch} from issue #${issue.number}: ${error.message}`); + issueLogger.error(`Error deleting branch ${branch} from issue #${issue.number}: ${error.message}`); } }); } // Remove a label from an issue - removeLabel(issue, label) { + _removeLabel(issue, label) { return __awaiter(this, void 0, void 0, function* () { - core.info(`Removing label "${label}" from issue #${issue.number}`); + const issueLogger = new issue_logger_1.IssueLogger(issue); + issueLogger.info(`Removing label "${label}" from issue #${issue.number}`); this.removedLabelIssues.push(issue); this.operationsLeft -= 1; // @todo remove the debug only to be able to test the code below @@ -399,15 +439,16 @@ class IssueProcessor { }); } catch (error) { - core.error(`Error removing a label: ${error.message}`); + issueLogger.error(`Error removing a label: ${error.message}`); } }); } // returns the creation date of a given label on an issue (or nothing if no label existed) ///see https://developer.github.com/v3/activity/events/ - getLabelCreationDate(issue, label) { + _getLabelCreationDate(issue, label) { return __awaiter(this, void 0, void 0, function* () { - core.info(`Checking for label on issue #${issue.number}`); + const issueLogger = new issue_logger_1.IssueLogger(issue); + issueLogger.info(`Checking for label on issue #${issue.number}`); this.operationsLeft -= 1; const options = this.client.issues.listEvents.endpoint.merge({ owner: github_1.context.repo.owner, @@ -425,10 +466,32 @@ class IssueProcessor { return staleLabeledEvent.created_at; }); } - static updatedSince(timestamp, num_days) { - const daysInMillis = 1000 * 60 * 60 * 24 * num_days; - const millisSinceLastUpdated = new Date().getTime() - new Date(timestamp).getTime(); - return millisSinceLastUpdated <= daysInMillis; + _getDaysBeforeIssueStale() { + return isNaN(this.options.daysBeforeIssueStale) + ? this.options.daysBeforeStale + : this.options.daysBeforeIssueStale; + } + _getDaysBeforePrStale() { + return isNaN(this.options.daysBeforePrStale) + ? this.options.daysBeforeStale + : this.options.daysBeforePrStale; + } + _getDaysBeforeIssueClose() { + return isNaN(this.options.daysBeforeIssueClose) + ? this.options.daysBeforeClose + : this.options.daysBeforeIssueClose; + } + _getDaysBeforePrClose() { + return isNaN(this.options.daysBeforePrClose) + ? this.options.daysBeforeClose + : this.options.daysBeforePrClose; + } + _removeStaleLabel(issue, staleLabel) { + return __awaiter(this, void 0, void 0, function* () { + const issueLogger = new issue_logger_1.IssueLogger(issue); + issueLogger.info(`Issue #${issue.number} is no longer stale. Removing stale label.`); + return this._removeLabel(issue, staleLabel); + }); } } exports.IssueProcessor = IssueProcessor; @@ -436,7 +499,202 @@ exports.IssueProcessor = IssueProcessor; /***/ }), -/***/ 792: +/***/ 1699: +/***/ (function(__unused_webpack_module, exports, __nccwpck_require__) { + +"use strict"; + +var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { + if (k2 === undefined) k2 = k; + Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } }); +}) : (function(o, m, k, k2) { + if (k2 === undefined) k2 = k; + o[k2] = m[k]; +})); +var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { + Object.defineProperty(o, "default", { enumerable: true, value: v }); +}) : function(o, v) { + o["default"] = v; +}); +var __importStar = (this && this.__importStar) || function (mod) { + if (mod && mod.__esModule) return mod; + var result = {}; + if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k); + __setModuleDefault(result, mod); + return result; +}; +Object.defineProperty(exports, "__esModule", ({ value: true })); +exports.IssueLogger = void 0; +const core = __importStar(__nccwpck_require__(2186)); +class IssueLogger { + constructor(issue) { + this._issue = issue; + } + warning(message) { + core.warning(this._prefixWithIssueNumber(message)); + } + info(message) { + core.info(this._prefixWithIssueNumber(message)); + } + error(message) { + core.error(this._prefixWithIssueNumber(message)); + } + _prefixWithIssueNumber(message) { + return `[#${this._getIssueNumber()}] ${message}`; + } + _getIssueNumber() { + return this._issue.number; + } +} +exports.IssueLogger = IssueLogger; + + +/***/ }), + +/***/ 8236: +/***/ (function(__unused_webpack_module, exports, __nccwpck_require__) { + +"use strict"; + +var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { + if (k2 === undefined) k2 = k; + Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } }); +}) : (function(o, m, k, k2) { + if (k2 === undefined) k2 = k; + o[k2] = m[k]; +})); +var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { + Object.defineProperty(o, "default", { enumerable: true, value: v }); +}) : function(o, v) { + o["default"] = v; +}); +var __importStar = (this && this.__importStar) || function (mod) { + if (mod && mod.__esModule) return mod; + var result = {}; + if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k); + __setModuleDefault(result, mod); + return result; +}; +Object.defineProperty(exports, "__esModule", ({ value: true })); +exports.Logger = void 0; +const core = __importStar(__nccwpck_require__(2186)); +class Logger { + warning(message) { + core.warning(message); + } + info(message) { + core.info(message); + } + error(message) { + core.error(message); + } +} +exports.Logger = Logger; + + +/***/ }), + +/***/ 9639: +/***/ ((__unused_webpack_module, exports) => { + +"use strict"; + +Object.defineProperty(exports, "__esModule", ({ value: true })); +exports.IssueType = void 0; +var IssueType; +(function (IssueType) { + IssueType["Issue"] = "issue"; + IssueType["PullRequest"] = "pr"; +})(IssueType = exports.IssueType || (exports.IssueType = {})); + + +/***/ }), + +/***/ 965: +/***/ ((__unused_webpack_module, exports) => { + +"use strict"; + +Object.defineProperty(exports, "__esModule", ({ value: true })); +exports.getHumanizedDate = void 0; +function getHumanizedDate(date) { + const year = date.getFullYear(); + let month = `${date.getMonth() + 1}`; + let day = `${date.getDate()}`; + if (month.length < 2) { + month = `0${month}`; + } + if (day.length < 2) { + day = `0${day}`; + } + return [day, month, year].join('-'); +} +exports.getHumanizedDate = getHumanizedDate; + + +/***/ }), + +/***/ 1473: +/***/ ((__unused_webpack_module, exports) => { + +"use strict"; + +Object.defineProperty(exports, "__esModule", ({ value: true })); +exports.isDateMoreRecentThan = void 0; +function isDateMoreRecentThan(date, comparedDate) { + return date > comparedDate; +} +exports.isDateMoreRecentThan = isDateMoreRecentThan; + + +/***/ }), + +/***/ 891: +/***/ ((__unused_webpack_module, exports) => { + +"use strict"; + +Object.defineProperty(exports, "__esModule", ({ value: true })); +exports.isValidDate = void 0; +/** + * @description + * Check if a date is valid + * + * @see + * https://stackoverflow.com/a/1353711/4440414 + * + * @param {Readonly} date The date to check + * + * @returns {boolean} true when the given date is valid + */ +function isValidDate(date) { + if (Object.prototype.toString.call(date) === '[object Date]') { + return !isNaN(date.getTime()); + } + return false; +} +exports.isValidDate = isValidDate; + + +/***/ }), + +/***/ 5153: +/***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { + +"use strict"; + +Object.defineProperty(exports, "__esModule", ({ value: true })); +exports.getIssueType = void 0; +const issue_type_1 = __nccwpck_require__(9639); +function getIssueType(isPullRequest) { + return isPullRequest ? issue_type_1.IssueType.PullRequest : issue_type_1.IssueType.Issue; +} +exports.getIssueType = getIssueType; + + +/***/ }), + +/***/ 6792: /***/ (function(__unused_webpack_module, exports, __nccwpck_require__) { "use strict"; @@ -446,7 +704,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) { }; Object.defineProperty(exports, "__esModule", ({ value: true })); exports.isLabeled = void 0; -const lodash_deburr_1 = __importDefault(__nccwpck_require__(601)); +const lodash_deburr_1 = __importDefault(__nccwpck_require__(1601)); /** * @description * Check if the label is listed as a label of the issue @@ -469,7 +727,22 @@ function cleanLabel(label) { /***/ }), -/***/ 107: +/***/ 5400: +/***/ ((__unused_webpack_module, exports) => { + +"use strict"; + +Object.defineProperty(exports, "__esModule", ({ value: true })); +exports.isPullRequest = void 0; +function isPullRequest(issue) { + return !!issue.pull_request; +} +exports.isPullRequest = isPullRequest; + + +/***/ }), + +/***/ 9107: /***/ ((__unused_webpack_module, exports) => { "use strict"; @@ -503,7 +776,22 @@ exports.labelsToList = labelsToList; /***/ }), -/***/ 109: +/***/ 2461: +/***/ ((__unused_webpack_module, exports) => { + +"use strict"; + +Object.defineProperty(exports, "__esModule", ({ value: true })); +exports.shouldMarkWhenStale = void 0; +function shouldMarkWhenStale(daysBeforeStale) { + return daysBeforeStale >= 0; +} +exports.shouldMarkWhenStale = shouldMarkWhenStale; + + +/***/ }), + +/***/ 3109: /***/ (function(__unused_webpack_module, exports, __nccwpck_require__) { "use strict"; @@ -537,8 +825,9 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge }); }; Object.defineProperty(exports, "__esModule", ({ value: true })); -const core = __importStar(__nccwpck_require__(186)); -const IssueProcessor_1 = __nccwpck_require__(407); +const core = __importStar(__nccwpck_require__(2186)); +const is_valid_date_1 = __nccwpck_require__(891); +const IssueProcessor_1 = __nccwpck_require__(4407); function run() { return __awaiter(this, void 0, void 0, function* () { try { @@ -560,7 +849,11 @@ function getAndValidateArgs() { closeIssueMessage: core.getInput('close-issue-message'), closePrMessage: core.getInput('close-pr-message'), daysBeforeStale: parseInt(core.getInput('days-before-stale', { required: true })), + daysBeforeIssueStale: parseInt(core.getInput('days-before-issue-stale')), + daysBeforePrStale: parseInt(core.getInput('days-before-pr-stale')), daysBeforeClose: parseInt(core.getInput('days-before-close', { required: true })), + daysBeforeIssueClose: parseInt(core.getInput('days-before-issue-close')), + daysBeforePrClose: parseInt(core.getInput('days-before-pr-close')), staleIssueLabel: core.getInput('stale-issue-label', { required: true }), closeIssueLabel: core.getInput('close-issue-label'), exemptIssueLabels: core.getInput('exempt-issue-labels'), @@ -574,17 +867,32 @@ function getAndValidateArgs() { ascending: core.getInput('ascending') === 'true', skipStalePrMessage: core.getInput('skip-stale-pr-message') === 'true', skipStaleIssueMessage: core.getInput('skip-stale-issue-message') === 'true', - deleteBranch: core.getInput('delete-branch') === 'true' + deleteBranch: core.getInput('delete-branch') === 'true', + startDate: core.getInput('start-date') !== '' + ? core.getInput('start-date') + : undefined }; for (const numberInput of [ 'days-before-stale', + 'days-before-issue-stale', + 'days-before-pr-stale', 'days-before-close', + 'days-before-issue-close', + 'days-before-pr-close', 'operations-per-run' ]) { if (isNaN(parseInt(core.getInput(numberInput)))) { throw Error(`input ${numberInput} did not parse to a valid integer`); } } + for (const optionalDateInput of ['start-date']) { + // Ignore empty dates because it is considered as the right type for a default value (so a valid one) + if (core.getInput(optionalDateInput) !== '') { + if (!is_valid_date_1.isValidDate(new Date(core.getInput(optionalDateInput)))) { + throw new Error(`input ${optionalDateInput} did not parse to a valid date`); + } + } + } return args; } run(); @@ -592,7 +900,7 @@ run(); /***/ }), -/***/ 351: +/***/ 7351: /***/ (function(__unused_webpack_module, exports, __nccwpck_require__) { "use strict"; @@ -605,8 +913,8 @@ var __importStar = (this && this.__importStar) || function (mod) { return result; }; Object.defineProperty(exports, "__esModule", ({ value: true })); -const os = __importStar(__nccwpck_require__(87)); -const utils_1 = __nccwpck_require__(278); +const os = __importStar(__nccwpck_require__(2087)); +const utils_1 = __nccwpck_require__(5278); /** * Commands * @@ -678,7 +986,7 @@ function escapeProperty(s) { /***/ }), -/***/ 186: +/***/ 2186: /***/ (function(__unused_webpack_module, exports, __nccwpck_require__) { "use strict"; @@ -700,11 +1008,11 @@ var __importStar = (this && this.__importStar) || function (mod) { return result; }; Object.defineProperty(exports, "__esModule", ({ value: true })); -const command_1 = __nccwpck_require__(351); +const command_1 = __nccwpck_require__(7351); const file_command_1 = __nccwpck_require__(717); -const utils_1 = __nccwpck_require__(278); -const os = __importStar(__nccwpck_require__(87)); -const path = __importStar(__nccwpck_require__(622)); +const utils_1 = __nccwpck_require__(5278); +const os = __importStar(__nccwpck_require__(2087)); +const path = __importStar(__nccwpck_require__(5622)); /** * The code to exit an action */ @@ -939,9 +1247,9 @@ var __importStar = (this && this.__importStar) || function (mod) { Object.defineProperty(exports, "__esModule", ({ value: true })); // We use any as a valid input type /* eslint-disable @typescript-eslint/no-explicit-any */ -const fs = __importStar(__nccwpck_require__(747)); -const os = __importStar(__nccwpck_require__(87)); -const utils_1 = __nccwpck_require__(278); +const fs = __importStar(__nccwpck_require__(5747)); +const os = __importStar(__nccwpck_require__(2087)); +const utils_1 = __nccwpck_require__(5278); function issueCommand(command, message) { const filePath = process.env[`GITHUB_${command}`]; if (!filePath) { @@ -959,7 +1267,7 @@ exports.issueCommand = issueCommand; /***/ }), -/***/ 278: +/***/ 5278: /***/ ((__unused_webpack_module, exports) => { "use strict"; @@ -985,15 +1293,15 @@ exports.toCommandValue = toCommandValue; /***/ }), -/***/ 53: +/***/ 4087: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; Object.defineProperty(exports, "__esModule", ({ value: true })); exports.Context = void 0; -const fs_1 = __nccwpck_require__(747); -const os_1 = __nccwpck_require__(87); +const fs_1 = __nccwpck_require__(5747); +const os_1 = __nccwpck_require__(2087); class Context { /** * Hydrate the context from the environment @@ -1042,7 +1350,7 @@ exports.Context = Context; /***/ }), -/***/ 438: +/***/ 5438: /***/ (function(__unused_webpack_module, exports, __nccwpck_require__) { "use strict"; @@ -1068,8 +1376,8 @@ var __importStar = (this && this.__importStar) || function (mod) { }; Object.defineProperty(exports, "__esModule", ({ value: true })); exports.getOctokit = exports.context = void 0; -const Context = __importStar(__nccwpck_require__(53)); -const utils_1 = __nccwpck_require__(30); +const Context = __importStar(__nccwpck_require__(4087)); +const utils_1 = __nccwpck_require__(3030); exports.context = new Context.Context(); /** * Returns a hydrated octokit ready to use for GitHub Actions @@ -1085,7 +1393,7 @@ exports.getOctokit = getOctokit; /***/ }), -/***/ 914: +/***/ 7914: /***/ (function(__unused_webpack_module, exports, __nccwpck_require__) { "use strict"; @@ -1111,7 +1419,7 @@ var __importStar = (this && this.__importStar) || function (mod) { }; Object.defineProperty(exports, "__esModule", ({ value: true })); exports.getApiBaseUrl = exports.getProxyAgent = exports.getAuthString = void 0; -const httpClient = __importStar(__nccwpck_require__(925)); +const httpClient = __importStar(__nccwpck_require__(9925)); function getAuthString(token, options) { if (!token && !options.auth) { throw new Error('Parameter token or opts.auth is required'); @@ -1135,7 +1443,7 @@ exports.getApiBaseUrl = getApiBaseUrl; /***/ }), -/***/ 30: +/***/ 3030: /***/ (function(__unused_webpack_module, exports, __nccwpck_require__) { "use strict"; @@ -1161,12 +1469,12 @@ var __importStar = (this && this.__importStar) || function (mod) { }; Object.defineProperty(exports, "__esModule", ({ value: true })); exports.getOctokitOptions = exports.GitHub = exports.context = void 0; -const Context = __importStar(__nccwpck_require__(53)); -const Utils = __importStar(__nccwpck_require__(914)); +const Context = __importStar(__nccwpck_require__(4087)); +const Utils = __importStar(__nccwpck_require__(7914)); // octokit + plugins -const core_1 = __nccwpck_require__(762); -const plugin_rest_endpoint_methods_1 = __nccwpck_require__(44); -const plugin_paginate_rest_1 = __nccwpck_require__(193); +const core_1 = __nccwpck_require__(6762); +const plugin_rest_endpoint_methods_1 = __nccwpck_require__(3044); +const plugin_paginate_rest_1 = __nccwpck_require__(4193); exports.context = new Context.Context(); const baseUrl = Utils.getApiBaseUrl(); const defaults = { @@ -1196,16 +1504,16 @@ exports.getOctokitOptions = getOctokitOptions; /***/ }), -/***/ 925: +/***/ 9925: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; Object.defineProperty(exports, "__esModule", ({ value: true })); -const url = __nccwpck_require__(835); -const http = __nccwpck_require__(605); -const https = __nccwpck_require__(211); -const pm = __nccwpck_require__(443); +const url = __nccwpck_require__(8835); +const http = __nccwpck_require__(8605); +const https = __nccwpck_require__(7211); +const pm = __nccwpck_require__(6443); let tunnel; var HttpCodes; (function (HttpCodes) { @@ -1615,7 +1923,7 @@ class HttpClient { if (useProxy) { // If using proxy, need tunnel if (!tunnel) { - tunnel = __nccwpck_require__(294); + tunnel = __nccwpck_require__(4294); } const agentOptions = { maxSockets: maxSockets, @@ -1735,13 +2043,13 @@ exports.HttpClient = HttpClient; /***/ }), -/***/ 443: +/***/ 6443: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; Object.defineProperty(exports, "__esModule", ({ value: true })); -const url = __nccwpck_require__(835); +const url = __nccwpck_require__(8835); function getProxyUrl(reqUrl) { let usingSsl = reqUrl.protocol === 'https:'; let proxyUrl; @@ -1858,7 +2166,7 @@ exports.createTokenAuth = createTokenAuth; /***/ }), -/***/ 762: +/***/ 6762: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; @@ -1866,10 +2174,10 @@ exports.createTokenAuth = createTokenAuth; Object.defineProperty(exports, "__esModule", ({ value: true })); -var universalUserAgent = __nccwpck_require__(429); -var beforeAfterHook = __nccwpck_require__(682); -var request = __nccwpck_require__(234); -var graphql = __nccwpck_require__(668); +var universalUserAgent = __nccwpck_require__(5030); +var beforeAfterHook = __nccwpck_require__(3682); +var request = __nccwpck_require__(6234); +var graphql = __nccwpck_require__(8467); var authToken = __nccwpck_require__(334); function _defineProperty(obj, key, value) { @@ -2042,7 +2350,7 @@ exports.Octokit = Octokit; /***/ }), -/***/ 440: +/***/ 9440: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; @@ -2052,8 +2360,8 @@ Object.defineProperty(exports, "__esModule", ({ value: true })); function _interopDefault (ex) { return (ex && (typeof ex === 'object') && 'default' in ex) ? ex['default'] : ex; } -var isPlainObject = _interopDefault(__nccwpck_require__(840)); -var universalUserAgent = __nccwpck_require__(429); +var isPlainObject = _interopDefault(__nccwpck_require__(8840)); +var universalUserAgent = __nccwpck_require__(5030); function lowercaseKeys(object) { if (!object) { @@ -2429,7 +2737,7 @@ exports.endpoint = endpoint; /***/ }), -/***/ 668: +/***/ 8467: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; @@ -2437,8 +2745,8 @@ exports.endpoint = endpoint; Object.defineProperty(exports, "__esModule", ({ value: true })); -var request = __nccwpck_require__(234); -var universalUserAgent = __nccwpck_require__(429); +var request = __nccwpck_require__(6234); +var universalUserAgent = __nccwpck_require__(5030); const VERSION = "4.5.2"; @@ -2522,7 +2830,7 @@ exports.withCustomRequest = withCustomRequest; /***/ }), -/***/ 193: +/***/ 4193: /***/ ((__unused_webpack_module, exports) => { "use strict"; @@ -2660,7 +2968,7 @@ exports.paginateRest = paginateRest; /***/ }), -/***/ 44: +/***/ 3044: /***/ ((__unused_webpack_module, exports) => { "use strict"; @@ -3885,8 +4193,8 @@ Object.defineProperty(exports, "__esModule", ({ value: true })); function _interopDefault (ex) { return (ex && (typeof ex === 'object') && 'default' in ex) ? ex['default'] : ex; } -var deprecation = __nccwpck_require__(932); -var once = _interopDefault(__nccwpck_require__(223)); +var deprecation = __nccwpck_require__(8932); +var once = _interopDefault(__nccwpck_require__(1223)); const logOnce = once(deprecation => console.warn(deprecation)); /** @@ -3938,7 +4246,7 @@ exports.RequestError = RequestError; /***/ }), -/***/ 234: +/***/ 6234: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; @@ -3948,9 +4256,9 @@ Object.defineProperty(exports, "__esModule", ({ value: true })); function _interopDefault (ex) { return (ex && (typeof ex === 'object') && 'default' in ex) ? ex['default'] : ex; } -var endpoint = __nccwpck_require__(440); -var universalUserAgent = __nccwpck_require__(429); -var isPlainObject = _interopDefault(__nccwpck_require__(840)); +var endpoint = __nccwpck_require__(9440); +var universalUserAgent = __nccwpck_require__(5030); +var isPlainObject = _interopDefault(__nccwpck_require__(8840)); var nodeFetch = _interopDefault(__nccwpck_require__(467)); var requestError = __nccwpck_require__(537); @@ -4094,12 +4402,12 @@ exports.request = request; /***/ }), -/***/ 682: +/***/ 3682: /***/ ((module, __unused_webpack_exports, __nccwpck_require__) => { -var register = __nccwpck_require__(670) -var addHook = __nccwpck_require__(549) -var removeHook = __nccwpck_require__(819) +var register = __nccwpck_require__(4670) +var addHook = __nccwpck_require__(5549) +var removeHook = __nccwpck_require__(6819) // bind with array of arguments: https://stackoverflow.com/a/21792913 var bind = Function.bind @@ -4158,7 +4466,7 @@ module.exports.Collection = Hook.Collection /***/ }), -/***/ 549: +/***/ 5549: /***/ ((module) => { module.exports = addHook @@ -4211,7 +4519,7 @@ function addHook (state, kind, name, hook) { /***/ }), -/***/ 670: +/***/ 4670: /***/ ((module) => { module.exports = register @@ -4246,7 +4554,7 @@ function register (state, name, method, options) { /***/ }), -/***/ 819: +/***/ 6819: /***/ ((module) => { module.exports = removeHook @@ -4270,7 +4578,7 @@ function removeHook (state, name, method) { /***/ }), -/***/ 932: +/***/ 8932: /***/ ((__unused_webpack_module, exports) => { "use strict"; @@ -4298,7 +4606,7 @@ exports.Deprecation = Deprecation; /***/ }), -/***/ 840: +/***/ 8840: /***/ ((module) => { "use strict"; @@ -4342,7 +4650,7 @@ module.exports = isPlainObject; /***/ }), -/***/ 601: +/***/ 1601: /***/ ((module) => { /** @@ -4616,11 +4924,11 @@ Object.defineProperty(exports, "__esModule", ({ value: true })); function _interopDefault (ex) { return (ex && (typeof ex === 'object') && 'default' in ex) ? ex['default'] : ex; } -var Stream = _interopDefault(__nccwpck_require__(413)); -var http = _interopDefault(__nccwpck_require__(605)); -var Url = _interopDefault(__nccwpck_require__(835)); -var https = _interopDefault(__nccwpck_require__(211)); -var zlib = _interopDefault(__nccwpck_require__(761)); +var Stream = _interopDefault(__nccwpck_require__(2413)); +var http = _interopDefault(__nccwpck_require__(8605)); +var Url = _interopDefault(__nccwpck_require__(8835)); +var https = _interopDefault(__nccwpck_require__(7211)); +var zlib = _interopDefault(__nccwpck_require__(8761)); // Based on https://github.com/tmpvar/jsdom/blob/aa85b2abf07766ff7bf5c1f6daafb3726f2f2db5/lib/jsdom/living/blob.js @@ -4771,7 +5079,7 @@ FetchError.prototype.name = 'FetchError'; let convert; try { - convert = __nccwpck_require__(877).convert; + convert = __nccwpck_require__(2877).convert; } catch (e) {} const INTERNALS = Symbol('Body internals'); @@ -6263,10 +6571,10 @@ exports.FetchError = FetchError; /***/ }), -/***/ 223: +/***/ 1223: /***/ ((module, __unused_webpack_exports, __nccwpck_require__) => { -var wrappy = __nccwpck_require__(940) +var wrappy = __nccwpck_require__(2940) module.exports = wrappy(once) module.exports.strict = wrappy(onceStrict) @@ -6312,27 +6620,27 @@ function onceStrict (fn) { /***/ }), -/***/ 294: +/***/ 4294: /***/ ((module, __unused_webpack_exports, __nccwpck_require__) => { -module.exports = __nccwpck_require__(219); +module.exports = __nccwpck_require__(4219); /***/ }), -/***/ 219: +/***/ 4219: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; -var net = __nccwpck_require__(631); -var tls = __nccwpck_require__(16); -var http = __nccwpck_require__(605); -var https = __nccwpck_require__(211); -var events = __nccwpck_require__(614); -var assert = __nccwpck_require__(357); -var util = __nccwpck_require__(669); +var net = __nccwpck_require__(1631); +var tls = __nccwpck_require__(4016); +var http = __nccwpck_require__(8605); +var https = __nccwpck_require__(7211); +var events = __nccwpck_require__(8614); +var assert = __nccwpck_require__(2357); +var util = __nccwpck_require__(1669); exports.httpOverHttp = httpOverHttp; @@ -6592,7 +6900,7 @@ exports.debug = debug; // for test /***/ }), -/***/ 429: +/***/ 5030: /***/ ((__unused_webpack_module, exports) => { "use strict"; @@ -6618,7 +6926,7 @@ exports.getUserAgent = getUserAgent; /***/ }), -/***/ 940: +/***/ 2940: /***/ ((module) => { // Returns a wrapper function that returns a wrapped callback @@ -6658,7 +6966,7 @@ function wrappy (fn, cb) { /***/ }), -/***/ 877: +/***/ 2877: /***/ ((module) => { module.exports = eval("require")("encoding"); @@ -6666,7 +6974,7 @@ module.exports = eval("require")("encoding"); /***/ }), -/***/ 357: +/***/ 2357: /***/ ((module) => { "use strict"; @@ -6674,7 +6982,7 @@ module.exports = require("assert");; /***/ }), -/***/ 614: +/***/ 8614: /***/ ((module) => { "use strict"; @@ -6682,7 +6990,7 @@ module.exports = require("events");; /***/ }), -/***/ 747: +/***/ 5747: /***/ ((module) => { "use strict"; @@ -6690,7 +6998,7 @@ module.exports = require("fs");; /***/ }), -/***/ 605: +/***/ 8605: /***/ ((module) => { "use strict"; @@ -6698,7 +7006,7 @@ module.exports = require("http");; /***/ }), -/***/ 211: +/***/ 7211: /***/ ((module) => { "use strict"; @@ -6706,7 +7014,7 @@ module.exports = require("https");; /***/ }), -/***/ 631: +/***/ 1631: /***/ ((module) => { "use strict"; @@ -6714,7 +7022,7 @@ module.exports = require("net");; /***/ }), -/***/ 87: +/***/ 2087: /***/ ((module) => { "use strict"; @@ -6722,7 +7030,7 @@ module.exports = require("os");; /***/ }), -/***/ 622: +/***/ 5622: /***/ ((module) => { "use strict"; @@ -6730,7 +7038,7 @@ module.exports = require("path");; /***/ }), -/***/ 413: +/***/ 2413: /***/ ((module) => { "use strict"; @@ -6738,7 +7046,7 @@ module.exports = require("stream");; /***/ }), -/***/ 16: +/***/ 4016: /***/ ((module) => { "use strict"; @@ -6746,7 +7054,7 @@ module.exports = require("tls");; /***/ }), -/***/ 835: +/***/ 8835: /***/ ((module) => { "use strict"; @@ -6754,7 +7062,7 @@ module.exports = require("url");; /***/ }), -/***/ 669: +/***/ 1669: /***/ ((module) => { "use strict"; @@ -6762,7 +7070,7 @@ module.exports = require("util");; /***/ }), -/***/ 761: +/***/ 8761: /***/ ((module) => { "use strict"; @@ -6808,6 +7116,6 @@ module.exports = require("zlib");; /******/ // module exports must be returned from runtime so entry inlining is disabled /******/ // startup /******/ // Load entry module and return exports -/******/ return __nccwpck_require__(109); +/******/ return __nccwpck_require__(3109); /******/ })() ; \ No newline at end of file diff --git a/package-lock.json b/package-lock.json index acfe5ed4e..f2385c681 100644 --- a/package-lock.json +++ b/package-lock.json @@ -418,9 +418,9 @@ } }, "@eslint/eslintrc": { - "version": "0.2.2", - "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-0.2.2.tgz", - "integrity": "sha512-EfB5OHNYp1F4px/LI/FEnGylop7nOqkQ1LRzCM0KccA2U8tvV8w01KBv37LbO7nW4H+YhKyo2LcJhRwjjV17QQ==", + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-0.3.0.tgz", + "integrity": "sha512-1JTKgrOKAHVivSvOYw+sJOunkBjUOvjqWk1DPja7ZFhIS2mX/4EgTT8M7eTK9jrKhL/FvXXEbQwIs3pg1xp3dg==", "dev": true, "requires": { "ajv": "^6.12.4", @@ -430,7 +430,7 @@ "ignore": "^4.0.6", "import-fresh": "^3.2.1", "js-yaml": "^3.13.1", - "lodash": "^4.17.19", + "lodash": "^4.17.20", "minimatch": "^3.0.4", "strip-json-comments": "^3.1.1" }, @@ -447,6 +447,15 @@ "uri-js": "^4.2.2" } }, + "argparse": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", + "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", + "dev": true, + "requires": { + "sprintf-js": "~1.0.2" + } + }, "globals": { "version": "12.4.0", "resolved": "https://registry.npmjs.org/globals/-/globals-12.4.0.tgz", @@ -455,6 +464,22 @@ "requires": { "type-fest": "^0.8.1" } + }, + "js-yaml": { + "version": "3.14.1", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.1.tgz", + "integrity": "sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==", + "dev": true, + "requires": { + "argparse": "^1.0.7", + "esprima": "^4.0.0" + } + }, + "lodash": { + "version": "4.17.20", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.20.tgz", + "integrity": "sha512-PlhdFcillOINfeV7Ni6oF1TAEayyZBoZ8bcshTHqOYJYlrqzRK5hagpagky5o4HfCzzd1TRkXPMFq6cKk9rGmA==", + "dev": true } } }, @@ -471,6 +496,15 @@ "resolve-from": "^5.0.0" }, "dependencies": { + "argparse": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", + "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", + "dev": true, + "requires": { + "sprintf-js": "~1.0.2" + } + }, "find-up": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", @@ -481,6 +515,16 @@ "path-exists": "^4.0.0" } }, + "js-yaml": { + "version": "3.14.1", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.1.tgz", + "integrity": "sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==", + "dev": true, + "requires": { + "argparse": "^1.0.7", + "esprima": "^4.0.0" + } + }, "locate-path": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", @@ -1523,6 +1567,11 @@ "universal-user-agent": "^6.0.0" } }, + "@octokit/openapi-types": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/@octokit/openapi-types/-/openapi-types-2.3.0.tgz", + "integrity": "sha512-Own8lHWVi5eEfLOnsIzAx16BoRbpkzac3QDUCxIqYMf4bjz+AGpv17UfRn1Va4lVmjwOpvZglpFI3mmxuQ+sIQ==" + }, "@octokit/plugin-paginate-rest": { "version": "2.2.4", "resolved": "https://registry.npmjs.org/@octokit/plugin-paginate-rest/-/plugin-paginate-rest-2.2.4.tgz", @@ -1571,32 +1620,92 @@ } }, "@octokit/rest": { - "version": "18.0.9", - "resolved": "https://registry.npmjs.org/@octokit/rest/-/rest-18.0.9.tgz", - "integrity": "sha512-CC5+cIx974Ygx9lQNfUn7/oXDQ9kqGiKUC6j1A9bAVZZ7aoTF8K6yxu0pQhQrLBwSl92J6Z3iVDhGhGFgISCZg==", - "requires": { - "@octokit/core": "^3.0.0", - "@octokit/plugin-paginate-rest": "^2.2.0", - "@octokit/plugin-request-log": "^1.0.0", - "@octokit/plugin-rest-endpoint-methods": "4.2.1" + "version": "18.0.12", + "resolved": "https://registry.npmjs.org/@octokit/rest/-/rest-18.0.12.tgz", + "integrity": "sha512-hNRCZfKPpeaIjOVuNJzkEL6zacfZlBPV8vw8ReNeyUkVvbuCvvrrx8K8Gw2eyHHsmd4dPlAxIXIZ9oHhJfkJpw==", + "requires": { + "@octokit/core": "^3.2.3", + "@octokit/plugin-paginate-rest": "^2.6.2", + "@octokit/plugin-request-log": "^1.0.2", + "@octokit/plugin-rest-endpoint-methods": "4.4.1" }, "dependencies": { + "@octokit/auth-token": { + "version": "2.4.4", + "resolved": "https://registry.npmjs.org/@octokit/auth-token/-/auth-token-2.4.4.tgz", + "integrity": "sha512-LNfGu3Ro9uFAYh10MUZVaT7X2CnNm2C8IDQmabx+3DygYIQjs9FwzFAHN/0t6mu5HEPhxcb1XOuxdpY82vCg2Q==", + "requires": { + "@octokit/types": "^6.0.0" + } + }, + "@octokit/core": { + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/@octokit/core/-/core-3.2.4.tgz", + "integrity": "sha512-d9dTsqdePBqOn7aGkyRFe7pQpCXdibSJ5SFnrTr0axevObZrpz3qkWm7t/NjYv5a66z6vhfteriaq4FRz3e0Qg==", + "requires": { + "@octokit/auth-token": "^2.4.4", + "@octokit/graphql": "^4.5.8", + "@octokit/request": "^5.4.12", + "@octokit/types": "^6.0.3", + "before-after-hook": "^2.1.0", + "universal-user-agent": "^6.0.0" + } + }, + "@octokit/graphql": { + "version": "4.5.8", + "resolved": "https://registry.npmjs.org/@octokit/graphql/-/graphql-4.5.8.tgz", + "integrity": "sha512-WnCtNXWOrupfPJgXe+vSmprZJUr0VIu14G58PMlkWGj3cH+KLZEfKMmbUQ6C3Wwx6fdhzVW1CD5RTnBdUHxhhA==", + "requires": { + "@octokit/request": "^5.3.0", + "@octokit/types": "^6.0.0", + "universal-user-agent": "^6.0.0" + } + }, + "@octokit/plugin-paginate-rest": { + "version": "2.7.1", + "resolved": "https://registry.npmjs.org/@octokit/plugin-paginate-rest/-/plugin-paginate-rest-2.7.1.tgz", + "integrity": "sha512-dUsxsEIrBqhlQNfXRhMhXOTQi0SSG38+QWcPGO226HFPFJk44vWukegHfMG3496vLv9T2oT7IuAGssGpcUg5bQ==", + "requires": { + "@octokit/types": "^6.3.1" + } + }, "@octokit/plugin-rest-endpoint-methods": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/@octokit/plugin-rest-endpoint-methods/-/plugin-rest-endpoint-methods-4.2.1.tgz", - "integrity": "sha512-QyFr4Bv807Pt1DXZOC5a7L5aFdrwz71UHTYoHVajYV5hsqffWm8FUl9+O7nxRu5PDMtB/IKrhFqTmdBTK5cx+A==", + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/@octokit/plugin-rest-endpoint-methods/-/plugin-rest-endpoint-methods-4.4.1.tgz", + "integrity": "sha512-+v5PcvrUcDeFXf8hv1gnNvNLdm4C0+2EiuWt9EatjjUmfriM1pTMM+r4j1lLHxeBQ9bVDmbywb11e3KjuavieA==", "requires": { - "@octokit/types": "^5.5.0", + "@octokit/types": "^6.1.0", "deprecation": "^2.3.1" } }, + "@octokit/request": { + "version": "5.4.12", + "resolved": "https://registry.npmjs.org/@octokit/request/-/request-5.4.12.tgz", + "integrity": "sha512-MvWYdxengUWTGFpfpefBBpVmmEYfkwMoxonIB3sUGp5rhdgwjXL1ejo6JbgzG/QD9B/NYt/9cJX1pxXeSIUCkg==", + "requires": { + "@octokit/endpoint": "^6.0.1", + "@octokit/request-error": "^2.0.0", + "@octokit/types": "^6.0.3", + "deprecation": "^2.0.0", + "is-plain-object": "^5.0.0", + "node-fetch": "^2.6.1", + "once": "^1.4.0", + "universal-user-agent": "^6.0.0" + } + }, "@octokit/types": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/@octokit/types/-/types-5.5.0.tgz", - "integrity": "sha512-UZ1pErDue6bZNjYOotCNveTXArOMZQFG6hKJfOnGnulVCMcVVi7YIIuuR4WfBhjo7zgpmzn/BkPDnUXtNx+PcQ==", + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/@octokit/types/-/types-6.3.1.tgz", + "integrity": "sha512-SyOaprLWVPS6QhbZY8hF9Oydx/UUnslKq1NyNUr4CN42UEPC3+9AvrYrDm4UvaU1D5u/vVMuSZOicFqOielRXQ==", "requires": { + "@octokit/openapi-types": "^2.3.0", "@types/node": ">= 8" } + }, + "is-plain-object": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-5.0.0.tgz", + "integrity": "sha512-VRSzKkbMm5jMDoKLbltAkFQ5Qr7VDiTFGXxYFXXowVj387GeGNOCsOH6Msy00SGZ3Fp84b1Naa1psqgcCIEP5Q==" } } }, @@ -1868,9 +1977,9 @@ } }, "@types/node": { - "version": "14.10.0", - "resolved": "https://registry.npmjs.org/@types/node/-/node-14.10.0.tgz", - "integrity": "sha512-SOIyrdADB4cq6eY1F+9iU48iIomFAPltu11LCvA9PKcyEwHadjCFzNVPotAR+oEJA0bCP4Xvvgy+vwu1ZjVh8g==" + "version": "14.14.21", + "resolved": "https://registry.npmjs.org/@types/node/-/node-14.14.21.tgz", + "integrity": "sha512-cHYfKsnwllYhjOzuC5q1VpguABBeecUp24yFluHpn/BQaVxB1CuQ1FSRZCzrPxrkIfWISXV2LbeoBthLWg0+0A==" }, "@types/normalize-package-data": { "version": "2.4.0", @@ -2230,13 +2339,10 @@ } }, "argparse": { - "version": "1.0.10", - "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", - "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", - "dev": true, - "requires": { - "sprintf-js": "~1.0.2" - } + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", + "dev": true }, "arr-diff": { "version": "4.0.0", @@ -2310,6 +2416,12 @@ "integrity": "sha1-WWZ/QfrdTyDMvCu5a41Pf3jsA2c=", "dev": true }, + "astral-regex": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/astral-regex/-/astral-regex-2.0.0.tgz", + "integrity": "sha512-Z7tMw1ytTXt5jqMcOP+OQteU1VuNK9Y02uuJtKQ1Sv69jXQKKg5cibLwGJow8yzZP+eAc18EmLGPal0bp36rvQ==", + "dev": true + }, "asynckit": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", @@ -3157,13 +3269,13 @@ } }, "eslint": { - "version": "7.17.0", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-7.17.0.tgz", - "integrity": "sha512-zJk08MiBgwuGoxes5sSQhOtibZ75pz0J35XTRlZOk9xMffhpA9BTbQZxoXZzOl5zMbleShbGwtw+1kGferfFwQ==", + "version": "7.18.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-7.18.0.tgz", + "integrity": "sha512-fbgTiE8BfUJZuBeq2Yi7J3RB3WGUQ9PNuNbmgi6jt9Iv8qrkxfy19Ds3OpL1Pm7zg3BtTVhvcUZbIRQ0wmSjAQ==", "dev": true, "requires": { "@babel/code-frame": "^7.0.0", - "@eslint/eslintrc": "^0.2.2", + "@eslint/eslintrc": "^0.3.0", "ajv": "^6.10.0", "chalk": "^4.0.0", "cross-spawn": "^7.0.2", @@ -3187,7 +3299,7 @@ "js-yaml": "^3.13.1", "json-stable-stringify-without-jsonify": "^1.0.1", "levn": "^0.4.1", - "lodash": "^4.17.19", + "lodash": "^4.17.20", "minimatch": "^3.0.4", "natural-compare": "^1.4.0", "optionator": "^0.9.1", @@ -3201,12 +3313,6 @@ "v8-compile-cache": "^2.0.3" }, "dependencies": { - "ansi-regex": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.0.tgz", - "integrity": "sha512-bY6fj56OUQ0hU1KjFNDQuJFezqKdrAyFdIevADiqrWHwSlbmBNMHp5ak2f40Pm8JTFyM2mqxkG6ngkHO11f/lg==", - "dev": true - }, "ansi-styles": { "version": "4.3.0", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", @@ -3216,6 +3322,15 @@ "color-convert": "^2.0.1" } }, + "argparse": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", + "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", + "dev": true, + "requires": { + "sprintf-js": "~1.0.2" + } + }, "chalk": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.0.tgz", @@ -3317,6 +3432,16 @@ "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", "dev": true }, + "js-yaml": { + "version": "3.14.1", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.1.tgz", + "integrity": "sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==", + "dev": true, + "requires": { + "argparse": "^1.0.7", + "esprima": "^4.0.0" + } + }, "levn": { "version": "0.4.1", "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", @@ -3327,6 +3452,12 @@ "type-check": "~0.4.0" } }, + "lodash": { + "version": "4.17.20", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.20.tgz", + "integrity": "sha512-PlhdFcillOINfeV7Ni6oF1TAEayyZBoZ8bcshTHqOYJYlrqzRK5hagpagky5o4HfCzzd1TRkXPMFq6cKk9rGmA==", + "dev": true + }, "optionator": { "version": "0.9.1", "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.1.tgz", @@ -3368,15 +3499,6 @@ "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", "dev": true }, - "strip-ansi": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.0.tgz", - "integrity": "sha512-AuvKTrTfQNYNIctbR1K/YGTR1756GycPsg7b9bdV9Duqur4gv6aKqHXah67Z8ImS7WEz5QVcOtlfW2rZEugt6w==", - "dev": true, - "requires": { - "ansi-regex": "^5.0.0" - } - }, "supports-color": { "version": "7.2.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", @@ -3631,12 +3753,6 @@ "eslint-visitor-keys": "^1.3.0" }, "dependencies": { - "acorn": { - "version": "7.4.1", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-7.4.1.tgz", - "integrity": "sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A==", - "dev": true - }, "eslint-visitor-keys": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-1.3.0.tgz", @@ -4069,17 +4185,6 @@ "requires": { "flatted": "^3.1.0", "rimraf": "^3.0.2" - }, - "dependencies": { - "rimraf": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", - "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", - "dev": true, - "requires": { - "glob": "^7.1.3" - } - } } }, "flatted": { @@ -4621,6 +4726,12 @@ "integrity": "sha1-qIwCU1eR8C7TfHahueqXc8gz+MI=", "dev": true }, + "is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "dev": true + }, "is-generator-fn": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/is-generator-fn/-/is-generator-fn-2.1.0.tgz", @@ -7544,13 +7655,12 @@ "dev": true }, "js-yaml": { - "version": "3.14.0", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.0.tgz", - "integrity": "sha512-/4IbIeHcD9VMHFqDR/gQ7EdZdLimOvW2DdcxFjdyyZ9NsbS+ccrXqVWDtab/lRl5AlUqmpBx8EhPaWR+OtY17A==", + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.0.0.tgz", + "integrity": "sha512-pqon0s+4ScYUvX30wxQi3PogGFAlUyH0awepWvwkj4jD4v+ova3RiYw8bmA6x2rDrEaj8i/oWKoRxpVNW+Re8Q==", "dev": true, "requires": { - "argparse": "^1.0.7", - "esprima": "^4.0.0" + "argparse": "^2.0.1" } }, "jsbn": { @@ -7742,6 +7852,14 @@ "integrity": "sha1-7dFMgk4sycHgsKG0K7UhBRakJDg=", "dev": true }, + "lru-cache": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "requires": { + "yallist": "^4.0.0" + } + }, "make-dir": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-3.1.0.tgz", @@ -8710,9 +8828,12 @@ } }, "semver": { - "version": "7.3.2", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.2.tgz", - "integrity": "sha512-OrOb32TeeambH6UrhtShmF7CRDqhL6/5XpPNp2DuRH6+9QLw/orhp72j87v8Qa1ScDkvrrBNpZcDejAirJmfXQ==" + "version": "7.3.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.4.tgz", + "integrity": "sha512-tCfb2WLjqFAtXn4KEdxIhalnRtoKFN7nAwj0B3ZXCbQloV2tq5eDbcTmT68JJD3nRJq24/XgxtQKFIpQdtvmVw==", + "requires": { + "lru-cache": "^6.0.0" + } }, "set-blocking": { "version": "2.0.0", @@ -8818,12 +8939,6 @@ "color-convert": "^2.0.1" } }, - "astral-regex": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/astral-regex/-/astral-regex-2.0.0.tgz", - "integrity": "sha512-Z7tMw1ytTXt5jqMcOP+OQteU1VuNK9Y02uuJtKQ1Sv69jXQKKg5cibLwGJow8yzZP+eAc18EmLGPal0bp36rvQ==", - "dev": true - }, "color-convert": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", @@ -8838,12 +8953,6 @@ "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", "dev": true - }, - "is-fullwidth-code-point": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", - "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", - "dev": true } } }, @@ -9858,6 +9967,11 @@ "integrity": "sha512-r9S/ZyXu/Xu9q1tYlpsLIsa3EeLXXk0VwlxqTcFRfg9EhMW+17kbt9G0NrgCmhGb5vT2hyhJZLfDGx+7+5Uj/w==", "dev": true }, + "yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==" + }, "yargs": { "version": "15.4.1", "resolved": "https://registry.npmjs.org/yargs/-/yargs-15.4.1.tgz", diff --git a/package.json b/package.json index b25b4f79a..ef50666a1 100644 --- a/package.json +++ b/package.json @@ -28,24 +28,24 @@ "dependencies": { "@actions/core": "^1.2.6", "@actions/github": "^4.0.0", - "@octokit/rest": "^18.0.9", + "@octokit/rest": "^18.0.12", "lodash.deburr": "^4.1.0", - "semver": "^7.3.2" + "semver": "^7.3.4" }, "devDependencies": { "@types/jest": "^26.0.15", "@types/lodash.deburr": "^4.1.6", - "@types/node": "^14.10.0", + "@types/node": "^14.14.21", "@types/semver": "^7.3.4", "@typescript-eslint/eslint-plugin": "^4.13.0", "@typescript-eslint/parser": "^4.13.0", "@vercel/ncc": "^0.27.0", - "eslint": "^7.17.0", + "eslint": "^7.18.0", "eslint-plugin-github": "^4.0.1", "eslint-plugin-jest": "^24.1.3", "jest": "^26.6.3", "jest-circus": "^26.6.3", - "js-yaml": "^3.14.0", + "js-yaml": "^4.0.0", "prettier": "^2.2.1", "ts-jest": "^26.4.4", "typescript": "^4.1.3" diff --git a/src/IssueProcessor.ts b/src/IssueProcessor.ts index 2d1106797..e8a3a0e21 100644 --- a/src/IssueProcessor.ts +++ b/src/IssueProcessor.ts @@ -1,7 +1,10 @@ import {context, getOctokit} from '@actions/github'; import {GitHub} from '@actions/github/lib/utils'; import {GetResponseTypeFromEndpointMethod} from '@octokit/types'; -import {IssueType} from './enums/issue-type.enum'; +import {IssueType} from './enums/issue-type'; +import {getHumanizedDate} from './functions/dates/get-humanized-date'; +import {isDateMoreRecentThan} from './functions/dates/is-date-more-recent-than'; +import {isValidDate} from './functions/dates/is-valid-date'; import {getIssueType} from './functions/get-issue-type'; import {IssueLogger} from './classes/issue-logger'; import {Logger} from './classes/logger'; @@ -9,11 +12,14 @@ import {isLabeled} from './functions/is-labeled'; import {isPullRequest} from './functions/is-pull-request'; import {labelsToList} from './functions/labels-to-list'; import {shouldMarkWhenStale} from './functions/should-mark-when-stale'; +import {IsoDateString} from './types/iso-date-string'; +import {IsoOrRfcDateString} from './types/iso-or-rfc-date-string'; export interface Issue { title: string; number: number; - updated_at: string; + created_at: IsoDateString; + updated_at: IsoDateString; labels: Label[]; pull_request: any; state: string; @@ -72,6 +78,7 @@ export interface IssueProcessorOptions { skipStaleIssueMessage: boolean; skipStalePrMessage: boolean; deleteBranch: boolean; + startDate: IsoOrRfcDateString | undefined; // Should be ISO 8601 or RFC 2822 } const logger: Logger = new Logger(); @@ -204,6 +211,39 @@ export class IssueProcessor { continue; // don't process locked issues } + if (this.options.startDate) { + const startDate: Date = new Date(this.options.startDate); + const createdAt: Date = new Date(issue.created_at); + + issueLogger.info( + `A start date was specified for the ${getHumanizedDate(startDate)} (${ + this.options.startDate + })` + ); + + // Expecting that GitHub will always set a creation date on the issues and PRs + // But you never know! + if (!isValidDate(createdAt)) { + throw new Error( + `Invalid issue field: "created_at". Expected a valid date` + ); + } + + issueLogger.info( + `Issue created the ${getHumanizedDate(createdAt)} (${ + issue.created_at + })` + ); + + if (!isDateMoreRecentThan(createdAt, startDate)) { + issueLogger.info( + `Skipping ${issueType} because it was created before the specified start date` + ); + + continue; // don't process issues which were created before the start date + } + } + // Does this issue have a stale label? let isStale: boolean = isLabeled(issue, staleLabel); diff --git a/src/enums/issue-type.enum.ts b/src/enums/issue-type.ts similarity index 100% rename from src/enums/issue-type.enum.ts rename to src/enums/issue-type.ts diff --git a/src/functions/dates/get-humanized-date.spec.ts b/src/functions/dates/get-humanized-date.spec.ts new file mode 100644 index 000000000..610772e0f --- /dev/null +++ b/src/functions/dates/get-humanized-date.spec.ts @@ -0,0 +1,33 @@ +import {getHumanizedDate} from './get-humanized-date'; + +describe('getHumanizedDate()', (): void => { + let date: Date; + + describe('when the given date is the 1st of april 2020', (): void => { + beforeEach((): void => { + date = new Date(2020, 3, 1); + }); + + it('should return the date formatted as DD-MM-YYYY', (): void => { + expect.assertions(1); + + const result = getHumanizedDate(date); + + expect(result).toStrictEqual('01-04-2020'); + }); + }); + + describe('when the given date is the 18st of december 2020', (): void => { + beforeEach((): void => { + date = new Date(2020, 11, 18); + }); + + it('should return the date formatted as DD-MM-YYYY', (): void => { + expect.assertions(1); + + const result = getHumanizedDate(date); + + expect(result).toStrictEqual('18-12-2020'); + }); + }); +}); diff --git a/src/functions/dates/get-humanized-date.ts b/src/functions/dates/get-humanized-date.ts new file mode 100644 index 000000000..a084758cc --- /dev/null +++ b/src/functions/dates/get-humanized-date.ts @@ -0,0 +1,17 @@ +import {HumanizedDate} from '../../types/humanized-date'; + +export function getHumanizedDate(date: Readonly): HumanizedDate { + const year: number = date.getFullYear(); + let month = `${date.getMonth() + 1}`; + let day = `${date.getDate()}`; + + if (month.length < 2) { + month = `0${month}`; + } + + if (day.length < 2) { + day = `0${day}`; + } + + return [day, month, year].join('-'); +} diff --git a/src/functions/dates/is-date-more-recent-than.spec.ts b/src/functions/dates/is-date-more-recent-than.spec.ts new file mode 100644 index 000000000..f564cf615 --- /dev/null +++ b/src/functions/dates/is-date-more-recent-than.spec.ts @@ -0,0 +1,51 @@ +import {isDateMoreRecentThan} from './is-date-more-recent-than'; + +describe('isDateMoreRecentThan()', (): void => { + let date: Date; + let comparedDate: Date; + + describe('when the given date is older than the compared date', (): void => { + beforeEach((): void => { + date = new Date(2020, 0, 20); + comparedDate = new Date(2021, 0, 20); + }); + + it('should return false', (): void => { + expect.assertions(1); + + const result = isDateMoreRecentThan(date, comparedDate); + + expect(result).toStrictEqual(false); + }); + }); + + describe('when the given date is equal to the compared date', (): void => { + beforeEach((): void => { + date = new Date(2020, 0, 20); + comparedDate = new Date(2020, 0, 20); + }); + + it('should return false', (): void => { + expect.assertions(1); + + const result = isDateMoreRecentThan(date, comparedDate); + + expect(result).toStrictEqual(false); + }); + }); + + describe('when the given date is more recent than the compared date', (): void => { + beforeEach((): void => { + date = new Date(2021, 0, 20); + comparedDate = new Date(2020, 0, 20); + }); + + it('should return true', (): void => { + expect.assertions(1); + + const result = isDateMoreRecentThan(date, comparedDate); + + expect(result).toStrictEqual(true); + }); + }); +}); diff --git a/src/functions/dates/is-date-more-recent-than.ts b/src/functions/dates/is-date-more-recent-than.ts new file mode 100644 index 000000000..482c9db8a --- /dev/null +++ b/src/functions/dates/is-date-more-recent-than.ts @@ -0,0 +1,6 @@ +export function isDateMoreRecentThan( + date: Readonly, + comparedDate: Readonly +): boolean { + return date > comparedDate; +} diff --git a/src/functions/dates/is-valid-date.spec.ts b/src/functions/dates/is-valid-date.spec.ts new file mode 100644 index 000000000..c2437f244 --- /dev/null +++ b/src/functions/dates/is-valid-date.spec.ts @@ -0,0 +1,61 @@ +import {isValidDate} from './is-valid-date'; + +describe('isValidDate()', (): void => { + let date: Date; + + describe('when the given date is an invalid date', (): void => { + beforeEach((): void => { + date = new Date('16-04-1994'); + }); + + it('should return false', (): void => { + expect.assertions(1); + + const result = isValidDate(date); + + expect(result).toStrictEqual(false); + }); + }); + + describe('when the given date is a new date', (): void => { + beforeEach((): void => { + date = new Date(); + }); + + it('should return true', (): void => { + expect.assertions(1); + + const result = isValidDate(date); + + expect(result).toStrictEqual(true); + }); + }); + + describe('when the given date is an ISO and valid date', (): void => { + beforeEach((): void => { + date = new Date('2011-04-22T13:33:48Z'); + }); + + it('should return true', (): void => { + expect.assertions(1); + + const result = isValidDate(date); + + expect(result).toStrictEqual(true); + }); + }); + + describe('when the given date is an ISO with ms and valid date', (): void => { + beforeEach((): void => { + date = new Date('2011-10-05T14:48:00.000Z'); + }); + + it('should return true', (): void => { + expect.assertions(1); + + const result = isValidDate(date); + + expect(result).toStrictEqual(true); + }); + }); +}); diff --git a/src/functions/dates/is-valid-date.ts b/src/functions/dates/is-valid-date.ts new file mode 100644 index 000000000..863b8864a --- /dev/null +++ b/src/functions/dates/is-valid-date.ts @@ -0,0 +1,18 @@ +/** + * @description + * Check if a date is valid + * + * @see + * https://stackoverflow.com/a/1353711/4440414 + * + * @param {Readonly} date The date to check + * + * @returns {boolean} true when the given date is valid + */ +export function isValidDate(date: Readonly): boolean { + if (Object.prototype.toString.call(date) === '[object Date]') { + return !isNaN(date.getTime()); + } + + return false; +} diff --git a/src/functions/get-issue-type.ts b/src/functions/get-issue-type.ts index 86830a4b7..d3603286d 100644 --- a/src/functions/get-issue-type.ts +++ b/src/functions/get-issue-type.ts @@ -1,4 +1,4 @@ -import {IssueType} from '../enums/issue-type.enum'; +import {IssueType} from '../enums/issue-type'; export function getIssueType(isPullRequest: Readonly): IssueType { return isPullRequest ? IssueType.PullRequest : IssueType.Issue; diff --git a/src/functions/is-labeled.ts b/src/functions/is-labeled.ts index 5ad332a7a..751117546 100644 --- a/src/functions/is-labeled.ts +++ b/src/functions/is-labeled.ts @@ -1,7 +1,6 @@ import deburr from 'lodash.deburr'; import {Issue, Label} from '../IssueProcessor'; - -type CleanLabel = string; +import {CleanLabel} from '../types/clean-label'; /** * @description diff --git a/src/main.ts b/src/main.ts index dfc99a6e4..2e44657da 100644 --- a/src/main.ts +++ b/src/main.ts @@ -1,4 +1,5 @@ import * as core from '@actions/core'; +import {isValidDate} from './functions/dates/is-valid-date'; import {IssueProcessor, IssueProcessorOptions} from './IssueProcessor'; async function run(): Promise { @@ -14,7 +15,7 @@ async function run(): Promise { } function getAndValidateArgs(): IssueProcessorOptions { - const args = { + const args: IssueProcessorOptions = { repoToken: core.getInput('repo-token'), staleIssueMessage: core.getInput('stale-issue-message'), stalePrMessage: core.getInput('stale-pr-message'), @@ -47,7 +48,11 @@ function getAndValidateArgs(): IssueProcessorOptions { ascending: core.getInput('ascending') === 'true', skipStalePrMessage: core.getInput('skip-stale-pr-message') === 'true', skipStaleIssueMessage: core.getInput('skip-stale-issue-message') === 'true', - deleteBranch: core.getInput('delete-branch') === 'true' + deleteBranch: core.getInput('delete-branch') === 'true', + startDate: + core.getInput('start-date') !== '' + ? core.getInput('start-date') + : undefined }; for (const numberInput of [ @@ -64,6 +69,17 @@ function getAndValidateArgs(): IssueProcessorOptions { } } + for (const optionalDateInput of ['start-date']) { + // Ignore empty dates because it is considered as the right type for a default value (so a valid one) + if (core.getInput(optionalDateInput) !== '') { + if (!isValidDate(new Date(core.getInput(optionalDateInput)))) { + throw new Error( + `input ${optionalDateInput} did not parse to a valid date` + ); + } + } + } + return args; } diff --git a/src/types/clean-label.ts b/src/types/clean-label.ts new file mode 100644 index 000000000..339a978b4 --- /dev/null +++ b/src/types/clean-label.ts @@ -0,0 +1 @@ +export type CleanLabel = string; diff --git a/src/types/humanized-date.ts b/src/types/humanized-date.ts new file mode 100644 index 000000000..3866556d8 --- /dev/null +++ b/src/types/humanized-date.ts @@ -0,0 +1 @@ +export type HumanizedDate = string; diff --git a/src/types/iso-date-string.ts b/src/types/iso-date-string.ts new file mode 100644 index 000000000..8fb000ce8 --- /dev/null +++ b/src/types/iso-date-string.ts @@ -0,0 +1 @@ +export type IsoDateString = string; diff --git a/src/types/iso-or-rfc-date-string.ts b/src/types/iso-or-rfc-date-string.ts new file mode 100644 index 000000000..d74eaae40 --- /dev/null +++ b/src/types/iso-or-rfc-date-string.ts @@ -0,0 +1 @@ +export type IsoOrRfcDateString = string;