mirror of
https://github.com/nick-fields/retry.git
synced 2026-02-10 07:05:29 +00:00
Compare commits
42 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
616fa81820 | ||
|
|
a25f198007 | ||
|
|
0f986c438b | ||
|
|
3dad7de805 | ||
|
|
14b6b46d04 | ||
|
|
f2eb0f4f8a | ||
|
|
2762157955 | ||
|
|
ce44dab6c9 | ||
|
|
40cf3886b8 | ||
|
|
02a3f09f15 | ||
|
|
6b1204d918 | ||
|
|
8629cc7c0b | ||
|
|
e88a9994b0 | ||
|
|
e4acf08f18 | ||
|
|
51e448da7c | ||
|
|
5f63400863 | ||
|
|
c0687a0dcd | ||
|
|
102f21a736 | ||
|
|
752366eac8 | ||
|
|
a3da592761 | ||
|
|
7c5cca7536 | ||
|
|
f227091f2e | ||
|
|
6183d5c3dd | ||
|
|
71062288b7 | ||
|
|
afe1ef9058 | ||
|
|
e53cf64f16 | ||
|
|
7f8f3d9f0f | ||
|
|
bf1736e338 | ||
|
|
f7cf641580 | ||
|
|
002ef572db | ||
|
|
e80198a9da | ||
|
|
c77dc43532 | ||
|
|
0019811846 | ||
|
|
a63662d5a7 | ||
|
|
67e1bdfd8d | ||
|
|
45ba062d35 | ||
|
|
0470ad1628 | ||
|
|
2750220347 | ||
|
|
b00fd808da | ||
|
|
bebba89192 | ||
|
|
52b3fdbcaa | ||
|
|
f3f0bb1a3c |
1
.config/.eslintignore
Normal file
1
.config/.eslintignore
Normal file
@@ -0,0 +1 @@
|
|||||||
|
.eslintrc.js
|
||||||
7
.config/.eslintrc.js
Normal file
7
.config/.eslintrc.js
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
module.exports = {
|
||||||
|
root: true,
|
||||||
|
parser: '@typescript-eslint/parser',
|
||||||
|
plugins: ['@typescript-eslint'],
|
||||||
|
extends: ['eslint:recommended', 'plugin:@typescript-eslint/recommended', 'prettier'],
|
||||||
|
ignorePatterns: ['**/*.js', 'dist/'],
|
||||||
|
};
|
||||||
2
.config/.prettierignore
Normal file
2
.config/.prettierignore
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
dist/
|
||||||
|
node_modules/
|
||||||
5
.config/.prettierrc.yml
Normal file
5
.config/.prettierrc.yml
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
trailingComma: 'es5'
|
||||||
|
tabWidth: 2
|
||||||
|
semi: true
|
||||||
|
singleQuote: true
|
||||||
|
printWidth: 100
|
||||||
13
.config/jest.config.js
Normal file
13
.config/jest.config.js
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
module.exports = {
|
||||||
|
clearMocks: true,
|
||||||
|
moduleFileExtensions: ['js', 'ts'],
|
||||||
|
rootDir: '..',
|
||||||
|
testEnvironment: 'node',
|
||||||
|
testMatch: ['<rootDir>/src/**/*.test.ts'],
|
||||||
|
transform: {
|
||||||
|
'^.+\\.ts$': 'ts-jest',
|
||||||
|
},
|
||||||
|
verbose: true,
|
||||||
|
collectCoverage: true,
|
||||||
|
collectCoverageFrom: ['src/**/*.{js,ts,jsx,tsx}'],
|
||||||
|
};
|
||||||
1
.github/ISSUE_TEMPLATE/bug-report.md
vendored
1
.github/ISSUE_TEMPLATE/bug-report.md
vendored
@@ -4,7 +4,6 @@ about: Create a report to help us improve
|
|||||||
title: ''
|
title: ''
|
||||||
labels: ''
|
labels: ''
|
||||||
assignees: nick-invision
|
assignees: nick-invision
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
**Describe the bug**
|
**Describe the bug**
|
||||||
|
|||||||
12
.github/codecov.yml
vendored
Normal file
12
.github/codecov.yml
vendored
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
# see https://docs.codecov.com/docs/codecovyml-reference
|
||||||
|
codecov:
|
||||||
|
require_ci_to_pass: false
|
||||||
|
comment:
|
||||||
|
layout: 'diff, flags'
|
||||||
|
behavior: default
|
||||||
|
require_changes: true
|
||||||
|
coverage:
|
||||||
|
# don't pass/fail PRs for coverage yet
|
||||||
|
status:
|
||||||
|
project: off
|
||||||
|
patch: off
|
||||||
267
.github/workflows/ci_cd.yml
vendored
267
.github/workflows/ci_cd.yml
vendored
@@ -1,21 +1,191 @@
|
|||||||
name: CI/CD
|
name: CI/CD
|
||||||
on:
|
on:
|
||||||
push:
|
push:
|
||||||
branches:
|
|
||||||
- '**'
|
|
||||||
jobs:
|
jobs:
|
||||||
# runs on branch pushes only
|
# runs on branch pushes only
|
||||||
ci:
|
ci_unit:
|
||||||
name: Run Tests
|
name: Run Unit Tests
|
||||||
if: startsWith(github.ref, 'refs/heads')
|
if: startsWith(github.ref, 'refs/heads')
|
||||||
runs-on: ubuntu-18.04
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout
|
- name: Checkout
|
||||||
uses: actions/checkout@v2
|
uses: actions/checkout@v2
|
||||||
- name: Setup Node.js
|
- name: Setup Node.js
|
||||||
uses: actions/setup-node@v1
|
uses: actions/setup-node@v1
|
||||||
with:
|
with:
|
||||||
node-version: 12
|
node-version: 16
|
||||||
|
- name: Install dependencies
|
||||||
|
run: npm ci
|
||||||
|
- name: Run Unit Tests
|
||||||
|
run: npm test
|
||||||
|
- uses: codecov/codecov-action@v3
|
||||||
|
with:
|
||||||
|
directory: ./coverage/
|
||||||
|
verbose: true
|
||||||
|
|
||||||
|
ci_integration_envvar:
|
||||||
|
name: Run Integration Env Var Tests
|
||||||
|
if: startsWith(github.ref, 'refs/heads')
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- name: Checkout
|
||||||
|
uses: actions/checkout@v2
|
||||||
|
- name: Setup Node.js
|
||||||
|
uses: actions/setup-node@v1
|
||||||
|
with:
|
||||||
|
node-version: 16
|
||||||
|
- name: Install dependencies
|
||||||
|
run: npm ci
|
||||||
|
- name: env-vars-passed-through
|
||||||
|
uses: ./
|
||||||
|
env:
|
||||||
|
NODE_OPTIONS: '--max_old_space_size=3072'
|
||||||
|
with:
|
||||||
|
timeout_minutes: 1
|
||||||
|
max_attempts: 2
|
||||||
|
command: node -e 'console.log(process.env.NODE_OPTIONS)'
|
||||||
|
|
||||||
|
ci_integration_large_output:
|
||||||
|
name: Run Integration Large Output Tests
|
||||||
|
if: startsWith(github.ref, 'refs/heads')
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- name: Checkout
|
||||||
|
uses: actions/checkout@v2
|
||||||
|
- name: Setup Node.js
|
||||||
|
uses: actions/setup-node@v1
|
||||||
|
with:
|
||||||
|
node-version: 16
|
||||||
|
- name: Install dependencies
|
||||||
|
run: npm ci
|
||||||
|
- name: Test 100MiB of output can be processed
|
||||||
|
id: large-output
|
||||||
|
continue-on-error: true
|
||||||
|
uses: ./
|
||||||
|
with:
|
||||||
|
max_attempts: 1
|
||||||
|
timeout_minutes: 5
|
||||||
|
command: 'make -C ./test-data/large-output bytes-102400'
|
||||||
|
- name: Assert test had expected result
|
||||||
|
uses: nick-invision/assert-action@v1
|
||||||
|
with:
|
||||||
|
expected: failure
|
||||||
|
actual: ${{ steps.large-output.outcome }}
|
||||||
|
- name: Assert exit code is expected
|
||||||
|
uses: nick-invision/assert-action@v1
|
||||||
|
with:
|
||||||
|
expected: 2
|
||||||
|
actual: ${{ steps.large-output.outputs.exit_code }}
|
||||||
|
|
||||||
|
ci_integration_retry_on_exit_code:
|
||||||
|
name: Run Integration retry_on_exit_code Tests
|
||||||
|
if: startsWith(github.ref, 'refs/heads')
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- name: Checkout
|
||||||
|
uses: actions/checkout@v2
|
||||||
|
- name: Setup Node.js
|
||||||
|
uses: actions/setup-node@v1
|
||||||
|
with:
|
||||||
|
node-version: 16
|
||||||
|
- name: Install dependencies
|
||||||
|
run: npm ci
|
||||||
|
- 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 }}
|
||||||
|
|
||||||
|
ci_integration_continue_on_error:
|
||||||
|
name: Run Integration continue_on_error Tests
|
||||||
|
if: startsWith(github.ref, 'refs/heads')
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- name: Checkout
|
||||||
|
uses: actions/checkout@v2
|
||||||
|
- name: Setup Node.js
|
||||||
|
uses: actions/setup-node@v1
|
||||||
|
with:
|
||||||
|
node-version: 16
|
||||||
|
- name: Install dependencies
|
||||||
|
run: npm ci
|
||||||
|
- 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 }}
|
||||||
|
|
||||||
|
ci_integration:
|
||||||
|
name: Run Integration Tests
|
||||||
|
if: startsWith(github.ref, 'refs/heads')
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- name: Checkout
|
||||||
|
uses: actions/checkout@v2
|
||||||
|
- name: Setup Node.js
|
||||||
|
uses: actions/setup-node@v1
|
||||||
|
with:
|
||||||
|
node-version: 16
|
||||||
- name: Install dependencies
|
- name: Install dependencies
|
||||||
run: npm ci
|
run: npm ci
|
||||||
|
|
||||||
@@ -60,6 +230,15 @@ jobs:
|
|||||||
actual: ${{ steps.sad_path_wait_sec.outputs.exit_error }}
|
actual: ${{ steps.sad_path_wait_sec.outputs.exit_error }}
|
||||||
comparison: contains
|
comparison: contains
|
||||||
|
|
||||||
|
- name: new-command-on-retry
|
||||||
|
id: new-command-on-retry
|
||||||
|
uses: ./
|
||||||
|
with:
|
||||||
|
timeout_minutes: 1
|
||||||
|
max_attempts: 3
|
||||||
|
command: node -e "process.exit(1)"
|
||||||
|
new_command_on_retry: node -e "console.log('this is the new command on retry')"
|
||||||
|
|
||||||
- name: on-retry-cmd
|
- name: on-retry-cmd
|
||||||
id: on-retry-cmd
|
id: on-retry-cmd
|
||||||
uses: ./
|
uses: ./
|
||||||
@@ -141,8 +320,39 @@ jobs:
|
|||||||
expected: 2
|
expected: 2
|
||||||
actual: ${{ steps.retry_on_error.outputs.exit_code }}
|
actual: ${{ steps.retry_on_error.outputs.exit_code }}
|
||||||
|
|
||||||
|
- name: sad-path (wrong shell for OS)
|
||||||
|
id: wrong_shell
|
||||||
|
uses: ./
|
||||||
|
continue-on-error: true
|
||||||
|
with:
|
||||||
|
timeout_minutes: 1
|
||||||
|
max_attempts: 2
|
||||||
|
shell: cmd
|
||||||
|
command: 'dir'
|
||||||
|
- uses: nick-invision/assert-action@v1
|
||||||
|
with:
|
||||||
|
expected: 2
|
||||||
|
actual: ${{ steps.wrong_shell.outputs.total_attempts }}
|
||||||
|
- uses: nick-invision/assert-action@v1
|
||||||
|
with:
|
||||||
|
expected: failure
|
||||||
|
actual: ${{ steps.wrong_shell.outcome }}
|
||||||
|
|
||||||
|
# timeout tests take longer to run so run in parallel
|
||||||
|
ci_integration_timeout:
|
||||||
|
name: Run Integration Timeout Tests
|
||||||
|
if: startsWith(github.ref, 'refs/heads')
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- name: Checkout
|
||||||
|
uses: actions/checkout@v2
|
||||||
|
- name: Setup Node.js
|
||||||
|
uses: actions/setup-node@v1
|
||||||
|
with:
|
||||||
|
node-version: 16
|
||||||
|
- name: Install dependencies
|
||||||
|
run: npm ci
|
||||||
|
|
||||||
# timeout tests (takes longer to run so run last)
|
|
||||||
- name: sad-path (timeout)
|
- name: sad-path (timeout)
|
||||||
id: sad_path_timeout
|
id: sad_path_timeout
|
||||||
uses: ./
|
uses: ./
|
||||||
@@ -217,24 +427,6 @@ jobs:
|
|||||||
expected: failure
|
expected: failure
|
||||||
actual: ${{ steps.sad_path_timeout.outcome }}
|
actual: ${{ steps.sad_path_timeout.outcome }}
|
||||||
|
|
||||||
- name: sad-path (wrong shell for OS)
|
|
||||||
id: wrong_shell
|
|
||||||
uses: ./
|
|
||||||
continue-on-error: true
|
|
||||||
with:
|
|
||||||
timeout_minutes: 1
|
|
||||||
max_attempts: 2
|
|
||||||
shell: cmd
|
|
||||||
command: "dir"
|
|
||||||
- uses: nick-invision/assert-action@v1
|
|
||||||
with:
|
|
||||||
expected: 2
|
|
||||||
actual: ${{ steps.wrong_shell.outputs.total_attempts }}
|
|
||||||
- uses: nick-invision/assert-action@v1
|
|
||||||
with:
|
|
||||||
expected: failure
|
|
||||||
actual: ${{ steps.wrong_shell.outcome }}
|
|
||||||
|
|
||||||
ci_windows:
|
ci_windows:
|
||||||
name: Run Windows Tests
|
name: Run Windows Tests
|
||||||
if: startsWith(github.ref, 'refs/heads')
|
if: startsWith(github.ref, 'refs/heads')
|
||||||
@@ -245,7 +437,7 @@ jobs:
|
|||||||
- name: Setup Node.js
|
- name: Setup Node.js
|
||||||
uses: actions/setup-node@v1
|
uses: actions/setup-node@v1
|
||||||
with:
|
with:
|
||||||
node-version: 12
|
node-version: 16
|
||||||
- name: Install dependencies
|
- name: Install dependencies
|
||||||
run: npm ci
|
run: npm ci
|
||||||
- name: Powershell test
|
- name: Powershell test
|
||||||
@@ -269,20 +461,37 @@ jobs:
|
|||||||
max_attempts: 2
|
max_attempts: 2
|
||||||
shell: python
|
shell: python
|
||||||
command: print('1', '2', '3')
|
command: print('1', '2', '3')
|
||||||
|
- name: Multi-line multi-command Test
|
||||||
|
uses: ./
|
||||||
|
with:
|
||||||
|
timeout_minutes: 1
|
||||||
|
max_attempts: 2
|
||||||
|
command: |
|
||||||
|
Get-ComputerInfo
|
||||||
|
Get-Date
|
||||||
|
- name: Multi-line single-command Test
|
||||||
|
uses: ./
|
||||||
|
with:
|
||||||
|
timeout_minutes: 1
|
||||||
|
max_attempts: 2
|
||||||
|
shell: cmd
|
||||||
|
command: >-
|
||||||
|
echo "this is
|
||||||
|
a test"
|
||||||
|
|
||||||
# runs on push to master only
|
# runs on push to master only
|
||||||
cd:
|
cd:
|
||||||
name: Publish Action
|
name: Publish Action
|
||||||
needs: ci
|
needs: [ci_integration, ci_integration_timeout, ci_windows]
|
||||||
if: github.ref == 'refs/heads/master'
|
if: github.ref == 'refs/heads/master'
|
||||||
runs-on: ubuntu-18.04
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout
|
- name: Checkout
|
||||||
uses: actions/checkout@v2
|
uses: actions/checkout@v2
|
||||||
- name: Setup Node.js
|
- name: Setup Node.js
|
||||||
uses: actions/setup-node@v1
|
uses: actions/setup-node@v1
|
||||||
with:
|
with:
|
||||||
node-version: 12
|
node-version: 16
|
||||||
- name: Install dependencies
|
- name: Install dependencies
|
||||||
run: npm ci
|
run: npm ci
|
||||||
- name: Release
|
- name: Release
|
||||||
|
|||||||
5
.husky/commit-msg
Executable file
5
.husky/commit-msg
Executable file
@@ -0,0 +1,5 @@
|
|||||||
|
#!/usr/bin/env sh
|
||||||
|
. "$(dirname -- "$0")/_/husky.sh"
|
||||||
|
|
||||||
|
# lint commit message
|
||||||
|
npx --no -- commitlint --config ./.config/.commitlintrc.js --edit $1
|
||||||
8
.husky/pre-commit
Executable file
8
.husky/pre-commit
Executable file
@@ -0,0 +1,8 @@
|
|||||||
|
#!/usr/bin/env sh
|
||||||
|
. "$(dirname -- "$0")/_/husky.sh"
|
||||||
|
|
||||||
|
# run lint/styling on staged changes
|
||||||
|
npx lint-staged
|
||||||
|
|
||||||
|
# regenerate dist
|
||||||
|
npm run prepare && git add .
|
||||||
@@ -1,7 +0,0 @@
|
|||||||
module.exports = {
|
|
||||||
tabWidth: 2,
|
|
||||||
printWidth: 100,
|
|
||||||
semi: true,
|
|
||||||
singleQuote: true,
|
|
||||||
trailingComma: 'es5',
|
|
||||||
};
|
|
||||||
3
.vscode/extensions.json
vendored
Normal file
3
.vscode/extensions.json
vendored
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
{
|
||||||
|
"recommendations": ["esbenp.prettier-vscode"]
|
||||||
|
}
|
||||||
7
.vscode/settings.json
vendored
7
.vscode/settings.json
vendored
@@ -1,6 +1,7 @@
|
|||||||
{
|
{
|
||||||
|
"editor.defaultFormatter": "esbenp.prettier-vscode",
|
||||||
"editor.formatOnSave": true,
|
"editor.formatOnSave": true,
|
||||||
"prettier.requireConfig": true,
|
"prettier.configPath": "./.config/.prettierrc.yml",
|
||||||
"typescript.tsdk": "node_modules/typescript/lib",
|
"prettier.ignorePath": "./.config/.prettierignore",
|
||||||
"editor.tabSize": 2
|
"typescript.tsdk": "node_modules/typescript/lib"
|
||||||
}
|
}
|
||||||
|
|||||||
110
README.md
110
README.md
@@ -2,6 +2,10 @@
|
|||||||
|
|
||||||
Retries an Action step on failure or timeout. This is currently intended to replace the `run` step for moody commands.
|
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)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
## Inputs
|
## Inputs
|
||||||
|
|
||||||
### `timeout_minutes`
|
### `timeout_minutes`
|
||||||
@@ -26,7 +30,7 @@ Retries an Action step on failure or timeout. This is currently intended to repl
|
|||||||
|
|
||||||
### `shell`
|
### `shell`
|
||||||
|
|
||||||
**Optional** Shell to use to execute `command`. Defaults to `powershell` on Windows, `bash` otherwise. Supports bash, python, pwsh, sh, cmd, and powershell per [docs](https://docs.github.com/en/free-pro-team@latest/actions/reference/workflow-syntax-for-github-actions#using-a-specific-shell)
|
**Optional** Shell to use to execute `command`. Defaults to `powershell` on Windows, `bash` otherwise. Supports bash, python, pwsh, sh, cmd, and powershell per [docs](https://docs.github.com/en/free-pro-team@latest/actions/reference/workflow-syntax-for-github-actions#using-a-specific-shell)
|
||||||
|
|
||||||
### `polling_interval_seconds`
|
### `polling_interval_seconds`
|
||||||
|
|
||||||
@@ -42,7 +46,19 @@ Retries an Action step on failure or timeout. This is currently intended to repl
|
|||||||
|
|
||||||
### `on_retry_command`
|
### `on_retry_command`
|
||||||
|
|
||||||
**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.
|
**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.
|
||||||
|
|
||||||
|
### `new_command_on_retry`
|
||||||
|
|
||||||
|
**Optional** Command to run if the first attempt fails. This command will be called on all subsequent attempts.
|
||||||
|
|
||||||
|
### `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`
|
||||||
|
|
||||||
|
### `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
|
## Outputs
|
||||||
|
|
||||||
@@ -63,7 +79,7 @@ The final error returned by the command
|
|||||||
### Shell
|
### Shell
|
||||||
|
|
||||||
```yaml
|
```yaml
|
||||||
uses: nick-invision/retry@v2
|
uses: nick-fields/retry@v2
|
||||||
with:
|
with:
|
||||||
timeout_minutes: 10
|
timeout_minutes: 10
|
||||||
max_attempts: 3
|
max_attempts: 3
|
||||||
@@ -74,7 +90,7 @@ with:
|
|||||||
### Timeout in minutes
|
### Timeout in minutes
|
||||||
|
|
||||||
```yaml
|
```yaml
|
||||||
uses: nick-invision/retry@v2
|
uses: nick-fields/retry@v2
|
||||||
with:
|
with:
|
||||||
timeout_minutes: 10
|
timeout_minutes: 10
|
||||||
max_attempts: 3
|
max_attempts: 3
|
||||||
@@ -84,7 +100,7 @@ with:
|
|||||||
### Timeout in seconds
|
### Timeout in seconds
|
||||||
|
|
||||||
```yaml
|
```yaml
|
||||||
uses: nick-invision/retry@v2
|
uses: nick-fields/retry@v2
|
||||||
with:
|
with:
|
||||||
timeout_seconds: 15
|
timeout_seconds: 15
|
||||||
max_attempts: 3
|
max_attempts: 3
|
||||||
@@ -94,7 +110,7 @@ with:
|
|||||||
### Only retry after timeout
|
### Only retry after timeout
|
||||||
|
|
||||||
```yaml
|
```yaml
|
||||||
uses: nick-invision/retry@v2
|
uses: nick-fields/retry@v2
|
||||||
with:
|
with:
|
||||||
timeout_seconds: 15
|
timeout_seconds: 15
|
||||||
max_attempts: 3
|
max_attempts: 3
|
||||||
@@ -105,7 +121,7 @@ with:
|
|||||||
### Only retry after error
|
### Only retry after error
|
||||||
|
|
||||||
```yaml
|
```yaml
|
||||||
uses: nick-invision/retry@v2
|
uses: nick-fields/retry@v2
|
||||||
with:
|
with:
|
||||||
timeout_seconds: 15
|
timeout_seconds: 15
|
||||||
max_attempts: 3
|
max_attempts: 3
|
||||||
@@ -113,10 +129,32 @@ with:
|
|||||||
command: npm run some-typically-fast-script
|
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
|
```yaml
|
||||||
- uses: nick-invision/retry@v2
|
- uses: nick-fields/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-fields/assert-action@v1
|
||||||
|
with:
|
||||||
|
expected: success
|
||||||
|
actual: ${{ steps.retry.outcome }}
|
||||||
|
- name: Assert that action exited with expected exit code
|
||||||
|
uses: nick-fields/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-fields/retry@v2
|
||||||
id: retry
|
id: retry
|
||||||
# see https://docs.github.com/en/free-pro-team@latest/actions/reference/workflow-syntax-for-github-actions#jobsjob_idcontinue-on-error
|
# see https://docs.github.com/en/free-pro-team@latest/actions/reference/workflow-syntax-for-github-actions#jobsjob_idcontinue-on-error
|
||||||
continue-on-error: true
|
continue-on-error: true
|
||||||
@@ -126,17 +164,17 @@ with:
|
|||||||
retry_on: error
|
retry_on: error
|
||||||
command: node -e 'process.exit(99);'
|
command: node -e 'process.exit(99);'
|
||||||
- name: Assert that action failed
|
- name: Assert that action failed
|
||||||
uses: nick-invision/assert-action@v1
|
uses: nick-fields/assert-action@v1
|
||||||
with:
|
with:
|
||||||
expected: failure
|
expected: failure
|
||||||
actual: ${{ steps.retry.outcome }}
|
actual: ${{ steps.retry.outcome }}
|
||||||
- name: Assert that action exited with expected exit code
|
- name: Assert that action exited with expected exit code
|
||||||
uses: nick-invision/assert-action@v1
|
uses: nick-fields/assert-action@v1
|
||||||
with:
|
with:
|
||||||
expected: 99
|
expected: 99
|
||||||
actual: ${{ steps.retry.outputs.exit_code }}
|
actual: ${{ steps.retry.outputs.exit_code }}
|
||||||
- name: Assert that action made expected number of attempts
|
- name: Assert that action made expected number of attempts
|
||||||
uses: nick-invision/assert-action@v1
|
uses: nick-fields/assert-action@v1
|
||||||
with:
|
with:
|
||||||
expected: 3
|
expected: 3
|
||||||
actual: ${{ steps.retry.outputs.total_attempts }}
|
actual: ${{ steps.retry.outputs.total_attempts }}
|
||||||
@@ -145,7 +183,7 @@ with:
|
|||||||
### Run script after failure but before retry
|
### Run script after failure but before retry
|
||||||
|
|
||||||
```yaml
|
```yaml
|
||||||
uses: nick-invision/retry@v2
|
uses: nick-fields/retry@v2
|
||||||
with:
|
with:
|
||||||
timeout_seconds: 15
|
timeout_seconds: 15
|
||||||
max_attempts: 3
|
max_attempts: 3
|
||||||
@@ -153,6 +191,52 @@ with:
|
|||||||
on_retry_command: npm run cleanup-flaky-script-output
|
on_retry_command: npm run cleanup-flaky-script-output
|
||||||
```
|
```
|
||||||
|
|
||||||
|
### Run different command after first failure
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
uses: nick-fields/retry@v2
|
||||||
|
with:
|
||||||
|
timeout_seconds: 15
|
||||||
|
max_attempts: 3
|
||||||
|
command: npx jest
|
||||||
|
new_command_on_retry: npx jest --onlyFailures
|
||||||
|
```
|
||||||
|
|
||||||
|
### Run multi-line, multi-command script
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
name: Multi-line multi-command Test
|
||||||
|
uses: ./
|
||||||
|
with:
|
||||||
|
timeout_minutes: 1
|
||||||
|
max_attempts: 2
|
||||||
|
command: |
|
||||||
|
Get-ComputerInfo
|
||||||
|
Get-Date
|
||||||
|
```
|
||||||
|
|
||||||
|
### Run multi-line, single-command script
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
name: Multi-line single-command Test
|
||||||
|
uses: ./
|
||||||
|
with:
|
||||||
|
timeout_minutes: 1
|
||||||
|
max_attempts: 2
|
||||||
|
shell: cmd
|
||||||
|
command: >-
|
||||||
|
echo "this is
|
||||||
|
a test"
|
||||||
|
```
|
||||||
|
|
||||||
## Requirements
|
## Requirements
|
||||||
|
|
||||||
NodeJS is required for this action to run. This runs without issue on all GitHub hosted runners but if you are running into issues with this on self hosted runners ensure NodeJS is installed.
|
NodeJS is required for this action to run. This runs without issue on all GitHub hosted runners but if you are running into issues with this on self hosted runners ensure NodeJS is installed.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## **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.
|
||||||
|
|
||||||
|
Existing workflow references to `nick-invision/retry@<whatever>` no longer work and must be updated to `nick-fields/retry@<whatever>`.
|
||||||
|
|||||||
@@ -33,6 +33,15 @@ inputs:
|
|||||||
on_retry_command:
|
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.
|
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
|
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
|
||||||
|
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:
|
outputs:
|
||||||
total_attempts:
|
total_attempts:
|
||||||
description: The final number of attempts made
|
description: The final number of attempts made
|
||||||
|
|||||||
1839
dist/index.js
vendored
1839
dist/index.js
vendored
File diff suppressed because it is too large
Load Diff
24343
package-lock.json
generated
24343
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
53
package.json
53
package.json
@@ -3,43 +3,64 @@
|
|||||||
"version": "0.0.0-managed-by-semantic-release",
|
"version": "0.0.0-managed-by-semantic-release",
|
||||||
"description": "Retries a GitHub Action step on failure or timeout.",
|
"description": "Retries a GitHub Action step on failure or timeout.",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
|
"lint:base": "eslint --config ./.config/.eslintrc.js ",
|
||||||
|
"lint": "npm run lint:base -- .",
|
||||||
"local": "npm run prepare && node -r dotenv/config ./dist/index.js",
|
"local": "npm run prepare && node -r dotenv/config ./dist/index.js",
|
||||||
"prepare": "ncc build src/index.ts"
|
"prepare": "ncc build src/index.ts && husky install",
|
||||||
|
"style:base": "prettier --config ./.config/.prettierrc.yml --ignore-path ./.config/.prettierignore --write ",
|
||||||
|
"style": "npm run style:base -- .",
|
||||||
|
"test": "jest -c ./.config/jest.config.js"
|
||||||
},
|
},
|
||||||
"repository": {
|
"repository": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
"url": "git+https://github.com/nick-invision/retry.git"
|
"url": "git+https://github.com/nick-invision/retry.git"
|
||||||
},
|
},
|
||||||
"keywords": [],
|
"keywords": [],
|
||||||
"author": "",
|
"author": "Nick Fields",
|
||||||
"license": "ISC",
|
"license": "ISC",
|
||||||
"bugs": {
|
"bugs": {
|
||||||
"url": "https://github.com/nick-invision/retry/issues"
|
"url": "https://github.com/nick-invision/retry/issues"
|
||||||
},
|
},
|
||||||
"homepage": "https://github.com/nick-invision/retry#readme",
|
"homepage": "https://github.com/nick-invision/retry#readme",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@actions/core": "^1.2.6",
|
"@actions/core": "^1.9.0",
|
||||||
"milliseconds": "^1.0.3",
|
"milliseconds": "^1.0.3",
|
||||||
"tree-kill": "^1.2.2"
|
"tree-kill": "^1.2.2"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@commitlint/cli": "11.0.0",
|
"@commitlint/cli": "^16.2.3",
|
||||||
"@commitlint/config-conventional": "11.0.0",
|
"@commitlint/config-conventional": "^16.2.1",
|
||||||
"@semantic-release/changelog": "5.0.1",
|
"@semantic-release/changelog": "^6.0.1",
|
||||||
"@semantic-release/git": "9.0.0",
|
"@semantic-release/git": "^10.0.1",
|
||||||
|
"@types/jest": "^28.1.6",
|
||||||
"@types/milliseconds": "0.0.30",
|
"@types/milliseconds": "0.0.30",
|
||||||
"@types/node": "14.14.7",
|
"@types/node": "^16.11.7",
|
||||||
|
"@typescript-eslint/eslint-plugin": "^5.32.0",
|
||||||
|
"@typescript-eslint/parser": "^5.32.0",
|
||||||
"@zeit/ncc": "^0.20.5",
|
"@zeit/ncc": "^0.20.5",
|
||||||
"dotenv": "8.2.0",
|
"dotenv": "8.2.0",
|
||||||
"husky": "4.3.0",
|
"eslint": "^8.21.0",
|
||||||
"semantic-release": "17.2.3",
|
"eslint-config-prettier": "^8.5.0",
|
||||||
|
"husky": "^8.0.1",
|
||||||
|
"jest": "^28.1.3",
|
||||||
|
"lint-staged": "^13.0.3",
|
||||||
|
"prettier": "^2.7.1",
|
||||||
|
"semantic-release": "19.0.3",
|
||||||
|
"ts-jest": "^28.0.7",
|
||||||
"ts-node": "9.0.0",
|
"ts-node": "9.0.0",
|
||||||
"typescript": "4.0.5"
|
"typescript": "^4.7.4",
|
||||||
|
"yaml-lint": "^1.7.0"
|
||||||
},
|
},
|
||||||
"husky": {
|
"lint-staged": {
|
||||||
"hooks": {
|
"**/*.ts": [
|
||||||
"commit-msg": "commitlint -E HUSKY_GIT_PARAMS",
|
"npm run style:base --",
|
||||||
"pre-commit": "npm run prepare && git add ."
|
"npm run lint:base --"
|
||||||
}
|
],
|
||||||
|
"**/*.{md,yaml,yml}": [
|
||||||
|
"npm run style:base --"
|
||||||
|
],
|
||||||
|
"**/*.{yaml,yml}": [
|
||||||
|
"npx yamllint "
|
||||||
|
]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
12
sample.env
12
sample.env
@@ -1,7 +1,11 @@
|
|||||||
|
# these are the bare minimum envvars required
|
||||||
INPUT_TIMEOUT_MINUTES=1
|
INPUT_TIMEOUT_MINUTES=1
|
||||||
INPUT_MAX_ATTEMPTS=3
|
INPUT_MAX_ATTEMPTS=3
|
||||||
INPUT_COMMAND="node -e 'process.exit(99)'"
|
INPUT_COMMAND="node -e 'process.exit(99)'"
|
||||||
INPUT_RETRY_WAIT_SECONDS=10
|
INPUT_CONTINUE_ON_ERROR=false
|
||||||
SHELL=pwsh
|
|
||||||
INPUT_POLLING_INTERVAL_SECONDS=1
|
# these are optional
|
||||||
INPUT_RETRY_ON=any
|
#INPUT_RETRY_WAIT_SECONDS=10
|
||||||
|
#SHELL=pwsh
|
||||||
|
#INPUT_POLLING_INTERVAL_SECONDS=1
|
||||||
|
#INPUT_RETRY_ON=any
|
||||||
|
|||||||
77
src/index.ts
77
src/index.ts
@@ -1,5 +1,5 @@
|
|||||||
import { getInput, error, warning, info, debug, setOutput } from '@actions/core';
|
import { getInput, error, warning, info, debug, setOutput } from '@actions/core';
|
||||||
import { exec, execSync } from 'child_process';
|
import { execSync, spawn } from 'child_process';
|
||||||
import ms from 'milliseconds';
|
import ms from 'milliseconds';
|
||||||
import kill from 'tree-kill';
|
import kill from 'tree-kill';
|
||||||
|
|
||||||
@@ -16,14 +16,17 @@ const POLLING_INTERVAL_SECONDS = getInputNumber('polling_interval_seconds', fals
|
|||||||
const RETRY_ON = getInput('retry_on') || 'any';
|
const RETRY_ON = getInput('retry_on') || 'any';
|
||||||
const WARNING_ON_RETRY = getInput('warning_on_retry').toLowerCase() === 'true';
|
const WARNING_ON_RETRY = getInput('warning_on_retry').toLowerCase() === 'true';
|
||||||
const ON_RETRY_COMMAND = getInput('on_retry_command');
|
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 OS = process.platform;
|
||||||
const OUTPUT_TOTAL_ATTEMPTS_KEY = 'total_attempts';
|
const OUTPUT_TOTAL_ATTEMPTS_KEY = 'total_attempts';
|
||||||
const OUTPUT_EXIT_CODE_KEY = 'exit_code';
|
const OUTPUT_EXIT_CODE_KEY = 'exit_code';
|
||||||
const OUTPUT_EXIT_ERROR_KEY = 'exit_error';
|
const OUTPUT_EXIT_ERROR_KEY = 'exit_error';
|
||||||
|
|
||||||
var exit: number;
|
let exit: number;
|
||||||
var done: boolean;
|
let done: boolean;
|
||||||
|
|
||||||
function getInputNumber(id: string, required: boolean): number | undefined {
|
function getInputNumber(id: string, required: boolean): number | undefined {
|
||||||
const input = getInput(id, { required });
|
const input = getInput(id, { required });
|
||||||
@@ -41,6 +44,15 @@ function getInputNumber(id: string, required: boolean): number | undefined {
|
|||||||
return num;
|
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() {
|
async function retryWait() {
|
||||||
const waitStart = Date.now();
|
const waitStart = Date.now();
|
||||||
await wait(ms.seconds(RETRY_WAIT_SECONDS));
|
await wait(ms.seconds(RETRY_WAIT_SECONDS));
|
||||||
@@ -71,32 +83,34 @@ function getExecutable(): string {
|
|||||||
|
|
||||||
let executable: string;
|
let executable: string;
|
||||||
switch (SHELL) {
|
switch (SHELL) {
|
||||||
case "bash":
|
case 'bash':
|
||||||
case "python":
|
case 'python':
|
||||||
case "pwsh": {
|
case 'pwsh': {
|
||||||
executable = SHELL;
|
executable = SHELL;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case "sh": {
|
case 'sh': {
|
||||||
if (OS === 'win32') {
|
if (OS === 'win32') {
|
||||||
throw new Error(`Shell ${SHELL} not allowed on OS ${OS}`);
|
throw new Error(`Shell ${SHELL} not allowed on OS ${OS}`);
|
||||||
}
|
}
|
||||||
executable = SHELL;
|
executable = SHELL;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case "cmd":
|
case 'cmd':
|
||||||
case "powershell": {
|
case 'powershell': {
|
||||||
if (OS !== 'win32') {
|
if (OS !== 'win32') {
|
||||||
throw new Error(`Shell ${SHELL} not allowed on OS ${OS}`);
|
throw new Error(`Shell ${SHELL} not allowed on OS ${OS}`);
|
||||||
}
|
}
|
||||||
executable = SHELL + ".exe";
|
executable = SHELL + '.exe';
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
default: {
|
default: {
|
||||||
throw new Error(`Shell ${SHELL} not supported. See https://docs.github.com/en/free-pro-team@latest/actions/reference/workflow-syntax-for-github-actions#using-a-specific-shell for supported shells`);
|
throw new Error(
|
||||||
|
`Shell ${SHELL} not supported. See https://docs.github.com/en/free-pro-team@latest/actions/reference/workflow-syntax-for-github-actions#using-a-specific-shell for supported shells`
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return executable
|
return executable;
|
||||||
}
|
}
|
||||||
|
|
||||||
async function runRetryCmd(): Promise<void> {
|
async function runRetryCmd(): Promise<void> {
|
||||||
@@ -107,20 +121,24 @@ async function runRetryCmd(): Promise<void> {
|
|||||||
|
|
||||||
try {
|
try {
|
||||||
await execSync(ON_RETRY_COMMAND, { stdio: 'inherit' });
|
await execSync(ON_RETRY_COMMAND, { stdio: 'inherit' });
|
||||||
} catch (error) {
|
// eslint-disable-next-line
|
||||||
info(`WARNING: Retry command threw the error ${error.message}`)
|
} catch (error: any) {
|
||||||
|
info(`WARNING: Retry command threw the error ${error.message}`);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async function runCmd() {
|
async function runCmd(attempt: number) {
|
||||||
const end_time = Date.now() + getTimeout();
|
const end_time = Date.now() + getTimeout();
|
||||||
const executable = getExecutable();
|
const executable = getExecutable();
|
||||||
|
|
||||||
exit = 0;
|
exit = 0;
|
||||||
done = false;
|
done = false;
|
||||||
|
|
||||||
debug(`Running command ${COMMAND} on ${OS} using shell ${executable}`)
|
debug(`Running command ${COMMAND} on ${OS} using shell ${executable}`);
|
||||||
var child = exec(COMMAND, { 'shell': executable });
|
const child =
|
||||||
|
attempt > 1 && NEW_COMMAND_ON_RETRY
|
||||||
|
? spawn(NEW_COMMAND_ON_RETRY, { shell: executable })
|
||||||
|
: spawn(COMMAND, { shell: executable });
|
||||||
|
|
||||||
child.stdout?.on('data', (data) => {
|
child.stdout?.on('data', (data) => {
|
||||||
process.stdout.write(data);
|
process.stdout.write(data);
|
||||||
@@ -146,7 +164,7 @@ async function runCmd() {
|
|||||||
await wait(ms.seconds(POLLING_INTERVAL_SECONDS));
|
await wait(ms.seconds(POLLING_INTERVAL_SECONDS));
|
||||||
} while (Date.now() < end_time && !done);
|
} while (Date.now() < end_time && !done);
|
||||||
|
|
||||||
if (!done) {
|
if (!done && child.pid) {
|
||||||
kill(child.pid);
|
kill(child.pid);
|
||||||
await retryWait();
|
await retryWait();
|
||||||
throw new Error(`Timeout of ${getTimeout()}ms hit`);
|
throw new Error(`Timeout of ${getTimeout()}ms hit`);
|
||||||
@@ -165,15 +183,18 @@ async function runAction() {
|
|||||||
try {
|
try {
|
||||||
// just keep overwriting attempts output
|
// just keep overwriting attempts output
|
||||||
setOutput(OUTPUT_TOTAL_ATTEMPTS_KEY, attempt);
|
setOutput(OUTPUT_TOTAL_ATTEMPTS_KEY, attempt);
|
||||||
await runCmd();
|
await runCmd(attempt);
|
||||||
info(`Command completed after ${attempt} attempt(s).`);
|
info(`Command completed after ${attempt} attempt(s).`);
|
||||||
break;
|
break;
|
||||||
} catch (error) {
|
// eslint-disable-next-line
|
||||||
|
} catch (error: any) {
|
||||||
if (attempt === MAX_ATTEMPTS) {
|
if (attempt === MAX_ATTEMPTS) {
|
||||||
throw new Error(`Final attempt failed. ${error.message}`);
|
throw new Error(`Final attempt failed. ${error.message}`);
|
||||||
} else if (!done && RETRY_ON === 'error') {
|
} else if (!done && RETRY_ON === 'error') {
|
||||||
// error: timeout
|
// error: timeout
|
||||||
throw error;
|
throw error;
|
||||||
|
} else if (RETRY_ON_EXIT_CODE && RETRY_ON_EXIT_CODE !== exit) {
|
||||||
|
throw error;
|
||||||
} else if (exit > 0 && RETRY_ON === 'timeout') {
|
} else if (exit > 0 && RETRY_ON === 'timeout') {
|
||||||
// error: error
|
// error: error
|
||||||
throw error;
|
throw error;
|
||||||
@@ -195,12 +216,20 @@ runAction()
|
|||||||
process.exit(0); // success
|
process.exit(0); // success
|
||||||
})
|
})
|
||||||
.catch((err) => {
|
.catch((err) => {
|
||||||
error(err.message);
|
// 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
|
// these can be helpful to know if continue-on-error is true
|
||||||
setOutput(OUTPUT_EXIT_ERROR_KEY, err.message);
|
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
|
// if continue_on_error, exit with exact error code else exit gracefully
|
||||||
process.exit(exit > 0 ? exit : 1);
|
// mimics native continue-on-error that is not supported in composite actions
|
||||||
|
process.exit(CONTINUE_ON_ERROR ? 0 : exitCode);
|
||||||
});
|
});
|
||||||
|
|||||||
17
src/util.test.ts
Normal file
17
src/util.test.ts
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
import 'jest';
|
||||||
|
import { getHeapStatistics } from 'v8';
|
||||||
|
|
||||||
|
import { wait } from './util';
|
||||||
|
|
||||||
|
// mocks the setTimeout function, see https://jestjs.io/docs/timer-mocks
|
||||||
|
jest.useFakeTimers();
|
||||||
|
jest.spyOn(global, 'setTimeout');
|
||||||
|
|
||||||
|
describe('util', () => {
|
||||||
|
test('wait', async () => {
|
||||||
|
const waitTime = 1000;
|
||||||
|
wait(waitTime);
|
||||||
|
expect(setTimeout).toHaveBeenCalledTimes(1);
|
||||||
|
expect(setTimeout).toHaveBeenLastCalledWith(expect.any(Function), waitTime);
|
||||||
|
});
|
||||||
|
});
|
||||||
13
test-data/large-output/Makefile
Normal file
13
test-data/large-output/Makefile
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
SHELL = bash
|
||||||
|
|
||||||
|
# this tests fix for the following issues
|
||||||
|
# https://github.com/nick-fields/retry/issues/76
|
||||||
|
# https://github.com/nick-fields/retry/issues/84
|
||||||
|
|
||||||
|
bytes-%:
|
||||||
|
for i in {1..$*}; do cat kibibyte.txt; done; exit 2
|
||||||
|
.PHONY: bytes-%
|
||||||
|
|
||||||
|
lines-%:
|
||||||
|
for i in {1..$*}; do echo a; done; exit 2
|
||||||
|
.PHONY: lines-%
|
||||||
13
test-data/large-output/kibibyte.txt
Normal file
13
test-data/large-output/kibibyte.txt
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
1: 0000 aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
|
||||||
|
2: 0081 aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
|
||||||
|
3: 0162 aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
|
||||||
|
4: 243 aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
|
||||||
|
5: 324 aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
|
||||||
|
6: 405 aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
|
||||||
|
7: 486 aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
|
||||||
|
8: 567 aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
|
||||||
|
9: 648 aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
|
||||||
|
a: 729 aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
|
||||||
|
b: 810 aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
|
||||||
|
c: 891 aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
|
||||||
|
d: 972 aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
|
||||||
Reference in New Issue
Block a user