datagrok-tools 6.4.7 → 6.4.8

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/CHANGELOG.md CHANGED
@@ -1,5 +1,9 @@
1
1
  # Datagrok-tools changelog
2
2
 
3
+ ## 6.4.8 (2026-07-22)
4
+
5
+ * GROK-20452: `grok publish` — always `docker build` the tools-generated worker dirs (`dockerfiles/queue`, `dockerfiles/celery`, `dockerfiles/<app>-celery`) instead of reusing a cached local tag. The "found local image" shortcut froze the worker at whatever stock base the daemon had when the tag was first built (CI ran day-old worker code with a fresh base available); the layer cache keeps an unchanged rebuild near-instant.
6
+
3
7
  ## 6.4.7 (2026-07-22)
4
8
 
5
9
  * GROK-20452: `grok publish` — generate `dockerfiles/celery` / `dockerfiles/queue` **before** gathering files for the zip. They were generated after, so the generated Dockerfile never reached the server: `meta.queue` functions fell back to a server-side container named `<pkg>-queue-celery` while the client had built `<pkg>-queue` — image validation 404'd and the worker container never started ("Container is not started" on every call).
@@ -11,6 +11,6 @@ async function dockerGen(args) {
11
11
  color.setVerbose(args.verbose || args.v || false);
12
12
  const packageDir = process.cwd();
13
13
  const generated = (0, _pythonCeleryGen.generateCeleryArtifacts)(packageDir);
14
- if (generated) color.success('Generated Celery Docker artifacts');else console.log('No python/ directory with annotated functions found');
14
+ if (generated.length) color.success('Generated Celery Docker artifacts');else console.log('No python/ directory with annotated functions found');
15
15
  return true;
16
16
  }
@@ -284,7 +284,7 @@ async function resolveLatestCompatible(host, devKey, dockerName, version, conten
284
284
  };
285
285
  }
286
286
  }
287
- async function processDockerImages(packageName, version, registry, devKey, host, rebuildDocker, zip, localTimestamps, debug, skipDockerRebuild = false) {
287
+ async function processDockerImages(packageName, version, registry, devKey, host, rebuildDocker, zip, localTimestamps, debug, skipDockerRebuild = false, generatedDirs = []) {
288
288
  const dockerImages = discoverDockerfiles(packageName, version, debug);
289
289
  if (dockerImages.length === 0) return;
290
290
  color.log(`Found ${dockerImages.length} Dockerfile(s)`);
@@ -317,6 +317,13 @@ async function processDockerImages(packageName, version, registry, devKey, host,
317
317
  dockerRemove(img.fullLocalName);
318
318
  if (registry) dockerRemove(`${registry}/datagrok/${remoteFullName}`);
319
319
  result = buildAndPush() ?? (await fallbackImage(img, host, devKey, registry, version, contentHash));
320
+ } else if (generatedDirs.includes(img.dirName)) {
321
+ // Generated worker dirs are just FROM the stock base: a cached local tag
322
+ // pins whatever base the daemon had when it was first built (CI served a
323
+ // day-old worker this way). Always run docker build — the layer cache
324
+ // makes an unchanged rebuild near-instant, and a refreshed base lands.
325
+ color.log(`Rebuilding generated image ${img.fullLocalName} against the current base...`);
326
+ result = buildAndPush() ?? (await fallbackImage(img, host, devKey, registry, version, contentHash));
320
327
  } else {
321
328
  // Look for registry-qualified image first, then unqualified
322
329
  let foundLocalName = null;
@@ -473,8 +480,7 @@ async function processPackage(debug, rebuild, host, devKey, packageName, dropDb,
473
480
  // never binds the queue funcs to the client-built image and falls back to a
474
481
  // differently-named server-side container that never starts
475
482
  // (<pkg>-queue-celery vs the built <pkg>-queue).
476
- (0, _pythonCeleryGen.generateCeleryArtifacts)(curDir);
477
- (0, _queueWorkerGen.generateQueueArtifacts)(curDir);
483
+ const generatedDockerDirs = [...(0, _pythonCeleryGen.generateCeleryArtifacts)(curDir), ...(0, _queueWorkerGen.generateQueueArtifacts)(curDir)];
478
484
 
479
485
  // Gather the files
480
486
  const localTimestamps = {};
@@ -572,7 +578,7 @@ async function processPackage(debug, rebuild, host, devKey, packageName, dropDb,
572
578
  const userInfo = await getUserLogin(host, devKey);
573
579
  if (userInfo) dockerVersion = userInfo.login;
574
580
  }
575
- await processDockerImages(packageName, dockerVersion, registry, devKey, host, rebuildDocker ?? false, zip, localTimestamps, debug, skipDockerRebuild ?? false);
581
+ await processDockerImages(packageName, dockerVersion, registry, devKey, host, rebuildDocker ?? false, zip, localTimestamps, debug, skipDockerRebuild ?? false, generatedDockerDirs);
576
582
  zip.append(JSON.stringify(localTimestamps), {
577
583
  name: 'timestamps.json'
578
584
  });
@@ -230,24 +230,27 @@ function deployFolder(packageDir, folderPath, dockerSubfolder, base) {
230
230
  color.log(`Generated Celery Docker artifacts in dockerfiles/${dockerSubfolder}/`);
231
231
  return true;
232
232
  }
233
+
234
+ /** Returns the generated dockerfiles/<dir> names (empty when nothing generated). */
233
235
  function generateCeleryArtifacts(packageDir) {
234
236
  const pythonDir = _path.default.join(packageDir, 'python');
235
- if (!_fs.default.existsSync(pythonDir)) return false;
237
+ if (!_fs.default.existsSync(pythonDir)) return [];
236
238
  const packageJson = JSON.parse(_fs.default.readFileSync(_path.default.join(packageDir, 'package.json'), 'utf-8'));
237
239
  const base = (0, _utils.removeScope)(packageJson.name).toLowerCase();
238
240
  const entries = _fs.default.readdirSync(pythonDir, {
239
241
  withFileTypes: true
240
242
  });
241
243
  const isNested = entries.length > 0 && entries.every(e => e.isDirectory());
242
- let generated = false;
244
+ const generated = [];
243
245
  if (isNested) {
244
246
  for (const entry of entries) {
245
247
  if (!entry.isDirectory()) continue;
246
248
  const folderPath = _path.default.join(pythonDir, entry.name);
247
- if (deployFolder(packageDir, folderPath, `${entry.name.toLowerCase()}-celery`, base)) generated = true;
249
+ const subfolder = `${entry.name.toLowerCase()}-celery`;
250
+ if (deployFolder(packageDir, folderPath, subfolder, base)) generated.push(subfolder);
248
251
  }
249
252
  } else {
250
- if (deployFolder(packageDir, pythonDir, 'celery', base)) generated = true;
253
+ if (deployFolder(packageDir, pythonDir, 'celery', base)) generated.push('celery');
251
254
  }
252
255
  return generated;
253
256
  }
@@ -21,11 +21,12 @@ EXPOSE 8000
21
21
  /** Generates dockerfiles/queue/ for packages with \`meta.queue: true\` functions so the
22
22
  * standard docker build/push/image.json flow covers the auto-created Node worker
23
23
  * container (mirror of generateCeleryArtifacts for python/). The worker fetches the
24
- * package bundle at runtime, so the image is just the stock worker base. */
24
+ * package bundle at runtime, so the image is just the stock worker base.
25
+ * Returns the generated dockerfiles/<dir> names (empty when nothing generated). */
25
26
  function generateQueueArtifacts(packageDir) {
26
27
  const candidates = ['package.ts', 'package.g.ts'].map(f => _path.default.join(packageDir, 'src', f)).filter(f => _fs.default.existsSync(f));
27
28
  const hasQueueFuncs = candidates.some(f => queueTrueRegex.test(_fs.default.readFileSync(f, 'utf-8')));
28
- if (!hasQueueFuncs) return false;
29
+ if (!hasQueueFuncs) return [];
29
30
  const dockerfilesDir = _path.default.join(packageDir, 'dockerfiles', QUEUE_DIR);
30
31
  const dockerfilePath = _path.default.join(dockerfilesDir, 'Dockerfile');
31
32
  _fs.default.mkdirSync(dockerfilesDir, {
@@ -38,5 +39,5 @@ function generateQueueArtifacts(packageDir) {
38
39
  const containerJsonDest = _path.default.join(dockerfilesDir, 'container.json');
39
40
  if (_fs.default.existsSync(containerJsonSrc) && !_fs.default.existsSync(containerJsonDest)) _fs.default.copyFileSync(containerJsonSrc, containerJsonDest);
40
41
  color.log(`Generated Node worker Docker artifacts in dockerfiles/${QUEUE_DIR}/`);
41
- return true;
42
+ return [QUEUE_DIR];
42
43
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "datagrok-tools",
3
- "version": "6.4.7",
3
+ "version": "6.4.8",
4
4
  "description": "Utility to upload and publish packages to Datagrok",
5
5
  "homepage": "https://github.com/datagrok-ai/public/tree/master/tools#readme",
6
6
  "dependencies": {