docs: Finalize Phase 4-5 CI/CD (Manual SSH deployment strategy, Gitea Actions reference)
Deploy to Production / Build Release (push) Failing after 39s
Deploy to Production / Pre-Deployment Verification (push) Has been skipped
Deploy to Production / Deploy to Production (push) Has been skipped
Deploy to Production / Post-Deployment Reporting (push) Successful in 1s

- Add Phase 4: CI/CD Pipeline Hardening status (80% complete)
  • deploy-prod.yml 4-stage pipeline (223 lines) ✓
  • Workflow consolidation (ci.yml + deploy-prod.yml) ✓
  • SSH_KEY secret registered ✓
  • Note: Act runner network limitation (workaround: manual SSH) ⚠️

- Add Phase 5: Admin UI & Deployment Optimization (complete)
  • Tabler redesign (dashboard, sidebar, responsive) ✓
  • Build: 0 errors, 0 warnings ✓
  • E2E tests: 8/8 passing ✓
  • Production: commit 30fb702 active since 21:00:55 KST ✓

- Update Deployment & Operations section:
  • Add complete manual SSH deployment procedure
  • Add rollback instructions
  • Document Gitea Actions limitation + workaround
  • Add health check and monitoring commands
  • Reference docs/GITEA_ACTIONS_API_GUIDE.md

- Add docs/GITEA_ACTIONS_API_GUIDE.md:
  • Gitea API reference (Run/Job queries)
  • PowerShell/Bash examples
  • Troubleshooting guide
  • FAQ

Decision: Option A (Current State Maintained) — Stable manual SSH deployment, infrastructure-limited auto-deployment.

Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
This commit is contained in:
2026-07-11 22:49:29 +09:00
parent 5b41423aef
commit e0af3c3d34
2 changed files with 372 additions and 8 deletions
+120 -8
View File
@@ -67,13 +67,43 @@ This file provides guidance to Claude Code (claude.ai/code) when working with co
- Makefile created (npm → make mappings)
- np operations documented
**Phase 4: CI/CD Pipeline Hardening** ✅ 80% COMPLETE (2026-07-11)
- ✅ deploy-prod.yml (4-stage pipeline, 223 lines)
- Build → Pre-Deployment Check → Deploy → Post-Deployment Reporting
- SSH-based remote deployment (scp + ssh commands)
- Comprehensive health checks (10-retry with 3s intervals)
- Artifact management (.tar.gz)
- ✅ Workflow consolidation (2 active files)
- ci.yml: PR validation only (maintains 29 validators)
- deploy-prod.yml: Production deployment
- Deleted: merge-to-main.yml (non-functional), fast-validation.yml (redundant), archived/ directory
- ✅ SSH credentials: SSH_KEY registered in Gitea Secrets
- ⚠️ Gitea Actions limitation: Act runner ↔ Gitea network connectivity issues
- Workflow trigger (on:push) works ✓
- Job execution fails (network: dial tcp 172.18.0.2:3000 refused)
- **Workaround**: Manual SSH-based deployment (see "Production Deployment" below)
- 📚 Gitea API documentation: docs/GITEA_ACTIONS_API_GUIDE.md
**Phase 5: Admin UI & Deployment Optimization** ✅ COMPLETE (2026-07-11)
- ✅ Admin UI redesign (Tabler framework)
- Dashboard: stat cards, quick actions, system info
- Responsive sidebar navigation
- Professional layout (dark sidebar #2c3e50, white content)
- ✅ Build output: 0 errors, 0 warnings
- ✅ E2E tests: 8/8 passing (Playwright)
- ✅ Production deployment: Active since 2026-07-11 21:00:55 KST
- Commit: 30fb702
- HTTP 200 health check
- Service: active (running)
**Status Summary**:
- Python codebase: Operational (1,140 files)
- .NET 9 coverage: Core (✅), Infrastructure (✅), API (✅), Web UI (✅)
- Database: PostgreSQL fully migrated
- CI/CD: Manual SSH deployment (fully operational), Gitea Actions (limited by infrastructure)
- Release gates: Python gates remain authority until Phase 2 integration testing complete
## Deployment & Operations
## Deployment & Operations (Phase 4-5, 2026-07-11)
**Production Server**: Hetzner Cloud `178.104.200.7` (kjh2064@178.104.200.7)
@@ -81,22 +111,104 @@ Projects on server:
1. **TaxBaik** (홈페이지) — Nginx location `/taxbaik`
2. **QuantEngine** (데이터 수집/분석) — Nginx location `/quantengine`
See [Temp/DEPLOYMENT_GUIDE.md](Temp/DEPLOYMENT_GUIDE.md) for deployment procedures.
### Production Deployment Strategy (Manual SSH-Based)
### Quick Deploy (QuantEngine)
**Current Status**: Gitea Actions automated deployment limited by infrastructure constraints. Deployed via stable manual SSH pipeline.
**Pre-Deployment Checklist**:
1. ✅ Local build: `dotnet build src/dotnet/QuantEngine.Web/QuantEngine.Web.csproj -c Release`
2. ✅ E2E tests pass: `npx playwright test`
3. ✅ Admin pages verified (200 status, no 500 errors)
4. ✅ Commit to main branch: `git push origin main`
**Deployment Procedure** (Manual SSH):
```powershell
# 1. SSH into production server
ssh kjh2064@178.104.200.7
systemctl status quantengine-api
journalctl -u quantengine-api -f
sudo systemctl restart quantengine-api
# 2. Navigate to deployment directory
cd ~/deployments
# 3. Run deployment script (or manual steps below)
./deploy.sh
# OR manual deployment:
# ─────────────────────
# 3a. Build release artifact locally, then SCP to server:
dotnet publish src/dotnet/QuantEngine.Web/QuantEngine.Web.csproj -c Release -o ./publish
tar -czf quantengine-release.tar.gz -C ./publish .
scp quantengine-release.tar.gz kjh2064@178.104.200.7:/tmp/
# 3b. On production server, extract and deploy:
mkdir -p ~/deployments/quantengine_$(date +%Y%m%d_%H%M%S)_$(git rev-parse --short HEAD)
tar -xzf /tmp/quantengine-release.tar.gz -C $DEPLOY_DIR
ln -sfn $DEPLOY_DIR ~/quantengine_active
systemctl restart quantengine
# 4. Verify deployment
curl http://127.0.0.1:5000/Account/Login
systemctl status quantengine
journalctl -u quantengine -n 20
```
**Monitoring Post-Deployment**:
```powershell
# Check service status
systemctl status quantengine.service
# View live logs
journalctl -u quantengine.service -f
# Verify active deployment
readlink ~/quantengine_active
# Health check (HTTP)
curl -I http://127.0.0.1:5000/Account/Login
```
### Rollback Procedure
```bash
# List recent deployments
ls -lht ~/deployments/quantengine_* | head -10
# Rollback to previous deployment
PREV_DEPLOY=$(ls -dt ~/deployments/quantengine_* | head -2 | tail -1)
ln -sfn $PREV_DEPLOY ~/quantengine_active
systemctl restart quantengine
# Verify
systemctl status quantengine
curl http://127.0.0.1:5000/Account/Login
```
### Gitea Actions (Limited - For Reference)
**Status**: Workflow trigger works (on:push detected), but Act runner cannot execute jobs due to Docker network constraints.
**Workaround**: Use manual SSH deployment (above). Gitea Actions configuration is prepared in:
- `.gitea/workflows/deploy-prod.yml` (4-stage pipeline, ready)
- `docs/GITEA_ACTIONS_API_GUIDE.md` (API reference for monitoring)
**API Monitoring** (when Actions are operational):
```powershell
$token = $env:GITEA_TOKEN_TAXBAIK
$response = Invoke-WebRequest `
-Uri "https://gitea.taxbaik.com/api/v1/repos/kjh2064/QuantEngineByItz/actions/runs?limit=5" `
-Headers @{ "Authorization" = "token $token" }
($response.Content | ConvertFrom-Json).workflow_runs | ForEach-Object {
Write-Host "Run #$($_.id): $($_.display_title) [$($_.conclusion)]"
}
```
See `docs/GITEA_ACTIONS_API_GUIDE.md` for complete API documentation.
### Git Repository
**Gitea Server** (동일 호스트):
- **HTTP**: `http://178.104.200.7/kjh2064/QuantEngineByItz.git`
- **SSH**: `git@178.104.200.7:2222/...`
- **HTTP**: `https://gitea.taxbaik.com/kjh2064/QuantEngineByItz.git`
- **SSH**: `ssh://git@gitea.taxbaik.com:2222/kjh2064/QuantEngineByItz.git`
## UI Design Principles (2026-07-11 — Migrated to Razor Pages)
+252
View File
@@ -0,0 +1,252 @@
# Gitea Actions API 호출 가이드
**작성일**: 2026-07-11
**대상**: QuantEngine CI/CD 담당자
**목표**: CLI에서 Gitea Actions 상태 조회 및 troubleshooting
---
## 사전 요구사항
### 환경 변수 설정
```powershell
# PowerShell
$env:GITEA_TOKEN_TAXBAIK = "your_gitea_access_token"
# 또는 Windows 환경변수 저장
[Environment]::SetEnvironmentVariable("GITEA_TOKEN_TAXBAIK", "your_token", "User")
```
### 토큰 생성
1. Gitea 웹 UI: https://gitea.taxbaik.com/user/settings/applications
2. "Generate New Token" → 권한: `repo`, `read:actions`
3. 토큰 복사 및 환경 변수 설정
---
## API Endpoints
### 1. 최근 Workflow Runs 조회
```powershell
$token = $env:GITEA_TOKEN_TAXBAIK
$response = Invoke-WebRequest `
-Uri "https://gitea.taxbaik.com/api/v1/repos/kjh2064/QuantEngineByItz/actions/runs" `
-Headers @{
"Accept" = "application/json"
"Authorization" = "token $token"
}
$data = $response.Content | ConvertFrom-Json
$data.workflow_runs | ForEach-Object {
Write-Host "Run #$($_.id): $($_.display_title) [$($_.status)/$($_.conclusion)]"
}
```
**Bash/cURL 버전:**
```bash
curl -X GET "https://gitea.taxbaik.com/api/v1/repos/kjh2064/QuantEngineByItz/actions/runs" \
-H "Accept: application/json" \
-H "Authorization: token $GITEA_TOKEN_TAXBAIK" | jq '.workflow_runs[] | {id, display_title, status, conclusion}'
```
---
### 2. 특정 Run 상세 정보 조회
```powershell
$run_id = 1987
$response = Invoke-WebRequest `
-Uri "https://gitea.taxbaik.com/api/v1/repos/kjh2064/QuantEngineByItz/actions/runs/$run_id" `
-Headers @{
"Authorization" = "token $env:GITEA_TOKEN_TAXBAIK"
}
$run = $response.Content | ConvertFrom-Json
Write-Host "Run #$($run.id)"
Write-Host " Title: $($run.display_title)"
Write-Host " Status: $($run.status)"
Write-Host " Conclusion: $($run.conclusion)"
Write-Host " Commit: $($run.head_sha)"
Write-Host " Branch: $($run.head_branch)"
Write-Host " Created: $($run.created_at)"
Write-Host " Updated: $($run.updated_at)"
```
---
### 3. Run의 Jobs 조회
```powershell
$run_id = 1987
$response = Invoke-WebRequest `
-Uri "https://gitea.taxbaik.com/api/v1/repos/kjh2064/QuantEngineByItz/actions/runs/$run_id/jobs" `
-Headers @{
"Authorization" = "token $env:GITEA_TOKEN_TAXBAIK"
}
$jobs_data = $response.Content | ConvertFrom-Json
$jobs_data.jobs | ForEach-Object {
Write-Host "Job #$($_.id): $($_.name)"
Write-Host " Status: $($_.status), Conclusion: $($_.conclusion)"
Write-Host " Started: $($_.started_at)"
Write-Host " Completed: $($_.completed_at)"
}
```
---
## Troubleshooting
### 문제: Run이 failure 상태
**원인 분석:**
```powershell
# 1. Jobs 상태 확인
$run_id = 1987
$response = Invoke-WebRequest `
-Uri "https://gitea.taxbaik.com/api/v1/repos/kjh2064/QuantEngineByItz/actions/runs/$run_id/jobs" `
-Headers @{ "Authorization" = "token $env:GITEA_TOKEN_TAXBAIK" }
$jobs = ($response.Content | ConvertFrom-Json).jobs
# 2. failure 상태인 job 찾기
$failed_jobs = $jobs | Where-Object { $_.conclusion -eq "failure" }
$failed_jobs | ForEach-Object {
Write-Host "Failed Job: $($_.name) (ID: $($_.id))"
Write-Host " Status: $($_.status)"
}
# 3. Build 로그 확인 (로컬 또는 프로덕션 서버)
ssh kjh2064@178.104.200.7 'ls /opt/stacks/gitea/gitea/actions_log/kjh2064/taxbaik/*/*.log.zst'
```
### 문제: Act Runner 연결 실패
**증상:**
```
error="unavailable: dial tcp 172.18.0.2:3000: connect: connection refused"
```
**해결 방법:**
```bash
# 1. Runner 상태 확인
docker ps | grep runner
# 2. Runner 로그 확인
docker logs gitea-runner | grep -E "error|failed|connection" | tail -20
# 3. Gitea ↔ Runner 네트워크 확인
docker network ls
docker network inspect bridge | grep -E "Name|Containers"
# 4. Runner 재시작 (위험: 진행 중인 job 중단)
docker restart gitea-runner gitea-runner-2 gitea-runner-3
```
---
## 실행 예제
### 예제 1: 최근 Failed Run 찾기
```powershell
$response = Invoke-WebRequest `
-Uri "https://gitea.taxbaik.com/api/v1/repos/kjh2064/QuantEngineByItz/actions/runs?limit=10" `
-Headers @{ "Authorization" = "token $env:GITEA_TOKEN_TAXBAIK" }
($response.Content | ConvertFrom-Json).workflow_runs `
| Where-Object { $_.conclusion -eq "failure" } `
| ForEach-Object {
Write-Host "❌ Run #$($_.id): $($_.display_title)"
Write-Host " Commit: $($_.head_sha.Substring(0, 7))"
Write-Host " Time: $($_.completed_at)"
}
```
### 예제 2: Run 전체 Job 상태 맵
```powershell
function Show-RunStatus {
param($RunId)
$run_url = "https://gitea.taxbaik.com/api/v1/repos/kjh2064/QuantEngineByItz/actions/runs/$RunId"
$run = (Invoke-WebRequest -Uri $run_url -Headers @{ "Authorization" = "token $env:GITEA_TOKEN_TAXBAIK" }).Content | ConvertFrom-Json
Write-Host "Run #$RunId ($($run.display_title))" -ForegroundColor Cyan
Write-Host "Status: $($run.status) / Conclusion: $($run.conclusion)"
Write-Host ""
$jobs_url = "$run_url/jobs"
$jobs = (Invoke-WebRequest -Uri $jobs_url -Headers @{ "Authorization" = "token $env:GITEA_TOKEN_TAXBAIK" }).Content | ConvertFrom-Json
$jobs.jobs | ForEach-Object {
$icon = if ($_.conclusion -eq "success") { "" } elseif ($_.conclusion -eq "failure") { "" } else { "" }
Write-Host " [$icon] $($_.name) ($($_.status))"
}
}
# 사용
Show-RunStatus -RunId 1987
```
---
## API 응답 구조
### Run Object
```json
{
"id": 1987,
"display_title": "CI: Trigger deploy-prod.yml workflow via git push",
"head_sha": "5b41423aef4a03398f6b80c55c959563583e4f28",
"head_branch": "main",
"status": "completed",
"conclusion": "failure",
"created_at": "2026-07-11T22:33:06+09:00",
"updated_at": "2026-07-11T22:33:34+09:00"
}
```
### Job Object
```json
{
"id": 2375,
"name": "Build Release",
"status": "completed",
"conclusion": "failure",
"started_at": "2026-07-11T13:33:06+09:00",
"completed_at": "2026-07-11T13:33:34+09:00"
}
```
---
## 자주 묻는 질문 (FAQ)
**Q: 토큰 권한이 부족하면?**
```
"message": "invalid username, password or token"
```
A: Gitea 설정에서 토큰 재생성, `repo` + `read:actions` 권한 부여
**Q: Run 로그를 API로 다운로드할 수 없나?**
A: 현재 Gitea API는 `/actions/runs/{id}/logs` 지원하지 않음. 프로덕션 서버에서 `/opt/stacks/gitea/gitea/actions_log/` 디렉토리 직접 접근
**Q: 가장 최신 Run 빠르게 확인하는 법?**
```powershell
$latest = ((Invoke-WebRequest -Uri "https://gitea.taxbaik.com/api/v1/repos/kjh2064/QuantEngineByItz/actions/runs?limit=1" `
-Headers @{ "Authorization" = "token $env:GITEA_TOKEN_TAXBAIK" }).Content | ConvertFrom-Json).workflow_runs[0]
Write-Host "$($latest.display_title): $($latest.conclusion)"
```
---
## 관련 문서
- [CLAUDE.md - Deployment Gates](https://gitea.taxbaik.com/kjh2064/QuantEngineByItz/src/branch/main/CLAUDE.md)
- [deploy-prod.yml - 4-Stage Pipeline](.gitea/workflows/deploy-prod.yml)
- [Gitea Official API Docs](https://docs.gitea.io/en-us/api-usage/)
---
**마지막 업데이트**: 2026-07-11
**상태**: 운영 중 - Act Runner 연결 불안정 이슈 진행 중