|
1 | 1 | #!/usr/bin/env bash |
2 | | -# Deploy the Antora site to bremen2 under |
3 | | -# /var/www/elixir-phoenix-ash/releases/<timestamp>/ |
4 | | -# and atomically swap the `current` symlink. |
| 2 | +# Deploy two Antora sites from this repo: |
5 | 3 | # |
6 | | -# Runs on the `eliph` self-hosted GitHub Actions runner from the |
7 | | -# actions/checkout workdir. Invoked as `./scripts/deploy.sh`. |
| 4 | +# - main Phoenix book -> /var/www/elixir-phoenix-ash/ |
| 5 | +# - Elixir mini-book -> /var/www/elixir-book/ |
8 | 6 | # |
9 | | -# The Antora build needs Node (provided via mise under ~eliph). |
| 7 | +# Both land via timestamped releases and an atomic `current` |
| 8 | +# symlink swap. Runs on the `eliph` self-hosted GitHub Actions |
| 9 | +# runner from the actions/checkout workdir. |
| 10 | +# |
| 11 | +# Node is provided via mise under ~eliph. |
10 | 12 |
|
11 | 13 | set -euo pipefail |
12 | 14 |
|
13 | | -# Activate mise so node/npm/npx resolve on the non-interactive shell. |
14 | 15 | if command -v mise >/dev/null 2>&1; then |
15 | 16 | eval "$(mise activate bash)" |
16 | 17 | elif [ -x "$HOME/.local/bin/mise" ]; then |
17 | 18 | eval "$("$HOME/.local/bin/mise" activate bash)" |
18 | 19 | fi |
19 | 20 |
|
20 | | -APP_DIR="/var/www/elixir-phoenix-ash" |
21 | | -RELEASES_DIR="${APP_DIR}/releases" |
22 | | -CURRENT_LINK="${APP_DIR}/current" |
23 | | -SHARED_DIR="${APP_DIR}/shared" |
24 | | -LOCK_FILE="${SHARED_DIR}/.deploy.lock" |
| 21 | +PHOENIX_APP_DIR="/var/www/elixir-phoenix-ash" |
| 22 | +ELIXIR_APP_DIR="/var/www/elixir-book" |
25 | 23 | KEEP_RELEASES=5 |
26 | 24 | TIMESTAMP="$(date +%Y%m%d%H%M%S)" |
27 | | -RELEASE_DIR="${RELEASES_DIR}/${TIMESTAMP}" |
| 25 | + |
| 26 | +PHOENIX_RELEASE_DIR="${PHOENIX_APP_DIR}/releases/${TIMESTAMP}" |
| 27 | +ELIXIR_RELEASE_DIR="${ELIXIR_APP_DIR}/releases/${TIMESTAMP}" |
| 28 | +LOCK_FILE="${PHOENIX_APP_DIR}/shared/.deploy.lock" |
28 | 29 |
|
29 | 30 | log() { echo "[$(date '+%H:%M:%S')] $*"; } |
30 | 31 |
|
31 | | -mkdir -p "${SHARED_DIR}" |
| 32 | +mkdir -p "${PHOENIX_APP_DIR}/shared" |
32 | 33 | exec 9>"${LOCK_FILE}" |
33 | 34 | flock -n 9 || { log "ERROR: another deploy is running"; exit 1; } |
34 | 35 |
|
35 | 36 | REPO_DIR="$(pwd)" |
36 | 37 | log "Repo: ${REPO_DIR}" |
37 | 38 |
|
| 39 | +publish_release() { |
| 40 | + local app_dir="$1" |
| 41 | + local release_dir="$2" |
| 42 | + local source_dir="$3" |
| 43 | + |
| 44 | + log "Publishing ${release_dir}..." |
| 45 | + mkdir -p "${release_dir}" |
| 46 | + cp -a "${source_dir}/." "${release_dir}/" |
| 47 | + chmod -R a+rX "${release_dir}" |
| 48 | + |
| 49 | + local current_link="${app_dir}/current" |
| 50 | + log "Atomic swap ${current_link} -> ${release_dir}" |
| 51 | + ln -sfn "${release_dir}" "${current_link}.new" |
| 52 | + mv -fT "${current_link}.new" "${current_link}" |
| 53 | + |
| 54 | + log "Pruning ${app_dir}/releases (keeping last ${KEEP_RELEASES})..." |
| 55 | + mapfile -t _old < <( |
| 56 | + find "${app_dir}/releases" -mindepth 1 -maxdepth 1 -type d -printf '%f\n' \ |
| 57 | + | sort | head -n "-${KEEP_RELEASES}" |
| 58 | + ) |
| 59 | + for r in "${_old[@]}"; do |
| 60 | + [ -n "${r}" ] && rm -rf "${app_dir}/releases/${r:?}" |
| 61 | + done |
| 62 | +} |
| 63 | + |
38 | 64 | log "Fetching latest nav + footer partials from wincon..." |
39 | 65 | ./scripts/fetch-partials.sh |
40 | 66 |
|
| 67 | +log "Syncing Elixir chapter into elixir-book/modules/ROOT/pages/..." |
| 68 | +python3 elixir-book/scripts/sync.py |
| 69 | + |
41 | 70 | log "Installing Antora..." |
42 | 71 | ( cd "${REPO_DIR}" && npm ci --no-audit --no-fund ) |
43 | 72 |
|
44 | | -log "Rendering site..." |
45 | | -# --fetch refreshes both the content source (this repo) and the UI |
46 | | -# bundle (wincon-antora-ui/releases/latest/ui-bundle.zip). The UI |
47 | | -# bundle's snapshot:true in the playbook tells Antora not to cache |
48 | | -# across runs, so a fresh bundle is always pulled. |
| 73 | +log "Rendering Phoenix book..." |
| 74 | +# --fetch refreshes the content source and the shared UI bundle |
| 75 | +# (wincon-antora-ui/releases/latest/ui-bundle.zip). snapshot:true |
| 76 | +# in the playbook tells Antora not to cache across runs. |
49 | 77 | ( cd "${REPO_DIR}" && npx antora --fetch antora-playbook.yml ) |
50 | 78 |
|
51 | 79 | if [ ! -d "${REPO_DIR}/build/site/book" ]; then |
52 | 80 | log "ERROR: expected build/site/book/ not found" |
53 | 81 | exit 1 |
54 | 82 | fi |
55 | 83 |
|
56 | | -log "Publishing release ${TIMESTAMP}..." |
57 | | -mkdir -p "${RELEASE_DIR}" |
58 | | -cp -a "${REPO_DIR}/build/site/." "${RELEASE_DIR}/" |
59 | | -chmod -R a+rX "${RELEASE_DIR}" |
| 84 | +log "Rendering Elixir mini-book..." |
| 85 | +( cd "${REPO_DIR}" && npx antora antora-elixir-playbook.yml ) |
60 | 86 |
|
61 | | -log "Atomic swap..." |
62 | | -ln -sfn "${RELEASE_DIR}" "${CURRENT_LINK}.new" |
63 | | -mv -fT "${CURRENT_LINK}.new" "${CURRENT_LINK}" |
| 87 | +if [ ! -d "${REPO_DIR}/build/elixir-site/book" ]; then |
| 88 | + log "ERROR: expected build/elixir-site/book/ not found" |
| 89 | + exit 1 |
| 90 | +fi |
64 | 91 |
|
65 | | -log "Pruning old releases (keeping last ${KEEP_RELEASES})..." |
66 | | -mapfile -t _old < <( |
67 | | - find "${RELEASES_DIR}" -mindepth 1 -maxdepth 1 -type d -printf '%f\n' \ |
68 | | - | sort | head -n "-${KEEP_RELEASES}" |
69 | | -) |
70 | | -for r in "${_old[@]}"; do |
71 | | - [ -n "${r}" ] && rm -rf "${RELEASES_DIR:?}/${r}" |
72 | | -done |
| 92 | +publish_release "${PHOENIX_APP_DIR}" "${PHOENIX_RELEASE_DIR}" "${REPO_DIR}/build/site" |
| 93 | +mkdir -p "${ELIXIR_APP_DIR}/shared" |
| 94 | +publish_release "${ELIXIR_APP_DIR}" "${ELIXIR_RELEASE_DIR}" "${REPO_DIR}/build/elixir-site" |
73 | 95 |
|
74 | 96 | log "Deploy complete: ${TIMESTAMP}" |
75 | | -log " Active: ${CURRENT_LINK} -> $(readlink -f "${CURRENT_LINK}")" |
| 97 | +log " Phoenix: ${PHOENIX_APP_DIR}/current -> $(readlink -f "${PHOENIX_APP_DIR}/current")" |
| 98 | +log " Elixir: ${ELIXIR_APP_DIR}/current -> $(readlink -f "${ELIXIR_APP_DIR}/current")" |
0 commit comments