From 5dfcbeaa236cb4f4484a1ef05c401e70448db134 Mon Sep 17 00:00:00 2001 From: Kirill Date: Thu, 14 May 2026 20:32:51 +0500 Subject: [PATCH] feat: add deploy-auto.sh with git diff detection; update gitignore --- .gitignore | 2 +- scripts/deploy-auto.sh | 215 +++++++++++++++++++++++++++++++++++++++++ 2 files changed, 216 insertions(+), 1 deletion(-) create mode 100644 scripts/deploy-auto.sh diff --git a/.gitignore b/.gitignore index 237e884..40574e9 100644 --- a/.gitignore +++ b/.gitignore @@ -3,6 +3,6 @@ dist *.log .env scripts/deploy.env -scripts/craftshop-remote-lan.env server/prisma/dev.db server/prisma/dev.db-journal +.deployed-commit diff --git a/scripts/deploy-auto.sh b/scripts/deploy-auto.sh new file mode 100644 index 0000000..1ad4e4d --- /dev/null +++ b/scripts/deploy-auto.sh @@ -0,0 +1,215 @@ +#!/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 <&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 ">>> Готово."