@kungfu-tech/buildchain 4.0.1-alpha.38 → 4.0.1-alpha.39

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.
@@ -10,6 +10,7 @@ import {
10
10
  observeDevDeliveryQueue,
11
11
  qualifyDevDeliveryWarrant,
12
12
  recoverExpiredDevDeliveryWarrant,
13
+ reconcileDevDeliveryTerminalEvidence,
13
14
  selectDevDeliveryWarrant,
14
15
  settleDevDeliveryTerminalEvent,
15
16
  submitDevDeliveryCandidate,
@@ -52,6 +53,43 @@ function exactSha(value, label) {
52
53
  return normalized;
53
54
  }
54
55
 
56
+ function terminalSourceHead(options) {
57
+ return exactSha(
58
+ options.expectedSourceHead || options.sourceHead,
59
+ "sourceHead",
60
+ );
61
+ }
62
+
63
+ function reconcileTerminalEvidenceCommand(queue, options) {
64
+ return reconcileDevDeliveryTerminalEvidence(
65
+ queue,
66
+ {
67
+ candidateId: exactRoot(options.candidateId, "candidateId"),
68
+ expectedPriorEvidenceRoot: exactRoot(
69
+ options.expectedPriorEvidenceRoot,
70
+ "expectedPriorEvidenceRoot",
71
+ ),
72
+ integrationProof:
73
+ options.integrationProof ||
74
+ jsonFile(options.integrationProofPath, "integration proof"),
75
+ reason: options.reason,
76
+ },
77
+ { now: options.now },
78
+ );
79
+ }
80
+
81
+ function requireTerminalEvidenceCas(options) {
82
+ if (
83
+ options.command === "reconcile-terminal-evidence" &&
84
+ options.execute &&
85
+ !options.expectedOldStateRoot
86
+ ) {
87
+ throw new Error(
88
+ "terminal evidence reconciliation execute requires expected-old CAS",
89
+ );
90
+ }
91
+ }
92
+
55
93
  function normalizeRepository(value) {
56
94
  const normalized = text(value);
57
95
  const match = normalized.match(/^([^/\s]+)\/([^/\s]+)$/);
@@ -245,10 +283,7 @@ function transitionFor(command, queue, options) {
245
283
  options.pullRequestNumber,
246
284
  "pullRequestNumber",
247
285
  ),
248
- sourceHead: exactSha(
249
- options.expectedSourceHead || options.sourceHead,
250
- "sourceHead",
251
- ),
286
+ sourceHead: terminalSourceHead(options),
252
287
  fencingToken: options.fencingToken,
253
288
  leaseGeneration: options.leaseGeneration,
254
289
  outcome: options.outcome,
@@ -263,6 +298,9 @@ function transitionFor(command, queue, options) {
263
298
  { now: options.now },
264
299
  );
265
300
  }
301
+ if (command === "reconcile-terminal-evidence") {
302
+ return reconcileTerminalEvidenceCommand(queue, options);
303
+ }
266
304
  if (command === "cancel-queued") {
267
305
  return cancelQueuedDevDeliveryCandidate(
268
306
  queue,
@@ -356,6 +394,7 @@ export async function runDevDeliveryCommand(optionsInput = {}, clientInput) {
356
394
  now: new Date(optionsInput.now || Date.now()).toISOString(),
357
395
  execute: bool(optionsInput.execute, false),
358
396
  };
397
+ requireTerminalEvidenceCas(options);
359
398
  if (
360
399
  options.command === "submit" &&
361
400
  options.execute &&
@@ -457,7 +496,7 @@ export async function runDevDeliveryCommand(optionsInput = {}, clientInput) {
457
496
  }
458
497
 
459
498
  function usage() {
460
- return "Usage:\n buildchain dev warrant <submit|select|heartbeat|qualify|recover|close|settle|cancel-queued|observe> --repository owner/repo --branch dev/vN/vN.M [--execute] [--output FILE] [--json]\n\nRead candidate:\n observe --read-mode v4 --read-qualification FILE --read-qualification-root sha256:... --read-typescript-revision SHA --read-rust-revision SHA --read-validator-version TOKEN [--read-evidence-output FILE]\n";
499
+ return "Usage:\n buildchain dev warrant <submit|select|heartbeat|qualify|recover|close|settle|reconcile-terminal-evidence|cancel-queued|observe> --repository owner/repo --branch dev/vN/vN.M [--execute] [--output FILE] [--json]\n\nRead candidate:\n observe --read-mode v4 --read-qualification FILE --read-qualification-root sha256:... --read-typescript-revision SHA --read-rust-revision SHA --read-validator-version TOKEN [--read-evidence-output FILE]\n";
461
500
  }
462
501
 
463
502
  async function main() {
@@ -476,6 +515,7 @@ async function main() {
476
515
  "recover",
477
516
  "close",
478
517
  "settle",
518
+ "reconcile-terminal-evidence",
479
519
  "cancel-queued",
480
520
  "observe",
481
521
  ].includes(options.command)
@@ -192,12 +192,12 @@ function replayProjectCut(cwd, base, head) {
192
192
  reason: "source-patch-unreadable",
193
193
  },
194
194
  );
195
- git(cwd, ["apply", "--cached", "--3way", "--whitespace=nowarn", "-"], {
196
- env: indexEnvironment,
197
- input: sourcePatch,
198
- encoding: "utf8",
199
- reason: "project-cut-conflict",
200
- });
195
+ const emptySourcePatch = sourcePatch.length === 0;
196
+ if (emptySourcePatch && commits.length !== 1)
197
+ throw new ProjectCutAdmissionError("empty-source-composition", "an empty source composition must be exactly one linear commit");
198
+ if (!emptySourcePatch) git(cwd, ["apply", "--cached", "--3way", "--whitespace=nowarn", "-"], {
199
+ env: indexEnvironment, input: sourcePatch, encoding: "utf8", reason: "project-cut-conflict",
200
+ });
201
201
  const candidateTreeOid = git(cwd, ["write-tree"], {
202
202
  env: indexEnvironment,
203
203
  reason: "candidate-tree-unreadable",
@@ -226,10 +226,10 @@ function replayProjectCut(cwd, base, head) {
226
226
  reason: "replay-paths-unreadable",
227
227
  },
228
228
  );
229
- if (
230
- patchId(cwd, sourcePatch) !== patchId(cwd, replayPatch) ||
231
- sourceNames !== replayNames
232
- ) {
229
+ const compositionDrifted = emptySourcePatch ? replayPatch.length !== 0 ||
230
+ sourceNames !== "" || replayNames !== "" : patchId(cwd, sourcePatch) !==
231
+ patchId(cwd, replayPatch) || sourceNames !== replayNames;
232
+ if (compositionDrifted) {
233
233
  throw new ProjectCutAdmissionError(
234
234
  "composition-drift",
235
235
  "latest-base replay changes the immutable source composition",