Compare commits

..

6 Commits

Author SHA1 Message Date
Nick Fields
002ef572db Merge pull request #49 from lwhiteley/patch-1
fix: incorrect option for continue_on_error
2021-10-23 21:32:55 -04:00
Layton Whiteley
e80198a9da fix: incorrect option for continue_on_error
continue_on_error example was incorrect and if used would generate a warning.

change `continue-on-error` to `continue_on_error`
2021-10-20 16:50:07 +02:00
Nick Fields
c77dc43532 Merge pull request #48 from nick-invision/nrf/continue-on-error
Add continue_on_error action input
2021-10-06 20:02:05 -04:00
Nick Fields
0019811846 docs: update README with new input and usage 2021-09-23 22:36:47 -04:00
Nick Fields
a63662d5a7 patch: refresh from master 2021-09-23 22:29:08 -04:00
Nick Fields
67e1bdfd8d minor: add continue_on_error input option 2021-09-23 22:26:06 -04:00
7 changed files with 149 additions and 21 deletions

View File

@@ -97,6 +97,41 @@ jobs:
expected: failure
actual: ${{ steps.sad_path_error.outcome }}
- name: happy-path (continue_on_error)
id: happy_path_continue_on_error
uses: ./
with:
command: node -e "process.exit(0)"
timeout_minutes: 1
continue_on_error: true
- name: sad-path (continue_on_error)
id: sad_path_continue_on_error
uses: ./
with:
command: node -e "process.exit(33)"
timeout_minutes: 1
continue_on_error: true
- name: Verify continue_on_error returns correct exit code on success
uses: nick-invision/assert-action@v1
with:
expected: 0
actual: ${{ steps.happy_path_continue_on_error.outputs.exit_code }}
- name: Verify continue_on_error exits with correct outcome on success
uses: nick-invision/assert-action@v1
with:
expected: success
actual: ${{ steps.happy_path_continue_on_error.outcome }}
- name: Verify continue_on_error returns correct exit code on error
uses: nick-invision/assert-action@v1
with:
expected: 33
actual: ${{ steps.sad_path_continue_on_error.outputs.exit_code }}
- name: Verify continue_on_error exits with successful outcome when an error occurs
uses: nick-invision/assert-action@v1
with:
expected: success
actual: ${{ steps.sad_path_continue_on_error.outcome }}
- name: retry_on (timeout) fails early if error encountered
id: retry_on_timeout_fail
uses: ./

View File

@@ -44,6 +44,10 @@ Retries an Action step on failure or timeout. This is currently intended to repl
**Optional** Command to run before a retry (such as a cleanup script). Any error thrown from retry command is caught and surfaced as a warning.
### `continue_on_error`
**Optional** Exit successfully even if an error occurs. Same as native continue-on-error behavior, but for use in composite actions. Defaults to `false`
## Outputs
### `total_attempts`
@@ -113,7 +117,29 @@ with:
command: npm run some-typically-fast-script
```
### Retry but allow failure and do something with output
### Retry using continue_on_error input (in composite action) but allow failure and do something with output
```yaml
- uses: nick-invision/retry@v2
id: retry
with:
timeout_seconds: 15
max_attempts: 3
continue_on_error: true
command: node -e 'process.exit(99);'
- name: Assert that step succeeded (despite failing command)
uses: nick-invision/assert-action@v1
with:
expected: success
actual: ${{ steps.retry.outcome }}
- name: Assert that action exited with expected exit code
uses: nick-invision/assert-action@v1
with:
expected: 99
actual: ${{ steps.retry.outputs.exit_code }}
```
### Retry using continue-on-error built-in command (in workflow action) but allow failure and do something with output
```yaml
- uses: nick-invision/retry@v2

View File

@@ -33,6 +33,9 @@ inputs:
on_retry_command:
description: Command to run before a retry (such as a cleanup script). Any error thrown from retry command is caught and surfaced as a warning.
required: false
continue_on_error:
description: Exits successfully even if an error occurs. Same as native continue-on-error behavior, but for use in composite actions. Default is false
default: false
outputs:
total_attempts:
description: The final number of attempts made

66
dist/index.js vendored
View File

@@ -51,7 +51,7 @@ module.exports =
// We use any as a valid input type
/* eslint-disable @typescript-eslint/no-explicit-any */
Object.defineProperty(exports, "__esModule", { value: true });
exports.toCommandValue = void 0;
exports.toCommandProperties = exports.toCommandValue = void 0;
/**
* Sanitizes an input into a string so it can be passed into issueCommand safely
* @param input input to sanitize into a string
@@ -66,6 +66,25 @@ function toCommandValue(input) {
return JSON.stringify(input);
}
exports.toCommandValue = toCommandValue;
/**
*
* @param annotationProperties
* @returns The command properties to send with the actual annotation command
* See IssueCommandProperties: https://github.com/actions/runner/blob/main/src/Runner.Worker/ActionCommandManager.cs#L646
*/
function toCommandProperties(annotationProperties) {
if (!Object.keys(annotationProperties).length) {
return {};
}
return {
title: annotationProperties.title,
line: annotationProperties.startLine,
endLine: annotationProperties.endLine,
col: annotationProperties.startColumn,
endColumn: annotationProperties.endColumn
};
}
exports.toCommandProperties = toCommandProperties;
//# sourceMappingURL=utils.js.map
/***/ }),
@@ -268,6 +287,7 @@ var POLLING_INTERVAL_SECONDS = getInputNumber('polling_interval_seconds', false)
var RETRY_ON = core_1.getInput('retry_on') || 'any';
var WARNING_ON_RETRY = core_1.getInput('warning_on_retry').toLowerCase() === 'true';
var ON_RETRY_COMMAND = core_1.getInput('on_retry_command');
var CONTINUE_ON_ERROR = getInputBoolean('continue_on_error');
var OS = process.platform;
var OUTPUT_TOTAL_ATTEMPTS_KEY = 'total_attempts';
var OUTPUT_EXIT_CODE_KEY = 'exit_code';
@@ -286,6 +306,13 @@ function getInputNumber(id, required) {
}
return num;
}
function getInputBoolean(id) {
var input = core_1.getInput(id);
if (!['true', 'false'].includes(input.toLowerCase())) {
throw "Input " + id + " only accepts boolean values. Received " + input;
}
return input.toLowerCase() === 'true';
}
function retryWait() {
return __awaiter(this, void 0, void 0, function () {
var waitStart;
@@ -497,12 +524,20 @@ runAction()
process.exit(0); // success
})
.catch(function (err) {
// exact error code if available, otherwise just 1
var exitCode = exit > 0 ? exit : 1;
if (CONTINUE_ON_ERROR) {
core_1.warning(err.message);
}
else {
core_1.error(err.message);
}
// these can be helpful to know if continue-on-error is true
core_1.setOutput(OUTPUT_EXIT_ERROR_KEY, err.message);
core_1.setOutput(OUTPUT_EXIT_CODE_KEY, exit > 0 ? exit : 1);
// exit with exact error code if available, otherwise just exit with 1
process.exit(exit > 0 ? exit : 1);
core_1.setOutput(OUTPUT_EXIT_CODE_KEY, exitCode);
// if continue_on_error, exit with exact error code else exit gracefully
// mimics native continue-on-error that is not supported in composite actions
process.exit(CONTINUE_ON_ERROR ? 0 : exitCode);
});
@@ -641,7 +676,7 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
});
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.getState = exports.saveState = exports.group = exports.endGroup = exports.startGroup = exports.info = exports.warning = exports.error = exports.debug = exports.isDebug = exports.setFailed = exports.setCommandEcho = exports.setOutput = exports.getBooleanInput = exports.getMultilineInput = exports.getInput = exports.addPath = exports.setSecret = exports.exportVariable = exports.ExitCode = void 0;
exports.getState = exports.saveState = exports.group = exports.endGroup = exports.startGroup = exports.info = exports.notice = exports.warning = exports.error = exports.debug = exports.isDebug = exports.setFailed = exports.setCommandEcho = exports.setOutput = exports.getBooleanInput = exports.getMultilineInput = exports.getInput = exports.addPath = exports.setSecret = exports.exportVariable = exports.ExitCode = void 0;
const command_1 = __webpack_require__(431);
const file_command_1 = __webpack_require__(102);
const utils_1 = __webpack_require__(82);
@@ -819,19 +854,30 @@ exports.debug = debug;
/**
* Adds an error issue
* @param message error issue message. Errors will be converted to string via toString()
* @param properties optional properties to add to the annotation.
*/
function error(message) {
command_1.issue('error', message instanceof Error ? message.toString() : message);
function error(message, properties = {}) {
command_1.issueCommand('error', utils_1.toCommandProperties(properties), message instanceof Error ? message.toString() : message);
}
exports.error = error;
/**
* Adds an warning issue
* Adds a warning issue
* @param message warning issue message. Errors will be converted to string via toString()
* @param properties optional properties to add to the annotation.
*/
function warning(message) {
command_1.issue('warning', message instanceof Error ? message.toString() : message);
function warning(message, properties = {}) {
command_1.issueCommand('warning', utils_1.toCommandProperties(properties), message instanceof Error ? message.toString() : message);
}
exports.warning = warning;
/**
* Adds a notice issue
* @param message notice issue message. Errors will be converted to string via toString()
* @param properties optional properties to add to the annotation.
*/
function notice(message, properties = {}) {
command_1.issueCommand('notice', utils_1.toCommandProperties(properties), message instanceof Error ? message.toString() : message);
}
exports.notice = notice;
/**
* Writes info to log with console.log.
* @param message info message

6
package-lock.json generated
View File

@@ -5,9 +5,9 @@
"requires": true,
"dependencies": {
"@actions/core": {
"version": "1.4.0",
"resolved": "https://registry.npmjs.org/@actions/core/-/core-1.4.0.tgz",
"integrity": "sha512-CGx2ilGq5i7zSLgiiGUtBCxhRRxibJYU6Fim0Q1Wg2aQL2LTnF27zbqZOrxfvFQ55eSBW0L8uVStgtKMpa0Qlg=="
"version": "1.5.0",
"resolved": "https://registry.npmjs.org/@actions/core/-/core-1.5.0.tgz",
"integrity": "sha512-eDOLH1Nq9zh+PJlYLqEMkS/jLQxhksPNmUGNBHfa4G+tQmnIhzpctxmchETtVGyBOvXgOVVpYuE40+eS4cUnwQ=="
},
"@babel/code-frame": {
"version": "7.8.3",

View File

@@ -18,7 +18,7 @@
},
"homepage": "https://github.com/nick-invision/retry#readme",
"dependencies": {
"@actions/core": "^1.4.0",
"@actions/core": "^1.5.0",
"milliseconds": "^1.0.3",
"tree-kill": "^1.2.2"
},

View File

@@ -16,6 +16,7 @@ const POLLING_INTERVAL_SECONDS = getInputNumber('polling_interval_seconds', fals
const RETRY_ON = getInput('retry_on') || 'any';
const WARNING_ON_RETRY = getInput('warning_on_retry').toLowerCase() === 'true';
const ON_RETRY_COMMAND = getInput('on_retry_command');
const CONTINUE_ON_ERROR = getInputBoolean('continue_on_error');
const OS = process.platform;
const OUTPUT_TOTAL_ATTEMPTS_KEY = 'total_attempts';
@@ -41,6 +42,15 @@ function getInputNumber(id: string, required: boolean): number | undefined {
return num;
}
function getInputBoolean(id: string): Boolean {
const input = getInput(id);
if (!['true','false'].includes(input.toLowerCase())) {
throw `Input ${id} only accepts boolean values. Received ${input}`;
}
return input.toLowerCase() === 'true'
}
async function retryWait() {
const waitStart = Date.now();
await wait(ms.seconds(RETRY_WAIT_SECONDS));
@@ -195,12 +205,20 @@ runAction()
process.exit(0); // success
})
.catch((err) => {
// exact error code if available, otherwise just 1
const exitCode = exit > 0 ? exit : 1;
if (CONTINUE_ON_ERROR) {
warning(err.message);
} else {
error(err.message);
}
// these can be helpful to know if continue-on-error is true
setOutput(OUTPUT_EXIT_ERROR_KEY, err.message);
setOutput(OUTPUT_EXIT_CODE_KEY, exit > 0 ? exit : 1);
setOutput(OUTPUT_EXIT_CODE_KEY, exitCode);
// exit with exact error code if available, otherwise just exit with 1
process.exit(exit > 0 ? exit : 1);
// if continue_on_error, exit with exact error code else exit gracefully
// mimics native continue-on-error that is not supported in composite actions
process.exit(CONTINUE_ON_ERROR ? 0 : exitCode);
});