@dotenvx/dotenvx 0.30.0 → 0.30.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
@@ -686,6 +686,13 @@ $ dotenvx hub push
686
686
  * [`dotenvx gitignore`](https://dotenvx.com/docs/features/gitignore) – gitignore your `.env` files
687
687
  * [`dotenvx prebuild`](https://dotenvx.com/docs/features/prebuild) – prevent `.env` files from being built into your docker container
688
688
  * [`dotenvx precommit`](https://dotenvx.com/docs/features/precommit) – prevent `.env` files from being committed to code
689
+ * [`dotenvx scan`](https://dotenvx.com/docs/features/scan) – scan for leaked secrets in code
690
+
691
+ > Convenience
692
+
693
+ * [`dotenvx get`](https://dotenvx.com/docs/features/get) – return a single environment variable
694
+ * [`dotenvx ls`](https://dotenvx.com/docs/features/ls) – list all .env files in your repo
695
+ * [`dotenvx settings`](https://dotenvx.com/docs/features/settings) – print current dotenvx settings
689
696
 
690
697
   
691
698
 
package/package.json CHANGED
@@ -1,5 +1,5 @@
1
1
  {
2
- "version": "0.30.0",
2
+ "version": "0.30.2",
3
3
  "name": "@dotenvx/dotenvx",
4
4
  "description": "a better dotenv–from the creator of `dotenv`",
5
5
  "author": "@motdotla",
@@ -2,9 +2,11 @@ const fs = require('fs')
2
2
  const dotenv = require('dotenv')
3
3
 
4
4
  const logger = require('./../../shared/logger')
5
- const helpers = require('./../helpers')
6
5
  const createSpinner = require('./../../shared/createSpinner')
6
+
7
7
  const libDecrypt = require('./../../lib/helpers/decrypt')
8
+ const sleep = require('./../../lib/helpers/sleep')
9
+ const resolvePath = require('./../../lib/helpers/resolvePath')
8
10
 
9
11
  const spinner = createSpinner('decrypting')
10
12
 
@@ -13,13 +15,13 @@ const ENCODING = 'utf8'
13
15
 
14
16
  async function decrypt () {
15
17
  spinner.start()
16
- await helpers.sleep(500) // better dx
18
+ await sleep(500) // better dx
17
19
 
18
20
  const options = this.opts()
19
21
  logger.debug(`options: ${JSON.stringify(options)}`)
20
22
 
21
- const vaultFilepath = helpers.resolvePath('.env.vault')
22
- const keysFilepath = helpers.resolvePath('.env.keys')
23
+ const vaultFilepath = resolvePath('.env.vault')
24
+ const keysFilepath = resolvePath('.env.keys')
23
25
  const changedEnvFilenames = new Set()
24
26
  const unchangedEnvFilenames = new Set()
25
27
 
@@ -3,13 +3,15 @@ const path = require('path')
3
3
 
4
4
  const main = require('./../../lib/main')
5
5
  const logger = require('./../../shared/logger')
6
- const helpers = require('./../helpers')
7
6
  const createSpinner = require('./../../shared/createSpinner')
7
+ const sleep = require('./../../lib/helpers/sleep')
8
+ const pluralize = require('./../../lib/helpers/pluralize')
9
+
8
10
  const spinner = createSpinner('encrypting')
9
11
 
10
12
  async function encrypt (directory) {
11
13
  spinner.start()
12
- await helpers.sleep(500) // better dx
14
+ await sleep(500) // better dx
13
15
 
14
16
  logger.debug(`directory: ${directory}`)
15
17
 
@@ -55,7 +57,7 @@ async function encrypt (directory) {
55
57
  }
56
58
 
57
59
  if (addedKeys.length > 0) {
58
- spinner.succeed(`${helpers.pluralize('key', addedKeys.length)} added to .env.keys (${addedKeys})`)
60
+ spinner.succeed(`${pluralize('key', addedKeys.length)} added to .env.keys (${addedKeys})`)
59
61
  logger.help2('ℹ push .env.keys up to hub: [dotenvx hub push]')
60
62
  }
61
63
 
@@ -1,16 +1,17 @@
1
1
  const fs = require('fs')
2
2
  const main = require('./../../lib/main')
3
- const helpers = require('./../helpers')
4
3
  const logger = require('./../../shared/logger')
5
4
  const createSpinner = require('./../../shared/createSpinner')
6
5
 
6
+ const sleep = require('./../../lib/helpers/sleep')
7
+
7
8
  const spinner = createSpinner('generating')
8
9
 
9
10
  const ENCODING = 'utf8'
10
11
 
11
12
  async function genexample (directory) {
12
13
  spinner.start()
13
- await helpers.sleep(500) // better dx
14
+ await sleep(500) // better dx
14
15
 
15
16
  logger.debug(`directory: ${directory}`)
16
17
 
@@ -3,29 +3,59 @@ const confirm = require('@inquirer/confirm').default
3
3
 
4
4
  const createSpinner = require('./../../../shared/createSpinner')
5
5
  const logger = require('./../../../shared/logger')
6
- const helpers = require('./../../helpers')
6
+
7
+ const isGitRepo = require('./../../../lib/helpers/isGitRepo')
8
+ const isGithub = require('./../../../lib/helpers/isGithub')
9
+ const gitUrl = require('./../../../lib/helpers/gitUrl')
10
+ const gitRoot = require('./../../../lib/helpers/gitRoot')
11
+ const extractUsernameName = require('./../../../lib/helpers/extractUsernameName')
12
+ const sleep = require('./../../../lib/helpers/sleep')
13
+
14
+ const spinner = createSpinner('opening')
7
15
 
8
16
  async function open () {
17
+ // debug opts
9
18
  const options = this.opts()
10
19
  logger.debug(`options: ${JSON.stringify(options)}`)
11
20
 
12
- const hostname = options.hostname
13
- const remoteOriginUrl = helpers.getRemoteOriginUrl()
14
- const usernameName = helpers.extractUsernameName(remoteOriginUrl)
21
+ // must be a git repo
22
+ if (!isGitRepo()) {
23
+ spinner.fail('oops, must be a git repository')
24
+ logger.help('? create one with [git init .]')
25
+ process.exit(1)
26
+ }
27
+ // must be a git root
28
+ const gitroot = gitRoot()
29
+ if (!gitroot) {
30
+ spinner.fail('oops, could not determine git repository\'s root')
31
+ logger.help('? create one with [git init .]')
32
+ process.exit(1)
33
+ }
34
+ // must have a remote origin url
35
+ const giturl = gitUrl()
36
+ if (!giturl) {
37
+ spinner.fail('oops, must have a remote origin (git remote -v)')
38
+ logger.help('? create it at [github.com/new] and then run [git remote add origin git@github.com:username/repository.git]')
39
+ process.exit(1)
40
+ }
41
+ // must be a github remote
42
+ if (!isGithub(giturl)) {
43
+ spinner.fail('oops, must be a github.com remote origin (git remote -v)')
44
+ logger.help('? create it at [github.com/new] and then run [git remote add origin git@github.com:username/repository.git]')
45
+ logger.help2('ℹ need support for other origins? [please tell us](https://github.com/dotenvx/dotenvx/issues)')
46
+ process.exit(1)
47
+ }
15
48
 
16
- const openUrl = `${hostname}/gh/${usernameName}`
49
+ const usernameName = extractUsernameName(giturl)
50
+ const openUrl = `${options.hostname}/gh/${usernameName}`
17
51
 
18
52
  // optionally allow user to open browser
19
53
  const answer = await confirm({ message: `press Enter to open [${openUrl}]...` })
20
54
 
21
55
  if (answer) {
22
- const spinner = createSpinner('opening')
23
56
  spinner.start()
24
-
25
- await helpers.sleep(500) // better dx
26
-
57
+ await sleep(500) // better dx
27
58
  await openBrowser(openUrl)
28
-
29
59
  spinner.succeed(`opened [${usernameName}]`)
30
60
  }
31
61
  }
@@ -4,13 +4,14 @@ const { request } = require('undici')
4
4
 
5
5
  const store = require('./../../../shared/store')
6
6
  const logger = require('./../../../shared/logger')
7
- const helpers = require('./../../helpers')
8
7
  const createSpinner = require('./../../../shared/createSpinner')
9
8
 
10
9
  const isGitRepo = require('./../../../lib/helpers/isGitRepo')
11
10
  const isGithub = require('./../../../lib/helpers/isGithub')
12
11
  const gitUrl = require('./../../../lib/helpers/gitUrl')
13
12
  const gitRoot = require('./../../../lib/helpers/gitRoot')
13
+ const extractUsernameName = require('./../../../lib/helpers/extractUsernameName')
14
+ const sleep = require('./../../../lib/helpers/sleep')
14
15
 
15
16
  const spinner = createSpinner('pulling')
16
17
 
@@ -20,7 +21,7 @@ const ENCODING = 'utf8'
20
21
  // Create a simple-git instance for the current directory
21
22
  async function pull (directory) {
22
23
  spinner.start()
23
- await helpers.sleep(500) // better dx
24
+ await sleep(500) // better dx
24
25
 
25
26
  // debug args
26
27
  logger.debug(`directory: ${directory}`)
@@ -61,7 +62,7 @@ async function pull (directory) {
61
62
  const hostname = options.hostname
62
63
  const pullUrl = `${hostname}/v1/pull`
63
64
  const oauthToken = store.getToken()
64
- const usernameName = helpers.extractUsernameName(giturl)
65
+ const usernameName = extractUsernameName(giturl)
65
66
  const relativeEnvKeysFilepath = path.relative(gitroot, path.join(process.cwd(), directory, '.env.keys')).replace(/\\/g, '/') // smartly determine path/to/.env.keys file from repository root - where user is cd-ed inside a folder or at repo root
66
67
 
67
68
  try {
@@ -83,7 +84,7 @@ async function pull (directory) {
83
84
  logger.http(responseData)
84
85
  spinner.fail(responseData.error.message)
85
86
  if (response.statusCode === 404) {
86
- logger.help(`? try visiting [${hostname}gh/${usernameName}] in your browser`)
87
+ logger.help(`? try visiting [${hostname}/gh/${usernameName}] in your browser`)
87
88
  }
88
89
  process.exit(1)
89
90
  }
@@ -4,13 +4,14 @@ const { request } = require('undici')
4
4
 
5
5
  const store = require('./../../../shared/store')
6
6
  const logger = require('./../../../shared/logger')
7
- const helpers = require('./../../helpers')
8
7
  const createSpinner = require('./../../../shared/createSpinner')
9
8
 
10
9
  const isGitRepo = require('./../../../lib/helpers/isGitRepo')
11
10
  const isGithub = require('./../../../lib/helpers/isGithub')
12
11
  const gitUrl = require('./../../../lib/helpers/gitUrl')
13
12
  const gitRoot = require('./../../../lib/helpers/gitRoot')
13
+ const extractUsernameName = require('./../../../lib/helpers/extractUsernameName')
14
+ const sleep = require('./../../../lib/helpers/sleep')
14
15
 
15
16
  const spinner = createSpinner('pushing')
16
17
 
@@ -20,7 +21,7 @@ const ENCODING = 'utf8'
20
21
  // Create a simple-git instance for the current directory
21
22
  async function push (directory) {
22
23
  spinner.start()
23
- await helpers.sleep(500) // better dx
24
+ await sleep(500) // better dx
24
25
 
25
26
  // debug args
26
27
  logger.debug(`directory: ${directory}`)
@@ -69,7 +70,7 @@ async function push (directory) {
69
70
  const pushUrl = `${hostname}/v1/push`
70
71
  const oauthToken = store.getToken()
71
72
  const dotenvKeysContent = fs.readFileSync(envKeysFilepath, ENCODING)
72
- const usernameName = helpers.extractUsernameName(giturl)
73
+ const usernameName = extractUsernameName(giturl)
73
74
  const relativeEnvKeysFilepath = path.relative(gitroot, path.join(process.cwd(), directory, '.env.keys')).replace(/\\/g, '/') // smartly determine path/to/.env.keys file from repository root - where user is cd-ed inside a folder or at repo root
74
75
 
75
76
  try {
@@ -92,7 +93,7 @@ async function push (directory) {
92
93
  logger.http(responseData)
93
94
  spinner.fail(responseData.error.message)
94
95
  if (response.statusCode === 404) {
95
- logger.help(`? try visiting [${hostname}gh/${usernameName}] in your browser`)
96
+ logger.help(`? try visiting [${hostname}/gh/${usernameName}] in your browser`)
96
97
  }
97
98
  process.exit(1)
98
99
  }
@@ -3,7 +3,7 @@ const fs = require('fs')
3
3
  const ignore = require('ignore')
4
4
 
5
5
  const logger = require('./../../shared/logger')
6
- const helpers = require('./../helpers')
6
+ const pluralize = require('./../../lib/helpers/pluralize')
7
7
 
8
8
  function prebuild () {
9
9
  const options = this.opts()
@@ -55,7 +55,7 @@ function prebuild () {
55
55
 
56
56
  // 3. outpout success
57
57
  if (warningCount > 0) {
58
- logger.successvpb(`success (with ${helpers.pluralize('warning', warningCount)})`)
58
+ logger.successvpb(`success (with ${pluralize('warning', warningCount)})`)
59
59
  } else {
60
60
  logger.successvpb('success')
61
61
  }
@@ -5,7 +5,7 @@ const store = require('./../../shared/store')
5
5
  const hub = new Command('hub')
6
6
 
7
7
  hub
8
- .description('Interact with the hub')
8
+ .description('interact with dotenvx hub')
9
9
 
10
10
  hub
11
11
  .command('login')
@@ -12,7 +12,7 @@ const packageJson = require('./../lib/helpers/packageJson')
12
12
  const notice = new UpdateNotice()
13
13
  notice.check()
14
14
  if (notice.update) {
15
- logger.warn(`Update available ${notice.packageVersion} → ${notice.latestVersion} [see changelog](dotenvx.com/changelog)`)
15
+ logger.warn(`Update available ${notice.packageVersion} → ${notice.latestVersion}\nchangelog: https://dotenvx.com/changelog`)
16
16
  }
17
17
 
18
18
  // for use with run
@@ -75,6 +75,23 @@ program.command('run')
75
75
  runAction.apply(this, args)
76
76
  })
77
77
 
78
+ // dotenvx get
79
+ const getAction = require('./actions/get')
80
+ program.command('get')
81
+ .description('return a single environment variable')
82
+ .argument('[key]', 'environment variable name')
83
+ .option('-e, --env <strings...>', 'environment variable(s) set as string (example: "HELLO=World")', collectEnvs('env'), [])
84
+ .option('-f, --env-file <paths...>', 'path(s) to your env file(s)', collectEnvs('envFile'), [])
85
+ .option('-fv, --env-vault-file <paths...>', 'path(s) to your .env.vault file(s)', collectEnvs('envVaultFile'), [])
86
+ .option('-o, --overload', 'override existing env variables')
87
+ .option('-a, --all', 'include all machine envs as well')
88
+ .option('-pp, --pretty-print', 'pretty print output')
89
+ .action(function (...args) {
90
+ this.envs = envs
91
+
92
+ getAction.apply(this, args)
93
+ })
94
+
78
95
  // dotenvx encrypt
79
96
  program.command('encrypt')
80
97
  .description('encrypt .env.* to .env.vault')
@@ -88,31 +105,31 @@ program.command('decrypt')
88
105
  .description('decrypt .env.vault to .env*')
89
106
  .action(require('./actions/decrypt'))
90
107
 
108
+ // dotenvx genexample
109
+ program.command('genexample')
110
+ .description('generate .env.example')
111
+ .argument('[directory]', 'directory to generate from', '.')
112
+ .option('-f, --env-file <paths...>', 'path(s) to your env file(s)', '.env')
113
+ .action(require('./actions/genexample'))
114
+
91
115
  // dotenvx gitignore
92
116
  program.command('gitignore')
93
117
  .description('append to .gitignore file (and if existing, .dockerignore, .npmignore, and .vercelignore)')
94
118
  .addHelpText('after', examples.gitignore)
95
119
  .action(require('./actions/gitignore'))
96
120
 
97
- // dotenvx precommit
98
- program.command('precommit')
99
- .description('prevent committing .env files to code')
100
- .addHelpText('after', examples.precommit)
101
- .option('-i, --install', 'install to .git/hooks/pre-commit')
102
- .action(require('./actions/precommit'))
103
-
104
121
  // dotenvx prebuild
105
122
  program.command('prebuild')
106
123
  .description('prevent including .env files in docker builds')
107
124
  .addHelpText('after', examples.prebuild)
108
125
  .action(require('./actions/prebuild'))
109
126
 
110
- // dotenvx genexample
111
- program.command('genexample')
112
- .description('generate .env.example')
113
- .argument('[directory]', 'directory to generate from', '.')
114
- .option('-f, --env-file <paths...>', 'path(s) to your env file(s)', '.env')
115
- .action(require('./actions/genexample'))
127
+ // dotenvx precommit
128
+ program.command('precommit')
129
+ .description('prevent committing .env files to code')
130
+ .addHelpText('after', examples.precommit)
131
+ .option('-i, --install', 'install to .git/hooks/pre-commit')
132
+ .action(require('./actions/precommit'))
116
133
 
117
134
  // dotenvx scan
118
135
  program.command('scan')
@@ -126,23 +143,6 @@ program.command('ls')
126
143
  .option('-f, --env-file <filenames...>', 'path(s) to your env file(s)', '.env*')
127
144
  .action(require('./actions/ls'))
128
145
 
129
- // dotenvx get
130
- const getAction = require('./actions/get')
131
- program.command('get')
132
- .description('return environment variable(s)')
133
- .argument('[key]', 'environment variable name')
134
- .option('-e, --env <strings...>', 'environment variable(s) set as string (example: "HELLO=World")', collectEnvs('env'), [])
135
- .option('-f, --env-file <paths...>', 'path(s) to your env file(s)', collectEnvs('envFile'), [])
136
- .option('-fv, --env-vault-file <paths...>', 'path(s) to your .env.vault file(s)', collectEnvs('envVaultFile'), [])
137
- .option('-o, --overload', 'override existing env variables')
138
- .option('-a, --all', 'include all machine envs as well')
139
- .option('-pp, --pretty-print', 'pretty print output')
140
- .action(function (...args) {
141
- this.envs = envs
142
-
143
- getAction.apply(this, args)
144
- })
145
-
146
146
  // dotenvx settings
147
147
  program.command('settings')
148
148
  .description('print current dotenvx settings')
@@ -0,0 +1,10 @@
1
+ function extractUsernameName (url) {
2
+ // Removing the protocol part and splitting by slashes and colons
3
+ // Removing the protocol part and .git suffix, then splitting by slashes and colons
4
+ const parts = url.replace(/(^\w+:|^)\/\//, '').replace(/\.git$/, '').split(/[/:]/)
5
+
6
+ // Extract the 'username/repository' part
7
+ return parts.slice(-2).join('/')
8
+ }
9
+
10
+ module.exports = extractUsernameName
@@ -1,10 +1,11 @@
1
- const { execSync } = require('child_process')
1
+ const execa = require('execa')
2
2
 
3
3
  function gitRoot () {
4
4
  try {
5
5
  // Redirect standard error to null to suppress Git error messages
6
- const root = execSync('git rev-parse --show-toplevel 2> /dev/null').toString().trim()
7
- return root
6
+ const raw = execa.sync('git', ['rev-parse', '--show-toplevel'], { stderr: 'ignore' })
7
+ const result = raw.stdout.toString().trim()
8
+ return result
8
9
  } catch (_error) {
9
10
  return null
10
11
  }
@@ -1,10 +1,10 @@
1
- const { execSync } = require('child_process')
1
+ const execa = require('execa')
2
2
 
3
3
  function gitUrl () {
4
4
  try {
5
- // Redirect standard error to null to suppress Git error messages
6
- const url = execSync('git remote get-url origin 2> /dev/null').toString().trim()
7
- return url
5
+ const raw = execa.sync('git', ['remote', 'get-url', 'origin'])
6
+
7
+ return raw.stdout.toString().trim()
8
8
  } catch (_error) {
9
9
  return null
10
10
  }
@@ -1,9 +1,9 @@
1
- const { execSync } = require('child_process')
1
+ const execa = require('execa')
2
2
 
3
3
  function isGitRepo () {
4
4
  try {
5
- // Redirect standard error to null to suppress Git error messages
6
- const result = execSync('git rev-parse --is-inside-work-tree 2> /dev/null').toString().trim()
5
+ const raw = execa.sync('git', ['rev-parse', '--is-inside-work-tree'], { stderr: 'ignore' })
6
+ const result = raw.stdout.toString().trim()
7
7
  return result === 'true'
8
8
  } catch (_error) {
9
9
  return false
@@ -0,0 +1,8 @@
1
+ const path = require('path')
2
+
3
+ // resolve path based on current running process location
4
+ function resolvePath (filepath) {
5
+ return path.resolve(process.cwd(), filepath)
6
+ }
7
+
8
+ module.exports = resolvePath
@@ -0,0 +1,5 @@
1
+ function sleep (ms) {
2
+ return new Promise(resolve => setTimeout(resolve, ms))
3
+ }
4
+
5
+ module.exports = sleep
@@ -1,46 +0,0 @@
1
- const path = require('path')
2
- const childProcess = require('child_process')
3
-
4
- const sleep = function (ms) {
5
- return new Promise(resolve => setTimeout(resolve, ms))
6
- }
7
-
8
- // resolve path based on current running process location
9
- const resolvePath = function (filepath) {
10
- return path.resolve(process.cwd(), filepath)
11
- }
12
-
13
- const pluralize = function (word, count) {
14
- // simple pluralization: add 's' at the end
15
- if (count === 0 || count > 1) {
16
- return word + 's'
17
- } else {
18
- return word
19
- }
20
- }
21
-
22
- const getRemoteOriginUrl = function () {
23
- try {
24
- const url = childProcess.execSync('git remote get-url origin 2> /dev/null').toString().trim()
25
- return url
26
- } catch (_error) {
27
- return null
28
- }
29
- }
30
-
31
- const extractUsernameName = function (url) {
32
- // Removing the protocol part and splitting by slashes and colons
33
- // Removing the protocol part and .git suffix, then splitting by slashes and colons
34
- const parts = url.replace(/(^\w+:|^)\/\//, '').replace(/\.git$/, '').split(/[/:]/)
35
-
36
- // Extract the 'username/repository' part
37
- return parts.slice(-2).join('/')
38
- }
39
-
40
- module.exports = {
41
- sleep,
42
- resolvePath,
43
- pluralize,
44
- getRemoteOriginUrl,
45
- extractUsernameName
46
- }