@cratis/pi 2.0.8 → 2.0.10
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/package/corpus/hooks/README.md +18 -1
- package/package/corpus/hooks/scripts/cratis-quality-gate.sh +35 -0
- package/package/corpus/hooks/scripts/hook-lib.sh +6 -1
- package/package/corpus/hooks/scripts/quality-gates.json +8 -3
- package/package/corpus/rules/pull-requests.md +2 -0
- package/package.json +1 -1
|
@@ -62,6 +62,23 @@ The two `within_type_attribute` patterns are not line greps — the scanner trac
|
|
|
62
62
|
blocks and type scope (positional record, multi-line declaration, or braced body), so a nullable
|
|
63
63
|
property is only reported when it really sits inside an `[EventType]`.
|
|
64
64
|
|
|
65
|
+
### Project-specific gate configuration
|
|
66
|
+
|
|
67
|
+
The shipped gates discover the repository's own solution and package, so most repositories need no
|
|
68
|
+
configuration at all. A repository whose project is not where discovery lands — several packages, a
|
|
69
|
+
frontend under `Source/<App>` — states only what differs in its own
|
|
70
|
+
`.cratis/ai/quality-gates.project.json`, which the gate merges over the managed file by gate id:
|
|
71
|
+
|
|
72
|
+
```json
|
|
73
|
+
{ "gates": [ { "id": "frontend-lint", "workingDirectory": "Source/App" } ] }
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
That file is project-owned and outside the managed manifest. **Do not put project facts into
|
|
77
|
+
`scripts/quality-gates.json`**: it is Cratis-managed, so the next managed update either reports it as
|
|
78
|
+
drift or replaces it, and the repository silently loses its own configuration. An override naming a
|
|
79
|
+
gate that does not exist is reported on stderr rather than ignored, and an unreadable override leaves
|
|
80
|
+
the managed gates running unchanged.
|
|
81
|
+
|
|
65
82
|
**Gated** (`Stop`, exit 2): the app-pinned commands from the Quality Gates table in
|
|
66
83
|
`general.md` and the steps in [`agent-stop.md`](./agent-stop.md) — Debug build, specs, Release
|
|
67
84
|
build (with `-p:CratisProxiesOutputPath=` per `general.md`, so the proxy generator does not
|
|
@@ -304,7 +321,7 @@ Each is an explicit, auditable opt-out — none of them is a default.
|
|
|
304
321
|
| `CRATIS_HOOKS_SKIP_GATE=1` | disables the quality gate |
|
|
305
322
|
| `CRATIS_HOOKS_GATE_DRYRUN=1` | prints which gates would run, and why, then exits 0 |
|
|
306
323
|
| `CRATIS_HOOKS_PATTERNS=<path>` | replaces the pattern file |
|
|
307
|
-
| `CRATIS_HOOKS_GATES=<path>` | replaces the gate file |
|
|
324
|
+
| `CRATIS_HOOKS_GATES=<path>` | replaces the gate file (the project override still merges over it) |
|
|
308
325
|
| `CRATIS_HOOKS_SUBPATH_REPORT=1` | prints every `@cratis/*` subpath reference and how it resolved, not only the failures |
|
|
309
326
|
| `CRATIS_HOOKS_IMPORT_REPORT=1` | prints every `@cratis/*` named import binding and how it resolved, not only the failures |
|
|
310
327
|
| `CRATIS_HOOKS_TYPE_REPORT=1` | prints every .NET type/attribute name the corpus mentions and how it resolved, not only the failures |
|
|
@@ -40,6 +40,41 @@ jq -e . "$config" >/dev/null 2>&1 || {
|
|
|
40
40
|
printf 'cratis-quality-gate: %s is not valid JSON — gate skipped.\n' "$config" >&2
|
|
41
41
|
exit 0
|
|
42
42
|
}
|
|
43
|
+
|
|
44
|
+
# ── Project-owned overrides ──────────────────────────────────────────────────
|
|
45
|
+
# Where a repository's own answer to "which directory does this gate build in" lives. It is
|
|
46
|
+
# outside the managed tree on purpose: a repository that instead edits the managed
|
|
47
|
+
# quality-gates.json mixes project facts into Cratis-owned content, so the next managed update
|
|
48
|
+
# either reports drift or silently discards the repository's own configuration. The override
|
|
49
|
+
# states only what differs, keyed by gate id, and nothing here needs a script fork.
|
|
50
|
+
overrides="$root/.cratis/ai/quality-gates.project.json"
|
|
51
|
+
if [ -f "$overrides" ]; then
|
|
52
|
+
if jq -e . "$overrides" >/dev/null 2>&1; then
|
|
53
|
+
merged="$(mktemp "${TMPDIR:-/tmp}/cratis-quality-gates.XXXXXX")"
|
|
54
|
+
if jq -s '
|
|
55
|
+
.[0] as $base | .[1] as $over
|
|
56
|
+
| ($over.gates // []) as $gates
|
|
57
|
+
| $base
|
|
58
|
+
+ ($over | del(.gates))
|
|
59
|
+
+ { gates: [ $base.gates[] as $gate
|
|
60
|
+
| ($gates | map(select(.id == $gate.id)) | first) as $patch
|
|
61
|
+
| if $patch == null then $gate else $gate + ($patch | del(.id)) end ] }
|
|
62
|
+
' "$config" "$overrides" >"$merged" 2>/dev/null; then
|
|
63
|
+
unknown="$(jq -r --slurpfile base "$config" '[.gates // [] | .[].id] - [$base[0].gates[].id] | .[]' "$overrides" 2>/dev/null || true)"
|
|
64
|
+
[ -n "$unknown" ] && printf 'cratis-quality-gate: %s overrides unknown gate(s): %s\n' \
|
|
65
|
+
"${overrides#"$root"/}" "$(printf '%s' "$unknown" | tr '\n' ' ')" >&2
|
|
66
|
+
config="$merged"
|
|
67
|
+
else
|
|
68
|
+
rm -f "$merged"
|
|
69
|
+
printf 'cratis-quality-gate: %s could not be merged — managed gates used unchanged.\n' \
|
|
70
|
+
"${overrides#"$root"/}" >&2
|
|
71
|
+
fi
|
|
72
|
+
else
|
|
73
|
+
printf 'cratis-quality-gate: %s is not valid JSON — managed gates used unchanged.\n' \
|
|
74
|
+
"${overrides#"$root"/}" >&2
|
|
75
|
+
fi
|
|
76
|
+
fi
|
|
77
|
+
|
|
43
78
|
[ "$(jq -r '.enabled // true' "$config")" = "true" ] || exit 0
|
|
44
79
|
|
|
45
80
|
# ── What changed in the working tree ─────────────────────────────────────────
|
|
@@ -9,13 +9,18 @@ set -euo pipefail
|
|
|
9
9
|
# ── Environment ───────────────────────────────────────────────────────────────
|
|
10
10
|
|
|
11
11
|
# Root of the repository the hook is running for.
|
|
12
|
+
#
|
|
13
|
+
# The fallback walks up from this script, which is installed at
|
|
14
|
+
# <root>/.cratis/ai/hooks/scripts/hook-lib.sh - four levels, not three. Three landed on
|
|
15
|
+
# <root>/.cratis, so without CLAUDE_PROJECT_DIR every hook read a repository whose git
|
|
16
|
+
# directory, tracked files and project files were all missing, and silently did nothing.
|
|
12
17
|
hook_repo_root() {
|
|
13
18
|
local d="${CLAUDE_PROJECT_DIR:-}"
|
|
14
19
|
if [ -n "$d" ] && [ -d "$d" ]; then
|
|
15
20
|
(cd "$d" && pwd)
|
|
16
21
|
return 0
|
|
17
22
|
fi
|
|
18
|
-
(cd "$(dirname "${BASH_SOURCE[0]}")
|
|
23
|
+
(cd "$(dirname "${BASH_SOURCE[0]}")/../../../.." && pwd)
|
|
19
24
|
}
|
|
20
25
|
|
|
21
26
|
# True when the named command is on PATH.
|
|
@@ -29,9 +29,14 @@
|
|
|
29
29
|
"an application repository, a framework repository, and (as nothing to run) in a corpus-only",
|
|
30
30
|
"repository such as this one, which carries no .NET or Node project at all.",
|
|
31
31
|
"",
|
|
32
|
-
"The override model, in order of increasing force: a repository
|
|
33
|
-
"
|
|
34
|
-
"
|
|
32
|
+
"The override model, in order of increasing force: a repository states only what differs in its",
|
|
33
|
+
"own .cratis/ai/quality-gates.project.json, which the runner merges over this file by gate id",
|
|
34
|
+
"(a project fact belongs there, never edited into this managed file, or the next managed update",
|
|
35
|
+
"reports drift or discards it); or it points CRATIS_HOOKS_GATES at a file anywhere. Neither",
|
|
36
|
+
"requires a script fork.",
|
|
37
|
+
"",
|
|
38
|
+
"Example .cratis/ai/quality-gates.project.json, for a repository whose frontend is not at the root:",
|
|
39
|
+
" { \"gates\": [ { \"id\": \"frontend-lint\", \"workingDirectory\": \"Source/App\" } ] }",
|
|
35
40
|
"",
|
|
36
41
|
"Commands are pinned to .cratis/ai/rules/general.md 'Quality Gates' and .cratis/ai/hooks/agent-stop.md.",
|
|
37
42
|
"Note the Release build passes -p:CratisProxiesOutputPath= per general.md so the proxy",
|
|
@@ -6,6 +6,8 @@ applyTo: "**/*"
|
|
|
6
6
|
|
|
7
7
|
PR descriptions serve two purposes: they help reviewers understand the change *now*, and they become the release notes that users read *later*. Write them with both audiences in mind.
|
|
8
8
|
|
|
9
|
+
**The description is the release note — it is published verbatim.** Write it as the note you want the person upgrading to read, in the repository template's sections. A generic development write-up (`## Summary`, `## Verification`, `## Testing`, a list of the files you touched, a description of how you arrived at the change) is not a release note, and shipping one makes the release history unreadable. The same applies wherever a release is produced by hand: release-notes text typed into a manual workflow run, or written straight into a published release, carries exactly the same shape and the same audience as a PR description. There is no path to a release whose notes are allowed to describe the work instead of the change.
|
|
10
|
+
|
|
9
11
|
## Description
|
|
10
12
|
|
|
11
13
|
- Follow the repository's pull request template (`.github/pull_request_template.md`).
|