Files
QuantEngineByItz/docs/GITEA_ACTIONS_API_GUIDE.md
T
kjh2064 f0e8ef9b4f docs: Harness Gitea Actions debugging methodology
Document the two-stage debugging pattern discovered while fixing
prepare-release.yml (Run #1996-2000):

1. PowerShell harness for workflow_dispatch trigger + poll-to-completion
   - Working pattern for POST .../dispatches (204 = success)
   - Known PowerShell/HttpClient limitation: cannot read error response
     body via GetResponseStream() in PS7

2. SSH log-reading harness for when the Gitea API has no working
   /logs endpoint (404 on job logs):
   - Match runner container logs (task ID) to the triggered run
   - Locate actions_log/{owner}/{repo}/{shard}/{taskId}.log.zst
   - Stream-decompress with 'zstd -dc' and grep for 'Failure'/'exitcode'

3. Network debugging commands for dispatch 500s / stuck runners
   (docker network inspect, restart timing, exec connectivity test)

4. Table of real failure patterns hit and their fixes (YAML multiline
   notes, unset git identity, missing gh CLI in runner image)
2026-07-12 00:20:53 +09:00

14 KiB

Gitea Actions API 호출 가이드

작성일: 2026-07-11
대상: QuantEngine CI/CD 담당자
목표: CLI에서 Gitea Actions 상태 조회 및 troubleshooting


사전 요구사항

환경 변수 설정

# 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 조회

$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 버전:

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 상세 정보 조회

$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 조회

$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 상태

원인 분석:

# 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"

해결 방법:

# 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 찾기

$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 상태 맵

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

{
  "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

{
  "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 빠르게 확인하는 법?

$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)"

Workflow 트리거 + 모니터링 하네스 (PowerShell)

Gitea Actions API에는 /actions/runs/{id}/jobs/{job_id}/logs 엔드포인트가 없다 (404). 따라서 워크플로우를 API로 트리거하고 완료까지 폴링한 뒤, 실패 시 SSH로 서버에 직접 접속해 로그 파일을 읽는 2단계 하네스가 필요하다. 아래 스크립트가 그 표준 패턴이다.

1단계: workflow_dispatch 트리거 + 완료까지 폴링

$token = $env:GITEA_TOKEN_TAXBAIK
$repo = "kjh2064/QuantEngineByItz"
$api = "https://gitea.taxbaik.com/api/v1"

# 트리거 (workflow 파일명을 그대로 ID로 사용 가능)
$body = @{ ref = "main" } | ConvertTo-Json
$response = Invoke-WebRequest -Method POST `
    -Uri "$api/repos/$repo/actions/workflows/prepare-release.yml/dispatches" `
    -Headers @{ "Authorization" = "token $token" } `
    -ContentType "application/json" -Body $body
# 성공 시 Status: 204 (No Content) 반환 -- 이것이 정상 응답이다

Start-Sleep -Seconds 3  # run이 목록에 나타날 때까지 약간의 지연 필요

# 방금 생성된 run 조회 (limit=1이 항상 최신순)
$runs = Invoke-WebRequest -Uri "$api/repos/$repo/actions/runs?limit=1" `
    -Headers @{ "Authorization" = "token $token" } | ConvertFrom-Json
$run = $runs.workflow_runs[0]
$runId = $run.id

# 완료까지 폴링 (8초 간격, 최대 5분)
$elapsed = 0
while ($run.status -ne "completed" -and $elapsed -lt 300) {
    Start-Sleep -Seconds 8
    $elapsed += 8
    $run = Invoke-WebRequest -Uri "$api/repos/$repo/actions/runs/$runId" `
        -Headers @{ "Authorization" = "token $token" } | ConvertFrom-Json
}

Write-Host "Conclusion: $($run.conclusion)"

# Job별 결과 확인
$jobs = Invoke-WebRequest -Uri "$api/repos/$repo/actions/runs/$runId/jobs" `
    -Headers @{ "Authorization" = "token $token" } | ConvertFrom-Json
$jobs.jobs | ForEach-Object {
    $icon = if ($_.conclusion -eq "success") { "OK" } elseif ($_.conclusion -eq "failure") { "FAIL" } else { "SKIP" }
    Write-Host "  [$icon] $($_.name)"
}

주의사항:

  • Invoke-WebRequest의 에러 응답 본문은 $_.Exception.Response.Content로 읽으려 하면 HttpResponseMessageGetResponseStream()이 없어서 실패한다 (PowerShell 7 / .NET HttpClient 기반이기 때문). 상태 코드($_.Exception.Response.StatusCode)만 신뢰하고, 본문이 필요하면 애초에 -ErrorAction Stop 없이 시도하거나 SSH 로그 쪽으로 넘어가는 게 빠르다.
  • workflow ID는 파일명(prepare-release.yml)을 그대로 쓸 수 있다 — 매번 /actions/workflows 목록을 조회해서 숫자 ID를 찾을 필요 없음.

2단계: 실패 시 SSH로 실제 로그 읽기 (API 로그 엔드포인트 우회)

Job이 failure면, 어떤 step에서 실패했는지 API로는 알 수 없다. 실제 stdout/stderr는 프로덕션 서버의 압축된 로그 파일에만 존재한다.

# 1. 어떤 act_runner가 이 run을 처리했는지, task ID가 몇 번인지 확인
#    (run 트리거 직후 곧바로 실행 — 여러 runner에 로드밸런싱되므로 3개 다 확인)
ssh kjh2064@178.104.200.7 \
  'for r in gitea-runner gitea-runner-2 gitea-runner-3; do
     echo "=== $r ==="; docker logs --since 3m $r 2>&1 | grep "task 2"
   done'
# 출력 예: task 2326 repo is kjh2064/QuantEngineByItz ...
# → task ID 2326이 방금 트리거한 run에 해당

# 2. task ID로 실제 로그 파일 위치 찾기 (디렉토리는 ID 기반 샤딩됨: XX/task_id.log.zst)
ssh kjh2064@178.104.200.7 \
  'find /opt/stacks/gitea/gitea/gitea/actions_log/kjh2064/QuantEngineByItz \
     -name "2326.log.zst"'
# → .../16/2326.log.zst

# 3. zstd로 압축 해제하며 바로 읽기 (파일로 풀 필요 없음)
ssh kjh2064@178.104.200.7 \
  'zstd -dc /opt/stacks/gitea/gitea/gitea/actions_log/kjh2064/QuantEngineByItz/16/2326.log.zst' \
  | grep -A 15 "Failure\|exitcode"

핵심 포인트:

  • 로그 경로 규칙: actions_log/{owner}/{repo}/{taskId 앞 또는 뒤 hex 2자리}/{taskId}.log.zst (샤딩 방식은 taskId를 hex로 표현한 문자열의 접두 디렉토리 — find로 찾는 게 가장 안전함)
  • 압축 해제 없이 zstd -dc로 스트리밍 읽기 가능. .zst 확장자를 보고 cat으로 읽으면 바이너리가 그대로 출력되니 반드시 zstd -dc를 거칠 것.
  • 로그 안에서 실패 지점은 ❌ Failure - Main <step name>exitcode 'N': ... 패턴으로 검색하면 즉시 찾아짐 (grep -A 15로 앞뒤 문맥 함께 확인).
  • taxbaik 프로젝트의 로그도 같은 서버, 같은 actions_log 루트 아래 kjh2064/taxbaik/에 섞여 있으니 repo 이름으로 경로를 좁혀야 함.

네트워크/인프라 디버깅 (dispatch가 500을 반환하거나 job이 안 뜰 때)

# Runner 컨테이너들이 올바른 네트워크에 붙어 있는지 확인
ssh kjh2064@178.104.200.7 \
  'docker network inspect gitea_default --format "{{range .Containers}}{{.Name}} {{.IPv4Address}}{{println}}{{end}}"'
# gitea-runner, gitea-runner-2, gitea-runner-3 만 여기 있어야 정상.
# (과거 실험적으로 띄웠던 이름 없는 컨테이너들이 default bridge에 남아있는 경우가
#  있는데, 이들은 gitea:3000에 도달 못해 "connection refused"로 무한 재시도만 함 —
#  실제 job 처리에는 영향 없지만 리소스 낭비이므로 발견 시 정리 대상)

# gitea 컨테이너가 재시작된 시점 확인 (재시작 직후 몇 초는 runner가 접속 실패할 수 있음)
ssh kjh2064@178.104.200.7 \
  'docker inspect gitea --format "RestartCount: {{.RestartCount}}\nStartedAt: {{.State.StartedAt}}"'

# 실제 러너 → gitea 연결 테스트 (컨테이너 내부에서)
ssh kjh2064@178.104.200.7 \
  'docker exec gitea-runner sh -c "wget -O- -T 5 http://gitea:3000/ 2>&1 | head -3"'

dispatch API가 500을 반환하는 흔한 원인 두 가지:

  1. workflow YAML 문법 오류--notes "여러줄\n텍스트"처럼 멀티라인 문자열에 콜론(:)이 포함되면 YAML 파서가 mapping values are not allowed here로 깨짐. 로컬에서 python3 -c "import yaml; yaml.safe_load(open('file.yml'))"로 먼저 검증할 것.
  2. Gitea 컨테이너 재시작 타이밍과 겹침 — 일시적이며 몇 초 후 재시도하면 해결.

실제로 겪은 실패 패턴 모음

증상 (API/로그) 원인 해결
dispatch 500, "mapping values are not allowed here" YAML 멀티라인 문자열에 : 포함 단일 라인 --notes로 축약, 또는 env: + heredoc 사용
job은 뜨는데 특정 step에서 exitcode '1' + 그 직전 줄이 git config user.name 러너 컨테이너에 git 전역 identity 미설정 (set -e라 즉시 중단) 태그/커밋 전에 git config user.name "Gitea Actions" 명시적으로 설정
exitcode '127': command not found act_runner 기본 이미지에 gh CLI 없음 gh release create 대신 curl + Gitea REST API (POST /repos/{r}/releases, POST /repos/{r}/releases/{id}/assets) 직접 호출
runner 로그에 dial tcp 172.18.0.2:3000: connect: connection refused gitea 컨테이너 재시작 타이밍과 겹친 일시적 현상, 또는 잘못된 네트워크(bridge)에 붙은 유령 러너 몇 초 후 재시도; docker network inspect gitea_default로 정상 러너 3개만 있는지 확인

관련 문서


마지막 업데이트: 2026-07-12 상태: prepare-release.yml 운영 검증 완료 (Run #2000 성공, 릴리즈 quant_20260711.1.6ab270f 생성)