Files
archy/tests/cloud/build-browser-runtime-offline.sh
T

102 lines
6.2 KiB
Bash
Executable File

#!/usr/bin/env bash
# Offline fixture assets only. Separate bounded manager admission is required.
set -euo pipefail
[[ $# == 3 ]] || { echo 'usage: build-browser-runtime-offline.sh EMPTY_OUTPUT PINNED_BROWSER_IMAGE LOCKED_FRONTEND_DIRECTORY' >&2; exit 2; }
out=$(realpath -e "$1"); image=$2; deps=$(realpath -e "$3")
[[ "$image" =~ ^sha256:[0-9a-f]{64}$ && -d "$deps/node_modules" ]] || exit 2
[[ -d "$out" && -z $(find "$out" -mindepth 1 -maxdepth 1 -print -quit) ]] || exit 2
root=$(cd "$(dirname "$0")/../.." && pwd)
PYTHONOPTIMIZE=0 python3 - <<'PY'
from pathlib import Path
group = next(line.split(':', 2)[2] for line in Path('/proc/self/cgroup').read_text().splitlines() if line.startswith('0::'))
root = Path('/sys/fs/cgroup') / group.lstrip('/')
assert 0 < int((root / 'memory.max').read_text()) <= 1073741824
assert int((root / 'memory.swap.max').read_text()) == 0
assert 0 < int((root / 'pids.max').read_text()) <= 256
quota, period = (root / 'cpu.max').read_text().split()
assert 0 < int(quota) <= int(period)
PY
cmp "$root/neode-ui/package.json" "$deps/package.json"
cmp "$root/neode-ui/package-lock.json" "$deps/package-lock.json"
inputs=(neode-ui tests/cloud/browser/index.html tests/cloud/browser/main.js tests/cloud/browser/vite.config.mjs
tests/cloud/browser/drive.cjs tests/cloud/browser/pagination.cjs tests/cloud/browser/pagination-seed.test.cjs
tests/cloud/build-browser-runtime-offline.sh core/archipelago/src/api/handler/cloud_browser_tests.rs)
git -C "$root" diff --exit-code HEAD -- "${inputs[@]}" >/dev/null
[[ -z $(git -C "$root" ls-files --others --exclude-standard -- "${inputs[@]}") ]] || exit 2
commit=$(git -C "$root" rev-parse HEAD)
printf '%s\n' "$commit" > "$out/source.commit"
printf '%s\n' "$image" > "$out/base-image"
podman ps -aq --no-trunc | sort > "$out/containers.before"
cleanup() {
local status=$? file cid exists
# A parent stop may arrive during cleanup; finish only these bounded removals.
trap '' INT TERM
trap - EXIT
set +e
for file in "$out/ui.cid" "$out/image.cid"; do
[[ -s "$file" ]] || continue
cid=$(<"$file")
if [[ "$cid" =~ ^[a-f0-9]{64}$ ]]; then
timeout --kill-after=1s 6s podman rm -f "$cid" >> "$out/cleanup.log" 2>&1 || status=1
timeout 2s podman container exists "$cid"; exists=$?
[[ "$exists" == 1 ]] || status=1
else status=1; fi
done
timeout 3s podman ps -aq --no-trunc | sort > "$out/containers.after" || status=1
cmp "$out/containers.before" "$out/containers.after" || status=1
printf '%s\n' "$status" > "$out/outer.exit"
exit "$status"
}
trap cleanup EXIT
trap 'exit 143' TERM
trap 'exit 130' INT
mkdir -p "$out/source" "$out/input/fixture" "$out/ui-output"
# Export tracked bytes from the pinned commit, never copy a dirty source tree.
git -C "$root" archive "$commit" neode-ui/src \
neode-ui/{tailwind.config.js,postcss.config.js,package.json,package-lock.json} \
neode-ui/public/assets/fonts/Montserrat/{Montserrat-Bold,Montserrat-ExtraBold}.ttf \
neode-ui/public/assets/img/bg.jpg tests/cloud/browser/{index.html,main.js,vite.config.mjs,drive.cjs,pagination.cjs} \
| tar --no-same-owner --no-same-permissions -xf - -C "$out/source"
[[ -z $(find "$out/source" ! -type d ! -type f -print -quit) ]] || exit 2
snapshot="$out/source"
cp -R "$snapshot/neode-ui/src" "$out/input/"
cp "$snapshot/neode-ui/"{tailwind.config.js,postcss.config.js,package.json,package-lock.json} "$out/input/"
cp "$snapshot/tests/cloud/browser/"{index.html,main.js,vite.config.mjs} "$out/input/fixture/"
mkdir -p "$out/input/fixture/public/assets/fonts/Montserrat" "$out/input/fixture/public/assets/img"
cp "$snapshot/neode-ui/public/assets/fonts/Montserrat/"{Montserrat-Bold,Montserrat-ExtraBold}.ttf "$out/input/fixture/public/assets/fonts/Montserrat/"
cp "$snapshot/neode-ui/public/assets/img/bg.jpg" "$out/input/fixture/public/assets/img/"
(cd "$out/input" && find . -type f -print0 | sort -z | xargs -0 sha256sum) > "$out/input.sha256"
nonce="$(date +%s)-$$"
podman create --cidfile "$out/ui.cid" --name "archy-cloud-assets-$nonce" --pull=never \
--network=none --pid=private --ipc=private --http-proxy=false --no-hosts \
--read-only --cap-drop=all --security-opt=no-new-privileges --memory=1g --memory-swap=1g \
--cpus=1 --pids-limit=256 --timeout=90 --log-driver=none \
--tmpfs /tmp:rw,size=256m --tmpfs /work:rw,size=128m \
--env HOME=/tmp --env XDG_CONFIG_HOME=/tmp/config --env NODE_PATH=/browser/node_modules \
--volume "$out/input:/input:ro" --volume "$out/ui-output:/out:rw" --volume "$deps/node_modules:/deps/node_modules:ro" \
--workdir /work --entrypoint /bin/sh "$image" -ec '
cp -R /input/. /work/
mkdir node_modules
for dep in /deps/node_modules/*; do ln -s "$dep" node_modules/; done
node /deps/node_modules/vite/bin/vite.js build --config fixture/vite.config.mjs
cp -R dist /out/
' > "$out/ui-create.log" 2>&1
timeout --kill-after=10s 100s podman start --attach "$(<"$out/ui.cid")" > "$out/ui-build.log" 2>&1
[[ -f "$out/ui-output/dist/index.html" && -z $(find "$out/ui-output" ! -type d ! -type f -print -quit) ]] || exit 2
# Never start the image-assembly container or mutate the retained base image.
# Only its new private layer is writable for the host control-plane copy/commit.
podman create --cidfile "$out/image.cid" --name "archy-cloud-image-$nonce" --pull=never \
--network=none --pid=private --ipc=private --http-proxy=false --no-hosts \
--cap-drop=all --security-opt=no-new-privileges --memory=1g --memory-swap=1g \
--cpus=1 --pids-limit=64 --timeout=10 --entrypoint /bin/false "$image" > "$out/image-create.log" 2>&1
cid=$(<"$out/image.cid")
timeout --kill-after=2s 15s podman cp "$out/ui-output" "$cid:/cloud-pagination"
timeout --kill-after=2s 15s podman cp "$cid:/cloud-pagination/dist" "$out/copied-dist"
(cd "$out/ui-output/dist" && find . -type f -print0 | sort -z | xargs -0 sha256sum) > "$out/assets.sha256"
(cd "$out/copied-dist" && find . -type f -print0 | sort -z | xargs -0 sha256sum) > "$out/copied-assets.sha256"
cmp "$out/assets.sha256" "$out/copied-assets.sha256"
timeout --kill-after=10s 60s podman commit --pause=false \
--change 'ENTRYPOINT ["/bin/false"]' --change 'CMD []' \
--change 'LABEL archy.fixture=cloud-pagination-only' "$cid" > "$out/runtime-image"
sha256sum "$snapshot/tests/cloud/browser/"{drive.cjs,pagination.cjs} > "$out/drivers.sha256"