Monorepo for Aesthetic.Computer
aesthetic.computer
1#!/usr/bin/env bash
2set -euo pipefail
3
4# Deploy AT frontend pages to PDS caddy container.
5# Intended for both local use and CI (GitHub Actions).
6#
7# Required env (or defaults):
8# AT_PDS_HOST (default: 165.227.120.137)
9# AT_PDS_USER (default: root)
10# AT_PDS_SSH_KEY_PATH (default: ~/.ssh/aesthetic_pds)
11# AT_PDS_CONTAINER (default: caddy)
12# AT_PDS_CONTAINER_WEBROOT (default: /data/www)
13# AT_FRONTEND_FILE_MAP (default: at/index.html:index.html;at/user-page.html:user.html;at/media-modal.js:media-modal.js;at/media-records.js:media-records.js)
14#
15# AT_FRONTEND_FILE_MAP format:
16# "repo/source/path:container/target/path;repo/source2:path2"
17
18AT_PDS_HOST="${AT_PDS_HOST:-165.227.120.137}"
19AT_PDS_USER="${AT_PDS_USER:-root}"
20AT_PDS_SSH_KEY_PATH="${AT_PDS_SSH_KEY_PATH:-$HOME/.ssh/aesthetic_pds}"
21AT_PDS_CONTAINER="${AT_PDS_CONTAINER:-caddy}"
22AT_PDS_CONTAINER_WEBROOT="${AT_PDS_CONTAINER_WEBROOT:-/data/www}"
23AT_FRONTEND_FILE_MAP="${AT_FRONTEND_FILE_MAP:-at/index.html:index.html;at/user-page.html:user.html;at/media-modal.js:media-modal.js;at/media-records.js:media-records.js}"
24
25SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
26REPO_ROOT="$(cd "$SCRIPT_DIR/../.." && pwd)"
27
28trim() {
29 local value="$1"
30 value="${value#"${value%%[![:space:]]*}"}"
31 value="${value%"${value##*[![:space:]]}"}"
32 printf "%s" "${value}"
33}
34
35declare -a SOURCE_FILES=()
36declare -a TARGET_FILES=()
37IFS=';' read -r -a FILE_MAP_ENTRIES <<< "$AT_FRONTEND_FILE_MAP"
38for raw_entry in "${FILE_MAP_ENTRIES[@]}"; do
39 entry="$(trim "$raw_entry")"
40 [[ -z "$entry" ]] && continue
41
42 if [[ "$entry" != *:* ]]; then
43 echo "Invalid AT_FRONTEND_FILE_MAP entry: '$entry' (expected source:target)" >&2
44 exit 1
45 fi
46
47 source_path="$(trim "${entry%%:*}")"
48 target_path="$(trim "${entry#*:}")"
49 local_source="$REPO_ROOT/$source_path"
50
51 if [[ -z "$source_path" || -z "$target_path" ]]; then
52 echo "Invalid AT_FRONTEND_FILE_MAP entry: '$entry' (empty source or target)" >&2
53 exit 1
54 fi
55
56 if [[ ! -f "$local_source" ]]; then
57 echo "Missing source file: $local_source" >&2
58 exit 1
59 fi
60
61 SOURCE_FILES+=("$source_path")
62 TARGET_FILES+=("$target_path")
63done
64
65if [[ "${#SOURCE_FILES[@]}" -eq 0 ]]; then
66 echo "No frontend files configured. Set AT_FRONTEND_FILE_MAP." >&2
67 exit 1
68fi
69
70if [[ ! -f "$AT_PDS_SSH_KEY_PATH" ]]; then
71 echo "Missing SSH key: $AT_PDS_SSH_KEY_PATH" >&2
72 exit 1
73fi
74
75SSH_TARGET="$AT_PDS_USER@$AT_PDS_HOST"
76SSH_OPTS=(
77 -i "$AT_PDS_SSH_KEY_PATH"
78 -o StrictHostKeyChecking=no
79 -o UserKnownHostsFile=/dev/null
80)
81
82STAMP="${GITHUB_SHA:-$(date +%Y%m%d%H%M%S)}"
83REMOTE_DIR="/tmp/at-frontend-${STAMP}"
84
85echo "Deploying AT frontend to ${SSH_TARGET}"
86echo "Uploading staging files..."
87ssh "${SSH_OPTS[@]}" "$SSH_TARGET" "mkdir -p '$REMOTE_DIR'"
88
89for i in "${!SOURCE_FILES[@]}"; do
90 source_path="${SOURCE_FILES[$i]}"
91 target_path="${TARGET_FILES[$i]}"
92 local_source="$REPO_ROOT/$source_path"
93 remote_stage="$REMOTE_DIR/${i}-$(basename "$target_path")"
94 target_dir="$(dirname "$target_path")"
95
96 scp "${SSH_OPTS[@]}" "$local_source" "${SSH_TARGET}:${remote_stage}"
97 if [[ "$target_dir" != "." ]]; then
98 ssh "${SSH_OPTS[@]}" "$SSH_TARGET" "docker exec '${AT_PDS_CONTAINER}' mkdir -p '${AT_PDS_CONTAINER_WEBROOT}/$target_dir'"
99 fi
100 ssh "${SSH_OPTS[@]}" "$SSH_TARGET" "docker cp '$remote_stage' '${AT_PDS_CONTAINER}:${AT_PDS_CONTAINER_WEBROOT}/$target_path'"
101done
102
103ssh "${SSH_OPTS[@]}" "$SSH_TARGET" "rm -rf '$REMOTE_DIR'"
104
105echo "Deployment complete."
106echo "Landing: https://at.aesthetic.computer"
107echo "User page: https://jeffrey.at.aesthetic.computer"