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:
@ -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 로 그대로 실행하고 출력만 보여준다.**
|
||||||
사전점검(설치/폴더/설정 확인)·결과 재검증·부연설명을 붙이지 말 것 — 터미널에서 직접 친 것과 동일하게 동작.
|
사전점검(설치/폴더/설정 확인)·결과 재검증·부연설명을 붙이지 말 것 — 터미널에서 직접 친 것과 동일하게 동작.
|
||||||
|
|||||||
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}"
|
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() {
|
||||||
|
|||||||
Reference in New Issue
Block a user