feat(build): opt-in PKG_CACHE build arg for the internal package cache
Adds scripts/pkg-cache-repos.sh and wires it into Dockerfile, Dockerfile.fpm, Dockerfile.shared-httpd and Dockerfile.shared-ols. With --build-arg PKG_CACHE=http://192.168.1.40 (CT130 pkg-cache), dnf/apt go through the LAN cache. That cache fetches from the Remi/EPEL/AlmaLinux/ Ubuntu/LiteSpeed origins over HTTPS, with fallback tiers and stale-on-error. Without the arg the script is a no-op and builds are unchanged. - rpm: each baseurl becomes "<cache>/<prefix>/... <origin>/...", so dnf fails over to the origin if the cache dies mid-build - every RUN restores the original repo files before it ends, so no shipped layer carries the LAN address - EPEL/Remi release RPMs come from the cache when it is set - the php.net version gates are untouched Dockerfile.litespeed/.lsphp are not wired yet (see docs/pkg-cache.md). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
fa7808897f
commit
b2bec5b3ee
6 files changed
+311
-11
No files matched your search
Executable
+136
@@ -0,0 +1,136 @@
|
||||
#!/bin/bash
|
||||
## pkg-cache-repos.sh — BUILD-TIME ONLY, OPT-IN. Points dnf/apt at the internal
|
||||
## package cache (nginx proxy_cache, CT130 "pkg-cache" on the Proxmox cluster)
|
||||
## when PKG_CACHE is set; a no-op otherwise, so builds behave exactly as before
|
||||
## without it.
|
||||
##
|
||||
## PKG_CACHE=http://192.168.1.40 pkg-cache-repos.sh rpm # AlmaLinux/EPEL/Remi
|
||||
## PKG_CACHE=http://192.168.1.40 pkg-cache-repos.sh apt # Ubuntu + LiteSpeed
|
||||
## pkg-cache-repos.sh release-urls # prints EPEL+Remi release RPM URLs
|
||||
## pkg-cache-repos.sh restore # put the original repo files back
|
||||
##
|
||||
## Every RUN that rewrites must `restore` before it ends, so no shipped layer
|
||||
## carries the cache's LAN address in /etc/yum.repos.d or /etc/apt (images run on
|
||||
## hosts that cannot reach it). restore is a no-op when nothing was rewritten.
|
||||
##
|
||||
## Why: on 2026-10-21 (PHP security release) builds failed on Remi mirrors lagging
|
||||
## hours behind origin, Remi's origin timing out over HTTPS, and an EPEL mirror
|
||||
## with a broken certificate. The cache fetches from the ORIGINS over HTTPS (with
|
||||
## origin/mirror fallbacks and stale-on-error), keeps metadata only ~3 minutes,
|
||||
## and keeps immutable .rpm/.deb files long-term.
|
||||
##
|
||||
## Integrity is unchanged: dnf/apt still verify package GPG signatures (and apt
|
||||
## the signed InRelease/Release.gpg); the build's php.net version gates
|
||||
## (EXPECT_PHP_MIN / EXPECT_PHP_VERSION) still fail a build that gets old PHP.
|
||||
##
|
||||
## rpm: each rewritten baseurl keeps the upstream origin as a SECOND url, so if
|
||||
## the cache dies mid-build dnf fails over to the origin by itself.
|
||||
## apt: apt has no per-source failover; CI only sets PKG_CACHE after probing the
|
||||
## cache, and a build without PKG_CACHE uses the upstream sources.
|
||||
set -euo pipefail
|
||||
|
||||
MODE="${1:?usage: pkg-cache-repos.sh rpm|apt|release-urls|restore}"
|
||||
STATE=/var/lib/pkg-cache-repos # pristine copies of every file we rewrite
|
||||
|
||||
if [ "$MODE" = restore ]; then
|
||||
[ -d "$STATE" ] || exit 0
|
||||
(cd "$STATE" && find . -type f) | while read -r rel; do
|
||||
cat "$STATE/$rel" > "/${rel#./}"
|
||||
done
|
||||
rm -rf "$STATE"
|
||||
echo "pkg-cache-repos: original repository files restored"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
# Keep the first-seen (pre-rewrite) copy of a file; later calls in the same RUN
|
||||
# never overwrite it.
|
||||
save() {
|
||||
[ -e "$STATE$1" ] && return 0
|
||||
mkdir -p "$STATE$(dirname "$1")"
|
||||
cp -p "$1" "$STATE$1"
|
||||
}
|
||||
C="${PKG_CACHE:-}"
|
||||
C="${C%/}"
|
||||
|
||||
if [ "$MODE" = release-urls ]; then
|
||||
if [ -n "$C" ]; then
|
||||
echo "$C/epel/epel-release-latest-10.noarch.rpm $C/remi/enterprise/remi-release-10.rpm"
|
||||
else
|
||||
echo "https://dl.fedoraproject.org/pub/epel/epel-release-latest-10.noarch.rpm https://rpms.remirepo.net/enterprise/remi-release-10.rpm"
|
||||
fi
|
||||
exit 0
|
||||
fi
|
||||
|
||||
if [ -z "$C" ]; then
|
||||
echo "pkg-cache-repos($MODE): PKG_CACHE not set - using upstream repositories"
|
||||
exit 0
|
||||
fi
|
||||
case "$C" in
|
||||
http://*|https://*) ;;
|
||||
*) echo "FATAL(pkg-cache-repos): PKG_CACHE must be an http(s) URL, got '$C'" >&2; exit 1 ;;
|
||||
esac
|
||||
|
||||
rewrite_rpm() {
|
||||
local f="$1"
|
||||
grep -q -E '^#? ?baseurl=https?://(repo\.almalinux\.org|download\.example|rpms\.remirepo\.net)/' "$f" || return 0
|
||||
save "$f"
|
||||
# Per [section]: a (commented or live) baseurl on a known origin becomes
|
||||
# "baseurl=<cache>/<prefix>/<path> <origin>/<path>", and that section's
|
||||
# mirrorlist/metalink is commented out (dnf prefers them over baseurl).
|
||||
# Sections without a known origin (vault debug/source, ...) are untouched.
|
||||
awk -v C="$C" '
|
||||
function flush( i) {
|
||||
for (i = 1; i <= n; i++) {
|
||||
if (hit && buf[i] ~ /^(mirrorlist|metalink)=/) buf[i] = "#" buf[i]
|
||||
print buf[i]
|
||||
}
|
||||
n = 0; hit = 0
|
||||
}
|
||||
function rw(line, re, prefix, origin, p) {
|
||||
if (match(line, re)) {
|
||||
p = substr(line, RSTART + RLENGTH); sub(/[ \t].*$/, "", p)
|
||||
hit = 1
|
||||
return "baseurl=" C "/" prefix "/" p " " origin "/" p
|
||||
}
|
||||
return line
|
||||
}
|
||||
/^\[/ { flush() }
|
||||
{
|
||||
l = $0
|
||||
if (l ~ /^#? ?baseurl=/) {
|
||||
l = rw(l, "^#? ?baseurl=https?://repo\\.almalinux\\.org/almalinux/", "almalinux", "https://repo.almalinux.org/almalinux")
|
||||
l = rw(l, "^#? ?baseurl=https?://download\\.example/pub/epel/", "epel", "https://dl.fedoraproject.org/pub/epel")
|
||||
l = rw(l, "^#? ?baseurl=https?://rpms\\.remirepo\\.net/", "remi", "https://rpms.remirepo.net")
|
||||
}
|
||||
buf[++n] = l
|
||||
}
|
||||
END { flush() }
|
||||
' "$f" > "$f.pkgcache" && cat "$f.pkgcache" > "$f" && rm -f "$f.pkgcache"
|
||||
}
|
||||
|
||||
rewrite_apt() {
|
||||
local f="$1"
|
||||
grep -q -E 'https?://([a-z]{2}\.)?(archive|security)\.ubuntu\.com/|https?://rpms\.litespeedtech\.com/' "$f" || return 0
|
||||
save "$f"
|
||||
sed -i -E \
|
||||
-e "s%https?://(archive|[a-z]{2}\.archive)\.ubuntu\.com/ubuntu/?%$C/ubuntu/%g" \
|
||||
-e "s%https?://security\.ubuntu\.com/ubuntu/?%$C/ubuntu-security/%g" \
|
||||
-e "s%https?://rpms\.litespeedtech\.com/%$C/litespeed/%g" \
|
||||
"$f"
|
||||
}
|
||||
|
||||
case "$MODE" in
|
||||
rpm)
|
||||
for f in /etc/yum.repos.d/*.repo; do [ -f "$f" ] && rewrite_rpm "$f"; done
|
||||
echo "pkg-cache-repos(rpm): repos now via $C:"
|
||||
grep -h '^baseurl=' /etc/yum.repos.d/*.repo | grep -F "$C/" | sort -u | sed 's/^/ /'
|
||||
;;
|
||||
apt)
|
||||
for f in /etc/apt/sources.list /etc/apt/sources.list.d/*.list /etc/apt/sources.list.d/*.sources; do
|
||||
[ -f "$f" ] && rewrite_apt "$f"
|
||||
done
|
||||
echo "pkg-cache-repos(apt): sources now via $C:"
|
||||
grep -h -E '^(deb|URIs:)' /etc/apt/sources.list /etc/apt/sources.list.d/* 2>/dev/null | grep -F "$C/" | sort -u | sed 's/^/ /'
|
||||
;;
|
||||
*) echo "FATAL(pkg-cache-repos): unknown mode '$MODE'" >&2; exit 1 ;;
|
||||
esac
|
||||
Reference in new issue
Block a user