Co-authored-by: UltralyticsAssistant <[email protected]>
138 lines
4.6 KiB
YAML
138 lines
4.6 KiB
YAML
# Ultralytics Actions 🚀, AGPL-3.0 License https://ultralytics.com/license
|
|
#
|
|
# Example usage:
|
|
#
|
|
# Basic usage (will retry failed step 3 times):
|
|
# steps:
|
|
# - uses: ultralytics/actions/retry@main
|
|
# with:
|
|
# run: python train.py
|
|
#
|
|
# Advanced usage:
|
|
# steps:
|
|
# - uses: ultralytics/actions/retry@main
|
|
# with:
|
|
# run: |
|
|
# python setup.py install
|
|
# pytest tests/
|
|
# retries: 2 # Retry twice after initial attempt (3 total runs)
|
|
# timeout_minutes: 30 # Each attempt times out after 30 minutes
|
|
# retry_delay_seconds: 60 # Wait 60 seconds between retries
|
|
# shell: bash # Use python or bash shell
|
|
|
|
name: "Step-Level Retry"
|
|
description: "Retries a step while preserving its full context"
|
|
inputs:
|
|
timeout_minutes:
|
|
description: "Maximum time in minutes for each attempt"
|
|
required: false
|
|
default: "60"
|
|
retries:
|
|
description: "Number of retry attempts after initial run"
|
|
required: false
|
|
default: "3"
|
|
retry_delay_seconds:
|
|
description: "Delay between retries in seconds"
|
|
required: false
|
|
default: "30"
|
|
run:
|
|
description: "Command to run"
|
|
required: true
|
|
shell:
|
|
description: "Shell to use (bash or python)"
|
|
required: false
|
|
default: "bash"
|
|
|
|
runs:
|
|
using: "composite"
|
|
steps:
|
|
- name: Execute with retry (Python)
|
|
if: inputs.shell == 'python'
|
|
shell: python
|
|
env: ${{ env }}
|
|
run: |
|
|
import time
|
|
start_time = time.time()
|
|
timeout_seconds = ${{ inputs.timeout_minutes }} * 60
|
|
attempt = 1
|
|
max_attempts = 1 + ${{ inputs.retries }} # Initial run + retries
|
|
|
|
while True:
|
|
if attempt > 1:
|
|
print(f"::group::Retry {attempt-1} of ${{ inputs.retries }}")
|
|
|
|
if (time.time() - start_time) > timeout_seconds:
|
|
print(f"::error::Step timed out after ${{ inputs.timeout_minutes }} minutes")
|
|
exit(1)
|
|
|
|
try:
|
|
${{ inputs.run }}
|
|
if attempt > 1:
|
|
print("::endgroup::")
|
|
exit(0)
|
|
except Exception as e:
|
|
if attempt > 1:
|
|
print(f"Retry {attempt-1} failed with error: {str(e)}")
|
|
print("::endgroup::")
|
|
else:
|
|
print(f"Initial attempt failed with error: {str(e)}")
|
|
|
|
if attempt >= max_attempts:
|
|
print(f"::error::Step failed after initial attempt and {attempt-1} retries")
|
|
exit(1)
|
|
|
|
print(f"Retrying in ${{ inputs.retry_delay_seconds }} seconds...")
|
|
time.sleep(${{ inputs.retry_delay_seconds }})
|
|
attempt += 1
|
|
|
|
- name: Execute with retry (Bash)
|
|
if: inputs.shell != 'python'
|
|
shell: bash
|
|
env: ${{ env }}
|
|
run: |
|
|
function retry_command {
|
|
local start_time=$(date +%s)
|
|
local timeout_seconds=$(( ${{ inputs.timeout_minutes }} * 60 ))
|
|
local attempt=1
|
|
local max_attempts=$(( 1 + ${{ inputs.retries }} )) # Initial run + retries
|
|
|
|
while true; do
|
|
if [ $attempt -gt 1 ]; then
|
|
echo "::group::Retry $((attempt-1)) of ${{ inputs.retries }}"
|
|
fi
|
|
|
|
local current_time=$(date +%s)
|
|
if [ $((current_time - start_time)) -gt $timeout_seconds ]; then
|
|
echo "::error::Step timed out after ${{ inputs.timeout_minutes }} minutes"
|
|
return 1
|
|
fi
|
|
|
|
set +e
|
|
${{ inputs.run }}
|
|
local exit_code=$?
|
|
set -e
|
|
|
|
if [ $exit_code -eq 0 ]; then
|
|
[ $attempt -gt 1 ] && echo "::endgroup::"
|
|
return 0
|
|
fi
|
|
|
|
if [ $attempt -gt 1 ]; then
|
|
echo "Retry $((attempt-1)) failed with exit code $exit_code"
|
|
echo "::endgroup::"
|
|
else
|
|
echo "Initial attempt failed with exit code $exit_code"
|
|
fi
|
|
|
|
if [ $attempt -ge $max_attempts ]; then
|
|
echo "::error::Step failed after initial attempt and $((attempt-1)) retries"
|
|
return $exit_code
|
|
fi
|
|
|
|
echo "Retrying in ${{ inputs.retry_delay_seconds }} seconds..."
|
|
sleep ${{ inputs.retry_delay_seconds }}
|
|
attempt=$((attempt + 1))
|
|
done
|
|
}
|
|
retry_command
|