v0.2.0
DVA v0.2.0
v0.2.0은 v0.1.48 이후 130여 개 커밋을 담은 첫 minor입니다. 이 저장소는 0.1.44부터
0.1.48까지 patch만 올려 왔지만, 이번에는 어제까지 통과하던 config를 거부하거나 같은
명령을 다르게 실행하는 변경이 10건이라 번호가 그 신호를 싣습니다. 0.x에서 SemVer는
patch로 breaking을 내는 것도 허용하지만, 그러면 0.1.47→0.1.48과 0.1.48→0.1.49가 똑같은
크기의 사건으로 보이면서 후자만 config를 거부합니다.
이 노트가 왜 CHANGELOG보다 긴가. 태그를 자르기 직전에
v0.1.48..HEAD를
커밋 단위로 다시 훑었더니, CHANGELOG에 기록을 남긴 커밋은 4개뿐이었습니다. 나머지에서
사용자에게 보이는 변경 24건이 나왔고, 아래 breaking 10건 중 7건이 그 과정에서 처음
드러났습니다. 8건만 보고 게시했다면 이 노트가 조용히 틀린 채로 굳었을 것입니다.
Breaking — 넘어가기 전에 확인하세요
한 문장 요약: Validate()만 보면 안 됩니다. 10건 중 4건(아래 §2의 셋과 §4)은
validate를 한 번도 돌리지 않는 사용자에게도 발생하고, 그중 하나는 데이터를 지웁니다.
1. config가 거부됩니다 (dva config validate exit 1, 플래그 불필요)
ci,secret,job이 예약어가 됐습니다. 이 세 이름을 interaction 키
(ci:build같은 namespace 접두사 포함)나 subproject 이름으로 쓰고 있었다면 지금 바로
깨집니다.up·manifest와 달리 실제로 쓰고 있었을 법한 흔한 단어이므로, 이 릴리스에서
가장 부딪히기 쉬운 항목입니다. 같은 이유로kubectl도 예약어입니다.- 내장 커맨드와 같은 이름의 subproject를 거부합니다 (TASK-263 §3).
subprojects.up은
dva up:web이 자식의web으로 라우팅되게 만드는데, 같은 철자를 interaction 키로도 쓰면
한 철자가 두 의미를 갖습니다. 더불어 자식 자신의 validator가 거부하는 키는 부모의 세
주소 형태(--project,p:key,p/keyimport) 전부에서 거부됩니다 — 이전에는 부모를
통해서만 실행되어 "어느 validator가 권위인가"가 서 있는 위치에 따라 달라졌습니다. root라는 이름의 subproject를 거부합니다 (TASK-333).root는 아래owner필드가
"이 dva.yml이 직접 선언한 항목"을 가리키는 값입니다. 같은 이름의 subproject가 있으면
로컬 항목과 그 subproject에서 import한 항목이 같은 owner를 보고하고, 이 필드로 필터링하는
소비자에게는 구별할 두 번째 신호가 없습니다.root/web라우팅 자체는 그대로입니다.- interaction의
env_file:을 schema가 거부합니다 (TASK-266 Stage B).
interaction.<name>.env_file과subcommands.*.env_file은 additional-property 오류이며,
최상위env_file:과 커맨드environment:로 옮기라는 path-scoped 안내가 붙습니다.
0.1.48이 내던 semantic 경고는 도달 불가가 되어 제거했습니다.
2. 실행이 달라집니다 (validate를 거치지 않아도 발생)
dva down <plan> --purge가 compose 프로젝트 전체를 내립니다 — 파괴적입니다.
서비스를 고르는 plan은 이전에compose rm으로 내려서 named volume과 프로젝트 네트워크에
닿지 못했습니다.--purge는 이제 선택된 서비스와 무관하게
compose down --remove-orphans --volumes --rmi local을 돌립니다 — plan이 고르지 않은
서비스의 named volume, 프로젝트 네트워크, orphan 컨테이너, 로컬 빌드 이미지가 함께
삭제됩니다. 스크립트에dva down <plan> --purge --force를 걸어 두었다면 올리기 전에
확인하세요. 평범한down과--volumes는 plan이 서비스를 고를 때만 그 범위를
유지합니다 — 고르지 않는 plan에서는 이전부터 프로젝트 전역이었고, 그건 바뀌지
않았습니다.interaction.<name>.workdir이 로컬 러너에서도 적용됩니다. 이 필드는 그동안 compose
러너의 컨테이너--workdir으로만 쓰였고 호스트 실행에서는 조용히 무시됐습니다. 없는
디렉토리를 적어 두었다면 이제workdir "sub": directory not found (resolved to …)로
실패합니다. 스크립트가 자기cd를 하고 있었다면 시작 디렉토리가 달라집니다.${VAR:-default}가 POSIX 의미대로 확장됩니다. 이전 확장기는 닫는 중괄호를 선택적으로
취급해서, 변수가 설정돼 있을 때${POSTGRES_USER:-gorisa}가gorisa:-gorisa}로
확장됐습니다. 이전 출력이 깨진 문자열이었으므로 실무 위험은 낮지만, 같은 config가 다른
문자열을 만듭니다.
3. --strict에서만 exit 1이 됩니다 (CI에 --strict를 걸었다면)
- 참조 무결성 경고 6종: plan이 선언하지 않은 서비스, 참조되지 않는 environment/site,
아무것도 바꾸지 않는 entry override, 빈 interaction command, 제거된 CLI를 가리키는 문자열,
고아 health check. 이 규칙을 도입한 커밋 자체가 이 저장소examples/4개에서 실제로 죽은
config를 찾아냈습니다 — 오탐만 만드는 규칙이 아닙니다. - 미등록 compose 파일 경고:
compose-*·docker-compose-*접두사가 autodiscovery에
들어가고include:로 도달하는 디렉토리까지 훑습니다. 등록된 파일 옆의compose-foo.yml이
없던 경고를 만듭니다.
4. exit code가 바뀝니다
dva init --recursive가 "이미 있음"을 진척으로 세지 않습니다: 남아 있던
webui/dva.yml하나가 쓸 수 없는 루트를 성공으로 보고하던 동작을 고쳤습니다. 부분적으로
scaffold된 트리에 이 명령을 건 CI 단계는 exit 0 → exit 1입니다.
새 커맨드와 새 config 섹션
dva ci [profile]— 로컬 CI 프로파일 실행기. 최상위ci.profiles.<name>
(steps[],depends_on,max_parallel1–32,timeout,warn_after,locks[]).
dva ci status,dva ci logs <run-id>, 전역--dry-run이 해석된 프로파일을 JSON으로
출력합니다. 자세히:docs/53-ci-profiles.md.dva secret push <target>— 최상위secrets.sources.<name>.sops와
secrets.targets.<name>. 자세히:docs/62-remote-artifact-jobs.md.dva job run|status|resume|verify— 최상위jobs.<name>(provider,repository,
ref,timeout,inputs,secret_targets,runs[]). 자세히: 같은 문서.plans.<plan>.entries[].profiles— compose--profile을 선언 순서대로 넘깁니다.
dva build/dva logs에도 전달됩니다.dva config migrate --write가 최상위 섹션을 정규 순서로 재배치합니다. 이 기능과 그
손상 사례들(들여쓴#이 스크립트 마지막 줄을 먹던 것, 중복 키 panic,...종결자,
빈 줄 구분자 이동, EOF 주석 상승, CRLF·단독 CR)이 모두 같은 릴리스에 들어 있습니다.
쓰기 전에 커밋해 두세요.
이번 릴리스에서 열리는 것
- import된 항목이 출처와 canonical 주소를 밝힙니다 (TASK-333):
dva manifest와
dva ls --json의 모든 항목에owner가 실립니다.as:alias를 준 import는 canonical
항목에aliases가, alias 항목에alias_of가 붙습니다.plans:import는 아직 이 세
필드를 싣지 않습니다. dva kubectl이 kubectl 패스스루의 canonical 이름입니다 (TASK-255/TASK-256).dva run --project가 subproject 이름을 완성합니다 (TASK-333).- subproject 하나를 로드하지 못해도 나머지의
p:keycompletion이 남습니다 (TASK-333). - 부모의
exclude_tags가 자식의 거부보다 먼저 판정합니다. dva config validate가 hard error를 전부 모아 한 번에 실패합니다 — legacy config를
한 번에 하나씩 고칠 필요가 없어집니다.- 그 밖의 수정(
dva logs/dva build의 plan 범위,--dry-run up의 health 대기, 중복 plan
오탐,config env seal의 원자적 create-only, Makefile 커버리지 매칭)은 CHANGELOG의
## [0.2.0]§Fixed에 있습니다.
문서 — 틀렸던 것을 철회했습니다
CHANGELOG ## [0.2.0] §Documentation에 전체 목록이 있습니다. 새 설명이 아니라 이전
문서가 사실과 달랐던 부분의 취소이므로, 그 문장에 기대어 config를 쓴 적이 있다면 읽어
보세요. 요지: interaction.runner:의 알 수 없는 값은 거부되지 않고 compose 러너로
폴백하며, script_file:의 컨테이너 내 실행 보장은 없고, endpoints.source는 compose
파일을 읽지 않으며, endpoints.tags는 CLI 플래그로 좁혀지지 않고, exclude_tags는
compose 태그를 거르지 않습니다.
호환성
MinScaffoldVersion은0.1.44로 유지됩니다. 이 상수가 묻는 것은 "이 릴리스가 무엇을
깨뜨렸는가"가 아니라 **"dva init이 이제 예전 DVA가 파싱할 수 없는 것을 내보내기
시작했는가"**입니다. 위 breaking 중 일부는Validate()밖에 있지만, scaffold가 내보내는
key 집합은 0.1.44 이후 늘지 않았습니다(v0.1.48..HEAD의init_scaffold.godiff에서
새로 나온 YAML 키 없음 — 바뀐 것은 어떤 템플릿을 고르는지이지 어떤 문법을 쓰는지가
아닙니다). 올리면 새로 만든 모든 config가 그보다 오래된 DVA 전부에서 로드를 거부하게
되고, 되돌리면 이미 배포된 config가 깨집니다.env_bridge:가 요구하는version:하한은 계속0.1.48입니다. 릴리스 번호와 같은
철자를 쓰지만 다른 값이며, 올리면 오늘 유효한 사용자 config가 내일 거부됩니다.dva manifest의schema_version은 1.5 → 1.8입니다(전부 추가 전용). 1.5 → 1.6은
ci_profiles와ci커맨드 항목, 1.6 → 1.7은 항목별owner·aliases·alias_of,
1.7 → 1.8은secret_targets·jobs와 커맨드 항목의 새effects입니다. 1.7을 핀한
소비자는 이 릴리스의 바이너리에서 깨집니다.dva ktl은dva kubectl을 가리키는 visible compatibility 이름으로 남습니다. 이
릴리스에서 deprecate하거나 제거하지 않습니다.dva validate는dva config validate의 visible compatibility route입니다.- 공개는 여전히 CI가 아닌 승인된 수동 절차입니다. 이미 공개된 tag는 이동하거나 다시
만들지 않습니다.