From 51f2474db67f1bfcf8cfb12a62b91a613f557d87 Mon Sep 17 00:00:00 2001 From: Damien Date: Thu, 30 Jul 2026 11:23:03 +0200 Subject: [PATCH] openbao,gitea-runner: source lib/common.sh, fix hardcoded Alpine template MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Both install scripts now source lib/common.sh instead of duplicating detect_latest_alpine_template(), find_existing_lxc(), and the tty1 autologin block. Since these scripts are distributed via curl one-liner and piped into `pct exec` inside the LXC, there is no local checkout to source from in those contexts — lib/common.sh is sourced from disk when a local checkout is available (BASH_SOURCE resolves to a real path), otherwise fetched over HTTP next to SCRIPT_URL. Both scripts already require outbound network to curl themselves and to download their respective binaries, so this adds no new failure mode. openbao/install.sh is a behavioral no-op: same log output, same control flow. One inline apk update/upgrade is intentionally left as-is in update_lxc() — refresh_os_packages() is a bash function in this process and can't run over `pct exec ... sh -c` without shipping the function definition into the container. gitea-runner/install.sh fixes the actual bug: TEMPLATE was hardcoded to a specific dated Alpine release, so create_lxc() would keep trying to provision a stale/absent template. TEMPLATE now defaults to empty and create_lxc() calls detect_latest_alpine_template() when unset, mirroring openbao. Also renamed HOSTNAME -> HOSTNAME_LXC and added LXC_TAG to match openbao's naming, since a follow-up (issue #15) will wire up find_existing_lxc()-based update detection here. Closes #12, Closes #14 --- gitea-runner/README.md | 3 +- gitea-runner/install.sh | 55 ++++++++++++++++------------ openbao/install.sh | 79 +++++++++++++++-------------------------- 3 files changed, 62 insertions(+), 75 deletions(-) diff --git a/gitea-runner/README.md b/gitea-runner/README.md index 4eaae73..2a2ee20 100644 --- a/gitea-runner/README.md +++ b/gitea-runner/README.md @@ -20,7 +20,7 @@ Single script, three automatic modes: bash -c "$(curl -fsSL https://gitea.arnodo.fr/Damien/infra-scripts/raw/branch/main/gitea-runner/install.sh)" ``` -The script automatically creates an Alpine 3.23 LXC with Docker and act_runner. +The script automatically creates an Alpine LXC (template auto-detected from `pveam available`) with Docker and act_runner. #### Customization @@ -34,6 +34,7 @@ CTID=120 HOSTNAME=runner-02 CORES=4 RAM=4096 bash -c "$(curl -fsSL https://gitea |----------|---------|-------------| | `CTID` | auto | Container ID | | `RUNNER_HOSTNAME` | `gitea-runner` | LXC Hostname | +| `TEMPLATE` | auto-detected | Alpine template; auto-detected from `pveam available` | | `CORES` | `2` | CPU cores | | `RAM` | `2048` | RAM in MiB | | `DISK` | `8` | Disk in GB | diff --git a/gitea-runner/install.sh b/gitea-runner/install.sh index d1a4b31..eb52004 100644 --- a/gitea-runner/install.sh +++ b/gitea-runner/install.sh @@ -8,14 +8,15 @@ set -euo pipefail # --- Config (override via environment) --- CTID="${CTID:-}" -HOSTNAME="${RUNNER_HOSTNAME:-gitea-runner}" -TEMPLATE="${TEMPLATE:-alpine-3.23-default_20260116_amd64.tar.xz}" +HOSTNAME_LXC="${RUNNER_HOSTNAME:-gitea-runner}" +TEMPLATE="${TEMPLATE:-}" # auto-detected when empty STORAGE="${STORAGE:-local-lvm}" TEMPLATE_STORAGE="${TEMPLATE_STORAGE:-local}" CORES="${CORES:-2}" RAM="${RAM:-2048}" DISK="${DISK:-8}" BRIDGE="${BRIDGE:-vmbr0}" +LXC_TAG="${LXC_TAG:-gitea-runner}" # stable identifier for the container SCRIPT_URL="https://gitea.arnodo.fr/Damien/infra-scripts/raw/branch/main/gitea-runner/install.sh" GITEA_HOSTNAME="${GITEA_HOSTNAME:-gitea.taila5ad8.ts.net}" GITEA_API="https://gitea.com/api/v1/repos/gitea/act_runner/releases" @@ -31,6 +32,25 @@ log_info() { echo -e "${GREEN}[INFO]${NC} $1"; } log_warn() { echo -e "${YELLOW}[WARN]${NC} $1"; } log_error() { echo -e "${RED}[ERROR]${NC} $1"; } +# ============================================================ +# Load shared helpers (lib/common.sh: detect_latest_alpine_template, +# enable_tty1_autologin, find_existing_lxc, refresh_os_packages). +# +# Same reasoning as openbao/install.sh: a local checkout has the file +# on disk right next to us, but the documented curl one-liner (host or +# piped into `pct exec` inside the LXC) has no BASH_SOURCE path worth +# trusting, so fall back to fetching lib/common.sh over HTTP next to +# SCRIPT_URL. The LXC already needs outbound network to curl this very +# script and to download the act_runner binary, so this adds no new +# failure mode. +# ============================================================ +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]:-.}")" 2>/dev/null && pwd || true)" +if [[ -n "$SCRIPT_DIR" && -f "${SCRIPT_DIR}/../lib/common.sh" ]]; then + source "${SCRIPT_DIR}/../lib/common.sh" +else + source <(curl -fsSL "$(dirname "$(dirname "$SCRIPT_URL")")/lib/common.sh") +fi + # --- Helpers --- get_latest_release() { local release @@ -73,6 +93,12 @@ download_runner() { create_lxc() { log_info "=== Gitea Act Runner — LXC Creation ===" + if [[ -z "$TEMPLATE" ]]; then + TEMPLATE=$(detect_latest_alpine_template) + else + log_info "Using user-provided template: $TEMPLATE" + fi + # Auto-select next CTID if not specified if [[ -z "$CTID" ]]; then CTID=$(pvesh get /cluster/resources --type vm --output-format json 2>/dev/null \ @@ -86,9 +112,9 @@ create_lxc() { pveam download "$TEMPLATE_STORAGE" "$TEMPLATE" fi - log_info "Creating LXC $CTID ($HOSTNAME)..." + log_info "Creating LXC $CTID ($HOSTNAME_LXC)..." pct create "$CTID" "${TEMPLATE_STORAGE}:vztmpl/${TEMPLATE}" \ - --hostname "$HOSTNAME" \ + --hostname "$HOSTNAME_LXC" \ --cores "$CORES" \ --memory "$RAM" \ --rootfs "${STORAGE}:${DISK}" \ @@ -122,7 +148,7 @@ EOF log_info "LXC $CTID created successfully!" log_info "=========================================" echo "" - echo " Hostname : $HOSTNAME" + echo " Hostname : $HOSTNAME_LXC" echo " IP : ${ip:-pending}" echo "" echo "Next steps:" @@ -232,24 +258,7 @@ LOGROTATE ln -sf /usr/sbin/logrotate /etc/periodic/daily/logrotate 2>/dev/null || true - log_info "Enabling console auto-login on tty1..." - # Alpine ships busybox getty by default; agetty (from util-linux) is what - # supports --autologin. - apk add --no-cache agetty >/dev/null 2>&1 || apk add --no-cache util-linux >/dev/null - - # Replace any existing tty1 entry, then append our autologin line. Doing it - # in two steps (delete + append) is more robust than an in-place sed against - # a pattern that may drift across Alpine releases. - sed -i '/^tty1::/d' /etc/inittab - echo 'tty1::respawn:/sbin/agetty --autologin root --noclear 38400 tty1' >> /etc/inittab - - # Tell PID 1 to re-read /etc/inittab so the change takes effect without a reboot. - kill -HUP 1 2>/dev/null || true - - # Kick any getty/agetty still attached to tty1 so init respawns it *now* with - # the new line — otherwise the first web-console session lands on the stale - # process and the operator has to type `exit` once before autologin kicks in. - pkill -KILL -f '(getty|agetty).*tty1' 2>/dev/null || true + enable_tty1_autologin log_info "Cleaning up..." rm -rf /var/cache/apk/* diff --git a/openbao/install.sh b/openbao/install.sh index c8be7f3..f5a62e1 100755 --- a/openbao/install.sh +++ b/openbao/install.sh @@ -56,6 +56,29 @@ log_info() { echo -e "${GREEN}[INFO]${NC} $1" >&2; } log_warn() { echo -e "${YELLOW}[WARN]${NC} $1" >&2; } log_error() { echo -e "${RED}[ERROR]${NC} $1" >&2; } +# ============================================================ +# Load shared helpers (lib/common.sh: detect_latest_alpine_template, +# enable_tty1_autologin, find_existing_lxc, refresh_os_packages). +# +# This script runs in three different contexts, only one of which has a +# real file on disk next to it: +# - local checkout (`bash openbao/install.sh`) -> lib/common.sh +# sits right there at ../lib/common.sh, source it straight from disk. +# - Proxmox host, documented one-liner (`bash -c "$(curl ... )"`) +# -> no checkout, no BASH_SOURCE path worth trusting. +# - inside the LXC (exec_in_lxc does `curl ... | pct exec ... bash -s --`) +# -> same story, script arrives on stdin. +# For the latter two we fetch lib/common.sh over HTTP, next to SCRIPT_URL. +# The LXC already needs outbound network to curl this very script and to +# download the bao binary, so this adds no new failure mode. +# ============================================================ +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]:-.}")" 2>/dev/null && pwd || true)" +if [[ -n "$SCRIPT_DIR" && -f "${SCRIPT_DIR}/../lib/common.sh" ]]; then + source "${SCRIPT_DIR}/../lib/common.sh" +else + source <(curl -fsSL "$(dirname "$(dirname "$SCRIPT_URL")")/lib/common.sh") +fi + # ============================================================ # Generic helpers # ============================================================ @@ -204,37 +227,6 @@ configure_tailscale_proxy() { # Proxmox-host helpers # ============================================================ -# Detect newest Alpine LXC template available from the Proxmox repos. -detect_latest_alpine_template() { - local tmpl - tmpl=$(pveam available --section system 2>/dev/null \ - | awk '/^system[[:space:]]+alpine-/ {print $2}' \ - | sort -V \ - | tail -n1) - - if [[ -z "$tmpl" ]]; then - log_warn "Could not query pveam; falling back to a known-good Alpine template." - tmpl="alpine-3.22-default_20250617_amd64.tar.xz" - fi - log_info "Selected Alpine template: $tmpl" - echo "$tmpl" -} - -# Find an existing LXC by tag or hostname. Echoes CTID, returns 1 if none. -find_existing_lxc() { - local id host tags - while read -r id _; do - [[ -z "$id" || "$id" == "VMID" ]] && continue - host=$(pct config "$id" 2>/dev/null | awk -F': ' '/^hostname:/ {print $2}' || true) - tags=$(pct config "$id" 2>/dev/null | awk -F': ' '/^tags:/ {print $2}' || true) - if [[ "$host" == "$HOSTNAME_LXC" ]] || [[ ",${tags//;/,}," == *",${LXC_TAG},"* ]]; then - echo "$id" - return 0 - fi - done < <(pct list | awk 'NR>1 {print $1}') - return 1 -} - ensure_template_present() { local tmpl="$1" if ! pveam list "$TEMPLATE_STORAGE" 2>/dev/null | grep -q "$tmpl"; then @@ -363,6 +355,9 @@ update_lxc() { fi log_info "Refreshing Alpine packages inside LXC ${ctid}..." + # refresh_os_packages() is a bash function local to this process; it can't + # run over `pct exec ... sh -c` without shipping the function definition + # into the container, so this call site stays inline rather than dedupe. pct exec "$ctid" -- sh -c "apk update >/dev/null && apk upgrade >/dev/null" log_info "Upgrading bao binary inside LXC ${ctid}..." @@ -474,24 +469,7 @@ EOF log_info "Starting openbao service..." rc-service openbao start || log_warn "openbao failed to start — inspect /var/log/openbao.log" - log_info "Enabling console auto-login on tty1..." - # Alpine ships busybox getty by default; agetty (from util-linux) is what - # supports --autologin. - apk add --no-cache agetty >/dev/null 2>&1 || apk add --no-cache util-linux >/dev/null - - # Replace any existing tty1 entry, then append our autologin line. Doing it - # in two steps (delete + append) is more robust than an in-place sed against - # a pattern that may drift across Alpine releases. - sed -i '/^tty1::/d' /etc/inittab - echo 'tty1::respawn:/sbin/agetty --autologin root --noclear 38400 tty1' >> /etc/inittab - - # Tell PID 1 to re-read /etc/inittab so the change takes effect without a reboot. - kill -HUP 1 2>/dev/null || true - - # Kick any getty/agetty still attached to tty1 so init respawns it *now* with - # the new line — otherwise the first web-console session lands on the stale - # process and the operator has to type `exit` once before autologin kicks in. - pkill -KILL -f '(getty|agetty).*tty1' 2>/dev/null || true + enable_tty1_autologin configure_tailscale_proxy @@ -563,8 +541,7 @@ MOTD # ============================================================ update_inside_lxc() { log_info "=== OpenBao — update ===" - apk update >/dev/null - apk upgrade >/dev/null + refresh_os_packages install_or_upgrade_bao configure_tailscale_proxy log_info "Update complete."