CI: skip unchanged base image and add registry build cache
Build and Deploy Verso / deploy (push) Successful in 12m40s
Build and Deploy Verso / deploy (push) Successful in 12m40s
Two build-speed changes to the Gitea Actions deploy workflow. (#1) Build the base image only when it changes. The base layers' only repo input is server-ce/Dockerfile-base, so the prepare step hashes that file and the base is tagged verso-base:base-<hash>; the app builds FROM that exact tag. If a base with the current hash already exists in the registry, the heavy base build (apt ~111s, TeX Live ~51s, Quarto, plus its ~49s export/push) is skipped entirely — which is every commit that doesn't touch Dockerfile-base. (#2) Import/export a registry-backed layer cache (verso-cache:base and verso-cache:app, mode=max) on both builds. Unchanged layers are reused instead of rebuilt: yarn install is skipped when package.json is unchanged, and only the web compile re-runs on a frontend source change. No new cluster resources — the cache lives as extra tags in the same in-cluster registry. First run after this is still a full build (populates the caches and the hash-tagged base); subsequent commits should be substantially faster. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -37,7 +37,23 @@ jobs:
|
||||
args:
|
||||
- |
|
||||
set -eux
|
||||
REG=registry.git.svc.cluster.local:5000
|
||||
git clone --depth 1 https://git.alocoq.fr/alois/verso.git /workspace/repo
|
||||
|
||||
# (#1) Build the base image only when it actually changes.
|
||||
# The base layers' only repo input is Dockerfile-base, so
|
||||
# we key on a content hash of that file: the base is tagged
|
||||
# verso-base:base-<hash> and the app builds FROM that exact
|
||||
# tag. If a base with this hash is already in the registry,
|
||||
# the heavy base build (apt, TeX Live, Quarto) is skipped.
|
||||
BTAG=$(sha256sum /workspace/repo/server-ce/Dockerfile-base | cut -c1-16)
|
||||
printf '%s' "$BTAG" > /workspace/base_tag
|
||||
if wget -qO- "http://$REG/v2/verso-base/tags/list" 2>/dev/null | grep -q "\"base-$BTAG\""; then
|
||||
echo "Base image base-$BTAG already present - skipping base build"
|
||||
else
|
||||
touch /workspace/build-base
|
||||
echo "Base image base-$BTAG not found - base will be built"
|
||||
fi
|
||||
volumeMounts:
|
||||
- name: workspace
|
||||
mountPath: /workspace
|
||||
@@ -54,30 +70,47 @@ jobs:
|
||||
|
||||
# Push to the in-cluster registry (plain HTTP) to bypass
|
||||
# the Traefik ingress, whose read timeout was killing the
|
||||
# multi-GB TeX Live layer upload mid-stream. The base
|
||||
# image is pulled back in for the second build, so the
|
||||
# registry must be marked insecure for both push and pull.
|
||||
# Write buildkitd.toml inside the container (no extra
|
||||
# k8s resources needed) so the second build's pull/resolve
|
||||
# treats the registry as http.
|
||||
# multi-GB TeX Live layer upload mid-stream. Mark the
|
||||
# registry http+insecure so both push and the base pull
|
||||
# for the app build treat it as plain HTTP. Written inside
|
||||
# the container so no extra k8s resources are needed.
|
||||
REG=registry.git.svc.cluster.local:5000
|
||||
|
||||
mkdir -p /etc/buildkit
|
||||
printf '[registry."%s"]\n http = true\n insecure = true\n' "$REG" > /etc/buildkit/buildkitd.toml
|
||||
|
||||
buildctl-daemonless.sh build \
|
||||
--frontend=dockerfile.v0 \
|
||||
--local context=/workspace/repo \
|
||||
--local dockerfile=/workspace/repo/server-ce \
|
||||
--opt filename=Dockerfile-base \
|
||||
--output type=image,name=$REG/verso-base:latest,push=true,registry.insecure=true
|
||||
BTAG=$(cat /workspace/base_tag)
|
||||
BASE_REF="$REG/verso-base:base-$BTAG"
|
||||
|
||||
# (#1) Base build, only when prepare flagged it changed.
|
||||
# (#2) Import/export a registry layer cache so that, when
|
||||
# the base does change, unchanged layers (e.g. apt) are
|
||||
# still reused instead of rebuilt from scratch.
|
||||
if [ -f /workspace/build-base ]; then
|
||||
buildctl-daemonless.sh build \
|
||||
--frontend=dockerfile.v0 \
|
||||
--local context=/workspace/repo \
|
||||
--local dockerfile=/workspace/repo/server-ce \
|
||||
--opt filename=Dockerfile-base \
|
||||
--import-cache type=registry,ref=$REG/verso-cache:base \
|
||||
--export-cache type=registry,ref=$REG/verso-cache:base,mode=max \
|
||||
--output type=image,name=$BASE_REF,push=true,registry.insecure=true
|
||||
else
|
||||
echo "Reusing existing base image $BASE_REF"
|
||||
fi
|
||||
|
||||
# App image, built FROM the content-pinned base tag.
|
||||
# (#2) The registry cache lets yarn install be skipped when
|
||||
# package.json is unchanged; the web build only re-runs
|
||||
# when the frontend source actually changes.
|
||||
buildctl-daemonless.sh build \
|
||||
--frontend=dockerfile.v0 \
|
||||
--local context=/workspace/repo \
|
||||
--local dockerfile=/workspace/repo/server-ce \
|
||||
--opt filename=Dockerfile \
|
||||
--opt build-arg:OVERLEAF_BASE_TAG=$REG/verso-base:latest \
|
||||
--opt build-arg:OVERLEAF_BASE_TAG=$BASE_REF \
|
||||
--import-cache type=registry,ref=$REG/verso-cache:app \
|
||||
--export-cache type=registry,ref=$REG/verso-cache:app,mode=max \
|
||||
--output type=image,name=$REG/verso:latest,push=true,registry.insecure=true
|
||||
volumeMounts:
|
||||
- name: workspace
|
||||
|
||||
Reference in New Issue
Block a user