@forgezero/agent 0.1.28 → 0.1.30
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 +24 -4
- package/dist/agent-heartbeat.js +1 -1
- package/dist/cli/agent-install.d.ts +1 -1
- package/dist/definition.d.ts +14 -8
- package/dist/definition.js +58 -23
- package/dist/deployment-pull.d.ts +3 -3
- package/dist/deployment.d.ts +5 -5
- package/dist/fz-agent.js +80 -48
- package/dist/fz.js +393 -31
- package/dist/index.d.ts +2 -2
- package/dist/metal-helper-socket.js +2 -3
- package/dist/metal-provision.js +2 -3
- package/dist/project-context.d.ts +37 -0
- package/dist/project-context.js +266 -0
- package/dist/provision.d.ts +9 -1
- package/dist/provision.js +85 -17
- package/dist/software-helper.js +17 -2
- package/dist/software.d.ts +24 -2
- package/dist/software.js +20 -3
- package/dist/version.d.ts +1 -1
- package/package.json +6 -2
package/dist/fz-agent.js
CHANGED
|
@@ -4886,6 +4886,16 @@ import { readFileSync } from "fs";
|
|
|
4886
4886
|
var BUN_INSTALLER_SHA256 = "bab8acfb046aac8c72407bdcce903957665d655d7acaa3e11c7c4616beae68dd";
|
|
4887
4887
|
var ARANGO_SHA256 = "b5a9197b4343f2ed554e1ebc1ef8e6529c7c39cde0035cdc311a4747a3355066";
|
|
4888
4888
|
var CLOUDFLARED_SHA256 = "9d71c677db00134c1bd4144b7783486b654ad281b1ea62b4972098d19f770f17";
|
|
4889
|
+
var OS_CATALOG = [
|
|
4890
|
+
{ id: "ubuntu", version: "26.04", architecture: "x64", status: "active" }
|
|
4891
|
+
];
|
|
4892
|
+
var SOFTWARE_CATALOG = [
|
|
4893
|
+
{ id: "bun", version: "1.3.14", status: "active", os: "ubuntu", osVersion: "26.04", architecture: "x64", evidence: "reviewed-strategy-and-tests" },
|
|
4894
|
+
{ id: "nginx", version: "ubuntu-26.04", status: "active", os: "ubuntu", osVersion: "26.04", architecture: "x64", evidence: "reviewed-strategy-and-tests" },
|
|
4895
|
+
{ id: "arangodb", version: "3.11.14", status: "active", os: "ubuntu", osVersion: "26.04", architecture: "x64", evidence: "reviewed-strategy-and-tests" },
|
|
4896
|
+
{ id: "cloudflared", version: "2026.7.3", status: "active", os: "ubuntu", osVersion: "26.04", architecture: "x64", evidence: "reviewed-strategy-and-tests" },
|
|
4897
|
+
{ id: "ufw", version: "ubuntu-26.04", status: "active", os: "ubuntu", osVersion: "26.04", architecture: "x64", evidence: "reviewed-strategy-and-tests" }
|
|
4898
|
+
];
|
|
4889
4899
|
var UBUNTU_2604_X64 = [
|
|
4890
4900
|
{
|
|
4891
4901
|
requirement: { id: "bun", version: "1.3.14" },
|
|
@@ -4924,7 +4934,7 @@ function observeSoftwareHost(osRelease = readFileSync("/etc/os-release", "utf8")
|
|
|
4924
4934
|
architecture
|
|
4925
4935
|
};
|
|
4926
4936
|
}
|
|
4927
|
-
function validateSoftwareRequirements(value) {
|
|
4937
|
+
function validateSoftwareRequirements(value, options = {}) {
|
|
4928
4938
|
if (!Array.isArray(value) || value.length > 32)
|
|
4929
4939
|
throw new Error("software requirements must be an array of at most 32 entries");
|
|
4930
4940
|
const seen = new Set;
|
|
@@ -4942,13 +4952,18 @@ function validateSoftwareRequirements(value) {
|
|
|
4942
4952
|
if (seen.has(requirement.id))
|
|
4943
4953
|
throw new Error(`duplicate software requirement: ${requirement.id}`);
|
|
4944
4954
|
seen.add(requirement.id);
|
|
4955
|
+
const catalog = SOFTWARE_CATALOG.find((candidate) => candidate.id === requirement.id && candidate.version === requirement.version);
|
|
4956
|
+
if (!catalog || catalog.status === "retired" || options.channel !== "development" && catalog.status !== "active") {
|
|
4957
|
+
throw new Error(`software requirement is not available for ${options.channel ?? "production"}: ${requirement.id}@${requirement.version}`);
|
|
4958
|
+
}
|
|
4945
4959
|
return requirement;
|
|
4946
4960
|
});
|
|
4947
4961
|
}
|
|
4948
4962
|
async function ensureSoftwareRequirements(requirementsInput, options) {
|
|
4949
4963
|
const requirements = validateSoftwareRequirements(requirementsInput);
|
|
4950
4964
|
const observation = options.observation ?? observeSoftwareHost();
|
|
4951
|
-
|
|
4965
|
+
const os = OS_CATALOG.find((candidate) => candidate.id === observation.os.id && candidate.version === observation.os.versionId && candidate.architecture === observation.architecture);
|
|
4966
|
+
if (!os || os.status !== "active") {
|
|
4952
4967
|
throw new Error(`unsupported software strategy: ${observation.os.id} ${observation.os.versionId} ${observation.architecture}`);
|
|
4953
4968
|
}
|
|
4954
4969
|
const results = [];
|
|
@@ -4973,7 +4988,7 @@ async function ensureSoftwareRequirements(requirementsInput, options) {
|
|
|
4973
4988
|
}
|
|
4974
4989
|
|
|
4975
4990
|
// src/definition.ts
|
|
4976
|
-
var PIPELINE_VERSION =
|
|
4991
|
+
var PIPELINE_VERSION = 2;
|
|
4977
4992
|
|
|
4978
4993
|
class DefinitionError extends Error {
|
|
4979
4994
|
constructor(message) {
|
|
@@ -4998,6 +5013,7 @@ var exactKeys = (value, allowed, where) => {
|
|
|
4998
5013
|
if (unknown.length > 0)
|
|
4999
5014
|
throw new DefinitionError(`${where} contains unknown field(s): ${unknown.join(", ")}.`);
|
|
5000
5015
|
};
|
|
5016
|
+
var NAME = /^[a-z][a-z0-9-]{0,62}$/;
|
|
5001
5017
|
var RESERVED_STEP_ENV = new Set([
|
|
5002
5018
|
"PATH",
|
|
5003
5019
|
"HOME",
|
|
@@ -5012,37 +5028,52 @@ var RESERVED_STEP_ENV = new Set([
|
|
|
5012
5028
|
]);
|
|
5013
5029
|
function parseDeployDefinition(value) {
|
|
5014
5030
|
const root = record(value, "pipeline");
|
|
5015
|
-
exactKeys(root, ["version", "name", "requireAttestation", "
|
|
5031
|
+
exactKeys(root, ["$schema", "version", "name", "requireAttestation", "profiles", "steps"], "pipeline");
|
|
5032
|
+
if (root.$schema !== undefined && (typeof root.$schema !== "string" || !root.$schema.startsWith("https://"))) {
|
|
5033
|
+
throw new DefinitionError("pipeline.$schema must be an HTTPS URL.");
|
|
5034
|
+
}
|
|
5016
5035
|
if (root.version !== PIPELINE_VERSION) {
|
|
5017
5036
|
throw new DefinitionError(`pipeline.version must be ${PIPELINE_VERSION}.`);
|
|
5018
5037
|
}
|
|
5019
|
-
|
|
5020
|
-
|
|
5038
|
+
const rawProfiles = record(root.profiles, "pipeline.profiles");
|
|
5039
|
+
const profileEntries = Object.entries(rawProfiles);
|
|
5040
|
+
if (profileEntries.length === 0 || profileEntries.length > 32) {
|
|
5041
|
+
throw new DefinitionError("pipeline.profiles must contain from 1 to 32 named profiles.");
|
|
5021
5042
|
}
|
|
5022
5043
|
if (!Array.isArray(root.steps) || root.steps.length === 0) {
|
|
5023
5044
|
throw new DefinitionError("pipeline.steps must contain at least one step.");
|
|
5024
5045
|
}
|
|
5025
|
-
const
|
|
5026
|
-
|
|
5027
|
-
|
|
5028
|
-
|
|
5029
|
-
|
|
5046
|
+
const profiles = {};
|
|
5047
|
+
for (const [name, raw] of profileEntries) {
|
|
5048
|
+
if (!NAME.test(name))
|
|
5049
|
+
throw new DefinitionError(`pipeline profile name is invalid: ${name}.`);
|
|
5050
|
+
const profile = record(raw, `profiles.${name}`);
|
|
5051
|
+
exactKeys(profile, ["software"], `profiles.${name}`);
|
|
5052
|
+
if (!Array.isArray(profile.software)) {
|
|
5053
|
+
throw new DefinitionError(`profiles.${name}.software must be an array.`);
|
|
5030
5054
|
}
|
|
5031
|
-
|
|
5032
|
-
name: text(role.name, `roles[${index}].name`),
|
|
5033
|
-
software: validateSoftwareRequirements(role.software)
|
|
5034
|
-
};
|
|
5035
|
-
});
|
|
5036
|
-
if (new Set(roles.map((role) => role.name)).size !== roles.length) {
|
|
5037
|
-
throw new DefinitionError("pipeline.roles must have unique names.");
|
|
5055
|
+
profiles[name] = { software: validateSoftwareRequirements(profile.software) };
|
|
5038
5056
|
}
|
|
5039
5057
|
const phases = new Set(["build", "release", "migrate", "health"]);
|
|
5040
5058
|
const steps = root.steps.map((raw, index) => {
|
|
5041
5059
|
const step = record(raw, `steps[${index}]`);
|
|
5042
|
-
exactKeys(step, ["name", "run", "phase", "
|
|
5060
|
+
exactKeys(step, ["name", "run", "phase", "scope", "profiles", "secrets", "always", "timeoutMs", "when"], `steps[${index}]`);
|
|
5043
5061
|
const phase = text(step.phase, `steps[${index}].phase`);
|
|
5044
5062
|
if (!phases.has(phase))
|
|
5045
5063
|
throw new DefinitionError(`steps[${index}].phase is not supported.`);
|
|
5064
|
+
if (step.scope !== "target" && step.scope !== "release") {
|
|
5065
|
+
throw new DefinitionError(`steps[${index}].scope must be target or release.`);
|
|
5066
|
+
}
|
|
5067
|
+
let selectedProfiles;
|
|
5068
|
+
if (step.profiles !== undefined) {
|
|
5069
|
+
if (!Array.isArray(step.profiles) || step.profiles.length === 0 || step.profiles.some((name) => typeof name !== "string" || !Object.hasOwn(profiles, name))) {
|
|
5070
|
+
throw new DefinitionError(`steps[${index}].profiles must name existing profiles.`);
|
|
5071
|
+
}
|
|
5072
|
+
selectedProfiles = [...step.profiles];
|
|
5073
|
+
if (new Set(selectedProfiles).size !== selectedProfiles.length) {
|
|
5074
|
+
throw new DefinitionError(`steps[${index}].profiles must not contain duplicates.`);
|
|
5075
|
+
}
|
|
5076
|
+
}
|
|
5046
5077
|
if (step.secrets !== undefined && (!Array.isArray(step.secrets) || step.secrets.some((name) => typeof name !== "string" || !/^[A-Z_][A-Z0-9_]*$/.test(name)))) {
|
|
5047
5078
|
throw new DefinitionError(`steps[${index}].secrets must contain names only.`);
|
|
5048
5079
|
}
|
|
@@ -5073,8 +5104,9 @@ function parseDeployDefinition(value) {
|
|
|
5073
5104
|
name: text(step.name, `steps[${index}].name`),
|
|
5074
5105
|
run: text(step.run, `steps[${index}].run`),
|
|
5075
5106
|
phase,
|
|
5107
|
+
scope: step.scope,
|
|
5108
|
+
profiles: selectedProfiles,
|
|
5076
5109
|
secrets: step.secrets,
|
|
5077
|
-
once: step.once === true,
|
|
5078
5110
|
always: step.always === true,
|
|
5079
5111
|
timeoutMs,
|
|
5080
5112
|
when
|
|
@@ -5087,7 +5119,7 @@ function parseDeployDefinition(value) {
|
|
|
5087
5119
|
version: PIPELINE_VERSION,
|
|
5088
5120
|
name: text(root.name, "pipeline.name"),
|
|
5089
5121
|
requireAttestation: root.requireAttestation === true,
|
|
5090
|
-
|
|
5122
|
+
profiles,
|
|
5091
5123
|
steps
|
|
5092
5124
|
};
|
|
5093
5125
|
}
|
|
@@ -5217,7 +5249,7 @@ function createDeploymentManager(options) {
|
|
|
5217
5249
|
const exec = options.exec ?? shell;
|
|
5218
5250
|
const projectExec = options.projectExec ?? exec;
|
|
5219
5251
|
const now = options.now ?? Date.now;
|
|
5220
|
-
const readDefinition = options.readDefinition ?? ((path) =>
|
|
5252
|
+
const readDefinition = options.readDefinition ?? ((path) => JSON.parse(readFileSync2(path, "utf8")));
|
|
5221
5253
|
const credentialsDirectory = process.env.CREDENTIALS_DIRECTORY;
|
|
5222
5254
|
const gitCredentialPath = options.gitCredentialPath ?? (credentialsDirectory ? join(credentialsDirectory, "git-deploy-key") : undefined);
|
|
5223
5255
|
const knownHostsPath = options.knownHostsPath ?? "/etc/forgezero/git/known_hosts";
|
|
@@ -5332,22 +5364,22 @@ function createDeploymentManager(options) {
|
|
|
5332
5364
|
if (request.revision && head !== request.revision) {
|
|
5333
5365
|
throw new DeploymentError("SOURCE_FAILED", `Git checked out ${head}, not requested ${request.revision}.`);
|
|
5334
5366
|
}
|
|
5335
|
-
const definition = parseDeployDefinition(readDefinition(join(release, ".fz", "deploy.
|
|
5336
|
-
const
|
|
5337
|
-
if (!
|
|
5338
|
-
throw new DeploymentError("PIPELINE_FAILED", `pipeline
|
|
5339
|
-
if (
|
|
5367
|
+
const definition = parseDeployDefinition(readDefinition(join(release, ".fz", "deploy.json")));
|
|
5368
|
+
const selectedProfile = definition.profiles[options.profile];
|
|
5369
|
+
if (!selectedProfile)
|
|
5370
|
+
throw new DeploymentError("PIPELINE_FAILED", `pipeline profile does not exist: ${options.profile}.`);
|
|
5371
|
+
if (selectedProfile.software.length > 0) {
|
|
5340
5372
|
if (!options.ensureSoftware) {
|
|
5341
5373
|
throw new DeploymentError("PIPELINE_FAILED", "the supervised software helper is unavailable");
|
|
5342
5374
|
}
|
|
5343
|
-
await options.ensureSoftware(
|
|
5375
|
+
await options.ensureSoftware(selectedProfile.software);
|
|
5344
5376
|
}
|
|
5345
5377
|
const phaseEnvironment = {
|
|
5346
5378
|
...options.environment ?? {},
|
|
5347
5379
|
FZ_RELEASE: release,
|
|
5348
5380
|
FZ_DEPLOY_REVISION: head,
|
|
5349
5381
|
FZ_DEPLOY_BRANCH: options.branch,
|
|
5350
|
-
|
|
5382
|
+
FZ_DEPLOY_PROFILE: options.profile,
|
|
5351
5383
|
...options.publicApiUrl ? { PUBLIC_API_URL: options.publicApiUrl } : {}
|
|
5352
5384
|
};
|
|
5353
5385
|
const phaseExec = ({ command, env: secrets, timeoutMs }) => projectExec({ command, cwd: release, env: { ...phaseEnvironment, ...secrets }, timeoutMs });
|
|
@@ -5363,7 +5395,7 @@ function createDeploymentManager(options) {
|
|
|
5363
5395
|
return {
|
|
5364
5396
|
name: `${definition.name}:${phase}`,
|
|
5365
5397
|
requireAttestation: definition.requireAttestation,
|
|
5366
|
-
steps: definition.steps.filter((step) => step.phase === phase && (!step.
|
|
5398
|
+
steps: definition.steps.filter((step) => step.phase === phase && (!step.profiles || step.profiles.includes(options.profile)) && (step.scope === "target" || request.releaseExecutor === true) && (!step.when || Object.entries(step.when).every(([name, expected]) => phaseEnvironment[name] === expected)))
|
|
5367
5399
|
};
|
|
5368
5400
|
})
|
|
5369
5401
|
].filter((pipeline) => pipeline.steps.length > 0);
|
|
@@ -5400,7 +5432,7 @@ function createDeploymentManager(options) {
|
|
|
5400
5432
|
return revision.toLowerCase();
|
|
5401
5433
|
},
|
|
5402
5434
|
deploy(request = {}) {
|
|
5403
|
-
const revisionKey = request.revision ? `${request.revision.toLowerCase()}:${request.
|
|
5435
|
+
const revisionKey = request.revision ? `${request.revision.toLowerCase()}:${request.releaseExecutor === true ? "release" : "target"}` : undefined;
|
|
5404
5436
|
if (revisionKey) {
|
|
5405
5437
|
const existing = activeRevisions.get(revisionKey);
|
|
5406
5438
|
if (existing)
|
|
@@ -5592,7 +5624,7 @@ async function deployClaim(options, claim) {
|
|
|
5592
5624
|
schedule();
|
|
5593
5625
|
let result;
|
|
5594
5626
|
try {
|
|
5595
|
-
result = await manager.deploy({ revision: claim.revision,
|
|
5627
|
+
result = await manager.deploy({ revision: claim.revision, releaseExecutor: claim.releaseExecutor }).result;
|
|
5596
5628
|
} finally {
|
|
5597
5629
|
stopped = true;
|
|
5598
5630
|
clearTimer(timer);
|
|
@@ -6377,11 +6409,10 @@ ${attestationSetup}if [[ ! -x /usr/local/bin/bun ]]; then
|
|
|
6377
6409
|
install -m 0755 ${agentBun}/bin/bun /usr/local/bin/bun
|
|
6378
6410
|
rm -f /run/fz-bun-install
|
|
6379
6411
|
fi
|
|
6380
|
-
if [[ ! -x /usr/local/
|
|
6412
|
+
if [[ ! -x /usr/local/lib/forgezero/agent/fz-agent ]] || [[ "$(/usr/local/lib/forgezero/agent/fz-agent --version 2>/dev/null || true)" != "${profile.agentVersion}" ]]; then
|
|
6381
6413
|
env BUN_INSTALL=${agentBun} /usr/local/bin/bun add -g --no-cache --force @forgezero/agent@${profile.agentVersion}
|
|
6382
|
-
ln -sfn ${agentBun}/bin/fz-agent /usr/local/bin/fz-agent
|
|
6383
6414
|
fi
|
|
6384
|
-
env FZ_API=${profile.apiUrl} FZ_AGENT_BIN=/usr/local/
|
|
6415
|
+
env FZ_API=${profile.apiUrl} FZ_AGENT_BIN=/usr/local/lib/forgezero/agent/fz-agent FZ_AGENT_USER=forgezero-agent FZ_SOCKET_PATH=/run/forgezero/vault.sock FZ_SEED_CREDENTIAL_PATH=/etc/forgezero/creds/agent-seed.cred FZ_GIT_CREDENTIAL_PATH=/etc/forgezero/creds/git-deploy-key.cred FZ_GIT_PUBLIC_KEY_PATH=/etc/forgezero/git/deploy.pub FZ_DEPLOY_ROOT=/opt/forgezero FZ_DEPLOY_PULL=${hasEnrolment ? "true" : "false"} FZ_NODE_LABEL=${nodeLabel} ${agentBun}/bin/fz agent install --apply${hasEnrolment ? " --enrol" : ""}
|
|
6385
6416
|
`;
|
|
6386
6417
|
}
|
|
6387
6418
|
function cloudInit(profile, claim, manifest) {
|
|
@@ -7876,7 +7907,7 @@ function requestAgentUpdate(request, socketPath = DEFAULT_AGENT_UPDATE_SOCKET, t
|
|
|
7876
7907
|
import { readFileSync as readFileSync7 } from "fs";
|
|
7877
7908
|
|
|
7878
7909
|
// src/version.ts
|
|
7879
|
-
var VERSION2 = "0.1.
|
|
7910
|
+
var VERSION2 = "0.1.30";
|
|
7880
7911
|
|
|
7881
7912
|
// src/agent-heartbeat.ts
|
|
7882
7913
|
var unquote = (value) => value.replace(/^['"]|['"]$/g, "");
|
|
@@ -8165,7 +8196,7 @@ if (import.meta.main) {
|
|
|
8165
8196
|
" FZ_SEED_PATH legacy/dev seed file (default: " + DEFAULT_SEED_PATH + ")",
|
|
8166
8197
|
" FZ_NODE_KEY override the node key (default: derived from the seed)",
|
|
8167
8198
|
"",
|
|
8168
|
-
" deploy [--revision=<full-sha>] [--
|
|
8199
|
+
" deploy [--revision=<full-sha>] [--release-executor]",
|
|
8169
8200
|
" identity print this sealed seed's public identity",
|
|
8170
8201
|
" enrol consume a systemd-loaded compute capability",
|
|
8171
8202
|
" metal-helper --profile=/etc/forgezero/metal.json",
|
|
@@ -8222,9 +8253,9 @@ if (import.meta.main) {
|
|
|
8222
8253
|
const profilePath = args.find((arg) => arg.startsWith("--profile="))?.slice("--profile=".length);
|
|
8223
8254
|
if (!profilePath)
|
|
8224
8255
|
throw new Error("metal-helper requires --profile=/absolute/path.json");
|
|
8225
|
-
const
|
|
8256
|
+
const profile2 = JSON.parse(readFileSync8(profilePath, "utf8"));
|
|
8226
8257
|
const helper = startMetalHelper({
|
|
8227
|
-
profile,
|
|
8258
|
+
profile: profile2,
|
|
8228
8259
|
socketPath: process.env.FZ_METAL_HELPER_SOCKET ?? DEFAULT_METAL_HELPER_SOCKET
|
|
8229
8260
|
});
|
|
8230
8261
|
console.log(`[metal-helper] listening on ${process.env.FZ_METAL_HELPER_SOCKET ?? DEFAULT_METAL_HELPER_SOCKET}`);
|
|
@@ -8340,8 +8371,8 @@ if (import.meta.main) {
|
|
|
8340
8371
|
const profilePath = args.find((arg) => arg.startsWith("--profile="))?.slice("--profile=".length);
|
|
8341
8372
|
if (!profilePath)
|
|
8342
8373
|
throw new Error("metal-isolation requires --profile=/absolute/path.json");
|
|
8343
|
-
const
|
|
8344
|
-
await applyMetalIsolation(
|
|
8374
|
+
const profile2 = JSON.parse(readFileSync8(profilePath, "utf8"));
|
|
8375
|
+
await applyMetalIsolation(profile2);
|
|
8345
8376
|
console.log("[metal-isolation] host and guest cgroup boundaries active");
|
|
8346
8377
|
process.exit(0);
|
|
8347
8378
|
}
|
|
@@ -8469,7 +8500,7 @@ if (import.meta.main) {
|
|
|
8469
8500
|
op: "deploy",
|
|
8470
8501
|
request: {
|
|
8471
8502
|
revision: revisionArg?.slice("--revision=".length),
|
|
8472
|
-
|
|
8503
|
+
releaseExecutor: args.includes("--release-executor")
|
|
8473
8504
|
}
|
|
8474
8505
|
} : command2 === "cancel" ? { op: "cancel", id: idArg?.slice("--id=".length) ?? "" } : ["pause-key", "resume-key", "stop-key", "start-key"].includes(command2) ? {
|
|
8475
8506
|
op: command2,
|
|
@@ -8487,7 +8518,6 @@ if (import.meta.main) {
|
|
|
8487
8518
|
const attestationSource = existsSync13("/dev/sev-guest") ? createSnpAttestationSource() : undefined;
|
|
8488
8519
|
const running = runAgent({
|
|
8489
8520
|
socketPath: process.env.FZ_SOCKET_PATH ?? DEFAULT_SOCKET_PATH,
|
|
8490
|
-
listenFd: systemdListenFd(),
|
|
8491
8521
|
seedCredential: process.env.FZ_SEED_CREDENTIAL,
|
|
8492
8522
|
seedPath: process.env.FZ_SEED_PATH ?? DEFAULT_SEED_PATH,
|
|
8493
8523
|
nodeKey: process.env.FZ_NODE_KEY,
|
|
@@ -8577,19 +8607,19 @@ if (import.meta.main) {
|
|
|
8577
8607
|
} : systemdDeploymentSecrets;
|
|
8578
8608
|
const repository = process.env.FZ_DEPLOY_REPO;
|
|
8579
8609
|
const branch = process.env.FZ_DEPLOY_BRANCH;
|
|
8580
|
-
const
|
|
8610
|
+
const profile = process.env.FZ_DEPLOY_PROFILE;
|
|
8581
8611
|
const root = process.env.FZ_DEPLOY_ROOT;
|
|
8582
8612
|
const pullEnabled = process.env.FZ_DEPLOY_PULL === "true" && Boolean(process.env.FZ_API);
|
|
8583
8613
|
if (pullEnabled && !binding) {
|
|
8584
8614
|
throw new Error("agent: outbound guest deployment requires a persisted tenant enrolment binding");
|
|
8585
8615
|
}
|
|
8586
|
-
if (root && (repository && branch &&
|
|
8616
|
+
if (root && (repository && branch && profile || pullEnabled)) {
|
|
8587
8617
|
const managers = new Map;
|
|
8588
8618
|
const managerOptions = (source, key) => ({
|
|
8589
8619
|
key,
|
|
8590
8620
|
repository: source.repository,
|
|
8591
8621
|
branch: source.branch,
|
|
8592
|
-
|
|
8622
|
+
profile: source.profile,
|
|
8593
8623
|
sourceAuth: source.auth,
|
|
8594
8624
|
root,
|
|
8595
8625
|
publicApiUrl: process.env.FZ_PUBLIC_API_URL,
|
|
@@ -8606,7 +8636,7 @@ if (import.meta.main) {
|
|
|
8606
8636
|
ensureSoftware: (requirements) => requestSoftware(requirements, process.env.FZ_SOFTWARE_HELPER_SOCKET ?? DEFAULT_SOFTWARE_HELPER_SOCKET),
|
|
8607
8637
|
projectExec: process.env.FZ_DEPLOY_RUNNER_SOCKET ? (input) => requestDeploymentCommand(input, process.env.FZ_DEPLOY_RUNNER_SOCKET) : undefined
|
|
8608
8638
|
});
|
|
8609
|
-
const staticManager = repository && branch &&
|
|
8639
|
+
const staticManager = repository && branch && profile ? createDeploymentManager(managerOptions({ repository, branch, profile }, process.env.FZ_DEPLOY_KEY ?? `${repository}:${branch}:${profile}`)) : undefined;
|
|
8610
8640
|
if (staticManager)
|
|
8611
8641
|
managers.set("__static__", staticManager);
|
|
8612
8642
|
const control = staticManager ? startControlServer(staticManager, process.env.FZ_CONTROL_SOCKET ?? DEFAULT_CONTROL_SOCKET) : undefined;
|
|
@@ -8622,7 +8652,7 @@ if (import.meta.main) {
|
|
|
8622
8652
|
claim.pipelineKey,
|
|
8623
8653
|
claim.source.repository,
|
|
8624
8654
|
claim.source.branch,
|
|
8625
|
-
claim.source.
|
|
8655
|
+
claim.source.profile,
|
|
8626
8656
|
JSON.stringify(claim.source.auth ?? null)
|
|
8627
8657
|
].join("\x00");
|
|
8628
8658
|
const existing = managers.get(cacheKey);
|
|
@@ -8820,6 +8850,8 @@ export {
|
|
|
8820
8850
|
SUPPORTED_GUEST_IMAGE,
|
|
8821
8851
|
SOFTWARE_HELPER_UNIT_PATH,
|
|
8822
8852
|
SOFTWARE_HELPER_GROUP,
|
|
8853
|
+
SOFTWARE_CATALOG,
|
|
8854
|
+
OS_CATALOG,
|
|
8823
8855
|
MAX_AGENT_TARBALL_BYTES,
|
|
8824
8856
|
DeploymentError,
|
|
8825
8857
|
DEFAULT_SOFTWARE_HELPER_SOCKET,
|