78 lines
4.2 KiB
Bash
Executable file
78 lines
4.2 KiB
Bash
Executable file
#!/usr/bin/env bash
|
|
# Root-owned publish step for storebound desktop releases (install as /usr/local/sbin/storebound-desktop-publish,
|
|
# root:root 755; see docs/desktop-release.md). tools/desktop-release.ts publish uploads one version folder to
|
|
# /var/lib/storebound-desktop/incoming/<version>/ and then runs: storebound-desktop-publish <version> [stable|beta]
|
|
#
|
|
# It checks the upload (regular files with plain names only, SHA256SUMS, latest.json of that version whose every URL
|
|
# points at an uploaded file of /desktop/dl/<version>/), copies the files into /var/www/storebound-desktop/dl/<version>/
|
|
# (a new version: a temp folder renamed into place; an existing version: only new files, a file already published is
|
|
# never replaced by different bytes), and only then swaps latest.json of the channel atomically (temp file + rename).
|
|
# Apache serves that folder under https://storebound.dev/desktop/ (tools/vds/apache-storebound-desktop.conf), apart
|
|
# from the site's own rsync, which deletes what is not in the repository. Nothing from the upload is executed.
|
|
set -euo pipefail
|
|
umask 022
|
|
|
|
VERSION="${1:-}"
|
|
CHANNEL="${2:-stable}"
|
|
INCOMING=/var/lib/storebound-desktop/incoming
|
|
ROOT=/var/www/storebound-desktop
|
|
SITE=https://storebound.dev/desktop
|
|
|
|
die() { echo "storebound-desktop-publish: $*" >&2; exit 1; }
|
|
[[ "$VERSION" =~ ^[0-9]+\.[0-9]+\.[0-9]+(-[0-9A-Za-z.]+)?$ ]] || die "usage: storebound-desktop-publish <version> [stable|beta]"
|
|
[[ "$CHANNEL" == stable || "$CHANNEL" == beta ]] || die "channel: stable or beta"
|
|
IN="$INCOMING/$VERSION"
|
|
DL="$ROOT/dl/$VERSION"
|
|
[ -d "$IN" ] && [ ! -L "$IN" ] || die "nothing uploaded at $IN"
|
|
|
|
# only regular files with plain names, nothing hidden, no folders, no links
|
|
if find "$IN" -mindepth 1 \( ! -type f -o -name '.*' \) -print | grep -q .; then die "the upload holds something other than plain files"; fi
|
|
if find "$IN" -mindepth 1 -type f -printf '%f\n' | grep -Ev '^[A-Za-z0-9][A-Za-z0-9._-]*$' | grep -q .; then die "a file name has unexpected characters"; fi
|
|
[ -f "$IN/latest.json" ] && [ -f "$IN/SHA256SUMS" ] || die "latest.json and SHA256SUMS are required"
|
|
(cd "$IN" && sha256sum --quiet --strict -c SHA256SUMS) || die "SHA256SUMS does not match the files"
|
|
|
|
python3 - "$IN" "$VERSION" "$SITE" <<'PY' || die "latest.json does not describe this upload"
|
|
import json, os, re, sys
|
|
src, version, site = sys.argv[1], sys.argv[2], sys.argv[3]
|
|
m = json.load(open(os.path.join(src, "latest.json")))
|
|
assert m.get("version") == version, "version"
|
|
files = set(os.listdir(src))
|
|
listed = {l.split()[1] for l in open(os.path.join(src, "SHA256SUMS")) if l.strip()}
|
|
assert files - {"SHA256SUMS"} <= listed, "files missing from SHA256SUMS"
|
|
prefix = f"{site}/dl/{version}/"
|
|
entries = list((m.get("platforms") or {}).values()) + list((m.get("downloads") or {}).values())
|
|
assert entries, "no platforms"
|
|
for e in entries:
|
|
url = e.get("url", "")
|
|
assert url.startswith(prefix), url
|
|
name = url[len(prefix):]
|
|
assert re.fullmatch(r"[A-Za-z0-9][A-Za-z0-9._-]*", name) and name in files, url
|
|
assert e.get("signature"), url
|
|
PY
|
|
|
|
install -d -m 755 "$ROOT" "$ROOT/dl" "$ROOT/beta"
|
|
if [ ! -e "$DL" ]; then
|
|
TMP="$(mktemp -d "$ROOT/dl/.tmp-$VERSION.XXXXXX")"
|
|
for f in "$IN"/*; do n="$(basename "$f")"; [ "$n" = latest.json ] || install -m 644 "$f" "$TMP/$n"; done
|
|
chmod 755 "$TMP"
|
|
mv -T "$TMP" "$DL"
|
|
else
|
|
for f in "$IN"/*; do
|
|
n="$(basename "$f")"
|
|
[ "$n" = latest.json ] && continue
|
|
if [ -e "$DL/$n" ]; then
|
|
[ "$n" = SHA256SUMS ] && continue
|
|
cmp -s "$f" "$DL/$n" || die "$n is already published with other bytes: a published file is never replaced (bump the version)"
|
|
else
|
|
install -m 644 "$f" "$DL/.$n.tmp" && mv -f "$DL/.$n.tmp" "$DL/$n"
|
|
fi
|
|
done
|
|
install -m 644 "$IN/SHA256SUMS" "$DL/.SHA256SUMS.tmp" && mv -f "$DL/.SHA256SUMS.tmp" "$DL/SHA256SUMS"
|
|
fi
|
|
|
|
# latest.json last: installed apps see the new version only once every file it names is in place
|
|
if [ "$CHANNEL" = stable ]; then LJ="$ROOT/latest.json"; URL="$SITE/latest.json"; else LJ="$ROOT/beta/latest.json"; URL="$SITE/beta/latest.json"; fi
|
|
install -m 644 "$IN/latest.json" "$LJ.tmp"
|
|
mv -f "$LJ.tmp" "$LJ"
|
|
rm -rf "$IN"
|
|
curl -fsS -o /dev/null --resolve storebound.dev:443:127.0.0.1 "$URL" && echo "storebound desktop $VERSION published ($CHANNEL): $URL"
|