From dd3b1e8f4b79865cfcae04063b907f40c8607a49 Mon Sep 17 00:00:00 2001 From: MickLesk <47820557+MickLesk@users.noreply.github.com> Date: Thu, 12 Nov 2026 11:43:25 +0100 Subject: [PATCH] Fetch the install script before the container is created 26 runs a week failed on install/-install.sh only after their container stood ready, all with a fork hint. Some were retired ct/ scripts that build a container but have no installer any more (dockge, dokploy), some forks, the rest network errors whose reason never reached the log. Fetch it first, say whether it is missing (404) or unreachable and why, and keep a closed console from killing the fetch. --- .github/workflows/verify-split-files.yml | 5 ++ pve/backend.func | 53 ++++++++++------ tests/install-script-fetch.sh | 81 ++++++++++++++++++++++++ 3 files changed, 120 insertions(+), 19 deletions(-) create mode 100644 tests/install-script-fetch.sh diff --git a/.github/workflows/verify-split-files.yml b/.github/workflows/verify-split-files.yml index 85a3153..7b2f415 100644 --- a/.github/workflows/verify-split-files.yml +++ b/.github/workflows/verify-split-files.yml @@ -29,6 +29,7 @@ on: - "tests/mongodb-upgrade.sh" - "tests/uv-required-version.sh" - "tests/docker-lxcfs.sh" + - "tests/install-script-fetch.sh" - "tests/java-memory.sh" - "tests/sudo-env.sh" - "tests/deb822-local-keyring.sh" @@ -90,6 +91,7 @@ on: - "tests/mongodb-upgrade.sh" - "tests/uv-required-version.sh" - "tests/docker-lxcfs.sh" + - "tests/install-script-fetch.sh" - "tests/java-memory.sh" - "tests/sudo-env.sh" - "tests/deb822-local-keyring.sh" @@ -173,6 +175,9 @@ jobs: - name: Docker lxcfs visibility regression tests run: bash tests/docker-lxcfs.sh + - name: Install script fetch tests + run: bash tests/install-script-fetch.sh + - name: Java memory drop-in tests run: bash tests/java-memory.sh diff --git a/pve/backend.func b/pve/backend.func index da4bd73..701cd6f 100644 --- a/pve/backend.func +++ b/pve/backend.func @@ -218,6 +218,38 @@ _pve_ct_gone_during_setup() { _CS_SETUP_GONE_RC=199 } +# ------------------------------------------------------------------------------ +# _pve_fetch_install_script +# +# Before anything is created: a missing or unreachable install script turned up +# only once the container stood ready, with a fork hint whatever the cause. +# Leaves the script in _install_script, or says why not and returns 115. +# ------------------------------------------------------------------------------ +_pve_fetch_install_script() { + local rel="install/${var_install}.sh" base="${COMMUNITY_SCRIPTS_ROOT:-${COMMUNITY_SCRIPTS_URL}}" out err rc=0 why + out="$(mktemp)" err="$(mktemp)" + _cs_shield_hangup _cs_fetch_text "$rel" >"$out" 2>"$err" || rc=$? + _install_script="$(cat "$out")" + why="$(grep -v '^[[:space:]]*$' "$err" | tail -n 1)" || why="" + rm -f "$out" "$err" + ((rc == 0)) && [[ -n "$_install_script" ]] && return 0 + _install_script="" + if [[ "$why" == *404* ]]; then + msg_error "${rel} is not in ${base}" + msg_custom "💡" "${YW}" "If ${APP} was retired or became an addon, its ct/ script only updates existing containers:" + msg_custom " " "${YW}" "https://community-scripts.org/scripts/${NSAPP}" + if [[ -z "${COMMUNITY_SCRIPTS_ROOT:-}" ]]; then + msg_custom "💡" "${YW}" "Running a fork? A ct/ script fetched with curl cannot tell the engine" + msg_custom " " "${YW}" "where it came from, so set the scripts base explicitly:" + msg_custom " " "${YW}" "COMMUNITY_SCRIPTS_URL=https://raw.githubusercontent.com///" + fi + else + msg_error "Could not fetch ${rel} from ${base}" + [[ -n "$why" ]] && msg_custom "â„šī¸" "${YW}" "$why" + fi + return 115 +} + # ------------------------------------------------------------------------------ # _pve_wait_for_detached_install # @@ -759,6 +791,8 @@ $PCT_OPTIONS_STRING" check_storage_health "$CONTAINER_STORAGE" "$DISK_SIZE" fi + local _install_script="" + _pve_fetch_install_script || exit $? create_lxc_container || exit $? # Transition to 'configuring' — container created, now setting up OS/userland post_progress_to_api "configuring" @@ -1504,25 +1538,6 @@ PROFILE export CONTAINER_INSTALLING=true - local _install_script - # Fetched explicitly rather than letting the ERR trap report it. - # - # A missing install script is almost always one thing: the ct/ script came - # from a fork but COMMUNITY_SCRIPTS_URL was not set, so the engine looked - # here -- upstream -- and found nothing. The trap reported that as - # "exit code 22 (Command failed with exit status 22)", which says neither - # what was missing nor where it was looked for. - if ! _install_script="$(_cs_fetch_text "install/${var_install}.sh")" || - [[ -z "$_install_script" ]]; then - msg_error "Could not fetch install/${var_install}.sh" - msg_custom "📁" "${YW}" "Looked in: ${COMMUNITY_SCRIPTS_ROOT:-${COMMUNITY_SCRIPTS_URL}}" - if [[ -z "${COMMUNITY_SCRIPTS_ROOT:-}" ]]; then - msg_custom "💡" "${YW}" "Running a fork? A ct/ script fetched with curl cannot tell the engine" - msg_custom " " "${YW}" "where it came from, so set the scripts base explicitly:" - msg_custom " " "${YW}" "COMMUNITY_SCRIPTS_URL=https://raw.githubusercontent.com///" - fi - exit 115 - fi # The install owns the terminal now; a block left open on the host would resume its spinner over it. stop_spinner _MSG_BLOCKS=() diff --git a/tests/install-script-fetch.sh b/tests/install-script-fetch.sh new file mode 100644 index 0000000..9b301de --- /dev/null +++ b/tests/install-script-fetch.sh @@ -0,0 +1,81 @@ +#!/usr/bin/env bash +# 26 runs a week failed on install/-install.sh only after their container +# stood ready, all with a fork hint: retired scripts (dockge, dokploy), forks, +# and network errors whose reason never reached the log. +set -uo pipefail + +CORE_DIR="$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")/.." && pwd)" +BACKEND="$CORE_DIR/pve/backend.func" +eval "$(awk '/^_cs_shield_hangup[(][)] [{]/,/^}/' "$CORE_DIR/core/core.func")" +eval "$(awk '/^_pve_fetch_install_script[(][)] [{]/,/^}/' "$BACKEND")" + +TEST_DIR="$(mktemp -d)" +trap 'rm -rf -- "$TEST_DIR"' EXIT +MSG="$TEST_DIR/msg" +passed=0 failed=0 + +expect() { + if [[ "$2" == "$3" ]]; then + echo "PASS $1" + passed=$((passed + 1)) + else + echo "FAIL $1: expected <$2>, got <$3>" >&2 + failed=$((failed + 1)) + fi +} + +msg_error() { echo "ERROR $1" >>"$MSG"; } +msg_custom() { echo "$3" >>"$MSG"; } +YW="" APP="Dockge" NSAPP="dockge" var_install="dockge-install" +COMMUNITY_SCRIPTS_URL="https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main" +unset COMMUNITY_SCRIPTS_ROOT + +fetch() { + : >"$MSG" + _install_script="stale" + _pve_fetch_install_script + echo "rc=$?" +} + +_cs_fetch_text() { printf '#!/usr/bin/env bash\necho installing\n'; } +expect "a fetched script is kept" "rc=0" "$(fetch)" +fetch >/dev/null +expect "with its content" "echo installing" "$(tail -n1 <<<"$_install_script")" + +_cs_fetch_text() { echo "curl: (22) The requested URL returned error: 404" >&2; return 22; } +expect "a missing script stops with 115" "rc=115" "$(fetch)" +fetch >/dev/null +expect "it says where it was not found" "ERROR install/dockge-install.sh is not in https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main" "$(head -n1 "$MSG")" +expect "it names a retired script as one cause" 1 "$(grep -c 'retired or became an addon' "$MSG")" +expect "and a fork as the other" 1 "$(grep -c 'Running a fork' "$MSG")" +expect "nothing is left to run" "" "$_install_script" + +_cs_fetch_text() { echo "curl: (6) Could not resolve host: raw.githubusercontent.com" >&2; return 6; } +fetch >/dev/null +expect "a network failure says so" "ERROR Could not fetch install/dockge-install.sh from https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main" "$(head -n1 "$MSG")" +expect "with curl's reason" "curl: (6) Could not resolve host: raw.githubusercontent.com" "$(sed -n 2p "$MSG")" +expect "and no fork hint" 0 "$(grep -c 'fork' "$MSG" || true)" + +_cs_fetch_text() { printf '#!/usr/bin/env bash\napt-get ins'; echo "curl: (18) transfer closed with 1024 bytes remaining" >&2; return 18; } +fetch >/dev/null +expect "a cut-off script is never run" "" "$_install_script" + +_cs_fetch_text() { return 0; } +expect "an empty answer counts as missing" "rc=115" "$(fetch)" + +rc="$( + set -eEo pipefail + trap 'echo ERR' ERR + _cs_fetch_text() { return 7; } + _pve_fetch_install_script >/dev/null || true + echo "rc=done" +)" +expect "no curl output at all trips neither errexit nor the ERR trap" "rc=done" "$rc" + +fetch_line=$(grep -n '_pve_fetch_install_script || exit' "$BACKEND" | cut -d: -f1) +create_line=$(grep -n ' create_lxc_container || exit' "$BACKEND" | cut -d: -f1) +expect "the script is fetched before the container is created" yes "$([[ -n "$fetch_line" && -n "$create_line" ]] && ((fetch_line < create_line)) && echo yes || echo no)" +expect "and not fetched a second time before the install" 1 "$(grep -c '_cs_fetch_text "install/${var_install}.sh"' "$BACKEND")" + +printf '%s passed, %s failed\n' "$passed" "$failed" +((failed == 0))