actions-template/docker-runner/common/upgrade.sh

285 lines
7.2 KiB
Bash

#!/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:]]+v?([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