@le-space/node 0.1.2 → 0.1.3
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/index.d.ts +194 -3
- package/index.js +669 -1
- package/package.json +4 -3
package/index.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import * as _shared_aleph_shared_types from '@shared-aleph/shared-types';
|
|
2
|
-
import { MessageSigner, InstanceAllocation, PortMapping, RuntimeDiagnostics, CrnRecord, RootfsRequiredPortForward, MessageHasher, RootfsManifest } from '@shared-aleph/shared-types';
|
|
2
|
+
import { MessageSigner, InstanceAllocation, PortMapping, RuntimeDiagnostics, CrnRecord, RootfsRequiredPortForward, MessageHasher, RootfsManifest as RootfsManifest$1 } from '@shared-aleph/shared-types';
|
|
3
3
|
|
|
4
4
|
declare function requiredEnv(name: string, env?: NodeJS.ProcessEnv): string;
|
|
5
5
|
declare function optionalEnv(name: string, fallback?: string, env?: NodeJS.ProcessEnv): string;
|
|
@@ -132,7 +132,7 @@ interface DeployExecutorDependencies {
|
|
|
132
132
|
signer?: MessageSigner;
|
|
133
133
|
sender?: string;
|
|
134
134
|
hasher?: MessageHasher;
|
|
135
|
-
manifest?: RootfsManifest | null;
|
|
135
|
+
manifest?: RootfsManifest$1 | null;
|
|
136
136
|
log?: (message: string) => void;
|
|
137
137
|
}
|
|
138
138
|
declare function executeDeployPlan(plan: DeployPlan, dependencies?: DeployExecutorDependencies): Promise<DeployOutputResult>;
|
|
@@ -216,4 +216,195 @@ declare function runActionMode(env?: NodeJS.ProcessEnv, hooks?: {
|
|
|
216
216
|
}): Promise<void>;
|
|
217
217
|
declare function main(): Promise<void>;
|
|
218
218
|
|
|
219
|
-
|
|
219
|
+
interface RootfsContractPort {
|
|
220
|
+
port: number;
|
|
221
|
+
tcp?: boolean;
|
|
222
|
+
udp?: boolean;
|
|
223
|
+
purpose?: string;
|
|
224
|
+
}
|
|
225
|
+
interface RootfsContractRootfs {
|
|
226
|
+
profile: string;
|
|
227
|
+
installMode: string;
|
|
228
|
+
installDir: string;
|
|
229
|
+
binaryPath: string;
|
|
230
|
+
dataDir: string;
|
|
231
|
+
envFile: string;
|
|
232
|
+
}
|
|
233
|
+
interface RootfsContractServices {
|
|
234
|
+
bootstrap: string;
|
|
235
|
+
main: string;
|
|
236
|
+
autotlsRefresh: string;
|
|
237
|
+
}
|
|
238
|
+
interface RootfsContractManifest {
|
|
239
|
+
copyTarget: string;
|
|
240
|
+
notes?: string;
|
|
241
|
+
}
|
|
242
|
+
interface RootfsContractSource {
|
|
243
|
+
repository?: string;
|
|
244
|
+
subdirectory?: string;
|
|
245
|
+
}
|
|
246
|
+
interface RootfsContract {
|
|
247
|
+
schemaVersion: number;
|
|
248
|
+
id: string;
|
|
249
|
+
displayName?: string;
|
|
250
|
+
source?: RootfsContractSource;
|
|
251
|
+
rootfs: RootfsContractRootfs;
|
|
252
|
+
services: RootfsContractServices;
|
|
253
|
+
ports: RootfsContractPort[];
|
|
254
|
+
manifest: RootfsContractManifest;
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
type RootfsBuildDriver = 'auto' | 'host' | 'docker';
|
|
258
|
+
interface RootfsBuildPlan {
|
|
259
|
+
contract: RootfsContract;
|
|
260
|
+
contractPath: string;
|
|
261
|
+
projectDir: string;
|
|
262
|
+
alephDir: string;
|
|
263
|
+
outDir: string;
|
|
264
|
+
driver: RootfsBuildDriver;
|
|
265
|
+
rootfsSizeMiB: number;
|
|
266
|
+
rootfsImageSize: string;
|
|
267
|
+
rootfsVersion: string;
|
|
268
|
+
channel: string;
|
|
269
|
+
skipUpload: boolean;
|
|
270
|
+
skipBuild: boolean;
|
|
271
|
+
ipfsAddUrl: string;
|
|
272
|
+
ipfsGatewayUrl: string;
|
|
273
|
+
alephApiHost: string;
|
|
274
|
+
alephMessageWaitAttempts: number;
|
|
275
|
+
alephMessageWaitDelaySeconds: number;
|
|
276
|
+
alephPinAttempts: number;
|
|
277
|
+
alephPinDelaySeconds: number;
|
|
278
|
+
ipfsGatewayWaitAttempts: number;
|
|
279
|
+
ipfsGatewayWaitDelaySeconds: number;
|
|
280
|
+
manifestPath: string;
|
|
281
|
+
latestManifestPath: string | null;
|
|
282
|
+
versionedManifestPath: string | null;
|
|
283
|
+
imagePath: string;
|
|
284
|
+
baseImagePath: string;
|
|
285
|
+
binaryPath: string;
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
interface RootfsManifest {
|
|
289
|
+
profile: string;
|
|
290
|
+
version: string;
|
|
291
|
+
rootfsInstallStrategy: string;
|
|
292
|
+
requiresBootstrapNetwork: boolean;
|
|
293
|
+
bootstrapSummary: string;
|
|
294
|
+
rootfsSourceSizeBytes?: number;
|
|
295
|
+
requiredPortForwards: RootfsContract["ports"];
|
|
296
|
+
rootfsCid?: string;
|
|
297
|
+
rootfsItemHash?: string;
|
|
298
|
+
rootfsSizeMiB: number;
|
|
299
|
+
createdAt: string;
|
|
300
|
+
notes: string;
|
|
301
|
+
}
|
|
302
|
+
interface RootfsManifestOutputPaths {
|
|
303
|
+
primaryPath: string;
|
|
304
|
+
copyTargetPath?: string;
|
|
305
|
+
versionedTargetPath?: string;
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
interface RootfsToolchainAvailability {
|
|
309
|
+
hasDocker: boolean;
|
|
310
|
+
dockerDaemonRunning?: boolean;
|
|
311
|
+
hasVirtCustomize: boolean;
|
|
312
|
+
githubActions?: boolean;
|
|
313
|
+
}
|
|
314
|
+
interface RootfsCommandPlan {
|
|
315
|
+
command: string;
|
|
316
|
+
args: string[];
|
|
317
|
+
env?: Record<string, string>;
|
|
318
|
+
workdir?: string;
|
|
319
|
+
}
|
|
320
|
+
interface RootfsExecutionPlan {
|
|
321
|
+
mode: "host" | "docker";
|
|
322
|
+
reason: string;
|
|
323
|
+
referenceRootfsDir: string;
|
|
324
|
+
prepareCommand?: RootfsCommandPlan;
|
|
325
|
+
runCommand: RootfsCommandPlan;
|
|
326
|
+
}
|
|
327
|
+
interface RootfsExecutionPlanOptions {
|
|
328
|
+
referenceRootfsDir?: string;
|
|
329
|
+
projectMountPath?: string;
|
|
330
|
+
rootfsMountPath?: string;
|
|
331
|
+
dockerImageTag?: string;
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
interface RootfsPublicationArtifacts {
|
|
335
|
+
ipfsAddResponsePath: string;
|
|
336
|
+
storeMessagePath: string;
|
|
337
|
+
storeMessageStderrPath: string;
|
|
338
|
+
}
|
|
339
|
+
interface RootfsPublicationResult {
|
|
340
|
+
cid: string;
|
|
341
|
+
itemHash: string;
|
|
342
|
+
sourceSizeBytes?: number;
|
|
343
|
+
}
|
|
344
|
+
|
|
345
|
+
interface RootfsBuildPipeline {
|
|
346
|
+
buildPlan: RootfsBuildPlan;
|
|
347
|
+
executionPlan: RootfsExecutionPlan;
|
|
348
|
+
publicationArtifacts: RootfsPublicationArtifacts;
|
|
349
|
+
manifestPaths: RootfsManifestOutputPaths;
|
|
350
|
+
}
|
|
351
|
+
interface RootfsFinalizeResult {
|
|
352
|
+
manifest: RootfsManifest;
|
|
353
|
+
manifestJson: string;
|
|
354
|
+
manifestPaths: RootfsManifestOutputPaths;
|
|
355
|
+
publication?: RootfsPublicationResult;
|
|
356
|
+
}
|
|
357
|
+
|
|
358
|
+
interface RootfsExecutedCommand {
|
|
359
|
+
command: string;
|
|
360
|
+
args: string[];
|
|
361
|
+
workdir?: string;
|
|
362
|
+
env?: Record<string, string>;
|
|
363
|
+
}
|
|
364
|
+
interface RootfsCommandRunner {
|
|
365
|
+
run(command: RootfsExecutedCommand): Promise<void>;
|
|
366
|
+
}
|
|
367
|
+
interface RootfsFileReader {
|
|
368
|
+
readText(path: string): Promise<string>;
|
|
369
|
+
}
|
|
370
|
+
interface RootfsExecutorDependencies extends RootfsCommandRunner, RootfsFileReader {
|
|
371
|
+
}
|
|
372
|
+
interface RootfsExecutionResult {
|
|
373
|
+
pipeline: RootfsBuildPipeline;
|
|
374
|
+
executedCommands: RootfsExecutedCommand[];
|
|
375
|
+
}
|
|
376
|
+
interface RootfsPublishExecutionResult extends RootfsExecutionResult {
|
|
377
|
+
finalized: RootfsFinalizeResult;
|
|
378
|
+
}
|
|
379
|
+
interface RootfsPublishOptions extends RootfsExecutionPlanOptions {
|
|
380
|
+
createdAt?: string;
|
|
381
|
+
referenceRootfsDir?: string;
|
|
382
|
+
}
|
|
383
|
+
declare function buildRootfs(buildPlan: RootfsBuildPlan, deps: RootfsCommandRunner, availability: RootfsToolchainAvailability, options?: RootfsExecutionPlanOptions): Promise<RootfsExecutionResult>;
|
|
384
|
+
declare function publishRootfs(buildPlan: RootfsBuildPlan, deps: RootfsExecutorDependencies, options?: RootfsPublishOptions): Promise<RootfsPublishExecutionResult>;
|
|
385
|
+
|
|
386
|
+
interface ParsedRootfsRunnerInputs {
|
|
387
|
+
buildPlan: RootfsBuildPlan;
|
|
388
|
+
availability: RootfsToolchainAvailability;
|
|
389
|
+
referenceRootfsDir?: string;
|
|
390
|
+
createdAt?: string;
|
|
391
|
+
}
|
|
392
|
+
declare function parseRootfsRunnerInputs(env?: NodeJS.ProcessEnv): Promise<ParsedRootfsRunnerInputs>;
|
|
393
|
+
declare function runLocalCommand(command: {
|
|
394
|
+
command: string;
|
|
395
|
+
args: string[];
|
|
396
|
+
workdir?: string;
|
|
397
|
+
env?: Record<string, string>;
|
|
398
|
+
}): Promise<void>;
|
|
399
|
+
declare function emitRootfsOutputs(result: RootfsPublishExecutionResult, env?: NodeJS.ProcessEnv): Promise<void>;
|
|
400
|
+
declare function runRootfsMode(env?: NodeJS.ProcessEnv, hooks?: {
|
|
401
|
+
stdout?: (text: string) => void;
|
|
402
|
+
parseInputs?: typeof parseRootfsRunnerInputs;
|
|
403
|
+
buildRootfs?: typeof buildRootfs;
|
|
404
|
+
publishRootfs?: typeof publishRootfs;
|
|
405
|
+
runCommand?: typeof runLocalCommand;
|
|
406
|
+
readText?: (targetPath: string) => Promise<string>;
|
|
407
|
+
}): Promise<void>;
|
|
408
|
+
declare function rootfsMain(): Promise<void>;
|
|
409
|
+
|
|
410
|
+
export { type DeployConfigurationResult, type DeployExecutorDependencies, type DeployMetadataResult, type DeployOutputResult, type DeployPlan, type ParsedRootfsRunnerInputs, type PrivateKeyIdentity, actionLog, appendGithubOutput, appendGithubSummary, booleanEnv, buildScaffoldDeployResult, createPrivateKeyIdentity, createPrivateKeySigner, emitDeployOutputs, emitGeocodedCrnOutputs, emitRootfsOutputs, executeDeployPlan, integerEnv, jsonEnv, main, optionalEnv, parseDeployPlan, parseRootfsRunnerInputs, requiredEnv, rootfsMain, runActionMode, runLocalCommand, runRootfsMode };
|
package/index.js
CHANGED
|
@@ -2102,6 +2102,669 @@ if (import.meta.url === pathToFileURL(process.argv[1] ?? "").href) {
|
|
|
2102
2102
|
process.exitCode = 1;
|
|
2103
2103
|
});
|
|
2104
2104
|
}
|
|
2105
|
+
|
|
2106
|
+
// src/rootfs-runner.ts
|
|
2107
|
+
import { pathToFileURL as pathToFileURL2 } from "url";
|
|
2108
|
+
import { readFile as readFile2 } from "fs/promises";
|
|
2109
|
+
import { spawn } from "child_process";
|
|
2110
|
+
|
|
2111
|
+
// ../rootfs/src/contract.ts
|
|
2112
|
+
import { readFile } from "fs/promises";
|
|
2113
|
+
import { fileURLToPath } from "url";
|
|
2114
|
+
function asObject(value) {
|
|
2115
|
+
return value && typeof value === "object" && !Array.isArray(value) ? value : null;
|
|
2116
|
+
}
|
|
2117
|
+
function asString5(value) {
|
|
2118
|
+
return typeof value === "string" && value.trim() ? value.trim() : null;
|
|
2119
|
+
}
|
|
2120
|
+
function asNumber3(value) {
|
|
2121
|
+
return typeof value === "number" && Number.isFinite(value) ? value : null;
|
|
2122
|
+
}
|
|
2123
|
+
function parsePorts(value, errors) {
|
|
2124
|
+
if (!Array.isArray(value)) {
|
|
2125
|
+
errors.push("ports must be an array");
|
|
2126
|
+
return [];
|
|
2127
|
+
}
|
|
2128
|
+
return value.flatMap((entry, index) => {
|
|
2129
|
+
const port = asObject(entry);
|
|
2130
|
+
if (!port) {
|
|
2131
|
+
errors.push(`ports[${index}] must be an object`);
|
|
2132
|
+
return [];
|
|
2133
|
+
}
|
|
2134
|
+
const portNumber = asNumber3(port.port);
|
|
2135
|
+
if (portNumber == null || portNumber < 1 || portNumber > 65535) {
|
|
2136
|
+
errors.push(`ports[${index}].port must be an integer between 1 and 65535`);
|
|
2137
|
+
return [];
|
|
2138
|
+
}
|
|
2139
|
+
return [{
|
|
2140
|
+
port: portNumber,
|
|
2141
|
+
tcp: port.tcp === true,
|
|
2142
|
+
udp: port.udp === true,
|
|
2143
|
+
purpose: asString5(port.purpose) ?? void 0
|
|
2144
|
+
}];
|
|
2145
|
+
});
|
|
2146
|
+
}
|
|
2147
|
+
function validateRootfsContract(input) {
|
|
2148
|
+
const errors = [];
|
|
2149
|
+
const payload = asObject(input);
|
|
2150
|
+
if (!payload) {
|
|
2151
|
+
return { contract: null, valid: false, errors: ["rootfs contract must be an object"] };
|
|
2152
|
+
}
|
|
2153
|
+
const rootfs = asObject(payload.rootfs);
|
|
2154
|
+
const services = asObject(payload.services);
|
|
2155
|
+
const manifest = asObject(payload.manifest);
|
|
2156
|
+
const source = asObject(payload.source) ?? void 0;
|
|
2157
|
+
const schemaVersion = asNumber3(payload.schemaVersion);
|
|
2158
|
+
if (schemaVersion == null || !Number.isInteger(schemaVersion)) {
|
|
2159
|
+
errors.push("schemaVersion must be an integer");
|
|
2160
|
+
}
|
|
2161
|
+
const id = asString5(payload.id);
|
|
2162
|
+
if (!id) {
|
|
2163
|
+
errors.push("id must be a non-empty string");
|
|
2164
|
+
}
|
|
2165
|
+
if (!rootfs) {
|
|
2166
|
+
errors.push("rootfs must be an object");
|
|
2167
|
+
}
|
|
2168
|
+
if (!services) {
|
|
2169
|
+
errors.push("services must be an object");
|
|
2170
|
+
}
|
|
2171
|
+
if (!manifest) {
|
|
2172
|
+
errors.push("manifest must be an object");
|
|
2173
|
+
}
|
|
2174
|
+
const profile = asString5(rootfs?.profile);
|
|
2175
|
+
if (!profile) errors.push("rootfs.profile must be a non-empty string");
|
|
2176
|
+
const installMode = asString5(rootfs?.installMode);
|
|
2177
|
+
if (!installMode) errors.push("rootfs.installMode must be a non-empty string");
|
|
2178
|
+
const installDir = asString5(rootfs?.installDir);
|
|
2179
|
+
if (!installDir) errors.push("rootfs.installDir must be a non-empty string");
|
|
2180
|
+
const binaryPath = asString5(rootfs?.binaryPath) ?? "/usr/local/bin/universal-chat-go";
|
|
2181
|
+
const dataDir = asString5(rootfs?.dataDir);
|
|
2182
|
+
if (!dataDir) errors.push("rootfs.dataDir must be a non-empty string");
|
|
2183
|
+
const envFile = asString5(rootfs?.envFile);
|
|
2184
|
+
if (!envFile) errors.push("rootfs.envFile must be a non-empty string");
|
|
2185
|
+
const bootstrap = asString5(services?.bootstrap);
|
|
2186
|
+
if (!bootstrap) errors.push("services.bootstrap must be a non-empty string");
|
|
2187
|
+
const main2 = asString5(services?.main);
|
|
2188
|
+
if (!main2) errors.push("services.main must be a non-empty string");
|
|
2189
|
+
const autotlsRefresh = asString5(services?.autotlsRefresh);
|
|
2190
|
+
if (!autotlsRefresh) errors.push("services.autotlsRefresh must be a non-empty string");
|
|
2191
|
+
const copyTarget = asString5(manifest?.copyTarget);
|
|
2192
|
+
if (!copyTarget) errors.push("manifest.copyTarget must be a non-empty string");
|
|
2193
|
+
const ports = parsePorts(payload.ports, errors);
|
|
2194
|
+
if (errors.length > 0 || !schemaVersion || !id || !profile || !installMode || !installDir || !dataDir || !envFile || !bootstrap || !main2 || !autotlsRefresh || !copyTarget) {
|
|
2195
|
+
return { contract: null, valid: false, errors };
|
|
2196
|
+
}
|
|
2197
|
+
return {
|
|
2198
|
+
contract: {
|
|
2199
|
+
schemaVersion,
|
|
2200
|
+
id,
|
|
2201
|
+
displayName: asString5(payload.displayName) ?? void 0,
|
|
2202
|
+
source: source ? {
|
|
2203
|
+
repository: asString5(source.repository) ?? void 0,
|
|
2204
|
+
subdirectory: asString5(source.subdirectory) ?? void 0
|
|
2205
|
+
} : void 0,
|
|
2206
|
+
rootfs: { profile, installMode, installDir, binaryPath, dataDir, envFile },
|
|
2207
|
+
services: { bootstrap, main: main2, autotlsRefresh },
|
|
2208
|
+
ports,
|
|
2209
|
+
manifest: { copyTarget, notes: asString5(manifest?.notes) ?? void 0 }
|
|
2210
|
+
},
|
|
2211
|
+
valid: true,
|
|
2212
|
+
errors: []
|
|
2213
|
+
};
|
|
2214
|
+
}
|
|
2215
|
+
function parseRootfsContract(input) {
|
|
2216
|
+
const payload = typeof input === "string" ? JSON.parse(input) : input;
|
|
2217
|
+
const result = validateRootfsContract(payload);
|
|
2218
|
+
if (!result.valid || !result.contract) {
|
|
2219
|
+
throw new Error(`Invalid rootfs contract: ${result.errors.join("; ")}`);
|
|
2220
|
+
}
|
|
2221
|
+
return result.contract;
|
|
2222
|
+
}
|
|
2223
|
+
async function readRootfsContractFile(path4) {
|
|
2224
|
+
return parseRootfsContract(await readFile(path4, "utf8"));
|
|
2225
|
+
}
|
|
2226
|
+
function referenceProfileRootfsDir(profile) {
|
|
2227
|
+
return fileURLToPath(new URL(`../reference/${profile}/rootfs/`, import.meta.url));
|
|
2228
|
+
}
|
|
2229
|
+
|
|
2230
|
+
// ../rootfs/src/build-plan.ts
|
|
2231
|
+
import path from "path";
|
|
2232
|
+
function positiveInteger(value, fallback) {
|
|
2233
|
+
return typeof value === "number" && Number.isInteger(value) && value > 0 ? value : fallback;
|
|
2234
|
+
}
|
|
2235
|
+
function deriveRootfsVersion(options = {}) {
|
|
2236
|
+
if (options.rootfsVersion && options.rootfsVersion.trim()) {
|
|
2237
|
+
return options.rootfsVersion.trim();
|
|
2238
|
+
}
|
|
2239
|
+
if (options.gitShortSha && options.gitShortSha.trim()) {
|
|
2240
|
+
const now = options.now ?? /* @__PURE__ */ new Date();
|
|
2241
|
+
const yyyy = String(now.getUTCFullYear());
|
|
2242
|
+
const mm = String(now.getUTCMonth() + 1).padStart(2, "0");
|
|
2243
|
+
const dd = String(now.getUTCDate()).padStart(2, "0");
|
|
2244
|
+
return `uc-go-peer-git-${yyyy}${mm}${dd}-${options.gitShortSha.trim()}`;
|
|
2245
|
+
}
|
|
2246
|
+
return "uc-go-peer-v0.1.0";
|
|
2247
|
+
}
|
|
2248
|
+
function createRootfsBuildPlan(contract, options) {
|
|
2249
|
+
const projectDir = path.resolve(options.projectDir);
|
|
2250
|
+
const alephDir = path.resolve(options.alephDir ?? path.join(projectDir, "go-peer/aleph"));
|
|
2251
|
+
const outDir = path.resolve(options.outDir ?? path.join(alephDir, "dist-rootfs"));
|
|
2252
|
+
const contractPath = path.resolve(options.contractPath ?? path.join(alephDir, "root-profiles", `${contract.id}.json`));
|
|
2253
|
+
const rootfsVersion = deriveRootfsVersion(options);
|
|
2254
|
+
const copyTarget = contract.manifest.copyTarget?.trim() ?? "";
|
|
2255
|
+
const latestManifestPath = copyTarget ? path.resolve(copyTarget.startsWith("/") ? copyTarget : path.join(projectDir, copyTarget)) : null;
|
|
2256
|
+
const versionedManifestPath = latestManifestPath ? path.join(path.dirname(latestManifestPath), `${rootfsVersion}.json`) : null;
|
|
2257
|
+
return {
|
|
2258
|
+
contract,
|
|
2259
|
+
contractPath,
|
|
2260
|
+
projectDir,
|
|
2261
|
+
alephDir,
|
|
2262
|
+
outDir,
|
|
2263
|
+
driver: options.driver ?? "auto",
|
|
2264
|
+
rootfsSizeMiB: positiveInteger(options.rootfsSizeMiB, 20480),
|
|
2265
|
+
rootfsImageSize: options.rootfsImageSize?.trim() || "20G",
|
|
2266
|
+
rootfsVersion,
|
|
2267
|
+
channel: options.channel?.trim() || "ALEPH-CLOUDSOLUTIONS",
|
|
2268
|
+
skipUpload: options.skipUpload === true,
|
|
2269
|
+
skipBuild: options.skipBuild === true,
|
|
2270
|
+
ipfsAddUrl: options.ipfsAddUrl?.trim() || "https://ipfs.aleph.cloud/api/v0/add",
|
|
2271
|
+
ipfsGatewayUrl: options.ipfsGatewayUrl?.trim() || "https://ipfs.aleph.cloud/ipfs",
|
|
2272
|
+
alephApiHost: options.alephApiHost?.trim() || "https://api2.aleph.im",
|
|
2273
|
+
alephMessageWaitAttempts: positiveInteger(options.alephMessageWaitAttempts, 60),
|
|
2274
|
+
alephMessageWaitDelaySeconds: positiveInteger(options.alephMessageWaitDelaySeconds, 5),
|
|
2275
|
+
alephPinAttempts: positiveInteger(options.alephPinAttempts, 4),
|
|
2276
|
+
alephPinDelaySeconds: positiveInteger(options.alephPinDelaySeconds, 10),
|
|
2277
|
+
ipfsGatewayWaitAttempts: positiveInteger(options.ipfsGatewayWaitAttempts, 30),
|
|
2278
|
+
ipfsGatewayWaitDelaySeconds: positiveInteger(options.ipfsGatewayWaitDelaySeconds, 10),
|
|
2279
|
+
manifestPath: path.join(outDir, "rootfs-manifest.json"),
|
|
2280
|
+
latestManifestPath,
|
|
2281
|
+
versionedManifestPath,
|
|
2282
|
+
imagePath: path.join(outDir, "aleph-uc-go-peer.qcow2"),
|
|
2283
|
+
baseImagePath: path.join(outDir, "debian-12-genericcloud-amd64.qcow2"),
|
|
2284
|
+
binaryPath: path.join(outDir, "universal-chat-go")
|
|
2285
|
+
};
|
|
2286
|
+
}
|
|
2287
|
+
function rootfsBuildShellEnv(plan) {
|
|
2288
|
+
return {
|
|
2289
|
+
OUT_DIR: plan.outDir,
|
|
2290
|
+
ROOTFS_CONTRACT_FILE: plan.contractPath,
|
|
2291
|
+
ROOTFS_BUILD_DRIVER: plan.driver,
|
|
2292
|
+
ROOTFS_SIZE_MIB: String(plan.rootfsSizeMiB),
|
|
2293
|
+
ROOTFS_IMAGE_SIZE: plan.rootfsImageSize,
|
|
2294
|
+
ROOTFS_VERSION: plan.rootfsVersion,
|
|
2295
|
+
CHANNEL: plan.channel,
|
|
2296
|
+
SKIP_UPLOAD: plan.skipUpload ? "1" : "0",
|
|
2297
|
+
SKIP_BUILD: plan.skipBuild ? "1" : "0",
|
|
2298
|
+
IPFS_ADD_URL: plan.ipfsAddUrl,
|
|
2299
|
+
IPFS_GATEWAY_URL: plan.ipfsGatewayUrl,
|
|
2300
|
+
ALEPH_API_HOST: plan.alephApiHost,
|
|
2301
|
+
ALEPH_MESSAGE_WAIT_ATTEMPTS: String(plan.alephMessageWaitAttempts),
|
|
2302
|
+
ALEPH_MESSAGE_WAIT_DELAY_SECONDS: String(plan.alephMessageWaitDelaySeconds),
|
|
2303
|
+
ALEPH_PIN_ATTEMPTS: String(plan.alephPinAttempts),
|
|
2304
|
+
ALEPH_PIN_DELAY_SECONDS: String(plan.alephPinDelaySeconds),
|
|
2305
|
+
IPFS_GATEWAY_WAIT_ATTEMPTS: String(plan.ipfsGatewayWaitAttempts),
|
|
2306
|
+
IPFS_GATEWAY_WAIT_DELAY_SECONDS: String(plan.ipfsGatewayWaitDelaySeconds)
|
|
2307
|
+
};
|
|
2308
|
+
}
|
|
2309
|
+
|
|
2310
|
+
// ../rootfs/src/manifest.ts
|
|
2311
|
+
import { dirname, extname, isAbsolute, join } from "path";
|
|
2312
|
+
function createRootfsManifest(plan, contract, options = {}) {
|
|
2313
|
+
const manifest = {
|
|
2314
|
+
profile: contract.rootfs.profile,
|
|
2315
|
+
version: plan.rootfsVersion,
|
|
2316
|
+
rootfsInstallStrategy: contract.rootfs.installMode,
|
|
2317
|
+
requiresBootstrapNetwork: false,
|
|
2318
|
+
bootstrapSummary: "Dependencies are preinstalled in the image.",
|
|
2319
|
+
requiredPortForwards: contract.ports,
|
|
2320
|
+
rootfsSizeMiB: plan.rootfsSizeMiB,
|
|
2321
|
+
createdAt: options.createdAt ?? (/* @__PURE__ */ new Date()).toISOString(),
|
|
2322
|
+
notes: contract.manifest.notes ?? ""
|
|
2323
|
+
};
|
|
2324
|
+
if (typeof options.rootfsSourceSizeBytes === "number" && Number.isFinite(options.rootfsSourceSizeBytes) && options.rootfsSourceSizeBytes > 0) {
|
|
2325
|
+
manifest.rootfsSourceSizeBytes = options.rootfsSourceSizeBytes;
|
|
2326
|
+
}
|
|
2327
|
+
if (options.rootfsCid) {
|
|
2328
|
+
manifest.rootfsCid = options.rootfsCid;
|
|
2329
|
+
}
|
|
2330
|
+
if (options.rootfsItemHash) {
|
|
2331
|
+
manifest.rootfsItemHash = options.rootfsItemHash;
|
|
2332
|
+
}
|
|
2333
|
+
return manifest;
|
|
2334
|
+
}
|
|
2335
|
+
function resolveRootfsManifestOutputPaths(plan) {
|
|
2336
|
+
const paths = {
|
|
2337
|
+
primaryPath: plan.manifestPath
|
|
2338
|
+
};
|
|
2339
|
+
const copyTarget = plan.latestManifestPath;
|
|
2340
|
+
if (!copyTarget) {
|
|
2341
|
+
return paths;
|
|
2342
|
+
}
|
|
2343
|
+
const resolvedCopyTarget = isAbsolute(copyTarget) ? copyTarget : join(plan.projectDir, copyTarget);
|
|
2344
|
+
paths.copyTargetPath = resolvedCopyTarget;
|
|
2345
|
+
const copyTargetExt = extname(resolvedCopyTarget) || ".json";
|
|
2346
|
+
paths.versionedTargetPath = join(dirname(resolvedCopyTarget), `${plan.rootfsVersion}${copyTargetExt}`);
|
|
2347
|
+
return paths;
|
|
2348
|
+
}
|
|
2349
|
+
function serializeRootfsManifest(manifest) {
|
|
2350
|
+
return `${JSON.stringify(manifest, null, 2)}
|
|
2351
|
+
`;
|
|
2352
|
+
}
|
|
2353
|
+
|
|
2354
|
+
// ../rootfs/src/execution-plan.ts
|
|
2355
|
+
import path2 from "path";
|
|
2356
|
+
function ensurePathWithin(parent, child, label) {
|
|
2357
|
+
const relative = path2.relative(parent, child);
|
|
2358
|
+
if (relative.startsWith("..") || path2.isAbsolute(relative)) {
|
|
2359
|
+
throw new Error(`${label} must be inside ${parent}`);
|
|
2360
|
+
}
|
|
2361
|
+
return relative || ".";
|
|
2362
|
+
}
|
|
2363
|
+
function containerPathForProjectFile(hostPath, projectDir, mountPath, label) {
|
|
2364
|
+
const relative = ensurePathWithin(projectDir, hostPath, label);
|
|
2365
|
+
return path2.posix.join(mountPath, relative.split(path2.sep).join("/"));
|
|
2366
|
+
}
|
|
2367
|
+
function resolveReferenceRootfsDir(contract, override) {
|
|
2368
|
+
if (override) {
|
|
2369
|
+
return path2.resolve(override);
|
|
2370
|
+
}
|
|
2371
|
+
return referenceProfileRootfsDir(contract.id);
|
|
2372
|
+
}
|
|
2373
|
+
function createHostRootfsExecutionPlan(plan, options = {}) {
|
|
2374
|
+
const referenceRootfsDir = resolveReferenceRootfsDir(plan.contract, options.referenceRootfsDir);
|
|
2375
|
+
return {
|
|
2376
|
+
mode: "host",
|
|
2377
|
+
reason: "Using host virt-customize/qemu-img toolchain.",
|
|
2378
|
+
referenceRootfsDir,
|
|
2379
|
+
runCommand: {
|
|
2380
|
+
command: "bash",
|
|
2381
|
+
args: [path2.join(referenceRootfsDir, "build-rootfs-image.sh")],
|
|
2382
|
+
workdir: referenceRootfsDir,
|
|
2383
|
+
env: {
|
|
2384
|
+
PROJECT_DIR: plan.projectDir,
|
|
2385
|
+
OUT_DIR: plan.outDir,
|
|
2386
|
+
ROOTFS_CONTRACT_FILE: plan.contractPath,
|
|
2387
|
+
ROOTFS_IMAGE_SIZE: plan.rootfsImageSize
|
|
2388
|
+
}
|
|
2389
|
+
}
|
|
2390
|
+
};
|
|
2391
|
+
}
|
|
2392
|
+
function createDockerRootfsExecutionPlan(plan, options = {}) {
|
|
2393
|
+
const referenceRootfsDir = resolveReferenceRootfsDir(plan.contract, options.referenceRootfsDir);
|
|
2394
|
+
const projectMountPath = options.projectMountPath ?? "/workspace/project";
|
|
2395
|
+
const rootfsMountPath = options.rootfsMountPath ?? "/workspace/shared-rootfs";
|
|
2396
|
+
const dockerImageTag = options.dockerImageTag ?? `${plan.contract.id}-rootfs-builder:local`;
|
|
2397
|
+
const containerProjectDir = projectMountPath;
|
|
2398
|
+
const containerContractPath = containerPathForProjectFile(plan.contractPath, plan.projectDir, projectMountPath, "contractPath");
|
|
2399
|
+
const containerOutDir = containerPathForProjectFile(plan.outDir, plan.projectDir, projectMountPath, "outDir");
|
|
2400
|
+
return {
|
|
2401
|
+
mode: "docker",
|
|
2402
|
+
reason: "Using Dockerized Debian/libguestfs builder.",
|
|
2403
|
+
referenceRootfsDir,
|
|
2404
|
+
prepareCommand: {
|
|
2405
|
+
command: "docker",
|
|
2406
|
+
args: [
|
|
2407
|
+
"build",
|
|
2408
|
+
"--platform",
|
|
2409
|
+
"linux/amd64",
|
|
2410
|
+
"-t",
|
|
2411
|
+
dockerImageTag,
|
|
2412
|
+
"-f",
|
|
2413
|
+
path2.join(referenceRootfsDir, "Dockerfile.rootfs"),
|
|
2414
|
+
referenceRootfsDir
|
|
2415
|
+
]
|
|
2416
|
+
},
|
|
2417
|
+
runCommand: {
|
|
2418
|
+
command: "docker",
|
|
2419
|
+
args: [
|
|
2420
|
+
"run",
|
|
2421
|
+
"--rm",
|
|
2422
|
+
"--privileged",
|
|
2423
|
+
"--platform",
|
|
2424
|
+
"linux/amd64",
|
|
2425
|
+
"-e",
|
|
2426
|
+
"LIBGUESTFS_BACKEND=direct",
|
|
2427
|
+
"-e",
|
|
2428
|
+
`ROOTFS_CONTRACT_FILE=${containerContractPath}`,
|
|
2429
|
+
"-e",
|
|
2430
|
+
`OUT_DIR=${containerOutDir}`,
|
|
2431
|
+
"-e",
|
|
2432
|
+
`ROOTFS_IMAGE_SIZE=${plan.rootfsImageSize}`,
|
|
2433
|
+
"-e",
|
|
2434
|
+
`PROJECT_DIR=${containerProjectDir}`,
|
|
2435
|
+
"-v",
|
|
2436
|
+
`${plan.projectDir}:${projectMountPath}`,
|
|
2437
|
+
"-v",
|
|
2438
|
+
`${referenceRootfsDir}:${rootfsMountPath}`,
|
|
2439
|
+
"-w",
|
|
2440
|
+
rootfsMountPath,
|
|
2441
|
+
dockerImageTag,
|
|
2442
|
+
"bash",
|
|
2443
|
+
path2.posix.join(rootfsMountPath, "build-rootfs-image.sh")
|
|
2444
|
+
]
|
|
2445
|
+
}
|
|
2446
|
+
};
|
|
2447
|
+
}
|
|
2448
|
+
function selectRootfsExecutionPlan(plan, availability, options = {}) {
|
|
2449
|
+
if (plan.driver === "host") {
|
|
2450
|
+
if (!availability.hasVirtCustomize) {
|
|
2451
|
+
throw new Error("ROOTFS_BUILD_DRIVER=host requested, but virt-customize is not available.");
|
|
2452
|
+
}
|
|
2453
|
+
return createHostRootfsExecutionPlan(plan, options);
|
|
2454
|
+
}
|
|
2455
|
+
if (plan.driver === "docker") {
|
|
2456
|
+
if (!availability.hasDocker) {
|
|
2457
|
+
throw new Error("ROOTFS_BUILD_DRIVER=docker requested, but docker is not available.");
|
|
2458
|
+
}
|
|
2459
|
+
if (availability.dockerDaemonRunning === false) {
|
|
2460
|
+
throw new Error("ROOTFS_BUILD_DRIVER=docker requested, but the Docker daemon is not running.");
|
|
2461
|
+
}
|
|
2462
|
+
return createDockerRootfsExecutionPlan(plan, options);
|
|
2463
|
+
}
|
|
2464
|
+
if (availability.githubActions && availability.hasDocker && availability.dockerDaemonRunning !== false) {
|
|
2465
|
+
return createDockerRootfsExecutionPlan(plan, options);
|
|
2466
|
+
}
|
|
2467
|
+
if (availability.hasVirtCustomize) {
|
|
2468
|
+
return createHostRootfsExecutionPlan(plan, options);
|
|
2469
|
+
}
|
|
2470
|
+
if (availability.hasDocker && availability.dockerDaemonRunning !== false) {
|
|
2471
|
+
return createDockerRootfsExecutionPlan(plan, options);
|
|
2472
|
+
}
|
|
2473
|
+
throw new Error("No supported rootfs build toolchain is available.");
|
|
2474
|
+
}
|
|
2475
|
+
|
|
2476
|
+
// ../rootfs/src/publication.ts
|
|
2477
|
+
import { join as join2 } from "path";
|
|
2478
|
+
function publicationArtifacts(plan) {
|
|
2479
|
+
return {
|
|
2480
|
+
ipfsAddResponsePath: join2(plan.outDir, "ipfs-add-response.jsonl"),
|
|
2481
|
+
storeMessagePath: join2(plan.outDir, "store-message.json"),
|
|
2482
|
+
storeMessageStderrPath: join2(plan.outDir, "store-message.stderr.log")
|
|
2483
|
+
};
|
|
2484
|
+
}
|
|
2485
|
+
function parseIpfsAddResponse(content) {
|
|
2486
|
+
return content.split(/\r?\n/u).map((line) => line.trim()).filter(Boolean).map((line) => JSON.parse(line));
|
|
2487
|
+
}
|
|
2488
|
+
function extractRootfsCid(entries) {
|
|
2489
|
+
if (entries.length === 0) {
|
|
2490
|
+
throw new Error("No response received from the IPFS add endpoint");
|
|
2491
|
+
}
|
|
2492
|
+
const cid = entries.at(-1)?.Hash?.trim();
|
|
2493
|
+
if (!cid) {
|
|
2494
|
+
throw new Error(`IPFS add response did not include a Hash: ${JSON.stringify(entries.at(-1) ?? {})}`);
|
|
2495
|
+
}
|
|
2496
|
+
return cid;
|
|
2497
|
+
}
|
|
2498
|
+
function extractRootfsSourceSizeBytes(entries) {
|
|
2499
|
+
const size = entries.at(-1)?.Size;
|
|
2500
|
+
if (typeof size === "number" && Number.isFinite(size) && size > 0) {
|
|
2501
|
+
return size;
|
|
2502
|
+
}
|
|
2503
|
+
if (typeof size === "string" && /^\d+$/u.test(size)) {
|
|
2504
|
+
return Number(size);
|
|
2505
|
+
}
|
|
2506
|
+
return void 0;
|
|
2507
|
+
}
|
|
2508
|
+
function parseStoreMessageResponse(content) {
|
|
2509
|
+
const payload = JSON.parse(content);
|
|
2510
|
+
const itemHash = payload.item_hash?.trim();
|
|
2511
|
+
if (!itemHash) {
|
|
2512
|
+
throw new Error("Failed to extract Aleph item hash from store message response");
|
|
2513
|
+
}
|
|
2514
|
+
return { item_hash: itemHash };
|
|
2515
|
+
}
|
|
2516
|
+
function createRootfsPublicationResult(ipfsAddContent, storeMessageContent) {
|
|
2517
|
+
const entries = parseIpfsAddResponse(ipfsAddContent);
|
|
2518
|
+
const storeMessage = parseStoreMessageResponse(storeMessageContent);
|
|
2519
|
+
return {
|
|
2520
|
+
cid: extractRootfsCid(entries),
|
|
2521
|
+
itemHash: storeMessage.item_hash,
|
|
2522
|
+
sourceSizeBytes: extractRootfsSourceSizeBytes(entries)
|
|
2523
|
+
};
|
|
2524
|
+
}
|
|
2525
|
+
|
|
2526
|
+
// ../rootfs/src/orchestration.ts
|
|
2527
|
+
function createRootfsBuildPipeline(buildPlan, availability, options = {}) {
|
|
2528
|
+
return {
|
|
2529
|
+
buildPlan,
|
|
2530
|
+
executionPlan: selectRootfsExecutionPlan(buildPlan, availability, options),
|
|
2531
|
+
publicationArtifacts: publicationArtifacts(buildPlan),
|
|
2532
|
+
manifestPaths: resolveRootfsManifestOutputPaths(buildPlan)
|
|
2533
|
+
};
|
|
2534
|
+
}
|
|
2535
|
+
function finalizeRootfsBuildPipeline(buildPlan, options = {}) {
|
|
2536
|
+
let publication;
|
|
2537
|
+
if (options.ipfsAddResponseContent && options.storeMessageContent) {
|
|
2538
|
+
publication = createRootfsPublicationResult(options.ipfsAddResponseContent, options.storeMessageContent);
|
|
2539
|
+
}
|
|
2540
|
+
const manifest = createRootfsManifest(buildPlan, buildPlan.contract, {
|
|
2541
|
+
createdAt: options.createdAt,
|
|
2542
|
+
rootfsCid: publication?.cid ?? options.rootfsCid,
|
|
2543
|
+
rootfsItemHash: publication?.itemHash ?? options.rootfsItemHash,
|
|
2544
|
+
rootfsSourceSizeBytes: publication?.sourceSizeBytes ?? options.rootfsSourceSizeBytes
|
|
2545
|
+
});
|
|
2546
|
+
return {
|
|
2547
|
+
manifest,
|
|
2548
|
+
manifestJson: serializeRootfsManifest(manifest),
|
|
2549
|
+
manifestPaths: resolveRootfsManifestOutputPaths(buildPlan),
|
|
2550
|
+
publication
|
|
2551
|
+
};
|
|
2552
|
+
}
|
|
2553
|
+
|
|
2554
|
+
// ../rootfs/src/executor.ts
|
|
2555
|
+
import path3 from "path";
|
|
2556
|
+
function rootfsScriptDir(buildPlan, override) {
|
|
2557
|
+
return override ? path3.resolve(override) : referenceProfileRootfsDir(buildPlan.contract.id);
|
|
2558
|
+
}
|
|
2559
|
+
function createRootfsScriptCommand(buildPlan, referenceRootfsDir) {
|
|
2560
|
+
const scriptDir = rootfsScriptDir(buildPlan, referenceRootfsDir);
|
|
2561
|
+
return {
|
|
2562
|
+
command: "bash",
|
|
2563
|
+
args: [path3.join(scriptDir, "build-rootfs.sh")],
|
|
2564
|
+
workdir: scriptDir,
|
|
2565
|
+
env: rootfsBuildShellEnv(buildPlan)
|
|
2566
|
+
};
|
|
2567
|
+
}
|
|
2568
|
+
async function buildRootfs(buildPlan, deps, availability, options = {}) {
|
|
2569
|
+
const pipeline = createRootfsBuildPipeline(buildPlan, availability, options);
|
|
2570
|
+
const executedCommands = [];
|
|
2571
|
+
if (pipeline.executionPlan.prepareCommand) {
|
|
2572
|
+
const prepareCommand = {
|
|
2573
|
+
command: pipeline.executionPlan.prepareCommand.command,
|
|
2574
|
+
args: pipeline.executionPlan.prepareCommand.args,
|
|
2575
|
+
workdir: pipeline.executionPlan.prepareCommand.workdir,
|
|
2576
|
+
env: pipeline.executionPlan.prepareCommand.env
|
|
2577
|
+
};
|
|
2578
|
+
await deps.run(prepareCommand);
|
|
2579
|
+
executedCommands.push(prepareCommand);
|
|
2580
|
+
}
|
|
2581
|
+
const runCommand = {
|
|
2582
|
+
command: pipeline.executionPlan.runCommand.command,
|
|
2583
|
+
args: pipeline.executionPlan.runCommand.args,
|
|
2584
|
+
workdir: pipeline.executionPlan.runCommand.workdir,
|
|
2585
|
+
env: pipeline.executionPlan.runCommand.env
|
|
2586
|
+
};
|
|
2587
|
+
await deps.run(runCommand);
|
|
2588
|
+
executedCommands.push(runCommand);
|
|
2589
|
+
return {
|
|
2590
|
+
pipeline,
|
|
2591
|
+
executedCommands
|
|
2592
|
+
};
|
|
2593
|
+
}
|
|
2594
|
+
async function publishRootfs(buildPlan, deps, options = {}) {
|
|
2595
|
+
const command = createRootfsScriptCommand(buildPlan, options.referenceRootfsDir);
|
|
2596
|
+
await deps.run(command);
|
|
2597
|
+
let ipfsAddResponseContent;
|
|
2598
|
+
let storeMessageContent;
|
|
2599
|
+
const publicationArtifacts2 = {
|
|
2600
|
+
ipfsAddResponsePath: path3.join(buildPlan.outDir, "ipfs-add-response.jsonl"),
|
|
2601
|
+
storeMessagePath: path3.join(buildPlan.outDir, "store-message.json"),
|
|
2602
|
+
storeMessageStderrPath: path3.join(buildPlan.outDir, "store-message.stderr.log")
|
|
2603
|
+
};
|
|
2604
|
+
if (!buildPlan.skipUpload) {
|
|
2605
|
+
ipfsAddResponseContent = await deps.readText(publicationArtifacts2.ipfsAddResponsePath);
|
|
2606
|
+
storeMessageContent = await deps.readText(publicationArtifacts2.storeMessagePath);
|
|
2607
|
+
}
|
|
2608
|
+
const finalized = finalizeRootfsBuildPipeline(buildPlan, {
|
|
2609
|
+
createdAt: options.createdAt,
|
|
2610
|
+
ipfsAddResponseContent,
|
|
2611
|
+
storeMessageContent
|
|
2612
|
+
});
|
|
2613
|
+
return {
|
|
2614
|
+
pipeline: {
|
|
2615
|
+
buildPlan,
|
|
2616
|
+
executionPlan: {
|
|
2617
|
+
mode: "docker",
|
|
2618
|
+
reason: "Running shared rootfs build orchestrator script.",
|
|
2619
|
+
referenceRootfsDir: command.workdir ?? rootfsScriptDir(buildPlan, options.referenceRootfsDir),
|
|
2620
|
+
runCommand: {
|
|
2621
|
+
command: command.command,
|
|
2622
|
+
args: command.args,
|
|
2623
|
+
workdir: command.workdir,
|
|
2624
|
+
env: command.env
|
|
2625
|
+
}
|
|
2626
|
+
},
|
|
2627
|
+
publicationArtifacts: publicationArtifacts2,
|
|
2628
|
+
manifestPaths: finalized.manifestPaths
|
|
2629
|
+
},
|
|
2630
|
+
executedCommands: [command],
|
|
2631
|
+
finalized
|
|
2632
|
+
};
|
|
2633
|
+
}
|
|
2634
|
+
|
|
2635
|
+
// src/rootfs-runner.ts
|
|
2636
|
+
async function parseRootfsRunnerInputs(env = process.env) {
|
|
2637
|
+
const contractPath = requiredEnv("ALEPH_ROOTFS_CONTRACT_PATH", env);
|
|
2638
|
+
const contract = await readRootfsContractFile(contractPath);
|
|
2639
|
+
const buildPlan = createRootfsBuildPlan(contract, {
|
|
2640
|
+
projectDir: requiredEnv("ALEPH_ROOTFS_PROJECT_DIR", env),
|
|
2641
|
+
contractPath,
|
|
2642
|
+
alephDir: optionalEnv("ALEPH_ROOTFS_ALEPH_DIR", void 0, env) || void 0,
|
|
2643
|
+
outDir: optionalEnv("ALEPH_ROOTFS_OUT_DIR", void 0, env) || void 0,
|
|
2644
|
+
driver: optionalEnv("ALEPH_ROOTFS_DRIVER", "auto", env),
|
|
2645
|
+
rootfsVersion: optionalEnv("ALEPH_ROOTFS_VERSION", void 0, env) || void 0,
|
|
2646
|
+
rootfsSizeMiB: Number(optionalEnv("ALEPH_ROOTFS_SIZE_MIB", "", env)) || void 0,
|
|
2647
|
+
rootfsImageSize: optionalEnv("ALEPH_ROOTFS_IMAGE_SIZE", void 0, env) || void 0,
|
|
2648
|
+
channel: optionalEnv("ALEPH_ROOTFS_CHANNEL", void 0, env) || void 0,
|
|
2649
|
+
skipUpload: booleanEnv("ALEPH_ROOTFS_SKIP_UPLOAD", false, env),
|
|
2650
|
+
skipBuild: booleanEnv("ALEPH_ROOTFS_SKIP_BUILD", false, env),
|
|
2651
|
+
ipfsAddUrl: optionalEnv("ALEPH_ROOTFS_IPFS_ADD_URL", void 0, env) || void 0,
|
|
2652
|
+
ipfsGatewayUrl: optionalEnv("ALEPH_ROOTFS_IPFS_GATEWAY_URL", void 0, env) || void 0,
|
|
2653
|
+
alephApiHost: optionalEnv("ALEPH_ROOTFS_ALEPH_API_HOST", void 0, env) || void 0,
|
|
2654
|
+
alephMessageWaitAttempts: Number(optionalEnv("ALEPH_ROOTFS_ALEPH_MESSAGE_WAIT_ATTEMPTS", "", env)) || void 0,
|
|
2655
|
+
alephMessageWaitDelaySeconds: Number(optionalEnv("ALEPH_ROOTFS_ALEPH_MESSAGE_WAIT_DELAY_SECONDS", "", env)) || void 0,
|
|
2656
|
+
alephPinAttempts: Number(optionalEnv("ALEPH_ROOTFS_ALEPH_PIN_ATTEMPTS", "", env)) || void 0,
|
|
2657
|
+
alephPinDelaySeconds: Number(optionalEnv("ALEPH_ROOTFS_ALEPH_PIN_DELAY_SECONDS", "", env)) || void 0,
|
|
2658
|
+
ipfsGatewayWaitAttempts: Number(optionalEnv("ALEPH_ROOTFS_IPFS_GATEWAY_WAIT_ATTEMPTS", "", env)) || void 0,
|
|
2659
|
+
ipfsGatewayWaitDelaySeconds: Number(optionalEnv("ALEPH_ROOTFS_IPFS_GATEWAY_WAIT_DELAY_SECONDS", "", env)) || void 0
|
|
2660
|
+
});
|
|
2661
|
+
return {
|
|
2662
|
+
buildPlan,
|
|
2663
|
+
availability: {
|
|
2664
|
+
githubActions: env.GITHUB_ACTIONS === "true",
|
|
2665
|
+
hasDocker: booleanEnv("ALEPH_ROOTFS_HAS_DOCKER", false, env),
|
|
2666
|
+
dockerDaemonRunning: env.ALEPH_ROOTFS_DOCKER_DAEMON_RUNNING == null ? void 0 : booleanEnv("ALEPH_ROOTFS_DOCKER_DAEMON_RUNNING", false, env),
|
|
2667
|
+
hasVirtCustomize: booleanEnv("ALEPH_ROOTFS_HAS_VIRT_CUSTOMIZE", false, env)
|
|
2668
|
+
},
|
|
2669
|
+
referenceRootfsDir: optionalEnv("ALEPH_ROOTFS_REFERENCE_ROOTFS_DIR", void 0, env) || void 0,
|
|
2670
|
+
createdAt: optionalEnv("ALEPH_ROOTFS_CREATED_AT", void 0, env) || void 0
|
|
2671
|
+
};
|
|
2672
|
+
}
|
|
2673
|
+
async function runLocalCommand(command) {
|
|
2674
|
+
await new Promise((resolve, reject) => {
|
|
2675
|
+
const child = spawn(command.command, command.args, {
|
|
2676
|
+
cwd: command.workdir,
|
|
2677
|
+
env: { ...process.env, ...command.env },
|
|
2678
|
+
stdio: "inherit"
|
|
2679
|
+
});
|
|
2680
|
+
child.on("error", reject);
|
|
2681
|
+
child.on("exit", (code) => {
|
|
2682
|
+
if (code === 0) {
|
|
2683
|
+
resolve();
|
|
2684
|
+
} else {
|
|
2685
|
+
reject(new Error(`${command.command} ${command.args.join(" ")} failed with exit code ${code ?? "unknown"}`));
|
|
2686
|
+
}
|
|
2687
|
+
});
|
|
2688
|
+
});
|
|
2689
|
+
}
|
|
2690
|
+
async function emitRootfsOutputs(result, env = process.env) {
|
|
2691
|
+
await appendGithubOutput("rootfs_version", result.finalized.manifest.version, env);
|
|
2692
|
+
await appendGithubOutput("rootfs_manifest_path", result.finalized.manifestPaths.primaryPath, env);
|
|
2693
|
+
await appendGithubOutput("rootfs_manifest_json", result.finalized.manifestJson, env);
|
|
2694
|
+
await appendGithubOutput("rootfs_image_path", result.pipeline.buildPlan.imagePath, env);
|
|
2695
|
+
await appendGithubOutput("rootfs_execution_mode", result.pipeline.executionPlan.mode, env);
|
|
2696
|
+
if (result.finalized.manifestPaths.copyTargetPath) {
|
|
2697
|
+
await appendGithubOutput("rootfs_manifest_copy_target_path", result.finalized.manifestPaths.copyTargetPath, env);
|
|
2698
|
+
}
|
|
2699
|
+
if (result.finalized.manifestPaths.versionedTargetPath) {
|
|
2700
|
+
await appendGithubOutput("rootfs_manifest_versioned_path", result.finalized.manifestPaths.versionedTargetPath, env);
|
|
2701
|
+
}
|
|
2702
|
+
if (result.finalized.publication?.cid) {
|
|
2703
|
+
await appendGithubOutput("rootfs_cid", result.finalized.publication.cid, env);
|
|
2704
|
+
}
|
|
2705
|
+
if (result.finalized.publication?.itemHash) {
|
|
2706
|
+
await appendGithubOutput("rootfs_item_hash", result.finalized.publication.itemHash, env);
|
|
2707
|
+
}
|
|
2708
|
+
if (typeof result.finalized.publication?.sourceSizeBytes === "number") {
|
|
2709
|
+
await appendGithubOutput("rootfs_source_size_bytes", result.finalized.publication.sourceSizeBytes, env);
|
|
2710
|
+
}
|
|
2711
|
+
await appendGithubSummary([
|
|
2712
|
+
"## Shared Rootfs Runner",
|
|
2713
|
+
"",
|
|
2714
|
+
`- Version: \`${result.finalized.manifest.version}\``,
|
|
2715
|
+
`- Execution mode: \`${result.pipeline.executionPlan.mode}\``,
|
|
2716
|
+
`- Image path: \`${result.pipeline.buildPlan.imagePath}\``,
|
|
2717
|
+
`- Manifest path: \`${result.finalized.manifestPaths.primaryPath}\``,
|
|
2718
|
+
`- Published CID: \`${result.finalized.publication?.cid ?? ""}\``,
|
|
2719
|
+
`- Aleph item hash: \`${result.finalized.publication?.itemHash ?? ""}\``
|
|
2720
|
+
], env);
|
|
2721
|
+
}
|
|
2722
|
+
async function runRootfsMode(env = process.env, hooks = {}) {
|
|
2723
|
+
const mode = optionalEnv("ALEPH_VM_MODE", "rootfs-publish", env);
|
|
2724
|
+
const stdout = hooks.stdout ?? ((text) => process.stdout.write(text));
|
|
2725
|
+
const parsed = await (hooks.parseInputs ?? parseRootfsRunnerInputs)(env);
|
|
2726
|
+
if (mode === "rootfs-build-plan") {
|
|
2727
|
+
stdout(`${JSON.stringify(parsed.buildPlan)}
|
|
2728
|
+
`);
|
|
2729
|
+
return;
|
|
2730
|
+
}
|
|
2731
|
+
if (mode === "rootfs-build") {
|
|
2732
|
+
const result = await (hooks.buildRootfs ?? buildRootfs)(
|
|
2733
|
+
parsed.buildPlan,
|
|
2734
|
+
{ run: hooks.runCommand ?? runLocalCommand },
|
|
2735
|
+
parsed.availability,
|
|
2736
|
+
{ referenceRootfsDir: parsed.referenceRootfsDir }
|
|
2737
|
+
);
|
|
2738
|
+
stdout(`${JSON.stringify(result.pipeline)}
|
|
2739
|
+
`);
|
|
2740
|
+
return;
|
|
2741
|
+
}
|
|
2742
|
+
if (mode === "rootfs-publish") {
|
|
2743
|
+
const result = await (hooks.publishRootfs ?? publishRootfs)(
|
|
2744
|
+
parsed.buildPlan,
|
|
2745
|
+
{
|
|
2746
|
+
run: hooks.runCommand ?? runLocalCommand,
|
|
2747
|
+
readText: hooks.readText ?? ((targetPath) => readFile2(targetPath, "utf8"))
|
|
2748
|
+
},
|
|
2749
|
+
{ createdAt: parsed.createdAt, referenceRootfsDir: parsed.referenceRootfsDir }
|
|
2750
|
+
);
|
|
2751
|
+
await emitRootfsOutputs(result, env);
|
|
2752
|
+
stdout(`${JSON.stringify(result.finalized)}
|
|
2753
|
+
`);
|
|
2754
|
+
return;
|
|
2755
|
+
}
|
|
2756
|
+
throw new Error(`Unsupported ALEPH_VM_MODE "${mode}" in shared rootfs runner.`);
|
|
2757
|
+
}
|
|
2758
|
+
async function rootfsMain() {
|
|
2759
|
+
await runRootfsMode(process.env);
|
|
2760
|
+
}
|
|
2761
|
+
if (import.meta.url === pathToFileURL2(process.argv[1] ?? "").href) {
|
|
2762
|
+
rootfsMain().catch((error) => {
|
|
2763
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
2764
|
+
console.error(message);
|
|
2765
|
+
process.exitCode = 1;
|
|
2766
|
+
});
|
|
2767
|
+
}
|
|
2105
2768
|
export {
|
|
2106
2769
|
actionLog,
|
|
2107
2770
|
appendGithubOutput,
|
|
@@ -2112,12 +2775,17 @@ export {
|
|
|
2112
2775
|
createPrivateKeySigner,
|
|
2113
2776
|
emitDeployOutputs,
|
|
2114
2777
|
emitGeocodedCrnOutputs,
|
|
2778
|
+
emitRootfsOutputs,
|
|
2115
2779
|
executeDeployPlan,
|
|
2116
2780
|
integerEnv,
|
|
2117
2781
|
jsonEnv,
|
|
2118
2782
|
main,
|
|
2119
2783
|
optionalEnv,
|
|
2120
2784
|
parseDeployPlan,
|
|
2785
|
+
parseRootfsRunnerInputs,
|
|
2121
2786
|
requiredEnv,
|
|
2122
|
-
|
|
2787
|
+
rootfsMain,
|
|
2788
|
+
runActionMode,
|
|
2789
|
+
runLocalCommand,
|
|
2790
|
+
runRootfsMode
|
|
2123
2791
|
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@le-space/node",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.3",
|
|
4
4
|
"description": "Node and GitHub Actions adapters for shared Aleph tooling.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|
|
@@ -16,8 +16,9 @@
|
|
|
16
16
|
"access": "public"
|
|
17
17
|
},
|
|
18
18
|
"dependencies": {
|
|
19
|
-
"@le-space/core": "0.1.
|
|
20
|
-
"@le-space/shared-types": "0.1.
|
|
19
|
+
"@le-space/core": "0.1.3",
|
|
20
|
+
"@le-space/shared-types": "0.1.3",
|
|
21
|
+
"@le-space/rootfs": "0.1.3",
|
|
21
22
|
"ethers": "^6.15.0"
|
|
22
23
|
}
|
|
23
24
|
}
|