From 3f12342693ac314435b9124c6739c4b134f99031 Mon Sep 17 00:00:00 2001 From: jungwoo choi Date: Sat, 29 Aug 2026 11:38:50 +0900 Subject: [PATCH] =?UTF-8?q?feat(cli):=20=EB=A1=9C=EC=BB=AC=20dev=20?= =?UTF-8?q?=ED=81=B4=EB=9F=AC=EC=8A=A4=ED=84=B0=EB=A5=BC=20=EC=BD=98?= =?UTF-8?q?=EC=86=94=20=ED=81=B4=EB=9F=AC=EC=8A=A4=ED=84=B0=20=EB=AA=A9?= =?UTF-8?q?=EB=A1=9D=EC=97=90=20=EB=93=B1=EB=A1=9D(=EB=82=B4=20=EB=A1=9C?= =?UTF-8?q?=EC=BB=AC/=EB=82=B4=20=EC=9B=90=EA=B2=A9)=20=E2=80=94=200.15.0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit dev up/deploy 가 배포토큰으로 콘솔에 로컬 클러스터를 best-effort 메타 등록/heartbeat, dev down 이 해제. 로그인 없으면 조용히 스킵(오프라인 dev 불변식). 콘솔 사이드바가 '내 로컬 클러스터'/'내 원격 클러스터'로 분리. Co-Authored-By: Claude Opus 4.8 --- .claude/skills/yakcloud-deploy/SKILL.md | 2 +- bin/yakcloud | 29 ++++++++++++++++++++++++- 2 files changed, 29 insertions(+), 2 deletions(-) diff --git a/.claude/skills/yakcloud-deploy/SKILL.md b/.claude/skills/yakcloud-deploy/SKILL.md index 7f18d5d..6016e45 100644 --- a/.claude/skills/yakcloud-deploy/SKILL.md +++ b/.claude/skills/yakcloud-deploy/SKILL.md @@ -25,7 +25,7 @@ YakCloud 콘솔 API로 **데이터소스 리컨실 + 워크로드 배포 + 바 **프로젝트 라이프사이클 (로컬 kind dev → 관리형 val/prod, 한 줄기 직선)** - **"클러스터 생성"** → `yakcloud cluster create [--template S|M] [--nodes N] [--golden]` (배포토큰으로 프로비저닝, 완료까지 대기). `yakcloud cluster ls` 로 목록. 검증/운영 클러스터를 만들어 매니페스트 environments 에 지정. - **"프로젝트 초기화"** → 빈 폴더에서 `yakcloud project init [name]` (스타터+CI+매니페스트+이 스킬) -- **"로컬 개발"**(권장 반복 루프) → `yakcloud dev up`(최초 1회 — 로컬 kind 생성+인그레스) → `yakcloud dev deploy`. `requires` 소스+`workloads` 가 로컬에서 **관리형과 동일한 바인딩 env**로 뜬다(콘솔/CI·인터넷 불필요, 오프라인 반복). `dev status`/`dev clean`/`dev down`. 상세 `docs/local-dev-cluster.md`. +- **"로컬 개발"**(권장 반복 루프) → `yakcloud dev up`(최초 1회 — 로컬 kind 생성+인그레스) → `yakcloud dev deploy`. `requires` 소스+`workloads` 가 로컬에서 **관리형과 동일한 바인딩 env**로 뜬다(콘솔/CI·인터넷 불필요, 오프라인 반복). `dev status`/`dev clean`/`dev down`. 로그인돼 있으면 이 로컬 클러스터가 **콘솔 클러스터 목록('내 로컬 클러스터')에도 자동 등록/해제**된다(콘솔은 접속 않고 목록 표시만). 상세 `docs/local-dev-cluster.md`. - **"개발 배포"**(관리형 개발 클러스터) → `yakcloud project deploy` (자동 버전업 → 태그 push → CI 빌드 → **개발 클러스터**). 기본 도메인으로 확인. - **"승격"**(개발→검증→운영) → `yakcloud project promote [vX.Y.Z] [--to val|prod]` (그 이미지를 **재빌드 없이 상위 환경으로**; `--to prod` 는 운영 도메인 부착). 검증계(val)는 선택 — `environments` 에 없으면 개발→운영 2단계 - **"프로젝트 개괄"** → `yakcloud project info` · 사전 점검 → `yakcloud project check [--env dev|val|prod]` diff --git a/bin/yakcloud b/bin/yakcloud index 0955024..ef629d4 100755 --- a/bin/yakcloud +++ b/bin/yakcloud @@ -16,7 +16,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.14.0" +VERSION="0.15.0" # 로컬 개발 클러스터(kind) — 개발 후 운영까지 '직선 배포'의 dev 구간. DEV_CTX="${YAK_DEV_CONTEXT:-kind-yak-dev}" DEV_CLUSTER="${YAK_DEV_CLUSTER:-yak-dev}" @@ -429,9 +429,34 @@ EOF kubectl --context "$DEV_CTX" -n ingress-nginx rollout status deploy/ingress-nginx-controller --timeout=180s >/dev/null 2>&1 \ || warn_line "ingress 컨트롤러 준비 지연 — 잠시 후 'yakcloud dev deploy'." echo "✓ 로컬 dev 클러스터 준비 ($DEV_CTX). 다음: yakcloud dev deploy" + _dev_register } warn_line() { echo " ⚠ $*" >&2; } +# 콘솔 클러스터 목록에 이 로컬 클러스터를 메타 등록 — 머신 호스트명 기준(머신당 1개, 프로젝트는 네임스페이스). +_local_reg_name() { + local h; h="$(hostname -s 2>/dev/null | tr '[:upper:]' '[:lower:]' | tr -cs 'a-z0-9-' '-' | sed 's/^-*//; s/-*$//')" + echo "${h:-local-dev}" +} +# best-effort 등록/heartbeat — 로그인(URL+토큰) 없으면 조용히 스킵(오프라인 dev 불변식 유지). +_dev_register() { + { [ -n "${YAKCLOUD_URL:-}" ] && [ -n "${YAKCLOUD_TOKEN:-}" ]; } || return 0 + local name; name="$(_local_reg_name)" + curl -fsS -m 6 -X POST "${YAKCLOUD_URL%/}/api/v1/clusters/local" \ + -H "Authorization: Bearer $YAKCLOUD_TOKEN" -H 'content-type: application/json' \ + -d "{\"name\":\"$name\",\"displayName\":\"로컬 개발 (kind)\"}" >/dev/null 2>&1 \ + && info "콘솔 클러스터 목록에 로컬 '$name' 등록/갱신" || true + return 0 +} +_dev_deregister() { + { [ -n "${YAKCLOUD_URL:-}" ] && [ -n "${YAKCLOUD_TOKEN:-}" ]; } || return 0 + local name; name="$(_local_reg_name)" + curl -fsS -m 6 -X DELETE "${YAKCLOUD_URL%/}/api/v1/clusters/local/$name" \ + -H "Authorization: Bearer $YAKCLOUD_TOKEN" >/dev/null 2>&1 \ + && info "콘솔 클러스터 목록에서 로컬 '$name' 해제" || true + return 0 +} + cmd_dev() { local sub="${1:-}"; [ "$#" -gt 0 ] && shift case "$sub" in @@ -445,12 +470,14 @@ cmd_dev() { local pysub="$sub"; [ "$sub" = clean ] && pysub="down" YAK_DEV_CONTEXT="$DEV_CTX" YAK_DEV_CLUSTER="$DEV_CLUSTER" YAK_DEV_RUNTIME="${rt:-docker}" \ TAG="${tag:-${TAG:-dev}}" python3 scripts/yakcloud_dev.py "$pysub" yakcloud.yaml + [ "$sub" = deploy ] && _dev_register || true ;; down) command -v kind >/dev/null 2>&1 || die "kind 필요" local rt kenv=""; rt="$(_dev_runtime)"; [ "$rt" = podman ] && kenv="KIND_EXPERIMENTAL_PROVIDER=podman" info "kind 클러스터 '$DEV_CLUSTER' 삭제…" env $kenv kind delete cluster --name "$DEV_CLUSTER" && echo "✓ 삭제 완료" + _dev_deregister ;; *) echo "yakcloud dev "; exit 1 ;; esac