문서빌드 결과 알림

빌드 결과 알림

웹훅, Slack, Discord, GitHub PR 상태와 xenoci watch로 기다리지 않고 결과 받기

AI로 연결하기 →

빌드 결과 알림 직접 확인하기펼쳐보기

빌드를 보내 놓고 터미널을 붙잡고 있지 않아도 됩니다. 끝나거나 멈추면 웹훅, Slack, Discord, GitHub PR 화면, 데스크톱 알림으로 받습니다.

1. 웹훅 등록

sh
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. 페이로드와 서명 확인

json
{
  "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).

yaml
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. 터미널에서 지켜보기

sh
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초씩 반복 호출).