新增 act_runner 自动升级脚本并同步最新版本探测
Ubuntu System Information / show-system-info (push) Waiting to run Details

This commit is contained in:
csh 2026-05-07 12:08:46 +08:00
parent 1a52ee08a2
commit 9d4c4a1f73
8 changed files with 425 additions and 7 deletions

View File

@ -29,6 +29,7 @@ docker-runner/
│ ├── check_crlf.sh # Windows 换行符检查工具 │ ├── check_crlf.sh # Windows 换行符检查工具
│ ├── entrypoint.sh # 容器启动脚本 │ ├── entrypoint.sh # 容器启动脚本
│ ├── setup.sh # Runner 安装脚本 │ ├── setup.sh # Runner 安装脚本
│ ├── upgrade.sh # Runner 升级脚本
│ ├── register.sh # Runner 注册脚本 │ ├── register.sh # Runner 注册脚本
│ └── manage.sh # Runner 管理脚本 │ └── manage.sh # Runner 管理脚本
@ -128,7 +129,7 @@ docker-compose up -d
docker-compose exec gitea-runner /data/setup.sh docker-compose exec gitea-runner /data/setup.sh
``` ```
按照提示选择版本(默认 0.2.13)和架构(自动检测) 脚本会自动探测最新 `act_runner` 版本作为默认值,然后提示你确认架构
#### 5. 注册 Runner #### 5. 注册 Runner
@ -250,6 +251,21 @@ docker-compose exec gitea-runner /data/manage.sh restart <runner-name>
docker-compose exec gitea-runner /data/manage.sh delete <runner-name> docker-compose exec gitea-runner /data/manage.sh delete <runner-name>
``` ```
### 升级 act_runner
```bash
# 自动检查最新稳定版本,下载后重启所有已注册 runners
docker-compose exec gitea-runner /data/upgrade.sh
# 指定版本
docker-compose exec gitea-runner /data/upgrade.sh --version 0.6.1
# 仅重启指定 runner
docker-compose exec gitea-runner /data/upgrade.sh --runner <runner-name>
```
`upgrade.sh` 只替换 `/data/bin/act_runner` 并重启 runner 进程,不会重新注册,也不需要重新输入 token。
### 添加多个 Runners ### 添加多个 Runners
```bash ```bash
@ -389,8 +405,14 @@ CMD ["/app"]
### Q1: 如何更新 Runner 版本? ### Q1: 如何更新 Runner 版本?
```bash ```bash
docker-compose exec gitea-runner /data/setup.sh docker-compose exec gitea-runner /data/upgrade.sh
# 脚本会检测到已安装并询问是否升级 # 脚本会自动从官方目录页检测最新 act_runner 版本
```
如需固定版本:
```bash
docker-compose exec gitea-runner /data/upgrade.sh --version 0.6.1
``` ```
### Q2: 如何添加多个 Runners? ### Q2: 如何添加多个 Runners?

View File

@ -15,6 +15,7 @@ NC='\033[0m'
SCRIPT_FILES=( SCRIPT_FILES=(
"entrypoint.sh" "entrypoint.sh"
"setup.sh" "setup.sh"
"upgrade.sh"
"register.sh" "register.sh"
"manage.sh" "manage.sh"
) )

View File

@ -1,5 +1,15 @@
#!/bin/bash #!/bin/bash
# 共享升级脚本中的版本探测逻辑
SCRIPT_DIR=$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)
UPGRADE_HELPER="${SCRIPT_DIR}/upgrade.sh"
DEFAULT_RUNNER_VERSION_FALLBACK="${DEFAULT_RUNNER_VERSION_FALLBACK:-0.2.13}"
if [ -f "${UPGRADE_HELPER}" ]; then
# shellcheck source=/dev/null
source "${UPGRADE_HELPER}"
fi
echo "==========================================" echo "=========================================="
echo " Gitea Runner Installation Script " echo " Gitea Runner Installation Script "
echo "==========================================" echo "=========================================="
@ -30,8 +40,20 @@ fi
# 获取要安装的版本 # 获取要安装的版本
echo "Available versions: https://dl.gitea.com/act_runner/" echo "Available versions: https://dl.gitea.com/act_runner/"
echo "" echo ""
read -p "Enter version to install (default: 0.2.13): " RUNNER_VERSION
RUNNER_VERSION=${RUNNER_VERSION:-0.2.13} DEFAULT_RUNNER_VERSION="${DEFAULT_RUNNER_VERSION_FALLBACK}"
if declare -F resolve_latest_version_or_fallback >/dev/null 2>&1; then
DEFAULT_RUNNER_VERSION=$(resolve_latest_version_or_fallback "${DEFAULT_RUNNER_VERSION_FALLBACK}")
fi
echo "Default install version: ${DEFAULT_RUNNER_VERSION}"
read -p "Enter version to install (default: ${DEFAULT_RUNNER_VERSION}): " RUNNER_VERSION
RUNNER_VERSION=${RUNNER_VERSION:-$DEFAULT_RUNNER_VERSION}
if declare -F validate_version >/dev/null 2>&1 && ! validate_version "${RUNNER_VERSION}"; then
echo "✗ Invalid version format: ${RUNNER_VERSION}"
exit 1
fi
ARCH=$(uname -m) ARCH=$(uname -m)
case "$ARCH" in case "$ARCH" in
@ -114,8 +136,7 @@ if curl -L "$DOWNLOAD_URL" -o "$INSTALL_PATH"; then
echo "1. Register the runner:" echo "1. Register the runner:"
echo " docker-compose exec gitea-runner /data/register.sh" echo " docker-compose exec gitea-runner /data/register.sh"
echo "" echo ""
echo "2. Restart the container:" echo "2. Start using the runner after registration."
echo " docker-compose restart"
echo "" echo ""
echo "Note: act_runner is saved in persistent storage" echo "Note: act_runner is saved in persistent storage"
echo " and will survive container restarts." echo " and will survive container restarts."

View File

@ -0,0 +1,284 @@
#!/bin/bash
INSTALL_PATH="/data/bin/act_runner"
SYSTEM_PATH="/usr/local/bin/act_runner"
RUNNER_INDEX_URL="${RUNNER_INDEX_URL:-https://dl.gitea.com/act_runner/}"
print_usage() {
cat <<'EOF'
Usage: /data/upgrade.sh [options]
Upgrade act_runner without re-registering existing runners.
Options:
--version <version> Upgrade to a specific version instead of auto-detecting latest
--arch <arch> Override detected architecture: amd64, arm64, arm-7
--runner <name> Restart only the specified runner (can be used multiple times)
-y, --yes Skip confirmation prompt
-h, --help Show this help message
EOF
}
log() {
echo "$@"
}
fail() {
echo "$*" >&2
exit 1
}
extract_version_number() {
sed -nE 's/.*version[[:space:]]+([0-9]+\.[0-9]+\.[0-9]+).*/\1/p' | head -n 1
}
extract_versions_from_listing() {
grep -oE '/act_runner/[0-9]+\.[0-9]+\.[0-9]+/' | \
sed -E 's#^/act_runner/([0-9]+\.[0-9]+\.[0-9]+)/$#\1#' | \
awk '!seen[$0]++'
}
pick_latest_version() {
awk 'NF' | sort -V | tail -n 1
}
fetch_latest_version() {
curl -fsSL "${RUNNER_INDEX_URL}" | extract_versions_from_listing | pick_latest_version
}
resolve_latest_version_or_fallback() {
local fallback_version=$1
local fetcher_cmd=${2:-fetch_latest_version}
local detected_version=""
if command -v "${fetcher_cmd}" >/dev/null 2>&1; then
detected_version=$("${fetcher_cmd}" 2>/dev/null || true)
fi
if [ -n "${detected_version}" ] && validate_version "${detected_version}"; then
echo "${detected_version}"
else
echo "${fallback_version}"
fi
}
validate_version() {
local version=$1
[[ "${version}" =~ ^[0-9]+\.[0-9]+\.[0-9]+$ ]]
}
detect_runner_arch() {
local arch
arch=$(uname -m)
case "${arch}" in
x86_64)
echo "amd64"
;;
aarch64|arm64)
echo "arm64"
;;
armv7l)
echo "arm-7"
;;
*)
fail "Unsupported architecture: ${arch}"
;;
esac
}
get_installed_version() {
local output=""
if [ -x "${INSTALL_PATH}" ]; then
output=$("${INSTALL_PATH}" --version 2>/dev/null || true)
elif command -v act_runner >/dev/null 2>&1; then
output=$(act_runner --version 2>/dev/null || true)
fi
if [ -n "${output}" ]; then
extract_version_number <<<"${output}"
fi
}
list_registered_runners() {
local runner_dir
if [ ! -d "/data/runners" ]; then
return 0
fi
for runner_dir in /data/runners/*/; do
if [ -d "${runner_dir}" ] && [ -f "${runner_dir}/.runner" ]; then
basename "${runner_dir}"
fi
done
}
build_download_url() {
local version=$1
local arch=$2
echo "https://dl.gitea.com/act_runner/${version}/act_runner-${version}-linux-${arch}"
}
confirm_or_exit() {
local auto_confirm=$1
if [ "${auto_confirm}" = "true" ]; then
return 0
fi
read -p "Proceed with upgrade? (y/N): " -n 1 -r
echo
if [[ ! "${REPLY}" =~ ^[Yy]$ ]]; then
echo "Upgrade cancelled."
exit 0
fi
}
download_and_install() {
local version=$1
local arch=$2
local url
local tmp_path
local downloaded_version
mkdir -p "$(dirname "${INSTALL_PATH}")"
url=$(build_download_url "${version}" "${arch}")
tmp_path=$(mktemp "${INSTALL_PATH}.tmp.XXXXXX")
trap 'rm -f "${tmp_path}"' EXIT
log "Downloading ${url}"
curl -fL "${url}" -o "${tmp_path}"
chmod +x "${tmp_path}"
downloaded_version=$("${tmp_path}" --version 2>/dev/null | extract_version_number)
if [ "${downloaded_version}" != "${version}" ]; then
fail "Downloaded binary version mismatch: expected ${version}, got ${downloaded_version:-unknown}"
fi
mv "${tmp_path}" "${INSTALL_PATH}"
ln -sf "${INSTALL_PATH}" "${SYSTEM_PATH}"
trap - EXIT
}
restart_runner_process() {
local runner_name=$1
if ! command -v supervisorctl >/dev/null 2>&1; then
log "⚠ supervisorctl not found, please restart the container manually."
return 1
fi
log "Restarting runner '${runner_name}'..."
supervisorctl restart "runner-${runner_name}"
}
restart_selected_runners() {
local requested_runners=("$@")
local runner_names=()
local runner_name
if [ "${#requested_runners[@]}" -gt 0 ]; then
runner_names=("${requested_runners[@]}")
else
while IFS= read -r runner_name; do
[ -n "${runner_name}" ] && runner_names+=("${runner_name}")
done < <(list_registered_runners)
fi
if [ "${#runner_names[@]}" -eq 0 ]; then
log "No registered runners found. Binary upgraded, no process restart needed."
return 0
fi
for runner_name in "${runner_names[@]}"; do
restart_runner_process "${runner_name}"
done
}
main() {
local target_version=""
local target_arch=""
local auto_confirm="false"
local current_version=""
local runner_names=()
while [ $# -gt 0 ]; do
case "$1" in
--version)
[ $# -ge 2 ] || fail "--version requires a value"
target_version=$2
shift 2
;;
--arch)
[ $# -ge 2 ] || fail "--arch requires a value"
target_arch=$2
shift 2
;;
--runner)
[ $# -ge 2 ] || fail "--runner requires a value"
runner_names+=("$2")
shift 2
;;
-y|--yes)
auto_confirm="true"
shift
;;
-h|--help)
print_usage
exit 0
;;
*)
fail "Unknown option: $1"
;;
esac
done
current_version=$(get_installed_version || true)
if [ -z "${target_version}" ]; then
log "Fetching latest act_runner version from ${RUNNER_INDEX_URL}"
target_version=$(fetch_latest_version)
[ -n "${target_version}" ] || fail "Unable to detect the latest act_runner version"
fi
validate_version "${target_version}" || fail "Invalid version: ${target_version}"
if [ -z "${target_arch}" ]; then
target_arch=$(detect_runner_arch)
fi
if [ -n "${current_version}" ] && [ "${current_version}" = "${target_version}" ]; then
log "act_runner is already at the latest version: ${current_version}"
exit 0
fi
log "Upgrade summary:"
log " Current version: ${current_version:-not installed}"
log " Target version: ${target_version}"
log " Architecture: ${target_arch}"
log " Install path: ${INSTALL_PATH}"
if [ "${#runner_names[@]}" -gt 0 ]; then
log " Restart target: ${runner_names[*]}"
else
log " Restart target: all registered runners"
fi
echo
confirm_or_exit "${auto_confirm}"
download_and_install "${target_version}" "${target_arch}"
restart_selected_runners "${runner_names[@]}"
log
log "✓ act_runner upgraded successfully"
log " Installed version: $(get_installed_version || echo unknown)"
}
if [[ "${BASH_SOURCE[0]}" == "${0}" ]]; then
set -euo pipefail
main "$@"
fi

View File

@ -8,6 +8,7 @@ services:
volumes: volumes:
- ./runner-data:/data - ./runner-data:/data
- ../../common/setup.sh:/data/setup.sh:ro - ../../common/setup.sh:/data/setup.sh:ro
- ../../common/upgrade.sh:/data/upgrade.sh:ro
- ../../common/register.sh:/data/register.sh:ro - ../../common/register.sh:/data/register.sh:ro
- ../../common/manage.sh:/data/manage.sh:ro - ../../common/manage.sh:/data/manage.sh:ro
- ../../common/check_crlf.sh:/data/check_crlf.sh:ro - ../../common/check_crlf.sh:/data/check_crlf.sh:ro

View File

@ -8,6 +8,7 @@ services:
volumes: volumes:
- ./runner-data:/data - ./runner-data:/data
- ../../common/setup.sh:/data/setup.sh:ro - ../../common/setup.sh:/data/setup.sh:ro
- ../../common/upgrade.sh:/data/upgrade.sh:ro
- ../../common/register.sh:/data/register.sh:ro - ../../common/register.sh:/data/register.sh:ro
- ../../common/manage.sh:/data/manage.sh:ro - ../../common/manage.sh:/data/manage.sh:ro
- ../../common/check_crlf.sh:/data/check_crlf.sh:ro - ../../common/check_crlf.sh:/data/check_crlf.sh:ro

View File

@ -7,6 +7,7 @@ services:
volumes: volumes:
- ./runner-data:/data - ./runner-data:/data
- ../../common/setup.sh:/data/setup.sh:ro - ../../common/setup.sh:/data/setup.sh:ro
- ../../common/upgrade.sh:/data/upgrade.sh:ro
- ../../common/register.sh:/data/register.sh:ro - ../../common/register.sh:/data/register.sh:ro
- ../../common/manage.sh:/data/manage.sh:ro - ../../common/manage.sh:/data/manage.sh:ro
- ../../common/entrypoint.sh:/data/entrypoint.sh:ro - ../../common/entrypoint.sh:/data/entrypoint.sh:ro

87
tests/upgrade_test.sh Normal file
View File

@ -0,0 +1,87 @@
#!/bin/bash
set -euo pipefail
SCRIPT_DIR=$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)
REPO_ROOT=$(cd "${SCRIPT_DIR}/.." && pwd)
# shellcheck source=/dev/null
source "${REPO_ROOT}/docker-runner/common/upgrade.sh"
assert_eq() {
local expected=$1
local actual=$2
local message=$3
if [ "${expected}" != "${actual}" ]; then
echo "FAIL: ${message}" >&2
echo " expected: ${expected}" >&2
echo " actual: ${actual}" >&2
exit 1
fi
}
test_extract_versions_from_listing() {
local html
html=$(cat <<'EOF'
<tr class="file ">
<td class="name"><a href="/act_runner/0.6.1/"><span class="name">0.6.1</span></a></td>
</tr>
<tr class="file ">
<td class="name"><a href="/act_runner/0.5.0/"><span class="name">0.5.0</span></a></td>
</tr>
<tr class="file ">
<td class="name"><a href="/act_runner/0.4.1/"><span class="name">0.4.1</span></a></td>
</tr>
EOF
)
local actual
actual=$(extract_versions_from_listing <<<"${html}")
assert_eq $'0.6.1\n0.5.0\n0.4.1' "${actual}" \
"extract_versions_from_listing should return every version in listing order"
}
test_pick_latest_version() {
local actual
actual=$(pick_latest_version <<'EOF'
0.5.0
0.6.1
0.4.9
EOF
)
assert_eq "0.6.1" "${actual}" \
"pick_latest_version should return the highest semantic version"
}
fake_latest_version() {
echo "0.6.2"
}
fake_invalid_version() {
echo "latest"
}
test_resolve_latest_version_or_fallback_prefers_latest() {
local actual
actual=$(resolve_latest_version_or_fallback "0.2.13" fake_latest_version)
assert_eq "0.6.2" "${actual}" \
"resolve_latest_version_or_fallback should prefer the detected latest version"
}
test_resolve_latest_version_or_fallback_uses_fallback() {
local actual
actual=$(resolve_latest_version_or_fallback "0.2.13" fake_invalid_version)
assert_eq "0.2.13" "${actual}" \
"resolve_latest_version_or_fallback should fall back when detected version is invalid"
}
test_extract_versions_from_listing
test_pick_latest_version
test_resolve_latest_version_or_fallback_prefers_latest
test_resolve_latest_version_or_fallback_uses_fallback
echo "upgrade_test.sh: PASS"