Compare commits

..

1 Commits

Author SHA1 Message Date
Ross Brodbeck
f4ddf3e2e8 Update package dependencies 2021-01-15 07:50:03 -05:00
47 changed files with 1099 additions and 4470 deletions

View File

@@ -1,57 +1,52 @@
{ {
"plugins": ["jest", "@typescript-eslint"], "plugins": ["jest", "@typescript-eslint"],
"extends": ["plugin:github/recommended"], "extends": ["plugin:github/recommended"],
"parser": "@typescript-eslint/parser", "parser": "@typescript-eslint/parser",
"parserOptions": { "parserOptions": {
"ecmaVersion": 9, "ecmaVersion": 9,
"sourceType": "module", "sourceType": "module",
"project": "./tsconfig.json" "project": "./tsconfig.json"
}, },
"rules": { "rules": {
"eslint-comments/no-use": "off", "eslint-comments/no-use": "off",
"import/no-namespace": "off", "import/no-namespace": "off",
"no-unused-vars": "off", "no-unused-vars": "off",
"@typescript-eslint/no-unused-vars": "error", "@typescript-eslint/no-unused-vars": "error",
"@typescript-eslint/explicit-member-accessibility": [ "@typescript-eslint/explicit-member-accessibility": ["error", {"accessibility": "no-public"}],
"error", "@typescript-eslint/no-require-imports": "error",
{"accessibility": "no-public"} "@typescript-eslint/array-type": "error",
], "@typescript-eslint/await-thenable": "error",
"@typescript-eslint/no-require-imports": "error", "@typescript-eslint/ban-ts-comment": "error",
"@typescript-eslint/array-type": "error", "camelcase": "off",
"@typescript-eslint/await-thenable": "error", "@typescript-eslint/consistent-type-assertions": "error",
"@typescript-eslint/ban-ts-comment": "error", "@typescript-eslint/func-call-spacing": ["error", "never"],
"camelcase": "off", "@typescript-eslint/no-array-constructor": "error",
"@typescript-eslint/consistent-type-assertions": "error", "@typescript-eslint/no-empty-interface": "error",
"@typescript-eslint/func-call-spacing": ["error", "never"], "@typescript-eslint/no-explicit-any": "off",
"@typescript-eslint/no-array-constructor": "error", "@typescript-eslint/no-extraneous-class": "error",
"@typescript-eslint/no-empty-interface": "error", "@typescript-eslint/no-for-in-array": "error",
"@typescript-eslint/no-explicit-any": "off", "@typescript-eslint/no-inferrable-types": "error",
"@typescript-eslint/no-extraneous-class": "error", "@typescript-eslint/no-misused-new": "error",
"@typescript-eslint/no-for-in-array": "error", "@typescript-eslint/no-namespace": "error",
"@typescript-eslint/no-inferrable-types": "error", "@typescript-eslint/no-non-null-assertion": "warn",
"@typescript-eslint/no-misused-new": "error", "@typescript-eslint/no-unnecessary-qualifier": "error",
"@typescript-eslint/no-namespace": "error", "@typescript-eslint/no-unnecessary-type-assertion": "error",
"@typescript-eslint/no-non-null-assertion": "warn", "@typescript-eslint/no-useless-constructor": "error",
"@typescript-eslint/no-unnecessary-qualifier": "error", "@typescript-eslint/no-var-requires": "error",
"@typescript-eslint/no-unnecessary-type-assertion": "error", "@typescript-eslint/prefer-for-of": "warn",
"@typescript-eslint/no-useless-constructor": "error", "@typescript-eslint/prefer-function-type": "warn",
"@typescript-eslint/no-var-requires": "error", "@typescript-eslint/prefer-includes": "error",
"@typescript-eslint/prefer-for-of": "warn", "@typescript-eslint/prefer-string-starts-ends-with": "error",
"@typescript-eslint/prefer-function-type": "warn", "@typescript-eslint/promise-function-async": "error",
"@typescript-eslint/prefer-includes": "error", "@typescript-eslint/require-array-sort-compare": "error",
"@typescript-eslint/prefer-string-starts-ends-with": "error", "@typescript-eslint/restrict-plus-operands": "error",
"@typescript-eslint/promise-function-async": "error", "semi": "off",
"@typescript-eslint/require-array-sort-compare": "error", "@typescript-eslint/type-annotation-spacing": "error",
"@typescript-eslint/restrict-plus-operands": "error", "@typescript-eslint/unbound-method": "off"
"semi": "off", },
"@typescript-eslint/type-annotation-spacing": "error", "env": {
"@typescript-eslint/unbound-method": "off", "node": true,
"no-shadow": "off", "es6": true,
"@typescript-eslint/no-shadow": "error" "jest/globals": true
}, }
"env": { }
"node": true,
"es6": true,
"jest/globals": true
}
}

View File

@@ -8,9 +8,8 @@ jobs:
stale: stale:
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- uses: actions/stale@main - uses: actions/stale@v3
with: with:
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' 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'
days-before-stale: 30 days-before-stale: 30
days-before-close: 5 days-before-close: 5
exempt-issue-labels: 'blocked,must,should,keep'

View File

@@ -11,4 +11,4 @@ allowed:
- unlicense - unlicense
reviewed: reviewed:
npm: npm:

View File

@@ -1,7 +0,0 @@
.idea
.licenses
.vscode
dist
lib
node_modules
package-lock.json

View File

@@ -1,10 +1,11 @@
{ {
"printWidth": 80, "printWidth": 80,
"tabWidth": 2, "tabWidth": 2,
"useTabs": false, "useTabs": false,
"semi": true, "semi": true,
"singleQuote": true, "singleQuote": true,
"trailingComma": "none", "trailingComma": "none",
"bracketSpacing": false, "bracketSpacing": false,
"arrowParens": "avoid" "arrowParens": "avoid",
} "parser": "typescript"
}

143
README.md
View File

@@ -24,36 +24,28 @@ $ npm test
### Arguments ### Arguments
| Input | Description | Usage | | Input | Description | Usage |
| --------------------------- | -------------------------------------------------------------------------------------------- | -------- | | :-------------------------: | :-------------------------------------------------------------------------------: | :------: |
| `repo-token` | PAT(Personal Access Token) for authorizing repository. _Defaults to **${{ github.token }}**_ | Optional | | `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-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-close` | Idle number of days before closing an stale issue/pr. \*Defaults to **7\*** | Optional |
| `days-before-pr-stale` | Idle number of days before marking an pr as stale (override `days-before-stale`). | Optional | | `stale-issue-message` | Message to post on the stale issue. | Optional |
| `days-before-close` | Idle number of days before closing an stale issue/pr. _Defaults to **7**_ | Optional | | `stale-pr-message` | Message to post on the stale pr. | Optional |
| `days-before-issue-close` | Idle number of days before closing an stale issue (override `days-before-close`). | Optional | | `close-issue-message` | Message to post on the stale issue while closing it. | Optional |
| `days-before-pr-close` | Idle number of days before closing an stale pr (override `days-before-close`). | Optional | | `close-pr-message` | Message to post on the stale pr while closing it. | Optional |
| `stale-issue-message` | Message to post on the stale issue. | Optional | | `stale-issue-label` | Label to apply on the stale issue. \*Defaults to **stale\*** | Optional |
| `stale-pr-message` | Message to post on the stale pr. | Optional | | `close-issue-label` | Label to apply on closing issue. | Optional |
| `close-issue-message` | Message to post on the stale issue while closing it. | Optional | | `stale-pr-label` | Label to apply on the stale pr. | Optional |
| `close-pr-message` | Message to post on the stale pr while closing it. | Optional | | `close-pr-label` | Label to apply on the closing pr. | Optional |
| `stale-issue-label` | Label to apply on the stale issue. _Defaults to **stale**_ | Optional | | `exempt-issue-labels` | Labels on an issue exempted from being marked as stale. | Optional |
| `close-issue-label` | Label to apply on closing issue. | Optional | | `exempt-pr-labels` | Labels on the pr exempted from being marked as stale. | Optional |
| `stale-pr-label` | Label to apply on the stale pr. | Optional | | `only-labels` | Only labels checked for stale issue/pr. | Optional |
| `close-pr-label` | Label to apply on the closing pr. | Optional | | `operations-per-run` | Maximum number of operations per run. \*Defaults to **30\*** | Optional |
| `exempt-issue-labels` | Labels on an issue exempted from being marked as stale. | Optional | | `remove-stale-when-updated` | Remove stale label from issue/pr on updates or comments. \*Defaults to **true\*** | Optional |
| `exempt-pr-labels` | Labels on the pr exempted from being marked as stale. | Optional | | `debug-only` | Dry-run on action. \*Defaults to **false\*** | Optional |
| `exempt-milestones` | Milestones on an issue or a pr exempted from being marked as stale. | Optional | | `ascending` | Order to get issues/pr. \*Defaults to **false\*** | Optional |
| `exempt-issue-milestones` | Milestones on an issue exempted from being marked as stale (override `exempt-milestones`). | Optional | | `skip-stale-issue-message` | Skip adding stale message on stale issue. \*Defaults to **false\*** | Optional |
| `exempt-pr-milestones` | Milestones on the pr exempted from being marked as stale (override `exempt-milestones`). | Optional | | `skip-stale-pr-message` | Skip adding stale message on stale pr. \*Defaults to **false\*** | 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 ### Usage
@@ -62,7 +54,7 @@ See [action.yml](./action.yml) For comprehensive list of options.
Basic: Basic:
```yaml ```yaml
name: 'Close stale issues and PRs' name: 'Close stale issues'
on: on:
schedule: schedule:
- cron: '30 1 * * *' - cron: '30 1 * * *'
@@ -80,7 +72,7 @@ jobs:
Configure stale timeouts: Configure stale timeouts:
```yaml ```yaml
name: 'Close stale issues and PRs' name: 'Close stale issues'
on: on:
schedule: schedule:
- cron: '30 1 * * *' - cron: '30 1 * * *'
@@ -91,63 +83,15 @@ jobs:
steps: steps:
- uses: actions/stale@v3 - uses: actions/stale@v3
with: with:
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.' 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'
days-before-stale: 30 days-before-stale: 30
days-before-close: 5 days-before-close: 5
``` ```
Configure different stale timeouts but never close a pr:
```yaml
name: 'Close stale issues and PR'
on:
schedule:
- cron: '30 1 * * *'
jobs:
stale:
runs-on: ubuntu-latest
steps:
- uses: actions/stale@v3
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.'
stale-pr-message: 'This pr is stale because it has been open 45 days with no activity. Remove stale label or comment or this will be closed in 10 days.'
close-issue-message: 'This issue was closed because it has been stalled for 5 days with no activity.'
days-before-stale: 30
days-before-close: 5
days-before-pr-close: -1
```
Configure different stale timeouts:
```yaml
name: 'Close stale issues and PRs'
on:
schedule:
- cron: '30 1 * * *'
jobs:
stale:
runs-on: ubuntu-latest
steps:
- uses: actions/stale@v3
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.'
stale-pr-message: 'This pr is stale because it has been open 45 days with no activity. Remove stale label or comment or this will be closed in 10 days.'
close-issue-message: 'This issue was closed because it has been stalled for 5 days with no activity.'
close-pr-message: 'This pr was closed because it has been stalled for 10 days with no activity.'
days-before-issue-stale: 30
days-before-pr-stale: 45
days-before-issue-close: 5
days-before-pr-close: 10
```
Configure labels: Configure labels:
```yaml ```yaml
name: 'Close stale issues and PRs' name: 'Close stale issues'
on: on:
schedule: schedule:
- cron: '30 1 * * *' - cron: '30 1 * * *'
@@ -167,41 +111,6 @@ jobs:
only-labels: 'awaiting-feedback,awaiting-answers' 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
```
Avoid stale for specific milestones:
```yaml
name: 'Close stale issues and PRs'
on:
schedule:
- cron: '30 1 * * *'
jobs:
stale:
runs-on: ubuntu-latest
steps:
- uses: actions/stale@v3
with:
exempt-issue-milestones: 'future,alpha,beta'
exempt-pr-milestones: 'bugfix,improvement'
```
### Debugging ### 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 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.

File diff suppressed because it is too large Load Diff

View File

@@ -4,110 +4,61 @@ author: 'GitHub'
inputs: inputs:
repo-token: repo-token:
description: 'Token for the repository. Can be passed in using `{{ secrets.GITHUB_TOKEN }}`.' description: 'Token for the repository. Can be passed in using `{{ secrets.GITHUB_TOKEN }}`.'
required: false
default: ${{ github.token }} default: ${{ github.token }}
stale-issue-message: stale-issue-message:
description: 'The message to post on the issue when tagging it. If none provided, will not mark issues stale.' description: 'The message to post on the issue when tagging it. If none provided, will not mark issues stale.'
required: false
stale-pr-message: stale-pr-message:
description: 'The message to post on the pr when tagging it. If none provided, will not mark pull requests stale.' description: 'The message to post on the pr when tagging it. If none provided, will not mark pull requests stale.'
required: false
close-issue-message: close-issue-message:
description: 'The message to post on the issue when closing it. If none provided, will not comment when closing an issue.' description: 'The message to post on the issue when closing it. If none provided, will not comment when closing an issue.'
required: false
close-pr-message: 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.' description: 'The message to post on the pr when closing it. If none provided, will not comment when closing a pull requests.'
required: false
days-before-stale: days-before-stale:
description: 'The number of days old an issue or a pull request 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. Set to -1 to never mark issues or pull requests as stale automatically.'
required: false default: 60
default: '60'
days-before-issue-stale:
description: 'The number of days old an issue can be before marking it stale. Set to -1 to never mark issues as stale automatically. Override "days-before-stale" option regarding only the issues.'
required: false
days-before-pr-stale:
description: 'The number of days old a pull request can be before marking it stale. Set to -1 to never mark pull requests as stale automatically. Override "days-before-stale" option regarding only the pull requests.'
required: false
days-before-close: days-before-close:
description: 'The number of days to wait to close an issue or a pull request after it being marked stale. Set to -1 to never close stale issues or pull requests.' 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.'
required: false default: 7
default: '7'
days-before-issue-close:
description: 'The number of days to wait to close an issue after it being marked stale. Set to -1 to never close stale issues. Override "days-before-close" option regarding only the issues.'
required: false
days-before-pr-close:
description: 'The number of days to wait to close a pull request after it being marked stale. Set to -1 to never close stale pull requests. Override "days-before-close" option regarding only the pull requests.'
required: false
stale-issue-label: stale-issue-label:
description: 'The label to apply when an issue is stale.' description: 'The label to apply when an issue is stale.'
required: false
default: 'Stale' default: 'Stale'
close-issue-label: close-issue-label:
description: 'The label to apply when an issue is closed.' description: 'The label to apply when an issue is closed.'
required: false
exempt-issue-labels: exempt-issue-labels:
description: 'The labels that mean an issue is exempt from being marked stale. Separate multiple labels with commas (eg. "label1,label2")' description: 'The labels that mean an issue is exempt from being marked stale. Separate multiple labels with commas (eg. "label1,label2")'
default: '' default: ''
required: false
stale-pr-label: stale-pr-label:
description: 'The label to apply when a pull request is stale.' description: 'The label to apply when a pull request is stale.'
default: 'Stale' default: 'Stale'
required: false
close-pr-label: close-pr-label:
description: 'The label to apply when a pull request is closed.' description: 'The label to apply when a pull request is closed.'
required: false
exempt-pr-labels: exempt-pr-labels:
description: 'The labels that mean a pull request is exempt from being marked stale. Separate multiple labels with commas (eg. "label1,label2")' description: 'The labels that mean a pull request is exempt from being marked stale. Separate multiple labels with commas (eg. "label1,label2")'
default: '' default: ''
required: false
exempt-milestones:
description: 'The milestones that mean an issue or a pr is exempt from being marked stale. Separate multiple milestones with commas (eg. "milestone1,milestone2")'
default: ''
required: false
exempt-issue-milestones:
description: 'The milestones that mean an issue is exempt from being marked stale. Separate multiple milestones with commas (eg. "milestone1,milestone2"). Override "exempt-milestones" option regarding only the issue.'
default: ''
required: false
exempt-pr-milestones:
description: 'The milestones that mean a pull request is exempt from being marked stale. Separate multiple milestones with commas (eg. "milestone1,milestone2"). Override "exempt-milestones" option regarding only the pull requests.'
default: ''
required: false
only-labels: only-labels:
description: 'Only issues or pull requests with all of these labels are checked if stale. Defaults to `[]` (disabled) and can be a comma-separated list of labels.' description: 'Only issues or pull requests with all of these labels are checked if stale. Defaults to `[]` (disabled) and can be a comma-separated list of labels.'
default: '' default: ''
required: false
operations-per-run: operations-per-run:
description: 'The maximum number of operations per run, used to control rate limiting (GitHub API CRUD related).' description: 'The maximum number of operations per run, used to control rate limiting.'
default: '30' default: 30
required: false
remove-stale-when-updated: remove-stale-when-updated:
description: 'Remove stale labels from issues when they are updated or commented on.' description: 'Remove stale labels from issues when they are updated or commented on.'
default: 'true' default: true
required: false
debug-only: debug-only:
description: 'Run the processor in debug mode without actually performing any operations on live issues.' description: 'Run the processor in debug mode without actually performing any operations on live issues.'
default: 'false' default: false
required: false
ascending: ascending:
description: 'The order to get issues or pull requests. Defaults to false, which is descending' description: 'The order to get issues or pull requests. Defaults to false, which is descending'
default: 'false' default: false
required: false
skip-stale-pr-message: skip-stale-pr-message:
description: 'Skip adding stale message when marking a pull request as stale.' description: 'Skip adding stale message when marking a pull request as stale.'
default: 'false' default: false
required: false
skip-stale-issue-message: skip-stale-issue-message:
description: 'Skip adding stale message when marking an issue as stale.' description: 'Skip adding stale message when marking an issue as stale.'
default: 'false' default: false
required: false
delete-branch: delete-branch:
description: 'Delete the git branch after closing a stale pull request.' description: 'Delete the git branch after closing a stale pull request.'
default: 'false' 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: runs:
using: 'node12' using: 'node12'
main: 'dist/index.js' main: 'dist/index.js'

897
dist/index.js vendored

File diff suppressed because it is too large Load Diff

538
package-lock.json generated
View File

@@ -418,9 +418,9 @@
} }
}, },
"@eslint/eslintrc": { "@eslint/eslintrc": {
"version": "0.3.0", "version": "0.2.2",
"resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-0.3.0.tgz", "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-0.2.2.tgz",
"integrity": "sha512-1JTKgrOKAHVivSvOYw+sJOunkBjUOvjqWk1DPja7ZFhIS2mX/4EgTT8M7eTK9jrKhL/FvXXEbQwIs3pg1xp3dg==", "integrity": "sha512-EfB5OHNYp1F4px/LI/FEnGylop7nOqkQ1LRzCM0KccA2U8tvV8w01KBv37LbO7nW4H+YhKyo2LcJhRwjjV17QQ==",
"dev": true, "dev": true,
"requires": { "requires": {
"ajv": "^6.12.4", "ajv": "^6.12.4",
@@ -430,7 +430,7 @@
"ignore": "^4.0.6", "ignore": "^4.0.6",
"import-fresh": "^3.2.1", "import-fresh": "^3.2.1",
"js-yaml": "^3.13.1", "js-yaml": "^3.13.1",
"lodash": "^4.17.20", "lodash": "^4.17.19",
"minimatch": "^3.0.4", "minimatch": "^3.0.4",
"strip-json-comments": "^3.1.1" "strip-json-comments": "^3.1.1"
}, },
@@ -447,15 +447,6 @@
"uri-js": "^4.2.2" "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": { "globals": {
"version": "12.4.0", "version": "12.4.0",
"resolved": "https://registry.npmjs.org/globals/-/globals-12.4.0.tgz", "resolved": "https://registry.npmjs.org/globals/-/globals-12.4.0.tgz",
@@ -464,22 +455,6 @@
"requires": { "requires": {
"type-fest": "^0.8.1" "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
} }
} }
}, },
@@ -496,15 +471,6 @@
"resolve-from": "^5.0.0" "resolve-from": "^5.0.0"
}, },
"dependencies": { "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": { "find-up": {
"version": "4.1.0", "version": "4.1.0",
"resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz",
@@ -515,16 +481,6 @@
"path-exists": "^4.0.0" "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": { "locate-path": {
"version": "5.0.0", "version": "5.0.0",
"resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz",
@@ -1567,11 +1523,6 @@
"universal-user-agent": "^6.0.0" "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": { "@octokit/plugin-paginate-rest": {
"version": "2.2.4", "version": "2.2.4",
"resolved": "https://registry.npmjs.org/@octokit/plugin-paginate-rest/-/plugin-paginate-rest-2.2.4.tgz", "resolved": "https://registry.npmjs.org/@octokit/plugin-paginate-rest/-/plugin-paginate-rest-2.2.4.tgz",
@@ -1620,92 +1571,32 @@
} }
}, },
"@octokit/rest": { "@octokit/rest": {
"version": "18.0.12", "version": "18.0.9",
"resolved": "https://registry.npmjs.org/@octokit/rest/-/rest-18.0.12.tgz", "resolved": "https://registry.npmjs.org/@octokit/rest/-/rest-18.0.9.tgz",
"integrity": "sha512-hNRCZfKPpeaIjOVuNJzkEL6zacfZlBPV8vw8ReNeyUkVvbuCvvrrx8K8Gw2eyHHsmd4dPlAxIXIZ9oHhJfkJpw==", "integrity": "sha512-CC5+cIx974Ygx9lQNfUn7/oXDQ9kqGiKUC6j1A9bAVZZ7aoTF8K6yxu0pQhQrLBwSl92J6Z3iVDhGhGFgISCZg==",
"requires": { "requires": {
"@octokit/core": "^3.2.3", "@octokit/core": "^3.0.0",
"@octokit/plugin-paginate-rest": "^2.6.2", "@octokit/plugin-paginate-rest": "^2.2.0",
"@octokit/plugin-request-log": "^1.0.2", "@octokit/plugin-request-log": "^1.0.0",
"@octokit/plugin-rest-endpoint-methods": "4.4.1" "@octokit/plugin-rest-endpoint-methods": "4.2.1"
}, },
"dependencies": { "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": { "@octokit/plugin-rest-endpoint-methods": {
"version": "4.4.1", "version": "4.2.1",
"resolved": "https://registry.npmjs.org/@octokit/plugin-rest-endpoint-methods/-/plugin-rest-endpoint-methods-4.4.1.tgz", "resolved": "https://registry.npmjs.org/@octokit/plugin-rest-endpoint-methods/-/plugin-rest-endpoint-methods-4.2.1.tgz",
"integrity": "sha512-+v5PcvrUcDeFXf8hv1gnNvNLdm4C0+2EiuWt9EatjjUmfriM1pTMM+r4j1lLHxeBQ9bVDmbywb11e3KjuavieA==", "integrity": "sha512-QyFr4Bv807Pt1DXZOC5a7L5aFdrwz71UHTYoHVajYV5hsqffWm8FUl9+O7nxRu5PDMtB/IKrhFqTmdBTK5cx+A==",
"requires": { "requires": {
"@octokit/types": "^6.1.0", "@octokit/types": "^5.5.0",
"deprecation": "^2.3.1" "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": { "@octokit/types": {
"version": "6.3.1", "version": "5.5.0",
"resolved": "https://registry.npmjs.org/@octokit/types/-/types-6.3.1.tgz", "resolved": "https://registry.npmjs.org/@octokit/types/-/types-5.5.0.tgz",
"integrity": "sha512-SyOaprLWVPS6QhbZY8hF9Oydx/UUnslKq1NyNUr4CN42UEPC3+9AvrYrDm4UvaU1D5u/vVMuSZOicFqOielRXQ==", "integrity": "sha512-UZ1pErDue6bZNjYOotCNveTXArOMZQFG6hKJfOnGnulVCMcVVi7YIIuuR4WfBhjo7zgpmzn/BkPDnUXtNx+PcQ==",
"requires": { "requires": {
"@octokit/openapi-types": "^2.3.0",
"@types/node": ">= 8" "@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=="
} }
} }
}, },
@@ -1810,13 +1701,143 @@
} }
}, },
"@types/jest": { "@types/jest": {
"version": "26.0.20", "version": "26.0.15",
"resolved": "https://registry.npmjs.org/@types/jest/-/jest-26.0.20.tgz", "resolved": "https://registry.npmjs.org/@types/jest/-/jest-26.0.15.tgz",
"integrity": "sha512-9zi2Y+5USJRxd0FsahERhBwlcvFh6D2GLQnY2FH2BzK8J9s9omvNHIbvABwIluXa0fD8XVKMLTO0aOEuUfACAA==", "integrity": "sha512-s2VMReFXRg9XXxV+CW9e5Nz8fH2K1aEhwgjUqPPbQd7g95T0laAcvLv032EhFHIa5GHsZ8W7iJEQVaJq6k3Gog==",
"dev": true, "dev": true,
"requires": { "requires": {
"jest-diff": "^26.0.0", "jest-diff": "^26.0.0",
"pretty-format": "^26.0.0" "pretty-format": "^26.0.0"
},
"dependencies": {
"@jest/types": {
"version": "26.6.2",
"resolved": "https://registry.npmjs.org/@jest/types/-/types-26.6.2.tgz",
"integrity": "sha512-fC6QCp7Sc5sX6g8Tvbmj4XUTbyrik0akgRy03yjXbQaBWWNWGE7SGtJk98m0N8nzegD/7SggrUlivxo5ax4KWQ==",
"dev": true,
"requires": {
"@types/istanbul-lib-coverage": "^2.0.0",
"@types/istanbul-reports": "^3.0.0",
"@types/node": "*",
"@types/yargs": "^15.0.0",
"chalk": "^4.0.0"
}
},
"@types/istanbul-reports": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/@types/istanbul-reports/-/istanbul-reports-3.0.0.tgz",
"integrity": "sha512-nwKNbvnwJ2/mndE9ItP/zc2TCzw6uuodnF4EHYWD+gCQDVBuRQL5UzbZD0/ezy1iKsFU2ZQiDqg4M9dN4+wZgA==",
"dev": true,
"requires": {
"@types/istanbul-lib-report": "*"
}
},
"@types/yargs": {
"version": "15.0.10",
"resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-15.0.10.tgz",
"integrity": "sha512-z8PNtlhrj7eJNLmrAivM7rjBESG6JwC5xP3RVk12i/8HVP7Xnx/sEmERnRImyEuUaJfO942X0qMOYsoupaJbZQ==",
"dev": true,
"requires": {
"@types/yargs-parser": "*"
}
},
"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",
"integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
"dev": true,
"requires": {
"color-convert": "^2.0.1"
}
},
"chalk": {
"version": "4.1.0",
"resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.0.tgz",
"integrity": "sha512-qwx12AxXe2Q5xQ43Ac//I6v5aXTipYrSESdOgzrN+9XjgEpyjpKuvSGaN4qE93f7TQTlerQQ8S+EQ0EyDoVL1A==",
"dev": true,
"requires": {
"ansi-styles": "^4.1.0",
"supports-color": "^7.1.0"
}
},
"color-convert": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
"integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
"dev": true,
"requires": {
"color-name": "~1.1.4"
}
},
"color-name": {
"version": "1.1.4",
"resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
"integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
"dev": true
},
"diff-sequences": {
"version": "26.6.2",
"resolved": "https://registry.npmjs.org/diff-sequences/-/diff-sequences-26.6.2.tgz",
"integrity": "sha512-Mv/TDa3nZ9sbc5soK+OoA74BsS3mL37yixCvUAQkiuA4Wz6YtwP/K47n2rv2ovzHZvoiQeA5FTQOschKkEwB0Q==",
"dev": true
},
"has-flag": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz",
"integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==",
"dev": true
},
"jest-diff": {
"version": "26.6.2",
"resolved": "https://registry.npmjs.org/jest-diff/-/jest-diff-26.6.2.tgz",
"integrity": "sha512-6m+9Z3Gv9wN0WFVasqjCL/06+EFCMTqDEUl/b87HYK2rAPTyfz4ZIuSlPhY51PIQRWx5TaxeF1qmXKe9gfN3sA==",
"dev": true,
"requires": {
"chalk": "^4.0.0",
"diff-sequences": "^26.6.2",
"jest-get-type": "^26.3.0",
"pretty-format": "^26.6.2"
}
},
"jest-get-type": {
"version": "26.3.0",
"resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-26.3.0.tgz",
"integrity": "sha512-TpfaviN1R2pQWkIihlfEanwOXK0zcxrKEE4MlU6Tn7keoXdN6/3gK/xl0yEh8DOunn5pOVGKf8hB4R9gVh04ig==",
"dev": true
},
"pretty-format": {
"version": "26.6.2",
"resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-26.6.2.tgz",
"integrity": "sha512-7AeGuCYNGmycyQbCqd/3PWH4eOoX/OiCa0uphp57NVTeAGdJGaAliecxwBDHYQCIvrW7aDBZCYeNTP/WX69mkg==",
"dev": true,
"requires": {
"@jest/types": "^26.6.2",
"ansi-regex": "^5.0.0",
"ansi-styles": "^4.0.0",
"react-is": "^17.0.1"
}
},
"react-is": {
"version": "17.0.1",
"resolved": "https://registry.npmjs.org/react-is/-/react-is-17.0.1.tgz",
"integrity": "sha512-NAnt2iGDXohE5LI7uBnLnqvLQMtzhkiAOLXTmv+qnF9Ky7xAPcX8Up/xWIhxvLVGJvuLiNc4xQLtuqDRzb4fSA==",
"dev": true
},
"supports-color": {
"version": "7.2.0",
"resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz",
"integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==",
"dev": true,
"requires": {
"has-flag": "^4.0.0"
}
}
} }
}, },
"@types/json-schema": { "@types/json-schema": {
@@ -1847,9 +1868,9 @@
} }
}, },
"@types/node": { "@types/node": {
"version": "14.14.21", "version": "14.10.0",
"resolved": "https://registry.npmjs.org/@types/node/-/node-14.14.21.tgz", "resolved": "https://registry.npmjs.org/@types/node/-/node-14.10.0.tgz",
"integrity": "sha512-cHYfKsnwllYhjOzuC5q1VpguABBeecUp24yFluHpn/BQaVxB1CuQ1FSRZCzrPxrkIfWISXV2LbeoBthLWg0+0A==" "integrity": "sha512-SOIyrdADB4cq6eY1F+9iU48iIomFAPltu11LCvA9PKcyEwHadjCFzNVPotAR+oEJA0bCP4Xvvgy+vwu1ZjVh8g=="
}, },
"@types/normalize-package-data": { "@types/normalize-package-data": {
"version": "2.4.0", "version": "2.4.0",
@@ -1891,60 +1912,53 @@
"dev": true "dev": true
}, },
"@typescript-eslint/eslint-plugin": { "@typescript-eslint/eslint-plugin": {
"version": "4.14.0", "version": "3.7.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-4.14.0.tgz", "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-3.7.0.tgz",
"integrity": "sha512-IJ5e2W7uFNfg4qh9eHkHRUCbgZ8VKtGwD07kannJvM5t/GU8P8+24NX8gi3Hf5jST5oWPY8kyV1s/WtfiZ4+Ww==", "integrity": "sha512-4OEcPON3QIx0ntsuiuFP/TkldmBGXf0uKxPQlGtS/W2F3ndYm8Vgdpj/woPJkzUc65gd3iR+qi3K8SDQP/obFg==",
"dev": true, "dev": true,
"requires": { "requires": {
"@typescript-eslint/experimental-utils": "4.14.0", "@typescript-eslint/experimental-utils": "3.7.0",
"@typescript-eslint/scope-manager": "4.14.0",
"debug": "^4.1.1", "debug": "^4.1.1",
"functional-red-black-tree": "^1.0.1", "functional-red-black-tree": "^1.0.1",
"lodash": "^4.17.15",
"regexpp": "^3.0.0", "regexpp": "^3.0.0",
"semver": "^7.3.2", "semver": "^7.3.2",
"tsutils": "^3.17.1" "tsutils": "^3.17.1"
}, },
"dependencies": { "dependencies": {
"@typescript-eslint/experimental-utils": { "@typescript-eslint/experimental-utils": {
"version": "4.14.0", "version": "3.7.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/experimental-utils/-/experimental-utils-4.14.0.tgz", "resolved": "https://registry.npmjs.org/@typescript-eslint/experimental-utils/-/experimental-utils-3.7.0.tgz",
"integrity": "sha512-6i6eAoiPlXMKRbXzvoQD5Yn9L7k9ezzGRvzC/x1V3650rUk3c3AOjQyGYyF9BDxQQDK2ElmKOZRD0CbtdkMzQQ==", "integrity": "sha512-xpfXXAfZqhhqs5RPQBfAFrWDHoNxD5+sVB5A46TF58Bq1hRfVROrWHcQHHUM9aCBdy9+cwATcvCbRg8aIRbaHQ==",
"dev": true, "dev": true,
"requires": { "requires": {
"@types/json-schema": "^7.0.3", "@types/json-schema": "^7.0.3",
"@typescript-eslint/scope-manager": "4.14.0", "@typescript-eslint/types": "3.7.0",
"@typescript-eslint/types": "4.14.0", "@typescript-eslint/typescript-estree": "3.7.0",
"@typescript-eslint/typescript-estree": "4.14.0",
"eslint-scope": "^5.0.0", "eslint-scope": "^5.0.0",
"eslint-utils": "^2.0.0" "eslint-utils": "^2.0.0"
} }
}, },
"@typescript-eslint/scope-manager": {
"version": "4.14.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-4.14.0.tgz",
"integrity": "sha512-/J+LlRMdbPh4RdL4hfP1eCwHN5bAhFAGOTsvE6SxsrM/47XQiPSgF5MDgLyp/i9kbZV9Lx80DW0OpPkzL+uf8Q==",
"dev": true,
"requires": {
"@typescript-eslint/types": "4.14.0",
"@typescript-eslint/visitor-keys": "4.14.0"
}
},
"@typescript-eslint/typescript-estree": { "@typescript-eslint/typescript-estree": {
"version": "4.14.0", "version": "3.7.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-4.14.0.tgz", "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-3.7.0.tgz",
"integrity": "sha512-wRjZ5qLao+bvS2F7pX4qi2oLcOONIB+ru8RGBieDptq/SudYwshveORwCVU4/yMAd4GK7Fsf8Uq1tjV838erag==", "integrity": "sha512-xr5oobkYRebejlACGr1TJ0Z/r0a2/HUf0SXqPvlgUMwiMqOCu/J+/Dr9U3T0IxpE5oLFSkqMx1FE/dKaZ8KsOQ==",
"dev": true, "dev": true,
"requires": { "requires": {
"@typescript-eslint/types": "4.14.0", "@typescript-eslint/types": "3.7.0",
"@typescript-eslint/visitor-keys": "4.14.0", "@typescript-eslint/visitor-keys": "3.7.0",
"debug": "^4.1.1", "debug": "^4.1.1",
"globby": "^11.0.1", "glob": "^7.1.6",
"is-glob": "^4.0.1", "is-glob": "^4.0.1",
"lodash": "^4.17.15", "lodash": "^4.17.15",
"semver": "^7.3.2", "semver": "^7.3.2",
"tsutils": "^3.17.1" "tsutils": "^3.17.1"
} }
},
"regexpp": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/regexpp/-/regexpp-3.1.0.tgz",
"integrity": "sha512-ZOIzd8yVsQQA7j8GCSlPGXwg5PfmA1mrq0JP4nGhh54LaKN3xdai/vHUDu74pKwV8OxseMS65u2NImosQcSD0Q==",
"dev": true
} }
} }
}, },
@@ -1971,41 +1985,41 @@
} }
}, },
"@typescript-eslint/parser": { "@typescript-eslint/parser": {
"version": "4.14.0", "version": "4.8.1",
"resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-4.14.0.tgz", "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-4.8.1.tgz",
"integrity": "sha512-sUDeuCjBU+ZF3Lzw0hphTyScmDDJ5QVkyE21pRoBo8iDl7WBtVFS+WDN3blY1CH3SBt7EmYCw6wfmJjF0l/uYg==", "integrity": "sha512-QND8XSVetATHK9y2Ltc/XBl5Ro7Y62YuZKnPEwnNPB8E379fDsvzJ1dMJ46fg/VOmk0hXhatc+GXs5MaXuL5Uw==",
"dev": true, "dev": true,
"requires": { "requires": {
"@typescript-eslint/scope-manager": "4.14.0", "@typescript-eslint/scope-manager": "4.8.1",
"@typescript-eslint/types": "4.14.0", "@typescript-eslint/types": "4.8.1",
"@typescript-eslint/typescript-estree": "4.14.0", "@typescript-eslint/typescript-estree": "4.8.1",
"debug": "^4.1.1" "debug": "^4.1.1"
}, },
"dependencies": { "dependencies": {
"@typescript-eslint/scope-manager": { "@typescript-eslint/scope-manager": {
"version": "4.14.0", "version": "4.8.1",
"resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-4.14.0.tgz", "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-4.8.1.tgz",
"integrity": "sha512-/J+LlRMdbPh4RdL4hfP1eCwHN5bAhFAGOTsvE6SxsrM/47XQiPSgF5MDgLyp/i9kbZV9Lx80DW0OpPkzL+uf8Q==", "integrity": "sha512-r0iUOc41KFFbZdPAdCS4K1mXivnSZqXS5D9oW+iykQsRlTbQRfuFRSW20xKDdYiaCoH+SkSLeIF484g3kWzwOQ==",
"dev": true, "dev": true,
"requires": { "requires": {
"@typescript-eslint/types": "4.14.0", "@typescript-eslint/types": "4.8.1",
"@typescript-eslint/visitor-keys": "4.14.0" "@typescript-eslint/visitor-keys": "4.8.1"
} }
}, },
"@typescript-eslint/types": { "@typescript-eslint/types": {
"version": "4.14.0", "version": "4.8.1",
"resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-4.14.0.tgz", "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-4.8.1.tgz",
"integrity": "sha512-VsQE4VvpldHrTFuVPY1ZnHn/Txw6cZGjL48e+iBxTi2ksa9DmebKjAeFmTVAYoSkTk7gjA7UqJ7pIsyifTsI4A==", "integrity": "sha512-ave2a18x2Y25q5K05K/U3JQIe2Av4+TNi/2YuzyaXLAsDx6UZkz1boZ7nR/N6Wwae2PpudTZmHFXqu7faXfHmA==",
"dev": true "dev": true
}, },
"@typescript-eslint/typescript-estree": { "@typescript-eslint/typescript-estree": {
"version": "4.14.0", "version": "4.8.1",
"resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-4.14.0.tgz", "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-4.8.1.tgz",
"integrity": "sha512-wRjZ5qLao+bvS2F7pX4qi2oLcOONIB+ru8RGBieDptq/SudYwshveORwCVU4/yMAd4GK7Fsf8Uq1tjV838erag==", "integrity": "sha512-bJ6Fn/6tW2g7WIkCWh3QRlaSU7CdUUK52shx36/J7T5oTQzANvi6raoTsbwGM11+7eBbeem8hCCKbyvAc0X3sQ==",
"dev": true, "dev": true,
"requires": { "requires": {
"@typescript-eslint/types": "4.14.0", "@typescript-eslint/types": "4.8.1",
"@typescript-eslint/visitor-keys": "4.14.0", "@typescript-eslint/visitor-keys": "4.8.1",
"debug": "^4.1.1", "debug": "^4.1.1",
"globby": "^11.0.1", "globby": "^11.0.1",
"is-glob": "^4.0.1", "is-glob": "^4.0.1",
@@ -2015,12 +2029,12 @@
} }
}, },
"@typescript-eslint/visitor-keys": { "@typescript-eslint/visitor-keys": {
"version": "4.14.0", "version": "4.8.1",
"resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-4.14.0.tgz", "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-4.8.1.tgz",
"integrity": "sha512-MeHHzUyRI50DuiPgV9+LxcM52FCJFYjJiWHtXlbyC27b80mfOwKeiKI+MHOTEpcpfmoPFm/vvQS88bYIx6PZTA==", "integrity": "sha512-3nrwXFdEYALQh/zW8rFwP4QltqsanCDz4CwWMPiIZmwlk9GlvBeueEIbq05SEq4ganqM0g9nh02xXgv5XI3PeQ==",
"dev": true, "dev": true,
"requires": { "requires": {
"@typescript-eslint/types": "4.14.0", "@typescript-eslint/types": "4.8.1",
"eslint-visitor-keys": "^2.0.0" "eslint-visitor-keys": "^2.0.0"
} }
}, },
@@ -2067,9 +2081,9 @@
} }
}, },
"@typescript-eslint/types": { "@typescript-eslint/types": {
"version": "4.14.0", "version": "3.7.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-4.14.0.tgz", "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-3.7.0.tgz",
"integrity": "sha512-VsQE4VvpldHrTFuVPY1ZnHn/Txw6cZGjL48e+iBxTi2ksa9DmebKjAeFmTVAYoSkTk7gjA7UqJ7pIsyifTsI4A==", "integrity": "sha512-reCaK+hyKkKF+itoylAnLzFeNYAEktB0XVfSQvf0gcVgpz1l49Lt6Vo9x4MVCCxiDydA0iLAjTF/ODH0pbfnpg==",
"dev": true "dev": true
}, },
"@typescript-eslint/typescript-estree": { "@typescript-eslint/typescript-estree": {
@@ -2113,21 +2127,12 @@
} }
}, },
"@typescript-eslint/visitor-keys": { "@typescript-eslint/visitor-keys": {
"version": "4.14.0", "version": "3.7.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-4.14.0.tgz", "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-3.7.0.tgz",
"integrity": "sha512-MeHHzUyRI50DuiPgV9+LxcM52FCJFYjJiWHtXlbyC27b80mfOwKeiKI+MHOTEpcpfmoPFm/vvQS88bYIx6PZTA==", "integrity": "sha512-k5PiZdB4vklUpUX4NBncn5RBKty8G3ihTY+hqJsCdMuD0v4jofI5xuqwnVcWxfv6iTm2P/dfEa2wMUnsUY8ODw==",
"dev": true, "dev": true,
"requires": { "requires": {
"@typescript-eslint/types": "4.14.0", "eslint-visitor-keys": "^1.1.0"
"eslint-visitor-keys": "^2.0.0"
},
"dependencies": {
"eslint-visitor-keys": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-2.0.0.tgz",
"integrity": "sha512-QudtT6av5WXels9WjIM7qz1XD1cWGvX4gGXvp/zBn9nXG02D0utdU3Em2m/QjTnrsk6bBjmCygl3rmj118msQQ==",
"dev": true
}
} }
}, },
"@vercel/ncc": { "@vercel/ncc": {
@@ -2231,10 +2236,13 @@
} }
}, },
"argparse": { "argparse": {
"version": "2.0.1", "version": "1.0.10",
"resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz",
"integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==",
"dev": true "dev": true,
"requires": {
"sprintf-js": "~1.0.2"
}
}, },
"arr-diff": { "arr-diff": {
"version": "4.0.0", "version": "4.0.0",
@@ -2308,12 +2316,6 @@
"integrity": "sha1-WWZ/QfrdTyDMvCu5a41Pf3jsA2c=", "integrity": "sha1-WWZ/QfrdTyDMvCu5a41Pf3jsA2c=",
"dev": true "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": { "asynckit": {
"version": "0.4.0", "version": "0.4.0",
"resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz",
@@ -3161,13 +3163,13 @@
} }
}, },
"eslint": { "eslint": {
"version": "7.18.0", "version": "7.17.0",
"resolved": "https://registry.npmjs.org/eslint/-/eslint-7.18.0.tgz", "resolved": "https://registry.npmjs.org/eslint/-/eslint-7.17.0.tgz",
"integrity": "sha512-fbgTiE8BfUJZuBeq2Yi7J3RB3WGUQ9PNuNbmgi6jt9Iv8qrkxfy19Ds3OpL1Pm7zg3BtTVhvcUZbIRQ0wmSjAQ==", "integrity": "sha512-zJk08MiBgwuGoxes5sSQhOtibZ75pz0J35XTRlZOk9xMffhpA9BTbQZxoXZzOl5zMbleShbGwtw+1kGferfFwQ==",
"dev": true, "dev": true,
"requires": { "requires": {
"@babel/code-frame": "^7.0.0", "@babel/code-frame": "^7.0.0",
"@eslint/eslintrc": "^0.3.0", "@eslint/eslintrc": "^0.2.2",
"ajv": "^6.10.0", "ajv": "^6.10.0",
"chalk": "^4.0.0", "chalk": "^4.0.0",
"cross-spawn": "^7.0.2", "cross-spawn": "^7.0.2",
@@ -3191,7 +3193,7 @@
"js-yaml": "^3.13.1", "js-yaml": "^3.13.1",
"json-stable-stringify-without-jsonify": "^1.0.1", "json-stable-stringify-without-jsonify": "^1.0.1",
"levn": "^0.4.1", "levn": "^0.4.1",
"lodash": "^4.17.20", "lodash": "^4.17.19",
"minimatch": "^3.0.4", "minimatch": "^3.0.4",
"natural-compare": "^1.4.0", "natural-compare": "^1.4.0",
"optionator": "^0.9.1", "optionator": "^0.9.1",
@@ -3205,6 +3207,12 @@
"v8-compile-cache": "^2.0.3" "v8-compile-cache": "^2.0.3"
}, },
"dependencies": { "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": { "ansi-styles": {
"version": "4.3.0", "version": "4.3.0",
"resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
@@ -3214,15 +3222,6 @@
"color-convert": "^2.0.1" "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": { "chalk": {
"version": "4.1.0", "version": "4.1.0",
"resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.0.tgz", "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.0.tgz",
@@ -3324,16 +3323,6 @@
"integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==",
"dev": true "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": { "levn": {
"version": "0.4.1", "version": "0.4.1",
"resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz",
@@ -3344,12 +3333,6 @@
"type-check": "~0.4.0" "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": { "optionator": {
"version": "0.9.1", "version": "0.9.1",
"resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.1.tgz", "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.1.tgz",
@@ -3391,6 +3374,15 @@
"integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==",
"dev": true "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": { "supports-color": {
"version": "7.2.0", "version": "7.2.0",
"resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz",
@@ -3645,6 +3637,12 @@
"eslint-visitor-keys": "^1.3.0" "eslint-visitor-keys": "^1.3.0"
}, },
"dependencies": { "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": { "eslint-visitor-keys": {
"version": "1.3.0", "version": "1.3.0",
"resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-1.3.0.tgz", "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-1.3.0.tgz",
@@ -4077,6 +4075,17 @@
"requires": { "requires": {
"flatted": "^3.1.0", "flatted": "^3.1.0",
"rimraf": "^3.0.2" "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": { "flatted": {
@@ -4618,12 +4627,6 @@
"integrity": "sha1-qIwCU1eR8C7TfHahueqXc8gz+MI=", "integrity": "sha1-qIwCU1eR8C7TfHahueqXc8gz+MI=",
"dev": true "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": { "is-generator-fn": {
"version": "2.1.0", "version": "2.1.0",
"resolved": "https://registry.npmjs.org/is-generator-fn/-/is-generator-fn-2.1.0.tgz", "resolved": "https://registry.npmjs.org/is-generator-fn/-/is-generator-fn-2.1.0.tgz",
@@ -7547,12 +7550,13 @@
"dev": true "dev": true
}, },
"js-yaml": { "js-yaml": {
"version": "4.0.0", "version": "3.14.0",
"resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.0.0.tgz", "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.0.tgz",
"integrity": "sha512-pqon0s+4ScYUvX30wxQi3PogGFAlUyH0awepWvwkj4jD4v+ova3RiYw8bmA6x2rDrEaj8i/oWKoRxpVNW+Re8Q==", "integrity": "sha512-/4IbIeHcD9VMHFqDR/gQ7EdZdLimOvW2DdcxFjdyyZ9NsbS+ccrXqVWDtab/lRl5AlUqmpBx8EhPaWR+OtY17A==",
"dev": true, "dev": true,
"requires": { "requires": {
"argparse": "^2.0.1" "argparse": "^1.0.7",
"esprima": "^4.0.0"
} }
}, },
"jsbn": { "jsbn": {
@@ -7744,14 +7748,6 @@
"integrity": "sha1-7dFMgk4sycHgsKG0K7UhBRakJDg=", "integrity": "sha1-7dFMgk4sycHgsKG0K7UhBRakJDg=",
"dev": true "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": { "make-dir": {
"version": "3.1.0", "version": "3.1.0",
"resolved": "https://registry.npmjs.org/make-dir/-/make-dir-3.1.0.tgz", "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-3.1.0.tgz",
@@ -8720,12 +8716,9 @@
} }
}, },
"semver": { "semver": {
"version": "7.3.4", "version": "7.3.2",
"resolved": "https://registry.npmjs.org/semver/-/semver-7.3.4.tgz", "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.2.tgz",
"integrity": "sha512-tCfb2WLjqFAtXn4KEdxIhalnRtoKFN7nAwj0B3ZXCbQloV2tq5eDbcTmT68JJD3nRJq24/XgxtQKFIpQdtvmVw==", "integrity": "sha512-OrOb32TeeambH6UrhtShmF7CRDqhL6/5XpPNp2DuRH6+9QLw/orhp72j87v8Qa1ScDkvrrBNpZcDejAirJmfXQ=="
"requires": {
"lru-cache": "^6.0.0"
}
}, },
"set-blocking": { "set-blocking": {
"version": "2.0.0", "version": "2.0.0",
@@ -8831,6 +8824,12 @@
"color-convert": "^2.0.1" "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": { "color-convert": {
"version": "2.0.1", "version": "2.0.1",
"resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
@@ -8845,6 +8844,12 @@
"resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
"integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
"dev": true "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
} }
} }
}, },
@@ -9859,11 +9864,6 @@
"integrity": "sha512-r9S/ZyXu/Xu9q1tYlpsLIsa3EeLXXk0VwlxqTcFRfg9EhMW+17kbt9G0NrgCmhGb5vT2hyhJZLfDGx+7+5Uj/w==", "integrity": "sha512-r9S/ZyXu/Xu9q1tYlpsLIsa3EeLXXk0VwlxqTcFRfg9EhMW+17kbt9G0NrgCmhGb5vT2hyhJZLfDGx+7+5Uj/w==",
"dev": true "dev": true
}, },
"yallist": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz",
"integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A=="
},
"yargs": { "yargs": {
"version": "15.4.1", "version": "15.4.1",
"resolved": "https://registry.npmjs.org/yargs/-/yargs-15.4.1.tgz", "resolved": "https://registry.npmjs.org/yargs/-/yargs-15.4.1.tgz",

View File

@@ -6,12 +6,10 @@
"main": "lib/main.js", "main": "lib/main.js",
"scripts": { "scripts": {
"build": "tsc", "build": "tsc",
"format": "prettier --write --ignore-unknown **/*.{md,json,yml,ts}", "format": "prettier --write **/*.ts",
"format-check": "prettier --check --ignore-unknown **/*.{md,json,yml,ts}", "format-check": "prettier --check **/*.ts",
"lint": "eslint src/**/*.ts", "lint": "eslint src/**/*.ts",
"lint:fix": "eslint src/**/*.ts --fix", "lint:fix": "eslint src/**/*.ts --fix",
"lint:all": "npm run format-check && npm run lint",
"lint:all:fix": "npm run format && npm run lint:fix",
"pack": "ncc build", "pack": "ncc build",
"test": "jest", "test": "jest",
"all": "npm run build && npm run format && npm run lint && npm run pack && npm test" "all": "npm run build && npm run format && npm run lint && npm run pack && npm test"
@@ -30,24 +28,23 @@
"dependencies": { "dependencies": {
"@actions/core": "^1.2.6", "@actions/core": "^1.2.6",
"@actions/github": "^4.0.0", "@actions/github": "^4.0.0",
"@octokit/rest": "^18.0.12", "@octokit/rest": "^18.0.9",
"lodash.deburr": "^4.1.0", "lodash.deburr": "^4.1.0",
"semver": "^7.3.4" "semver": "^7.3.2"
}, },
"devDependencies": { "devDependencies": {
"@types/jest": "^26.0.20", "@types/jest": "^26.0.15",
"@types/lodash.deburr": "^4.1.6", "@types/lodash.deburr": "^4.1.6",
"@types/node": "^14.14.21", "@types/node": "^14.10.0",
"@types/semver": "^7.3.4", "@types/semver": "^7.3.4",
"@typescript-eslint/eslint-plugin": "^4.14.0", "@typescript-eslint/parser": "^4.8.1",
"@typescript-eslint/parser": "^4.14.0",
"@vercel/ncc": "^0.27.0", "@vercel/ncc": "^0.27.0",
"eslint": "^7.18.0", "eslint": "^7.17.0",
"eslint-plugin-github": "^4.0.1", "eslint-plugin-github": "^4.0.1",
"eslint-plugin-jest": "^24.1.3", "eslint-plugin-jest": "^24.1.3",
"jest": "^26.6.3", "jest": "^26.6.3",
"jest-circus": "^26.6.3", "jest-circus": "^26.6.3",
"js-yaml": "^4.0.0", "js-yaml": "^3.14.0",
"prettier": "^2.2.1", "prettier": "^2.2.1",
"ts-jest": "^26.4.4", "ts-jest": "^26.4.4",
"typescript": "^4.1.3" "typescript": "^4.1.3"

View File

@@ -1,21 +1,19 @@
import * as core from '@actions/core';
import {context, getOctokit} from '@actions/github'; import {context, getOctokit} from '@actions/github';
import {GitHub} from '@actions/github/lib/utils'; import {GitHub} from '@actions/github/lib/utils';
import {GetResponseTypeFromEndpointMethod} from '@octokit/types'; import {GetResponseTypeFromEndpointMethod} from '@octokit/types';
import {Issue} from './classes/issue';
import {IssueLogger} from './classes/loggers/issue-logger';
import {Logger} from './classes/loggers/logger';
import {Milestones} from './classes/milestones';
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 {isLabeled} from './functions/is-labeled'; 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 {IsoOrRfcDateString} from './types/iso-or-rfc-date-string'; export interface Issue {
import {wordsToList} from './functions/words-to-list'; title: string;
import {IIssue} from './interfaces/issue'; number: number;
updated_at: string;
labels: Label[];
pull_request: any;
state: string;
locked: boolean;
}
export interface PullRequest { export interface PullRequest {
number: number; number: number;
@@ -50,11 +48,7 @@ export interface IssueProcessorOptions {
closeIssueMessage: string; closeIssueMessage: string;
closePrMessage: string; closePrMessage: string;
daysBeforeStale: number; daysBeforeStale: number;
daysBeforeIssueStale: number; // Could be NaN
daysBeforePrStale: number; // Could be NaN
daysBeforeClose: number; daysBeforeClose: number;
daysBeforeIssueClose: number; // Could be NaN
daysBeforePrClose: number; // Could be NaN
staleIssueLabel: string; staleIssueLabel: string;
closeIssueLabel: string; closeIssueLabel: string;
exemptIssueLabels: string; exemptIssueLabels: string;
@@ -69,28 +63,16 @@ export interface IssueProcessorOptions {
skipStaleIssueMessage: boolean; skipStaleIssueMessage: boolean;
skipStalePrMessage: boolean; skipStalePrMessage: boolean;
deleteBranch: boolean; deleteBranch: boolean;
startDate: IsoOrRfcDateString | undefined; // Should be ISO 8601 or RFC 2822
exemptMilestones: string;
exemptIssueMilestones: string;
exemptPrMilestones: string;
} }
/*** /***
* Handle processing of issues for staleness/closure. * Handle processing of issues for staleness/closure.
*/ */
export class IssueProcessor { export class IssueProcessor {
private static _updatedSince(timestamp: string, num_days: number): boolean {
const daysInMillis = 1000 * 60 * 60 * 24 * num_days;
const millisSinceLastUpdated =
new Date().getTime() - new Date(timestamp).getTime();
return millisSinceLastUpdated <= daysInMillis;
}
private readonly _logger: Logger = new Logger();
private _operationsLeft = 0;
readonly client: InstanceType<typeof GitHub>; readonly client: InstanceType<typeof GitHub>;
readonly options: IssueProcessorOptions; readonly options: IssueProcessorOptions;
private operationsLeft = 0;
readonly staleIssues: Issue[] = []; readonly staleIssues: Issue[] = [];
readonly closedIssues: Issue[] = []; readonly closedIssues: Issue[] = [];
readonly deletedBranchIssues: Issue[] = []; readonly deletedBranchIssues: Issue[] = [];
@@ -110,27 +92,27 @@ export class IssueProcessor {
) => Promise<string | undefined> ) => Promise<string | undefined>
) { ) {
this.options = options; this.options = options;
this._operationsLeft = options.operationsPerRun; this.operationsLeft = options.operationsPerRun;
this.client = getOctokit(options.repoToken); this.client = getOctokit(options.repoToken);
if (getActor) { if (getActor) {
this._getActor = getActor; this.getActor = getActor;
} }
if (getIssues) { if (getIssues) {
this._getIssues = getIssues; this.getIssues = getIssues;
} }
if (listIssueComments) { if (listIssueComments) {
this._listIssueComments = listIssueComments; this.listIssueComments = listIssueComments;
} }
if (getLabelCreationDate) { if (getLabelCreationDate) {
this._getLabelCreationDate = getLabelCreationDate; this.getLabelCreationDate = getLabelCreationDate;
} }
if (this.options.debugOnly) { if (this.options.debugOnly) {
this._logger.warning( core.warning(
'Executing in debug mode. Debug output will be written but no issues will be processed.' 'Executing in debug mode. Debug output will be written but no issues will be processed.'
); );
} }
@@ -138,157 +120,97 @@ export class IssueProcessor {
async processIssues(page = 1): Promise<number> { async processIssues(page = 1): Promise<number> {
// get the next batch of issues // get the next batch of issues
const issues: Issue[] = await this._getIssues(page); const issues: Issue[] = await this.getIssues(page);
this._operationsLeft -= 1; this.operationsLeft -= 1;
const actor: string = await this._getActor(); const actor: string = await this.getActor();
if (issues.length <= 0) { if (issues.length <= 0) {
this._logger.info('---'); core.info('No more issues found to process. Exiting.');
this._logger.info('No more issues found to process. Exiting.'); return this.operationsLeft;
return this._operationsLeft;
} }
for (const issue of issues.values()) { for (const issue of issues.values()) {
const issueLogger: IssueLogger = new IssueLogger(issue); const isPr = !!issue.pull_request;
issueLogger.info( core.info(
`Found issue: issue #${issue.number} last updated ${issue.updated_at} (is pr? ${issue.isPullRequest})` `Found issue: issue #${issue.number} last updated ${issue.updated_at} (is pr? ${isPr})`
); );
// calculate string based messages for this issue // calculate string based messages for this issue
const staleMessage: string = issue.isPullRequest const staleMessage: string = isPr
? this.options.stalePrMessage ? this.options.stalePrMessage
: this.options.staleIssueMessage; : this.options.staleIssueMessage;
const closeMessage: string = issue.isPullRequest const closeMessage: string = isPr
? this.options.closePrMessage ? this.options.closePrMessage
: this.options.closeIssueMessage; : this.options.closeIssueMessage;
const staleLabel: string = issue.isPullRequest const staleLabel: string = isPr
? this.options.stalePrLabel ? this.options.stalePrLabel
: this.options.staleIssueLabel; : this.options.staleIssueLabel;
const closeLabel: string = issue.isPullRequest const closeLabel: string = isPr
? this.options.closePrLabel ? this.options.closePrLabel
: this.options.closeIssueLabel; : this.options.closeIssueLabel;
const skipMessage = issue.isPullRequest const exemptLabels: string[] = labelsToList(
isPr ? this.options.exemptPrLabels : this.options.exemptIssueLabels
);
const skipMessage = isPr
? this.options.skipStalePrMessage ? this.options.skipStalePrMessage
: this.options.skipStaleIssueMessage; : this.options.skipStaleIssueMessage;
const issueType: IssueType = getIssueType(issue.isPullRequest); const issueType: string = isPr ? 'pr' : 'issue';
const daysBeforeStale: number = issue.isPullRequest const shouldMarkWhenStale = this.options.daysBeforeStale > -1;
? this._getDaysBeforePrStale()
: this._getDaysBeforeIssueStale();
if (issue.isPullRequest) { if (!staleMessage && shouldMarkWhenStale) {
issueLogger.info(`Days before pull request stale: ${daysBeforeStale}`); core.info(`Skipping ${issueType} due to empty stale message`);
} else {
issueLogger.info(`Days before issue stale: ${daysBeforeStale}`);
}
const shouldMarkAsStale: boolean = shouldMarkWhenStale(daysBeforeStale);
if (!staleMessage && shouldMarkAsStale) {
issueLogger.info(`Skipping ${issueType} due to empty stale message`);
continue; continue;
} }
if (issue.state === 'closed') { if (issue.state === 'closed') {
issueLogger.info(`Skipping ${issueType} because it is closed`); core.info(`Skipping ${issueType} because it is closed`);
continue; // don't process closed issues continue; // don't process closed issues
} }
if (issue.locked) { if (issue.locked) {
issueLogger.info(`Skipping ${issueType} because it is locked`); core.info(`Skipping ${issueType} because it is locked`);
continue; // don't process locked issues 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
}
}
if (issue.isStale) {
issueLogger.info(`This issue has a stale label`);
} else {
issueLogger.info(`This issue hasn't a stale label`);
}
const exemptLabels: string[] = wordsToList(
issue.isPullRequest
? this.options.exemptPrLabels
: this.options.exemptIssueLabels
);
if ( if (
exemptLabels.some((exemptLabel: Readonly<string>): boolean => exemptLabels.some((exemptLabel: string) =>
isLabeled(issue, exemptLabel) isLabeled(issue, exemptLabel)
) )
) { ) {
if (issue.isStale) { core.info(`Skipping ${issueType} because it has an exempt label`);
issueLogger.info(`An exempt label was added after the stale label.`);
await this._removeStaleLabel(issue, staleLabel);
}
issueLogger.info(
`Skipping ${issueType} because it has an exempt label`
);
continue; // don't process exempt issues continue; // don't process exempt issues
} }
const milestones: Milestones = new Milestones(this.options, issue); // does this issue have a stale label?
let isStale: boolean = isLabeled(issue, staleLabel);
if (milestones.shouldExemptMilestones()) { if (isStale) {
issueLogger.info( core.info(`This issue has a stale label`);
`Skipping ${issueType} because it has an exempt milestone` } else {
); core.info(`This issue hasn't a stale label`);
continue; // don't process exempt milestones
} }
// should this issue be marked stale? // should this issue be marked stale?
const shouldBeStale = !IssueProcessor._updatedSince( const shouldBeStale = !IssueProcessor.updatedSince(
issue.updated_at, issue.updated_at,
this.options.daysBeforeStale this.options.daysBeforeStale
); );
// determine if this issue needs to be marked stale first // determine if this issue needs to be marked stale first
if (!issue.isStale && shouldBeStale && shouldMarkAsStale) { if (!isStale && shouldBeStale && shouldMarkWhenStale) {
issueLogger.info( core.info(
`Marking ${issueType} stale because it was last updated on ${issue.updated_at} and it does not have a stale label` `Marking ${issueType} stale because it was last updated on ${issue.updated_at} and it does not have a stale label`
); );
await this._markStale(issue, staleMessage, staleLabel, skipMessage); await this.markStale(issue, staleMessage, staleLabel, skipMessage);
issue.isStale = true; // this issue is now considered stale isStale = true; // this issue is now considered stale
} }
// process the issue if it was marked stale // process the issue if it was marked stale
if (issue.isStale) { if (isStale) {
issueLogger.info(`Found a stale ${issueType}`); core.info(`Found a stale ${issueType}`);
await this._processStaleIssue( await this.processStaleIssue(
issue, issue,
issueType, issueType,
staleLabel, staleLabel,
@@ -299,10 +221,8 @@ export class IssueProcessor {
} }
} }
if (this._operationsLeft <= 0) { if (this.operationsLeft <= 0) {
this._logger.warning( core.warning('Reached max number of operations to process. Exiting.');
'Reached max number of operations to process. Exiting.'
);
return 0; return 0;
} }
@@ -311,88 +231,73 @@ export class IssueProcessor {
} }
// handle all of the stale issue logic when we find a stale issue // handle all of the stale issue logic when we find a stale issue
private async _processStaleIssue( private async processStaleIssue(
issue: Issue, issue: Issue,
issueType: IssueType, issueType: string,
staleLabel: string, staleLabel: string,
actor: string, actor: string,
closeMessage?: string, closeMessage?: string,
closeLabel?: string closeLabel?: string
) { ) {
const issueLogger: IssueLogger = new IssueLogger(issue);
const markedStaleOn: string = const markedStaleOn: string =
(await this._getLabelCreationDate(issue, staleLabel)) || issue.updated_at; (await this.getLabelCreationDate(issue, staleLabel)) || issue.updated_at;
issueLogger.info( core.info(`Issue #${issue.number} marked stale on: ${markedStaleOn}`);
`Issue #${issue.number} marked stale on: ${markedStaleOn}`
);
const issueHasComments: boolean = await this._hasCommentsSince( const issueHasComments: boolean = await this.hasCommentsSince(
issue, issue,
markedStaleOn, markedStaleOn,
actor actor
); );
issueLogger.info( core.info(
`Issue #${issue.number} has been commented on: ${issueHasComments}` `Issue #${issue.number} has been commented on: ${issueHasComments}`
); );
const isPr: boolean = isPullRequest(issue); const issueHasUpdate: boolean = IssueProcessor.updatedSince(
const daysBeforeClose: number = 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: boolean = IssueProcessor._updatedSince(
issue.updated_at, issue.updated_at,
daysBeforeClose this.options.daysBeforeClose
);
issueLogger.info(
`Issue #${issue.number} has been updated: ${issueHasUpdate}`
); );
core.info(`Issue #${issue.number} has been updated: ${issueHasUpdate}`);
// should we un-stale this issue? // should we un-stale this issue?
if (this.options.removeStaleWhenUpdated && issueHasComments) { if (this.options.removeStaleWhenUpdated && issueHasComments) {
await this._removeStaleLabel(issue, staleLabel); core.info(
`Issue #${issue.number} is no longer stale. Removing stale label.`
);
await this.removeLabel(issue, staleLabel);
} }
// now start closing logic // now start closing logic
if (daysBeforeClose < 0) { if (this.options.daysBeforeClose < 0) {
return; // nothing to do because we aren't closing stale issues return; // nothing to do because we aren't closing stale issues
} }
if (!issueHasComments && !issueHasUpdate) { if (!issueHasComments && !issueHasUpdate) {
issueLogger.info( core.info(
`Closing ${issueType} because it was last updated on ${issue.updated_at}` `Closing ${issueType} because it was last updated on ${issue.updated_at}`
); );
await this._closeIssue(issue, closeMessage, closeLabel); await this.closeIssue(issue, closeMessage, closeLabel);
if (this.options.deleteBranch && issue.pull_request) { if (this.options.deleteBranch && issue.pull_request) {
issueLogger.info( core.info(
`Deleting branch for #${issue.number} as delete-branch option was specified` `Deleting branch for #${issue.number} as delete-branch option was specified`
); );
await this._deleteBranch(issue); await this.deleteBranch(issue);
this.deletedBranchIssues.push(issue); this.deletedBranchIssues.push(issue);
} }
} else { } else {
issueLogger.info( core.info(
`Stale ${issueType} is not old enough to close yet (hasComments? ${issueHasComments}, hasUpdate? ${issueHasUpdate})` `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) // checks to see if a given issue is still stale (has had activity on it)
private async _hasCommentsSince( private async hasCommentsSince(
issue: Issue, issue: Issue,
sinceDate: string, sinceDate: string,
actor: string actor: string
): Promise<boolean> { ): Promise<boolean> {
const issueLogger: IssueLogger = new IssueLogger(issue); core.info(
issueLogger.info(
`Checking for comments on issue #${issue.number} since ${sinceDate}` `Checking for comments on issue #${issue.number} since ${sinceDate}`
); );
@@ -401,13 +306,13 @@ export class IssueProcessor {
} }
// find any comments since the date // find any comments since the date
const comments = await this._listIssueComments(issue.number, sinceDate); const comments = await this.listIssueComments(issue.number, sinceDate);
const filteredComments = comments.filter( const filteredComments = comments.filter(
comment => comment.user.type === 'User' && comment.user.login !== actor comment => comment.user.type === 'User' && comment.user.login !== actor
); );
issueLogger.info( core.info(
`Comments not made by actor or another bot: ${filteredComments.length}` `Comments not made by actor or another bot: ${filteredComments.length}`
); );
@@ -416,7 +321,7 @@ export class IssueProcessor {
} }
// grab comments for an issue since a given date // grab comments for an issue since a given date
private async _listIssueComments( private async listIssueComments(
issueNumber: number, issueNumber: number,
sinceDate: string sinceDate: string
): Promise<Comment[]> { ): Promise<Comment[]> {
@@ -430,13 +335,13 @@ export class IssueProcessor {
}); });
return comments.data; return comments.data;
} catch (error) { } catch (error) {
this._logger.error(`List issue comments error: ${error.message}`); core.error(`List issue comments error: ${error.message}`);
return Promise.resolve([]); return Promise.resolve([]);
} }
} }
// get the actor from the GitHub token or context // get the actor from the GitHub token or context
private async _getActor(): Promise<string> { private async getActor(): Promise<string> {
let actor; let actor;
try { try {
actor = await this.client.users.getAuthenticated(); actor = await this.client.users.getAuthenticated();
@@ -447,8 +352,8 @@ export class IssueProcessor {
return actor.data.login; return actor.data.login;
} }
// grab issues from github in batches of 100 // grab issues from github in baches of 100
private async _getIssues(page: number): Promise<Issue[]> { private async getIssues(page: number): Promise<Issue[]> {
// generate type for response // generate type for response
const endpoint = this.client.issues.listForRepo; const endpoint = this.client.issues.listForRepo;
type OctoKitIssueList = GetResponseTypeFromEndpointMethod<typeof endpoint>; type OctoKitIssueList = GetResponseTypeFromEndpointMethod<typeof endpoint>;
@@ -465,30 +370,25 @@ export class IssueProcessor {
page page
} }
); );
return issueResult.data;
return issueResult.data.map(
(issue: Readonly<IIssue>): Issue => new Issue(this.options, issue)
);
} catch (error) { } catch (error) {
this._logger.error(`Get issues for repo error: ${error.message}`); core.error(`Get issues for repo error: ${error.message}`);
return Promise.resolve([]); return Promise.resolve([]);
} }
} }
// Mark an issue as stale with a comment and a label // Mark an issue as stale with a comment and a label
private async _markStale( private async markStale(
issue: Issue, issue: Issue,
staleMessage: string, staleMessage: string,
staleLabel: string, staleLabel: string,
skipMessage: boolean skipMessage: boolean
): Promise<void> { ): Promise<void> {
const issueLogger: IssueLogger = new IssueLogger(issue); core.info(`Marking issue #${issue.number} as stale`);
issueLogger.info(`Marking issue #${issue.number} as stale`);
this.staleIssues.push(issue); this.staleIssues.push(issue);
this._operationsLeft -= 2; this.operationsLeft -= 2;
// if the issue is being marked stale, the updated date should be changed to right now // if the issue is being marked stale, the updated date should be changed to right now
// so that close calculations work correctly // so that close calculations work correctly
@@ -508,7 +408,7 @@ export class IssueProcessor {
body: staleMessage body: staleMessage
}); });
} catch (error) { } catch (error) {
issueLogger.error(`Error creating a comment: ${error.message}`); core.error(`Error creating a comment: ${error.message}`);
} }
} }
@@ -520,23 +420,21 @@ export class IssueProcessor {
labels: [staleLabel] labels: [staleLabel]
}); });
} catch (error) { } catch (error) {
issueLogger.error(`Error adding a label: ${error.message}`); core.error(`Error adding a label: ${error.message}`);
} }
} }
// Close an issue based on staleness // Close an issue based on staleness
private async _closeIssue( private async closeIssue(
issue: Issue, issue: Issue,
closeMessage?: string, closeMessage?: string,
closeLabel?: string closeLabel?: string
): Promise<void> { ): Promise<void> {
const issueLogger: IssueLogger = new IssueLogger(issue); core.info(`Closing issue #${issue.number} for being stale`);
issueLogger.info(`Closing issue #${issue.number} for being stale`);
this.closedIssues.push(issue); this.closedIssues.push(issue);
this._operationsLeft -= 1; this.operationsLeft -= 1;
if (this.options.debugOnly) { if (this.options.debugOnly) {
return; return;
@@ -551,7 +449,7 @@ export class IssueProcessor {
body: closeMessage body: closeMessage
}); });
} catch (error) { } catch (error) {
issueLogger.error(`Error creating a comment: ${error.message}`); core.error(`Error creating a comment: ${error.message}`);
} }
} }
@@ -564,7 +462,7 @@ export class IssueProcessor {
labels: [closeLabel] labels: [closeLabel]
}); });
} catch (error) { } catch (error) {
issueLogger.error(`Error adding a label: ${error.message}`); core.error(`Error adding a label: ${error.message}`);
} }
} }
@@ -576,36 +474,31 @@ export class IssueProcessor {
state: 'closed' state: 'closed'
}); });
} catch (error) { } catch (error) {
issueLogger.error(`Error updating an issue: ${error.message}`); core.error(`Error updating an issue: ${error.message}`);
} }
} }
private async _getPullRequest( private async getPullRequest(
issue: Issue pullNumber: number
): Promise<PullRequest | undefined> { ): Promise<PullRequest | undefined> {
const issueLogger: IssueLogger = new IssueLogger(issue); this.operationsLeft -= 1;
this._operationsLeft -= 1;
try { try {
const pullRequest = await this.client.pulls.get({ const pullRequest = await this.client.pulls.get({
owner: context.repo.owner, owner: context.repo.owner,
repo: context.repo.repo, repo: context.repo.repo,
pull_number: issue.number pull_number: pullNumber
}); });
return pullRequest.data; return pullRequest.data;
} catch (error) { } catch (error) {
issueLogger.error( core.error(`Error getting pull request ${pullNumber}: ${error.message}`);
`Error getting pull request ${issue.number}: ${error.message}`
);
} }
} }
// Delete the branch on closed pull request // Delete the branch on closed pull request
private async _deleteBranch(issue: Issue): Promise<void> { private async deleteBranch(issue: Issue): Promise<void> {
const issueLogger: IssueLogger = new IssueLogger(issue); core.info(
issueLogger.info(
`Delete branch from closed issue #${issue.number} - ${issue.title}` `Delete branch from closed issue #${issue.number} - ${issue.title}`
); );
@@ -613,21 +506,19 @@ export class IssueProcessor {
return; return;
} }
const pullRequest = await this._getPullRequest(issue); const pullRequest = await this.getPullRequest(issue.number);
if (!pullRequest) { if (!pullRequest) {
issueLogger.info( core.info(
`Not deleting branch as pull request not found for issue ${issue.number}` `Not deleting branch as pull request not found for issue ${issue.number}`
); );
return; return;
} }
const branch = pullRequest.head.ref; const branch = pullRequest.head.ref;
issueLogger.info( core.info(`Deleting branch ${branch} from closed issue #${issue.number}`);
`Deleting branch ${branch} from closed issue #${issue.number}`
);
this._operationsLeft -= 1; this.operationsLeft -= 1;
try { try {
await this.client.git.deleteRef({ await this.client.git.deleteRef({
@@ -636,21 +527,19 @@ export class IssueProcessor {
ref: `heads/${branch}` ref: `heads/${branch}`
}); });
} catch (error) { } catch (error) {
issueLogger.error( core.error(
`Error deleting branch ${branch} from issue #${issue.number}: ${error.message}` `Error deleting branch ${branch} from issue #${issue.number}: ${error.message}`
); );
} }
} }
// Remove a label from an issue // Remove a label from an issue
private async _removeLabel(issue: Issue, label: string): Promise<void> { private async removeLabel(issue: Issue, label: string): Promise<void> {
const issueLogger: IssueLogger = new IssueLogger(issue); core.info(`Removing label "${label}" from issue #${issue.number}`);
issueLogger.info(`Removing label "${label}" from issue #${issue.number}`);
this.removedLabelIssues.push(issue); this.removedLabelIssues.push(issue);
this._operationsLeft -= 1; this.operationsLeft -= 1;
// @todo remove the debug only to be able to test the code below // @todo remove the debug only to be able to test the code below
if (this.options.debugOnly) { if (this.options.debugOnly) {
@@ -665,21 +554,19 @@ export class IssueProcessor {
name: label name: label
}); });
} catch (error) { } catch (error) {
issueLogger.error(`Error removing a label: ${error.message}`); core.error(`Error removing a label: ${error.message}`);
} }
} }
// returns the creation date of a given label on an issue (or nothing if no label existed) // 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/ ///see https://developer.github.com/v3/activity/events/
private async _getLabelCreationDate( private async getLabelCreationDate(
issue: Issue, issue: Issue,
label: string label: string
): Promise<string | undefined> { ): Promise<string | undefined> {
const issueLogger: IssueLogger = new IssueLogger(issue); core.info(`Checking for label on issue #${issue.number}`);
issueLogger.info(`Checking for label on issue #${issue.number}`); this.operationsLeft -= 1;
this._operationsLeft -= 1;
const options = this.client.issues.listEvents.endpoint.merge({ const options = this.client.issues.listEvents.endpoint.merge({
owner: context.repo.owner, owner: context.repo.owner,
@@ -703,40 +590,11 @@ export class IssueProcessor {
return staleLabeledEvent.created_at; return staleLabeledEvent.created_at;
} }
private _getDaysBeforeIssueStale(): number { private static updatedSince(timestamp: string, num_days: number): boolean {
return isNaN(this.options.daysBeforeIssueStale) const daysInMillis = 1000 * 60 * 60 * 24 * num_days;
? this.options.daysBeforeStale const millisSinceLastUpdated =
: this.options.daysBeforeIssueStale; new Date().getTime() - new Date(timestamp).getTime();
}
private _getDaysBeforePrStale(): number { return millisSinceLastUpdated <= daysInMillis;
return isNaN(this.options.daysBeforePrStale)
? this.options.daysBeforeStale
: this.options.daysBeforePrStale;
}
private _getDaysBeforeIssueClose(): number {
return isNaN(this.options.daysBeforeIssueClose)
? this.options.daysBeforeClose
: this.options.daysBeforeIssueClose;
}
private _getDaysBeforePrClose(): number {
return isNaN(this.options.daysBeforePrClose)
? this.options.daysBeforeClose
: this.options.daysBeforePrClose;
}
private async _removeStaleLabel(
issue: Issue,
staleLabel: Readonly<string>
): Promise<void> {
const issueLogger: IssueLogger = new IssueLogger(issue);
issueLogger.info(
`Issue #${issue.number} is no longer stale. Removing stale label.`
);
return this._removeLabel(issue, staleLabel);
} }
} }

View File

@@ -1,208 +0,0 @@
import {IIssue} from '../interfaces/issue';
import {IMilestone} from '../interfaces/milestone';
import {IssueProcessorOptions, Label} from '../IssueProcessor';
import {Issue} from './issue';
describe('Issue', (): void => {
let issue: Issue;
let optionsInterface: IssueProcessorOptions;
let issueInterface: IIssue;
beforeEach((): void => {
optionsInterface = {
ascending: false,
closeIssueLabel: '',
closeIssueMessage: '',
closePrLabel: '',
closePrMessage: '',
daysBeforeClose: 0,
daysBeforeIssueClose: 0,
daysBeforeIssueStale: 0,
daysBeforePrClose: 0,
daysBeforePrStale: 0,
daysBeforeStale: 0,
debugOnly: false,
deleteBranch: false,
exemptIssueLabels: '',
exemptIssueMilestones: '',
exemptMilestones: '',
exemptPrLabels: '',
exemptPrMilestones: '',
onlyLabels: '',
operationsPerRun: 0,
removeStaleWhenUpdated: false,
repoToken: '',
skipStaleIssueMessage: false,
skipStalePrMessage: false,
staleIssueMessage: '',
stalePrMessage: '',
startDate: undefined,
stalePrLabel: 'dummy-stale-pr-label',
staleIssueLabel: 'dummy-stale-issue-label'
};
issueInterface = {
title: 'dummy-title',
number: 8,
created_at: 'dummy-created-at',
updated_at: 'dummy-updated-at',
labels: [
{
name: 'dummy-name'
}
],
pull_request: {},
state: 'dummy-state',
locked: false,
milestone: {
title: 'dummy-milestone'
}
};
issue = new Issue(optionsInterface, issueInterface);
});
describe('constructor()', (): void => {
it('should set the title with the given issue title', (): void => {
expect.assertions(1);
expect(issue.title).toStrictEqual('dummy-title');
});
it('should set the number with the given issue number', (): void => {
expect.assertions(1);
expect(issue.number).toStrictEqual(8);
});
it('should set the created_at with the given issue created_at', (): void => {
expect.assertions(1);
expect(issue.created_at).toStrictEqual('dummy-created-at');
});
it('should set the updated_at with the given issue updated_at', (): void => {
expect.assertions(1);
expect(issue.updated_at).toStrictEqual('dummy-updated-at');
});
it('should set the labels with the given issue labels', (): void => {
expect.assertions(1);
expect(issue.labels).toStrictEqual([
{
name: 'dummy-name'
} as Label
]);
});
it('should set the pull_request with the given issue pull_request', (): void => {
expect.assertions(1);
expect(issue.pull_request).toStrictEqual({});
});
it('should set the state with the given issue state', (): void => {
expect.assertions(1);
expect(issue.state).toStrictEqual('dummy-state');
});
it('should set the locked with the given issue locked', (): void => {
expect.assertions(1);
expect(issue.locked).toStrictEqual(false);
});
it('should set the milestone with the given issue milestone', (): void => {
expect.assertions(1);
expect(issue.milestone).toStrictEqual({
title: 'dummy-milestone'
} as IMilestone);
});
describe('when the given issue pull_request is not set', (): void => {
beforeEach((): void => {
issueInterface.pull_request = undefined;
issue = new Issue(optionsInterface, issueInterface);
});
it('should set the isPullRequest to false', (): void => {
expect.assertions(1);
expect(issue.isPullRequest).toStrictEqual(false);
});
});
describe('when the given issue pull_request is set', (): void => {
beforeEach((): void => {
issueInterface.pull_request = {};
issue = new Issue(optionsInterface, issueInterface);
});
it('should set the isPullRequest to true', (): void => {
expect.assertions(1);
expect(issue.isPullRequest).toStrictEqual(true);
});
});
describe('when the given issue is not a pull request', (): void => {
beforeEach((): void => {
issueInterface.pull_request = undefined;
issue = new Issue(optionsInterface, issueInterface);
});
it('should set the staleLabel with the given option staleIssueLabel', (): void => {
expect.assertions(1);
expect(issue.staleLabel).toStrictEqual('dummy-stale-issue-label');
});
});
describe('when the given issue is a pull request', (): void => {
beforeEach((): void => {
issueInterface.pull_request = {};
issue = new Issue(optionsInterface, issueInterface);
});
it('should set the staleLabel with the given option stalePrLabel', (): void => {
expect.assertions(1);
expect(issue.staleLabel).toStrictEqual('dummy-stale-pr-label');
});
});
describe('when the given issue does not contains the stale label', (): void => {
beforeEach((): void => {
issueInterface.pull_request = undefined;
issueInterface.labels = [];
issue = new Issue(optionsInterface, issueInterface);
});
it('should set the isStale to false', (): void => {
expect.assertions(1);
expect(issue.isStale).toStrictEqual(false);
});
});
describe('when the given issue contains the stale label', (): void => {
beforeEach((): void => {
issueInterface.pull_request = undefined;
issueInterface.labels = [
{
name: 'dummy-stale-issue-label'
} as Label
];
issue = new Issue(optionsInterface, issueInterface);
});
it('should set the isStale to true', (): void => {
expect.assertions(1);
expect(issue.isStale).toStrictEqual(true);
});
});
});
});

View File

@@ -1,48 +0,0 @@
import {isLabeled} from '../functions/is-labeled';
import {isPullRequest} from '../functions/is-pull-request';
import {IIssue} from '../interfaces/issue';
import {IMilestone} from '../interfaces/milestone';
import {IssueProcessorOptions, Label} from '../IssueProcessor';
import {IsoDateString} from '../types/iso-date-string';
export class Issue implements IIssue {
private readonly _options: IssueProcessorOptions;
readonly title: string;
readonly number: number;
created_at: IsoDateString;
updated_at: IsoDateString;
readonly labels: Label[];
readonly pull_request: Object | null | undefined;
readonly state: string;
readonly locked: boolean;
readonly milestone: IMilestone | undefined;
readonly isPullRequest: boolean;
readonly staleLabel: string;
isStale: boolean;
constructor(
options: Readonly<IssueProcessorOptions>,
issue: Readonly<IIssue>
) {
this._options = options;
this.title = issue.title;
this.number = issue.number;
this.created_at = issue.created_at;
this.updated_at = issue.updated_at;
this.labels = issue.labels;
this.pull_request = issue.pull_request;
this.state = issue.state;
this.locked = issue.locked;
this.milestone = issue.milestone;
this.isPullRequest = isPullRequest(this);
this.staleLabel = this._getStaleLabel();
this.isStale = isLabeled(this, this.staleLabel);
}
private _getStaleLabel(): string {
return this.isPullRequest
? this._options.stalePrLabel
: this._options.staleIssueLabel;
}
}

View File

@@ -1,78 +0,0 @@
import {Issue} from '../issue';
import {IssueLogger} from './issue-logger';
import * as core from '@actions/core';
describe('IssueLogger', (): void => {
let issue: Issue;
let issueLogger: IssueLogger;
beforeEach((): void => {
issue = {
number: 8
} as Issue;
issueLogger = new IssueLogger(issue);
});
describe('warning()', (): void => {
let message: string;
let coreWarningSpy: jest.SpyInstance;
beforeEach((): void => {
message = 'dummy-message';
coreWarningSpy = jest.spyOn(core, 'warning').mockImplementation();
});
it('should log a warning with the given message and with the issue number as prefix', (): void => {
expect.assertions(2);
issueLogger.warning(message);
expect(coreWarningSpy).toHaveBeenCalledTimes(1);
expect(coreWarningSpy).toHaveBeenCalledWith('[#8] dummy-message');
});
});
describe('info()', (): void => {
let message: string;
let coreInfoSpy: jest.SpyInstance;
beforeEach((): void => {
message = 'dummy-message';
coreInfoSpy = jest.spyOn(core, 'info').mockImplementation();
});
it('should log an information with the given message and with the issue number as prefix', (): void => {
expect.assertions(2);
issueLogger.info(message);
expect(coreInfoSpy).toHaveBeenCalledTimes(1);
expect(coreInfoSpy).toHaveBeenCalledWith('[#8] dummy-message');
});
});
describe('error()', (): void => {
let message: string;
let coreErrorSpy: jest.SpyInstance;
beforeEach((): void => {
message = 'dummy-message';
coreErrorSpy = jest.spyOn(core, 'error').mockImplementation();
});
it('should log an error with the given message and with the issue number as prefix', (): void => {
expect.assertions(2);
issueLogger.error(message);
expect(coreErrorSpy).toHaveBeenCalledTimes(1);
expect(coreErrorSpy).toHaveBeenCalledWith('[#8] dummy-message');
});
});
});

View File

@@ -1,31 +0,0 @@
import * as core from '@actions/core';
import {Issue} from '../issue';
import {Logger} from './logger';
export class IssueLogger implements Logger {
private readonly _issue: Issue;
constructor(issue: Issue) {
this._issue = issue;
}
warning(message: Readonly<string>): void {
core.warning(this._prefixWithIssueNumber(message));
}
info(message: Readonly<string>): void {
core.info(this._prefixWithIssueNumber(message));
}
error(message: Readonly<string>): void {
core.error(this._prefixWithIssueNumber(message));
}
private _prefixWithIssueNumber(message: Readonly<string>): string {
return `[#${this._getIssueNumber()}] ${message}`;
}
private _getIssueNumber(): number {
return this._issue.number;
}
}

View File

@@ -1,73 +0,0 @@
import {Logger} from './logger';
import * as core from '@actions/core';
describe('Logger', (): void => {
let logger: Logger;
beforeEach((): void => {
logger = new Logger();
});
describe('warning()', (): void => {
let message: string;
let coreWarningSpy: jest.SpyInstance;
beforeEach((): void => {
message = 'dummy-message';
coreWarningSpy = jest.spyOn(core, 'warning').mockImplementation();
});
it('should log a warning with the given message', (): void => {
expect.assertions(2);
logger.warning(message);
expect(coreWarningSpy).toHaveBeenCalledTimes(1);
expect(coreWarningSpy).toHaveBeenCalledWith('dummy-message');
});
});
describe('info()', (): void => {
let message: string;
let coreInfoSpy: jest.SpyInstance;
beforeEach((): void => {
message = 'dummy-message';
coreInfoSpy = jest.spyOn(core, 'info').mockImplementation();
});
it('should log an information with the given message', (): void => {
expect.assertions(2);
logger.info(message);
expect(coreInfoSpy).toHaveBeenCalledTimes(1);
expect(coreInfoSpy).toHaveBeenCalledWith('dummy-message');
});
});
describe('error()', (): void => {
let message: string;
let coreErrorSpy: jest.SpyInstance;
beforeEach((): void => {
message = 'dummy-message';
coreErrorSpy = jest.spyOn(core, 'error').mockImplementation();
});
it('should log an error with the given message', (): void => {
expect.assertions(2);
logger.error(message);
expect(coreErrorSpy).toHaveBeenCalledTimes(1);
expect(coreErrorSpy).toHaveBeenCalledWith('dummy-message');
});
});
});

View File

@@ -1,15 +0,0 @@
import * as core from '@actions/core';
export class Logger {
warning(message: Readonly<string>): void {
core.warning(message);
}
info(message: Readonly<string>): void {
core.info(message);
}
error(message: Readonly<string>): void {
core.error(message);
}
}

View File

@@ -1,596 +0,0 @@
import {IIssue} from '../interfaces/issue';
import {IssueProcessorOptions} from '../IssueProcessor';
import {Issue} from './issue';
import {Milestones} from './milestones';
describe('Milestones', (): void => {
let milestones: Milestones;
let optionsInterface: IssueProcessorOptions;
let issue: Issue;
let issueInterface: IIssue;
beforeEach((): void => {
optionsInterface = {
ascending: false,
closeIssueLabel: '',
closeIssueMessage: '',
closePrLabel: '',
closePrMessage: '',
daysBeforeClose: 0,
daysBeforeIssueClose: 0,
daysBeforeIssueStale: 0,
daysBeforePrClose: 0,
daysBeforePrStale: 0,
daysBeforeStale: 0,
debugOnly: false,
deleteBranch: false,
exemptIssueLabels: '',
exemptPrLabels: '',
onlyLabels: '',
operationsPerRun: 0,
removeStaleWhenUpdated: false,
repoToken: '',
skipStaleIssueMessage: false,
skipStalePrMessage: false,
staleIssueLabel: '',
staleIssueMessage: '',
stalePrLabel: '',
stalePrMessage: '',
startDate: undefined,
exemptIssueMilestones: '',
exemptPrMilestones: '',
exemptMilestones: ''
};
issueInterface = {
created_at: '',
locked: false,
milestone: undefined,
number: 0,
pull_request: undefined,
state: '',
title: '',
updated_at: '',
labels: []
};
});
describe('shouldExemptMilestones()', (): void => {
describe('when the given issue is not a pull request', (): void => {
beforeEach((): void => {
issueInterface.pull_request = undefined;
});
describe('when the given options are not configured to exempt a milestone', (): void => {
beforeEach((): void => {
optionsInterface.exemptMilestones = '';
});
describe('when the given options are not configured to exempt an issue milestone', (): void => {
beforeEach((): void => {
optionsInterface.exemptIssueMilestones = '';
});
describe('when the given issue does not have a milestone', (): void => {
beforeEach((): void => {
issueInterface.milestone = undefined;
});
it('should return false', (): void => {
expect.assertions(1);
issue = new Issue(optionsInterface, issueInterface);
milestones = new Milestones(optionsInterface, issue);
const result = milestones.shouldExemptMilestones();
expect(result).toStrictEqual(false);
});
});
describe('when the given issue does have a milestone', (): void => {
beforeEach((): void => {
issueInterface.milestone = {
title: 'dummy-title'
};
});
it('should return false', (): void => {
expect.assertions(1);
issue = new Issue(optionsInterface, issueInterface);
milestones = new Milestones(optionsInterface, issue);
const result = milestones.shouldExemptMilestones();
expect(result).toStrictEqual(false);
});
});
});
describe('when the given options are configured to exempt an issue milestone', (): void => {
beforeEach((): void => {
optionsInterface.exemptIssueMilestones =
'dummy-exempt-issue-milestone';
});
describe('when the given issue does not have a milestone', (): void => {
beforeEach((): void => {
issueInterface.milestone = undefined;
});
it('should return false', (): void => {
expect.assertions(1);
issue = new Issue(optionsInterface, issueInterface);
milestones = new Milestones(optionsInterface, issue);
const result = milestones.shouldExemptMilestones();
expect(result).toStrictEqual(false);
});
});
describe('when the given issue does have a milestone different than the exempt issue milestone', (): void => {
beforeEach((): void => {
issueInterface.milestone = {
title: 'dummy-title'
};
});
it('should return false', (): void => {
expect.assertions(1);
issue = new Issue(optionsInterface, issueInterface);
milestones = new Milestones(optionsInterface, issue);
const result = milestones.shouldExemptMilestones();
expect(result).toStrictEqual(false);
});
});
describe('when the given issue does have a milestone equaling the exempt issue milestone', (): void => {
beforeEach((): void => {
issueInterface.milestone = {
title: 'dummy-exempt-issue-milestone'
};
});
it('should return true', (): void => {
expect.assertions(1);
issue = new Issue(optionsInterface, issueInterface);
milestones = new Milestones(optionsInterface, issue);
const result = milestones.shouldExemptMilestones();
expect(result).toStrictEqual(true);
});
});
});
});
describe('when the given options are configured to exempt a milestone', (): void => {
beforeEach((): void => {
optionsInterface.exemptMilestones = 'dummy-exempt-milestone';
});
describe('when the given options are not configured to exempt an issue milestone', (): void => {
beforeEach((): void => {
optionsInterface.exemptIssueMilestones = '';
});
describe('when the given issue does not have a milestone', (): void => {
beforeEach((): void => {
issueInterface.milestone = undefined;
});
it('should return false', (): void => {
expect.assertions(1);
issue = new Issue(optionsInterface, issueInterface);
milestones = new Milestones(optionsInterface, issue);
const result = milestones.shouldExemptMilestones();
expect(result).toStrictEqual(false);
});
});
describe('when the given issue does have a milestone different than the exempt milestone', (): void => {
beforeEach((): void => {
issueInterface.milestone = {
title: 'dummy-title'
};
});
it('should return false', (): void => {
expect.assertions(1);
issue = new Issue(optionsInterface, issueInterface);
milestones = new Milestones(optionsInterface, issue);
const result = milestones.shouldExemptMilestones();
expect(result).toStrictEqual(false);
});
});
describe('when the given issue does have a milestone equaling the exempt milestone', (): void => {
beforeEach((): void => {
issueInterface.milestone = {
title: 'dummy-exempt-milestone'
};
});
it('should return true', (): void => {
expect.assertions(1);
issue = new Issue(optionsInterface, issueInterface);
milestones = new Milestones(optionsInterface, issue);
const result = milestones.shouldExemptMilestones();
expect(result).toStrictEqual(true);
});
});
});
describe('when the given options are configured to exempt an issue milestone', (): void => {
beforeEach((): void => {
optionsInterface.exemptIssueMilestones =
'dummy-exempt-issue-milestone';
});
describe('when the given issue does not have a milestone', (): void => {
beforeEach((): void => {
issueInterface.milestone = undefined;
});
it('should return false', (): void => {
expect.assertions(1);
issue = new Issue(optionsInterface, issueInterface);
milestones = new Milestones(optionsInterface, issue);
const result = milestones.shouldExemptMilestones();
expect(result).toStrictEqual(false);
});
});
describe('when the given issue does have a milestone different than the exempt issue milestone', (): void => {
beforeEach((): void => {
issueInterface.milestone = {
title: 'dummy-title'
};
});
it('should return false', (): void => {
expect.assertions(1);
issue = new Issue(optionsInterface, issueInterface);
milestones = new Milestones(optionsInterface, issue);
const result = milestones.shouldExemptMilestones();
expect(result).toStrictEqual(false);
});
});
describe('when the given issue does have a milestone equaling the exempt issue milestone', (): void => {
beforeEach((): void => {
issueInterface.milestone = {
title: 'dummy-exempt-issue-milestone'
};
});
it('should return true', (): void => {
expect.assertions(1);
issue = new Issue(optionsInterface, issueInterface);
milestones = new Milestones(optionsInterface, issue);
const result = milestones.shouldExemptMilestones();
expect(result).toStrictEqual(true);
});
});
describe('when the given issue does have a milestone different than the exempt milestone', (): void => {
beforeEach((): void => {
issueInterface.milestone = {
title: 'dummy-title'
};
});
it('should return false', (): void => {
expect.assertions(1);
issue = new Issue(optionsInterface, issueInterface);
milestones = new Milestones(optionsInterface, issue);
const result = milestones.shouldExemptMilestones();
expect(result).toStrictEqual(false);
});
});
describe('when the given issue does have a milestone equaling the exempt milestone', (): void => {
beforeEach((): void => {
issueInterface.milestone = {
title: 'dummy-exempt-milestone'
};
});
it('should return false', (): void => {
expect.assertions(1);
issue = new Issue(optionsInterface, issueInterface);
milestones = new Milestones(optionsInterface, issue);
const result = milestones.shouldExemptMilestones();
expect(result).toStrictEqual(false);
});
});
});
});
});
describe('when the given issue is a pull request', (): void => {
beforeEach((): void => {
issueInterface.pull_request = {};
});
describe('when the given options are not configured to exempt a milestone', (): void => {
beforeEach((): void => {
optionsInterface.exemptMilestones = '';
});
describe('when the given options are not configured to exempt a pull request milestone', (): void => {
beforeEach((): void => {
optionsInterface.exemptPrMilestones = '';
});
describe('when the given issue does not have a milestone', (): void => {
beforeEach((): void => {
issueInterface.milestone = undefined;
});
it('should return false', (): void => {
expect.assertions(1);
issue = new Issue(optionsInterface, issueInterface);
milestones = new Milestones(optionsInterface, issue);
const result = milestones.shouldExemptMilestones();
expect(result).toStrictEqual(false);
});
});
describe('when the given issue does have a milestone', (): void => {
beforeEach((): void => {
issueInterface.milestone = {
title: 'dummy-title'
};
});
it('should return false', (): void => {
expect.assertions(1);
issue = new Issue(optionsInterface, issueInterface);
milestones = new Milestones(optionsInterface, issue);
const result = milestones.shouldExemptMilestones();
expect(result).toStrictEqual(false);
});
});
});
describe('when the given options are configured to exempt a pull request milestone', (): void => {
beforeEach((): void => {
optionsInterface.exemptPrMilestones = 'dummy-exempt-pr-milestone';
});
describe('when the given issue does not have a milestone', (): void => {
beforeEach((): void => {
issueInterface.milestone = undefined;
});
it('should return false', (): void => {
expect.assertions(1);
issue = new Issue(optionsInterface, issueInterface);
milestones = new Milestones(optionsInterface, issue);
const result = milestones.shouldExemptMilestones();
expect(result).toStrictEqual(false);
});
});
describe('when the given issue does have a milestone different than the exempt pull request milestone', (): void => {
beforeEach((): void => {
issueInterface.milestone = {
title: 'dummy-title'
};
});
it('should return false', (): void => {
expect.assertions(1);
issue = new Issue(optionsInterface, issueInterface);
milestones = new Milestones(optionsInterface, issue);
const result = milestones.shouldExemptMilestones();
expect(result).toStrictEqual(false);
});
});
describe('when the given issue does have a milestone equaling the exempt pull request milestone', (): void => {
beforeEach((): void => {
issueInterface.milestone = {
title: 'dummy-exempt-pr-milestone'
};
});
it('should return true', (): void => {
expect.assertions(1);
issue = new Issue(optionsInterface, issueInterface);
milestones = new Milestones(optionsInterface, issue);
const result = milestones.shouldExemptMilestones();
expect(result).toStrictEqual(true);
});
});
});
});
describe('when the given options are configured to exempt a milestone', (): void => {
beforeEach((): void => {
optionsInterface.exemptMilestones = 'dummy-exempt-milestone';
});
describe('when the given options are not configured to exempt a pull request milestone', (): void => {
beforeEach((): void => {
optionsInterface.exemptPrMilestones = '';
});
describe('when the given issue does not have a milestone', (): void => {
beforeEach((): void => {
issueInterface.milestone = undefined;
});
it('should return false', (): void => {
expect.assertions(1);
issue = new Issue(optionsInterface, issueInterface);
milestones = new Milestones(optionsInterface, issue);
const result = milestones.shouldExemptMilestones();
expect(result).toStrictEqual(false);
});
});
describe('when the given issue does have a milestone different than the exempt milestone', (): void => {
beforeEach((): void => {
issueInterface.milestone = {
title: 'dummy-title'
};
});
it('should return false', (): void => {
expect.assertions(1);
issue = new Issue(optionsInterface, issueInterface);
milestones = new Milestones(optionsInterface, issue);
const result = milestones.shouldExemptMilestones();
expect(result).toStrictEqual(false);
});
});
describe('when the given issue does have a milestone equaling the exempt milestone', (): void => {
beforeEach((): void => {
issueInterface.milestone = {
title: 'dummy-exempt-milestone'
};
});
it('should return true', (): void => {
expect.assertions(1);
issue = new Issue(optionsInterface, issueInterface);
milestones = new Milestones(optionsInterface, issue);
const result = milestones.shouldExemptMilestones();
expect(result).toStrictEqual(true);
});
});
});
describe('when the given options are configured to exempt a pull request milestone', (): void => {
beforeEach((): void => {
optionsInterface.exemptPrMilestones = 'dummy-exempt-pr-milestone';
});
describe('when the given issue does not have a milestone', (): void => {
beforeEach((): void => {
issueInterface.milestone = undefined;
});
it('should return false', (): void => {
expect.assertions(1);
issue = new Issue(optionsInterface, issueInterface);
milestones = new Milestones(optionsInterface, issue);
const result = milestones.shouldExemptMilestones();
expect(result).toStrictEqual(false);
});
});
describe('when the given issue does have a milestone different than the exempt pull request milestone', (): void => {
beforeEach((): void => {
issueInterface.milestone = {
title: 'dummy-title'
};
});
it('should return false', (): void => {
expect.assertions(1);
issue = new Issue(optionsInterface, issueInterface);
milestones = new Milestones(optionsInterface, issue);
const result = milestones.shouldExemptMilestones();
expect(result).toStrictEqual(false);
});
});
describe('when the given issue does have a milestone equaling the exempt pull request milestone', (): void => {
beforeEach((): void => {
issueInterface.milestone = {
title: 'dummy-exempt-pr-milestone'
};
});
it('should return true', (): void => {
expect.assertions(1);
issue = new Issue(optionsInterface, issueInterface);
milestones = new Milestones(optionsInterface, issue);
const result = milestones.shouldExemptMilestones();
expect(result).toStrictEqual(true);
});
});
describe('when the given issue does have a milestone different than the exempt milestone', (): void => {
beforeEach((): void => {
issueInterface.milestone = {
title: 'dummy-title'
};
});
it('should return false', (): void => {
expect.assertions(1);
issue = new Issue(optionsInterface, issueInterface);
milestones = new Milestones(optionsInterface, issue);
const result = milestones.shouldExemptMilestones();
expect(result).toStrictEqual(false);
});
});
describe('when the given issue does have a milestone equaling the exempt milestone', (): void => {
beforeEach((): void => {
issueInterface.milestone = {
title: 'dummy-exempt-milestone'
};
});
it('should return false', (): void => {
expect.assertions(1);
issue = new Issue(optionsInterface, issueInterface);
milestones = new Milestones(optionsInterface, issue);
const result = milestones.shouldExemptMilestones();
expect(result).toStrictEqual(false);
});
});
});
});
});
});
});

View File

@@ -1,59 +0,0 @@
import deburr from 'lodash.deburr';
import {wordsToList} from '../functions/words-to-list';
import {IssueProcessorOptions} from '../IssueProcessor';
import {Issue} from './issue';
type CleanMilestone = string;
export class Milestones {
private static _cleanMilestone(label: Readonly<string>): CleanMilestone {
return deburr(label.toLowerCase());
}
private readonly _options: IssueProcessorOptions;
private readonly _issue: Issue;
constructor(options: Readonly<IssueProcessorOptions>, issue: Issue) {
this._options = options;
this._issue = issue;
}
shouldExemptMilestones(): boolean {
const exemptMilestones: string[] = this._getExemptMilestones();
return exemptMilestones.some((exemptMilestone: Readonly<string>): boolean =>
this._hasMilestone(exemptMilestone)
);
}
private _getExemptMilestones(): string[] {
return wordsToList(
this._issue.isPullRequest
? this._getExemptPullRequestMilestones()
: this._getExemptIssueMilestones()
);
}
private _getExemptIssueMilestones(): string {
return this._options.exemptIssueMilestones !== ''
? this._options.exemptIssueMilestones
: this._options.exemptMilestones;
}
private _getExemptPullRequestMilestones(): string {
return this._options.exemptPrMilestones !== ''
? this._options.exemptPrMilestones
: this._options.exemptMilestones;
}
private _hasMilestone(milestone: Readonly<string>): boolean {
if (!this._issue.milestone) {
return false;
}
return (
Milestones._cleanMilestone(milestone) ===
Milestones._cleanMilestone(this._issue.milestone.title)
);
}
}

View File

@@ -1,4 +0,0 @@
export enum IssueType {
Issue = 'issue',
PullRequest = 'pr'
}

View File

@@ -1,33 +0,0 @@
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');
});
});
});

View File

@@ -1,17 +0,0 @@
import {HumanizedDate} from '../../types/humanized-date';
export function getHumanizedDate(date: Readonly<Date>): 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('-');
}

View File

@@ -1,51 +0,0 @@
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);
});
});
});

View File

@@ -1,6 +0,0 @@
export function isDateMoreRecentThan(
date: Readonly<Date>,
comparedDate: Readonly<Date>
): boolean {
return date > comparedDate;
}

View File

@@ -1,61 +0,0 @@
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);
});
});
});

View File

@@ -1,18 +0,0 @@
/**
* @description
* Check if a date is valid
*
* @see
* https://stackoverflow.com/a/1353711/4440414
*
* @param {Readonly<Date>} date The date to check
*
* @returns {boolean} true when the given date is valid
*/
export function isValidDate(date: Readonly<Date>): boolean {
if (Object.prototype.toString.call(date) === '[object Date]') {
return !isNaN(date.getTime());
}
return false;
}

View File

@@ -1,33 +0,0 @@
import {getIssueType} from './get-issue-type';
describe('getIssueType()', (): void => {
let isPullRequest: boolean;
describe('when the issue is a not pull request', (): void => {
beforeEach((): void => {
isPullRequest = false;
});
it('should return that the issue is really an issue', (): void => {
expect.assertions(1);
const result = getIssueType(isPullRequest);
expect(result).toStrictEqual('issue');
});
});
describe('when the issue is a pull request', (): void => {
beforeEach((): void => {
isPullRequest = true;
});
it('should return that the issue is a pull request', (): void => {
expect.assertions(1);
const result = getIssueType(isPullRequest);
expect(result).toStrictEqual('pr');
});
});
});

View File

@@ -1,5 +0,0 @@
import {IssueType} from '../enums/issue-type';
export function getIssueType(isPullRequest: Readonly<boolean>): IssueType {
return isPullRequest ? IssueType.PullRequest : IssueType.Issue;
}

View File

@@ -1,4 +1,4 @@
import {Issue} from '../classes/issue'; import {Issue} from '../IssueProcessor';
import {isLabeled} from './is-labeled'; import {isLabeled} from './is-labeled';
describe('isLabeled()', (): void => { describe('isLabeled()', (): void => {

View File

@@ -1,16 +1,16 @@
import deburr from 'lodash.deburr'; import deburr from 'lodash.deburr';
import {Issue} from '../classes/issue'; import {Issue, Label} from '../IssueProcessor';
import {Label} from '../IssueProcessor';
import {CleanLabel} from '../types/clean-label'; type CleanLabel = string;
/** /**
* @description * @description
* Check if the given label is listed as a label of the given issue * Check if the label is listed as a label of the issue
* *
* @param {Readonly<Issue>} issue A GitHub issue containing some labels * @param {Readonly<Issue>} issue A GitHub issue containing some labels
* @param {Readonly<string>} label The label to check the presence with * @param {Readonly<string>} label The label to check the presence with
* *
* @return {boolean} Return true when the given label is also in the given issue labels * @return {boolean} Return true when the given label is also in the issue labels
*/ */
export function isLabeled( export function isLabeled(
issue: Readonly<Issue>, issue: Readonly<Issue>,

View File

@@ -1,57 +0,0 @@
import {Issue} from '../classes/issue';
import {isPullRequest} from './is-pull-request';
describe('isPullRequest()', (): void => {
let issue: Issue;
describe('when the given issue has an undefined pull request', (): void => {
beforeEach((): void => {
issue = {
pull_request: undefined
} as Issue;
});
it('should return false', (): void => {
expect.assertions(1);
const result = isPullRequest(issue);
expect(result).toStrictEqual(false);
});
});
describe('when the given issue has a null pull request', (): void => {
beforeEach((): void => {
issue = {
pull_request: null
} as Issue;
});
it('should return false', (): void => {
expect.assertions(1);
const result = isPullRequest(issue);
expect(result).toStrictEqual(false);
});
});
describe.each([{}, true])(
'when the given issue has pull request',
(value): void => {
beforeEach((): void => {
issue = {
pull_request: value
} as Issue;
});
it('should return true', (): void => {
expect.assertions(1);
const result = isPullRequest(issue);
expect(result).toStrictEqual(true);
});
}
);
});

View File

@@ -1,5 +0,0 @@
import {Issue} from '../classes/issue';
export function isPullRequest(issue: Readonly<Issue>): boolean {
return !!issue.pull_request;
}

View File

@@ -0,0 +1,141 @@
import {labelsToList} from './labels-to-list';
describe('labelsToList()', (): void => {
let labels: string;
describe('when the given labels is empty', (): void => {
beforeEach((): void => {
labels = '';
});
it('should return an empty list of labels', (): void => {
expect.assertions(1);
const result = labelsToList(labels);
expect(result).toStrictEqual([]);
});
});
describe('when the given labels is a simple label', (): void => {
beforeEach((): void => {
labels = 'label';
});
it('should return a list of one label', (): void => {
expect.assertions(1);
const result = labelsToList(labels);
expect(result).toStrictEqual(['label']);
});
});
describe('when the given labels is a label with extra spaces before and after', (): void => {
beforeEach((): void => {
labels = ' label ';
});
it('should return a list of one label and remove all spaces before and after', (): void => {
expect.assertions(1);
const result = labelsToList(labels);
expect(result).toStrictEqual(['label']);
});
});
describe('when the given labels is a kebab case label', (): void => {
beforeEach((): void => {
labels = 'kebab-case-label';
});
it('should return a list of one label', (): void => {
expect.assertions(1);
const result = labelsToList(labels);
expect(result).toStrictEqual(['kebab-case-label']);
});
});
describe('when the given labels is two kebab case labels separated with a comma', (): void => {
beforeEach((): void => {
labels = 'kebab-case-label-1,kebab-case-label-2';
});
it('should return a list of two labels', (): void => {
expect.assertions(1);
const result = labelsToList(labels);
expect(result).toStrictEqual([
'kebab-case-label-1',
'kebab-case-label-2'
]);
});
});
describe('when the given labels is a multiple word label', (): void => {
beforeEach((): void => {
labels = 'label like a sentence';
});
it('should return a list of one label', (): void => {
expect.assertions(1);
const result = labelsToList(labels);
expect(result).toStrictEqual(['label like a sentence']);
});
});
describe('when the given labels is two multiple word labels separated with a comma', (): void => {
beforeEach((): void => {
labels = 'label like a sentence, another label like a sentence';
});
it('should return a list of two labels', (): void => {
expect.assertions(1);
const result = labelsToList(labels);
expect(result).toStrictEqual([
'label like a sentence',
'another label like a sentence'
]);
});
});
describe('when the given labels is a multiple word label with %20 spaces', (): void => {
beforeEach((): void => {
labels = 'label%20like%20a%20sentence';
});
it('should return a list of one label', (): void => {
expect.assertions(1);
const result = labelsToList(labels);
expect(result).toStrictEqual(['label%20like%20a%20sentence']);
});
});
describe('when the given labels is two multiple word labels with %20 spaces separated with a comma', (): void => {
beforeEach((): void => {
labels =
'label%20like%20a%20sentence,another%20label%20like%20a%20sentence';
});
it('should return a list of two labels', (): void => {
expect.assertions(1);
const result = labelsToList(labels);
expect(result).toStrictEqual([
'label%20like%20a%20sentence',
'another%20label%20like%20a%20sentence'
]);
});
});
});

View File

@@ -0,0 +1,23 @@
/**
* @description
* Transform a string of comma separated labels
* to an array of labels
*
* @example
* labelsToList('label') => ['label']
* labelsToList('label,label') => ['label', 'label']
* labelsToList('kebab-label') => ['kebab-label']
* labelsToList('kebab%20label') => ['kebab%20label']
* labelsToList('label with words') => ['label with words']
*
* @param {Readonly<string>} labels A string of comma separated labels
*
* @return {string[]} A list of labels
*/
export function labelsToList(labels: Readonly<string>): string[] {
if (!labels.length) {
return [];
}
return labels.split(',').map(l => l.trim());
}

View File

@@ -1,47 +0,0 @@
import {shouldMarkWhenStale} from './should-mark-when-stale';
describe('shouldMarkWhenStale()', (): void => {
let daysBeforeStale: number;
describe('when the given number of days indicate that it should be stalled', (): void => {
beforeEach((): void => {
daysBeforeStale = -1;
});
it('should return false', (): void => {
expect.assertions(1);
const result = shouldMarkWhenStale(daysBeforeStale);
expect(result).toStrictEqual(false);
});
});
describe('when the given number of days indicate that it should be stalled today', (): void => {
beforeEach((): void => {
daysBeforeStale = 0;
});
it('should return true', (): void => {
expect.assertions(1);
const result = shouldMarkWhenStale(daysBeforeStale);
expect(result).toStrictEqual(true);
});
});
describe('when the given number of days indicate that it should be stalled tomorrow', (): void => {
beforeEach((): void => {
daysBeforeStale = 1;
});
it('should return true', (): void => {
expect.assertions(1);
const result = shouldMarkWhenStale(daysBeforeStale);
expect(result).toStrictEqual(true);
});
});
});

View File

@@ -1,5 +0,0 @@
export function shouldMarkWhenStale(
daysBeforeStale: Readonly<number>
): boolean {
return daysBeforeStale >= 0;
}

View File

@@ -1,137 +0,0 @@
import {wordsToList} from './words-to-list';
describe('wordsToList()', (): void => {
let words: string;
describe('when the given words is empty', (): void => {
beforeEach((): void => {
words = '';
});
it('should return an empty list of words', (): void => {
expect.assertions(1);
const result = wordsToList(words);
expect(result).toStrictEqual([]);
});
});
describe('when the given words is a simple word', (): void => {
beforeEach((): void => {
words = 'word';
});
it('should return a list of one word', (): void => {
expect.assertions(1);
const result = wordsToList(words);
expect(result).toStrictEqual(['word']);
});
});
describe('when the given words is a word with extra spaces before and after', (): void => {
beforeEach((): void => {
words = ' word ';
});
it('should return a list of one word and remove all spaces before and after', (): void => {
expect.assertions(1);
const result = wordsToList(words);
expect(result).toStrictEqual(['word']);
});
});
describe('when the given words is a kebab case word', (): void => {
beforeEach((): void => {
words = 'kebab-case-word';
});
it('should return a list of one word', (): void => {
expect.assertions(1);
const result = wordsToList(words);
expect(result).toStrictEqual(['kebab-case-word']);
});
});
describe('when the given words is two kebab case words separated with a comma', (): void => {
beforeEach((): void => {
words = 'kebab-case-word-1,kebab-case-word-2';
});
it('should return a list of two words', (): void => {
expect.assertions(1);
const result = wordsToList(words);
expect(result).toStrictEqual(['kebab-case-word-1', 'kebab-case-word-2']);
});
});
describe('when the given words is a multiple word word', (): void => {
beforeEach((): void => {
words = 'word like a sentence';
});
it('should return a list of one word', (): void => {
expect.assertions(1);
const result = wordsToList(words);
expect(result).toStrictEqual(['word like a sentence']);
});
});
describe('when the given words is two multiple word words separated with a comma', (): void => {
beforeEach((): void => {
words = 'word like a sentence, another word like a sentence';
});
it('should return a list of two words', (): void => {
expect.assertions(1);
const result = wordsToList(words);
expect(result).toStrictEqual([
'word like a sentence',
'another word like a sentence'
]);
});
});
describe('when the given words is a multiple word word with %20 spaces', (): void => {
beforeEach((): void => {
words = 'word%20like%20a%20sentence';
});
it('should return a list of one word', (): void => {
expect.assertions(1);
const result = wordsToList(words);
expect(result).toStrictEqual(['word%20like%20a%20sentence']);
});
});
describe('when the given words is two multiple word words with %20 spaces separated with a comma', (): void => {
beforeEach((): void => {
words = 'word%20like%20a%20sentence,another%20word%20like%20a%20sentence';
});
it('should return a list of two words', (): void => {
expect.assertions(1);
const result = wordsToList(words);
expect(result).toStrictEqual([
'word%20like%20a%20sentence',
'another%20word%20like%20a%20sentence'
]);
});
});
});

View File

@@ -1,23 +0,0 @@
/**
* @description
* Transform a string of comma separated words
* to an array of words
*
* @example
* wordsToList('label') => ['label']
* wordsToList('label,label') => ['label', 'label']
* wordsToList('kebab-label') => ['kebab-label']
* wordsToList('kebab%20label') => ['kebab%20label']
* wordsToList('label with words') => ['label with words']
*
* @param {Readonly<string>} words A string of comma separated words
*
* @return {string[]} A list of words
*/
export function wordsToList(words: Readonly<string>): string[] {
if (!words.length) {
return [];
}
return words.split(',').map((word: Readonly<string>): string => word.trim());
}

View File

@@ -1,15 +0,0 @@
import {Label} from '../IssueProcessor';
import {IsoDateString} from '../types/iso-date-string';
import {IMilestone} from './milestone';
export interface IIssue {
title: string;
number: number;
created_at: IsoDateString;
updated_at: IsoDateString;
labels: Label[];
pull_request: Object | null | undefined;
state: string;
locked: boolean;
milestone: IMilestone | undefined;
}

View File

@@ -1,3 +0,0 @@
export interface IMilestone {
title: string;
}

View File

@@ -1,5 +1,4 @@
import * as core from '@actions/core'; import * as core from '@actions/core';
import {isValidDate} from './functions/dates/is-valid-date';
import {IssueProcessor, IssueProcessorOptions} from './IssueProcessor'; import {IssueProcessor, IssueProcessorOptions} from './IssueProcessor';
async function run(): Promise<void> { async function run(): Promise<void> {
@@ -15,7 +14,7 @@ async function run(): Promise<void> {
} }
function getAndValidateArgs(): IssueProcessorOptions { function getAndValidateArgs(): IssueProcessorOptions {
const args: IssueProcessorOptions = { const args = {
repoToken: core.getInput('repo-token'), repoToken: core.getInput('repo-token'),
staleIssueMessage: core.getInput('stale-issue-message'), staleIssueMessage: core.getInput('stale-issue-message'),
stalePrMessage: core.getInput('stale-pr-message'), stalePrMessage: core.getInput('stale-pr-message'),
@@ -24,13 +23,9 @@ function getAndValidateArgs(): IssueProcessorOptions {
daysBeforeStale: parseInt( daysBeforeStale: parseInt(
core.getInput('days-before-stale', {required: true}) 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( daysBeforeClose: parseInt(
core.getInput('days-before-close', {required: true}) 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}), staleIssueLabel: core.getInput('stale-issue-label', {required: true}),
closeIssueLabel: core.getInput('close-issue-label'), closeIssueLabel: core.getInput('close-issue-label'),
exemptIssueLabels: core.getInput('exempt-issue-labels'), exemptIssueLabels: core.getInput('exempt-issue-labels'),
@@ -48,14 +43,7 @@ function getAndValidateArgs(): IssueProcessorOptions {
ascending: core.getInput('ascending') === 'true', ascending: core.getInput('ascending') === 'true',
skipStalePrMessage: core.getInput('skip-stale-pr-message') === 'true', skipStalePrMessage: core.getInput('skip-stale-pr-message') === 'true',
skipStaleIssueMessage: core.getInput('skip-stale-issue-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,
exemptMilestones: core.getInput('exempt-milestones'),
exemptIssueMilestones: core.getInput('exempt-issue-milestones'),
exemptPrMilestones: core.getInput('exempt-pr-milestones')
}; };
for (const numberInput of [ for (const numberInput of [
@@ -68,17 +56,6 @@ 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; return args;
} }

View File

@@ -1 +0,0 @@
export type CleanLabel = string;

View File

@@ -1 +0,0 @@
export type HumanizedDate = string;

View File

@@ -1 +0,0 @@
export type IsoDateString = string;

View File

@@ -1 +0,0 @@
export type IsoOrRfcDateString = string;

View File

@@ -1,13 +1,13 @@
{ {
"compilerOptions": { "compilerOptions": {
"target": "es6" /* Specify ECMAScript target version: 'ES3' (default), 'ES5', 'ES2015', 'ES2016', 'ES2017', 'ES2018', 'ES2019' or 'ESNEXT'. */, "target": "es6", /* Specify ECMAScript target version: 'ES3' (default), 'ES5', 'ES2015', 'ES2016', 'ES2017', 'ES2018', 'ES2019' or 'ESNEXT'. */
"module": "commonjs" /* Specify module code generation: 'none', 'commonjs', 'amd', 'system', 'umd', 'es2015', or 'ESNext'. */, "module": "commonjs", /* Specify module code generation: 'none', 'commonjs', 'amd', 'system', 'umd', 'es2015', or 'ESNext'. */
"outDir": "./lib" /* Redirect output structure to the directory. */, "outDir": "./lib", /* Redirect output structure to the directory. */
"rootDir": "./src" /* Specify the root directory of input files. Use to control the output directory structure with --outDir. */, "rootDir": "./src", /* Specify the root directory of input files. Use to control the output directory structure with --outDir. */
"strict": true /* Enable all strict type-checking options. */, "strict": true, /* Enable all strict type-checking options. */
"noImplicitAny": true /* Raise error on expressions and declarations with an implied 'any' type. */, "noImplicitAny": true, /* Raise error on expressions and declarations with an implied 'any' type. */
"esModuleInterop": true /* Enables emit interoperability between CommonJS and ES Modules via creation of namespace objects for all imports. Implies 'allowSyntheticDefaultImports'. */ "esModuleInterop": true, /* Enables emit interoperability between CommonJS and ES Modules via creation of namespace objects for all imports. Implies 'allowSyntheticDefaultImports'. */
//"sourceMap": true //"sourceMap": true
}, },
"exclude": ["node_modules", "**/*.test.ts"] "exclude": ["node_modules", "**/*.test.ts"]
} }