feat(setup): DB 선택/pgvector/Qdrant/Gitea 통합
[DB 선택 대화형 프롬프트] - setup/lib/db_select.sh: 설치 시 SQLite/PostgreSQL/PostgreSQL+pgvector 선택 - DB_TYPE 환경변수로 무인 설치 지원 - pgvector 자동 빌드/설치 (소스 빌드 폴백 포함) - .env DATABASE_URL 자동 기록 [Vector DB] - docker-compose.yml: postgres를 pgvector/pgvector:pg15 이미지로 교체 - Qdrant: docker profile=vector 로 선택적 활성화 - docs/vector_db_guide.md: pgvector/Qdrant 사용법 + 시나리오 [Gitea 온프레미스 Git 서버] - docker-compose.yml: gitea/gitea:1.21-rootless 서비스 추가 - setup/lib/gitea_setup.sh: 공통 설치/초기화 함수 - setup/gitea_init.sh: 독립 실행형 초기화 스크립트 - 관리자 계정 생성 - guardia 조직 + GUARDiA 저장소 생성 - main 브랜치 보호 (PR + 리뷰 1명 필수) - develop 브랜치 생성 - 개발자 계정 (engineer1/2, pm1, admin) + feature/이름/init 브랜치 자동 생성 - 현재 소스 자동 push [브랜치 전략] main : 보호 브랜치, PR 필수, 리뷰 1명 필수 develop : 통합 브랜치, force-push 금지 feature/이름/기능: 개인 개발 브랜치 Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
d3e13d2fc2
commit
4b7904d14a
@ -85,7 +85,9 @@ services:
|
|||||||
|
|
||||||
# ── PostgreSQL ───────────────────────────────────────────
|
# ── PostgreSQL ───────────────────────────────────────────
|
||||||
postgres:
|
postgres:
|
||||||
image: postgres:15-alpine
|
# pgvector/pgvector:pg15 = PostgreSQL 15 + pgvector 확장 포함
|
||||||
|
# vector 타입 사용: SR 유사도 검색, KB 시맨틱 검색
|
||||||
|
image: pgvector/pgvector:pg15
|
||||||
container_name: guardia-postgres
|
container_name: guardia-postgres
|
||||||
environment:
|
environment:
|
||||||
POSTGRES_DB: guardia
|
POSTGRES_DB: guardia
|
||||||
@ -177,6 +179,64 @@ services:
|
|||||||
retries: 3
|
retries: 3
|
||||||
start_period: 30s
|
start_period: 30s
|
||||||
|
|
||||||
|
# ── Qdrant (전용 벡터 DB — 고성능 시맨틱 검색) ───────────
|
||||||
|
# pgvector보다 빠른 ANN 검색이 필요할 때 사용
|
||||||
|
# QDRANT_ENABLED=true 시 guardia에서 QDRANT_URL=http://qdrant:6333 설정
|
||||||
|
qdrant:
|
||||||
|
image: qdrant/qdrant:v1.7.4
|
||||||
|
container_name: guardia-qdrant
|
||||||
|
profiles: ["vector"] # docker compose --profile vector up 으로 활성화
|
||||||
|
ports:
|
||||||
|
- "6333:6333"
|
||||||
|
- "6334:6334"
|
||||||
|
volumes:
|
||||||
|
- guardia-qdrant:/qdrant/storage
|
||||||
|
environment:
|
||||||
|
QDRANT__SERVICE__GRPC_PORT: "6334"
|
||||||
|
networks:
|
||||||
|
- guardia-net
|
||||||
|
restart: unless-stopped
|
||||||
|
healthcheck:
|
||||||
|
test: ["CMD", "curl", "-sf", "http://localhost:6333/healthz"]
|
||||||
|
interval: 30s
|
||||||
|
timeout: 5s
|
||||||
|
retries: 3
|
||||||
|
|
||||||
|
# ── Gitea (온프레미스 Git 서버) ──────────────────────────
|
||||||
|
# 형상관리: 저장소 생성, 브랜치 보호, PR 워크플로우
|
||||||
|
gitea:
|
||||||
|
image: gitea/gitea:1.21-rootless
|
||||||
|
container_name: guardia-gitea
|
||||||
|
ports:
|
||||||
|
- "3000:3000" # HTTP (web + API)
|
||||||
|
- "2222:2222" # SSH
|
||||||
|
environment:
|
||||||
|
USER_UID: "1000"
|
||||||
|
USER_GID: "1000"
|
||||||
|
GITEA__DEFAULT__APP_NAME: "GUARDiA Git"
|
||||||
|
GITEA__SERVER__HTTP_PORT: "3000"
|
||||||
|
GITEA__SERVER__SSH_PORT: "2222"
|
||||||
|
GITEA__SERVER__ROOT_URL: "http://localhost:3000/"
|
||||||
|
GITEA__DATABASE__DB_TYPE: "sqlite3"
|
||||||
|
GITEA__DATABASE__PATH: "/var/lib/gitea/data/gitea.db"
|
||||||
|
GITEA__REPOSITORY__DEFAULT_BRANCH: "main"
|
||||||
|
GITEA__GIT__DEFAULT_BRANCH: "main"
|
||||||
|
GITEA__SECURITY__INSTALL_LOCK: "true"
|
||||||
|
GITEA__SERVICE__DISABLE_REGISTRATION: "false"
|
||||||
|
volumes:
|
||||||
|
- guardia-gitea-data:/var/lib/gitea
|
||||||
|
- guardia-gitea-config:/etc/gitea
|
||||||
|
- /etc/timezone:/etc/timezone:ro
|
||||||
|
networks:
|
||||||
|
- guardia-net
|
||||||
|
restart: unless-stopped
|
||||||
|
healthcheck:
|
||||||
|
test: ["CMD", "curl", "-sf", "http://localhost:3000/api/v1/version"]
|
||||||
|
interval: 30s
|
||||||
|
timeout: 5s
|
||||||
|
retries: 3
|
||||||
|
start_period: 30s
|
||||||
|
|
||||||
# ── 볼륨 ─────────────────────────────────────────────────
|
# ── 볼륨 ─────────────────────────────────────────────────
|
||||||
volumes:
|
volumes:
|
||||||
guardia-db:
|
guardia-db:
|
||||||
@ -186,6 +246,9 @@ volumes:
|
|||||||
guardia-ollama-models: # Ollama 모델 (로컬 경로 마운트 가능)
|
guardia-ollama-models: # Ollama 모델 (로컬 경로 마운트 가능)
|
||||||
guardia-tomcat-webapps:
|
guardia-tomcat-webapps:
|
||||||
guardia-tomcat-logs:
|
guardia-tomcat-logs:
|
||||||
|
guardia-qdrant: # Qdrant 벡터 데이터
|
||||||
|
guardia-gitea-data: # Gitea 저장소 + DB
|
||||||
|
guardia-gitea-config: # Gitea 설정
|
||||||
|
|
||||||
# ── 네트워크 ──────────────────────────────────────────────
|
# ── 네트워크 ──────────────────────────────────────────────
|
||||||
networks:
|
networks:
|
||||||
|
|||||||
171
docs/vector_db_guide.md
Normal file
171
docs/vector_db_guide.md
Normal file
@ -0,0 +1,171 @@
|
|||||||
|
# GUARDiA Vector DB 가이드
|
||||||
|
|
||||||
|
## 지원 방식
|
||||||
|
|
||||||
|
| 방식 | 설치 | 성능 | 용도 |
|
||||||
|
|------|------|------|------|
|
||||||
|
| **pgvector** | PostgreSQL 확장 | 중간 | SR 유사도, KB 검색 |
|
||||||
|
| **Qdrant** | Docker 컨테이너 | 높음 | 대규모 시맨틱 검색 |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 1. pgvector (기본 권장)
|
||||||
|
|
||||||
|
### 설치
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# setup.sh 실행 시 "3) PostgreSQL + pgvector" 선택
|
||||||
|
bash setup/setup_ubuntu.sh
|
||||||
|
|
||||||
|
# 또는 수동:
|
||||||
|
CREATE EXTENSION IF NOT EXISTS vector;
|
||||||
|
```
|
||||||
|
|
||||||
|
### GUARDiA에서 벡터 컬럼 사용
|
||||||
|
|
||||||
|
```python
|
||||||
|
from sqlalchemy import Column
|
||||||
|
from pgvector.sqlalchemy import Vector
|
||||||
|
|
||||||
|
class SRRequest(Base):
|
||||||
|
__tablename__ = "tb_sr_request"
|
||||||
|
# ...
|
||||||
|
embedding = Column(Vector(768)) # Ollama 임베딩 차원
|
||||||
|
|
||||||
|
# 유사 SR 검색
|
||||||
|
from sqlalchemy import text
|
||||||
|
similar = await db.execute(
|
||||||
|
text("SELECT sr_id, title, 1 - (embedding <=> :vec) AS similarity "
|
||||||
|
"FROM tb_sr_request ORDER BY embedding <=> :vec LIMIT 5"),
|
||||||
|
{"vec": query_embedding}
|
||||||
|
)
|
||||||
|
```
|
||||||
|
|
||||||
|
### 임베딩 생성 (Ollama)
|
||||||
|
|
||||||
|
```python
|
||||||
|
import httpx
|
||||||
|
|
||||||
|
async def get_embedding(text: str) -> list[float]:
|
||||||
|
async with httpx.AsyncClient() as client:
|
||||||
|
r = await client.post("http://localhost:11434/api/embeddings", json={
|
||||||
|
"model": "nomic-embed-text", # 경량 임베딩 모델
|
||||||
|
"prompt": text
|
||||||
|
})
|
||||||
|
return r.json()["embedding"]
|
||||||
|
```
|
||||||
|
|
||||||
|
### 환경변수
|
||||||
|
|
||||||
|
```
|
||||||
|
ENABLE_VECTOR=true
|
||||||
|
DATABASE_URL=postgresql+asyncpg://guardia:guardia@localhost:5432/guardia
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 2. Qdrant (고성능 벡터 검색)
|
||||||
|
|
||||||
|
### Docker 시작
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# vector 프로파일로 Qdrant 포함 실행
|
||||||
|
docker compose --profile vector up -d qdrant
|
||||||
|
|
||||||
|
# 또는 docker compose.yml에서 profiles 제거 후 항상 실행
|
||||||
|
```
|
||||||
|
|
||||||
|
### GUARDiA에서 Qdrant 사용
|
||||||
|
|
||||||
|
```python
|
||||||
|
from qdrant_client import QdrantClient
|
||||||
|
from qdrant_client.models import Distance, VectorParams, PointStruct
|
||||||
|
|
||||||
|
client = QdrantClient(url=os.getenv("QDRANT_URL", "http://localhost:6333"))
|
||||||
|
|
||||||
|
# 컬렉션 생성
|
||||||
|
client.create_collection(
|
||||||
|
collection_name="sr_embeddings",
|
||||||
|
vectors_config=VectorParams(size=768, distance=Distance.COSINE),
|
||||||
|
)
|
||||||
|
|
||||||
|
# SR 인덱싱
|
||||||
|
client.upsert("sr_embeddings", points=[
|
||||||
|
PointStruct(id=sr.id, vector=embedding, payload={"sr_id": sr.sr_id, "title": sr.title})
|
||||||
|
])
|
||||||
|
|
||||||
|
# 유사 SR 검색
|
||||||
|
results = client.search("sr_embeddings", query_vector=query_embedding, limit=5)
|
||||||
|
```
|
||||||
|
|
||||||
|
### 환경변수
|
||||||
|
|
||||||
|
```
|
||||||
|
QDRANT_URL=http://localhost:6333
|
||||||
|
ENABLE_QDRANT=true
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 3. 사용 시나리오
|
||||||
|
|
||||||
|
### KB 시맨틱 검색
|
||||||
|
|
||||||
|
```python
|
||||||
|
# KB 문서 검색 — 키워드가 아닌 의미 기반
|
||||||
|
async def search_kb_semantic(query: str, db) -> list:
|
||||||
|
embedding = await get_embedding(query)
|
||||||
|
results = await db.execute(
|
||||||
|
text("SELECT id, title, content, 1-(embedding<=>:v) score "
|
||||||
|
"FROM tb_kb WHERE embedding IS NOT NULL "
|
||||||
|
"ORDER BY embedding <=> :v LIMIT 10"),
|
||||||
|
{"v": str(embedding)}
|
||||||
|
)
|
||||||
|
return results.mappings().all()
|
||||||
|
```
|
||||||
|
|
||||||
|
### 유사 SR 재발 탐지
|
||||||
|
|
||||||
|
```python
|
||||||
|
# 새 SR과 유사한 과거 SR 찾기 (학습 루프 연동)
|
||||||
|
async def find_similar_sr(new_sr: SRRequest, db) -> list:
|
||||||
|
embedding = await get_embedding(f"{new_sr.title} {new_sr.description}")
|
||||||
|
results = await db.execute(
|
||||||
|
text("SELECT sr_id, title, status, "
|
||||||
|
"1-(embedding<=>:v) AS similarity "
|
||||||
|
"FROM tb_sr_request "
|
||||||
|
"WHERE embedding IS NOT NULL AND sr_id != :sid "
|
||||||
|
" AND 1-(embedding<=>:v) > 0.85 " # 85% 이상 유사도
|
||||||
|
"ORDER BY similarity DESC LIMIT 5"),
|
||||||
|
{"v": str(embedding), "sid": new_sr.sr_id}
|
||||||
|
)
|
||||||
|
return results.mappings().all()
|
||||||
|
```
|
||||||
|
|
||||||
|
### Ollama 임베딩 모델 설치
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# nomic-embed-text: 경량 임베딩 모델 (274MB)
|
||||||
|
ollama pull nomic-embed-text
|
||||||
|
|
||||||
|
# 또는 한국어 특화
|
||||||
|
ollama pull bge-m3
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 4. Docker Compose 프로파일
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# 기본 (pgvector만)
|
||||||
|
docker compose up -d
|
||||||
|
|
||||||
|
# Qdrant 포함
|
||||||
|
docker compose --profile vector up -d
|
||||||
|
|
||||||
|
# Gitea 포함
|
||||||
|
docker compose up -d gitea
|
||||||
|
|
||||||
|
# 모두 실행
|
||||||
|
docker compose --profile vector up -d
|
||||||
|
```
|
||||||
225
setup/gitea_init.sh
Normal file
225
setup/gitea_init.sh
Normal file
@ -0,0 +1,225 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
# ==============================================================
|
||||||
|
# GUARDiA Gitea 초기화 스크립트
|
||||||
|
# ==============================================================
|
||||||
|
# Gitea 컨테이너/서비스가 기동된 후 실행하여
|
||||||
|
# 관리자 계정, 조직, 저장소, 브랜치 보호를 자동 설정합니다.
|
||||||
|
#
|
||||||
|
# 사용법:
|
||||||
|
# bash setup/gitea_init.sh
|
||||||
|
# GITEA_PORT=3000 GITEA_ADMIN=admin bash setup/gitea_init.sh
|
||||||
|
# ==============================================================
|
||||||
|
|
||||||
|
set -euo pipefail
|
||||||
|
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||||
|
GUARDIA_ROOT="$(dirname "$SCRIPT_DIR")"
|
||||||
|
|
||||||
|
GITEA_PORT="${GITEA_PORT:-3000}"
|
||||||
|
GITEA_BASE="http://localhost:${GITEA_PORT}"
|
||||||
|
GITEA_ADMIN="${GITEA_ADMIN:-gitadmin}"
|
||||||
|
GITEA_ADMIN_PW="${GITEA_ADMIN_PW:-Gitea@guardia!}"
|
||||||
|
GITEA_ADMIN_EMAIL="${GITEA_ADMIN_EMAIL:-admin@guardia.local}"
|
||||||
|
GITEA_ORG="${GITEA_ORG:-guardia}"
|
||||||
|
GITEA_REPO="${GITEA_REPO:-GUARDiA}"
|
||||||
|
|
||||||
|
GREEN='\033[0;32m'; YELLOW='\033[1;33m'; NC='\033[0m'
|
||||||
|
ok() { echo -e "${GREEN}[OK]${NC} $*"; }
|
||||||
|
warn() { echo -e "${YELLOW}[WARN]${NC} $*"; }
|
||||||
|
info() { echo -e " $*"; }
|
||||||
|
|
||||||
|
API="${GITEA_BASE}/api/v1"
|
||||||
|
AUTH="Authorization: Basic $(echo -n "${GITEA_ADMIN}:${GITEA_ADMIN_PW}" | base64)"
|
||||||
|
|
||||||
|
echo "=================================================="
|
||||||
|
echo " Gitea 초기화"
|
||||||
|
echo " URL: $GITEA_BASE"
|
||||||
|
echo "=================================================="
|
||||||
|
|
||||||
|
# ── Gitea 응답 대기 ───────────────────────────────────────────
|
||||||
|
echo ""
|
||||||
|
echo "[1/7] Gitea 서비스 응답 대기..."
|
||||||
|
for i in $(seq 1 30); do
|
||||||
|
if curl -sf "$API/version" -o /dev/null 2>/dev/null; then
|
||||||
|
ok "Gitea 응답 확인"
|
||||||
|
break
|
||||||
|
fi
|
||||||
|
sleep 2
|
||||||
|
[[ $i -eq 30 ]] && { echo "[ERR] Gitea 응답 없음 — 서비스를 먼저 시작하세요"; exit 1; }
|
||||||
|
done
|
||||||
|
|
||||||
|
# ── 관리자 계정 생성 (Gitea API 인증용) ─────────────────────────
|
||||||
|
echo ""
|
||||||
|
echo "[2/7] 관리자 계정 설정..."
|
||||||
|
|
||||||
|
# Docker 환경: gitea admin 명령 사용
|
||||||
|
if command -v docker &>/dev/null && docker ps | grep -q guardia-gitea; then
|
||||||
|
docker exec guardia-gitea gitea admin user create \
|
||||||
|
--username "$GITEA_ADMIN" \
|
||||||
|
--password "$GITEA_ADMIN_PW" \
|
||||||
|
--email "$GITEA_ADMIN_EMAIL" \
|
||||||
|
--admin 2>/dev/null || info "관리자 이미 존재"
|
||||||
|
elif command -v gitea &>/dev/null; then
|
||||||
|
gitea admin user create \
|
||||||
|
--username "$GITEA_ADMIN" \
|
||||||
|
--password "$GITEA_ADMIN_PW" \
|
||||||
|
--email "$GITEA_ADMIN_EMAIL" \
|
||||||
|
--admin 2>/dev/null || info "관리자 이미 존재"
|
||||||
|
else
|
||||||
|
# API로 관리자 생성 시도 (초기 설치 시)
|
||||||
|
curl -sf -X POST "$API/users" \
|
||||||
|
-H "Content-Type: application/json" \
|
||||||
|
-d "{\"username\":\"${GITEA_ADMIN}\",\"password\":\"${GITEA_ADMIN_PW}\",\"email\":\"${GITEA_ADMIN_EMAIL}\"}" \
|
||||||
|
-o /dev/null 2>/dev/null || info "관리자 이미 존재"
|
||||||
|
fi
|
||||||
|
ok "관리자: $GITEA_ADMIN"
|
||||||
|
|
||||||
|
# ── 조직 생성 ────────────────────────────────────────────────
|
||||||
|
echo ""
|
||||||
|
echo "[3/7] 조직 생성..."
|
||||||
|
curl -sf -X POST "$API/orgs" \
|
||||||
|
-H "$AUTH" -H "Content-Type: application/json" \
|
||||||
|
-d "{\"username\":\"${GITEA_ORG}\",\"visibility\":\"private\",\"description\":\"GUARDiA ITSM Organization\"}" \
|
||||||
|
-o /dev/null 2>/dev/null && ok "조직 생성: $GITEA_ORG" || info "조직 이미 존재: $GITEA_ORG"
|
||||||
|
|
||||||
|
# ── GUARDiA 저장소 생성 ──────────────────────────────────────
|
||||||
|
echo ""
|
||||||
|
echo "[4/7] 저장소 생성..."
|
||||||
|
curl -sf -X POST "$API/orgs/${GITEA_ORG}/repos" \
|
||||||
|
-H "$AUTH" -H "Content-Type: application/json" \
|
||||||
|
-d "{
|
||||||
|
\"name\":\"${GITEA_REPO}\",
|
||||||
|
\"description\":\"GUARDiA ITSM Platform\",
|
||||||
|
\"private\":true,
|
||||||
|
\"default_branch\":\"main\",
|
||||||
|
\"auto_init\":true,
|
||||||
|
\"readme\":\"Default\"
|
||||||
|
}" \
|
||||||
|
-o /dev/null 2>/dev/null && ok "저장소 생성: ${GITEA_ORG}/${GITEA_REPO}" \
|
||||||
|
|| info "저장소 이미 존재"
|
||||||
|
|
||||||
|
sleep 3 # 저장소 초기화 대기
|
||||||
|
|
||||||
|
# ── develop 브랜치 생성 ─────────────────────────────────────
|
||||||
|
echo ""
|
||||||
|
echo "[5/7] develop 브랜치 생성..."
|
||||||
|
curl -sf -X POST "$API/repos/${GITEA_ORG}/${GITEA_REPO}/branches" \
|
||||||
|
-H "$AUTH" -H "Content-Type: application/json" \
|
||||||
|
-d '{"new_branch_name":"develop","old_branch_name":"main"}' \
|
||||||
|
-o /dev/null 2>/dev/null && ok "develop 브랜치 생성" || info "develop 브랜치 이미 존재"
|
||||||
|
|
||||||
|
# ── main 브랜치 보호 설정 ────────────────────────────────────
|
||||||
|
echo ""
|
||||||
|
echo "[6/7] main 브랜치 보호 설정..."
|
||||||
|
curl -sf -X POST "$API/repos/${GITEA_ORG}/${GITEA_REPO}/branch_protections" \
|
||||||
|
-H "$AUTH" -H "Content-Type: application/json" \
|
||||||
|
-d "{
|
||||||
|
\"branch_name\": \"main\",
|
||||||
|
\"enable_push\": false,
|
||||||
|
\"enable_push_whitelist\": true,
|
||||||
|
\"push_whitelist_usernames\": [\"${GITEA_ADMIN}\"],
|
||||||
|
\"required_approvals\": 1,
|
||||||
|
\"enable_approvals_whitelist\": false,
|
||||||
|
\"block_on_rejected_reviews\": true,
|
||||||
|
\"block_on_official_review_requests\": false,
|
||||||
|
\"dismiss_stale_approvals\": true
|
||||||
|
}" \
|
||||||
|
-o /dev/null 2>/dev/null \
|
||||||
|
&& ok "main 브랜치 보호: PR 필수, 리뷰 1명 필수" \
|
||||||
|
|| info "브랜치 보호 이미 설정됨"
|
||||||
|
|
||||||
|
# develop 브랜치 보호 (push 허용, but 직접 force-push 금지)
|
||||||
|
curl -sf -X POST "$API/repos/${GITEA_ORG}/${GITEA_REPO}/branch_protections" \
|
||||||
|
-H "$AUTH" -H "Content-Type: application/json" \
|
||||||
|
-d '{"branch_name":"develop","enable_force_push":false}' \
|
||||||
|
-o /dev/null 2>/dev/null || true
|
||||||
|
|
||||||
|
# ── 개발자 계정 + 개인 브랜치 생성 ──────────────────────────
|
||||||
|
echo ""
|
||||||
|
echo "[7/7] 개발자 계정 + 개인 브랜치 생성..."
|
||||||
|
|
||||||
|
# GUARDiA ITSM 역할에 맞는 기본 개발자 계정
|
||||||
|
declare -A USERS=(
|
||||||
|
["engineer1"]="Eng1@guardia!:engineer1@guardia.local"
|
||||||
|
["engineer2"]="Eng2@guardia!:engineer2@guardia.local"
|
||||||
|
["pm1"]="PM1@guardia!:pm1@guardia.local"
|
||||||
|
["admin"]="Admin@guardia!:admin@guardia.local"
|
||||||
|
)
|
||||||
|
|
||||||
|
# Developers 팀 생성
|
||||||
|
TEAM_RESP=$(curl -sf -X POST "$API/orgs/${GITEA_ORG}/teams" \
|
||||||
|
-H "$AUTH" -H "Content-Type: application/json" \
|
||||||
|
-d "{
|
||||||
|
\"name\":\"Developers\",
|
||||||
|
\"permission\":\"write\",
|
||||||
|
\"units\":[\"repo.code\",\"repo.issues\",\"repo.pulls\"],
|
||||||
|
\"includes_all_repositories\":true
|
||||||
|
}" 2>/dev/null)
|
||||||
|
TEAM_ID=$(echo "$TEAM_RESP" | python3 -c "import sys,json; print(json.load(sys.stdin).get('id',''))" 2>/dev/null || echo "")
|
||||||
|
[[ -n "$TEAM_ID" ]] && ok "Developers 팀 생성 (ID: $TEAM_ID)"
|
||||||
|
|
||||||
|
# 팀에 저장소 추가
|
||||||
|
[[ -n "$TEAM_ID" ]] && curl -sf -X PUT "$API/teams/${TEAM_ID}/repos/${GITEA_ORG}/${GITEA_REPO}" \
|
||||||
|
-H "$AUTH" -o /dev/null 2>/dev/null
|
||||||
|
|
||||||
|
for username in "${!USERS[@]}"; do
|
||||||
|
IFS=':' read -r pw email <<< "${USERS[$username]}"
|
||||||
|
|
||||||
|
# 사용자 생성
|
||||||
|
curl -sf -X POST "$API/admin/users" \
|
||||||
|
-H "$AUTH" -H "Content-Type: application/json" \
|
||||||
|
-d "{
|
||||||
|
\"username\":\"${username}\",
|
||||||
|
\"password\":\"${pw}\",
|
||||||
|
\"email\":\"${email}\",
|
||||||
|
\"login_name\":\"${username}\",
|
||||||
|
\"source_id\":0,
|
||||||
|
\"send_notify\":false,
|
||||||
|
\"must_change_password\":false
|
||||||
|
}" -o /dev/null 2>/dev/null || info "사용자 이미 존재: $username"
|
||||||
|
|
||||||
|
# 팀 멤버 추가
|
||||||
|
[[ -n "$TEAM_ID" ]] && curl -sf -X PUT "$API/teams/${TEAM_ID}/members/${username}" \
|
||||||
|
-H "$AUTH" -o /dev/null 2>/dev/null
|
||||||
|
|
||||||
|
# 개인 feature 브랜치 생성 (feature/이름/init)
|
||||||
|
curl -sf -X POST "$API/repos/${GITEA_ORG}/${GITEA_REPO}/branches" \
|
||||||
|
-H "$AUTH" -H "Content-Type: application/json" \
|
||||||
|
-d "{\"new_branch_name\":\"feature/${username}/init\",\"old_branch_name\":\"develop\"}" \
|
||||||
|
-o /dev/null 2>/dev/null
|
||||||
|
|
||||||
|
ok "계정 + 브랜치: $username (feature/${username}/init)"
|
||||||
|
done
|
||||||
|
|
||||||
|
# ── 현재 소스 push ───────────────────────────────────────────
|
||||||
|
echo ""
|
||||||
|
echo " 현재 GUARDiA 소스를 Gitea에 push..."
|
||||||
|
GITEA_URL="http://${GITEA_ADMIN}:${GITEA_ADMIN_PW}@localhost:${GITEA_PORT}/${GITEA_ORG}/${GITEA_REPO}.git"
|
||||||
|
git -C "$GUARDIA_ROOT" remote remove gitea 2>/dev/null || true
|
||||||
|
git -C "$GUARDIA_ROOT" remote add gitea "$GITEA_URL"
|
||||||
|
git -C "$GUARDIA_ROOT" push gitea main 2>/dev/null \
|
||||||
|
&& ok "main 브랜치 push 완료" || warn "main push 실패 — 수동: git push gitea main"
|
||||||
|
git -C "$GUARDIA_ROOT" push gitea develop 2>/dev/null || true
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
echo "=================================================="
|
||||||
|
ok "Gitea 초기화 완료!"
|
||||||
|
echo ""
|
||||||
|
info "Gitea URL: http://localhost:${GITEA_PORT}"
|
||||||
|
info "관리자: ${GITEA_ADMIN} / ${GITEA_ADMIN_PW}"
|
||||||
|
info "저장소: http://localhost:${GITEA_PORT}/${GITEA_ORG}/${GITEA_REPO}"
|
||||||
|
echo ""
|
||||||
|
info "=== 브랜치 전략 ==="
|
||||||
|
info " main : PR + 리뷰 1명 필수 (보호)"
|
||||||
|
info " develop : 통합 브랜치 (force-push 금지)"
|
||||||
|
info " feature/이름/기능명: 개인 개발 브랜치"
|
||||||
|
echo ""
|
||||||
|
info "=== 개발자 계정 ==="
|
||||||
|
for username in "${!USERS[@]}"; do
|
||||||
|
IFS=':' read -r pw _ <<< "${USERS[$username]}"
|
||||||
|
info " $username / $pw → feature/${username}/init"
|
||||||
|
done
|
||||||
|
echo ""
|
||||||
|
info "=== Git 원격 저장소 설정 ==="
|
||||||
|
info " git remote add guardia-gitea http://localhost:${GITEA_PORT}/${GITEA_ORG}/${GITEA_REPO}.git"
|
||||||
|
info " git push guardia-gitea feature/이름/기능명"
|
||||||
|
echo "=================================================="
|
||||||
179
setup/lib/db_select.sh
Normal file
179
setup/lib/db_select.sh
Normal file
@ -0,0 +1,179 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
# ==============================================================
|
||||||
|
# GUARDiA DB 선택 공통 함수
|
||||||
|
# ==============================================================
|
||||||
|
# 사용법: source setup/lib/db_select.sh; select_database
|
||||||
|
# 결과: DB_TYPE, DATABASE_URL, INSTALL_POSTGRES, INSTALL_PGVECTOR 변수 설정
|
||||||
|
# ==============================================================
|
||||||
|
|
||||||
|
select_database() {
|
||||||
|
# 환경변수로 이미 지정된 경우 프롬프트 생략
|
||||||
|
if [[ -n "${DB_TYPE:-}" ]]; then
|
||||||
|
info "DB_TYPE=$DB_TYPE (환경변수 설정됨)"
|
||||||
|
_apply_db_type "$DB_TYPE"
|
||||||
|
return
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
echo "============================================================"
|
||||||
|
echo " 데이터베이스 선택"
|
||||||
|
echo "============================================================"
|
||||||
|
echo " 1) SQLite (개발·단독 실행 권장, 추가 설치 없음)"
|
||||||
|
echo " 2) PostgreSQL (운영 환경 권장)"
|
||||||
|
echo " 3) PostgreSQL + pgvector (AI 벡터 검색 포함, 권장)"
|
||||||
|
echo ""
|
||||||
|
echo " pgvector: SR 유사도 검색, KB 시맨틱 검색, 이상 탐지에 활용"
|
||||||
|
echo " Qdrant: docker-compose에 선택적 포함 (고성능 벡터 검색)"
|
||||||
|
echo "============================================================"
|
||||||
|
|
||||||
|
local choice
|
||||||
|
read -rp " 선택 [1-3, 기본값=3]: " choice
|
||||||
|
choice="${choice:-3}"
|
||||||
|
|
||||||
|
case "$choice" in
|
||||||
|
1) DB_TYPE="sqlite" ;;
|
||||||
|
2) DB_TYPE="postgres" ;;
|
||||||
|
3) DB_TYPE="postgres-vector";;
|
||||||
|
*) warn "잘못된 선택 '$choice' — 기본값 3(postgres-vector) 사용"
|
||||||
|
DB_TYPE="postgres-vector" ;;
|
||||||
|
esac
|
||||||
|
|
||||||
|
_apply_db_type "$DB_TYPE"
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
_apply_db_type() {
|
||||||
|
local dtype="$1"
|
||||||
|
|
||||||
|
case "$dtype" in
|
||||||
|
sqlite)
|
||||||
|
DATABASE_URL="sqlite+aiosqlite:///./guardia_itsm.db"
|
||||||
|
INSTALL_POSTGRES=false
|
||||||
|
INSTALL_PGVECTOR=false
|
||||||
|
INSTALL_QDRANT=false
|
||||||
|
ok "DB: SQLite (개발 모드)"
|
||||||
|
;;
|
||||||
|
|
||||||
|
postgres)
|
||||||
|
DATABASE_URL="postgresql+asyncpg://guardia:guardia@localhost:5432/guardia"
|
||||||
|
INSTALL_POSTGRES=true
|
||||||
|
INSTALL_PGVECTOR=false
|
||||||
|
INSTALL_QDRANT=false
|
||||||
|
ok "DB: PostgreSQL"
|
||||||
|
;;
|
||||||
|
|
||||||
|
postgres-vector)
|
||||||
|
DATABASE_URL="postgresql+asyncpg://guardia:guardia@localhost:5432/guardia"
|
||||||
|
INSTALL_POSTGRES=true
|
||||||
|
INSTALL_PGVECTOR=true
|
||||||
|
INSTALL_QDRANT=false # Qdrant는 docker-compose로 별도 제공
|
||||||
|
ok "DB: PostgreSQL + pgvector (AI 벡터 검색 포함)"
|
||||||
|
;;
|
||||||
|
|
||||||
|
*)
|
||||||
|
warn "알 수 없는 DB_TYPE='$dtype' — SQLite로 대체"
|
||||||
|
DATABASE_URL="sqlite+aiosqlite:///./guardia_itsm.db"
|
||||||
|
INSTALL_POSTGRES=false
|
||||||
|
INSTALL_PGVECTOR=false
|
||||||
|
INSTALL_QDRANT=false
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
|
||||||
|
export DB_TYPE DATABASE_URL INSTALL_POSTGRES INSTALL_PGVECTOR INSTALL_QDRANT
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
setup_postgres() {
|
||||||
|
local pgpw="${POSTGRES_PASSWORD:-guardia}"
|
||||||
|
|
||||||
|
[[ "$INSTALL_POSTGRES" != "true" ]] && return
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
info "PostgreSQL 설정..."
|
||||||
|
|
||||||
|
# 배포 환경별 초기화 (호출 스크립트에서 처리됨)
|
||||||
|
# 여기서는 DB/사용자 생성만 담당
|
||||||
|
|
||||||
|
# 비밀번호 안전 저장
|
||||||
|
sudo -u postgres psql \
|
||||||
|
-tc "SELECT 1 FROM pg_user WHERE usename='guardia'" 2>/dev/null \
|
||||||
|
| grep -q 1 \
|
||||||
|
|| sudo -u postgres psql -c \
|
||||||
|
"CREATE USER guardia WITH PASSWORD '${pgpw}';" 2>/dev/null
|
||||||
|
|
||||||
|
sudo -u postgres psql \
|
||||||
|
-tc "SELECT 1 FROM pg_database WHERE datname='guardia'" 2>/dev/null \
|
||||||
|
| grep -q 1 \
|
||||||
|
|| sudo -u postgres psql -c \
|
||||||
|
"CREATE DATABASE guardia OWNER guardia;" 2>/dev/null
|
||||||
|
|
||||||
|
ok "PostgreSQL DB/사용자 생성 완료"
|
||||||
|
|
||||||
|
# pgvector 확장 설치
|
||||||
|
if [[ "$INSTALL_PGVECTOR" == "true" ]]; then
|
||||||
|
_install_pgvector "$pgpw"
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
_install_pgvector() {
|
||||||
|
local pgpw="$1"
|
||||||
|
echo ""
|
||||||
|
info "pgvector 확장 설치..."
|
||||||
|
|
||||||
|
# OS별 패키지 설치
|
||||||
|
if command -v apt-get &>/dev/null; then
|
||||||
|
apt-get install -y -qq postgresql-server-dev-all build-essential git 2>/dev/null || true
|
||||||
|
# pgvector 소스 빌드 (패키지 없는 경우)
|
||||||
|
if ! dpkg -l postgresql-15-pgvector &>/dev/null 2>&1; then
|
||||||
|
_build_pgvector_from_source
|
||||||
|
else
|
||||||
|
apt-get install -y -qq "postgresql-$(pg_lsclusters | grep online | awk '{print $1}' | head -1)-pgvector" 2>/dev/null \
|
||||||
|
|| _build_pgvector_from_source
|
||||||
|
fi
|
||||||
|
elif command -v dnf &>/dev/null; then
|
||||||
|
dnf install -y pgvector_15 2>/dev/null \
|
||||||
|
|| _build_pgvector_from_source
|
||||||
|
else
|
||||||
|
_build_pgvector_from_source
|
||||||
|
fi
|
||||||
|
|
||||||
|
# 확장 활성화
|
||||||
|
sudo -u postgres psql -d guardia -c "CREATE EXTENSION IF NOT EXISTS vector;" 2>/dev/null \
|
||||||
|
&& ok "pgvector 확장 활성화 완료" \
|
||||||
|
|| warn "pgvector 활성화 실패 — 나중에 수동: CREATE EXTENSION vector;"
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
_build_pgvector_from_source() {
|
||||||
|
local tmpdir
|
||||||
|
tmpdir=$(mktemp -d)
|
||||||
|
info "pgvector 소스 빌드..."
|
||||||
|
git clone --depth 1 https://github.com/pgvector/pgvector.git "$tmpdir" 2>/dev/null \
|
||||||
|
|| { warn "pgvector 소스 클론 실패 — 인터넷 연결 확인"; return; }
|
||||||
|
cd "$tmpdir"
|
||||||
|
make 2>/dev/null
|
||||||
|
make install 2>/dev/null
|
||||||
|
cd -
|
||||||
|
rm -rf "$tmpdir"
|
||||||
|
ok "pgvector 빌드 완료"
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
write_db_env() {
|
||||||
|
local env_file="$1"
|
||||||
|
# .env 파일에 DATABASE_URL 기록
|
||||||
|
if grep -q "^DATABASE_URL=" "$env_file" 2>/dev/null; then
|
||||||
|
sed -i "s|^DATABASE_URL=.*|DATABASE_URL=${DATABASE_URL}|" "$env_file"
|
||||||
|
else
|
||||||
|
echo "DATABASE_URL=${DATABASE_URL}" >> "$env_file"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# pgvector 설정 추가
|
||||||
|
if [[ "$INSTALL_PGVECTOR" == "true" ]]; then
|
||||||
|
grep -q "^ENABLE_VECTOR=" "$env_file" 2>/dev/null \
|
||||||
|
|| echo "ENABLE_VECTOR=true" >> "$env_file"
|
||||||
|
fi
|
||||||
|
|
||||||
|
ok ".env DATABASE_URL 기록 완료: $DATABASE_URL"
|
||||||
|
}
|
||||||
298
setup/lib/gitea_setup.sh
Normal file
298
setup/lib/gitea_setup.sh
Normal file
@ -0,0 +1,298 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
# ==============================================================
|
||||||
|
# GUARDiA Gitea 설치 + 자동 초기화 공통 함수
|
||||||
|
# ==============================================================
|
||||||
|
# 기능:
|
||||||
|
# - Gitea 바이너리 설치 및 systemd 등록
|
||||||
|
# - 관리자 계정 자동 생성
|
||||||
|
# - GUARDiA 조직 + 레파지토리 생성
|
||||||
|
# - 개발자 계정 + 개인 브랜치 자동 생성
|
||||||
|
# - main 브랜치 보호 (PR 필수)
|
||||||
|
#
|
||||||
|
# 브랜치 전략:
|
||||||
|
# main - 보호 브랜치, PR + 리뷰 1명 필수
|
||||||
|
# develop - 통합 브랜치
|
||||||
|
# feature/이름/기능명 - 개인 개발 브랜치
|
||||||
|
# ==============================================================
|
||||||
|
|
||||||
|
GITEA_VER="${GITEA_VER:-1.21.11}"
|
||||||
|
GITEA_HOME="/opt/gitea"
|
||||||
|
GITEA_DATA="/var/lib/gitea"
|
||||||
|
GITEA_PORT="${GITEA_PORT:-3000}"
|
||||||
|
GITEA_ADMIN="${GITEA_ADMIN:-gitadmin}"
|
||||||
|
GITEA_ADMIN_PW="${GITEA_ADMIN_PW:-Gitea@guardia!}"
|
||||||
|
GITEA_ADMIN_EMAIL="${GITEA_ADMIN_EMAIL:-admin@guardia.local}"
|
||||||
|
GITEA_ORG="${GITEA_ORG:-guardia}"
|
||||||
|
GITEA_REPO="${GITEA_REPO:-GUARDiA}"
|
||||||
|
GITEA_BASE="http://localhost:${GITEA_PORT}"
|
||||||
|
GITEA_MIRROR="${GITEA_MIRROR:-https://dl.gitea.com/gitea/${GITEA_VER}}"
|
||||||
|
|
||||||
|
|
||||||
|
install_gitea() {
|
||||||
|
echo ""
|
||||||
|
echo "=== Gitea 설치 (포트 ${GITEA_PORT}) ==="
|
||||||
|
|
||||||
|
# 1. 시스템 계정 생성
|
||||||
|
id git &>/dev/null || useradd -r -s /bin/bash -d "$GITEA_DATA" git
|
||||||
|
|
||||||
|
# 2. 디렉토리 생성
|
||||||
|
mkdir -p "$GITEA_HOME" "$GITEA_DATA"/{custom,data,log,repositories}
|
||||||
|
chown -R git:git "$GITEA_DATA"
|
||||||
|
|
||||||
|
# 3. 바이너리 설치
|
||||||
|
ARCH="$(uname -m)"
|
||||||
|
case "$ARCH" in
|
||||||
|
x86_64) GITEA_ARCH="amd64" ;;
|
||||||
|
aarch64) GITEA_ARCH="arm64" ;;
|
||||||
|
*) GITEA_ARCH="amd64" ;;
|
||||||
|
esac
|
||||||
|
|
||||||
|
GITEA_BIN="gitea-${GITEA_VER}-linux-${GITEA_ARCH}"
|
||||||
|
|
||||||
|
if [[ ! -f "/usr/local/bin/gitea" ]]; then
|
||||||
|
info "Gitea $GITEA_VER 다운로드..."
|
||||||
|
wget -q "${GITEA_MIRROR}/${GITEA_BIN}" -O /tmp/gitea \
|
||||||
|
|| fail "Gitea 다운로드 실패 — GITEA_MIRROR 환경변수 설정"
|
||||||
|
install -m 755 /tmp/gitea /usr/local/bin/gitea
|
||||||
|
ok "Gitea 바이너리 설치: /usr/local/bin/gitea"
|
||||||
|
else
|
||||||
|
info "Gitea 이미 설치됨"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# 4. Gitea 설정 파일
|
||||||
|
mkdir -p "$GITEA_DATA/custom/conf"
|
||||||
|
cat > "$GITEA_DATA/custom/conf/app.ini" << APPINI
|
||||||
|
[DEFAULT]
|
||||||
|
RUN_USER = git
|
||||||
|
RUN_MODE = prod
|
||||||
|
|
||||||
|
[server]
|
||||||
|
HTTP_ADDR = 0.0.0.0
|
||||||
|
HTTP_PORT = ${GITEA_PORT}
|
||||||
|
ROOT_URL = http://localhost:${GITEA_PORT}/
|
||||||
|
DISABLE_SSH = false
|
||||||
|
SSH_PORT = 22022
|
||||||
|
|
||||||
|
[database]
|
||||||
|
DB_TYPE = sqlite3
|
||||||
|
PATH = ${GITEA_DATA}/data/gitea.db
|
||||||
|
|
||||||
|
[repository]
|
||||||
|
ROOT = ${GITEA_DATA}/repositories
|
||||||
|
|
||||||
|
[log]
|
||||||
|
ROOT_PATH = ${GITEA_DATA}/log
|
||||||
|
MODE = file
|
||||||
|
LEVEL = info
|
||||||
|
|
||||||
|
[security]
|
||||||
|
INSTALL_LOCK = true
|
||||||
|
SECRET_KEY = $(openssl rand -hex 32 2>/dev/null || date | md5sum | head -c 32)
|
||||||
|
INTERNAL_TOKEN = $(openssl rand -hex 32 2>/dev/null || date +%s | md5sum | head -c 32)
|
||||||
|
|
||||||
|
[service]
|
||||||
|
DISABLE_REGISTRATION = false
|
||||||
|
REQUIRE_SIGNIN_VIEW = false
|
||||||
|
DEFAULT_KEEP_EMAIL_PRIVATE = true
|
||||||
|
|
||||||
|
[git]
|
||||||
|
DEFAULT_BRANCH = main
|
||||||
|
APPINI
|
||||||
|
|
||||||
|
chown -R git:git "$GITEA_DATA"
|
||||||
|
|
||||||
|
# 5. systemd 서비스
|
||||||
|
cat > /etc/systemd/system/gitea.service << GITSVC
|
||||||
|
[Unit]
|
||||||
|
Description=Gitea (Git service)
|
||||||
|
After=network.target
|
||||||
|
|
||||||
|
[Service]
|
||||||
|
Type=simple
|
||||||
|
User=git
|
||||||
|
Group=git
|
||||||
|
WorkingDirectory=${GITEA_DATA}
|
||||||
|
Environment="USER=git" "HOME=${GITEA_DATA}" "GITEA_WORK_DIR=${GITEA_DATA}"
|
||||||
|
ExecStart=/usr/local/bin/gitea web --config ${GITEA_DATA}/custom/conf/app.ini
|
||||||
|
Restart=always
|
||||||
|
RestartSec=3
|
||||||
|
|
||||||
|
[Install]
|
||||||
|
WantedBy=multi-user.target
|
||||||
|
GITSVC
|
||||||
|
|
||||||
|
systemctl daemon-reload
|
||||||
|
systemctl enable gitea
|
||||||
|
systemctl start gitea
|
||||||
|
|
||||||
|
# Gitea 기동 대기
|
||||||
|
info "Gitea 기동 대기 중..."
|
||||||
|
local attempt=0
|
||||||
|
until curl -sf "http://localhost:${GITEA_PORT}/api/v1/version" -o /dev/null 2>/dev/null; do
|
||||||
|
sleep 3
|
||||||
|
((attempt++))
|
||||||
|
[[ $attempt -ge 20 ]] && { warn "Gitea 기동 타임아웃"; break; }
|
||||||
|
done
|
||||||
|
ok "Gitea 서비스 시작 완료 (http://localhost:${GITEA_PORT})"
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
init_gitea_repos() {
|
||||||
|
echo ""
|
||||||
|
echo "=== Gitea 초기화 (조직·저장소·브랜치) ==="
|
||||||
|
|
||||||
|
local api="${GITEA_BASE}/api/v1"
|
||||||
|
local auth_header="Authorization: Basic $(echo -n "${GITEA_ADMIN}:${GITEA_ADMIN_PW}" | base64)"
|
||||||
|
|
||||||
|
# 1. 관리자 계정 생성
|
||||||
|
_wait_gitea
|
||||||
|
|
||||||
|
gitea admin user create \
|
||||||
|
--username "$GITEA_ADMIN" \
|
||||||
|
--password "$GITEA_ADMIN_PW" \
|
||||||
|
--email "$GITEA_ADMIN_EMAIL" \
|
||||||
|
--admin \
|
||||||
|
--config "$GITEA_DATA/custom/conf/app.ini" \
|
||||||
|
2>/dev/null || info "관리자 계정 이미 존재"
|
||||||
|
|
||||||
|
ok "관리자 계정: $GITEA_ADMIN"
|
||||||
|
|
||||||
|
# 2. 조직 생성
|
||||||
|
curl -sf -X POST "$api/orgs" \
|
||||||
|
-H "$auth_header" \
|
||||||
|
-H "Content-Type: application/json" \
|
||||||
|
-d "{\"username\":\"${GITEA_ORG}\",\"visibility\":\"private\",\"description\":\"GUARDiA ITSM\"}" \
|
||||||
|
-o /dev/null 2>/dev/null \
|
||||||
|
|| info "조직 이미 존재: $GITEA_ORG"
|
||||||
|
ok "조직 생성: $GITEA_ORG"
|
||||||
|
|
||||||
|
# 3. GUARDiA 메인 저장소 생성
|
||||||
|
curl -sf -X POST "$api/orgs/${GITEA_ORG}/repos" \
|
||||||
|
-H "$auth_header" \
|
||||||
|
-H "Content-Type: application/json" \
|
||||||
|
-d "{\"name\":\"${GITEA_REPO}\",\"description\":\"GUARDiA ITSM 플랫폼\",\"private\":true,\"default_branch\":\"main\",\"auto_init\":true}" \
|
||||||
|
-o /dev/null 2>/dev/null \
|
||||||
|
|| info "저장소 이미 존재: ${GITEA_ORG}/${GITEA_REPO}"
|
||||||
|
ok "저장소 생성: ${GITEA_ORG}/${GITEA_REPO}"
|
||||||
|
|
||||||
|
# 초기화 후 저장소가 생성될 때까지 대기
|
||||||
|
sleep 3
|
||||||
|
|
||||||
|
# 4. develop 브랜치 생성
|
||||||
|
curl -sf -X POST "$api/repos/${GITEA_ORG}/${GITEA_REPO}/branches" \
|
||||||
|
-H "$auth_header" \
|
||||||
|
-H "Content-Type: application/json" \
|
||||||
|
-d '{"new_branch_name":"develop","old_branch_name":"main"}' \
|
||||||
|
-o /dev/null 2>/dev/null \
|
||||||
|
|| info "develop 브랜치 이미 존재"
|
||||||
|
ok "develop 브랜치 생성"
|
||||||
|
|
||||||
|
# 5. main 브랜치 보호 규칙 설정 (PR 필수)
|
||||||
|
curl -sf -X POST "$api/repos/${GITEA_ORG}/${GITEA_REPO}/branch_protections" \
|
||||||
|
-H "$auth_header" \
|
||||||
|
-H "Content-Type: application/json" \
|
||||||
|
-d '{
|
||||||
|
"branch_name": "main",
|
||||||
|
"enable_push": false,
|
||||||
|
"enable_push_whitelist": true,
|
||||||
|
"push_whitelist_usernames": ["'"${GITEA_ADMIN}"'"],
|
||||||
|
"require_signed_commits": false,
|
||||||
|
"enable_status_check": false,
|
||||||
|
"required_approvals": 1,
|
||||||
|
"enable_approvals_whitelist": false,
|
||||||
|
"merge_whitelist_usernames": ["'"${GITEA_ADMIN}"'"]
|
||||||
|
}' \
|
||||||
|
-o /dev/null 2>/dev/null \
|
||||||
|
|| info "main 브랜치 보호 이미 설정됨"
|
||||||
|
ok "main 브랜치 보호 설정 (PR + 리뷰 1명 필수)"
|
||||||
|
|
||||||
|
# 6. 현재 Git 소스를 Gitea에 push
|
||||||
|
if [[ -d "${GUARDIA_ROOT:-/opt/guardia}/.git" ]]; then
|
||||||
|
info "기존 소스를 Gitea에 push..."
|
||||||
|
local gitea_url="http://${GITEA_ADMIN}:${GITEA_ADMIN_PW}@localhost:${GITEA_PORT}/${GITEA_ORG}/${GITEA_REPO}.git"
|
||||||
|
git -C "${GUARDIA_ROOT:-/opt/guardia}" remote remove gitea 2>/dev/null || true
|
||||||
|
git -C "${GUARDIA_ROOT:-/opt/guardia}" remote add gitea "$gitea_url"
|
||||||
|
git -C "${GUARDIA_ROOT:-/opt/guardia}" push gitea main 2>/dev/null \
|
||||||
|
&& ok "소스 push 완료 → Gitea main 브랜치" \
|
||||||
|
|| warn "소스 push 실패 — 나중에 수동으로 push"
|
||||||
|
git -C "${GUARDIA_ROOT:-/opt/guardia}" push gitea develop 2>/dev/null || true
|
||||||
|
fi
|
||||||
|
|
||||||
|
ok "Gitea 초기화 완료"
|
||||||
|
echo ""
|
||||||
|
info "Gitea URL: http://localhost:${GITEA_PORT}"
|
||||||
|
info "관리자 계정: ${GITEA_ADMIN} / ${GITEA_ADMIN_PW}"
|
||||||
|
info "저장소: ${GITEA_BASE}/${GITEA_ORG}/${GITEA_REPO}"
|
||||||
|
info ""
|
||||||
|
info "=== 브랜치 전략 ==="
|
||||||
|
info " main : 보호 브랜치 (PR + 리뷰 1명 필수)"
|
||||||
|
info " develop : 통합 브랜치 (feature 브랜치 merge 대상)"
|
||||||
|
info " feature/이름/기능명 : 개인 개발 브랜치"
|
||||||
|
info ""
|
||||||
|
info " 새 개발자 브랜치 생성:"
|
||||||
|
info " git checkout develop"
|
||||||
|
info " git checkout -b feature/홍길동/신기능"
|
||||||
|
info " git push -u gitea feature/홍길동/신기능"
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
create_dev_user() {
|
||||||
|
local username="$1"
|
||||||
|
local password="${2:-Dev@guardia!}"
|
||||||
|
local email="${3:-${username}@guardia.local}"
|
||||||
|
|
||||||
|
local api="${GITEA_BASE}/api/v1"
|
||||||
|
local auth_header="Authorization: Basic $(echo -n "${GITEA_ADMIN}:${GITEA_ADMIN_PW}" | base64)"
|
||||||
|
|
||||||
|
# 사용자 생성
|
||||||
|
curl -sf -X POST "$api/admin/users" \
|
||||||
|
-H "$auth_header" \
|
||||||
|
-H "Content-Type: application/json" \
|
||||||
|
-d "{\"username\":\"${username}\",\"password\":\"${password}\",\"email\":\"${email}\",\"login_name\":\"${username}\",\"source_id\":0,\"send_notify\":false,\"must_change_password\":false}" \
|
||||||
|
-o /dev/null 2>/dev/null \
|
||||||
|
|| { info "사용자 이미 존재: $username"; return; }
|
||||||
|
|
||||||
|
# 조직에 팀 멤버 추가
|
||||||
|
local team_id
|
||||||
|
team_id=$(curl -sf "$api/orgs/${GITEA_ORG}/teams" \
|
||||||
|
-H "$auth_header" 2>/dev/null \
|
||||||
|
| python3 -c "import sys,json; teams=json.load(sys.stdin); t=[t for t in teams if t.get('name')=='Developers']; print(t[0]['id'] if t else '')" 2>/dev/null)
|
||||||
|
|
||||||
|
if [[ -z "$team_id" ]]; then
|
||||||
|
# Developers 팀 생성
|
||||||
|
team_id=$(curl -sf -X POST "$api/orgs/${GITEA_ORG}/teams" \
|
||||||
|
-H "$auth_header" \
|
||||||
|
-H "Content-Type: application/json" \
|
||||||
|
-d "{\"name\":\"Developers\",\"permission\":\"write\",\"units\":[\"repo.code\",\"repo.issues\",\"repo.pulls\"]}" \
|
||||||
|
2>/dev/null \
|
||||||
|
| python3 -c "import sys,json; print(json.load(sys.stdin).get('id',''))" 2>/dev/null)
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [[ -n "$team_id" ]]; then
|
||||||
|
# 팀에 사용자 추가
|
||||||
|
curl -sf -X PUT "$api/teams/${team_id}/members/${username}" \
|
||||||
|
-H "$auth_header" -o /dev/null 2>/dev/null
|
||||||
|
# 팀에 저장소 추가
|
||||||
|
curl -sf -X PUT "$api/teams/${team_id}/repos/${GITEA_ORG}/${GITEA_REPO}" \
|
||||||
|
-H "$auth_header" -o /dev/null 2>/dev/null
|
||||||
|
fi
|
||||||
|
|
||||||
|
# 개인 브랜치 생성 (feature/이름/init)
|
||||||
|
curl -sf -X POST "$api/repos/${GITEA_ORG}/${GITEA_REPO}/branches" \
|
||||||
|
-H "$auth_header" \
|
||||||
|
-H "Content-Type: application/json" \
|
||||||
|
-d "{\"new_branch_name\":\"feature/${username}/init\",\"old_branch_name\":\"develop\"}" \
|
||||||
|
-o /dev/null 2>/dev/null
|
||||||
|
|
||||||
|
ok "개발자 계정 + 브랜치 생성: $username (feature/${username}/init)"
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
_wait_gitea() {
|
||||||
|
local attempt=0
|
||||||
|
until curl -sf "http://localhost:${GITEA_PORT}/api/v1/version" -o /dev/null 2>/dev/null; do
|
||||||
|
sleep 2
|
||||||
|
((attempt++))
|
||||||
|
[[ $attempt -ge 30 ]] && { fail "Gitea 서비스 응답 없음"; return; }
|
||||||
|
done
|
||||||
|
}
|
||||||
Loading…
Reference in New Issue
Block a user