@dotenvx/dotenvx 1.21.0 → 1.22.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/CHANGELOG.md CHANGED
@@ -2,7 +2,25 @@
2
2
 
3
3
  All notable changes to this project will be documented in this file. See [standard-version](https://github.com/conventional-changelog/standard-version) for commit guidelines.
4
4
 
5
- ## [Unreleased](https://github.com/dotenvx/dotenvx/compare/v1.21.0...main)
5
+ ## [Unreleased](https://github.com/dotenvx/dotenvx/compare/v1.22.0...main)
6
+
7
+ ## 1.22.0
8
+
9
+ ### Added
10
+
11
+ * add `--pattern` argument to `ext gitignore` (`dotenvx ext gitignore --pattern .env.keys`) ([#430](https://github.com/dotenvx/dotenvx/pull/430))
12
+
13
+ ### Changed
14
+
15
+ * clarify next steps after first time encrypting ([#430](https://github.com/dotenvx/dotenvx/pull/430))
16
+
17
+ ## 1.21.1
18
+
19
+ ### Changed
20
+
21
+ * for `--convention nextjs` ingnore `.env.local` for TEST environment ([#425](https://github.com/dotenvx/dotenvx/pull/425))
22
+ * for `precommit` redirect missing `dotenvx` command using POSIX compliant redirection ([#424](https://github.com/dotenvx/dotenvx/pull/424))
23
+ * make parent `dotenvx help` command less noisy by removing `[options]`. run `dotenvx COMMAND -h` to list all available options like always ([#429](https://github.com/dotenvx/dotenvx/pull/429))
6
24
 
7
25
  ## 1.21.0
8
26
 
package/README.md CHANGED
@@ -1222,8 +1222,8 @@ More examples
1222
1222
  $ dotenvx encrypt
1223
1223
  ✔ encrypted (.env)
1224
1224
  ✔ key added to .env.keys (DOTENV_PRIVATE_KEY)
1225
- add .env.keys to .gitignore: [echo ".env.keys" >> .gitignore]
1226
- run [DOTENV_PRIVATE_KEY='122...0b8' dotenvx run -- yourcommand] to test decryption locally
1225
+ ⮕ next run [dotenvx ext gitignore --pattern .env.keys] to gitignore .env.keys
1226
+ ⮕ next run [DOTENV_PRIVATE_KEY='122...0b8' dotenvx run -- yourcommand] to test decryption locally
1227
1227
  ```
1228
1228
 
1229
1229
  </details>
@@ -1238,8 +1238,8 @@ More examples
1238
1238
  $ dotenvx encrypt -f .env.production
1239
1239
  ✔ encrypted (.env.production)
1240
1240
  ✔ key added to .env.keys (DOTENV_PRIVATE_KEY_PRODUCTION)
1241
- add .env.keys to .gitignore: [echo ".env.keys" >> .gitignore]
1242
- run [DOTENV_PRIVATE_KEY_PRODUCTION='bff..bc4' dotenvx run -- yourcommand] to test decryption locally
1241
+ ⮕ next run [dotenvx ext gitignore --pattern .env.keys] to gitignore .env.keys
1242
+ ⮕ next run [DOTENV_PRIVATE_KEY='bff...bc4' dotenvx run -- yourcommand] to test decryption locally
1243
1243
  ```
1244
1244
 
1245
1245
  </details>
@@ -1536,7 +1536,7 @@ More examples
1536
1536
 
1537
1537
  ```sh
1538
1538
  $ dotenvx help
1539
- Usage: dotenvx [options] [command] [command] [args...]
1539
+ Usage: dotenvx run -- yourcommand
1540
1540
 
1541
1541
  a better dotenv–from the creator of `dotenv`
1542
1542
 
@@ -1549,12 +1549,13 @@ More examples
1549
1549
  -h, --help display help for command
1550
1550
 
1551
1551
  Commands:
1552
- run [options] inject env at runtime [dotenvx run -- yourcommand]
1553
- get [options] [key] return a single environment variable
1554
- set [options] <KEY> <value> set a single environment variable
1555
- encrypt [options] convert .env file(s) to encrypted .env file(s)
1556
- decrypt [options] convert encrypted .env file(s) to plain .env file(s)
1557
- ls [options] [directory] print all .env files in a tree structure
1552
+ run inject env at runtime [dotenvx run -- yourcommand]
1553
+ get [KEY] return a single environment variable
1554
+ set <KEY> <value> set a single environment variable
1555
+ encrypt convert .env file(s) to encrypted .env file(s)
1556
+ decrypt convert encrypted .env file(s) to plain .env file(s)
1557
+ keypair [KEY] print public/private keys for .env file(s)
1558
+ ls [directory] print all .env files in a tree structure
1558
1559
 
1559
1560
  Advanced:
1560
1561
  pro 🏆 pro
package/package.json CHANGED
@@ -1,5 +1,5 @@
1
1
  {
2
- "version": "1.21.0",
2
+ "version": "1.22.0",
3
3
  "name": "@dotenvx/dotenvx",
4
4
  "description": "a better dotenv–from the creator of `dotenv`",
5
5
  "author": "@motdotla",
@@ -64,10 +64,10 @@ function encrypt () {
64
64
  logger.success(`✔ key added to .env.keys (${processedEnv.privateKeyName})`)
65
65
 
66
66
  if (!isIgnoringDotenvKeys()) {
67
- logger.help2(' add .env.keys to .gitignore: [echo ".env.keys" >> .gitignore]')
67
+ logger.help('⮕ next run [dotenvx ext gitignore --pattern .env.keys] to gitignore .env.keys')
68
68
  }
69
69
 
70
- logger.help2(`ℹ run [${processedEnv.privateKeyName}='${processedEnv.privateKey}' dotenvx run -- yourcommand] to test decryption locally`)
70
+ logger.help(`⮕ next run [${processedEnv.privateKeyName}='${processedEnv.privateKey}' dotenvx run -- yourcommand] to test decryption locally`)
71
71
  }
72
72
  }
73
73
  } catch (error) {
@@ -1,12 +1,12 @@
1
1
  const fsx = require('./../../../lib/helpers/fsx')
2
2
 
3
- const FORMATS = ['.env*', '!.env.vault']
3
+ const DEFAULT_PATTERNS = ['.env*']
4
4
  const { logger } = require('./../../../shared/logger')
5
5
 
6
6
  class Generic {
7
- constructor (filename, touchFile = false) {
7
+ constructor (filename, patterns = DEFAULT_PATTERNS, touchFile = false) {
8
8
  this.filename = filename
9
- this.formats = FORMATS
9
+ this.patterns = patterns
10
10
  this.touchFile = touchFile
11
11
  }
12
12
 
@@ -15,10 +15,9 @@ class Generic {
15
15
  }
16
16
 
17
17
  run () {
18
+ const changedPatterns = []
18
19
  if (!fsx.existsSync(this.filename)) {
19
- if (this.touchFile === true) {
20
- logger.info(`creating ${this.filename}`)
21
-
20
+ if (this.touchFile === true && this.patterns.length > 0) {
22
21
  fsx.writeFileX(this.filename, '')
23
22
  } else {
24
23
  return
@@ -26,41 +25,63 @@ class Generic {
26
25
  }
27
26
 
28
27
  const lines = fsx.readFileX(this.filename).split(/\r?\n/)
29
- this.formats.forEach(format => {
30
- if (!lines.includes(format.trim())) {
31
- logger.info(`appending ${format} to ${this.filename}`)
28
+ this.patterns.forEach(pattern => {
29
+ if (!lines.includes(pattern.trim())) {
30
+ this.append(pattern)
32
31
 
33
- this.append(format)
32
+ changedPatterns.push(pattern.trim())
34
33
  }
35
34
  })
35
+
36
+ if (changedPatterns.length > 0) {
37
+ logger.success(`✔ ignored ${this.patterns} (${this.filename})`)
38
+ } else {
39
+ logger.info(`no changes (${this.filename})`)
40
+ }
36
41
  }
37
42
  }
38
43
 
39
44
  class Git {
45
+ constructor (patterns = DEFAULT_PATTERNS) {
46
+ this.patterns = patterns
47
+ }
48
+
40
49
  run () {
41
- logger.verbose('appending to .gitignore')
42
- new Generic('.gitignore', true).run()
50
+ logger.verbose('add to .gitignore')
51
+ new Generic('.gitignore', this.patterns, true).run()
43
52
  }
44
53
  }
45
54
 
46
55
  class Docker {
56
+ constructor (patterns = DEFAULT_PATTERNS) {
57
+ this.patterns = patterns
58
+ }
59
+
47
60
  run () {
48
- logger.verbose('appending to .dockerignore (if existing)')
49
- new Generic('.dockerignore').run()
61
+ logger.verbose('add to .dockerignore (if exists)')
62
+ new Generic('.dockerignore', this.patterns).run()
50
63
  }
51
64
  }
52
65
 
53
66
  class Npm {
67
+ constructor (patterns = DEFAULT_PATTERNS) {
68
+ this.patterns = patterns
69
+ }
70
+
54
71
  run () {
55
- logger.verbose('appending to .npmignore (if existing)')
56
- new Generic('.npmignore').run()
72
+ logger.verbose('add to .npmignore (if existing)')
73
+ new Generic('.npmignore', this.patterns).run()
57
74
  }
58
75
  }
59
76
 
60
77
  class Vercel {
78
+ constructor (patterns = DEFAULT_PATTERNS) {
79
+ this.patterns = patterns
80
+ }
81
+
61
82
  run () {
62
- logger.verbose('appending to .vercelignore (if existing)')
63
- new Generic('.vercelignore').run()
83
+ logger.verbose('add to .vercelignore (if existing)')
84
+ new Generic('.vercelignore', this.patterns).run()
64
85
  }
65
86
  }
66
87
 
@@ -68,12 +89,12 @@ function gitignore () {
68
89
  const options = this.opts()
69
90
  logger.debug(`options: ${JSON.stringify(options)}`)
70
91
 
71
- new Git().run()
72
- new Docker().run()
73
- new Npm().run()
74
- new Vercel().run()
92
+ const patterns = options.pattern
75
93
 
76
- logger.success('done')
94
+ new Git(patterns).run()
95
+ new Docker(patterns).run()
96
+ new Npm(patterns).run()
97
+ new Vercel(patterns).run()
77
98
  }
78
99
 
79
100
  module.exports = gitignore
@@ -68,10 +68,10 @@ function set (key, value) {
68
68
  logger.success(`✔ key added to .env.keys (${processedEnv.privateKeyName})`)
69
69
 
70
70
  if (!isIgnoringDotenvKeys()) {
71
- logger.help2(' add .env.keys to .gitignore: [echo ".env.keys" >> .gitignore]')
71
+ logger.help('⮕ next run [dotenvx ext gitignore --pattern .env.keys] to gitignore .env.keys')
72
72
  }
73
73
 
74
- logger.help2(`ℹ run [${processedEnv.privateKeyName}='${processedEnv.privateKey}' dotenvx get ${key}] to test decryption locally`)
74
+ logger.help(`⮕ next run [${processedEnv.privateKeyName}='${processedEnv.privateKey}' dotenvx get ${key}] to test decryption locally`)
75
75
  }
76
76
  }
77
77
  } catch (error) {
@@ -40,6 +40,7 @@ ext.command('genexample')
40
40
  ext.command('gitignore')
41
41
  .description('append to .gitignore file (and if existing, .dockerignore, .npmignore, and .vercelignore)')
42
42
  .addHelpText('after', examples.gitignore)
43
+ .option('--pattern <patterns...>', 'pattern(s) to gitignore', ['.env*'])
43
44
  .action(require('./../actions/ext/gitignore'))
44
45
 
45
46
  // dotenvx ext prebuild
@@ -9,6 +9,7 @@ const examples = require('./examples')
9
9
  const packageJson = require('./../lib/helpers/packageJson')
10
10
  const executeDynamic = require('./../lib/helpers/executeDynamic')
11
11
  const removeDynamicHelpSection = require('./../lib/helpers/removeDynamicHelpSection')
12
+ const removeOptionsHelpParts = require('./../lib/helpers/removeOptionsHelpParts')
12
13
 
13
14
  // for use with run
14
15
  const envs = []
@@ -21,6 +22,7 @@ function collectEnvs (type) {
21
22
 
22
23
  // global log levels
23
24
  program
25
+ .usage('run -- yourcommand')
24
26
  .option('-l, --log-level <level>', 'set log level', 'info')
25
27
  .option('-q, --quiet', 'sets log level to error')
26
28
  .option('-v, --verbose', 'sets log level to verbose')
@@ -65,8 +67,9 @@ program.command('run')
65
67
  // dotenvx get
66
68
  const getAction = require('./actions/get')
67
69
  program.command('get')
70
+ .usage('[KEY] [options]')
68
71
  .description('return a single environment variable')
69
- .argument('[key]', 'environment variable name')
72
+ .argument('[KEY]', 'environment variable name')
70
73
  .option('-e, --env <strings...>', 'environment variable(s) set as string (example: "HELLO=World")', collectEnvs('env'), [])
71
74
  .option('-f, --env-file <paths...>', 'path(s) to your env file(s)', collectEnvs('envFile'), [])
72
75
  .option('-fv, --env-vault-file <paths...>', 'path(s) to your .env.vault file(s)', collectEnvs('envVaultFile'), [])
@@ -83,6 +86,7 @@ program.command('get')
83
86
  // dotenvx set
84
87
  const setAction = require('./actions/set')
85
88
  program.command('set')
89
+ .usage('<KEY> <value> [options]')
86
90
  .description('set a single environment variable')
87
91
  .addHelpText('after', examples.set)
88
92
  .allowUnknownOption()
@@ -125,8 +129,9 @@ program.command('decrypt')
125
129
  // dotenvx keypair
126
130
  const keypairAction = require('./actions/keypair')
127
131
  program.command('keypair')
132
+ .usage('[KEY] [options]')
128
133
  .description('print public/private keys for .env file(s)')
129
- .argument('[key]', 'environment variable key name')
134
+ .argument('[KEY]', 'environment variable key name')
130
135
  .option('-f, --env-file <paths...>', 'path(s) to your env file(s)')
131
136
  .option('-pp, --pretty-print', 'pretty print output')
132
137
  .option('--format <type>', 'format of the output (json, shell)', 'json')
@@ -196,6 +201,7 @@ program.helpInformation = function () {
196
201
  const lines = originalHelp.split('\n')
197
202
 
198
203
  removeDynamicHelpSection(lines)
204
+ removeOptionsHelpParts(lines)
199
205
 
200
206
  // Filter out the hidden command from the help output
201
207
  const filteredLines = lines.filter(line =>
@@ -63,13 +63,14 @@ Examples:
63
63
 
64
64
  \`\`\`
65
65
  $ dotenvx ext gitignore
66
+ $ dotenvx ext gitignore --pattern .env.keys
66
67
  \`\`\`
67
68
 
68
69
  Try it:
69
70
 
70
71
  \`\`\`
71
72
  $ dotenvx ext gitignore
72
- done
73
+ ✔ ignored .env* (.gitignore)
73
74
  \`\`\`
74
75
  `
75
76
  }
@@ -1,26 +1,14 @@
1
1
  function conventions (convention) {
2
2
  if (convention === 'nextjs') {
3
3
  const nodeEnv = process.env.NODE_ENV || 'development'
4
+ const canonicalEnv = ['development', 'test', 'production'].includes(nodeEnv) && nodeEnv
4
5
 
5
- const envs = []
6
-
7
- if (['development', 'test', 'production'].includes(nodeEnv)) {
8
- envs.push({ type: 'envFile', value: `.env.${nodeEnv}.local` })
9
- }
10
-
11
- if (['development', 'production'].includes(nodeEnv)) {
12
- envs.push({ type: 'envFile', value: '.env.local' })
13
- }
14
-
15
- if (['development', 'test', 'production'].includes(nodeEnv)) {
16
- envs.push({ type: 'envFile', value: `.env.${nodeEnv}` })
17
- }
18
-
19
- if (['development', 'test', 'production'].includes(nodeEnv)) {
20
- envs.push({ type: 'envFile', value: '.env' })
21
- }
22
-
23
- return envs
6
+ return [
7
+ canonicalEnv && { type: 'envFile', value: `.env.${canonicalEnv}.local` },
8
+ canonicalEnv !== 'test' && { type: 'envFile', value: '.env.local' },
9
+ canonicalEnv && { type: 'envFile', value: `.env.${canonicalEnv}` },
10
+ { type: 'envFile', value: '.env' }
11
+ ].filter(Boolean)
24
12
  } else {
25
13
  throw new Error(`INVALID_CONVENTION: '${convention}'. permitted conventions: ['nextjs']`)
26
14
  }
@@ -3,10 +3,10 @@ const path = require('path')
3
3
 
4
4
  const HOOK_SCRIPT = `#!/bin/sh
5
5
 
6
- if ! command -v dotenvx &> /dev/null
6
+ if ! command -v dotenvx 2>&1 >/dev/null
7
7
  then
8
8
  echo "[dotenvx][precommit] 'dotenvx' command not found"
9
- echo "[dotenvx][precommit] ? install it with [brew install dotenvx/brew/dotenvx]"
9
+ echo "[dotenvx][precommit] ? install it with [curl -fsS https://dotenvx.sh | sh]"
10
10
  echo "[dotenvx][precommit] ? other install options [https://dotenvx.com/docs/install]"
11
11
  exit 1
12
12
  fi
@@ -0,0 +1,11 @@
1
+ // Remove [options] from help text. example:
2
+
3
+ function removeOptionsHelpParts (lines) {
4
+ for (let i = 0; i < lines.length; i++) {
5
+ lines[i] = lines[i].replace(' [options]', '')
6
+ }
7
+
8
+ return lines
9
+ }
10
+
11
+ module.exports = removeOptionsHelpParts
@@ -4,7 +4,6 @@ const ignore = require('ignore')
4
4
 
5
5
  const Ls = require('../services/ls')
6
6
 
7
- const pluralize = require('./../helpers/pluralize')
8
7
  const isFullyEncrypted = require('./../helpers/isFullyEncrypted')
9
8
  const InstallPrecommitHook = require('./../helpers/installPrecommitHook')
10
9
  const childProcess = require('child_process')
@@ -27,8 +26,8 @@ class Precommit {
27
26
  warnings: []
28
27
  }
29
28
  } else {
29
+ let count = 0
30
30
  const warnings = []
31
- let successMessage = 'success'
32
31
  let gitignore = MISSING_GITIGNORE
33
32
 
34
33
  // 1. check for .gitignore file
@@ -45,13 +44,15 @@ class Precommit {
45
44
  const lsService = new Ls(process.cwd(), undefined, this.excludeEnvFile)
46
45
  const dotenvFiles = lsService.run()
47
46
  dotenvFiles.forEach(file => {
47
+ count += 1
48
+
48
49
  // check if file is going to be commited
49
50
  if (this._isFileToBeCommitted(file)) {
50
51
  // check if that file is being ignored
51
52
  if (ig.ignores(file)) {
52
53
  if (file === '.env.example' || file === '.env.vault') {
53
54
  const warning = new Error(`${file} (currently ignored but should not be)`)
54
- warning.help = `? add !${file} to .gitignore with [echo "!${file}" >> .gitignore]`
55
+ warning.help = `? add !${file} to .gitignore [echo "!${file}" >> .gitignore]`
55
56
  warnings.push(warning)
56
57
  }
57
58
  } else {
@@ -61,8 +62,8 @@ class Precommit {
61
62
 
62
63
  // if contents are encrypted don't raise an error
63
64
  if (!encrypted) {
64
- const error = new Error(`${file} not encrypted (or not gitignored)`)
65
- error.help = `? encrypt it with [dotenvx encrypt -f ${file}] or add ${file} to .gitignore with [echo ".env*" >> .gitignore]`
65
+ const error = new Error(`${file} not protected (encrypted or gitignored)`)
66
+ error.help = `? encrypt it [dotenvx encrypt -f ${file}] or gitignore it [echo "${file}" >> .gitignore]`
66
67
  throw error
67
68
  }
68
69
  }
@@ -70,8 +71,12 @@ class Precommit {
70
71
  }
71
72
  })
72
73
 
74
+ let successMessage = `.env files (${count}) protected (encrypted or gitignored)`
75
+ if (count === 0) {
76
+ successMessage = 'zero .env files'
77
+ }
73
78
  if (warnings.length > 0) {
74
- successMessage = `success (with ${pluralize('warning', warnings.length)})`
79
+ successMessage += ` with warnings (${warnings.length})`
75
80
  }
76
81
 
77
82
  return {