113 lines
3.2 KiB
Bash
113 lines
3.2 KiB
Bash
#!/usr/bin/env bash
|
|
# Sync local Nexus working tree to production host (no Gitea push/pull).
|
|
# Transfer via tar|ssh; remote ownership auto-detected (www:www on BT, else deploy path owner).
|
|
#
|
|
# Usage:
|
|
# bash deploy/rsync-local-to-server.sh
|
|
# NEXUS_DEPLOY_PATH=/opt/nexus bash deploy/rsync-local-to-server.sh
|
|
|
|
set -euo pipefail
|
|
|
|
ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
|
|
cd "${ROOT}"
|
|
|
|
SECRETS="${ROOT}/deploy/nexus-1panel.secrets.sh"
|
|
if [[ -f "$SECRETS" ]]; then
|
|
# shellcheck disable=SC1090
|
|
source "$SECRETS"
|
|
fi
|
|
|
|
NEXUS_SSH_HOST="${NEXUS_SSH:-nexus}"
|
|
SSH_OPTS=(-o BatchMode=yes -o ConnectTimeout=15)
|
|
if [[ -n "${NEXUS_SSH_KEY:-}" ]]; then
|
|
SSH_OPTS+=(-i "${NEXUS_SSH_KEY}")
|
|
fi
|
|
ssh_cmd() { ssh "${SSH_OPTS[@]}" "${NEXUS_SSH_HOST}" "$@"; }
|
|
|
|
if ! ssh_cmd "echo SSH OK" >/dev/null 2>&1; then
|
|
echo "ERROR: Cannot SSH to ${NEXUS_SSH_HOST}" >&2
|
|
exit 1
|
|
fi
|
|
|
|
DEPLOY_PATH="${NEXUS_DEPLOY_PATH:-}"
|
|
if [[ -z "$DEPLOY_PATH" ]]; then
|
|
DEPLOY_PATH="$(ssh_cmd 'for d in /opt/nexus /www/wwwroot/api.synaglobal.vip; do [[ -d "$d/server" ]] && echo "$d" && exit 0; done; echo /opt/nexus')"
|
|
fi
|
|
|
|
resolve_deploy_chown() {
|
|
if [[ -n "${NEXUS_DEPLOY_CHOWN:-}" ]]; then
|
|
echo "${NEXUS_DEPLOY_CHOWN}"
|
|
return
|
|
fi
|
|
if ssh_cmd "getent passwd www >/dev/null 2>&1"; then
|
|
echo "www:www"
|
|
return
|
|
fi
|
|
ssh_cmd "stat -c '%U:%G' '${DEPLOY_PATH}'" 2>/dev/null || echo "azureuser:azureuser"
|
|
}
|
|
DEPLOY_CHOWN="$(resolve_deploy_chown)"
|
|
|
|
echo "═══ Nexus local → server sync ═══"
|
|
echo "SSH target: ${NEXUS_SSH_HOST}"
|
|
echo "Remote path: ${DEPLOY_PATH}"
|
|
echo "Owner: ${DEPLOY_CHOWN}"
|
|
|
|
TAR_EXCLUDES=(
|
|
--exclude='.git'
|
|
--exclude='.env'
|
|
--exclude='.env.*'
|
|
--exclude='SECRETS.md'
|
|
--exclude='deploy/nexus-1panel.secrets.sh'
|
|
--exclude='docker/.env.prod'
|
|
--exclude='web/data'
|
|
--exclude='frontend/node_modules'
|
|
--exclude='frontend-v2/node_modules'
|
|
--exclude='node_modules'
|
|
--exclude='.venv'
|
|
--exclude='.venv-*'
|
|
--exclude='venv'
|
|
--exclude='__pycache__'
|
|
--exclude='.pytest_cache'
|
|
--exclude='.ruff_cache'
|
|
--exclude='.cursor'
|
|
--exclude='.playwright-mcp'
|
|
--exclude='.install_locked'
|
|
--exclude='.install_state.json'
|
|
--exclude='web/app/assets'
|
|
--exclude='tmp'
|
|
--exclude='2025.2'
|
|
--exclude='=2025.2'
|
|
--exclude='*.bak-'
|
|
--exclude='frontend/test-results'
|
|
--exclude='frontend/e2e/.auth'
|
|
)
|
|
|
|
echo "▶ tar stream → ${DEPLOY_PATH} ..."
|
|
tar -C "${ROOT}" -czf - "${TAR_EXCLUDES[@]}" . | \
|
|
ssh "${SSH_OPTS[@]}" "${NEXUS_SSH_HOST}" "sudo tar -xzf - -C '${DEPLOY_PATH}'"
|
|
|
|
echo "▶ chown ${DEPLOY_CHOWN} (skip docker/.env.prod, web/data, .env) ..."
|
|
ssh_cmd "sudo bash -s" <<REMOTE
|
|
set -euo pipefail
|
|
root='${DEPLOY_PATH}'
|
|
own='${DEPLOY_CHOWN}'
|
|
chown_tree() {
|
|
local p="\$1"
|
|
[[ -e "\$p" ]] || return 0
|
|
sudo chown -R "\$own" "\$p"
|
|
}
|
|
for top in server deploy scripts tests standards docs frontend web; do
|
|
chown_tree "\$root/\$top"
|
|
done
|
|
if [[ -d "\$root/docker" ]]; then
|
|
while IFS= read -r -d '' p; do
|
|
sudo chown -R "\$own" "\$p"
|
|
done < <(find "\$root/docker" \( -path "\$root/docker/.env.prod" \) -prune -o -print0)
|
|
fi
|
|
for f in pyproject.toml requirements.txt requirements-dev.txt AGENTS.md CLAUDE.md; do
|
|
[[ -f "\$root/\$f" ]] && sudo chown "\$own" "\$root/\$f"
|
|
done
|
|
REMOTE
|
|
|
|
echo "✓ sync complete (owner ${DEPLOY_CHOWN})"
|