@ploaness/cli 1.12.0 → 1.14.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/bin.js +38 -15
- package/dist/bin.js.map +1 -1
- package/dist/change-scope.d.ts +24 -0
- package/dist/change-scope.d.ts.map +1 -0
- package/dist/change-scope.js +132 -0
- package/dist/change-scope.js.map +1 -0
- package/dist/checks/assets.d.ts +7 -0
- package/dist/checks/assets.d.ts.map +1 -1
- package/dist/checks/assets.js +15 -1
- package/dist/checks/assets.js.map +1 -1
- package/dist/checks/history.js +1 -1
- package/dist/checks/history.js.map +1 -1
- package/dist/checks/infra.d.ts.map +1 -1
- package/dist/checks/infra.js +16 -5
- package/dist/checks/infra.js.map +1 -1
- package/dist/checks/tests.d.ts +6 -3
- package/dist/checks/tests.d.ts.map +1 -1
- package/dist/checks/tests.js +55 -19
- package/dist/checks/tests.js.map +1 -1
- package/dist/checks/toolchain.d.ts +7 -0
- package/dist/checks/toolchain.d.ts.map +1 -1
- package/dist/checks/toolchain.js +19 -6
- package/dist/checks/toolchain.js.map +1 -1
- package/dist/checks/wiring.d.ts.map +1 -1
- package/dist/checks/wiring.js +4 -7
- package/dist/checks/wiring.js.map +1 -1
- package/dist/commands/init.d.ts.map +1 -1
- package/dist/commands/init.js +7 -27
- package/dist/commands/init.js.map +1 -1
- package/dist/commands/scope.d.ts +9 -0
- package/dist/commands/scope.d.ts.map +1 -0
- package/dist/commands/scope.js +32 -0
- package/dist/commands/scope.js.map +1 -0
- package/dist/commands/sync.d.ts +1 -1
- package/dist/commands/sync.d.ts.map +1 -1
- package/dist/commands/sync.js +29 -2
- package/dist/commands/sync.js.map +1 -1
- package/dist/commands/verify.d.ts +7 -4
- package/dist/commands/verify.d.ts.map +1 -1
- package/dist/commands/verify.js +33 -23
- package/dist/commands/verify.js.map +1 -1
- package/dist/context.d.ts +10 -1
- package/dist/context.d.ts.map +1 -1
- package/dist/context.js +5 -3
- package/dist/context.js.map +1 -1
- package/dist/gates.d.ts +11 -10
- package/dist/gates.d.ts.map +1 -1
- package/dist/gates.js +68 -67
- package/dist/gates.js.map +1 -1
- package/dist/pinned-browsers.d.ts +25 -0
- package/dist/pinned-browsers.d.ts.map +1 -0
- package/dist/pinned-browsers.js +138 -0
- package/dist/pinned-browsers.js.map +1 -0
- package/dist/report.d.ts +4 -3
- package/dist/report.d.ts.map +1 -1
- package/dist/report.js +7 -7
- package/dist/report.js.map +1 -1
- package/dist/test-invocation.d.ts.map +1 -1
- package/dist/test-invocation.js +4 -1
- package/dist/test-invocation.js.map +1 -1
- package/dist/working-tree.d.ts +2 -0
- package/dist/working-tree.d.ts.map +1 -1
- package/dist/working-tree.js +9 -0
- package/dist/working-tree.js.map +1 -1
- package/package.json +4 -4
package/dist/gates.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
// Registry order is run order: structural preconditions precede analyzers, and tree verification is last.
|
|
2
|
-
import { planSteps } from '@ploaness/governance';
|
|
2
|
+
import { coversLevel, planSteps, } from '@ploaness/governance';
|
|
3
3
|
import { assets } from './checks/assets.js';
|
|
4
4
|
import { blocklist } from './checks/blocklist.js';
|
|
5
5
|
import { complianceReport } from './checks/compliance-report.js';
|
|
@@ -25,13 +25,13 @@ import { build, endToEnd, tests } from './checks/tests.js';
|
|
|
25
25
|
import { architecture, biome, biomeSchema, css, eslint, knip, typeCoverage, types } from './checks/toolchain.js';
|
|
26
26
|
import { treeSnapshot, treeVerify } from './checks/tree.js';
|
|
27
27
|
import { wiring } from './checks/wiring.js';
|
|
28
|
-
/**
|
|
29
|
-
const
|
|
28
|
+
/** Quick verification: the static gates that run on every invocation. */
|
|
29
|
+
const QUICK_GATES = [
|
|
30
30
|
{
|
|
31
31
|
id: 'preflight',
|
|
32
32
|
scope: 'repository',
|
|
33
33
|
title: 'supported Payload project',
|
|
34
|
-
|
|
34
|
+
level: 'quick',
|
|
35
35
|
isPrecondition: true,
|
|
36
36
|
run: preflight,
|
|
37
37
|
},
|
|
@@ -39,89 +39,89 @@ const DEFAULT_GATES = [
|
|
|
39
39
|
id: 'wiring',
|
|
40
40
|
scope: 'repository',
|
|
41
41
|
title: 'harness wiring',
|
|
42
|
-
|
|
42
|
+
level: 'quick',
|
|
43
43
|
isPrecondition: true,
|
|
44
44
|
run: wiring,
|
|
45
45
|
},
|
|
46
|
-
{ id: 'assets', scope: 'repository', title: 'managed files',
|
|
46
|
+
{ id: 'assets', scope: 'repository', title: 'managed files', level: 'quick', run: assets },
|
|
47
47
|
{
|
|
48
48
|
id: 'tree-snapshot',
|
|
49
49
|
scope: 'repository',
|
|
50
50
|
title: 'working-tree fingerprint',
|
|
51
|
-
|
|
51
|
+
level: 'quick',
|
|
52
52
|
run: treeSnapshot,
|
|
53
53
|
},
|
|
54
|
-
{ id: 'types', scope: 'package', title: 'strict type check',
|
|
54
|
+
{ id: 'types', scope: 'package', title: 'strict type check', level: 'quick', run: types },
|
|
55
55
|
{
|
|
56
56
|
id: 'biome',
|
|
57
57
|
scope: 'package',
|
|
58
58
|
title: 'formatting and fast lint',
|
|
59
|
-
|
|
59
|
+
level: 'quick',
|
|
60
60
|
run: biome,
|
|
61
61
|
},
|
|
62
62
|
{
|
|
63
63
|
id: 'biome-schema',
|
|
64
64
|
scope: 'repository',
|
|
65
65
|
title: 'Biome schema drift',
|
|
66
|
-
|
|
66
|
+
level: 'quick',
|
|
67
67
|
run: biomeSchema,
|
|
68
68
|
},
|
|
69
69
|
{
|
|
70
70
|
id: 'oxlint',
|
|
71
71
|
scope: 'package',
|
|
72
72
|
title: 'Native JavaScript and TypeScript rules',
|
|
73
|
-
|
|
73
|
+
level: 'quick',
|
|
74
74
|
run: oxlint,
|
|
75
75
|
},
|
|
76
|
-
{ id: 'eslint', scope: 'package', title: 'type-aware lint',
|
|
76
|
+
{ id: 'eslint', scope: 'package', title: 'type-aware lint', level: 'quick', run: eslint },
|
|
77
77
|
{
|
|
78
78
|
id: 'conventions',
|
|
79
79
|
scope: 'repository',
|
|
80
80
|
title: 'source conventions',
|
|
81
|
-
|
|
81
|
+
level: 'quick',
|
|
82
82
|
run: conventions,
|
|
83
83
|
},
|
|
84
84
|
{
|
|
85
85
|
id: 'editorconfig',
|
|
86
86
|
scope: 'repository',
|
|
87
87
|
title: 'committed formatting',
|
|
88
|
-
|
|
88
|
+
level: 'quick',
|
|
89
89
|
run: editorconfig,
|
|
90
90
|
},
|
|
91
91
|
{
|
|
92
92
|
id: 'suppressions',
|
|
93
93
|
scope: 'package',
|
|
94
94
|
title: 'suppression ceiling',
|
|
95
|
-
|
|
95
|
+
level: 'quick',
|
|
96
96
|
run: suppressions,
|
|
97
97
|
},
|
|
98
|
-
{ id: 'css', scope: 'package', title: 'style sheets',
|
|
98
|
+
{ id: 'css', scope: 'package', title: 'style sheets', level: 'quick', run: css },
|
|
99
99
|
{
|
|
100
100
|
id: 'tailwind-tokens',
|
|
101
101
|
scope: 'package',
|
|
102
102
|
title: 'token-bound Tailwind values',
|
|
103
|
-
|
|
103
|
+
level: 'quick',
|
|
104
104
|
run: tailwindTokens,
|
|
105
105
|
},
|
|
106
106
|
{
|
|
107
107
|
id: 'arch',
|
|
108
108
|
scope: 'package',
|
|
109
109
|
title: 'module architecture',
|
|
110
|
-
|
|
110
|
+
level: 'quick',
|
|
111
111
|
run: architecture,
|
|
112
112
|
},
|
|
113
113
|
{
|
|
114
114
|
id: 'type-coverage',
|
|
115
115
|
scope: 'package',
|
|
116
116
|
title: 'type coverage',
|
|
117
|
-
|
|
117
|
+
level: 'quick',
|
|
118
118
|
run: typeCoverage,
|
|
119
119
|
},
|
|
120
120
|
{
|
|
121
121
|
id: 'payload-generated',
|
|
122
122
|
scope: 'payload',
|
|
123
123
|
title: 'generated Payload artefacts',
|
|
124
|
-
|
|
124
|
+
level: 'quick',
|
|
125
125
|
run: payloadGenerated,
|
|
126
126
|
},
|
|
127
127
|
{
|
|
@@ -130,14 +130,14 @@ const DEFAULT_GATES = [
|
|
|
130
130
|
id: 'payload-defaults',
|
|
131
131
|
scope: 'payload',
|
|
132
132
|
title: 'access decided in the built configuration',
|
|
133
|
-
|
|
133
|
+
level: 'quick',
|
|
134
134
|
run: payloadDefaults,
|
|
135
135
|
},
|
|
136
136
|
{
|
|
137
137
|
id: 'generated-denial',
|
|
138
138
|
scope: 'repository',
|
|
139
139
|
title: 'generated files denied',
|
|
140
|
-
|
|
140
|
+
level: 'quick',
|
|
141
141
|
run: generatedDenial,
|
|
142
142
|
},
|
|
143
143
|
{
|
|
@@ -146,7 +146,7 @@ const DEFAULT_GATES = [
|
|
|
146
146
|
// it here meant it ran only where Payload did.
|
|
147
147
|
scope: 'package',
|
|
148
148
|
title: 'source usage rules',
|
|
149
|
-
|
|
149
|
+
level: 'quick',
|
|
150
150
|
run: payloadRules,
|
|
151
151
|
},
|
|
152
152
|
{
|
|
@@ -155,43 +155,43 @@ const DEFAULT_GATES = [
|
|
|
155
155
|
id: 'admin-views',
|
|
156
156
|
scope: 'payload',
|
|
157
157
|
title: 'custom admin views scanned',
|
|
158
|
-
|
|
158
|
+
level: 'quick',
|
|
159
159
|
run: adminViews,
|
|
160
160
|
},
|
|
161
161
|
{
|
|
162
162
|
id: 'config-refs',
|
|
163
163
|
scope: 'package',
|
|
164
164
|
title: 'config references',
|
|
165
|
-
|
|
165
|
+
level: 'quick',
|
|
166
166
|
run: configReferences,
|
|
167
167
|
},
|
|
168
|
-
{ id: 'secrets', scope: 'repository', title: 'secret scan',
|
|
168
|
+
{ id: 'secrets', scope: 'repository', title: 'secret scan', level: 'quick', run: secrets },
|
|
169
169
|
{
|
|
170
170
|
id: 'licenses',
|
|
171
171
|
scope: 'repository',
|
|
172
172
|
title: 'dependency licenses',
|
|
173
|
-
|
|
173
|
+
level: 'quick',
|
|
174
174
|
run: licenses,
|
|
175
175
|
},
|
|
176
176
|
{
|
|
177
177
|
id: 'vulnerabilities',
|
|
178
178
|
scope: 'repository',
|
|
179
179
|
title: 'known vulnerabilities',
|
|
180
|
-
|
|
180
|
+
level: 'quick',
|
|
181
181
|
run: vulnerabilities,
|
|
182
182
|
},
|
|
183
183
|
{
|
|
184
184
|
id: 'install-scripts',
|
|
185
185
|
scope: 'repository',
|
|
186
186
|
title: 'install-script allowlist',
|
|
187
|
-
|
|
187
|
+
level: 'quick',
|
|
188
188
|
run: installScripts,
|
|
189
189
|
},
|
|
190
190
|
{
|
|
191
191
|
id: 'release-age',
|
|
192
192
|
scope: 'repository',
|
|
193
193
|
title: 'release-age floor',
|
|
194
|
-
|
|
194
|
+
level: 'quick',
|
|
195
195
|
run: releaseAge,
|
|
196
196
|
},
|
|
197
197
|
// Before `deps`, so a refused image or package is named before the freshness report that has to
|
|
@@ -200,21 +200,21 @@ const DEFAULT_GATES = [
|
|
|
200
200
|
id: 'blocklist',
|
|
201
201
|
scope: 'repository',
|
|
202
202
|
title: 'refused images and packages',
|
|
203
|
-
|
|
203
|
+
level: 'quick',
|
|
204
204
|
run: blocklist,
|
|
205
205
|
},
|
|
206
206
|
{
|
|
207
207
|
id: 'deps',
|
|
208
208
|
scope: 'repository',
|
|
209
209
|
title: 'dependency freshness',
|
|
210
|
-
|
|
210
|
+
level: 'quick',
|
|
211
211
|
run: dependencyFreshness,
|
|
212
212
|
},
|
|
213
213
|
{
|
|
214
214
|
id: 'image-assets',
|
|
215
215
|
scope: 'repository',
|
|
216
216
|
title: 'image integrity',
|
|
217
|
-
|
|
217
|
+
level: 'quick',
|
|
218
218
|
run: imageAssets,
|
|
219
219
|
},
|
|
220
220
|
{
|
|
@@ -223,14 +223,14 @@ const DEFAULT_GATES = [
|
|
|
223
223
|
id: 'environment',
|
|
224
224
|
scope: 'repository',
|
|
225
225
|
title: 'environment variable coherence',
|
|
226
|
-
|
|
226
|
+
level: 'quick',
|
|
227
227
|
run: environment,
|
|
228
228
|
},
|
|
229
229
|
{
|
|
230
230
|
id: 'docs',
|
|
231
231
|
scope: 'repository',
|
|
232
232
|
title: 'agent documentation references',
|
|
233
|
-
|
|
233
|
+
level: 'quick',
|
|
234
234
|
// The gate identifiers are supplied as reserved words: documenting a gate must not be read as a
|
|
235
235
|
// reference to a script that no longer exists, since the two share a vocabulary. ALL_GATES is read
|
|
236
236
|
// when the gate runs, by which point this module has finished initialising.
|
|
@@ -240,105 +240,106 @@ const DEFAULT_GATES = [
|
|
|
240
240
|
id: 'skills',
|
|
241
241
|
scope: 'repository',
|
|
242
242
|
title: 'agent skill manifests',
|
|
243
|
-
|
|
243
|
+
level: 'quick',
|
|
244
244
|
run: skills,
|
|
245
245
|
},
|
|
246
246
|
{
|
|
247
247
|
id: 'docker',
|
|
248
248
|
scope: 'repository',
|
|
249
249
|
title: 'container definitions',
|
|
250
|
-
|
|
250
|
+
level: 'quick',
|
|
251
251
|
run: containers,
|
|
252
252
|
},
|
|
253
253
|
{
|
|
254
254
|
id: 'actions',
|
|
255
255
|
scope: 'repository',
|
|
256
256
|
title: 'workflow definitions',
|
|
257
|
-
|
|
257
|
+
level: 'quick',
|
|
258
258
|
run: actions,
|
|
259
259
|
},
|
|
260
260
|
{
|
|
261
261
|
id: 'knip',
|
|
262
262
|
scope: 'package',
|
|
263
263
|
title: 'dead code and unused dependencies',
|
|
264
|
-
|
|
264
|
+
level: 'quick',
|
|
265
265
|
run: knip,
|
|
266
266
|
},
|
|
267
|
-
{ id: 'tests', scope: 'package', title: 'suite and coverage',
|
|
267
|
+
{ id: 'tests', scope: 'package', title: 'suite and coverage', level: 'quick', run: tests },
|
|
268
268
|
];
|
|
269
269
|
/**
|
|
270
|
-
*
|
|
271
|
-
* end-to-end checks, and a workspace dependency inventory.
|
|
270
|
+
* Delta verification adds the shell scripts, the infrastructure definitions, history, build, bundle,
|
|
271
|
+
* end-to-end checks, and a workspace dependency inventory. Full verification runs the same gates; what
|
|
272
|
+
* it adds is the complete browser sweep in every engine.
|
|
272
273
|
*/
|
|
273
|
-
const
|
|
274
|
+
const DELTA_GATES = [
|
|
274
275
|
// First in the tier because it is the cheapest thing in it: a static read of the tracked scripts,
|
|
275
276
|
// ahead of a production build that takes minutes. A broken deployment script should be reported
|
|
276
277
|
// before the time is spent, not after.
|
|
277
|
-
{ id: 'shell', scope: 'repository', title: 'shell scripts',
|
|
278
|
+
{ id: 'shell', scope: 'repository', title: 'shell scripts', level: 'delta', run: shell },
|
|
278
279
|
{
|
|
279
280
|
id: 'infra',
|
|
280
281
|
scope: 'repository',
|
|
281
282
|
title: 'infrastructure definitions',
|
|
282
|
-
|
|
283
|
+
level: 'delta',
|
|
283
284
|
run: infrastructure,
|
|
284
285
|
},
|
|
285
286
|
{
|
|
286
287
|
id: 'require-full-history',
|
|
287
288
|
scope: 'repository',
|
|
288
289
|
title: 'full history present',
|
|
289
|
-
|
|
290
|
+
level: 'delta',
|
|
290
291
|
run: requireFullHistory,
|
|
291
292
|
},
|
|
292
293
|
{
|
|
293
294
|
id: 'commit-history',
|
|
294
295
|
scope: 'repository',
|
|
295
296
|
title: 'commit messages across the whole history',
|
|
296
|
-
|
|
297
|
-
|
|
297
|
+
level: 'delta',
|
|
298
|
+
// Wrapped, because its second parameter is a revision range for the `commit-message` command.
|
|
299
|
+
run: (repository) => commitHistory(repository),
|
|
298
300
|
},
|
|
299
301
|
{
|
|
300
302
|
id: 'linear-history',
|
|
301
303
|
scope: 'repository',
|
|
302
304
|
title: 'linear history',
|
|
303
|
-
|
|
305
|
+
level: 'delta',
|
|
304
306
|
run: linearHistory,
|
|
305
307
|
},
|
|
306
|
-
{ id: 'build', scope: 'package', title: 'production build',
|
|
307
|
-
{ id: 'bundle', scope: 'package', title: 'client bundle budget',
|
|
308
|
-
{ id: 'e2e', scope: 'package', title: 'end-to-end suite',
|
|
309
|
-
{ id: 'sbom', scope: 'repository', title: 'dependency inventory',
|
|
308
|
+
{ id: 'build', scope: 'package', title: 'production build', level: 'delta', run: build },
|
|
309
|
+
{ id: 'bundle', scope: 'package', title: 'client bundle budget', level: 'delta', run: bundle },
|
|
310
|
+
{ id: 'e2e', scope: 'package', title: 'end-to-end suite', level: 'delta', run: endToEnd },
|
|
311
|
+
{ id: 'sbom', scope: 'repository', title: 'dependency inventory', level: 'delta', run: sbomInventory },
|
|
310
312
|
{
|
|
311
313
|
id: 'compliance-report',
|
|
312
314
|
scope: 'repository',
|
|
313
315
|
title: 'compliance report',
|
|
314
|
-
|
|
316
|
+
level: 'delta',
|
|
315
317
|
run: complianceReport,
|
|
316
318
|
},
|
|
317
319
|
];
|
|
318
|
-
// Last
|
|
319
|
-
//
|
|
320
|
-
//
|
|
321
|
-
// verification was the one mode that could not see it.
|
|
320
|
+
// Last at every level. It once closed the static gates, with the browser-level gates appended after it,
|
|
321
|
+
// so `build`, `bundle`, and `e2e` all ran once the fingerprint had already been compared - and `next
|
|
322
|
+
// build` rewriting a tracked file is exactly what this gate exists to catch.
|
|
322
323
|
const TREE_VERIFY = {
|
|
323
324
|
id: 'tree-verify',
|
|
324
325
|
scope: 'repository',
|
|
325
326
|
title: 'working tree unchanged',
|
|
326
|
-
|
|
327
|
+
level: 'quick',
|
|
327
328
|
run: treeVerify,
|
|
328
329
|
};
|
|
329
330
|
/** Every gate ploaness knows, in run order. */
|
|
330
|
-
export const ALL_GATES = [...
|
|
331
|
+
export const ALL_GATES = [...QUICK_GATES, ...DELTA_GATES, TREE_VERIFY];
|
|
331
332
|
/**
|
|
332
|
-
* The gates for one verification
|
|
333
|
-
*
|
|
333
|
+
* The gates for one verification level. Each level includes the one before it: the extra gates are
|
|
334
|
+
* added rather than substituted, so a later level is never a different verdict about less.
|
|
334
335
|
*/
|
|
335
|
-
export const gatesFor = (
|
|
336
|
+
export const gatesFor = (level) => ALL_GATES.filter((gate) => coversLevel(level, gate.level));
|
|
336
337
|
/** Look up one gate by identifier. */
|
|
337
338
|
export const gateById = (id) => ALL_GATES.find((gate) => gate.id === id);
|
|
338
339
|
const describe = (gate) => ({
|
|
339
340
|
id: gate.id,
|
|
340
341
|
scope: gate.scope,
|
|
341
|
-
|
|
342
|
+
level: gate.level,
|
|
342
343
|
});
|
|
343
344
|
/**
|
|
344
345
|
* Expand the registry into the invocations one run performs against this repository.
|
|
@@ -347,12 +348,12 @@ const describe = (gate) => ({
|
|
|
347
348
|
* before members existed; this only zips that plan back onto the registry entries and the member
|
|
348
349
|
* objects the gates are actually handed.
|
|
349
350
|
* @param repository the repository being judged.
|
|
350
|
-
* @param
|
|
351
|
+
* @param level the level the run was asked for.
|
|
351
352
|
* @returns one planned invocation per step, in run order.
|
|
352
353
|
*/
|
|
353
|
-
export const planFor = (repository,
|
|
354
|
+
export const planFor = (repository, level) => {
|
|
354
355
|
const members = repository.members.map((member) => ({ path: member.path, isPayload: member.isPayload }));
|
|
355
|
-
return planSteps(ALL_GATES.map((gate) => describe(gate)), members,
|
|
356
|
+
return planSteps(ALL_GATES.map((gate) => describe(gate)), members, level).flatMap((step) => {
|
|
356
357
|
const gate = gateById(step.gateId);
|
|
357
358
|
if (gate === undefined) {
|
|
358
359
|
return [];
|
package/dist/gates.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"gates.js","sourceRoot":"","sources":["../src/gates.ts"],"names":[],"mappings":"AAAA,0GAA0G;AAE1G,OAAO,
|
|
1
|
+
{"version":3,"file":"gates.js","sourceRoot":"","sources":["../src/gates.ts"],"names":[],"mappings":"AAAA,0GAA0G;AAE1G,OAAO,EACL,WAAW,EAIX,SAAS,GAEV,MAAM,sBAAsB,CAAA;AAC7B,OAAO,EAAE,MAAM,EAAE,MAAM,oBAAoB,CAAA;AAC3C,OAAO,EAAE,SAAS,EAAE,MAAM,uBAAuB,CAAA;AACjD,OAAO,EAAE,gBAAgB,EAAE,MAAM,+BAA+B,CAAA;AAChE,OAAO,EAAE,OAAO,EAAE,UAAU,EAAE,OAAO,EAAE,MAAM,wBAAwB,CAAA;AACrE,OAAO,EAAE,WAAW,EAAE,MAAM,yBAAyB,CAAA;AACrD,OAAO,EAAE,mBAAmB,EAAE,QAAQ,EAAE,eAAe,EAAE,MAAM,0BAA0B,CAAA;AACzF,OAAO,EAAE,YAAY,EAAE,MAAM,0BAA0B,CAAA;AACvD,OAAO,EAAE,WAAW,EAAE,MAAM,yBAAyB,CAAA;AACrD,OAAO,EAAE,eAAe,EAAE,MAAM,uBAAuB,CAAA;AACvD,OAAO,EAAE,aAAa,EAAE,aAAa,EAAE,kBAAkB,EAAE,MAAM,qBAAqB,CAAA;AACtF,OAAO,EAAE,cAAc,EAAE,MAAM,mBAAmB,CAAA;AAClD,OAAO,EAAE,cAAc,EAAE,UAAU,EAAE,MAAM,qBAAqB,CAAA;AAChE,OAAO,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,uBAAuB,CAAA;AAC3D,OAAO,EAAE,MAAM,EAAE,MAAM,oBAAoB,CAAA;AAC3C,OAAO,EAAE,UAAU,EAAE,eAAe,EAAE,gBAAgB,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAA;AACjG,OAAO,EAAE,SAAS,EAAE,MAAM,uBAAuB,CAAA;AACjD,OAAO,EAAE,gBAAgB,EAAE,aAAa,EAAE,MAAM,EAAE,MAAM,wBAAwB,CAAA;AAChF,OAAO,EAAE,aAAa,EAAE,MAAM,kBAAkB,CAAA;AAChD,OAAO,EAAE,KAAK,EAAE,MAAM,mBAAmB,CAAA;AACzC,OAAO,EAAE,YAAY,EAAE,MAAM,0BAA0B,CAAA;AACvD,OAAO,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAA;AACrD,OAAO,EAAE,KAAK,EAAE,QAAQ,EAAE,KAAK,EAAE,MAAM,mBAAmB,CAAA;AAC1D,OAAO,EAAE,YAAY,EAAE,KAAK,EAAE,WAAW,EAAE,GAAG,EAAE,MAAM,EAAE,IAAI,EAAE,YAAY,EAAE,KAAK,EAAE,MAAM,uBAAuB,CAAA;AAChH,OAAO,EAAE,YAAY,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAA;AAC3D,OAAO,EAAE,MAAM,EAAE,MAAM,oBAAoB,CAAA;AAwC3C,yEAAyE;AACzE,MAAM,WAAW,GAAoB;IACnC;QACE,EAAE,EAAE,WAAW;QACf,KAAK,EAAE,YAAY;QACnB,KAAK,EAAE,2BAA2B;QAClC,KAAK,EAAE,OAAO;QACd,cAAc,EAAE,IAAI;QACpB,GAAG,EAAE,SAAS;KACf;IACD;QACE,EAAE,EAAE,QAAQ;QACZ,KAAK,EAAE,YAAY;QACnB,KAAK,EAAE,gBAAgB;QACvB,KAAK,EAAE,OAAO;QACd,cAAc,EAAE,IAAI;QACpB,GAAG,EAAE,MAAM;KACZ;IACD,EAAE,EAAE,EAAE,QAAQ,EAAE,KAAK,EAAE,YAAY,EAAE,KAAK,EAAE,eAAe,EAAE,KAAK,EAAE,OAAO,EAAE,GAAG,EAAE,MAAM,EAAE;IAC1F;QACE,EAAE,EAAE,eAAe;QACnB,KAAK,EAAE,YAAY;QACnB,KAAK,EAAE,0BAA0B;QACjC,KAAK,EAAE,OAAO;QACd,GAAG,EAAE,YAAY;KAClB;IACD,EAAE,EAAE,EAAE,OAAO,EAAE,KAAK,EAAE,SAAS,EAAE,KAAK,EAAE,mBAAmB,EAAE,KAAK,EAAE,OAAO,EAAE,GAAG,EAAE,KAAK,EAAE;IACzF;QACE,EAAE,EAAE,OAAO;QACX,KAAK,EAAE,SAAS;QAChB,KAAK,EAAE,0BAA0B;QACjC,KAAK,EAAE,OAAO;QACd,GAAG,EAAE,KAAK;KACX;IACD;QACE,EAAE,EAAE,cAAc;QAClB,KAAK,EAAE,YAAY;QACnB,KAAK,EAAE,oBAAoB;QAC3B,KAAK,EAAE,OAAO;QACd,GAAG,EAAE,WAAW;KACjB;IACD;QACE,EAAE,EAAE,QAAQ;QACZ,KAAK,EAAE,SAAS;QAChB,KAAK,EAAE,wCAAwC;QAC/C,KAAK,EAAE,OAAO;QACd,GAAG,EAAE,MAAM;KACZ;IACD,EAAE,EAAE,EAAE,QAAQ,EAAE,KAAK,EAAE,SAAS,EAAE,KAAK,EAAE,iBAAiB,EAAE,KAAK,EAAE,OAAO,EAAE,GAAG,EAAE,MAAM,EAAE;IACzF;QACE,EAAE,EAAE,aAAa;QACjB,KAAK,EAAE,YAAY;QACnB,KAAK,EAAE,oBAAoB;QAC3B,KAAK,EAAE,OAAO;QACd,GAAG,EAAE,WAAW;KACjB;IACD;QACE,EAAE,EAAE,cAAc;QAClB,KAAK,EAAE,YAAY;QACnB,KAAK,EAAE,sBAAsB;QAC7B,KAAK,EAAE,OAAO;QACd,GAAG,EAAE,YAAY;KAClB;IACD;QACE,EAAE,EAAE,cAAc;QAClB,KAAK,EAAE,SAAS;QAChB,KAAK,EAAE,qBAAqB;QAC5B,KAAK,EAAE,OAAO;QACd,GAAG,EAAE,YAAY;KAClB;IACD,EAAE,EAAE,EAAE,KAAK,EAAE,KAAK,EAAE,SAAS,EAAE,KAAK,EAAE,cAAc,EAAE,KAAK,EAAE,OAAO,EAAE,GAAG,EAAE,GAAG,EAAE;IAChF;QACE,EAAE,EAAE,iBAAiB;QACrB,KAAK,EAAE,SAAS;QAChB,KAAK,EAAE,6BAA6B;QACpC,KAAK,EAAE,OAAO;QACd,GAAG,EAAE,cAAc;KACpB;IACD;QACE,EAAE,EAAE,MAAM;QACV,KAAK,EAAE,SAAS;QAChB,KAAK,EAAE,qBAAqB;QAC5B,KAAK,EAAE,OAAO;QACd,GAAG,EAAE,YAAY;KAClB;IACD;QACE,EAAE,EAAE,eAAe;QACnB,KAAK,EAAE,SAAS;QAChB,KAAK,EAAE,eAAe;QACtB,KAAK,EAAE,OAAO;QACd,GAAG,EAAE,YAAY;KAClB;IACD;QACE,EAAE,EAAE,mBAAmB;QACvB,KAAK,EAAE,SAAS;QAChB,KAAK,EAAE,6BAA6B;QACpC,KAAK,EAAE,OAAO;QACd,GAAG,EAAE,gBAAgB;KACtB;IACD;QACE,mGAAmG;QACnG,uEAAuE;QACvE,EAAE,EAAE,kBAAkB;QACtB,KAAK,EAAE,SAAS;QAChB,KAAK,EAAE,2CAA2C;QAClD,KAAK,EAAE,OAAO;QACd,GAAG,EAAE,eAAe;KACrB;IACD;QACE,EAAE,EAAE,kBAAkB;QACtB,KAAK,EAAE,YAAY;QACnB,KAAK,EAAE,wBAAwB;QAC/B,KAAK,EAAE,OAAO;QACd,GAAG,EAAE,eAAe;KACrB;IACD;QACE,EAAE,EAAE,eAAe;QACnB,kGAAkG;QAClG,+CAA+C;QAC/C,KAAK,EAAE,SAAS;QAChB,KAAK,EAAE,oBAAoB;QAC3B,KAAK,EAAE,OAAO;QACd,GAAG,EAAE,YAAY;KAClB;IACD;QACE,6FAA6F;QAC7F,qEAAqE;QACrE,EAAE,EAAE,aAAa;QACjB,KAAK,EAAE,SAAS;QAChB,KAAK,EAAE,4BAA4B;QACnC,KAAK,EAAE,OAAO;QACd,GAAG,EAAE,UAAU;KAChB;IACD;QACE,EAAE,EAAE,aAAa;QACjB,KAAK,EAAE,SAAS;QAChB,KAAK,EAAE,mBAAmB;QAC1B,KAAK,EAAE,OAAO;QACd,GAAG,EAAE,gBAAgB;KACtB;IACD,EAAE,EAAE,EAAE,SAAS,EAAE,KAAK,EAAE,YAAY,EAAE,KAAK,EAAE,aAAa,EAAE,KAAK,EAAE,OAAO,EAAE,GAAG,EAAE,OAAO,EAAE;IAC1F;QACE,EAAE,EAAE,UAAU;QACd,KAAK,EAAE,YAAY;QACnB,KAAK,EAAE,qBAAqB;QAC5B,KAAK,EAAE,OAAO;QACd,GAAG,EAAE,QAAQ;KACd;IACD;QACE,EAAE,EAAE,iBAAiB;QACrB,KAAK,EAAE,YAAY;QACnB,KAAK,EAAE,uBAAuB;QAC9B,KAAK,EAAE,OAAO;QACd,GAAG,EAAE,eAAe;KACrB;IACD;QACE,EAAE,EAAE,iBAAiB;QACrB,KAAK,EAAE,YAAY;QACnB,KAAK,EAAE,0BAA0B;QACjC,KAAK,EAAE,OAAO;QACd,GAAG,EAAE,cAAc;KACpB;IACD;QACE,EAAE,EAAE,aAAa;QACjB,KAAK,EAAE,YAAY;QACnB,KAAK,EAAE,mBAAmB;QAC1B,KAAK,EAAE,OAAO;QACd,GAAG,EAAE,UAAU;KAChB;IACD,gGAAgG;IAChG,oBAAoB;IACpB;QACE,EAAE,EAAE,WAAW;QACf,KAAK,EAAE,YAAY;QACnB,KAAK,EAAE,6BAA6B;QACpC,KAAK,EAAE,OAAO;QACd,GAAG,EAAE,SAAS;KACf;IACD;QACE,EAAE,EAAE,MAAM;QACV,KAAK,EAAE,YAAY;QACnB,KAAK,EAAE,sBAAsB;QAC7B,KAAK,EAAE,OAAO;QACd,GAAG,EAAE,mBAAmB;KACzB;IACD;QACE,EAAE,EAAE,cAAc;QAClB,KAAK,EAAE,YAAY;QACnB,KAAK,EAAE,iBAAiB;QACxB,KAAK,EAAE,OAAO;QACd,GAAG,EAAE,WAAW;KACjB;IACD;QACE,mGAAmG;QACnG,qFAAqF;QACrF,EAAE,EAAE,aAAa;QACjB,KAAK,EAAE,YAAY;QACnB,KAAK,EAAE,gCAAgC;QACvC,KAAK,EAAE,OAAO;QACd,GAAG,EAAE,WAAW;KACjB;IACD;QACE,EAAE,EAAE,MAAM;QACV,KAAK,EAAE,YAAY;QACnB,KAAK,EAAE,gCAAgC;QACvC,KAAK,EAAE,OAAO;QACd,gGAAgG;QAChG,mGAAmG;QACnG,4EAA4E;QAC5E,GAAG,EAAE,CAAC,UAAsB,EAAc,EAAE,CAC1C,aAAa,CAAC,UAAU,EAAE,IAAI,GAAG,CAAC,SAAS,CAAC,GAAG,CAAC,CAAC,IAAU,EAAU,EAAE,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC,CAAC;KACrF;IACD;QACE,EAAE,EAAE,QAAQ;QACZ,KAAK,EAAE,YAAY;QACnB,KAAK,EAAE,uBAAuB;QAC9B,KAAK,EAAE,OAAO;QACd,GAAG,EAAE,MAAM;KACZ;IACD;QACE,EAAE,EAAE,QAAQ;QACZ,KAAK,EAAE,YAAY;QACnB,KAAK,EAAE,uBAAuB;QAC9B,KAAK,EAAE,OAAO;QACd,GAAG,EAAE,UAAU;KAChB;IACD;QACE,EAAE,EAAE,SAAS;QACb,KAAK,EAAE,YAAY;QACnB,KAAK,EAAE,sBAAsB;QAC7B,KAAK,EAAE,OAAO;QACd,GAAG,EAAE,OAAO;KACb;IACD;QACE,EAAE,EAAE,MAAM;QACV,KAAK,EAAE,SAAS;QAChB,KAAK,EAAE,mCAAmC;QAC1C,KAAK,EAAE,OAAO;QACd,GAAG,EAAE,IAAI;KACV;IACD,EAAE,EAAE,EAAE,OAAO,EAAE,KAAK,EAAE,SAAS,EAAE,KAAK,EAAE,oBAAoB,EAAE,KAAK,EAAE,OAAO,EAAE,GAAG,EAAE,KAAK,EAAE;CAC3F,CAAA;AAED;;;;GAIG;AACH,MAAM,WAAW,GAAoB;IACnC,kGAAkG;IAClG,gGAAgG;IAChG,uCAAuC;IACvC,EAAE,EAAE,EAAE,OAAO,EAAE,KAAK,EAAE,YAAY,EAAE,KAAK,EAAE,eAAe,EAAE,KAAK,EAAE,OAAO,EAAE,GAAG,EAAE,KAAK,EAAE;IACxF;QACE,EAAE,EAAE,OAAO;QACX,KAAK,EAAE,YAAY;QACnB,KAAK,EAAE,4BAA4B;QACnC,KAAK,EAAE,OAAO;QACd,GAAG,EAAE,cAAc;KACpB;IACD;QACE,EAAE,EAAE,sBAAsB;QAC1B,KAAK,EAAE,YAAY;QACnB,KAAK,EAAE,sBAAsB;QAC7B,KAAK,EAAE,OAAO;QACd,GAAG,EAAE,kBAAkB;KACxB;IACD;QACE,EAAE,EAAE,gBAAgB;QACpB,KAAK,EAAE,YAAY;QACnB,KAAK,EAAE,0CAA0C;QACjD,KAAK,EAAE,OAAO;QACd,8FAA8F;QAC9F,GAAG,EAAE,CAAC,UAAsB,EAAc,EAAE,CAAC,aAAa,CAAC,UAAU,CAAC;KACvE;IACD;QACE,EAAE,EAAE,gBAAgB;QACpB,KAAK,EAAE,YAAY;QACnB,KAAK,EAAE,gBAAgB;QACvB,KAAK,EAAE,OAAO;QACd,GAAG,EAAE,aAAa;KACnB;IACD,EAAE,EAAE,EAAE,OAAO,EAAE,KAAK,EAAE,SAAS,EAAE,KAAK,EAAE,kBAAkB,EAAE,KAAK,EAAE,OAAO,EAAE,GAAG,EAAE,KAAK,EAAE;IACxF,EAAE,EAAE,EAAE,QAAQ,EAAE,KAAK,EAAE,SAAS,EAAE,KAAK,EAAE,sBAAsB,EAAE,KAAK,EAAE,OAAO,EAAE,GAAG,EAAE,MAAM,EAAE;IAC9F,EAAE,EAAE,EAAE,KAAK,EAAE,KAAK,EAAE,SAAS,EAAE,KAAK,EAAE,kBAAkB,EAAE,KAAK,EAAE,OAAO,EAAE,GAAG,EAAE,QAAQ,EAAE;IACzF,EAAE,EAAE,EAAE,MAAM,EAAE,KAAK,EAAE,YAAY,EAAE,KAAK,EAAE,sBAAsB,EAAE,KAAK,EAAE,OAAO,EAAE,GAAG,EAAE,aAAa,EAAE;IACtG;QACE,EAAE,EAAE,mBAAmB;QACvB,KAAK,EAAE,YAAY;QACnB,KAAK,EAAE,mBAAmB;QAC1B,KAAK,EAAE,OAAO;QACd,GAAG,EAAE,gBAAgB;KACtB;CACF,CAAA;AAED,wGAAwG;AACxG,qGAAqG;AACrG,6EAA6E;AAC7E,MAAM,WAAW,GAAS;IACxB,EAAE,EAAE,aAAa;IACjB,KAAK,EAAE,YAAY;IACnB,KAAK,EAAE,wBAAwB;IAC/B,KAAK,EAAE,OAAO;IACd,GAAG,EAAE,UAAU;CAChB,CAAA;AAED,+CAA+C;AAC/C,MAAM,CAAC,MAAM,SAAS,GAAoB,CAAC,GAAG,WAAW,EAAE,GAAG,WAAW,EAAE,WAAW,CAAC,CAAA;AAEvF;;;GAGG;AACH,MAAM,CAAC,MAAM,QAAQ,GAAG,CAAC,KAAwB,EAAmB,EAAE,CACpE,SAAS,CAAC,MAAM,CAAC,CAAC,IAAU,EAAW,EAAE,CAAC,WAAW,CAAC,KAAK,EAAE,IAAI,CAAC,KAAK,CAAC,CAAC,CAAA;AAE3E,sCAAsC;AACtC,MAAM,CAAC,MAAM,QAAQ,GAAG,CAAC,EAAU,EAAoB,EAAE,CAAC,SAAS,CAAC,IAAI,CAAC,CAAC,IAAU,EAAW,EAAE,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,CAAC,CAAA;AAQjH,MAAM,QAAQ,GAAG,CAAC,IAAU,EAAkB,EAAE,CAAC,CAAC;IAChD,EAAE,EAAE,IAAI,CAAC,EAAE;IACX,KAAK,EAAE,IAAI,CAAC,KAAK;IACjB,KAAK,EAAE,IAAI,CAAC,KAAK;CAClB,CAAC,CAAA;AAEF;;;;;;;;;GASG;AACH,MAAM,CAAC,MAAM,OAAO,GAAG,CAAC,UAAsB,EAAE,KAAwB,EAA0B,EAAE;IAClG,MAAM,OAAO,GAAgC,UAAU,CAAC,OAAO,CAAC,GAAG,CACjE,CAAC,MAAc,EAAoB,EAAE,CAAC,CAAC,EAAE,IAAI,EAAE,MAAM,CAAC,IAAI,EAAE,SAAS,EAAE,MAAM,CAAC,SAAS,EAAE,CAAC,CAC3F,CAAA;IACD,OAAO,SAAS,CACd,SAAS,CAAC,GAAG,CAAC,CAAC,IAAU,EAAkB,EAAE,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC,EAC7D,OAAO,EACP,KAAK,CACN,CAAC,OAAO,CAAC,CAAC,IAAc,EAA0B,EAAE;QACnD,MAAM,IAAI,GAAqB,QAAQ,CAAC,IAAI,CAAC,MAAM,CAAC,CAAA;QACpD,IAAI,IAAI,KAAK,SAAS,EAAE,CAAC;YACvB,OAAO,EAAE,CAAA;QACX,CAAC;QACD,OAAO;YACL;gBACE,IAAI;gBACJ,MAAM,EAAE,UAAU,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,SAAiB,EAAW,EAAE,CAAC,SAAS,CAAC,IAAI,KAAK,IAAI,CAAC,MAAM,CAAC;aAChG;SACF,CAAA;IACH,CAAC,CAAC,CAAA;AACJ,CAAC,CAAA"}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import type { Context } from './context.js';
|
|
2
|
+
import { type GateResult } from './exec.js';
|
|
3
|
+
/** The browsers a run connects to, and how to remove them afterwards. */
|
|
4
|
+
export interface PinnedBrowsers {
|
|
5
|
+
readonly endpoint: string;
|
|
6
|
+
readonly stop: () => void;
|
|
7
|
+
/** Whether pages reach the application only through the client's tunnel. */
|
|
8
|
+
readonly isTunnelled: boolean;
|
|
9
|
+
}
|
|
10
|
+
/**
|
|
11
|
+
* Start the pinned browser server for one end-to-end run.
|
|
12
|
+
* @param context the member under test.
|
|
13
|
+
* @returns the endpoint and its stop, or the failure that prevented starting it.
|
|
14
|
+
*/
|
|
15
|
+
export declare const startPinnedBrowsers: (context: Context) => Promise<PinnedBrowsers | GateResult>;
|
|
16
|
+
/** Whether a start answered a failure rather than browsers. */
|
|
17
|
+
export declare const isStartFailure: (started: PinnedBrowsers | GateResult) => started is GateResult;
|
|
18
|
+
/**
|
|
19
|
+
* The environment that starts an application process at the verification clock.
|
|
20
|
+
* @param context the member.
|
|
21
|
+
* @param nodeOptions the `NODE_OPTIONS` the process needs anyway.
|
|
22
|
+
* @returns `NODE_OPTIONS` with the clock preload, and the instant; only `NODE_OPTIONS` for the real clock.
|
|
23
|
+
*/
|
|
24
|
+
export declare const clockedEnvironment: (context: Context, nodeOptions: string) => Readonly<Record<string, string>>;
|
|
25
|
+
//# sourceMappingURL=pinned-browsers.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"pinned-browsers.d.ts","sourceRoot":"","sources":["../src/pinned-browsers.ts"],"names":[],"mappings":"AAkBA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,cAAc,CAAA;AAC3C,OAAO,EAAU,KAAK,UAAU,EAAuB,MAAM,WAAW,CAAA;AAcxE,yEAAyE;AACzE,MAAM,WAAW,cAAc;IAC7B,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAA;IACzB,QAAQ,CAAC,IAAI,EAAE,MAAM,IAAI,CAAA;IACzB,4EAA4E;IAC5E,QAAQ,CAAC,WAAW,EAAE,OAAO,CAAA;CAC9B;AA+DD;;;;GAIG;AACH,eAAO,MAAM,mBAAmB,GAAU,SAAS,OAAO,KAAG,OAAO,CAAC,cAAc,GAAG,UAAU,CAiC/F,CAAA;AAmCD,+DAA+D;AAC/D,eAAO,MAAM,cAAc,GAAI,SAAS,cAAc,GAAG,UAAU,KAAG,OAAO,IAAI,UAAsC,CAAA;AAIvH;;;;;GAKG;AACH,eAAO,MAAM,kBAAkB,GAAI,SAAS,OAAO,EAAE,aAAa,MAAM,KAAG,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAMnG,CAAA"}
|
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
// The pinned browsers a verification run measures in, and the application clock it runs at.
|
|
2
|
+
//
|
|
3
|
+
// The browsers run in the Playwright image of the version the project resolves, started here before the
|
|
4
|
+
// suite and removed after it. The image is never pulled during verification: a run that fetched it
|
|
5
|
+
// would depend on the network and on what the registry serves that day, so a missing image is a failure
|
|
6
|
+
// that names the command to fetch it beforehand.
|
|
7
|
+
import { randomBytes } from 'node:crypto';
|
|
8
|
+
import { realpathSync } from 'node:fs';
|
|
9
|
+
import { createRequire } from 'node:module';
|
|
10
|
+
import { createConnection, createServer } from 'node:net';
|
|
11
|
+
import path from 'node:path';
|
|
12
|
+
import { CLOCK_ENVIRONMENT_VARIABLE, pinnedBrowserEndpoint, pinnedBrowserServerArguments, playwrightImageOf, REAL_CLOCK, } from '@ploaness/governance';
|
|
13
|
+
import { failed, run } from './exec.js';
|
|
14
|
+
const START_TIMEOUT_MS = 60_000;
|
|
15
|
+
const SECRET_BYTES = 16;
|
|
16
|
+
// Long enough for the server to start on a machine whose host network works, short enough that a machine
|
|
17
|
+
// whose does not falls back to the tunnel without delaying the run much.
|
|
18
|
+
const HOST_NETWORK_PROBE_MS = 20_000;
|
|
19
|
+
const POLL_MS = 250;
|
|
20
|
+
const MILLISECONDS_PER_SECOND = 1000;
|
|
21
|
+
const freePort = async () => await new Promise((resolve, reject) => {
|
|
22
|
+
const server = createServer();
|
|
23
|
+
server.once('error', reject);
|
|
24
|
+
server.listen(0, '127.0.0.1', () => {
|
|
25
|
+
const address = server.address();
|
|
26
|
+
const port = typeof address === 'object' && address !== null ? address.port : 0;
|
|
27
|
+
server.close(() => {
|
|
28
|
+
resolve(port);
|
|
29
|
+
});
|
|
30
|
+
});
|
|
31
|
+
});
|
|
32
|
+
const isListening = async (port) => await new Promise((resolve) => {
|
|
33
|
+
const socket = createConnection({ host: '127.0.0.1', port });
|
|
34
|
+
socket.once('connect', () => {
|
|
35
|
+
socket.destroy();
|
|
36
|
+
resolve(true);
|
|
37
|
+
});
|
|
38
|
+
socket.once('error', () => {
|
|
39
|
+
resolve(false);
|
|
40
|
+
});
|
|
41
|
+
});
|
|
42
|
+
const isListeningBy = async (port, deadline) => {
|
|
43
|
+
if (await isListening(port)) {
|
|
44
|
+
return true;
|
|
45
|
+
}
|
|
46
|
+
if (performance.now() > deadline) {
|
|
47
|
+
return false;
|
|
48
|
+
}
|
|
49
|
+
await new Promise((resolve) => {
|
|
50
|
+
setTimeout(resolve, POLL_MS);
|
|
51
|
+
});
|
|
52
|
+
return await isListeningBy(port, deadline);
|
|
53
|
+
};
|
|
54
|
+
// The `playwright-core` the project's own `@playwright/test` loads, which is the version the image must
|
|
55
|
+
// carry browsers for.
|
|
56
|
+
const resolvePlaywrightCore = (context) => {
|
|
57
|
+
try {
|
|
58
|
+
const fromProject = createRequire(path.join(context.root, 'package.json'));
|
|
59
|
+
const testManifest = fromProject.resolve('@playwright/test/package.json');
|
|
60
|
+
const fromTest = createRequire(testManifest);
|
|
61
|
+
const playwrightManifest = fromTest.resolve('playwright/package.json');
|
|
62
|
+
const coreManifest = createRequire(playwrightManifest).resolve('playwright-core/package.json');
|
|
63
|
+
const manifest = createRequire(coreManifest)(coreManifest);
|
|
64
|
+
const version = typeof manifest === 'object' && manifest !== null ? Reflect.get(manifest, 'version') : undefined;
|
|
65
|
+
return typeof version === 'string' ? { directory: realpathSync(path.dirname(coreManifest)), version } : undefined;
|
|
66
|
+
}
|
|
67
|
+
catch {
|
|
68
|
+
return undefined;
|
|
69
|
+
}
|
|
70
|
+
};
|
|
71
|
+
/**
|
|
72
|
+
* Start the pinned browser server for one end-to-end run.
|
|
73
|
+
* @param context the member under test.
|
|
74
|
+
* @returns the endpoint and its stop, or the failure that prevented starting it.
|
|
75
|
+
*/
|
|
76
|
+
export const startPinnedBrowsers = async (context) => {
|
|
77
|
+
const playwright = resolvePlaywrightCore(context);
|
|
78
|
+
if (playwright === undefined) {
|
|
79
|
+
return failed("playwright-core could not be resolved through the project's @playwright/test", [
|
|
80
|
+
"install the project dependencies; the pinned browsers run the project's own Playwright",
|
|
81
|
+
]);
|
|
82
|
+
}
|
|
83
|
+
const image = playwrightImageOf(playwright.version);
|
|
84
|
+
const inspected = run('docker', ['image', 'inspect', '--format={{.Id}}', image], { cwd: context.root });
|
|
85
|
+
if (inspected.code !== 0) {
|
|
86
|
+
return failed(`the pinned browser image ${image} is not available locally`, [
|
|
87
|
+
`run \`docker pull ${image}\` before verification; the run never fetches it`,
|
|
88
|
+
...(inspected.output.length > 0 ? [inspected.output] : []),
|
|
89
|
+
]);
|
|
90
|
+
}
|
|
91
|
+
// A Linux Docker engine shares the host's network, so the container reaches the application without a
|
|
92
|
+
// tunnel. Docker Desktop's VM does not share the host's loopback, which shows as a server the host
|
|
93
|
+
// cannot reach on its own loopback, and the run then publishes a port and tunnels instead.
|
|
94
|
+
const shared = process.platform === 'linux'
|
|
95
|
+
? await startServer(context, { image, playwrightCore: playwright.directory, isHostNetwork: true }, HOST_NETWORK_PROBE_MS)
|
|
96
|
+
: undefined;
|
|
97
|
+
return shared === undefined || isStartFailure(shared)
|
|
98
|
+
? await startServer(context, { image, playwrightCore: playwright.directory, isHostNetwork: false }, START_TIMEOUT_MS)
|
|
99
|
+
: shared;
|
|
100
|
+
};
|
|
101
|
+
const startServer = async (context, plan, timeoutMs) => {
|
|
102
|
+
const port = await freePort();
|
|
103
|
+
const name = `ploaness-browsers-${String(port)}`;
|
|
104
|
+
const secret = randomBytes(SECRET_BYTES).toString('hex');
|
|
105
|
+
const started = run('docker', pinnedBrowserServerArguments({ ...plan, name, port, secret }), {
|
|
106
|
+
cwd: context.root,
|
|
107
|
+
});
|
|
108
|
+
if (started.code !== 0) {
|
|
109
|
+
return failed('the pinned browser server did not start', [started.output]);
|
|
110
|
+
}
|
|
111
|
+
const stop = () => {
|
|
112
|
+
run('docker', ['rm', '--force', name], { cwd: context.root });
|
|
113
|
+
};
|
|
114
|
+
if (!(await isListeningBy(port, performance.now() + timeoutMs))) {
|
|
115
|
+
const logs = run('docker', ['logs', name], { cwd: context.root });
|
|
116
|
+
stop();
|
|
117
|
+
return failed(`the pinned browser server did not listen within ${String(timeoutMs / MILLISECONDS_PER_SECOND)}s`, [
|
|
118
|
+
logs.output,
|
|
119
|
+
]);
|
|
120
|
+
}
|
|
121
|
+
return { endpoint: pinnedBrowserEndpoint(port, secret), stop, isTunnelled: !plan.isHostNetwork };
|
|
122
|
+
};
|
|
123
|
+
/** Whether a start answered a failure rather than browsers. */
|
|
124
|
+
export const isStartFailure = (started) => !('endpoint' in started);
|
|
125
|
+
const CLOCK_PRELOAD = import.meta.resolve('@ploaness/config/clock');
|
|
126
|
+
/**
|
|
127
|
+
* The environment that starts an application process at the verification clock.
|
|
128
|
+
* @param context the member.
|
|
129
|
+
* @param nodeOptions the `NODE_OPTIONS` the process needs anyway.
|
|
130
|
+
* @returns `NODE_OPTIONS` with the clock preload, and the instant; only `NODE_OPTIONS` for the real clock.
|
|
131
|
+
*/
|
|
132
|
+
export const clockedEnvironment = (context, nodeOptions) => context.settings.clock === REAL_CLOCK
|
|
133
|
+
? { NODE_OPTIONS: nodeOptions }
|
|
134
|
+
: {
|
|
135
|
+
NODE_OPTIONS: `${nodeOptions} --import=${CLOCK_PRELOAD}`,
|
|
136
|
+
[CLOCK_ENVIRONMENT_VARIABLE]: context.settings.clock,
|
|
137
|
+
};
|
|
138
|
+
//# sourceMappingURL=pinned-browsers.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"pinned-browsers.js","sourceRoot":"","sources":["../src/pinned-browsers.ts"],"names":[],"mappings":"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"}
|