374 lines
9.1 KiB
Markdown
374 lines
9.1 KiB
Markdown
# OTO CLI
|
|
|
|
YAML로 정의된 CI/CD 파이프라인을 실행하는 Dart 기반 자동화 도구.
|
|
Jenkins, FTP, Git, Slack/Mattermost, iOS/Flutter/Android 빌드 등 다양한 작업을 커맨드로 추상화하고, 파이프라인 엔진이 순차 실행한다.
|
|
|
|
---
|
|
|
|
## 설치
|
|
|
|
```bash
|
|
dart pub get
|
|
dart compile exe bin/main.dart -o oto
|
|
```
|
|
|
|
---
|
|
|
|
## 실행 모드
|
|
|
|
| 플래그 | 모드 | 설명 |
|
|
|--------|------|------|
|
|
| `-j` | `jenkins` | Jenkins 환경 변수에서 워크스페이스·YAML 읽기 |
|
|
| `-t` | `test` | 하드코딩된 테스트 데이터로 로컬 실행 |
|
|
| `-f <path>` | `file` | 지정한 YAML 파일 경로에서 파이프라인 실행 |
|
|
| (daemon) | `scheduler` | 스케줄러 데몬 모드 |
|
|
|
|
```bash
|
|
oto -j # Jenkins CI 모드
|
|
oto -t # 로컬 테스트 모드
|
|
oto -f ./build.yaml # 파일 기반 실행
|
|
```
|
|
|
|
---
|
|
|
|
## 실행 흐름
|
|
|
|
```
|
|
bin/main.dart
|
|
└── Application (singleton)
|
|
└── DataComposer ← YAML + Jenkins env 파싱
|
|
└── Pipeline
|
|
└── Command.byType(CommandType) → execute(DataCommand)
|
|
```
|
|
|
|
---
|
|
|
|
## YAML 파이프라인 구조
|
|
|
|
```yaml
|
|
property:
|
|
workspace: /path/to/project
|
|
version: 1.0.0
|
|
env: prod
|
|
|
|
commands:
|
|
- command: BuildiOS
|
|
id: buildApp
|
|
param:
|
|
workspacePath: <!property.workspace>/ios/Runner.xcworkspace
|
|
scheme: Runner
|
|
|
|
pipeline:
|
|
workflow:
|
|
- exe: buildApp
|
|
```
|
|
|
|
### 태그 시스템
|
|
|
|
| 태그 | 방향 | 설명 |
|
|
|------|------|------|
|
|
| `<!property.key>` | 읽기 | property에서 값 치환 |
|
|
| `<@property.key>` | 쓰기 | 커맨드 결과를 property에 저장 |
|
|
|
|
- 태그가 문자열 전체이면 원본 타입(List, Map 등) 유지
|
|
- 태그가 문자열 일부이면 `toString()` 변환
|
|
- 중첩 접근 지원: `<!property.build.version>`
|
|
|
|
---
|
|
|
|
## 파이프라인 흐름 제어
|
|
|
|
### exe — 단순 실행
|
|
|
|
```yaml
|
|
- exe: buildApp
|
|
```
|
|
|
|
### exe-handle — 성공/실패 분기
|
|
|
|
```yaml
|
|
- exe-handle:
|
|
id: uploadArtifact
|
|
on-success:
|
|
- exe: notifySuccess
|
|
on-fail:
|
|
- exe: notifyError
|
|
```
|
|
|
|
### if — 조건 분기
|
|
|
|
```yaml
|
|
- if:
|
|
condition-string: "<!property.env> == prod"
|
|
on-true:
|
|
- exe: deployProd
|
|
on-false:
|
|
- exe: deployDev
|
|
```
|
|
|
|
**지원 연산자:** `==`, `!=`, `<`, `<=`, `>`, `>=`
|
|
|
|
**지원 타입:** `string`, `int`, `float`, `bool`, `date`, `version`, `object`
|
|
|
|
### foreach — 반복
|
|
|
|
```yaml
|
|
- foreach:
|
|
iterator: <!property.platforms> # List 또는 Map
|
|
setValue: <@property.target>
|
|
on-do:
|
|
- exe: buildTarget
|
|
```
|
|
|
|
### while — 조건 루프
|
|
|
|
```yaml
|
|
- while:
|
|
condition-int: <!property.retry> < 3
|
|
on-do:
|
|
- exe: checkStatus
|
|
```
|
|
|
|
### switch — 멀티 케이스
|
|
|
|
```yaml
|
|
- switch:
|
|
condition-string: <!property.env>
|
|
cases:
|
|
- value: prod
|
|
tasks:
|
|
- exe: deployProd
|
|
- value: dev
|
|
tasks:
|
|
- exe: deployDev
|
|
```
|
|
|
|
### wait-until — 대기
|
|
|
|
```yaml
|
|
- async: buildStep
|
|
- wait-until-string: <!state.buildStep> != complete
|
|
```
|
|
|
|
### async — 비동기 실행
|
|
|
|
```yaml
|
|
- async: notifyStart # 완료를 기다리지 않고 다음 단계 진행
|
|
```
|
|
|
|
---
|
|
|
|
## 커맨드 목록
|
|
|
|
### 빌드
|
|
|
|
| 커맨드 | 설명 |
|
|
|--------|------|
|
|
| `BuildiOS` | xcodebuild로 iOS 앱 빌드 |
|
|
| `ArchiveiOS` | xcodebuild archive |
|
|
| `ExportiOS` | xcodebuild -exportArchive |
|
|
| `PublishiOS` | App Store Connect 배포 |
|
|
| `TestflightUpload` | TestFlight 업로드 |
|
|
| `TestflightStatusCheck` | TestFlight 처리 상태 확인 |
|
|
| `TestflightDistribute` | TestFlight 그룹 배포 |
|
|
| `BuildFlutter` | flutter build |
|
|
| `BuildDart` | dart build |
|
|
| `BuildDartCompile` | dart compile |
|
|
| `BuildDotNet` | dotnet build |
|
|
| `BuildMSBuild` | MSBuild (.NET) |
|
|
| `XcodeprojAddFile` | Xcode 프로젝트 파일 추가 |
|
|
| `CodeSign` | macOS codesign |
|
|
| `CodeSignVerify` | codesign 검증 |
|
|
| `ProductBuild` | macOS productbuild |
|
|
| `Notarize` | macOS 공증 |
|
|
|
|
### 파일
|
|
|
|
| 커맨드 | 설명 |
|
|
|--------|------|
|
|
| `Copy` | 파일/디렉토리 복사 (이동 포함) |
|
|
| `Delete` | 파일/디렉토리 삭제 |
|
|
| `Rename` | 이름 변경 |
|
|
| `FileRead` | 파일 읽기 → property 저장 |
|
|
| `FileWrite` | property 값 → 파일 쓰기 |
|
|
| `Files` | 파일 목록 조회 |
|
|
| `FileInfo` | 파일 메타데이터 조회 |
|
|
| `DirectoryCreate` | 디렉토리 생성 |
|
|
| `Zip` | 압축/해제 |
|
|
|
|
### Git
|
|
|
|
| 커맨드 | 설명 |
|
|
|--------|------|
|
|
| `Git` | 임의 git 명령 실행 |
|
|
| `GitCommit` | 변경사항 커밋 |
|
|
| `GitPull` | pull |
|
|
| `GitPush` | push |
|
|
| `GitCheckout` | 브랜치/파일 체크아웃 |
|
|
| `GitRev` | 리비전 해시 조회 |
|
|
| `GitCount` | 커밋 수 조회 |
|
|
| `GitReset` | reset |
|
|
| `GitStashPush` / `GitStashApply` | stash 관리 |
|
|
| `GitBranch` | 브랜치 목록/생성/삭제 |
|
|
| `GitHub` | GitHub API 호출 |
|
|
| `GitHubPullRequestCreate` / `List` / `Close` | PR 관리 |
|
|
|
|
### 알림
|
|
|
|
| 커맨드 | 설명 |
|
|
|--------|------|
|
|
| `Slack` | Slack 메시지 전송 |
|
|
| `SlackFile` | Slack 파일 업로드 |
|
|
| `SlackBuild` | 빌드 결과 Slack 리포트 |
|
|
| `Mattermost` | Mattermost 메시지 전송 |
|
|
| `MattermostBuild` | 빌드 결과 Mattermost 리포트 |
|
|
|
|
### 네트워크 / FTP
|
|
|
|
| 커맨드 | 설명 |
|
|
|--------|------|
|
|
| `Upload` | FTP 업로드 |
|
|
| `Download` | FTP 다운로드 |
|
|
| `WebRequest` | HTTP 요청 |
|
|
| `WebFile` | HTTP multipart 파일 업로드 |
|
|
| `URLInfo` | URL 정보 파싱 |
|
|
|
|
### 문자열 / 유틸
|
|
|
|
| 커맨드 | 설명 |
|
|
|--------|------|
|
|
| `Print` | 메시지 출력 |
|
|
| `SetValue` | property 값 직접 설정 |
|
|
| `Delay` | 지연 (초) |
|
|
| `StringSub` | 문자열 자르기 |
|
|
| `StringReplace` | 문자열 치환 |
|
|
| `StringReplacePattern` | 정규식 치환 |
|
|
| `StringIndex` | 인덱스 검색 |
|
|
| `JsonReader` | JSON 값 읽기 |
|
|
| `JsonReaderFile` | JSON 파일 읽기 |
|
|
| `JsonWriterFile` | JSON 파일 쓰기 |
|
|
|
|
### 셸 / 프로세스
|
|
|
|
| 커맨드 | 설명 |
|
|
|--------|------|
|
|
| `Shell` | 셸 명령 실행 |
|
|
| `ShellFile` | 셸 스크립트 파일 실행 |
|
|
| `ProcessRun` | 프로세스 실행 |
|
|
| `ProcessKill` | 프로세스 종료 |
|
|
| `ProcessPortUse` | 포트 사용 여부 확인 |
|
|
|
|
### 외부 서비스
|
|
|
|
| 커맨드 | 설명 |
|
|
|--------|------|
|
|
| `Jira` | Jira 이슈 조회/수정 |
|
|
| `AwsCli` | AWS CLI 명령 실행 |
|
|
| `Docker` | Docker 명령 실행 |
|
|
| `Gradle` | Gradle 빌드 실행 |
|
|
| `Protobuf` | protoc 컴파일 |
|
|
| `JenkinsParameterModify` | Jenkins 파라미터 수정 |
|
|
| `SMBAuth` | SMB 인증 |
|
|
|
|
---
|
|
|
|
## 데이터 모델 구조
|
|
|
|
모든 커맨드 파라미터는 `DataParam`을 상속한다.
|
|
|
|
```dart
|
|
DataParam
|
|
├── workspace?: String // 기본 작업 디렉토리
|
|
├── passExitCodes?: List<int> // 성공으로 간주할 exit code
|
|
├── setResult?: String // 결과 저장 태그
|
|
├── setExitCode?: String // exit code 저장 태그
|
|
└── showPrint?: bool
|
|
|
|
// 예시
|
|
DataBuildiOS extends DataParam {
|
|
workspacePath: String
|
|
scheme: String
|
|
destination: String
|
|
...
|
|
}
|
|
```
|
|
|
|
**워크스페이스 해석 우선순위:**
|
|
1. `param.workspace`
|
|
2. `property.workspace`
|
|
3. `DataCommon.workspace` (Jenkins env)
|
|
|
|
---
|
|
|
|
## 새 커맨드 추가
|
|
|
|
```
|
|
1. lib/oto/data/*.dart DataParam 상속 모델 정의 + @JsonSerializable
|
|
2. lib/oto/commands/command.dart CommandType enum에 값 추가
|
|
3. lib/oto/commands/{category}/ Command 상속 클래스 구현
|
|
4. Command.register(..., spec: CommandSpec(...))에 구현체/데이터 모델/샘플 경로 등록
|
|
5. lib/oto/commands/command_registry.dart registerAllCommands()에 등록 함수 연결
|
|
6. dart run build_runner build *.g.dart 생성
|
|
```
|
|
|
|
---
|
|
|
|
## 스케줄러
|
|
|
|
```bash
|
|
oto scheduler -r build.yaml # 스케줄러 등록
|
|
oto scheduler -l # 목록 조회
|
|
oto scheduler -e <alias> true # 활성화/비활성화
|
|
oto scheduler -u <alias> # 해제
|
|
```
|
|
|
|
**YAML 스케줄러 설정:**
|
|
|
|
```yaml
|
|
scheduler:
|
|
alias: nightly-build
|
|
cron: "0 2 * * *" # cron 표현식 또는
|
|
# interval: 3600 # 초 단위 interval
|
|
enableLog: true
|
|
```
|
|
|
|
**플랫폼별 실행 메커니즘:**
|
|
- **Linux:** cron
|
|
- **macOS:** LaunchAgent
|
|
- **Windows:** Task Scheduler
|
|
|
|
---
|
|
|
|
## 프로젝트 구조
|
|
|
|
```
|
|
lib/
|
|
├── cli/ # CLI 인자 파싱, 스케줄러 관리
|
|
└── oto/
|
|
├── application.dart # 싱글턴 오케스트레이터
|
|
├── commands/ # 커맨드 구현체 (카테고리별)
|
|
├── core/ # 태그 시스템, 데이터 합성, 정의 데이터
|
|
├── data/ # JSON 직렬화 데이터 모델 (*.g.dart 포함)
|
|
├── pipeline/ # 파이프라인 흐름 제어 엔진
|
|
└── utils/ # 공통 유틸리티
|
|
```
|
|
|
|
---
|
|
|
|
## 기술 스택
|
|
|
|
- **언어:** Dart (SDK >=2.18.6 <4.0.0)
|
|
- **의존성:** dart_framework (커스텀), http, json_annotation, cron, xml
|
|
- **코드 생성:** json_serializable + build_runner
|
|
|
|
```bash
|
|
dart run build_runner build # 데이터 모델 생성
|
|
```
|
|
|
|
---
|
|
|
|
## 에러 처리
|
|
|
|
- `Application.build()`는 `catch (e, stacktrace)`로 Exception과 Error 모두 처리
|
|
- 실패 시 `BuildResult.failure(..., exitCode: 10)`을 반환하고 CLI가 부모 프로세스에 exit code 전달
|
|
- `passExitCodes`로 성공으로 간주할 exit code 지정 가능
|