@strapi/cloud-cli 4.25.4 → 4.25.5

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/dist/index.js CHANGED
@@ -194,7 +194,7 @@ async function saveLocalConfig(data) {
194
194
  await fse__namespace.default.writeJson(configFilePath, data, { encoding: "utf8", spaces: 2, mode: 384 });
195
195
  }
196
196
  const name = "@strapi/cloud-cli";
197
- const version = "4.25.3";
197
+ const version = "4.25.4";
198
198
  const description = "Commands to interact with the Strapi Cloud";
199
199
  const keywords = [
200
200
  "strapi",
@@ -239,7 +239,7 @@ const scripts = {
239
239
  watch: "pack-up watch"
240
240
  };
241
241
  const dependencies = {
242
- "@strapi/utils": "4.25.3",
242
+ "@strapi/utils": "4.25.4",
243
243
  axios: "1.6.0",
244
244
  chalk: "4.1.2",
245
245
  "cli-progress": "3.12.0",
@@ -264,8 +264,8 @@ const devDependencies = {
264
264
  "@types/cli-progress": "3.11.5",
265
265
  "@types/eventsource": "1.1.15",
266
266
  "@types/lodash": "^4.14.191",
267
- "eslint-config-custom": "4.25.3",
268
- tsconfig: "4.25.3"
267
+ "eslint-config-custom": "4.25.4",
268
+ tsconfig: "4.25.4"
269
269
  };
270
270
  const engines = {
271
271
  node: ">=18.0.0 <=20.x.x",
@@ -375,6 +375,20 @@ async function cloudApiFactory({ logger }, token) {
375
375
  throw error;
376
376
  }
377
377
  },
378
+ async listLinkProjects() {
379
+ try {
380
+ const response = await axiosCloudAPI.get("/projects/linkable");
381
+ if (response.status !== 200) {
382
+ throw new Error("Error fetching cloud projects from the server.");
383
+ }
384
+ return response;
385
+ } catch (error) {
386
+ logger.debug(
387
+ "🥲 Oops! Couldn't retrieve your project's list from the server. Please try again."
388
+ );
389
+ throw error;
390
+ }
391
+ },
378
392
  track(event, payload = {}) {
379
393
  return axiosCloudAPI.post("/track", {
380
394
  event,
@@ -696,6 +710,13 @@ function applyDefaultName(newDefaultName, questions, defaultValues) {
696
710
  });
697
711
  return { newQuestions, newDefaultValues };
698
712
  }
713
+ const trackEvent = async (ctx, cloudApiService, eventName, eventData) => {
714
+ try {
715
+ await cloudApiService.track(eventName, eventData);
716
+ } catch (e) {
717
+ ctx.logger.debug(`Failed to track ${eventName}`, e);
718
+ }
719
+ };
699
720
  const openModule$1 = import("open");
700
721
  async function promptLogin(ctx) {
701
722
  const response = await inquirer__default.default.prompt([
@@ -716,13 +737,6 @@ async function loginAction(ctx) {
716
737
  const tokenService = await tokenServiceFactory(ctx);
717
738
  const existingToken = await tokenService.retrieveToken();
718
739
  const cloudApiService = await cloudApiFactory(ctx, existingToken || void 0);
719
- const trackFailedLogin = async () => {
720
- try {
721
- await cloudApiService.track("didNotLogin", { loginMethod: "cli" });
722
- } catch (e) {
723
- logger.debug("Failed to track failed login", e);
724
- }
725
- };
726
740
  if (existingToken) {
727
741
  const isTokenValid = await tokenService.isTokenValid(existingToken);
728
742
  if (isTokenValid) {
@@ -754,11 +768,7 @@ async function loginAction(ctx) {
754
768
  logger.debug(e);
755
769
  return false;
756
770
  }
757
- try {
758
- await cloudApiService.track("willLoginAttempt", {});
759
- } catch (e) {
760
- logger.debug("Failed to track login attempt", e);
761
- }
771
+ await trackEvent(ctx, cloudApiService, "willLoginAttempt", {});
762
772
  logger.debug("🔐 Creating device authentication request...", {
763
773
  client_id: cliConfig2.clientId,
764
774
  scope: cliConfig2.scope,
@@ -838,13 +848,13 @@ async function loginAction(ctx) {
838
848
  "There seems to be a problem with your login information. Please try logging in again."
839
849
  );
840
850
  spinnerFail();
841
- await trackFailedLogin();
851
+ await trackEvent(ctx, cloudApiService, "didNotLogin", { loginMethod: "cli" });
842
852
  return false;
843
853
  }
844
854
  if (e.response?.data.error && !["authorization_pending", "slow_down"].includes(e.response.data.error)) {
845
855
  logger.debug(e);
846
856
  spinnerFail();
847
- await trackFailedLogin();
857
+ await trackEvent(ctx, cloudApiService, "didNotLogin", { loginMethod: "cli" });
848
858
  return false;
849
859
  }
850
860
  await new Promise((resolve) => {
@@ -858,11 +868,7 @@ async function loginAction(ctx) {
858
868
  "To access your dashboard, please copy and paste the following URL into your web browser:"
859
869
  );
860
870
  logger.log(chalk__default.default.underline(`${apiConfig.dashboardBaseUrl}/projects`));
861
- try {
862
- await cloudApiService.track("didLogin", { loginMethod: "cli" });
863
- } catch (e) {
864
- logger.debug("Failed to track login", e);
865
- }
871
+ await trackEvent(ctx, cloudApiService, "didLogin", { loginMethod: "cli" });
866
872
  };
867
873
  await authenticate();
868
874
  return isAuthenticated;
@@ -911,7 +917,7 @@ async function createProject$1(ctx, cloudApi, projectInput) {
911
917
  throw e;
912
918
  }
913
919
  }
914
- const action$3 = async (ctx) => {
920
+ const action$4 = async (ctx) => {
915
921
  const { logger } = ctx;
916
922
  const { getValidToken, eraseToken } = await tokenServiceFactory(ctx);
917
923
  const token = await getValidToken(ctx, promptLogin);
@@ -1026,6 +1032,7 @@ const buildLogsServiceFactory = ({ logger }) => {
1026
1032
  if (retries > MAX_RETRIES) {
1027
1033
  spinner.fail("We were unable to connect to the server to get build logs at this time.");
1028
1034
  es.close();
1035
+ clearExistingTimeout();
1029
1036
  reject(new Error("Max retries reached"));
1030
1037
  }
1031
1038
  };
@@ -1126,7 +1133,7 @@ async function getProject(ctx) {
1126
1133
  const { project } = await retrieve();
1127
1134
  if (!project) {
1128
1135
  try {
1129
- return await action$3(ctx);
1136
+ return await action$4(ctx);
1130
1137
  } catch (e) {
1131
1138
  ctx.logger.error("An error occurred while deploying the project. Please try again later.");
1132
1139
  ctx.logger.debug(e);
@@ -1135,7 +1142,19 @@ async function getProject(ctx) {
1135
1142
  }
1136
1143
  return project;
1137
1144
  }
1138
- const action$2 = async (ctx) => {
1145
+ async function getConfig({
1146
+ ctx,
1147
+ cloudApiService
1148
+ }) {
1149
+ try {
1150
+ const { data: cliConfig2 } = await cloudApiService.config();
1151
+ return cliConfig2;
1152
+ } catch (e) {
1153
+ ctx.logger.debug("Failed to get cli config", e);
1154
+ return null;
1155
+ }
1156
+ }
1157
+ const action$3 = async (ctx) => {
1139
1158
  const { getValidToken } = await tokenServiceFactory(ctx);
1140
1159
  const token = await getValidToken(ctx, promptLogin);
1141
1160
  if (!token) {
@@ -1146,14 +1165,18 @@ const action$2 = async (ctx) => {
1146
1165
  return;
1147
1166
  }
1148
1167
  const cloudApiService = await cloudApiFactory(ctx);
1149
- try {
1150
- await cloudApiService.track("willDeployWithCLI", { projectInternalName: project.name });
1151
- } catch (e) {
1152
- ctx.logger.debug("Failed to track willDeploy", e);
1153
- }
1168
+ await trackEvent(ctx, cloudApiService, "willDeployWithCLI", {
1169
+ projectInternalName: project.name
1170
+ });
1154
1171
  const notificationService = notificationServiceFactory(ctx);
1155
1172
  const buildLogsService = buildLogsServiceFactory(ctx);
1156
- const { data: cliConfig2 } = await cloudApiService.config();
1173
+ const cliConfig2 = await getConfig({ ctx, cloudApiService });
1174
+ if (!cliConfig2) {
1175
+ ctx.logger.error(
1176
+ "An error occurred while retrieving data from Strapi Cloud. Please try check your network or again later."
1177
+ );
1178
+ return;
1179
+ }
1157
1180
  let maxSize = parseInt(cliConfig2.maxProjectFileSize, 10);
1158
1181
  if (Number.isNaN(maxSize)) {
1159
1182
  ctx.logger.debug(
@@ -1175,10 +1198,11 @@ const action$2 = async (ctx) => {
1175
1198
  chalk__default.default.underline(`${apiConfig.dashboardBaseUrl}/projects/${project.name}/deployments`)
1176
1199
  );
1177
1200
  } catch (e) {
1201
+ ctx.logger.debug(e);
1178
1202
  if (e instanceof Error) {
1179
1203
  ctx.logger.error(e.message);
1180
1204
  } else {
1181
- throw e;
1205
+ ctx.logger.error("An error occurred while deploying the project. Please try again later.");
1182
1206
  }
1183
1207
  }
1184
1208
  };
@@ -1209,12 +1233,158 @@ const runAction = (name2, action2) => (...args) => {
1209
1233
  process.exit(1);
1210
1234
  });
1211
1235
  };
1212
- const command$4 = ({ command: command2, ctx }) => {
1213
- 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$2)(ctx));
1236
+ const command$5 = ({ command: command2, ctx }) => {
1237
+ 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));
1214
1238
  };
1215
1239
  const deployProject = {
1216
1240
  name: "deploy-project",
1217
1241
  description: "Deploy a Strapi Cloud project",
1242
+ action: action$3,
1243
+ command: command$5
1244
+ };
1245
+ const QUIT_OPTION = "Quit";
1246
+ async function getExistingConfig(ctx) {
1247
+ try {
1248
+ return await retrieve();
1249
+ } catch (e) {
1250
+ ctx.logger.debug("Failed to get project config", e);
1251
+ ctx.logger.error("An error occurred while retrieving config data from your local project.");
1252
+ return null;
1253
+ }
1254
+ }
1255
+ async function promptForRelink(ctx, cloudApiService, existingConfig) {
1256
+ if (existingConfig && existingConfig.project) {
1257
+ const { shouldRelink } = await inquirer__default.default.prompt([
1258
+ {
1259
+ type: "confirm",
1260
+ name: "shouldRelink",
1261
+ message: `A project named ${chalk__default.default.cyan(
1262
+ existingConfig.project.displayName ? existingConfig.project.displayName : existingConfig.project.name
1263
+ )} is already linked to this local folder. Do you want to update the link?`,
1264
+ default: false
1265
+ }
1266
+ ]);
1267
+ if (!shouldRelink) {
1268
+ await trackEvent(ctx, cloudApiService, "didNotLinkProject", {
1269
+ currentProjectName: existingConfig.project?.name
1270
+ });
1271
+ return false;
1272
+ }
1273
+ }
1274
+ return true;
1275
+ }
1276
+ async function getProjectsList(ctx, cloudApiService, existingConfig) {
1277
+ const spinner = ctx.logger.spinner("Fetching your projects...\n").start();
1278
+ try {
1279
+ const {
1280
+ data: { data: projectList }
1281
+ } = await cloudApiService.listLinkProjects();
1282
+ spinner.succeed();
1283
+ if (!Array.isArray(projectList)) {
1284
+ ctx.logger.log("We couldn't find any projects available for linking in Strapi Cloud");
1285
+ return null;
1286
+ }
1287
+ const projects = projectList.filter(
1288
+ (project) => !(project.isMaintainer || project.name === existingConfig?.project?.name)
1289
+ ).map((project) => {
1290
+ return {
1291
+ name: project.displayName,
1292
+ value: { name: project.name, displayName: project.displayName }
1293
+ };
1294
+ });
1295
+ if (projects.length === 0) {
1296
+ ctx.logger.log("We couldn't find any projects available for linking in Strapi Cloud");
1297
+ return null;
1298
+ }
1299
+ return projects;
1300
+ } catch (e) {
1301
+ spinner.fail("An error occurred while fetching your projects from Strapi Cloud.");
1302
+ ctx.logger.debug("Failed to list projects", e);
1303
+ return null;
1304
+ }
1305
+ }
1306
+ async function getUserSelection(ctx, projects) {
1307
+ const { logger } = ctx;
1308
+ try {
1309
+ const answer = await inquirer__default.default.prompt([
1310
+ {
1311
+ type: "list",
1312
+ name: "linkProject",
1313
+ message: "Which project do you want to link?",
1314
+ choices: [...projects, { name: chalk__default.default.grey(`(${QUIT_OPTION})`), value: null }]
1315
+ }
1316
+ ]);
1317
+ if (!answer.linkProject) {
1318
+ return null;
1319
+ }
1320
+ return answer;
1321
+ } catch (e) {
1322
+ logger.debug("Failed to get user input", e);
1323
+ logger.error("An error occurred while trying to get your input.");
1324
+ return null;
1325
+ }
1326
+ }
1327
+ const action$2 = async (ctx) => {
1328
+ const { getValidToken } = await tokenServiceFactory(ctx);
1329
+ const token = await getValidToken(ctx, promptLogin);
1330
+ const { logger } = ctx;
1331
+ if (!token) {
1332
+ return;
1333
+ }
1334
+ const cloudApiService = await cloudApiFactory(ctx, token);
1335
+ const existingConfig = await getExistingConfig(ctx);
1336
+ const shouldRelink = await promptForRelink(ctx, cloudApiService, existingConfig);
1337
+ if (!shouldRelink) {
1338
+ return;
1339
+ }
1340
+ await trackEvent(ctx, cloudApiService, "willLinkProject", {});
1341
+ const projects = await getProjectsList(
1342
+ ctx,
1343
+ cloudApiService,
1344
+ existingConfig
1345
+ );
1346
+ if (!projects) {
1347
+ return;
1348
+ }
1349
+ const answer = await getUserSelection(ctx, projects);
1350
+ if (!answer) {
1351
+ return;
1352
+ }
1353
+ try {
1354
+ const { confirmAction } = await inquirer__default.default.prompt([
1355
+ {
1356
+ type: "confirm",
1357
+ name: "confirmAction",
1358
+ message: "Warning: Once linked, deploying from CLI will replace the existing project and its data. Confirm to proceed:",
1359
+ default: false
1360
+ }
1361
+ ]);
1362
+ if (!confirmAction) {
1363
+ await trackEvent(ctx, cloudApiService, "didNotLinkProject", {
1364
+ cancelledProjectName: answer.linkProject.name,
1365
+ currentProjectName: existingConfig ? existingConfig.project?.name : null
1366
+ });
1367
+ return;
1368
+ }
1369
+ await save({ project: answer.linkProject });
1370
+ logger.log(`Project ${chalk__default.default.cyan(answer.linkProject.displayName)} linked successfully.`);
1371
+ await trackEvent(ctx, cloudApiService, "didLinkProject", {
1372
+ projectInternalName: answer.linkProject
1373
+ });
1374
+ } catch (e) {
1375
+ logger.debug("Failed to link project", e);
1376
+ logger.error("An error occurred while linking the project.");
1377
+ await trackEvent(ctx, cloudApiService, "didNotLinkProject", {
1378
+ projectInternalName: answer.linkProject
1379
+ });
1380
+ }
1381
+ };
1382
+ const command$4 = ({ command: command2, ctx }) => {
1383
+ 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));
1384
+ };
1385
+ const link = {
1386
+ name: "link-project",
1387
+ description: "Link a local directory to a Strapi Cloud project",
1218
1388
  action: action$2,
1219
1389
  command: command$4
1220
1390
  };
@@ -1261,11 +1431,7 @@ const action$1 = async (ctx) => {
1261
1431
  logger.error("🥲 Oops! Something went wrong while logging you out. Please try again.");
1262
1432
  logger.debug(e);
1263
1433
  }
1264
- try {
1265
- await cloudApiService.track("didLogout", { loginMethod: "cli" });
1266
- } catch (e) {
1267
- logger.debug("Failed to track logout event", e);
1268
- }
1434
+ await trackEvent(ctx, cloudApiService, "didLogout", { loginMethod: "cli" });
1269
1435
  };
1270
1436
  const command$2 = ({ command: command2, ctx }) => {
1271
1437
  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));
@@ -1277,12 +1443,12 @@ const logout = {
1277
1443
  command: command$2
1278
1444
  };
1279
1445
  const command$1 = ({ command: command2, ctx }) => {
1280
- 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$3)(ctx));
1446
+ 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));
1281
1447
  };
1282
1448
  const createProject = {
1283
1449
  name: "create-project",
1284
1450
  description: "Create a new project",
1285
- action: action$3,
1451
+ action: action$4,
1286
1452
  command: command$1
1287
1453
  };
1288
1454
  const action = async (ctx) => {
@@ -1306,7 +1472,7 @@ const action = async (ctx) => {
1306
1472
  }
1307
1473
  };
1308
1474
  const command = ({ command: command2, ctx }) => {
1309
- 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("listProjects", action)(ctx));
1475
+ 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));
1310
1476
  };
1311
1477
  const listProjects = {
1312
1478
  name: "list-projects",
@@ -1316,12 +1482,13 @@ const listProjects = {
1316
1482
  };
1317
1483
  const cli = {
1318
1484
  deployProject,
1485
+ link,
1319
1486
  login,
1320
1487
  logout,
1321
1488
  createProject,
1322
1489
  listProjects
1323
1490
  };
1324
- const cloudCommands = [deployProject, login, logout, listProjects];
1491
+ const cloudCommands = [deployProject, link, login, logout, listProjects];
1325
1492
  async function initCloudCLIConfig() {
1326
1493
  const localConfig = await getLocalConfig();
1327
1494
  if (!localConfig.deviceId) {