mirror of
https://github.com/actions/stale.git
synced 2025-12-23 08:58:17 +00:00
Compare commits
50 Commits
revert-bre
...
v6
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
5ebf00ea0e | ||
|
|
3de2653986 | ||
|
|
02e44c81cc | ||
|
|
99b6c70959 | ||
|
|
8e8a0e6680 | ||
|
|
80962c1062 | ||
|
|
3e4418e47e | ||
|
|
33e37032bb | ||
|
|
97911cb595 | ||
|
|
65afee970e | ||
|
|
48bae5932f | ||
|
|
2b58cc900f | ||
|
|
532554b8a8 | ||
|
|
aaab997cce | ||
|
|
136efb520f | ||
|
|
06d2a3904b | ||
|
|
29e800e1c8 | ||
|
|
6b7f9717b7 | ||
|
|
6c298b192c | ||
|
|
c048b97ed3 | ||
|
|
71b06d0194 | ||
|
|
7bb514d8f8 | ||
|
|
a78f1809b1 | ||
|
|
e577b690d3 | ||
|
|
736a97ebc6 | ||
|
|
b9ccbf0648 | ||
|
|
69c1cc9976 | ||
|
|
8c947ce781 | ||
|
|
4b36ad56cb | ||
|
|
64112b01cc | ||
|
|
d693f556ca | ||
|
|
65d24b7092 | ||
|
|
57708ae6dd | ||
|
|
a88f7b30b9 | ||
|
|
74dfff0835 | ||
|
|
3cc1237663 | ||
|
|
76e9fbc6ae | ||
|
|
6467b96231 | ||
|
|
8af60513da | ||
|
|
7a7efcae1f | ||
|
|
04a1828bc1 | ||
|
|
65ca3956bd | ||
|
|
eee276c280 | ||
|
|
6c2f9f3f54 | ||
|
|
37323f14dd | ||
|
|
3be940e59b | ||
|
|
7d0e5bedbf | ||
|
|
77bfb89501 | ||
|
|
7fb802b307 | ||
|
|
54197c7137 |
9
.github/dependabot.yml
vendored
9
.github/dependabot.yml
vendored
@@ -4,6 +4,11 @@ updates:
|
||||
- package-ecosystem: 'npm'
|
||||
# Look for `package.json` and `lock` files in the `root` directory
|
||||
directory: '/'
|
||||
# Check the npm registry for updates every day (weekdays)
|
||||
# Check the npm registry for updates once a week (Monday)
|
||||
schedule:
|
||||
interval: 'daily'
|
||||
interval: 'weekly'
|
||||
|
||||
- package-ecosystem: 'github-actions'
|
||||
directory: '/'
|
||||
schedule:
|
||||
interval: 'weekly'
|
||||
|
||||
14
.github/workflows/check-dist.yml
vendored
14
.github/workflows/check-dist.yml
vendored
@@ -21,18 +21,20 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions/checkout@v3
|
||||
|
||||
- name: Set Node.js 12.x
|
||||
uses: actions/setup-node@v1
|
||||
- name: Set Node.js 16.x
|
||||
uses: actions/setup-node@v3
|
||||
with:
|
||||
node-version: 12.x
|
||||
node-version: 16.x
|
||||
|
||||
- name: Install dependencies
|
||||
run: npm ci
|
||||
|
||||
- name: Rebuild the dist/ directory
|
||||
run: npm run build
|
||||
run: |
|
||||
npm run build
|
||||
npm run pack
|
||||
|
||||
- name: Compare the expected and actual dist/ directories
|
||||
run: |
|
||||
@@ -44,7 +46,7 @@ jobs:
|
||||
id: diff
|
||||
|
||||
# If index.js was different than expected, upload the expected version as an artifact
|
||||
- uses: actions/upload-artifact@v2
|
||||
- uses: actions/upload-artifact@v3
|
||||
if: ${{ failure() && steps.diff.conclusion == 'failure' }}
|
||||
with:
|
||||
name: dist
|
||||
|
||||
8
.github/workflows/codeql.yml
vendored
8
.github/workflows/codeql.yml
vendored
@@ -13,13 +13,13 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v2
|
||||
uses: actions/checkout@v3
|
||||
|
||||
- name: Initialize CodeQL
|
||||
uses: github/codeql-action/init@v1
|
||||
uses: github/codeql-action/init@v2
|
||||
|
||||
- name: Autobuild
|
||||
uses: github/codeql-action/autobuild@v1
|
||||
uses: github/codeql-action/autobuild@v2
|
||||
|
||||
- name: Perform CodeQL Analysis
|
||||
uses: github/codeql-action/analyze@v1
|
||||
uses: github/codeql-action/analyze@v2
|
||||
|
||||
4
.github/workflows/test.yml
vendored
4
.github/workflows/test.yml
vendored
@@ -10,14 +10,14 @@ jobs:
|
||||
build: # make sure build/ci work properly
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions/checkout@v3
|
||||
- run: |
|
||||
npm ci
|
||||
npm run all:ci
|
||||
test: # make sure the action works on a clean machine without building
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions/checkout@v3
|
||||
- uses: ./
|
||||
id: stale
|
||||
with:
|
||||
|
||||
18
CHANGELOG.md
18
CHANGELOG.md
@@ -1,6 +1,22 @@
|
||||
# Changelog
|
||||
|
||||
Starting in version 4.0.0 we will maintain a changelog
|
||||
# [6.0.0]
|
||||
|
||||
:warning: Breaking change :warning:
|
||||
|
||||
Issues/PRs default `close-issue-reason` is now `not_planned`([#789](https://github.com/actions/stale/issues/789))
|
||||
|
||||
# [5.1.0]
|
||||
|
||||
[Don't process stale issues right after they're marked stale](https://github.com/actions/stale/issues/696)
|
||||
[Add close-issue-reason option][#764](https://github.com/actions/stale/pull/764)[#772](https://github.com/actions/stale/pull/772)
|
||||
Various dependabot/dependency updates
|
||||
|
||||
## [4.1.0](https://github.com/actions/stale/compare/v3.0.19...v4.1.0) (2021-07-14)
|
||||
|
||||
## Features
|
||||
|
||||
- [Ability to exempt draft PRs](https://github.com/actions/stale/commit/9912fa74d1c01b5d6187793d97441019cbe325d0)
|
||||
|
||||
## [4.0.0](https://github.com/actions/stale/compare/v3.0.19...v4.0.0) (2021-07-14)
|
||||
|
||||
|
||||
@@ -46,6 +46,13 @@ Build, lint, package and test everything.
|
||||
$ npm run all
|
||||
```
|
||||
|
||||
IMPORTANT:
|
||||
Be sure to commit the result of:
|
||||
```bash
|
||||
$ npm run pack
|
||||
```
|
||||
Otherwise PR checks will fail.
|
||||
|
||||
# Release
|
||||
|
||||
Based on [standard-version](https://github.com/conventional-changelog/standard-version).
|
||||
|
||||
41
README.md
41
README.md
@@ -11,11 +11,12 @@ The configuration must be on the default branch and the default values will:
|
||||
## Recommended permissions
|
||||
|
||||
For the execution of this action, it must be able to fetch all issues and pull requests from your repository.
|
||||
In addition, based on the provided configuration, the action could require more permission(s) (e.g.: add label, remove label, comment, close, etc.).
|
||||
In addition, based on the provided configuration, the action could require more permission(s) (e.g.: add label, remove label, comment, close, delete branch, etc.).
|
||||
This can be achieved with the following [configuration in the action](https://docs.github.com/en/actions/reference/workflow-syntax-for-github-actions#permissions) if the permissions are restricted:
|
||||
|
||||
```yaml
|
||||
permissions:
|
||||
contents: write # only for delete-branch option
|
||||
issues: write
|
||||
pull-requests: write
|
||||
```
|
||||
@@ -43,6 +44,7 @@ Every argument is optional.
|
||||
| [close-pr-message](#close-pr-message) | Comment on the staled PRs while closed | |
|
||||
| [stale-issue-label](#stale-issue-label) | Label to apply on staled issues | `Stale` |
|
||||
| [close-issue-label](#close-issue-label) | Label to apply on closed issues | |
|
||||
| [close-issue-reason](#close-issue-reason) | Reason to use when closing issues | `not_planned` |
|
||||
| [stale-pr-label](#stale-pr-label) | Label to apply on staled PRs | `Stale` |
|
||||
| [close-pr-label](#close-pr-label) | Label to apply on closed PRs | |
|
||||
| [exempt-issue-labels](#exempt-issue-labels) | Labels on issues exempted from stale | |
|
||||
@@ -80,6 +82,7 @@ Every argument is optional.
|
||||
| [ignore-updates](#ignore-updates) | Any update (update/comment) can reset the stale idle time on the issues/PRs | `false` |
|
||||
| [ignore-issue-updates](#ignore-issue-updates) | Override [ignore-updates](#ignore-updates) for issues only | |
|
||||
| [ignore-pr-updates](#ignore-pr-updates) | Override [ignore-updates](#ignore-updates) for PRs only | |
|
||||
| [include-only-assigned](#include-only-assigned) | Process only assigned issues | `false` |
|
||||
|
||||
### List of output options
|
||||
|
||||
@@ -219,6 +222,12 @@ It will be automatically removed if the issues are no longer closed nor locked.
|
||||
Default value: unset
|
||||
Required Permission: `issues: write`
|
||||
|
||||
#### close-issue-reason
|
||||
|
||||
Specify the [reason](https://github.blog/changelog/2022-05-19-the-new-github-issues-may-19th-update/) used when closing issues. Valid values are `completed` and `not_planned`.
|
||||
|
||||
Default value: `not_planned`
|
||||
|
||||
#### stale-pr-label
|
||||
|
||||
The label that will be added to the pull requests when automatically marked as stale.
|
||||
@@ -389,7 +398,7 @@ Default value: unset
|
||||
If set to `true`, the stale workflow will automatically delete the GitHub branches related to the pull requests automatically closed by the stale workflow.
|
||||
|
||||
Default value: `false`
|
||||
Required Permission: `pull-requests: write`
|
||||
Required Permission: `pull-requests: write` and `contents: write`
|
||||
|
||||
#### exempt-milestones
|
||||
|
||||
@@ -509,6 +518,12 @@ Useful to override [ignore-updates](#ignore-updates) but only to ignore the upda
|
||||
|
||||
Default value: unset
|
||||
|
||||
#### include-only-assigned
|
||||
|
||||
If set to `true`, only the issues or the pull requests with an assignee will be marked as stale automatically.
|
||||
|
||||
Default value: `false`
|
||||
|
||||
### Usage
|
||||
|
||||
See also [action.yml](./action.yml) for a comprehensive list of all the options.
|
||||
@@ -525,7 +540,7 @@ jobs:
|
||||
stale:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/stale@v4
|
||||
- uses: actions/stale@v6
|
||||
with:
|
||||
stale-issue-message: 'Message to comment on stale issues. If none provided, will not mark issues stale'
|
||||
stale-pr-message: 'Message to comment on stale PRs. If none provided, will not mark PRs stale'
|
||||
@@ -543,7 +558,7 @@ jobs:
|
||||
stale:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/stale@v4
|
||||
- uses: actions/stale@v6
|
||||
with:
|
||||
stale-issue-message: 'This issue is stale because it has been open 30 days with no activity. Remove stale label or comment or this will be closed in 5 days.'
|
||||
days-before-stale: 30
|
||||
@@ -562,7 +577,7 @@ jobs:
|
||||
stale:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/stale@v4
|
||||
- uses: actions/stale@v6
|
||||
with:
|
||||
stale-issue-message: 'This issue is stale because it has been open 30 days with no activity. Remove stale label or comment or this will be closed in 5 days.'
|
||||
stale-pr-message: 'This PR is stale because it has been open 45 days with no activity. Remove stale label or comment or this will be closed in 10 days.'
|
||||
@@ -584,7 +599,7 @@ jobs:
|
||||
stale:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/stale@v4
|
||||
- uses: actions/stale@v6
|
||||
with:
|
||||
stale-issue-message: 'This issue is stale because it has been open 30 days with no activity. Remove stale label or comment or this will be closed in 5 days.'
|
||||
stale-pr-message: 'This PR is stale because it has been open 45 days with no activity. Remove stale label or comment or this will be closed in 10 days.'
|
||||
@@ -608,7 +623,7 @@ jobs:
|
||||
stale:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/stale@v4
|
||||
- uses: actions/stale@v6
|
||||
with:
|
||||
stale-issue-message: 'Stale issue message'
|
||||
stale-pr-message: 'Stale pull request message'
|
||||
@@ -631,7 +646,7 @@ jobs:
|
||||
stale:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/stale@v4
|
||||
- uses: actions/stale@v6
|
||||
with:
|
||||
start-date: '2020-04-18T00:00:00Z' # ISO 8601 or RFC 2822
|
||||
```
|
||||
@@ -648,7 +663,7 @@ jobs:
|
||||
stale:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/stale@v4
|
||||
- uses: actions/stale@v6
|
||||
with:
|
||||
exempt-issue-milestones: 'future,alpha,beta'
|
||||
exempt-pr-milestones: 'bugfix,improvement'
|
||||
@@ -666,7 +681,7 @@ jobs:
|
||||
stale:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/stale@v4
|
||||
- uses: actions/stale@v6
|
||||
with:
|
||||
exempt-all-pr-milestones: true
|
||||
```
|
||||
@@ -683,7 +698,7 @@ jobs:
|
||||
stale:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/stale@v4
|
||||
- uses: actions/stale@v6
|
||||
with:
|
||||
any-of-labels: 'needs-more-info,needs-demo'
|
||||
# You can opt for 'only-labels' instead if your use-case requires all labels
|
||||
@@ -702,7 +717,7 @@ jobs:
|
||||
stale:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/stale@v4
|
||||
- uses: actions/stale@v6
|
||||
with:
|
||||
exempt-issue-assignees: 'marco,polo'
|
||||
exempt-pr-assignees: 'marco'
|
||||
@@ -720,7 +735,7 @@ jobs:
|
||||
stale:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/stale@v4
|
||||
- uses: actions/stale@v6
|
||||
with:
|
||||
exempt-all-pr-assignees: true
|
||||
```
|
||||
|
||||
@@ -1103,7 +1103,7 @@ class IssuesProcessorBuilder {
|
||||
issue.updated_at ?? new Date().toDateString(),
|
||||
issue.created_at ?? new Date().toDateString(),
|
||||
!!issue.pull_request,
|
||||
issue.labels ? issue.labels.map(label => label.name) : []
|
||||
issue.labels ? issue.labels.map(label => label.name || '') : []
|
||||
)
|
||||
);
|
||||
|
||||
|
||||
@@ -9,7 +9,7 @@ export class IssuesProcessorMock extends IssuesProcessor {
|
||||
options: IIssuesProcessorOptions,
|
||||
getIssues?: (page: number) => Promise<Issue[]>,
|
||||
listIssueComments?: (
|
||||
issueNumber: number,
|
||||
issue: Issue,
|
||||
sinceDate: string
|
||||
) => Promise<IComment[]>,
|
||||
getLabelCreationDate?: (
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
import {IIssuesProcessorOptions} from '../../src/interfaces/issues-processor-options';
|
||||
|
||||
// Default options for use in tests.
|
||||
// Mirrors the defaults defined in action.yml
|
||||
export const DefaultProcessorOptions: IIssuesProcessorOptions = Object.freeze({
|
||||
repoToken: 'none',
|
||||
staleIssueMessage: 'This issue is stale',
|
||||
@@ -50,5 +52,7 @@ export const DefaultProcessorOptions: IIssuesProcessorOptions = Object.freeze({
|
||||
ignoreUpdates: false,
|
||||
ignoreIssueUpdates: undefined,
|
||||
ignorePrUpdates: undefined,
|
||||
exemptDraftPr: false
|
||||
exemptDraftPr: false,
|
||||
closeIssueReason: 'not_planned',
|
||||
includeOnlyAssigned: false
|
||||
});
|
||||
|
||||
@@ -85,7 +85,7 @@ class IssuesProcessorBuilder {
|
||||
issue.updated_at ?? new Date().toDateString(),
|
||||
issue.created_at ?? new Date().toDateString(),
|
||||
!!issue.pull_request,
|
||||
issue.labels ? issue.labels.map(label => label.name) : []
|
||||
issue.labels ? issue.labels.map(label => label.name || '') : []
|
||||
)
|
||||
);
|
||||
|
||||
|
||||
@@ -2352,3 +2352,69 @@ test('processing a pull request to be stale with the "stalePrMessage" option set
|
||||
expect(processor.closedIssues).toHaveLength(0);
|
||||
expect(processor.statistics?.addedPullRequestsCommentsCount).toStrictEqual(0);
|
||||
});
|
||||
|
||||
test('processing an issue with the "includeOnlyAssigned" option and nonempty assignee list will stale the issue', async () => {
|
||||
const issueDate = new Date();
|
||||
issueDate.setDate(issueDate.getDate() - 2);
|
||||
|
||||
const opts: IIssuesProcessorOptions = {
|
||||
...DefaultProcessorOptions,
|
||||
staleIssueLabel: 'This issue is stale',
|
||||
includeOnlyAssigned: true
|
||||
};
|
||||
|
||||
const TestIssueList: Issue[] = [
|
||||
generateIssue(
|
||||
opts,
|
||||
1,
|
||||
'An issue with no label',
|
||||
issueDate.toDateString(),
|
||||
issueDate.toDateString(),
|
||||
false,
|
||||
[],
|
||||
false,
|
||||
false,
|
||||
undefined,
|
||||
['assignee1']
|
||||
)
|
||||
];
|
||||
const processor = new IssuesProcessorMock(
|
||||
opts,
|
||||
async p => (p === 1 ? TestIssueList : []),
|
||||
async () => [],
|
||||
async () => new Date().toDateString()
|
||||
);
|
||||
|
||||
// process our fake issue list
|
||||
await processor.processIssues(1);
|
||||
|
||||
expect(processor.staleIssues).toHaveLength(1);
|
||||
expect(processor.closedIssues).toHaveLength(0);
|
||||
});
|
||||
|
||||
test('processing an issue with the "includeOnlyAssigned" option set and no assignees will not stale the issue', async () => {
|
||||
const issueDate = new Date();
|
||||
issueDate.setDate(issueDate.getDate() - 2);
|
||||
|
||||
const opts: IIssuesProcessorOptions = {
|
||||
...DefaultProcessorOptions,
|
||||
staleIssueLabel: 'This issue is stale',
|
||||
includeOnlyAssigned: true
|
||||
};
|
||||
|
||||
const TestIssueList: Issue[] = [
|
||||
generateIssue(opts, 1, 'An issue with no label', issueDate.toDateString())
|
||||
];
|
||||
const processor = new IssuesProcessorMock(
|
||||
opts,
|
||||
async p => (p === 1 ? TestIssueList : []),
|
||||
async () => [],
|
||||
async () => new Date().toDateString()
|
||||
);
|
||||
|
||||
// process our fake issue list
|
||||
await processor.processIssues(1);
|
||||
|
||||
expect(processor.staleIssues).toHaveLength(0);
|
||||
expect(processor.closedIssues).toHaveLength(0);
|
||||
});
|
||||
|
||||
@@ -1103,7 +1103,7 @@ class IssuesProcessorBuilder {
|
||||
issue.updated_at ?? new Date().toDateString(),
|
||||
issue.created_at ?? new Date().toDateString(),
|
||||
!!issue.pull_request,
|
||||
issue.labels ? issue.labels.map(label => label.name) : []
|
||||
issue.labels ? issue.labels.map(label => label.name || '') : []
|
||||
)
|
||||
);
|
||||
|
||||
|
||||
@@ -455,7 +455,7 @@ class IssuesProcessorBuilder {
|
||||
issue.updated_at ?? new Date().toDateString(),
|
||||
issue.created_at ?? new Date().toDateString(),
|
||||
!!issue.pull_request,
|
||||
issue.labels ? issue.labels.map(label => label.name) : []
|
||||
issue.labels ? issue.labels.map(label => label.name || '') : []
|
||||
)
|
||||
);
|
||||
|
||||
|
||||
10
action.yml
10
action.yml
@@ -49,6 +49,10 @@ inputs:
|
||||
description: 'The labels that mean an issue is exempt from being marked stale. Separate multiple labels with commas (eg. "label1,label2").'
|
||||
default: ''
|
||||
required: false
|
||||
close-issue-reason:
|
||||
description: 'The reason to use when closing an issue.'
|
||||
default: 'not_planned'
|
||||
required: false
|
||||
stale-pr-label:
|
||||
description: 'The label to apply when a pull request is stale.'
|
||||
default: 'Stale'
|
||||
@@ -192,11 +196,15 @@ inputs:
|
||||
description: 'Any update (update/comment) can reset the stale idle time on the pull requests. Override "ignore-updates" option regarding only the pull requests.'
|
||||
default: ''
|
||||
required: false
|
||||
include-only-assigned:
|
||||
description: 'Only the issues or the pull requests with an assignee will be marked as stale automatically.'
|
||||
default: 'false'
|
||||
required: false
|
||||
outputs:
|
||||
closed-issues-prs:
|
||||
description: 'List of all closed issues and pull requests.'
|
||||
staled-issues-prs:
|
||||
description: 'List of all staled issues and pull requests.'
|
||||
runs:
|
||||
using: 'node12'
|
||||
using: 'node16'
|
||||
main: 'dist/index.js'
|
||||
|
||||
5990
dist/index.js
vendored
5990
dist/index.js
vendored
File diff suppressed because one or more lines are too long
13088
package-lock.json
generated
13088
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
16
package.json
16
package.json
@@ -6,8 +6,8 @@
|
||||
"main": "lib/main.js",
|
||||
"scripts": {
|
||||
"build": "tsc --project tsconfig.app.json",
|
||||
"format": "prettier --write --ignore-unknown **/*.{md,json,yml,ts}",
|
||||
"format-check": "prettier --check --ignore-unknown **/*.{md,json,yml,ts}",
|
||||
"format": "prettier --write --ignore-unknown **/*.{json,yml,ts}",
|
||||
"format-check": "prettier --check --ignore-unknown **/*.{json,yml,ts}",
|
||||
"lint": "eslint src/**/*.ts",
|
||||
"lint:fix": "eslint src/**/*.ts --fix",
|
||||
"lint:all": "npm run format-check && npm run lint",
|
||||
@@ -38,8 +38,8 @@
|
||||
"author": "GitHub",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@actions/core": "^1.2.6",
|
||||
"@actions/github": "^4.0.0",
|
||||
"@actions/core": "^1.10.0",
|
||||
"@actions/github": "^5.0.1",
|
||||
"lodash.deburr": "^4.1.0",
|
||||
"semver": "^7.3.5"
|
||||
},
|
||||
@@ -54,15 +54,15 @@
|
||||
"ansi-styles": "5.2.0",
|
||||
"eslint": "^7.28.0",
|
||||
"eslint-plugin-github": "^4.1.2",
|
||||
"eslint-plugin-jest": "^24.4.2",
|
||||
"eslint-plugin-jest": "^25.3.2",
|
||||
"jest": "^27.2.5",
|
||||
"jest-circus": "^27.2.0",
|
||||
"jest-circus": "^27.4.6",
|
||||
"jest-silent-reporter": "^0.5.0",
|
||||
"js-yaml": "^4.1.0",
|
||||
"prettier": "^2.4.1",
|
||||
"prettier": "^2.5.1",
|
||||
"standard-version": "^9.3.1",
|
||||
"terminal-link": "^2.1.1",
|
||||
"ts-jest": "^27.0.5",
|
||||
"ts-jest": "^27.1.2",
|
||||
"typescript": "^4.3.2"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -61,7 +61,9 @@ describe('Issue', (): void => {
|
||||
ignoreUpdates: false,
|
||||
ignoreIssueUpdates: undefined,
|
||||
ignorePrUpdates: undefined,
|
||||
exemptDraftPr: false
|
||||
exemptDraftPr: false,
|
||||
closeIssueReason: '',
|
||||
includeOnlyAssigned: false
|
||||
};
|
||||
issueInterface = {
|
||||
title: 'dummy-title',
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import {isLabeled} from '../functions/is-labeled';
|
||||
import {isPullRequest} from '../functions/is-pull-request';
|
||||
import {Assignee} from '../interfaces/assignee';
|
||||
import {IIssue} from '../interfaces/issue';
|
||||
import {IIssue, OctokitIssue} from '../interfaces/issue';
|
||||
import {IIssuesProcessorOptions} from '../interfaces/issues-processor-options';
|
||||
import {ILabel} from '../interfaces/label';
|
||||
import {IMilestone} from '../interfaces/milestone';
|
||||
@@ -17,28 +17,30 @@ export class Issue implements IIssue {
|
||||
readonly pull_request: Object | null | undefined;
|
||||
readonly state: string | 'closed' | 'open';
|
||||
readonly locked: boolean;
|
||||
readonly milestone: IMilestone | undefined;
|
||||
readonly milestone?: IMilestone | null;
|
||||
readonly assignees: Assignee[];
|
||||
isStale: boolean;
|
||||
markedStaleThisRun: boolean;
|
||||
operations = new Operations();
|
||||
private readonly _options: IIssuesProcessorOptions;
|
||||
|
||||
constructor(
|
||||
options: Readonly<IIssuesProcessorOptions>,
|
||||
issue: Readonly<IIssue>
|
||||
issue: Readonly<OctokitIssue> | Readonly<IIssue>
|
||||
) {
|
||||
this._options = options;
|
||||
this.title = issue.title;
|
||||
this.number = issue.number;
|
||||
this.created_at = issue.created_at;
|
||||
this.updated_at = issue.updated_at;
|
||||
this.labels = issue.labels;
|
||||
this.labels = mapLabels(issue.labels);
|
||||
this.pull_request = issue.pull_request;
|
||||
this.state = issue.state;
|
||||
this.locked = issue.locked;
|
||||
this.milestone = issue.milestone;
|
||||
this.assignees = issue.assignees;
|
||||
this.assignees = issue.assignees || [];
|
||||
this.isStale = isLabeled(this, this.staleLabel);
|
||||
this.markedStaleThisRun = false;
|
||||
}
|
||||
|
||||
get isPullRequest(): boolean {
|
||||
@@ -59,3 +61,14 @@ export class Issue implements IIssue {
|
||||
: this._options.staleIssueLabel;
|
||||
}
|
||||
}
|
||||
|
||||
function mapLabels(labels: (string | ILabel)[] | ILabel[]): ILabel[] {
|
||||
return labels.map(label => {
|
||||
if (typeof label == 'string') {
|
||||
return {
|
||||
name: label
|
||||
};
|
||||
}
|
||||
return label;
|
||||
});
|
||||
}
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
import * as core from '@actions/core';
|
||||
import {context, getOctokit} from '@actions/github';
|
||||
import {GitHub} from '@actions/github/lib/utils';
|
||||
import {GetResponseTypeFromEndpointMethod} from '@octokit/types';
|
||||
import {Option} from '../enums/option';
|
||||
import {getHumanizedDate} from '../functions/dates/get-humanized-date';
|
||||
import {isDateMoreRecentThan} from '../functions/dates/is-date-more-recent-than';
|
||||
@@ -25,7 +24,7 @@ import {Milestones} from './milestones';
|
||||
import {StaleOperations} from './stale-operations';
|
||||
import {Statistics} from './statistics';
|
||||
import {LoggerService} from '../services/logger.service';
|
||||
import {IIssue} from '../interfaces/issue';
|
||||
import {OctokitIssue} from '../interfaces/issue';
|
||||
|
||||
/***
|
||||
* Handle processing of issues for staleness/closure.
|
||||
@@ -222,6 +221,14 @@ export class IssuesProcessor {
|
||||
return; // Don't process locked issues
|
||||
}
|
||||
|
||||
if (this._isIncludeOnlyAssigned(issue)) {
|
||||
issueLogger.info(
|
||||
`Skipping this $$type because its assignees list is empty`
|
||||
);
|
||||
IssuesProcessor._endIssueProcessing(issue);
|
||||
return; // If the issue has an 'include-only-assigned' option set, process only issues with nonempty assignees list
|
||||
}
|
||||
|
||||
const onlyLabels: string[] = wordsToList(this._getOnlyLabels(issue));
|
||||
|
||||
if (onlyLabels.length > 0) {
|
||||
@@ -466,6 +473,7 @@ export class IssuesProcessor {
|
||||
);
|
||||
await this._markStale(issue, staleMessage, staleLabel, skipMessage);
|
||||
issue.isStale = true; // This issue is now considered stale
|
||||
issue.markedStaleThisRun = true;
|
||||
issueLogger.info(`This $$type is now stale`);
|
||||
} else {
|
||||
issueLogger.info(
|
||||
@@ -510,17 +518,17 @@ export class IssuesProcessor {
|
||||
|
||||
// Grab comments for an issue since a given date
|
||||
async listIssueComments(
|
||||
issueNumber: Readonly<number>,
|
||||
issue: Readonly<Issue>,
|
||||
sinceDate: Readonly<string>
|
||||
): Promise<IComment[]> {
|
||||
// Find any comments since date on the given issue
|
||||
try {
|
||||
this.operations.consumeOperation();
|
||||
this._consumeIssueOperation(issue);
|
||||
this.statistics?.incrementFetchedItemsCommentsCount();
|
||||
const comments = await this.client.issues.listComments({
|
||||
const comments = await this.client.rest.issues.listComments({
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
issue_number: issueNumber,
|
||||
issue_number: issue.number,
|
||||
since: sinceDate
|
||||
});
|
||||
return comments.data;
|
||||
@@ -532,14 +540,9 @@ export class IssuesProcessor {
|
||||
|
||||
// grab issues from github in batches of 100
|
||||
async getIssues(page: number): Promise<Issue[]> {
|
||||
// generate type for response
|
||||
const endpoint = this.client.issues.listForRepo;
|
||||
type OctoKitIssueList = GetResponseTypeFromEndpointMethod<typeof endpoint>;
|
||||
|
||||
try {
|
||||
this.operations.consumeOperation();
|
||||
const issueResult: OctoKitIssueList =
|
||||
await this.client.issues.listForRepo({
|
||||
const issueResult = await this.client.rest.issues.listForRepo({
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
state: 'open',
|
||||
@@ -550,7 +553,7 @@ export class IssuesProcessor {
|
||||
this.statistics?.incrementFetchedItemsCount(issueResult.data.length);
|
||||
|
||||
return issueResult.data.map(
|
||||
(issue: Readonly<IIssue>): Issue => new Issue(this.options, issue)
|
||||
(issue: Readonly<OctokitIssue>): Issue => new Issue(this.options, issue)
|
||||
);
|
||||
} catch (error) {
|
||||
this._logger.error(`Get issues for repo error: ${error.message}`);
|
||||
@@ -570,7 +573,7 @@ export class IssuesProcessor {
|
||||
|
||||
this._consumeIssueOperation(issue);
|
||||
this.statistics?.incrementFetchedItemsEventsCount();
|
||||
const options = this.client.issues.listEvents.endpoint.merge({
|
||||
const options = this.client.rest.issues.listEvents.endpoint.merge({
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
per_page: 100,
|
||||
@@ -601,7 +604,7 @@ export class IssuesProcessor {
|
||||
this._consumeIssueOperation(issue);
|
||||
this.statistics?.incrementFetchedPullRequestsCount();
|
||||
|
||||
const pullRequest = await this.client.pulls.get({
|
||||
const pullRequest = await this.client.rest.pulls.get({
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
pull_number: issue.number
|
||||
@@ -630,13 +633,15 @@ export class IssuesProcessor {
|
||||
`$$type marked stale on: ${LoggerService.cyan(markedStaleOn)}`
|
||||
);
|
||||
|
||||
const issueHasComments: boolean = await this._hasCommentsSince(
|
||||
const issueHasCommentsSinceStale: boolean = await this._hasCommentsSince(
|
||||
issue,
|
||||
markedStaleOn,
|
||||
staleMessage
|
||||
);
|
||||
issueLogger.info(
|
||||
`$$type has been commented on: ${LoggerService.cyan(issueHasComments)}`
|
||||
`$$type has been commented on: ${LoggerService.cyan(
|
||||
issueHasCommentsSinceStale
|
||||
)}`
|
||||
);
|
||||
|
||||
const daysBeforeClose: number = issue.isPullRequest
|
||||
@@ -647,14 +652,6 @@ export class IssuesProcessor {
|
||||
`Days before $$type close: ${LoggerService.cyan(daysBeforeClose)}`
|
||||
);
|
||||
|
||||
const issueHasUpdate: boolean = IssuesProcessor._updatedSince(
|
||||
issue.updated_at,
|
||||
daysBeforeClose
|
||||
);
|
||||
issueLogger.info(
|
||||
`$$type has been updated: ${LoggerService.cyan(issueHasUpdate)}`
|
||||
);
|
||||
|
||||
const shouldRemoveStaleWhenUpdated: boolean =
|
||||
this._shouldRemoveStaleWhenUpdated(issue);
|
||||
|
||||
@@ -672,10 +669,32 @@ export class IssuesProcessor {
|
||||
);
|
||||
}
|
||||
|
||||
// Should we un-stale this issue?
|
||||
if (shouldRemoveStaleWhenUpdated && issueHasComments) {
|
||||
if (issue.markedStaleThisRun) {
|
||||
issueLogger.info(`marked stale this run, so don't check for updates`);
|
||||
}
|
||||
|
||||
// The issue.updated_at and markedStaleOn are not always exactly in sync (they can be off by a second or 2)
|
||||
// isDateMoreRecentThan makes sure they are not the same date within a certain tolerance (15 seconds in this case)
|
||||
const issueHasUpdateSinceStale = isDateMoreRecentThan(
|
||||
new Date(issue.updated_at),
|
||||
new Date(markedStaleOn),
|
||||
15
|
||||
);
|
||||
|
||||
issueLogger.info(
|
||||
`Remove the stale label since the $$type has a comment and the workflow should remove the stale label when updated`
|
||||
`$$type has been updated since it was marked stale: ${LoggerService.cyan(
|
||||
issueHasUpdateSinceStale
|
||||
)}`
|
||||
);
|
||||
|
||||
// Should we un-stale this issue?
|
||||
if (
|
||||
shouldRemoveStaleWhenUpdated &&
|
||||
(issueHasUpdateSinceStale || issueHasCommentsSinceStale) &&
|
||||
!issue.markedStaleThisRun
|
||||
) {
|
||||
issueLogger.info(
|
||||
`Remove the stale label since the $$type has been updated and the workflow should remove the stale label when updated`
|
||||
);
|
||||
await this._removeStaleLabel(issue, staleLabel);
|
||||
|
||||
@@ -693,7 +712,17 @@ export class IssuesProcessor {
|
||||
return; // Nothing to do because we aren't closing stale issues
|
||||
}
|
||||
|
||||
if (!issueHasComments && !issueHasUpdate) {
|
||||
const issueHasUpdateInCloseWindow: boolean = IssuesProcessor._updatedSince(
|
||||
issue.updated_at,
|
||||
daysBeforeClose
|
||||
);
|
||||
issueLogger.info(
|
||||
`$$type has been updated in the last ${daysBeforeClose} days: ${LoggerService.cyan(
|
||||
issueHasUpdateInCloseWindow
|
||||
)}`
|
||||
);
|
||||
|
||||
if (!issueHasCommentsSinceStale && !issueHasUpdateInCloseWindow) {
|
||||
issueLogger.info(
|
||||
`Closing $$type because it was last updated on: ${LoggerService.cyan(
|
||||
issue.updated_at
|
||||
@@ -712,7 +741,7 @@ export class IssuesProcessor {
|
||||
}
|
||||
} else {
|
||||
issueLogger.info(
|
||||
`Stale $$type is not old enough to close yet (hasComments? ${issueHasComments}, hasUpdate? ${issueHasUpdate})`
|
||||
`Stale $$type is not old enough to close yet (hasComments? ${issueHasCommentsSinceStale}, hasUpdate? ${issueHasUpdateInCloseWindow})`
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -734,12 +763,12 @@ export class IssuesProcessor {
|
||||
}
|
||||
|
||||
// find any comments since the date
|
||||
const comments = await this.listIssueComments(issue.number, sinceDate);
|
||||
const comments = await this.listIssueComments(issue, sinceDate);
|
||||
|
||||
const filteredComments = comments.filter(
|
||||
comment =>
|
||||
comment.user.type === 'User' &&
|
||||
comment.body.toLowerCase() !== staleMessage.toLowerCase()
|
||||
comment.user?.type === 'User' &&
|
||||
comment.body?.toLowerCase() !== staleMessage.toLowerCase()
|
||||
);
|
||||
|
||||
issueLogger.info(
|
||||
@@ -775,7 +804,7 @@ export class IssuesProcessor {
|
||||
this.statistics?.incrementAddedItemsComment(issue);
|
||||
|
||||
if (!this.options.debugOnly) {
|
||||
await this.client.issues.createComment({
|
||||
await this.client.rest.issues.createComment({
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
issue_number: issue.number,
|
||||
@@ -793,7 +822,7 @@ export class IssuesProcessor {
|
||||
this.statistics?.incrementStaleItemsCount(issue);
|
||||
|
||||
if (!this.options.debugOnly) {
|
||||
await this.client.issues.addLabels({
|
||||
await this.client.rest.issues.addLabels({
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
issue_number: issue.number,
|
||||
@@ -823,7 +852,7 @@ export class IssuesProcessor {
|
||||
this.addedCloseCommentIssues.push(issue);
|
||||
|
||||
if (!this.options.debugOnly) {
|
||||
await this.client.issues.createComment({
|
||||
await this.client.rest.issues.createComment({
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
issue_number: issue.number,
|
||||
@@ -841,7 +870,7 @@ export class IssuesProcessor {
|
||||
this.statistics?.incrementAddedItemsLabel(issue);
|
||||
|
||||
if (!this.options.debugOnly) {
|
||||
await this.client.issues.addLabels({
|
||||
await this.client.rest.issues.addLabels({
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
issue_number: issue.number,
|
||||
@@ -858,11 +887,12 @@ export class IssuesProcessor {
|
||||
this.statistics?.incrementClosedItemsCount(issue);
|
||||
|
||||
if (!this.options.debugOnly) {
|
||||
await this.client.issues.update({
|
||||
await this.client.rest.issues.update({
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
issue_number: issue.number,
|
||||
state: 'closed'
|
||||
state: 'closed',
|
||||
state_reason: this.options.closeIssueReason || undefined
|
||||
});
|
||||
}
|
||||
} catch (error) {
|
||||
@@ -900,7 +930,7 @@ export class IssuesProcessor {
|
||||
this.statistics?.incrementDeletedBranchesCount();
|
||||
|
||||
if (!this.options.debugOnly) {
|
||||
await this.client.git.deleteRef({
|
||||
await this.client.rest.git.deleteRef({
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
ref: `heads/${branch}`
|
||||
@@ -935,7 +965,7 @@ export class IssuesProcessor {
|
||||
this.statistics?.incrementDeletedItemsLabelsCount(issue);
|
||||
|
||||
if (!this.options.debugOnly) {
|
||||
await this.client.issues.removeLabel({
|
||||
await this.client.rest.issues.removeLabel({
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
issue_number: issue.number,
|
||||
@@ -995,6 +1025,10 @@ export class IssuesProcessor {
|
||||
return this.options.onlyLabels;
|
||||
}
|
||||
|
||||
private _isIncludeOnlyAssigned(issue: Issue): boolean {
|
||||
return this.options.includeOnlyAssigned && !issue.hasAssignees;
|
||||
}
|
||||
|
||||
private _getAnyOfLabels(issue: Issue): string {
|
||||
if (issue.isPullRequest) {
|
||||
if (this.options.anyOfPrLabels !== '') {
|
||||
@@ -1065,10 +1099,10 @@ export class IssuesProcessor {
|
||||
this.addedLabelIssues.push(issue);
|
||||
|
||||
try {
|
||||
this.operations.consumeOperation();
|
||||
this._consumeIssueOperation(issue);
|
||||
this.statistics?.incrementAddedItemsLabel(issue);
|
||||
if (!this.options.debugOnly) {
|
||||
await this.client.issues.addLabels({
|
||||
await this.client.rest.issues.addLabels({
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
issue_number: issue.number,
|
||||
|
||||
@@ -46,5 +46,6 @@ export enum Option {
|
||||
IgnoreUpdates = 'ignore-updates',
|
||||
IgnoreIssueUpdates = 'ignore-issue-updates',
|
||||
IgnorePrUpdates = 'ignore-pr-updates',
|
||||
ExemptDraftPr = 'exempt-draft-pr'
|
||||
ExemptDraftPr = 'exempt-draft-pr',
|
||||
CloseIssueReason = 'close-issue-reason'
|
||||
}
|
||||
|
||||
@@ -9,6 +9,6 @@ import {CleanLabel} from '../types/clean-label';
|
||||
*
|
||||
* @return {string} A lowercased, deburred version of the passed in label
|
||||
*/
|
||||
export function cleanLabel(label: Readonly<string>): CleanLabel {
|
||||
return deburr(label.toLowerCase());
|
||||
export function cleanLabel(label?: Readonly<string>): CleanLabel {
|
||||
return deburr(label?.toLowerCase());
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import {isDateMoreRecentThan} from './is-date-more-recent-than';
|
||||
import {isDateEqualTo, isDateMoreRecentThan} from './is-date-more-recent-than';
|
||||
|
||||
describe('isDateMoreRecentThan()', (): void => {
|
||||
let date: Date;
|
||||
@@ -48,4 +48,68 @@ describe('isDateMoreRecentThan()', (): void => {
|
||||
expect(result).toStrictEqual(true);
|
||||
});
|
||||
});
|
||||
|
||||
describe('date equality', (): void => {
|
||||
it('should correctly compare a before date outside tolerance', (): void => {
|
||||
const aDate = new Date('2022-09-09T13:00:00');
|
||||
const otherDate = new Date('2022-09-09T14:00:00');
|
||||
expect(isDateEqualTo(aDate, otherDate, 60)).toBe(false);
|
||||
});
|
||||
|
||||
it('should correctly compare a before date inside tolerance', (): void => {
|
||||
const aDate = new Date('2022-09-09T13:00:00');
|
||||
const otherDate = new Date('2022-09-09T13:00:42');
|
||||
expect(isDateEqualTo(aDate, otherDate, 60)).toBe(true);
|
||||
});
|
||||
|
||||
it('should correctly compare an after date outside tolerance', (): void => {
|
||||
const aDate = new Date('2022-09-09T13:00:00');
|
||||
const otherDate = new Date('2022-09-09T12:00:00');
|
||||
expect(isDateEqualTo(aDate, otherDate, 60)).toBe(false);
|
||||
});
|
||||
|
||||
it('should correctly compare an after date inside tolerance', (): void => {
|
||||
const aDate = new Date('2022-09-09T13:00:00');
|
||||
const otherDate = new Date('2022-09-09T12:59:42');
|
||||
expect(isDateEqualTo(aDate, otherDate, 60)).toBe(true);
|
||||
});
|
||||
|
||||
it('should correctly compare an exactly equal date', (): void => {
|
||||
const aDate = new Date('2022-09-09T13:00:00');
|
||||
const otherDate = new Date('2022-09-09T13:00:00');
|
||||
expect(isDateEqualTo(aDate, otherDate, 60)).toBe(true);
|
||||
});
|
||||
});
|
||||
|
||||
describe('date comparison with tolerances', (): void => {
|
||||
it('should correctly compare a before date outside tolerance', (): void => {
|
||||
const aDate = new Date('2022-09-09T13:00:00');
|
||||
const otherDate = new Date('2022-09-09T14:00:00');
|
||||
expect(isDateMoreRecentThan(aDate, otherDate)).toBe(false);
|
||||
});
|
||||
|
||||
it('should correctly compare a before date inside tolerance', (): void => {
|
||||
const aDate = new Date('2022-09-09T13:00:00');
|
||||
const otherDate = new Date('2022-09-09T13:00:42');
|
||||
expect(isDateMoreRecentThan(aDate, otherDate, 60)).toBe(false); // considered equal here
|
||||
});
|
||||
|
||||
it('should correctly compare an after date outside tolerance', (): void => {
|
||||
const aDate = new Date('2022-09-09T13:00:00');
|
||||
const otherDate = new Date('2022-09-09T12:00:00');
|
||||
expect(isDateMoreRecentThan(aDate, otherDate, 60)).toBe(true);
|
||||
});
|
||||
|
||||
it('should correctly compare an after date inside tolerance', (): void => {
|
||||
const aDate = new Date('2022-09-09T13:00:00');
|
||||
const otherDate = new Date('2022-09-09T12:59:42');
|
||||
expect(isDateMoreRecentThan(aDate, otherDate, 60)).toBe(false); // considered equal here
|
||||
});
|
||||
|
||||
it('should correctly compare an exactly equal date', (): void => {
|
||||
const aDate = new Date('2022-09-09T13:00:00');
|
||||
const otherDate = new Date('2022-09-09T13:00:00');
|
||||
expect(isDateMoreRecentThan(aDate, otherDate, 60)).toBe(false);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,6 +1,31 @@
|
||||
/// returns false if the dates are equal within the `equalityToleranceInSeconds` number of seconds
|
||||
/// otherwise returns true if `comparedDate` is after `date`
|
||||
|
||||
export function isDateMoreRecentThan(
|
||||
date: Readonly<Date>,
|
||||
comparedDate: Readonly<Date>
|
||||
comparedDate: Readonly<Date>,
|
||||
equalityToleranceInSeconds = 0
|
||||
): boolean {
|
||||
if (equalityToleranceInSeconds > 0) {
|
||||
const areDatesEqual = isDateEqualTo(
|
||||
date,
|
||||
comparedDate,
|
||||
equalityToleranceInSeconds
|
||||
);
|
||||
|
||||
return !areDatesEqual && date > comparedDate;
|
||||
}
|
||||
|
||||
return date > comparedDate;
|
||||
}
|
||||
|
||||
export function isDateEqualTo(
|
||||
date: Date,
|
||||
otherDate: Date,
|
||||
toleranceInSeconds: number
|
||||
): boolean {
|
||||
const timestamp = date.getTime();
|
||||
const otherTimestamp = otherDate.getTime();
|
||||
const deltaInSeconds = Math.abs(timestamp - otherTimestamp) / 1000;
|
||||
return deltaInSeconds <= toleranceInSeconds;
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import {IUser} from './user';
|
||||
|
||||
export interface IComment {
|
||||
user: IUser;
|
||||
body: string;
|
||||
user: IUser | null;
|
||||
body?: string;
|
||||
}
|
||||
|
||||
@@ -2,16 +2,18 @@ import {IsoDateString} from '../types/iso-date-string';
|
||||
import {Assignee} from './assignee';
|
||||
import {ILabel} from './label';
|
||||
import {IMilestone} from './milestone';
|
||||
|
||||
import {components} from '@octokit/openapi-types';
|
||||
export interface IIssue {
|
||||
title: string;
|
||||
number: number;
|
||||
created_at: IsoDateString;
|
||||
updated_at: IsoDateString;
|
||||
labels: ILabel[];
|
||||
pull_request: Object | null | undefined;
|
||||
pull_request?: Object | null;
|
||||
state: string;
|
||||
locked: boolean;
|
||||
milestone: IMilestone | undefined;
|
||||
assignees: Assignee[];
|
||||
milestone?: IMilestone | null;
|
||||
assignees?: Assignee[] | null;
|
||||
}
|
||||
|
||||
export type OctokitIssue = components['schemas']['issue'];
|
||||
|
||||
@@ -51,4 +51,6 @@ export interface IIssuesProcessorOptions {
|
||||
ignoreIssueUpdates: boolean | undefined;
|
||||
ignorePrUpdates: boolean | undefined;
|
||||
exemptDraftPr: boolean;
|
||||
closeIssueReason: string;
|
||||
includeOnlyAssigned: boolean;
|
||||
}
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
export interface ILabel {
|
||||
name: string;
|
||||
name?: string;
|
||||
}
|
||||
|
||||
@@ -3,5 +3,5 @@ export interface IPullRequest {
|
||||
head: {
|
||||
ref: string;
|
||||
};
|
||||
draft: boolean;
|
||||
draft?: boolean;
|
||||
}
|
||||
|
||||
13
src/main.ts
13
src/main.ts
@@ -87,7 +87,9 @@ function _getAndValidateArgs(): IIssuesProcessorOptions {
|
||||
ignoreUpdates: core.getInput('ignore-updates') === 'true',
|
||||
ignoreIssueUpdates: _toOptionalBoolean('ignore-issue-updates'),
|
||||
ignorePrUpdates: _toOptionalBoolean('ignore-pr-updates'),
|
||||
exemptDraftPr: core.getInput('exempt-draft-pr') === 'true'
|
||||
exemptDraftPr: core.getInput('exempt-draft-pr') === 'true',
|
||||
closeIssueReason: core.getInput('close-issue-reason'),
|
||||
includeOnlyAssigned: core.getInput('include-only-assigned') === 'true'
|
||||
};
|
||||
|
||||
for (const numberInput of [
|
||||
@@ -113,6 +115,15 @@ function _getAndValidateArgs(): IIssuesProcessorOptions {
|
||||
}
|
||||
}
|
||||
|
||||
const validCloseReasons = ['', 'completed', 'not_planned'];
|
||||
if (!validCloseReasons.includes(args.closeIssueReason)) {
|
||||
const errorMessage = `Unrecognized close-issue-reason "${
|
||||
args.closeIssueReason
|
||||
}", valid values are: ${validCloseReasons.filter(Boolean).join(', ')}`;
|
||||
core.setFailed(errorMessage);
|
||||
throw new Error(errorMessage);
|
||||
}
|
||||
|
||||
return args;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user