#!/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- [--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-, as CI names images). # --out DIR where to write the archive (default the current folder). # # Writes packetstation-.tar.gz and packetstation-.tar.gz.sha256. The archive holds one # folder, packetstation-/, 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-" 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"