@forgezero/agent 0.1.53 → 0.1.56
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +118 -9
- package/dist/agent-heartbeat.js +1 -1
- package/dist/bootstrap.js +110 -69
- package/dist/community-rehearsal-host.d.ts +7 -1
- package/dist/community-rehearsal-host.js +64 -6
- package/dist/container-supervisor.d.ts +36 -0
- package/dist/definition.js +37 -5
- package/dist/deploy-actions.d.ts +87 -0
- package/dist/deploy-compiler.d.ts +26 -0
- package/dist/deploy-compiler.js +1050 -0
- package/dist/deploy-file.js +44 -12
- package/dist/deploy-plan-runner.d.ts +65 -0
- package/dist/deploy-plan-runner.js +1205 -0
- package/dist/deploy-plan.d.ts +41 -0
- package/dist/deploy-plan.js +925 -0
- package/dist/deploy-providers.d.ts +85 -0
- package/dist/deploy.d.ts +439 -0
- package/dist/deploy.js +169 -0
- package/dist/deployment.d.ts +5 -0
- package/dist/fz-agent.js +2137 -326
- package/dist/fz.js +1492 -320
- package/dist/metal-bootstrap.js +1 -1
- package/dist/operator-bootstrap.d.ts +1 -1
- package/dist/operator-bootstrap.js +212 -130
- package/dist/platform-bootstrap-runtime.js +1 -1
- package/dist/platform-fleet-verification.js +499 -120
- package/dist/platform-genesis.d.ts +9 -0
- package/dist/provision.js +399 -20
- package/dist/software-helper.d.ts +5 -0
- package/dist/software-helper.js +399 -18
- package/dist/software.d.ts +1 -1
- package/dist/software.js +37 -5
- package/dist/version.d.ts +1 -1
- package/package.json +17 -1
|
@@ -4,6 +4,7 @@ import {
|
|
|
4
4
|
accessSync,
|
|
5
5
|
chmodSync,
|
|
6
6
|
copyFileSync,
|
|
7
|
+
existsSync,
|
|
7
8
|
mkdtempSync,
|
|
8
9
|
mkdirSync,
|
|
9
10
|
readFileSync,
|
|
@@ -24,6 +25,7 @@ var OS_CATALOG = [
|
|
|
24
25
|
];
|
|
25
26
|
var SOFTWARE_CATALOG = [
|
|
26
27
|
{ id: "bun", version: "1.3.14", status: "active", os: "ubuntu", osVersion: "26.04", architecture: "x64", evidence: "reviewed-strategy-and-tests" },
|
|
28
|
+
{ id: "docker", version: "ubuntu-26.04", status: "active", os: "ubuntu", osVersion: "26.04", architecture: "x64", evidence: "reviewed-strategy-and-tests" },
|
|
27
29
|
{ id: "nginx", version: "ubuntu-26.04", status: "active", os: "ubuntu", osVersion: "26.04", architecture: "x64", evidence: "reviewed-strategy-and-tests" },
|
|
28
30
|
{ id: "arangodb", version: "3.11.14", status: "active", os: "ubuntu", osVersion: "26.04", architecture: "x64", evidence: "reviewed-strategy-and-tests" },
|
|
29
31
|
{ id: "cloudflared", version: "2026.7.3", status: "active", os: "ubuntu", osVersion: "26.04", architecture: "x64", evidence: "reviewed-strategy-and-tests" },
|
|
@@ -33,6 +35,7 @@ var SOFTWARE_CATALOG = [
|
|
|
33
35
|
];
|
|
34
36
|
var UBUNTU_2604_X64 = [
|
|
35
37
|
{ requirement: { id: "bun", version: "1.3.14" } },
|
|
38
|
+
{ requirement: { id: "docker", version: "ubuntu-26.04" } },
|
|
36
39
|
{ requirement: { id: "nginx", version: "ubuntu-26.04" } },
|
|
37
40
|
{ requirement: { id: "arangodb", version: "3.11.14" } },
|
|
38
41
|
{ requirement: { id: "cloudflared", version: "2026.7.3" } },
|
|
@@ -41,6 +44,15 @@ var UBUNTU_2604_X64 = [
|
|
|
41
44
|
{ requirement: { id: "openssh-client", version: "ubuntu-26.04" } }
|
|
42
45
|
];
|
|
43
46
|
var path = "/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin";
|
|
47
|
+
var DOCKER_DAEMON_PATH = "/etc/docker/daemon.json";
|
|
48
|
+
var DOCKER_DAEMON_CONFIG = `${JSON.stringify({
|
|
49
|
+
"data-root": "/var/lib/docker",
|
|
50
|
+
"storage-driver": "overlay2",
|
|
51
|
+
"live-restore": true,
|
|
52
|
+
"log-driver": "local",
|
|
53
|
+
"log-opts": { "max-size": "10m", "max-file": "3" }
|
|
54
|
+
}, null, 2)}
|
|
55
|
+
`;
|
|
44
56
|
var softwareSpawnFailure = (cause, argv) => cause.code === "ENOENT" ? { exitCode: 127, output: `executable is absent: ${argv[0]}` } : undefined;
|
|
45
57
|
var runSoftwareCommand = async (argv, env = {}) => {
|
|
46
58
|
let child;
|
|
@@ -83,6 +95,15 @@ async function executeSoftwareOperation(operation) {
|
|
|
83
95
|
if (operation.kind === "check") {
|
|
84
96
|
if (software === "bun")
|
|
85
97
|
return run(["/usr/local/bin/bun", "--version"]).then((r) => ({ ...r, exitCode: successful(r, /^1\.3\.14\s*$/m) ? 0 : 1 }));
|
|
98
|
+
if (software === "docker") {
|
|
99
|
+
const binary = await run(["/usr/bin/docker", "--version"]);
|
|
100
|
+
if (!successful(binary, /Docker version/))
|
|
101
|
+
return { ...binary, exitCode: 1 };
|
|
102
|
+
if (!existsSync(DOCKER_DAEMON_PATH) || readFileSync(DOCKER_DAEMON_PATH, "utf8") !== DOCKER_DAEMON_CONFIG)
|
|
103
|
+
return { exitCode: 1, output: "Docker daemon policy is absent or differs from the reviewed ForgeZero policy" };
|
|
104
|
+
const active = await run(["/usr/bin/systemctl", "is-active", "--quiet", "docker.service"]);
|
|
105
|
+
return active.exitCode === 0 ? { exitCode: 0, output: binary.output } : active;
|
|
106
|
+
}
|
|
86
107
|
if (software === "nginx") {
|
|
87
108
|
const binary = await run(["/usr/sbin/nginx", "-v"]);
|
|
88
109
|
return binary.exitCode === 0 ? run(["/usr/bin/systemctl", "is-active", "--quiet", "nginx.service"]) : binary;
|
|
@@ -115,11 +136,22 @@ async function executeSoftwareOperation(operation) {
|
|
|
115
136
|
return { exitCode: 1, output: cause instanceof Error ? cause.message : String(cause) };
|
|
116
137
|
}
|
|
117
138
|
}
|
|
118
|
-
if (software === "nginx" || software === "ufw" || software === "openssh-client") {
|
|
119
|
-
const
|
|
120
|
-
|
|
139
|
+
if (software === "docker" || software === "nginx" || software === "ufw" || software === "openssh-client") {
|
|
140
|
+
const packageName = software === "openssh-client" ? "openssh-client" : software === "docker" ? "docker.io" : software;
|
|
141
|
+
const installed = await aptInstall(packageName);
|
|
142
|
+
if (installed.exitCode !== 0 || software !== "nginx" && software !== "docker")
|
|
121
143
|
return installed;
|
|
122
|
-
|
|
144
|
+
if (software === "docker") {
|
|
145
|
+
mkdirSync("/etc/docker", { recursive: true, mode: 493 });
|
|
146
|
+
if (existsSync(DOCKER_DAEMON_PATH) && readFileSync(DOCKER_DAEMON_PATH, "utf8") !== DOCKER_DAEMON_CONFIG) {
|
|
147
|
+
return { exitCode: 2, output: "refusing to overwrite an existing Docker daemon configuration that differs from the reviewed ForgeZero policy" };
|
|
148
|
+
}
|
|
149
|
+
if (!existsSync(DOCKER_DAEMON_PATH))
|
|
150
|
+
writeFileSync(DOCKER_DAEMON_PATH, DOCKER_DAEMON_CONFIG, { mode: 420, flag: "wx" });
|
|
151
|
+
const enabled = await run(["/usr/bin/systemctl", "enable", "docker.service"]);
|
|
152
|
+
return enabled.exitCode === 0 ? run(["/usr/bin/systemctl", "restart", "docker.service"]) : enabled;
|
|
153
|
+
}
|
|
154
|
+
return run(["/usr/bin/systemctl", "enable", "--now", `${software}.service`]);
|
|
123
155
|
}
|
|
124
156
|
const directory = mkdtempSync(join(tmpdir(), "forgezero-software-"));
|
|
125
157
|
try {
|
|
@@ -204,7 +236,7 @@ function validateSoftwareRequirements(value, _options = {}) {
|
|
|
204
236
|
if (Object.keys(row).some((key) => key !== "id" && key !== "version")) {
|
|
205
237
|
throw new Error("software requirement contains an unknown field");
|
|
206
238
|
}
|
|
207
|
-
if (!["bun", "nginx", "arangodb", "cloudflared", "cloudflare-warp", "ufw", "openssh-client"].includes(String(row.id)) || typeof row.version !== "string" || !/^[A-Za-z0-9][A-Za-z0-9.-]{0,31}$/.test(row.version)) {
|
|
239
|
+
if (!["bun", "docker", "nginx", "arangodb", "cloudflared", "cloudflare-warp", "ufw", "openssh-client"].includes(String(row.id)) || typeof row.version !== "string" || !/^[A-Za-z0-9][A-Za-z0-9.-]{0,31}$/.test(row.version)) {
|
|
208
240
|
throw new Error("software requirement coordinate is invalid");
|
|
209
241
|
}
|
|
210
242
|
const requirement = { id: row.id, version: row.version };
|
|
@@ -253,6 +285,7 @@ import { dirname } from "node:path";
|
|
|
253
285
|
var COMMUNITY_REHEARSAL_VERSION = "3.11.14";
|
|
254
286
|
var COMMUNITY_REHEARSAL_ROOT = "/opt/forgezero-rehearsals/community-cluster-api";
|
|
255
287
|
var COMMUNITY_DATABASE_ROOT = "/var/lib/forgezero-rehearsal-cluster";
|
|
288
|
+
var COMMUNITY_CREDENTIAL_PARENT = "/etc/forgezero-rehearsal";
|
|
256
289
|
var COMMUNITY_CREDENTIAL_ROOT = "/etc/forgezero-rehearsal/creds";
|
|
257
290
|
var NODES = new Map([
|
|
258
291
|
["dev-fz-n1", { address: "10.42.0.21", agency: true }],
|
|
@@ -294,6 +327,12 @@ function parseCommunityRehearsalHostRequest(value) {
|
|
|
294
327
|
}
|
|
295
328
|
return row;
|
|
296
329
|
}
|
|
330
|
+
if (action === "cleanup") {
|
|
331
|
+
if (Object.keys(row).some((key) => !["action", "node"].includes(key))) {
|
|
332
|
+
throw new Error("community rehearsal cleanup has unknown fields");
|
|
333
|
+
}
|
|
334
|
+
return { action, node: node.name };
|
|
335
|
+
}
|
|
297
336
|
if (["database-enable", "api-enable", "database-ready", "starter-ready", "api-ready", "database-status", "api-status"].includes(String(action))) {
|
|
298
337
|
if (Object.keys(row).some((key) => !["action", "node"].includes(key)))
|
|
299
338
|
throw new Error("community rehearsal host action has unknown fields");
|
|
@@ -409,6 +448,19 @@ function planCommunityRehearsalPrepare(request) {
|
|
|
409
448
|
{ kind: "unlink", path: request.archivePath }
|
|
410
449
|
];
|
|
411
450
|
}
|
|
451
|
+
function planCommunityRehearsalCleanup() {
|
|
452
|
+
return [
|
|
453
|
+
{ kind: "exec", argv: ["/usr/bin/systemctl", "disable", "--now", "forgezero-community-api.service"], accepted: [0, 1, 5] },
|
|
454
|
+
{ kind: "exec", argv: ["/usr/bin/systemctl", "disable", "--now", "forgezero-rehearsal-db.service"], accepted: [0, 1, 5] },
|
|
455
|
+
{ kind: "unlink", path: "/etc/systemd/system/forgezero-community-api.service" },
|
|
456
|
+
{ kind: "unlink", path: "/etc/systemd/system/forgezero-rehearsal-db.service" },
|
|
457
|
+
{ kind: "remove-tree", path: COMMUNITY_DATABASE_ROOT },
|
|
458
|
+
{ kind: "remove-tree", path: COMMUNITY_REHEARSAL_ROOT },
|
|
459
|
+
{ kind: "remove-tree", path: COMMUNITY_CREDENTIAL_PARENT },
|
|
460
|
+
{ kind: "exec", argv: ["/usr/bin/systemctl", "daemon-reload"] },
|
|
461
|
+
{ kind: "exec", argv: ["/usr/bin/systemctl", "reset-failed", "forgezero-community-api.service", "forgezero-rehearsal-db.service"], accepted: [0, 1, 5] }
|
|
462
|
+
];
|
|
463
|
+
}
|
|
412
464
|
async function exec(argv, stdin) {
|
|
413
465
|
const child = Bun.spawn([...argv], { stdin: stdin === undefined ? "ignore" : "pipe", stdout: "pipe", stderr: "pipe" });
|
|
414
466
|
if (stdin !== undefined && child.stdin && typeof child.stdin !== "number") {
|
|
@@ -471,6 +523,10 @@ async function runCommunityRehearsalHost(request) {
|
|
|
471
523
|
if ((process.getuid?.() ?? -1) !== 0)
|
|
472
524
|
throw new Error("community-rehearsal must run as root");
|
|
473
525
|
const node = exactNode(request.node);
|
|
526
|
+
if (request.action === "cleanup") {
|
|
527
|
+
await applyOperations(planCommunityRehearsalCleanup());
|
|
528
|
+
return { ok: true, action: request.action, node: node.name };
|
|
529
|
+
}
|
|
474
530
|
if (request.action === "prepare") {
|
|
475
531
|
const metadata = lstatSync(request.archivePath);
|
|
476
532
|
if (!metadata.isFile() || metadata.isSymbolicLink() || metadata.size < 1 || metadata.size > 64 * 1024 * 1024 || createHash2("sha256").update(readFileSync2(request.archivePath)).digest("hex") !== request.archiveSha256) {
|
|
@@ -522,10 +578,12 @@ async function runCommunityRehearsalHost(request) {
|
|
|
522
578
|
export {
|
|
523
579
|
runCommunityRehearsalHost,
|
|
524
580
|
planCommunityRehearsalPrepare,
|
|
581
|
+
planCommunityRehearsalCleanup,
|
|
525
582
|
parseCommunityRehearsalHostRequest,
|
|
526
583
|
ensureCommunityRehearsalArango,
|
|
527
584
|
COMMUNITY_REHEARSAL_VERSION,
|
|
528
585
|
COMMUNITY_REHEARSAL_ROOT,
|
|
529
586
|
COMMUNITY_DATABASE_ROOT,
|
|
530
|
-
COMMUNITY_CREDENTIAL_ROOT
|
|
587
|
+
COMMUNITY_CREDENTIAL_ROOT,
|
|
588
|
+
COMMUNITY_CREDENTIAL_PARENT
|
|
531
589
|
};
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import type { ApplicationComponent } from './deploy';
|
|
2
|
+
import type { ServiceActivationState } from './service-supervisor';
|
|
3
|
+
export interface ContainerBuildRequest {
|
|
4
|
+
key: string;
|
|
5
|
+
revision: string;
|
|
6
|
+
root: string;
|
|
7
|
+
release: string;
|
|
8
|
+
component: string;
|
|
9
|
+
application: ApplicationComponent;
|
|
10
|
+
noCache?: boolean;
|
|
11
|
+
}
|
|
12
|
+
export interface ContainerActivateRequest extends ContainerBuildRequest {
|
|
13
|
+
image: string;
|
|
14
|
+
}
|
|
15
|
+
export interface ContainerBuildResult {
|
|
16
|
+
image: string;
|
|
17
|
+
digest: string;
|
|
18
|
+
}
|
|
19
|
+
export interface ContainerHost {
|
|
20
|
+
realpath(path: string): string;
|
|
21
|
+
exists(path: string): boolean;
|
|
22
|
+
read(path: string): string;
|
|
23
|
+
write(path: string, content: string, mode: number): void;
|
|
24
|
+
remove(path: string): void;
|
|
25
|
+
list(path: string): string[];
|
|
26
|
+
mkdir(path: string, mode: number): void;
|
|
27
|
+
exec(argv: readonly string[]): Promise<{
|
|
28
|
+
exitCode: number;
|
|
29
|
+
output: string;
|
|
30
|
+
}>;
|
|
31
|
+
health(port: number, path: string, timeoutMs: number, method: 'GET' | 'HEAD', expectedStatus: readonly number[]): Promise<boolean>;
|
|
32
|
+
sleep(ms: number): Promise<void>;
|
|
33
|
+
now(): number;
|
|
34
|
+
}
|
|
35
|
+
export declare function buildContainerImage(requestValue: ContainerBuildRequest, host?: ContainerHost): Promise<ContainerBuildResult>;
|
|
36
|
+
export declare function activateContainer(requestValue: ContainerActivateRequest, host?: ContainerHost): Promise<ServiceActivationState>;
|
package/dist/definition.js
CHANGED
|
@@ -4,6 +4,7 @@ import {
|
|
|
4
4
|
accessSync,
|
|
5
5
|
chmodSync,
|
|
6
6
|
copyFileSync,
|
|
7
|
+
existsSync,
|
|
7
8
|
mkdtempSync,
|
|
8
9
|
mkdirSync,
|
|
9
10
|
readFileSync,
|
|
@@ -24,6 +25,7 @@ var OS_CATALOG = [
|
|
|
24
25
|
];
|
|
25
26
|
var SOFTWARE_CATALOG = [
|
|
26
27
|
{ id: "bun", version: "1.3.14", status: "active", os: "ubuntu", osVersion: "26.04", architecture: "x64", evidence: "reviewed-strategy-and-tests" },
|
|
28
|
+
{ id: "docker", version: "ubuntu-26.04", status: "active", os: "ubuntu", osVersion: "26.04", architecture: "x64", evidence: "reviewed-strategy-and-tests" },
|
|
27
29
|
{ id: "nginx", version: "ubuntu-26.04", status: "active", os: "ubuntu", osVersion: "26.04", architecture: "x64", evidence: "reviewed-strategy-and-tests" },
|
|
28
30
|
{ id: "arangodb", version: "3.11.14", status: "active", os: "ubuntu", osVersion: "26.04", architecture: "x64", evidence: "reviewed-strategy-and-tests" },
|
|
29
31
|
{ id: "cloudflared", version: "2026.7.3", status: "active", os: "ubuntu", osVersion: "26.04", architecture: "x64", evidence: "reviewed-strategy-and-tests" },
|
|
@@ -33,6 +35,7 @@ var SOFTWARE_CATALOG = [
|
|
|
33
35
|
];
|
|
34
36
|
var UBUNTU_2604_X64 = [
|
|
35
37
|
{ requirement: { id: "bun", version: "1.3.14" } },
|
|
38
|
+
{ requirement: { id: "docker", version: "ubuntu-26.04" } },
|
|
36
39
|
{ requirement: { id: "nginx", version: "ubuntu-26.04" } },
|
|
37
40
|
{ requirement: { id: "arangodb", version: "3.11.14" } },
|
|
38
41
|
{ requirement: { id: "cloudflared", version: "2026.7.3" } },
|
|
@@ -41,6 +44,15 @@ var UBUNTU_2604_X64 = [
|
|
|
41
44
|
{ requirement: { id: "openssh-client", version: "ubuntu-26.04" } }
|
|
42
45
|
];
|
|
43
46
|
var path = "/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin";
|
|
47
|
+
var DOCKER_DAEMON_PATH = "/etc/docker/daemon.json";
|
|
48
|
+
var DOCKER_DAEMON_CONFIG = `${JSON.stringify({
|
|
49
|
+
"data-root": "/var/lib/docker",
|
|
50
|
+
"storage-driver": "overlay2",
|
|
51
|
+
"live-restore": true,
|
|
52
|
+
"log-driver": "local",
|
|
53
|
+
"log-opts": { "max-size": "10m", "max-file": "3" }
|
|
54
|
+
}, null, 2)}
|
|
55
|
+
`;
|
|
44
56
|
var softwareSpawnFailure = (cause, argv) => cause.code === "ENOENT" ? { exitCode: 127, output: `executable is absent: ${argv[0]}` } : undefined;
|
|
45
57
|
var runSoftwareCommand = async (argv, env = {}) => {
|
|
46
58
|
let child;
|
|
@@ -83,6 +95,15 @@ async function executeSoftwareOperation(operation) {
|
|
|
83
95
|
if (operation.kind === "check") {
|
|
84
96
|
if (software === "bun")
|
|
85
97
|
return run(["/usr/local/bin/bun", "--version"]).then((r) => ({ ...r, exitCode: successful(r, /^1\.3\.14\s*$/m) ? 0 : 1 }));
|
|
98
|
+
if (software === "docker") {
|
|
99
|
+
const binary = await run(["/usr/bin/docker", "--version"]);
|
|
100
|
+
if (!successful(binary, /Docker version/))
|
|
101
|
+
return { ...binary, exitCode: 1 };
|
|
102
|
+
if (!existsSync(DOCKER_DAEMON_PATH) || readFileSync(DOCKER_DAEMON_PATH, "utf8") !== DOCKER_DAEMON_CONFIG)
|
|
103
|
+
return { exitCode: 1, output: "Docker daemon policy is absent or differs from the reviewed ForgeZero policy" };
|
|
104
|
+
const active = await run(["/usr/bin/systemctl", "is-active", "--quiet", "docker.service"]);
|
|
105
|
+
return active.exitCode === 0 ? { exitCode: 0, output: binary.output } : active;
|
|
106
|
+
}
|
|
86
107
|
if (software === "nginx") {
|
|
87
108
|
const binary = await run(["/usr/sbin/nginx", "-v"]);
|
|
88
109
|
return binary.exitCode === 0 ? run(["/usr/bin/systemctl", "is-active", "--quiet", "nginx.service"]) : binary;
|
|
@@ -115,11 +136,22 @@ async function executeSoftwareOperation(operation) {
|
|
|
115
136
|
return { exitCode: 1, output: cause instanceof Error ? cause.message : String(cause) };
|
|
116
137
|
}
|
|
117
138
|
}
|
|
118
|
-
if (software === "nginx" || software === "ufw" || software === "openssh-client") {
|
|
119
|
-
const
|
|
120
|
-
|
|
139
|
+
if (software === "docker" || software === "nginx" || software === "ufw" || software === "openssh-client") {
|
|
140
|
+
const packageName = software === "openssh-client" ? "openssh-client" : software === "docker" ? "docker.io" : software;
|
|
141
|
+
const installed = await aptInstall(packageName);
|
|
142
|
+
if (installed.exitCode !== 0 || software !== "nginx" && software !== "docker")
|
|
121
143
|
return installed;
|
|
122
|
-
|
|
144
|
+
if (software === "docker") {
|
|
145
|
+
mkdirSync("/etc/docker", { recursive: true, mode: 493 });
|
|
146
|
+
if (existsSync(DOCKER_DAEMON_PATH) && readFileSync(DOCKER_DAEMON_PATH, "utf8") !== DOCKER_DAEMON_CONFIG) {
|
|
147
|
+
return { exitCode: 2, output: "refusing to overwrite an existing Docker daemon configuration that differs from the reviewed ForgeZero policy" };
|
|
148
|
+
}
|
|
149
|
+
if (!existsSync(DOCKER_DAEMON_PATH))
|
|
150
|
+
writeFileSync(DOCKER_DAEMON_PATH, DOCKER_DAEMON_CONFIG, { mode: 420, flag: "wx" });
|
|
151
|
+
const enabled = await run(["/usr/bin/systemctl", "enable", "docker.service"]);
|
|
152
|
+
return enabled.exitCode === 0 ? run(["/usr/bin/systemctl", "restart", "docker.service"]) : enabled;
|
|
153
|
+
}
|
|
154
|
+
return run(["/usr/bin/systemctl", "enable", "--now", `${software}.service`]);
|
|
123
155
|
}
|
|
124
156
|
const directory = mkdtempSync(join(tmpdir(), "forgezero-software-"));
|
|
125
157
|
try {
|
|
@@ -204,7 +236,7 @@ function validateSoftwareRequirements(value, _options = {}) {
|
|
|
204
236
|
if (Object.keys(row).some((key) => key !== "id" && key !== "version")) {
|
|
205
237
|
throw new Error("software requirement contains an unknown field");
|
|
206
238
|
}
|
|
207
|
-
if (!["bun", "nginx", "arangodb", "cloudflared", "cloudflare-warp", "ufw", "openssh-client"].includes(String(row.id)) || typeof row.version !== "string" || !/^[A-Za-z0-9][A-Za-z0-9.-]{0,31}$/.test(row.version)) {
|
|
239
|
+
if (!["bun", "docker", "nginx", "arangodb", "cloudflared", "cloudflare-warp", "ufw", "openssh-client"].includes(String(row.id)) || typeof row.version !== "string" || !/^[A-Za-z0-9][A-Za-z0-9.-]{0,31}$/.test(row.version)) {
|
|
208
240
|
throw new Error("software requirement coordinate is invalid");
|
|
209
241
|
}
|
|
210
242
|
const requirement = { id: row.id, version: row.version };
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
import { type ComponentDefinition, type Condition, type CredentialDefinition, type JsonObject, type RequirementDefinition, type RetryDefinition, type StepScope, type WorkflowStepDefinition } from './deploy';
|
|
2
|
+
interface StepOptions {
|
|
3
|
+
scope: StepScope;
|
|
4
|
+
if?: Condition;
|
|
5
|
+
timeoutMs?: number;
|
|
6
|
+
retry?: RetryDefinition;
|
|
7
|
+
failure?: WorkflowStepDefinition['failure'];
|
|
8
|
+
compensate?: WorkflowStepDefinition['compensate'];
|
|
9
|
+
}
|
|
10
|
+
export declare const actions: {
|
|
11
|
+
readonly software: {
|
|
12
|
+
readonly ensure: (withValue: {
|
|
13
|
+
requirements: readonly string[];
|
|
14
|
+
}, options: StepOptions) => WorkflowStepDefinition<{
|
|
15
|
+
requirements: readonly string[];
|
|
16
|
+
}>;
|
|
17
|
+
};
|
|
18
|
+
readonly exec: {
|
|
19
|
+
readonly argv: (withValue: {
|
|
20
|
+
component: string;
|
|
21
|
+
argv: readonly string[];
|
|
22
|
+
credentials?: Readonly<Record<string, string>>;
|
|
23
|
+
}, options: StepOptions) => WorkflowStepDefinition<{
|
|
24
|
+
component: string;
|
|
25
|
+
argv: readonly string[];
|
|
26
|
+
credentials?: Readonly<Record<string, string>>;
|
|
27
|
+
}>;
|
|
28
|
+
};
|
|
29
|
+
readonly container: {
|
|
30
|
+
readonly build: (withValue: {
|
|
31
|
+
component: string;
|
|
32
|
+
noCache?: boolean;
|
|
33
|
+
}, options: StepOptions) => WorkflowStepDefinition<{
|
|
34
|
+
component: string;
|
|
35
|
+
noCache?: boolean;
|
|
36
|
+
}>;
|
|
37
|
+
readonly pull: (withValue: {
|
|
38
|
+
component: string;
|
|
39
|
+
}, options: StepOptions) => WorkflowStepDefinition<{
|
|
40
|
+
component: string;
|
|
41
|
+
}>;
|
|
42
|
+
};
|
|
43
|
+
readonly service: {
|
|
44
|
+
readonly deploy: (withValue: {
|
|
45
|
+
component: string;
|
|
46
|
+
imageDigest?: string | {
|
|
47
|
+
$ref: `steps.${string}.outputs.${string}`;
|
|
48
|
+
};
|
|
49
|
+
}, options: StepOptions) => WorkflowStepDefinition<{
|
|
50
|
+
component: string;
|
|
51
|
+
imageDigest?: string | {
|
|
52
|
+
$ref: `steps.${string}.outputs.${string}`;
|
|
53
|
+
};
|
|
54
|
+
}>;
|
|
55
|
+
readonly health: (withValue: {
|
|
56
|
+
component: string;
|
|
57
|
+
}, options: StepOptions) => WorkflowStepDefinition<{
|
|
58
|
+
component: string;
|
|
59
|
+
}>;
|
|
60
|
+
readonly promote: (withValue: {
|
|
61
|
+
component: string;
|
|
62
|
+
imageDigest?: string | {
|
|
63
|
+
$ref: `steps.${string}.outputs.${string}`;
|
|
64
|
+
};
|
|
65
|
+
}, options: StepOptions) => WorkflowStepDefinition<{
|
|
66
|
+
component: string;
|
|
67
|
+
imageDigest?: string | {
|
|
68
|
+
$ref: `steps.${string}.outputs.${string}`;
|
|
69
|
+
};
|
|
70
|
+
}>;
|
|
71
|
+
};
|
|
72
|
+
readonly storage: {
|
|
73
|
+
readonly verify: (withValue: {
|
|
74
|
+
component: string;
|
|
75
|
+
}, options: StepOptions) => WorkflowStepDefinition<{
|
|
76
|
+
component: string;
|
|
77
|
+
}>;
|
|
78
|
+
};
|
|
79
|
+
readonly extension: <T extends JsonObject>(uses: `${string}@${number}`, withValue: T, options: StepOptions) => WorkflowStepDefinition<T>;
|
|
80
|
+
};
|
|
81
|
+
/** Close package-owned action inputs at runtime. Third-party actions stay namespaced and are closed by their installed handler contract. */
|
|
82
|
+
export declare function validateBuiltInAction(definition: WorkflowStepDefinition, context: {
|
|
83
|
+
components: Readonly<Record<string, ComponentDefinition>>;
|
|
84
|
+
requirements: Readonly<Record<string, RequirementDefinition>>;
|
|
85
|
+
credentials: Readonly<Record<string, CredentialDefinition>>;
|
|
86
|
+
}, where: string): void;
|
|
87
|
+
export {};
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { type DeploymentPlan } from './deploy-plan';
|
|
2
|
+
export declare const DEPLOY_SOURCE_FILE = "forgezero.deploy.ts";
|
|
3
|
+
export declare const DEPLOY_PLAN_FILE = ".fz/deploy.plan.json";
|
|
4
|
+
export interface DeploymentCompilation {
|
|
5
|
+
source: string;
|
|
6
|
+
output: string;
|
|
7
|
+
plan: DeploymentPlan;
|
|
8
|
+
digest: `sha256:${string}`;
|
|
9
|
+
changed: boolean;
|
|
10
|
+
}
|
|
11
|
+
export declare function defaultTypeScriptDeployment(name: string): string;
|
|
12
|
+
export declare function initializeTypeScriptDeployment(rootValue: string, options: {
|
|
13
|
+
name: string;
|
|
14
|
+
force?: boolean;
|
|
15
|
+
}): string;
|
|
16
|
+
export declare function loadDeploymentSource(root: string, sourceFile?: string): Promise<unknown>;
|
|
17
|
+
export declare function compileDeploymentProject(rootValue: string, options?: {
|
|
18
|
+
sourceFile?: string;
|
|
19
|
+
outputFile?: string;
|
|
20
|
+
write?: boolean;
|
|
21
|
+
}): Promise<DeploymentCompilation>;
|
|
22
|
+
export declare function inspectCompiledDeployment(rootValue: string, outputFile?: string): {
|
|
23
|
+
path: string;
|
|
24
|
+
plan: DeploymentPlan;
|
|
25
|
+
digest: `sha256:${string}`;
|
|
26
|
+
};
|