summaryrefslogtreecommitdiffstats
path: root/scripts
diff options
context:
space:
mode:
authorsalmonik <salmonik@tuta.io>2026-10-30 00:40:10 +0100
committersalmonik <salmonik@tuta.io>2026-10-30 00:40:10 +0100
commit79588a8bba1648ea4d396a1d1e9884af54e497e2 (patch)
treebcc795839e1b848bb8e4c5ba017aaef6e9399ecb /scripts
parentadd publish script that names release artifacts from Cargo.toml and the targe... (diff)
downloadzagan-bet-79588a8bba1648ea4d396a1d1e9884af54e497e2.tar.gz
zagan-bet-79588a8bba1648ea4d396a1d1e9884af54e497e2.zip
add deploy script with live verification; cache file hashes by size and mtime
Diffstat (limited to 'scripts')
-rwxr-xr-xscripts/deploy.sh136
-rwxr-xr-xscripts/publish.sh3
2 files changed, 138 insertions, 1 deletions
diff --git a/scripts/deploy.sh b/scripts/deploy.sh
new file mode 100755
index 0000000..99129ad
--- /dev/null
+++ b/scripts/deploy.sh
@@ -0,0 +1,136 @@
1#!/usr/bin/env bash
2# Upload web/ and verify every published artifact over the public URL.
3#
4# Usage:
5# ./scripts/deploy.sh user@host:/srv/zagan.bet https://zagan.bet/
6# ./scripts/deploy.sh /srv/staging http://127.0.0.1:8899/
7# ./scripts/deploy.sh --verify-only user@host:/srv/zagan.bet https://zagan.bet/
8#
9# The target may be a local directory, which is handy for staging and for
10# testing the verification itself. Old versions are left in place on purpose;
11# prune them by hand when you want to drop them.
12#
13# Environment:
14# DEPLOY_TARGET, DEPLOY_URL same as the two arguments
15# SSH_PORT ssh port for a remote target
16set -euo pipefail
17
18ROOT=$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)
19cd "$ROOT"
20
21die() { echo "deploy: $*" >&2; exit 1; }
22
23VERIFY_ONLY=0
24if [ "${1:-}" = "--verify-only" ]; then
25 VERIFY_ONLY=1
26 shift
27fi
28
29TARGET=${1:-${DEPLOY_TARGET:-}}
30URL=${2:-${DEPLOY_URL:-}}
31if [ -z "$TARGET" ] || [ -z "$URL" ]; then
32 cat >&2 <<'EOF'
33usage: scripts/deploy.sh [--verify-only] <target> <url>
34
35 target user@host:/srv/zagan.bet or a local directory
36 url public URL of the page, e.g. https://zagan.bet/
37 --verify-only skips the upload and only checks the live page
38
39environment:
40 DEPLOY_TARGET, DEPLOY_URL same as the two arguments
41 SSH_PORT ssh port for a remote target
42EOF
43 exit 2
44fi
45
46for tool in rsync curl sha256sum; do
47 command -v "$tool" >/dev/null || die "$tool is required"
48done
49
50[ -f web/index.php ] || die "web/index.php is missing"
51
52shopt -s nullglob
53artifacts=(web/bin/client/zagan-bet-* web/bin/server/zagan-bet-*)
54shopt -u nullglob
55[ ${#artifacts[@]} -gt 0 ] || die "no artifacts in web/bin; run scripts/publish.sh first"
56
57if [ "$VERIFY_ONLY" -eq 0 ]; then
58 echo "==> uploading web/ to $TARGET"
59
60 # --delay-updates writes to temp names and renames at the end, so a visitor
61 # never sees a half-written binary. Old versions are kept: no --delete.
62 RSYNC_FLAGS=(
63 -a --delay-updates --human-readable --chmod=D755,F644
64 --exclude=README.md --exclude=.gitignore
65 )
66
67 if [[ "$TARGET" == *:* ]]; then
68 host=${TARGET%%:*}
69 path=${TARGET#*:}
70 ssh_args=()
71 if [ -n "${SSH_PORT:-}" ]; then
72 ssh_args+=(-p "$SSH_PORT")
73 fi
74 ssh ${ssh_args[@]+"${ssh_args[@]}"} "$host" "mkdir -p $(printf '%q' "$path")"
75 rsync "${RSYNC_FLAGS[@]}" -e "ssh ${ssh_args[*]:-}" web/ "$host:$path/"
76 else
77 mkdir -p "$TARGET"
78 rsync "${RSYNC_FLAGS[@]}" web/ "$TARGET/"
79 fi
80fi
81
82echo "==> verifying $URL"
83tmp=$(mktemp -d)
84trap 'rm -rf "$tmp"' EXIT
85
86curl -fsSL --max-time 30 "$URL" -o "$tmp/page.html" \
87 || die "could not fetch $URL"
88
89origin=$(printf '%s' "$URL" | sed -E 's#^(https?://[^/]+).*#\1#')
90grep -oE '[0-9a-f]{64} [A-Za-z0-9._-]+' "$tmp/page.html" > "$tmp/sums" || true
91[ -s "$tmp/sums" ] || die "no SHA-256 lines on the page (is index.php being served?)"
92
93failed=0
94verified=0
95for local in "${artifacts[@]}"; do
96 [ -f "$local" ] || continue
97
98 file=$(basename "$local")
99 kind=$(basename "$(dirname "$local")")
100 local_hash=$(sha256sum "$local" | cut -d' ' -f1)
101
102 href=$(grep -oE "href=\"[^\"]*/bin/$kind/$file\"" "$tmp/page.html" | head -1 \
103 | sed 's/^href="//;s/"$//' || true)
104 if [ -z "$href" ]; then
105 echo " $file: not listed on the page"
106 failed=1
107 continue
108 fi
109
110 shown=$(awk -v name="$file" '$2 == name { print $1 }' "$tmp/sums" | head -1)
111 if [ "$shown" != "$local_hash" ]; then
112 echo " $file: page shows ${shown:-nothing}, local is $local_hash"
113 failed=1
114 continue
115 fi
116
117 if ! curl -fsSL --max-time 120 "$origin$href" -o "$tmp/$file"; then
118 echo " $file: download failed"
119 failed=1
120 continue
121 fi
122
123 remote_hash=$(sha256sum "$tmp/$file" | cut -d' ' -f1)
124 if [ "$remote_hash" != "$local_hash" ]; then
125 echo " $file: downloaded bytes differ from local"
126 failed=1
127 continue
128 fi
129
130 echo " $file: ok"
131 verified=$((verified + 1))
132done
133
134[ "$failed" -eq 0 ] || die "verification failed"
135
136echo "==> $verified artifact(s) verified at $URL"
diff --git a/scripts/publish.sh b/scripts/publish.sh
index d8cc77a..8240859 100755
--- a/scripts/publish.sh
+++ b/scripts/publish.sh
@@ -27,7 +27,7 @@ DEST=${DEST:-web/bin}
27publish() { 27publish() {
28 local triple=$1 kind=$2 platform=$3 arch=$4 28 local triple=$1 kind=$2 platform=$3 arch=$4
29 local ext="" 29 local ext=""
30 if [ "$kind" = client ] && [ "$platform" = windows ]; then 30 if [ "$platform" = windows ]; then
31 ext=".exe" 31 ext=".exe"
32 fi 32 fi
33 33
@@ -51,6 +51,7 @@ publish x86_64-unknown-linux-gnu client linux x86_64
51publish i686-unknown-linux-gnu client linux i686 51publish i686-unknown-linux-gnu client linux i686
52publish x86_64-pc-windows-gnu client windows x86_64 52publish x86_64-pc-windows-gnu client windows x86_64
53publish x86_64-unknown-linux-gnu server linux x86_64 53publish x86_64-unknown-linux-gnu server linux x86_64
54publish x86_64-pc-windows-gnu server windows x86_64
54 55
55echo 56echo
56echo "published $VERSION to $DEST" 57echo "published $VERSION to $DEST"