@glash/cli 0.2.48 → 0.2.49

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/bin/glash.mjs CHANGED
@@ -91,7 +91,7 @@ ${color.bold('PULL')}
91
91
  pull [<slug>] [--out dir] [--no-env] Unpack a project (asks which, if no slug)
92
92
 
93
93
  ${color.bold('SYSTEM')}
94
- update Update @glash/cli to the latest version
94
+ update [--sudo] Update the glash CLI to the latest version
95
95
 
96
96
  ${color.bold('FLAGS')}
97
97
  --project <slug|id> Override the linked project for this command
@@ -1,46 +1,107 @@
1
1
  import { spawnSync } from 'node:child_process';
2
+ import { accessSync, constants } from 'node:fs';
3
+ import { join } from 'node:path';
4
+ import { stdout } from 'node:process';
2
5
  import { ok, fail, info, warn, color } from '../lib/ui.mjs';
3
6
  import { VERSION } from '../lib/version.mjs';
4
7
 
5
- export async function update() {
6
- info(color.dim('→ updating @glash/cli to the latest version...'));
8
+ // Both package names install the same CLI: @glash/cli is the primary, glashdb
9
+ // the alias. Update whichever one this machine actually has.
10
+ const PRIMARY = '@glash/cli';
11
+ const ALIAS = 'glashdb';
7
12
 
8
- // Quiet npm's funding/audit chatter so the only output is our own clean
9
- // status line. --loglevel=error keeps real failures visible.
10
- const child = spawnSync(
11
- 'npm',
12
- ['install', '-g', '@glash/cli@latest', '--no-fund', '--no-audit', '--loglevel=error'],
13
- { stdio: ['ignore', 'pipe', 'pipe'], encoding: 'utf8' },
14
- );
13
+ export async function update(parsed) {
14
+ const useSudo = Boolean(parsed?.flags?.sudo);
15
+ const pkg = installedPackage() ?? PRIMARY;
16
+
17
+ info(color.dim(`→ updating ${pkg} to the latest version...`));
18
+
19
+ const args = ['install', '-g', `${pkg}@latest`, '--no-fund', '--no-audit', '--loglevel=error'];
20
+ const child = useSudo
21
+ // Inherit stdio so sudo can prompt for the password.
22
+ ? spawnSync('sudo', ['npm', ...args], { stdio: 'inherit', encoding: 'utf8' })
23
+ : spawnSync('npm', args, { stdio: ['ignore', 'pipe', 'pipe'], encoding: 'utf8' });
24
+
25
+ if (child.error) {
26
+ return fail(`Could not run npm: ${child.error.message}`);
27
+ }
15
28
 
16
29
  if (child.status !== 0) {
17
30
  const stderr = (child.stderr || '').trim();
18
- if (/EACCES|permission denied|EPERM/i.test(stderr)) {
19
- warn('npm needs elevated permissions to write the global package directory.');
20
- return fail('Run `sudo npm install -g @glash/cli@latest`, or set a user-writable npm prefix (`npm config set prefix ~/.npm-global`).');
31
+ // Show what npm actually said before any advice: guessing at the cause and
32
+ // hiding the error is how this command used to send people to sudo for
33
+ // problems sudo could not fix.
34
+ if (stderr) stdout.write(color.dim(`\n${stderr}\n\n`));
35
+
36
+ const target = globalLibDir();
37
+ const writable = target ? isWritable(target) : null;
38
+
39
+ if (writable === false) {
40
+ warn(`The global package directory is not writable: ${target}`);
41
+ return fail(
42
+ `Either re-run as \`glash update --sudo\`, or move npm's prefix somewhere you own:\n` +
43
+ ` npm config set prefix ~/.npm-global\n` +
44
+ ` export PATH="$HOME/.npm-global/bin:$PATH" # add to ~/.zshrc`,
45
+ );
46
+ }
47
+
48
+ if (/EACCES|EPERM|permission denied/i.test(stderr)) {
49
+ // Writable prefix but a permission error: almost always a cache left
50
+ // root-owned by an earlier `sudo npm install`.
51
+ warn(`${target ?? 'The global package directory'} is writable, so this is probably npm's cache, not the install location.`);
52
+ return fail(
53
+ `Check the cache owner and fix it if root owns it:\n` +
54
+ ` ls -ld ~/.npm\n` +
55
+ ` sudo chown -R "$(whoami)" ~/.npm\n` +
56
+ `Then run \`glash update\` again.`,
57
+ );
21
58
  }
22
- if (stderr) info(color.dim(stderr));
23
- return fail(`Update failed with exit code ${child.status ?? 'unknown'}. Run \`npm install -g @glash/cli@latest\` manually.`);
59
+
60
+ return fail(`Update failed with exit code ${child.status ?? 'unknown'}. Run \`npm install -g ${pkg}@latest\` to see the full output.`);
24
61
  }
25
62
 
26
- const installed = resolveInstalledVersion();
63
+ const installed = resolveInstalledVersion(pkg);
27
64
  if (installed && installed === VERSION) {
28
65
  ok(`Already on the latest version (${color.bold(`v${installed}`)}).`);
29
66
  } else if (installed) {
30
- ok(`Updated @glash/cli → ${color.bold(`v${installed}`)}.`);
67
+ ok(`Updated ${pkg} → ${color.bold(`v${installed}`)}.`);
31
68
  } else {
32
- ok('Updated @glash/cli to the latest version.');
69
+ ok(`Updated ${pkg} to the latest version.`);
70
+ }
71
+ }
72
+
73
+ /** Which of the two package names is installed globally, if either. */
74
+ function installedPackage() {
75
+ for (const name of [PRIMARY, ALIAS]) {
76
+ if (resolveInstalledVersion(name)) return name;
77
+ }
78
+ return null;
79
+ }
80
+
81
+ function globalLibDir() {
82
+ const res = spawnSync('npm', ['prefix', '-g'], { stdio: ['ignore', 'pipe', 'ignore'], encoding: 'utf8' });
83
+ const prefix = (res.stdout || '').trim();
84
+ if (!prefix) return null;
85
+ return process.platform === 'win32' ? join(prefix, 'node_modules') : join(prefix, 'lib', 'node_modules');
86
+ }
87
+
88
+ function isWritable(dir) {
89
+ try {
90
+ accessSync(dir, constants.W_OK);
91
+ return true;
92
+ } catch {
93
+ return false;
33
94
  }
34
95
  }
35
96
 
36
- function resolveInstalledVersion() {
97
+ function resolveInstalledVersion(pkg = PRIMARY) {
37
98
  try {
38
- const res = spawnSync('npm', ['ls', '-g', '@glash/cli', '--depth=0', '--json'], {
99
+ const res = spawnSync('npm', ['ls', '-g', pkg, '--depth=0', '--json'], {
39
100
  stdio: ['ignore', 'pipe', 'ignore'],
40
101
  encoding: 'utf8',
41
102
  });
42
103
  const parsed = JSON.parse(res.stdout || '{}');
43
- return parsed?.dependencies?.['@glash/cli']?.version || null;
104
+ return parsed?.dependencies?.[pkg]?.version || null;
44
105
  } catch {
45
106
  return null;
46
107
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@glash/cli",
3
- "version": "0.2.48",
3
+ "version": "0.2.49",
4
4
  "description": "glashDB command-line interface - deploy projects, pull source + env, manage env vars.",
5
5
  "license": "Apache-2.0",
6
6
  "homepage": "https://glashdb.com",