oto/apps/runner
toki 04adcaf93a refactor(pipeline): 출력 포트 경계를 적용한다
runner 출력 경계 분리 마일스톤에서 pipeline이 CLI 구현체에 직접 의존하지 않도록 explicit ExecutionContext.output 경계를 사용한다.

현재 iop 미구현 legacy smoke는 사용자 승인으로 완료 범위에서 제외하고 task archive와 완료 로그를 남긴다.
2026-06-07 17:52:25 +09:00
..
assets feat(agent): 부트스트랩 명령 발급을 추가한다 2026-06-05 20:53:41 +09:00
bin refactor(runner): 런타임 패키지를 앱 하위로 이동한다 2026-06-05 06:34:45 +09:00
lib refactor(pipeline): 출력 포트 경계를 적용한다 2026-06-07 17:52:25 +09:00
test refactor(pipeline): 출력 포트 경계를 적용한다 2026-06-07 17:52:25 +09:00
analysis_options.yaml refactor(runner): 런타임 패키지를 앱 하위로 이동한다 2026-06-05 06:34:45 +09:00
pubspec.yaml refactor(runner): 런타임 패키지를 앱 하위로 이동한다 2026-06-05 06:34:45 +09:00
README.md feat(runner): output port 경계 작업을 반영한다 2026-06-07 13:12:46 +09:00

OTO CLI

YAML로 정의된 CI/CD 파이프라인을 실행하는 Dart 기반 자동화 도구. Jenkins, FTP, Git, Slack/Mattermost, iOS/Flutter/Android 빌드 등 다양한 작업을 커맨드로 추상화하고, 파이프라인 엔진이 순차 실행한다.


현재 상태

OTO CLI는 현재 Jenkins 실행, 로컬 YAML 파일 실행, 스케줄러 실행을 지원한다. 이 실행 표면은 이후 구조화 출력, YAML validation, 독립 Control Plane agent 확장을 추가할 때도 깨지면 안 되는 호환 기준선이다. 상세 진행 상황과 다음 작업 후보는 agent-roadmap/current.mdagent-roadmap/ROADMAP.md에서 확인한다.


설치

dart pub get
dart compile exe bin/main.dart -o oto

실행 모드

플래그 모드 설명
-j jenkins Jenkins 환경 변수에서 워크스페이스·YAML 읽기
-t test 하드코딩된 테스트 데이터로 로컬 실행
-f <path> file 지정한 YAML 파일 경로에서 파이프라인 실행
(daemon) scheduler 스케줄러 데몬 모드
oto -j                   # Jenkins CI 모드
oto -t                   # 로컬 테스트 모드
oto -f ./build.yaml      # 파일 기반 실행

CLI 호환 기준선

표면 호환 기준
oto -j Jenkins 환경 변수와 Jenkins용 YAML 탐색을 사용하는 기존 CI 실행 경로로 유지한다. Jenkins 지원을 제거하지 않는다.
oto -f <path> 지정한 YAML 파일을 Jenkins 없이 실행하는 로컬/외부 자동화 진입점으로 유지한다.
oto scheduler ... YAML을 등록하고 백그라운드 스케줄러가 실행하는 daemon 경로를 유지한다. -r, -l, -e, -u 명령 형태를 호환 표면으로 본다.
YAML 파이프라인 property, commands, pipeline.workflow 구조를 핵심 입력 계약으로 유지한다.
커맨드 확장 DataParam 모델, CommandType, Command.register(...), Command.specs/Command.catalogRows, 샘플 YAML을 커맨드 확장 경계로 유지한다.
단일 바이너리 dart compile exe bin/main.dart -o oto로 만드는 단일 실행 파일을 설치와 배포의 기본 산출물로 유지한다.

Jenkins 의존은 jenkins 실행 모드의 호환 경로로 남긴다. 새 자동화 표면은 filescheduler 모드가 Jenkins 환경 변수 없이 동작할 수 있다는 전제를 깨지 않아야 한다.

Jenkins 호환 경계

  • oto -j만 Jenkins 환경 변수와 Jenkins BuildData 입력을 읽는 compatibility adapter다.
  • oto -f <path>는 YAML 파일만 입력으로 삼으며 Jenkins env 없이 local workspace 기본값으로 실행된다.
  • oto scheduler ...는 등록된 YAML과 scheduler 설정으로 실행되며 Jenkins env를 요구하지 않는다.
  • JenkinsParameterModify는 Jenkins job XML을 다루는 command일 뿐 -j 실행 모드를 요구한다는 뜻이 아니다.

실행 흐름

bin/main.dart
  └── Application (singleton)
        └── DataComposer      ← 실행 모드별 YAML/env 파싱
              └── Pipeline
                    └── Command.byType(CommandType) → execute(DataCommand)

YAML 파이프라인 구조

property:
  workspace: /path/to/project
  version: 1.0.0
  env: prod

commands:
  - command: BuildiOS
    id: buildApp
    param:
      xcworkspaceFilePath: <!property.workspace>/ios/Runner.xcworkspace
      scheme: Runner

pipeline:
  id: main
  workflow:
    - exe: buildApp

태그 시스템

태그 방향 설명
<!property.key> 읽기 property에서 값 치환
<@property.key> 쓰기 커맨드 결과를 property에 저장
  • 태그가 문자열 전체이면 원본 타입(List, Map 등) 유지
  • 태그가 문자열 일부이면 toString() 변환
  • 중첩 접근 지원: <!property.build.version>

파이프라인 흐름 제어

exe — 단순 실행

- exe: buildApp

exe-handle — 성공/실패 분기

- exe-handle:
    id: uploadArtifact
    on-success:
      - exe: notifySuccess
    on-fail:
      - exe: notifyError

if — 조건 분기

- if:
    condition-string: "<!property.env> == prod"
    on-true:
      - exe: deployProd
    on-false:
      - exe: deployDev

지원 연산자: ==, !=, <, <=, >, >=

지원 타입: string, int, float, bool, date, version, object

foreach — 반복

- foreach:
    iterator: <!property.platforms>   # List 또는 Map
    setValue: <@property.target>
    on-do:
      - exe: buildTarget

while — 조건 루프

- while:
    condition-int: <!property.retry> < 3
    on-do:
      - exe: checkStatus

switch — 멀티 케이스

- switch:
    condition-string: <!property.env>
    cases:
    - value: prod
      tasks:
      - exe: deployProd
    - value: dev
      tasks:
      - exe: deployDev

wait-until — 대기

- async: buildStep
- wait-until-string: <!state.buildStep> != complete

async — 비동기 실행

- async: notifyStart    # 완료를 기다리지 않고 다음 단계 진행

커맨드 카탈로그

등록된 커맨드는 Command.specsCommand.catalogRows가 단일 진실 소스다. 샘플은 assets/yaml/sample/**에서 확인한다.

CLI 조회 경로

CLI 명령을 통해 등록된 커맨드 정보와 카탈로그를 직접 조회할 수 있다.

# 사람용 테이블 형식 출력
oto catalog
oto catalog --category build
oto catalog --command Print

# 자동화 및 AI 연동용 구조화된 JSON 출력 (stdout 실행 로그가 억제됨)
oto catalog --json
oto catalog --command BuildiOS --json

데이터 모델 구조

모든 커맨드 파라미터는 DataParam을 상속한다.

DataParam
├── workspace?: String          // 기본 작업 디렉토리
├── passExitCodes?: List<int>   // 성공으로 간주할 exit code
├── setResult?: String          // 결과 저장 태그
├── setExitCode?: String        // exit code 저장 태그
└── showPrint?: bool

// 예시
DataBuildiOS extends DataParam {
  xcodeProjectFilePath?: String
  xcworkspaceFilePath?: String
  scheme: String
  configuration?: 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(type: ..., category: ..., dataModel: ..., samplePath: ...))에 구현체 등록
5. lib/oto/commands/command_registry.dart  registerAllCommands()에 등록 함수 연결
6. dart run build_runner build   *.g.dart 생성

등록된 커맨드 목록: Command.catalogRows (category별 정렬)

스케줄러

oto scheduler -r build.yaml       # 스케줄러 등록
oto scheduler -l                  # 목록 조회
oto scheduler -e <alias> true     # 활성화/비활성화
oto scheduler -u <alias>          # 해제

YAML 스케줄러 설정:

scheduler:
  alias: nightly-build
  cron: "0 2 * * *"        # cron 표현식 또는
  # interval: 3600000      # 밀리초 단위 interval
  enableLog: true

플랫폼별 실행 메커니즘:

  • Linux: cron
  • macOS: LaunchAgent
  • Windows: Task Scheduler

로드맵

상세 로드맵은 agent-roadmap/ROADMAP.md에 둔다. README에는 장기 방향과 작업 진입 문서 경로만 유지한다.

OTO의 장기 방향은 Jenkins 안에서 실행되는 CLI에 머물지 않고, OTO Control Plane에 등록되는 가벼운 build/deploy agent까지 확장하는 것이다.

  • 현재 활성 Milestone 후보는 agent-roadmap/current.md에서 확인한다.
  • CLI 자동화 표면 정리: 기존 CLI 실행 모드와 YAML 파이프라인, 커맨드 확장, 단일 바이너리 배포 구조를 장기 호환 표면으로 정리한다.
  • 독립 Control Plane 기반 oto-agent: Jenkins node 연결식 UX를 OTO Server bootstrap 기반 oto-agent 설치와 등록 계약으로 재해석한다.
  • 메시지 기반 빌드 에이전트: OTO Server가 OTO 파이프라인을 원격 제어할 수 있게 한다.

Agent bootstrap 환경 기준

  • OTO_RUNNER_RELEASE_BASE_URL은 runner binary asset을 내려받는 release source of truth다.
  • Core가 HTTP로 실행될 때는 release base URL을 자동 추론하지 않으며, 환경 변수로 주입한 값도 https:// scheme이어야 한다.
  • bootstrap command 예시는 public validation 기준만 문서화하고, enrollment token, private release host, credential 원문은 tracked 문서에 남기지 않는다.
  • bootstrap script의 --edge-url 인자는 기존 호출 호환 alias로만 보며, 새 문서와 smoke 기준은 --server-url을 사용한다.

프로젝트 구조

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
dart run build_runner build      # 데이터 모델 생성
dart test                        # 테스트 실행

에러 처리

  • Application.build()catch (e, stacktrace)로 Exception과 Error 모두 처리
  • 실패 시 BuildResult.failure(..., exitCode: 10)을 반환하고 CLI가 부모 프로세스에 exit code 전달
  • passExitCodes로 성공으로 간주할 exit code 지정 가능