blob: c345a218704c2bf4031b3c34db9156f58e1d7213 (
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
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
|
#!/usr/bin/env bash
# Deploy a locally built bokf image to BOKF_HOST over SSH. See docs/DEPLOY.md.
#
# scripts/deploy.sh [tag]
#
# BOKF_HOST (user@host) and BOKF_REMOTE_DIR are read from the environment or
# from .env in the repository root. The host needs only Docker and Compose.
set -euo pipefail
cd "$(dirname "$0")/.."
env_val() {
sed -n "s/^$1=//p" .env 2>/dev/null | tail -n 1
}
if [ -f .env ]; then
BOKF_HOST="${BOKF_HOST:-$(env_val BOKF_HOST)}"
BOKF_REMOTE_DIR="${BOKF_REMOTE_DIR:-$(env_val BOKF_REMOTE_DIR)}"
BOKF_BUILD="${BOKF_BUILD:-$(env_val BOKF_BUILD)}"
fi
: "${BOKF_HOST:?set BOKF_HOST (user@host) in .env or the environment}"
BOKF_REMOTE_DIR="${BOKF_REMOTE_DIR:-/srv/bokf}"
SSH=(ssh "$BOKF_HOST")
TAG="${1:-}"
if [ -z "$TAG" ]; then
TAG="$(git describe --tags --always --dirty 2>/dev/null || echo dev)"
fi
case "$TAG" in
*[!A-Za-z0-9._-]* | "")
echo "deploy: invalid tag '$TAG'" >&2
exit 1
;;
esac
remote() { "${SSH[@]}" "$1"; }
echo "deploy: gate: build + tests"
make -j"$(nproc)"
make test
BOKF_BUILD="${BOKF_BUILD:-auto}"
case "$BOKF_BUILD" in
auto)
host_arch="$(remote 'uname -m' 2>/dev/null || echo unknown)"
if [ "$(uname -m)" = "$host_arch" ]; then
BOKF_BUILD=local
else
echo "deploy: host is $host_arch, cross-building here"
BOKF_BUILD=remote
fi
;;
local | remote) ;;
*)
echo "deploy: BOKF_BUILD must be auto, local or remote" >&2
exit 1
;;
esac
if [ "$BOKF_BUILD" = local ]; then
echo "deploy: building image bokf:$TAG locally"
docker build --build-arg "VERSION=$TAG" -t "bokf:$TAG" .
echo "deploy: shipping image to $BOKF_HOST"
docker save "bokf:$TAG" | gzip | "${SSH[@]}" 'gunzip | docker load'
else
echo "deploy: cross-compiling $host_arch binaries on this machine"
rm -rf .prebuilt
mkdir -p .prebuilt
docker build -q -f deploy/Dockerfile.cross -t bokf-cross deploy/ >/dev/null
docker run --rm -e "VERSION=$TAG" -v "$PWD":/src:ro \
-v "$PWD/.prebuilt":/out bokf-cross
if command -v file >/dev/null 2>&1 &&
! file .prebuilt/bokfd | grep -q aarch64; then
echo "deploy: cross-build produced no arm64 binary" >&2
rm -rf .prebuilt
exit 1
fi
echo "deploy: assembling image bokf:$TAG on $BOKF_HOST"
tar -cf - \
--exclude=./.git --exclude=./build --exclude=./var --exclude=./.env \
--exclude='./*.se' --exclude='./*.db' --exclude='./*.db-wal' \
--exclude='./*.db-shm' --exclude=./docs . |
"${SSH[@]}" "docker build --build-arg 'VERSION=$TAG' -t 'bokf:$TAG' -"
rm -rf .prebuilt
fi
remote "mkdir -p '$BOKF_REMOTE_DIR'"
scp -q compose.yaml "$BOKF_HOST:$BOKF_REMOTE_DIR/compose.yaml"
prev_tag="$(remote "grep -E '^BOKF_TAG=' '$BOKF_REMOTE_DIR/.env' 2>/dev/null | cut -d= -f2" || true)"
remote "touch '$BOKF_REMOTE_DIR/.env'
sed -i '/^BOKF_IMAGE=/d;/^BOKF_TAG=/d' '$BOKF_REMOTE_DIR/.env'
printf 'BOKF_IMAGE=bokf\nBOKF_TAG=%s\n' '$TAG' >> '$BOKF_REMOTE_DIR/.env'"
if ! remote "test -f '$BOKF_REMOTE_DIR/var/db/bokfd.db'"; then
echo "deploy: fresh host: no database yet."
echo "deploy: run once on $BOKF_HOST, then deploy again:"
echo " cd $BOKF_REMOTE_DIR"
echo " docker compose run --rm -e BOKFD_PASSWORD='<admin-password>' bokfd init"
echo " docker compose up -d"
exit 0
fi
echo "deploy: starting containers"
remote "cd '$BOKF_REMOTE_DIR' && docker compose up -d --no-build"
wait_healthy() {
for _ in $(seq 1 30); do
cid="$(remote "cd '$BOKF_REMOTE_DIR' && docker compose ps -q bokfd" 2>/dev/null || true)"
if [ -n "$cid" ]; then
st="$(remote "docker inspect --format '{{.State.Health.Status}}' '$cid'" 2>/dev/null || true)"
if [ "$st" = "healthy" ]; then
return 0
fi
fi
sleep 2
done
return 1
}
if wait_healthy; then
echo "deploy: bokf:$TAG is healthy on $BOKF_HOST"
exit 0
fi
echo "deploy: health check failed for bokf:$TAG" >&2
remote "cd '$BOKF_REMOTE_DIR' && docker compose logs --tail=50 bokfd" >&2 || true
if [ -n "$prev_tag" ] && [ "$prev_tag" != "$TAG" ]; then
echo "deploy: rolling back to bokf:$prev_tag" >&2
remote "sed -i 's/^BOKF_TAG=.*/BOKF_TAG=$prev_tag/' '$BOKF_REMOTE_DIR/.env'"
if remote "cd '$BOKF_REMOTE_DIR' && docker compose up -d --no-build" &&
wait_healthy; then
echo "deploy: rollback to bokf:$prev_tag is healthy" >&2
else
echo "deploy: rollback FAILED, manual attention needed" >&2
fi
fi
exit 1
|