@altf4llc/vorpal-sdk 0.1.0-alpha.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/api/agent/agent.d.ts +68 -0
- package/dist/api/agent/agent.d.ts.map +1 -0
- package/dist/api/agent/agent.js +246 -0
- package/dist/api/agent/agent.js.map +1 -0
- package/dist/api/archive/archive.d.ts +98 -0
- package/dist/api/archive/archive.d.ts.map +1 -0
- package/dist/api/archive/archive.js +288 -0
- package/dist/api/archive/archive.js.map +1 -0
- package/dist/api/artifact/artifact.d.ts +169 -0
- package/dist/api/artifact/artifact.d.ts.map +1 -0
- package/dist/api/artifact/artifact.js +1041 -0
- package/dist/api/artifact/artifact.js.map +1 -0
- package/dist/api/context/context.d.ts +42 -0
- package/dist/api/context/context.d.ts.map +1 -0
- package/dist/api/context/context.js +31 -0
- package/dist/api/context/context.js.map +1 -0
- package/dist/api/worker/worker.d.ts +65 -0
- package/dist/api/worker/worker.d.ts.map +1 -0
- package/dist/api/worker/worker.js +185 -0
- package/dist/api/worker/worker.js.map +1 -0
- package/dist/artifact/language/go.d.ts +165 -0
- package/dist/artifact/language/go.d.ts.map +1 -0
- package/dist/artifact/language/go.js +361 -0
- package/dist/artifact/language/go.js.map +1 -0
- package/dist/artifact/language/rust.d.ts +136 -0
- package/dist/artifact/language/rust.d.ts.map +1 -0
- package/dist/artifact/language/rust.js +576 -0
- package/dist/artifact/language/rust.js.map +1 -0
- package/dist/artifact/language/typescript.d.ts +112 -0
- package/dist/artifact/language/typescript.d.ts.map +1 -0
- package/dist/artifact/language/typescript.js +232 -0
- package/dist/artifact/language/typescript.js.map +1 -0
- package/dist/artifact/step.d.ts +28 -0
- package/dist/artifact/step.d.ts.map +1 -0
- package/dist/artifact/step.js +214 -0
- package/dist/artifact/step.js.map +1 -0
- package/dist/artifact.d.ts +392 -0
- package/dist/artifact.d.ts.map +1 -0
- package/dist/artifact.js +938 -0
- package/dist/artifact.js.map +1 -0
- package/dist/cli.d.ts +42 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +112 -0
- package/dist/cli.js.map +1 -0
- package/dist/context.d.ts +169 -0
- package/dist/context.d.ts.map +1 -0
- package/dist/context.js +779 -0
- package/dist/context.js.map +1 -0
- package/dist/index.d.ts +16 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +22 -0
- package/dist/index.js.map +1 -0
- package/dist/system.d.ts +20 -0
- package/dist/system.d.ts.map +1 -0
- package/dist/system.js +76 -0
- package/dist/system.js.map +1 -0
- package/dist/vorpal.d.ts +2 -0
- package/dist/vorpal.d.ts.map +1 -0
- package/dist/vorpal.js +148 -0
- package/dist/vorpal.js.map +1 -0
- package/package.json +55 -0
package/dist/artifact.js
ADDED
|
@@ -0,0 +1,938 @@
|
|
|
1
|
+
import { ArtifactSystem } from "./api/artifact/artifact.js";
|
|
2
|
+
import { shell } from "./artifact/step.js";
|
|
3
|
+
/**
|
|
4
|
+
* Returns the environment variable key for an artifact digest.
|
|
5
|
+
* Matches Rust get_env_key() and Go GetEnvKey().
|
|
6
|
+
*/
|
|
7
|
+
export function getEnvKey(digest) {
|
|
8
|
+
return `$VORPAL_ARTIFACT_${digest}`;
|
|
9
|
+
}
|
|
10
|
+
/**
|
|
11
|
+
* Converts a Map of secrets to a sorted array of proto objects.
|
|
12
|
+
* Matches Go SDK's SecretsToProto function.
|
|
13
|
+
*/
|
|
14
|
+
export function secretsToProto(secrets) {
|
|
15
|
+
const keys = Array.from(secrets.keys()).sort();
|
|
16
|
+
return keys.map((name) => ({ name, value: secrets.get(name) }));
|
|
17
|
+
}
|
|
18
|
+
// ---------------------------------------------------------------------------
|
|
19
|
+
// ArtifactSource
|
|
20
|
+
// ---------------------------------------------------------------------------
|
|
21
|
+
/**
|
|
22
|
+
* Builder for ArtifactSource messages.
|
|
23
|
+
* Matches Rust sdk/rust/src/artifact.rs ArtifactSource impl.
|
|
24
|
+
*/
|
|
25
|
+
export class ArtifactSource {
|
|
26
|
+
_digest = undefined;
|
|
27
|
+
_excludes = [];
|
|
28
|
+
_includes = [];
|
|
29
|
+
_name;
|
|
30
|
+
_path;
|
|
31
|
+
/**
|
|
32
|
+
* @param name - Source name (used as a key in the artifact's source map)
|
|
33
|
+
* @param path - Filesystem path to the source directory or file
|
|
34
|
+
*/
|
|
35
|
+
constructor(name, path) {
|
|
36
|
+
this._name = name;
|
|
37
|
+
this._path = path;
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* Sets a pre-computed digest for this source.
|
|
41
|
+
* When set, the agent skips re-hashing the source contents.
|
|
42
|
+
*/
|
|
43
|
+
withDigest(digest) {
|
|
44
|
+
this._digest = digest;
|
|
45
|
+
return this;
|
|
46
|
+
}
|
|
47
|
+
/**
|
|
48
|
+
* Sets glob patterns to exclude from the source.
|
|
49
|
+
* Patterns are matched relative to the source path.
|
|
50
|
+
*
|
|
51
|
+
* @param excludes - Array of glob patterns (e.g., `["node_modules", "*.log"]`)
|
|
52
|
+
*/
|
|
53
|
+
withExcludes(excludes) {
|
|
54
|
+
this._excludes = excludes;
|
|
55
|
+
return this;
|
|
56
|
+
}
|
|
57
|
+
/**
|
|
58
|
+
* Sets glob patterns to include in the source.
|
|
59
|
+
* Only matching files will be included. Patterns are matched relative to the source path.
|
|
60
|
+
*
|
|
61
|
+
* @param includes - Array of glob patterns (e.g., `["src/**", "package.json"]`)
|
|
62
|
+
*/
|
|
63
|
+
withIncludes(includes) {
|
|
64
|
+
this._includes = includes;
|
|
65
|
+
return this;
|
|
66
|
+
}
|
|
67
|
+
/** Builds the {@link ArtifactSource} message. */
|
|
68
|
+
build() {
|
|
69
|
+
return {
|
|
70
|
+
digest: this._digest,
|
|
71
|
+
excludes: this._excludes,
|
|
72
|
+
includes: this._includes,
|
|
73
|
+
name: this._name,
|
|
74
|
+
path: this._path,
|
|
75
|
+
};
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
// ---------------------------------------------------------------------------
|
|
79
|
+
// ArtifactStep
|
|
80
|
+
// ---------------------------------------------------------------------------
|
|
81
|
+
/**
|
|
82
|
+
* Builder for ArtifactStep messages.
|
|
83
|
+
* Matches Rust sdk/rust/src/artifact.rs ArtifactStep impl.
|
|
84
|
+
*/
|
|
85
|
+
export class ArtifactStep {
|
|
86
|
+
_arguments = [];
|
|
87
|
+
_artifacts = [];
|
|
88
|
+
_entrypoint;
|
|
89
|
+
_environments = [];
|
|
90
|
+
_secrets = [];
|
|
91
|
+
_script = undefined;
|
|
92
|
+
/**
|
|
93
|
+
* @param entrypoint - The executable entrypoint (e.g., `"bash"`, `"bwrap"`, `"docker"`)
|
|
94
|
+
*/
|
|
95
|
+
constructor(entrypoint) {
|
|
96
|
+
this._entrypoint = entrypoint;
|
|
97
|
+
}
|
|
98
|
+
/**
|
|
99
|
+
* Sets command-line arguments passed to the entrypoint.
|
|
100
|
+
*
|
|
101
|
+
* @param args - Array of argument strings
|
|
102
|
+
*/
|
|
103
|
+
withArguments(args) {
|
|
104
|
+
this._arguments = args;
|
|
105
|
+
return this;
|
|
106
|
+
}
|
|
107
|
+
/**
|
|
108
|
+
* Sets artifact digests whose outputs are available during this step.
|
|
109
|
+
* Each artifact's `$VORPAL_ARTIFACT_{digest}` directory will be mounted.
|
|
110
|
+
*
|
|
111
|
+
* @param artifacts - Array of artifact digest strings
|
|
112
|
+
*/
|
|
113
|
+
withArtifacts(artifacts) {
|
|
114
|
+
this._artifacts = artifacts;
|
|
115
|
+
return this;
|
|
116
|
+
}
|
|
117
|
+
/**
|
|
118
|
+
* Sets environment variables for the step execution.
|
|
119
|
+
* Format: `"KEY=VALUE"`.
|
|
120
|
+
*
|
|
121
|
+
* @param environments - Array of environment variable strings
|
|
122
|
+
*/
|
|
123
|
+
withEnvironments(environments) {
|
|
124
|
+
this._environments = environments;
|
|
125
|
+
return this;
|
|
126
|
+
}
|
|
127
|
+
/**
|
|
128
|
+
* Adds secrets available during the step. Secrets are deduplicated by name.
|
|
129
|
+
*
|
|
130
|
+
* @param secrets - Array of {@link ArtifactStepSecret} objects
|
|
131
|
+
*/
|
|
132
|
+
withSecrets(secrets) {
|
|
133
|
+
for (const secret of secrets) {
|
|
134
|
+
if (!this._secrets.some((s) => s.name === secret.name)) {
|
|
135
|
+
this._secrets.push(secret);
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
return this;
|
|
139
|
+
}
|
|
140
|
+
/**
|
|
141
|
+
* Sets the shell script to execute in this step.
|
|
142
|
+
* A bash shebang and `set -euo pipefail` are **not** prepended automatically;
|
|
143
|
+
* use the {@link bash} or {@link shell} helpers for that behavior.
|
|
144
|
+
*/
|
|
145
|
+
withScript(script) {
|
|
146
|
+
this._script = script;
|
|
147
|
+
return this;
|
|
148
|
+
}
|
|
149
|
+
/** Builds the {@link ArtifactStep} message. */
|
|
150
|
+
build() {
|
|
151
|
+
return {
|
|
152
|
+
entrypoint: this._entrypoint,
|
|
153
|
+
script: this._script,
|
|
154
|
+
secrets: this._secrets,
|
|
155
|
+
arguments: this._arguments,
|
|
156
|
+
artifacts: this._artifacts,
|
|
157
|
+
environments: this._environments,
|
|
158
|
+
};
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
// ---------------------------------------------------------------------------
|
|
162
|
+
// Artifact
|
|
163
|
+
// ---------------------------------------------------------------------------
|
|
164
|
+
/**
|
|
165
|
+
* Builder for Artifact messages.
|
|
166
|
+
* Matches Rust sdk/rust/src/artifact.rs Artifact impl (lines 211-256).
|
|
167
|
+
*/
|
|
168
|
+
export class Artifact {
|
|
169
|
+
_aliases = [];
|
|
170
|
+
_name;
|
|
171
|
+
_sources = [];
|
|
172
|
+
_steps;
|
|
173
|
+
_systems;
|
|
174
|
+
/**
|
|
175
|
+
* @param name - Artifact name (must be unique within a namespace)
|
|
176
|
+
* @param steps - Build steps that produce the artifact output
|
|
177
|
+
* @param systems - Target systems this artifact supports (e.g., `[ArtifactSystem.AARCH64_DARWIN]`)
|
|
178
|
+
*/
|
|
179
|
+
constructor(name, steps, systems) {
|
|
180
|
+
this._name = name;
|
|
181
|
+
this._steps = steps;
|
|
182
|
+
this._systems = systems;
|
|
183
|
+
}
|
|
184
|
+
/**
|
|
185
|
+
* Adds human-readable aliases for this artifact (e.g., `"my-tool:latest"`).
|
|
186
|
+
* Duplicates are ignored.
|
|
187
|
+
*
|
|
188
|
+
* @param aliases - Array of alias strings in `[namespace/]name[:tag]` format
|
|
189
|
+
*/
|
|
190
|
+
withAliases(aliases) {
|
|
191
|
+
for (const alias of aliases) {
|
|
192
|
+
if (!this._aliases.includes(alias)) {
|
|
193
|
+
this._aliases.push(alias);
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
return this;
|
|
197
|
+
}
|
|
198
|
+
/**
|
|
199
|
+
* Adds source definitions for this artifact. Sources are deduplicated by name.
|
|
200
|
+
*
|
|
201
|
+
* @param sources - Array of {@link ArtifactSource} messages
|
|
202
|
+
*/
|
|
203
|
+
withSources(sources) {
|
|
204
|
+
for (const source of sources) {
|
|
205
|
+
if (!this._sources.some((s) => s.name === source.name)) {
|
|
206
|
+
this._sources.push(source);
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
return this;
|
|
210
|
+
}
|
|
211
|
+
/**
|
|
212
|
+
* Builds the artifact, computes its SHA-256 digest, and registers it
|
|
213
|
+
* with the agent service via the provided {@link ConfigContext}.
|
|
214
|
+
*
|
|
215
|
+
* @returns The hex-encoded SHA-256 digest of the artifact
|
|
216
|
+
*/
|
|
217
|
+
async build(context) {
|
|
218
|
+
const artifact = {
|
|
219
|
+
target: context.getSystem(),
|
|
220
|
+
sources: this._sources,
|
|
221
|
+
steps: this._steps,
|
|
222
|
+
systems: this._systems,
|
|
223
|
+
aliases: this._aliases,
|
|
224
|
+
name: this._name,
|
|
225
|
+
};
|
|
226
|
+
return context.addArtifact(artifact);
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
// ---------------------------------------------------------------------------
|
|
230
|
+
// Job
|
|
231
|
+
// ---------------------------------------------------------------------------
|
|
232
|
+
/**
|
|
233
|
+
* Builder for Job artifacts (simple script execution).
|
|
234
|
+
* Matches Rust sdk/rust/src/artifact.rs Job impl (lines 259-297).
|
|
235
|
+
*
|
|
236
|
+
* CRITICAL: Secrets are sorted by name before building (Rust line 290).
|
|
237
|
+
*/
|
|
238
|
+
export class Job {
|
|
239
|
+
_artifacts = [];
|
|
240
|
+
_name;
|
|
241
|
+
_secrets = new Map();
|
|
242
|
+
_script;
|
|
243
|
+
_systems;
|
|
244
|
+
/**
|
|
245
|
+
* @param name - Job artifact name
|
|
246
|
+
* @param script - Shell script to execute
|
|
247
|
+
* @param systems - Target systems this job supports
|
|
248
|
+
*/
|
|
249
|
+
constructor(name, script, systems) {
|
|
250
|
+
this._name = name;
|
|
251
|
+
this._script = script;
|
|
252
|
+
this._systems = systems;
|
|
253
|
+
}
|
|
254
|
+
/**
|
|
255
|
+
* Sets artifact digests whose outputs are available during the job's build step.
|
|
256
|
+
*
|
|
257
|
+
* @param artifacts - Array of artifact digest strings
|
|
258
|
+
*/
|
|
259
|
+
withArtifacts(artifacts) {
|
|
260
|
+
this._artifacts = artifacts;
|
|
261
|
+
return this;
|
|
262
|
+
}
|
|
263
|
+
/**
|
|
264
|
+
* Adds secrets available during the job's build step.
|
|
265
|
+
*
|
|
266
|
+
* @param secrets - Map of secret name to value
|
|
267
|
+
*/
|
|
268
|
+
withSecrets(secrets) {
|
|
269
|
+
for (const [k, v] of secrets) {
|
|
270
|
+
if (!this._secrets.has(k)) {
|
|
271
|
+
this._secrets.set(k, v);
|
|
272
|
+
}
|
|
273
|
+
}
|
|
274
|
+
return this;
|
|
275
|
+
}
|
|
276
|
+
/**
|
|
277
|
+
* Builds the job artifact and registers it with the agent service.
|
|
278
|
+
*
|
|
279
|
+
* @returns The hex-encoded SHA-256 digest of the artifact
|
|
280
|
+
*/
|
|
281
|
+
async build(context) {
|
|
282
|
+
const step = await shell(context, this._artifacts, [], this._script, secretsToProto(this._secrets));
|
|
283
|
+
return new Artifact(this._name, [step], this._systems).build(context);
|
|
284
|
+
}
|
|
285
|
+
}
|
|
286
|
+
// ---------------------------------------------------------------------------
|
|
287
|
+
// Process
|
|
288
|
+
// ---------------------------------------------------------------------------
|
|
289
|
+
/**
|
|
290
|
+
* Builder for Process artifacts.
|
|
291
|
+
* Matches Rust sdk/rust/src/artifact.rs Process impl (lines 432-553).
|
|
292
|
+
*
|
|
293
|
+
* CRITICAL: Shell script template must be character-for-character identical.
|
|
294
|
+
* Secrets sorted by name (Rust line 477).
|
|
295
|
+
*/
|
|
296
|
+
export class Process {
|
|
297
|
+
_arguments = [];
|
|
298
|
+
_artifacts = [];
|
|
299
|
+
_entrypoint;
|
|
300
|
+
_name;
|
|
301
|
+
_secrets = new Map();
|
|
302
|
+
_systems;
|
|
303
|
+
/**
|
|
304
|
+
* @param name - Process artifact name (used for start/stop/logs scripts)
|
|
305
|
+
* @param entrypoint - Path to the executable to run as a background process
|
|
306
|
+
* @param systems - Target systems this process supports
|
|
307
|
+
*/
|
|
308
|
+
constructor(name, entrypoint, systems) {
|
|
309
|
+
this._name = name;
|
|
310
|
+
this._entrypoint = entrypoint;
|
|
311
|
+
this._systems = systems;
|
|
312
|
+
}
|
|
313
|
+
/**
|
|
314
|
+
* Sets command-line arguments passed to the process entrypoint.
|
|
315
|
+
*
|
|
316
|
+
* @param args - Array of argument strings
|
|
317
|
+
*/
|
|
318
|
+
withArguments(args) {
|
|
319
|
+
this._arguments = args;
|
|
320
|
+
return this;
|
|
321
|
+
}
|
|
322
|
+
/**
|
|
323
|
+
* Adds artifact dependencies whose bin directories are added to PATH.
|
|
324
|
+
* Duplicates are ignored.
|
|
325
|
+
*
|
|
326
|
+
* @param artifacts - Array of artifact digest strings
|
|
327
|
+
*/
|
|
328
|
+
withArtifacts(artifacts) {
|
|
329
|
+
for (const artifact of artifacts) {
|
|
330
|
+
if (!this._artifacts.includes(artifact)) {
|
|
331
|
+
this._artifacts.push(artifact);
|
|
332
|
+
}
|
|
333
|
+
}
|
|
334
|
+
return this;
|
|
335
|
+
}
|
|
336
|
+
/**
|
|
337
|
+
* Adds secrets available during the process build step.
|
|
338
|
+
*
|
|
339
|
+
* @param secrets - Map of secret name to value
|
|
340
|
+
*/
|
|
341
|
+
withSecrets(secrets) {
|
|
342
|
+
for (const [k, v] of secrets) {
|
|
343
|
+
if (!this._secrets.has(k)) {
|
|
344
|
+
this._secrets.set(k, v);
|
|
345
|
+
}
|
|
346
|
+
}
|
|
347
|
+
return this;
|
|
348
|
+
}
|
|
349
|
+
/**
|
|
350
|
+
* Builds the process artifact, which includes start/stop/logs helper scripts.
|
|
351
|
+
*
|
|
352
|
+
* @returns The hex-encoded SHA-256 digest of the artifact
|
|
353
|
+
*/
|
|
354
|
+
async build(context) {
|
|
355
|
+
const argumentsStr = this._arguments.join(" ");
|
|
356
|
+
const artifactsStr = this._artifacts
|
|
357
|
+
.map((v) => `$VORPAL_ARTIFACT_${v}/bin`)
|
|
358
|
+
.join(":");
|
|
359
|
+
// Script template matches Rust formatdoc! in Process::build()
|
|
360
|
+
const script = `mkdir -p $VORPAL_OUTPUT/bin
|
|
361
|
+
|
|
362
|
+
cat > $VORPAL_OUTPUT/bin/${this._name}-logs << "EOF"
|
|
363
|
+
#!/bin/bash
|
|
364
|
+
set -euo pipefail
|
|
365
|
+
|
|
366
|
+
if [ -f $VORPAL_OUTPUT/logs.txt ]; then
|
|
367
|
+
tail -f $VORPAL_OUTPUT/logs.txt
|
|
368
|
+
else
|
|
369
|
+
echo "No logs found"
|
|
370
|
+
fi
|
|
371
|
+
EOF
|
|
372
|
+
|
|
373
|
+
chmod +x $VORPAL_OUTPUT/bin/${this._name}-logs
|
|
374
|
+
|
|
375
|
+
cat > $VORPAL_OUTPUT/bin/${this._name}-stop << "EOF"
|
|
376
|
+
#!/bin/bash
|
|
377
|
+
set -euo pipefail
|
|
378
|
+
|
|
379
|
+
if [ -f $VORPAL_OUTPUT/pid ]; then
|
|
380
|
+
kill $(cat $VORPAL_OUTPUT/pid)
|
|
381
|
+
rm -rf $VORPAL_OUTPUT/pid
|
|
382
|
+
fi
|
|
383
|
+
EOF
|
|
384
|
+
|
|
385
|
+
chmod +x $VORPAL_OUTPUT/bin/${this._name}-stop
|
|
386
|
+
|
|
387
|
+
cat > $VORPAL_OUTPUT/bin/${this._name}-start << "EOF"
|
|
388
|
+
#!/bin/bash
|
|
389
|
+
set -euo pipefail
|
|
390
|
+
|
|
391
|
+
export PATH=${artifactsStr}:$PATH
|
|
392
|
+
|
|
393
|
+
$VORPAL_OUTPUT/bin/${this._name}-stop
|
|
394
|
+
|
|
395
|
+
echo "Process: ${this._entrypoint} ${argumentsStr}"
|
|
396
|
+
|
|
397
|
+
nohup ${this._entrypoint} ${argumentsStr} > $VORPAL_OUTPUT/logs.txt 2>&1 &
|
|
398
|
+
|
|
399
|
+
PROCESS_PID=$!
|
|
400
|
+
|
|
401
|
+
echo "Process ID: $PROCESS_PID"
|
|
402
|
+
|
|
403
|
+
echo $PROCESS_PID > $VORPAL_OUTPUT/pid
|
|
404
|
+
|
|
405
|
+
echo "Process commands:"
|
|
406
|
+
echo "- ${this._name}-logs (tail logs)"
|
|
407
|
+
echo "- ${this._name}-stop (stop process)"
|
|
408
|
+
echo "- ${this._name}-start (start process)"
|
|
409
|
+
EOF
|
|
410
|
+
|
|
411
|
+
chmod +x $VORPAL_OUTPUT/bin/${this._name}-start`;
|
|
412
|
+
const step = await shell(context, this._artifacts, [], script, secretsToProto(this._secrets));
|
|
413
|
+
return new Artifact(this._name, [step], this._systems).build(context);
|
|
414
|
+
}
|
|
415
|
+
}
|
|
416
|
+
// ---------------------------------------------------------------------------
|
|
417
|
+
// DevelopmentEnvironment
|
|
418
|
+
// ---------------------------------------------------------------------------
|
|
419
|
+
/**
|
|
420
|
+
* Builder for DevelopmentEnvironment artifacts.
|
|
421
|
+
* Matches Rust sdk/rust/src/artifact.rs DevelopmentEnvironment impl (lines 300-429).
|
|
422
|
+
*
|
|
423
|
+
* CRITICAL: Shell script template must match exactly. Secrets sorted by name.
|
|
424
|
+
*/
|
|
425
|
+
export class DevelopmentEnvironment {
|
|
426
|
+
_artifacts = [];
|
|
427
|
+
_environments = [];
|
|
428
|
+
_name;
|
|
429
|
+
_secrets = new Map();
|
|
430
|
+
_systems;
|
|
431
|
+
/**
|
|
432
|
+
* @param name - Environment name (shown in shell prompt as `(name)`)
|
|
433
|
+
* @param systems - Target systems this environment supports
|
|
434
|
+
*/
|
|
435
|
+
constructor(name, systems) {
|
|
436
|
+
this._name = name;
|
|
437
|
+
this._systems = systems;
|
|
438
|
+
}
|
|
439
|
+
/**
|
|
440
|
+
* Sets artifact dependencies whose bin directories are added to PATH.
|
|
441
|
+
*
|
|
442
|
+
* @param artifacts - Array of artifact digest strings
|
|
443
|
+
*/
|
|
444
|
+
withArtifacts(artifacts) {
|
|
445
|
+
this._artifacts = artifacts;
|
|
446
|
+
return this;
|
|
447
|
+
}
|
|
448
|
+
/**
|
|
449
|
+
* Sets environment variables exported when the environment is activated.
|
|
450
|
+
* Format: `"KEY=VALUE"`. PATH entries are handled specially and merged
|
|
451
|
+
* with artifact bin paths.
|
|
452
|
+
*
|
|
453
|
+
* @param environments - Array of environment variable strings
|
|
454
|
+
*/
|
|
455
|
+
withEnvironments(environments) {
|
|
456
|
+
this._environments = environments;
|
|
457
|
+
return this;
|
|
458
|
+
}
|
|
459
|
+
/**
|
|
460
|
+
* Adds secrets available during the environment build step.
|
|
461
|
+
*
|
|
462
|
+
* @param secrets - Map of secret name to value
|
|
463
|
+
*/
|
|
464
|
+
withSecrets(secrets) {
|
|
465
|
+
for (const [k, v] of secrets) {
|
|
466
|
+
if (!this._secrets.has(k)) {
|
|
467
|
+
this._secrets.set(k, v);
|
|
468
|
+
}
|
|
469
|
+
}
|
|
470
|
+
return this;
|
|
471
|
+
}
|
|
472
|
+
/**
|
|
473
|
+
* Builds the development environment artifact, which includes activate/deactivate
|
|
474
|
+
* scripts for shell integration.
|
|
475
|
+
*
|
|
476
|
+
* @returns The hex-encoded SHA-256 digest of the artifact
|
|
477
|
+
*/
|
|
478
|
+
async build(context) {
|
|
479
|
+
const envsBackup = [
|
|
480
|
+
'export VORPAL_SHELL_BACKUP_PATH="$PATH"',
|
|
481
|
+
'export VORPAL_SHELL_BACKUP_PS1="$PS1"',
|
|
482
|
+
'export VORPAL_SHELL_BACKUP_VORPAL_SHELL="$VORPAL_SHELL"',
|
|
483
|
+
];
|
|
484
|
+
const envsExport = [
|
|
485
|
+
`export PS1="(${this._name}) $PS1"`,
|
|
486
|
+
'export VORPAL_SHELL="1"',
|
|
487
|
+
];
|
|
488
|
+
const envsRestore = [
|
|
489
|
+
'export PATH="$VORPAL_SHELL_BACKUP_PATH"',
|
|
490
|
+
'export PS1="$VORPAL_SHELL_BACKUP_PS1"',
|
|
491
|
+
'export VORPAL_SHELL="$VORPAL_SHELL_BACKUP_VORPAL_SHELL"',
|
|
492
|
+
];
|
|
493
|
+
const envsUnset = [
|
|
494
|
+
"unset VORPAL_SHELL_BACKUP_PATH",
|
|
495
|
+
"unset VORPAL_SHELL_BACKUP_PS1",
|
|
496
|
+
"unset VORPAL_SHELL_BACKUP_VORPAL_SHELL",
|
|
497
|
+
];
|
|
498
|
+
for (const env of this._environments) {
|
|
499
|
+
const key = env.split("=")[0];
|
|
500
|
+
if (key === "PATH") {
|
|
501
|
+
continue;
|
|
502
|
+
}
|
|
503
|
+
envsBackup.push(`export VORPAL_SHELL_BACKUP_${key}="\$${key}"`);
|
|
504
|
+
envsExport.push(`export ${env}`);
|
|
505
|
+
envsRestore.push(`export ${key}="\$VORPAL_SHELL_BACKUP_${key}"`);
|
|
506
|
+
envsUnset.push(`unset VORPAL_SHELL_BACKUP_${key}`);
|
|
507
|
+
}
|
|
508
|
+
// Setup path
|
|
509
|
+
const stepPathArtifacts = this._artifacts
|
|
510
|
+
.map((artifact) => `${getEnvKey(artifact)}/bin`)
|
|
511
|
+
.join(":");
|
|
512
|
+
let stepPath = stepPathArtifacts;
|
|
513
|
+
const pathEnv = this._environments.find((x) => x.startsWith("PATH="));
|
|
514
|
+
if (pathEnv) {
|
|
515
|
+
const pathValue = pathEnv.split("=").slice(1).join("=");
|
|
516
|
+
if (pathValue) {
|
|
517
|
+
stepPath = `${pathValue}:${stepPath}`;
|
|
518
|
+
}
|
|
519
|
+
}
|
|
520
|
+
envsExport.push(`export PATH=${stepPath}:$PATH`);
|
|
521
|
+
// Setup script - matches Rust formatdoc!
|
|
522
|
+
const stepScript = `mkdir -p $VORPAL_WORKSPACE/bin
|
|
523
|
+
|
|
524
|
+
cat > bin/activate << "EOF"
|
|
525
|
+
#!/bin/bash
|
|
526
|
+
|
|
527
|
+
${envsBackup.join("\n")}
|
|
528
|
+
${envsExport.join("\n")}
|
|
529
|
+
|
|
530
|
+
deactivate(){
|
|
531
|
+
${envsRestore.join("\n")}
|
|
532
|
+
${envsUnset.join("\n")}
|
|
533
|
+
}
|
|
534
|
+
|
|
535
|
+
exec "$@"
|
|
536
|
+
EOF
|
|
537
|
+
|
|
538
|
+
chmod +x $VORPAL_WORKSPACE/bin/activate
|
|
539
|
+
|
|
540
|
+
mkdir -p $VORPAL_OUTPUT/bin
|
|
541
|
+
|
|
542
|
+
cp -pr bin "$VORPAL_OUTPUT"`;
|
|
543
|
+
const steps = [
|
|
544
|
+
await shell(context, this._artifacts, [], stepScript, secretsToProto(this._secrets)),
|
|
545
|
+
];
|
|
546
|
+
return new Artifact(this._name, steps, this._systems).build(context);
|
|
547
|
+
}
|
|
548
|
+
}
|
|
549
|
+
// ---------------------------------------------------------------------------
|
|
550
|
+
// UserEnvironment
|
|
551
|
+
// ---------------------------------------------------------------------------
|
|
552
|
+
/**
|
|
553
|
+
* Builder for UserEnvironment artifacts.
|
|
554
|
+
* Matches Rust sdk/rust/src/artifact.rs UserEnvironment impl (lines 556-682).
|
|
555
|
+
*
|
|
556
|
+
* CRITICAL: Symlinks MUST be sorted by source path (Rust line 586).
|
|
557
|
+
*/
|
|
558
|
+
export class UserEnvironment {
|
|
559
|
+
_artifacts = [];
|
|
560
|
+
_environments = [];
|
|
561
|
+
_name;
|
|
562
|
+
_symlinks = [];
|
|
563
|
+
_systems;
|
|
564
|
+
/**
|
|
565
|
+
* @param name - User environment name
|
|
566
|
+
* @param systems - Target systems this environment supports
|
|
567
|
+
*/
|
|
568
|
+
constructor(name, systems) {
|
|
569
|
+
this._name = name;
|
|
570
|
+
this._systems = systems;
|
|
571
|
+
}
|
|
572
|
+
/**
|
|
573
|
+
* Sets artifact dependencies whose bin directories are added to PATH.
|
|
574
|
+
*
|
|
575
|
+
* @param artifacts - Array of artifact digest strings
|
|
576
|
+
*/
|
|
577
|
+
withArtifacts(artifacts) {
|
|
578
|
+
this._artifacts = artifacts;
|
|
579
|
+
return this;
|
|
580
|
+
}
|
|
581
|
+
/**
|
|
582
|
+
* Sets environment variables exported when the user environment is activated.
|
|
583
|
+
* Format: `"KEY=VALUE"`. PATH entries are handled specially and merged
|
|
584
|
+
* with artifact bin paths.
|
|
585
|
+
*
|
|
586
|
+
* @param environments - Array of environment variable strings
|
|
587
|
+
*/
|
|
588
|
+
withEnvironments(environments) {
|
|
589
|
+
this._environments = environments;
|
|
590
|
+
return this;
|
|
591
|
+
}
|
|
592
|
+
/**
|
|
593
|
+
* Adds symlinks created when the user environment is activated.
|
|
594
|
+
* Symlinks are sorted by source path before building for deterministic output.
|
|
595
|
+
*
|
|
596
|
+
* @param symlinks - Array of `[source, target]` path tuples
|
|
597
|
+
*/
|
|
598
|
+
withSymlinks(symlinks) {
|
|
599
|
+
for (const [source, target] of symlinks) {
|
|
600
|
+
this._symlinks.push([source, target]);
|
|
601
|
+
}
|
|
602
|
+
return this;
|
|
603
|
+
}
|
|
604
|
+
/**
|
|
605
|
+
* Builds the user environment artifact, which includes activate/deactivate
|
|
606
|
+
* scripts and symlink management.
|
|
607
|
+
*
|
|
608
|
+
* @returns The hex-encoded SHA-256 digest of the artifact
|
|
609
|
+
*/
|
|
610
|
+
async build(context) {
|
|
611
|
+
// Sort for deterministic output -- sorted by source path (index 0)
|
|
612
|
+
this._symlinks.sort((a, b) => a[0].localeCompare(b[0]));
|
|
613
|
+
// Setup path
|
|
614
|
+
const stepPathArtifacts = this._artifacts
|
|
615
|
+
.map((artifact) => `${getEnvKey(artifact)}/bin`)
|
|
616
|
+
.join(":");
|
|
617
|
+
let stepPath = stepPathArtifacts;
|
|
618
|
+
const pathEnv = this._environments.find((x) => x.startsWith("PATH="));
|
|
619
|
+
if (pathEnv) {
|
|
620
|
+
const pathValue = pathEnv.split("=").slice(1).join("=");
|
|
621
|
+
if (pathValue) {
|
|
622
|
+
stepPath = `${pathValue}:${stepPath}`;
|
|
623
|
+
}
|
|
624
|
+
}
|
|
625
|
+
// Setup environments for script (filter PATH)
|
|
626
|
+
const stepEnvironments = this._environments
|
|
627
|
+
.filter((e) => !e.startsWith("PATH="))
|
|
628
|
+
.map((e) => `export ${e}`)
|
|
629
|
+
.join("\n");
|
|
630
|
+
const symlinksDeactivate = this._symlinks
|
|
631
|
+
.map(([_, target]) => `rm -f ${target}`)
|
|
632
|
+
.join("\n");
|
|
633
|
+
const symlinksCheck = this._symlinks
|
|
634
|
+
.map(([_, target]) => `if [ -f ${target} ]; then echo "ERROR: Symlink target exists -> ${target}" && exit 1; fi`)
|
|
635
|
+
.join("\n");
|
|
636
|
+
const symlinksActivate = this._symlinks
|
|
637
|
+
.map(([source, target]) => `ln -s ${source} ${target}`)
|
|
638
|
+
.join("\n");
|
|
639
|
+
// Script template matches Rust formatdoc! in UserEnvironment::build()
|
|
640
|
+
const stepScript = `mkdir -p $VORPAL_OUTPUT/bin
|
|
641
|
+
|
|
642
|
+
cat > $VORPAL_OUTPUT/bin/vorpal-activate-shell << "EOF"
|
|
643
|
+
${stepEnvironments}
|
|
644
|
+
export PATH="$VORPAL_OUTPUT/bin:${stepPath}:$PATH"
|
|
645
|
+
EOF
|
|
646
|
+
|
|
647
|
+
cat > $VORPAL_OUTPUT/bin/vorpal-deactivate-symlinks << "EOF"
|
|
648
|
+
#!/bin/bash
|
|
649
|
+
set -euo pipefail
|
|
650
|
+
${symlinksDeactivate}
|
|
651
|
+
EOF
|
|
652
|
+
|
|
653
|
+
cat > $VORPAL_OUTPUT/bin/vorpal-activate-symlinks << "EOF"
|
|
654
|
+
#!/bin/bash
|
|
655
|
+
set -euo pipefail
|
|
656
|
+
${symlinksCheck}
|
|
657
|
+
${symlinksActivate}
|
|
658
|
+
EOF
|
|
659
|
+
|
|
660
|
+
cat > $VORPAL_OUTPUT/bin/vorpal-activate << "EOF"
|
|
661
|
+
#!/bin/bash
|
|
662
|
+
set -euo pipefail
|
|
663
|
+
|
|
664
|
+
echo "Deactivating previous symlinks..."
|
|
665
|
+
|
|
666
|
+
if [ -f $HOME/.vorpal/bin/vorpal-deactivate-symlinks ]; then
|
|
667
|
+
$HOME/.vorpal/bin/vorpal-deactivate-symlinks
|
|
668
|
+
fi
|
|
669
|
+
|
|
670
|
+
echo "Activating symlinks..."
|
|
671
|
+
|
|
672
|
+
$VORPAL_OUTPUT/bin/vorpal-activate-symlinks
|
|
673
|
+
|
|
674
|
+
echo "Vorpal userenv installed. Run 'source vorpal-activate-shell' to activate."
|
|
675
|
+
|
|
676
|
+
ln -sf $VORPAL_OUTPUT/bin/vorpal-activate-shell $HOME/.vorpal/bin/vorpal-activate-shell
|
|
677
|
+
ln -sf $VORPAL_OUTPUT/bin/vorpal-activate-symlinks $HOME/.vorpal/bin/vorpal-activate-symlinks
|
|
678
|
+
ln -sf $VORPAL_OUTPUT/bin/vorpal-deactivate-symlinks $HOME/.vorpal/bin/vorpal-deactivate-symlinks
|
|
679
|
+
EOF
|
|
680
|
+
|
|
681
|
+
|
|
682
|
+
chmod +x $VORPAL_OUTPUT/bin/vorpal-activate-shell
|
|
683
|
+
chmod +x $VORPAL_OUTPUT/bin/vorpal-deactivate-symlinks
|
|
684
|
+
chmod +x $VORPAL_OUTPUT/bin/vorpal-activate-symlinks
|
|
685
|
+
chmod +x $VORPAL_OUTPUT/bin/vorpal-activate`;
|
|
686
|
+
const steps = [
|
|
687
|
+
await shell(context, this._artifacts, [], stepScript, []),
|
|
688
|
+
];
|
|
689
|
+
return new Artifact(this._name, steps, this._systems).build(context);
|
|
690
|
+
}
|
|
691
|
+
}
|
|
692
|
+
// ---------------------------------------------------------------------------
|
|
693
|
+
// OciImage
|
|
694
|
+
// ---------------------------------------------------------------------------
|
|
695
|
+
/**
|
|
696
|
+
* Builder for OCI container image artifacts.
|
|
697
|
+
* Matches Rust sdk/rust/src/artifact/oci_image.rs OciImage impl.
|
|
698
|
+
*
|
|
699
|
+
* Produces a container image tarball using crane, rsync, and a rootfs artifact.
|
|
700
|
+
* Only supports Linux systems (AARCH64_LINUX, X8664_LINUX).
|
|
701
|
+
*/
|
|
702
|
+
export class OciImage {
|
|
703
|
+
_aliases = [];
|
|
704
|
+
_artifacts = [];
|
|
705
|
+
_crane = undefined;
|
|
706
|
+
_name;
|
|
707
|
+
_rootfs;
|
|
708
|
+
_rsync = undefined;
|
|
709
|
+
/**
|
|
710
|
+
* @param name - OCI image name (must be lowercase, valid chars: a-z, 0-9, / : - . _)
|
|
711
|
+
* @param rootfs - Artifact digest for the rootfs base layer
|
|
712
|
+
*/
|
|
713
|
+
constructor(name, rootfs) {
|
|
714
|
+
this._name = name;
|
|
715
|
+
this._rootfs = rootfs;
|
|
716
|
+
}
|
|
717
|
+
/**
|
|
718
|
+
* Adds human-readable aliases for this image artifact.
|
|
719
|
+
*
|
|
720
|
+
* @param aliases - Array of alias strings
|
|
721
|
+
*/
|
|
722
|
+
withAliases(aliases) {
|
|
723
|
+
this._aliases = aliases;
|
|
724
|
+
return this;
|
|
725
|
+
}
|
|
726
|
+
/**
|
|
727
|
+
* Sets artifact digests to include as layers in the container image.
|
|
728
|
+
* Each artifact's bin directory is symlinked into /usr/local/bin.
|
|
729
|
+
*
|
|
730
|
+
* @param artifacts - Array of artifact digest strings
|
|
731
|
+
*/
|
|
732
|
+
withArtifacts(artifacts) {
|
|
733
|
+
this._artifacts = artifacts;
|
|
734
|
+
return this;
|
|
735
|
+
}
|
|
736
|
+
/**
|
|
737
|
+
* Overrides the default crane artifact used for building the OCI image.
|
|
738
|
+
* When not set, crane is fetched from the registry alias "crane:0.21.1".
|
|
739
|
+
*
|
|
740
|
+
* @param crane - Artifact digest for crane
|
|
741
|
+
*/
|
|
742
|
+
withCrane(crane) {
|
|
743
|
+
this._crane = crane;
|
|
744
|
+
return this;
|
|
745
|
+
}
|
|
746
|
+
/**
|
|
747
|
+
* Overrides the default rsync artifact used for building the OCI image.
|
|
748
|
+
* When not set, rsync is fetched from the registry alias "rsync:3.4.1".
|
|
749
|
+
*
|
|
750
|
+
* @param rsync - Artifact digest for rsync
|
|
751
|
+
*/
|
|
752
|
+
withRsync(rsync) {
|
|
753
|
+
this._rsync = rsync;
|
|
754
|
+
return this;
|
|
755
|
+
}
|
|
756
|
+
/**
|
|
757
|
+
* Builds the OCI image artifact and registers it with the agent service.
|
|
758
|
+
*
|
|
759
|
+
* Fetches crane and rsync as pre-built aliases from the registry,
|
|
760
|
+
* generates the build script, and creates the artifact.
|
|
761
|
+
*
|
|
762
|
+
* @returns The hex-encoded SHA-256 digest of the artifact
|
|
763
|
+
*/
|
|
764
|
+
async build(context) {
|
|
765
|
+
// Validate image name (matches Rust validation)
|
|
766
|
+
if (this._name !== this._name.toLowerCase()) {
|
|
767
|
+
throw new Error(`container image name must be lowercase: '${this._name}'`);
|
|
768
|
+
}
|
|
769
|
+
for (const c of this._name) {
|
|
770
|
+
if (!((c >= "a" && c <= "z") ||
|
|
771
|
+
(c >= "0" && c <= "9") ||
|
|
772
|
+
c === "/" ||
|
|
773
|
+
c === ":" ||
|
|
774
|
+
c === "-" ||
|
|
775
|
+
c === "." ||
|
|
776
|
+
c === "_")) {
|
|
777
|
+
throw new Error(`container image name invalid character '${c}': '${this._name}'. ` +
|
|
778
|
+
`Allowed: lowercase letters, digits, and / : - . _`);
|
|
779
|
+
}
|
|
780
|
+
}
|
|
781
|
+
const crane = this._crane ?? await context.fetchArtifactAlias("crane:0.21.1");
|
|
782
|
+
const rsync = this._rsync ?? await context.fetchArtifactAlias("rsync:3.4.1");
|
|
783
|
+
const artifactsList = this._artifacts.join(" ");
|
|
784
|
+
const namespace = context.getArtifactNamespace();
|
|
785
|
+
const stepScript = `OCI_IMAGE_ARTIFACTS="${artifactsList}"
|
|
786
|
+
OCI_IMAGE_CRANE="${getEnvKey(crane)}"
|
|
787
|
+
OCI_IMAGE_NAME="${this._name}"
|
|
788
|
+
OCI_IMAGE_ROOTFS="${getEnvKey(this._rootfs)}"
|
|
789
|
+
OCI_IMAGE_RSYNC="${getEnvKey(rsync)}"
|
|
790
|
+
OUTPUT_TAR=\${PWD}/rootfs.tar
|
|
791
|
+
ROOTFS_DIR=\${PWD}/rootfs
|
|
792
|
+
STORE_PREFIX=var/lib/vorpal/store/artifact/output/${namespace}
|
|
793
|
+
|
|
794
|
+
# Detect platform based on build architecture
|
|
795
|
+
case "$(uname -m)" in
|
|
796
|
+
x86_64) OCI_PLATFORM="linux/amd64" ;;
|
|
797
|
+
aarch64) OCI_PLATFORM="linux/arm64" ;;
|
|
798
|
+
*) OCI_PLATFORM="linux/$(uname -m)" ;;
|
|
799
|
+
esac
|
|
800
|
+
|
|
801
|
+
mkdir -p \${ROOTFS_DIR}
|
|
802
|
+
|
|
803
|
+
for artifact in \${OCI_IMAGE_ARTIFACTS}; do
|
|
804
|
+
SOURCE_DIR=/\${STORE_PREFIX}/\${artifact}
|
|
805
|
+
TARGET_PATH=\${STORE_PREFIX}/\${artifact}
|
|
806
|
+
|
|
807
|
+
mkdir -p \${ROOTFS_DIR}/\${TARGET_PATH}
|
|
808
|
+
|
|
809
|
+
echo "Copying artifact layer \${artifact}..."
|
|
810
|
+
|
|
811
|
+
\${OCI_IMAGE_RSYNC}/bin/rsync -aW \${SOURCE_DIR}/ \${ROOTFS_DIR}/\${TARGET_PATH}
|
|
812
|
+
|
|
813
|
+
echo "Copied artifact layer \${artifact}"
|
|
814
|
+
|
|
815
|
+
# Symlink bin files to /usr/local/bin
|
|
816
|
+
if [ -d "\${SOURCE_DIR}/bin" ]; then
|
|
817
|
+
mkdir -p \${ROOTFS_DIR}/usr/local/bin
|
|
818
|
+
for bin_file in \${SOURCE_DIR}/bin/*; do
|
|
819
|
+
if [ -f "\${bin_file}" ]; then
|
|
820
|
+
bin_name=$(basename "\${bin_file}")
|
|
821
|
+
ln -sf /\${TARGET_PATH}/bin/\${bin_name} \${ROOTFS_DIR}/usr/local/bin/\${bin_name}
|
|
822
|
+
echo "Symlinked \${bin_name} to /usr/local/bin"
|
|
823
|
+
fi
|
|
824
|
+
done
|
|
825
|
+
fi
|
|
826
|
+
done
|
|
827
|
+
|
|
828
|
+
echo "Copying Vorpal operating system files..."
|
|
829
|
+
|
|
830
|
+
\${OCI_IMAGE_RSYNC}/bin/rsync -aW \${OCI_IMAGE_ROOTFS}/ \${ROOTFS_DIR}
|
|
831
|
+
|
|
832
|
+
echo "Copied Vorpal operating system files"
|
|
833
|
+
|
|
834
|
+
echo "Creating output tarball..."
|
|
835
|
+
|
|
836
|
+
tar -cf \${OUTPUT_TAR} -C \${ROOTFS_DIR} .
|
|
837
|
+
|
|
838
|
+
echo "Created output tarball"
|
|
839
|
+
|
|
840
|
+
mkdir -p \${VORPAL_OUTPUT}
|
|
841
|
+
|
|
842
|
+
echo "Creating OCI image \${OCI_IMAGE_NAME}:latest"
|
|
843
|
+
|
|
844
|
+
\${OCI_IMAGE_CRANE}/bin/crane append \\
|
|
845
|
+
--new_layer \${OUTPUT_TAR} \\
|
|
846
|
+
--new_tag \${OCI_IMAGE_NAME}:latest \\
|
|
847
|
+
--oci-empty-base \\
|
|
848
|
+
--output \${VORPAL_OUTPUT}/image.tar \\
|
|
849
|
+
--platform \${OCI_PLATFORM}
|
|
850
|
+
|
|
851
|
+
echo "Setting platform metadata in image config..."
|
|
852
|
+
|
|
853
|
+
# Extract tarball to modify config (crane mutate cannot work with local files)
|
|
854
|
+
WORK_DIR=\${PWD}/image-work
|
|
855
|
+
mkdir -p \${WORK_DIR}
|
|
856
|
+
tar -xf \${VORPAL_OUTPUT}/image.tar -C \${WORK_DIR}
|
|
857
|
+
|
|
858
|
+
# Get config filename from manifest
|
|
859
|
+
CONFIG_FILE=$(sed -n 's/.*"Config":"\\([^"]*\\)".*/\\1/p' \${WORK_DIR}/manifest.json)
|
|
860
|
+
|
|
861
|
+
# Detect architecture for config metadata
|
|
862
|
+
case "$(uname -m)" in
|
|
863
|
+
x86_64) CONFIG_ARCH="amd64" ;;
|
|
864
|
+
aarch64) CONFIG_ARCH="arm64" ;;
|
|
865
|
+
*) CONFIG_ARCH="$(uname -m)" ;;
|
|
866
|
+
esac
|
|
867
|
+
|
|
868
|
+
# Modify config to set platform (crane append leaves these empty)
|
|
869
|
+
sed -i "s/\\"architecture\\":\\"\\"/\\"architecture\\":\\"\${CONFIG_ARCH}\\"/" \${WORK_DIR}/\${CONFIG_FILE}
|
|
870
|
+
sed -i "s/\\"os\\":\\"\\"/\\"os\\":\\"linux\\"/" \${WORK_DIR}/\${CONFIG_FILE}
|
|
871
|
+
|
|
872
|
+
# Compute new hash and rename config file
|
|
873
|
+
NEW_HASH=$(sha256sum \${WORK_DIR}/\${CONFIG_FILE} | awk '{print $1}')
|
|
874
|
+
NEW_CONFIG="sha256:\${NEW_HASH}"
|
|
875
|
+
mv \${WORK_DIR}/\${CONFIG_FILE} \${WORK_DIR}/\${NEW_CONFIG}
|
|
876
|
+
|
|
877
|
+
# Update manifest with new config reference
|
|
878
|
+
sed -i "s|\${CONFIG_FILE}|\${NEW_CONFIG}|" \${WORK_DIR}/manifest.json
|
|
879
|
+
|
|
880
|
+
# Repackage tarball
|
|
881
|
+
pushd \${WORK_DIR}
|
|
882
|
+
tar -cf \${VORPAL_OUTPUT}/image.tar manifest.json \${NEW_CONFIG} *.tar.gz
|
|
883
|
+
popd
|
|
884
|
+
|
|
885
|
+
# Cleanup
|
|
886
|
+
rm -rf \${WORK_DIR}
|
|
887
|
+
|
|
888
|
+
echo "Created OCI image \${OCI_IMAGE_NAME}:latest"`;
|
|
889
|
+
const stepArtifacts = [crane, rsync, this._rootfs, ...this._artifacts];
|
|
890
|
+
const step = await shell(context, stepArtifacts, [], stepScript, []);
|
|
891
|
+
const systems = [
|
|
892
|
+
ArtifactSystem.AARCH64_LINUX,
|
|
893
|
+
ArtifactSystem.X8664_LINUX,
|
|
894
|
+
];
|
|
895
|
+
return new Artifact(this._name, [step], systems)
|
|
896
|
+
.withAliases(this._aliases)
|
|
897
|
+
.build(context);
|
|
898
|
+
}
|
|
899
|
+
}
|
|
900
|
+
// ---------------------------------------------------------------------------
|
|
901
|
+
// Argument
|
|
902
|
+
// ---------------------------------------------------------------------------
|
|
903
|
+
/**
|
|
904
|
+
* Argument builder for artifact variables.
|
|
905
|
+
* Matches Rust sdk/rust/src/artifact.rs Argument impl.
|
|
906
|
+
*/
|
|
907
|
+
export class Argument {
|
|
908
|
+
_name;
|
|
909
|
+
_require = false;
|
|
910
|
+
/**
|
|
911
|
+
* @param name - The variable name to look up in the context
|
|
912
|
+
*/
|
|
913
|
+
constructor(name) {
|
|
914
|
+
this._name = name;
|
|
915
|
+
}
|
|
916
|
+
/**
|
|
917
|
+
* Marks this argument as required. If the variable is not set in the
|
|
918
|
+
* context when {@link Argument.build} is called, an error is thrown.
|
|
919
|
+
*/
|
|
920
|
+
withRequire() {
|
|
921
|
+
this._require = true;
|
|
922
|
+
return this;
|
|
923
|
+
}
|
|
924
|
+
/**
|
|
925
|
+
* Resolves the argument value from the {@link ConfigContext}.
|
|
926
|
+
*
|
|
927
|
+
* @returns The variable value, or `undefined` if not set and not required
|
|
928
|
+
* @throws If the variable is required but not set
|
|
929
|
+
*/
|
|
930
|
+
build(context) {
|
|
931
|
+
const variable = context.getVariable(this._name);
|
|
932
|
+
if (this._require && variable === undefined) {
|
|
933
|
+
throw new Error(`variable '${this._name}' is required`);
|
|
934
|
+
}
|
|
935
|
+
return variable;
|
|
936
|
+
}
|
|
937
|
+
}
|
|
938
|
+
//# sourceMappingURL=artifact.js.map
|