216 lines
6.7 KiB
Bash
216 lines
6.7 KiB
Bash
#!/usr/bin/env bash
|
|
# Auto-deploy: детект изменений через git diff, сборка и деплой только изменённых компонентов.
|
|
#
|
|
# Зависимости: bash, git, ssh; rsync (Linux/macOS) или tar (Git Bash).
|
|
# Конфиг: scripts/deploy.env.
|
|
#
|
|
# Примеры:
|
|
# ./scripts/deploy-auto.sh # деплой изменений
|
|
# ./scripts/deploy-auto.sh --force # полный деплой всех компонентов
|
|
# ./scripts/deploy-auto.sh -f # только фронт
|
|
# ./scripts/deploy-auto.sh -b # только бэкенд
|
|
|
|
set -euo pipefail
|
|
|
|
case "$(uname -s 2>/dev/null)" in
|
|
MINGW* | MSYS*) export MSYS2_ARG_CONV_EXCL="*" ;;
|
|
esac
|
|
|
|
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
ROOT="$(cd "$SCRIPT_DIR/.." && pwd)"
|
|
|
|
# --- Config ---
|
|
if [[ -f "$SCRIPT_DIR/deploy.env" ]]; then
|
|
set -a
|
|
source "$SCRIPT_DIR/deploy.env"
|
|
set +a
|
|
fi
|
|
|
|
DEPLOY_HOST="${DEPLOY_HOST:-}"
|
|
DEPLOY_USER="${DEPLOY_USER:-root}"
|
|
DEPLOY_PATH="${DEPLOY_PATH:-/opt/craftshop}"
|
|
DEPLOY_FRONTEND_DIST="${DEPLOY_FRONTEND_DIST:-$DEPLOY_PATH/www}"
|
|
DEPLOY_SSH_IDENTITY="${DEPLOY_SSH_IDENTITY:-}"
|
|
DEPLOY_RESTART_CMD="${DEPLOY_RESTART_CMD:-systemctl restart craftshop-api}"
|
|
DEPLOY_SERVER_OWNER="${DEPLOY_SERVER_OWNER:-deploy}"
|
|
DEPLOY_SKIP_CHOWN="${DEPLOY_SKIP_CHOWN:-0}"
|
|
|
|
RSYNC_OPTS=(-az --delete --human-readable --progress)
|
|
SSH_OPTS=()
|
|
if [[ -n "${DEPLOY_SSH_IDENTITY}" ]]; then
|
|
SSH_OPTS+=(-i "$DEPLOY_SSH_IDENTITY")
|
|
fi
|
|
|
|
REMOTE="${DEPLOY_USER}@${DEPLOY_HOST}"
|
|
SSH_BASE=(ssh "${SSH_OPTS[@]}" -o StrictHostKeyChecking=accept-new "$REMOTE")
|
|
|
|
# --- Flags ---
|
|
FORCE=false
|
|
TARGET="auto"
|
|
|
|
usage() {
|
|
cat <<USAGE
|
|
Использование: $(basename "$0") [опции]
|
|
|
|
--force Деплой всех компонентов (игнорировать diff)
|
|
--frontend-only | -f Только клиент
|
|
--backend-only | -b Только сервер
|
|
--help | -h Помощь
|
|
|
|
Окружение (или scripts/deploy.env):
|
|
DEPLOY_HOST хост SSH (обязательно)
|
|
DEPLOY_PATH корень приложения на сервере (по умолчанию: /opt/craftshop)
|
|
DEPLOY_SSH_IDENTITY путь к приватному ключу (-i ssh)
|
|
DEPLOY_RESTART_CMD команда рестарта API после деплоя бэкенда
|
|
USAGE
|
|
}
|
|
|
|
while [[ $# -gt 0 ]]; do
|
|
case "$1" in
|
|
--force) FORCE=true ;;
|
|
--frontend-only | -f) TARGET="frontend" ;;
|
|
--backend-only | -b) TARGET="backend" ;;
|
|
-h | --help) usage; exit 0 ;;
|
|
*) echo "Неизвестный аргумент: $1" >&2; usage >&2; exit 1 ;;
|
|
esac
|
|
shift
|
|
done
|
|
|
|
if [[ -z "$DEPLOY_HOST" ]]; then
|
|
echo "Укажите DEPLOY_HOST (или добавьте в scripts/deploy.env)" >&2
|
|
exit 1
|
|
fi
|
|
|
|
# --- Helpers ---
|
|
remote_exec() {
|
|
"${SSH_BASE[@]}" "$@"
|
|
}
|
|
|
|
should_use_tar_transport() {
|
|
case "$(uname -s 2>/dev/null)" in
|
|
MINGW*|MSYS*|CYGWIN_NT*) return 0 ;;
|
|
*) return 1 ;;
|
|
esac
|
|
}
|
|
|
|
build_rsync_rsh() {
|
|
printf '%q ' ssh "${SSH_OPTS[@]}" -o StrictHostKeyChecking=accept-new
|
|
}
|
|
|
|
# --- Diff detection ---
|
|
changed_client=false
|
|
changed_server=false
|
|
|
|
if [[ "$TARGET" == "frontend" ]]; then
|
|
changed_client=true
|
|
elif [[ "$TARGET" == "backend" ]]; then
|
|
changed_server=true
|
|
elif [[ "$FORCE" == true ]]; then
|
|
changed_client=true
|
|
changed_server=true
|
|
else
|
|
DEPLOYED_FILE="$ROOT/.deployed-commit"
|
|
if [[ -f "$DEPLOYED_FILE" ]]; then
|
|
LAST_DEPLOYED=$(cat "$DEPLOYED_FILE")
|
|
else
|
|
LAST_DEPLOYED="HEAD~1"
|
|
fi
|
|
|
|
echo ">>> Diff: $LAST_DEPLOYED..HEAD"
|
|
CHANGED_FILES=$(git -C "$ROOT" diff --name-only "$LAST_DEPLOYED" HEAD 2>/dev/null || true)
|
|
|
|
if echo "$CHANGED_FILES" | grep -q "^client/"; then
|
|
changed_client=true
|
|
fi
|
|
if echo "$CHANGED_FILES" | grep -q "^server/"; then
|
|
changed_server=true
|
|
fi
|
|
if echo "$CHANGED_FILES" | grep -q "^shared/"; then
|
|
changed_client=true
|
|
changed_server=true
|
|
fi
|
|
|
|
if [[ "$changed_client" == false && "$changed_server" == false ]]; then
|
|
echo ">>> Ничего не изменилось с последнего деплоя."
|
|
exit 0
|
|
fi
|
|
fi
|
|
|
|
echo ">>> Клиент: $changed_client, Сервер: $changed_server"
|
|
|
|
# --- Deploy: Client ---
|
|
if [[ "$changed_client" == true ]]; then
|
|
echo ">>> Сборка клиента..."
|
|
(cd "$ROOT/client" && npm ci && npm run build)
|
|
|
|
remote_exec mkdir -p "$DEPLOY_FRONTEND_DIST"
|
|
|
|
if should_use_tar_transport; then
|
|
remote_exec "find ${DEPLOY_FRONTEND_DIST} -mindepth 1 -delete 2>/dev/null || true"
|
|
(cd "$ROOT/client/dist" && tar -czf - .) | \
|
|
"${SSH_BASE[@]}" "mkdir -p ${DEPLOY_FRONTEND_DIST} && tar xzf - -C ${DEPLOY_FRONTEND_DIST}"
|
|
else
|
|
rsh="$(build_rsync_rsh)"
|
|
rsync "${RSYNC_OPTS[@]}" -e "$rsh" \
|
|
"$ROOT/client/dist/" "${REMOTE}:${DEPLOY_FRONTEND_DIST}/"
|
|
fi
|
|
|
|
echo ">>> Клиент задеплоен"
|
|
fi
|
|
|
|
# --- Deploy: Server ---
|
|
if [[ "$changed_server" == true ]]; then
|
|
remote_exec mkdir -p "$DEPLOY_PATH/server" "$DEPLOY_PATH/shared"
|
|
|
|
if should_use_tar_transport; then
|
|
(cd "$ROOT/server" && tar -czf - \
|
|
--exclude=node_modules --exclude=uploads --exclude=.git \
|
|
--exclude='*.db' --exclude=.env --exclude=.dev_env \
|
|
.) | "${SSH_BASE[@]}" "mkdir -p ${DEPLOY_PATH}/server && tar xzf - -C ${DEPLOY_PATH}/server"
|
|
|
|
(cd "$ROOT/shared" && tar -czf - \
|
|
--exclude=.git \
|
|
.) | "${SSH_BASE[@]}" "mkdir -p ${DEPLOY_PATH}/shared && tar xzf - -C ${DEPLOY_PATH}/shared"
|
|
else
|
|
rsh="$(build_rsync_rsh)"
|
|
rsync "${RSYNC_OPTS[@]}" -e "$rsh" \
|
|
--exclude node_modules --exclude uploads --exclude .git \
|
|
--exclude '*.db' --exclude .env --exclude .dev_env \
|
|
"$ROOT/server/" "${REMOTE}:${DEPLOY_PATH}/server/"
|
|
|
|
rsync "${RSYNC_OPTS[@]}" -e "$rsh" \
|
|
--exclude .git \
|
|
"$ROOT/shared/" "${REMOTE}:${DEPLOY_PATH}/shared/"
|
|
fi
|
|
|
|
echo ">>> Сервер: npm ci, prisma generate, migrate deploy"
|
|
remote_exec bash -lc "set -e
|
|
cd \"$DEPLOY_PATH/server\"
|
|
npm ci
|
|
npx prisma generate
|
|
npx prisma migrate deploy
|
|
"
|
|
|
|
if [[ "${DEPLOY_USER}" == "root" && "${DEPLOY_SKIP_CHOWN}" != "1" ]]; then
|
|
echo ">>> chown ${DEPLOY_SERVER_OWNER}"
|
|
remote_exec chown -R "${DEPLOY_SERVER_OWNER}:${DEPLOY_SERVER_OWNER}" "$DEPLOY_PATH/server"
|
|
remote_exec chown -R "${DEPLOY_SERVER_OWNER}:${DEPLOY_SERVER_OWNER}" "$DEPLOY_PATH/shared"
|
|
fi
|
|
|
|
if [[ -n "${DEPLOY_RESTART_CMD}" ]]; then
|
|
echo ">>> Рестарт: $DEPLOY_RESTART_CMD"
|
|
remote_exec bash -lc "$DEPLOY_RESTART_CMD"
|
|
fi
|
|
|
|
echo ">>> Сервер задеплоен"
|
|
fi
|
|
|
|
# --- Save deployed commit ---
|
|
if [[ "$TARGET" == "auto" && "$FORCE" != true ]]; then
|
|
CURRENT_HEAD=$(git -C "$ROOT" rev-parse HEAD)
|
|
echo "$CURRENT_HEAD" > "$ROOT/.deployed-commit"
|
|
echo ">>> Сохранён коммит $CURRENT_HEAD"
|
|
fi
|
|
|
|
echo ">>> Готово."
|