TeamCity → 터널/엣지 → 127.0.0.1 OpenClaw로 웹훅을 받고, 공유 비밀·raw 바디 검증 뒤 격리 WORK_ROOT에서 얕은 클론·npm ci --dry-run·lockfile diff를 돌린 다음 TeamCity REST로 요약을 붙이는 최소 절차를 정리했습니다. 연관 글: Jenkins 웹훅 프리플라이트, 재시도·복구, 설치 문제 해결. 공식 문서: docs.openclaw.ai.

설치·게이트웨이·온보딩(Node 22+·데몬)

Node 22.16+ 또는 24 LTS를 셸과 launchd에서 동일하게 맞춘 뒤 openclaw doctor·openclaw onboard --install-daemon으로 LaunchAgent를 등록합니다. plist에 NODE_BINARY·PATH·NODE_EXTRA_CA_CERTS(해당 시)를 절대 경로로 넣어 nvm 대화형 환경과 분리하세요.

게이트웨이는 127.0.0.1만 리슨하고, 외부는 SSH 터널 또는 리버스 프록시로 웹훅 경로만 전달합니다. 대시보드 토큰·웹훅 비밀·REST PAT(빌드 조회·서비스 메시지 수준)은 파일을 나누고 chmod 600입니다.

온보딩·환경 체크리스트
  • which node·node -v가 셸·plist·게이트웨이에서 동일
  • curl -fsS http://127.0.0.1:…/health가 모니터링 주기마다 성공
  • WORK_ROOT 디스크 여유·동시 작업 상한·자동 정리 정책 정의
  • TeamCity 에이전트와 동일한 PROXY·npm/pnpm registry를 쓰면 프리체크 결과가 본 빌드 로그와 맞습니다

의사결정 소표: 게이트웨이 × 콜백 × 작업 디렉터리

항목 권장 피할 것
리슨 127.0.0.1 + 터널/프록시로 웹훅 경로만 관리 UI와 훅을 같은 공인 포트에 노출
검증 쿼리 또는 고정 헤더의 공유 비밀·수신 raw 바이트 그대로 비교 JSON 파싱 후 재직렬화한 문자열로 비밀 비교
실행 수백 ms 내 2xx·무거운 클론은 워커 큐 요청 스레드에서 곧바로 깊은 클론·설치
작업본 빌드 ID·리비전별 전용 디렉터리 또는 worktree 병렬 빌드가 한 WORK_ROOT 공유
요약 회송 REST PAT는 빌드 메시지·조회만 관리자·전 프로젝트 권한 토큰을 스크립트에 박제

TeamCity Webhook: 트리거·플레이스홀더·검증

프로젝트·전역 Webhook에서 조건을 성공 빌드·특정 구성으로 좁히고, 본문에 %teamcity.build.id%·%build.vcs.number%·%system.teamcity.buildType.id% 등 공식 매크로를 넣습니다. 공유 비밀은 TeamCity에만 두고 핸들러에는 환경 변수로 주입합니다.

운영 단계 체크리스트
  • 1 스테이징에서 curl로 토큰·본문을 맞춘 뒤 2xx·/health 확인
  • 2 TeamCity UI에 웹훅 등록 후 배달 기록(Delivery log)에서 지연·응답 코드 확인
  • 3 핸들러에서 raw 바디 검증 → 빌드 ID로 중복 처리 방지(멱등)
  • 4 큐에 작업 넣고 즉시 응답해 타임아웃 재시도 루프를 끊기
  • 5 REST 요약 전송 실패 시 2·4·8초 지수 백오프·최대 3회
curl -fsS -X POST "https://예시-엣지/openclaw/teamcity-hook?token=***" \ -H "Content-Type: application/json" \ -d '{"buildId":"123","revision":"abc","buildTypeId":"MyBuild"}'

엣지 프록시가 본문을 압축 해제·재인코딩하지 않는지, Content-Type이 변하지 않는지 스테이징에서 먼저 검증하세요.

프리체크·lockfile 스캔·요약 필드

${WORK_ROOT}/tc-${buildId}에 git clone --depth 1 후 Node는 npm ci --dry-run 또는 pnpm install --frozen-lockfile --dry-run 등 읽기 위주만 실행합니다. lockfile은 기준 산출물과 git diff·해시 비교로 요약합니다.

node -e "const f=require('fs');const j=JSON.parse(f.readFileSync('package-lock.json'));console.log('lockfileVersion',j.lockfileVersion,'packages',Object.keys(j.packages||{}).length)"

요약 JSON은 buildId, revision, lockfile_ok, precheck_exit 수준으로 짧게 쓰고, REST 실패는 2·4·8초 백오프로 최대 세 번입니다.

흔한 오류 FAQ: 검증·타임아웃·REST·로그

웹훅 검증이 계속 실패합니다

TeamCity에 저장한 토큰과 핸들러 환경 변수가 일치하는지, 프록시가 쿼리스트링·본문 인코딩을 바꾸지 않는지 확인합니다. 가능하면 수신한 raw 바이트를 그대로 비교합니다.

TeamCity가 타임아웃·재전송을 반복합니다

핸들러가 클론 전에 즉시 2xx를 내는지, 백그라운드 워커 큐가 막히지 않았는지 봅니다. 저장소가 크면 --depth를 줄이고 동시 클론 수를 제한합니다.

REST가 401·403입니다

PAT에 빌드 조회·서비스 메시지(또는 설명) 추가 권한만 남겼는지, 사용자가 해당 프로젝트에 연결돼 있는지 확인합니다. 사내 프록시 Basic 인증이 끼면 curl -v로 헤더를 추적합니다.

데몬과 터미널의 Node 버전이 다릅니다

LaunchAgent plist에 NODE_BINARY 절대 경로를 쓰고, 대화형 셸 전용 nvm에만 의존하지 않습니다. TLS 오류는 상단 링크의 설치 문제 해결 글에 있는 인증서 절차를 적용합니다.

로그에서 원인을 못 찾겠습니다

macOS에서는 log stream --predicate로 게이트웨이 프로세스 라벨을 필터하고, plist의 StandardOutPath와 TeamCity 웹훅 배달 기록을 같은 타임스탬프로 맞춰 봅니다. 로그 본문에는 비밀 대신 빌드 ID·멱등 키만 남깁니다.

원격 Mac을 CI 프리플라이트 노드로 쓰는 이유

전용 원격 Mac에 게이트웨이를 두면 TeamCity 이벤트만 받아 프리체크 OS·툴체인을 고정하기 쉽습니다. MacPull 노드에 SSH로 올린 뒤 같은 Runbook을 복제하고, 로그인 없이 도움말·구매·홈을 참고하세요.

맺음말: 루프백 게이트웨이·최소 권한 토큰·raw 검증·멱등·격리 WORK_ROOT만 지키면 TeamCity와 OpenClaw를 안전하게 잇습니다. 아래 CTA의 기술 블로그에서 OpenClaw 실전 글을 이어서 보세요.

TeamCity × OpenClaw

프리체크·lockfile 게이트를 24시간 유지할 원격 Mac

MacPull 전용 Apple Silicon에서 TeamCity 웹훅과 OpenClaw를 고정하세요. 도움말(SSH)·구매·홈·기술 블로그(OpenClaw 모음)는 로그인 없이 이용할 수 있습니다.

전용 노드
SSH
헬스 유지
지원