Fix server deploy: rsync submodules, local chatbotApp build, deploy flow.
Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
+16
-5
@@ -13,6 +13,9 @@
|
|||||||
set -euo pipefail
|
set -euo pipefail
|
||||||
|
|
||||||
ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||||
|
# bare repo 배포 시 상위에서 GIT_DIR 이 export 되면 submodule git 이 깨짐
|
||||||
|
MONOREPO_GIT_DIR="${MONOREPO_GIT_DIR:-${GIT_DIR:-}}"
|
||||||
|
unset GIT_DIR GIT_WORK_TREE
|
||||||
INIT=false
|
INIT=false
|
||||||
DRY_RUN=false
|
DRY_RUN=false
|
||||||
|
|
||||||
@@ -56,8 +59,8 @@ if $INIT && ! $DRY_RUN; then
|
|||||||
fi
|
fi
|
||||||
|
|
||||||
resolve_monorepo_git_dir() {
|
resolve_monorepo_git_dir() {
|
||||||
if [[ -n "${GIT_DIR:-}" && -d "${GIT_DIR}" ]]; then
|
if [[ -n "${MONOREPO_GIT_DIR}" && -d "${MONOREPO_GIT_DIR}" ]]; then
|
||||||
echo "${GIT_DIR}"
|
echo "${MONOREPO_GIT_DIR}"
|
||||||
elif git -C "${ROOT_DIR}" rev-parse --git-dir >/dev/null 2>&1; then
|
elif git -C "${ROOT_DIR}" rev-parse --git-dir >/dev/null 2>&1; then
|
||||||
git -C "${ROOT_DIR}" rev-parse --git-dir
|
git -C "${ROOT_DIR}" rev-parse --git-dir
|
||||||
fi
|
fi
|
||||||
@@ -79,7 +82,10 @@ clone_submodule_repo() {
|
|||||||
sha="$(submodule_pinned_sha "${path}" || true)"
|
sha="$(submodule_pinned_sha "${path}" || true)"
|
||||||
log " clone ${url} → ${path} (branch=${branch})"
|
log " clone ${url} → ${path} (branch=${branch})"
|
||||||
rm -rf "${target}"
|
rm -rf "${target}"
|
||||||
git clone --branch "${branch}" --single-branch "${url}" "${target}"
|
if ! git clone --branch "${branch}" --single-branch "${url}" "${target}"; then
|
||||||
|
warn "${path} — clone 실패 (서버 GitHub 미접속 시 sync-submodules-rsync.sh 사용)"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
if [[ -n "${sha}" ]]; then
|
if [[ -n "${sha}" ]]; then
|
||||||
git -C "${target}" checkout "${sha}" 2>/dev/null || true
|
git -C "${target}" checkout "${sha}" 2>/dev/null || true
|
||||||
fi
|
fi
|
||||||
@@ -98,6 +104,11 @@ restore_monorepo_overlays() {
|
|||||||
fi
|
fi
|
||||||
}
|
}
|
||||||
|
|
||||||
|
submodule_is_cloned() {
|
||||||
|
local path="$1"
|
||||||
|
[[ -e "${ROOT_DIR}/${path}/.git" ]]
|
||||||
|
}
|
||||||
|
|
||||||
fetch_one() {
|
fetch_one() {
|
||||||
local path="$1"
|
local path="$1"
|
||||||
local url branch old new dirty
|
local url branch old new dirty
|
||||||
@@ -109,13 +120,13 @@ fetch_one() {
|
|||||||
log ">>> ${path}"
|
log ">>> ${path}"
|
||||||
log " url=${url} branch=${branch}"
|
log " url=${url} branch=${branch}"
|
||||||
|
|
||||||
if ! git -C "${ROOT_DIR}/${path}" rev-parse --git-dir >/dev/null 2>&1; then
|
if ! submodule_is_cloned "${path}"; then
|
||||||
if $DRY_RUN; then
|
if $DRY_RUN; then
|
||||||
log " [dry-run] submodule clone 필요 — ./git_fetch_all.sh --init"
|
log " [dry-run] submodule clone 필요 — ./git_fetch_all.sh --init"
|
||||||
return 0
|
return 0
|
||||||
fi
|
fi
|
||||||
if $INIT; then
|
if $INIT; then
|
||||||
clone_submodule_repo "${path}" "${url}" "${branch}"
|
clone_submodule_repo "${path}" "${url}" "${branch}" || return 1
|
||||||
else
|
else
|
||||||
warn "${path} — submodule 미초기화. --init 옵션 사용"
|
warn "${path} — submodule 미초기화. --init 옵션 사용"
|
||||||
return 1
|
return 1
|
||||||
|
|||||||
@@ -71,6 +71,8 @@ Gitea(`https://github.exdev.co.kr`)에서 **`exAichatbot_agent`** 저장소 생
|
|||||||
|
|
||||||
**main push 시 자동:** `post-receive` → `deploy-exAichatbot_agent.sh` → checkout + `git_fetch_all --init` → `start-all-server.sh --no-pull --no-push`
|
**main push 시 자동:** `post-receive` → `deploy-exAichatbot_agent.sh` → checkout + `git_fetch_all --init` → `start-all-server.sh --no-pull --no-push`
|
||||||
|
|
||||||
|
서버에서 GitHub clone 이 안 되면 **로컬 `./start-all-server.sh`** 가 submodule rsync + chatbotApp 로컬 빌드 후 배포합니다.
|
||||||
|
|
||||||
배포 로그: `/Volumes/ADATA/logs/deploy/exAichatbot_agent.log`
|
배포 로그: `/Volumes/ADATA/logs/deploy/exAichatbot_agent.log`
|
||||||
|
|
||||||
## 로컬 → Gitea push (수동)
|
## 로컬 → Gitea push (수동)
|
||||||
|
|||||||
+47
-30
@@ -12,9 +12,6 @@ BRANCH="${BRANCH:-main}"
|
|||||||
DEPLOY_LOG="${DEPLOY_LOG:-/Volumes/ADATA/logs/deploy/exAichatbot_agent.log}"
|
DEPLOY_LOG="${DEPLOY_LOG:-/Volumes/ADATA/logs/deploy/exAichatbot_agent.log}"
|
||||||
LOG_TAG="[exAichatbot_agent-deploy]"
|
LOG_TAG="[exAichatbot_agent-deploy]"
|
||||||
|
|
||||||
# 레거시 bare repo (Gitea 미사용 시)
|
|
||||||
# GIT_DIR=/Volumes/ADATA/git/exAichatbot_agent.git
|
|
||||||
|
|
||||||
export PATH="/opt/homebrew/bin:/opt/homebrew/sbin:/usr/local/bin:/Applications/Docker.app/Contents/Resources/bin:${PATH:-/usr/bin:/bin}"
|
export PATH="/opt/homebrew/bin:/opt/homebrew/sbin:/usr/local/bin:/Applications/Docker.app/Contents/Resources/bin:${PATH:-/usr/bin:/bin}"
|
||||||
|
|
||||||
mkdir -p "$(dirname "$DEPLOY_LOG")"
|
mkdir -p "$(dirname "$DEPLOY_LOG")"
|
||||||
@@ -34,47 +31,63 @@ fi
|
|||||||
echo $$ > "$LOCK_FILE"
|
echo $$ > "$LOCK_FILE"
|
||||||
trap 'rm -f "$LOCK_FILE"; log "락 해제"' EXIT
|
trap 'rm -f "$LOCK_FILE"; log "락 해제"' EXIT
|
||||||
|
|
||||||
|
for arg in "$@"; do
|
||||||
|
case "$arg" in
|
||||||
|
--start-only) START_ONLY=true ;;
|
||||||
|
esac
|
||||||
|
done
|
||||||
|
START_ONLY="${START_ONLY:-false}"
|
||||||
|
|
||||||
log "======================================================"
|
log "======================================================"
|
||||||
log " exAichatbot Agent 2.0 배포 시작"
|
log " exAichatbot Agent 2.0 배포 시작"
|
||||||
log " GIT_DIR : $GIT_DIR"
|
log " GIT_DIR : $GIT_DIR"
|
||||||
log " WORK_TREE : $WORK_TREE"
|
log " WORK_TREE : $WORK_TREE"
|
||||||
log " BRANCH : $BRANCH"
|
log " BRANCH : $BRANCH"
|
||||||
|
log " START_ONLY: $START_ONLY"
|
||||||
log "======================================================"
|
log "======================================================"
|
||||||
|
|
||||||
[[ -d "$GIT_DIR" ]] || fail "GIT_DIR 없음: $GIT_DIR"
|
[[ -d "$GIT_DIR" ]] || fail "GIT_DIR 없음: $GIT_DIR"
|
||||||
mkdir -p "$WORK_TREE"
|
mkdir -p "$WORK_TREE"
|
||||||
|
|
||||||
chmod +x "$WORK_TREE/server-dev/"*.sh 2>/dev/null || true
|
chmod +x "$WORK_TREE/server-dev/"*.sh 2>/dev/null || true
|
||||||
|
chmod +x "$WORK_TREE/git_fetch_all.sh" 2>/dev/null || true
|
||||||
chmod +x "$WORK_TREE/chatbotApp-chatbot2.0-agent/build.sh" 2>/dev/null || true
|
chmod +x "$WORK_TREE/chatbotApp-chatbot2.0-agent/build.sh" 2>/dev/null || true
|
||||||
|
|
||||||
|
sync_worktree_submodules() {
|
||||||
|
local tree="$1"
|
||||||
|
local path ready=0
|
||||||
|
for path in chatbotApp-chatbot2.0-agent chatbotApi-chatbot2.0-agent \
|
||||||
|
exAiChatBot-chatbot2.0-agent kakaoChatbotSkill-chatbot2.0-agent; do
|
||||||
|
if [[ -f "${tree}/${path}/build.gradle" ]] || [[ -f "${tree}/${path}/build.sh" ]] \
|
||||||
|
|| [[ -f "${tree}/${path}/docker/service-slim.Dockerfile" ]]; then
|
||||||
|
ready=$((ready + 1))
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
if [[ ${ready} -ge 4 ]]; then
|
||||||
|
log "submodule 소스 이미 존재 (rsync 또는 clone 완료) — GitHub fetch 생략"
|
||||||
|
if [[ -f "${tree}/server-dev/docker-compose.rag.server.yml" ]]; then
|
||||||
|
cp "${tree}/server-dev/docker-compose.rag.server.yml" \
|
||||||
|
"${tree}/exAiChatBot-chatbot2.0-agent/docker-compose.server.yml" 2>/dev/null || true
|
||||||
|
fi
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
if [[ -x "${tree}/git_fetch_all.sh" ]]; then
|
||||||
|
log "GitHub submodule 동기화 (git_fetch_all.sh --init)..."
|
||||||
|
MONOREPO_GIT_DIR="${GIT_DIR}" "${tree}/git_fetch_all.sh" --init || log "[WARN] git_fetch_all 실패 — 로컬 rsync 필요"
|
||||||
|
else
|
||||||
|
log "[WARN] git_fetch_all.sh 없음 — 로컬 rsync 필요"
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
log "[Phase 1] stop-all-server.sh"
|
log "[Phase 1] stop-all-server.sh"
|
||||||
"$WORK_TREE/server-dev/stop-all-server.sh" --keep-caddy || true
|
"$WORK_TREE/server-dev/stop-all-server.sh" --keep-caddy || true
|
||||||
|
|
||||||
ensure_worktree_git() {
|
if ! $START_ONLY; then
|
||||||
local tree="$1"
|
log "[Phase 2] checkout + GitHub submodule"
|
||||||
local git_dir="$2"
|
git --work-tree="$WORK_TREE" --git-dir="$GIT_DIR" checkout -f "$BRANCH"
|
||||||
if [[ ! -e "${tree}/.git" ]]; then
|
rm -f "$WORK_TREE/.git"
|
||||||
echo "gitdir: ${git_dir}" > "${tree}/.git"
|
sync_worktree_submodules "$WORK_TREE"
|
||||||
log "work tree .git 연결: ${tree}"
|
fi
|
||||||
fi
|
|
||||||
}
|
|
||||||
|
|
||||||
sync_worktree_submodules() {
|
|
||||||
local tree="$1"
|
|
||||||
ensure_worktree_git "${tree}" "${GIT_DIR}"
|
|
||||||
if [[ -x "${tree}/git_fetch_all.sh" ]]; then
|
|
||||||
log "GitHub submodule 동기화 (git_fetch_all.sh --init)..."
|
|
||||||
"${tree}/git_fetch_all.sh" --init || log "[WARN] git_fetch_all 실패"
|
|
||||||
else
|
|
||||||
log "git submodule update --init --recursive"
|
|
||||||
git -C "${tree}" submodule sync --recursive 2>/dev/null || true
|
|
||||||
git -C "${tree}" submodule update --init --recursive || log "[WARN] submodule update 실패"
|
|
||||||
fi
|
|
||||||
}
|
|
||||||
|
|
||||||
log "[Phase 2] checkout + GitHub submodule"
|
|
||||||
git --work-tree="$WORK_TREE" --git-dir="$GIT_DIR" checkout -f "$BRANCH"
|
|
||||||
sync_worktree_submodules "$WORK_TREE"
|
|
||||||
|
|
||||||
log "[Phase 3] .env 준비"
|
log "[Phase 3] .env 준비"
|
||||||
ENV_FILE="$WORK_TREE/exAiChatBot-chatbot2.0-agent/.env"
|
ENV_FILE="$WORK_TREE/exAiChatBot-chatbot2.0-agent/.env"
|
||||||
@@ -83,8 +96,12 @@ if [[ ! -f "$ENV_FILE" ]]; then
|
|||||||
log ".env 생성 (env.exdev-server.template)"
|
log ".env 생성 (env.exdev-server.template)"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
log "[Phase 4] start-all-server.sh (빌드 + 기동, push 생략)"
|
log "[Phase 4] start-all-server.sh (기동)"
|
||||||
"$WORK_TREE/server-dev/start-all-server.sh" --skip-legacy-stop --no-pull --no-push
|
START_ARGS=(--skip-legacy-stop --no-pull --no-push)
|
||||||
|
if $START_ONLY || [[ -f "$WORK_TREE/chatbotApp-chatbot2.0-agent/public/index.html" ]]; then
|
||||||
|
START_ARGS+=(--no-build)
|
||||||
|
fi
|
||||||
|
"$WORK_TREE/server-dev/start-all-server.sh" "${START_ARGS[@]}"
|
||||||
|
|
||||||
COMMIT="$(git --git-dir="$GIT_DIR" rev-parse --short HEAD 2>/dev/null || echo unknown)"
|
COMMIT="$(git --git-dir="$GIT_DIR" rev-parse --short HEAD 2>/dev/null || echo unknown)"
|
||||||
log "======================================================"
|
log "======================================================"
|
||||||
|
|||||||
@@ -130,12 +130,48 @@ wait_remote_deploy() {
|
|||||||
fi
|
fi
|
||||||
}
|
}
|
||||||
|
|
||||||
|
sync_submodules_to_server() {
|
||||||
|
local rsync_script="${SERVER_DEV_DIR}/sync-submodules-rsync.sh"
|
||||||
|
[[ -x "${rsync_script}" ]] || fail "sync-submodules-rsync.sh 없음"
|
||||||
|
log "submodule 소스 → chatbot.exdev.co.kr rsync..."
|
||||||
|
EXDEV_SSH="${EXDEV_SSH}" "${rsync_script}"
|
||||||
|
}
|
||||||
|
|
||||||
|
trigger_remote_deploy() {
|
||||||
|
log "원격 monorepo checkout..."
|
||||||
|
ssh -o BatchMode=yes "${EXDEV_SSH}" \
|
||||||
|
"GIT_DIR=/Volumes/ADATA/gitea/data/gitea-repositories/aidev/exaichatbot_agent.git; \
|
||||||
|
WT=/Users/aidev/dev/exAichatbot_agent; \
|
||||||
|
git --work-tree=\"\${WT}\" --git-dir=\"\${GIT_DIR}\" checkout -f main; \
|
||||||
|
rm -f \"\${WT}/.git\" /tmp/exAichatbot_agent_deploy.lock"
|
||||||
|
sync_submodules_to_server
|
||||||
|
log "원격 deploy (빌드 + 기동)..."
|
||||||
|
ssh -o BatchMode=yes "${EXDEV_SSH}" \
|
||||||
|
"cp /Users/aidev/dev/exAichatbot_agent/server-dev/deploy.sh /Volumes/ADATA/git/deploy-exAichatbot_agent.sh 2>/dev/null || true; \
|
||||||
|
chmod +x /Volumes/ADATA/git/deploy-exAichatbot_agent.sh; \
|
||||||
|
export PATH=\"/opt/homebrew/bin:/Applications/Docker.app/Contents/Resources/bin:\$PATH\"; \
|
||||||
|
bash /Volumes/ADATA/git/deploy-exAichatbot_agent.sh --start-only" \
|
||||||
|
|| warn "원격 deploy 실패"
|
||||||
|
}
|
||||||
|
|
||||||
|
build_chatbot_app_for_deploy() {
|
||||||
|
log "chatbotApp 로컬 빌드 (서버 Next.js 대신 public/ 생성)..."
|
||||||
|
(
|
||||||
|
cd "${APP_DIR}/web"
|
||||||
|
npm ci --prefer-offline 2>/dev/null || npm install
|
||||||
|
)
|
||||||
|
bash "${APP_DIR}/build.sh"
|
||||||
|
[[ -f "${APP_DIR}/public/index.html" ]] || fail "chatbotApp public/ 빌드 실패"
|
||||||
|
}
|
||||||
|
|
||||||
deploy_from_local_mac() {
|
deploy_from_local_mac() {
|
||||||
log "로컬 Mac → Gitea push → chatbot.exdev.co.kr 배포"
|
log "로컬 Mac → Gitea push → chatbot.exdev.co.kr 배포"
|
||||||
if ! $NO_PULL; then
|
if ! $NO_PULL; then
|
||||||
sync_github_submodules "${ROOT_DIR}" || true
|
sync_github_submodules "${ROOT_DIR}" || true
|
||||||
fi
|
fi
|
||||||
|
build_chatbot_app_for_deploy
|
||||||
push_source_to_gitea
|
push_source_to_gitea
|
||||||
|
trigger_remote_deploy
|
||||||
wait_remote_deploy
|
wait_remote_deploy
|
||||||
log "=========================================="
|
log "=========================================="
|
||||||
log "원격 배포 요청 완료"
|
log "원격 배포 요청 완료"
|
||||||
@@ -161,9 +197,6 @@ sync_source_from_git() {
|
|||||||
fail "브랜치 없음: ${branch} (GIT_DIR=${GIT_DIR})"
|
fail "브랜치 없음: ${branch} (GIT_DIR=${GIT_DIR})"
|
||||||
fi
|
fi
|
||||||
git --work-tree="${tree}" --git-dir="${GIT_DIR}" checkout -f "${branch}"
|
git --work-tree="${tree}" --git-dir="${GIT_DIR}" checkout -f "${branch}"
|
||||||
if [[ ! -e "${tree}/.git" ]]; then
|
|
||||||
echo "gitdir: ${GIT_DIR}" > "${tree}/.git"
|
|
||||||
fi
|
|
||||||
sync_github_submodules "${tree}" || true
|
sync_github_submodules "${tree}" || true
|
||||||
local commit
|
local commit
|
||||||
commit="$(git --git-dir="${GIT_DIR}" rev-parse --short HEAD)"
|
commit="$(git --git-dir="${GIT_DIR}" rev-parse --short HEAD)"
|
||||||
|
|||||||
Executable
+49
@@ -0,0 +1,49 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
# 로컬 submodule 소스 → chatbot.exdev.co.kr work tree rsync
|
||||||
|
# (서버에서 GitHub HTTPS/SSH 미설정 시 deploy 보조)
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
|
||||||
|
EXDEV_SSH="${EXDEV_SSH:-aidev@chatbot.exdev.co.kr}"
|
||||||
|
REMOTE_WT="${REMOTE_WORK_TREE:-/Users/aidev/dev/exAichatbot_agent}"
|
||||||
|
|
||||||
|
log() { echo "[sync-submodules] $*"; }
|
||||||
|
|
||||||
|
SUBMODULES=(
|
||||||
|
chatbotApp-chatbot2.0-agent
|
||||||
|
chatbotApi-chatbot2.0-agent
|
||||||
|
exAiChatBot-chatbot2.0-agent
|
||||||
|
kakaoChatbotSkill-chatbot2.0-agent
|
||||||
|
)
|
||||||
|
|
||||||
|
log "원격 동기화: ${EXDEV_SSH}:${REMOTE_WT}"
|
||||||
|
|
||||||
|
for path in "${SUBMODULES[@]}"; do
|
||||||
|
local_src="${ROOT_DIR}/${path}"
|
||||||
|
[[ -d "${local_src}" ]] || { echo "ERROR: ${local_src} 없음"; exit 1; }
|
||||||
|
log ">>> rsync ${path}"
|
||||||
|
rsync -az --delete \
|
||||||
|
--exclude 'node_modules/' \
|
||||||
|
--exclude 'build/' \
|
||||||
|
--exclude '.gradle/' \
|
||||||
|
--exclude 'qdrant_data/' \
|
||||||
|
--exclude 'logs/' \
|
||||||
|
"${local_src}/" "${EXDEV_SSH}:${REMOTE_WT}/${path}/"
|
||||||
|
done
|
||||||
|
|
||||||
|
log "overlay 복사..."
|
||||||
|
rsync -az "${ROOT_DIR}/server-dev/docker-compose.rag.server.yml" \
|
||||||
|
"${EXDEV_SSH}:${REMOTE_WT}/server-dev/docker-compose.rag.server.yml"
|
||||||
|
rsync -az "${ROOT_DIR}/local-dev/docker-compose.rag.local.yml" \
|
||||||
|
"${EXDEV_SSH}:${REMOTE_WT}/local-dev/docker-compose.rag.local.yml" 2>/dev/null || true
|
||||||
|
|
||||||
|
ssh -o BatchMode=yes "${EXDEV_SSH}" "mkdir -p '${REMOTE_WT}/exAiChatBot-chatbot2.0-agent' && \
|
||||||
|
cp '${REMOTE_WT}/server-dev/docker-compose.rag.server.yml' '${REMOTE_WT}/exAiChatBot-chatbot2.0-agent/docker-compose.server.yml' 2>/dev/null || true"
|
||||||
|
|
||||||
|
if [[ -d "${ROOT_DIR}/chatbotApp-chatbot2.0-agent/public" ]]; then
|
||||||
|
log ">>> rsync chatbotApp/public (로컬 빌드 산출물)"
|
||||||
|
rsync -az "${ROOT_DIR}/chatbotApp-chatbot2.0-agent/public/" \
|
||||||
|
"${EXDEV_SSH}:${REMOTE_WT}/chatbotApp-chatbot2.0-agent/public/"
|
||||||
|
fi
|
||||||
|
|
||||||
|
log "✅ submodule rsync 완료"
|
||||||
Reference in New Issue
Block a user