create-afe-project 0.0.0-stage → 2.0.0
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/CHANGELOG.md +458 -0
- package/LICENSE +21 -0
- package/README.md +77 -2
- package/bin/create-afe-project.js +123 -0
- package/package.json +43 -4
- package/src/answers.js +147 -0
- package/src/config.js +151 -0
- package/src/project.js +217 -0
- package/src/render.js +51 -0
- package/template/.claude/harness.conf +50 -0
- package/template/.claude/hooks/post-edit-check.sh +41 -0
- package/template/.claude/hooks/protect-docs.sh +315 -0
- package/template/.claude/hooks/session-start.sh +81 -0
- package/template/.claude/hooks/stop-check-decisions.sh +25 -0
- package/template/.claude/hooks/stop-check-docs.sh +29 -0
- package/template/.claude/hooks/stop-check-fast.sh +33 -0
- package/template/.claude/hooks/stop-check-template.sh +23 -0
- package/template/.claude/hooks/stop-run-project-hooks.sh +27 -0
- package/template/.claude/path-classes +102 -0
- package/template/.claude/settings.project.json +5 -0
- package/template/.claude/settings.template.json +92 -0
- package/template/.claude/skills/afe-init/SKILL.md +104 -0
- package/template/.claude/skills/create-pr/SKILL.md.jinja +110 -0
- package/template/.claude/skills/update-template/SKILL.md +40 -0
- package/template/.claude/skills/{% if code_review %}review-code{% endif %}/SKILL.md.jinja +50 -0
- package/template/.claude/skills/{% if docs_review %}docs-review{% endif %}/SKILL.md +60 -0
- package/template/.claude/skills/{% if dual_review %}dual-review{% endif %}/SKILL.md +58 -0
- package/template/.github/workflows/decisions.yml.jinja +57 -0
- package/template/.github/workflows/docs.yml.jinja +40 -0
- package/template/.github/workflows/{% if code_review %}code-review.yml{% endif %}.jinja +54 -0
- package/template/.github/workflows/{% if docs_review %}docs-review.yml{% endif %}.jinja +52 -0
- package/template/.github/workflows/{% if fail_without_change %}fail-without-change.yml{% endif %}.jinja +42 -0
- package/template/.github/workflows/{% if secret_scan %}secret-scan.yml{% endif %}.jinja +47 -0
- package/template/.markdownlint-cli2.jsonc +27 -0
- package/template/.template/owned.txt.jinja +65 -0
- package/template/AGENTS.md.jinja +91 -0
- package/template/CLAUDE.md +3 -0
- package/template/README.md.jinja +53 -0
- package/template/_gitignore +14 -0
- package/template/docs/README.md.jinja +44 -0
- package/template/docs/contributing.md.jinja +206 -0
- package/template/docs/decisions/adr-template.md +38 -0
- package/template/docs/documentation-rules.md.jinja +200 -0
- package/template/docs/testing-rules.md +80 -0
- package/template/scripts/check-decisions.sh +382 -0
- package/template/scripts/check-docs.sh +20 -0
- package/template/scripts/check-links.mjs +142 -0
- package/template/scripts/check-project.sh +27 -0
- package/template/scripts/check-template.sh +88 -0
- package/template/scripts/check.sh +21 -0
- package/template/scripts/env.sh +29 -0
- package/template/scripts/gen-decisions-index.sh +57 -0
- package/template/scripts/gen-settings.sh +14 -0
- package/template/scripts/lib/classes.sh +89 -0
- package/template/scripts/lib/git-base.sh +25 -0
- package/template/scripts/lib/harness-conf.sh +48 -0
- package/template/scripts/lib/json.mjs +160 -0
- package/template/scripts/pr-metrics.sh +80 -0
- package/template/scripts/protect-branch.sh +101 -0
- package/template/scripts/{% if acceptance_check %}check-acceptance.sh{% endif %} +47 -0
- package/template/scripts/{% if fail_without_change %}fail-without-change.sh{% endif %} +88 -0
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# Acceptance check, the `acceptance_check` module (docs/contributing.md, section 3): every
|
|
3
|
+
# identifier that the section "Acceptance and test plan" of a pull request description names, by
|
|
4
|
+
# ID_PATTERN of .claude/harness.conf, appears as a whole word in at least one test file of
|
|
5
|
+
# TEST_GLOBS. It does not judge whether that test proves the identifier; the review does.
|
|
6
|
+
# scripts/check-decisions.sh --ready runs it. It skips when TEST_GLOBS is empty or the section
|
|
7
|
+
# names no identifier. A project may replace it with a stricter check in scripts/checks.d/.
|
|
8
|
+
#
|
|
9
|
+
# Usage: scripts/check-acceptance.sh --pr-body <file>
|
|
10
|
+
set -uo pipefail
|
|
11
|
+
cd "$(dirname "$0")/.."
|
|
12
|
+
# shellcheck source=scripts/lib/harness-conf.sh
|
|
13
|
+
. scripts/lib/harness-conf.sh
|
|
14
|
+
|
|
15
|
+
body=
|
|
16
|
+
while [ $# -gt 0 ]; do
|
|
17
|
+
case "$1" in
|
|
18
|
+
--pr-body) body="${2:?--pr-body needs a file}"; shift 2 ;;
|
|
19
|
+
*) echo "unknown argument: $1" >&2; exit 2 ;;
|
|
20
|
+
esac
|
|
21
|
+
done
|
|
22
|
+
[ -f "$body" ] || { echo "check-acceptance: --pr-body <file> is required" >&2; exit 2; }
|
|
23
|
+
|
|
24
|
+
echo "== acceptance: every identifier of the description is carried by a test file"
|
|
25
|
+
pattern=$(conf_get ID_PATTERN '(REQ|AC|INV)-[0-9]+')
|
|
26
|
+
globs=$(conf_get TEST_GLOBS)
|
|
27
|
+
[ -n "$globs" ] || { echo "SKIP: TEST_GLOBS is empty in .claude/harness.conf"; exit 0; }
|
|
28
|
+
ids=$(tr -d '\r' < "$body" |
|
|
29
|
+
awk '/^## / {l = $0; sub(/[[:space:]]+$/, "", l); s = (l == "## Acceptance and test plan"); next} s' |
|
|
30
|
+
grep -o -E -- "$pattern" | sort -u)
|
|
31
|
+
[ -n "$ids" ] || { echo "SKIP: the Acceptance section names no identifier"; exit 0; }
|
|
32
|
+
files=$(matching_files "$globs")
|
|
33
|
+
|
|
34
|
+
failures=0
|
|
35
|
+
while IFS= read -r id; do
|
|
36
|
+
hit=$(printf '%s\n' "$files" | while IFS= read -r f; do
|
|
37
|
+
[ -f "$f" ] && grep -q -F -w -- "$id" "$f" 2>/dev/null && { printf '%s\n' "$f"; break; }
|
|
38
|
+
done)
|
|
39
|
+
if [ -n "$hit" ]; then
|
|
40
|
+
echo "ok: $id in $hit"
|
|
41
|
+
else
|
|
42
|
+
echo "FAIL: $id: no test file of TEST_GLOBS carries it (docs/testing-rules.md, section 1)"
|
|
43
|
+
failures=$((failures + 1))
|
|
44
|
+
fi
|
|
45
|
+
done <<< "$ids"
|
|
46
|
+
[ "$failures" -eq 0 ] || { echo "== acceptance check failed: $failures"; exit 1; }
|
|
47
|
+
echo "== acceptance check passed"
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# Fail-without-change driver, the `fail_without_change` module (docs/testing-rules.md, section
|
|
3
|
+
# 3): each test file that the change adds, modifies, renames, or copies, by TEST_GLOBS of
|
|
4
|
+
# .claude/harness.conf, must pass with the change and fail on the base: a failure on both sides,
|
|
5
|
+
# such as a missing toolchain, is no evidence. It builds a temporary git worktree at the merge
|
|
6
|
+
# base, copies the change's version of the test files and of FWC_TOOLCHAIN_GLOBS into it, runs
|
|
7
|
+
# FWC_INSTALL there (SETUP when FWC_INSTALL is empty), then FWC_TEST with one test file as its
|
|
8
|
+
# last argument, and reads pass or fail from the exit code, per file: a file that passes on the
|
|
9
|
+
# base fails the check, unless --allow-pass-on-base is given (the pull request label
|
|
10
|
+
# `tests-pass-on-base`). When FWC_CLASSIFIER is set, it receives the log and the test file and
|
|
11
|
+
# exits 0 when the base failed for the expected reason. It skips when TEST_GLOBS or FWC_TEST is
|
|
12
|
+
# empty, or when no test file changed. It runs the project's own commands, locally before the
|
|
13
|
+
# pull request and in the module's workflow (.github/workflows/fail-without-change.yml).
|
|
14
|
+
#
|
|
15
|
+
# Usage: scripts/fail-without-change.sh [--base <ref>] [--allow-pass-on-base]
|
|
16
|
+
set -uo pipefail
|
|
17
|
+
cd "$(dirname "$0")/.."
|
|
18
|
+
# shellcheck source=scripts/lib/git-base.sh
|
|
19
|
+
. scripts/lib/git-base.sh
|
|
20
|
+
# shellcheck source=scripts/lib/harness-conf.sh
|
|
21
|
+
. scripts/lib/harness-conf.sh
|
|
22
|
+
|
|
23
|
+
base= allow=0
|
|
24
|
+
while [ $# -gt 0 ]; do
|
|
25
|
+
case "$1" in
|
|
26
|
+
--base) base="${2:?--base needs a ref}"; shift 2 ;;
|
|
27
|
+
--allow-pass-on-base) allow=1; shift ;;
|
|
28
|
+
*) echo "unknown argument: $1" >&2; exit 2 ;;
|
|
29
|
+
esac
|
|
30
|
+
done
|
|
31
|
+
|
|
32
|
+
echo "== fail-without-change: new and changed tests fail on the base"
|
|
33
|
+
globs=$(conf_get TEST_GLOBS)
|
|
34
|
+
test_cmd=$(conf_get FWC_TEST)
|
|
35
|
+
[ -n "$globs" ] && [ -n "$test_cmd" ] || { echo "SKIP: TEST_GLOBS or FWC_TEST is empty in .claude/harness.conf"; exit 0; }
|
|
36
|
+
mb=$(resolve_base "$base") || exit 2
|
|
37
|
+
|
|
38
|
+
# The test files the change adds or modifies, and the toolchain files to copy with them.
|
|
39
|
+
changed=$( { git diff --name-only --diff-filter=ACMR "$mb"; git ls-files --others --exclude-standard; } | sort -u)
|
|
40
|
+
tests=$(matching_files "$globs" | grep -x -F -f <(printf '%s\n' "$changed"))
|
|
41
|
+
[ -n "$tests" ] || { echo "SKIP: the change adds or modifies no test file"; exit 0; }
|
|
42
|
+
toolchain=
|
|
43
|
+
tc_globs=$(conf_get FWC_TOOLCHAIN_GLOBS)
|
|
44
|
+
[ -n "$tc_globs" ] && toolchain=$(matching_files "$tc_globs")
|
|
45
|
+
|
|
46
|
+
work=$(mktemp -d)
|
|
47
|
+
wt="$work/base"
|
|
48
|
+
git worktree add -q --detach "$wt" "$mb" || { echo "FAIL: cannot create a worktree at the base"; exit 2; }
|
|
49
|
+
trap 'git worktree remove --force "$wt" >/dev/null 2>&1; rm -rf "$work"' EXIT
|
|
50
|
+
printf '%s\n' "$tests" "$toolchain" | sed '/^$/d' | sort -u | while IFS= read -r f; do
|
|
51
|
+
mkdir -p "$wt/$(dirname "$f")" && cp -p "$f" "$wt/$f"
|
|
52
|
+
done
|
|
53
|
+
|
|
54
|
+
install=$(conf_get FWC_INSTALL "$(conf_get SETUP)")
|
|
55
|
+
if [ -n "$install" ] && ! (cd "$wt" && bash -c "$install") < /dev/null > "$work/install.log" 2>&1; then
|
|
56
|
+
echo "FAIL: FWC_INSTALL failed on the base: $install"
|
|
57
|
+
tail -n 20 "$work/install.log"
|
|
58
|
+
exit 1
|
|
59
|
+
fi
|
|
60
|
+
|
|
61
|
+
failures=0
|
|
62
|
+
classifier=$(conf_get FWC_CLASSIFIER)
|
|
63
|
+
while IFS= read -r t; do
|
|
64
|
+
# shellcheck disable=SC2016
|
|
65
|
+
if ! bash -c "$test_cmd \"\$1\"" fwc "$t" < /dev/null > "$work/now.log" 2>&1; then
|
|
66
|
+
echo "FAIL: $t does not pass with the change, so its failure on the base proves nothing:"
|
|
67
|
+
tail -n 20 "$work/now.log"
|
|
68
|
+
failures=$((failures + 1))
|
|
69
|
+
continue
|
|
70
|
+
fi
|
|
71
|
+
# shellcheck disable=SC2016
|
|
72
|
+
if (cd "$wt" && bash -c "$test_cmd \"\$1\"" fwc "$t") < /dev/null > "$work/test.log" 2>&1; then
|
|
73
|
+
if [ "$allow" = 1 ]; then
|
|
74
|
+
echo "PASS ON BASE, allowed by --allow-pass-on-base: $t"
|
|
75
|
+
else
|
|
76
|
+
echo "FAIL: $t passes on the base; a new or changed test must fail without the change"
|
|
77
|
+
failures=$((failures + 1))
|
|
78
|
+
fi
|
|
79
|
+
elif [ -n "$classifier" ] && ! bash -c "$classifier \"\$1\" \"\$2\"" fwc "$work/test.log" "$t" < /dev/null > "$work/class.log" 2>&1; then
|
|
80
|
+
echo "FAIL: $t fails on the base for another reason than the change (FWC_CLASSIFIER):"
|
|
81
|
+
tail -n 20 "$work/class.log"
|
|
82
|
+
failures=$((failures + 1))
|
|
83
|
+
else
|
|
84
|
+
echo "ok: $t passes with the change and fails on the base"
|
|
85
|
+
fi
|
|
86
|
+
done <<< "$tests"
|
|
87
|
+
[ "$failures" -eq 0 ] || { echo "== fail-without-change failed: $failures"; exit 1; }
|
|
88
|
+
echo "== fail-without-change passed"
|