@continuous-excellence/ze-great-dashboard-aws 0.27.9 → 0.27.11
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.js +480 -165
- package/dist/index.js +458 -143
- package/dist/lambda.mjs +226 -65
- package/package.json +1 -1
- package/dist/internal-board.d.ts +0 -61
package/dist/cli.js
CHANGED
|
@@ -2,18 +2,18 @@
|
|
|
2
2
|
|
|
3
3
|
// packages/aws/src/cli.ts
|
|
4
4
|
import { execFile as execFile2 } from "node:child_process";
|
|
5
|
-
import { readFile as
|
|
5
|
+
import { readFile as readFile7, writeFile as writeFile4 } from "node:fs/promises";
|
|
6
6
|
import { promisify as promisify2 } from "node:util";
|
|
7
|
-
import { parse as
|
|
7
|
+
import { parse as parse2 } from "yaml";
|
|
8
8
|
|
|
9
9
|
// packages/aws/src/doctor.ts
|
|
10
10
|
import { execFile } from "node:child_process";
|
|
11
|
-
import { readFile as
|
|
11
|
+
import { readFile as readFile6 } from "node:fs/promises";
|
|
12
12
|
import { promisify } from "node:util";
|
|
13
|
-
import { parse
|
|
13
|
+
import { parse } from "yaml";
|
|
14
14
|
|
|
15
15
|
// packages/aws/src/bootstrap.ts
|
|
16
|
-
import { readFile as
|
|
16
|
+
import { readFile as readFile3 } from "node:fs/promises";
|
|
17
17
|
import { fileURLToPath } from "node:url";
|
|
18
18
|
|
|
19
19
|
// packages/aws/src/compute-mode.ts
|
|
@@ -43,31 +43,21 @@ function computeMode(config) {
|
|
|
43
43
|
|
|
44
44
|
// packages/aws/src/release.ts
|
|
45
45
|
import { createHash } from "node:crypto";
|
|
46
|
-
import { mkdir,
|
|
46
|
+
import { mkdir, writeFile } from "node:fs/promises";
|
|
47
47
|
import { join, resolve } from "node:path";
|
|
48
48
|
|
|
49
|
+
// packages/server/src/allowlist.ts
|
|
50
|
+
import { ZodError } from "zod";
|
|
51
|
+
|
|
52
|
+
// packages/server/src/adapters/azure-devops.ts
|
|
53
|
+
import { readFile } from "node:fs/promises";
|
|
54
|
+
|
|
49
55
|
// packages/shared/src/board-config.ts
|
|
50
56
|
import { z as z2 } from "zod";
|
|
51
57
|
|
|
52
58
|
// packages/shared/src/duration.ts
|
|
53
59
|
import { z } from "zod";
|
|
54
|
-
var
|
|
55
|
-
var UNIT_MILLIS = {
|
|
56
|
-
ms: 1,
|
|
57
|
-
s: 1e3,
|
|
58
|
-
m: 6e4,
|
|
59
|
-
h: 36e5
|
|
60
|
-
};
|
|
61
|
-
function parseDuration(value2) {
|
|
62
|
-
const match = DURATION_PATTERN.exec(value2);
|
|
63
|
-
if (!match) return null;
|
|
64
|
-
const [, rawAmount, rawUnit] = match;
|
|
65
|
-
if (rawAmount === void 0 || rawUnit === void 0) return null;
|
|
66
|
-
const amount = Number(rawAmount);
|
|
67
|
-
if (amount <= 0) return null;
|
|
68
|
-
return amount * UNIT_MILLIS[rawUnit];
|
|
69
|
-
}
|
|
70
|
-
var durationSchema = z.string().refine((value2) => parseDuration(value2) !== null, {
|
|
60
|
+
var durationSchema = z.string().regex(/^[1-9][0-9]*(?:ms|s|m|h)$/, {
|
|
71
61
|
message: 'must be a positive duration like "30s", "5m", or "1h"'
|
|
72
62
|
}).brand();
|
|
73
63
|
|
|
@@ -154,7 +144,8 @@ var panelSchema = z2.looseObject({
|
|
|
154
144
|
ctx.addIssue({
|
|
155
145
|
code: "custom",
|
|
156
146
|
path: ["display"],
|
|
157
|
-
message: "display was removed; use density"
|
|
147
|
+
message: "display was removed; use density",
|
|
148
|
+
params: { constraint: "display was removed; use density" }
|
|
158
149
|
});
|
|
159
150
|
}
|
|
160
151
|
if (panel.type !== "http-value") return;
|
|
@@ -163,13 +154,15 @@ var panelSchema = z2.looseObject({
|
|
|
163
154
|
ctx.addIssue({
|
|
164
155
|
code: "custom",
|
|
165
156
|
path: ["url"],
|
|
166
|
-
message: "use facts URLs for grouped panels"
|
|
157
|
+
message: "use facts URLs for grouped panels",
|
|
158
|
+
params: { constraint: "use facts URLs for grouped panels" }
|
|
167
159
|
});
|
|
168
160
|
if (panel.json_path !== void 0)
|
|
169
161
|
ctx.addIssue({
|
|
170
162
|
code: "custom",
|
|
171
163
|
path: ["json_path"],
|
|
172
|
-
message: "use facts json_path values for grouped panels"
|
|
164
|
+
message: "use facts json_path values for grouped panels",
|
|
165
|
+
params: { constraint: "use facts json_path values for grouped panels" }
|
|
173
166
|
});
|
|
174
167
|
const seen = /* @__PURE__ */ new Map();
|
|
175
168
|
panel.facts.forEach((fact, index) => {
|
|
@@ -179,7 +172,8 @@ var panelSchema = z2.looseObject({
|
|
|
179
172
|
ctx.addIssue({
|
|
180
173
|
code: "custom",
|
|
181
174
|
path: ["facts", index, "id"],
|
|
182
|
-
message: `duplicate fact id "${fact.id}" (already used by fact at index ${first})
|
|
175
|
+
message: `duplicate fact id "${fact.id}" (already used by fact at index ${first})`,
|
|
176
|
+
params: { constraint: "Use a unique fact id within this panel." }
|
|
183
177
|
});
|
|
184
178
|
});
|
|
185
179
|
}
|
|
@@ -196,7 +190,11 @@ var sourceAuthenticationSchema = {
|
|
|
196
190
|
};
|
|
197
191
|
function exactlyOneGithubAuthenticationMode(source, ctx) {
|
|
198
192
|
if (source.token_env && source.github_app)
|
|
199
|
-
ctx.addIssue({
|
|
193
|
+
ctx.addIssue({
|
|
194
|
+
code: "custom",
|
|
195
|
+
message: "configure token_env or github_app, not both",
|
|
196
|
+
params: { constraint: "configure token_env or github_app, not both" }
|
|
197
|
+
});
|
|
200
198
|
}
|
|
201
199
|
var githubActionsSourceSchema = z2.looseObject({
|
|
202
200
|
type: z2.literal("github-actions"),
|
|
@@ -220,24 +218,44 @@ var azureDevOpsSourceSchema = z2.looseObject({
|
|
|
220
218
|
if (source.token_env && source.entra_token_file_env) {
|
|
221
219
|
ctx.addIssue({
|
|
222
220
|
code: "custom",
|
|
223
|
-
message: "configure token_env or entra_token_file_env, not both"
|
|
221
|
+
message: "configure token_env or entra_token_file_env, not both",
|
|
222
|
+
params: { constraint: "configure token_env or entra_token_file_env, not both" }
|
|
224
223
|
});
|
|
225
224
|
}
|
|
226
225
|
if (!source.token_env && !source.entra_token_file_env) {
|
|
227
226
|
ctx.addIssue({
|
|
228
227
|
code: "custom",
|
|
229
|
-
message: "configure token_env or entra_token_file_env"
|
|
228
|
+
message: "configure token_env or entra_token_file_env",
|
|
229
|
+
params: { constraint: "configure token_env or entra_token_file_env" }
|
|
230
230
|
});
|
|
231
231
|
}
|
|
232
232
|
});
|
|
233
233
|
var gitlabInstanceUrlSchema = z2.url().superRefine((value2, ctx) => {
|
|
234
234
|
const url = new URL(value2);
|
|
235
235
|
if (url.protocol !== "https:")
|
|
236
|
-
ctx.addIssue({
|
|
236
|
+
ctx.addIssue({
|
|
237
|
+
code: "custom",
|
|
238
|
+
message: "must be an HTTPS GitLab instance URL",
|
|
239
|
+
params: { constraint: "must be an HTTPS GitLab instance URL" }
|
|
240
|
+
});
|
|
237
241
|
if (url.username || url.password)
|
|
238
|
-
ctx.addIssue({
|
|
239
|
-
|
|
240
|
-
|
|
242
|
+
ctx.addIssue({
|
|
243
|
+
code: "custom",
|
|
244
|
+
message: "must not include credentials",
|
|
245
|
+
params: { constraint: "must not include credentials" }
|
|
246
|
+
});
|
|
247
|
+
if (url.search)
|
|
248
|
+
ctx.addIssue({
|
|
249
|
+
code: "custom",
|
|
250
|
+
message: "must not include a query string",
|
|
251
|
+
params: { constraint: "must not include a query string" }
|
|
252
|
+
});
|
|
253
|
+
if (url.hash)
|
|
254
|
+
ctx.addIssue({
|
|
255
|
+
code: "custom",
|
|
256
|
+
message: "must not include a fragment",
|
|
257
|
+
params: { constraint: "must not include a fragment" }
|
|
258
|
+
});
|
|
241
259
|
});
|
|
242
260
|
var gitlabCiSourceSchema = z2.looseObject({
|
|
243
261
|
type: z2.literal("gitlab-ci"),
|
|
@@ -251,6 +269,17 @@ var gitlabCiSourceSchema = z2.looseObject({
|
|
|
251
269
|
url: gitlabInstanceUrlSchema.optional().default("https://gitlab.com")
|
|
252
270
|
});
|
|
253
271
|
var sourceSchema = z2.looseObject({ type: z2.string().min(1), ...sourceAuthenticationSchema }).superRefine(exactlyOneGithubAuthenticationMode);
|
|
272
|
+
function credentialEnvironmentNames(source) {
|
|
273
|
+
const githubApp = githubAppSchema.safeParse(source.github_app);
|
|
274
|
+
return [
|
|
275
|
+
...source.token_env ? [source.token_env] : [],
|
|
276
|
+
...githubApp.success ? [
|
|
277
|
+
githubApp.data.app_id_env,
|
|
278
|
+
githubApp.data.private_key_env,
|
|
279
|
+
githubApp.data.installation_id_env
|
|
280
|
+
] : []
|
|
281
|
+
];
|
|
282
|
+
}
|
|
254
283
|
var boardSchema = z2.object({
|
|
255
284
|
refresh: durationSchema.optional(),
|
|
256
285
|
running_refresh: durationSchema.optional(),
|
|
@@ -267,7 +296,8 @@ var boardSchema = z2.object({
|
|
|
267
296
|
ctx.addIssue({
|
|
268
297
|
code: "custom",
|
|
269
298
|
path: [index, "id"],
|
|
270
|
-
message: `duplicate panel id "${panel.id}" (already used by panel at index ${firstIndex})
|
|
299
|
+
message: `duplicate panel id "${panel.id}" (already used by panel at index ${firstIndex})`,
|
|
300
|
+
params: { constraint: "Use a unique panel id within this board." }
|
|
271
301
|
});
|
|
272
302
|
});
|
|
273
303
|
})
|
|
@@ -287,15 +317,14 @@ var boardConfigSchema = z2.object({
|
|
|
287
317
|
auth: authSchema.optional()
|
|
288
318
|
}).superRefine((config, ctx) => {
|
|
289
319
|
for (const [sourceName, source] of Object.entries(config.sources)) {
|
|
290
|
-
const
|
|
291
|
-
if (!
|
|
292
|
-
const parsedSource =
|
|
320
|
+
const sourceSchema2 = source.type === "azure-devops" ? azureDevOpsSourceSchema : source.type === "gitlab-ci" ? gitlabCiSourceSchema : void 0;
|
|
321
|
+
if (!sourceSchema2) continue;
|
|
322
|
+
const parsedSource = sourceSchema2.safeParse(source);
|
|
293
323
|
if (!parsedSource.success) {
|
|
294
324
|
for (const issue of parsedSource.error.issues) {
|
|
295
325
|
ctx.addIssue({
|
|
296
|
-
|
|
297
|
-
path: ["sources", sourceName, ...issue.path]
|
|
298
|
-
message: issue.message
|
|
326
|
+
...issue,
|
|
327
|
+
path: ["sources", sourceName, ...issue.path]
|
|
299
328
|
});
|
|
300
329
|
}
|
|
301
330
|
}
|
|
@@ -308,7 +337,10 @@ var boardConfigSchema = z2.object({
|
|
|
308
337
|
ctx.addIssue({
|
|
309
338
|
code: "custom",
|
|
310
339
|
path: ["boards", boardName, "panels", panelIndex, "pipeline"],
|
|
311
|
-
message: "Azure DevOps pipeline-status panels require a positive numeric pipeline definition id"
|
|
340
|
+
message: "Azure DevOps pipeline-status panels require a positive numeric pipeline definition id",
|
|
341
|
+
params: {
|
|
342
|
+
constraint: "Provide a positive numeric Azure DevOps pipeline definition id."
|
|
343
|
+
}
|
|
312
344
|
});
|
|
313
345
|
}
|
|
314
346
|
});
|
|
@@ -471,95 +503,376 @@ var httpValueSchema = z4.object({
|
|
|
471
503
|
value: z4.union([z4.string(), z4.number(), z4.boolean()])
|
|
472
504
|
});
|
|
473
505
|
|
|
474
|
-
// packages/
|
|
475
|
-
import { parse, stringify } from "yaml";
|
|
476
|
-
import { z as z6 } from "zod";
|
|
477
|
-
|
|
478
|
-
// packages/aws/src/internal-board.ts
|
|
506
|
+
// packages/server/src/adapters/azure-devops.ts
|
|
479
507
|
import { z as z5 } from "zod";
|
|
480
|
-
var
|
|
481
|
-
var positionSchema2 = z5.union([
|
|
482
|
-
z5.object({
|
|
483
|
-
x: z5.number().int().min(0),
|
|
484
|
-
y: z5.number().int().min(0),
|
|
485
|
-
w: z5.number().int().min(1).max(12),
|
|
486
|
-
h: z5.number().int().min(1)
|
|
487
|
-
}),
|
|
488
|
-
z5.object({ x: z5.literal(0), y: z5.literal(0), w: z5.literal(0), h: z5.literal(0) })
|
|
489
|
-
]);
|
|
490
|
-
var panelSchema2 = z5.looseObject({
|
|
491
|
-
// Presentation-only; ids still address proxy calls and the generated allowlist.
|
|
492
|
-
label: z5.string().min(1).optional(),
|
|
508
|
+
var pipelinePanelSchema = z5.object({
|
|
493
509
|
id: z5.string().min(1),
|
|
494
|
-
type: z5.
|
|
495
|
-
source: z5.string().min(1)
|
|
496
|
-
|
|
497
|
-
position: positionSchema2.optional(),
|
|
498
|
-
refresh: durationSchema2.optional(),
|
|
499
|
-
link: z5.url().optional(),
|
|
500
|
-
url: z5.url().optional(),
|
|
501
|
-
json_path: z5.string().regex(/^\$(?:\.[A-Za-z_][A-Za-z0-9_]*|\[\d+\])*$/).optional()
|
|
502
|
-
}).superRefine((panel, ctx) => {
|
|
503
|
-
if ("display" in panel) {
|
|
504
|
-
ctx.addIssue({
|
|
505
|
-
code: "custom",
|
|
506
|
-
path: ["display"],
|
|
507
|
-
message: "display was removed; use density"
|
|
508
|
-
});
|
|
509
|
-
}
|
|
510
|
+
type: z5.literal("pipeline-status"),
|
|
511
|
+
source: z5.string().min(1),
|
|
512
|
+
pipeline: z5.number().int().positive()
|
|
510
513
|
});
|
|
511
|
-
var
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
if (source.token_env && source.github_app)
|
|
521
|
-
ctx.addIssue({ code: "custom", message: "configure token_env or github_app, not both" });
|
|
514
|
+
var buildSchema = z5.object({
|
|
515
|
+
id: z5.number().int().positive(),
|
|
516
|
+
buildNumber: z5.string().min(1),
|
|
517
|
+
status: z5.string().nullable().optional(),
|
|
518
|
+
result: z5.string().nullable().optional(),
|
|
519
|
+
sourceBranch: z5.string().min(1).nullable().optional(),
|
|
520
|
+
startTime: z5.string().nullable().optional(),
|
|
521
|
+
finishTime: z5.string().nullable().optional(),
|
|
522
|
+
lastChangedDate: z5.string().nullable().optional()
|
|
522
523
|
});
|
|
523
|
-
var
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
524
|
+
var buildsSchema = z5.object({ value: z5.array(buildSchema) });
|
|
525
|
+
var timelineSchema = z5.object({
|
|
526
|
+
records: z5.array(
|
|
527
|
+
z5.object({
|
|
528
|
+
id: z5.string().min(1),
|
|
529
|
+
parentId: z5.string().min(1).nullable().optional(),
|
|
530
|
+
type: z5.string().min(1),
|
|
531
|
+
name: z5.string().min(1),
|
|
532
|
+
state: z5.string().nullable().optional()
|
|
533
|
+
})
|
|
534
|
+
)
|
|
535
|
+
});
|
|
536
|
+
function permittedAzureDevOpsCalls(panel, source) {
|
|
537
|
+
const { panel: parsedPanel, source: parsedSource } = z5.object({ panel: pipelinePanelSchema, source: azureDevOpsSourceSchema }).parse({ panel, source });
|
|
538
|
+
return [buildsCall(parsedPanel.pipeline, parsedSource)];
|
|
539
|
+
}
|
|
540
|
+
var delegatedTokenFileSchema = z5.object({
|
|
541
|
+
accessToken: z5.string().min(1),
|
|
542
|
+
expiresAt: z5.string().min(1)
|
|
543
|
+
});
|
|
544
|
+
function buildsCall(definitionId, source) {
|
|
545
|
+
const url = apiUrl(source, "build/builds");
|
|
546
|
+
url.searchParams.set("definitions", String(definitionId));
|
|
547
|
+
if (source.branch) url.searchParams.set("branchName", adoBranch(source.branch));
|
|
548
|
+
url.searchParams.set("queryOrder", "queueTimeDescending");
|
|
549
|
+
url.searchParams.set("$top", "1");
|
|
550
|
+
return { url: url.toString(), headers: new Headers({ accept: "application/json" }) };
|
|
551
|
+
}
|
|
552
|
+
function apiUrl(source, suffix) {
|
|
553
|
+
const url = new URL(
|
|
554
|
+
`https://dev.azure.com/${encodeURIComponent(source.organization)}/${encodeURIComponent(source.project)}/_apis/${suffix}`
|
|
555
|
+
);
|
|
556
|
+
url.searchParams.set("api-version", "7.1");
|
|
557
|
+
return url;
|
|
558
|
+
}
|
|
559
|
+
function adoBranch(branch) {
|
|
560
|
+
return branch.startsWith("refs/") ? branch : `refs/heads/${branch}`;
|
|
561
|
+
}
|
|
562
|
+
|
|
563
|
+
// packages/server/src/adapters/github-actions.ts
|
|
564
|
+
import { z as z6 } from "zod";
|
|
565
|
+
var pipelinePanelSchema2 = z6.object({
|
|
566
|
+
id: z6.string().min(1),
|
|
567
|
+
type: z6.literal("pipeline-status"),
|
|
568
|
+
source: z6.string().min(1),
|
|
569
|
+
/** GitHub accepts a workflow filename or numeric workflow id in this URL segment. */
|
|
570
|
+
pipeline: z6.string().min(1)
|
|
571
|
+
});
|
|
572
|
+
var pullRequestHealthPanelSchema = z6.object({
|
|
573
|
+
id: z6.string().min(1),
|
|
574
|
+
type: z6.literal("pull-request-health"),
|
|
575
|
+
source: z6.string().min(1),
|
|
576
|
+
base_branch: z6.string().min(1),
|
|
577
|
+
update_workflows: z6.array(
|
|
578
|
+
z6.object({
|
|
579
|
+
workflow: z6.string().min(1),
|
|
580
|
+
branch_prefixes: z6.array(z6.string().min(1)).min(1)
|
|
581
|
+
})
|
|
582
|
+
).min(1),
|
|
583
|
+
build_workflow: z6.string().min(1)
|
|
584
|
+
});
|
|
585
|
+
var runSchema = z6.object({
|
|
586
|
+
id: z6.number().int().positive().optional(),
|
|
587
|
+
status: z6.string(),
|
|
588
|
+
conclusion: z6.string().nullable(),
|
|
589
|
+
name: z6.string().min(1),
|
|
590
|
+
html_url: z6.url(),
|
|
591
|
+
/** The branch that produced this run; absent for some event types. */
|
|
592
|
+
head_branch: z6.string().min(1).nullable().optional(),
|
|
593
|
+
// Timestamp quality should only remove timing advice, never make an otherwise useful run unreadable.
|
|
594
|
+
run_started_at: z6.string().nullable().optional(),
|
|
595
|
+
updated_at: z6.string().optional()
|
|
596
|
+
});
|
|
597
|
+
var runsSchema = z6.object({ workflow_runs: z6.array(runSchema) });
|
|
598
|
+
var jobsSchema = z6.object({
|
|
599
|
+
jobs: z6.array(
|
|
600
|
+
z6.object({
|
|
601
|
+
name: z6.string().min(1),
|
|
602
|
+
status: z6.string(),
|
|
603
|
+
steps: z6.array(z6.object({ name: z6.string().min(1), status: z6.string() })).optional()
|
|
604
|
+
})
|
|
605
|
+
)
|
|
606
|
+
});
|
|
607
|
+
var pullRequestsSchema = z6.array(
|
|
608
|
+
z6.object({
|
|
609
|
+
number: z6.number().int().positive(),
|
|
610
|
+
html_url: z6.url(),
|
|
611
|
+
head: z6.object({ ref: z6.string().min(1) }),
|
|
612
|
+
base: z6.object({ ref: z6.string().min(1) })
|
|
537
613
|
})
|
|
614
|
+
);
|
|
615
|
+
function permittedGithubActionsCalls(panel, source, _credentials) {
|
|
616
|
+
const { panel: parsedPanel, source: parsedSource } = z6.object({ panel: pipelinePanelSchema2, source: githubActionsSourceSchema }).parse({ panel, source });
|
|
617
|
+
const url = new URL(
|
|
618
|
+
`https://api.github.com/repos/${parsedSource.repo}/actions/workflows/${encodeURIComponent(parsedPanel.pipeline)}/runs`
|
|
619
|
+
);
|
|
620
|
+
if (parsedSource.branch) url.searchParams.set("branch", parsedSource.branch);
|
|
621
|
+
url.searchParams.set("per_page", "1");
|
|
622
|
+
return [{ url: url.toString(), headers: new Headers() }];
|
|
623
|
+
}
|
|
624
|
+
function pullRequestHealthCapabilities(panel) {
|
|
625
|
+
const { panel: parsed } = z6.object({ panel: pullRequestHealthPanelSchema }).parse({ panel });
|
|
626
|
+
return {
|
|
627
|
+
panel: parsed,
|
|
628
|
+
configuredUpdateWorkflow(workflow) {
|
|
629
|
+
return parsed.update_workflows.find((candidate) => candidate.workflow === workflow);
|
|
630
|
+
},
|
|
631
|
+
permitsBuildBranch(branch) {
|
|
632
|
+
return parsed.update_workflows.some(
|
|
633
|
+
({ branch_prefixes }) => branch_prefixes.some((prefix) => branch.startsWith(prefix))
|
|
634
|
+
);
|
|
635
|
+
}
|
|
636
|
+
};
|
|
637
|
+
}
|
|
638
|
+
|
|
639
|
+
// packages/server/src/adapters/gitlab-ci.ts
|
|
640
|
+
import { z as z7 } from "zod";
|
|
641
|
+
var pipelinePanelSchema3 = z7.object({
|
|
642
|
+
id: z7.string().min(1),
|
|
643
|
+
type: z7.literal("pipeline-status"),
|
|
644
|
+
source: z7.string().min(1)
|
|
538
645
|
});
|
|
539
|
-
var
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
subjects: z5.array(z5.string().min(1)).optional()
|
|
548
|
-
}).optional()
|
|
549
|
-
}).optional()
|
|
646
|
+
var pipelineSchema = z7.object({
|
|
647
|
+
id: z7.number().int().positive(),
|
|
648
|
+
name: z7.string().min(1).nullable().optional(),
|
|
649
|
+
status: z7.string(),
|
|
650
|
+
ref: z7.string().min(1).nullable().optional(),
|
|
651
|
+
created_at: z7.string().nullable().optional(),
|
|
652
|
+
updated_at: z7.string().nullable().optional(),
|
|
653
|
+
web_url: z7.url()
|
|
550
654
|
});
|
|
551
|
-
function
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
655
|
+
function permittedGitlabCiCalls(panel, source) {
|
|
656
|
+
const { source: parsedSource } = z7.object({ panel: pipelinePanelSchema3, source: gitlabCiSourceSchema }).parse({ panel, source });
|
|
657
|
+
return [pipelinesCall(parsedSource)];
|
|
658
|
+
}
|
|
659
|
+
function pipelinesCall(source) {
|
|
660
|
+
const url = new URL(source.url);
|
|
661
|
+
url.pathname = `${url.pathname.replace(/\/$/, "")}/api/v4/projects/${encodeURIComponent(source.project)}/pipelines`;
|
|
662
|
+
url.searchParams.set("per_page", "1");
|
|
663
|
+
if (source.branch) url.searchParams.set("ref", source.branch);
|
|
664
|
+
return { url: url.toString(), headers: new Headers({ accept: "application/json" }) };
|
|
665
|
+
}
|
|
666
|
+
|
|
667
|
+
// packages/server/src/adapters/http-value.ts
|
|
668
|
+
import { z as z8 } from "zod";
|
|
669
|
+
function permittedHttpValueCalls(panel) {
|
|
670
|
+
const grouped = httpValueGroupedPanelSchema.safeParse(panel);
|
|
671
|
+
const urls = grouped.success ? grouped.data.facts.map((fact) => fact.url) : [z8.object({ panel: httpValueScalarPanelSchema }).parse({ panel }).panel.url];
|
|
672
|
+
return urls.map(permittedCall);
|
|
673
|
+
}
|
|
674
|
+
function permittedCall(value2) {
|
|
675
|
+
const url = new URL(value2);
|
|
676
|
+
if (url.protocol !== "http:" && url.protocol !== "https:")
|
|
677
|
+
throw new Error("http-value URLs must use http or https");
|
|
678
|
+
return { url: url.toString(), headers: new Headers({ accept: "application/json, text/plain" }) };
|
|
679
|
+
}
|
|
680
|
+
|
|
681
|
+
// packages/server/src/configuration-error.ts
|
|
682
|
+
var ConfigurationError = class extends Error {
|
|
683
|
+
constructor(message2, diagnostics) {
|
|
684
|
+
super(message2);
|
|
685
|
+
this.diagnostics = diagnostics;
|
|
686
|
+
}
|
|
687
|
+
diagnostics;
|
|
688
|
+
};
|
|
689
|
+
function configurationLocation(path) {
|
|
690
|
+
return path.reduce(
|
|
691
|
+
(location, part) => typeof part === "number" ? `${location}[${part}]` : `${location}${location ? "." : ""}${String(part)}`,
|
|
692
|
+
""
|
|
693
|
+
) || "configuration";
|
|
694
|
+
}
|
|
695
|
+
function boardConfigurationLocation(config, path) {
|
|
696
|
+
const [record, key, ...rest] = path;
|
|
697
|
+
if ((record === "boards" || record === "sources") && key !== void 0) {
|
|
698
|
+
const entries = config && typeof config === "object" ? Reflect.get(config, record) : void 0;
|
|
699
|
+
const index = entries && typeof entries === "object" ? Object.keys(entries).indexOf(String(key)) : -1;
|
|
700
|
+
return configurationLocation([record, index, ...rest]);
|
|
701
|
+
}
|
|
702
|
+
return configurationLocation(path);
|
|
703
|
+
}
|
|
704
|
+
function schemaDiagnostics(error, locate, kind = "schema") {
|
|
705
|
+
return error.issues.map((issue) => ({
|
|
706
|
+
kind,
|
|
707
|
+
location: locate(issue.path),
|
|
708
|
+
constraint: correctiveConstraint(issue)
|
|
709
|
+
}));
|
|
710
|
+
}
|
|
711
|
+
function correctiveConstraint(issue) {
|
|
712
|
+
switch (issue.code) {
|
|
713
|
+
case "invalid_type":
|
|
714
|
+
return `Provide a value of type ${issue.expected}.`;
|
|
715
|
+
case "too_big":
|
|
716
|
+
return `Use ${issue.inclusive ? "at most" : "fewer than"} ${issue.maximum} ${issue.origin === "array" ? "entries" : issue.origin === "string" ? "characters" : "as the value"}.`;
|
|
717
|
+
case "too_small":
|
|
718
|
+
return `Use ${issue.inclusive ? "at least" : "more than"} ${issue.minimum} ${issue.origin === "array" ? "entries" : issue.origin === "string" ? "characters" : "as the value"}.`;
|
|
719
|
+
// Format messages are fixed by our schemas (or Zod); unlike custom issues they contain no authored values.
|
|
720
|
+
case "invalid_format":
|
|
721
|
+
return issue.message;
|
|
722
|
+
case "invalid_value":
|
|
723
|
+
return "Use one of the values allowed by the board schema.";
|
|
724
|
+
case "custom":
|
|
725
|
+
return typeof issue.params?.constraint === "string" ? issue.params.constraint : "Correct this field according to the board schema.";
|
|
726
|
+
default:
|
|
727
|
+
return "Provide a value matching the board schema for this field.";
|
|
728
|
+
}
|
|
729
|
+
}
|
|
730
|
+
|
|
731
|
+
// packages/server/src/allowlist.ts
|
|
732
|
+
function deriveValidatedAllowlist(config) {
|
|
733
|
+
return derive(config, true);
|
|
734
|
+
}
|
|
735
|
+
function derive(config, rejectUnsupported) {
|
|
736
|
+
const allowed = /* @__PURE__ */ new Map();
|
|
737
|
+
for (const [boardName, board] of Object.entries(config.boards)) {
|
|
738
|
+
for (const panel of board.panels) {
|
|
739
|
+
const source = panel.source ? config.sources[panel.source] : void 0;
|
|
740
|
+
try {
|
|
741
|
+
const capability = panelCapability(panel, source);
|
|
742
|
+
if (capability.kind === "unsupported") {
|
|
743
|
+
if (rejectUnsupported) throw new Error("no bounded operations");
|
|
744
|
+
continue;
|
|
745
|
+
}
|
|
746
|
+
if (capability.kind === "server")
|
|
747
|
+
allowed.set(`${boardName}/${panel.id}`, new Set(capability.operations));
|
|
748
|
+
} catch (error) {
|
|
749
|
+
if (rejectUnsupported) {
|
|
750
|
+
const location = `boards[${Object.keys(config.boards).indexOf(boardName)}].panels[${board.panels.indexOf(panel)}]`;
|
|
751
|
+
throw new ConfigurationError(
|
|
752
|
+
unsupportedPanelOperation(boardName, panel, source, error).message,
|
|
753
|
+
error instanceof ZodError ? schemaDiagnostics(
|
|
754
|
+
error,
|
|
755
|
+
([subject, ...path]) => {
|
|
756
|
+
const root = subject === "source" ? ["sources", Object.keys(config.sources).indexOf(panel.source ?? "")] : [
|
|
757
|
+
"boards",
|
|
758
|
+
Object.keys(config.boards).indexOf(boardName),
|
|
759
|
+
"panels",
|
|
760
|
+
board.panels.indexOf(panel)
|
|
761
|
+
];
|
|
762
|
+
return configurationLocation([...root, ...path]);
|
|
763
|
+
},
|
|
764
|
+
"panel-admission"
|
|
765
|
+
) : [
|
|
766
|
+
{
|
|
767
|
+
kind: "panel-admission",
|
|
768
|
+
location,
|
|
769
|
+
constraint: "Use a supported panel/source combination with bounded HTTP(S) operations."
|
|
770
|
+
}
|
|
771
|
+
]
|
|
772
|
+
);
|
|
773
|
+
}
|
|
774
|
+
throw error;
|
|
775
|
+
}
|
|
776
|
+
}
|
|
777
|
+
}
|
|
778
|
+
return allowed;
|
|
779
|
+
}
|
|
780
|
+
function panelCapability(panel, source) {
|
|
781
|
+
if (panel.type === "pipeline-status" && source?.type === "github-actions") {
|
|
782
|
+
permittedGithubActionsCalls(panel, source);
|
|
783
|
+
return { kind: "server", operations: ["read"] };
|
|
784
|
+
}
|
|
785
|
+
if (panel.type === "pipeline-status" && source?.type === "azure-devops") {
|
|
786
|
+
permittedAzureDevOpsCalls(panel, source);
|
|
787
|
+
return { kind: "server", operations: ["read"] };
|
|
788
|
+
}
|
|
789
|
+
if (panel.type === "pipeline-status" && source?.type === "gitlab-ci") {
|
|
790
|
+
permittedGitlabCiCalls(panel, source);
|
|
791
|
+
return { kind: "server", operations: ["read"] };
|
|
792
|
+
}
|
|
793
|
+
if (panel.type === "http-value") {
|
|
794
|
+
permittedHttpValueCalls(panel);
|
|
795
|
+
return { kind: "server", operations: ["read"] };
|
|
796
|
+
}
|
|
797
|
+
if (panel.type === "pull-request-health" && source?.type === "github-actions") {
|
|
798
|
+
pullRequestHealthCapabilities(panel);
|
|
799
|
+
return {
|
|
800
|
+
kind: "server",
|
|
801
|
+
operations: ["pull-requests", "update-workflow", "pull-request-build"]
|
|
802
|
+
};
|
|
803
|
+
}
|
|
804
|
+
if (panel.type === "pipeline-animation-demo") return { kind: "client-only" };
|
|
805
|
+
return { kind: "unsupported" };
|
|
806
|
+
}
|
|
807
|
+
function unsupportedPanelOperation(boardName, panel, source, error) {
|
|
808
|
+
const sourceName = panel.source ?? "(none)";
|
|
809
|
+
const sourceType = source?.type ?? "(none)";
|
|
810
|
+
const detail = error instanceof Error && error.message !== "no bounded operations" ? ` ${error.message}` : "";
|
|
811
|
+
return new Error(
|
|
812
|
+
`Unsupported configured panel operation: board "${boardName}", panel "${panel.id}", source "${sourceName}" (${sourceType}), signal "${panel.type}".${detail} Configure a supported panel/source combination and all adapter-required fields.`
|
|
813
|
+
);
|
|
814
|
+
}
|
|
815
|
+
|
|
816
|
+
// packages/server/src/board-config.ts
|
|
817
|
+
import { readFile as readFile2 } from "node:fs/promises";
|
|
818
|
+
import { parse as parseYaml, YAMLParseError } from "yaml";
|
|
819
|
+
async function loadBoardConfig(location, fetcher = globalThis.fetch, expectedSchemaUrl) {
|
|
820
|
+
const text = isUrl(location) ? await fetchBoardConfig(location, fetcher) : await readFile2(location, "utf-8");
|
|
821
|
+
let authoredSchemaUrl;
|
|
822
|
+
try {
|
|
823
|
+
authoredSchemaUrl = readBoardSchemaModeline(text, expectedSchemaUrl);
|
|
824
|
+
} catch (error) {
|
|
825
|
+
throw new ConfigurationError(error instanceof Error ? error.message : "Invalid modeline", [
|
|
826
|
+
{
|
|
827
|
+
kind: "modeline",
|
|
828
|
+
location: "line 1",
|
|
829
|
+
constraint: "Add a first-line yaml-language-server schema modeline ending in /board-config.schema.json."
|
|
830
|
+
}
|
|
831
|
+
]);
|
|
832
|
+
}
|
|
833
|
+
let parsed;
|
|
834
|
+
try {
|
|
835
|
+
parsed = parseYaml(text);
|
|
836
|
+
} catch (error) {
|
|
837
|
+
const position = error instanceof YAMLParseError ? error.linePos?.[0] : void 0;
|
|
838
|
+
const location2 = position ? `line ${position.line}, column ${position.col}` : "configuration";
|
|
839
|
+
throw new ConfigurationError(
|
|
840
|
+
`Invalid board configuration YAML at ${location2}: correct YAML syntax.`,
|
|
841
|
+
[
|
|
842
|
+
{
|
|
843
|
+
kind: "yaml",
|
|
844
|
+
location: location2,
|
|
845
|
+
constraint: "Correct YAML syntax and indentation."
|
|
846
|
+
}
|
|
847
|
+
]
|
|
848
|
+
);
|
|
849
|
+
}
|
|
850
|
+
const result = boardConfigSchema.safeParse(parsed);
|
|
851
|
+
if (!result.success) {
|
|
852
|
+
const stale = expectedSchemaUrl && authoredSchemaUrl !== expectedSchemaUrl ? `
|
|
853
|
+
Stale schema modeline: expected ${expectedSchemaUrl}` : "";
|
|
854
|
+
throw new ConfigurationError(
|
|
855
|
+
`Invalid board configuration:
|
|
856
|
+
${result.error.message}${stale}`,
|
|
857
|
+
schemaDiagnostics(result.error, (path) => boardConfigurationLocation(parsed, path))
|
|
858
|
+
);
|
|
859
|
+
}
|
|
860
|
+
return result.data;
|
|
861
|
+
}
|
|
862
|
+
function isUrl(value2) {
|
|
863
|
+
return /^https?:\/\//i.test(value2);
|
|
864
|
+
}
|
|
865
|
+
async function fetchBoardConfig(url, fetcher) {
|
|
866
|
+
const response = await fetcher(url);
|
|
867
|
+
if (!response.ok)
|
|
868
|
+
throw new Error(
|
|
869
|
+
`Unable to fetch board configuration from ${url}: ${response.status} ${response.statusText}`
|
|
870
|
+
);
|
|
871
|
+
return response.text();
|
|
560
872
|
}
|
|
561
873
|
|
|
562
874
|
// packages/aws/src/release.ts
|
|
875
|
+
import { stringify } from "yaml";
|
|
563
876
|
var CORE_RUNTIME_VERSION = "1.0.0";
|
|
564
877
|
var CANONICAL_ASSET_DOMAIN = "https://public-assets.zegreatrob.com";
|
|
565
878
|
function legacyAssetPath(version, domain = CANONICAL_ASSET_DOMAIN) {
|
|
@@ -582,22 +895,27 @@ function normalizeAssetPath(value2) {
|
|
|
582
895
|
return assetPath;
|
|
583
896
|
}
|
|
584
897
|
async function validateBoardConfig(path, expectedSchemaUrl) {
|
|
585
|
-
const
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
898
|
+
const config = await loadBoardConfig(resolve(path), void 0, expectedSchemaUrl).catch(
|
|
899
|
+
(error) => {
|
|
900
|
+
throw new Error(
|
|
901
|
+
`${path}: ${error instanceof Error ? error.message : "Invalid board configuration"}`
|
|
902
|
+
);
|
|
903
|
+
}
|
|
904
|
+
);
|
|
905
|
+
try {
|
|
906
|
+
deriveValidatedAllowlist(config);
|
|
907
|
+
} catch (error) {
|
|
908
|
+
throw new Error(
|
|
909
|
+
`${path}: ${error instanceof Error ? error.message : "Invalid panel configuration"}`
|
|
910
|
+
);
|
|
593
911
|
}
|
|
594
912
|
const yaml = `${boardSchemaModeline(expectedSchemaUrl)}
|
|
595
|
-
${stringify(
|
|
913
|
+
${stringify(config, { sortMapEntries: true })}`;
|
|
596
914
|
return {
|
|
597
915
|
yaml,
|
|
598
916
|
sha256: sha256(yaml),
|
|
599
|
-
usesCredentials: Object.values(
|
|
600
|
-
(
|
|
917
|
+
usesCredentials: Object.values(config.sources).some(
|
|
918
|
+
(source) => credentialEnvironmentNames(source).length > 0
|
|
601
919
|
)
|
|
602
920
|
};
|
|
603
921
|
}
|
|
@@ -750,11 +1068,11 @@ async function bootstrapTemplatePath(kind, mode = "lambda") {
|
|
|
750
1068
|
return fileURLToPath(new URL(templates[kind][mode], import.meta.url));
|
|
751
1069
|
}
|
|
752
1070
|
async function bootstrapTemplate(kind, mode = "lambda") {
|
|
753
|
-
return
|
|
1071
|
+
return readFile3(await bootstrapTemplatePath(kind, mode), "utf8");
|
|
754
1072
|
}
|
|
755
1073
|
async function installedBootstrapDesiredState(config = {}) {
|
|
756
1074
|
const packageManifest = JSON.parse(
|
|
757
|
-
await
|
|
1075
|
+
await readFile3(new URL("../package.json", import.meta.url), "utf8")
|
|
758
1076
|
);
|
|
759
1077
|
return desiredStateFromTemplates(
|
|
760
1078
|
packageVersion(packageManifest),
|
|
@@ -824,7 +1142,7 @@ function inspectTemplate(kind, path, template) {
|
|
|
824
1142
|
}
|
|
825
1143
|
async function bootstrapPlan(config) {
|
|
826
1144
|
const packageManifest = JSON.parse(
|
|
827
|
-
await
|
|
1145
|
+
await readFile3(new URL("../package.json", import.meta.url), "utf8")
|
|
828
1146
|
);
|
|
829
1147
|
const packageTemplates = await installedTemplateInspections(computeMode(config));
|
|
830
1148
|
const installedDesiredState = desiredStateFromTemplates(
|
|
@@ -997,7 +1315,7 @@ function requiredBootstrapParameters(kind, values) {
|
|
|
997
1315
|
}
|
|
998
1316
|
|
|
999
1317
|
// packages/aws/src/index.ts
|
|
1000
|
-
import { cp,
|
|
1318
|
+
import { cp, readFile as readFile5, rm, writeFile as writeFile3 } from "node:fs/promises";
|
|
1001
1319
|
import { join as join2, resolve as resolve2 } from "node:path";
|
|
1002
1320
|
import { fileURLToPath as fileURLToPath2 } from "node:url";
|
|
1003
1321
|
|
|
@@ -2039,7 +2357,7 @@ async function checkBootstrap(config, options, dependencies) {
|
|
|
2039
2357
|
}
|
|
2040
2358
|
|
|
2041
2359
|
// packages/aws/src/guided.ts
|
|
2042
|
-
import { readFile as
|
|
2360
|
+
import { readFile as readFile4, writeFile as writeFile2 } from "node:fs/promises";
|
|
2043
2361
|
|
|
2044
2362
|
// packages/aws/src/handoff.ts
|
|
2045
2363
|
var githubSubjectKeys = [
|
|
@@ -2555,7 +2873,7 @@ async function scaffoldBootstrapManifest(input) {
|
|
|
2555
2873
|
async function upgradeBootstrapManifest(path) {
|
|
2556
2874
|
let parsed;
|
|
2557
2875
|
try {
|
|
2558
|
-
parsed = JSON.parse(await
|
|
2876
|
+
parsed = JSON.parse(await readFile4(path, "utf8"));
|
|
2559
2877
|
} catch (error) {
|
|
2560
2878
|
throw new Error(
|
|
2561
2879
|
`Unable to read bootstrap manifest ${path}: ${error instanceof Error ? error.message : String(error)}`
|
|
@@ -2851,11 +3169,8 @@ function deploymentTemplate(template, values) {
|
|
|
2851
3169
|
}
|
|
2852
3170
|
async function packageLambda(options) {
|
|
2853
3171
|
const outputDir = resolve2(options.outputDir);
|
|
2854
|
-
await mkdir2(outputDir, { recursive: true });
|
|
2855
3172
|
const runtimeDir = join2(outputDir, "lambda");
|
|
2856
|
-
await mkdir2(runtimeDir, { recursive: true });
|
|
2857
3173
|
const lambdaSource = fileURLToPath2(new URL("../dist/lambda.mjs", import.meta.url));
|
|
2858
|
-
await cp(lambdaSource, join2(runtimeDir, "index.mjs"));
|
|
2859
3174
|
const release = await assembleRelease({
|
|
2860
3175
|
boardConfigPath: options.boardConfigPath,
|
|
2861
3176
|
outputDir: runtimeDir,
|
|
@@ -2865,12 +3180,13 @@ async function packageLambda(options) {
|
|
|
2865
3180
|
assetDomain: options.assetDomain,
|
|
2866
3181
|
secretReference: options.secretReference
|
|
2867
3182
|
});
|
|
3183
|
+
await cp(lambdaSource, join2(runtimeDir, "index.mjs"));
|
|
2868
3184
|
const runtimeMetadata = {
|
|
2869
3185
|
...release.metadata,
|
|
2870
3186
|
computeMode: "lambda",
|
|
2871
3187
|
artifactChecksums: {
|
|
2872
3188
|
...release.metadata.artifactChecksums,
|
|
2873
|
-
"index.mjs": sha256(await
|
|
3189
|
+
"index.mjs": sha256(await readFile5(join2(runtimeDir, "index.mjs")))
|
|
2874
3190
|
}
|
|
2875
3191
|
};
|
|
2876
3192
|
await writeFile3(join2(runtimeDir, "release.json"), `${JSON.stringify(runtimeMetadata, null, 2)}
|
|
@@ -2885,7 +3201,7 @@ async function packageLambda(options) {
|
|
|
2885
3201
|
archiveFiles.map(async (name) => [
|
|
2886
3202
|
name,
|
|
2887
3203
|
[
|
|
2888
|
-
strToU8(await
|
|
3204
|
+
strToU8(await readFile5(join2(runtimeDir, name), "utf8")),
|
|
2889
3205
|
{ mtime: new Date(1980, 0, 1, 0, 0, 0), level: 9 }
|
|
2890
3206
|
]
|
|
2891
3207
|
])
|
|
@@ -2893,7 +3209,7 @@ async function packageLambda(options) {
|
|
|
2893
3209
|
);
|
|
2894
3210
|
await writeFile3(lambdaPath, zipSync(archive));
|
|
2895
3211
|
await rm(runtimeDir, { recursive: true, force: true });
|
|
2896
|
-
const lambdaChecksum = sha256(await
|
|
3212
|
+
const lambdaChecksum = sha256(await readFile5(lambdaPath));
|
|
2897
3213
|
const deploymentChecksum = sha256(JSON.stringify(runtimeMetadata));
|
|
2898
3214
|
const packagedRelease = {
|
|
2899
3215
|
...runtimeMetadata,
|
|
@@ -2914,7 +3230,6 @@ async function packageLambda(options) {
|
|
|
2914
3230
|
}
|
|
2915
3231
|
async function packageEcs(options) {
|
|
2916
3232
|
const outputDir = resolve2(options.outputDir);
|
|
2917
|
-
await mkdir2(outputDir, { recursive: true });
|
|
2918
3233
|
const imageReference = requireImageDigest(options.imageReference ?? "");
|
|
2919
3234
|
const release = await assembleRelease({
|
|
2920
3235
|
boardConfigPath: options.boardConfigPath,
|
|
@@ -2943,13 +3258,13 @@ async function packageEcs(options) {
|
|
|
2943
3258
|
);
|
|
2944
3259
|
await writeFile3(
|
|
2945
3260
|
join2(outputDir, "SHA256SUMS"),
|
|
2946
|
-
`${sha256(await
|
|
3261
|
+
`${sha256(await readFile5(join2(outputDir, "release.json")))} release.json
|
|
2947
3262
|
`
|
|
2948
3263
|
);
|
|
2949
3264
|
return packagedRelease;
|
|
2950
3265
|
}
|
|
2951
3266
|
async function cloudFormationTemplate(mode = "lambda") {
|
|
2952
|
-
return
|
|
3267
|
+
return readFile5(
|
|
2953
3268
|
fileURLToPath2(
|
|
2954
3269
|
new URL(mode === "ecs" ? "../template-ecs.yml" : "../template.yml", import.meta.url)
|
|
2955
3270
|
),
|
|
@@ -2991,8 +3306,8 @@ async function templateContract(parameters) {
|
|
|
2991
3306
|
)?.[0];
|
|
2992
3307
|
const metadataBlock = template.match(/^Metadata:\n[\s\S]*?(?=^Parameters:\s*$)/m)?.[0];
|
|
2993
3308
|
if (!parametersBlock) throw new Error("template has no Parameters block");
|
|
2994
|
-
const parsedParameters =
|
|
2995
|
-
const parsedMetadata = metadataBlock ?
|
|
3309
|
+
const parsedParameters = parse(parametersBlock);
|
|
3310
|
+
const parsedMetadata = metadataBlock ? parse(metadataBlock) : {};
|
|
2996
3311
|
const definitions = parsedParameters.Parameters ?? {};
|
|
2997
3312
|
const managedValue = parsedMetadata.Metadata?.PackageManagedParameters ?? [];
|
|
2998
3313
|
if (!Array.isArray(managedValue) || !managedValue.every((key) => typeof key === "string"))
|
|
@@ -3054,7 +3369,7 @@ async function runDoctor(options, dependencies = actualDependencies) {
|
|
|
3054
3369
|
let parameterData;
|
|
3055
3370
|
await check("Parameters/template", async () => {
|
|
3056
3371
|
const parameters = readParameterValues(
|
|
3057
|
-
JSON.parse(await
|
|
3372
|
+
JSON.parse(await readFile6(options.parametersPath, "utf8"))
|
|
3058
3373
|
);
|
|
3059
3374
|
parameterData = await templateContract(parameters);
|
|
3060
3375
|
return `${options.parametersPath} is compatible (${computeMode({ mode: parameterData.values.ComputeMode })})`;
|
|
@@ -3094,7 +3409,7 @@ async function runDoctor(options, dependencies = actualDependencies) {
|
|
|
3094
3409
|
const template = await bootstrapTemplate("github-oidc");
|
|
3095
3410
|
const expectedRevision = bootstrapTemplateRevision(template);
|
|
3096
3411
|
await check("Bootstrap template", async () => {
|
|
3097
|
-
const raw = JSON.parse(await
|
|
3412
|
+
const raw = JSON.parse(await readFile6(stackPath, "utf8"));
|
|
3098
3413
|
const stack = raw && typeof raw === "object" && "Stacks" in raw ? raw.Stacks?.[0] : raw;
|
|
3099
3414
|
if (!stack || typeof stack !== "object")
|
|
3100
3415
|
throw new Error("captured stack JSON contains no stack");
|
|
@@ -3132,7 +3447,7 @@ var option = (name, fallback) => {
|
|
|
3132
3447
|
};
|
|
3133
3448
|
async function installedPackageVersion() {
|
|
3134
3449
|
const packageManifest = JSON.parse(
|
|
3135
|
-
await
|
|
3450
|
+
await readFile7(new URL("../package.json", import.meta.url), "utf8")
|
|
3136
3451
|
);
|
|
3137
3452
|
return typeof packageManifest.version === "string" ? packageManifest.version : "";
|
|
3138
3453
|
}
|
|
@@ -3146,7 +3461,7 @@ function parameter(key, value2) {
|
|
|
3146
3461
|
}
|
|
3147
3462
|
async function existingParameters(path, allowMissing = false) {
|
|
3148
3463
|
try {
|
|
3149
|
-
const parsed = JSON.parse(await
|
|
3464
|
+
const parsed = JSON.parse(await readFile7(path, "utf8"));
|
|
3150
3465
|
if (!Array.isArray(parsed)) throw new Error(`${path} must contain a JSON parameter array`);
|
|
3151
3466
|
const values = parsed.map((value2) => {
|
|
3152
3467
|
if (!value2 || typeof value2 !== "object" || typeof value2.ParameterKey !== "string" || typeof value2.ParameterValue !== "string")
|
|
@@ -3170,7 +3485,7 @@ function bootstrapKind() {
|
|
|
3170
3485
|
throw new Error("--kind must be core or github-oidc");
|
|
3171
3486
|
}
|
|
3172
3487
|
async function readBootstrapConfig(path) {
|
|
3173
|
-
const parsed = JSON.parse(await
|
|
3488
|
+
const parsed = JSON.parse(await readFile7(path, "utf8"));
|
|
3174
3489
|
if (!parsed || typeof parsed !== "object" || Array.isArray(parsed))
|
|
3175
3490
|
throw new Error("--config must contain a JSON object");
|
|
3176
3491
|
return parsed;
|
|
@@ -3251,10 +3566,10 @@ async function templateParameters(mode = "lambda") {
|
|
|
3251
3566
|
const template = await cloudFormationTemplate(mode);
|
|
3252
3567
|
const block = template.match(/^Parameters:\n[\s\S]*?(?=^[A-Za-z][A-Za-z0-9]*:\s*$)/m)?.[0];
|
|
3253
3568
|
if (!block) throw new Error("Unable to read Parameters from the CloudFormation template");
|
|
3254
|
-
const parsed =
|
|
3569
|
+
const parsed = parse2(block);
|
|
3255
3570
|
if (!parsed.Parameters) throw new Error("CloudFormation template has no Parameters");
|
|
3256
3571
|
const metadataBlock = template.match(/^Metadata:\n[\s\S]*?(?=^Parameters:\s*$)/m)?.[0];
|
|
3257
|
-
const metadata = metadataBlock ?
|
|
3572
|
+
const metadata = metadataBlock ? parse2(metadataBlock).Metadata ?? {} : {};
|
|
3258
3573
|
if (metadata.PackageManagedParameters !== void 0 && (!Array.isArray(metadata.PackageManagedParameters) || !metadata.PackageManagedParameters.every((key) => typeof key === "string")))
|
|
3259
3574
|
throw new Error("CloudFormation template has invalid PackageManagedParameters metadata");
|
|
3260
3575
|
const packageManaged = new Set(metadata.PackageManagedParameters ?? []);
|
|
@@ -3428,7 +3743,7 @@ try {
|
|
|
3428
3743
|
if (action === "init") {
|
|
3429
3744
|
const output = requiredOption("--output");
|
|
3430
3745
|
try {
|
|
3431
|
-
await
|
|
3746
|
+
await readFile7(output, "utf8");
|
|
3432
3747
|
throw new Error(`Refusing to overwrite existing manifest: ${output}`);
|
|
3433
3748
|
} catch (error) {
|
|
3434
3749
|
if (!(error && typeof error === "object" && "code" in error && error.code === "ENOENT"))
|
|
@@ -3486,9 +3801,9 @@ Review and commit this manifest before deployment.`
|
|
|
3486
3801
|
config,
|
|
3487
3802
|
configPath,
|
|
3488
3803
|
workDir: option("--work-dir"),
|
|
3489
|
-
coreStack: coreStackPath ? JSON.parse(await
|
|
3804
|
+
coreStack: coreStackPath ? JSON.parse(await readFile7(coreStackPath, "utf8")) : void 0,
|
|
3490
3805
|
coreStackPath,
|
|
3491
|
-
githubOidcStack: githubStackPath ? JSON.parse(await
|
|
3806
|
+
githubOidcStack: githubStackPath ? JSON.parse(await readFile7(githubStackPath, "utf8")) : void 0,
|
|
3492
3807
|
githubOidcStackPath: githubStackPath,
|
|
3493
3808
|
runner
|
|
3494
3809
|
});
|
|
@@ -3510,9 +3825,9 @@ Review and commit this manifest before deployment.`
|
|
|
3510
3825
|
config,
|
|
3511
3826
|
configPath,
|
|
3512
3827
|
workDir: option("--work-dir"),
|
|
3513
|
-
coreStack: coreStackPath ? JSON.parse(await
|
|
3828
|
+
coreStack: coreStackPath ? JSON.parse(await readFile7(coreStackPath, "utf8")) : void 0,
|
|
3514
3829
|
coreStackPath,
|
|
3515
|
-
githubOidcStack: githubStackPath ? JSON.parse(await
|
|
3830
|
+
githubOidcStack: githubStackPath ? JSON.parse(await readFile7(githubStackPath, "utf8")) : void 0,
|
|
3516
3831
|
githubOidcStackPath: githubStackPath,
|
|
3517
3832
|
runner
|
|
3518
3833
|
});
|
|
@@ -3527,8 +3842,8 @@ ${formatBootstrapRemediationText(handoff.remediation).join("\n")}`
|
|
|
3527
3842
|
const githubStackPath = requiredOption("--github-oidc-stack-json");
|
|
3528
3843
|
const verified = await verifyBootstrap({
|
|
3529
3844
|
config,
|
|
3530
|
-
coreStack: JSON.parse(await
|
|
3531
|
-
githubOidcStack: JSON.parse(await
|
|
3845
|
+
coreStack: JSON.parse(await readFile7(coreStackPath, "utf8")),
|
|
3846
|
+
githubOidcStack: JSON.parse(await readFile7(githubStackPath, "utf8"))
|
|
3532
3847
|
});
|
|
3533
3848
|
printBootstrap(
|
|
3534
3849
|
verified,
|
|
@@ -3612,7 +3927,7 @@ ${plan.notes.join("\n")}`);
|
|
|
3612
3927
|
if (kind === "github-oidc" && coreStackPath) {
|
|
3613
3928
|
coreOutputs = coreBootstrapOutputs(
|
|
3614
3929
|
deployedBootstrapStack(
|
|
3615
|
-
JSON.parse(await
|
|
3930
|
+
JSON.parse(await readFile7(coreStackPath, "utf8")),
|
|
3616
3931
|
bootstrapContractVersion(await bootstrapTemplate("core", mode))
|
|
3617
3932
|
)
|
|
3618
3933
|
);
|
|
@@ -3625,7 +3940,7 @@ ${plan.notes.join("\n")}`);
|
|
|
3625
3940
|
const deployedStackPath = option("--deployed-stack-json");
|
|
3626
3941
|
if (deployedStackPath) {
|
|
3627
3942
|
const stack = deployedBootstrapStack(
|
|
3628
|
-
JSON.parse(await
|
|
3943
|
+
JSON.parse(await readFile7(deployedStackPath, "utf8")),
|
|
3629
3944
|
bootstrapContractVersion(await bootstrapTemplate(kind, mode))
|
|
3630
3945
|
);
|
|
3631
3946
|
if (kind === "github-oidc" && !(stack.Parameters ?? []).some(
|