Compare commits

...

7 Commits

Author SHA1 Message Date
John Sudol
601cc005e7 update README and logs to provide clarity 2022-11-25 21:47:46 +00:00
Iván Reinoso García
65b52aff67 Allow daysBeforeStale options to be float (#841)
* feat: allow daysBeforeStale options to be float

* update dist
2022-10-19 12:08:31 +02:00
Francesco Renzi
0d6f830071 Update changelog for 6.0.1 2022-10-07 11:12:02 +01:00
Francesco Renzi
b909bf8927 Merge pull request #839 from actions/rentziass/update-actions-core
Update @actions/core to 1.10.0
2022-10-07 11:05:38 +01:00
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
10 changed files with 285 additions and 70 deletions

View File

@@ -1,18 +1,26 @@
# Changelog # Changelog
# [6.0.1]
Update @actions/core to v1.10.0 ([#839](https://github.com/actions/stale/pull/839))
# [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] # [5.1.0]
[Don't process stale issues right after they're marked stale](https://github.com/actions/stale/issues/696) [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) [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 Various dependabot/dependency updates
## [4.1.0](https://github.com/actions/stale/compare/v3.0.19...v4.1.0) (2021-07-14) ## [4.1.0](https://github.com/actions/stale/compare/v3.0.19...v4.1.0) (2021-07-14)
## Features ## Features
- [Ability to exempt draft PRs](https://github.com/actions/stale/commit/9912fa74d1c01b5d6187793d97441019cbe325d0 - [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) ## [4.0.0](https://github.com/actions/stale/compare/v3.0.19...v4.0.0) (2021-07-14)

View File

@@ -44,7 +44,7 @@ Every argument is optional.
| [close-pr-message](#close-pr-message) | Comment on the staled PRs while closed | | | [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` | | [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-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` | | [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 | | | [close-pr-label](#close-pr-label) | Label to apply on closed PRs | |
| [exempt-issue-labels](#exempt-issue-labels) | Labels on issues exempted from stale | | | [exempt-issue-labels](#exempt-issue-labels) | Labels on issues exempted from stale | |
@@ -82,7 +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-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-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 | | | [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` | | [include-only-assigned](#include-only-assigned) | Process only assigned issues | `false` |
### List of output options ### List of output options
@@ -226,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`. 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 #### stale-pr-label
@@ -246,8 +246,8 @@ Required Permission: `pull-requests: write`
#### exempt-issue-labels #### exempt-issue-labels
The label(s) that can exempt to automatically mark as stale the issues. Comma separated list of labels that exclude the issue from being marked as stale
It can be a comma separated list of labels (e.g: `question,bug`). (e.g: `question,bug`)
If unset (or an empty string), this option will not alter the stale workflow. If unset (or an empty string), this option will not alter the stale workflow.
@@ -255,8 +255,8 @@ Default value: unset
#### exempt-pr-labels #### exempt-pr-labels
The label(s) that can exempt to automatically mark as stale the pull requests. Comma separated list of labels that exclude the pull request from being marked as stale
It can be a comma separated list of labels (e.g: `need-help,WIP`). (e.g: `need-help,WIP`)
If unset (or an empty string), this option will not alter the stale workflow. If unset (or an empty string), this option will not alter the stale workflow.
@@ -540,7 +540,7 @@ jobs:
stale: stale:
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- uses: actions/stale@v5 - uses: actions/stale@v6
with: with:
stale-issue-message: 'Message to comment on stale issues. If none provided, will not mark issues stale' 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' stale-pr-message: 'Message to comment on stale PRs. If none provided, will not mark PRs stale'
@@ -558,7 +558,7 @@ jobs:
stale: stale:
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- uses: actions/stale@v5 - uses: actions/stale@v6
with: 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-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 days-before-stale: 30
@@ -577,7 +577,7 @@ jobs:
stale: stale:
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- uses: actions/stale@v5 - uses: actions/stale@v6
with: 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-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.' 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 +599,7 @@ jobs:
stale: stale:
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- uses: actions/stale@v5 - uses: actions/stale@v6
with: 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-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.' 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 +623,7 @@ jobs:
stale: stale:
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- uses: actions/stale@v5 - uses: actions/stale@v6
with: with:
stale-issue-message: 'Stale issue message' stale-issue-message: 'Stale issue message'
stale-pr-message: 'Stale pull request message' stale-pr-message: 'Stale pull request message'
@@ -646,7 +646,7 @@ jobs:
stale: stale:
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- uses: actions/stale@v5 - uses: actions/stale@v6
with: with:
start-date: '2020-04-18T00:00:00Z' # ISO 8601 or RFC 2822 start-date: '2020-04-18T00:00:00Z' # ISO 8601 or RFC 2822
``` ```
@@ -663,7 +663,7 @@ jobs:
stale: stale:
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- uses: actions/stale@v5 - uses: actions/stale@v6
with: with:
exempt-issue-milestones: 'future,alpha,beta' exempt-issue-milestones: 'future,alpha,beta'
exempt-pr-milestones: 'bugfix,improvement' exempt-pr-milestones: 'bugfix,improvement'
@@ -681,7 +681,7 @@ jobs:
stale: stale:
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- uses: actions/stale@v5 - uses: actions/stale@v6
with: with:
exempt-all-pr-milestones: true exempt-all-pr-milestones: true
``` ```
@@ -698,7 +698,7 @@ jobs:
stale: stale:
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- uses: actions/stale@v5 - uses: actions/stale@v6
with: with:
any-of-labels: 'needs-more-info,needs-demo' any-of-labels: 'needs-more-info,needs-demo'
# You can opt for 'only-labels' instead if your use-case requires all labels # You can opt for 'only-labels' instead if your use-case requires all labels
@@ -717,7 +717,7 @@ jobs:
stale: stale:
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- uses: actions/stale@v5 - uses: actions/stale@v6
with: with:
exempt-issue-assignees: 'marco,polo' exempt-issue-assignees: 'marco,polo'
exempt-pr-assignees: 'marco' exempt-pr-assignees: 'marco'
@@ -735,7 +735,7 @@ jobs:
stale: stale:
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- uses: actions/stale@v5 - uses: actions/stale@v6
with: with:
exempt-all-pr-assignees: true exempt-all-pr-assignees: true
``` ```

View File

@@ -1,5 +1,7 @@
import {IIssuesProcessorOptions} from '../../src/interfaces/issues-processor-options'; 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({ export const DefaultProcessorOptions: IIssuesProcessorOptions = Object.freeze({
repoToken: 'none', repoToken: 'none',
staleIssueMessage: 'This issue is stale', staleIssueMessage: 'This issue is stale',
@@ -51,6 +53,6 @@ export const DefaultProcessorOptions: IIssuesProcessorOptions = Object.freeze({
ignoreIssueUpdates: undefined, ignoreIssueUpdates: undefined,
ignorePrUpdates: undefined, ignorePrUpdates: undefined,
exemptDraftPr: false, exemptDraftPr: false,
closeIssueReason: '', closeIssueReason: 'not_planned',
includeOnlyAssigned: false includeOnlyAssigned: false
}); });

View File

@@ -1998,6 +1998,84 @@ test('processing an issue opened since 2 days and with the option "daysBeforeIss
expect(processor.closedIssues).toHaveLength(0); expect(processor.closedIssues).toHaveLength(0);
}); });
test('processing an issue opened since 1 hour and with the option "daysBeforeIssueStale" at 0.1666666667 (4 hours) will not make it stale', async () => {
expect.assertions(2);
const opts: IIssuesProcessorOptions = {
...DefaultProcessorOptions,
daysBeforeStale: 10,
daysBeforeIssueStale: 0.1666666667
};
const issueDate = new Date();
issueDate.setHours(issueDate.getHours() - 1);
const TestIssueList: Issue[] = [
generateIssue(opts, 1, 'An issue with no label', issueDate.toISOString())
];
const processor = new IssuesProcessorMock(
opts,
async p => (p === 1 ? TestIssueList : []),
async () => [],
async () => new Date().toISOString()
);
// process our fake issue list
await processor.processIssues(1);
expect(processor.staleIssues).toHaveLength(0);
expect(processor.closedIssues).toHaveLength(0);
});
test('processing an issue opened since 4 hours and with the option "daysBeforeIssueStale" at 0.1666666667 (4 hours) will make it stale', async () => {
expect.assertions(2);
const opts: IIssuesProcessorOptions = {
...DefaultProcessorOptions,
daysBeforeStale: 10,
daysBeforeIssueStale: 0.1666666667
};
const issueDate = new Date();
issueDate.setHours(issueDate.getHours() - 4);
const TestIssueList: Issue[] = [
generateIssue(opts, 1, 'An issue with no label', issueDate.toISOString())
];
const processor = new IssuesProcessorMock(
opts,
async p => (p === 1 ? TestIssueList : []),
async () => [],
async () => new Date().toISOString()
);
// process our fake issue list
await processor.processIssues(1);
expect(processor.staleIssues).toHaveLength(1);
expect(processor.closedIssues).toHaveLength(0);
});
test('processing an issue opened since 5 hours and with the option "daysBeforeIssueStale" at 0.1666666667 (4 hours) will make it stale', async () => {
expect.assertions(2);
const opts: IIssuesProcessorOptions = {
...DefaultProcessorOptions,
daysBeforeStale: 10,
daysBeforeIssueStale: 0.1666666667
};
const issueDate = new Date();
issueDate.setHours(issueDate.getHours() - 5);
const TestIssueList: Issue[] = [
generateIssue(opts, 1, 'An issue with no label', issueDate.toISOString())
];
const processor = new IssuesProcessorMock(
opts,
async p => (p === 1 ? TestIssueList : []),
async () => [],
async () => new Date().toISOString()
);
// process our fake issue list
await processor.processIssues(1);
expect(processor.staleIssues).toHaveLength(1);
expect(processor.closedIssues).toHaveLength(0);
});
test('processing a pull request opened since 2 days and with the option "daysBeforePrStale" at 3 will not make it stale', async () => { test('processing a pull request opened since 2 days and with the option "daysBeforePrStale" at 3 will not make it stale', async () => {
expect.assertions(2); expect.assertions(2);
const opts: IIssuesProcessorOptions = { const opts: IIssuesProcessorOptions = {
@@ -2097,6 +2175,105 @@ test('processing a pull request opened since 2 days and with the option "daysBef
expect(processor.closedIssues).toHaveLength(0); expect(processor.closedIssues).toHaveLength(0);
}); });
test('processing a pull request opened since 1 hour and with the option "daysBeforePrStale" at 0.1666666667 (4 hours) will not make it stale', async () => {
expect.assertions(2);
const opts: IIssuesProcessorOptions = {
...DefaultProcessorOptions,
daysBeforeStale: 10,
daysBeforePrStale: 0.1666666667
};
const issueDate = new Date();
issueDate.setHours(issueDate.getHours() - 1);
const TestIssueList: Issue[] = [
generateIssue(
opts,
1,
'A pull request with no label',
issueDate.toISOString(),
issueDate.toISOString(),
true
)
];
const processor = new IssuesProcessorMock(
opts,
async p => (p === 1 ? TestIssueList : []),
async () => [],
async () => new Date().toISOString()
);
// process our fake issue list
await processor.processIssues(1);
expect(processor.staleIssues).toHaveLength(0);
expect(processor.closedIssues).toHaveLength(0);
});
test('processing a pull request opened since 4 hours and with the option "daysBeforePrStale" at 0.1666666667 (4 hours) will make it stale', async () => {
expect.assertions(2);
const opts: IIssuesProcessorOptions = {
...DefaultProcessorOptions,
daysBeforeStale: 10,
daysBeforePrStale: 0.1666666667
};
const issueDate = new Date();
issueDate.setHours(issueDate.getHours() - 4);
const TestIssueList: Issue[] = [
generateIssue(
opts,
1,
'A pull request with no label',
issueDate.toISOString(),
issueDate.toISOString(),
true
)
];
const processor = new IssuesProcessorMock(
opts,
async p => (p === 1 ? TestIssueList : []),
async () => [],
async () => new Date().toISOString()
);
// process our fake issue list
await processor.processIssues(1);
expect(processor.staleIssues).toHaveLength(1);
expect(processor.closedIssues).toHaveLength(0);
});
test('processing a pull request opened since 5 hours and with the option "daysBeforePrStale" at 0.1666666667 (4 hours) will make it stale', async () => {
expect.assertions(2);
const opts: IIssuesProcessorOptions = {
...DefaultProcessorOptions,
daysBeforeStale: 10,
daysBeforePrStale: 0.1666666667
};
const issueDate = new Date();
issueDate.setHours(issueDate.getHours() - 5);
const TestIssueList: Issue[] = [
generateIssue(
opts,
1,
'A pull request with no label',
issueDate.toISOString(),
issueDate.toISOString(),
true
)
];
const processor = new IssuesProcessorMock(
opts,
async p => (p === 1 ? TestIssueList : []),
async () => [],
async () => new Date().toISOString()
);
// process our fake issue list
await processor.processIssues(1);
expect(processor.staleIssues).toHaveLength(1);
expect(processor.closedIssues).toHaveLength(0);
});
test('processing a previously closed issue with a close label will remove the close label', async () => { test('processing a previously closed issue with a close label will remove the close label', async () => {
expect.assertions(1); expect.assertions(1);
const opts: IIssuesProcessorOptions = { const opts: IIssuesProcessorOptions = {

View File

@@ -51,7 +51,7 @@ inputs:
required: false required: false
close-issue-reason: close-issue-reason:
description: 'The reason to use when closing an issue.' description: 'The reason to use when closing an issue.'
default: '' default: 'not_planned'
required: false required: false
stale-pr-label: stale-pr-label:
description: 'The label to apply when a pull request is stale.' description: 'The label to apply when a pull request is stale.'

80
dist/index.js vendored
View File

@@ -532,8 +532,10 @@ class IssuesProcessor {
? this.options.exemptPrLabels ? this.options.exemptPrLabels
: this.options.exemptIssueLabels); : this.options.exemptIssueLabels);
if (exemptLabels.some((exemptLabel) => is_labeled_1.isLabeled(issue, exemptLabel))) { if (exemptLabels.some((exemptLabel) => is_labeled_1.isLabeled(issue, exemptLabel))) {
// check to see if the issue is stale and has an exempt label
// see issue #136 for more details
if (issue.isStale) { if (issue.isStale) {
issueLogger.info(`An exempt label was added after the stale label.`); issueLogger.info(`This $$type has both an exempt and stale label, proceeding to remove the stale label.`);
yield this._removeStaleLabel(issue, staleLabel); yield this._removeStaleLabel(issue, staleLabel);
} }
issueLogger.info(`Skipping this $$type because it has an exempt label`); issueLogger.info(`Skipping this $$type because it has an exempt label`);
@@ -2183,9 +2185,9 @@ function _getAndValidateArgs() {
stalePrMessage: core.getInput('stale-pr-message'), stalePrMessage: core.getInput('stale-pr-message'),
closeIssueMessage: core.getInput('close-issue-message'), closeIssueMessage: core.getInput('close-issue-message'),
closePrMessage: core.getInput('close-pr-message'), closePrMessage: core.getInput('close-pr-message'),
daysBeforeStale: parseInt(core.getInput('days-before-stale', { required: true })), daysBeforeStale: parseFloat(core.getInput('days-before-stale', { required: true })),
daysBeforeIssueStale: parseInt(core.getInput('days-before-issue-stale')), daysBeforeIssueStale: parseFloat(core.getInput('days-before-issue-stale')),
daysBeforePrStale: parseInt(core.getInput('days-before-pr-stale')), daysBeforePrStale: parseFloat(core.getInput('days-before-pr-stale')),
daysBeforeClose: parseInt(core.getInput('days-before-close', { required: true })), daysBeforeClose: parseInt(core.getInput('days-before-close', { required: true })),
daysBeforeIssueClose: parseInt(core.getInput('days-before-issue-close')), daysBeforeIssueClose: parseInt(core.getInput('days-before-issue-close')),
daysBeforePrClose: parseInt(core.getInput('days-before-pr-close')), daysBeforePrClose: parseInt(core.getInput('days-before-pr-close')),
@@ -2233,11 +2235,14 @@ function _getAndValidateArgs() {
closeIssueReason: core.getInput('close-issue-reason'), closeIssueReason: core.getInput('close-issue-reason'),
includeOnlyAssigned: core.getInput('include-only-assigned') === 'true' includeOnlyAssigned: core.getInput('include-only-assigned') === 'true'
}; };
for (const numberInput of [ for (const numberInput of ['days-before-stale']) {
'days-before-stale', if (isNaN(parseFloat(core.getInput(numberInput)))) {
'days-before-close', const errorMessage = `Option "${numberInput}" did not parse to a valid float`;
'operations-per-run' core.setFailed(errorMessage);
]) { throw new Error(errorMessage);
}
}
for (const numberInput of ['days-before-close', 'operations-per-run']) {
if (isNaN(parseInt(core.getInput(numberInput)))) { if (isNaN(parseInt(core.getInput(numberInput)))) {
const errorMessage = `Option "${numberInput}" did not parse to a valid integer`; const errorMessage = `Option "${numberInput}" did not parse to a valid integer`;
core.setFailed(errorMessage); core.setFailed(errorMessage);
@@ -2484,7 +2489,6 @@ const file_command_1 = __nccwpck_require__(717);
const utils_1 = __nccwpck_require__(5278); const utils_1 = __nccwpck_require__(5278);
const os = __importStar(__nccwpck_require__(2087)); const os = __importStar(__nccwpck_require__(2087));
const path = __importStar(__nccwpck_require__(5622)); const path = __importStar(__nccwpck_require__(5622));
const uuid_1 = __nccwpck_require__(5840);
const oidc_utils_1 = __nccwpck_require__(8041); const oidc_utils_1 = __nccwpck_require__(8041);
/** /**
* The code to exit an action * The code to exit an action
@@ -2514,20 +2518,9 @@ function exportVariable(name, val) {
process.env[name] = convertedVal; process.env[name] = convertedVal;
const filePath = process.env['GITHUB_ENV'] || ''; const filePath = process.env['GITHUB_ENV'] || '';
if (filePath) { if (filePath) {
const delimiter = `ghadelimiter_${uuid_1.v4()}`; return file_command_1.issueFileCommand('ENV', file_command_1.prepareKeyValueMessage(name, val));
// These should realistically never happen, but just in case someone finds a way to exploit uuid generation let's not allow keys or values that contain the delimiter.
if (name.includes(delimiter)) {
throw new Error(`Unexpected input: name should not contain the delimiter "${delimiter}"`);
}
if (convertedVal.includes(delimiter)) {
throw new Error(`Unexpected input: value should not contain the delimiter "${delimiter}"`);
}
const commandValue = `${name}<<${delimiter}${os.EOL}${convertedVal}${os.EOL}${delimiter}`;
file_command_1.issueCommand('ENV', commandValue);
}
else {
command_1.issueCommand('set-env', { name }, convertedVal);
} }
command_1.issueCommand('set-env', { name }, convertedVal);
} }
exports.exportVariable = exportVariable; exports.exportVariable = exportVariable;
/** /**
@@ -2545,7 +2538,7 @@ exports.setSecret = setSecret;
function addPath(inputPath) { function addPath(inputPath) {
const filePath = process.env['GITHUB_PATH'] || ''; const filePath = process.env['GITHUB_PATH'] || '';
if (filePath) { if (filePath) {
file_command_1.issueCommand('PATH', inputPath); file_command_1.issueFileCommand('PATH', inputPath);
} }
else { else {
command_1.issueCommand('add-path', {}, inputPath); command_1.issueCommand('add-path', {}, inputPath);
@@ -2585,7 +2578,10 @@ function getMultilineInput(name, options) {
const inputs = getInput(name, options) const inputs = getInput(name, options)
.split('\n') .split('\n')
.filter(x => x !== ''); .filter(x => x !== '');
return inputs; if (options && options.trimWhitespace === false) {
return inputs;
}
return inputs.map(input => input.trim());
} }
exports.getMultilineInput = getMultilineInput; exports.getMultilineInput = getMultilineInput;
/** /**
@@ -2618,8 +2614,12 @@ exports.getBooleanInput = getBooleanInput;
*/ */
// eslint-disable-next-line @typescript-eslint/no-explicit-any // eslint-disable-next-line @typescript-eslint/no-explicit-any
function setOutput(name, value) { function setOutput(name, value) {
const filePath = process.env['GITHUB_OUTPUT'] || '';
if (filePath) {
return file_command_1.issueFileCommand('OUTPUT', file_command_1.prepareKeyValueMessage(name, value));
}
process.stdout.write(os.EOL); process.stdout.write(os.EOL);
command_1.issueCommand('set-output', { name }, value); command_1.issueCommand('set-output', { name }, utils_1.toCommandValue(value));
} }
exports.setOutput = setOutput; exports.setOutput = setOutput;
/** /**
@@ -2748,7 +2748,11 @@ exports.group = group;
*/ */
// eslint-disable-next-line @typescript-eslint/no-explicit-any // eslint-disable-next-line @typescript-eslint/no-explicit-any
function saveState(name, value) { function saveState(name, value) {
command_1.issueCommand('save-state', { name }, value); const filePath = process.env['GITHUB_STATE'] || '';
if (filePath) {
return file_command_1.issueFileCommand('STATE', file_command_1.prepareKeyValueMessage(name, value));
}
command_1.issueCommand('save-state', { name }, utils_1.toCommandValue(value));
} }
exports.saveState = saveState; exports.saveState = saveState;
/** /**
@@ -2814,13 +2818,14 @@ var __importStar = (this && this.__importStar) || function (mod) {
return result; return result;
}; };
Object.defineProperty(exports, "__esModule", ({ value: true })); Object.defineProperty(exports, "__esModule", ({ value: true }));
exports.issueCommand = void 0; exports.prepareKeyValueMessage = exports.issueFileCommand = void 0;
// We use any as a valid input type // We use any as a valid input type
/* eslint-disable @typescript-eslint/no-explicit-any */ /* eslint-disable @typescript-eslint/no-explicit-any */
const fs = __importStar(__nccwpck_require__(5747)); const fs = __importStar(__nccwpck_require__(5747));
const os = __importStar(__nccwpck_require__(2087)); const os = __importStar(__nccwpck_require__(2087));
const uuid_1 = __nccwpck_require__(5840);
const utils_1 = __nccwpck_require__(5278); const utils_1 = __nccwpck_require__(5278);
function issueCommand(command, message) { function issueFileCommand(command, message) {
const filePath = process.env[`GITHUB_${command}`]; const filePath = process.env[`GITHUB_${command}`];
if (!filePath) { if (!filePath) {
throw new Error(`Unable to find environment variable for file command ${command}`); throw new Error(`Unable to find environment variable for file command ${command}`);
@@ -2832,7 +2837,22 @@ function issueCommand(command, message) {
encoding: 'utf8' encoding: 'utf8'
}); });
} }
exports.issueCommand = issueCommand; exports.issueFileCommand = issueFileCommand;
function prepareKeyValueMessage(key, value) {
const delimiter = `ghadelimiter_${uuid_1.v4()}`;
const convertedValue = utils_1.toCommandValue(value);
// These should realistically never happen, but just in case someone finds a
// way to exploit uuid generation let's not allow keys or values that contain
// the delimiter.
if (key.includes(delimiter)) {
throw new Error(`Unexpected input: name should not contain the delimiter "${delimiter}"`);
}
if (convertedValue.includes(delimiter)) {
throw new Error(`Unexpected input: value should not contain the delimiter "${delimiter}"`);
}
return `${key}<<${delimiter}${os.EOL}${convertedValue}${os.EOL}${delimiter}`;
}
exports.prepareKeyValueMessage = prepareKeyValueMessage;
//# sourceMappingURL=file-command.js.map //# sourceMappingURL=file-command.js.map
/***/ }), /***/ }),

14
package-lock.json generated
View File

@@ -9,7 +9,7 @@
"version": "2.0.0", "version": "2.0.0",
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"@actions/core": "^1.2.6", "@actions/core": "^1.10.0",
"@actions/github": "^5.0.1", "@actions/github": "^5.0.1",
"lodash.deburr": "^4.1.0", "lodash.deburr": "^4.1.0",
"semver": "^7.3.5" "semver": "^7.3.5"
@@ -42,9 +42,9 @@
} }
}, },
"node_modules/@actions/core": { "node_modules/@actions/core": {
"version": "1.9.1", "version": "1.10.0",
"resolved": "https://registry.npmjs.org/@actions/core/-/core-1.9.1.tgz", "resolved": "https://registry.npmjs.org/@actions/core/-/core-1.10.0.tgz",
"integrity": "sha512-5ad+U2YGrmmiw6du20AQW5XuWo7UKN2052FjSV7MX+Wfjf8sCqcsZe62NfgHys4QI4/Y+vQvLKYL8jWtA1ZBTA==", "integrity": "sha512-2aZDDa3zrrZbP5ZYg159sNoLRb61nQ7awl5pSvIq5Qpj81vwDzdMRKzkWJGJuwVvWpvZKx7vspJALyvaaIQyug==",
"dependencies": { "dependencies": {
"@actions/http-client": "^2.0.1", "@actions/http-client": "^2.0.1",
"uuid": "^8.3.2" "uuid": "^8.3.2"
@@ -11187,9 +11187,9 @@
}, },
"dependencies": { "dependencies": {
"@actions/core": { "@actions/core": {
"version": "1.9.1", "version": "1.10.0",
"resolved": "https://registry.npmjs.org/@actions/core/-/core-1.9.1.tgz", "resolved": "https://registry.npmjs.org/@actions/core/-/core-1.10.0.tgz",
"integrity": "sha512-5ad+U2YGrmmiw6du20AQW5XuWo7UKN2052FjSV7MX+Wfjf8sCqcsZe62NfgHys4QI4/Y+vQvLKYL8jWtA1ZBTA==", "integrity": "sha512-2aZDDa3zrrZbP5ZYg159sNoLRb61nQ7awl5pSvIq5Qpj81vwDzdMRKzkWJGJuwVvWpvZKx7vspJALyvaaIQyug==",
"requires": { "requires": {
"@actions/http-client": "^2.0.1", "@actions/http-client": "^2.0.1",
"uuid": "^8.3.2" "uuid": "^8.3.2"

View File

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

View File

@@ -337,8 +337,12 @@ export class IssuesProcessor {
isLabeled(issue, exemptLabel) isLabeled(issue, exemptLabel)
) )
) { ) {
// check to see if the issue is stale and has an exempt label
// see issue #136 for more details
if (issue.isStale) { if (issue.isStale) {
issueLogger.info(`An exempt label was added after the stale label.`); issueLogger.info(
`This $$type has both an exempt and stale label, proceeding to remove the stale label.`
);
await this._removeStaleLabel(issue, staleLabel); await this._removeStaleLabel(issue, staleLabel);
} }

View File

@@ -28,11 +28,11 @@ function _getAndValidateArgs(): IIssuesProcessorOptions {
stalePrMessage: core.getInput('stale-pr-message'), stalePrMessage: core.getInput('stale-pr-message'),
closeIssueMessage: core.getInput('close-issue-message'), closeIssueMessage: core.getInput('close-issue-message'),
closePrMessage: core.getInput('close-pr-message'), closePrMessage: core.getInput('close-pr-message'),
daysBeforeStale: parseInt( daysBeforeStale: parseFloat(
core.getInput('days-before-stale', {required: true}) core.getInput('days-before-stale', {required: true})
), ),
daysBeforeIssueStale: parseInt(core.getInput('days-before-issue-stale')), daysBeforeIssueStale: parseFloat(core.getInput('days-before-issue-stale')),
daysBeforePrStale: parseInt(core.getInput('days-before-pr-stale')), daysBeforePrStale: parseFloat(core.getInput('days-before-pr-stale')),
daysBeforeClose: parseInt( daysBeforeClose: parseInt(
core.getInput('days-before-close', {required: true}) core.getInput('days-before-close', {required: true})
), ),
@@ -92,11 +92,15 @@ function _getAndValidateArgs(): IIssuesProcessorOptions {
includeOnlyAssigned: core.getInput('include-only-assigned') === 'true' includeOnlyAssigned: core.getInput('include-only-assigned') === 'true'
}; };
for (const numberInput of [ for (const numberInput of ['days-before-stale']) {
'days-before-stale', if (isNaN(parseFloat(core.getInput(numberInput)))) {
'days-before-close', const errorMessage = `Option "${numberInput}" did not parse to a valid float`;
'operations-per-run' core.setFailed(errorMessage);
]) { throw new Error(errorMessage);
}
}
for (const numberInput of ['days-before-close', 'operations-per-run']) {
if (isNaN(parseInt(core.getInput(numberInput)))) { if (isNaN(parseInt(core.getInput(numberInput)))) {
const errorMessage = `Option "${numberInput}" did not parse to a valid integer`; const errorMessage = `Option "${numberInput}" did not parse to a valid integer`;
core.setFailed(errorMessage); core.setFailed(errorMessage);