blob: 0d1100d9921e548a4dbfab5834f7f6d8a7956f91 (
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
98
99
100
101
102
103
104
105
106
|
#!/usr/bin/env bash
# deploy-docker.sh — build + deploy jayrup.me. Versioned in the repo so the
# post-receive hook always uses the deploy logic that matches the pushed tree.
#
# Designed to run ON MERU (docker-only machine) from a git archive snapshot:
# git archive HEAD | tar -x -C "$BUILD_DIR" && bash "$BUILD_DIR/deploy-docker.sh" "$BUILD_DIR" <sha>
# Quarto runs inside the pinned devxygmbh/alpine-quarto container; rsync ships
# public/ to nandi (jayrup.me).
#
# Usage: deploy-docker.sh <build-dir> <short-sha>
set -euo pipefail
BUILD_DIR="${1:?usage: deploy-docker.sh <build-dir> <short-sha>}"
SHORT_SHA="${2:?missing short sha}"
cd "$BUILD_DIR"
# Pinned digest — update deliberately (docker pull devxygmbh/alpine-quarto:latest
# then replace the sha256:).
QUARTO_IMAGE="${QUARTO_IMAGE:-devxygmbh/alpine-quarto@sha256:0c0d785139b467ab75c053bc24463d2450cbf928ca8ece8d9a20dedba9568fa9}"
# Fallback if meru can't reach jayrup.me (IPv6): DEPLOY_HOST=jayrup@100.94.131.98
DEPLOY_HOST="${DEPLOY_HOST:-jayrup.me}"
RUN_UID="$(id -u)"
RUN_GID="$(id -g)"
# Render script runs INSIDE the container (one container spin, both passes).
# HOME=/tmp so quarto's cache is writable by the unprivileged uid.
# Written into the private build dir (not /tmp) per review finding.
cat > "$BUILD_DIR/homepage-render.sh" <<'RENDER_EOF'
#!/usr/bin/env bash
set -euo pipefail
cd /site
rm -rf public
quarto render --to html
find . -name "*.qmd" -not -path "./.*" -not -path "./public/*" | while read -r file; do
rel_dir=$(dirname "${file#./}")
base_name=$(basename "$file" .qmd)
quarto render "$file" --to plain --output "${base_name}.txt" --output-dir "public/$rel_dir"
done
RENDER_EOF
chmod +x "$BUILD_DIR/homepage-render.sh"
echo ">> rendering with $QUARTO_IMAGE (commit $SHORT_SHA)"
# --entrypoint /bin/bash overrides whatever entrypoint the image declares
docker run --rm \
--entrypoint /bin/bash \
-u "$RUN_UID:$RUN_GID" \
-e HOME=/tmp \
-v "$BUILD_DIR":/site \
-v "$BUILD_DIR/homepage-render.sh":/render.sh:ro \
-w /site \
"$QUARTO_IMAGE" /render.sh
# Static assets bundled in the repo
mkdir -p public/cv public/assets
cp cv/index.pdf public/cv.pdf
cp cv/index.txt public/cv.txt
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
# Deploy status marker — site-visible proof of the last successful deploy
printf 'deployed %s commit %s\n' "$(date -u +%Y-%m-%dT%H:%M:%SZ)" "$SHORT_SHA" > public/last-deploy.txt
echo ">> pushing to $DEPLOY_HOST"
rsync -avz --delete public/ "$DEPLOY_HOST":~/homepage/public/
# nginx config is bind-mounted into the container as a SINGLE FILE, so rsync's
# rename-into-place would swap the inode the mount points at and the container
# would keep reading the old file while `nginx -t` validated the old bytes.
# Stage to a sidecar, copy in place so the mounted inode keeps its identity,
# test, and only then reload; restore the previous content on failure.
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
echo ">> deploy OK ($SHORT_SHA)"
|