self-hosted-github-runnerlisted
Install: claude install-skill uzysjung/uzys-agent-harness
# Self-Hosted GitHub Runner
CI 가 멈췄을 때 **검증을 포기하지 않고, 동시에 검증을 위조하지도 않는** 방법.
## 이 스킬이 막으려는 것
CI 가 멈추면 두 가지 유혹이 온다.
1. **그냥 배포한다.** 게이트를 한 번 건너뛰면 다음에도 건너뛴다.
2. **CI 스텝을 로컬 스크립트에 베껴 적는다.** 이게 더 위험하다 — 초록이 나오니까 검증한 것처럼
보이는데, 워크플로에 스텝이 하나 추가되는 순간 스크립트는 그걸 모른 채 계속 초록을 낸다.
**검증을 잃은 것보다 나쁜 것은, 잃은 줄 모르는 것이다.**
self-hosted runner 는 워크플로 파일 **자체**를 실행한다. 복제본이 0이므로 드리프트가 구조적으로
불가능하고, PR 상태 체크·태그 트리거·필수 체크가 전부 그대로 살아 있다.
## Step 0 — 정말 인프라 문제인지 먼저 가른다
"CI 가 빨갛다"에서 곧바로 러너로 뛰지 마라. 코드가 깨진 건데 인프라 탓으로 돌리면 결함을
러너 뒤에 숨기게 된다. **대조군으로 가른다** — 시각 기준으로 갈리면 인프라, 커밋 기준으로
갈리면 코드다.
```bash
gh run list --workflow=<wf>.yml --limit 10 \
--json createdAt,headSha,conclusion,event \
--jq '.[] | "\(.createdAt) \(.headSha[0:7]) \(.event) \(.conclusion)"'
```
잡이 **스텝 0개로 몇 초 만에** 죽었다면 잡이 시작조차 못 한 것이다. 실제 사유는 어노테이션에 있다:
```bash
JOB=$(gh api repos/<owner>/<repo>/actions/runs/<run-id>/jobs --jq '.jobs[0].id')
gh api repos/<owner>/<repo>/check-runs/$JOB/annotations --jq '.[].message'
```
실측 예: `"The job was not started because recent account payments have failed or your spending
limit needs to be increased."` — 이건 코드와 무관하다.
## Step 1 — 워크플로를 복제하지 않고 전환 가능하게 만든다
`runs-on` 은 `vars` 컨텍스트를 받는다(공식 Context availability 표). 그래서 **한 줄**로 옮길 수 있다:
```yaml
jobs:
build:
runs-on: ${{ vars.CI_RUNNER_LABEL || 'ubuntu-latest' }}
```
변수를 설정하면 self-hosted 로, 지우면 호스티드로 돌아온다. 워크플로 파일을 복사하거나 `if:` 로
잡을 두 벌 만들지 마라 — 그게 바로 이 스킬이 막으려는 드리프트다.
전 잡에 적용해야 한다. 하나라도 빠뜨리면 그 잡만 호스티드를 기다리다 영원히 큐에 남는다.
## Step 2