Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: retry only on specific exit code #58

Merged
merged 7 commits into from Apr 26, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
39 changes: 37 additions & 2 deletions .github/workflows/ci_cd.yml
@@ -1,8 +1,7 @@
name: CI/CD
on:
push:
branches:
- '**'

jobs:
# runs on branch pushes only
ci:
Expand Down Expand Up @@ -79,6 +78,42 @@ jobs:
command: node -e "process.exit(1)"
on_retry_command: node -e "console.log('this is a retry command')"

- name: retry_on_exit_code (with expected error code)
id: retry_on_exit_code_expected
uses: ./
continue-on-error: true
with:
timeout_minutes: 1
retry_on_exit_code: 2
max_attempts: 3
command: node -e "process.exit(2)"
- uses: nick-invision/assert-action@v1
with:
expected: failure
actual: ${{ steps.retry_on_exit_code_expected.outcome }}
- uses: nick-invision/assert-action@v1
with:
expected: 3
actual: ${{ steps.retry_on_exit_code_expected.outputs.total_attempts }}

- name: retry_on_exit_code (with unexpected error code)
Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@andersfischernielsen @erwinw

Can you take a look at this test failure? I made a few tweaks to your tests, first being that a duplicate id was used which caused the entire workflow to fail, then I added assertions to your two tests.

The assertion is failing for your retry_on_exit_code (with unexpected error code) test. I assert that if an unexpected exit code is thrown (exit code 1), and the retry_on_exit_code is set (exit code 2), then the rerun should only occur once not 3x as is happening currently. I think you need to also handle max_attempts when retry_on_exit_code is set and skip additional attempts if an unexpected exit code is returned.

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hey @nick-fields , well danged, I've poked gently asked @andersfischernielsen to have a look at this!

id: retry_on_exit_code_unexpected
uses: ./
continue-on-error: true
with:
timeout_minutes: 1
retry_on_exit_code: 2
max_attempts: 3
command: node -e "process.exit(1)"
- uses: nick-invision/assert-action@v1
with:
expected: failure
actual: ${{ steps.retry_on_exit_code_unexpected.outcome }}
- uses: nick-invision/assert-action@v1
with:
expected: 1
actual: ${{ steps.retry_on_exit_code_unexpected.outputs.total_attempts }}

- name: on-retry-cmd (on-retry fails)
id: on-retry-cmd-fails
uses: ./
Expand Down
8 changes: 6 additions & 2 deletions README.md
Expand Up @@ -2,7 +2,7 @@

Retries an Action step on failure or timeout. This is currently intended to replace the `run` step for moody commands.

**NOTE:** Ownership of this project was transferred to my personal account `nick-fields` from my work account `nick-invision`. Details [here](#Ownership)
**NOTE:** Ownership of this project was transferred to my personal account `nick-fields` from my work account `nick-invision`. Details [here](#Ownership)

---

Expand Down Expand Up @@ -56,6 +56,10 @@ Retries an Action step on failure or timeout. This is currently intended to repl

**Optional** Exit successfully even if an error occurs. Same as native continue-on-error behavior, but for use in composite actions. Defaults to `false`

### `retry_on_exit_code`

**Optional** Specific exit code to retry on. This will only retry for the given error code and fail immediately other error codes.

## Outputs

### `total_attempts`
Expand Down Expand Up @@ -233,6 +237,6 @@ NodeJS is required for this action to run. This runs without issue on all GitHub

## **Ownership**

As of 2022/02/15 ownership of this project has been transferred to my personal account `nick-fields` from my work account `nick-invision` due to me leaving InVision. I am the author and have been the primary maintainer since day one and will continue to maintain this as needed.
As of 2022/02/15 ownership of this project has been transferred to my personal account `nick-fields` from my work account `nick-invision` due to me leaving InVision. I am the author and have been the primary maintainer since day one and will continue to maintain this as needed.

Existing workflow references to `nick-invision/retry@<whatever>` no longer work and must be updated to `nick-fields/retry@<whatever>`.
3 changes: 3 additions & 0 deletions action.yml
Expand Up @@ -39,6 +39,9 @@ inputs:
new_command_on_retry:
description: Command to run if the first attempt fails. This command will be called on all subsequent attempts.
required: false
retry_on_exit_code:
description: Specific exit code to retry on. This will only retry for the given error code and fail immediately other error codes.
required: false
outputs:
total_attempts:
description: The final number of attempts made
Expand Down
24 changes: 14 additions & 10 deletions dist/index.js
Expand Up @@ -289,6 +289,7 @@ var WARNING_ON_RETRY = core_1.getInput('warning_on_retry').toLowerCase() === 'tr
var ON_RETRY_COMMAND = core_1.getInput('on_retry_command');
var CONTINUE_ON_ERROR = getInputBoolean('continue_on_error');
var NEW_COMMAND_ON_RETRY = core_1.getInput('new_command_on_retry');
var RETRY_ON_EXIT_CODE = getInputNumber('retry_on_exit_code', false);
var OS = process.platform;
var OUTPUT_TOTAL_ATTEMPTS_KEY = 'total_attempts';
var OUTPUT_EXIT_CODE_KEY = 'exit_code';
Expand Down Expand Up @@ -479,17 +480,17 @@ function runAction() {
attempt = 1;
_a.label = 2;
case 2:
if (!(attempt <= MAX_ATTEMPTS)) return [3 /*break*/, 12];
if (!(attempt <= MAX_ATTEMPTS)) return [3 /*break*/, 13];
_a.label = 3;
case 3:
_a.trys.push([3, 5, , 11]);
_a.trys.push([3, 5, , 12]);
// just keep overwriting attempts output
core_1.setOutput(OUTPUT_TOTAL_ATTEMPTS_KEY, attempt);
return [4 /*yield*/, runCmd(attempt)];
case 4:
_a.sent();
core_1.info("Command completed after " + attempt + " attempt(s).");
return [3 /*break*/, 12];
return [3 /*break*/, 13];
case 5:
error_2 = _a.sent();
if (!(attempt === MAX_ATTEMPTS)) return [3 /*break*/, 6];
Expand All @@ -499,24 +500,27 @@ function runAction() {
// error: timeout
throw error_2;
case 7:
if (!(exit > 0 && RETRY_ON === 'timeout')) return [3 /*break*/, 8];
if (!(RETRY_ON_EXIT_CODE && RETRY_ON_EXIT_CODE !== exit)) return [3 /*break*/, 8];
throw error_2;
case 8:
if (!(exit > 0 && RETRY_ON === 'timeout')) return [3 /*break*/, 9];
// error: error
throw error_2;
case 8: return [4 /*yield*/, runRetryCmd()];
case 9:
case 9: return [4 /*yield*/, runRetryCmd()];
case 10:
_a.sent();
if (WARNING_ON_RETRY) {
core_1.warning("Attempt " + attempt + " failed. Reason: " + error_2.message);
}
else {
core_1.info("Attempt " + attempt + " failed. Reason: " + error_2.message);
}
_a.label = 10;
case 10: return [3 /*break*/, 11];
case 11:
_a.label = 11;
case 11: return [3 /*break*/, 12];
case 12:
attempt++;
return [3 /*break*/, 2];
case 12: return [2 /*return*/];
case 13: return [2 /*return*/];
}
});
});
Expand Down
3 changes: 3 additions & 0 deletions src/index.ts
Expand Up @@ -18,6 +18,7 @@ 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 NEW_COMMAND_ON_RETRY = getInput('new_command_on_retry');
const RETRY_ON_EXIT_CODE = getInputNumber('retry_on_exit_code', false);

const OS = process.platform;
const OUTPUT_TOTAL_ATTEMPTS_KEY = 'total_attempts';
Expand Down Expand Up @@ -187,6 +188,8 @@ async function runAction() {
} else if (!done && RETRY_ON === 'error') {
// error: timeout
throw error;
} else if (RETRY_ON_EXIT_CODE && RETRY_ON_EXIT_CODE !== exit){
throw error;
} else if (exit > 0 && RETRY_ON === 'timeout') {
// error: error
throw error;
Expand Down