108 lines
4.7 KiB
Bash
108 lines
4.7 KiB
Bash
#!/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"
|