Compare commits

..

6 Commits

Author SHA1 Message Date
Ross Brodbeck
7a47124766 Default un-stale to true in new version 2020-05-11 10:34:40 -04:00
Ross Brodbeck
299593dff1 Add vscode debug settings 2020-05-11 10:30:00 -04:00
Ross Brodbeck
379e98cf36 Add a test for un-staling 2020-05-11 10:29:41 -04:00
Ross Brodbeck
51cc4676ff Add debugging, update tests and fix logic. 2020-05-11 09:44:50 -04:00
Ross Brodbeck
5a67f409c9 Fix up tests a bit to make this runnable (tests failing though) 2020-05-02 06:29:17 -04:00
Ross Brodbeck
cd198e6d88 First pass at ignorning issues with comments and allowing removal of the stale label 2020-05-02 06:04:09 -04:00
13 changed files with 22047 additions and 5374 deletions

View File

@@ -1,6 +1,6 @@
{
"plugins": ["jest", "@typescript-eslint"],
"extends": ["plugin:github/recommended"],
"extends": ["plugin:github/es6"],
"parser": "@typescript-eslint/parser",
"parserOptions": {
"ecmaVersion": 9,
@@ -16,10 +16,11 @@
"@typescript-eslint/no-require-imports": "error",
"@typescript-eslint/array-type": "error",
"@typescript-eslint/await-thenable": "error",
"@typescript-eslint/ban-ts-comment": "error",
"@typescript-eslint/ban-ts-ignore": "error",
"camelcase": "off",
"@typescript-eslint/consistent-type-assertions": "error",
"@typescript-eslint/class-name-casing": "error",
"@typescript-eslint/func-call-spacing": ["error", "never"],
"@typescript-eslint/generic-type-naming": ["error", "^[A-Z][A-Za-z]*$"],
"@typescript-eslint/no-array-constructor": "error",
"@typescript-eslint/no-empty-interface": "error",
"@typescript-eslint/no-explicit-any": "off",
@@ -29,6 +30,7 @@
"@typescript-eslint/no-misused-new": "error",
"@typescript-eslint/no-namespace": "error",
"@typescript-eslint/no-non-null-assertion": "warn",
"@typescript-eslint/no-object-literal-type-assertion": "error",
"@typescript-eslint/no-unnecessary-qualifier": "error",
"@typescript-eslint/no-unnecessary-type-assertion": "error",
"@typescript-eslint/no-useless-constructor": "error",
@@ -36,6 +38,7 @@
"@typescript-eslint/prefer-for-of": "warn",
"@typescript-eslint/prefer-function-type": "warn",
"@typescript-eslint/prefer-includes": "error",
"@typescript-eslint/prefer-interface": "error",
"@typescript-eslint/prefer-string-starts-ends-with": "error",
"@typescript-eslint/promise-function-async": "error",
"@typescript-eslint/require-array-sort-compare": "error",

View File

@@ -1,9 +0,0 @@
version: 2
updates:
# Enable version updates for npm
- package-ecosystem: 'npm'
# Look for `package.json` and `lock` files in the `root` directory
directory: '/'
# Check the npm registry for updates every day (weekdays)
schedule:
interval: 'daily'

View File

@@ -1,36 +0,0 @@
name: "Code scanning"
on:
push:
pull_request:
schedule:
- cron: '0 19 * * 0'
jobs:
CodeQL-Build:
# CodeQL runs on ubuntu-latest and windows-latest
runs-on: ubuntu-latest
steps:
- name: Checkout repository
uses: actions/checkout@v2
with:
# We must fetch at least the immediate parents so that if this is
# a pull request then we can checkout the head.
fetch-depth: 2
# If this run was triggered by a pull request event, then checkout
# the head of the pull request instead of the merge commit.
- run: git checkout HEAD^2
if: ${{ github.event_name == 'pull_request' }}
# Initializes the CodeQL tools for scanning.
- name: Initialize CodeQL
uses: github/codeql-action/init@v1
- name: Autobuild
uses: github/codeql-action/autobuild@v1
- name: Perform CodeQL Analysis
uses: github/codeql-action/analyze@v1

View File

@@ -3,7 +3,7 @@ on: # rebuild any PRs and main branch changes
pull_request:
push:
branches:
- main
- master
- 'releases/*'
jobs:

View File

@@ -34,7 +34,7 @@ jobs:
stale:
runs-on: ubuntu-latest
steps:
- uses: actions/stale@v3
- uses: actions/stale@v1.1.0
with:
repo-token: ${{ secrets.GITHUB_TOKEN }}
stale-issue-message: 'Message to comment on stale issues. If none provided, will not mark issues stale'
@@ -52,7 +52,7 @@ jobs:
stale:
runs-on: ubuntu-latest
steps:
- uses: actions/stale@v3
- uses: actions/stale@v1.1.0
with:
repo-token: ${{ secrets.GITHUB_TOKEN }}
stale-issue-message: 'This issue is stale because it has been open 30 days with no activity. Remove stale label or comment or this will be closed in 5 days'
@@ -71,18 +71,17 @@ jobs:
stale:
runs-on: ubuntu-latest
steps:
- uses: actions/stale@v3
- uses: actions/stale@v1.1.0
with:
repo-token: ${{ secrets.GITHUB_TOKEN }}
stale-issue-message: 'Stale issue message'
stale-pr-message: 'Stale pull request message'
stale-pr-message: 'Stale issue message'
stale-issue-label: 'no-issue-activity'
exempt-issue-labels: 'awaiting-approval,work-in-progress'
stale-pr-label: 'no-pr-activity'
exempt-pr-labels: 'awaiting-approval,work-in-progress'
only-labels: 'awaiting-feedback,awaiting-answers'
```
### 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.
To see debug ouput 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.

View File

@@ -31,14 +31,12 @@ function generateIssue(
};
}
const DefaultProcessorOptions: IssueProcessorOptions = Object.freeze({
const DefaultProcessorOptions: IssueProcessorOptions = {
repoToken: 'none',
staleIssueMessage: 'This issue is stale',
stalePrMessage: 'This PR is stale',
closeIssueMessage: 'This issue is being closed',
closePrMessage: 'This PR is being closed',
daysBeforeStale: 1,
daysBeforeClose: 30,
daysBeforeClose: 1,
staleIssueLabel: 'Stale',
exemptIssueLabels: '',
stalePrLabel: 'Stale',
@@ -46,11 +44,8 @@ const DefaultProcessorOptions: IssueProcessorOptions = Object.freeze({
onlyLabels: '',
operationsPerRun: 100,
debugOnly: true,
removeStaleWhenUpdated: false,
ascending: false,
skipStaleIssueMessage: false,
skipStalePrMessage: false
});
removeStaleWhenUpdated: false
};
test('empty issue list results in 1 operation', async () => {
const processor = new IssueProcessor(
@@ -67,85 +62,11 @@ test('empty issue list results in 1 operation', async () => {
expect(operationsLeft).toEqual(99);
});
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', async () => {
test('processing an issue with no label will make it stale', async () => {
const TestIssueList: Issue[] = [
generateIssue(1, 'An issue with no label', '2020-01-01T17:00:00Z')
];
const opts = {...DefaultProcessorOptions};
opts.daysBeforeClose = 0;
const processor = new IssueProcessor(
opts,
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).toEqual(1);
expect(processor.closedIssues.length).toEqual(1);
});
test('processing an issue with no label will make it stale and not close it if days-before-close is set to > 0', async () => {
const TestIssueList: Issue[] = [
generateIssue(1, 'An issue with no label', '2020-01-01T17:00:00Z')
];
const opts = {...DefaultProcessorOptions};
opts.daysBeforeClose = 15;
const processor = new IssueProcessor(
DefaultProcessorOptions,
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).toEqual(1);
expect(processor.closedIssues.length).toEqual(0);
});
test('processing an issue with no label will not make it stale if days-before-stale is set to -1', async () => {
const TestIssueList: Issue[] = [
generateIssue(1, 'An issue with no label', '2020-01-01T17:00:00Z')
];
const opts = {
...DefaultProcessorOptions,
staleIssueMessage: '',
daysBeforeStale: -1
};
const processor = new IssueProcessor(
opts,
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).toEqual(0);
expect(processor.closedIssues.length).toEqual(0);
});
test('processing an issue with no label will make it stale but not close it', async () => {
// issue should be from 2 days ago so it will be
// stale but not close-able, based on default settings
let issueDate = new Date();
issueDate.setDate(issueDate.getDate() - 2);
const TestIssueList: Issue[] = [
generateIssue(1, 'An issue with no label', issueDate.toDateString())
];
const processor = new IssueProcessor(
DefaultProcessorOptions,
async p => (p == 1 ? TestIssueList : []),
@@ -210,60 +131,6 @@ test('processing a stale PR will close it', async () => {
expect(processor.closedIssues.length).toEqual(1);
});
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'
])
];
const opts = {
...DefaultProcessorOptions,
daysBeforeStale: -1,
staleIssueMessage: ''
};
const processor = new IssueProcessor(
opts,
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).toEqual(0);
expect(processor.closedIssues.length).toEqual(1);
});
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'
])
];
const opts = {
...DefaultProcessorOptions,
daysBeforeStale: -1,
stalePrMessage: ''
};
const processor = new IssueProcessor(
opts,
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).toEqual(0);
expect(processor.closedIssues.length).toEqual(1);
});
test('closed issues will not be marked stale', async () => {
const TestIssueList: Issue[] = [
generateIssue(
@@ -535,7 +402,6 @@ test('exempt issue labels will not be marked stale (multi issue label)', async (
expect(processor.staleIssues.length).toEqual(0);
expect(processor.closedIssues.length).toEqual(0);
expect(processor.removedLabelIssues.length).toEqual(0);
});
test('exempt pr labels will not be marked stale', async () => {
@@ -584,7 +450,6 @@ test('stale issues should not be closed if days is set to -1', async () => {
await processor.processIssues(1);
expect(processor.closedIssues.length).toEqual(0);
expect(processor.removedLabelIssues.length).toEqual(0);
});
test('stale label should be removed if a comment was added to a stale issue', async () => {
@@ -598,13 +463,13 @@ test('stale label should be removed if a comment was added to a stale issue', as
)
];
const opts = {...DefaultProcessorOptions};
const opts = DefaultProcessorOptions;
opts.removeStaleWhenUpdated = true;
const processor = new IssueProcessor(
opts,
async p => (p == 1 ? TestIssueList : []),
async (num, dt) => [{user: {login: 'notme', type: 'User'}}], // return a fake comment to indicate there was an update
async (num, dt) => [{user: {type: 'User'}}], // return a fake comment so indicate there was an update
async (issue, label) => new Date().toDateString()
);
@@ -615,281 +480,3 @@ test('stale label should be removed if a comment was added to a stale issue', as
expect(processor.staleIssues.length).toEqual(0);
expect(processor.removedLabelIssues.length).toEqual(1);
});
test('stale label should not be removed if a comment was added by the bot (and the issue should be closed)', async () => {
github.context.actor = 'abot';
const TestIssueList: Issue[] = [
generateIssue(
1,
'An issue that should stay stale',
'2020-01-01T17:00:00Z',
false,
['Stale']
)
];
const opts = {...DefaultProcessorOptions};
opts.removeStaleWhenUpdated = true;
const processor = new IssueProcessor(
opts,
async p => (p == 1 ? TestIssueList : []),
async (num, dt) => [{user: {login: 'abot', type: 'User'}}], // return a fake comment to indicate there was an update by the bot
async (issue, label) => new Date().toDateString()
);
// process our fake issue list
await processor.processIssues(1);
expect(processor.closedIssues.length).toEqual(1);
expect(processor.staleIssues.length).toEqual(0);
expect(processor.removedLabelIssues.length).toEqual(0);
});
test('stale issues should not be closed until after the closed number of days', async () => {
let lastUpdate = new Date();
lastUpdate.setDate(lastUpdate.getDate() - 5);
const TestIssueList: Issue[] = [
generateIssue(
1,
'An issue that should be marked stale but not closed',
lastUpdate.toString(),
false
)
];
const opts = {...DefaultProcessorOptions};
opts.daysBeforeStale = 5; // stale after 5 days
opts.daysBeforeClose = 1; // closes after 6 days
const processor = new IssueProcessor(
opts,
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.closedIssues.length).toEqual(0);
expect(processor.removedLabelIssues.length).toEqual(0);
expect(processor.staleIssues.length).toEqual(1);
});
test('stale issues should be closed if the closed nubmer of days (additive) is also passed', async () => {
let lastUpdate = new Date();
lastUpdate.setDate(lastUpdate.getDate() - 7);
const TestIssueList: Issue[] = [
generateIssue(
1,
'An issue that should be stale and closed',
lastUpdate.toString(),
false,
['Stale']
)
];
const opts = {...DefaultProcessorOptions};
opts.daysBeforeStale = 5; // stale after 5 days
opts.daysBeforeClose = 1; // closes after 6 days
const processor = new IssueProcessor(
opts,
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.closedIssues.length).toEqual(1);
expect(processor.removedLabelIssues.length).toEqual(0);
expect(processor.staleIssues.length).toEqual(0);
});
test('stale issues should not be closed until after the closed number of days (long)', async () => {
let lastUpdate = new Date();
lastUpdate.setDate(lastUpdate.getDate() - 10);
const TestIssueList: Issue[] = [
generateIssue(
1,
'An issue that should be marked stale but not closed',
lastUpdate.toString(),
false
)
];
const opts = {...DefaultProcessorOptions};
opts.daysBeforeStale = 5; // stale after 5 days
opts.daysBeforeClose = 20; // closes after 25 days
const processor = new IssueProcessor(
opts,
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.closedIssues.length).toEqual(0);
expect(processor.removedLabelIssues.length).toEqual(0);
expect(processor.staleIssues.length).toEqual(1);
});
test('skips stale message on issues when skip-stale-issue-message is set', async () => {
let lastUpdate = new Date();
lastUpdate.setDate(lastUpdate.getDate() - 10);
const TestIssueList: Issue[] = [
generateIssue(
1,
'An issue that should be marked stale but not closed',
lastUpdate.toString(),
false
)
];
const opts = {...DefaultProcessorOptions};
opts.daysBeforeStale = 5; // stale after 5 days
opts.daysBeforeClose = 20; // closes after 25 days
opts.skipStaleIssueMessage = true;
const processor = new IssueProcessor(
opts,
async p => (p == 1 ? TestIssueList : []),
async (num, dt) => [],
async (issue, label) => new Date().toDateString()
);
// for sake of testing, mocking private function
const markSpy = jest.spyOn(processor as any, 'markStale');
await processor.processIssues(1);
// issue should be staled
expect(processor.closedIssues.length).toEqual(0);
expect(processor.removedLabelIssues.length).toEqual(0);
expect(processor.staleIssues.length).toEqual(1);
// comment should not be created
expect(markSpy).toHaveBeenCalledWith(
TestIssueList[0],
opts.staleIssueMessage,
opts.staleIssueLabel,
// this option is skipMessage
true
);
});
test('skips stale message on prs when skip-stale-pr-message is set', async () => {
let lastUpdate = new Date();
lastUpdate.setDate(lastUpdate.getDate() - 10);
const TestIssueList: Issue[] = [
generateIssue(
1,
'An issue that should be marked stale but not closed',
lastUpdate.toString(),
true
)
];
const opts = {...DefaultProcessorOptions};
opts.daysBeforeStale = 5; // stale after 5 days
opts.daysBeforeClose = 20; // closes after 25 days
opts.skipStalePrMessage = true;
const processor = new IssueProcessor(
opts,
async p => (p == 1 ? TestIssueList : []),
async (num, dt) => [],
async (issue, label) => new Date().toDateString()
);
// for sake of testing, mocking private function
const markSpy = jest.spyOn(processor as any, 'markStale');
await processor.processIssues(1);
// issue should be staled
expect(processor.closedIssues.length).toEqual(0);
expect(processor.removedLabelIssues.length).toEqual(0);
expect(processor.staleIssues.length).toEqual(1);
// comment should not be created
expect(markSpy).toHaveBeenCalledWith(
TestIssueList[0],
opts.stalePrMessage,
opts.stalePrLabel,
// this option is skipMessage
true
);
});
test('not providing state takes precedence over skipStaleIssueMessage', async () => {
let lastUpdate = new Date();
lastUpdate.setDate(lastUpdate.getDate() - 10);
const TestIssueList: Issue[] = [
generateIssue(
1,
'An issue that should be marked stale but not closed',
lastUpdate.toString(),
false
)
];
const opts = {...DefaultProcessorOptions};
opts.daysBeforeStale = 5; // stale after 5 days
opts.daysBeforeClose = 20; // closes after 25 days
opts.skipStalePrMessage = true;
opts.staleIssueMessage = '';
const processor = new IssueProcessor(
opts,
async p => (p == 1 ? TestIssueList : []),
async (num, dt) => [],
async (issue, label) => new Date().toDateString()
);
await processor.processIssues(1);
// issue should be staled
expect(processor.closedIssues.length).toEqual(0);
expect(processor.removedLabelIssues.length).toEqual(0);
expect(processor.staleIssues.length).toEqual(0);
});
test('not providing stalePrMessage takes precedence over skipStalePrMessage', async () => {
let lastUpdate = new Date();
lastUpdate.setDate(lastUpdate.getDate() - 10);
const TestIssueList: Issue[] = [
generateIssue(
1,
'An issue that should be marked stale but not closed',
lastUpdate.toString(),
true
)
];
const opts = {...DefaultProcessorOptions};
opts.daysBeforeStale = 5; // stale after 5 days
opts.daysBeforeClose = 20; // closes after 25 days
opts.skipStalePrMessage = true;
opts.stalePrMessage = '';
const processor = new IssueProcessor(
opts,
async p => (p == 1 ? TestIssueList : []),
async (num, dt) => [],
async (issue, label) => new Date().toDateString()
);
await processor.processIssues(1);
// issue should be staled
expect(processor.closedIssues.length).toEqual(0);
expect(processor.removedLabelIssues.length).toEqual(0);
expect(processor.staleIssues.length).toEqual(0);
});

View File

@@ -9,12 +9,8 @@ inputs:
description: 'The message to post on the issue when tagging it. If none provided, will not mark issues stale.'
stale-pr-message:
description: 'The message to post on the pr when tagging it. If none provided, will not mark pull requests stale.'
close-issue-message:
description: 'The message to post on the issue when closing it. If none provided, will not comment when closing an issue.'
close-pr-message:
description: 'The message to post on the pr when closing it. If none provided, will not comment when closing a pull requests.'
days-before-stale:
description: 'The number of days old an issue can be before marking it stale. Set to -1 to never mark issues or pull requests as stale automatically.'
description: 'The number of days old an issue can be before marking it stale.'
default: 60
days-before-close:
description: 'The number of days to wait to close an issue or pull request after it being marked stale. Set to -1 to never close stale issues.'
@@ -43,15 +39,6 @@ inputs:
debug-only:
description: 'Run the processor in debug mode without actually performing any operations on live issues.'
default: false
ascending:
description: 'The order to get issues or pull requests. Defaults to false, which is descending'
default: false
skip-stale-pr-message:
description: 'Skip adding stale message when marking a pull request as stale.'
default: false
skip-stale-issue-message:
description: 'Skip adding stale message when marking an issue as stale.'
default: false
runs:
using: 'node12'
main: 'dist/index.js'

22951
dist/index.js vendored

File diff suppressed because it is too large Load Diff

593
dist/licenses.txt vendored
View File

@@ -1,593 +0,0 @@
@actions/core
MIT
Copyright 2019 GitHub
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and
associated documentation files (the "Software"), to deal in the Software without restriction,
including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense,
and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so,
subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT
LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@actions/github
MIT
Copyright 2019 GitHub
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and
associated documentation files (the "Software"), to deal in the Software without restriction,
including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense,
and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so,
subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT
LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@actions/http-client
MIT
Actions Http Client for Node.js
Copyright (c) GitHub, Inc.
All rights reserved.
MIT License
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and
associated documentation files (the "Software"), to deal in the Software without restriction,
including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense,
and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so,
subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED *AS IS*, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT
LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@octokit/auth-token
MIT
The MIT License
Copyright (c) 2019 Octokit contributors
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
@octokit/core
MIT
The MIT License
Copyright (c) 2019 Octokit contributors
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
@octokit/endpoint
MIT
The MIT License
Copyright (c) 2018 Octokit contributors
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
@octokit/graphql
MIT
The MIT License
Copyright (c) 2018 Octokit contributors
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
@octokit/plugin-paginate-rest
MIT
MIT License Copyright (c) 2019 Octokit contributors
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice (including the next paragraph) shall be included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@octokit/plugin-rest-endpoint-methods
MIT
MIT License Copyright (c) 2019 Octokit contributors
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice (including the next paragraph) shall be included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@octokit/request
MIT
The MIT License
Copyright (c) 2018 Octokit contributors
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
@octokit/request-error
MIT
The MIT License
Copyright (c) 2019 Octokit contributors
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
@zeit/ncc
MIT
Copyright 2018 ZEIT, Inc.
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
before-after-hook
Apache-2.0
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "{}"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright 2018 Gregor Martynus and other contributors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
deprecation
ISC
The ISC License
Copyright (c) Gregor Martynus and contributors
Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted, provided that the above
copyright notice and this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR
IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
is-plain-object
MIT
The MIT License (MIT)
Copyright (c) 2014-2017, Jon Schlinkert.
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
node-fetch
MIT
The MIT License (MIT)
Copyright (c) 2016 David Frank
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
once
ISC
The ISC License
Copyright (c) Isaac Z. Schlueter and Contributors
Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted, provided that the above
copyright notice and this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR
IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
tunnel
MIT
The MIT License (MIT)
Copyright (c) 2012 Koichi Kobayashi
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
universal-user-agent
ISC
# [ISC License](https://spdx.org/licenses/ISC)
Copyright (c) 2018, Gregor Martynus (https://github.com/gr2m)
Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted, provided that the above copyright notice and this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
wrappy
ISC
The ISC License
Copyright (c) Isaac Z. Schlueter and Contributors
Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted, provided that the above
copyright notice and this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR
IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.

3001
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@@ -25,25 +25,25 @@
"author": "GitHub",
"license": "MIT",
"dependencies": {
"@actions/core": "^1.2.4",
"@actions/github": "^4.0.0",
"@octokit/rest": "^18.0.2",
"semver": "^7.3.2"
"@actions/core": "^1.2.3",
"@actions/github": "^2.1.1",
"@octokit/rest": "^16.43.1",
"semver": "^6.1.1"
},
"devDependencies": {
"@types/semver": "^7.3.1",
"@types/jest": "^26.0.5",
"@types/node": "^14.6.0",
"@typescript-eslint/parser": "^3.7.0",
"@vercel/ncc": "^0.23.0",
"@types/semver": "^6.0.0",
"@types/jest": "^24.0.23",
"@types/node": "^12.7.12",
"@typescript-eslint/parser": "^2.8.0",
"@zeit/ncc": "^0.20.5",
"eslint": "^5.16.0",
"eslint-plugin-github": "^4.0.1",
"eslint-plugin-jest": "^23.18.0",
"eslint-plugin-github": "^2.0.0",
"eslint-plugin-jest": "^22.21.0",
"jest": "^24.9.0",
"jest-circus": "^26.1.0",
"js-yaml": "^3.14.0",
"prettier": "^2.0.5",
"jest-circus": "^24.9.0",
"js-yaml": "^3.13.1",
"prettier": "^1.19.1",
"ts-jest": "^24.2.0",
"typescript": "^3.9.7"
"typescript": "^3.6.4"
}
}

View File

@@ -1,6 +1,8 @@
import * as core from '@actions/core';
import {context, getOctokit} from '@actions/github';
import {GetResponseTypeFromEndpointMethod} from '@octokit/types';
import * as github from '@actions/github';
import {Octokit} from '@octokit/rest';
type OcotoKitIssueList = Octokit.Response<Octokit.IssuesListForRepoResponse>;
export interface Issue {
title: string;
@@ -14,7 +16,6 @@ export interface Issue {
export interface User {
type: string;
login: string;
}
export interface Comment {
@@ -35,8 +36,6 @@ export interface IssueProcessorOptions {
repoToken: string;
staleIssueMessage: string;
stalePrMessage: string;
closeIssueMessage: string;
closePrMessage: string;
daysBeforeStale: number;
daysBeforeClose: number;
staleIssueLabel: string;
@@ -47,18 +46,15 @@ export interface IssueProcessorOptions {
operationsPerRun: number;
removeStaleWhenUpdated: boolean;
debugOnly: boolean;
ascending: boolean;
skipStaleIssueMessage: boolean;
skipStalePrMessage: boolean;
}
/***
* Handle processing of issues for staleness/closure.
*/
export class IssueProcessor {
readonly client: any; // need to make this the correct type
readonly client: github.GitHub;
readonly options: IssueProcessorOptions;
private operationsLeft = 0;
private operationsLeft: number = 0;
readonly staleIssues: Issue[] = [];
readonly closedIssues: Issue[] = [];
@@ -71,14 +67,11 @@ export class IssueProcessor {
issueNumber: number,
sinceDate: string
) => Promise<Comment[]>,
getLabelCreationDate?: (
issue: Issue,
label: string
) => Promise<string | undefined>
getLabelCreationDate?: (issue: Issue, label: string) => Promise<string>
) {
this.options = options;
this.operationsLeft = options.operationsPerRun;
this.client = getOctokit(options.repoToken);
this.client = new github.GitHub(options.repoToken);
if (getIssues) {
this.getIssues = getIssues;
@@ -99,20 +92,25 @@ export class IssueProcessor {
}
}
async processIssues(page = 1): Promise<number> {
async processIssues(page: number = 1): Promise<number> {
if (this.operationsLeft <= 0) {
core.warning('Reached max number of operations to process. Exiting.');
return 0;
}
// get the next batch of issues
const issues: Issue[] = await this.getIssues(page);
this.operationsLeft -= 1;
if (issues.length <= 0) {
core.info('No more issues found to process. Exiting.');
core.debug('No more issues found to process. Exiting.');
return this.operationsLeft;
}
for (const issue of issues.values()) {
const isPr = !!issue.pull_request;
core.info(
core.debug(
`Found issue: issue #${issue.number} - ${issue.title} last updated ${issue.updated_at} (is pr? ${isPr})`
);
@@ -120,33 +118,26 @@ export class IssueProcessor {
const staleMessage: string = isPr
? this.options.stalePrMessage
: this.options.staleIssueMessage;
const closeMessage: string = isPr
? this.options.closePrMessage
: this.options.closeIssueMessage;
const staleLabel: string = isPr
? this.options.stalePrLabel
: this.options.staleIssueLabel;
const exemptLabels = IssueProcessor.parseCommaSeparatedString(
isPr ? this.options.exemptPrLabels : this.options.exemptIssueLabels
);
const skipMessage = isPr
? this.options.skipStalePrMessage
: this.options.skipStaleIssueMessage;
const issueType: string = isPr ? 'pr' : 'issue';
const shouldMarkWhenStale = this.options.daysBeforeStale > -1;
if (!staleMessage && shouldMarkWhenStale) {
core.info(`Skipping ${issueType} due to empty stale message`);
if (!staleMessage) {
core.debug(`Skipping ${issueType} due to empty stale message`);
continue;
}
if (issue.state === 'closed') {
core.info(`Skipping ${issueType} because it is closed`);
core.debug(`Skipping ${issueType} because it is closed`);
continue; // don't process closed issues
}
if (issue.locked) {
core.info(`Skipping ${issueType} because it is locked`);
core.debug(`Skipping ${issueType} because it is locked`);
continue; // don't process locked issues
}
@@ -155,43 +146,25 @@ export class IssueProcessor {
IssueProcessor.isLabeled(issue, exemptLabel)
)
) {
core.info(`Skipping ${issueType} because it has an exempt label`);
core.debug(`Skipping ${issueType} because it has an exempt label`);
continue; // don't process exempt issues
}
// does this issue have a stale label?
let isStale = IssueProcessor.isLabeled(issue, staleLabel);
// should this issue be marked stale?
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`
if (IssueProcessor.isLabeled(issue, staleLabel)) {
core.debug(`Found a stale ${issueType}`);
await this.processStaleIssue(issue, issueType, staleLabel);
} else if (
!IssueProcessor.updatedSince(
issue.updated_at,
this.options.daysBeforeStale
)
) {
core.debug(
`Marking ${issueType} stale because it was last updated on ${issue.updated_at}`
);
await this.markStale(issue, staleMessage, staleLabel, skipMessage);
isStale = true; // this issue is now considered stale
await this.markStale(issue, staleMessage, staleLabel);
this.operationsLeft -= 2;
}
// process the issue if it was marked stale
if (isStale) {
core.info(`Found a stale ${issueType}`);
await this.processStaleIssue(
issue,
issueType,
staleLabel,
closeMessage
);
}
}
if (this.operationsLeft <= 0) {
core.warning('Reached max number of operations to process. Exiting.');
return 0;
}
// do the next batch
@@ -202,79 +175,65 @@ export class IssueProcessor {
private async processStaleIssue(
issue: Issue,
issueType: string,
staleLabel: string,
closeMessage?: string
staleLabel: string
) {
const markedStaleOn: string =
(await this.getLabelCreationDate(issue, staleLabel)) || issue.updated_at;
core.info(`Issue #${issue.number} marked stale on: ${markedStaleOn}`);
if (this.options.daysBeforeClose < 0) {
return; // nothing to do because we aren't closing stale issues
}
const issueHasComments: boolean = await this.hasCommentsSince(
const markedStaleOn: string = await this.getLabelCreationDate(
issue,
staleLabel
);
const issueHasComments: boolean = await this.isIssueStillStale(
issue,
markedStaleOn
);
core.info(
`Issue #${issue.number} has been commented on: ${issueHasComments}`
);
const issueHasUpdate: boolean = IssueProcessor.updatedSince(
issue.updated_at,
this.options.daysBeforeClose
);
core.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.`
);
await this.removeLabel(issue, staleLabel);
}
// now start closing logic
if (this.options.daysBeforeClose < 0) {
return; // nothing to do because we aren't closing stale issues
}
core.debug(`Issue #${issue.number} marked stale on: ${markedStaleOn}`);
core.debug(`Issue #${issue.number} has been updated: ${issueHasUpdate}`);
core.debug(
`Issue #${issue.number} has been commented on: ${issueHasComments}`
);
if (!issueHasComments && !issueHasUpdate) {
core.info(
core.debug(
`Closing ${issueType} because it was last updated on ${issue.updated_at}`
);
await this.closeIssue(issue, closeMessage);
await this.closeIssue(issue);
} else {
core.info(
`Stale ${issueType} is not old enough to close yet (hasComments? ${issueHasComments}, hasUpdate? ${issueHasUpdate}`
);
if (this.options.removeStaleWhenUpdated) {
await this.removeLabel(issue, staleLabel);
}
core.debug(`Ignoring stale ${issueType} because it was updated recenlty`);
}
}
// checks to see if a given issue is still stale (has had activity on it)
private async hasCommentsSince(
private async isIssueStillStale(
issue: Issue,
sinceDate: string
): Promise<boolean> {
core.info(
`Checking for comments on issue #${issue.number} since ${sinceDate}`
core.debug(
`Checking for comments on issue #${issue.number} since ${sinceDate} to see if it is still stale`
);
if (!sinceDate) {
return true;
return true; // if no date was provided then the issue was marked stale a long time ago
}
// find any comments since the date
this.operationsLeft -= 1;
// find any comments since the stale label
const comments = await this.listIssueComments(issue.number, sinceDate);
const filteredComments = comments.filter(
comment =>
comment.user.type === 'User' && comment.user.login !== context.actor
);
core.info(
`Comments not made by ${context.actor} or another bot: ${filteredComments.length}`
);
// if there are any user comments returned
return filteredComments.length > 0;
// if there are any user comments returned, issue is not stale anymore
return comments.filter(comment => comment.user.type === 'User').length > 0;
}
// grab comments for an issue since a given date
@@ -283,95 +242,66 @@ export class IssueProcessor {
sinceDate: string
): Promise<Comment[]> {
// find any comments since date on the given issue
try {
const comments = await this.client.issues.listComments({
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: issueNumber,
since: sinceDate
});
return comments.data;
} catch (error) {
core.error(`List issue comments error: ${error.message}`);
return Promise.resolve([]);
}
const comments = await this.client.issues.listComments({
owner: github.context.repo.owner,
repo: github.context.repo.repo,
issue_number: issueNumber,
since: sinceDate
});
return comments.data;
}
// grab issues from github in baches of 100
private async getIssues(page: number): Promise<Issue[]> {
// generate type for response
const endpoint = this.client.issues.listForRepo;
type OctoKitIssueList = GetResponseTypeFromEndpointMethod<typeof endpoint>;
const issueResult: OcotoKitIssueList = await this.client.issues.listForRepo(
{
owner: github.context.repo.owner,
repo: github.context.repo.repo,
state: 'open',
labels: this.options.onlyLabels,
per_page: 100,
page
}
);
try {
const issueResult: OctoKitIssueList = await this.client.issues.listForRepo(
{
owner: context.repo.owner,
repo: context.repo.repo,
state: 'open',
labels: this.options.onlyLabels,
per_page: 100,
direction: this.options.ascending ? 'asc' : 'desc',
page
}
);
return issueResult.data;
} catch (error) {
core.error(`Get issues for repo error: ${error.message}`);
return Promise.resolve([]);
}
return issueResult.data;
}
// Mark an issue as stale with a comment and a label
private async markStale(
issue: Issue,
staleMessage: string,
staleLabel: string,
skipMessage: boolean
staleLabel: string
): Promise<void> {
core.info(`Marking issue #${issue.number} - ${issue.title} as stale`);
core.debug(`Marking issue #${issue.number} - ${issue.title} 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
// so that close calculations work correctly
const newUpdatedAtDate: Date = new Date();
issue.updated_at = newUpdatedAtDate.toString();
if (this.options.debugOnly) {
return;
}
if (!skipMessage) {
try {
await this.client.issues.createComment({
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: issue.number,
body: staleMessage
});
} catch (error) {
core.error(`Error creating a comment: ${error.message}`);
}
}
await this.client.issues.createComment({
owner: github.context.repo.owner,
repo: github.context.repo.repo,
issue_number: issue.number,
body: staleMessage
});
try {
await this.client.issues.addLabels({
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: issue.number,
labels: [staleLabel]
});
} catch (error) {
core.error(`Error adding a label: ${error.message}`);
}
await this.client.issues.addLabels({
owner: github.context.repo.owner,
repo: github.context.repo.repo,
issue_number: issue.number,
labels: [staleLabel]
});
}
// Close an issue based on staleness
private async closeIssue(issue: Issue, closeMessage?: string): Promise<void> {
core.info(
private async closeIssue(issue: Issue): Promise<void> {
core.debug(
`Closing issue #${issue.number} - ${issue.title} for being stale`
);
@@ -383,34 +313,17 @@ export class IssueProcessor {
return;
}
if (closeMessage) {
try {
await this.client.issues.createComment({
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: issue.number,
body: closeMessage
});
} catch (error) {
core.error(`Error creating a comment: ${error.message}`);
}
}
try {
await this.client.issues.update({
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: issue.number,
state: 'closed'
});
} catch (error) {
core.error(`Error updating an issue: ${error.message}`);
}
await this.client.issues.update({
owner: github.context.repo.owner,
repo: github.context.repo.repo,
issue_number: issue.number,
state: 'closed'
});
}
// Remove a label from an issue
private async removeLabel(issue: Issue, label: string): Promise<void> {
core.info(
core.debug(
`Removing label ${label} from issue #${issue.number} - ${issue.title}`
);
@@ -422,16 +335,12 @@ export class IssueProcessor {
return;
}
try {
await this.client.issues.removeLabel({
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: issue.number,
name: encodeURIComponent(label) // A label can have a "?" in the name
});
} catch (error) {
core.error(`Error removing a label: ${error.message}`);
}
await this.client.issues.removeLabel({
owner: github.context.repo.owner,
repo: github.context.repo.repo,
issue_number: issue.number,
name: encodeURIComponent(label) // A label can have a "?" in the name
});
}
// returns the creation date of a given label on an issue (or nothing if no label existed)
@@ -439,14 +348,14 @@ export class IssueProcessor {
private async getLabelCreationDate(
issue: Issue,
label: string
): Promise<string | undefined> {
core.info(`Checking for label ${label} on issue #${issue.number}`);
): Promise<string> {
core.debug(`Checking for label ${label} on issue #${issue.number}`);
this.operationsLeft -= 1;
const options = this.client.issues.listEvents.endpoint.merge({
owner: context.repo.owner,
repo: context.repo.repo,
owner: github.context.repo.owner,
repo: github.context.repo.repo,
per_page: 100,
issue_number: issue.number
});
@@ -458,12 +367,7 @@ export class IssueProcessor {
event => event.event === 'labeled' && event.label.name === label
);
if (!staleLabeledEvent) {
// Must be old rather than labeled
return undefined;
}
return staleLabeledEvent.created_at;
return staleLabeledEvent!.created_at;
}
private static isLabeled(issue: Issue, label: string): boolean {
@@ -477,7 +381,7 @@ export class IssueProcessor {
const millisSinceLastUpdated =
new Date().getTime() - new Date(timestamp).getTime();
return millisSinceLastUpdated <= daysInMillis;
return millisSinceLastUpdated < daysInMillis;
}
private static parseCommaSeparatedString(s: string): string[] {

View File

@@ -18,8 +18,6 @@ function getAndValidateArgs(): IssueProcessorOptions {
repoToken: core.getInput('repo-token', {required: true}),
staleIssueMessage: core.getInput('stale-issue-message'),
stalePrMessage: core.getInput('stale-pr-message'),
closeIssueMessage: core.getInput('close-issue-message'),
closePrMessage: core.getInput('close-pr-message'),
daysBeforeStale: parseInt(
core.getInput('days-before-stale', {required: true})
),
@@ -37,10 +35,7 @@ function getAndValidateArgs(): IssueProcessorOptions {
removeStaleWhenUpdated: !(
core.getInput('remove-stale-when-updated') === 'false'
),
debugOnly: core.getInput('debug-only') === 'true',
ascending: core.getInput('ascending') === 'true',
skipStalePrMessage: core.getInput('skip-stale-pr-message') === 'true',
skipStaleIssueMessage: core.getInput('skip-stale-issue-message') === 'true'
debugOnly: core.getInput('debug-only') === 'true'
};
for (const numberInput of [