From d2de054b3bb98497d570d19a2b2434664aee3bc4 Mon Sep 17 00:00:00 2001 From: yakenator Date: Fri, 28 Aug 2026 06:02:21 +0900 Subject: [PATCH] =?UTF-8?q?cli=20v0.12.2:=20=EA=B0=9C=EB=B0=9C=E2=86=92?= =?UTF-8?q?=EA=B2=80=EC=A6=9D=E2=86=92=EC=9A=B4=EC=98=81=203=EB=8B=A8?= =?UTF-8?q?=EA=B3=84=20=E2=80=94=20promote=20--to=20=20=EC=9D=BC?= =?UTF-8?q?=EB=B0=98=ED=99=94,=20environments=20=ED=85=9C=ED=94=8C?= =?UTF-8?q?=EB=A6=BF=20dev/val/prod,=20init=20=EB=A7=88=EB=B2=95=EC=82=AC?= =?UTF-8?q?=20=EA=B2=80=EC=A6=9D=20=ED=81=B4=EB=9F=AC=EC=8A=A4=ED=84=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .claude/commands/yakcloud.md | 4 ++-- .claude/skills/yakcloud-deploy/SKILL.md | 2 +- bin/yakcloud | 30 ++++++++++++++++--------- yakcloud.yaml | 11 +++++---- 4 files changed, 30 insertions(+), 17 deletions(-) diff --git a/.claude/commands/yakcloud.md b/.claude/commands/yakcloud.md index 8033e3e..b637273 100644 --- a/.claude/commands/yakcloud.md +++ b/.claude/commands/yakcloud.md @@ -20,8 +20,8 @@ allowed-tools: Bash, Read, Write, Edit, Glob, Grep, AskUserQuestion - **`login`** → 비밀 입력이라 여기서 실행하지 말고, 터미널에서 `yakcloud login` 하라고 한 줄 안내. (사용자가 값을 직접 주면 `YAKCLOUD_URL=… YAKCLOUD_TOKEN=… GITEA_USER=… GITEA_TOKEN=… yakcloud config` 로 저장하되 **값은 화면에 출력 금지**.) - **`cluster create …`** → 배포토큰으로 클러스터 프로비저닝(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`, `source`, `bind`, `unbind`, `config`, `upgrade`) → `yakcloud $ARGUMENTS` 를 실행하고 **출력을 그대로** 보여준 뒤, 필요할 때만 한 줄로 해석. 배포/승격/삭제성(`deploy`·`promote`·`source rm`·`unbind`·`domain`)은 실행 전 무엇을 하는지 한 줄 알리고 진행. diff --git a/.claude/skills/yakcloud-deploy/SKILL.md b/.claude/skills/yakcloud-deploy/SKILL.md index e6efb1a..af5029b 100644 --- a/.claude/skills/yakcloud-deploy/SKILL.md +++ b/.claude/skills/yakcloud-deploy/SKILL.md @@ -26,7 +26,7 @@ YakCloud 콘솔 API로 **데이터소스 리컨실 + 워크로드 배포 + 바 - **"클러스터 생성"** → `yakcloud cluster create [--template S|M] [--nodes N] [--golden]` (배포토큰으로 프로비저닝, 완료까지 대기). `yakcloud cluster ls` 로 목록. 개발/운영 각각 만들어 매니페스트 environments 에 지정. - **"프로젝트 초기화"** → 빈 폴더에서 `yakcloud project init [name]` (스타터+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]` - 매니페스트 `environments.{dev,prod}.cluster` (+ `prod.domains`). 로컬 개발/로컬 데이터소스는 없음 — 개발도 클러스터 배포로 확인. diff --git a/bin/yakcloud b/bin/yakcloud index cbd38cf..081cfc5 100755 --- a/bin/yakcloud +++ b/bin/yakcloud @@ -3,7 +3,7 @@ # # yakcloud project init [name] 빈 폴더에 스타터 스캐폴딩(앱+CI+매니페스트+Claude 스킬) # 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 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}" BRANCH="${YAKCLOUD_STARTER_BRANCH:-main}" -VERSION="0.12.1" +VERSION="0.12.2" CONFIG_DIR="${YAKCLOUD_CONFIG_DIR:-$HOME/.config/yakcloud}" CONFIG_FILE="$CONFIG_DIR/config" @@ -158,10 +158,11 @@ cmd_project_init() { # 매니페스트 채우기 마법사 — ctl 은 API 미접속(YAKCLOUD_TOKEN 비움)으로 매니페스트만 편집. _init_wizard() { - local dflt_app="$1" app devc prodc pdom port health cpu mem reps path kv - echo "── 프로젝트 설정 (엔터=기본값) ──" + local dflt_app="$1" app devc valc prodc pdom port health cpu mem reps path kv + echo "── 프로젝트 설정 (엔터=기본값) · 개발 → 검증 → 운영 3단계 ──" printf "앱 이름 [%s]: " "$dflt_app"; read -r app; app="${app:-$dflt_app}" printf "개발 클러스터(이름 또는 콘솔 id): "; read -r devc + printf "검증 클러스터(선택, 엔터=생략): "; read -r valc printf "운영 클러스터 [%s]: " "${devc:-개발과 동일}"; read -r prodc; prodc="${prodc:-$devc}" printf "운영 도메인(선택, 예 app.example.com): "; read -r pdom 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 "$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; } + 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 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 @@ -184,7 +187,7 @@ _init_wizard() { 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)" 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() { @@ -280,16 +283,23 @@ cmd_project_promote() { [ -f scripts/yakcloud_deploy.py ] || die "'yakcloud project init' 먼저" : "${YAKCLOUD_URL:?YAKCLOUD_URL 필요 — 'yakcloud login'}"; : "${YAKCLOUD_TOKEN:?YAKCLOUD_TOKEN 필요 — 'yakcloud login'}" 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 git fetch -q --tags origin 2>/dev/null || true tag="$(git tag -l 'v*' --sort=-v:refname 2>/dev/null | head -1)" fi [ -n "$tag" ] || die "승격할 태그가 없습니다 — 'yakcloud project promote vX.Y.Z' (개발 배포된 태그)" case "$tag" in v*) : ;; *) tag="v$tag" ;; esac - info "운영 승격: $tag → prod 환경(재빌드 없이 개발에서 빌드된 그 이미지를 운영 클러스터로 리컨실)" - YAKCLOUD_ENV=prod TAG="$tag" python3 scripts/yakcloud_deploy.py yakcloud.yaml - echo "✓ 승격 완료: $tag → 운영 클러스터 + 운영 도메인(기본 도메인은 자동)" + info "'$env' 승격: $tag → '$env' 환경(재빌드 없이 개발에서 빌드된 그 이미지를 리컨실)" + YAKCLOUD_ENV="$env" TAG="$tag" python3 scripts/yakcloud_deploy.py yakcloud.yaml + echo "✓ 승격 완료: $tag → '$env' 클러스터 + 도메인(기본 도메인은 자동)" } # 클러스터 — 계정 배포토큰으로 생성/조회(프로젝트 폴더 불필요). POST /clusters(템플릿) + 프로비저닝 폴. @@ -433,7 +443,7 @@ yakcloud $VERSION — YakCloud 프로젝트 CLI (개발·운영 모두 yakcloud project init [name] 빈 폴더에 스타터 스캐폴딩(대화형 마법사) + 자격 프롬프트 project deploy [vX.Y.Z] 커밋 + 태그 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 check [--prod] dry-run(직접 API 계획만) project update 이 프로젝트의 엔진·CI·스킬만 최신화(앱·매니페스트 보존) diff --git a/yakcloud.yaml b/yakcloud.yaml index e3c9a8d..9deef06 100644 --- a/yakcloud.yaml +++ b/yakcloud.yaml @@ -6,11 +6,14 @@ apiVersion: yakcloud/v1 project: my-app # 프로젝트 이름(이미지 경로/표시에 사용) — 소문자·숫자·하이픈 -# 환경 — 개발/운영 클러스터. 'yakcloud project deploy'=dev 로 배포, 'yakcloud project promote'=그 이미지를 -# 재빌드 없이 prod 로 승격(운영 도메인 부착; 기본 도메인 c-xxxx.yakenator.io 은 두 환경 모두 자동). +# 환경 — 개발 → 검증 → 운영 3단계. 각 환경 = 별도 클러스터. 검증계(val)가 필요 없으면 그 줄을 지워도 됩니다. +# deploy → dev (태그 push → CI 빌드 → 개발 클러스터, 기본 도메인으로 확인) +# promote --to val→ 그 이미지를 재빌드 없이 검증 클러스터로 +# promote --to prod→ 검증 통과분을 운영 클러스터로 + 운영 도메인(기본 도메인 c-xxxx.yakenator.io 은 자동) environments: - dev: { cluster: my-dev-cluster } # 태그 push → CI 빌드 → 개발 클러스터 - prod: { cluster: my-prod-cluster, domains: [] } # promote → 운영 클러스터 + domains(예: [app.example.com]) + dev: { cluster: my-dev-cluster } # deploy 대상(기본) + 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