- Record rejected answers in decision.md at the moment they are rejected - Allow explicit pre-authorized Build via a pre-authorized marker line - Land scripts/check-dev-flow.sh and derive devflow/index.md from archives - Add dev-flow process artifacts (decisions, prd) for open changes
142 lines
5.1 KiB
Bash
142 lines
5.1 KiB
Bash
#!/usr/bin/env bash
|
|
set -euo pipefail
|
|
|
|
# dev-flow 收尾检查 + index 派生
|
|
# 检查模式: check-dev-flow.sh <slug> → 对 openspec/changes/<slug>/ 执行三项收尾检查
|
|
# 索引模式: check-dev-flow.sh --index → 扫描 openspec/changes/archive/ 与 openspec/archive/ 生成 devflow/index.md
|
|
# 全量模式: check-dev-flow.sh --all → 对所有活跃 change 执行检查
|
|
#
|
|
# 三项收尾检查:
|
|
# 1. decision.md 存在(豁免需显式: decision.md 或提交信息中写明)
|
|
# 2. 含 ## Alternatives considered 或显式 <!-- alternatives-not-recorded -->
|
|
# 3. ## Verification 无"已确认 X"类不可重跑结论
|
|
|
|
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
|
|
PROJECT_ROOT="$(cd "$SCRIPT_DIR/.." && pwd)"
|
|
CHANGES_ROOT="$PROJECT_ROOT/openspec/changes"
|
|
|
|
fail() { echo "✗ $1" >&2; exit 1; }
|
|
|
|
check_change() {
|
|
local slug="$1"
|
|
local dir="$CHANGES_ROOT/$slug"
|
|
local decision="$dir/decision.md"
|
|
local errors=0
|
|
|
|
[ -d "$dir" ] || fail "change 目录不存在: openspec/changes/$slug"
|
|
|
|
echo "== dev-flow 收尾检查: $slug =="
|
|
|
|
# 1. decision.md 存在
|
|
if [ ! -f "$decision" ]; then
|
|
echo "✗ [1] 缺少 openspec/changes/$slug/decision.md (纯机械改动可豁免,但豁免必须显式写在提交信息里)"
|
|
errors=$((errors+1))
|
|
else
|
|
echo "✓ [1] decision.md 存在"
|
|
|
|
# 2. Alternatives 强制
|
|
if grep -q '^## Alternatives considered' "$decision" || grep -q '<!-- alternatives-not-recorded -->' "$decision"; then
|
|
echo "✓ [2] 备选已记录或显式声明未记录"
|
|
else
|
|
echo "✗ [2] decision.md 缺少 ## Alternatives considered,也没有 <!-- alternatives-not-recorded --> 显式豁免"
|
|
errors=$((errors+1))
|
|
fi
|
|
|
|
# 3. Verification 无不可重跑结论
|
|
local vtext
|
|
vtext="$(awk '/^## Verification/{flag=1;next} /^## /{flag=0} flag' "$decision")"
|
|
if echo "$vtext" | grep -qE '已确认|已验证[^,。;(]|测试通过$'; then
|
|
echo "✗ [3] ## Verification 含不可重跑的结论(如\"已确认 X 存在\")——改为可重跑的命令或明确的人工步骤"
|
|
errors=$((errors+1))
|
|
elif [ -z "$vtext" ]; then
|
|
echo "✗ [3] 缺少 ## Verification 小节"
|
|
errors=$((errors+1))
|
|
else
|
|
echo "✓ [3] Verification 通过"
|
|
fi
|
|
fi
|
|
|
|
if [ "$errors" -gt 0 ]; then
|
|
echo "== $slug: $errors 项未通过 =="
|
|
return 1
|
|
fi
|
|
echo "== $slug: 全部通过 =="
|
|
return 0
|
|
}
|
|
|
|
gen_index() {
|
|
local out="$PROJECT_ROOT/devflow/index.md"
|
|
echo "# devflow 索引" > "$out"
|
|
echo "" >> "$out"
|
|
echo "> 本文件由 scripts/check-dev-flow.sh --index 派生生成,**不要手写**。" >> "$out"
|
|
echo "> 存储层是 openspec/archive/ 下的归档目录;此处只是浏览视图。" >> "$out"
|
|
echo "" >> "$out"
|
|
|
|
local found=0
|
|
local rows=()
|
|
local arch_dir
|
|
# 两个历史归档位置都扫:openspec/changes/archive/ 与 openspec/archive/
|
|
for arch_dir in "$CHANGES_ROOT/archive" "$PROJECT_ROOT/openspec/archive"; do
|
|
[ -d "$arch_dir" ] || continue
|
|
local d
|
|
while IFS= read -r d; do
|
|
d="${d%/}"
|
|
[ -d "$d" ] || continue
|
|
local name; name="$(basename "$d")"
|
|
local title; title=""
|
|
local date; date=""
|
|
local rel; rel="${d#"$PROJECT_ROOT"/}"
|
|
# 标题取 decision.md 首行,否则 proposal.md 首个 # 标题
|
|
if [ -f "$d/decision.md" ]; then
|
|
title="$(head -1 "$d/decision.md" | sed 's/^# *//; s/^[[:space:]]*//; s/[[:space:]]*$//' || true)"
|
|
elif [ -f "$d/proposal.md" ]; then
|
|
title="$(grep -m1 '^# ' "$d/proposal.md" | sed 's/^# *//' || true)"
|
|
fi
|
|
# 日期取目录名前缀 YYYY-MM-DD
|
|
if [[ "$name" =~ ^([0-9]{4}-[0-9]{2}-[0-9]{2}) ]]; then
|
|
date="${BASH_REMATCH[1]}"
|
|
fi
|
|
rows+=("| ${date:-—} | ${title:-$name} | ${rel} |")
|
|
found=$((found+1))
|
|
done < <(find "$arch_dir" -mindepth 1 -maxdepth 1 -type d | sort)
|
|
done
|
|
|
|
# 写表格(带表头)
|
|
{
|
|
echo "| 日期 | 标题 | 归档位置 |"
|
|
echo "|---|---|---|"
|
|
if [ ${#rows[@]} -gt 0 ]; then
|
|
printf '%s\n' "${rows[@]}"
|
|
fi
|
|
} >> "$out"
|
|
|
|
echo "[index] 生成 $found 个归档条目 → devflow/index.md"
|
|
}
|
|
|
|
case "${1:-}" in
|
|
--index)
|
|
gen_index
|
|
;;
|
|
--all)
|
|
rc=0
|
|
for d in "$CHANGES_ROOT"/*/; do
|
|
d="${d%/}"
|
|
[ -d "$d" ] || continue
|
|
name="$(basename "$d")"
|
|
[ "$name" = "archive" ] && continue
|
|
check_change "$name" || rc=1
|
|
done
|
|
exit $rc
|
|
;;
|
|
"")
|
|
echo "用法: check-dev-flow.sh <slug> | --all | --index" >&2
|
|
echo " <slug> 对单个活跃 change 执行三项收尾检查" >&2
|
|
echo " --all 检查所有活跃 change" >&2
|
|
echo " --index 扫描归档生成 devflow/index.md" >&2
|
|
exit 2
|
|
;;
|
|
*)
|
|
check_change "$1"
|
|
;;
|
|
esac
|