@pd4castr/cli 0.0.11 → 0.0.13
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 +20 -27
- package/dist/index.js +78 -46
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# pd4castr CLI
|
|
2
2
|
|
|
3
|
-
CLI tool for creating, testing, and publishing pd4castr models.
|
|
3
|
+
CLI tool for creating, testing, and publishing [pd4castr](https://pdview.com.au/services/pd4castr/) models.
|
|
4
4
|
|
|
5
5
|
Install via:
|
|
6
6
|
|
|
@@ -8,41 +8,34 @@ Install via:
|
|
|
8
8
|
npm install -g @pd4castr/cli
|
|
9
9
|
```
|
|
10
10
|
|
|
11
|
-
|
|
11
|
+
Read the [full documentation here](https://github.com/pipelabs/pd4castr-model-examples/)
|
|
12
12
|
|
|
13
|
-
|
|
13
|
+
## Quick Usage
|
|
14
14
|
|
|
15
|
-
|
|
16
|
-
# set this repository up for linking during develpment
|
|
17
|
-
yarn link
|
|
15
|
+
Authenticate with the pd4castr API
|
|
18
16
|
|
|
19
|
-
|
|
20
|
-
|
|
17
|
+
```sh
|
|
18
|
+
pd4castr login
|
|
19
|
+
```
|
|
21
20
|
|
|
22
|
-
|
|
23
|
-
# ex. https://github.com/pipelabs/pd4castr-model-examples/tree/main/examples/python-demo
|
|
24
|
-
yarn link @pd4castr/cli
|
|
21
|
+
Run model input data fetchers and generate test input data
|
|
25
22
|
|
|
26
|
-
|
|
27
|
-
|
|
23
|
+
```sh
|
|
24
|
+
pd4castr fetch
|
|
28
25
|
```
|
|
29
26
|
|
|
30
|
-
|
|
27
|
+
Run your model locally and verify it reads inputs & uploads output as expected
|
|
31
28
|
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
- `yarn test` - Run tests once
|
|
36
|
-
- `yarn test:watch` - Run tests in watch mode
|
|
37
|
-
- `yarn lint` - Check for linting issues
|
|
38
|
-
- `yarn format` - Format code with Prettier
|
|
39
|
-
- `yarn type-check` - TypeScript type checking
|
|
29
|
+
```sh
|
|
30
|
+
pd4castr test
|
|
31
|
+
```
|
|
40
32
|
|
|
41
|
-
|
|
33
|
+
Publish your model to the pd4castr platform
|
|
42
34
|
|
|
43
|
-
|
|
35
|
+
```sh
|
|
36
|
+
pd4castr publish
|
|
37
|
+
```
|
|
44
38
|
|
|
45
|
-
|
|
46
|
-
- disk I/O (`fs`) is mocked using [`memfs`](https://github.com/streamich/memfs) which is handled by Vitest in our [`__mocks__/`](./src/__mocks__) folder
|
|
39
|
+
## Contributing
|
|
47
40
|
|
|
48
|
-
|
|
41
|
+
For development docs, check [CONTRIBUTING.MD](./CONTRIBUTING.md)
|
package/dist/index.js
CHANGED
|
@@ -317,9 +317,14 @@ async function handleAction(options) {
|
|
|
317
317
|
try {
|
|
318
318
|
const authCtx = await getAuth();
|
|
319
319
|
const ctx = await loadProjectContext();
|
|
320
|
+
const inputsWithFetchers = ctx.config.inputs.filter((input2) => input2.fetcher);
|
|
321
|
+
if (inputsWithFetchers.length === 0) {
|
|
322
|
+
spinner.info("No inputs with data fetchers found, skipping");
|
|
323
|
+
return;
|
|
324
|
+
}
|
|
320
325
|
for (const input2 of ctx.config.inputs) {
|
|
321
326
|
if (!input2.fetcher) {
|
|
322
|
-
spinner.
|
|
327
|
+
spinner.info(`\`${input2.key}\` - no data fetcher defined, skipping`);
|
|
323
328
|
continue;
|
|
324
329
|
}
|
|
325
330
|
if (!FETCHABLE_DATA_FETCHER_TYPES.has(input2.fetcher.type)) {
|
|
@@ -626,7 +631,7 @@ async function startWebServer(app, port) {
|
|
|
626
631
|
|
|
627
632
|
// src/commands/publish/handle-create-model-flow.ts
|
|
628
633
|
import * as inquirer2 from "@inquirer/prompts";
|
|
629
|
-
import
|
|
634
|
+
import chalk3 from "chalk";
|
|
630
635
|
|
|
631
636
|
// src/api/create-model.ts
|
|
632
637
|
async function createModel(config, authCtx) {
|
|
@@ -667,10 +672,14 @@ async function updateProjectConfig(updateFn) {
|
|
|
667
672
|
import { execa } from "execa";
|
|
668
673
|
async function buildDockerImage(dockerImage, ctx) {
|
|
669
674
|
try {
|
|
670
|
-
await execa(
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
675
|
+
await execa(
|
|
676
|
+
"docker",
|
|
677
|
+
["build", "--platform=linux/amd64", "-t", dockerImage, "."],
|
|
678
|
+
{
|
|
679
|
+
cwd: ctx.projectRoot,
|
|
680
|
+
stdio: "pipe"
|
|
681
|
+
}
|
|
682
|
+
);
|
|
674
683
|
} catch (error) {
|
|
675
684
|
throw new Error("Failed to build docker image", { cause: error });
|
|
676
685
|
}
|
|
@@ -771,19 +780,27 @@ function logEmptyLine() {
|
|
|
771
780
|
console.log("");
|
|
772
781
|
}
|
|
773
782
|
|
|
774
|
-
// src/commands/publish/
|
|
783
|
+
// src/commands/publish/constants.ts
|
|
775
784
|
import chalk from "chalk";
|
|
785
|
+
var MODEL_RUN_TRIGGER_MESSAGE = `${chalk.whiteBright.bold("NOTE!")} If you do not see your model output in the pd4castr UI:
|
|
786
|
+
|
|
787
|
+
\u2022 If you are using static inputs - check you have uploaded your input data to your input bucket
|
|
788
|
+
\u2022 If you are using inputs with data fetchers - wait a few minutes and check again
|
|
789
|
+
`;
|
|
790
|
+
|
|
791
|
+
// src/commands/publish/utils/get-model-summary-lines.ts
|
|
792
|
+
import chalk2 from "chalk";
|
|
776
793
|
function getModelSummaryLines(ctx) {
|
|
777
794
|
return [
|
|
778
|
-
` ${
|
|
779
|
-
` ${
|
|
780
|
-
` ${
|
|
781
|
-
` ${
|
|
782
|
-
` ${
|
|
795
|
+
` ${chalk2.bold("Model name:")} ${ctx.config.name}`,
|
|
796
|
+
` ${chalk2.bold("Revision:")} ${ctx.config.$$revision}`,
|
|
797
|
+
` ${chalk2.bold("Forecast variable:")} ${ctx.config.forecastVariable}`,
|
|
798
|
+
` ${chalk2.bold("Time horizon:")} ${ctx.config.timeHorizon}`,
|
|
799
|
+
` ${chalk2.bold("Inputs:")}`,
|
|
783
800
|
...ctx.config.inputs.map(
|
|
784
801
|
(input2) => ` \u2022 ${input2.key} - ${getInputType(input2)}`
|
|
785
802
|
),
|
|
786
|
-
` ${
|
|
803
|
+
` ${chalk2.bold("Outputs:")}`,
|
|
787
804
|
...ctx.config.outputs.map((output) => ` \u2022 ${output.name} - ${output.type}`),
|
|
788
805
|
""
|
|
789
806
|
];
|
|
@@ -973,7 +990,7 @@ async function runModelIOTests(dockerImage, options, app, ctx) {
|
|
|
973
990
|
|
|
974
991
|
// src/commands/publish/handle-create-model-flow.ts
|
|
975
992
|
async function handleCreateModelFlow(options, app, spinner, ctx, authCtx) {
|
|
976
|
-
spinner.info(`You are publishing a ${
|
|
993
|
+
spinner.info(`You are publishing a ${chalk3.bold("new")} model:
|
|
977
994
|
`);
|
|
978
995
|
getModelSummaryLines(ctx).map((line) => console.log(line));
|
|
979
996
|
const confirm4 = await inquirer2.confirm({
|
|
@@ -1009,29 +1026,34 @@ async function handleCreateModelFlow(options, app, spinner, ctx, authCtx) {
|
|
|
1009
1026
|
await pushDockerImage(dockerImage, model.dockerImage);
|
|
1010
1027
|
spinner.succeed("Model image pushed to reogistry successfully");
|
|
1011
1028
|
spinner.start("Triggering model run...");
|
|
1012
|
-
|
|
1013
|
-
|
|
1014
|
-
|
|
1015
|
-
|
|
1016
|
-
|
|
1017
|
-
|
|
1018
|
-
|
|
1029
|
+
let modelRunTriggered = false;
|
|
1030
|
+
if (!options.skipTrigger) {
|
|
1031
|
+
try {
|
|
1032
|
+
await triggerModelRun(model.id, authCtx);
|
|
1033
|
+
spinner.succeed("Model run triggered successfully");
|
|
1034
|
+
modelRunTriggered = true;
|
|
1035
|
+
} catch {
|
|
1036
|
+
spinner.info("Model run did not trigger");
|
|
1037
|
+
}
|
|
1019
1038
|
}
|
|
1020
1039
|
spinner.stopAndPersist({
|
|
1021
1040
|
symbol: "\u{1F680} ",
|
|
1022
1041
|
prefixText: "\n",
|
|
1023
1042
|
suffixText: "\n",
|
|
1024
|
-
text:
|
|
1043
|
+
text: chalk3.bold(`${model.name} r${model.revision} published successfully`)
|
|
1025
1044
|
});
|
|
1045
|
+
if (!modelRunTriggered && !options.skipTrigger) {
|
|
1046
|
+
console.log(MODEL_RUN_TRIGGER_MESSAGE);
|
|
1047
|
+
}
|
|
1026
1048
|
}
|
|
1027
1049
|
|
|
1028
1050
|
// src/commands/publish/handle-update-existing-model-flow.ts
|
|
1029
1051
|
import * as inquirer5 from "@inquirer/prompts";
|
|
1030
|
-
import
|
|
1052
|
+
import chalk6 from "chalk";
|
|
1031
1053
|
|
|
1032
1054
|
// src/commands/publish/handle-model-revision-create-flow.ts
|
|
1033
1055
|
import * as inquirer3 from "@inquirer/prompts";
|
|
1034
|
-
import
|
|
1056
|
+
import chalk4 from "chalk";
|
|
1035
1057
|
|
|
1036
1058
|
// src/commands/publish/utils/validate-local-model-state.ts
|
|
1037
1059
|
import invariant2 from "tiny-invariant";
|
|
@@ -1060,7 +1082,7 @@ async function validateLocalModelState(ctx, authCtx) {
|
|
|
1060
1082
|
}
|
|
1061
1083
|
|
|
1062
1084
|
// src/commands/publish/handle-model-revision-create-flow.ts
|
|
1063
|
-
var WARNING_LABEL =
|
|
1085
|
+
var WARNING_LABEL = chalk4.yellowBright.bold("WARNING!");
|
|
1064
1086
|
var CONFIRMATION_MESSAGE = `${WARNING_LABEL} Creating a new revision will preserve existing revisions.
|
|
1065
1087
|
Previous revisions will still be available in the pd4castr UI.
|
|
1066
1088
|
`;
|
|
@@ -1102,27 +1124,32 @@ async function handleModelRevisionCreateFlow(options, app, spinner, ctx, authCtx
|
|
|
1102
1124
|
await pushDockerImage(dockerImage, model.dockerImage);
|
|
1103
1125
|
spinner.succeed("New model revision image pushed to registry successfully");
|
|
1104
1126
|
spinner.start("Triggering model run...");
|
|
1105
|
-
|
|
1106
|
-
|
|
1107
|
-
|
|
1108
|
-
|
|
1109
|
-
|
|
1110
|
-
|
|
1111
|
-
|
|
1127
|
+
let modelRunTriggered = false;
|
|
1128
|
+
if (!options.skipTrigger) {
|
|
1129
|
+
try {
|
|
1130
|
+
await triggerModelRun(model.id, authCtx);
|
|
1131
|
+
spinner.succeed("Model run triggered successfully");
|
|
1132
|
+
modelRunTriggered = true;
|
|
1133
|
+
} catch {
|
|
1134
|
+
spinner.info("Model run did not trigger");
|
|
1135
|
+
}
|
|
1112
1136
|
}
|
|
1113
1137
|
spinner.stopAndPersist({
|
|
1114
1138
|
symbol: "\u{1F680} ",
|
|
1115
1139
|
prefixText: "\n",
|
|
1116
1140
|
suffixText: "\n",
|
|
1117
|
-
text:
|
|
1141
|
+
text: chalk4.bold(
|
|
1118
1142
|
`New model revision (r${model.revision}) published successfully`
|
|
1119
1143
|
)
|
|
1120
1144
|
});
|
|
1145
|
+
if (!modelRunTriggered && !options.skipTrigger) {
|
|
1146
|
+
console.log(MODEL_RUN_TRIGGER_MESSAGE);
|
|
1147
|
+
}
|
|
1121
1148
|
}
|
|
1122
1149
|
|
|
1123
1150
|
// src/commands/publish/handle-model-revision-update-flow.ts
|
|
1124
1151
|
import * as inquirer4 from "@inquirer/prompts";
|
|
1125
|
-
import
|
|
1152
|
+
import chalk5 from "chalk";
|
|
1126
1153
|
|
|
1127
1154
|
// src/api/update-model.ts
|
|
1128
1155
|
async function updateModel(config, authCtx) {
|
|
@@ -1132,7 +1159,7 @@ async function updateModel(config, authCtx) {
|
|
|
1132
1159
|
}
|
|
1133
1160
|
|
|
1134
1161
|
// src/commands/publish/handle-model-revision-update-flow.ts
|
|
1135
|
-
var WARNING_LABEL2 =
|
|
1162
|
+
var WARNING_LABEL2 = chalk5.yellowBright.bold("WARNING!");
|
|
1136
1163
|
var CONFIRMATION_MESSAGE2 = `${WARNING_LABEL2} Updating a model revision recreates the associated inputs and outputs.
|
|
1137
1164
|
Historical data is preserved, but it will no longer be displayed in the pd4castr UI.
|
|
1138
1165
|
`;
|
|
@@ -1174,25 +1201,30 @@ async function handleModelRevisionUpdateFlow(options, app, spinner, ctx, authCtx
|
|
|
1174
1201
|
await pushDockerImage(dockerImage, model.dockerImage);
|
|
1175
1202
|
spinner.succeed("Updated model image pushed to registry successfully");
|
|
1176
1203
|
spinner.start("Triggering model run...");
|
|
1177
|
-
|
|
1178
|
-
|
|
1179
|
-
|
|
1180
|
-
|
|
1181
|
-
|
|
1182
|
-
|
|
1183
|
-
|
|
1204
|
+
let modelRunTriggered = false;
|
|
1205
|
+
if (!options.skipTrigger) {
|
|
1206
|
+
try {
|
|
1207
|
+
await triggerModelRun(model.id, authCtx);
|
|
1208
|
+
spinner.succeed("Model run triggered successfully");
|
|
1209
|
+
modelRunTriggered = true;
|
|
1210
|
+
} catch {
|
|
1211
|
+
spinner.info("Model run did not trigger");
|
|
1212
|
+
}
|
|
1184
1213
|
}
|
|
1185
1214
|
spinner.stopAndPersist({
|
|
1186
1215
|
symbol: "\u{1F680} ",
|
|
1187
1216
|
prefixText: "\n",
|
|
1188
1217
|
suffixText: "\n",
|
|
1189
|
-
text:
|
|
1218
|
+
text: chalk5.bold(`${model.name} (r${model.revision}) updated successfully`)
|
|
1190
1219
|
});
|
|
1220
|
+
if (!modelRunTriggered && !options.skipTrigger) {
|
|
1221
|
+
console.log(MODEL_RUN_TRIGGER_MESSAGE);
|
|
1222
|
+
}
|
|
1191
1223
|
}
|
|
1192
1224
|
|
|
1193
1225
|
// src/commands/publish/handle-update-existing-model-flow.ts
|
|
1194
1226
|
async function handleUpdateExistingModelFlow(options, app, spinner, ctx, authCtx) {
|
|
1195
|
-
spinner.info(`You are publishing an ${
|
|
1227
|
+
spinner.info(`You are publishing an ${chalk6.bold("existing")} model:
|
|
1196
1228
|
`);
|
|
1197
1229
|
getModelSummaryLines(ctx).map((line) => console.log(line));
|
|
1198
1230
|
const revision = ctx.config.$$revision ?? 0;
|
|
@@ -1266,7 +1298,7 @@ function registerPublishCommand(program2) {
|
|
|
1266
1298
|
"-p, --port <port>",
|
|
1267
1299
|
"The port to run the IO testing webserver on",
|
|
1268
1300
|
TEST_WEBSERVER_PORT.toString()
|
|
1269
|
-
).option("
|
|
1301
|
+
).option("--sc, --skip-checks", "Skip the model I/O checks", false).option("--st, --skip-trigger", "Skip the model trigger", false).action(handleAction5);
|
|
1270
1302
|
}
|
|
1271
1303
|
|
|
1272
1304
|
// src/commands/test/handle-action.ts
|
|
@@ -1367,7 +1399,7 @@ import { Command } from "commander";
|
|
|
1367
1399
|
// package.json
|
|
1368
1400
|
var package_default = {
|
|
1369
1401
|
name: "@pd4castr/cli",
|
|
1370
|
-
version: "0.0.
|
|
1402
|
+
version: "0.0.13",
|
|
1371
1403
|
description: "CLI tool for creating, testing, and publishing pd4castr models",
|
|
1372
1404
|
main: "dist/index.js",
|
|
1373
1405
|
type: "module",
|