@plannotator/pi-extension 0.26.0 → 0.26.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.
- package/generated/gitbutler-core.ts +62 -6
- package/package.json +1 -1
- package/plannotator.html +2 -2
- package/review-editor.html +2 -2
|
@@ -43,6 +43,32 @@ const VERSION_TIMEOUT_MS = 5_000;
|
|
|
43
43
|
const OBJECT_ID_RE = /^(?:[0-9a-f]{40}|[0-9a-f]{64})$/i;
|
|
44
44
|
const STATUS_CACHE_MS = 1_000;
|
|
45
45
|
|
|
46
|
+
/**
|
|
47
|
+
* GitButler has shipped two spellings of the global JSON output flag:
|
|
48
|
+
* 0.21.x accepts only `--format json` (gitbutlerapp/gitbutler#14061) while
|
|
49
|
+
* 0.22.0+ accepts only `--json` (gitbutlerapp/gitbutler#15026), and each
|
|
50
|
+
* release rejects the other spelling as an unknown argument. The primary
|
|
51
|
+
* spelling matches GITBUTLER_MIN_VERSION; when a status call fails with
|
|
52
|
+
* clap's unexpected-argument rejection for the exact flag we passed, the
|
|
53
|
+
* other spelling is tried once. Any other failure propagates unchanged.
|
|
54
|
+
*/
|
|
55
|
+
interface GitButlerStatusSyntax {
|
|
56
|
+
args: readonly string[];
|
|
57
|
+
/** The flag clap names in its unexpected-argument rejection. */
|
|
58
|
+
flag: string;
|
|
59
|
+
label: string;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
const GITBUTLER_STATUS_SYNTAXES: readonly [GitButlerStatusSyntax, GitButlerStatusSyntax] = [
|
|
63
|
+
{ args: ["--format", "json", "status"], flag: "--format", label: "but --format json status" },
|
|
64
|
+
{ args: ["--json", "status"], flag: "--json", label: "but --json status" },
|
|
65
|
+
];
|
|
66
|
+
|
|
67
|
+
/** Narrow sniff for clap rejecting exactly the JSON flag this syntax passed. */
|
|
68
|
+
function isUnexpectedArgumentRejection(result: GitCommandResult, flag: string): boolean {
|
|
69
|
+
return result.exitCode !== 0 && result.stderr.includes(`unexpected argument '${flag}'`);
|
|
70
|
+
}
|
|
71
|
+
|
|
46
72
|
/** Runtime operations needed by the shared GitButler provider. */
|
|
47
73
|
export interface ReviewGitButlerRuntime extends ReviewGitRuntime {
|
|
48
74
|
runBut(args: string[], options?: GitCommandOptions): Promise<GitCommandResult>;
|
|
@@ -68,7 +94,7 @@ export interface GitButlerStack {
|
|
|
68
94
|
branches: GitButlerBranch[];
|
|
69
95
|
}
|
|
70
96
|
|
|
71
|
-
/** Validated subset of `but --format json status
|
|
97
|
+
/** Validated subset of `but --format json status` (0.21.x) / `but --json status` (0.22.0+). */
|
|
72
98
|
export interface GitButlerStatus {
|
|
73
99
|
mergeBase: GitButlerCommit;
|
|
74
100
|
stacks: GitButlerStack[];
|
|
@@ -154,12 +180,15 @@ function parseStack(value: unknown, path: string): GitButlerStack {
|
|
|
154
180
|
}
|
|
155
181
|
|
|
156
182
|
/** Parse and validate the status fields Plannotator relies on. Unknown fields are allowed. */
|
|
157
|
-
export function parseGitButlerStatus(
|
|
183
|
+
export function parseGitButlerStatus(
|
|
184
|
+
output: string,
|
|
185
|
+
commandLabel = GITBUTLER_STATUS_SYNTAXES[0].label,
|
|
186
|
+
): GitButlerStatus {
|
|
158
187
|
let decoded: unknown;
|
|
159
188
|
try {
|
|
160
189
|
decoded = JSON.parse(output);
|
|
161
190
|
} catch {
|
|
162
|
-
throw new GitButlerContractError(
|
|
191
|
+
throw new GitButlerContractError(`GitButler returned invalid JSON from \`${commandLabel}\`.`);
|
|
163
192
|
}
|
|
164
193
|
|
|
165
194
|
const root = requireRecord(decoded, "root");
|
|
@@ -203,6 +232,8 @@ function versionAtLeast(actual: ParsedVersion, minimum: ParsedVersion): boolean
|
|
|
203
232
|
}
|
|
204
233
|
|
|
205
234
|
const versionChecks = new WeakMap<ReviewGitButlerRuntime, Promise<void>>();
|
|
235
|
+
/** Index into GITBUTLER_STATUS_SYNTAXES of the spelling this runtime's CLI last accepted. */
|
|
236
|
+
const statusSyntaxPreferences = new WeakMap<ReviewGitButlerRuntime, 0 | 1>();
|
|
206
237
|
const statusCaches = new WeakMap<
|
|
207
238
|
ReviewGitButlerRuntime,
|
|
208
239
|
Map<string, { expiresAt: number; inFlight: boolean; status: Promise<GitButlerStatus> }>
|
|
@@ -256,17 +287,42 @@ async function loadStatus(runtime: ReviewGitButlerRuntime, cwd: string): Promise
|
|
|
256
287
|
if (existing && (existing.inFlight || existing.expiresAt > now)) return existing.status;
|
|
257
288
|
|
|
258
289
|
const status = (async () => {
|
|
259
|
-
const
|
|
290
|
+
const preferredIndex = statusSyntaxPreferences.get(runtime) ?? 0;
|
|
291
|
+
let syntaxIndex = preferredIndex;
|
|
292
|
+
let syntax = GITBUTLER_STATUS_SYNTAXES[syntaxIndex];
|
|
293
|
+
let result = await runtime.runBut([...syntax.args], {
|
|
260
294
|
cwd,
|
|
261
295
|
timeoutMs: STATUS_TIMEOUT_MS,
|
|
262
296
|
});
|
|
297
|
+
if (isUnexpectedArgumentRejection(result, syntax.flag)) {
|
|
298
|
+
// This CLI does not know this spelling of the JSON flag; try the other
|
|
299
|
+
// spelling exactly once. Only clap's unexpected-argument rejection for
|
|
300
|
+
// the flag we passed triggers the retry — a real status failure must
|
|
301
|
+
// keep failing loudly below.
|
|
302
|
+
syntaxIndex = preferredIndex === 0 ? 1 : 0;
|
|
303
|
+
const fallback = GITBUTLER_STATUS_SYNTAXES[syntaxIndex];
|
|
304
|
+
const retried = await runtime.runBut([...fallback.args], {
|
|
305
|
+
cwd,
|
|
306
|
+
timeoutMs: STATUS_TIMEOUT_MS,
|
|
307
|
+
});
|
|
308
|
+
if (isUnexpectedArgumentRejection(retried, fallback.flag)) {
|
|
309
|
+
throw new GitButlerContractError(
|
|
310
|
+
`GitButler rejected both \`${syntax.label}\` and \`${fallback.label}\`; ` +
|
|
311
|
+
`Plannotator requires GitButler ${GITBUTLER_MIN_VERSION} or newer.`,
|
|
312
|
+
);
|
|
313
|
+
}
|
|
314
|
+
syntax = fallback;
|
|
315
|
+
result = retried;
|
|
316
|
+
}
|
|
263
317
|
if (result.exitCode !== 0) {
|
|
264
318
|
const detail = result.stderr.trim();
|
|
265
319
|
throw new GitButlerContractError(
|
|
266
|
-
`GitButler status failed${detail ? `: ${detail}` : ` with exit code ${result.exitCode}`}`,
|
|
320
|
+
`GitButler status (\`${syntax.label}\`) failed${detail ? `: ${detail}` : ` with exit code ${result.exitCode}`}`,
|
|
267
321
|
);
|
|
268
322
|
}
|
|
269
|
-
|
|
323
|
+
const parsed = parseGitButlerStatus(result.stdout, syntax.label);
|
|
324
|
+
statusSyntaxPreferences.set(runtime, syntaxIndex);
|
|
325
|
+
return parsed;
|
|
270
326
|
})();
|
|
271
327
|
const entry = { expiresAt: 0, inFlight: true, status };
|
|
272
328
|
cache.set(cwd, entry);
|
package/package.json
CHANGED