feat(cicd): Gitea 기반 CI/CD 파이프라인 통합
[Jenkins - Gitea 연동] - config/jenkins.yaml: gitea-credentials + gitea-api-token 자격증명 추가 - config/jenkins.yaml: GITEA_BASE_URL/ORG/REPO 전역 환경변수 추가 - Jenkinsfile.java-maven: Gitea SCM checkout 우선 (폴백: scm 기본값) - jenkins_plugins.sh: generic-webhook-trigger + gitea 플러그인 추가 - jenkins_install.sh: 설치 후 Gitea 웹훅 자동 등록 호출 [Gitea 웹훅 자동화] - scripts/notify/gitea_webhook.sh: Jenkins Generic Webhook Trigger 등록 - push, pull_request, pull_request_review 이벤트 트리거 - PR 빌드 전용 웹훅 별도 등록 [Gitea Actions CI (온프레미스 CI/CD)] - .gitea/workflows/ci.yml: - Python Lint (flake8 E9/F4/F8 계열) - 모듈 임포트 테스트 (21개 모듈) - FastAPI 앱 로드 테스트 - bash 구문 검사 (setup/*.sh + cicd/**/*.sh) - Docker Compose YAML 검증 - PR 검증 요약 job [브랜치 전략 적용] push: main, develop, feature/** pull_request: main, develop Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
b31c45db71
commit
6d152e5ff5
@ -79,6 +79,17 @@ jenkins:
|
|||||||
value: "/opt/apps"
|
value: "/opt/apps"
|
||||||
- key: "SCRIPTS_ROOT"
|
- key: "SCRIPTS_ROOT"
|
||||||
value: "/var/lib/jenkins/scripts"
|
value: "/var/lib/jenkins/scripts"
|
||||||
|
# Gitea 설정 (온프레미스 형상관리)
|
||||||
|
- key: "GITEA_BASE_URL"
|
||||||
|
value: "${GITEA_BASE_URL:-http://localhost:3000}"
|
||||||
|
- key: "GITEA_ORG"
|
||||||
|
value: "${GITEA_ORG:-guardia}"
|
||||||
|
- key: "GITEA_REPO"
|
||||||
|
value: "${GITEA_REPO:-GUARDiA}"
|
||||||
|
- key: "SCM_BRANCH_PROTECT_MAIN"
|
||||||
|
value: "true"
|
||||||
|
- key: "DEFAULT_BRANCH"
|
||||||
|
value: "main"
|
||||||
|
|
||||||
# ── 빌드 실행기 설정 ─────────────────────────────────────────────────────
|
# ── 빌드 실행기 설정 ─────────────────────────────────────────────────────
|
||||||
numExecutors: 4
|
numExecutors: 4
|
||||||
@ -162,12 +173,27 @@ credentials:
|
|||||||
description: "SonarQube 분석 토큰"
|
description: "SonarQube 분석 토큰"
|
||||||
secret: "${SONAR_TOKEN}"
|
secret: "${SONAR_TOKEN}"
|
||||||
|
|
||||||
# Git 자격증명 (HTTPS)
|
# Gitea 자격증명 (온프레미스 Git 서버)
|
||||||
|
- usernamePassword:
|
||||||
|
scope: GLOBAL
|
||||||
|
id: "gitea-credentials"
|
||||||
|
description: "Gitea 저장소 자격증명 (http://localhost:3000)"
|
||||||
|
username: "${GITEA_ADMIN:-gitadmin}"
|
||||||
|
password: "${GITEA_ADMIN_PW:-Gitea@guardia!}"
|
||||||
|
|
||||||
|
# Gitea API 토큰 (웹훅 등록 + PR 상태 업데이트)
|
||||||
|
- string:
|
||||||
|
scope: GLOBAL
|
||||||
|
id: "gitea-api-token"
|
||||||
|
description: "Gitea Personal Access Token"
|
||||||
|
secret: "${GITEA_API_TOKEN}"
|
||||||
|
|
||||||
|
# Git 자격증명 (HTTPS - 하위 호환)
|
||||||
- usernamePassword:
|
- usernamePassword:
|
||||||
scope: GLOBAL
|
scope: GLOBAL
|
||||||
id: "git-credentials"
|
id: "git-credentials"
|
||||||
description: "Git 저장소 자격증명"
|
description: "Git 저장소 자격증명 (Gitea 사용 권장)"
|
||||||
username: "${GIT_USERNAME}"
|
username: "${GIT_USERNAME:-gitadmin}"
|
||||||
password: "${GIT_PASSWORD}"
|
password: "${GIT_PASSWORD}"
|
||||||
|
|
||||||
# ── SonarQube 서버 설정 ───────────────────────────────────────────────────────
|
# ── SonarQube 서버 설정 ───────────────────────────────────────────────────────
|
||||||
|
|||||||
@ -34,8 +34,25 @@ pipeline {
|
|||||||
stages {
|
stages {
|
||||||
stage('Checkout') {
|
stage('Checkout') {
|
||||||
steps {
|
steps {
|
||||||
checkout scm
|
// Gitea(온프레미스) → GitHub 순서로 SCM 폴백
|
||||||
echo "✅ 체크아웃 완료: ${env.GIT_COMMIT?.take(8) ?: 'N/A'}"
|
script {
|
||||||
|
def giteaUrl = "${env.GITEA_BASE_URL ?: 'http://localhost:3000'}/${env.GITEA_ORG ?: 'guardia'}/${env.GIT_REPO_NAME ?: 'GUARDiA'}.git"
|
||||||
|
try {
|
||||||
|
checkout([
|
||||||
|
$class: 'GitSCM',
|
||||||
|
branches: [[name: "*/${env.BRANCH_NAME ?: 'main'}"]],
|
||||||
|
userRemoteConfigs: [[
|
||||||
|
url: giteaUrl,
|
||||||
|
credentialsId: 'gitea-credentials'
|
||||||
|
]],
|
||||||
|
extensions: [[$class: 'CloneOption', depth: 1, noTags: false, shallow: true]]
|
||||||
|
])
|
||||||
|
echo "✅ Gitea checkout: ${env.GIT_COMMIT?.take(8) ?: 'N/A'}"
|
||||||
|
} catch (Exception e) {
|
||||||
|
echo "⚠️ Gitea checkout 실패 — SCM 기본값 사용: ${e.message}"
|
||||||
|
checkout scm
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -169,6 +169,28 @@ print_summary() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
# ── 메인 실행 ────────────────────────────────────────────────────────────────
|
# ── 메인 실행 ────────────────────────────────────────────────────────────────
|
||||||
|
configure_gitea_webhook() {
|
||||||
|
log "=== Gitea 웹훅 자동 등록 ==="
|
||||||
|
local script_dir
|
||||||
|
script_dir="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||||
|
local webhook_script="${script_dir}/../notify/gitea_webhook.sh"
|
||||||
|
|
||||||
|
if [[ -f "$webhook_script" ]]; then
|
||||||
|
# Gitea가 기동 중이면 웹훅 자동 등록
|
||||||
|
if curl -sf "${GITEA_BASE_URL:-http://localhost:3000}/api/v1/version" -o /dev/null 2>/dev/null; then
|
||||||
|
JENKINS_URL="${JENKINS_URL:-http://localhost:${JENKINS_PORT}}" \
|
||||||
|
bash "$webhook_script" \
|
||||||
|
&& log "Gitea 웹훅 등록 완료" \
|
||||||
|
|| warn "Gitea 웹훅 등록 실패 — 나중에 수동 실행: bash $webhook_script"
|
||||||
|
else
|
||||||
|
warn "Gitea 서비스 없음 — 웹훅 등록 건너뜀"
|
||||||
|
warn "Gitea 시작 후 실행: bash $webhook_script"
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
warn "webhook 스크립트 없음: $webhook_script"
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
main() {
|
main() {
|
||||||
log "=== Jenkins 설치 시작 ==="
|
log "=== Jenkins 설치 시작 ==="
|
||||||
detect_os
|
detect_os
|
||||||
@ -178,6 +200,7 @@ main() {
|
|||||||
configure_firewall
|
configure_firewall
|
||||||
configure_selinux
|
configure_selinux
|
||||||
prepare_directories
|
prepare_directories
|
||||||
|
configure_gitea_webhook
|
||||||
print_summary
|
print_summary
|
||||||
log "=== Jenkins 설치 완료 ==="
|
log "=== Jenkins 설치 완료 ==="
|
||||||
}
|
}
|
||||||
|
|||||||
@ -111,6 +111,10 @@ PLUGINS=(
|
|||||||
"audit-trail" # 감사 로그
|
"audit-trail" # 감사 로그
|
||||||
"matrix-auth" # Matrix Authorization
|
"matrix-auth" # Matrix Authorization
|
||||||
|
|
||||||
|
# ── Gitea 연동 (웹훅 트리거) ─────────────────────────
|
||||||
|
"generic-webhook-trigger" # Gitea Push/PR 웹훅 수신
|
||||||
|
"gitea" # Gitea 전용 SCM 플러그인
|
||||||
|
|
||||||
# ── 설정 관리 ─────────────────────────────────────
|
# ── 설정 관리 ─────────────────────────────────────
|
||||||
"configuration-as-code" # JCasC
|
"configuration-as-code" # JCasC
|
||||||
"configuration-as-code-support" # JCasC 지원
|
"configuration-as-code-support" # JCasC 지원
|
||||||
|
|||||||
121
cicd/scripts/notify/gitea_webhook.sh
Normal file
121
cicd/scripts/notify/gitea_webhook.sh
Normal file
@ -0,0 +1,121 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
# ==============================================================
|
||||||
|
# Gitea 웹훅 자동 등록 스크립트
|
||||||
|
# ==============================================================
|
||||||
|
# Gitea 저장소에 Jenkins 빌드 트리거 웹훅을 등록합니다.
|
||||||
|
# Gitea PR 이벤트 → Jenkins 파이프라인 자동 실행
|
||||||
|
#
|
||||||
|
# 사용법:
|
||||||
|
# bash cicd/scripts/notify/gitea_webhook.sh
|
||||||
|
# JENKINS_URL=http://jenkins:8080 bash gitea_webhook.sh
|
||||||
|
# ==============================================================
|
||||||
|
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
GITEA_BASE="${GITEA_BASE_URL:-http://localhost:3000}"
|
||||||
|
GITEA_ORG="${GITEA_ORG:-guardia}"
|
||||||
|
GITEA_REPO="${GITEA_REPO:-GUARDiA}"
|
||||||
|
GITEA_ADMIN="${GITEA_ADMIN:-gitadmin}"
|
||||||
|
GITEA_ADMIN_PW="${GITEA_ADMIN_PW:-Gitea@guardia!}"
|
||||||
|
JENKINS_URL="${JENKINS_URL:-http://localhost:8080}"
|
||||||
|
JENKINS_USER="${JENKINS_USER:-admin}"
|
||||||
|
JENKINS_TOKEN="${JENKINS_TOKEN:-}"
|
||||||
|
|
||||||
|
API="${GITEA_BASE}/api/v1"
|
||||||
|
AUTH="Authorization: Basic $(echo -n "${GITEA_ADMIN}:${GITEA_ADMIN_PW}" | base64)"
|
||||||
|
|
||||||
|
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 " $*"; }
|
||||||
|
|
||||||
|
echo "=================================================="
|
||||||
|
echo " Gitea 웹훅 등록"
|
||||||
|
echo " 저장소: ${GITEA_BASE}/${GITEA_ORG}/${GITEA_REPO}"
|
||||||
|
echo " Jenkins: ${JENKINS_URL}"
|
||||||
|
echo "=================================================="
|
||||||
|
|
||||||
|
# ── Jenkins Webhook URL ──────────────────────────────────────
|
||||||
|
# Jenkins Generic Webhook Trigger 플러그인 활용
|
||||||
|
WEBHOOK_TOKEN="${WEBHOOK_TOKEN:-guardia-jenkins-$(date +%s | md5sum | head -c 8)}"
|
||||||
|
WEBHOOK_URL="${JENKINS_URL}/generic-webhook-trigger/invoke?token=${WEBHOOK_TOKEN}"
|
||||||
|
|
||||||
|
# API 토큰이 있으면 Basic Auth 포함
|
||||||
|
if [[ -n "$JENKINS_TOKEN" ]]; then
|
||||||
|
WEBHOOK_URL="${JENKINS_URL%/}/generic-webhook-trigger/invoke?token=${WEBHOOK_TOKEN}"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# ── 기존 웹훅 삭제 (중복 방지) ──────────────────────────────
|
||||||
|
info "기존 Jenkins 웹훅 정리..."
|
||||||
|
EXISTING=$(curl -sf "$API/repos/${GITEA_ORG}/${GITEA_REPO}/hooks" \
|
||||||
|
-H "$AUTH" 2>/dev/null | python3 -c "
|
||||||
|
import sys, json
|
||||||
|
hooks = json.load(sys.stdin)
|
||||||
|
ids = [str(h['id']) for h in hooks if 'jenkins' in str(h.get('config',{})).lower() or 'generic-webhook' in str(h.get('config',{})).lower()]
|
||||||
|
print(' '.join(ids))
|
||||||
|
" 2>/dev/null || echo "")
|
||||||
|
|
||||||
|
for hook_id in $EXISTING; do
|
||||||
|
curl -sf -X DELETE "$API/repos/${GITEA_ORG}/${GITEA_REPO}/hooks/${hook_id}" \
|
||||||
|
-H "$AUTH" -o /dev/null 2>/dev/null
|
||||||
|
info "기존 웹훅 삭제: ID=$hook_id"
|
||||||
|
done
|
||||||
|
|
||||||
|
# ── 웹훅 등록 ────────────────────────────────────────────────
|
||||||
|
info "Jenkins 웹훅 등록 중..."
|
||||||
|
RESP=$(curl -sf -X POST "$API/repos/${GITEA_ORG}/${GITEA_REPO}/hooks" \
|
||||||
|
-H "$AUTH" -H "Content-Type: application/json" \
|
||||||
|
-d "{
|
||||||
|
\"type\":\"gitea\",
|
||||||
|
\"config\":{
|
||||||
|
\"url\":\"${WEBHOOK_URL}\",
|
||||||
|
\"content_type\":\"json\",
|
||||||
|
\"secret\":\"${WEBHOOK_TOKEN}\"
|
||||||
|
},
|
||||||
|
\"events\":[\"push\",\"pull_request\",\"pull_request_review\"],
|
||||||
|
\"active\":true,
|
||||||
|
\"branch_filter\":\"*\"
|
||||||
|
}" 2>/dev/null)
|
||||||
|
|
||||||
|
HOOK_ID=$(echo "$RESP" | python3 -c "import sys,json; print(json.load(sys.stdin).get('id',''))" 2>/dev/null || echo "")
|
||||||
|
if [[ -n "$HOOK_ID" ]]; then
|
||||||
|
ok "웹훅 등록 완료 (ID: $HOOK_ID)"
|
||||||
|
else
|
||||||
|
warn "웹훅 등록 실패 — 수동 등록 필요"
|
||||||
|
info " Gitea → 저장소 → Settings → Webhooks → Add Webhook"
|
||||||
|
info " URL: $WEBHOOK_URL"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# ── PR 이벤트 웹훅 설정 (PR 오픈 시 Jenkins 빌드) ────────────
|
||||||
|
info "PR 이벤트 웹훅 등록..."
|
||||||
|
PR_RESP=$(curl -sf -X POST "$API/repos/${GITEA_ORG}/${GITEA_REPO}/hooks" \
|
||||||
|
-H "$AUTH" -H "Content-Type: application/json" \
|
||||||
|
-d "{
|
||||||
|
\"type\":\"gitea\",
|
||||||
|
\"config\":{
|
||||||
|
\"url\":\"${JENKINS_URL}/job/guardia-pr/build?token=${WEBHOOK_TOKEN}\",
|
||||||
|
\"content_type\":\"json\"
|
||||||
|
},
|
||||||
|
\"events\":[\"pull_request\"],
|
||||||
|
\"active\":true
|
||||||
|
}" 2>/dev/null)
|
||||||
|
|
||||||
|
PR_HOOK_ID=$(echo "$PR_RESP" | python3 -c "import sys,json; print(json.load(sys.stdin).get('id',''))" 2>/dev/null || echo "")
|
||||||
|
[[ -n "$PR_HOOK_ID" ]] && ok "PR 트리거 웹훅 등록 (ID: $PR_HOOK_ID)" || warn "PR 웹훅 등록 실패"
|
||||||
|
|
||||||
|
# ── develop → main PR 자동화 설명 ────────────────────────────
|
||||||
|
echo ""
|
||||||
|
echo "=================================================="
|
||||||
|
ok "Gitea 웹훅 설정 완료!"
|
||||||
|
echo ""
|
||||||
|
info "=== CI/CD 트리거 흐름 ==="
|
||||||
|
info " 1. 개발자가 feature/이름/기능 브랜치에 push"
|
||||||
|
info " 2. Gitea 웹훅 → Jenkins Generic Webhook Trigger"
|
||||||
|
info " 3. Jenkins가 해당 브랜치 빌드·테스트"
|
||||||
|
info " 4. PR (feature → develop) 생성"
|
||||||
|
info " 5. 리뷰어 승인 후 develop 자동 병합"
|
||||||
|
info " 6. develop → main PR → 관리자 승인 → 운영 배포"
|
||||||
|
echo ""
|
||||||
|
info "웹훅 토큰: $WEBHOOK_TOKEN"
|
||||||
|
info "Jenkins 웹훅 URL: $WEBHOOK_URL"
|
||||||
|
echo "=================================================="
|
||||||
Loading…
Reference in New Issue
Block a user