Update pipeline files and clean up refactoring documents
This commit is contained in:
parent
a54f54ef20
commit
45e79bdebd
10 changed files with 103 additions and 192 deletions
0
.codex
Normal file
0
.codex
Normal file
|
|
@ -6,6 +6,14 @@ import 'package:oto_cli/oto/data/command_data.dart';
|
||||||
import 'package:oto_cli/oto/pipeline/pipeline.dart';
|
import 'package:oto_cli/oto/pipeline/pipeline.dart';
|
||||||
import 'package:oto_cli/oto/pipeline/pipeline_condition.dart';
|
import 'package:oto_cli/oto/pipeline/pipeline_condition.dart';
|
||||||
|
|
||||||
|
/// Base class for all pipeline executors.
|
||||||
|
///
|
||||||
|
/// Example YAML (workflow snippet):
|
||||||
|
/// ```yaml
|
||||||
|
/// workflow:
|
||||||
|
/// - exe: buildIos # execute a command by ID
|
||||||
|
/// - async: uploadSymbols # execute a command asynchronously (non-blocking)
|
||||||
|
/// ```
|
||||||
abstract class PipelineExecutor {
|
abstract class PipelineExecutor {
|
||||||
//set & validate data
|
//set & validate data
|
||||||
PipelineValidateResult initialize(Map<String, dynamic> set);
|
PipelineValidateResult initialize(Map<String, dynamic> set);
|
||||||
|
|
@ -29,6 +37,12 @@ abstract class PipelineExecutor {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Executes a single command synchronously by its command ID.
|
||||||
|
///
|
||||||
|
/// Example YAML:
|
||||||
|
/// ```yaml
|
||||||
|
/// - exe: buildIos
|
||||||
|
/// ```
|
||||||
class PipelineExe extends PipelineExecutor {
|
class PipelineExe extends PipelineExecutor {
|
||||||
late String commandID;
|
late String commandID;
|
||||||
String printPrefix = '';
|
String printPrefix = '';
|
||||||
|
|
@ -79,6 +93,12 @@ class PipelineExe extends PipelineExecutor {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Executes a command asynchronously (fire-and-forget, does not wait for completion).
|
||||||
|
///
|
||||||
|
/// Example YAML:
|
||||||
|
/// ```yaml
|
||||||
|
/// - async: uploadSymbols
|
||||||
|
/// ```
|
||||||
class PipelineAsync extends PipelineExe {
|
class PipelineAsync extends PipelineExe {
|
||||||
@override
|
@override
|
||||||
Future execute() async {
|
Future execute() async {
|
||||||
|
|
|
||||||
|
|
@ -12,6 +12,17 @@ class ExeHandleData {
|
||||||
late Pipeline? pipelineFail;
|
late Pipeline? pipelineFail;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Executes a command and branches to on-success or on-fail pipeline based on result.
|
||||||
|
///
|
||||||
|
/// Example YAML:
|
||||||
|
/// ```yaml
|
||||||
|
/// - exe-handle:
|
||||||
|
/// id: buildIos
|
||||||
|
/// on-success:
|
||||||
|
/// - exe: notifySuccess
|
||||||
|
/// on-fail:
|
||||||
|
/// - exe: notifyFail
|
||||||
|
/// ```
|
||||||
class PipelineExeHandle extends PipelineExecutor {
|
class PipelineExeHandle extends PipelineExecutor {
|
||||||
late DataExeHandle data;
|
late DataExeHandle data;
|
||||||
late String commandID;
|
late String commandID;
|
||||||
|
|
|
||||||
|
|
@ -6,6 +6,27 @@ import 'package:oto_cli/oto/data/pipeline_data.dart';
|
||||||
import 'package:oto_cli/oto/pipeline/pipeline.dart';
|
import 'package:oto_cli/oto/pipeline/pipeline.dart';
|
||||||
import 'package:oto_cli/oto/pipeline/pipeline_exe.dart';
|
import 'package:oto_cli/oto/pipeline/pipeline_exe.dart';
|
||||||
|
|
||||||
|
/// Iterates over a List or Map and executes on-do pipeline for each element.
|
||||||
|
/// - List: sets setValue for each item.
|
||||||
|
/// - Map: sets setKey (optional) and setValue for each key-value pair.
|
||||||
|
///
|
||||||
|
/// Example YAML:
|
||||||
|
/// ```yaml
|
||||||
|
/// # List iteration
|
||||||
|
/// - foreach:
|
||||||
|
/// iterator: <!property.arr>
|
||||||
|
/// setValue: <@property.item>
|
||||||
|
/// on-do:
|
||||||
|
/// - exe: processItem
|
||||||
|
///
|
||||||
|
/// # Map iteration
|
||||||
|
/// - foreach:
|
||||||
|
/// iterator: <!property.map>
|
||||||
|
/// setKey: <@property.key> # optional
|
||||||
|
/// setValue: <@property.value>
|
||||||
|
/// on-do:
|
||||||
|
/// - exe: processEntry
|
||||||
|
/// ```
|
||||||
class PipelineForeach extends PipelineExecutor {
|
class PipelineForeach extends PipelineExecutor {
|
||||||
late Pipeline? _pipelineOnDo;
|
late Pipeline? _pipelineOnDo;
|
||||||
late DataForeach data;
|
late DataForeach data;
|
||||||
|
|
|
||||||
|
|
@ -4,6 +4,19 @@ import 'package:oto_cli/oto/pipeline/pipeline.dart';
|
||||||
import 'package:oto_cli/oto/pipeline/pipeline_condition.dart';
|
import 'package:oto_cli/oto/pipeline/pipeline_condition.dart';
|
||||||
import 'package:oto_cli/oto/pipeline/pipeline_exe.dart';
|
import 'package:oto_cli/oto/pipeline/pipeline_exe.dart';
|
||||||
|
|
||||||
|
/// Executes one of two pipelines based on a condition.
|
||||||
|
/// Condition types: condition-string, condition-int, condition-float,
|
||||||
|
/// condition-bool, condition-date, condition-version, condition-object
|
||||||
|
///
|
||||||
|
/// Example YAML:
|
||||||
|
/// ```yaml
|
||||||
|
/// - if:
|
||||||
|
/// condition-string: "<!property.env> == prod"
|
||||||
|
/// on-true:
|
||||||
|
/// - exe: deployProd
|
||||||
|
/// on-false:
|
||||||
|
/// - exe: deployDev
|
||||||
|
/// ```
|
||||||
class PipelineIf extends PipelineCondition {
|
class PipelineIf extends PipelineCondition {
|
||||||
late Pipeline? _pipelineTrue;
|
late Pipeline? _pipelineTrue;
|
||||||
late Pipeline? _pipelineFalse;
|
late Pipeline? _pipelineFalse;
|
||||||
|
|
|
||||||
|
|
@ -4,6 +4,21 @@ import 'package:oto_cli/oto/pipeline/pipeline.dart';
|
||||||
import 'package:oto_cli/oto/pipeline/pipeline_condition.dart';
|
import 'package:oto_cli/oto/pipeline/pipeline_condition.dart';
|
||||||
import 'package:oto_cli/oto/pipeline/pipeline_exe.dart';
|
import 'package:oto_cli/oto/pipeline/pipeline_exe.dart';
|
||||||
|
|
||||||
|
/// Executes tasks for the first matching case value.
|
||||||
|
/// Uses condition-string/int/etc. compared to each case value with ==.
|
||||||
|
///
|
||||||
|
/// Example YAML:
|
||||||
|
/// ```yaml
|
||||||
|
/// - switch:
|
||||||
|
/// condition-string: "<!property.env>"
|
||||||
|
/// cases:
|
||||||
|
/// - value: prod
|
||||||
|
/// tasks:
|
||||||
|
/// - exe: deployProd
|
||||||
|
/// - value: staging
|
||||||
|
/// tasks:
|
||||||
|
/// - exe: deployStaging
|
||||||
|
/// ```
|
||||||
class PipelineSwitch extends PipelineCondition {
|
class PipelineSwitch extends PipelineCondition {
|
||||||
late Pipeline? _pipeline;
|
late Pipeline? _pipeline;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -4,6 +4,14 @@ import 'package:oto_cli/oto/pipeline/pipeline.dart';
|
||||||
import 'package:oto_cli/oto/pipeline/pipeline_condition.dart';
|
import 'package:oto_cli/oto/pipeline/pipeline_condition.dart';
|
||||||
import 'package:oto_cli/oto/pipeline/pipeline_exe.dart';
|
import 'package:oto_cli/oto/pipeline/pipeline_exe.dart';
|
||||||
|
|
||||||
|
/// Polls every 200ms and blocks until the condition becomes false.
|
||||||
|
/// Use wait-until-int / wait-until-double / wait-until-string depending on type.
|
||||||
|
///
|
||||||
|
/// Example YAML:
|
||||||
|
/// ```yaml
|
||||||
|
/// - wait-until-bool: "<!property.isUploading>" # waits while true
|
||||||
|
/// - wait-until-string: "<!property.status> == done" # waits while condition holds
|
||||||
|
/// ```
|
||||||
class PipelineWaitUntil extends PipelineCondition {
|
class PipelineWaitUntil extends PipelineCondition {
|
||||||
PipelineWaitUntil(String type) {
|
PipelineWaitUntil(String type) {
|
||||||
data = DataCondition();
|
data = DataCondition();
|
||||||
|
|
@ -30,6 +38,12 @@ class PipelineWaitUntil extends PipelineCondition {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Waits for a fixed number of seconds (supports fractional values).
|
||||||
|
///
|
||||||
|
/// Example YAML:
|
||||||
|
/// ```yaml
|
||||||
|
/// - wait-until-seconds: 2.5
|
||||||
|
/// ```
|
||||||
class PipelineWaitUntilSeconds extends PipelineExecutor {
|
class PipelineWaitUntilSeconds extends PipelineExecutor {
|
||||||
PipelineWaitUntilSeconds();
|
PipelineWaitUntilSeconds();
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -4,6 +4,15 @@ import 'package:oto_cli/oto/pipeline/pipeline.dart';
|
||||||
import 'package:oto_cli/oto/pipeline/pipeline_condition.dart';
|
import 'package:oto_cli/oto/pipeline/pipeline_condition.dart';
|
||||||
import 'package:oto_cli/oto/pipeline/pipeline_exe.dart';
|
import 'package:oto_cli/oto/pipeline/pipeline_exe.dart';
|
||||||
|
|
||||||
|
/// Repeatedly executes on-do pipeline while the condition is true.
|
||||||
|
///
|
||||||
|
/// Example YAML:
|
||||||
|
/// ```yaml
|
||||||
|
/// - while:
|
||||||
|
/// condition-bool: "<!property.isRunning>"
|
||||||
|
/// on-do:
|
||||||
|
/// - exe: checkStatus
|
||||||
|
/// ```
|
||||||
class PipelineWhile extends PipelineCondition {
|
class PipelineWhile extends PipelineCondition {
|
||||||
late Pipeline? _pipelineOnDo;
|
late Pipeline? _pipelineOnDo;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,94 +0,0 @@
|
||||||
# Refactoring History 001 — command_data.dart 도메인 분리
|
|
||||||
|
|
||||||
## 목표
|
|
||||||
AI(Claude 등)가 코드베이스를 더 효율적으로 읽고 수정할 수 있도록
|
|
||||||
하나의 거대한 파일에 집중된 데이터 클래스들을 도메인별로 분리한다.
|
|
||||||
|
|
||||||
## 배경
|
|
||||||
`command_data.dart` 파일에 100개 이상의 JSON 직렬화 데이터 클래스가
|
|
||||||
단일 파일에 존재했다. `json_serializable`의 `build_runner`가 하나의
|
|
||||||
`command_data.g.dart`를 생성하는 구조라 관리 편의상 통합되어 있었으나,
|
|
||||||
AI가 단일 커맨드를 수정할 때도 전체 파일을 읽어야 해서 컨텍스트 낭비가 컸다.
|
|
||||||
|
|
||||||
AI가 코드 관리를 지원하는 환경에서는 파일 파편화 비용이 사라지므로
|
|
||||||
도메인 분리 결정을 내렸다.
|
|
||||||
|
|
||||||
## 변경 내용
|
|
||||||
|
|
||||||
### 새로 생성된 파일
|
|
||||||
|
|
||||||
| 파일 | 내용 | 클래스 수 |
|
|
||||||
|------|------|-----------|
|
|
||||||
| `lib/oto/data/base_data.dart` | DataParam, DataFile (모든 커맨드 데이터의 베이스) | 2 |
|
|
||||||
| `lib/oto/data/build_data.dart` | iOS/Flutter/MSBuild/Xcode/Testflight 등 빌드 관련 | 19 |
|
|
||||||
| `lib/oto/data/file_data.dart` | Copy/Delete/Rename/Zip/FileRead/FileWrite 등 | 10 |
|
|
||||||
| `lib/oto/data/git_data.dart` | Git/GitHub 커맨드 데이터 | 15 |
|
|
||||||
| `lib/oto/data/notification_data.dart` | Slack, Mattermost 발송 데이터 | 5 |
|
|
||||||
| `lib/oto/data/integration_data.dart` | Jira, Jenkins 연동 데이터 | 3 |
|
|
||||||
| `lib/oto/data/network_data.dart` | FTP, Web 요청 데이터 | 3 |
|
|
||||||
| `lib/oto/data/infra_data.dart` | Docker, AWS CLI, Gradle, Protobuf | 4 |
|
|
||||||
| `lib/oto/data/util_data.dart` | Shell, String, JSON, Process, SMB, Timer 등 | 20 |
|
|
||||||
|
|
||||||
### 수정된 파일
|
|
||||||
|
|
||||||
**`lib/oto/data/command_data.dart`**
|
|
||||||
- DataParam, DataBuildFlutter 등 모든 도메인 클래스 제거
|
|
||||||
- DataBuild, DataScheduler, DataCommand 3개만 유지 (파이프라인 루트 데이터)
|
|
||||||
- 도메인 파일 9개를 `export`하는 barrel 파일로 전환
|
|
||||||
|
|
||||||
### 영향받은 파일
|
|
||||||
없음. 기존 48개 파일의 `import 'package:oto_cli/oto/data/command_data.dart'`가
|
|
||||||
barrel export를 통해 그대로 동작하므로 import 수정 불필요.
|
|
||||||
|
|
||||||
### 생성된 .g.dart 파일
|
|
||||||
`build_runner build --delete-conflicting-outputs` 실행으로 자동 생성됨:
|
|
||||||
- `base_data.g.dart`
|
|
||||||
- `build_data.g.dart`
|
|
||||||
- `file_data.g.dart`
|
|
||||||
- `git_data.g.dart`
|
|
||||||
- `notification_data.g.dart`
|
|
||||||
- `integration_data.g.dart`
|
|
||||||
- `network_data.g.dart`
|
|
||||||
- `infra_data.g.dart`
|
|
||||||
- `util_data.g.dart`
|
|
||||||
|
|
||||||
## 결과
|
|
||||||
- `dart analyze` 에러 0개 (기존 warning 1개 그대로)
|
|
||||||
- `build_runner` 278 outputs 성공
|
|
||||||
- AI가 특정 도메인 커맨드 수정 시 읽어야 할 코드량 대폭 감소
|
|
||||||
- 예: Flutter 빌드 수정 → `build_data.dart` (~330줄) 만 읽으면 됨
|
|
||||||
- 이전: `command_data.dart` (1300+ 줄) 전체를 읽어야 했음
|
|
||||||
|
|
||||||
## 현재 data/ 디렉토리 구조
|
|
||||||
```
|
|
||||||
lib/oto/data/
|
|
||||||
├── command_data.dart ← barrel (DataBuild, DataScheduler, DataCommand + exports)
|
|
||||||
├── command_data.g.dart
|
|
||||||
├── base_data.dart ← DataParam, DataFile 베이스 클래스
|
|
||||||
├── base_data.g.dart
|
|
||||||
├── build_data.dart ← 빌드 관련 (iOS, Flutter, MSBuild 등)
|
|
||||||
├── build_data.g.dart
|
|
||||||
├── file_data.dart ← 파일 시스템 operations
|
|
||||||
├── file_data.g.dart
|
|
||||||
├── git_data.dart ← Git, GitHub
|
|
||||||
├── git_data.g.dart
|
|
||||||
├── notification_data.dart ← Slack, Mattermost
|
|
||||||
├── notification_data.g.dart
|
|
||||||
├── integration_data.dart ← Jira, Jenkins
|
|
||||||
├── integration_data.g.dart
|
|
||||||
├── network_data.dart ← FTP, Web
|
|
||||||
├── network_data.g.dart
|
|
||||||
├── infra_data.dart ← Docker, AWS, Gradle, Protobuf
|
|
||||||
├── infra_data.g.dart
|
|
||||||
├── util_data.dart ← Shell, String, JSON, Process, Timer 등
|
|
||||||
├── util_data.g.dart
|
|
||||||
├── pipeline_data.dart ← 기존 파이프라인 구조 데이터 (변경 없음)
|
|
||||||
├── pipeline_data.g.dart
|
|
||||||
├── jenkins_data.dart ← Jenkins 환경변수 응답 데이터 (변경 없음)
|
|
||||||
├── jenkins_data.g.dart
|
|
||||||
├── jira_data.dart ← Jira API 응답 데이터 (변경 없음)
|
|
||||||
└── jira_data.g.dart
|
|
||||||
```
|
|
||||||
|
|
||||||
## 다음 작업
|
|
||||||
`refactoring/to-do.md` 참조
|
|
||||||
|
|
@ -1,98 +0,0 @@
|
||||||
# AI-Friendly Refactoring To-Do
|
|
||||||
|
|
||||||
AI(Claude 등)가 코드베이스를 더 잘 이해하고 수정할 수 있도록 하는
|
|
||||||
리팩토링 작업 목록. 우선순위 순으로 정렬.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## ✅ 완료
|
|
||||||
|
|
||||||
### 1. command_data.dart 도메인별 분리
|
|
||||||
- **작업**: 1300줄짜리 단일 파일을 9개 도메인 파일로 분리
|
|
||||||
- **결과**: AI가 특정 커맨드 수정 시 읽어야 할 코드량 대폭 감소
|
|
||||||
- **상세**: `refactoring/history_001.md` 참조
|
|
||||||
|
|
||||||
### 2. commands/util/ 폴더 정리 — 잘못 배치된 커맨드 이동
|
|
||||||
- **작업**: `util/`의 도메인 혼재 파일 5개 이동, 중복 파일 1개 삭제
|
|
||||||
- **결과**:
|
|
||||||
- `util/slack.dart` → `notification/slack.dart`
|
|
||||||
- `util/mattermost.dart` → `notification/mattermost.dart`
|
|
||||||
- `util/publish_ios.dart` → `build/publish_ios.dart`
|
|
||||||
- `util/zip.dart` → `file/zip.dart`
|
|
||||||
- `util/smb_authorize.dart` → `infra/smb_authorize.dart`
|
|
||||||
- `util/jira.dart` → 삭제 (`jira/jira.dart`와 중복, import 없음)
|
|
||||||
- `command.dart` import 경로 5곳 수정
|
|
||||||
|
|
||||||
### 3. Self-registering Command 패턴
|
|
||||||
- **작업**: `command.dart`의 import 40개 + `_commandMap` 리터럴 78줄 제거, registry 패턴 도입
|
|
||||||
- **결과**:
|
|
||||||
- `Command.register()` static 메서드 추가
|
|
||||||
- 커맨드 파일 39개에 `registerXxx()` top-level 함수 추가
|
|
||||||
- `commands/command_registry.dart` 신규 생성 — 모든 커맨드 등록 중앙화
|
|
||||||
- `application.dart`의 `build()` 초반에 `registerAllCommands()` 호출
|
|
||||||
- **새 커맨드 추가 절차**:
|
|
||||||
1. 커맨드 파일 작성 + 해당 파일의 `registerXxx()`에 1줄 추가
|
|
||||||
2. `command.dart` enum에 1줄 추가 → `build_runner` 재실행
|
|
||||||
3. `command_registry.dart`에 import + 함수 호출 추가 (신규 파일인 경우)
|
|
||||||
|
|
||||||
### 4. 태그 시스템 중앙화
|
|
||||||
- **작업**: 태그 파싱/치환 로직을 `command.dart`에서 분리
|
|
||||||
- **결과**:
|
|
||||||
- `lib/oto/core/tag_system.dart` 신규 생성 — `TagSystem` 클래스에 전체 구현 이동
|
|
||||||
- 태그 문법 레퍼런스 주석 포함 (`<!property.key>`, `<!state.cmdId>`, `<@property.key>`)
|
|
||||||
- `command.dart`의 태그 메서드는 `TagSystem`으로의 얇은 위임(delegate)만 유지
|
|
||||||
- 기존 `Command.replaceTagValue()` 등 외부 호출부는 수정 없이 그대로 동작
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 📋 할 일
|
|
||||||
|
|
||||||
### 5. Pipeline Executor에 YAML 예제 주석 추가
|
|
||||||
**우선순위: 중간 / 난이도: 낮음**
|
|
||||||
|
|
||||||
현재 `assets/yaml/example/` 폴더에 예제가 따로 있어 코드-예제 매핑이 안 된다.
|
|
||||||
각 PipelineExecutor 클래스 상단에 인라인 YAML 예제를 주석으로 추가하면
|
|
||||||
AI가 파이프라인 구조를 한 파일만 읽어도 파악할 수 있다.
|
|
||||||
|
|
||||||
대상 파일:
|
|
||||||
- `lib/oto/pipeline/pipeline_exe.dart`
|
|
||||||
- `lib/oto/pipeline/pipeline_if.dart`
|
|
||||||
- `lib/oto/pipeline/pipeline_foreach.dart`
|
|
||||||
- `lib/oto/pipeline/pipeline_while.dart`
|
|
||||||
- `lib/oto/pipeline/pipeline_switch.dart`
|
|
||||||
- `lib/oto/pipeline/pipeline_exe_handle.dart`
|
|
||||||
- `lib/oto/pipeline/pipeline_wait_until.dart`
|
|
||||||
|
|
||||||
예시:
|
|
||||||
```dart
|
|
||||||
/// Example YAML:
|
|
||||||
/// ```yaml
|
|
||||||
/// - if:
|
|
||||||
/// equal-string: "<!property.env>"
|
|
||||||
/// value: "prod"
|
|
||||||
/// on-true:
|
|
||||||
/// - exe: deployProd
|
|
||||||
/// on-false:
|
|
||||||
/// - exe: deployDev
|
|
||||||
/// ```
|
|
||||||
class PipelineIf extends PipelineExecutor { ... }
|
|
||||||
```
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
### 6. CLAUDE.md 작성 — 마지막에 작성
|
|
||||||
**우선순위: 높음 / 난이도: 낮음 / 시점: 모든 리팩토링 완료 후**
|
|
||||||
|
|
||||||
> ⚠️ 리팩토링이 진행되면 구조가 계속 바뀌므로 반드시 마지막에 작성한다.
|
|
||||||
|
|
||||||
프로젝트 루트에 `CLAUDE.md`를 두면 Claude가 대화 시작 시 자동으로 읽는다.
|
|
||||||
매 대화마다 프로젝트 구조를 다시 파악하는 비용을 없앨 수 있다.
|
|
||||||
|
|
||||||
포함할 내용:
|
|
||||||
- 프로젝트 개요 (Dart CLI, Jenkins 연동 빌드 자동화)
|
|
||||||
- 최종 디렉토리 구조 요약
|
|
||||||
- 새 커맨드 추가 방법 (단계별)
|
|
||||||
- 새 도메인 데이터 클래스 추가 방법 (`build_runner` 실행 포함)
|
|
||||||
- 태그 문법 레퍼런스 (`<!property.key>`, `<!state.cmdId>`, `<!common.field>`)
|
|
||||||
- 에러 핸들링 패턴 (`application.dart` catch 구조)
|
|
||||||
- 주의사항 (xcworkspace/xcodeproj `??` 패턴 등)
|
|
||||||
Loading…
Reference in a new issue