@patronage/factory-ci 1.0.0-alpha.37 → 1.0.0-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.
- package/README.md +1 -1
- package/dist/.build-fingerprint.json +1 -1
- package/dist/index.d.ts +11 -1
- package/dist/index.js +8 -2
- package/package.json +1 -1
- package/src/preview-proof-lifecycle.ts +26 -7
package/README.md
CHANGED
|
@@ -459,7 +459,7 @@ import { previewProofLifecycle } from "@patronage/factory-ci";
|
|
|
459
459
|
|
|
460
460
|
Events:
|
|
461
461
|
|
|
462
|
-
- `begin` — the pre-deploy gate.
|
|
462
|
+
- `begin` — the pre-deploy gate. A mismatched branch name is accepted at the PR head SHA. A dirty tree or a SHA mismatch is refused, as are a draft, a closed PR, and any stage the disposable grammar does not own for this PR head. Does not create a registration.
|
|
463
463
|
- `register` — the same identity gate, plus passed smoke, passed convergence, and a passing three-way-bound evidence envelope at this head. Writes a registration with `cleanup.outcome: "pending"`. Cleanup, smoke, and convergence status live only on their nested objects, so no top-level copy can diverge from them. Machine evidence is that envelope; this module never posts a PR comment.
|
|
464
464
|
- `cleanup` — records an explicit passed or failed outcome onto an existing registration. Cleanup is never inferred from whether the stage still exists. `recover` is the failed-to-resolved re-entry of this event.
|
|
465
465
|
|
package/dist/index.d.ts
CHANGED
|
@@ -178,7 +178,10 @@ declare const localPreviewStage: (options: LocalPreviewStageOptions) => LocalPre
|
|
|
178
178
|
*
|
|
179
179
|
* Proof sits entirely on one side of GitHub's draft/ready boundary
|
|
180
180
|
* (paitronage#1089): a draft candidate cannot begin or register. Machine
|
|
181
|
-
* evidence is the envelope at head (#355), never a PR comment.
|
|
181
|
+
* evidence is the envelope at head (#355), never a PR comment. The SHA and
|
|
182
|
+
* repository bind that proof (#1312): a detached HEAD or a branch name that
|
|
183
|
+
* differs from the PR head is the same commit, and the registration records
|
|
184
|
+
* `branch: "(detached)"`.
|
|
182
185
|
*/
|
|
183
186
|
type PreviewProofCleanupStatus = "failed" | "passed" | "pending";
|
|
184
187
|
interface PreviewProofCandidate {
|
|
@@ -206,6 +209,13 @@ interface PreviewProofCleanupOutcome {
|
|
|
206
209
|
readonly runUrl?: string;
|
|
207
210
|
}
|
|
208
211
|
interface PreviewProofRegistration {
|
|
212
|
+
/**
|
|
213
|
+
* Present only when the checkout is detached or its branch name is not the
|
|
214
|
+
* PR head. The `(detached)` sentinel is on the lifecycle registration
|
|
215
|
+
* `transition` returns, and `previewProofInventory` does not persist it.
|
|
216
|
+
* It is not a branch to check out.
|
|
217
|
+
*/
|
|
218
|
+
readonly branch?: "(detached)";
|
|
209
219
|
readonly cleanup: {
|
|
210
220
|
readonly evidence: readonly string[];
|
|
211
221
|
readonly outcome: PreviewProofCleanupStatus;
|
package/dist/index.js
CHANGED
|
@@ -74,6 +74,12 @@ const factoryCandidateOrPushCondition = (candidateCondition) => {
|
|
|
74
74
|
//#endregion
|
|
75
75
|
//#region src/preview-proof-lifecycle.ts
|
|
76
76
|
const EMPTY_STATE = Object.freeze({ registrations: Object.freeze([]) });
|
|
77
|
+
const DETACHED_PROOF_BRANCH = "(detached)";
|
|
78
|
+
/**
|
|
79
|
+
* The branch name adds no guarantee. Record the sentinel unless the checkout
|
|
80
|
+
* is actually on the PR head branch.
|
|
81
|
+
*/
|
|
82
|
+
const proofBranch = (candidate) => candidate.branch.length > 0 && candidate.branch === candidate.prHeadRef ? void 0 : DETACHED_PROOF_BRANCH;
|
|
77
83
|
const registrationKey$1 = (row) => `${row.stack}:${row.stage}`;
|
|
78
84
|
const disposableStage = (stage, candidate) => {
|
|
79
85
|
const parsed = parseLocalPreviewStage(stage, {
|
|
@@ -88,9 +94,7 @@ const assertAdmissibleCandidate = (candidate, stage) => {
|
|
|
88
94
|
if (candidate.prState !== "OPEN") throw new Error("Preview proof requires an open pull request.");
|
|
89
95
|
const errors = [];
|
|
90
96
|
if (candidate.dirty) errors.push("Worktree must be clean.");
|
|
91
|
-
if (!candidate.branch) errors.push("Detached HEAD is not trusted for preview proof.");
|
|
92
97
|
if (candidate.headSha !== candidate.prHeadSha) errors.push("HEAD is stale for the current PR head.");
|
|
93
|
-
if (candidate.branch !== candidate.prHeadRef) errors.push("Current branch does not match the PR head branch.");
|
|
94
98
|
if (candidate.repository !== candidate.prRepository) errors.push("PR head repository does not match the current repository.");
|
|
95
99
|
if (errors.length > 0) throw new Error(errors.join("\n"));
|
|
96
100
|
return disposableStage(stage, candidate);
|
|
@@ -116,7 +120,9 @@ const transition = (state, event) => {
|
|
|
116
120
|
if (event.convergence.status !== "passed") throw new Error("Preview proof requires passed convergence evidence.");
|
|
117
121
|
if (!event.stack || !event.url) throw new Error("Preview proof registration requires a stack and URL.");
|
|
118
122
|
assertPassingEnvelope(event.evidence, event.candidate.headSha);
|
|
123
|
+
const branch = proofBranch(event.candidate);
|
|
119
124
|
return replaceRegistration$1(state, {
|
|
125
|
+
...branch === void 0 ? {} : { branch },
|
|
120
126
|
cleanup: {
|
|
121
127
|
evidence: [],
|
|
122
128
|
outcome: "pending"
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@patronage/factory-ci",
|
|
3
|
-
"version": "1.0.0-alpha.
|
|
3
|
+
"version": "1.0.0-alpha.39",
|
|
4
4
|
"description": "Deep CI and deploy building blocks for Patronage factory projects: workflow source artifacts, hosted diff classification, Alchemy entry execution, and disposable-stage semantics",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"alchemy",
|
|
@@ -11,7 +11,10 @@ import type { LocalPreviewStage } from "./disposable-stage.ts";
|
|
|
11
11
|
*
|
|
12
12
|
* Proof sits entirely on one side of GitHub's draft/ready boundary
|
|
13
13
|
* (paitronage#1089): a draft candidate cannot begin or register. Machine
|
|
14
|
-
* evidence is the envelope at head (#355), never a PR comment.
|
|
14
|
+
* evidence is the envelope at head (#355), never a PR comment. The SHA and
|
|
15
|
+
* repository bind that proof (#1312): a detached HEAD or a branch name that
|
|
16
|
+
* differs from the PR head is the same commit, and the registration records
|
|
17
|
+
* `branch: "(detached)"`.
|
|
15
18
|
*/
|
|
16
19
|
|
|
17
20
|
export type PreviewProofCleanupStatus = "failed" | "passed" | "pending";
|
|
@@ -44,6 +47,13 @@ export interface PreviewProofCleanupOutcome {
|
|
|
44
47
|
}
|
|
45
48
|
|
|
46
49
|
export interface PreviewProofRegistration {
|
|
50
|
+
/**
|
|
51
|
+
* Present only when the checkout is detached or its branch name is not the
|
|
52
|
+
* PR head. The `(detached)` sentinel is on the lifecycle registration
|
|
53
|
+
* `transition` returns, and `previewProofInventory` does not persist it.
|
|
54
|
+
* It is not a branch to check out.
|
|
55
|
+
*/
|
|
56
|
+
readonly branch?: "(detached)";
|
|
47
57
|
readonly cleanup: {
|
|
48
58
|
readonly evidence: readonly string[];
|
|
49
59
|
readonly outcome: PreviewProofCleanupStatus;
|
|
@@ -111,6 +121,19 @@ const EMPTY_STATE: PreviewProofLifecycleState = Object.freeze({
|
|
|
111
121
|
registrations: Object.freeze([]),
|
|
112
122
|
});
|
|
113
123
|
|
|
124
|
+
const DETACHED_PROOF_BRANCH = "(detached)" as const;
|
|
125
|
+
|
|
126
|
+
/**
|
|
127
|
+
* The branch name adds no guarantee. Record the sentinel unless the checkout
|
|
128
|
+
* is actually on the PR head branch.
|
|
129
|
+
*/
|
|
130
|
+
const proofBranch = (
|
|
131
|
+
candidate: PreviewProofCandidate
|
|
132
|
+
): typeof DETACHED_PROOF_BRANCH | undefined =>
|
|
133
|
+
candidate.branch.length > 0 && candidate.branch === candidate.prHeadRef
|
|
134
|
+
? undefined
|
|
135
|
+
: DETACHED_PROOF_BRANCH;
|
|
136
|
+
|
|
114
137
|
const registrationKey = (row: {
|
|
115
138
|
readonly stack: string;
|
|
116
139
|
readonly stage: string;
|
|
@@ -149,15 +172,9 @@ const assertAdmissibleCandidate = (
|
|
|
149
172
|
if (candidate.dirty) {
|
|
150
173
|
errors.push("Worktree must be clean.");
|
|
151
174
|
}
|
|
152
|
-
if (!candidate.branch) {
|
|
153
|
-
errors.push("Detached HEAD is not trusted for preview proof.");
|
|
154
|
-
}
|
|
155
175
|
if (candidate.headSha !== candidate.prHeadSha) {
|
|
156
176
|
errors.push("HEAD is stale for the current PR head.");
|
|
157
177
|
}
|
|
158
|
-
if (candidate.branch !== candidate.prHeadRef) {
|
|
159
|
-
errors.push("Current branch does not match the PR head branch.");
|
|
160
|
-
}
|
|
161
178
|
if (candidate.repository !== candidate.prRepository) {
|
|
162
179
|
errors.push("PR head repository does not match the current repository.");
|
|
163
180
|
}
|
|
@@ -233,8 +250,10 @@ const transition = (
|
|
|
233
250
|
throw new Error("Preview proof registration requires a stack and URL.");
|
|
234
251
|
}
|
|
235
252
|
assertPassingEnvelope(event.evidence, event.candidate.headSha);
|
|
253
|
+
const branch = proofBranch(event.candidate);
|
|
236
254
|
|
|
237
255
|
return replaceRegistration(state, {
|
|
256
|
+
...(branch === undefined ? {} : { branch }),
|
|
238
257
|
cleanup: { evidence: [], outcome: "pending" },
|
|
239
258
|
convergence: {
|
|
240
259
|
detail: event.convergence.detail,
|