spec-runner 1.0.7 → 1.0.8
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +16 -9
- package/bin/spec-runner.js +112 -144
- package/package.json +1 -6
- package/templates/.spec-runner/hooks/pre-commit +25 -11
- package/templates/.spec-runner/project.json.example +10 -8
- package/templates/.spec-runner/scripts/branch/uc-next-start.sh +100 -9
- package/templates/.spec-runner/scripts/check.sh +396 -13
- package/templates/.spec-runner/scripts/spec-runner-core.sh +286 -157
- package/templates/.spec-runner/scripts/test/require-tests-green.sh +7 -63
- package/templates/.spec-runner/steps/steps.json +171 -0
- package/templates/.spec-runner/steps//343/201/235/343/201/256/344/273/226/344/275/234/346/245/255.md +25 -13
- package/templates/.spec-runner/steps//343/202/277/343/202/271/343/202/257/344/270/200/350/246/247.md +67 -104
- package/templates/.spec-runner/steps//343/203/201/343/202/247/343/203/203/343/202/257/343/203/252/343/202/271/343/203/210.md +52 -78
- package/templates/.spec-runner/steps//343/203/206/343/202/271/343/203/210/350/250/255/350/250/210.md +41 -34
- package/templates/.spec-runner/steps//343/203/211/343/203/241/343/202/244/343/203/263/350/250/255/350/250/210.md +34 -14
- package/templates/.spec-runner/steps//344/273/225/346/247/230/347/255/226/345/256/232.md +161 -207
- package/templates/.spec-runner/steps//345/210/206/346/236/220.md +64 -127
- package/templates/.spec-runner/steps//345/256/237/350/243/205.md +67 -79
- package/templates/.spec-runner/steps//345/256/237/350/243/205/350/250/210/347/224/273.md +56 -56
- package/templates/.spec-runner/steps//346/206/262/347/253/240.md +67 -46
- package/templates/.spec-runner/steps//346/233/226/346/230/247/343/201/225/350/247/243/346/266/210.md +88 -148
- package/templates/.spec-runner/templates/UC-N-MMDD-/345/210/244/346/226/255/350/250/230/351/214/262/343/203/206/343/203/263/343/203/227/343/203/254.md +33 -0
- package/templates/.spec-runner/templates/{UC-NNN- → UC-N-}/343/203/246/343/203/274/343/202/271/343/202/261/343/203/274/343/202/271/345/220/215.md +1 -3
- package/templates/.spec-runner/templates/grade-history.json +5 -0
- package/templates/.spec-runner/templates/phase-locks.json +29 -0
- package/templates/.spec-runner/templates//343/203/211/343/203/241/343/202/244/343/203/263/343/203/242/343/203/207/343/203/253.md +21 -0
- package/templates/.spec-runner/templates//343/203/246/343/203/223/343/202/255/343/202/277/343/202/271/350/250/200/350/252/236/350/276/236/346/233/270.md +16 -0
- package/templates/.spec-runner/templates//346/206/262/347/253/240.md +51 -0
- package/templates/.spec-runner/templates//351/233/206/347/264/204.md +46 -0
- package/templates/.spec-runner/scripts/branch/create-uc-branch.sh +0 -105
- package/templates/.spec-runner/scripts/branch/uc-next-id.sh +0 -17
- package/templates/.spec-runner/scripts/check/drift.sh +0 -66
- package/templates/.spec-runner/scripts/check/health.sh +0 -103
- package/templates/.spec-runner/scripts/check/naming.sh +0 -51
- package/templates/.spec-runner/scripts/check/schema-drift.sh +0 -74
- package/templates/.spec-runner/scripts/check/schema-sync.sh +0 -153
- package/templates/.spec-runner/scripts/lib/uc-context.sh +0 -75
- package/templates/.spec-runner/scripts/openapi/openapi-generate.sh +0 -207
- package/templates/.spec-runner/scripts/setup/init-project.sh +0 -152
|
@@ -1,105 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env bash
|
|
2
|
-
# 新規 UC 用ブランチ作成(Section 8-1)
|
|
3
|
-
# 使用例: ./create-uc-branch.sh UC-001 order-placement [カテゴリ] [--json]
|
|
4
|
-
# ブランチ名: feature/UC-001-order-placement。UC 仕様書: docs/05_ユースケース仕様/<カテゴリ>/UC-001-order-placement.md
|
|
5
|
-
|
|
6
|
-
set -e
|
|
7
|
-
REPO_ROOT="${REPO_ROOT:-$(git rev-parse --show-toplevel 2>/dev/null || echo ".")}"
|
|
8
|
-
cd "$REPO_ROOT"
|
|
9
|
-
|
|
10
|
-
JSON_MODE=false
|
|
11
|
-
ARGS=()
|
|
12
|
-
for a in "$@"; do
|
|
13
|
-
case "$a" in
|
|
14
|
-
--json) JSON_MODE=true ;;
|
|
15
|
-
*) ARGS+=("$a") ;;
|
|
16
|
-
esac
|
|
17
|
-
done
|
|
18
|
-
|
|
19
|
-
# 引数が 1 つ(説明のみ)のときは uc-next-id で UC 番号を取得
|
|
20
|
-
if [[ ${#ARGS[@]} -eq 1 ]]; then
|
|
21
|
-
UC_ID=$("$(dirname "$0")/uc-next-id.sh" 2>/dev/null || true)
|
|
22
|
-
[[ -z "$UC_ID" ]] && { echo "Error: uc-next-id の取得に失敗しました" >&2; exit 1; }
|
|
23
|
-
ARGS=("$UC_ID" "${ARGS[0]}")
|
|
24
|
-
fi
|
|
25
|
-
|
|
26
|
-
UC_ID="${ARGS[0]:-}"
|
|
27
|
-
DESC="${ARGS[1]:-}"
|
|
28
|
-
if [[ -z "$UC_ID" || -z "$DESC" ]]; then
|
|
29
|
-
echo "Usage: $0 <UC-ID> <kebab-description> [カテゴリ] [--json]" >&2
|
|
30
|
-
echo " $0 <kebab-description> # UC 番号は自動採番" >&2
|
|
31
|
-
echo "Example: $0 UC-001 order-placement" >&2
|
|
32
|
-
echo "Example: $0 order-placement" >&2
|
|
33
|
-
exit 1
|
|
34
|
-
fi
|
|
35
|
-
# 第3引数: カテゴリ(省略時はデフォルト)。--json の場合はカテゴリ未指定
|
|
36
|
-
CATEGORY="${ARGS[2]:-}"
|
|
37
|
-
[[ "${CATEGORY}" == "--json" ]] && CATEGORY=""
|
|
38
|
-
# カテゴリ未指定時はデフォルト(英数字・ハイフンのみでサニタイズ)
|
|
39
|
-
CATEGORY="${CATEGORY:-ユースケース}"
|
|
40
|
-
CATEGORY=$(echo "$CATEGORY" | sed 's/[^a-zA-Z0-9_ーぁ-んァ-ン一-龥\-]//g')
|
|
41
|
-
[[ -z "$CATEGORY" ]] && CATEGORY="ユースケース"
|
|
42
|
-
|
|
43
|
-
# UC-ID 形式: UC-NNN
|
|
44
|
-
if ! echo "$UC_ID" | grep -qE '^UC-[0-9]{3}$'; then
|
|
45
|
-
echo "Error: UC-ID must be UC-NNN (e.g. UC-001)" >&2
|
|
46
|
-
exit 1
|
|
47
|
-
fi
|
|
48
|
-
|
|
49
|
-
# 説明は英小文字・ハイフン
|
|
50
|
-
DESC=$(echo "$DESC" | tr '[:upper:]' '[:lower:]' | sed 's/[^a-z0-9]/-/g' | sed 's/-\+/-/g' | sed 's/^-//' | sed 's/-$//')
|
|
51
|
-
# ブランチ接頭辞は project.json の naming.branch_prefix(初期化で設定)。無ければ feature
|
|
52
|
-
BRANCH_PREFIX="feature"
|
|
53
|
-
if [[ -f ".spec-runner/project.json" ]] && command -v jq >/dev/null 2>&1; then
|
|
54
|
-
p=$(jq -r '.naming.branch_prefix // empty' .spec-runner/project.json 2>/dev/null)
|
|
55
|
-
[[ -n "$p" ]] && BRANCH_PREFIX="$p"
|
|
56
|
-
fi
|
|
57
|
-
BRANCH_NAME="${BRANCH_PREFIX}/${UC_ID}-${DESC}"
|
|
58
|
-
|
|
59
|
-
# 命名規則チェック(project.json の branch_prefix + UC 形式)。AI 実行前に検証。
|
|
60
|
-
valid_uc_pattern="^${BRANCH_PREFIX}/UC-[0-9]{3}-[a-z0-9-]+\$"
|
|
61
|
-
if ! echo "$BRANCH_NAME" | grep -qE "$valid_uc_pattern"; then
|
|
62
|
-
echo "Error: ブランチ名が命名規則に合いません: $BRANCH_NAME(期待: ${BRANCH_PREFIX}/UC-NNN-kebab-description)" >&2
|
|
63
|
-
exit 1
|
|
64
|
-
fi
|
|
65
|
-
|
|
66
|
-
if git rev-parse --verify "$BRANCH_NAME" >/dev/null 2>&1; then
|
|
67
|
-
echo "Error: Branch '$BRANCH_NAME' already exists." >&2
|
|
68
|
-
exit 1
|
|
69
|
-
fi
|
|
70
|
-
|
|
71
|
-
git checkout -b "$BRANCH_NAME"
|
|
72
|
-
echo "Created branch: $BRANCH_NAME"
|
|
73
|
-
|
|
74
|
-
# UC 仕様書を docs/05_ユースケース仕様/<カテゴリ>/UC-NNN-xxx.md に作成(1 UC = 1 ファイル)
|
|
75
|
-
FEATURE_DIR="docs/05_ユースケース仕様/${CATEGORY}"
|
|
76
|
-
UC_DOC="${FEATURE_DIR}/${UC_ID}-${DESC}.md"
|
|
77
|
-
mkdir -p "$FEATURE_DIR"
|
|
78
|
-
TEMPLATE="$REPO_ROOT/.spec-runner/templates/UC-NNN-ユースケース名.md"
|
|
79
|
-
if [[ -f "$TEMPLATE" ]]; then
|
|
80
|
-
sed "s/UC-NNN/${UC_ID}/g; s/{ユースケース名}/${DESC}/g" "$TEMPLATE" > "$UC_DOC"
|
|
81
|
-
echo "Created: $UC_DOC"
|
|
82
|
-
else
|
|
83
|
-
touch "$UC_DOC"
|
|
84
|
-
echo "# ${UC_ID}: ${DESC}" >> "$UC_DOC"
|
|
85
|
-
echo "status: draft" >> "$UC_DOC"
|
|
86
|
-
echo "Created: $UC_DOC"
|
|
87
|
-
fi
|
|
88
|
-
|
|
89
|
-
if [[ "$JSON_MODE" == true ]]; then
|
|
90
|
-
SPEC_ABS="$REPO_ROOT/$UC_DOC"
|
|
91
|
-
DIR_ABS="$REPO_ROOT/$FEATURE_DIR"
|
|
92
|
-
if command -v jq >/dev/null 2>&1; then
|
|
93
|
-
jq -cn \
|
|
94
|
-
--arg branch "$BRANCH_NAME" \
|
|
95
|
-
--arg spec_file "$SPEC_ABS" \
|
|
96
|
-
--arg feature_dir "$DIR_ABS" \
|
|
97
|
-
'{BRANCH_NAME:$branch,SPEC_FILE:$spec_file,FEATURE_SPEC:$spec_file,FEATURE_DIR:$feature_dir}'
|
|
98
|
-
else
|
|
99
|
-
printf '{"BRANCH_NAME":"%s","SPEC_FILE":"%s","FEATURE_SPEC":"%s","FEATURE_DIR":"%s"}\n' \
|
|
100
|
-
"$(printf '%s' "$BRANCH_NAME" | sed 's/\\/\\\\/g; s/"/\\"/g')" \
|
|
101
|
-
"$(printf '%s' "$SPEC_ABS" | sed 's/\\/\\\\/g; s/"/\\"/g')" \
|
|
102
|
-
"$(printf '%s' "$SPEC_ABS" | sed 's/\\/\\\\/g; s/"/\\"/g')" \
|
|
103
|
-
"$(printf '%s' "$DIR_ABS" | sed 's/\\/\\\\/g; s/"/\\"/g')"
|
|
104
|
-
fi
|
|
105
|
-
fi
|
|
@@ -1,17 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env bash
|
|
2
|
-
# docs/05_ユースケース仕様/<カテゴリ>/UC-*.md から次に使う UC-NNN を表示する(例: UC-001)
|
|
3
|
-
|
|
4
|
-
set -e
|
|
5
|
-
REPO_ROOT="${REPO_ROOT:-$(git rev-parse --show-toplevel 2>/dev/null || echo ".")}"
|
|
6
|
-
DIR="$REPO_ROOT/docs/05_ユースケース仕様"
|
|
7
|
-
mkdir -p "$DIR"
|
|
8
|
-
MAX=0
|
|
9
|
-
for f in "$DIR"/*/UC-*.md; do
|
|
10
|
-
[[ -e "$f" ]] || continue
|
|
11
|
-
base=$(basename "$f" .md)
|
|
12
|
-
if [[ "$base" =~ ^UC-([0-9]{3})- ]]; then
|
|
13
|
-
n=$((10#${BASH_REMATCH[1]}))
|
|
14
|
-
[[ $n -gt $MAX ]] && MAX=$n
|
|
15
|
-
fi
|
|
16
|
-
done
|
|
17
|
-
printf "UC-%03d\n" $((MAX + 1))
|
|
@@ -1,66 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env bash
|
|
2
|
-
# ドリフト確認(Section 10: 仕様とコードの乖離検出)
|
|
3
|
-
# オプション: --用語(禁止語), --スキーマ(集約↔DBML), --命名(命名規則), --レポート(レポート出力)
|
|
4
|
-
|
|
5
|
-
set -e
|
|
6
|
-
REPO_ROOT="${REPO_ROOT:-$(git rev-parse --show-toplevel 2>/dev/null || echo ".")}"
|
|
7
|
-
cd "$REPO_ROOT"
|
|
8
|
-
|
|
9
|
-
MODE=""
|
|
10
|
-
for a in "$@"; do
|
|
11
|
-
case "$a" in
|
|
12
|
-
--用語) MODE="terms" ;;
|
|
13
|
-
--スキーマ) MODE="schema" ;;
|
|
14
|
-
--命名) MODE="naming" ;;
|
|
15
|
-
--レポート) MODE="report" ;;
|
|
16
|
-
esac
|
|
17
|
-
done
|
|
18
|
-
|
|
19
|
-
if [[ "$MODE" == "naming" ]]; then
|
|
20
|
-
exec "$(dirname "$0")/naming.sh"
|
|
21
|
-
fi
|
|
22
|
-
|
|
23
|
-
if [[ "$MODE" == "terms" ]]; then
|
|
24
|
-
# 禁止語チェック: ユビキタス言語辞書の forbidden を src/ で検索(命名規則.md の SOURCE_EXTENSIONS を参照)
|
|
25
|
-
DICT="docs/02_ドメイン設計/ユビキタス言語辞書.md"
|
|
26
|
-
NAMING="docs/03_アーキテクチャ/命名規則.md"
|
|
27
|
-
if [[ ! -f "$DICT" ]]; then
|
|
28
|
-
echo "ドリフト確認(用語): ユビキタス言語辞書がありません。スキップします。"
|
|
29
|
-
exit 0
|
|
30
|
-
fi
|
|
31
|
-
source_exts="ts,php,py,go,java"
|
|
32
|
-
if [[ -f "$NAMING" ]]; then
|
|
33
|
-
line=$(grep -E '^SOURCE_EXTENSIONS:' "$NAMING" 2>/dev/null | head -1)
|
|
34
|
-
[[ -n "$line" ]] && source_exts=$(echo "$line" | sed 's/SOURCE_EXTENSIONS:[[:space:]]*//;s/[[:space:]]//g')
|
|
35
|
-
fi
|
|
36
|
-
found=0
|
|
37
|
-
if command -v yq >/dev/null 2>&1; then
|
|
38
|
-
while IFS= read -r -d '' forbidden; do
|
|
39
|
-
[[ -z "$forbidden" ]] && continue
|
|
40
|
-
c=0
|
|
41
|
-
if [[ -d "src" ]]; then
|
|
42
|
-
for ext in $(echo "$source_exts" | tr ',' ' '); do
|
|
43
|
-
n=$(find src -type f -name "*.$ext" 2>/dev/null | xargs grep -l "$forbidden" 2>/dev/null | wc -l)
|
|
44
|
-
c=$((c + n))
|
|
45
|
-
done
|
|
46
|
-
fi
|
|
47
|
-
if [[ "$c" -gt 0 ]]; then
|
|
48
|
-
echo "禁止語「$forbidden」が src/ に ${c} 件あります。"
|
|
49
|
-
found=$((found+1))
|
|
50
|
-
fi
|
|
51
|
-
done < <(yq '.terms[]? | select(.forbidden != null) | .forbidden[]?' "$DICT" -r -0 2>/dev/null || true)
|
|
52
|
-
fi
|
|
53
|
-
if [[ $found -gt 0 ]]; then
|
|
54
|
-
exit 1
|
|
55
|
-
fi
|
|
56
|
-
echo "✅ 禁止語チェック: 問題なし"
|
|
57
|
-
exit 0
|
|
58
|
-
fi
|
|
59
|
-
|
|
60
|
-
if [[ "$MODE" == "schema" ]]; then
|
|
61
|
-
exec "$(dirname "$0")/schema-drift.sh"
|
|
62
|
-
fi
|
|
63
|
-
|
|
64
|
-
# デフォルト: オプション未指定時はメッセージのみ
|
|
65
|
-
echo "✅ ドリフト確認: 完了(--用語 / --スキーマ / --命名 で個別チェック、健全性確認で一括チェック)"
|
|
66
|
-
exit 0
|
|
@@ -1,103 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env bash
|
|
2
|
-
# 健全性確認(spec.md セクション 12)。--形式=json で JSON を標準出力に出す。
|
|
3
|
-
|
|
4
|
-
set -e
|
|
5
|
-
REPO_ROOT="${REPO_ROOT:-$(git rev-parse --show-toplevel 2>/dev/null || echo ".")}"
|
|
6
|
-
cd "$REPO_ROOT"
|
|
7
|
-
|
|
8
|
-
drifts=()
|
|
9
|
-
|
|
10
|
-
# 設計書品質
|
|
11
|
-
for f in docs/05_ユースケース仕様/*/UC-*.md; do
|
|
12
|
-
[[ -f "$f" ]] || continue
|
|
13
|
-
base=$(basename "$f" .md)
|
|
14
|
-
if ! grep -qE '受入条件|成功基準|Given|When|Then|EARS' "$f" 2>/dev/null; then
|
|
15
|
-
drifts+=("UC ${base}: 受入条件または成功基準がありません")
|
|
16
|
-
fi
|
|
17
|
-
count=$(grep -c '\[要確認:' "$f" 2>/dev/null || echo 0)
|
|
18
|
-
count=$(echo "$count" | head -1 | tr -cd '0-9'); count=${count:-0}
|
|
19
|
-
if [[ "$count" -gt 3 ]]; then
|
|
20
|
-
drifts+=("UC ${base}: [要確認: が ${count} 個(3個以下にすること)")
|
|
21
|
-
fi
|
|
22
|
-
# 実装方針・タスクは UC の .md の一番下に記載する。
|
|
23
|
-
if ! grep -qE '^## 実装方針' "$f" 2>/dev/null; then
|
|
24
|
-
drifts+=("UC ${base}: 「## 実装方針」の見出しがありません(UC 仕様書の一番下に記載すること)")
|
|
25
|
-
fi
|
|
26
|
-
if ! grep -qE '^## タスク一覧|^## タスク\b' "$f" 2>/dev/null; then
|
|
27
|
-
drifts+=("UC ${base}: 「## タスク」または「## タスク一覧」の見出しがありません(UC 仕様書の一番下に記載すること)")
|
|
28
|
-
fi
|
|
29
|
-
done
|
|
30
|
-
|
|
31
|
-
adr_count=$(find docs/03_アーキテクチャ/設計判断記録 -name "*.md" 2>/dev/null | wc -l)
|
|
32
|
-
[[ "${adr_count:-0}" -lt 1 ]] && drifts+=("ADR が 1 件もありません(設計判断記録)")
|
|
33
|
-
|
|
34
|
-
if [[ ! -f "docs/02_ドメイン設計/ユビキタス言語辞書.md" ]]; then
|
|
35
|
-
drifts+=("ユビキタス言語辞書.md が存在しません")
|
|
36
|
-
else
|
|
37
|
-
if ! grep -qE '禁止語|forbidden' "docs/02_ドメイン設計/ユビキタス言語辞書.md" 2>/dev/null; then
|
|
38
|
-
drifts+=("ユビキタス言語辞書に禁止語欄が定義されていません")
|
|
39
|
-
fi
|
|
40
|
-
fi
|
|
41
|
-
|
|
42
|
-
if [[ -f "docs/02_ドメイン設計/集約.md" ]]; then
|
|
43
|
-
if ! grep -q '対応テーブル' "docs/02_ドメイン設計/集約.md" 2>/dev/null; then
|
|
44
|
-
drifts+=("集約.md に「対応テーブル」欄がありません")
|
|
45
|
-
fi
|
|
46
|
-
fi
|
|
47
|
-
|
|
48
|
-
if [[ -f ".spec-runner/grade-history.json" ]] && command -v jq >/dev/null 2>&1; then
|
|
49
|
-
grade=$(jq -r '.current_grade // ""' .spec-runner/grade-history.json 2>/dev/null)
|
|
50
|
-
if [[ "$grade" == "A" ]]; then
|
|
51
|
-
[[ ! -f "docs/04_インフラ設計/schema.dbml" ]] && drifts+=("Grade A 必須: schema.dbml が存在しません")
|
|
52
|
-
# ドキュメントと Prisma / マイグレーション結果の一致
|
|
53
|
-
if [[ -f "$(dirname "$0")/schema-sync.sh" ]]; then
|
|
54
|
-
"$(dirname "$0")/schema-sync.sh" >/dev/null 2>&1 || drifts+=("Prisma と schema.dbml のテーブルが一致していません(spec-runner 健全性確認 または .spec-runner/scripts/check/schema-sync.sh で詳細確認)")
|
|
55
|
-
fi
|
|
56
|
-
# schema-drift(禁止語・集約参照・必須カラム)は任意。必要なら spec-runner ドリフト確認 --スキーマ を実行
|
|
57
|
-
fi
|
|
58
|
-
fi
|
|
59
|
-
|
|
60
|
-
[[ ! -f "docs/06_API仕様/openapi.yaml" ]] && drifts+=("openapi.yaml が存在しません")
|
|
61
|
-
|
|
62
|
-
# プロセス品質
|
|
63
|
-
branch=$(git branch --show-current 2>/dev/null || echo "")
|
|
64
|
-
bp="feature"
|
|
65
|
-
other_work="work/.+|infra/.+|cicd/.+"
|
|
66
|
-
if [[ -f "$REPO_ROOT/.spec-runner/project.json" ]] && command -v jq >/dev/null 2>&1; then
|
|
67
|
-
p=$(jq -r '.naming.branch_prefix // empty' "$REPO_ROOT/.spec-runner/project.json" 2>/dev/null); [[ -n "$p" ]] && bp="$p"
|
|
68
|
-
ow=$(jq -r '.naming.other_work_prefixes[]? | . + "/.+"' "$REPO_ROOT/.spec-runner/project.json" 2>/dev/null | tr '\n' '|' | sed 's/|$//'); [[ -n "$ow" ]] && other_work="$ow"
|
|
69
|
-
fi
|
|
70
|
-
valid_branch="^(main|develop|${bp}/(UC-[0-9]{3}-.+|${other_work})|fix/UC-[0-9]{3}-.+|release/[0-9]+\.[0-9]+\.[0-9]+.*|hotfix/[0-9]+\.[0-9]+\.[0-9]+-.+)\$"
|
|
71
|
-
if [[ -n "$branch" ]] && ! echo "$branch" | grep -qE "$valid_branch"; then
|
|
72
|
-
drifts+=("ブランチ名が規則違反: $branch")
|
|
73
|
-
fi
|
|
74
|
-
|
|
75
|
-
# JSON 出力(jq がなければ手組み)
|
|
76
|
-
build_json() {
|
|
77
|
-
local i
|
|
78
|
-
echo -n '{"drifts":['
|
|
79
|
-
for i in "${!drifts[@]}"; do
|
|
80
|
-
[[ $i -gt 0 ]] && echo -n ','
|
|
81
|
-
# 簡易エスケープ
|
|
82
|
-
s="${drifts[$i]}"
|
|
83
|
-
s="${s//\\/\\\\}"
|
|
84
|
-
s="${s//\"/\\\"}"
|
|
85
|
-
echo -n "\"$s\""
|
|
86
|
-
done
|
|
87
|
-
echo -n '],"phase":"ok","grade":"ok"}'
|
|
88
|
-
}
|
|
89
|
-
|
|
90
|
-
if [[ "$*" == *"--形式=json"* ]] || [[ "$*" == *"--形式"="json"* ]]; then
|
|
91
|
-
build_json
|
|
92
|
-
[[ ${#drifts[@]} -eq 0 ]] && exit 0 || exit 1
|
|
93
|
-
fi
|
|
94
|
-
|
|
95
|
-
# テキスト出力
|
|
96
|
-
if [[ ${#drifts[@]} -eq 0 ]]; then
|
|
97
|
-
echo "健全性確認: 問題なし"
|
|
98
|
-
exit 0
|
|
99
|
-
else
|
|
100
|
-
echo "健全性確認: ${#drifts[@]} 件の指摘" >&2
|
|
101
|
-
for d in "${drifts[@]}"; do echo " - $d" >&2; done
|
|
102
|
-
exit 1
|
|
103
|
-
fi
|
|
@@ -1,51 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env bash
|
|
2
|
-
# 命名規則チェック(Section 8-3 簡易版)
|
|
3
|
-
# ブランチ名・フォルダ名・禁止パターンを検証する。
|
|
4
|
-
|
|
5
|
-
set -e
|
|
6
|
-
REPO_ROOT="${REPO_ROOT:-$(git rev-parse --show-toplevel 2>/dev/null || echo ".")}"
|
|
7
|
-
cd "$REPO_ROOT"
|
|
8
|
-
errors=0
|
|
9
|
-
|
|
10
|
-
# ① ブランチ名チェック
|
|
11
|
-
check_branch_name() {
|
|
12
|
-
local branch
|
|
13
|
-
branch=$(git branch --show-current 2>/dev/null || echo "nobranch")
|
|
14
|
-
local bp="feature"
|
|
15
|
-
local other_work="work/.+|infra/.+|cicd/.+"
|
|
16
|
-
if [[ -f "$REPO_ROOT/.spec-runner/project.json" ]] && command -v jq >/dev/null 2>&1; then
|
|
17
|
-
p=$(jq -r '.naming.branch_prefix // empty' "$REPO_ROOT/.spec-runner/project.json" 2>/dev/null); [[ -n "$p" ]] && bp="$p"
|
|
18
|
-
ow=$(jq -r '.naming.other_work_prefixes[]? | . + "/.+"' "$REPO_ROOT/.spec-runner/project.json" 2>/dev/null | tr '\n' '|' | sed 's/|$//'); [[ -n "$ow" ]] && other_work="$ow"
|
|
19
|
-
fi
|
|
20
|
-
local valid="^(main|develop|${bp}/(UC-[0-9]{3}-.+|${other_work})|fix/UC-[0-9]{3}-.+|release/[0-9]+\.[0-9]+\.[0-9]+.*|hotfix/[0-9]+\.[0-9]+\.[0-9]+-.+)$"
|
|
21
|
-
if [[ "$branch" != "nobranch" ]] && ! echo "$branch" | grep -qE "$valid"; then
|
|
22
|
-
echo "NAMING: ブランチ名「$branch」が規則違反" >&2
|
|
23
|
-
return 1
|
|
24
|
-
fi
|
|
25
|
-
return 0
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
# ② フォルダ名チェック(src が存在する場合のみ・kebab-case)
|
|
29
|
-
check_folder_names() {
|
|
30
|
-
local n=0
|
|
31
|
-
[[ -d "src" ]] || return 0
|
|
32
|
-
while IFS= read -r dir; do
|
|
33
|
-
[[ -z "$dir" ]] && continue
|
|
34
|
-
base=$(basename "$dir")
|
|
35
|
-
if ! echo "$base" | grep -qE '^[a-z][a-z0-9-]*$'; then
|
|
36
|
-
echo "NAMING: フォルダ名「$dir」はkebab-caseで命名してください" >&2
|
|
37
|
-
n=$((n+1))
|
|
38
|
-
fi
|
|
39
|
-
done < <(find src/ -type d 2>/dev/null || true)
|
|
40
|
-
return $n
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
check_branch_name || errors=$((errors+1))
|
|
44
|
-
check_folder_names || errors=$((errors+1))
|
|
45
|
-
|
|
46
|
-
if [[ $errors -eq 0 ]]; then
|
|
47
|
-
echo "✅ 命名規則チェック: 問題なし"
|
|
48
|
-
exit 0
|
|
49
|
-
else
|
|
50
|
-
exit 1
|
|
51
|
-
fi
|
|
@@ -1,74 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env bash
|
|
2
|
-
# ドリフト確認 --スキーマ(spec.md セクション 11)
|
|
3
|
-
# ① DBML テーブル名の禁止語 ② テーブル note の集約参照 ③ 集約.md 対応テーブル↔DBML ④ 必須カラム
|
|
4
|
-
|
|
5
|
-
set -e
|
|
6
|
-
REPO_ROOT="${REPO_ROOT:-$(git rev-parse --show-toplevel 2>/dev/null || echo ".")}"
|
|
7
|
-
cd "$REPO_ROOT"
|
|
8
|
-
|
|
9
|
-
DBML="docs/04_インフラ設計/schema.dbml"
|
|
10
|
-
AGGREGATES="docs/02_ドメイン設計/集約.md"
|
|
11
|
-
DICT="docs/02_ドメイン設計/ユビキタス言語辞書.md"
|
|
12
|
-
errors=0
|
|
13
|
-
|
|
14
|
-
[[ -f "$DBML" ]] || { echo "SCHEMA_DRIFT: $DBML が存在しません" >&2; exit 1; }
|
|
15
|
-
|
|
16
|
-
# DBML 内のテーブル名一覧を取得(Table name { の形式)
|
|
17
|
-
get_dbml_tables() {
|
|
18
|
-
grep -E '^Table [a-zA-Z_][a-zA-Z0-9_]*' "$DBML" 2>/dev/null | sed 's/^Table \([a-zA-Z_][a-zA-Z0-9_]*\).*/\1/' || true
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
# ① DBML テーブル名に禁止語が含まれていないか
|
|
22
|
-
if [[ -f "$DICT" ]] && command -v yq >/dev/null 2>&1; then
|
|
23
|
-
while IFS= read -r -d '' forbidden; do
|
|
24
|
-
[[ -z "$forbidden" ]] && continue
|
|
25
|
-
while IFS= read -r table; do
|
|
26
|
-
[[ -z "$table" ]] && continue
|
|
27
|
-
if echo "$table" | grep -qi "$forbidden"; then
|
|
28
|
-
echo "SCHEMA_TERM_DRIFT: テーブル「$table」に禁止語「$forbidden」が含まれています" >&2
|
|
29
|
-
errors=$((errors+1))
|
|
30
|
-
fi
|
|
31
|
-
done < <(get_dbml_tables)
|
|
32
|
-
done < <(yq '.terms[]? | select(.forbidden != null) | .forbidden[]?' "$DICT" -r -0 2>/dev/null || true)
|
|
33
|
-
fi
|
|
34
|
-
|
|
35
|
-
# ② 各テーブルの Note に集約参照(docs/02_ドメイン設計 または 集約)が含まれるか
|
|
36
|
-
while IFS= read -r table; do
|
|
37
|
-
[[ -z "$table" ]] && continue
|
|
38
|
-
block=$(awk "/^Table ${table}[^a-zA-Z0-9_]/,/^}/" "$DBML" 2>/dev/null || true)
|
|
39
|
-
if ! echo "$block" | grep -qE 'docs/02_ドメイン設計|集約\.md'; then
|
|
40
|
-
echo "SCHEMA_DRIFT: テーブル「$table」の Note に集約参照がありません(docs/02_ドメイン設計 または 集約.md)" >&2
|
|
41
|
-
errors=$((errors+1))
|
|
42
|
-
fi
|
|
43
|
-
done < <(get_dbml_tables)
|
|
44
|
-
|
|
45
|
-
# ③ 集約.md の「対応テーブル」欄のテーブルが DBML に存在するか
|
|
46
|
-
if [[ -f "$AGGREGATES" ]]; then
|
|
47
|
-
# 対応テーブルセクション内の `tablename` を抽出(| `name` | の形式)
|
|
48
|
-
mapping_tables=$(sed -n '/### 対応テーブル/,/^###/p' "$AGGREGATES" 2>/dev/null | grep -oE '\`[a-z_][a-z0-9_]*\`' | tr -d '`' || true)
|
|
49
|
-
for mapping_table in $mapping_tables; do
|
|
50
|
-
if ! get_dbml_tables | grep -qx "$mapping_table"; then
|
|
51
|
-
echo "SCHEMA_DRIFT: 集約.md の対応テーブル「$mapping_table」が DBML に存在しません" >&2
|
|
52
|
-
errors=$((errors+1))
|
|
53
|
-
fi
|
|
54
|
-
done
|
|
55
|
-
fi
|
|
56
|
-
|
|
57
|
-
# ④ 各テーブルに必須カラム id, created_at, updated_at があるか
|
|
58
|
-
while IFS= read -r table; do
|
|
59
|
-
[[ -z "$table" ]] && continue
|
|
60
|
-
block=$(awk "/^Table ${table}[^a-zA-Z0-9_]/,/^}/" "$DBML" 2>/dev/null || true)
|
|
61
|
-
for col in id created_at updated_at; do
|
|
62
|
-
if ! echo "$block" | grep -qE "^\s+${col}\s"; then
|
|
63
|
-
echo "SCHEMA_DRIFT: テーブル「$table」に必須カラム「${col}」がありません" >&2
|
|
64
|
-
errors=$((errors+1))
|
|
65
|
-
fi
|
|
66
|
-
done
|
|
67
|
-
done < <(get_dbml_tables)
|
|
68
|
-
|
|
69
|
-
if [[ $errors -eq 0 ]]; then
|
|
70
|
-
echo "✅ スキーマ整合性チェック: 問題なし"
|
|
71
|
-
exit 0
|
|
72
|
-
else
|
|
73
|
-
exit 1
|
|
74
|
-
fi
|
|
@@ -1,153 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env bash
|
|
2
|
-
# ドキュメントとマイグレーション結果の一致を検証する。
|
|
3
|
-
# ① Prisma スキーマ ↔ schema.dbml のテーブル・ENUM 一致
|
|
4
|
-
# ② 集約.md の「対応テーブル」に登場するテーブルが schema.dbml に存在するか
|
|
5
|
-
# ③ 設計書の「テーブル.カラム」の使い方・型が schema.dbml の定義と一致するか(集約.md の型列は本スクリプトの正規化ルールで比較)
|
|
6
|
-
|
|
7
|
-
set -e
|
|
8
|
-
REPO_ROOT="${REPO_ROOT:-$(git rev-parse --show-toplevel 2>/dev/null || echo ".")}"
|
|
9
|
-
cd "$REPO_ROOT"
|
|
10
|
-
|
|
11
|
-
PRISMA="prisma/schema.prisma"
|
|
12
|
-
DBML="docs/04_インフラ設計/schema.dbml"
|
|
13
|
-
AGGREGATES="docs/02_ドメイン設計/集約.md"
|
|
14
|
-
errors=0
|
|
15
|
-
|
|
16
|
-
[[ -f "$PRISMA" ]] || { echo "SCHEMA_SYNC: $PRISMA が存在しません" >&2; exit 1; }
|
|
17
|
-
[[ -f "$DBML" ]] || { echo "SCHEMA_SYNC: $DBML が存在しません" >&2; exit 1; }
|
|
18
|
-
|
|
19
|
-
# Prisma の @@map("table_name") からテーブル名一覧を取得
|
|
20
|
-
get_prisma_tables() {
|
|
21
|
-
grep -E '@@map\(' "$PRISMA" 2>/dev/null | sed -n 's/.*@@map("\([^"]*\)").*/\1/p' | sort -u
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
# Prisma の enum 名を取得(DBML では status_enum のように _enum 付きのことが多い)
|
|
25
|
-
get_prisma_enums() {
|
|
26
|
-
grep -E '^enum ' "$PRISMA" 2>/dev/null | sed 's/^enum \([A-Za-z0-9_]*\).*/\1/' | sort -u
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
# DBML の Table 名一覧を取得
|
|
30
|
-
get_dbml_tables() {
|
|
31
|
-
grep -E '^Table [a-zA-Z_][a-zA-Z0-9_]*' "$DBML" 2>/dev/null | sed 's/^Table \([a-zA-Z_][a-zA-Z0-9_]*\).*/\1/' | sort -u
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
# DBML の Enum 名一覧を取得
|
|
35
|
-
get_dbml_enums() {
|
|
36
|
-
grep -E '^Enum [a-zA-Z_][a-zA-Z0-9_]*' "$DBML" 2>/dev/null | sed 's/^Enum \([a-zA-Z_][a-zA-Z0-9_]*\).*/\1/' | sort -u
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
# テーブル一致チェック
|
|
40
|
-
prisma_tables=$(get_prisma_tables)
|
|
41
|
-
dbml_tables=$(get_dbml_tables)
|
|
42
|
-
|
|
43
|
-
for t in $prisma_tables; do
|
|
44
|
-
if ! echo "$dbml_tables" | grep -qx "$t"; then
|
|
45
|
-
echo "SCHEMA_SYNC: Prisma のテーブル「$t」が schema.dbml にありません" >&2
|
|
46
|
-
errors=$((errors+1))
|
|
47
|
-
fi
|
|
48
|
-
done
|
|
49
|
-
for t in $dbml_tables; do
|
|
50
|
-
if ! echo "$prisma_tables" | grep -qx "$t"; then
|
|
51
|
-
echo "SCHEMA_SYNC: schema.dbml のテーブル「$t」が Prisma にありません" >&2
|
|
52
|
-
errors=$((errors+1))
|
|
53
|
-
fi
|
|
54
|
-
done
|
|
55
|
-
|
|
56
|
-
# ENUM は Prisma が PascalCase・DBML が snake_case_enum のため、DBML に Enum が存在するかだけ確認
|
|
57
|
-
# (値の一致までは見ない。テーブル一致で運用のずれは検知できる)
|
|
58
|
-
dbml_enum_count=$(get_dbml_enums | wc -l | tr -d ' ')
|
|
59
|
-
prisma_enum_count=$(get_prisma_enums | wc -l | tr -d ' ')
|
|
60
|
-
if [[ "$prisma_enum_count" -gt 0 ]] && [[ "$dbml_enum_count" -eq 0 ]]; then
|
|
61
|
-
echo "SCHEMA_SYNC: Prisma に enum がありますが schema.dbml に Enum がありません" >&2
|
|
62
|
-
errors=$((errors+1))
|
|
63
|
-
fi
|
|
64
|
-
|
|
65
|
-
# DBML 内で指定テーブルが指定カラムを持つか(Table ブロック内の行で「 col type」を検出)
|
|
66
|
-
table_has_column() {
|
|
67
|
-
local table="$1"
|
|
68
|
-
local col="$2"
|
|
69
|
-
local block
|
|
70
|
-
block=$(awk "/^Table ${table}[^a-zA-Z0-9_]/,/^}/" "$DBML" 2>/dev/null)
|
|
71
|
-
echo "$block" | grep -qE "^\s+${col}\s+"
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
# DBML 内の指定テーブル.カラムの型を取得(2番目のトークン。例: uuid, varchar(200), status_enum)
|
|
75
|
-
get_dbml_column_type() {
|
|
76
|
-
local table="$1"
|
|
77
|
-
local col="$2"
|
|
78
|
-
local block
|
|
79
|
-
block=$(awk "/^Table ${table}[^a-zA-Z0-9_]/,/^}/" "$DBML" 2>/dev/null)
|
|
80
|
-
echo "$block" | grep -E "^\s+${col}\s+" | head -1 | awk '{ printf "%s", $2 }'
|
|
81
|
-
}
|
|
82
|
-
|
|
83
|
-
# 集約.md の対応テーブル表で table.column の行から「型」列(| 区切り4番目)を取得
|
|
84
|
-
get_aggregate_doc_type() {
|
|
85
|
-
local ref="$1"
|
|
86
|
-
grep "| *${ref} *|" "$AGGREGATES" 2>/dev/null | head -1 | awk -F'|' 'NF>=4 {
|
|
87
|
-
gsub(/^[ \t]+|[ \t]+$/, "", $4); print $4
|
|
88
|
-
}'
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
# 型を正規化して比較用に統一(uuid/varchar/text/timestamptz/date/enum)
|
|
92
|
-
# 正規化後: uuid | varchar | text | timestamptz | date | enum
|
|
93
|
-
normalize_type() {
|
|
94
|
-
local raw
|
|
95
|
-
raw=$(printf '%s' "$1" | tr -d '\r\n' | sed 's/^[ \t]*//; s/[ \t]*$//; s/ .*//; s/(.*//')
|
|
96
|
-
[[ -z "$raw" ]] && return
|
|
97
|
-
raw=$(echo "$raw" | awk '{ print tolower($0) }')
|
|
98
|
-
if [[ "$raw" == varchar* ]]; then echo -n "varchar"; return; fi
|
|
99
|
-
if [[ "$raw" == *enum* ]] || [[ "$raw" == *_enum ]]; then echo -n "enum"; return; fi
|
|
100
|
-
case "$raw" in
|
|
101
|
-
uuid|text|timestamptz|date) echo -n "$raw" ;;
|
|
102
|
-
*) echo -n "$raw" ;;
|
|
103
|
-
esac
|
|
104
|
-
}
|
|
105
|
-
|
|
106
|
-
# 集約.md の「対応テーブル」に登場するテーブルが DBML に存在するか(schema-drift と重複するがここでも確認)
|
|
107
|
-
if [[ -f "$AGGREGATES" ]]; then
|
|
108
|
-
mapping_tables=$(sed -n '/### 対応テーブル/,/^###/p' "$AGGREGATES" 2>/dev/null | grep -oE '\`[a-z_][a-z0-9_]*\`' | tr -d '`' | sort -u)
|
|
109
|
-
for t in $mapping_tables; do
|
|
110
|
-
if ! echo "$dbml_tables" | grep -qx "$t"; then
|
|
111
|
-
echo "SCHEMA_SYNC: 集約.md の対応テーブル「$t」が schema.dbml にありません" >&2
|
|
112
|
-
errors=$((errors+1))
|
|
113
|
-
fi
|
|
114
|
-
done
|
|
115
|
-
|
|
116
|
-
# 設計書の「テーブル.カラム」の使い方が schema.dbml の定義と一致するか
|
|
117
|
-
# 集約.md の「DBテーブル.カラム」列から table.column を抽出("schema.dbml" は見出し由来のため除外)
|
|
118
|
-
table_col_refs=$(grep -oE '[a-z_][a-z0-9_]*\.[a-z_][a-z0-9_]*' "$AGGREGATES" 2>/dev/null | grep -v '^schema\.dbml$' | sort -u)
|
|
119
|
-
while IFS= read -r ref; do
|
|
120
|
-
ref=$(printf '%s' "$ref" | tr -d '\r\n')
|
|
121
|
-
[[ -z "$ref" ]] && continue
|
|
122
|
-
t="${ref%%.*}"
|
|
123
|
-
c="${ref#*.}"
|
|
124
|
-
[[ -z "$t" || -z "$c" ]] && continue
|
|
125
|
-
if ! echo "$dbml_tables" | grep -qx "$t"; then
|
|
126
|
-
echo "SCHEMA_SYNC: 集約.md で参照されているテーブル「$t」が schema.dbml にありません" >&2
|
|
127
|
-
errors=$((errors+1))
|
|
128
|
-
elif ! table_has_column "$t" "$c"; then
|
|
129
|
-
echo "SCHEMA_SYNC: 集約.md の「$ref」— schema.dbml のテーブル「$t」にカラム「$c」がありません" >&2
|
|
130
|
-
errors=$((errors+1))
|
|
131
|
-
else
|
|
132
|
-
# 型の一致(集約.md の「型」列と schema.dbml を正規化して比較)
|
|
133
|
-
doc_type=$(get_aggregate_doc_type "$ref" | tr -d '\r\n')
|
|
134
|
-
dbml_type=$(get_dbml_column_type "$t" "$c" | tr -d '\r\n')
|
|
135
|
-
if [[ -n "$doc_type" ]] && [[ -n "$dbml_type" ]]; then
|
|
136
|
-
norm_doc=$(normalize_type "$doc_type" | tr -d '\r\n' | sed 's/^[ \t]*//; s/[ \t]*$//')
|
|
137
|
-
norm_dbml=$(normalize_type "$dbml_type" | tr -d '\r\n' | sed 's/^[ \t]*//; s/[ \t]*$//')
|
|
138
|
-
if [[ -n "$norm_doc" ]] && [[ -n "$norm_dbml" ]] && [[ "x${norm_doc}" != "x${norm_dbml}" ]]; then
|
|
139
|
-
echo "SCHEMA_SYNC: 集約.md の「${ref}」の型が schema.dbml と不一致(集約: ${doc_type} → ${norm_doc}, DBML: ${dbml_type} → ${norm_dbml})" >&2
|
|
140
|
-
errors=$((errors+1))
|
|
141
|
-
fi
|
|
142
|
-
fi
|
|
143
|
-
fi
|
|
144
|
-
done <<< "$table_col_refs"
|
|
145
|
-
fi
|
|
146
|
-
|
|
147
|
-
if [[ $errors -eq 0 ]]; then
|
|
148
|
-
echo "✅ スキーマ同期チェック: ドキュメントと Prisma は一致しています"
|
|
149
|
-
exit 0
|
|
150
|
-
else
|
|
151
|
-
echo "SCHEMA_SYNC: $errors 件の不整合" >&2
|
|
152
|
-
exit 1
|
|
153
|
-
fi
|
|
@@ -1,75 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env bash
|
|
2
|
-
# UC コンテキスト: 現在ブランチ(feature/UC-NNN-xxx)に対応する「関連ファイルのパス」を返す。
|
|
3
|
-
# ステップ(分析・仕様策定・実装 等)で AI が「どのファイルを読むか」を 1 回の実行で取得するために使う。
|
|
4
|
-
# 構成: docs/05_ユースケース仕様/<カテゴリ>/UC-NNN-xxx.md(1 UC = 1 ファイル。実装方針・タスクはこの .md の一番下に記載)
|
|
5
|
-
# 出力: FEATURE_SPEC, FEATURE_DIR を JSON またはテキスト
|
|
6
|
-
# 使用: uc-context.sh [--json] [--paths-only]
|
|
7
|
-
|
|
8
|
-
set -e
|
|
9
|
-
REPO_ROOT="${REPO_ROOT:-$(git rev-parse --show-toplevel 2>/dev/null || echo ".")}"
|
|
10
|
-
cd "$REPO_ROOT"
|
|
11
|
-
|
|
12
|
-
PATHS_ONLY=false
|
|
13
|
-
JSON_MODE=false
|
|
14
|
-
for a in "$@"; do
|
|
15
|
-
case "$a" in
|
|
16
|
-
--paths-only) PATHS_ONLY=true ;;
|
|
17
|
-
--json) JSON_MODE=true ;;
|
|
18
|
-
esac
|
|
19
|
-
done
|
|
20
|
-
|
|
21
|
-
BRANCH=$(git branch --show-current 2>/dev/null || echo "")
|
|
22
|
-
# ブランチ接頭辞は project.json の naming.branch_prefix。無ければ feature
|
|
23
|
-
BRANCH_PREFIX="feature"
|
|
24
|
-
if [[ -f ".spec-runner/project.json" ]] && command -v jq >/dev/null 2>&1; then
|
|
25
|
-
p=$(jq -r '.naming.branch_prefix // empty' .spec-runner/project.json 2>/dev/null)
|
|
26
|
-
[[ -n "$p" ]] && BRANCH_PREFIX="$p"
|
|
27
|
-
fi
|
|
28
|
-
# 正規表現は接頭辞/UC-NNN-xxx 形式(接頭辞は英数字とハイフンのみ想定)
|
|
29
|
-
if [[ ! "$BRANCH" =~ ^${BRANCH_PREFIX}/(UC-[0-9]{3}-[a-z0-9-]+)$ ]]; then
|
|
30
|
-
echo "ERROR: UC 用ブランチではありません。${BRANCH_PREFIX}/UC-NNN-xxx 形式で作成してください(例: ブランチ作成 UC-001 order-placement)。接頭辞は project.json の naming.branch_prefix で変更可。" >&2
|
|
31
|
-
exit 1
|
|
32
|
-
fi
|
|
33
|
-
|
|
34
|
-
# feature/UC-001-order-placement → UC-001-order-placement
|
|
35
|
-
UC_BASE="${BASH_REMATCH[1]}"
|
|
36
|
-
UC_DIR="$REPO_ROOT/docs/05_ユースケース仕様"
|
|
37
|
-
# カテゴリフォルダ内の UC-NNN-xxx.md を探す(docs/05_ユースケース仕様/<カテゴリ>/UC-NNN-xxx.md)
|
|
38
|
-
FEATURE_SPEC=""
|
|
39
|
-
for f in "$UC_DIR"/*/"${UC_BASE}.md"; do
|
|
40
|
-
[[ -f "$f" ]] && FEATURE_SPEC="$f" && break
|
|
41
|
-
done
|
|
42
|
-
if [[ -n "$FEATURE_SPEC" ]]; then
|
|
43
|
-
FEATURE_DIR=$(dirname "$FEATURE_SPEC")
|
|
44
|
-
else
|
|
45
|
-
FEATURE_DIR="$UC_DIR"
|
|
46
|
-
fi
|
|
47
|
-
|
|
48
|
-
if [[ "$PATHS_ONLY" != true ]]; then
|
|
49
|
-
if [[ -z "$FEATURE_SPEC" || ! -f "$FEATURE_SPEC" ]]; then
|
|
50
|
-
echo "ERROR: UC 仕様書がありません。docs/05_ユースケース仕様/<カテゴリ>/${UC_BASE}.md のいずれかに配置してください。" >&2
|
|
51
|
-
exit 1
|
|
52
|
-
fi
|
|
53
|
-
fi
|
|
54
|
-
|
|
55
|
-
json_escape() {
|
|
56
|
-
printf '%s' "$1" | sed 's/\\/\\\\/g; s/"/\\"/g; s/\t/\\t/g; s/\r/\\r/g'
|
|
57
|
-
}
|
|
58
|
-
if [[ "$JSON_MODE" == true ]]; then
|
|
59
|
-
if command -v jq >/dev/null 2>&1; then
|
|
60
|
-
jq -cn \
|
|
61
|
-
--arg repo_root "$REPO_ROOT" \
|
|
62
|
-
--arg branch "$BRANCH" \
|
|
63
|
-
--arg feature_spec "$FEATURE_SPEC" \
|
|
64
|
-
--arg feature_dir "$FEATURE_DIR" \
|
|
65
|
-
'{REPO_ROOT:$repo_root,BRANCH:$branch,FEATURE_SPEC:$feature_spec,FEATURE_DIR:$feature_dir}'
|
|
66
|
-
else
|
|
67
|
-
printf '{"REPO_ROOT":"%s","BRANCH":"%s","FEATURE_SPEC":"%s","FEATURE_DIR":"%s"}\n' \
|
|
68
|
-
"$(json_escape "$REPO_ROOT")" "$(json_escape "$BRANCH")" "$(json_escape "$FEATURE_SPEC")" "$(json_escape "$FEATURE_DIR")"
|
|
69
|
-
fi
|
|
70
|
-
else
|
|
71
|
-
echo "REPO_ROOT: $REPO_ROOT"
|
|
72
|
-
echo "BRANCH: $BRANCH"
|
|
73
|
-
echo "FEATURE_SPEC: $FEATURE_SPEC"
|
|
74
|
-
echo "FEATURE_DIR: $FEATURE_DIR"
|
|
75
|
-
fi
|