handoff pass: vendored sveltia bundle, astro pinned ^7.2.10, root README + refreshed deploy runbook, review fixes (coverless card grid, shared NewsCard/lib, real site origin, nginx security headers, linger step, dead config removed, preview.css sync, tsconfig)

This commit is contained in:
2026-09-03 14:56:53 +02:00
parent c64528a1d3
commit cec8018c18
35 changed files with 4095 additions and 2111 deletions
+23 -9
View File
@@ -21,6 +21,12 @@ REPO_INTERNAL="${REPO_INTERNAL:-http://localhost:3000/$REPO.git}"
KEEP="${KEEP:-3}" # released builds to retain
mkdir -p "$BASE/repo" "$BASE/releases" "$BASE/state" "$BASE/npm-cache"
# Host prerequisites (everything else runs inside containers). Fail loud —
# a missing tool is permanent, unlike a Gitea hiccup below.
for tool in curl jq podman; do
command -v "$tool" >/dev/null || { echo "missing host tool: $tool"; exit 1; }
done
# --- 1. Cheap poll: branch head via the local Gitea API (host curl + jq) ---
sha=$(curl -fsS --max-time 5 "$GITEA_URL/api/v1/repos/$REPO/branches/$BRANCH" \
| jq -r '.commit.id' || true)
@@ -30,7 +36,10 @@ if [[ ! "$sha" =~ ^[0-9a-f]{40}$ ]]; then
echo "poll failed (gitea unreachable?) — skipping this tick"
exit 0
fi
[[ "$sha" == "$(cat "$BASE/state/last-built" 2>/dev/null)" ]] && exit 0
# Skip only if this sha is both recorded AND still present in releases/
# (a deleted release dir must trigger a rebuild, not an eternal skip).
[[ "$sha" == "$(cat "$BASE/state/last-built" 2>/dev/null)" \
&& -d "$BASE/releases/$sha" ]] && exit 0
echo "building $sha"
t0=$(date +%s)
@@ -48,18 +57,21 @@ podman run --rm --network="$BUILD_NETNS" --memory=1g \
-v "$BASE/releases:/work/releases:z" \
-v "$BASE/npm-cache:/root/.npm:z" \
-w /work "$BUILD_IMAGE" sh -ec '
git config --global safe.directory "*"
git config --global safe.directory /work/repo
[ -d repo/.git ] || git clone --branch "$BRANCH" "$REPO_URL" repo
git -C repo remote set-url origin "$REPO_URL" # self-heal if the URL changes
git -C repo fetch --quiet origin "$BRANCH"
git -C repo checkout --quiet "$SHA"
# --force: the working copy is disposable; a stray tracked-file edit must
# not wedge every future build.
git -C repo checkout --quiet --force "$SHA"
cd repo
lock=$(sha256sum package-lock.json | cut -d" " -f1)
if [ ! -d node_modules ] || [ "$lock" != "$(cat .deps-hash 2>/dev/null)" ]; then
# --ignore-scripts: (1) kills the malicious-postinstall vector from npm
# deps entirely, (2) avoids the esbuild ETXTBSY postinstall race in
# rootless containers. esbuild ships its binary as an optional dep, so
# nothing needed here actually requires lifecycle scripts.
# --ignore-scripts: (1) removes the install-time postinstall vector from
# npm deps (build-time repo code still runs `npm run build` below — the
# netns confinement is the control for that), (2) avoids the esbuild
# ETXTBSY postinstall race in rootless containers. esbuild ships its
# binary as an optional dep, so nothing here needs lifecycle scripts.
npm ci --ignore-scripts --no-audit --no-fund
echo "$lock" > .deps-hash
fi
@@ -76,11 +88,13 @@ ln -s "$sha" "$BASE/releases/.current.$$"
mv -Tf "$BASE/releases/.current.$$" "$BASE/releases/current"
echo "$sha" > "$BASE/state/last-built"
# --- 4. Prune old releases (never touches `current` — it is always newest).
# --- 4. Prune old releases. `current`'s target is excluded explicitly —
# mtime ordering makes it newest today, but nothing should depend on that.
# `|| true`: an empty match must not fail the unit after a successful publish
# (grep exits 1 under pipefail when there is nothing to prune).
cd "$BASE/releases"
ls -1t | grep -vx current | tail -n +"$((KEEP + 1))" | while read -r old; do
cur=$(readlink current || true)
ls -1t | grep -vx current | grep -vx -- "$cur" | tail -n +"$((KEEP + 1))" | while read -r old; do
rm -rf -- "$old"
done || true