datagrok-tools 6.4.6 → 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,13 @@
|
|
|
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
|
+
|
|
7
|
+
## 6.4.7 (2026-07-22)
|
|
8
|
+
|
|
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).
|
|
10
|
+
|
|
3
11
|
## 6.4.6 (2026-07-22)
|
|
4
12
|
|
|
5
13
|
* `grok test` — the whole-run Puppeteer cap (60 min) is now overridable via `GROK_TEST_INVOCATION_TIMEOUT_MS`. When the cap fires mid-pass all collected results are discarded (empty `test-report.csv`, "Passed tests: 0"), which CI reports as "no results produced"; loaded CI agents can now raise the cap instead.
|
|
@@ -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
|
}
|
package/bin/commands/publish.js
CHANGED
|
@@ -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;
|
|
@@ -468,6 +475,13 @@ async function processPackage(debug, rebuild, host, devKey, packageName, dropDb,
|
|
|
468
475
|
const chunks = [];
|
|
469
476
|
zip.on('data', chunk => chunks.push(chunk));
|
|
470
477
|
|
|
478
|
+
// Generate Celery / Node-worker Docker artifacts BEFORE gathering files: the
|
|
479
|
+
// generated dockerfiles/<dir>/Dockerfile must land in the zip, or the server
|
|
480
|
+
// never binds the queue funcs to the client-built image and falls back to a
|
|
481
|
+
// differently-named server-side container that never starts
|
|
482
|
+
// (<pkg>-queue-celery vs the built <pkg>-queue).
|
|
483
|
+
const generatedDockerDirs = [...(0, _pythonCeleryGen.generateCeleryArtifacts)(curDir), ...(0, _queueWorkerGen.generateQueueArtifacts)(curDir)];
|
|
484
|
+
|
|
471
485
|
// Gather the files
|
|
472
486
|
const localTimestamps = {};
|
|
473
487
|
const files = await (0, _ignoreWalk.default)({
|
|
@@ -558,19 +572,13 @@ async function processPackage(debug, rebuild, host, devKey, packageName, dropDb,
|
|
|
558
572
|
return 1;
|
|
559
573
|
}
|
|
560
574
|
|
|
561
|
-
// Generate Celery Docker artifacts from python/ if present
|
|
562
|
-
(0, _pythonCeleryGen.generateCeleryArtifacts)(curDir);
|
|
563
|
-
|
|
564
|
-
// Generate the Node worker container for meta.queue JS functions if present
|
|
565
|
-
(0, _queueWorkerGen.generateQueueArtifacts)(curDir);
|
|
566
|
-
|
|
567
575
|
// Process Docker images and inject image.json into the ZIP
|
|
568
576
|
let dockerVersion = json.version;
|
|
569
577
|
if (debug) {
|
|
570
578
|
const userInfo = await getUserLogin(host, devKey);
|
|
571
579
|
if (userInfo) dockerVersion = userInfo.login;
|
|
572
580
|
}
|
|
573
|
-
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);
|
|
574
582
|
zip.append(JSON.stringify(localTimestamps), {
|
|
575
583
|
name: 'timestamps.json'
|
|
576
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
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
253
|
+
if (deployFolder(packageDir, pythonDir, 'celery', base)) generated.push('celery');
|
|
251
254
|
}
|
|
252
255
|
return generated;
|
|
253
256
|
}
|
|
@@ -9,9 +9,10 @@ var _fs = _interopRequireDefault(require("fs"));
|
|
|
9
9
|
var _path = _interopRequireDefault(require("path"));
|
|
10
10
|
var color = _interopRequireWildcard(require("./color-utils"));
|
|
11
11
|
function _interopRequireWildcard(e, t) { if ("function" == typeof WeakMap) var r = new WeakMap(), n = new WeakMap(); return (_interopRequireWildcard = function (e, t) { if (!t && e && e.__esModule) return e; var o, i, f = { __proto__: null, default: e }; if (null === e || "object" != typeof e && "function" != typeof e) return f; if (o = t ? n : r) { if (o.has(e)) return o.get(e); o.set(e, f); } for (const t in e) "default" !== t && {}.hasOwnProperty.call(e, t) && ((i = (o = Object.defineProperty) && Object.getOwnPropertyDescriptor(e, t)) && (i.get || i.set) ? o(f, t, i) : f[t] = e[t]); return f; })(e, t); }
|
|
12
|
-
// Matches `//meta.queue: true` in package.ts —
|
|
13
|
-
// in the Node worker
|
|
14
|
-
|
|
12
|
+
// Matches `//meta.queue: true` (or its alias `//meta.server: true`) in package.ts —
|
|
13
|
+
// functions executed as celery tasks in the Node worker
|
|
14
|
+
// (see help/develop/how-to/packages/js-server-functions.md).
|
|
15
|
+
const queueTrueRegex = /^\s*\/\/\s*meta\.(queue|server)\s*:\s*true\s*$/m;
|
|
15
16
|
const QUEUE_DIR = 'queue';
|
|
16
17
|
const TEMPLATE_NODE_DOCKER = `FROM datagrok/celery_node_worker:bleeding-edge
|
|
17
18
|
EXPOSE 8000
|
|
@@ -20,11 +21,12 @@ EXPOSE 8000
|
|
|
20
21
|
/** Generates dockerfiles/queue/ for packages with \`meta.queue: true\` functions so the
|
|
21
22
|
* standard docker build/push/image.json flow covers the auto-created Node worker
|
|
22
23
|
* container (mirror of generateCeleryArtifacts for python/). The worker fetches the
|
|
23
|
-
* 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). */
|
|
24
26
|
function generateQueueArtifacts(packageDir) {
|
|
25
27
|
const candidates = ['package.ts', 'package.g.ts'].map(f => _path.default.join(packageDir, 'src', f)).filter(f => _fs.default.existsSync(f));
|
|
26
28
|
const hasQueueFuncs = candidates.some(f => queueTrueRegex.test(_fs.default.readFileSync(f, 'utf-8')));
|
|
27
|
-
if (!hasQueueFuncs) return
|
|
29
|
+
if (!hasQueueFuncs) return [];
|
|
28
30
|
const dockerfilesDir = _path.default.join(packageDir, 'dockerfiles', QUEUE_DIR);
|
|
29
31
|
const dockerfilePath = _path.default.join(dockerfilesDir, 'Dockerfile');
|
|
30
32
|
_fs.default.mkdirSync(dockerfilesDir, {
|
|
@@ -37,5 +39,5 @@ function generateQueueArtifacts(packageDir) {
|
|
|
37
39
|
const containerJsonDest = _path.default.join(dockerfilesDir, 'container.json');
|
|
38
40
|
if (_fs.default.existsSync(containerJsonSrc) && !_fs.default.existsSync(containerJsonDest)) _fs.default.copyFileSync(containerJsonSrc, containerJsonDest);
|
|
39
41
|
color.log(`Generated Node worker Docker artifacts in dockerfiles/${QUEUE_DIR}/`);
|
|
40
|
-
return
|
|
42
|
+
return [QUEUE_DIR];
|
|
41
43
|
}
|
package/package.json
CHANGED