@dotenvx/dotenvx 1.56.0 → 1.57.1

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.
@@ -4,7 +4,7 @@ const { logger } = require('./../../shared/logger')
4
4
  const Rotate = require('./../../lib/services/rotate')
5
5
 
6
6
  const catchAndLog = require('../../lib/helpers/catchAndLog')
7
- const isIgnoringDotenvKeys = require('../../lib/helpers/isIgnoringDotenvKeys')
7
+ const localDisplayPath = require('../../lib/helpers/localDisplayPath')
8
8
 
9
9
  function rotate () {
10
10
  const options = this.opts()
@@ -38,15 +38,7 @@ function rotate () {
38
38
  for (const processedEnv of processedEnvs) {
39
39
  logger.verbose(`rotating ${processedEnv.envFilepath} (${processedEnv.filepath})`)
40
40
  if (processedEnv.error) {
41
- if (processedEnv.error.code === 'MISSING_ENV_FILE') {
42
- logger.warn(processedEnv.error.message)
43
- logger.help(`? add one with [echo "HELLO=World" > ${processedEnv.envFilepath}] and re-run [dotenvx rotate]`)
44
- } else {
45
- logger.warn(processedEnv.error.message)
46
- if (processedEnv.error.help) {
47
- logger.help(processedEnv.error.help)
48
- }
49
- }
41
+ logger.warn(processedEnv.error.messageWithHelp)
50
42
  } else if (processedEnv.changed) {
51
43
  fsx.writeFileX(processedEnv.filepath, processedEnv.envSrc)
52
44
  if (processedEnv.privateKeyAdded) {
@@ -60,25 +52,18 @@ function rotate () {
60
52
  }
61
53
 
62
54
  if (changedFilepaths.length > 0) {
63
- logger.success(`✔ rotated (${changedFilepaths.join(',')})`)
55
+ const keyAddedEnv = processedEnvs.find((processedEnv) => processedEnv.privateKeyAdded)
56
+ let msg = `⟳ rotated (${changedFilepaths.join(',')})`
57
+ if (keyAddedEnv) {
58
+ const envKeysFilepath = localDisplayPath(keyAddedEnv.envKeysFilepath)
59
+ msg += ` + key (${envKeysFilepath})`
60
+ }
61
+ logger.success(msg)
64
62
  } else if (unchangedFilepaths.length > 0) {
65
- logger.info(`no changes (${unchangedFilepaths})`)
63
+ logger.info(`○ no changes (${unchangedFilepaths})`)
66
64
  } else {
67
65
  // do nothing - scenario when no .env files found
68
66
  }
69
-
70
- for (const processedEnv of processedEnvs) {
71
- if (processedEnv.privateKeyAdded) {
72
- logger.success(`✔ key added to .env.keys (${processedEnv.privateKeyName})`)
73
- // logger.help('⮕ optional: [dotenvx ops backup] to securely backup private key')
74
-
75
- if (!isIgnoringDotenvKeys()) {
76
- logger.help('⮕ next run: [dotenvx ext gitignore --pattern .env.keys] to gitignore .env.keys')
77
- }
78
-
79
- logger.help(`⮕ next run: [${processedEnv.privateKeyName}='${processedEnv.privateKey}' dotenvx get] to test decryption locally`)
80
- }
81
- }
82
67
  } catch (error) {
83
68
  catchAndLog(error)
84
69
  process.exit(1)
@@ -3,6 +3,7 @@ const { logger } = require('./../../shared/logger')
3
3
 
4
4
  const executeCommand = require('./../../lib/helpers/executeCommand')
5
5
  const Run = require('./../../lib/services/run')
6
+ const catchAndLog = require('./../../lib/helpers/catchAndLog')
6
7
 
7
8
  const conventions = require('./../../lib/helpers/conventions')
8
9
 
@@ -64,18 +65,10 @@ async function run () {
64
65
 
65
66
  if (options.strict) throw error // throw if strict and not ignored
66
67
 
67
- if (error.code === 'MISSING_ENV_FILE') {
68
- if (!options.convention) { // do not output error for conventions (too noisy)
69
- logger.error(error.message)
70
- if (error.help) {
71
- logger.error(`${error.help} and re-run [dotenvx run -- ${commandArgs.join(' ')}]`)
72
- }
73
- }
68
+ if (error.code === 'MISSING_ENV_FILE' && options.convention) { // do not output error for conventions (too noisy)
69
+ // intentionally quiet
74
70
  } else {
75
- logger.error(error.message)
76
- if (error.help) {
77
- logger.error(error.help)
78
- }
71
+ logger.error(error.messageWithHelp)
79
72
  }
80
73
  }
81
74
 
@@ -106,10 +99,7 @@ async function run () {
106
99
 
107
100
  logger.successv(msg)
108
101
  } catch (error) {
109
- logger.error(error.message)
110
- if (error.help) {
111
- logger.error(error.help)
112
- }
102
+ catchAndLog(error)
113
103
  process.exit(1)
114
104
  }
115
105
 
@@ -4,7 +4,7 @@ const { logger } = require('./../../shared/logger')
4
4
  const Sets = require('./../../lib/services/sets')
5
5
 
6
6
  const catchAndLog = require('../../lib/helpers/catchAndLog')
7
- const isIgnoringDotenvKeys = require('../../lib/helpers/isIgnoringDotenvKeys')
7
+ const localDisplayPath = require('../../lib/helpers/localDisplayPath')
8
8
 
9
9
  function set (key, value) {
10
10
  logger.debug(`key: ${key}`)
@@ -40,15 +40,7 @@ function set (key, value) {
40
40
  logger.verbose(`setting for ${processedEnv.envFilepath}`)
41
41
 
42
42
  if (processedEnv.error) {
43
- if (processedEnv.error.code === 'MISSING_ENV_FILE') {
44
- logger.warn(processedEnv.error.message)
45
- logger.help(`? add one with [echo "HELLO=World" > ${processedEnv.envFilepath}] and re-run [dotenvx set]`)
46
- } else {
47
- logger.warn(processedEnv.error.message)
48
- if (processedEnv.error.help) {
49
- logger.help(processedEnv.error.help)
50
- }
51
- }
43
+ logger.warn(processedEnv.error.messageWithHelp)
52
44
  } else {
53
45
  fsx.writeFileX(processedEnv.filepath, processedEnv.envSrc)
54
46
 
@@ -57,26 +49,25 @@ function set (key, value) {
57
49
  }
58
50
  }
59
51
 
52
+ const keyAddedEnv = processedEnvs.find((processedEnv) => processedEnv.privateKeyAdded)
53
+ const keyAddedSuffix = keyAddedEnv ? ` + key (${localDisplayPath(keyAddedEnv.envKeysFilepath)})` : ''
54
+
60
55
  if (changedFilepaths.length > 0) {
61
- logger.success(`✔ set ${key}${withEncryption} (${changedFilepaths.join(',')})`)
56
+ if (encrypt) {
57
+ logger.success(`◈ encrypted ${key} (${changedFilepaths.join(',')})${keyAddedSuffix}`)
58
+ } else {
59
+ logger.success(`◇ set ${key} (${changedFilepaths.join(',')})`)
60
+ }
61
+ } else if (encrypt && keyAddedEnv) {
62
+ const keyAddedEnvFilepath = keyAddedEnv.envFilepath || changedFilepaths[0] || '.env'
63
+ logger.success(`◈ encrypted ${key} (${keyAddedEnvFilepath})${keyAddedSuffix}`)
62
64
  } else if (unchangedFilepaths.length > 0) {
63
- logger.info(`no changes (${unchangedFilepaths})`)
65
+ logger.info(`○ no changes (${unchangedFilepaths})`)
64
66
  } else {
65
67
  // do nothing
66
68
  }
67
69
 
68
- for (const processedEnv of processedEnvs) {
69
- if (processedEnv.privateKeyAdded) { // TODO: change to localPrivateKeyAdded
70
- logger.success(`✔ key added to ${processedEnv.envKeysFilepath} (${processedEnv.privateKeyName})`)
71
- // logger.help('⮕ optional: [dotenvx ops backup] to securely backup private key')
72
-
73
- if (!isIgnoringDotenvKeys()) {
74
- logger.help('⮕ next run: [dotenvx ext gitignore --pattern .env.keys] to gitignore .env.keys')
75
- }
76
-
77
- logger.help(`⮕ next run: [${processedEnv.privateKeyName}='${processedEnv.privateKey}' dotenvx get ${key}] to test decryption locally`)
78
- }
79
- }
70
+ // intentionally quiet: success line communicates key creation
80
71
  } catch (error) {
81
72
  catchAndLog(error)
82
73
  process.exit(1)
@@ -30,8 +30,7 @@ const commanderVersion = getCommanderVersion()
30
30
  if (commanderVersion && parseInt(commanderVersion.split('.')[0], 10) >= 12) {
31
31
  const message = `dotenvx depends on commander@11.x.x but you are attempting to hoist commander@${commanderVersion}`
32
32
  const error = new Errors({ message }).dangerousDependencyHoist()
33
- logger.error(error.message)
34
- if (error.help) logger.error(error.help)
33
+ logger.error(error.messageWithHelp)
35
34
  }
36
35
 
37
36
  // global log levels
@@ -108,7 +107,7 @@ program.command('get')
108
107
  const setAction = require('./actions/set')
109
108
  program.command('set')
110
109
  .usage('<KEY> <value> [options]')
111
- .description('set a single environment variable')
110
+ .description('encrypt a single environment variable')
112
111
  .addHelpText('after', examples.set)
113
112
  .allowUnknownOption()
114
113
  .argument('KEY', 'KEY')
@@ -153,6 +152,21 @@ program.command('decrypt')
153
152
  decryptAction.apply(this, args)
154
153
  })
155
154
 
155
+ // dotenvx rotate
156
+ const rotateAction = require('./actions/rotate')
157
+ program.command('rotate')
158
+ .description('rotate keypair(s) and re-encrypt .env file(s)')
159
+ .option('-f, --env-file <paths...>', 'path(s) to your env file(s)', collectEnvs('envFile'), [])
160
+ .option('-fk, --env-keys-file <path>', 'path to your .env.keys file (default: same path as your env file)')
161
+ .option('-k, --key <keys...>', 'keys(s) to encrypt (default: all keys in file)')
162
+ .option('-ek, --exclude-key <excludeKeys...>', 'keys(s) to exclude from encryption (default: none)')
163
+ .option('--ops-off', 'disable dotenvx-ops features', sesh.opsOff())
164
+ .option('--stdout', 'send to stdout')
165
+ .action(function (...args) {
166
+ this.envs = envs
167
+ rotateAction.apply(this, args)
168
+ })
169
+
156
170
  // dotenvx keypair
157
171
  const keypairAction = require('./actions/keypair')
158
172
  program.command('keypair')
@@ -176,21 +190,6 @@ program.command('ls')
176
190
  .option('-ef, --exclude-env-file <excludeFilenames...>', 'path(s) to exclude from your env file(s) (default: none)')
177
191
  .action(lsAction)
178
192
 
179
- // dotenvx rotate
180
- const rotateAction = require('./actions/rotate')
181
- program.command('rotate')
182
- .description('rotate keypair(s) and re-encrypt .env file(s)')
183
- .option('-f, --env-file <paths...>', 'path(s) to your env file(s)', collectEnvs('envFile'), [])
184
- .option('-fk, --env-keys-file <path>', 'path to your .env.keys file (default: same path as your env file)')
185
- .option('-k, --key <keys...>', 'keys(s) to encrypt (default: all keys in file)')
186
- .option('-ek, --exclude-key <excludeKeys...>', 'keys(s) to exclude from encryption (default: none)')
187
- .option('--ops-off', 'disable dotenvx-ops features', sesh.opsOff())
188
- .option('--stdout', 'send to stdout')
189
- .action(function (...args) {
190
- this.envs = envs
191
- rotateAction.apply(this, args)
192
- })
193
-
194
193
  // dotenvx help
195
194
  program.command('help [command]')
196
195
  .description('display help for command')
@@ -252,7 +251,8 @@ program.helpInformation = function () {
252
251
  const filteredLines = lines.filter(line =>
253
252
  !line.includes('DEPRECATED') &&
254
253
  !line.includes('help [command]') &&
255
- !line.includes('🔌 extensions')
254
+ !line.includes('🔌 extensions') &&
255
+ !/^\s*ls\b/.test(line)
256
256
  )
257
257
 
258
258
  return filteredLines.join('\n')
@@ -66,11 +66,11 @@ Examples:
66
66
  $ dotenvx ext gitignore --pattern .env.keys
67
67
  \`\`\`
68
68
 
69
- Try it:
69
+ Try it:
70
70
 
71
71
  \`\`\`
72
72
  $ dotenvx ext gitignore
73
- ignored .env* (.gitignore)
73
+ ignored .env* (.gitignore)
74
74
  \`\`\`
75
75
  `
76
76
  }
@@ -1,10 +1,7 @@
1
1
  const { logger } = require('./../../shared/logger')
2
2
 
3
3
  function catchAndLog (error) {
4
- logger.error(error.message)
5
- if (error.help) {
6
- logger.help(error.help)
7
- }
4
+ logger.error(error.messageWithHelp)
8
5
  if (error.debug) {
9
6
  logger.debug(error.debug)
10
7
  }
@@ -1,3 +1,5 @@
1
+ const Errors = require('./errors')
2
+
1
3
  function conventions (convention) {
2
4
  const env = process.env.DOTENV_ENV || process.env.NODE_ENV || 'development'
3
5
 
@@ -19,7 +21,7 @@ function conventions (convention) {
19
21
  { type: 'envFile', value: '.env.defaults' }
20
22
  ]
21
23
  } else {
22
- throw new Error(`INVALID_CONVENTION: '${convention}'. permitted conventions: ['nextjs', 'flow']`)
24
+ throw new Errors({ convention }).invalidConvention()
23
25
  }
24
26
  }
25
27
 
@@ -30,7 +30,7 @@ function decryptKeyValue (key, value, privateKeyName, privateKey) {
30
30
  if (e.message === 'Invalid private key') {
31
31
  decryptionError = new Errors({ key, privateKeyName, privateKey }).invalidPrivateKey()
32
32
  } else if (e.message === 'Unsupported state or unable to authenticate data') {
33
- decryptionError = new Errors({ key, privateKeyName, privateKey }).looksWrongPrivateKey()
33
+ decryptionError = new Errors({ key, privateKeyName, privateKey }).wrongPrivateKey()
34
34
  } else if (e.message === 'Point of length 65 was invalid. Expected 33 compressed bytes or 65 uncompressed bytes') {
35
35
  decryptionError = new Errors({ key, privateKeyName, privateKey }).malformedEncryptedData()
36
36
  } else {
@@ -1,5 +1,26 @@
1
1
  const truncate = require('./truncate')
2
2
 
3
+ const ISSUE_BY_CODE = {
4
+ COMMAND_EXITED_WITH_CODE: 'https://github.com/dotenvx/dotenvx/issues/new',
5
+ COMMAND_SUBSTITUTION_FAILED: 'https://github.com/dotenvx/dotenvx/issues/532',
6
+ DECRYPTION_FAILED: 'https://github.com/dotenvx/dotenvx/issues/757',
7
+ DANGEROUS_DEPENDENCY_HOIST: 'https://github.com/dotenvx/dotenvx/issues/622',
8
+ INVALID_COLOR: 'must be 256 colors',
9
+ INVALID_CONVENTION: 'https://github.com/dotenvx/dotenvx/issues/761',
10
+ INVALID_PRIVATE_KEY: 'https://github.com/dotenvx/dotenvx/issues/465',
11
+ INVALID_PUBLIC_KEY: 'https://github.com/dotenvx/dotenvx/issues/756',
12
+ MALFORMED_ENCRYPTED_DATA: 'https://github.com/dotenvx/dotenvx/issues/467',
13
+ MISPAIRED_PRIVATE_KEY: 'https://github.com/dotenvx/dotenvx/issues/752',
14
+ MISSING_DIRECTORY: 'https://github.com/dotenvx/dotenvx/issues/758',
15
+ MISSING_ENV_FILE: 'https://github.com/dotenvx/dotenvx/issues/484',
16
+ MISSING_ENV_FILES: 'https://github.com/dotenvx/dotenvx/issues/760',
17
+ MISSING_KEY: 'https://github.com/dotenvx/dotenvx/issues/759',
18
+ MISSING_LOG_LEVEL: 'must be valid log level',
19
+ MISSING_PRIVATE_KEY: 'https://github.com/dotenvx/dotenvx/issues/464',
20
+ PRECOMMIT_HOOK_MODIFY_FAILED: 'try again or report error',
21
+ WRONG_PRIVATE_KEY: 'https://github.com/dotenvx/dotenvx/issues/466'
22
+ }
23
+
3
24
  class Errors {
4
25
  constructor (options = {}) {
5
26
  this.filepath = options.filepath
@@ -8,118 +29,252 @@ class Errors {
8
29
  this.key = options.key
9
30
  this.privateKey = options.privateKey
10
31
  this.privateKeyName = options.privateKeyName
32
+ this.publicKeyName = options.publicKeyName
11
33
  this.publicKey = options.publicKey
12
34
  this.publicKeyExisting = options.publicKeyExisting
13
35
  this.command = options.command
14
36
 
15
37
  this.message = options.message
38
+ this.code = options.code
39
+ this.help = options.help
40
+ this.debug = options.debug
41
+
42
+ this.convention = options.convention
43
+ this.directory = options.directory
44
+ this.exitCode = options.exitCode
45
+ this.level = options.level
46
+ this.color = options.color
47
+ this.error = options.error
16
48
  }
17
49
 
18
- missingEnvFile () {
19
- const code = 'MISSING_ENV_FILE'
20
- const message = `[${code}] missing ${this.envFilepath} file (${this.filepath})`
21
- const help = `[${code}] https://github.com/dotenvx/dotenvx/issues/484`
50
+ custom () {
51
+ const e = new Error(this.message)
52
+ if (this.code) e.code = this.code
53
+ if (this.help) e.help = this.help
54
+ if (this.code && !e.help) e.help = `fix: [${ISSUE_BY_CODE[this.code]}]`
55
+ e.messageWithHelp = `${e.message}. ${e.help}`
56
+ if (this.debug) e.debug = this.debug
57
+ return e
58
+ }
59
+
60
+ commandExitedWithCode () {
61
+ const code = 'COMMAND_EXITED_WITH_CODE'
62
+ const message = `[${code}] Command exited with exit code ${this.exitCode}`
63
+ const help = `fix: [${ISSUE_BY_CODE[code]}]`
22
64
 
23
65
  const e = new Error(message)
24
66
  e.code = code
25
67
  e.help = help
68
+ e.messageWithHelp = `${message}. ${help}`
26
69
  return e
27
70
  }
28
71
 
29
- missingKey () {
30
- const code = 'MISSING_KEY'
31
- const message = `[${code}] missing ${this.key} key`
72
+ commandSubstitutionFailed () {
73
+ const code = 'COMMAND_SUBSTITUTION_FAILED'
74
+ const message = `[${code}] could not eval ${this.key} containing command '${this.command}': ${this.message}`
75
+ const help = `fix: [${ISSUE_BY_CODE[code]}]`
32
76
 
33
77
  const e = new Error(message)
34
78
  e.code = code
79
+ e.help = help
80
+ e.messageWithHelp = `${message}. ${help}`
35
81
  return e
36
82
  }
37
83
 
38
- missingPrivateKey () {
39
- const code = 'MISSING_PRIVATE_KEY'
40
- const message = `[${code}] could not decrypt ${this.key} using private key '${this.privateKeyName}=${truncate(this.privateKey)}'`
41
- const help = `[${code}] https://github.com/dotenvx/dotenvx/issues/464`
84
+ decryptionFailed () {
85
+ const code = 'DECRYPTION_FAILED'
86
+ const message = `[${code}] ${this.message}`
87
+ const help = `fix: [${ISSUE_BY_CODE[code]}]`
42
88
 
43
89
  const e = new Error(message)
44
90
  e.code = code
45
91
  e.help = help
92
+ e.messageWithHelp = `${message}. ${help}`
93
+ return e
94
+ }
95
+
96
+ dangerousDependencyHoist () {
97
+ const code = 'DANGEROUS_DEPENDENCY_HOIST'
98
+ const message = `[${code}] your environment has hoisted an incompatible version of a dotenvx dependency: ${this.message}`
99
+ const help = `fix: [${ISSUE_BY_CODE[code]}]`
100
+
101
+ const e = new Error(message)
102
+ e.code = code
103
+ e.help = help
104
+ e.messageWithHelp = `${message}. ${help}`
105
+ return e
106
+ }
107
+
108
+ invalidColor () {
109
+ const code = 'INVALID_COLOR'
110
+ const message = `[${code}] Invalid color ${this.color}`
111
+ const help = `fix: [${ISSUE_BY_CODE[code]}]`
112
+
113
+ const e = new Error(message)
114
+ e.code = code
115
+ e.help = help
116
+ e.messageWithHelp = `${message}. ${help}`
117
+ return e
118
+ }
119
+
120
+ invalidConvention () {
121
+ const code = 'INVALID_CONVENTION'
122
+ const message = `[${code}] invalid convention (${this.convention})`
123
+ const help = `fix: [${ISSUE_BY_CODE[code]}]`
124
+
125
+ const e = new Error(message)
126
+ e.code = code
127
+ e.help = help
128
+ e.messageWithHelp = `${message}. ${help}`
46
129
  return e
47
130
  }
48
131
 
49
132
  invalidPrivateKey () {
50
133
  const code = 'INVALID_PRIVATE_KEY'
51
134
  const message = `[${code}] could not decrypt ${this.key} using private key '${this.privateKeyName}=${truncate(this.privateKey)}'`
52
- const help = `[${code}] https://github.com/dotenvx/dotenvx/issues/465`
135
+ const help = `fix: [${ISSUE_BY_CODE[code]}]`
53
136
 
54
137
  const e = new Error(message)
55
138
  e.code = code
56
139
  e.help = help
140
+ e.messageWithHelp = `${message}. ${help}`
141
+ return e
142
+ }
143
+
144
+ invalidPublicKey () {
145
+ const code = 'INVALID_PUBLIC_KEY'
146
+ const message = `[${code}] could not encrypt using public key '${this.publicKeyName}=${truncate(this.publicKey)}'`
147
+ const help = `fix: [${ISSUE_BY_CODE[code]}]`
148
+
149
+ const e = new Error(message)
150
+ e.code = code
151
+ e.help = help
152
+ e.messageWithHelp = `${message}. ${help}`
153
+ return e
154
+ }
155
+
156
+ malformedEncryptedData () {
157
+ const code = 'MALFORMED_ENCRYPTED_DATA'
158
+ const message = `[${code}] could not decrypt ${this.key} because encrypted data appears malformed`
159
+ const help = `fix: [${ISSUE_BY_CODE[code]}]`
160
+
161
+ const e = new Error(message)
162
+ e.code = code
163
+ e.help = help
164
+ e.messageWithHelp = `${message}. ${help}`
57
165
  return e
58
166
  }
59
167
 
60
168
  mispairedPrivateKey () {
61
169
  const code = 'MISPAIRED_PRIVATE_KEY'
62
170
  const message = `[${code}] private key's derived public key (${truncate(this.publicKey)}) does not match the existing public key (${truncate(this.publicKeyExisting)})`
63
- const help = `[${code}] https://github.com/dotenvx/dotenvx/issues/752`
171
+ const help = `fix: [${ISSUE_BY_CODE[code]}]`
64
172
 
65
173
  const e = new Error(message)
66
174
  e.code = code
67
175
  e.help = help
176
+ e.messageWithHelp = `${message}. ${help}`
68
177
  return e
69
178
  }
70
179
 
71
- looksWrongPrivateKey () {
72
- const code = 'WRONG_PRIVATE_KEY'
73
- const message = `[${code}] could not decrypt ${this.key} using private key '${this.privateKeyName}=${truncate(this.privateKey)}'`
74
- const help = `[${code}] https://github.com/dotenvx/dotenvx/issues/466`
180
+ missingDirectory () {
181
+ const code = 'MISSING_DIRECTORY'
182
+ const message = `[${code}] missing directory (${this.directory})`
183
+ const help = `fix: [${ISSUE_BY_CODE[code]}]`
75
184
 
76
185
  const e = new Error(message)
77
186
  e.code = code
78
187
  e.help = help
188
+ e.messageWithHelp = `${message}. ${help}`
79
189
  return e
80
190
  }
81
191
 
82
- malformedEncryptedData () {
83
- const code = 'MALFORMED_ENCRYPTED_DATA'
84
- const message = `[${code}] could not decrypt ${this.key} because encrypted data appears malformed`
85
- const help = `[${code}] https://github.com/dotenvx/dotenvx/issues/467`
192
+ missingEnvFile () {
193
+ const code = 'MISSING_ENV_FILE'
194
+ const envFilepath = this.envFilepath || '.env'
195
+ const message = `[${code}] missing file (${envFilepath})`
196
+ const help = `fix: [${ISSUE_BY_CODE[code]}]`
86
197
 
87
198
  const e = new Error(message)
88
199
  e.code = code
89
200
  e.help = help
201
+ e.messageWithHelp = `${message}. ${help}`
90
202
  return e
91
203
  }
92
204
 
93
- decryptionFailed () {
94
- const code = 'DECRYPTION_FAILED'
95
- const message = this.message
205
+ missingEnvFiles () {
206
+ const code = 'MISSING_ENV_FILES'
207
+ const message = `[${code}] no .env* files found`
208
+ const help = `fix: [${ISSUE_BY_CODE[code]}]`
96
209
 
97
210
  const e = new Error(message)
98
211
  e.code = code
212
+ e.help = help
213
+ e.messageWithHelp = `${message}. ${help}`
99
214
  return e
100
215
  }
101
216
 
102
- commandSubstitutionFailed () {
103
- const code = 'COMMAND_SUBSTITUTION_FAILED'
104
- const message = `[${code}] could not eval ${this.key} containing command '${this.command}': ${this.message}`
105
- const help = `[${code}] https://github.com/dotenvx/dotenvx/issues/532`
217
+ missingKey () {
218
+ const code = 'MISSING_KEY'
219
+ const message = `[${code}] missing key (${this.key})`
220
+ const help = `fix: [${ISSUE_BY_CODE[code]}]`
106
221
 
107
222
  const e = new Error(message)
108
223
  e.code = code
109
224
  e.help = help
225
+ e.messageWithHelp = `${message}. ${help}`
110
226
  return e
111
227
  }
112
228
 
113
- dangerousDependencyHoist () {
114
- const code = 'DANGEROUS_DEPENDENCY_HOIST'
115
- const message = `[${code}] your environment has hoisted an incompatible version of a dotenvx dependency: ${this.message}`
116
- const help = `[${code}] https://github.com/dotenvx/dotenvx/issues/622`
229
+ missingLogLevel () {
230
+ const code = 'MISSING_LOG_LEVEL'
231
+ const message = `[${code}] missing log level '${this.level}'. implement in logger`
232
+ const help = `fix: [${ISSUE_BY_CODE[code]}]`
233
+
234
+ const e = new Error(message)
235
+ e.code = code
236
+ e.help = help
237
+ e.messageWithHelp = `${message}. ${help}`
238
+ return e
239
+ }
240
+
241
+ missingPrivateKey () {
242
+ const code = 'MISSING_PRIVATE_KEY'
243
+ const message = `[${code}] could not decrypt ${this.key} using private key '${this.privateKeyName}=${truncate(this.privateKey)}'`
244
+ const help = `fix: [${ISSUE_BY_CODE[code]}]`
245
+
246
+ const e = new Error(message)
247
+ e.code = code
248
+ e.help = help
249
+ e.messageWithHelp = `${message}. ${help}`
250
+ return e
251
+ }
252
+
253
+ precommitHookModifyFailed () {
254
+ const code = 'PRECOMMIT_HOOK_MODIFY_FAILED'
255
+ const message = `[${code}] failed to modify pre-commit hook: ${this.error.message}`
256
+ const help = `fix: [${ISSUE_BY_CODE[code]}]`
117
257
 
118
258
  const e = new Error(message)
119
259
  e.code = code
120
260
  e.help = help
261
+ e.messageWithHelp = `${message}. ${help}`
262
+ return e
263
+ }
264
+
265
+ wrongPrivateKey () {
266
+ const code = 'WRONG_PRIVATE_KEY'
267
+ const message = `[${code}] could not decrypt ${this.key} using private key '${this.privateKeyName}=${truncate(this.privateKey)}'`
268
+ const help = `fix: [${ISSUE_BY_CODE[code]}]`
269
+
270
+ const e = new Error(message)
271
+ e.code = code
272
+ e.help = help
273
+ e.messageWithHelp = `${message}. ${help}`
121
274
  return e
122
275
  }
123
276
  }
124
277
 
278
+ Errors.ISSUE_BY_CODE = ISSUE_BY_CODE
279
+
125
280
  module.exports = Errors
@@ -2,6 +2,7 @@ const path = require('path')
2
2
  const which = require('which')
3
3
  const execute = require('./../../lib/helpers/execute')
4
4
  const { logger } = require('./../../shared/logger')
5
+ const Errors = require('./errors')
5
6
 
6
7
  async function executeCommand (commandArgs, env) {
7
8
  const signals = [
@@ -91,7 +92,7 @@ async function executeCommand (commandArgs, env) {
91
92
 
92
93
  if (exitCode !== 0) {
93
94
  logger.debug(`received exitCode ${exitCode}`)
94
- throw new Error(`Command exited with exit code ${exitCode}`)
95
+ throw new Errors({ exitCode }).commandExitedWithCode()
95
96
  }
96
97
  } catch (error) {
97
98
  // no color on these errors as they can be standard errors for things like jest exiting with exitCode 1 for a single failed test.