timsquad 3.3.0 → 3.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.ko.md +288 -0
- package/README.md +158 -151
- package/dist/commands/compile.d.ts +3 -0
- package/dist/commands/compile.d.ts.map +1 -0
- package/dist/commands/compile.js +170 -0
- package/dist/commands/compile.js.map +1 -0
- package/dist/commands/daemon.d.ts.map +1 -1
- package/dist/commands/daemon.js +94 -5
- package/dist/commands/daemon.js.map +1 -1
- package/dist/commands/init.d.ts.map +1 -1
- package/dist/commands/init.js +12 -3
- package/dist/commands/init.js.map +1 -1
- package/dist/commands/skills.d.ts +12 -0
- package/dist/commands/skills.d.ts.map +1 -0
- package/dist/commands/skills.js +231 -0
- package/dist/commands/skills.js.map +1 -0
- package/dist/commands/upgrade.js +5 -0
- package/dist/commands/upgrade.js.map +1 -1
- package/dist/daemon/entry.js +3 -3
- package/dist/daemon/entry.js.map +1 -1
- package/dist/daemon/index.d.ts +3 -2
- package/dist/daemon/index.d.ts.map +1 -1
- package/dist/daemon/index.js +137 -45
- package/dist/daemon/index.js.map +1 -1
- package/dist/daemon/meta-cache.d.ts +1 -0
- package/dist/daemon/meta-cache.d.ts.map +1 -1
- package/dist/daemon/meta-cache.js +9 -0
- package/dist/daemon/meta-cache.js.map +1 -1
- package/dist/daemon/session-state.d.ts +19 -0
- package/dist/daemon/session-state.d.ts.map +1 -0
- package/dist/daemon/session-state.js +132 -0
- package/dist/daemon/session-state.js.map +1 -0
- package/dist/daemon/shutdown.d.ts.map +1 -1
- package/dist/daemon/shutdown.js +7 -1
- package/dist/daemon/shutdown.js.map +1 -1
- package/dist/index.js +4 -0
- package/dist/index.js.map +1 -1
- package/dist/lib/compile-rules.d.ts +66 -0
- package/dist/lib/compile-rules.d.ts.map +1 -0
- package/dist/lib/compile-rules.js +114 -0
- package/dist/lib/compile-rules.js.map +1 -0
- package/dist/lib/compiler.d.ts +105 -0
- package/dist/lib/compiler.d.ts.map +1 -0
- package/dist/lib/compiler.js +368 -0
- package/dist/lib/compiler.js.map +1 -0
- package/dist/lib/config.d.ts +1 -0
- package/dist/lib/config.d.ts.map +1 -1
- package/dist/lib/config.js +8 -1
- package/dist/lib/config.js.map +1 -1
- package/dist/lib/template.d.ts.map +1 -1
- package/dist/lib/template.js +6 -0
- package/dist/lib/template.js.map +1 -1
- package/dist/types/config.d.ts +1 -0
- package/dist/types/config.d.ts.map +1 -1
- package/dist/types/config.js +12 -1
- package/dist/types/config.js.map +1 -1
- package/dist/types/project.d.ts +1 -1
- package/dist/types/project.d.ts.map +1 -1
- package/dist/types/project.js +2 -0
- package/dist/types/project.js.map +1 -1
- package/package.json +1 -1
- package/templates/base/agents/overlays/domain/mobile/_common.md +13 -0
- package/templates/base/skills/controller/SKILL.md +111 -0
- package/templates/base/skills/controller/references/README.md +35 -0
- package/templates/base/skills/controller/rules/README.md +18 -0
- package/templates/base/skills/mobile/dart/SKILL.md +69 -0
- package/templates/base/skills/mobile/dart/rules/async-patterns.md +112 -0
- package/templates/base/skills/mobile/dart/rules/code-style.md +96 -0
- package/templates/base/skills/mobile/dart/rules/null-safety.md +84 -0
- package/templates/base/skills/mobile/dart/rules/type-system.md +111 -0
- package/templates/base/skills/mobile/flutter/SKILL.md +89 -0
- package/templates/base/skills/mobile/flutter/ci-cd/SKILL.md +82 -0
- package/templates/base/skills/mobile/flutter/ci-cd/references/ci-cd-pipeline.md +314 -0
- package/templates/base/skills/mobile/flutter/ci-cd/rules/code-signing.md +106 -0
- package/templates/base/skills/mobile/flutter/ci-cd/rules/codemagic-setup.md +116 -0
- package/templates/base/skills/mobile/flutter/ci-cd/rules/fastlane-setup.md +105 -0
- package/templates/base/skills/mobile/flutter/ci-cd/rules/github-actions.md +112 -0
- package/templates/base/skills/mobile/flutter/ci-cd/rules/store-deployment.md +106 -0
- package/templates/base/skills/mobile/flutter/ci-cd/rules/versioning.md +107 -0
- package/templates/base/skills/mobile/flutter/i18n/SKILL.md +78 -0
- package/templates/base/skills/mobile/flutter/i18n/references/i18n-architecture.md +225 -0
- package/templates/base/skills/mobile/flutter/i18n/rules/arb-files.md +182 -0
- package/templates/base/skills/mobile/flutter/i18n/rules/locale-switching.md +226 -0
- package/templates/base/skills/mobile/flutter/i18n/rules/localization-setup.md +137 -0
- package/templates/base/skills/mobile/flutter/i18n/rules/plural-gender.md +159 -0
- package/templates/base/skills/mobile/flutter/i18n/rules/text-direction.md +199 -0
- package/templates/base/skills/mobile/flutter/monitoring/SKILL.md +81 -0
- package/templates/base/skills/mobile/flutter/monitoring/references/monitoring-architecture.md +269 -0
- package/templates/base/skills/mobile/flutter/monitoring/rules/analytics.md +227 -0
- package/templates/base/skills/mobile/flutter/monitoring/rules/crashlytics-setup.md +195 -0
- package/templates/base/skills/mobile/flutter/monitoring/rules/logging.md +258 -0
- package/templates/base/skills/mobile/flutter/monitoring/rules/performance-monitoring.md +248 -0
- package/templates/base/skills/mobile/flutter/monitoring/rules/sentry-integration.md +249 -0
- package/templates/base/skills/mobile/flutter/networking/SKILL.md +88 -0
- package/templates/base/skills/mobile/flutter/networking/references/api-client-architecture.md +305 -0
- package/templates/base/skills/mobile/flutter/networking/rules/caching.md +212 -0
- package/templates/base/skills/mobile/flutter/networking/rules/connectivity.md +213 -0
- package/templates/base/skills/mobile/flutter/networking/rules/dio-setup.md +159 -0
- package/templates/base/skills/mobile/flutter/networking/rules/error-handling.md +209 -0
- package/templates/base/skills/mobile/flutter/networking/rules/interceptors.md +205 -0
- package/templates/base/skills/mobile/flutter/networking/rules/retrofit-patterns.md +194 -0
- package/templates/base/skills/mobile/flutter/push-notifications/SKILL.md +87 -0
- package/templates/base/skills/mobile/flutter/push-notifications/references/notification-architecture.md +340 -0
- package/templates/base/skills/mobile/flutter/push-notifications/references/platform-setup.md +286 -0
- package/templates/base/skills/mobile/flutter/push-notifications/rules/background-processing.md +308 -0
- package/templates/base/skills/mobile/flutter/push-notifications/rules/deep-linking.md +217 -0
- package/templates/base/skills/mobile/flutter/push-notifications/rules/fcm-setup.md +164 -0
- package/templates/base/skills/mobile/flutter/push-notifications/rules/local-notifications.md +262 -0
- package/templates/base/skills/mobile/flutter/push-notifications/rules/notification-handling.md +210 -0
- package/templates/base/skills/mobile/flutter/push-notifications/rules/notification-permissions.md +246 -0
- package/templates/base/skills/mobile/flutter/push-notifications/rules/rich-notifications.md +320 -0
- package/templates/base/skills/mobile/flutter/references/freezed-patterns.md +162 -0
- package/templates/base/skills/mobile/flutter/references/project-structure.md +170 -0
- package/templates/base/skills/mobile/flutter/rules/animations.md +112 -0
- package/templates/base/skills/mobile/flutter/rules/architecture.md +121 -0
- package/templates/base/skills/mobile/flutter/rules/navigation-routing.md +117 -0
- package/templates/base/skills/mobile/flutter/rules/performance.md +112 -0
- package/templates/base/skills/mobile/flutter/rules/platform-adaptive.md +126 -0
- package/templates/base/skills/mobile/flutter/rules/state-management.md +110 -0
- package/templates/base/skills/mobile/flutter/rules/testing.md +131 -0
- package/templates/base/skills/mobile/flutter/rules/widget-conventions.md +122 -0
- package/templates/base/skills/mobile/flutter/security/SKILL.md +86 -0
- package/templates/base/skills/mobile/flutter/security/references/mobile-security-checklist.md +168 -0
- package/templates/base/skills/mobile/flutter/security/rules/api-key-protection.md +206 -0
- package/templates/base/skills/mobile/flutter/security/rules/authentication.md +248 -0
- package/templates/base/skills/mobile/flutter/security/rules/data-protection.md +271 -0
- package/templates/base/skills/mobile/flutter/security/rules/obfuscation.md +213 -0
- package/templates/base/skills/mobile/flutter/security/rules/secure-storage.md +171 -0
- package/templates/base/skills/mobile/flutter/security/rules/ssl-pinning.md +197 -0
- package/templates/platforms/claude-code/CLAUDE.md.template +25 -0
- package/templates/platforms/claude-code/scripts/completion-guard.sh +57 -0
- package/templates/platforms/claude-code/scripts/phase-guard.sh +79 -0
- package/templates/platforms/claude-code/settings.json +75 -3
- package/templates/project-types/mobile-app/config.yaml +123 -0
- package/templates/project-types/mobile-app/process/workflow.xml +191 -0
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Codemagic Setup
|
|
3
|
+
impact: HIGH
|
|
4
|
+
impactDescription: "CI 미구성 → 수동 빌드, Codemagic 자동화 → Flutter 네이티브 지원"
|
|
5
|
+
tags: codemagic, ci, yaml, workflow, artifact, trigger
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
## Codemagic Setup
|
|
9
|
+
|
|
10
|
+
**Impact: HIGH (CI 미구성 → 수동 빌드, Codemagic 자동화 → Flutter 네이티브 지원)**
|
|
11
|
+
|
|
12
|
+
codemagic.yaml 워크플로우 구성, 환경변수/시크릿, 빌드 트리거, artifact 배포.
|
|
13
|
+
|
|
14
|
+
### codemagic.yaml
|
|
15
|
+
|
|
16
|
+
**Incorrect (GUI만 사용):**
|
|
17
|
+
```
|
|
18
|
+
Codemagic 웹 UI에서 빌드 설정 → 코드 관리 불가, 롤백 불가, 팀 차이 발생
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
**Correct (yaml 코드 관리):**
|
|
22
|
+
```yaml
|
|
23
|
+
workflows:
|
|
24
|
+
ios-release:
|
|
25
|
+
name: iOS Release
|
|
26
|
+
max_build_duration: 30
|
|
27
|
+
instance_type: mac_mini_m2
|
|
28
|
+
environment:
|
|
29
|
+
groups: [ios_credentials, app_store_connect]
|
|
30
|
+
flutter: "3.27.0"
|
|
31
|
+
xcode: latest
|
|
32
|
+
cocoapods: default
|
|
33
|
+
triggering:
|
|
34
|
+
events: [tag]
|
|
35
|
+
tag_patterns:
|
|
36
|
+
- pattern: "v*"
|
|
37
|
+
include: true
|
|
38
|
+
cancel_previous_builds: true
|
|
39
|
+
scripts:
|
|
40
|
+
- name: Set up code signing
|
|
41
|
+
script: |
|
|
42
|
+
keychain initialize
|
|
43
|
+
app-store-connect fetch-signing-files $(BUNDLE_ID) --type IOS_APP_STORE --create
|
|
44
|
+
keychain add-certificates
|
|
45
|
+
- name: Build
|
|
46
|
+
script: |
|
|
47
|
+
xcode-project use-profiles
|
|
48
|
+
flutter packages pub get
|
|
49
|
+
flutter build ipa --release --build-number=$PROJECT_BUILD_NUMBER \
|
|
50
|
+
--export-options-plist=/Users/builder/export_options.plist
|
|
51
|
+
artifacts:
|
|
52
|
+
- build/ios/ipa/*.ipa
|
|
53
|
+
publishing:
|
|
54
|
+
app_store_connect:
|
|
55
|
+
auth: integration
|
|
56
|
+
submit_to_testflight: true
|
|
57
|
+
slack:
|
|
58
|
+
channel: "#deployments"
|
|
59
|
+
notify:
|
|
60
|
+
success: true
|
|
61
|
+
failure: true
|
|
62
|
+
|
|
63
|
+
android-release:
|
|
64
|
+
name: Android Release
|
|
65
|
+
instance_type: linux_x2
|
|
66
|
+
environment:
|
|
67
|
+
groups: [android_credentials, google_play]
|
|
68
|
+
flutter: "3.27.0"
|
|
69
|
+
triggering:
|
|
70
|
+
events: [tag]
|
|
71
|
+
tag_patterns:
|
|
72
|
+
- pattern: "v*"
|
|
73
|
+
scripts:
|
|
74
|
+
- name: Set up signing + Build
|
|
75
|
+
script: |
|
|
76
|
+
echo $ANDROID_KEYSTORE_BASE64 | base64 --decode > android/release.keystore
|
|
77
|
+
flutter packages pub get
|
|
78
|
+
flutter build appbundle --release --build-number=$PROJECT_BUILD_NUMBER
|
|
79
|
+
artifacts:
|
|
80
|
+
- build/app/outputs/bundle/release/*.aab
|
|
81
|
+
publishing:
|
|
82
|
+
google_play:
|
|
83
|
+
credentials: $GCLOUD_SERVICE_ACCOUNT_CREDENTIALS
|
|
84
|
+
track: internal
|
|
85
|
+
|
|
86
|
+
test:
|
|
87
|
+
name: Test & Analyze
|
|
88
|
+
instance_type: linux_x2
|
|
89
|
+
environment:
|
|
90
|
+
flutter: "3.27.0"
|
|
91
|
+
triggering:
|
|
92
|
+
events: [pull_request]
|
|
93
|
+
scripts:
|
|
94
|
+
- name: Test
|
|
95
|
+
script: |
|
|
96
|
+
flutter packages pub get && flutter analyze && flutter test --coverage
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
### 환경변수/시크릿
|
|
100
|
+
|
|
101
|
+
```
|
|
102
|
+
Codemagic UI > Environment variables:
|
|
103
|
+
├── ios_credentials: CERTIFICATE_PRIVATE_KEY, BUNDLE_ID, PROVISIONING_PROFILE
|
|
104
|
+
├── app_store_connect: ASC_KEY_ID, ASC_ISSUER_ID, ASC_API_KEY (.p8)
|
|
105
|
+
├── android_credentials: ANDROID_KEYSTORE_BASE64, PASSWORD, ALIAS, KEY_PASSWORD
|
|
106
|
+
└── google_play: GCLOUD_SERVICE_ACCOUNT_CREDENTIALS (JSON)
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
### 규칙
|
|
110
|
+
|
|
111
|
+
- `codemagic.yaml` 로 워크플로우 정의 — GUI 설정 대신 코드 관리
|
|
112
|
+
- 시크릿은 Codemagic UI에서만 설정 — yaml에 비밀값 금지
|
|
113
|
+
- `instance_type: mac_mini_m2` — iOS 빌드는 macOS 필수
|
|
114
|
+
- `cancel_previous_builds: true` — 동일 브랜치 중복 빌드 방지
|
|
115
|
+
- PR → test, 태그 → release 워크플로우 분리
|
|
116
|
+
- `PROJECT_BUILD_NUMBER` 활용 — 자동 증가 빌드 넘버
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Fastlane Setup
|
|
3
|
+
impact: HIGH
|
|
4
|
+
impactDescription: "수동 배포 → 휴먼 에러, Fastlane 자동화 → 일관된 빌드/배포"
|
|
5
|
+
tags: fastlane, match, supply, deliver, ios, android, automation
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
## Fastlane Setup
|
|
9
|
+
|
|
10
|
+
**Impact: HIGH (수동 배포 → 휴먼 에러, Fastlane 자동화 → 일관된 빌드/배포)**
|
|
11
|
+
|
|
12
|
+
Fastfile 구성, match (iOS 인증서), supply (Google Play), deliver (App Store), 환경 관리.
|
|
13
|
+
|
|
14
|
+
### Fastfile 구성
|
|
15
|
+
|
|
16
|
+
**Incorrect (빌드와 배포 혼합, 하드코딩):**
|
|
17
|
+
```ruby
|
|
18
|
+
lane :deploy do
|
|
19
|
+
build_app(scheme: "Runner")
|
|
20
|
+
upload_to_testflight(username: "me@email.com")
|
|
21
|
+
# → 인증서 관리 없음, 환경 분리 없음, 2FA 문제
|
|
22
|
+
end
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
**Correct (레인 분리, API Key):**
|
|
26
|
+
```ruby
|
|
27
|
+
# ios/fastlane/Fastfile
|
|
28
|
+
platform :ios do
|
|
29
|
+
lane :setup_signing do
|
|
30
|
+
create_keychain(name: "ci_keychain", password: ENV["KEYCHAIN_PASSWORD"],
|
|
31
|
+
default_keychain: true, unlock: true, timeout: 3600)
|
|
32
|
+
match(type: "appstore", keychain_name: "ci_keychain",
|
|
33
|
+
keychain_password: ENV["KEYCHAIN_PASSWORD"], readonly: true)
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
lane :beta do
|
|
37
|
+
setup_signing
|
|
38
|
+
build_app(workspace: "Runner.xcworkspace", scheme: "Runner",
|
|
39
|
+
export_method: "app-store")
|
|
40
|
+
upload_to_testflight(
|
|
41
|
+
skip_waiting_for_build_processing: true,
|
|
42
|
+
api_key_path: "fastlane/api_key.json",
|
|
43
|
+
)
|
|
44
|
+
end
|
|
45
|
+
end
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
```ruby
|
|
49
|
+
# android/fastlane/Fastfile
|
|
50
|
+
platform :android do
|
|
51
|
+
lane :beta do
|
|
52
|
+
sh("cd ../.. && flutter build appbundle --release")
|
|
53
|
+
supply(
|
|
54
|
+
track: "internal",
|
|
55
|
+
aab: "../build/app/outputs/bundle/release/app-release.aab",
|
|
56
|
+
json_key: ENV["GOOGLE_PLAY_JSON_KEY_PATH"],
|
|
57
|
+
skip_upload_metadata: true,
|
|
58
|
+
skip_upload_images: true,
|
|
59
|
+
)
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
lane :release do |options|
|
|
63
|
+
sh("cd ../.. && flutter build appbundle --release")
|
|
64
|
+
supply(
|
|
65
|
+
track: "production",
|
|
66
|
+
aab: "../build/app/outputs/bundle/release/app-release.aab",
|
|
67
|
+
json_key: ENV["GOOGLE_PLAY_JSON_KEY_PATH"],
|
|
68
|
+
rollout: options[:rollout] || "0.1",
|
|
69
|
+
)
|
|
70
|
+
end
|
|
71
|
+
end
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
### App Store Connect API Key
|
|
75
|
+
|
|
76
|
+
```json
|
|
77
|
+
// fastlane/api_key.json (CI 환경변수로 생성, .gitignore 필수)
|
|
78
|
+
{
|
|
79
|
+
"key_id": "YOUR_KEY_ID",
|
|
80
|
+
"issuer_id": "YOUR_ISSUER_ID",
|
|
81
|
+
"key": "-----BEGIN EC PRIVATE KEY-----\n...\n-----END EC PRIVATE KEY-----",
|
|
82
|
+
"in_house": false
|
|
83
|
+
}
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
### .env 관리
|
|
87
|
+
|
|
88
|
+
```bash
|
|
89
|
+
# ios/fastlane/.env.default (.gitignore 추가)
|
|
90
|
+
APPLE_ID=ci@yourteam.com
|
|
91
|
+
TEAM_ID=XXXXXXXXXX
|
|
92
|
+
MATCH_GIT_URL=https://github.com/your-org/certificates
|
|
93
|
+
|
|
94
|
+
# ios/fastlane/.env.production
|
|
95
|
+
APP_IDENTIFIER=com.yourapp.id
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
### 규칙
|
|
99
|
+
|
|
100
|
+
- `Fastfile` 레인 분리 — `setup_signing`, `beta`, `release` 독립
|
|
101
|
+
- `match(readonly: true)` — CI에서 인증서 신규 생성 금지
|
|
102
|
+
- App Store Connect API Key — 2FA 우회, CI 호환
|
|
103
|
+
- `supply` 에 `json_key` — Google Play 서비스 계정 키
|
|
104
|
+
- `.env` 로 환경 분리 — 하드코딩 금지, `.gitignore` 추가
|
|
105
|
+
- 단계적 출시 — `supply(rollout: "0.1")` 소규모 배포 후 확대
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: GitHub Actions
|
|
3
|
+
impact: HIGH
|
|
4
|
+
impactDescription: "CI 없음 → 릴리스 품질 불안정, GitHub Actions → 코드 변경마다 자동 검증"
|
|
5
|
+
tags: github-actions, ci, workflow, flutter-action, cache, artifact
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
## GitHub Actions
|
|
9
|
+
|
|
10
|
+
**Impact: HIGH (CI 없음 → 릴리스 품질 불안정, GitHub Actions → 코드 변경마다 자동 검증)**
|
|
11
|
+
|
|
12
|
+
Flutter 빌드 워크플로우, flutter-action, 테스트-빌드-업로드 파이프라인, 캐시.
|
|
13
|
+
|
|
14
|
+
### 테스트 워크플로우
|
|
15
|
+
|
|
16
|
+
**Incorrect (Flutter 수동 설치, 캐시 없음):**
|
|
17
|
+
```yaml
|
|
18
|
+
on: push
|
|
19
|
+
jobs:
|
|
20
|
+
test:
|
|
21
|
+
runs-on: ubuntu-latest
|
|
22
|
+
steps:
|
|
23
|
+
- run: git clone https://github.com/flutter/flutter.git
|
|
24
|
+
- run: flutter test # 매번 전체 다운로드 → 느림
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
**Correct (flutter-action + 캐시):**
|
|
28
|
+
```yaml
|
|
29
|
+
# .github/workflows/test.yml
|
|
30
|
+
name: Test & Analyze
|
|
31
|
+
on:
|
|
32
|
+
pull_request:
|
|
33
|
+
branches: [main, develop]
|
|
34
|
+
concurrency:
|
|
35
|
+
group: ${{ github.workflow }}-${{ github.ref }}
|
|
36
|
+
cancel-in-progress: true
|
|
37
|
+
jobs:
|
|
38
|
+
test:
|
|
39
|
+
runs-on: ubuntu-latest
|
|
40
|
+
timeout-minutes: 15
|
|
41
|
+
steps:
|
|
42
|
+
- uses: actions/checkout@v4
|
|
43
|
+
- uses: subosito/flutter-action@v2
|
|
44
|
+
with:
|
|
45
|
+
flutter-version: "3.27.0"
|
|
46
|
+
cache: true
|
|
47
|
+
- uses: actions/cache@v4
|
|
48
|
+
with:
|
|
49
|
+
path: ${{ env.PUB_CACHE }}
|
|
50
|
+
key: pub-${{ runner.os }}-${{ hashFiles('**/pubspec.lock') }}
|
|
51
|
+
- run: flutter pub get
|
|
52
|
+
- run: flutter analyze --no-fatal-infos
|
|
53
|
+
- run: flutter test --coverage
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
### 릴리스 워크플로우 (iOS + Android)
|
|
57
|
+
|
|
58
|
+
```yaml
|
|
59
|
+
# .github/workflows/ios-release.yml — 태그 푸시 트리거
|
|
60
|
+
name: iOS Release
|
|
61
|
+
on: { push: { tags: ["v*"] } }
|
|
62
|
+
jobs:
|
|
63
|
+
ios-build:
|
|
64
|
+
runs-on: macos-latest
|
|
65
|
+
timeout-minutes: 45
|
|
66
|
+
steps:
|
|
67
|
+
- uses: actions/checkout@v4
|
|
68
|
+
- uses: subosito/flutter-action@v2
|
|
69
|
+
with: { flutter-version: "3.27.0", cache: true }
|
|
70
|
+
- uses: ruby/setup-ruby@v1
|
|
71
|
+
with: { ruby-version: "3.2", bundler-cache: true, working-directory: ios }
|
|
72
|
+
- run: |
|
|
73
|
+
VERSION=${GITHUB_REF#refs/tags/v}
|
|
74
|
+
flutter build ipa --release --build-name=$VERSION --build-number=$GITHUB_RUN_NUMBER
|
|
75
|
+
cd ios && bundle exec fastlane beta
|
|
76
|
+
env:
|
|
77
|
+
MATCH_GIT_URL: ${{ secrets.MATCH_GIT_URL }}
|
|
78
|
+
MATCH_PASSWORD: ${{ secrets.MATCH_PASSWORD }}
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
```yaml
|
|
82
|
+
# .github/workflows/android-release.yml
|
|
83
|
+
name: Android Release
|
|
84
|
+
on: { push: { tags: ["v*"] } }
|
|
85
|
+
jobs:
|
|
86
|
+
android-build:
|
|
87
|
+
runs-on: ubuntu-latest
|
|
88
|
+
timeout-minutes: 30
|
|
89
|
+
steps:
|
|
90
|
+
- uses: actions/checkout@v4
|
|
91
|
+
- uses: actions/setup-java@v4
|
|
92
|
+
with: { distribution: "temurin", java-version: "17" }
|
|
93
|
+
- uses: subosito/flutter-action@v2
|
|
94
|
+
with: { flutter-version: "3.27.0", cache: true }
|
|
95
|
+
- run: echo "${{ secrets.ANDROID_KEYSTORE_BASE64 }}" | base64 --decode > android/release.keystore
|
|
96
|
+
- run: flutter build appbundle --release --build-number=${{ github.run_number }}
|
|
97
|
+
- uses: r0adkll/upload-google-play@v1
|
|
98
|
+
with:
|
|
99
|
+
serviceAccountJsonPlainText: ${{ secrets.GOOGLE_PLAY_SERVICE_ACCOUNT }}
|
|
100
|
+
packageName: com.yourapp.id
|
|
101
|
+
releaseFiles: build/app/outputs/bundle/release/app-release.aab
|
|
102
|
+
track: internal
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
### 규칙
|
|
106
|
+
|
|
107
|
+
- `subosito/flutter-action@v2` — Flutter SDK 설치 + `cache: true`
|
|
108
|
+
- pub 캐시 — `actions/cache@v4` + `pubspec.lock` 해시 키
|
|
109
|
+
- `concurrency` — 동일 PR 중복 빌드 취소
|
|
110
|
+
- `timeout-minutes` — 테스트 15분, iOS 45분, Android 30분
|
|
111
|
+
- 태그 `v*` 트리거 — 릴리스 빌드, PR → 테스트만
|
|
112
|
+
- 시크릿 — `${{ secrets.* }}` 로 주입, 하드코딩 금지
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Store Deployment
|
|
3
|
+
impact: HIGH
|
|
4
|
+
impactDescription: "배포 실수 → 심사 거절/사용자 불만, 체계적 배포 → 안정적 출시"
|
|
5
|
+
tags: app-store, play-store, testflight, deployment, rollout, metadata
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
## Store Deployment
|
|
9
|
+
|
|
10
|
+
**Impact: HIGH (배포 실수 → 심사 거절/사용자 불만, 체계적 배포 → 안정적 출시)**
|
|
11
|
+
|
|
12
|
+
TestFlight 업로드, Play Store 트랙 관리, 메타데이터, 단계적 출시 전략.
|
|
13
|
+
|
|
14
|
+
### TestFlight (iOS) 배포
|
|
15
|
+
|
|
16
|
+
**Incorrect (Xcode에서 수동 업로드):**
|
|
17
|
+
```
|
|
18
|
+
Xcode > Archive > Distribute App → 수동 반복, 재현 불가
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
**Correct (Fastlane + API Key):**
|
|
22
|
+
```ruby
|
|
23
|
+
lane :beta do
|
|
24
|
+
api_key = app_store_connect_api_key(
|
|
25
|
+
key_id: ENV["ASC_KEY_ID"],
|
|
26
|
+
issuer_id: ENV["ASC_ISSUER_ID"],
|
|
27
|
+
key_filepath: ENV["ASC_KEY_PATH"],
|
|
28
|
+
)
|
|
29
|
+
build_app(workspace: "Runner.xcworkspace", scheme: "Runner",
|
|
30
|
+
export_method: "app-store")
|
|
31
|
+
upload_to_testflight(
|
|
32
|
+
api_key: api_key,
|
|
33
|
+
skip_waiting_for_build_processing: true,
|
|
34
|
+
groups: ["Internal Testers"],
|
|
35
|
+
changelog: ENV["RELEASE_NOTES"] || "Bug fixes and improvements",
|
|
36
|
+
)
|
|
37
|
+
end
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
### Play Store 트랙 전략
|
|
41
|
+
|
|
42
|
+
**Incorrect (바로 production):**
|
|
43
|
+
```ruby
|
|
44
|
+
supply(track: "production", aab: "app-release.aab")
|
|
45
|
+
# → 테스트 없이 전체 배포 → 크래시 위험
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
**Correct (단계적 트랙 승격):**
|
|
49
|
+
```ruby
|
|
50
|
+
lane :internal do
|
|
51
|
+
supply(track: "internal", aab: "app-release.aab",
|
|
52
|
+
json_key: ENV["GOOGLE_PLAY_JSON_KEY_PATH"])
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
lane :promote_production do |options|
|
|
56
|
+
supply(track: "internal", track_promote_to: "production",
|
|
57
|
+
rollout: options[:rollout] || "0.01",
|
|
58
|
+
json_key: ENV["GOOGLE_PLAY_JSON_KEY_PATH"])
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
lane :increase_rollout do |options|
|
|
62
|
+
supply(track: "production", rollout: options[:rollout],
|
|
63
|
+
json_key: ENV["GOOGLE_PLAY_JSON_KEY_PATH"])
|
|
64
|
+
end
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
### 단계적 출시 전략
|
|
68
|
+
|
|
69
|
+
```
|
|
70
|
+
Day 0: internal (팀) → 크래시율/기능 확인
|
|
71
|
+
Day 1: closed testing → 소규모 외부 테스터
|
|
72
|
+
Day 3: production 1% → 크래시 모니터링
|
|
73
|
+
Day 5: production 5% → ANR 비율 확인
|
|
74
|
+
Day 7: production 20% → 사용자 피드백
|
|
75
|
+
Day 10: production 50%
|
|
76
|
+
Day 14: production 100%
|
|
77
|
+
|
|
78
|
+
중단 기준: 크래시율 > 1%, ANR > 0.5%, 평점 급락
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
### 메타데이터 관리
|
|
82
|
+
|
|
83
|
+
```
|
|
84
|
+
fastlane/metadata/
|
|
85
|
+
├── android/en-US/ # title.txt, short_description.txt, full_description.txt
|
|
86
|
+
├── android/ko/
|
|
87
|
+
├── ios/en-US/ # name.txt, subtitle.txt, description.txt, keywords.txt
|
|
88
|
+
├── ios/ko/
|
|
89
|
+
└── screenshots/en-US/ # iPhone6.5/, iPhone5.5/, iPad12.9/
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
```ruby
|
|
93
|
+
lane :update_metadata do
|
|
94
|
+
deliver(skip_binary_upload: true, skip_screenshots: false,
|
|
95
|
+
overwrite_screenshots: true, api_key_path: "fastlane/api_key.json")
|
|
96
|
+
end
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
### 규칙
|
|
100
|
+
|
|
101
|
+
- TestFlight 자동 업로드 — `upload_to_testflight` + API Key, 수동 금지
|
|
102
|
+
- Play Store 트랙 순서 — internal → closed → open → production
|
|
103
|
+
- 단계적 출시 — 1%부터 시작, 크래시율 모니터링 후 확대
|
|
104
|
+
- 메타데이터 코드 관리 — `fastlane/metadata/` 텍스트 파일
|
|
105
|
+
- 릴리스 노트 — CHANGELOG에서 추출, 수동 작성 금지
|
|
106
|
+
- 크래시율 1% 초과 → 즉시 rollout 중단
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Versioning
|
|
3
|
+
impact: MEDIUM
|
|
4
|
+
impactDescription: "버전 관리 부재 → 빌드 추적 불가, 체계적 버전 → 출시 이력 완전 추적"
|
|
5
|
+
tags: versioning, semver, build-number, changelog, git-tag, pubspec
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
## Versioning
|
|
9
|
+
|
|
10
|
+
**Impact: MEDIUM (버전 관리 부재 → 빌드 추적 불가, 체계적 버전 → 출시 이력 완전 추적)**
|
|
11
|
+
|
|
12
|
+
pubspec.yaml 시맨틱 버저닝, 빌드 넘버 자동 증가, CHANGELOG 생성, git 태그 연동.
|
|
13
|
+
|
|
14
|
+
### pubspec.yaml 버전 형식
|
|
15
|
+
|
|
16
|
+
**Incorrect (빌드 넘버 누락):**
|
|
17
|
+
```yaml
|
|
18
|
+
version: 1.0.0 # → 스토어 업로드 시 구분 불가, 수동 관리 실수
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
**Correct (시맨틱 버전 + 빌드 넘버):**
|
|
22
|
+
```yaml
|
|
23
|
+
version: 1.2.3+45
|
|
24
|
+
# major(1) — 호환 불가 변경
|
|
25
|
+
# minor(2) — 새 기능 (하위 호환)
|
|
26
|
+
# patch(3) — 버그 수정
|
|
27
|
+
# build(45) — CI 빌드 넘버 (스토어 구분용, 항상 증가)
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
### 빌드 넘버 자동 증가
|
|
31
|
+
|
|
32
|
+
```bash
|
|
33
|
+
# 전략 1: CI 빌드 넘버 (권장)
|
|
34
|
+
flutter build appbundle --build-number=${{ github.run_number }} # GitHub Actions
|
|
35
|
+
flutter build ipa --build-number=$PROJECT_BUILD_NUMBER # Codemagic
|
|
36
|
+
|
|
37
|
+
# 전략 2: 타임스탬프 (YYYYMMDDNN)
|
|
38
|
+
BUILD_NUMBER=$(date +%Y%m%d)$(printf "%02d" $BUILD_COUNT)
|
|
39
|
+
|
|
40
|
+
# 전략 3: Git 커밋 카운트
|
|
41
|
+
BUILD_NUMBER=$(git rev-list --count HEAD)
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
### CI에서 버전 오버라이드
|
|
45
|
+
|
|
46
|
+
```bash
|
|
47
|
+
TAG=${GITHUB_REF#refs/tags/v} # v1.2.3 → 1.2.3
|
|
48
|
+
flutter build ipa --release \
|
|
49
|
+
--build-name=$TAG \
|
|
50
|
+
--build-number=${{ github.run_number }}
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
### 런타임 버전 접근
|
|
54
|
+
|
|
55
|
+
```dart
|
|
56
|
+
import 'package:package_info_plus/package_info_plus.dart';
|
|
57
|
+
|
|
58
|
+
class AppVersion {
|
|
59
|
+
static Future<String> getFullVersion() async {
|
|
60
|
+
final info = await PackageInfo.fromPlatform();
|
|
61
|
+
return '${info.version}+${info.buildNumber}'; // "1.2.3+45"
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
### CHANGELOG 자동 생성
|
|
67
|
+
|
|
68
|
+
```yaml
|
|
69
|
+
# .github/workflows/changelog.yml (태그 푸시 시)
|
|
70
|
+
- uses: orhun/git-cliff-action@v3
|
|
71
|
+
with:
|
|
72
|
+
config: cliff.toml
|
|
73
|
+
args: --verbose
|
|
74
|
+
env:
|
|
75
|
+
OUTPUT: CHANGELOG.md
|
|
76
|
+
- uses: softprops/action-gh-release@v2
|
|
77
|
+
with:
|
|
78
|
+
body_path: CHANGELOG.md
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
### Git 태그 전략
|
|
82
|
+
|
|
83
|
+
```bash
|
|
84
|
+
git tag -a v1.2.3 -m "Release v1.2.3"
|
|
85
|
+
git push origin v1.2.3
|
|
86
|
+
|
|
87
|
+
# 태그 규칙:
|
|
88
|
+
# v1.2.3 → production
|
|
89
|
+
# v1.2.3-rc1 → release candidate
|
|
90
|
+
# v1.2.3-beta.1 → beta (TestFlight/Internal)
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
### 플랫폼별 주의
|
|
94
|
+
|
|
95
|
+
```
|
|
96
|
+
iOS: CFBundleVersion = build-number, 동일 버전 재업로드 불가
|
|
97
|
+
Android: versionCode = 정수, 감소 불가 (한번 올리면 되돌릴 수 없음)
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
### 규칙
|
|
101
|
+
|
|
102
|
+
- `version: major.minor.patch+buildNumber` 형식 유지
|
|
103
|
+
- 빌드 넘버는 CI에서 자동 증가 — 수동 관리 금지
|
|
104
|
+
- `--build-name` + `--build-number` CI 오버라이드
|
|
105
|
+
- git 태그 `v*` — 릴리스와 1:1 매핑
|
|
106
|
+
- CHANGELOG 자동 생성 — conventional commits 기반
|
|
107
|
+
- 빌드 넘버 절대 감소 불가 — 스토어 거부
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: i18n
|
|
3
|
+
description: |
|
|
4
|
+
Flutter 국제화(i18n) 가이드라인.
|
|
5
|
+
flutter_localizations, intl, ARB 파일 관리,
|
|
6
|
+
RTL 레이아웃, 복수형/성별 처리, 런타임 로캘 전환.
|
|
7
|
+
version: "1.0.0"
|
|
8
|
+
tags: [flutter, i18n, l10n, localization, arb, intl, rtl]
|
|
9
|
+
user-invocable: false
|
|
10
|
+
---
|
|
11
|
+
|
|
12
|
+
# Internationalization (i18n) & Localization
|
|
13
|
+
|
|
14
|
+
Flutter 다국어 지원 통합 가이드.
|
|
15
|
+
ARB 파일 기반 번역 관리, ICU MessageFormat, RTL 대응, 런타임 로캘 전환.
|
|
16
|
+
|
|
17
|
+
## Philosophy
|
|
18
|
+
|
|
19
|
+
- 텍스트는 코드에 없다 — 모든 사용자 노출 문자열은 ARB 파일에
|
|
20
|
+
- 로캘은 상태 — Riverpod provider로 관리, SharedPreferences로 영속화
|
|
21
|
+
- RTL은 기본 — Directionality 위젯 활용, 하드코딩된 padding/margin 금지
|
|
22
|
+
- 번역은 프로세스 — 개발자 → 번역가 → 검수, CI에서 누락 키 자동 감지
|
|
23
|
+
|
|
24
|
+
## Resources
|
|
25
|
+
|
|
26
|
+
5개 규칙 + 1개 참조. i18n 파이프라인 전체를 커버.
|
|
27
|
+
|
|
28
|
+
| Priority | Type | Resource | Description |
|
|
29
|
+
|----------|------|----------|-------------|
|
|
30
|
+
| CRITICAL | rule | [localization-setup](rules/localization-setup.md) | flutter_localizations, intl, l10n.yaml, pubspec generate |
|
|
31
|
+
| CRITICAL | rule | [arb-files](rules/arb-files.md) | ARB 파일 구조, @placeholder, 네이밍 컨벤션, gen-l10n |
|
|
32
|
+
| HIGH | rule | [text-direction](rules/text-direction.md) | RTL 대응, EdgeInsetsDirectional, AlignmentDirectional |
|
|
33
|
+
| HIGH | rule | [plural-gender](rules/plural-gender.md) | ICU MessageFormat, plural, select, ordinal |
|
|
34
|
+
| MEDIUM | rule | [locale-switching](rules/locale-switching.md) | LocaleNotifier, SharedPreferences, 런타임 전환 |
|
|
35
|
+
| — | ref | [i18n-architecture](references/i18n-architecture.md) | 디렉토리 구조, 번역 워크플로우, CI 검증 |
|
|
36
|
+
|
|
37
|
+
## Quick Rules
|
|
38
|
+
|
|
39
|
+
### 설정
|
|
40
|
+
- `pubspec.yaml` 에 `generate: true` + `flutter_localizations`, `intl` 의존성
|
|
41
|
+
- `l10n.yaml` 로 ARB 디렉토리, 출력 클래스, 기본 로캘 지정
|
|
42
|
+
- `MaterialApp.localizationsDelegates` + `supportedLocales` 필수 설정
|
|
43
|
+
|
|
44
|
+
### ARB 파일
|
|
45
|
+
- `app_en.arb` 가 기본 (template ARB), `app_ko.arb` 등 로캘별 추가
|
|
46
|
+
- 키 네이밍: `featureName_context` (예: `matchDetail_inviteButton`)
|
|
47
|
+
- `@placeholder` 메타데이터로 동적 값 타입 명시
|
|
48
|
+
- `flutter gen-l10n` 으로 자동 생성 (`AppLocalizations` 클래스)
|
|
49
|
+
|
|
50
|
+
### RTL 대응
|
|
51
|
+
- `EdgeInsetsDirectional.only(start: 16)` (left/right 대신 start/end)
|
|
52
|
+
- `AlignmentDirectional.centerStart` (centerLeft 대신)
|
|
53
|
+
- 아이콘 미러링: `Directionality` 감지 후 조건부 Transform
|
|
54
|
+
|
|
55
|
+
### 복수형/성별
|
|
56
|
+
- ICU `{count, plural, =0{no items} =1{1 item} other{{count} items}}`
|
|
57
|
+
- `{gender, select, male{his} female{her} other{their}}`
|
|
58
|
+
- ARB 파일에 직접 ICU 구문 작성, intl이 자동 파싱
|
|
59
|
+
|
|
60
|
+
### 로캘 전환
|
|
61
|
+
- `LocaleNotifier` (Riverpod StateNotifier) → 현재 로캘 관리
|
|
62
|
+
- `SharedPreferences` 로 선택 로캘 영속화
|
|
63
|
+
- 앱 재시작 없이 `MaterialApp.locale` 변경으로 즉시 반영
|
|
64
|
+
|
|
65
|
+
## Checklist
|
|
66
|
+
|
|
67
|
+
| Priority | Item |
|
|
68
|
+
|----------|------|
|
|
69
|
+
| CRITICAL | `pubspec.yaml` 에 `generate: true` 설정 |
|
|
70
|
+
| CRITICAL | `l10n.yaml` 생성 (arb-dir, template-arb-file, output-localization-file) |
|
|
71
|
+
| CRITICAL | `MaterialApp.localizationsDelegates` 3종 등록 |
|
|
72
|
+
| CRITICAL | 모든 사용자 노출 문자열이 ARB 파일에 존재 |
|
|
73
|
+
| HIGH | `@placeholder` 메타데이터 완전성 (타입, 예시, 설명) |
|
|
74
|
+
| HIGH | RTL 레이아웃에서 Directional 위젯 사용 |
|
|
75
|
+
| HIGH | 복수형/성별 표현이 ICU MessageFormat 사용 |
|
|
76
|
+
| MEDIUM | SharedPreferences 로캘 영속화 |
|
|
77
|
+
| MEDIUM | CI에서 누락 키 자동 감지 |
|
|
78
|
+
| MEDIUM | 번역 워크플로우 문서화 (개발자 → 번역가 → 검수) |
|