@zuplo/cli 1.11.0 → 1.13.0

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 CHANGED
@@ -1 +1,12 @@
1
1
  # Zuplo CLI
2
+
3
+ ```
4
+ zup <command>
5
+
6
+ Commands:
7
+ zup deploy Deploys current Git branch of the current directory.
8
+ zup test Runs the tests under /tests against an endpoint
9
+
10
+ Options:
11
+ --help Show help [boolean]
12
+ ```
@@ -3,15 +3,15 @@ import { YargsChecker } from "../common/validators/lib.js";
3
3
  import { deploy } from "../deploy/handler.js";
4
4
  export default {
5
5
  desc: "Deploys current Git branch of the current directory.",
6
- command: "deploy [--apiKey]",
6
+ command: "deploy",
7
7
  builder: (yargs) => {
8
8
  return yargs
9
- .option("apiKey", {
9
+ .option("api-key", {
10
10
  type: "string",
11
11
  describe: "The API Key from Zuplo",
12
12
  envVar: "API_KEY",
13
13
  })
14
- .demandOption(["apiKey"])
14
+ .demandOption(["api-key"])
15
15
  .option("dir", {
16
16
  type: "string",
17
17
  describe: "The directory containing your zup",
package/dist/cmds/test.js CHANGED
@@ -2,11 +2,11 @@ import { validTestDirectoryValidator } from "../common/validators/file-system-va
2
2
  import { YargsChecker } from "../common/validators/lib.js";
3
3
  import { test } from "../test/handler.js";
4
4
  export default {
5
- desc: "Runs the tests under /tests against the [endpoint]",
6
- command: "test [endpoint]",
5
+ desc: "Runs the tests under /tests against an endpoint",
6
+ command: "test",
7
7
  builder: (yargs) => {
8
8
  return yargs
9
- .positional("endpoint", {
9
+ .option("endpoint", {
10
10
  type: "string",
11
11
  describe: "The URL of the zup to test against",
12
12
  })
@@ -1,7 +1,12 @@
1
- export function logDiagnosticsToConsole(message, ...optionalParams) {
2
- console.error(message, ...optionalParams);
1
+ import chalk from "chalk";
2
+ export function printDiagnosticsToConsole(message) {
3
+ console.error(chalk.bold.blue(message));
3
4
  }
4
- export function outputResultToConsole(message, ...optionalParams) {
5
- console.log(message, ...optionalParams);
5
+ export function printCriticalFailureToConsoleAndExit(message) {
6
+ console.error(chalk.bold.red(message));
7
+ process.exit(1);
8
+ }
9
+ export function printResultToConsole(message) {
10
+ console.log(chalk.bold.green(message));
6
11
  }
7
12
  //# sourceMappingURL=output.js.map
@@ -3,7 +3,7 @@ class Settings {
3
3
  return process.env.ZUPLO_DEVELOPER_API_ENDPOINT ?? "https://dev.zuplo.com";
4
4
  }
5
5
  get MAX_POLL_RETRIES() {
6
- return parseInt(process.env.MAX_POLL_RETRIES ?? "30");
6
+ return parseInt(process.env.MAX_POLL_RETRIES ?? "60");
7
7
  }
8
8
  get POLL_INTERVAL() {
9
9
  return parseInt(process.env.POLL_INTERVAL ?? "1000");
@@ -21,6 +21,24 @@ export class ZuploProjectMissingTests extends Error {
21
21
  Object.setPrototypeOf(this, ZuploProjectMissingTests.prototype);
22
22
  }
23
23
  }
24
+ export class NoGitCommits extends Error {
25
+ constructor() {
26
+ super("Invalid Git state: The current directory does not contain any commits. Make a commit before deploying.");
27
+ Object.setPrototypeOf(this, NoGitCommits.prototype);
28
+ }
29
+ }
30
+ export class NotOnAGitBranch extends Error {
31
+ constructor() {
32
+ super("Invalid Git state: You are not on a branch. Run `git checkout -b <branch-name>` to create a new branch.");
33
+ Object.setPrototypeOf(this, NotOnAGitBranch.prototype);
34
+ }
35
+ }
36
+ export class MissingGitRemote extends Error {
37
+ constructor() {
38
+ super("Invalid Git state: You do not have a remote origin configured. Run `git remote add origin <url>` to add the url of Git repository matching your project on Zuplo.");
39
+ Object.setPrototypeOf(this, MissingGitRemote.prototype);
40
+ }
41
+ }
24
42
  export class GitVersionControlValidator {
25
43
  async validate(dir) {
26
44
  try {
@@ -35,6 +53,54 @@ export class GitVersionControlValidator {
35
53
  }
36
54
  }
37
55
  }
56
+ export class GitCommitValidator {
57
+ async validate(dir) {
58
+ try {
59
+ const git = simpleGit({ baseDir: dir });
60
+ const commits = await git.log({ maxCount: 1 });
61
+ if (commits.total === 0) {
62
+ return {
63
+ ok: false,
64
+ error: new NoGitCommits(),
65
+ };
66
+ }
67
+ return { ok: true };
68
+ }
69
+ catch (err) {
70
+ return {
71
+ ok: false,
72
+ error: new NoGitCommits(),
73
+ };
74
+ }
75
+ }
76
+ }
77
+ export class GitBranchValidator {
78
+ async validate(dir) {
79
+ const git = simpleGit({ baseDir: dir });
80
+ const branch = await git.branch();
81
+ if (!branch.current) {
82
+ return {
83
+ ok: false,
84
+ error: new NotOnAGitBranch(),
85
+ };
86
+ }
87
+ return { ok: true };
88
+ }
89
+ }
90
+ export class GitRemoteValidator {
91
+ async validate(dir) {
92
+ const git = simpleGit({ baseDir: dir });
93
+ const remotes = await git.getRemotes(true);
94
+ const origin = remotes.find((r) => r.name === "origin");
95
+ if (!origin) {
96
+ return {
97
+ ok: false,
98
+ error: new MissingGitRemote(),
99
+ };
100
+ }
101
+ return { ok: true };
102
+ }
103
+ }
38
104
  export class ZuploProjectValidator {
39
105
  async validate(dir) {
40
106
  try {
@@ -77,6 +143,6 @@ export class ZuploProjectHasTestsValidator {
77
143
  }
78
144
  }
79
145
  }
80
- export const validDeployDirectoryValidator = new CompositeValidator(new GitVersionControlValidator(), new ZuploProjectValidator());
146
+ export const validDeployDirectoryValidator = new CompositeValidator(new ZuploProjectValidator(), new GitVersionControlValidator(), new GitCommitValidator(), new GitBranchValidator(), new GitRemoteValidator());
81
147
  export const validTestDirectoryValidator = new CompositeValidator(new ZuploProjectValidator(), new ZuploProjectHasTestsValidator());
82
148
  //# sourceMappingURL=file-system-validator.js.map
@@ -1,7 +1,6 @@
1
- import chalk from "chalk";
2
1
  import { parse } from "path";
3
2
  import { logger } from "../common/logger.js";
4
- import { logDiagnosticsToConsole, outputResultToConsole, } from "../common/output.js";
3
+ import { printCriticalFailureToConsoleAndExit, printDiagnosticsToConsole, printResultToConsole, } from "../common/output.js";
5
4
  import settings from "../common/settings.js";
6
5
  import { archive, ARCHIVE_EXTENSION } from "./archive.js";
7
6
  import { upload } from "./file-upload.js";
@@ -12,7 +11,7 @@ export async function deploy(argv) {
12
11
  const whoAmIResponse = await fetch(`${settings.ZUPLO_DEVELOPER_API_ENDPOINT}/v1/who-am-i`, {
13
12
  method: "GET",
14
13
  headers: {
15
- Authorization: `Bearer ${argv.apiKey}`,
14
+ Authorization: `Bearer ${argv["api-key"]}`,
16
15
  },
17
16
  });
18
17
  if (whoAmIResponse.ok) {
@@ -20,21 +19,21 @@ export async function deploy(argv) {
20
19
  const uploadUrlResponse = await fetch(`${settings.ZUPLO_DEVELOPER_API_ENDPOINT}/v1/accounts/${account}/projects/${project}/sources`, {
21
20
  method: "POST",
22
21
  headers: {
23
- Authorization: `Bearer ${argv.apiKey}`,
22
+ Authorization: `Bearer ${argv["api-key"]}`,
24
23
  },
25
24
  });
26
25
  if (uploadUrlResponse.ok) {
27
26
  const { uploadUrl } = await uploadUrlResponse.json();
28
27
  const uploadResponse = await upload(archiveMetadata.tarball, uploadUrl);
29
28
  if (uploadResponse.ok) {
30
- logDiagnosticsToConsole(chalk.green.bold(`Deploying the current branch ${archiveMetadata.metadata.branch} to project ${project} on account ${account}...`));
29
+ printDiagnosticsToConsole(`Deploying the current branch ${archiveMetadata.metadata.branch} to project ${project} on account ${account}...`);
31
30
  const fileId = parse(new URL(uploadUrl).pathname).base.replace(ARCHIVE_EXTENSION, "");
32
31
  const { url } = await pollDeployment(argv, fileId, account, project);
33
32
  if (url) {
34
- outputResultToConsole(chalk.green.bold(`Deployed to ${url}`));
33
+ printResultToConsole(`Deployed to ${url}`);
35
34
  }
36
35
  else {
37
- logDiagnosticsToConsole(`Failed to deploy the current branch ${archiveMetadata.metadata.branch} to project ${project} on account ${account}`);
36
+ printCriticalFailureToConsoleAndExit(`Failed to deploy the current branch ${archiveMetadata.metadata.branch} to project ${project} on account ${account}.`);
38
37
  }
39
38
  }
40
39
  else {
@@ -42,7 +41,7 @@ export async function deploy(argv) {
42
41
  status: uploadResponse.status,
43
42
  statusText: uploadResponse.statusText,
44
43
  }, "Failed to upload source to cloud storage");
45
- process.exit(1);
44
+ printDiagnosticsToConsole("Error: Failed to upload source to cloud storage. Try again later.");
46
45
  }
47
46
  }
48
47
  else {
@@ -50,12 +49,12 @@ export async function deploy(argv) {
50
49
  status: uploadUrlResponse.status,
51
50
  statusText: uploadUrlResponse.statusText,
52
51
  }, "Failed to retrieve uploadUrl");
53
- process.exit(1);
52
+ printDiagnosticsToConsole("Error: Failed to determine where to upload your files. Try again later.");
54
53
  }
55
54
  }
56
55
  else {
57
56
  logger.error({ status: whoAmIResponse.status, statusText: whoAmIResponse.statusText }, "Failed to determine who-am-i");
58
- process.exit(1);
57
+ printDiagnosticsToConsole("Error: Failed to validate the API key. Check your API key.");
59
58
  }
60
59
  }
61
60
  //# sourceMappingURL=handler.js.map
@@ -1,16 +1,15 @@
1
- import chalk from "chalk";
2
- import { logDiagnosticsToConsole } from "../common/output.js";
1
+ import { printDiagnosticsToConsole } from "../common/output.js";
3
2
  import settings from "../common/settings.js";
4
3
  function wait(duration = settings.POLL_INTERVAL) {
5
4
  return new Promise((resolve) => setTimeout(resolve, duration));
6
5
  }
7
6
  export async function pollDeployment(argv, fileId, account, project) {
8
7
  for (let pollRetry = 0; pollRetry < settings.MAX_POLL_RETRIES; pollRetry++) {
9
- logDiagnosticsToConsole(chalk.yellow(`Polling for deployment status... (${pollRetry}/${settings.MAX_POLL_RETRIES})`));
8
+ printDiagnosticsToConsole(`Polling for deployment status... (${pollRetry}/${settings.MAX_POLL_RETRIES})`);
10
9
  const response = await fetch(`${settings.ZUPLO_DEVELOPER_API_ENDPOINT}/v1/accounts/${account}/projects/${project}/deployment-status/${fileId}`, {
11
10
  method: "GET",
12
11
  headers: {
13
- Authorization: `Bearer ${argv.apiKey}`,
12
+ Authorization: `Bearer ${argv["api-key"]}`,
14
13
  },
15
14
  });
16
15
  if (response.ok) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@zuplo/cli",
3
- "version": "1.11.0",
3
+ "version": "1.13.0",
4
4
  "type": "module",
5
5
  "repository": "https://github.com/zuplo/cli",
6
6
  "author": "Zuplo, Inc.",