mirror of
https://github.com/actions/stale.git
synced 2025-12-24 17:38:17 +00:00
Compare commits
21 Commits
issue-comm
...
v3.0.19
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
98ed4cb500 | ||
|
|
523075947f | ||
|
|
5a36bdc457 | ||
|
|
4a6aba205d | ||
|
|
2a6d5d6990 | ||
|
|
ebd757c01b | ||
|
|
1ebe9f7454 | ||
|
|
a61d498059 | ||
|
|
0a21a5ec79 | ||
|
|
e7199f9c84 | ||
|
|
e307118008 | ||
|
|
b53e7be65a | ||
|
|
ff764c226b | ||
|
|
c11507e9b7 | ||
|
|
440fb174b5 | ||
|
|
043fbbdea3 | ||
|
|
c70e174d4a | ||
|
|
704929ea5a | ||
|
|
5e20aa8410 | ||
|
|
10eec4583b | ||
|
|
b717aa9f47 |
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
|
||||
|
||||
125
README.md
125
README.md
@@ -2,54 +2,85 @@
|
||||
|
||||
Warns and then closes issues and PRs that have had no activity for a specified amount of time.
|
||||
|
||||
### Arguments
|
||||
## All options
|
||||
|
||||
### List of options
|
||||
|
||||
Every argument is optional.
|
||||
|
||||
| Input | Description |
|
||||
| ----------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| `repo-token` | PAT(Personal Access Token) for authorizing 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 an PR as stale.<br>_Override `days-before-stale`_. |
|
||||
| `days-before-close` | Idle number of days before closing an stale issue/PR.<br>_Defaults to **7**_. |
|
||||
| `days-before-issue-close` | Idle number of days before closing an stale issue.<br>_Override `days-before-close`_. |
|
||||
| `days-before-pr-close` | Idle number of days before closing an 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 (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 (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. Separate multiple labels with commas (eg. "question,answered"). |
|
||||
| `only-issue-labels` | Only issues with ALL these labels are checked. Separate multiple labels with commas (eg. "question,answered").<br>_Override `only-labels`_. |
|
||||
| `only-pr-labels` | Only PRs with ALL these labels are checked. 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. Separate multiple labels with commas (eg. "incomplete,waiting-feedback"). |
|
||||
| `operations-per-run` | Maximum number of operations per run (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**_. |
|
||||
| `debug-only` | Dry-run on action.<br>_Defaults to **false**_. |
|
||||
| `ascending` | Order to get issues/PR (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 (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 (only when the logs are enabled).<br>_Defaults to **true**_. |
|
||||
| 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**_. |
|
||||
|
||||
### Detailed options
|
||||
|
||||
#### operations-per-run
|
||||
|
||||
_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.
|
||||
|
||||
_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).
|
||||
|
||||
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
|
||||
|
||||
@@ -215,7 +246,7 @@ jobs:
|
||||
exempt-all-pr-milestones: true
|
||||
```
|
||||
|
||||
Avoid stale for specific labels:
|
||||
Check stale for specific labels:
|
||||
|
||||
```yaml
|
||||
name: 'Close stale issues and PRs'
|
||||
@@ -230,7 +261,7 @@ jobs:
|
||||
- uses: actions/stale@v3
|
||||
with:
|
||||
any-of-labels: 'needs-more-info,needs-demo'
|
||||
# You can opt for 'only-labels' instead if your usecase requires all labels
|
||||
# You can opt for 'only-labels' instead if your use-case requires all labels
|
||||
# to be present in the issue/PR
|
||||
```
|
||||
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -22,9 +22,13 @@ export const DefaultProcessorOptions: IIssuesProcessorOptions = Object.freeze({
|
||||
onlyIssueLabels: '',
|
||||
onlyPrLabels: '',
|
||||
anyOfLabels: '',
|
||||
anyOfIssueLabels: '',
|
||||
anyOfPrLabels: '',
|
||||
operationsPerRun: 100,
|
||||
debugOnly: true,
|
||||
removeStaleWhenUpdated: false,
|
||||
removeIssueStaleWhenUpdated: undefined,
|
||||
removePrStaleWhenUpdated: undefined,
|
||||
ascending: false,
|
||||
skipStaleIssueMessage: false,
|
||||
skipStalePrMessage: false,
|
||||
@@ -42,5 +46,5 @@ export const DefaultProcessorOptions: IIssuesProcessorOptions = Object.freeze({
|
||||
exemptAllAssignees: false,
|
||||
exemptAllIssueAssignees: undefined,
|
||||
exemptAllPrAssignees: undefined,
|
||||
enableStatistics: false
|
||||
enableStatistics: true
|
||||
});
|
||||
|
||||
@@ -25,8 +25,8 @@ test('processing an issue with no label will make it stale and close it, if it i
|
||||
// process our fake issue list
|
||||
await processor.processIssues(1);
|
||||
|
||||
expect(processor.staleIssues.length).toEqual(1);
|
||||
expect(processor.closedIssues.length).toEqual(1);
|
||||
expect(processor.staleIssues).toHaveLength(1);
|
||||
expect(processor.closedIssues).toHaveLength(1);
|
||||
});
|
||||
|
||||
test('processing an issue with no label and a start date as ECMAScript epoch in seconds being before the issue creation date will not make it stale nor close it when it is old enough and days-before-close is set to 0', async () => {
|
||||
@@ -305,9 +305,9 @@ test('processing an issue with no label will make it stale and close it, if it i
|
||||
// process our fake issue list
|
||||
await processor.processIssues(1);
|
||||
|
||||
expect(processor.staleIssues.length).toEqual(1);
|
||||
expect(processor.closedIssues.length).toEqual(1);
|
||||
expect(processor.deletedBranchIssues.length).toEqual(0);
|
||||
expect(processor.staleIssues).toHaveLength(1);
|
||||
expect(processor.closedIssues).toHaveLength(1);
|
||||
expect(processor.deletedBranchIssues).toHaveLength(0);
|
||||
});
|
||||
|
||||
test('processing an issue with no label will make it stale and not close it, if it is old enough only if days-before-close is set to > 0 and days-before-issue-close is set to > 0', async () => {
|
||||
@@ -330,8 +330,8 @@ test('processing an issue with no label will make it stale and not close it, if
|
||||
// process our fake issue list
|
||||
await processor.processIssues(1);
|
||||
|
||||
expect(processor.staleIssues.length).toEqual(1);
|
||||
expect(processor.closedIssues.length).toEqual(0);
|
||||
expect(processor.staleIssues).toHaveLength(1);
|
||||
expect(processor.closedIssues).toHaveLength(0);
|
||||
});
|
||||
|
||||
test('processing an issue with no label will make it stale and not close it if days-before-close is set to > 0', async () => {
|
||||
@@ -353,8 +353,8 @@ test('processing an issue with no label will make it stale and not close it if d
|
||||
// process our fake issue list
|
||||
await processor.processIssues(1);
|
||||
|
||||
expect(processor.staleIssues.length).toEqual(1);
|
||||
expect(processor.closedIssues.length).toEqual(0);
|
||||
expect(processor.staleIssues).toHaveLength(1);
|
||||
expect(processor.closedIssues).toHaveLength(0);
|
||||
});
|
||||
|
||||
test('processing an issue with no label will make it stale and not close it if days-before-close is set to -1 and days-before-issue-close is set to > 0', async () => {
|
||||
@@ -377,8 +377,8 @@ test('processing an issue with no label will make it stale and not close it if d
|
||||
// process our fake issue list
|
||||
await processor.processIssues(1);
|
||||
|
||||
expect(processor.staleIssues.length).toEqual(1);
|
||||
expect(processor.closedIssues.length).toEqual(0);
|
||||
expect(processor.staleIssues).toHaveLength(1);
|
||||
expect(processor.closedIssues).toHaveLength(0);
|
||||
});
|
||||
|
||||
test('processing an issue with no label will not make it stale if days-before-stale is set to -1', async () => {
|
||||
@@ -401,8 +401,8 @@ test('processing an issue with no label will not make it stale if days-before-st
|
||||
// process our fake issue list
|
||||
await processor.processIssues(1);
|
||||
|
||||
expect(processor.staleIssues.length).toEqual(0);
|
||||
expect(processor.closedIssues.length).toEqual(0);
|
||||
expect(processor.staleIssues).toHaveLength(0);
|
||||
expect(processor.closedIssues).toHaveLength(0);
|
||||
});
|
||||
|
||||
test('processing an issue with no label will not make it stale if days-before-stale and days-before-issue-stale are set to -1', async () => {
|
||||
@@ -426,8 +426,8 @@ test('processing an issue with no label will not make it stale if days-before-st
|
||||
// process our fake issue list
|
||||
await processor.processIssues(1);
|
||||
|
||||
expect(processor.staleIssues.length).toEqual(0);
|
||||
expect(processor.closedIssues.length).toEqual(0);
|
||||
expect(processor.staleIssues).toHaveLength(0);
|
||||
expect(processor.closedIssues).toHaveLength(0);
|
||||
});
|
||||
|
||||
test('processing an issue with no label will make it stale but not close it', async () => {
|
||||
@@ -454,8 +454,8 @@ test('processing an issue with no label will make it stale but not close it', as
|
||||
// process our fake issue list
|
||||
await processor.processIssues(1);
|
||||
|
||||
expect(processor.staleIssues.length).toEqual(1);
|
||||
expect(processor.closedIssues.length).toEqual(0);
|
||||
expect(processor.staleIssues).toHaveLength(1);
|
||||
expect(processor.closedIssues).toHaveLength(0);
|
||||
});
|
||||
|
||||
test('processing a stale issue will close it', async () => {
|
||||
@@ -485,8 +485,8 @@ test('processing a stale issue will close it', async () => {
|
||||
// process our fake issue list
|
||||
await processor.processIssues(1);
|
||||
|
||||
expect(processor.staleIssues.length).toEqual(0);
|
||||
expect(processor.closedIssues.length).toEqual(1);
|
||||
expect(processor.staleIssues).toHaveLength(0);
|
||||
expect(processor.closedIssues).toHaveLength(1);
|
||||
});
|
||||
|
||||
test('processing a stale issue containing a space in the label will close it', async () => {
|
||||
@@ -516,8 +516,8 @@ test('processing a stale issue containing a space in the label will close it', a
|
||||
// process our fake issue list
|
||||
await processor.processIssues(1);
|
||||
|
||||
expect(processor.staleIssues.length).toEqual(0);
|
||||
expect(processor.closedIssues.length).toEqual(1);
|
||||
expect(processor.staleIssues).toHaveLength(0);
|
||||
expect(processor.closedIssues).toHaveLength(1);
|
||||
});
|
||||
|
||||
test('processing a stale issue containing a slash in the label will close it', async () => {
|
||||
@@ -547,8 +547,8 @@ test('processing a stale issue containing a slash in the label will close it', a
|
||||
// process our fake issue list
|
||||
await processor.processIssues(1);
|
||||
|
||||
expect(processor.staleIssues.length).toEqual(0);
|
||||
expect(processor.closedIssues.length).toEqual(1);
|
||||
expect(processor.staleIssues).toHaveLength(0);
|
||||
expect(processor.closedIssues).toHaveLength(1);
|
||||
});
|
||||
|
||||
test('processing a stale issue will close it when days-before-issue-stale override days-before-stale', async () => {
|
||||
@@ -579,8 +579,8 @@ test('processing a stale issue will close it when days-before-issue-stale overri
|
||||
// process our fake issue list
|
||||
await processor.processIssues(1);
|
||||
|
||||
expect(processor.staleIssues.length).toEqual(0);
|
||||
expect(processor.closedIssues.length).toEqual(1);
|
||||
expect(processor.staleIssues).toHaveLength(0);
|
||||
expect(processor.closedIssues).toHaveLength(1);
|
||||
});
|
||||
|
||||
test('processing a stale PR will close it', async () => {
|
||||
@@ -610,8 +610,8 @@ test('processing a stale PR will close it', async () => {
|
||||
// process our fake issue list
|
||||
await processor.processIssues(1);
|
||||
|
||||
expect(processor.staleIssues.length).toEqual(0);
|
||||
expect(processor.closedIssues.length).toEqual(1);
|
||||
expect(processor.staleIssues).toHaveLength(0);
|
||||
expect(processor.closedIssues).toHaveLength(1);
|
||||
});
|
||||
|
||||
test('processing a stale PR will close it when days-before-pr-stale override days-before-stale', async () => {
|
||||
@@ -642,8 +642,8 @@ test('processing a stale PR will close it when days-before-pr-stale override day
|
||||
// process our fake issue list
|
||||
await processor.processIssues(1);
|
||||
|
||||
expect(processor.staleIssues.length).toEqual(0);
|
||||
expect(processor.closedIssues.length).toEqual(1);
|
||||
expect(processor.staleIssues).toHaveLength(0);
|
||||
expect(processor.closedIssues).toHaveLength(1);
|
||||
});
|
||||
|
||||
test('processing a stale issue will close it even if configured not to mark as stale', async () => {
|
||||
@@ -674,8 +674,8 @@ test('processing a stale issue will close it even if configured not to mark as s
|
||||
// process our fake issue list
|
||||
await processor.processIssues(1);
|
||||
|
||||
expect(processor.staleIssues.length).toEqual(0);
|
||||
expect(processor.closedIssues.length).toEqual(1);
|
||||
expect(processor.staleIssues).toHaveLength(0);
|
||||
expect(processor.closedIssues).toHaveLength(1);
|
||||
});
|
||||
|
||||
test('processing a stale issue will close it even if configured not to mark as stale when days-before-issue-stale override days-before-stale', async () => {
|
||||
@@ -707,8 +707,8 @@ test('processing a stale issue will close it even if configured not to mark as s
|
||||
// process our fake issue list
|
||||
await processor.processIssues(1);
|
||||
|
||||
expect(processor.staleIssues.length).toEqual(0);
|
||||
expect(processor.closedIssues.length).toEqual(1);
|
||||
expect(processor.staleIssues).toHaveLength(0);
|
||||
expect(processor.closedIssues).toHaveLength(1);
|
||||
});
|
||||
|
||||
test('processing a stale PR will close it even if configured not to mark as stale', async () => {
|
||||
@@ -739,8 +739,8 @@ test('processing a stale PR will close it even if configured not to mark as stal
|
||||
// process our fake issue list
|
||||
await processor.processIssues(1);
|
||||
|
||||
expect(processor.staleIssues.length).toEqual(0);
|
||||
expect(processor.closedIssues.length).toEqual(1);
|
||||
expect(processor.staleIssues).toHaveLength(0);
|
||||
expect(processor.closedIssues).toHaveLength(1);
|
||||
});
|
||||
|
||||
test('processing a stale PR will close it even if configured not to mark as stale when days-before-pr-stale override days-before-stale', async () => {
|
||||
@@ -772,8 +772,8 @@ test('processing a stale PR will close it even if configured not to mark as stal
|
||||
// process our fake issue list
|
||||
await processor.processIssues(1);
|
||||
|
||||
expect(processor.staleIssues.length).toEqual(0);
|
||||
expect(processor.closedIssues.length).toEqual(1);
|
||||
expect(processor.staleIssues).toHaveLength(0);
|
||||
expect(processor.closedIssues).toHaveLength(1);
|
||||
});
|
||||
|
||||
test('closed issues will not be marked stale', async () => {
|
||||
@@ -799,8 +799,8 @@ test('closed issues will not be marked stale', async () => {
|
||||
// process our fake issue list
|
||||
await processor.processIssues(1);
|
||||
|
||||
expect(processor.staleIssues.length).toEqual(0);
|
||||
expect(processor.closedIssues.length).toEqual(0);
|
||||
expect(processor.staleIssues).toHaveLength(0);
|
||||
expect(processor.closedIssues).toHaveLength(0);
|
||||
});
|
||||
|
||||
test('stale closed issues will not be closed', async () => {
|
||||
@@ -827,8 +827,8 @@ test('stale closed issues will not be closed', async () => {
|
||||
// process our fake issue list
|
||||
await processor.processIssues(1);
|
||||
|
||||
expect(processor.staleIssues.length).toEqual(0);
|
||||
expect(processor.closedIssues.length).toEqual(0);
|
||||
expect(processor.staleIssues).toHaveLength(0);
|
||||
expect(processor.closedIssues).toHaveLength(0);
|
||||
});
|
||||
|
||||
test('closed prs will not be marked stale', async () => {
|
||||
@@ -855,8 +855,8 @@ test('closed prs will not be marked stale', async () => {
|
||||
// process our fake issue list
|
||||
await processor.processIssues(1);
|
||||
|
||||
expect(processor.staleIssues.length).toEqual(0);
|
||||
expect(processor.closedIssues.length).toEqual(0);
|
||||
expect(processor.staleIssues).toHaveLength(0);
|
||||
expect(processor.closedIssues).toHaveLength(0);
|
||||
});
|
||||
|
||||
test('stale closed prs will not be closed', async () => {
|
||||
@@ -883,8 +883,8 @@ test('stale closed prs will not be closed', async () => {
|
||||
// process our fake issue list
|
||||
await processor.processIssues(1);
|
||||
|
||||
expect(processor.staleIssues.length).toEqual(0);
|
||||
expect(processor.closedIssues.length).toEqual(0);
|
||||
expect(processor.staleIssues).toHaveLength(0);
|
||||
expect(processor.closedIssues).toHaveLength(0);
|
||||
});
|
||||
|
||||
test('locked issues will not be marked stale', async () => {
|
||||
@@ -910,8 +910,8 @@ test('locked issues will not be marked stale', async () => {
|
||||
// process our fake issue list
|
||||
await processor.processIssues(1);
|
||||
|
||||
expect(processor.staleIssues.length).toEqual(0);
|
||||
expect(processor.closedIssues.length).toEqual(0);
|
||||
expect(processor.staleIssues).toHaveLength(0);
|
||||
expect(processor.closedIssues).toHaveLength(0);
|
||||
});
|
||||
|
||||
test('stale locked issues will not be closed', async () => {
|
||||
@@ -939,8 +939,8 @@ test('stale locked issues will not be closed', async () => {
|
||||
// process our fake issue list
|
||||
await processor.processIssues(1);
|
||||
|
||||
expect(processor.staleIssues.length).toEqual(0);
|
||||
expect(processor.closedIssues.length).toEqual(0);
|
||||
expect(processor.staleIssues).toHaveLength(0);
|
||||
expect(processor.closedIssues).toHaveLength(0);
|
||||
});
|
||||
|
||||
test('locked prs will not be marked stale', async () => {
|
||||
@@ -966,8 +966,8 @@ test('locked prs will not be marked stale', async () => {
|
||||
// process our fake issue list
|
||||
await processor.processIssues(1);
|
||||
|
||||
expect(processor.staleIssues.length).toEqual(0);
|
||||
expect(processor.closedIssues.length).toEqual(0);
|
||||
expect(processor.staleIssues).toHaveLength(0);
|
||||
expect(processor.closedIssues).toHaveLength(0);
|
||||
});
|
||||
|
||||
test('stale locked prs will not be closed', async () => {
|
||||
@@ -995,8 +995,8 @@ test('stale locked prs will not be closed', async () => {
|
||||
// process our fake issue list
|
||||
await processor.processIssues(1);
|
||||
|
||||
expect(processor.staleIssues.length).toEqual(0);
|
||||
expect(processor.closedIssues.length).toEqual(0);
|
||||
expect(processor.staleIssues).toHaveLength(0);
|
||||
expect(processor.closedIssues).toHaveLength(0);
|
||||
});
|
||||
|
||||
test('exempt issue labels will not be marked stale', async () => {
|
||||
@@ -1055,8 +1055,8 @@ test('exempt issue labels will not be marked stale (multi issue label with space
|
||||
// process our fake issue list
|
||||
await processor.processIssues(1);
|
||||
|
||||
expect(processor.staleIssues.length).toEqual(0);
|
||||
expect(processor.closedIssues.length).toEqual(0);
|
||||
expect(processor.staleIssues).toHaveLength(0);
|
||||
expect(processor.closedIssues).toHaveLength(0);
|
||||
});
|
||||
|
||||
test('exempt issue labels will not be marked stale (multi issue label)', async () => {
|
||||
@@ -1084,9 +1084,9 @@ test('exempt issue labels will not be marked stale (multi issue label)', async (
|
||||
// process our fake issue list
|
||||
await processor.processIssues(1);
|
||||
|
||||
expect(processor.staleIssues.length).toEqual(0);
|
||||
expect(processor.closedIssues.length).toEqual(0);
|
||||
expect(processor.removedLabelIssues.length).toEqual(0);
|
||||
expect(processor.staleIssues).toHaveLength(0);
|
||||
expect(processor.closedIssues).toHaveLength(0);
|
||||
expect(processor.removedLabelIssues).toHaveLength(0);
|
||||
});
|
||||
|
||||
test('exempt pr labels will not be marked stale', async () => {
|
||||
@@ -1131,7 +1131,7 @@ test('exempt pr labels will not be marked stale', async () => {
|
||||
// process our fake issue list
|
||||
await processor.processIssues(1);
|
||||
|
||||
expect(processor.staleIssues.length).toEqual(2); // PR should get processed even though it has an exempt **issue** label
|
||||
expect(processor.staleIssues).toHaveLength(2); // PR should get processed even though it has an exempt **issue** label
|
||||
});
|
||||
|
||||
test('exempt issue labels will not be marked stale and will remove the existing stale label', async () => {
|
||||
@@ -1215,13 +1215,12 @@ test('stale issues should not be closed if days is set to -1', async () => {
|
||||
// process our fake issue list
|
||||
await processor.processIssues(1);
|
||||
|
||||
expect(processor.closedIssues.length).toEqual(0);
|
||||
expect(processor.removedLabelIssues.length).toEqual(0);
|
||||
expect(processor.closedIssues).toHaveLength(0);
|
||||
expect(processor.removedLabelIssues).toHaveLength(0);
|
||||
});
|
||||
|
||||
test('stale label should be removed if a comment was added to a stale issue', async () => {
|
||||
const opts = {...DefaultProcessorOptions};
|
||||
opts.removeStaleWhenUpdated = true;
|
||||
const opts = {...DefaultProcessorOptions, removeStaleWhenUpdated: true};
|
||||
const TestIssueList: Issue[] = [
|
||||
generateIssue(
|
||||
opts,
|
||||
@@ -1251,14 +1250,13 @@ test('stale label should be removed if a comment was added to a stale issue', as
|
||||
// process our fake issue list
|
||||
await processor.processIssues(1);
|
||||
|
||||
expect(processor.closedIssues.length).toEqual(0);
|
||||
expect(processor.staleIssues.length).toEqual(0);
|
||||
expect(processor.removedLabelIssues.length).toEqual(1);
|
||||
expect(processor.closedIssues).toHaveLength(0);
|
||||
expect(processor.staleIssues).toHaveLength(0);
|
||||
expect(processor.removedLabelIssues).toHaveLength(1);
|
||||
});
|
||||
|
||||
test('stale label should not be removed if a comment was added by the bot (and the issue should be closed)', async () => {
|
||||
const opts = {...DefaultProcessorOptions};
|
||||
opts.removeStaleWhenUpdated = true;
|
||||
const opts = {...DefaultProcessorOptions, removeStaleWhenUpdated: true};
|
||||
github.context.actor = 'abot';
|
||||
const TestIssueList: Issue[] = [
|
||||
generateIssue(
|
||||
@@ -1289,9 +1287,9 @@ test('stale label should not be removed if a comment was added by the bot (and t
|
||||
// process our fake issue list
|
||||
await processor.processIssues(1);
|
||||
|
||||
expect(processor.closedIssues.length).toEqual(1);
|
||||
expect(processor.staleIssues.length).toEqual(0);
|
||||
expect(processor.removedLabelIssues.length).toEqual(0);
|
||||
expect(processor.closedIssues).toHaveLength(1);
|
||||
expect(processor.staleIssues).toHaveLength(0);
|
||||
expect(processor.removedLabelIssues).toHaveLength(0);
|
||||
});
|
||||
|
||||
test('stale label containing a space should be removed if a comment was added to a stale issue', async () => {
|
||||
@@ -1322,9 +1320,9 @@ test('stale label containing a space should be removed if a comment was added to
|
||||
// process our fake issue list
|
||||
await processor.processIssues(1);
|
||||
|
||||
expect(processor.closedIssues.length).toEqual(0);
|
||||
expect(processor.staleIssues.length).toEqual(0);
|
||||
expect(processor.removedLabelIssues.length).toEqual(1);
|
||||
expect(processor.closedIssues).toHaveLength(0);
|
||||
expect(processor.staleIssues).toHaveLength(0);
|
||||
expect(processor.removedLabelIssues).toHaveLength(1);
|
||||
});
|
||||
|
||||
test('stale issues should not be closed until after the closed number of days', async () => {
|
||||
@@ -1354,9 +1352,9 @@ test('stale issues should not be closed until after the closed number of days',
|
||||
// process our fake issue list
|
||||
await processor.processIssues(1);
|
||||
|
||||
expect(processor.closedIssues.length).toEqual(0);
|
||||
expect(processor.removedLabelIssues.length).toEqual(0);
|
||||
expect(processor.staleIssues.length).toEqual(1);
|
||||
expect(processor.closedIssues).toHaveLength(0);
|
||||
expect(processor.removedLabelIssues).toHaveLength(0);
|
||||
expect(processor.staleIssues).toHaveLength(1);
|
||||
});
|
||||
|
||||
test('stale issues should be closed if the closed nubmer of days (additive) is also passed', async () => {
|
||||
@@ -1387,9 +1385,9 @@ test('stale issues should be closed if the closed nubmer of days (additive) is a
|
||||
// process our fake issue list
|
||||
await processor.processIssues(1);
|
||||
|
||||
expect(processor.closedIssues.length).toEqual(1);
|
||||
expect(processor.removedLabelIssues.length).toEqual(0);
|
||||
expect(processor.staleIssues.length).toEqual(0);
|
||||
expect(processor.closedIssues).toHaveLength(1);
|
||||
expect(processor.removedLabelIssues).toHaveLength(0);
|
||||
expect(processor.staleIssues).toHaveLength(0);
|
||||
});
|
||||
|
||||
test('stale issues should not be closed until after the closed number of days (long)', async () => {
|
||||
@@ -1419,9 +1417,9 @@ test('stale issues should not be closed until after the closed number of days (l
|
||||
// process our fake issue list
|
||||
await processor.processIssues(1);
|
||||
|
||||
expect(processor.closedIssues.length).toEqual(0);
|
||||
expect(processor.removedLabelIssues.length).toEqual(0);
|
||||
expect(processor.staleIssues.length).toEqual(1);
|
||||
expect(processor.closedIssues).toHaveLength(0);
|
||||
expect(processor.removedLabelIssues).toHaveLength(0);
|
||||
expect(processor.staleIssues).toHaveLength(1);
|
||||
});
|
||||
|
||||
test('skips stale message on issues when skip-stale-issue-message is set', async () => {
|
||||
@@ -1455,9 +1453,9 @@ test('skips stale message on issues when skip-stale-issue-message is set', async
|
||||
await processor.processIssues(1);
|
||||
|
||||
// issue should be staled
|
||||
expect(processor.closedIssues.length).toEqual(0);
|
||||
expect(processor.removedLabelIssues.length).toEqual(0);
|
||||
expect(processor.staleIssues.length).toEqual(1);
|
||||
expect(processor.closedIssues).toHaveLength(0);
|
||||
expect(processor.removedLabelIssues).toHaveLength(0);
|
||||
expect(processor.staleIssues).toHaveLength(1);
|
||||
|
||||
// comment should not be created
|
||||
expect(markSpy).toHaveBeenCalledWith(
|
||||
@@ -1500,9 +1498,9 @@ test('skips stale message on prs when skip-stale-pr-message is set', async () =>
|
||||
await processor.processIssues(1);
|
||||
|
||||
// issue should be staled
|
||||
expect(processor.closedIssues.length).toEqual(0);
|
||||
expect(processor.removedLabelIssues.length).toEqual(0);
|
||||
expect(processor.staleIssues.length).toEqual(1);
|
||||
expect(processor.closedIssues).toHaveLength(0);
|
||||
expect(processor.removedLabelIssues).toHaveLength(0);
|
||||
expect(processor.staleIssues).toHaveLength(1);
|
||||
|
||||
// comment should not be created
|
||||
expect(markSpy).toHaveBeenCalledWith(
|
||||
@@ -1543,9 +1541,9 @@ test('not providing state takes precedence over skipStaleIssueMessage', async ()
|
||||
await processor.processIssues(1);
|
||||
|
||||
// issue should be staled
|
||||
expect(processor.closedIssues.length).toEqual(0);
|
||||
expect(processor.removedLabelIssues.length).toEqual(0);
|
||||
expect(processor.staleIssues.length).toEqual(0);
|
||||
expect(processor.closedIssues).toHaveLength(0);
|
||||
expect(processor.removedLabelIssues).toHaveLength(0);
|
||||
expect(processor.staleIssues).toHaveLength(0);
|
||||
});
|
||||
|
||||
test('not providing stalePrMessage takes precedence over skipStalePrMessage', async () => {
|
||||
@@ -1577,9 +1575,9 @@ test('not providing stalePrMessage takes precedence over skipStalePrMessage', as
|
||||
await processor.processIssues(1);
|
||||
|
||||
// issue should be staled
|
||||
expect(processor.closedIssues.length).toEqual(0);
|
||||
expect(processor.removedLabelIssues.length).toEqual(0);
|
||||
expect(processor.staleIssues.length).toEqual(0);
|
||||
expect(processor.closedIssues).toHaveLength(0);
|
||||
expect(processor.removedLabelIssues).toHaveLength(0);
|
||||
expect(processor.staleIssues).toHaveLength(0);
|
||||
});
|
||||
|
||||
test('git branch is deleted when option is enabled', async () => {
|
||||
@@ -1606,10 +1604,10 @@ test('git branch is deleted when option is enabled', async () => {
|
||||
|
||||
await processor.processIssues(1);
|
||||
|
||||
expect(processor.closedIssues.length).toEqual(1);
|
||||
expect(processor.removedLabelIssues.length).toEqual(0);
|
||||
expect(processor.staleIssues.length).toEqual(0);
|
||||
expect(processor.deletedBranchIssues.length).toEqual(1);
|
||||
expect(processor.closedIssues).toHaveLength(1);
|
||||
expect(processor.removedLabelIssues).toHaveLength(0);
|
||||
expect(processor.staleIssues).toHaveLength(0);
|
||||
expect(processor.deletedBranchIssues).toHaveLength(1);
|
||||
});
|
||||
|
||||
test('git branch is not deleted when issue is not pull request', async () => {
|
||||
@@ -1636,10 +1634,10 @@ test('git branch is not deleted when issue is not pull request', async () => {
|
||||
|
||||
await processor.processIssues(1);
|
||||
|
||||
expect(processor.closedIssues.length).toEqual(1);
|
||||
expect(processor.removedLabelIssues.length).toEqual(0);
|
||||
expect(processor.staleIssues.length).toEqual(0);
|
||||
expect(processor.deletedBranchIssues.length).toEqual(0);
|
||||
expect(processor.closedIssues).toHaveLength(1);
|
||||
expect(processor.removedLabelIssues).toHaveLength(0);
|
||||
expect(processor.staleIssues).toHaveLength(0);
|
||||
expect(processor.deletedBranchIssues).toHaveLength(0);
|
||||
});
|
||||
|
||||
test('an issue without a milestone will be marked as stale', async () => {
|
||||
@@ -1938,8 +1936,8 @@ test('processing an issue opened since 2 days and with the option "daysBeforeIss
|
||||
// process our fake issue list
|
||||
await processor.processIssues(1);
|
||||
|
||||
expect(processor.staleIssues.length).toEqual(0);
|
||||
expect(processor.closedIssues.length).toEqual(0);
|
||||
expect(processor.staleIssues).toHaveLength(0);
|
||||
expect(processor.closedIssues).toHaveLength(0);
|
||||
});
|
||||
|
||||
test('processing an issue opened since 2 days and with the option "daysBeforeIssueStale" at 2 will make it stale', async () => {
|
||||
@@ -1965,8 +1963,8 @@ test('processing an issue opened since 2 days and with the option "daysBeforeIss
|
||||
// process our fake issue list
|
||||
await processor.processIssues(1);
|
||||
|
||||
expect(processor.staleIssues.length).toEqual(1);
|
||||
expect(processor.closedIssues.length).toEqual(0);
|
||||
expect(processor.staleIssues).toHaveLength(1);
|
||||
expect(processor.closedIssues).toHaveLength(0);
|
||||
});
|
||||
|
||||
test('processing an issue opened since 2 days and with the option "daysBeforeIssueStale" at 1 will make it stale', async () => {
|
||||
@@ -1992,8 +1990,8 @@ test('processing an issue opened since 2 days and with the option "daysBeforeIss
|
||||
// process our fake issue list
|
||||
await processor.processIssues(1);
|
||||
|
||||
expect(processor.staleIssues.length).toEqual(1);
|
||||
expect(processor.closedIssues.length).toEqual(0);
|
||||
expect(processor.staleIssues).toHaveLength(1);
|
||||
expect(processor.closedIssues).toHaveLength(0);
|
||||
});
|
||||
|
||||
test('processing a pull request opened since 2 days and with the option "daysBeforePrStale" at 3 will not make it stale', async () => {
|
||||
@@ -2026,8 +2024,8 @@ test('processing a pull request opened since 2 days and with the option "daysBef
|
||||
// process our fake issue list
|
||||
await processor.processIssues(1);
|
||||
|
||||
expect(processor.staleIssues.length).toEqual(0);
|
||||
expect(processor.closedIssues.length).toEqual(0);
|
||||
expect(processor.staleIssues).toHaveLength(0);
|
||||
expect(processor.closedIssues).toHaveLength(0);
|
||||
});
|
||||
|
||||
test('processing a pull request opened since 2 days and with the option "daysBeforePrStale" at 2 will make it stale', async () => {
|
||||
@@ -2060,8 +2058,8 @@ test('processing a pull request opened since 2 days and with the option "daysBef
|
||||
// process our fake issue list
|
||||
await processor.processIssues(1);
|
||||
|
||||
expect(processor.staleIssues.length).toEqual(1);
|
||||
expect(processor.closedIssues.length).toEqual(0);
|
||||
expect(processor.staleIssues).toHaveLength(1);
|
||||
expect(processor.closedIssues).toHaveLength(0);
|
||||
});
|
||||
|
||||
test('processing a pull request opened since 2 days and with the option "daysBeforePrStale" at 1 will make it stale', async () => {
|
||||
@@ -2094,8 +2092,8 @@ test('processing a pull request opened since 2 days and with the option "daysBef
|
||||
// process our fake issue list
|
||||
await processor.processIssues(1);
|
||||
|
||||
expect(processor.staleIssues.length).toEqual(1);
|
||||
expect(processor.closedIssues.length).toEqual(0);
|
||||
expect(processor.staleIssues).toHaveLength(1);
|
||||
expect(processor.closedIssues).toHaveLength(0);
|
||||
});
|
||||
|
||||
test('processing a previously closed issue with a close label will remove the close label', async () => {
|
||||
|
||||
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()
|
||||
);
|
||||
}
|
||||
}
|
||||
18
action.yml
18
action.yml
@@ -92,6 +92,14 @@ inputs:
|
||||
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: ''
|
||||
@@ -105,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:
|
||||
|
||||
3519
dist/index.js
vendored
3519
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;
|
||||
322
package-lock.json
generated
322
package-lock.json
generated
@@ -206,6 +206,19 @@
|
||||
"@babel/helper-validator-identifier": "^7.9.0",
|
||||
"chalk": "^2.0.0",
|
||||
"js-tokens": "^4.0.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"chalk": {
|
||||
"version": "2.4.2",
|
||||
"resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz",
|
||||
"integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"ansi-styles": "^3.2.1",
|
||||
"escape-string-regexp": "^1.0.5",
|
||||
"supports-color": "^5.3.0"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"@babel/parser": {
|
||||
@@ -1560,11 +1573,6 @@
|
||||
"universal-user-agent": "^6.0.0"
|
||||
}
|
||||
},
|
||||
"@octokit/openapi-types": {
|
||||
"version": "5.3.0",
|
||||
"resolved": "https://registry.npmjs.org/@octokit/openapi-types/-/openapi-types-5.3.0.tgz",
|
||||
"integrity": "sha512-5q2qBz4iZ0xS/DEJ0ROusFbN4cVlbJE9GvOByen+mv7artuGXfVhONqcuRd7jYN2glTmCnzcZw+X6LrjRVqs0A=="
|
||||
},
|
||||
"@octokit/plugin-paginate-rest": {
|
||||
"version": "2.2.4",
|
||||
"resolved": "https://registry.npmjs.org/@octokit/plugin-paginate-rest/-/plugin-paginate-rest-2.2.4.tgz",
|
||||
@@ -1573,11 +1581,6 @@
|
||||
"@octokit/types": "^5.0.0"
|
||||
}
|
||||
},
|
||||
"@octokit/plugin-request-log": {
|
||||
"version": "1.0.3",
|
||||
"resolved": "https://registry.npmjs.org/@octokit/plugin-request-log/-/plugin-request-log-1.0.3.tgz",
|
||||
"integrity": "sha512-4RFU4li238jMJAzLgAwkBAw+4Loile5haQMQr+uhFq27BmyJXcXSKvoQKqh0agsZEiUlW6iSv3FAgvmGkur7OQ=="
|
||||
},
|
||||
"@octokit/plugin-rest-endpoint-methods": {
|
||||
"version": "4.1.2",
|
||||
"resolved": "https://registry.npmjs.org/@octokit/plugin-rest-endpoint-methods/-/plugin-rest-endpoint-methods-4.1.2.tgz",
|
||||
@@ -1612,95 +1615,6 @@
|
||||
"once": "^1.4.0"
|
||||
}
|
||||
},
|
||||
"@octokit/rest": {
|
||||
"version": "18.3.3",
|
||||
"resolved": "https://registry.npmjs.org/@octokit/rest/-/rest-18.3.3.tgz",
|
||||
"integrity": "sha512-OxElwBanZn1AShCaIrRTLM9PwhGE5/busMke/go30OWAQ+eJMD7Us/67mtapE77EYY4FM2tvb4Eg25rZaA/NPA==",
|
||||
"requires": {
|
||||
"@octokit/core": "^3.2.3",
|
||||
"@octokit/plugin-paginate-rest": "^2.6.2",
|
||||
"@octokit/plugin-request-log": "^1.0.2",
|
||||
"@octokit/plugin-rest-endpoint-methods": "4.13.3"
|
||||
},
|
||||
"dependencies": {
|
||||
"@octokit/auth-token": {
|
||||
"version": "2.4.5",
|
||||
"resolved": "https://registry.npmjs.org/@octokit/auth-token/-/auth-token-2.4.5.tgz",
|
||||
"integrity": "sha512-BpGYsPgJt05M7/L/5FoE1PiAbdxXFZkX/3kDYcsvd1v6UhlnE5e96dTDr0ezX/EFwciQxf3cNV0loipsURU+WA==",
|
||||
"requires": {
|
||||
"@octokit/types": "^6.0.3"
|
||||
}
|
||||
},
|
||||
"@octokit/core": {
|
||||
"version": "3.2.5",
|
||||
"resolved": "https://registry.npmjs.org/@octokit/core/-/core-3.2.5.tgz",
|
||||
"integrity": "sha512-+DCtPykGnvXKWWQI0E1XD+CCeWSBhB6kwItXqfFmNBlIlhczuDPbg+P6BtLnVBaRJDAjv+1mrUJuRsFSjktopg==",
|
||||
"requires": {
|
||||
"@octokit/auth-token": "^2.4.4",
|
||||
"@octokit/graphql": "^4.5.8",
|
||||
"@octokit/request": "^5.4.12",
|
||||
"@octokit/types": "^6.0.3",
|
||||
"before-after-hook": "^2.1.0",
|
||||
"universal-user-agent": "^6.0.0"
|
||||
}
|
||||
},
|
||||
"@octokit/graphql": {
|
||||
"version": "4.6.0",
|
||||
"resolved": "https://registry.npmjs.org/@octokit/graphql/-/graphql-4.6.0.tgz",
|
||||
"integrity": "sha512-CJ6n7izLFXLvPZaWzCQDjU/RP+vHiZmWdOunaCS87v+2jxMsW9FB5ktfIxybRBxZjxuJGRnxk7xJecWTVxFUYQ==",
|
||||
"requires": {
|
||||
"@octokit/request": "^5.3.0",
|
||||
"@octokit/types": "^6.0.3",
|
||||
"universal-user-agent": "^6.0.0"
|
||||
}
|
||||
},
|
||||
"@octokit/plugin-paginate-rest": {
|
||||
"version": "2.11.0",
|
||||
"resolved": "https://registry.npmjs.org/@octokit/plugin-paginate-rest/-/plugin-paginate-rest-2.11.0.tgz",
|
||||
"integrity": "sha512-7L9xQank2G3r1dGqrVPo1z62V5utbykOUzlmNHPz87Pww/JpZQ9KyG5CHtUzgmB4n5iDRKYNK/86A8D98HP0yA==",
|
||||
"requires": {
|
||||
"@octokit/types": "^6.11.0"
|
||||
}
|
||||
},
|
||||
"@octokit/plugin-rest-endpoint-methods": {
|
||||
"version": "4.13.3",
|
||||
"resolved": "https://registry.npmjs.org/@octokit/plugin-rest-endpoint-methods/-/plugin-rest-endpoint-methods-4.13.3.tgz",
|
||||
"integrity": "sha512-nMGS2osFcWXRfHkDR0d+lB1zpMPTZJ0NjysPUfs7BT5/juNG/Q0+5UB6nC1f62jPzun154qekzwOb7Q5oahCXQ==",
|
||||
"requires": {
|
||||
"@octokit/types": "^6.12.0",
|
||||
"deprecation": "^2.3.1"
|
||||
}
|
||||
},
|
||||
"@octokit/request": {
|
||||
"version": "5.4.14",
|
||||
"resolved": "https://registry.npmjs.org/@octokit/request/-/request-5.4.14.tgz",
|
||||
"integrity": "sha512-VkmtacOIQp9daSnBmDI92xNIeLuSRDOIuplp/CJomkvzt7M18NXgG044Cx/LFKLgjKt9T2tZR6AtJayba9GTSA==",
|
||||
"requires": {
|
||||
"@octokit/endpoint": "^6.0.1",
|
||||
"@octokit/request-error": "^2.0.0",
|
||||
"@octokit/types": "^6.7.1",
|
||||
"deprecation": "^2.0.0",
|
||||
"is-plain-object": "^5.0.0",
|
||||
"node-fetch": "^2.6.1",
|
||||
"once": "^1.4.0",
|
||||
"universal-user-agent": "^6.0.0"
|
||||
}
|
||||
},
|
||||
"@octokit/types": {
|
||||
"version": "6.12.0",
|
||||
"resolved": "https://registry.npmjs.org/@octokit/types/-/types-6.12.0.tgz",
|
||||
"integrity": "sha512-KwOf16soD7aDEEi/PgNeJlHzjZPfrmmNy+7WezSdrpnqZ7YImBJcNnX9+5RUHt1MnA4h8oISRHTqaZDGsX9DRQ==",
|
||||
"requires": {
|
||||
"@octokit/openapi-types": "^5.3.0"
|
||||
}
|
||||
},
|
||||
"is-plain-object": {
|
||||
"version": "5.0.0",
|
||||
"resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-5.0.0.tgz",
|
||||
"integrity": "sha512-VRSzKkbMm5jMDoKLbltAkFQ5Qr7VDiTFGXxYFXXowVj387GeGNOCsOH6Msy00SGZ3Fp84b1Naa1psqgcCIEP5Q=="
|
||||
}
|
||||
}
|
||||
},
|
||||
"@octokit/types": {
|
||||
"version": "5.1.2",
|
||||
"resolved": "https://registry.npmjs.org/@octokit/types/-/types-5.1.2.tgz",
|
||||
@@ -1839,9 +1753,9 @@
|
||||
}
|
||||
},
|
||||
"@types/node": {
|
||||
"version": "14.14.31",
|
||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-14.14.31.tgz",
|
||||
"integrity": "sha512-vFHy/ezP5qI0rFgJ7aQnjDXwAMrG0KqqIH7tQG5PPv3BWBayOPIQNBjVc/P6hhdZfMx51REc6tfDNXHUio893g=="
|
||||
"version": "15.0.2",
|
||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-15.0.2.tgz",
|
||||
"integrity": "sha512-p68+a+KoxpoB47015IeYZYRrdqMUcpbK8re/zpFB8Ld46LHC1lPEbp3EXgkEhAYEcPvjJF6ZO+869SQ0aH1dcA=="
|
||||
},
|
||||
"@types/normalize-package-data": {
|
||||
"version": "2.4.0",
|
||||
@@ -1856,9 +1770,9 @@
|
||||
"dev": true
|
||||
},
|
||||
"@types/semver": {
|
||||
"version": "7.3.4",
|
||||
"resolved": "https://registry.npmjs.org/@types/semver/-/semver-7.3.4.tgz",
|
||||
"integrity": "sha512-+nVsLKlcUCeMzD2ufHEYuJ9a2ovstb6Dp52A5VsoKxDXgvE051XgHI/33I1EymwkRGQkwnA0LkhnUzituGs4EQ==",
|
||||
"version": "7.3.5",
|
||||
"resolved": "https://registry.npmjs.org/@types/semver/-/semver-7.3.5.tgz",
|
||||
"integrity": "sha512-iotVxtCCsPLRAvxMFFgxL8HD2l4mAZ2Oin7/VJ2ooWO0VOK4EGOGmZWZn1uCq7RofR3I/1IOSjCHlFT71eVK0Q==",
|
||||
"dev": true
|
||||
},
|
||||
"@types/stack-utils": {
|
||||
@@ -1962,55 +1876,55 @@
|
||||
}
|
||||
},
|
||||
"@typescript-eslint/experimental-utils": {
|
||||
"version": "4.15.1",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/experimental-utils/-/experimental-utils-4.15.1.tgz",
|
||||
"integrity": "sha512-9LQRmOzBRI1iOdJorr4jEnQhadxK4c9R2aEAsm7WE/7dq8wkKD1suaV0S/JucTL8QlYUPU1y2yjqg+aGC0IQBQ==",
|
||||
"version": "4.22.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/experimental-utils/-/experimental-utils-4.22.0.tgz",
|
||||
"integrity": "sha512-xJXHHl6TuAxB5AWiVrGhvbGL8/hbiCQ8FiWwObO3r0fnvBdrbWEDy1hlvGQOAWc6qsCWuWMKdVWlLAEMpxnddg==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@types/json-schema": "^7.0.3",
|
||||
"@typescript-eslint/scope-manager": "4.15.1",
|
||||
"@typescript-eslint/types": "4.15.1",
|
||||
"@typescript-eslint/typescript-estree": "4.15.1",
|
||||
"@typescript-eslint/scope-manager": "4.22.0",
|
||||
"@typescript-eslint/types": "4.22.0",
|
||||
"@typescript-eslint/typescript-estree": "4.22.0",
|
||||
"eslint-scope": "^5.0.0",
|
||||
"eslint-utils": "^2.0.0"
|
||||
}
|
||||
},
|
||||
"@typescript-eslint/parser": {
|
||||
"version": "4.16.1",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-4.16.1.tgz",
|
||||
"integrity": "sha512-/c0LEZcDL5y8RyI1zLcmZMvJrsR6SM1uetskFkoh3dvqDKVXPsXI+wFB/CbVw7WkEyyTKobC1mUNp/5y6gRvXg==",
|
||||
"version": "4.22.1",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-4.22.1.tgz",
|
||||
"integrity": "sha512-l+sUJFInWhuMxA6rtirzjooh8cM/AATAe3amvIkqKFeMzkn85V+eLzb1RyuXkHak4dLfYzOmF6DXPyflJvjQnw==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@typescript-eslint/scope-manager": "4.16.1",
|
||||
"@typescript-eslint/types": "4.16.1",
|
||||
"@typescript-eslint/typescript-estree": "4.16.1",
|
||||
"@typescript-eslint/scope-manager": "4.22.1",
|
||||
"@typescript-eslint/types": "4.22.1",
|
||||
"@typescript-eslint/typescript-estree": "4.22.1",
|
||||
"debug": "^4.1.1"
|
||||
},
|
||||
"dependencies": {
|
||||
"@typescript-eslint/scope-manager": {
|
||||
"version": "4.16.1",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-4.16.1.tgz",
|
||||
"integrity": "sha512-6IlZv9JaurqV0jkEg923cV49aAn8V6+1H1DRfhRcvZUrptQ+UtSKHb5kwTayzOYTJJ/RsYZdcvhOEKiBLyc0Cw==",
|
||||
"version": "4.22.1",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-4.22.1.tgz",
|
||||
"integrity": "sha512-d5bAiPBiessSmNi8Amq/RuLslvcumxLmyhf1/Xa9IuaoFJ0YtshlJKxhlbY7l2JdEk3wS0EnmnfeJWSvADOe0g==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@typescript-eslint/types": "4.16.1",
|
||||
"@typescript-eslint/visitor-keys": "4.16.1"
|
||||
"@typescript-eslint/types": "4.22.1",
|
||||
"@typescript-eslint/visitor-keys": "4.22.1"
|
||||
}
|
||||
},
|
||||
"@typescript-eslint/types": {
|
||||
"version": "4.16.1",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-4.16.1.tgz",
|
||||
"integrity": "sha512-nnKqBwMgRlhzmJQF8tnFDZWfunXmJyuXj55xc8Kbfup4PbkzdoDXZvzN8//EiKR27J6vUSU8j4t37yUuYPiLqA==",
|
||||
"version": "4.22.1",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-4.22.1.tgz",
|
||||
"integrity": "sha512-2HTkbkdAeI3OOcWbqA8hWf/7z9c6gkmnWNGz0dKSLYLWywUlkOAQ2XcjhlKLj5xBFDf8FgAOF5aQbnLRvgNbCw==",
|
||||
"dev": true
|
||||
},
|
||||
"@typescript-eslint/typescript-estree": {
|
||||
"version": "4.16.1",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-4.16.1.tgz",
|
||||
"integrity": "sha512-m8I/DKHa8YbeHt31T+UGd/l8Kwr0XCTCZL3H4HMvvLCT7HU9V7yYdinTOv1gf/zfqNeDcCgaFH2BMsS8x6NvJg==",
|
||||
"version": "4.22.1",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-4.22.1.tgz",
|
||||
"integrity": "sha512-p3We0pAPacT+onSGM+sPR+M9CblVqdA9F1JEdIqRVlxK5Qth4ochXQgIyb9daBomyQKAXbygxp1aXQRV0GC79A==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@typescript-eslint/types": "4.16.1",
|
||||
"@typescript-eslint/visitor-keys": "4.16.1",
|
||||
"@typescript-eslint/types": "4.22.1",
|
||||
"@typescript-eslint/visitor-keys": "4.22.1",
|
||||
"debug": "^4.1.1",
|
||||
"globby": "^11.0.1",
|
||||
"is-glob": "^4.0.1",
|
||||
@@ -2019,47 +1933,47 @@
|
||||
}
|
||||
},
|
||||
"@typescript-eslint/visitor-keys": {
|
||||
"version": "4.16.1",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-4.16.1.tgz",
|
||||
"integrity": "sha512-s/aIP1XcMkEqCNcPQtl60ogUYjSM8FU2mq1O7y5cFf3Xcob1z1iXWNB6cC43Op+NGRTFgGolri6s8z/efA9i1w==",
|
||||
"version": "4.22.1",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-4.22.1.tgz",
|
||||
"integrity": "sha512-WPkOrIRm+WCLZxXQHCi+WG8T2MMTUFR70rWjdWYddLT7cEfb2P4a3O/J2U1FBVsSFTocXLCoXWY6MZGejeStvQ==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@typescript-eslint/types": "4.16.1",
|
||||
"@typescript-eslint/types": "4.22.1",
|
||||
"eslint-visitor-keys": "^2.0.0"
|
||||
}
|
||||
},
|
||||
"eslint-visitor-keys": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-2.0.0.tgz",
|
||||
"integrity": "sha512-QudtT6av5WXels9WjIM7qz1XD1cWGvX4gGXvp/zBn9nXG02D0utdU3Em2m/QjTnrsk6bBjmCygl3rmj118msQQ==",
|
||||
"version": "2.1.0",
|
||||
"resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-2.1.0.tgz",
|
||||
"integrity": "sha512-0rSmRBzXgDzIsD6mGdJgevzgezI534Cer5L/vyMX0kHzT/jiB43jRhd9YUlMGYLQy2zprNmoT8qasCGtY+QaKw==",
|
||||
"dev": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"@typescript-eslint/scope-manager": {
|
||||
"version": "4.15.1",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-4.15.1.tgz",
|
||||
"integrity": "sha512-ibQrTFcAm7yG4C1iwpIYK7vDnFg+fKaZVfvyOm3sNsGAerKfwPVFtYft5EbjzByDJ4dj1WD8/34REJfw/9wdVA==",
|
||||
"version": "4.22.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-4.22.0.tgz",
|
||||
"integrity": "sha512-OcCO7LTdk6ukawUM40wo61WdeoA7NM/zaoq1/2cs13M7GyiF+T4rxuA4xM+6LeHWjWbss7hkGXjFDRcKD4O04Q==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@typescript-eslint/types": "4.15.1",
|
||||
"@typescript-eslint/visitor-keys": "4.15.1"
|
||||
"@typescript-eslint/types": "4.22.0",
|
||||
"@typescript-eslint/visitor-keys": "4.22.0"
|
||||
}
|
||||
},
|
||||
"@typescript-eslint/types": {
|
||||
"version": "4.15.1",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-4.15.1.tgz",
|
||||
"integrity": "sha512-iGsaUyWFyLz0mHfXhX4zO6P7O3sExQpBJ2dgXB0G5g/8PRVfBBsmQIc3r83ranEQTALLR3Vko/fnCIVqmH+mPw==",
|
||||
"version": "4.22.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-4.22.0.tgz",
|
||||
"integrity": "sha512-sW/BiXmmyMqDPO2kpOhSy2Py5w6KvRRsKZnV0c4+0nr4GIcedJwXAq+RHNK4lLVEZAJYFltnnk1tJSlbeS9lYA==",
|
||||
"dev": true
|
||||
},
|
||||
"@typescript-eslint/typescript-estree": {
|
||||
"version": "4.15.1",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-4.15.1.tgz",
|
||||
"integrity": "sha512-z8MN3CicTEumrWAEB2e2CcoZa3KP9+SMYLIA2aM49XW3cWIaiVSOAGq30ffR5XHxRirqE90fgLw3e6WmNx5uNw==",
|
||||
"version": "4.22.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-4.22.0.tgz",
|
||||
"integrity": "sha512-TkIFeu5JEeSs5ze/4NID+PIcVjgoU3cUQUIZnH3Sb1cEn1lBo7StSV5bwPuJQuoxKXlzAObjYTilOEKRuhR5yg==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@typescript-eslint/types": "4.15.1",
|
||||
"@typescript-eslint/visitor-keys": "4.15.1",
|
||||
"@typescript-eslint/types": "4.22.0",
|
||||
"@typescript-eslint/visitor-keys": "4.22.0",
|
||||
"debug": "^4.1.1",
|
||||
"globby": "^11.0.1",
|
||||
"is-glob": "^4.0.1",
|
||||
@@ -2068,12 +1982,12 @@
|
||||
}
|
||||
},
|
||||
"@typescript-eslint/visitor-keys": {
|
||||
"version": "4.15.1",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-4.15.1.tgz",
|
||||
"integrity": "sha512-tYzaTP9plooRJY8eNlpAewTOqtWW/4ff/5wBjNVaJ0S0wC4Gpq/zDVRTJa5bq2v1pCNQ08xxMCndcvR+h7lMww==",
|
||||
"version": "4.22.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-4.22.0.tgz",
|
||||
"integrity": "sha512-nnMu4F+s4o0sll6cBSsTeVsT4cwxB7zECK3dFxzEjPBii9xLpq4yqqsy/FU5zMfan6G60DKZSCXAa3sHJZrcYw==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@typescript-eslint/types": "4.15.1",
|
||||
"@typescript-eslint/types": "4.22.0",
|
||||
"eslint-visitor-keys": "^2.0.0"
|
||||
},
|
||||
"dependencies": {
|
||||
@@ -2643,14 +2557,54 @@
|
||||
"dev": true
|
||||
},
|
||||
"chalk": {
|
||||
"version": "2.4.2",
|
||||
"resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz",
|
||||
"integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==",
|
||||
"version": "4.1.0",
|
||||
"resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.0.tgz",
|
||||
"integrity": "sha512-qwx12AxXe2Q5xQ43Ac//I6v5aXTipYrSESdOgzrN+9XjgEpyjpKuvSGaN4qE93f7TQTlerQQ8S+EQ0EyDoVL1A==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"ansi-styles": "^3.2.1",
|
||||
"escape-string-regexp": "^1.0.5",
|
||||
"supports-color": "^5.3.0"
|
||||
"ansi-styles": "^4.1.0",
|
||||
"supports-color": "^7.1.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"ansi-styles": {
|
||||
"version": "4.3.0",
|
||||
"resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
|
||||
"integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"color-convert": "^2.0.1"
|
||||
}
|
||||
},
|
||||
"color-convert": {
|
||||
"version": "2.0.1",
|
||||
"resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
|
||||
"integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"color-name": "~1.1.4"
|
||||
}
|
||||
},
|
||||
"color-name": {
|
||||
"version": "1.1.4",
|
||||
"resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
|
||||
"integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
|
||||
"dev": true
|
||||
},
|
||||
"has-flag": {
|
||||
"version": "4.0.0",
|
||||
"resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz",
|
||||
"integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==",
|
||||
"dev": true
|
||||
},
|
||||
"supports-color": {
|
||||
"version": "7.2.0",
|
||||
"resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz",
|
||||
"integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"has-flag": "^4.0.0"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"char-regex": {
|
||||
@@ -3367,12 +3321,6 @@
|
||||
"type-check": "~0.4.0"
|
||||
}
|
||||
},
|
||||
"lodash": {
|
||||
"version": "4.17.21",
|
||||
"resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz",
|
||||
"integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==",
|
||||
"dev": true
|
||||
},
|
||||
"optionator": {
|
||||
"version": "0.9.1",
|
||||
"resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.1.tgz",
|
||||
@@ -3607,9 +3555,9 @@
|
||||
}
|
||||
},
|
||||
"eslint-plugin-jest": {
|
||||
"version": "24.1.5",
|
||||
"resolved": "https://registry.npmjs.org/eslint-plugin-jest/-/eslint-plugin-jest-24.1.5.tgz",
|
||||
"integrity": "sha512-FIP3lwC8EzEG+rOs1y96cOJmMVpdFNreoDJv29B5vIupVssRi8zrSY3QadogT0K3h1Y8TMxJ6ZSAzYUmFCp2hg==",
|
||||
"version": "24.3.6",
|
||||
"resolved": "https://registry.npmjs.org/eslint-plugin-jest/-/eslint-plugin-jest-24.3.6.tgz",
|
||||
"integrity": "sha512-WOVH4TIaBLIeCX576rLcOgjNXqP+jNlCiEmRgFTfQtJ52DpwnIQKAVGlGPAN7CZ33bW6eNfHD6s8ZbEUTQubJg==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@typescript-eslint/experimental-utils": "^4.0.1"
|
||||
@@ -4370,9 +4318,9 @@
|
||||
}
|
||||
},
|
||||
"hosted-git-info": {
|
||||
"version": "2.8.8",
|
||||
"resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.8.8.tgz",
|
||||
"integrity": "sha512-f/wzC2QaWBs7t9IYqB4T3sR1xviIViXJRJTWBlx2Gf3g0Xi5vI7Yy4koXQ1c9OYDGHN9sBy1DQ2AB8fqZBWhUg==",
|
||||
"version": "2.8.9",
|
||||
"resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.8.9.tgz",
|
||||
"integrity": "sha512-mxIDAb9Lsm6DoOJ7xH+5+X4y1LU/4Hi50L9C5sIswK3JzULS4bwk1FvjdBgvYR4bzT4tuUQiC15FE2f5HbLvYw==",
|
||||
"dev": true
|
||||
},
|
||||
"html-encoding-sniffer": {
|
||||
@@ -7843,9 +7791,9 @@
|
||||
}
|
||||
},
|
||||
"lodash": {
|
||||
"version": "4.17.19",
|
||||
"resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.19.tgz",
|
||||
"integrity": "sha512-JNvd8XER9GQX0v2qJgsaN/mzFCNA5BRe/j8JN9d+tWyGLSodKQHKFicdwNYzWwI3wjRnaKPsGj1XkBjx/F96DQ==",
|
||||
"version": "4.17.21",
|
||||
"resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz",
|
||||
"integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==",
|
||||
"dev": true
|
||||
},
|
||||
"lodash.deburr": {
|
||||
@@ -8835,9 +8783,9 @@
|
||||
}
|
||||
},
|
||||
"semver": {
|
||||
"version": "7.3.4",
|
||||
"resolved": "https://registry.npmjs.org/semver/-/semver-7.3.4.tgz",
|
||||
"integrity": "sha512-tCfb2WLjqFAtXn4KEdxIhalnRtoKFN7nAwj0B3ZXCbQloV2tq5eDbcTmT68JJD3nRJq24/XgxtQKFIpQdtvmVw==",
|
||||
"version": "7.3.5",
|
||||
"resolved": "https://registry.npmjs.org/semver/-/semver-7.3.5.tgz",
|
||||
"integrity": "sha512-PoeGJYh8HK4BTO/a9Tf6ZG3veo/A7ZVsYrSA6J8ny9nb3B1VrpkuN+z9OE5wfE5p6H4LchYZsegiQgbJD94ZFQ==",
|
||||
"requires": {
|
||||
"lru-cache": "^6.0.0"
|
||||
}
|
||||
@@ -9422,12 +9370,6 @@
|
||||
"resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz",
|
||||
"integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==",
|
||||
"dev": true
|
||||
},
|
||||
"lodash": {
|
||||
"version": "4.17.21",
|
||||
"resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz",
|
||||
"integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==",
|
||||
"dev": true
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -9652,9 +9594,9 @@
|
||||
}
|
||||
},
|
||||
"typescript": {
|
||||
"version": "4.2.3",
|
||||
"resolved": "https://registry.npmjs.org/typescript/-/typescript-4.2.3.tgz",
|
||||
"integrity": "sha512-qOcYwxaByStAWrBf4x0fibwZvMRG+r4cQoTjbPtUlrWjBHbmCAww1i448U0GJ+3cNNEtebDteo/cHOR3xJ4wEw==",
|
||||
"version": "4.2.4",
|
||||
"resolved": "https://registry.npmjs.org/typescript/-/typescript-4.2.4.tgz",
|
||||
"integrity": "sha512-V+evlYHZnQkaz8TRBuxTA92yZBPotr5H+WhQ7bD3hZUndx5tGOa1fuCgeSjxAzM1RiN5IzvadIXTVefuuwZCRg==",
|
||||
"dev": true
|
||||
},
|
||||
"unbox-primitive": {
|
||||
@@ -9971,9 +9913,9 @@
|
||||
"dev": true
|
||||
},
|
||||
"y18n": {
|
||||
"version": "4.0.0",
|
||||
"resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.0.tgz",
|
||||
"integrity": "sha512-r9S/ZyXu/Xu9q1tYlpsLIsa3EeLXXk0VwlxqTcFRfg9EhMW+17kbt9G0NrgCmhGb5vT2hyhJZLfDGx+7+5Uj/w==",
|
||||
"version": "4.0.1",
|
||||
"resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.1.tgz",
|
||||
"integrity": "sha512-wNcy4NvjMYL8gogWWYAO7ZFWFfHcbdbE57tZO8e4cbpj8tfUcwrwqSl3ad8HxpYWCdXcJUCeKKZS62Av1affwQ==",
|
||||
"dev": true
|
||||
},
|
||||
"yallist": {
|
||||
|
||||
17
package.json
17
package.json
@@ -15,7 +15,7 @@
|
||||
"pack": "ncc build",
|
||||
"test": "jest",
|
||||
"test:only-errors": "jest --reporters jest-silent-reporter --silent",
|
||||
"test:watch": "jest --watch --notify --expandf",
|
||||
"test:watch": "jest --watch --notify --expand",
|
||||
"all": "npm run build && npm run format && npm run lint && npm run pack && npm test"
|
||||
},
|
||||
"repository": {
|
||||
@@ -32,27 +32,28 @@
|
||||
"dependencies": {
|
||||
"@actions/core": "^1.2.6",
|
||||
"@actions/github": "^4.0.0",
|
||||
"@octokit/rest": "^18.3.3",
|
||||
"lodash.deburr": "^4.1.0",
|
||||
"semver": "^7.3.4"
|
||||
"semver": "^7.3.5"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/jest": "^26.0.20",
|
||||
"@types/lodash.deburr": "^4.1.6",
|
||||
"@types/node": "^14.14.31",
|
||||
"@types/semver": "^7.3.4",
|
||||
"@types/node": "^15.0.2",
|
||||
"@types/semver": "^7.3.5",
|
||||
"@typescript-eslint/eslint-plugin": "^4.16.1",
|
||||
"@typescript-eslint/parser": "^4.16.1",
|
||||
"@typescript-eslint/parser": "^4.22.1",
|
||||
"@vercel/ncc": "^0.27.0",
|
||||
"chalk": "^4.1.0",
|
||||
"eslint": "^7.21.0",
|
||||
"eslint-plugin-github": "^4.1.2",
|
||||
"eslint-plugin-jest": "^24.1.5",
|
||||
"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",
|
||||
"terminal-link": "^2.1.1",
|
||||
"ts-jest": "^26.5.3",
|
||||
"typescript": "^4.2.3"
|
||||
"typescript": "^4.2.4"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,8 +2,8 @@ import {DefaultProcessorOptions} from '../../__tests__/constants/default-process
|
||||
import {generateIIssue} from '../../__tests__/functions/generate-iissue';
|
||||
import {IIssue} from '../interfaces/issue';
|
||||
import {IIssuesProcessorOptions} from '../interfaces/issues-processor-options';
|
||||
import {Issue} from './issue';
|
||||
import {Assignees} from './assignees';
|
||||
import {Issue} from './issue';
|
||||
|
||||
describe('Assignees', (): void => {
|
||||
let assignees: Assignees;
|
||||
@@ -12,7 +12,10 @@ describe('Assignees', (): void => {
|
||||
let issueInterface: IIssue;
|
||||
|
||||
beforeEach((): void => {
|
||||
optionsInterface = {...DefaultProcessorOptions};
|
||||
optionsInterface = {
|
||||
...DefaultProcessorOptions,
|
||||
exemptAllAssignees: false
|
||||
};
|
||||
issueInterface = generateIIssue();
|
||||
});
|
||||
|
||||
@@ -303,21 +306,51 @@ describe('Assignees', (): 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 an assignee', (): void => {
|
||||
describe('when the given options are configured to exempt all assignees', (): void => {
|
||||
beforeEach((): void => {
|
||||
optionsInterface.exemptAssignees = '';
|
||||
optionsInterface.exemptAllAssignees = true;
|
||||
});
|
||||
|
||||
describe('when the given options are not configured to exempt a pull request with an assignee', (): void => {
|
||||
describe('when the given issue does not have an assignee', (): void => {
|
||||
beforeEach((): void => {
|
||||
optionsInterface.exemptPrAssignees = '';
|
||||
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 => {
|
||||
@@ -337,6 +370,102 @@ describe('Assignees', (): void => {
|
||||
});
|
||||
|
||||
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 = [
|
||||
{
|
||||
@@ -362,7 +491,7 @@ describe('Assignees', (): void => {
|
||||
optionsInterface.exemptPrAssignees = 'dummy-exempt-pr-assignee';
|
||||
});
|
||||
|
||||
describe('when the given issue does not have an assignee', (): void => {
|
||||
describe('when the given pull request does not have an assignee', (): void => {
|
||||
beforeEach((): void => {
|
||||
issueInterface.assignees = [];
|
||||
});
|
||||
@@ -378,7 +507,7 @@ describe('Assignees', (): void => {
|
||||
});
|
||||
});
|
||||
|
||||
describe('when the given issue does have an assignee different than the exempt pull request assignee', (): void => {
|
||||
describe('when the given pull request does have an assignee different than the exempt pull request assignee', (): void => {
|
||||
beforeEach((): void => {
|
||||
issueInterface.assignees = [
|
||||
{
|
||||
@@ -398,7 +527,7 @@ describe('Assignees', (): void => {
|
||||
});
|
||||
});
|
||||
|
||||
describe('when the given issue does have an assignee equaling the exempt pull request assignee', (): void => {
|
||||
describe('when the given pull request does have an assignee equaling the exempt pull request assignee', (): void => {
|
||||
beforeEach((): void => {
|
||||
issueInterface.assignees = [
|
||||
{
|
||||
@@ -430,7 +559,7 @@ describe('Assignees', (): void => {
|
||||
optionsInterface.exemptPrAssignees = '';
|
||||
});
|
||||
|
||||
describe('when the given issue does not have an assignee', (): void => {
|
||||
describe('when the given pull request does not have an assignee', (): void => {
|
||||
beforeEach((): void => {
|
||||
issueInterface.assignees = [];
|
||||
});
|
||||
@@ -446,7 +575,7 @@ describe('Assignees', (): void => {
|
||||
});
|
||||
});
|
||||
|
||||
describe('when the given issue does have an assignee different than the exempt assignee', (): void => {
|
||||
describe('when the given pull request does have an assignee different than the exempt assignee', (): void => {
|
||||
beforeEach((): void => {
|
||||
issueInterface.assignees = [
|
||||
{
|
||||
@@ -466,7 +595,7 @@ describe('Assignees', (): void => {
|
||||
});
|
||||
});
|
||||
|
||||
describe('when the given issue does have an assignee equaling the exempt assignee', (): void => {
|
||||
describe('when the given pull request does have an assignee equaling the exempt assignee', (): void => {
|
||||
beforeEach((): void => {
|
||||
issueInterface.assignees = [
|
||||
{
|
||||
@@ -492,7 +621,7 @@ describe('Assignees', (): void => {
|
||||
optionsInterface.exemptPrAssignees = 'dummy-exempt-pr-assignee';
|
||||
});
|
||||
|
||||
describe('when the given issue does not have an assignee', (): void => {
|
||||
describe('when the given pull request does not have an assignee', (): void => {
|
||||
beforeEach((): void => {
|
||||
issueInterface.assignees = [];
|
||||
});
|
||||
@@ -508,7 +637,7 @@ describe('Assignees', (): void => {
|
||||
});
|
||||
});
|
||||
|
||||
describe('when the given issue does have an assignee different than the exempt pull request assignee', (): void => {
|
||||
describe('when the given pull request does have an assignee different than the exempt pull request assignee', (): void => {
|
||||
beforeEach((): void => {
|
||||
issueInterface.assignees = [
|
||||
{
|
||||
@@ -528,7 +657,7 @@ describe('Assignees', (): void => {
|
||||
});
|
||||
});
|
||||
|
||||
describe('when the given issue does have an assignee equaling the exempt pull request assignee', (): void => {
|
||||
describe('when the given pull request does have an assignee equaling the exempt pull request assignee', (): void => {
|
||||
beforeEach((): void => {
|
||||
issueInterface.assignees = [
|
||||
{
|
||||
@@ -548,7 +677,7 @@ describe('Assignees', (): void => {
|
||||
});
|
||||
});
|
||||
|
||||
describe('when the given issue does have an assignee different than the exempt assignee', (): void => {
|
||||
describe('when the given pull request does have an assignee different than the exempt assignee', (): void => {
|
||||
beforeEach((): void => {
|
||||
issueInterface.assignees = [
|
||||
{
|
||||
@@ -568,7 +697,7 @@ describe('Assignees', (): void => {
|
||||
});
|
||||
});
|
||||
|
||||
describe('when the given issue does have an assignee equaling the exempt assignee', (): void => {
|
||||
describe('when the given pull request does have an assignee equaling the exempt assignee', (): void => {
|
||||
beforeEach((): void => {
|
||||
issueInterface.assignees = [
|
||||
{
|
||||
@@ -589,6 +718,132 @@ describe('Assignees', (): void => {
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
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);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
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';
|
||||
@@ -32,7 +34,8 @@ export class Assignees {
|
||||
|
||||
if (this._shouldExemptAllAssignees()) {
|
||||
this._issueLogger.info(
|
||||
'Skipping $$type because it has an exempt assignee'
|
||||
chalk.white('└──'),
|
||||
'Skipping this $$type because it has an exempt assignee'
|
||||
);
|
||||
|
||||
return true;
|
||||
@@ -42,7 +45,8 @@ export class Assignees {
|
||||
|
||||
if (exemptAssignees.length === 0) {
|
||||
this._issueLogger.info(
|
||||
`No option was specified to skip the stale process for this $$type`
|
||||
chalk.white('├──'),
|
||||
`No assignee option was specified to skip the stale process for this $$type`
|
||||
);
|
||||
this._logSkip();
|
||||
|
||||
@@ -50,9 +54,10 @@ export class Assignees {
|
||||
}
|
||||
|
||||
this._issueLogger.info(
|
||||
`Found ${exemptAssignees.length} assignee${
|
||||
chalk.white('├──'),
|
||||
`Found ${chalk.cyan(exemptAssignees.length)} assignee${
|
||||
exemptAssignees.length > 1 ? 's' : ''
|
||||
} on this $$type`
|
||||
} that can exempt stale on this $$type`
|
||||
);
|
||||
|
||||
const hasExemptAssignee: boolean = exemptAssignees.some(
|
||||
@@ -62,11 +67,13 @@ export class Assignees {
|
||||
|
||||
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'
|
||||
);
|
||||
}
|
||||
@@ -83,12 +90,18 @@ export class Assignees {
|
||||
private _getExemptIssueAssignees(): string[] {
|
||||
if (this._options.exemptIssueAssignees === '') {
|
||||
this._issueLogger.info(
|
||||
'The option "exemptIssueAssignees" is disabled. No specific assignee can skip the stale process for this $$type'
|
||||
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(
|
||||
'The option "exemptAssignees" is disabled. No specific assignee can skip the stale process for this $$type'
|
||||
chalk.white('├──'),
|
||||
`The option ${this._issueLogger.createOptionLink(
|
||||
Option.ExemptAssignees
|
||||
)} is disabled. No specific assignee can skip the stale process for this $$type`
|
||||
);
|
||||
|
||||
return [];
|
||||
@@ -99,9 +112,10 @@ export class Assignees {
|
||||
);
|
||||
|
||||
this._issueLogger.info(
|
||||
`The option "exemptAssignees" is set. ${
|
||||
exemptAssignees.length
|
||||
} assignee${
|
||||
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`
|
||||
);
|
||||
@@ -114,9 +128,10 @@ export class Assignees {
|
||||
);
|
||||
|
||||
this._issueLogger.info(
|
||||
`The option "exemptIssueAssignees" is set. ${
|
||||
exemptAssignees.length
|
||||
} assignee${
|
||||
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`
|
||||
);
|
||||
@@ -127,12 +142,18 @@ export class Assignees {
|
||||
private _getExemptPullRequestAssignees(): string[] {
|
||||
if (this._options.exemptPrAssignees === '') {
|
||||
this._issueLogger.info(
|
||||
'The option "exemptPrAssignees" is disabled. No specific assignee can skip the stale process for this $$type'
|
||||
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(
|
||||
'The option "exemptAssignees" is disabled. No specific assignee can skip the stale process for this $$type'
|
||||
chalk.white('├──'),
|
||||
`The option ${this._issueLogger.createOptionLink(
|
||||
Option.ExemptAssignees
|
||||
)} is disabled. No specific assignee can skip the stale process for this $$type`
|
||||
);
|
||||
|
||||
return [];
|
||||
@@ -143,9 +164,10 @@ export class Assignees {
|
||||
);
|
||||
|
||||
this._issueLogger.info(
|
||||
`The option "exemptAssignees" is set. ${
|
||||
exemptAssignees.length
|
||||
} assignee${
|
||||
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`
|
||||
);
|
||||
@@ -158,9 +180,10 @@ export class Assignees {
|
||||
);
|
||||
|
||||
this._issueLogger.info(
|
||||
`The option "exemptPrAssignees" is set. ${
|
||||
exemptAssignees.length
|
||||
} assignee${
|
||||
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`
|
||||
);
|
||||
@@ -178,6 +201,7 @@ export class Assignees {
|
||||
|
||||
if (isSameAssignee) {
|
||||
this._issueLogger.info(
|
||||
chalk.white('├──'),
|
||||
`@${issueAssignee.login} is assigned on this $$type and is an exempt assignee`
|
||||
);
|
||||
}
|
||||
@@ -196,13 +220,17 @@ export class Assignees {
|
||||
private _shouldExemptAllIssueAssignees(): boolean {
|
||||
if (this._options.exemptAllIssueAssignees === true) {
|
||||
this._issueLogger.info(
|
||||
'The option "exemptAllIssueAssignees" is enabled. Any assignee on this $$type will skip the stale process'
|
||||
`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 "exemptAllIssueAssignees" is disabled. Only some specific assignees on this $$type will skip the stale process'
|
||||
`The option ${this._issueLogger.createOptionLink(
|
||||
Option.ExemptAllIssueAssignees
|
||||
)} is disabled. Only some specific assignees on this $$type will skip the stale process`
|
||||
);
|
||||
|
||||
return false;
|
||||
@@ -216,13 +244,17 @@ export class Assignees {
|
||||
private _shouldExemptAllPullRequestAssignees(): boolean {
|
||||
if (this._options.exemptAllPrAssignees === true) {
|
||||
this._issueLogger.info(
|
||||
'The option "exemptAllPrAssignees" is enabled. Any assignee on this $$type will skip the stale process'
|
||||
`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 "exemptAllPrAssignees" is disabled. Only some specific assignees on this $$type will skip the stale process'
|
||||
`The option ${this._issueLogger.createOptionLink(
|
||||
Option.ExemptAllPrAssignees
|
||||
)} is disabled. Only some specific assignees on this $$type will skip the stale process`
|
||||
);
|
||||
|
||||
return false;
|
||||
@@ -236,16 +268,20 @@ export class Assignees {
|
||||
private _logExemptAllAssigneesOption(): void {
|
||||
if (this._options.exemptAllAssignees) {
|
||||
this._issueLogger.info(
|
||||
'The option "exemptAllAssignees" is enabled. Any assignee on this $$type will skip the stale process'
|
||||
`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 "exemptAllAssignees" is disabled. Only some specific assignees on this $$type will skip the stale process'
|
||||
`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('Skip the assignees checks');
|
||||
this._issueLogger.info(chalk.white('└──'), 'Skip the assignees checks');
|
||||
}
|
||||
}
|
||||
|
||||
@@ -31,8 +31,12 @@ describe('Issue', (): void => {
|
||||
onlyIssueLabels: '',
|
||||
onlyPrLabels: '',
|
||||
anyOfLabels: '',
|
||||
anyOfIssueLabels: '',
|
||||
anyOfPrLabels: '',
|
||||
operationsPerRun: 0,
|
||||
removeStaleWhenUpdated: false,
|
||||
removeIssueStaleWhenUpdated: undefined,
|
||||
removePrStaleWhenUpdated: undefined,
|
||||
repoToken: '',
|
||||
skipStaleIssueMessage: false,
|
||||
skipStalePrMessage: false,
|
||||
|
||||
@@ -6,6 +6,7 @@ import {IIssuesProcessorOptions} from '../interfaces/issues-processor-options';
|
||||
import {ILabel} from '../interfaces/label';
|
||||
import {IMilestone} from '../interfaces/milestone';
|
||||
import {IsoDateString} from '../types/iso-date-string';
|
||||
import {Operations} from './operations';
|
||||
|
||||
export class Issue implements IIssue {
|
||||
private readonly _options: IIssuesProcessorOptions;
|
||||
@@ -20,6 +21,19 @@ export class Issue implements IIssue {
|
||||
readonly milestone: IMilestone | undefined;
|
||||
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<IIssuesProcessorOptions>,
|
||||
@@ -40,18 +54,6 @@ export class Issue implements IIssue {
|
||||
this.isStale = isLabeled(this, this.staleLabel);
|
||||
}
|
||||
|
||||
get isPullRequest(): boolean {
|
||||
return isPullRequest(this);
|
||||
}
|
||||
|
||||
get staleLabel(): string {
|
||||
return this._getStaleLabel();
|
||||
}
|
||||
|
||||
get hasAssignees(): boolean {
|
||||
return this.assignees.length > 0;
|
||||
}
|
||||
|
||||
private _getStaleLabel(): string {
|
||||
return this.isPullRequest
|
||||
? this._options.stalePrLabel
|
||||
|
||||
@@ -1,11 +1,14 @@
|
||||
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 {isPullRequest} from '../functions/is-pull-request';
|
||||
import {shouldMarkWhenStale} from '../functions/should-mark-when-stale';
|
||||
import {wordsToList} from '../functions/words-to-list';
|
||||
import {IComment} from '../interfaces/comment';
|
||||
@@ -18,6 +21,7 @@ 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';
|
||||
|
||||
/***
|
||||
@@ -32,9 +36,32 @@ export class IssuesProcessor {
|
||||
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;
|
||||
private _operationsLeft = 0;
|
||||
readonly client: InstanceType<typeof GitHub>;
|
||||
readonly options: IIssuesProcessorOptions;
|
||||
readonly staleIssues: Issue[] = [];
|
||||
@@ -44,38 +71,58 @@ export class IssuesProcessor {
|
||||
|
||||
constructor(options: IIssuesProcessorOptions) {
|
||||
this.options = options;
|
||||
this._operationsLeft = this.options.operationsPerRun;
|
||||
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(
|
||||
'Executing in debug mode. Debug output will be written but no issues will be processed.'
|
||||
chalk.yellowBright(
|
||||
'The debug output will be written but no issues/PRs will be processed.'
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
if (this.options.enableStatistics) {
|
||||
this._statistics = new Statistics(this.options);
|
||||
this._statistics = new Statistics();
|
||||
}
|
||||
}
|
||||
|
||||
async processIssues(page = 1): Promise<number> {
|
||||
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('---');
|
||||
this._statistics?.setOperationsLeft(this._operationsLeft).logStats();
|
||||
this._logger.info('No more issues found to process. Exiting.');
|
||||
this._logger.info(
|
||||
chalk.green('No more issues found to process. Exiting...')
|
||||
);
|
||||
this._statistics
|
||||
?.setRemainingOperations(this._operations.getRemainingOperationsCount())
|
||||
.logStats();
|
||||
|
||||
return this._operationsLeft;
|
||||
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?.incrementProcessedIssuesCount();
|
||||
this._statistics?.incrementProcessedItemsCount(issue);
|
||||
|
||||
issueLogger.info(`Found this $$type last updated ${issue.updated_at}`);
|
||||
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
|
||||
@@ -100,7 +147,11 @@ export class IssuesProcessor {
|
||||
|
||||
if (onlyLabels.length > 0) {
|
||||
issueLogger.info(
|
||||
`The option "onlyLabels" was specified to only processed the issues and pull requests with all those labels (${onlyLabels.length})`
|
||||
`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(
|
||||
@@ -111,37 +162,64 @@ export class IssuesProcessor {
|
||||
|
||||
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(
|
||||
`All the required labels are present on this $$type. Continuing the process`
|
||||
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 "onlyLabels" was not specified. Continuing the process for this $$type`
|
||||
`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: ${daysBeforeStale}`);
|
||||
issueLogger.info(
|
||||
`Days before $$type stale: ${chalk.cyan(daysBeforeStale)}`
|
||||
);
|
||||
|
||||
const shouldMarkAsStale: boolean = shouldMarkWhenStale(daysBeforeStale);
|
||||
|
||||
if (!staleMessage && shouldMarkAsStale) {
|
||||
issueLogger.info(`Skipping $$type due to empty stale message`);
|
||||
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 $$type because it is closed`);
|
||||
continue; // don't process closed issues
|
||||
issueLogger.info(`Skipping this $$type because it is closed`);
|
||||
IssuesProcessor._endIssueProcessing(issue);
|
||||
continue; // Don't process closed issues
|
||||
}
|
||||
|
||||
if (issue.locked) {
|
||||
issueLogger.info(`Skipping $$type because it is locked`);
|
||||
continue; // don't process locked issues
|
||||
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
|
||||
@@ -152,31 +230,35 @@ export class IssuesProcessor {
|
||||
const createdAt: Date = new Date(issue.created_at);
|
||||
|
||||
issueLogger.info(
|
||||
`A start date was specified for the ${getHumanizedDate(startDate)} (${
|
||||
this.options.startDate
|
||||
})`
|
||||
`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)) {
|
||||
throw new Error(
|
||||
`Invalid issue field: "created_at". Expected a valid date`
|
||||
IssuesProcessor._endIssueProcessing(issue);
|
||||
core.setFailed(
|
||||
new Error(
|
||||
`Invalid issue field: "created_at". Expected a valid date`
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
issueLogger.info(
|
||||
`$$type created the ${getHumanizedDate(createdAt)} (${
|
||||
`$$type created the ${getHumanizedDate(createdAt)} (${chalk.cyan(
|
||||
issue.created_at
|
||||
})`
|
||||
)})`
|
||||
);
|
||||
|
||||
if (!isDateMoreRecentThan(createdAt, startDate)) {
|
||||
issueLogger.info(
|
||||
`Skipping $$type because it was created before the specified start date`
|
||||
`Skipping this $$type because it was created before the specified start date`
|
||||
);
|
||||
|
||||
continue; // don't process issues which were created before the start date
|
||||
IssuesProcessor._endIssueProcessing(issue);
|
||||
continue; // Don't process issues which were created before the start date
|
||||
}
|
||||
}
|
||||
|
||||
@@ -202,60 +284,117 @@ export class IssuesProcessor {
|
||||
await this._removeStaleLabel(issue, staleLabel);
|
||||
}
|
||||
|
||||
issueLogger.info(`Skipping $$type because it has an exempt label`);
|
||||
continue; // don't process exempt issues
|
||||
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.options.anyOfLabels);
|
||||
if (
|
||||
anyOfLabels.length &&
|
||||
!anyOfLabels.some((label: Readonly<string>): boolean =>
|
||||
isLabeled(issue, label)
|
||||
)
|
||||
) {
|
||||
const anyOfLabels: string[] = wordsToList(this._getAnyOfLabels(issue));
|
||||
|
||||
if (anyOfLabels.length > 0) {
|
||||
issueLogger.info(
|
||||
`Skipping $$type because it does not have any of the required labels`
|
||||
`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`
|
||||
);
|
||||
continue; // don't process issues without any of the required labels
|
||||
}
|
||||
|
||||
const milestones: Milestones = new Milestones(this.options, issue);
|
||||
|
||||
if (milestones.shouldExemptMilestones()) {
|
||||
issueLogger.info(
|
||||
`Skipping $$type because it has an exempted milestone`
|
||||
);
|
||||
continue; // don't process exempt milestones
|
||||
IssuesProcessor._endIssueProcessing(issue);
|
||||
continue; // Don't process exempt milestones
|
||||
}
|
||||
|
||||
const assignees: Assignees = new Assignees(this.options, issue);
|
||||
|
||||
if (assignees.shouldExemptAssignees()) {
|
||||
continue; // don't process exempt assignees
|
||||
IssuesProcessor._endIssueProcessing(issue);
|
||||
continue; // Don't process exempt assignees
|
||||
}
|
||||
|
||||
// should this issue be marked stale?
|
||||
// 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 && shouldBeStale && shouldMarkAsStale) {
|
||||
issueLogger.info(
|
||||
`Marking $$type 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}`
|
||||
);
|
||||
// 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
|
||||
// Process the issue if it was marked stale
|
||||
if (issue.isStale) {
|
||||
issueLogger.info(`Found a stale $$type`);
|
||||
issueLogger.info(`This $$type is already stale`);
|
||||
await this._processStaleIssue(
|
||||
issue,
|
||||
staleLabel,
|
||||
@@ -264,28 +403,44 @@ export class IssuesProcessor {
|
||||
closeLabel
|
||||
);
|
||||
}
|
||||
|
||||
IssuesProcessor._endIssueProcessing(issue);
|
||||
}
|
||||
|
||||
if (this._operationsLeft <= 0) {
|
||||
if (!this._operations.hasRemainingOperations()) {
|
||||
this._logger.warning(
|
||||
'Reached max number of operations to process. Exiting.'
|
||||
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;
|
||||
}
|
||||
|
||||
// do the next batch
|
||||
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
|
||||
// Grab comments for an issue since a given date
|
||||
async listIssueComments(
|
||||
issueNumber: number,
|
||||
sinceDate: string
|
||||
issueNumber: Readonly<number>,
|
||||
sinceDate: Readonly<string>
|
||||
): Promise<IComment[]> {
|
||||
// find any comments since date on the given issue
|
||||
// Find any comments since date on the given issue
|
||||
try {
|
||||
this._operationsLeft -= 1;
|
||||
this._statistics?.incrementFetchedIssuesCommentsCount();
|
||||
this._operations.consumeOperation();
|
||||
this._statistics?.incrementFetchedItemsCommentsCount();
|
||||
const comments = await this.client.issues.listComments({
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
@@ -304,7 +459,7 @@ export class IssuesProcessor {
|
||||
let actor;
|
||||
|
||||
try {
|
||||
this._operationsLeft -= 1;
|
||||
this._operations.consumeOperation();
|
||||
actor = await this.client.users.getAuthenticated();
|
||||
} catch (error) {
|
||||
return context.actor;
|
||||
@@ -320,8 +475,7 @@ export class IssuesProcessor {
|
||||
type OctoKitIssueList = GetResponseTypeFromEndpointMethod<typeof endpoint>;
|
||||
|
||||
try {
|
||||
this._operationsLeft -= 1;
|
||||
this._statistics?.incrementFetchedIssuesCount();
|
||||
this._operations.consumeOperation();
|
||||
const issueResult: OctoKitIssueList = await this.client.issues.listForRepo(
|
||||
{
|
||||
owner: context.repo.owner,
|
||||
@@ -332,6 +486,7 @@ export class IssuesProcessor {
|
||||
page
|
||||
}
|
||||
);
|
||||
this._statistics?.incrementFetchedItemsCount(issueResult.data.length);
|
||||
|
||||
return issueResult.data.map(
|
||||
(issue: Readonly<IIssue>): Issue => new Issue(this.options, issue)
|
||||
@@ -350,10 +505,10 @@ export class IssuesProcessor {
|
||||
): Promise<string | undefined> {
|
||||
const issueLogger: IssueLogger = new IssueLogger(issue);
|
||||
|
||||
issueLogger.info(`Checking for label on $$type`);
|
||||
issueLogger.info(`Checking for label on this $$type`);
|
||||
|
||||
this._operationsLeft -= 1;
|
||||
this._statistics?.incrementFetchedIssuesEventsCount();
|
||||
this._consumeIssueOperation(issue);
|
||||
this._statistics?.incrementFetchedItemsEventsCount();
|
||||
const options = this.client.issues.listEvents.endpoint.merge({
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
@@ -387,31 +542,38 @@ export class IssuesProcessor {
|
||||
const issueLogger: IssueLogger = new IssueLogger(issue);
|
||||
const markedStaleOn: string =
|
||||
(await this.getLabelCreationDate(issue, staleLabel)) || issue.updated_at;
|
||||
issueLogger.info(`$$type marked stale on: ${markedStaleOn}`);
|
||||
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: ${issueHasComments}`);
|
||||
issueLogger.info(
|
||||
`$$type has been commented on: ${chalk.cyan(issueHasComments)}`
|
||||
);
|
||||
|
||||
const isPr: boolean = isPullRequest(issue);
|
||||
const daysBeforeClose: number = isPr
|
||||
const daysBeforeClose: number = issue.isPullRequest
|
||||
? this._getDaysBeforePrClose()
|
||||
: this._getDaysBeforeIssueClose();
|
||||
|
||||
issueLogger.info(`Days before $$type close: ${daysBeforeClose}`);
|
||||
issueLogger.info(
|
||||
`Days before $$type close: ${chalk.cyan(daysBeforeClose)}`
|
||||
);
|
||||
|
||||
const issueHasUpdate: boolean = IssuesProcessor._updatedSince(
|
||||
issue.updated_at,
|
||||
daysBeforeClose
|
||||
);
|
||||
issueLogger.info(`$$type has been updated: ${issueHasUpdate}`);
|
||||
issueLogger.info(`$$type has been updated: ${chalk.cyan(issueHasUpdate)}`);
|
||||
|
||||
// should we un-stale this issue?
|
||||
if (this.options.removeStaleWhenUpdated && issueHasComments) {
|
||||
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
|
||||
@@ -421,13 +583,17 @@ export class IssuesProcessor {
|
||||
|
||||
if (!issueHasComments && !issueHasUpdate) {
|
||||
issueLogger.info(
|
||||
`Closing $$type because it was last updated on ${issue.updated_at}`
|
||||
`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 branch for as delete-branch option was specified`
|
||||
`Deleting the branch the option ${issueLogger.createOptionLink(
|
||||
Option.DeleteBranch
|
||||
)} was specified`
|
||||
);
|
||||
await this._deleteBranch(issue);
|
||||
this.deletedBranchIssues.push(issue);
|
||||
@@ -447,7 +613,9 @@ export class IssuesProcessor {
|
||||
): Promise<boolean> {
|
||||
const issueLogger: IssueLogger = new IssueLogger(issue);
|
||||
|
||||
issueLogger.info(`Checking for comments on $$type since ${sinceDate}`);
|
||||
issueLogger.info(
|
||||
`Checking for comments on $$type since: ${chalk.cyan(sinceDate)}`
|
||||
);
|
||||
|
||||
if (!sinceDate) {
|
||||
return true;
|
||||
@@ -461,7 +629,9 @@ export class IssuesProcessor {
|
||||
);
|
||||
|
||||
issueLogger.info(
|
||||
`Comments not made by actor or another bot: ${filteredComments.length}`
|
||||
`Comments not made by actor or another bot: ${chalk.cyan(
|
||||
filteredComments.length
|
||||
)}`
|
||||
);
|
||||
|
||||
// if there are any user comments returned
|
||||
@@ -477,7 +647,7 @@ export class IssuesProcessor {
|
||||
): Promise<void> {
|
||||
const issueLogger: IssueLogger = new IssueLogger(issue);
|
||||
|
||||
issueLogger.info(`Marking $$type as stale`);
|
||||
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
|
||||
@@ -491,8 +661,8 @@ export class IssuesProcessor {
|
||||
|
||||
if (!skipMessage) {
|
||||
try {
|
||||
this._operationsLeft -= 1;
|
||||
this._statistics?.incrementAddedComment();
|
||||
this._consumeIssueOperation(issue);
|
||||
this._statistics?.incrementAddedItemsComment(issue);
|
||||
await this.client.issues.createComment({
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
@@ -500,14 +670,14 @@ export class IssuesProcessor {
|
||||
body: staleMessage
|
||||
});
|
||||
} catch (error) {
|
||||
issueLogger.error(`Error creating a comment: ${error.message}`);
|
||||
issueLogger.error(`Error when creating a comment: ${error.message}`);
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
this._operationsLeft -= 1;
|
||||
this._statistics?.incrementAddedLabel();
|
||||
this._statistics?.incrementStaleIssuesCount();
|
||||
this._consumeIssueOperation(issue);
|
||||
this._statistics?.incrementAddedItemsLabel(issue);
|
||||
this._statistics?.incrementStaleItemsCount(issue);
|
||||
await this.client.issues.addLabels({
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
@@ -515,7 +685,7 @@ export class IssuesProcessor {
|
||||
labels: [staleLabel]
|
||||
});
|
||||
} catch (error) {
|
||||
issueLogger.error(`Error adding a label: ${error.message}`);
|
||||
issueLogger.error(`Error when adding a label: ${error.message}`);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -536,8 +706,8 @@ export class IssuesProcessor {
|
||||
|
||||
if (closeMessage) {
|
||||
try {
|
||||
this._operationsLeft -= 1;
|
||||
this._statistics?.incrementAddedComment();
|
||||
this._consumeIssueOperation(issue);
|
||||
this._statistics?.incrementAddedItemsComment(issue);
|
||||
await this.client.issues.createComment({
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
@@ -545,14 +715,14 @@ export class IssuesProcessor {
|
||||
body: closeMessage
|
||||
});
|
||||
} catch (error) {
|
||||
issueLogger.error(`Error creating a comment: ${error.message}`);
|
||||
issueLogger.error(`Error when creating a comment: ${error.message}`);
|
||||
}
|
||||
}
|
||||
|
||||
if (closeLabel) {
|
||||
try {
|
||||
this._operationsLeft -= 1;
|
||||
this._statistics?.incrementAddedLabel();
|
||||
this._consumeIssueOperation(issue);
|
||||
this._statistics?.incrementAddedItemsLabel(issue);
|
||||
await this.client.issues.addLabels({
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
@@ -560,13 +730,13 @@ export class IssuesProcessor {
|
||||
labels: [closeLabel]
|
||||
});
|
||||
} catch (error) {
|
||||
issueLogger.error(`Error adding a label: ${error.message}`);
|
||||
issueLogger.error(`Error when adding a label: ${error.message}`);
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
this._operationsLeft -= 1;
|
||||
this._statistics?.incrementClosedIssuesCount();
|
||||
this._consumeIssueOperation(issue);
|
||||
this._statistics?.incrementClosedItemsCount(issue);
|
||||
await this.client.issues.update({
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
@@ -574,7 +744,7 @@ export class IssuesProcessor {
|
||||
state: 'closed'
|
||||
});
|
||||
} catch (error) {
|
||||
issueLogger.error(`Error updating this $$type: ${error.message}`);
|
||||
issueLogger.error(`Error when updating this $$type: ${error.message}`);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -588,7 +758,7 @@ export class IssuesProcessor {
|
||||
}
|
||||
|
||||
try {
|
||||
this._operationsLeft -= 1;
|
||||
this._consumeIssueOperation(issue);
|
||||
this._statistics?.incrementFetchedPullRequestsCount();
|
||||
const pullRequest = await this.client.pulls.get({
|
||||
owner: context.repo.owner,
|
||||
@@ -598,7 +768,7 @@ export class IssuesProcessor {
|
||||
|
||||
return pullRequest.data;
|
||||
} catch (error) {
|
||||
issueLogger.error(`Error getting this $$type: ${error.message}`);
|
||||
issueLogger.error(`Error when getting this $$type: ${error.message}`);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -612,7 +782,7 @@ export class IssuesProcessor {
|
||||
|
||||
if (!pullRequest) {
|
||||
issueLogger.info(
|
||||
`Not deleting branch as pull request not found for this $$type`
|
||||
`Not deleting this branch as no pull request was found for this $$type`
|
||||
);
|
||||
return;
|
||||
}
|
||||
@@ -622,10 +792,12 @@ export class IssuesProcessor {
|
||||
}
|
||||
|
||||
const branch = pullRequest.head.ref;
|
||||
issueLogger.info(`Deleting branch ${branch} from closed $$type`);
|
||||
issueLogger.info(
|
||||
`Deleting the branch "${chalk.cyan(branch)}" from closed $$type`
|
||||
);
|
||||
|
||||
try {
|
||||
this._operationsLeft -= 1;
|
||||
this._consumeIssueOperation(issue);
|
||||
this._statistics?.incrementDeletedBranchesCount();
|
||||
await this.client.git.deleteRef({
|
||||
owner: context.repo.owner,
|
||||
@@ -634,34 +806,40 @@ export class IssuesProcessor {
|
||||
});
|
||||
} catch (error) {
|
||||
issueLogger.error(
|
||||
`Error deleting branch ${branch} from $$type: ${error.message}`
|
||||
`Error when deleting the branch "${chalk.cyan(branch)}" from $$type: ${
|
||||
error.message
|
||||
}`
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// Remove a label from an issue
|
||||
// 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 label "${label}" from $$type`);
|
||||
issueLogger.info(
|
||||
`Removing the label "${chalk.cyan(label)}" from this $$type...`
|
||||
);
|
||||
this.removedLabelIssues.push(issue);
|
||||
|
||||
// @todo remove the debug only to be able to test the code below
|
||||
if (this.options.debugOnly) {
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
this._operationsLeft -= 1;
|
||||
this._statistics?.incrementDeletedLabelsCount();
|
||||
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 removing a label: ${error.message}`);
|
||||
issueLogger.error(
|
||||
`Error when removing the label: "${chalk.cyan(error.message)}"`
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -703,6 +881,36 @@ export class IssuesProcessor {
|
||||
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>
|
||||
@@ -714,7 +922,7 @@ export class IssuesProcessor {
|
||||
);
|
||||
|
||||
await this._removeLabel(issue, staleLabel);
|
||||
this._statistics?.incrementUndoStaleIssuesCount();
|
||||
this._statistics?.incrementUndoStaleItemsCount(issue);
|
||||
}
|
||||
|
||||
private async _removeCloseLabel(
|
||||
@@ -735,11 +943,45 @@ export class IssuesProcessor {
|
||||
|
||||
if (isLabeled(issue, closeLabel)) {
|
||||
issueLogger.info(
|
||||
`The $$type has a close label "${closeLabel}". Removing the close label...`
|
||||
`The $$type has a close label "${chalk.cyan(
|
||||
closeLabel
|
||||
)}". Removing the close label...`
|
||||
);
|
||||
|
||||
await this._removeLabel(issue, closeLabel);
|
||||
this._statistics?.incrementDeletedCloseLabelsCount();
|
||||
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,4 +1,4 @@
|
||||
import * as core from '@actions/core';
|
||||
import chalk from 'chalk';
|
||||
import {Issue} from '../issue';
|
||||
import {Logger} from './logger';
|
||||
|
||||
@@ -15,23 +15,24 @@ import {Logger} from './logger';
|
||||
* @example
|
||||
* warning('The $$type will stale') => "The pull request will stale"
|
||||
*/
|
||||
export class IssueLogger implements Logger {
|
||||
export class IssueLogger extends Logger {
|
||||
private readonly _issue: Issue;
|
||||
|
||||
constructor(issue: Issue) {
|
||||
super();
|
||||
this._issue = issue;
|
||||
}
|
||||
|
||||
warning(message: Readonly<string>): void {
|
||||
core.warning(this._format(message));
|
||||
warning(...message: string[]): void {
|
||||
super.warning(this._format(...message));
|
||||
}
|
||||
|
||||
info(message: Readonly<string>): void {
|
||||
core.info(this._format(message));
|
||||
info(...message: string[]): void {
|
||||
super.info(this._format(...message));
|
||||
}
|
||||
|
||||
error(message: Readonly<string>): void {
|
||||
core.error(this._format(message));
|
||||
error(...message: string[]): void {
|
||||
super.error(this._format(...message));
|
||||
}
|
||||
|
||||
private _replaceTokens(message: Readonly<string>): string {
|
||||
@@ -51,14 +52,28 @@ export class IssueLogger implements Logger {
|
||||
}
|
||||
|
||||
private _prefixWithIssueNumber(message: Readonly<string>): string {
|
||||
return `[#${this._getIssueNumber()}] ${message}`;
|
||||
return `${this._getPrefix()} ${message}`;
|
||||
}
|
||||
|
||||
private _getIssueNumber(): number {
|
||||
return this._issue.number;
|
||||
}
|
||||
|
||||
private _format(message: Readonly<string>): string {
|
||||
return this._prefixWithIssueNumber(this._replaceTokens(message));
|
||||
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}`)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -12,7 +12,7 @@ describe('Milestones', (): void => {
|
||||
let issueInterface: IIssue;
|
||||
|
||||
beforeEach((): void => {
|
||||
optionsInterface = {...DefaultProcessorOptions};
|
||||
optionsInterface = {...DefaultProcessorOptions, exemptAllMilestones: false};
|
||||
issueInterface = generateIIssue();
|
||||
});
|
||||
|
||||
@@ -285,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 => {
|
||||
@@ -319,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'
|
||||
@@ -342,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;
|
||||
});
|
||||
@@ -358,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'
|
||||
@@ -376,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'
|
||||
@@ -406,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;
|
||||
});
|
||||
@@ -422,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'
|
||||
@@ -440,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'
|
||||
@@ -464,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;
|
||||
});
|
||||
@@ -480,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'
|
||||
@@ -498,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'
|
||||
@@ -516,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'
|
||||
@@ -534,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'
|
||||
@@ -553,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,7 +1,10 @@
|
||||
import chalk from 'chalk';
|
||||
import deburr from 'lodash.deburr';
|
||||
import {Option} from '../enums/option';
|
||||
import {wordsToList} from '../functions/words-to-list';
|
||||
import {IIssuesProcessorOptions} from '../interfaces/issues-processor-options';
|
||||
import {Issue} from './issue';
|
||||
import {IssueLogger} from './loggers/issue-logger';
|
||||
|
||||
type CleanMilestone = string;
|
||||
|
||||
@@ -12,42 +15,179 @@ export class Milestones {
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
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 {
|
||||
@@ -55,10 +195,22 @@ 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 {
|
||||
@@ -73,21 +225,69 @@ export class Milestones {
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
@@ -1,108 +1,155 @@
|
||||
import {IIssuesProcessorOptions} from '../interfaces/issues-processor-options';
|
||||
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 readonly _options: IIssuesProcessorOptions;
|
||||
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 _deletedLabelsCount = 0;
|
||||
private _deletedCloseLabelsCount = 0;
|
||||
private _closedPullRequestsCount = 0;
|
||||
private _deletedIssuesLabelsCount = 0;
|
||||
private _deletedPullRequestsLabelsCount = 0;
|
||||
private _deletedCloseIssuesLabelsCount = 0;
|
||||
private _deletedClosePullRequestsLabelsCount = 0;
|
||||
private _deletedBranchesCount = 0;
|
||||
private _addedLabelsCount = 0;
|
||||
private _addedCommentsCount = 0;
|
||||
private _fetchedIssuesCount = 0;
|
||||
private _fetchedIssuesEventsCount = 0;
|
||||
private _fetchedIssuesCommentsCount = 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;
|
||||
|
||||
constructor(options: IIssuesProcessorOptions) {
|
||||
this._options = options;
|
||||
}
|
||||
|
||||
incrementProcessedIssuesCount(increment: Readonly<number> = 1): Statistics {
|
||||
this._processedIssuesCount += increment;
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
incrementStaleIssuesCount(increment: Readonly<number> = 1): Statistics {
|
||||
this._staleIssuesCount += increment;
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
incrementUndoStaleIssuesCount(increment: Readonly<number> = 1): Statistics {
|
||||
this._undoStaleIssuesCount += increment;
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
setOperationsLeft(operationsLeft: Readonly<number>): Statistics {
|
||||
this._operationsCount = this._options.operationsPerRun - operationsLeft;
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
incrementClosedIssuesCount(increment: Readonly<number> = 1): Statistics {
|
||||
this._closedIssuesCount += increment;
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
incrementDeletedLabelsCount(increment: Readonly<number> = 1): Statistics {
|
||||
this._deletedLabelsCount += increment;
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
incrementDeletedCloseLabelsCount(
|
||||
incrementProcessedItemsCount(
|
||||
issue: Readonly<Issue>,
|
||||
increment: Readonly<number> = 1
|
||||
): Statistics {
|
||||
this._deletedCloseLabelsCount += increment;
|
||||
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;
|
||||
}
|
||||
|
||||
incrementAddedLabel(increment: Readonly<number> = 1): Statistics {
|
||||
this._addedLabelsCount += increment;
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
incrementAddedComment(increment: Readonly<number> = 1): Statistics {
|
||||
this._addedCommentsCount += increment;
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
incrementFetchedIssuesCount(increment: Readonly<number> = 1): Statistics {
|
||||
this._fetchedIssuesCount += increment;
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
incrementFetchedIssuesEventsCount(
|
||||
incrementAddedItemsLabel(
|
||||
issue: Readonly<Issue>,
|
||||
increment: Readonly<number> = 1
|
||||
): Statistics {
|
||||
this._fetchedIssuesEventsCount += increment;
|
||||
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;
|
||||
}
|
||||
|
||||
incrementFetchedIssuesCommentsCount(
|
||||
incrementFetchedItemsEventsCount(
|
||||
increment: Readonly<number> = 1
|
||||
): Statistics {
|
||||
this._fetchedIssuesCommentsCount += increment;
|
||||
this._fetchedItemsEventsCount += increment;
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
incrementFetchedItemsCommentsCount(
|
||||
increment: Readonly<number> = 1
|
||||
): Statistics {
|
||||
this._fetchedItemsCommentsCount += increment;
|
||||
|
||||
return this;
|
||||
}
|
||||
@@ -116,85 +163,358 @@ export class Statistics {
|
||||
}
|
||||
|
||||
logStats(): Statistics {
|
||||
this._logger.info('Statistics');
|
||||
this._logProcessedIssuesCount();
|
||||
this._logStaleIssuesCount();
|
||||
this._logUndoStaleIssuesCount();
|
||||
this._logOperationsCount();
|
||||
this._logClosedIssuesCount();
|
||||
this._logDeletedLabelsCount();
|
||||
this._logDeletedCloseLabelsCount();
|
||||
this._logger.info(chalk.yellow.bold('Statistics:'));
|
||||
this._logProcessedIssuesAndPullRequestsCount();
|
||||
this._logStaleIssuesAndPullRequestsCount();
|
||||
this._logUndoStaleIssuesAndPullRequestsCount();
|
||||
this._logClosedIssuesAndPullRequestsCount();
|
||||
this._logDeletedIssuesAndPullRequestsLabelsCount();
|
||||
this._logDeletedCloseIssuesAndPullRequestsLabelsCount();
|
||||
this._logDeletedBranchesCount();
|
||||
this._logAddedLabelsCount();
|
||||
this._logAddedCommentsCount();
|
||||
this._logFetchedIssuesCount();
|
||||
this._logFetchedIssuesEventsCount();
|
||||
this._logFetchedIssuesCommentsCount();
|
||||
this._logAddedIssuesAndPullRequestsLabelsCount();
|
||||
this._logAddedIssuesAndPullRequestsCommentsCount();
|
||||
this._logFetchedItemsCount();
|
||||
this._logFetchedItemsEventsCount();
|
||||
this._logFetchedItemsCommentsCount();
|
||||
this._logFetchedPullRequestsCount();
|
||||
this._logger.info('---');
|
||||
this._logOperationsCount();
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
private _logProcessedIssuesCount(): void {
|
||||
this._logCount('Processed issues/PRs', this._processedIssuesCount);
|
||||
private _incrementProcessedIssuesCount(
|
||||
increment: Readonly<number> = 1
|
||||
): Statistics {
|
||||
this._processedIssuesCount += increment;
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
private _logStaleIssuesCount(): void {
|
||||
this._logCount('New stale issues/PRs', this._staleIssuesCount);
|
||||
private _incrementProcessedPullRequestsCount(
|
||||
increment: Readonly<number> = 1
|
||||
): Statistics {
|
||||
this._processedPullRequestsCount += increment;
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
private _logUndoStaleIssuesCount(): void {
|
||||
this._logCount('No longer stale issues/PRs', this._undoStaleIssuesCount);
|
||||
private _incrementStaleIssuesCount(
|
||||
increment: Readonly<number> = 1
|
||||
): Statistics {
|
||||
this._staleIssuesCount += increment;
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
private _logOperationsCount(): void {
|
||||
this._logCount('Operations performed', this._operationsCount);
|
||||
private _incrementStalePullRequestsCount(
|
||||
increment: Readonly<number> = 1
|
||||
): Statistics {
|
||||
this._stalePullRequestsCount += increment;
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
private _logClosedIssuesCount(): void {
|
||||
this._logCount('Closed issues', this._closedIssuesCount);
|
||||
private _incrementUndoStaleIssuesCount(
|
||||
increment: Readonly<number> = 1
|
||||
): Statistics {
|
||||
this._undoStaleIssuesCount += increment;
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
private _logDeletedLabelsCount(): void {
|
||||
this._logCount('Deleted labels', this._deletedLabelsCount);
|
||||
private _incrementUndoStalePullRequestsCount(
|
||||
increment: Readonly<number> = 1
|
||||
): Statistics {
|
||||
this._undoStalePullRequestsCount += increment;
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
private _logDeletedCloseLabelsCount(): void {
|
||||
this._logCount('Deleted close labels', this._deletedCloseLabelsCount);
|
||||
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 _logAddedLabelsCount(): void {
|
||||
this._logCount('Added labels', this._addedLabelsCount);
|
||||
private _logAddedIssuesAndPullRequestsLabelsCount(): void {
|
||||
this._logGroup('Added items labels', [
|
||||
{
|
||||
name: 'Added issues labels',
|
||||
count: this._addedIssuesLabelsCount
|
||||
},
|
||||
{
|
||||
name: 'Added PRs labels',
|
||||
count: this._addedPullRequestsLabelsCount
|
||||
}
|
||||
]);
|
||||
}
|
||||
|
||||
private _logAddedCommentsCount(): void {
|
||||
this._logCount('Added comments', this._addedCommentsCount);
|
||||
private _logAddedIssuesAndPullRequestsCommentsCount(): void {
|
||||
this._logGroup('Added items comments', [
|
||||
{
|
||||
name: 'Added issues comments',
|
||||
count: this._addedIssuesCommentsCount
|
||||
},
|
||||
{
|
||||
name: 'Added PRs comments',
|
||||
count: this._addedPullRequestsCommentsCount
|
||||
}
|
||||
]);
|
||||
}
|
||||
|
||||
private _logFetchedIssuesCount(): void {
|
||||
this._logCount('Fetched issues', this._fetchedIssuesCount);
|
||||
private _logFetchedItemsCount(): void {
|
||||
this._logCount('Fetched items', this._fetchedItemsCount);
|
||||
}
|
||||
|
||||
private _logFetchedIssuesEventsCount(): void {
|
||||
this._logCount('Fetched issues events', this._fetchedIssuesEventsCount);
|
||||
private _logFetchedItemsEventsCount(): void {
|
||||
this._logCount('Fetched items events', this._fetchedItemsEventsCount);
|
||||
}
|
||||
|
||||
private _logFetchedIssuesCommentsCount(): void {
|
||||
this._logCount('Fetched issues comments', this._fetchedIssuesCommentsCount);
|
||||
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}: ${count}`);
|
||||
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'
|
||||
}
|
||||
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;
|
||||
}
|
||||
@@ -22,8 +22,12 @@ export interface IIssuesProcessorOptions {
|
||||
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;
|
||||
|
||||
23
src/main.ts
23
src/main.ts
@@ -1,14 +1,13 @@
|
||||
import * as core from '@actions/core';
|
||||
import {isValidDate} from './functions/dates/is-valid-date';
|
||||
import {IssuesProcessor} from './classes/issues-processor';
|
||||
import {isValidDate} from './functions/dates/is-valid-date';
|
||||
import {IIssuesProcessorOptions} from './interfaces/issues-processor-options';
|
||||
|
||||
async function _run(): Promise<void> {
|
||||
try {
|
||||
const args = _getAndValidateArgs();
|
||||
|
||||
const processor: IssuesProcessor = new IssuesProcessor(args);
|
||||
await processor.processIssues();
|
||||
await new IssuesProcessor(args).processIssues();
|
||||
} catch (error) {
|
||||
core.error(error);
|
||||
core.setFailed(error.message);
|
||||
@@ -42,12 +41,20 @@ function _getAndValidateArgs(): IIssuesProcessorOptions {
|
||||
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',
|
||||
@@ -78,7 +85,9 @@ function _getAndValidateArgs(): IIssuesProcessorOptions {
|
||||
'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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -86,9 +95,9 @@ function _getAndValidateArgs(): IIssuesProcessorOptions {
|
||||
// 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user