From 45e79bdebde7d621076bbcb236b3a844b62c49c8 Mon Sep 17 00:00:00 2001 From: toki Date: Sat, 4 Apr 2026 18:02:44 +0900 Subject: [PATCH] Update pipeline files and clean up refactoring documents --- .codex | 0 lib/oto/pipeline/pipeline_exe.dart | 20 +++++ lib/oto/pipeline/pipeline_exe_handle.dart | 11 +++ lib/oto/pipeline/pipeline_foreach.dart | 21 +++++ lib/oto/pipeline/pipeline_if.dart | 13 +++ lib/oto/pipeline/pipeline_switch.dart | 15 ++++ lib/oto/pipeline/pipeline_wait_until.dart | 14 ++++ lib/oto/pipeline/pipeline_while.dart | 9 +++ refactoring/history_001.md | 94 ---------------------- refactoring/to-do.md | 98 ----------------------- 10 files changed, 103 insertions(+), 192 deletions(-) create mode 100644 .codex delete mode 100644 refactoring/history_001.md delete mode 100644 refactoring/to-do.md diff --git a/.codex b/.codex new file mode 100644 index 0000000..e69de29 diff --git a/lib/oto/pipeline/pipeline_exe.dart b/lib/oto/pipeline/pipeline_exe.dart index af4ac6b..eef637f 100644 --- a/lib/oto/pipeline/pipeline_exe.dart +++ b/lib/oto/pipeline/pipeline_exe.dart @@ -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_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 { //set & validate data PipelineValidateResult initialize(Map 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 { late String commandID; 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 { @override Future execute() async { diff --git a/lib/oto/pipeline/pipeline_exe_handle.dart b/lib/oto/pipeline/pipeline_exe_handle.dart index cb14441..afd0165 100644 --- a/lib/oto/pipeline/pipeline_exe_handle.dart +++ b/lib/oto/pipeline/pipeline_exe_handle.dart @@ -12,6 +12,17 @@ class ExeHandleData { 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 { late DataExeHandle data; late String commandID; diff --git a/lib/oto/pipeline/pipeline_foreach.dart b/lib/oto/pipeline/pipeline_foreach.dart index 8525673..a35c674 100644 --- a/lib/oto/pipeline/pipeline_foreach.dart +++ b/lib/oto/pipeline/pipeline_foreach.dart @@ -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_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: +/// setValue: <@property.item> +/// on-do: +/// - exe: processItem +/// +/// # Map iteration +/// - foreach: +/// iterator: +/// setKey: <@property.key> # optional +/// setValue: <@property.value> +/// on-do: +/// - exe: processEntry +/// ``` class PipelineForeach extends PipelineExecutor { late Pipeline? _pipelineOnDo; late DataForeach data; diff --git a/lib/oto/pipeline/pipeline_if.dart b/lib/oto/pipeline/pipeline_if.dart index 38a0b56..f1f824e 100644 --- a/lib/oto/pipeline/pipeline_if.dart +++ b/lib/oto/pipeline/pipeline_if.dart @@ -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_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: " == prod" +/// on-true: +/// - exe: deployProd +/// on-false: +/// - exe: deployDev +/// ``` class PipelineIf extends PipelineCondition { late Pipeline? _pipelineTrue; late Pipeline? _pipelineFalse; diff --git a/lib/oto/pipeline/pipeline_switch.dart b/lib/oto/pipeline/pipeline_switch.dart index 44c876e..f78f682 100644 --- a/lib/oto/pipeline/pipeline_switch.dart +++ b/lib/oto/pipeline/pipeline_switch.dart @@ -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_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: "" +/// cases: +/// - value: prod +/// tasks: +/// - exe: deployProd +/// - value: staging +/// tasks: +/// - exe: deployStaging +/// ``` class PipelineSwitch extends PipelineCondition { late Pipeline? _pipeline; diff --git a/lib/oto/pipeline/pipeline_wait_until.dart b/lib/oto/pipeline/pipeline_wait_until.dart index eff7bf4..98d515d 100644 --- a/lib/oto/pipeline/pipeline_wait_until.dart +++ b/lib/oto/pipeline/pipeline_wait_until.dart @@ -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_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: "" # waits while true +/// - wait-until-string: " == done" # waits while condition holds +/// ``` class PipelineWaitUntil extends PipelineCondition { PipelineWaitUntil(String type) { 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 { PipelineWaitUntilSeconds(); diff --git a/lib/oto/pipeline/pipeline_while.dart b/lib/oto/pipeline/pipeline_while.dart index 8a9e13b..9d47f93 100644 --- a/lib/oto/pipeline/pipeline_while.dart +++ b/lib/oto/pipeline/pipeline_while.dart @@ -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_exe.dart'; +/// Repeatedly executes on-do pipeline while the condition is true. +/// +/// Example YAML: +/// ```yaml +/// - while: +/// condition-bool: "" +/// on-do: +/// - exe: checkStatus +/// ``` class PipelineWhile extends PipelineCondition { late Pipeline? _pipelineOnDo; diff --git a/refactoring/history_001.md b/refactoring/history_001.md deleted file mode 100644 index 9d7c866..0000000 --- a/refactoring/history_001.md +++ /dev/null @@ -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` 참조 diff --git a/refactoring/to-do.md b/refactoring/to-do.md deleted file mode 100644 index 985cf31..0000000 --- a/refactoring/to-do.md +++ /dev/null @@ -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>`) - - `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: "" -/// 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` 실행 포함) -- 태그 문법 레퍼런스 (``, ``, ``) -- 에러 핸들링 패턴 (`application.dart` catch 구조) -- 주의사항 (xcworkspace/xcodeproj `??` 패턴 등)