refactor Consolidate setup scripts into one

Replace setup-root, fix-layout-root, and reset-srv-hito-root with a single setup.sh exposing install, deploy, and reset subcommands over shared building blocks. The one-time fix-layout corrective is dropped; reset supersedes it.

Commit
1281928db8e49d0b550a7a889930fdc8c42eb33c
Author
Marius Peter <dev@marius-peter.com>
Author date
Committer
Marius Peter <dev@marius-peter.com>
Committer date
Changed files
deploy/fix-layout-root.sh
index bd07f486..00000000 100755..000000
@@ -1,119 +0,0 @@
1 Removed: #!/usr/bin/env bash
2 Removed: # Corrective root script for the hito deploy layout.
3 Removed: #
4 Removed: # A manual seed nested shared/ and releases/ INSIDE the live release, so the
5 Removed: # unit's /srv/hito/shared/hito.sqlite path vanished and SQLite could not open
6 Removed: # the DB. This restores the intended sibling layout:
7 Removed: #
8 Removed: # /srv/hito/shared/{hito.sqlite,_opam} (never inside a release)
9 Removed: # /srv/hito/releases/<ts>/ (the app tree)
10 Removed: # /srv/hito/current -> releases/<ts>
11 Removed: #
12 Removed: # Run as root: sudo bash fix-layout-root.sh
13 Removed: # Idempotent and conservative: it moves, never deletes, the database.
14 Removed: set -euo pipefail
15 Removed:
16 Removed: SRV=/srv/hito
17 Removed: DEPLOY_USER=mpeter
18 Removed: DEPLOY_GROUP=git
19 Removed:
20 Removed: # The release that is currently live (resolve the symlink, strip trailing /).
21 Removed: LIVE=$(readlink -f "$SRV/current")
22 Removed: echo "live release: $LIVE"
23 Removed: [ -d "$LIVE" ] || { echo "current does not resolve to a directory"; exit 1; }
24 Removed:
25 Removed: echo "== 1. restore top-level shared/ =="
26 Removed: mkdir -p "$SRV/shared"
27 Removed: # Move the DB back to the top-level shared dir if it drifted into the release.
28 Removed: if [ -f "$LIVE/shared/hito.sqlite" ] && [ ! -e "$SRV/shared/hito.sqlite" ]; then
29 Removed: mv "$LIVE/shared/hito.sqlite" "$SRV/shared/hito.sqlite"
30 Removed: echo " moved DB -> $SRV/shared/hito.sqlite"
31 Removed: fi
32 Removed: # Move the shared opam switch back up if it drifted into the release.
33 Removed: if [ -d "$LIVE/shared/_opam" ] && [ ! -e "$SRV/shared/_opam" ]; then
34 Removed: mv "$LIVE/shared/_opam" "$SRV/shared/_opam"
35 Removed: echo " moved shared switch -> $SRV/shared/_opam"
36 Removed: fi
37 Removed:
38 Removed: echo "== 2. relocate the live release under releases/ =="
39 Removed: mkdir -p "$SRV/releases"
40 Removed: # Drop the nested shared/ and releases/ that the manual seed pulled in.
41 Removed: rm -rf "$LIVE/shared" "$LIVE/releases"
42 Removed: base=$(basename "$LIVE")
43 Removed: if [ "$(dirname "$LIVE")" != "$SRV/releases" ]; then
44 Removed: mv "$LIVE" "$SRV/releases/$base"
45 Removed: LIVE="$SRV/releases/$base"
46 Removed: echo " moved release -> $LIVE"
47 Removed: fi
48 Removed:
49 Removed: echo "== 3. ownership and current symlink =="
50 Removed: chown -R "$DEPLOY_USER:$DEPLOY_GROUP" "$SRV/shared" "$SRV/releases"
51 Removed: ln -sfn "$LIVE" "$SRV/current.tmp"
52 Removed: mv -T "$SRV/current.tmp" "$SRV/current"
53 Removed: chown -h "$DEPLOY_USER:$DEPLOY_GROUP" "$SRV/current"
54 Removed: echo " current -> $LIVE"
55 Removed:
56 Removed: echo "== 4. wire DB into the release and reinstall the corrected hook =="
57 Removed: ln -sfn "$SRV/shared/hito.sqlite" "$LIVE/hito.sqlite"
58 Removed: install -o "$DEPLOY_USER" -g "$DEPLOY_GROUP" -m 0755 \
59 Removed: "$(dirname "$0")/post-receive" /srv/git/hito.git/hooks/post-receive
60 Removed:
61 Removed: echo "== 5. remove the obsolete top-level working clone =="
62 Removed: # /srv/hito must contain only shared/, releases/, and current. A pre-pipeline
63 Removed: # in-place clone still lives at the top level and makes dune see the hito
64 Removed: # package twice (top-level dune-project AND current/dune-project). Its content
65 Removed: # is fully preserved in the bare repo and in every release, and the only unique
66 Removed: # runtime state (the DB) is already in shared/. Remove ONLY known clone
67 Removed: # entries; never touch shared/, releases/, or current.
68 Removed: for entry in ARCHITECTURE.org RAIL.org REQUESTS.org bin lib test site \
69 Removed: dune dune-project hito.opam hito.sqlite LICENSE \
70 Removed: _build .git .gitignore .ocamlformat .envrc .cache .kiro; do
71 Removed: target="$SRV/$entry"
72 Removed: if [ -e "$target" ] || [ -L "$target" ]; then
73 Removed: rm -rf "$target"
74 Removed: echo " removed $target"
75 Removed: fi
76 Removed: done
77 Removed: # Guard: the three managed entries must survive.
78 Removed: for keep in shared releases current; do
79 Removed: [ -e "$SRV/$keep" ] || { echo "FATAL: $SRV/$keep missing after cleanup"; exit 1; }
80 Removed: done
81 Removed:
82 Removed: echo "== 6. deploy master as a fresh release, built on the shared switch =="
83 Removed: # Build in a staging dir OUTSIDE /srv/hito so dune's upward project-root search
84 Removed: # never sees the current symlink or any sibling release (which would make the
85 Removed: # hito package appear twice). Move the built tree into releases/ only after a
86 Removed: # clean build, then flip current.
87 Removed: NEW_TS=$(date -u +"%Y-%m-%dT%H-%M-%SZ")
88 Removed: NEW_REL="$SRV/releases/$NEW_TS"
89 Removed: STAGE=$(sudo -u "$DEPLOY_USER" mktemp -d "/tmp/hito-stage.XXXXXX")
90 Removed: sha=$(git --git-dir=/srv/git/hito.git rev-parse refs/heads/master)
91 Removed: git --git-dir=/srv/git/hito.git archive "$sha" | tar -x -C "$STAGE"
92 Removed: chown -R "$DEPLOY_USER:$DEPLOY_GROUP" "$STAGE"
93 Removed: sudo -u "$DEPLOY_USER" -H bash -lc "
94 Removed: set -euo pipefail
95 Removed: export OPAMROOT=/home/$DEPLOY_USER/.opam
96 Removed: cd '$STAGE'
97 Removed: eval \"\$(opam env --switch='$SRV/shared' --set-switch)\"
98 Removed: opam install . --deps-only --with-test -y
99 Removed: dune build @check
100 Removed: dune runtest
101 Removed: dune build
102 Removed: "
103 Removed: # Publish the built tree as the new release.
104 Removed: mkdir -p "$SRV/releases"
105 Removed: mv "$STAGE" "$NEW_REL"
106 Removed: ln -sfn "$SRV/shared/hito.sqlite" "$NEW_REL/hito.sqlite"
107 Removed: chown -R "$DEPLOY_USER:$DEPLOY_GROUP" "$NEW_REL"
108 Removed: ln -sfn "$NEW_REL" "$SRV/current.tmp"
109 Removed: mv -T "$SRV/current.tmp" "$SRV/current"
110 Removed: chown -h "$DEPLOY_USER:$DEPLOY_GROUP" "$SRV/current"
111 Removed: echo " current -> $NEW_REL (master ${sha:0:12})"
112 Removed:
113 Removed: echo "== 7. restart =="
114 Removed: systemctl restart hito
115 Removed: sleep 1
116 Removed: systemctl is-active hito && echo "hito is active" || {
117 Removed: echo "still failing; recent journal:"; journalctl -u hito -n 15 --no-pager; exit 1;
118 Removed: }
119 Removed: echo "Done. /srv/hito holds shared/, releases/, current; live release is master."
deploy/reset-srv-hito-root.sh
index 95065c44..00000000 100755..000000
@@ -1,114 +0,0 @@
1 Removed: #!/usr/bin/env bash
2 Removed: # Full reset of /srv/hito to the clean release layout.
3 Removed: #
4 Removed: # Wipes everything under /srv/hito EXCEPT the database and the shared opam
5 Removed: # switch, which are preserved (the DB is the only unique runtime state; the
6 Removed: # switch is expensive to rebuild). Result:
7 Removed: #
8 Removed: # /srv/hito/shared/{hito.sqlite,_opam}
9 Removed: # /srv/hito/releases/<ts>/ (master, freshly built)
10 Removed: # /srv/hito/current -> releases/<ts>
11 Removed: #
12 Removed: # Run as root: sudo bash reset-srv-hito-root.sh
13 Removed: set -euo pipefail
14 Removed:
15 Removed: SRV=/srv/hito
16 Removed: DEPLOY_USER=mpeter
17 Removed: DEPLOY_GROUP=git
18 Removed: BARE=/srv/git/hito.git
19 Removed: DB="$SRV/shared/hito.sqlite"
20 Removed: SWITCH="$SRV/shared/_opam"
21 Removed:
22 Removed: [ -f "$DB" ] || { echo "FATAL: database not found at $DB; aborting before any wipe"; exit 1; }
23 Removed:
24 Removed: echo "== 1. stop the service =="
25 Removed: systemctl stop hito || true
26 Removed:
27 Removed: echo "== 2. back up and set aside the database and shared switch =="
28 Removed: STASH=$(mktemp -d /root/hito-reset-stash.XXXXXX)
29 Removed: cp -a "$DB" "$STASH/hito.sqlite" # safety copy
30 Removed: echo " DB backed up to $STASH/hito.sqlite"
31 Removed: mv "$DB" "$STASH/hito.sqlite.live"
32 Removed: [ -d "$SWITCH" ] && mv "$SWITCH" "$STASH/_opam" && echo " switch set aside"
33 Removed:
34 Removed: echo "== 3. wipe everything under /srv/hito (contents only, keep the dir) =="
35 Removed: # Remove all entries, including dotfiles, without following into the stash.
36 Removed: find "$SRV" -mindepth 1 -maxdepth 1 -exec rm -rf {} +
37 Removed: echo " /srv/hito emptied"
38 Removed:
39 Removed: echo "== 4. rebuild the clean skeleton =="
40 Removed: mkdir -p "$SRV/shared" "$SRV/releases"
41 Removed: mv "$STASH/hito.sqlite.live" "$DB"
42 Removed: [ -d "$STASH/_opam" ] && mv "$STASH/_opam" "$SWITCH"
43 Removed: chown -R "$DEPLOY_USER:$DEPLOY_GROUP" "$SRV/shared" "$SRV/releases"
44 Removed: echo " restored shared/ (DB + switch) and empty releases/"
45 Removed:
46 Removed: echo "== 5. deploy master as the first clean release =="
47 Removed: # Build in a staging dir OUTSIDE /srv/hito so dune's project-root search never
48 Removed: # sees current or a sibling release. Publish only after a clean build.
49 Removed: start=$SECONDS
50 Removed: TS=$(date -u +"%Y-%m-%dT%H-%M-%SZ")
51 Removed: REL="$SRV/releases/$TS"
52 Removed: STAGE=$(sudo -u "$DEPLOY_USER" mktemp -d /tmp/hito-stage.XXXXXX)
53 Removed: sha=$(git --git-dir="$BARE" rev-parse refs/heads/master)
54 Removed: git --git-dir="$BARE" archive "$sha" | tar -x -C "$STAGE"
55 Removed: chown -R "$DEPLOY_USER:$DEPLOY_GROUP" "$STAGE"
56 Removed: sudo -u "$DEPLOY_USER" -H bash -lc "
57 Removed: set -euo pipefail
58 Removed: export OPAMROOT=/home/$DEPLOY_USER/.opam
59 Removed: cd '$STAGE'
60 Removed: eval \"\$(opam env --switch='$SRV/shared' --set-switch)\"
61 Removed: opam install . --deps-only --with-test -y 2>&1 | tee deploy.log
62 Removed: dune build @check 2>&1 | tee -a deploy.log
63 Removed: dune runtest 2>&1 | tee -a deploy.log
64 Removed: dune build 2>&1 | tee -a deploy.log
65 Removed: "
66 Removed: build_secs=$((SECONDS - start))
67 Removed: mv "$STAGE" "$REL"
68 Removed: ln -sfn "$SRV/shared/hito.sqlite" "$REL/hito.sqlite"
69 Removed:
70 Removed: # Deploy metadata, matching the hook: provenance in the release and a durable
71 Removed: # history line under shared/.
72 Removed: subject=$(git --git-dir="$BARE" log -1 --format=%s "$sha")
73 Removed: cat >"$REL/deploy-info.json" <<JSON
74 Removed: {
75 Removed: "commit": "$sha",
76 Removed: "subject": $(printf '%s' "$subject" | sed 's/\\/\\\\/g; s/"/\\"/g; s/.*/"&"/'),
77 Removed: "branch": "master",
78 Removed: "deployed_at": "$TS",
79 Removed: "build_seconds": $build_secs,
80 Removed: "release": "$REL"
81 Removed: }
82 Removed: JSON
83 Removed: printf '%s\t%s\t%ss\t%s\t%s\n' \
84 Removed: "$TS" "${sha:0:12}" "$build_secs" "master" "$subject" \
85 Removed: >>"$SRV/shared/deploy-history.tsv"
86 Removed:
87 Removed: chown -R "$DEPLOY_USER:$DEPLOY_GROUP" "$REL"
88 Removed: chown "$DEPLOY_USER:$DEPLOY_GROUP" "$SRV/shared/deploy-history.tsv"
89 Removed: ln -sfn "$REL" "$SRV/current.tmp"
90 Removed: mv -T "$SRV/current.tmp" "$SRV/current"
91 Removed: chown -h "$DEPLOY_USER:$DEPLOY_GROUP" "$SRV/current"
92 Removed: echo " current -> $REL (master ${sha:0:12}, built in ${build_secs}s)"
93 Removed:
94 Removed: echo "== 6. start the service and health-check =="
95 Removed: systemctl start hito
96 Removed: HEALTH_URL="http://127.0.0.1:8080/login"
97 Removed: healthy=false
98 Removed: for _ in $(seq 1 10); do
99 Removed: if curl -sf -o /dev/null --max-time 3 "$HEALTH_URL"; then healthy=true; break; fi
100 Removed: sleep 1
101 Removed: done
102 Removed: if $healthy; then
103 Removed: echo " hito is healthy at $HEALTH_URL"
104 Removed: else
105 Removed: echo " FAILED health check; recent journal:"
106 Removed: journalctl -u hito -n 15 --no-pager
107 Removed: exit 1
108 Removed: fi
109 Removed:
110 Removed: echo
111 Removed: echo "Done. /srv/hito now holds only shared/, releases/, current."
112 Removed: echo "Safety backup of the DB remains at $STASH/hito.sqlite"
113 Removed: echo "Remove it once you have confirmed the app works: rm -rf $STASH"
114 Removed: ls -la "$SRV"
deploy/setup-root.sh
index 594f6b9d..00000000 100755..000000
@@ -1,69 +0,0 @@
1 Removed: #!/usr/bin/env bash
2 Removed: # One-time root setup for the hito push-to-deploy pipeline on the VPS.
3 Removed: # Run as root: sudo bash setup-root.sh
4 Removed: #
5 Removed: # Idempotent: safe to re-run. It does not touch the live release or the
6 Removed: # database beyond moving the DB once into the shared directory.
7 Removed: set -euo pipefail
8 Removed:
9 Removed: DEPLOY_USER=mpeter
10 Removed: DEPLOY_GROUP=git
11 Removed: SRV=/srv/hito
12 Removed: BARE=/srv/git/hito.git
13 Removed: UNIT=/etc/systemd/system/hito.service
14 Removed: SUDOERS=/etc/sudoers.d/hito-deploy
15 Removed: SHARED="$SRV/shared"
16 Removed: RELEASES="$SRV/releases"
17 Removed:
18 Removed: echo "== 1. release and shared directories =="
19 Removed: mkdir -p "$RELEASES" "$SHARED"
20 Removed: # Move the live database under shared/ exactly once. The unit is edited below
21 Removed: # to read it from there, so a new release never orphans the data.
22 Removed: if [ -f "$SRV/hito.sqlite" ] && [ ! -e "$SHARED/hito.sqlite" ]; then
23 Removed: mv "$SRV/hito.sqlite" "$SHARED/hito.sqlite"
24 Removed: echo " moved hito.sqlite -> $SHARED/hito.sqlite"
25 Removed: fi
26 Removed: chown -R "$DEPLOY_USER:$DEPLOY_GROUP" "$RELEASES" "$SHARED"
27 Removed:
28 Removed: echo "== 2. shared opam switch (matches existing 5.5.0) =="
29 Removed: # Built once as the deploy user, reused by every release unless hito.opam
30 Removed: # changes. Kept outside releases so pruning a release never deletes it.
31 Removed: if [ ! -d "$SHARED/_opam" ]; then
32 Removed: sudo -u "$DEPLOY_USER" -H bash -lc "
33 Removed: export OPAMROOT=/home/$DEPLOY_USER/.opam
34 Removed: opam switch create '$SHARED' ocaml-base-compiler.5.5.0 --no-install -y
35 Removed: "
36 Removed: echo " created shared switch at $SHARED/_opam"
37 Removed: else
38 Removed: echo " shared switch already present"
39 Removed: fi
40 Removed:
41 Removed: echo "== 3. post-receive hook =="
42 Removed: install -o "$DEPLOY_USER" -g "$DEPLOY_GROUP" -m 0755 \
43 Removed: "$(dirname "$0")/post-receive" "$BARE/hooks/post-receive"
44 Removed: echo " installed $BARE/hooks/post-receive"
45 Removed:
46 Removed: echo "== 4. scoped sudoers for restart =="
47 Removed: cat > "$SUDOERS" <<SUDO
48 Removed: $DEPLOY_USER ALL=(root) NOPASSWD: /usr/bin/systemctl restart hito
49 Removed: SUDO
50 Removed: chmod 0440 "$SUDOERS"
51 Removed: visudo -c -f "$SUDOERS"
52 Removed:
53 Removed: echo "== 5. systemd unit -> current/ =="
54 Removed: # Point ExecStart and WorkingDirectory at the current symlink, read the DB
55 Removed: # from shared/, and keep the existing hardening (ReadWritePaths already /srv/hito).
56 Removed: cp -a "$UNIT" "$UNIT.bak.$(date +%s)"
57 Removed: sed -i \
58 Removed: -e 's#^ExecStart=.*#ExecStart=/srv/hito/current/_build/default/bin/main.exe#' \
59 Removed: -e 's#^WorkingDirectory=.*#WorkingDirectory=/srv/hito/current#' \
60 Removed: -e 's#^Environment=HITO_DB=.*#Environment=HITO_DB=sqlite3:/srv/hito/shared/hito.sqlite#' \
61 Removed: "$UNIT"
62 Removed: systemctl daemon-reload
63 Removed: echo " unit updated (backup kept alongside)."
64 Removed:
65 Removed: echo
66 Removed: echo "Done. The service still runs the old in-place build until the first"
67 Removed: echo "successful push flips /srv/hito/current. To go live now without a push:"
68 Removed: echo " 1) su - $DEPLOY_USER and run deploy/first-release.sh (creates current/)"
69 Removed: echo " 2) sudo systemctl restart hito"
deploy/setup.sh
index 00000000..0e7b4e60 000000..100755
@@ -0,0 +1,208 @@
1 Added: #!/usr/bin/env bash
2 Added: # hito deployment setup — one root script, three subcommands.
3 Added: #
4 Added: # sudo bash setup.sh install One-time host setup: shared switch, hook,
5 Added: # scoped sudoers, and systemd unit pointed at
6 Added: # current/. Idempotent. Does NOT create a
7 Added: # release; the first push (or `deploy`) does.
8 Added: #
9 Added: # sudo bash setup.sh deploy Build master as a fresh release on the shared
10 Added: # switch and flip current. Health-checks and
11 Added: # rolls back on failure. Use to go live without
12 Added: # a push, or after `install`.
13 Added: #
14 Added: # sudo bash setup.sh reset Wipe /srv/hito to the clean layout, preserving
15 Added: # the database and the shared switch, then run
16 Added: # `deploy`. Destructive: everything else under
17 Added: # /srv/hito is removed.
18 Added: #
19 Added: # The hook (post-receive) beside this script is the per-push equivalent of
20 Added: # `deploy`; `install` copies it into the bare repo.
21 Added: set -euo pipefail
22 Added:
23 Added: DEPLOY_USER=mpeter
24 Added: DEPLOY_GROUP=git
25 Added: SRV=/srv/hito
26 Added: BARE=/srv/git/hito.git
27 Added: UNIT=/etc/systemd/system/hito.service
28 Added: SUDOERS=/etc/sudoers.d/hito-deploy
29 Added: SHARED="$SRV/shared"
30 Added: RELEASES="$SRV/releases"
31 Added: DB="$SHARED/hito.sqlite"
32 Added: SWITCH="$SHARED/_opam"
33 Added: OCAML_COMPILER=ocaml-base-compiler.5.5.0
34 Added: HEALTH_URL="http://127.0.0.1:8080/login"
35 Added: HERE=$(cd "$(dirname "$0")" && pwd)
36 Added:
37 Added: require_root() { [ "$(id -u)" -eq 0 ] || { echo "run as root"; exit 1; }; }
38 Added:
39 Added: # --- shared building blocks -------------------------------------------------
40 Added:
41 Added: ensure_switch() {
42 Added: if [ ! -d "$SWITCH" ]; then
43 Added: echo " creating shared switch ($OCAML_COMPILER)"
44 Added: sudo -u "$DEPLOY_USER" -H bash -lc "
45 Added: export OPAMROOT=/home/$DEPLOY_USER/.opam
46 Added: opam switch create '$SHARED' '$OCAML_COMPILER' --no-install -y
47 Added: "
48 Added: else
49 Added: echo " shared switch present"
50 Added: fi
51 Added: }
52 Added:
53 Added: install_hook() {
54 Added: install -o "$DEPLOY_USER" -g "$DEPLOY_GROUP" -m 0755 \
55 Added: "$HERE/post-receive" "$BARE/hooks/post-receive"
56 Added: echo " installed $BARE/hooks/post-receive"
57 Added: }
58 Added:
59 Added: install_sudoers() {
60 Added: cat >"$SUDOERS" <<SUDO
61 Added: $DEPLOY_USER ALL=(root) NOPASSWD: /usr/bin/systemctl restart hito
62 Added: SUDO
63 Added: chmod 0440 "$SUDOERS"
64 Added: visudo -c -f "$SUDOERS" >/dev/null && echo " sudoers valid"
65 Added: }
66 Added:
67 Added: point_unit_at_current() {
68 Added: cp -a "$UNIT" "$UNIT.bak.$(date +%s)"
69 Added: sed -i \
70 Added: -e 's#^ExecStart=.*#ExecStart=/srv/hito/current/_build/default/bin/main.exe#' \
71 Added: -e 's#^WorkingDirectory=.*#WorkingDirectory=/srv/hito/current#' \
72 Added: -e 's#^Environment=HITO_DB=.*#Environment=HITO_DB=sqlite3:/srv/hito/shared/hito.sqlite#' \
73 Added: "$UNIT"
74 Added: systemctl daemon-reload
75 Added: echo " unit points at current/ (backup kept alongside)"
76 Added: }
77 Added:
78 Added: # Build master in a staging dir OUTSIDE /srv/hito (so dune's project-root search
79 Added: # never sees current or a sibling release), publish it, flip current, restart,
80 Added: # health-check, and roll back on failure. Records deploy metadata.
81 Added: build_and_flip() {
82 Added: local start ts rel stage sha subject build_secs prev
83 Added: start=$SECONDS
84 Added: ts=$(date -u +"%Y-%m-%dT%H-%M-%SZ")
85 Added: rel="$RELEASES/$ts"
86 Added: prev=$(readlink -f "$SRV/current" 2>/dev/null || true)
87 Added: sha=$(git --git-dir="$BARE" rev-parse refs/heads/master)
88 Added: stage=$(sudo -u "$DEPLOY_USER" mktemp -d /tmp/hito-stage.XXXXXX)
89 Added:
90 Added: echo " building master ${sha:0:12}"
91 Added: git --git-dir="$BARE" archive "$sha" | tar -x -C "$stage"
92 Added: chown -R "$DEPLOY_USER:$DEPLOY_GROUP" "$stage"
93 Added: sudo -u "$DEPLOY_USER" -H bash -lc "
94 Added: set -euo pipefail
95 Added: export OPAMROOT=/home/$DEPLOY_USER/.opam
96 Added: cd '$stage'
97 Added: eval \"\$(opam env --switch='$SHARED' --set-switch)\"
98 Added: opam install . --deps-only --with-test -y 2>&1 | tee deploy.log
99 Added: dune build @check 2>&1 | tee -a deploy.log
100 Added: dune runtest 2>&1 | tee -a deploy.log
101 Added: dune build 2>&1 | tee -a deploy.log
102 Added: "
103 Added: build_secs=$((SECONDS - start))
104 Added:
105 Added: mkdir -p "$RELEASES"
106 Added: mv "$stage" "$rel"
107 Added: ln -sfn "$DB" "$rel/hito.sqlite"
108 Added:
109 Added: subject=$(git --git-dir="$BARE" log -1 --format=%s "$sha")
110 Added: cat >"$rel/deploy-info.json" <<JSON
111 Added: {
112 Added: "commit": "$sha",
113 Added: "subject": $(printf '%s' "$subject" | sed 's/\\/\\\\/g; s/"/\\"/g; s/.*/"&"/'),
114 Added: "branch": "master",
115 Added: "deployed_at": "$ts",
116 Added: "build_seconds": $build_secs,
117 Added: "release": "$rel"
118 Added: }
119 Added: JSON
120 Added: printf '%s\t%s\t%ss\t%s\t%s\n' \
121 Added: "$ts" "${sha:0:12}" "$build_secs" "master" "$subject" \
122 Added: >>"$SHARED/deploy-history.tsv"
123 Added:
124 Added: chown -R "$DEPLOY_USER:$DEPLOY_GROUP" "$rel"
125 Added: chown "$DEPLOY_USER:$DEPLOY_GROUP" "$SHARED/deploy-history.tsv"
126 Added: ln -sfn "$rel" "$SRV/current.tmp"
127 Added: mv -T "$SRV/current.tmp" "$SRV/current"
128 Added: chown -h "$DEPLOY_USER:$DEPLOY_GROUP" "$SRV/current"
129 Added: echo " current -> $rel (built in ${build_secs}s)"
130 Added:
131 Added: systemctl restart hito
132 Added: if health_ok; then
133 Added: echo " healthy at $HEALTH_URL"
134 Added: elif [ -n "$prev" ] && [ -d "$prev" ]; then
135 Added: echo " health check failed; rolling back to $prev"
136 Added: ln -sfn "$prev" "$SRV/current.tmp"
137 Added: mv -T "$SRV/current.tmp" "$SRV/current"
138 Added: systemctl restart hito || true
139 Added: echo " rolled back; recent journal:"; journalctl -u hito -n 15 --no-pager
140 Added: exit 1
141 Added: else
142 Added: echo " health check failed and no previous release; journal:"
143 Added: journalctl -u hito -n 15 --no-pager; exit 1
144 Added: fi
145 Added: }
146 Added:
147 Added: health_ok() {
148 Added: local i
149 Added: for i in $(seq 1 10); do
150 Added: curl -sf -o /dev/null --max-time 3 "$HEALTH_URL" && return 0
151 Added: sleep 1
152 Added: done
153 Added: return 1
154 Added: }
155 Added:
156 Added: # --- subcommands ------------------------------------------------------------
157 Added:
158 Added: cmd_install() {
159 Added: echo "== shared and releases directories =="
160 Added: mkdir -p "$RELEASES" "$SHARED"
161 Added: # Adopt a pre-existing in-place DB exactly once.
162 Added: if [ -f "$SRV/hito.sqlite" ] && [ ! -e "$DB" ]; then
163 Added: mv "$SRV/hito.sqlite" "$DB"; echo " adopted existing hito.sqlite -> $DB"
164 Added: fi
165 Added: chown -R "$DEPLOY_USER:$DEPLOY_GROUP" "$RELEASES" "$SHARED"
166 Added: echo "== shared opam switch =="; ensure_switch
167 Added: echo "== post-receive hook =="; install_hook
168 Added: echo "== scoped sudoers =="; install_sudoers
169 Added: echo "== systemd unit =="; point_unit_at_current
170 Added: echo
171 Added: echo "Done. Run 'sudo bash setup.sh deploy' to build the first release,"
172 Added: echo "or push master to trigger the hook."
173 Added: }
174 Added:
175 Added: cmd_deploy() {
176 Added: [ -d "$SWITCH" ] || { echo "FATAL: no shared switch; run 'install' first"; exit 1; }
177 Added: echo "== deploy master =="; build_and_flip
178 Added: }
179 Added:
180 Added: cmd_reset() {
181 Added: [ -f "$DB" ] || { echo "FATAL: no database at $DB; aborting before wipe"; exit 1; }
182 Added: echo "== stop service =="; systemctl stop hito || true
183 Added: echo "== stash DB and switch =="
184 Added: local stash; stash=$(mktemp -d /root/hito-reset-stash.XXXXXX)
185 Added: cp -a "$DB" "$stash/hito.sqlite" # safety copy, never removed by us
186 Added: mv "$DB" "$stash/hito.sqlite.live"
187 Added: [ -d "$SWITCH" ] && mv "$SWITCH" "$stash/_opam"
188 Added: echo " DB backup at $stash/hito.sqlite"
189 Added: echo "== wipe /srv/hito contents =="
190 Added: find "$SRV" -mindepth 1 -maxdepth 1 -exec rm -rf {} +
191 Added: echo "== rebuild skeleton =="
192 Added: mkdir -p "$SHARED" "$RELEASES"
193 Added: mv "$stash/hito.sqlite.live" "$DB"
194 Added: [ -d "$stash/_opam" ] && mv "$stash/_opam" "$SWITCH"
195 Added: chown -R "$DEPLOY_USER:$DEPLOY_GROUP" "$SHARED" "$RELEASES"
196 Added: ensure_switch
197 Added: echo "== deploy master =="; build_and_flip
198 Added: echo
199 Added: echo "Done. Safety DB backup remains at $stash/hito.sqlite (remove when confident)."
200 Added: }
201 Added:
202 Added: require_root
203 Added: case "${1:-}" in
204 Added: install) cmd_install ;;
205 Added: deploy) cmd_deploy ;;
206 Added: reset) cmd_reset ;;
207 Added: *) echo "usage: sudo bash setup.sh {install|deploy|reset}"; exit 2 ;;
208 Added: esac