feat(statistics): display some stats in the logs (#337)
* test: add more coverage * docs: reorder and enhance typo * docs(contributing): add more information about the npm scripts * feat(statistics): add simple statistics * feat(statistics): add more stats * refactor(issues-processor): remove some options from the constructor it should have been only useful for the tests * feat(statistics): add stats for new stale or undo stale issues * chore(rebase): handle rebase conflicts
This commit is contained in:
parent
63ae8ac024
commit
419a53bc05
114
README.md
114
README.md
|
@ -4,56 +4,51 @@ Warns and then closes issues and PRs that have had no activity for a specified a
|
||||||
|
|
||||||
### Arguments
|
### Arguments
|
||||||
|
|
||||||
| Input | Description | Usage |
|
| Input | Description | Usage |
|
||||||
| ----------------------------- | ----------------------------------------------------------------------------------------------------------------------------------- | -------- |
|
| ----------------------------- | ------------------------------------------------------------------------------------------------------------------------------------ | -------- |
|
||||||
| `repo-token` | PAT(Personal Access Token) for authorizing repository. _Defaults to **${{ github.token }}**_ | Optional |
|
| `repo-token` | PAT(Personal Access Token) for authorizing repository. _Defaults to **${{ github.token }}**_ | Optional |
|
||||||
| `days-before-stale` | Idle number of days before marking an issue/PR as stale. _Defaults to **60**_ | Optional |
|
| `days-before-stale` | Idle number of days before marking an issue/PR as stale. _Defaults to **60**_ | Optional |
|
||||||
| `days-before-issue-stale` | Idle number of days before marking an issue as stale (override `days-before-stale`). | Optional |
|
| `days-before-issue-stale` | Idle number of days before marking an issue as stale (override `days-before-stale`). | Optional |
|
||||||
| `days-before-pr-stale` | Idle number of days before marking an PR as stale (override `days-before-stale`). | Optional |
|
| `days-before-pr-stale` | Idle number of days before marking an PR as stale (override `days-before-stale`). | Optional |
|
||||||
| `days-before-close` | Idle number of days before closing an stale issue/PR. _Defaults to **7**_ | Optional |
|
| `days-before-close` | Idle number of days before closing an stale issue/PR. _Defaults to **7**_ | Optional |
|
||||||
| `days-before-issue-close` | Idle number of days before closing an stale issue (override `days-before-close`). | Optional |
|
| `days-before-issue-close` | Idle number of days before closing an stale issue (override `days-before-close`). | Optional |
|
||||||
| `days-before-pr-close` | Idle number of days before closing an stale PR (override `days-before-close`). | Optional |
|
| `days-before-pr-close` | Idle number of days before closing an stale PR (override `days-before-close`). | Optional |
|
||||||
| `stale-issue-message` | Message to post on the stale issue. | Optional |
|
| `stale-issue-message` | Message to post on the stale issue. | Optional |
|
||||||
| `stale-pr-message` | Message to post on the stale PR. | Optional |
|
| `stale-pr-message` | Message to post on the stale PR. | Optional |
|
||||||
| `close-issue-message` | Message to post on the stale issue while closing it. | Optional |
|
| `close-issue-message` | Message to post on the stale issue while closing it. | Optional |
|
||||||
| `close-pr-message` | Message to post on the stale PR while closing it. | Optional |
|
| `close-pr-message` | Message to post on the stale PR while closing it. | Optional |
|
||||||
| `stale-issue-label` | Label to apply on the stale issue. _Defaults to **Stale**_ | Optional |
|
| `stale-issue-label` | Label to apply on the stale issue. _Defaults to **Stale**_ | Optional |
|
||||||
| `close-issue-label` | Label to apply on closing issue (automatically removed if no longer closed nor locked). | Optional |
|
| `close-issue-label` | Label to apply on closing issue (automatically removed if no longer closed nor locked). | Optional |
|
||||||
| `stale-pr-label` | Label to apply on the stale PR. _Defaults to **Stale**_ | Optional |
|
| `stale-pr-label` | Label to apply on the stale PR. _Defaults to **Stale**_ | Optional |
|
||||||
| `close-pr-label` | Label to apply on the closing PR (automatically removed if no longer closed nor locked). | Optional |
|
| `close-pr-label` | Label to apply on the closing PR (automatically removed if no longer closed nor locked). | Optional |
|
||||||
| `exempt-issue-labels` | Labels on an issue exempted from being marked as stale. | Optional |
|
| `exempt-issue-labels` | Labels on an issue exempted from being marked as stale. | Optional |
|
||||||
| `exempt-pr-labels` | Labels on the PR exempted from being marked as stale. | Optional |
|
| `exempt-pr-labels` | Labels on the PR exempted from being marked as stale. | Optional |
|
||||||
| `exempt-milestones` | Milestones on an issue or a PR exempted from being marked as stale. | Optional |
|
| `only-labels` | Only issues and PRs with ALL these labels are checked. Separate multiple labels with commas (eg. "question,answered"). | Optional |
|
||||||
| `exempt-issue-milestones` | Milestones on an issue exempted from being marked as stale (override `exempt-milestones`). | Optional |
|
| `only-labels` | Only labels checked for stale issue/PR. | Optional |
|
||||||
| `exempt-pr-milestones` | Milestones on the PR exempted from being marked as stale (override `exempt-milestones`). | Optional |
|
| `only-issue-labels` | Only labels checked for stale issue (override `only-labels`). | Optional |
|
||||||
| `exempt-all-milestones` | Exempt all issues and PRs with milestones from being marked as stale. (priority over `exempt-milestones` rules) | Optional |
|
| `only-pr-labels` | Only labels checked for stale PR (override `only-labels`). | Optional |
|
||||||
| `exempt-all-issue-milestones` | Exempt all issues with milestones from being marked as stale. (override `exempt-all-milestones`). | Optional |
|
| `any-of-labels` | Only issues and PRs with ANY of these labels are checked. Separate multiple labels with commas (eg. "incomplete,waiting-feedback"). | Optional |
|
||||||
| `exempt-all-pr-milestones` | Exempt all PRs with milestones from being marked as stale. (override `exempt-all-milestones`). | Optional |
|
| `operations-per-run` | Maximum number of operations per run (GitHub API CRUD related). _Defaults to **30**_ | Optional |
|
||||||
| `only-labels` | Only issues and PRs with ALL these labels are checked. Separate multiple labels with commas (eg. "question,answered"). | Optional |
|
| `remove-stale-when-updated` | Remove stale label from issue/PR on updates or comments. _Defaults to **true**_ | Optional |
|
||||||
| `only-labels` | Only labels checked for stale issue/PR. | Optional |
|
| `debug-only` | Dry-run on action. _Defaults to **false**_ | Optional |
|
||||||
| `only-issue-labels` | Only labels checked for stale issue (override `only-labels`). | Optional |
|
| `ascending` | Order to get issues/PR. _Defaults to **false**_ | Optional |
|
||||||
| `only-pr-labels` | Only labels checked for stale PR (override `only-labels`). | Optional |
|
| `skip-stale-issue-message` | Skip adding stale message on stale issue. _Defaults to **false**_ | Optional |
|
||||||
| `any-of-labels` | Only issues and PRs with ANY of these labels are checked. Separate multiple labels with commas (eg. "incomplete,waiting-feedback"). | Optional |
|
| `skip-stale-pr-message` | Skip adding stale message on stale PR. _Defaults to **false**_ | Optional |
|
||||||
| `operations-per-run` | Maximum number of operations per run (GitHub API CRUD related). _Defaults to **30**_ | Optional |
|
| `start-date` | The date used to skip the stale action on issue/PR created before it (ISO 8601 or RFC 2822). | Optional |
|
||||||
| `remove-stale-when-updated` | Remove stale label from issue/PR on updates or comments. _Defaults to **true**_ | Optional |
|
| `delete-branch` | Delete the git branch after closing a stale pull request. _Defaults to **false**_ | Optional |
|
||||||
| `debug-only` | Dry-run on action. _Defaults to **false**_ | Optional |
|
| `exempt-milestones` | Milestones on an issue or a PR exempted from being marked as stale. | Optional |
|
||||||
| `ascending` | Order to get issues/PR. _Defaults to **false**_ | Optional |
|
| `exempt-issue-milestones` | Milestones on an issue exempted from being marked as stale (override `exempt-milestones`). | Optional |
|
||||||
| `skip-stale-issue-message` | Skip adding stale message on stale issue. _Defaults to **false**_ | Optional |
|
| `exempt-pr-milestones` | Milestones on the PR exempted from being marked as stale (override `exempt-milestones`). | Optional |
|
||||||
| `skip-stale-pr-message` | Skip adding stale message on stale PR. _Defaults to **false**_ | Optional |
|
| `exempt-all-milestones` | Exempt all issues and PRs with milestones from being marked as stale. (priority over `exempt-milestones` rules) | Optional |
|
||||||
| `start-date` | The date used to skip the stale action on issue/PR created before it (ISO 8601 or RFC 2822). | Optional |
|
| `exempt-all-issue-milestones` | Exempt all issues with milestones from being marked as stale. (override `exempt-all-milestones`). | Optional |
|
||||||
| `delete-branch` | Delete the git branch after closing a stale pull request. _Defaults to **false**_ | Optional |
|
| `exempt-all-pr-milestones` | Exempt all PRs with milestones from being marked as stale. (override `exempt-all-milestones`). | Optional |
|
||||||
| `exempt-milestones` | Milestones on an issue or a PR exempted from being marked as stale. | Optional |
|
| `exempt-assignees` | Assignees on an issue or a PR exempted from being marked as stale. | Optional |
|
||||||
| `exempt-issue-milestones` | Milestones on an issue exempted from being marked as stale (override `exempt-milestones`). | Optional |
|
| `exempt-issue-assignees` | Assignees on an issue exempted from being marked as stale (override `exempt-assignees`). | Optional |
|
||||||
| `exempt-pr-milestones` | Milestones on the PR exempted from being marked as stale (override `exempt-milestones`). | Optional |
|
| `exempt-pr-assignees` | Assignees on the PR exempted from being marked as stale (override `exempt-assignees`). | Optional |
|
||||||
| `exempt-all-milestones` | Exempt all issues and PRs with milestones from being marked as stale. (priority over `exempt-milestones` rules) | Optional |
|
| `exempt-all-assignees` | Exempt all issues and PRs with assignees from being marked as stale. (priority over `exempt-assignees` rules) | Optional |
|
||||||
| `exempt-all-issue-milestones` | Exempt all issues with milestones from being marked as stale. (override `exempt-all-milestones`). | Optional |
|
| `exempt-all-issue-assignees` | Exempt all issues with assignees from being marked as stale. (override `exempt-all-assignees`). | Optional |
|
||||||
| `exempt-all-pr-milestones` | Exempt all PRs with milestones from being marked as stale. (override `exempt-all-milestones`). | Optional |
|
| `exempt-all-pr-assignees` | Exempt all PRs with assignees from being marked as stale. (override `exempt-all-assignees`). | Optional |
|
||||||
| `exempt-assignees` | Assignees on an issue or a PR exempted from being marked as stale. | Optional |
|
| `enable-statistics` | Display some statistics at the end of the logs regarding the stale workflow (only when the logs are enabled). _Defaults to **true**_ | Optional |
|
||||||
| `exempt-issue-assignees` | Assignees on an issue exempted from being marked as stale (override `exempt-assignees`). | Optional |
|
|
||||||
| `exempt-pr-assignees` | Assignees on the PR exempted from being marked as stale (override `exempt-assignees`). | Optional |
|
|
||||||
| `exempt-all-assignees` | Exempt all issues and PRs with assignees from being marked as stale. (priority over `exempt-assignees` rules) | Optional |
|
|
||||||
| `exempt-all-issue-assignees` | Exempt all issues with assignees from being marked as stale. (override `exempt-all-assignees`). | Optional |
|
|
||||||
| `exempt-all-pr-assignees` | Exempt all PRs with assignees from being marked as stale. (override `exempt-all-assignees`). | Optional |
|
|
||||||
|
|
||||||
### Usage
|
### Usage
|
||||||
|
|
||||||
|
@ -275,10 +270,25 @@ jobs:
|
||||||
|
|
||||||
### Debugging
|
### Debugging
|
||||||
|
|
||||||
|
**Logs:**
|
||||||
To see the debug output from this action, you must set the secret `ACTIONS_STEP_DEBUG` to `true` in your repository.
|
To see the debug output from this action, you must set the secret `ACTIONS_STEP_DEBUG` to `true` in your repository.
|
||||||
|
There is a lot of logs so this can be very helpful!
|
||||||
|
|
||||||
|
**Statistics:**
|
||||||
|
If the logs are enabled, you can also enable the statistics log which will be visible at the end of the logs once all issues were processed.
|
||||||
|
This is very helpful to have a quick understanding of the whole stale workflow.
|
||||||
|
Set `enable-statistics` to `true` in your workflow configuration file.
|
||||||
|
|
||||||
|
**Dry-run:**
|
||||||
You can run this action in debug only mode (no actions will be taken on your issues and pull requests) by passing `debug-only` to `true` as an argument to the action.
|
You can run this action in debug only mode (no actions will be taken on your issues and pull requests) by passing `debug-only` to `true` as an argument to the action.
|
||||||
You can also increase the maximum number of operations per run by passing `operations-per-run` to `100` for example.
|
|
||||||
Finally, you could also change the cron job frequency in the stale workflow to run stale more often.
|
**More operations:**
|
||||||
|
You can increase the maximum number of operations per run by passing `operations-per-run` to `1000` for example which will help you to handle more operations in a single stale workflow run.
|
||||||
|
If the `debug-only` option is enabled, this is very helpful because the workflow will (almost) never reach the GitHub API rate, and you will be able to deep-dive into the logs.
|
||||||
|
|
||||||
|
**Job frequency:**
|
||||||
|
You could change the cron job frequency in the stale workflow to run the stale workflow more often.
|
||||||
|
Usually this is not very helpful though.
|
||||||
|
|
||||||
### Contributing
|
### Contributing
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
import {Issue} from '../src/classes/issue';
|
import {Issue} from '../src/classes/issue';
|
||||||
import {IssuesProcessor} from '../src/classes/issues-processor';
|
|
||||||
import {IIssuesProcessorOptions} from '../src/interfaces/issues-processor-options';
|
import {IIssuesProcessorOptions} from '../src/interfaces/issues-processor-options';
|
||||||
|
import {IssuesProcessorMock} from './classes/issues-processor-mock';
|
||||||
import {DefaultProcessorOptions} from './constants/default-processor-options';
|
import {DefaultProcessorOptions} from './constants/default-processor-options';
|
||||||
import {generateIssue} from './functions/generate-issue';
|
import {generateIssue} from './functions/generate-issue';
|
||||||
|
|
||||||
|
@ -95,8 +95,8 @@ class IssuesProcessorBuilder {
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
build(): IssuesProcessor {
|
build(): IssuesProcessorMock {
|
||||||
return new IssuesProcessor(
|
return new IssuesProcessorMock(
|
||||||
this._options,
|
this._options,
|
||||||
async () => 'abot',
|
async () => 'abot',
|
||||||
async p => (p === 1 ? this._issues : []),
|
async p => (p === 1 ? this._issues : []),
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
import {Issue} from '../src/classes/issue';
|
import {Issue} from '../src/classes/issue';
|
||||||
import {IssuesProcessor} from '../src/classes/issues-processor';
|
|
||||||
import {IIssuesProcessorOptions} from '../src/interfaces/issues-processor-options';
|
import {IIssuesProcessorOptions} from '../src/interfaces/issues-processor-options';
|
||||||
|
import {IssuesProcessorMock} from './classes/issues-processor-mock';
|
||||||
import {DefaultProcessorOptions} from './constants/default-processor-options';
|
import {DefaultProcessorOptions} from './constants/default-processor-options';
|
||||||
import {generateIssue} from './functions/generate-issue';
|
import {generateIssue} from './functions/generate-issue';
|
||||||
|
|
||||||
|
@ -21,7 +21,7 @@ interface ITestData {
|
||||||
describe('assignees options', (): void => {
|
describe('assignees options', (): void => {
|
||||||
let opts: IIssuesProcessorOptions;
|
let opts: IIssuesProcessorOptions;
|
||||||
let testIssueList: Issue[];
|
let testIssueList: Issue[];
|
||||||
let processor: IssuesProcessor;
|
let processor: IssuesProcessorMock;
|
||||||
|
|
||||||
const setTestIssueList = (
|
const setTestIssueList = (
|
||||||
isPullRequest: boolean,
|
isPullRequest: boolean,
|
||||||
|
@ -46,7 +46,7 @@ describe('assignees options', (): void => {
|
||||||
};
|
};
|
||||||
|
|
||||||
const setProcessor = () => {
|
const setProcessor = () => {
|
||||||
processor = new IssuesProcessor(
|
processor = new IssuesProcessorMock(
|
||||||
opts,
|
opts,
|
||||||
async () => 'abot',
|
async () => 'abot',
|
||||||
async p => (p === 1 ? testIssueList : []),
|
async p => (p === 1 ? testIssueList : []),
|
||||||
|
|
|
@ -0,0 +1,38 @@
|
||||||
|
import {Issue} from '../../src/classes/issue';
|
||||||
|
import {IssuesProcessor} from '../../src/classes/issues-processor';
|
||||||
|
import {IComment} from '../../src/interfaces/comment';
|
||||||
|
import {IIssuesProcessorOptions} from '../../src/interfaces/issues-processor-options';
|
||||||
|
|
||||||
|
export class IssuesProcessorMock extends IssuesProcessor {
|
||||||
|
constructor(
|
||||||
|
options: IIssuesProcessorOptions,
|
||||||
|
getActor?: () => Promise<string>,
|
||||||
|
getIssues?: (page: number) => Promise<Issue[]>,
|
||||||
|
listIssueComments?: (
|
||||||
|
issueNumber: number,
|
||||||
|
sinceDate: string
|
||||||
|
) => Promise<IComment[]>,
|
||||||
|
getLabelCreationDate?: (
|
||||||
|
issue: Issue,
|
||||||
|
label: string
|
||||||
|
) => Promise<string | undefined>
|
||||||
|
) {
|
||||||
|
super(options);
|
||||||
|
|
||||||
|
if (getActor) {
|
||||||
|
this.getActor = getActor;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (getIssues) {
|
||||||
|
this.getIssues = getIssues;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (listIssueComments) {
|
||||||
|
this.listIssueComments = listIssueComments;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (getLabelCreationDate) {
|
||||||
|
this.getLabelCreationDate = getLabelCreationDate;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -41,5 +41,6 @@ export const DefaultProcessorOptions: IIssuesProcessorOptions = Object.freeze({
|
||||||
exemptPrAssignees: '',
|
exemptPrAssignees: '',
|
||||||
exemptAllAssignees: false,
|
exemptAllAssignees: false,
|
||||||
exemptAllIssueAssignees: undefined,
|
exemptAllIssueAssignees: undefined,
|
||||||
exemptAllPrAssignees: undefined
|
exemptAllPrAssignees: undefined,
|
||||||
|
enableStatistics: false
|
||||||
});
|
});
|
||||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -1,6 +1,6 @@
|
||||||
import {Issue} from '../src/classes/issue';
|
import {Issue} from '../src/classes/issue';
|
||||||
import {IssuesProcessor} from '../src/classes/issues-processor';
|
|
||||||
import {IIssuesProcessorOptions} from '../src/interfaces/issues-processor-options';
|
import {IIssuesProcessorOptions} from '../src/interfaces/issues-processor-options';
|
||||||
|
import {IssuesProcessorMock} from './classes/issues-processor-mock';
|
||||||
import {DefaultProcessorOptions} from './constants/default-processor-options';
|
import {DefaultProcessorOptions} from './constants/default-processor-options';
|
||||||
import {generateIssue} from './functions/generate-issue';
|
import {generateIssue} from './functions/generate-issue';
|
||||||
|
|
||||||
|
@ -14,7 +14,7 @@ interface ITestData {
|
||||||
describe('milestones options', (): void => {
|
describe('milestones options', (): void => {
|
||||||
let opts: IIssuesProcessorOptions;
|
let opts: IIssuesProcessorOptions;
|
||||||
let testIssueList: Issue[];
|
let testIssueList: Issue[];
|
||||||
let processor: IssuesProcessor;
|
let processor: IssuesProcessorMock;
|
||||||
|
|
||||||
const setTestIssueList = (
|
const setTestIssueList = (
|
||||||
isPullRequest: boolean,
|
isPullRequest: boolean,
|
||||||
|
@ -37,7 +37,7 @@ describe('milestones options', (): void => {
|
||||||
};
|
};
|
||||||
|
|
||||||
const setProcessor = () => {
|
const setProcessor = () => {
|
||||||
processor = new IssuesProcessor(
|
processor = new IssuesProcessorMock(
|
||||||
opts,
|
opts,
|
||||||
async () => 'abot',
|
async () => 'abot',
|
||||||
async p => (p === 1 ? testIssueList : []),
|
async p => (p === 1 ? testIssueList : []),
|
||||||
|
|
|
@ -1,12 +1,12 @@
|
||||||
import {Issue} from '../src/classes/issue';
|
import {Issue} from '../src/classes/issue';
|
||||||
import {IssuesProcessor} from '../src/classes/issues-processor';
|
|
||||||
import {IIssue} from '../src/interfaces/issue';
|
import {IIssue} from '../src/interfaces/issue';
|
||||||
import {IIssuesProcessorOptions} from '../src/interfaces/issues-processor-options';
|
import {IIssuesProcessorOptions} from '../src/interfaces/issues-processor-options';
|
||||||
|
import {IssuesProcessorMock} from './classes/issues-processor-mock';
|
||||||
import {DefaultProcessorOptions} from './constants/default-processor-options';
|
import {DefaultProcessorOptions} from './constants/default-processor-options';
|
||||||
import {generateIssue} from './functions/generate-issue';
|
import {generateIssue} from './functions/generate-issue';
|
||||||
|
|
||||||
let issuesProcessorBuilder: IssuesProcessorBuilder;
|
let issuesProcessorBuilder: IssuesProcessorBuilder;
|
||||||
let issuesProcessor: IssuesProcessor;
|
let issuesProcessor: IssuesProcessorMock;
|
||||||
|
|
||||||
describe('only-labels option', (): void => {
|
describe('only-labels option', (): void => {
|
||||||
beforeEach((): void => {
|
beforeEach((): void => {
|
||||||
|
@ -1140,8 +1140,8 @@ class IssuesProcessorBuilder {
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
build(): IssuesProcessor {
|
build(): IssuesProcessorMock {
|
||||||
return new IssuesProcessor(
|
return new IssuesProcessorMock(
|
||||||
this._options,
|
this._options,
|
||||||
async () => 'abot',
|
async () => 'abot',
|
||||||
async p => (p === 1 ? this._issues : []),
|
async p => (p === 1 ? this._issues : []),
|
||||||
|
|
|
@ -156,6 +156,10 @@ inputs:
|
||||||
description: 'Exempt all pull requests with assignees from being marked as stale. Override "exempt-all-assignees" option regarding only the pull requests.'
|
description: 'Exempt all pull requests with assignees from being marked as stale. Override "exempt-all-assignees" option regarding only the pull requests.'
|
||||||
default: ''
|
default: ''
|
||||||
required: false
|
required: false
|
||||||
|
enable-statistics:
|
||||||
|
description: 'Display some statistics at the end regarding the stale workflow (only when the logs are enabled).'
|
||||||
|
default: 'true'
|
||||||
|
required: false
|
||||||
runs:
|
runs:
|
||||||
using: 'node12'
|
using: 'node12'
|
||||||
main: 'dist/index.js'
|
main: 'dist/index.js'
|
||||||
|
|
|
@ -206,7 +206,6 @@ const github_1 = __nccwpck_require__(5438);
|
||||||
const get_humanized_date_1 = __nccwpck_require__(965);
|
const get_humanized_date_1 = __nccwpck_require__(965);
|
||||||
const is_date_more_recent_than_1 = __nccwpck_require__(1473);
|
const is_date_more_recent_than_1 = __nccwpck_require__(1473);
|
||||||
const is_valid_date_1 = __nccwpck_require__(891);
|
const is_valid_date_1 = __nccwpck_require__(891);
|
||||||
const get_issue_type_1 = __nccwpck_require__(5153);
|
|
||||||
const is_labeled_1 = __nccwpck_require__(6792);
|
const is_labeled_1 = __nccwpck_require__(6792);
|
||||||
const is_pull_request_1 = __nccwpck_require__(5400);
|
const is_pull_request_1 = __nccwpck_require__(5400);
|
||||||
const should_mark_when_stale_1 = __nccwpck_require__(2461);
|
const should_mark_when_stale_1 = __nccwpck_require__(2461);
|
||||||
|
@ -216,11 +215,12 @@ const issue_1 = __nccwpck_require__(4783);
|
||||||
const issue_logger_1 = __nccwpck_require__(2984);
|
const issue_logger_1 = __nccwpck_require__(2984);
|
||||||
const logger_1 = __nccwpck_require__(6212);
|
const logger_1 = __nccwpck_require__(6212);
|
||||||
const milestones_1 = __nccwpck_require__(4601);
|
const milestones_1 = __nccwpck_require__(4601);
|
||||||
|
const statistics_1 = __nccwpck_require__(3334);
|
||||||
/***
|
/***
|
||||||
* Handle processing of issues for staleness/closure.
|
* Handle processing of issues for staleness/closure.
|
||||||
*/
|
*/
|
||||||
class IssuesProcessor {
|
class IssuesProcessor {
|
||||||
constructor(options, getActor, getIssues, listIssueComments, getLabelCreationDate) {
|
constructor(options) {
|
||||||
this._logger = new logger_1.Logger();
|
this._logger = new logger_1.Logger();
|
||||||
this._operationsLeft = 0;
|
this._operationsLeft = 0;
|
||||||
this.staleIssues = [];
|
this.staleIssues = [];
|
||||||
|
@ -228,23 +228,14 @@ class IssuesProcessor {
|
||||||
this.deletedBranchIssues = [];
|
this.deletedBranchIssues = [];
|
||||||
this.removedLabelIssues = [];
|
this.removedLabelIssues = [];
|
||||||
this.options = options;
|
this.options = options;
|
||||||
this._operationsLeft = options.operationsPerRun;
|
this._operationsLeft = this.options.operationsPerRun;
|
||||||
this.client = github_1.getOctokit(options.repoToken);
|
this.client = github_1.getOctokit(this.options.repoToken);
|
||||||
if (getActor) {
|
|
||||||
this._getActor = getActor;
|
|
||||||
}
|
|
||||||
if (getIssues) {
|
|
||||||
this._getIssues = getIssues;
|
|
||||||
}
|
|
||||||
if (listIssueComments) {
|
|
||||||
this._listIssueComments = listIssueComments;
|
|
||||||
}
|
|
||||||
if (getLabelCreationDate) {
|
|
||||||
this._getLabelCreationDate = getLabelCreationDate;
|
|
||||||
}
|
|
||||||
if (this.options.debugOnly) {
|
if (this.options.debugOnly) {
|
||||||
this._logger.warning('Executing in debug mode. Debug output will be written but no issues will be processed.');
|
this._logger.warning('Executing in debug mode. Debug output will be written but no issues will be processed.');
|
||||||
}
|
}
|
||||||
|
if (this.options.enableStatistics) {
|
||||||
|
this._statistics = new statistics_1.Statistics(this.options);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
static _updatedSince(timestamp, num_days) {
|
static _updatedSince(timestamp, num_days) {
|
||||||
const daysInMillis = 1000 * 60 * 60 * 24 * num_days;
|
const daysInMillis = 1000 * 60 * 60 * 24 * num_days;
|
||||||
|
@ -252,18 +243,20 @@ class IssuesProcessor {
|
||||||
return millisSinceLastUpdated <= daysInMillis;
|
return millisSinceLastUpdated <= daysInMillis;
|
||||||
}
|
}
|
||||||
processIssues(page = 1) {
|
processIssues(page = 1) {
|
||||||
|
var _a, _b;
|
||||||
return __awaiter(this, void 0, void 0, function* () {
|
return __awaiter(this, void 0, void 0, function* () {
|
||||||
// get the next batch of issues
|
// get the next batch of issues
|
||||||
const issues = yield this._getIssues(page);
|
const issues = yield this.getIssues(page);
|
||||||
this._operationsLeft -= 1;
|
const actor = yield this.getActor();
|
||||||
const actor = yield this._getActor();
|
|
||||||
if (issues.length <= 0) {
|
if (issues.length <= 0) {
|
||||||
this._logger.info('---');
|
this._logger.info('---');
|
||||||
|
(_a = this._statistics) === null || _a === void 0 ? void 0 : _a.setOperationsLeft(this._operationsLeft).logStats();
|
||||||
this._logger.info('No more issues found to process. Exiting.');
|
this._logger.info('No more issues found to process. Exiting.');
|
||||||
return this._operationsLeft;
|
return this._operationsLeft;
|
||||||
}
|
}
|
||||||
for (const issue of issues.values()) {
|
for (const issue of issues.values()) {
|
||||||
const issueLogger = new issue_logger_1.IssueLogger(issue);
|
const issueLogger = new issue_logger_1.IssueLogger(issue);
|
||||||
|
(_b = this._statistics) === null || _b === void 0 ? void 0 : _b.incrementProcessedIssuesCount();
|
||||||
issueLogger.info(`Found this $$type last updated ${issue.updated_at}`);
|
issueLogger.info(`Found this $$type last updated ${issue.updated_at}`);
|
||||||
// calculate string based messages for this issue
|
// calculate string based messages for this issue
|
||||||
const staleMessage = issue.isPullRequest
|
const staleMessage = issue.isPullRequest
|
||||||
|
@ -281,7 +274,6 @@ class IssuesProcessor {
|
||||||
const skipMessage = issue.isPullRequest
|
const skipMessage = issue.isPullRequest
|
||||||
? this.options.skipStalePrMessage
|
? this.options.skipStalePrMessage
|
||||||
: this.options.skipStaleIssueMessage;
|
: this.options.skipStaleIssueMessage;
|
||||||
const issueType = get_issue_type_1.getIssueType(issue.isPullRequest);
|
|
||||||
const daysBeforeStale = issue.isPullRequest
|
const daysBeforeStale = issue.isPullRequest
|
||||||
? this._getDaysBeforePrStale()
|
? this._getDaysBeforePrStale()
|
||||||
: this._getDaysBeforeIssueStale();
|
: this._getDaysBeforeIssueStale();
|
||||||
|
@ -353,7 +345,7 @@ class IssuesProcessor {
|
||||||
const anyOfLabels = words_to_list_1.wordsToList(this.options.anyOfLabels);
|
const anyOfLabels = words_to_list_1.wordsToList(this.options.anyOfLabels);
|
||||||
if (anyOfLabels.length &&
|
if (anyOfLabels.length &&
|
||||||
!anyOfLabels.some((label) => is_labeled_1.isLabeled(issue, label))) {
|
!anyOfLabels.some((label) => is_labeled_1.isLabeled(issue, label))) {
|
||||||
issueLogger.info(`Skipping ${issueType} because it does not have any of the required labels`);
|
issueLogger.info(`Skipping $$type because it does not have any of the required labels`);
|
||||||
continue; // don't process issues without any of the required labels
|
continue; // don't process issues without any of the required labels
|
||||||
}
|
}
|
||||||
const milestones = new milestones_1.Milestones(this.options, issue);
|
const milestones = new milestones_1.Milestones(this.options, issue);
|
||||||
|
@ -379,7 +371,7 @@ class IssuesProcessor {
|
||||||
// process the issue if it was marked stale
|
// process the issue if it was marked stale
|
||||||
if (issue.isStale) {
|
if (issue.isStale) {
|
||||||
issueLogger.info(`Found a stale $$type`);
|
issueLogger.info(`Found a stale $$type`);
|
||||||
yield this._processStaleIssue(issue, issueType, staleLabel, actor, closeMessage, closeLabel);
|
yield this._processStaleIssue(issue, staleLabel, actor, closeMessage, closeLabel);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (this._operationsLeft <= 0) {
|
if (this._operationsLeft <= 0) {
|
||||||
|
@ -390,11 +382,97 @@ class IssuesProcessor {
|
||||||
return this.processIssues(page + 1);
|
return this.processIssues(page + 1);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
// handle all of the stale issue logic when we find a stale issue
|
// grab comments for an issue since a given date
|
||||||
_processStaleIssue(issue, issueType, staleLabel, actor, closeMessage, closeLabel) {
|
listIssueComments(issueNumber, sinceDate) {
|
||||||
|
var _a;
|
||||||
|
return __awaiter(this, void 0, void 0, function* () {
|
||||||
|
// find any comments since date on the given issue
|
||||||
|
try {
|
||||||
|
this._operationsLeft -= 1;
|
||||||
|
(_a = this._statistics) === null || _a === void 0 ? void 0 : _a.incrementFetchedIssuesCommentsCount();
|
||||||
|
const comments = yield this.client.issues.listComments({
|
||||||
|
owner: github_1.context.repo.owner,
|
||||||
|
repo: github_1.context.repo.repo,
|
||||||
|
issue_number: issueNumber,
|
||||||
|
since: sinceDate
|
||||||
|
});
|
||||||
|
return comments.data;
|
||||||
|
}
|
||||||
|
catch (error) {
|
||||||
|
this._logger.error(`List issue comments error: ${error.message}`);
|
||||||
|
return Promise.resolve([]);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
// get the actor from the GitHub token or context
|
||||||
|
getActor() {
|
||||||
|
return __awaiter(this, void 0, void 0, function* () {
|
||||||
|
let actor;
|
||||||
|
try {
|
||||||
|
this._operationsLeft -= 1;
|
||||||
|
actor = yield this.client.users.getAuthenticated();
|
||||||
|
}
|
||||||
|
catch (error) {
|
||||||
|
return github_1.context.actor;
|
||||||
|
}
|
||||||
|
return actor.data.login;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
// grab issues from github in batches of 100
|
||||||
|
getIssues(page) {
|
||||||
|
var _a;
|
||||||
|
return __awaiter(this, void 0, void 0, function* () {
|
||||||
|
// generate type for response
|
||||||
|
const endpoint = this.client.issues.listForRepo;
|
||||||
|
try {
|
||||||
|
this._operationsLeft -= 1;
|
||||||
|
(_a = this._statistics) === null || _a === void 0 ? void 0 : _a.incrementFetchedIssuesCount();
|
||||||
|
const issueResult = yield this.client.issues.listForRepo({
|
||||||
|
owner: github_1.context.repo.owner,
|
||||||
|
repo: github_1.context.repo.repo,
|
||||||
|
state: 'open',
|
||||||
|
per_page: 100,
|
||||||
|
direction: this.options.ascending ? 'asc' : 'desc',
|
||||||
|
page
|
||||||
|
});
|
||||||
|
return issueResult.data.map((issue) => new issue_1.Issue(this.options, issue));
|
||||||
|
}
|
||||||
|
catch (error) {
|
||||||
|
this._logger.error(`Get issues for repo error: ${error.message}`);
|
||||||
|
return Promise.resolve([]);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
// returns the creation date of a given label on an issue (or nothing if no label existed)
|
||||||
|
///see https://developer.github.com/v3/activity/events/
|
||||||
|
getLabelCreationDate(issue, label) {
|
||||||
|
var _a;
|
||||||
return __awaiter(this, void 0, void 0, function* () {
|
return __awaiter(this, void 0, void 0, function* () {
|
||||||
const issueLogger = new issue_logger_1.IssueLogger(issue);
|
const issueLogger = new issue_logger_1.IssueLogger(issue);
|
||||||
const markedStaleOn = (yield this._getLabelCreationDate(issue, staleLabel)) || issue.updated_at;
|
issueLogger.info(`Checking for label on $$type`);
|
||||||
|
this._operationsLeft -= 1;
|
||||||
|
(_a = this._statistics) === null || _a === void 0 ? void 0 : _a.incrementFetchedIssuesEventsCount();
|
||||||
|
const options = this.client.issues.listEvents.endpoint.merge({
|
||||||
|
owner: github_1.context.repo.owner,
|
||||||
|
repo: github_1.context.repo.repo,
|
||||||
|
per_page: 100,
|
||||||
|
issue_number: issue.number
|
||||||
|
});
|
||||||
|
const events = yield this.client.paginate(options);
|
||||||
|
const reversedEvents = events.reverse();
|
||||||
|
const staleLabeledEvent = reversedEvents.find(event => event.event === 'labeled' && event.label.name === label);
|
||||||
|
if (!staleLabeledEvent) {
|
||||||
|
// Must be old rather than labeled
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
return staleLabeledEvent.created_at;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
// handle all of the stale issue logic when we find a stale issue
|
||||||
|
_processStaleIssue(issue, staleLabel, actor, closeMessage, closeLabel) {
|
||||||
|
return __awaiter(this, void 0, void 0, function* () {
|
||||||
|
const issueLogger = new issue_logger_1.IssueLogger(issue);
|
||||||
|
const markedStaleOn = (yield this.getLabelCreationDate(issue, staleLabel)) || issue.updated_at;
|
||||||
issueLogger.info(`$$type marked stale on: ${markedStaleOn}`);
|
issueLogger.info(`$$type marked stale on: ${markedStaleOn}`);
|
||||||
const issueHasComments = yield this._hasCommentsSince(issue, markedStaleOn, actor);
|
const issueHasComments = yield this._hasCommentsSince(issue, markedStaleOn, actor);
|
||||||
issueLogger.info(`$$type has been commented on: ${issueHasComments}`);
|
issueLogger.info(`$$type has been commented on: ${issueHasComments}`);
|
||||||
|
@ -436,74 +514,20 @@ class IssuesProcessor {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
// find any comments since the date
|
// find any comments since the date
|
||||||
const comments = yield this._listIssueComments(issue.number, sinceDate);
|
const comments = yield this.listIssueComments(issue.number, sinceDate);
|
||||||
const filteredComments = comments.filter(comment => comment.user.type === 'User' && comment.user.login !== actor);
|
const filteredComments = comments.filter(comment => comment.user.type === 'User' && comment.user.login !== actor);
|
||||||
issueLogger.info(`Comments not made by actor or another bot: ${filteredComments.length}`);
|
issueLogger.info(`Comments not made by actor or another bot: ${filteredComments.length}`);
|
||||||
// if there are any user comments returned
|
// if there are any user comments returned
|
||||||
return filteredComments.length > 0;
|
return filteredComments.length > 0;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
// grab comments for an issue since a given date
|
|
||||||
_listIssueComments(issueNumber, sinceDate) {
|
|
||||||
return __awaiter(this, void 0, void 0, function* () {
|
|
||||||
// find any comments since date on the given issue
|
|
||||||
try {
|
|
||||||
const comments = yield this.client.issues.listComments({
|
|
||||||
owner: github_1.context.repo.owner,
|
|
||||||
repo: github_1.context.repo.repo,
|
|
||||||
issue_number: issueNumber,
|
|
||||||
since: sinceDate
|
|
||||||
});
|
|
||||||
return comments.data;
|
|
||||||
}
|
|
||||||
catch (error) {
|
|
||||||
this._logger.error(`List issue comments error: ${error.message}`);
|
|
||||||
return Promise.resolve([]);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
// get the actor from the GitHub token or context
|
|
||||||
_getActor() {
|
|
||||||
return __awaiter(this, void 0, void 0, function* () {
|
|
||||||
let actor;
|
|
||||||
try {
|
|
||||||
actor = yield this.client.users.getAuthenticated();
|
|
||||||
}
|
|
||||||
catch (error) {
|
|
||||||
return github_1.context.actor;
|
|
||||||
}
|
|
||||||
return actor.data.login;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
// grab issues from github in batches of 100
|
|
||||||
_getIssues(page) {
|
|
||||||
return __awaiter(this, void 0, void 0, function* () {
|
|
||||||
// generate type for response
|
|
||||||
const endpoint = this.client.issues.listForRepo;
|
|
||||||
try {
|
|
||||||
const issueResult = yield this.client.issues.listForRepo({
|
|
||||||
owner: github_1.context.repo.owner,
|
|
||||||
repo: github_1.context.repo.repo,
|
|
||||||
state: 'open',
|
|
||||||
per_page: 100,
|
|
||||||
direction: this.options.ascending ? 'asc' : 'desc',
|
|
||||||
page
|
|
||||||
});
|
|
||||||
return issueResult.data.map((issue) => new issue_1.Issue(this.options, issue));
|
|
||||||
}
|
|
||||||
catch (error) {
|
|
||||||
this._logger.error(`Get issues for repo error: ${error.message}`);
|
|
||||||
return Promise.resolve([]);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
// Mark an issue as stale with a comment and a label
|
// Mark an issue as stale with a comment and a label
|
||||||
_markStale(issue, staleMessage, staleLabel, skipMessage) {
|
_markStale(issue, staleMessage, staleLabel, skipMessage) {
|
||||||
|
var _a, _b, _c;
|
||||||
return __awaiter(this, void 0, void 0, function* () {
|
return __awaiter(this, void 0, void 0, function* () {
|
||||||
const issueLogger = new issue_logger_1.IssueLogger(issue);
|
const issueLogger = new issue_logger_1.IssueLogger(issue);
|
||||||
issueLogger.info(`Marking $$type as stale`);
|
issueLogger.info(`Marking $$type as stale`);
|
||||||
this.staleIssues.push(issue);
|
this.staleIssues.push(issue);
|
||||||
this._operationsLeft -= 2;
|
|
||||||
// if the issue is being marked stale, the updated date should be changed to right now
|
// if the issue is being marked stale, the updated date should be changed to right now
|
||||||
// so that close calculations work correctly
|
// so that close calculations work correctly
|
||||||
const newUpdatedAtDate = new Date();
|
const newUpdatedAtDate = new Date();
|
||||||
|
@ -513,6 +537,8 @@ class IssuesProcessor {
|
||||||
}
|
}
|
||||||
if (!skipMessage) {
|
if (!skipMessage) {
|
||||||
try {
|
try {
|
||||||
|
this._operationsLeft -= 1;
|
||||||
|
(_a = this._statistics) === null || _a === void 0 ? void 0 : _a.incrementAddedComment();
|
||||||
yield this.client.issues.createComment({
|
yield this.client.issues.createComment({
|
||||||
owner: github_1.context.repo.owner,
|
owner: github_1.context.repo.owner,
|
||||||
repo: github_1.context.repo.repo,
|
repo: github_1.context.repo.repo,
|
||||||
|
@ -525,6 +551,9 @@ class IssuesProcessor {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
|
this._operationsLeft -= 1;
|
||||||
|
(_b = this._statistics) === null || _b === void 0 ? void 0 : _b.incrementAddedLabel();
|
||||||
|
(_c = this._statistics) === null || _c === void 0 ? void 0 : _c.incrementStaleIssuesCount();
|
||||||
yield this.client.issues.addLabels({
|
yield this.client.issues.addLabels({
|
||||||
owner: github_1.context.repo.owner,
|
owner: github_1.context.repo.owner,
|
||||||
repo: github_1.context.repo.repo,
|
repo: github_1.context.repo.repo,
|
||||||
|
@ -539,16 +568,18 @@ class IssuesProcessor {
|
||||||
}
|
}
|
||||||
// Close an issue based on staleness
|
// Close an issue based on staleness
|
||||||
_closeIssue(issue, closeMessage, closeLabel) {
|
_closeIssue(issue, closeMessage, closeLabel) {
|
||||||
|
var _a, _b, _c;
|
||||||
return __awaiter(this, void 0, void 0, function* () {
|
return __awaiter(this, void 0, void 0, function* () {
|
||||||
const issueLogger = new issue_logger_1.IssueLogger(issue);
|
const issueLogger = new issue_logger_1.IssueLogger(issue);
|
||||||
issueLogger.info(`Closing $$type for being stale`);
|
issueLogger.info(`Closing $$type for being stale`);
|
||||||
this.closedIssues.push(issue);
|
this.closedIssues.push(issue);
|
||||||
this._operationsLeft -= 1;
|
|
||||||
if (this.options.debugOnly) {
|
if (this.options.debugOnly) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (closeMessage) {
|
if (closeMessage) {
|
||||||
try {
|
try {
|
||||||
|
this._operationsLeft -= 1;
|
||||||
|
(_a = this._statistics) === null || _a === void 0 ? void 0 : _a.incrementAddedComment();
|
||||||
yield this.client.issues.createComment({
|
yield this.client.issues.createComment({
|
||||||
owner: github_1.context.repo.owner,
|
owner: github_1.context.repo.owner,
|
||||||
repo: github_1.context.repo.repo,
|
repo: github_1.context.repo.repo,
|
||||||
|
@ -562,6 +593,8 @@ class IssuesProcessor {
|
||||||
}
|
}
|
||||||
if (closeLabel) {
|
if (closeLabel) {
|
||||||
try {
|
try {
|
||||||
|
this._operationsLeft -= 1;
|
||||||
|
(_b = this._statistics) === null || _b === void 0 ? void 0 : _b.incrementAddedLabel();
|
||||||
yield this.client.issues.addLabels({
|
yield this.client.issues.addLabels({
|
||||||
owner: github_1.context.repo.owner,
|
owner: github_1.context.repo.owner,
|
||||||
repo: github_1.context.repo.repo,
|
repo: github_1.context.repo.repo,
|
||||||
|
@ -574,6 +607,8 @@ class IssuesProcessor {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
|
this._operationsLeft -= 1;
|
||||||
|
(_c = this._statistics) === null || _c === void 0 ? void 0 : _c.incrementClosedIssuesCount();
|
||||||
yield this.client.issues.update({
|
yield this.client.issues.update({
|
||||||
owner: github_1.context.repo.owner,
|
owner: github_1.context.repo.owner,
|
||||||
repo: github_1.context.repo.repo,
|
repo: github_1.context.repo.repo,
|
||||||
|
@ -587,10 +622,15 @@ class IssuesProcessor {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
_getPullRequest(issue) {
|
_getPullRequest(issue) {
|
||||||
|
var _a;
|
||||||
return __awaiter(this, void 0, void 0, function* () {
|
return __awaiter(this, void 0, void 0, function* () {
|
||||||
const issueLogger = new issue_logger_1.IssueLogger(issue);
|
const issueLogger = new issue_logger_1.IssueLogger(issue);
|
||||||
this._operationsLeft -= 1;
|
if (this.options.debugOnly) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
try {
|
try {
|
||||||
|
this._operationsLeft -= 1;
|
||||||
|
(_a = this._statistics) === null || _a === void 0 ? void 0 : _a.incrementFetchedPullRequestsCount();
|
||||||
const pullRequest = yield this.client.pulls.get({
|
const pullRequest = yield this.client.pulls.get({
|
||||||
owner: github_1.context.repo.owner,
|
owner: github_1.context.repo.owner,
|
||||||
repo: github_1.context.repo.repo,
|
repo: github_1.context.repo.repo,
|
||||||
|
@ -605,21 +645,23 @@ class IssuesProcessor {
|
||||||
}
|
}
|
||||||
// Delete the branch on closed pull request
|
// Delete the branch on closed pull request
|
||||||
_deleteBranch(issue) {
|
_deleteBranch(issue) {
|
||||||
|
var _a;
|
||||||
return __awaiter(this, void 0, void 0, function* () {
|
return __awaiter(this, void 0, void 0, function* () {
|
||||||
const issueLogger = new issue_logger_1.IssueLogger(issue);
|
const issueLogger = new issue_logger_1.IssueLogger(issue);
|
||||||
issueLogger.info(`Delete branch from closed $$type - ${issue.title}`);
|
issueLogger.info(`Delete branch from closed $$type - ${issue.title}`);
|
||||||
if (this.options.debugOnly) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
const pullRequest = yield this._getPullRequest(issue);
|
const pullRequest = yield this._getPullRequest(issue);
|
||||||
if (!pullRequest) {
|
if (!pullRequest) {
|
||||||
issueLogger.info(`Not deleting branch as pull request not found for this $$type`);
|
issueLogger.info(`Not deleting branch as pull request not found for this $$type`);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
if (this.options.debugOnly) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
const branch = pullRequest.head.ref;
|
const branch = pullRequest.head.ref;
|
||||||
issueLogger.info(`Deleting branch ${branch} from closed $$type`);
|
issueLogger.info(`Deleting branch ${branch} from closed $$type`);
|
||||||
this._operationsLeft -= 1;
|
|
||||||
try {
|
try {
|
||||||
|
this._operationsLeft -= 1;
|
||||||
|
(_a = this._statistics) === null || _a === void 0 ? void 0 : _a.incrementDeletedBranchesCount();
|
||||||
yield this.client.git.deleteRef({
|
yield this.client.git.deleteRef({
|
||||||
owner: github_1.context.repo.owner,
|
owner: github_1.context.repo.owner,
|
||||||
repo: github_1.context.repo.repo,
|
repo: github_1.context.repo.repo,
|
||||||
|
@ -633,16 +675,18 @@ class IssuesProcessor {
|
||||||
}
|
}
|
||||||
// Remove a label from an issue
|
// Remove a label from an issue
|
||||||
_removeLabel(issue, label) {
|
_removeLabel(issue, label) {
|
||||||
|
var _a;
|
||||||
return __awaiter(this, void 0, void 0, function* () {
|
return __awaiter(this, void 0, void 0, function* () {
|
||||||
const issueLogger = new issue_logger_1.IssueLogger(issue);
|
const issueLogger = new issue_logger_1.IssueLogger(issue);
|
||||||
issueLogger.info(`Removing label "${label}" from $$type`);
|
issueLogger.info(`Removing label "${label}" from $$type`);
|
||||||
this.removedLabelIssues.push(issue);
|
this.removedLabelIssues.push(issue);
|
||||||
this._operationsLeft -= 1;
|
|
||||||
// @todo remove the debug only to be able to test the code below
|
// @todo remove the debug only to be able to test the code below
|
||||||
if (this.options.debugOnly) {
|
if (this.options.debugOnly) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
|
this._operationsLeft -= 1;
|
||||||
|
(_a = this._statistics) === null || _a === void 0 ? void 0 : _a.incrementDeletedLabelsCount();
|
||||||
yield this.client.issues.removeLabel({
|
yield this.client.issues.removeLabel({
|
||||||
owner: github_1.context.repo.owner,
|
owner: github_1.context.repo.owner,
|
||||||
repo: github_1.context.repo.repo,
|
repo: github_1.context.repo.repo,
|
||||||
|
@ -655,29 +699,6 @@ class IssuesProcessor {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
// returns the creation date of a given label on an issue (or nothing if no label existed)
|
|
||||||
///see https://developer.github.com/v3/activity/events/
|
|
||||||
_getLabelCreationDate(issue, label) {
|
|
||||||
return __awaiter(this, void 0, void 0, function* () {
|
|
||||||
const issueLogger = new issue_logger_1.IssueLogger(issue);
|
|
||||||
issueLogger.info(`Checking for label on $$type`);
|
|
||||||
this._operationsLeft -= 1;
|
|
||||||
const options = this.client.issues.listEvents.endpoint.merge({
|
|
||||||
owner: github_1.context.repo.owner,
|
|
||||||
repo: github_1.context.repo.repo,
|
|
||||||
per_page: 100,
|
|
||||||
issue_number: issue.number
|
|
||||||
});
|
|
||||||
const events = yield this.client.paginate(options);
|
|
||||||
const reversedEvents = events.reverse();
|
|
||||||
const staleLabeledEvent = reversedEvents.find(event => event.event === 'labeled' && event.label.name === label);
|
|
||||||
if (!staleLabeledEvent) {
|
|
||||||
// Must be old rather than labeled
|
|
||||||
return undefined;
|
|
||||||
}
|
|
||||||
return staleLabeledEvent.created_at;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
_getDaysBeforeIssueStale() {
|
_getDaysBeforeIssueStale() {
|
||||||
return isNaN(this.options.daysBeforeIssueStale)
|
return isNaN(this.options.daysBeforeIssueStale)
|
||||||
? this.options.daysBeforeStale
|
? this.options.daysBeforeStale
|
||||||
|
@ -712,13 +733,16 @@ class IssuesProcessor {
|
||||||
return this.options.onlyLabels;
|
return this.options.onlyLabels;
|
||||||
}
|
}
|
||||||
_removeStaleLabel(issue, staleLabel) {
|
_removeStaleLabel(issue, staleLabel) {
|
||||||
|
var _a;
|
||||||
return __awaiter(this, void 0, void 0, function* () {
|
return __awaiter(this, void 0, void 0, function* () {
|
||||||
const issueLogger = new issue_logger_1.IssueLogger(issue);
|
const issueLogger = new issue_logger_1.IssueLogger(issue);
|
||||||
issueLogger.info(`The $$type is no longer stale. Removing the stale label...`);
|
issueLogger.info(`The $$type is no longer stale. Removing the stale label...`);
|
||||||
return this._removeLabel(issue, staleLabel);
|
yield this._removeLabel(issue, staleLabel);
|
||||||
|
(_a = this._statistics) === null || _a === void 0 ? void 0 : _a.incrementUndoStaleIssuesCount();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
_removeCloseLabel(issue, closeLabel) {
|
_removeCloseLabel(issue, closeLabel) {
|
||||||
|
var _a;
|
||||||
return __awaiter(this, void 0, void 0, function* () {
|
return __awaiter(this, void 0, void 0, function* () {
|
||||||
const issueLogger = new issue_logger_1.IssueLogger(issue);
|
const issueLogger = new issue_logger_1.IssueLogger(issue);
|
||||||
issueLogger.info(`The $$type is not closed nor locked. Trying to remove the close label...`);
|
issueLogger.info(`The $$type is not closed nor locked. Trying to remove the close label...`);
|
||||||
|
@ -728,7 +752,8 @@ class IssuesProcessor {
|
||||||
}
|
}
|
||||||
if (is_labeled_1.isLabeled(issue, closeLabel)) {
|
if (is_labeled_1.isLabeled(issue, closeLabel)) {
|
||||||
issueLogger.info(`The $$type has a close label "${closeLabel}". Removing the close label...`);
|
issueLogger.info(`The $$type has a close label "${closeLabel}". Removing the close label...`);
|
||||||
return this._removeLabel(issue, closeLabel);
|
yield this._removeLabel(issue, closeLabel);
|
||||||
|
(_a = this._statistics) === null || _a === void 0 ? void 0 : _a.incrementDeletedCloseLabelsCount();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -938,18 +963,157 @@ exports.Milestones = Milestones;
|
||||||
|
|
||||||
/***/ }),
|
/***/ }),
|
||||||
|
|
||||||
/***/ 9639:
|
/***/ 3334:
|
||||||
/***/ ((__unused_webpack_module, exports) => {
|
/***/ ((__unused_webpack_module, exports, __nccwpck_require__) => {
|
||||||
|
|
||||||
"use strict";
|
"use strict";
|
||||||
|
|
||||||
Object.defineProperty(exports, "__esModule", ({ value: true }));
|
Object.defineProperty(exports, "__esModule", ({ value: true }));
|
||||||
exports.IssueType = void 0;
|
exports.Statistics = void 0;
|
||||||
var IssueType;
|
const logger_1 = __nccwpck_require__(6212);
|
||||||
(function (IssueType) {
|
class Statistics {
|
||||||
IssueType["Issue"] = "issue";
|
constructor(options) {
|
||||||
IssueType["PullRequest"] = "pr";
|
this._logger = new logger_1.Logger();
|
||||||
})(IssueType = exports.IssueType || (exports.IssueType = {}));
|
this._processedIssuesCount = 0;
|
||||||
|
this._staleIssuesCount = 0;
|
||||||
|
this._undoStaleIssuesCount = 0;
|
||||||
|
this._operationsCount = 0;
|
||||||
|
this._closedIssuesCount = 0;
|
||||||
|
this._deletedLabelsCount = 0;
|
||||||
|
this._deletedCloseLabelsCount = 0;
|
||||||
|
this._deletedBranchesCount = 0;
|
||||||
|
this._addedLabelsCount = 0;
|
||||||
|
this._addedCommentsCount = 0;
|
||||||
|
this._fetchedIssuesCount = 0;
|
||||||
|
this._fetchedIssuesEventsCount = 0;
|
||||||
|
this._fetchedIssuesCommentsCount = 0;
|
||||||
|
this._fetchedPullRequestsCount = 0;
|
||||||
|
this._options = options;
|
||||||
|
}
|
||||||
|
incrementProcessedIssuesCount(increment = 1) {
|
||||||
|
this._processedIssuesCount += increment;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
incrementStaleIssuesCount(increment = 1) {
|
||||||
|
this._staleIssuesCount += increment;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
incrementUndoStaleIssuesCount(increment = 1) {
|
||||||
|
this._undoStaleIssuesCount += increment;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
setOperationsLeft(operationsLeft) {
|
||||||
|
this._operationsCount = this._options.operationsPerRun - operationsLeft;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
incrementClosedIssuesCount(increment = 1) {
|
||||||
|
this._closedIssuesCount += increment;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
incrementDeletedLabelsCount(increment = 1) {
|
||||||
|
this._deletedLabelsCount += increment;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
incrementDeletedCloseLabelsCount(increment = 1) {
|
||||||
|
this._deletedCloseLabelsCount += increment;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
incrementDeletedBranchesCount(increment = 1) {
|
||||||
|
this._deletedBranchesCount += increment;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
incrementAddedLabel(increment = 1) {
|
||||||
|
this._addedLabelsCount += increment;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
incrementAddedComment(increment = 1) {
|
||||||
|
this._addedCommentsCount += increment;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
incrementFetchedIssuesCount(increment = 1) {
|
||||||
|
this._fetchedIssuesCount += increment;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
incrementFetchedIssuesEventsCount(increment = 1) {
|
||||||
|
this._fetchedIssuesEventsCount += increment;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
incrementFetchedIssuesCommentsCount(increment = 1) {
|
||||||
|
this._fetchedIssuesCommentsCount += increment;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
incrementFetchedPullRequestsCount(increment = 1) {
|
||||||
|
this._fetchedPullRequestsCount += increment;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
logStats() {
|
||||||
|
this._logger.info('Statistics');
|
||||||
|
this._logProcessedIssuesCount();
|
||||||
|
this._logStaleIssuesCount();
|
||||||
|
this._logUndoStaleIssuesCount();
|
||||||
|
this._logOperationsCount();
|
||||||
|
this._logClosedIssuesCount();
|
||||||
|
this._logDeletedLabelsCount();
|
||||||
|
this._logDeletedCloseLabelsCount();
|
||||||
|
this._logDeletedBranchesCount();
|
||||||
|
this._logAddedLabelsCount();
|
||||||
|
this._logAddedCommentsCount();
|
||||||
|
this._logFetchedIssuesCount();
|
||||||
|
this._logFetchedIssuesEventsCount();
|
||||||
|
this._logFetchedIssuesCommentsCount();
|
||||||
|
this._logFetchedPullRequestsCount();
|
||||||
|
this._logger.info('---');
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
_logProcessedIssuesCount() {
|
||||||
|
this._logCount('Processed issues/PRs', this._processedIssuesCount);
|
||||||
|
}
|
||||||
|
_logStaleIssuesCount() {
|
||||||
|
this._logCount('New stale issues/PRs', this._staleIssuesCount);
|
||||||
|
}
|
||||||
|
_logUndoStaleIssuesCount() {
|
||||||
|
this._logCount('No longer stale issues/PRs', this._undoStaleIssuesCount);
|
||||||
|
}
|
||||||
|
_logOperationsCount() {
|
||||||
|
this._logCount('Operations performed', this._operationsCount);
|
||||||
|
}
|
||||||
|
_logClosedIssuesCount() {
|
||||||
|
this._logCount('Closed issues', this._closedIssuesCount);
|
||||||
|
}
|
||||||
|
_logDeletedLabelsCount() {
|
||||||
|
this._logCount('Deleted labels', this._deletedLabelsCount);
|
||||||
|
}
|
||||||
|
_logDeletedCloseLabelsCount() {
|
||||||
|
this._logCount('Deleted close labels', this._deletedCloseLabelsCount);
|
||||||
|
}
|
||||||
|
_logDeletedBranchesCount() {
|
||||||
|
this._logCount('Deleted branches', this._deletedBranchesCount);
|
||||||
|
}
|
||||||
|
_logAddedLabelsCount() {
|
||||||
|
this._logCount('Added labels', this._addedLabelsCount);
|
||||||
|
}
|
||||||
|
_logAddedCommentsCount() {
|
||||||
|
this._logCount('Added comments', this._addedCommentsCount);
|
||||||
|
}
|
||||||
|
_logFetchedIssuesCount() {
|
||||||
|
this._logCount('Fetched issues', this._fetchedIssuesCount);
|
||||||
|
}
|
||||||
|
_logFetchedIssuesEventsCount() {
|
||||||
|
this._logCount('Fetched issues events', this._fetchedIssuesEventsCount);
|
||||||
|
}
|
||||||
|
_logFetchedIssuesCommentsCount() {
|
||||||
|
this._logCount('Fetched issues comments', this._fetchedIssuesCommentsCount);
|
||||||
|
}
|
||||||
|
_logFetchedPullRequestsCount() {
|
||||||
|
this._logCount('Fetched pull requests', this._fetchedPullRequestsCount);
|
||||||
|
}
|
||||||
|
_logCount(name, count) {
|
||||||
|
if (count > 0) {
|
||||||
|
this._logger.info(`${name}: ${count}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
exports.Statistics = Statistics;
|
||||||
|
|
||||||
|
|
||||||
/***/ }),
|
/***/ }),
|
||||||
|
@ -1020,22 +1184,6 @@ function isValidDate(date) {
|
||||||
exports.isValidDate = isValidDate;
|
exports.isValidDate = isValidDate;
|
||||||
|
|
||||||
|
|
||||||
/***/ }),
|
|
||||||
|
|
||||||
/***/ 5153:
|
|
||||||
/***/ ((__unused_webpack_module, exports, __nccwpck_require__) => {
|
|
||||||
|
|
||||||
"use strict";
|
|
||||||
|
|
||||||
Object.defineProperty(exports, "__esModule", ({ value: true }));
|
|
||||||
exports.getIssueType = void 0;
|
|
||||||
const issue_type_1 = __nccwpck_require__(9639);
|
|
||||||
function getIssueType(isPullRequest) {
|
|
||||||
return isPullRequest ? issue_type_1.IssueType.PullRequest : issue_type_1.IssueType.Issue;
|
|
||||||
}
|
|
||||||
exports.getIssueType = getIssueType;
|
|
||||||
|
|
||||||
|
|
||||||
/***/ }),
|
/***/ }),
|
||||||
|
|
||||||
/***/ 6792:
|
/***/ 6792:
|
||||||
|
@ -1229,7 +1377,8 @@ function _getAndValidateArgs() {
|
||||||
exemptPrAssignees: core.getInput('exempt-pr-assignees'),
|
exemptPrAssignees: core.getInput('exempt-pr-assignees'),
|
||||||
exemptAllAssignees: core.getInput('exempt-all-assignees') === 'true',
|
exemptAllAssignees: core.getInput('exempt-all-assignees') === 'true',
|
||||||
exemptAllIssueAssignees: _toOptionalBoolean('exempt-all-issue-assignees'),
|
exemptAllIssueAssignees: _toOptionalBoolean('exempt-all-issue-assignees'),
|
||||||
exemptAllPrAssignees: _toOptionalBoolean('exempt-all-pr-assignees')
|
exemptAllPrAssignees: _toOptionalBoolean('exempt-all-pr-assignees'),
|
||||||
|
enableStatistics: core.getInput('enable-statistics') === 'true'
|
||||||
};
|
};
|
||||||
for (const numberInput of [
|
for (const numberInput of [
|
||||||
'days-before-stale',
|
'days-before-stale',
|
||||||
|
|
|
@ -52,7 +52,8 @@ describe('Issue', (): void => {
|
||||||
exemptPrAssignees: '',
|
exemptPrAssignees: '',
|
||||||
exemptAllAssignees: false,
|
exemptAllAssignees: false,
|
||||||
exemptAllIssueAssignees: undefined,
|
exemptAllIssueAssignees: undefined,
|
||||||
exemptAllPrAssignees: undefined
|
exemptAllPrAssignees: undefined,
|
||||||
|
enableStatistics: false
|
||||||
};
|
};
|
||||||
issueInterface = {
|
issueInterface = {
|
||||||
title: 'dummy-title',
|
title: 'dummy-title',
|
||||||
|
|
|
@ -1,11 +1,9 @@
|
||||||
import {context, getOctokit} from '@actions/github';
|
import {context, getOctokit} from '@actions/github';
|
||||||
import {GitHub} from '@actions/github/lib/utils';
|
import {GitHub} from '@actions/github/lib/utils';
|
||||||
import {GetResponseTypeFromEndpointMethod} from '@octokit/types';
|
import {GetResponseTypeFromEndpointMethod} from '@octokit/types';
|
||||||
import {IssueType} from '../enums/issue-type';
|
|
||||||
import {getHumanizedDate} from '../functions/dates/get-humanized-date';
|
import {getHumanizedDate} from '../functions/dates/get-humanized-date';
|
||||||
import {isDateMoreRecentThan} from '../functions/dates/is-date-more-recent-than';
|
import {isDateMoreRecentThan} from '../functions/dates/is-date-more-recent-than';
|
||||||
import {isValidDate} from '../functions/dates/is-valid-date';
|
import {isValidDate} from '../functions/dates/is-valid-date';
|
||||||
import {getIssueType} from '../functions/get-issue-type';
|
|
||||||
import {isLabeled} from '../functions/is-labeled';
|
import {isLabeled} from '../functions/is-labeled';
|
||||||
import {isPullRequest} from '../functions/is-pull-request';
|
import {isPullRequest} from '../functions/is-pull-request';
|
||||||
import {shouldMarkWhenStale} from '../functions/should-mark-when-stale';
|
import {shouldMarkWhenStale} from '../functions/should-mark-when-stale';
|
||||||
|
@ -20,6 +18,7 @@ import {Issue} from './issue';
|
||||||
import {IssueLogger} from './loggers/issue-logger';
|
import {IssueLogger} from './loggers/issue-logger';
|
||||||
import {Logger} from './loggers/logger';
|
import {Logger} from './loggers/logger';
|
||||||
import {Milestones} from './milestones';
|
import {Milestones} from './milestones';
|
||||||
|
import {Statistics} from './statistics';
|
||||||
|
|
||||||
/***
|
/***
|
||||||
* Handle processing of issues for staleness/closure.
|
* Handle processing of issues for staleness/closure.
|
||||||
|
@ -34,6 +33,7 @@ export class IssuesProcessor {
|
||||||
}
|
}
|
||||||
|
|
||||||
private readonly _logger: Logger = new Logger();
|
private readonly _logger: Logger = new Logger();
|
||||||
|
private readonly _statistics: Statistics | undefined;
|
||||||
private _operationsLeft = 0;
|
private _operationsLeft = 0;
|
||||||
readonly client: InstanceType<typeof GitHub>;
|
readonly client: InstanceType<typeof GitHub>;
|
||||||
readonly options: IIssuesProcessorOptions;
|
readonly options: IIssuesProcessorOptions;
|
||||||
|
@ -42,61 +42,38 @@ export class IssuesProcessor {
|
||||||
readonly deletedBranchIssues: Issue[] = [];
|
readonly deletedBranchIssues: Issue[] = [];
|
||||||
readonly removedLabelIssues: Issue[] = [];
|
readonly removedLabelIssues: Issue[] = [];
|
||||||
|
|
||||||
constructor(
|
constructor(options: IIssuesProcessorOptions) {
|
||||||
options: IIssuesProcessorOptions,
|
|
||||||
getActor?: () => Promise<string>,
|
|
||||||
getIssues?: (page: number) => Promise<Issue[]>,
|
|
||||||
listIssueComments?: (
|
|
||||||
issueNumber: number,
|
|
||||||
sinceDate: string
|
|
||||||
) => Promise<IComment[]>,
|
|
||||||
getLabelCreationDate?: (
|
|
||||||
issue: Issue,
|
|
||||||
label: string
|
|
||||||
) => Promise<string | undefined>
|
|
||||||
) {
|
|
||||||
this.options = options;
|
this.options = options;
|
||||||
this._operationsLeft = options.operationsPerRun;
|
this._operationsLeft = this.options.operationsPerRun;
|
||||||
this.client = getOctokit(options.repoToken);
|
this.client = getOctokit(this.options.repoToken);
|
||||||
|
|
||||||
if (getActor) {
|
|
||||||
this._getActor = getActor;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (getIssues) {
|
|
||||||
this._getIssues = getIssues;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (listIssueComments) {
|
|
||||||
this._listIssueComments = listIssueComments;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (getLabelCreationDate) {
|
|
||||||
this._getLabelCreationDate = getLabelCreationDate;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (this.options.debugOnly) {
|
if (this.options.debugOnly) {
|
||||||
this._logger.warning(
|
this._logger.warning(
|
||||||
'Executing in debug mode. Debug output will be written but no issues will be processed.'
|
'Executing in debug mode. Debug output will be written but no issues will be processed.'
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (this.options.enableStatistics) {
|
||||||
|
this._statistics = new Statistics(this.options);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async processIssues(page = 1): Promise<number> {
|
async processIssues(page = 1): Promise<number> {
|
||||||
// get the next batch of issues
|
// get the next batch of issues
|
||||||
const issues: Issue[] = await this._getIssues(page);
|
const issues: Issue[] = await this.getIssues(page);
|
||||||
this._operationsLeft -= 1;
|
const actor: string = await this.getActor();
|
||||||
|
|
||||||
const actor: string = await this._getActor();
|
|
||||||
|
|
||||||
if (issues.length <= 0) {
|
if (issues.length <= 0) {
|
||||||
this._logger.info('---');
|
this._logger.info('---');
|
||||||
|
this._statistics?.setOperationsLeft(this._operationsLeft).logStats();
|
||||||
this._logger.info('No more issues found to process. Exiting.');
|
this._logger.info('No more issues found to process. Exiting.');
|
||||||
|
|
||||||
return this._operationsLeft;
|
return this._operationsLeft;
|
||||||
}
|
}
|
||||||
|
|
||||||
for (const issue of issues.values()) {
|
for (const issue of issues.values()) {
|
||||||
const issueLogger: IssueLogger = new IssueLogger(issue);
|
const issueLogger: IssueLogger = new IssueLogger(issue);
|
||||||
|
this._statistics?.incrementProcessedIssuesCount();
|
||||||
|
|
||||||
issueLogger.info(`Found this $$type last updated ${issue.updated_at}`);
|
issueLogger.info(`Found this $$type last updated ${issue.updated_at}`);
|
||||||
|
|
||||||
|
@ -116,7 +93,6 @@ export class IssuesProcessor {
|
||||||
const skipMessage = issue.isPullRequest
|
const skipMessage = issue.isPullRequest
|
||||||
? this.options.skipStalePrMessage
|
? this.options.skipStalePrMessage
|
||||||
: this.options.skipStaleIssueMessage;
|
: this.options.skipStaleIssueMessage;
|
||||||
const issueType: IssueType = getIssueType(issue.isPullRequest);
|
|
||||||
const daysBeforeStale: number = issue.isPullRequest
|
const daysBeforeStale: number = issue.isPullRequest
|
||||||
? this._getDaysBeforePrStale()
|
? this._getDaysBeforePrStale()
|
||||||
: this._getDaysBeforeIssueStale();
|
: this._getDaysBeforeIssueStale();
|
||||||
|
@ -238,7 +214,7 @@ export class IssuesProcessor {
|
||||||
)
|
)
|
||||||
) {
|
) {
|
||||||
issueLogger.info(
|
issueLogger.info(
|
||||||
`Skipping ${issueType} because it does not have any of the required labels`
|
`Skipping $$type because it does not have any of the required labels`
|
||||||
);
|
);
|
||||||
continue; // don't process issues without any of the required labels
|
continue; // don't process issues without any of the required labels
|
||||||
}
|
}
|
||||||
|
@ -282,7 +258,6 @@ export class IssuesProcessor {
|
||||||
issueLogger.info(`Found a stale $$type`);
|
issueLogger.info(`Found a stale $$type`);
|
||||||
await this._processStaleIssue(
|
await this._processStaleIssue(
|
||||||
issue,
|
issue,
|
||||||
issueType,
|
|
||||||
staleLabel,
|
staleLabel,
|
||||||
actor,
|
actor,
|
||||||
closeMessage,
|
closeMessage,
|
||||||
|
@ -302,10 +277,108 @@ export class IssuesProcessor {
|
||||||
return this.processIssues(page + 1);
|
return this.processIssues(page + 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// grab comments for an issue since a given date
|
||||||
|
async listIssueComments(
|
||||||
|
issueNumber: number,
|
||||||
|
sinceDate: string
|
||||||
|
): Promise<IComment[]> {
|
||||||
|
// find any comments since date on the given issue
|
||||||
|
try {
|
||||||
|
this._operationsLeft -= 1;
|
||||||
|
this._statistics?.incrementFetchedIssuesCommentsCount();
|
||||||
|
const comments = await this.client.issues.listComments({
|
||||||
|
owner: context.repo.owner,
|
||||||
|
repo: context.repo.repo,
|
||||||
|
issue_number: issueNumber,
|
||||||
|
since: sinceDate
|
||||||
|
});
|
||||||
|
return comments.data;
|
||||||
|
} catch (error) {
|
||||||
|
this._logger.error(`List issue comments error: ${error.message}`);
|
||||||
|
return Promise.resolve([]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// get the actor from the GitHub token or context
|
||||||
|
async getActor(): Promise<string> {
|
||||||
|
let actor;
|
||||||
|
|
||||||
|
try {
|
||||||
|
this._operationsLeft -= 1;
|
||||||
|
actor = await this.client.users.getAuthenticated();
|
||||||
|
} catch (error) {
|
||||||
|
return context.actor;
|
||||||
|
}
|
||||||
|
|
||||||
|
return actor.data.login;
|
||||||
|
}
|
||||||
|
|
||||||
|
// grab issues from github in batches of 100
|
||||||
|
async getIssues(page: number): Promise<Issue[]> {
|
||||||
|
// generate type for response
|
||||||
|
const endpoint = this.client.issues.listForRepo;
|
||||||
|
type OctoKitIssueList = GetResponseTypeFromEndpointMethod<typeof endpoint>;
|
||||||
|
|
||||||
|
try {
|
||||||
|
this._operationsLeft -= 1;
|
||||||
|
this._statistics?.incrementFetchedIssuesCount();
|
||||||
|
const issueResult: OctoKitIssueList = await this.client.issues.listForRepo(
|
||||||
|
{
|
||||||
|
owner: context.repo.owner,
|
||||||
|
repo: context.repo.repo,
|
||||||
|
state: 'open',
|
||||||
|
per_page: 100,
|
||||||
|
direction: this.options.ascending ? 'asc' : 'desc',
|
||||||
|
page
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
return issueResult.data.map(
|
||||||
|
(issue: Readonly<IIssue>): Issue => new Issue(this.options, issue)
|
||||||
|
);
|
||||||
|
} catch (error) {
|
||||||
|
this._logger.error(`Get issues for repo error: ${error.message}`);
|
||||||
|
return Promise.resolve([]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// returns the creation date of a given label on an issue (or nothing if no label existed)
|
||||||
|
///see https://developer.github.com/v3/activity/events/
|
||||||
|
async getLabelCreationDate(
|
||||||
|
issue: Issue,
|
||||||
|
label: string
|
||||||
|
): Promise<string | undefined> {
|
||||||
|
const issueLogger: IssueLogger = new IssueLogger(issue);
|
||||||
|
|
||||||
|
issueLogger.info(`Checking for label on $$type`);
|
||||||
|
|
||||||
|
this._operationsLeft -= 1;
|
||||||
|
this._statistics?.incrementFetchedIssuesEventsCount();
|
||||||
|
const options = this.client.issues.listEvents.endpoint.merge({
|
||||||
|
owner: context.repo.owner,
|
||||||
|
repo: context.repo.repo,
|
||||||
|
per_page: 100,
|
||||||
|
issue_number: issue.number
|
||||||
|
});
|
||||||
|
|
||||||
|
const events: IIssueEvent[] = await this.client.paginate(options);
|
||||||
|
const reversedEvents = events.reverse();
|
||||||
|
|
||||||
|
const staleLabeledEvent = reversedEvents.find(
|
||||||
|
event => event.event === 'labeled' && event.label.name === label
|
||||||
|
);
|
||||||
|
|
||||||
|
if (!staleLabeledEvent) {
|
||||||
|
// Must be old rather than labeled
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
return staleLabeledEvent.created_at;
|
||||||
|
}
|
||||||
|
|
||||||
// handle all of the stale issue logic when we find a stale issue
|
// handle all of the stale issue logic when we find a stale issue
|
||||||
private async _processStaleIssue(
|
private async _processStaleIssue(
|
||||||
issue: Issue,
|
issue: Issue,
|
||||||
issueType: IssueType,
|
|
||||||
staleLabel: string,
|
staleLabel: string,
|
||||||
actor: string,
|
actor: string,
|
||||||
closeMessage?: string,
|
closeMessage?: string,
|
||||||
|
@ -313,7 +386,7 @@ export class IssuesProcessor {
|
||||||
) {
|
) {
|
||||||
const issueLogger: IssueLogger = new IssueLogger(issue);
|
const issueLogger: IssueLogger = new IssueLogger(issue);
|
||||||
const markedStaleOn: string =
|
const markedStaleOn: string =
|
||||||
(await this._getLabelCreationDate(issue, staleLabel)) || issue.updated_at;
|
(await this.getLabelCreationDate(issue, staleLabel)) || issue.updated_at;
|
||||||
issueLogger.info(`$$type marked stale on: ${markedStaleOn}`);
|
issueLogger.info(`$$type marked stale on: ${markedStaleOn}`);
|
||||||
|
|
||||||
const issueHasComments: boolean = await this._hasCommentsSince(
|
const issueHasComments: boolean = await this._hasCommentsSince(
|
||||||
|
@ -381,7 +454,7 @@ export class IssuesProcessor {
|
||||||
}
|
}
|
||||||
|
|
||||||
// find any comments since the date
|
// find any comments since the date
|
||||||
const comments = await this._listIssueComments(issue.number, sinceDate);
|
const comments = await this.listIssueComments(issue.number, sinceDate);
|
||||||
|
|
||||||
const filteredComments = comments.filter(
|
const filteredComments = comments.filter(
|
||||||
comment => comment.user.type === 'User' && comment.user.login !== actor
|
comment => comment.user.type === 'User' && comment.user.login !== actor
|
||||||
|
@ -395,65 +468,6 @@ export class IssuesProcessor {
|
||||||
return filteredComments.length > 0;
|
return filteredComments.length > 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
// grab comments for an issue since a given date
|
|
||||||
private async _listIssueComments(
|
|
||||||
issueNumber: number,
|
|
||||||
sinceDate: string
|
|
||||||
): Promise<IComment[]> {
|
|
||||||
// find any comments since date on the given issue
|
|
||||||
try {
|
|
||||||
const comments = await this.client.issues.listComments({
|
|
||||||
owner: context.repo.owner,
|
|
||||||
repo: context.repo.repo,
|
|
||||||
issue_number: issueNumber,
|
|
||||||
since: sinceDate
|
|
||||||
});
|
|
||||||
return comments.data;
|
|
||||||
} catch (error) {
|
|
||||||
this._logger.error(`List issue comments error: ${error.message}`);
|
|
||||||
return Promise.resolve([]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// get the actor from the GitHub token or context
|
|
||||||
private async _getActor(): Promise<string> {
|
|
||||||
let actor;
|
|
||||||
try {
|
|
||||||
actor = await this.client.users.getAuthenticated();
|
|
||||||
} catch (error) {
|
|
||||||
return context.actor;
|
|
||||||
}
|
|
||||||
|
|
||||||
return actor.data.login;
|
|
||||||
}
|
|
||||||
|
|
||||||
// grab issues from github in batches of 100
|
|
||||||
private async _getIssues(page: number): Promise<Issue[]> {
|
|
||||||
// generate type for response
|
|
||||||
const endpoint = this.client.issues.listForRepo;
|
|
||||||
type OctoKitIssueList = GetResponseTypeFromEndpointMethod<typeof endpoint>;
|
|
||||||
|
|
||||||
try {
|
|
||||||
const issueResult: OctoKitIssueList = await this.client.issues.listForRepo(
|
|
||||||
{
|
|
||||||
owner: context.repo.owner,
|
|
||||||
repo: context.repo.repo,
|
|
||||||
state: 'open',
|
|
||||||
per_page: 100,
|
|
||||||
direction: this.options.ascending ? 'asc' : 'desc',
|
|
||||||
page
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
return issueResult.data.map(
|
|
||||||
(issue: Readonly<IIssue>): Issue => new Issue(this.options, issue)
|
|
||||||
);
|
|
||||||
} catch (error) {
|
|
||||||
this._logger.error(`Get issues for repo error: ${error.message}`);
|
|
||||||
return Promise.resolve([]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Mark an issue as stale with a comment and a label
|
// Mark an issue as stale with a comment and a label
|
||||||
private async _markStale(
|
private async _markStale(
|
||||||
issue: Issue,
|
issue: Issue,
|
||||||
|
@ -464,11 +478,8 @@ export class IssuesProcessor {
|
||||||
const issueLogger: IssueLogger = new IssueLogger(issue);
|
const issueLogger: IssueLogger = new IssueLogger(issue);
|
||||||
|
|
||||||
issueLogger.info(`Marking $$type as stale`);
|
issueLogger.info(`Marking $$type as stale`);
|
||||||
|
|
||||||
this.staleIssues.push(issue);
|
this.staleIssues.push(issue);
|
||||||
|
|
||||||
this._operationsLeft -= 2;
|
|
||||||
|
|
||||||
// if the issue is being marked stale, the updated date should be changed to right now
|
// if the issue is being marked stale, the updated date should be changed to right now
|
||||||
// so that close calculations work correctly
|
// so that close calculations work correctly
|
||||||
const newUpdatedAtDate: Date = new Date();
|
const newUpdatedAtDate: Date = new Date();
|
||||||
|
@ -480,6 +491,8 @@ export class IssuesProcessor {
|
||||||
|
|
||||||
if (!skipMessage) {
|
if (!skipMessage) {
|
||||||
try {
|
try {
|
||||||
|
this._operationsLeft -= 1;
|
||||||
|
this._statistics?.incrementAddedComment();
|
||||||
await this.client.issues.createComment({
|
await this.client.issues.createComment({
|
||||||
owner: context.repo.owner,
|
owner: context.repo.owner,
|
||||||
repo: context.repo.repo,
|
repo: context.repo.repo,
|
||||||
|
@ -492,6 +505,9 @@ export class IssuesProcessor {
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
this._operationsLeft -= 1;
|
||||||
|
this._statistics?.incrementAddedLabel();
|
||||||
|
this._statistics?.incrementStaleIssuesCount();
|
||||||
await this.client.issues.addLabels({
|
await this.client.issues.addLabels({
|
||||||
owner: context.repo.owner,
|
owner: context.repo.owner,
|
||||||
repo: context.repo.repo,
|
repo: context.repo.repo,
|
||||||
|
@ -512,17 +528,16 @@ export class IssuesProcessor {
|
||||||
const issueLogger: IssueLogger = new IssueLogger(issue);
|
const issueLogger: IssueLogger = new IssueLogger(issue);
|
||||||
|
|
||||||
issueLogger.info(`Closing $$type for being stale`);
|
issueLogger.info(`Closing $$type for being stale`);
|
||||||
|
|
||||||
this.closedIssues.push(issue);
|
this.closedIssues.push(issue);
|
||||||
|
|
||||||
this._operationsLeft -= 1;
|
|
||||||
|
|
||||||
if (this.options.debugOnly) {
|
if (this.options.debugOnly) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (closeMessage) {
|
if (closeMessage) {
|
||||||
try {
|
try {
|
||||||
|
this._operationsLeft -= 1;
|
||||||
|
this._statistics?.incrementAddedComment();
|
||||||
await this.client.issues.createComment({
|
await this.client.issues.createComment({
|
||||||
owner: context.repo.owner,
|
owner: context.repo.owner,
|
||||||
repo: context.repo.repo,
|
repo: context.repo.repo,
|
||||||
|
@ -536,6 +551,8 @@ export class IssuesProcessor {
|
||||||
|
|
||||||
if (closeLabel) {
|
if (closeLabel) {
|
||||||
try {
|
try {
|
||||||
|
this._operationsLeft -= 1;
|
||||||
|
this._statistics?.incrementAddedLabel();
|
||||||
await this.client.issues.addLabels({
|
await this.client.issues.addLabels({
|
||||||
owner: context.repo.owner,
|
owner: context.repo.owner,
|
||||||
repo: context.repo.repo,
|
repo: context.repo.repo,
|
||||||
|
@ -548,6 +565,8 @@ export class IssuesProcessor {
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
this._operationsLeft -= 1;
|
||||||
|
this._statistics?.incrementClosedIssuesCount();
|
||||||
await this.client.issues.update({
|
await this.client.issues.update({
|
||||||
owner: context.repo.owner,
|
owner: context.repo.owner,
|
||||||
repo: context.repo.repo,
|
repo: context.repo.repo,
|
||||||
|
@ -561,11 +580,16 @@ export class IssuesProcessor {
|
||||||
|
|
||||||
private async _getPullRequest(
|
private async _getPullRequest(
|
||||||
issue: Issue
|
issue: Issue
|
||||||
): Promise<IPullRequest | undefined> {
|
): Promise<IPullRequest | undefined | void> {
|
||||||
const issueLogger: IssueLogger = new IssueLogger(issue);
|
const issueLogger: IssueLogger = new IssueLogger(issue);
|
||||||
this._operationsLeft -= 1;
|
|
||||||
|
if (this.options.debugOnly) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
this._operationsLeft -= 1;
|
||||||
|
this._statistics?.incrementFetchedPullRequestsCount();
|
||||||
const pullRequest = await this.client.pulls.get({
|
const pullRequest = await this.client.pulls.get({
|
||||||
owner: context.repo.owner,
|
owner: context.repo.owner,
|
||||||
repo: context.repo.repo,
|
repo: context.repo.repo,
|
||||||
|
@ -584,10 +608,6 @@ export class IssuesProcessor {
|
||||||
|
|
||||||
issueLogger.info(`Delete branch from closed $$type - ${issue.title}`);
|
issueLogger.info(`Delete branch from closed $$type - ${issue.title}`);
|
||||||
|
|
||||||
if (this.options.debugOnly) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const pullRequest = await this._getPullRequest(issue);
|
const pullRequest = await this._getPullRequest(issue);
|
||||||
|
|
||||||
if (!pullRequest) {
|
if (!pullRequest) {
|
||||||
|
@ -597,12 +617,16 @@ export class IssuesProcessor {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (this.options.debugOnly) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
const branch = pullRequest.head.ref;
|
const branch = pullRequest.head.ref;
|
||||||
issueLogger.info(`Deleting branch ${branch} from closed $$type`);
|
issueLogger.info(`Deleting branch ${branch} from closed $$type`);
|
||||||
|
|
||||||
this._operationsLeft -= 1;
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
this._operationsLeft -= 1;
|
||||||
|
this._statistics?.incrementDeletedBranchesCount();
|
||||||
await this.client.git.deleteRef({
|
await this.client.git.deleteRef({
|
||||||
owner: context.repo.owner,
|
owner: context.repo.owner,
|
||||||
repo: context.repo.repo,
|
repo: context.repo.repo,
|
||||||
|
@ -620,17 +644,16 @@ export class IssuesProcessor {
|
||||||
const issueLogger: IssueLogger = new IssueLogger(issue);
|
const issueLogger: IssueLogger = new IssueLogger(issue);
|
||||||
|
|
||||||
issueLogger.info(`Removing label "${label}" from $$type`);
|
issueLogger.info(`Removing label "${label}" from $$type`);
|
||||||
|
|
||||||
this.removedLabelIssues.push(issue);
|
this.removedLabelIssues.push(issue);
|
||||||
|
|
||||||
this._operationsLeft -= 1;
|
|
||||||
|
|
||||||
// @todo remove the debug only to be able to test the code below
|
// @todo remove the debug only to be able to test the code below
|
||||||
if (this.options.debugOnly) {
|
if (this.options.debugOnly) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
this._operationsLeft -= 1;
|
||||||
|
this._statistics?.incrementDeletedLabelsCount();
|
||||||
await this.client.issues.removeLabel({
|
await this.client.issues.removeLabel({
|
||||||
owner: context.repo.owner,
|
owner: context.repo.owner,
|
||||||
repo: context.repo.repo,
|
repo: context.repo.repo,
|
||||||
|
@ -642,40 +665,6 @@ export class IssuesProcessor {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// returns the creation date of a given label on an issue (or nothing if no label existed)
|
|
||||||
///see https://developer.github.com/v3/activity/events/
|
|
||||||
private async _getLabelCreationDate(
|
|
||||||
issue: Issue,
|
|
||||||
label: string
|
|
||||||
): Promise<string | undefined> {
|
|
||||||
const issueLogger: IssueLogger = new IssueLogger(issue);
|
|
||||||
|
|
||||||
issueLogger.info(`Checking for label on $$type`);
|
|
||||||
|
|
||||||
this._operationsLeft -= 1;
|
|
||||||
|
|
||||||
const options = this.client.issues.listEvents.endpoint.merge({
|
|
||||||
owner: context.repo.owner,
|
|
||||||
repo: context.repo.repo,
|
|
||||||
per_page: 100,
|
|
||||||
issue_number: issue.number
|
|
||||||
});
|
|
||||||
|
|
||||||
const events: IIssueEvent[] = await this.client.paginate(options);
|
|
||||||
const reversedEvents = events.reverse();
|
|
||||||
|
|
||||||
const staleLabeledEvent = reversedEvents.find(
|
|
||||||
event => event.event === 'labeled' && event.label.name === label
|
|
||||||
);
|
|
||||||
|
|
||||||
if (!staleLabeledEvent) {
|
|
||||||
// Must be old rather than labeled
|
|
||||||
return undefined;
|
|
||||||
}
|
|
||||||
|
|
||||||
return staleLabeledEvent.created_at;
|
|
||||||
}
|
|
||||||
|
|
||||||
private _getDaysBeforeIssueStale(): number {
|
private _getDaysBeforeIssueStale(): number {
|
||||||
return isNaN(this.options.daysBeforeIssueStale)
|
return isNaN(this.options.daysBeforeIssueStale)
|
||||||
? this.options.daysBeforeStale
|
? this.options.daysBeforeStale
|
||||||
|
@ -724,7 +713,8 @@ export class IssuesProcessor {
|
||||||
`The $$type is no longer stale. Removing the stale label...`
|
`The $$type is no longer stale. Removing the stale label...`
|
||||||
);
|
);
|
||||||
|
|
||||||
return this._removeLabel(issue, staleLabel);
|
await this._removeLabel(issue, staleLabel);
|
||||||
|
this._statistics?.incrementUndoStaleIssuesCount();
|
||||||
}
|
}
|
||||||
|
|
||||||
private async _removeCloseLabel(
|
private async _removeCloseLabel(
|
||||||
|
@ -748,7 +738,8 @@ export class IssuesProcessor {
|
||||||
`The $$type has a close label "${closeLabel}". Removing the close label...`
|
`The $$type has a close label "${closeLabel}". Removing the close label...`
|
||||||
);
|
);
|
||||||
|
|
||||||
return this._removeLabel(issue, closeLabel);
|
await this._removeLabel(issue, closeLabel);
|
||||||
|
this._statistics?.incrementDeletedCloseLabelsCount();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,200 @@
|
||||||
|
import {IIssuesProcessorOptions} from '../interfaces/issues-processor-options';
|
||||||
|
import {Logger} from './loggers/logger';
|
||||||
|
|
||||||
|
export class Statistics {
|
||||||
|
private readonly _logger: Logger = new Logger();
|
||||||
|
private readonly _options: IIssuesProcessorOptions;
|
||||||
|
private _processedIssuesCount = 0;
|
||||||
|
private _staleIssuesCount = 0;
|
||||||
|
private _undoStaleIssuesCount = 0;
|
||||||
|
private _operationsCount = 0;
|
||||||
|
private _closedIssuesCount = 0;
|
||||||
|
private _deletedLabelsCount = 0;
|
||||||
|
private _deletedCloseLabelsCount = 0;
|
||||||
|
private _deletedBranchesCount = 0;
|
||||||
|
private _addedLabelsCount = 0;
|
||||||
|
private _addedCommentsCount = 0;
|
||||||
|
private _fetchedIssuesCount = 0;
|
||||||
|
private _fetchedIssuesEventsCount = 0;
|
||||||
|
private _fetchedIssuesCommentsCount = 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(
|
||||||
|
increment: Readonly<number> = 1
|
||||||
|
): Statistics {
|
||||||
|
this._deletedCloseLabelsCount += increment;
|
||||||
|
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
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(
|
||||||
|
increment: Readonly<number> = 1
|
||||||
|
): Statistics {
|
||||||
|
this._fetchedIssuesEventsCount += increment;
|
||||||
|
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
incrementFetchedIssuesCommentsCount(
|
||||||
|
increment: Readonly<number> = 1
|
||||||
|
): Statistics {
|
||||||
|
this._fetchedIssuesCommentsCount += increment;
|
||||||
|
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
incrementFetchedPullRequestsCount(
|
||||||
|
increment: Readonly<number> = 1
|
||||||
|
): Statistics {
|
||||||
|
this._fetchedPullRequestsCount += increment;
|
||||||
|
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
logStats(): Statistics {
|
||||||
|
this._logger.info('Statistics');
|
||||||
|
this._logProcessedIssuesCount();
|
||||||
|
this._logStaleIssuesCount();
|
||||||
|
this._logUndoStaleIssuesCount();
|
||||||
|
this._logOperationsCount();
|
||||||
|
this._logClosedIssuesCount();
|
||||||
|
this._logDeletedLabelsCount();
|
||||||
|
this._logDeletedCloseLabelsCount();
|
||||||
|
this._logDeletedBranchesCount();
|
||||||
|
this._logAddedLabelsCount();
|
||||||
|
this._logAddedCommentsCount();
|
||||||
|
this._logFetchedIssuesCount();
|
||||||
|
this._logFetchedIssuesEventsCount();
|
||||||
|
this._logFetchedIssuesCommentsCount();
|
||||||
|
this._logFetchedPullRequestsCount();
|
||||||
|
this._logger.info('---');
|
||||||
|
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
private _logProcessedIssuesCount(): void {
|
||||||
|
this._logCount('Processed issues/PRs', this._processedIssuesCount);
|
||||||
|
}
|
||||||
|
|
||||||
|
private _logStaleIssuesCount(): void {
|
||||||
|
this._logCount('New stale issues/PRs', this._staleIssuesCount);
|
||||||
|
}
|
||||||
|
|
||||||
|
private _logUndoStaleIssuesCount(): void {
|
||||||
|
this._logCount('No longer stale issues/PRs', this._undoStaleIssuesCount);
|
||||||
|
}
|
||||||
|
|
||||||
|
private _logOperationsCount(): void {
|
||||||
|
this._logCount('Operations performed', this._operationsCount);
|
||||||
|
}
|
||||||
|
|
||||||
|
private _logClosedIssuesCount(): void {
|
||||||
|
this._logCount('Closed issues', this._closedIssuesCount);
|
||||||
|
}
|
||||||
|
|
||||||
|
private _logDeletedLabelsCount(): void {
|
||||||
|
this._logCount('Deleted labels', this._deletedLabelsCount);
|
||||||
|
}
|
||||||
|
|
||||||
|
private _logDeletedCloseLabelsCount(): void {
|
||||||
|
this._logCount('Deleted close labels', this._deletedCloseLabelsCount);
|
||||||
|
}
|
||||||
|
|
||||||
|
private _logDeletedBranchesCount(): void {
|
||||||
|
this._logCount('Deleted branches', this._deletedBranchesCount);
|
||||||
|
}
|
||||||
|
|
||||||
|
private _logAddedLabelsCount(): void {
|
||||||
|
this._logCount('Added labels', this._addedLabelsCount);
|
||||||
|
}
|
||||||
|
|
||||||
|
private _logAddedCommentsCount(): void {
|
||||||
|
this._logCount('Added comments', this._addedCommentsCount);
|
||||||
|
}
|
||||||
|
|
||||||
|
private _logFetchedIssuesCount(): void {
|
||||||
|
this._logCount('Fetched issues', this._fetchedIssuesCount);
|
||||||
|
}
|
||||||
|
|
||||||
|
private _logFetchedIssuesEventsCount(): void {
|
||||||
|
this._logCount('Fetched issues events', this._fetchedIssuesEventsCount);
|
||||||
|
}
|
||||||
|
|
||||||
|
private _logFetchedIssuesCommentsCount(): void {
|
||||||
|
this._logCount('Fetched issues comments', this._fetchedIssuesCommentsCount);
|
||||||
|
}
|
||||||
|
|
||||||
|
private _logFetchedPullRequestsCount(): void {
|
||||||
|
this._logCount('Fetched pull requests', this._fetchedPullRequestsCount);
|
||||||
|
}
|
||||||
|
|
||||||
|
private _logCount(name: Readonly<string>, count: Readonly<number>): void {
|
||||||
|
if (count > 0) {
|
||||||
|
this._logger.info(`${name}: ${count}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,33 +0,0 @@
|
||||||
import {getIssueType} from './get-issue-type';
|
|
||||||
|
|
||||||
describe('getIssueType()', (): void => {
|
|
||||||
let isPullRequest: boolean;
|
|
||||||
|
|
||||||
describe('when the issue is a not pull request', (): void => {
|
|
||||||
beforeEach((): void => {
|
|
||||||
isPullRequest = false;
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should return that the issue is really an issue', (): void => {
|
|
||||||
expect.assertions(1);
|
|
||||||
|
|
||||||
const result = getIssueType(isPullRequest);
|
|
||||||
|
|
||||||
expect(result).toStrictEqual('issue');
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('when the issue is a pull request', (): void => {
|
|
||||||
beforeEach((): void => {
|
|
||||||
isPullRequest = true;
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should return that the issue is a pull request', (): void => {
|
|
||||||
expect.assertions(1);
|
|
||||||
|
|
||||||
const result = getIssueType(isPullRequest);
|
|
||||||
|
|
||||||
expect(result).toStrictEqual('pr');
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
|
@ -1,5 +0,0 @@
|
||||||
import {IssueType} from '../enums/issue-type';
|
|
||||||
|
|
||||||
export function getIssueType(isPullRequest: Readonly<boolean>): IssueType {
|
|
||||||
return isPullRequest ? IssueType.PullRequest : IssueType.Issue;
|
|
||||||
}
|
|
|
@ -42,4 +42,5 @@ export interface IIssuesProcessorOptions {
|
||||||
exemptAllAssignees: boolean;
|
exemptAllAssignees: boolean;
|
||||||
exemptAllIssueAssignees: boolean | undefined;
|
exemptAllIssueAssignees: boolean | undefined;
|
||||||
exemptAllPrAssignees: boolean | undefined;
|
exemptAllPrAssignees: boolean | undefined;
|
||||||
|
enableStatistics: boolean;
|
||||||
}
|
}
|
||||||
|
|
|
@ -68,7 +68,8 @@ function _getAndValidateArgs(): IIssuesProcessorOptions {
|
||||||
exemptPrAssignees: core.getInput('exempt-pr-assignees'),
|
exemptPrAssignees: core.getInput('exempt-pr-assignees'),
|
||||||
exemptAllAssignees: core.getInput('exempt-all-assignees') === 'true',
|
exemptAllAssignees: core.getInput('exempt-all-assignees') === 'true',
|
||||||
exemptAllIssueAssignees: _toOptionalBoolean('exempt-all-issue-assignees'),
|
exemptAllIssueAssignees: _toOptionalBoolean('exempt-all-issue-assignees'),
|
||||||
exemptAllPrAssignees: _toOptionalBoolean('exempt-all-pr-assignees')
|
exemptAllPrAssignees: _toOptionalBoolean('exempt-all-pr-assignees'),
|
||||||
|
enableStatistics: core.getInput('enable-statistics') === 'true'
|
||||||
};
|
};
|
||||||
|
|
||||||
for (const numberInput of [
|
for (const numberInput of [
|
||||||
|
|
Loading…
Reference in New Issue