diff --git a/git_fetch_all.sh b/git_fetch_all.sh index 543c3ff..5d995c0 100755 --- a/git_fetch_all.sh +++ b/git_fetch_all.sh @@ -13,6 +13,9 @@ set -euo pipefail 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 DRY_RUN=false @@ -56,8 +59,8 @@ if $INIT && ! $DRY_RUN; then fi resolve_monorepo_git_dir() { - if [[ -n "${GIT_DIR:-}" && -d "${GIT_DIR}" ]]; then - echo "${GIT_DIR}" + if [[ -n "${MONOREPO_GIT_DIR}" && -d "${MONOREPO_GIT_DIR}" ]]; then + echo "${MONOREPO_GIT_DIR}" elif git -C "${ROOT_DIR}" rev-parse --git-dir >/dev/null 2>&1; then git -C "${ROOT_DIR}" rev-parse --git-dir fi @@ -79,7 +82,10 @@ clone_submodule_repo() { sha="$(submodule_pinned_sha "${path}" || true)" log " clone ${url} → ${path} (branch=${branch})" 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 git -C "${target}" checkout "${sha}" 2>/dev/null || true fi @@ -98,6 +104,11 @@ restore_monorepo_overlays() { fi } +submodule_is_cloned() { + local path="$1" + [[ -e "${ROOT_DIR}/${path}/.git" ]] +} + fetch_one() { local path="$1" local url branch old new dirty @@ -109,13 +120,13 @@ fetch_one() { log ">>> ${path}" 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 log " [dry-run] submodule clone 필요 — ./git_fetch_all.sh --init" return 0 fi if $INIT; then - clone_submodule_repo "${path}" "${url}" "${branch}" + clone_submodule_repo "${path}" "${url}" "${branch}" || return 1 else warn "${path} — submodule 미초기화. --init 옵션 사용" return 1 diff --git a/server-dev/README.md b/server-dev/README.md index 583faf8..26d58c7 100644 --- a/server-dev/README.md +++ b/server-dev/README.md @@ -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` +서버에서 GitHub clone 이 안 되면 **로컬 `./start-all-server.sh`** 가 submodule rsync + chatbotApp 로컬 빌드 후 배포합니다. + 배포 로그: `/Volumes/ADATA/logs/deploy/exAichatbot_agent.log` ## 로컬 → Gitea push (수동) diff --git a/server-dev/deploy.sh b/server-dev/deploy.sh index acfdfd1..f7347d5 100755 --- a/server-dev/deploy.sh +++ b/server-dev/deploy.sh @@ -12,9 +12,6 @@ BRANCH="${BRANCH:-main}" DEPLOY_LOG="${DEPLOY_LOG:-/Volumes/ADATA/logs/deploy/exAichatbot_agent.log}" 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}" mkdir -p "$(dirname "$DEPLOY_LOG")" @@ -34,47 +31,63 @@ fi echo $$ > "$LOCK_FILE" 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 " exAichatbot Agent 2.0 배포 시작" log " GIT_DIR : $GIT_DIR" log " WORK_TREE : $WORK_TREE" log " BRANCH : $BRANCH" +log " START_ONLY: $START_ONLY" log "======================================================" [[ -d "$GIT_DIR" ]] || fail "GIT_DIR 없음: $GIT_DIR" mkdir -p "$WORK_TREE" 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 +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" "$WORK_TREE/server-dev/stop-all-server.sh" --keep-caddy || true -ensure_worktree_git() { - local tree="$1" - local git_dir="$2" - if [[ ! -e "${tree}/.git" ]]; then - echo "gitdir: ${git_dir}" > "${tree}/.git" - log "work tree .git 연결: ${tree}" - 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" +if ! $START_ONLY; then + log "[Phase 2] checkout + GitHub submodule" + git --work-tree="$WORK_TREE" --git-dir="$GIT_DIR" checkout -f "$BRANCH" + rm -f "$WORK_TREE/.git" + sync_worktree_submodules "$WORK_TREE" +fi log "[Phase 3] .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)" fi -log "[Phase 4] start-all-server.sh (빌드 + 기동, push 생략)" -"$WORK_TREE/server-dev/start-all-server.sh" --skip-legacy-stop --no-pull --no-push +log "[Phase 4] start-all-server.sh (기동)" +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)" log "======================================================" diff --git a/server-dev/start-all-server.sh b/server-dev/start-all-server.sh index 236c461..e118635 100755 --- a/server-dev/start-all-server.sh +++ b/server-dev/start-all-server.sh @@ -130,12 +130,48 @@ wait_remote_deploy() { 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() { log "로컬 Mac → Gitea push → chatbot.exdev.co.kr 배포" if ! $NO_PULL; then sync_github_submodules "${ROOT_DIR}" || true fi + build_chatbot_app_for_deploy push_source_to_gitea + trigger_remote_deploy wait_remote_deploy log "==========================================" log "원격 배포 요청 완료" @@ -161,9 +197,6 @@ sync_source_from_git() { fail "브랜치 없음: ${branch} (GIT_DIR=${GIT_DIR})" fi 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 local commit commit="$(git --git-dir="${GIT_DIR}" rev-parse --short HEAD)" diff --git a/server-dev/sync-submodules-rsync.sh b/server-dev/sync-submodules-rsync.sh new file mode 100755 index 0000000..0fd9250 --- /dev/null +++ b/server-dev/sync-submodules-rsync.sh @@ -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 완료"