datagrok-tools 6.1.1 → 6.1.4
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/commands/help.js +11 -10
- package/bin/commands/publish.js +87 -21
- package/bin/grok.js +2 -0
- package/package.json +1 -1
package/bin/commands/help.js
CHANGED
|
@@ -158,16 +158,17 @@ Uploads a package
|
|
|
158
158
|
Checks for errors before publishing — the package won't be published if there are any.
|
|
159
159
|
|
|
160
160
|
Options:
|
|
161
|
-
[--all] [--refresh] [--link] [--build] [--release] [--rebuild-docker] [--skip-check] [-v | --verbose]
|
|
162
|
-
|
|
163
|
-
--all
|
|
164
|
-
--refresh
|
|
165
|
-
--link
|
|
166
|
-
--build
|
|
167
|
-
--release
|
|
168
|
-
--rebuild-docker
|
|
169
|
-
--skip-
|
|
170
|
-
--
|
|
161
|
+
[--all] [--refresh] [--link] [--build] [--release] [--rebuild-docker] [--skip-docker-rebuild] [--skip-check] [-v | --verbose]
|
|
162
|
+
|
|
163
|
+
--all Publish all available packages (run in packages directory)
|
|
164
|
+
--refresh Publish all available already loaded packages (run in packages directory)
|
|
165
|
+
--link Link the package to local packages
|
|
166
|
+
--build Builds the package
|
|
167
|
+
--release Publish package as release version
|
|
168
|
+
--rebuild-docker Force rebuild Docker images locally before pushing to registry
|
|
169
|
+
--skip-docker-rebuild Skip auto-rebuild when Dockerfile folder has changed
|
|
170
|
+
--skip-check Skip check stage
|
|
171
|
+
--verbose Print detailed output
|
|
171
172
|
|
|
172
173
|
Running \`grok publish\` is the same as running \`grok publish defaultHost --build --debug\`
|
|
173
174
|
`;
|
package/bin/commands/publish.js
CHANGED
|
@@ -7,6 +7,7 @@ Object.defineProperty(exports, "__esModule", {
|
|
|
7
7
|
exports.processPackage = processPackage;
|
|
8
8
|
exports.publish = publish;
|
|
9
9
|
var _archiverPromise = _interopRequireDefault(require("archiver-promise"));
|
|
10
|
+
var _crypto = _interopRequireDefault(require("crypto"));
|
|
10
11
|
var _fs = _interopRequireDefault(require("fs"));
|
|
11
12
|
var _nodeFetch = _interopRequireDefault(require("node-fetch"));
|
|
12
13
|
var _os = _interopRequireDefault(require("os"));
|
|
@@ -49,7 +50,7 @@ function discoverDockerfiles(packageName, version, debug) {
|
|
|
49
50
|
if (!entry.isDirectory()) continue;
|
|
50
51
|
const dockerfilePath = _path.default.join(dockerfilesDir, entry.name, 'Dockerfile');
|
|
51
52
|
if (!_fs.default.existsSync(dockerfilePath)) continue;
|
|
52
|
-
const cleanName = utils.removeScope(packageName).
|
|
53
|
+
const cleanName = utils.removeScope(packageName).toLowerCase();
|
|
53
54
|
const imageName = `${cleanName}-${entry.name.toLowerCase()}`;
|
|
54
55
|
const imageTag = debug ? version : `${version}.X`;
|
|
55
56
|
results.push({
|
|
@@ -95,7 +96,10 @@ function dockerTag(source, target) {
|
|
|
95
96
|
}
|
|
96
97
|
function dockerPush(image) {
|
|
97
98
|
try {
|
|
98
|
-
|
|
99
|
+
execSync(`docker push ${image}`, {
|
|
100
|
+
encoding: 'utf-8',
|
|
101
|
+
stdio: 'inherit'
|
|
102
|
+
});
|
|
99
103
|
return true;
|
|
100
104
|
} catch (e) {
|
|
101
105
|
color.error(`Failed to push ${image}: ${e.message || e}`);
|
|
@@ -115,6 +119,50 @@ function dockerBuild(imageName, dockerfilePath, context) {
|
|
|
115
119
|
return false;
|
|
116
120
|
}
|
|
117
121
|
}
|
|
122
|
+
function calculateFolderHash(dirPath) {
|
|
123
|
+
const hash = _crypto.default.createHash('sha256');
|
|
124
|
+
const entries = listRecursive(dirPath, '');
|
|
125
|
+
entries.sort((a, b) => a.relPath < b.relPath ? -1 : a.relPath > b.relPath ? 1 : 0);
|
|
126
|
+
for (const entry of entries) {
|
|
127
|
+
if (entry.isDir) {
|
|
128
|
+
color.log(` Hash entry: dir:${entry.relPath}:`);
|
|
129
|
+
hash.update(`dir:${entry.relPath}:`);
|
|
130
|
+
} else {
|
|
131
|
+
// Normalize CRLF to LF to match server-side storage
|
|
132
|
+
const raw = _fs.default.readFileSync(entry.fullPath);
|
|
133
|
+
const content = raw.includes(0x0d) && !raw.includes(0x00) ? Buffer.from(raw.toString('utf8').replace(/\r\n/g, '\n'), 'utf8') : raw;
|
|
134
|
+
color.log(` Hash entry: file:${entry.relPath}: (${content.length} bytes)`);
|
|
135
|
+
hash.update(`file:${entry.relPath}:`);
|
|
136
|
+
hash.update(content);
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
const result = hash.digest('hex');
|
|
140
|
+
color.log(` Folder hash: ${result}`);
|
|
141
|
+
return result;
|
|
142
|
+
}
|
|
143
|
+
function listRecursive(basePath, rel) {
|
|
144
|
+
const results = [];
|
|
145
|
+
const fullDir = rel ? _path.default.join(basePath, rel) : basePath;
|
|
146
|
+
for (const entry of _fs.default.readdirSync(fullDir, {
|
|
147
|
+
withFileTypes: true
|
|
148
|
+
})) {
|
|
149
|
+
const relPath = rel ? `${rel}/${entry.name}` : entry.name;
|
|
150
|
+
const fullPath = _path.default.join(fullDir, entry.name);
|
|
151
|
+
if (entry.isDirectory()) {
|
|
152
|
+
results.push({
|
|
153
|
+
relPath,
|
|
154
|
+
fullPath,
|
|
155
|
+
isDir: true
|
|
156
|
+
});
|
|
157
|
+
results.push(...listRecursive(basePath, relPath));
|
|
158
|
+
} else results.push({
|
|
159
|
+
relPath,
|
|
160
|
+
fullPath,
|
|
161
|
+
isDir: false
|
|
162
|
+
});
|
|
163
|
+
}
|
|
164
|
+
return results;
|
|
165
|
+
}
|
|
118
166
|
async function getUserLogin(host, devKey) {
|
|
119
167
|
let loginResp;
|
|
120
168
|
try {
|
|
@@ -144,14 +192,18 @@ async function getUserLogin(host, devKey) {
|
|
|
144
192
|
return null;
|
|
145
193
|
}
|
|
146
194
|
}
|
|
147
|
-
async function resolveLatestCompatible(host, devKey, dockerName) {
|
|
195
|
+
async function resolveLatestCompatible(host, devKey, dockerName, version, contentHash) {
|
|
148
196
|
const userInfo = await getUserLogin(host, devKey);
|
|
149
197
|
if (!userInfo) return {
|
|
150
198
|
found: null,
|
|
151
199
|
serverError: `Authentication failed. Check your developer key.`
|
|
152
200
|
};
|
|
153
201
|
try {
|
|
154
|
-
|
|
202
|
+
let url = `${host}/docker/images/${encodeURIComponent(dockerName)}/latest-compatible`;
|
|
203
|
+
const params = [];
|
|
204
|
+
if (version) params.push(`version=${encodeURIComponent(version)}`);
|
|
205
|
+
if (contentHash) params.push(`contentHash=${encodeURIComponent(contentHash)}`);
|
|
206
|
+
if (params.length > 0) url += '?' + params.join('&');
|
|
155
207
|
const resp = await (0, _nodeFetch.default)(url, {
|
|
156
208
|
headers: {
|
|
157
209
|
'Authorization': userInfo.token
|
|
@@ -176,29 +228,31 @@ async function resolveLatestCompatible(host, devKey, dockerName) {
|
|
|
176
228
|
};
|
|
177
229
|
}
|
|
178
230
|
}
|
|
179
|
-
async function processDockerImages(packageName, version, registry, devKey, host, rebuildDocker, zip, localTimestamps, debug) {
|
|
231
|
+
async function processDockerImages(packageName, version, registry, devKey, host, rebuildDocker, zip, localTimestamps, debug, skipDockerRebuild = false) {
|
|
180
232
|
const dockerImages = discoverDockerfiles(packageName, version, debug);
|
|
181
233
|
if (dockerImages.length === 0) return;
|
|
182
234
|
color.log(`Found ${dockerImages.length} Dockerfile(s)`);
|
|
183
235
|
if (registry) dockerLogin(registry, devKey);
|
|
184
236
|
for (const img of dockerImages) {
|
|
237
|
+
color.info(`Processing docker image ${img.fullLocalName}...`);
|
|
185
238
|
let result;
|
|
239
|
+
const dockerfileDir = _path.default.join('dockerfiles', img.dirName);
|
|
240
|
+
const dockerfilePath = _path.default.join(dockerfileDir, 'Dockerfile');
|
|
241
|
+
const contentHash = calculateFolderHash(_path.default.join(curDir, dockerfileDir));
|
|
186
242
|
if (rebuildDocker) {
|
|
187
|
-
const dockerfileDir = _path.default.join('dockerfiles', img.dirName);
|
|
188
|
-
const dockerfilePath = _path.default.join(dockerfileDir, 'Dockerfile');
|
|
189
243
|
if (dockerBuild(img.fullLocalName, dockerfilePath, dockerfileDir)) {
|
|
190
244
|
result = pushImage(img, registry);
|
|
191
245
|
color.success(`Built and tagged ${img.fullLocalName}`);
|
|
192
246
|
} else {
|
|
193
|
-
result = await fallbackImage(img, host, devKey, registry);
|
|
247
|
+
result = await fallbackImage(img, host, devKey, registry, version, contentHash);
|
|
194
248
|
}
|
|
195
249
|
} else if (localImageExists(img.fullLocalName)) {
|
|
196
|
-
result = pushImage(img, registry);
|
|
197
250
|
color.success(`Found local image ${img.fullLocalName}`);
|
|
251
|
+
result = pushImage(img, registry);
|
|
198
252
|
} else {
|
|
199
253
|
color.warn(`Local image not found. Expected: ${img.fullLocalName}`);
|
|
200
|
-
color.log(` Build it with: docker build -t ${img.fullLocalName} -f
|
|
201
|
-
const fallback = await fallbackImage(img, host, devKey, registry);
|
|
254
|
+
color.log(` Build it with: docker build -t ${img.fullLocalName} -f ${dockerfilePath} ${dockerfileDir}`);
|
|
255
|
+
const fallback = await fallbackImage(img, host, devKey, registry, version, contentHash);
|
|
202
256
|
if (fallback.serverError) {
|
|
203
257
|
color.error(`Cannot resolve fallback: ${fallback.serverError}`);
|
|
204
258
|
result = {
|
|
@@ -206,14 +260,25 @@ async function processDockerImages(packageName, version, registry, devKey, host,
|
|
|
206
260
|
fallback: true,
|
|
207
261
|
requestedVersion: img.imageTag
|
|
208
262
|
};
|
|
209
|
-
} else if (fallback.image) {
|
|
263
|
+
} else if (fallback.image && fallback.hashMatch === true) {
|
|
210
264
|
result = fallback;
|
|
211
|
-
color.
|
|
265
|
+
color.success(`Falling back to ${fallback.image} (dockerfile unchanged)`);
|
|
266
|
+
} else if (fallback.image && fallback.hashMatch === false && !skipDockerRebuild) {
|
|
267
|
+
color.warn(`Dockerfile folder has changed. Rebuilding image...`);
|
|
268
|
+
if (dockerBuild(img.fullLocalName, dockerfilePath, dockerfileDir)) {
|
|
269
|
+
result = pushImage(img, registry);
|
|
270
|
+
color.success(`Built and tagged ${img.fullLocalName}`);
|
|
271
|
+
} else {
|
|
272
|
+
result = {
|
|
273
|
+
image: fallback.image,
|
|
274
|
+
fallback: true,
|
|
275
|
+
requestedVersion: img.imageTag
|
|
276
|
+
};
|
|
277
|
+
color.warn(`Build failed. Falling back to ${fallback.image} (hash mismatch)`);
|
|
278
|
+
}
|
|
212
279
|
} else {
|
|
213
280
|
// No fallback and no local image — must build
|
|
214
281
|
color.warn(`No fallback available. Building ${img.fullLocalName}...`);
|
|
215
|
-
const dockerfileDir = _path.default.join('dockerfiles', img.dirName);
|
|
216
|
-
const dockerfilePath = _path.default.join(dockerfileDir, 'Dockerfile');
|
|
217
282
|
if (dockerBuild(img.fullLocalName, dockerfilePath, dockerfileDir)) {
|
|
218
283
|
result = pushImage(img, registry);
|
|
219
284
|
color.success(`Built and tagged ${img.fullLocalName}`);
|
|
@@ -256,11 +321,11 @@ function pushImage(img, registry) {
|
|
|
256
321
|
fallback: false
|
|
257
322
|
};
|
|
258
323
|
}
|
|
259
|
-
async function fallbackImage(img, host, devKey, registry) {
|
|
324
|
+
async function fallbackImage(img, host, devKey, registry, version, contentHash) {
|
|
260
325
|
const {
|
|
261
326
|
found,
|
|
262
327
|
serverError
|
|
263
|
-
} = await resolveLatestCompatible(host, devKey, img.imageName);
|
|
328
|
+
} = await resolveLatestCompatible(host, devKey, img.imageName, version, contentHash);
|
|
264
329
|
if (serverError) return {
|
|
265
330
|
image: null,
|
|
266
331
|
fallback: true,
|
|
@@ -270,7 +335,8 @@ async function fallbackImage(img, host, devKey, registry) {
|
|
|
270
335
|
if (found) return {
|
|
271
336
|
image: found.image,
|
|
272
337
|
fallback: true,
|
|
273
|
-
requestedVersion: img.imageTag
|
|
338
|
+
requestedVersion: img.imageTag,
|
|
339
|
+
hashMatch: found.hashMatch
|
|
274
340
|
};
|
|
275
341
|
return {
|
|
276
342
|
image: null,
|
|
@@ -278,7 +344,7 @@ async function fallbackImage(img, host, devKey, registry) {
|
|
|
278
344
|
requestedVersion: img.imageTag
|
|
279
345
|
};
|
|
280
346
|
}
|
|
281
|
-
async function processPackage(debug, rebuild, host, devKey, packageName, dropDb, suffix, hostAlias, registry, rebuildDocker) {
|
|
347
|
+
async function processPackage(debug, rebuild, host, devKey, packageName, dropDb, suffix, hostAlias, registry, rebuildDocker, skipDockerRebuild) {
|
|
282
348
|
// Validate server connectivity and dev key
|
|
283
349
|
let timestamps = {};
|
|
284
350
|
let url = `${host}/packages/dev/${devKey}/${packageName}`;
|
|
@@ -400,7 +466,7 @@ async function processPackage(debug, rebuild, host, devKey, packageName, dropDb,
|
|
|
400
466
|
const userInfo = await getUserLogin(host, devKey);
|
|
401
467
|
if (userInfo) dockerVersion = userInfo.login;
|
|
402
468
|
}
|
|
403
|
-
await processDockerImages(packageName, dockerVersion, registry, devKey, host, rebuildDocker ?? false, zip, localTimestamps, debug);
|
|
469
|
+
await processDockerImages(packageName, dockerVersion, registry, devKey, host, rebuildDocker ?? false, zip, localTimestamps, debug, skipDockerRebuild ?? false);
|
|
404
470
|
zip.append(JSON.stringify(localTimestamps), {
|
|
405
471
|
name: 'timestamps.json'
|
|
406
472
|
});
|
|
@@ -545,7 +611,7 @@ async function publishPackage(args) {
|
|
|
545
611
|
if (!args.suffix && stdout) args.suffix = stdout.toString().substring(0, 8);
|
|
546
612
|
});
|
|
547
613
|
await utils.delay(100);
|
|
548
|
-
code = await processPackage(!args.release, Boolean(args.rebuild), url, key, packageName, args.dropDb ?? false, args.suffix, host, registry, args['rebuild-docker']);
|
|
614
|
+
code = await processPackage(!args.release, Boolean(args.rebuild), url, key, packageName, args.dropDb ?? false, args.suffix, host, registry, args['rebuild-docker'], args['skip-docker-rebuild']);
|
|
549
615
|
} catch (error) {
|
|
550
616
|
console.error(error);
|
|
551
617
|
code = 1;
|
package/bin/grok.js
CHANGED
|
@@ -27,6 +27,8 @@ const commands = {
|
|
|
27
27
|
const onPackageCommandNames = ['api', 'check', 'link', 'publish', 'test'];
|
|
28
28
|
|
|
29
29
|
const command = argv['_'][0];
|
|
30
|
+
if (command !== 'test' && command !== 'stresstest')
|
|
31
|
+
delete argv.dartium;
|
|
30
32
|
if (command in commands) {
|
|
31
33
|
try {
|
|
32
34
|
if (argv['help']) {
|
package/package.json
CHANGED