빌드 결과 알림
웹훅, Slack, Discord, GitHub PR 상태와 xenoci watch로 기다리지 않고 결과 받기
빌드 결과 알림 직접 확인하기펼쳐보기
빌드를 보내 놓고 터미널을 붙잡고 있지 않아도 됩니다. 끝나거나 멈추면 웹훅, Slack, Discord, GitHub PR 화면, 데스크톱 알림으로 받습니다.
1. 웹훅 등록
xenoci webhooks add https://example.com/xenoci-hook # 기본: 성공, 실패, 취소, 시작 못함, 멈춤
xenoci webhooks add https://hooks.slack.com/services/T…/B…/… # Slack Incoming Webhook URL 그대로
xenoci webhooks add https://discord.com/api/webhooks/…/… # Discord 채널 웹훅 URL 그대로
xenoci webhooks add https://example.com/hook --events build.failed,build.stalled
xenoci webhooks list | test <id> | rotate <id> | remove <id> | deliveries <id>- 등록하면 바로 테스트 POST를 보내고, 2xx로 답해야 켜집니다. 비밀값(
whsec_…)은 등록, 재발급 때 한 번만 보입니다. - HTTPS와 공개 주소만 받습니다. localhost, 사설망, 링크 로컬, 메타데이터 주소는 등록 때와 매 전송 때(DNS 확인 후) 거절합니다. 리다이렉트는 따라가지 않습니다.
- 계정당 5개. API:
POST /api/ci/v1/webhooks {url, events}, MCP:webhooks도구. - 빌드 하나만 알림받기:
xenoci build … --notify https://…또는POST /builds의notify_url. 그 빌드의 멈춤, 끝 이벤트만 가고, 응답의notify.secret이 그 URL의 서명 비밀값입니다.
2. 이벤트
| 이벤트 | 언제 |
|---|---|
| build.succeeded | 종료 코드 0 |
| build.failed | 종료 코드가 0이 아님, 시간 초과, 플랫폼 오류 |
| build.cancelled | 사용자가 취소했거나 맥 이용 시간이 끝남 |
| build.expired | 시작하지 못함(대기 24시간 초과, 지정한 맥 종료, 남은 맥 없음) |
| build.stalled | 대기열에서 15분 넘게 시작 못함(queued_too_long) 또는 실행 중 10분 넘게 로그 없음(no_log_output). 빌드 하나당 이유별 한 번, 상태는 바뀌지 않음 |
| build.queued, build.started | 접수, 맥 배정(원할 때만 events에 넣음) |
| build.completed | 위 네 가지 끝 이벤트와 함께(예전 수신기 호환) |
맥 이용 시간이 끝나 다른 맥에서 다시 도는 빌드는 다시 끝날 때 한 번만 끝 이벤트를 보냅니다. 기존 한도는 그대로입니다: timeout_min(기본 60분, 최대 360분)을 넘기면 실패, 맥을 기다리는 빌드는 24시간 뒤 만료.
3. 페이로드와 서명 확인
{
"schema_version": 1, "event_id": "evt_…", "event_type": "build.failed", "sequence": 3,
"occurred_at": "2026-10-10T01:02:03.000Z",
"build": {
"id": "rb_…", "status": "failed", "exit_code": 65, "end_reason": null,
"repo": "acme/app", "commit": "abcdef0…", "pr": 12, "xcode": "26.6", "rental_id": "rt_…",
"created_at": "…", "started_at": "…", "ended_at": "…", "queue_s": 4, "run_s": 192, "duration_s": 196,
"wait_reason": null,
"failure": { "code": "compile_error", "fault": "customer", "summary": "Sources/App.swift:9: error: …" },
"log_url": "https://xenoci.com/api/ci/v1/builds/rb_…/log",
"console_url": "https://xenoci.com/app/builds/rb_…"
},
"stall": null
}헤더: x-xeno-event, x-xeno-event-id, x-xeno-delivery, x-xeno-timestamp, x-xeno-signature: sha256=<hex>. hex는 HMAC-SHA256(비밀값, <timestamp>.<원본 본문>)입니다. 5분보다 오래된 timestamp는 거절하고, 같은 event_id는 한 번만 처리합니다.
import crypto from 'node:crypto';
import http from 'node:http';
const secret = process.env.XENOCI_WEBHOOK_SECRET; // whsec_… (shown once when the webhook was added)
http.createServer((req, res) => {
const chunks = [];
req.on('data', c => { chunks.push(c); });
req.on('end', () => {
const body = Buffer.concat(chunks); // raw bytes: a Korean character can be split between chunks
const ts = req.headers['x-xeno-timestamp'];
const expected = 'sha256=' + crypto.createHmac('sha256', secret).update(`${ts}.`).update(body).digest('hex');
const got = String(req.headers['x-xeno-signature'] || '');
const fresh = Math.abs(Date.now() / 1000 - Number(ts)) <= 300;
if (!fresh || got.length !== expected.length || !crypto.timingSafeEqual(Buffer.from(got), Buffer.from(expected))) { res.writeHead(401).end(); return; }
const event = JSON.parse(body.toString('utf8')); // event.event_id is unique: ignore one you already handled
console.log(event.event_type, event.build?.id, event.build?.status, event.build?.exit_code);
res.writeHead(204).end();
});
}).listen(8787);전송: 10초 안에 2xx가 아니면 10초, 30초, 2분, 10분, 30분 뒤 다시 보냅니다(최대 6번, 429, 5xx, 네트워크 오류일 때, Retry-After를 따름). 410이면 그 웹훅을 끕니다. 기록: xenoci webhooks deliveries <id>.
4. GitHub PR에 상태 표시
커밋 상태 XenoCI가 PR 화면에 대기 → 성공, 실패로 나옵니다. 저장소(repo)와 40자리 커밋(commit)이 있는 빌드에 붙습니다. XenoCI에는 GitHub App이 없어서, 상태를 쓸 토큰이 필요합니다(statuses: write).
permissions:
contents: read
statuses: write # 커밋 상태를 쓰는 GITHUB_TOKEN 권한
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: xeno-ci/build@v1
with:
api-key: ${{ secrets.XENOCI_API_KEY }}
script: ci.sh
github-status: true- 다른 CI, 로컬:
xenoci build … --github-status --github-status-token-env GH_TOKEN(토큰은 그 빌드가 끝날 때까지 메모리에만 둠), 또는 계정 시크릿xenoci secrets put GITHUB_STATUS_TOKEN(fine-grained 토큰의 Commit statuses 읽기, 쓰기)을 두고--github-status만 붙입니다. - 상태를 못 쓰면(권한 없음 등) 빌드는 그대로 진행하고 실패만 기록합니다.
5. 터미널에서 지켜보기
xenoci build --script ./ci.sh --no-wait --notify desktop # 접수하고 바로 돌아옴, 끝나면 데스크톱 알림
xenoci watch rb_… # 바뀔 때마다 한 줄, 종료 코드 = 빌드 종료 코드
xenoci watch --pr 12 --json # PR 12의 최신 빌드를 따라감, 이벤트마다 JSON 한 줄
xenoci watch rb_… --notify desktop데스크톱 알림은 macOS osascript, Linux notify-send, Windows PowerShell 알림을 쓰고, 안 되면 터미널 벨을 울립니다. AI 에이전트는 MCP status의 action: watch로 같은 이벤트를 받습니다(최대 60초씩 반복 호출).