From f227091f2ee6d16f25e959afb6adbca5ebb9926c Mon Sep 17 00:00:00 2001 From: Nick Fields <46869826+nick-fields@users.noreply.github.com> Date: Mon, 25 Apr 2022 22:10:55 -0400 Subject: [PATCH] feat: retry only on specific exit code (#58) * feat: retry only on specific exit code * Run ci_cd on all push events * dedupe step IDs * add assertions for retry_on_exit_code tests * minor: implemented suggested fix from @andersfischernielsen * docs: update readme to reflect new retry_on_exit_code input Co-authored-by: Anders Fischer-Nielsen --- .github/workflows/ci_cd.yml | 39 +++++++++++++++++++++++++++++++++++-- README.md | 8 ++++++-- action.yml | 3 +++ dist/index.js | 24 +++++++++++++---------- src/index.ts | 3 +++ 5 files changed, 63 insertions(+), 14 deletions(-) diff --git a/.github/workflows/ci_cd.yml b/.github/workflows/ci_cd.yml index 7dfd143..b886f5a 100644 --- a/.github/workflows/ci_cd.yml +++ b/.github/workflows/ci_cd.yml @@ -1,8 +1,7 @@ name: CI/CD on: push: - branches: - - '**' + jobs: # runs on branch pushes only ci: @@ -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) + 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: ./ diff --git a/README.md b/README.md index 25a03ab..5539f45 100644 --- a/README.md +++ b/README.md @@ -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) --- @@ -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` @@ -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@` no longer work and must be updated to `nick-fields/retry@`. diff --git a/action.yml b/action.yml index 44de21a..7528bc4 100644 --- a/action.yml +++ b/action.yml @@ -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 diff --git a/dist/index.js b/dist/index.js index ae13ef3..098970d 100644 --- a/dist/index.js +++ b/dist/index.js @@ -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'; @@ -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]; @@ -499,11 +500,14 @@ 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); @@ -511,12 +515,12 @@ function runAction() { 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*/]; } }); }); diff --git a/src/index.ts b/src/index.ts index 0fa1420..b2fa2ff 100644 --- a/src/index.ts +++ b/src/index.ts @@ -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'; @@ -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;