Compare commits

..

11 Commits
remove ... v6

Author SHA1 Message Date
Francesco Renzi
5ebf00ea0e Update @actions/core to 1.10.0 2022-10-06 12:19:32 +01:00
Luke Tomlinson
3de2653986 Update changelog for v6.0.0 (#829)
* Update changelog for v6.0.0

* whitespace
2022-09-21 14:54:43 -04:00
David Allison
02e44c81cc fix(options)!: Make not_planned the default close-issue-reason (#807)
* docs: add source for default testing options

* fix(options)!: Default to 'Close as not planned'

GitHub introduced an additional close reason 'Close as not planned':
https://github.blog/changelog/2022-05-19-the-new-github-issues-may-19th-update/

'stale' is a use case for this close reason

Fixes #789

BREAKING CHANGE:
`close-issue-reason` defaults to `not_planned` (previously: ``
which defaulted to `completed`)
This closes the issue as 'not planned' on GitHub rather than 'completed'

See: https://github.blog/changelog/2022-05-19-the-new-github-issues-may-19th-update
2022-09-20 14:01:22 -04:00
JoannaaKL
99b6c70959 Add include-only-assigned to the action.yml (#824) 2022-09-12 17:39:44 +02:00
Luke Tomlinson
8e8a0e6680 [Bugfix-795] Fix date comparison edge case (#816)
* Fix updatedAt and markedStaleOn date comparison

* Delete accidental file

* Refactor

* Cleanup

* cleanup
2022-09-12 09:38:46 -04:00
JoannaaKL
80962c1062 Update README.md (#821)
* Update README.md

* Update README.md

Co-authored-by: Tatyana Kostromskaya <32135588+takost@users.noreply.github.com>

Co-authored-by: Tatyana Kostromskaya <32135588+takost@users.noreply.github.com>
2022-09-12 14:41:28 +02:00
JoannaaKL
3e4418e47e Issue 596/include only assigned (#817)
* Add new 'include-only-assigned' option

If set, only issues containing assignees will be processed

* Test new flag

* Update code comment

* Update src/classes/issues-processor.ts

Co-authored-by: Francesco Renzi <rentziass@github.com>

* Update index.js with typo fix

Co-authored-by: Francesco Renzi <rentziass@github.com>
2022-09-12 13:20:57 +02:00
Luke Tomlinson
33e37032bb Update actions core to 1.9.1 (#812)
* Update @actions/core to 1.9.1

* Update dist/

* Fix conflicts
2022-09-07 11:23:27 -04:00
Luke Tomlinson
97911cb595 Update package.lock version (#811) 2022-09-07 11:17:21 -04:00
Dan Pavlov
65afee970e docs(readme): add missing permission for delete-branch option in readme (#796) 2022-09-07 11:16:55 -04:00
dependabot[bot]
48bae5932f build(deps): bump actions/setup-node from 1 to 3 (#761)
Bumps [actions/setup-node](https://github.com/actions/setup-node) from 1 to 3.
- [Release notes](https://github.com/actions/setup-node/releases)
- [Commits](https://github.com/actions/setup-node/compare/v1...v3)

---
updated-dependencies:
- dependency-name: actions/setup-node
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-07-29 16:28:44 -04:00
15 changed files with 13538 additions and 525 deletions

View File

@@ -24,7 +24,7 @@ jobs:
- uses: actions/checkout@v3
- name: Set Node.js 16.x
uses: actions/setup-node@v1
uses: actions/setup-node@v3
with:
node-version: 16.x

View File

@@ -1,8 +1,10 @@
# Changelog
# [5.1.1]
# [6.0.0]
[Fix issue when days-before-close is more than days-before-stale](https://github.com/actions/stale/pull/775)
: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]

View File

@@ -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,7 +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 | |
| [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 | |
@@ -81,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
@@ -224,7 +226,7 @@ Required Permission: `issues: write`
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: unset
Default value: `not_planned`
#### stale-pr-label
@@ -396,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
@@ -516,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.
@@ -532,7 +540,7 @@ jobs:
stale:
runs-on: ubuntu-latest
steps:
- uses: actions/stale@v5
- 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'
@@ -550,7 +558,7 @@ jobs:
stale:
runs-on: ubuntu-latest
steps:
- uses: actions/stale@v5
- 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
@@ -569,7 +577,7 @@ jobs:
stale:
runs-on: ubuntu-latest
steps:
- uses: actions/stale@v5
- 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.'
@@ -591,7 +599,7 @@ jobs:
stale:
runs-on: ubuntu-latest
steps:
- uses: actions/stale@v5
- 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.'
@@ -615,7 +623,7 @@ jobs:
stale:
runs-on: ubuntu-latest
steps:
- uses: actions/stale@v5
- uses: actions/stale@v6
with:
stale-issue-message: 'Stale issue message'
stale-pr-message: 'Stale pull request message'
@@ -638,7 +646,7 @@ jobs:
stale:
runs-on: ubuntu-latest
steps:
- uses: actions/stale@v5
- uses: actions/stale@v6
with:
start-date: '2020-04-18T00:00:00Z' # ISO 8601 or RFC 2822
```
@@ -655,7 +663,7 @@ jobs:
stale:
runs-on: ubuntu-latest
steps:
- uses: actions/stale@v5
- uses: actions/stale@v6
with:
exempt-issue-milestones: 'future,alpha,beta'
exempt-pr-milestones: 'bugfix,improvement'
@@ -673,7 +681,7 @@ jobs:
stale:
runs-on: ubuntu-latest
steps:
- uses: actions/stale@v5
- uses: actions/stale@v6
with:
exempt-all-pr-milestones: true
```
@@ -690,7 +698,7 @@ jobs:
stale:
runs-on: ubuntu-latest
steps:
- uses: actions/stale@v5
- 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
@@ -709,7 +717,7 @@ jobs:
stale:
runs-on: ubuntu-latest
steps:
- uses: actions/stale@v5
- uses: actions/stale@v6
with:
exempt-issue-assignees: 'marco,polo'
exempt-pr-assignees: 'marco'
@@ -727,7 +735,7 @@ jobs:
stale:
runs-on: ubuntu-latest
steps:
- uses: actions/stale@v5
- uses: actions/stale@v6
with:
exempt-all-pr-assignees: true
```

View File

@@ -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',
@@ -51,5 +53,6 @@ export const DefaultProcessorOptions: IIssuesProcessorOptions = Object.freeze({
ignoreIssueUpdates: undefined,
ignorePrUpdates: undefined,
exemptDraftPr: false,
closeIssueReason: ''
closeIssueReason: 'not_planned',
includeOnlyAssigned: false
});

View File

@@ -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);
});

View File

@@ -51,7 +51,7 @@ inputs:
required: false
close-issue-reason:
description: 'The reason to use when closing an issue.'
default: ''
default: 'not_planned'
required: false
stale-pr-label:
description: 'The label to apply when a pull request is stale.'
@@ -196,6 +196,10 @@ 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.'

2110
dist/index.js vendored

File diff suppressed because it is too large Load Diff

11707
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@@ -38,7 +38,7 @@
"author": "GitHub",
"license": "MIT",
"dependencies": {
"@actions/core": "^1.2.6",
"@actions/core": "^1.10.0",
"@actions/github": "^5.0.1",
"lodash.deburr": "^4.1.0",
"semver": "^7.3.5"

View File

@@ -62,7 +62,8 @@ describe('Issue', (): void => {
ignoreIssueUpdates: undefined,
ignorePrUpdates: undefined,
exemptDraftPr: false,
closeIssueReason: ''
closeIssueReason: '',
includeOnlyAssigned: false
};
issueInterface = {
title: 'dummy-title',

View File

@@ -221,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) {
@@ -665,8 +673,13 @@ export class IssuesProcessor {
issueLogger.info(`marked stale this run, so don't check for updates`);
}
const issueHasUpdateSinceStale =
new Date(issue.updated_at) > new Date(markedStaleOn);
// 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(
`$$type has been updated since it was marked stale: ${LoggerService.cyan(
@@ -1012,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 !== '') {

View File

@@ -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);
});
});
});

View File

@@ -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;
}

View File

@@ -52,4 +52,5 @@ export interface IIssuesProcessorOptions {
ignorePrUpdates: boolean | undefined;
exemptDraftPr: boolean;
closeIssueReason: string;
includeOnlyAssigned: boolean;
}

View File

@@ -88,7 +88,8 @@ function _getAndValidateArgs(): IIssuesProcessorOptions {
ignoreIssueUpdates: _toOptionalBoolean('ignore-issue-updates'),
ignorePrUpdates: _toOptionalBoolean('ignore-pr-updates'),
exemptDraftPr: core.getInput('exempt-draft-pr') === 'true',
closeIssueReason: core.getInput('close-issue-reason')
closeIssueReason: core.getInput('close-issue-reason'),
includeOnlyAssigned: core.getInput('include-only-assigned') === 'true'
};
for (const numberInput of [