230 lines
8.3 KiB
Bash
Executable File
230 lines
8.3 KiB
Bash
Executable File
#!/usr/bin/env bash
|
|
|
|
set -Eeuo pipefail
|
|
|
|
WM_DEPLOY_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
|
|
WM_ROOT_DIR="$(cd "$WM_DEPLOY_DIR/.." && pwd)"
|
|
# shellcheck source=remote-common.sh
|
|
source "$WM_DEPLOY_DIR/scripts/remote-common.sh"
|
|
|
|
server_ipv4=""
|
|
identity_file=""
|
|
run_go_live=false
|
|
|
|
usage() {
|
|
cat <<'USAGE'
|
|
Verwendung:
|
|
./deploy/scripts/upload-and-deploy.sh [Optionen]
|
|
|
|
Optionen:
|
|
--host IPV4 Server-IP statt `tofu output -raw server_ipv4`
|
|
--identity DATEI Privater SSH-Key (alternativ WATERMAPS_SSH_KEY)
|
|
--user BENUTZER SSH-Benutzer; Standard: deploy
|
|
--go-live Nach dem Deployment sofort den SSL-Livegang versuchen
|
|
-h, --help Hilfe anzeigen
|
|
|
|
Die Datei deploy/.env.production muss lokal vorhanden sein. Sie wird separat
|
|
mit Dateimodus 0600 übertragen. Es wird ausschließlich der Deployment-Ordner
|
|
übertragen; App und Routingdaten-Builder werden als commitgenaue Images aus der
|
|
in .env.production konfigurierten Gitea Registry geladen.
|
|
|
|
Für eine private Registry können WATERMAPS_REGISTRY_USERNAME und
|
|
WATERMAPS_REGISTRY_TOKEN lokal gesetzt werden. Der Token wird nur über SSH an
|
|
`docker login --password-stdin` übergeben und nicht auf den Server hochgeladen.
|
|
USAGE
|
|
}
|
|
|
|
while [[ "$#" -gt 0 ]]; do
|
|
case "$1" in
|
|
--host)
|
|
[[ "$#" -ge 2 ]] || wm_local_die "Wert für --host fehlt."
|
|
server_ipv4="$2"
|
|
shift 2
|
|
;;
|
|
--identity)
|
|
[[ "$#" -ge 2 ]] || wm_local_die "Wert für --identity fehlt."
|
|
identity_file="$2"
|
|
shift 2
|
|
;;
|
|
--user)
|
|
[[ "$#" -ge 2 ]] || wm_local_die "Wert für --user fehlt."
|
|
WATERMAPS_SSH_USER="$2"
|
|
export WATERMAPS_SSH_USER
|
|
shift 2
|
|
;;
|
|
--go-live)
|
|
run_go_live=true
|
|
shift
|
|
;;
|
|
-h|--help)
|
|
usage
|
|
exit 0
|
|
;;
|
|
*)
|
|
wm_local_die "Unbekannte Option: $1"
|
|
;;
|
|
esac
|
|
done
|
|
|
|
command -v rsync >/dev/null 2>&1 || wm_local_die "rsync ist lokal nicht installiert."
|
|
command -v git >/dev/null 2>&1 || wm_local_die "git ist lokal nicht installiert."
|
|
local_env="$WM_DEPLOY_DIR/.env.production"
|
|
[[ -f "$local_env" ]] ||
|
|
wm_local_die "Bitte zuerst deploy/.env.production aus der Vorlage erstellen und konfigurieren."
|
|
|
|
if [[ -n "$(git -C "$WM_ROOT_DIR" status --porcelain --untracked-files=normal)" ]]; then
|
|
wm_local_die "Deployment nur aus einem vollständig committeten, sauberen Git-Stand erlaubt."
|
|
fi
|
|
revision="$(git -C "$WM_ROOT_DIR" rev-parse HEAD)"
|
|
[[ "$revision" =~ ^[0-9a-f]{40}$ ]] ||
|
|
wm_local_die "Aktueller Git-Commit konnte nicht als vollständige SHA ermittelt werden."
|
|
|
|
origin_main_revision() {
|
|
local main_revision
|
|
|
|
main_revision="$(
|
|
git -C "$WM_ROOT_DIR" \
|
|
ls-remote --exit-code origin refs/heads/main |
|
|
awk 'NR == 1 { print $1 }'
|
|
)"
|
|
[[ "$main_revision" =~ ^[0-9a-f]{40}$ ]] ||
|
|
return 1
|
|
printf '%s\n' "$main_revision"
|
|
}
|
|
|
|
published_revision="$(origin_main_revision)" ||
|
|
wm_local_die "origin/main konnte nicht revisionsgenau gelesen werden."
|
|
[[ "$published_revision" == "$revision" ]] ||
|
|
wm_local_die "Lokaler HEAD $revision ist nicht der aktuelle origin/main-Commit $published_revision."
|
|
|
|
set -a
|
|
# shellcheck disable=SC1090
|
|
source "$local_env"
|
|
set +a
|
|
registry="${WATERMAPS_REGISTRY:-}"
|
|
registry_owner="${WATERMAPS_REGISTRY_OWNER:-}"
|
|
[[ "$registry" =~ ^[a-z0-9][a-z0-9.-]*(:[0-9]{1,5})?$ ]] ||
|
|
wm_local_die "WATERMAPS_REGISTRY muss ein Registry-Hostname ohne URL-Schema sein."
|
|
[[ "$registry_owner" =~ ^[A-Za-z0-9_.-]+$ ]] ||
|
|
wm_local_die "WATERMAPS_REGISTRY_OWNER enthält keinen gültigen Gitea-Besitzer."
|
|
|
|
registry_username="${WATERMAPS_REGISTRY_USERNAME:-}"
|
|
registry_token="${WATERMAPS_REGISTRY_TOKEN:-}"
|
|
if [[ -n "$registry_username" || -n "$registry_token" ]]; then
|
|
[[ -n "$registry_username" && -n "$registry_token" ]] ||
|
|
wm_local_die "WATERMAPS_REGISTRY_USERNAME und WATERMAPS_REGISTRY_TOKEN müssen gemeinsam gesetzt werden."
|
|
[[ "$registry_username" =~ ^[A-Za-z0-9_.-]+$ ]] ||
|
|
wm_local_die "WATERMAPS_REGISTRY_USERNAME enthält ungültige Zeichen."
|
|
fi
|
|
|
|
wm_local_resolve_ssh "$server_ipv4" "$identity_file"
|
|
wm_local_wait_for_ssh
|
|
|
|
remote_prefix=""
|
|
rsync_path="rsync"
|
|
if [[ -n "$WM_REMOTE_SUDO" ]]; then
|
|
remote_prefix="$WM_REMOTE_SUDO "
|
|
rsync_path="$WM_REMOTE_SUDO rsync"
|
|
fi
|
|
|
|
wait_for_remote_release() {
|
|
local expected_revision="$1"
|
|
local timeout_seconds="${WATERMAPS_RELEASE_WAIT_SECONDS:-43200}"
|
|
local poll_seconds="${WATERMAPS_RELEASE_POLL_SECONDS:-15}"
|
|
local deadline
|
|
local attempts=0
|
|
local current_main_revision
|
|
local remote_check
|
|
|
|
[[ "$timeout_seconds" =~ ^[1-9][0-9]*$ ]] &&
|
|
((timeout_seconds >= 60 && timeout_seconds <= 46800)) ||
|
|
wm_local_die "WATERMAPS_RELEASE_WAIT_SECONDS muss zwischen 60 und 46800 liegen."
|
|
[[ "$poll_seconds" =~ ^[1-9][0-9]*$ ]] &&
|
|
((poll_seconds >= 5 && poll_seconds <= 60)) ||
|
|
wm_local_die "WATERMAPS_RELEASE_POLL_SECONDS muss zwischen 5 und 60 liegen."
|
|
deadline=$((SECONDS + timeout_seconds))
|
|
|
|
remote_check="${remote_prefix}test -L /opt/watermaps/current"
|
|
remote_check+=" && ${remote_prefix}grep -Fxq"
|
|
remote_check+=" 'WATERMAPS_DEPLOY_REVISION=$expected_revision'"
|
|
remote_check+=" /opt/watermaps/deploy/.env.images"
|
|
remote_check+=" && ${remote_prefix}readlink --canonicalize /opt/watermaps/current"
|
|
remote_check+=" | grep -Eq '/$expected_revision$'"
|
|
|
|
while ((SECONDS < deadline)); do
|
|
if ssh "${WM_SSH_OPTIONS[@]}" "$WM_SSH_TARGET" "$remote_check" \
|
|
>/dev/null 2>&1; then
|
|
printf '[watermaps] Commit %s ist auf dem Server aktiv.\n' \
|
|
"$expected_revision"
|
|
return 0
|
|
fi
|
|
|
|
attempts=$((attempts + 1))
|
|
if ((attempts == 1 || attempts % 4 == 0)); then
|
|
if current_main_revision="$(origin_main_revision 2>/dev/null)" &&
|
|
[[ "$current_main_revision" != "$expected_revision" ]]; then
|
|
wm_local_die "origin/main wurde während des Erstdeployments von $expected_revision auf $current_main_revision verschoben; starte den Livegang aus dem aktuellen main-Stand erneut."
|
|
fi
|
|
printf '[watermaps] Das revisionsgenaue Erstdeployment läuft unter systemd; warte auf Commit %s …\n' \
|
|
"$expected_revision"
|
|
fi
|
|
sleep "$poll_seconds"
|
|
done
|
|
|
|
ssh "${WM_SSH_OPTIONS[@]}" "$WM_SSH_TARGET" \
|
|
"${remote_prefix}systemctl --no-pager --full status watermaps-auto-deploy.service" \
|
|
|| true
|
|
wm_local_die "Commit $expected_revision wurde nicht innerhalb von ${timeout_seconds}s aktiviert."
|
|
}
|
|
|
|
printf '[watermaps] Warte auf Cloud-init und das persistente Hetzner-Volume.\n'
|
|
ssh "${WM_SSH_OPTIONS[@]}" "$WM_SSH_TARGET" \
|
|
"cloud-init status --wait && ${remote_prefix}systemctl start watermaps-volume-setup.service && mountpoint --quiet /srv/watermaps-data"
|
|
|
|
printf '[watermaps] Übertrage Bootstrap-Dateien nach %s:/opt/watermaps\n' "$WM_SSH_TARGET"
|
|
ssh "${WM_SSH_OPTIONS[@]}" "$WM_SSH_TARGET" \
|
|
"${remote_prefix}install -d -m 0755 /opt/watermaps /opt/watermaps/deploy"
|
|
|
|
rsync \
|
|
--archive \
|
|
--compress \
|
|
--delete-delay \
|
|
--human-readable \
|
|
--rsync-path="$rsync_path" \
|
|
--exclude='.env.production' \
|
|
--exclude='.env.images*' \
|
|
--exclude='.gitea-token' \
|
|
--exclude='.gitea-*-token' \
|
|
-e "ssh ${WM_SSH_OPTIONS[*]@Q}" \
|
|
"$WM_DEPLOY_DIR/" \
|
|
"$WM_SSH_TARGET:/opt/watermaps/deploy/"
|
|
|
|
rsync \
|
|
--archive \
|
|
--chmod=F600 \
|
|
--rsync-path="$rsync_path" \
|
|
-e "ssh ${WM_SSH_OPTIONS[*]@Q}" \
|
|
"$local_env" \
|
|
"$WM_SSH_TARGET:/opt/watermaps/deploy/.env.production"
|
|
|
|
if [[ -n "$registry_token" ]]; then
|
|
printf '[watermaps] Aktualisiere den privaten Registry-Login auf dem Server.\n'
|
|
printf '%s\n' "$registry_token" |
|
|
ssh "${WM_SSH_OPTIONS[@]}" "$WM_SSH_TARGET" \
|
|
"${remote_prefix}docker login '$registry' --username '$registry_username' --password-stdin"
|
|
fi
|
|
|
|
remote_command="${remote_prefix}chmod +x /opt/watermaps/deploy/scripts/*.sh"
|
|
remote_command+=" && ${remote_prefix}/opt/watermaps/deploy/scripts/bootstrap-server.sh"
|
|
remote_command+=" && ${remote_prefix}systemctl start --no-block watermaps-auto-deploy.service"
|
|
ssh "${WM_SSH_OPTIONS[@]}" "$WM_SSH_TARGET" "$remote_command"
|
|
wait_for_remote_release "$revision"
|
|
|
|
if [[ "$run_go_live" == "true" ]]; then
|
|
ssh "${WM_SSH_OPTIONS[@]}" "$WM_SSH_TARGET" \
|
|
"${remote_prefix}WATERMAPS_ENV_FILE=/opt/watermaps/deploy/.env.production /opt/watermaps/current/deploy/scripts/go-live.sh '$WM_SERVER_IPV4'"
|
|
else
|
|
printf '[watermaps] Commit %s wurde aus der Registry deployt. Nach dem DNS-Eintrag:\n' "$revision"
|
|
printf ' ./deploy/scripts/remote-go-live.sh --host %s\n' "$WM_SERVER_IPV4"
|
|
fi
|