@grayspar/cli 0.0.1 → 0.0.2

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 (38) hide show
  1. package/README.md +3 -3
  2. package/dist/commands/access.js +12 -12
  3. package/dist/commands/deploy/archive.js +124 -35
  4. package/dist/commands/deploy/await-discovery.js +17 -0
  5. package/dist/commands/deploy/deploy-destination.js +101 -0
  6. package/dist/commands/deploy/deploy-flags.js +10 -4
  7. package/dist/commands/deploy/deploy-target.js +19 -0
  8. package/dist/commands/deploy/deploy.js +119 -276
  9. package/dist/commands/deploy/env-files.js +7 -23
  10. package/dist/commands/deploy/git-state.js +19 -6
  11. package/dist/commands/deploy/submit-deploy.js +127 -0
  12. package/dist/commands/deploy/variables-to-send.js +83 -12
  13. package/dist/commands/env.js +13 -12
  14. package/dist/commands/link.js +6 -5
  15. package/dist/commands/login/login.js +9 -6
  16. package/dist/commands/login/open-browser.js +2 -2
  17. package/dist/commands/login/sign-in-callback.js +5 -4
  18. package/dist/commands/logout.js +5 -17
  19. package/dist/commands/logs.js +23 -18
  20. package/dist/commands/open-path.js +4 -4
  21. package/dist/commands/restart.js +7 -5
  22. package/dist/commands/service.js +16 -25
  23. package/dist/commands/volume.js +33 -19
  24. package/dist/index.js +6 -1
  25. package/dist/lib/client.js +33 -7
  26. package/dist/lib/credentials.js +14 -3
  27. package/dist/lib/follow-deployment.js +79 -51
  28. package/dist/lib/output.js +6 -2
  29. package/dist/lib/project-link.js +2 -5
  30. package/dist/lib/prompt.js +43 -5
  31. package/dist/lib/resolve-project.js +6 -2
  32. package/dist/lib/resolve-team.js +1 -1
  33. package/dist/lib/retry-transient.js +32 -0
  34. package/dist/lib/services-table.js +1 -0
  35. package/dist/lib/usage-errors.js +43 -0
  36. package/dist/lib/version.js +1 -1
  37. package/package.json +6 -1
  38. package/dist/lib/listening.js +0 -24
@@ -0,0 +1,32 @@
1
+ /* A request that never arrived or failed without Grayspar's own answer is tried again with a growing, jittered wait,
2
+ for as long as failures follow one another without a break, then the last failure is let through. The two minutes
3
+ count from the start of the first failing attempt, and each later attempt is cut off when they are up. */
4
+ import { setTimeout as pause } from 'node:timers/promises';
5
+ import { CliError } from './errors.js';
6
+ const FIRST_DELAY_MS = 1000;
7
+ const LONGEST_DELAY_MS = 15_000;
8
+ const GIVE_UP_AFTER_MS = 2 * 60 * 1000;
9
+ export const realClock = { now: () => Date.now(), sleep: (ms) => pause(ms) };
10
+ export function isTransient(error) {
11
+ return (error instanceof CliError && (error.code === 'unreachable' || error.code === 'unavailable'));
12
+ }
13
+ export async function retryTransient(work, clock = realClock) {
14
+ let failingSince = null;
15
+ for (let attempt = 0;; attempt += 1) {
16
+ const started = clock.now();
17
+ try {
18
+ return await work(failingSince === null
19
+ ? undefined
20
+ : AbortSignal.timeout(Math.max(1, Math.round(failingSince + GIVE_UP_AFTER_MS - started))));
21
+ }
22
+ catch (error) {
23
+ if (!isTransient(error))
24
+ throw error;
25
+ failingSince ??= started;
26
+ if (clock.now() - failingSince >= GIVE_UP_AFTER_MS)
27
+ throw error;
28
+ const delay = Math.min(LONGEST_DELAY_MS, FIRST_DELAY_MS * 2 ** attempt);
29
+ await clock.sleep(delay * (0.5 + Math.random() / 2));
30
+ }
31
+ }
32
+ }
@@ -5,6 +5,7 @@ const RUNTIME_STATUS = {
5
5
  running: { label: 'Running', tint: color.ok },
6
6
  restarting: { label: 'Restarting', tint: untinted },
7
7
  crashed: { label: 'Crashed', tint: color.bad },
8
+ unknown: { label: 'Unknown', tint: untinted },
8
9
  failed: { label: 'Failed', tint: color.bad },
9
10
  };
10
11
  export function servicesTable(project) {
@@ -0,0 +1,43 @@
1
+ import { CliError } from './errors.js';
2
+ import { exitWithError } from './output.js';
3
+ const HELP_LINE = 'Run it with --help to see what it takes.';
4
+ const FLAG_NAME_PATTERN = /^--[\w-]+/;
5
+ function match(error, pattern) {
6
+ return pattern.exec(error.message);
7
+ }
8
+ export function usageErrorFor(error) {
9
+ const flag = match(error, /option '([^']+)'/)?.[1];
10
+ const flagName = flag === undefined ? undefined : FLAG_NAME_PATTERN.exec(flag)?.[0];
11
+ switch (error.code) {
12
+ case 'commander.missingMandatoryOptionValue':
13
+ return new CliError('bad_usage', `This command needs ${flag ?? 'a flag'}.\n${HELP_LINE}`, flagName);
14
+ case 'commander.optionMissingArgument':
15
+ return new CliError('bad_usage', `${flag ?? 'A flag'} needs a value.\n${HELP_LINE}`, flagName);
16
+ case 'commander.invalidArgument': {
17
+ const choices = match(error, /Allowed choices are ([^.]+)/)?.[1];
18
+ return new CliError('bad_usage', `${flag ?? 'That'} ${choices === undefined ? "doesn't take that value" : `takes one of ${choices}`}.\n${HELP_LINE}`, flagName);
19
+ }
20
+ case 'commander.unknownOption':
21
+ return new CliError('bad_usage', `${match(error, /unknown option '([^']+)'/)?.[1] ?? 'That flag'} isn't a flag of this command.\n${HELP_LINE}`);
22
+ case 'commander.unknownCommand':
23
+ return new CliError('bad_usage', `${match(error, /unknown command '([^']+)'/)?.[1] ?? 'That'} isn't a command.\nRun grayspar --help to see the commands.`);
24
+ case 'commander.missingArgument':
25
+ return new CliError('bad_usage', `This command needs ${match(error, /argument '([^']+)'/)?.[1] ?? 'more'} after its name.\n${HELP_LINE}`);
26
+ case 'commander.excessArguments':
27
+ return new CliError('bad_usage', `This command was given more than it takes.\n${HELP_LINE}`);
28
+ default:
29
+ return new CliError('bad_usage', `That command line isn't valid.\n${HELP_LINE}`);
30
+ }
31
+ }
32
+ export function routeUsageErrors(program) {
33
+ const refuse = (error) => {
34
+ if (error.exitCode === 0 || error.code === 'commander.help')
35
+ process.exit(error.exitCode);
36
+ exitWithError(program.opts().json === true ? 'json' : 'text', usageErrorFor(error));
37
+ };
38
+ const route = (command) => {
39
+ command.exitOverride(refuse).configureOutput({ outputError: () => undefined });
40
+ command.commands.forEach(route);
41
+ };
42
+ route(program);
43
+ }
@@ -7,6 +7,6 @@ function readVersion() {
7
7
  typeof manifest.version === 'string') {
8
8
  return manifest.version;
9
9
  }
10
- throw new Error('The CLI\u2019s package.json has no version.');
10
+ throw new Error('package.json of the CLI has no version');
11
11
  }
12
12
  export const cliVersion = readVersion();
package/package.json CHANGED
@@ -1,7 +1,12 @@
1
1
  {
2
2
  "name": "@grayspar/cli",
3
- "version": "0.0.1",
3
+ "version": "0.0.2",
4
4
  "description": "The cloud for small software. Deploy your applications with one command, and share them with exactly the people you choose.",
5
+ "repository": {
6
+ "type": "git",
7
+ "url": "git+https://github.com/grayspar/Grayspar.git",
8
+ "directory": "packages/cli"
9
+ },
5
10
  "keywords": [
6
11
  "grayspar",
7
12
  "deploy",
@@ -1,24 +0,0 @@
1
- import { receipt, warning } from './ui.js';
2
- function advice(found) {
3
- switch (found.kind) {
4
- case 'several':
5
- return `listens on ports ${found.ports.join(' and ')}. Choose one with grayspar service set ${found.service} --port …`;
6
- case 'localhost':
7
- return 'listens only on localhost, where no request reaches it. Listen on 0.0.0.0';
8
- case 'none':
9
- return 'has not opened a port, so no request reaches it. Listen on $PORT';
10
- }
11
- }
12
- export function portLines(listening) {
13
- const receipts = [];
14
- const warnings = [];
15
- for (const found of listening) {
16
- if (found.kind === 'adopted') {
17
- receipts.push(receipt(`Set port of ${found.service} to ${found.port}, the port it listens on`));
18
- }
19
- else {
20
- warnings.push(warning(`${found.service} ${advice(found)}`));
21
- }
22
- }
23
- return { receipts, warnings };
24
- }