cli v0.12.2: 개발→검증→운영 3단계 — promote --to <env> 일반화, environments 템플릿 dev/val/prod, init 마법사 검증 클러스터

This commit is contained in:
2026-08-28 06:02:21 +09:00
parent ef287cbfd4
commit d2de054b3b
4 changed files with 30 additions and 17 deletions

View File

@ -20,8 +20,8 @@ allowed-tools: Bash, Read, Write, Edit, Glob, Grep, AskUserQuestion
- **`login`** → 비밀 입력이라 여기서 실행하지 말고, 터미널에서 `yakcloud login` 하라고 한 줄 안내. - **`login`** → 비밀 입력이라 여기서 실행하지 말고, 터미널에서 `yakcloud login` 하라고 한 줄 안내.
(사용자가 값을 직접 주면 `YAKCLOUD_URL=… YAKCLOUD_TOKEN=… GITEA_USER=… GITEA_TOKEN=… yakcloud config` 로 저장하되 **값은 화면에 출력 금지**.) (사용자가 값을 직접 주면 `YAKCLOUD_URL=… YAKCLOUD_TOKEN=… GITEA_USER=… GITEA_TOKEN=… yakcloud config` 로 저장하되 **값은 화면에 출력 금지**.)
- **`cluster create <name> …`** → 배포토큰으로 클러스터 프로비저닝(5~8분 대기). 실행 전 무엇을(템플릿·노드 수) 만드는지 한 줄 알리고 진행. `cluster ls` = 목록. - **`cluster create <name> …`** → 배포토큰으로 클러스터 프로비저닝(5~8분 대기). 실행 전 무엇을(템플릿·노드 수) 만드는지 한 줄 알리고 진행. `cluster ls` = 목록.
- **`project promote [vX.Y.Z]`** → 개발에서 검증한 그 이미지를 **재빌드 없이 운영 클러스터로** 승격 + 운영 도메인. - **`project promote [vX.Y.Z] [--to val|prod]`** → 그 이미지를 **재빌드 없이 상위 환경으로** 승격(개발→검증→운영, 기본 --to prod).
파괴적/외부노출이므로 실행 전 무엇을(어느 태그를 어느 운영 클러스터로) 하는지 한 줄 알리고 진행. 파괴적/외부노출이므로 실행 전 무엇을(어느 태그를 어느 환경 클러스터로) 하는지 한 줄 알리고 진행.
- **그 외 전부**(`project deploy|info|check|update`, `datasource ls`, `domain`, `scale`, `set`, `env`, - **그 외 전부**(`project deploy|info|check|update`, `datasource ls`, `domain`, `scale`, `set`, `env`,
`source`, `bind`, `unbind`, `config`, `upgrade`) → `yakcloud $ARGUMENTS` 를 실행하고 **출력을 그대로** 보여준 뒤, `source`, `bind`, `unbind`, `config`, `upgrade`) → `yakcloud $ARGUMENTS` 를 실행하고 **출력을 그대로** 보여준 뒤,
필요할 때만 한 줄로 해석. 배포/승격/삭제성(`deploy`·`promote`·`source rm`·`unbind`·`domain`)은 실행 전 무엇을 하는지 한 줄 알리고 진행. 필요할 때만 한 줄로 해석. 배포/승격/삭제성(`deploy`·`promote`·`source rm`·`unbind`·`domain`)은 실행 전 무엇을 하는지 한 줄 알리고 진행.

View File

@ -26,7 +26,7 @@ YakCloud 콘솔 API로 **데이터소스 리컨실 + 워크로드 배포 + 바
- **"클러스터 생성"** → `yakcloud cluster create <name> [--template S|M] [--nodes N] [--golden]` (배포토큰으로 프로비저닝, 완료까지 대기). `yakcloud cluster ls` 로 목록. 개발/운영 각각 만들어 매니페스트 environments 에 지정. - **"클러스터 생성"** → `yakcloud cluster create <name> [--template S|M] [--nodes N] [--golden]` (배포토큰으로 프로비저닝, 완료까지 대기). `yakcloud cluster ls` 로 목록. 개발/운영 각각 만들어 매니페스트 environments 에 지정.
- **"프로젝트 초기화"** → 빈 폴더에서 `yakcloud project init [name]` (스타터+CI+매니페스트+이 스킬) - **"프로젝트 초기화"** → 빈 폴더에서 `yakcloud project init [name]` (스타터+CI+매니페스트+이 스킬)
- **"개발 배포"** → `yakcloud project deploy` (자동 버전업 → 태그 push → CI 빌드 → **개발 클러스터**). 기본 도메인으로 확인. - **"개발 배포"** → `yakcloud project deploy` (자동 버전업 → 태그 push → CI 빌드 → **개발 클러스터**). 기본 도메인으로 확인.
- **"운영 승격"** → `yakcloud project promote [vX.Y.Z]` (개발에서 검증한 **그 이미지를 재빌드 없이 운영 클러스터로** + 운영 도메인) - **"승격"**(개발→검증→운영)`yakcloud project promote [vX.Y.Z] [--to val|prod]` (그 이미지를 **재빌드 없이 상위 환경으로**; `--to prod` 는 운영 도메인 부착). 검증계(val)는 선택 — `environments` 에 없으면 개발→운영 2단계
- **"프로젝트 개괄"** → `yakcloud project info` · 사전 점검 → `yakcloud project check [--prod]` - **"프로젝트 개괄"** → `yakcloud project info` · 사전 점검 → `yakcloud project check [--prod]`
- 매니페스트 `environments.{dev,prod}.cluster` (+ `prod.domains`). 로컬 개발/로컬 데이터소스는 없음 — 개발도 클러스터 배포로 확인. - 매니페스트 `environments.{dev,prod}.cluster` (+ `prod.domains`). 로컬 개발/로컬 데이터소스는 없음 — 개발도 클러스터 배포로 확인.

View File

@ -3,7 +3,7 @@
# #
# yakcloud project init [name] 빈 폴더에 스타터 스캐폴딩(앱+CI+매니페스트+Claude 스킬) # yakcloud project init [name] 빈 폴더에 스타터 스캐폴딩(앱+CI+매니페스트+Claude 스킬)
# yakcloud project deploy [vX.Y.Z] 커밋 + 태그 push → CI 빌드 → 개발 클러스터 배포 # yakcloud project deploy [vX.Y.Z] 커밋 + 태그 push → CI 빌드 → 개발 클러스터 배포
# yakcloud project promote [vX.Y.Z] 그 이미지를 재빌드 없이 운영 클러스터로 승격 + 운영 도메인 # yakcloud project promote [vX.Y.Z] [--to val|prod] 그 이미지를 재빌드 없이 상위 환경으로 승격(개발→검증→운영)
# yakcloud project info 프로젝트 개괄(매니페스트 + dev/prod 라이브 상태) # yakcloud project info 프로젝트 개괄(매니페스트 + dev/prod 라이브 상태)
# yakcloud project check [--prod] dry-run(직접 API 계획만) # yakcloud project check [--prod] dry-run(직접 API 계획만)
# #
@ -14,7 +14,7 @@ set -uo pipefail # -e 미사용: 'test && action' 관용구가 값 없을 때
REPO="${YAKCLOUD_STARTER_REPO:-https://gitea.yakenator.io/yakenator/yakcloud-starter}" REPO="${YAKCLOUD_STARTER_REPO:-https://gitea.yakenator.io/yakenator/yakcloud-starter}"
BRANCH="${YAKCLOUD_STARTER_BRANCH:-main}" BRANCH="${YAKCLOUD_STARTER_BRANCH:-main}"
VERSION="0.12.1" VERSION="0.12.2"
CONFIG_DIR="${YAKCLOUD_CONFIG_DIR:-$HOME/.config/yakcloud}" CONFIG_DIR="${YAKCLOUD_CONFIG_DIR:-$HOME/.config/yakcloud}"
CONFIG_FILE="$CONFIG_DIR/config" CONFIG_FILE="$CONFIG_DIR/config"
@ -158,10 +158,11 @@ cmd_project_init() {
# 매니페스트 채우기 마법사 — ctl 은 API 미접속(YAKCLOUD_TOKEN 비움)으로 매니페스트만 편집. # 매니페스트 채우기 마법사 — ctl 은 API 미접속(YAKCLOUD_TOKEN 비움)으로 매니페스트만 편집.
_init_wizard() { _init_wizard() {
local dflt_app="$1" app devc prodc pdom port health cpu mem reps path kv local dflt_app="$1" app devc valc prodc pdom port health cpu mem reps path kv
echo "── 프로젝트 설정 (엔터=기본값) ──" echo "── 프로젝트 설정 (엔터=기본값) · 개발 → 검증 → 운영 3단계 ──"
printf "앱 이름 [%s]: " "$dflt_app"; read -r app; app="${app:-$dflt_app}" printf "앱 이름 [%s]: " "$dflt_app"; read -r app; app="${app:-$dflt_app}"
printf "개발 클러스터(이름 또는 콘솔 id): "; read -r devc printf "개발 클러스터(이름 또는 콘솔 id): "; read -r devc
printf "검증 클러스터(선택, 엔터=생략): "; read -r valc
printf "운영 클러스터 [%s]: " "${devc:-개발과 동일}"; read -r prodc; prodc="${prodc:-$devc}" printf "운영 클러스터 [%s]: " "${devc:-개발과 동일}"; read -r prodc; prodc="${prodc:-$devc}"
printf "운영 도메인(선택, 예 app.example.com): "; read -r pdom printf "운영 도메인(선택, 예 app.example.com): "; read -r pdom
printf "컨테이너 포트 [8080]: "; read -r port; port="${port:-8080}" printf "컨테이너 포트 [8080]: "; read -r port; port="${port:-8080}"
@ -176,6 +177,8 @@ _init_wizard() {
[ -n "$devc" ] && { sed -i.bak "s#my-dev-cluster#$devc#" yakcloud.yaml && rm -f yakcloud.yaml.bak; } [ -n "$devc" ] && { sed -i.bak "s#my-dev-cluster#$devc#" yakcloud.yaml && rm -f yakcloud.yaml.bak; }
[ -n "$prodc" ] && { sed -i.bak "s#my-prod-cluster#$prodc#" yakcloud.yaml && rm -f yakcloud.yaml.bak; } [ -n "$prodc" ] && { sed -i.bak "s#my-prod-cluster#$prodc#" yakcloud.yaml && rm -f yakcloud.yaml.bak; }
[ -n "$pdom" ] && { sed -i.bak "s#domains: \[\]#domains: [$pdom]#" yakcloud.yaml && rm -f yakcloud.yaml.bak; } [ -n "$pdom" ] && { sed -i.bak "s#domains: \[\]#domains: [$pdom]#" yakcloud.yaml && rm -f yakcloud.yaml.bak; }
if [ -n "$valc" ]; then sed -i.bak "s#my-val-cluster#$valc#" yakcloud.yaml && rm -f yakcloud.yaml.bak
else sed -i.bak "/my-val-cluster/d" yakcloud.yaml && rm -f yakcloud.yaml.bak; fi # 검증 생략 시 val 줄 제거
ensure_pyyaml ensure_pyyaml
YAKCLOUD_TOKEN= python3 scripts/yakcloud_ctl.py set web --port "$port" --health "$health" --cpu "$cpu" --mem "$mem" --path "$path" >/dev/null 2>&1 || true YAKCLOUD_TOKEN= python3 scripts/yakcloud_ctl.py set web --port "$port" --health "$health" --cpu "$cpu" --mem "$mem" --path "$path" >/dev/null 2>&1 || true
YAKCLOUD_TOKEN= python3 scripts/yakcloud_ctl.py scale web "$reps" >/dev/null 2>&1 || true YAKCLOUD_TOKEN= python3 scripts/yakcloud_ctl.py scale web "$reps" >/dev/null 2>&1 || true
@ -184,7 +187,7 @@ _init_wizard() {
printf " env> "; read -r kv || break; [ -z "$kv" ] && break printf " env> "; read -r kv || break; [ -z "$kv" ] && break
YAKCLOUD_TOKEN= python3 scripts/yakcloud_ctl.py env web "$kv" >/dev/null 2>&1 || echo " (형식: KEY=VALUE)" YAKCLOUD_TOKEN= python3 scripts/yakcloud_ctl.py env web "$kv" >/dev/null 2>&1 || echo " (형식: KEY=VALUE)"
done done
echo "✓ 매니페스트 반영: app=$app dev=${devc:-<미지정>} prod=${prodc:-<미지정>} port=$port path=$path cpu=$cpu mem=$mem replicas=$reps" echo "✓ 매니페스트 반영: app=$app dev=${devc:-<미지정>} val=${valc:-<생략>} prod=${prodc:-<미지정>} port=$port path=$path cpu=$cpu mem=$mem replicas=$reps"
} }
_maybe_login_prompt() { _maybe_login_prompt() {
@ -280,16 +283,23 @@ cmd_project_promote() {
[ -f scripts/yakcloud_deploy.py ] || die "'yakcloud project init' 먼저" [ -f scripts/yakcloud_deploy.py ] || die "'yakcloud project init' 먼저"
: "${YAKCLOUD_URL:?YAKCLOUD_URL 필요 — 'yakcloud login'}"; : "${YAKCLOUD_TOKEN:?YAKCLOUD_TOKEN 필요 — 'yakcloud login'}" : "${YAKCLOUD_URL:?YAKCLOUD_URL 필요 — 'yakcloud login'}"; : "${YAKCLOUD_TOKEN:?YAKCLOUD_TOKEN 필요 — 'yakcloud login'}"
ensure_pyyaml ensure_pyyaml
local tag=""; for a in "$@"; do case "$a" in -*) : ;; *) [ -z "$tag" ] && tag="$a" ;; esac; done local tag="" env="prod"
while [ "$#" -gt 0 ]; do
case "$1" in
--to|--env) env="${2:-prod}"; shift 2 || shift ;;
-*) shift ;;
*) [ -z "$tag" ] && tag="$1"; shift ;;
esac
done
if [ -z "$tag" ]; then if [ -z "$tag" ]; then
git fetch -q --tags origin 2>/dev/null || true git fetch -q --tags origin 2>/dev/null || true
tag="$(git tag -l 'v*' --sort=-v:refname 2>/dev/null | head -1)" tag="$(git tag -l 'v*' --sort=-v:refname 2>/dev/null | head -1)"
fi fi
[ -n "$tag" ] || die "승격할 태그가 없습니다 — 'yakcloud project promote vX.Y.Z' (개발 배포된 태그)" [ -n "$tag" ] || die "승격할 태그가 없습니다 — 'yakcloud project promote vX.Y.Z' (개발 배포된 태그)"
case "$tag" in v*) : ;; *) tag="v$tag" ;; esac case "$tag" in v*) : ;; *) tag="v$tag" ;; esac
info "운영 승격: $tag → prod 환경(재빌드 없이 개발에서 빌드된 그 이미지를 운영 클러스터로 리컨실)" info "'$env' 승격: $tag → '$env' 환경(재빌드 없이 개발에서 빌드된 그 이미지를 리컨실)"
YAKCLOUD_ENV=prod TAG="$tag" python3 scripts/yakcloud_deploy.py yakcloud.yaml YAKCLOUD_ENV="$env" TAG="$tag" python3 scripts/yakcloud_deploy.py yakcloud.yaml
echo "✓ 승격 완료: $tag → 운영 클러스터 + 운영 도메인(기본 도메인은 자동)" echo "✓ 승격 완료: $tag → '$env' 클러스터 + 도메인(기본 도메인은 자동)"
} }
# 클러스터 — 계정 배포토큰으로 생성/조회(프로젝트 폴더 불필요). POST /clusters(템플릿) + 프로비저닝 폴. # 클러스터 — 계정 배포토큰으로 생성/조회(프로젝트 폴더 불필요). POST /clusters(템플릿) + 프로비저닝 폴.
@ -433,7 +443,7 @@ yakcloud $VERSION — YakCloud 프로젝트 CLI (개발·운영 모두 yakcloud
project init [name] 빈 폴더에 스타터 스캐폴딩(대화형 마법사) + 자격 프롬프트 project init [name] 빈 폴더에 스타터 스캐폴딩(대화형 마법사) + 자격 프롬프트
project deploy [vX.Y.Z] 커밋 + 태그 push → CI 빌드 → **개발 클러스터** 배포(버전 생략=자동 버전업) project deploy [vX.Y.Z] 커밋 + 태그 push → CI 빌드 → **개발 클러스터** 배포(버전 생략=자동 버전업)
project deploy --local 러너 없이 로컬 build/push/리컨실(CI 대안, 개발 클러스터) project deploy --local 러너 없이 로컬 build/push/리컨실(CI 대안, 개발 클러스터)
project promote [vX.Y.Z] 개발에서 검증한 그 이미지를 재빌드 없이 **운영 클러스터**로 승격 + 운영 도메인 project promote [vX.Y.Z] [--to val|prod] 그 이미지를 재빌드 없이 상위 환경으로 승격(개발→검증→운영). 기본 --to prod
project info 프로젝트 개괄(매니페스트 + dev/prod 라이브 상태) project info 프로젝트 개괄(매니페스트 + dev/prod 라이브 상태)
project check [--prod] dry-run(직접 API 계획만) project check [--prod] dry-run(직접 API 계획만)
project update 이 프로젝트의 엔진·CI·스킬만 최신화(앱·매니페스트 보존) project update 이 프로젝트의 엔진·CI·스킬만 최신화(앱·매니페스트 보존)

View File

@ -6,11 +6,14 @@
apiVersion: yakcloud/v1 apiVersion: yakcloud/v1
project: my-app # 프로젝트 이름(이미지 경로/표시에 사용) — 소문자·숫자·하이픈 project: my-app # 프로젝트 이름(이미지 경로/표시에 사용) — 소문자·숫자·하이픈
# 환경 — 개발/운영 클러스터. 'yakcloud project deploy'=dev 로 배포, 'yakcloud project promote'=그 이미지를 # 환경 — 개발 → 검증 → 운영 3단계. 각 환경 = 별도 클러스터. 검증계(val)가 필요 없으면 그 줄을 지워도 됩니다.
# 재빌드 없이 prod 로 승격(운영 도메인 부착; 기본 도메인 c-xxxx.yakenator.io 은 두 환경 모두 자동). # deploy → dev (태그 push → CI 빌드 → 개발 클러스터, 기본 도메인으로 확인)
# promote --to val→ 그 이미지를 재빌드 없이 검증 클러스터로
# promote --to prod→ 검증 통과분을 운영 클러스터로 + 운영 도메인(기본 도메인 c-xxxx.yakenator.io 은 자동)
environments: environments:
dev: { cluster: my-dev-cluster } # 태그 push → CI 빌드 → 개발 클러스터 dev: { cluster: my-dev-cluster } # deploy 대상(기본)
prod: { cluster: my-prod-cluster, domains: [] } # promote → 운영 클러스터 + domains(예: [app.example.com]) val: { cluster: my-val-cluster } # promote --to val
prod: { cluster: my-prod-cluster, domains: [] } # promote --to prod (domains 예: [app.example.com])
# 필요한 데이터 소스(논리 이름). 없으면 [] 로 둬도 됨. # 필요한 데이터 소스(논리 이름). 없으면 [] 로 둬도 됨.
# type ∈ postgresql·mysql·mariadb·mongodb·redis·minio·rabbitmq·solr·oracle, plan ∈ small·medium·large # type ∈ postgresql·mysql·mariadb·mongodb·redis·minio·rabbitmq·solr·oracle, plan ∈ small·medium·large