@pipeline-builder/pipeline-manager 0.0.1
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/LICENSE +202 -0
- package/README.md +145 -0
- package/cdk.json +91 -0
- package/config.yml +94 -0
- package/dist/agent/ai.d.ts +48 -0
- package/dist/agent/ai.d.ts.map +1 -0
- package/dist/agent/ai.js +149 -0
- package/dist/agent/bootstrap.d.ts +39 -0
- package/dist/agent/bootstrap.d.ts.map +1 -0
- package/dist/agent/bootstrap.js +66 -0
- package/dist/agent/env-file.d.ts +17 -0
- package/dist/agent/env-file.d.ts.map +1 -0
- package/dist/agent/env-file.js +47 -0
- package/dist/agent/executor.d.ts +30 -0
- package/dist/agent/executor.d.ts.map +1 -0
- package/dist/agent/executor.js +60 -0
- package/dist/agent/health.d.ts +43 -0
- package/dist/agent/health.d.ts.map +1 -0
- package/dist/agent/health.js +109 -0
- package/dist/agent/ports.d.ts +26 -0
- package/dist/agent/ports.d.ts.map +1 -0
- package/dist/agent/ports.js +132 -0
- package/dist/agent/post-steps.d.ts +61 -0
- package/dist/agent/post-steps.d.ts.map +1 -0
- package/dist/agent/post-steps.js +131 -0
- package/dist/agent/prereqs.d.ts +26 -0
- package/dist/agent/prereqs.d.ts.map +1 -0
- package/dist/agent/prereqs.js +134 -0
- package/dist/agent/targets.d.ts +128 -0
- package/dist/agent/targets.d.ts.map +1 -0
- package/dist/agent/targets.js +265 -0
- package/dist/agent/tools.d.ts +13 -0
- package/dist/agent/tools.d.ts.map +1 -0
- package/dist/agent/tools.js +75 -0
- package/dist/agent/troubleshoot.d.ts +30 -0
- package/dist/agent/troubleshoot.d.ts.map +1 -0
- package/dist/agent/troubleshoot.js +87 -0
- package/dist/boilerplate.d.ts +3 -0
- package/dist/boilerplate.d.ts.map +1 -0
- package/dist/boilerplate.js +54 -0
- package/dist/cdk.json +91 -0
- package/dist/cli.d.ts +62 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +463 -0
- package/dist/commands/audit-stacks.d.ts +24 -0
- package/dist/commands/audit-stacks.d.ts.map +1 -0
- package/dist/commands/audit-stacks.js +182 -0
- package/dist/commands/audit-tokens.d.ts +21 -0
- package/dist/commands/audit-tokens.d.ts.map +1 -0
- package/dist/commands/audit-tokens.js +153 -0
- package/dist/commands/bootstrap.d.ts +11 -0
- package/dist/commands/bootstrap.d.ts.map +1 -0
- package/dist/commands/bootstrap.js +159 -0
- package/dist/commands/create-pat.d.ts +27 -0
- package/dist/commands/create-pat.d.ts.map +1 -0
- package/dist/commands/create-pat.js +194 -0
- package/dist/commands/create-pipeline.d.ts +12 -0
- package/dist/commands/create-pipeline.d.ts.map +1 -0
- package/dist/commands/create-pipeline.js +315 -0
- package/dist/commands/deploy.d.ts +15 -0
- package/dist/commands/deploy.d.ts.map +1 -0
- package/dist/commands/deploy.js +157 -0
- package/dist/commands/entity-get.d.ts +51 -0
- package/dist/commands/entity-get.d.ts.map +1 -0
- package/dist/commands/entity-get.js +69 -0
- package/dist/commands/entity-list.d.ts +39 -0
- package/dist/commands/entity-list.d.ts.map +1 -0
- package/dist/commands/entity-list.js +88 -0
- package/dist/commands/get-pipeline.d.ts +13 -0
- package/dist/commands/get-pipeline.d.ts.map +1 -0
- package/dist/commands/get-pipeline.js +49 -0
- package/dist/commands/get-plugin.d.ts +13 -0
- package/dist/commands/get-plugin.d.ts.map +1 -0
- package/dist/commands/get-plugin.js +51 -0
- package/dist/commands/instantiate-template.d.ts +20 -0
- package/dist/commands/instantiate-template.d.ts.map +1 -0
- package/dist/commands/instantiate-template.js +274 -0
- package/dist/commands/list-pipelines.d.ts +20 -0
- package/dist/commands/list-pipelines.d.ts.map +1 -0
- package/dist/commands/list-pipelines.js +99 -0
- package/dist/commands/list-plugins.d.ts +20 -0
- package/dist/commands/list-plugins.d.ts.map +1 -0
- package/dist/commands/list-plugins.js +92 -0
- package/dist/commands/login.d.ts +21 -0
- package/dist/commands/login.d.ts.map +1 -0
- package/dist/commands/login.js +174 -0
- package/dist/commands/new-plugin.d.ts +12 -0
- package/dist/commands/new-plugin.d.ts.map +1 -0
- package/dist/commands/new-plugin.js +129 -0
- package/dist/commands/org-export.d.ts +25 -0
- package/dist/commands/org-export.d.ts.map +1 -0
- package/dist/commands/org-export.js +100 -0
- package/dist/commands/provision.d.ts +122 -0
- package/dist/commands/provision.d.ts.map +1 -0
- package/dist/commands/provision.js +867 -0
- package/dist/commands/redrive-events.d.ts +24 -0
- package/dist/commands/redrive-events.d.ts.map +1 -0
- package/dist/commands/redrive-events.js +127 -0
- package/dist/commands/register.d.ts +38 -0
- package/dist/commands/register.d.ts.map +1 -0
- package/dist/commands/register.js +160 -0
- package/dist/commands/setup-events.d.ts +15 -0
- package/dist/commands/setup-events.d.ts.map +1 -0
- package/dist/commands/setup-events.js +168 -0
- package/dist/commands/status.d.ts +11 -0
- package/dist/commands/status.d.ts.map +1 -0
- package/dist/commands/status.js +81 -0
- package/dist/commands/store-token.d.ts +26 -0
- package/dist/commands/store-token.d.ts.map +1 -0
- package/dist/commands/store-token.js +305 -0
- package/dist/commands/synth.d.ts +21 -0
- package/dist/commands/synth.d.ts.map +1 -0
- package/dist/commands/synth.js +158 -0
- package/dist/commands/upload-plugin.d.ts +21 -0
- package/dist/commands/upload-plugin.d.ts.map +1 -0
- package/dist/commands/upload-plugin.js +275 -0
- package/dist/commands/validate-plugin.d.ts +12 -0
- package/dist/commands/validate-plugin.d.ts.map +1 -0
- package/dist/commands/validate-plugin.js +175 -0
- package/dist/commands/validate-templates.d.ts +11 -0
- package/dist/commands/validate-templates.d.ts.map +1 -0
- package/dist/commands/validate-templates.js +116 -0
- package/dist/commands/version.d.ts +12 -0
- package/dist/commands/version.d.ts.map +1 -0
- package/dist/commands/version.js +216 -0
- package/dist/config/cli.constants.d.ts +116 -0
- package/dist/config/cli.constants.d.ts.map +1 -0
- package/dist/config/cli.constants.js +194 -0
- package/dist/config.yml +94 -0
- package/dist/lambda/token-renew-handler.d.ts +2 -0
- package/dist/lambda/token-renew-handler.d.ts.map +1 -0
- package/dist/lambda/token-renew-handler.js +125 -0
- package/dist/templates/events-stack.json +174 -0
- package/dist/templates/token-renew-stack.json +122 -0
- package/dist/types/config.d.ts +53 -0
- package/dist/types/config.d.ts.map +1 -0
- package/dist/types/config.js +4 -0
- package/dist/types/error.d.ts +61 -0
- package/dist/types/error.d.ts.map +1 -0
- package/dist/types/error.js +35 -0
- package/dist/types/index.d.ts +9 -0
- package/dist/types/index.d.ts.map +1 -0
- package/dist/types/index.js +11 -0
- package/dist/types/pipeline-template.d.ts +48 -0
- package/dist/types/pipeline-template.d.ts.map +1 -0
- package/dist/types/pipeline-template.js +4 -0
- package/dist/types/pipeline.d.ts +121 -0
- package/dist/types/pipeline.d.ts.map +1 -0
- package/dist/types/pipeline.js +4 -0
- package/dist/types/plugin.d.ts +135 -0
- package/dist/types/plugin.d.ts.map +1 -0
- package/dist/types/plugin.js +4 -0
- package/dist/utils/api-client.d.ts +26 -0
- package/dist/utils/api-client.d.ts.map +1 -0
- package/dist/utils/api-client.js +151 -0
- package/dist/utils/audit-log.d.ts +8 -0
- package/dist/utils/audit-log.d.ts.map +1 -0
- package/dist/utils/audit-log.js +57 -0
- package/dist/utils/auth-guard.d.ts +20 -0
- package/dist/utils/auth-guard.d.ts.map +1 -0
- package/dist/utils/auth-guard.js +22 -0
- package/dist/utils/auth-utils.d.ts +68 -0
- package/dist/utils/auth-utils.d.ts.map +1 -0
- package/dist/utils/auth-utils.js +77 -0
- package/dist/utils/aws-env.d.ts +21 -0
- package/dist/utils/aws-env.d.ts.map +1 -0
- package/dist/utils/aws-env.js +34 -0
- package/dist/utils/aws-secrets.d.ts +48 -0
- package/dist/utils/aws-secrets.d.ts.map +1 -0
- package/dist/utils/aws-secrets.js +115 -0
- package/dist/utils/banner.d.ts +19 -0
- package/dist/utils/banner.d.ts.map +1 -0
- package/dist/utils/banner.js +52 -0
- package/dist/utils/cdk-utils.d.ts +74 -0
- package/dist/utils/cdk-utils.d.ts.map +1 -0
- package/dist/utils/cdk-utils.js +152 -0
- package/dist/utils/command-utils.d.ts +75 -0
- package/dist/utils/command-utils.d.ts.map +1 -0
- package/dist/utils/command-utils.js +155 -0
- package/dist/utils/config-loader.d.ts +33 -0
- package/dist/utils/config-loader.d.ts.map +1 -0
- package/dist/utils/config-loader.js +154 -0
- package/dist/utils/cron.d.ts +28 -0
- package/dist/utils/cron.d.ts.map +1 -0
- package/dist/utils/cron.js +119 -0
- package/dist/utils/deploy-runner.d.ts +38 -0
- package/dist/utils/deploy-runner.d.ts.map +1 -0
- package/dist/utils/deploy-runner.js +112 -0
- package/dist/utils/error-handler.d.ts +29 -0
- package/dist/utils/error-handler.d.ts.map +1 -0
- package/dist/utils/error-handler.js +302 -0
- package/dist/utils/list-command-utils.d.ts +23 -0
- package/dist/utils/list-command-utils.d.ts.map +1 -0
- package/dist/utils/list-command-utils.js +57 -0
- package/dist/utils/output-utils.d.ts +40 -0
- package/dist/utils/output-utils.d.ts.map +1 -0
- package/dist/utils/output-utils.js +250 -0
- package/dist/utils/pipeline-config.d.ts +33 -0
- package/dist/utils/pipeline-config.d.ts.map +1 -0
- package/dist/utils/pipeline-config.js +55 -0
- package/dist/utils/platform-secret.d.ts +27 -0
- package/dist/utils/platform-secret.d.ts.map +1 -0
- package/dist/utils/platform-secret.js +92 -0
- package/dist/utils/plugin-resolver.d.ts +17 -0
- package/dist/utils/plugin-resolver.d.ts.map +1 -0
- package/dist/utils/plugin-resolver.js +101 -0
- package/dist/utils/preflight.d.ts +26 -0
- package/dist/utils/preflight.d.ts.map +1 -0
- package/dist/utils/preflight.js +59 -0
- package/dist/utils/rate-limiter.d.ts +15 -0
- package/dist/utils/rate-limiter.d.ts.map +1 -0
- package/dist/utils/rate-limiter.js +164 -0
- package/dist/utils/registry.d.ts +86 -0
- package/dist/utils/registry.d.ts.map +1 -0
- package/dist/utils/registry.js +140 -0
- package/dist/utils/response-utils.d.ts +21 -0
- package/dist/utils/response-utils.d.ts.map +1 -0
- package/dist/utils/response-utils.js +70 -0
- package/dist/utils/tls.d.ts +52 -0
- package/dist/utils/tls.d.ts.map +1 -0
- package/dist/utils/tls.js +88 -0
- package/package.json +181 -0
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
// Copyright 2026 Pipeline Builder Contributors
|
|
2
|
+
// SPDX-License-Identifier: Apache-2.0
|
|
3
|
+
/**
|
|
4
|
+
* Post-deploy health verification. A CloudFormation CREATE_COMPLETE (or a
|
|
5
|
+
* finished setup.sh) does NOT mean the app is serving — the ALB targets stay
|
|
6
|
+
* unhealthy until tasks/pods come up. The real done-signal is the platform's
|
|
7
|
+
* `/health` + `/ready` endpoints, so the executor polls them with backoff.
|
|
8
|
+
*/
|
|
9
|
+
import { spawn } from 'child_process';
|
|
10
|
+
import axios from 'axios';
|
|
11
|
+
import { httpsAgentForUrl } from '../utils/tls.js';
|
|
12
|
+
/**
|
|
13
|
+
* Derive the URL the platform should be reachable at. Local/minikube front the
|
|
14
|
+
* stack on localhost:8443; EC2/EKS use the deploy domain. Returns null when
|
|
15
|
+
* it can't be determined (caller surfaces a "verify manually" note).
|
|
16
|
+
*/
|
|
17
|
+
export function deriveHealthUrl(target, params) {
|
|
18
|
+
if (target === 'docker' || target === 'minikube')
|
|
19
|
+
return 'https://localhost:8443';
|
|
20
|
+
const domain = params.domain;
|
|
21
|
+
return typeof domain === 'string' && domain ? `https://${domain}` : null;
|
|
22
|
+
}
|
|
23
|
+
async function probe(url) {
|
|
24
|
+
try {
|
|
25
|
+
// Skip cert verification only for local/self-signed hosts (docker/minikube);
|
|
26
|
+
// a real ec2/eks domain presents a valid ACM cert and IS verified.
|
|
27
|
+
const res = await axios.get(url, { timeout: 5000, httpsAgent: httpsAgentForUrl(url), validateStatus: () => true });
|
|
28
|
+
return res.status >= 200 && res.status < 300;
|
|
29
|
+
}
|
|
30
|
+
catch {
|
|
31
|
+
return false;
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
const delay = (ms) => new Promise((r) => setTimeout(r, ms));
|
|
35
|
+
/**
|
|
36
|
+
* Poll `<url>/health` then `<url>/ready` until both pass or the timeout elapses.
|
|
37
|
+
* Non-fatal by design: a timeout returns `healthy: false` with guidance, never throws.
|
|
38
|
+
*/
|
|
39
|
+
export async function waitHealthy(url, opts = {}) {
|
|
40
|
+
const timeoutMs = opts.timeoutMs ?? 300_000; // 5 min — services come up async
|
|
41
|
+
const intervalMs = opts.intervalMs ?? 10_000;
|
|
42
|
+
// Once /health is up, only wait this much longer for /ready before proceeding.
|
|
43
|
+
const readyGraceMs = opts.readyGraceMs ?? 90_000; // 90s covers normal warmup
|
|
44
|
+
const deadline = Date.now() + timeoutMs;
|
|
45
|
+
// Wait for /health, then give /ready a BOUNDED grace window. /health flips first
|
|
46
|
+
// (the process is up); /ready also requires every backend dependency to be up.
|
|
47
|
+
// We prefer /ready (post-install register hits a not-yet-ready platform → 502),
|
|
48
|
+
// but if a dependency is DOWN (e.g. a crash-looping service) /ready never flips —
|
|
49
|
+
// so we must not block the full timeout on it. Once /health is up we wait at most
|
|
50
|
+
// `readyGraceMs` more, then proceed with a warning (register only needs the
|
|
51
|
+
// platform service, which /health already confirms is up).
|
|
52
|
+
let healthSeenAt = 0;
|
|
53
|
+
while (Date.now() < deadline) {
|
|
54
|
+
if (await probe(`${url}/health`)) {
|
|
55
|
+
if (healthSeenAt === 0)
|
|
56
|
+
healthSeenAt = Date.now();
|
|
57
|
+
if (await probe(`${url}/ready`)) {
|
|
58
|
+
return { url, healthy: true, ready: true, detail: 'health + ready OK' };
|
|
59
|
+
}
|
|
60
|
+
const leftMs = readyGraceMs - (Date.now() - healthSeenAt);
|
|
61
|
+
if (leftMs <= 0) {
|
|
62
|
+
return {
|
|
63
|
+
url,
|
|
64
|
+
healthy: true,
|
|
65
|
+
ready: false,
|
|
66
|
+
detail: `health OK, but /ready didn't pass within ${Math.round(readyGraceMs / 1000)}s — a backend service is likely down (check \`docker compose ps\` / its logs). Continuing; routes for that service may 502 until it's healthy.`,
|
|
67
|
+
};
|
|
68
|
+
}
|
|
69
|
+
opts.onTick?.(`Health OK — waiting up to ${Math.ceil(leftMs / 1000)}s for ${url}/ready (dependencies warming up) …`);
|
|
70
|
+
}
|
|
71
|
+
else {
|
|
72
|
+
opts.onTick?.(`Waiting for ${url}/health …`);
|
|
73
|
+
}
|
|
74
|
+
await delay(intervalMs);
|
|
75
|
+
}
|
|
76
|
+
// Overall timeout. If /health came up but /ready never did, proceed (non-fatal).
|
|
77
|
+
return healthSeenAt > 0
|
|
78
|
+
? { url, healthy: true, ready: false, detail: 'health OK but /ready not reached — the platform is still warming up; post-install steps (register) may need a re-run' }
|
|
79
|
+
: { url, healthy: false, ready: false, detail: `not reachable within ${Math.round(timeoutMs / 1000)}s — check the stack / DNS / health logs` };
|
|
80
|
+
}
|
|
81
|
+
/**
|
|
82
|
+
* Minikube only: the gateway (https://localhost:8443) is reached through a
|
|
83
|
+
* `kubectl port-forward svc/nginx` that setup.sh backgrounds — which can die or
|
|
84
|
+
* fail to bind (e.g. a busy port) while the pods stay healthy, leaving the gateway
|
|
85
|
+
* unreachable so the health poll just times out. Before polling, probe the gateway;
|
|
86
|
+
* if it's down, (re)start the forward DETACHED so it outlives this CLI, then give
|
|
87
|
+
* it a moment to bind. Best-effort: never throws; on no-kubectl it logs the manual
|
|
88
|
+
* command. (setup.sh now forwards 8443 only, so a busy 8080 can't take it down.)
|
|
89
|
+
*/
|
|
90
|
+
export async function ensureMinikubeGateway(url, opts = {}) {
|
|
91
|
+
const ns = opts.ns ?? 'pipeline-builder';
|
|
92
|
+
const port = opts.port ?? 8443;
|
|
93
|
+
if (await probe(`${url}/health`))
|
|
94
|
+
return; // forward already up — nothing to do
|
|
95
|
+
opts.onInfo?.(`Gateway unreachable — (re)starting the nginx port-forward (svc/nginx ${port}:${port}, ns ${ns}) …`);
|
|
96
|
+
// A missing kubectl surfaces as an async 'error' event (ENOENT), NOT a synchronous
|
|
97
|
+
// throw — a try/catch would miss it and the unhandled event would crash the CLI. Attach
|
|
98
|
+
// an 'error' listener so this stays best-effort (logs the manual command, never throws).
|
|
99
|
+
const child = spawn('kubectl', ['port-forward', '-n', ns, 'svc/nginx', `${port}:${port}`], {
|
|
100
|
+
detached: true,
|
|
101
|
+
stdio: 'ignore',
|
|
102
|
+
});
|
|
103
|
+
child.on('error', () => {
|
|
104
|
+
opts.onInfo?.(`Couldn't start it (is kubectl on PATH?). Start it manually: kubectl port-forward -n ${ns} svc/nginx ${port}:${port}`);
|
|
105
|
+
});
|
|
106
|
+
child.unref(); // detached + unref → survives this CLI so the user keeps the gateway
|
|
107
|
+
await delay(2000); // let it bind before the caller polls
|
|
108
|
+
}
|
|
109
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaGVhbHRoLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vc3JjL2FnZW50L2hlYWx0aC50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQSwrQ0FBK0M7QUFDL0Msc0NBQXNDO0FBRXRDOzs7OztHQUtHO0FBRUgsT0FBTyxFQUFFLEtBQUssRUFBRSxNQUFNLGVBQWUsQ0FBQztBQUN0QyxPQUFPLEtBQUssTUFBTSxPQUFPLENBQUM7QUFFMUIsT0FBTyxFQUFFLGdCQUFnQixFQUFFLE1BQU0saUJBQWlCLENBQUM7QUFjbkQ7Ozs7R0FJRztBQUNILE1BQU0sVUFBVSxlQUFlLENBQUMsTUFBZ0IsRUFBRSxNQUErQjtJQUMvRSxJQUFJLE1BQU0sS0FBSyxRQUFRLElBQUksTUFBTSxLQUFLLFVBQVU7UUFBRSxPQUFPLHdCQUF3QixDQUFDO0lBQ2xGLE1BQU0sTUFBTSxHQUFHLE1BQU0sQ0FBQyxNQUFNLENBQUM7SUFDN0IsT0FBTyxPQUFPLE1BQU0sS0FBSyxRQUFRLElBQUksTUFBTSxDQUFDLENBQUMsQ0FBQyxXQUFXLE1BQU0sRUFBRSxDQUFDLENBQUMsQ0FBQyxJQUFJLENBQUM7QUFDM0UsQ0FBQztBQUVELEtBQUssVUFBVSxLQUFLLENBQUMsR0FBVztJQUM5QixJQUFJLENBQUM7UUFDSCw2RUFBNkU7UUFDN0UsbUVBQW1FO1FBQ25FLE1BQU0sR0FBRyxHQUFHLE1BQU0sS0FBSyxDQUFDLEdBQUcsQ0FBQyxHQUFHLEVBQUUsRUFBRSxPQUFPLEVBQUUsSUFBSSxFQUFFLFVBQVUsRUFBRSxnQkFBZ0IsQ0FBQyxHQUFHLENBQUMsRUFBRSxjQUFjLEVBQUUsR0FBRyxFQUFFLENBQUMsSUFBSSxFQUFFLENBQUMsQ0FBQztRQUNuSCxPQUFPLEdBQUcsQ0FBQyxNQUFNLElBQUksR0FBRyxJQUFJLEdBQUcsQ0FBQyxNQUFNLEdBQUcsR0FBRyxDQUFDO0lBQy9DLENBQUM7SUFBQyxNQUFNLENBQUM7UUFDUCxPQUFPLEtBQUssQ0FBQztJQUNmLENBQUM7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RSxHQUFHLElBQUksQ0FBQyxFQUFFLElBQUksa0JBQWtCLENBQUM7SUFDekMsTUFBTSxJQUFJLEdBQUcsSUFBSSxDQUFDLElBQUksSUFBSSxJQUFJLENBQUM7SUFDL0IsSUFBSSxNQUFNLEtBQUssQ0FBQyxHQUFHLEdBQUcsU0FBUyxDQUFDO1FBQUUsT0FBTyxDQUFDLHFDQUFxQztJQUMvRSxJQUFJLENBQUMsTUFBTSxFQUFFLENBQUMsd0VBQXdFLElBQUksSUFBSSxJQUFJLFFBQVEsRUFBRSxLQUFLLENBQUMsQ0FBQztJQUNuSCxtRkFBbUY7SUFDbkYsd0ZBQXdGO0lBQ3hGLHlGQUF5RjtJQUN6RixNQUFNLEtBQUssR0FBRyxLQUFLLENBQUMsU0FBUyxFQUFFLENBQUMsY0FBYyxFQUFFLElBQUksRUFBRSxFQUFFLEVBQUUsV0FBVyxFQUFFLEdBQUcsSUFBSSxJQUFJLElBQUksRUFBRSxDQUFDLEVBQUU7UUFDekYsUUFBUSxFQUFFLElBQUk7UUFDZCxLQUFLLEVBQUUsUUFBUTtLQUNoQixDQUFDLENBQUM7SUFDSCxLQUFLLENBQUMsRUFBRSxDQUFDLE9BQU8sRUFBRSxHQUFHLEVBQUU7UUFDckIsSUFBSSxDQUFDLE1BQU0sRUFBRSxDQUFDLHVGQUF1RixFQUFFLGNBQWMsSUFBSSxJQUFJLElBQUksRUFBRSxDQUFDLENBQUM7SUFDdkksQ0FBQyxDQUFDLENBQUM7SUFDSCxLQUFLLENBQUMsS0FBSyxFQUFFLENBQUMsQ0FBQyxxRUFBcUU7SUFDcEYsTUFBTSxLQUFLLENBQUMsSUFBSSxDQUFDLENBQUMsQ0FBQyxzQ0FBc0M7QUFDM0QsQ0FBQyIsInNvdXJjZXNDb250ZW50IjpbIi8vIENvcHlyaWdodCAyMDI2IFBpcGVsaW5lIEJ1aWxkZXIgQ29udHJpYnV0b3JzXG4vLyBTUERYLUxpY2Vuc2UtSWRlbnRpZmllcjogQXBhY2hlLTIuMFxuXG4vKipcbiAqIFBvc3QtZGVwbG95IGhlYWx0aCB2ZXJpZmljYXRpb24uIEEgQ2xvdWRGb3JtYXRpb24gQ1JFQVRFX0NPTVBMRVRFIChvciBhXG4gKiBmaW5pc2hlZCBzZXR1cC5zaCkgZG9lcyBOT1QgbWVhbiB0aGUgYXBwIGlzIHNlcnZpbmcg4oCUIHRoZSBBTEIgdGFyZ2V0cyBzdGF5XG4gKiB1bmhlYWx0aHkgdW50aWwgdGFza3MvcG9kcyBjb21lIHVwLiBUaGUgcmVhbCBkb25lLXNpZ25hbCBpcyB0aGUgcGxhdGZvcm0nc1xuICogYC9oZWFsdGhgICsgYC9yZWFkeWAgZW5kcG9pbnRzLCBzbyB0aGUgZXhlY3V0b3IgcG9sbHMgdGhlbSB3aXRoIGJhY2tvZmYuXG4gKi9cblxuaW1wb3J0IHsgc3Bhd24gfSBmcm9tICdjaGlsZF9wcm9jZXNzJztcbmltcG9ydCBheGlvcyBmcm9tICdheGlvcyc7XG5pbXBvcnQgdHlwZSB7IFRhcmdldElkIH0gZnJvbSAnLi90YXJnZXRzLmpzJztcbmltcG9ydCB7IGh0dHBzQWdlbnRGb3JVcmwgfSBmcm9tICcuLi91dGlscy90bHMuanMnO1xuXG5leHBvcnQgaW50ZXJmYWNlIEhlYWx0aFJlc3VsdCB7XG4gIHJlYWRvbmx5IHVybDogc3RyaW5nO1xuICByZWFkb25seSBoZWFsdGh5OiBib29sZWFuO1xuICAvKipcbiAgICogVHJ1ZSBvbmx5IHdoZW4gQk9USCAvaGVhbHRoIGFuZCAvcmVhZHkgcGFzc2VkLiBEaXN0aW5ndWlzaGVzIGEgZnVsbHktcmVhZHlcbiAgICogcGxhdGZvcm0gZnJvbSB0aGUgZGVncmFkZWQgXCJoZWFsdGggT0sgYnV0IC9yZWFkeSBuZXZlciBjYW1lLCBwcm9jZWVkaW5nIGFueXdheVwiXG4gICAqIHN0YXRlIOKAlCBib3RoIGhhdmUgaGVhbHRoeTp0cnVlLCBzbyBjYWxsZXJzIHNob3VsZCBzdHlsZSBzdWNjZXNzIG9uIGByZWFkeWAuXG4gICAqL1xuICByZWFkb25seSByZWFkeTogYm9vbGVhbjtcbiAgcmVhZG9ubHkgZGV0YWlsOiBzdHJpbmc7XG59XG5cbi8qKlxuICogRGVyaXZlIHRoZSBVUkwgdGhlIHBsYXRmb3JtIHNob3VsZCBiZSByZWFjaGFibGUgYXQuIExvY2FsL21pbmlrdWJlIGZyb250IHRoZVxuICogc3RhY2sgb24gbG9jYWxob3N0Ojg0NDM7IEVDMi9FS1MgdXNlIHRoZSBkZXBsb3kgZG9tYWluLiBSZXR1cm5zIG51bGwgd2hlblxuICogaXQgY2FuJ3QgYmUgZGV0ZXJtaW5lZCAoY2FsbGVyIHN1cmZhY2VzIGEgXCJ2ZXJpZnkgbWFudWFsbHlcIiBub3RlKS5cbiAqL1xuZXhwb3J0IGZ1bmN0aW9uIGRlcml2ZUhlYWx0aFVybCh0YXJnZXQ6IFRhcmdldElkLCBwYXJhbXM6IFJlY29yZDxzdHJpbmcsIHVua25vd24+KTogc3RyaW5nIHwgbnVsbCB7XG4gIGlmICh0YXJnZXQgPT09ICdkb2NrZXInIHx8IHRhcmdldCA9PT0gJ21pbmlrdWJlJykgcmV0dXJuICdodHRwczovL2xvY2FsaG9zdDo4NDQzJztcbiAgY29uc3QgZG9tYWluID0gcGFyYW1zLmRvbWFpbjtcbiAgcmV0dXJuIHR5cGVvZiBkb21haW4gPT09ICdzdHJpbmcnICYmIGRvbWFpbiA/IGBodHRwczovLyR7ZG9tYWlufWAgOiBudWxsO1xufVxuXG5hc3luYyBmdW5jdGlvbiBwcm9iZSh1cmw6IHN0cmluZyk6IFByb21pc2U8Ym9vbGVhbj4ge1xuICB0cnkge1xuICAgIC8vIFNraXAgY2VydCB2ZXJpZmljYXRpb24gb25seSBmb3IgbG9jYWwvc2VsZi1zaWduZWQgaG9zdHMgKGRvY2tlci9taW5pa3ViZSk7XG4gICAgLy8gYSByZWFsIGVjMi9la3MgZG9tYWluIHByZXNlbnRzIGEgdmFsaWQgQUNNIGNlcnQgYW5kIElTIHZlcmlmaWVkLlxuICAgIGNvbnN0IHJlcyA9IGF3YWl0IGF4aW9zLmdldCh1cmwsIHsgdGltZW91dDogNTAwMCwgaHR0cHNBZ2VudDogaHR0cHNBZ2VudEZvclVybCh1cmwpLCB2YWxpZGF0ZVN0YXR1czogKCkgPT4gdHJ1ZSB9KTtcbiAgICByZXR1cm4gcmVzLnN0YXR1cyA+PSAyMDAgJiYgcmVzLnN0YXR1cyA8IDMwMDtcbiAgfSBjYXRjaCB7XG4gICAgcmV0dXJuIGZhbHNlO1xuICB9XG59XG5cbmNvbnN0IGRlbGF5ID0gKG1zOiBudW1iZXIpID0+IG5ldyBQcm9taXNlPHZvaWQ+KChyKSA9PiBzZXRUaW1lb3V0KHIsIG1zKSk7XG5cbi8qKlxuICogUG9sbCBgPHVybD4vaGVhbHRoYCB0aGVuIGA8dXJsPi9yZWFkeWAgdW50aWwgYm90aCBwYXNzIG9yIHRoZSB0aW1lb3V0IGVsYXBzZXMuXG4gKiBOb24tZmF0YWwgYnkgZGVzaWduOiBhIHRpbWVvdXQgcmV0dXJucyBgaGVhbHRoeTogZmFsc2VgIHdpdGggZ3VpZGFuY2UsIG5ldmVyIHRocm93cy5cbiAqL1xuZXhwb3J0IGFzeW5jIGZ1bmN0aW9uIHdhaXRIZWFsdGh5KFxuICB1cmw6IHN0cmluZyxcbiAgb3B0czogeyB0aW1lb3V0TXM/OiBudW1iZXI7IGludGVydmFsTXM/OiBudW1iZXI7IHJlYWR5R3JhY2VNcz86IG51bWJlcjsgb25UaWNrPzogKG1zZzogc3RyaW5nKSA9PiB2b2lkIH0gPSB7fSxcbik6IFByb21pc2U8SGVhbHRoUmVzdWx0PiB7XG4gIGNvbnN0IHRpbWVvdXRNcyA9IG9wdHMudGltZW91dE1zID8/IDMwMF8wMDA7IC8vIDUgbWluIOKAlCBzZXJ2aWNlcyBjb21lIHVwIGFzeW5jXG4gIGNvbnN0IGludGVydmFsTXMgPSBvcHRzLmludGVydmFsTXMgPz8gMTBfMDAwO1xuICAvLyBPbmNlIC9oZWFsdGggaXMgdXAsIG9ubHkgd2FpdCB0aGlzIG11Y2ggbG9uZ2VyIGZvciAvcmVhZHkgYmVmb3JlIHByb2NlZWRpbmcuXG4gIGNvbnN0IHJlYWR5R3JhY2VNcyA9IG9wdHMucmVhZHlHcmFjZU1zID8/IDkwXzAwMDsgLy8gOTBzIGNvdmVycyBub3JtYWwgd2FybXVwXG4gIGNvbnN0IGRlYWRsaW5lID0gRGF0ZS5ub3coKSArIHRpbWVvdXRNcztcblxuICAvLyBXYWl0IGZvciAvaGVhbHRoLCB0aGVuIGdpdmUgL3JlYWR5IGEgQk9VTkRFRCBncmFjZSB3aW5kb3cuIC9oZWFsdGggZmxpcHMgZmlyc3RcbiAgLy8gKHRoZSBwcm9jZXNzIGlzIHVwKTsgL3JlYWR5IGFsc28gcmVxdWlyZXMgZXZlcnkgYmFja2VuZCBkZXBlbmRlbmN5IHRvIGJlIHVwLlxuICAvLyBXZSBwcmVmZXIgL3JlYWR5IChwb3N0LWluc3RhbGwgcmVnaXN0ZXIgaGl0cyBhIG5vdC15ZXQtcmVhZHkgcGxhdGZvcm0g4oaSIDUwMiksXG4gIC8vIGJ1dCBpZiBhIGRlcGVuZGVuY3kgaXMgRE9XTiAoZS5nLiBhIGNyYXNoLWxvb3Bpbmcgc2VydmljZSkgL3JlYWR5IG5ldmVyIGZsaXBzIOKAlFxuICAvLyBzbyB3ZSBtdXN0IG5vdCBibG9jayB0aGUgZnVsbCB0aW1lb3V0IG9uIGl0LiBPbmNlIC9oZWFsdGggaXMgdXAgd2Ugd2FpdCBhdCBtb3N0XG4gIC8vIGByZWFkeUdyYWNlTXNgIG1vcmUsIHRoZW4gcHJvY2VlZCB3aXRoIGEgd2FybmluZyAocmVnaXN0ZXIgb25seSBuZWVkcyB0aGVcbiAgLy8gcGxhdGZvcm0gc2VydmljZSwgd2hpY2ggL2hlYWx0aCBhbHJlYWR5IGNvbmZpcm1zIGlzIHVwKS5cbiAgbGV0IGhlYWx0aFNlZW5BdCA9IDA7XG4gIHdoaWxlIChEYXRlLm5vdygpIDwgZGVhZGxpbmUpIHtcbiAgICBpZiAoYXdhaXQgcHJvYmUoYCR7dXJsfS9oZWFsdGhgKSkge1xuICAgICAgaWYgKGhlYWx0aFNlZW5BdCA9PT0gMCkgaGVhbHRoU2VlbkF0ID0gRGF0ZS5ub3coKTtcbiAgICAgIGlmIChhd2FpdCBwcm9iZShgJHt1cmx9L3JlYWR5YCkpIHtcbiAgICAgICAgcmV0dXJuIHsgdXJsLCBoZWFsdGh5OiB0cnVlLCByZWFkeTogdHJ1ZSwgZGV0YWlsOiAnaGVhbHRoICsgcmVhZHkgT0snIH07XG4gICAgICB9XG4gICAgICBjb25zdCBsZWZ0TXMgPSByZWFkeUdyYWNlTXMgLSAoRGF0ZS5ub3coKSAtIGhlYWx0aFNlZW5BdCk7XG4gICAgICBpZiAobGVmdE1zIDw9IDApIHtcbiAgICAgICAgcmV0dXJuIHtcbiAgICAgICAgICB1cmwsXG4gICAgICAgICAgaGVhbHRoeTogdHJ1ZSxcbiAgICAgICAgICByZWFkeTogZmFsc2UsXG4gICAgICAgICAgZGV0YWlsOiBgaGVhbHRoIE9LLCBidXQgL3JlYWR5IGRpZG4ndCBwYXNzIHdpdGhpbiAke01hdGgucm91bmQocmVhZHlHcmFjZU1zIC8gMTAwMCl9cyDigJQgYSBiYWNrZW5kIHNlcnZpY2UgaXMgbGlrZWx5IGRvd24gKGNoZWNrIFxcYGRvY2tlciBjb21wb3NlIHBzXFxgIC8gaXRzIGxvZ3MpLiBDb250aW51aW5nOyByb3V0ZXMgZm9yIHRoYXQgc2VydmljZSBtYXkgNTAyIHVudGlsIGl0J3MgaGVhbHRoeS5gLFxuICAgICAgICB9O1xuICAgICAgfVxuICAgICAgb3B0cy5vblRpY2s/LihgSGVhbHRoIE9LIOKAlCB3YWl0aW5nIHVwIHRvICR7TWF0aC5jZWlsKGxlZnRNcyAvIDEwMDApfXMgZm9yICR7dXJsfS9yZWFkeSAoZGVwZW5kZW5jaWVzIHdhcm1pbmcgdXApIOKApmApO1xuICAgIH0gZWxzZSB7XG4gICAgICBvcHRzLm9uVGljaz8uKGBXYWl0aW5nIGZvciAke3VybH0vaGVhbHRoIOKApmApO1xuICAgIH1cbiAgICBhd2FpdCBkZWxheShpbnRlcnZhbE1zKTtcbiAgfVxuICAvLyBPdmVyYWxsIHRpbWVvdXQuIElmIC9oZWFsdGggY2FtZSB1cCBidXQgL3JlYWR5IG5ldmVyIGRpZCwgcHJvY2VlZCAobm9uLWZhdGFsKS5cbiAgcmV0dXJuIGhlYWx0aFNlZW5BdCA+IDBcbiAgICA/IHsgdXJsLCBoZWFsdGh5OiB0cnVlLCByZWFkeTogZmFsc2UsIGRldGFpbDogJ2hlYWx0aCBPSyBidXQgL3JlYWR5IG5vdCByZWFjaGVkIOKAlCB0aGUgcGxhdGZvcm0gaXMgc3RpbGwgd2FybWluZyB1cDsgcG9zdC1pbnN0YWxsIHN0ZXBzIChyZWdpc3RlcikgbWF5IG5lZWQgYSByZS1ydW4nIH1cbiAgICA6IHsgdXJsLCBoZWFsdGh5OiBmYWxzZSwgcmVhZHk6IGZhbHNlLCBkZXRhaWw6IGBub3QgcmVhY2hhYmxlIHdpdGhpbiAke01hdGgucm91bmQodGltZW91dE1zIC8gMTAwMCl9cyDigJQgY2hlY2sgdGhlIHN0YWNrIC8gRE5TIC8gaGVhbHRoIGxvZ3NgIH07XG59XG5cbi8qKlxuICogTWluaWt1YmUgb25seTogdGhlIGdhdGV3YXkgKGh0dHBzOi8vbG9jYWxob3N0Ojg0NDMpIGlzIHJlYWNoZWQgdGhyb3VnaCBhXG4gKiBga3ViZWN0bCBwb3J0LWZvcndhcmQgc3ZjL25naW54YCB0aGF0IHNldHVwLnNoIGJhY2tncm91bmRzIOKAlCB3aGljaCBjYW4gZGllIG9yXG4gKiBmYWlsIHRvIGJpbmQgKGUuZy4gYSBidXN5IHBvcnQpIHdoaWxlIHRoZSBwb2RzIHN0YXkgaGVhbHRoeSwgbGVhdmluZyB0aGUgZ2F0ZXdheVxuICogdW5yZWFjaGFibGUgc28gdGhlIGhlYWx0aCBwb2xsIGp1c3QgdGltZXMgb3V0LiBCZWZvcmUgcG9sbGluZywgcHJvYmUgdGhlIGdhdGV3YXk7XG4gKiBpZiBpdCdzIGRvd24sIChyZSlzdGFydCB0aGUgZm9yd2FyZCBERVRBQ0hFRCBzbyBpdCBvdXRsaXZlcyB0aGlzIENMSSwgdGhlbiBnaXZlXG4gKiBpdCBhIG1vbWVudCB0byBiaW5kLiBCZXN0LWVmZm9ydDogbmV2ZXIgdGhyb3dzOyBvbiBuby1rdWJlY3RsIGl0IGxvZ3MgdGhlIG1hbnVhbFxuICogY29tbWFuZC4gKHNldHVwLnNoIG5vdyBmb3J3YXJkcyA4NDQzIG9ubHksIHNvIGEgYnVzeSA4MDgwIGNhbid0IHRha2UgaXQgZG93bi4pXG4gKi9cbmV4cG9ydCBhc3luYyBmdW5jdGlvbiBlbnN1cmVNaW5pa3ViZUdhdGV3YXkoXG4gIHVybDogc3RyaW5nLFxuICBvcHRzOiB7IG5zPzogc3RyaW5nOyBwb3J0PzogbnVtYmVyOyBvbkluZm8/OiAobXNnOiBzdHJpbmcpID0+IHZvaWQgfSA9IHt9LFxuKTogUHJvbWlzZTx2b2lkPiB7XG4gIGNvbnN0IG5zID0gb3B0cy5ucyA/PyAncGlwZWxpbmUtYnVpbGRlcic7XG4gIGNvbnN0IHBvcnQgPSBvcHRzLnBvcnQgPz8gODQ0MztcbiAgaWYgKGF3YWl0IHByb2JlKGAke3VybH0vaGVhbHRoYCkpIHJldHVybjsgLy8gZm9yd2FyZCBhbHJlYWR5IHVwIOKAlCBub3RoaW5nIHRvIGRvXG4gIG9wdHMub25JbmZvPy4oYEdhdGV3YXkgdW5yZWFjaGFibGUg4oCUIChyZSlzdGFydGluZyB0aGUgbmdpbnggcG9ydC1mb3J3YXJkIChzdmMvbmdpbnggJHtwb3J0fToke3BvcnR9LCBucyAke25zfSkg4oCmYCk7XG4gIC8vIEEgbWlzc2luZyBrdWJlY3RsIHN1cmZhY2VzIGFzIGFuIGFzeW5jICdlcnJvcicgZXZlbnQgKEVOT0VOVCksIE5PVCBhIHN5bmNocm9ub3VzXG4gIC8vIHRocm93IOKAlCBhIHRyeS9jYXRjaCB3b3VsZCBtaXNzIGl0IGFuZCB0aGUgdW5oYW5kbGVkIGV2ZW50IHdvdWxkIGNyYXNoIHRoZSBDTEkuIEF0dGFjaFxuICAvLyBhbiAnZXJyb3InIGxpc3RlbmVyIHNvIHRoaXMgc3RheXMgYmVzdC1lZmZvcnQgKGxvZ3MgdGhlIG1hbnVhbCBjb21tYW5kLCBuZXZlciB0aHJvd3MpLlxuICBjb25zdCBjaGlsZCA9IHNwYXduKCdrdWJlY3RsJywgWydwb3J0LWZvcndhcmQnLCAnLW4nLCBucywgJ3N2Yy9uZ2lueCcsIGAke3BvcnR9OiR7cG9ydH1gXSwge1xuICAgIGRldGFjaGVkOiB0cnVlLFxuICAgIHN0ZGlvOiAnaWdub3JlJyxcbiAgfSk7XG4gIGNoaWxkLm9uKCdlcnJvcicsICgpID0+IHtcbiAgICBvcHRzLm9uSW5mbz8uKGBDb3VsZG4ndCBzdGFydCBpdCAoaXMga3ViZWN0bCBvbiBQQVRIPykuIFN0YXJ0IGl0IG1hbnVhbGx5OiBrdWJlY3RsIHBvcnQtZm9yd2FyZCAtbiAke25zfSBzdmMvbmdpbnggJHtwb3J0fToke3BvcnR9YCk7XG4gIH0pO1xuICBjaGlsZC51bnJlZigpOyAvLyBkZXRhY2hlZCArIHVucmVmIOKGkiBzdXJ2aXZlcyB0aGlzIENMSSBzbyB0aGUgdXNlciBrZWVwcyB0aGUgZ2F0ZXdheVxuICBhd2FpdCBkZWxheSgyMDAwKTsgLy8gbGV0IGl0IGJpbmQgYmVmb3JlIHRoZSBjYWxsZXIgcG9sbHNcbn1cbiJdfQ==
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import type { TargetId, TargetSpec } from './targets.js';
|
|
2
|
+
export interface HostPort {
|
|
3
|
+
readonly service: string;
|
|
4
|
+
readonly port: number;
|
|
5
|
+
}
|
|
6
|
+
export interface PortCheck extends HostPort {
|
|
7
|
+
readonly available: boolean;
|
|
8
|
+
}
|
|
9
|
+
/**
|
|
10
|
+
* Derive the host ports a target binds from its ACTUAL (cloned) deploy source, so the
|
|
11
|
+
* list can't drift from the deploy. local → docker-compose.yml; minikube → setup.sh;
|
|
12
|
+
* ec2/eks → none (CloudFormation binds nothing on the operator's machine). Needs
|
|
13
|
+
* the deploy files on disk (run post-clone); falls back to the target's static
|
|
14
|
+
* `hostPorts` if the source is missing/unreadable.
|
|
15
|
+
*/
|
|
16
|
+
export declare function discoverHostPorts(target: TargetId, cwd: string, spec: TargetSpec): HostPort[];
|
|
17
|
+
/** Probe each host port; returns one PortCheck per port (probed in parallel). */
|
|
18
|
+
export declare function checkHostPorts(ports: readonly HostPort[]): Promise<PortCheck[]>;
|
|
19
|
+
/**
|
|
20
|
+
* Is the target's OWN deploy stack already running? (local docker-compose only.) Used
|
|
21
|
+
* so the port pre-flight doesn't block an idempotent RE-RUN: if the "taken" ports are
|
|
22
|
+
* held by the stack you already have up, `docker compose up` just no-ops them — that's
|
|
23
|
+
* not a conflict. Read-only `docker compose ps`; false on any error / non-local target.
|
|
24
|
+
*/
|
|
25
|
+
export declare function stackRunning(target: TargetId, cwd: string, spec: TargetSpec): boolean;
|
|
26
|
+
//# sourceMappingURL=ports.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ports.d.ts","sourceRoot":"","sources":["../../src/agent/ports.ts"],"names":[],"mappings":"AAuBA,OAAO,KAAK,EAAE,QAAQ,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;AAEzD,MAAM,WAAW,QAAQ;IACvB,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;CACvB;AAED,MAAM,WAAW,SAAU,SAAQ,QAAQ;IACzC,QAAQ,CAAC,SAAS,EAAE,OAAO,CAAC;CAC7B;AAqED;;;;;;GAMG;AACH,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,QAAQ,EAAE,GAAG,EAAE,MAAM,EAAE,IAAI,EAAE,UAAU,GAAG,QAAQ,EAAE,CAQ7F;AAED,iFAAiF;AACjF,wBAAsB,cAAc,CAAC,KAAK,EAAE,SAAS,QAAQ,EAAE,GAAG,OAAO,CAAC,SAAS,EAAE,CAAC,CAErF;AAED;;;;;GAKG;AACH,wBAAgB,YAAY,CAAC,MAAM,EAAE,QAAQ,EAAE,GAAG,EAAE,MAAM,EAAE,IAAI,EAAE,UAAU,GAAG,OAAO,CAWrF"}
|
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
// Copyright 2026 Pipeline Builder Contributors
|
|
2
|
+
// SPDX-License-Identifier: Apache-2.0
|
|
3
|
+
/**
|
|
4
|
+
* Host-port pre-flight. Local (Docker-published) and minikube (kubectl port-forward)
|
|
5
|
+
* deploys bind fixed ports on the operator's machine; if one is already taken, the
|
|
6
|
+
* container/forward fails to bind — and for the gateway that surfaces as a silent
|
|
7
|
+
* "/health never reachable" hang. We probe the ports before deploying so provision
|
|
8
|
+
* can stop with a clear summary instead of failing mid-deploy.
|
|
9
|
+
*
|
|
10
|
+
* The port list is DERIVED from each target's actual (cloned) deploy source — so it
|
|
11
|
+
* can't drift from the deploy: local from `docker-compose.yml`'s published ports,
|
|
12
|
+
* minikube from `setup.sh`'s kubectl port-forwards. ec2/eks deploy via
|
|
13
|
+
* CloudFormation and bind NOTHING on the operator's machine (only the remote ALB),
|
|
14
|
+
* so they yield no host ports. `targets.hostPorts` is only a fallback when the source
|
|
15
|
+
* file can't be read.
|
|
16
|
+
*/
|
|
17
|
+
import { execSync } from 'child_process';
|
|
18
|
+
import { readFileSync } from 'fs';
|
|
19
|
+
import net from 'net';
|
|
20
|
+
import path from 'path';
|
|
21
|
+
import { parse as parseYaml } from 'yaml';
|
|
22
|
+
/** Try to bind <host>:<port>; true if free, false on EADDRINUSE/EACCES. */
|
|
23
|
+
function canBind(port, host) {
|
|
24
|
+
return new Promise((resolve) => {
|
|
25
|
+
const srv = net.createServer();
|
|
26
|
+
srv.once('error', () => resolve(false));
|
|
27
|
+
srv.once('listening', () => srv.close(() => resolve(true)));
|
|
28
|
+
srv.listen(port, host);
|
|
29
|
+
});
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* Is `port` free? Probes BOTH `0.0.0.0` (where Docker publishes `0.0.0.0:<port>`)
|
|
33
|
+
* AND `127.0.0.1` (where kubectl port-forward / Docker Desktop bind), requiring both.
|
|
34
|
+
*
|
|
35
|
+
* This is NOT redundant — do not "simplify" to one bind. Verified empirically on
|
|
36
|
+
* macOS (Node sets SO_REUSEADDR): neither interface subsumes the other. With a holder
|
|
37
|
+
* on 127.0.0.1, binding 0.0.0.0 still SUCCEEDS (a single 0.0.0.0 probe would MISS a
|
|
38
|
+
* port-forward squatting the gateway on loopback); with a holder on 0.0.0.0, binding
|
|
39
|
+
* 127.0.0.1 still succeeds. Only checking both reliably catches the real conflict (a
|
|
40
|
+
* port-forward on 8443, AirPlay on 5000, …). For a pre-flight, a false positive
|
|
41
|
+
* (over-cautious) beats a false negative (miss it → the deploy fails mid-way).
|
|
42
|
+
*/
|
|
43
|
+
async function isPortFree(port) {
|
|
44
|
+
const [allIfaces, loopback] = await Promise.all([canBind(port, '0.0.0.0'), canBind(port, '127.0.0.1')]);
|
|
45
|
+
return allIfaces && loopback;
|
|
46
|
+
}
|
|
47
|
+
/**
|
|
48
|
+
* Host (left-side) ports a docker-compose.yml publishes, keyed by service. Handles
|
|
49
|
+
* the short forms "HOST:CONTAINER", "HOST:CONTAINER/proto", "IP:HOST:CONTAINER" and
|
|
50
|
+
* the long form `{ published, target }`. Bare "CONTAINER" (random host port) is
|
|
51
|
+
* skipped — it can't conflict on a fixed port.
|
|
52
|
+
*/
|
|
53
|
+
function composeHostPorts(file) {
|
|
54
|
+
const doc = parseYaml(readFileSync(file, 'utf8'));
|
|
55
|
+
const out = [];
|
|
56
|
+
for (const [service, def] of Object.entries(doc?.services ?? {})) {
|
|
57
|
+
for (const entry of def?.ports ?? []) {
|
|
58
|
+
let host;
|
|
59
|
+
if (typeof entry === 'string') {
|
|
60
|
+
const parts = (entry.split('/')[0] ?? '').split(':'); // strip /proto, split ip:host:container
|
|
61
|
+
if (parts.length >= 2)
|
|
62
|
+
host = Number(parts[parts.length - 2] ?? ''); // host is second-from-last
|
|
63
|
+
}
|
|
64
|
+
else if (entry && typeof entry === 'object' && 'published' in entry) {
|
|
65
|
+
host = Number(entry.published);
|
|
66
|
+
}
|
|
67
|
+
if (host !== undefined && Number.isInteger(host))
|
|
68
|
+
out.push({ service, port: host });
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
return out;
|
|
72
|
+
}
|
|
73
|
+
/** Host ports from minikube setup.sh's `port_forward "Name" svc "HOST:CONTAINER …"` calls. */
|
|
74
|
+
function forwardHostPorts(file) {
|
|
75
|
+
const out = [];
|
|
76
|
+
const re = /port_forward\s+"([^"]+)"\s+\S+\s+"([^"]+)"/g;
|
|
77
|
+
const text = readFileSync(file, 'utf8');
|
|
78
|
+
let m;
|
|
79
|
+
while ((m = re.exec(text)) !== null) {
|
|
80
|
+
const service = m[1] ?? '';
|
|
81
|
+
for (const pair of (m[2] ?? '').trim().split(/\s+/)) {
|
|
82
|
+
const host = Number(pair.split(':')[0] ?? '');
|
|
83
|
+
if (Number.isInteger(host))
|
|
84
|
+
out.push({ service, port: host });
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
return out;
|
|
88
|
+
}
|
|
89
|
+
/**
|
|
90
|
+
* Derive the host ports a target binds from its ACTUAL (cloned) deploy source, so the
|
|
91
|
+
* list can't drift from the deploy. local → docker-compose.yml; minikube → setup.sh;
|
|
92
|
+
* ec2/eks → none (CloudFormation binds nothing on the operator's machine). Needs
|
|
93
|
+
* the deploy files on disk (run post-clone); falls back to the target's static
|
|
94
|
+
* `hostPorts` if the source is missing/unreadable.
|
|
95
|
+
*/
|
|
96
|
+
export function discoverHostPorts(target, cwd, spec) {
|
|
97
|
+
try {
|
|
98
|
+
if (target === 'docker')
|
|
99
|
+
return composeHostPorts(path.join(cwd, spec.dir, 'docker-compose.yml'));
|
|
100
|
+
if (target === 'minikube')
|
|
101
|
+
return forwardHostPorts(path.join(cwd, spec.dir, 'bin', 'setup.sh'));
|
|
102
|
+
return []; // ec2/eks deploy remotely — nothing binds locally
|
|
103
|
+
}
|
|
104
|
+
catch {
|
|
105
|
+
return spec.hostPorts.map((p) => ({ ...p }));
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
/** Probe each host port; returns one PortCheck per port (probed in parallel). */
|
|
109
|
+
export async function checkHostPorts(ports) {
|
|
110
|
+
return Promise.all(ports.map(async ({ service, port }) => ({ service, port, available: await isPortFree(port) })));
|
|
111
|
+
}
|
|
112
|
+
/**
|
|
113
|
+
* Is the target's OWN deploy stack already running? (local docker-compose only.) Used
|
|
114
|
+
* so the port pre-flight doesn't block an idempotent RE-RUN: if the "taken" ports are
|
|
115
|
+
* held by the stack you already have up, `docker compose up` just no-ops them — that's
|
|
116
|
+
* not a conflict. Read-only `docker compose ps`; false on any error / non-local target.
|
|
117
|
+
*/
|
|
118
|
+
export function stackRunning(target, cwd, spec) {
|
|
119
|
+
if (target !== 'docker')
|
|
120
|
+
return false;
|
|
121
|
+
try {
|
|
122
|
+
const compose = path.join(cwd, spec.dir, 'docker-compose.yml');
|
|
123
|
+
const out = execSync(`docker compose -f '${compose}' ps -q`, { stdio: ['ignore', 'pipe', 'ignore'], timeout: 10000 })
|
|
124
|
+
.toString()
|
|
125
|
+
.trim();
|
|
126
|
+
return out.length > 0;
|
|
127
|
+
}
|
|
128
|
+
catch {
|
|
129
|
+
return false;
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoicG9ydHMuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi9zcmMvYWdlbnQvcG9ydHMudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUEsK0NBQStDO0FBQy9DLHNDQUFzQztBQUV0Qzs7Ozs7Ozs7Ozs7OztHQWFHO0FBRUgsT0FBTyxFQUFFLFFBQVEsRUFBRSxNQUFNLGVBQWUsQ0FBQztBQUN6QyxPQUFPLEVBQUUsWUFBWSxFQUFFLE1BQU0sSUFBSSxDQUFDO0FBQ2xDLE9BQU8sR0FBRyxNQUFNLEtBQUssQ0FBQztBQUN0QixPQUFPLElBQUksTUFBTSxNQUFNLENBQUM7QUFDeEIsT0FBTyxFQUFFLEtBQUssSUFBSSxTQUFTLEVBQUUsTUFBTSxNQUFNLENBQUM7QUFZMUMsMkVBQTJFO0FBQzNFLFNBQVMsT0FBTyxDQUFDLElBQVksRUFBRSxJQUFZO0lBQ3pDLE9BQU8sSUFBSSxPQUFPLENBQUMsQ0FBQyxPQUFPLEVBQUUsRUFBRTtRQUM3QixNQUFNLEdBQUcsR0FBRyxHQUFHLENBQUMsWUFBWSxFQUFFLENBQUM7UUFDL0IsR0FBRyxDQUFDLElBQUksQ0FBQyxPQUFPLEVBQUUsR0FBRyxFQUFFLENBQUMsT0FBTyxDQUFDLEtBQUssQ0FBQyxDQUFDLENBQUM7UUFDeEMsR0FBRyxDQUFDLElBQUksQ0FBQyxXQUFXLEVBQUUsR0FBRyxFQUFFLENBQUMsR0FBRyxDQUFDLEtBQUssQ0FBQyxHQUFHLEVBQUUsQ0FBQyxPQUFPLENBQUMsSUFBSSxDQUFDLENBQUMsQ0FBQyxDQUFDO1FBQzVELEdBQUcsQ0FBQyxNQUFNLENBQUMsSUFBSSxFQUFFLElBQUksQ0FBQyxDQUFDO0lBQ3pCLENBQUMsQ0FBQyxDQUFDO0FBQ0wsQ0FBQztBQUVEOzs7Ozs7Ozs7OztHQVdHO0FBQ0gsS0FBSyxVQUFVLFVBQVUsQ0FBQyxJQUFZO0lBQ3BDLE1BQU0sQ0FBQyxTQUFTLEVBQUUsUUFBUSxDQUFDLEdBQUcsTUFBTSxPQUFPLENBQUMsR0FBRyxDQUFDLENBQUMsT0FBTyxDQUFDLElBQUksRUFBRSxTQUFTLENBQUMsRUFBRSxPQUFPLENBQUMsSUFBSSxFQUFFLFdBQVcsQ0FBQyxDQUFDLENBQUMsQ0FBQztJQUN4RyxPQUFPLFNBQVMsSUFBSSxRQUFRLENBQUM7QUFDL0IsQ0FBQztBQUVEOzs7OztHQUtHO0FBQ0gsU0FBUyxnQkFBZ0IsQ0FBQyxJQUFZO0lBQ3BDLE1BQU0sR0FBRyxHQUFHLFNBQVMsQ0FBQyxZQUFZLENBQUMsSUFBSSxFQUFFLE1BQU0sQ0FBQyxDQUF5RCxDQUFDO0lBQzFHLE1BQU0sR0FBRyxHQUFlLEVBQUUsQ0FBQztJQUMzQixLQUFLLE1BQU0sQ0FBQyxPQUFPLEVBQUUsR0FBRyxDQUFDLElBQUksTUFBTSxDQUFDLE9BQU8sQ0FBQyxHQUFHLEVBQUUsUUFBUSxJQUFJLEVBQUUsQ0FBQyxFQUFFLENBQUM7UUFDakUsS0FBSyxNQUFNLEtBQUssSUFBSSxHQUFHLEVBQUUsS0FBSyxJQUFJLEVBQUUsRUFBRSxDQUFDO1lBQ3JDLElBQUksSUFBd0IsQ0FBQztZQUM3QixJQUFJLE9BQU8sS0FBSyxLQUFLLFFBQVEsRUFBRSxDQUFDO2dCQUM5QixNQUFNLEtBQUssR0FBRyxDQUFDLEtBQUssQ0FBQyxLQUFLLENBQUMsR0FBRyxDQUFDLENBQUMsQ0FBQyxDQUFDLElBQUksRUFBRSxDQUFDLENBQUMsS0FBSyxDQUFDLEdBQUcsQ0FBQyxDQUFDLENBQUMsd0NBQXdDO2dCQUM5RixJQUFJLEtBQUssQ0FBQyxNQUFNLElBQUksQ0FBQztvQkFBRSxJQUFJLEdBQUcsTUFBTSxDQUFDLEtBQUssQ0FBQyxLQUFLLENBQUMsTUFBTSxHQUFHLENBQUMsQ0FBQyxJQUFJLEVBQUUsQ0FBQyxDQUFDLENBQUMsMkJBQTJCO1lBQ2xHLENBQUM7aUJBQU0sSUFBSSxLQUFLLElBQUksT0FBTyxLQUFLLEtBQUssUUFBUSxJQUFJLFdBQVcsSUFBSSxLQUFLLEVBQUUsQ0FBQztnQkFDdEUsSUFBSSxHQUFHLE1BQU0sQ0FBRSxLQUFnQyxDQUFDLFNBQVMsQ0FBQyxDQUFDO1lBQzdELENBQUM7WUFDRCxJQUFJLElBQUksS0FBSyxTQUFTLElBQUksTUFBTSxDQUFDLFNBQVMsQ0FBQyxJQUFJLENBQUM7Z0JBQUUsR0FBRyxDQUFDLElBQUksQ0FBQyxFQUFFLE9BQU8sRUFBRSxJQUFJLEVBQUUsSUFBSSxFQUFFLENBQUMsQ0FBQztRQUN0RixDQUFDO0lBQ0gsQ0FBQztJQUNELE9BQU8sR0FBRyxDQUFDO0FBQ2IsQ0FBQztBQUVELDhGQUE4RjtBQUM5RixTQUFTLGdCQUFnQixDQUFDLElBQVk7SUFDcEMsTUFBTSxHQUFHLEdBQWUsRUFBRSxDQUFDO0lBQzNCLE1BQU0sRUFBRSxHQUFHLDZDQUE2QyxDQUFDO0lBQ3pELE1BQU0sSUFBSSxHQUFHLFlBQVksQ0FBQyxJQUFJLEVBQUUsTUFBTSxDQUFDLENBQUM7SUFDeEMsSUFBSSxDQUF5QixDQUFDO0lBQzlCLE9BQU8sQ0FBQyxDQUFDLEdBQUcsRUFBRSxDQUFDLElBQUksQ0FBQyxJQUFJLENBQUMsQ0FBQyxLQUFLLElBQUksRUFBRSxDQUFDO1FBQ3BDLE1BQU0sT0FBTyxHQUFHLENBQUMsQ0FBQyxDQUFDLENBQUMsSUFBSSxFQUFFLENBQUM7UUFDM0IsS0FBSyxNQUFNLElBQUksSUFBSSxDQUFDLENBQUMsQ0FBQyxDQUFDLENBQUMsSUFBSSxFQUFFLENBQUMsQ0FBQyxJQUFJLEVBQUUsQ0FBQyxLQUFLLENBQUMsS0FBSyxDQUFDLEVBQUUsQ0FBQztZQUNwRCxNQUFNLElBQUksR0FBRyxNQUFNLENBQUMsSUFBSSxDQUFDLEtBQUssQ0FBQyxHQUFHLENBQUMsQ0FBQyxDQUFDLENBQUMsSUFBSSxFQUFFLENBQUMsQ0FBQztZQUM5QyxJQUFJLE1BQU0sQ0FBQyxTQUFTLENBQUMsSUFBSSxDQUFDO2dCQUFFLEdBQUcsQ0FBQyxJQUFJLENBQUMsRUFBRSxPQUFPLEVBQUUsSUFBSSxFQUFFLElBQUksRUFBRSxDQUFDLENBQUM7UUFDaEUsQ0FBQztJQUNILENBQUM7SUFDRCxPQUFPLEdBQUcsQ0FBQztBQUNiLENBQUM7QUFFRDs7Ozs7O0dBTUc7QUFDSCxNQUFNLFVBQVUsaUJBQWlCLENBQUMsTUFBZ0IsRUFBRSxHQUFXLEVBQUUsSUFBZ0I7SUFDL0UsSUFBSSxDQUFDO1FBQ0gsSUFBSSxNQUFNLEtBQUssUUFBUTtZQUFFLE9BQU8sZ0JBQWdCLENBQUMsSUFBSSxDQUFDLElBQUksQ0FBQyxHQUFHLEVBQUUsSUFBSSxDQUFDLEdBQUcsRUFBRSxvQkFBb0IsQ0FBQyxDQUFDLENBQUM7UUFDakcsSUFBSSxNQUFNLEtBQUssVUFBVTtZQUFFLE9BQU8sZ0JBQWdCLENBQUMsSUFBSSxDQUFDLElBQUksQ0FBQyxHQUFHLEVBQUUsSUFBSSxDQUFDLEdBQUcsRUFBRSxLQUFLLEVBQUUsVUFBVSxDQUFDLENBQUMsQ0FBQztRQUNoRyxPQUFPLEVBQUUsQ0FBQyxDQUFDLGtEQUFrRDtJQUMvRCxDQUFDO0lBQUMsTUFBTSxDQUFDO1FBQ1AsT0FBTyxJQUFJLENBQUMsU0FBUyxDQUFDLEdBQUcsQ0FBQyxDQUFDLENBQUMsRUFBRSxFQUFFLENBQUMsQ0FBQyxFQUFFLEdBQUcsQ0FBQyxFQUFFLENBQUMsQ0FBQyxDQUFDO0lBQy9DLENBQUM7QUFDSCxDQUFDO0FBRUQsaUZBQWlGO0FBQ2pGLE1BQU0sQ0FBQyxLQUFLLFVBQVUsY0FBYyxDQUFDLEtBQTBCO0lBQzdELE9BQU8sT0FBTyxDQUFDLEdBQUcsQ0FBQyxLQUFLLENBQUMsR0FBRyxDQUFDLEtBQUssRUFBRSxFQUFFLE9BQU8sRUFBRSxJQUFJLEVBQUUsRUFBRSxFQUFFLENBQUMsQ0FBQyxFQUFFLE9BQU8sRUFBRSxJQUFJLEVBQUUsU0FBUyxFQUFFLE1BQU0sVUFBVSxDQUFDLElBQUksQ0FBQyxFQUFFLENBQUMsQ0FBQyxDQUFDLENBQUM7QUFDckgsQ0FBQztBQUVEOzs7OztHQUtHO0FBQ0gsTUFBTSxVQUFVLFlBQVksQ0FBQyxNQUFnQixFQUFFLEdBQVcsRUFBRSxJQUFnQjtJQUMxRSxJQUFJLE1BQU0sS0FBSyxRQUFRO1FBQUUsT0FBTyxLQUFLLENBQUM7SUFDdEMsSUFBSSxDQUFDO1FBQ0gsTUFBTSxPQUFPLEdBQUcsSUFBSSxDQUFDLElBQUksQ0FBQyxHQUFHLEVBQUUsSUFBSSxDQUFDLEdBQUcsRUFBRSxvQkFBb0IsQ0FBQyxDQUFDO1FBQy9ELE1BQU0sR0FBRyxHQUFHLFFBQVEsQ0FBQyxzQkFBc0IsT0FBTyxTQUFTLEVBQUUsRUFBRSxLQUFLLEVBQUUsQ0FBQyxRQUFRLEVBQUUsTUFBTSxFQUFFLFFBQVEsQ0FBQyxFQUFFLE9BQU8sRUFBRSxLQUFLLEVBQUUsQ0FBQzthQUNsSCxRQUFRLEVBQUU7YUFDVixJQUFJLEVBQUUsQ0FBQztRQUNWLE9BQU8sR0FBRyxDQUFDLE1BQU0sR0FBRyxDQUFDLENBQUM7SUFDeEIsQ0FBQztJQUFDLE1BQU0sQ0FBQztRQUNQLE9BQU8sS0FBSyxDQUFDO0lBQ2YsQ0FBQztBQUNILENBQUMiLCJzb3VyY2VzQ29udGVudCI6WyIvLyBDb3B5cmlnaHQgMjAyNiBQaXBlbGluZSBCdWlsZGVyIENvbnRyaWJ1dG9yc1xuLy8gU1BEWC1MaWNlbnNlLUlkZW50aWZpZXI6IEFwYWNoZS0yLjBcblxuLyoqXG4gKiBIb3N0LXBvcnQgcHJlLWZsaWdodC4gTG9jYWwgKERvY2tlci1wdWJsaXNoZWQpIGFuZCBtaW5pa3ViZSAoa3ViZWN0bCBwb3J0LWZvcndhcmQpXG4gKiBkZXBsb3lzIGJpbmQgZml4ZWQgcG9ydHMgb24gdGhlIG9wZXJhdG9yJ3MgbWFjaGluZTsgaWYgb25lIGlzIGFscmVhZHkgdGFrZW4sIHRoZVxuICogY29udGFpbmVyL2ZvcndhcmQgZmFpbHMgdG8gYmluZCDigJQgYW5kIGZvciB0aGUgZ2F0ZXdheSB0aGF0IHN1cmZhY2VzIGFzIGEgc2lsZW50XG4gKiBcIi9oZWFsdGggbmV2ZXIgcmVhY2hhYmxlXCIgaGFuZy4gV2UgcHJvYmUgdGhlIHBvcnRzIGJlZm9yZSBkZXBsb3lpbmcgc28gcHJvdmlzaW9uXG4gKiBjYW4gc3RvcCB3aXRoIGEgY2xlYXIgc3VtbWFyeSBpbnN0ZWFkIG9mIGZhaWxpbmcgbWlkLWRlcGxveS5cbiAqXG4gKiBUaGUgcG9ydCBsaXN0IGlzIERFUklWRUQgZnJvbSBlYWNoIHRhcmdldCdzIGFjdHVhbCAoY2xvbmVkKSBkZXBsb3kgc291cmNlIOKAlCBzbyBpdFxuICogY2FuJ3QgZHJpZnQgZnJvbSB0aGUgZGVwbG95OiBsb2NhbCBmcm9tIGBkb2NrZXItY29tcG9zZS55bWxgJ3MgcHVibGlzaGVkIHBvcnRzLFxuICogbWluaWt1YmUgZnJvbSBgc2V0dXAuc2hgJ3Mga3ViZWN0bCBwb3J0LWZvcndhcmRzLiBlYzIvZWtzIGRlcGxveSB2aWFcbiAqIENsb3VkRm9ybWF0aW9uIGFuZCBiaW5kIE5PVEhJTkcgb24gdGhlIG9wZXJhdG9yJ3MgbWFjaGluZSAob25seSB0aGUgcmVtb3RlIEFMQiksXG4gKiBzbyB0aGV5IHlpZWxkIG5vIGhvc3QgcG9ydHMuIGB0YXJnZXRzLmhvc3RQb3J0c2AgaXMgb25seSBhIGZhbGxiYWNrIHdoZW4gdGhlIHNvdXJjZVxuICogZmlsZSBjYW4ndCBiZSByZWFkLlxuICovXG5cbmltcG9ydCB7IGV4ZWNTeW5jIH0gZnJvbSAnY2hpbGRfcHJvY2Vzcyc7XG5pbXBvcnQgeyByZWFkRmlsZVN5bmMgfSBmcm9tICdmcyc7XG5pbXBvcnQgbmV0IGZyb20gJ25ldCc7XG5pbXBvcnQgcGF0aCBmcm9tICdwYXRoJztcbmltcG9ydCB7IHBhcnNlIGFzIHBhcnNlWWFtbCB9IGZyb20gJ3lhbWwnO1xuaW1wb3J0IHR5cGUgeyBUYXJnZXRJZCwgVGFyZ2V0U3BlYyB9IGZyb20gJy4vdGFyZ2V0cy5qcyc7XG5cbmV4cG9ydCBpbnRlcmZhY2UgSG9zdFBvcnQge1xuICByZWFkb25seSBzZXJ2aWNlOiBzdHJpbmc7XG4gIHJlYWRvbmx5IHBvcnQ6IG51bWJlcjtcbn1cblxuZXhwb3J0IGludGVyZmFjZSBQb3J0Q2hlY2sgZXh0ZW5kcyBIb3N0UG9ydCB7XG4gIHJlYWRvbmx5IGF2YWlsYWJsZTogYm9vbGVhbjtcbn1cblxuLyoqIFRyeSB0byBiaW5kIDxob3N0Pjo8cG9ydD47IHRydWUgaWYgZnJlZSwgZmFsc2Ugb24gRUFERFJJTlVTRS9FQUNDRVMuICovXG5mdW5jdGlvbiBjYW5CaW5kKHBvcnQ6IG51bWJlciwgaG9zdDogc3RyaW5nKTogUHJvbWlzZTxib29sZWFuPiB7XG4gIHJldHVybiBuZXcgUHJvbWlzZSgocmVzb2x2ZSkgPT4ge1xuICAgIGNvbnN0IHNydiA9IG5ldC5jcmVhdGVTZXJ2ZXIoKTtcbiAgICBzcnYub25jZSgnZXJyb3InLCAoKSA9PiByZXNvbHZlKGZhbHNlKSk7XG4gICAgc3J2Lm9uY2UoJ2xpc3RlbmluZycsICgpID0+IHNydi5jbG9zZSgoKSA9PiByZXNvbHZlKHRydWUpKSk7XG4gICAgc3J2Lmxpc3Rlbihwb3J0LCBob3N0KTtcbiAgfSk7XG59XG5cbi8qKlxuICogSXMgYHBvcnRgIGZyZWU/IFByb2JlcyBCT1RIIGAwLjAuMC4wYCAod2hlcmUgRG9ja2VyIHB1Ymxpc2hlcyBgMC4wLjAuMDo8cG9ydD5gKVxuICogQU5EIGAxMjcuMC4wLjFgICh3aGVyZSBrdWJlY3RsIHBvcnQtZm9yd2FyZCAvIERvY2tlciBEZXNrdG9wIGJpbmQpLCByZXF1aXJpbmcgYm90aC5cbiAqXG4gKiBUaGlzIGlzIE5PVCByZWR1bmRhbnQg4oCUIGRvIG5vdCBcInNpbXBsaWZ5XCIgdG8gb25lIGJpbmQuIFZlcmlmaWVkIGVtcGlyaWNhbGx5IG9uXG4gKiBtYWNPUyAoTm9kZSBzZXRzIFNPX1JFVVNFQUREUik6IG5laXRoZXIgaW50ZXJmYWNlIHN1YnN1bWVzIHRoZSBvdGhlci4gV2l0aCBhIGhvbGRlclxuICogb24gMTI3LjAuMC4xLCBiaW5kaW5nIDAuMC4wLjAgc3RpbGwgU1VDQ0VFRFMgKGEgc2luZ2xlIDAuMC4wLjAgcHJvYmUgd291bGQgTUlTUyBhXG4gKiBwb3J0LWZvcndhcmQgc3F1YXR0aW5nIHRoZSBnYXRld2F5IG9uIGxvb3BiYWNrKTsgd2l0aCBhIGhvbGRlciBvbiAwLjAuMC4wLCBiaW5kaW5nXG4gKiAxMjcuMC4wLjEgc3RpbGwgc3VjY2VlZHMuIE9ubHkgY2hlY2tpbmcgYm90aCByZWxpYWJseSBjYXRjaGVzIHRoZSByZWFsIGNvbmZsaWN0IChhXG4gKiBwb3J0LWZvcndhcmQgb24gODQ0MywgQWlyUGxheSBvbiA1MDAwLCDigKYpLiBGb3IgYSBwcmUtZmxpZ2h0LCBhIGZhbHNlIHBvc2l0aXZlXG4gKiAob3Zlci1jYXV0aW91cykgYmVhdHMgYSBmYWxzZSBuZWdhdGl2ZSAobWlzcyBpdCDihpIgdGhlIGRlcGxveSBmYWlscyBtaWQtd2F5KS5cbiAqL1xuYXN5bmMgZnVuY3Rpb24gaXNQb3J0RnJlZShwb3J0OiBudW1iZXIpOiBQcm9taXNlPGJvb2xlYW4+IHtcbiAgY29uc3QgW2FsbElmYWNlcywgbG9vcGJhY2tdID0gYXdhaXQgUHJvbWlzZS5hbGwoW2NhbkJpbmQocG9ydCwgJzAuMC4wLjAnKSwgY2FuQmluZChwb3J0LCAnMTI3LjAuMC4xJyldKTtcbiAgcmV0dXJuIGFsbElmYWNlcyAmJiBsb29wYmFjaztcbn1cblxuLyoqXG4gKiBIb3N0IChsZWZ0LXNpZGUpIHBvcnRzIGEgZG9ja2VyLWNvbXBvc2UueW1sIHB1Ymxpc2hlcywga2V5ZWQgYnkgc2VydmljZS4gSGFuZGxlc1xuICogdGhlIHNob3J0IGZvcm1zIFwiSE9TVDpDT05UQUlORVJcIiwgXCJIT1NUOkNPTlRBSU5FUi9wcm90b1wiLCBcIklQOkhPU1Q6Q09OVEFJTkVSXCIgYW5kXG4gKiB0aGUgbG9uZyBmb3JtIGB7IHB1Ymxpc2hlZCwgdGFyZ2V0IH1gLiBCYXJlIFwiQ09OVEFJTkVSXCIgKHJhbmRvbSBob3N0IHBvcnQpIGlzXG4gKiBza2lwcGVkIOKAlCBpdCBjYW4ndCBjb25mbGljdCBvbiBhIGZpeGVkIHBvcnQuXG4gKi9cbmZ1bmN0aW9uIGNvbXBvc2VIb3N0UG9ydHMoZmlsZTogc3RyaW5nKTogSG9zdFBvcnRbXSB7XG4gIGNvbnN0IGRvYyA9IHBhcnNlWWFtbChyZWFkRmlsZVN5bmMoZmlsZSwgJ3V0ZjgnKSkgYXMgeyBzZXJ2aWNlcz86IFJlY29yZDxzdHJpbmcsIHsgcG9ydHM/OiB1bmtub3duW10gfT4gfTtcbiAgY29uc3Qgb3V0OiBIb3N0UG9ydFtdID0gW107XG4gIGZvciAoY29uc3QgW3NlcnZpY2UsIGRlZl0gb2YgT2JqZWN0LmVudHJpZXMoZG9jPy5zZXJ2aWNlcyA/PyB7fSkpIHtcbiAgICBmb3IgKGNvbnN0IGVudHJ5IG9mIGRlZj8ucG9ydHMgPz8gW10pIHtcbiAgICAgIGxldCBob3N0OiBudW1iZXIgfCB1bmRlZmluZWQ7XG4gICAgICBpZiAodHlwZW9mIGVudHJ5ID09PSAnc3RyaW5nJykge1xuICAgICAgICBjb25zdCBwYXJ0cyA9IChlbnRyeS5zcGxpdCgnLycpWzBdID8/ICcnKS5zcGxpdCgnOicpOyAvLyBzdHJpcCAvcHJvdG8sIHNwbGl0IGlwOmhvc3Q6Y29udGFpbmVyXG4gICAgICAgIGlmIChwYXJ0cy5sZW5ndGggPj0gMikgaG9zdCA9IE51bWJlcihwYXJ0c1twYXJ0cy5sZW5ndGggLSAyXSA/PyAnJyk7IC8vIGhvc3QgaXMgc2Vjb25kLWZyb20tbGFzdFxuICAgICAgfSBlbHNlIGlmIChlbnRyeSAmJiB0eXBlb2YgZW50cnkgPT09ICdvYmplY3QnICYmICdwdWJsaXNoZWQnIGluIGVudHJ5KSB7XG4gICAgICAgIGhvc3QgPSBOdW1iZXIoKGVudHJ5IGFzIHsgcHVibGlzaGVkOiB1bmtub3duIH0pLnB1Ymxpc2hlZCk7XG4gICAgICB9XG4gICAgICBpZiAoaG9zdCAhPT0gdW5kZWZpbmVkICYmIE51bWJlci5pc0ludGVnZXIoaG9zdCkpIG91dC5wdXNoKHsgc2VydmljZSwgcG9ydDogaG9zdCB9KTtcbiAgICB9XG4gIH1cbiAgcmV0dXJuIG91dDtcbn1cblxuLyoqIEhvc3QgcG9ydHMgZnJvbSBtaW5pa3ViZSBzZXR1cC5zaCdzIGBwb3J0X2ZvcndhcmQgXCJOYW1lXCIgc3ZjIFwiSE9TVDpDT05UQUlORVIg4oCmXCJgIGNhbGxzLiAqL1xuZnVuY3Rpb24gZm9yd2FyZEhvc3RQb3J0cyhmaWxlOiBzdHJpbmcpOiBIb3N0UG9ydFtdIHtcbiAgY29uc3Qgb3V0OiBIb3N0UG9ydFtdID0gW107XG4gIGNvbnN0IHJlID0gL3BvcnRfZm9yd2FyZFxccytcIihbXlwiXSspXCJcXHMrXFxTK1xccytcIihbXlwiXSspXCIvZztcbiAgY29uc3QgdGV4dCA9IHJlYWRGaWxlU3luYyhmaWxlLCAndXRmOCcpO1xuICBsZXQgbTogUmVnRXhwRXhlY0FycmF5IHwgbnVsbDtcbiAgd2hpbGUgKChtID0gcmUuZXhlYyh0ZXh0KSkgIT09IG51bGwpIHtcbiAgICBjb25zdCBzZXJ2aWNlID0gbVsxXSA/PyAnJztcbiAgICBmb3IgKGNvbnN0IHBhaXIgb2YgKG1bMl0gPz8gJycpLnRyaW0oKS5zcGxpdCgvXFxzKy8pKSB7XG4gICAgICBjb25zdCBob3N0ID0gTnVtYmVyKHBhaXIuc3BsaXQoJzonKVswXSA/PyAnJyk7XG4gICAgICBpZiAoTnVtYmVyLmlzSW50ZWdlcihob3N0KSkgb3V0LnB1c2goeyBzZXJ2aWNlLCBwb3J0OiBob3N0IH0pO1xuICAgIH1cbiAgfVxuICByZXR1cm4gb3V0O1xufVxuXG4vKipcbiAqIERlcml2ZSB0aGUgaG9zdCBwb3J0cyBhIHRhcmdldCBiaW5kcyBmcm9tIGl0cyBBQ1RVQUwgKGNsb25lZCkgZGVwbG95IHNvdXJjZSwgc28gdGhlXG4gKiBsaXN0IGNhbid0IGRyaWZ0IGZyb20gdGhlIGRlcGxveS4gbG9jYWwg4oaSIGRvY2tlci1jb21wb3NlLnltbDsgbWluaWt1YmUg4oaSIHNldHVwLnNoO1xuICogZWMyL2VrcyDihpIgbm9uZSAoQ2xvdWRGb3JtYXRpb24gYmluZHMgbm90aGluZyBvbiB0aGUgb3BlcmF0b3IncyBtYWNoaW5lKS4gTmVlZHNcbiAqIHRoZSBkZXBsb3kgZmlsZXMgb24gZGlzayAocnVuIHBvc3QtY2xvbmUpOyBmYWxscyBiYWNrIHRvIHRoZSB0YXJnZXQncyBzdGF0aWNcbiAqIGBob3N0UG9ydHNgIGlmIHRoZSBzb3VyY2UgaXMgbWlzc2luZy91bnJlYWRhYmxlLlxuICovXG5leHBvcnQgZnVuY3Rpb24gZGlzY292ZXJIb3N0UG9ydHModGFyZ2V0OiBUYXJnZXRJZCwgY3dkOiBzdHJpbmcsIHNwZWM6IFRhcmdldFNwZWMpOiBIb3N0UG9ydFtdIHtcbiAgdHJ5IHtcbiAgICBpZiAodGFyZ2V0ID09PSAnZG9ja2VyJykgcmV0dXJuIGNvbXBvc2VIb3N0UG9ydHMocGF0aC5qb2luKGN3ZCwgc3BlYy5kaXIsICdkb2NrZXItY29tcG9zZS55bWwnKSk7XG4gICAgaWYgKHRhcmdldCA9PT0gJ21pbmlrdWJlJykgcmV0dXJuIGZvcndhcmRIb3N0UG9ydHMocGF0aC5qb2luKGN3ZCwgc3BlYy5kaXIsICdiaW4nLCAnc2V0dXAuc2gnKSk7XG4gICAgcmV0dXJuIFtdOyAvLyBlYzIvZWtzIGRlcGxveSByZW1vdGVseSDigJQgbm90aGluZyBiaW5kcyBsb2NhbGx5XG4gIH0gY2F0Y2gge1xuICAgIHJldHVybiBzcGVjLmhvc3RQb3J0cy5tYXAoKHApID0+ICh7IC4uLnAgfSkpO1xuICB9XG59XG5cbi8qKiBQcm9iZSBlYWNoIGhvc3QgcG9ydDsgcmV0dXJucyBvbmUgUG9ydENoZWNrIHBlciBwb3J0IChwcm9iZWQgaW4gcGFyYWxsZWwpLiAqL1xuZXhwb3J0IGFzeW5jIGZ1bmN0aW9uIGNoZWNrSG9zdFBvcnRzKHBvcnRzOiByZWFkb25seSBIb3N0UG9ydFtdKTogUHJvbWlzZTxQb3J0Q2hlY2tbXT4ge1xuICByZXR1cm4gUHJvbWlzZS5hbGwocG9ydHMubWFwKGFzeW5jICh7IHNlcnZpY2UsIHBvcnQgfSkgPT4gKHsgc2VydmljZSwgcG9ydCwgYXZhaWxhYmxlOiBhd2FpdCBpc1BvcnRGcmVlKHBvcnQpIH0pKSk7XG59XG5cbi8qKlxuICogSXMgdGhlIHRhcmdldCdzIE9XTiBkZXBsb3kgc3RhY2sgYWxyZWFkeSBydW5uaW5nPyAobG9jYWwgZG9ja2VyLWNvbXBvc2Ugb25seS4pIFVzZWRcbiAqIHNvIHRoZSBwb3J0IHByZS1mbGlnaHQgZG9lc24ndCBibG9jayBhbiBpZGVtcG90ZW50IFJFLVJVTjogaWYgdGhlIFwidGFrZW5cIiBwb3J0cyBhcmVcbiAqIGhlbGQgYnkgdGhlIHN0YWNrIHlvdSBhbHJlYWR5IGhhdmUgdXAsIGBkb2NrZXIgY29tcG9zZSB1cGAganVzdCBuby1vcHMgdGhlbSDigJQgdGhhdCdzXG4gKiBub3QgYSBjb25mbGljdC4gUmVhZC1vbmx5IGBkb2NrZXIgY29tcG9zZSBwc2A7IGZhbHNlIG9uIGFueSBlcnJvciAvIG5vbi1sb2NhbCB0YXJnZXQuXG4gKi9cbmV4cG9ydCBmdW5jdGlvbiBzdGFja1J1bm5pbmcodGFyZ2V0OiBUYXJnZXRJZCwgY3dkOiBzdHJpbmcsIHNwZWM6IFRhcmdldFNwZWMpOiBib29sZWFuIHtcbiAgaWYgKHRhcmdldCAhPT0gJ2RvY2tlcicpIHJldHVybiBmYWxzZTtcbiAgdHJ5IHtcbiAgICBjb25zdCBjb21wb3NlID0gcGF0aC5qb2luKGN3ZCwgc3BlYy5kaXIsICdkb2NrZXItY29tcG9zZS55bWwnKTtcbiAgICBjb25zdCBvdXQgPSBleGVjU3luYyhgZG9ja2VyIGNvbXBvc2UgLWYgJyR7Y29tcG9zZX0nIHBzIC1xYCwgeyBzdGRpbzogWydpZ25vcmUnLCAncGlwZScsICdpZ25vcmUnXSwgdGltZW91dDogMTAwMDAgfSlcbiAgICAgIC50b1N0cmluZygpXG4gICAgICAudHJpbSgpO1xuICAgIHJldHVybiBvdXQubGVuZ3RoID4gMDtcbiAgfSBjYXRjaCB7XG4gICAgcmV0dXJuIGZhbHNlO1xuICB9XG59XG4iXX0=
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Post-install steps.
|
|
3
|
+
*
|
|
4
|
+
* After the platform is deployed + health-checked, provision runs an ordered set
|
|
5
|
+
* of post-install steps: **register** (the admin, via `init-platform.sh`, driven
|
|
6
|
+
* non-interactively by env — which also performs any opt-in plugin/compliance/
|
|
7
|
+
* sample loads), then an optional **smoke-test**, **events** (AWS), and any
|
|
8
|
+
* operator **--post-step** commands. Pure, deterministic assembly here — provision
|
|
9
|
+
* runs each via the same gated `runScript()` as the deploy.
|
|
10
|
+
*/
|
|
11
|
+
import { type TargetId } from './targets.js';
|
|
12
|
+
export interface PostStep {
|
|
13
|
+
/** Stable id (`register` / `smoke-test` / `events`) or `custom-N`. */
|
|
14
|
+
readonly id: string;
|
|
15
|
+
/** Human label shown in the plan. */
|
|
16
|
+
readonly label: string;
|
|
17
|
+
/** The exact command to run. */
|
|
18
|
+
readonly command: string;
|
|
19
|
+
/** Non-secret env applied to the child for this step (e.g. LOAD_* for register). */
|
|
20
|
+
readonly env?: Readonly<Record<string, string>>;
|
|
21
|
+
}
|
|
22
|
+
/** A requested step that doesn't apply to the chosen target. */
|
|
23
|
+
export interface SkippedStep {
|
|
24
|
+
readonly id: string;
|
|
25
|
+
readonly reason: string;
|
|
26
|
+
}
|
|
27
|
+
export interface ResolvedPostSteps {
|
|
28
|
+
readonly steps: PostStep[];
|
|
29
|
+
readonly skipped: SkippedStep[];
|
|
30
|
+
}
|
|
31
|
+
export interface PostStepOptions {
|
|
32
|
+
readonly target: TargetId;
|
|
33
|
+
/** Base URL of the deployed platform (from deriveHealthUrl), or null. */
|
|
34
|
+
readonly url: string | null;
|
|
35
|
+
/** AWS region (ec2/eks), if provided. */
|
|
36
|
+
readonly region?: string;
|
|
37
|
+
/** Load-step ids enabled via `--with-*` (drive init-platform's LOAD_* envs + sparse paths). */
|
|
38
|
+
readonly enabledLoadIds: readonly string[];
|
|
39
|
+
/** `--build-bootstrap`: build the CodeBuild bootstrap image during register. */
|
|
40
|
+
readonly buildBootstrap: boolean;
|
|
41
|
+
/** False when `--no-init` — skips the register step entirely. */
|
|
42
|
+
readonly init: boolean;
|
|
43
|
+
/** True for `--init auto` (the default mode). On the AWS targets the deploy self-runs
|
|
44
|
+
* init-platform (ec2 on first boot, eks in setup.sh's final phase), so the register step is
|
|
45
|
+
* NOT surfaced here — it would duplicate that deploy-managed run. (minikube/local run init
|
|
46
|
+
* via provision, not a deploy-managed init.) */
|
|
47
|
+
readonly autoInit?: boolean;
|
|
48
|
+
readonly smokeTest: boolean;
|
|
49
|
+
/** `--with-events`: the AWS event-ingestion bundle (store-token → setup-events). */
|
|
50
|
+
readonly events: boolean;
|
|
51
|
+
/** Raw operator `--post-step` commands, in order. */
|
|
52
|
+
readonly steps: readonly string[];
|
|
53
|
+
}
|
|
54
|
+
/**
|
|
55
|
+
* Resolve the ordered post-install step list: register → smoke-test → events →
|
|
56
|
+
* custom. `register` carries the load + BUILD_BOOTSTRAP env so init-platform runs
|
|
57
|
+
* the enabled loads non-interactively. Requested-but-inapplicable steps land in
|
|
58
|
+
* `skipped` so the caller can warn rather than silently drop them.
|
|
59
|
+
*/
|
|
60
|
+
export declare function resolvePostSteps(opts: PostStepOptions): ResolvedPostSteps;
|
|
61
|
+
//# sourceMappingURL=post-steps.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"post-steps.d.ts","sourceRoot":"","sources":["../../src/agent/post-steps.ts"],"names":[],"mappings":"AAGA;;;;;;;;;GASG;AAEH,OAAO,EAAc,KAAK,QAAQ,EAAE,MAAM,cAAc,CAAC;AAEzD,MAAM,WAAW,QAAQ;IACvB,sEAAsE;IACtE,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAC;IACpB,qCAAqC;IACrC,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,gCAAgC;IAChC,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,oFAAoF;IACpF,QAAQ,CAAC,GAAG,CAAC,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAAC;CACjD;AAED,gEAAgE;AAChE,MAAM,WAAW,WAAW;IAC1B,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAC;IACpB,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;CACzB;AAED,MAAM,WAAW,iBAAiB;IAChC,QAAQ,CAAC,KAAK,EAAE,QAAQ,EAAE,CAAC;IAC3B,QAAQ,CAAC,OAAO,EAAE,WAAW,EAAE,CAAC;CACjC;AAED,MAAM,WAAW,eAAe;IAC9B,QAAQ,CAAC,MAAM,EAAE,QAAQ,CAAC;IAC1B,yEAAyE;IACzE,QAAQ,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI,CAAC;IAC5B,yCAAyC;IACzC,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,CAAC;IACzB,+FAA+F;IAC/F,QAAQ,CAAC,cAAc,EAAE,SAAS,MAAM,EAAE,CAAC;IAC3C,gFAAgF;IAChF,QAAQ,CAAC,cAAc,EAAE,OAAO,CAAC;IACjC,iEAAiE;IACjE,QAAQ,CAAC,IAAI,EAAE,OAAO,CAAC;IACvB;;;qDAGiD;IACjD,QAAQ,CAAC,QAAQ,CAAC,EAAE,OAAO,CAAC;IAC5B,QAAQ,CAAC,SAAS,EAAE,OAAO,CAAC;IAC5B,oFAAoF;IACpF,QAAQ,CAAC,MAAM,EAAE,OAAO,CAAC;IACzB,qDAAqD;IACrD,QAAQ,CAAC,KAAK,EAAE,SAAS,MAAM,EAAE,CAAC;CACnC;AASD;;;;;GAKG;AACH,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,eAAe,GAAG,iBAAiB,CA0GzE"}
|
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
// Copyright 2026 Pipeline Builder Contributors
|
|
2
|
+
// SPDX-License-Identifier: Apache-2.0
|
|
3
|
+
/**
|
|
4
|
+
* Post-install steps.
|
|
5
|
+
*
|
|
6
|
+
* After the platform is deployed + health-checked, provision runs an ordered set
|
|
7
|
+
* of post-install steps: **register** (the admin, via `init-platform.sh`, driven
|
|
8
|
+
* non-interactively by env — which also performs any opt-in plugin/compliance/
|
|
9
|
+
* sample loads), then an optional **smoke-test**, **events** (AWS), and any
|
|
10
|
+
* operator **--post-step** commands. Pure, deterministic assembly here — provision
|
|
11
|
+
* runs each via the same gated `runScript()` as the deploy.
|
|
12
|
+
*/
|
|
13
|
+
import { LOAD_STEPS } from './targets.js';
|
|
14
|
+
/** Compute the non-interactive env that drives init-platform's prompts. */
|
|
15
|
+
function registerEnv(enabledLoadIds, buildBootstrap) {
|
|
16
|
+
const env = { BUILD_BOOTSTRAP: buildBootstrap ? 'y' : 'n' };
|
|
17
|
+
for (const step of LOAD_STEPS)
|
|
18
|
+
env[step.env] = enabledLoadIds.includes(step.id) ? 'y' : 'n';
|
|
19
|
+
return env;
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* Resolve the ordered post-install step list: register → smoke-test → events →
|
|
23
|
+
* custom. `register` carries the load + BUILD_BOOTSTRAP env so init-platform runs
|
|
24
|
+
* the enabled loads non-interactively. Requested-but-inapplicable steps land in
|
|
25
|
+
* `skipped` so the caller can warn rather than silently drop them.
|
|
26
|
+
*/
|
|
27
|
+
export function resolvePostSteps(opts) {
|
|
28
|
+
const steps = [];
|
|
29
|
+
const skipped = [];
|
|
30
|
+
// `--init auto` (the default) on the AWS targets: the deploy runs init-platform itself —
|
|
31
|
+
// ec2 on the instance on first boot, eks in setup.sh's final phase — so DON'T add a register
|
|
32
|
+
// step here; it would duplicate the deploy-managed run. (minikube/local run init via provision.)
|
|
33
|
+
const autoInitManaged = opts.init && opts.autoInit === true && (opts.target === 'ec2' || opts.target === 'eks');
|
|
34
|
+
if (opts.init && !autoInitManaged) {
|
|
35
|
+
const enabled = LOAD_STEPS.filter((s) => opts.enabledLoadIds.includes(s.id)).map((s) => s.id);
|
|
36
|
+
const loads = enabled.length > 0 ? ` (+ ${enabled.join(', ')})` : '';
|
|
37
|
+
// Register is surfaced as a manual next-step on AWS (see runPostSteps). The command differs:
|
|
38
|
+
// - ec2: runs ON the box as the minikube user, where the in-VPC URL resolves → bake PLATFORM_BASE_URL.
|
|
39
|
+
// - eks: init-platform port-forwards to svc/nginx, so it runs from anywhere with kubectl access —
|
|
40
|
+
// do NOT bake PLATFORM_BASE_URL (that would force the public URL and wait on the ALB/DNS).
|
|
41
|
+
// - local/minikube: plain command (the URL is localhost).
|
|
42
|
+
const registerCommand = opts.target === 'ec2' && opts.url
|
|
43
|
+
? `PLATFORM_BASE_URL=${opts.url} ./deploy/bin/init-platform.sh ec2`
|
|
44
|
+
: `./deploy/bin/init-platform.sh ${opts.target}`;
|
|
45
|
+
steps.push({
|
|
46
|
+
id: 'register',
|
|
47
|
+
label: `Register admin${loads}`,
|
|
48
|
+
command: registerCommand,
|
|
49
|
+
env: registerEnv(opts.enabledLoadIds, opts.buildBootstrap),
|
|
50
|
+
});
|
|
51
|
+
}
|
|
52
|
+
if (opts.smokeTest) {
|
|
53
|
+
if (opts.url) {
|
|
54
|
+
// -k: local/minikube front a self-signed cert; harmless against real-cert AWS URLs.
|
|
55
|
+
// Probe /health — the one route guaranteed to exist and serve unauthenticated
|
|
56
|
+
// through nginx (every /api/* route requires auth → 401; /api/version doesn't
|
|
57
|
+
// exist and would fall through to the SPA, making `curl -f` meaningless).
|
|
58
|
+
steps.push({
|
|
59
|
+
id: 'smoke-test',
|
|
60
|
+
label: 'Smoke test — gateway/API reachability',
|
|
61
|
+
command: `curl -fsSk ${opts.url}/health && echo 'smoke-test: API reachable'`,
|
|
62
|
+
});
|
|
63
|
+
}
|
|
64
|
+
else {
|
|
65
|
+
skipped.push({ id: 'smoke-test', reason: 'no platform URL to probe for this target' });
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
if (opts.events) {
|
|
69
|
+
if (opts.target === 'ec2' || opts.target === 'eks') {
|
|
70
|
+
// Event ingestion is a THREE-step bundle, in this order:
|
|
71
|
+
// 1. store-token — the full-privilege platform JWT (synth/deploy
|
|
72
|
+
// callbacks read it; --schedule auto-renews it).
|
|
73
|
+
// 2. store-token --scope reporting:ingest --schedule
|
|
74
|
+
// — a DISTINCT least-privilege token whose `scope`
|
|
75
|
+
// claim is exactly `reporting:ingest`. The ingest
|
|
76
|
+
// endpoint requires this scope and `hasScope` is a
|
|
77
|
+
// single-value equality check, so the platform
|
|
78
|
+
// token (no scope) can NEVER ingest — the Lambda
|
|
79
|
+
// must read this dedicated token. Stored at
|
|
80
|
+
// `.../reporting-ingest`. --schedule deploys its OWN
|
|
81
|
+
// renewal stack (named `-reporting-ingest`, distinct
|
|
82
|
+
// from the platform one) whose handler preserves the
|
|
83
|
+
// scope on re-mint, so it auto-renews and never lapses.
|
|
84
|
+
// See setup-events --scoped-ingest.
|
|
85
|
+
// 3. setup-events --scoped-ingest
|
|
86
|
+
// — deploys the EventBridge → SQS → Lambda and points
|
|
87
|
+
// the Lambda at the reporting-ingest secret from #2.
|
|
88
|
+
// WITHOUT --scoped-ingest the Lambda reads the
|
|
89
|
+
// platform secret and every ingest 403s
|
|
90
|
+
// ("Token must carry the 'reporting:ingest' scope").
|
|
91
|
+
// provision surfaces (does not auto-run) the bundle on AWS — it needs a
|
|
92
|
+
// registered, in-VPC platform.
|
|
93
|
+
const region = opts.region ? ` --region ${opts.region}` : '';
|
|
94
|
+
// Mirror the register branch's target rule: bake PLATFORM_BASE_URL only on
|
|
95
|
+
// ec2 (the in-VPC URL resolves on the box). On eks the public ALB/DNS isn't
|
|
96
|
+
// up yet, so baking it would point store-token/setup-events at an
|
|
97
|
+
// unresolvable host — the operator sets PLATFORM_BASE_URL when running the
|
|
98
|
+
// surfaced bundle, once the ALB is ready.
|
|
99
|
+
const env = opts.target === 'ec2' && opts.url ? { PLATFORM_BASE_URL: opts.url } : undefined;
|
|
100
|
+
steps.push({
|
|
101
|
+
id: 'store-token',
|
|
102
|
+
label: 'Store platform token in AWS Secrets Manager (+ daily auto-renewal)',
|
|
103
|
+
// --schedule: synth/deploy callbacks read this token, so install the
|
|
104
|
+
// renewal stack so it never lapses (store-token no longer deploys it by default).
|
|
105
|
+
command: `pipeline-manager infra store-token --schedule${region}`,
|
|
106
|
+
env,
|
|
107
|
+
});
|
|
108
|
+
steps.push({
|
|
109
|
+
id: 'store-token-ingest',
|
|
110
|
+
label: 'Store reporting-ingest (scoped) token in AWS Secrets Manager (+ auto-renewal)',
|
|
111
|
+
command: `pipeline-manager infra store-token --scope reporting:ingest --schedule${region}`,
|
|
112
|
+
env,
|
|
113
|
+
});
|
|
114
|
+
steps.push({
|
|
115
|
+
id: 'events',
|
|
116
|
+
label: 'EventBridge ingestion (setup-events, scoped-ingest)',
|
|
117
|
+
command: `pipeline-manager infra setup-events --scoped-ingest${region}`,
|
|
118
|
+
env,
|
|
119
|
+
});
|
|
120
|
+
}
|
|
121
|
+
else {
|
|
122
|
+
skipped.push({ id: 'events', reason: `not applicable to target '${opts.target}' (ec2/eks only)` });
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
opts.steps.forEach((command, i) => {
|
|
126
|
+
if (command.trim() !== '')
|
|
127
|
+
steps.push({ id: `custom-${i + 1}`, label: `Custom step ${i + 1}`, command });
|
|
128
|
+
});
|
|
129
|
+
return { steps, skipped };
|
|
130
|
+
}
|
|
131
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoicG9zdC1zdGVwcy5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uL3NyYy9hZ2VudC9wb3N0LXN0ZXBzLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBLCtDQUErQztBQUMvQyxzQ0FBc0M7QUFFdEM7Ozs7Ozs7OztHQVNHO0FBRUgsT0FBTyxFQUFFLFVBQVUsRUFBaUIsTUFBTSxjQUFjLENBQUM7QUFnRHpELDJFQUEyRTtBQUMzRSxTQUFTLFdBQVcsQ0FBQyxjQUFpQyxFQUFFLGNBQXVCO0lBQzdFLE1BQU0sR0FBRyxHQUEyQixFQUFFLGVBQWUsRUFBRSxjQUFjLENBQUMsQ0FBQyxDQUFDLEdBQUcsQ0FBQyxDQUFDLENBQUMsR0FBRyxFQUFFLENBQUM7SUFDcEYsS0FBSyxNQUFNLElBQUksSUFBSSxVQUFVO1FBQUUsR0FBRyxDQUFDLElBQUksQ0FBQyxHQUFHLENBQUMsR0FBRyxjQUFjLENBQUMsUUFBUSxDQUFDLElBQUksQ0FBQyxFQUFFLENBQUMsQ0FBQyxDQUFDLENBQUMsR0FBRyxDQUFDLENBQUMsQ0FBQyxHQUFHLENBQUM7SUFDNUYsT0FBTyxHQUFHLENBQUM7QUFDYixDQUFDO0FBRUQ7Ozs7O0dBS0c7QUFDSCxNQUFNLFVBQVUsZ0JBQWdCLENBQUMsSUFBcUI7SUFDcEQsTUFBTSxLQUFLLEdBQWUsRUFBRSxDQUFDO0lBQzdCLE1BQU0sT0FBTyxHQUFrQixFQUFFLENBQUM7SUFFbEMseUZBQXlGO0lBQ3pGLDZGQUE2RjtJQUM3RixpR0FBaUc7SUFDakcsTUFBTSxlQUFlLEdBQ25CLElBQUksQ0FBQyxJQUFJLElBQUksSUFBSSxDQUFDLFFBQVEsS0FBSyxJQUFJLElBQUksQ0FBQyxJQUFJLENBQUMsTUFBTSxLQUFLLEtBQUssSUFBSSxJQUFJLENBQUMsTUFBTSxLQUFLLEtBQUssQ0FBQyxDQUFDO0lBQzFGLElBQUksSUFBSSxDQUFDLElBQUksSUFBSSxDQUFDLGVBQWUsRUFBRSxDQUFDO1FBQ2xDLE1BQU0sT0FBTyxHQUFHLFVB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cHlyaWdodCAyMDI2IFBpcGVsaW5lIEJ1aWxkZXIgQ29udHJpYnV0b3JzXG4vLyBTUERYLUxpY2Vuc2UtSWRlbnRpZmllcjogQXBhY2hlLTIuMFxuXG4vKipcbiAqIFBvc3QtaW5zdGFsbCBzdGVwcy5cbiAqXG4gKiBBZnRlciB0aGUgcGxhdGZvcm0gaXMgZGVwbG95ZWQgKyBoZWFsdGgtY2hlY2tlZCwgcHJvdmlzaW9uIHJ1bnMgYW4gb3JkZXJlZCBzZXRcbiAqIG9mIHBvc3QtaW5zdGFsbCBzdGVwczogKipyZWdpc3RlcioqICh0aGUgYWRtaW4sIHZpYSBgaW5pdC1wbGF0Zm9ybS5zaGAsIGRyaXZlblxuICogbm9uLWludGVyYWN0aXZlbHkgYnkgZW52IOKAlCB3aGljaCBhbHNvIHBlcmZvcm1zIGFueSBvcHQtaW4gcGx1Z2luL2NvbXBsaWFuY2UvXG4gKiBzYW1wbGUgbG9hZHMpLCB0aGVuIGFuIG9wdGlvbmFsICoqc21va2UtdGVzdCoqLCAqKmV2ZW50cyoqIChBV1MpLCBhbmQgYW55XG4gKiBvcGVyYXRvciAqKi0tcG9zdC1zdGVwKiogY29tbWFuZHMuIFB1cmUsIGRldGVybWluaXN0aWMgYXNzZW1ibHkgaGVyZSDigJQgcHJvdmlzaW9uXG4gKiBydW5zIGVhY2ggdmlhIHRoZSBzYW1lIGdhdGVkIGBydW5TY3JpcHQoKWAgYXMgdGhlIGRlcGxveS5cbiAqL1xuXG5pbXBvcnQgeyBMT0FEX1NURVBTLCB0eXBlIFRhcmdldElkIH0gZnJvbSAnLi90YXJnZXRzLmpzJztcblxuZXhwb3J0IGludGVyZmFjZSBQb3N0U3RlcCB7XG4gIC8qKiBTdGFibGUgaWQgKGByZWdpc3RlcmAgLyBgc21va2UtdGVzdGAgLyBgZXZlbnRzYCkgb3IgYGN1c3RvbS1OYC4gKi9cbiAgcmVhZG9ubHkgaWQ6IHN0cmluZztcbiAgLyoqIEh1bWFuIGxhYmVsIHNob3duIGluIHRoZSBwbGFuLiAqL1xuICByZWFkb25seSBsYWJlbDogc3RyaW5nO1xuICAvKiogVGhlIGV4YWN0IGNvbW1hbmQgdG8gcnVuLiAqL1xuICByZWFkb25seSBjb21tYW5kOiBzdHJpbmc7XG4gIC8qKiBOb24tc2VjcmV0IGVudiBhcHBsaWVkIHRvIHRoZSBjaGlsZCBmb3IgdGhpcyBzdGVwIChlLmcuIExPQURfKiBmb3IgcmVnaXN0ZXIpLiAqL1xuICByZWFkb25seSBlbnY/OiBSZWFkb25seTxSZWNvcmQ8c3RyaW5nLCBzdHJpbmc+Pjtcbn1cblxuLyoqIEEgcmVxdWVzdGVkIHN0ZXAgdGhhdCBkb2Vzbid0IGFwcGx5IHRvIHRoZSBjaG9zZW4gdGFyZ2V0LiAqL1xuZXhwb3J0IGludGVyZmFjZSBTa2lwcGVkU3RlcCB7XG4gIHJlYWRvbmx5IGlkOiBzdHJpbmc7XG4gIHJlYWRvbmx5IHJlYXNvbjogc3RyaW5nO1xufVxuXG5leHBvcnQgaW50ZXJmYWNlIFJlc29sdmVkUG9zdFN0ZXBzIHtcbiAgcmVhZG9ubHkgc3RlcHM6IFBvc3RTdGVwW107XG4gIHJlYWRvbmx5IHNraXBwZWQ6IFNraXBwZWRTdGVwW107XG59XG5cbmV4cG9ydCBpbnRlcmZhY2UgUG9zdFN0ZXBPcHRpb25zIHtcbiAgcmVhZG9ubHkgdGFyZ2V0OiBUYXJnZXRJZDtcbiAgLyoqIEJhc2UgVVJMIG9mIHRoZSBkZXBsb3llZCBwbGF0Zm9ybSAoZnJvbSBkZXJpdmVIZWFsdGhVcmwpLCBvciBudWxsLiAqL1xuICByZWFkb25seSB1cmw6IHN0cmluZyB8IG51bGw7XG4gIC8qKiBBV1MgcmVnaW9uIChlYzIvZWtzKSwgaWYgcHJvdmlkZWQuICovXG4gIHJlYWRvbmx5IHJlZ2lvbj86IHN0cmluZztcbiAgLyoqIExvYWQtc3RlcCBpZHMgZW5hYmxlZCB2aWEgYC0td2l0aC0qYCAoZHJpdmUgaW5pdC1wbGF0Zm9ybSdzIExPQURfKiBlbnZzICsgc3BhcnNlIHBhdGhzKS4gKi9cbiAgcmVhZG9ubHkgZW5hYmxlZExvYWRJZHM6IHJlYWRvbmx5IHN0cmluZ1tdO1xuICAvKiogYC0tYnVpbGQtYm9vdHN0cmFwYDogYnVpbGQgdGhlIENvZGVCdWlsZCBib290c3RyYXAgaW1hZ2UgZHVyaW5nIHJlZ2lzdGVyLiAqL1xuICByZWFkb25seSBidWlsZEJvb3RzdHJhcDogYm9vbGVhbjtcbiAgLyoqIEZhbHNlIHdoZW4gYC0tbm8taW5pdGAg4oCUIHNraXBzIHRoZSByZWdpc3RlciBzdGVwIGVudGlyZWx5LiAqL1xuICByZWFkb25seSBpbml0OiBib29sZWFuO1xuICAvKiogVHJ1ZSBmb3IgYC0taW5pdCBhdXRvYCAodGhlIGRlZmF1bHQgbW9kZSkuIE9uIHRoZSBBV1MgdGFyZ2V0cyB0aGUgZGVwbG95IHNlbGYtcnVuc1xuICAgKiAgaW5pdC1wbGF0Zm9ybSAoZWMyIG9uIGZpcnN0IGJvb3QsIGVrcyBpbiBzZXR1cC5zaCdzIGZpbmFsIHBoYXNlKSwgc28gdGhlIHJlZ2lzdGVyIHN0ZXAgaXNcbiAgICogIE5PVCBzdXJmYWNlZCBoZXJlIOKAlCBpdCB3b3VsZCBkdXBsaWNhdGUgdGhhdCBkZXBsb3ktbWFuYWdlZCBydW4uIChtaW5pa3ViZS9sb2NhbCBydW4gaW5pdFxuICAgKiAgdmlhIHByb3Zpc2lvbiwgbm90IGEgZGVwbG95LW1hbmFnZWQgaW5pdC4pICovXG4gIHJlYWRvbmx5IGF1dG9Jbml0PzogYm9vbGVhbjtcbiAgcmVhZG9ubHkgc21va2VUZXN0OiBib29sZWFuO1xuICAvKiogYC0td2l0aC1ldmVudHNgOiB0aGUgQVdTIGV2ZW50LWluZ2VzdGlvbiBidW5kbGUgKHN0b3JlLXRva2VuIOKGkiBzZXR1cC1ldmVudHMpLiAqL1xuICByZWFkb25seSBldmVudHM6IGJvb2xlYW47XG4gIC8qKiBSYXcgb3BlcmF0b3IgYC0tcG9zdC1zdGVwYCBjb21tYW5kcywgaW4gb3JkZXIuICovXG4gIHJlYWRvbmx5IHN0ZXBzOiByZWFkb25seSBzdHJpbmdbXTtcbn1cblxuLyoqIENvbXB1dGUgdGhlIG5vbi1pbnRlcmFjdGl2ZSBlbnYgdGhhdCBkcml2ZXMgaW5pdC1wbGF0Zm9ybSdzIHByb21wdHMuICovXG5mdW5jdGlvbiByZWdpc3RlckVudihlbmFibGVkTG9hZElkczogcmVhZG9ubHkgc3RyaW5nW10sIGJ1aWxkQm9vdHN0cmFwOiBib29sZWFuKTogUmVjb3JkPHN0cmluZywgc3RyaW5nPiB7XG4gIGNvbnN0IGVudjogUmVjb3JkPHN0cmluZywgc3RyaW5nPiA9IHsgQlVJTERfQk9PVFNUUkFQOiBidWlsZEJvb3RzdHJhcCA/ICd5JyA6ICduJyB9O1xuICBmb3IgKGNvbnN0IHN0ZXAgb2YgTE9BRF9TVEVQUykgZW52W3N0ZXAuZW52XSA9IGVuYWJsZWRMb2FkSWRzLmluY2x1ZGVzKHN0ZXAuaWQpID8gJ3knIDogJ24nO1xuICByZXR1cm4gZW52O1xufVxuXG4vKipcbiAqIFJlc29sdmUgdGhlIG9yZGVyZWQgcG9zdC1pbnN0YWxsIHN0ZXAgbGlzdDogcmVnaXN0ZXIg4oaSIHNtb2tlLXRlc3Qg4oaSIGV2ZW50cyDihpJcbiAqIGN1c3RvbS4gYHJlZ2lzdGVyYCBjYXJyaWVzIHRoZSBsb2FkICsgQlVJTERfQk9PVFNUUkFQIGVudiBzbyBpbml0LXBsYXRmb3JtIHJ1bnNcbiAqIHRoZSBlbmFibGVkIGxvYWRzIG5vbi1pbnRlcmFjdGl2ZWx5LiBSZXF1ZXN0ZWQtYnV0LWluYXBwbGljYWJsZSBzdGVwcyBsYW5kIGluXG4gKiBgc2tpcHBlZGAgc28gdGhlIGNhbGxlciBjYW4gd2FybiByYXRoZXIgdGhhbiBzaWxlbnRseSBkcm9wIHRoZW0uXG4gKi9cbmV4cG9ydCBmdW5jdGlvbiByZXNvbHZlUG9zdFN0ZXBzKG9wdHM6IFBvc3RTdGVwT3B0aW9ucyk6IFJlc29sdmVkUG9zdFN0ZXBzIHtcbiAgY29uc3Qgc3RlcHM6IFBvc3RTdGVwW10gPSBbXTtcbiAgY29uc3Qgc2tpcHBlZDogU2tpcHBlZFN0ZXBbXSA9IFtdO1xuXG4gIC8vIGAtLWluaXQgYXV0b2AgKHRoZSBkZWZhdWx0KSBvbiB0aGUgQVdTIHRhcmdldHM6IHRoZSBkZXBsb3kgcnVucyBpbml0LXBsYXRmb3JtIGl0c2VsZiDigJRcbiAgLy8gZWMyIG9uIHRoZSBpbnN0YW5jZSBvbiBmaXJzdCBib290LCBla3MgaW4gc2V0dXAuc2gncyBmaW5hbCBwaGFzZSDigJQgc28gRE9OJ1QgYWRkIGEgcmVnaXN0ZXJcbiAgLy8gc3RlcCBoZXJlOyBpdCB3b3VsZCBkdXBsaWNhdGUgdGhlIGRlcGxveS1tYW5hZ2VkIHJ1bi4gKG1pbmlrdWJlL2xvY2FsIHJ1biBpbml0IHZpYSBwcm92aXNpb24uKVxuICBjb25zdCBhdXRvSW5pdE1hbmFnZWQgPVxuICAgIG9wdHMuaW5pdCAmJiBvcHRzLmF1dG9Jbml0ID09PSB0cnVlICYmIChvcHRzLnRhcmdldCA9PT0gJ2VjMicgfHwgb3B0cy50YXJnZXQgPT09ICdla3MnKTtcbiAgaWYgKG9wdHMuaW5pdCAmJiAhYXV0b0luaXRNYW5hZ2VkKSB7XG4gICAgY29uc3QgZW5hYmxlZCA9IExPQURfU1RFUFMuZmlsdGVyKChzKSA9PiBvcHRzLmVuYWJsZWRMb2FkSWRzLmluY2x1ZGVzKHMuaWQpKS5tYXAoKHMpID0+IHMuaWQpO1xuICAgIGNvbnN0IGxvYWRzID0gZW5hYmxlZC5sZW5ndGggPiAwID8gYCAoKyAke2VuYWJsZWQuam9pbignLCAnKX0pYCA6ICcnO1xuICAgIC8vIFJlZ2lzdGVyIGlzIHN1cmZhY2VkIGFzIGEgbWFudWFsIG5leHQtc3RlcCBvbiBBV1MgKHNlZSBydW5Qb3N0U3RlcHMpLiBUaGUgY29tbWFuZCBkaWZmZXJzOlxuICAgIC8vICAtIGVjMjogcnVucyBPTiB0aGUgYm94IGFzIHRoZSBtaW5pa3ViZSB1c2VyLCB3aGVyZSB0aGUgaW4tVlBDIFVSTCByZXNvbHZlcyDihpIgYmFrZSBQTEFURk9STV9CQVNFX1VSTC5cbiAgICAvLyAgLSBla3M6IGluaXQtcGxhdGZvcm0gcG9ydC1mb3J3YXJkcyB0byBzdmMvbmdpbngsIHNvIGl0IHJ1bnMgZnJvbSBhbnl3aGVyZSB3aXRoIGt1YmVjdGwgYWNjZXNzIOKAlFxuICAgIC8vICAgIGRvIE5PVCBiYWtlIFBMQVRGT1JNX0JBU0VfVVJMICh0aGF0IHdvdWxkIGZvcmNlIHRoZSBwdWJsaWMgVVJMIGFuZCB3YWl0IG9uIHRoZSBBTEIvRE5TKS5cbiAgICAvLyAgLSBsb2NhbC9taW5pa3ViZTogcGxhaW4gY29tbWFuZCAodGhlIFVSTCBpcyBsb2NhbGhvc3QpLlxuICAgIGNvbnN0IHJlZ2lzdGVyQ29tbWFuZCA9IG9wdHMudGFyZ2V0ID09PSAnZWMyJyAmJiBvcHRzLnVybFxuICAgICAgPyBgUExBVEZPUk1fQkFTRV9VUkw9JHtvcHRzLnVybH0gLi9kZXBsb3kvYmluL2luaXQtcGxhdGZvcm0uc2ggZWMyYFxuICAgICAgOiBgLi9kZXBsb3kvYmluL2luaXQtcGxhdGZvcm0uc2ggJHtvcHRzLnRhcmdldH1gO1xuICAgIHN0ZXBzLnB1c2goe1xuICAgICAgaWQ6ICdyZWdpc3RlcicsXG4gICAgICBsYWJlbDogYFJlZ2lzdGVyIGFkbWluJHtsb2Fkc31gLFxuICAgICAgY29tbWFuZDogcmVnaXN0ZXJDb21tYW5kLFxuICAgICAgZW52OiByZWdpc3RlckVudihvcHRzLmVuYWJsZWRMb2FkSWRzLCBvcHRzLmJ1aWxkQm9vdHN0cmFwKSxcbiAgICB9KTtcbiAgfVxuXG4gIGlmIChvcHRzLnNtb2tlVGVzdCkge1xuICAgIGlmIChvcHRzLnVybCkge1xuICAgICAgLy8gLWs6IGxvY2FsL21pbmlrdWJlIGZyb250IGEgc2VsZi1zaWduZWQgY2VydDsgaGFybWxlc3MgYWdhaW5zdCByZWFsLWNlcnQgQVdTIFVSTHMuXG4gICAgICAvLyBQcm9iZSAvaGVhbHRoIOKAlCB0aGUgb25lIHJvdXRlIGd1YXJhbnRlZWQgdG8gZXhpc3QgYW5kIHNlcnZlIHVuYXV0aGVudGljYXRlZFxuICAgICAgLy8gdGhyb3VnaCBuZ2lueCAoZXZlcnkgL2FwaS8qIHJvdXRlIHJlcXVpcmVzIGF1dGgg4oaSIDQwMTsgL2FwaS92ZXJzaW9uIGRvZXNuJ3RcbiAgICAgIC8vIGV4aXN0IGFuZCB3b3VsZCBmYWxsIHRocm91Z2ggdG8gdGhlIFNQQSwgbWFraW5nIGBjdXJsIC1mYCBtZWFuaW5nbGVzcykuXG4gICAgICBzdGVwcy5wdXNoKHtcbiAgICAgICAgaWQ6ICdzbW9rZS10ZXN0JyxcbiAgICAgICAgbGFiZWw6ICdTbW9rZSB0ZXN0IOKAlCBnYXRld2F5L0FQSSByZWFjaGFiaWxpdHknLFxuICAgICAgICBjb21tYW5kOiBgY3VybCAtZnNTayAke29wdHMudXJsfS9oZWFsdGggJiYgZWNobyAnc21va2UtdGVzdDogQVBJIHJlYWNoYWJsZSdgLFxuICAgICAgfSk7XG4gICAgfSBlbHNlIHtcbiAgICAgIHNraXBwZWQucHVzaCh7IGlkOiAnc21va2UtdGVzdCcsIHJlYXNvbjogJ25vIHBsYXRmb3JtIFVSTCB0byBwcm9iZSBmb3IgdGhpcyB0YXJnZXQnIH0pO1xuICAgIH1cbiAgfVxuXG4gIGlmIChvcHRzLmV2ZW50cykge1xuICAgIGlmIChvcHRzLnRhcmdldCA9PT0gJ2VjMicgfHwgb3B0cy50YXJnZXQgPT09ICdla3MnKSB7XG4gICAgICAvLyBFdmVudCBpbmdlc3Rpb24gaXMgYSBUSFJFRS1zdGVwIGJ1bmRsZSwgaW4gdGhpcyBvcmRlcjpcbiAgICAgIC8vICAgMS4gc3RvcmUtdG9rZW4gICAgICAgICAg4oCUIHRoZSBmdWxsLXByaXZpbGVnZSBwbGF0Zm9ybSBKV1QgKHN5bnRoL2RlcGxveVxuICAgICAgLy8gICAgICAgICAgICAgICAgICAgICAgICAgICAgIGNhbGxiYWNrcyByZWFkIGl0OyAtLXNjaGVkdWxlIGF1dG8tcmVuZXdzIGl0KS5cbiAgICAgIC8vICAgMi4gc3RvcmUtdG9rZW4gLS1zY29wZSByZXBvcnRpbmc6aW5nZXN0IC0tc2NoZWR1bGVcbiAgICAgIC8vICAgICAgICAgICAgICAgICAgICAgICAgICAg4oCUIGEgRElTVElOQ1QgbGVhc3QtcHJpdmlsZWdlIHRva2VuIHdob3NlIGBzY29wZWBcbiAgICAgIC8vICAgICAgICAgICAgICAgICAgICAgICAgICAgICBjbGFpbSBpcyBleGFjdGx5IGByZXBvcnRpbmc6aW5nZXN0YC4gVGhlIGluZ2VzdFxuICAgICAgLy8gICAgICAgICAgICAgICAgICAgICAgICAgICAgIGVuZHBvaW50IHJlcXVpcmVzIHRoaXMgc2NvcGUgYW5kIGBoYXNTY29wZWAgaXMgYVxuICAgICAgLy8gICAgICAgICAgICAgICAgICAgICAgICAgICAgIHNpbmdsZS12YWx1ZSBlcXVhbGl0eSBjaGVjaywgc28gdGhlIHBsYXRmb3JtXG4gICAgICAvLyAgICAgICAgICAgICAgICAgICAgICAgICAgICAgdG9rZW4gKG5vIHNjb3BlKSBjYW4gTkVWRVIgaW5nZXN0IOKAlCB0aGUgTGFtYmRhXG4gICAgICAvLyAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbXVzdCByZWFkIHRoaXMgZGVkaWNhdGVkIHRva2VuLiBTdG9yZWQgYXRcbiAgICAgIC8vICAgICAgICAgICAgICAgICAgICAgICAgICAgICBgLi4uL3JlcG9ydGluZy1pbmdlc3RgLiAtLXNjaGVkdWxlIGRlcGxveXMgaXRzIE9XTlxuICAgICAgLy8gICAgICAgICAgICAgICAgICAgICAgICAgICAgIHJlbmV3YWwgc3RhY2sgKG5hbWVkIGAtcmVwb3J0aW5nLWluZ2VzdGAsIGRpc3RpbmN0XG4gICAgICAvLyAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZnJvbSB0aGUgcGxhdGZvcm0gb25lKSB3aG9zZSBoYW5kbGVyIHByZXNlcnZlcyB0aGVcbiAgICAgIC8vICAgICAgICAgICAgICAgICAgICAgICAgICAgICBzY29wZSBvbiByZS1taW50LCBzbyBpdCBhdXRvLXJlbmV3cyBhbmQgbmV2ZXIgbGFwc2VzLlxuICAgICAgLy8gICAgICAgICAgICAgICAgICAgICAgICAgICAgIFNlZSBzZXR1cC1ldmVudHMgLS1zY29wZWQtaW5nZXN0LlxuICAgICAgLy8gICAzLiBzZXR1cC1ldmVudHMgLS1zY29wZWQtaW5nZXN0XG4gICAgICAvLyAgICAgICAgICAgICAgICAgICAgICAgICAgIOKAlCBkZXBsb3lzIHRoZSBFdmVudEJyaWRnZSDihpIgU1FTIOKGkiBMYW1iZGEgYW5kIHBvaW50c1xuICAgICAgLy8gICAgICAgICAgICAgICAgICAgICAgICAgICAgIHRoZSBMYW1iZGEgYXQgdGhlIHJlcG9ydGluZy1pbmdlc3Qgc2VjcmV0IGZyb20gIzIuXG4gICAgICAvLyAgICAgICAgICAgICAgICAgICAgICAgICAgICAgV0lUSE9VVCAtLXNjb3BlZC1pbmdlc3QgdGhlIExhbWJkYSByZWFkcyB0aGVcbiAgICAgIC8vICAgICAgICAgICAgICAgICAgICAgICAgICAgICBwbGF0Zm9ybSBzZWNyZXQgYW5kIGV2ZXJ5IGluZ2VzdCA0MDNzXG4gICAgICAvLyAgICAgICAgICAgICAgICAgICAgICAgICAgICAgKFwiVG9rZW4gbXVzdCBjYXJyeSB0aGUgJ3JlcG9ydGluZzppbmdlc3QnIHNjb3BlXCIpLlxuICAgICAgLy8gcHJvdmlzaW9uIHN1cmZhY2VzIChkb2VzIG5vdCBhdXRvLXJ1bikgdGhlIGJ1bmRsZSBvbiBBV1Mg4oCUIGl0IG5lZWRzIGFcbiAgICAgIC8vIHJlZ2lzdGVyZWQsIGluLVZQQyBwbGF0Zm9ybS5cbiAgICAgIGNvbnN0IHJlZ2lvbiA9IG9wdHMucmVnaW9uID8gYCAtLXJlZ2lvbiAke29wdHMucmVnaW9ufWAgOiAnJztcbiAgICAgIC8vIE1pcnJvciB0aGUgcmVnaXN0ZXIgYnJhbmNoJ3MgdGFyZ2V0IHJ1bGU6IGJha2UgUExBVEZPUk1fQkFTRV9VUkwgb25seSBvblxuICAgICAgLy8gZWMyICh0aGUgaW4tVlBDIFVSTCByZXNvbHZlcyBvbiB0aGUgYm94KS4gT24gZWtzIHRoZSBwdWJsaWMgQUxCL0ROUyBpc24ndFxuICAgICAgLy8gdXAgeWV0LCBzbyBiYWtpbmcgaXQgd291bGQgcG9pbnQgc3RvcmUtdG9rZW4vc2V0dXAtZXZlbnRzIGF0IGFuXG4gICAgICAvLyB1bnJlc29sdmFibGUgaG9zdCDigJQgdGhlIG9wZXJhdG9yIHNldHMgUExBVEZPUk1fQkFTRV9VUkwgd2hlbiBydW5uaW5nIHRoZVxuICAgICAgLy8gc3VyZmFjZWQgYnVuZGxlLCBvbmNlIHRoZSBBTEIgaXMgcmVhZHkuXG4gICAgICBjb25zdCBlbnYgPSBvcHRzLnRhcmdldCA9PT0gJ2VjMicgJiYgb3B0cy51cmwgPyB7IFBMQVRGT1JNX0JBU0VfVVJMOiBvcHRzLnVybCB9IDogdW5kZWZpbmVkO1xuICAgICAgc3RlcHMucHVzaCh7XG4gICAgICAgIGlkOiAnc3RvcmUtdG9rZW4nLFxuICAgICAgICBsYWJlbDogJ1N0b3JlIHBsYXRmb3JtIHRva2VuIGluIEFXUyBTZWNyZXRzIE1hbmFnZXIgKCsgZGFpbHkgYXV0by1yZW5ld2FsKScsXG4gICAgICAgIC8vIC0tc2NoZWR1bGU6IHN5bnRoL2RlcGxveSBjYWxsYmFja3MgcmVhZCB0aGlzIHRva2VuLCBzbyBpbnN0YWxsIHRoZVxuICAgICAgICAvLyByZW5ld2FsIHN0YWNrIHNvIGl0IG5ldmVyIGxhcHNlcyAoc3RvcmUtdG9rZW4gbm8gbG9uZ2VyIGRlcGxveXMgaXQgYnkgZGVmYXVsdCkuXG4gICAgICAgIGNvbW1hbmQ6IGBwaXBlbGluZS1tYW5hZ2VyIGluZnJhIHN0b3JlLXRva2VuIC0tc2NoZWR1bGUke3JlZ2lvbn1gLFxuICAgICAgICBlbnYsXG4gICAgICB9KTtcbiAgICAgIHN0ZXBzLnB1c2goe1xuICAgICAgICBpZDogJ3N0b3JlLXRva2VuLWluZ2VzdCcsXG4gICAgICAgIGxhYmVsOiAnU3RvcmUgcmVwb3J0aW5nLWluZ2VzdCAoc2NvcGVkKSB0b2tlbiBpbiBBV1MgU2VjcmV0cyBNYW5hZ2VyICgrIGF1dG8tcmVuZXdhbCknLFxuICAgICAgICBjb21tYW5kOiBgcGlwZWxpbmUtbWFuYWdlciBpbmZyYSBzdG9yZS10b2tlbiAtLXNjb3BlIHJlcG9ydGluZzppbmdlc3QgLS1zY2hlZHVsZSR7cmVnaW9ufWAsXG4gICAgICAgIGVudixcbiAgICAgIH0pO1xuICAgICAgc3RlcHMucHVzaCh7XG4gICAgICAgIGlkOiAnZXZlbnRzJyxcbiAgICAgICAgbGFiZWw6ICdFdmVudEJyaWRnZSBpbmdlc3Rpb24gKHNldHVwLWV2ZW50cywgc2NvcGVkLWluZ2VzdCknLFxuICAgICAgICBjb21tYW5kOiBgcGlwZWxpbmUtbWFuYWdlciBpbmZyYSBzZXR1cC1ldmVudHMgLS1zY29wZWQtaW5nZXN0JHtyZWdpb259YCxcbiAgICAgICAgZW52LFxuICAgICAgfSk7XG4gICAgfSBlbHNlIHtcbiAgICAgIHNraXBwZWQucHVzaCh7IGlkOiAnZXZlbnRzJywgcmVhc29uOiBgbm90IGFwcGxpY2FibGUgdG8gdGFyZ2V0ICcke29wdHMudGFyZ2V0fScgKGVjMi9la3Mgb25seSlgIH0pO1xuICAgIH1cbiAgfVxuXG4gIG9wdHMuc3RlcHMuZm9yRWFjaCgoY29tbWFuZCwgaSkgPT4ge1xuICAgIGlmIChjb21tYW5kLnRyaW0oKSAhPT0gJycpIHN0ZXBzLnB1c2goeyBpZDogYGN1c3RvbS0ke2kgKyAxfWAsIGxhYmVsOiBgQ3VzdG9tIHN0ZXAgJHtpICsgMX1gLCBjb21tYW5kIH0pO1xuICB9KTtcblxuICByZXR1cm4geyBzdGVwcywgc2tpcHBlZCB9O1xufVxuIl19
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import type { TargetId } from './targets.js';
|
|
2
|
+
export interface PrereqCheck {
|
|
3
|
+
readonly name: string;
|
|
4
|
+
readonly ok: boolean;
|
|
5
|
+
readonly detail: string;
|
|
6
|
+
/** A failing required check blocks deployment; advisory ones are warnings. */
|
|
7
|
+
readonly required: boolean;
|
|
8
|
+
}
|
|
9
|
+
/** True when git is on PATH (required to bootstrap a clone at all). */
|
|
10
|
+
export declare function gitAvailable(): boolean;
|
|
11
|
+
/**
|
|
12
|
+
* Cone `sparse-checkout` (+ `--filter=blob:none`) needs git ≥ 2.27. Below that,
|
|
13
|
+
* the bootstrap falls back to a (correct, just larger) full clone.
|
|
14
|
+
*/
|
|
15
|
+
export declare function gitSupportsSparseCheckout(): boolean;
|
|
16
|
+
/**
|
|
17
|
+
* Run the read-only prerequisite checks for a target. Pure inspection — safe to
|
|
18
|
+
* run unconditionally (the plan assembles this before the gated execution).
|
|
19
|
+
*/
|
|
20
|
+
export declare function checkPrereqs(target: TargetId, opts?: {
|
|
21
|
+
bootstrap?: boolean;
|
|
22
|
+
withPlugins?: boolean;
|
|
23
|
+
}): PrereqCheck[];
|
|
24
|
+
/** True when every REQUIRED prerequisite passed. */
|
|
25
|
+
export declare function prereqsSatisfied(checks: readonly PrereqCheck[]): boolean;
|
|
26
|
+
//# sourceMappingURL=prereqs.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"prereqs.d.ts","sourceRoot":"","sources":["../../src/agent/prereqs.ts"],"names":[],"mappings":"AAWA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,cAAc,CAAC;AAE7C,MAAM,WAAW,WAAW;IAC1B,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,EAAE,EAAE,OAAO,CAAC;IACrB,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,8EAA8E;IAC9E,QAAQ,CAAC,QAAQ,EAAE,OAAO,CAAC;CAC5B;AA0CD,uEAAuE;AACvE,wBAAgB,YAAY,IAAI,OAAO,CAEtC;AAED;;;GAGG;AACH,wBAAgB,yBAAyB,IAAI,OAAO,CAGnD;AAWD;;;GAGG;AACH,wBAAgB,YAAY,CAAC,MAAM,EAAE,QAAQ,EAAE,IAAI,GAAE;IAAE,SAAS,CAAC,EAAE,OAAO,CAAC;IAAC,WAAW,CAAC,EAAE,OAAO,CAAA;CAAO,GAAG,WAAW,EAAE,CAgEvH;AAED,oDAAoD;AACpD,wBAAgB,gBAAgB,CAAC,MAAM,EAAE,SAAS,WAAW,EAAE,GAAG,OAAO,CAExE"}
|