datagrok-tools 6.1.6 → 6.1.7
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/CLAUDE.md +22 -33
- package/bin/commands/help.js +21 -0
- package/bin/commands/publish.js +12 -0
- package/bin/commands/run.js +132 -0
- package/bin/grok.js +1 -0
- package/package.json +1 -1
package/CLAUDE.md
CHANGED
|
@@ -1,31 +1,12 @@
|
|
|
1
1
|
# CLAUDE.md
|
|
2
2
|
|
|
3
|
-
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
|
|
4
|
-
|
|
5
3
|
## Overview
|
|
6
4
|
|
|
7
5
|
This is **datagrok-tools**, a CLI utility for creating, validating, testing, and publishing packages to Datagrok. The tool is distributed as `grok` command globally via npm.
|
|
8
6
|
|
|
9
|
-
## Build
|
|
10
|
-
|
|
11
|
-
### Build the CLI
|
|
12
|
-
```bash
|
|
13
|
-
npm run build # Transpile TypeScript to JavaScript using Babel
|
|
14
|
-
npm run debug-source-map # Build with source maps for debugging
|
|
15
|
-
```
|
|
16
|
-
|
|
17
|
-
The build process uses Babel with `@babel/preset-typescript` to transpile TypeScript files from `bin/` to `bin/` (in-place). **Important:** `.ts` source and `.js` output coexist in the same `bin/` directory — `grok.js` requires the transpiled `.js` files, not the `.ts` sources. After editing any `.ts` file, you must run `npm run build` before testing.
|
|
18
|
-
|
|
19
|
-
### Link for Local Development
|
|
20
|
-
```bash
|
|
21
|
-
npm link # Make 'grok' command available globally for testing
|
|
22
|
-
```
|
|
7
|
+
## Build Notes
|
|
23
8
|
|
|
24
|
-
|
|
25
|
-
While this CLI tool manages testing for Datagrok packages, it doesn't have its own test suite. To test changes:
|
|
26
|
-
1. Build the tool: `npm run build`
|
|
27
|
-
2. Link it: `npm link`
|
|
28
|
-
3. Test commands manually: `grok create test-package`, `grok check`, etc.
|
|
9
|
+
The build process uses Babel with `@babel/preset-typescript` to transpile TypeScript files from `bin/` to `bin/` (in-place). `.ts` source and `.js` output coexist in the same `bin/` directory -- `grok.js` requires the transpiled `.js` files, not the `.ts` sources. After editing any `.ts` file, you must run `npm run build` before testing.
|
|
29
10
|
|
|
30
11
|
## Code Architecture
|
|
31
12
|
|
|
@@ -110,14 +91,32 @@ The `publish` command executes these steps:
|
|
|
110
91
|
1. Gather files using `ignore-walk` (respects .npmignore/.gitignore/.grokignore)
|
|
111
92
|
2. Run validation checks (signatures, imports, package.json, changelog)
|
|
112
93
|
3. Process environment variables in `/connections/*.json` files (replace `${VAR}`)
|
|
113
|
-
4.
|
|
114
|
-
5.
|
|
94
|
+
4. **Process Docker images** (see below)
|
|
95
|
+
5. Create ZIP archive with archiver-promise (includes `image.json` metadata per container)
|
|
96
|
+
6. Upload to server: `POST ${host}/packages/dev/${devKey}/${packageName}`
|
|
115
97
|
|
|
116
98
|
**Key flags:**
|
|
117
99
|
- `--debug` (default) - Package visible only to developer
|
|
118
100
|
- `--release` - Public package
|
|
119
101
|
- `--build` / `--rebuild` - Control webpack bundling
|
|
120
102
|
- `--skip-check` - Skip validation
|
|
103
|
+
- `--rebuild-docker` - Force rebuild Docker images locally before pushing to registry
|
|
104
|
+
- `--skip-docker-rebuild` - Skip auto-rebuild even when dockerfile folder has changed
|
|
105
|
+
|
|
106
|
+
### Docker Image Handling in `publish`
|
|
107
|
+
|
|
108
|
+
Implemented in `bin/commands/publish.ts` (`processDockerImages()`, `discoverDockerfiles()`).
|
|
109
|
+
|
|
110
|
+
When a package has a `dockerfiles/` directory, `grok publish` automatically manages Docker images:
|
|
111
|
+
|
|
112
|
+
1. **Discovery** — scans `dockerfiles/` for subdirectories containing a `Dockerfile`. Image name = `<packageName>-<folderName>`.
|
|
113
|
+
2. **Change detection** — computes SHA256 of the entire dockerfile directory. If the hash differs from what the server has, rebuilds automatically (unless `--skip-docker-rebuild`).
|
|
114
|
+
3. **Build** — `docker build --platform linux/amd64` (always cross-compiled for linux/amd64).
|
|
115
|
+
4. **Registry** — tags and pushes to configured Docker registry if one is set.
|
|
116
|
+
5. **Metadata** — writes `image.json` into the ZIP for each container so the server knows which image to use.
|
|
117
|
+
6. **Fallback** — if a local build isn't available, queries the server for a compatible pre-built image.
|
|
118
|
+
|
|
119
|
+
**Practical implication:** editing any file inside `dockerfiles/<name>/` will cause `grok publish` to rebuild that container image on the next publish, without needing `--rebuild-docker`.
|
|
121
120
|
|
|
122
121
|
### Testing Framework
|
|
123
122
|
|
|
@@ -273,16 +272,6 @@ servers:
|
|
|
273
272
|
key: ''
|
|
274
273
|
```
|
|
275
274
|
|
|
276
|
-
### Webpack Externals
|
|
277
|
-
|
|
278
|
-
The CLI validates that imports match webpack externals to prevent bundling datagrok-api and other provided libraries. Common externals:
|
|
279
|
-
- `datagrok-api`
|
|
280
|
-
- `rxjs`
|
|
281
|
-
- `cash-dom`
|
|
282
|
-
- `dayjs`
|
|
283
|
-
- `openchemlib/full`
|
|
284
|
-
- `wu`
|
|
285
|
-
|
|
286
275
|
## File References
|
|
287
276
|
|
|
288
277
|
When working with commands, key entry points are:
|
package/bin/commands/help.js
CHANGED
|
@@ -22,6 +22,7 @@ Commands:
|
|
|
22
22
|
link Link \`datagrok-api\` and libraries for local development
|
|
23
23
|
publish Upload a package
|
|
24
24
|
report Manage user error reports (fetch, resolve, create ticket)
|
|
25
|
+
run Build, publish, and open in browser
|
|
25
26
|
test Run package tests
|
|
26
27
|
testall Run packages tests
|
|
27
28
|
migrate Migrate legacy tags to meta.role
|
|
@@ -322,6 +323,25 @@ Examples:
|
|
|
322
323
|
// file and converting your scripts in the \`package.json\` file
|
|
323
324
|
// `;
|
|
324
325
|
|
|
326
|
+
const HELP_RUN = `
|
|
327
|
+
Usage: grok run [host]
|
|
328
|
+
|
|
329
|
+
Build, publish, and open the package in the browser.
|
|
330
|
+
|
|
331
|
+
Runs \`grok build\`, publishes the package to the server, then opens the server in the default browser.
|
|
332
|
+
|
|
333
|
+
Options:
|
|
334
|
+
[-k | --key] [--release] [-v | --verbose]
|
|
335
|
+
|
|
336
|
+
--key Developer key (overrides config)
|
|
337
|
+
--release Publish as a release version (default: debug)
|
|
338
|
+
--verbose Print detailed output
|
|
339
|
+
|
|
340
|
+
Examples:
|
|
341
|
+
grok run Build, publish to default server, and open browser
|
|
342
|
+
grok run dev Build, publish to 'dev' server alias, and open browser
|
|
343
|
+
grok run https://my.datagrok.ai/api --key abc123
|
|
344
|
+
`;
|
|
325
345
|
const HELP_REPORT = `
|
|
326
346
|
Usage: grok report <subcommand> <instance> <id>
|
|
327
347
|
|
|
@@ -352,6 +372,7 @@ const help = exports.help = {
|
|
|
352
372
|
link: HELP_LINK,
|
|
353
373
|
publish: HELP_PUBLISH,
|
|
354
374
|
report: HELP_REPORT,
|
|
375
|
+
run: HELP_RUN,
|
|
355
376
|
test: HELP_TEST,
|
|
356
377
|
testall: HELP_TESTALL,
|
|
357
378
|
migrate: HELP_MIGRATE,
|
package/bin/commands/publish.js
CHANGED
|
@@ -60,6 +60,18 @@ function discoverDockerfiles(packageName, version, debug) {
|
|
|
60
60
|
fullLocalName: `${imageName}:${imageTag}`
|
|
61
61
|
});
|
|
62
62
|
}
|
|
63
|
+
|
|
64
|
+
// Handle Dockerfile directly in dockerfiles/ (single-container layout)
|
|
65
|
+
const rootDockerfile = _path.default.join(dockerfilesDir, 'Dockerfile');
|
|
66
|
+
if (_fs.default.existsSync(rootDockerfile)) {
|
|
67
|
+
const cleanName = utils.removeScope(packageName).toLowerCase();
|
|
68
|
+
results.push({
|
|
69
|
+
imageName: cleanName,
|
|
70
|
+
imageTag: version,
|
|
71
|
+
dirName: '.',
|
|
72
|
+
fullLocalName: `${cleanName}:${version}`
|
|
73
|
+
});
|
|
74
|
+
}
|
|
63
75
|
return results;
|
|
64
76
|
}
|
|
65
77
|
function dockerCommand(args) {
|
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault");
|
|
4
|
+
Object.defineProperty(exports, "__esModule", {
|
|
5
|
+
value: true
|
|
6
|
+
});
|
|
7
|
+
exports.run = run;
|
|
8
|
+
var _fs = _interopRequireDefault(require("fs"));
|
|
9
|
+
var _os = _interopRequireDefault(require("os"));
|
|
10
|
+
var _path = _interopRequireDefault(require("path"));
|
|
11
|
+
var _child_process = require("child_process");
|
|
12
|
+
var _jsYaml = _interopRequireDefault(require("js-yaml"));
|
|
13
|
+
var utils = _interopRequireWildcard(require("../utils/utils"));
|
|
14
|
+
var color = _interopRequireWildcard(require("../utils/color-utils"));
|
|
15
|
+
var _publish = require("./publish");
|
|
16
|
+
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); }
|
|
17
|
+
const grokDir = _path.default.join(_os.default.homedir(), '.grok');
|
|
18
|
+
const confPath = _path.default.join(grokDir, 'config.yaml');
|
|
19
|
+
function getWebUrl(apiUrl) {
|
|
20
|
+
const u = new URL(apiUrl);
|
|
21
|
+
u.pathname = u.pathname.replace(/\/api\/?$/, '') || '/';
|
|
22
|
+
return u.toString().replace(/\/$/, '');
|
|
23
|
+
}
|
|
24
|
+
function openBrowser(url) {
|
|
25
|
+
let command;
|
|
26
|
+
switch (process.platform) {
|
|
27
|
+
case 'darwin':
|
|
28
|
+
command = `open "${url}"`;
|
|
29
|
+
break;
|
|
30
|
+
case 'win32':
|
|
31
|
+
command = `start "" "${url}"`;
|
|
32
|
+
break;
|
|
33
|
+
default:
|
|
34
|
+
command = `xdg-open "${url}"`;
|
|
35
|
+
}
|
|
36
|
+
(0, _child_process.exec)(command, err => {
|
|
37
|
+
if (err) color.warn(`Could not open browser: ${err.message}`);
|
|
38
|
+
});
|
|
39
|
+
}
|
|
40
|
+
const MISSING_MODULE_PATTERNS = ['cannot find module', 'module not found', 'can\'t resolve'];
|
|
41
|
+
async function buildPackage(dir) {
|
|
42
|
+
const buildCmd = 'npm run build -- --env incremental';
|
|
43
|
+
const packageJson = JSON.parse(_fs.default.readFileSync(_path.default.join(dir, 'package.json'), 'utf-8'));
|
|
44
|
+
const name = packageJson.friendlyName || packageJson.name;
|
|
45
|
+
console.log(`Building ${name}...`);
|
|
46
|
+
try {
|
|
47
|
+
await utils.runScript(buildCmd, dir, color.isVerbose());
|
|
48
|
+
color.success(`Successfully built ${name}`);
|
|
49
|
+
return true;
|
|
50
|
+
} catch (error) {
|
|
51
|
+
const msg = (error?.message ?? '').toLowerCase();
|
|
52
|
+
if (MISSING_MODULE_PATTERNS.some(p => msg.includes(p))) {
|
|
53
|
+
color.warn('Missing modules detected, running npm install...');
|
|
54
|
+
try {
|
|
55
|
+
await utils.runScript('npm install', dir, color.isVerbose());
|
|
56
|
+
await utils.runScript(buildCmd, dir, color.isVerbose());
|
|
57
|
+
color.success(`Successfully built ${name}`);
|
|
58
|
+
return true;
|
|
59
|
+
} catch (retryError) {
|
|
60
|
+
color.error(`Failed to build ${name}`);
|
|
61
|
+
if (retryError.message) color.error(retryError.message);
|
|
62
|
+
return false;
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
color.error(`Failed to build ${name}`);
|
|
66
|
+
if (error.message) color.error(error.message);
|
|
67
|
+
return false;
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
async function run(args) {
|
|
71
|
+
color.setVerbose(args.verbose || false);
|
|
72
|
+
|
|
73
|
+
// Step 1: Build (skip npm install to preserve npm link; retry with npm install only if needed)
|
|
74
|
+
const built = await buildPackage(process.cwd());
|
|
75
|
+
if (!built) return false;
|
|
76
|
+
|
|
77
|
+
// Step 2: Resolve server URL and key
|
|
78
|
+
if (!_fs.default.existsSync(confPath)) {
|
|
79
|
+
color.error(`Config not found at ${confPath}. Run \`grok config\` first.`);
|
|
80
|
+
return false;
|
|
81
|
+
}
|
|
82
|
+
const config = _jsYaml.default.load(_fs.default.readFileSync(confPath, {
|
|
83
|
+
encoding: 'utf-8'
|
|
84
|
+
}));
|
|
85
|
+
const urls = utils.mapURL(config);
|
|
86
|
+
let host = config.default;
|
|
87
|
+
if (args['_'].length === 2) host = args['_'][1];
|
|
88
|
+
let key = '';
|
|
89
|
+
let url = '';
|
|
90
|
+
let registry;
|
|
91
|
+
try {
|
|
92
|
+
url = new URL(host).href;
|
|
93
|
+
if (url.endsWith('/')) url = url.slice(0, -1);
|
|
94
|
+
if (url in urls) {
|
|
95
|
+
const alias = urls[url];
|
|
96
|
+
key = config['servers'][alias]['key'];
|
|
97
|
+
registry = config['servers'][alias]['registry'];
|
|
98
|
+
}
|
|
99
|
+
} catch (error) {
|
|
100
|
+
if (!(host in config.servers)) {
|
|
101
|
+
color.error(`Unknown server alias. Please add it to ${confPath}`);
|
|
102
|
+
return false;
|
|
103
|
+
}
|
|
104
|
+
url = config['servers'][host]['url'];
|
|
105
|
+
key = config['servers'][host]['key'];
|
|
106
|
+
registry = config['servers'][host]['registry'];
|
|
107
|
+
}
|
|
108
|
+
if (args.key) key = args.key;
|
|
109
|
+
if (key === '') {
|
|
110
|
+
color.warn('Please provide the key with `--key` option or add it by running `grok config`');
|
|
111
|
+
return false;
|
|
112
|
+
}
|
|
113
|
+
const packDir = _path.default.join(process.cwd(), 'package.json');
|
|
114
|
+
if (!_fs.default.existsSync(packDir)) {
|
|
115
|
+
color.error('`package.json` doesn\'t exist');
|
|
116
|
+
return false;
|
|
117
|
+
}
|
|
118
|
+
const packageName = JSON.parse(_fs.default.readFileSync(packDir, {
|
|
119
|
+
encoding: 'utf-8'
|
|
120
|
+
})).name;
|
|
121
|
+
|
|
122
|
+
// Step 3: Publish
|
|
123
|
+
process.env.NODE_TLS_REJECT_UNAUTHORIZED = '0';
|
|
124
|
+
const code = await (0, _publish.processPackage)(!args.release, false, url, key, packageName, false, undefined, host, registry);
|
|
125
|
+
if (code !== 0) return false;
|
|
126
|
+
|
|
127
|
+
// Step 4: Open browser
|
|
128
|
+
const webUrl = getWebUrl(url);
|
|
129
|
+
color.success(`Opening ${webUrl}`);
|
|
130
|
+
openBrowser(webUrl);
|
|
131
|
+
return true;
|
|
132
|
+
}
|
package/bin/grok.js
CHANGED
|
@@ -19,6 +19,7 @@ const commands = {
|
|
|
19
19
|
link: require('./commands/link').link,
|
|
20
20
|
publish: require('./commands/publish').publish,
|
|
21
21
|
report: require('./commands/report').report,
|
|
22
|
+
run: require('./commands/run').run,
|
|
22
23
|
test: require('./commands/test').test,
|
|
23
24
|
testall: require('./commands/test-all').testAll,
|
|
24
25
|
stresstest: require('./commands/stress-tests').stressTests,
|
package/package.json
CHANGED