envstash-cli 0.5.1 → 0.5.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.
package/README.md CHANGED
@@ -38,6 +38,7 @@ envs pull
38
38
  | `diff` | Show which keys differ, and how |
39
39
  | `pull` | Write the cloud's version of each file to disk |
40
40
  | `push` | Send local files to the cloud |
41
+ | `remove` | Delete files from the cloud (local copies stay) |
41
42
  | `version` | Show the CLI version and its environment |
42
43
 
43
44
  Run `envs help <command>` for details on any one of them.
@@ -111,6 +112,8 @@ definition and therefore has no undo:
111
112
  file are left alone, so pushing a partial `.env` cannot destroy a teammate's
112
113
  key. Use `--prune` when you do want the cloud to mirror local exactly.
113
114
  - **`diff` masks values.** Use `--show-values` to reveal them.
115
+ - **`remove` needs every file named** — there is no "remove everything"
116
+ default — and it only touches the cloud copy; files on your machines stay.
114
117
  - Pulled files are written `0600`, and `~/.envstash/config.json` is `0600`
115
118
  inside a `0700` directory.
116
119
 
@@ -125,6 +128,7 @@ whichever repo needs them:
125
128
  ```bash
126
129
  envs push --shared .npmrc # from any repo, once
127
130
  envs pull --shared .npmrc # in every repo that needs it
131
+ envs rm --shared .npmrc # remove it from the cloud again
128
132
  ```
129
133
 
130
134
  `--shared` always needs the files named: discovery sweeps up a repo's own
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "envstash-cli",
3
- "version": "0.5.1",
3
+ "version": "0.5.2",
4
4
  "description": "Git-style CLI for EnvStash — keep your .env files in sync with your cloud account.",
5
5
  "keywords": [
6
6
  "env",
package/src/api.mjs CHANGED
@@ -159,6 +159,12 @@ export const api = {
159
159
  token,
160
160
  body: { kind: 'file', contentBase64 },
161
161
  }),
162
+
163
+ deleteFile: (host, token, project, file) =>
164
+ request(host, `/api/cli/projects/${encodeURIComponent(project)}/files/${encodePath(file)}`, {
165
+ method: 'DELETE',
166
+ token,
167
+ }),
162
168
  }
163
169
 
164
170
  /** Encode each path segment but keep the "/" separators that address a nested file. */
@@ -0,0 +1,72 @@
1
+ import { api, ApiError } from '../api.mjs'
2
+ import { resolveContext, SHARED_SCOPE } from '../context.mjs'
3
+ import { CliError, out, pluralize, style } from '../ui.mjs'
4
+
5
+ /**
6
+ * `envs remove` (alias `rm`) — delete files from the cloud.
7
+ *
8
+ * Cloud-side only, and the report says so: the copies on this machine are not
9
+ * this command's to touch, which is also what makes it survivable — a removed
10
+ * file is one `envs push` away as long as a local copy exists.
11
+ *
12
+ * Names are always required. Every other sync command can fall back to
13
+ * discovery or the link file because acting on "everything tracked" is safe
14
+ * for a compare, a pull, or a push; for a delete it is the whole account's
15
+ * files gone on an empty invocation, so there is no default set.
16
+ *
17
+ * `--shared` targets the account-wide shared files instead of this repo's
18
+ * project, mirroring push and pull.
19
+ */
20
+ export async function remove(flags, args) {
21
+ const shared = Boolean(flags.shared)
22
+
23
+ if (args.length === 0) {
24
+ throw new CliError('Name the files to remove.', {
25
+ hint: shared ? 'Example: envs rm --shared .npmrc' : 'Example: envs rm .env.local',
26
+ })
27
+ }
28
+
29
+ const context = resolveContext(flags)
30
+ const scope = shared ? SHARED_SCOPE : context.project.name
31
+
32
+ out()
33
+
34
+ let removed = 0
35
+
36
+ for (const name of [...new Set(args)].sort()) {
37
+ if (flags['dry-run']) {
38
+ out(` ${style.dim('would remove')} ${name}`)
39
+ continue
40
+ }
41
+
42
+ try {
43
+ await api.deleteFile(context.host, context.token, scope, name)
44
+ } catch (error) {
45
+ // "Already gone" is a state, not a failure — the file the user wanted
46
+ // absent is absent. Say which it was and keep going.
47
+ if (error instanceof ApiError && error.status === 404) {
48
+ out(` ${style.dim('=')} ${name} ${style.dim('not in the cloud — nothing to remove')}`)
49
+ continue
50
+ }
51
+
52
+ throw error
53
+ }
54
+
55
+ removed += 1
56
+ out(` ${style.red('✗')} ${name} ${style.dim('removed')}`)
57
+ }
58
+
59
+ out()
60
+
61
+ if (!flags['dry-run'] && removed > 0) {
62
+ const source = shared ? 'your shared files' : context.project.name
63
+ out(
64
+ ` ${style.dim(
65
+ `Removed ${pluralize(removed, 'file')} from ${source}. Local copies are untouched.`
66
+ )}`
67
+ )
68
+ out()
69
+ }
70
+
71
+ return 0
72
+ }
package/src/index.mjs CHANGED
@@ -7,6 +7,7 @@ import { login, logout } from './commands/login.mjs'
7
7
  import { init, link, list, version as versionCommand, whoami } from './commands/misc.mjs'
8
8
  import { pull } from './commands/pull.mjs'
9
9
  import { push } from './commands/push.mjs'
10
+ import { remove } from './commands/remove.mjs'
10
11
  import { status } from './commands/status.mjs'
11
12
  import { DEFAULT_HOST } from './config.mjs'
12
13
  import { CLI_ALIASES, CLI_COMMAND_PAD, CLI_COMMANDS, CLI_ENV_VARS } from './manifest.mjs'
@@ -33,6 +34,7 @@ const HANDLERS = {
33
34
  diff,
34
35
  pull,
35
36
  push,
37
+ remove,
36
38
  version: versionCommand,
37
39
  }
38
40
 
@@ -268,8 +270,10 @@ function printHelp(topic) {
268
270
  ` --json ${style.dim('Machine-readable output (status, list, whoami, version)')}`
269
271
  )
270
272
  out(` --exit-zero ${style.dim('Always exit 0, even when out of sync (status, diff)')}`)
271
- out(` --dry-run ${style.dim('Show what would happen (pull, push)')}`)
272
- out(` --shared ${style.dim('Target your account-wide shared files (pull, push)')}`)
273
+ out(` --dry-run ${style.dim('Show what would happen (pull, push, remove)')}`)
274
+ out(
275
+ ` --shared ${style.dim('Target your account-wide shared files (pull, push, remove)')}`
276
+ )
273
277
  out(` --dev ${style.dim('Log in to a local dev server if one is running')}`)
274
278
  out()
275
279
  out(
@@ -345,6 +349,17 @@ const COMMAND_HELP = {
345
349
  ' envs push --prune',
346
350
  ' envs push --shared .npmrc',
347
351
  ],
352
+ remove: [
353
+ 'Delete files from the cloud. Copies on your machines are untouched, so a',
354
+ 'removed file is one `envs push` away as long as a local copy exists.',
355
+ '',
356
+ 'Names are always required — there is no "remove everything" default.',
357
+ 'A name that is not in the cloud is reported and skipped, not an error.',
358
+ '',
359
+ ' envs rm .env.local',
360
+ ' envs rm --shared .npmrc',
361
+ ' envs rm --dry-run .env.local',
362
+ ],
348
363
  login: [
349
364
  'Authorize this machine using a browser device code.',
350
365
  '',
package/src/manifest.mjs CHANGED
@@ -27,6 +27,7 @@ export const CLI_COMMANDS = [
27
27
  { name: 'diff', summary: 'Show which keys differ, and how' },
28
28
  { name: 'pull', summary: 'Write the cloud’s version of each file to disk' },
29
29
  { name: 'push', summary: 'Send local files to the cloud' },
30
+ { name: 'remove', summary: 'Delete files from the cloud (local copies stay)', aliases: ['rm'] },
30
31
  { name: 'version', summary: 'Show the CLI version and its environment' },
31
32
  ]
32
33