datagrok-tools 6.1.5 → 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 +40 -0
- package/bin/commands/publish.js +12 -0
- package/bin/commands/report.js +184 -0
- package/bin/commands/run.js +132 -0
- package/bin/grok.js +2 -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
|
@@ -21,6 +21,8 @@ Commands:
|
|
|
21
21
|
init Modify a package template
|
|
22
22
|
link Link \`datagrok-api\` and libraries for local development
|
|
23
23
|
publish Upload a package
|
|
24
|
+
report Manage user error reports (fetch, resolve, create ticket)
|
|
25
|
+
run Build, publish, and open in browser
|
|
24
26
|
test Run package tests
|
|
25
27
|
testall Run packages tests
|
|
26
28
|
migrate Migrate legacy tags to meta.role
|
|
@@ -321,6 +323,42 @@ Examples:
|
|
|
321
323
|
// file and converting your scripts in the \`package.json\` file
|
|
322
324
|
// `;
|
|
323
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
|
+
`;
|
|
345
|
+
const HELP_REPORT = `
|
|
346
|
+
Usage: grok report <subcommand> <instance> <id>
|
|
347
|
+
|
|
348
|
+
Manage Datagrok user error reports
|
|
349
|
+
|
|
350
|
+
Subcommands:
|
|
351
|
+
fetch Download a report zip from a managed instance
|
|
352
|
+
resolve Mark a report as resolved
|
|
353
|
+
ticket Create a JIRA ticket for a report via the Datlas API
|
|
354
|
+
|
|
355
|
+
Examples:
|
|
356
|
+
grok report fetch dev 1528 Download report #1528 from the 'dev' instance
|
|
357
|
+
grok report resolve dev 1528 Resolve report #1528 on the 'dev' instance
|
|
358
|
+
grok report ticket dev <report-uuid> Create a JIRA ticket for a report
|
|
359
|
+
|
|
360
|
+
The instance name must match a server alias in ~/.grok/config.yaml.
|
|
361
|
+
`;
|
|
324
362
|
const help = exports.help = {
|
|
325
363
|
add: HELP_ADD,
|
|
326
364
|
api: HELP_API,
|
|
@@ -333,6 +371,8 @@ const help = exports.help = {
|
|
|
333
371
|
init: HELP_INIT,
|
|
334
372
|
link: HELP_LINK,
|
|
335
373
|
publish: HELP_PUBLISH,
|
|
374
|
+
report: HELP_REPORT,
|
|
375
|
+
run: HELP_RUN,
|
|
336
376
|
test: HELP_TEST,
|
|
337
377
|
testall: HELP_TESTALL,
|
|
338
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,184 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault");
|
|
4
|
+
Object.defineProperty(exports, "__esModule", {
|
|
5
|
+
value: true
|
|
6
|
+
});
|
|
7
|
+
exports.report = report;
|
|
8
|
+
var _fs = _interopRequireDefault(require("fs"));
|
|
9
|
+
var _os = _interopRequireDefault(require("os"));
|
|
10
|
+
var _path = _interopRequireDefault(require("path"));
|
|
11
|
+
var color = _interopRequireWildcard(require("../utils/color-utils"));
|
|
12
|
+
var _testUtils = require("../utils/test-utils");
|
|
13
|
+
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); }
|
|
14
|
+
const fetch = require('node-fetch');
|
|
15
|
+
async function report(args) {
|
|
16
|
+
const subcommand = args._[1];
|
|
17
|
+
switch (subcommand) {
|
|
18
|
+
case 'fetch':
|
|
19
|
+
return await handleFetch(args);
|
|
20
|
+
case 'resolve':
|
|
21
|
+
return await handleResolve(args);
|
|
22
|
+
case 'ticket':
|
|
23
|
+
return await handleTicket(args);
|
|
24
|
+
default:
|
|
25
|
+
return false;
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
async function handleFetch(args) {
|
|
29
|
+
const instance = args._[2];
|
|
30
|
+
const number = args._[3];
|
|
31
|
+
if (!instance || !number) {
|
|
32
|
+
color.error('Usage: grok report fetch <instance> <number>');
|
|
33
|
+
return false;
|
|
34
|
+
}
|
|
35
|
+
try {
|
|
36
|
+
const {
|
|
37
|
+
url,
|
|
38
|
+
key
|
|
39
|
+
} = (0, _testUtils.getDevKey)(instance);
|
|
40
|
+
const token = await (0, _testUtils.getToken)(url, key);
|
|
41
|
+
console.log(`Searching for report #${number}...`);
|
|
42
|
+
const searchResp = await fetch(`${url}/reports?text=number%3D${encodeURIComponent(number)}`, {
|
|
43
|
+
headers: {
|
|
44
|
+
Authorization: token
|
|
45
|
+
}
|
|
46
|
+
});
|
|
47
|
+
if (!searchResp.ok) {
|
|
48
|
+
color.error(`Report search failed (HTTP ${searchResp.status})`);
|
|
49
|
+
return false;
|
|
50
|
+
}
|
|
51
|
+
const results = await searchResp.json();
|
|
52
|
+
if (!Array.isArray(results) || results.length === 0) {
|
|
53
|
+
color.error(`Report #${number} not found`);
|
|
54
|
+
return false;
|
|
55
|
+
}
|
|
56
|
+
const reportData = results[0];
|
|
57
|
+
const reportId = reportData.id || reportData.Id;
|
|
58
|
+
if (!reportId) {
|
|
59
|
+
color.error('Report found but has no id field');
|
|
60
|
+
return false;
|
|
61
|
+
}
|
|
62
|
+
console.log(`Downloading report ${reportId}...`);
|
|
63
|
+
const downloadResp = await fetch(`${url}/reports/${reportId}/zip`, {
|
|
64
|
+
headers: {
|
|
65
|
+
Authorization: token
|
|
66
|
+
}
|
|
67
|
+
});
|
|
68
|
+
if (!downloadResp.ok) {
|
|
69
|
+
color.error(`Report download failed (HTTP ${downloadResp.status})`);
|
|
70
|
+
return false;
|
|
71
|
+
}
|
|
72
|
+
const buffer = await downloadResp.buffer();
|
|
73
|
+
const outputPath = _path.default.join(_os.default.tmpdir(), `report_${instance}_${number}.zip`);
|
|
74
|
+
_fs.default.writeFileSync(outputPath, buffer);
|
|
75
|
+
const metaPath = outputPath.replace('.zip', '_meta.json');
|
|
76
|
+
_fs.default.writeFileSync(metaPath, JSON.stringify(reportData, null, 2));
|
|
77
|
+
color.success(`Report saved to: ${outputPath}`);
|
|
78
|
+
console.log(outputPath);
|
|
79
|
+
return true;
|
|
80
|
+
} catch (err) {
|
|
81
|
+
color.error(`Error: ${err.message}`);
|
|
82
|
+
return false;
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
async function handleResolve(args) {
|
|
86
|
+
const instance = args._[2];
|
|
87
|
+
const number = args._[3];
|
|
88
|
+
if (!instance || !number) {
|
|
89
|
+
color.error('Usage: grok report resolve <instance> <number>');
|
|
90
|
+
return false;
|
|
91
|
+
}
|
|
92
|
+
try {
|
|
93
|
+
const metaPath = _path.default.join(_os.default.tmpdir(), `report_${instance}_${number}_meta.json`);
|
|
94
|
+
if (!_fs.default.existsSync(metaPath)) {
|
|
95
|
+
color.error(`Meta file not found: ${metaPath}`);
|
|
96
|
+
color.warn('Hint: was the report fetched via `grok report fetch` first?');
|
|
97
|
+
return false;
|
|
98
|
+
}
|
|
99
|
+
const meta = JSON.parse(_fs.default.readFileSync(metaPath, 'utf-8'));
|
|
100
|
+
const reportId = meta.id || meta.Id;
|
|
101
|
+
if (!reportId) {
|
|
102
|
+
color.error(`No report id in meta file: ${metaPath}`);
|
|
103
|
+
return false;
|
|
104
|
+
}
|
|
105
|
+
const {
|
|
106
|
+
url,
|
|
107
|
+
key
|
|
108
|
+
} = (0, _testUtils.getDevKey)(instance);
|
|
109
|
+
const token = await (0, _testUtils.getToken)(url, key);
|
|
110
|
+
console.log(`Resolving report #${number} (id: ${reportId})...`);
|
|
111
|
+
const resp = await fetch(`${url}/reports/${reportId}/resolve`, {
|
|
112
|
+
method: 'POST',
|
|
113
|
+
headers: {
|
|
114
|
+
Authorization: token
|
|
115
|
+
}
|
|
116
|
+
});
|
|
117
|
+
if (resp.ok) {
|
|
118
|
+
color.success(`Report #${number} resolved on ${instance}`);
|
|
119
|
+
return true;
|
|
120
|
+
}
|
|
121
|
+
const body = await resp.text();
|
|
122
|
+
color.error(`Resolve failed (HTTP ${resp.status}): ${body}`);
|
|
123
|
+
return false;
|
|
124
|
+
} catch (err) {
|
|
125
|
+
color.error(`Error: ${err.message}`);
|
|
126
|
+
return false;
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
async function handleTicket(args) {
|
|
130
|
+
const instance = args._[2];
|
|
131
|
+
const reportId = args._[3];
|
|
132
|
+
if (!instance || !reportId) {
|
|
133
|
+
color.error('Usage: grok report ticket <instance> <report-id>');
|
|
134
|
+
return false;
|
|
135
|
+
}
|
|
136
|
+
try {
|
|
137
|
+
const {
|
|
138
|
+
url,
|
|
139
|
+
key
|
|
140
|
+
} = (0, _testUtils.getDevKey)(instance);
|
|
141
|
+
const token = await (0, _testUtils.getToken)(url, key);
|
|
142
|
+
console.log('Getting current user...');
|
|
143
|
+
const userResp = await fetch(`${url}/users/current`, {
|
|
144
|
+
headers: {
|
|
145
|
+
Authorization: token
|
|
146
|
+
}
|
|
147
|
+
});
|
|
148
|
+
if (!userResp.ok) {
|
|
149
|
+
color.error(`Failed to get current user (HTTP ${userResp.status})`);
|
|
150
|
+
return false;
|
|
151
|
+
}
|
|
152
|
+
const user = await userResp.json();
|
|
153
|
+
const userId = user.id || user.Id;
|
|
154
|
+
if (!userId) {
|
|
155
|
+
color.error('No user id in response');
|
|
156
|
+
return false;
|
|
157
|
+
}
|
|
158
|
+
console.log(`Creating JIRA ticket for report ${reportId}...`);
|
|
159
|
+
const ticketResp = await fetch(`${url}/reports/${reportId}/jira?assigneeId=${userId}`, {
|
|
160
|
+
method: 'POST',
|
|
161
|
+
headers: {
|
|
162
|
+
Authorization: token,
|
|
163
|
+
'Content-Type': 'application/json'
|
|
164
|
+
}
|
|
165
|
+
});
|
|
166
|
+
if (ticketResp.status !== 200 && ticketResp.status !== 201) {
|
|
167
|
+
const body = await ticketResp.text();
|
|
168
|
+
color.error(`JIRA ticket creation failed (HTTP ${ticketResp.status}): ${body}`);
|
|
169
|
+
return false;
|
|
170
|
+
}
|
|
171
|
+
const result = await ticketResp.json();
|
|
172
|
+
const ticketKey = result.key;
|
|
173
|
+
if (!ticketKey) {
|
|
174
|
+
color.error(`No ticket key in response: ${JSON.stringify(result).slice(0, 200)}`);
|
|
175
|
+
return false;
|
|
176
|
+
}
|
|
177
|
+
color.success(`Created ticket: ${ticketKey}`);
|
|
178
|
+
console.log(ticketKey);
|
|
179
|
+
return true;
|
|
180
|
+
} catch (err) {
|
|
181
|
+
color.error(`Error: ${err.message}`);
|
|
182
|
+
return false;
|
|
183
|
+
}
|
|
184
|
+
}
|
|
@@ -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
|
@@ -18,6 +18,8 @@ const commands = {
|
|
|
18
18
|
init: require('./commands/init').init,
|
|
19
19
|
link: require('./commands/link').link,
|
|
20
20
|
publish: require('./commands/publish').publish,
|
|
21
|
+
report: require('./commands/report').report,
|
|
22
|
+
run: require('./commands/run').run,
|
|
21
23
|
test: require('./commands/test').test,
|
|
22
24
|
testall: require('./commands/test-all').testAll,
|
|
23
25
|
stresstest: require('./commands/stress-tests').stressTests,
|
package/package.json
CHANGED