timsquad 3.3.0 → 3.5.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 +95 -5
- package/dist/commands/daemon.js.map +1 -1
- package/dist/commands/full.js +1 -0
- package/dist/commands/full.js.map +1 -1
- package/dist/commands/git/pr.js +6 -5
- package/dist/commands/git/pr.js.map +1 -1
- package/dist/commands/git/release.js +2 -7
- package/dist/commands/git/release.js.map +1 -1
- package/dist/commands/improve.js +2 -2
- package/dist/commands/improve.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/log.d.ts.map +1 -1
- package/dist/commands/log.js +2 -2
- package/dist/commands/log.js.map +1 -1
- package/dist/commands/metrics.d.ts.map +1 -1
- package/dist/commands/metrics.js +6 -2
- package/dist/commands/metrics.js.map +1 -1
- package/dist/commands/retro.js +8 -8
- package/dist/commands/retro.js.map +1 -1
- package/dist/commands/session.js +3 -3
- package/dist/commands/session.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 +228 -0
- package/dist/commands/skills.js.map +1 -0
- package/dist/commands/status.js +1 -1
- package/dist/commands/status.js.map +1 -1
- package/dist/commands/upgrade.d.ts.map +1 -1
- package/dist/commands/upgrade.js +23 -1
- 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/event-queue.d.ts.map +1 -1
- package/dist/daemon/event-queue.js +2 -2
- package/dist/daemon/event-queue.js.map +1 -1
- package/dist/daemon/index.d.ts +4 -2
- package/dist/daemon/index.d.ts.map +1 -1
- package/dist/daemon/index.js +214 -52
- package/dist/daemon/index.js.map +1 -1
- package/dist/daemon/jsonl-watcher.d.ts +1 -0
- package/dist/daemon/jsonl-watcher.d.ts.map +1 -1
- package/dist/daemon/jsonl-watcher.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-notes.d.ts +33 -0
- package/dist/daemon/session-notes.d.ts.map +1 -0
- package/dist/daemon/session-notes.js +74 -0
- package/dist/daemon/session-notes.js.map +1 -0
- package/dist/daemon/session-state.d.ts +27 -0
- package/dist/daemon/session-state.d.ts.map +1 -0
- package/dist/daemon/session-state.js +165 -0
- package/dist/daemon/session-state.js.map +1 -0
- package/dist/daemon/shutdown.d.ts.map +1 -1
- package/dist/daemon/shutdown.js +9 -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/agent-generator.d.ts +4 -0
- package/dist/lib/agent-generator.d.ts.map +1 -1
- package/dist/lib/agent-generator.js +52 -3
- package/dist/lib/agent-generator.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/project.d.ts.map +1 -1
- package/dist/lib/project.js +8 -3
- package/dist/lib/project.js.map +1 -1
- package/dist/lib/skill-generator.d.ts.map +1 -1
- package/dist/lib/skill-generator.js +22 -1
- package/dist/lib/skill-generator.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 +4 -4
- package/templates/base/agents/base/tsq-architect.md +2 -2
- package/templates/base/agents/overlays/domain/mobile/_common.md +13 -0
- package/templates/base/knowledge/checklists/plan-quality.md +31 -0
- package/templates/base/knowledge/checklists/stability-verification.md +14 -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/base/skills/stability-verification/SKILL.md +64 -0
- package/templates/base/skills/stability-verification/references/release-checklist.md +34 -0
- package/templates/base/skills/stability-verification/references/security-fix-patterns.md +112 -0
- package/templates/base/skills/stability-verification/rules/verification-layers.md +67 -0
- package/templates/base/skills/stability-verification/rules/verification-workflow.md +69 -0
- package/templates/base/skills/stability-verification/scripts/verify.sh +294 -0
- package/templates/platforms/claude-code/CLAUDE.md.template +25 -0
- package/templates/platforms/claude-code/rules/build-gate.md +28 -0
- package/templates/platforms/claude-code/rules/completion-verification.md +30 -0
- package/templates/platforms/claude-code/rules/context-monitor.md +23 -0
- package/templates/platforms/claude-code/rules/plan-review.md +45 -0
- package/templates/platforms/claude-code/rules/quality-guards.md +43 -0
- package/templates/platforms/claude-code/rules/session-notes.md +18 -0
- package/templates/platforms/claude-code/rules/skill-suggest.md +27 -0
- package/templates/platforms/claude-code/scripts/build-gate.sh +73 -0
- package/templates/platforms/claude-code/scripts/completion-guard.sh +93 -0
- package/templates/platforms/claude-code/scripts/phase-guard.sh +79 -0
- package/templates/platforms/claude-code/scripts/safe-guard.sh +83 -0
- package/templates/platforms/claude-code/scripts/skill-rules.json +85 -0
- package/templates/platforms/claude-code/scripts/skill-suggest.sh +105 -0
- package/templates/platforms/claude-code/settings.json +111 -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,314 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: CI/CD Pipeline Architecture
|
|
3
|
+
category: reference
|
|
4
|
+
source: internal
|
|
5
|
+
tags: pipeline, architecture, environment, secrets, rollback, diagram
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
# CI/CD Pipeline Architecture
|
|
9
|
+
|
|
10
|
+
파이프라인 아키텍처 다이어그램, 환경별 설정, 시크릿 관리 전략, 롤백 절차.
|
|
11
|
+
|
|
12
|
+
## Key Concepts
|
|
13
|
+
|
|
14
|
+
- **파이프라인**: 코드 변경 → 테스트 → 빌드 → 배포 자동화 흐름
|
|
15
|
+
- **환경 분리**: dev/staging/prod 각각 독립 설정, 시크릿, 배포 대상
|
|
16
|
+
- **시크릿 관리**: 서명 키, API 키, 서비스 계정을 안전하게 저장/주입
|
|
17
|
+
- **롤백**: 문제 발생 시 이전 버전으로 빠르게 복원
|
|
18
|
+
|
|
19
|
+
## Pipeline Architecture
|
|
20
|
+
|
|
21
|
+
```
|
|
22
|
+
코드 변경 흐름:
|
|
23
|
+
|
|
24
|
+
Feature Branch PR (→ main) Tag (v*) Store
|
|
25
|
+
│ │ │ │
|
|
26
|
+
▼ ▼ ▼ ▼
|
|
27
|
+
[Lint] [Test] [Build] [Deploy]
|
|
28
|
+
분석 + 단위 테스트 iOS IPA TestFlight
|
|
29
|
+
포맷 검사 통합 테스트 Android AAB Play Store
|
|
30
|
+
커버리지 단계적 출시
|
|
31
|
+
│ │ │ │
|
|
32
|
+
▼ ▼ ▼ ▼
|
|
33
|
+
피드백 PR 체크 Artifact Notify
|
|
34
|
+
즉시 알림 통과/실패 저장 Slack/Email
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
### 상세 파이프라인
|
|
38
|
+
|
|
39
|
+
```
|
|
40
|
+
PR 생성/업데이트:
|
|
41
|
+
├─ 1. Checkout
|
|
42
|
+
├─ 2. flutter pub get (캐시)
|
|
43
|
+
├─ 3. flutter analyze
|
|
44
|
+
├─ 4. flutter test --coverage
|
|
45
|
+
├─ 5. 커버리지 리포트 업로드
|
|
46
|
+
└─ 6. PR 상태 업데이트 (pass/fail)
|
|
47
|
+
|
|
48
|
+
태그 푸시 (v1.2.3):
|
|
49
|
+
├─ 1. Checkout + 태그에서 버전 추출
|
|
50
|
+
├─ 2. flutter pub get
|
|
51
|
+
├─ 3. flutter test (릴리스 전 최종 검증)
|
|
52
|
+
├─ 4. 코드 서명 설정
|
|
53
|
+
│ ├─ iOS: match (provisioning + 인증서)
|
|
54
|
+
│ └─ Android: keystore 복원
|
|
55
|
+
├─ 5. 빌드
|
|
56
|
+
│ ├─ iOS: flutter build ipa --release
|
|
57
|
+
│ └─ Android: flutter build appbundle --release
|
|
58
|
+
├─ 6. 스토어 업로드
|
|
59
|
+
│ ├─ iOS: upload_to_testflight
|
|
60
|
+
│ └─ Android: supply (internal track)
|
|
61
|
+
├─ 7. Artifact 저장 (30일)
|
|
62
|
+
├─ 8. GitHub Release 생성 + CHANGELOG
|
|
63
|
+
└─ 9. Slack 알림 (#deployments)
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
## Environment Configuration
|
|
67
|
+
|
|
68
|
+
### Flavor/Scheme 기반 환경 분리
|
|
69
|
+
|
|
70
|
+
```dart
|
|
71
|
+
// lib/config/environment.dart
|
|
72
|
+
enum Environment { dev, staging, prod }
|
|
73
|
+
|
|
74
|
+
class EnvConfig {
|
|
75
|
+
final Environment env;
|
|
76
|
+
final String apiBaseUrl;
|
|
77
|
+
final String sentryDsn;
|
|
78
|
+
final bool enableAnalytics;
|
|
79
|
+
|
|
80
|
+
const EnvConfig({
|
|
81
|
+
required this.env,
|
|
82
|
+
required this.apiBaseUrl,
|
|
83
|
+
required this.sentryDsn,
|
|
84
|
+
required this.enableAnalytics,
|
|
85
|
+
});
|
|
86
|
+
|
|
87
|
+
static const dev = EnvConfig(
|
|
88
|
+
env: Environment.dev,
|
|
89
|
+
apiBaseUrl: 'https://api-dev.yourapp.com',
|
|
90
|
+
sentryDsn: '',
|
|
91
|
+
enableAnalytics: false,
|
|
92
|
+
);
|
|
93
|
+
|
|
94
|
+
static const staging = EnvConfig(
|
|
95
|
+
env: Environment.staging,
|
|
96
|
+
apiBaseUrl: 'https://api-staging.yourapp.com',
|
|
97
|
+
sentryDsn: 'https://xxx@sentry.io/staging',
|
|
98
|
+
enableAnalytics: false,
|
|
99
|
+
);
|
|
100
|
+
|
|
101
|
+
static const prod = EnvConfig(
|
|
102
|
+
env: Environment.prod,
|
|
103
|
+
apiBaseUrl: 'https://api.yourapp.com',
|
|
104
|
+
sentryDsn: 'https://xxx@sentry.io/prod',
|
|
105
|
+
enableAnalytics: true,
|
|
106
|
+
);
|
|
107
|
+
}
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
### --dart-define 으로 빌드 시 주입
|
|
111
|
+
|
|
112
|
+
```bash
|
|
113
|
+
# Dev
|
|
114
|
+
flutter run \
|
|
115
|
+
--dart-define=ENV=dev \
|
|
116
|
+
--dart-define=API_URL=https://api-dev.yourapp.com
|
|
117
|
+
|
|
118
|
+
# Staging
|
|
119
|
+
flutter build apk \
|
|
120
|
+
--dart-define=ENV=staging \
|
|
121
|
+
--dart-define=API_URL=https://api-staging.yourapp.com
|
|
122
|
+
|
|
123
|
+
# Production
|
|
124
|
+
flutter build appbundle \
|
|
125
|
+
--release \
|
|
126
|
+
--dart-define=ENV=prod \
|
|
127
|
+
--dart-define=API_URL=https://api.yourapp.com
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
```dart
|
|
131
|
+
// 빌드 시 주입된 값 읽기
|
|
132
|
+
class BuildConfig {
|
|
133
|
+
static const env = String.fromEnvironment('ENV', defaultValue: 'dev');
|
|
134
|
+
static const apiUrl = String.fromEnvironment('API_URL');
|
|
135
|
+
|
|
136
|
+
static bool get isProduction => env == 'prod';
|
|
137
|
+
static bool get isDev => env == 'dev';
|
|
138
|
+
}
|
|
139
|
+
```
|
|
140
|
+
|
|
141
|
+
### CI 환경별 워크플로우
|
|
142
|
+
|
|
143
|
+
```yaml
|
|
144
|
+
# Codemagic: 환경별 워크플로우
|
|
145
|
+
workflows:
|
|
146
|
+
dev-build:
|
|
147
|
+
triggering:
|
|
148
|
+
events: [push]
|
|
149
|
+
branch_patterns:
|
|
150
|
+
- pattern: "develop"
|
|
151
|
+
scripts:
|
|
152
|
+
- name: Build dev
|
|
153
|
+
script: |
|
|
154
|
+
flutter build apk --dart-define=ENV=dev
|
|
155
|
+
|
|
156
|
+
staging-build:
|
|
157
|
+
triggering:
|
|
158
|
+
events: [push]
|
|
159
|
+
branch_patterns:
|
|
160
|
+
- pattern: "release/*"
|
|
161
|
+
scripts:
|
|
162
|
+
- name: Build staging
|
|
163
|
+
script: |
|
|
164
|
+
flutter build appbundle --dart-define=ENV=staging
|
|
165
|
+
|
|
166
|
+
prod-build:
|
|
167
|
+
triggering:
|
|
168
|
+
events: [tag]
|
|
169
|
+
tag_patterns:
|
|
170
|
+
- pattern: "v*"
|
|
171
|
+
scripts:
|
|
172
|
+
- name: Build production
|
|
173
|
+
script: |
|
|
174
|
+
flutter build appbundle --release --dart-define=ENV=prod
|
|
175
|
+
```
|
|
176
|
+
|
|
177
|
+
## Secrets Management
|
|
178
|
+
|
|
179
|
+
### 시크릿 분류
|
|
180
|
+
|
|
181
|
+
```
|
|
182
|
+
Level 1 — 최고 기밀 (유출 시 앱 탈취):
|
|
183
|
+
├── Android keystore + 비밀번호
|
|
184
|
+
├── iOS Distribution 인증서 + 개인키
|
|
185
|
+
└── App Store Connect API Key (.p8)
|
|
186
|
+
|
|
187
|
+
Level 2 — 기밀 (유출 시 서비스 악용):
|
|
188
|
+
├── Google Play 서비스 계정 키
|
|
189
|
+
├── Firebase 서비스 계정
|
|
190
|
+
├── Sentry DSN (prod)
|
|
191
|
+
└── match 암호화 비밀번호
|
|
192
|
+
|
|
193
|
+
Level 3 — 일반 (유출 시 정보 노출):
|
|
194
|
+
├── Slack Webhook URL
|
|
195
|
+
├── Codecov Token
|
|
196
|
+
└── Firebase App ID
|
|
197
|
+
```
|
|
198
|
+
|
|
199
|
+
### 시크릿 저장 위치
|
|
200
|
+
|
|
201
|
+
```
|
|
202
|
+
GitHub Actions:
|
|
203
|
+
Repository > Settings > Secrets and variables > Actions
|
|
204
|
+
├── Repository secrets (레포 전체)
|
|
205
|
+
└── Environment secrets (환경별 분리)
|
|
206
|
+
├── production (승인 필요 설정 가능)
|
|
207
|
+
└── staging
|
|
208
|
+
|
|
209
|
+
Codemagic:
|
|
210
|
+
App Settings > Environment variables
|
|
211
|
+
├── Variable groups (그룹별 관리)
|
|
212
|
+
└── Secure 체크 → 로그에 마스킹
|
|
213
|
+
|
|
214
|
+
Fastlane:
|
|
215
|
+
.env 파일 (로컬) + CI 환경변수 (CI)
|
|
216
|
+
├── .env.default (공통, .gitignore)
|
|
217
|
+
└── .env.production (.gitignore)
|
|
218
|
+
```
|
|
219
|
+
|
|
220
|
+
### 시크릿 접근 제한
|
|
221
|
+
|
|
222
|
+
```yaml
|
|
223
|
+
# GitHub Actions: 환경별 보호 규칙
|
|
224
|
+
# Repository > Settings > Environments > production
|
|
225
|
+
# ├── Required reviewers: 2명
|
|
226
|
+
# ├── Wait timer: 5분
|
|
227
|
+
# └── Deployment branches: main only
|
|
228
|
+
|
|
229
|
+
jobs:
|
|
230
|
+
deploy-production:
|
|
231
|
+
runs-on: ubuntu-latest
|
|
232
|
+
environment: production # 승인 필요
|
|
233
|
+
steps:
|
|
234
|
+
- name: Deploy
|
|
235
|
+
env:
|
|
236
|
+
# production 환경의 시크릿만 접근 가능
|
|
237
|
+
KEYSTORE: ${{ secrets.ANDROID_KEYSTORE_BASE64 }}
|
|
238
|
+
run: ...
|
|
239
|
+
```
|
|
240
|
+
|
|
241
|
+
## Rollback Procedures
|
|
242
|
+
|
|
243
|
+
### iOS 롤백
|
|
244
|
+
|
|
245
|
+
```
|
|
246
|
+
App Store:
|
|
247
|
+
1. 이전 버전으로 롤백 불가 (Apple 정책)
|
|
248
|
+
2. 대안: 핫픽스 빌드를 새 버전으로 긴급 제출
|
|
249
|
+
- Expedited Review 요청 (App Store Connect)
|
|
250
|
+
- 심사 시간: 보통 24시간 → 긴급 시 수시간
|
|
251
|
+
3. TestFlight: 이전 빌드 재활성화 가능
|
|
252
|
+
|
|
253
|
+
긴급 핫픽스 절차:
|
|
254
|
+
git checkout -b hotfix/v1.2.4 v1.2.3
|
|
255
|
+
# 수정 적용
|
|
256
|
+
git tag v1.2.4
|
|
257
|
+
git push origin v1.2.4
|
|
258
|
+
# → CI 자동 빌드 → TestFlight 업로드 → 긴급 심사 요청
|
|
259
|
+
```
|
|
260
|
+
|
|
261
|
+
### Android 롤백
|
|
262
|
+
|
|
263
|
+
```
|
|
264
|
+
Play Store:
|
|
265
|
+
1. Console > Release > Production > Release history
|
|
266
|
+
2. 이전 릴리스의 "..." > "Release to Production"
|
|
267
|
+
(이전 버전을 새 릴리스로 재배포)
|
|
268
|
+
3. 또는 단계적 출시 중이라면 "Halt rollout"
|
|
269
|
+
|
|
270
|
+
자동 롤백 (Fastlane):
|
|
271
|
+
lane :rollback do |options|
|
|
272
|
+
version_code = options[:version_code]
|
|
273
|
+
# Play Store에서 이전 AAB 다운로드 후 재배포
|
|
274
|
+
# 또는 CI artifact에서 이전 빌드 가져와 재업로드
|
|
275
|
+
supply(
|
|
276
|
+
track: "production",
|
|
277
|
+
rollout: "1.0",
|
|
278
|
+
version_code: version_code,
|
|
279
|
+
json_key: ENV["GOOGLE_PLAY_JSON_KEY_PATH"],
|
|
280
|
+
)
|
|
281
|
+
end
|
|
282
|
+
```
|
|
283
|
+
|
|
284
|
+
### 롤백 판단 기준
|
|
285
|
+
|
|
286
|
+
```
|
|
287
|
+
즉시 롤백 (< 1시간 이내 판단):
|
|
288
|
+
├── 크래시율 > 2% (이전 버전 대비 3배 이상 증가)
|
|
289
|
+
├── ANR 비율 > 1%
|
|
290
|
+
├── 결제/인증 등 핵심 기능 장애
|
|
291
|
+
└── 데이터 손실 발생
|
|
292
|
+
|
|
293
|
+
모니터링 후 판단 (24시간):
|
|
294
|
+
├── 크래시율 1-2%
|
|
295
|
+
├── 특정 기기/OS 버전에서만 발생
|
|
296
|
+
├── 사용자 리뷰 평점 급락
|
|
297
|
+
└── 비핵심 기능 장애
|
|
298
|
+
|
|
299
|
+
유지 + 핫픽스:
|
|
300
|
+
├── 크래시율 < 1% (경미)
|
|
301
|
+
├── UI 버그 (기능 작동)
|
|
302
|
+
└── 성능 저하 (크래시 아님)
|
|
303
|
+
```
|
|
304
|
+
|
|
305
|
+
## Common Pitfalls
|
|
306
|
+
|
|
307
|
+
1. **빌드 넘버 충돌**: iOS/Android 빌드 넘버를 별도 관리 → 동일 CI 변수 사용 권장
|
|
308
|
+
2. **캐시 오염**: Flutter/Gradle/CocoaPods 캐시가 오래되면 빌드 실패 → 주기적 캐시 무효화
|
|
309
|
+
3. **시크릿 로그 노출**: `echo $SECRET` → CI 로그에 노출 → 항상 마스킹 확인
|
|
310
|
+
4. **인증서 만료**: match 인증서 1년 만료 → 알림 설정 필수
|
|
311
|
+
5. **Xcode 버전 차이**: CI와 로컬 Xcode 버전 불일치 → `xcode: 16.2` 고정
|
|
312
|
+
6. **Gradle 메모리**: Android 빌드 OOM → `org.gradle.jvmargs=-Xmx4g` 설정
|
|
313
|
+
7. **동시 빌드**: 같은 브랜치 여러 빌드 → `cancel_previous_builds` 설정
|
|
314
|
+
8. **태그 실수**: 잘못된 태그 푸시 → production 빌드 트리거 → 태그 보호 규칙 설정
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Code Signing
|
|
3
|
+
impact: CRITICAL
|
|
4
|
+
impactDescription: "서명 누락 → 스토어 업로드 불가, 키 유출 → 앱 탈취 위험"
|
|
5
|
+
tags: code-signing, ios, android, provisioning, keystore, match
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
## Code Signing
|
|
9
|
+
|
|
10
|
+
**Impact: CRITICAL (서명 누락 → 스토어 업로드 불가, 키 유출 → 앱 탈취 위험)**
|
|
11
|
+
|
|
12
|
+
iOS provisioning profile, Android keystore 관리. CI 환경에서 안전한 서명 키 주입.
|
|
13
|
+
|
|
14
|
+
### iOS 코드 서명 (match)
|
|
15
|
+
|
|
16
|
+
**Incorrect (수동 관리):**
|
|
17
|
+
```
|
|
18
|
+
Xcode > Automatically manage signing 체크
|
|
19
|
+
→ 개발자마다 다른 profile, CI 빌드 실패, 인증서 충돌
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
**Correct (match로 중앙 관리):**
|
|
23
|
+
```ruby
|
|
24
|
+
# fastlane/Matchfile
|
|
25
|
+
git_url(ENV["MATCH_GIT_URL"])
|
|
26
|
+
storage_mode("git")
|
|
27
|
+
type("appstore")
|
|
28
|
+
app_identifier("com.yourapp.id")
|
|
29
|
+
|
|
30
|
+
# CI에서 실행
|
|
31
|
+
create_keychain(
|
|
32
|
+
name: "ci_keychain",
|
|
33
|
+
password: ENV["KEYCHAIN_PASSWORD"],
|
|
34
|
+
default_keychain: true, unlock: true, timeout: 3600,
|
|
35
|
+
)
|
|
36
|
+
match(
|
|
37
|
+
type: "appstore",
|
|
38
|
+
keychain_name: "ci_keychain",
|
|
39
|
+
keychain_password: ENV["KEYCHAIN_PASSWORD"],
|
|
40
|
+
readonly: true, # CI에서는 readonly — 인증서 생성 금지
|
|
41
|
+
)
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
### Android 코드 서명
|
|
45
|
+
|
|
46
|
+
**Incorrect (keystore를 레포에 포함):**
|
|
47
|
+
```
|
|
48
|
+
android/app/release.keystore # 절대 금지!
|
|
49
|
+
android/key.properties # 비밀번호 하드코딩
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
**Correct (CI 환경변수로 주입):**
|
|
53
|
+
```groovy
|
|
54
|
+
// android/app/build.gradle
|
|
55
|
+
android {
|
|
56
|
+
signingConfigs {
|
|
57
|
+
release {
|
|
58
|
+
if (System.getenv("ANDROID_KEYSTORE_BASE64")) {
|
|
59
|
+
storeFile file("../release.keystore")
|
|
60
|
+
storePassword System.getenv("ANDROID_KEYSTORE_PASSWORD")
|
|
61
|
+
keyAlias System.getenv("ANDROID_KEY_ALIAS")
|
|
62
|
+
keyPassword System.getenv("ANDROID_KEY_PASSWORD")
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
```bash
|
|
70
|
+
# CI에서 keystore 복원
|
|
71
|
+
echo "$ANDROID_KEYSTORE_BASE64" | base64 --decode > android/release.keystore
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
### 개발/배포 인증서 분리
|
|
75
|
+
|
|
76
|
+
```
|
|
77
|
+
├── Development — 디버그/테스트 (iOS provisioning, Android debug.keystore)
|
|
78
|
+
├── Ad Hoc — 내부 테스트 배포 (iOS only)
|
|
79
|
+
└── Distribution — 스토어 제출 (iOS App Store, Android release.keystore)
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
### 키 로테이션
|
|
83
|
+
|
|
84
|
+
- iOS: Apple 인증서 1년 만료 → `match nuke distribution` → `match appstore`
|
|
85
|
+
- Android: Play App Signing 활성화 → Google이 앱 서명 키 관리, upload key만 팀 관리
|
|
86
|
+
- Upload Key 분실 시 → Google Play Console에서 리셋 요청
|
|
87
|
+
|
|
88
|
+
### .gitignore 필수 항목
|
|
89
|
+
|
|
90
|
+
```gitignore
|
|
91
|
+
*.mobileprovision
|
|
92
|
+
*.p12
|
|
93
|
+
*.cer
|
|
94
|
+
*.keystore
|
|
95
|
+
*.jks
|
|
96
|
+
key.properties
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
### 규칙
|
|
100
|
+
|
|
101
|
+
- `match` 로 iOS 인증서 관리 — 수동 Xcode 서명 관리 금지
|
|
102
|
+
- CI에서 `match(readonly: true)` — 인증서 신규 생성 방지
|
|
103
|
+
- Android keystore → base64 → CI 환경변수 주입
|
|
104
|
+
- `key.properties` `.gitignore` 추가 — 레포 커밋 금지
|
|
105
|
+
- Play App Signing 활성화 — upload key만 팀 관리
|
|
106
|
+
- 개발/배포 인증서 분리 — 동일 인증서 혼용 금지
|
|
@@ -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")` 소규모 배포 후 확대
|