@cratis/pi 7.68.0 → 7.68.1

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.
@@ -4,9 +4,9 @@
4
4
 
5
5
  import { spawnSync } from 'node:child_process';
6
6
  import { createHash } from 'node:crypto';
7
- import { mkdirSync, readFileSync, readdirSync, statSync, writeFileSync, renameSync } from 'node:fs';
7
+ import { existsSync, mkdirSync, readFileSync, readdirSync, statSync, writeFileSync, renameSync } from 'node:fs';
8
8
  import { homedir } from 'node:os';
9
- import { isAbsolute, join, resolve } from 'node:path';
9
+ import { dirname, isAbsolute, join, resolve } from 'node:path';
10
10
  import { parseArgs } from 'node:util';
11
11
  import { Script } from 'node:vm';
12
12
  import { fileURLToPath } from 'node:url';
@@ -16,6 +16,7 @@ const commandTimeout = 15_000;
16
16
  const reviewedRulesRef = '40125b4fcae388e62ed9471edfffd0e991422f0a';
17
17
  const hookMode = process.argv[2] === '--hook';
18
18
  let claudeHook = false;
19
+ let invocation = {};
19
20
  const hookWarnings = [];
20
21
  const warningLine = line => /^(::warning\b|::notice title=Release notes (?:drift )?not checked|Warning:)/.test(line);
21
22
  const warning = message => {
@@ -25,6 +26,23 @@ const warning = message => {
25
26
  const run = (command, args, options = {}) => spawnSync(command, args, {
26
27
  encoding: 'utf8', timeout: commandTimeout, maxBuffer: 32 * 1024 * 1024, ...options,
27
28
  });
29
+ // A bare ENOENT cannot be told apart from a path resolved against the wrong directory, a copy of this
30
+ // checker from another checkout, or a file that is not there yet. Naming all of those lets the next
31
+ // occurrence be diagnosed instead of retried.
32
+ function readBodyFile(value) {
33
+ const resolved = resolve(value);
34
+ try {
35
+ return readFileSync(resolved, 'utf8');
36
+ } catch (error) {
37
+ const parent = dirname(resolved);
38
+ const details = [`--body-file value: ${value}`, `path tried: ${resolved}`,
39
+ `parent directory exists: ${existsSync(parent)}`, `file exists now: ${existsSync(resolved)}`,
40
+ `checker working directory: ${process.cwd()}`,
41
+ ...Object.entries(invocation).map(([name, detail]) => `${name}: ${detail}`),
42
+ `checker script: ${fileURLToPath(import.meta.url)}`];
43
+ throw new Error(`cannot read the pull-request body file (${error.code || error.message}); ${details.join('; ')}`, { cause: error });
44
+ }
45
+ }
28
46
  const gh = (args, options = {}) => {
29
47
  const result = run('gh', args, options);
30
48
  if (result.error || result.status !== 0) throw new Error(`gh ${args[0]} failed: ${result.stderr?.trim() || result.error?.message || result.status}`);
@@ -156,7 +174,7 @@ function check(argv, repositoryCallers, lookupOptions) {
156
174
  for (const label of split(values['add-label'])) labels.add(label);
157
175
  const intent = intents.filter(label => labels.has(label));
158
176
  if (intent.length !== 1) throw new Error(`Exactly one release-intent label is required (major/minor/patch/no-release); found ${intent.join(', ') || 'none'}.`);
159
- const body = values['body-file'] ? readFileSync(resolve(values['body-file']), 'utf8') : pull.body || '';
177
+ const body = values['body-file'] ? readBodyFile(values['body-file']) : pull.body || '';
160
178
  let repository = values.repo;
161
179
  let defaultBranch = git(['symbolic-ref', '--short', 'refs/remotes/origin/HEAD']).replace(/^origin\//, '') || 'main';
162
180
  try {
@@ -324,6 +342,7 @@ function hook() {
324
342
  if (typeof text !== 'string' || !/\bgh\s+pr\s+(create|edit)\b/.test(text.replace(/\\\r?\n/g, ''))) return 0;
325
343
  // Match each simple command, never the same text inside an echo/grep argument.
326
344
  let cwd = payload.cwd || process.cwd();
345
+ invocation = { 'hook payload directory': payload.cwd || '(none)' };
327
346
  let knownDirectory = true;
328
347
  let parsed;
329
348
  try { parsed = commands(text); } catch (error) { if (!optedIn(cwd)) return 0; throw error; }
@@ -386,6 +405,7 @@ function hook() {
386
405
  if (targetedRepository) lookupEnvironment.GH_REPO = targetedRepository;
387
406
  else delete lookupEnvironment.GH_REPO;
388
407
  process.chdir(cwd);
408
+ invocation['command directory'] = cwd;
389
409
  const code = check(args, repositoryCallers, { env: lookupEnvironment });
390
410
  if (code !== 0 && code !== 3) throw new Error('The pull-request body or release intent failed cratis-check-pr. Fix the reported violations before retrying.');
391
411
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cratis/pi",
3
- "version": "7.68.0",
3
+ "version": "7.68.1",
4
4
  "description": "Configuration-aware Cratis AI integration for Pi",
5
5
  "type": "module",
6
6
  "files": [