@gfdelarue/testkit 0.3.7 → 0.4.0
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/dist/cli/command-catalog.d.ts +0 -127
- package/dist/cli/command-catalog.js +1 -1
- package/dist/cli/command-registry.d.ts +1 -0
- package/dist/cli/command-registry.js +1 -1
- package/dist/cli/entrypoint.js +1 -1
- package/dist/cli/framework/execute.d.ts +1 -0
- package/dist/cli/framework/execute.js +6 -6
- package/dist/cli/index.js +1 -1
- package/dist/cli/version-drift.js +7 -7
- package/dist/config/authoring/index.d.ts +0 -5
- package/dist/config/loader/index.d.ts +0 -1
- package/dist/config/loader/index.js +1 -1
- package/dist/evidence/bundles.d.ts +2 -3
- package/dist/evidence/bundles.js +1 -1
- package/dist/evidence/contracts.d.ts +0 -8
- package/dist/evidence/index.d.ts +1 -1
- package/dist/evidence/index.js +1 -1
- package/dist/process/execa-bundle.json +1 -1
- package/package.json +3 -10
- package/testkit-config-schema.md +0 -23
- package/dist/app/cloud/campaigns.d.ts +0 -6
- package/dist/app/cloud/campaigns.js +0 -1
- package/dist/app/cloud/control-actions.d.ts +0 -12
- package/dist/app/cloud/control-actions.js +0 -2
- package/dist/app/cloud/control-links.d.ts +0 -5
- package/dist/app/cloud/control-links.js +0 -1
- package/dist/app/cloud/control-read.d.ts +0 -24
- package/dist/app/cloud/control-read.js +0 -1
- package/dist/app/cloud/control-resources.d.ts +0 -3
- package/dist/app/cloud/control-resources.js +0 -1
- package/dist/app/cloud/control.d.ts +0 -34
- package/dist/app/cloud/control.js +0 -5
- package/dist/app/cloud/credentials.d.ts +0 -11
- package/dist/app/cloud/credentials.js +0 -2
- package/dist/app/cloud/index.d.ts +0 -22
- package/dist/app/cloud/index.js +0 -2
- package/dist/app/cloud/login.d.ts +0 -63
- package/dist/app/cloud/login.js +0 -2
- package/dist/app/cloud/migration/archive.d.ts +0 -13
- package/dist/app/cloud/migration/archive.js +0 -1
- package/dist/app/cloud/migration/commands.d.ts +0 -8
- package/dist/app/cloud/migration/commands.js +0 -1
- package/dist/app/cloud/migration/evidence.d.ts +0 -13
- package/dist/app/cloud/migration/evidence.js +0 -2
- package/dist/app/cloud/migration/files.d.ts +0 -16
- package/dist/app/cloud/migration/files.js +0 -2
- package/dist/app/cloud/migration/index.d.ts +0 -6
- package/dist/app/cloud/migration/index.js +0 -1
- package/dist/app/cloud/migration/journal.d.ts +0 -64
- package/dist/app/cloud/migration/journal.js +0 -1
- package/dist/app/cloud/migration/mapping-dispositions.d.ts +0 -3
- package/dist/app/cloud/migration/mapping-dispositions.js +0 -1
- package/dist/app/cloud/migration/mapping.d.ts +0 -11
- package/dist/app/cloud/migration/mapping.js +0 -2
- package/dist/app/cloud/migration/model.d.ts +0 -175
- package/dist/app/cloud/migration/model.js +0 -1
- package/dist/app/cloud/migration/plan.d.ts +0 -7
- package/dist/app/cloud/migration/plan.js +0 -1
- package/dist/app/cloud/migration/progress.d.ts +0 -19
- package/dist/app/cloud/migration/progress.js +0 -1
- package/dist/app/cloud/migration/reconcile.d.ts +0 -22
- package/dist/app/cloud/migration/reconcile.js +0 -1
- package/dist/app/cloud/migration/reconciliation-spool.d.ts +0 -4
- package/dist/app/cloud/migration/reconciliation-spool.js +0 -1
- package/dist/app/cloud/migration/state.d.ts +0 -15
- package/dist/app/cloud/migration/state.js +0 -1
- package/dist/app/cloud/migration/transfer.d.ts +0 -6
- package/dist/app/cloud/migration/transfer.js +0 -1
- package/dist/cli/cloud-apply-help.d.ts +0 -1
- package/dist/cli/cloud-apply-help.js +0 -56
- package/dist/cli/cloud-selection.d.ts +0 -2
- package/dist/cli/cloud-selection.js +0 -3
- package/dist/cli/commands/cloud/apply.d.ts +0 -5
- package/dist/cli/commands/cloud/apply.js +0 -1
- package/dist/cli/commands/cloud/campaign/create.d.ts +0 -5
- package/dist/cli/commands/cloud/campaign/create.js +0 -1
- package/dist/cli/commands/cloud/download.d.ts +0 -5
- package/dist/cli/commands/cloud/download.js +0 -1
- package/dist/cli/commands/cloud/list.d.ts +0 -5
- package/dist/cli/commands/cloud/list.js +0 -1
- package/dist/cli/commands/cloud/login.d.ts +0 -13
- package/dist/cli/commands/cloud/login.js +0 -2
- package/dist/cli/commands/cloud/logout.d.ts +0 -7
- package/dist/cli/commands/cloud/logout.js +0 -1
- package/dist/cli/commands/cloud/migrate/apply.d.ts +0 -2
- package/dist/cli/commands/cloud/migrate/apply.js +0 -1
- package/dist/cli/commands/cloud/migrate/plan.d.ts +0 -7
- package/dist/cli/commands/cloud/migrate/plan.js +0 -1
- package/dist/cli/commands/cloud/migrate/progress.d.ts +0 -2
- package/dist/cli/commands/cloud/migrate/progress.js +0 -2
- package/dist/cli/commands/cloud/migrate/reconcile.d.ts +0 -2
- package/dist/cli/commands/cloud/migrate/reconcile.js +0 -1
- package/dist/cli/commands/cloud/policy/preview.d.ts +0 -5
- package/dist/cli/commands/cloud/policy/preview.js +0 -1
- package/dist/cli/commands/cloud/project/create.d.ts +0 -5
- package/dist/cli/commands/cloud/project/create.js +0 -1
- package/dist/cli/commands/cloud/publish.d.ts +0 -7
- package/dist/cli/commands/cloud/publish.js +0 -1
- package/dist/cli/commands/cloud/session/revoke.d.ts +0 -8
- package/dist/cli/commands/cloud/session/revoke.js +0 -1
- package/dist/cli/commands/cloud/show.d.ts +0 -5
- package/dist/cli/commands/cloud/show.js +0 -1
- package/dist/cli/commands/cloud/snapshot.d.ts +0 -5
- package/dist/cli/commands/cloud/snapshot.js +0 -1
- package/dist/cli/commands/cloud/status.d.ts +0 -33
- package/dist/cli/commands/cloud/status.js +0 -1
- package/dist/cloud/account.d.ts +0 -34
- package/dist/cloud/account.js +0 -1
- package/dist/cloud/archive-contract.d.ts +0 -1
- package/dist/cloud/archive-contract.js +0 -1
- package/dist/cloud/archive.d.ts +0 -12
- package/dist/cloud/archive.js +0 -1
- package/dist/cloud/campaigns.d.ts +0 -200
- package/dist/cloud/campaigns.js +0 -2
- package/dist/cloud/client.d.ts +0 -68
- package/dist/cloud/client.js +0 -4
- package/dist/cloud/download.d.ts +0 -5
- package/dist/cloud/download.js +0 -1
- package/dist/cloud/historical-contract.d.ts +0 -224
- package/dist/cloud/historical-contract.js +0 -1
- package/dist/cloud/historical-json.d.ts +0 -16
- package/dist/cloud/historical-json.js +0 -1
- package/dist/cloud/historical.d.ts +0 -69
- package/dist/cloud/historical.js +0 -1
- package/dist/cloud/pagination.d.ts +0 -5
- package/dist/cloud/pagination.js +0 -1
- package/dist/cloud/transport.d.ts +0 -18
- package/dist/cloud/transport.js +0 -1
- package/dist/config/loader/cloud.d.ts +0 -2
- package/dist/config/loader/cloud.js +0 -1
- package/docs/cloud-control.md +0 -108
- package/docs/testkit-cloud-boundary.md +0 -628
- package/node_modules/buffer-crc32/LICENSE +0 -19
- package/node_modules/buffer-crc32/README.md +0 -55
- package/node_modules/buffer-crc32/dist/index.cjs +0 -306
- package/node_modules/buffer-crc32/dist/index.d.cts +0 -35
- package/node_modules/buffer-crc32/dist/index.d.mts +0 -35
- package/node_modules/buffer-crc32/dist/index.mjs +0 -304
- package/node_modules/buffer-crc32/index.d.ts +0 -35
- package/node_modules/buffer-crc32/package.json +0 -54
- package/node_modules/yazl/LICENSE +0 -21
- package/node_modules/yazl/README.md +0 -457
- package/node_modules/yazl/index.js +0 -826
- package/node_modules/yazl/package.json +0 -34
|
@@ -1,175 +0,0 @@
|
|
|
1
|
-
import type { FindingData, Relationship } from "../../../cloud/campaigns.js";
|
|
2
|
-
import type { HistoricalCase, HistoricalManifest } from "../../../cloud/historical-contract.js";
|
|
3
|
-
export interface MigrationTarget {
|
|
4
|
-
origin: string;
|
|
5
|
-
projectId: string;
|
|
6
|
-
campaignId: string;
|
|
7
|
-
}
|
|
8
|
-
export interface RepositoryIdentity {
|
|
9
|
-
host: string;
|
|
10
|
-
owner: string;
|
|
11
|
-
name: string;
|
|
12
|
-
}
|
|
13
|
-
export interface FindingMapping {
|
|
14
|
-
title?: string;
|
|
15
|
-
rootCauseKey?: string;
|
|
16
|
-
classification?: FindingData["classification"];
|
|
17
|
-
observedAt?: string;
|
|
18
|
-
observationRule?: "second_confirmation" | "last_evidence";
|
|
19
|
-
observationRationale?: string;
|
|
20
|
-
verify?: boolean;
|
|
21
|
-
relationship?: Relationship;
|
|
22
|
-
regressionCaseIds?: string[];
|
|
23
|
-
issueReferences?: string[];
|
|
24
|
-
fields?: Partial<Pick<FindingData, "expectedBehavior" | "authority" | "observedBehavior" | "impact" | "severity" | "reproduction" | "limitations">>;
|
|
25
|
-
}
|
|
26
|
-
export interface CaseMapping {
|
|
27
|
-
sourceId?: string;
|
|
28
|
-
engineId: string;
|
|
29
|
-
profileId: string;
|
|
30
|
-
failureKey?: string;
|
|
31
|
-
testType?: string;
|
|
32
|
-
classification?: string;
|
|
33
|
-
}
|
|
34
|
-
export interface MigrationMapping {
|
|
35
|
-
schemaVersion: 1;
|
|
36
|
-
namespace: string;
|
|
37
|
-
repository: RepositoryIdentity;
|
|
38
|
-
findings: Record<string, FindingMapping>;
|
|
39
|
-
cases: Record<string, CaseMapping>;
|
|
40
|
-
runReceipts?: Record<string, string>;
|
|
41
|
-
}
|
|
42
|
-
export interface SourceFile {
|
|
43
|
-
path: string;
|
|
44
|
-
bytes: number;
|
|
45
|
-
sha256: string;
|
|
46
|
-
}
|
|
47
|
-
export interface ImportSource {
|
|
48
|
-
kind: "run" | "case" | "finding";
|
|
49
|
-
id: string;
|
|
50
|
-
digest: string;
|
|
51
|
-
}
|
|
52
|
-
export interface EvidenceFile {
|
|
53
|
-
path: string;
|
|
54
|
-
absolutePath: string;
|
|
55
|
-
bytes: number;
|
|
56
|
-
sha256: string;
|
|
57
|
-
}
|
|
58
|
-
export interface PlannedRun {
|
|
59
|
-
runId: string;
|
|
60
|
-
reusedOriginalEvidence?: boolean;
|
|
61
|
-
observedAt: string;
|
|
62
|
-
request: {
|
|
63
|
-
schemaVersion: 1;
|
|
64
|
-
evidenceFormat?: "zip-v1";
|
|
65
|
-
source: ImportSource;
|
|
66
|
-
sourceRecord: RunSourceRecord;
|
|
67
|
-
manifest: HistoricalManifest;
|
|
68
|
-
artifacts: Array<{
|
|
69
|
-
path: string;
|
|
70
|
-
originalPath: string;
|
|
71
|
-
originalBytes: number;
|
|
72
|
-
originalSha256: string;
|
|
73
|
-
transformation?: string;
|
|
74
|
-
}>;
|
|
75
|
-
};
|
|
76
|
-
files: EvidenceFile[];
|
|
77
|
-
}
|
|
78
|
-
export interface RunSourceRecord {
|
|
79
|
-
runId: string;
|
|
80
|
-
observedAt: string;
|
|
81
|
-
originalManifestSha256: string;
|
|
82
|
-
originalManifestDigest: string;
|
|
83
|
-
converterVersion: "testkit-legacy-v1";
|
|
84
|
-
schemaTuple: {
|
|
85
|
-
manifest: number;
|
|
86
|
-
run: number;
|
|
87
|
-
provenance: number;
|
|
88
|
-
};
|
|
89
|
-
}
|
|
90
|
-
export interface PlannedRecord {
|
|
91
|
-
originalId: string;
|
|
92
|
-
source: ImportSource;
|
|
93
|
-
sourceRecord: unknown;
|
|
94
|
-
observedAt: string;
|
|
95
|
-
case?: HistoricalCase;
|
|
96
|
-
finding?: FindingData;
|
|
97
|
-
relationship?: Relationship;
|
|
98
|
-
verify?: boolean;
|
|
99
|
-
evidence?: Array<{
|
|
100
|
-
runId: string;
|
|
101
|
-
path: string;
|
|
102
|
-
}>;
|
|
103
|
-
regressionSourceIds?: string[];
|
|
104
|
-
observation?: {
|
|
105
|
-
origin: "source" | "mapping" | "second_confirmation" | "last_evidence" | "lifecycle";
|
|
106
|
-
rationale: string;
|
|
107
|
-
runIds?: string[];
|
|
108
|
-
};
|
|
109
|
-
fieldDispositions?: Array<{
|
|
110
|
-
field: string;
|
|
111
|
-
disposition: "mapped" | "retained" | "blocked";
|
|
112
|
-
reason: string;
|
|
113
|
-
target?: string;
|
|
114
|
-
}>;
|
|
115
|
-
}
|
|
116
|
-
export interface MigrationProblem {
|
|
117
|
-
kind: string;
|
|
118
|
-
id: string;
|
|
119
|
-
field: string;
|
|
120
|
-
detail: string;
|
|
121
|
-
}
|
|
122
|
-
export interface MigrationPlan {
|
|
123
|
-
schemaVersion: 1;
|
|
124
|
-
converterVersion: "testkit-legacy-v1";
|
|
125
|
-
target: MigrationTarget;
|
|
126
|
-
namespace: string;
|
|
127
|
-
sourceDir: string;
|
|
128
|
-
outputDir: string;
|
|
129
|
-
sourceFiles: SourceFile[];
|
|
130
|
-
mappingFile: SourceFile;
|
|
131
|
-
inputFiles: {
|
|
132
|
-
registry: string;
|
|
133
|
-
regressions: string;
|
|
134
|
-
mapping: string;
|
|
135
|
-
};
|
|
136
|
-
sourceSummary: {
|
|
137
|
-
bugs: number;
|
|
138
|
-
observations: number;
|
|
139
|
-
cases: number;
|
|
140
|
-
runs: number;
|
|
141
|
-
};
|
|
142
|
-
repository: RepositoryIdentity;
|
|
143
|
-
campaignVersion: number;
|
|
144
|
-
minimumConfirmingRuns: number;
|
|
145
|
-
runs: PlannedRun[];
|
|
146
|
-
records: PlannedRecord[];
|
|
147
|
-
reusedRuns: Record<string, string>;
|
|
148
|
-
problems: MigrationProblem[];
|
|
149
|
-
dispositions?: Array<{
|
|
150
|
-
kind: string;
|
|
151
|
-
id: string;
|
|
152
|
-
status: "mapped" | "unmapped";
|
|
153
|
-
sourceId?: string;
|
|
154
|
-
detail?: string;
|
|
155
|
-
retainedFields?: string[];
|
|
156
|
-
fields?: NonNullable<PlannedRecord["fieldDispositions"]>;
|
|
157
|
-
}>;
|
|
158
|
-
plannedCaseIds?: Record<string, string>;
|
|
159
|
-
ready: boolean;
|
|
160
|
-
}
|
|
161
|
-
export interface PlanOptions extends MigrationTarget {
|
|
162
|
-
sourceDir: string;
|
|
163
|
-
registry: string;
|
|
164
|
-
regressions: string;
|
|
165
|
-
mapping: string;
|
|
166
|
-
outputDir: string;
|
|
167
|
-
env?: NodeJS.ProcessEnv;
|
|
168
|
-
}
|
|
169
|
-
export interface MigrationResult {
|
|
170
|
-
complete: boolean;
|
|
171
|
-
planPath: string;
|
|
172
|
-
reportPath: string;
|
|
173
|
-
importId?: string;
|
|
174
|
-
problems: MigrationProblem[];
|
|
175
|
-
}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export{};
|
|
@@ -1,7 +0,0 @@
|
|
|
1
|
-
import type { MigrationPlan, MigrationProblem, PlanOptions } from "./model.js";
|
|
2
|
-
export declare function planMigration(options: PlanOptions): Promise<{
|
|
3
|
-
ready: boolean;
|
|
4
|
-
planPath: string;
|
|
5
|
-
problems: MigrationProblem[];
|
|
6
|
-
summary: MigrationPlan["sourceSummary"];
|
|
7
|
-
}>;
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
import $ from"node:fs";import M from"node:path";import{walkCloudPages as _}from"../../../cloud/pagination.js";import{CloudClient as Z}from"../../../cloud/client.js";import{cloudToken as ee}from"../credentials.js";import{HistoricalClient as ie}from"../../../cloud/historical.js";import{historicalDigest as L,parseHistoricalJson as ne}from"../../../cloud/historical-json.js";import{array as z,digest as U,object as D,readBoundedFile as re,readSourceJson as G,sanitize as h,separateOutput as te,text as Q,withOutputLock as oe,writeDocument as se}from"./files.js";import{includeOriginalManifest as ae,prepareHistoricalRun as de,referencedRuns as ce,verifyCitedHashes as W}from"./evidence.js";import{decodeMapping as pe,mapRecords as fe}from"./mapping.js";export async function planMigration(t){const g=$.realpathSync(t.sourceDir),u=M.resolve(t.outputDir);te(g,u);const a=t.env??process.env,y={origin:t.origin,projectId:t.projectId,campaignId:t.campaignId},H=await ee(y.origin,a),f=new Z({...y,token:H}),q=new ie({...y,token:H});y.origin=f.origin.origin;for(const[w,d]of Object.entries(y))Q(d,w,w==="origin"?4096:128);return oe(u,async()=>{const w=M.join(u,"plan.json");if($.existsSync(w)||$.existsSync(M.join(u,"evidence")))throw new Error("Migration output already contains a plan or derived evidence; use a new output directory");const d=[],I=G(g,t.registry,d),R=G(g,t.regressions,d),x=M.resolve(g,t.mapping),O=re(x,8*1024**2),o=pe(ne(new TextDecoder("utf-8",{fatal:!0}).decode(O)));if(L(o)!==L(h(o,a)))throw new Error("Mapping contains sensitive URL or credential data; sanitize it before planning");const X=await q.repository();for(const e of["host","owner","name"])if(X[e].toLowerCase()!==o.repository[e].toLowerCase())throw new Error("Selected Cloud project repository differs from the explicit mapping");const l=await f.campaign(t.campaignId),V=await f.snapshot(),r=[],C=[],B=ce(I,R);for(const e of B)try{const i=de({sourceDir:g,outputDir:u,runId:e,mapping:o,sourceFiles:d,env:a});i.request.evidenceFormat="zip-v1",C.push(i)}catch(i){r.push({kind:"run",id:e,field:"evidence",detail:String(h(i instanceof Error?i.message:String(i),a))})}try{W(I,d),W(R,d)}catch(e){r.push({kind:"source",id:"registry",field:"sha256",detail:String(h(e instanceof Error?e.message:String(e),a))})}const s=fe(h(I,a),h(R,a),o,new Map(C.map(e=>[e.runId,e.observedAt])));r.push(...s.problems);const S=new Map(C.map(e=>[e.runId,e])),K=new Set(s.records.flatMap(e=>(e.evidence??[]).filter(i=>i.path==="manifest.json").map(i=>i.runId)));for(const e of K){const i=S.get(e);if(i)try{ae(i,g,u,d,a)}catch(n){r.push({kind:"run",id:e,field:"evidence",detail:String(h(n instanceof Error?n.message:String(n),a))})}}for(const e of s.records)for(const i of e.evidence??[])S.get(i.runId)?.files.some(n=>n.path===i.path)||r.push({kind:"finding",id:e.originalId,field:"evidence",detail:`Referenced run artifact is unavailable: ${i.runId}/${i.path}`});const F=Object.create(null);for(const[e,i]of Object.entries(o.runReceipts??{})){if(!S.has(e)){r.push({kind:"run",id:e,field:"receipt",detail:"Receipt mapping names an unreferenced run"});continue}try{const n=await f.receipt(i),c=S.get(e);if(!c)throw new Error("Missing planned run");if(n.runId!==e||n.repository.toLowerCase()!==`${o.repository.owner}/${o.repository.name}`.toLowerCase())throw new Error("Mapped receipt identity does not match original run/repository");if(n.manifestDigest!==c.request.manifest.digest){if(n.manifestDigest!==c.request.sourceRecord.originalManifestDigest)throw new Error("Mapped receipt has a different evidence representation");if(K.has(e))throw new Error("Reused original receipt cannot supply a manifest.json evidence citation");c.reusedOriginalEvidence=!0}F[e]=i}catch(n){r.push({kind:"run",id:e,field:"receipt",detail:String(h(n instanceof Error?n.message:String(n),a))})}}const b=new Set,P=new Map;await _(async e=>{const i=await f.snapshot({campaignId:t.campaignId,limit:100,...e?{cursor:e}:{}});if(i.campaignVersion!==void 0&&i.campaignVersion!==l.version)throw new Error("Campaign changed during dry-run collision reads; repeat planning");return i},e=>{for(const i of e){if(b.has(i.id))throw new Error("Cloud repeated a finding identity during planning");b.add(i.id),P.set(i.rootCauseKey,i.id)}if(b.size>1e5)throw new Error("Remote finding inventory exceeds migration bound")});for(const e of s.records){if(!e.finding)continue;if(b.has(e.source.id)){const m=(await f.finding(t.campaignId,e.source.id)).finding.import;(!m||m.namespace!==o.namespace||m.digest!==e.source.digest)&&r.push({kind:"finding",id:e.originalId,field:"identity",detail:"Existing finding conflicts with this source identity; resolve before applying"})}const n=P.get(e.finding.rootCauseKey);n&&n!==e.source.id&&r.push({kind:"finding",id:e.originalId,field:"rootCauseKey",detail:"Another remote finding uses this root-cause key"})}const A={},k=new Map,J=new Map;for(const e of V.cases)J.set(e.fingerprint,e.id);await _(e=>q.cases(e,100),e=>{for(const i of e){if(k.has(i.id))throw new Error("Cloud repeated a case identity during planning");k.set(i.id,i.fingerprint)}if(k.size>1e5)throw new Error("Remote case inventory exceeds migration bound")});for(const e of s.records){if(!e.case)continue;const i=e.case.fingerprint,n=J.get(i)??`case_${U(i).slice(0,16)}`;A[e.source.id]=n,k.has(n)&&k.get(n)!==i&&r.push({kind:"case",id:e.originalId,field:"identity",detail:"Mapped case identity collides with a different remote fingerprint"})}if((await f.campaign(t.campaignId)).version!==l.version)throw new Error("Campaign changed during dry run; repeat planning");if((await f.snapshot()).version!==V.version)throw new Error("Project cases changed during dry run; repeat planning");const N=C.length-Object.keys(F).length+s.records.length;(N<1||N>1e4)&&r.push({kind:"source",id:"inventory",field:"count",detail:"Historical import requires 1 to 10,000 inventory records"});for(const e of s.records)e.verify&&new Set(e.evidence?.map(i=>i.runId)).size<l.minimumConfirmingRuns&&r.push({kind:"finding",id:e.originalId,field:"verification",detail:`Campaign requires ${String(l.minimumConfirmingRuns)} distinct confirming runs`});s.records.some(e=>e.verify)&&l.minimumConfirmingRuns!==2&&r.push({kind:"campaign",id:t.campaignId,field:"minimumConfirmingRuns",detail:"Historical two-run verification policy differs from the selected campaign threshold"});const T=[];for(const[e,i]of[["bug",z(D(I,"registry").bugs,"bugs")],["observation",z(D(I,"registry").classifiedNonBugs,"classifiedNonBugs")],["case",z(D(R,"regressions").cases,"cases")]])for(const n of i){const c=D(n,"source record"),m=Q(c.id,"source ID"),E=s.records.find(p=>p.originalId===m&&p.source.kind===(e==="case"?"case":"finding")),j=r.filter(p=>p.id===m);T.push({kind:e,id:m,status:!E||j.length?"unmapped":"mapped",...E?{sourceId:E.source.id,retainedFields:Object.keys(c).sort(),fields:E.fieldDispositions}:{fields:Object.keys(c).sort().map(p=>({field:p,disposition:"blocked",reason:j.map(Y=>Y.detail).join("; ")||"Record could not be mapped"}))},...j.length?{detail:j.map(p=>`${p.field}: ${p.detail}`).join("; ")}:{}})}const v={schemaVersion:1,converterVersion:"testkit-legacy-v1",target:y,namespace:o.namespace,sourceDir:g,outputDir:u,sourceFiles:d.sort((e,i)=>e.path.localeCompare(i.path)),mappingFile:{path:x,bytes:O.length,sha256:U(O)},inputFiles:{registry:t.registry,regressions:t.regressions,mapping:x},sourceSummary:{...s.sourceSummary,runs:B.length},repository:o.repository,campaignVersion:l.version,minimumConfirmingRuns:l.minimumConfirmingRuns,runs:C,records:s.records,reusedRuns:F,problems:r,ready:r.length===0};return v.dispositions=T,v.plannedCaseIds=A,se(w,{...v,planDigest:L(v)}),{ready:v.ready,planPath:w,problems:r,summary:v.sourceSummary}})}
|
|
@@ -1,19 +0,0 @@
|
|
|
1
|
-
export interface MigrationProgress {
|
|
2
|
-
phase: "source-verification" | "journal-upgrade" | "packaging" | "uploading" | "receipt-accepted" | "record-admission" | "reconciliation" | "completion" | "failed";
|
|
3
|
-
completedRuns: number;
|
|
4
|
-
totalRuns: number;
|
|
5
|
-
/** Payload bytes read for upload during this attempt, including retries. */
|
|
6
|
-
bytesTransferred: number;
|
|
7
|
-
verifiedBytes?: number;
|
|
8
|
-
retries: number;
|
|
9
|
-
runId?: string;
|
|
10
|
-
operation?: string;
|
|
11
|
-
detail?: string;
|
|
12
|
-
}
|
|
13
|
-
export type MigrationProgressCallback = (progress: MigrationProgress) => void;
|
|
14
|
-
export type MigrationProgressReporter = ReturnType<typeof createMigrationProgress>;
|
|
15
|
-
export declare function createMigrationProgress(onProgress: MigrationProgressCallback | undefined, env: NodeJS.ProcessEnv): {
|
|
16
|
-
readonly current: MigrationProgress;
|
|
17
|
-
fail(error: unknown): void;
|
|
18
|
-
emit(phase: MigrationProgress["phase"], update?: Partial<Omit<MigrationProgress, "phase">>): void;
|
|
19
|
-
};
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
import{sanitize as o}from"./files.js";export function createMigrationProgress(s,a){let r={phase:"source-verification",completedRuns:0,totalRuns:0,bytesTransferred:0,verifiedBytes:0,retries:0};const t=e=>String(o(e,a)).replace(/[a-z][a-z0-9+.-]*:\/\/[^\s"'<>]+/giu,"<url>").replace(/\p{Cc}/gu," ").slice(0,1024),i=e=>{r={...e,...e.runId?{runId:t(e.runId)}:{},...e.operation?{operation:t(e.operation)}:{},...e.detail?{detail:t(e.detail)}:{}},s?.(r)};return{get current(){return r},fail(e){i({...r,phase:"failed",detail:e instanceof Error?e.message:String(e)})},emit(e,n={}){i({phase:e,completedRuns:r.completedRuns,totalRuns:r.totalRuns,bytesTransferred:r.bytesTransferred,verifiedBytes:r.verifiedBytes??0,retries:r.retries,...n})}}}
|
|
@@ -1,22 +0,0 @@
|
|
|
1
|
-
import type { FindingData } from "../../../cloud/campaigns.js";
|
|
2
|
-
import { type HistoricalOutcome, type HistoricalCase } from "../../../cloud/historical-contract.js";
|
|
3
|
-
import type { HistoricalClient } from "../../../cloud/historical.js";
|
|
4
|
-
import type { MigrationPlan, MigrationProblem, PlannedRecord } from "./model.js";
|
|
5
|
-
import type { MigrationJournal } from "./state.js";
|
|
6
|
-
interface ReconciliationResult {
|
|
7
|
-
complete: boolean;
|
|
8
|
-
problems: MigrationProblem[];
|
|
9
|
-
items: HistoricalOutcome[];
|
|
10
|
-
counts: unknown;
|
|
11
|
-
campaignVersion: number;
|
|
12
|
-
verifyCompletion(): Promise<ReconciliationResult>;
|
|
13
|
-
}
|
|
14
|
-
interface ReconciliationOptions {
|
|
15
|
-
onVerified?: (bytes: number) => void;
|
|
16
|
-
}
|
|
17
|
-
/** Go emits empty text and null slices for omitted optional finding fields. */
|
|
18
|
-
export declare function normalizedFindingData(data: FindingData): Record<string, unknown>;
|
|
19
|
-
export declare function expectedFindingData(record: PlannedRecord, journal: MigrationJournal): FindingData;
|
|
20
|
-
export declare function reconcileState(plan: MigrationPlan, journal: MigrationJournal, client: HistoricalClient, options?: ReconciliationOptions): Promise<ReconciliationResult>;
|
|
21
|
-
export declare function compareHistoricalCase(value: HistoricalCase, expected: HistoricalCase): void;
|
|
22
|
-
export {};
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
import U from"node:path";import{rm as P}from"node:fs/promises";import"../../../cloud/historical-contract.js";import{canonicalHistoricalJson as H,historicalDigest as T}from"../../../cloud/historical-json.js";import{walkCloudPages as F}from"../../../cloud/pagination.js";import{createReconciliationSpool as _}from"./reconciliation-spool.js";const M=n=>n==="accepted"||n==="reused",R=n=>`${n.kind}\0${n.id}`;function t(n,a){if(!n)throw new Error(a)}function v(n,a,s){t(H(n)===H(a),`${s} differs from the reviewed migration plan`)}function x(n,a,s){const l=y=>{const I=/T\d\d:\d\d:\d\d\.(\d+)/.exec(y)?.[1]??"";return BigInt(Date.parse(y))*1000000n+BigInt((I+"000000000").slice(3,9))};t(typeof n=="string"&&typeof a=="string"&&Number.isFinite(Date.parse(n))&&Number.isFinite(Date.parse(a))&&l(n)===l(a),`${s} differs from the original observation time`)}export function normalizedFindingData(n){const a={};for(const s of["title","rootCauseKey","classification","surface","severity","expectedBehavior","authority","observedBehavior","impact","reproduction","limitations"])a[s]=n[s]??"";for(const s of["variants","issueReferences","regressionCaseIds","evidence"])a[s]=n[s]??[];return a}export function expectedFindingData(n,a){return t(n.finding,"Mapped finding data is missing"),{...n.finding,regressionCaseIds:(n.regressionSourceIds??[]).map(s=>{const l=a.caseIds[s];return t(l,`Missing admitted case mapping for ${s}`),l}),evidence:(n.evidence??[]).map(s=>{const l=a.receiptIds[s.runId];return t(l,`Missing admitted receipt mapping for run ${s.runId}`),{receiptId:l,path:s.path}})}}export async function reconcileState(n,a,s,l={}){return L(n,a,s,l)}async function L(n,a,s,l,y){const I=[],q=[];let b=n.campaignVersion,C=n.minimumConfirmingRuns;const h=async(i,r,d,e)=>{try{await e()}catch(o){I.push({kind:i,id:r,field:d,detail:o instanceof Error?o.message:String(o)})}};await h("campaign",n.target.campaignId,"version",async()=>{const i=await s.campaign();b=i.version,C=i.minimumConfirmingRuns,y&&t(b===y.campaignVersion&&C===y.minimumConfirmingRuns,"Campaign changed across import completion; repeat full reconciliation")});const $=[...n.runs.filter(i=>!Object.hasOwn(n.reusedRuns,i.runId)).map(i=>i.request.source),...n.records.map(i=>i.source)],V=new Map($.map(i=>[R(i),i])),w=new Map;let k;await h("import",a.importId??"missing","inventory",async()=>{t(a.importId,"The migration journal has no admitted import ID");const i=a.importId;t(V.size===$.length,"The reviewed inventory contains duplicate source identities");let r;await F(async d=>{const e=await s.page(i,d);t(e.import.namespace===n.namespace&&e.import.inventoryDigest===T($)&&e.import.total===$.length,"Cloud import inventory or namespace differs from the reviewed plan"),y&&t(e.import.completedAt,"Server import is not complete");const o={id:e.import.id,namespace:e.import.namespace,inventoryDigest:e.import.inventoryDigest,total:e.import.total,completedAt:e.import.completedAt??null};return r===void 0?r=o:v(o,r,"Import identity across pages"),k===void 0?k=e.counts:v(e.counts,k,"Import counts across pages"),e},d=>{for(const e of d){const o=R(e);t(!w.has(o),`Duplicate reconciliation outcome: ${e.kind} ${e.id}`),w.set(o,e),q.push(e)}}),t(w.size===V.size,"Cloud reconciliation does not cover the complete declared inventory");for(const[d,e]of w)t(V.get(d)?.digest===e.digest,`Unexpected or changed reconciliation source: ${e.kind} ${e.id}`)});const O={};for(const i of q){const r=O[i.kind]??={};r[i.status]=(r[i.status]??0)+1}await h("import",a.importId??"missing","counts",()=>{t(k!==void 0,"Cloud reconciliation counts are unavailable"),v(O,k,"Counts derived from all returned source outcomes")});for(const i of $){const r=w.get(R(i));(!r||r.digest!==i.digest||!M(r.status))&&I.push({kind:i.kind,id:i.id,field:"status",detail:r?.detail??`Source is ${r?.status??"missing"}; every declared source must be accepted or reused`})}const z=new Map,B=new Set,E=new Set,A=[];let S;try{await h("evidence","downloads","spool",async()=>{y||(S=await _(n,a))});for(const e of n.runs){const o=a.receiptIds[e.runId];A.push(()=>h("run",e.runId,"receipt",async()=>{t(o,"The journal has no admitted receipt mapping for this run");const c=Object.hasOwn(n.reusedRuns,e.runId),p=w.get(R(e.request.source));c?t(n.reusedRuns[e.runId]===o,"Current receipt mapping differs from the reviewed plan"):(t(p&&M(p.status)&&p.cloudId===o,"Run outcome differs from its admitted receipt mapping"),t(p.evidenceAvailable===!0,"Historical evidence is no longer available"));const f=c?await s.currentReceipt(o):await s.receipt(o);t(f.id===o&&f.projectId===n.target.projectId&&f.runId===e.runId&&f.manifestDigest===(e.reusedOriginalEvidence?e.request.sourceRecord.originalManifestDigest:e.request.manifest.digest),"Receipt run, project, or manifest identity differs from the reviewed run"),t(f.repository.toLowerCase()===`${n.repository.owner}/${n.repository.name}`.toLowerCase(),"Receipt repository differs from the reviewed repository");const m=z.get(o);if(t(m===void 0||m===e.runId,"One receipt was assigned to different original run identities"),z.set(o,e.runId),!c){t("historical"in f,"Expected a historical receipt");const g=f.historical;t(g.sourceDigest===e.request.source.digest&&g.manifestVersion===e.request.manifest.schemaVersion,"Receipt source or schema differs from the reviewed run"),v(g.sourceRecord,e.request.sourceRecord,"Receipt retained source record"),v(g.artifacts.map(u=>({path:u.path,originalPath:u.originalPath,originalSha256:u.originalSha256,originalBytes:u.originalBytes,...u.transformation===void 0?{}:{transformation:u.transformation}})),e.request.artifacts,"Receipt original artifact mappings")}}));for(const c of e.files){const p=A.length;A.push(()=>h("run",e.runId,`artifact:${c.path}`,async()=>{t(o,"Cannot verify artifacts without an admitted receipt");const f=e.request.artifacts.find(u=>u.path===c.path);t(f,"Original artifact mapping is missing");const m=e.reusedOriginalEvidence?{bytes:f.originalBytes,sha256:f.originalSha256}:{bytes:c.bytes,sha256:c.sha256},g=H({receiptId:o,path:c.path,...m});if(y)t(y.artifacts.has(g),"Artifact was not verified before import completion"),Object.hasOwn(n.reusedRuns,e.runId)&&await s.checkArtifactAvailable(o,c.path);else{t(S,"Cannot verify artifacts without a local download directory");const u=U.join(S,`artifact-${String(p)}`);try{await s.download(s.artifactURL(o,c.path),u,m),l.onVerified?.(m.bytes)}finally{await P(u,{force:!0})}}E.add(g),B.add(`${o}\0${c.path}`)}))}}let i=0;const r=async()=>{for(;i<A.length;)await A[i++]()},d=await Promise.allSettled([r(),r()]);for(const e of d)if(e.status==="rejected")throw e.reason}finally{S&&await P(S,{recursive:!0,force:!0})}const N=new Map(n.records.filter(i=>i.source.kind==="case").map(i=>[a.caseIds[i.source.id],i])),K=new Map;N.size&&await h("case","all","list",async()=>{const i=new Set;await F(r=>s.cases(r),r=>{for(const d of r)t(!i.has(d.id),`Duplicate case across Cloud pages: ${d.id}`),i.add(d.id),N.has(d.id)&&K.set(d.id,d)})});for(const i of n.records.filter(r=>r.source.kind==="case"))await h("case",i.source.id,"history",async()=>{const r=a.caseIds[i.source.id],d=w.get(R(i.source));t(r&&d&&d.cloudId===r&&M(d.status),"Case outcome differs from its journal identity mapping");const e=K.get(r);t(e,"Admitted case is absent from the complete Cloud case list"),t(i.case,"Mapped historical case is missing");const o=i.case;t(e.fingerprint===i.case.fingerprint,"Cloud case fingerprint differs from the reviewed fingerprint");let c=0;const p=new Set;await F(f=>s.caseHistory(r,f),f=>{for(const m of f){const g=`${m.id}\0${m.commandType}`;t(!p.has(g),`Duplicate case history event across pages: ${m.id}`),p.add(g);const u=m.historical;m.commandType!=="case.import"||!u||u.sourceId!==i.source.id||T(u.sourceRecord)!==i.source.digest||(d.status!=="reused"&&t(u.importId===a.importId,"Case observation belongs to another import"),v(u.sourceRecord,i.sourceRecord,"Retained case source record"),x(u.observedAt,i.observedAt,"Case observedAt"),compareHistoricalCase(u.case,o),t(m.state===u.case.state&&m.classification===u.case.classification&&m.issueNumber===(u.case.issueNumber??0)&&m.issueRepository===(u.case.issueRepository??""),"Case history event contradicts its original observation"),x(m.appliedAt,u.importedAt,"Case admission time"),c++)}}),t(c===1,`Expected one matching historical case observation; found ${String(c)}`)});const D=new Map;for(const i of n.records.filter(r=>r.source.kind==="finding"))await h("finding",i.source.id,"detail",async()=>{const r=w.get(R(i.source));t(r?.cloudId&&M(r.status),"Finding has no accepted Cloud identity");const d=await s.finding(r.cloudId);t(d.campaignVersion===b,"Campaign changed while reading finding details; reconciliation is inconclusive");const e=d.finding;t(e.id===i.source.id,"Admitted finding ID differs from the declared source identity");const o=expectedFindingData(i,a);v(normalizedFindingData(e.data),normalizedFindingData(o),"Mapped finding data"),v(e.relationship,i.relationship??{kind:"independent"},"Finding relationship");const c=e.import;t(c,"Historical finding source metadata is missing"),t(c.namespace===n.namespace&&c.sourceId===i.source.id&&c.digest===i.source.digest,"Finding import source identity differs from the reviewed plan"),r.status!=="reused"&&t(c.importId===a.importId,"Finding metadata belongs to another import"),v(c.record,i.sourceRecord,"Retained finding source record"),t(T(c.record)===i.source.digest,"Retained finding source digest does not match"),x(c.observedAt,i.observedAt,"Finding observedAt");for(const p of d.evidence)t(p.available&&B.has(`${p.receiptId}\0${p.path}`),`Finding evidence unavailable or not verified: ${p.receiptId}/${p.path}`);if(i.verify){const p=e.verification;t(p,"Requested finding verification is missing"),t(p.findingRevision===e.revision,"Finding verification does not cover its current revision"),t(p.minimumConfirmingRuns===C,"Verification threshold differs from the campaign's current threshold");const f=new Map;for(const m of d.evidence.filter(g=>g.available)){const g=z.get(m.receiptId);t(g,`Verification receipt has no reconciled original run: ${m.receiptId}`);const u=f.get(g);(u===void 0||m.receiptId<u)&&f.set(g,m.receiptId)}t(f.size>=C,`Verification requires ${String(C)} distinct original runs; ${String(f.size)} eligible`),v([...p.confirmingReceiptIds].sort(),[...f.values()].sort(),"Distinct confirming receipt identities")}else t(e.verification===null,"An explicitly unverified historical finding unexpectedly has verification");D.set(e.id,{counted:e.data.classification==="product_bug"&&e.verification!==null&&e.relationship.kind==="independent",verified:e.verification!==null})});return D.size&&await h("campaign",n.target.campaignId,"finding counts",async()=>{const i=new Set;let r=0,d;await F(async e=>{const o=await s.snapshot({campaignId:n.target.campaignId,findings:!0,view:"all",...e?{cursor:e}:{}});return t(o.campaignVersion===b,"Campaign changed during paginated finding reconciliation"),d===void 0?d=o.bugCount:t(o.bugCount===d,"Campaign bug count changed across pages"),o},e=>{for(const o of e){const c=o.id;t(!i.has(c),`Duplicate campaign finding across pages: ${c}`),i.add(c);const p=o.classification==="product_bug"&&o.verified&&o.relationship.kind==="independent";t(o.counted===p,`Finding count does not follow classification, verification and relationship: ${c}`),p&&r++;const f=D.get(c);f&&t(o.counted===f.counted&&o.verified===f.verified,`Finding summary contradicts admitted detail: ${c}`)}}),t(r===d,"Campaign bug count differs from counted findings across all pages");for(const e of D.keys())t(i.has(e),`Admitted finding is absent from the complete campaign feed: ${e}`)}),await h("campaign",n.target.campaignId,"version",async()=>{const i=await s.campaign();t(i.version===b&&i.minimumConfirmingRuns===C,"Campaign changed during reconciliation; repeat reads before declaring completion")}),{complete:I.length===0,problems:I,items:q,counts:O,campaignVersion:b,verifyCompletion:()=>(t(I.length===0,"Full reconciliation must succeed before checking import completion"),L(n,a,s,l,{artifacts:E,campaignVersion:b,minimumConfirmingRuns:C}))}}export function compareHistoricalCase(n,a){for(const s of["id","classification","engineId","failureKey","fingerprint","path","profileId","service","state","testType","title"])v(n[s],a[s],`Historical case ${s}`);for(const s of["firstSeenAt","lastSeenAt"])x(n[s],a[s],`Historical case ${s}`);t((n.version??0)===(a.version??0)&&(n.issueNumber??0)===(a.issueNumber??0)&&(n.issueRepository??"")===(a.issueRepository??""),"Historical case version or issue linkage differs from its original mapping")}
|
|
@@ -1,4 +0,0 @@
|
|
|
1
|
-
import type { MigrationPlan } from "./model.js";
|
|
2
|
-
import type { MigrationJournal } from "./state.js";
|
|
3
|
-
/** Called while execute holds the migration output lock. Unknown files are never reclaimed. */
|
|
4
|
-
export declare function createReconciliationSpool(plan: Pick<MigrationPlan, "outputDir">, journal: Pick<MigrationJournal, "planDigest" | "importId">): Promise<string>;
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
import r from"node:path";import{lstat as l,mkdtemp as p,readdir as m,rm as f,writeFile as w}from"node:fs/promises";import{canonicalHistoricalJson as d}from"../../../cloud/historical-json.js";import{readBoundedFile as y}from"./files.js";const e=".testkit-reconciliation",h=/^artifact-\d+(?:\.[a-f0-9-]{36}\.partial)?$/u;export async function createReconciliationSpool(n,a){const s=d({schemaVersion:1,planDigest:a.planDigest,importId:a.importId??null});for(const t of await m(n.outputDir,{withFileTypes:!0})){if(!/^reconcile-[A-Za-z0-9]{6}$/u.test(t.name)||!t.isDirectory()||t.isSymbolicLink())continue;const c=r.join(n.outputDir,t.name),u=await m(c,{withFileTypes:!0});if(!u.some(i=>!i.isFile()||i.name!==e&&!h.test(i.name))&&u.some(i=>i.name===e)){try{if(y(r.join(c,e),1024).toString("utf8")!==s)continue}catch{continue}await f(c,{recursive:!0,force:!0})}}const o=await p(r.join(n.outputDir,"reconcile-"));try{const t=await l(o);if(!t.isDirectory()||t.isSymbolicLink())throw new Error("Reconciliation directory is unsafe");return await w(r.join(o,e),s,{flag:"wx",mode:384}),o}catch(t){throw await f(o,{recursive:!0,force:!0}),t}}
|
|
@@ -1,15 +0,0 @@
|
|
|
1
|
-
import { type OperationKind, type OperationRequests, type OperationResults, type MigrationJournal } from "./journal.js";
|
|
2
|
-
export type { MigrationJournal, MigrationOperation } from "./journal.js";
|
|
3
|
-
import type { MigrationPlan } from "./model.js";
|
|
4
|
-
export declare function loadPlan(filename: string): {
|
|
5
|
-
plan: MigrationPlan;
|
|
6
|
-
planDigest: string;
|
|
7
|
-
};
|
|
8
|
-
export declare function verifyPlanFiles(plan: MigrationPlan): void;
|
|
9
|
-
export declare function openJournal(plan: MigrationPlan, planDigest: string): MigrationJournal;
|
|
10
|
-
export declare function saveJournal(plan: MigrationPlan, journal: MigrationJournal): void;
|
|
11
|
-
/** Atomically upgrade a journal already decoded by openJournal, retaining every staged request. */
|
|
12
|
-
export declare function upgradeJournal(plan: MigrationPlan, journal: MigrationJournal): void;
|
|
13
|
-
/** Re-staging must establish availability before an obsolete evidence rejection can be retried. */
|
|
14
|
-
export declare function recoverEvidenceOperation(plan: MigrationPlan, journal: MigrationJournal, key: string): void;
|
|
15
|
-
export declare function performOperation<K extends OperationKind>(plan: MigrationPlan, journal: MigrationJournal, kind: K, key: string, input: OperationRequests[K], run: (saved: OperationRequests[K]) => Promise<OperationResults[K]>, env: NodeJS.ProcessEnv, refreshAccepted?: boolean): Promise<OperationResults[K]>;
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
import w from"node:fs";import g from"node:path";import{canonicalHistoricalJson as y,historicalDigest as I}from"../../../cloud/historical-json.js";import{validateHistoricalBatch as V,validateHistoricalBegin as R,validateHistoricalEvidence as F}from"../../../cloud/historical-contract.js";import{decodeJournal as q,emptyJournal as M,operationIs as k}from"./journal.js";import{array as v,contained as J,digest as b,object as u,plain as N,readDocument as D,readSource as S,sanitize as O,separateOutput as P,text as f,writeDocument as C}from"./files.js";const j="campaign command conflict: abandoned evidence cleanup is pending; retry after retention completes";function x(t,e){if(t==="receipt"&&e==="evidence is expired")return 422;if((t==="upload"||t==="archive")&&(e==="conflict: publication is pending"||e==="conflict: publication does not accept an archive"))return 409}export function loadPlan(t){const e=u(D(t),"migration plan"),{planDigest:i,...r}=e;if(e.schemaVersion!==1||e.converterVersion!=="testkit-legacy-v1"||I(r)!==i)throw new Error("Migration plan version/digest is invalid");for(const n of["sourceDir","outputDir"])f(e[n],n);if(f(e.namespace,"namespace",128),g.resolve(t)!==g.join(String(e.outputDir),"plan.json"))throw new Error("Migration plan must remain in its bound output directory");P(String(e.sourceDir),String(e.outputDir));const a=u(e.target,"target");for(const n of["origin","projectId","campaignId"])f(a[n],n);for(const n of["sourceFiles","runs","records","problems"])v(e[n],n);if(typeof e.ready!="boolean")throw new Error("Migration readiness must be a boolean");if(!Number.isSafeInteger(e.campaignVersion)||Number(e.campaignVersion)<1||!Number.isSafeInteger(e.minimumConfirmingRuns)||Number(e.minimumConfirmingRuns)<1||Number(e.minimumConfirmingRuns)>64)throw new Error("Invalid migration campaign version or verification threshold");const m=v(e.runs,"runs"),p=v(e.records,"records");if(m.length+p.length>1e4)throw new Error("Migration inventory exceeds bound");const h=u(e.reusedRuns,"reusedRuns");for(const[n,o]of Object.entries(h))if(!m.some(c=>u(c,"run").runId===n)||!/^[A-Za-z0-9_-]{1,128}$/u.test(f(o,"reused receipt",128)))throw new Error("Invalid reused run receipt identity");for(const n of v(e.runs,"runs")){const o=u(n,"run"),c=u(o.request,"evidence request");F(c);const d=u(N(c.sourceRecord),"run sourceRecord");if(y(d)!==y(c.sourceRecord))throw new Error("Invalid run source numeric encoding");if(c.sourceRecord=d,d.runId!==o.runId||d.observedAt!==o.observedAt||d.converterVersion!=="testkit-legacy-v1")throw new Error("Invalid run source identity");for(const E of["originalManifestSha256","originalManifestDigest"])if(!/^[a-f0-9]{64}$/.test(f(d[E],E,64)))throw new Error("Invalid original manifest digest");const l=u(d.schemaTuple,"schemaTuple"),$=u(c.manifest,"manifest");if(l.manifest!==$.schemaVersion||l.run!==(l.manifest===1?16:19)||l.provenance!==(l.manifest===1?1:3))throw new Error("Invalid original schema tuple")}const s=[...m.filter(n=>!Object.hasOwn(h,f(u(n,"run").runId,"runId"))).map(n=>u(u(n,"run").request,"evidence request").source),...p.map(n=>u(n,"record").source)];(e.ready||s.length)&&R({schemaVersion:1,idempotencyKey:`migration-${i}`,namespace:e.namespace,inventory:s,inventoryDigest:I(s)});for(let n=0;n<p.length;n+=25)V({schemaVersion:1,idempotencyKey:"plan-records",expectedVersion:e.campaignVersion,records:p.slice(n,n+25)});return{plan:r,planDigest:f(i,"planDigest",64)}}export function verifyPlanFiles(t){for(const r of t.sourceFiles){const a=S(t.sourceDir,f(r.path,"source path"),[],r.bytes);if(a.length!==r.bytes||b(a)!==r.sha256)throw new Error(`Source changed since planning: ${r.path}`)}const e=f(t.mappingFile.path,"mapping file"),i=w.lstatSync(e);if(!i.isFile()||i.isSymbolicLink()||i.size!==t.mappingFile.bytes||b(w.readFileSync(e))!==t.mappingFile.sha256)throw new Error("Mapping changed since planning");for(const r of t.runs)for(const a of r.files){if(J(t.outputDir,`evidence/${r.runId}/${a.path}`)!==a.absolutePath)throw new Error("Derived evidence path differs from its plan");const p=S(t.outputDir,`evidence/${r.runId}/${a.path}`,[],a.bytes);if(p.length!==a.bytes||b(p)!==a.sha256)throw new Error(`Derived evidence changed: ${r.runId}/${a.path}`)}}export function openJournal(t,e){const i=g.join(t.outputDir,"journal.json");return w.existsSync(i)?q(D(i),t,e):M(t,e)}export function saveJournal(t,e){C(g.join(t.outputDir,"journal.json"),e)}export function upgradeJournal(t,e){if(e.schemaVersion===2)return;const i=w.existsSync(g.join(t.outputDir,"journal.json"));e.schemaVersion=2,i&&(e.upgradedFrom=1),e.legacyStages=e.operations.filter(r=>r.kind==="stage").map(r=>r.key.slice(6));for(const r of e.operations)r.kind==="stage"&&r.state==="rejected"&&r.error===j&&(r.state="unknown");saveJournal(t,e)}export function recoverEvidenceOperation(t,e,i){const r=e.operations.find(a=>a.key===i);r?.state==="rejected"&&x(r.kind,r.error)!==void 0&&(r.state="unknown",saveJournal(t,e))}export async function performOperation(t,e,i,r,a,m,p,h=!1){let s=e.operations.find(o=>k(o,i)&&o.key===r);const n=y(a);if(s&&s.request!==n)throw new Error(`Conflicting retry payload: ${r}`);if(s?.state==="accepted"&&!h){if(s.result===void 0)throw new Error("Accepted operation has no decoded result");return s.result}if(s?.state==="rejected")throw new Error(`Prior operation was definitively rejected: ${r}; use a reviewed new plan for corrections`);s||(s={kind:i,key:r,request:n,state:"pending"},e.operations.push(s)),saveJournal(t,e);try{const o=await m(a);if(s.result=o,s.state="rejected"in o&&o.rejected===!0?"rejected":"accepted",delete s.error,saveJournal(t,e),s.state==="rejected")throw new Error(`Cloud rejected the atomic batch: ${r}; durable per-record diagnostics are in the journal`);return o}catch(o){if(s.state!=="rejected"){const c=o&&typeof o=="object"&&"status"in o?Number(o.status):void 0,d=o instanceof Error?o.message:void 0;s.state=c&&c>=400&&c<500&&![401,403,408,429,...i==="begin"?[404]:[]].includes(c)&&!(i==="stage"&&c===409&&d===j)&&c!==x(i,d)?"rejected":"unknown"}throw s.error=String(O(o instanceof Error?o.message:String(o),p)),saveJournal(t,e),o}}
|
|
@@ -1,6 +0,0 @@
|
|
|
1
|
-
import { HistoricalClient } from "../../../cloud/historical.js";
|
|
2
|
-
import type { MigrationPlan } from "./model.js";
|
|
3
|
-
export { batchRecords, dependencyOrder } from "./commands.js";
|
|
4
|
-
import { type MigrationJournal } from "./state.js";
|
|
5
|
-
import type { MigrationProgressReporter } from "./progress.js";
|
|
6
|
-
export declare function transfer(plan: MigrationPlan, journal: MigrationJournal, client: HistoricalClient, env: NodeJS.ProcessEnv, progress: MigrationProgressReporter): Promise<void>;
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
import"../../../cloud/historical.js";import{historicalDigest as b}from"../../../cloud/historical-json.js";import{batchRecords as k,beginCommand as q,plannedRecords as D}from"./commands.js";export{batchRecords,dependencyOrder}from"./commands.js";import{performOperation as m,recoverEvidenceOperation as z,saveJournal as l}from"./state.js";import{savedArchiveRequest as A,stagingRequest as R}from"./journal.js";import{prepareMigrationArchive as O,removeMigrationArchive as C}from"./archive.js";export async function transfer(r,i,u,p,I){let y=r.runs.filter(e=>i.receiptIds[e.runId]).length;const h=(e,o={})=>{I.emit(e,{completedRuns:y,totalRuns:r.runs.length,...o})},g=(e,o,t,f=!1)=>{const a=i.operations.some(s=>s.key===o&&(f||s.state!=="accepted"));h(e,{runId:t,operation:o,retries:I.current.retries+(a?1:0)})},w=(e,o,t=!1)=>{z(r,i,e),g("uploading",e,o,t)},$=(e,o,t)=>{h("uploading",{runId:o,operation:t,bytesTransferred:I.current.bytesTransferred+e})};for(const e of r.runs)i.receiptIds[e.runId]&&await C(r,e.runId);if(!i.operations.some(e=>e.kind==="batch")){const e=await u.campaign();if(e.version!==r.campaignVersion||e.minimumConfirmingRuns!==r.minimumConfirmingRuns)throw new Error("Campaign version or verification threshold changed since planning; create a new reviewed plan")}g("uploading","begin");const v=await m(r,i,"begin","begin",q(r,i.planDigest),e=>u.begin(e),p);i.importId=v.id,l(r,i);for(const e of r.runs){if(i.receiptIds[e.runId])continue;const o=`stage:${e.runId}`;g("uploading",o,e.runId,!0);const t=await m(r,i,"stage",o,R(r,i,e.runId),s=>u.stage(v.id,s),p,!0);if(t.status==="rejected")throw new Error(`Cloud rejected evidence for ${e.runId}; inspect the publication before retrying`);if(t.status==="accepted"&&(t.archive||t.uploads.length))throw new Error("Accepted historical evidence unexpectedly includes upload targets");if(t.status==="pending"&&(R(r,i,e.runId).evidenceFormat==="zip-v1"?!t.archive||t.uploads.length!==0:t.archive||t.uploads.length!==e.files.length))throw new Error(`Cloud staging transport differs from the saved request for ${e.runId}`);if(t.archive){const s=t.archive,n=`archive:${e.runId}:${b(t.archive.url)}`,d=A(i,n);h("packaging",{runId:e.runId});const c=await O(r,e,t.archive.maxBytes,d);w(n,e.runId,!0),await m(r,i,"archive",n,{url:t.archive.url,bytes:c.bytes,sha256:c.sha256,manifestDigest:e.request.manifest.digest},async()=>(await u.upload(s.url,c.path,c.bytes,"application/zip",x=>{$(x,e.runId,n)}),{uploaded:!0}),p,!0)}for(const s of t.uploads){const n=e.files.find(c=>c.path===s.path);if(!n||n.bytes!==s.bytes||n.sha256!==s.sha256)throw new Error(`Cloud requested an unplanned artifact: ${e.runId}/${s.path}`);const d=`upload:${e.runId}:${String(s.index)}:${b(s.url)}`;w(d,e.runId,!0),await m(r,i,"upload",d,{url:s.url,path:n.absolutePath,bytes:n.bytes,sha256:n.sha256},async()=>(await u.upload(s.url,n.absolutePath,n.bytes,"application/octet-stream",c=>{$(c,e.runId,d)}),{uploaded:!0}),p,!0)}const f=`receipt:${e.runId}:${b(t.completeUrl)}`;w(f,e.runId);const a=await m(r,i,"receipt",f,{url:t.completeUrl},()=>u.completeEvidence(t.completeUrl),p);if(a.runId!==e.runId||a.manifestDigest!==e.request.manifest.digest)throw new Error(`Receipt differs from planned run: ${e.runId}`);i.receiptIds[e.runId]=a.id,l(r,i),y++,h("receipt-accepted",{runId:e.runId}),await C(r,e.runId)}let E=r.campaignVersion;for(const e of["case","finding"]){const o=D(r,i,e);for(const[t,f]of k(o).entries()){const a=`${e}s-${String(t)}`,s={schemaVersion:1,idempotencyKey:`migration-${i.planDigest}-${e}-${String(t)}`,expectedVersion:E,records:f};g("record-admission",a);const n=await m(r,i,"batch",a,s,d=>u.batch(v.id,d),p);if(E=n.version,e==="case"){for(const d of n.items){if(!d.cloudId||!["accepted","reused"].includes(d.status))throw new Error(`Case was not admitted: ${d.id}`);i.caseIds[d.id]=d.cloudId}l(r,i)}}}}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export declare const cloudApplyHelp = "Account command file:\n Required envelope: schemaVersion: 1, idempotencyKey, operation, input: {}.\n Add the scope IDs listed below. Omit unrelated scope IDs.\n Keys use 1-128 characters without surrounding whitespace. Files are limited to 256 KiB.\n Required input fields are listed below; optional fields are marked optional.\n\nOperations, scope, and input:\n account.theme no scope: theme (system|light|dark)\n organisation.update organisationId: name\n invitation.create organisationId: email, role (owner|admin|member|viewer)\n invitation.revoke organisationId, resourceId: {}\n provider.create organisationId: provider (gitea), host (HTTPS origin),\n displayName, oauthClientId, oauthClientSecret;\n optional botToken, caBundlePEM\n provider.delete organisationId, resourceId: {}\n project.create organisationId: name, provider (github|gitea), host,\n repository (owner/repo); optional publishDecisions (false)\n project.publishing projectId: enabled (boolean)\n project.baseline projectId: branch (empty string resets provider default)\n project.retention projectId: days (1-3650)\n project.policy projectId: policyId (string or null to inherit)\n policy.create organisationId: name; optional rules or sourcePolicyId\n policy.revise organisationId, resourceId: complete rules array\n policy.default organisationId, resourceId: {}\n notification.create projectId: kind (slack|email), displayName, target, events\n notification.update projectId, resourceId: enabled (boolean), events\n notification.delete projectId, resourceId: {}\n notification.test projectId, resourceId: {}; sends a test notification\n token.create projectId: name; requires --secret-output NEW_FILE\n token.revoke projectId, resourceId: {}\n\nNotification events: baseline_hold, case_opened, case_resolved; at least one.\nPolicy rules: id, kind, mode, branches, typed params. Read a policy before revising.\nFind scope and resource IDs with cloud list and cloud show.\nUse cloud session revoke ID to revoke a CLI login; token.revoke revokes a project token.\n\nExample command.json:\n {\"schemaVersion\":1,\"idempotencyKey\":\"retention-30\",\"operation\":\"project.retention\",\n \"projectId\":\"prj_example\",\"input\":{\"days\":30}}\n\nKeep secrets in private files. Token output never goes to stdout.\nRetry the identical command/key after a lost response; changing the command conflicts.\nIf a token was issued but saving failed, retry the same key with a new --secret-output.\nFor outcome_uncertain, inspect cloud list/show and audit history before choosing a new key.\n\nOther file formats:\n Versioned case commands: schemaVersion: 1, projectId, idempotencyKey,\n expectedVersion, commands. Read current versions with cloud snapshot.\n Each case command has caseId and type: case.set_state (state: active|resolved),\n case.classify (classification: untriaged|regression|flaky|expected|quarantined),\n or case.link_issue (issueRepository, positive issueNumber).\n Campaign reports with --campaign ID: schemaVersion: 1, projectId, campaignId,\n idempotencyKey, expectedVersion, command. Command types: state.report, receipt.link,\n finding.record, finding.update, finding.verify, finding.relate, verification.settings.update.\n Campaign verification can load product configuration from --dir.\n See the installed docs/testkit-cloud-boundary.md for complete case and campaign formats.";
|
|
@@ -1,56 +0,0 @@
|
|
|
1
|
-
export const cloudApplyHelp=`Account command file:
|
|
2
|
-
Required envelope: schemaVersion: 1, idempotencyKey, operation, input: {}.
|
|
3
|
-
Add the scope IDs listed below. Omit unrelated scope IDs.
|
|
4
|
-
Keys use 1-128 characters without surrounding whitespace. Files are limited to 256 KiB.
|
|
5
|
-
Required input fields are listed below; optional fields are marked optional.
|
|
6
|
-
|
|
7
|
-
Operations, scope, and input:
|
|
8
|
-
account.theme no scope: theme (system|light|dark)
|
|
9
|
-
organisation.update organisationId: name
|
|
10
|
-
invitation.create organisationId: email, role (owner|admin|member|viewer)
|
|
11
|
-
invitation.revoke organisationId, resourceId: {}
|
|
12
|
-
provider.create organisationId: provider (gitea), host (HTTPS origin),
|
|
13
|
-
displayName, oauthClientId, oauthClientSecret;
|
|
14
|
-
optional botToken, caBundlePEM
|
|
15
|
-
provider.delete organisationId, resourceId: {}
|
|
16
|
-
project.create organisationId: name, provider (github|gitea), host,
|
|
17
|
-
repository (owner/repo); optional publishDecisions (false)
|
|
18
|
-
project.publishing projectId: enabled (boolean)
|
|
19
|
-
project.baseline projectId: branch (empty string resets provider default)
|
|
20
|
-
project.retention projectId: days (1-3650)
|
|
21
|
-
project.policy projectId: policyId (string or null to inherit)
|
|
22
|
-
policy.create organisationId: name; optional rules or sourcePolicyId
|
|
23
|
-
policy.revise organisationId, resourceId: complete rules array
|
|
24
|
-
policy.default organisationId, resourceId: {}
|
|
25
|
-
notification.create projectId: kind (slack|email), displayName, target, events
|
|
26
|
-
notification.update projectId, resourceId: enabled (boolean), events
|
|
27
|
-
notification.delete projectId, resourceId: {}
|
|
28
|
-
notification.test projectId, resourceId: {}; sends a test notification
|
|
29
|
-
token.create projectId: name; requires --secret-output NEW_FILE
|
|
30
|
-
token.revoke projectId, resourceId: {}
|
|
31
|
-
|
|
32
|
-
Notification events: baseline_hold, case_opened, case_resolved; at least one.
|
|
33
|
-
Policy rules: id, kind, mode, branches, typed params. Read a policy before revising.
|
|
34
|
-
Find scope and resource IDs with cloud list and cloud show.
|
|
35
|
-
Use cloud session revoke ID to revoke a CLI login; token.revoke revokes a project token.
|
|
36
|
-
|
|
37
|
-
Example command.json:
|
|
38
|
-
{"schemaVersion":1,"idempotencyKey":"retention-30","operation":"project.retention",
|
|
39
|
-
"projectId":"prj_example","input":{"days":30}}
|
|
40
|
-
|
|
41
|
-
Keep secrets in private files. Token output never goes to stdout.
|
|
42
|
-
Retry the identical command/key after a lost response; changing the command conflicts.
|
|
43
|
-
If a token was issued but saving failed, retry the same key with a new --secret-output.
|
|
44
|
-
For outcome_uncertain, inspect cloud list/show and audit history before choosing a new key.
|
|
45
|
-
|
|
46
|
-
Other file formats:
|
|
47
|
-
Versioned case commands: schemaVersion: 1, projectId, idempotencyKey,
|
|
48
|
-
expectedVersion, commands. Read current versions with cloud snapshot.
|
|
49
|
-
Each case command has caseId and type: case.set_state (state: active|resolved),
|
|
50
|
-
case.classify (classification: untriaged|regression|flaky|expected|quarantined),
|
|
51
|
-
or case.link_issue (issueRepository, positive issueNumber).
|
|
52
|
-
Campaign reports with --campaign ID: schemaVersion: 1, projectId, campaignId,
|
|
53
|
-
idempotencyKey, expectedVersion, command. Command types: state.report, receipt.link,
|
|
54
|
-
finding.record, finding.update, finding.verify, finding.relate, verification.settings.update.
|
|
55
|
-
Campaign verification can load product configuration from --dir.
|
|
56
|
-
See the installed docs/testkit-cloud-boundary.md for complete case and campaign formats.`;
|
|
@@ -1,3 +0,0 @@
|
|
|
1
|
-
import{createInterface as c}from"node:readline/promises";export function cloudInteraction(r){return{notify:e=>process.stderr.write(`${e.replace(/\n$/u,"")}
|
|
2
|
-
`),...!r&&!process.env.CI&&process.stdin.isTTY&&process.stderr.isTTY?{choose:a}:{}}}async function a(r,e){const n=c({input:process.stdin,output:process.stderr});try{e.forEach((o,i)=>process.stderr.write(`${String(i+1)}. ${o.name} (${o.id})
|
|
3
|
-
`));const s=(await n.question(`Select ${r} [1-${String(e.length)}]: `)).trim(),t=/^\d+$/u.test(s)?Number(s)-1:-1;if(!Number.isSafeInteger(t)||t<0||t>=e.length)throw new Error(`Invalid ${r} selection. Select an explicit ID with a command flag.`);return e[t].id}finally{n.close()}}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
import{cloudInteraction as u}from"../../cloud-selection.js";import{runWithCliProcessSignal as n}from"../../process-signal.js";import{applyCloudMutationFile as c}from"../../../app/cloud/index.js";export default async function p({flags:r,json:t,log:o}){const i=await n(e=>c(r.file,{...u(t),signal:e,browser:r.browser,secretOutput:r["secret-output"],campaignId:r.campaign,productDir:r.dir,origin:r.url,projectId:r.project}));return t||o(JSON.stringify(i,null,2)),{ok:!0,receipt:i}}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
import{cloudInteraction as u}from"../../../cloud-selection.js";import{createCloudCampaign as n}from"../../../../app/cloud/control-actions.js";export default async function i({flags:t,json:r,log:e}){const o=await n({...u(r),origin:t.url,organisation:t.org,project:t.project,requestKey:t["request-key"],focus:t.focus,targetBugCount:t["target-bugs"]});return r||e(JSON.stringify(o,null,2)),{ok:!0,result:o}}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
import{cloudInteraction as u}from"../../cloud-selection.js";import{downloadCloudArtifact as n}from"../../../app/cloud/control-actions.js";export default async function e({flags:t,json:r,log:i}){const o=await n(t.receipt,t.path,t.output,{origin:t.url,...u(r)});return r||i(JSON.stringify(o,null,2)),{ok:!0,result:o}}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
import{cloudInteraction as c}from"../../cloud-selection.js";import{listCloudResources as n}from"../../../app/cloud/control-read.js";export default async function u({args:o,flags:r,json:e,log:t}){const i=await n(o.resource,{...c(e),origin:r.url,organisation:r.org,project:r.project,limit:r.limit,cursor:r.cursor,branch:r.branch,decision:r.decision,pr:r.pr,sort:r.sort,direction:r.direction,flaky:r.flaky,receipt:r.receipt,caseId:r.case,testPath:r.path,page:r.page,campaign:r.campaign,rootCauseKey:r["root-cause-key"],issueReference:r["issue-reference"],view:r.view,order:r.order});return e||t(JSON.stringify(i,null,2)),{ok:!0,result:i}}
|
|
@@ -1,13 +0,0 @@
|
|
|
1
|
-
import type { CommandContext } from "../../command-catalog.js";
|
|
2
|
-
export default function run({ flags, json, log }: CommandContext<"cloud login">): Promise<{
|
|
3
|
-
user: {
|
|
4
|
-
id: string;
|
|
5
|
-
email: string;
|
|
6
|
-
name: string;
|
|
7
|
-
};
|
|
8
|
-
expiresAt: string;
|
|
9
|
-
maximumExpiresAt: string;
|
|
10
|
-
origin: string;
|
|
11
|
-
reused: boolean;
|
|
12
|
-
ok: boolean;
|
|
13
|
-
}>;
|
|
@@ -1,2 +0,0 @@
|
|
|
1
|
-
import{runWithCliProcessSignal as s}from"../../process-signal.js";import{loginToCloud as u}from"../../../app/cloud/login.js";export default async function c({flags:r,json:e,log:i}){const o=await s(t=>u({origin:r.url,browser:r.browser,force:r.force,signal:t,notify:n=>process.stderr.write(`${n}
|
|
2
|
-
`)}));return e||i(`Connected to ${o.origin} as ${o.user.email}.`),{ok:!0,...o}}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
import{logoutOfCloud as e}from"../../../app/cloud/login.js";export default async function r({flags:n,json:t,log:i}){const o=await e({origin:n.url});return t||(i(`Signed out of ${o.origin}.`),o.environmentTokenActive&&i("TESTKIT_CLOUD_TOKEN is still set and will be used by Cloud commands.")),{ok:!0,...o}}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
import{applyMigration as p}from"../../../../app/cloud/migration/index.js";import{migrationProgressRenderer as i}from"./progress.js";export default async function l({flags:r,json:t,log:o,setExitCode:n}){const e=await p(r.plan,process.env,i());return e.complete||n(1),t||o(`${e.complete?"Complete":"Incomplete"}: ${e.reportPath} (${String(e.problems.length)} problems)`),e}
|
|
@@ -1,7 +0,0 @@
|
|
|
1
|
-
import type { CommandContext } from "../../../command-catalog.js";
|
|
2
|
-
export default function run({ flags, json, log, setExitCode }: CommandContext<"cloud migrate plan">): Promise<{
|
|
3
|
-
ready: boolean;
|
|
4
|
-
planPath: string;
|
|
5
|
-
problems: import("../../../../app/cloud/migration/model.js").MigrationProblem[];
|
|
6
|
-
summary: import("../../../../app/cloud/migration/model.js").MigrationPlan["sourceSummary"];
|
|
7
|
-
}>;
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
import{planMigration as o}from"../../../../app/cloud/migration/index.js";export default async function p({flags:r,json:i,log:t,setExitCode:n}){const e=await o({sourceDir:r.source,registry:r.registry,regressions:r.regressions,mapping:r.mapping,outputDir:r.output,origin:r.url,projectId:r.project,campaignId:r.campaign});return e.ready||n(1),i||t(`${e.ready?"Ready":"Blocked"}: ${e.planPath} (${String(e.problems.length)} problems)`),e}
|
|
@@ -1,2 +0,0 @@
|
|
|
1
|
-
const c={"source-verification":"Verifying source","journal-upgrade":"Upgraded recovery journal",packaging:"Packaging",uploading:"Uploading","receipt-accepted":"Receipt accepted","record-admission":"Admitting records",reconciliation:"Reconciling evidence and records",completion:"Import complete",failed:"Migration incomplete"};export function migrationProgressRenderer(){let i="",o=0;return r=>{const e=`${r.phase}:${r.runId??""}`,t=Date.now();if(e===i&&t-o<250&&r.phase!=="failed"&&r.phase!=="completion")return;i=e,o=t;const a=`${String(r.completedRuns)}/${String(r.totalRuns)} runs accepted, ${String(Math.max(0,r.totalRuns-r.completedRuns))} remaining`,s=`${String(r.bytesTransferred)} upload bytes, ${String(r.retries)} retries, ${String(r.verifiedBytes??0)} verified bytes`,n=[r.runId?`run ${r.runId}`:"",r.operation?`operation ${r.operation}`:""].filter(Boolean).join(", ");process.stderr.write(`[migration] ${c[r.phase]}: ${a}; ${s}${n?`; ${n}`:""}${r.detail?`; ${r.detail}`:""}
|
|
2
|
-
`)}}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
import{reconcileMigration as i}from"../../../../app/cloud/migration/index.js";import{migrationProgressRenderer as p}from"./progress.js";export default async function l({flags:r,json:o,log:t,setExitCode:n}){const e=await i(r.plan,process.env,p());return e.complete||n(1),o||t(`${e.complete?"Complete":"Incomplete"}: ${e.reportPath} (${String(e.problems.length)} problems)`),e}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
import{previewCloudPolicy as u}from"../../../../app/cloud/control-actions.js";export default async function e({args:o,flags:i,json:n,log:t}){const r=await u(o.policy,i.file,{origin:i.url,organisation:i.org});return n||t(JSON.stringify(r,null,2)),{ok:!0,result:r}}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
import{cloudInteraction as n}from"../../../cloud-selection.js";import{runWithCliProcessSignal as u}from"../../../process-signal.js";import{createCloudProject as s}from"../../../../app/cloud/control.js";export default async function c({flags:r,json:o,log:i}){const e=await u(t=>s({...n(o),origin:r.url,organisation:r.org,name:r.name,repository:r.repository,provider:r.provider,host:r.host,publishDecisions:r["publish-decisions"],requestKey:r["request-key"],browser:r.browser,signal:t}));return o||i(JSON.stringify(e,null,2)),{ok:!0,result:e}}
|
|
@@ -1,7 +0,0 @@
|
|
|
1
|
-
import type { CommandContext } from "../../command-catalog.js";
|
|
2
|
-
export default function run({ flags, json, log }: CommandContext<"cloud publish">): Promise<{
|
|
3
|
-
receipt: import("../../../cloud/client.js").CloudReceipt;
|
|
4
|
-
association?: import("../../../cloud/campaigns.js").CampaignResult;
|
|
5
|
-
dashboardUrl: string;
|
|
6
|
-
ok: boolean;
|
|
7
|
-
}>;
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
import{cloudInteraction as t}from"../../cloud-selection.js";import c from"node:path";import{publishRunToCloud as s}from"../../../app/cloud/index.js";export default async function d({flags:o,json:e,log:r}){const i=await s({campaignId:o.campaign,...t(e),origin:o.url,productDir:c.resolve(o.dir??process.cwd()),projectId:o.project,runId:o.run??null}),{receipt:n}=i;if(!e){r(`Published ${n.runId}: ${n.decision} (${n.id})`),r(i.dashboardUrl);for(const a of n.decisionReasons)r(` ${a.message}`);i.association&&r(`Campaign ${i.association.campaignId}: receipt ${i.association.changed?"linked":"already linked"}, version ${String(i.association.version)}`)}return{ok:!0,...i}}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
import{revokeCloudSession as t}from"../../../../app/cloud/control-actions.js";export default async function u({args:e,flags:r,json:i,log:n}){const o=await t(e.id,{origin:r.url});return i||n(`Revoked Cloud session ${o.id}.`),{ok:!0,result:o}}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
import{cloudInteraction as e}from"../../cloud-selection.js";import{showCloudResource as u}from"../../../app/cloud/control-read.js";export default async function c({args:o,flags:r,json:t,log:n}){const i=await u(o.resource,o.id,{...e(t),origin:r.url,organisation:r.org,project:r.project,campaign:r.campaign});return t||n(JSON.stringify(i,null,2)),{ok:!0,result:i}}
|