From 63021f63b20e70668261fcd4a7a8b5e649f41b6e Mon Sep 17 00:00:00 2001 From: jungwoo choi Date: Mon, 31 Aug 2026 12:31:41 +0900 Subject: [PATCH] =?UTF-8?q?feat:=20=EC=9A=B4=EC=98=81=20=EC=8A=B9=EA=B2=A9?= =?UTF-8?q?=20=EA=B2=8C=EC=9D=B4=ED=8A=B8=20Phase2=20=E2=80=94=20=EC=BD=98?= =?UTF-8?q?=EC=86=94=20=ED=9C=B4=EB=A8=BC=20=EC=8A=B9=EC=9D=B8=20=EC=97=B0?= =?UTF-8?q?=EB=8F=99=20(v0.28.0)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - project confirm: 게이트 켜진 prod 클러스터에 승인 요청 best-effort 등록(YAKCLOUD_ACTION=register-approval) - deploy.py: register-approval 액션(워크로드별 PENDING 생성) + 403 힌트를 운영 승인 안내로 - SKILL.md: 운영 승격 게이트에 콘솔 휴먼 승인(Phase2) 명시 — 에이전트 승인 불가 Co-Authored-By: Claude Opus 4.8 --- .claude/skills/yakcloud-deploy/SKILL.md | 6 ++++-- .yakcloud/deploy.py | 15 ++++++++++++++- bin/yakcloud | 10 ++++++++-- 3 files changed, 26 insertions(+), 5 deletions(-) diff --git a/.claude/skills/yakcloud-deploy/SKILL.md b/.claude/skills/yakcloud-deploy/SKILL.md index 2c9d2e1..a1d7316 100644 --- a/.claude/skills/yakcloud-deploy/SKILL.md +++ b/.claude/skills/yakcloud-deploy/SKILL.md @@ -27,8 +27,10 @@ CLI 가 스스로 전제조건을 검사하고 오류를 안내하므로, 실패 읽기·로컬·멱등 명령은 확인 없이 즉시 실행.) **⚠ 운영 승격 게이트 — `project confirm` 은 사람이 dev 검증을 마쳤다는 사인오프다. 에이전트가 임의로 실행하지 마라.** -`project promote --to prod ` 는 그 버전의 `project confirm` 이 선행돼야 통과한다(val 은 자유). 사람의 명시적 검증 확인이 없으면 -confirm/prod-promote 를 실행하지 말고, "dev 에서 확인 후 confirm 하라"고 안내만 한다. +`project promote --to prod ` 는 그 버전의 `project confirm`(로컬 게이트, Phase1)이 선행돼야 통과한다(val 은 자유). +게다가 **클러스터에 '운영 승격 승인'이 켜져 있으면**(콘솔 클러스터 설정) 백엔드가 그 버전을 막고, **소유자가 콘솔의 배포 탭 +'운영 승격 승인'에서 사람이 직접 승인**해야만 배포된다(Phase2 — 배포토큰/에이전트는 승인 불가). 에이전트는 confirm/prod-promote 를 +임의로 하지 말고 "dev 에서 확인 후 confirm, 콘솔에서 승인"을 안내만 한다. ## CLI (권장) 설치: `curl -fsSL https://gitea.yakenator.io/yakenator/yakcloud-starter/raw/branch/main/install.sh | sh` diff --git a/.yakcloud/deploy.py b/.yakcloud/deploy.py index e85332d..1e2df24 100644 --- a/.yakcloud/deploy.py +++ b/.yakcloud/deploy.py @@ -57,7 +57,7 @@ def api(method: str, path: str, body: dict | None = None, _retry: int = 5) -> di return api(method, path, body, _retry - 1) hint = { 401: " — 배포 토큰(YAKCLOUD_TOKEN)이 없거나 만료/오류", - 403: " — 이 토큰 권한으로는 불가(엔드포인트 권한 확인)", + 403: " — 권한 없음 또는 운영 승격 승인 대기(콘솔의 배포 탭 '운영 승격 승인'에서 이 버전을 승인 후 재시도)", 404: " — 경로의 id/이름 확인", 405: " — 이 배포 토큰/엔드포인트로 허용되지 않는 작업(콘솔에서 처리 필요)", }.get(e.code, "") @@ -292,6 +292,19 @@ def main() -> None: dom = f" +도메인 {ENV_DOMAINS}" if ENV_DOMAINS else "" log(f"[{ENV}] project '{m.get('project')}' → cluster '{cname}' ({CLUSTER}){dom} (tag {TAG}){' [DRY-RUN]' if DRY else ''}") requires, workloads = resolve_env(m, ENV) # base + environments. 오버레이 병합 + # 운영 승격 승인 요청만 등록(project confirm 이 호출) — 게이트 켜진 클러스터면 워크로드별 PENDING 생성 후 종료. + if os.environ.get("YAKCLOUD_ACTION") == "register-approval": + cinfo = unwrap(api("GET", f"/clusters/{CLUSTER}")) or {} + if not cinfo.get("requireProdApproval"): + log(f"클러스터 '{cname}' 은 운영 승격 승인 게이트가 꺼져 있어 요청 불필요.") + return + for w in workloads: + try: + api("POST", f"/clusters/{CLUSTER}/prod-approvals", {"name": w["name"], "version": TAG}) + log(f"운영 승격 승인 요청 등록: {w['name']} {TAG} — 콘솔에서 승인 필요") + except BaseException as e: # noqa: BLE001 — best-effort(로그인/네트워크 실패 무시) + log(f"승인 요청 등록 스킵({w['name']}): {e}") + return src_ids: dict[str, str | None] = {} for req in requires: src_ids[req["name"]] = reconcile_source(req) diff --git a/bin/yakcloud b/bin/yakcloud index 6b044b6..1aa572e 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.27.0" +VERSION="0.28.0" # 로컬 개발 클러스터(kind) — 개발 후 운영까지 '직선 배포'의 dev 구간. DEV_CTX="${YAK_DEV_CONTEXT:-kind-yak-dev}" DEV_CLUSTER="${YAK_DEV_CLUSTER:-yak-dev}" @@ -438,7 +438,13 @@ rec={"project":m.get("project",""),"version":os.environ["YC_TAG"],"cluster":os.e "host":os.uname().nodename,"note":os.environ.get("YC_NOTE","")} print(json.dumps(rec,ensure_ascii=False,indent=2)) PY - echo "✓ 개발계 검증 확정: $tag. 이제 'yakcloud project promote --to prod $tag' 가능(그 버전 한정)." + # 백엔드에 운영 승격 승인 '요청'을 등록(best-effort) — 게이트 켜진 prod 클러스터면 콘솔 승인 대기함에 뜬다. + # 승인 자체는 콘솔(사람)만 가능. 로그인 안 됐거나 prod 미지정이면 조용히 스킵(promote 첫 시도에 자동 생성). + if [ -n "${YAKCLOUD_TOKEN:-}" ] && [ -n "${YAKCLOUD_URL:-}" ]; then + YAKCLOUD_ENV=prod YAKCLOUD_ACTION=register-approval TAG="$tag" python3 "$(_engine deploy)" yakcloud.yaml 2>/dev/null \ + || echo " (콘솔 승인 요청 등록은 건너뜀 — 운영 게이트가 켜져 있으면 promote 시 콘솔 승인이 필요합니다.)" + fi + echo "✓ 개발계 검증 확정: $tag. 다음: 운영 게이트가 켜져 있으면 콘솔에서 승인 → 'yakcloud project promote --to prod $tag'." } cmd_project_promote() {