hologit 0.47.3 → 0.47.5
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/lib/Lens.js +55 -52
- package/lib/Projection.js +2 -2
- package/package.json +1 -1
package/lib/Lens.js
CHANGED
|
@@ -111,45 +111,44 @@ class Lens extends Configurable {
|
|
|
111
111
|
}
|
|
112
112
|
|
|
113
113
|
async getImageHash (containerQuery) {
|
|
114
|
-
//
|
|
114
|
+
// Get manifest digest from registry without pulling.
|
|
115
|
+
// This is the only identifier that is consistent across all machines
|
|
116
|
+
// and Docker storage backends (graphdriver vs containerd-snapshotter).
|
|
117
|
+
let manifestDigest;
|
|
115
118
|
try {
|
|
116
|
-
|
|
117
|
-
const
|
|
118
|
-
|
|
119
|
-
|
|
119
|
+
logger.info(`querying registry for manifest digest: ${containerQuery}`);
|
|
120
|
+
const output = await Studio.execDocker([
|
|
121
|
+
'buildx', 'imagetools', 'inspect', '--format', '{{json .}}', containerQuery
|
|
122
|
+
]);
|
|
123
|
+
const info = JSON.parse(output);
|
|
124
|
+
manifestDigest = info.manifest.digest;
|
|
120
125
|
} catch (err) {
|
|
121
|
-
|
|
126
|
+
throw new Error(`failed to get manifest digest for container image ${containerQuery}: ${err.message}`);
|
|
122
127
|
}
|
|
123
128
|
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
const manifestOutput = await Studio.execDocker(['manifest', 'inspect', containerQuery]);
|
|
128
|
-
const manifest = JSON.parse(manifestOutput);
|
|
129
|
-
|
|
130
|
-
// Extract digest from manifest
|
|
131
|
-
// For multi-arch images, manifest.config.digest contains the config digest
|
|
132
|
-
// For single-arch images, we may need to look at the digest field
|
|
133
|
-
const digest = manifest.config?.digest || manifest.digest;
|
|
134
|
-
|
|
135
|
-
if (!digest) {
|
|
136
|
-
throw new Error('No digest found in manifest');
|
|
137
|
-
}
|
|
129
|
+
if (!manifestDigest) {
|
|
130
|
+
throw new Error(`no manifest digest found for ${containerQuery}`);
|
|
131
|
+
}
|
|
138
132
|
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
133
|
+
// Check if already available locally by digest reference
|
|
134
|
+
const repoName = containerQuery.replace(/:.*$/, '');
|
|
135
|
+
let isLocal = false;
|
|
136
|
+
try {
|
|
137
|
+
await Studio.execDocker(['inspect', `${repoName}@${manifestDigest}`]);
|
|
138
|
+
isLocal = true;
|
|
143
139
|
} catch (err) {
|
|
144
|
-
|
|
140
|
+
// not local
|
|
145
141
|
}
|
|
142
|
+
|
|
143
|
+
logger.info(`image ${containerQuery} manifest digest: ${manifestDigest} (local: ${isLocal})`);
|
|
144
|
+
return { hash: manifestDigest, isLocal };
|
|
146
145
|
}
|
|
147
146
|
|
|
148
147
|
async buildSpecForContainer (inputTree, config) {
|
|
149
148
|
const { container: containerQuery } = config;
|
|
150
149
|
|
|
151
|
-
// Get
|
|
152
|
-
const { hash: imageHash
|
|
150
|
+
// Get manifest digest from registry without pulling
|
|
151
|
+
const { hash: imageHash } = await this.getImageHash(containerQuery);
|
|
153
152
|
|
|
154
153
|
// build spec
|
|
155
154
|
const data = {
|
|
@@ -165,8 +164,7 @@ class Lens extends Configurable {
|
|
|
165
164
|
return {
|
|
166
165
|
...await SpecObject.write(this.workspace.getRepo(), 'lens', data),
|
|
167
166
|
data,
|
|
168
|
-
type: 'container'
|
|
169
|
-
imageIsLocal: isLocal
|
|
167
|
+
type: 'container'
|
|
170
168
|
};
|
|
171
169
|
}
|
|
172
170
|
|
|
@@ -249,11 +247,11 @@ class Lens extends Configurable {
|
|
|
249
247
|
};
|
|
250
248
|
}
|
|
251
249
|
|
|
252
|
-
async executeSpec (
|
|
253
|
-
return Lens.executeSpec(
|
|
250
|
+
async executeSpec (specHash, options) {
|
|
251
|
+
return Lens.executeSpec(specHash, {...options, repo: this.workspace.getRepo()});
|
|
254
252
|
}
|
|
255
253
|
|
|
256
|
-
static async executeSpec (
|
|
254
|
+
static async executeSpec (specHash, options) {
|
|
257
255
|
const { refresh=false, cacheFrom=null, cacheTo=null, save=true } = options;
|
|
258
256
|
|
|
259
257
|
|
|
@@ -262,6 +260,16 @@ class Lens extends Configurable {
|
|
|
262
260
|
const git = await repo.getGit();
|
|
263
261
|
|
|
264
262
|
|
|
263
|
+
// determine spec type from content
|
|
264
|
+
const specToml = await git.catFile({ p: true }, specHash);
|
|
265
|
+
const { holospec: { lens: spec } } = TOML.parse(specToml);
|
|
266
|
+
const specType = spec.container ? 'container' : spec.package ? 'habitat' : null;
|
|
267
|
+
|
|
268
|
+
if (!specType) {
|
|
269
|
+
throw new Error(`unable to determine spec type: spec has neither container nor package field`);
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
|
|
265
273
|
// check for existing build
|
|
266
274
|
const specRef = SpecObject.buildRef('lens', specHash);
|
|
267
275
|
if (!refresh) {
|
|
@@ -324,30 +332,25 @@ class Lens extends Configurable {
|
|
|
324
332
|
m: specToml
|
|
325
333
|
});
|
|
326
334
|
|
|
327
|
-
//
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
335
|
+
// use full digest reference (e.g. "ghcr.io/hologit/lenses/shell@sha256:0f8acb01...")
|
|
336
|
+
// for all docker operations — this is the manifest digest, which works
|
|
337
|
+
// consistently across Docker storage backends
|
|
338
|
+
const containerRef = spec.container;
|
|
339
|
+
if (!containerRef.match(/^.+@sha256:[a-f0-9]{64}$/)) {
|
|
340
|
+
throw new Error(`Invalid container format: ${containerRef}`);
|
|
331
341
|
}
|
|
332
|
-
const [, sha256Hash] = containerMatch;
|
|
333
342
|
|
|
334
343
|
// Ensure image is available locally
|
|
335
344
|
try {
|
|
336
|
-
await Studio.execDocker(['inspect',
|
|
345
|
+
await Studio.execDocker(['inspect', containerRef]);
|
|
337
346
|
} catch (err) {
|
|
338
|
-
//
|
|
339
|
-
//
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
await Studio.execDocker(['pull', containerQuery], { $relayStdout: true });
|
|
346
|
-
} catch (pullErr) {
|
|
347
|
-
throw new Error(`failed to pull container image ${containerQuery}: ${pullErr.message}`);
|
|
348
|
-
}
|
|
349
|
-
} else {
|
|
350
|
-
throw new Error(`cannot extract container query from spec.container: ${spec.container}`);
|
|
347
|
+
// Pull by digest — guarantees we get the exact image version
|
|
348
|
+
// matching the spec that was used for caching
|
|
349
|
+
logger.info(`pulling required image: ${containerRef}`);
|
|
350
|
+
try {
|
|
351
|
+
await Studio.execDocker(['pull', containerRef], { $relayStdout: true });
|
|
352
|
+
} catch (pullErr) {
|
|
353
|
+
throw new Error(`failed to pull container image ${containerRef}: ${pullErr.message}`);
|
|
351
354
|
}
|
|
352
355
|
}
|
|
353
356
|
|
|
@@ -376,7 +379,7 @@ class Lens extends Configurable {
|
|
|
376
379
|
'-p', '9000:9000',
|
|
377
380
|
...(persistentDebugContainer ? ['--name', persistentDebugContainer] : []),
|
|
378
381
|
...(process.env.DEBUG ? ['-e', 'DEBUG=1'] : []),
|
|
379
|
-
|
|
382
|
+
containerRef
|
|
380
383
|
]);
|
|
381
384
|
containerId = containerId.trim();
|
|
382
385
|
|
package/lib/Projection.js
CHANGED
|
@@ -162,11 +162,11 @@ class Projection {
|
|
|
162
162
|
|
|
163
163
|
// build tree of matching files to input to lens
|
|
164
164
|
logger.info(`building input tree for lens ${lens.name} from ${inputRoot == '.' ? '' : (path.join(inputRoot, '.')+'/')}{${inputFiles}}`);
|
|
165
|
-
const { hash: specHash
|
|
165
|
+
const { hash: specHash } = await lens.buildSpec(await lens.buildInputTree(this.output.root));
|
|
166
166
|
|
|
167
167
|
|
|
168
168
|
// check for existing output tree
|
|
169
|
-
const outputTreeHash = await lens.executeSpec(
|
|
169
|
+
const outputTreeHash = await lens.executeSpec(specHash, { cacheFrom, cacheTo });
|
|
170
170
|
|
|
171
171
|
|
|
172
172
|
// verify output
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "hologit",
|
|
3
|
-
"version": "0.47.
|
|
3
|
+
"version": "0.47.5",
|
|
4
4
|
"description": "Hologit automates the projection of layered composite file trees based on flat, declarative plans",
|
|
5
5
|
"repository": "https://github.com/JarvusInnovations/hologit",
|
|
6
6
|
"main": "lib/index.js",
|