Skip to Content
문서YAML 스펙

워크플로우

이 문서는 워크플로우 YAML의 모든 필드를 다뤄요.
입문자라면 워크플로우 생성부터 보시는 게 좋아요.

파일 규칙

  • 위치: Agent의 워크플로우 디렉토리 (기본 ./workflows/, DEPLITE_WORKFLOWS_DIR로 오버라이드 가능)
  • 확장자: .yaml 또는 .yml
  • 파일이 추가·수정·삭제되면 Agent가 파일시스템 이벤트로 즉시 감지해서 반영해요

최상위 필드

필드타입필수기본값설명
namestring✓—워크플로우 식별자. 영문 대소문자·숫자·_·.·-만 허용, 1~128자
stepsobject[]✓—최소 1개
timeout-minutesint전체 워크플로우 timeout (지정 시 step 전체에 적용)
envobject{}모든 step에 적용되는 환경변수
secrets(string | object)[][]Agent 프로세스의 DEPLITE_SECRET_<KEY> 환경변수에서 가져와 마스킹할 시크릿 목록 (시크릿 참고)
outputsobject[][]step이 캡처한 값을 워크플로우 결과로 노출 (출력 참고)

Step 필드

필드타입필수기본값설명
namestring✓—워크플로우 안에서 고유해야 해요
runstring✓—실행할 셸 명령 (multi-line 가능)
idstring—step 식별자. 형식 ^[a-zA-Z][a-zA-Z0-9_-]{0,63}$, 워크플로우 안에서 유일해야 해요. 출력값 캡처와 시크릿 scope 지정에 쓰여요
verboseboolfalsedebug 실행 대상 step 표시 (아래 참고)
timeout-minutesint(워크플로우 값 상속)step별 timeout
working-directorystring(Agent 작업 폴더)cd 후 실행
envobject{}step별 환경변수
shellstring(Agent 기본 셸)이 step만 다른 셸로 실행할 때 사용
continue-on-errorboolfalse실패해도 다음 step 진행

시크릿 값은 stdout/stderr에 노출되어도 ***로 자동 마스킹돼요.

verbose: true 동작

step에 verbose: true를 선언하면, 트리거를 debug: true로 호출했을 때 해당 step의 원본 stdout/stderr가 그대로 서버에 전송돼요.
워크플로우 안의 어느 step도 verbose: true로 선언되지 않으면 debug: true 호출 자체가 거절돼요.

환경변수 우선순위

낮은 우선순위부터 높은 우선순위 순서로 적용돼요.

  1. Agent 프로세스 환경변수 (호스트)
  2. DEPLITE_WORKDIR (Agent가 자동 주입하는 step 작업 디렉토리), TMPDIR·TMP
  3. 워크플로우 env
  4. DEPLITE_PARAM_<KEY> (트리거의 params에서 자동 주입)
  5. DEPLITE_SECRET_<KEY> (워크플로우 secrets 목록의 시크릿)
  6. Step env

같은 키가 여러 곳에 있으면 아래쪽이 이겨요.

step이 실행되는 머신(호스트 또는 컨테이너)과 PATH 처리 방식은 스텝 실행 환경에서 다뤄요.

트리거 주입 변수

환경변수의미
DEPLITE_PARAM_<KEY>트리거 호출 시 params.<key> 값 (대문자로 변환)
DEPLITE_SECRET_<KEY>워크플로우 secrets에 선언된 키. Agent 환경변수 DEPLITE_SECRET_<KEY>에서 가져옴
DEPLITE_WORKDIRstep별로 새로 생성되는 임시 작업 디렉토리
TMPDIR, TMPDEPLITE_WORKDIR과 같은 값
DEPLITE_OUTPUTstep에 id가 있을 때만 주입. 출력 캡처용 파일 경로 (출력 참고)
DEPLITE_OUTPUT_DIR워크플로우 전역 출력 디렉토리. 여기에 result.json을 쓰면 결과로 실려요

트리거의 ref·debug·jobId는 환경변수로 자동 주입되지 않아요.
워크플로우에서 받고 싶다면 트리거의 params에 명시적으로 포함해서 호출하면 DEPLITE_PARAM_REF 같은 형태로 받을 수 있어요.

시크릿

secrets: 배열의 항목은 이름만 적는 문자열과 객체를 섞어 쓸 수 있어요.

secrets: - DATABASE_URL # 문자열 형태는 required: true가 적용돼요 - name: DEPLOY_KEY required: true scope: [deploy] # id가 deploy인 step에만 주입돼요 - name: SLACK_WEBHOOK required: false
필드타입필수설명
namestring✓UPPER_SNAKE_CASE. 형식 ^[A-Z][A-Z0-9_]{0,63}$
requiredbool값이 바인딩되지 않았을 때 job을 실패시킬지
descriptionstring설명
scopestring[]이 시크릿을 볼 수 있는 step id 목록

여기 나열된 이름은 stdout에서 자동 마스킹되고, 어떤 경우에도 서버로 전송되지 않아요.
시크릿 값은 Agent 머신의 환경변수에 DEPLITE_SECRET_DATABASE_URL·DEPLITE_SECRET_API_KEY 형태로 주입해주세요.

scope로 주입 범위 좁히기

  • scope를 생략하거나 빈 배열로 두면 모든 step에 주입돼요 (기본 동작)
  • 지정하면 목록에 있는 id를 가진 step에만 주입돼요
  • id가 없는 step은 scope가 지정된 시크릿을 항상 받지 못해요
  • 주입 형태는 동일하게 DEPLITE_SECRET_<이름> 환경변수예요
  • required 검사는 scope와 무관하게 워크플로우 단위로 먼저 이뤄져요. 값만 바인딩되어 있으면 scope로 가려진 step이 있어도 통과해요
  • 마스킹도 scope와 무관하게 바인딩된 모든 시크릿 값에 적용돼요
  • scope는 서버로 전송되지 않는 Agent 로컬 정책이에요

scope에 적은 step id가 실제로 존재하는지는 검증하지 않아요.
이름을 잘못 적으면 에러 없이 어떤 step에도 주입되지 않고, step은 빈 환경변수로 실행돼요.

출력 (output)

Job의 output을 만드는 방법은 두 가지고, 둘 다 stdout이 아니라 파일이에요.

방법주입 변수step id쓰임새
선언형 캡처DEPLITE_OUTPUT (step별 파일)필요key=value로 값을 캡처하고 outputs:에서 타입을 지정해 노출
자유형 JSONDEPLITE_OUTPUT_DIR (워크플로우 전역 디렉토리)불필요result.json에 임의 구조의 JSON. 파일을 결과로 첨부할 수 있음

stdout에 JSON을 출력해도 output으로 잡히지 않아요.
stdout·stderr는 로그 스트림(raw)으로만 흘러요.
변수 이름이 DEPLITE_OUTPUT(단수, step별 파일)과 DEPLITE_OUTPUT_DIR(워크플로우 전역 디렉토리)로 한 끗 차이니 헷갈리지 않게 확인해주세요.

Job output은 트리거를 동기 응답 모드로 호출했을 때 호출자에게 반환돼요.

1. outputs:와 $DEPLITE_OUTPUT

step에 id를 붙이면 그 step에만 DEPLITE_OUTPUT 환경변수가 주입돼요.
값은 해당 작업 전용 임시 디렉토리(DEPLITE_WORKDIR) 안의 파일 경로예요.
이 파일에 값을 적어두면 Agent가 step 종료 후 읽어서 캡처해요.

steps: - id: build name: 빌드 run: | echo "commit=$(git rev-parse HEAD)" >> "$DEPLITE_OUTPUT" echo "size=42" >> "$DEPLITE_OUTPUT" outputs: - name: commit type: string from: build.commit - name: size type: number from: build.size

파일 포맷

  • 한 줄에 key=value 하나예요
  • key는 ^[a-zA-Z][a-zA-Z0-9_-]{0,63}$ 형식이에요
  • 첫 =를 기준으로 자르기 때문에 값 안에 =가 들어가도 괜찮아요
  • =가 없는 줄과 형식에 맞지 않는 key의 줄은 무시돼요 (실패로 처리되지 않아요)
  • 값은 시크릿 마스킹을 거친 뒤 캡처되고, 파일은 step이 끝나면 읽힌 뒤 삭제돼요

멀티라인 값 문법은 없어요.
GitHub Actions의 구분자(<<EOF) 방식은 지원하지 않으니, 여러 줄 값이 필요하면 DEPLITE_WORKDIR 아래에 파일로 남겨주세요.

outputs로 꺼내기

캡처된 값은 워크플로우 최상위 outputs:를 통해서만 노출돼요.
outputs:를 선언하지 않으면 캡처한 값은 어디에도 실리지 않아요.

필드설명
name워크플로우 결과에 실릴 이름
typestring · number · boolean
from<step-id>.<key> 형식. 형식이 틀리거나 없는 step id를 가리키면 검증 에러

캡처된 값은 후속 step의 환경변수로 들어가지 않아요.
GitHub Actions의 steps.<id>.outputs.<key>처럼 다음 step에서 바로 참조할 수 없고, 워크플로우 outputs:에만 투영돼요.
step 사이에 값을 넘기려면 DEPLITE_WORKDIR 아래에 파일을 직접 쓰고 읽어주세요.

2. $DEPLITE_OUTPUT_DIR/result.json

DEPLITE_OUTPUT_DIR은 워크플로우 전체에 주입되는 디렉토리 경로예요.
step id 없이도 쓸 수 있고, 이 디렉토리에 result.json을 쓰면 Agent가 파싱해서 그대로 결과에 실어요.
파일이 없으면 조용히 넘어가요.

- name: report run: | cat > "$DEPLITE_OUTPUT_DIR/result.json" <<'JSON' {"deployedRef":"main-7f3a9b","url":"https://app.example.com"} JSON

result.json의 최상위는 반드시 JSON 객체여야 해요.
배열이나 스칼라면 결과 병합 자체를 건너뛰어요.

$file 마커로 파일 첨부하기

result.json 트리 안에 {"$file": "<상대 경로>"} 마커를 두면, 해당 파일이 업로드된 뒤 그 자리가 FileRef 객체로 치환돼요.
파일을 결과로 돌려주는 방법은 이것뿐이에요.

- name: build-and-attach run: | ./build.sh > "$DEPLITE_OUTPUT_DIR/app.apk" cat > "$DEPLITE_OUTPUT_DIR/result.json" <<'JSON' {"version":"1.4.0","artifact":{"$file":"app.apk"}} JSON

마커 인식 조건은 다음과 같아요.

  • 객체의 키가 정확히 하나이고 그 키가 $file일 때만 치환돼요. 다른 키가 하나라도 같이 있으면 일반 객체로 그대로 실려요
  • 경로는 DEPLITE_OUTPUT_DIR 기준 상대 경로이고, 대상은 정규 파일이어야 해요 (디렉토리를 가리키면 실패해요)
  • 배열이나 중첩 객체 안의 마커도 재귀적으로 치환돼요
{ "artifact": { "$file": "report.tar.gz" } } // 치환됨 { "artifact": { "$file": "report.tar.gz", "note": "x" } } // 치환 안 됨

치환된 FileRef 객체는 이런 모양이에요.

{ "__type": "FileRef", "id": "9f1c…", "filename": "report.tar.gz", "contentType": "application/octet-stream", "size": 20481 }
필드타입값
__typestring항상 "FileRef"
idstring파일 id. 다운로드에 쓰는 값
filenamestring마커에 적은 경로의 basename만
contentTypestring항상 application/octet-stream
sizenumber바이트

내려받을 때는 id로 GET /storage/files/:id/download-url을 호출해주세요.
출력 FileRef에는 downloadUrl·expiresAt이 들어 있지 않아서, API를 거치는 것이 유일한 경로예요.
업로드된 파일은 persistent로 저장되어 job이 끝나도 자동 삭제되지 않아요.

contentType은 실제 확장자와 무관하게 항상 application/octet-stream이에요.
이 값으로 파일 종류를 분기하지 말아주세요.
App Deploy(OTA)의 deploy 이벤트에 실리는 입력 방향 FileRef는 downloadUrl을 갖지만, 여기서 다루는 출력 방향 FileRef는 URL이 없어요.

업로드에 실패하면

업로드가 실패해도 job은 실패하지 않아요.
경고 로그만 남고 job 상태는 그대로예요.
업로드에는 job당 10분 타임아웃이 걸려 있고, 초과해도 같은 방식으로 경고만 남기고 진행돼요.

  • 마커 하나라도 업로드에 실패하면 result.json 트리 전체가 output에서 빠져요. 성공한 마커만 골라 담는 부분 성공은 없어요
  • 마커가 원래 모양으로 남지도 않아요. 트리 자체가 병합 대상에서 제외돼요
  • outputs:로 캡처한 값은 영향을 받지 않고 그대로 output에 남아요

그래서 호출자 입장에서는 “output은 왔는데 파일 필드만 통째로 없는” 모습이 돼요.
파일이 반드시 필요한 워크플로우라면, 다운스트림에서 해당 필드가 실제로 있는지 확인하도록 만들어두시는 걸 권해드려요.

두 결과의 병합

  • 두 경로의 결과는 하나의 Job output 객체로 병합돼요. 별도 필드로 나뉘지 않아요
  • 키가 겹치면 result.json 쪽이 이겨요 (outputs: 결과 위에 덮어써요)
  • 결과가 비어 있으면 output 필드는 아예 전송되지 않아요

검증 규칙

  • name: 영문 대소문자·숫자·_·.·-만 허용, 1~128자
  • steps: 1개 이상
  • 각 step.name: 비어있지 않고, 같은 워크플로우 안에서 중복되지 않음
  • 각 step.run: 비어있지 않음
  • 각 step.id (선택): ^[a-zA-Z][a-zA-Z0-9_-]{0,63}$ 형식이고, 같은 워크플로우 안에서 중복되지 않음
  • 각 outputs[].from: <step-id>.<key> 형식이고, 실제로 존재하는 step id를 가리킴

규칙을 어기면 Agent 시작 시 또는 워크플로우 파일이 다시 스캔될 때 에러 로그가 남고, 해당 파일은 서버 카탈로그에서 제외돼요.

실전 레시피 모음

1) DB 야간 백업

PostgreSQL 표준 환경변수(PGHOST·PGUSER·PGPASSWORD·PGDATABASE)를 step에 주입하면 psql 계열 도구(psql·pg_dump 등)가 별도 인자 없이 자동 인식해요.

name: backup-pg timeout-minutes: 60 secrets: - PGPASSWORD - AWS_SECRET_ACCESS_KEY env: PGHOST: db.internal PGPORT: "5432" PGUSER: backup PGDATABASE: prod steps: - name: connectivity-check run: psql -c "SELECT 1" > /dev/null env: PGPASSWORD: $DEPLITE_SECRET_PGPASSWORD timeout-minutes: 1 - name: dump run: pg_dump --no-owner --clean | gzip > /tmp/backup.sql.gz env: PGPASSWORD: $DEPLITE_SECRET_PGPASSWORD timeout-minutes: 30 - name: upload run: | aws s3 cp /tmp/backup.sql.gz \ s3://backups/pg/$(date +%F).sql.gz timeout-minutes: 15 - name: cleanup run: rm -f /tmp/backup.sql.gz continue-on-error: true

2) Docker 이미지 빌드·푸시·배포

name: deploy-myapp timeout-minutes: 25 env: IMAGE: registry.example.com/myapp secrets: - REGISTRY_PASSWORD steps: - name: build run: docker build -t "$IMAGE:$DEPLITE_PARAM_REF" . timeout-minutes: 12 - name: push run: | echo "$DEPLITE_SECRET_REGISTRY_PASSWORD" | docker login -u deploy --password-stdin registry.example.com docker push "$IMAGE:$DEPLITE_PARAM_REF" timeout-minutes: 8 - name: rollout run: | kubectl set image deployment/myapp myapp="$IMAGE:$DEPLITE_PARAM_REF" kubectl rollout status deployment/myapp --timeout=5m timeout-minutes: 6 - id: report name: report run: | DEP_URL=$(kubectl get ingress myapp -o jsonpath='{.spec.rules[0].host}') echo "deployedRef=$DEPLITE_PARAM_REF" >> "$DEPLITE_OUTPUT" echo "url=https://$DEP_URL" >> "$DEPLITE_OUTPUT" outputs: - name: deployedRef type: string from: report.deployedRef - name: url type: string from: report.url

트리거 호출 시 params: { "ref": "main-7f3a9b" }로 넘기면 워크플로우에서 $DEPLITE_PARAM_REF로 받을 수 있어요.

3) 외부 API → 데이터 웨어하우스 ETL

name: etl-orders timeout-minutes: 45 secrets: - SHOPIFY_TOKEN - SNOWFLAKE_PWD env: WINDOW_HOURS: "24" steps: - name: extract run: | curl -H "X-Shopify-Access-Token: $DEPLITE_SECRET_SHOPIFY_TOKEN" \ "https://store.myshopify.com/admin/api/2024-01/orders.json?updated_at_min=$(date -d '24 hours ago' -Iseconds)" \ > /tmp/orders.json timeout-minutes: 15 - name: transform run: python3 /opt/etl/transform.py /tmp/orders.json /tmp/orders.parquet timeout-minutes: 10 - name: load run: python3 /opt/etl/load_snowflake.py /tmp/orders.parquet env: SNOWFLAKE_USER: deplite SNOWFLAKE_ACCOUNT: ab12345.us-east-1 timeout-minutes: 20

4) 헬스체크 + 자동 복구

name: healthcheck timeout-minutes: 5 steps: - name: probe run: | if ! curl -fsS https://app.example.com/health > /dev/null; then echo "UNHEALTHY" exit 1 fi echo "OK" - name: restart-if-failed run: kubectl rollout restart deployment/myapp continue-on-error: true

서버 측 트리거의 쿨다운·동시 실행 제한은 제한과 정책에서 다뤄요.

5) 디버그 환경 임시 부팅 + verbose

debug 모드로 호출할 step에 verbose: true를 붙여 두면, 트리거를 debug: true로 부를 때 원본 로그가 그대로 전송돼요.

name: spin-up-debug timeout-minutes: 15 steps: - id: launch name: launch verbose: true run: | ID=$(docker run -d --rm -p 0:3000 myapp:debug) HOST_PORT=$(docker port "$ID" 3000 | awk -F: '{print $2}') echo "containerId=$ID" >> "$DEPLITE_OUTPUT" echo "url=http://debug.internal:$HOST_PORT" >> "$DEPLITE_OUTPUT" outputs: - name: containerId type: string from: launch.containerId - name: url type: string from: launch.url

Webhook을 동기 응답 모드로 호출하면 응답의 output.url을 그대로 Slack에 던질 수 있어요.

관련 문서

최종 수정 일자: