blob: 1849e64f3ef6f13ee0b127cba52957118fe8f74e (
plain)
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
87
88
89
90
91
92
93
94
95
96
97
|
#!/usr/bin/env bash
# deploy.sh — local native-quarto build + deploy for jayrup.me.
# For machines with quarto installed natively (e.g. Arch laptop / voidlaptop).
# The Docker-based path is deploy-docker.sh (used by meru's post-receive hook).
#
# Strict mode: a failed render exits BEFORE rsync --delete can touch the VPS.
set -euo pipefail
# Resolve the repo root from this script's location and capture the SHA up
# front: the build runs inside a temp dir with no .git, so every later git
# call must be pointed at $REPO explicitly.
REPO="$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" && pwd)"
SHA="$(git -C "$REPO" rev-parse --short HEAD)"
DEPLOY_HOST="${DEPLOY_HOST:-jayrup.me}"
# 1. Render into a fresh temp dir so a partial build never gets deployed
BUILD_DIR="$(mktemp -d /tmp/homepage-local.XXXXXX)"
trap 'rm -rf "$BUILD_DIR"' EXIT
cd "$BUILD_DIR"
git -C "$REPO" archive --format=tar --output="$BUILD_DIR/tree.tar" HEAD
tar xf tree.tar
rm tree.tar
# 2. Render the whole website (HTML)
# This creates the directories and index.html files correctly
quarto render --to html
# 3. Render the Plain Text versions manually to ensure mirroring
# We find every .qmd, and render it to a .txt in the corresponding public/ folder
find . -name "*.qmd" -not -path "./.*" -not -path "./public/*" | while read -r file; do
# Get the directory structure (e.g., ./projects/index.qmd -> projects)
rel_dir=$(dirname "${file#./}")
# Get the filename without extension (e.g., index)
base_name=$(basename "$file" .qmd)
# Render to plain text directly into the correct public sub-directory
quarto render "$file" --to plain --output "${base_name}.txt" --output-dir "public/$rel_dir"
done
# 4. Add cv
cp cv/index.pdf public/cv.pdf
cp cv/index.txt public/cv.txt
# 5. Add assets (static files bundled in the repo)
mkdir -p public/assets
cp -r assets/* public/
cp -r assets/* public/assets/
# .txt twin for curl/wget: normalize to exactly one line ending in a newline,
# so `curl https://jayrup.me/public_key >> ~/.ssh/authorized_keys` appends a
# clean key instead of gluing itself onto the previous line.
KEY="$(tr -d '\r' < assets/public_key | sed -e 's/[[:space:]]*$//' | tr -d '\n')"
[ -n "$KEY" ] || { echo "assets/public_key is empty" >&2; exit 1; }
printf '%s\n' "$KEY" > public/public_key.txt
# 6. Deploy marker
printf 'deployed %s commit %s\n' "$(date -u +%Y-%m-%dT%H:%M:%SZ)" "$SHA" > public/last-deploy.txt
echo "Pushing to vps"
# 7. Content. Only reached if all renders succeeded (set -e)
rsync -avz --delete public/ "$DEPLOY_HOST":~/homepage/public/
# 8. nginx config. It is bind-mounted into the container as a SINGLE FILE, so
# it does not travel with public/, and rsync's rename-into-place would swap
# the inode the mount points at: the container would keep reading the old
# inode while `nginx -t` cheerfully validated it. Stage to a sidecar, then
# copy in place (cat >) so the mounted inode keeps its identity.
# Test before reloading; on failure restore the previous content the same
# way and fail loudly instead of taking the running workers down.
echo "Pushing nginx.conf"
rsync -avz nginx.conf "$DEPLOY_HOST":~/homepage/nginx.conf.new
ssh "$DEPLOY_HOST" 'cp -a ~/homepage/nginx.conf ~/homepage/nginx.conf.prev'
ssh "$DEPLOY_HOST" 'cat ~/homepage/nginx.conf.new > ~/homepage/nginx.conf'
if ssh "$DEPLOY_HOST" 'docker exec homepage nginx -t'; then
ssh "$DEPLOY_HOST" 'docker exec homepage nginx -s reload'
ssh "$DEPLOY_HOST" 'rm -f ~/homepage/nginx.conf.new'
echo "nginx reloaded"
else
echo "!! nginx config failed its test - restoring previous config" >&2
ssh "$DEPLOY_HOST" 'cat ~/homepage/nginx.conf.prev > ~/homepage/nginx.conf'
exit 1
fi
# A single-file bind mount pins whatever inode existed when the container
# started. If the host file was ever replaced by rename (an older deploy, an
# editor, `cp`), the container keeps serving the old bytes while `nginx -t`
# validates them happily. Compare what the container actually sees and restart
# to re-resolve the path if it has drifted.
HOST_MD5="$(ssh "$DEPLOY_HOST" 'md5sum ~/homepage/nginx.conf' | cut -d' ' -f1)"
CTR_MD5="$(ssh "$DEPLOY_HOST" 'docker exec homepage md5sum /etc/nginx/conf.d/default.conf' | cut -d' ' -f1)"
if [ "$HOST_MD5" != "$CTR_MD5" ]; then
echo "!! container is serving a stale nginx.conf (bind mount detached) - restarting" >&2
ssh "$DEPLOY_HOST" 'docker restart homepage'
CTR_MD5="$(ssh "$DEPLOY_HOST" 'docker exec homepage md5sum /etc/nginx/conf.d/default.conf' | cut -d' ' -f1)"
[ "$HOST_MD5" = "$CTR_MD5" ] || { echo "!! nginx.conf still not mounted - aborting" >&2; exit 1; }
fi
|