diff --git a/.forgejo/workflows/ci.yml b/.forgejo/workflows/ci.yml index 678979e..6dcbade 100644 --- a/.forgejo/workflows/ci.yml +++ b/.forgejo/workflows/ci.yml @@ -76,14 +76,15 @@ jobs: # 설치 -> 실행 -> 제거 왕복 sandbox="$(mktemp -d)" + state_dir="$sandbox/.designpaca-state" cd "$sandbox" npm init -y >/dev/null 2>&1 npm i --no-audit --no-fund --silent "$tarball" - npx --no-install designpaca --version - npx --no-install designpaca install --target claude-code --scope project --yes + env DESIGNPACA_STATE_DIR="$state_dir" npx --no-install designpaca --version + env DESIGNPACA_STATE_DIR="$state_dir" npx --no-install designpaca install --target claude-code --scope project --yes test -f .claude/skills/designpaca/SKILL.md || { echo "설치 후 스킬이 없다" >&2; exit 1; } - npx --no-install designpaca doctor - npx --no-install designpaca uninstall --yes + env DESIGNPACA_STATE_DIR="$state_dir" npx --no-install designpaca doctor + env DESIGNPACA_STATE_DIR="$state_dir" npx --no-install designpaca uninstall --yes test ! -f .claude/skills/designpaca/SKILL.md || { echo "제거 후에도 파일이 남았다" >&2; exit 1; } cd / && rm -rf "$sandbox" "$tarball" diff --git a/apps/site/tools/__shots__/current/gaon-lms-dashboard-1440.png b/apps/site/tools/__shots__/current/gaon-lms-dashboard-1440.png index 81d2548..3f8593d 100644 Binary files a/apps/site/tools/__shots__/current/gaon-lms-dashboard-1440.png and b/apps/site/tools/__shots__/current/gaon-lms-dashboard-1440.png differ diff --git a/apps/site/tools/__shots__/current/gaon-lms-dashboard-390.png b/apps/site/tools/__shots__/current/gaon-lms-dashboard-390.png index b3e8060..7e03748 100644 Binary files a/apps/site/tools/__shots__/current/gaon-lms-dashboard-390.png and b/apps/site/tools/__shots__/current/gaon-lms-dashboard-390.png differ diff --git a/apps/site/tools/e2e-report.md b/apps/site/tools/e2e-report.md index 0effc59..2afdb52 100644 --- a/apps/site/tools/e2e-report.md +++ b/apps/site/tools/e2e-report.md @@ -1,4 +1,4 @@ -# E2E 시나리오 게이트 — 2026-08-24 03:41 +# E2E 시나리오 게이트 — 2026-08-24 05:13 단언 1805 · 통과 1805 · 실패 0 — **GREEN — 전 단언 통과** diff --git a/apps/site/tools/verify-report.md b/apps/site/tools/verify-report.md index 953ba60..2e12f91 100644 --- a/apps/site/tools/verify-report.md +++ b/apps/site/tools/verify-report.md @@ -1,16 +1,16 @@ -# 검증 리포트 — 2026-08-24 03:41 +# 검증 리포트 — 2026-08-24 05:13 -게이트: **전 계층 통과 — 배포 가능** · 총 96.1초 +게이트: **전 계층 통과 — 배포 가능** · 총 103.0초 | 계층 | 결과 | 항목 | 소요 | |---|---|---|---| -| L0-정적/stylelint | PASS | PASS — | 1.5s | -| L0-정식/html-validate | PASS | PASS — | 1.5s | +| L0-정적/stylelint | PASS | PASS — | 2.7s | +| L0-정식/html-validate | PASS | PASS — | 2.8s | | L0-무결성/변수·경로·조판 | PASS | PASS 1 | 0.1s | | L1-단위/계산로직 | PASS | PASS 41 | 0.0s | -| L2-기능/감사 | PASS | PASS 72 | 9.9s | -| L2-불변식/렌더 | PASS | PASS 10 | 17.2s | -| L3-시각회귀/diff | PASS | PASS 30 | 12.2s | -| L4-WebKit | PASS | PASS 8 | 3.7s | -| L5-탐색/키보드 | PASS | PASS 14 | 6.6s | -| L6-시나리오/E2E | PASS | PASS — | 43.3s | +| L2-기능/감사 | PASS | PASS 72 | 9.8s | +| L2-불변식/렌더 | PASS | PASS 10 | 16.7s | +| L3-시각회귀/diff | PASS | PASS 30 | 18.7s | +| L4-WebKit | PASS | PASS 8 | 3.8s | +| L5-탐색/키보드 | PASS | PASS 14 | 6.0s | +| L6-시나리오/E2E | PASS | PASS — | 42.3s | diff --git a/packages/core/src/paths.ts b/packages/core/src/paths.ts index 3476e58..c66fe6f 100644 --- a/packages/core/src/paths.ts +++ b/packages/core/src/paths.ts @@ -1,17 +1,27 @@ import os from "node:os"; import path from "node:path"; -/** designpaca 자체 상태 디렉터리 (~/.designpaca) */ -export function stateDir(home = os.homedir()): string { - return path.join(home, ".designpaca"); +/** + * CI·테스트처럼 실제 사용자 설치 기록과 분리해야 하는 실행은 전용 상태 디렉터리를 쓴다. + * 사용자 설치 경로의 기준 홈은 바꾸지 않고, designpaca 자체 상태만 격리한다. + */ +function isolatedStateDir(): string | undefined { + const dir = process.env["DESIGNPACA_STATE_DIR"]?.trim(); + return dir ? path.resolve(dir) : undefined; } -export function manifestPath(home = os.homedir()): string { +/** designpaca 자체 상태 디렉터리 (~/.designpaca) */ +export function stateDir(home?: string): string { + if (home) return path.join(home, ".designpaca"); + return isolatedStateDir() ?? path.join(os.homedir(), ".designpaca"); +} + +export function manifestPath(home?: string): string { return path.join(stateDir(home), "manifest.json"); } /** 업데이트 확인 캐시 */ -export function updateCachePath(home = os.homedir()): string { +export function updateCachePath(home?: string): string { return path.join(stateDir(home), "update-check.json"); } diff --git a/packages/core/test/paths.test.ts b/packages/core/test/paths.test.ts new file mode 100644 index 0000000..1b2391f --- /dev/null +++ b/packages/core/test/paths.test.ts @@ -0,0 +1,23 @@ +import assert from "node:assert/strict"; +import os from "node:os"; +import path from "node:path"; +import { test } from "node:test"; +import { manifestPath, stateDir, updateCachePath } from "../src/paths.ts"; + +test("DESIGNPACA_STATE_DIR 은 사용자 홈을 바꾸지 않고 상태 파일만 격리한다", () => { + const before = process.env["DESIGNPACA_STATE_DIR"]; + const isolated = path.join(os.tmpdir(), "designpaca-isolated-state"); + + try { + process.env["DESIGNPACA_STATE_DIR"] = isolated; + assert.equal(stateDir(), path.resolve(isolated)); + assert.equal(manifestPath(), path.join(path.resolve(isolated), "manifest.json")); + assert.equal(updateCachePath(), path.join(path.resolve(isolated), "update-check.json")); + + const explicitHome = path.join(os.tmpdir(), "designpaca-explicit-home"); + assert.equal(stateDir(explicitHome), path.join(explicitHome, ".designpaca")); + } finally { + if (before === undefined) delete process.env["DESIGNPACA_STATE_DIR"]; + else process.env["DESIGNPACA_STATE_DIR"] = before; + } +});