Refactor: Move util commands to appropriate directories and update command.dart

This commit is contained in:
toki 2026-04-04 12:55:33 +09:00
parent 6210ab09ad
commit 144f46bf65
8 changed files with 121 additions and 123 deletions

View file

@ -29,10 +29,10 @@ import 'package:oto_cli/oto/commands/jira/jira.dart';
import 'package:oto_cli/oto/commands/process/process.dart';
import 'package:oto_cli/oto/commands/proto/protobuf.dart';
import 'package:oto_cli/oto/commands/util/json.dart';
import 'package:oto_cli/oto/commands/util/mattermost.dart';
import 'package:oto_cli/oto/commands/notification/mattermost.dart';
import 'package:oto_cli/oto/commands/util/print.dart';
import 'package:oto_cli/oto/commands/util/set_value.dart';
import 'package:oto_cli/oto/commands/util/smb_authorize.dart';
import 'package:oto_cli/oto/commands/infra/smb_authorize.dart';
import 'package:oto_cli/oto/commands/util/string_util.dart';
import 'package:oto_cli/oto/commands/util/timer.dart';
import 'package:oto_cli/oto/commands/web/web.dart';
@ -42,9 +42,9 @@ import 'package:oto_cli/oto/commands/file/delete.dart';
import 'package:oto_cli/oto/commands/file/rename.dart';
import 'package:oto_cli/oto/commands/shell/shell.dart';
import 'package:oto_cli/oto/commands/file/copy.dart';
import 'package:oto_cli/oto/commands/util/publish_ios.dart';
import 'package:oto_cli/oto/commands/util/slack.dart';
import 'package:oto_cli/oto/commands/util/zip.dart';
import 'package:oto_cli/oto/commands/build/publish_ios.dart';
import 'package:oto_cli/oto/commands/notification/slack.dart';
import 'package:oto_cli/oto/commands/file/zip.dart';
import 'package:oto_cli/oto/commands/ftp/upload.dart';
import 'package:oto_cli/oto/pipeline/pipeline_exe_handle.dart';

View file

@ -1,118 +0,0 @@
// ignore_for_file: prefer_function_declarations_over_variables, avoid_init_to_null
import 'dart:convert';
import 'package:dart_framework/utils/slack/slack_data.dart';
import 'package:dart_framework/utils/system_util.dart';
import 'package:oto_cli/oto/application.dart';
import 'package:oto_cli/oto/commands/command.dart';
import 'package:oto_cli/oto/data/command_data.dart';
import 'package:http/http.dart' as http;
import 'package:oto_cli/oto/data/jira_data.dart';
class Jira extends Command {
String? findContent(DataJiraContent? data) {
if (data != null) {
if (data.text != null) {
return data.text;
} else {
if (data.content != null && data.content!.isNotEmpty) {
return findContent(data.content!.first);
} else {
return null;
}
}
} else {
return null;
}
}
@override
Future execute(DataCommand command) async {
var data = DataJira.fromJson(command.param);
var text = '${data.id}:${data.token}';
var base64 = base64Encode(utf8.encode(text));
var header = {
'Content-Type': 'application/json',
'Authorization': 'Basic $base64'
};
var url = 'https://${data.domain}/rest/api/3/issue/${data.issue}';
Application.log(url);
var response = await http.get(Uri.parse(url), headers: header);
if (response.statusCode < 300 && response.statusCode >= 200) {
Application.log(response.body);
var issue = DataJiraIssue.fromJson(jsonDecode(response.body));
var targetUser = issue.fields?.assignee?.emailAddress;
if (targetUser != null) {
var slackID = '';
for (var user in data.users) {
if (targetUser.contains(user.mail!)) {
slackID = user.slackID!;
break;
}
}
if (slackID.isNotEmpty) {
var arr = issue.fields?.description?.content;
var summary = issue.fields?.summary;
if ((arr != null && arr.isNotEmpty) || summary != null) {
String? content = "";
if (arr != null && arr.isNotEmpty) {
content = findContent(arr.first);
content ??= summary!;
} else {
content = summary!;
}
var browseUrl = 'https://${data.domain}/browse/${data.issue}';
DataSlack Function(String) getData = (channel) {
var slackData = DataSlack(channel);
slackData.attachments = [];
var attachment = SlackAttachment();
slackData.blocks = [];
var blockSection = SlackBlockSection();
blockSection.text = SlackBlockText();
blockSection.text!.text = ':jira: 새 Jira 업무가 할당되었습니다.';
slackData.blocks!.add(blockSection);
attachment.blocks = [];
blockSection = SlackBlockSection();
blockSection.text = SlackBlockText();
blockSection.text!.type = SlackTextType.mrkdwn;
blockSection.text!.text =
'*<$browseUrl|${data.issue}> ${issue.fields?.summary}*';
attachment.blocks!.add(blockSection);
blockSection = SlackBlockSection();
blockSection.text = SlackBlockText();
blockSection.text!.text = content;
blockSection.accessory = SlackAccessory();
blockSection.accessory!.type = SlackActionType.button;
blockSection.accessory!.url = browseUrl;
blockSection.accessory!.text = SlackBlockText();
blockSection.accessory!.text!.type = SlackTextType.plain_text;
blockSection.accessory!.text!.text = 'Open';
attachment.blocks!.add(blockSection);
slackData.attachments!.add(attachment);
return slackData;
};
// setReturn(data.return_slack_data, getData(slackID));
// if(data.sub_channel != null) {
// setReturn(data.return_slack_channel, getData(data.sub_channel!));
// }
}
} else {
Application.log('User not found');
}
}
} else {
throw Exception('Request Jira API Faile: $url');
}
return simpleFuture;
}
}

116
refactoring/to-do.md Normal file
View file

@ -0,0 +1,116 @@
# 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 패턴
**우선순위: 최고 / 난이도: 높음**
2번 작업(util/ 정리)과 연계. `commands/` 구조 개편의 마무리 단계.
현재 새 커맨드 추가 시 두 곳을 수정해야 한다:
1. 도메인 데이터 파일 — 데이터 클래스 추가
2. `commands/command.dart` — 130+ case factory switch에 추가
각 Command 클래스가 자신을 등록하는 패턴으로 변경하면
새 커맨드 추가 시 해당 파일 하나만 수정하면 된다.
작업 내용:
- `Command` 클래스에 `registerFactory()` static 메서드 추가
- 각 커맨드 파일에 `static void register()` 메서드 추가
- `application.dart` 또는 별도 registry 파일에서 모든 커맨드 등록
- 기존 switch factory 제거
> ⚠️ 변경 범위가 크므로 2번 완료 후 진행 권장
---
### 4. 태그 시스템 중앙화
**우선순위: 중간 / 난이도: 낮음**
현재 태그 처리 로직(`<!property.key>` 등)이 `commands/command.dart` 내부에
흩어져 있다. 별도 파일로 분리하면 AI가 "어떤 태그를 쓸 수 있나?" 질문에
코드 한 곳만 읽어도 답할 수 있다.
작업 내용:
- `lib/oto/core/tag_system.dart` 파일 생성
- 태그 파싱/치환 로직을 `command.dart`에서 이동
- 태그 종류 명세를 주석으로 정리 (`property`, `state`, `common` 등)
- `command.dart``tag_system.dart`를 import해서 사용
---
### 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 `??` 패턴 등)