Compare commits

..

1 Commits

Author SHA1 Message Date
Luke Tomlinson
9c1b1c6e11 Changelog 5.1.1 2022-07-27 16:17:02 -04:00
15 changed files with 525 additions and 13538 deletions

View File

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

View File

@@ -1,10 +1,8 @@
# Changelog
# [6.0.0]
# [5.1.1]
:warning: Breaking change :warning:
Issues/PRs default `close-issue-reason` is now `not_planned`([#789](https://github.com/actions/stale/issues/789))
[Fix issue when days-before-close is more than days-before-stale](https://github.com/actions/stale/pull/775)
# [5.1.0]

View File

@@ -11,12 +11,11 @@ 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, delete branch, etc.).
In addition, based on the provided configuration, the action could require more permission(s) (e.g.: add label, remove label, comment, close, 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
```
@@ -44,7 +43,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` |
| [close-issue-reason](#close-issue-reason) | Reason to use when closing issues | |
| [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 | |
@@ -82,7 +81,6 @@ 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
@@ -226,7 +224,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: `not_planned`
Default value: unset
#### stale-pr-label
@@ -398,7 +396,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` and `contents: write`
Required Permission: `pull-requests: write`
#### exempt-milestones
@@ -518,12 +516,6 @@ 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.
@@ -540,7 +532,7 @@ jobs:
stale:
runs-on: ubuntu-latest
steps:
- uses: actions/stale@v6
- uses: actions/stale@v5
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'
@@ -558,7 +550,7 @@ jobs:
stale:
runs-on: ubuntu-latest
steps:
- uses: actions/stale@v6
- uses: actions/stale@v5
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
@@ -577,7 +569,7 @@ jobs:
stale:
runs-on: ubuntu-latest
steps:
- uses: actions/stale@v6
- uses: actions/stale@v5
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.'
@@ -599,7 +591,7 @@ jobs:
stale:
runs-on: ubuntu-latest
steps:
- uses: actions/stale@v6
- uses: actions/stale@v5
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.'
@@ -623,7 +615,7 @@ jobs:
stale:
runs-on: ubuntu-latest
steps:
- uses: actions/stale@v6
- uses: actions/stale@v5
with:
stale-issue-message: 'Stale issue message'
stale-pr-message: 'Stale pull request message'
@@ -646,7 +638,7 @@ jobs:
stale:
runs-on: ubuntu-latest
steps:
- uses: actions/stale@v6
- uses: actions/stale@v5
with:
start-date: '2020-04-18T00:00:00Z' # ISO 8601 or RFC 2822
```
@@ -663,7 +655,7 @@ jobs:
stale:
runs-on: ubuntu-latest
steps:
- uses: actions/stale@v6
- uses: actions/stale@v5
with:
exempt-issue-milestones: 'future,alpha,beta'
exempt-pr-milestones: 'bugfix,improvement'
@@ -681,7 +673,7 @@ jobs:
stale:
runs-on: ubuntu-latest
steps:
- uses: actions/stale@v6
- uses: actions/stale@v5
with:
exempt-all-pr-milestones: true
```
@@ -698,7 +690,7 @@ jobs:
stale:
runs-on: ubuntu-latest
steps:
- uses: actions/stale@v6
- uses: actions/stale@v5
with:
any-of-labels: 'needs-more-info,needs-demo'
# You can opt for 'only-labels' instead if your use-case requires all labels
@@ -717,7 +709,7 @@ jobs:
stale:
runs-on: ubuntu-latest
steps:
- uses: actions/stale@v6
- uses: actions/stale@v5
with:
exempt-issue-assignees: 'marco,polo'
exempt-pr-assignees: 'marco'
@@ -735,7 +727,7 @@ jobs:
stale:
runs-on: ubuntu-latest
steps:
- uses: actions/stale@v6
- uses: actions/stale@v5
with:
exempt-all-pr-assignees: true
```

View File

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

View File

@@ -2352,69 +2352,3 @@ 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: 'not_planned'
default: ''
required: false
stale-pr-label:
description: 'The label to apply when a pull request is stale.'
@@ -196,10 +196,6 @@ 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.10.0",
"@actions/core": "^1.2.6",
"@actions/github": "^5.0.1",
"lodash.deburr": "^4.1.0",
"semver": "^7.3.5"

View File

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

View File

@@ -221,14 +221,6 @@ 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) {
@@ -673,13 +665,8 @@ export class IssuesProcessor {
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
);
const issueHasUpdateSinceStale =
new Date(issue.updated_at) > new Date(markedStaleOn);
issueLogger.info(
`$$type has been updated since it was marked stale: ${LoggerService.cyan(
@@ -1025,10 +1012,6 @@ 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 {isDateEqualTo, isDateMoreRecentThan} from './is-date-more-recent-than';
import {isDateMoreRecentThan} from './is-date-more-recent-than';
describe('isDateMoreRecentThan()', (): void => {
let date: Date;
@@ -48,68 +48,4 @@ 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,31 +1,6 @@
/// 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>,
equalityToleranceInSeconds = 0
comparedDate: Readonly<Date>
): 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,5 +52,4 @@ export interface IIssuesProcessorOptions {
ignorePrUpdates: boolean | undefined;
exemptDraftPr: boolean;
closeIssueReason: string;
includeOnlyAssigned: boolean;
}

View File

@@ -88,8 +88,7 @@ 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'),
includeOnlyAssigned: core.getInput('include-only-assigned') === 'true'
closeIssueReason: core.getInput('close-issue-reason')
};
for (const numberInput of [