diff --git a/.github/workflows/build-engine.yml b/.github/workflows/build-engine.yml index 0b4e065..5a45b98 100644 --- a/.github/workflows/build-engine.yml +++ b/.github/workflows/build-engine.yml @@ -4,13 +4,13 @@ on: workflow_dispatch: inputs: telemt_commit: - description: 'Telemt commit hash to build from' + description: 'Full Telemt commit SHA to build from' required: true - default: 'e3f62db' + default: '9d5b896bb695c55e2905b82f276da84e45a0a2ec' version_tag: description: 'Version tag (e.g. 3.4.8-10c9bcd)' required: true - default: '3.5.9-e3f62db' + default: '3.5.14-9d5b896bb695c55e2905b82f276da84e45a0a2ec' env: REGISTRY: ghcr.io @@ -46,9 +46,9 @@ jobs: RUN apt-get update && apt-get install -y --no-install-recommends git && rm -rf /var/lib/apt/lists/* RUN git clone "https://github.com/${TELEMT_REPO}.git" /build WORKDIR /build - RUN git checkout "${TELEMT_COMMIT}" + RUN git checkout --detach "${TELEMT_COMMIT}" && test "$(git rev-parse HEAD)" = "${TELEMT_COMMIT}" ENV CARGO_PROFILE_RELEASE_LTO=true CARGO_PROFILE_RELEASE_CODEGEN_UNITS=1 CARGO_PROFILE_RELEASE_DEBUG=false - RUN cargo build --release && strip target/release/telemt 2>/dev/null || true && cp target/release/telemt /telemt + RUN cargo build --release --locked && (strip target/release/telemt 2>/dev/null || true) && cp target/release/telemt /telemt FROM debian:bookworm-slim RUN apt-get update && apt-get install -y --no-install-recommends ca-certificates && rm -rf /var/lib/apt/lists/* COPY --from=builder /telemt /usr/local/bin/telemt diff --git a/mtproxymax.sh b/mtproxymax.sh index 8b28912..957eb45 100644 --- a/mtproxymax.sh +++ b/mtproxymax.sh @@ -41,8 +41,8 @@ RUNLEVELS_DIR="${RUNLEVELS_DIR:-/etc/runlevels}" SCANNER_SHIELD_SET="mtp_scanners" CONTAINER_NAME="mtproxymax" DOCKER_IMAGE_BASE="mtproxymax-telemt" -TELEMT_MIN_VERSION="3.5.9" -TELEMT_COMMIT="e3f62db" # Pinned: v3.5.9 — Kostenfaktor: Trusted Helper Argv0 for multi-call firewall binaries & ME lifecycle +TELEMT_MIN_VERSION="3.5.14" +TELEMT_COMMIT="9d5b896bb695c55e2905b82f276da84e45a0a2ec" # Pinned: v3.5.14 — autonomous, lifecycle-bounded ME pool convergence GITHUB_REPO="SamNet-dev/MTProxyMax" REGISTRY_IMAGE="ghcr.io/samnet-dev/mtproxymax-telemt" @@ -1150,7 +1150,7 @@ wait_for_docker() { return 1 } -# Build telemt Docker image from latest GitHub release binary +# Obtain the pinned telemt image, or build the exact commit from source build_telemt_image() { local force="${1:-false}" @@ -1158,14 +1158,14 @@ build_telemt_image() { local version="${TELEMT_MIN_VERSION}-${commit}" # Skip if image already exists (unless forced) - if [ "$force" != "true" ] && docker image inspect "${DOCKER_IMAGE_BASE}:${version}" &>/dev/null; then + if [ "$force" = "false" ] && docker image inspect "${DOCKER_IMAGE_BASE}:${version}" &>/dev/null; then return 0 fi # Strategy 1: Pull pre-built image from registry (fast — seconds) log_info "Pulling pre-built telemt v${version}..." - if docker pull "${REGISTRY_IMAGE}:${version}" 2>/dev/null; then - docker tag "${REGISTRY_IMAGE}:${version}" "${DOCKER_IMAGE_BASE}:${version}" + if [ "$force" != "source" ] && docker pull "${REGISTRY_IMAGE}:${version}" 2>/dev/null; then + docker tag "${REGISTRY_IMAGE}:${version}" "${DOCKER_IMAGE_BASE}:${version}" || return 1 docker tag "${DOCKER_IMAGE_BASE}:${version}" "${DOCKER_IMAGE_BASE}:latest" 2>/dev/null || true log_success "Pulled telemt v${version}" mkdir -p "$INSTALL_DIR" @@ -1173,20 +1173,8 @@ build_telemt_image() { return 0 fi - # Strategy 2: Pull latest from registry if exact version not found - if [ "$force" != "source" ]; then - log_info "Exact version not in registry, trying latest..." - if docker pull "${REGISTRY_IMAGE}:latest" 2>/dev/null; then - docker tag "${REGISTRY_IMAGE}:latest" "${DOCKER_IMAGE_BASE}:${version}" - docker tag "${DOCKER_IMAGE_BASE}:${version}" "${DOCKER_IMAGE_BASE}:latest" 2>/dev/null || true - log_success "Pulled telemt (latest)" - mkdir -p "$INSTALL_DIR" - echo "$version" > "${INSTALL_DIR}/.telemt_version" - return 0 - fi - fi - - # Strategy 3: Build from source (slow first time, cached after) + # Never relabel registry :latest as the requested version. + # If the exact tag is unavailable (or source was requested), build the pin. log_warn "Pre-built image not available, compiling from source..." log_info "Includes: Prometheus metrics, ME perf fixes, critical ME bug fixes" @@ -1201,10 +1189,10 @@ RUN apt-get update && apt-get install -y --no-install-recommends git && \ rm -rf /var/lib/apt/lists/* RUN git clone "https://github.com/telemt/telemt.git" /build WORKDIR /build -RUN git checkout "${TELEMT_COMMIT}" +RUN git checkout --detach "${TELEMT_COMMIT}" && test "$(git rev-parse HEAD)" = "${TELEMT_COMMIT}" ENV CARGO_PROFILE_RELEASE_LTO=true CARGO_PROFILE_RELEASE_CODEGEN_UNITS=1 CARGO_PROFILE_RELEASE_DEBUG=false -RUN cargo build --release && \ - strip target/release/telemt 2>/dev/null || true && \ +RUN cargo build --release --locked && \ + (strip target/release/telemt 2>/dev/null || true) && \ cp target/release/telemt /telemt FROM debian:bookworm-slim diff --git a/tests/test_engine_image.sh b/tests/test_engine_image.sh new file mode 100644 index 0000000..72982fa --- /dev/null +++ b/tests/test_engine_image.sh @@ -0,0 +1,94 @@ +#!/bin/bash +# Exercise image acquisition without Docker, networking, or sourcing the manager. +set -euo pipefail +repo="${1:-$(cd "$(dirname "$0")/.." && pwd)}" +tmp=$(mktemp -d) +trap 'rm -rf "$tmp"' EXIT +eval "$(sed -n '/^build_telemt_image() {$/,/^# Get installed telemt version/p' "$repo/mtproxymax.sh" | sed '$d')" +eval "$(sed -n '/^TELEMT_MIN_VERSION=/p; /^TELEMT_COMMIT=/p; /^DOCKER_IMAGE_BASE=/p; /^REGISTRY_IMAGE=/p' "$repo/mtproxymax.sh")" +version="${TELEMT_MIN_VERSION}-${TELEMT_COMMIT}" +INSTALL_DIR="$tmp/install" +declare -a _TEMP_FILES=() +log_info() { :; } +log_warn() { :; } +log_success() { :; } +log_error() { :; } +docker() { + printf '%s\n' "$*" >> "$tmp/calls" + case "$1" in + image) return "$cached" ;; + pull) return "$pull_result" ;; + tag) return "$tag_result" ;; + build) + local build_context="${!#}" + cp "$build_context/Dockerfile" "$tmp/Dockerfile" + return "$build_result" ;; + *) echo "Unexpected Docker operation: $*" >&2; return 99 ;; + esac +} +reset_case() { + cached=1 pull_result=1 tag_result=0 build_result=0 + mkdir -p "$INSTALL_DIR" + printf 'previous\n' > "$INSTALL_DIR/.telemt_version" + : > "$tmp/calls" +} +assert_marker() { test "$(cat "$INSTALL_DIR/.telemt_version")" = "$1"; } +assert_no_latest_pull() { ! grep -q "pull ${REGISTRY_IMAGE}:latest" "$tmp/calls"; } + +reset_case +cached=0 +build_telemt_image +test "$(wc -l < "$tmp/calls")" -eq 1 +assert_marker previous + +reset_case +pull_result=0 +build_telemt_image +grep -q "pull ${REGISTRY_IMAGE}:${version}" "$tmp/calls" +! grep -q '^build ' "$tmp/calls" +assert_marker "$version" + +reset_case +build_telemt_image +assert_no_latest_pull +grep -q "^build --build-arg TELEMT_COMMIT=${TELEMT_COMMIT} " "$tmp/calls" +assert_marker "$version" +grep -Fq 'RUN git checkout --detach "${TELEMT_COMMIT}" && test "$(git rev-parse HEAD)" = "${TELEMT_COMMIT}"' "$tmp/Dockerfile" +grep -Fq 'cargo build --release --locked' "$tmp/Dockerfile" + +reset_case +cached=0 pull_result=0 +build_telemt_image source +! grep -Eq '^(image|pull) ' "$tmp/calls" +grep -q '^build ' "$tmp/calls" +assert_marker "$version" + +reset_case +cached=0 pull_result=0 +build_telemt_image true +! grep -q '^image ' "$tmp/calls" +assert_marker "$version" + +reset_case +build_result=1 +if build_telemt_image; then echo 'FAIL: build failure accepted'; exit 1; fi +assert_marker previous +! grep -q '^tag ' "$tmp/calls" +assert_no_latest_pull + +reset_case +pull_result=0 tag_result=1 +if build_telemt_image; then echo 'FAIL: tag failure accepted'; exit 1; fi +assert_marker previous + +# Execute the generated build command with failed cargo and a stale output file. +# A failed compile must never reach strip/cp, even if an old binary exists. +command_text=$(sed -n '/^RUN cargo build/,/^ cp /p' "$tmp/Dockerfile") +command_text=${command_text#RUN } +cargo() { return 1; } +strip() { touch "$tmp/stripped"; } +cp() { touch "$tmp/copied"; } +if eval "$command_text"; then echo 'FAIL: cargo failure masked'; exit 1; fi +test ! -e "$tmp/stripped" +test ! -e "$tmp/copied" +echo 'PASS: engine cache, exact pull, pinned fallback, source/force, build/tag failures, cargo failure propagation'