Deploy: GitHub submodule sync 2026-07-23 12:46:52
This commit is contained in:
+58
-9
@@ -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 생략 (서버 전용)
|
||||
```
|
||||
|
||||
## 스택 구성
|
||||
|
||||
@@ -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 재기동 완료"
|
||||
|
||||
@@ -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
@@ -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/"
|
||||
|
||||
@@ -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
|
||||
@@ -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,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]"
|
||||
|
||||
|
||||
Executable
+118
@@ -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 배포 트리거됨"
|
||||
@@ -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
|
||||
|
||||
Executable
+43
@@ -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
|
||||
Reference in New Issue
Block a user