hologit 0.46.1 → 0.47.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/bin/cli.js CHANGED
@@ -21,7 +21,7 @@ module.exports = { logger };
21
21
 
22
22
 
23
23
  // route command line
24
- require('yargs')
24
+ require('yargs')(process.argv.slice(2))
25
25
  .version(require('../package.json').version)
26
26
  .option('d', {
27
27
  alias: 'debug',
@@ -58,4 +58,4 @@ require('yargs')
58
58
  })
59
59
  .strict()
60
60
  .help()
61
- .argv;
61
+ .parse();
@@ -10,6 +10,11 @@ exports.builder = {
10
10
  describe: 'A commit message to use if commit-branch is specified',
11
11
  type: 'string'
12
12
  },
13
+ 'commit-source-parent': {
14
+ describe: 'Include the source commit as a second parent in the projection commit',
15
+ type: 'boolean',
16
+ default: true
17
+ },
13
18
  'ref': {
14
19
  describe: 'Commit ref to read holobranch from',
15
20
  default: 'HEAD'
@@ -53,6 +58,7 @@ exports.handler = async function project ({
53
58
  watch = false,
54
59
  commitTo = null,
55
60
  commitMessage = null,
61
+ commitSourceParent = true,
56
62
  cacheFrom = null,
57
63
  cacheTo = null
58
64
  }) {
@@ -136,7 +142,7 @@ exports.handler = async function project ({
136
142
  lens,
137
143
  commitTo,
138
144
  commitMessage,
139
- parentCommit,
145
+ parentCommit: commitSourceParent ? parentCommit : null,
140
146
  fetch,
141
147
  cacheFrom,
142
148
  cacheTo
@@ -156,7 +162,7 @@ exports.handler = async function project ({
156
162
  lens,
157
163
  commitTo,
158
164
  commitMessage,
159
- parentCommit: newCommitHash,
165
+ parentCommit: commitSourceParent ? newCommitHash : null,
160
166
  cacheFrom,
161
167
  cacheTo
162
168
  });
package/jest.config.js ADDED
@@ -0,0 +1,7 @@
1
+ module.exports = {
2
+ testEnvironment: 'node',
3
+ testMatch: ['**/test/**/*.test.js'],
4
+ testPathIgnorePatterns: ['/node_modules/'],
5
+ setupFilesAfterEnv: ['<rootDir>/test/setup.js'],
6
+ testTimeout: 30000, // Git operations can be slow
7
+ };
package/lib/Lens.js CHANGED
@@ -110,33 +110,46 @@ class Lens extends Configurable {
110
110
  }
111
111
  }
112
112
 
113
- async buildSpecForContainer (inputTree, config) {
114
- const { container: containerQuery } = config;
115
-
116
- // check if image exists locally first
117
- let imageHash;
113
+ async getImageHash (containerQuery) {
114
+ // Try local first (fastest)
118
115
  try {
119
116
  const inspectOutput = await Studio.execDocker(['inspect', containerQuery]);
120
117
  const imageInfo = JSON.parse(inspectOutput)[0];
121
- imageHash = imageInfo.Id;
122
- logger.info(`found local image: ${containerQuery}@${imageHash}`);
118
+ logger.info(`found local image: ${containerQuery}@${imageInfo.Id}`);
119
+ return { hash: imageInfo.Id, isLocal: true };
123
120
  } catch (err) {
124
- // image doesn't exist locally or can't be inspected, try pulling
125
- logger.info(`pulling image: ${containerQuery}`);
126
-
127
- try {
128
- await Studio.execDocker(['pull', containerQuery], { $relayStdout: true });
129
- const inspectOutput = await Studio.execDocker(['inspect', containerQuery]);
130
- const imageInfo = JSON.parse(inspectOutput)[0];
131
- imageHash = imageInfo.Id;
132
- } catch (err) {
133
- throw new Error(`failed to pull container image ${containerQuery}: ${err.message}`);
134
- }
121
+ // Not found locally, try remote manifest
135
122
  }
136
123
 
137
- if (!imageHash) {
138
- throw new Error(`failed to get hash for container image ${containerQuery}`);
124
+ // Try remote manifest (doesn't pull the image)
125
+ try {
126
+ logger.info(`querying remote manifest for: ${containerQuery}`);
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
+ }
138
+
139
+ // Ensure the digest is in the format sha256:...
140
+ const imageHash = digest.startsWith('sha256:') ? digest : `sha256:${digest}`;
141
+ logger.info(`found remote image hash: ${containerQuery}@${imageHash}`);
142
+ return { hash: imageHash, isLocal: false };
143
+ } catch (err) {
144
+ throw new Error(`failed to get hash for container image ${containerQuery}: ${err.message}`);
139
145
  }
146
+ }
147
+
148
+ async buildSpecForContainer (inputTree, config) {
149
+ const { container: containerQuery } = config;
150
+
151
+ // Get image hash without pulling
152
+ const { hash: imageHash, isLocal } = await this.getImageHash(containerQuery);
140
153
 
141
154
  // build spec
142
155
  const data = {
@@ -152,7 +165,8 @@ class Lens extends Configurable {
152
165
  return {
153
166
  ...await SpecObject.write(this.workspace.getRepo(), 'lens', data),
154
167
  data,
155
- type: 'container'
168
+ type: 'container',
169
+ imageIsLocal: isLocal
156
170
  };
157
171
  }
158
172
 
@@ -317,6 +331,26 @@ class Lens extends Configurable {
317
331
  }
318
332
  const [, sha256Hash] = containerMatch;
319
333
 
334
+ // Ensure image is available locally
335
+ try {
336
+ await Studio.execDocker(['inspect', sha256Hash]);
337
+ } catch (err) {
338
+ // Image not found locally, need to pull it
339
+ // Extract the original container query from spec to pull by name:tag
340
+ const containerQueryMatch = spec.container.match(/^(.+)@sha256:[a-f0-9]{64}$/);
341
+ if (containerQueryMatch) {
342
+ const containerQuery = containerQueryMatch[1];
343
+ logger.info(`pulling required image: ${containerQuery}`);
344
+ try {
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}`);
351
+ }
352
+ }
353
+
320
354
  // create and start container
321
355
  const persistentDebugContainer = process.env.HOLO_DEBUG_PERSIST_CONTAINER;
322
356
  let containerId;
package/lib/Repo.js CHANGED
@@ -196,7 +196,7 @@ class Repo {
196
196
  }
197
197
 
198
198
  async watch ({ callback }) {
199
- const debounce = require('debounce');
199
+ const { default: debounce } = await import('debounce');
200
200
  const logger = require('../lib/logger.js');
201
201
 
202
202
 
@@ -313,7 +313,7 @@ class Repo {
313
313
  cancel
314
314
  };
315
315
  } else {
316
- const chokidar = require('chokidar');
316
+ const { default: chokidar } = await import('chokidar');
317
317
  const watcher = chokidar.watch([], {
318
318
  persistent: true,
319
319
  cwd: this.gitDir,
package/lib/SpecObject.js CHANGED
@@ -1,4 +1,3 @@
1
- const sortKeys = require('sort-keys');
2
1
  const TOML = require('@iarna/toml');
3
2
 
4
3
 
@@ -8,7 +7,8 @@ const BlobObject = require('./BlobObject.js');
8
7
  class SpecObject extends BlobObject {
9
8
 
10
9
  static async write (repo, kind, data) {
11
- // build ordered spec object
10
+ // build ordered spec object (sort-keys is ESM-only, requires dynamic import)
11
+ const { default: sortKeys } = await import('sort-keys');
12
12
  const holospec = {};
13
13
  holospec[kind] = sortKeys(data, { deep: true });
14
14
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "hologit",
3
- "version": "0.46.1",
3
+ "version": "0.47.0",
4
4
  "description": "Hologit automates the projection of layered composite file trees based on flat, declarative plans",
5
5
  "repository": "https://github.com/EmergencePlatform/hologit",
6
6
  "main": "lib/index.js",
@@ -11,29 +11,29 @@
11
11
  "dependencies": {
12
12
  "@iarna/toml": "^2.2.5",
13
13
  "async-exit-hook": "^2.0.1",
14
- "axios": "^1.7.7",
15
- "chokidar": "^4.0.1",
16
- "debounce": "^2.0.0",
17
- "fb-watchman": "^2.0.1",
14
+ "axios": "^1.13.2",
15
+ "chokidar": "^5.0.0",
16
+ "debounce": "^3.0.0",
17
+ "fb-watchman": "^2.0.2",
18
18
  "git-client": "^1.9.4",
19
19
  "hab-client": "^1.1.3",
20
- "handlebars": "^4.7.6",
21
- "minimatch": "^10.0.1",
22
- "mz": "^2.4.0",
20
+ "handlebars": "^4.7.8",
21
+ "minimatch": "^10.1.1",
22
+ "mz": "^2.7.0",
23
23
  "mz-modules": "^2.1.0",
24
24
  "object-squish": "^1.1.0",
25
- "parse-url": "^9.2.0",
25
+ "parse-url": "^10.0.3",
26
26
  "shell-quote-word": "^1.0.1",
27
- "sort-keys": "^4.2.0",
27
+ "sort-keys": "^6.0.0",
28
28
  "toposort": "^2.0.2",
29
- "winston": "^3.17.0",
30
- "yargs": "^17.5.1"
29
+ "winston": "^3.19.0",
30
+ "yargs": "^18.0.0"
31
31
  },
32
32
  "bin": {
33
33
  "git-holo": "./bin/cli.js"
34
34
  },
35
35
  "engines": {
36
- "node": ">=8.3.0"
36
+ "node": "^20.19.0 || ^22.12.0 || >=23"
37
37
  },
38
38
  "os": [
39
39
  "darwin",
@@ -48,11 +48,8 @@
48
48
  "projection"
49
49
  ],
50
50
  "devDependencies": {
51
- "@types/jest": "^29.1.2",
52
- "jest": "^29.5.0"
53
- },
54
- "jest": {
55
- "testEnvironment": "node"
51
+ "@types/jest": "^30.0.0",
52
+ "jest": "^30.2.0"
56
53
  },
57
54
  "scripts": {
58
55
  "test": "jest"