@strapi/cloud-cli 0.0.0-next.7466630056657c483fb286557dda890490597807 → 0.0.0-next.77e66829c8c9c19a6fd9c80944bb2395c7d7f714

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 (35) hide show
  1. package/dist/index.js +300 -91
  2. package/dist/index.js.map +1 -1
  3. package/dist/index.mjs +299 -89
  4. package/dist/index.mjs.map +1 -1
  5. package/dist/src/cloud/command.d.ts +3 -0
  6. package/dist/src/cloud/command.d.ts.map +1 -0
  7. package/dist/src/create-project/action.d.ts.map +1 -1
  8. package/dist/src/create-project/command.d.ts.map +1 -1
  9. package/dist/src/create-project/utils/project-questions.utils.d.ts +20 -0
  10. package/dist/src/create-project/utils/project-questions.utils.d.ts.map +1 -0
  11. package/dist/src/deploy-project/action.d.ts +4 -1
  12. package/dist/src/deploy-project/action.d.ts.map +1 -1
  13. package/dist/src/deploy-project/command.d.ts.map +1 -1
  14. package/dist/src/environment/command.d.ts +3 -0
  15. package/dist/src/environment/command.d.ts.map +1 -0
  16. package/dist/src/environment/list/action.d.ts +4 -0
  17. package/dist/src/environment/list/action.d.ts.map +1 -0
  18. package/dist/src/environment/list/command.d.ts +4 -0
  19. package/dist/src/environment/list/command.d.ts.map +1 -0
  20. package/dist/src/environment/list/index.d.ts +7 -0
  21. package/dist/src/environment/list/index.d.ts.map +1 -0
  22. package/dist/src/index.d.ts +1 -0
  23. package/dist/src/index.d.ts.map +1 -1
  24. package/dist/src/login/command.d.ts.map +1 -1
  25. package/dist/src/logout/command.d.ts.map +1 -1
  26. package/dist/src/services/cli-api.d.ts +28 -0
  27. package/dist/src/services/cli-api.d.ts.map +1 -1
  28. package/dist/src/types.d.ts +4 -1
  29. package/dist/src/types.d.ts.map +1 -1
  30. package/dist/src/utils/pkg.d.ts.map +1 -1
  31. package/dist/src/utils/tests/compress-files.test.d.ts +2 -0
  32. package/dist/src/utils/tests/compress-files.test.d.ts.map +1 -0
  33. package/package.json +9 -9
  34. package/dist/src/create-project/utils/apply-default-name.d.ts +0 -7
  35. package/dist/src/create-project/utils/apply-default-name.d.ts.map +0 -1
package/dist/index.mjs CHANGED
@@ -1,6 +1,7 @@
1
1
  import crypto$1 from "crypto";
2
2
  import * as fse from "fs-extra";
3
3
  import fse__default from "fs-extra";
4
+ import inquirer from "inquirer";
4
5
  import * as path from "path";
5
6
  import path__default from "path";
6
7
  import chalk from "chalk";
@@ -9,7 +10,6 @@ import * as crypto from "node:crypto";
9
10
  import { env } from "@strapi/utils";
10
11
  import * as tar from "tar";
11
12
  import { minimatch } from "minimatch";
12
- import inquirer from "inquirer";
13
13
  import { defaults, has } from "lodash/fp";
14
14
  import os from "os";
15
15
  import XDGAppPaths from "xdg-app-paths";
@@ -20,8 +20,8 @@ import ora from "ora";
20
20
  import * as cliProgress from "cli-progress";
21
21
  import pkgUp from "pkg-up";
22
22
  import * as yup from "yup";
23
- import _ from "lodash";
24
23
  import EventSource from "eventsource";
24
+ import { createCommand } from "commander";
25
25
  const apiConfig = {
26
26
  apiBaseUrl: env("STRAPI_CLI_CLOUD_API", "https://cloud-cli-api.strapi.io"),
27
27
  dashboardBaseUrl: env("STRAPI_CLI_CLOUD_DASHBOARD", "https://cloud.strapi.io")
@@ -134,7 +134,7 @@ async function saveLocalConfig(data) {
134
134
  await fse__default.writeJson(configFilePath, data, { encoding: "utf8", spaces: 2, mode: 384 });
135
135
  }
136
136
  const name = "@strapi/cloud-cli";
137
- const version = "4.25.7";
137
+ const version = "5.2.0";
138
138
  const description = "Commands to interact with the Strapi Cloud";
139
139
  const keywords = [
140
140
  "strapi",
@@ -179,14 +179,14 @@ const scripts = {
179
179
  watch: "pack-up watch"
180
180
  };
181
181
  const dependencies = {
182
- "@strapi/utils": "4.25.7",
183
- axios: "1.6.0",
182
+ "@strapi/utils": "workspace:*",
183
+ axios: "1.7.4",
184
184
  chalk: "4.1.2",
185
185
  "cli-progress": "3.12.0",
186
186
  commander: "8.3.0",
187
187
  eventsource: "2.0.2",
188
188
  "fast-safe-stringify": "2.1.1",
189
- "fs-extra": "10.0.0",
189
+ "fs-extra": "11.2.0",
190
190
  inquirer: "8.2.5",
191
191
  jsonwebtoken: "9.0.0",
192
192
  "jwks-rsa": "3.1.0",
@@ -195,7 +195,7 @@ const dependencies = {
195
195
  open: "8.4.0",
196
196
  ora: "5.4.1",
197
197
  "pkg-up": "3.1.0",
198
- tar: "6.1.13",
198
+ tar: "6.2.1",
199
199
  "xdg-app-paths": "8.3.0",
200
200
  yup: "0.32.9"
201
201
  };
@@ -204,11 +204,11 @@ const devDependencies = {
204
204
  "@types/cli-progress": "3.11.5",
205
205
  "@types/eventsource": "1.1.15",
206
206
  "@types/lodash": "^4.14.191",
207
- "eslint-config-custom": "4.25.7",
208
- tsconfig: "4.25.7"
207
+ "eslint-config-custom": "workspace:*",
208
+ tsconfig: "workspace:*"
209
209
  };
210
210
  const engines = {
211
- node: ">=18.0.0 <=20.x.x",
211
+ node: ">=18.0.0 <=22.x.x",
212
212
  npm: ">=6.0.0"
213
213
  };
214
214
  const packageJson = {
@@ -258,7 +258,7 @@ async function cloudApiFactory({ logger }, token) {
258
258
  deploy({ filePath, project }, { onUploadProgress }) {
259
259
  return axiosCloudAPI.post(
260
260
  `/deploy/${project.name}`,
261
- { file: fse__default.createReadStream(filePath) },
261
+ { file: fse__default.createReadStream(filePath), targetEnvironment: project.targetEnvironment },
262
262
  {
263
263
  headers: {
264
264
  "Content-Type": "multipart/form-data"
@@ -317,7 +317,7 @@ async function cloudApiFactory({ logger }, token) {
317
317
  },
318
318
  async listLinkProjects() {
319
319
  try {
320
- const response = await axiosCloudAPI.get("/projects/linkable");
320
+ const response = await axiosCloudAPI.get("/projects-linkable");
321
321
  if (response.status !== 200) {
322
322
  throw new Error("Error fetching cloud projects from the server.");
323
323
  }
@@ -329,6 +329,34 @@ async function cloudApiFactory({ logger }, token) {
329
329
  throw error;
330
330
  }
331
331
  },
332
+ async listEnvironments({ name: name2 }) {
333
+ try {
334
+ const response = await axiosCloudAPI.get(`/projects/${name2}/environments`);
335
+ if (response.status !== 200) {
336
+ throw new Error("Error fetching cloud environments from the server.");
337
+ }
338
+ return response;
339
+ } catch (error) {
340
+ logger.debug(
341
+ "🥲 Oops! Couldn't retrieve your project's environments from the server. Please try again."
342
+ );
343
+ throw error;
344
+ }
345
+ },
346
+ async getProject({ name: name2 }) {
347
+ try {
348
+ const response = await axiosCloudAPI.get(`/projects/${name2}`);
349
+ if (response.status !== 200) {
350
+ throw new Error("Error fetching project's details.");
351
+ }
352
+ return response;
353
+ } catch (error) {
354
+ logger.debug(
355
+ "🥲 Oops! There was a problem retrieving your project's details. Please try again."
356
+ );
357
+ throw error;
358
+ }
359
+ },
332
360
  track(event, payload = {}) {
333
361
  return axiosCloudAPI.post("/track", {
334
362
  event,
@@ -602,21 +630,24 @@ yup.object({
602
630
  name: yup.string().required(),
603
631
  exports: yup.lazy(
604
632
  (value) => yup.object(
605
- typeof value === "object" ? Object.entries(value).reduce((acc, [key, value2]) => {
606
- if (typeof value2 === "object") {
607
- acc[key] = yup.object({
608
- types: yup.string().optional(),
609
- source: yup.string().required(),
610
- module: yup.string().optional(),
611
- import: yup.string().required(),
612
- require: yup.string().required(),
613
- default: yup.string().required()
614
- }).noUnknown(true);
615
- } else {
616
- acc[key] = yup.string().matches(/^\.\/.*\.json$/).required();
617
- }
618
- return acc;
619
- }, {}) : void 0
633
+ typeof value === "object" ? Object.entries(value).reduce(
634
+ (acc, [key, value2]) => {
635
+ if (typeof value2 === "object") {
636
+ acc[key] = yup.object({
637
+ types: yup.string().optional(),
638
+ source: yup.string().required(),
639
+ module: yup.string().optional(),
640
+ import: yup.string().required(),
641
+ require: yup.string().required(),
642
+ default: yup.string().required()
643
+ }).noUnknown(true);
644
+ } else {
645
+ acc[key] = yup.string().matches(/^\.\/.*\.json$/).required();
646
+ }
647
+ return acc;
648
+ },
649
+ {}
650
+ ) : void 0
620
651
  ).optional()
621
652
  )
622
653
  });
@@ -638,18 +669,6 @@ async function getProjectNameFromPackageJson(ctx) {
638
669
  return "my-strapi-project";
639
670
  }
640
671
  }
641
- function applyDefaultName(newDefaultName, questions, defaultValues) {
642
- const newDefaultValues = _.cloneDeep(defaultValues);
643
- newDefaultValues.name = newDefaultName;
644
- const newQuestions = questions.map((question) => {
645
- const questionCopy = _.cloneDeep(question);
646
- if (questionCopy.name === "name") {
647
- questionCopy.default = newDefaultName;
648
- }
649
- return questionCopy;
650
- });
651
- return { newQuestions, newDefaultValues };
652
- }
653
672
  const trackEvent = async (ctx, cloudApiService, eventName, eventData) => {
654
673
  try {
655
674
  await cloudApiService.track(eventName, eventData);
@@ -813,6 +832,45 @@ async function loginAction(ctx) {
813
832
  await authenticate();
814
833
  return isAuthenticated;
815
834
  }
835
+ function questionDefaultValuesMapper(questionsMap) {
836
+ return (questions) => {
837
+ return questions.map((question) => {
838
+ const questionName = question.name;
839
+ if (questionName in questionsMap) {
840
+ const questionDefault = questionsMap[questionName];
841
+ if (typeof questionDefault === "function") {
842
+ return {
843
+ ...question,
844
+ default: questionDefault(question)
845
+ };
846
+ }
847
+ return {
848
+ ...question,
849
+ default: questionDefault
850
+ };
851
+ }
852
+ return question;
853
+ });
854
+ };
855
+ }
856
+ function getDefaultsFromQuestions(questions) {
857
+ return questions.reduce((acc, question) => {
858
+ if (question.default && question.name) {
859
+ return { ...acc, [question.name]: question.default };
860
+ }
861
+ return acc;
862
+ }, {});
863
+ }
864
+ function getProjectNodeVersionDefault(question) {
865
+ const currentNodeVersion = process.versions.node.split(".")[0];
866
+ if (question.type === "list" && Array.isArray(question.choices)) {
867
+ const choice = question.choices.find((choice2) => choice2.value === currentNodeVersion);
868
+ if (choice) {
869
+ return choice.value;
870
+ }
871
+ }
872
+ return question.default;
873
+ }
816
874
  async function handleError(ctx, error) {
817
875
  const { logger } = ctx;
818
876
  logger.debug(error);
@@ -857,7 +915,7 @@ async function createProject$1(ctx, cloudApi, projectInput) {
857
915
  throw e;
858
916
  }
859
917
  }
860
- const action$4 = async (ctx) => {
918
+ const action$5 = async (ctx) => {
861
919
  const { logger } = ctx;
862
920
  const { getValidToken, eraseToken } = await tokenServiceFactory(ctx);
863
921
  const token = await getValidToken(ctx, promptLogin);
@@ -866,11 +924,16 @@ const action$4 = async (ctx) => {
866
924
  }
867
925
  const cloudApi = await cloudApiFactory(ctx, token);
868
926
  const { data: config } = await cloudApi.config();
869
- const { newQuestions: questions, newDefaultValues: defaultValues } = applyDefaultName(
870
- await getProjectNameFromPackageJson(ctx),
871
- config.projectCreation.questions,
872
- config.projectCreation.defaults
873
- );
927
+ const projectName = await getProjectNameFromPackageJson(ctx);
928
+ const defaultAnswersMapper = questionDefaultValuesMapper({
929
+ name: projectName,
930
+ nodeVersion: getProjectNodeVersionDefault
931
+ });
932
+ const questions = defaultAnswersMapper(config.projectCreation.questions);
933
+ const defaultValues = {
934
+ ...config.projectCreation.defaults,
935
+ ...getDefaultsFromQuestions(questions)
936
+ };
874
937
  const projectAnswersDefaulted = defaults(defaultValues);
875
938
  const projectAnswers = await inquirer.prompt(questions);
876
939
  const projectInput = projectAnswersDefaulted(projectAnswers);
@@ -981,6 +1044,32 @@ const buildLogsServiceFactory = ({ logger }) => {
981
1044
  });
982
1045
  };
983
1046
  };
1047
+ const QUIT_OPTION$1 = "Quit";
1048
+ async function promptForEnvironment(environments) {
1049
+ const choices = environments.map((env2) => ({ name: env2, value: env2 }));
1050
+ const { selectedEnvironment } = await inquirer.prompt([
1051
+ {
1052
+ type: "list",
1053
+ name: "selectedEnvironment",
1054
+ message: "Select the environment to deploy:",
1055
+ choices: [...choices, { name: chalk.grey(`(${QUIT_OPTION$1})`), value: null }]
1056
+ }
1057
+ ]);
1058
+ if (selectedEnvironment === null) {
1059
+ process.exit(1);
1060
+ }
1061
+ const { confirm } = await inquirer.prompt([
1062
+ {
1063
+ type: "confirm",
1064
+ name: "confirm",
1065
+ message: `Do you want to proceed with deployment to ${chalk.cyan(selectedEnvironment)}?`
1066
+ }
1067
+ ]);
1068
+ if (!confirm) {
1069
+ process.exit(1);
1070
+ }
1071
+ return selectedEnvironment;
1072
+ }
984
1073
  async function upload(ctx, project, token, maxProjectFileSize) {
985
1074
  const cloudApi = await cloudApiFactory(ctx, token);
986
1075
  try {
@@ -1047,17 +1136,7 @@ async function upload(ctx, project, token, maxProjectFileSize) {
1047
1136
  return data.build_id;
1048
1137
  } catch (e) {
1049
1138
  progressBar.stop();
1050
- if (e instanceof AxiosError && e.response?.data) {
1051
- if (e.response.status === 404) {
1052
- ctx.logger.warn(
1053
- `The project does not exist. Please link your local project to a Strapi Cloud project using the link command.`
1054
- );
1055
- } else {
1056
- ctx.logger.error(e.response.data);
1057
- }
1058
- } else {
1059
- ctx.logger.error("An error occurred while deploying the project. Please try again later.");
1060
- }
1139
+ ctx.logger.error("An error occurred while deploying the project. Please try again later.");
1061
1140
  ctx.logger.debug(e);
1062
1141
  } finally {
1063
1142
  await fse__default.remove(tarFilePath);
@@ -1069,11 +1148,11 @@ async function upload(ctx, project, token, maxProjectFileSize) {
1069
1148
  process.exit(1);
1070
1149
  }
1071
1150
  }
1072
- async function getProject(ctx) {
1151
+ async function getProject$1(ctx) {
1073
1152
  const { project } = await retrieve();
1074
1153
  if (!project) {
1075
1154
  try {
1076
- return await action$4(ctx);
1155
+ return await action$5(ctx);
1077
1156
  } catch (e) {
1078
1157
  ctx.logger.error("An error occurred while deploying the project. Please try again later.");
1079
1158
  ctx.logger.debug(e);
@@ -1094,17 +1173,51 @@ async function getConfig({
1094
1173
  return null;
1095
1174
  }
1096
1175
  }
1097
- const action$3 = async (ctx) => {
1176
+ const action$4 = async (ctx, opts) => {
1098
1177
  const { getValidToken } = await tokenServiceFactory(ctx);
1099
1178
  const token = await getValidToken(ctx, promptLogin);
1100
1179
  if (!token) {
1101
1180
  return;
1102
1181
  }
1103
- const project = await getProject(ctx);
1182
+ const project = await getProject$1(ctx);
1104
1183
  if (!project) {
1105
1184
  return;
1106
1185
  }
1107
- const cloudApiService = await cloudApiFactory(ctx);
1186
+ const cloudApiService = await cloudApiFactory(ctx, token);
1187
+ let environments;
1188
+ try {
1189
+ const {
1190
+ data: { data: projectData, metadata }
1191
+ } = await cloudApiService.getProject({ name: project.name });
1192
+ const isProjectSuspended = projectData.suspendedAt;
1193
+ environments = projectData.environments;
1194
+ if (isProjectSuspended) {
1195
+ ctx.logger.log(
1196
+ "\n Oops! This project has been suspended. \n\n Please reactivate it from the dashboard to continue deploying: "
1197
+ );
1198
+ ctx.logger.log(chalk.underline(`${metadata.dashboardUrls.project}`));
1199
+ return;
1200
+ }
1201
+ } catch (e) {
1202
+ if (e instanceof AxiosError && e.response?.data) {
1203
+ if (e.response.status === 404) {
1204
+ ctx.logger.warn(
1205
+ `The project associated with this folder does not exist in Strapi Cloud.
1206
+ Please link your local project to an existing Strapi Cloud project using the ${chalk.cyan(
1207
+ "link"
1208
+ )} command before deploying.`
1209
+ );
1210
+ } else {
1211
+ ctx.logger.error(e.response.data);
1212
+ }
1213
+ } else {
1214
+ ctx.logger.error(
1215
+ "An error occurred while retrieving the project's information. Please try again later."
1216
+ );
1217
+ }
1218
+ ctx.logger.debug(e);
1219
+ return;
1220
+ }
1108
1221
  await trackEvent(ctx, cloudApiService, "willDeployWithCLI", {
1109
1222
  projectInternalName: project.name
1110
1223
  });
@@ -1113,7 +1226,7 @@ const action$3 = async (ctx) => {
1113
1226
  const cliConfig2 = await getConfig({ ctx, cloudApiService });
1114
1227
  if (!cliConfig2) {
1115
1228
  ctx.logger.error(
1116
- "An error occurred while retrieving data from Strapi Cloud. Please try check your network or again later."
1229
+ "An error occurred while retrieving data from Strapi Cloud. Please check your network or try again later."
1117
1230
  );
1118
1231
  return;
1119
1232
  }
@@ -1124,6 +1237,17 @@ const action$3 = async (ctx) => {
1124
1237
  );
1125
1238
  maxSize = 1e8;
1126
1239
  }
1240
+ let targetEnvironment;
1241
+ if (opts.environment) {
1242
+ if (!environments.includes(opts.environment)) {
1243
+ ctx.logger.error(`Environment ${opts.environment} does not exist.`);
1244
+ return;
1245
+ }
1246
+ targetEnvironment = opts.environment;
1247
+ } else {
1248
+ targetEnvironment = environments.length > 1 ? await promptForEnvironment(environments) : environments[0];
1249
+ }
1250
+ project.targetEnvironment = targetEnvironment;
1127
1251
  const buildId = await upload(ctx, project, token, maxSize);
1128
1252
  if (!buildId) {
1129
1253
  return;
@@ -1173,14 +1297,14 @@ const runAction = (name2, action2) => (...args) => {
1173
1297
  process.exit(1);
1174
1298
  });
1175
1299
  };
1176
- const command$5 = ({ command: command2, ctx }) => {
1177
- command2.command("cloud:deploy").alias("deploy").description("Deploy a Strapi Cloud project").option("-d, --debug", "Enable debugging mode with verbose logs").option("-s, --silent", "Don't log anything").action(() => runAction("deploy", action$3)(ctx));
1300
+ const command$6 = ({ ctx }) => {
1301
+ return createCommand("cloud:deploy").alias("deploy").description("Deploy a Strapi Cloud project").option("-d, --debug", "Enable debugging mode with verbose logs").option("-s, --silent", "Don't log anything").option("-e, --environment <name>", "Specify the environment to deploy").action((opts) => runAction("deploy", action$4)(ctx, opts));
1178
1302
  };
1179
1303
  const deployProject = {
1180
1304
  name: "deploy-project",
1181
1305
  description: "Deploy a Strapi Cloud project",
1182
- action: action$3,
1183
- command: command$5
1306
+ action: action$4,
1307
+ command: command$6
1184
1308
  };
1185
1309
  const QUIT_OPTION = "Quit";
1186
1310
  async function getExistingConfig(ctx) {
@@ -1264,7 +1388,7 @@ async function getUserSelection(ctx, projects) {
1264
1388
  return null;
1265
1389
  }
1266
1390
  }
1267
- const action$2 = async (ctx) => {
1391
+ const action$3 = async (ctx) => {
1268
1392
  const { getValidToken } = await tokenServiceFactory(ctx);
1269
1393
  const token = await getValidToken(ctx, promptLogin);
1270
1394
  const { logger } = ctx;
@@ -1319,17 +1443,17 @@ const action$2 = async (ctx) => {
1319
1443
  });
1320
1444
  }
1321
1445
  };
1322
- const command$4 = ({ command: command2, ctx }) => {
1323
- command2.command("cloud:link").alias("link").description("Link a local directory to a Strapi Cloud project").option("-d, --debug", "Enable debugging mode with verbose logs").option("-s, --silent", "Don't log anything").action(() => runAction("link", action$2)(ctx));
1446
+ const command$5 = ({ command: command2, ctx }) => {
1447
+ command2.command("cloud:link").alias("link").description("Link a local directory to a Strapi Cloud project").option("-d, --debug", "Enable debugging mode with verbose logs").option("-s, --silent", "Don't log anything").action(() => runAction("link", action$3)(ctx));
1324
1448
  };
1325
1449
  const link = {
1326
1450
  name: "link-project",
1327
1451
  description: "Link a local directory to a Strapi Cloud project",
1328
- action: action$2,
1329
- command: command$4
1452
+ action: action$3,
1453
+ command: command$5
1330
1454
  };
1331
- const command$3 = ({ command: command2, ctx }) => {
1332
- command2.command("cloud:login").alias("login").description("Strapi Cloud Login").addHelpText(
1455
+ const command$4 = ({ ctx }) => {
1456
+ return createCommand("cloud:login").alias("login").description("Strapi Cloud Login").addHelpText(
1333
1457
  "after",
1334
1458
  "\nAfter running this command, you will be prompted to enter your authentication information."
1335
1459
  ).option("-d, --debug", "Enable debugging mode with verbose logs").option("-s, --silent", "Don't log anything").action(() => runAction("login", loginAction)(ctx));
@@ -1338,10 +1462,10 @@ const login = {
1338
1462
  name: "login",
1339
1463
  description: "Strapi Cloud Login",
1340
1464
  action: loginAction,
1341
- command: command$3
1465
+ command: command$4
1342
1466
  };
1343
1467
  const openModule = import("open");
1344
- const action$1 = async (ctx) => {
1468
+ const action$2 = async (ctx) => {
1345
1469
  const { logger } = ctx;
1346
1470
  const { retrieveToken, eraseToken } = await tokenServiceFactory(ctx);
1347
1471
  const token = await retrieveToken();
@@ -1373,25 +1497,25 @@ const action$1 = async (ctx) => {
1373
1497
  }
1374
1498
  await trackEvent(ctx, cloudApiService, "didLogout", { loginMethod: "cli" });
1375
1499
  };
1376
- const command$2 = ({ command: command2, ctx }) => {
1377
- command2.command("cloud:logout").alias("logout").description("Strapi Cloud Logout").option("-d, --debug", "Enable debugging mode with verbose logs").option("-s, --silent", "Don't log anything").action(() => runAction("logout", action$1)(ctx));
1500
+ const command$3 = ({ ctx }) => {
1501
+ return createCommand("cloud:logout").alias("logout").description("Strapi Cloud Logout").option("-d, --debug", "Enable debugging mode with verbose logs").option("-s, --silent", "Don't log anything").action(() => runAction("logout", action$2)(ctx));
1378
1502
  };
1379
1503
  const logout = {
1380
1504
  name: "logout",
1381
1505
  description: "Strapi Cloud Logout",
1382
- action: action$1,
1383
- command: command$2
1506
+ action: action$2,
1507
+ command: command$3
1384
1508
  };
1385
- const command$1 = ({ command: command2, ctx }) => {
1386
- command2.command("cloud:create-project").description("Create a Strapi Cloud project").option("-d, --debug", "Enable debugging mode with verbose logs").option("-s, --silent", "Don't log anything").action(() => runAction("cloud:create-project", action$4)(ctx));
1509
+ const command$2 = ({ ctx }) => {
1510
+ return createCommand("cloud:create-project").description("Create a Strapi Cloud project").option("-d, --debug", "Enable debugging mode with verbose logs").option("-s, --silent", "Don't log anything").action(() => runAction("cloud:create-project", action$5)(ctx));
1387
1511
  };
1388
1512
  const createProject = {
1389
1513
  name: "create-project",
1390
1514
  description: "Create a new project",
1391
- action: action$4,
1392
- command: command$1
1515
+ action: action$5,
1516
+ command: command$2
1393
1517
  };
1394
- const action = async (ctx) => {
1518
+ const action$1 = async (ctx) => {
1395
1519
  const { getValidToken } = await tokenServiceFactory(ctx);
1396
1520
  const token = await getValidToken(ctx, promptLogin);
1397
1521
  const { logger } = ctx;
@@ -1411,12 +1535,94 @@ const action = async (ctx) => {
1411
1535
  spinner.fail("An error occurred while fetching your projects from Strapi Cloud.");
1412
1536
  }
1413
1537
  };
1414
- const command = ({ command: command2, ctx }) => {
1415
- command2.command("cloud:projects").alias("projects").description("List Strapi Cloud projects").option("-d, --debug", "Enable debugging mode with verbose logs").option("-s, --silent", "Don't log anything").action(() => runAction("projects", action)(ctx));
1538
+ const command$1 = ({ command: command2, ctx }) => {
1539
+ command2.command("cloud:projects").alias("projects").description("List Strapi Cloud projects").option("-d, --debug", "Enable debugging mode with verbose logs").option("-s, --silent", "Don't log anything").action(() => runAction("projects", action$1)(ctx));
1416
1540
  };
1417
1541
  const listProjects = {
1418
1542
  name: "list-projects",
1419
1543
  description: "List Strapi Cloud projects",
1544
+ action: action$1,
1545
+ command: command$1
1546
+ };
1547
+ async function getProject(ctx) {
1548
+ const { project } = await retrieve();
1549
+ if (!project) {
1550
+ ctx.logger.warn(
1551
+ `
1552
+ We couldn't find a valid local project config.
1553
+ Please link your local project to an existing Strapi Cloud project using the ${chalk.cyan(
1554
+ "link"
1555
+ )} command`
1556
+ );
1557
+ process.exit(1);
1558
+ }
1559
+ return project;
1560
+ }
1561
+ const action = async (ctx) => {
1562
+ const { getValidToken } = await tokenServiceFactory(ctx);
1563
+ const token = await getValidToken(ctx, promptLogin);
1564
+ const { logger } = ctx;
1565
+ if (!token) {
1566
+ return;
1567
+ }
1568
+ const project = await getProject(ctx);
1569
+ if (!project) {
1570
+ ctx.logger.debug(`No valid local project configuration was found.`);
1571
+ return;
1572
+ }
1573
+ const cloudApiService = await cloudApiFactory(ctx, token);
1574
+ const spinner = logger.spinner("Fetching environments...").start();
1575
+ await trackEvent(ctx, cloudApiService, "willListEnvironment", {
1576
+ projectInternalName: project.name
1577
+ });
1578
+ try {
1579
+ const {
1580
+ data: { data: environmentsList }
1581
+ } = await cloudApiService.listEnvironments({ name: project.name });
1582
+ spinner.succeed();
1583
+ logger.log(environmentsList);
1584
+ await trackEvent(ctx, cloudApiService, "didListEnvironment", {
1585
+ projectInternalName: project.name
1586
+ });
1587
+ } catch (e) {
1588
+ if (e.response && e.response.status === 404) {
1589
+ spinner.succeed();
1590
+ logger.warn(
1591
+ `
1592
+ The project associated with this folder does not exist in Strapi Cloud.
1593
+ Please link your local project to an existing Strapi Cloud project using the ${chalk.cyan(
1594
+ "link"
1595
+ )} command`
1596
+ );
1597
+ } else {
1598
+ spinner.fail("An error occurred while fetching environments data from Strapi Cloud.");
1599
+ logger.debug("Failed to list environments", e);
1600
+ }
1601
+ await trackEvent(ctx, cloudApiService, "didNotListEnvironment", {
1602
+ projectInternalName: project.name
1603
+ });
1604
+ }
1605
+ };
1606
+ function defineCloudNamespace(command2, ctx) {
1607
+ const cloud = command2.command("cloud").description("Manage Strapi Cloud projects");
1608
+ cloud.command("environments").description("Alias for cloud environment list").action(() => runAction("list", action)(ctx));
1609
+ return cloud;
1610
+ }
1611
+ let environmentCmd = null;
1612
+ const initializeEnvironmentCommand = (command2, ctx) => {
1613
+ if (!environmentCmd) {
1614
+ const cloud = defineCloudNamespace(command2, ctx);
1615
+ environmentCmd = cloud.command("environment").description("Manage environments");
1616
+ }
1617
+ return environmentCmd;
1618
+ };
1619
+ const command = ({ command: command2, ctx }) => {
1620
+ const environmentCmd2 = initializeEnvironmentCommand(command2, ctx);
1621
+ environmentCmd2.command("list").description("List Strapi Cloud project environments").option("-d, --debug", "Enable debugging mode with verbose logs").option("-s, --silent", "Don't log anything").action(() => runAction("list", action)(ctx));
1622
+ };
1623
+ const listEnvironments = {
1624
+ name: "list-environments",
1625
+ description: "List Strapi Cloud environments",
1420
1626
  action,
1421
1627
  command
1422
1628
  };
@@ -1426,9 +1632,10 @@ const cli = {
1426
1632
  login,
1427
1633
  logout,
1428
1634
  createProject,
1429
- listProjects
1635
+ listProjects,
1636
+ listEnvironments
1430
1637
  };
1431
- const cloudCommands = [deployProject, link, login, logout, listProjects];
1638
+ const cloudCommands = [deployProject, link, login, logout, listProjects, listEnvironments];
1432
1639
  async function initCloudCLIConfig() {
1433
1640
  const localConfig = await getLocalConfig();
1434
1641
  if (!localConfig.deviceId) {
@@ -1444,7 +1651,10 @@ async function buildStrapiCloudCommands({
1444
1651
  await initCloudCLIConfig();
1445
1652
  for (const cloudCommand of cloudCommands) {
1446
1653
  try {
1447
- await cloudCommand.command({ command: command2, ctx, argv });
1654
+ const subCommand = await cloudCommand.command({ command: command2, ctx, argv });
1655
+ if (subCommand) {
1656
+ command2.addCommand(subCommand);
1657
+ }
1448
1658
  } catch (e) {
1449
1659
  console.error(`Failed to load command ${cloudCommand.name}`, e);
1450
1660
  }