mirror of
https://github.com/actions/stale.git
synced 2025-12-24 09:28:18 +00:00
Compare commits
60 Commits
v3.0.16
...
test-dash-
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
be4d1402c1 | ||
|
|
2af88ebbea | ||
|
|
713e405f0c | ||
|
|
2b520f1a1a | ||
|
|
98ed4cb500 | ||
|
|
523075947f | ||
|
|
5a36bdc457 | ||
|
|
4a6aba205d | ||
|
|
2a6d5d6990 | ||
|
|
ebd757c01b | ||
|
|
1ebe9f7454 | ||
|
|
a61d498059 | ||
|
|
0a21a5ec79 | ||
|
|
e7199f9c84 | ||
|
|
e307118008 | ||
|
|
b53e7be65a | ||
|
|
ff764c226b | ||
|
|
c11507e9b7 | ||
|
|
440fb174b5 | ||
|
|
043fbbdea3 | ||
|
|
c70e174d4a | ||
|
|
704929ea5a | ||
|
|
5e20aa8410 | ||
|
|
10eec4583b | ||
|
|
b717aa9f47 | ||
|
|
3b3c3f03cd | ||
|
|
70f07a7b62 | ||
|
|
6a2a52084e | ||
|
|
bea117ba11 | ||
|
|
c181784783 | ||
|
|
93cc018477 | ||
|
|
b80d40901f | ||
|
|
10e1968c4f | ||
|
|
ba1c02f61a | ||
|
|
419a53bc05 | ||
|
|
63ae8ac024 | ||
|
|
8f5f223d0c | ||
|
|
aac59820db | ||
|
|
4109d00d07 | ||
|
|
b3213c1ffa | ||
|
|
12d218f917 | ||
|
|
e1732283c7 | ||
|
|
8439944051 | ||
|
|
0e95ddbecb | ||
|
|
836169b81a | ||
|
|
ec96ff65b0 | ||
|
|
996798eb71 | ||
|
|
6a0398d581 | ||
|
|
7499904a44 | ||
|
|
a8c9fbca3b | ||
|
|
8df2fb36e6 | ||
|
|
d4d9b2a583 | ||
|
|
aee97aa05d | ||
|
|
d6e4b48a0d | ||
|
|
6a493760cf | ||
|
|
07f3f88b6d | ||
|
|
e2a65edb45 | ||
|
|
076138c5c7 | ||
|
|
bf7548e250 | ||
|
|
e96f31f877 |
@@ -14,7 +14,9 @@
|
||||
"@typescript-eslint/no-unused-vars": "error",
|
||||
"@typescript-eslint/explicit-member-accessibility": [
|
||||
"error",
|
||||
{"accessibility": "no-public"}
|
||||
{
|
||||
"accessibility": "no-public"
|
||||
}
|
||||
],
|
||||
"@typescript-eslint/no-require-imports": "error",
|
||||
"@typescript-eslint/array-type": "error",
|
||||
@@ -47,7 +49,13 @@
|
||||
"@typescript-eslint/type-annotation-spacing": "error",
|
||||
"@typescript-eslint/unbound-method": "off",
|
||||
"no-shadow": "off",
|
||||
"@typescript-eslint/no-shadow": "error"
|
||||
"@typescript-eslint/no-shadow": "error",
|
||||
"prettier/prettier": [
|
||||
"error",
|
||||
{
|
||||
"endOfLine": "auto"
|
||||
}
|
||||
]
|
||||
},
|
||||
"env": {
|
||||
"node": true,
|
||||
|
||||
1
.github/ISSUE_TEMPLATE/config.yml
vendored
Normal file
1
.github/ISSUE_TEMPLATE/config.yml
vendored
Normal file
@@ -0,0 +1 @@
|
||||
blank_issues_enabled: false
|
||||
13
.github/ISSUE_TEMPLATE/other_issue_report.md
vendored
Normal file
13
.github/ISSUE_TEMPLATE/other_issue_report.md
vendored
Normal file
@@ -0,0 +1,13 @@
|
||||
---
|
||||
name: Other issue report
|
||||
about: Report other issue
|
||||
title: ''
|
||||
labels: bug
|
||||
assignees: ''
|
||||
---
|
||||
|
||||
## Describe your issue
|
||||
|
||||
## Further context
|
||||
|
||||
<!-- If helpful please provide screenshots, logs, links to other related issues. -->
|
||||
29
.github/ISSUE_TEMPLATE/stale_issue_report.md
vendored
Normal file
29
.github/ISSUE_TEMPLATE/stale_issue_report.md
vendored
Normal file
@@ -0,0 +1,29 @@
|
||||
---
|
||||
name: Stale issue report
|
||||
about: Report issues with using the stale action
|
||||
title: ''
|
||||
labels: bug
|
||||
assignees: ''
|
||||
---
|
||||
|
||||
<!-- Have you tried the [debugging](https://github.com/actions/stale#debugging) section of the readme? -->
|
||||
|
||||
## Describe your issue
|
||||
|
||||
## Your stale action configuration
|
||||
|
||||
<!-- This is an example config, please copy/paste your config into it. -->
|
||||
|
||||
```yml
|
||||
jobs:
|
||||
stale:
|
||||
runs-on: ...
|
||||
steps:
|
||||
- uses: actions/stale@...
|
||||
with:
|
||||
...
|
||||
```
|
||||
|
||||
## Further context
|
||||
|
||||
<!-- If helpful please provide screenshots, logs, links to other related issues. -->
|
||||
8
.github/pull_request_template.md
vendored
Normal file
8
.github/pull_request_template.md
vendored
Normal file
@@ -0,0 +1,8 @@
|
||||
<!-- List the change(s) you're making with this PR. -->
|
||||
## Changes
|
||||
- [x] ...
|
||||
|
||||
## Context
|
||||
|
||||
<!-- Explain why you're making the change(s). -->
|
||||
<!-- If you're closing an issue with this PR, [link them with a keyword](https://docs.github.com/en/github/managing-your-work-on-github/linking-a-pull-request-to-an-issue#linking-a-pull-request-to-an-issue-using-a-keyword). -->
|
||||
2
.github/workflows/codeql.yml
vendored
2
.github/workflows/codeql.yml
vendored
@@ -2,6 +2,8 @@ name: 'Code scanning'
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- main
|
||||
pull_request:
|
||||
schedule:
|
||||
- cron: '0 19 * * 0'
|
||||
|
||||
2
.github/workflows/test.yml
vendored
2
.github/workflows/test.yml
vendored
@@ -12,7 +12,7 @@ jobs:
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- run: |
|
||||
npm install
|
||||
npm ci
|
||||
npm run all
|
||||
test: # make sure the action works on a clean machine without building
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
@@ -6,5 +6,6 @@
|
||||
"singleQuote": true,
|
||||
"trailingComma": "none",
|
||||
"bracketSpacing": false,
|
||||
"arrowParens": "avoid"
|
||||
"arrowParens": "avoid",
|
||||
"endOfLine": "auto"
|
||||
}
|
||||
|
||||
4
.vscode/launch.json
vendored
4
.vscode/launch.json
vendored
@@ -9,7 +9,7 @@
|
||||
"args": [
|
||||
"-i"
|
||||
],
|
||||
"preLaunchTask": "tsc: build - tsconfig.json",
|
||||
"preLaunchTask": "tsc: build - tsconfig.app.json",
|
||||
"internalConsoleOptions": "openOnSessionStart",
|
||||
"console": "integratedTerminal",
|
||||
"outFiles": [
|
||||
@@ -17,4 +17,4 @@
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
7
CHANGELOG.md
Normal file
7
CHANGELOG.md
Normal file
@@ -0,0 +1,7 @@
|
||||
# Changelog
|
||||
|
||||
All notable changes to this project will be documented in this file. See [standard-version](https://github.com/conventional-changelog/standard-version) for commit guidelines.
|
||||
|
||||
### [4.0.1](https://github.com/actions/stale/compare/v3.0.19...v4.0.1) (2021-05-19)
|
||||
|
||||
### [2.0.1](https://github.com/actions/stale/compare/v3.0.19...v2.0.1) (2021-05-19)
|
||||
45
CONTRIBUTING.md
Normal file
45
CONTRIBUTING.md
Normal file
@@ -0,0 +1,45 @@
|
||||
### Building and testing
|
||||
|
||||
Install the dependencies.
|
||||
|
||||
```bash
|
||||
$ npm install
|
||||
```
|
||||
|
||||
Build the typescript and package it for distribution.
|
||||
|
||||
```bash
|
||||
$ npm run build && npm run pack
|
||||
```
|
||||
|
||||
Run the tests :heavy_check_mark:
|
||||
|
||||
```bash
|
||||
$ npm test
|
||||
```
|
||||
|
||||
Run the tests and display only the first failing tests :heavy_check_mark:
|
||||
|
||||
```bash
|
||||
$ npm test:only-errors
|
||||
```
|
||||
|
||||
Run the tests with the watch mode :heavy_check_mark:
|
||||
|
||||
```bash
|
||||
$ npm test:watch
|
||||
```
|
||||
|
||||
Run the linter and fix (almost) every issue for you :heavy_check_mark:
|
||||
|
||||
```bash
|
||||
$ npm lint:all:fix
|
||||
```
|
||||
|
||||
### Before creating a PR
|
||||
|
||||
Build, lint, package and test everything.
|
||||
|
||||
```bash
|
||||
$ npm all
|
||||
```
|
||||
223
README.md
223
README.md
@@ -2,62 +2,89 @@
|
||||
|
||||
Warns and then closes issues and PRs that have had no activity for a specified amount of time.
|
||||
|
||||
### Building and testing
|
||||
## All options
|
||||
|
||||
Install the dependencies
|
||||
### List of options
|
||||
|
||||
```bash
|
||||
$ npm install
|
||||
```
|
||||
Every argument is optional.
|
||||
|
||||
Build the typescript and package it for distribution
|
||||
| Input | Description |
|
||||
| --------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| `repo-token` | PAT (Personal Access Token) for authorizing the repository.<br>_Defaults to **${{ github.token }}**_. |
|
||||
| `days-before-stale` | Idle number of days before marking an issue/PR as stale.<br>_Defaults to **60**_. |
|
||||
| `days-before-issue-stale` | Idle number of days before marking an issue as stale.<br>_Override `days-before-stale`_. |
|
||||
| `days-before-pr-stale` | Idle number of days before marking a PR as stale.<br>_Override `days-before-stale`_. |
|
||||
| `days-before-close` | Idle number of days before closing a stale issue/PR.<br>_Defaults to **7**_. |
|
||||
| `days-before-issue-close` | Idle number of days before closing a stale issue.<br>_Override `days-before-close`_. |
|
||||
| `days-before-pr-close` | Idle number of days before closing a stale PR.<br>_Override `days-before-close`_. |
|
||||
| `stale-issue-message` | Message to post on the stale issue. |
|
||||
| `stale-pr-message` | Message to post on the stale PR. |
|
||||
| `close-issue-message` | Message to post on the stale issue while closing it. |
|
||||
| `close-pr-message` | Message to post on the stale PR while closing it. |
|
||||
| `stale-issue-label` | Label to apply on the stale issue.<br>_Defaults to **Stale**_. |
|
||||
| `close-issue-label` | Label to apply on closing issue.<br>Automatically removed if no longer closed nor locked). |
|
||||
| `stale-pr-label` | Label to apply on the stale PR.<br>_Defaults to **Stale**_. |
|
||||
| `close-pr-label` | Label to apply on the closing PR.<br>Automatically removed if no longer closed nor locked). |
|
||||
| `exempt-issue-labels` | Labels on an issue exempted from being marked as stale. |
|
||||
| `exempt-pr-labels` | Labels on the PR exempted from being marked as stale. |
|
||||
| `only-labels` | Only issues and PRs with ALL these labels are checked.<br>Separate multiple labels with commas (eg. "question,answered"). |
|
||||
| `only-issue-labels` | Only issues with ALL these labels are checked.<br>Separate multiple labels with commas (eg. "question,answered").<br>_Override `only-labels`_. |
|
||||
| `only-pr-labels` | Only PRs with ALL these labels are checked.<br>Separate multiple labels with commas (eg. "question,answered").<br>_Override `only-labels`_. |
|
||||
| `any-of-labels` | Only issues and PRs with ANY of these labels are checked.<br>Separate multiple labels with commas (eg. "incomplete,waiting-feedback"). |
|
||||
| `any-of-issue-labels` | Only issues with ANY of these labels are checked.<br>Separate multiple labels with commas (eg. "incomplete,waiting-feedback").<br>_Override `any-of-labels`_. |
|
||||
| `any-of-pr-labels` | Only PRs with ANY of these labels are checked.<br>Separate multiple labels with commas (eg. "incomplete,waiting-feedback").<br>_Override `any-of-labels`_. |
|
||||
| `operations-per-run` | Maximum number of operations per run.<br>GitHub API CRUD related.<br>_Defaults to **30**_. |
|
||||
| `remove-stale-when-updated` | Remove stale label from issue/PR on updates or comments.<br>_Defaults to **true**_. |
|
||||
| `remove-issue-stale-when-updated` | Remove stale label from issue on updates or comments.<br>_Defaults to **true**_.<br>_Override `remove-stale-when-updated`_. |
|
||||
| `remove-pr-stale-when-updated` | Remove stale label from PR on updates or comments.<br>_Defaults to **true**_.<br>_Override `remove-stale-when-updated`_. |
|
||||
| `remove-issue-stale-when-updated` | Remove stale label from issue on updates or comments.<br>_Defaults to **true**_.<br>_Override `remove-stale-when-updated`_. |
|
||||
| `remove-pr-stale-when-updated` | Remove stale label from PR on updates or comments.<br>_Defaults to **true**_.<br>_Override `remove-stale-when-updated`_. |
|
||||
| `debug-only` | Dry-run on action.<br>_Defaults to **false**_. |
|
||||
| `ascending` | Order to get issues/PR.<br>`true` is ascending, `false` is descending.<br>_Defaults to **false**_. |
|
||||
| `skip-stale-issue-message` | Skip adding stale message on stale issue.<br>_Defaults to **false**_. |
|
||||
| `skip-stale-pr-message` | Skip adding stale message on stale PR.<br>_Defaults to **false**_. |
|
||||
| `start-date` | The date used to skip the stale action on issue/PR created before it.<br>ISO 8601 or RFC 2822. |
|
||||
| `delete-branch` | Delete the git branch after closing a stale pull request.<br>_Defaults to **false**_. |
|
||||
| `exempt-milestones` | Milestones on an issue or a PR exempted from being marked as stale. |
|
||||
| `exempt-issue-milestones` | Milestones on an issue exempted from being marked as stale.<br>_Override `exempt-milestones`_. |
|
||||
| `exempt-pr-milestones` | Milestones on the PR exempted from being marked as stale.<br>_Override `exempt-milestones`_. |
|
||||
| `exempt-all-milestones` | Exempt all issues and PRs with milestones from being marked as stale.<br>_Priority over `exempt-milestones` rules_. |
|
||||
| `exempt-all-issue-milestones` | Exempt all issues with milestones from being marked as stale.<br>_Override `exempt-all-milestones`_. |
|
||||
| `exempt-all-pr-milestones` | Exempt all PRs with milestones from being marked as stale.<br>_Override `exempt-all-milestones`_. |
|
||||
| `exempt-assignees` | Assignees on an issue or a PR exempted from being marked as stale. |
|
||||
| `exempt-issue-assignees` | Assignees on an issue exempted from being marked as stale.<br>_Override `exempt-assignees`_. |
|
||||
| `exempt-pr-assignees` | Assignees on the PR exempted from being marked as stale.<br>_Override `exempt-assignees`_. |
|
||||
| `exempt-all-assignees` | Exempt all issues and PRs with assignees from being marked as stale.<br>_Priority over `exempt-assignees` rules_. |
|
||||
| `exempt-all-issue-assignees` | Exempt all issues with assignees from being marked as stale.<br>_Override `exempt-all-assignees`_. |
|
||||
| `exempt-all-pr-assignees` | Exempt all PRs with assignees from being marked as stale.<br>_Override `exempt-all-assignees`_. |
|
||||
| `enable-statistics` | Display some statistics at the end of the logs regarding the stale workflow.<br>Only when the logs are enabled.<br>_Defaults to **true**_. |
|
||||
|
||||
```bash
|
||||
$ npm run build && npm run pack
|
||||
```
|
||||
### Detailed options
|
||||
|
||||
Run the tests :heavy_check_mark:
|
||||
#### operations-per-run
|
||||
|
||||
```bash
|
||||
$ npm test
|
||||
```
|
||||
_Context:_
|
||||
This action performs some API calls to GitHub to fetch or close issues and pull requests, set or update labels, add comments, delete branches, etc.
|
||||
These operations are made in a very short period of time - because the action is very fast to run - and can be numerous based on your project action configuration and the quantity of issues and pull requests within it.
|
||||
GitHub has a [rate limit](https://docs.github.com/en/rest/overview/resources-in-the-rest-api#rate-limiting) and if reached will block all of these API calls for one hour (or API calls from other actions using the same user (a.k.a: the github-token from the [repo-token](#repo-token) option)).
|
||||
This option helps you to stay within the GitHub rate limits, as you can use this option to limit the number of operations for a single run.
|
||||
|
||||
### Arguments
|
||||
_Purpose:_
|
||||
This option aims to limit the number of operations made with the GitHub API to avoid reaching the [rate limit](https://docs.github.com/en/rest/overview/resources-in-the-rest-api#rate-limiting).
|
||||
|
||||
| Input | Description | Usage |
|
||||
| --------------------------- | -------------------------------------------------------------------------------------------- | -------- |
|
||||
| `repo-token` | PAT(Personal Access Token) for authorizing repository. _Defaults to **${{ github.token }}**_ | Optional |
|
||||
| `days-before-stale` | Idle number of days before marking an issue/pr as stale. _Defaults to **60**_ | Optional |
|
||||
| `days-before-issue-stale` | Idle number of days before marking an issue as stale (override `days-before-stale`). | Optional |
|
||||
| `days-before-pr-stale` | Idle number of days before marking an pr as stale (override `days-before-stale`). | Optional |
|
||||
| `days-before-close` | Idle number of days before closing an stale issue/pr. _Defaults to **7**_ | Optional |
|
||||
| `days-before-issue-close` | Idle number of days before closing an stale issue (override `days-before-close`). | Optional |
|
||||
| `days-before-pr-close` | Idle number of days before closing an stale pr (override `days-before-close`). | Optional |
|
||||
| `stale-issue-message` | Message to post on the stale issue. | Optional |
|
||||
| `stale-pr-message` | Message to post on the stale pr. | Optional |
|
||||
| `close-issue-message` | Message to post on the stale issue while closing it. | Optional |
|
||||
| `close-pr-message` | Message to post on the stale pr while closing it. | Optional |
|
||||
| `stale-issue-label` | Label to apply on the stale issue. _Defaults to **stale**_ | Optional |
|
||||
| `close-issue-label` | Label to apply on closing issue. | Optional |
|
||||
| `stale-pr-label` | Label to apply on the stale pr. | Optional |
|
||||
| `close-pr-label` | Label to apply on the closing pr. | Optional |
|
||||
| `exempt-issue-labels` | Labels on an issue exempted from being marked as stale. | Optional |
|
||||
| `exempt-pr-labels` | Labels on the pr exempted from being marked as stale. | Optional |
|
||||
| `exempt-milestones` | Milestones on an issue or a pr exempted from being marked as stale. | Optional |
|
||||
| `exempt-issue-milestones` | Milestones on an issue exempted from being marked as stale (override `exempt-milestones`). | Optional |
|
||||
| `exempt-pr-milestones` | Milestones on the pr exempted from being marked as stale (override `exempt-milestones`). | 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 |
|
||||
Based on your project, your GitHub business plan and the date of the cron job you set for this action, you can increase this limit to a higher number.
|
||||
If you are not sure which is the right value for you or if the default value is good enough, you could enable the logs and look at the end of the stale action.
|
||||
If you reached the limit, you will see a warning message in the logs, telling you that you should increase the number of operations.
|
||||
If you choose not to increase the limit, you might end up with un-processed issues or pull requests after a stale action run.
|
||||
|
||||
When [debugging](#Debugging), you can set it to a much higher number like `1000` since there will be fewer operations made with the GitHub API.
|
||||
Only the [actor](#repo-token) and the batch of issues (100 per batch) will consume the operations.
|
||||
|
||||
Default value: `30`
|
||||
|
||||
### Usage
|
||||
|
||||
See [action.yml](./action.yml) For comprehensive list of options.
|
||||
See also [action.yml](./action.yml) for a comprehensive list of all the options.
|
||||
|
||||
Basic:
|
||||
|
||||
@@ -96,7 +123,7 @@ jobs:
|
||||
days-before-close: 5
|
||||
```
|
||||
|
||||
Configure different stale timeouts but never close a pr:
|
||||
Configure different stale timeouts but never close a PR:
|
||||
|
||||
```yaml
|
||||
name: 'Close stale issues and PR'
|
||||
@@ -112,7 +139,7 @@ jobs:
|
||||
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.'
|
||||
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
|
||||
@@ -135,9 +162,9 @@ jobs:
|
||||
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.'
|
||||
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.'
|
||||
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
|
||||
@@ -167,7 +194,7 @@ jobs:
|
||||
only-labels: 'awaiting-feedback,awaiting-answers'
|
||||
```
|
||||
|
||||
Configure the stale action to only stale issue/pr created after the 18th april 2020:
|
||||
Configure the stale action to only stale issue/PR created after the 18th april 2020:
|
||||
|
||||
```yaml
|
||||
name: 'Close stale issues and PRs'
|
||||
@@ -202,6 +229,100 @@ jobs:
|
||||
exempt-pr-milestones: 'bugfix,improvement'
|
||||
```
|
||||
|
||||
Avoid stale for all PR with 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-all-pr-milestones: true
|
||||
```
|
||||
|
||||
Check stale for specific labels:
|
||||
|
||||
```yaml
|
||||
name: 'Close stale issues and PRs'
|
||||
on:
|
||||
schedule:
|
||||
- cron: '30 1 * * *'
|
||||
|
||||
jobs:
|
||||
stale:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/stale@v3
|
||||
with:
|
||||
any-of-labels: 'needs-more-info,needs-demo'
|
||||
# You can opt for 'only-labels' instead if your use-case requires all labels
|
||||
# to be present in the issue/PR
|
||||
```
|
||||
|
||||
Avoid stale for specific assignees:
|
||||
|
||||
```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-assignees: 'marco,polo'
|
||||
exempt-pr-assignees: 'marco'
|
||||
```
|
||||
|
||||
Avoid stale for all PR with assignees:
|
||||
|
||||
```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-all-pr-assignees: true
|
||||
```
|
||||
|
||||
### 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.
|
||||
**Logs:**
|
||||
To see the debug output from this action, you must set the secret `ACTIONS_STEP_DEBUG` to `true` in your repository.
|
||||
There is a lot of logs so this can be very helpful!
|
||||
|
||||
**Statistics:**
|
||||
If the logs are enabled, you can also enable the statistics log which will be visible at the end of the logs once all issues were processed.
|
||||
This is very helpful to have a quick understanding of the whole stale workflow.
|
||||
Set `enable-statistics` to `true` in your workflow configuration file.
|
||||
|
||||
**Dry-run:**
|
||||
You can run this action in debug only mode (no actions will be taken on your issues and pull requests) by passing `debug-only` to `true` as an argument to the action.
|
||||
|
||||
**More operations:**
|
||||
You can increase the maximum number of operations per run by passing `operations-per-run` to `1000` for example which will help you to handle more operations in a single stale workflow run.
|
||||
If the `debug-only` option is enabled, this is very helpful because the workflow will (almost) never reach the GitHub API rate, and you will be able to deep-dive into the logs.
|
||||
|
||||
**Job frequency:**
|
||||
You could change the cron job frequency in the stale workflow to run the stale workflow more often.
|
||||
Usually this is not very helpful though.
|
||||
|
||||
### Contributing
|
||||
|
||||
You wish to contribute?
|
||||
Check out the [contributing](CONTRIBUTING.md) file before helping us.
|
||||
|
||||
1152
__tests__/any-of-labels.spec.ts
Normal file
1152
__tests__/any-of-labels.spec.ts
Normal file
File diff suppressed because it is too large
Load Diff
413
__tests__/assignees.spec.ts
Normal file
413
__tests__/assignees.spec.ts
Normal file
@@ -0,0 +1,413 @@
|
||||
import {Issue} from '../src/classes/issue';
|
||||
import {IIssuesProcessorOptions} from '../src/interfaces/issues-processor-options';
|
||||
import {IssuesProcessorMock} from './classes/issues-processor-mock';
|
||||
import {DefaultProcessorOptions} from './constants/default-processor-options';
|
||||
import {generateIssue} from './functions/generate-issue';
|
||||
|
||||
interface ITestData {
|
||||
id: number;
|
||||
isPullRequest: boolean;
|
||||
assignees: string[];
|
||||
exemptAllAssignees: boolean;
|
||||
exemptAllIssueAssignees: boolean | undefined;
|
||||
exemptAllPrAssignees: boolean | undefined;
|
||||
exemptAssignees: string;
|
||||
exemptIssueAssignees: string;
|
||||
exemptPrAssignees: string;
|
||||
shouldStale: boolean;
|
||||
description: string;
|
||||
}
|
||||
|
||||
describe('assignees options', (): void => {
|
||||
let opts: IIssuesProcessorOptions;
|
||||
let testIssueList: Issue[];
|
||||
let processor: IssuesProcessorMock;
|
||||
|
||||
const setTestIssueList = (
|
||||
isPullRequest: boolean,
|
||||
assignees: string[],
|
||||
id: number
|
||||
) => {
|
||||
testIssueList = [
|
||||
generateIssue(
|
||||
opts,
|
||||
id,
|
||||
'My first issue',
|
||||
'2020-01-01T17:00:00Z',
|
||||
'2020-01-01T17:00:00Z',
|
||||
isPullRequest,
|
||||
undefined,
|
||||
undefined,
|
||||
undefined,
|
||||
undefined,
|
||||
assignees
|
||||
)
|
||||
];
|
||||
};
|
||||
|
||||
const setProcessor = () => {
|
||||
processor = new IssuesProcessorMock(
|
||||
opts,
|
||||
async () => 'abot',
|
||||
async p => (p === 1 ? testIssueList : []),
|
||||
async () => [],
|
||||
async () => new Date().toDateString()
|
||||
);
|
||||
};
|
||||
|
||||
beforeEach((): void => {
|
||||
opts = {...DefaultProcessorOptions};
|
||||
});
|
||||
|
||||
describe.each`
|
||||
id | isPullRequest | assignees | exemptAllAssignees | exemptAllIssueAssignees | exemptAllPrAssignees | exemptAssignees | exemptIssueAssignees | exemptPrAssignees | shouldStale | description
|
||||
${100} | ${false} | ${[]} | ${false} | ${undefined} | ${undefined} | ${''} | ${''} | ${''} | ${true} | ${'when the issue does not have an assignee'}
|
||||
${101} | ${false} | ${[]} | ${true} | ${undefined} | ${undefined} | ${''} | ${''} | ${''} | ${true} | ${'when the issue does not have an assignee and only exemptAllAssignees is enabled'}
|
||||
${102} | ${false} | ${[]} | ${false} | ${true} | ${undefined} | ${''} | ${''} | ${''} | ${true} | ${'when the issue does not have an assignee and only exemptAllIssueAssignees is enabled'}
|
||||
${103} | ${false} | ${[]} | ${false} | ${undefined} | ${true} | ${''} | ${''} | ${''} | ${true} | ${'when the issue does not have an assignee and only exemptAllPrAssignees is enabled'}
|
||||
${104} | ${false} | ${[]} | ${true} | ${false} | ${undefined} | ${''} | ${''} | ${''} | ${true} | ${'when the issue does not have an assignee and exemptAllAssignees is enabled and exemptAllIssueAssignees is disabled'}
|
||||
${105} | ${false} | ${[]} | ${true} | ${true} | ${undefined} | ${''} | ${''} | ${''} | ${true} | ${'when the issue does not have an assignee and exemptAllAssignees is enabled and exemptAllIssueAssignees is enabled'}
|
||||
${106} | ${false} | ${[]} | ${true} | ${undefined} | ${false} | ${''} | ${''} | ${''} | ${true} | ${'when the issue does not have an assignee and exemptAllAssignees is enabled and exemptAllPrAssignees is disabled'}
|
||||
${107} | ${false} | ${[]} | ${true} | ${undefined} | ${true} | ${''} | ${''} | ${''} | ${true} | ${'when the issue does not have an assignee and exemptAllAssignees is enabled and exemptAllPrAssignees is enabled'}
|
||||
${108} | ${false} | ${[]} | ${false} | ${false} | ${undefined} | ${''} | ${''} | ${''} | ${true} | ${'when the issue does not have an assignee and exemptAllAssignees is disabled and exemptAllIssueAssignees is disabled'}
|
||||
${109} | ${false} | ${[]} | ${false} | ${true} | ${undefined} | ${''} | ${''} | ${''} | ${true} | ${'when the issue does not have an assignee and exemptAllAssignees is disabled and exemptAllIssueAssignees is enabled'}
|
||||
${200} | ${false} | ${['assignee']} | ${false} | ${undefined} | ${false} | ${''} | ${''} | ${''} | ${true} | ${'when the issue has an assignee and exemptAllAssignees is disabled and exemptAllPrAssignees is disabled'}
|
||||
${201} | ${false} | ${['assignee']} | ${false} | ${undefined} | ${true} | ${''} | ${''} | ${''} | ${true} | ${'when the issue has an assignee and exemptAllAssignees is disabled and exemptAllPrAssignees is enabled'}
|
||||
${202} | ${false} | ${['assignee']} | ${true} | ${undefined} | ${undefined} | ${''} | ${''} | ${''} | ${false} | ${'when the issue has an assignee and only exemptAllAssignees is enabled'}
|
||||
${203} | ${false} | ${['assignee']} | ${false} | ${true} | ${undefined} | ${''} | ${''} | ${''} | ${false} | ${'when the issue has an assignee and only exemptAllIssueAssignees is enabled'}
|
||||
${204} | ${false} | ${['assignee']} | ${false} | ${undefined} | ${true} | ${''} | ${''} | ${''} | ${true} | ${'when the issue has an assignee and only exemptAllPrAssignees is enabled'}
|
||||
${205} | ${false} | ${['assignee']} | ${true} | ${false} | ${undefined} | ${''} | ${''} | ${''} | ${true} | ${'when the issue has an assignee and exemptAllAssignees is enabled and exemptAllIssueAssignees is disabled'}
|
||||
${206} | ${false} | ${['assignee']} | ${true} | ${true} | ${undefined} | ${''} | ${''} | ${''} | ${false} | ${'when the issue has an assignee and exemptAllAssignees is enabled and exemptAllIssueAssignees is enabled'}
|
||||
${207} | ${false} | ${['assignee']} | ${true} | ${undefined} | ${false} | ${''} | ${''} | ${''} | ${false} | ${'when the issue has an assignee and exemptAllAssignees is enabled and exemptAllPrAssignees is disabled'}
|
||||
${208} | ${false} | ${['assignee']} | ${true} | ${undefined} | ${true} | ${''} | ${''} | ${''} | ${false} | ${'when the issue has an assignee and exemptAllAssignees is enabled and exemptAllPrAssignees is enabled'}
|
||||
${209} | ${false} | ${['assignee']} | ${false} | ${false} | ${undefined} | ${''} | ${''} | ${''} | ${true} | ${'when the issue has an assignee and exemptAllAssignees is disabled and exemptAllIssueAssignees is disabled'}
|
||||
${210} | ${false} | ${['assignee']} | ${false} | ${true} | ${undefined} | ${''} | ${''} | ${''} | ${false} | ${'when the issue has an assignee and exemptAllAssignees is disabled and exemptAllIssueAssignees is enabled'}
|
||||
${211} | ${false} | ${['assignee']} | ${false} | ${undefined} | ${false} | ${''} | ${''} | ${''} | ${true} | ${'when the issue has an assignee and exemptAllAssignees is disabled and exemptAllPrAssignees is disabled'}
|
||||
${212} | ${false} | ${['assignee']} | ${false} | ${undefined} | ${true} | ${''} | ${''} | ${''} | ${true} | ${'when the issue has an assignee and exemptAllAssignees is disabled and exemptAllPrAssignees is enabled'}
|
||||
${213} | ${false} | ${['assignee']} | ${false} | ${undefined} | ${false} | ${''} | ${''} | ${''} | ${true} | ${'when the issue has an assignee and exemptAllAssignees is disabled and exemptAllPrAssignees is disabled'}
|
||||
${300} | ${false} | ${['assignee']} | ${false} | ${undefined} | ${false} | ${'bad'} | ${''} | ${''} | ${true} | ${'when the issue has an assignee and exemptAllAssignees is disabled, exemptAllPrAssignees is disabled and exemptAssignees has a different assignee'}
|
||||
${301} | ${false} | ${['assignee']} | ${false} | ${undefined} | ${true} | ${'bad'} | ${''} | ${''} | ${true} | ${'when the issue has an assignee and exemptAllAssignees is disabled, exemptAllPrAssignees is enabled and exemptAssignees has a different assignee'}
|
||||
${302} | ${false} | ${['assignee']} | ${true} | ${undefined} | ${undefined} | ${'bad'} | ${''} | ${''} | ${false} | ${'when the issue has an assignee and exemptAllAssignees is enabled and exemptAssignees has a different assignee'}
|
||||
${303} | ${false} | ${['assignee']} | ${false} | ${true} | ${undefined} | ${'bad'} | ${''} | ${''} | ${false} | ${'when the issue has an assignee and exemptAllIssueAssignees is enabled and exemptAssignees has a different assignee'}
|
||||
${304} | ${false} | ${['assignee']} | ${false} | ${undefined} | ${true} | ${'bad'} | ${''} | ${''} | ${true} | ${'when the issue has an assignee and exemptAllPrAssignees is enabled and exemptAssignees has a different assignee'}
|
||||
${305} | ${false} | ${['assignee']} | ${true} | ${false} | ${undefined} | ${'bad'} | ${''} | ${''} | ${true} | ${'when the issue has an assignee and exemptAllAssignees is enabled, exemptAllIssueAssignees is disabled and exemptAssignees has a different assignee'}
|
||||
${306} | ${false} | ${['assignee']} | ${true} | ${true} | ${undefined} | ${'bad'} | ${''} | ${''} | ${false} | ${'when the issue has an assignee and exemptAllAssignees is enabled, exemptAllIssueAssignees is enabled and exemptAssignees has a different assignee'}
|
||||
${307} | ${false} | ${['assignee']} | ${true} | ${undefined} | ${false} | ${'bad'} | ${''} | ${''} | ${false} | ${'when the issue has an assignee and exemptAllAssignees is enabled, exemptAllPrAssignees is disabled and exemptAssignees has a different assignee'}
|
||||
${308} | ${false} | ${['assignee']} | ${true} | ${undefined} | ${true} | ${'bad'} | ${''} | ${''} | ${false} | ${'when the issue has an assignee and exemptAllAssignees is enabled, exemptAllPrAssignees is enabled and exemptAssignees has a different assignee'}
|
||||
${309} | ${false} | ${['assignee']} | ${false} | ${false} | ${undefined} | ${'bad'} | ${''} | ${''} | ${true} | ${'when the issue has an assignee and exemptAllAssignees is disabled, exemptAllIssueAssignees is disabled and exemptAssignees has a different assignee'}
|
||||
${310} | ${false} | ${['assignee']} | ${false} | ${true} | ${undefined} | ${'bad'} | ${''} | ${''} | ${false} | ${'when the issue has an assignee and exemptAllAssignees is disabled, exemptAllIssueAssignees is enabled and exemptAssignees has a different assignee'}
|
||||
${311} | ${false} | ${['assignee']} | ${false} | ${undefined} | ${false} | ${'bad'} | ${''} | ${''} | ${true} | ${'when the issue has an assignee and exemptAllAssignees is disabled, exemptAllPrAssignees is disabled and exemptAssignees has a different assignee'}
|
||||
${312} | ${false} | ${['assignee']} | ${false} | ${undefined} | ${true} | ${'bad'} | ${''} | ${''} | ${true} | ${'when the issue has an assignee and exemptAllAssignees is disabled, exemptAllPrAssignees is enabled and exemptAssignees has a different assignee'}
|
||||
${313} | ${false} | ${['assignee']} | ${false} | ${undefined} | ${false} | ${'bad'} | ${''} | ${''} | ${true} | ${'when the issue has an assignee and exemptAllAssignees is disabled, exemptAllPrAssignees is disabled and exemptAssignees has a different assignee'}
|
||||
${400} | ${false} | ${['assignee']} | ${false} | ${undefined} | ${false} | ${'assignee'} | ${''} | ${''} | ${false} | ${'when the issue has an assignee and exemptAllAssignees is disabled, exemptAllPrAssignees is disabled and exemptAssignees has the same assignee'}
|
||||
${401} | ${false} | ${['assignee']} | ${false} | ${undefined} | ${true} | ${'assignee'} | ${''} | ${''} | ${false} | ${'when the issue has an assignee and exemptAllAssignees is disabled, exemptAllPrAssignees is enabled and exemptAssignees has the same assignee'}
|
||||
${402} | ${false} | ${['assignee']} | ${true} | ${undefined} | ${undefined} | ${'assignee'} | ${''} | ${''} | ${false} | ${'when the issue has an assignee and exemptAllAssignees is enabled and exemptAssignees has the same assignee'}
|
||||
${403} | ${false} | ${['assignee']} | ${false} | ${true} | ${undefined} | ${'assignee'} | ${''} | ${''} | ${false} | ${'when the issue has an assignee and exemptAllIssueAssignees is enabled and exemptAssignees has the same assignee'}
|
||||
${404} | ${false} | ${['assignee']} | ${false} | ${undefined} | ${true} | ${'assignee'} | ${''} | ${''} | ${false} | ${'when the issue has an assignee and exemptAllPrAssignees is enabled and exemptAssignees has the same assignee'}
|
||||
${405} | ${false} | ${['assignee']} | ${true} | ${false} | ${undefined} | ${'assignee'} | ${''} | ${''} | ${false} | ${'when the issue has an assignee and exemptAllAssignees is enabled, exemptAllIssueAssignees is disabled and exemptAssignees has the same assignee'}
|
||||
${406} | ${false} | ${['assignee']} | ${true} | ${true} | ${undefined} | ${'assignee'} | ${''} | ${''} | ${false} | ${'when the issue has an assignee and exemptAllAssignees is enabled, exemptAllIssueAssignees is enabled and exemptAssignees has the same assignee'}
|
||||
${407} | ${false} | ${['assignee']} | ${true} | ${undefined} | ${false} | ${'assignee'} | ${''} | ${''} | ${false} | ${'when the issue has an assignee and exemptAllAssignees is enabled, exemptAllPrAssignees is disabled and exemptAssignees has the same assignee'}
|
||||
${408} | ${false} | ${['assignee']} | ${true} | ${undefined} | ${true} | ${'assignee'} | ${''} | ${''} | ${false} | ${'when the issue has an assignee and exemptAllAssignees is enabled, exemptAllPrAssignees is enabled and exemptAssignees has the same assignee'}
|
||||
${409} | ${false} | ${['assignee']} | ${false} | ${false} | ${undefined} | ${'assignee'} | ${''} | ${''} | ${false} | ${'when the issue has an assignee and exemptAllAssignees is disabled, exemptAllIssueAssignees is disabled and exemptAssignees has the same assignee'}
|
||||
${410} | ${false} | ${['assignee']} | ${false} | ${true} | ${undefined} | ${'assignee'} | ${''} | ${''} | ${false} | ${'when the issue has an assignee and exemptAllAssignees is disabled, exemptAllIssueAssignees is enabled and exemptAssignees has the same assignee'}
|
||||
${411} | ${false} | ${['assignee']} | ${false} | ${undefined} | ${false} | ${'assignee'} | ${''} | ${''} | ${false} | ${'when the issue has an assignee and exemptAllAssignees is disabled, exemptAllPrAssignees is disabled and exemptAssignees has the same assignee'}
|
||||
${412} | ${false} | ${['assignee']} | ${false} | ${undefined} | ${true} | ${'assignee'} | ${''} | ${''} | ${false} | ${'when the issue has an assignee and exemptAllAssignees is disabled, exemptAllPrAssignees is enabled and exemptAssignees has the same assignee'}
|
||||
${413} | ${false} | ${['assignee']} | ${false} | ${undefined} | ${false} | ${'assignee'} | ${''} | ${''} | ${false} | ${'when the issue has an assignee and exemptAllAssignees is disabled, exemptAllPrAssignees is disabled and exemptAssignees has the same assignee'}
|
||||
${500} | ${false} | ${['assignee']} | ${false} | ${undefined} | ${false} | ${'bad'} | ${'bad'} | ${''} | ${true} | ${'when the issue has an assignee and exemptAllAssignees is disabled, exemptAllPrAssignees is disabled and exemptAssignees and exemptIssueAssignees has a different assignee'}
|
||||
${501} | ${false} | ${['assignee']} | ${false} | ${undefined} | ${true} | ${'bad'} | ${'bad'} | ${''} | ${true} | ${'when the issue has an assignee and exemptAllAssignees is disabled, exemptAllPrAssignees is enabled and exemptAssignees and exemptIssueAssignees has a different assignee'}
|
||||
${502} | ${false} | ${['assignee']} | ${true} | ${undefined} | ${undefined} | ${'bad'} | ${'bad'} | ${''} | ${false} | ${'when the issue has an assignee and exemptAllAssignees is enabled and exemptAssignees and exemptIssueAssignees has a different assignee'}
|
||||
${503} | ${false} | ${['assignee']} | ${false} | ${true} | ${undefined} | ${'bad'} | ${'bad'} | ${''} | ${false} | ${'when the issue has an assignee and exemptAllIssueAssignees is enabled and exemptAssignees and exemptIssueAssignees has a different assignee'}
|
||||
${504} | ${false} | ${['assignee']} | ${false} | ${undefined} | ${true} | ${'bad'} | ${'bad'} | ${''} | ${true} | ${'when the issue has an assignee and exemptAllPrAssignees is enabled and exemptAssignees and exemptIssueAssignees has a different assignee'}
|
||||
${505} | ${false} | ${['assignee']} | ${true} | ${false} | ${undefined} | ${'bad'} | ${'bad'} | ${''} | ${true} | ${'when the issue has an assignee and exemptAllAssignees is enabled, exemptAllIssueAssignees is disabled and exemptAssignees and exemptIssueAssignees has a different assignee'}
|
||||
${506} | ${false} | ${['assignee']} | ${true} | ${true} | ${undefined} | ${'bad'} | ${'bad'} | ${''} | ${false} | ${'when the issue has an assignee and exemptAllAssignees is enabled, exemptAllIssueAssignees is enabled and exemptAssignees and exemptIssueAssignees has a different assignee'}
|
||||
${507} | ${false} | ${['assignee']} | ${true} | ${undefined} | ${false} | ${'bad'} | ${'bad'} | ${''} | ${false} | ${'when the issue has an assignee and exemptAllAssignees is enabled, exemptAllPrAssignees is disabled and exemptAssignees and exemptIssueAssignees has a different assignee'}
|
||||
${508} | ${false} | ${['assignee']} | ${true} | ${undefined} | ${true} | ${'bad'} | ${'bad'} | ${''} | ${false} | ${'when the issue has an assignee and exemptAllAssignees is enabled, exemptAllPrAssignees is enabled and exemptAssignees and exemptIssueAssignees has a different assignee'}
|
||||
${509} | ${false} | ${['assignee']} | ${false} | ${false} | ${undefined} | ${'bad'} | ${'bad'} | ${''} | ${true} | ${'when the issue has an assignee and exemptAllAssignees is disabled, exemptAllIssueAssignees is disabled and exemptAssignees and exemptIssueAssignees has a different assignee'}
|
||||
${510} | ${false} | ${['assignee']} | ${false} | ${true} | ${undefined} | ${'bad'} | ${'bad'} | ${''} | ${false} | ${'when the issue has an assignee and exemptAllAssignees is disabled, exemptAllIssueAssignees is enabled and exemptAssignees and exemptIssueAssignees has a different assignee'}
|
||||
${511} | ${false} | ${['assignee']} | ${false} | ${undefined} | ${false} | ${'bad'} | ${'bad'} | ${''} | ${true} | ${'when the issue has an assignee and exemptAllAssignees is disabled, exemptAllPrAssignees is disabled and exemptAssignees and exemptIssueAssignees has a different assignee'}
|
||||
${512} | ${false} | ${['assignee']} | ${false} | ${undefined} | ${true} | ${'bad'} | ${'bad'} | ${''} | ${true} | ${'when the issue has an assignee and exemptAllAssignees is disabled, exemptAllPrAssignees is enabled and exemptAssignees and exemptIssueAssignees has a different assignee'}
|
||||
${513} | ${false} | ${['assignee']} | ${false} | ${undefined} | ${false} | ${'bad'} | ${'bad'} | ${''} | ${true} | ${'when the issue has an assignee and exemptAllAssignees is disabled, exemptAllPrAssignees is disabled and exemptAssignees and exemptIssueAssignees has a different assignee'}
|
||||
${600} | ${false} | ${['assignee']} | ${false} | ${undefined} | ${false} | ${'bad'} | ${'assignee'} | ${''} | ${false} | ${'when the issue has an assignee and exemptAllAssignees is disabled, exemptAllPrAssignees is disabled, exemptAssignees has a different assignee and exemptIssueAssignees has the same assignee'}
|
||||
${601} | ${false} | ${['assignee']} | ${false} | ${undefined} | ${true} | ${'bad'} | ${'assignee'} | ${''} | ${false} | ${'when the issue has an assignee and exemptAllAssignees is disabled, exemptAllPrAssignees is enabled, exemptAssignees has a different assignee and exemptIssueAssignees has the same assignee'}
|
||||
${602} | ${false} | ${['assignee']} | ${true} | ${undefined} | ${undefined} | ${'bad'} | ${'assignee'} | ${''} | ${false} | ${'when the issue has an assignee and exemptAllAssignees is enabled, exemptAssignees has a different assignee and exemptIssueAssignees has the same assignee'}
|
||||
${603} | ${false} | ${['assignee']} | ${false} | ${true} | ${undefined} | ${'bad'} | ${'assignee'} | ${''} | ${false} | ${'when the issue has an assignee and exemptAllIssueAssignees is enabled, exemptAssignees has a different assignee and exemptIssueAssignees has the same assignee'}
|
||||
${604} | ${false} | ${['assignee']} | ${false} | ${undefined} | ${true} | ${'bad'} | ${'assignee'} | ${''} | ${false} | ${'when the issue has an assignee and exemptAllPrAssignees is enabled, exemptAssignees has a different assignee and exemptIssueAssignees has the same assignee'}
|
||||
${605} | ${false} | ${['assignee']} | ${true} | ${false} | ${undefined} | ${'bad'} | ${'assignee'} | ${''} | ${false} | ${'when the issue has an assignee and exemptAllAssignees is enabled, exemptAllIssueAssignees is disabled, exemptAssignees has a different assignee and exemptIssueAssignees has the same assignee'}
|
||||
${606} | ${false} | ${['assignee']} | ${true} | ${true} | ${undefined} | ${'bad'} | ${'assignee'} | ${''} | ${false} | ${'when the issue has an assignee and exemptAllAssignees is enabled, exemptAllIssueAssignees is enabled, exemptAssignees has a different assignee and exemptIssueAssignees has the same assignee'}
|
||||
${607} | ${false} | ${['assignee']} | ${true} | ${undefined} | ${false} | ${'bad'} | ${'assignee'} | ${''} | ${false} | ${'when the issue has an assignee and exemptAllAssignees is enabled, exemptAllPrAssignees is disabled, exemptAssignees has a different assignee and exemptIssueAssignees has the same assignee'}
|
||||
${608} | ${false} | ${['assignee']} | ${true} | ${undefined} | ${true} | ${'bad'} | ${'assignee'} | ${''} | ${false} | ${'when the issue has an assignee and exemptAllAssignees is enabled, exemptAllPrAssignees is enabled, exemptAssignees has a different assignee and exemptIssueAssignees has the same assignee'}
|
||||
${609} | ${false} | ${['assignee']} | ${false} | ${false} | ${undefined} | ${'bad'} | ${'assignee'} | ${''} | ${false} | ${'when the issue has an assignee and exemptAllAssignees is disabled, exemptAllIssueAssignees is disabled, exemptAssignees has a different assignee and exemptIssueAssignees has the same assignee'}
|
||||
${610} | ${false} | ${['assignee']} | ${false} | ${true} | ${undefined} | ${'bad'} | ${'assignee'} | ${''} | ${false} | ${'when the issue has an assignee and exemptAllAssignees is disabled, exemptAllIssueAssignees is enabled, exemptAssignees has a different assignee and exemptIssueAssignees has the same assignee'}
|
||||
${611} | ${false} | ${['assignee']} | ${false} | ${undefined} | ${false} | ${'bad'} | ${'assignee'} | ${''} | ${false} | ${'when the issue has an assignee and exemptAllAssignees is disabled, exemptAllPrAssignees is disabled, exemptAssignees has a different assignee and exemptIssueAssignees has the same assignee'}
|
||||
${612} | ${false} | ${['assignee']} | ${false} | ${undefined} | ${true} | ${'bad'} | ${'assignee'} | ${''} | ${false} | ${'when the issue has an assignee and exemptAllAssignees is disabled, exemptAllPrAssignees is enabled, exemptAssignees has a different assignee and exemptIssueAssignees has the same assignee'}
|
||||
${613} | ${false} | ${['assignee']} | ${false} | ${undefined} | ${false} | ${'bad'} | ${'assignee'} | ${''} | ${false} | ${'when the issue has an assignee and exemptAllAssignees is disabled, exemptAllPrAssignees is disabled, exemptAssignees has a different assignee and exemptIssueAssignees has the same assignee'}
|
||||
${700} | ${false} | ${['assignee']} | ${false} | ${undefined} | ${false} | ${'bad'} | ${''} | ${'bad'} | ${true} | ${'when the issue has an assignee and exemptAllAssignees is disabled, exemptAllPrAssignees is disabled and exemptAssignees and exemptPrAssignees has a different assignee'}
|
||||
${701} | ${false} | ${['assignee']} | ${false} | ${undefined} | ${true} | ${'bad'} | ${''} | ${'bad'} | ${true} | ${'when the issue has an assignee and exemptAllAssignees is disabled, exemptAllPrAssignees is enabled and exemptAssignees and exemptPrAssignees has a different assignee'}
|
||||
${702} | ${false} | ${['assignee']} | ${true} | ${undefined} | ${undefined} | ${'bad'} | ${''} | ${'bad'} | ${false} | ${'when the issue has an assignee and exemptAllAssignees is enabled and exemptAssignees and exemptPrAssignees has a different assignee'}
|
||||
${703} | ${false} | ${['assignee']} | ${false} | ${true} | ${undefined} | ${'bad'} | ${''} | ${'bad'} | ${false} | ${'when the issue has an assignee and exemptAllIssueAssignees is enabled and exemptAssignees and exemptPrAssignees has a different assignee'}
|
||||
${704} | ${false} | ${['assignee']} | ${false} | ${undefined} | ${true} | ${'bad'} | ${''} | ${'bad'} | ${true} | ${'when the issue has an assignee and exemptAllPrAssignees is enabled and exemptAssignees and exemptPrAssignees has a different assignee'}
|
||||
${705} | ${false} | ${['assignee']} | ${true} | ${false} | ${undefined} | ${'bad'} | ${''} | ${'bad'} | ${true} | ${'when the issue has an assignee and exemptAllAssignees is enabled, exemptAllIssueAssignees is disabled and exemptAssignees and exemptPrAssignees has a different assignee'}
|
||||
${706} | ${false} | ${['assignee']} | ${true} | ${true} | ${undefined} | ${'bad'} | ${''} | ${'bad'} | ${false} | ${'when the issue has an assignee and exemptAllAssignees is enabled, exemptAllIssueAssignees is enabled and exemptAssignees and exemptPrAssignees has a different assignee'}
|
||||
${707} | ${false} | ${['assignee']} | ${true} | ${undefined} | ${false} | ${'bad'} | ${''} | ${'bad'} | ${false} | ${'when the issue has an assignee and exemptAllAssignees is enabled, exemptAllPrAssignees is disabled and exemptAssignees and exemptPrAssignees has a different assignee'}
|
||||
${708} | ${false} | ${['assignee']} | ${true} | ${undefined} | ${true} | ${'bad'} | ${''} | ${'bad'} | ${false} | ${'when the issue has an assignee and exemptAllAssignees is enabled, exemptAllPrAssignees is enabled and exemptAssignees and exemptPrAssignees has a different assignee'}
|
||||
${709} | ${false} | ${['assignee']} | ${false} | ${false} | ${undefined} | ${'bad'} | ${''} | ${'bad'} | ${true} | ${'when the issue has an assignee and exemptAllAssignees is disabled, exemptAllIssueAssignees is disabled and exemptAssignees and exemptPrAssignees has a different assignee'}
|
||||
${710} | ${false} | ${['assignee']} | ${false} | ${true} | ${undefined} | ${'bad'} | ${''} | ${'bad'} | ${false} | ${'when the issue has an assignee and exemptAllAssignees is disabled, exemptAllIssueAssignees is enabled and exemptAssignees and exemptPrAssignees has a different assignee'}
|
||||
${711} | ${false} | ${['assignee']} | ${false} | ${undefined} | ${false} | ${'bad'} | ${''} | ${'bad'} | ${true} | ${'when the issue has an assignee and exemptAllAssignees is disabled, exemptAllPrAssignees is disabled and exemptAssignees and exemptPrAssignees has a different assignee'}
|
||||
${712} | ${false} | ${['assignee']} | ${false} | ${undefined} | ${true} | ${'bad'} | ${''} | ${'bad'} | ${true} | ${'when the issue has an assignee and exemptAllAssignees is disabled, exemptAllPrAssignees is enabled and exemptAssignees and exemptPrAssignees has a different assignee'}
|
||||
${713} | ${false} | ${['assignee']} | ${false} | ${undefined} | ${false} | ${'bad'} | ${''} | ${'bad'} | ${true} | ${'when the issue has an assignee and exemptAllAssignees is disabled, exemptAllPrAssignees is disabled and exemptAssignees and exemptPrAssignees has a different assignee'}
|
||||
${800} | ${false} | ${['assignee']} | ${false} | ${undefined} | ${false} | ${'bad'} | ${''} | ${'assignee'} | ${true} | ${'when the issue has an assignee and exemptAllAssignees is disabled, exemptAllPrAssignees is disabled, exemptAssignees has a different assignee and exemptPrAssignees has the same assignee'}
|
||||
${801} | ${false} | ${['assignee']} | ${false} | ${undefined} | ${true} | ${'bad'} | ${''} | ${'assignee'} | ${true} | ${'when the issue has an assignee and exemptAllAssignees is disabled, exemptAllPrAssignees is enabled, exemptAssignees has a different assignee and exemptPrAssignees has the same assignee'}
|
||||
${802} | ${false} | ${['assignee']} | ${true} | ${undefined} | ${undefined} | ${'bad'} | ${''} | ${'assignee'} | ${false} | ${'when the issue has an assignee and exemptAllAssignees is enabled, exemptAssignees has a different assignee and exemptPrAssignees has the same assignee'}
|
||||
${803} | ${false} | ${['assignee']} | ${false} | ${true} | ${undefined} | ${'bad'} | ${''} | ${'assignee'} | ${false} | ${'when the issue has an assignee and exemptAllIssueAssignees is enabled, exemptAssignees has a different assignee and exemptPrAssignees has the same assignee'}
|
||||
${804} | ${false} | ${['assignee']} | ${false} | ${undefined} | ${true} | ${'bad'} | ${''} | ${'assignee'} | ${true} | ${'when the issue has an assignee and exemptAllPrAssignees is enabled, exemptAssignees has a different assignee and exemptPrAssignees has the same assignee'}
|
||||
${805} | ${false} | ${['assignee']} | ${true} | ${false} | ${undefined} | ${'bad'} | ${''} | ${'assignee'} | ${true} | ${'when the issue has an assignee and exemptAllAssignees is enabled, exemptAllIssueAssignees is disabled, exemptAssignees has a different assignee and exemptPrAssignees has the same assignee'}
|
||||
${806} | ${false} | ${['assignee']} | ${true} | ${true} | ${undefined} | ${'bad'} | ${''} | ${'assignee'} | ${false} | ${'when the issue has an assignee and exemptAllAssignees is enabled, exemptAllIssueAssignees is enabled, exemptAssignees has a different assignee and exemptPrAssignees has the same assignee'}
|
||||
${807} | ${false} | ${['assignee']} | ${true} | ${undefined} | ${false} | ${'bad'} | ${''} | ${'assignee'} | ${false} | ${'when the issue has an assignee and exemptAllAssignees is enabled, exemptAllPrAssignees is disabled, exemptAssignees has a different assignee and exemptPrAssignees has the same assignee'}
|
||||
${808} | ${false} | ${['assignee']} | ${true} | ${undefined} | ${true} | ${'bad'} | ${''} | ${'assignee'} | ${false} | ${'when the issue has an assignee and exemptAllAssignees is enabled, exemptAllPrAssignees is enabled, exemptAssignees has a different assignee and exemptPrAssignees has the same assignee'}
|
||||
${809} | ${false} | ${['assignee']} | ${false} | ${false} | ${undefined} | ${'bad'} | ${''} | ${'assignee'} | ${true} | ${'when the issue has an assignee and exemptAllAssignees is disabled, exemptAllIssueAssignees is disabled, exemptAssignees has a different assignee and exemptPrAssignees has the same assignee'}
|
||||
${810} | ${false} | ${['assignee']} | ${false} | ${true} | ${undefined} | ${'bad'} | ${''} | ${'assignee'} | ${false} | ${'when the issue has an assignee and exemptAllAssignees is disabled, exemptAllIssueAssignees is enabled, exemptAssignees has a different assignee and exemptPrAssignees has the same assignee'}
|
||||
${811} | ${false} | ${['assignee']} | ${false} | ${undefined} | ${false} | ${'bad'} | ${''} | ${'assignee'} | ${true} | ${'when the issue has an assignee and exemptAllAssignees is disabled, exemptAllPrAssignees is disabled, exemptAssignees has a different assignee and exemptPrAssignees has the same assignee'}
|
||||
${812} | ${false} | ${['assignee']} | ${false} | ${undefined} | ${true} | ${'bad'} | ${''} | ${'assignee'} | ${true} | ${'when the issue has an assignee and exemptAllAssignees is disabled, exemptAllPrAssignees is enabled, exemptAssignees has a different assignee and exemptPrAssignees has the same assignee'}
|
||||
${813} | ${false} | ${['assignee']} | ${false} | ${undefined} | ${false} | ${'bad'} | ${''} | ${'assignee'} | ${true} | ${'when the issue has an assignee and exemptAllAssignees is disabled, exemptAllPrAssignees is disabled, exemptAssignees has a different assignee and exemptPrAssignees has the same assignee'}
|
||||
${900} | ${false} | ${['assignee']} | ${false} | ${undefined} | ${false} | ${'assignee'} | ${'bad'} | ${''} | ${true} | ${'when the issue has an assignee and exemptAllAssignees is disabled, exemptAllPrAssignees is disabled, exemptAssignees has the same assignee and exemptIssueAssignees has a different assignee'}
|
||||
${901} | ${false} | ${['assignee']} | ${false} | ${undefined} | ${true} | ${'assignee'} | ${'bad'} | ${''} | ${true} | ${'when the issue has an assignee and exemptAllAssignees is disabled, exemptAllPrAssignees is enabled, exemptAssignees has the same assignee and exemptIssueAssignees has a different assignee'}
|
||||
${902} | ${false} | ${['assignee']} | ${true} | ${undefined} | ${undefined} | ${'assignee'} | ${'bad'} | ${''} | ${false} | ${'when the issue has an assignee and exemptAllAssignees is enabled, exemptAssignees has the same assignee and exemptIssueAssignees has a different assignee'}
|
||||
${903} | ${false} | ${['assignee']} | ${false} | ${true} | ${undefined} | ${'assignee'} | ${'bad'} | ${''} | ${false} | ${'when the issue has an assignee and exemptAllIssueAssignees is enabled, exemptAssignees has the same assignee and exemptIssueAssignees has a different assignee'}
|
||||
${904} | ${false} | ${['assignee']} | ${false} | ${undefined} | ${true} | ${'assignee'} | ${'bad'} | ${''} | ${true} | ${'when the issue has an assignee and exemptAllPrAssignees is enabled, exemptAssignees has the same assignee and exemptIssueAssignees has a different assignee'}
|
||||
${905} | ${false} | ${['assignee']} | ${true} | ${false} | ${undefined} | ${'assignee'} | ${'bad'} | ${''} | ${true} | ${'when the issue has an assignee and exemptAllAssignees is enabled, exemptAllIssueAssignees is disabled, exemptAssignees has the same assignee and exemptIssueAssignees has a different assignee'}
|
||||
${906} | ${false} | ${['assignee']} | ${true} | ${true} | ${undefined} | ${'assignee'} | ${'bad'} | ${''} | ${false} | ${'when the issue has an assignee and exemptAllAssignees is enabled, exemptAllIssueAssignees is enabled, exemptAssignees has the same assignee and exemptIssueAssignees has a different assignee'}
|
||||
${907} | ${false} | ${['assignee']} | ${true} | ${undefined} | ${false} | ${'assignee'} | ${'bad'} | ${''} | ${false} | ${'when the issue has an assignee and exemptAllAssignees is enabled, exemptAllPrAssignees is disabled, exemptAssignees has the same assignee and exemptIssueAssignees has a different assignee'}
|
||||
${908} | ${false} | ${['assignee']} | ${true} | ${undefined} | ${true} | ${'assignee'} | ${'bad'} | ${''} | ${false} | ${'when the issue has an assignee and exemptAllAssignees is enabled, exemptAllPrAssignees is enabled, exemptAssignees has the same assignee and exemptIssueAssignees has a different assignee'}
|
||||
${909} | ${false} | ${['assignee']} | ${false} | ${false} | ${undefined} | ${'assignee'} | ${'bad'} | ${''} | ${true} | ${'when the issue has an assignee and exemptAllAssignees is disabled, exemptAllIssueAssignees is disabled, exemptAssignees has the same assignee and exemptIssueAssignees has a different assignee'}
|
||||
${910} | ${false} | ${['assignee']} | ${false} | ${true} | ${undefined} | ${'assignee'} | ${'bad'} | ${''} | ${false} | ${'when the issue has an assignee and exemptAllAssignees is disabled, exemptAllIssueAssignees is enabled, exemptAssignees has the same assignee and exemptIssueAssignees has a different assignee'}
|
||||
${911} | ${false} | ${['assignee']} | ${false} | ${undefined} | ${false} | ${'assignee'} | ${'bad'} | ${''} | ${true} | ${'when the issue has an assignee and exemptAllAssignees is disabled, exemptAllPrAssignees is disabled, exemptAssignees has the same assignee and exemptIssueAssignees has a different assignee'}
|
||||
${912} | ${false} | ${['assignee']} | ${false} | ${undefined} | ${true} | ${'assignee'} | ${'bad'} | ${''} | ${true} | ${'when the issue has an assignee and exemptAllAssignees is disabled, exemptAllPrAssignees is enabled, exemptAssignees has the same assignee and exemptIssueAssignees has a different assignee'}
|
||||
${913} | ${false} | ${['assignee']} | ${false} | ${undefined} | ${false} | ${'assignee'} | ${'bad'} | ${''} | ${true} | ${'when the issue has an assignee and exemptAllAssignees is disabled, exemptAllPrAssignees is disabled, exemptAssignees has the same assignee and exemptIssueAssignees has a different assignee'}
|
||||
${1000} | ${false} | ${['assignee']} | ${false} | ${undefined} | ${false} | ${'assignee'} | ${'assignee'} | ${''} | ${false} | ${'when the issue has an assignee and exemptAllAssignees is disabled, exemptAllPrAssignees is disabled, exemptAssignees has the same assignee and exemptIssueAssignees has the same assignee'}
|
||||
${1001} | ${false} | ${['assignee']} | ${false} | ${undefined} | ${true} | ${'assignee'} | ${'assignee'} | ${''} | ${false} | ${'when the issue has an assignee and exemptAllAssignees is disabled, exemptAllPrAssignees is enabled, exemptAssignees has the same assignee and exemptIssueAssignees has the same assignee'}
|
||||
${1002} | ${false} | ${['assignee']} | ${true} | ${undefined} | ${undefined} | ${'assignee'} | ${'assignee'} | ${''} | ${false} | ${'when the issue has an assignee and exemptAllAssignees is enabled, exemptAssignees has the same assignee and exemptIssueAssignees has the same assignee'}
|
||||
${1003} | ${false} | ${['assignee']} | ${false} | ${true} | ${undefined} | ${'assignee'} | ${'assignee'} | ${''} | ${false} | ${'when the issue has an assignee and exemptAllIssueAssignees is enabled, exemptAssignees has the same assignee and exemptIssueAssignees has the same assignee'}
|
||||
${1004} | ${false} | ${['assignee']} | ${false} | ${undefined} | ${true} | ${'assignee'} | ${'assignee'} | ${''} | ${false} | ${'when the issue has an assignee and exemptAllPrAssignees is enabled, exemptAssignees has the same assignee and exemptIssueAssignees has the same assignee'}
|
||||
${1005} | ${false} | ${['assignee']} | ${true} | ${false} | ${undefined} | ${'assignee'} | ${'assignee'} | ${''} | ${false} | ${'when the issue has an assignee and exemptAllAssignees is enabled, exemptAllIssueAssignees is disabled, exemptAssignees has the same assignee and exemptIssueAssignees has the same assignee'}
|
||||
${1006} | ${false} | ${['assignee']} | ${true} | ${true} | ${undefined} | ${'assignee'} | ${'assignee'} | ${''} | ${false} | ${'when the issue has an assignee and exemptAllAssignees is enabled, exemptAllIssueAssignees is enabled, exemptAssignees has the same assignee and exemptIssueAssignees has the same assignee'}
|
||||
${1007} | ${false} | ${['assignee']} | ${true} | ${undefined} | ${false} | ${'assignee'} | ${'assignee'} | ${''} | ${false} | ${'when the issue has an assignee and exemptAllAssignees is enabled, exemptAllPrAssignees is disabled, exemptAssignees has the same assignee and exemptIssueAssignees has the same assignee'}
|
||||
${1008} | ${false} | ${['assignee']} | ${true} | ${undefined} | ${true} | ${'assignee'} | ${'assignee'} | ${''} | ${false} | ${'when the issue has an assignee and exemptAllAssignees is enabled, exemptAllPrAssignees is enabled, exemptAssignees has the same assignee and exemptIssueAssignees has the same assignee'}
|
||||
${1009} | ${false} | ${['assignee']} | ${false} | ${false} | ${undefined} | ${'assignee'} | ${'assignee'} | ${''} | ${false} | ${'when the issue has an assignee and exemptAllAssignees is disabled, exemptAllIssueAssignees is disabled, exemptAssignees has the same assignee and exemptIssueAssignees has the same assignee'}
|
||||
${1010} | ${false} | ${['assignee']} | ${false} | ${true} | ${undefined} | ${'assignee'} | ${'assignee'} | ${''} | ${false} | ${'when the issue has an assignee and exemptAllAssignees is disabled, exemptAllIssueAssignees is enabled, exemptAssignees has the same assignee and exemptIssueAssignees has the same assignee'}
|
||||
${1011} | ${false} | ${['assignee']} | ${false} | ${undefined} | ${false} | ${'assignee'} | ${'assignee'} | ${''} | ${false} | ${'when the issue has an assignee and exemptAllAssignees is disabled, exemptAllPrAssignees is disabled, exemptAssignees has the same assignee and exemptIssueAssignees has the same assignee'}
|
||||
${1012} | ${false} | ${['assignee']} | ${false} | ${undefined} | ${true} | ${'assignee'} | ${'assignee'} | ${''} | ${false} | ${'when the issue has an assignee and exemptAllAssignees is disabled, exemptAllPrAssignees is enabled, exemptAssignees has the same assignee and exemptIssueAssignees has the same assignee'}
|
||||
${1013} | ${false} | ${['assignee']} | ${false} | ${undefined} | ${false} | ${'assignee'} | ${'assignee'} | ${''} | ${false} | ${'when the issue has an assignee and exemptAllAssignees is disabled, exemptAllPrAssignees is disabled, exemptAssignees has the same assignee and exemptIssueAssignees has the same assignee'}
|
||||
${1100} | ${false} | ${['assignee']} | ${false} | ${undefined} | ${false} | ${'assignee'} | ${''} | ${'bad'} | ${false} | ${'when the issue has an assignee and exemptAllAssignees is disabled, exemptAllPrAssignees is disabled, exemptAssignees has the same assignee and exemptPrAssignees has a different assignee'}
|
||||
${1101} | ${false} | ${['assignee']} | ${false} | ${undefined} | ${true} | ${'assignee'} | ${''} | ${'bad'} | ${false} | ${'when the issue has an assignee and exemptAllAssignees is disabled, exemptAllPrAssignees is enabled, exemptAssignees has the same assignee and exemptPrAssignees has a different assignee'}
|
||||
${1102} | ${false} | ${['assignee']} | ${true} | ${undefined} | ${undefined} | ${'assignee'} | ${''} | ${'bad'} | ${false} | ${'when the issue has an assignee and exemptAllAssignees is enabled, exemptAssignees has the same assignee and exemptPrAssignees has a different assignee'}
|
||||
${1103} | ${false} | ${['assignee']} | ${false} | ${true} | ${undefined} | ${'assignee'} | ${''} | ${'bad'} | ${false} | ${'when the issue has an assignee and exemptAllIssueAssignees is enabled, exemptAssignees has the same assignee and exemptPrAssignees has a different assignee'}
|
||||
${1104} | ${false} | ${['assignee']} | ${false} | ${undefined} | ${true} | ${'assignee'} | ${''} | ${'bad'} | ${false} | ${'when the issue has an assignee and exemptAllPrAssignees is enabled, exemptAssignees has the same assignee and exemptPrAssignees has a different assignee'}
|
||||
${1105} | ${false} | ${['assignee']} | ${true} | ${false} | ${undefined} | ${'assignee'} | ${''} | ${'bad'} | ${false} | ${'when the issue has an assignee and exemptAllAssignees is enabled, exemptAllIssueAssignees is disabled, exemptAssignees has the same assignee and exemptPrAssignees has a different assignee'}
|
||||
${1106} | ${false} | ${['assignee']} | ${true} | ${true} | ${undefined} | ${'assignee'} | ${''} | ${'bad'} | ${false} | ${'when the issue has an assignee and exemptAllAssignees is enabled, exemptAllIssueAssignees is enabled, exemptAssignees has the same assignee and exemptPrAssignees has a different assignee'}
|
||||
${1107} | ${false} | ${['assignee']} | ${true} | ${undefined} | ${false} | ${'assignee'} | ${''} | ${'bad'} | ${false} | ${'when the issue has an assignee and exemptAllAssignees is enabled, exemptAllPrAssignees is disabled, exemptAssignees has the same assignee and exemptPrAssignees has a different assignee'}
|
||||
${1108} | ${false} | ${['assignee']} | ${true} | ${undefined} | ${true} | ${'assignee'} | ${''} | ${'bad'} | ${false} | ${'when the issue has an assignee and exemptAllAssignees is enabled, exemptAllPrAssignees is enabled, exemptAssignees has the same assignee and exemptPrAssignees has a different assignee'}
|
||||
${1109} | ${false} | ${['assignee']} | ${false} | ${false} | ${undefined} | ${'assignee'} | ${''} | ${'bad'} | ${false} | ${'when the issue has an assignee and exemptAllAssignees is disabled, exemptAllIssueAssignees is disabled, exemptAssignees has the same assignee and exemptPrAssignees has a different assignee'}
|
||||
${1110} | ${false} | ${['assignee']} | ${false} | ${true} | ${undefined} | ${'assignee'} | ${''} | ${'bad'} | ${false} | ${'when the issue has an assignee and exemptAllAssignees is disabled, exemptAllIssueAssignees is enabled, exemptAssignees has the same assignee and exemptPrAssignees has a different assignee'}
|
||||
${1111} | ${false} | ${['assignee']} | ${false} | ${undefined} | ${false} | ${'assignee'} | ${''} | ${'bad'} | ${false} | ${'when the issue has an assignee and exemptAllAssignees is disabled, exemptAllPrAssignees is disabled, exemptAssignees has the same assignee and exemptPrAssignees has a different assignee'}
|
||||
${1112} | ${false} | ${['assignee']} | ${false} | ${undefined} | ${true} | ${'assignee'} | ${''} | ${'bad'} | ${false} | ${'when the issue has an assignee and exemptAllAssignees is disabled, exemptAllPrAssignees is enabled, exemptAssignees has the same assignee and exemptPrAssignees has a different assignee'}
|
||||
${1113} | ${false} | ${['assignee']} | ${false} | ${undefined} | ${false} | ${'assignee'} | ${''} | ${'bad'} | ${false} | ${'when the issue has an assignee and exemptAllAssignees is disabled, exemptAllPrAssignees is disabled, exemptAssignees has the same assignee and exemptPrAssignees has a different assignee'}
|
||||
${1200} | ${false} | ${['assignee']} | ${false} | ${undefined} | ${false} | ${'assignee'} | ${''} | ${'assignee'} | ${false} | ${'when the issue has an assignee and exemptAllAssignees is disabled, exemptAllPrAssignees is disabled, exemptAssignees has the same assignee and exemptPrAssignees has the same assignee'}
|
||||
${1201} | ${false} | ${['assignee']} | ${false} | ${undefined} | ${true} | ${'assignee'} | ${''} | ${'assignee'} | ${false} | ${'when the issue has an assignee and exemptAllAssignees is disabled, exemptAllPrAssignees is enabled, exemptAssignees has the same assignee and exemptPrAssignees has the same assignee'}
|
||||
${1202} | ${false} | ${['assignee']} | ${true} | ${undefined} | ${undefined} | ${'assignee'} | ${''} | ${'assignee'} | ${false} | ${'when the issue has an assignee and exemptAllAssignees is enabled, exemptAssignees has the same assignee and exemptPrAssignees has the same assignee'}
|
||||
${1203} | ${false} | ${['assignee']} | ${false} | ${true} | ${undefined} | ${'assignee'} | ${''} | ${'assignee'} | ${false} | ${'when the issue has an assignee and exemptAllIssueAssignees is enabled, exemptAssignees has the same assignee and exemptPrAssignees has the same assignee'}
|
||||
${1204} | ${false} | ${['assignee']} | ${false} | ${undefined} | ${true} | ${'assignee'} | ${''} | ${'assignee'} | ${false} | ${'when the issue has an assignee and exemptAllPrAssignees is enabled, exemptAssignees has the same assignee and exemptPrAssignees has the same assignee'}
|
||||
${1205} | ${false} | ${['assignee']} | ${true} | ${false} | ${undefined} | ${'assignee'} | ${''} | ${'assignee'} | ${false} | ${'when the issue has an assignee and exemptAllAssignees is enabled, exemptAllIssueAssignees is disabled, exemptAssignees has the same assignee and exemptPrAssignees has the same assignee'}
|
||||
${1206} | ${false} | ${['assignee']} | ${true} | ${true} | ${undefined} | ${'assignee'} | ${''} | ${'assignee'} | ${false} | ${'when the issue has an assignee and exemptAllAssignees is enabled, exemptAllIssueAssignees is enabled, exemptAssignees has the same assignee and exemptPrAssignees has the same assignee'}
|
||||
${1207} | ${false} | ${['assignee']} | ${true} | ${undefined} | ${false} | ${'assignee'} | ${''} | ${'assignee'} | ${false} | ${'when the issue has an assignee and exemptAllAssignees is enabled, exemptAllPrAssignees is disabled, exemptAssignees has the same assignee and exemptPrAssignees has the same assignee'}
|
||||
${1208} | ${false} | ${['assignee']} | ${true} | ${undefined} | ${true} | ${'assignee'} | ${''} | ${'assignee'} | ${false} | ${'when the issue has an assignee and exemptAllAssignees is enabled, exemptAllPrAssignees is enabled, exemptAssignees has the same assignee and exemptPrAssignees has the same assignee'}
|
||||
${1209} | ${false} | ${['assignee']} | ${false} | ${false} | ${undefined} | ${'assignee'} | ${''} | ${'assignee'} | ${false} | ${'when the issue has an assignee and exemptAllAssignees is disabled, exemptAllIssueAssignees is disabled, exemptAssignees has the same assignee and exemptPrAssignees has the same assignee'}
|
||||
${1210} | ${false} | ${['assignee']} | ${false} | ${true} | ${undefined} | ${'assignee'} | ${''} | ${'assignee'} | ${false} | ${'when the issue has an assignee and exemptAllAssignees is disabled, exemptAllIssueAssignees is enabled, exemptAssignees has the same assignee and exemptPrAssignees has the same assignee'}
|
||||
${1211} | ${false} | ${['assignee']} | ${false} | ${undefined} | ${false} | ${'assignee'} | ${''} | ${'assignee'} | ${false} | ${'when the issue has an assignee and exemptAllAssignees is disabled, exemptAllPrAssignees is disabled, exemptAssignees has the same assignee and exemptPrAssignees has the same assignee'}
|
||||
${1212} | ${false} | ${['assignee']} | ${false} | ${undefined} | ${true} | ${'assignee'} | ${''} | ${'assignee'} | ${false} | ${'when the issue has an assignee and exemptAllAssignees is disabled, exemptAllPrAssignees is enabled, exemptAssignees has the same assignee and exemptPrAssignees has the same assignee'}
|
||||
${1213} | ${false} | ${['assignee']} | ${false} | ${undefined} | ${false} | ${'assignee'} | ${''} | ${'assignee'} | ${false} | ${'when the issue has an assignee and exemptAllAssignees is disabled, exemptAllPrAssignees is disabled, exemptAssignees has the same assignee and exemptPrAssignees has the same assignee'}
|
||||
${1300} | ${true} | ${[]} | ${false} | ${undefined} | ${undefined} | ${''} | ${''} | ${''} | ${true} | ${'when the pull request does not have an assignee'}
|
||||
${1301} | ${true} | ${[]} | ${true} | ${undefined} | ${undefined} | ${''} | ${''} | ${''} | ${true} | ${'when the pull request does not have an assignee and only exemptAllAssignees is enabled'}
|
||||
${1302} | ${true} | ${[]} | ${false} | ${true} | ${undefined} | ${''} | ${''} | ${''} | ${true} | ${'when the pull request does not have an assignee and only exemptAllIssueAssignees is enabled'}
|
||||
${1303} | ${true} | ${[]} | ${false} | ${undefined} | ${true} | ${''} | ${''} | ${''} | ${true} | ${'when the pull request does not have an assignee and only exemptAllPrAssignees is enabled'}
|
||||
${1304} | ${true} | ${[]} | ${true} | ${false} | ${undefined} | ${''} | ${''} | ${''} | ${true} | ${'when the pull request does not have an assignee and exemptAllAssignees is enabled and exemptAllIssueAssignees is disabled'}
|
||||
${1305} | ${true} | ${[]} | ${true} | ${true} | ${undefined} | ${''} | ${''} | ${''} | ${true} | ${'when the pull request does not have an assignee and exemptAllAssignees is enabled and exemptAllIssueAssignees is enabled'}
|
||||
${1306} | ${true} | ${[]} | ${true} | ${undefined} | ${false} | ${''} | ${''} | ${''} | ${true} | ${'when the pull request does not have an assignee and exemptAllAssignees is enabled and exemptAllPrAssignees is disabled'}
|
||||
${1307} | ${true} | ${[]} | ${true} | ${undefined} | ${true} | ${''} | ${''} | ${''} | ${true} | ${'when the pull request does not have an assignee and exemptAllAssignees is enabled and exemptAllPrAssignees is enabled'}
|
||||
${1308} | ${true} | ${[]} | ${false} | ${false} | ${undefined} | ${''} | ${''} | ${''} | ${true} | ${'when the pull request does not have an assignee and exemptAllAssignees is disabled and exemptAllIssueAssignees is disabled'}
|
||||
${1309} | ${true} | ${[]} | ${false} | ${true} | ${undefined} | ${''} | ${''} | ${''} | ${true} | ${'when the pull request does not have an assignee and exemptAllAssignees is disabled and exemptAllIssueAssignees is enabled'}
|
||||
${1400} | ${true} | ${['assignee']} | ${false} | ${undefined} | ${false} | ${''} | ${''} | ${''} | ${true} | ${'when the pull request has an assignee and exemptAllAssignees is disabled and exemptAllPrAssignees is disabled'}
|
||||
${1401} | ${true} | ${['assignee']} | ${false} | ${undefined} | ${true} | ${''} | ${''} | ${''} | ${false} | ${'when the pull request has an assignee and exemptAllAssignees is disabled and exemptAllPrAssignees is enabled'}
|
||||
${1402} | ${true} | ${['assignee']} | ${true} | ${undefined} | ${undefined} | ${''} | ${''} | ${''} | ${false} | ${'when the pull request has an assignee and only exemptAllAssignees is enabled'}
|
||||
${1403} | ${true} | ${['assignee']} | ${false} | ${true} | ${undefined} | ${''} | ${''} | ${''} | ${true} | ${'when the pull request has an assignee and only exemptAllIssueAssignees is enabled'}
|
||||
${1404} | ${true} | ${['assignee']} | ${false} | ${undefined} | ${true} | ${''} | ${''} | ${''} | ${false} | ${'when the pull request has an assignee and only exemptAllPrAssignees is enabled'}
|
||||
${1405} | ${true} | ${['assignee']} | ${true} | ${false} | ${undefined} | ${''} | ${''} | ${''} | ${false} | ${'when the pull request has an assignee and exemptAllAssignees is enabled and exemptAllIssueAssignees is disabled'}
|
||||
${1406} | ${true} | ${['assignee']} | ${true} | ${true} | ${undefined} | ${''} | ${''} | ${''} | ${false} | ${'when the pull request has an assignee and exemptAllAssignees is enabled and exemptAllIssueAssignees is enabled'}
|
||||
${1407} | ${true} | ${['assignee']} | ${true} | ${undefined} | ${false} | ${''} | ${''} | ${''} | ${true} | ${'when the pull request has an assignee and exemptAllAssignees is enabled and exemptAllPrAssignees is disabled'}
|
||||
${1408} | ${true} | ${['assignee']} | ${true} | ${undefined} | ${true} | ${''} | ${''} | ${''} | ${false} | ${'when the pull request has an assignee and exemptAllAssignees is enabled and exemptAllPrAssignees is enabled'}
|
||||
${1409} | ${true} | ${['assignee']} | ${false} | ${false} | ${undefined} | ${''} | ${''} | ${''} | ${true} | ${'when the pull request has an assignee and exemptAllAssignees is disabled and exemptAllIssueAssignees is disabled'}
|
||||
${1410} | ${true} | ${['assignee']} | ${false} | ${true} | ${undefined} | ${''} | ${''} | ${''} | ${true} | ${'when the pull request has an assignee and exemptAllAssignees is disabled and exemptAllIssueAssignees is enabled'}
|
||||
${1411} | ${true} | ${['assignee']} | ${false} | ${undefined} | ${false} | ${''} | ${''} | ${''} | ${true} | ${'when the pull request has an assignee and exemptAllAssignees is disabled and exemptAllPrAssignees is disabled'}
|
||||
${1412} | ${true} | ${['assignee']} | ${false} | ${undefined} | ${true} | ${''} | ${''} | ${''} | ${false} | ${'when the pull request has an assignee and exemptAllAssignees is disabled and exemptAllPrAssignees is enabled'}
|
||||
${1413} | ${true} | ${['assignee']} | ${false} | ${undefined} | ${false} | ${''} | ${''} | ${''} | ${true} | ${'when the pull request has an assignee and exemptAllAssignees is disabled and exemptAllPrAssignees is disabled'}
|
||||
${1500} | ${true} | ${['assignee']} | ${false} | ${undefined} | ${false} | ${'bad'} | ${''} | ${''} | ${true} | ${'when the pull request has an assignee and exemptAllAssignees is disabled, exemptAllPrAssignees is disabled and exemptAssignees has a different assignee'}
|
||||
${1501} | ${true} | ${['assignee']} | ${false} | ${undefined} | ${true} | ${'bad'} | ${''} | ${''} | ${false} | ${'when the pull request has an assignee and exemptAllAssignees is disabled, exemptAllPrAssignees is enabled and exemptAssignees has a different assignee'}
|
||||
${1502} | ${true} | ${['assignee']} | ${true} | ${undefined} | ${undefined} | ${'bad'} | ${''} | ${''} | ${false} | ${'when the pull request has an assignee and exemptAllAssignees is enabled and exemptAssignees has a different assignee'}
|
||||
${1503} | ${true} | ${['assignee']} | ${false} | ${true} | ${undefined} | ${'bad'} | ${''} | ${''} | ${true} | ${'when the pull request has an assignee and exemptAllIssueAssignees is enabled and exemptAssignees has a different assignee'}
|
||||
${1504} | ${true} | ${['assignee']} | ${false} | ${undefined} | ${true} | ${'bad'} | ${''} | ${''} | ${false} | ${'when the pull request has an assignee and exemptAllPrAssignees is enabled and exemptAssignees has a different assignee'}
|
||||
${1505} | ${true} | ${['assignee']} | ${true} | ${false} | ${undefined} | ${'bad'} | ${''} | ${''} | ${false} | ${'when the pull request has an assignee and exemptAllAssignees is enabled, exemptAllIssueAssignees is disabled and exemptAssignees has a different assignee'}
|
||||
${1506} | ${true} | ${['assignee']} | ${true} | ${true} | ${undefined} | ${'bad'} | ${''} | ${''} | ${false} | ${'when the pull request has an assignee and exemptAllAssignees is enabled, exemptAllIssueAssignees is enabled and exemptAssignees has a different assignee'}
|
||||
${1507} | ${true} | ${['assignee']} | ${true} | ${undefined} | ${false} | ${'bad'} | ${''} | ${''} | ${true} | ${'when the pull request has an assignee and exemptAllAssignees is enabled, exemptAllPrAssignees is disabled and exemptAssignees has a different assignee'}
|
||||
${1508} | ${true} | ${['assignee']} | ${true} | ${undefined} | ${true} | ${'bad'} | ${''} | ${''} | ${false} | ${'when the pull request has an assignee and exemptAllAssignees is enabled, exemptAllPrAssignees is enabled and exemptAssignees has a different assignee'}
|
||||
${1509} | ${true} | ${['assignee']} | ${false} | ${false} | ${undefined} | ${'bad'} | ${''} | ${''} | ${true} | ${'when the pull request has an assignee and exemptAllAssignees is disabled, exemptAllIssueAssignees is disabled and exemptAssignees has a different assignee'}
|
||||
${1510} | ${true} | ${['assignee']} | ${false} | ${true} | ${undefined} | ${'bad'} | ${''} | ${''} | ${true} | ${'when the pull request has an assignee and exemptAllAssignees is disabled, exemptAllIssueAssignees is enabled and exemptAssignees has a different assignee'}
|
||||
${1511} | ${true} | ${['assignee']} | ${false} | ${undefined} | ${false} | ${'bad'} | ${''} | ${''} | ${true} | ${'when the pull request has an assignee and exemptAllAssignees is disabled, exemptAllPrAssignees is disabled and exemptAssignees has a different assignee'}
|
||||
${1513} | ${true} | ${['assignee']} | ${false} | ${undefined} | ${false} | ${'bad'} | ${''} | ${''} | ${true} | ${'when the pull request has an assignee and exemptAllAssignees is disabled, exemptAllPrAssignees is disabled and exemptAssignees has a different assignee'}
|
||||
${1600} | ${true} | ${['assignee']} | ${false} | ${undefined} | ${false} | ${'assignee'} | ${''} | ${''} | ${false} | ${'when the pull request has an assignee and exemptAllAssignees is disabled, exemptAllPrAssignees is disabled and exemptAssignees has the same assignee'}
|
||||
${1601} | ${true} | ${['assignee']} | ${false} | ${undefined} | ${true} | ${'assignee'} | ${''} | ${''} | ${false} | ${'when the pull request has an assignee and exemptAllAssignees is disabled, exemptAllPrAssignees is enabled and exemptAssignees has the same assignee'}
|
||||
${1602} | ${true} | ${['assignee']} | ${true} | ${undefined} | ${undefined} | ${'assignee'} | ${''} | ${''} | ${false} | ${'when the pull request has an assignee and exemptAllAssignees is enabled and exemptAssignees has the same assignee'}
|
||||
${1603} | ${true} | ${['assignee']} | ${false} | ${true} | ${undefined} | ${'assignee'} | ${''} | ${''} | ${false} | ${'when the pull request has an assignee and exemptAllIssueAssignees is enabled and exemptAssignees has the same assignee'}
|
||||
${1604} | ${true} | ${['assignee']} | ${false} | ${undefined} | ${true} | ${'assignee'} | ${''} | ${''} | ${false} | ${'when the pull request has an assignee and exemptAllPrAssignees is enabled and exemptAssignees has the same assignee'}
|
||||
${1605} | ${true} | ${['assignee']} | ${true} | ${false} | ${undefined} | ${'assignee'} | ${''} | ${''} | ${false} | ${'when the pull request has an assignee and exemptAllAssignees is enabled, exemptAllIssueAssignees is disabled and exemptAssignees has the same assignee'}
|
||||
${1606} | ${true} | ${['assignee']} | ${true} | ${true} | ${undefined} | ${'assignee'} | ${''} | ${''} | ${false} | ${'when the pull request has an assignee and exemptAllAssignees is enabled, exemptAllIssueAssignees is enabled and exemptAssignees has the same assignee'}
|
||||
${1607} | ${true} | ${['assignee']} | ${true} | ${undefined} | ${false} | ${'assignee'} | ${''} | ${''} | ${false} | ${'when the pull request has an assignee and exemptAllAssignees is enabled, exemptAllPrAssignees is disabled and exemptAssignees has the same assignee'}
|
||||
${1608} | ${true} | ${['assignee']} | ${true} | ${undefined} | ${true} | ${'assignee'} | ${''} | ${''} | ${false} | ${'when the pull request has an assignee and exemptAllAssignees is enabled, exemptAllPrAssignees is enabled and exemptAssignees has the same assignee'}
|
||||
${1609} | ${true} | ${['assignee']} | ${false} | ${false} | ${undefined} | ${'assignee'} | ${''} | ${''} | ${false} | ${'when the pull request has an assignee and exemptAllAssignees is disabled, exemptAllIssueAssignees is disabled and exemptAssignees has the same assignee'}
|
||||
${1610} | ${true} | ${['assignee']} | ${false} | ${true} | ${undefined} | ${'assignee'} | ${''} | ${''} | ${false} | ${'when the pull request has an assignee and exemptAllAssignees is disabled, exemptAllIssueAssignees is enabled and exemptAssignees has the same assignee'}
|
||||
${1611} | ${true} | ${['assignee']} | ${false} | ${undefined} | ${false} | ${'assignee'} | ${''} | ${''} | ${false} | ${'when the pull request has an assignee and exemptAllAssignees is disabled, exemptAllPrAssignees is disabled and exemptAssignees has the same assignee'}
|
||||
${1612} | ${true} | ${['assignee']} | ${false} | ${undefined} | ${true} | ${'assignee'} | ${''} | ${''} | ${false} | ${'when the pull request has an assignee and exemptAllAssignees is disabled, exemptAllPrAssignees is enabled and exemptAssignees has the same assignee'}
|
||||
${1613} | ${true} | ${['assignee']} | ${false} | ${undefined} | ${false} | ${'assignee'} | ${''} | ${''} | ${false} | ${'when the pull request has an assignee and exemptAllAssignees is disabled, exemptAllPrAssignees is disabled and exemptAssignees has the same assignee'}
|
||||
${1701} | ${true} | ${['assignee']} | ${false} | ${undefined} | ${true} | ${'bad'} | ${'bad'} | ${''} | ${false} | ${'when the pull request has an assignee and exemptAllAssignees is disabled, exemptAllPrAssignees is enabled and exemptAssignees and exemptIssueAssignees has a different assignee'}
|
||||
${1702} | ${true} | ${['assignee']} | ${true} | ${undefined} | ${undefined} | ${'bad'} | ${'bad'} | ${''} | ${false} | ${'when the pull request has an assignee and exemptAllAssignees is enabled and exemptAssignees and exemptIssueAssignees has a different assignee'}
|
||||
${1703} | ${true} | ${['assignee']} | ${false} | ${true} | ${undefined} | ${'bad'} | ${'bad'} | ${''} | ${true} | ${'when the pull request has an assignee and exemptAllIssueAssignees is enabled and exemptAssignees and exemptIssueAssignees has a different assignee'}
|
||||
${1704} | ${true} | ${['assignee']} | ${false} | ${undefined} | ${true} | ${'bad'} | ${'bad'} | ${''} | ${false} | ${'when the pull request has an assignee and exemptAllPrAssignees is enabled and exemptAssignees and exemptIssueAssignees has a different assignee'}
|
||||
${1705} | ${true} | ${['assignee']} | ${true} | ${false} | ${undefined} | ${'bad'} | ${'bad'} | ${''} | ${false} | ${'when the pull request has an assignee and exemptAllAssignees is enabled, exemptAllIssueAssignees is disabled and exemptAssignees and exemptIssueAssignees has a different assignee'}
|
||||
${1706} | ${true} | ${['assignee']} | ${true} | ${true} | ${undefined} | ${'bad'} | ${'bad'} | ${''} | ${false} | ${'when the pull request has an assignee and exemptAllAssignees is enabled, exemptAllIssueAssignees is enabled and exemptAssignees and exemptIssueAssignees has a different assignee'}
|
||||
${1707} | ${true} | ${['assignee']} | ${true} | ${undefined} | ${false} | ${'bad'} | ${'bad'} | ${''} | ${true} | ${'when the pull request has an assignee and exemptAllAssignees is enabled, exemptAllPrAssignees is disabled and exemptAssignees and exemptIssueAssignees has a different assignee'}
|
||||
${1708} | ${true} | ${['assignee']} | ${true} | ${undefined} | ${true} | ${'bad'} | ${'bad'} | ${''} | ${false} | ${'when the pull request has an assignee and exemptAllAssignees is enabled, exemptAllPrAssignees is enabled and exemptAssignees and exemptIssueAssignees has a different assignee'}
|
||||
${1709} | ${true} | ${['assignee']} | ${false} | ${false} | ${undefined} | ${'bad'} | ${'bad'} | ${''} | ${true} | ${'when the pull request has an assignee and exemptAllAssignees is disabled, exemptAllIssueAssignees is disabled and exemptAssignees and exemptIssueAssignees has a different assignee'}
|
||||
${1710} | ${true} | ${['assignee']} | ${false} | ${true} | ${undefined} | ${'bad'} | ${'bad'} | ${''} | ${true} | ${'when the pull request has an assignee and exemptAllAssignees is disabled, exemptAllIssueAssignees is enabled and exemptAssignees and exemptIssueAssignees has a different assignee'}
|
||||
${1711} | ${true} | ${['assignee']} | ${false} | ${undefined} | ${false} | ${'bad'} | ${'bad'} | ${''} | ${true} | ${'when the pull request has an assignee and exemptAllAssignees is disabled, exemptAllPrAssignees is disabled and exemptAssignees and exemptIssueAssignees has a different assignee'}
|
||||
${1800} | ${true} | ${['assignee']} | ${true} | ${undefined} | ${undefined} | ${'bad'} | ${'assignee'} | ${''} | ${false} | ${'when the pull request has an assignee and exemptAllAssignees is enabled, exemptAssignees has a different assignee and exemptIssueAssignees has the same assignee'}
|
||||
${1801} | ${true} | ${['assignee']} | ${false} | ${true} | ${undefined} | ${'bad'} | ${'assignee'} | ${''} | ${true} | ${'when the pull request has an assignee and exemptAllIssueAssignees is enabled, exemptAssignees has a different assignee and exemptIssueAssignees has the same assignee'}
|
||||
${1802} | ${true} | ${['assignee']} | ${false} | ${undefined} | ${true} | ${'bad'} | ${'assignee'} | ${''} | ${false} | ${'when the pull request has an assignee and exemptAllPrAssignees is enabled, exemptAssignees has a different assignee and exemptIssueAssignees has the same assignee'}
|
||||
${1803} | ${true} | ${['assignee']} | ${true} | ${false} | ${undefined} | ${'bad'} | ${'assignee'} | ${''} | ${false} | ${'when the pull request has an assignee and exemptAllAssignees is enabled, exemptAllIssueAssignees is disabled, exemptAssignees has a different assignee and exemptIssueAssignees has the same assignee'}
|
||||
${1804} | ${true} | ${['assignee']} | ${true} | ${true} | ${undefined} | ${'bad'} | ${'assignee'} | ${''} | ${false} | ${'when the pull request has an assignee and exemptAllAssignees is enabled, exemptAllIssueAssignees is enabled, exemptAssignees has a different assignee and exemptIssueAssignees has the same assignee'}
|
||||
${1805} | ${true} | ${['assignee']} | ${true} | ${undefined} | ${false} | ${'bad'} | ${'assignee'} | ${''} | ${true} | ${'when the pull request has an assignee and exemptAllAssignees is enabled, exemptAllPrAssignees is disabled, exemptAssignees has a different assignee and exemptIssueAssignees has the same assignee'}
|
||||
${1806} | ${true} | ${['assignee']} | ${true} | ${undefined} | ${true} | ${'bad'} | ${'assignee'} | ${''} | ${false} | ${'when the pull request has an assignee and exemptAllAssignees is enabled, exemptAllPrAssignees is enabled, exemptAssignees has a different assignee and exemptIssueAssignees has the same assignee'}
|
||||
${1807} | ${true} | ${['assignee']} | ${false} | ${false} | ${undefined} | ${'bad'} | ${'assignee'} | ${''} | ${true} | ${'when the pull request has an assignee and exemptAllAssignees is disabled, exemptAllIssueAssignees is disabled, exemptAssignees has a different assignee and exemptIssueAssignees has the same assignee'}
|
||||
${1808} | ${true} | ${['assignee']} | ${false} | ${true} | ${undefined} | ${'bad'} | ${'assignee'} | ${''} | ${true} | ${'when the pull request has an assignee and exemptAllAssignees is disabled, exemptAllIssueAssignees is enabled, exemptAssignees has a different assignee and exemptIssueAssignees has the same assignee'}
|
||||
${1809} | ${true} | ${['assignee']} | ${false} | ${undefined} | ${true} | ${'bad'} | ${'assignee'} | ${''} | ${false} | ${'when the pull request has an assignee and exemptAllAssignees is disabled, exemptAllPrAssignees is enabled, exemptAssignees has a different assignee and exemptIssueAssignees has the same assignee'}
|
||||
${1810} | ${true} | ${['assignee']} | ${false} | ${undefined} | ${false} | ${'bad'} | ${'assignee'} | ${''} | ${true} | ${'when the pull request has an assignee and exemptAllAssignees is disabled, exemptAllPrAssignees is disabled, exemptAssignees has a different assignee and exemptIssueAssignees has the same assignee'}
|
||||
${1900} | ${true} | ${['assignee']} | ${false} | ${undefined} | ${true} | ${'bad'} | ${''} | ${'bad'} | ${false} | ${'when the pull request has an assignee and exemptAllAssignees is disabled, exemptAllPrAssignees is enabled and exemptAssignees and exemptPrAssignees has a different assignee'}
|
||||
${1901} | ${true} | ${['assignee']} | ${true} | ${undefined} | ${undefined} | ${'bad'} | ${''} | ${'bad'} | ${false} | ${'when the pull request has an assignee and exemptAllAssignees is enabled and exemptAssignees and exemptPrAssignees has a different assignee'}
|
||||
${1902} | ${true} | ${['assignee']} | ${false} | ${true} | ${undefined} | ${'bad'} | ${''} | ${'bad'} | ${true} | ${'when the pull request has an assignee and exemptAllIssueAssignees is enabled and exemptAssignees and exemptPrAssignees has a different assignee'}
|
||||
${1903} | ${true} | ${['assignee']} | ${false} | ${undefined} | ${true} | ${'bad'} | ${''} | ${'bad'} | ${false} | ${'when the pull request has an assignee and exemptAllPrAssignees is enabled and exemptAssignees and exemptPrAssignees has a different assignee'}
|
||||
${1904} | ${true} | ${['assignee']} | ${true} | ${false} | ${undefined} | ${'bad'} | ${''} | ${'bad'} | ${false} | ${'when the pull request has an assignee and exemptAllAssignees is enabled, exemptAllIssueAssignees is disabled and exemptAssignees and exemptPrAssignees has a different assignee'}
|
||||
${1905} | ${true} | ${['assignee']} | ${true} | ${true} | ${undefined} | ${'bad'} | ${''} | ${'bad'} | ${false} | ${'when the pull request has an assignee and exemptAllAssignees is enabled, exemptAllIssueAssignees is enabled and exemptAssignees and exemptPrAssignees has a different assignee'}
|
||||
${1906} | ${true} | ${['assignee']} | ${true} | ${undefined} | ${false} | ${'bad'} | ${''} | ${'bad'} | ${true} | ${'when the pull request has an assignee and exemptAllAssignees is enabled, exemptAllPrAssignees is disabled and exemptAssignees and exemptPrAssignees has a different assignee'}
|
||||
${1907} | ${true} | ${['assignee']} | ${true} | ${undefined} | ${true} | ${'bad'} | ${''} | ${'bad'} | ${false} | ${'when the pull request has an assignee and exemptAllAssignees is enabled, exemptAllPrAssignees is enabled and exemptAssignees and exemptPrAssignees has a different assignee'}
|
||||
${1908} | ${true} | ${['assignee']} | ${false} | ${false} | ${undefined} | ${'bad'} | ${''} | ${'bad'} | ${true} | ${'when the pull request has an assignee and exemptAllAssignees is disabled, exemptAllIssueAssignees is disabled and exemptAssignees and exemptPrAssignees has a different assignee'}
|
||||
${1909} | ${true} | ${['assignee']} | ${false} | ${true} | ${undefined} | ${'bad'} | ${''} | ${'bad'} | ${true} | ${'when the pull request has an assignee and exemptAllAssignees is disabled, exemptAllIssueAssignees is enabled and exemptAssignees and exemptPrAssignees has a different assignee'}
|
||||
${1910} | ${true} | ${['assignee']} | ${false} | ${undefined} | ${true} | ${'bad'} | ${''} | ${'bad'} | ${false} | ${'when the pull request has an assignee and exemptAllAssignees is disabled, exemptAllPrAssignees is enabled and exemptAssignees and exemptPrAssignees has a different assignee'}
|
||||
${1911} | ${true} | ${['assignee']} | ${false} | ${undefined} | ${false} | ${'bad'} | ${''} | ${'bad'} | ${true} | ${'when the pull request has an assignee and exemptAllAssignees is disabled, exemptAllPrAssignees is disabled and exemptAssignees and exemptPrAssignees has a different assignee'}
|
||||
${2000} | ${true} | ${['assignee']} | ${true} | ${undefined} | ${undefined} | ${'bad'} | ${''} | ${'assignee'} | ${false} | ${'when the pull request has an assignee and exemptAllAssignees is enabled, exemptAssignees has a different assignee and exemptPrAssignees has the same assignee'}
|
||||
${2001} | ${true} | ${['assignee']} | ${false} | ${true} | ${undefined} | ${'bad'} | ${''} | ${'assignee'} | ${false} | ${'when the pull request has an assignee and exemptAllIssueAssignees is enabled, exemptAssignees has a different assignee and exemptPrAssignees has the same assignee'}
|
||||
${2002} | ${true} | ${['assignee']} | ${false} | ${undefined} | ${true} | ${'bad'} | ${''} | ${'assignee'} | ${false} | ${'when the pull request has an assignee and exemptAllPrAssignees is enabled, exemptAssignees has a different assignee and exemptPrAssignees has the same assignee'}
|
||||
${2003} | ${true} | ${['assignee']} | ${true} | ${false} | ${undefined} | ${'bad'} | ${''} | ${'assignee'} | ${false} | ${'when the pull request has an assignee and exemptAllAssignees is enabled, exemptAllIssueAssignees is disabled, exemptAssignees has a different assignee and exemptPrAssignees has the same assignee'}
|
||||
${2004} | ${true} | ${['assignee']} | ${true} | ${true} | ${undefined} | ${'bad'} | ${''} | ${'assignee'} | ${false} | ${'when the pull request has an assignee and exemptAllAssignees is enabled, exemptAllIssueAssignees is enabled, exemptAssignees has a different assignee and exemptPrAssignees has the same assignee'}
|
||||
${2005} | ${true} | ${['assignee']} | ${true} | ${undefined} | ${false} | ${'bad'} | ${''} | ${'assignee'} | ${false} | ${'when the pull request has an assignee and exemptAllAssignees is enabled, exemptAllPrAssignees is disabled, exemptAssignees has a different assignee and exemptPrAssignees has the same assignee'}
|
||||
${2006} | ${true} | ${['assignee']} | ${true} | ${undefined} | ${true} | ${'bad'} | ${''} | ${'assignee'} | ${false} | ${'when the pull request has an assignee and exemptAllAssignees is enabled, exemptAllPrAssignees is enabled, exemptAssignees has a different assignee and exemptPrAssignees has the same assignee'}
|
||||
${2007} | ${true} | ${['assignee']} | ${false} | ${false} | ${undefined} | ${'bad'} | ${''} | ${'assignee'} | ${false} | ${'when the pull request has an assignee and exemptAllAssignees is disabled, exemptAllIssueAssignees is disabled, exemptAssignees has a different assignee and exemptPrAssignees has the same assignee'}
|
||||
${2008} | ${true} | ${['assignee']} | ${false} | ${true} | ${undefined} | ${'bad'} | ${''} | ${'assignee'} | ${false} | ${'when the pull request has an assignee and exemptAllAssignees is disabled, exemptAllIssueAssignees is enabled, exemptAssignees has a different assignee and exemptPrAssignees has the same assignee'}
|
||||
${2009} | ${true} | ${['assignee']} | ${false} | ${undefined} | ${true} | ${'bad'} | ${''} | ${'assignee'} | ${false} | ${'when the pull request has an assignee and exemptAllAssignees is disabled, exemptAllPrAssignees is enabled, exemptAssignees has a different assignee and exemptPrAssignees has the same assignee'}
|
||||
${2010} | ${true} | ${['assignee']} | ${false} | ${undefined} | ${false} | ${'bad'} | ${''} | ${'assignee'} | ${false} | ${'when the pull request has an assignee and exemptAllAssignees is disabled, exemptAllPrAssignees is disabled, exemptAssignees has a different assignee and exemptPrAssignees has the same assignee'}
|
||||
${2100} | ${true} | ${['assignee']} | ${false} | ${undefined} | ${true} | ${'assignee'} | ${'bad'} | ${''} | ${false} | ${'when the pull request has an assignee and exemptAllAssignees is disabled, exemptAllPrAssignees is enabled, exemptAssignees has the same assignee and exemptIssueAssignees has a different assignee'}
|
||||
${2101} | ${true} | ${['assignee']} | ${true} | ${undefined} | ${undefined} | ${'assignee'} | ${'bad'} | ${''} | ${false} | ${'when the pull request has an assignee and exemptAllAssignees is enabled, exemptAssignees has the same assignee and exemptIssueAssignees has a different assignee'}
|
||||
${2102} | ${true} | ${['assignee']} | ${false} | ${true} | ${undefined} | ${'assignee'} | ${'bad'} | ${''} | ${false} | ${'when the pull request has an assignee and exemptAllIssueAssignees is enabled, exemptAssignees has the same assignee and exemptIssueAssignees has a different assignee'}
|
||||
${2103} | ${true} | ${['assignee']} | ${false} | ${undefined} | ${true} | ${'assignee'} | ${'bad'} | ${''} | ${false} | ${'when the pull request has an assignee and exemptAllPrAssignees is enabled, exemptAssignees has the same assignee and exemptIssueAssignees has a different assignee'}
|
||||
${2104} | ${true} | ${['assignee']} | ${true} | ${false} | ${undefined} | ${'assignee'} | ${'bad'} | ${''} | ${false} | ${'when the pull request has an assignee and exemptAllAssignees is enabled, exemptAllIssueAssignees is disabled, exemptAssignees has the same assignee and exemptIssueAssignees has a different assignee'}
|
||||
${2105} | ${true} | ${['assignee']} | ${true} | ${true} | ${undefined} | ${'assignee'} | ${'bad'} | ${''} | ${false} | ${'when the pull request has an assignee and exemptAllAssignees is enabled, exemptAllIssueAssignees is enabled, exemptAssignees has the same assignee and exemptIssueAssignees has a different assignee'}
|
||||
${2106} | ${true} | ${['assignee']} | ${true} | ${undefined} | ${false} | ${'assignee'} | ${'bad'} | ${''} | ${false} | ${'when the pull request has an assignee and exemptAllAssignees is enabled, exemptAllPrAssignees is disabled, exemptAssignees has the same assignee and exemptIssueAssignees has a different assignee'}
|
||||
${2107} | ${true} | ${['assignee']} | ${true} | ${undefined} | ${true} | ${'assignee'} | ${'bad'} | ${''} | ${false} | ${'when the pull request has an assignee and exemptAllAssignees is enabled, exemptAllPrAssignees is enabled, exemptAssignees has the same assignee and exemptIssueAssignees has a different assignee'}
|
||||
${2108} | ${true} | ${['assignee']} | ${false} | ${false} | ${undefined} | ${'assignee'} | ${'bad'} | ${''} | ${false} | ${'when the pull request has an assignee and exemptAllAssignees is disabled, exemptAllIssueAssignees is disabled, exemptAssignees has the same assignee and exemptIssueAssignees has a different assignee'}
|
||||
${2109} | ${true} | ${['assignee']} | ${false} | ${true} | ${undefined} | ${'assignee'} | ${'bad'} | ${''} | ${false} | ${'when the pull request has an assignee and exemptAllAssignees is disabled, exemptAllIssueAssignees is enabled, exemptAssignees has the same assignee and exemptIssueAssignees has a different assignee'}
|
||||
${2110} | ${true} | ${['assignee']} | ${false} | ${undefined} | ${false} | ${'assignee'} | ${'bad'} | ${''} | ${false} | ${'when the pull request has an assignee and exemptAllAssignees is disabled, exemptAllPrAssignees is disabled, exemptAssignees has the same assignee and exemptIssueAssignees has a different assignee'}
|
||||
${2200} | ${true} | ${['assignee']} | ${false} | ${undefined} | ${false} | ${'assignee'} | ${'assignee'} | ${''} | ${false} | ${'when the pull request has an assignee and exemptAllAssignees is disabled, exemptAllPrAssignees is disabled, exemptAssignees has the same assignee and exemptIssueAssignees has the same assignee'}
|
||||
${2201} | ${true} | ${['assignee']} | ${false} | ${undefined} | ${true} | ${'assignee'} | ${'assignee'} | ${''} | ${false} | ${'when the pull request has an assignee and exemptAllAssignees is disabled, exemptAllPrAssignees is enabled, exemptAssignees has the same assignee and exemptIssueAssignees has the same assignee'}
|
||||
${2202} | ${true} | ${['assignee']} | ${true} | ${undefined} | ${undefined} | ${'assignee'} | ${'assignee'} | ${''} | ${false} | ${'when the pull request has an assignee and exemptAllAssignees is enabled, exemptAssignees has the same assignee and exemptIssueAssignees has the same assignee'}
|
||||
${2203} | ${true} | ${['assignee']} | ${false} | ${true} | ${undefined} | ${'assignee'} | ${'assignee'} | ${''} | ${false} | ${'when the pull request has an assignee and exemptAllIssueAssignees is enabled, exemptAssignees has the same assignee and exemptIssueAssignees has the same assignee'}
|
||||
${2204} | ${true} | ${['assignee']} | ${false} | ${undefined} | ${true} | ${'assignee'} | ${'assignee'} | ${''} | ${false} | ${'when the pull request has an assignee and exemptAllPrAssignees is enabled, exemptAssignees has the same assignee and exemptIssueAssignees has the same assignee'}
|
||||
${2205} | ${true} | ${['assignee']} | ${true} | ${false} | ${undefined} | ${'assignee'} | ${'assignee'} | ${''} | ${false} | ${'when the pull request has an assignee and exemptAllAssignees is enabled, exemptAllIssueAssignees is disabled, exemptAssignees has the same assignee and exemptIssueAssignees has the same assignee'}
|
||||
${2206} | ${true} | ${['assignee']} | ${true} | ${true} | ${undefined} | ${'assignee'} | ${'assignee'} | ${''} | ${false} | ${'when the pull request has an assignee and exemptAllAssignees is enabled, exemptAllIssueAssignees is enabled, exemptAssignees has the same assignee and exemptIssueAssignees has the same assignee'}
|
||||
${2207} | ${true} | ${['assignee']} | ${true} | ${undefined} | ${false} | ${'assignee'} | ${'assignee'} | ${''} | ${false} | ${'when the pull request has an assignee and exemptAllAssignees is enabled, exemptAllPrAssignees is disabled, exemptAssignees has the same assignee and exemptIssueAssignees has the same assignee'}
|
||||
${2208} | ${true} | ${['assignee']} | ${true} | ${undefined} | ${true} | ${'assignee'} | ${'assignee'} | ${''} | ${false} | ${'when the pull request has an assignee and exemptAllAssignees is enabled, exemptAllPrAssignees is enabled, exemptAssignees has the same assignee and exemptIssueAssignees has the same assignee'}
|
||||
${2209} | ${true} | ${['assignee']} | ${false} | ${false} | ${undefined} | ${'assignee'} | ${'assignee'} | ${''} | ${false} | ${'when the pull request has an assignee and exemptAllAssignees is disabled, exemptAllIssueAssignees is disabled, exemptAssignees has the same assignee and exemptIssueAssignees has the same assignee'}
|
||||
${2210} | ${true} | ${['assignee']} | ${false} | ${true} | ${undefined} | ${'assignee'} | ${'assignee'} | ${''} | ${false} | ${'when the pull request has an assignee and exemptAllAssignees is disabled, exemptAllIssueAssignees is enabled, exemptAssignees has the same assignee and exemptIssueAssignees has the same assignee'}
|
||||
${2311} | ${true} | ${['assignee']} | ${false} | ${undefined} | ${false} | ${'assignee'} | ${'assignee'} | ${''} | ${false} | ${'when the pull request has an assignee and exemptAllAssignees is disabled, exemptAllPrAssignees is disabled, exemptAssignees has the same assignee and exemptIssueAssignees has the same assignee'}
|
||||
${2312} | ${true} | ${['assignee']} | ${false} | ${undefined} | ${true} | ${'assignee'} | ${'assignee'} | ${''} | ${false} | ${'when the pull request has an assignee and exemptAllAssignees is disabled, exemptAllPrAssignees is enabled, exemptAssignees has the same assignee and exemptIssueAssignees has the same assignee'}
|
||||
${2313} | ${true} | ${['assignee']} | ${false} | ${undefined} | ${false} | ${'assignee'} | ${'assignee'} | ${''} | ${false} | ${'when the pull request has an assignee and exemptAllAssignees is disabled, exemptAllPrAssignees is disabled, exemptAssignees has the same assignee and exemptIssueAssignees has the same assignee'}
|
||||
${2300} | ${true} | ${['assignee']} | ${false} | ${undefined} | ${false} | ${'assignee'} | ${''} | ${'bad'} | ${true} | ${'when the pull request has an assignee and exemptAllAssignees is disabled, exemptAllPrAssignees is disabled, exemptAssignees has the same assignee and exemptPrAssignees has a different assignee'}
|
||||
${2301} | ${true} | ${['assignee']} | ${false} | ${undefined} | ${true} | ${'assignee'} | ${''} | ${'bad'} | ${false} | ${'when the pull request has an assignee and exemptAllAssignees is disabled, exemptAllPrAssignees is enabled, exemptAssignees has the same assignee and exemptPrAssignees has a different assignee'}
|
||||
${2302} | ${true} | ${['assignee']} | ${true} | ${undefined} | ${undefined} | ${'assignee'} | ${''} | ${'bad'} | ${false} | ${'when the pull request has an assignee and exemptAllAssignees is enabled, exemptAssignees has the same assignee and exemptPrAssignees has a different assignee'}
|
||||
${2303} | ${true} | ${['assignee']} | ${false} | ${true} | ${undefined} | ${'assignee'} | ${''} | ${'bad'} | ${true} | ${'when the pull request has an assignee and exemptAllIssueAssignees is enabled, exemptAssignees has the same assignee and exemptPrAssignees has a different assignee'}
|
||||
${2304} | ${true} | ${['assignee']} | ${false} | ${undefined} | ${true} | ${'assignee'} | ${''} | ${'bad'} | ${false} | ${'when the pull request has an assignee and exemptAllPrAssignees is enabled, exemptAssignees has the same assignee and exemptPrAssignees has a different assignee'}
|
||||
${2305} | ${true} | ${['assignee']} | ${true} | ${false} | ${undefined} | ${'assignee'} | ${''} | ${'bad'} | ${false} | ${'when the pull request has an assignee and exemptAllAssignees is enabled, exemptAllIssueAssignees is disabled, exemptAssignees has the same assignee and exemptPrAssignees has a different assignee'}
|
||||
${2306} | ${true} | ${['assignee']} | ${true} | ${true} | ${undefined} | ${'assignee'} | ${''} | ${'bad'} | ${false} | ${'when the pull request has an assignee and exemptAllAssignees is enabled, exemptAllIssueAssignees is enabled, exemptAssignees has the same assignee and exemptPrAssignees has a different assignee'}
|
||||
${2307} | ${true} | ${['assignee']} | ${true} | ${undefined} | ${false} | ${'assignee'} | ${''} | ${'bad'} | ${true} | ${'when the pull request has an assignee and exemptAllAssignees is enabled, exemptAllPrAssignees is disabled, exemptAssignees has the same assignee and exemptPrAssignees has a different assignee'}
|
||||
${2308} | ${true} | ${['assignee']} | ${true} | ${undefined} | ${true} | ${'assignee'} | ${''} | ${'bad'} | ${false} | ${'when the pull request has an assignee and exemptAllAssignees is enabled, exemptAllPrAssignees is enabled, exemptAssignees has the same assignee and exemptPrAssignees has a different assignee'}
|
||||
${2309} | ${true} | ${['assignee']} | ${false} | ${false} | ${undefined} | ${'assignee'} | ${''} | ${'bad'} | ${true} | ${'when the pull request has an assignee and exemptAllAssignees is disabled, exemptAllIssueAssignees is disabled, exemptAssignees has the same assignee and exemptPrAssignees has a different assignee'}
|
||||
${2310} | ${true} | ${['assignee']} | ${false} | ${true} | ${undefined} | ${'assignee'} | ${''} | ${'bad'} | ${true} | ${'when the pull request has an assignee and exemptAllAssignees is disabled, exemptAllIssueAssignees is enabled, exemptAssignees has the same assignee and exemptPrAssignees has a different assignee'}
|
||||
${2311} | ${true} | ${['assignee']} | ${false} | ${undefined} | ${true} | ${'assignee'} | ${''} | ${'bad'} | ${false} | ${'when the pull request has an assignee and exemptAllAssignees is disabled, exemptAllPrAssignees is enabled, exemptAssignees has the same assignee and exemptPrAssignees has a different assignee'}
|
||||
${2400} | ${true} | ${['assignee']} | ${false} | ${undefined} | ${false} | ${'assignee'} | ${''} | ${'assignee'} | ${false} | ${'when the pull request has an assignee and exemptAllAssignees is disabled, exemptAllPrAssignees is disabled, exemptAssignees has the same assignee and exemptPrAssignees has the same assignee'}
|
||||
${2401} | ${true} | ${['assignee']} | ${false} | ${undefined} | ${true} | ${'assignee'} | ${''} | ${'assignee'} | ${false} | ${'when the pull request has an assignee and exemptAllAssignees is disabled, exemptAllPrAssignees is enabled, exemptAssignees has the same assignee and exemptPrAssignees has the same assignee'}
|
||||
${2402} | ${true} | ${['assignee']} | ${true} | ${undefined} | ${undefined} | ${'assignee'} | ${''} | ${'assignee'} | ${false} | ${'when the pull request has an assignee and exemptAllAssignees is enabled, exemptAssignees has the same assignee and exemptPrAssignees has the same assignee'}
|
||||
${2403} | ${true} | ${['assignee']} | ${false} | ${true} | ${undefined} | ${'assignee'} | ${''} | ${'assignee'} | ${false} | ${'when the pull request has an assignee and exemptAllIssueAssignees is enabled, exemptAssignees has the same assignee and exemptPrAssignees has the same assignee'}
|
||||
${2404} | ${true} | ${['assignee']} | ${false} | ${undefined} | ${true} | ${'assignee'} | ${''} | ${'assignee'} | ${false} | ${'when the pull request has an assignee and exemptAllPrAssignees is enabled, exemptAssignees has the same assignee and exemptPrAssignees has the same assignee'}
|
||||
${2405} | ${true} | ${['assignee']} | ${true} | ${false} | ${undefined} | ${'assignee'} | ${''} | ${'assignee'} | ${false} | ${'when the pull request has an assignee and exemptAllAssignees is enabled, exemptAllIssueAssignees is disabled, exemptAssignees has the same assignee and exemptPrAssignees has the same assignee'}
|
||||
${2406} | ${true} | ${['assignee']} | ${true} | ${true} | ${undefined} | ${'assignee'} | ${''} | ${'assignee'} | ${false} | ${'when the pull request has an assignee and exemptAllAssignees is enabled, exemptAllIssueAssignees is enabled, exemptAssignees has the same assignee and exemptPrAssignees has the same assignee'}
|
||||
${2407} | ${true} | ${['assignee']} | ${true} | ${undefined} | ${false} | ${'assignee'} | ${''} | ${'assignee'} | ${false} | ${'when the pull request has an assignee and exemptAllAssignees is enabled, exemptAllPrAssignees is disabled, exemptAssignees has the same assignee and exemptPrAssignees has the same assignee'}
|
||||
${2408} | ${true} | ${['assignee']} | ${true} | ${undefined} | ${true} | ${'assignee'} | ${''} | ${'assignee'} | ${false} | ${'when the pull request has an assignee and exemptAllAssignees is enabled, exemptAllPrAssignees is enabled, exemptAssignees has the same assignee and exemptPrAssignees has the same assignee'}
|
||||
${2409} | ${true} | ${['assignee']} | ${false} | ${false} | ${undefined} | ${'assignee'} | ${''} | ${'assignee'} | ${false} | ${'when the pull request has an assignee and exemptAllAssignees is disabled, exemptAllIssueAssignees is disabled, exemptAssignees has the same assignee and exemptPrAssignees has the same assignee'}
|
||||
${2410} | ${true} | ${['assignee']} | ${false} | ${true} | ${undefined} | ${'assignee'} | ${''} | ${'assignee'} | ${false} | ${'when the pull request has an assignee and exemptAllAssignees is disabled, exemptAllIssueAssignees is enabled, exemptAssignees has the same assignee and exemptPrAssignees has the same assignee'}
|
||||
${2411} | ${true} | ${['assignee']} | ${false} | ${undefined} | ${false} | ${'assignee'} | ${''} | ${'assignee'} | ${false} | ${'when the pull request has an assignee and exemptAllAssignees is disabled, exemptAllPrAssignees is disabled, exemptAssignees has the same assignee and exemptPrAssignees has the same assignee'}
|
||||
${2412} | ${true} | ${['assignee']} | ${false} | ${undefined} | ${true} | ${'assignee'} | ${''} | ${'assignee'} | ${false} | ${'when the pull request has an assignee and exemptAllAssignees is disabled, exemptAllPrAssignees is enabled, exemptAssignees has the same assignee and exemptPrAssignees has the same assignee'}
|
||||
${2413} | ${true} | ${['assignee']} | ${false} | ${undefined} | ${false} | ${'assignee'} | ${''} | ${'assignee'} | ${false} | ${'when the pull request has an assignee and exemptAllAssignees is disabled, exemptAllPrAssignees is disabled, exemptAssignees has the same assignee and exemptPrAssignees has the same assignee'}
|
||||
`(
|
||||
'$description',
|
||||
({
|
||||
id,
|
||||
isPullRequest,
|
||||
assignees,
|
||||
exemptAllAssignees,
|
||||
exemptAllIssueAssignees,
|
||||
exemptAllPrAssignees,
|
||||
exemptAssignees,
|
||||
exemptIssueAssignees,
|
||||
exemptPrAssignees,
|
||||
shouldStale
|
||||
}: ITestData): void => {
|
||||
beforeEach((): void => {
|
||||
opts.exemptAllAssignees = exemptAllAssignees;
|
||||
opts.exemptAllIssueAssignees = exemptAllIssueAssignees;
|
||||
opts.exemptAllPrAssignees = exemptAllPrAssignees;
|
||||
opts.exemptAssignees = exemptAssignees;
|
||||
opts.exemptIssueAssignees = exemptIssueAssignees;
|
||||
opts.exemptPrAssignees = exemptPrAssignees;
|
||||
setTestIssueList(isPullRequest, assignees, id);
|
||||
setProcessor();
|
||||
});
|
||||
|
||||
test(`should${
|
||||
shouldStale ? '' : ' not'
|
||||
} be marked as stale`, async () => {
|
||||
expect.assertions(3);
|
||||
|
||||
await processor.processIssues(1);
|
||||
|
||||
expect(processor.staleIssues).toHaveLength(shouldStale ? 1 : 0);
|
||||
expect(processor.closedIssues).toHaveLength(0);
|
||||
expect(processor.removedLabelIssues).toHaveLength(0);
|
||||
});
|
||||
}
|
||||
);
|
||||
});
|
||||
38
__tests__/classes/issues-processor-mock.ts
Normal file
38
__tests__/classes/issues-processor-mock.ts
Normal file
@@ -0,0 +1,38 @@
|
||||
import {Issue} from '../../src/classes/issue';
|
||||
import {IssuesProcessor} from '../../src/classes/issues-processor';
|
||||
import {IComment} from '../../src/interfaces/comment';
|
||||
import {IIssuesProcessorOptions} from '../../src/interfaces/issues-processor-options';
|
||||
|
||||
export class IssuesProcessorMock extends IssuesProcessor {
|
||||
constructor(
|
||||
options: IIssuesProcessorOptions,
|
||||
getActor?: () => Promise<string>,
|
||||
getIssues?: (page: number) => Promise<Issue[]>,
|
||||
listIssueComments?: (
|
||||
issueNumber: number,
|
||||
sinceDate: string
|
||||
) => Promise<IComment[]>,
|
||||
getLabelCreationDate?: (
|
||||
issue: Issue,
|
||||
label: string
|
||||
) => Promise<string | undefined>
|
||||
) {
|
||||
super(options);
|
||||
|
||||
if (getActor) {
|
||||
this.getActor = getActor;
|
||||
}
|
||||
|
||||
if (getIssues) {
|
||||
this.getIssues = getIssues;
|
||||
}
|
||||
|
||||
if (listIssueComments) {
|
||||
this.listIssueComments = listIssueComments;
|
||||
}
|
||||
|
||||
if (getLabelCreationDate) {
|
||||
this.getLabelCreationDate = getLabelCreationDate;
|
||||
}
|
||||
}
|
||||
}
|
||||
50
__tests__/constants/default-processor-options.ts
Normal file
50
__tests__/constants/default-processor-options.ts
Normal file
@@ -0,0 +1,50 @@
|
||||
import {IIssuesProcessorOptions} from '../../src/interfaces/issues-processor-options';
|
||||
|
||||
export const DefaultProcessorOptions: IIssuesProcessorOptions = Object.freeze({
|
||||
repoToken: 'none',
|
||||
staleIssueMessage: 'This issue is stale',
|
||||
stalePrMessage: 'This PR is stale',
|
||||
closeIssueMessage: 'This issue is being closed',
|
||||
closePrMessage: 'This PR is being closed',
|
||||
daysBeforeStale: 1,
|
||||
daysBeforeIssueStale: NaN,
|
||||
daysBeforePrStale: NaN,
|
||||
daysBeforeClose: 30,
|
||||
daysBeforeIssueClose: NaN,
|
||||
daysBeforePrClose: NaN,
|
||||
staleIssueLabel: 'Stale',
|
||||
closeIssueLabel: '',
|
||||
exemptIssueLabels: '',
|
||||
stalePrLabel: 'Stale',
|
||||
closePrLabel: '',
|
||||
exemptPrLabels: '',
|
||||
onlyLabels: '',
|
||||
onlyIssueLabels: '',
|
||||
onlyPrLabels: '',
|
||||
anyOfLabels: '',
|
||||
anyOfIssueLabels: '',
|
||||
anyOfPrLabels: '',
|
||||
operationsPerRun: 100,
|
||||
debugOnly: true,
|
||||
removeStaleWhenUpdated: false,
|
||||
removeIssueStaleWhenUpdated: undefined,
|
||||
removePrStaleWhenUpdated: undefined,
|
||||
ascending: false,
|
||||
skipStaleIssueMessage: false,
|
||||
skipStalePrMessage: false,
|
||||
deleteBranch: false,
|
||||
startDate: '',
|
||||
exemptMilestones: '',
|
||||
exemptIssueMilestones: '',
|
||||
exemptPrMilestones: '',
|
||||
exemptAllMilestones: false,
|
||||
exemptAllIssueMilestones: undefined,
|
||||
exemptAllPrMilestones: undefined,
|
||||
exemptAssignees: '',
|
||||
exemptIssueAssignees: '',
|
||||
exemptPrAssignees: '',
|
||||
exemptAllAssignees: false,
|
||||
exemptAllIssueAssignees: undefined,
|
||||
exemptAllPrAssignees: undefined,
|
||||
enableStatistics: true
|
||||
});
|
||||
19
__tests__/functions/generate-iissue.ts
Normal file
19
__tests__/functions/generate-iissue.ts
Normal file
@@ -0,0 +1,19 @@
|
||||
import {IIssue} from '../../src/interfaces/issue';
|
||||
|
||||
export function generateIIssue(
|
||||
partialIssue?: Readonly<Partial<IIssue>>
|
||||
): IIssue {
|
||||
return {
|
||||
milestone: undefined,
|
||||
assignees: [],
|
||||
labels: [],
|
||||
created_at: new Date().toISOString(),
|
||||
updated_at: new Date().toISOString(),
|
||||
number: Math.round(Math.random() * 5000),
|
||||
pull_request: null,
|
||||
title: 'dummy-title',
|
||||
locked: false,
|
||||
state: 'dummy-state',
|
||||
...partialIssue
|
||||
};
|
||||
}
|
||||
43
__tests__/functions/generate-issue.ts
Normal file
43
__tests__/functions/generate-issue.ts
Normal file
@@ -0,0 +1,43 @@
|
||||
import {Issue} from '../../src/classes/issue';
|
||||
import {IAssignee} from '../../src/interfaces/assignee';
|
||||
import {IIssuesProcessorOptions} from '../../src/interfaces/issues-processor-options';
|
||||
import {IsoDateString} from '../../src/types/iso-date-string';
|
||||
|
||||
export function generateIssue(
|
||||
options: IIssuesProcessorOptions,
|
||||
id: number,
|
||||
title: string,
|
||||
updatedAt: IsoDateString,
|
||||
createdAt: IsoDateString = updatedAt,
|
||||
isPullRequest = false,
|
||||
labels: string[] = [],
|
||||
isClosed = false,
|
||||
isLocked = false,
|
||||
milestone: string | undefined = undefined,
|
||||
assignees: string[] = []
|
||||
): Issue {
|
||||
return new Issue(options, {
|
||||
number: id,
|
||||
labels: labels.map(l => {
|
||||
return {name: l};
|
||||
}),
|
||||
title,
|
||||
created_at: createdAt,
|
||||
updated_at: updatedAt,
|
||||
pull_request: isPullRequest ? {} : null,
|
||||
state: isClosed ? 'closed' : 'open',
|
||||
locked: isLocked,
|
||||
milestone: milestone
|
||||
? {
|
||||
title: milestone
|
||||
}
|
||||
: undefined,
|
||||
assignees: assignees.map(
|
||||
(assignee: Readonly<string>): IAssignee => {
|
||||
return {
|
||||
login: assignee
|
||||
};
|
||||
}
|
||||
)
|
||||
});
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
3412
__tests__/milestones.spec.ts
Normal file
3412
__tests__/milestones.spec.ts
Normal file
File diff suppressed because it is too large
Load Diff
1152
__tests__/only-labels.spec.ts
Normal file
1152
__tests__/only-labels.spec.ts
Normal file
File diff suppressed because it is too large
Load Diff
567
__tests__/remove-stale-when-updated.spec.ts
Normal file
567
__tests__/remove-stale-when-updated.spec.ts
Normal file
@@ -0,0 +1,567 @@
|
||||
import {Issue} from '../src/classes/issue';
|
||||
import {IIssue} from '../src/interfaces/issue';
|
||||
import {IIssuesProcessorOptions} from '../src/interfaces/issues-processor-options';
|
||||
import {ILabel} from '../src/interfaces/label';
|
||||
import {IssuesProcessorMock} from './classes/issues-processor-mock';
|
||||
import {DefaultProcessorOptions} from './constants/default-processor-options';
|
||||
import {generateIssue} from './functions/generate-issue';
|
||||
|
||||
let issuesProcessorBuilder: IssuesProcessorBuilder;
|
||||
let issuesProcessor: IssuesProcessorMock;
|
||||
|
||||
/**
|
||||
* @description
|
||||
* Assuming there is a comment on the issue
|
||||
*/
|
||||
describe('remove-stale-when-updated option', (): void => {
|
||||
beforeEach((): void => {
|
||||
issuesProcessorBuilder = new IssuesProcessorBuilder();
|
||||
});
|
||||
|
||||
describe('when the option "remove-stale-when-updated" is disabled', (): void => {
|
||||
beforeEach((): void => {
|
||||
issuesProcessorBuilder.keepStaleWhenUpdated();
|
||||
});
|
||||
|
||||
test('should not remove the stale label on the issue', async (): Promise<void> => {
|
||||
expect.assertions(1);
|
||||
issuesProcessor = issuesProcessorBuilder.staleIssues([{}]).build();
|
||||
|
||||
await issuesProcessor.processIssues();
|
||||
|
||||
expect(issuesProcessor.removedLabelIssues).toHaveLength(0);
|
||||
});
|
||||
|
||||
test('should not remove the stale label on the pull request', async (): Promise<void> => {
|
||||
expect.assertions(1);
|
||||
issuesProcessor = issuesProcessorBuilder.stalePrs([{}]).build();
|
||||
|
||||
await issuesProcessor.processIssues();
|
||||
|
||||
expect(issuesProcessor.removedLabelIssues).toHaveLength(0);
|
||||
});
|
||||
});
|
||||
|
||||
describe('when the option "remove-stale-when-updated" is enabled', (): void => {
|
||||
beforeEach((): void => {
|
||||
issuesProcessorBuilder.removeStaleWhenUpdated();
|
||||
});
|
||||
|
||||
test('should remove the stale label on the issue', async (): Promise<void> => {
|
||||
expect.assertions(1);
|
||||
issuesProcessor = issuesProcessorBuilder.staleIssues([{}]).build();
|
||||
|
||||
await issuesProcessor.processIssues();
|
||||
|
||||
expect(issuesProcessor.removedLabelIssues).toHaveLength(1);
|
||||
});
|
||||
|
||||
test('should remove the stale label on the pull request', async (): Promise<void> => {
|
||||
expect.assertions(1);
|
||||
issuesProcessor = issuesProcessorBuilder.stalePrs([{}]).build();
|
||||
|
||||
await issuesProcessor.processIssues();
|
||||
|
||||
expect(issuesProcessor.removedLabelIssues).toHaveLength(1);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('remove-issue-stale-when-updated option', (): void => {
|
||||
beforeEach((): void => {
|
||||
issuesProcessorBuilder = new IssuesProcessorBuilder();
|
||||
});
|
||||
|
||||
describe('when the option "remove-stale-when-updated" is disabled', (): void => {
|
||||
beforeEach((): void => {
|
||||
issuesProcessorBuilder.keepStaleWhenUpdated();
|
||||
});
|
||||
|
||||
describe('when the option "remove-issue-stale-when-updated" is unset', (): void => {
|
||||
beforeEach((): void => {
|
||||
issuesProcessorBuilder.unsetIssueStaleWhenUpdated();
|
||||
});
|
||||
|
||||
test('should not remove the stale label on the issue', async (): Promise<void> => {
|
||||
expect.assertions(1);
|
||||
issuesProcessor = issuesProcessorBuilder.staleIssues([{}]).build();
|
||||
|
||||
await issuesProcessor.processIssues();
|
||||
|
||||
expect(issuesProcessor.removedLabelIssues).toHaveLength(0);
|
||||
});
|
||||
|
||||
test('should not remove the stale label on the pull request', async (): Promise<void> => {
|
||||
expect.assertions(1);
|
||||
issuesProcessor = issuesProcessorBuilder.stalePrs([{}]).build();
|
||||
|
||||
await issuesProcessor.processIssues();
|
||||
|
||||
expect(issuesProcessor.removedLabelIssues).toHaveLength(0);
|
||||
});
|
||||
});
|
||||
|
||||
describe('when the option "remove-issue-stale-when-updated" is disabled', (): void => {
|
||||
beforeEach((): void => {
|
||||
issuesProcessorBuilder.keepIssueStaleWhenUpdated();
|
||||
});
|
||||
|
||||
test('should not remove the stale label on the issue', async (): Promise<void> => {
|
||||
expect.assertions(1);
|
||||
issuesProcessor = issuesProcessorBuilder.staleIssues([{}]).build();
|
||||
|
||||
await issuesProcessor.processIssues();
|
||||
|
||||
expect(issuesProcessor.removedLabelIssues).toHaveLength(0);
|
||||
});
|
||||
|
||||
test('should not remove the stale label on the pull request', async (): Promise<void> => {
|
||||
expect.assertions(1);
|
||||
issuesProcessor = issuesProcessorBuilder.stalePrs([{}]).build();
|
||||
|
||||
await issuesProcessor.processIssues();
|
||||
|
||||
expect(issuesProcessor.removedLabelIssues).toHaveLength(0);
|
||||
});
|
||||
});
|
||||
|
||||
describe('when the option "remove-issue-stale-when-updated" is enabled', (): void => {
|
||||
beforeEach((): void => {
|
||||
issuesProcessorBuilder.removeIssueStaleWhenUpdated();
|
||||
});
|
||||
|
||||
test('should remove the stale label on the issue', async (): Promise<void> => {
|
||||
expect.assertions(1);
|
||||
issuesProcessor = issuesProcessorBuilder.staleIssues([{}]).build();
|
||||
|
||||
await issuesProcessor.processIssues();
|
||||
|
||||
expect(issuesProcessor.removedLabelIssues).toHaveLength(1);
|
||||
});
|
||||
|
||||
test('should not remove the stale label on the pull request', async (): Promise<void> => {
|
||||
expect.assertions(1);
|
||||
issuesProcessor = issuesProcessorBuilder.stalePrs([{}]).build();
|
||||
|
||||
await issuesProcessor.processIssues();
|
||||
|
||||
expect(issuesProcessor.removedLabelIssues).toHaveLength(0);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('when the option "remove-stale-when-updated" is enabled', (): void => {
|
||||
beforeEach((): void => {
|
||||
issuesProcessorBuilder.removeStaleWhenUpdated();
|
||||
});
|
||||
|
||||
describe('when the option "remove-issue-stale-when-updated" is unset', (): void => {
|
||||
beforeEach((): void => {
|
||||
issuesProcessorBuilder.unsetIssueStaleWhenUpdated();
|
||||
});
|
||||
|
||||
test('should remove the stale label on the issue', async (): Promise<void> => {
|
||||
expect.assertions(1);
|
||||
issuesProcessor = issuesProcessorBuilder.staleIssues([{}]).build();
|
||||
|
||||
await issuesProcessor.processIssues();
|
||||
|
||||
expect(issuesProcessor.removedLabelIssues).toHaveLength(1);
|
||||
});
|
||||
|
||||
test('should remove the stale label on the pull request', async (): Promise<void> => {
|
||||
expect.assertions(1);
|
||||
issuesProcessor = issuesProcessorBuilder.stalePrs([{}]).build();
|
||||
|
||||
await issuesProcessor.processIssues();
|
||||
|
||||
expect(issuesProcessor.removedLabelIssues).toHaveLength(1);
|
||||
});
|
||||
});
|
||||
|
||||
describe('when the option "remove-issue-stale-when-updated" is disabled', (): void => {
|
||||
beforeEach((): void => {
|
||||
issuesProcessorBuilder.keepIssueStaleWhenUpdated();
|
||||
});
|
||||
|
||||
test('should not remove the stale label on the issue', async (): Promise<void> => {
|
||||
expect.assertions(1);
|
||||
issuesProcessor = issuesProcessorBuilder.staleIssues([{}]).build();
|
||||
|
||||
await issuesProcessor.processIssues();
|
||||
|
||||
expect(issuesProcessor.removedLabelIssues).toHaveLength(0);
|
||||
});
|
||||
|
||||
test('should remove the stale label on the pull request', async (): Promise<void> => {
|
||||
expect.assertions(1);
|
||||
issuesProcessor = issuesProcessorBuilder.stalePrs([{}]).build();
|
||||
|
||||
await issuesProcessor.processIssues();
|
||||
|
||||
expect(issuesProcessor.removedLabelIssues).toHaveLength(1);
|
||||
});
|
||||
});
|
||||
|
||||
describe('when the option "remove-issue-stale-when-updated" is enabled', (): void => {
|
||||
beforeEach((): void => {
|
||||
issuesProcessorBuilder.removeIssueStaleWhenUpdated();
|
||||
});
|
||||
|
||||
test('should remove the stale label on the issue', async (): Promise<void> => {
|
||||
expect.assertions(1);
|
||||
issuesProcessor = issuesProcessorBuilder.staleIssues([{}]).build();
|
||||
|
||||
await issuesProcessor.processIssues();
|
||||
|
||||
expect(issuesProcessor.removedLabelIssues).toHaveLength(1);
|
||||
});
|
||||
|
||||
test('should remove the stale label on the pull request', async (): Promise<void> => {
|
||||
expect.assertions(1);
|
||||
issuesProcessor = issuesProcessorBuilder.stalePrs([{}]).build();
|
||||
|
||||
await issuesProcessor.processIssues();
|
||||
|
||||
expect(issuesProcessor.removedLabelIssues).toHaveLength(1);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('remove-pr-stale-when-updated option', (): void => {
|
||||
beforeEach((): void => {
|
||||
issuesProcessorBuilder = new IssuesProcessorBuilder();
|
||||
});
|
||||
|
||||
describe('when the option "remove-stale-when-updated" is disabled', (): void => {
|
||||
beforeEach((): void => {
|
||||
issuesProcessorBuilder.keepStaleWhenUpdated();
|
||||
});
|
||||
|
||||
describe('when the option "remove-pr-stale-when-updated" is unset', (): void => {
|
||||
beforeEach((): void => {
|
||||
issuesProcessorBuilder.unsetPrStaleWhenUpdated();
|
||||
});
|
||||
|
||||
test('should not remove the stale label on the issue', async (): Promise<void> => {
|
||||
expect.assertions(1);
|
||||
issuesProcessor = issuesProcessorBuilder.staleIssues([{}]).build();
|
||||
|
||||
await issuesProcessor.processIssues();
|
||||
|
||||
expect(issuesProcessor.removedLabelIssues).toHaveLength(0);
|
||||
});
|
||||
|
||||
test('should not remove the stale label on the pull request', async (): Promise<void> => {
|
||||
expect.assertions(1);
|
||||
issuesProcessor = issuesProcessorBuilder.stalePrs([{}]).build();
|
||||
|
||||
await issuesProcessor.processIssues();
|
||||
|
||||
expect(issuesProcessor.removedLabelIssues).toHaveLength(0);
|
||||
});
|
||||
});
|
||||
|
||||
describe('when the option "remove-pr-stale-when-updated" is disabled', (): void => {
|
||||
beforeEach((): void => {
|
||||
issuesProcessorBuilder.keepPrStaleWhenUpdated();
|
||||
});
|
||||
|
||||
test('should not remove the stale label on the issue', async (): Promise<void> => {
|
||||
expect.assertions(1);
|
||||
issuesProcessor = issuesProcessorBuilder.staleIssues([{}]).build();
|
||||
|
||||
await issuesProcessor.processIssues();
|
||||
|
||||
expect(issuesProcessor.removedLabelIssues).toHaveLength(0);
|
||||
});
|
||||
|
||||
test('should not remove the stale label on the pull request', async (): Promise<void> => {
|
||||
expect.assertions(1);
|
||||
issuesProcessor = issuesProcessorBuilder.stalePrs([{}]).build();
|
||||
|
||||
await issuesProcessor.processIssues();
|
||||
|
||||
expect(issuesProcessor.removedLabelIssues).toHaveLength(0);
|
||||
});
|
||||
});
|
||||
|
||||
describe('when the option "remove-pr-stale-when-updated" is enabled', (): void => {
|
||||
beforeEach((): void => {
|
||||
issuesProcessorBuilder.removePrStaleWhenUpdated();
|
||||
});
|
||||
|
||||
test('should not remove the stale label on the issue', async (): Promise<void> => {
|
||||
expect.assertions(1);
|
||||
issuesProcessor = issuesProcessorBuilder.staleIssues([{}]).build();
|
||||
|
||||
await issuesProcessor.processIssues();
|
||||
|
||||
expect(issuesProcessor.removedLabelIssues).toHaveLength(0);
|
||||
});
|
||||
|
||||
test('should remove the stale label on the pull request', async (): Promise<void> => {
|
||||
expect.assertions(1);
|
||||
issuesProcessor = issuesProcessorBuilder.stalePrs([{}]).build();
|
||||
|
||||
await issuesProcessor.processIssues();
|
||||
|
||||
expect(issuesProcessor.removedLabelIssues).toHaveLength(1);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('when the option "remove-stale-when-updated" is enabled', (): void => {
|
||||
beforeEach((): void => {
|
||||
issuesProcessorBuilder.removeStaleWhenUpdated();
|
||||
});
|
||||
|
||||
describe('when the option "remove-pr-stale-when-updated" is unset', (): void => {
|
||||
beforeEach((): void => {
|
||||
issuesProcessorBuilder.unsetPrStaleWhenUpdated();
|
||||
});
|
||||
|
||||
test('should remove the stale label on the issue', async (): Promise<void> => {
|
||||
expect.assertions(1);
|
||||
issuesProcessor = issuesProcessorBuilder.staleIssues([{}]).build();
|
||||
|
||||
await issuesProcessor.processIssues();
|
||||
|
||||
expect(issuesProcessor.removedLabelIssues).toHaveLength(1);
|
||||
});
|
||||
|
||||
test('should remove the stale label on the pull request', async (): Promise<void> => {
|
||||
expect.assertions(1);
|
||||
issuesProcessor = issuesProcessorBuilder.stalePrs([{}]).build();
|
||||
|
||||
await issuesProcessor.processIssues();
|
||||
|
||||
expect(issuesProcessor.removedLabelIssues).toHaveLength(1);
|
||||
});
|
||||
});
|
||||
|
||||
describe('when the option "remove-pr-stale-when-updated" is disabled', (): void => {
|
||||
beforeEach((): void => {
|
||||
issuesProcessorBuilder.keepPrStaleWhenUpdated();
|
||||
});
|
||||
|
||||
test('should remove the stale label on the issue', async (): Promise<void> => {
|
||||
expect.assertions(1);
|
||||
issuesProcessor = issuesProcessorBuilder.staleIssues([{}]).build();
|
||||
|
||||
await issuesProcessor.processIssues();
|
||||
|
||||
expect(issuesProcessor.removedLabelIssues).toHaveLength(1);
|
||||
});
|
||||
|
||||
test('should not remove the stale label on the pull request', async (): Promise<void> => {
|
||||
expect.assertions(1);
|
||||
issuesProcessor = issuesProcessorBuilder.stalePrs([{}]).build();
|
||||
|
||||
await issuesProcessor.processIssues();
|
||||
|
||||
expect(issuesProcessor.removedLabelIssues).toHaveLength(0);
|
||||
});
|
||||
});
|
||||
|
||||
describe('when the option "remove-pr-stale-when-updated" is enabled', (): void => {
|
||||
beforeEach((): void => {
|
||||
issuesProcessorBuilder.removePrStaleWhenUpdated();
|
||||
});
|
||||
|
||||
test('should remove the stale label on the issue', async (): Promise<void> => {
|
||||
expect.assertions(1);
|
||||
issuesProcessor = issuesProcessorBuilder.staleIssues([{}]).build();
|
||||
|
||||
await issuesProcessor.processIssues();
|
||||
|
||||
expect(issuesProcessor.removedLabelIssues).toHaveLength(1);
|
||||
});
|
||||
|
||||
test('should remove the stale label on the pull request', async (): Promise<void> => {
|
||||
expect.assertions(1);
|
||||
issuesProcessor = issuesProcessorBuilder.stalePrs([{}]).build();
|
||||
|
||||
await issuesProcessor.processIssues();
|
||||
|
||||
expect(issuesProcessor.removedLabelIssues).toHaveLength(1);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
class IssuesProcessorBuilder {
|
||||
private _options: IIssuesProcessorOptions = {
|
||||
...DefaultProcessorOptions
|
||||
};
|
||||
private _issues: Issue[] = [];
|
||||
|
||||
keepStaleWhenUpdated(): IssuesProcessorBuilder {
|
||||
this._options.removeStaleWhenUpdated = false;
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
removeStaleWhenUpdated(): IssuesProcessorBuilder {
|
||||
this._options.removeStaleWhenUpdated = true;
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
unsetIssueStaleWhenUpdated(): IssuesProcessorBuilder {
|
||||
delete this._options.removeIssueStaleWhenUpdated;
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
keepIssueStaleWhenUpdated(): IssuesProcessorBuilder {
|
||||
this._options.removeIssueStaleWhenUpdated = false;
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
removeIssueStaleWhenUpdated(): IssuesProcessorBuilder {
|
||||
this._options.removeIssueStaleWhenUpdated = true;
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
unsetPrStaleWhenUpdated(): IssuesProcessorBuilder {
|
||||
delete this._options.removePrStaleWhenUpdated;
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
keepPrStaleWhenUpdated(): IssuesProcessorBuilder {
|
||||
this._options.removePrStaleWhenUpdated = false;
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
removePrStaleWhenUpdated(): IssuesProcessorBuilder {
|
||||
this._options.removePrStaleWhenUpdated = true;
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
issuesOrPrs(issues: Partial<IIssue>[]): IssuesProcessorBuilder {
|
||||
this._issues = issues.map(
|
||||
(issue: Readonly<Partial<IIssue>>, index: Readonly<number>): Issue =>
|
||||
generateIssue(
|
||||
this._options,
|
||||
index,
|
||||
issue.title ?? 'dummy-title',
|
||||
issue.updated_at ?? new Date().toDateString(),
|
||||
issue.created_at ?? new Date().toDateString(),
|
||||
!!issue.pull_request,
|
||||
issue.labels ? issue.labels.map(label => label.name) : []
|
||||
)
|
||||
);
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
issues(issues: Partial<IIssue>[]): IssuesProcessorBuilder {
|
||||
this.issuesOrPrs(
|
||||
issues.map(
|
||||
(issue: Readonly<Partial<IIssue>>): Partial<IIssue> => {
|
||||
return {
|
||||
...issue,
|
||||
pull_request: null
|
||||
};
|
||||
}
|
||||
)
|
||||
);
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
staleIssues(issues: Partial<IIssue>[]): IssuesProcessorBuilder {
|
||||
this.issues(
|
||||
issues.map(
|
||||
(issue: Readonly<Partial<IIssue>>): Partial<IIssue> => {
|
||||
return {
|
||||
...issue,
|
||||
updated_at: '2020-01-01T17:00:00Z',
|
||||
created_at: '2020-01-01T17:00:00Z',
|
||||
labels: issue.labels?.map(
|
||||
(label: Readonly<ILabel>): ILabel => {
|
||||
return {
|
||||
...label,
|
||||
name: 'Stale'
|
||||
};
|
||||
}
|
||||
) ?? [
|
||||
{
|
||||
name: 'Stale'
|
||||
}
|
||||
]
|
||||
};
|
||||
}
|
||||
)
|
||||
);
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
prs(issues: Partial<IIssue>[]): IssuesProcessorBuilder {
|
||||
this.issuesOrPrs(
|
||||
issues.map(
|
||||
(issue: Readonly<Partial<IIssue>>): Partial<IIssue> => {
|
||||
return {
|
||||
...issue,
|
||||
pull_request: {key: 'value'}
|
||||
};
|
||||
}
|
||||
)
|
||||
);
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
stalePrs(issues: Partial<IIssue>[]): IssuesProcessorBuilder {
|
||||
this.prs(
|
||||
issues.map(
|
||||
(issue: Readonly<Partial<IIssue>>): Partial<IIssue> => {
|
||||
return {
|
||||
...issue,
|
||||
updated_at: '2020-01-01T17:00:00Z',
|
||||
created_at: '2020-01-01T17:00:00Z',
|
||||
labels: issue.labels?.map(
|
||||
(label: Readonly<ILabel>): ILabel => {
|
||||
return {
|
||||
...label,
|
||||
name: 'Stale'
|
||||
};
|
||||
}
|
||||
) ?? [
|
||||
{
|
||||
name: 'Stale'
|
||||
}
|
||||
]
|
||||
};
|
||||
}
|
||||
)
|
||||
);
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
build(): IssuesProcessorMock {
|
||||
return new IssuesProcessorMock(
|
||||
this._options,
|
||||
async () => 'abot',
|
||||
async p => (p === 1 ? this._issues : []),
|
||||
async () => [
|
||||
{
|
||||
user: {
|
||||
login: 'notme',
|
||||
type: 'User'
|
||||
}
|
||||
}
|
||||
],
|
||||
async () => new Date().toDateString()
|
||||
);
|
||||
}
|
||||
}
|
||||
90
action.yml
90
action.yml
@@ -10,13 +10,13 @@ inputs:
|
||||
description: 'The message to post on the issue when tagging it. If none provided, will not mark issues stale.'
|
||||
required: false
|
||||
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 pull request when tagging it. If none provided, will not mark pull requests stale.'
|
||||
required: false
|
||||
close-issue-message:
|
||||
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:
|
||||
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 pull request when closing it. If none provided, will not comment when closing a pull requests.'
|
||||
required: false
|
||||
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.'
|
||||
@@ -46,7 +46,7 @@ inputs:
|
||||
description: 'The label to apply when an issue is closed.'
|
||||
required: false
|
||||
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: ''
|
||||
required: false
|
||||
stale-pr-label:
|
||||
@@ -57,23 +57,55 @@ inputs:
|
||||
description: 'The label to apply when a pull request is closed.'
|
||||
required: false
|
||||
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 as stale. Separate multiple labels with commas (eg. "label1,label2").'
|
||||
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")'
|
||||
description: 'The milestones that mean an issue or a pull request is exempt from being marked as 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.'
|
||||
description: 'The milestones that mean an issue is exempt from being marked as stale. Separate multiple milestones with commas (eg. "milestone1,milestone2"). Override "exempt-milestones" option regarding only the issues.'
|
||||
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.'
|
||||
description: 'The milestones that mean a pull request is exempt from being marked as stale. Separate multiple milestones with commas (eg. "milestone1,milestone2"). Override "exempt-milestones" option regarding only the pull requests.'
|
||||
default: ''
|
||||
required: false
|
||||
exempt-all-milestones:
|
||||
description: 'Exempt all issues and pull requests with milestones from being marked as stale. Default to false.'
|
||||
default: 'false'
|
||||
required: false
|
||||
exempt-all-issue-milestones:
|
||||
description: 'Exempt all issues with milestones from being marked as stale. Override "exempt-all-milestones" option regarding only the issues.'
|
||||
default: ''
|
||||
required: false
|
||||
exempt-all-pr-milestones:
|
||||
description: 'Exempt all pull requests with milestones from being marked as stale. Override "exempt-all-milestones" option regarding only the pull requests.'
|
||||
default: ''
|
||||
required: false
|
||||
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: ''
|
||||
required: false
|
||||
any-of-labels:
|
||||
description: 'Only issues or pull requests with at least one of these labels are checked if stale. Defaults to `` (disabled) and can be a comma-separated list of labels.'
|
||||
default: ''
|
||||
required: false
|
||||
any-of-issue-labels:
|
||||
description: 'Only issues with at least one of these labels are checked if stale. Defaults to `` (disabled) and can be a comma-separated list of labels. Override "any-of-labels" option regarding only the issues.'
|
||||
default: ''
|
||||
required: false
|
||||
any-of-pr-labels:
|
||||
description: 'Only pull requests with at least one of these labels are checked if stale. Defaults to `` (disabled) and can be a comma-separated list of labels. Override "any-of-labels" option regarding only the pull requests.'
|
||||
default: ''
|
||||
required: false
|
||||
only-issue-labels:
|
||||
description: 'Only issues with all of these labels are checked if stale. Defaults to `[]` (disabled) and can be a comma-separated list of labels. Override "only-labels" option regarding only the issues.'
|
||||
default: ''
|
||||
required: false
|
||||
only-pr-labels:
|
||||
description: 'Only pull requests with all of these labels are checked if stale. Defaults to `[]` (disabled) and can be a comma-separated list of labels. Override "only-labels" option regarding only the pull requests.'
|
||||
default: ''
|
||||
required: false
|
||||
operations-per-run:
|
||||
@@ -81,7 +113,15 @@ inputs:
|
||||
default: '30'
|
||||
required: false
|
||||
remove-stale-when-updated:
|
||||
description: 'Remove stale labels from issues when they are updated or commented on.'
|
||||
description: 'Remove stale labels from issues and pull requests when they are updated or commented on.'
|
||||
default: 'true'
|
||||
required: false
|
||||
remove-issue-stale-when-updated:
|
||||
description: 'Remove stale labels from issues when they are updated or commented on. Override "remove-stale-when-updated" option regarding only the issues.'
|
||||
default: 'true'
|
||||
required: false
|
||||
remove-pr-stale-when-updated:
|
||||
description: 'Remove stale labels from pull requests when they are updated or commented on. Override "remove-stale-when-updated" option regarding only the pull requests.'
|
||||
default: 'true'
|
||||
required: false
|
||||
debug-only:
|
||||
@@ -89,7 +129,7 @@ inputs:
|
||||
default: 'false'
|
||||
required: false
|
||||
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'
|
||||
required: false
|
||||
skip-stale-pr-message:
|
||||
@@ -105,9 +145,37 @@ inputs:
|
||||
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).'
|
||||
description: 'The date used to skip the stale action on issue/pull request created before it (ISO 8601 or RFC 2822).'
|
||||
default: ''
|
||||
required: false
|
||||
exempt-assignees:
|
||||
description: 'The assignees which exempt an issue or a pull request from being marked as stale. Separate multiple assignees with commas (eg. "user1,user2").'
|
||||
default: ''
|
||||
required: false
|
||||
exempt-issue-assignees:
|
||||
description: 'The assignees which exempt an issue from being marked as stale. Separate multiple assignees with commas (eg. "user1,user2"). Override "exempt-assignees" option regarding only the issues.'
|
||||
default: ''
|
||||
required: false
|
||||
exempt-pr-assignees:
|
||||
description: 'The assignees which exempt a pull request from being marked as stale. Separate multiple assignees with commas (eg. "user1,user2"). Override "exempt-assignees" option regarding only the pull requests.'
|
||||
default: ''
|
||||
required: false
|
||||
exempt-all-assignees:
|
||||
description: 'Exempt all issues and pull requests with assignees from being marked as stale. Default to false.'
|
||||
default: 'false'
|
||||
required: false
|
||||
exempt-all-issue-assignees:
|
||||
description: 'Exempt all issues with assignees from being marked as stale. Override "exempt-all-assignees" option regarding only the issues.'
|
||||
default: ''
|
||||
required: false
|
||||
exempt-all-pr-assignees:
|
||||
description: 'Exempt all pull requests with assignees from being marked as stale. Override "exempt-all-assignees" option regarding only the pull requests.'
|
||||
default: ''
|
||||
required: false
|
||||
enable-statistics:
|
||||
description: 'Display some statistics at the end regarding the stale workflow (only when the logs are enabled).'
|
||||
default: 'true'
|
||||
required: false
|
||||
runs:
|
||||
using: 'node12'
|
||||
main: 'dist/index.js'
|
||||
|
||||
5333
dist/index.js
vendored
5333
dist/index.js
vendored
File diff suppressed because it is too large
Load Diff
@@ -7,5 +7,6 @@ module.exports = {
|
||||
transform: {
|
||||
'^.+\\.ts$': 'ts-jest'
|
||||
},
|
||||
verbose: true
|
||||
verbose: true,
|
||||
setupFilesAfterEnv: [`./jest/test.ts`]
|
||||
};
|
||||
|
||||
11
jest/test.ts
Normal file
11
jest/test.ts
Normal file
@@ -0,0 +1,11 @@
|
||||
import chalk from 'chalk';
|
||||
|
||||
// Disabled the colors to:
|
||||
// - improve the performances
|
||||
// - avoid to mock chalk
|
||||
// - avoid to have failing tests when testing the logs due to the extra text the log message will contains
|
||||
//
|
||||
// Note:
|
||||
// If you need to debug the log colours you can remove this line temporarily
|
||||
// But some tests will fail
|
||||
chalk.level = 0;
|
||||
2377
package-lock.json
generated
2377
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
33
package.json
33
package.json
@@ -1,11 +1,11 @@
|
||||
{
|
||||
"name": "stale-action",
|
||||
"version": "2.0.0",
|
||||
"version": "4.0.1",
|
||||
"private": true,
|
||||
"description": "Marks old issues and PRs as stale",
|
||||
"main": "lib/main.js",
|
||||
"scripts": {
|
||||
"build": "tsc",
|
||||
"build": "tsc --project tsconfig.app.json",
|
||||
"format": "prettier --write --ignore-unknown **/*.{md,json,yml,ts}",
|
||||
"format-check": "prettier --check --ignore-unknown **/*.{md,json,yml,ts}",
|
||||
"lint": "eslint src/**/*.ts",
|
||||
@@ -14,6 +14,8 @@
|
||||
"lint:all:fix": "npm run format && npm run lint:fix",
|
||||
"pack": "ncc build",
|
||||
"test": "jest",
|
||||
"test:only-errors": "jest --reporters jest-silent-reporter --silent",
|
||||
"test:watch": "jest --watch --notify --expand",
|
||||
"all": "npm run build && npm run format && npm run lint && npm run pack && npm test"
|
||||
},
|
||||
"repository": {
|
||||
@@ -28,28 +30,31 @@
|
||||
"author": "GitHub",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@actions/core": "^1.2.6",
|
||||
"@actions/core": "file:../toolkit/packages/core",
|
||||
"@actions/github": "^4.0.0",
|
||||
"@octokit/rest": "^18.0.12",
|
||||
"lodash.deburr": "^4.1.0",
|
||||
"semver": "^7.3.4"
|
||||
"semver": "^7.3.5",
|
||||
"standard-version": "^9.3.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/jest": "^26.0.20",
|
||||
"@types/lodash.deburr": "^4.1.6",
|
||||
"@types/node": "^14.14.21",
|
||||
"@types/semver": "^7.3.4",
|
||||
"@typescript-eslint/eslint-plugin": "^4.14.0",
|
||||
"@typescript-eslint/parser": "^4.14.0",
|
||||
"@types/node": "^15.0.2",
|
||||
"@types/semver": "^7.3.5",
|
||||
"@typescript-eslint/eslint-plugin": "^4.16.1",
|
||||
"@typescript-eslint/parser": "^4.22.1",
|
||||
"@vercel/ncc": "^0.27.0",
|
||||
"eslint": "^7.18.0",
|
||||
"eslint-plugin-github": "^4.0.1",
|
||||
"eslint-plugin-jest": "^24.1.3",
|
||||
"chalk": "^4.1.0",
|
||||
"eslint": "^7.21.0",
|
||||
"eslint-plugin-github": "^4.1.2",
|
||||
"eslint-plugin-jest": "^24.3.6",
|
||||
"jest": "^26.6.3",
|
||||
"jest-circus": "^26.6.3",
|
||||
"jest-silent-reporter": "^0.4.0",
|
||||
"js-yaml": "^4.0.0",
|
||||
"prettier": "^2.2.1",
|
||||
"ts-jest": "^26.4.4",
|
||||
"typescript": "^4.1.3"
|
||||
"terminal-link": "^2.1.1",
|
||||
"ts-jest": "^26.5.3",
|
||||
"typescript": "^4.2.4"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,746 +0,0 @@
|
||||
import {context, getOctokit} from '@actions/github';
|
||||
import {GitHub} from '@actions/github/lib/utils';
|
||||
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 {isPullRequest} from './functions/is-pull-request';
|
||||
import {shouldMarkWhenStale} from './functions/should-mark-when-stale';
|
||||
import {IsoOrRfcDateString} from './types/iso-or-rfc-date-string';
|
||||
import {wordsToList} from './functions/words-to-list';
|
||||
import {IIssue} from './interfaces/issue';
|
||||
|
||||
export interface PullRequest {
|
||||
number: number;
|
||||
head: {
|
||||
ref: string;
|
||||
};
|
||||
}
|
||||
|
||||
export interface User {
|
||||
type: string;
|
||||
login: string;
|
||||
}
|
||||
|
||||
export interface Comment {
|
||||
user: User;
|
||||
}
|
||||
|
||||
export interface IssueEvent {
|
||||
created_at: string;
|
||||
event: string;
|
||||
label: Label;
|
||||
}
|
||||
|
||||
export interface Label {
|
||||
name: string;
|
||||
}
|
||||
|
||||
export interface IssueProcessorOptions {
|
||||
repoToken: string;
|
||||
staleIssueMessage: string;
|
||||
stalePrMessage: string;
|
||||
closeIssueMessage: string;
|
||||
closePrMessage: string;
|
||||
daysBeforeStale: number;
|
||||
daysBeforeIssueStale: number; // Could be NaN
|
||||
daysBeforePrStale: number; // Could be NaN
|
||||
daysBeforeClose: number;
|
||||
daysBeforeIssueClose: number; // Could be NaN
|
||||
daysBeforePrClose: number; // Could be NaN
|
||||
staleIssueLabel: string;
|
||||
closeIssueLabel: string;
|
||||
exemptIssueLabels: string;
|
||||
stalePrLabel: string;
|
||||
closePrLabel: string;
|
||||
exemptPrLabels: string;
|
||||
onlyLabels: string;
|
||||
operationsPerRun: number;
|
||||
removeStaleWhenUpdated: boolean;
|
||||
debugOnly: boolean;
|
||||
ascending: boolean;
|
||||
skipStaleIssueMessage: boolean;
|
||||
skipStalePrMessage: 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.
|
||||
*/
|
||||
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 options: IssueProcessorOptions;
|
||||
readonly staleIssues: Issue[] = [];
|
||||
readonly closedIssues: Issue[] = [];
|
||||
readonly deletedBranchIssues: Issue[] = [];
|
||||
readonly removedLabelIssues: Issue[] = [];
|
||||
|
||||
constructor(
|
||||
options: IssueProcessorOptions,
|
||||
getActor?: () => Promise<string>,
|
||||
getIssues?: (page: number) => Promise<Issue[]>,
|
||||
listIssueComments?: (
|
||||
issueNumber: number,
|
||||
sinceDate: string
|
||||
) => Promise<Comment[]>,
|
||||
getLabelCreationDate?: (
|
||||
issue: Issue,
|
||||
label: string
|
||||
) => Promise<string | undefined>
|
||||
) {
|
||||
this.options = options;
|
||||
this._operationsLeft = options.operationsPerRun;
|
||||
this.client = getOctokit(options.repoToken);
|
||||
|
||||
if (getActor) {
|
||||
this._getActor = getActor;
|
||||
}
|
||||
|
||||
if (getIssues) {
|
||||
this._getIssues = getIssues;
|
||||
}
|
||||
|
||||
if (listIssueComments) {
|
||||
this._listIssueComments = listIssueComments;
|
||||
}
|
||||
|
||||
if (getLabelCreationDate) {
|
||||
this._getLabelCreationDate = getLabelCreationDate;
|
||||
}
|
||||
|
||||
if (this.options.debugOnly) {
|
||||
this._logger.warning(
|
||||
'Executing in debug mode. Debug output will be written but no issues will be processed.'
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
async processIssues(page = 1): Promise<number> {
|
||||
// get the next batch of issues
|
||||
const issues: Issue[] = await this._getIssues(page);
|
||||
this._operationsLeft -= 1;
|
||||
|
||||
const actor: string = await this._getActor();
|
||||
|
||||
if (issues.length <= 0) {
|
||||
this._logger.info('---');
|
||||
this._logger.info('No more issues found to process. Exiting.');
|
||||
return this._operationsLeft;
|
||||
}
|
||||
|
||||
for (const issue of issues.values()) {
|
||||
const issueLogger: IssueLogger = new IssueLogger(issue);
|
||||
|
||||
issueLogger.info(
|
||||
`Found issue: issue #${issue.number} last updated ${issue.updated_at} (is pr? ${issue.isPullRequest})`
|
||||
);
|
||||
|
||||
// calculate string based messages for this issue
|
||||
const staleMessage: string = issue.isPullRequest
|
||||
? this.options.stalePrMessage
|
||||
: this.options.staleIssueMessage;
|
||||
const closeMessage: string = issue.isPullRequest
|
||||
? this.options.closePrMessage
|
||||
: this.options.closeIssueMessage;
|
||||
const staleLabel: string = issue.isPullRequest
|
||||
? this.options.stalePrLabel
|
||||
: this.options.staleIssueLabel;
|
||||
const closeLabel: string = issue.isPullRequest
|
||||
? this.options.closePrLabel
|
||||
: this.options.closeIssueLabel;
|
||||
const skipMessage = issue.isPullRequest
|
||||
? this.options.skipStalePrMessage
|
||||
: this.options.skipStaleIssueMessage;
|
||||
const issueType: IssueType = getIssueType(issue.isPullRequest);
|
||||
const daysBeforeStale: number = issue.isPullRequest
|
||||
? this._getDaysBeforePrStale()
|
||||
: this._getDaysBeforeIssueStale();
|
||||
|
||||
if (issue.isPullRequest) {
|
||||
issueLogger.info(`Days before pull request stale: ${daysBeforeStale}`);
|
||||
} 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;
|
||||
}
|
||||
|
||||
if (issue.state === 'closed') {
|
||||
issueLogger.info(`Skipping ${issueType} because it is closed`);
|
||||
continue; // don't process closed issues
|
||||
}
|
||||
|
||||
if (issue.locked) {
|
||||
issueLogger.info(`Skipping ${issueType} because it is locked`);
|
||||
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 (
|
||||
exemptLabels.some((exemptLabel: Readonly<string>): boolean =>
|
||||
isLabeled(issue, exemptLabel)
|
||||
)
|
||||
) {
|
||||
if (issue.isStale) {
|
||||
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
|
||||
}
|
||||
|
||||
const milestones: Milestones = new Milestones(this.options, issue);
|
||||
|
||||
if (milestones.shouldExemptMilestones()) {
|
||||
issueLogger.info(
|
||||
`Skipping ${issueType} because it has an exempt milestone`
|
||||
);
|
||||
continue; // don't process exempt milestones
|
||||
}
|
||||
|
||||
// should this issue be marked stale?
|
||||
const shouldBeStale = !IssueProcessor._updatedSince(
|
||||
issue.updated_at,
|
||||
daysBeforeStale
|
||||
);
|
||||
|
||||
// determine if this issue needs to be marked stale first
|
||||
if (!issue.isStale && shouldBeStale && shouldMarkAsStale) {
|
||||
issueLogger.info(
|
||||
`Marking ${issueType} stale because it was last updated on ${issue.updated_at} and it does not have a stale label`
|
||||
);
|
||||
await this._markStale(issue, staleMessage, staleLabel, skipMessage);
|
||||
issue.isStale = true; // this issue is now considered stale
|
||||
} else if (!issue.isStale) {
|
||||
issueLogger.info(
|
||||
`Not marking as stale: shouldBeStale=${shouldBeStale}, shouldMarkAsStale=${shouldMarkAsStale}`
|
||||
);
|
||||
}
|
||||
|
||||
// process the issue if it was marked stale
|
||||
if (issue.isStale) {
|
||||
issueLogger.info(`Found a stale ${issueType}`);
|
||||
await this._processStaleIssue(
|
||||
issue,
|
||||
issueType,
|
||||
staleLabel,
|
||||
actor,
|
||||
closeMessage,
|
||||
closeLabel
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
if (this._operationsLeft <= 0) {
|
||||
this._logger.warning(
|
||||
'Reached max number of operations to process. Exiting.'
|
||||
);
|
||||
return 0;
|
||||
}
|
||||
|
||||
// do the next batch
|
||||
return this.processIssues(page + 1);
|
||||
}
|
||||
|
||||
// handle all of the stale issue logic when we find a stale issue
|
||||
private async _processStaleIssue(
|
||||
issue: Issue,
|
||||
issueType: IssueType,
|
||||
staleLabel: string,
|
||||
actor: string,
|
||||
closeMessage?: string,
|
||||
closeLabel?: string
|
||||
) {
|
||||
const issueLogger: IssueLogger = new IssueLogger(issue);
|
||||
const markedStaleOn: string =
|
||||
(await this._getLabelCreationDate(issue, staleLabel)) || issue.updated_at;
|
||||
issueLogger.info(
|
||||
`Issue #${issue.number} marked stale on: ${markedStaleOn}`
|
||||
);
|
||||
|
||||
const issueHasComments: boolean = await this._hasCommentsSince(
|
||||
issue,
|
||||
markedStaleOn,
|
||||
actor
|
||||
);
|
||||
issueLogger.info(
|
||||
`Issue #${issue.number} has been commented on: ${issueHasComments}`
|
||||
);
|
||||
|
||||
const isPr: boolean = isPullRequest(issue);
|
||||
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,
|
||||
daysBeforeClose
|
||||
);
|
||||
issueLogger.info(
|
||||
`Issue #${issue.number} has been updated: ${issueHasUpdate}`
|
||||
);
|
||||
|
||||
// should we un-stale this issue?
|
||||
if (this.options.removeStaleWhenUpdated && issueHasComments) {
|
||||
await this._removeStaleLabel(issue, staleLabel);
|
||||
}
|
||||
|
||||
// now start closing logic
|
||||
if (daysBeforeClose < 0) {
|
||||
return; // nothing to do because we aren't closing stale issues
|
||||
}
|
||||
|
||||
if (!issueHasComments && !issueHasUpdate) {
|
||||
issueLogger.info(
|
||||
`Closing ${issueType} because it was last updated on ${issue.updated_at}`
|
||||
);
|
||||
await this._closeIssue(issue, closeMessage, closeLabel);
|
||||
|
||||
if (this.options.deleteBranch && issue.pull_request) {
|
||||
issueLogger.info(
|
||||
`Deleting branch for #${issue.number} as delete-branch option was specified`
|
||||
);
|
||||
await this._deleteBranch(issue);
|
||||
this.deletedBranchIssues.push(issue);
|
||||
}
|
||||
} else {
|
||||
issueLogger.info(
|
||||
`Stale ${issueType} is not old enough to close yet (hasComments? ${issueHasComments}, hasUpdate? ${issueHasUpdate})`
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// checks to see if a given issue is still stale (has had activity on it)
|
||||
private async _hasCommentsSince(
|
||||
issue: Issue,
|
||||
sinceDate: string,
|
||||
actor: string
|
||||
): Promise<boolean> {
|
||||
const issueLogger: IssueLogger = new IssueLogger(issue);
|
||||
|
||||
issueLogger.info(
|
||||
`Checking for comments on issue #${issue.number} since ${sinceDate}`
|
||||
);
|
||||
|
||||
if (!sinceDate) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// find any comments since the date
|
||||
const comments = await this._listIssueComments(issue.number, sinceDate);
|
||||
|
||||
const filteredComments = comments.filter(
|
||||
comment => comment.user.type === 'User' && comment.user.login !== actor
|
||||
);
|
||||
|
||||
issueLogger.info(
|
||||
`Comments not made by actor or another bot: ${filteredComments.length}`
|
||||
);
|
||||
|
||||
// if there are any user comments returned
|
||||
return filteredComments.length > 0;
|
||||
}
|
||||
|
||||
// grab comments for an issue since a given date
|
||||
private async _listIssueComments(
|
||||
issueNumber: number,
|
||||
sinceDate: string
|
||||
): Promise<Comment[]> {
|
||||
// find any comments since date on the given issue
|
||||
try {
|
||||
const comments = await this.client.issues.listComments({
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
issue_number: issueNumber,
|
||||
since: sinceDate
|
||||
});
|
||||
return comments.data;
|
||||
} catch (error) {
|
||||
this._logger.error(`List issue comments error: ${error.message}`);
|
||||
return Promise.resolve([]);
|
||||
}
|
||||
}
|
||||
|
||||
// get the actor from the GitHub token or context
|
||||
private async _getActor(): Promise<string> {
|
||||
let actor;
|
||||
try {
|
||||
actor = await this.client.users.getAuthenticated();
|
||||
} catch (error) {
|
||||
return context.actor;
|
||||
}
|
||||
|
||||
return actor.data.login;
|
||||
}
|
||||
|
||||
// grab issues from github in batches of 100
|
||||
private async _getIssues(page: number): Promise<Issue[]> {
|
||||
// generate type for response
|
||||
const endpoint = this.client.issues.listForRepo;
|
||||
type OctoKitIssueList = GetResponseTypeFromEndpointMethod<typeof endpoint>;
|
||||
|
||||
try {
|
||||
const issueResult: OctoKitIssueList = await this.client.issues.listForRepo(
|
||||
{
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
state: 'open',
|
||||
labels: this.options.onlyLabels,
|
||||
per_page: 100,
|
||||
direction: this.options.ascending ? 'asc' : 'desc',
|
||||
page
|
||||
}
|
||||
);
|
||||
|
||||
return issueResult.data.map(
|
||||
(issue: Readonly<IIssue>): Issue => new Issue(this.options, issue)
|
||||
);
|
||||
} catch (error) {
|
||||
this._logger.error(`Get issues for repo error: ${error.message}`);
|
||||
return Promise.resolve([]);
|
||||
}
|
||||
}
|
||||
|
||||
// Mark an issue as stale with a comment and a label
|
||||
private async _markStale(
|
||||
issue: Issue,
|
||||
staleMessage: string,
|
||||
staleLabel: string,
|
||||
skipMessage: boolean
|
||||
): Promise<void> {
|
||||
const issueLogger: IssueLogger = new IssueLogger(issue);
|
||||
|
||||
issueLogger.info(`Marking issue #${issue.number} as stale`);
|
||||
|
||||
this.staleIssues.push(issue);
|
||||
|
||||
this._operationsLeft -= 2;
|
||||
|
||||
// if the issue is being marked stale, the updated date should be changed to right now
|
||||
// so that close calculations work correctly
|
||||
const newUpdatedAtDate: Date = new Date();
|
||||
issue.updated_at = newUpdatedAtDate.toString();
|
||||
|
||||
if (this.options.debugOnly) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!skipMessage) {
|
||||
try {
|
||||
await this.client.issues.createComment({
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
issue_number: issue.number,
|
||||
body: staleMessage
|
||||
});
|
||||
} catch (error) {
|
||||
issueLogger.error(`Error creating a comment: ${error.message}`);
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
await this.client.issues.addLabels({
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
issue_number: issue.number,
|
||||
labels: [staleLabel]
|
||||
});
|
||||
} catch (error) {
|
||||
issueLogger.error(`Error adding a label: ${error.message}`);
|
||||
}
|
||||
}
|
||||
|
||||
// Close an issue based on staleness
|
||||
private async _closeIssue(
|
||||
issue: Issue,
|
||||
closeMessage?: string,
|
||||
closeLabel?: string
|
||||
): Promise<void> {
|
||||
const issueLogger: IssueLogger = new IssueLogger(issue);
|
||||
|
||||
issueLogger.info(`Closing issue #${issue.number} for being stale`);
|
||||
|
||||
this.closedIssues.push(issue);
|
||||
|
||||
this._operationsLeft -= 1;
|
||||
|
||||
if (this.options.debugOnly) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (closeMessage) {
|
||||
try {
|
||||
await this.client.issues.createComment({
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
issue_number: issue.number,
|
||||
body: closeMessage
|
||||
});
|
||||
} catch (error) {
|
||||
issueLogger.error(`Error creating a comment: ${error.message}`);
|
||||
}
|
||||
}
|
||||
|
||||
if (closeLabel) {
|
||||
try {
|
||||
await this.client.issues.addLabels({
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
issue_number: issue.number,
|
||||
labels: [closeLabel]
|
||||
});
|
||||
} catch (error) {
|
||||
issueLogger.error(`Error adding a label: ${error.message}`);
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
await this.client.issues.update({
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
issue_number: issue.number,
|
||||
state: 'closed'
|
||||
});
|
||||
} catch (error) {
|
||||
issueLogger.error(`Error updating an issue: ${error.message}`);
|
||||
}
|
||||
}
|
||||
|
||||
private async _getPullRequest(
|
||||
issue: Issue
|
||||
): Promise<PullRequest | undefined> {
|
||||
const issueLogger: IssueLogger = new IssueLogger(issue);
|
||||
this._operationsLeft -= 1;
|
||||
|
||||
try {
|
||||
const pullRequest = await this.client.pulls.get({
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
pull_number: issue.number
|
||||
});
|
||||
|
||||
return pullRequest.data;
|
||||
} catch (error) {
|
||||
issueLogger.error(
|
||||
`Error getting pull request ${issue.number}: ${error.message}`
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// Delete the branch on closed pull request
|
||||
private async _deleteBranch(issue: Issue): Promise<void> {
|
||||
const issueLogger: IssueLogger = new IssueLogger(issue);
|
||||
|
||||
issueLogger.info(
|
||||
`Delete branch from closed issue #${issue.number} - ${issue.title}`
|
||||
);
|
||||
|
||||
if (this.options.debugOnly) {
|
||||
return;
|
||||
}
|
||||
|
||||
const pullRequest = await this._getPullRequest(issue);
|
||||
|
||||
if (!pullRequest) {
|
||||
issueLogger.info(
|
||||
`Not deleting branch as pull request not found for issue ${issue.number}`
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
const branch = pullRequest.head.ref;
|
||||
issueLogger.info(
|
||||
`Deleting branch ${branch} from closed issue #${issue.number}`
|
||||
);
|
||||
|
||||
this._operationsLeft -= 1;
|
||||
|
||||
try {
|
||||
await this.client.git.deleteRef({
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
ref: `heads/${branch}`
|
||||
});
|
||||
} catch (error) {
|
||||
issueLogger.error(
|
||||
`Error deleting branch ${branch} from issue #${issue.number}: ${error.message}`
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// Remove a label from an issue
|
||||
private async _removeLabel(issue: Issue, label: string): Promise<void> {
|
||||
const issueLogger: IssueLogger = new IssueLogger(issue);
|
||||
|
||||
issueLogger.info(`Removing label "${label}" from issue #${issue.number}`);
|
||||
|
||||
this.removedLabelIssues.push(issue);
|
||||
|
||||
this._operationsLeft -= 1;
|
||||
|
||||
// @todo remove the debug only to be able to test the code below
|
||||
if (this.options.debugOnly) {
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
await this.client.issues.removeLabel({
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
issue_number: issue.number,
|
||||
name: label
|
||||
});
|
||||
} catch (error) {
|
||||
issueLogger.error(`Error removing a label: ${error.message}`);
|
||||
}
|
||||
}
|
||||
|
||||
// returns the creation date of a given label on an issue (or nothing if no label existed)
|
||||
///see https://developer.github.com/v3/activity/events/
|
||||
private async _getLabelCreationDate(
|
||||
issue: Issue,
|
||||
label: string
|
||||
): Promise<string | undefined> {
|
||||
const issueLogger: IssueLogger = new IssueLogger(issue);
|
||||
|
||||
issueLogger.info(`Checking for label on issue #${issue.number}`);
|
||||
|
||||
this._operationsLeft -= 1;
|
||||
|
||||
const options = this.client.issues.listEvents.endpoint.merge({
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
per_page: 100,
|
||||
issue_number: issue.number
|
||||
});
|
||||
|
||||
const events: IssueEvent[] = await this.client.paginate(options);
|
||||
const reversedEvents = events.reverse();
|
||||
|
||||
const staleLabeledEvent = reversedEvents.find(
|
||||
event => event.event === 'labeled' && event.label.name === label
|
||||
);
|
||||
|
||||
if (!staleLabeledEvent) {
|
||||
// Must be old rather than labeled
|
||||
return undefined;
|
||||
}
|
||||
|
||||
return staleLabeledEvent.created_at;
|
||||
}
|
||||
|
||||
private _getDaysBeforeIssueStale(): number {
|
||||
return isNaN(this.options.daysBeforeIssueStale)
|
||||
? this.options.daysBeforeStale
|
||||
: this.options.daysBeforeIssueStale;
|
||||
}
|
||||
|
||||
private _getDaysBeforePrStale(): number {
|
||||
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);
|
||||
}
|
||||
}
|
||||
849
src/classes/assignees.spec.ts
Normal file
849
src/classes/assignees.spec.ts
Normal file
@@ -0,0 +1,849 @@
|
||||
import {DefaultProcessorOptions} from '../../__tests__/constants/default-processor-options';
|
||||
import {generateIIssue} from '../../__tests__/functions/generate-iissue';
|
||||
import {IIssue} from '../interfaces/issue';
|
||||
import {IIssuesProcessorOptions} from '../interfaces/issues-processor-options';
|
||||
import {Assignees} from './assignees';
|
||||
import {Issue} from './issue';
|
||||
|
||||
describe('Assignees', (): void => {
|
||||
let assignees: Assignees;
|
||||
let optionsInterface: IIssuesProcessorOptions;
|
||||
let issue: Issue;
|
||||
let issueInterface: IIssue;
|
||||
|
||||
beforeEach((): void => {
|
||||
optionsInterface = {
|
||||
...DefaultProcessorOptions,
|
||||
exemptAllAssignees: false
|
||||
};
|
||||
issueInterface = generateIIssue();
|
||||
});
|
||||
|
||||
describe('shouldExemptAssignees()', (): 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 an assignee', (): void => {
|
||||
beforeEach((): void => {
|
||||
optionsInterface.exemptAssignees = '';
|
||||
});
|
||||
|
||||
describe('when the given options are not configured to exempt an issue with an assignee', (): void => {
|
||||
beforeEach((): void => {
|
||||
optionsInterface.exemptIssueAssignees = '';
|
||||
});
|
||||
|
||||
describe('when the given issue does not have an assignee', (): void => {
|
||||
beforeEach((): void => {
|
||||
issueInterface.assignees = [];
|
||||
});
|
||||
|
||||
it('should return false', (): void => {
|
||||
expect.assertions(1);
|
||||
issue = new Issue(optionsInterface, issueInterface);
|
||||
assignees = new Assignees(optionsInterface, issue);
|
||||
|
||||
const result = assignees.shouldExemptAssignees();
|
||||
|
||||
expect(result).toStrictEqual(false);
|
||||
});
|
||||
});
|
||||
|
||||
describe('when the given issue does have an assignee', (): void => {
|
||||
beforeEach((): void => {
|
||||
issueInterface.assignees = [
|
||||
{
|
||||
login: 'dummy-login'
|
||||
}
|
||||
];
|
||||
});
|
||||
|
||||
it('should return false', (): void => {
|
||||
expect.assertions(1);
|
||||
issue = new Issue(optionsInterface, issueInterface);
|
||||
assignees = new Assignees(optionsInterface, issue);
|
||||
|
||||
const result = assignees.shouldExemptAssignees();
|
||||
|
||||
expect(result).toStrictEqual(false);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('when the given options are configured to exempt an issue with an assignee', (): void => {
|
||||
beforeEach((): void => {
|
||||
optionsInterface.exemptIssueAssignees =
|
||||
'dummy-exempt-issue-assignee';
|
||||
});
|
||||
|
||||
describe('when the given issue does not have an assignee', (): void => {
|
||||
beforeEach((): void => {
|
||||
issueInterface.assignees = [];
|
||||
});
|
||||
|
||||
it('should return false', (): void => {
|
||||
expect.assertions(1);
|
||||
issue = new Issue(optionsInterface, issueInterface);
|
||||
assignees = new Assignees(optionsInterface, issue);
|
||||
|
||||
const result = assignees.shouldExemptAssignees();
|
||||
|
||||
expect(result).toStrictEqual(false);
|
||||
});
|
||||
});
|
||||
|
||||
describe('when the given issue does have an assignee different than the exempt issue assignee', (): void => {
|
||||
beforeEach((): void => {
|
||||
issueInterface.assignees = [
|
||||
{
|
||||
login: 'dummy-login'
|
||||
}
|
||||
];
|
||||
});
|
||||
|
||||
it('should return false', (): void => {
|
||||
expect.assertions(1);
|
||||
issue = new Issue(optionsInterface, issueInterface);
|
||||
assignees = new Assignees(optionsInterface, issue);
|
||||
|
||||
const result = assignees.shouldExemptAssignees();
|
||||
|
||||
expect(result).toStrictEqual(false);
|
||||
});
|
||||
});
|
||||
|
||||
describe('when the given issue does have an assignee equaling the exempt issue assignee', (): void => {
|
||||
beforeEach((): void => {
|
||||
issueInterface.assignees = [
|
||||
{
|
||||
login: 'dummy-exempt-issue-assignee'
|
||||
}
|
||||
];
|
||||
});
|
||||
|
||||
it('should return true', (): void => {
|
||||
expect.assertions(1);
|
||||
issue = new Issue(optionsInterface, issueInterface);
|
||||
assignees = new Assignees(optionsInterface, issue);
|
||||
|
||||
const result = assignees.shouldExemptAssignees();
|
||||
|
||||
expect(result).toStrictEqual(true);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('when the given options are configured to exempt an assignee', (): void => {
|
||||
beforeEach((): void => {
|
||||
optionsInterface.exemptAssignees = 'dummy-exempt-assignee';
|
||||
});
|
||||
|
||||
describe('when the given options are not configured to exempt an issue with an assignee', (): void => {
|
||||
beforeEach((): void => {
|
||||
optionsInterface.exemptIssueAssignees = '';
|
||||
});
|
||||
|
||||
describe('when the given issue does not have an assignee', (): void => {
|
||||
beforeEach((): void => {
|
||||
issueInterface.assignees = [];
|
||||
});
|
||||
|
||||
it('should return false', (): void => {
|
||||
expect.assertions(1);
|
||||
issue = new Issue(optionsInterface, issueInterface);
|
||||
assignees = new Assignees(optionsInterface, issue);
|
||||
|
||||
const result = assignees.shouldExemptAssignees();
|
||||
|
||||
expect(result).toStrictEqual(false);
|
||||
});
|
||||
});
|
||||
|
||||
describe('when the given issue does have an assignee different than the exempt assignee', (): void => {
|
||||
beforeEach((): void => {
|
||||
issueInterface.assignees = [
|
||||
{
|
||||
login: 'dummy-login'
|
||||
}
|
||||
];
|
||||
});
|
||||
|
||||
it('should return false', (): void => {
|
||||
expect.assertions(1);
|
||||
issue = new Issue(optionsInterface, issueInterface);
|
||||
assignees = new Assignees(optionsInterface, issue);
|
||||
|
||||
const result = assignees.shouldExemptAssignees();
|
||||
|
||||
expect(result).toStrictEqual(false);
|
||||
});
|
||||
});
|
||||
|
||||
describe('when the given issue does have an assignee equaling the exempt assignee', (): void => {
|
||||
beforeEach((): void => {
|
||||
issueInterface.assignees = [
|
||||
{
|
||||
login: 'dummy-exempt-assignee'
|
||||
}
|
||||
];
|
||||
});
|
||||
|
||||
it('should return true', (): void => {
|
||||
expect.assertions(1);
|
||||
issue = new Issue(optionsInterface, issueInterface);
|
||||
assignees = new Assignees(optionsInterface, issue);
|
||||
|
||||
const result = assignees.shouldExemptAssignees();
|
||||
|
||||
expect(result).toStrictEqual(true);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('when the given options are configured to exempt an issue with an assignee', (): void => {
|
||||
beforeEach((): void => {
|
||||
optionsInterface.exemptIssueAssignees =
|
||||
'dummy-exempt-issue-assignee';
|
||||
});
|
||||
|
||||
describe('when the given issue does not have an assignee', (): void => {
|
||||
beforeEach((): void => {
|
||||
issueInterface.assignees = [];
|
||||
});
|
||||
|
||||
it('should return false', (): void => {
|
||||
expect.assertions(1);
|
||||
issue = new Issue(optionsInterface, issueInterface);
|
||||
assignees = new Assignees(optionsInterface, issue);
|
||||
|
||||
const result = assignees.shouldExemptAssignees();
|
||||
|
||||
expect(result).toStrictEqual(false);
|
||||
});
|
||||
});
|
||||
|
||||
describe('when the given issue does have an assignee different than the exempt issue assignee', (): void => {
|
||||
beforeEach((): void => {
|
||||
issueInterface.assignees = [
|
||||
{
|
||||
login: 'dummy-login'
|
||||
}
|
||||
];
|
||||
});
|
||||
|
||||
it('should return false', (): void => {
|
||||
expect.assertions(1);
|
||||
issue = new Issue(optionsInterface, issueInterface);
|
||||
assignees = new Assignees(optionsInterface, issue);
|
||||
|
||||
const result = assignees.shouldExemptAssignees();
|
||||
|
||||
expect(result).toStrictEqual(false);
|
||||
});
|
||||
});
|
||||
|
||||
describe('when the given issue does have an assignee equaling the exempt issue assignee', (): void => {
|
||||
beforeEach((): void => {
|
||||
issueInterface.assignees = [
|
||||
{
|
||||
login: 'dummy-exempt-issue-assignee'
|
||||
}
|
||||
];
|
||||
});
|
||||
|
||||
it('should return true', (): void => {
|
||||
expect.assertions(1);
|
||||
issue = new Issue(optionsInterface, issueInterface);
|
||||
assignees = new Assignees(optionsInterface, issue);
|
||||
|
||||
const result = assignees.shouldExemptAssignees();
|
||||
|
||||
expect(result).toStrictEqual(true);
|
||||
});
|
||||
});
|
||||
|
||||
describe('when the given issue does have an assignee different than the exempt assignee', (): void => {
|
||||
beforeEach((): void => {
|
||||
issueInterface.assignees = [
|
||||
{
|
||||
login: 'dummy-login'
|
||||
}
|
||||
];
|
||||
});
|
||||
|
||||
it('should return false', (): void => {
|
||||
expect.assertions(1);
|
||||
issue = new Issue(optionsInterface, issueInterface);
|
||||
assignees = new Assignees(optionsInterface, issue);
|
||||
|
||||
const result = assignees.shouldExemptAssignees();
|
||||
|
||||
expect(result).toStrictEqual(false);
|
||||
});
|
||||
});
|
||||
|
||||
describe('when the given issue does have an assignee equaling the exempt assignee', (): void => {
|
||||
beforeEach((): void => {
|
||||
issueInterface.assignees = [
|
||||
{
|
||||
login: 'dummy-exempt-assignee'
|
||||
}
|
||||
];
|
||||
});
|
||||
|
||||
it('should return false', (): void => {
|
||||
expect.assertions(1);
|
||||
issue = new Issue(optionsInterface, issueInterface);
|
||||
assignees = new Assignees(optionsInterface, issue);
|
||||
|
||||
const result = assignees.shouldExemptAssignees();
|
||||
|
||||
expect(result).toStrictEqual(false);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('when the given options are configured to exempt all assignees', (): void => {
|
||||
beforeEach((): void => {
|
||||
optionsInterface.exemptAllAssignees = true;
|
||||
});
|
||||
|
||||
describe('when the given issue does not have an assignee', (): void => {
|
||||
beforeEach((): void => {
|
||||
issueInterface.assignees = [];
|
||||
});
|
||||
|
||||
it('should return false', (): void => {
|
||||
expect.assertions(1);
|
||||
issue = new Issue(optionsInterface, issueInterface);
|
||||
assignees = new Assignees(optionsInterface, issue);
|
||||
|
||||
const result = assignees.shouldExemptAssignees();
|
||||
|
||||
expect(result).toStrictEqual(false);
|
||||
});
|
||||
});
|
||||
|
||||
describe('when the given issue does have an assignee', (): void => {
|
||||
beforeEach((): void => {
|
||||
issueInterface.assignees = [
|
||||
{
|
||||
login: 'dummy-exempt-assignee'
|
||||
}
|
||||
];
|
||||
});
|
||||
|
||||
it('should return true', (): void => {
|
||||
expect.assertions(1);
|
||||
issue = new Issue(optionsInterface, issueInterface);
|
||||
assignees = new Assignees(optionsInterface, issue);
|
||||
|
||||
const result = assignees.shouldExemptAssignees();
|
||||
|
||||
expect(result).toStrictEqual(true);
|
||||
});
|
||||
});
|
||||
|
||||
describe('when the given options are not configured to exempt all issue assignees', (): void => {
|
||||
beforeEach((): void => {
|
||||
optionsInterface.exemptAllIssueAssignees = false;
|
||||
});
|
||||
|
||||
describe('when the given issue does not have an assignee', (): void => {
|
||||
beforeEach((): void => {
|
||||
issueInterface.assignees = [];
|
||||
});
|
||||
|
||||
it('should return false', (): void => {
|
||||
expect.assertions(1);
|
||||
issue = new Issue(optionsInterface, issueInterface);
|
||||
assignees = new Assignees(optionsInterface, issue);
|
||||
|
||||
const result = assignees.shouldExemptAssignees();
|
||||
|
||||
expect(result).toStrictEqual(false);
|
||||
});
|
||||
});
|
||||
|
||||
describe('when the given issue does have an assignee', (): void => {
|
||||
beforeEach((): void => {
|
||||
issueInterface.assignees = [
|
||||
{
|
||||
login: 'dummy-exempt-assignee'
|
||||
}
|
||||
];
|
||||
});
|
||||
|
||||
it('should return false', (): void => {
|
||||
expect.assertions(1);
|
||||
issue = new Issue(optionsInterface, issueInterface);
|
||||
assignees = new Assignees(optionsInterface, issue);
|
||||
|
||||
const result = assignees.shouldExemptAssignees();
|
||||
|
||||
expect(result).toStrictEqual(false);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('when the given options are configured to exempt all issue assignees', (): void => {
|
||||
beforeEach((): void => {
|
||||
optionsInterface.exemptAllIssueAssignees = true;
|
||||
});
|
||||
|
||||
describe('when the given issue does not have an assignee', (): void => {
|
||||
beforeEach((): void => {
|
||||
issueInterface.assignees = [];
|
||||
});
|
||||
|
||||
it('should return false', (): void => {
|
||||
expect.assertions(1);
|
||||
issue = new Issue(optionsInterface, issueInterface);
|
||||
assignees = new Assignees(optionsInterface, issue);
|
||||
|
||||
const result = assignees.shouldExemptAssignees();
|
||||
|
||||
expect(result).toStrictEqual(false);
|
||||
});
|
||||
});
|
||||
|
||||
describe('when the given issue does have an assignee', (): void => {
|
||||
beforeEach((): void => {
|
||||
issueInterface.assignees = [
|
||||
{
|
||||
login: 'dummy-exempt-issue-assignee'
|
||||
}
|
||||
];
|
||||
});
|
||||
|
||||
it('should return true', (): void => {
|
||||
expect.assertions(1);
|
||||
issue = new Issue(optionsInterface, issueInterface);
|
||||
assignees = new Assignees(optionsInterface, issue);
|
||||
|
||||
const result = assignees.shouldExemptAssignees();
|
||||
|
||||
expect(result).toStrictEqual(true);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
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 an assignee', (): void => {
|
||||
beforeEach((): void => {
|
||||
optionsInterface.exemptAssignees = '';
|
||||
});
|
||||
|
||||
describe('when the given options are not configured to exempt a pull request with an assignee', (): void => {
|
||||
beforeEach((): void => {
|
||||
optionsInterface.exemptPrAssignees = '';
|
||||
});
|
||||
|
||||
describe('when the given pull request does not have an assignee', (): void => {
|
||||
beforeEach((): void => {
|
||||
issueInterface.assignees = [];
|
||||
});
|
||||
|
||||
it('should return false', (): void => {
|
||||
expect.assertions(1);
|
||||
issue = new Issue(optionsInterface, issueInterface);
|
||||
assignees = new Assignees(optionsInterface, issue);
|
||||
|
||||
const result = assignees.shouldExemptAssignees();
|
||||
|
||||
expect(result).toStrictEqual(false);
|
||||
});
|
||||
});
|
||||
|
||||
describe('when the given pull request does have an assignee', (): void => {
|
||||
beforeEach((): void => {
|
||||
issueInterface.assignees = [
|
||||
{
|
||||
login: 'dummy-login'
|
||||
}
|
||||
];
|
||||
});
|
||||
|
||||
it('should return false', (): void => {
|
||||
expect.assertions(1);
|
||||
issue = new Issue(optionsInterface, issueInterface);
|
||||
assignees = new Assignees(optionsInterface, issue);
|
||||
|
||||
const result = assignees.shouldExemptAssignees();
|
||||
|
||||
expect(result).toStrictEqual(false);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('when the given options are configured to exempt a pull request with an assignee', (): void => {
|
||||
beforeEach((): void => {
|
||||
optionsInterface.exemptPrAssignees = 'dummy-exempt-pr-assignee';
|
||||
});
|
||||
|
||||
describe('when the given pull request does not have an assignee', (): void => {
|
||||
beforeEach((): void => {
|
||||
issueInterface.assignees = [];
|
||||
});
|
||||
|
||||
it('should return false', (): void => {
|
||||
expect.assertions(1);
|
||||
issue = new Issue(optionsInterface, issueInterface);
|
||||
assignees = new Assignees(optionsInterface, issue);
|
||||
|
||||
const result = assignees.shouldExemptAssignees();
|
||||
|
||||
expect(result).toStrictEqual(false);
|
||||
});
|
||||
});
|
||||
|
||||
describe('when the given pull request does have an assignee different than the exempt pull request assignee', (): void => {
|
||||
beforeEach((): void => {
|
||||
issueInterface.assignees = [
|
||||
{
|
||||
login: 'dummy-login'
|
||||
}
|
||||
];
|
||||
});
|
||||
|
||||
it('should return false', (): void => {
|
||||
expect.assertions(1);
|
||||
issue = new Issue(optionsInterface, issueInterface);
|
||||
assignees = new Assignees(optionsInterface, issue);
|
||||
|
||||
const result = assignees.shouldExemptAssignees();
|
||||
|
||||
expect(result).toStrictEqual(false);
|
||||
});
|
||||
});
|
||||
|
||||
describe('when the given pull request does have an assignee equaling the exempt pull request assignee', (): void => {
|
||||
beforeEach((): void => {
|
||||
issueInterface.assignees = [
|
||||
{
|
||||
login: 'dummy-exempt-pr-assignee'
|
||||
}
|
||||
];
|
||||
});
|
||||
|
||||
it('should return true', (): void => {
|
||||
expect.assertions(1);
|
||||
issue = new Issue(optionsInterface, issueInterface);
|
||||
assignees = new Assignees(optionsInterface, issue);
|
||||
|
||||
const result = assignees.shouldExemptAssignees();
|
||||
|
||||
expect(result).toStrictEqual(true);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('when the given options are configured to exempt an assignee', (): void => {
|
||||
beforeEach((): void => {
|
||||
optionsInterface.exemptAssignees = 'dummy-exempt-assignee';
|
||||
});
|
||||
|
||||
describe('when the given options are not configured to exempt a pull request with an assignee', (): void => {
|
||||
beforeEach((): void => {
|
||||
optionsInterface.exemptPrAssignees = '';
|
||||
});
|
||||
|
||||
describe('when the given pull request does not have an assignee', (): void => {
|
||||
beforeEach((): void => {
|
||||
issueInterface.assignees = [];
|
||||
});
|
||||
|
||||
it('should return false', (): void => {
|
||||
expect.assertions(1);
|
||||
issue = new Issue(optionsInterface, issueInterface);
|
||||
assignees = new Assignees(optionsInterface, issue);
|
||||
|
||||
const result = assignees.shouldExemptAssignees();
|
||||
|
||||
expect(result).toStrictEqual(false);
|
||||
});
|
||||
});
|
||||
|
||||
describe('when the given pull request does have an assignee different than the exempt assignee', (): void => {
|
||||
beforeEach((): void => {
|
||||
issueInterface.assignees = [
|
||||
{
|
||||
login: 'dummy-login'
|
||||
}
|
||||
];
|
||||
});
|
||||
|
||||
it('should return false', (): void => {
|
||||
expect.assertions(1);
|
||||
issue = new Issue(optionsInterface, issueInterface);
|
||||
assignees = new Assignees(optionsInterface, issue);
|
||||
|
||||
const result = assignees.shouldExemptAssignees();
|
||||
|
||||
expect(result).toStrictEqual(false);
|
||||
});
|
||||
});
|
||||
|
||||
describe('when the given pull request does have an assignee equaling the exempt assignee', (): void => {
|
||||
beforeEach((): void => {
|
||||
issueInterface.assignees = [
|
||||
{
|
||||
login: 'dummy-exempt-assignee'
|
||||
}
|
||||
];
|
||||
});
|
||||
|
||||
it('should return true', (): void => {
|
||||
expect.assertions(1);
|
||||
issue = new Issue(optionsInterface, issueInterface);
|
||||
assignees = new Assignees(optionsInterface, issue);
|
||||
|
||||
const result = assignees.shouldExemptAssignees();
|
||||
|
||||
expect(result).toStrictEqual(true);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('when the given options are configured to exempt a pull request with an assignee', (): void => {
|
||||
beforeEach((): void => {
|
||||
optionsInterface.exemptPrAssignees = 'dummy-exempt-pr-assignee';
|
||||
});
|
||||
|
||||
describe('when the given pull request does not have an assignee', (): void => {
|
||||
beforeEach((): void => {
|
||||
issueInterface.assignees = [];
|
||||
});
|
||||
|
||||
it('should return false', (): void => {
|
||||
expect.assertions(1);
|
||||
issue = new Issue(optionsInterface, issueInterface);
|
||||
assignees = new Assignees(optionsInterface, issue);
|
||||
|
||||
const result = assignees.shouldExemptAssignees();
|
||||
|
||||
expect(result).toStrictEqual(false);
|
||||
});
|
||||
});
|
||||
|
||||
describe('when the given pull request does have an assignee different than the exempt pull request assignee', (): void => {
|
||||
beforeEach((): void => {
|
||||
issueInterface.assignees = [
|
||||
{
|
||||
login: 'dummy-login'
|
||||
}
|
||||
];
|
||||
});
|
||||
|
||||
it('should return false', (): void => {
|
||||
expect.assertions(1);
|
||||
issue = new Issue(optionsInterface, issueInterface);
|
||||
assignees = new Assignees(optionsInterface, issue);
|
||||
|
||||
const result = assignees.shouldExemptAssignees();
|
||||
|
||||
expect(result).toStrictEqual(false);
|
||||
});
|
||||
});
|
||||
|
||||
describe('when the given pull request does have an assignee equaling the exempt pull request assignee', (): void => {
|
||||
beforeEach((): void => {
|
||||
issueInterface.assignees = [
|
||||
{
|
||||
login: 'dummy-exempt-pr-assignee'
|
||||
}
|
||||
];
|
||||
});
|
||||
|
||||
it('should return true', (): void => {
|
||||
expect.assertions(1);
|
||||
issue = new Issue(optionsInterface, issueInterface);
|
||||
assignees = new Assignees(optionsInterface, issue);
|
||||
|
||||
const result = assignees.shouldExemptAssignees();
|
||||
|
||||
expect(result).toStrictEqual(true);
|
||||
});
|
||||
});
|
||||
|
||||
describe('when the given pull request does have an assignee different than the exempt assignee', (): void => {
|
||||
beforeEach((): void => {
|
||||
issueInterface.assignees = [
|
||||
{
|
||||
login: 'dummy-login'
|
||||
}
|
||||
];
|
||||
});
|
||||
|
||||
it('should return false', (): void => {
|
||||
expect.assertions(1);
|
||||
issue = new Issue(optionsInterface, issueInterface);
|
||||
assignees = new Assignees(optionsInterface, issue);
|
||||
|
||||
const result = assignees.shouldExemptAssignees();
|
||||
|
||||
expect(result).toStrictEqual(false);
|
||||
});
|
||||
});
|
||||
|
||||
describe('when the given pull request does have an assignee equaling the exempt assignee', (): void => {
|
||||
beforeEach((): void => {
|
||||
issueInterface.assignees = [
|
||||
{
|
||||
login: 'dummy-exempt-assignee'
|
||||
}
|
||||
];
|
||||
});
|
||||
|
||||
it('should return false', (): void => {
|
||||
expect.assertions(1);
|
||||
issue = new Issue(optionsInterface, issueInterface);
|
||||
assignees = new Assignees(optionsInterface, issue);
|
||||
|
||||
const result = assignees.shouldExemptAssignees();
|
||||
|
||||
expect(result).toStrictEqual(false);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('when the given options are configured to exempt all assignees', (): void => {
|
||||
beforeEach((): void => {
|
||||
optionsInterface.exemptAllAssignees = true;
|
||||
});
|
||||
|
||||
describe('when the given pull request does not have an assignee', (): void => {
|
||||
beforeEach((): void => {
|
||||
issueInterface.assignees = [];
|
||||
});
|
||||
|
||||
it('should return false', (): void => {
|
||||
expect.assertions(1);
|
||||
issue = new Issue(optionsInterface, issueInterface);
|
||||
assignees = new Assignees(optionsInterface, issue);
|
||||
|
||||
const result = assignees.shouldExemptAssignees();
|
||||
|
||||
expect(result).toStrictEqual(false);
|
||||
});
|
||||
});
|
||||
|
||||
describe('when the given pull request does have an assignee', (): void => {
|
||||
beforeEach((): void => {
|
||||
issueInterface.assignees = [
|
||||
{
|
||||
login: 'dummy-exempt-assignee'
|
||||
}
|
||||
];
|
||||
});
|
||||
|
||||
it('should return true', (): void => {
|
||||
expect.assertions(1);
|
||||
issue = new Issue(optionsInterface, issueInterface);
|
||||
assignees = new Assignees(optionsInterface, issue);
|
||||
|
||||
const result = assignees.shouldExemptAssignees();
|
||||
|
||||
expect(result).toStrictEqual(true);
|
||||
});
|
||||
});
|
||||
|
||||
describe('when the given options are not configured to exempt all pull request assignees', (): void => {
|
||||
beforeEach((): void => {
|
||||
optionsInterface.exemptAllPrAssignees = false;
|
||||
});
|
||||
|
||||
describe('when the given pull request does not have an assignee', (): void => {
|
||||
beforeEach((): void => {
|
||||
issueInterface.assignees = [];
|
||||
});
|
||||
|
||||
it('should return false', (): void => {
|
||||
expect.assertions(1);
|
||||
issue = new Issue(optionsInterface, issueInterface);
|
||||
assignees = new Assignees(optionsInterface, issue);
|
||||
|
||||
const result = assignees.shouldExemptAssignees();
|
||||
|
||||
expect(result).toStrictEqual(false);
|
||||
});
|
||||
});
|
||||
|
||||
describe('when the given pull request does have an assignee', (): void => {
|
||||
beforeEach((): void => {
|
||||
issueInterface.assignees = [
|
||||
{
|
||||
login: 'dummy-exempt-assignee'
|
||||
}
|
||||
];
|
||||
});
|
||||
|
||||
it('should return false', (): void => {
|
||||
expect.assertions(1);
|
||||
issue = new Issue(optionsInterface, issueInterface);
|
||||
assignees = new Assignees(optionsInterface, issue);
|
||||
|
||||
const result = assignees.shouldExemptAssignees();
|
||||
|
||||
expect(result).toStrictEqual(false);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('when the given options are configured to exempt all pull request assignees', (): void => {
|
||||
beforeEach((): void => {
|
||||
optionsInterface.exemptAllPrAssignees = true;
|
||||
});
|
||||
|
||||
describe('when the given pull request does not have an assignee', (): void => {
|
||||
beforeEach((): void => {
|
||||
issueInterface.assignees = [];
|
||||
});
|
||||
|
||||
it('should return false', (): void => {
|
||||
expect.assertions(1);
|
||||
issue = new Issue(optionsInterface, issueInterface);
|
||||
assignees = new Assignees(optionsInterface, issue);
|
||||
|
||||
const result = assignees.shouldExemptAssignees();
|
||||
|
||||
expect(result).toStrictEqual(false);
|
||||
});
|
||||
});
|
||||
|
||||
describe('when the given pull request does have an assignee', (): void => {
|
||||
beforeEach((): void => {
|
||||
issueInterface.assignees = [
|
||||
{
|
||||
login: 'dummy-exempt-issue-assignee'
|
||||
}
|
||||
];
|
||||
});
|
||||
|
||||
it('should return true', (): void => {
|
||||
expect.assertions(1);
|
||||
issue = new Issue(optionsInterface, issueInterface);
|
||||
assignees = new Assignees(optionsInterface, issue);
|
||||
|
||||
const result = assignees.shouldExemptAssignees();
|
||||
|
||||
expect(result).toStrictEqual(true);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
287
src/classes/assignees.ts
Normal file
287
src/classes/assignees.ts
Normal file
@@ -0,0 +1,287 @@
|
||||
import chalk from 'chalk';
|
||||
import deburr from 'lodash.deburr';
|
||||
import {Option} from '../enums/option';
|
||||
import {wordsToList} from '../functions/words-to-list';
|
||||
import {IAssignee} from '../interfaces/assignee';
|
||||
import {IIssuesProcessorOptions} from '../interfaces/issues-processor-options';
|
||||
import {Issue} from './issue';
|
||||
import {IssueLogger} from './loggers/issue-logger';
|
||||
|
||||
type CleanAssignee = string;
|
||||
|
||||
export class Assignees {
|
||||
private static _cleanAssignee(assignee: Readonly<string>): CleanAssignee {
|
||||
return deburr(assignee.toLowerCase());
|
||||
}
|
||||
|
||||
private readonly _options: IIssuesProcessorOptions;
|
||||
private readonly _issue: Issue;
|
||||
private readonly _issueLogger: IssueLogger;
|
||||
|
||||
constructor(options: Readonly<IIssuesProcessorOptions>, issue: Issue) {
|
||||
this._options = options;
|
||||
this._issue = issue;
|
||||
this._issueLogger = new IssueLogger(issue);
|
||||
}
|
||||
|
||||
shouldExemptAssignees(): boolean {
|
||||
if (!this._issue.hasAssignees) {
|
||||
this._issueLogger.info('This $$type has no assignee');
|
||||
this._logSkip();
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
if (this._shouldExemptAllAssignees()) {
|
||||
this._issueLogger.info(
|
||||
chalk.white('└──'),
|
||||
'Skipping this $$type because it has an exempt assignee'
|
||||
);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
const exemptAssignees: string[] = this._getExemptAssignees();
|
||||
|
||||
if (exemptAssignees.length === 0) {
|
||||
this._issueLogger.info(
|
||||
chalk.white('├──'),
|
||||
`No assignee option was specified to skip the stale process for this $$type`
|
||||
);
|
||||
this._logSkip();
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
this._issueLogger.info(
|
||||
chalk.white('├──'),
|
||||
`Found ${chalk.cyan(exemptAssignees.length)} assignee${
|
||||
exemptAssignees.length > 1 ? 's' : ''
|
||||
} that can exempt stale on this $$type`
|
||||
);
|
||||
|
||||
const hasExemptAssignee: boolean = exemptAssignees.some(
|
||||
(exemptAssignee: Readonly<string>): boolean =>
|
||||
this._hasAssignee(exemptAssignee)
|
||||
);
|
||||
|
||||
if (!hasExemptAssignee) {
|
||||
this._issueLogger.info(
|
||||
chalk.white('├──'),
|
||||
'No assignee on this $$type can exempt the stale process'
|
||||
);
|
||||
this._logSkip();
|
||||
} else {
|
||||
this._issueLogger.info(
|
||||
chalk.white('└──'),
|
||||
'Skipping this $$type because it has an exempt assignee'
|
||||
);
|
||||
}
|
||||
|
||||
return hasExemptAssignee;
|
||||
}
|
||||
|
||||
private _getExemptAssignees(): string[] {
|
||||
return this._issue.isPullRequest
|
||||
? this._getExemptPullRequestAssignees()
|
||||
: this._getExemptIssueAssignees();
|
||||
}
|
||||
|
||||
private _getExemptIssueAssignees(): string[] {
|
||||
if (this._options.exemptIssueAssignees === '') {
|
||||
this._issueLogger.info(
|
||||
chalk.white('├──'),
|
||||
`The option ${this._issueLogger.createOptionLink(
|
||||
Option.ExemptIssueAssignees
|
||||
)} is disabled. No specific assignee can skip the stale process for this $$type`
|
||||
);
|
||||
|
||||
if (this._options.exemptAssignees === '') {
|
||||
this._issueLogger.info(
|
||||
chalk.white('├──'),
|
||||
`The option ${this._issueLogger.createOptionLink(
|
||||
Option.ExemptAssignees
|
||||
)} is disabled. No specific assignee can skip the stale process for this $$type`
|
||||
);
|
||||
|
||||
return [];
|
||||
}
|
||||
|
||||
const exemptAssignees: string[] = wordsToList(
|
||||
this._options.exemptAssignees
|
||||
);
|
||||
|
||||
this._issueLogger.info(
|
||||
chalk.white('├──'),
|
||||
`The option ${this._issueLogger.createOptionLink(
|
||||
Option.ExemptAssignees
|
||||
)} is set. ${chalk.cyan(exemptAssignees.length)} assignee${
|
||||
exemptAssignees.length === 1 ? '' : 's'
|
||||
} can skip the stale process for this $$type`
|
||||
);
|
||||
|
||||
return exemptAssignees;
|
||||
}
|
||||
|
||||
const exemptAssignees: string[] = wordsToList(
|
||||
this._options.exemptIssueAssignees
|
||||
);
|
||||
|
||||
this._issueLogger.info(
|
||||
chalk.white('├──'),
|
||||
`The option ${this._issueLogger.createOptionLink(
|
||||
Option.ExemptIssueAssignees
|
||||
)} is set. ${chalk.cyan(exemptAssignees.length)} assignee${
|
||||
exemptAssignees.length === 1 ? '' : 's'
|
||||
} can skip the stale process for this $$type`
|
||||
);
|
||||
|
||||
return exemptAssignees;
|
||||
}
|
||||
|
||||
private _getExemptPullRequestAssignees(): string[] {
|
||||
if (this._options.exemptPrAssignees === '') {
|
||||
this._issueLogger.info(
|
||||
chalk.white('├──'),
|
||||
`The option ${this._issueLogger.createOptionLink(
|
||||
Option.ExemptPrAssignees
|
||||
)} is disabled. No specific assignee can skip the stale process for this $$type`
|
||||
);
|
||||
|
||||
if (this._options.exemptAssignees === '') {
|
||||
this._issueLogger.info(
|
||||
chalk.white('├──'),
|
||||
`The option ${this._issueLogger.createOptionLink(
|
||||
Option.ExemptAssignees
|
||||
)} is disabled. No specific assignee can skip the stale process for this $$type`
|
||||
);
|
||||
|
||||
return [];
|
||||
}
|
||||
|
||||
const exemptAssignees: string[] = wordsToList(
|
||||
this._options.exemptAssignees
|
||||
);
|
||||
|
||||
this._issueLogger.info(
|
||||
chalk.white('├──'),
|
||||
`The option ${this._issueLogger.createOptionLink(
|
||||
Option.ExemptAssignees
|
||||
)} is set. ${chalk.cyan(exemptAssignees.length)} assignee${
|
||||
exemptAssignees.length === 1 ? '' : 's'
|
||||
} can skip the stale process for this $$type`
|
||||
);
|
||||
|
||||
return exemptAssignees;
|
||||
}
|
||||
|
||||
const exemptAssignees: string[] = wordsToList(
|
||||
this._options.exemptPrAssignees
|
||||
);
|
||||
|
||||
this._issueLogger.info(
|
||||
chalk.white('├──'),
|
||||
`The option ${this._issueLogger.createOptionLink(
|
||||
Option.ExemptPrAssignees
|
||||
)} is set. ${chalk.cyan(exemptAssignees.length)} assignee${
|
||||
exemptAssignees.length === 1 ? '' : 's'
|
||||
} can skip the stale process for this $$type`
|
||||
);
|
||||
|
||||
return exemptAssignees;
|
||||
}
|
||||
|
||||
private _hasAssignee(assignee: Readonly<string>): boolean {
|
||||
const cleanAssignee: CleanAssignee = Assignees._cleanAssignee(assignee);
|
||||
|
||||
return this._issue.assignees.some(
|
||||
(issueAssignee: Readonly<IAssignee>): boolean => {
|
||||
const isSameAssignee: boolean =
|
||||
cleanAssignee === Assignees._cleanAssignee(issueAssignee.login);
|
||||
|
||||
if (isSameAssignee) {
|
||||
this._issueLogger.info(
|
||||
chalk.white('├──'),
|
||||
`@${issueAssignee.login} is assigned on this $$type and is an exempt assignee`
|
||||
);
|
||||
}
|
||||
|
||||
return isSameAssignee;
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
private _shouldExemptAllAssignees(): boolean {
|
||||
return this._issue.isPullRequest
|
||||
? this._shouldExemptAllPullRequestAssignees()
|
||||
: this._shouldExemptAllIssueAssignees();
|
||||
}
|
||||
|
||||
private _shouldExemptAllIssueAssignees(): boolean {
|
||||
if (this._options.exemptAllIssueAssignees === true) {
|
||||
this._issueLogger.info(
|
||||
`The option ${this._issueLogger.createOptionLink(
|
||||
Option.ExemptAllIssueAssignees
|
||||
)} is enabled. Any assignee on this $$type will skip the stale process`
|
||||
);
|
||||
|
||||
return true;
|
||||
} else if (this._options.exemptAllIssueAssignees === false) {
|
||||
this._issueLogger.info(
|
||||
`The option ${this._issueLogger.createOptionLink(
|
||||
Option.ExemptAllIssueAssignees
|
||||
)} is disabled. Only some specific assignees on this $$type will skip the stale process`
|
||||
);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
this._logExemptAllAssigneesOption();
|
||||
|
||||
return this._options.exemptAllAssignees;
|
||||
}
|
||||
|
||||
private _shouldExemptAllPullRequestAssignees(): boolean {
|
||||
if (this._options.exemptAllPrAssignees === true) {
|
||||
this._issueLogger.info(
|
||||
`The option ${this._issueLogger.createOptionLink(
|
||||
Option.ExemptAllPrAssignees
|
||||
)} is enabled. Any assignee on this $$type will skip the stale process`
|
||||
);
|
||||
|
||||
return true;
|
||||
} else if (this._options.exemptAllPrAssignees === false) {
|
||||
this._issueLogger.info(
|
||||
`The option ${this._issueLogger.createOptionLink(
|
||||
Option.ExemptAllPrAssignees
|
||||
)} is disabled. Only some specific assignees on this $$type will skip the stale process`
|
||||
);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
this._logExemptAllAssigneesOption();
|
||||
|
||||
return this._options.exemptAllAssignees;
|
||||
}
|
||||
|
||||
private _logExemptAllAssigneesOption(): void {
|
||||
if (this._options.exemptAllAssignees) {
|
||||
this._issueLogger.info(
|
||||
`The option ${this._issueLogger.createOptionLink(
|
||||
Option.ExemptAllAssignees
|
||||
)} is enabled. Any assignee on this $$type will skip the stale process`
|
||||
);
|
||||
} else {
|
||||
this._issueLogger.info(
|
||||
`The option ${this._issueLogger.createOptionLink(
|
||||
Option.ExemptAllAssignees
|
||||
)} is disabled. Only some specific assignees on this $$type will skip the stale process`
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
private _logSkip(): void {
|
||||
this._issueLogger.info(chalk.white('└──'), 'Skip the assignees checks');
|
||||
}
|
||||
}
|
||||
@@ -1,11 +1,13 @@
|
||||
import {IAssignee} from '../interfaces/assignee';
|
||||
import {IIssue} from '../interfaces/issue';
|
||||
import {IIssuesProcessorOptions} from '../interfaces/issues-processor-options';
|
||||
import {ILabel} from '../interfaces/label';
|
||||
import {IMilestone} from '../interfaces/milestone';
|
||||
import {IssueProcessorOptions, Label} from '../IssueProcessor';
|
||||
import {Issue} from './issue';
|
||||
|
||||
describe('Issue', (): void => {
|
||||
let issue: Issue;
|
||||
let optionsInterface: IssueProcessorOptions;
|
||||
let optionsInterface: IIssuesProcessorOptions;
|
||||
let issueInterface: IIssue;
|
||||
|
||||
beforeEach((): void => {
|
||||
@@ -24,13 +26,17 @@ describe('Issue', (): void => {
|
||||
debugOnly: false,
|
||||
deleteBranch: false,
|
||||
exemptIssueLabels: '',
|
||||
exemptIssueMilestones: '',
|
||||
exemptMilestones: '',
|
||||
exemptPrLabels: '',
|
||||
exemptPrMilestones: '',
|
||||
onlyLabels: '',
|
||||
onlyIssueLabels: '',
|
||||
onlyPrLabels: '',
|
||||
anyOfLabels: '',
|
||||
anyOfIssueLabels: '',
|
||||
anyOfPrLabels: '',
|
||||
operationsPerRun: 0,
|
||||
removeStaleWhenUpdated: false,
|
||||
removeIssueStaleWhenUpdated: undefined,
|
||||
removePrStaleWhenUpdated: undefined,
|
||||
repoToken: '',
|
||||
skipStaleIssueMessage: false,
|
||||
skipStalePrMessage: false,
|
||||
@@ -38,7 +44,20 @@ describe('Issue', (): void => {
|
||||
stalePrMessage: '',
|
||||
startDate: undefined,
|
||||
stalePrLabel: 'dummy-stale-pr-label',
|
||||
staleIssueLabel: 'dummy-stale-issue-label'
|
||||
staleIssueLabel: 'dummy-stale-issue-label',
|
||||
exemptMilestones: '',
|
||||
exemptIssueMilestones: '',
|
||||
exemptPrMilestones: '',
|
||||
exemptAllMilestones: false,
|
||||
exemptAllIssueMilestones: undefined,
|
||||
exemptAllPrMilestones: undefined,
|
||||
exemptAssignees: '',
|
||||
exemptIssueAssignees: '',
|
||||
exemptPrAssignees: '',
|
||||
exemptAllAssignees: false,
|
||||
exemptAllIssueAssignees: undefined,
|
||||
exemptAllPrAssignees: undefined,
|
||||
enableStatistics: false
|
||||
};
|
||||
issueInterface = {
|
||||
title: 'dummy-title',
|
||||
@@ -55,7 +74,12 @@ describe('Issue', (): void => {
|
||||
locked: false,
|
||||
milestone: {
|
||||
title: 'dummy-milestone'
|
||||
}
|
||||
},
|
||||
assignees: [
|
||||
{
|
||||
login: 'dummy-login'
|
||||
}
|
||||
]
|
||||
};
|
||||
issue = new Issue(optionsInterface, issueInterface);
|
||||
});
|
||||
@@ -91,7 +115,7 @@ describe('Issue', (): void => {
|
||||
expect(issue.labels).toStrictEqual([
|
||||
{
|
||||
name: 'dummy-name'
|
||||
} as Label
|
||||
} as ILabel
|
||||
]);
|
||||
});
|
||||
|
||||
@@ -121,56 +145,14 @@ describe('Issue', (): void => {
|
||||
} 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 assignees with the given issue assignees', (): void => {
|
||||
expect.assertions(1);
|
||||
|
||||
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');
|
||||
});
|
||||
expect(issue.assignees).toStrictEqual([
|
||||
{
|
||||
login: 'dummy-login'
|
||||
} as IAssignee
|
||||
]);
|
||||
});
|
||||
|
||||
describe('when the given issue does not contains the stale label', (): void => {
|
||||
@@ -193,7 +175,7 @@ describe('Issue', (): void => {
|
||||
issueInterface.labels = [
|
||||
{
|
||||
name: 'dummy-stale-issue-label'
|
||||
} as Label
|
||||
} as ILabel
|
||||
];
|
||||
issue = new Issue(optionsInterface, issueInterface);
|
||||
});
|
||||
@@ -205,4 +187,104 @@ describe('Issue', (): void => {
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('get isPullRequest', (): void => {
|
||||
describe('when the issue pull_request is not set', (): void => {
|
||||
beforeEach((): void => {
|
||||
issueInterface.pull_request = undefined;
|
||||
issue = new Issue(optionsInterface, issueInterface);
|
||||
});
|
||||
|
||||
it('should return false', (): void => {
|
||||
expect.assertions(1);
|
||||
|
||||
const result = issue.isPullRequest;
|
||||
|
||||
expect(result).toStrictEqual(false);
|
||||
});
|
||||
});
|
||||
|
||||
describe('when the issue pull_request is set', (): void => {
|
||||
beforeEach((): void => {
|
||||
issueInterface.pull_request = {};
|
||||
issue = new Issue(optionsInterface, issueInterface);
|
||||
});
|
||||
|
||||
it('should return true', (): void => {
|
||||
expect.assertions(1);
|
||||
|
||||
const result = issue.isPullRequest;
|
||||
|
||||
expect(result).toStrictEqual(true);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('get staleLabel', (): void => {
|
||||
describe('when the issue is not a pull request', (): void => {
|
||||
beforeEach((): void => {
|
||||
issueInterface.pull_request = undefined;
|
||||
issue = new Issue(optionsInterface, issueInterface);
|
||||
});
|
||||
|
||||
it('should return the issue stale label', (): void => {
|
||||
expect.assertions(1);
|
||||
|
||||
const result = issue.staleLabel;
|
||||
|
||||
expect(result).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 return the pull request stale label', (): void => {
|
||||
expect.assertions(1);
|
||||
|
||||
const result = issue.staleLabel;
|
||||
|
||||
expect(result).toStrictEqual('dummy-stale-pr-label');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('get hasAssignees', (): void => {
|
||||
describe('when the issue has no assignee', (): void => {
|
||||
beforeEach((): void => {
|
||||
issueInterface.assignees = [];
|
||||
issue = new Issue(optionsInterface, issueInterface);
|
||||
});
|
||||
|
||||
it('should return false', (): void => {
|
||||
expect.assertions(1);
|
||||
|
||||
const result = issue.hasAssignees;
|
||||
|
||||
expect(result).toStrictEqual(false);
|
||||
});
|
||||
});
|
||||
|
||||
describe('when the issue has at least one assignee', (): void => {
|
||||
beforeEach((): void => {
|
||||
issueInterface.assignees = [
|
||||
{
|
||||
login: 'dummy-login'
|
||||
}
|
||||
];
|
||||
issue = new Issue(optionsInterface, issueInterface);
|
||||
});
|
||||
|
||||
it('should return true', (): void => {
|
||||
expect.assertions(1);
|
||||
|
||||
const result = issue.hasAssignees;
|
||||
|
||||
expect(result).toStrictEqual(true);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,27 +1,42 @@
|
||||
import {isLabeled} from '../functions/is-labeled';
|
||||
import {isPullRequest} from '../functions/is-pull-request';
|
||||
import {IAssignee} from '../interfaces/assignee';
|
||||
import {IIssue} from '../interfaces/issue';
|
||||
import {IIssuesProcessorOptions} from '../interfaces/issues-processor-options';
|
||||
import {ILabel} from '../interfaces/label';
|
||||
import {IMilestone} from '../interfaces/milestone';
|
||||
import {IssueProcessorOptions, Label} from '../IssueProcessor';
|
||||
import {IsoDateString} from '../types/iso-date-string';
|
||||
import {Operations} from './operations';
|
||||
|
||||
export class Issue implements IIssue {
|
||||
private readonly _options: IssueProcessorOptions;
|
||||
private readonly _options: IIssuesProcessorOptions;
|
||||
readonly title: string;
|
||||
readonly number: number;
|
||||
created_at: IsoDateString;
|
||||
updated_at: IsoDateString;
|
||||
readonly labels: Label[];
|
||||
readonly labels: ILabel[];
|
||||
readonly pull_request: Object | null | undefined;
|
||||
readonly state: string;
|
||||
readonly state: string | 'closed' | 'open';
|
||||
readonly locked: boolean;
|
||||
readonly milestone: IMilestone | undefined;
|
||||
readonly isPullRequest: boolean;
|
||||
readonly staleLabel: string;
|
||||
readonly assignees: IAssignee[];
|
||||
isStale: boolean;
|
||||
operations = new Operations();
|
||||
|
||||
get isPullRequest(): boolean {
|
||||
return isPullRequest(this);
|
||||
}
|
||||
|
||||
get staleLabel(): string {
|
||||
return this._getStaleLabel();
|
||||
}
|
||||
|
||||
get hasAssignees(): boolean {
|
||||
return this.assignees.length > 0;
|
||||
}
|
||||
|
||||
constructor(
|
||||
options: Readonly<IssueProcessorOptions>,
|
||||
options: Readonly<IIssuesProcessorOptions>,
|
||||
issue: Readonly<IIssue>
|
||||
) {
|
||||
this._options = options;
|
||||
@@ -34,9 +49,8 @@ export class Issue implements IIssue {
|
||||
this.state = issue.state;
|
||||
this.locked = issue.locked;
|
||||
this.milestone = issue.milestone;
|
||||
this.assignees = issue.assignees;
|
||||
|
||||
this.isPullRequest = isPullRequest(this);
|
||||
this.staleLabel = this._getStaleLabel();
|
||||
this.isStale = isLabeled(this, this.staleLabel);
|
||||
}
|
||||
|
||||
|
||||
987
src/classes/issues-processor.ts
Normal file
987
src/classes/issues-processor.ts
Normal file
@@ -0,0 +1,987 @@
|
||||
import * as core from '@actions/core';
|
||||
import {context, getOctokit} from '@actions/github';
|
||||
import {GitHub} from '@actions/github/lib/utils';
|
||||
import {GetResponseTypeFromEndpointMethod} from '@octokit/types';
|
||||
import chalk from 'chalk';
|
||||
import {Option} from '../enums/option';
|
||||
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 {isBoolean} from '../functions/is-boolean';
|
||||
import {isLabeled} from '../functions/is-labeled';
|
||||
import {shouldMarkWhenStale} from '../functions/should-mark-when-stale';
|
||||
import {wordsToList} from '../functions/words-to-list';
|
||||
import {IComment} from '../interfaces/comment';
|
||||
import {IIssue} from '../interfaces/issue';
|
||||
import {IIssueEvent} from '../interfaces/issue-event';
|
||||
import {IIssuesProcessorOptions} from '../interfaces/issues-processor-options';
|
||||
import {IPullRequest} from '../interfaces/pull-request';
|
||||
import {Assignees} from './assignees';
|
||||
import {Issue} from './issue';
|
||||
import {IssueLogger} from './loggers/issue-logger';
|
||||
import {Logger} from './loggers/logger';
|
||||
import {Milestones} from './milestones';
|
||||
import {StaleOperations} from './stale-operations';
|
||||
import {Statistics} from './statistics';
|
||||
|
||||
/***
|
||||
* Handle processing of issues for staleness/closure.
|
||||
*/
|
||||
export class IssuesProcessor {
|
||||
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 static _endIssueProcessing(issue: Issue): void {
|
||||
const consumedOperationsCount: number = issue.operations.getConsumedOperationsCount();
|
||||
|
||||
if (consumedOperationsCount > 0) {
|
||||
const issueLogger: IssueLogger = new IssueLogger(issue);
|
||||
|
||||
issueLogger.info(
|
||||
chalk.cyan(consumedOperationsCount),
|
||||
`operation${
|
||||
consumedOperationsCount > 1 ? 's' : ''
|
||||
} consumed for this $$type`
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
private static _getStaleMessageUsedOptionName(
|
||||
issue: Readonly<Issue>
|
||||
): Option.StalePrMessage | Option.StaleIssueMessage {
|
||||
return issue.isPullRequest
|
||||
? Option.StalePrMessage
|
||||
: Option.StaleIssueMessage;
|
||||
}
|
||||
|
||||
private readonly _logger: Logger = new Logger();
|
||||
private readonly _operations: StaleOperations;
|
||||
private readonly _statistics: Statistics | undefined;
|
||||
readonly client: InstanceType<typeof GitHub>;
|
||||
readonly options: IIssuesProcessorOptions;
|
||||
readonly staleIssues: Issue[] = [];
|
||||
readonly closedIssues: Issue[] = [];
|
||||
readonly deletedBranchIssues: Issue[] = [];
|
||||
readonly removedLabelIssues: Issue[] = [];
|
||||
|
||||
constructor(options: IIssuesProcessorOptions) {
|
||||
this.options = options;
|
||||
this.client = getOctokit(this.options.repoToken);
|
||||
this._operations = new StaleOperations(this.options);
|
||||
|
||||
this._logger.info(chalk.yellow('Starting the stale action process...'));
|
||||
|
||||
if (this.options.debugOnly) {
|
||||
this._logger.warning(chalk.yellowBright('Executing in debug mode!'));
|
||||
this._logger.warning(
|
||||
chalk.yellowBright(
|
||||
'The debug output will be written but no issues/PRs will be processed.'
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
if (this.options.enableStatistics) {
|
||||
this._statistics = new Statistics();
|
||||
}
|
||||
}
|
||||
|
||||
async processIssues(page: Readonly<number> = 1): Promise<number> {
|
||||
// get the next batch of issues
|
||||
const issues: Issue[] = await this.getIssues(page);
|
||||
const actor: string = await this.getActor();
|
||||
|
||||
if (issues.length <= 0) {
|
||||
this._logger.info(
|
||||
chalk.green('No more issues found to process. Exiting...')
|
||||
);
|
||||
this._statistics
|
||||
?.setRemainingOperations(this._operations.getRemainingOperationsCount())
|
||||
.logStats();
|
||||
|
||||
return this._operations.getRemainingOperationsCount();
|
||||
} else {
|
||||
this._logger.info(
|
||||
chalk.yellow(
|
||||
`Processing the batch of issues ${chalk.cyan(
|
||||
`#${page}`
|
||||
)} containing ${chalk.cyan(issues.length)} issue${
|
||||
issues.length > 1 ? 's' : ''
|
||||
}...`
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
for (const issue of issues.values()) {
|
||||
const issueLogger: IssueLogger = new IssueLogger(issue);
|
||||
this._statistics?.incrementProcessedItemsCount(issue);
|
||||
|
||||
issueLogger.info(
|
||||
`Found this $$type last updated at: ${chalk.cyan(issue.updated_at)}`
|
||||
);
|
||||
|
||||
// calculate string based messages for this issue
|
||||
const staleMessage: string = issue.isPullRequest
|
||||
? this.options.stalePrMessage
|
||||
: this.options.staleIssueMessage;
|
||||
const closeMessage: string = issue.isPullRequest
|
||||
? this.options.closePrMessage
|
||||
: this.options.closeIssueMessage;
|
||||
const staleLabel: string = issue.isPullRequest
|
||||
? this.options.stalePrLabel
|
||||
: this.options.staleIssueLabel;
|
||||
const closeLabel: string = issue.isPullRequest
|
||||
? this.options.closePrLabel
|
||||
: this.options.closeIssueLabel;
|
||||
const skipMessage = issue.isPullRequest
|
||||
? this.options.skipStalePrMessage
|
||||
: this.options.skipStaleIssueMessage;
|
||||
const daysBeforeStale: number = issue.isPullRequest
|
||||
? this._getDaysBeforePrStale()
|
||||
: this._getDaysBeforeIssueStale();
|
||||
const onlyLabels: string[] = wordsToList(this._getOnlyLabels(issue));
|
||||
|
||||
if (onlyLabels.length > 0) {
|
||||
issueLogger.info(
|
||||
`The option ${issueLogger.createOptionLink(
|
||||
Option.OnlyLabels
|
||||
)} was specified to only process issues and pull requests with all those labels (${chalk.cyan(
|
||||
onlyLabels.length
|
||||
)})`
|
||||
);
|
||||
|
||||
const hasAllWhitelistedLabels: boolean = onlyLabels.every(
|
||||
(label: Readonly<string>): boolean => {
|
||||
return isLabeled(issue, label);
|
||||
}
|
||||
);
|
||||
|
||||
if (!hasAllWhitelistedLabels) {
|
||||
issueLogger.info(
|
||||
chalk.white('└──'),
|
||||
`Skipping this $$type because it doesn't have all the required labels`
|
||||
);
|
||||
|
||||
IssuesProcessor._endIssueProcessing(issue);
|
||||
continue; // Don't process issues without all of the required labels
|
||||
} else {
|
||||
issueLogger.info(
|
||||
chalk.white('├──'),
|
||||
`All the required labels are present on this $$type`
|
||||
);
|
||||
issueLogger.info(
|
||||
chalk.white('└──'),
|
||||
`Continuing the process for this $$type`
|
||||
);
|
||||
}
|
||||
} else {
|
||||
issueLogger.info(
|
||||
`The option ${issueLogger.createOptionLink(
|
||||
Option.OnlyLabels
|
||||
)} was not specified`
|
||||
);
|
||||
issueLogger.info(
|
||||
chalk.white('└──'),
|
||||
`Continuing the process for this $$type`
|
||||
);
|
||||
}
|
||||
|
||||
issueLogger.info(
|
||||
`Days before $$type stale: ${chalk.cyan(daysBeforeStale)}`
|
||||
);
|
||||
|
||||
const shouldMarkAsStale: boolean = shouldMarkWhenStale(daysBeforeStale);
|
||||
|
||||
if (!staleMessage && shouldMarkAsStale) {
|
||||
issueLogger.info(
|
||||
`Skipping this $$type because it should be marked as stale based on the option ${issueLogger.createOptionLink(
|
||||
this._getDaysBeforeStaleUsedOptionName(issue)
|
||||
)} (${chalk.cyan(
|
||||
daysBeforeStale
|
||||
)}) but the option ${issueLogger.createOptionLink(
|
||||
IssuesProcessor._getStaleMessageUsedOptionName(issue)
|
||||
)} is not set`
|
||||
);
|
||||
IssuesProcessor._endIssueProcessing(issue);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (issue.state === 'closed') {
|
||||
issueLogger.info(`Skipping this $$type because it is closed`);
|
||||
IssuesProcessor._endIssueProcessing(issue);
|
||||
continue; // Don't process closed issues
|
||||
}
|
||||
|
||||
if (issue.locked) {
|
||||
issueLogger.info(`Skipping this $$type because it is locked`);
|
||||
IssuesProcessor._endIssueProcessing(issue);
|
||||
continue; // Don't process locked issues
|
||||
}
|
||||
|
||||
// Try to remove the close label when not close/locked issue or PR
|
||||
await this._removeCloseLabel(issue, closeLabel);
|
||||
|
||||
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
|
||||
)} (${chalk.cyan(this.options.startDate)})`
|
||||
);
|
||||
|
||||
// Expecting that GitHub will always set a creation date on the issues and PRs
|
||||
// But you never know!
|
||||
if (!isValidDate(createdAt)) {
|
||||
IssuesProcessor._endIssueProcessing(issue);
|
||||
core.setFailed(
|
||||
new Error(
|
||||
`Invalid issue field: "created_at". Expected a valid date`
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
issueLogger.info(
|
||||
`$$type created the ${getHumanizedDate(createdAt)} (${chalk.cyan(
|
||||
issue.created_at
|
||||
)})`
|
||||
);
|
||||
|
||||
if (!isDateMoreRecentThan(createdAt, startDate)) {
|
||||
issueLogger.info(
|
||||
`Skipping this $$type because it was created before the specified start date`
|
||||
);
|
||||
|
||||
IssuesProcessor._endIssueProcessing(issue);
|
||||
continue; // Don't process issues which were created before the start date
|
||||
}
|
||||
}
|
||||
|
||||
if (issue.isStale) {
|
||||
issueLogger.info(`This $$type has a stale label`);
|
||||
} else {
|
||||
issueLogger.info(`This $$type hasn't a stale label`);
|
||||
}
|
||||
|
||||
const exemptLabels: string[] = wordsToList(
|
||||
issue.isPullRequest
|
||||
? this.options.exemptPrLabels
|
||||
: this.options.exemptIssueLabels
|
||||
);
|
||||
|
||||
if (
|
||||
exemptLabels.some((exemptLabel: Readonly<string>): boolean =>
|
||||
isLabeled(issue, exemptLabel)
|
||||
)
|
||||
) {
|
||||
if (issue.isStale) {
|
||||
issueLogger.info(`An exempt label was added after the stale label.`);
|
||||
await this._removeStaleLabel(issue, staleLabel);
|
||||
}
|
||||
|
||||
issueLogger.info(`Skipping this $$type because it has an exempt label`);
|
||||
IssuesProcessor._endIssueProcessing(issue);
|
||||
continue; // Don't process exempt issues
|
||||
}
|
||||
|
||||
const anyOfLabels: string[] = wordsToList(this._getAnyOfLabels(issue));
|
||||
|
||||
if (anyOfLabels.length > 0) {
|
||||
issueLogger.info(
|
||||
`The option ${issueLogger.createOptionLink(
|
||||
Option.AnyOfLabels
|
||||
)} was specified to only process the issues and pull requests with one of those labels (${chalk.cyan(
|
||||
anyOfLabels.length
|
||||
)})`
|
||||
);
|
||||
|
||||
const hasOneOfWhitelistedLabels: boolean = anyOfLabels.some(
|
||||
(label: Readonly<string>): boolean => {
|
||||
return isLabeled(issue, label);
|
||||
}
|
||||
);
|
||||
|
||||
if (!hasOneOfWhitelistedLabels) {
|
||||
issueLogger.info(
|
||||
chalk.white('└──'),
|
||||
`Skipping this $$type because it doesn't have one of the required labels`
|
||||
);
|
||||
IssuesProcessor._endIssueProcessing(issue);
|
||||
continue; // Don't process issues without any of the required labels
|
||||
} else {
|
||||
issueLogger.info(
|
||||
chalk.white('├──'),
|
||||
`One of the required labels is present on this $$type`
|
||||
);
|
||||
issueLogger.info(
|
||||
chalk.white('└──'),
|
||||
`Continuing the process for this $$type`
|
||||
);
|
||||
}
|
||||
} else {
|
||||
issueLogger.info(
|
||||
`The option ${issueLogger.createOptionLink(
|
||||
Option.AnyOfLabels
|
||||
)} was not specified`
|
||||
);
|
||||
issueLogger.info(
|
||||
chalk.white('└──'),
|
||||
`Continuing the process for this $$type`
|
||||
);
|
||||
}
|
||||
|
||||
const milestones: Milestones = new Milestones(this.options, issue);
|
||||
|
||||
if (milestones.shouldExemptMilestones()) {
|
||||
IssuesProcessor._endIssueProcessing(issue);
|
||||
continue; // Don't process exempt milestones
|
||||
}
|
||||
|
||||
const assignees: Assignees = new Assignees(this.options, issue);
|
||||
|
||||
if (assignees.shouldExemptAssignees()) {
|
||||
IssuesProcessor._endIssueProcessing(issue);
|
||||
continue; // Don't process exempt assignees
|
||||
}
|
||||
|
||||
// Should this issue be marked stale?
|
||||
const shouldBeStale = !IssuesProcessor._updatedSince(
|
||||
issue.updated_at,
|
||||
daysBeforeStale
|
||||
);
|
||||
|
||||
// Determine if this issue needs to be marked stale first
|
||||
if (!issue.isStale) {
|
||||
issueLogger.info(`This $$type is not stale`);
|
||||
const updatedAtDate: Date = new Date(issue.updated_at);
|
||||
|
||||
if (shouldBeStale) {
|
||||
issueLogger.info(
|
||||
`This $$type should be stale based on the last update date the ${getHumanizedDate(
|
||||
updatedAtDate
|
||||
)} (${chalk.cyan(issue.updated_at)})`
|
||||
);
|
||||
|
||||
if (shouldMarkAsStale) {
|
||||
issueLogger.info(
|
||||
`This $$type should be marked as stale based on the option ${issueLogger.createOptionLink(
|
||||
this._getDaysBeforeStaleUsedOptionName(issue)
|
||||
)} (${chalk.cyan(daysBeforeStale)})`
|
||||
);
|
||||
await this._markStale(issue, staleMessage, staleLabel, skipMessage);
|
||||
issue.isStale = true; // This issue is now considered stale
|
||||
issueLogger.info(`This $$type is now stale`);
|
||||
} else {
|
||||
issueLogger.info(
|
||||
`This $$type should not be marked as stale based on the option ${issueLogger.createOptionLink(
|
||||
this._getDaysBeforeStaleUsedOptionName(issue)
|
||||
)} (${chalk.cyan(daysBeforeStale)})`
|
||||
);
|
||||
}
|
||||
} else {
|
||||
issueLogger.info(
|
||||
`This $$type should not be stale based on the last update date the ${getHumanizedDate(
|
||||
updatedAtDate
|
||||
)} (${chalk.cyan(issue.updated_at)})`
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// Process the issue if it was marked stale
|
||||
if (issue.isStale) {
|
||||
issueLogger.info(`This $$type is already stale`);
|
||||
await this._processStaleIssue(
|
||||
issue,
|
||||
staleLabel,
|
||||
actor,
|
||||
closeMessage,
|
||||
closeLabel
|
||||
);
|
||||
}
|
||||
|
||||
IssuesProcessor._endIssueProcessing(issue);
|
||||
}
|
||||
|
||||
if (!this._operations.hasRemainingOperations()) {
|
||||
this._logger.warning(
|
||||
chalk.yellowBright('No more operations left! Exiting...')
|
||||
);
|
||||
this._logger.warning(
|
||||
chalk.yellowBright(
|
||||
`If you think that not enough issues were processed you could try to increase the quantity related to the ${this._logger.createOptionLink(
|
||||
Option.OperationsPerRun
|
||||
)} option which is currently set to ${chalk.cyan(
|
||||
this.options.operationsPerRun
|
||||
)}`
|
||||
)
|
||||
);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
this._logger.info(
|
||||
chalk.green(`Batch ${chalk.cyan(`#${page}`)} processed.`)
|
||||
);
|
||||
|
||||
// Do the next batch
|
||||
return this.processIssues(page + 1);
|
||||
}
|
||||
|
||||
// Grab comments for an issue since a given date
|
||||
async listIssueComments(
|
||||
issueNumber: Readonly<number>,
|
||||
sinceDate: Readonly<string>
|
||||
): Promise<IComment[]> {
|
||||
// Find any comments since date on the given issue
|
||||
try {
|
||||
this._operations.consumeOperation();
|
||||
this._statistics?.incrementFetchedItemsCommentsCount();
|
||||
const comments = await this.client.issues.listComments({
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
issue_number: issueNumber,
|
||||
since: sinceDate
|
||||
});
|
||||
return comments.data;
|
||||
} catch (error) {
|
||||
this._logger.error(`List issue comments error: ${error.message}`);
|
||||
return Promise.resolve([]);
|
||||
}
|
||||
}
|
||||
|
||||
// get the actor from the GitHub token or context
|
||||
async getActor(): Promise<string> {
|
||||
let actor;
|
||||
|
||||
try {
|
||||
this._operations.consumeOperation();
|
||||
actor = await this.client.users.getAuthenticated();
|
||||
} catch (error) {
|
||||
return context.actor;
|
||||
}
|
||||
|
||||
return actor.data.login;
|
||||
}
|
||||
|
||||
// grab issues from github in batches of 100
|
||||
async getIssues(page: number): Promise<Issue[]> {
|
||||
// generate type for response
|
||||
const endpoint = this.client.issues.listForRepo;
|
||||
type OctoKitIssueList = GetResponseTypeFromEndpointMethod<typeof endpoint>;
|
||||
|
||||
try {
|
||||
this._operations.consumeOperation();
|
||||
const issueResult: OctoKitIssueList = await this.client.issues.listForRepo(
|
||||
{
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
state: 'open',
|
||||
per_page: 100,
|
||||
direction: this.options.ascending ? 'asc' : 'desc',
|
||||
page
|
||||
}
|
||||
);
|
||||
this._statistics?.incrementFetchedItemsCount(issueResult.data.length);
|
||||
|
||||
return issueResult.data.map(
|
||||
(issue: Readonly<IIssue>): Issue => new Issue(this.options, issue)
|
||||
);
|
||||
} catch (error) {
|
||||
this._logger.error(`Get issues for repo error: ${error.message}`);
|
||||
return Promise.resolve([]);
|
||||
}
|
||||
}
|
||||
|
||||
// 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/
|
||||
async getLabelCreationDate(
|
||||
issue: Issue,
|
||||
label: string
|
||||
): Promise<string | undefined> {
|
||||
const issueLogger: IssueLogger = new IssueLogger(issue);
|
||||
|
||||
issueLogger.info(`Checking for label on this $$type`);
|
||||
|
||||
this._consumeIssueOperation(issue);
|
||||
this._statistics?.incrementFetchedItemsEventsCount();
|
||||
const options = this.client.issues.listEvents.endpoint.merge({
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
per_page: 100,
|
||||
issue_number: issue.number
|
||||
});
|
||||
|
||||
const events: IIssueEvent[] = await this.client.paginate(options);
|
||||
const reversedEvents = events.reverse();
|
||||
|
||||
const staleLabeledEvent = reversedEvents.find(
|
||||
event => event.event === 'labeled' && event.label.name === label
|
||||
);
|
||||
|
||||
if (!staleLabeledEvent) {
|
||||
// Must be old rather than labeled
|
||||
return undefined;
|
||||
}
|
||||
|
||||
return staleLabeledEvent.created_at;
|
||||
}
|
||||
|
||||
// handle all of the stale issue logic when we find a stale issue
|
||||
private async _processStaleIssue(
|
||||
issue: Issue,
|
||||
staleLabel: string,
|
||||
actor: string,
|
||||
closeMessage?: string,
|
||||
closeLabel?: string
|
||||
) {
|
||||
const issueLogger: IssueLogger = new IssueLogger(issue);
|
||||
const markedStaleOn: string =
|
||||
(await this.getLabelCreationDate(issue, staleLabel)) || issue.updated_at;
|
||||
issueLogger.info(`$$type marked stale on: ${chalk.cyan(markedStaleOn)}`);
|
||||
|
||||
const issueHasComments: boolean = await this._hasCommentsSince(
|
||||
issue,
|
||||
markedStaleOn,
|
||||
actor
|
||||
);
|
||||
issueLogger.info(
|
||||
`$$type has been commented on: ${chalk.cyan(issueHasComments)}`
|
||||
);
|
||||
|
||||
const daysBeforeClose: number = issue.isPullRequest
|
||||
? this._getDaysBeforePrClose()
|
||||
: this._getDaysBeforeIssueClose();
|
||||
|
||||
issueLogger.info(
|
||||
`Days before $$type close: ${chalk.cyan(daysBeforeClose)}`
|
||||
);
|
||||
|
||||
const issueHasUpdate: boolean = IssuesProcessor._updatedSince(
|
||||
issue.updated_at,
|
||||
daysBeforeClose
|
||||
);
|
||||
issueLogger.info(`$$type has been updated: ${chalk.cyan(issueHasUpdate)}`);
|
||||
|
||||
// should we un-stale this issue?
|
||||
if (this._shouldRemoveStaleWhenUpdated(issue) && issueHasComments) {
|
||||
await this._removeStaleLabel(issue, staleLabel);
|
||||
|
||||
issueLogger.info(`Skipping the process since the $$type is now un-stale`);
|
||||
|
||||
return; // nothing to do because it is no longer stale
|
||||
}
|
||||
|
||||
// now start closing logic
|
||||
if (daysBeforeClose < 0) {
|
||||
return; // nothing to do because we aren't closing stale issues
|
||||
}
|
||||
|
||||
if (!issueHasComments && !issueHasUpdate) {
|
||||
issueLogger.info(
|
||||
`Closing $$type because it was last updated on! ${chalk.cyan(
|
||||
issue.updated_at
|
||||
)}`
|
||||
);
|
||||
await this._closeIssue(issue, closeMessage, closeLabel);
|
||||
|
||||
if (this.options.deleteBranch && issue.pull_request) {
|
||||
issueLogger.info(
|
||||
`Deleting the branch the option ${issueLogger.createOptionLink(
|
||||
Option.DeleteBranch
|
||||
)} was specified`
|
||||
);
|
||||
await this._deleteBranch(issue);
|
||||
this.deletedBranchIssues.push(issue);
|
||||
}
|
||||
} else {
|
||||
issueLogger.info(
|
||||
`Stale $$type 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)
|
||||
private async _hasCommentsSince(
|
||||
issue: Issue,
|
||||
sinceDate: string,
|
||||
actor: string
|
||||
): Promise<boolean> {
|
||||
const issueLogger: IssueLogger = new IssueLogger(issue);
|
||||
|
||||
issueLogger.info(
|
||||
`Checking for comments on $$type since: ${chalk.cyan(sinceDate)}`
|
||||
);
|
||||
|
||||
if (!sinceDate) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// find any comments since the date
|
||||
const comments = await this.listIssueComments(issue.number, sinceDate);
|
||||
|
||||
const filteredComments = comments.filter(
|
||||
comment => comment.user.type === 'User' && comment.user.login !== actor
|
||||
);
|
||||
|
||||
issueLogger.info(
|
||||
`Comments not made by actor or another bot: ${chalk.cyan(
|
||||
filteredComments.length
|
||||
)}`
|
||||
);
|
||||
|
||||
// if there are any user comments returned
|
||||
return filteredComments.length > 0;
|
||||
}
|
||||
|
||||
// Mark an issue as stale with a comment and a label
|
||||
private async _markStale(
|
||||
issue: Issue,
|
||||
staleMessage: string,
|
||||
staleLabel: string,
|
||||
skipMessage: boolean
|
||||
): Promise<void> {
|
||||
const issueLogger: IssueLogger = new IssueLogger(issue);
|
||||
|
||||
issueLogger.info(`Marking this $$type as stale`);
|
||||
this.staleIssues.push(issue);
|
||||
|
||||
// if the issue is being marked stale, the updated date should be changed to right now
|
||||
// so that close calculations work correctly
|
||||
const newUpdatedAtDate: Date = new Date();
|
||||
issue.updated_at = newUpdatedAtDate.toString();
|
||||
|
||||
if (this.options.debugOnly) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!skipMessage) {
|
||||
try {
|
||||
this._consumeIssueOperation(issue);
|
||||
this._statistics?.incrementAddedItemsComment(issue);
|
||||
await this.client.issues.createComment({
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
issue_number: issue.number,
|
||||
body: staleMessage
|
||||
});
|
||||
} catch (error) {
|
||||
issueLogger.error(`Error when creating a comment: ${error.message}`);
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
this._consumeIssueOperation(issue);
|
||||
this._statistics?.incrementAddedItemsLabel(issue);
|
||||
this._statistics?.incrementStaleItemsCount(issue);
|
||||
await this.client.issues.addLabels({
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
issue_number: issue.number,
|
||||
labels: [staleLabel]
|
||||
});
|
||||
} catch (error) {
|
||||
issueLogger.error(`Error when adding a label: ${error.message}`);
|
||||
}
|
||||
}
|
||||
|
||||
// Close an issue based on staleness
|
||||
private async _closeIssue(
|
||||
issue: Issue,
|
||||
closeMessage?: string,
|
||||
closeLabel?: string
|
||||
): Promise<void> {
|
||||
const issueLogger: IssueLogger = new IssueLogger(issue);
|
||||
|
||||
issueLogger.info(`Closing $$type for being stale`);
|
||||
this.closedIssues.push(issue);
|
||||
|
||||
if (this.options.debugOnly) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (closeMessage) {
|
||||
try {
|
||||
this._consumeIssueOperation(issue);
|
||||
this._statistics?.incrementAddedItemsComment(issue);
|
||||
await this.client.issues.createComment({
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
issue_number: issue.number,
|
||||
body: closeMessage
|
||||
});
|
||||
} catch (error) {
|
||||
issueLogger.error(`Error when creating a comment: ${error.message}`);
|
||||
}
|
||||
}
|
||||
|
||||
if (closeLabel) {
|
||||
try {
|
||||
this._consumeIssueOperation(issue);
|
||||
this._statistics?.incrementAddedItemsLabel(issue);
|
||||
await this.client.issues.addLabels({
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
issue_number: issue.number,
|
||||
labels: [closeLabel]
|
||||
});
|
||||
} catch (error) {
|
||||
issueLogger.error(`Error when adding a label: ${error.message}`);
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
this._consumeIssueOperation(issue);
|
||||
this._statistics?.incrementClosedItemsCount(issue);
|
||||
await this.client.issues.update({
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
issue_number: issue.number,
|
||||
state: 'closed'
|
||||
});
|
||||
} catch (error) {
|
||||
issueLogger.error(`Error when updating this $$type: ${error.message}`);
|
||||
}
|
||||
}
|
||||
|
||||
private async _getPullRequest(
|
||||
issue: Issue
|
||||
): Promise<IPullRequest | undefined | void> {
|
||||
const issueLogger: IssueLogger = new IssueLogger(issue);
|
||||
|
||||
if (this.options.debugOnly) {
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
this._consumeIssueOperation(issue);
|
||||
this._statistics?.incrementFetchedPullRequestsCount();
|
||||
const pullRequest = await this.client.pulls.get({
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
pull_number: issue.number
|
||||
});
|
||||
|
||||
return pullRequest.data;
|
||||
} catch (error) {
|
||||
issueLogger.error(`Error when getting this $$type: ${error.message}`);
|
||||
}
|
||||
}
|
||||
|
||||
// Delete the branch on closed pull request
|
||||
private async _deleteBranch(issue: Issue): Promise<void> {
|
||||
const issueLogger: IssueLogger = new IssueLogger(issue);
|
||||
|
||||
issueLogger.info(`Delete branch from closed $$type - ${issue.title}`);
|
||||
|
||||
const pullRequest = await this._getPullRequest(issue);
|
||||
|
||||
if (!pullRequest) {
|
||||
issueLogger.info(
|
||||
`Not deleting this branch as no pull request was found for this $$type`
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
if (this.options.debugOnly) {
|
||||
return;
|
||||
}
|
||||
|
||||
const branch = pullRequest.head.ref;
|
||||
issueLogger.info(
|
||||
`Deleting the branch "${chalk.cyan(branch)}" from closed $$type`
|
||||
);
|
||||
|
||||
try {
|
||||
this._consumeIssueOperation(issue);
|
||||
this._statistics?.incrementDeletedBranchesCount();
|
||||
await this.client.git.deleteRef({
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
ref: `heads/${branch}`
|
||||
});
|
||||
} catch (error) {
|
||||
issueLogger.error(
|
||||
`Error when deleting the branch "${chalk.cyan(branch)}" from $$type: ${
|
||||
error.message
|
||||
}`
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// Remove a label from an issue or a pull request
|
||||
private async _removeLabel(issue: Issue, label: string): Promise<void> {
|
||||
const issueLogger: IssueLogger = new IssueLogger(issue);
|
||||
|
||||
issueLogger.info(
|
||||
`Removing the label "${chalk.cyan(label)}" from this $$type...`
|
||||
);
|
||||
this.removedLabelIssues.push(issue);
|
||||
|
||||
if (this.options.debugOnly) {
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
this._consumeIssueOperation(issue);
|
||||
this._statistics?.incrementDeletedItemsLabelsCount(issue);
|
||||
await this.client.issues.removeLabel({
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
issue_number: issue.number,
|
||||
name: label
|
||||
});
|
||||
issueLogger.info(`The label "${chalk.cyan(label)}" was removed`);
|
||||
} catch (error) {
|
||||
issueLogger.error(
|
||||
`Error when removing the label: "${chalk.cyan(error.message)}"`
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
private _getDaysBeforeIssueStale(): number {
|
||||
return isNaN(this.options.daysBeforeIssueStale)
|
||||
? this.options.daysBeforeStale
|
||||
: this.options.daysBeforeIssueStale;
|
||||
}
|
||||
|
||||
private _getDaysBeforePrStale(): number {
|
||||
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 _getOnlyLabels(issue: Issue): string {
|
||||
if (issue.isPullRequest) {
|
||||
if (this.options.onlyPrLabels !== '') {
|
||||
return this.options.onlyPrLabels;
|
||||
}
|
||||
} else {
|
||||
if (this.options.onlyIssueLabels !== '') {
|
||||
return this.options.onlyIssueLabels;
|
||||
}
|
||||
}
|
||||
|
||||
return this.options.onlyLabels;
|
||||
}
|
||||
|
||||
private _getAnyOfLabels(issue: Issue): string {
|
||||
if (issue.isPullRequest) {
|
||||
if (this.options.anyOfPrLabels !== '') {
|
||||
return this.options.anyOfPrLabels;
|
||||
}
|
||||
} else {
|
||||
if (this.options.anyOfIssueLabels !== '') {
|
||||
return this.options.anyOfIssueLabels;
|
||||
}
|
||||
}
|
||||
|
||||
return this.options.anyOfLabels;
|
||||
}
|
||||
|
||||
private _shouldRemoveStaleWhenUpdated(issue: Issue): boolean {
|
||||
if (issue.isPullRequest) {
|
||||
if (isBoolean(this.options.removePrStaleWhenUpdated)) {
|
||||
return this.options.removePrStaleWhenUpdated;
|
||||
}
|
||||
|
||||
return this.options.removeStaleWhenUpdated;
|
||||
}
|
||||
|
||||
if (isBoolean(this.options.removeIssueStaleWhenUpdated)) {
|
||||
return this.options.removeIssueStaleWhenUpdated;
|
||||
}
|
||||
|
||||
return this.options.removeStaleWhenUpdated;
|
||||
}
|
||||
|
||||
private async _removeStaleLabel(
|
||||
issue: Issue,
|
||||
staleLabel: Readonly<string>
|
||||
): Promise<void> {
|
||||
const issueLogger: IssueLogger = new IssueLogger(issue);
|
||||
|
||||
issueLogger.info(
|
||||
`The $$type is no longer stale. Removing the stale label...`
|
||||
);
|
||||
|
||||
await this._removeLabel(issue, staleLabel);
|
||||
this._statistics?.incrementUndoStaleItemsCount(issue);
|
||||
}
|
||||
|
||||
private async _removeCloseLabel(
|
||||
issue: Issue,
|
||||
closeLabel: Readonly<string | undefined>
|
||||
): Promise<void> {
|
||||
const issueLogger: IssueLogger = new IssueLogger(issue);
|
||||
|
||||
issueLogger.info(
|
||||
`The $$type is not closed nor locked. Trying to remove the close label...`
|
||||
);
|
||||
|
||||
if (!closeLabel) {
|
||||
issueLogger.info(`There is no close label on this $$type. Skip`);
|
||||
|
||||
return Promise.resolve();
|
||||
}
|
||||
|
||||
if (isLabeled(issue, closeLabel)) {
|
||||
issueLogger.info(
|
||||
`The $$type has a close label "${chalk.cyan(
|
||||
closeLabel
|
||||
)}". Removing the close label...`
|
||||
);
|
||||
|
||||
await this._removeLabel(issue, closeLabel);
|
||||
this._statistics?.incrementDeletedCloseItemsLabelsCount(issue);
|
||||
}
|
||||
}
|
||||
|
||||
private _consumeIssueOperation(issue: Readonly<Issue>): void {
|
||||
this._operations.consumeOperation();
|
||||
issue.operations.consumeOperation();
|
||||
}
|
||||
|
||||
private _getDaysBeforeStaleUsedOptionName(
|
||||
issue: Readonly<Issue>
|
||||
):
|
||||
| Option.DaysBeforeStale
|
||||
| Option.DaysBeforeIssueStale
|
||||
| Option.DaysBeforePrStale {
|
||||
return issue.isPullRequest
|
||||
? this._getDaysBeforePrStaleUsedOptionName()
|
||||
: this._getDaysBeforeIssueStaleUsedOptionName();
|
||||
}
|
||||
|
||||
private _getDaysBeforeIssueStaleUsedOptionName():
|
||||
| Option.DaysBeforeStale
|
||||
| Option.DaysBeforeIssueStale {
|
||||
return isNaN(this.options.daysBeforeIssueStale)
|
||||
? Option.DaysBeforeStale
|
||||
: Option.DaysBeforeIssueStale;
|
||||
}
|
||||
|
||||
private _getDaysBeforePrStaleUsedOptionName():
|
||||
| Option.DaysBeforeStale
|
||||
| Option.DaysBeforePrStale {
|
||||
return isNaN(this.options.daysBeforePrStale)
|
||||
? Option.DaysBeforeStale
|
||||
: Option.DaysBeforePrStale;
|
||||
}
|
||||
}
|
||||
@@ -1,3 +1,5 @@
|
||||
import {DefaultProcessorOptions} from '../../../__tests__/constants/default-processor-options';
|
||||
import {generateIIssue} from '../../../__tests__/functions/generate-iissue';
|
||||
import {Issue} from '../issue';
|
||||
import {IssueLogger} from './issue-logger';
|
||||
import * as core from '@actions/core';
|
||||
@@ -5,21 +7,20 @@ import * as core from '@actions/core';
|
||||
describe('IssueLogger', (): void => {
|
||||
let issue: Issue;
|
||||
let issueLogger: IssueLogger;
|
||||
let message: string;
|
||||
|
||||
beforeEach((): void => {
|
||||
issue = {
|
||||
number: 8
|
||||
} as Issue;
|
||||
issueLogger = new IssueLogger(issue);
|
||||
});
|
||||
let coreWarningSpy: jest.SpyInstance;
|
||||
|
||||
describe('warning()', (): void => {
|
||||
let message: string;
|
||||
|
||||
let coreWarningSpy: jest.SpyInstance;
|
||||
|
||||
beforeEach((): void => {
|
||||
message = 'dummy-message';
|
||||
issue = new Issue(
|
||||
DefaultProcessorOptions,
|
||||
generateIIssue({
|
||||
number: 8
|
||||
})
|
||||
);
|
||||
issueLogger = new IssueLogger(issue);
|
||||
|
||||
coreWarningSpy = jest.spyOn(core, 'warning').mockImplementation();
|
||||
});
|
||||
@@ -35,12 +36,17 @@ describe('IssueLogger', (): void => {
|
||||
});
|
||||
|
||||
describe('info()', (): void => {
|
||||
let message: string;
|
||||
|
||||
let coreInfoSpy: jest.SpyInstance;
|
||||
|
||||
beforeEach((): void => {
|
||||
message = 'dummy-message';
|
||||
issue = new Issue(
|
||||
DefaultProcessorOptions,
|
||||
generateIIssue({
|
||||
number: 8
|
||||
})
|
||||
);
|
||||
issueLogger = new IssueLogger(issue);
|
||||
|
||||
coreInfoSpy = jest.spyOn(core, 'info').mockImplementation();
|
||||
});
|
||||
@@ -56,12 +62,17 @@ describe('IssueLogger', (): void => {
|
||||
});
|
||||
|
||||
describe('error()', (): void => {
|
||||
let message: string;
|
||||
|
||||
let coreErrorSpy: jest.SpyInstance;
|
||||
|
||||
beforeEach((): void => {
|
||||
message = 'dummy-message';
|
||||
issue = new Issue(
|
||||
DefaultProcessorOptions,
|
||||
generateIIssue({
|
||||
number: 8
|
||||
})
|
||||
);
|
||||
issueLogger = new IssueLogger(issue);
|
||||
|
||||
coreErrorSpy = jest.spyOn(core, 'error').mockImplementation();
|
||||
});
|
||||
@@ -75,4 +86,82 @@ describe('IssueLogger', (): void => {
|
||||
expect(coreErrorSpy).toHaveBeenCalledWith('[#8] dummy-message');
|
||||
});
|
||||
});
|
||||
|
||||
it('should prefix the message with the issue number', (): void => {
|
||||
expect.assertions(2);
|
||||
message = 'dummy-message';
|
||||
issue = new Issue(
|
||||
DefaultProcessorOptions,
|
||||
generateIIssue({
|
||||
number: 123
|
||||
})
|
||||
);
|
||||
issueLogger = new IssueLogger(issue);
|
||||
coreWarningSpy = jest.spyOn(core, 'warning').mockImplementation();
|
||||
|
||||
issueLogger.warning(message);
|
||||
|
||||
expect(coreWarningSpy).toHaveBeenCalledTimes(1);
|
||||
expect(coreWarningSpy).toHaveBeenCalledWith('[#123] dummy-message');
|
||||
});
|
||||
|
||||
it.each`
|
||||
pull_request | replacement
|
||||
${{key: 'value'}} | ${'pull request'}
|
||||
${{}} | ${'pull request'}
|
||||
${null} | ${'issue'}
|
||||
${undefined} | ${'issue'}
|
||||
`(
|
||||
'should replace the special tokens "$$type" with the corresponding type',
|
||||
({pull_request, replacement}): void => {
|
||||
expect.assertions(2);
|
||||
message = 'The $$type will stale! $$type will soon be closed!';
|
||||
issue = new Issue(
|
||||
DefaultProcessorOptions,
|
||||
generateIIssue({
|
||||
number: 8,
|
||||
pull_request
|
||||
})
|
||||
);
|
||||
issueLogger = new IssueLogger(issue);
|
||||
coreWarningSpy = jest.spyOn(core, 'warning').mockImplementation();
|
||||
|
||||
issueLogger.warning(message);
|
||||
|
||||
expect(coreWarningSpy).toHaveBeenCalledTimes(1);
|
||||
expect(coreWarningSpy).toHaveBeenCalledWith(
|
||||
`[#8] The ${replacement} will stale! ${replacement} will soon be closed!`
|
||||
);
|
||||
}
|
||||
);
|
||||
|
||||
it.each`
|
||||
pull_request | replacement
|
||||
${{key: 'value'}} | ${'Pull request'}
|
||||
${{}} | ${'Pull request'}
|
||||
${null} | ${'Issue'}
|
||||
${undefined} | ${'Issue'}
|
||||
`(
|
||||
'should replace the special token "$$type" with the corresponding type with first letter as uppercase',
|
||||
({pull_request, replacement}): void => {
|
||||
expect.assertions(2);
|
||||
message = '$$type will stale';
|
||||
issue = new Issue(
|
||||
DefaultProcessorOptions,
|
||||
generateIIssue({
|
||||
number: 8,
|
||||
pull_request
|
||||
})
|
||||
);
|
||||
issueLogger = new IssueLogger(issue);
|
||||
coreWarningSpy = jest.spyOn(core, 'warning').mockImplementation();
|
||||
|
||||
issueLogger.warning(message);
|
||||
|
||||
expect(coreWarningSpy).toHaveBeenCalledTimes(1);
|
||||
expect(coreWarningSpy).toHaveBeenCalledWith(
|
||||
`[#8] ${replacement} will stale`
|
||||
);
|
||||
}
|
||||
);
|
||||
});
|
||||
|
||||
@@ -1,31 +1,79 @@
|
||||
import * as core from '@actions/core';
|
||||
import chalk from 'chalk';
|
||||
import {Issue} from '../issue';
|
||||
import {Logger} from './logger';
|
||||
|
||||
export class IssueLogger implements Logger {
|
||||
/**
|
||||
* @description
|
||||
* Each log will prefix the message with the issue number
|
||||
*
|
||||
* @example
|
||||
* warning('No stale') => "[#123] No stale"
|
||||
*
|
||||
* Each log method can have special tokens:
|
||||
* - $$type => will replace this by either "pull request" or "issue" depending of the type of issue
|
||||
*
|
||||
* @example
|
||||
* warning('The $$type will stale') => "The pull request will stale"
|
||||
*/
|
||||
export class IssueLogger extends Logger {
|
||||
private readonly _issue: Issue;
|
||||
|
||||
constructor(issue: Issue) {
|
||||
super();
|
||||
this._issue = issue;
|
||||
}
|
||||
|
||||
warning(message: Readonly<string>): void {
|
||||
core.warning(this._prefixWithIssueNumber(message));
|
||||
warning(...message: string[]): void {
|
||||
super.warning(this._format(...message));
|
||||
}
|
||||
|
||||
info(message: Readonly<string>): void {
|
||||
core.info(this._prefixWithIssueNumber(message));
|
||||
info(...message: string[]): void {
|
||||
super.info(this._format(...message));
|
||||
}
|
||||
|
||||
error(message: Readonly<string>): void {
|
||||
core.error(this._prefixWithIssueNumber(message));
|
||||
error(...message: string[]): void {
|
||||
super.error(this._format(...message));
|
||||
}
|
||||
|
||||
private _replaceTokens(message: Readonly<string>): string {
|
||||
return this._replaceTypeToken(message);
|
||||
}
|
||||
|
||||
private _replaceTypeToken(message: Readonly<string>): string {
|
||||
return message
|
||||
.replace(
|
||||
/^\$\$type/,
|
||||
this._issue.isPullRequest ? 'Pull request' : 'Issue'
|
||||
)
|
||||
.replace(
|
||||
/\$\$type/g,
|
||||
this._issue.isPullRequest ? 'pull request' : 'issue'
|
||||
);
|
||||
}
|
||||
|
||||
private _prefixWithIssueNumber(message: Readonly<string>): string {
|
||||
return `[#${this._getIssueNumber()}] ${message}`;
|
||||
return `${this._getPrefix()} ${message}`;
|
||||
}
|
||||
|
||||
private _getIssueNumber(): number {
|
||||
return this._issue.number;
|
||||
}
|
||||
|
||||
private _format(...message: string[]): string {
|
||||
return this._prefixWithIssueNumber(this._replaceTokens(message.join(' ')));
|
||||
}
|
||||
|
||||
private _getPrefix(): string {
|
||||
return this._issue.isPullRequest
|
||||
? this._getPullRequestPrefix()
|
||||
: this._getIssuePrefix();
|
||||
}
|
||||
|
||||
private _getIssuePrefix(): string {
|
||||
return chalk.red(`[#${this._getIssueNumber()}]`);
|
||||
}
|
||||
|
||||
private _getPullRequestPrefix(): string {
|
||||
return chalk.blue(`[#${this._getIssueNumber()}]`);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,15 +1,28 @@
|
||||
import * as core from '@actions/core';
|
||||
import chalk from 'chalk';
|
||||
import terminalLink from 'terminal-link';
|
||||
import {Option} from '../../enums/option';
|
||||
|
||||
export class Logger {
|
||||
warning(message: Readonly<string>): void {
|
||||
core.warning(message);
|
||||
warning(...message: string[]): void {
|
||||
core.warning(chalk.whiteBright(...message));
|
||||
}
|
||||
|
||||
info(message: Readonly<string>): void {
|
||||
core.info(message);
|
||||
info(...message: string[]): void {
|
||||
core.info(chalk.whiteBright(...message));
|
||||
}
|
||||
|
||||
error(message: Readonly<string>): void {
|
||||
core.error(message);
|
||||
error(...message: string[]): void {
|
||||
core.error(chalk.whiteBright(...message));
|
||||
}
|
||||
|
||||
createLink(name: Readonly<string>, link: Readonly<string>): string {
|
||||
return terminalLink(name, link);
|
||||
}
|
||||
|
||||
createOptionLink(option: Readonly<Option>): string {
|
||||
return chalk.magenta(
|
||||
this.createLink(option, `https://github.com/actions/stale#${option}`)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,57 +1,19 @@
|
||||
import {DefaultProcessorOptions} from '../../__tests__/constants/default-processor-options';
|
||||
import {generateIIssue} from '../../__tests__/functions/generate-iissue';
|
||||
import {IIssue} from '../interfaces/issue';
|
||||
import {IssueProcessorOptions} from '../IssueProcessor';
|
||||
import {IIssuesProcessorOptions} from '../interfaces/issues-processor-options';
|
||||
import {Issue} from './issue';
|
||||
import {Milestones} from './milestones';
|
||||
|
||||
describe('Milestones', (): void => {
|
||||
let milestones: Milestones;
|
||||
let optionsInterface: IssueProcessorOptions;
|
||||
let optionsInterface: IIssuesProcessorOptions;
|
||||
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: []
|
||||
};
|
||||
optionsInterface = {...DefaultProcessorOptions, exemptAllMilestones: false};
|
||||
issueInterface = generateIIssue();
|
||||
});
|
||||
|
||||
describe('shouldExemptMilestones()', (): void => {
|
||||
@@ -323,21 +285,49 @@ describe('Milestones', (): void => {
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
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 => {
|
||||
describe('when the given options are configured to exempt all milestones', (): void => {
|
||||
beforeEach((): void => {
|
||||
optionsInterface.exemptMilestones = '';
|
||||
optionsInterface.exemptAllMilestones = true;
|
||||
});
|
||||
|
||||
describe('when the given options are not configured to exempt a pull request milestone', (): void => {
|
||||
describe('when the given issue does not have a milestone', (): void => {
|
||||
beforeEach((): void => {
|
||||
optionsInterface.exemptPrMilestones = '';
|
||||
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-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 not configured to exempt all issue milestones', (): void => {
|
||||
beforeEach((): void => {
|
||||
optionsInterface.exemptAllIssueMilestones = false;
|
||||
});
|
||||
|
||||
describe('when the given issue does not have a milestone', (): void => {
|
||||
@@ -357,6 +347,98 @@ describe('Milestones', (): void => {
|
||||
});
|
||||
|
||||
describe('when the given issue does have a 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 options are configured to exempt all issue milestones', (): void => {
|
||||
beforeEach((): void => {
|
||||
optionsInterface.exemptAllIssueMilestones = true;
|
||||
});
|
||||
|
||||
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-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 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 pull request 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 pull request does have a milestone', (): void => {
|
||||
beforeEach((): void => {
|
||||
issueInterface.milestone = {
|
||||
title: 'dummy-title'
|
||||
@@ -380,7 +462,7 @@ describe('Milestones', (): void => {
|
||||
optionsInterface.exemptPrMilestones = 'dummy-exempt-pr-milestone';
|
||||
});
|
||||
|
||||
describe('when the given issue does not have a milestone', (): void => {
|
||||
describe('when the given pull request does not have a milestone', (): void => {
|
||||
beforeEach((): void => {
|
||||
issueInterface.milestone = undefined;
|
||||
});
|
||||
@@ -396,7 +478,7 @@ describe('Milestones', (): void => {
|
||||
});
|
||||
});
|
||||
|
||||
describe('when the given issue does have a milestone different than the exempt pull request milestone', (): void => {
|
||||
describe('when the given pull request does have a milestone different than the exempt pull request milestone', (): void => {
|
||||
beforeEach((): void => {
|
||||
issueInterface.milestone = {
|
||||
title: 'dummy-title'
|
||||
@@ -414,7 +496,7 @@ describe('Milestones', (): void => {
|
||||
});
|
||||
});
|
||||
|
||||
describe('when the given issue does have a milestone equaling the exempt pull request milestone', (): void => {
|
||||
describe('when the given pull request does have a milestone equaling the exempt pull request milestone', (): void => {
|
||||
beforeEach((): void => {
|
||||
issueInterface.milestone = {
|
||||
title: 'dummy-exempt-pr-milestone'
|
||||
@@ -444,7 +526,7 @@ describe('Milestones', (): void => {
|
||||
optionsInterface.exemptPrMilestones = '';
|
||||
});
|
||||
|
||||
describe('when the given issue does not have a milestone', (): void => {
|
||||
describe('when the given pull request does not have a milestone', (): void => {
|
||||
beforeEach((): void => {
|
||||
issueInterface.milestone = undefined;
|
||||
});
|
||||
@@ -460,7 +542,7 @@ describe('Milestones', (): void => {
|
||||
});
|
||||
});
|
||||
|
||||
describe('when the given issue does have a milestone different than the exempt milestone', (): void => {
|
||||
describe('when the given pull request does have a milestone different than the exempt milestone', (): void => {
|
||||
beforeEach((): void => {
|
||||
issueInterface.milestone = {
|
||||
title: 'dummy-title'
|
||||
@@ -478,7 +560,7 @@ describe('Milestones', (): void => {
|
||||
});
|
||||
});
|
||||
|
||||
describe('when the given issue does have a milestone equaling the exempt milestone', (): void => {
|
||||
describe('when the given pull request does have a milestone equaling the exempt milestone', (): void => {
|
||||
beforeEach((): void => {
|
||||
issueInterface.milestone = {
|
||||
title: 'dummy-exempt-milestone'
|
||||
@@ -502,7 +584,7 @@ describe('Milestones', (): void => {
|
||||
optionsInterface.exemptPrMilestones = 'dummy-exempt-pr-milestone';
|
||||
});
|
||||
|
||||
describe('when the given issue does not have a milestone', (): void => {
|
||||
describe('when the given pull request does not have a milestone', (): void => {
|
||||
beforeEach((): void => {
|
||||
issueInterface.milestone = undefined;
|
||||
});
|
||||
@@ -518,7 +600,7 @@ describe('Milestones', (): void => {
|
||||
});
|
||||
});
|
||||
|
||||
describe('when the given issue does have a milestone different than the exempt pull request milestone', (): void => {
|
||||
describe('when the given pull request does have a milestone different than the exempt pull request milestone', (): void => {
|
||||
beforeEach((): void => {
|
||||
issueInterface.milestone = {
|
||||
title: 'dummy-title'
|
||||
@@ -536,7 +618,7 @@ describe('Milestones', (): void => {
|
||||
});
|
||||
});
|
||||
|
||||
describe('when the given issue does have a milestone equaling the exempt pull request milestone', (): void => {
|
||||
describe('when the given pull request does have a milestone equaling the exempt pull request milestone', (): void => {
|
||||
beforeEach((): void => {
|
||||
issueInterface.milestone = {
|
||||
title: 'dummy-exempt-pr-milestone'
|
||||
@@ -554,7 +636,7 @@ describe('Milestones', (): void => {
|
||||
});
|
||||
});
|
||||
|
||||
describe('when the given issue does have a milestone different than the exempt milestone', (): void => {
|
||||
describe('when the given pull request does have a milestone different than the exempt milestone', (): void => {
|
||||
beforeEach((): void => {
|
||||
issueInterface.milestone = {
|
||||
title: 'dummy-title'
|
||||
@@ -572,7 +654,7 @@ describe('Milestones', (): void => {
|
||||
});
|
||||
});
|
||||
|
||||
describe('when the given issue does have a milestone equaling the exempt milestone', (): void => {
|
||||
describe('when the given pull request does have a milestone equaling the exempt milestone', (): void => {
|
||||
beforeEach((): void => {
|
||||
issueInterface.milestone = {
|
||||
title: 'dummy-exempt-milestone'
|
||||
@@ -591,6 +673,126 @@ describe('Milestones', (): void => {
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('when the given options are configured to exempt all milestones', (): void => {
|
||||
beforeEach((): void => {
|
||||
optionsInterface.exemptAllMilestones = true;
|
||||
});
|
||||
|
||||
describe('when the given pull request 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 pull request does have a 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 not configured to exempt all pull request milestones', (): void => {
|
||||
beforeEach((): void => {
|
||||
optionsInterface.exemptAllPrMilestones = false;
|
||||
});
|
||||
|
||||
describe('when the given pull request 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 pull request does have a 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 options are configured to exempt all pull request milestones', (): void => {
|
||||
beforeEach((): void => {
|
||||
optionsInterface.exemptAllPrMilestones = true;
|
||||
});
|
||||
|
||||
describe('when the given pull request 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 pull request does have a 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);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,49 +1,193 @@
|
||||
import chalk from 'chalk';
|
||||
import deburr from 'lodash.deburr';
|
||||
import {Option} from '../enums/option';
|
||||
import {wordsToList} from '../functions/words-to-list';
|
||||
import {IssueProcessorOptions} from '../IssueProcessor';
|
||||
import {IIssuesProcessorOptions} from '../interfaces/issues-processor-options';
|
||||
import {Issue} from './issue';
|
||||
import {IssueLogger} from './loggers/issue-logger';
|
||||
|
||||
type CleanMilestone = string;
|
||||
|
||||
export class Milestones {
|
||||
private static _cleanMilestone(label: Readonly<string>): CleanMilestone {
|
||||
return deburr(label.toLowerCase());
|
||||
private static _cleanMilestone(milestone: Readonly<string>): CleanMilestone {
|
||||
return deburr(milestone.toLowerCase());
|
||||
}
|
||||
|
||||
private readonly _options: IssueProcessorOptions;
|
||||
private readonly _options: IIssuesProcessorOptions;
|
||||
private readonly _issue: Issue;
|
||||
private readonly _issueLogger: IssueLogger;
|
||||
|
||||
constructor(options: Readonly<IssueProcessorOptions>, issue: Issue) {
|
||||
constructor(options: Readonly<IIssuesProcessorOptions>, issue: Issue) {
|
||||
this._options = options;
|
||||
this._issue = issue;
|
||||
this._issueLogger = new IssueLogger(issue);
|
||||
}
|
||||
|
||||
shouldExemptMilestones(): boolean {
|
||||
if (!this._issue.milestone) {
|
||||
this._issueLogger.info('This $$type has no milestone');
|
||||
this._logSkip();
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
if (this._shouldExemptAllMilestones()) {
|
||||
this._issueLogger.info(
|
||||
chalk.white('└──'),
|
||||
'Skipping this $$type because it has a milestone'
|
||||
);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
const exemptMilestones: string[] = this._getExemptMilestones();
|
||||
|
||||
return exemptMilestones.some((exemptMilestone: Readonly<string>): boolean =>
|
||||
this._hasMilestone(exemptMilestone)
|
||||
if (exemptMilestones.length === 0) {
|
||||
this._issueLogger.info(
|
||||
chalk.white('├──'),
|
||||
`No milestone option was specified to skip the stale process for this $$type`
|
||||
);
|
||||
this._logSkip();
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
this._issueLogger.info(
|
||||
chalk.white('├──'),
|
||||
`Found ${chalk.cyan(exemptMilestones.length)} milestone${
|
||||
exemptMilestones.length > 1 ? 's' : ''
|
||||
} that can exempt stale on this $$type`
|
||||
);
|
||||
|
||||
const hasExemptMilestone: boolean = exemptMilestones.some(
|
||||
(exemptMilestone: Readonly<string>): boolean =>
|
||||
this._hasMilestone(exemptMilestone)
|
||||
);
|
||||
|
||||
if (!hasExemptMilestone) {
|
||||
this._issueLogger.info(
|
||||
chalk.white('├──'),
|
||||
'No milestone on this $$type can exempt the stale process'
|
||||
);
|
||||
this._logSkip();
|
||||
} else {
|
||||
this._issueLogger.info(
|
||||
chalk.white('└──'),
|
||||
'Skipping this $$type because it has an exempt milestone'
|
||||
);
|
||||
}
|
||||
|
||||
return hasExemptMilestone;
|
||||
}
|
||||
|
||||
private _getExemptMilestones(): string[] {
|
||||
return wordsToList(
|
||||
this._issue.isPullRequest
|
||||
? this._getExemptPullRequestMilestones()
|
||||
: this._getExemptIssueMilestones()
|
||||
return this._issue.isPullRequest
|
||||
? this._getExemptPullRequestMilestones()
|
||||
: this._getExemptIssueMilestones();
|
||||
}
|
||||
|
||||
private _getExemptIssueMilestones(): string[] {
|
||||
if (this._options.exemptIssueMilestones === '') {
|
||||
this._issueLogger.info(
|
||||
chalk.white('├──'),
|
||||
`The option ${this._issueLogger.createOptionLink(
|
||||
Option.ExemptIssueMilestones
|
||||
)} is disabled. No specific milestone can skip the stale process for this $$type`
|
||||
);
|
||||
|
||||
if (this._options.exemptMilestones === '') {
|
||||
this._issueLogger.info(
|
||||
chalk.white('├──'),
|
||||
`The option ${this._issueLogger.createOptionLink(
|
||||
Option.ExemptMilestones
|
||||
)} is disabled. No specific milestone can skip the stale process for this $$type`
|
||||
);
|
||||
|
||||
return [];
|
||||
}
|
||||
|
||||
const exemptMilestones: string[] = wordsToList(
|
||||
this._options.exemptMilestones
|
||||
);
|
||||
|
||||
this._issueLogger.info(
|
||||
chalk.white('├──'),
|
||||
`The option ${this._issueLogger.createOptionLink(
|
||||
Option.ExemptMilestones
|
||||
)} is set. ${chalk.cyan(exemptMilestones.length)} milestone${
|
||||
exemptMilestones.length === 1 ? '' : 's'
|
||||
} can skip the stale process for this $$type`
|
||||
);
|
||||
|
||||
return exemptMilestones;
|
||||
}
|
||||
|
||||
const exemptMilestones: string[] = wordsToList(
|
||||
this._options.exemptIssueMilestones
|
||||
);
|
||||
|
||||
this._issueLogger.info(
|
||||
chalk.white('├──'),
|
||||
`The option ${this._issueLogger.createOptionLink(
|
||||
Option.ExemptIssueMilestones
|
||||
)} is set. ${chalk.cyan(exemptMilestones.length)} milestone${
|
||||
exemptMilestones.length === 1 ? '' : 's'
|
||||
} can skip the stale process for this $$type`
|
||||
);
|
||||
|
||||
return exemptMilestones;
|
||||
}
|
||||
|
||||
private _getExemptIssueMilestones(): string {
|
||||
return this._options.exemptIssueMilestones !== ''
|
||||
? this._options.exemptIssueMilestones
|
||||
: this._options.exemptMilestones;
|
||||
}
|
||||
private _getExemptPullRequestMilestones(): string[] {
|
||||
if (this._options.exemptPrMilestones === '') {
|
||||
this._issueLogger.info(
|
||||
chalk.white('├──'),
|
||||
`The option ${this._issueLogger.createOptionLink(
|
||||
Option.ExemptPrMilestones
|
||||
)} is disabled. No specific milestone can skip the stale process for this $$type`
|
||||
);
|
||||
|
||||
private _getExemptPullRequestMilestones(): string {
|
||||
return this._options.exemptPrMilestones !== ''
|
||||
? this._options.exemptPrMilestones
|
||||
: this._options.exemptMilestones;
|
||||
if (this._options.exemptMilestones === '') {
|
||||
this._issueLogger.info(
|
||||
chalk.white('├──'),
|
||||
`The option ${this._issueLogger.createOptionLink(
|
||||
Option.ExemptMilestones
|
||||
)} is disabled. No specific milestone can skip the stale process for this $$type`
|
||||
);
|
||||
|
||||
return [];
|
||||
}
|
||||
|
||||
const exemptMilestones: string[] = wordsToList(
|
||||
this._options.exemptMilestones
|
||||
);
|
||||
|
||||
this._issueLogger.info(
|
||||
chalk.white('├──'),
|
||||
`The option ${this._issueLogger.createOptionLink(
|
||||
Option.ExemptMilestones
|
||||
)} is set. ${chalk.cyan(exemptMilestones.length)} milestone${
|
||||
exemptMilestones.length === 1 ? '' : 's'
|
||||
} can skip the stale process for this $$type`
|
||||
);
|
||||
|
||||
return exemptMilestones;
|
||||
}
|
||||
|
||||
const exemptMilestones: string[] = wordsToList(
|
||||
this._options.exemptPrMilestones
|
||||
);
|
||||
|
||||
this._issueLogger.info(
|
||||
chalk.white('├──'),
|
||||
`The option ${this._issueLogger.createOptionLink(
|
||||
Option.ExemptPrMilestones
|
||||
)} is set. ${chalk.cyan(exemptMilestones.length)} milestone${
|
||||
exemptMilestones.length === 1 ? '' : 's'
|
||||
} can skip the stale process for this $$type`
|
||||
);
|
||||
|
||||
return exemptMilestones;
|
||||
}
|
||||
|
||||
private _hasMilestone(milestone: Readonly<string>): boolean {
|
||||
@@ -51,9 +195,99 @@ export class Milestones {
|
||||
return false;
|
||||
}
|
||||
|
||||
return (
|
||||
Milestones._cleanMilestone(milestone) ===
|
||||
Milestones._cleanMilestone(this._issue.milestone.title)
|
||||
const cleanMilestone: CleanMilestone = Milestones._cleanMilestone(
|
||||
milestone
|
||||
);
|
||||
|
||||
const isSameMilestone: boolean =
|
||||
cleanMilestone ===
|
||||
Milestones._cleanMilestone(this._issue.milestone.title);
|
||||
|
||||
if (isSameMilestone) {
|
||||
this._issueLogger.info(
|
||||
chalk.white('├──'),
|
||||
`The milestone "${milestone}" is set on this $$type and is an exempt milestone`
|
||||
);
|
||||
}
|
||||
|
||||
return isSameMilestone;
|
||||
}
|
||||
|
||||
private _shouldExemptAllMilestones(): boolean {
|
||||
if (this._issue.milestone) {
|
||||
return this._issue.isPullRequest
|
||||
? this._shouldExemptAllPullRequestMilestones()
|
||||
: this._shouldExemptAllIssueMilestones();
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private _shouldExemptAllIssueMilestones(): boolean {
|
||||
if (this._options.exemptAllIssueMilestones === true) {
|
||||
this._issueLogger.info(
|
||||
`The option ${this._issueLogger.createOptionLink(
|
||||
Option.ExemptAllIssueMilestones
|
||||
)} is enabled. Any milestone on this $$type will skip the stale process`
|
||||
);
|
||||
|
||||
return true;
|
||||
} else if (this._options.exemptAllIssueMilestones === false) {
|
||||
this._issueLogger.info(
|
||||
`The option ${this._issueLogger.createOptionLink(
|
||||
Option.ExemptAllIssueMilestones
|
||||
)} is disabled. Only some specific milestones on this $$type will skip the stale process`
|
||||
);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
this._logExemptAllMilestonesOption();
|
||||
|
||||
return this._options.exemptAllMilestones;
|
||||
}
|
||||
|
||||
private _shouldExemptAllPullRequestMilestones(): boolean {
|
||||
if (this._options.exemptAllPrMilestones === true) {
|
||||
this._issueLogger.info(
|
||||
`The option ${this._issueLogger.createOptionLink(
|
||||
Option.ExemptAllPrMilestones
|
||||
)} is enabled. Any milestone on this $$type will skip the stale process`
|
||||
);
|
||||
|
||||
return true;
|
||||
} else if (this._options.exemptAllPrMilestones === false) {
|
||||
this._issueLogger.info(
|
||||
`The option ${this._issueLogger.createOptionLink(
|
||||
Option.ExemptAllPrMilestones
|
||||
)} is disabled. Only some specific milestones on this $$type will skip the stale process`
|
||||
);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
this._logExemptAllMilestonesOption();
|
||||
|
||||
return this._options.exemptAllMilestones;
|
||||
}
|
||||
|
||||
private _logExemptAllMilestonesOption(): void {
|
||||
if (this._options.exemptAllMilestones) {
|
||||
this._issueLogger.info(
|
||||
`The option ${this._issueLogger.createOptionLink(
|
||||
Option.ExemptAllMilestones
|
||||
)} is enabled. Any milestone on this $$type will skip the stale process`
|
||||
);
|
||||
} else {
|
||||
this._issueLogger.info(
|
||||
`The option ${this._issueLogger.createOptionLink(
|
||||
Option.ExemptAllMilestones
|
||||
)} is disabled. Only some specific milestones on this $$type will skip the stale process`
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
private _logSkip(): void {
|
||||
this._issueLogger.info(chalk.white('└──'), 'Skip the milestones checks');
|
||||
}
|
||||
}
|
||||
|
||||
49
src/classes/operations.spec.ts
Normal file
49
src/classes/operations.spec.ts
Normal file
@@ -0,0 +1,49 @@
|
||||
import {Operations} from './operations';
|
||||
|
||||
describe('Operations', (): void => {
|
||||
let operations: Operations;
|
||||
|
||||
describe('consumeOperation()', (): void => {
|
||||
beforeEach((): void => {
|
||||
operations = new Operations();
|
||||
});
|
||||
|
||||
it('should increase the count of operation consume by 1', (): void => {
|
||||
expect.assertions(1);
|
||||
operations.consumeOperation();
|
||||
|
||||
const result = operations.getConsumedOperationsCount();
|
||||
|
||||
expect(result).toStrictEqual(1);
|
||||
});
|
||||
});
|
||||
|
||||
describe('consumeOperations()', (): void => {
|
||||
beforeEach((): void => {
|
||||
operations = new Operations();
|
||||
});
|
||||
|
||||
it('should increase the count of operation consume by the provided quantity', (): void => {
|
||||
expect.assertions(1);
|
||||
operations.consumeOperations(8);
|
||||
|
||||
const result = operations.getConsumedOperationsCount();
|
||||
|
||||
expect(result).toStrictEqual(8);
|
||||
});
|
||||
});
|
||||
|
||||
describe('getConsumedOperationsCount()', (): void => {
|
||||
beforeEach((): void => {
|
||||
operations = new Operations();
|
||||
});
|
||||
|
||||
it('should return 0 by default', (): void => {
|
||||
expect.assertions(1);
|
||||
|
||||
const result = operations.getConsumedOperationsCount();
|
||||
|
||||
expect(result).toStrictEqual(0);
|
||||
});
|
||||
});
|
||||
});
|
||||
17
src/classes/operations.ts
Normal file
17
src/classes/operations.ts
Normal file
@@ -0,0 +1,17 @@
|
||||
export class Operations {
|
||||
protected _operationsConsumed = 0;
|
||||
|
||||
consumeOperation(): Operations {
|
||||
return this.consumeOperations(1);
|
||||
}
|
||||
|
||||
consumeOperations(quantity: Readonly<number>): Operations {
|
||||
this._operationsConsumed += quantity;
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
getConsumedOperationsCount(): number {
|
||||
return this._operationsConsumed;
|
||||
}
|
||||
}
|
||||
134
src/classes/stale-operations.spec.ts
Normal file
134
src/classes/stale-operations.spec.ts
Normal file
@@ -0,0 +1,134 @@
|
||||
import {DefaultProcessorOptions} from '../../__tests__/constants/default-processor-options';
|
||||
import {IIssuesProcessorOptions} from '../interfaces/issues-processor-options';
|
||||
import {StaleOperations} from './stale-operations';
|
||||
|
||||
interface IHasRemainingOperationsMatrix {
|
||||
operationsPerRun: number;
|
||||
consumeOperations: number;
|
||||
hasRemainingOperations: number;
|
||||
}
|
||||
|
||||
interface IGetRemainingOperationsCountMatrix {
|
||||
operationsPerRun: number;
|
||||
consumeOperations: number;
|
||||
getRemainingOperationsCount: number;
|
||||
}
|
||||
|
||||
describe('StaleOperations', (): void => {
|
||||
let operations: StaleOperations;
|
||||
let options: IIssuesProcessorOptions;
|
||||
|
||||
beforeEach((): void => {
|
||||
options = {...DefaultProcessorOptions};
|
||||
});
|
||||
|
||||
describe('consumeOperation()', (): void => {
|
||||
beforeEach((): void => {
|
||||
operations = new StaleOperations(options);
|
||||
});
|
||||
|
||||
it('should increase the count of operation consume by 1', (): void => {
|
||||
expect.assertions(1);
|
||||
operations.consumeOperation();
|
||||
|
||||
const result = operations.getConsumedOperationsCount();
|
||||
|
||||
expect(result).toStrictEqual(1);
|
||||
});
|
||||
});
|
||||
|
||||
describe('consumeOperations()', (): void => {
|
||||
beforeEach((): void => {
|
||||
operations = new StaleOperations(options);
|
||||
});
|
||||
|
||||
it('should increase the count of operation consume by the provided quantity', (): void => {
|
||||
expect.assertions(1);
|
||||
operations.consumeOperations(8);
|
||||
|
||||
const result = operations.getConsumedOperationsCount();
|
||||
|
||||
expect(result).toStrictEqual(8);
|
||||
});
|
||||
});
|
||||
|
||||
describe('getConsumedOperationsCount()', (): void => {
|
||||
beforeEach((): void => {
|
||||
operations = new StaleOperations(options);
|
||||
});
|
||||
|
||||
it('should return 0 by default', (): void => {
|
||||
expect.assertions(1);
|
||||
|
||||
const result = operations.getConsumedOperationsCount();
|
||||
|
||||
expect(result).toStrictEqual(0);
|
||||
});
|
||||
});
|
||||
|
||||
describe('hasRemainingOperations()', (): void => {
|
||||
beforeEach((): void => {
|
||||
operations = new StaleOperations(options);
|
||||
});
|
||||
|
||||
describe.each`
|
||||
operationsPerRun | consumeOperations | hasRemainingOperations
|
||||
${1} | ${1} | ${false}
|
||||
${2} | ${1} | ${true}
|
||||
`(
|
||||
'when the operations per run is $operationsPerRun and $consumeOperations operations were consumed',
|
||||
({
|
||||
operationsPerRun,
|
||||
consumeOperations,
|
||||
hasRemainingOperations
|
||||
}: IHasRemainingOperationsMatrix): void => {
|
||||
beforeEach((): void => {
|
||||
options.operationsPerRun = operationsPerRun;
|
||||
operations = new StaleOperations(options);
|
||||
});
|
||||
|
||||
it(`should return ${hasRemainingOperations}`, (): void => {
|
||||
expect.assertions(1);
|
||||
operations.consumeOperations(consumeOperations);
|
||||
|
||||
const result = operations.hasRemainingOperations();
|
||||
|
||||
expect(result).toStrictEqual(hasRemainingOperations);
|
||||
});
|
||||
}
|
||||
);
|
||||
});
|
||||
|
||||
describe('getRemainingOperationsCount()', (): void => {
|
||||
beforeEach((): void => {
|
||||
operations = new StaleOperations(options);
|
||||
});
|
||||
|
||||
describe.each`
|
||||
operationsPerRun | consumeOperations | getRemainingOperationsCount
|
||||
${1} | ${1} | ${0}
|
||||
${2} | ${1} | ${1}
|
||||
`(
|
||||
'when the operations per run is $operationsPerRun and $consumeOperations operations were consumed',
|
||||
({
|
||||
operationsPerRun,
|
||||
consumeOperations,
|
||||
getRemainingOperationsCount
|
||||
}: IGetRemainingOperationsCountMatrix): void => {
|
||||
beforeEach((): void => {
|
||||
options.operationsPerRun = operationsPerRun;
|
||||
operations = new StaleOperations(options);
|
||||
});
|
||||
|
||||
it(`should return ${getRemainingOperationsCount}`, (): void => {
|
||||
expect.assertions(1);
|
||||
operations.consumeOperations(consumeOperations);
|
||||
|
||||
const result = operations.getRemainingOperationsCount();
|
||||
|
||||
expect(result).toStrictEqual(getRemainingOperationsCount);
|
||||
});
|
||||
}
|
||||
);
|
||||
});
|
||||
});
|
||||
19
src/classes/stale-operations.ts
Normal file
19
src/classes/stale-operations.ts
Normal file
@@ -0,0 +1,19 @@
|
||||
import {IIssuesProcessorOptions} from '../interfaces/issues-processor-options';
|
||||
import {Operations} from './operations';
|
||||
|
||||
export class StaleOperations extends Operations {
|
||||
private readonly _options: IIssuesProcessorOptions;
|
||||
|
||||
constructor(options: Readonly<IIssuesProcessorOptions>) {
|
||||
super();
|
||||
this._options = options;
|
||||
}
|
||||
|
||||
hasRemainingOperations(): boolean {
|
||||
return this._operationsConsumed < this._options.operationsPerRun;
|
||||
}
|
||||
|
||||
getRemainingOperationsCount(): number {
|
||||
return this._options.operationsPerRun - this._operationsConsumed;
|
||||
}
|
||||
}
|
||||
520
src/classes/statistics.ts
Normal file
520
src/classes/statistics.ts
Normal file
@@ -0,0 +1,520 @@
|
||||
import chalk from 'chalk';
|
||||
import {Issue} from './issue';
|
||||
import {Logger} from './loggers/logger';
|
||||
|
||||
interface IGroupValue {
|
||||
name: string;
|
||||
count: number;
|
||||
}
|
||||
|
||||
export class Statistics {
|
||||
private readonly _logger: Logger = new Logger();
|
||||
private _processedIssuesCount = 0;
|
||||
private _processedPullRequestsCount = 0;
|
||||
private _staleIssuesCount = 0;
|
||||
private _stalePullRequestsCount = 0;
|
||||
private _undoStaleIssuesCount = 0;
|
||||
private _undoStalePullRequestsCount = 0;
|
||||
private _operationsCount = 0;
|
||||
private _closedIssuesCount = 0;
|
||||
private _closedPullRequestsCount = 0;
|
||||
private _deletedIssuesLabelsCount = 0;
|
||||
private _deletedPullRequestsLabelsCount = 0;
|
||||
private _deletedCloseIssuesLabelsCount = 0;
|
||||
private _deletedClosePullRequestsLabelsCount = 0;
|
||||
private _deletedBranchesCount = 0;
|
||||
private _addedIssuesLabelsCount = 0;
|
||||
private _addedPullRequestsLabelsCount = 0;
|
||||
private _addedIssuesCommentsCount = 0;
|
||||
private _addedPullRequestsCommentsCount = 0;
|
||||
private _fetchedItemsCount = 0;
|
||||
private _fetchedItemsEventsCount = 0;
|
||||
private _fetchedItemsCommentsCount = 0;
|
||||
private _fetchedPullRequestsCount = 0;
|
||||
|
||||
incrementProcessedItemsCount(
|
||||
issue: Readonly<Issue>,
|
||||
increment: Readonly<number> = 1
|
||||
): Statistics {
|
||||
if (issue.isPullRequest) {
|
||||
return this._incrementProcessedPullRequestsCount(increment);
|
||||
}
|
||||
|
||||
return this._incrementProcessedIssuesCount(increment);
|
||||
}
|
||||
|
||||
incrementStaleItemsCount(
|
||||
issue: Readonly<Issue>,
|
||||
increment: Readonly<number> = 1
|
||||
): Statistics {
|
||||
if (issue.isPullRequest) {
|
||||
return this._incrementStalePullRequestsCount(increment);
|
||||
}
|
||||
|
||||
return this._incrementStaleIssuesCount(increment);
|
||||
}
|
||||
|
||||
incrementUndoStaleItemsCount(
|
||||
issue: Readonly<Issue>,
|
||||
increment: Readonly<number> = 1
|
||||
): Statistics {
|
||||
if (issue.isPullRequest) {
|
||||
return this._incrementUndoStalePullRequestsCount(increment);
|
||||
}
|
||||
|
||||
return this._incrementUndoStaleIssuesCount(increment);
|
||||
}
|
||||
|
||||
setRemainingOperations(remainingOperations: Readonly<number>): Statistics {
|
||||
this._operationsCount = remainingOperations;
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
incrementClosedItemsCount(
|
||||
issue: Readonly<Issue>,
|
||||
increment: Readonly<number> = 1
|
||||
): Statistics {
|
||||
if (issue.isPullRequest) {
|
||||
return this._incrementClosedPullRequestsCount(increment);
|
||||
}
|
||||
|
||||
return this._incrementClosedIssuesCount(increment);
|
||||
}
|
||||
|
||||
incrementDeletedItemsLabelsCount(
|
||||
issue: Readonly<Issue>,
|
||||
increment: Readonly<number> = 1
|
||||
): Statistics {
|
||||
if (issue.isPullRequest) {
|
||||
return this._incrementDeletedPullRequestsLabelsCount(increment);
|
||||
}
|
||||
|
||||
return this._incrementDeletedIssuesLabelsCount(increment);
|
||||
}
|
||||
|
||||
incrementDeletedCloseItemsLabelsCount(
|
||||
issue: Readonly<Issue>,
|
||||
increment: Readonly<number> = 1
|
||||
): Statistics {
|
||||
if (issue.isPullRequest) {
|
||||
return this._incrementDeletedClosePullRequestsLabelsCount(increment);
|
||||
}
|
||||
|
||||
return this._incrementDeletedCloseIssuesLabelsCount(increment);
|
||||
}
|
||||
|
||||
incrementDeletedBranchesCount(increment: Readonly<number> = 1): Statistics {
|
||||
this._deletedBranchesCount += increment;
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
incrementAddedItemsLabel(
|
||||
issue: Readonly<Issue>,
|
||||
increment: Readonly<number> = 1
|
||||
): Statistics {
|
||||
if (issue.isPullRequest) {
|
||||
return this._incrementAddedPullRequestsLabel(increment);
|
||||
}
|
||||
|
||||
return this._incrementAddedIssuesLabel(increment);
|
||||
}
|
||||
|
||||
incrementAddedItemsComment(
|
||||
issue: Readonly<Issue>,
|
||||
increment: Readonly<number> = 1
|
||||
): Statistics {
|
||||
if (issue.isPullRequest) {
|
||||
return this._incrementAddedPullRequestsComment(increment);
|
||||
}
|
||||
|
||||
return this._incrementAddedIssuesComment(increment);
|
||||
}
|
||||
|
||||
incrementFetchedItemsCount(increment: Readonly<number> = 1): Statistics {
|
||||
this._fetchedItemsCount += increment;
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
incrementFetchedItemsEventsCount(
|
||||
increment: Readonly<number> = 1
|
||||
): Statistics {
|
||||
this._fetchedItemsEventsCount += increment;
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
incrementFetchedItemsCommentsCount(
|
||||
increment: Readonly<number> = 1
|
||||
): Statistics {
|
||||
this._fetchedItemsCommentsCount += increment;
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
incrementFetchedPullRequestsCount(
|
||||
increment: Readonly<number> = 1
|
||||
): Statistics {
|
||||
this._fetchedPullRequestsCount += increment;
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
logStats(): Statistics {
|
||||
this._logger.info(chalk.yellow.bold('Statistics:'));
|
||||
this._logProcessedIssuesAndPullRequestsCount();
|
||||
this._logStaleIssuesAndPullRequestsCount();
|
||||
this._logUndoStaleIssuesAndPullRequestsCount();
|
||||
this._logClosedIssuesAndPullRequestsCount();
|
||||
this._logDeletedIssuesAndPullRequestsLabelsCount();
|
||||
this._logDeletedCloseIssuesAndPullRequestsLabelsCount();
|
||||
this._logDeletedBranchesCount();
|
||||
this._logAddedIssuesAndPullRequestsLabelsCount();
|
||||
this._logAddedIssuesAndPullRequestsCommentsCount();
|
||||
this._logFetchedItemsCount();
|
||||
this._logFetchedItemsEventsCount();
|
||||
this._logFetchedItemsCommentsCount();
|
||||
this._logFetchedPullRequestsCount();
|
||||
this._logOperationsCount();
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
private _incrementProcessedIssuesCount(
|
||||
increment: Readonly<number> = 1
|
||||
): Statistics {
|
||||
this._processedIssuesCount += increment;
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
private _incrementProcessedPullRequestsCount(
|
||||
increment: Readonly<number> = 1
|
||||
): Statistics {
|
||||
this._processedPullRequestsCount += increment;
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
private _incrementStaleIssuesCount(
|
||||
increment: Readonly<number> = 1
|
||||
): Statistics {
|
||||
this._staleIssuesCount += increment;
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
private _incrementStalePullRequestsCount(
|
||||
increment: Readonly<number> = 1
|
||||
): Statistics {
|
||||
this._stalePullRequestsCount += increment;
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
private _incrementUndoStaleIssuesCount(
|
||||
increment: Readonly<number> = 1
|
||||
): Statistics {
|
||||
this._undoStaleIssuesCount += increment;
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
private _incrementUndoStalePullRequestsCount(
|
||||
increment: Readonly<number> = 1
|
||||
): Statistics {
|
||||
this._undoStalePullRequestsCount += increment;
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
private _incrementClosedIssuesCount(
|
||||
increment: Readonly<number> = 1
|
||||
): Statistics {
|
||||
this._closedIssuesCount += increment;
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
private _incrementClosedPullRequestsCount(
|
||||
increment: Readonly<number> = 1
|
||||
): Statistics {
|
||||
this._closedPullRequestsCount += increment;
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
private _incrementDeletedIssuesLabelsCount(
|
||||
increment: Readonly<number> = 1
|
||||
): Statistics {
|
||||
this._deletedIssuesLabelsCount += increment;
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
private _incrementDeletedPullRequestsLabelsCount(
|
||||
increment: Readonly<number> = 1
|
||||
): Statistics {
|
||||
this._deletedPullRequestsLabelsCount += increment;
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
private _incrementDeletedCloseIssuesLabelsCount(
|
||||
increment: Readonly<number> = 1
|
||||
): Statistics {
|
||||
this._deletedCloseIssuesLabelsCount += increment;
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
private _incrementDeletedClosePullRequestsLabelsCount(
|
||||
increment: Readonly<number> = 1
|
||||
): Statistics {
|
||||
this._deletedClosePullRequestsLabelsCount += increment;
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
private _incrementAddedIssuesLabel(
|
||||
increment: Readonly<number> = 1
|
||||
): Statistics {
|
||||
this._addedIssuesLabelsCount += increment;
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
private _incrementAddedPullRequestsLabel(
|
||||
increment: Readonly<number> = 1
|
||||
): Statistics {
|
||||
this._addedPullRequestsLabelsCount += increment;
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
private _incrementAddedIssuesComment(
|
||||
increment: Readonly<number> = 1
|
||||
): Statistics {
|
||||
this._addedIssuesCommentsCount += increment;
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
private _incrementAddedPullRequestsComment(
|
||||
increment: Readonly<number> = 1
|
||||
): Statistics {
|
||||
this._addedPullRequestsCommentsCount += increment;
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
private _logProcessedIssuesAndPullRequestsCount(): void {
|
||||
this._logGroup('Processed items', [
|
||||
{
|
||||
name: 'Processed issues',
|
||||
count: this._processedIssuesCount
|
||||
},
|
||||
{
|
||||
name: 'Processed PRs',
|
||||
count: this._processedPullRequestsCount
|
||||
}
|
||||
]);
|
||||
}
|
||||
|
||||
private _logStaleIssuesAndPullRequestsCount(): void {
|
||||
this._logGroup('New stale items', [
|
||||
{
|
||||
name: 'New stale issues',
|
||||
count: this._staleIssuesCount
|
||||
},
|
||||
{
|
||||
name: 'New stale PRs',
|
||||
count: this._stalePullRequestsCount
|
||||
}
|
||||
]);
|
||||
}
|
||||
|
||||
private _logUndoStaleIssuesAndPullRequestsCount(): void {
|
||||
this._logGroup('No longer stale items', [
|
||||
{
|
||||
name: 'No longer stale issues',
|
||||
count: this._undoStaleIssuesCount
|
||||
},
|
||||
{
|
||||
name: 'No longer stale PRs',
|
||||
count: this._undoStalePullRequestsCount
|
||||
}
|
||||
]);
|
||||
}
|
||||
|
||||
private _logClosedIssuesAndPullRequestsCount(): void {
|
||||
this._logGroup('Closed items', [
|
||||
{
|
||||
name: 'Closed issues',
|
||||
count: this._closedIssuesCount
|
||||
},
|
||||
{
|
||||
name: 'Closed PRs',
|
||||
count: this._closedPullRequestsCount
|
||||
}
|
||||
]);
|
||||
}
|
||||
|
||||
private _logDeletedIssuesAndPullRequestsLabelsCount(): void {
|
||||
this._logGroup('Deleted items labels', [
|
||||
{
|
||||
name: 'Deleted issues labels',
|
||||
count: this._deletedIssuesLabelsCount
|
||||
},
|
||||
{
|
||||
name: 'Deleted PRs labels',
|
||||
count: this._deletedPullRequestsLabelsCount
|
||||
}
|
||||
]);
|
||||
}
|
||||
|
||||
private _logDeletedCloseIssuesAndPullRequestsLabelsCount(): void {
|
||||
this._logGroup('Deleted close items labels', [
|
||||
{
|
||||
name: 'Deleted close issues labels',
|
||||
count: this._deletedCloseIssuesLabelsCount
|
||||
},
|
||||
{
|
||||
name: 'Deleted close PRs labels',
|
||||
count: this._deletedClosePullRequestsLabelsCount
|
||||
}
|
||||
]);
|
||||
}
|
||||
|
||||
private _logDeletedBranchesCount(): void {
|
||||
this._logCount('Deleted branches', this._deletedBranchesCount);
|
||||
}
|
||||
|
||||
private _logAddedIssuesAndPullRequestsLabelsCount(): void {
|
||||
this._logGroup('Added items labels', [
|
||||
{
|
||||
name: 'Added issues labels',
|
||||
count: this._addedIssuesLabelsCount
|
||||
},
|
||||
{
|
||||
name: 'Added PRs labels',
|
||||
count: this._addedPullRequestsLabelsCount
|
||||
}
|
||||
]);
|
||||
}
|
||||
|
||||
private _logAddedIssuesAndPullRequestsCommentsCount(): void {
|
||||
this._logGroup('Added items comments', [
|
||||
{
|
||||
name: 'Added issues comments',
|
||||
count: this._addedIssuesCommentsCount
|
||||
},
|
||||
{
|
||||
name: 'Added PRs comments',
|
||||
count: this._addedPullRequestsCommentsCount
|
||||
}
|
||||
]);
|
||||
}
|
||||
|
||||
private _logFetchedItemsCount(): void {
|
||||
this._logCount('Fetched items', this._fetchedItemsCount);
|
||||
}
|
||||
|
||||
private _logFetchedItemsEventsCount(): void {
|
||||
this._logCount('Fetched items events', this._fetchedItemsEventsCount);
|
||||
}
|
||||
|
||||
private _logFetchedItemsCommentsCount(): void {
|
||||
this._logCount('Fetched items comments', this._fetchedItemsCommentsCount);
|
||||
}
|
||||
|
||||
private _logFetchedPullRequestsCount(): void {
|
||||
this._logCount('Fetched pull requests', this._fetchedPullRequestsCount);
|
||||
}
|
||||
|
||||
private _logOperationsCount(): void {
|
||||
this._logCount('Operations performed', this._operationsCount);
|
||||
}
|
||||
|
||||
private _logCount(name: Readonly<string>, count: Readonly<number>): void {
|
||||
if (count > 0) {
|
||||
this._logger.info(`${name}:`, chalk.cyan(count));
|
||||
}
|
||||
}
|
||||
|
||||
private _logGroup(groupName: Readonly<string>, values: IGroupValue[]): void {
|
||||
if (this._isGroupValuesPartiallySet(values)) {
|
||||
this._logCount(groupName, this._getGroupValuesTotalCount(values));
|
||||
|
||||
this._logGroupValues(values);
|
||||
} else {
|
||||
// Only one value will be display
|
||||
for (const value of values) {
|
||||
this._logCount(value.name, value.count);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
* @description
|
||||
* If there is a least two elements with a valid count then it's partially set
|
||||
* Useful to defined if we should display the values as a group or not
|
||||
*
|
||||
* @param {IGroupValue[]} values The list of group values to check
|
||||
*/
|
||||
private _isGroupValuesPartiallySet(values: IGroupValue[]): boolean {
|
||||
return (
|
||||
values
|
||||
.map((value: Readonly<IGroupValue>): boolean => {
|
||||
return value.count > 0;
|
||||
})
|
||||
.filter((isSet: Readonly<boolean>): boolean => isSet).length >= 2
|
||||
);
|
||||
}
|
||||
|
||||
private _getGroupValuesTotalCount(values: IGroupValue[]): number {
|
||||
return values.reduce(
|
||||
(count: Readonly<number>, value: Readonly<IGroupValue>): number => {
|
||||
return count + value.count;
|
||||
},
|
||||
0
|
||||
);
|
||||
}
|
||||
|
||||
private _getAllGroupValuesSet(values: IGroupValue[]): IGroupValue[] {
|
||||
return values.filter((value: Readonly<IGroupValue>): boolean => {
|
||||
return value.count > 0;
|
||||
});
|
||||
}
|
||||
|
||||
private _logGroupValues(values: IGroupValue[]): void {
|
||||
const onlyValuesSet: IGroupValue[] = this._getAllGroupValuesSet(values);
|
||||
const longestValue: number = this._getLongestGroupValue(onlyValuesSet);
|
||||
|
||||
for (const [index, value] of onlyValuesSet.entries()) {
|
||||
const prefix = index === onlyValuesSet.length - 1 ? '└──' : '├──';
|
||||
|
||||
this._logCount(
|
||||
`${chalk.white(prefix)} ${value.name.padEnd(longestValue, ' ')}`,
|
||||
value.count
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
private _getLongestGroupValue(values: IGroupValue[]): number {
|
||||
return values.reduce(
|
||||
(
|
||||
longestValue: Readonly<number>,
|
||||
value: Readonly<IGroupValue>
|
||||
): number => {
|
||||
return value.name.length > longestValue
|
||||
? value.name.length
|
||||
: longestValue;
|
||||
},
|
||||
0
|
||||
);
|
||||
}
|
||||
}
|
||||
44
src/enums/option.ts
Normal file
44
src/enums/option.ts
Normal file
@@ -0,0 +1,44 @@
|
||||
export enum Option {
|
||||
RepoToken = 'repo-token',
|
||||
StaleIssueMessage = 'stale-issue-message',
|
||||
StalePrMessage = 'stale-pr-message',
|
||||
CloseIssueMessage = 'close-issue-message',
|
||||
ClosePrMessage = 'close-pr-message',
|
||||
DaysBeforeStale = 'days-before-stale',
|
||||
DaysBeforeIssueStale = 'days-before-issue-stale',
|
||||
DaysBeforePrStale = 'days-before-pr-stale',
|
||||
DaysBeforeClose = 'days-before-close',
|
||||
DaysBeforeIssueClose = 'days-before-issue-close',
|
||||
DaysBeforePrClose = 'days-before-pr-close',
|
||||
StaleIssueLabel = 'stale-issue-label',
|
||||
CloseIssueLabel = 'close-issue-label',
|
||||
ExemptIssueLabels = 'exempt-issue-labels',
|
||||
StalePrLabel = 'stale-pr-label',
|
||||
ClosePrLabel = 'close-pr-label',
|
||||
ExemptPrLabels = 'exempt-pr-labels',
|
||||
OnlyLabels = 'only-labels',
|
||||
OnlyIssueLabels = 'only-issue-labels',
|
||||
OnlyPrLabels = 'only-pr-labels',
|
||||
AnyOfLabels = 'any-of-labels',
|
||||
OperationsPerRun = 'operations-per-run',
|
||||
RemoveStaleWhenUpdated = 'remove-stale-when-updated',
|
||||
DebugOnly = 'debug-only',
|
||||
Ascending = 'ascending',
|
||||
SkipStaleIssueMessage = 'skip-stale-issue-message',
|
||||
SkipStalePrMessage = 'skip-stale-pr-message',
|
||||
DeleteBranch = 'delete-branch',
|
||||
StartDate = 'start-date',
|
||||
ExemptMilestones = 'exempt-milestones',
|
||||
ExemptIssueMilestones = 'exempt-issue-milestones',
|
||||
ExemptPrMilestones = 'exempt-pr-milestones',
|
||||
ExemptAllMilestones = 'exempt-all-milestones',
|
||||
ExemptAllIssueMilestones = 'exempt-all-issue-milestones',
|
||||
ExemptAllPrMilestones = 'exempt-all-pr-milestones',
|
||||
ExemptAssignees = 'exempt-assignees',
|
||||
ExemptIssueAssignees = 'exempt-issue-assignees',
|
||||
ExemptPrAssignees = 'exempt-pr-assignees',
|
||||
ExemptAllAssignees = 'exempt-all-assignees',
|
||||
ExemptAllIssueAssignees = 'exempt-all-issue-assignees',
|
||||
ExemptAllPrAssignees = 'exempt-all-pr-assignees',
|
||||
EnableStatistics = 'enable-statistics'
|
||||
}
|
||||
@@ -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');
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -1,5 +0,0 @@
|
||||
import {IssueType} from '../enums/issue-type';
|
||||
|
||||
export function getIssueType(isPullRequest: Readonly<boolean>): IssueType {
|
||||
return isPullRequest ? IssueType.PullRequest : IssueType.Issue;
|
||||
}
|
||||
29
src/functions/is-boolean.spec.ts
Normal file
29
src/functions/is-boolean.spec.ts
Normal file
@@ -0,0 +1,29 @@
|
||||
import {isBoolean} from './is-boolean';
|
||||
|
||||
describe('isBoolean()', (): void => {
|
||||
describe.each([0, 1, undefined, null, ''])(
|
||||
'when the given value is not a boolean',
|
||||
(value): void => {
|
||||
it('should return false', (): void => {
|
||||
expect.assertions(1);
|
||||
|
||||
const result = isBoolean(value);
|
||||
|
||||
expect(result).toStrictEqual(false);
|
||||
});
|
||||
}
|
||||
);
|
||||
|
||||
describe.each([false, true])(
|
||||
'when the given value is a boolean',
|
||||
(value): void => {
|
||||
it('should return true', (): void => {
|
||||
expect.assertions(1);
|
||||
|
||||
const result = isBoolean(value);
|
||||
|
||||
expect(result).toStrictEqual(true);
|
||||
});
|
||||
}
|
||||
);
|
||||
});
|
||||
3
src/functions/is-boolean.ts
Normal file
3
src/functions/is-boolean.ts
Normal file
@@ -0,0 +1,3 @@
|
||||
export function isBoolean(value: unknown): value is boolean {
|
||||
return value === true || value === false;
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
import deburr from 'lodash.deburr';
|
||||
import {Issue} from '../classes/issue';
|
||||
import {Label} from '../IssueProcessor';
|
||||
import {ILabel} from '../interfaces/label';
|
||||
import {CleanLabel} from '../types/clean-label';
|
||||
|
||||
/**
|
||||
@@ -16,7 +16,7 @@ export function isLabeled(
|
||||
issue: Readonly<Issue>,
|
||||
label: Readonly<string>
|
||||
): boolean {
|
||||
return !!issue.labels.find((issueLabel: Readonly<Label>): boolean => {
|
||||
return !!issue.labels.find((issueLabel: Readonly<ILabel>): boolean => {
|
||||
return cleanLabel(label) === cleanLabel(issueLabel.name);
|
||||
});
|
||||
}
|
||||
|
||||
3
src/interfaces/assignee.ts
Normal file
3
src/interfaces/assignee.ts
Normal file
@@ -0,0 +1,3 @@
|
||||
export interface IAssignee {
|
||||
login: string;
|
||||
}
|
||||
5
src/interfaces/comment.ts
Normal file
5
src/interfaces/comment.ts
Normal file
@@ -0,0 +1,5 @@
|
||||
import {IUser} from './user';
|
||||
|
||||
export interface IComment {
|
||||
user: IUser;
|
||||
}
|
||||
7
src/interfaces/issue-event.ts
Normal file
7
src/interfaces/issue-event.ts
Normal file
@@ -0,0 +1,7 @@
|
||||
import {ILabel} from './label';
|
||||
|
||||
export interface IIssueEvent {
|
||||
created_at: string;
|
||||
event: string;
|
||||
label: ILabel;
|
||||
}
|
||||
@@ -1,5 +1,6 @@
|
||||
import {Label} from '../IssueProcessor';
|
||||
import {IsoDateString} from '../types/iso-date-string';
|
||||
import {IAssignee} from './assignee';
|
||||
import {ILabel} from './label';
|
||||
import {IMilestone} from './milestone';
|
||||
|
||||
export interface IIssue {
|
||||
@@ -7,9 +8,10 @@ export interface IIssue {
|
||||
number: number;
|
||||
created_at: IsoDateString;
|
||||
updated_at: IsoDateString;
|
||||
labels: Label[];
|
||||
labels: ILabel[];
|
||||
pull_request: Object | null | undefined;
|
||||
state: string;
|
||||
locked: boolean;
|
||||
milestone: IMilestone | undefined;
|
||||
assignees: IAssignee[];
|
||||
}
|
||||
|
||||
50
src/interfaces/issues-processor-options.ts
Normal file
50
src/interfaces/issues-processor-options.ts
Normal file
@@ -0,0 +1,50 @@
|
||||
import {IsoOrRfcDateString} from '../types/iso-or-rfc-date-string';
|
||||
|
||||
export interface IIssuesProcessorOptions {
|
||||
repoToken: string;
|
||||
staleIssueMessage: string;
|
||||
stalePrMessage: string;
|
||||
closeIssueMessage: string;
|
||||
closePrMessage: string;
|
||||
daysBeforeStale: number;
|
||||
daysBeforeIssueStale: number; // Could be NaN
|
||||
daysBeforePrStale: number; // Could be NaN
|
||||
daysBeforeClose: number;
|
||||
daysBeforeIssueClose: number; // Could be NaN
|
||||
daysBeforePrClose: number; // Could be NaN
|
||||
staleIssueLabel: string;
|
||||
closeIssueLabel: string;
|
||||
exemptIssueLabels: string;
|
||||
stalePrLabel: string;
|
||||
closePrLabel: string;
|
||||
exemptPrLabels: string;
|
||||
onlyLabels: string;
|
||||
onlyIssueLabels: string;
|
||||
onlyPrLabels: string;
|
||||
anyOfLabels: string;
|
||||
anyOfIssueLabels: string;
|
||||
anyOfPrLabels: string;
|
||||
operationsPerRun: number;
|
||||
removeStaleWhenUpdated: boolean;
|
||||
removeIssueStaleWhenUpdated: boolean | undefined;
|
||||
removePrStaleWhenUpdated: boolean | undefined;
|
||||
debugOnly: boolean;
|
||||
ascending: boolean;
|
||||
skipStaleIssueMessage: boolean;
|
||||
skipStalePrMessage: boolean;
|
||||
deleteBranch: boolean;
|
||||
startDate: IsoOrRfcDateString | undefined; // Should be ISO 8601 or RFC 2822
|
||||
exemptMilestones: string;
|
||||
exemptIssueMilestones: string;
|
||||
exemptPrMilestones: string;
|
||||
exemptAllMilestones: boolean;
|
||||
exemptAllIssueMilestones: boolean | undefined;
|
||||
exemptAllPrMilestones: boolean | undefined;
|
||||
exemptAssignees: string;
|
||||
exemptIssueAssignees: string;
|
||||
exemptPrAssignees: string;
|
||||
exemptAllAssignees: boolean;
|
||||
exemptAllIssueAssignees: boolean | undefined;
|
||||
exemptAllPrAssignees: boolean | undefined;
|
||||
enableStatistics: boolean;
|
||||
}
|
||||
3
src/interfaces/label.ts
Normal file
3
src/interfaces/label.ts
Normal file
@@ -0,0 +1,3 @@
|
||||
export interface ILabel {
|
||||
name: string;
|
||||
}
|
||||
6
src/interfaces/pull-request.ts
Normal file
6
src/interfaces/pull-request.ts
Normal file
@@ -0,0 +1,6 @@
|
||||
export interface IPullRequest {
|
||||
number: number;
|
||||
head: {
|
||||
ref: string;
|
||||
};
|
||||
}
|
||||
4
src/interfaces/user.ts
Normal file
4
src/interfaces/user.ts
Normal file
@@ -0,0 +1,4 @@
|
||||
export interface IUser {
|
||||
type: string | 'User';
|
||||
login: string;
|
||||
}
|
||||
63
src/main.ts
63
src/main.ts
@@ -1,21 +1,21 @@
|
||||
import * as core from '@actions/core';
|
||||
import {IssuesProcessor} from './classes/issues-processor';
|
||||
import {isValidDate} from './functions/dates/is-valid-date';
|
||||
import {IssueProcessor, IssueProcessorOptions} from './IssueProcessor';
|
||||
import {IIssuesProcessorOptions} from './interfaces/issues-processor-options';
|
||||
|
||||
async function run(): Promise<void> {
|
||||
async function _run(): Promise<void> {
|
||||
try {
|
||||
const args = getAndValidateArgs();
|
||||
const args = _getAndValidateArgs();
|
||||
|
||||
const processor: IssueProcessor = new IssueProcessor(args);
|
||||
await processor.processIssues();
|
||||
await new IssuesProcessor(args).processIssues();
|
||||
} catch (error) {
|
||||
core.error(error);
|
||||
core.setFailed(error.message);
|
||||
}
|
||||
}
|
||||
|
||||
function getAndValidateArgs(): IssueProcessorOptions {
|
||||
const args: IssueProcessorOptions = {
|
||||
function _getAndValidateArgs(): IIssuesProcessorOptions {
|
||||
const args: IIssuesProcessorOptions = {
|
||||
repoToken: core.getInput('repo-token'),
|
||||
staleIssueMessage: core.getInput('stale-issue-message'),
|
||||
stalePrMessage: core.getInput('stale-pr-message'),
|
||||
@@ -38,12 +38,23 @@ function getAndValidateArgs(): IssueProcessorOptions {
|
||||
closePrLabel: core.getInput('close-pr-label'),
|
||||
exemptPrLabels: core.getInput('exempt-pr-labels'),
|
||||
onlyLabels: core.getInput('only-labels'),
|
||||
onlyIssueLabels: core.getInput('only-issue-labels'),
|
||||
onlyPrLabels: core.getInput('only-pr-labels'),
|
||||
anyOfLabels: core.getInput('any-of-labels'),
|
||||
anyOfIssueLabels: core.getInput('any-of-issue-labels'),
|
||||
anyOfPrLabels: core.getInput('any-of-pr-labels'),
|
||||
operationsPerRun: parseInt(
|
||||
core.getInput('operations-per-run', {required: true})
|
||||
),
|
||||
removeStaleWhenUpdated: !(
|
||||
core.getInput('remove-stale-when-updated') === 'false'
|
||||
),
|
||||
removeIssueStaleWhenUpdated: _toOptionalBoolean(
|
||||
core.getInput('remove-issue-stale-when-updated')
|
||||
),
|
||||
removePrStaleWhenUpdated: _toOptionalBoolean(
|
||||
core.getInput('remove-pr-stale-when-updated')
|
||||
),
|
||||
debugOnly: core.getInput('debug-only') === 'true',
|
||||
ascending: core.getInput('ascending') === 'true',
|
||||
skipStalePrMessage: core.getInput('skip-stale-pr-message') === 'true',
|
||||
@@ -55,7 +66,17 @@ function getAndValidateArgs(): IssueProcessorOptions {
|
||||
: undefined,
|
||||
exemptMilestones: core.getInput('exempt-milestones'),
|
||||
exemptIssueMilestones: core.getInput('exempt-issue-milestones'),
|
||||
exemptPrMilestones: core.getInput('exempt-pr-milestones')
|
||||
exemptPrMilestones: core.getInput('exempt-pr-milestones'),
|
||||
exemptAllMilestones: core.getInput('exempt-all-milestones') === 'true',
|
||||
exemptAllIssueMilestones: _toOptionalBoolean('exempt-all-issue-milestones'),
|
||||
exemptAllPrMilestones: _toOptionalBoolean('exempt-all-pr-milestones'),
|
||||
exemptAssignees: core.getInput('exempt-assignees'),
|
||||
exemptIssueAssignees: core.getInput('exempt-issue-assignees'),
|
||||
exemptPrAssignees: core.getInput('exempt-pr-assignees'),
|
||||
exemptAllAssignees: core.getInput('exempt-all-assignees') === 'true',
|
||||
exemptAllIssueAssignees: _toOptionalBoolean('exempt-all-issue-assignees'),
|
||||
exemptAllPrAssignees: _toOptionalBoolean('exempt-all-pr-assignees'),
|
||||
enableStatistics: core.getInput('enable-statistics') === 'true'
|
||||
};
|
||||
|
||||
for (const numberInput of [
|
||||
@@ -64,7 +85,9 @@ function getAndValidateArgs(): IssueProcessorOptions {
|
||||
'operations-per-run'
|
||||
]) {
|
||||
if (isNaN(parseInt(core.getInput(numberInput)))) {
|
||||
throw Error(`input ${numberInput} did not parse to a valid integer`);
|
||||
const errorMessage = `Option "${numberInput}" did not parse to a valid integer`;
|
||||
core.setFailed(errorMessage);
|
||||
throw new Error(errorMessage);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -72,9 +95,9 @@ function getAndValidateArgs(): IssueProcessorOptions {
|
||||
// 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`
|
||||
);
|
||||
const errorMessage = `Option "${optionalDateInput}" did not parse to a valid date`;
|
||||
core.setFailed(errorMessage);
|
||||
throw new Error(errorMessage);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -82,4 +105,18 @@ function getAndValidateArgs(): IssueProcessorOptions {
|
||||
return args;
|
||||
}
|
||||
|
||||
run();
|
||||
function _toOptionalBoolean(
|
||||
argumentName: Readonly<string>
|
||||
): boolean | undefined {
|
||||
const argument: string = core.getInput(argumentName);
|
||||
|
||||
if (argument === 'true') {
|
||||
return true;
|
||||
} else if (argument === 'false') {
|
||||
return false;
|
||||
}
|
||||
|
||||
return undefined;
|
||||
}
|
||||
|
||||
void _run();
|
||||
|
||||
5
tsconfig.app.json
Normal file
5
tsconfig.app.json
Normal file
@@ -0,0 +1,5 @@
|
||||
{
|
||||
"extends": "./tsconfig.json",
|
||||
"exclude": ["node_modules", "**/*.spec.ts"],
|
||||
"include": ["src"]
|
||||
}
|
||||
@@ -3,11 +3,10 @@
|
||||
"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'. */,
|
||||
"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. */,
|
||||
"strict": true /* Enable all strict type-checking options. */,
|
||||
"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'. */
|
||||
//"sourceMap": true
|
||||
},
|
||||
"exclude": ["node_modules", "**/*.test.ts"]
|
||||
"include": ["src", "__tests__"]
|
||||
}
|
||||
|
||||
5
tsconfig.spec.json
Normal file
5
tsconfig.spec.json
Normal file
@@ -0,0 +1,5 @@
|
||||
{
|
||||
"extends": "./tsconfig.json",
|
||||
"include": ["src", "__tests__"],
|
||||
"exclude": ["node_modules"]
|
||||
}
|
||||
Reference in New Issue
Block a user