@pd4castr/cli 0.0.10 → 0.0.12
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 +86 -25
- 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
|
@@ -626,7 +626,7 @@ async function startWebServer(app, port) {
|
|
|
626
626
|
|
|
627
627
|
// src/commands/publish/handle-create-model-flow.ts
|
|
628
628
|
import * as inquirer2 from "@inquirer/prompts";
|
|
629
|
-
import
|
|
629
|
+
import chalk3 from "chalk";
|
|
630
630
|
|
|
631
631
|
// src/api/create-model.ts
|
|
632
632
|
async function createModel(config, authCtx) {
|
|
@@ -643,6 +643,13 @@ async function getRegistryPushCredentials(modelID, authCtx) {
|
|
|
643
643
|
return result;
|
|
644
644
|
}
|
|
645
645
|
|
|
646
|
+
// src/api/trigger-model-run.ts
|
|
647
|
+
async function triggerModelRun(modelId, authCtx) {
|
|
648
|
+
const headers = { Authorization: `Bearer ${authCtx.accessToken}` };
|
|
649
|
+
const result = await api.post(`model/${modelId}/trigger`, { headers }).json();
|
|
650
|
+
return result;
|
|
651
|
+
}
|
|
652
|
+
|
|
646
653
|
// src/config/update-project-config.ts
|
|
647
654
|
import fs7 from "fs/promises";
|
|
648
655
|
import path8 from "path";
|
|
@@ -660,10 +667,14 @@ async function updateProjectConfig(updateFn) {
|
|
|
660
667
|
import { execa } from "execa";
|
|
661
668
|
async function buildDockerImage(dockerImage, ctx) {
|
|
662
669
|
try {
|
|
663
|
-
await execa(
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
670
|
+
await execa(
|
|
671
|
+
"docker",
|
|
672
|
+
["build", "--platform=linux/amd64", "-t", dockerImage, "."],
|
|
673
|
+
{
|
|
674
|
+
cwd: ctx.projectRoot,
|
|
675
|
+
stdio: "pipe"
|
|
676
|
+
}
|
|
677
|
+
);
|
|
667
678
|
} catch (error) {
|
|
668
679
|
throw new Error("Failed to build docker image", { cause: error });
|
|
669
680
|
}
|
|
@@ -764,19 +775,27 @@ function logEmptyLine() {
|
|
|
764
775
|
console.log("");
|
|
765
776
|
}
|
|
766
777
|
|
|
767
|
-
// src/commands/publish/
|
|
778
|
+
// src/commands/publish/constants.ts
|
|
768
779
|
import chalk from "chalk";
|
|
780
|
+
var MODEL_RUN_TRIGGER_MESSAGE = `${chalk.whiteBright.bold("NOTE!")} If you do not see your model output in the pd4castr UI:
|
|
781
|
+
|
|
782
|
+
\u2022 If you are using static inputs - check you have uploaded your input data to your input bucket
|
|
783
|
+
\u2022 If you are using inputs with data fetchers - wait a few minutes and check again
|
|
784
|
+
`;
|
|
785
|
+
|
|
786
|
+
// src/commands/publish/utils/get-model-summary-lines.ts
|
|
787
|
+
import chalk2 from "chalk";
|
|
769
788
|
function getModelSummaryLines(ctx) {
|
|
770
789
|
return [
|
|
771
|
-
` ${
|
|
772
|
-
` ${
|
|
773
|
-
` ${
|
|
774
|
-
` ${
|
|
775
|
-
` ${
|
|
790
|
+
` ${chalk2.bold("Model name:")} ${ctx.config.name}`,
|
|
791
|
+
` ${chalk2.bold("Revision:")} ${ctx.config.$$revision}`,
|
|
792
|
+
` ${chalk2.bold("Forecast variable:")} ${ctx.config.forecastVariable}`,
|
|
793
|
+
` ${chalk2.bold("Time horizon:")} ${ctx.config.timeHorizon}`,
|
|
794
|
+
` ${chalk2.bold("Inputs:")}`,
|
|
776
795
|
...ctx.config.inputs.map(
|
|
777
796
|
(input2) => ` \u2022 ${input2.key} - ${getInputType(input2)}`
|
|
778
797
|
),
|
|
779
|
-
` ${
|
|
798
|
+
` ${chalk2.bold("Outputs:")}`,
|
|
780
799
|
...ctx.config.outputs.map((output) => ` \u2022 ${output.name} - ${output.type}`),
|
|
781
800
|
""
|
|
782
801
|
];
|
|
@@ -966,7 +985,7 @@ async function runModelIOTests(dockerImage, options, app, ctx) {
|
|
|
966
985
|
|
|
967
986
|
// src/commands/publish/handle-create-model-flow.ts
|
|
968
987
|
async function handleCreateModelFlow(options, app, spinner, ctx, authCtx) {
|
|
969
|
-
spinner.info(`You are publishing a ${
|
|
988
|
+
spinner.info(`You are publishing a ${chalk3.bold("new")} model:
|
|
970
989
|
`);
|
|
971
990
|
getModelSummaryLines(ctx).map((line) => console.log(line));
|
|
972
991
|
const confirm4 = await inquirer2.confirm({
|
|
@@ -1000,22 +1019,36 @@ async function handleCreateModelFlow(options, app, spinner, ctx, authCtx) {
|
|
|
1000
1019
|
await loginToDockerRegistry(pushCredentials);
|
|
1001
1020
|
await buildDockerImage(dockerImage, ctx);
|
|
1002
1021
|
await pushDockerImage(dockerImage, model.dockerImage);
|
|
1003
|
-
spinner.succeed("Model image pushed to
|
|
1022
|
+
spinner.succeed("Model image pushed to reogistry successfully");
|
|
1023
|
+
spinner.start("Triggering model run...");
|
|
1024
|
+
let modelRunTriggered = false;
|
|
1025
|
+
if (!options.skipTrigger) {
|
|
1026
|
+
try {
|
|
1027
|
+
await triggerModelRun(model.id, authCtx);
|
|
1028
|
+
spinner.succeed("Model run triggered successfully");
|
|
1029
|
+
modelRunTriggered = true;
|
|
1030
|
+
} catch {
|
|
1031
|
+
spinner.info("Model run did not trigger");
|
|
1032
|
+
}
|
|
1033
|
+
}
|
|
1004
1034
|
spinner.stopAndPersist({
|
|
1005
1035
|
symbol: "\u{1F680} ",
|
|
1006
1036
|
prefixText: "\n",
|
|
1007
1037
|
suffixText: "\n",
|
|
1008
|
-
text:
|
|
1038
|
+
text: chalk3.bold(`${model.name} r${model.revision} published successfully`)
|
|
1009
1039
|
});
|
|
1040
|
+
if (!modelRunTriggered && !options.skipTrigger) {
|
|
1041
|
+
console.log(MODEL_RUN_TRIGGER_MESSAGE);
|
|
1042
|
+
}
|
|
1010
1043
|
}
|
|
1011
1044
|
|
|
1012
1045
|
// src/commands/publish/handle-update-existing-model-flow.ts
|
|
1013
1046
|
import * as inquirer5 from "@inquirer/prompts";
|
|
1014
|
-
import
|
|
1047
|
+
import chalk6 from "chalk";
|
|
1015
1048
|
|
|
1016
1049
|
// src/commands/publish/handle-model-revision-create-flow.ts
|
|
1017
1050
|
import * as inquirer3 from "@inquirer/prompts";
|
|
1018
|
-
import
|
|
1051
|
+
import chalk4 from "chalk";
|
|
1019
1052
|
|
|
1020
1053
|
// src/commands/publish/utils/validate-local-model-state.ts
|
|
1021
1054
|
import invariant2 from "tiny-invariant";
|
|
@@ -1044,7 +1077,7 @@ async function validateLocalModelState(ctx, authCtx) {
|
|
|
1044
1077
|
}
|
|
1045
1078
|
|
|
1046
1079
|
// src/commands/publish/handle-model-revision-create-flow.ts
|
|
1047
|
-
var WARNING_LABEL =
|
|
1080
|
+
var WARNING_LABEL = chalk4.yellowBright.bold("WARNING!");
|
|
1048
1081
|
var CONFIRMATION_MESSAGE = `${WARNING_LABEL} Creating a new revision will preserve existing revisions.
|
|
1049
1082
|
Previous revisions will still be available in the pd4castr UI.
|
|
1050
1083
|
`;
|
|
@@ -1085,19 +1118,33 @@ async function handleModelRevisionCreateFlow(options, app, spinner, ctx, authCtx
|
|
|
1085
1118
|
await buildDockerImage(dockerImage, ctx);
|
|
1086
1119
|
await pushDockerImage(dockerImage, model.dockerImage);
|
|
1087
1120
|
spinner.succeed("New model revision image pushed to registry successfully");
|
|
1121
|
+
spinner.start("Triggering model run...");
|
|
1122
|
+
let modelRunTriggered = false;
|
|
1123
|
+
if (!options.skipTrigger) {
|
|
1124
|
+
try {
|
|
1125
|
+
await triggerModelRun(model.id, authCtx);
|
|
1126
|
+
spinner.succeed("Model run triggered successfully");
|
|
1127
|
+
modelRunTriggered = true;
|
|
1128
|
+
} catch {
|
|
1129
|
+
spinner.info("Model run did not trigger");
|
|
1130
|
+
}
|
|
1131
|
+
}
|
|
1088
1132
|
spinner.stopAndPersist({
|
|
1089
1133
|
symbol: "\u{1F680} ",
|
|
1090
1134
|
prefixText: "\n",
|
|
1091
1135
|
suffixText: "\n",
|
|
1092
|
-
text:
|
|
1136
|
+
text: chalk4.bold(
|
|
1093
1137
|
`New model revision (r${model.revision}) published successfully`
|
|
1094
1138
|
)
|
|
1095
1139
|
});
|
|
1140
|
+
if (!modelRunTriggered && !options.skipTrigger) {
|
|
1141
|
+
console.log(MODEL_RUN_TRIGGER_MESSAGE);
|
|
1142
|
+
}
|
|
1096
1143
|
}
|
|
1097
1144
|
|
|
1098
1145
|
// src/commands/publish/handle-model-revision-update-flow.ts
|
|
1099
1146
|
import * as inquirer4 from "@inquirer/prompts";
|
|
1100
|
-
import
|
|
1147
|
+
import chalk5 from "chalk";
|
|
1101
1148
|
|
|
1102
1149
|
// src/api/update-model.ts
|
|
1103
1150
|
async function updateModel(config, authCtx) {
|
|
@@ -1107,7 +1154,7 @@ async function updateModel(config, authCtx) {
|
|
|
1107
1154
|
}
|
|
1108
1155
|
|
|
1109
1156
|
// src/commands/publish/handle-model-revision-update-flow.ts
|
|
1110
|
-
var WARNING_LABEL2 =
|
|
1157
|
+
var WARNING_LABEL2 = chalk5.yellowBright.bold("WARNING!");
|
|
1111
1158
|
var CONFIRMATION_MESSAGE2 = `${WARNING_LABEL2} Updating a model revision recreates the associated inputs and outputs.
|
|
1112
1159
|
Historical data is preserved, but it will no longer be displayed in the pd4castr UI.
|
|
1113
1160
|
`;
|
|
@@ -1148,17 +1195,31 @@ async function handleModelRevisionUpdateFlow(options, app, spinner, ctx, authCtx
|
|
|
1148
1195
|
await buildDockerImage(dockerImage, ctx);
|
|
1149
1196
|
await pushDockerImage(dockerImage, model.dockerImage);
|
|
1150
1197
|
spinner.succeed("Updated model image pushed to registry successfully");
|
|
1198
|
+
spinner.start("Triggering model run...");
|
|
1199
|
+
let modelRunTriggered = false;
|
|
1200
|
+
if (!options.skipTrigger) {
|
|
1201
|
+
try {
|
|
1202
|
+
await triggerModelRun(model.id, authCtx);
|
|
1203
|
+
spinner.succeed("Model run triggered successfully");
|
|
1204
|
+
modelRunTriggered = true;
|
|
1205
|
+
} catch {
|
|
1206
|
+
spinner.info("Model run did not trigger");
|
|
1207
|
+
}
|
|
1208
|
+
}
|
|
1151
1209
|
spinner.stopAndPersist({
|
|
1152
1210
|
symbol: "\u{1F680} ",
|
|
1153
1211
|
prefixText: "\n",
|
|
1154
1212
|
suffixText: "\n",
|
|
1155
|
-
text:
|
|
1213
|
+
text: chalk5.bold(`${model.name} (r${model.revision}) updated successfully`)
|
|
1156
1214
|
});
|
|
1215
|
+
if (!modelRunTriggered && !options.skipTrigger) {
|
|
1216
|
+
console.log(MODEL_RUN_TRIGGER_MESSAGE);
|
|
1217
|
+
}
|
|
1157
1218
|
}
|
|
1158
1219
|
|
|
1159
1220
|
// src/commands/publish/handle-update-existing-model-flow.ts
|
|
1160
1221
|
async function handleUpdateExistingModelFlow(options, app, spinner, ctx, authCtx) {
|
|
1161
|
-
spinner.info(`You are publishing an ${
|
|
1222
|
+
spinner.info(`You are publishing an ${chalk6.bold("existing")} model:
|
|
1162
1223
|
`);
|
|
1163
1224
|
getModelSummaryLines(ctx).map((line) => console.log(line));
|
|
1164
1225
|
const revision = ctx.config.$$revision ?? 0;
|
|
@@ -1232,7 +1293,7 @@ function registerPublishCommand(program2) {
|
|
|
1232
1293
|
"-p, --port <port>",
|
|
1233
1294
|
"The port to run the IO testing webserver on",
|
|
1234
1295
|
TEST_WEBSERVER_PORT.toString()
|
|
1235
|
-
).option("
|
|
1296
|
+
).option("--sc, --skip-checks", "Skip the model I/O checks", false).option("--st, --skip-trigger", "Skip the model trigger", false).action(handleAction5);
|
|
1236
1297
|
}
|
|
1237
1298
|
|
|
1238
1299
|
// src/commands/test/handle-action.ts
|
|
@@ -1333,7 +1394,7 @@ import { Command } from "commander";
|
|
|
1333
1394
|
// package.json
|
|
1334
1395
|
var package_default = {
|
|
1335
1396
|
name: "@pd4castr/cli",
|
|
1336
|
-
version: "0.0.
|
|
1397
|
+
version: "0.0.12",
|
|
1337
1398
|
description: "CLI tool for creating, testing, and publishing pd4castr models",
|
|
1338
1399
|
main: "dist/index.js",
|
|
1339
1400
|
type: "module",
|