-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathmake-zip.sh
More file actions
executable file
·86 lines (78 loc) · 4.45 KB
/
Copy pathmake-zip.sh
File metadata and controls
executable file
·86 lines (78 loc) · 4.45 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
#!/opt/homebrew/bin/bash
#
# make-zip.sh — build the sendable instrument.
#
# ./music/make-zip.sh <instrument> [destination-dir]
# ./music/make-zip.sh the-firing
# ./music/make-zip.sh the-stepwell
#
# Produces `<instrument>.zip`: the instrument, self-contained, runnable by somebody who has
# only Node or Python and no idea what this repository is. The recipient README is written
# here rather than reusing music/README.md, because that one is written for whoever is
# working on the label and this one is written for whoever just received a zip file.
#
# 🔴 The build VERIFIES the artefact rather than trusting it. A zip that does not run is
# the ordinary way this fails, and it fails after it has been sent. So the script unpacks
# what it just made into a clean directory, serves it, and checks the page is there.
set -euo pipefail
ROOT="$(cd "$(dirname "$0")" && pwd)"
INST="${1:-}"
[[ -n "$INST" ]] || { echo "usage: make-zip.sh <instrument> [dest]"; echo " available: $(cd "$ROOT" && ls -d */ | tr -d / | tr '\n' ' ')"; exit 2; }
HERE="$ROOT/$INST"
[[ -d "$HERE" ]] || { echo "[FATAL] no such instrument: $INST"; exit 2; }
DEST="${2:-$PWD/dist}" # pass a destination as the second argument
STAGE="$(mktemp -d)/$INST"
trap 'rm -rf "$(dirname "$STAGE")"' EXIT
mkdir -p "$STAGE"
for item in index.html serve.mjs lib fonts; do
[[ -e "$HERE/$item" ]] || { echo "[FATAL] missing: $item"; exit 2; }
rsync -a "$HERE/$item" "$STAGE/"
done
# material/ is the firing's; the stepwell synthesises its own surface and needs none
[[ -d "$HERE/material" ]] && rsync -a "$HERE/material" "$STAGE/"
# 🔴 The codec instruments import ../shared/latent.mjs, which lives beside them rather than
# inside them. Zipping only the instrument produces a folder that loads and then fails on the
# first import — a broken artefact that looks fine until the recipient opens it. Refuse.
if grep -q "\.\./shared/" "$HERE/index.html"; then
echo "[FATAL] $INST imports ../shared/ and cannot be zipped on its own yet."
echo " It would unpack, serve, and 404 its own module. Send the repo folder, or"
echo " teach this script to vendor shared/ and rewrite the import."
exit 6
fi
[[ -f "$HERE/SEND-README.md" ]] || { echo "[FATAL] missing SEND-README.md"; exit 2; }
cp "$HERE/SEND-README.md" "$STAGE/README.md"
[[ -f "$HERE/fonts/OFL.md" ]] || { echo "[FATAL] fonts/OFL.md missing — the faces are"
echo " redistributed under the SIL OFL and the licence has to travel with them"; exit 2; }
# Where an instrument depends on shipped material, its absence would open it silent.
if [[ -d "$HERE/material" ]]; then
[[ -s "$STAGE/material/starter.wav" ]] || { echo "[FATAL] material/starter.wav missing — the"
echo " instrument would open silent. Run: python3 $INST/make-starter.py"; exit 3; }
fi
mkdir -p "$DEST"
ZIP="$DEST/$INST.zip"
rm -f "$ZIP"
( cd "$(dirname "$STAGE")" && zip -qr9 "$ZIP" "$INST" -x '.*' -x '__MACOSX/*' )
# ── verify: unpack what was actually written and serve it ──────────────────
CHECK="$(mktemp -d)"
( cd "$CHECK" && unzip -q "$ZIP" )
[[ -f "$CHECK/$INST/index.html" ]] || { echo "[FATAL] the zip has no index.html"; exit 4; }
PORT=8231
# 🔴 Free the port FIRST and kill by port afterwards. Backgrounding a subshell gives you the
# subshell's pid, not node's, so `kill $!` leaves the server running — and the next build
# then checks its own zip against the PREVIOUS run's server, whose directory has since been
# deleted. That reads as "the zip is broken" when the zip is fine.
lsof -ti:$PORT | xargs -r kill 2>/dev/null || true
( cd "$CHECK/$INST" && PORT=$PORT node serve.mjs >/dev/null 2>&1 ) &
for _ in 1 2 3 4 5 6 7 8; do
sleep 0.5
curl -s -o /dev/null "http://localhost:$PORT/" && break
done
CODE=$(curl -s -o /dev/null -w '%{http_code}' "http://localhost:$PORT/" || echo 000)
FIRSTMOD=$(cd "$HERE/lib" && ls *.mjs | head -1)
MOD=$(curl -s -o /dev/null -w '%{http_code}' "http://localhost:$PORT/lib/$FIRSTMOD" || echo 000)
if [[ -d "$HERE/material" ]]; then WAV=$(curl -s -o /dev/null -w '%{http_code}' "http://localhost:$PORT/material/starter.wav" || echo 000); else WAV=200; fi
lsof -ti:$PORT | xargs -r kill 2>/dev/null || true
rm -rf "$CHECK"
[[ "$CODE $MOD $WAV" == "200 200 200" ]] || { echo "[FATAL] unpacked zip did not serve: page=$CODE module=$MOD material=$WAV"; rm -f "$ZIP"; exit 5; }
echo "✓ $ZIP ($(du -h "$ZIP" | cut -f1 | tr -d ' '))"
echo " unpacked, served and checked — page, modules and material all 200"