@openfn/cli 0.0.35 → 0.0.39
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/README.md +68 -22
- package/dist/{chunk-XYZNU5CH.js → chunk-LV5XDERP.js} +20 -5
- package/dist/index.js +56 -32
- package/dist/process/runner.js +507 -225
- package/package.json +6 -7
package/README.md
CHANGED
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
# @openfn/cli
|
|
2
2
|
|
|
3
|
-
This package contains a new devtools CLI for running
|
|
3
|
+
This package contains a new devtools CLI for running OpenFn jobs.
|
|
4
4
|
|
|
5
|
-
The
|
|
5
|
+
The CLI includes:
|
|
6
6
|
|
|
7
|
-
* A
|
|
8
|
-
* A
|
|
9
|
-
*
|
|
10
|
-
* Auto
|
|
7
|
+
* A secure runtime for executing OpenFn jobs and workflows
|
|
8
|
+
* A compiler for making OpenFn jobs runnable
|
|
9
|
+
* Configurable logging output
|
|
10
|
+
* Auto-installation of language adaptors
|
|
11
11
|
* Support for the adaptors monorepo
|
|
12
12
|
|
|
13
13
|
## Getting Started
|
|
@@ -36,41 +36,60 @@ Get help:
|
|
|
36
36
|
openfn help
|
|
37
37
|
```
|
|
38
38
|
|
|
39
|
+
## Updating
|
|
40
|
+
|
|
41
|
+
You should be able to install a new version straight on top of your current installation:
|
|
42
|
+
|
|
43
|
+
```
|
|
44
|
+
npm install -g @openfn/cli
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
If this fails, try uninstalling the current version first:
|
|
48
|
+
|
|
49
|
+
```
|
|
50
|
+
npm uninstall -g @openfn/cli
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
And then re-installing.
|
|
54
|
+
|
|
39
55
|
## Migrating from devtools
|
|
40
56
|
|
|
41
57
|
If you're coming to the CLI from the old openfn devtools, here are a couple of key points to be aware of:
|
|
42
58
|
|
|
43
59
|
* The CLI has a shorter, sleeker syntax, so your command should be much shorter
|
|
44
60
|
* The CLI will automatically install adaptors for you (with full version control)
|
|
45
|
-
* By default, the CLI will only write state.data to output. This is to encourage better state management. Pass `--no-strict-output` to save the entire state object.
|
|
46
61
|
|
|
47
62
|
## Basic Usage
|
|
48
63
|
|
|
49
|
-
You're probably here to run jobs (expressions), which the CLI makes easy:
|
|
64
|
+
You're probably here to run jobs (expressions) or workflows, which the CLI makes easy:
|
|
65
|
+
|
|
50
66
|
|
|
51
67
|
```
|
|
68
|
+
openfn path/to/workflow.json
|
|
52
69
|
openfn path/to/job.js -ia adaptor-name
|
|
53
70
|
```
|
|
54
71
|
|
|
55
|
-
|
|
72
|
+
If running a single job, you MUST specify which adaptor to use.
|
|
73
|
+
|
|
74
|
+
Pass the `-i` flag to auto-install any required adaptors (it's safe to do this redundantly, although the run will be a little slower).
|
|
56
75
|
|
|
57
|
-
When the
|
|
76
|
+
When the finished, the CLI will write the resulting state to disk. By default the CLI will create an `output.json` next to the job file. You can pass a path to output by passing `-o path/to/output.json` and state by adding `-s path/to/state.json`. You can use `-S` and `-O` to pass state through stdin and return the output through stdout.
|
|
58
77
|
|
|
59
|
-
The CLI
|
|
78
|
+
The CLI maintains a repo for auto-installed adaptors. Run `openfn repo list` to see where the repo is, and what's in it. Set the `OPENFN_REPO_DIR` env var to specify the repo folder. When autoinstalling, the CLI will check to see if a matching version is found in the repo. `openfn repo clean` will remove all adaptors from the repo. The repo also includes any documentation and metadata built with the CLI.
|
|
60
79
|
|
|
61
80
|
You can specify adaptors with a shorthand (`http`) or use the full package name (`@openfn/language-http`). You can add a specific version like `http@2.0.0`. You can pass a path to a locally installed adaptor like `http=/repo/openfn/adaptors/my-http-build`.
|
|
62
81
|
|
|
63
|
-
If you have the adaptors monorepo set up on your machine, you can also run adaptors straight from
|
|
82
|
+
If you have the adaptors monorepo set up on your machine, you can also run adaptors straight from the local build. Pass the `-m <path>` flag to load from the monorepo. You can also set the monorepo location by setting the `OPENFN_ADAPTORS_REPO` env var to a valid path. After that just include `-m` to load from the monorepo. Remember that adaptors will be loaded from the BUILT package in `dist`, so remember to build an adaptor before running!
|
|
64
83
|
|
|
65
84
|
You can pass `--log info` to get more feedback about what's happening, or `--log debug` for more details than you could ever use.
|
|
66
85
|
|
|
67
86
|
## Advanced Usage
|
|
68
87
|
|
|
69
|
-
The CLI has
|
|
88
|
+
The CLI has a number of commands (the first argument after openfn)
|
|
70
89
|
|
|
71
90
|
* execute - run a job
|
|
72
91
|
* compile - compile a job to a .js file
|
|
73
|
-
*
|
|
92
|
+
* docs - show documentation for an adaptor function
|
|
74
93
|
* repo - manage the repo of installed modules
|
|
75
94
|
* docgen - generate JSON documentation for an adaptor based on its typescript
|
|
76
95
|
|
|
@@ -109,9 +128,42 @@ For a more structured output, you can emit logs as JSON objects with `level`, `n
|
|
|
109
128
|
```
|
|
110
129
|
{ level: 'info', name: 'CLI', message: ['Loaded adaptor'] }
|
|
111
130
|
```
|
|
112
|
-
|
|
113
131
|
Pass `--log-json` to the CLI to do this. You can also set the OPENFN_LOG_JSON env var (and use `--no-log-json` to disable).
|
|
114
132
|
|
|
133
|
+
## Workflows
|
|
134
|
+
|
|
135
|
+
As of v0.0.35 the CLI supports running workflows as well as jobs.
|
|
136
|
+
|
|
137
|
+
A workflow is in execution plan for running several jobs in a sequence. It is defined as a JSON structure.
|
|
138
|
+
|
|
139
|
+
To see an example workflow, run the test command with `openfn test`.
|
|
140
|
+
|
|
141
|
+
A workflow has a structure like this (better documentation is coming soon):
|
|
142
|
+
|
|
143
|
+
```
|
|
144
|
+
{
|
|
145
|
+
"start": "a", // optionally specify the start node (defaults to jobs[0])
|
|
146
|
+
"jobs": [
|
|
147
|
+
{
|
|
148
|
+
"id": "a",
|
|
149
|
+
"expression": "fn((state) => state)", // code or a path
|
|
150
|
+
"adaptor": "@openfn/language-common@1.75", // specifiy the adaptor to use (version optional)
|
|
151
|
+
"data": {}, // optionally pre-populate the data object (this will be overriden by keys in in previous state)
|
|
152
|
+
"configuration": {}, // Use this to pass credentials
|
|
153
|
+
"next": {
|
|
154
|
+
// This object defines which jobs to call next
|
|
155
|
+
// All edges returning true will run
|
|
156
|
+
// If there are no next edges, the workflow will end
|
|
157
|
+
"b": true,
|
|
158
|
+
"c": {
|
|
159
|
+
"condition": "!state.error" // Not that this is an expression, not a function
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
},
|
|
163
|
+
]
|
|
164
|
+
}
|
|
165
|
+
```
|
|
166
|
+
|
|
115
167
|
## Compilation
|
|
116
168
|
|
|
117
169
|
The CLI will attempt to compile your job code into normalized Javascript. It will do a number of things to make your code robust and portable:
|
|
@@ -129,12 +181,6 @@ All jobs which work against `@openfn/core` will work in the new CLI and runtime
|
|
|
129
181
|
|
|
130
182
|
If you want to see how the compiler is changing your job, run `openfn compile path/to/job -a <adaptor>` to return the compiled code to stdout. Add `-o path/to/output.js` to save the result to disk.
|
|
131
183
|
|
|
132
|
-
## New Runtime notes
|
|
133
|
-
|
|
134
|
-
The new OpenFn runtime will create a secure sandboxed environemtn which loads a Javascript Module, finds the default export, and execute the functions held within it.
|
|
135
|
-
|
|
136
|
-
So long as your job has an array of functions as its default export, it will run in the new runtime.
|
|
137
|
-
|
|
138
184
|
# Contributing
|
|
139
185
|
|
|
140
186
|
First of all, thanks for helping! You're contributing to a digital public good that will always be free and open source and aimed at serving innovative NGOs, governments, and social impact organizations the world over! You rock. heart
|
|
@@ -170,8 +216,8 @@ The CLI will save and load adaptors from an arbitrary folder on your system.
|
|
|
170
216
|
|
|
171
217
|
You should set the OPENFN_REPO_DIR env var to something sensible.
|
|
172
218
|
|
|
219
|
+
In `~/.bashrc` (or whatever you use), add:
|
|
173
220
|
```
|
|
174
|
-
# In ~/.bashc or whatever
|
|
175
221
|
export OPENFN_REPO_DIR=~/repo/openfn/cli-repo
|
|
176
222
|
```
|
|
177
223
|
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
// src/util/expand-adaptors.ts
|
|
2
|
-
var
|
|
2
|
+
var expand = (name) => {
|
|
3
3
|
if (typeof name === "string") {
|
|
4
4
|
const [left] = name.split("=");
|
|
5
5
|
if (left.match("/") || left.endsWith(".js")) {
|
|
@@ -8,7 +8,21 @@ var expand_adaptors_default = (names) => names?.map((name) => {
|
|
|
8
8
|
return `@openfn/language-${name}`;
|
|
9
9
|
}
|
|
10
10
|
return name;
|
|
11
|
-
}
|
|
11
|
+
};
|
|
12
|
+
var expand_adaptors_default = (opts) => {
|
|
13
|
+
const { adaptors, workflow } = opts;
|
|
14
|
+
if (adaptors) {
|
|
15
|
+
opts.adaptors = adaptors?.map(expand);
|
|
16
|
+
}
|
|
17
|
+
if (workflow) {
|
|
18
|
+
Object.values(workflow.jobs).forEach((job) => {
|
|
19
|
+
if (job.adaptor) {
|
|
20
|
+
job.adaptor = expand(job.adaptor);
|
|
21
|
+
}
|
|
22
|
+
});
|
|
23
|
+
}
|
|
24
|
+
return opts;
|
|
25
|
+
};
|
|
12
26
|
|
|
13
27
|
// src/util/logger.ts
|
|
14
28
|
import actualCreateLogger, { printDuration } from "@openfn/logger";
|
|
@@ -24,13 +38,15 @@ var namespaces = {
|
|
|
24
38
|
[JOB]: "JOB"
|
|
25
39
|
};
|
|
26
40
|
var createLogger = (name = "", options) => {
|
|
27
|
-
const logOptions = options.log;
|
|
41
|
+
const logOptions = options.log || {};
|
|
42
|
+
let json = false;
|
|
28
43
|
let level = logOptions[name] || logOptions.default || "default";
|
|
29
44
|
if (options.logJson) {
|
|
30
|
-
|
|
45
|
+
json = true;
|
|
31
46
|
}
|
|
32
47
|
return actualCreateLogger(namespaces[name] || name, {
|
|
33
48
|
level,
|
|
49
|
+
json,
|
|
34
50
|
...logOptions
|
|
35
51
|
});
|
|
36
52
|
};
|
|
@@ -107,7 +123,6 @@ function ensureOpts(basePath = ".", opts) {
|
|
|
107
123
|
skipAdaptorValidation: opts.skipAdaptorValidation ?? false,
|
|
108
124
|
specifier: opts.specifier,
|
|
109
125
|
stateStdin: opts.stateStdin,
|
|
110
|
-
strictOutput: opts.strictOutput ?? true,
|
|
111
126
|
timeout: opts.timeout
|
|
112
127
|
};
|
|
113
128
|
const set = (key, value) => {
|
package/dist/index.js
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
import {
|
|
3
3
|
DEFAULT_REPO_DIR,
|
|
4
4
|
expand_adaptors_default
|
|
5
|
-
} from "./chunk-
|
|
5
|
+
} from "./chunk-LV5XDERP.js";
|
|
6
6
|
|
|
7
7
|
// src/process/spawn.ts
|
|
8
8
|
import path from "node:path";
|
|
@@ -128,7 +128,7 @@ var adaptors = {
|
|
|
128
128
|
opts2.adaptors = [];
|
|
129
129
|
}
|
|
130
130
|
if (opts2.expandAdaptors) {
|
|
131
|
-
|
|
131
|
+
expand_adaptors_default(opts2);
|
|
132
132
|
}
|
|
133
133
|
delete opts2.adaptor;
|
|
134
134
|
delete opts2.a;
|
|
@@ -193,19 +193,21 @@ var ignoreImports = {
|
|
|
193
193
|
};
|
|
194
194
|
var getBaseDir = (opts2) => {
|
|
195
195
|
const basePath = opts2.path ?? ".";
|
|
196
|
-
if (
|
|
196
|
+
if (/\.(jso?n?)$/.test(basePath)) {
|
|
197
197
|
return path2.dirname(basePath);
|
|
198
198
|
}
|
|
199
199
|
return basePath;
|
|
200
200
|
};
|
|
201
|
-
var
|
|
202
|
-
name: "
|
|
201
|
+
var inputPath = {
|
|
202
|
+
name: "input-path",
|
|
203
203
|
yargs: {
|
|
204
204
|
hidden: true
|
|
205
205
|
},
|
|
206
206
|
ensure: (opts2) => {
|
|
207
207
|
const { path: basePath } = opts2;
|
|
208
|
-
if (basePath?.endsWith(".
|
|
208
|
+
if (basePath?.endsWith(".json")) {
|
|
209
|
+
opts2.workflowPath = basePath;
|
|
210
|
+
} else if (basePath?.endsWith(".js")) {
|
|
209
211
|
opts2.jobPath = basePath;
|
|
210
212
|
} else {
|
|
211
213
|
const base = getBaseDir(opts2);
|
|
@@ -262,14 +264,38 @@ var repoDir = {
|
|
|
262
264
|
default: process.env.OPENFN_REPO_DIR || DEFAULT_REPO_DIR
|
|
263
265
|
}
|
|
264
266
|
};
|
|
267
|
+
var start = {
|
|
268
|
+
name: "start",
|
|
269
|
+
yargs: {
|
|
270
|
+
string: true,
|
|
271
|
+
description: "Specifiy the start node in a workflow"
|
|
272
|
+
}
|
|
273
|
+
};
|
|
265
274
|
var strictOutput = {
|
|
266
275
|
name: "no-strict-output",
|
|
267
276
|
yargs: {
|
|
277
|
+
deprecated: true,
|
|
278
|
+
hidden: true,
|
|
279
|
+
boolean: true
|
|
280
|
+
},
|
|
281
|
+
ensure: (opts2) => {
|
|
282
|
+
if (!opts2.hasOwnProperty("strict")) {
|
|
283
|
+
opts2.strict = opts2.strictOutput;
|
|
284
|
+
}
|
|
285
|
+
delete opts2.strictOutput;
|
|
286
|
+
}
|
|
287
|
+
};
|
|
288
|
+
var strict = {
|
|
289
|
+
name: "no-strict",
|
|
290
|
+
yargs: {
|
|
291
|
+
default: false,
|
|
268
292
|
boolean: true,
|
|
269
|
-
description: "
|
|
293
|
+
description: "Strict state handling, meaning only state.data is returned from a job."
|
|
270
294
|
},
|
|
271
295
|
ensure: (opts2) => {
|
|
272
|
-
|
|
296
|
+
if (!opts2.hasOwnProperty("strictOutput")) {
|
|
297
|
+
setDefaultValue(opts2, "strict", false);
|
|
298
|
+
}
|
|
273
299
|
}
|
|
274
300
|
};
|
|
275
301
|
var skipAdaptorValidation = {
|
|
@@ -325,38 +351,40 @@ var options = [
|
|
|
325
351
|
compile,
|
|
326
352
|
immutable,
|
|
327
353
|
ignoreImports,
|
|
328
|
-
|
|
354
|
+
inputPath,
|
|
329
355
|
logJson,
|
|
330
356
|
outputPath,
|
|
331
357
|
outputStdout,
|
|
332
358
|
repoDir,
|
|
333
359
|
skipAdaptorValidation,
|
|
360
|
+
start,
|
|
334
361
|
statePath,
|
|
335
362
|
stateStdin,
|
|
363
|
+
strict,
|
|
336
364
|
strictOutput,
|
|
337
365
|
timeout,
|
|
338
366
|
useAdaptorsMonorepo
|
|
339
367
|
];
|
|
340
368
|
var executeCommand = {
|
|
341
369
|
command: "execute [path]",
|
|
342
|
-
desc: `Run an openfn job. Get more help by running openfn <command> help.
|
|
370
|
+
desc: `Run an openfn job or workflow. Get more help by running openfn <command> help.
|
|
343
371
|
|
|
344
|
-
Execute will run a job/
|
|
372
|
+
Execute will run a job/workflow at the path and write the output state to disk (to ./state.json unless otherwise specified)
|
|
345
373
|
|
|
346
|
-
By default only state.data will be
|
|
374
|
+
By default only state.data will be returned fron a job. Include --no-strict to write the entire state object.
|
|
347
375
|
|
|
348
376
|
Remember to include the adaptor name with -a. Auto install adaptors with the -i flag.`,
|
|
349
377
|
aliases: ["$0"],
|
|
350
378
|
handler: ensure("execute", options),
|
|
351
379
|
builder: (yargs2) => build(options, yargs2).positional("path", {
|
|
352
|
-
describe: "The path to load the job from (a .js file or a dir containing a job.js file)",
|
|
380
|
+
describe: "The path to load the job or workflow from (a .js or .json file or a dir containing a job.js file)",
|
|
353
381
|
demandOption: true
|
|
354
382
|
}).example(
|
|
355
383
|
"openfn foo/job.js",
|
|
356
384
|
"Execute foo/job.js with no adaptor and write the final state to foo/job.json"
|
|
357
385
|
).example(
|
|
358
|
-
"openfn
|
|
359
|
-
"Execute
|
|
386
|
+
"openfn workflow.json -ia common",
|
|
387
|
+
"Execute workflow.json using @openfn/language-commom (with autoinstall enabled)"
|
|
360
388
|
).example(
|
|
361
389
|
"openfn job.js -a common --log info",
|
|
362
390
|
"Execute job.js with common adaptor and info-level logging"
|
|
@@ -372,7 +400,7 @@ var options2 = [
|
|
|
372
400
|
expandAdaptors,
|
|
373
401
|
adaptors,
|
|
374
402
|
ignoreImports,
|
|
375
|
-
|
|
403
|
+
inputPath,
|
|
376
404
|
logJson,
|
|
377
405
|
override(outputStdout, {
|
|
378
406
|
default: true
|
|
@@ -383,32 +411,28 @@ var options2 = [
|
|
|
383
411
|
];
|
|
384
412
|
var compileCommand = {
|
|
385
413
|
command: "compile [path]",
|
|
386
|
-
desc: "Compile an openfn job and print or save the resulting JavaScript.",
|
|
414
|
+
desc: "Compile an openfn job or workflow and print or save the resulting JavaScript.",
|
|
387
415
|
handler: ensure("compile", options2),
|
|
388
416
|
builder: (yargs2) => build(options2, yargs2).positional("path", {
|
|
389
|
-
describe: "The path to load the job from (a .js file or a dir containing a job.js file)",
|
|
417
|
+
describe: "The path to load the job or workflow from (a .js or .json file or a dir containing a job.js file)",
|
|
390
418
|
demandOption: true
|
|
391
419
|
}).example(
|
|
392
|
-
"compile foo/job.js
|
|
393
|
-
"Compiles foo/job.js and prints the result to stdout"
|
|
420
|
+
"compile foo/job.js",
|
|
421
|
+
"Compiles the job at foo/job.js and prints the result to stdout"
|
|
394
422
|
).example(
|
|
395
|
-
"compile foo/
|
|
396
|
-
"Compiles foo/
|
|
423
|
+
"compile foo/workflow.json -o foo/workflow-compiled.json",
|
|
424
|
+
"Compiles the workflow at foo/work.json and prints the result to -o foo/workflow-compiled.json"
|
|
397
425
|
)
|
|
398
426
|
};
|
|
399
427
|
var command_default2 = compileCommand;
|
|
400
428
|
|
|
401
429
|
// src/test/command.ts
|
|
430
|
+
var options3 = [stateStdin];
|
|
402
431
|
var command_default3 = {
|
|
403
432
|
command: "test",
|
|
404
433
|
desc: "Compiles and runs a test job, printing the result to stdout",
|
|
405
|
-
handler: (
|
|
406
|
-
|
|
407
|
-
},
|
|
408
|
-
builder: (yargs2) => yargs2.option("state-stdin", {
|
|
409
|
-
alias: "S",
|
|
410
|
-
description: "Read state from stdin (instead of a file)"
|
|
411
|
-
}).example("test", "run the test script").example("test -S 42", "run the test script with state 42")
|
|
434
|
+
handler: ensure("test", options3),
|
|
435
|
+
builder: (yargs2) => build(options3, yargs2).example("test", "Run the test script")
|
|
412
436
|
};
|
|
413
437
|
|
|
414
438
|
// src/docgen/command.ts
|
|
@@ -435,7 +459,7 @@ var command_default5 = {
|
|
|
435
459
|
};
|
|
436
460
|
|
|
437
461
|
// src/metadata/command.ts
|
|
438
|
-
var
|
|
462
|
+
var options4 = [
|
|
439
463
|
expandAdaptors,
|
|
440
464
|
adaptors,
|
|
441
465
|
force,
|
|
@@ -448,8 +472,8 @@ var options3 = [
|
|
|
448
472
|
var command_default6 = {
|
|
449
473
|
command: "metadata",
|
|
450
474
|
desc: "Generate metadata for an adaptor config",
|
|
451
|
-
handler: ensure("metadata",
|
|
452
|
-
builder: (yargs2) => build(
|
|
475
|
+
handler: ensure("metadata", options4),
|
|
476
|
+
builder: (yargs2) => build(options4, yargs2).example(
|
|
453
477
|
"metadata -a salesforce -s tmp/state.json",
|
|
454
478
|
"Generate salesforce metadata from config in state.json"
|
|
455
479
|
)
|