@kungfu-tech/buildchain 3.0.9-alpha.12 → 3.0.9-alpha.13

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.
@@ -1,4 +1,4 @@
1
- import { createNativeProofReuseDecision } from "../packages/core/dev-delivery-warrant.js";
1
+ import { createNativeProofReuseDecision, createNativeQualificationProof } from "../packages/core/dev-delivery-warrant.js";
2
2
 
3
3
  const ROOT_PATTERN = /^sha256:[0-9a-f]{64}$/u;
4
4
 
@@ -70,6 +70,7 @@ export async function classifyNativeProofAgainstCurrent(
70
70
  const currentBase = await client.baseSha(options.branch);
71
71
  const delta = await client.baseDelta(proof.qualifiedBase, currentBase);
72
72
  const current = {
73
+ sourceHead: options.expectedHead,
73
74
  sourceIdentityRoot: options.sourceIdentityRoot,
74
75
  sourcePatchRoot: options.sourcePatchRoot,
75
76
  planRoot: options.planRoot,
@@ -88,3 +89,81 @@ export async function classifyNativeProofAgainstCurrent(
88
89
  decision: createNativeProofReuseDecision({ proof, current }),
89
90
  };
90
91
  }
92
+
93
+ export async function runNativeQualificationAttempt({
94
+ options,
95
+ warrant,
96
+ attempt,
97
+ client,
98
+ runCommand,
99
+ runNative,
100
+ composeCandidate,
101
+ writeEvidence,
102
+ }) {
103
+ await client.exactPullRequestHead(
104
+ options.pullRequestNumber,
105
+ options.expectedHead,
106
+ );
107
+ const qualifiedBase = await client.baseSha(options.branch);
108
+ composeCandidate(
109
+ options.candidateDirectory,
110
+ options.expectedHead,
111
+ qualifiedBase,
112
+ );
113
+ const nativeExecutionReceipt = await runNative({
114
+ command: options.nativeCommand,
115
+ cwd: options.candidateDirectory,
116
+ intervalMs: options.heartbeatSeconds * 1000,
117
+ executionBinding: {
118
+ repository: options.repository,
119
+ protectedBase: options.branch,
120
+ sourceHead: options.expectedHead,
121
+ qualifiedBase,
122
+ toolchainRoot: options.toolchainRoot,
123
+ environmentRoot: options.environmentRoot,
124
+ },
125
+ heartbeat: async () => {
126
+ await runCommand({
127
+ command: "heartbeat",
128
+ repository: options.repository,
129
+ branch: options.branch,
130
+ fencingToken: warrant.fencingToken,
131
+ leaseGeneration: warrant.generation,
132
+ leaseSeconds: options.leaseSeconds,
133
+ execute: true,
134
+ token: options.token,
135
+ apiUrl: options.apiUrl,
136
+ });
137
+ },
138
+ });
139
+ writeEvidence(
140
+ `native-heartbeat-attempt-${attempt}.json`,
141
+ nativeExecutionReceipt,
142
+ );
143
+ await client.exactPullRequestHead(
144
+ options.pullRequestNumber,
145
+ options.expectedHead,
146
+ );
147
+ const proof = createNativeQualificationProof({
148
+ repository: options.repository,
149
+ protectedBase: options.branch,
150
+ sourceIdentityRoot: options.sourceIdentityRoot,
151
+ sourcePatchRoot: options.sourcePatchRoot,
152
+ planRoot: options.planRoot,
153
+ closureRoot: options.closureRoot,
154
+ dependencyRoot: options.dependencyRoot,
155
+ toolchainRoot: options.toolchainRoot,
156
+ environmentRoot: options.environmentRoot,
157
+ sourceHead: options.expectedHead,
158
+ qualifiedBase,
159
+ nativeExecutionReceipt,
160
+ affectedPaths: options.affectedPaths,
161
+ shardEvidenceRoots: [
162
+ ...options.shardEvidenceRoots,
163
+ nativeExecutionReceipt.receiptRoot,
164
+ ],
165
+ qualifiedAt: new Date().toISOString(),
166
+ });
167
+ writeEvidence("native-proof.json", proof);
168
+ return proof;
169
+ }
@@ -4,7 +4,6 @@ import fs from "node:fs";
4
4
  import path from "node:path";
5
5
 
6
6
  import {
7
- createNativeQualificationProof,
8
7
  devDeliveryContentRoot,
9
8
  } from "../packages/core/dev-delivery-warrant.js";
10
9
  import { runNativeWithHeartbeat } from "./dev-delivery-native-run.mjs";
@@ -12,6 +11,7 @@ import {
12
11
  attributedGitHubBaseDelta,
13
12
  classifyNativeProofAgainstCurrent,
14
13
  replayQualifiedNativeWarrant,
14
+ runNativeQualificationAttempt,
15
15
  } from "./dev-delivery-two-phase-resume.mjs";
16
16
  import { runDevDeliveryCommand } from "./dev-delivery-warrant.mjs";
17
17
 
@@ -49,6 +49,16 @@ function exactSha(value, label) {
49
49
  return normalized;
50
50
  }
51
51
 
52
+ function exactRoot(value, label) {
53
+ const normalized = String(value || "")
54
+ .trim()
55
+ .toLowerCase();
56
+ if (!/^sha256:[0-9a-f]{64}$/u.test(normalized)) {
57
+ throw new Error(`${label} must be a sha256 content root`);
58
+ }
59
+ return normalized;
60
+ }
61
+
52
62
  function readJson(file, label) {
53
63
  if (!file) throw new Error(`${label} is required`);
54
64
  return JSON.parse(fs.readFileSync(file, "utf8"));
@@ -245,13 +255,13 @@ async function releaseFailedAttempt({
245
255
  }
246
256
 
247
257
  export async function runTwoPhaseDelivery(options, dependencies = {}) {
258
+ options.environmentRoot = exactRoot(
259
+ options.environmentRoot,
260
+ "environmentRoot",
261
+ );
248
262
  const client =
249
263
  dependencies.client ||
250
- new GitHubTwoPhaseClient({
251
- repository: options.repository,
252
- token: options.token,
253
- apiUrl: options.apiUrl,
254
- });
264
+ new GitHubTwoPhaseClient(options);
255
265
  const runCommand = dependencies.runCommand || runDevDeliveryCommand;
256
266
  const runNative = dependencies.runNative || runNativeWithHeartbeat;
257
267
  const warrantResult = readJson(options.warrantResultPath, "Warrant result");
@@ -270,7 +280,12 @@ export async function runTwoPhaseDelivery(options, dependencies = {}) {
270
280
  }
271
281
  const evidenceDirectory = path.resolve(options.evidenceDirectory);
272
282
  fs.mkdirSync(evidenceDirectory, { recursive: true });
273
- const qualifiedReplay = await replayQualifiedNativeWarrant({ warrant, pullRequestNumber: options.pullRequestNumber, expectedHead: options.expectedHead, exactPullRequestHead: (...args) => client.exactPullRequestHead(...args) });
283
+ const qualifiedReplay = await replayQualifiedNativeWarrant({
284
+ warrant,
285
+ pullRequestNumber: options.pullRequestNumber,
286
+ expectedHead: options.expectedHead,
287
+ exactPullRequestHead: (...args) => client.exactPullRequestHead(...args),
288
+ });
274
289
  if (qualifiedReplay) return qualifiedReplay;
275
290
  let proof = options.nativeProofPath
276
291
  ? readJson(options.nativeProofPath, "native proof")
@@ -287,7 +302,11 @@ export async function runTwoPhaseDelivery(options, dependencies = {}) {
287
302
  options.pullRequestNumber,
288
303
  options.expectedHead,
289
304
  );
290
- classified = await classifyNativeProofAgainstCurrent(proof, options, client);
305
+ classified = await classifyNativeProofAgainstCurrent(
306
+ proof,
307
+ options,
308
+ client,
309
+ );
291
310
  if (classified.decision.reusable) break;
292
311
  }
293
312
  if (nativeAttempts >= 2) break;
@@ -296,73 +315,23 @@ export async function runTwoPhaseDelivery(options, dependencies = {}) {
296
315
  `native proof cannot be reused (${classified?.decision.reason || "not-supplied"}) and native-command is empty`,
297
316
  );
298
317
  }
299
- await client.exactPullRequestHead(
300
- options.pullRequestNumber,
301
- options.expectedHead,
302
- );
303
- const qualifiedBase = await client.baseSha(options.branch);
304
- composeCandidate(
305
- options.candidateDirectory,
306
- options.expectedHead,
307
- qualifiedBase,
308
- );
309
318
  nativeAttempts += 1;
310
- const heartbeatReceipt = await runNative({
311
- command: options.nativeCommand,
312
- cwd: options.candidateDirectory,
313
- intervalMs: options.heartbeatSeconds * 1000,
314
- executionBinding: {
315
- repository: options.repository,
316
- protectedBase: options.branch,
317
- sourceHead: options.expectedHead,
318
- qualifiedBase,
319
- toolchainRoot: options.toolchainRoot,
320
- },
321
- heartbeat: async () => {
322
- await runCommand({
323
- command: "heartbeat",
324
- repository: options.repository,
325
- branch: options.branch,
326
- fencingToken: warrant.fencingToken,
327
- leaseGeneration: warrant.generation,
328
- leaseSeconds: options.leaseSeconds,
329
- execute: true,
330
- token: options.token,
331
- apiUrl: options.apiUrl,
332
- });
333
- },
319
+ proof = await runNativeQualificationAttempt({
320
+ options,
321
+ warrant,
322
+ attempt: nativeAttempts,
323
+ client,
324
+ runCommand,
325
+ runNative,
326
+ composeCandidate,
327
+ writeEvidence: (name, value) =>
328
+ writeJson(path.join(evidenceDirectory, name), value),
334
329
  });
335
- writeJson(
336
- path.join(
337
- evidenceDirectory,
338
- `native-heartbeat-attempt-${nativeAttempts}.json`,
339
- ),
340
- heartbeatReceipt,
330
+ classified = await classifyNativeProofAgainstCurrent(
331
+ proof,
332
+ options,
333
+ client,
341
334
  );
342
- await client.exactPullRequestHead(
343
- options.pullRequestNumber,
344
- options.expectedHead,
345
- );
346
- proof = createNativeQualificationProof({
347
- repository: options.repository,
348
- protectedBase: options.branch,
349
- sourceIdentityRoot: options.sourceIdentityRoot,
350
- sourcePatchRoot: options.sourcePatchRoot,
351
- planRoot: options.planRoot,
352
- closureRoot: options.closureRoot,
353
- dependencyRoot: options.dependencyRoot,
354
- toolchainRoot: options.toolchainRoot,
355
- environmentRoot: options.environmentRoot,
356
- qualifiedBase,
357
- affectedPaths: options.affectedPaths,
358
- shardEvidenceRoots: [
359
- ...options.shardEvidenceRoots,
360
- heartbeatReceipt.receiptRoot,
361
- ],
362
- qualifiedAt: new Date().toISOString(),
363
- });
364
- writeJson(path.join(evidenceDirectory, "native-proof.json"), proof);
365
- classified = await classifyNativeProofAgainstCurrent(proof, options, client);
366
335
  }
367
336
 
368
337
  if (!classified?.decision.reusable) {
@@ -295,6 +295,9 @@ export async function runDevDeliveryCommand(optionsInput = {}, clientInput) {
295
295
  now: new Date(optionsInput.now || Date.now()).toISOString(),
296
296
  execute: bool(optionsInput.execute, false),
297
297
  };
298
+ if (options.command === "submit" && options.execute && options.deliveryClass !== "non-native-fast") {
299
+ exactRoot(options.environmentRoot, "environmentRoot");
300
+ }
298
301
  const store =
299
302
  clientInput ||
300
303
  new GitHubDevDeliveryStore({
@@ -40,7 +40,7 @@ on:
40
40
  description: "Native toolchain root"
41
41
  required: true
42
42
  environment-root:
43
- description: "Native execution environment contract root"
43
+ description: "Exact native execution environment contract root bound before native spawn"
44
44
  required: true
45
45
  affected-paths-json:
46
46
  description: "JSON array representing the affected closure"