@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.
Files changed (3) hide show
  1. package/README.md +20 -27
  2. package/dist/index.js +86 -25
  3. 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
- ## Contributing
11
+ Read the [full documentation here](https://github.com/pipelabs/pd4castr-model-examples/)
12
12
 
13
- ### Quick Start
13
+ ## Quick Usage
14
14
 
15
- ```bash
16
- # set this repository up for linking during develpment
17
- yarn link
15
+ Authenticate with the pd4castr API
18
16
 
19
- # run the project in watch mode
20
- yarn dev
17
+ ```sh
18
+ pd4castr login
19
+ ```
21
20
 
22
- # from a model project, link the module
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
- # from that project, execute a command
27
- yarn pd4castr <command>
23
+ ```sh
24
+ pd4castr fetch
28
25
  ```
29
26
 
30
- ### Scripts
27
+ Run your model locally and verify it reads inputs & uploads output as expected
31
28
 
32
- - `yarn build` - Build the project
33
- - `yarn dev` - Watch mode for development
34
- - `yarn cli <command>` - Run CLI commands against local build
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
- ### Testing
33
+ Publish your model to the pd4castr platform
42
34
 
43
- As this project requires a lot of disk I/O and network reqeuests, we opt for 2 mocking solutions that keep us as close to the metal as possible:
35
+ ```sh
36
+ pd4castr publish
37
+ ```
44
38
 
45
- - network requests are mocked uses [msw](https://mswjs.io/) - request handlers live in the [mocks/handlers](./src/mocks/handlers/) folder
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
- Both of these mocks are initialised globally via our [setup script](./vitest.setup.ts).
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 chalk2 from "chalk";
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("docker", ["build", "-t", dockerImage, "."], {
664
- cwd: ctx.projectRoot,
665
- stdio: "pipe"
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/utils/get-model-summary-lines.ts
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
- ` ${chalk.bold("Model name:")} ${ctx.config.name}`,
772
- ` ${chalk.bold("Revision:")} ${ctx.config.$$revision}`,
773
- ` ${chalk.bold("Forecast variable:")} ${ctx.config.forecastVariable}`,
774
- ` ${chalk.bold("Time horizon:")} ${ctx.config.timeHorizon}`,
775
- ` ${chalk.bold("Inputs:")}`,
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
- ` ${chalk.bold("Outputs:")}`,
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 ${chalk2.bold("new")} model:
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 registry successfully");
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: chalk2.bold(`${model.name} r${model.revision} published successfully`)
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 chalk5 from "chalk";
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 chalk3 from "chalk";
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 = chalk3.yellowBright.bold("WARNING!");
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: chalk3.bold(
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 chalk4 from "chalk";
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 = chalk4.yellowBright.bold("WARNING!");
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: chalk4.bold(`${model.name} (r${model.revision}) updated successfully`)
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 ${chalk5.bold("existing")} model:
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("-s, --skip-checks", "Skip the model I/O checks", false).action(handleAction5);
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.10",
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",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pd4castr/cli",
3
- "version": "0.0.10",
3
+ "version": "0.0.12",
4
4
  "description": "CLI tool for creating, testing, and publishing pd4castr models",
5
5
  "main": "dist/index.js",
6
6
  "type": "module",