@cratis/pi 7.84.1 → 7.84.2
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.
|
@@ -230,8 +230,8 @@ inside the command (`CRATIS_HOOKS_ALLOW_STORE_MUTATIONS=1 cratis …`) changes n
|
|
|
230
230
|
|
|
231
231
|
The Bash guard is a no-op unless **both** target-repository opt-in conditions hold: the target is under
|
|
232
232
|
`github.com/Cratis/`, and its `.github/workflows` contains a reusable Cratis/Workflows
|
|
233
|
-
`verify-
|
|
234
|
-
`verify-release-intent`
|
|
233
|
+
`verify-pull-request-policy` caller. The legacy `verify-release-notes`, `verify-semver-label` and
|
|
234
|
+
`verify-release-intent` callers it replaced are still recognized. Installing the corpus or `@cratis/pi` alone does not opt in a
|
|
235
235
|
consumer or personal repository. This scope applies to both Claude Code and Pi; the direct checker
|
|
236
236
|
remains available explicitly in any repository. As in `gh`, an edit's PR URL takes priority over
|
|
237
237
|
`--repo` / `-R`, then literal leading or inherited `GH_REPO`, then the current directory's `origin`.
|
|
@@ -258,14 +258,17 @@ node .cratis/ai/hooks/scripts/cratis-check-pr.mjs --body-file .ai-work/pr-body.m
|
|
|
258
258
|
node .cratis/ai/hooks/scripts/cratis-check-pr.mjs --body-file .ai-work/pr-body.md --pr 123 --strict
|
|
259
259
|
```
|
|
260
260
|
|
|
261
|
-
The checker fetches both marked programs
|
|
262
|
-
|
|
261
|
+
The checker fetches both marked programs (`release-notes` and `release-notes-drift`) from
|
|
262
|
+
Cratis/Workflows' `verify-pull-request-policy.yml` at the ref in the repository's own policy caller,
|
|
263
|
+
and ignores the other programs marked in that file. Without a policy caller it fetches them from
|
|
264
|
+
`verify-release-notes.yml` at the ref in a legacy release-notes caller. If there is neither,
|
|
263
265
|
it uses the reviewed pin `40125b4fcae388e62ed9471edfffd0e991422f0a`. Floating callers such as `@main`
|
|
264
266
|
fetch the programs at that ref; if the fetch fails, they use the bundled programs from the reviewed
|
|
265
267
|
pin with a warning, so body validation is never silently skipped. Programs receive only the proposed PR
|
|
266
268
|
metadata and `PATH`, `HOME`, and `LANG`, not inherited credentials or Node runtime options.
|
|
267
269
|
The exact programs are cached by workflow blob SHA under
|
|
268
|
-
`${XDG_CACHE_HOME:-~/.cache}/cratis/
|
|
270
|
+
`${XDG_CACHE_HOME:-~/.cache}/cratis/verify-pull-request-policy/<ref>/<sha>.cjs` for a policy caller and
|
|
271
|
+
`${XDG_CACHE_HOME:-~/.cache}/cratis/release-notes/<ref>/<sha>.cjs` otherwise. Offline, the newest complete
|
|
269
272
|
cached copy for that same pin is used with a warning. With no cache it reports **unchecked** and
|
|
270
273
|
exits **3**; the hook passes through with that warning. Successful checks are silent in hook mode;
|
|
271
274
|
real warnings go to Pi's context or Claude Code's JSON `systemMessage` and `additionalContext`. Exit **0** means no violations; **1** means invalid notes or
|
|
@@ -53,8 +53,9 @@ const git = (args, cwd = process.cwd()) => {
|
|
|
53
53
|
return result.status === 0 ? result.stdout.trim() : '';
|
|
54
54
|
};
|
|
55
55
|
|
|
56
|
+
// The combined pull-request policy caller opts in, as do the legacy per-check callers it replaced.
|
|
56
57
|
function workflowCallers(source) {
|
|
57
|
-
return [...source.matchAll(/^\s*uses:\s*['"]?Cratis\/Workflows\/\.github\/workflows\/(verify-(?:release-notes|semver-label|release-intent))\.yml@([^\s'"#]+)/gmi)]
|
|
58
|
+
return [...source.matchAll(/^\s*uses:\s*['"]?Cratis\/Workflows\/\.github\/workflows\/(verify-(?:pull-request-policy|release-notes|semver-label|release-intent))\.yml@([^\s'"#]+)/gmi)]
|
|
58
59
|
.map(match => ({ name: match[1], ref: match[2] }));
|
|
59
60
|
}
|
|
60
61
|
|
|
@@ -117,12 +118,27 @@ function programs(workflow) {
|
|
|
117
118
|
return bundle;
|
|
118
119
|
}
|
|
119
120
|
|
|
121
|
+
// Workflows that carry the marked release-note programs, in order of preference. Each has its own
|
|
122
|
+
// cache root so a bundle from one file is never offered for the other at the same ref.
|
|
123
|
+
const ruleSources = [
|
|
124
|
+
{ caller: 'verify-pull-request-policy', file: 'verify-pull-request-policy.yml', cache: 'verify-pull-request-policy' },
|
|
125
|
+
{ caller: 'verify-release-notes', file: 'verify-release-notes.yml', cache: 'release-notes' },
|
|
126
|
+
];
|
|
127
|
+
|
|
128
|
+
function ruleSource(repositoryCallers) {
|
|
129
|
+
for (const source of ruleSources) {
|
|
130
|
+
const caller = repositoryCallers.find(candidate => candidate.name === source.caller);
|
|
131
|
+
if (caller) return { ...source, ref: caller.ref };
|
|
132
|
+
}
|
|
133
|
+
return { ...ruleSources.find(source => source.caller === 'verify-release-notes'), ref: reviewedRulesRef };
|
|
134
|
+
}
|
|
135
|
+
|
|
120
136
|
function rules(warn, repositoryCallers) {
|
|
121
|
-
const
|
|
137
|
+
const { file: workflowFile, cache: cacheName, ref } = ruleSource(repositoryCallers);
|
|
122
138
|
const floating = !/^[a-f0-9]{40}$/i.test(ref);
|
|
123
|
-
const cache = join(process.env.XDG_CACHE_HOME || join(homedir(), '.cache'), 'cratis',
|
|
139
|
+
const cache = join(process.env.XDG_CACHE_HOME || join(homedir(), '.cache'), 'cratis', cacheName, ref);
|
|
124
140
|
try {
|
|
125
|
-
const workflow = gh(['api', '-H', 'Accept: application/vnd.github.raw', `repos/Cratis/Workflows/contents/.github/workflows
|
|
141
|
+
const workflow = gh(['api', '-H', 'Accept: application/vnd.github.raw', `repos/Cratis/Workflows/contents/.github/workflows/${workflowFile}?ref=${encodeURIComponent(ref)}`]);
|
|
126
142
|
const bundle = programs(workflow);
|
|
127
143
|
const bytes = Buffer.from(workflow);
|
|
128
144
|
const sha = createHash('sha1').update(`blob ${bytes.length}\0`).update(bytes).digest('hex');
|
|
@@ -136,7 +152,7 @@ function rules(warn, repositoryCallers) {
|
|
|
136
152
|
if (floating || ref === reviewedRulesRef) {
|
|
137
153
|
const bundled = fileURLToPath(new URL('./cratis-release-notes-reviewed.cjs', import.meta.url));
|
|
138
154
|
new Script(readFileSync(bundled, 'utf8'));
|
|
139
|
-
warn(`Could not fetch release-note rules at ${ref}; using bundled reviewed ${reviewedRulesRef}. ${error.message}`);
|
|
155
|
+
warn(`Could not fetch release-note rules from ${workflowFile} at ${ref}; using bundled reviewed ${reviewedRulesRef}. ${error.message}`);
|
|
140
156
|
return bundled;
|
|
141
157
|
}
|
|
142
158
|
try {
|
|
@@ -148,12 +164,12 @@ function rules(warn, repositoryCallers) {
|
|
|
148
164
|
const source = readFileSync(file, 'utf8');
|
|
149
165
|
if (!source.includes('// cratis:program release-notes\n') || !source.includes('// cratis:program release-notes-drift\n')) continue;
|
|
150
166
|
new Script(source);
|
|
151
|
-
warn(`Could not fetch release-note rules; using cached ${file}. ${error.message}`);
|
|
167
|
+
warn(`Could not fetch release-note rules from ${workflowFile}; using cached ${file}. ${error.message}`);
|
|
152
168
|
return file;
|
|
153
169
|
} catch { /* Try the next complete cache entry. */ }
|
|
154
170
|
}
|
|
155
171
|
} catch { /* No cache available. */ }
|
|
156
|
-
warn(`unchecked: could not fetch release-note rules and nothing is cached. ${error.message}`);
|
|
172
|
+
warn(`unchecked: could not fetch release-note rules from ${workflowFile} at ${ref} and nothing is cached. ${error.message}`);
|
|
157
173
|
return undefined;
|
|
158
174
|
}
|
|
159
175
|
}
|
|
@@ -24,7 +24,7 @@ if ! command -v node >/dev/null 2>&1; then
|
|
|
24
24
|
exit 0
|
|
25
25
|
fi
|
|
26
26
|
root=$(git rev-parse --show-toplevel 2>/dev/null || true)
|
|
27
|
-
if ! grep -Eq "^[[:space:]]*uses:[[:space:]]*['\"]?Cratis/Workflows/\\.github/workflows/verify-(release-notes|semver-label|release-intent)\\.yml@" "$root"/.github/workflows/*.yml "$root"/.github/workflows/*.yaml 2>/dev/null; then
|
|
27
|
+
if ! grep -Eq "^[[:space:]]*uses:[[:space:]]*['\"]?Cratis/Workflows/\\.github/workflows/verify-(pull-request-policy|release-notes|semver-label|release-intent)\\.yml@" "$root"/.github/workflows/*.yml "$root"/.github/workflows/*.yaml 2>/dev/null; then
|
|
28
28
|
exit 0
|
|
29
29
|
fi
|
|
30
30
|
# JSON makes fail-open warnings visible to Claude; stderr exposes the same warning to Pi.
|