Native VM install kit: systemd units, launcher, install and upgrade scripts; backups, restore and doctor without docker exec; DEPLOY.md VM, NPM and NFS sections
Co-Authored-By: Claude Opus 5.5 <[email protected]>
This commit is contained in:
Executable
+107
@@ -0,0 +1,107 @@
|
||||
#!/usr/bin/env bash
|
||||
# Build a release archive for the native VMs. Runs on a build host as a normal user, inside a git
|
||||
# checkout of this repository: docker-1 (with --image) or a PC with Node (with --npm). Never on a VM.
|
||||
#
|
||||
# Usage:
|
||||
# make-release.sh --image gt.pescoinc.biz/spencerm/packetstation:sha-<sha> [--out DIR]
|
||||
# make-release.sh --npm [--ref REF] [--build NAME] [--out DIR]
|
||||
#
|
||||
# --image IMAGE take the built frontend out of a CI-tested image (docker create and docker cp,
|
||||
# nothing runs). The build name and the commit come from the image's build label,
|
||||
# so the source and the frontend are the tested commit.
|
||||
# --npm build the frontend here with npm ci and npm run build, from the same commit.
|
||||
# --ref REF the commit for --npm (default HEAD). Uncommitted changes are never included.
|
||||
# --build NAME the build name for --npm (default sha-<full sha>, as CI names images).
|
||||
# --out DIR where to write the archive (default the current folder).
|
||||
#
|
||||
# Writes packetstation-<build>.tar.gz and packetstation-<build>.tar.gz.sha256. The archive holds one
|
||||
# folder, packetstation-<build>/, with BUILD, pyproject.toml, README.md, server/, deploy/, scripts/,
|
||||
# docs/ and static/ (the built frontend). Python packages are not in it: ps-upgrade builds the venv
|
||||
# on the VM from the pinned requirements.
|
||||
set -euo pipefail
|
||||
umask 022
|
||||
|
||||
DOCKER="${DOCKER:-docker}"
|
||||
LABEL=biz.pescoinc.packetstation.build
|
||||
|
||||
log() { printf 'make-release: %s\n' "$*" >&2; }
|
||||
die() { printf 'make-release: ERROR: %s\n' "$*" >&2; exit 1; }
|
||||
usage() { sed -n '2,21p' "$0" | sed 's/^# \{0,1\}//' >&2; exit 2; }
|
||||
|
||||
image=""
|
||||
npm=0
|
||||
ref=""
|
||||
build=""
|
||||
out="."
|
||||
while [[ $# -gt 0 ]]; do
|
||||
case "$1" in
|
||||
--image) [[ $# -ge 2 ]] || usage; image="$2"; shift 2 ;;
|
||||
--npm) npm=1; shift ;;
|
||||
--ref) [[ $# -ge 2 ]] || usage; ref="$2"; shift 2 ;;
|
||||
--build) [[ $# -ge 2 ]] || usage; build="$2"; shift 2 ;;
|
||||
--out) [[ $# -ge 2 ]] || usage; out="$2"; shift 2 ;;
|
||||
-h|--help) usage ;;
|
||||
*) log "unknown argument $1"; usage ;;
|
||||
esac
|
||||
done
|
||||
if [[ -n "$image" && $npm -eq 1 ]] || [[ -z "$image" && $npm -eq 0 ]]; then
|
||||
die "give exactly one of --image or --npm"
|
||||
fi
|
||||
if [[ -n "$image" && ( -n "$ref" || -n "$build" ) ]]; then
|
||||
die "--ref and --build come from the image's label with --image"
|
||||
fi
|
||||
[[ "$image" != *:latest && ( -z "$image" || "$image" == *:* ) ]] || die "pull an image by its tag, never latest"
|
||||
|
||||
repo="$(git rev-parse --show-toplevel 2>/dev/null)" || die "run inside a git checkout of Packet Station"
|
||||
cd -- "$repo"
|
||||
|
||||
if [[ -n "$image" ]]; then
|
||||
"$DOCKER" image inspect "$image" >/dev/null 2>&1 || "$DOCKER" pull "$image" >/dev/null
|
||||
build="$("$DOCKER" image inspect -f "{{ index .Config.Labels \"$LABEL\" }}" "$image")"
|
||||
[[ "$build" =~ ^sha-[0-9a-f]{7,40}$ ]] || die "image label $LABEL is '$build', expected sha-<commit>"
|
||||
ref="${build#sha-}"
|
||||
else
|
||||
ref="${ref:-HEAD}"
|
||||
fi
|
||||
sha="$(git rev-parse --verify --quiet "$ref^{commit}")" || die "commit $ref is not in this checkout (git fetch first)"
|
||||
if [[ -z "$build" ]]; then
|
||||
build="sha-$sha"
|
||||
fi
|
||||
[[ "$build" =~ ^[A-Za-z0-9][A-Za-z0-9._-]{0,63}$ ]] || die "build name '$build' must be letters, digits, '.', '_' or '-'"
|
||||
|
||||
name="packetstation-$build"
|
||||
mkdir -p -- "$out"
|
||||
out="$(cd -- "$out" && pwd)"
|
||||
work="$(mktemp -d)"
|
||||
cid=""
|
||||
cleanup() {
|
||||
if [[ -n "$cid" ]]; then "$DOCKER" rm -f "$cid" >/dev/null 2>&1 || true; fi
|
||||
rm -rf -- "$work"
|
||||
}
|
||||
trap cleanup EXIT
|
||||
|
||||
log "source of $sha"
|
||||
# LF line endings whatever the checkout's autocrlf: these files run on Linux.
|
||||
git -c core.autocrlf=false -c core.eol=lf archive --format=tar --prefix="$name/" "$sha" \
|
||||
pyproject.toml README.md server deploy scripts docs | tar -xf - -C "$work"
|
||||
printf '%s\n' "$build" > "$work/$name/BUILD"
|
||||
[[ -f "$work/$name/deploy/vm/ps-launch.sh" ]] || die "commit $sha has no deploy/vm (older than phase 10)"
|
||||
chmod 0755 "$work/$name"/deploy/vm/*.sh "$work/$name"/scripts/backup/*.sh
|
||||
|
||||
if [[ -n "$image" ]]; then
|
||||
log "frontend from $image"
|
||||
cid="$("$DOCKER" create "$image")"
|
||||
"$DOCKER" cp "$cid:/app/static" "$work/$name/static"
|
||||
else
|
||||
log "frontend: npm ci and npm run build at $sha"
|
||||
git -c core.autocrlf=false -c core.eol=lf archive --format=tar --prefix=frontend/ "$sha:frontend" \
|
||||
| tar -xf - -C "$work"
|
||||
( cd -- "$work/frontend" && npm ci --no-audit --no-fund && npm run build ) >&2
|
||||
cp -R -- "$work/frontend/dist" "$work/$name/static"
|
||||
fi
|
||||
[[ -f "$work/$name/static/index.html" ]] || die "no static/index.html in the built frontend"
|
||||
|
||||
log "writing $out/$name.tar.gz"
|
||||
tar -czf "$out/$name.tar.gz" -C "$work" --owner=0 --group=0 --numeric-owner --sort=name "$name"
|
||||
( cd -- "$out" && sha256sum -- "$name.tar.gz" > "$name.tar.gz.sha256" )
|
||||
log "done: $out/$name.tar.gz ($(wc -c < "$out/$name.tar.gz") bytes) and its .sha256"
|
||||
Reference in New Issue
Block a user