Use when the user wants a test build distributed to Firebase App Distribution — 개발 완료·핫픽스 검증 시점에 특정 브랜치를 테스터에게 내보내는 절차. 피처플래그를 켠 빌드도 배포 브랜치를 따서 내보낸다. Triggers on "테스트 배포 돌려", "이 브랜치 배포해서 확인해볼게", "<플래그명> 켜서 배포해줘". Does NOT trigger on TestFlight 업로드(testflight-deploy 스킬)·앱스토어 심사 제출, 테스트 실행(run-tests 스킬), PR 생성(pr 스킬), 코드 수정(implement 스킬) — 이 스킬의 FeatureFlag.swift 편집은 사전 규정된 리터럴 치환이라 별도 발동 대상이 아니다.
Scanned 9/22/2026
Install to Claude Code
npx -y skills add sudopark/TodoCalendar --skill test-deploy --agent claude-codeInstalls into .claude/skills of the current project.
Are you the author of Test Deploy?
Add the live security badge to your README — it updates automatically with every re-scan.
[](https://www.skillsdirectory.com/skills/sudopark-test-deploy)More formats (shields.io, HTML) on the badges page.
---
name: test-deploy
description: Use when the user wants a test build distributed to Firebase App Distribution — 개발 완료·핫픽스 검증 시점에 특정 브랜치를 테스터에게 내보내는 절차. 피처플래그를 켠 빌드도 배포 브랜치를 따서 내보낸다. Triggers on "테스트 배포 돌려", "이 브랜치 배포해서 확인해볼게", "<플래그명> 켜서 배포해줘". Does NOT trigger on TestFlight 업로드(testflight-deploy 스킬)·앱스토어 심사 제출, 테스트 실행(run-tests 스킬), PR 생성(pr 스킬), 코드 수정(implement 스킬) — 이 스킬의 FeatureFlag.swift 편집은 사전 규정된 리터럴 치환이라 별도 발동 대상이 아니다.
argument-hint: "[flags]"
---
# Test Deploy — TodoCalendar
Fastlane 레인 `ios test_deploy`로 Release 아카이브를 Development 재서명해 Firebase App Distribution `testers` 그룹에 배포한다. GitHub Actions workflow_dispatch(`test_deploy.yml`)로 원격에서 돌리거나, 로컬에서 fastlane 레인을 직접 실행한다.
## 사전 조건
(a) 대상 브랜치가 원격에 push 돼 있을 것 — dispatch는 원격 ref를 기준으로 돈다.
(b) `test_deploy.yml`이 develop에 머지돼 있을 것 — `workflow_dispatch`는 워크플로우 파일이 default 브랜치에 있어야 호출된다. 아직 없으면 `gh workflow run`이 `could not find any workflows named`로 실패한다. 머지 전이라는 뜻이지 스킬 오류가 아니다.
(c) 러너 볼트 `/Users/sudo.park/Desktop/actions/secrets/TodoCalendar`에 실 config 3종(`InfoPlist_Secrets.swift`, `GoogleService-Info.plist`, `secrets.json`)이 있을 것. 최초 1회 셋업:
```bash
mkdir -p /Users/sudo.park/Desktop/actions/secrets/TodoCalendar
chmod 700 /Users/sudo.park/Desktop/actions/secrets/TodoCalendar
SRC=/Users/sudo.park/Documents/codebase/TodoCalendar
DST=/Users/sudo.park/Desktop/actions/secrets/TodoCalendar
cp "$SRC/Tuist/ProjectDescriptionHelpers/InfoPlist_Secrets.swift" "$DST/"
cp "$SRC/TodoCalendarApp/Resources/GoogleService-Info.plist" "$DST/"
cp "$SRC/TodoCalendarApp/Resources/secrets.json" "$DST/"
chmod 600 "$DST"/*
```
## 절차 — 플래그 없이
```bash
# 1. 대상 브랜치 push 확인
git push -u origin "$(git branch --show-current)"
# 2. dispatch — 실패하면 여기서 멈춘다
gh workflow run test_deploy.yml --ref "$(git branch --show-current)"
# 3. 방금 건 run 확보 (목록에 뜨기까지 몇 초 걸린다)
sleep 5
gh run list --workflow test_deploy.yml --branch "$(git branch --show-current)" \
--event workflow_dispatch --limit 1 --json databaseId,number,status
# 4. 추적
gh run watch <run-id> --exit-status
# 5. 실패 시 로그
gh run view <run-id> --log-failed
```
각 명령이 자족적이다 — Bash 도구는 작업 디렉토리만 유지하고 셸 변수는 블록이 끝나면 사라지므로, 블록을 넘어가는 변수 대입에 기대지 않는다.
2단계가 실패하면 사전 조건 (a)(b)를 다시 확인하고 **중단**한다. 추적으로 넘어가지 않는다.
3단계 결과가 비었거나 `status`가 이미 `completed`면 dispatch가 안 걸린 것이다. 추적하지 말고 중단·보고한다.
3단계 출력의 `databaseId`를 4·5단계의 `<run-id>` 자리에 넣는다. `number`가 빌드번호다.
Firebase 콘솔 링크: `https://console.firebase.google.com/project/todocalendar-1707723626269/appdistribution`
## 절차 — 피처플래그를 켜고
호출 형식은 `/test-deploy <플래그명>`. 요청에 켤 플래그 지정이 있으면 이 경로, 없으면 위 플래그 없음 경로를 탄다.
1. **플래그명 검증** — `Domain/Sources/Utils/FeatureFlag.swift`의 `Flags` enum case와 대조한다. 목록에 없는 이름이 하나라도 있으면 배포하지 않고 중단하고 유효한 case 목록을 보고한다.
```bash
grep -oE "case [a-zA-Z]+" Domain/Sources/Utils/FeatureFlag.swift
```
2. **배포 브랜치 생성** — 먼저 `git status --porcelain`이 비어 있는지 확인한다. 비어 있지 않으면 중단하고 사용자에게 알린다 (브랜치를 갈아타면 미커밋 변경이 배포 브랜치로 따라오고 배포 커밋에 딸려 들어간다). 그다음 대상 브랜치가 원격에 push 돼 있는지 확인한다.
```bash
TARGET=$(git branch --show-current)
git push -u origin "$TARGET"
git checkout -B "test-deploy/$TARGET" "$TARGET"
```
`-B`라 같은 이름의 배포 브랜치가 이미 있으면 대상 브랜치 기준으로 다시 만든다. 유지하는 것은 배포 브랜치의 **존재**이지 커밋 이력이 아니다 — 매 배포마다 대상 브랜치 기준으로 다시 만들어 강제 갱신한다.
이후 단계는 이 블록의 셸 변수를 넘겨받지 못한다 — 매 단계가 현재 체크아웃 상태에서 브랜치명을 다시 읽는다.
3. **플래그 활성** — `Domain/Sources/Utils/FeatureFlag.swift`의 아래 한 줄만 고친다. 다른 줄은 건드리지 않는다.
```swift
private var enableFlags: Set<Flags> = []
```
→
```swift
private var enableFlags: Set<Flags> = [.<플래그명>]
```
이 편집은 사전 규정된 리터럴 치환이라 implement 스킬을 별도로 발동하지 않는다.
4. **커밋** — 메시지에 켠 플래그 목록을 반드시 넣는다. 워크플로우가 릴리즈 노트에 브랜치명과 커밋 subject를 싣기 때문에, 이 목록이 곧 Firebase 릴리즈 노트에서 "어떤 플래그가 켜진 빌드인가"를 알려주는 유일한 단서다.
대상 브랜치명에서 이슈번호를 뽑을 수 있으면(`features/920-...` → `920`) `[#920]`을 쓰고, 못 뽑으면 `[test-deploy]`를 쓴다 (CLAUDE.md §5는 `[#이슈번호]` 형식을 못 박는다 — 이슈번호를 못 뽑을 때만 쓰는 예외).
```bash
git add Domain/Sources/Utils/FeatureFlag.swift
git commit -m "[#920] 테스트 배포용 피처플래그 활성 — <플래그명>"
```
이 커밋은 이 스킬이 형식을 규정한다 — commit 스킬을 별도로 발동하지 않는다.
5. **push 후 dispatch** — 위 플래그 없음 절차의 3~5단계를 그대로 따르되 ref만 배포 브랜치다. 2단계가 이미 배포 브랜치로 체크아웃을 옮겨놨으므로 현재 체크아웃 상태를 그대로 쓴다. 배포 브랜치는 매번 `-B`로 다시 만들어지므로 두 번째 배포부터 기존 원격 브랜치와 non-fast-forward로 충돌한다 — `--force-with-lease`로 강제 갱신한다.
```bash
git push --force-with-lease -u origin "$(git branch --show-current)"
gh workflow run test_deploy.yml --ref "$(git branch --show-current)"
```
6. **원래 브랜치로 복귀** — dispatch를 건 뒤 바로 돌아온다. 배포 브랜치는 지우지 않는다 — 배포본이 어떤 코드였는지 나중에 추적할 근거다. 현재 브랜치명에서 `test-deploy/` prefix를 벗겨 대상 브랜치명을 역산한다 — 배포 브랜치에 있든 이미 대상 브랜치에 있든 같은 명령으로 동작한다.
```bash
git checkout "$(git branch --show-current | sed 's|^test-deploy/||')"
```
어느 단계에서 실패하든 아래 명령으로 돌아온 뒤 보고한다.
```bash
git checkout "$(git branch --show-current | sed 's|^test-deploy/||')"
```
## 결과 보고 형식
성공: 빌드번호·브랜치·커밋·켠 플래그 + Firebase 콘솔 링크("절차 — 플래그 없이" 절 참조). 빌드번호는 위 3단계의 `number` 필드다 — `databaseId`는 GitHub 내부 run 식별자일 뿐 테스터가 Firebase에서 보는 빌드번호가 아니다.
실패: 실패 스텝 이름 + 로그 상위 에러.
## 두 실행 경로의 차이
| | dispatch | 로컬 직접 실행 |
|---|---|---|
| 체크아웃 | 원격 ref 기준 fresh (`_work/TodoCalendar/TodoCalendar`) | 현재 워크트리 (미커밋 변경 포함) |
| 실 config | 볼트에서 복사 | 이미 깔린 것 사용 |
| 빌드번호 | run number | `1` 고정 |
| 릴리즈 노트 | 브랜치·커밋·빌드번호 자동 | `"local build"` |
빌드·서명·업로드는 둘 다 같은 fastlane 레인이라 나오는 IPA는 동일하다.
로컬 직접 실행 명령:
```bash
export PATH="/opt/homebrew/opt/ruby/bin:$PATH"
TEST_DEPLOY_RELEASE_NOTES="<메모>" bundle exec fastlane ios test_deploy
```
쓸 자리는 둘이다 — (a) 워크플로우가 아직 develop에 없을 때 (b) 커밋하지 않은 로컬 변경을 그대로 확인할 때.
## 종료 기록 — skill_end
배포 결과(성공: 빌드번호·브랜치·플래그 / 실패: 실패 스텝·상위 에러)를 보고한 직후 기록한다 (명령·compliance 규칙은 CLAUDE.md §1). 실패 보고도 절차 완료다.
Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.
No comments yet. Be the first to comment!