Unity로 개발하는 모바일 퍼즐 게임 프로젝트입니다. Android 빌드와 Google Play 내부 테스트 배포를 GitHub Actions로 관리합니다.
| 항목 | 값 |
|---|---|
| Unity | 6000.2.7f2 |
| 주요 플랫폼 | Android ARM64 |
| Android applicationId | com.tessellation.honeycomb0 |
| 최초 CI/CD 릴리스 | v1.1.1 |
저장소를 clone한 뒤 Unity Hub에서 루트 폴더를 열어 작업합니다. Library, Temp, Logs, Build 등 Unity 생성 파일은 커밋하지 않습니다.
main만 장기 유지합니다. 모든 변경은 짧게 유지되는 작업 브랜치에서 개발한 뒤 Pull Request로 병합합니다.
브랜치 이름은 다음 형식을 사용합니다.
<type>/<선택적-이슈번호>-<kebab-case>
예시:
feat/33-tile-editor
fix/restart-failure-ui
ci/android-release
허용되는 type은 feat, fix, refactor, perf, docs, test, build, ci, chore, hotfix입니다. 새 단어는 소문자 영문과 숫자를 하이픈으로 연결합니다. Dependabot 브랜치는 이 규칙에서 제외됩니다.
PR 제목은 다음 Conventional Commit 형식을 사용합니다.
<type>(<선택적 scope>): <설명>
예: feat(tile): 폭탄 타일 생성 규칙 추가
개별 작업 커밋은 자유롭게 작성할 수 있지만 최종 이력은 PR 제목을 사용하는 Squash Merge로 정리합니다.
GitHub Ruleset에서 다음 값을 적용합니다.
- 직접 push, force push, 브랜치 삭제 금지
- PR 승인 1명 필수, 새 커밋이 올라오면 기존 승인 무효화
- 모든 리뷰 대화 해결 필수
- 필수 검사:
Policy,Unity Tests (EditMode),Unity Tests (PlayMode) - Squash Merge만 허용하고 병합된 브랜치는 자동 삭제
- 관리자도 일반 변경에서는 규칙을 우회하지 않음
v*.*.* 릴리스 태그는 관리자만 생성하며 수정하거나 삭제하지 않습니다. 긴급 수정도 hotfix/... 브랜치와 PR을 거칩니다.
| 시점 | 검사 |
|---|---|
main 대상 PR |
브랜치명, PR 제목, Unity EditMode/PlayMode 테스트, 전체 스크립트 컴파일 |
| CI 작업 브랜치 push | 위 테스트 후 Release 구성의 테스트 APK 생성 |
main 병합 |
위 테스트 후 Android 검증용 APK 생성 |
vMAJOR.MINOR.PATCH 태그 |
APK 생성 후 GitHub Release에 첨부. release keystore Secrets가 있으면 서명된 AAB도 함께 첨부 |
| 승인된 수동 실행 | 해당 Release의 AAB를 Google Play 내부 테스트에 게시 |
테스트 결과는 14일, CI Release APK는 14일, 릴리스 APK·AAB는 90일 동안 Actions artifact로 보관합니다. 같은 PR의 이전 실행은 취소하지만 릴리스와 Play 배포는 중간 취소하지 않습니다.
수동 Android 검증은 직전 PR CI가 통과한 동일 SHA에서 순차 실행하며, 이미 완료한 Unity 테스트를 중복 실행하지 않고 APK 빌드만 수행합니다. main push에서는 Unity 테스트가 성공한 뒤에만 Android 빌드를 시작합니다.
CI의 테스트 APK는 Development Build가 아닌 Release 구성으로 빌드합니다. release keystore Secrets가 모두 등록되어 있으면 IL2CPP·ARM64와 해당 키를 사용합니다. Secrets가 모두 없으면 제한된 GitHub runner에서 빠르게 검증할 수 있도록 Mono·ARMv7과 Unity 기본 debug keystore를 사용합니다. Secrets가 일부만 등록된 잘못된 구성은 실패합니다.
vMAJOR.MINOR.PATCH 태그 릴리스도 같은 규칙을 따릅니다. release keystore Secrets가 모두 등록되어 있으면 IL2CPP·ARM64로 APK와 AAB를 만들어 정식 Release로 공개합니다. Secrets가 없으면 Mono·ARMv7 debug 서명 APK만 만들어 prerelease로 표시하며, 이 빌드는 Google Play에 게시할 수 없습니다. Play 배포까지 하려면 keystore Secrets 등록이 필요합니다.
CI에서 Unity를 실행하려면 Personal 라이선스의 UNITY_LICENSE, 또는 Pro 라이선스의 UNITY_EMAIL·UNITY_PASSWORD·UNITY_SERIAL 조합이 필요합니다. 유효한 라이선스가 없으면 Unity 테스트와 빌드는 실패하도록 두어 보호 규칙을 우회하지 않습니다.
예외로 Dependabot PR은 Unity 테스트를 건너뜁니다. Dependabot 실행에는 저장소 Secrets가 전달되지 않아 Unity 라이선스 인증이 구조적으로 불가능하고, 그대로 두면 모든 의존성 PR이 항상 빨간 상태가 되기 때문입니다. 대신 Dependabot PR은 main 병합 후 main push CI에서 검증됩니다. Dependabot PR에서도 테스트를 돌리려면 같은 Unity 값을 Dependabot secrets로 등록한 뒤, ci.yml의 unity-tests 잡에 있는 github.actor != 'dependabot[bot]' 조건을 함께 제거해야 합니다. GitHub은 잡 수준 if에서 secrets 컨텍스트를 제공하지 않아 이 조건을 Secrets 등록 여부에 따라 자동으로 바꿀 수 없습니다.
Repository secrets:
UNITY_LICENSEUNITY_EMAIL(Pro 라이선스 방식에서 사용)UNITY_PASSWORD(Pro 라이선스 방식에서 사용)UNITY_SERIAL(Pro 라이선스 방식에서 사용)ANDROID_KEYSTORE_BASE64ANDROID_KEYSTORE_PASSANDROID_KEYALIAS_PASS
키 alias는 저장소의 기존 release key에 맞춰 기본값 tessellation을 사용합니다. 다른 alias를 사용한다면 ANDROID_KEYALIAS_NAME을 등록합니다. 기존 GameCI 이름인 KEYSTORE_BASE64, KEYSTORE_PASS, KEY_ALIAS_NAME, KEY_ALIAS_PASS도 호환됩니다.
google-play-internal Environment variables:
GCP_WORKLOAD_IDENTITY_PROVIDERGCP_SERVICE_ACCOUNT
Environment에는 배포 가능한 관리자만 접근할 수 있게 제한합니다. Google 서비스 계정에는 해당 앱의 내부 테스트 릴리스에 필요한 최소 권한만 부여합니다. JSON 서비스 계정 키 대신 GitHub OIDC와 Google Workload Identity Federation을 사용합니다.
-
새 업로드 키를 오프라인에서 생성하고 팀 비밀 저장소에 백업합니다.
-
위 keystore 관련 Secrets를 등록합니다. keystore 파일이나 비밀번호는 저장소에 커밋하지 않습니다.
-
Play Console에 applicationId
com.tessellation.honeycomb0인 앱을 만들고 Play App Signing을 활성화합니다. -
main의 검사가 모두 통과한 커밋에 최초 태그를 생성합니다.git tag -a v1.1.1 -m "Android internal v1.1.1" git push origin v1.1.1
-
Android Release Build가 만든 GitHub Release에서 AAB를 받아 Play Console 내부 테스트 트랙에 최초 한 번 수동 업로드합니다. Google Play API는 콘솔에 패키지가 존재하기 전에는 자동 업로드할 수 없습니다. -
Android Publisher API, 서비스 계정, Workload Identity Federation을 연결하고 GitHub Environment variables를 등록합니다.
-
이후 버전부터 아래 자동 배포 절차를 사용합니다.
- 릴리스할
main커밋에vMAJOR.MINOR.PATCH태그를 push합니다. Android Release Build가 성공하고 Release에 APK가, 서명 Secrets가 있다면 AAB도 첨부됐는지 확인합니다. 워크플로 마지막 단계가 APK 첨부 여부를 직접 검사하므로, 자산 없이 성공하는 일은 없습니다.- GitHub Actions의
Publish to Google Play Internal을 열고Run workflow에서 동일한 태그를 입력합니다. google-play-internal승인을 거치면 같은 AAB가 내부 테스트 트랙에completed상태로 게시됩니다.- Play 업로드가 성공하면 해당 Release가 최신 정식 릴리스로 표시됩니다.
태그 vMAJOR.MINOR.PATCH는 Unity bundleVersion의 MAJOR.MINOR.PATCH가 됩니다. Android versionCode는 MAJOR × 1,000,000 + MINOR × 1,000 + PATCH로 계산됩니다.
Release는 자산을 모두 첨부한 뒤에만 공개됩니다. 자산 교체 중에는 Release를 draft로 되돌리므로, 업로드가 실패해도 자산이 빠진 Release가 노출되지 않고 같은 태그로 재실행할 수 있습니다. 이미 공개된 정식 Release는 덮어쓸 수 없으므로, 수정이 필요하면 patch 버전을 올려 새 태그를 만듭니다.
과거에 추적된 KeyStore/Tessellation.keystore는 폐기됐습니다. 이 키는 public 히스토리에 노출됐으므로 절대 재사용하지 않습니다. 현재 업로드 키는 2026-08-16에 새로 생성해 Secrets에 등록한 별개의 키입니다.
2026-08-16에 아래 절차로 히스토리를 정리했습니다.
# 1. mirror clone 과 백업 (되돌릴 유일한 수단)
git clone --mirror https://github.com/TeamTessellation/Tessellation.git tess-mirror.git
Copy-Item -Recurse tess-mirror.git tess-backup.git
# 2. 재작성
cd tess-mirror.git
git filter-repo --invert-paths --path KeyStore/Tessellation.keystore --force
# 3. main 룰셋의 non_fast_forward 를 잠시 disabled 로 내린 뒤
git remote add origin https://github.com/TeamTessellation/Tessellation.git
git push --force --mirror origin
# 4. 룰셋을 active 로 되돌린다--sensitive-data-removal 플래그는 git-filter-repo 최신 버전에 없습니다. 위 형태를 사용합니다.
주의할 점이 세 가지 있습니다.
refs/pull/*는 재작성되지 않습니다. GitHub이 쓰기를 막는 관리 refs라 push가 거부되고, 옛 커밋이 그대로 고정됩니다. 그래서 새로 clone하면 유출 파일이 없지만, blob SHA를 직접 지정하면 API로 여전히 접근됩니다. 완전히 지우려면 GitHub Support에 unreachable object 정리를 요청해야 합니다.- 태그 force push 는
Android Release Build를 다시 트리거합니다. 기존 릴리스의 prerelease 상태가 덮어써질 수 있으니, push 직후 해당 실행을 취소합니다. - 모든 commit SHA가 바뀝니다. 관리자 한 명이 공지 후 진행하고, 완료 뒤 전원이 새로 clone합니다. 기존 clone의 로컬 브랜치는 옛 히스토리를 가리키므로, 그대로 push하면 유출이 되살아납니다.
키 값, 비밀번호, base64 문자열은 이슈·PR·Actions 로그에 남기지 않습니다.
- EditMode: 활성화된 빌드 씬의 존재, 첫 로딩 씬, Android applicationId, ARM64 설정 검증
- PlayMode:
InitialLoadingScene로드 smoke test - 실제 릴리스 전:
main에서 생성된 Release APK를 Android 기기에 설치해 시작 화면 진입 확인
로컬 Unity Test Runner에서도 EditMode와 PlayMode를 모두 통과시킨 뒤 PR을 올리는 것을 권장합니다.