Compare commits

..
Author SHA1 Message Date
jknapp e7d4db5bd3 Merge pull request 'fix(cac): healthcheck fails only on no answer or 5xx, not 4xx' (#34) from fix/healthcheck-4xx into trunk
Cloud Apache Container / Shell-Checks (push) Successful in 17s
Cloud Apache Container / Build-and-Push (82) (push) Successful in 4m12s
Cloud Apache Container / Build-and-Push (83) (push) Successful in 5m6s
Cloud Apache Container / Build-and-Push (84) (push) Successful in 5m21s
Cloud Apache Container / Build-FPM-Images (85) (push) Successful in 2m31s
Cloud Apache Container / Build-and-Push (85) (push) Successful in 5m49s
Cloud Apache Container / Build-FPM-Images (83) (push) Successful in 5m35s
Cloud Apache Container / Build-FPM-Images (82) (push) Successful in 5m53s
Cloud Apache Container / Build-FPM-Images (84) (push) Successful in 5m39s
Cloud Apache Container / Build-LiteSpeed-Images (82) (push) Successful in 2m36s
Cloud Apache Container / Build-LiteSpeed-Images (83) (push) Successful in 4m52s
Cloud Apache Container / Build-LiteSpeed-Images (84) (push) Successful in 4m53s
Cloud Apache Container / Build-LSPHP-Images (84) (push) Successful in 2m50s
Cloud Apache Container / Build-LiteSpeed-Images (85) (push) Successful in 4m36s
Cloud Apache Container / Build-Shared-httpd (push) Successful in 1m40s
Cloud Apache Container / Build-Shared-OLS (push) Successful in 38s
Cloud Apache Container / Build-LSPHP-Images (83) (push) Successful in 5m25s
Cloud Apache Container / Build-LSPHP-Images (82) (push) Successful in 5m46s
Cloud Apache Container / Build-LSPHP-Images (85) (push) Successful in 5m33s
2026-10-22 15:04:10 +00:00
jknapp 0fee1b54c4 Merge pull request 'pkg-cache: Debian/NodeSource/Go/EL9 modes; wire cac-litespeed + cac-lsphp; docs' (#36) from ci/pkg-cache-more-upstreams into trunk
Cloud Apache Container / Shell-Checks (push) Successful in 37s
Cloud Apache Container / Build-and-Push (84) (push) Successful in 2m44s
Cloud Apache Container / Build-and-Push (85) (push) Successful in 4m36s
Cloud Apache Container / Build-and-Push (83) (push) Successful in 4m52s
Cloud Apache Container / Build-and-Push (82) (push) Successful in 5m9s
Cloud Apache Container / Build-FPM-Images (84) (push) Successful in 4m14s
Cloud Apache Container / Build-FPM-Images (85) (push) Successful in 2m31s
Cloud Apache Container / Build-FPM-Images (82) (push) Successful in 4m53s
Cloud Apache Container / Build-FPM-Images (83) (push) Successful in 4m45s
Cloud Apache Container / Build-LiteSpeed-Images (82) (push) Successful in 2m43s
Cloud Apache Container / Build-LiteSpeed-Images (83) (push) Successful in 3m57s
Cloud Apache Container / Build-LSPHP-Images (85) (push) Successful in 4m6s
Cloud Apache Container / Build-LiteSpeed-Images (85) (push) Successful in 5m17s
Cloud Apache Container / Build-LiteSpeed-Images (84) (push) Successful in 5m48s
Cloud Apache Container / Build-LSPHP-Images (83) (push) Successful in 5m20s
Cloud Apache Container / Build-LSPHP-Images (82) (push) Successful in 6m2s
Cloud Apache Container / Build-LSPHP-Images (84) (push) Successful in 5m37s
Cloud Apache Container / Build-Shared-httpd (push) Successful in 1m43s
Cloud Apache Container / Build-Shared-OLS (push) Successful in 44s
2026-10-22 14:35:51 +00:00
shadowdaoandClaude Opus 5.5 9baa7a6029 docs(pkg-cache): new upstreams, modes, wired repos, how to add an upstream
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-22 07:35:36 -07:00
shadowdaoandClaude Opus 5.5 3a6cab19ee ci: wire cac-litespeed and cac-lsphp to the package cache and digest-pinned base
Dockerfile.litespeed / Dockerfile.lsphp: one global
ARG BASE_IMAGE=litespeedtech/openlitespeed:${OLS_VERSION}-lsphp${PHPVER}
used by every stage (incl. the emergency lsphp-src stage, which stays on
upstream repos). The stages that run lsphp-apt-upgrade.sh (litespeed final,
lsphp ext-build + final) take ARG PKG_CACHE=, ship pkg-cache-repos.sh +
keys/pkg-cache/, and wrap the upgrade in `pkg-cache-repos.sh apt` ...
`pkg-cache-repos.sh restore`. Without PKG_CACHE both are no-ops.

lsphp-apt-upgrade.sh: the madison gate also accepts ${PKG_CACHE}/litespeed/
as the lsphp source, but only when PKG_CACHE is set.

build-push.yaml: Build-LiteSpeed-Images and Build-LSPHP-Images resolve the
base digest directly from Docker Hub, probe the cache, set up buildx with
the registry mirror only for a pinned base, and pass BASE_IMAGE + PKG_CACHE.
OLS_VERSION (1.8.4) is now a job-level env read by both the digest lookup
and the build-arg.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-22 07:11:52 -07:00
shadowdaoandClaude Opus 5.5 6a11fc9894 feat(pkg-cache): Debian, NodeSource, Go proxy and EL9 support in pkg-cache-repos.sh
Backward compatible; existing modes unchanged for existing callers.
- apt: also rewrites deb.debian.org/debian, deb.debian.org/debian-security and
  security.debian.org to <cache>/debian and <cache>/debian-security; the
  listing no longer fails under pipefail when /etc/apt/sources.list is absent
  (Debian 13 images).
- rpm: rewrites rpm.nodesource.com repos to <cache>/nodesource (gpgcheck=1;
  NodeSource does not sign repomd).
- new `nodesource MAJOR`: writes the nodesource-nodejs repo itself with the
  committed signing key (replaces curl setup_N.x | bash).
- new `go-env`: prints GOPROXY=<cache>/goproxy|https://proxy.golang.org|direct
  plus SSL_CERT_FILE for the RUN; go.sum + sum.golang.org still verify.
- release-install: EL9 or EL10 picked from rpm %rhel; adds RPM-GPG-KEY-EPEL-9.
- PKG_CACHE is regex-validated.
Tested through the cache on a throwaway runner CT (pve vmbr2): EL9+EPEL9,
NodeSource 22 on EL10, Debian trixie apt, golang:1.25 apt + go-env; restore
left no cache reference in each case.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-22 07:08:22 -07:00
jknapp 05ad47a833 Merge pull request 'ci: pin base images by digest resolved directly from Docker Hub' (#35) from ci/base-image-digest-pin into trunk
Cloud Apache Container / Shell-Checks (push) Successful in 1m8s
Cloud Apache Container / Build-and-Push (84) (push) Successful in 3m50s
Cloud Apache Container / Build-and-Push (82) (push) Successful in 5m55s
Cloud Apache Container / Build-and-Push (85) (push) Successful in 5m24s
Cloud Apache Container / Build-and-Push (83) (push) Successful in 5m55s
Cloud Apache Container / Build-FPM-Images (83) (push) Successful in 5m21s
Cloud Apache Container / Build-FPM-Images (85) (push) Successful in 2m15s
Cloud Apache Container / Build-FPM-Images (82) (push) Successful in 6m7s
Cloud Apache Container / Build-FPM-Images (84) (push) Successful in 6m1s
Cloud Apache Container / Build-LiteSpeed-Images (82) (push) Successful in 1m28s
Cloud Apache Container / Build-LiteSpeed-Images (83) (push) Successful in 6m4s
Cloud Apache Container / Build-LiteSpeed-Images (85) (push) Successful in 7m2s
Cloud Apache Container / Build-LiteSpeed-Images (84) (push) Successful in 7m21s
Cloud Apache Container / Build-LSPHP-Images (83) (push) Successful in 7m3s
Cloud Apache Container / Build-LSPHP-Images (84) (push) Successful in 7m3s
Cloud Apache Container / Build-LSPHP-Images (85) (push) Successful in 7m30s
Cloud Apache Container / Build-Shared-httpd (push) Successful in 3m48s
Cloud Apache Container / Build-Shared-OLS (push) Successful in 3m47s
Cloud Apache Container / Build-LSPHP-Images (82) (push) Successful in 14m41s
2026-10-22 13:59:04 +00:00
shadowdaoandClaude Opus 5.5 d1547f3418 ci: pin base images by digest resolved directly from Docker Hub
With the pkg-cache BuildKit mirror on, `FROM almalinux/10-base` was resolved
by tag through the cache, so a compromised cache could substitute the image.

Each job using the mirror now looks up the base tag's digest DIRECTLY at
registry-1.docker.io (.gitea/scripts/resolve-base-image.sh, HTTPS, HEAD on
the manifest, regex-validated) and builds FROM image@sha256:... via the
BASE_IMAGE build-arg, so the mirror can only serve content-addressed blobs.
If the lookup fails, the mirror is NOT used for that job and the tag is pulled
straight from Docker Hub. The digest is resolved every run (no manual bumps);
local builds default to the tag.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-22 06:58:16 -07:00
shadowdaoandClaude Opus 5.5 a00a3c7fc3 fix(cac): healthcheck fails only on no answer or 5xx, not 4xx
curl -f also failed on 403/404, so a new site with an empty docroot showed
unhealthy forever and WHP's monitor restarted it as PHP wedged.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-22 05:41:40 -07:00
jknapp 3334f94cd5 Merge pull request 'ci: stop building PHP 7.4, 8.0 and 8.1 images' (#33) from ci/drop-eol-php into trunk
Cloud Apache Container / Shell-Checks (push) Successful in 17s
Cloud Apache Container / Build-and-Push (82) (push) Successful in 2m52s
Cloud Apache Container / Build-and-Push (83) (push) Successful in 4m57s
Cloud Apache Container / Build-FPM-Images (85) (push) Successful in 2m36s
Cloud Apache Container / Build-FPM-Images (82) (push) Successful in 4m40s
Cloud Apache Container / Build-FPM-Images (83) (push) Successful in 4m26s
Cloud Apache Container / Build-and-Push (84) (push) Successful in 5m13s
Cloud Apache Container / Build-FPM-Images (84) (push) Successful in 4m12s
Cloud Apache Container / Build-and-Push (85) (push) Successful in 4m59s
Cloud Apache Container / Build-LiteSpeed-Images (82) (push) Successful in 2m40s
Cloud Apache Container / Build-LSPHP-Images (85) (push) Successful in 1m57s
Cloud Apache Container / Build-LiteSpeed-Images (83) (push) Successful in 5m6s
Cloud Apache Container / Build-Shared-httpd (push) Successful in 55s
Cloud Apache Container / Build-Shared-OLS (push) Successful in 47s
Cloud Apache Container / Build-LiteSpeed-Images (85) (push) Successful in 5m19s
Cloud Apache Container / Build-LiteSpeed-Images (84) (push) Successful in 5m40s
Cloud Apache Container / Build-LSPHP-Images (83) (push) Successful in 5m47s
Cloud Apache Container / Build-LSPHP-Images (82) (push) Successful in 6m29s
Cloud Apache Container / Build-LSPHP-Images (84) (push) Successful in 6m12s
2026-10-22 01:13:20 +00:00
14 changed files with 603 additions and 44 deletions

No files matched your search

+35
View File
@@ -0,0 +1,35 @@
#!/bin/bash
## resolve-base-image.sh IMAGE:TAG — CI only (not copied into images).
## Prints IMAGE@sha256:<digest> for a Docker Hub tag, looked up DIRECTLY at
## registry-1.docker.io over HTTPS (never via the pkg-cache mirror), so the
## build can say `FROM IMAGE@sha256:...` and the mirror can only serve
## content-addressed blobs that BuildKit verifies. Exits non-zero (prints
## nothing on stdout) if the lookup fails; the workflow then skips the mirror
## and pulls the tag directly from Docker Hub. HEAD requests do not count
## against Docker Hub pull rate limits.
## Canonical copy: cloud-apache-container/.gitea/scripts/resolve-base-image.sh
## (docs/pkg-cache.md there); other repos carry identical copies.
set -euo pipefail
ref="${1:?usage: resolve-base-image.sh IMAGE:TAG}"
name_re='^[a-z0-9]+([._-][a-z0-9]+)*(/[a-z0-9]+([._-][a-z0-9]+)*)?$'
tag_re='^[A-Za-z0-9_][A-Za-z0-9_.-]{0,127}$'
name="${ref%:*}"; tag="${ref##*:}"
[ "$name" != "$ref" ] || { echo "resolve-base-image: '$ref' has no :tag" >&2; exit 2; }
[[ "$name" =~ $name_re ]] || { echo "resolve-base-image: bad image name '$name'" >&2; exit 2; }
[[ "$tag" =~ $tag_re ]] || { echo "resolve-base-image: bad tag '$tag'" >&2; exit 2; }
repo="$name"; [[ "$repo" == */* ]] || repo="library/$repo" # official images
curl_opts=(-fsS --proto '=https' --retry 3 --retry-all-errors --max-time 20)
token=$(curl "${curl_opts[@]}" "https://auth.docker.io/token?service=registry.docker.io&scope=repository:${repo}:pull" |
python3 -c 'import json,sys; print(json.load(sys.stdin)["token"])')
[ -n "$token" ] || { echo "resolve-base-image: no Docker Hub token for $repo" >&2; exit 1; }
hdrs=$(curl "${curl_opts[@]}" -I \
-H "Authorization: Bearer ${token}" \
-H 'Accept: application/vnd.oci.image.index.v1+json' \
-H 'Accept: application/vnd.docker.distribution.manifest.list.v2+json' \
-H 'Accept: application/vnd.oci.image.manifest.v1+json' \
-H 'Accept: application/vnd.docker.distribution.manifest.v2+json' \
"https://registry-1.docker.io/v2/${repo}/manifests/${tag}")
digest=$(printf '%s\n' "$hdrs" | tr -d '\r' | awk 'tolower($1)=="docker-content-digest:" {print $2}' | tail -n1)
[[ "$digest" =~ ^sha256:[0-9a-f]{64}$ ]] || { echo "resolve-base-image: no valid digest for $ref (got '${digest}')" >&2; exit 1; }
printf '%s@%s\n' "$name" "$digest"
+232 -13
View File
@@ -57,7 +57,8 @@ jobs:
scripts/lsphp-abi-gate.sh \ scripts/lsphp-abi-gate.sh \
scripts/tune-mpm.sh \ scripts/tune-mpm.sh \
scripts/tests/lsphp-info-probe.test.sh \ scripts/tests/lsphp-info-probe.test.sh \
scripts/tests/lsphp-abi-gate.test.sh scripts/tests/lsphp-abi-gate.test.sh \
.gitea/scripts/resolve-base-image.sh
- name: lsphp probe regression test - name: lsphp probe regression test
run: ./scripts/tests/lsphp-info-probe.test.sh run: ./scripts/tests/lsphp-info-probe.test.sh
@@ -85,6 +86,26 @@ jobs:
- name: Checkout - name: Checkout
uses: actions/checkout@v4 uses: actions/checkout@v4
# Base image pinned by DIGEST, looked up directly at Docker Hub (never via
# the pkg-cache mirror), so the mirror can only serve content-addressed
# blobs that BuildKit verifies. If the lookup fails this job does NOT use
# the mirror and pulls the tag straight from Docker Hub. docs/pkg-cache.md.
- name: Resolve base image digest (Docker Hub, direct)
id: base
env:
BASE_TAG: almalinux/10-base:latest
run: |
set -uo pipefail
if ref=$(.gitea/scripts/resolve-base-image.sh "$BASE_TAG") && [[ "$ref" =~ ^[a-z0-9][a-z0-9._/-]*@sha256:[0-9a-f]{64}$ ]]; then
echo "base-image: ${BASE_TAG} -> ${ref} (digest from registry-1.docker.io)"
echo "image=${ref}" >> "$GITHUB_OUTPUT"
echo "pinned=true" >> "$GITHUB_OUTPUT"
else
echo "::warning::base image ${BASE_TAG}: Docker Hub digest lookup failed - pulling the tag directly from Docker Hub, pkg-cache registry mirror NOT used"
echo "image=${BASE_TAG}" >> "$GITHUB_OUTPUT"
echo "pinned=false" >> "$GITHUB_OUTPUT"
fi
# Internal package cache (CT130 pkg-cache, docs/pkg-cache.md). Used ONLY if # Internal package cache (CT130 pkg-cache, docs/pkg-cache.md). Used ONLY if
# it answers over TLS with our pinned CA; otherwise (down, unreachable, # it answers over TLS with our pinned CA; otherwise (down, unreachable,
# bad/expired cert) this job builds from the upstream repos and Docker Hub # bad/expired cert) this job builds from the upstream repos and Docker Hub
@@ -92,11 +113,14 @@ jobs:
# separately so either can fail back on its own. # separately so either can fail back on its own.
- name: Probe package cache - name: Probe package cache
id: pkgcache id: pkgcache
# The registry mirror is only used for a digest-pinned base image.
env:
BASE_PINNED: ${{ steps.base.outputs.pinned }}
run: | run: |
url=""; mirror="" url=""; mirror=""
if curl -fsS --max-time 5 --cacert keys/pkg-cache/ca.crt -o /dev/null https://192.168.1.40/; then url="https://192.168.1.40"; fi if curl -fsS --max-time 5 --cacert keys/pkg-cache/ca.crt -o /dev/null https://192.168.1.40/; then url="https://192.168.1.40"; fi
if curl -fsS --max-time 5 --cacert keys/pkg-cache/ca.crt -o /dev/null https://192.168.1.40:5000/v2/; then mirror="192.168.1.40:5000"; fi if [ "$BASE_PINNED" = true ] && curl -fsS --max-time 5 --cacert keys/pkg-cache/ca.crt -o /dev/null https://192.168.1.40:5000/v2/; then mirror="192.168.1.40:5000"; fi
echo "pkg-cache: repos=${url:-<unreachable: upstream>} registry-mirror=${mirror:-<unreachable: Docker Hub>}" echo "pkg-cache: repos=${url:-<unreachable: upstream>} registry-mirror=${mirror:-<not used: Docker Hub direct>}"
[ -n "$url" ] || echo "::warning::pkg-cache unreachable - building from upstream repositories" [ -n "$url" ] || echo "::warning::pkg-cache unreachable - building from upstream repositories"
echo "url=${url}" >> "$GITHUB_OUTPUT" echo "url=${url}" >> "$GITHUB_OUTPUT"
echo "mirror=${mirror}" >> "$GITHUB_OUTPUT" echo "mirror=${mirror}" >> "$GITHUB_OUTPUT"
@@ -165,6 +189,7 @@ jobs:
build-args: | build-args: |
PHPVER=${{ matrix.phpver }} PHPVER=${{ matrix.phpver }}
CACHE_BUST=${{ steps.bust.outputs.value }} CACHE_BUST=${{ steps.bust.outputs.value }}
BASE_IMAGE=${{ steps.base.outputs.image }}
PKG_CACHE=${{ steps.pkgcache.outputs.url }} PKG_CACHE=${{ steps.pkgcache.outputs.url }}
EXPECT_PHP_MIN=${{ steps.phpmin.outputs.min }} EXPECT_PHP_MIN=${{ steps.phpmin.outputs.min }}
tags: | tags: |
@@ -183,6 +208,26 @@ jobs:
- name: Checkout - name: Checkout
uses: actions/checkout@v4 uses: actions/checkout@v4
# Base image pinned by DIGEST, looked up directly at Docker Hub (never via
# the pkg-cache mirror), so the mirror can only serve content-addressed
# blobs that BuildKit verifies. If the lookup fails this job does NOT use
# the mirror and pulls the tag straight from Docker Hub. docs/pkg-cache.md.
- name: Resolve base image digest (Docker Hub, direct)
id: base
env:
BASE_TAG: almalinux/10-base:latest
run: |
set -uo pipefail
if ref=$(.gitea/scripts/resolve-base-image.sh "$BASE_TAG") && [[ "$ref" =~ ^[a-z0-9][a-z0-9._/-]*@sha256:[0-9a-f]{64}$ ]]; then
echo "base-image: ${BASE_TAG} -> ${ref} (digest from registry-1.docker.io)"
echo "image=${ref}" >> "$GITHUB_OUTPUT"
echo "pinned=true" >> "$GITHUB_OUTPUT"
else
echo "::warning::base image ${BASE_TAG}: Docker Hub digest lookup failed - pulling the tag directly from Docker Hub, pkg-cache registry mirror NOT used"
echo "image=${BASE_TAG}" >> "$GITHUB_OUTPUT"
echo "pinned=false" >> "$GITHUB_OUTPUT"
fi
# Internal package cache (CT130 pkg-cache, docs/pkg-cache.md). Used ONLY if # Internal package cache (CT130 pkg-cache, docs/pkg-cache.md). Used ONLY if
# it answers over TLS with our pinned CA; otherwise (down, unreachable, # it answers over TLS with our pinned CA; otherwise (down, unreachable,
# bad/expired cert) this job builds from the upstream repos and Docker Hub # bad/expired cert) this job builds from the upstream repos and Docker Hub
@@ -190,11 +235,14 @@ jobs:
# separately so either can fail back on its own. # separately so either can fail back on its own.
- name: Probe package cache - name: Probe package cache
id: pkgcache id: pkgcache
# The registry mirror is only used for a digest-pinned base image.
env:
BASE_PINNED: ${{ steps.base.outputs.pinned }}
run: | run: |
url=""; mirror="" url=""; mirror=""
if curl -fsS --max-time 5 --cacert keys/pkg-cache/ca.crt -o /dev/null https://192.168.1.40/; then url="https://192.168.1.40"; fi if curl -fsS --max-time 5 --cacert keys/pkg-cache/ca.crt -o /dev/null https://192.168.1.40/; then url="https://192.168.1.40"; fi
if curl -fsS --max-time 5 --cacert keys/pkg-cache/ca.crt -o /dev/null https://192.168.1.40:5000/v2/; then mirror="192.168.1.40:5000"; fi if [ "$BASE_PINNED" = true ] && curl -fsS --max-time 5 --cacert keys/pkg-cache/ca.crt -o /dev/null https://192.168.1.40:5000/v2/; then mirror="192.168.1.40:5000"; fi
echo "pkg-cache: repos=${url:-<unreachable: upstream>} registry-mirror=${mirror:-<unreachable: Docker Hub>}" echo "pkg-cache: repos=${url:-<unreachable: upstream>} registry-mirror=${mirror:-<not used: Docker Hub direct>}"
[ -n "$url" ] || echo "::warning::pkg-cache unreachable - building from upstream repositories" [ -n "$url" ] || echo "::warning::pkg-cache unreachable - building from upstream repositories"
echo "url=${url}" >> "$GITHUB_OUTPUT" echo "url=${url}" >> "$GITHUB_OUTPUT"
echo "mirror=${mirror}" >> "$GITHUB_OUTPUT" echo "mirror=${mirror}" >> "$GITHUB_OUTPUT"
@@ -264,6 +312,7 @@ jobs:
build-args: | build-args: |
PHPVER=${{ matrix.phpver }} PHPVER=${{ matrix.phpver }}
CACHE_BUST=${{ steps.bust.outputs.value }} CACHE_BUST=${{ steps.bust.outputs.value }}
BASE_IMAGE=${{ steps.base.outputs.image }}
PKG_CACHE=${{ steps.pkgcache.outputs.url }} PKG_CACHE=${{ steps.pkgcache.outputs.url }}
EXPECT_PHP_MIN=${{ steps.phpmin.outputs.min }} EXPECT_PHP_MIN=${{ steps.phpmin.outputs.min }}
tags: | tags: |
@@ -272,6 +321,10 @@ jobs:
Build-LiteSpeed-Images: Build-LiteSpeed-Images:
runs-on: ubuntu-latest runs-on: ubuntu-latest
# One place for the OLS base tag: digest lookup and build-args both read it.
# OLS_VERSION pinned to 1.8.4 (see the build step); Dockerfile default differs.
env:
OLS_VERSION: "1.8.4"
strategy: strategy:
# One PHP version failing (e.g. an EOL stream) must not cancel the # One PHP version failing (e.g. an EOL stream) must not cancel the
# security builds of the others. # security builds of the others.
@@ -286,10 +339,63 @@ jobs:
- name: Checkout - name: Checkout
uses: actions/checkout@v4 uses: actions/checkout@v4
# Base image pinned by DIGEST, looked up directly at Docker Hub (never via
# the pkg-cache mirror), so the mirror can only serve content-addressed
# blobs that BuildKit verifies. If the lookup fails this job does NOT use
# the mirror and pulls the tag straight from Docker Hub. docs/pkg-cache.md.
- name: Resolve base image digest (Docker Hub, direct)
id: base
env:
BASE_TAG: litespeedtech/openlitespeed:${{ env.OLS_VERSION }}-lsphp${{ matrix.phpver }}
run: |
set -uo pipefail
if ref=$(.gitea/scripts/resolve-base-image.sh "$BASE_TAG") && [[ "$ref" =~ ^[a-z0-9][a-z0-9._/-]*@sha256:[0-9a-f]{64}$ ]]; then
echo "base-image: ${BASE_TAG} -> ${ref} (digest from registry-1.docker.io)"
echo "image=${ref}" >> "$GITHUB_OUTPUT"
echo "pinned=true" >> "$GITHUB_OUTPUT"
else
echo "::warning::base image ${BASE_TAG}: Docker Hub digest lookup failed - pulling the tag directly from Docker Hub, pkg-cache registry mirror NOT used"
echo "image=${BASE_TAG}" >> "$GITHUB_OUTPUT"
echo "pinned=false" >> "$GITHUB_OUTPUT"
fi
# Internal package cache (CT130 pkg-cache, docs/pkg-cache.md). Used ONLY if
# it answers over TLS with our pinned CA; otherwise (down, unreachable,
# bad/expired cert) this job builds from the upstream repos and Docker Hub
# exactly as before. The repo cache and the registry mirror are probed
# separately so either can fail back on its own.
- name: Probe package cache
id: pkgcache
# The registry mirror is only used for a digest-pinned base image.
env:
BASE_PINNED: ${{ steps.base.outputs.pinned }}
run: |
url=""; mirror=""
if curl -fsS --max-time 5 --cacert keys/pkg-cache/ca.crt -o /dev/null https://192.168.1.40/; then url="https://192.168.1.40"; fi
if [ "$BASE_PINNED" = true ] && curl -fsS --max-time 5 --cacert keys/pkg-cache/ca.crt -o /dev/null https://192.168.1.40:5000/v2/; then mirror="192.168.1.40:5000"; fi
echo "pkg-cache: repos=${url:-<unreachable: upstream>} registry-mirror=${mirror:-<not used: Docker Hub direct>}"
[ -n "$url" ] || echo "::warning::pkg-cache unreachable - building from upstream repositories"
echo "url=${url}" >> "$GITHUB_OUTPUT"
echo "mirror=${mirror}" >> "$GITHUB_OUTPUT"
- name: Set up QEMU - name: Set up QEMU
uses: docker/setup-qemu-action@v3 uses: docker/setup-qemu-action@v3
# BuildKit (docker-container driver) ignores dockerd's registry-mirrors,
# so the mirror is configured on the builder; BuildKit itself still falls
# back to Docker Hub if the mirror stops answering mid-run.
- name: Set up Docker Buildx (Docker Hub via pkg-cache mirror)
if: steps.pkgcache.outputs.mirror != ''
uses: docker/setup-buildx-action@v3
with:
buildkitd-config-inline: |
[registry."docker.io"]
mirrors = ["192.168.1.40:5000"]
[registry."192.168.1.40:5000"]
ca = ["${{ github.workspace }}/keys/pkg-cache/ca.crt"]
- name: Set up Docker Buildx - name: Set up Docker Buildx
if: steps.pkgcache.outputs.mirror == ''
uses: docker/setup-buildx-action@v3 uses: docker/setup-buildx-action@v3
# Per-run cache-bust value. Each job's buildx builder is created fresh by # Per-run cache-bust value. Each job's buildx builder is created fresh by
@@ -360,7 +466,9 @@ jobs:
push: true push: true
build-args: | build-args: |
PHPVER=${{ matrix.phpver }} PHPVER=${{ matrix.phpver }}
OLS_VERSION=1.8.4 OLS_VERSION=${{ env.OLS_VERSION }}
BASE_IMAGE=${{ steps.base.outputs.image }}
PKG_CACHE=${{ steps.pkgcache.outputs.url }}
CACHE_BUST=${{ steps.bust.outputs.value }} CACHE_BUST=${{ steps.bust.outputs.value }}
EXPECT_LSPHP_PKG=${{ steps.lsphp.outputs.pkg }} EXPECT_LSPHP_PKG=${{ steps.lsphp.outputs.pkg }}
EXPECT_PHP_VERSION=${{ steps.lsphp.outputs.php }} EXPECT_PHP_VERSION=${{ steps.lsphp.outputs.php }}
@@ -374,6 +482,10 @@ jobs:
Build-LSPHP-Images: Build-LSPHP-Images:
runs-on: ubuntu-latest runs-on: ubuntu-latest
# One place for the OLS base tag: digest lookup and build-args both read it.
# OLS_VERSION pinned to 1.8.4 (see the build step); Dockerfile default differs.
env:
OLS_VERSION: "1.8.4"
strategy: strategy:
# One PHP version failing (e.g. an EOL stream) must not cancel the # One PHP version failing (e.g. an EOL stream) must not cancel the
# security builds of the others. # security builds of the others.
@@ -388,10 +500,63 @@ jobs:
- name: Checkout - name: Checkout
uses: actions/checkout@v4 uses: actions/checkout@v4
# Base image pinned by DIGEST, looked up directly at Docker Hub (never via
# the pkg-cache mirror), so the mirror can only serve content-addressed
# blobs that BuildKit verifies. If the lookup fails this job does NOT use
# the mirror and pulls the tag straight from Docker Hub. docs/pkg-cache.md.
- name: Resolve base image digest (Docker Hub, direct)
id: base
env:
BASE_TAG: litespeedtech/openlitespeed:${{ env.OLS_VERSION }}-lsphp${{ matrix.phpver }}
run: |
set -uo pipefail
if ref=$(.gitea/scripts/resolve-base-image.sh "$BASE_TAG") && [[ "$ref" =~ ^[a-z0-9][a-z0-9._/-]*@sha256:[0-9a-f]{64}$ ]]; then
echo "base-image: ${BASE_TAG} -> ${ref} (digest from registry-1.docker.io)"
echo "image=${ref}" >> "$GITHUB_OUTPUT"
echo "pinned=true" >> "$GITHUB_OUTPUT"
else
echo "::warning::base image ${BASE_TAG}: Docker Hub digest lookup failed - pulling the tag directly from Docker Hub, pkg-cache registry mirror NOT used"
echo "image=${BASE_TAG}" >> "$GITHUB_OUTPUT"
echo "pinned=false" >> "$GITHUB_OUTPUT"
fi
# Internal package cache (CT130 pkg-cache, docs/pkg-cache.md). Used ONLY if
# it answers over TLS with our pinned CA; otherwise (down, unreachable,
# bad/expired cert) this job builds from the upstream repos and Docker Hub
# exactly as before. The repo cache and the registry mirror are probed
# separately so either can fail back on its own.
- name: Probe package cache
id: pkgcache
# The registry mirror is only used for a digest-pinned base image.
env:
BASE_PINNED: ${{ steps.base.outputs.pinned }}
run: |
url=""; mirror=""
if curl -fsS --max-time 5 --cacert keys/pkg-cache/ca.crt -o /dev/null https://192.168.1.40/; then url="https://192.168.1.40"; fi
if [ "$BASE_PINNED" = true ] && curl -fsS --max-time 5 --cacert keys/pkg-cache/ca.crt -o /dev/null https://192.168.1.40:5000/v2/; then mirror="192.168.1.40:5000"; fi
echo "pkg-cache: repos=${url:-<unreachable: upstream>} registry-mirror=${mirror:-<not used: Docker Hub direct>}"
[ -n "$url" ] || echo "::warning::pkg-cache unreachable - building from upstream repositories"
echo "url=${url}" >> "$GITHUB_OUTPUT"
echo "mirror=${mirror}" >> "$GITHUB_OUTPUT"
- name: Set up QEMU - name: Set up QEMU
uses: docker/setup-qemu-action@v3 uses: docker/setup-qemu-action@v3
# BuildKit (docker-container driver) ignores dockerd's registry-mirrors,
# so the mirror is configured on the builder; BuildKit itself still falls
# back to Docker Hub if the mirror stops answering mid-run.
- name: Set up Docker Buildx (Docker Hub via pkg-cache mirror)
if: steps.pkgcache.outputs.mirror != ''
uses: docker/setup-buildx-action@v3
with:
buildkitd-config-inline: |
[registry."docker.io"]
mirrors = ["192.168.1.40:5000"]
[registry."192.168.1.40:5000"]
ca = ["${{ github.workspace }}/keys/pkg-cache/ca.crt"]
- name: Set up Docker Buildx - name: Set up Docker Buildx
if: steps.pkgcache.outputs.mirror == ''
uses: docker/setup-buildx-action@v3 uses: docker/setup-buildx-action@v3
# Per-run cache-bust value. Each job's buildx builder is created fresh by # Per-run cache-bust value. Each job's buildx builder is created fresh by
@@ -462,7 +627,9 @@ jobs:
push: true push: true
build-args: | build-args: |
PHPVER=${{ matrix.phpver }} PHPVER=${{ matrix.phpver }}
OLS_VERSION=1.8.4 OLS_VERSION=${{ env.OLS_VERSION }}
BASE_IMAGE=${{ steps.base.outputs.image }}
PKG_CACHE=${{ steps.pkgcache.outputs.url }}
CACHE_BUST=${{ steps.bust.outputs.value }} CACHE_BUST=${{ steps.bust.outputs.value }}
EXPECT_LSPHP_PKG=${{ steps.lsphp.outputs.pkg }} EXPECT_LSPHP_PKG=${{ steps.lsphp.outputs.pkg }}
EXPECT_PHP_VERSION=${{ steps.lsphp.outputs.php }} EXPECT_PHP_VERSION=${{ steps.lsphp.outputs.php }}
@@ -478,6 +645,26 @@ jobs:
- name: Checkout - name: Checkout
uses: actions/checkout@v4 uses: actions/checkout@v4
# Base image pinned by DIGEST, looked up directly at Docker Hub (never via
# the pkg-cache mirror), so the mirror can only serve content-addressed
# blobs that BuildKit verifies. If the lookup fails this job does NOT use
# the mirror and pulls the tag straight from Docker Hub. docs/pkg-cache.md.
- name: Resolve base image digest (Docker Hub, direct)
id: base
env:
BASE_TAG: almalinux/10-base:latest
run: |
set -uo pipefail
if ref=$(.gitea/scripts/resolve-base-image.sh "$BASE_TAG") && [[ "$ref" =~ ^[a-z0-9][a-z0-9._/-]*@sha256:[0-9a-f]{64}$ ]]; then
echo "base-image: ${BASE_TAG} -> ${ref} (digest from registry-1.docker.io)"
echo "image=${ref}" >> "$GITHUB_OUTPUT"
echo "pinned=true" >> "$GITHUB_OUTPUT"
else
echo "::warning::base image ${BASE_TAG}: Docker Hub digest lookup failed - pulling the tag directly from Docker Hub, pkg-cache registry mirror NOT used"
echo "image=${BASE_TAG}" >> "$GITHUB_OUTPUT"
echo "pinned=false" >> "$GITHUB_OUTPUT"
fi
# Internal package cache (CT130 pkg-cache, docs/pkg-cache.md). Used ONLY if # Internal package cache (CT130 pkg-cache, docs/pkg-cache.md). Used ONLY if
# it answers over TLS with our pinned CA; otherwise (down, unreachable, # it answers over TLS with our pinned CA; otherwise (down, unreachable,
# bad/expired cert) this job builds from the upstream repos and Docker Hub # bad/expired cert) this job builds from the upstream repos and Docker Hub
@@ -485,11 +672,14 @@ jobs:
# separately so either can fail back on its own. # separately so either can fail back on its own.
- name: Probe package cache - name: Probe package cache
id: pkgcache id: pkgcache
# The registry mirror is only used for a digest-pinned base image.
env:
BASE_PINNED: ${{ steps.base.outputs.pinned }}
run: | run: |
url=""; mirror="" url=""; mirror=""
if curl -fsS --max-time 5 --cacert keys/pkg-cache/ca.crt -o /dev/null https://192.168.1.40/; then url="https://192.168.1.40"; fi if curl -fsS --max-time 5 --cacert keys/pkg-cache/ca.crt -o /dev/null https://192.168.1.40/; then url="https://192.168.1.40"; fi
if curl -fsS --max-time 5 --cacert keys/pkg-cache/ca.crt -o /dev/null https://192.168.1.40:5000/v2/; then mirror="192.168.1.40:5000"; fi if [ "$BASE_PINNED" = true ] && curl -fsS --max-time 5 --cacert keys/pkg-cache/ca.crt -o /dev/null https://192.168.1.40:5000/v2/; then mirror="192.168.1.40:5000"; fi
echo "pkg-cache: repos=${url:-<unreachable: upstream>} registry-mirror=${mirror:-<unreachable: Docker Hub>}" echo "pkg-cache: repos=${url:-<unreachable: upstream>} registry-mirror=${mirror:-<not used: Docker Hub direct>}"
[ -n "$url" ] || echo "::warning::pkg-cache unreachable - building from upstream repositories" [ -n "$url" ] || echo "::warning::pkg-cache unreachable - building from upstream repositories"
echo "url=${url}" >> "$GITHUB_OUTPUT" echo "url=${url}" >> "$GITHUB_OUTPUT"
echo "mirror=${mirror}" >> "$GITHUB_OUTPUT" echo "mirror=${mirror}" >> "$GITHUB_OUTPUT"
@@ -528,16 +718,41 @@ jobs:
platforms: linux/amd64 platforms: linux/amd64
push: true push: true
build-args: | build-args: |
BASE_IMAGE=${{ steps.base.outputs.image }}
PKG_CACHE=${{ steps.pkgcache.outputs.url }} PKG_CACHE=${{ steps.pkgcache.outputs.url }}
tags: | tags: |
repo.anhonesthost.net/cloud-hosting-platform/shared-httpd:latest repo.anhonesthost.net/cloud-hosting-platform/shared-httpd:latest
Build-Shared-OLS: Build-Shared-OLS:
runs-on: ubuntu-latest runs-on: ubuntu-latest
# One place for the OLS base tag: digest lookup and build-args both read it.
env:
OLS_VERSION: "1.8.4"
PHPVER: "83"
steps: steps:
- name: Checkout - name: Checkout
uses: actions/checkout@v4 uses: actions/checkout@v4
# Base image pinned by DIGEST, looked up directly at Docker Hub (never via
# the pkg-cache mirror), so the mirror can only serve content-addressed
# blobs that BuildKit verifies. If the lookup fails this job does NOT use
# the mirror and pulls the tag straight from Docker Hub. docs/pkg-cache.md.
- name: Resolve base image digest (Docker Hub, direct)
id: base
env:
BASE_TAG: litespeedtech/openlitespeed:${{ env.OLS_VERSION }}-lsphp${{ env.PHPVER }}
run: |
set -uo pipefail
if ref=$(.gitea/scripts/resolve-base-image.sh "$BASE_TAG") && [[ "$ref" =~ ^[a-z0-9][a-z0-9._/-]*@sha256:[0-9a-f]{64}$ ]]; then
echo "base-image: ${BASE_TAG} -> ${ref} (digest from registry-1.docker.io)"
echo "image=${ref}" >> "$GITHUB_OUTPUT"
echo "pinned=true" >> "$GITHUB_OUTPUT"
else
echo "::warning::base image ${BASE_TAG}: Docker Hub digest lookup failed - pulling the tag directly from Docker Hub, pkg-cache registry mirror NOT used"
echo "image=${BASE_TAG}" >> "$GITHUB_OUTPUT"
echo "pinned=false" >> "$GITHUB_OUTPUT"
fi
# Internal package cache (CT130 pkg-cache, docs/pkg-cache.md). Used ONLY if # Internal package cache (CT130 pkg-cache, docs/pkg-cache.md). Used ONLY if
# it answers over TLS with our pinned CA; otherwise (down, unreachable, # it answers over TLS with our pinned CA; otherwise (down, unreachable,
# bad/expired cert) this job builds from the upstream repos and Docker Hub # bad/expired cert) this job builds from the upstream repos and Docker Hub
@@ -545,11 +760,14 @@ jobs:
# separately so either can fail back on its own. # separately so either can fail back on its own.
- name: Probe package cache - name: Probe package cache
id: pkgcache id: pkgcache
# The registry mirror is only used for a digest-pinned base image.
env:
BASE_PINNED: ${{ steps.base.outputs.pinned }}
run: | run: |
url=""; mirror="" url=""; mirror=""
if curl -fsS --max-time 5 --cacert keys/pkg-cache/ca.crt -o /dev/null https://192.168.1.40/; then url="https://192.168.1.40"; fi if curl -fsS --max-time 5 --cacert keys/pkg-cache/ca.crt -o /dev/null https://192.168.1.40/; then url="https://192.168.1.40"; fi
if curl -fsS --max-time 5 --cacert keys/pkg-cache/ca.crt -o /dev/null https://192.168.1.40:5000/v2/; then mirror="192.168.1.40:5000"; fi if [ "$BASE_PINNED" = true ] && curl -fsS --max-time 5 --cacert keys/pkg-cache/ca.crt -o /dev/null https://192.168.1.40:5000/v2/; then mirror="192.168.1.40:5000"; fi
echo "pkg-cache: repos=${url:-<unreachable: upstream>} registry-mirror=${mirror:-<unreachable: Docker Hub>}" echo "pkg-cache: repos=${url:-<unreachable: upstream>} registry-mirror=${mirror:-<not used: Docker Hub direct>}"
[ -n "$url" ] || echo "::warning::pkg-cache unreachable - building from upstream repositories" [ -n "$url" ] || echo "::warning::pkg-cache unreachable - building from upstream repositories"
echo "url=${url}" >> "$GITHUB_OUTPUT" echo "url=${url}" >> "$GITHUB_OUTPUT"
echo "mirror=${mirror}" >> "$GITHUB_OUTPUT" echo "mirror=${mirror}" >> "$GITHUB_OUTPUT"
@@ -590,8 +808,9 @@ jobs:
# Single image (runs no PHP). PHPVER just selects the OLS base tag; # Single image (runs no PHP). PHPVER just selects the OLS base tag;
# pinned to 83 / OLS 1.8.4 to match the rest of the litespeed family. # pinned to 83 / OLS 1.8.4 to match the rest of the litespeed family.
build-args: | build-args: |
PHPVER=83 PHPVER=${{ env.PHPVER }}
OLS_VERSION=1.8.4 OLS_VERSION=${{ env.OLS_VERSION }}
BASE_IMAGE=${{ steps.base.outputs.image }}
PKG_CACHE=${{ steps.pkgcache.outputs.url }} PKG_CACHE=${{ steps.pkgcache.outputs.url }}
tags: | tags: |
repo.anhonesthost.net/cloud-hosting-platform/shared-ols:latest repo.anhonesthost.net/cloud-hosting-platform/shared-ols:latest
+12 -2
View File
@@ -1,4 +1,9 @@
FROM almalinux/10-base # Base image. CI passes BASE_IMAGE=almalinux/10-base@sha256:<digest>, resolved
# DIRECTLY from Docker Hub in the same run (.gitea/scripts/resolve-base-image.sh),
# so the pkg-cache registry mirror can only serve content-addressed blobs.
# Local builds use the tag. docs/pkg-cache.md "Base-image digest pinning".
ARG BASE_IMAGE=almalinux/10-base
FROM ${BASE_IMAGE}
ARG PHPVER=83 ARG PHPVER=83
# Per-CI-run value: the first RUN below (dnf update) and everything after it can # Per-CI-run value: the first RUN below (dnf update) and everything after it can
# never be a layer-cache hit, so every build gets the repos' current packages. # never be a layer-cache hit, so every build gets the repos' current packages.
@@ -82,7 +87,12 @@ COPY ./configs/whp-security.conf /etc/httpd/conf.d/
# Set up cron job in a single layer # Set up cron job in a single layer
RUN echo "15 */12 * * * root /scripts/log-rotate.sh" >> /etc/crontab RUN echo "15 */12 * * * root /scripts/log-rotate.sh" >> /etc/crontab
## Healthy = Apache answered with anything but a 5xx. `curl -f` also failed on
## 4xx, so a new site with an empty docroot (403) or an app with no route for /
## (404) showed unhealthy forever, and WHP's monitor restarted it as "PHP
## wedged". 000 (no answer) and 5xx (PHP/app error) still fail.
HEALTHCHECK --interval=30s --timeout=5s --start-period=60s --retries=3 \ HEALTHCHECK --interval=30s --timeout=5s --start-period=60s --retries=3 \
CMD curl -f http://localhost/ || exit 1 CMD code=$(curl -s -o /dev/null -m 4 -w '%{http_code}' http://localhost/); \
case "$code" in 000|5*) echo "http $code"; exit 1;; esac
ENTRYPOINT [ "/scripts/entrypoint.sh" ] ENTRYPOINT [ "/scripts/entrypoint.sh" ]
+6 -1
View File
@@ -1,4 +1,9 @@
FROM almalinux/10-base # Base image. CI passes BASE_IMAGE=almalinux/10-base@sha256:<digest>, resolved
# DIRECTLY from Docker Hub in the same run (.gitea/scripts/resolve-base-image.sh),
# so the pkg-cache registry mirror can only serve content-addressed blobs.
# Local builds use the tag. docs/pkg-cache.md "Base-image digest pinning".
ARG BASE_IMAGE=almalinux/10-base
FROM ${BASE_IMAGE}
ARG PHPVER=83 ARG PHPVER=83
# Per-CI-run value: the first RUN below (dnf update) and everything after it can # Per-CI-run value: the first RUN below (dnf update) and everything after it can
# never be a layer-cache hit, so every build gets the repos' current packages. # never be a layer-cache hit, so every build gets the repos' current packages.
+18 -3
View File
@@ -22,12 +22,18 @@
## after FROM for any RUN steps that need it. ## after FROM for any RUN steps that need it.
ARG OLS_VERSION=1.8.5 ARG OLS_VERSION=1.8.5
ARG PHPVER=83 ARG PHPVER=83
## CI passes BASE_IMAGE=litespeedtech/openlitespeed@sha256:<digest> of the
## ${OLS_VERSION}-lsphp${PHPVER} tag, resolved DIRECTLY from Docker Hub in the same
## run (.gitea/scripts/resolve-base-image.sh), so the pkg-cache registry mirror can
## only serve content-addressed blobs. Local builds use the tag. Every stage below
## (incl. the emergency lsphp-src stage) starts from this one image.
ARG BASE_IMAGE=litespeedtech/openlitespeed:${OLS_VERSION}-lsphp${PHPVER}
## ---- EMERGENCY ONLY: lsphp compiled from php.net source ---------------------- ## ---- EMERGENCY ONLY: lsphp compiled from php.net source ----------------------
## Idle unless PHP_SRC_VERSION is set (then /lsphp-src-out stays empty and the ## Idle unless PHP_SRC_VERSION is set (then /lsphp-src-out stays empty and the
## overlay step below is a no-op). Same stage as Dockerfile.lsphp — see there and ## overlay step below is a no-op). Same stage as Dockerfile.lsphp — see there and
## docs/lsphp-source-emergency.md. ## docs/lsphp-source-emergency.md.
FROM litespeedtech/openlitespeed:${OLS_VERSION}-lsphp${PHPVER} AS lsphp-src FROM ${BASE_IMAGE} AS lsphp-src
ARG PHPVER=83 ARG PHPVER=83
ARG CACHE_BUST= ARG CACHE_BUST=
ARG EXPECT_LSPHP_PKG= ARG EXPECT_LSPHP_PKG=
@@ -42,7 +48,7 @@ RUN --mount=type=bind,source=scripts,target=/mnt/scripts \
fi; \ fi; \
PHPVER="${PHPVER}" bash /mnt/scripts/lsphp-src-build.sh PHPVER="${PHPVER}" bash /mnt/scripts/lsphp-src-build.sh
FROM litespeedtech/openlitespeed:${OLS_VERSION}-lsphp${PHPVER} FROM ${BASE_IMAGE}
ARG PHPVER=83 ARG PHPVER=83
ENV PHPVER=${PHPVER} ENV PHPVER=${PHPVER}
@@ -80,11 +86,20 @@ ENV PHPVER=${PHPVER}
ARG CACHE_BUST= ARG CACHE_BUST=
ARG EXPECT_LSPHP_PKG= ARG EXPECT_LSPHP_PKG=
ARG EXPECT_PHP_VERSION= ARG EXPECT_PHP_VERSION=
## OPTIONAL internal package cache, e.g. --build-arg PKG_CACHE=https://192.168.1.40
## (CT130 pkg-cache, https only). Unset = upstream repositories (Ubuntu +
## rpms.litespeedtech.com). Repo files are restored at the end of the RUN, so no
## layer keeps the cache address. See scripts/pkg-cache-repos.sh.
ARG PKG_CACHE=
COPY ./scripts/pkg-cache-repos.sh /usr/local/sbin/pkg-cache-repos.sh
COPY ./keys/pkg-cache/ /usr/local/share/pkg-cache/
COPY ./scripts/lsphp-apt-upgrade.sh /usr/local/sbin/lsphp-apt-upgrade.sh COPY ./scripts/lsphp-apt-upgrade.sh /usr/local/sbin/lsphp-apt-upgrade.sh
RUN /usr/local/sbin/lsphp-apt-upgrade.sh "${PHPVER}" \ RUN /usr/local/sbin/pkg-cache-repos.sh apt && \
/usr/local/sbin/lsphp-apt-upgrade.sh "${PHPVER}" \
gettext-base sudo cron \ gettext-base sudo cron \
ca-certificates curl wget \ ca-certificates curl wget \
lsphp${PHPVER}-ldap && \ lsphp${PHPVER}-ldap && \
/usr/local/sbin/pkg-cache-repos.sh restore && \
apt-get clean && \ apt-get clean && \
rm -rf /var/lib/apt/lists/* /var/cache/apt/archives/* rm -rf /var/lib/apt/lists/* /var/cache/apt/archives/*
+27 -3
View File
@@ -21,6 +21,12 @@
ARG OLS_VERSION=1.8.4 ARG OLS_VERSION=1.8.4
ARG PHPVER=83 ARG PHPVER=83
## CI passes BASE_IMAGE=litespeedtech/openlitespeed@sha256:<digest> of the
## ${OLS_VERSION}-lsphp${PHPVER} tag, resolved DIRECTLY from Docker Hub in the same
## run (.gitea/scripts/resolve-base-image.sh), so the pkg-cache registry mirror can
## only serve content-addressed blobs. Local builds use the tag. Every stage below
## (incl. the emergency lsphp-src stage) starts from this one image.
ARG BASE_IMAGE=litespeedtech/openlitespeed:${OLS_VERSION}-lsphp${PHPVER}
## ---- stage 0 (EMERGENCY ONLY): lsphp compiled from php.net source ----------- ## ---- stage 0 (EMERGENCY ONLY): lsphp compiled from php.net source -----------
## Idle unless PHP_SRC_VERSION is set: lsphp-src-build.sh then exits at once and ## Idle unless PHP_SRC_VERSION is set: lsphp-src-build.sh then exits at once and
@@ -30,7 +36,7 @@ ARG PHPVER=83
## — CI: .gitea/workflows/lsphp-source-emergency.yaml; runbook: ## — CI: .gitea/workflows/lsphp-source-emergency.yaml; runbook:
## docs/lsphp-source-emergency.md. The tarball is sha256- AND OpenPGP-verified ## docs/lsphp-source-emergency.md. The tarball is sha256- AND OpenPGP-verified
## against keys/php-src/; configure flags are replayed from the packaged lsphp. ## against keys/php-src/; configure flags are replayed from the packaged lsphp.
FROM litespeedtech/openlitespeed:${OLS_VERSION}-lsphp${PHPVER} AS lsphp-src FROM ${BASE_IMAGE} AS lsphp-src
ARG PHPVER=83 ARG PHPVER=83
ARG CACHE_BUST= ARG CACHE_BUST=
ARG EXPECT_LSPHP_PKG= ARG EXPECT_LSPHP_PKG=
@@ -77,7 +83,7 @@ RUN --mount=type=bind,source=scripts,target=/mnt/scripts \
## Separate stage on purpose: the compiler + headers (~400MB) stay out of the ## Separate stage on purpose: the compiler + headers (~400MB) stay out of the
## shipped image, which gains only the ~40KB .so. Costs ~1-2 min of CI per PHP ## shipped image, which gains only the ~40KB .so. Costs ~1-2 min of CI per PHP
## version; both stages share the same base layer, so no extra pull. ## version; both stages share the same base layer, so no extra pull.
FROM litespeedtech/openlitespeed:${OLS_VERSION}-lsphp${PHPVER} AS ext-build FROM ${BASE_IMAGE} AS ext-build
ARG PHPVER=83 ARG PHPVER=83
## Toolchain layer, deliberately BEFORE the source COPY so editing the extension ## Toolchain layer, deliberately BEFORE the source COPY so editing the extension
@@ -94,11 +100,20 @@ ARG PHPVER=83
ARG CACHE_BUST= ARG CACHE_BUST=
ARG EXPECT_LSPHP_PKG= ARG EXPECT_LSPHP_PKG=
ARG EXPECT_PHP_VERSION= ARG EXPECT_PHP_VERSION=
## OPTIONAL internal package cache, e.g. --build-arg PKG_CACHE=https://192.168.1.40
## (CT130 pkg-cache, https only). Unset = upstream repositories (Ubuntu +
## rpms.litespeedtech.com). Repo files are restored at the end of the RUN, so no
## layer keeps the cache address. See scripts/pkg-cache-repos.sh.
ARG PKG_CACHE=
COPY ./scripts/pkg-cache-repos.sh /usr/local/sbin/pkg-cache-repos.sh
COPY ./keys/pkg-cache/ /usr/local/share/pkg-cache/
COPY ./scripts/lsphp-apt-upgrade.sh /usr/local/sbin/lsphp-apt-upgrade.sh COPY ./scripts/lsphp-apt-upgrade.sh /usr/local/sbin/lsphp-apt-upgrade.sh
RUN set -e; \ RUN set -e; \
/usr/local/sbin/pkg-cache-repos.sh apt; \
/usr/local/sbin/lsphp-apt-upgrade.sh --no-os-upgrade "${PHPVER}" \ /usr/local/sbin/lsphp-apt-upgrade.sh --no-os-upgrade "${PHPVER}" \
build-essential autoconf pkg-config \ build-essential autoconf pkg-config \
lsphp${PHPVER}-dev; \ lsphp${PHPVER}-dev; \
/usr/local/sbin/pkg-cache-repos.sh restore; \
RTV=$(dpkg-query -W -f='${Version}' lsphp${PHPVER}); \ RTV=$(dpkg-query -W -f='${Version}' lsphp${PHPVER}); \
DEVV=$(dpkg-query -W -f='${Version}' lsphp${PHPVER}-dev); \ DEVV=$(dpkg-query -W -f='${Version}' lsphp${PHPVER}-dev); \
if [ "$RTV" != "$DEVV" ]; then \ if [ "$RTV" != "$DEVV" ]; then \
@@ -168,7 +183,7 @@ RUN set -e; \
cp modules/cac_path_parity.so /build-out/ cp modules/cac_path_parity.so /build-out/
## ---- stage 2: the shipped sidecar image ------------------------------------ ## ---- stage 2: the shipped sidecar image ------------------------------------
FROM litespeedtech/openlitespeed:${OLS_VERSION}-lsphp${PHPVER} FROM ${BASE_IMAGE}
ARG PHPVER=83 ARG PHPVER=83
ENV PHPVER=${PHPVER} ENV PHPVER=${PHPVER}
@@ -196,10 +211,18 @@ ENV PHPVER=${PHPVER}
ARG CACHE_BUST= ARG CACHE_BUST=
ARG EXPECT_LSPHP_PKG= ARG EXPECT_LSPHP_PKG=
ARG EXPECT_PHP_VERSION= ARG EXPECT_PHP_VERSION=
## OPTIONAL internal package cache, e.g. --build-arg PKG_CACHE=https://192.168.1.40
## (CT130 pkg-cache, https only). Unset = upstream repositories (Ubuntu +
## rpms.litespeedtech.com). Repo files are restored at the end of the RUN, so no
## layer keeps the cache address. See scripts/pkg-cache-repos.sh.
ARG PKG_CACHE=
COPY ./scripts/pkg-cache-repos.sh /usr/local/sbin/pkg-cache-repos.sh
COPY ./keys/pkg-cache/ /usr/local/share/pkg-cache/
COPY --from=ext-build /build-out/lsphp.version /etc/cac-lsphp-build.version COPY --from=ext-build /build-out/lsphp.version /etc/cac-lsphp-build.version
COPY ./scripts/lsphp-apt-upgrade.sh /usr/local/sbin/lsphp-apt-upgrade.sh COPY ./scripts/lsphp-apt-upgrade.sh /usr/local/sbin/lsphp-apt-upgrade.sh
RUN set -e; \ RUN set -e; \
V=$(cat /etc/cac-lsphp-build.version); \ V=$(cat /etc/cac-lsphp-build.version); \
/usr/local/sbin/pkg-cache-repos.sh apt; \
if ! /usr/local/sbin/lsphp-apt-upgrade.sh "${PHPVER}" \ if ! /usr/local/sbin/lsphp-apt-upgrade.sh "${PHPVER}" \
ca-certificates \ ca-certificates \
lsphp${PHPVER}="$V" lsphp${PHPVER}-common="$V" lsphp${PHPVER}-ldap="$V"; then \ lsphp${PHPVER}="$V" lsphp${PHPVER}-common="$V" lsphp${PHPVER}-ldap="$V"; then \
@@ -211,6 +234,7 @@ RUN set -e; \
echo " build (CI) / rebuild with --no-cache (local)." >&2; \ echo " build (CI) / rebuild with --no-cache (local)." >&2; \
exit 1; \ exit 1; \
fi; \ fi; \
/usr/local/sbin/pkg-cache-repos.sh restore; \
apt-get clean; \ apt-get clean; \
rm -rf /var/lib/apt/lists/* /var/cache/apt/archives/*; \ rm -rf /var/lib/apt/lists/* /var/cache/apt/archives/*; \
RTV=$(dpkg-query -W -f='${Version}' lsphp${PHPVER}); \ RTV=$(dpkg-query -W -f='${Version}' lsphp${PHPVER}); \
+6 -1
View File
@@ -1,4 +1,9 @@
FROM almalinux/10-base # Base image. CI passes BASE_IMAGE=almalinux/10-base@sha256:<digest>, resolved
# DIRECTLY from Docker Hub in the same run (.gitea/scripts/resolve-base-image.sh),
# so the pkg-cache registry mirror can only serve content-addressed blobs.
# Local builds use the tag. docs/pkg-cache.md "Base-image digest pinning".
ARG BASE_IMAGE=almalinux/10-base
FROM ${BASE_IMAGE}
# OPTIONAL internal package cache, e.g. --build-arg PKG_CACHE=https://192.168.1.40 # OPTIONAL internal package cache, e.g. --build-arg PKG_CACHE=https://192.168.1.40
# (CT130 pkg-cache, https only). Unset = upstream repositories. Release RPMs are # (CT130 pkg-cache, https only). Unset = upstream repositories. Release RPMs are
+6 -1
View File
@@ -17,7 +17,12 @@
ARG OLS_VERSION=1.8.4 ARG OLS_VERSION=1.8.4
ARG PHPVER=83 ARG PHPVER=83
FROM litespeedtech/openlitespeed:${OLS_VERSION}-lsphp${PHPVER} ## CI passes BASE_IMAGE=litespeedtech/openlitespeed@sha256:<digest> of the
## ${OLS_VERSION}-lsphp${PHPVER} tag, resolved DIRECTLY from Docker Hub in the same
## run (.gitea/scripts/resolve-base-image.sh), so the pkg-cache registry mirror can
## only serve content-addressed blobs. Local builds use the tag.
ARG BASE_IMAGE=litespeedtech/openlitespeed:${OLS_VERSION}-lsphp${PHPVER}
FROM ${BASE_IMAGE}
## OPTIONAL internal package cache, e.g. --build-arg PKG_CACHE=https://192.168.1.40 ## OPTIONAL internal package cache, e.g. --build-arg PKG_CACHE=https://192.168.1.40
## (CT130 pkg-cache, https only). Unset = upstream repositories. Release RPMs are ## (CT130 pkg-cache, https only). Unset = upstream repositories. Release RPMs are
+71 -3
View File
@@ -21,6 +21,10 @@ On that day a build without the cache spent **1031 s** in its first dnf step. Th
| `https://192.168.1.40/ubuntu/` | `https://archive.ubuntu.com/ubuntu`, `https://us.archive.ubuntu.com/ubuntu` | | `https://192.168.1.40/ubuntu/` | `https://archive.ubuntu.com/ubuntu`, `https://us.archive.ubuntu.com/ubuntu` |
| `https://192.168.1.40/ubuntu-security/` | `https://security.ubuntu.com/ubuntu`, `https://archive.ubuntu.com/ubuntu` | | `https://192.168.1.40/ubuntu-security/` | `https://security.ubuntu.com/ubuntu`, `https://archive.ubuntu.com/ubuntu` |
| `https://192.168.1.40/litespeed/` | `https://rpms.litespeedtech.com`, then `http://rpms.litespeedtech.com` | | `https://192.168.1.40/litespeed/` | `https://rpms.litespeedtech.com`, then `http://rpms.litespeedtech.com` |
| `https://192.168.1.40/nodesource/` | `https://rpm.nodesource.com` (origin only; RPMs signed, repomd not) — added 2026-10-22 |
| `https://192.168.1.40/debian/` | `https://deb.debian.org/debian` (CDN origin; princeton was 6 h behind on trixie-updates) — added 2026-10-22 |
| `https://192.168.1.40/debian-security/` | `https://security.debian.org/debian-security`, `https://deb.debian.org/debian-security` — added 2026-10-22 |
| `https://192.168.1.40/goproxy/` | `https://proxy.golang.org` (Go module proxy; `/sumdb/` is 404 there, so `go` checks sum.golang.org directly) — added 2026-10-22 |
| `192.168.1.40:5000` (TLS, pkg-cache CA) | Docker Hub pull-through cache (`registry-1.docker.io`) | | `192.168.1.40:5000` (TLS, pkg-cache CA) | Docker Hub pull-through cache (`registry-1.docker.io`) |
Plain `http://192.168.1.40/` answers **403** to LAN clients. Plain `http://192.168.1.40/` answers **403** to LAN clients.
@@ -32,6 +36,7 @@ Cache rules:
- apt `dists/`, except `by-hash/` - apt `dists/`, except `by-hash/`
- `*-latest*.rpm` and `remi-release-N.rpm` - `*-latest*.rpm` and `remi-release-N.rpm`
- `RPM-GPG-KEY*` - `RPM-GPG-KEY*`
- Go proxy `/@v/list` and `/@latest`
- directory listings - directory listings
- **Packages** (`*.rpm`, `*.deb`, hash-named repodata, `by-hash/`) are cached for 30 days and evicted after 60 days unused. Total size is capped at 110 GB, with 15 GB kept free. - **Packages** (`*.rpm`, `*.deb`, hash-named repodata, `by-hash/`) are cached for 30 days and evicted after 60 days unused. Total size is capped at 110 GB, with 15 GB kept free.
- On a connect error, timeout or 5xx, the request falls through to the next tier. Only the **last** tier may answer from a stale copy, so the order is primary, then fallback, then stale. - On a connect error, timeout or 5xx, the request falls through to the next tier. Only the **last** tier may answer from a stale copy, so the order is primary, then fallback, then stale.
@@ -65,6 +70,18 @@ docker buildx build --build-arg PKG_CACHE=https://192.168.1.40 --build-arg PHPVE
- Requires `rpm -K` to report `digests signatures OK`. - Requires `rpm -K` to report `digests signatures OK`.
- Installs with `--setopt=localpkg_gpgcheck=1`. - Installs with `--setopt=localpkg_gpgcheck=1`.
**`nodesource MAJOR`** (added 2026-10-22) replaces `curl https://rpm.nodesource.com/setup_N.x | bash`:
- writes `/etc/yum.repos.d/nodesource-nodejs.repo` with the ORIGIN baseurl, `gpgcheck=1` and `gpgkey=file:///usr/local/share/pkg-cache/RPM-GPG-KEY-nodesource` (key committed in `keys/pkg-cache/`);
- with `PKG_CACHE` it is then rewritten like the other repos; `restore` puts the origin version back, so the shipped image keeps a normal NodeSource repo.
**`go-env`** (added 2026-10-22): `eval "$(pkg-cache-repos.sh go-env)"` inside the RUN that runs `go mod download`/`go build`. Prints `GOPROXY=<cache>/goproxy|https://proxy.golang.org|direct` (`|` = fall through on ANY error) and `SSL_CERT_FILE=<state>/ca-bundle.pem`; nothing without `PKG_CACHE`. Modules stay verified by `go.sum` + sum.golang.org. End the RUN with `restore` (removes the bundle).
**EL9**: `release-install epel|remi` picks the EL major from `rpm -E %rhel` (9 or 10); `RPM-GPG-KEY-EPEL-9` is committed.
**Debian** (`apt` mode): `deb.debian.org/debian`, `deb.debian.org/debian-security` and `security.debian.org` (classic and deb822 `.sources`) are rewritten to `<cache>/debian/` and `<cache>/debian-security/`.
**Not proxied, on purpose:** PyPI and the npm registry. Our pip/npm installs are not hash-locked (`pip install -r` without `--require-hashes`, `npm install -g pm2@latest`), so the client would trust whatever the cache served — a compromised cache would mean code execution in shipped images. Proxy them only after the installs are hash-pinned. GitHub-release downloads are likewise left direct.
**`restore`**: **`restore`**:
- Puts rewritten files back. - Puts rewritten files back.
- Deletes files the script created. - Deletes files the script created.
@@ -75,9 +92,31 @@ Every RUN that rewrites ends with `restore`, so no shipped layer carries the LAN
`PKG_CACHE` must start with `https://`. `http://` is refused. `PKG_CACHE` must start with `https://`. `http://` is refused.
`Dockerfile.litespeed` and `Dockerfile.lsphp` are **not wired yet**. Two things are needed first: `Dockerfile.litespeed` and `Dockerfile.lsphp` are wired since 2026-10-22 (normal apt path; the php.net EMERGENCY stage `lsphp-src` stays on upstream). `lsphp-apt-upgrade.sh`'s madison gate accepts `<PKG_CACHE>/litespeed/` only when `PKG_CACHE` is set.
- the multi-stage `lsphp-apt-upgrade.sh` flow needs a `PKG_CACHE` ARG in each stage;
- its madison check must accept the cache URL. It currently requires `*rpms.litespeedtech.com*`, and via the cache madison shows `https://192.168.1.40/litespeed/debian`. ## Repositories wired (2026-10-22)
The script and `keys/pkg-cache/` are **canonical here**; every other repo carries byte-identical copies (`sha256sum scripts/pkg-cache-repos.sh` to compare). Change it here first, then sync. Same for `.gitea/scripts/resolve-base-image.sh`.
| Repo | Image(s) | Bases | Through the cache | Base digest-pinned |
|---|---|---|---|---|
| cloud-apache-container | cac, cac-fpm, shared-httpd (almalinux/10-base); shared-ols, cac-litespeed, cac-lsphp (litespeedtech/openlitespeed) | Docker Hub | AlmaLinux/EPEL/Remi (rpm); Ubuntu + LiteSpeed (apt) | yes, all 6 jobs |
| cloud-node-container | cnoc:node18..26 | almalinux/10-base | AlmaLinux/EPEL + NodeSource (`nodesource N`) | yes |
| cloud-memcached-container | cmc | almalinux/9-base | AlmaLinux 9 + EPEL 9 | yes |
| cloud-haproxy-container | chc | almalinux/9-base | AlmaLinux 9 + EPEL 9 (inotify-tools GitHub tarball direct) | yes |
| whp-default-container | whp-default | httpd:alpine | nothing to install (mirror only) | yes |
| versitygw-container | versitygw | ubuntu:latest | Ubuntu (versitygw .deb from GitHub direct) | yes |
| haproxy-manager-base | haproxy-manager-base, coraza-spoa | repo.anhonesthost.net python:3.12-slim / golang:1.25 (not Docker Hub: no mirror); gcr.io distroless | Debian (apt) + Go modules (`go-env`); pip direct | n/a; `mirror-base-image.yaml` now copies Docker Hub images by the digest resolved directly from Docker Hub |
| cpanel-importer | cpanel-importer | almalinux:10-minimal | AlmaLinux/EPEL/Remi — **PR #1 open, not merged** (pending rebase onto the PHP 8.5 work) | in the PR |
Not image builds (nothing to wire): kb-anhonesthost, anhonesthost-blog, dao-mail-signup, renovate-runner. ols-mail-client builds images in CI only for scanning/smoke tests (never pushed), its bases are already digest-pinned, and they install no OS packages.
## Adding a new upstream
1. On CT130: back up, then add the prefix to `UPSTREAMS` in `/usr/local/sbin/pkgcache-gen-nginx` (origin first; add a mirror tier only if you measured it is as fresh as the origin). If its mutable metadata isn't covered by `META_RE`, extend it there AND in `/usr/local/sbin/pkgcache-refresh`. Add the prefix to the `location = /` text in `/etc/nginx/pkgcache/server-common.conf`.
2. `pkgcache-gen-nginx > /etc/nginx/sites-available/pkgcache && nginx -t && systemctl reload nginx` (`pct exec` has no /usr/local/sbin in PATH: use full paths). Verify old and new prefixes with `curl --cacert keys/pkg-cache/ca.crt https://192.168.1.40/<prefix>/...` (look at `X-Cache-Status`/`X-Pkgcache-Tier`). CT130 has no egress filter, so no firewall change is needed for new origins.
3. Only proxy upstreams whose content the CLIENT verifies end to end (GPG-signed packages/metadata, Go sumdb, content digests). Commit any signing key under `keys/pkg-cache/` with its fingerprint and provenance in the README.
4. Teach `scripts/pkg-cache-repos.sh` the rewrite (keep the origin as a fallback where the client supports it), test through the cache from a runner-network host, then sync the copies.
## Trust model ## Trust model
@@ -149,6 +188,35 @@ Remaining risks:
# /etc/docker/daemon.json: { "registry-mirrors": ["https://192.168.1.40:5000"] } then restart docker # /etc/docker/daemon.json: { "registry-mirrors": ["https://192.168.1.40:5000"] } then restart docker
``` ```
## Base-image digest pinning (2026-10-22)
Gap closed (security review): with the BuildKit mirror on, `FROM almalinux/10-base`
was resolved **by tag through the cache**, so a compromised cache could hand out a
different image for that tag.
Now every job that uses the mirror first runs **"Resolve base image digest (Docker
Hub, direct)"**:
- `.gitea/scripts/resolve-base-image.sh IMAGE:TAG` gets an anonymous pull token from
`auth.docker.io` and sends a `HEAD /v2/<repo>/manifests/<tag>` to
`registry-1.docker.io` over HTTPS (never the mirror; HEADs don't count against the
pull rate limit). It prints `IMAGE@sha256:<index digest>`. Input and output are
regex-validated; values reach `run:` only via `env:`.
- The job passes `BASE_IMAGE=<that>` as a build arg; the Dockerfiles say
`ARG BASE_IMAGE=<tag>` / `FROM ${BASE_IMAGE}`. The mirror can then only serve
content-addressed manifests/blobs, which BuildKit verifies against the digest.
- **Lookup fails:** `BASE_IMAGE` falls back to the tag, the probe step does **not**
enable the registry mirror (`registry-mirror=<not used: Docker Hub direct>`), and
the tag is pulled straight from Docker Hub, as before the cache existed. The
package-repo cache (`PKG_CACHE`) is unaffected, since its content is GPG-checked.
- Freshness is automatic: the digest is looked up on every run, nothing is
hard-coded. Local builds without the arg use the tag.
- Log lines to look for: `base-image: almalinux/10-base:latest -> almalinux/10-base@sha256:...`
and `pkg-cache: repos=https://192.168.1.40 registry-mirror=192.168.1.40:5000`.
Applied to every job here (Build-and-Push, FPM, Shared-httpd, Shared-OLS, LiteSpeed, LSPHP) and to every other repo with a Docker Hub base (see "Repositories wired").
The script is canonical here (`.gitea/scripts/resolve-base-image.sh`); other repos
carry byte-identical copies.
## WHP hosts (`scripts/host/pkgcache-host-repos.sh`) ## WHP hosts (`scripts/host/pkgcache-host-repos.sh`)
`pkgcache-host-repos.sh apply https://192.168.1.40` does the following: `pkgcache-host-repos.sh apply https://192.168.1.40` does the following:
+12
View File
@@ -18,3 +18,15 @@ RPM-GPG-KEY-remi2024 Remi's RPM repository 2024 CF1D F005 7CE8 5DFF 5B2F 2A37
Remi rotates keys (remi2025, ...): when remi-release starts being Remi rotates keys (remi2025, ...): when remi-release starts being
signed by a new key, `release-install remi` fails loudly - add the signed by a new key, `release-install remi` fails loudly - add the
new key here (and to pkg-cache-repos.sh). new key here (and to pkg-cache-repos.sh).
RPM-GPG-KEY-EPEL-9 Fedora (epel9) FF8A D134 4597 106E CE81 3B91 8A38 72BF 3228 467C
fetched 2026-10-22 over HTTPS from dl.fedoraproject.org and
mirror.math.princeton.edu (identical); used by `release-install epel`
on EL9 images (cloud-memcached-container, cloud-haproxy-container).
RPM-GPG-KEY-nodesource Nodesource Operations <operations@nodesource.com>
242B 8138 31AF 0956 2B6C 46F7 6B88 DA4E 3AF2 8A14 (rsa4096, 2023-12-22)
fetched 2026-10-22 over HTTPS from
https://rpm.nodesource.com/gpgkey/ns-operations-public.key (the key
NodeSource's own setup_N.x scripts reference); verified by dnf
gpgcheck against the nodejs RPMs. Not on keys.openpgp.org /
keyserver.ubuntu.com - confirm out of band. Used by
`pkg-cache-repos.sh nodesource MAJOR` (cloud-node-container).
+29
View File
@@ -0,0 +1,29 @@
-----BEGIN PGP PUBLIC KEY BLOCK-----
mQINBGE3mOsBEACsU+XwJWDJVkItBaugXhXIIkb9oe+7aadELuVo0kBmc3HXt/Yp
CJW9hHEiGZ6z2jwgPqyJjZhCvcAWvgzKcvqE+9i0NItV1rzfxrBe2BtUtZmVcuE6
2b+SPfxQ2Hr8llaawRjt8BCFX/ZzM4/1Qk+EzlfTcEcpkMf6wdO7kD6ulBk/tbsW
DHX2lNcxszTf+XP9HXHWJlA2xBfP+Dk4gl4DnO2Y1xR0OSywE/QtvEbN5cY94ieu
n7CBy29AleMhmbnx9pw3NyxcFIAsEZHJoU4ZW9ulAJ/ogttSyAWeacW7eJGW31/Z
39cS+I4KXJgeGRI20RmpqfH0tuT+X5Da59YpjYxkbhSK3HYBVnNPhoJFUc2j5iKy
XLgkapu1xRnEJhw05kr4LCbud0NTvfecqSqa+59kuVc+zWmfTnGTYc0PXZ6Oa3rK
44UOmE6eAT5zd/ToleDO0VesN+EO7CXfRsm7HWGpABF5wNK3vIEF2uRr2VJMvgqS
9eNwhJyOzoca4xFSwCkc6dACGGkV+CqhufdFBhmcAsUotSxe3zmrBjqA0B/nxIvH
DVgOAMnVCe+Lmv8T0mFgqZSJdIUdKjnOLu/GRFhjDKIak4jeMBMTYpVnU+HhMHLq
uDiZkNEvEEGhBQmZuI8J55F/a6UURnxUwT3piyi3Pmr2IFD7ahBxPzOBCQARAQAB
tCdGZWRvcmEgKGVwZWw5KSA8ZXBlbEBmZWRvcmFwcm9qZWN0Lm9yZz6JAk4EEwEI
ADgWIQT/itE0RZcQbs6BO5GKOHK/MihGfAUCYTeY6wIbDwULCQgHAgYVCgkICwIE
FgIDAQIeAQIXgAAKCRCKOHK/MihGfFX/EACBPWv20+ttYu1A5WvtHJPzwbj0U4yF
3zTQpBglQ2UfkRpYdipTlT3Ih6j5h2VmgRPtINCc/ZE28adrWpBoeFIS2YAKOCLC
nZYtHl2nCoLq1U7FSttUGsZ/t8uGCBgnugTfnIYcmlP1jKKA6RJAclK89evDQX5n
R9ZD+Cq3CBMlttvSTCht0qQVlwycedH8iWyYgP/mF0W35BIn7NuuZwWhgR00n/VG
4nbKPOzTWbsP45awcmivdrS74P6mL84WfkghipdmcoyVb1B8ZP4Y/Ke0RXOnLhNe
CfrXXvuW+Pvg2RTfwRDtehGQPAgXbmLmz2ZkV69RGIr54HJv84NDbqZovRTMr7gL
9k3ciCzXCiYQgM8yAyGHV0KEhFSQ1HV7gMnt9UmxbxBE2pGU7vu3CwjYga5DpwU7
w5wu1TmM5KgZtZvuWOTDnqDLf0cKoIbW8FeeCOn24elcj32bnQDuF9DPey1mqcvT
/yEo/Ushyz6CVYxN8DGgcy2M9JOsnmjDx02h6qgWGWDuKgb9jZrvRedpAQCeemEd
fhEs6ihqVxRFl16HxC4EVijybhAL76SsM2nbtIqW1apBQJQpXWtQwwdvgTVpdEtE
r4ArVJYX5LrswnWEQMOelugUG6S3ZjMfcyOa/O0364iY73vyVgaYK+2XtT2usMux
VL469Kj5m13T6w==
=Mjs/
-----END PGP PUBLIC KEY BLOCK-----
+52
View File
@@ -0,0 +1,52 @@
-----BEGIN PGP PUBLIC KEY BLOCK-----
mQINBGWFsqMBEAC4YlSuNpHjOiQVTC8StolVx3J8E9SC6G4wmVWbytMD1Zivfanj
3LKDqdy7vpbViXA9whpyGQgQYdOlPKaNHWw4GSeGTnP1FbvaK1NJ9MnAzUysh49R
cqoktYeIQbqgOgZHw5/gVH5bYObf1KJQ2LY/je9WTlW3QMRe1TaW0F0AZnJMBIOG
JllE33QIpxDnOQHnvdUGq+HscUOTrW4Q3AdzwsQxU4GX82d9qFFr8HjAmUjlUKyh
PgvMpb8VDvR7NE6et+wWh6BMYILo3VJFP7/gTXGiy3WWs5GW/PNXIXw5/f2w1B+e
Dh5tlfGG4q/4P8mbv7C6RPHoF2Lq+W69HLqSq/l42WUL41tHlFTsluCIKpeeMxAu
olxx3Z5momHMMyh7rbZtQDqFMTAqxNjT1NinKkWyj7KP5WE9hVosyd3UMR4UYDMm
Zb6LI4N7Hht887+CY7grxTJ9cGQhMFfvLyvlf/FWQY4S6G7sEaUV71N9StvXBNG/
iNoIMzhwjBmrmvT/UGPkLSi0SIBOxeGw2rwngnYikIJxuTQ4V40lm8o6eMFca7rW
BQvAEncV8pvi/3FqrUBZ/U53s7NO24hBDoaQPzFbVCw0GHHf9GD7KoQb5c9ONoCZ
Qu2YWo7ajPLzl/h2pJ2Kn29Ywev9MpuuPTGvifiKilcBVLYP3ZJg4ZfRBwARAQAB
tDFOb2Rlc291cmNlIE9wZXJhdGlvbnMgPG9wZXJhdGlvbnNAbm9kZXNvdXJjZS5j
b20+iQJOBBMBCgA4FiEEJCuBODGvCVYrbEb3a4jaTjryihQFAmWFsqMCGwMFCwkI
BwIGFQoJCAsCBBYCAwECHgECF4AACgkQa4jaTjryihTD+hAAtTU155ke7xmnT0ph
odbGjm3mOGdkdaDH+xmWtPtufiPncgHMCYh4aIDRloog8rFE+pP3qfwJmUKxthwD
drrLAzfUwOrGxPnYH3MWwg66RVVoiiU+Hh42gJTez+P/K0VMQZXH0XTZD96TiwT/
S0+WvQPuJ9pda2L4cjV+YP2+m0ERUG9QPj0Shgw+/PWCGWKD6J9DRToiwfwCTOvc
DNolF1EcRlfPMTsqI2Bdc+TQywqU1Py1MUDSAPXxdK6nW2oM7eC5l3XXefcQ6JOS
AuNdEDIsaf+H76j6/wSGsuIcJjUo9wmKjEV1iJYwT6jLbjaVQxXgEPHBSb+kzanx
FWyrYP6TZpeY2gT4bBMsOKd7ay1I3/nIHwe6x8Yzkqzz+7YVhNpMbybH72NTxDWQ
UmiboJs9r7j1lkHlBjxU9RLF5laYSeCIXD62KQVoP5ao1LSPKsCXFqcdpUgLYtRv
CXxdriiHp4m9eGzbS7RW1TLm6tMOKbI4LzOtMm4DoZzGD2FTfsUtn6ALx9FSgvl/
VMlzqZes+rclcukACtutvtB8e0yRhuzPUN56ctLEZfI1XzQFoN8Vdj6HWtiXWRiJ
EohuufE2cEebmX90NRsbh4KAFXNEQROlMeMf7ufuSsk7QXLTutxmWSQvWwyS+P5/
ZqCapIDlRCJFZX4N9a1IXo0OgCC5Ag0EZYWyowEQAKz7sA6WBKFVcmzcyr6y9TI5
RemnzotRUdqXj8tD70xvV1nBEf+BfTw3hsiV019ZboPBae+cRaD4plQbNb0du/+Q
UFq4ja055ZEld01RqE49L562pWvA5cwSmjs9+ZMI1ja0HbTb2K4razpzX3be+Zul
TbH20X5FHTz+Jyfmw+G3Y9okw3OIzuYo16Lv/Qs8VoBmj01PItzq8dX5wJiIlVxU
8wKHKy1kiKGCgyPnvqcXCGen0EoUS9wsWK1QHfryscYGVF9y1TBQvJa9HIpWPrIZ
slSiVS4tQhGHfLDVlaDsYFrH9SIUkvW7peSEiPE1TKQjvbMS3CcUZRaMcGfgyDPe
gXwnNwZbJ2/cisXbdAJaGT49V1Y+Sc1oKSGSG4FaGUNhS3p3cB7qlX2zS/sR2ael
90fXR6nhjvhMAS98i7663pCUv9q0Z/NXNZwdXjbSF4W8gq3PVt7hCnJrngsGSkGu
Qz0dc227/sAjh9CxYm8kQqUuQtiPVfKwkcUUT+O7jr2/6eAXZbTYVZbvaLEGMrKA
bdWKpZMMOKAI45f+ZAcEmJG4D68N0jzVrLCXMuuLq/yqqyEmxDjSOH0qVutl8jxg
vfxPxKYX8nnAzJZJHGHmV/ihjfClSdYePKExHuFVVhvc4BxE1XfPUeCZG4JpB9/B
gNCiDPsg4JIw6GO5LS2xABEBAAGJAjYEGAEKACAWIQQkK4E4Ma8JVitsRvdriNpO
OvKKFAUCZYWyowIbDAAKCRBriNpOOvKKFOTKD/40WN6ZRfP6hVLAX26DdyN9Bqzg
D+0hJilmHL1YFeLt9U/YPhOZJxPaDGrMfldC0FSXKKr7H5C7FQErl936108wGohz
53KGbNid0crNeaWrQuWk3QjaMF62Yc+NVB8b5LoEk0TEt9KPO6kveYDtW6Ygkw9i
5aaS/AC+8GChOH6lWhLHcO91T7+Hi5qRez7qyme/AlHUTV9/jE6JgFJ7thCCAqBQ
AfX3mGFlTwSTMHDeYq81MMl9UhYgoFJtx8PuqmSV7Eb29ccJofz3J+b5OJvpmLSZ
Hu22Csa8k2V/1ojeL0DjQHD06V75F7BerxLR/rbJ64GkIiOdm4ntXC4a335gjQfM
5tI6LtGsIpr+Le4C068eDj7YpzS0ql3g6o4A1TbtapLccgEj4iw95H4KKh0Ro5r0
0s6LoSLu9uva3LiLVn6xXvpw3iv8FnhTr5PEiPnorXvxcCnUrB3Rkk6Wh1SKjpxa
u6KiFzH0CXR1AgUdWSC0rAmvEdFjXgaoQd+Z2eJFHEAsg91URd9NtdeDsyMwdbHh
iBZPGX5ddPjJA7nwKbkibCv7xiodd9ph8NmGLqD/2SDU1rqvPuCDMqLuxc89Gvhs
3CeLTSJK9Uzb3oNdZLxL8CECip1ZkowVfTL+jY23phS9+Z0Db/yZqot3ZZJAAG8g
oSVkzT/Mwp9zFVQu5w==
=drMP
-----END PGP PUBLIC KEY BLOCK-----
+13 -2
View File
@@ -18,7 +18,8 @@
## Fails LOUDLY — never ships the base image's PHP by accident: ## Fails LOUDLY — never ships the base image's PHP by accident:
## * `apt-get update --error-on=any`: plain `apt-get update` only WARNS when a ## * `apt-get update --error-on=any`: plain `apt-get update` only WARNS when a
## repo is unreachable and carries on with no/old lists, exit 0. ## repo is unreachable and carries on with no/old lists, exit 0.
## * the LiteSpeed repo must be the candidate source for lsphp${PHPVER}. ## * the LiteSpeed repo must be the candidate source for lsphp${PHPVER}
## (rpms.litespeedtech.com, or ${PKG_CACHE}/litespeed/ when PKG_CACHE is set).
## * after upgrading, no lsphp${PHPVER}* package may remain upgradable. ## * after upgrading, no lsphp${PHPVER}* package may remain upgradable.
## * if CI passed the version it resolved from the repo index (EXPECT_LSPHP_PKG, ## * if CI passed the version it resolved from the repo index (EXPECT_LSPHP_PKG,
## EXPECT_PHP_VERSION), the installed package / running PHP must match it; ## EXPECT_PHP_VERSION), the installed package / running PHP must match it;
@@ -52,8 +53,18 @@ echo "lsphp-apt-upgrade: base image lsphp${PHPVER}=$(dpkg-query -W -f='${Version
# Read each output into a variable and match in the shell: piping into an # Read each output into a variable and match in the shell: piping into an
# early-exit reader (grep -q, awk ... exit) under pipefail can SIGPIPE the writer # early-exit reader (grep -q, awk ... exit) under pipefail can SIGPIPE the writer
# and report "missing" for a package that is there (scripts/tests/lsphp-info-probe.test.sh). # and report "missing" for a package that is there (scripts/tests/lsphp-info-probe.test.sh).
# With the internal package cache (PKG_CACHE build-arg, scripts/pkg-cache-repos.sh
# apt) the LiteSpeed source reads ${PKG_CACHE}/litespeed/... instead; that URL is
# accepted ONLY when PKG_CACHE is set, and only under its /litespeed/ path.
MADISON=$(apt-cache madison "lsphp${PHPVER}") MADISON=$(apt-cache madison "lsphp${PHPVER}")
if [[ "$MADISON" != *rpms.litespeedtech.com* ]]; then LS_REPO_OK=0
if [[ "$MADISON" == *rpms.litespeedtech.com* ]]; then
LS_REPO_OK=1
elif [ -n "${PKG_CACHE:-}" ] && [[ "$MADISON" == *"${PKG_CACHE%/}/litespeed/"* ]]; then
LS_REPO_OK=1
echo "lsphp-apt-upgrade: LiteSpeed repo via package cache ${PKG_CACHE%/}/litespeed/"
fi
if [ "$LS_REPO_OK" != 1 ]; then
apt-cache policy "lsphp${PHPVER}" >&2 || true apt-cache policy "lsphp${PHPVER}" >&2 || true
die "the LiteSpeed apt repo does not offer lsphp${PHPVER} (repo missing from sources, or its index is empty)." die "the LiteSpeed apt repo does not offer lsphp${PHPVER} (repo missing from sources, or its index is empty)."
fi fi
+84 -15
View File
@@ -4,11 +4,16 @@
## when PKG_CACHE is set; without it the repo files are left alone and builds ## when PKG_CACHE is set; without it the repo files are left alone and builds
## use the upstream repositories as before. ## use the upstream repositories as before.
## ##
## PKG_CACHE=https://192.168.1.40 pkg-cache-repos.sh rpm # AlmaLinux/EPEL/Remi ## PKG_CACHE=https://192.168.1.40 pkg-cache-repos.sh rpm # AlmaLinux/EPEL/Remi/NodeSource
## PKG_CACHE=https://192.168.1.40 pkg-cache-repos.sh apt # Ubuntu + LiteSpeed ## PKG_CACHE=https://192.168.1.40 pkg-cache-repos.sh apt # Ubuntu, Debian, LiteSpeed
## pkg-cache-repos.sh release-install epel [remi] # GPG-verified release RPMs ## pkg-cache-repos.sh release-install epel [remi] # GPG-verified release RPMs (EL9/EL10)
## pkg-cache-repos.sh nodesource MAJOR # NodeSource repo file + pinned key
## eval "$(pkg-cache-repos.sh go-env)" # GOPROXY via the cache (go.sum/sumdb verified)
## pkg-cache-repos.sh restore # undo everything above ## pkg-cache-repos.sh restore # undo everything above
## ##
## CANONICAL copy: cloud-apache-container/scripts/pkg-cache-repos.sh (docs/pkg-cache.md
## there). Other repos carry byte-identical copies - change it there first.
##
## Every RUN that rewrites must `restore` before it ends: it puts rewritten files ## Every RUN that rewrites must `restore` before it ends: it puts rewritten files
## back, deletes files this script created, deletes any NEW repo/apt file that ## back, deletes files this script created, deletes any NEW repo/apt file that
## still references the cache, then FAILS if any reference is left — so no shipped ## still references the cache, then FAILS if any reference is left — so no shipped
@@ -43,7 +48,7 @@
## cache (with the CA), and a build without PKG_CACHE uses upstream sources. ## cache (with the CA), and a build without PKG_CACHE uses upstream sources.
set -euo pipefail set -euo pipefail
MODE="${1:?usage: pkg-cache-repos.sh rpm|apt|release-install NAME...|restore}" MODE="${1:?usage: pkg-cache-repos.sh rpm|apt|release-install NAME...|nodesource MAJOR|go-env|restore}"
shift || true shift || true
STATE=/var/lib/pkg-cache-repos # saved originals + manifest + created files STATE=/var/lib/pkg-cache-repos # saved originals + manifest + created files
SHARE="${PKG_CACHE_SHARE:-/usr/local/share/pkg-cache}" # ca.crt + RPM-GPG-KEY-* SHARE="${PKG_CACHE_SHARE:-/usr/local/share/pkg-cache}" # ca.crt + RPM-GPG-KEY-*
@@ -57,10 +62,22 @@ if [ -n "$C" ]; then
https://*) ;; https://*) ;;
*) die "PKG_CACHE must be an https:// URL (plain http is refused), got '$C'" ;; *) die "PKG_CACHE must be an https:// URL (plain http is refused), got '$C'" ;;
esac esac
[[ "$C" =~ ^https://[A-Za-z0-9.-]+(:[0-9]+)?(/[A-Za-z0-9._/-]*)?$ ]] || die "PKG_CACHE has unexpected characters: '$C'"
HOSTPORT="${C#https://}"; HOSTPORT="${HOSTPORT%%/*}" HOSTPORT="${C#https://}"; HOSTPORT="${HOSTPORT%%/*}"
[ -s "$SHARE/ca.crt" ] || die "PKG_CACHE set but $SHARE/ca.crt (pkg-cache CA) is missing" [ -s "$SHARE/ca.crt" ] || die "PKG_CACHE set but $SHARE/ca.crt (pkg-cache CA) is missing"
fi fi
# System CA bundle (EL vs Debian/Ubuntu path); the pkg-cache CA is appended to a
# copy in the state dir, never to the system store.
sys_bundle() {
local b
for b in /etc/pki/tls/certs/ca-bundle.crt /etc/ssl/certs/ca-certificates.crt; do
[ -s "$b" ] && { echo "$b"; return 0; }
done
die "no system CA bundle found"
}
make_bundle() { cat "$(sys_bundle)" "$SHARE/ca.crt" > "$STATE/ca-bundle.pem"; }
# Files that may hold repo configuration; restore re-checks all of them. # Files that may hold repo configuration; restore re-checks all of them.
repo_files() { repo_files() {
find /etc/yum.repos.d /etc/dnf /etc/apt -type f 2>/dev/null | sort || true find /etc/yum.repos.d /etc/dnf /etc/apt -type f 2>/dev/null | sort || true
@@ -112,18 +129,20 @@ if [ "$MODE" = release-install ]; then
curl_opts=(-fsS --proto '=https' --retry 5 --retry-all-errors --max-time 120) curl_opts=(-fsS --proto '=https' --retry 5 --retry-all-errors --max-time 120)
if [ -n "$C" ]; then if [ -n "$C" ]; then
start_session start_session
cat /etc/pki/tls/certs/ca-bundle.crt "$SHARE/ca.crt" > "$STATE/ca-bundle.pem" make_bundle
curl_opts+=(--cacert "$STATE/ca-bundle.pem") curl_opts+=(--cacert "$STATE/ca-bundle.pem")
fi fi
files=() files=()
el=$(rpm -E '%{?rhel}' 2>/dev/null || true)
case "$el" in 9|10) ;; *) die "release-install supports EL9/EL10 only (rpm -E %rhel = '$el')" ;; esac
for name in "$@"; do for name in "$@"; do
case "$name" in case "$name" in
epel) pkg=epel-release key=RPM-GPG-KEY-EPEL-10 epel) pkg=epel-release key=RPM-GPG-KEY-EPEL-$el
path=/epel/epel-release-latest-10.noarch.rpm path=/epel/epel-release-latest-$el.noarch.rpm
origin=https://dl.fedoraproject.org/pub/epel/epel-release-latest-10.noarch.rpm ;; origin=https://dl.fedoraproject.org/pub/epel/epel-release-latest-$el.noarch.rpm ;;
remi) pkg=remi-release key=RPM-GPG-KEY-remi2024 remi) pkg=remi-release key=RPM-GPG-KEY-remi2024
path=/remi/enterprise/remi-release-10.rpm path=/remi/enterprise/remi-release-$el.rpm
origin=https://rpms.remirepo.net/enterprise/remi-release-10.rpm ;; origin=https://rpms.remirepo.net/enterprise/remi-release-$el.rpm ;;
*) die "unknown release package '$name'" ;; *) die "unknown release package '$name'" ;;
esac esac
url="$origin"; [ -n "$C" ] && url="$C$path" url="$origin"; [ -n "$C" ] && url="$C$path"
@@ -142,14 +161,14 @@ if [ "$MODE" = release-install ]; then
exit 0 exit 0
fi fi
if [ -z "$C" ]; then if [ -z "$C" ] && [ "$MODE" != nodesource ] && [ "$MODE" != go-env ]; then
echo "pkg-cache-repos($MODE): PKG_CACHE not set - using upstream repositories" echo "pkg-cache-repos($MODE): PKG_CACHE not set - using upstream repositories"
exit 0 exit 0
fi fi
rewrite_rpm() { rewrite_rpm() {
local f="$1" local f="$1"
grep -q -E '^#? ?baseurl=https?://(repo\.almalinux\.org|download\.example|rpms\.remirepo\.net)/' "$f" || return 0 grep -q -E '^#? ?baseurl=https?://(repo\.almalinux\.org|download\.example|rpms\.remirepo\.net|rpm\.nodesource\.com)/' "$f" || return 0
save "$f" save "$f"
# Per [section]: a (commented or live) baseurl on a known origin becomes # Per [section]: a (commented or live) baseurl on a known origin becomes
# "baseurl=<cache>/<prefix>/<path> <origin>/<path>"; that section's # "baseurl=<cache>/<prefix>/<path> <origin>/<path>"; that section's
@@ -185,6 +204,8 @@ rewrite_rpm() {
l = rw(l, "^#? ?baseurl=https?://repo\\.almalinux\\.org/almalinux/", "almalinux", "https://repo.almalinux.org/almalinux", 1) l = rw(l, "^#? ?baseurl=https?://repo\\.almalinux\\.org/almalinux/", "almalinux", "https://repo.almalinux.org/almalinux", 1)
l = rw(l, "^#? ?baseurl=https?://download\\.example/pub/epel/", "epel", "https://dl.fedoraproject.org/pub/epel", 0) l = rw(l, "^#? ?baseurl=https?://download\\.example/pub/epel/", "epel", "https://dl.fedoraproject.org/pub/epel", 0)
l = rw(l, "^#? ?baseurl=https?://rpms\\.remirepo\\.net/", "remi", "https://rpms.remirepo.net", 1) l = rw(l, "^#? ?baseurl=https?://rpms\\.remirepo\\.net/", "remi", "https://rpms.remirepo.net", 1)
# NodeSource signs its RPMs but not repomd.xml (404): gpgcheck only.
l = rw(l, "^#? ?baseurl=https?://rpm\\.nodesource\\.com/", "nodesource", "https://rpm.nodesource.com", 0)
} }
buf[++n] = l buf[++n] = l
} }
@@ -194,19 +215,65 @@ rewrite_rpm() {
rewrite_apt() { rewrite_apt() {
local f="$1" local f="$1"
grep -q -E 'https?://([a-z]{2}\.)?(archive|security)\.ubuntu\.com/|https?://rpms\.litespeedtech\.com/' "$f" || return 0 grep -q -E 'https?://([a-z]{2}\.)?(archive|security)\.ubuntu\.com/|https?://rpms\.litespeedtech\.com/|https?://(deb|security)\.debian\.org/' "$f" || return 0
save "$f" save "$f"
# Debian: debian-security first, and "debian" only as a whole path segment,
# so .../debian-security is never rewritten as /debian/.
sed -i -E \ sed -i -E \
-e "s%https?://(archive|[a-z]{2}\.archive)\.ubuntu\.com/ubuntu/?%$C/ubuntu/%g" \ -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?://security\.ubuntu\.com/ubuntu/?%$C/ubuntu-security/%g" \
-e "s%https?://rpms\.litespeedtech\.com/%$C/litespeed/%g" \ -e "s%https?://rpms\.litespeedtech\.com/%$C/litespeed/%g" \
-e "s%https?://(deb|security)\.debian\.org/debian-security/?([[:space:]]|\$)%$C/debian-security/\\2%g" \
-e "s%https?://security\.debian\.org/?([[:space:]]|\$)%$C/debian-security/\\1%g" \
-e "s%https?://deb\.debian\.org/debian/?([[:space:]]|\$)%$C/debian/\\1%g" \
"$f" "$f"
} }
if [ "$MODE" = go-env ]; then
# Prints shell exports for `eval`. Without PKG_CACHE: nothing (Go defaults).
# GOPROXY "a|b" falls through on ANY error, so a dead cache costs one
# failed request, not the build. GOSUMDB stays the default sum.golang.org,
# reached directly: modules are still verified against go.sum + the sumdb.
[ -n "$C" ] || { echo "pkg-cache-repos(go-env): PKG_CACHE not set - default GOPROXY" >&2; exit 0; }
start_session
make_bundle
printf "export GOPROXY='%s/goproxy|https://proxy.golang.org|direct' SSL_CERT_FILE='%s'\n" "$C" "$STATE/ca-bundle.pem"
echo "pkg-cache-repos(go-env): GOPROXY via $C/goproxy (fallback proxy.golang.org)" >&2
exit 0
fi
if [ "$MODE" = nodesource ]; then
# Replaces `curl https://rpm.nodesource.com/setup_N.x | bash` (unverified
# remote script): writes the same nodesource-nodejs repo (origin URL, so the
# shipped image keeps a normal repo file) with gpgcheck=1 and the signing key
# committed in keys/pkg-cache/; with PKG_CACHE it is then rewritten like any
# other repo and `restore` puts the origin version back.
major="${1:?usage: pkg-cache-repos.sh nodesource MAJOR}"
[[ "$major" =~ ^[0-9]{1,3}$ ]] || die "bad Node major '$major'"
key="$SHARE/RPM-GPG-KEY-nodesource"
[ -s "$key" ] || die "missing $key"
rpm --import "$key" || die "cannot import $key"
rm -f /etc/yum.repos.d/nodesource*.repo
f=/etc/yum.repos.d/nodesource-nodejs.repo
printf '%s\n' "[nodesource-nodejs]" \
"name=Node.js Packages for Linux RPM based distros - \$basearch" \
"baseurl=https://rpm.nodesource.com/pub_${major}.x/nodistro/nodejs/\$basearch" \
"priority=9" "enabled=1" "gpgcheck=1" \
"gpgkey=file://$key" "module_hotfixes=1" > "$f"
echo "pkg-cache-repos: wrote $f (Node ${major}.x, key $(basename "$key"))"
if [ -n "$C" ]; then
start_session
make_bundle
rewrite_rpm "$f"
grep -h '^baseurl=' "$f" | sed 's/^/ /'
fi
exit 0
fi
case "$MODE" in case "$MODE" in
rpm) rpm)
start_session start_session
cat /etc/pki/tls/certs/ca-bundle.crt "$SHARE/ca.crt" > "$STATE/ca-bundle.pem" make_bundle
for f in /etc/yum.repos.d/*.repo; do [ -f "$f" ] && rewrite_rpm "$f"; done for f in /etc/yum.repos.d/*.repo; do [ -f "$f" ] && rewrite_rpm "$f"; done
echo "pkg-cache-repos(rpm): repos now via $C:" echo "pkg-cache-repos(rpm): repos now via $C:"
grep -h '^baseurl=' /etc/yum.repos.d/*.repo | grep -F "$C/" | sort -u | sed 's/^/ /' grep -h '^baseurl=' /etc/yum.repos.d/*.repo | grep -F "$C/" | sort -u | sed 's/^/ /'
@@ -221,7 +288,9 @@ case "$MODE" in
[ -f "$f" ] && rewrite_apt "$f" [ -f "$f" ] && rewrite_apt "$f"
done done
echo "pkg-cache-repos(apt): sources now via $C:" 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/^/ /' # (|| true: Debian 13 images have no /etc/apt/sources.list; grep's exit 2
# for the missing file must not fail the build under pipefail)
{ grep -h -E '^(deb|URIs:)' /etc/apt/sources.list /etc/apt/sources.list.d/* 2>/dev/null || true; } | grep -F "$C/" | sort -u | sed 's/^/ /' || true
;; ;;
*) die "unknown mode '$MODE'" ;; *) die "unknown mode '$MODE'" ;;
esac esac