Skip to content

빌드 배포 ​

CI 파이프라인을 연결하면 빌드가 App Center에 자동으로 등록되어 팀원 누구든 바로 확인할 수 있습니다.

동작 방식 ​

CI 파이프라인
  → POST /api/v1/builds (tapflow relay)
  → App Center에 빌드 등록
  → 팀원이 브라우저에서 기기를 선택하고 직접 테스트
      PO / PM: 기획한 대로 나왔는지
      디자이너: 디자인 스펙에 맞는지
      백엔드: API 연동이 맞는지
      QA: 버그가 있는지

CI 잡이 빌드 파일을 업로드하면 누구나 별도 도구 설치나 기기 세팅 없이 브라우저에서 바로 테스트할 수 있습니다.

두 가지 테스트 경로

이 가이드는 수동 리뷰 경로를 다룹니다. CI가 빌드를 전달하고, 팀원이 직접 테스트하는 방식입니다.

LLM 에이전트가 시뮬레이터를 자동으로 조작하는 방식은 CI/CD에서 MCP 활용을 참고하세요. 이는 별도의 실험적 기능입니다.

어떤 빌드 도구든 ​

tapflow는 빌드 도구가 아니라 완성된 아티팩트를 다룹니다. 네이티브 Xcode·Gradle 빌드, React Native, Flutter, 클라우드 빌드 서비스가 만드는 빌드를 모두 같은 방식으로 업로드합니다. 아티팩트(.app.zip·.tar.gz/.tgz·.apk)만 나오면 이후 과정은 이 페이지 그대로입니다.

사전 조건 ​

항목설명
tapflow 릴레이실행 중이고 CI 환경에서 접근 가능해야 합니다
개인 액세스 토큰(PAT)Settings → Tokens에서 API 종류로 생성합니다(builds:write 권한 포함). 이 메뉴는 Admin에게만 보입니다

CI가 릴레이에 도달하려면 ​

CI 잡이 릴레이의 POST /api/v1/builds에 접근할 수 있어야 합니다. 릴레이는 에이전트와 같은 내부 네트워크에 두는 것이 원칙입니다(배포 방식 선택). 그래서 CI가 어디서 실행되는지에 따라 경로가 갈립니다.

릴레이 배치CI가 업로드하는 방법
LAN 전용 (기본)클라우드 러너(GitHub 호스티드 등)는 LAN 릴레이에 닿지 못합니다. 내부 네트워크에 둔 self-hosted 러너에서 릴레이 내부 주소(http://192.168.x.x:4000)로 업로드하세요
VPS + rathole 터널릴레이를 외부 접근용으로 열어 두면 공개 URL(https://your-vps.com)로 어디서든 업로드할 수 있어 클라우드 CI에 가장 잘 맞습니다
Tailscale 터널tailnet 멤버만 접근할 수 있으므로 CI 러너도 tailnet에 연결돼 있어야 합니다

릴레이는 클라우드에 직접 올리지 않습니다

릴레이를 fly.io·Railway 같은 서비스에 직접 배포하면 에이전트→릴레이 구간이 인터넷을 타면서 스트림이 끊깁니다(미지원). 공개 접근이 필요하면 릴레이는 내부 네트워크에 둔 채 터널로 노출하세요. VPS는 릴레이 호스트가 아니라 터널 호스트입니다.

1. 토큰 생성 ​

대시보드의 Settings → Tokens → New token에서 생성합니다. Tokens 메뉴는 Admin에게만 보이므로 Admin 계정으로 진행하세요.

  • Name: GitHub Actions처럼 용도를 알 수 있는 이름
  • Expiration: 기본 30일. 7·30·60·90일, 직접 입력(1~365일), No expiration 중에서 고를 수 있으며 CI용이라면 90일 이하를 권장합니다.
  • Type: API. view, builds:write 권한이 자동으로 부여됩니다.

토큰은 생성 시 한 번만 표시됩니다. CI 시크릿(예: TAPFLOW_PAT)으로 저장하세요.

2. 빌드 업로드 ​

빌드 파일이 준비되면 POST /api/v1/builds를 호출합니다.

sh
# iOS (.app.zip)
curl -X POST https://your-relay/api/v1/builds \
  -H "Authorization: Bearer $TAPFLOW_PAT" \
  -F "file=@MyApp.app.zip" \
  -F "status=In Progress" \
  -F "label=$GIT_BRANCH"

# Android (.apk)
curl -X POST https://your-relay/api/v1/builds \
  -H "Authorization: Bearer $TAPFLOW_PAT" \
  -F "file=@MyApp.apk" \
  -F "status=In Progress" \
  -F "label=$GIT_BRANCH"

status=In Progress는 팀에게 리뷰 준비가 됐다는 신호입니다.
label에는 브랜치명, 티켓 번호 등 맥락을 담을 수 있습니다.

iOS 빌드

.ipa 파일은 지원하지 않습니다. 시뮬레이터 빌드는 .app.zip 또는 .tar.gz/.tgz를 올립니다. .app.zip은 xcodebuild -sdk iphonesimulator로 빌드해 .app 폴더를 zip으로 압축하고, .tar.gz/.tgz는 클라우드 시뮬레이터 빌드가 만드는 아카이브입니다.

3. 빌드 메타데이터 첨부 (선택) ​

커밋과 브랜치 정보를 코멘트로 남기면 리뷰어가 무엇이 바뀌었는지 바로 확인할 수 있습니다.

sh
BUILD_ID=$(curl -sf -X POST https://your-relay/api/v1/builds \
  -H "Authorization: Bearer $TAPFLOW_PAT" \
  -F "file=@MyApp.app.zip" \
  -F "status=In Progress" \
  -F "label=$GIT_BRANCH" | jq -r '.id')

curl -X POST https://your-relay/api/v1/comments \
  -H "Authorization: Bearer $TAPFLOW_PAT" \
  -F "build_id=$BUILD_ID" \
  -F "body=Branch: $GIT_BRANCH
Commit: $GIT_SHA
$GIT_COMMIT_MSG"

GitHub Actions 예시 ​

이 예시는 릴레이 내부 주소에 닿는 self-hosted macOS 러너를 가정합니다. 릴레이를 VPS + rathole 터널처럼 공개 URL로 열어 두었다면 runs-on을 macos-latest 같은 클라우드 러너로 바꿔도 됩니다.

self-hosted 러너와 pull request

self-hosted 러너는 워크플로가 체크아웃한 코드를 내부 네트워크의 Mac에서 그대로 실행합니다. 아래의 if:는 포크에서 연 pull request를 건너뛰므로 같은 저장소의 브랜치만 이 러너에서 빌드됩니다. 이 조건을 지우지 말고 외부 pull request를 받는 공개 저장소에는 self-hosted 러너를 연결하지 마세요.

yaml
name: tapflow에 업로드

on:
  push:
    branches: [main, 'release/**']
  pull_request:

jobs:
  upload:
    if: github.event_name == 'push' || github.event.pull_request.head.repo.full_name == github.repository
    runs-on: [self-hosted, macos]

    steps:
      - uses: actions/checkout@v4

      - name: 앱 빌드
        run: |
          xcodebuild -scheme MyApp -sdk iphonesimulator \
            -configuration Debug \
            CONFIGURATION_BUILD_DIR=build/Debug
          cd build/Debug && zip -r MyApp.app.zip MyApp.app

      - name: tapflow에 업로드
        env:
          TAPFLOW_PAT: ${{ secrets.TAPFLOW_PAT }}
          TAPFLOW_RELAY_URL: ${{ secrets.TAPFLOW_RELAY_URL }}
          BRANCH: ${{ github.head_ref || github.ref_name }}
          COMMIT: ${{ github.sha }}
          COMMIT_MSG: ${{ github.event.head_commit.message || github.event.pull_request.title }}
        run: |
          BUILD_RESPONSE=$(curl -sf -X POST "$TAPFLOW_RELAY_URL/api/v1/builds" \
            -H "Authorization: Bearer $TAPFLOW_PAT" \
            -F "file=@build/Debug/MyApp.app.zip" \
            -F "status=In Progress" \
            -F "label=$BRANCH")

          BUILD_ID=$(echo "$BUILD_RESPONSE" | jq -r '.id')

          COMMENT="Branch: $BRANCH"$'\n'"Commit: $COMMIT"$'\n'"$COMMIT_MSG"

          curl -sf -X POST "$TAPFLOW_RELAY_URL/api/v1/comments" \
            -H "Authorization: Bearer $TAPFLOW_PAT" \
            -F "build_id=$BUILD_ID" \
            -F "body=$COMMENT"

빌드 상태 참고 ​

상태의미
Backlog업로드됐지만 아직 리뷰 준비 전
In Progress준비 완료 — 팀이 테스트를 시작할 수 있음
Done이해관계자 승인 완료
Rejected문제 발견, 수정 필요

CI는 업로드 시 In Progress로 설정합니다. Done과 Rejected는 리뷰 후 팀원이 직접 변경합니다.

Released under the MIT License.