You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
#735와 Draft PR #741은 Play App Signing, 출시 런북, AAB 검증을 준비하지만 Google Play에 실제로 올리는 CD는 포함하지 않는다. Console·계정·서명 준비가 끝난 뒤 production 자동 승격 없이 내부 테스트 트랙만 자동화한다.
개발자 계정 초대를 받아 콘솔에 들어가 봤습니다. 워크플로(release-play-internal.yml)는 서 있고, 남은 것은 전부 콘솔과 자격 쪽입니다. 지금은 그중 첫 칸에서 막혀 있습니다.
확인한 것
상태
개발자 계정
개인 계정으로 존재
신원 확인
Google 심사 중. 문서 업로드됨, 며칠 소요 안내
연락처 전화번호 인증
미완료. 계정 소유자만 가능
앱 등록
버튼 잠김. "새 앱을 만들려면 계정 확인을 완료하세요"
패키지 이름 등록
같은 사유로 불가
초대받은 FE 계정 권한
"사용자 및 권한" 이 "권한이 필요함" 으로 막힘, API 액세스 경로도 안 열림
이슈 본문의 사전 준비 1번(앱 등록)부터 대기라 2~5번이 전부 그 뒤에 있습니다. 신원 확인은 기다리는 것 말고 할 일이 없고, 전화번호 인증과 서비스 계정 권한 부여는 계정 소유자 몫입니다.
FE 쪽에서 미리 만들어 둘 수 있는 것도 지금은 없습니다. 첫 AAB 는 release keystore 가 있어야 하는데 그 자격은 배포 담당자만 갖고 있고, 없으면 :app:bundleRelease 가 checkReleaseSigningForRelease 에서 끊깁니다.
실측 내용은 러너북에 반영했습니다: #1909 (Closes #1908). 확인 경로를 같이 적어 뒀으니 다음에 상태를 볼 때 같은 화면을 다시 찾지 않아도 됩니다.
계정 확인이 끝나면 앱을 만들고 App integrity 에서 Play App Signing 등록 상태와 Automatic integrity protection 제공 여부를 보는 것이 다음 칸입니다. 첫 AAB 수동 업로드에서 서명 방식이 확정되므로 그 전에 러너북의 "Play App Signing 키 결정" 을 읽고 골라야 합니다.
이 이슈에서 지금 코드로 뗄 독립 범위가 없어 기록만 남깁니다. 저장소 몫은 이미 develop 에 서 있고, 남은 항목은 전부 Play Console 과 자격입니다. 그 사이에 versionCode 대역 작업이 이 이슈의 첫 업로드 숫자를 바꿔 놓았습니다. 다음에 잡는 사람이 이미 있는 것을 다시 만들거나 낡은 숫자로 시작하지 않도록 네 가지를 적습니다.
연결값 3종 누락 검사, PLAY_PACKAGE_NAME 을 applicationId 와 대조
L120-145
조회용 토큰 발급, Play 현재 최댓값 조회, 빌드 전 versionCode 확정
L147-174
main 병합 PR 본문에서 릴리스 노트 생성
L193-224
서명 검증, attest, digest 재확인
L228-253
업로드 직전 토큰 재발급 후 게시
L255-273
track, versionCode, source SHA, digest, attestation, edit id 기록
L276-288
산출물과 서명 설정 정리
.github/scripts/play-internal-track.mjs (281줄) 의 publishInternalBundle (L158-214) 이 edit 생성, 최댓값 재조회, 업로드, internal track 갱신, commit 과 실패 시 edit 정리를 담습니다. L10 의 INTERNAL_TRACK = "internal" 말고 production, open, closed 경로는 파일 어디에도 없습니다.
완료 조건 2 의 네 실패 모드는 이미 테스트로 고정돼 있습니다.
같은 versionCode 재업로드: play-internal-track.test.mjs L157, 그리고 L174-197 의 파라미터 케이스 3종(더 큰 bundle, 같은 track 값, 다른 track 의 더 큰 값)
권한 부족 403: 같은 파일 L199
API commit 실패: 같은 파일 L257
잘못된 서명: scripts/verify-play-release-bundle.sh L52-57 의 jarsigner -strict 비트 판정, release-aab-negative-fixtures.test.mjs L106 이 그 판정을 무력화하는 변이를 거부
잠그는 테스트는 release-play-internal-policy.test.mjs 16건과 play-internal-track.test.mjs 16건입니다(후자 중 하나가 3케이스로 펼쳐집니다). 여기에 play-publish-guards-mutation.test.mjs 가 변이 9종으로 그 테스트들이 실제로 잡는지까지 확인합니다.
PLAY_PACKAGE_NAME = com.afternote.afternote_fe, app/build.gradle.kts:39 의 applicationId 와 일치
environment secrets
0건
워크플로가 필요로 하는 secret 은 여섯 개(GCP_WORKLOAD_IDENTITY_PROVIDER, GCP_PLAY_SERVICE_ACCOUNT, RELEASE_STORE_FILE_B64, RELEASE_STORE_PASSWORD, RELEASE_KEY_ALIAS, RELEASE_KEY_PASSWORD)이고 그중 하나도 등록돼 있지 않습니다. 저장소 secret 은 KAKAO_NATIVE_APP_KEY, GOOGLE_WEB_CLIENT_ID, GOOGLE_SERVICES_JSON_B64 세 개뿐이라 이 여섯 개를 덮지 못합니다.
여기서 오해하기 쉬운 자리가 두 군데입니다. 첫째, 같은 이름 다섯 개(RELEASE_* 넷과 GCP_WORKLOAD_IDENTITY_PROVIDER)가 release-distribution environment secret 으로 존재하지만 다른 environment 이므로 play-internal job 에서는 보이지 않고, GitHub API 가 값을 돌려주지 않아 복사도 자동화도 불가능합니다. 값을 쥔 사람이 직접 재입력해야 합니다. 둘째, GCP_PLAY_SERVICE_ACCOUNT 는 저장소 어디에도 없습니다. release-distribution 에 있는 것은 GCP_FIREBASE_SERVICE_ACCOUNT 이고, 이 이슈 범위가 Play 권한을 Firebase service account 와 분리하라고 못박고 있으므로 그 값을 가져다 쓰는 것은 범위 위반입니다. 새 service account 를 만들어야 합니다.
즉 0906 코멘트의 서술이 일주일 뒤에도 그대로입니다. 항목과 위치, 값 형식은 docs/play-release.md 의 "자동화 사전 준비" 5절 표가 정본입니다.
3. versionCode 대역이 이 이슈의 첫 업로드 절차를 바꾼다
열린 PR #2073 (Closes #2062, base 는 #2060 의 docs/1865-version-code-policy) 이 version-code-bands.mjs 를 새로 만들고 PLAY_BAND_FLOOR = 1_000_000_000n 을 Play resolver 에 더합니다. 산출식이 PLAY_BAND_FLOOR + run_number * 100 + run_attempt 가 되므로 워크플로가 만드는 첫 값이 101 에서 1000000101 로 바뀝니다.
부트스트랩 업로드 자체는 그대로입니다. AFTERNOTE_VERSION_CODE 없이 빌드해 versionCode 1 로 수동 업로드하는 절차는 유효하고, 단조 증가 조건도 자동으로 만족합니다. 다만 이 이슈 0831 코멘트의 "워크플로의 첫 값 101" 은 #2073 이 머지되는 순간 거짓이 됩니다. PR #2073 은 러너북만 고치고 이슈 코멘트는 고칠 수 없으므로, 콘솔이 열려 첫 수동 업로드를 할 사람은 이 이슈가 아니라 갱신된 docs/play-release.md 를 보십시오.
이슈 범위의 "versionCode가 모든 배포에서 단조 증가" 중 Firebase 채널 몫도 이 이슈가 아닙니다. #1865 로 갈라져 나가 #2062 가 resolve-firebase-version-code.mjs 신설로 닫습니다(#2062 의 blocked_by 는 #1989 와 #1865). #852 에서 다시 손대면 중복 착수입니다. 열린 PR 62건의 본문을 훑었고 #852 를 물고 있는 PR 은 0건입니다.
4. 완료 조건 5 의 blocked 상태를 표현할 수단이 없다
0913 기준 이 이슈의 REST dependencies 는 blocked_by, blocking 모두 0건이고, 저장소 라벨에 blocked 계열이 없습니다(awaiting-author 하나뿐). 차단자가 GitHub 이슈가 아니라 외부(Google 신원 심사, 계정 소유자의 전화번호 인증)라 blocked_by 로 걸 대상 자체가 없습니다. 의존성 그래프를 뒤져도 나오지 않으니 이 코멘트를 상태로 보시면 됩니다.
5. 본문과 기존 코멘트에서 develop 실태와 어긋나는 항목
본문은 고치지 않았습니다. 아래 항목은 완료 조건을 좁히는 편집이 아니라 현재 상태 서술이고, #2062 계열이 아직 머지되지 않아 범위 이관을 본문에 박기에도 이릅니다. 정정만 적어 둡니다.
범위의 "play-internal 전용 GitHub Environment와 required reviewer를 구성한다": 0906 에 끝났습니다. 위 2번이 실측값입니다. 다만 prevent_self_review 가 꺼져 있어 실행자가 자기 배포를 스스로 승인할 수 있습니다. 이 승인은 남의 확인이 아니라 실행자의 두 번째 확인입니다.
범위의 "workflow_dispatch + 승인 방식으로 signed AAB를 내부 테스트 트랙에만 업로드한다" 와 "API edit 생성 → bundle upload → track update → commit ... 실패 시 미완료 edit 정리": 위 1번대로 구현돼 있고 정책 테스트가 잠그고 있습니다. 남은 것은 실행이지 구현이 아닙니다.
범위의 "commit/issue 기반 release note": 구현은 됐지만 범위 해석이 본문 뉘앙스와 다릅니다. play-internal-track.mjs L32-38 의 parseIssueReferences 는 "포함 이슈" 섹션이나 Closes 로 좁히지 않고 main 병합 PR 본문 전체에서 #N 을 전부 긁습니다. 릴리스 노트에 의도하지 않은 참조 번호가 섞일 수 있습니다.
완료 조건 5 의 "이슈를 blocked 상태로 두되": 표현 수단이 없습니다(위 4번).
0906·0913 코멘트에서 첫 칸이던 계정 확인이 풀렸습니다. 지금은 앱 등록부터 할 수 있습니다. 초대받은 FE 계정으로 읽기만 했고 아무것도 만들거나 제출하지 않았습니다.
항목
0913
1005
연락처 전화번호 인증
미완료
완료(연락처·개발자 이메일도 인증됨)
Google 신원 확인
심사 중
개발자 계정 화면에 심사 중·확인 요구 배너가 없음. 「확인 완료」 문구를 직접 본 것은 아니고, 앱 생성이 열린 것으로 통과를 판단함
앱 만들기
잠김
잠금 안내가 사라지고 생성 양식(앱 이름·패키지 이름·기본 언어)이 열림
등록된 앱
0
0. 그래서 App integrity·Play App Signing 화면은 아직 없음
FE 계정의 사용자 및 권한
권한이 필요함
그대로 권한이 필요함
play-internal environment secrets
0/6
0/6
화면 캡처는 확인한 로컬 세션에만 있어 여기 붙이지 못했습니다.
남은 순서
앱 등록은 PM 몫입니다. FE 는 손대지 않습니다. 패키지 이름은 com.afternote.afternote_fe(app/build.gradle.kts 의 applicationId, environment 변수 PLAY_PACKAGE_NAME 과 같은 값)여야 워크플로의 대조 검사를 통과하고, 등록 뒤에는 바꿀 수 없습니다.
첫 AAB 수동 업로드. 이 업로드에서 Play App Signing 방식이 확정되고 되돌릴 수 없으니 docs/play-release.md 의 「Play App Signing 키 결정」을 먼저 읽습니다. release keystore 가 있어야 빌드가 되므로 배포 담당자가 할 칸입니다.
서비스 계정 초대. FE 계정은 사용자 및 권한에 들어가지 못하므로, 계정 소유자가 직접 초대하거나 FE 계정에 관리자 권한을 줘야 합니다.
시크릿 6개 등록(docs/play-release.md 「자동화 사전 준비」 5절 표).
저장소 코드로 할 일은 여전히 없습니다. 이 이슈는 1번 앱 등록을 기다립니다. 1~4가 끝나면 워크플로 첫 실행으로 완료 조건 1을 확인합니다.
계정 소유자와 연락이 닿지 않아 Play Console 쪽 남은 칸(앱 등록, 서비스 계정 초대)을 진행하지 않기로 했습니다. 서비스 계정이 Play Console 에 초대되지 않으면 시크릿을 채워도 워크플로가 업로드 단계에서 권한 부족으로 멈추므로, FE 쪽에서 더 할 수 있는 일이 없어 보류로 닫습니다.
저장소 몫(release-play-internal.yml, play-internal-track.mjs, 정책 테스트, docs/play-release.md)은 develop 에 그대로 둡니다. 다시 열 조건은 계정 소유자가 앱을 등록하고 서비스 계정 초대(또는 FE 계정에 관리자 권한 부여)를 할 수 있게 되는 것이고, 그때 순서는 바로 위 1005 코멘트의 1~4 입니다.
다시 열 때 하나 더 필요합니다(2026-10-05 gcloud 로 읽기만 해서 확인). docs/play-release.md 는 기존 Workload Identity provider 를 재사용하라고 적고 있는데, 그 provider 의 attribute condition 이 workflow_ref 를 firebase-wif-canary.yml·release-distribution.yml 로만 허용합니다. release-play-internal.yml 이 빠져 있어 같은 provider 경로를 play-internal 시크릿에 넣어도 토큰 교환이 거부됩니다. 조건에 이 워크플로를 더하거나 Play 전용 provider 를 따로 만들어야 합니다.
마지막 줄은 provider 에 Play 워크플로를 더하면서 생긴 구멍을 막은 것입니다. 저장소 전체로 묶여 있으면 Play 워크플로가 Firebase 계정도 빌릴 수 있어 두 채널의 자격 분리가 깨집니다.
확인이 남은 점: afternote-ci 를 좁힌 뒤 Firebase 배포가 실제로 도는지는 다음 main push 의 release-distribution.yml 인증 단계에서 드러납니다. 실패하면 principalSet://iam.googleapis.com/projects/918752702102/locations/global/workloadIdentityPools/github-actions/attribute.repository/Afternote/Afternote-FE 를 같은 역할로 다시 더하면 원래대로 돌아갑니다.
시크릿 값 중 비밀이 아닌 두 개(GCP_WORKLOAD_IDENTITY_PROVIDER, GCP_PLAY_SERVICE_ACCOUNT)는 1Password 항목 Afternote GCP Workload Identity 에 적어 두었습니다. docs/play-release.md 3절 4번(기존 provider 재사용) 문구는 조건 추가와 워크플로 단위 바인딩을 반영해 따로 고칩니다.
Activity
ci(release): prepare Play versionCode policy (#852)
Merge pull request #856 from Afternote/feat/852
[-]release(cd): Google Play 내부 테스트 트랙 배포 자동화[/-][+]Google Play 내부 테스트 트랙 배포 자동화[/+]OnlyReadOneBook commentedon Aug 30, 2026
자동화 쪽은 세웠고, 남은 건 웹 UI 작업이다 — PR #1593
이 이슈에서 Play Console 없이 만들 수 있는 부분을 먼저 세웠다. 되돌리기 어려운 외부 작업(실제 Play 업로드, 서비스 계정 생성, 시크릿 등록)은 하지 않았다.
실측한 현재 상태
versionCode1고정 (build-logic/src/main/kotlin/VersionCode.kt의DEFAULT_AFTERNOTE_VERSION_CODE)AFTERNOTE_VERSION_CODE주입 워크플로resolve-play-version-code.mjs는 dd09f66에 들어왔지만 부르는 곳이 없었다release-distribution.yml은 Firebase App Distribution APK 경로)release-distribution,copilot둘뿐 —play-internal없음세운 정책
1. 이 채널은 versionCode로 빌드를 구분하지 않아 올릴 이유가 없다.run_number * 100 + run_attempt. Play로 가는 경로가 새 워크플로 하나뿐이라run_number만으로 단조 증가가 성립하고, 재실행은run_attempt로 갈린다.만든 것 (PR #1593)
.github/workflows/release-play-internal.yml—main에서 수동 실행 +play-internalenvironment 승인으로만 도는 경로.production/open/closed트랙으로 가는 step은 없다..github/scripts/play-internal-track.mjs— Android Publisher v3 edit 수명주기(생성 → 업로드 → internal 트랙 → commit), 실패 시 미완료 edit 정리.docs/play-release.md/docs/release/distribution.md— versionCode 정책, 사전 준비, 실행, 실패 모드, 롤백, Firebase 채널과의 경계표.사용자가 눌러야 하는 것
1. Play Console — 앱 등록 (
play.google.com/console)Afternote, 기본 언어 한국어, 유형앱, 무료.2. Play Console — 첫 AAB 수동 업로드 (API 제약)
Android Publisher API는 Console에서 최소 한 번 수동 업로드된 앱에만 업로드를 허용한다. 자동화의 선행 조건이다.
./scripts/verify-play-release-bundle.sh로 만든 AAB를 테스트 및 출시 → 내부 테스트 → 새 버전 만들기에서 직접 올린다.AFTERNOTE_VERSION_CODE없이 빌드해 versionCode1을 쓴다 → 워크플로의 첫 값101이 안전하게 크다.docs/play-release.md의 「Play App Signing 키 결정」을 먼저 읽는다.3. Google Cloud (
console.cloud.google.com)Google Play Android Developer API사용 설정.play-internal-publisher. 프로젝트 IAM 역할은 주지 않는다.roles/iam.workloadIdentityUser를 이 저장소로 한정해 부여.4. Play Console — service account 최소 권한
설정 → API 액세스에서 위 GCP 프로젝트 연결 → 사용자 및 권한 → 사용자 초대로 SA 이메일 추가.
Afternote하나만.5. GitHub —
play-internalenvironmentSettings → Environments → New environment →
play-internalSelected branches→mainGCP_WORKLOAD_IDENTITY_PROVIDERprojects/<번호>/locations/global/workloadIdentityPools/<pool>/providers/<provider>GCP_PLAY_SERVICE_ACCOUNTplay-internal-publisher@<프로젝트>.iam.gserviceaccount.comRELEASE_STORE_FILE_B64RELEASE_STORE_PASSWORDRELEASE_KEY_ALIASRELEASE_KEY_PASSWORDPLAY_PACKAGE_NAMEcom.afternote.afternote_feKAKAO_NATIVE_APP_KEY·GOOGLE_WEB_CLIENT_ID·GOOGLE_SERVICES_JSON_B64는 이미 저장소 secret으로 있어 그대로 쓴다.6. 첫 실행
Actions → Release Play Internal Track → Run workflow → 브랜치
main. 승인 뒤 job이 시작된다.Firebase App Distribution과의 경계 (뭉개지 않는다)
mainpush 자동main수동 실행 + 승인1고정Play 출시 이후에도 Firebase는 기존 QA 채널로 유지한다.
release-distribution.yml은 이 PR에서 한 글자도 바뀌지 않았다.#1539 해제 조건
이 워크플로가 실제로 한 번 성공해 Play에
1보다 큰 versionCode가 올라간 시점부터 강제 업데이트 배선이 기준값을 가질 수 있다. 위 1~6이 그 선행 조건이다.ci(platform): Play 내부 테스트 트랙 배포 파이프라인을 세운다 (#852) (#1593)
OnlyReadOneBook commentedon Sep 6, 2026
콘솔 쪽 사전 준비 상태 (2026-09-06 실측)
개발자 계정 초대를 받아 콘솔에 들어가 봤습니다. 워크플로(
release-play-internal.yml)는 서 있고, 남은 것은 전부 콘솔과 자격 쪽입니다. 지금은 그중 첫 칸에서 막혀 있습니다.이슈 본문의 사전 준비 1번(앱 등록)부터 대기라 2~5번이 전부 그 뒤에 있습니다. 신원 확인은 기다리는 것 말고 할 일이 없고, 전화번호 인증과 서비스 계정 권한 부여는 계정 소유자 몫입니다.
FE 쪽에서 미리 만들어 둘 수 있는 것도 지금은 없습니다. 첫 AAB 는 release keystore 가 있어야 하는데 그 자격은 배포 담당자만 갖고 있고, 없으면
:app:bundleRelease가checkReleaseSigningForRelease에서 끊깁니다.실측 내용은 러너북에 반영했습니다: #1909 (Closes #1908). 확인 경로를 같이 적어 뒀으니 다음에 상태를 볼 때 같은 화면을 다시 찾지 않아도 됩니다.
계정 확인이 끝나면 앱을 만들고 App integrity 에서 Play App Signing 등록 상태와 Automatic integrity protection 제공 여부를 보는 것이 다음 칸입니다. 첫 AAB 수동 업로드에서 서명 방식이 확정되므로 그 전에 러너북의 "Play App Signing 키 결정" 을 읽고 골라야 합니다.
OnlyReadOneBook commentedon Sep 13, 2026
실측 기록 (2026-09-13,
origin/develop7d87aa3)이 이슈에서 지금 코드로 뗄 독립 범위가 없어 기록만 남깁니다. 저장소 몫은 이미 develop 에 서 있고, 남은 항목은 전부 Play Console 과 자격입니다. 그 사이에 versionCode 대역 작업이 이 이슈의 첫 업로드 숫자를 바꿔 놓았습니다. 다음에 잡는 사람이 이미 있는 것을 다시 만들거나 낡은 숫자로 시작하지 않도록 네 가지를 적습니다.
1. 저장소 몫이 어디까지 닫혔나
7d87aa3 기준으로 파일과 줄을 짚습니다. 재구현 대상이 아닙니다.
.github/workflows/release-play-internal.yml(288줄)workflow_dispatch단독environment: play-internalmain이 아닌 ref 에서 실행 거부PLAY_PACKAGE_NAME을applicationId와 대조main병합 PR 본문에서 릴리스 노트 생성.github/scripts/play-internal-track.mjs(281줄) 의publishInternalBundle(L158-214) 이 edit 생성, 최댓값 재조회, 업로드, internal track 갱신, commit 과 실패 시 edit 정리를 담습니다. L10 의INTERNAL_TRACK = "internal"말고 production, open, closed 경로는 파일 어디에도 없습니다.완료 조건 2 의 네 실패 모드는 이미 테스트로 고정돼 있습니다.
play-internal-track.test.mjsL157, 그리고 L174-197 의 파라미터 케이스 3종(더 큰 bundle, 같은 track 값, 다른 track 의 더 큰 값)scripts/verify-play-release-bundle.shL52-57 의 jarsigner-strict비트 판정,release-aab-negative-fixtures.test.mjsL106 이 그 판정을 무력화하는 변이를 거부잠그는 테스트는
release-play-internal-policy.test.mjs16건과play-internal-track.test.mjs16건입니다(후자 중 하나가 3케이스로 펼쳐집니다). 여기에play-publish-guards-mutation.test.mjs가 변이 9종으로 그 테스트들이 실제로 잡는지까지 확인합니다.2. GitHub 쪽 실태 (0913 API 실측)
play-internalrequired_reviewers=1hyok,prevent_self_review= falsemain한 개PLAY_PACKAGE_NAME=com.afternote.afternote_fe,app/build.gradle.kts:39의 applicationId 와 일치워크플로가 필요로 하는 secret 은 여섯 개(
GCP_WORKLOAD_IDENTITY_PROVIDER,GCP_PLAY_SERVICE_ACCOUNT,RELEASE_STORE_FILE_B64,RELEASE_STORE_PASSWORD,RELEASE_KEY_ALIAS,RELEASE_KEY_PASSWORD)이고 그중 하나도 등록돼 있지 않습니다. 저장소 secret 은KAKAO_NATIVE_APP_KEY,GOOGLE_WEB_CLIENT_ID,GOOGLE_SERVICES_JSON_B64세 개뿐이라 이 여섯 개를 덮지 못합니다.여기서 오해하기 쉬운 자리가 두 군데입니다. 첫째, 같은 이름 다섯 개(
RELEASE_*넷과GCP_WORKLOAD_IDENTITY_PROVIDER)가release-distributionenvironment secret 으로 존재하지만 다른 environment 이므로 play-internal job 에서는 보이지 않고, GitHub API 가 값을 돌려주지 않아 복사도 자동화도 불가능합니다. 값을 쥔 사람이 직접 재입력해야 합니다. 둘째,GCP_PLAY_SERVICE_ACCOUNT는 저장소 어디에도 없습니다.release-distribution에 있는 것은GCP_FIREBASE_SERVICE_ACCOUNT이고, 이 이슈 범위가 Play 권한을 Firebase service account 와 분리하라고 못박고 있으므로 그 값을 가져다 쓰는 것은 범위 위반입니다. 새 service account 를 만들어야 합니다.즉 0906 코멘트의 서술이 일주일 뒤에도 그대로입니다. 항목과 위치, 값 형식은
docs/play-release.md의 "자동화 사전 준비" 5절 표가 정본입니다.3. versionCode 대역이 이 이슈의 첫 업로드 절차를 바꾼다
열린 PR #2073 (
Closes #2062, base 는 #2060 의docs/1865-version-code-policy) 이version-code-bands.mjs를 새로 만들고PLAY_BAND_FLOOR = 1_000_000_000n을 Play resolver 에 더합니다. 산출식이PLAY_BAND_FLOOR + run_number * 100 + run_attempt가 되므로 워크플로가 만드는 첫 값이101에서1000000101로 바뀝니다.부트스트랩 업로드 자체는 그대로입니다.
AFTERNOTE_VERSION_CODE없이 빌드해 versionCode1로 수동 업로드하는 절차는 유효하고, 단조 증가 조건도 자동으로 만족합니다. 다만 이 이슈 0831 코멘트의 "워크플로의 첫 값101" 은 #2073 이 머지되는 순간 거짓이 됩니다. PR #2073 은 러너북만 고치고 이슈 코멘트는 고칠 수 없으므로, 콘솔이 열려 첫 수동 업로드를 할 사람은 이 이슈가 아니라 갱신된docs/play-release.md를 보십시오.이슈 범위의 "versionCode가 모든 배포에서 단조 증가" 중 Firebase 채널 몫도 이 이슈가 아닙니다. #1865 로 갈라져 나가 #2062 가
resolve-firebase-version-code.mjs신설로 닫습니다(#2062 의blocked_by는 #1989 와 #1865). #852 에서 다시 손대면 중복 착수입니다. 열린 PR 62건의 본문을 훑었고#852를 물고 있는 PR 은 0건입니다.4. 완료 조건 5 의 blocked 상태를 표현할 수단이 없다
0913 기준 이 이슈의 REST dependencies 는
blocked_by,blocking모두 0건이고, 저장소 라벨에 blocked 계열이 없습니다(awaiting-author하나뿐). 차단자가 GitHub 이슈가 아니라 외부(Google 신원 심사, 계정 소유자의 전화번호 인증)라blocked_by로 걸 대상 자체가 없습니다. 의존성 그래프를 뒤져도 나오지 않으니 이 코멘트를 상태로 보시면 됩니다.5. 본문과 기존 코멘트에서 develop 실태와 어긋나는 항목
본문은 고치지 않았습니다. 아래 항목은 완료 조건을 좁히는 편집이 아니라 현재 상태 서술이고, #2062 계열이 아직 머지되지 않아 범위 이관을 본문에 박기에도 이릅니다. 정정만 적어 둡니다.
prevent_self_review가 꺼져 있어 실행자가 자기 배포를 스스로 승인할 수 있습니다. 이 승인은 남의 확인이 아니라 실행자의 두 번째 확인입니다.play-internal-track.mjsL32-38 의parseIssueReferences는 "포함 이슈" 섹션이나Closes로 좁히지 않고 main 병합 PR 본문 전체에서#N을 전부 긁습니다. 릴리스 노트에 의도하지 않은 참조 번호가 섞일 수 있습니다.blocked_by가 해제됐고 2026-09-07 에 완료로 닫혔습니다. 이 이슈가 GET /app/version 이 배선 0건 — 강제 업데이트를 붙이거나 죽은 서비스를 걷는다 #1539 를 막고 있지 않습니다.OnlyReadOneBook commentedon Oct 5, 2026
콘솔 상태 갱신 (2026-10-05 실측)
0906·0913 코멘트에서 첫 칸이던 계정 확인이 풀렸습니다. 지금은 앱 등록부터 할 수 있습니다. 초대받은 FE 계정으로 읽기만 했고 아무것도 만들거나 제출하지 않았습니다.
play-internalenvironment secrets화면 캡처는 확인한 로컬 세션에만 있어 여기 붙이지 못했습니다.
남은 순서
com.afternote.afternote_fe(app/build.gradle.kts의 applicationId, environment 변수PLAY_PACKAGE_NAME과 같은 값)여야 워크플로의 대조 검사를 통과하고, 등록 뒤에는 바꿀 수 없습니다.docs/play-release.md의 「Play App Signing 키 결정」을 먼저 읽습니다. release keystore 가 있어야 빌드가 되므로 배포 담당자가 할 칸입니다.docs/play-release.md「자동화 사전 준비」 5절 표).저장소 코드로 할 일은 여전히 없습니다. 이 이슈는 1번 앱 등록을 기다립니다. 1~4가 끝나면 워크플로 첫 실행으로 완료 조건 1을 확인합니다.
OnlyReadOneBook commentedon Oct 5, 2026
계정 소유자와 연락이 닿지 않아 Play Console 쪽 남은 칸(앱 등록, 서비스 계정 초대)을 진행하지 않기로 했습니다. 서비스 계정이 Play Console 에 초대되지 않으면 시크릿을 채워도 워크플로가 업로드 단계에서 권한 부족으로 멈추므로, FE 쪽에서 더 할 수 있는 일이 없어 보류로 닫습니다.
저장소 몫(
release-play-internal.yml,play-internal-track.mjs, 정책 테스트,docs/play-release.md)은 develop 에 그대로 둡니다. 다시 열 조건은 계정 소유자가 앱을 등록하고 서비스 계정 초대(또는 FE 계정에 관리자 권한 부여)를 할 수 있게 되는 것이고, 그때 순서는 바로 위 1005 코멘트의 1~4 입니다.다시 열 때 하나 더 필요합니다(2026-10-05 gcloud 로 읽기만 해서 확인).
docs/play-release.md는 기존 Workload Identity provider 를 재사용하라고 적고 있는데, 그 provider 의 attribute condition 이workflow_ref를firebase-wif-canary.yml·release-distribution.yml로만 허용합니다.release-play-internal.yml이 빠져 있어 같은 provider 경로를play-internal시크릿에 넣어도 토큰 교환이 거부됩니다. 조건에 이 워크플로를 더하거나 Play 전용 provider 를 따로 만들어야 합니다.OnlyReadOneBook commentedon Oct 5, 2026
GCP 쪽 준비를 끝냈습니다 (2026-10-05)
바로 위 코멘트에서 다시 열 때 하겠다고 적은 GCP 설정은 계정 소유자와 무관해서 지금 처리했습니다. 남은 것은 Play Console 칸(앱 등록, 첫 AAB 업로드, 서비스 계정 초대)과 keystore 시크릿 4개입니다.
afternote-android에서 사용 설정play-internal-publisher@afternote-android.iam.gserviceaccount.com생성, 프로젝트 IAM 역할 없음afternote-feattribute conditionworkflow_ref허용 목록에release-play-internal.yml추가(다른 설정은 그대로)roles/iam.workloadIdentityUserattribute.workflow_ref/Afternote/Afternote-FE/.github/workflows/release-play-internal.yml@refs/heads/main하나에만 부여afternote-ci의 같은 역할attribute.repository)에서release-distribution.yml@main,firebase-wif-canary.yml@main,firebase-wif-canary.yml@develop셋으로 좁힘마지막 줄은 provider 에 Play 워크플로를 더하면서 생긴 구멍을 막은 것입니다. 저장소 전체로 묶여 있으면 Play 워크플로가 Firebase 계정도 빌릴 수 있어 두 채널의 자격 분리가 깨집니다.
확인이 남은 점:
afternote-ci를 좁힌 뒤 Firebase 배포가 실제로 도는지는 다음 main push 의release-distribution.yml인증 단계에서 드러납니다. 실패하면principalSet://iam.googleapis.com/projects/918752702102/locations/global/workloadIdentityPools/github-actions/attribute.repository/Afternote/Afternote-FE를 같은 역할로 다시 더하면 원래대로 돌아갑니다.시크릿 값 중 비밀이 아닌 두 개(
GCP_WORKLOAD_IDENTITY_PROVIDER,GCP_PLAY_SERVICE_ACCOUNT)는 1Password 항목Afternote GCP Workload Identity에 적어 두었습니다.docs/play-release.md3절 4번(기존 provider 재사용) 문구는 조건 추가와 워크플로 단위 바인딩을 반영해 따로 고칩니다.