@dotenvx/dotenvx 1.4.0 → 1.5.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 +15 -1
- package/README.md +12 -0
- package/package.json +2 -3
- package/src/cli/actions/ext/genexample.js +1 -1
- package/src/cli/actions/ext/vault/decrypt.js +1 -1
- package/src/cli/actions/ext/vault/encrypt.js +1 -1
- package/src/cli/commands/ext.js +42 -1
- package/src/cli/dotenvx.js +4 -1
- package/src/cli/examples.js +17 -1
- package/src/shared/confirm.js +12 -0
- package/src/shared/createSpinner.js +84 -4
- package/src/cli/actions/ext/hub/login.js +0 -126
- package/src/cli/actions/ext/hub/logout.js +0 -43
- package/src/cli/actions/ext/hub/open.js +0 -63
- package/src/cli/actions/ext/hub/pull.js +0 -105
- package/src/cli/actions/ext/hub/push.js +0 -112
- package/src/cli/actions/ext/hub/status.js +0 -8
- package/src/cli/actions/ext/hub/token.js +0 -9
- package/src/cli/commands/ext/hub.js +0 -89
package/CHANGELOG.md
CHANGED
|
@@ -2,7 +2,21 @@
|
|
|
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.
|
|
5
|
+
## [Unreleased](https://github.com/dotenvx/dotenvx/compare/v1.5.0...main)
|
|
6
|
+
|
|
7
|
+
## 1.5.0
|
|
8
|
+
|
|
9
|
+
### Added
|
|
10
|
+
|
|
11
|
+
* add help text for dashed values on `set`. example: `dotenvx set KEY -- "- + * ÷"` ([#293](https://github.com/dotenvx/dotenvx/pull/293))
|
|
12
|
+
|
|
13
|
+
### Changed
|
|
14
|
+
|
|
15
|
+
* replace `@inquirer/confirm` and `ora` ([#285](https://github.com/dotenvx/dotenvx/pull/285))
|
|
16
|
+
|
|
17
|
+
### Removed
|
|
18
|
+
|
|
19
|
+
* remove `dotenvx ext hub`, replace with [dotenvx-ext-hub](https://github.com/dotenvx/dotenvx-ext-hub) (install there to continue using hub) ([#291](https://github.com/dotenvx/dotenvx/pull/291))
|
|
6
20
|
|
|
7
21
|
## 1.4.0
|
|
8
22
|
|
package/README.md
CHANGED
|
@@ -1071,6 +1071,18 @@ More examples
|
|
|
1071
1071
|
set HELLO with encryption (.env.ci)
|
|
1072
1072
|
```
|
|
1073
1073
|
|
|
1074
|
+
</details>
|
|
1075
|
+
* <details><summary>`set KEY -- "- + * ÷"`</summary><br>
|
|
1076
|
+
|
|
1077
|
+
If your value starts with a dash (`-`), then place two dashes instructing the cli that there are no more flag arguments.
|
|
1078
|
+
|
|
1079
|
+
```sh
|
|
1080
|
+
$ touch .env.ci
|
|
1081
|
+
|
|
1082
|
+
$ dotenvx set HELLO -f .env.ci -- "- + * ÷"
|
|
1083
|
+
set HELLO with encryption (.env.ci)
|
|
1084
|
+
```
|
|
1085
|
+
|
|
1074
1086
|
</details>
|
|
1075
1087
|
* <details><summary>`set KEY value --plain`</summary><br>
|
|
1076
1088
|
|
package/package.json
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
{
|
|
2
|
-
"version": "1.
|
|
2
|
+
"version": "1.5.0",
|
|
3
3
|
"name": "@dotenvx/dotenvx",
|
|
4
4
|
"description": "a better dotenv–from the creator of `dotenv`",
|
|
5
5
|
"author": "@motdotla",
|
|
@@ -31,7 +31,7 @@
|
|
|
31
31
|
},
|
|
32
32
|
"funding": "https://dotenvx.com",
|
|
33
33
|
"dependencies": {
|
|
34
|
-
"@
|
|
34
|
+
"@clack/core": "^0.3.4",
|
|
35
35
|
"arch": "^2.1.1",
|
|
36
36
|
"chalk": "^4.1.2",
|
|
37
37
|
"commander": "^11.1.0",
|
|
@@ -45,7 +45,6 @@
|
|
|
45
45
|
"is-wsl": "^2.1.1",
|
|
46
46
|
"object-treeify": "1.1.33",
|
|
47
47
|
"open": "^8.4.2",
|
|
48
|
-
"ora": "^5.4.1",
|
|
49
48
|
"picomatch": "^3.0.1",
|
|
50
49
|
"undici": "^5.28.3",
|
|
51
50
|
"which": "^4.0.0",
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
const fs = require('fs')
|
|
2
2
|
const main = require('./../../../lib/main')
|
|
3
3
|
const { logger } = require('./../../../shared/logger')
|
|
4
|
-
const createSpinner = require('./../../../shared/createSpinner')
|
|
4
|
+
const { createSpinner } = require('./../../../shared/createSpinner')
|
|
5
5
|
|
|
6
6
|
const sleep = require('./../../../lib/helpers/sleep')
|
|
7
7
|
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
const fs = require('fs')
|
|
2
2
|
|
|
3
3
|
const { logger } = require('./../../../../shared/logger')
|
|
4
|
-
const createSpinner = require('./../../../../shared/createSpinner')
|
|
4
|
+
const { createSpinner } = require('./../../../../shared/createSpinner')
|
|
5
5
|
const sleep = require('./../../../../lib/helpers/sleep')
|
|
6
6
|
|
|
7
7
|
const Decrypt = require('./../../../../lib/services/decrypt')
|
|
@@ -3,7 +3,7 @@ const path = require('path')
|
|
|
3
3
|
|
|
4
4
|
const main = require('./../../../../lib/main')
|
|
5
5
|
const { logger } = require('./../../../../shared/logger')
|
|
6
|
-
const createSpinner = require('./../../../../shared/createSpinner')
|
|
6
|
+
const { createSpinner } = require('./../../../../shared/createSpinner')
|
|
7
7
|
const sleep = require('./../../../../lib/helpers/sleep')
|
|
8
8
|
const pluralize = require('./../../../../lib/helpers/pluralize')
|
|
9
9
|
|
package/src/cli/commands/ext.js
CHANGED
|
@@ -1,4 +1,7 @@
|
|
|
1
|
+
const path = require('path')
|
|
2
|
+
const { spawnSync } = require('child_process')
|
|
1
3
|
const { Command } = require('commander')
|
|
4
|
+
const { logger } = require('../../shared/logger')
|
|
2
5
|
|
|
3
6
|
const examples = require('./../examples')
|
|
4
7
|
|
|
@@ -6,6 +9,45 @@ const ext = new Command('ext')
|
|
|
6
9
|
|
|
7
10
|
ext
|
|
8
11
|
.description('🔌 extensions')
|
|
12
|
+
.allowUnknownOption()
|
|
13
|
+
|
|
14
|
+
ext.addHelpText('after', ' hub 🚫 DEPRECATED: to be replaced by [dotenvx pro]')
|
|
15
|
+
|
|
16
|
+
ext
|
|
17
|
+
.argument('[command]', 'dynamic ext command')
|
|
18
|
+
.argument('[args...]', 'dynamic ext command arguments')
|
|
19
|
+
.action((command, args, cmdObj) => {
|
|
20
|
+
if (!command) {
|
|
21
|
+
ext.outputHelp()
|
|
22
|
+
process.exit(1)
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
// construct the full command line manually including flags
|
|
26
|
+
const rawArgs = process.argv.slice(3) // adjust the index based on where actual args start
|
|
27
|
+
const commandIndex = rawArgs.indexOf(command)
|
|
28
|
+
const forwardedArgs = rawArgs.slice(commandIndex + 1)
|
|
29
|
+
|
|
30
|
+
logger.debug(`command: ${command}`)
|
|
31
|
+
logger.debug(`args: ${JSON.stringify(forwardedArgs)}`)
|
|
32
|
+
|
|
33
|
+
const binPath = path.join(process.cwd(), 'node_modules', '.bin')
|
|
34
|
+
const newPath = `${binPath}:${process.env.PATH}`
|
|
35
|
+
const env = { ...process.env, PATH: newPath }
|
|
36
|
+
|
|
37
|
+
const result = spawnSync(`dotenvx-ext-${command}`, forwardedArgs, { stdio: 'inherit', env })
|
|
38
|
+
if (result.error) {
|
|
39
|
+
if (command === 'hub') {
|
|
40
|
+
logger.warn(`[INSTALLATION_NEEDED] install dotenvx-ext-${command} to use [dotenvx ext ${command}] commands`)
|
|
41
|
+
logger.help('? see installation instructions [https://github.com/dotenvx/dotenvx-ext-hub]')
|
|
42
|
+
} else {
|
|
43
|
+
logger.info(`error: unknown command '${command}'`)
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
if (result.status !== 0) {
|
|
48
|
+
process.exit(result.status)
|
|
49
|
+
}
|
|
50
|
+
})
|
|
9
51
|
|
|
10
52
|
// dotenvx ext ls
|
|
11
53
|
ext.command('ls')
|
|
@@ -53,6 +95,5 @@ ext.command('settings')
|
|
|
53
95
|
.action(require('./../actions/ext/settings'))
|
|
54
96
|
|
|
55
97
|
ext.addCommand(require('./../commands/ext/vault'))
|
|
56
|
-
ext.addCommand(require('./../commands/ext/hub'))
|
|
57
98
|
|
|
58
99
|
module.exports = ext
|
package/src/cli/dotenvx.js
CHANGED
|
@@ -72,14 +72,17 @@ program.command('get')
|
|
|
72
72
|
})
|
|
73
73
|
|
|
74
74
|
// dotenvx set
|
|
75
|
+
const setAction = require('./actions/set')
|
|
75
76
|
program.command('set')
|
|
76
77
|
.description('set a single environment variable')
|
|
78
|
+
.addHelpText('after', examples.set)
|
|
79
|
+
.allowUnknownOption()
|
|
77
80
|
.argument('KEY', 'KEY')
|
|
78
81
|
.argument('value', 'value')
|
|
79
82
|
.option('-f, --env-file <paths...>', 'path(s) to your env file(s)', '.env')
|
|
80
83
|
.option('-c, --encrypt', 'encrypt value (default: true)', true)
|
|
81
84
|
.option('-p, --plain', 'store value as plain text', false)
|
|
82
|
-
.action(
|
|
85
|
+
.action(setAction)
|
|
83
86
|
|
|
84
87
|
// dotenvx encrypt
|
|
85
88
|
const encryptAction = require('./actions/encrypt')
|
package/src/cli/examples.js
CHANGED
|
@@ -100,10 +100,26 @@ Try it:
|
|
|
100
100
|
`
|
|
101
101
|
}
|
|
102
102
|
|
|
103
|
+
const set = function () {
|
|
104
|
+
return `
|
|
105
|
+
Examples:
|
|
106
|
+
|
|
107
|
+
\`\`\`
|
|
108
|
+
$ dotenvx set KEY value
|
|
109
|
+
$ dotenvx set KEY "value with spaces"
|
|
110
|
+
$ dotenvx set KEY -- "---value with a dash---"
|
|
111
|
+
$ dotenvx set KEY -- "-----BEGIN OPENSSH PRIVATE KEY-----
|
|
112
|
+
b3BlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQAAAAAAAAABAAAAMwAAAAtzc2gtZW
|
|
113
|
+
-----END OPENSSH PRIVATE KEY-----"
|
|
114
|
+
\`\`\`
|
|
115
|
+
`
|
|
116
|
+
}
|
|
117
|
+
|
|
103
118
|
module.exports = {
|
|
104
119
|
run,
|
|
105
120
|
vaultEncrypt,
|
|
106
121
|
precommit,
|
|
107
122
|
prebuild,
|
|
108
|
-
gitignore
|
|
123
|
+
gitignore,
|
|
124
|
+
set
|
|
109
125
|
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
const { ConfirmPrompt } = require('@clack/core')
|
|
2
|
+
|
|
3
|
+
module.exports = (opts) => {
|
|
4
|
+
return new ConfirmPrompt({
|
|
5
|
+
active: 'Y',
|
|
6
|
+
inactive: 'N',
|
|
7
|
+
initialValue: true,
|
|
8
|
+
render () {
|
|
9
|
+
return `${opts.message} (${this.value ? 'Y/n' : 'y/N'})`
|
|
10
|
+
}
|
|
11
|
+
}).prompt()
|
|
12
|
+
}
|
|
@@ -1,8 +1,88 @@
|
|
|
1
|
-
const ora = require('ora')
|
|
2
1
|
const chalk = require('chalk')
|
|
3
2
|
|
|
4
|
-
const
|
|
5
|
-
|
|
3
|
+
const FRAMES = ['⠋', '⠙', '⠹', '⠸', '⠼', '⠴', '⠦', '⠧', '⠇', '⠏']
|
|
4
|
+
const HIDE_CURSOR = '\u001B[?25l'
|
|
5
|
+
const SHOW_CURSOR = '\u001B[?25h'
|
|
6
|
+
const CLEAR_LINE = '\r\x1b[K'
|
|
7
|
+
const SYMBOL_INFO = 'ℹ'
|
|
8
|
+
const SYMBOL_WARN = '⚠'
|
|
9
|
+
const SYMBOL_ERROR = '✖'
|
|
10
|
+
const SYMBOL_SUCCESS = '✔'
|
|
11
|
+
|
|
12
|
+
class Spinner {
|
|
13
|
+
text
|
|
14
|
+
interval
|
|
15
|
+
frameIndex = 0
|
|
16
|
+
symbol = chalk.blue(FRAMES[0])
|
|
17
|
+
|
|
18
|
+
constructor (text) {
|
|
19
|
+
this.text = text
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
start (text) {
|
|
23
|
+
if (text) {
|
|
24
|
+
this.text = text
|
|
25
|
+
}
|
|
26
|
+
this.render()
|
|
27
|
+
this.interval = setInterval(() => this.tick(), 50)
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
tick () {
|
|
31
|
+
this.symbol = chalk.blue(FRAMES[this.frameIndex++])
|
|
32
|
+
if (this.frameIndex === FRAMES.length - 1) this.frameIndex = 0
|
|
33
|
+
this.render()
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
render () {
|
|
37
|
+
process.stdout.write(CLEAR_LINE + HIDE_CURSOR + (this.symbol ? this.symbol + ' ' : '') + this.text)
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
succeed (text) {
|
|
41
|
+
if (text) {
|
|
42
|
+
this.text = text
|
|
43
|
+
}
|
|
44
|
+
this.symbol = chalk.green(SYMBOL_SUCCESS)
|
|
45
|
+
this.end()
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
info (text) {
|
|
49
|
+
if (text) {
|
|
50
|
+
this.text = text
|
|
51
|
+
}
|
|
52
|
+
this.symbol = chalk.blue(SYMBOL_INFO)
|
|
53
|
+
this.end()
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
warn (text) {
|
|
57
|
+
if (text) {
|
|
58
|
+
this.text = text
|
|
59
|
+
}
|
|
60
|
+
this.symbol = chalk.yellow(SYMBOL_WARN)
|
|
61
|
+
this.end()
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
fail (text) {
|
|
65
|
+
if (text) {
|
|
66
|
+
this.text = text
|
|
67
|
+
}
|
|
68
|
+
this.symbol = chalk.red(SYMBOL_ERROR)
|
|
69
|
+
this.end()
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
stop () {
|
|
73
|
+
this.symbol = ''
|
|
74
|
+
this.end()
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
end () {
|
|
78
|
+
this.render()
|
|
79
|
+
clearInterval(this.interval)
|
|
80
|
+
process.stdout.write(SHOW_CURSOR + '\n')
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
const createSpinner = (initialMessage = '') => {
|
|
85
|
+
const spinner = new Spinner(initialMessage)
|
|
6
86
|
|
|
7
87
|
return {
|
|
8
88
|
start: (message) => spinner.start(message),
|
|
@@ -14,4 +94,4 @@ const createSpinner = function (initialMessage = '') {
|
|
|
14
94
|
}
|
|
15
95
|
}
|
|
16
96
|
|
|
17
|
-
module.exports = createSpinner
|
|
97
|
+
module.exports = { createSpinner, Spinner }
|
|
@@ -1,126 +0,0 @@
|
|
|
1
|
-
const open = require('open')
|
|
2
|
-
const { request } = require('undici')
|
|
3
|
-
const clipboardy = require('./../../../../lib/helpers/clipboardy')
|
|
4
|
-
const confirm = require('@inquirer/confirm').default
|
|
5
|
-
|
|
6
|
-
const createSpinner = require('./../../../../shared/createSpinner')
|
|
7
|
-
const store = require('./../../../../shared/store')
|
|
8
|
-
const { logger } = require('./../../../../shared/logger')
|
|
9
|
-
|
|
10
|
-
const OAUTH_CLIENT_ID = 'oac_dotenvxcli'
|
|
11
|
-
|
|
12
|
-
const spinner = createSpinner('waiting on user authorization')
|
|
13
|
-
|
|
14
|
-
const formatCode = function (str) {
|
|
15
|
-
const parts = []
|
|
16
|
-
|
|
17
|
-
for (let i = 0; i < str.length; i += 4) {
|
|
18
|
-
parts.push(str.substring(i, i + 4))
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
return parts.join('-')
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
async function pollTokenUrl (tokenUrl, deviceCode, interval) {
|
|
25
|
-
logger.http(`POST ${tokenUrl} with deviceCode ${deviceCode} at interval ${interval}`)
|
|
26
|
-
|
|
27
|
-
try {
|
|
28
|
-
const response = await request(tokenUrl, {
|
|
29
|
-
method: 'POST',
|
|
30
|
-
headers: {
|
|
31
|
-
'Content-Type': 'application/json'
|
|
32
|
-
},
|
|
33
|
-
body: JSON.stringify({
|
|
34
|
-
client_id: OAUTH_CLIENT_ID,
|
|
35
|
-
device_code: deviceCode,
|
|
36
|
-
grant_type: 'urn:ietf:params:oauth:grant-type:device_code'
|
|
37
|
-
})
|
|
38
|
-
})
|
|
39
|
-
|
|
40
|
-
const responseData = await response.body.json()
|
|
41
|
-
|
|
42
|
-
logger.http(responseData)
|
|
43
|
-
|
|
44
|
-
if (response.statusCode >= 400) {
|
|
45
|
-
// continue polling if authorization_pending
|
|
46
|
-
if (responseData.error === 'authorization_pending') {
|
|
47
|
-
setTimeout(() => pollTokenUrl(tokenUrl, deviceCode, interval), interval * 1000)
|
|
48
|
-
} else {
|
|
49
|
-
spinner.start()
|
|
50
|
-
spinner.fail(responseData.error_description)
|
|
51
|
-
process.exit(1)
|
|
52
|
-
}
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
if (responseData.access_token) {
|
|
56
|
-
spinner.start()
|
|
57
|
-
store.setToken(responseData.full_username, responseData.access_token)
|
|
58
|
-
store.setHostname(responseData.hostname)
|
|
59
|
-
spinner.succeed(`logged in as ${responseData.username}`)
|
|
60
|
-
process.exit(0)
|
|
61
|
-
} else {
|
|
62
|
-
// continue polling if no access_token. shouldn't ever get here it server is implemented correctly
|
|
63
|
-
setTimeout(() => pollTokenUrl(tokenUrl, deviceCode, interval), interval * 1000)
|
|
64
|
-
}
|
|
65
|
-
} catch (error) {
|
|
66
|
-
spinner.start()
|
|
67
|
-
spinner.fail(error.toString())
|
|
68
|
-
process.exit(1)
|
|
69
|
-
}
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
async function login () {
|
|
73
|
-
const options = this.opts()
|
|
74
|
-
logger.debug(`options: ${JSON.stringify(options)}`)
|
|
75
|
-
|
|
76
|
-
const hostname = options.hostname
|
|
77
|
-
const deviceCodeUrl = `${hostname}/oauth/device/code`
|
|
78
|
-
const tokenUrl = `${hostname}/oauth/token`
|
|
79
|
-
|
|
80
|
-
try {
|
|
81
|
-
const response = await request(deviceCodeUrl, {
|
|
82
|
-
method: 'POST',
|
|
83
|
-
headers: {
|
|
84
|
-
'Content-Type': 'application/json'
|
|
85
|
-
},
|
|
86
|
-
body: JSON.stringify({ client_id: OAUTH_CLIENT_ID })
|
|
87
|
-
})
|
|
88
|
-
|
|
89
|
-
const responseData = await response.body.json()
|
|
90
|
-
|
|
91
|
-
if (response.statusCode >= 400) {
|
|
92
|
-
logger.http(responseData)
|
|
93
|
-
|
|
94
|
-
spinner.start()
|
|
95
|
-
spinner.fail(responseData.error_description)
|
|
96
|
-
process.exit(1)
|
|
97
|
-
}
|
|
98
|
-
|
|
99
|
-
const deviceCode = responseData.device_code
|
|
100
|
-
const userCode = responseData.user_code
|
|
101
|
-
const verificationUri = responseData.verification_uri
|
|
102
|
-
const interval = responseData.interval
|
|
103
|
-
|
|
104
|
-
try { clipboardy.writeSync(userCode) } catch (_e) {}
|
|
105
|
-
|
|
106
|
-
// qrcode.generate(verificationUri, { small: true }) // too verbose
|
|
107
|
-
|
|
108
|
-
// begin polling
|
|
109
|
-
pollTokenUrl(tokenUrl, deviceCode, interval)
|
|
110
|
-
|
|
111
|
-
// optionally allow user to open browser
|
|
112
|
-
const answer = await confirm({ message: `press Enter to open [${verificationUri}] and enter code [${formatCode(userCode)}]...` })
|
|
113
|
-
|
|
114
|
-
if (answer) {
|
|
115
|
-
await open(verificationUri)
|
|
116
|
-
|
|
117
|
-
spinner.start()
|
|
118
|
-
}
|
|
119
|
-
} catch (error) {
|
|
120
|
-
spinner.start()
|
|
121
|
-
spinner.fail(error.toString())
|
|
122
|
-
process.exit(1)
|
|
123
|
-
}
|
|
124
|
-
}
|
|
125
|
-
|
|
126
|
-
module.exports = login
|
|
@@ -1,43 +0,0 @@
|
|
|
1
|
-
const openBrowser = require('open')
|
|
2
|
-
const confirm = require('@inquirer/confirm').default
|
|
3
|
-
|
|
4
|
-
const createSpinner = require('./../../../../shared/createSpinner')
|
|
5
|
-
const store = require('./../../../../shared/store')
|
|
6
|
-
const { logger } = require('./../../../../shared/logger')
|
|
7
|
-
const sleep = require('./../../../../lib/helpers/sleep')
|
|
8
|
-
|
|
9
|
-
const username = store.getUsername()
|
|
10
|
-
const usernamePart = username ? ` [${username}]` : ''
|
|
11
|
-
const spinner = createSpinner(`logging off machine${usernamePart}`)
|
|
12
|
-
|
|
13
|
-
async function logout () {
|
|
14
|
-
spinner.start()
|
|
15
|
-
await sleep(500) // better dx
|
|
16
|
-
|
|
17
|
-
// debug opts
|
|
18
|
-
const options = this.opts()
|
|
19
|
-
logger.debug(`options: ${JSON.stringify(options)}`)
|
|
20
|
-
|
|
21
|
-
logger.debug('deleting settings.DOTENVX_TOKEN')
|
|
22
|
-
store.deleteToken()
|
|
23
|
-
|
|
24
|
-
logger.debug('deleting settings.DOTENVX_HOSTNAME')
|
|
25
|
-
store.deleteHostname()
|
|
26
|
-
|
|
27
|
-
spinner.done(`logged off machine${usernamePart}`)
|
|
28
|
-
|
|
29
|
-
const hostname = options.hostname
|
|
30
|
-
const logoutUrl = `${hostname}/logout`
|
|
31
|
-
|
|
32
|
-
// optionally allow user to open browser
|
|
33
|
-
const answer = await confirm({ message: `press Enter to also log off browser [${logoutUrl}]...` })
|
|
34
|
-
|
|
35
|
-
if (answer) {
|
|
36
|
-
spinner.start()
|
|
37
|
-
await sleep(500) // better dx
|
|
38
|
-
await openBrowser(logoutUrl)
|
|
39
|
-
spinner.done(`logged off browser${usernamePart}`)
|
|
40
|
-
}
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
module.exports = logout
|
|
@@ -1,63 +0,0 @@
|
|
|
1
|
-
const openBrowser = require('open')
|
|
2
|
-
const confirm = require('@inquirer/confirm').default
|
|
3
|
-
|
|
4
|
-
const createSpinner = require('./../../../../shared/createSpinner')
|
|
5
|
-
const { logger } = require('./../../../../shared/logger')
|
|
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')
|
|
15
|
-
|
|
16
|
-
async function open () {
|
|
17
|
-
// debug opts
|
|
18
|
-
const options = this.opts()
|
|
19
|
-
logger.debug(`options: ${JSON.stringify(options)}`)
|
|
20
|
-
|
|
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
|
-
}
|
|
48
|
-
|
|
49
|
-
const usernameName = extractUsernameName(giturl)
|
|
50
|
-
const openUrl = `${options.hostname}/gh/${usernameName}`
|
|
51
|
-
|
|
52
|
-
// optionally allow user to open browser
|
|
53
|
-
const answer = await confirm({ message: `press Enter to open [${openUrl}]...` })
|
|
54
|
-
|
|
55
|
-
if (answer) {
|
|
56
|
-
spinner.start()
|
|
57
|
-
await sleep(500) // better dx
|
|
58
|
-
await openBrowser(openUrl)
|
|
59
|
-
spinner.succeed(`opened [${usernameName}]`)
|
|
60
|
-
}
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
module.exports = open
|
|
@@ -1,105 +0,0 @@
|
|
|
1
|
-
const fs = require('fs')
|
|
2
|
-
const path = require('path')
|
|
3
|
-
const { request } = require('undici')
|
|
4
|
-
|
|
5
|
-
const store = require('./../../../../shared/store')
|
|
6
|
-
const { logger } = require('./../../../../shared/logger')
|
|
7
|
-
const createSpinner = require('./../../../../shared/createSpinner')
|
|
8
|
-
|
|
9
|
-
const isGitRepo = require('./../../../../lib/helpers/isGitRepo')
|
|
10
|
-
const isGithub = require('./../../../../lib/helpers/isGithub')
|
|
11
|
-
const gitUrl = require('./../../../../lib/helpers/gitUrl')
|
|
12
|
-
const gitRoot = require('./../../../../lib/helpers/gitRoot')
|
|
13
|
-
const extractUsernameName = require('./../../../../lib/helpers/extractUsernameName')
|
|
14
|
-
const sleep = require('./../../../../lib/helpers/sleep')
|
|
15
|
-
|
|
16
|
-
const spinner = createSpinner('pulling')
|
|
17
|
-
|
|
18
|
-
// constants
|
|
19
|
-
const ENCODING = 'utf8'
|
|
20
|
-
|
|
21
|
-
// Create a simple-git instance for the current directory
|
|
22
|
-
async function pull (directory) {
|
|
23
|
-
spinner.start()
|
|
24
|
-
await sleep(500) // better dx
|
|
25
|
-
|
|
26
|
-
// debug args
|
|
27
|
-
logger.debug(`directory: ${directory}`)
|
|
28
|
-
|
|
29
|
-
// debug opts
|
|
30
|
-
const options = this.opts()
|
|
31
|
-
logger.debug(`options: ${JSON.stringify(options)}`)
|
|
32
|
-
|
|
33
|
-
// must be a git repo
|
|
34
|
-
if (!isGitRepo()) {
|
|
35
|
-
spinner.fail('oops, must be a git repository')
|
|
36
|
-
logger.help('? create one with [git init .]')
|
|
37
|
-
process.exit(1)
|
|
38
|
-
}
|
|
39
|
-
// must be a git root
|
|
40
|
-
const gitroot = gitRoot()
|
|
41
|
-
if (!gitroot) {
|
|
42
|
-
spinner.fail('oops, could not determine git repository\'s root')
|
|
43
|
-
logger.help('? create one with [git init .]')
|
|
44
|
-
process.exit(1)
|
|
45
|
-
}
|
|
46
|
-
// must have a remote origin url
|
|
47
|
-
const giturl = gitUrl()
|
|
48
|
-
if (!giturl) {
|
|
49
|
-
spinner.fail('oops, must have a remote origin (git remote -v)')
|
|
50
|
-
logger.help('? create it at [github.com/new] and then run [git remote add origin git@github.com:username/repository.git]')
|
|
51
|
-
process.exit(1)
|
|
52
|
-
}
|
|
53
|
-
// must be a github remote
|
|
54
|
-
if (!isGithub(giturl)) {
|
|
55
|
-
spinner.fail('oops, must be a github.com remote origin (git remote -v)')
|
|
56
|
-
logger.help('? create it at [github.com/new] and then run [git remote add origin git@github.com:username/repository.git]')
|
|
57
|
-
logger.help2('ℹ need support for other origins? [please tell us](https://github.com/dotenvx/dotenvx/issues)')
|
|
58
|
-
process.exit(1)
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
const envKeysFilepath = path.join(directory, '.env.keys')
|
|
62
|
-
const hostname = options.hostname
|
|
63
|
-
const pullUrl = `${hostname}/v1/pull`
|
|
64
|
-
const oauthToken = store.getToken()
|
|
65
|
-
const usernameName = extractUsernameName(giturl)
|
|
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
|
|
67
|
-
|
|
68
|
-
try {
|
|
69
|
-
const response = await request(pullUrl, {
|
|
70
|
-
method: 'POST',
|
|
71
|
-
headers: {
|
|
72
|
-
Authorization: `Bearer ${oauthToken}`,
|
|
73
|
-
'Content-Type': 'application/json'
|
|
74
|
-
},
|
|
75
|
-
body: JSON.stringify({
|
|
76
|
-
username_name: usernameName,
|
|
77
|
-
filepath: relativeEnvKeysFilepath
|
|
78
|
-
})
|
|
79
|
-
})
|
|
80
|
-
|
|
81
|
-
const responseData = await response.body.json()
|
|
82
|
-
|
|
83
|
-
if (response.statusCode >= 400) {
|
|
84
|
-
logger.http(responseData)
|
|
85
|
-
spinner.fail(responseData.error.message)
|
|
86
|
-
if (response.statusCode === 404) {
|
|
87
|
-
logger.help(`? try visiting [${hostname}/gh/${usernameName}] in your browser`)
|
|
88
|
-
}
|
|
89
|
-
process.exit(1)
|
|
90
|
-
}
|
|
91
|
-
|
|
92
|
-
if (fs.existsSync(envKeysFilepath) && fs.readFileSync(envKeysFilepath, ENCODING) === responseData.DOTENV_KEYS) {
|
|
93
|
-
spinner.done(`no changes (${envKeysFilepath})`)
|
|
94
|
-
} else {
|
|
95
|
-
fs.writeFileSync(envKeysFilepath, responseData.DOTENV_KEYS)
|
|
96
|
-
spinner.succeed(`pulled [${usernameName}]`)
|
|
97
|
-
logger.help2(`ℹ run [cat ${envKeysFilepath}] to view locally`)
|
|
98
|
-
}
|
|
99
|
-
} catch (error) {
|
|
100
|
-
spinner.fail(error.toString())
|
|
101
|
-
process.exit(1)
|
|
102
|
-
}
|
|
103
|
-
}
|
|
104
|
-
|
|
105
|
-
module.exports = pull
|
|
@@ -1,112 +0,0 @@
|
|
|
1
|
-
const fs = require('fs')
|
|
2
|
-
const path = require('path')
|
|
3
|
-
const { request } = require('undici')
|
|
4
|
-
|
|
5
|
-
const store = require('./../../../../shared/store')
|
|
6
|
-
const { logger } = require('./../../../../shared/logger')
|
|
7
|
-
const createSpinner = require('./../../../../shared/createSpinner')
|
|
8
|
-
|
|
9
|
-
const isGitRepo = require('./../../../../lib/helpers/isGitRepo')
|
|
10
|
-
const isGithub = require('./../../../../lib/helpers/isGithub')
|
|
11
|
-
const gitUrl = require('./../../../../lib/helpers/gitUrl')
|
|
12
|
-
const gitRoot = require('./../../../../lib/helpers/gitRoot')
|
|
13
|
-
const extractUsernameName = require('./../../../../lib/helpers/extractUsernameName')
|
|
14
|
-
const sleep = require('./../../../../lib/helpers/sleep')
|
|
15
|
-
const forgivingDirectory = require('./../../../../lib/helpers/forgivingDirectory')
|
|
16
|
-
|
|
17
|
-
const spinner = createSpinner('pushing')
|
|
18
|
-
|
|
19
|
-
// constants
|
|
20
|
-
const ENCODING = 'utf8'
|
|
21
|
-
|
|
22
|
-
// Create a simple-git instance for the current directory
|
|
23
|
-
async function push (directory) {
|
|
24
|
-
spinner.start()
|
|
25
|
-
await sleep(500) // better dx
|
|
26
|
-
|
|
27
|
-
directory = forgivingDirectory(directory)
|
|
28
|
-
|
|
29
|
-
// debug args
|
|
30
|
-
logger.debug(`directory: ${directory}`)
|
|
31
|
-
|
|
32
|
-
// debug opts
|
|
33
|
-
const options = this.opts()
|
|
34
|
-
logger.debug(`options: ${JSON.stringify(options)}`)
|
|
35
|
-
|
|
36
|
-
// must be a git repo
|
|
37
|
-
if (!isGitRepo()) {
|
|
38
|
-
spinner.fail('oops, must be a git repository')
|
|
39
|
-
logger.help('? create one with [git init .]')
|
|
40
|
-
process.exit(1)
|
|
41
|
-
}
|
|
42
|
-
// must be a git root
|
|
43
|
-
const gitroot = gitRoot()
|
|
44
|
-
if (!gitroot) {
|
|
45
|
-
spinner.fail('oops, could not determine git repository\'s root')
|
|
46
|
-
logger.help('? create one with [git init .]')
|
|
47
|
-
process.exit(1)
|
|
48
|
-
}
|
|
49
|
-
// must have a remote origin url
|
|
50
|
-
const giturl = gitUrl()
|
|
51
|
-
if (!giturl) {
|
|
52
|
-
spinner.fail('oops, must have a remote origin (git remote -v)')
|
|
53
|
-
logger.help('? create it at [github.com/new] and then run [git remote add origin git@github.com:username/repository.git]')
|
|
54
|
-
process.exit(1)
|
|
55
|
-
}
|
|
56
|
-
// must be a github remote
|
|
57
|
-
if (!isGithub(giturl)) {
|
|
58
|
-
spinner.fail('oops, must be a github.com remote origin (git remote -v)')
|
|
59
|
-
logger.help('? create it at [github.com/new] and then run [git remote add origin git@github.com:username/repository.git]')
|
|
60
|
-
logger.help2('ℹ need support for other origins? [please tell us](https://github.com/dotenvx/dotenvx/issues)')
|
|
61
|
-
process.exit(1)
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
const envKeysFilepath = path.join(directory, '.env.keys')
|
|
65
|
-
if (!fs.existsSync(envKeysFilepath)) {
|
|
66
|
-
spinner.fail('oops, missing .env.keys file')
|
|
67
|
-
logger.help(`? generate one with [dotenvx encrypt${directory ? ` ${directory}` : ''}]`)
|
|
68
|
-
logger.help2('ℹ a .env.keys file holds decryption keys for a .env.vault file')
|
|
69
|
-
process.exit(1)
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
const hostname = options.hostname
|
|
73
|
-
const pushUrl = `${hostname}/v1/push`
|
|
74
|
-
const oauthToken = store.getToken()
|
|
75
|
-
const dotenvKeysContent = fs.readFileSync(envKeysFilepath, ENCODING)
|
|
76
|
-
const usernameName = extractUsernameName(giturl)
|
|
77
|
-
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
|
|
78
|
-
|
|
79
|
-
try {
|
|
80
|
-
const response = await request(pushUrl, {
|
|
81
|
-
method: 'POST',
|
|
82
|
-
headers: {
|
|
83
|
-
Authorization: `Bearer ${oauthToken}`,
|
|
84
|
-
'Content-Type': 'application/json'
|
|
85
|
-
},
|
|
86
|
-
body: JSON.stringify({
|
|
87
|
-
username_name: usernameName,
|
|
88
|
-
DOTENV_KEYS: dotenvKeysContent,
|
|
89
|
-
filepath: relativeEnvKeysFilepath
|
|
90
|
-
})
|
|
91
|
-
})
|
|
92
|
-
|
|
93
|
-
const responseData = await response.body.json()
|
|
94
|
-
|
|
95
|
-
if (response.statusCode >= 400) {
|
|
96
|
-
logger.http(responseData)
|
|
97
|
-
spinner.fail(responseData.error.message)
|
|
98
|
-
if (response.statusCode === 404) {
|
|
99
|
-
logger.help(`? try visiting [${hostname}/gh/${usernameName}] in your browser`)
|
|
100
|
-
}
|
|
101
|
-
process.exit(1)
|
|
102
|
-
}
|
|
103
|
-
} catch (error) {
|
|
104
|
-
spinner.fail(error.toString())
|
|
105
|
-
process.exit(1)
|
|
106
|
-
}
|
|
107
|
-
|
|
108
|
-
spinner.succeed(`pushed [${usernameName}]`)
|
|
109
|
-
logger.help2('ℹ run [dotenvx ext hub open] to view on hub')
|
|
110
|
-
}
|
|
111
|
-
|
|
112
|
-
module.exports = push
|
|
@@ -1,89 +0,0 @@
|
|
|
1
|
-
const { Command } = require('commander')
|
|
2
|
-
|
|
3
|
-
const store = require('./../../../shared/store')
|
|
4
|
-
const { logger } = require('./../../../shared/logger')
|
|
5
|
-
|
|
6
|
-
const hub = new Command('hub')
|
|
7
|
-
|
|
8
|
-
hub
|
|
9
|
-
.description('🚫 DEPRECATED: to be replaced by [dotenvx pro]')
|
|
10
|
-
|
|
11
|
-
const loginAction = require('./../../actions/ext/hub/login')
|
|
12
|
-
hub
|
|
13
|
-
.command('login')
|
|
14
|
-
.description('authenticate to dotenvx hub')
|
|
15
|
-
.option('-h, --hostname <url>', 'set hostname', store.getHostname())
|
|
16
|
-
.action(function (...args) {
|
|
17
|
-
logger.warn('DEPRECATION NOTICE: to be replaced by [dotenvx pro]')
|
|
18
|
-
|
|
19
|
-
loginAction.apply(this, args)
|
|
20
|
-
})
|
|
21
|
-
|
|
22
|
-
const pushAction = require('./../../actions/ext/hub/push')
|
|
23
|
-
hub
|
|
24
|
-
.command('push')
|
|
25
|
-
.description('push .env.keys to dotenvx hub')
|
|
26
|
-
.argument('[directory]', 'directory to push', '.')
|
|
27
|
-
.option('-h, --hostname <url>', 'set hostname', store.getHostname())
|
|
28
|
-
.action(function (...args) {
|
|
29
|
-
logger.warn('DEPRECATION NOTICE: to be replaced by [dotenvx pro]')
|
|
30
|
-
|
|
31
|
-
pushAction.apply(this, args)
|
|
32
|
-
})
|
|
33
|
-
|
|
34
|
-
const pullAction = require('./../../actions/ext/hub/pull')
|
|
35
|
-
hub
|
|
36
|
-
.command('pull')
|
|
37
|
-
.description('pull .env.keys from dotenvx hub')
|
|
38
|
-
.argument('[directory]', 'directory to pull', '.')
|
|
39
|
-
.option('-h, --hostname <url>', 'set hostname', store.getHostname())
|
|
40
|
-
.action(function (...args) {
|
|
41
|
-
logger.warn('DEPRECATION NOTICE: to be replaced by [dotenvx pro]')
|
|
42
|
-
|
|
43
|
-
pullAction.apply(this, args)
|
|
44
|
-
})
|
|
45
|
-
|
|
46
|
-
const openAction = require('./../../actions/ext/hub/open')
|
|
47
|
-
hub
|
|
48
|
-
.command('open')
|
|
49
|
-
.description('view repository on dotenvx hub')
|
|
50
|
-
.option('-h, --hostname <url>', 'set hostname', store.getHostname())
|
|
51
|
-
.action(function (...args) {
|
|
52
|
-
logger.warn('DEPRECATION NOTICE: to be replaced by [dotenvx pro]')
|
|
53
|
-
|
|
54
|
-
openAction.apply(this, args)
|
|
55
|
-
})
|
|
56
|
-
|
|
57
|
-
const tokenAction = require('./../../actions/ext/hub/token')
|
|
58
|
-
hub
|
|
59
|
-
.command('token')
|
|
60
|
-
.description('print the auth token dotenvx hub is configured to use')
|
|
61
|
-
.option('-h, --hostname <url>', 'set hostname', 'https://hub.dotenvx.com')
|
|
62
|
-
.action(function (...args) {
|
|
63
|
-
logger.warn('DEPRECATION NOTICE: to be replaced by [dotenvx pro]')
|
|
64
|
-
|
|
65
|
-
tokenAction.apply(this, args)
|
|
66
|
-
})
|
|
67
|
-
|
|
68
|
-
const statusAction = require('./../../actions/ext/hub/status')
|
|
69
|
-
hub
|
|
70
|
-
.command('status')
|
|
71
|
-
.description('display logged in user')
|
|
72
|
-
.action(function (...args) {
|
|
73
|
-
logger.warn('DEPRECATION NOTICE: to be replaced by [dotenvx pro]')
|
|
74
|
-
|
|
75
|
-
statusAction.apply(this, args)
|
|
76
|
-
})
|
|
77
|
-
|
|
78
|
-
const logoutAction = require('./../../actions/ext/hub/logout')
|
|
79
|
-
hub
|
|
80
|
-
.command('logout')
|
|
81
|
-
.description('log out this machine from dotenvx hub')
|
|
82
|
-
.option('-h, --hostname <url>', 'set hostname', store.getHostname())
|
|
83
|
-
.action(function (...args) {
|
|
84
|
-
logger.warn('DEPRECATION NOTICE: to be replaced by [dotenvx pro]')
|
|
85
|
-
|
|
86
|
-
logoutAction.apply(this, args)
|
|
87
|
-
})
|
|
88
|
-
|
|
89
|
-
module.exports = hub
|