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:
70
bin/yakcloud
70
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.25.0"
|
||||
VERSION="0.26.0"
|
||||
# 로컬 개발 클러스터(kind) — 개발 후 운영까지 '직선 배포'의 dev 구간.
|
||||
DEV_CTX="${YAK_DEV_CONTEXT:-kind-yak-dev}"
|
||||
DEV_CLUSTER="${YAK_DEV_CLUSTER:-yak-dev}"
|
||||
@ -152,6 +152,58 @@ _sanitize_name() {
|
||||
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() {
|
||||
local name="" noninteractive=0
|
||||
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"
|
||||
cp -R "$tmp/s/." .
|
||||
rm -rf "$tmp"
|
||||
# 스캐폴드는 '앱 + 매니페스트 + 얇은 CI'만 남긴다 — 엔진·스킬·명령은 전역(프로젝트에 미포함).
|
||||
# bin·install.sh=CLI · .yakcloud=엔진(전역) · .claude=스킬·명령(전역) · README/DATA-SOURCES=스타터 문서.
|
||||
rm -rf bin install.sh .yakcloud .claude README.md DATA-SOURCES.md
|
||||
# 스캐폴드는 '앱 + 매니페스트 + 얇은 CI + 프로젝트 가드레일'만 남긴다. 엔진·스킬·명령은 전역.
|
||||
# bin·install.sh=CLI · .yakcloud=엔진(전역) · .claude/skills·commands=스킬·명령(전역) · README/DATA-SOURCES=스타터 문서.
|
||||
# ⚠ .claude 는 통째로 지우지 않는다 — 프로젝트 로컬 .claude/settings.json(가드레일)은 남겨야 하므로 skills/commands 만 제거.
|
||||
rm -rf bin install.sh .yakcloud .claude/skills .claude/commands README.md DATA-SOURCES.md
|
||||
# 전역 엔진/스킬/명령 준비(최초 init 시에도 보장).
|
||||
install_global_assets 2>/dev/null || true
|
||||
# 프로젝트에 yakcloud 강제 가드레일 심기(CLAUDE.md 지침 + docker-compose 차단) — 에이전트가 딴 길로 새지 않게.
|
||||
_scaffold_yakcloud_guardrails 1
|
||||
# 프로젝트명 = 지정 이름 or 현재 폴더명 → K8s/이미지 안전하게 정규화(소문자·영숫자·하이픈).
|
||||
local proj; proj="$(_sanitize_name "${name:-$(basename "$(pwd)")}")"
|
||||
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(전역 엔진 받아 실행)
|
||||
rm -rf "$tmp"
|
||||
# 엔진·스킬·명령은 전역(프로젝트에 복제 안 함). 옛 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 명령 최신화
|
||||
echo "✓ 최신화 완료 — .gitea CI + 전역 엔진/스킬 갱신(app.py·yakcloud.yaml·Dockerfile 그대로)"
|
||||
# 가드레일 보장(없으면 생성, 있으면 사용자 편집 보존) — 옛 프로젝트도 update 시 yakcloud 강제 지침을 얻는다.
|
||||
_scaffold_yakcloud_guardrails 0
|
||||
echo "✓ 최신화 완료 — .gitea CI + 전역 엔진/스킬 갱신 + yakcloud 가드레일(app.py·yakcloud.yaml·Dockerfile 그대로)"
|
||||
}
|
||||
|
||||
usage() {
|
||||
|
||||
Reference in New Issue
Block a user