mirror of
https://github.com/appleboy/ssh-action.git
synced 2026-06-29 00:19:03 +00:00
Merge 2168c3d3c7 into 1530429296
This commit is contained in:
commit
daf58f9dca
18
README.md
18
README.md
@ -99,6 +99,8 @@ These parameters control the commands executed on the remote host and related be
|
|||||||
| curl_insecure | Allow curl to connect to SSL sites without certificates | false |
|
| curl_insecure | Allow curl to connect to SSL sites without certificates | false |
|
||||||
| capture_stdout | Capture standard output from commands as action output | false |
|
| capture_stdout | Capture standard output from commands as action output | false |
|
||||||
| version | drone-ssh binary version. If not specified, the latest version will be used. | |
|
| version | drone-ssh binary version. If not specified, the latest version will be used. | |
|
||||||
|
| retry_attempts | Number of retries after an SSH connection failure | 0 |
|
||||||
|
| retry_delay | Delay between retry attempts | 0s |
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
@ -309,6 +311,22 @@ This section covers common and advanced usage patterns, including multi-host, pr
|
|||||||
script_path: scripts/script.sh
|
script_path: scripts/script.sh
|
||||||
```
|
```
|
||||||
|
|
||||||
|
### Retry SSH connection failures
|
||||||
|
|
||||||
|
Use `retry_attempts` to retry transient SSH connection failures, such as `dial tcp ...: i/o timeout`. Retries are disabled by default. Remote command failures are not retried.
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
- name: Retry transient SSH connection failures
|
||||||
|
uses: appleboy/ssh-action@v1
|
||||||
|
with:
|
||||||
|
host: ${{ secrets.HOST }}
|
||||||
|
username: ${{ secrets.USERNAME }}
|
||||||
|
key: ${{ secrets.KEY }}
|
||||||
|
retry_attempts: 3
|
||||||
|
retry_delay: 5s
|
||||||
|
script: whoami
|
||||||
|
```
|
||||||
|
|
||||||
### Multiple hosts
|
### Multiple hosts
|
||||||
|
|
||||||
```diff
|
```diff
|
||||||
|
|||||||
@ -99,6 +99,8 @@
|
|||||||
| curl_insecure | 允许 curl 连接无证书的 SSL 站点 | false |
|
| curl_insecure | 允许 curl 连接无证书的 SSL 站点 | false |
|
||||||
| capture_stdout | 捕获命令的标准输出作为 Action 输出 | false |
|
| capture_stdout | 捕获命令的标准输出作为 Action 输出 | false |
|
||||||
| version | drone-ssh 二进制版本,未指定时使用最新版本 | |
|
| version | drone-ssh 二进制版本,未指定时使用最新版本 | |
|
||||||
|
| retry_attempts | SSH 连接失败后的重试次数 | 0 |
|
||||||
|
| retry_delay | 每次重试之间的延迟 | 0s |
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
@ -309,6 +311,22 @@ ssh-keygen -t ed25519 -a 200 -C "your_email@example.com"
|
|||||||
script_path: scripts/script.sh
|
script_path: scripts/script.sh
|
||||||
```
|
```
|
||||||
|
|
||||||
|
### 重试 SSH 连接失败
|
||||||
|
|
||||||
|
使用 `retry_attempts` 重试临时 SSH 连接失败,例如 `dial tcp ...: i/o timeout`。默认不重试。远程命令失败不会重试。
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
- name: 重试临时 SSH 连接失败
|
||||||
|
uses: appleboy/ssh-action@v1
|
||||||
|
with:
|
||||||
|
host: ${{ secrets.HOST }}
|
||||||
|
username: ${{ secrets.USERNAME }}
|
||||||
|
key: ${{ secrets.KEY }}
|
||||||
|
retry_attempts: 3
|
||||||
|
retry_delay: 5s
|
||||||
|
script: whoami
|
||||||
|
```
|
||||||
|
|
||||||
### 多主机
|
### 多主机
|
||||||
|
|
||||||
```diff
|
```diff
|
||||||
|
|||||||
@ -99,6 +99,8 @@
|
|||||||
| curl_insecure | 允許 curl 連線無憑證的 SSL 網站 | false |
|
| curl_insecure | 允許 curl 連線無憑證的 SSL 網站 | false |
|
||||||
| capture_stdout | 擷取指令的標準輸出作為 Action 輸出 | false |
|
| capture_stdout | 擷取指令的標準輸出作為 Action 輸出 | false |
|
||||||
| version | drone-ssh 執行檔版本,未指定時使用最新版本 | |
|
| version | drone-ssh 執行檔版本,未指定時使用最新版本 | |
|
||||||
|
| retry_attempts | SSH 連線失敗後的重試次數 | 0 |
|
||||||
|
| retry_delay | 每次重試之間的延遲 | 0s |
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
@ -309,6 +311,22 @@ ssh-keygen -t ed25519 -a 200 -C "your_email@example.com"
|
|||||||
script_path: scripts/script.sh
|
script_path: scripts/script.sh
|
||||||
```
|
```
|
||||||
|
|
||||||
|
### 重試 SSH 連線失敗
|
||||||
|
|
||||||
|
使用 `retry_attempts` 重試暫時性 SSH 連線失敗,例如 `dial tcp ...: i/o timeout`。預設不重試。遠端指令失敗不會重試。
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
- name: 重試暫時性 SSH 連線失敗
|
||||||
|
uses: appleboy/ssh-action@v1
|
||||||
|
with:
|
||||||
|
host: ${{ secrets.HOST }}
|
||||||
|
username: ${{ secrets.USERNAME }}
|
||||||
|
key: ${{ secrets.KEY }}
|
||||||
|
retry_attempts: 3
|
||||||
|
retry_delay: 5s
|
||||||
|
script: whoami
|
||||||
|
```
|
||||||
|
|
||||||
### 多主機
|
### 多主機
|
||||||
|
|
||||||
```diff
|
```diff
|
||||||
|
|||||||
@ -84,6 +84,12 @@ inputs:
|
|||||||
version:
|
version:
|
||||||
description: |
|
description: |
|
||||||
The version of drone-ssh to use.
|
The version of drone-ssh to use.
|
||||||
|
retry_attempts:
|
||||||
|
description: "Number of retry attempts after the initial SSH command fails."
|
||||||
|
default: "0"
|
||||||
|
retry_delay:
|
||||||
|
description: "Delay between retry attempts"
|
||||||
|
default: "0s"
|
||||||
|
|
||||||
outputs:
|
outputs:
|
||||||
stdout:
|
stdout:
|
||||||
@ -138,6 +144,8 @@ runs:
|
|||||||
INPUT_SYNC: ${{ inputs.sync }}
|
INPUT_SYNC: ${{ inputs.sync }}
|
||||||
INPUT_CAPTURE_STDOUT: ${{ inputs.capture_stdout }}
|
INPUT_CAPTURE_STDOUT: ${{ inputs.capture_stdout }}
|
||||||
INPUT_CURL_INSECURE: ${{ inputs.curl_insecure }}
|
INPUT_CURL_INSECURE: ${{ inputs.curl_insecure }}
|
||||||
|
INPUT_RETRY_ATTEMPTS: ${{ inputs.retry_attempts }}
|
||||||
|
INPUT_RETRY_DELAY: ${{ inputs.retry_delay }}
|
||||||
DRONE_SSH_VERSION: ${{ inputs.version }}
|
DRONE_SSH_VERSION: ${{ inputs.version }}
|
||||||
|
|
||||||
branding:
|
branding:
|
||||||
|
|||||||
106
entrypoint.sh
106
entrypoint.sh
@ -20,6 +20,104 @@ function log_error() {
|
|||||||
exit "$2"
|
exit "$2"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function validate_non_negative_integer() {
|
||||||
|
local name="$1"
|
||||||
|
local value="$2"
|
||||||
|
|
||||||
|
if [[ ! "${value}" =~ ^[0-9]+$ ]]; then
|
||||||
|
log_error "${name} must be a non-negative integer, got: ${value}" 1
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
function is_retryable_connection_failure() {
|
||||||
|
local output="$1"
|
||||||
|
|
||||||
|
[[ "${output}" =~ dial[[:space:]]+tcp ]] || \
|
||||||
|
[[ "${output}" =~ i/o[[:space:]]+timeout ]] || \
|
||||||
|
[[ "${output}" =~ connection[[:space:]]+refused ]] || \
|
||||||
|
[[ "${output}" =~ connection[[:space:]]+reset ]] || \
|
||||||
|
[[ "${output}" =~ connection[[:space:]]+timed[[:space:]]+out ]] || \
|
||||||
|
[[ "${output}" =~ no[[:space:]]+route[[:space:]]+to[[:space:]]+host ]] || \
|
||||||
|
[[ "${output}" =~ network[[:space:]]+is[[:space:]]+unreachable ]] || \
|
||||||
|
[[ "${output}" =~ no[[:space:]]+such[[:space:]]+host ]] || \
|
||||||
|
[[ "${output}" =~ ssh:[[:space:]]+handshake[[:space:]]+failed ]]
|
||||||
|
}
|
||||||
|
|
||||||
|
function run_ssh_command() {
|
||||||
|
local status=0
|
||||||
|
local stderr=""
|
||||||
|
|
||||||
|
if [[ "${INPUT_CAPTURE_STDOUT}" == 'true' ]]; then
|
||||||
|
echo 'stdout<<EOF' >> "${GITHUB_OUTPUT}"
|
||||||
|
exec 3>&1
|
||||||
|
set +e
|
||||||
|
stderr="$(
|
||||||
|
{
|
||||||
|
"${TARGET}" "$@" 1> >(tee -a "${GITHUB_OUTPUT}" >&3)
|
||||||
|
} 2>&1 | tee /dev/stderr
|
||||||
|
)"
|
||||||
|
status="${PIPESTATUS[0]}"
|
||||||
|
set -e
|
||||||
|
exec 3>&-
|
||||||
|
echo 'EOF' >> "${GITHUB_OUTPUT}"
|
||||||
|
RUN_SSH_OUTPUT="${stderr}"
|
||||||
|
return "${status}"
|
||||||
|
else
|
||||||
|
exec 3>&1
|
||||||
|
set +e
|
||||||
|
stderr="$(
|
||||||
|
{
|
||||||
|
"${TARGET}" "$@" 1>&3
|
||||||
|
} 2>&1 | tee /dev/stderr
|
||||||
|
)"
|
||||||
|
status="${PIPESTATUS[0]}"
|
||||||
|
set -e
|
||||||
|
exec 3>&-
|
||||||
|
RUN_SSH_OUTPUT="${stderr}"
|
||||||
|
return "${status}"
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
function run_ssh_command_with_retry() {
|
||||||
|
local retries="${INPUT_RETRY_ATTEMPTS:-0}"
|
||||||
|
local delay="${INPUT_RETRY_DELAY:-0}"
|
||||||
|
local max_attempts
|
||||||
|
local attempt=1
|
||||||
|
local status=0
|
||||||
|
RUN_SSH_OUTPUT=""
|
||||||
|
|
||||||
|
validate_non_negative_integer "retry_attempts" "${retries}"
|
||||||
|
|
||||||
|
max_attempts=$((retries + 1))
|
||||||
|
|
||||||
|
while true; do
|
||||||
|
if (( retries > 0 )); then
|
||||||
|
echo "SSH command attempt ${attempt}/${max_attempts}"
|
||||||
|
fi
|
||||||
|
|
||||||
|
run_ssh_command "$@"
|
||||||
|
status="$?"
|
||||||
|
|
||||||
|
if (( status == 0 )); then
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
if (( attempt >= max_attempts )); then
|
||||||
|
return "${status}"
|
||||||
|
fi
|
||||||
|
|
||||||
|
if ! is_retryable_connection_failure "${RUN_SSH_OUTPUT}"; then
|
||||||
|
return "${status}"
|
||||||
|
fi
|
||||||
|
|
||||||
|
attempt=$((attempt + 1))
|
||||||
|
|
||||||
|
if [[ "${delay}" != "0" && "${delay}" != "0s" ]]; then
|
||||||
|
sleep "${delay}"
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
}
|
||||||
|
|
||||||
function detect_client_info() {
|
function detect_client_info() {
|
||||||
CLIENT_PLATFORM="${SSH_CLIENT_OS:-$(uname -s | tr '[:upper:]' '[:lower:]')}"
|
CLIENT_PLATFORM="${SSH_CLIENT_OS:-$(uname -s | tr '[:upper:]' '[:lower:]')}"
|
||||||
CLIENT_ARCH="${SSH_CLIENT_ARCH:-$(uname -m)}"
|
CLIENT_ARCH="${SSH_CLIENT_ARCH:-$(uname -m)}"
|
||||||
@ -70,10 +168,4 @@ if ! "${TARGET}" --version; then
|
|||||||
log_error "Failed to execute ${TARGET} --version. The binary may be corrupted." "${ERR_VERSION_CHECK_FAILED}"
|
log_error "Failed to execute ${TARGET} --version. The binary may be corrupted." "${ERR_VERSION_CHECK_FAILED}"
|
||||||
fi
|
fi
|
||||||
echo "======================================="
|
echo "======================================="
|
||||||
if [[ "${INPUT_CAPTURE_STDOUT}" == 'true' ]]; then
|
run_ssh_command_with_retry "$@"
|
||||||
echo 'stdout<<EOF' >> "${GITHUB_OUTPUT}"
|
|
||||||
"${TARGET}" "$@" | tee -a "${GITHUB_OUTPUT}"
|
|
||||||
echo 'EOF' >> "${GITHUB_OUTPUT}"
|
|
||||||
else
|
|
||||||
"${TARGET}" "$@"
|
|
||||||
fi
|
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user