feat: yakcloud 강제 가드레일 스캐폴딩 — project init 이 CLAUDE.md + .claude/settings.json(docker-compose 차단) 생성 (v0.26.0)

- init: 프로젝트 루트 CLAUDE.md(항상 로드, yakcloud-only 지침) + .claude/settings.json permissions.deny(docker compose/run 차단) 생성
- update: 위 가드레일 backfill(사용자 편집 보존)
- .claude 통삭제 → skills/commands 만 제거로 수정(프로젝트 settings.json 보존)
- SKILL.md: description 트리거를 로컬 실행·검증·개발환경으로 확장 + compose 금지 원칙

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
jungwoo choi
2026-08-31 11:25:50 +09:00
parent 0453520550
commit 6d3118dd47
2 changed files with 73 additions and 7 deletions

View File

@ -1,6 +1,6 @@
--- ---
name: yakcloud-deploy name: yakcloud-deploy
description: Deploy a project to YakCloud — yakcloud CLI (local kind dev → managed val/prod) or Gitea Actions (v* tag push) → build/push image → reconcile data sources → deploy workloads → bind env → custom domain. Use when deploying this project to YakCloud, editing yakcloud.yaml, binding data sources, adding a domain, or starting a new project. description: Develop, verify, and deploy this project via the yakcloud CLI (local kind dev → managed val/prod) — never docker-compose. Build/push image → reconcile data sources → deploy workloads → bind env → custom domain. Use when running/testing/verifying the app locally, setting up a local dev environment, deploying to YakCloud, editing yakcloud.yaml, binding data sources, adding a domain, or starting a new project. If tempted to use docker-compose / docker run to run this app, use `yakcloud project deploy` (local kind) instead.
--- ---
# YakCloud 배포 스킬 # YakCloud 배포 스킬
@ -8,6 +8,14 @@ description: Deploy a project to YakCloud — yakcloud CLI (local kind dev → m
`yakcloud` CLI 로 **로컬 kind 개발 → 관리형 검증/운영**을 한 줄기로 배포한다. 또는 `v*` 태그 push → `yakcloud` CLI 로 **로컬 kind 개발 → 관리형 검증/운영**을 한 줄기로 배포한다. 또는 `v*` 태그 push →
Gitea Actions 가 이미지 빌드/푸시 + 콘솔 API 로 데이터소스 리컨실·워크로드 배포·바인딩을 자동 처리. Gitea Actions 가 이미지 빌드/푸시 + 콘솔 API 로 데이터소스 리컨실·워크로드 배포·바인딩을 자동 처리.
## 개발·검증도 yakcloud — docker-compose 금지 (원칙)
이 프로젝트의 앱은 **로컬 개발·검증부터 운영까지 전 구간을 yakcloud 로** 진행한다.
- **로컬 실행·검증 = `yakcloud project deploy`** (로컬 kind 자동 생성, 오프라인·무료, 운영과 동일한 바인딩 env·데이터소스).
- **`docker compose` / `docker-compose` / `docker run` 으로 앱을 띄우지 마라** — 프로젝트 `.claude/settings.json` 이 이를 차단하고,
`Dockerfile` 은 yakcloud 의 이미지 빌드 전용이다. compose 스택은 운영과 환경이 달라져 파리티가 깨진다.
- 데이터소스는 손으로 컨테이너를 띄우지 말고 `yakcloud.yaml``requires` 로 선언한다.
(각 프로젝트 루트 `CLAUDE.md` 에 같은 규칙이 항상 로드되도록 심겨 있다 — `yakcloud project init` 이 생성.)
## 실행 규약 (패스스루) — 최우선 ## 실행 규약 (패스스루) — 최우선
`/yakcloud <args>` 로 호출되면 **`yakcloud <args>` 를 Bash 로 그대로 실행하고 출력만 보여준다.** `/yakcloud <args>` 로 호출되면 **`yakcloud <args>` 를 Bash 로 그대로 실행하고 출력만 보여준다.**
사전점검(설치/폴더/설정 확인)·결과 재검증·부연설명을 붙이지 말 것 — 터미널에서 직접 친 것과 동일하게 동작. 사전점검(설치/폴더/설정 확인)·결과 재검증·부연설명을 붙이지 말 것 — 터미널에서 직접 친 것과 동일하게 동작.

View File

@ -16,7 +16,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.25.0" VERSION="0.26.0"
# 로컬 개발 클러스터(kind) — 개발 후 운영까지 '직선 배포'의 dev 구간. # 로컬 개발 클러스터(kind) — 개발 후 운영까지 '직선 배포'의 dev 구간.
DEV_CTX="${YAK_DEV_CONTEXT:-kind-yak-dev}" DEV_CTX="${YAK_DEV_CONTEXT:-kind-yak-dev}"
DEV_CLUSTER="${YAK_DEV_CLUSTER:-yak-dev}" DEV_CLUSTER="${YAK_DEV_CLUSTER:-yak-dev}"
@ -152,6 +152,58 @@ _sanitize_name() {
printf '%s' "${s:-app}" printf '%s' "${s:-app}"
} }
# 프로젝트에 "yakcloud 로만 개발·검증·배포" 가드레일을 심는다 — AI 에이전트가 스킬을 켜기 전에도
# 강제되도록 (1) 항상 로드되는 CLAUDE.md 지침 + (2) .claude/settings.json 의 docker-compose 차단(deny).
# $1=force: 1=덮어씀(init), 0=없을 때만 생성(update — 사용자 편집 보존).
_scaffold_yakcloud_guardrails() {
local force="${1:-0}"
if [ "$force" = 1 ] || [ ! -f CLAUDE.md ]; then
cat > CLAUDE.md <<'YCLAUDE'
# 이 프로젝트는 YakCloud 로 개발·검증·배포합니다 (AI 에이전트 필독)
이 저장소의 앱은 **개발·검증부터 운영까지 전 구간을 `yakcloud` CLI** 로 진행합니다. 아래는 강제 규칙입니다.
## 절대 금지 (NEVER)
- **`docker compose` / `docker-compose` / `docker run` 으로 이 앱을 로컬 실행·검증하지 마라.** (`.claude/settings.json` 이 차단)
- DB·캐시·큐 등 **데이터소스 컨테이너를 손으로 띄우지 마라** — `yakcloud.yaml` 의 `requires` 로 선언한다.
- `Dockerfile` 은 **yakcloud 의 이미지 빌드 전용**. 로컬 구동 도구가 아니다.
## 일상 개발 루프 (ALWAYS)
1. 코드를 고친다.
2. **`yakcloud project deploy`** — 로컬 kind 클러스터에 배포(없으면 자동 생성, 오프라인·무료).
관리형과 **동일한 바인딩 env·데이터소스** 로 뜬다.
3. 로컬 도메인 `*.localhost` 로 화면·API·WSS 확인. 반복 개발 = 2번 반복.
4. 확인되면 **`yakcloud project promote --to val|prod`** (같은 이미지, 재빌드 없이 관리형 승격).
## 데이터소스
- `yakcloud.yaml` 의 `requires` 에 `{ name, type }` 선언 → 워크로드 `binds` 로 연결 → `<ALIAS>_URL` 등 자동 주입.
- type ∈ postgresql·mysql·mariadb·mongodb·redis·minio·rabbitmq·solr·oracle. 목록 = `yakcloud datasource ls`.
## 참고
- 상태 = `yakcloud project info` · 배포 전 dry-run = `yakcloud project check`.
- 슬래시로도 실행: `/yakcloud <명령>`. 상세 절차 = 전역 스킬 `yakcloud-deploy`.
- **왜 compose 를 안 쓰나**: compose 로컬 스택은 운영(관리형 k8s + env 주입 + 격리 소스)과 환경이 달라져
"로컬은 되는데 운영에서 깨짐" 을 만든다. yakcloud 로컬 kind 는 운영과 **직선 파리티** 를 보장한다.
YCLAUDE
fi
mkdir -p .claude
if [ "$force" = 1 ] || [ ! -f .claude/settings.json ]; then
cat > .claude/settings.json <<'YSETTINGS'
{
"permissions": {
"deny": [
"Bash(docker compose:*)",
"Bash(docker-compose:*)",
"Bash(docker run:*)",
"Bash(sudo docker compose:*)",
"Bash(sudo docker-compose:*)"
]
}
}
YSETTINGS
fi
}
cmd_project_init() { cmd_project_init() {
local name="" noninteractive=0 local name="" noninteractive=0
for a in "$@"; do case "$a" in -y|--yes|--no-wizard) noninteractive=1 ;; *) [ -z "$name" ] && name="$a" ;; esac; done for a in "$@"; do case "$a" in -y|--yes|--no-wizard) noninteractive=1 ;; *) [ -z "$name" ] && name="$a" ;; esac; done
@ -169,11 +221,14 @@ cmd_project_init() {
rm -rf "$tmp/s/.git" rm -rf "$tmp/s/.git"
cp -R "$tmp/s/." . cp -R "$tmp/s/." .
rm -rf "$tmp" rm -rf "$tmp"
# 스캐폴드는 '앱 + 매니페스트 + 얇은 CI'만 남긴다 엔진·스킬·명령은 전역(프로젝트에 미포함). # 스캐폴드는 '앱 + 매니페스트 + 얇은 CI + 프로젝트 가드레일'만 남긴다. 엔진·스킬·명령은 전역.
# bin·install.sh=CLI · .yakcloud=엔진(전역) · .claude=스킬·명령(전역) · README/DATA-SOURCES=스타터 문서. # bin·install.sh=CLI · .yakcloud=엔진(전역) · .claude/skills·commands=스킬·명령(전역) · README/DATA-SOURCES=스타터 문서.
rm -rf bin install.sh .yakcloud .claude README.md DATA-SOURCES.md # ⚠ .claude 는 통째로 지우지 않는다 — 프로젝트 로컬 .claude/settings.json(가드레일)은 남겨야 하므로 skills/commands 만 제거.
rm -rf bin install.sh .yakcloud .claude/skills .claude/commands README.md DATA-SOURCES.md
# 전역 엔진/스킬/명령 준비(최초 init 시에도 보장). # 전역 엔진/스킬/명령 준비(최초 init 시에도 보장).
install_global_assets 2>/dev/null || true install_global_assets 2>/dev/null || true
# 프로젝트에 yakcloud 강제 가드레일 심기(CLAUDE.md 지침 + docker-compose 차단) — 에이전트가 딴 길로 새지 않게.
_scaffold_yakcloud_guardrails 1
# 프로젝트명 = 지정 이름 or 현재 폴더명 → K8s/이미지 안전하게 정규화(소문자·영숫자·하이픈). # 프로젝트명 = 지정 이름 or 현재 폴더명 → K8s/이미지 안전하게 정규화(소문자·영숫자·하이픈).
local proj; proj="$(_sanitize_name "${name:-$(basename "$(pwd)")}")" local proj; proj="$(_sanitize_name "${name:-$(basename "$(pwd)")}")"
git rev-parse --git-dir >/dev/null 2>&1 || git init -q git rev-parse --git-dir >/dev/null 2>&1 || git init -q
@ -756,9 +811,12 @@ cmd_project_update() {
cp "$tmp/s/.gitea/workflows/deploy.yml" .gitea/workflows/ 2>/dev/null || true # 얇은 CI(전역 엔진 받아 실행) cp "$tmp/s/.gitea/workflows/deploy.yml" .gitea/workflows/ 2>/dev/null || true # 얇은 CI(전역 엔진 받아 실행)
rm -rf "$tmp" rm -rf "$tmp"
# 엔진·스킬·명령은 전역(프로젝트에 복제 안 함). 옛 per-project 잔재 제거. # 엔진·스킬·명령은 전역(프로젝트에 복제 안 함). 옛 per-project 잔재 제거.
rm -rf scripts .yakcloud .claude bin install.sh README.md DATA-SOURCES.md # ⚠ .claude 를 통째로 지우지 않는다 — 프로젝트 로컬 .claude/settings.json(가드레일) 보존. skills/commands 만 제거.
rm -rf scripts .yakcloud .claude/skills .claude/commands bin install.sh README.md DATA-SOURCES.md
install_global_assets # 전역 엔진(deploy/ctl/dev) + 스킬 + /yakcloud 명령 최신화 install_global_assets # 전역 엔진(deploy/ctl/dev) + 스킬 + /yakcloud 명령 최신화
echo "✓ 최신화 완료 — .gitea CI + 전역 엔진/스킬 갱신(app.py·yakcloud.yaml·Dockerfile 그대로)" # 가드레일 보장(없으면 생성, 있으면 사용자 편집 보존) — 옛 프로젝트도 update 시 yakcloud 강제 지침을 얻는다.
_scaffold_yakcloud_guardrails 0
echo "✓ 최신화 완료 — .gitea CI + 전역 엔진/스킬 갱신 + yakcloud 가드레일(app.py·yakcloud.yaml·Dockerfile 그대로)"
} }
usage() { usage() {