Deploy: GitHub submodule sync 2026-07-23 12:46:52

This commit is contained in:
Macbook
2026-07-23 12:46:52 +09:00
parent fb55262bbc
commit 9ea8540979
339 changed files with 811 additions and 44137 deletions
+58 -9
View File
@@ -2,6 +2,33 @@
`chatbot.exdev.co.kr` Mac Studio 서버에 Agent 2.0 스택(RAG + WAS + Skill + chatbotApp)을 배포합니다.
## GitHub upstream (submodule)
4개 서비스 소스는 GitHub `KoreaExpressWayCorp` 저장소를 **git submodule**로 연결합니다.
| monorepo 경로 | GitHub | 브랜치 |
|---------------|--------|--------|
| `chatbotApp-chatbot2.0-agent` | https://github.com/KoreaExpressWayCorp/chatbotApp.git | `chatbot2.0-agent` |
| `chatbotApi-chatbot2.0-agent` | https://github.com/KoreaExpressWayCorp/chatbotApi.git | `chatbot2.0-agent` |
| `exAiChatBot-chatbot2.0-agent` | https://github.com/KoreaExpressWayCorp/exAiChatBot.git | `chatbot2.0-agent` |
| `kakaoChatbotSkill-chatbot2.0-agent` | https://github.com/KoreaExpressWayCorp/kakaoChatbotSkill.git | `chatbot2.0-agent` |
최초 clone:
```bash
git clone --recurse-submodules ssh://aidev@github.exdev.co.kr:2222/aidev/exAichatbot_agent.git
```
GitHub 최신 반영:
```bash
./git_fetch_all.sh # GitHub → 로컬 submodule fast-forward
./git_fetch_all.sh --dry-run # 변경 예상만 확인
./git_fetch_all.sh --init # submodule 최초 clone 포함
```
RAG Docker compose(`docker-compose.server.yml`)는 submodule 밖 **`server-dev/docker-compose.rag.server.yml`** 에 두고, 기동 시 `exAiChatBot-chatbot2.0-agent/`로 복사합니다.
## 디렉터리
| 경로 (서버) | 용도 |
@@ -19,13 +46,37 @@ bash /Users/aidev/dev/exAichatbot_agent/server-dev/setup-gitea.sh
Gitea(`https://github.exdev.co.kr`)에서 **`exAichatbot_agent`** 저장소 생성 (Initialize unchecked).
## 로컬 → Gitea push
## 로컬 Mac → Gitea push → 서버 배포
개발 Mac에서 **한 번에** GitHub 최신 반영 · Gitea push · 서버 빌드·배포:
```bash
./start-all-server.sh
# = git_fetch_all (GitHub) → push-to-gitea → post-receive 배포 대기
```
| 옵션 | 설명 |
|------|------|
| `--local` | 예전처럼 `local-dev/start-all.sh` 로컬 개발 모드 |
| `--no-push` | Gitea push·원격 배포 생략 |
| `--no-pull` | GitHub submodule fetch 생략 |
| `--no-build` | Docker/Next 빌드 생략 (서버에서만 해당) |
수동 push만:
```bash
./server-dev/push-to-gitea.sh
./server-dev/push-to-gitea.sh --dry-run
```
**main push 시 자동:** `post-receive` → `deploy-exAichatbot_agent.sh` → checkout + `git_fetch_all --init` → `start-all-server.sh --no-pull --no-push`
배포 로그: `/Volumes/ADATA/logs/deploy/exAichatbot_agent.log`
## 로컬 → Gitea push (수동)
```bash
cd exChatbot_agent
git init
git add .
git commit -m "Agent 2.0 server deploy"
# Gitea (이 서버는 git@ 가 아닌 aidev@ 사용)
git remote add origin ssh://aidev@github.exdev.co.kr:2222/aidev/exAichatbot_agent.git
@@ -35,18 +86,16 @@ git push -u origin main
# git@github.exdev.co.kr:aidev/exAichatbot_agent.git
```
**main push 시 자동:** `post-receive` → `deploy-exAichatbot_agent.sh` → `stop-all-server.sh` → `start-all-server.sh`
배포 로그: `/Volumes/ADATA/logs/deploy/exAichatbot_agent.log`
## 수동 기동 / 중지 (서버)
```bash
cd /Users/aidev/dev/exAichatbot_agent
./server-dev/stop-all-server.sh
./server-dev/start-all-server.sh # 빌드 포함
./server-dev/start-all-server.sh # Gitea checkout → GitHub submodule → 빌드 → 기동
./server-dev/start-all-server.sh --no-build
./server-dev/start-all-server.sh --no-pull # git 동기화 생략
./server-dev/start-all-server.sh --no-push # Gitea push 생략 (서버 전용)
```
## 스택 구성
+38 -16
View File
@@ -1,36 +1,58 @@
#!/usr/bin/env bash
# 레거시 exAIChatbot2.0 Qdrant(qa_vectors) → Agent 스택 qdrant_data 복사
# 최초 배포 시 vector_count=0 일 때 start-all-server.sh 에서 자동 호출
# 레거시 Qdrant → Agent qdrant_data 복사 (qa_vectors_v2 사용 시 재인덱싱 필요할 수 있음)
set -euo pipefail
ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
RAG_DIR="${ROOT_DIR}/exAiChatBot-chatbot2.0-agent"
LEGACY_STACK_DIR="${LEGACY_STACK_DIR:-/Users/aidev/dev/exAIChatbot2.0}"
LEGACY_QDRANT="${LEGACY_QDRANT_DATA:-${LEGACY_STACK_DIR}/exAiChatBot-chatbot2.0/qdrant_data}"
TARGET_QDRANT="${RAG_DIR}/qdrant_data"
RAG_ENV="${RAG_DIR}/.env"
QDRANT_COLLECTION_NAME="${QDRANT_COLLECTION:-qa_vectors_v2}"
if [[ -f "${RAG_ENV}" ]]; then
_col="$(grep -E '^QDRANT_COLLECTION=' "${RAG_ENV}" 2>/dev/null | tail -1 | cut -d= -f2- | tr -d ' "\r' || true)"
[[ -n "${_col}" ]] && QDRANT_COLLECTION_NAME="${_col}"
fi
LEGACY_CANDIDATES=(
"${LEGACY_QDRANT_DATA:-}"
"/Users/aidev/dev/ex-chatbot-stack/qdrant_data"
"/Users/aidev/dev/exAIChatbot2.0/exAiChatBot-chatbot2.0/qdrant_data"
"/Users/aidev/dev/exAiChatBot/qdrant_data"
)
log() { echo "[$(date '+%H:%M:%S')] $*"; }
if [[ ! -d "${LEGACY_QDRANT}/collections" ]]; then
log "레거시 qdrant_data 없음: ${LEGACY_QDRANT}"
export PATH="/opt/homebrew/bin:/Applications/Docker.app/Contents/Resources/bin:${PATH}"
LEGACY_QDRANT=""
for candidate in "${LEGACY_CANDIDATES[@]}"; do
[[ -n "${candidate}" ]] || continue
if [[ -d "${candidate}/collections" ]]; then
LEGACY_QDRANT="${candidate}"
break
fi
done
if [[ -z "${LEGACY_QDRANT}" ]]; then
log "레거시 qdrant_data 없음"
exit 1
fi
if [[ -d "${TARGET_QDRANT}/collections/qa_vectors" ]]; then
local_count="$(curl -sf http://127.0.0.1:6333/collections/qa_vectors 2>/dev/null \
| python3 -c "import sys,json; print(json.load(sys.stdin).get('result',{}).get('points_count',0))" 2>/dev/null || echo 0)"
if [[ "${local_count}" -gt 0 ]]; then
log "이미 ${local_count} points — bootstrap 생략"
exit 0
fi
local_count="$(curl -sf "http://127.0.0.1:6333/collections/${QDRANT_COLLECTION_NAME}" 2>/dev/null \
| python3 -c "import sys,json; print(json.load(sys.stdin).get('result',{}).get('points_count',0))" 2>/dev/null || echo 0)"
if [[ "${local_count}" -gt 0 ]]; then
log "이미 ${local_count} points — bootstrap 생략"
exit 0
fi
log "Qdrant 중지 후 데이터 복사..."
export PATH="/opt/homebrew/bin:/Applications/Docker.app/Contents/Resources/bin:${PATH}"
log "Qdrant 중지 후 데이터 복사 (${LEGACY_QDRANT})..."
(cd "${RAG_DIR}" && docker compose -f docker-compose.server.yml stop qdrant api admin 2>/dev/null) || true
rm -rf "${TARGET_QDRANT}"
mkdir -p "${TARGET_QDRANT}"
cp -a "${LEGACY_QDRANT}/." "${TARGET_QDRANT}/"
log "✅ qdrant_data 복사 완료 → ${TARGET_QDRANT}"
(cd "${RAG_DIR}" && docker compose -f docker-compose.server.yml up -d qdrant api admin 2>/dev/null) || \
(cd "${RAG_DIR}" && docker compose -f docker-compose.server.yml up -d)
sleep 5
log "✅ qdrant_data 복사 및 RAG 재기동 완료"
+73 -8
View File
@@ -1,5 +1,5 @@
# Agent 2.0 — chatbot.exdev.co.kr HTTPS 라우팅
# chatbotApp(:8092) + Skill(:8083) + RAG(:28012) + Admin(:28013)
# exdev 통합 HTTPS (443) — stock / chatbot(Agent) / bizcare / nationcompany / github / apex
# Agent 배포 시 chatbot-only Caddyfile 로 덮어써 다른 서브도메인이 깨진 것을 복구
{
email {$CADDY_ACME_EMAIL:admin@exdev.co.kr}
auto_https disable_redirects
@@ -9,10 +9,36 @@
tls /etc/caddy/certs/fullchain.pem /etc/caddy/certs/privkey.pem
}
(proxy_std) {
flush_interval -1
transport http {
read_timeout 120s
write_timeout 120s
versions 1.1 2
}
header_up Host {host}
header_up X-Real-IP {remote_host}
header_up X-Forwarded-For {remote_host}
header_up X-Forwarded-Proto {scheme}
}
# ── GoldenChart ───────────────────────────────────────────────────────────────
https://{$STOCK_DOMAIN:stock.exdev.co.kr} {
import tls_certs
handle {
reverse_proxy host.docker.internal:{$GOLDENCHART_HOST_PORT:80} {
import proxy_std
}
}
}
# ── Chatbot Agent 2.0 ─────────────────────────────────────────────────────────
https://{$CHATBOT_DOMAIN:chatbot.exdev.co.kr} {
import tls_certs
# RAG Agent API (Skill 내부 호출·외부 디버그)
# 업스트림 nginx 가 HTTP 로 absolute redirect 해서 http:// 로 떨어지지 않게 Caddy 에서 처리
redir / /chatbot/app/ 302
handle /agent/* {
reverse_proxy host.docker.internal:{$RAG_API_PORT:28012} {
transport http {
@@ -22,7 +48,6 @@ https://{$CHATBOT_DOMAIN:chatbot.exdev.co.kr} {
}
}
# Vector Admin
handle /chatbot/admin* {
uri strip_prefix /chatbot/admin
reverse_proxy host.docker.internal:{$ADMIN_PORT:28013} {
@@ -33,7 +58,6 @@ https://{$CHATBOT_DOMAIN:chatbot.exdev.co.kr} {
}
}
# Skill 웹훅·API (/web/ask 등)
handle /web/* {
reverse_proxy host.docker.internal:{$SKILL_PORT:8083} {
transport http {
@@ -43,13 +67,42 @@ https://{$CHATBOT_DOMAIN:chatbot.exdev.co.kr} {
}
}
# UI + /chatbot/* API 프록시 (chatbotApp nginx)
handle {
reverse_proxy host.docker.internal:{$CHATBOT_APP_PORT:8092} {
import proxy_std
}
}
}
# ── BizCare ───────────────────────────────────────────────────────────────────
https://{$BIZCARE_DOMAIN:bizcare.exdev.co.kr} {
tls /etc/caddy/certs/bizcare/fullchain.pem /etc/caddy/certs/bizcare/privkey.pem
handle {
reverse_proxy host.docker.internal:{$BIZCARE_HOST_PORT:83} {
import proxy_std
}
}
}
# ── ChannelONE (nationcompany) — TLS-ALPN ACME ────────────────────────────────
https://{$NATIONCOMPANY_DOMAIN:nationcompany.exdev.co.kr} {
handle {
reverse_proxy host.docker.internal:{$NATIONCOMPANY_HOST_PORT:84} {
import proxy_std
}
}
}
# ── Gitea (github.exdev.co.kr) ────────────────────────────────────────────────
https://{$GITHUB_DOMAIN:github.exdev.co.kr} {
tls /etc/caddy/certs/github/fullchain.pem /etc/caddy/certs/github/privkey.pem
handle {
reverse_proxy host.docker.internal:{$GITEA_HOST_PORT:3000} {
flush_interval -1
transport http {
read_timeout 120s
write_timeout 120s
read_timeout 3600s
write_timeout 3600s
versions 1.1 2
}
header_up Host {host}
header_up X-Real-IP {remote_host}
@@ -58,3 +111,15 @@ https://{$CHATBOT_DOMAIN:chatbot.exdev.co.kr} {
}
}
}
# ── Legacy apex ───────────────────────────────────────────────────────────────
https://{$HTTPS_DOMAIN:exdev.co.kr} {
import tls_certs
@legacy_chatbot path /chatbot /chatbot/*
handle @legacy_chatbot {
redir https://{$CHATBOT_DOMAIN:chatbot.exdev.co.kr}{uri} permanent
}
handle {
redir https://{$STOCK_DOMAIN:stock.exdev.co.kr}{uri} permanent
}
}
+13 -9
View File
@@ -44,27 +44,31 @@ log "======================================================"
[[ -d "$GIT_DIR" ]] || fail "GIT_DIR 없음: $GIT_DIR"
mkdir -p "$WORK_TREE"
log "[Phase 1] git checkout -f $BRANCH"
git --work-tree="$WORK_TREE" --git-dir="$GIT_DIR" checkout -f "$BRANCH"
COMMIT="$(git --git-dir="$GIT_DIR" rev-parse --short HEAD)"
log "체크아웃: $COMMIT"
chmod +x "$WORK_TREE/server-dev/"*.sh 2>/dev/null || true
chmod +x "$WORK_TREE/chatbotApp-chatbot2.0-agent/build.sh" 2>/dev/null || true
log "[Phase 2] stop-all-server.sh"
log "[Phase 1] stop-all-server.sh"
"$WORK_TREE/server-dev/stop-all-server.sh" --keep-caddy || true
log "[Phase 3] .env 준비"
log "[Phase 2] .env 준비"
ENV_FILE="$WORK_TREE/exAiChatBot-chatbot2.0-agent/.env"
if [[ ! -f "$ENV_FILE" ]]; then
cp "$WORK_TREE/server-dev/env.exdev-server.template" "$ENV_FILE"
log ".env 생성 (env.exdev-server.template)"
fi
log "[Phase 4] start-all-server.sh (빌드 포함)"
"$WORK_TREE/server-dev/start-all-server.sh" --skip-legacy-stop
log "[Phase 2.5] work tree checkout + GitHub submodule"
git --work-tree="$WORK_TREE" --git-dir="$GIT_DIR" checkout -f "$BRANCH"
if [[ -x "$WORK_TREE/git_fetch_all.sh" ]]; then
"$WORK_TREE/git_fetch_all.sh" --init || log "[WARN] git_fetch_all 실패"
else
git -C "$WORK_TREE" submodule update --init --recursive 2>/dev/null || true
fi
log "[Phase 3] start-all-server.sh (빌드 + 기동, push 생략)"
"$WORK_TREE/server-dev/start-all-server.sh" --skip-legacy-stop --no-pull --no-push
COMMIT="$(git --git-dir="$GIT_DIR" rev-parse --short HEAD 2>/dev/null || echo unknown)"
log "======================================================"
log " 배포 완료 — $COMMIT"
log " UI: https://chatbot.exdev.co.kr/chatbot/app/"
+75
View File
@@ -0,0 +1,75 @@
# exdev Mac Studio 서버 — Agent RAG (Mac Docker: 포트 매핑 필수, host network 미지원)
name: exaichatbot-agent
services:
qdrant:
image: qdrant/qdrant:v1.18.2
ports:
- "6333:6333"
- "6334:6334"
volumes:
- ./qdrant_data:/qdrant/storage
restart: unless-stopped
api:
image: "${RAG_API_IMAGE:-rag-api-slim:latest}"
build:
context: .
dockerfile: docker/service-slim.Dockerfile
ports:
- "${RAG_API_PORT:-28012}:28012"
extra_hosts:
- "host.docker.internal:host-gateway"
depends_on:
- qdrant
env_file:
- .env
environment:
PYTHONUNBUFFERED: "1"
HTTP_PROXY: ""
HTTPS_PROXY: ""
QDRANT_HOST: qdrant
QDRANT_PORT: "6333"
MONGO_HOST: host.docker.internal
LLM_BASE_URL: http://host.docker.internal:16000
TEI_EMBED_URL: http://host.docker.internal:16001
TEI_RERANK_URL: http://host.docker.internal:16002
volumes:
- ./data:/app/data
- ./scripts:/app/scripts
restart: unless-stopped
healthcheck:
test: ["CMD", "curl", "-sf", "http://localhost:28012/health"]
interval: 10s
timeout: 10s
retries: 12
start_period: 60s
admin:
image: "${RAG_API_IMAGE:-rag-api-slim:latest}"
ports:
- "${ADMIN_PORT:-28013}:28013"
extra_hosts:
- "host.docker.internal:host-gateway"
command:
- python
- -m
- uvicorn
- scripts.admin_service:app
- --host
- 0.0.0.0
- --port
- "28013"
working_dir: /app
depends_on:
- qdrant
env_file:
- .env
environment:
PYTHONUNBUFFERED: "1"
QDRANT_HOST: qdrant
QDRANT_PORT: "6333"
volumes:
- ./data:/app/data
- ./scripts:/app/scripts
restart: unless-stopped
+1 -1
View File
@@ -26,7 +26,7 @@ MONGO_COLLECTION=rag_conversations
VECTOR_STORE=qdrant
QDRANT_HOST=127.0.0.1
QDRANT_PORT=6333
QDRANT_COLLECTION=qa_vectors
QDRANT_COLLECTION=qa_vectors_v2
# ── Agent / WAS ──
AGENT_MODE=agent
+3 -3
View File
@@ -3,9 +3,9 @@
# 설치: setup-gitea.sh 가 server hooks/post-receive 로 복사
set -euo pipefail
GIT_DIR="/Volumes/ADATA/git/exAichatbot_agent.git"
WORK_TREE="/Users/aidev/dev/exAichatbot_agent"
DEPLOY_SCRIPT="/Volumes/ADATA/git/deploy-exAichatbot_agent.sh"
GIT_DIR="${GIT_DIR:-/Volumes/ADATA/gitea/data/gitea-repositories/aidev/exaichatbot_agent.git}"
WORK_TREE="${WORK_TREE:-/Users/aidev/dev/exAichatbot_agent}"
DEPLOY_SCRIPT="${DEPLOY_SCRIPT:-/Volumes/ADATA/git/deploy-exAichatbot_agent.sh}"
DEPLOY_LOG="/Volumes/ADATA/logs/deploy/exAichatbot_agent.log"
HOOK_TAG="[post-receive]"
+118
View File
@@ -0,0 +1,118 @@
#!/usr/bin/env bash
# 로컬 monorepo + GitHub submodule 포인터 → Gitea push (post-receive 배포 트리거)
#
# ./server-dev/push-to-gitea.sh
# ./server-dev/push-to-gitea.sh --dry-run
set -euo pipefail
ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
GIT_BRANCH="${GIT_BRANCH:-main}"
GITEA_REMOTE="${GITEA_REMOTE:-origin}"
DRY_RUN=false
for arg in "$@"; do
case "$arg" in
--dry-run) DRY_RUN=true ;;
-h|--help)
sed -n '2,6p' "$0"
exit 0
;;
*)
echo "알 수 없는 옵션: $arg (--help)"
exit 1
;;
esac
done
log() { echo "[push-to-gitea] $*"; }
warn() { echo "[push-to-gitea] ⚠️ $*" >&2; }
fail() { log "ERROR: $*"; exit 1; }
cd "${ROOT_DIR}"
[[ -d .git ]] || fail "Git 저장소 아님: ${ROOT_DIR}"
if [[ ! -f .gitmodules ]]; then
fail ".gitmodules 없음 — submodule 설정 후 다시 실행"
fi
if [[ -x "${ROOT_DIR}/git_fetch_all.sh" ]]; then
log "GitHub submodule 동기화..."
if ! $DRY_RUN; then
"${ROOT_DIR}/git_fetch_all.sh"
else
"${ROOT_DIR}/git_fetch_all.sh" --dry-run
fi
else
warn "git_fetch_all.sh 없음 — submodule GitHub 동기화 생략"
fi
# 서버 env.template·overlay와 중복되는 submodule 작업트리 변경은 되돌림
reset_submodule_noise() {
local path="$1"
shift
[[ -d "${ROOT_DIR}/${path}/.git" ]] || return 0
local f
for f in "$@"; do
if git -C "${ROOT_DIR}/${path}" diff --quiet -- "$f" 2>/dev/null; then
continue
fi
if $DRY_RUN; then
log "[dry-run] ${path} — 되돌림: $f"
else
git -C "${ROOT_DIR}/${path}" checkout -- "$f" 2>/dev/null || true
fi
done
}
reset_submodule_noise "exAiChatBot-chatbot2.0-agent" \
create-offline-package-slim.sh docker-compose-slim.yml \
env.example env.template scripts/vector_store.py
reset_submodule_noise "chatbotApi-chatbot2.0-agent" gradlew
reset_submodule_noise "kakaoChatbotSkill-chatbot2.0-agent" gradlew
if $DRY_RUN; then
log "[dry-run] git add / commit / push 생략"
git status -sb
git submodule status || true
exit 0
fi
chmod +x git_fetch_all.sh server-dev/*.sh 2>/dev/null || true
chmod +x chatbotApp-chatbot2.0-agent/build.sh 2>/dev/null || true
git add .gitmodules
git add local-dev/ server-dev/ scripts/ git_fetch_all.sh 2>/dev/null || true
git add start-all-server.sh stop-all-server.sh 2>/dev/null || true
for path in chatbotApp-chatbot2.0-agent chatbotApi-chatbot2.0-agent \
exAiChatBot-chatbot2.0-agent kakaoChatbotSkill-chatbot2.0-agent; do
[[ -d "${path}" ]] && git add "${path}" 2>/dev/null || true
done
# embedded → submodule 전환 잔여 삭제 반영
git add -u
if ! git diff --cached --quiet; then
msg="Deploy: GitHub submodule sync $(date '+%Y-%m-%d %H:%M:%S')"
log "monorepo 커밋: ${msg}"
git commit -m "${msg}"
else
log "monorepo 스테이징 변경 없음"
fi
git fetch "${GITEA_REMOTE}" "${GIT_BRANCH}" 2>/dev/null || true
local_rev="$(git rev-parse HEAD)"
remote_rev=""
if git rev-parse "${GITEA_REMOTE}/${GIT_BRANCH}" >/dev/null 2>&1; then
remote_rev="$(git rev-parse "${GITEA_REMOTE}/${GIT_BRANCH}")"
fi
if [[ -n "${remote_rev}" && "${local_rev}" == "${remote_rev}" ]]; then
log "✅ Gitea 이미 최신 (${local_rev:0:7})"
exit 0
fi
log "Gitea push ${GITEA_REMOTE} ${GIT_BRANCH} (${local_rev:0:7})..."
git push "${GITEA_REMOTE}" "${GIT_BRANCH}"
log "✅ Gitea push 완료 — post-receive 배포 트리거됨"
+177 -7
View File
@@ -5,6 +5,9 @@
# ./server-dev/start-all-server.sh --no-build
# ./server-dev/start-all-server.sh --skip-java
# ./server-dev/start-all-server.sh --skip-legacy-stop
# ./server-dev/start-all-server.sh --no-pull # git 동기화 생략
# ./server-dev/start-all-server.sh --no-push # Gitea push 생략
# ./server-dev/start-all-server.sh --local # 로컬 Mac 개발 모드 (local-dev 위임)
set -euo pipefail
ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
@@ -12,6 +15,10 @@ SERVER_DEV_DIR="${ROOT_DIR}/server-dev"
RUN_DIR="${SERVER_DEV_DIR}/run"
LOG_DIR="${SERVER_DEV_DIR}/logs"
GIT_DIR="${GIT_DIR:-/Volumes/ADATA/gitea/data/gitea-repositories/aidev/exaichatbot_agent.git}"
WORK_TREE="${WORK_TREE:-/Users/aidev/dev/exAichatbot_agent}"
GIT_BRANCH="${GIT_BRANCH:-main}"
RAG_DIR="${ROOT_DIR}/exAiChatBot-chatbot2.0-agent"
API_DIR="${ROOT_DIR}/chatbotApi-chatbot2.0-agent"
SKILL_DIR="${ROOT_DIR}/kakaoChatbotSkill-chatbot2.0-agent"
@@ -40,6 +47,10 @@ NO_BUILD=false
SKIP_JAVA=false
SKIP_LEGACY_STOP=false
SKIP_CADDY=false
NO_PULL=false
NO_PUSH=false
LOCAL_DEV=false
EXDEV_SSH="${EXDEV_SSH:-aidev@chatbot.exdev.co.kr}"
for arg in "$@"; do
case "$arg" in
@@ -47,8 +58,11 @@ for arg in "$@"; do
--skip-java) SKIP_JAVA=true ;;
--skip-legacy-stop) SKIP_LEGACY_STOP=true ;;
--skip-caddy) SKIP_CADDY=true ;;
--no-pull) NO_PULL=true ;;
--no-push) NO_PUSH=true ;;
--local) LOCAL_DEV=true ;;
-h|--help)
sed -n '2,10p' "$0"
sed -n '2,12p' "$0"
exit 0
;;
*)
@@ -62,6 +76,133 @@ export PATH="/opt/homebrew/bin:/usr/local/bin:/Applications/Docker.app/Contents/
log() { echo "[$(date '+%H:%M:%S')] $*"; }
fail() { log "ERROR: $*"; exit 1; }
warn() { log "⚠️ $*"; }
sync_github_submodules() {
local tree="$1"
local fetch_script="${tree}/git_fetch_all.sh"
local -a fetch_args=()
if is_exdev_server; then
fetch_args+=(--init)
fi
if [[ ! -x "${fetch_script}" ]]; then
log "git_fetch_all.sh 없음 — submodule GitHub 동기화 생략"
return 0
fi
log "GitHub submodule 최신 동기화 (git_fetch_all.sh)..."
if [[ ${#fetch_args[@]} -gt 0 ]]; then
if ! "${fetch_script}" "${fetch_args[@]}"; then
warn "GitHub submodule 동기화 실패 — ./git_fetch_all.sh 수동 실행"
return 1
fi
elif ! "${fetch_script}"; then
warn "GitHub submodule 동기화 실패 — ./git_fetch_all.sh 수동 실행"
return 1
fi
return 0
}
push_source_to_gitea() {
if $NO_PUSH; then
log "Gitea push 생략 (--no-push)"
return 0
fi
if is_exdev_server; then
log "exdev 서버 — Gitea push 생략 (post-receive/deploy 경로 사용)"
return 0
fi
local push_script="${SERVER_DEV_DIR}/push-to-gitea.sh"
[[ -x "${push_script}" ]] || fail "push-to-gitea.sh 없음: ${push_script}"
log "현재 소스 → Gitea (${GIT_BRANCH}) 반영..."
"${push_script}"
}
wait_remote_deploy() {
if $NO_PUSH; then
return 0
fi
local wait_script="${SERVER_DEV_DIR}/wait-remote-deploy.sh"
if [[ -x "${wait_script}" ]]; then
EXDEV_SSH="${EXDEV_SSH}" PUBLIC_BASE_URL="${PUBLIC_BASE_URL}" "${wait_script}" || \
warn "원격 배포 대기 실패 — ssh ${EXDEV_SSH} tail /Volumes/ADATA/logs/deploy/exAichatbot_agent.log"
else
warn "wait-remote-deploy.sh 없음 — https://chatbot.exdev.co.kr/chatbot/app/ 수동 확인"
fi
}
deploy_from_local_mac() {
log "로컬 Mac → Gitea push → chatbot.exdev.co.kr 배포"
if ! $NO_PULL; then
sync_github_submodules "${ROOT_DIR}" || true
fi
push_source_to_gitea
wait_remote_deploy
log "=========================================="
log "원격 배포 요청 완료"
log " UI ${PUBLIC_BASE_URL}/chatbot/app/"
log " 로그 ssh ${EXDEV_SSH} tail -f /Volumes/ADATA/logs/deploy/exAichatbot_agent.log"
log "=========================================="
exit 0
}
sync_source_from_git() {
if $NO_PULL; then
log "git 동기화 생략 (--no-pull)"
return 0
fi
local tree="${WORK_TREE}"
local branch="${GIT_BRANCH}"
if [[ -d "${GIT_DIR}" ]]; then
mkdir -p "${tree}"
log "Gitea 최신 소스 동기화 (${branch})..."
if ! git --git-dir="${GIT_DIR}" show-ref --verify --quiet "refs/heads/${branch}" 2>/dev/null; then
fail "브랜치 없음: ${branch} (GIT_DIR=${GIT_DIR})"
fi
git --work-tree="${tree}" --git-dir="${GIT_DIR}" checkout -f "${branch}"
sync_github_submodules "${tree}" || true
local commit
commit="$(git --git-dir="${GIT_DIR}" rev-parse --short HEAD)"
log "✅ 소스 동기화: ${commit} (${branch})"
elif [[ -d "${ROOT_DIR}/.git" ]]; then
log "git pull origin ${branch}..."
(
cd "${ROOT_DIR}"
git fetch origin "${branch}"
git checkout "${branch}"
git pull --ff-only origin "${branch}"
)
sync_github_submodules "${ROOT_DIR}" || true
log "✅ git pull 완료: $(git -C "${ROOT_DIR}" rev-parse --short HEAD)"
tree="${ROOT_DIR}"
else
log "Git 저장소 없음 — 현재 디렉터리 소스 사용 (${ROOT_DIR})"
return 0
fi
chmod +x "${tree}/server-dev/"*.sh 2>/dev/null || true
chmod +x "${tree}/git_fetch_all.sh" 2>/dev/null || true
chmod +x "${tree}/chatbotApp-chatbot2.0-agent/build.sh" 2>/dev/null || true
chmod +x "${tree}/start-all-server.sh" "${tree}/stop-all-server.sh" 2>/dev/null || true
}
is_exdev_server() {
[[ -d "${GIT_DIR}" ]] \
&& [[ -f "${LEGACY_STACK_DIR}/scripts/exdev/start-host-ai.sh" ]]
}
run_local_dev() {
log "로컬 Mac — local-dev/start-all.sh 로 위임 (exdev AI SSH 터널 사용)"
local -a args=()
$NO_BUILD && args+=(--no-build)
$SKIP_JAVA && args+=(--skip-java)
if ((${#args[@]} > 0)); then
exec "${ROOT_DIR}/local-dev/start-all.sh" "${args[@]}"
else
exec "${ROOT_DIR}/local-dev/start-all.sh"
fi
}
wait_http() {
local url="$1" label="$2" max="${3:-90}"
@@ -136,7 +277,7 @@ ensure_rag_env() {
}
ensure_mongo() {
if docker ps --format '{{.Names}}' | grep -qx "${MONGO_CONTAINER}"; then
if docker ps -a --format '{{.Names}}' | grep -qx "${MONGO_CONTAINER}"; then
docker start "${MONGO_CONTAINER}" >/dev/null 2>&1 || true
log "MongoDB 재사용: ${MONGO_CONTAINER}"
wait_port 27017 "MongoDB" 20 || true
@@ -152,8 +293,14 @@ ensure_mongo() {
}
bootstrap_qdrant_if_empty() {
local collection="${QDRANT_COLLECTION:-qa_vectors_v2}"
if [[ -f "${RAG_ENV}" ]]; then
local from_env
from_env="$(grep -E '^QDRANT_COLLECTION=' "${RAG_ENV}" 2>/dev/null | tail -1 | cut -d= -f2- | tr -d ' "\r' || true)"
[[ -n "${from_env}" ]] && collection="${from_env}"
fi
local vc
vc="$(curl -sf "http://127.0.0.1:6333/collections/qa_vectors" 2>/dev/null \
vc="$(curl -sf "http://127.0.0.1:6333/collections/${collection}" 2>/dev/null \
| python3 -c "import sys,json; d=json.load(sys.stdin); print(d.get('result',{}).get('points_count',0))" 2>/dev/null || echo 0)"
if [[ "${vc}" -gt 0 ]]; then
log "Qdrant 기존 데이터 ${vc} points"
@@ -166,17 +313,26 @@ bootstrap_qdrant_if_empty() {
fi
}
ensure_rag_compose() {
local template="${SERVER_DEV_DIR}/docker-compose.rag.server.yml"
[[ -f "${template}" ]] || fail "server-dev/docker-compose.rag.server.yml 없음"
cp "${template}" "${COMPOSE_SERVER}"
}
start_rag() {
ensure_rag_env
ensure_rag_compose
[[ -f "${COMPOSE_SERVER}" ]] || fail "docker-compose.server.yml 없음"
cd "${RAG_DIR}"
docker compose -f "${COMPOSE_SERVER}" down --remove-orphans 2>/dev/null || true
local -a up_flags=(-d)
if $NO_BUILD; then up_flags+=(--no-build); else up_flags+=(--build); fi
log "RAG 스택 기동..."
log "RAG 스택 기동 (docker-compose.server.yml)..."
docker compose -f "${COMPOSE_SERVER}" up "${up_flags[@]}"
wait_http "http://127.0.0.1:6333/collections" "Qdrant" 30 || true
bootstrap_qdrant_if_empty
wait_http "http://127.0.0.1:${RAG_PORT}/health" "RAG API" 120
(cd "${RAG_DIR}" && docker compose -f "${COMPOSE_SERVER}" up -d) || true
wait_http "http://127.0.0.1:${RAG_PORT}/health" "RAG API" 180 || true
wait_port "${ADMIN_PORT}" "Vector Admin" 30 || true
}
@@ -278,12 +434,26 @@ verify_stack() {
}
mkdir -p "${RUN_DIR}" "${LOG_DIR}"
docker info >/dev/null 2>&1 || fail "Docker Engine 미기동"
log "=========================================="
log "exAichatbot Agent 2.0 — exdev 서버 기동"
log "exAichatbot Agent 2.0 기동"
log "=========================================="
if ! is_exdev_server; then
if $LOCAL_DEV; then
sync_source_from_git
run_local_dev
else
deploy_from_local_mac
fi
fi
sync_source_from_git
docker info >/dev/null 2>&1 || fail "Docker Engine 미기동"
log "exdev 서버 모드"
stop_legacy_stack
ensure_host_ai
ensure_mongo
+43
View File
@@ -0,0 +1,43 @@
#!/usr/bin/env bash
# Gitea push 후 chatbot.exdev.co.kr 배포 완료 대기
set -euo pipefail
EXDEV_SSH="${EXDEV_SSH:-aidev@chatbot.exdev.co.kr}"
DEPLOY_LOG="${DEPLOY_LOG:-/Volumes/ADATA/logs/deploy/exAichatbot_agent.log}"
PUBLIC_URL="${PUBLIC_BASE_URL:-https://chatbot.exdev.co.kr}/chatbot/app/"
MAX_WAIT="${DEPLOY_WAIT_SEC:-900}"
log() { echo "[wait-deploy] $*"; }
ssh_cmd() {
ssh -o BatchMode=yes -o ConnectTimeout=15 "${EXDEV_SSH}" "$@"
}
if ! ssh_cmd "test -f '${DEPLOY_LOG}'" 2>/dev/null; then
log "배포 로그 없음 — SSH로 deploy 스크립트 직접 실행"
ssh_cmd "bash /Volumes/ADATA/git/deploy-exAichatbot_agent.sh" || true
fi
log "배포 완료 대기 (최대 ${MAX_WAIT}s) — ${EXDEV_SSH}"
start_ts="$(date +%s)"
deadline=$((start_ts + MAX_WAIT))
while (( $(date +%s) < deadline )); do
if curl -sf --max-time 5 "${PUBLIC_URL}" 2>/dev/null | grep -q 'data-chatbot-app'; then
log "✅ UI 응답: ${PUBLIC_URL}"
ssh_cmd "tail -8 '${DEPLOY_LOG}'" 2>/dev/null || true
exit 0
fi
if ssh_cmd "grep -q '배포 완료' '${DEPLOY_LOG}'" 2>/dev/null; then
last_line="$(ssh_cmd "tail -1 '${DEPLOY_LOG}'" 2>/dev/null || true)"
if [[ "${last_line}" == *"배포 완료"* ]]; then
log "✅ 배포 로그 확인"
ssh_cmd "tail -8 '${DEPLOY_LOG}'" 2>/dev/null || true
exit 0
fi
fi
sleep 10
done
log "⚠️ 배포 대기 타임아웃 — 로그 확인: ssh ${EXDEV_SSH} tail -50 ${DEPLOY_LOG}"
exit 1