Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
166 commits
Select commit Hold shift + click to select a range
bbb9158
fix(mindrecord): 홈 MEMORIES 카드·추억 공간을 실기록으로 연결, 돌아가기 인셋 처리
Sadturtleman Aug 3, 2026
ef8a7ef
fix(mindrecord): 추억 공간 조회 실패에 재시도 버튼을 연결한다
Sadturtleman Aug 13, 2026
80b121a
fix(mindrecord): 확정되지 않은 MEMORIES 카드 0건 문구·버튼 목적지를 제거한다
Sadturtleman Aug 13, 2026
aaef497
fix(mindrecord): 추억 공간에서 임시저장 일기를 제외한다
Sadturtleman Aug 13, 2026
6b17e5a
fix(mindrecord): 추억 공간 조회를 runCatchingCancellable 로 감싼다
Sadturtleman Aug 15, 2026
ecb648d
docs(mindrecord): MEMORIES 카드 주석을 명세 정렬 계약·0건 표시에 맞춘다
Sadturtleman Aug 17, 2026
a20749b
fix(mindrecord): 추억 공간 조회 실패에 예외 원문을 노출하지 않는다
Sadturtleman Aug 20, 2026
5d3485c
fix(mindrecord): 부분 조회 실패를 기록 0건으로 확정하지 않는다
Sadturtleman Aug 23, 2026
64fb893
fix(mindrecord): MEMORIES 카드 초기 조회 중복과 응답 경합을 막는다
Sadturtleman Aug 23, 2026
694d975
fix(mindrecord): 화면 배경을 시안 #FAFAFA 로 되돌린다
Sadturtleman Aug 23, 2026
460187f
fix(mindrecord): 탭 스트립 배경도 시안 색으로 맞춘다
Sadturtleman Aug 23, 2026
ac76289
fix(mindrecord): data 계층이 취소를 Result 로 삼키지 않게 한다
Sadturtleman Aug 23, 2026
dd4912e
fix(mindrecord): 홈 TODAY'S QUESTION 카드의 답변 CTA 를 연결한다
Sadturtleman Aug 23, 2026
c96abab
fix(mindrecord): 작성 툴바의 임시저장 카운트를 실제 개수로 채운다
Sadturtleman Aug 23, 2026
7193641
fix(mindrecord): 임시저장 삭제 실패를 완료로 보고하지 않는다
Sadturtleman Aug 23, 2026
b8b6ae3
style(mindrecord): ktlint 포맷 정리
Sadturtleman Aug 23, 2026
6fadad2
docs(qa): 추측으로 진행한 항목 문서를 추가한다
Sadturtleman Aug 23, 2026
dd96843
fix(mindrecord): 주간리포트 요약 문구의 공백을 지킨다
Sadturtleman Aug 23, 2026
ff6a15e
fix(mindrecord): 일기 날짜 선택 제목을 '기록 날짜' 로 바꾼다
Sadturtleman Aug 23, 2026
bc0e9ae
fix(mindrecord): 주간 캘린더에서 이모지와 점을 함께 그리지 않는다
Sadturtleman Aug 23, 2026
5510583
fix(mindrecord): 날짜 파싱 실패를 오늘 날짜로 덮지 않는다
Sadturtleman Aug 23, 2026
8fd9bb4
feat(mindrecord): 데일리질문 목록 상단에 오늘의 추천 질문을 노출한다
Sadturtleman Aug 23, 2026
69c64d7
docs(qa): #716·#592·#751 추측 항목을 추가한다
Sadturtleman Aug 23, 2026
dbe8e39
fix(mindrecord): 데일리질문 생성·수정 응답의 식별자를 버리지 않는다
Sadturtleman Aug 23, 2026
ad74e7a
test(mindrecord): fake repository 를 새 Result<Long> 계약에 맞춘다
Sadturtleman Aug 23, 2026
406b3ca
refactor: DataStore 수명(scope) 계약 도입, 저장소별 clear 제거 (#912)
OnlyReadOneBook Aug 23, 2026
20502c8
Merge branch 'develop' into fix/573-daily-question-response-id
OnlyReadOneBook Aug 23, 2026
a6da1a6
Merge branch 'develop' into fix/592-today-recommended-question
OnlyReadOneBook Aug 23, 2026
628c1b5
Merge branch 'develop' into fix/751-date-parse-fallback
OnlyReadOneBook Aug 23, 2026
6db4e66
Merge branch 'develop' into fix/749-calendar-emoji-dot
OnlyReadOneBook Aug 23, 2026
0c4f617
Merge branch 'develop' into docs/qa-assumptions
OnlyReadOneBook Aug 23, 2026
bf7b977
refactor(app): 홈·수신자 홈 화면을 feature 모듈로 이전
OnlyReadOneBook Aug 23, 2026
ff4dc30
chore(build): 빌드·테스트 클래스패스 bouncycastle 하한을 1.84 로 상향
OnlyReadOneBook Aug 23, 2026
9f88772
test(app): HomeTabViewModelTest fake 를 새 Result<Long> 계약에 맞춘다
Sadturtleman Aug 23, 2026
ec06741
fix: 로그아웃·탈퇴 시 SESSION 스코프 로컬 저장소 일괄 정리 (#912)
OnlyReadOneBook Aug 23, 2026
99b49ae
Merge remote-tracking branch 'origin/develop' into fix/912
OnlyReadOneBook Aug 23, 2026
0c08cce
test: 제거된 clearAuthCode·clear 오버라이드를 androidTest fake 에서 정리 (#912)
OnlyReadOneBook Aug 23, 2026
36dea05
docs(qa): #789 기분 enum 모르는 값 처리 판단을 기록한다
Sadturtleman Aug 23, 2026
b765ddb
refactor(mindrecord): DTO 기본값을 걷어내 누락 계약이 파싱 실패로 드러나게 한다
Sadturtleman Aug 23, 2026
9503e9b
docs(qa): #729 과거 주간리포트 범위를 52주로 정한 근거를 기록한다
Sadturtleman Aug 23, 2026
98ac361
fix(mindrecord): 주차 드롭다운 선택지를 1년으로 넓히고 메뉴를 5개 높이로 묶는다
Sadturtleman Aug 23, 2026
52fee2a
Merge remote-tracking branch 'origin/develop' into refactor/546
OnlyReadOneBook Aug 23, 2026
eae987a
fix(app): develop 병합 정합 — 신규 androidTest 의 이전 경로·문자열 보정
OnlyReadOneBook Aug 23, 2026
ef21fe7
fix(mindrecord): 기록일수를 기록 종류로 거르지 않는다
Sadturtleman Aug 23, 2026
01a6a20
docs(qa): #725 부분 실패 처리·폴링 간격 판단을 기록한다
Sadturtleman Aug 23, 2026
e0df3af
fix(test): androidTest 를 현행 도메인 API·화면 계약으로 갱신
OnlyReadOneBook Aug 23, 2026
5493498
ci(unit-test): PR 단계에 androidTest 컴파일 게이트 추가
OnlyReadOneBook Aug 23, 2026
74125eb
fix(core): presigned URL 요청에 필수 contentLength 를 싣는다
Sadturtleman Aug 23, 2026
25b3676
chore(app): 역할 끝난 NavigationDebugLogger 삭제
OnlyReadOneBook Aug 23, 2026
85d6810
docs(qa): #547 날짜 파싱 실패 정책 판단을 기록한다
Sadturtleman Aug 23, 2026
3767e3d
refactor(app): core:startup 모듈을 삭제하고 초기화 로직을 app으로 통합한다
OnlyReadOneBook Aug 23, 2026
a6ffc43
refactor(core): core:di 모듈을 삭제하고 DI 설정을 각 모듈로 분산한다
OnlyReadOneBook Aug 23, 2026
9434cff
fix(afternote): 에디터 수신자 목록을 UserRepository 로 연결 — 죽은 AuthorReceiverRe…
OnlyReadOneBook Aug 23, 2026
625c17c
docs(qa): #618·#614 수신자 화면 구성·문구 판단을 기록한다
Sadturtleman Aug 24, 2026
16af94d
Merge pull request #917 from Afternote/refactor/546
OnlyReadOneBook Aug 24, 2026
31ee4b6
refactor(afternote): 수신자 흐름 전체를 :feature:receiver 로 이전
OnlyReadOneBook Aug 23, 2026
b0eda8a
fix(receiver): 이전 누락분 보정 — CI 클린 빌드에서만 드러난 3건
OnlyReadOneBook Aug 23, 2026
f936e3d
merge: refactor/546(develop 통합 반영) 병합 + 신규 androidTest 615 경로 정합
OnlyReadOneBook Aug 23, 2026
887e903
Merge pull request #919 from Afternote/refactor/615
OnlyReadOneBook Aug 24, 2026
6d2136c
ci(merge-order-guard): base 규칙을 실패에서 경고로 강등
OnlyReadOneBook Aug 24, 2026
495ebaf
Merge pull request #999 from Afternote/ci/merge-order-guard-stack-noise
OnlyReadOneBook Aug 24, 2026
45d7f5c
fix(mindrecord): 기록일수를 일기만으로 세지 않는다
Sadturtleman Aug 24, 2026
3a1b218
ci: PR 검증 workflow 겹침 해소 — concurrency 누락·build cache 전역 off
OnlyReadOneBook Aug 24, 2026
8ad2ca3
docs(qa): 깊은 생각 제거 후 홈 요약 그리드 판단을 기록한다
Sadturtleman Aug 24, 2026
08e3ac5
Merge branch 'develop' into fix/950-presigned-content-length
OnlyReadOneBook Aug 24, 2026
86e2a99
fix(ci): 릴리스·WIF canary 가 같은 Firebase 앱에 동시 업로드 가능 (#995)
OnlyReadOneBook Aug 24, 2026
68a1a01
merge: develop(#917·#919) 병합 — 수신자 DataStore 를 이관된 :feature:receiver …
OnlyReadOneBook Aug 24, 2026
e4740d3
merge: develop(#917·#919) 병합 — androidTest 를 이관된 :feature:receiver·:f…
OnlyReadOneBook Aug 24, 2026
41e30b3
chore(ci): 실서비스 secret workflow 3종의 검증·decode 3변종을 composite 로 통합 (#997)
OnlyReadOneBook Aug 24, 2026
32d7384
refactor(afternote): 추모 미디어 업로드 경로 통합 — Outcome 타입 제거, 사진·영상 구현 1개로
OnlyReadOneBook Aug 24, 2026
c406eba
Merge pull request #1000 from Afternote/ci/996-pr-verification-overlap
OnlyReadOneBook Aug 24, 2026
c5a957f
docs(qa): 상세 화면 4종에서 시안에 없어 판단한 값들을 기록한다
Sadturtleman Aug 24, 2026
825c946
Merge pull request #1001 from Afternote/ci/995-distribution-concurrency
OnlyReadOneBook Aug 24, 2026
7ffa34e
Merge branch 'develop' into ci/997-release-secret-composite
OnlyReadOneBook Aug 24, 2026
24ce139
docs(qa): 일기 수정 시 빈 수신자 선택의 해석을 기록한다
Sadturtleman Aug 24, 2026
eab1216
merge: develop(#999·#1000·#1001) 병합 — unit-test 워크플로 충돌 해소
OnlyReadOneBook Aug 24, 2026
095c7e4
Merge pull request #1002 from Afternote/ci/997-release-secret-composite
OnlyReadOneBook Aug 24, 2026
2eefa3d
Merge branch 'develop' into feat/559
OnlyReadOneBook Aug 24, 2026
df9ee38
Merge branch 'develop' into fix/691-mindrecord-scaffold-background
OnlyReadOneBook Aug 24, 2026
d888e13
Merge branch 'develop' into fix/670-mindrecord-cancellation
OnlyReadOneBook Aug 24, 2026
86a2a99
Merge branch 'develop' into fix/627-today-question-cta
OnlyReadOneBook Aug 24, 2026
f017604
Merge branch 'develop' into fix/769-draft-count
OnlyReadOneBook Aug 24, 2026
3e9f9b0
Merge branch 'develop' into fix/442-draft-delete-failure
OnlyReadOneBook Aug 24, 2026
7ad4e0c
Merge branch 'develop' into docs/qa-assumptions
OnlyReadOneBook Aug 24, 2026
4a15b30
Merge branch 'develop' into fix/732-weekly-summary-spacing
OnlyReadOneBook Aug 24, 2026
a8b0976
Merge branch 'develop' into fix/760-diary-date-title
OnlyReadOneBook Aug 24, 2026
9197e6c
Merge branch 'develop' into fix/749-calendar-emoji-dot
OnlyReadOneBook Aug 24, 2026
348a664
Merge branch 'develop' into fix/751-date-parse-fallback
OnlyReadOneBook Aug 24, 2026
e30f68d
Merge branch 'develop' into fix/592-today-recommended-question
OnlyReadOneBook Aug 24, 2026
4eef761
Merge branch 'develop' into chore/921
OnlyReadOneBook Aug 24, 2026
cf32158
Merge branch 'develop' into fix/912
OnlyReadOneBook Aug 24, 2026
c5e62ec
Merge branch 'develop' into refactor/789-dto-defaults
OnlyReadOneBook Aug 24, 2026
8b75a44
Merge branch 'develop' into fix/729-week-dropdown-scroll
OnlyReadOneBook Aug 24, 2026
a7d128a
Merge branch 'develop' into fix/590-recorded-days-nondiary
OnlyReadOneBook Aug 24, 2026
b796dd6
Merge branch 'develop' into fix/936
OnlyReadOneBook Aug 24, 2026
18e472f
Merge branch 'develop' into fix/950-presigned-content-length
OnlyReadOneBook Aug 24, 2026
2fa76e3
Merge branch 'develop' into chore/927
OnlyReadOneBook Aug 24, 2026
31adfd9
Merge branch 'develop' into refactor/929
OnlyReadOneBook Aug 24, 2026
9761efa
Merge branch 'develop' into refactor/930
OnlyReadOneBook Aug 24, 2026
9b058ae
Merge branch 'develop' into fix/935
OnlyReadOneBook Aug 24, 2026
d895867
Merge branch 'develop' into refactor/938
OnlyReadOneBook Aug 24, 2026
0b3b75c
Merge pull request #894 from Afternote/fix/627-today-question-cta
Sadturtleman Aug 24, 2026
5c5e9d4
Merge pull request #893 from Afternote/fix/670-mindrecord-cancellation
Sadturtleman Aug 24, 2026
b27e1dd
fix(ci): dependency-graph 두 job 의 correlator 를 통일한다
OnlyReadOneBook Aug 24, 2026
fba07ee
Merge pull request #895 from Afternote/fix/769-draft-count
Sadturtleman Aug 24, 2026
c9b8029
Merge pull request #889 from Afternote/fix/691-mindrecord-scaffold-ba…
Sadturtleman Aug 24, 2026
776e250
Merge pull request #898 from Afternote/fix/732-weekly-summary-spacing
Sadturtleman Aug 24, 2026
ff2e77a
Merge pull request #897 from Afternote/docs/qa-assumptions
Sadturtleman Aug 24, 2026
2b8c6f5
Merge pull request #909 from Afternote/fix/592-today-recommended-ques…
Sadturtleman Aug 24, 2026
ec050c2
Merge pull request #721 from Afternote/feat/559
Sadturtleman Aug 24, 2026
d779635
Merge pull request #907 from Afternote/fix/749-calendar-emoji-dot
Sadturtleman Aug 24, 2026
2c4e2dd
Merge pull request #946 from Afternote/fix/590-recorded-days-nondiary
Sadturtleman Aug 24, 2026
d5065ef
Merge pull request #896 from Afternote/fix/442-draft-delete-failure
Sadturtleman Aug 24, 2026
810a1d4
Merge pull request #899 from Afternote/fix/760-diary-date-title
Sadturtleman Aug 24, 2026
d6295c2
Merge pull request #953 from Afternote/fix/950-presigned-content-length
Sadturtleman Aug 24, 2026
b9c6606
fix(ci): CodeQL 에서 --build-cache 를 되돌린다 — 추출기가 컴파일러 호출을 관찰한다
OnlyReadOneBook Aug 24, 2026
c9753f2
fix(mindrecord): 임시저장 삭제 테스트를 loader 생성자에 맞춘다
Sadturtleman Aug 24, 2026
98bfdf7
Merge branch 'develop' into fix/729-week-dropdown-scroll
Sadturtleman Aug 24, 2026
00dba6b
fix(mindrecord): 임시저장 삭제 테스트를 loader 생성자에 맞춘다
Sadturtleman Aug 24, 2026
e26a766
Merge branch 'develop' into fix/751-date-parse-fallback
Sadturtleman Aug 24, 2026
f6c57d8
Merge pull request #933 from Afternote/refactor/789-dto-defaults
Sadturtleman Aug 24, 2026
fe998ac
fix(mindrecord): 날짜 미확정 항목 제외를 추억 공간·임시저장까지 맞춘다
Sadturtleman Aug 24, 2026
0342ac5
Merge branch 'develop' into fix/573-daily-question-response-id
Sadturtleman Aug 24, 2026
567db89
fix(mindrecord): 수신자 데일리질문의 isDraft 를 계약대로 파싱한다
Sadturtleman Aug 24, 2026
350cadf
Merge pull request #1013 from Afternote/fix/develop-draftlist-test-co…
Sadturtleman Aug 24, 2026
a04eafc
Merge branch 'develop' into fix/751-date-parse-fallback
Sadturtleman Aug 24, 2026
e9e7be6
Merge branch 'develop' into fix/729-week-dropdown-scroll
Sadturtleman Aug 24, 2026
ac443ab
Merge branch 'develop' into fix/573-daily-question-response-id
Sadturtleman Aug 24, 2026
f897372
Merge pull request #1015 from Afternote/fix/956-receiver-daily-questi…
Sadturtleman Aug 24, 2026
2b0a1be
Merge pull request #937 from Afternote/fix/729-week-dropdown-scroll
Sadturtleman Aug 24, 2026
9bfbf82
Merge pull request #911 from Afternote/fix/573-daily-question-respons…
Sadturtleman Aug 24, 2026
a26b039
Merge branch 'develop' into fix/751-date-parse-fallback
Sadturtleman Aug 24, 2026
a21d311
Merge pull request #908 from Afternote/fix/751-date-parse-fallback
Sadturtleman Aug 24, 2026
8a24cbd
Merge pull request #1011 from Afternote/ci/1009-dependency-graph-corr…
OnlyReadOneBook Aug 24, 2026
928d09e
Merge branch 'develop' into ci/996-codeql-no-build-cache
OnlyReadOneBook Aug 24, 2026
9bc9d19
Merge pull request #949 from Afternote/fix/936
OnlyReadOneBook Aug 24, 2026
e4c9495
Merge pull request #931 from Afternote/fix/912
OnlyReadOneBook Aug 24, 2026
ca69737
Merge pull request #922 from Afternote/chore/921
OnlyReadOneBook Aug 24, 2026
768dc1c
fix(build): 빌드 툴체인 보안 하한 9건과 해석 정합성 2건을 정렬
OnlyReadOneBook Aug 24, 2026
28283c2
Merge pull request #960 from Afternote/chore/927
OnlyReadOneBook Aug 24, 2026
2f59988
Merge pull request #968 from Afternote/refactor/929
OnlyReadOneBook Aug 24, 2026
ca9b749
Merge pull request #1005 from Afternote/refactor/938
OnlyReadOneBook Aug 24, 2026
55fa582
merge: develop 병합 — app 모듈 의존을 core:data 하나로 정리
OnlyReadOneBook Aug 24, 2026
0a19caa
merge: develop 병합 — 사진 업로드 통합·수신자 소스 교체 충돌 해소
OnlyReadOneBook Aug 24, 2026
78ce0be
Merge pull request #1003 from Afternote/fix/973
OnlyReadOneBook Aug 24, 2026
2fc4a61
Merge pull request #969 from Afternote/refactor/930
OnlyReadOneBook Aug 24, 2026
af3f461
Merge pull request #971 from Afternote/fix/935
OnlyReadOneBook Aug 24, 2026
60b099a
test(android): androidTest 를 현재 도메인 계약에 맞춘다 — 컴파일 실패 21건 해소
OnlyReadOneBook Aug 24, 2026
58bdd2a
fix(ci): CodeQL 빌드를 compileDebugSources 로 좁힌다 — dex 병합 OOM 제거
OnlyReadOneBook Aug 24, 2026
85b7ba4
perf(ci): ktlint 를 PR 당 1회로 줄인다 — reviewdog 경로 제거, 위반은 job summary 로
OnlyReadOneBook Aug 24, 2026
05b0d15
Merge remote-tracking branch 'origin/develop' into ci/996-codeql-no-b…
OnlyReadOneBook Aug 24, 2026
4c2a615
Merge remote-tracking branch 'origin/ci/996-codeql-no-build-cache' in…
OnlyReadOneBook Aug 24, 2026
679b8be
ci: base 와 충돌한 PR 에 conflict 라벨을 붙인다 (closes 1028)
OnlyReadOneBook Aug 24, 2026
37a4d61
Merge pull request #1026 from Afternote/fix/1022
OnlyReadOneBook Aug 24, 2026
5e6e83b
perf(ci): develop push 에 build cache 워밍 워크플로를 둔다 — writer 를 하나로
OnlyReadOneBook Aug 24, 2026
90c0ceb
security(ci): 워밍 워크플로의 액션도 commit SHA 로 고정
OnlyReadOneBook Aug 24, 2026
b57af14
ci: conflict 라벨 워크플로를 수동 실행 전용으로 시작한다
OnlyReadOneBook Aug 24, 2026
fba2dc4
Merge branch 'develop' into ci/996-codeql-no-build-cache
OnlyReadOneBook Aug 24, 2026
17d3fbd
Merge remote-tracking branch 'origin/ci/996-codeql-no-build-cache' in…
OnlyReadOneBook Aug 24, 2026
8b68fdf
Merge remote-tracking branch 'origin/fix/1023-codeql-oom' into perf/9…
OnlyReadOneBook Aug 24, 2026
2632ebb
Merge remote-tracking branch 'origin/develop' into perf/1012-ktlint-s…
OnlyReadOneBook Aug 24, 2026
322ed7d
Merge pull request #1014 from Afternote/ci/996-codeql-no-build-cache
OnlyReadOneBook Aug 24, 2026
b4d044c
Merge pull request #1027 from Afternote/fix/1023-codeql-oom
OnlyReadOneBook Aug 24, 2026
a6d8ecf
Merge pull request #1036 from Afternote/ci/1028-conflict-label
OnlyReadOneBook Aug 24, 2026
46a934b
Merge pull request #1037 from Afternote/perf/996-build-cache-warm
OnlyReadOneBook Aug 24, 2026
efbb125
Merge pull request #1034 from Afternote/perf/1012-ktlint-single-pass
OnlyReadOneBook Aug 24, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
35 changes: 35 additions & 0 deletions .github/actions/cleanup-release-config/action.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
name: Remove release build configuration
description: Delete the credential files setup-release-config created

# 공개 저장소라 job 이 어떻게 끝나든 러너에 credential 을 남기지 않는다. if: always() 로 부를 것.

inputs:
keystore-path:
description: setup-release-config 의 keystore-path output
required: false
default: ""
firebase-credentials-path:
description: setup-release-config 의 firebase-credentials-path output
required: false
default: ""

runs:
using: composite
steps:
- name: Remove release build files
shell: bash
env:
KEYSTORE_PATH: ${{ inputs.keystore-path }}
FIREBASE_CREDENTIALS_PATH: ${{ inputs.firebase-credentials-path }}
run: |
set -euo pipefail

rm -f app/google-services.json local.properties

if [ -n "$KEYSTORE_PATH" ]; then
rm -f "$KEYSTORE_PATH"
fi

if [ -n "$FIREBASE_CREDENTIALS_PATH" ]; then
rm -f "$FIREBASE_CREDENTIALS_PATH"
fi
162 changes: 162 additions & 0 deletions .github/actions/setup-release-config/action.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,162 @@
name: Set up release build configuration
description: Verify the release secrets a job declares and materialise the build files they back

# setup-ci-config 와 별개 action 이다. 그쪽은 실서비스 값이 없는 결정적 stub 을 만들어 PR 검증에
# 쓰고(#842), 이쪽은 승인 environment 뒤에서 실서비스 secret 을 다룬다. 두 경로가 한 action 으로
# 섞이면 PR 검증이 실서비스 secret 을 참조할 여지가 생기므로 합치지 않는다.

inputs:
require:
description: "검증할 secret 이름 목록 (공백 구분). 나열된 이름의 값이 비면 이름만 보고하고 실패한다."
required: true
kakao-native-app-key:
description: KAKAO_NATIVE_APP_KEY
required: false
default: ""
google-web-client-id:
description: GOOGLE_WEB_CLIENT_ID
required: false
default: ""
google-services-json-b64:
description: GOOGLE_SERVICES_JSON_B64 — 주어지면 app/google-services.json 을 만든다
required: false
default: ""
release-store-file-b64:
description: RELEASE_STORE_FILE_B64 — 주어지면 release keystore 를 만든다
required: false
default: ""
release-store-password:
description: RELEASE_STORE_PASSWORD
required: false
default: ""
release-key-alias:
description: RELEASE_KEY_ALIAS
required: false
default: ""
release-key-password:
description: RELEASE_KEY_PASSWORD
required: false
default: ""
firebase-service-account-json:
description: FIREBASE_SERVICE_ACCOUNT_JSON — 주어지면 ADC credential 파일을 만든다
required: false
default: ""
compose-local-properties:
description: "true 면 app/build.gradle.kts 의 signingConfigs.release 가 읽는 local.properties 를 만든다"
required: false
default: "false"

outputs:
keystore-path:
description: decode 된 release keystore 경로 (만들지 않았으면 빈 값)
value: ${{ steps.materialise.outputs.keystore-path }}
firebase-credentials-path:
description: Firebase service account credential 경로 (만들지 않았으면 빈 값)
value: ${{ steps.materialise.outputs.firebase-credentials-path }}

runs:
using: composite
steps:
# fail-fast — secret 누락 시 Gradle 까지 가지 않고 즉시 실패.
# secret 값은 echo 하지 않는다 (env 로만 받아 길이 검사). 누락 키 이름은 안전.
- name: Verify required secrets are set
shell: bash
env:
REQUIRE: ${{ inputs.require }}
KAKAO_NATIVE_APP_KEY: ${{ inputs.kakao-native-app-key }}
GOOGLE_WEB_CLIENT_ID: ${{ inputs.google-web-client-id }}
GOOGLE_SERVICES_JSON_B64: ${{ inputs.google-services-json-b64 }}
RELEASE_STORE_FILE_B64: ${{ inputs.release-store-file-b64 }}
RELEASE_STORE_PASSWORD: ${{ inputs.release-store-password }}
RELEASE_KEY_ALIAS: ${{ inputs.release-key-alias }}
RELEASE_KEY_PASSWORD: ${{ inputs.release-key-password }}
FIREBASE_SERVICE_ACCOUNT_JSON: ${{ inputs.firebase-service-account-json }}
run: |
set -euo pipefail

read -ra required_names <<< "$REQUIRE"
if [ "${#required_names[@]}" -eq 0 ]; then
echo "::error::require 가 비어 있다 — 검증할 secret 이름을 지정할 것"
exit 1
fi

missing=""
for name in "${required_names[@]}"; do
case "$name" in
KAKAO_NATIVE_APP_KEY) value="$KAKAO_NATIVE_APP_KEY" ;;
GOOGLE_WEB_CLIENT_ID) value="$GOOGLE_WEB_CLIENT_ID" ;;
GOOGLE_SERVICES_JSON_B64) value="$GOOGLE_SERVICES_JSON_B64" ;;
RELEASE_STORE_FILE_B64) value="$RELEASE_STORE_FILE_B64" ;;
RELEASE_STORE_PASSWORD) value="$RELEASE_STORE_PASSWORD" ;;
RELEASE_KEY_ALIAS) value="$RELEASE_KEY_ALIAS" ;;
RELEASE_KEY_PASSWORD) value="$RELEASE_KEY_PASSWORD" ;;
FIREBASE_SERVICE_ACCOUNT_JSON) value="$FIREBASE_SERVICE_ACCOUNT_JSON" ;;
*)
echo "::error::이 action 이 모르는 secret 이름: $name"
exit 1
;;
esac
if [ -z "$value" ]; then
missing="$missing $name"
fi
done

if [ -n "$missing" ]; then
echo "::error::필수 secret 누락:$missing — Settings → Secrets and variables → Actions 에 등록"
exit 1
fi

# keystore·credential 은 workspace 밖 RUNNER_TEMP 에 둔다 — job 종료 시 러너와 함께 폐기되고
# actions/upload-artifact 의 workspace glob 에 딸려 올라갈 수 없다.
- name: Materialise release build files
id: materialise
shell: bash
env:
KAKAO_NATIVE_APP_KEY: ${{ inputs.kakao-native-app-key }}
GOOGLE_WEB_CLIENT_ID: ${{ inputs.google-web-client-id }}
GOOGLE_SERVICES_JSON_B64: ${{ inputs.google-services-json-b64 }}
RELEASE_STORE_FILE_B64: ${{ inputs.release-store-file-b64 }}
RELEASE_STORE_PASSWORD: ${{ inputs.release-store-password }}
RELEASE_KEY_ALIAS: ${{ inputs.release-key-alias }}
RELEASE_KEY_PASSWORD: ${{ inputs.release-key-password }}
FIREBASE_SERVICE_ACCOUNT_JSON: ${{ inputs.firebase-service-account-json }}
COMPOSE_LOCAL_PROPERTIES: ${{ inputs.compose-local-properties }}
run: |
set -euo pipefail

keystore_path=""
firebase_credentials_path=""

# google-services plugin 이 app/google-services.json 을 요구한다. 파일은 .gitignore 라
# CI 환경엔 없으므로 base64 secret 에서 복원한다.
if [ -n "$GOOGLE_SERVICES_JSON_B64" ]; then
printf '%s' "$GOOGLE_SERVICES_JSON_B64" | base64 -d > app/google-services.json
fi

if [ -n "$RELEASE_STORE_FILE_B64" ]; then
keystore_path="$RUNNER_TEMP/afternote-release.jks"
printf '%s' "$RELEASE_STORE_FILE_B64" | base64 -d > "$keystore_path"
fi

# Firebase App Distribution Gradle plugin 은 GOOGLE_APPLICATION_CREDENTIALS 환경변수로
# service account JSON 경로를 인식한다. 경로만 output 으로 넘기고 export 는 호출자가 한다.
if [ -n "$FIREBASE_SERVICE_ACCOUNT_JSON" ]; then
firebase_credentials_path="$RUNNER_TEMP/firebase-service-account.json"
printf '%s' "$FIREBASE_SERVICE_ACCOUNT_JSON" > "$firebase_credentials_path"
fi

if [ "$COMPOSE_LOCAL_PROPERTIES" = "true" ]; then
{
echo "KAKAO_NATIVE_APP_KEY=$KAKAO_NATIVE_APP_KEY"
echo "GOOGLE_WEB_CLIENT_ID=$GOOGLE_WEB_CLIENT_ID"
echo "RELEASE_STORE_FILE=$keystore_path"
echo "RELEASE_STORE_PASSWORD=$RELEASE_STORE_PASSWORD"
echo "RELEASE_KEY_ALIAS=$RELEASE_KEY_ALIAS"
echo "RELEASE_KEY_PASSWORD=$RELEASE_KEY_PASSWORD"
} > local.properties
fi

{
echo "keystore-path=$keystore_path"
echo "firebase-credentials-path=$firebase_credentials_path"
} >> "$GITHUB_OUTPUT"
98 changes: 98 additions & 0 deletions .github/scripts/build-cache-policy.test.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
import assert from "node:assert/strict";
import { readdir, readFile } from "node:fs/promises";
import test from "node:test";

const workflowDirectory = new URL("../workflows/", import.meta.url);

async function workflows() {
const names = (await readdir(workflowDirectory)).filter((name) => name.endsWith(".yml"));
return Promise.all(names.map(async (name) => [name, await readFile(new URL(name, workflowDirectory), "utf8")]));
}

// 주석은 왜 그렇게 했는지를 적어 두는 자리라 액션 이름·플래그가 그대로 등장한다.
// 정책은 실제로 실행되는 줄에만 걸어야 한다.
function withoutComments(source) {
return source
.split("\n")
.filter((line) => !/^\s*#/.test(line))
.join("\n");
}

// Actions 캐시 엔트리는 (키, ref) 로 격리되고, 읽기는 «현재 ref → base → default branch»
// 순으로 매칭된다. 그래서 경쟁이 일어나는 범위는 develop 스코프 하나다 — release·canary
// 워크플로는 main 스코프에 쓰므로 develop PR 이 받는 엔트리를 가리지 않는다.
function runsOnDevelopPush(source) {
return /^\s{2}push:\n\s{4}branches:\s*\[[^\]]*\bdevelop\b/m.test(source);
}

function runsOnPullRequests(source) {
return /^on:\n(?:[^\n]*\n)*?\s{2}pull_request:/m.test(source);
}

test("exactly one workflow may write the shared Gradle User Home cache", async () => {
// Actions 캐시는 같은 키를 덮어쓰지 못한다. writer 가 둘이면 먼저 끝난 쪽이 키를
// 차지하고, build cache 를 채운 쪽의 저장이 조용히 버려진다 (#996).
const writers = (await workflows())
.filter(([, source]) => runsOnDevelopPush(source))
.filter(([, source]) => /cache-read-only:\s*false/.test(withoutComments(source)))
.map(([name]) => name);

assert.deepEqual(writers, ["build-cache-warm.yml"]);
});

test("every other Gradle workflow reads the cache without writing to it", async () => {
for (const [name, source] of await workflows()) {
if (name === "build-cache-warm.yml" || !/uses:\s*gradle\/actions\/setup-gradle@/.test(source)) {
continue;
}
if (!runsOnPullRequests(source) && !runsOnDevelopPush(source)) {
continue;
}
assert.match(source, /cache-read-only:\s*true/, `${name} must not write to the shared cache`);
// 조건식으로 쓰면 pull_request 가 아닌 trigger 에서 writer 로 돌변한다.
assert.doesNotMatch(
source,
/cache-read-only:\s*\$\{\{/,
`${name} must not decide cache writability from the event`,
);
}
});

test("the warming workflow runs on develop pushes and is never cancelled mid-run", async () => {
const source = await readFile(new URL("build-cache-warm.yml", workflowDirectory), "utf8");

assert.match(source, /push:\n\s+branches:\s*\[develop\]/);
// 캐시는 job 의 post 스텝에서 저장된다. 중도 취소되면 그 스텝에 닿지 못한다.
assert.match(source, /cancel-in-progress:\s*false/);
assert.match(source, /--build-cache/);
});

test("the warming workflow covers the tasks pull request jobs actually run", async () => {
const warm = await readFile(new URL("build-cache-warm.yml", workflowDirectory), "utf8");
const lint = await readFile(new URL("lint.yml", workflowDirectory), "utf8");
const unitTest = await readFile(new URL("unit-test.yml", workflowDirectory), "utf8");

for (const task of ["ktlintCheck", "lintDebug"]) {
assert.match(lint, new RegExp(`\\./gradlew[^\\n]*${task}`), `lint.yml no longer runs ${task}`);
assert.match(warm, new RegExp(`\\b${task}\\b`), `warming misses ${task}`);
}
for (const task of [":koverXmlReportCi", ":konsist:test", ":app:compileDebugAndroidTestKotlin"]) {
assert.ok(unitTest.includes(task), `unit-test.yml no longer runs ${task}`);
assert.ok(warm.includes(task), `warming misses ${task}`);
}
});

test("CodeQL stays out of the build cache in both directions", async () => {
const source = await readFile(new URL("codeql.yml", workflowDirectory), "utf8");

// 추출기가 컴파일러 호출을 관찰해야 하므로 FROM-CACHE 로 넘어가면 안 된다 (#1014).
assert.doesNotMatch(withoutComments(source), /--build-cache/);
assert.match(source, /cache-read-only:\s*true/);
});

test("the local Gradle default stays uncached", async () => {
// #996 은 CI 한정이다. 로컬은 손상된 cache 엔트리 사례 때문에 여전히 꺼 둔다.
const properties = await readFile(new URL("../../gradle.properties", import.meta.url), "utf8");

assert.match(properties, /^org\.gradle\.caching=false$/m);
});
Loading
Loading