@dotenvx/dotenvx-ops 0.40.0 → 0.41.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,13 @@
|
|
|
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-ops/compare/v0.
|
|
5
|
+
[Unreleased](https://github.com/dotenvx/dotenvx-ops/compare/v0.41.0...main)
|
|
6
|
+
|
|
7
|
+
## [0.41.0](https://github.com/dotenvx/dotenvx-ops/compare/v0.40.0...v0.41.0) (2026-04-26)
|
|
8
|
+
|
|
9
|
+
### Added
|
|
10
|
+
|
|
11
|
+
* Add `armor up` ([#58](https://github.com/dotenvx/dotenvx-ops/pull/58))
|
|
6
12
|
|
|
7
13
|
## [0.40.0](https://github.com/dotenvx/dotenvx-ops/compare/v0.39.1...v0.40.0) (2026-04-24)
|
|
8
14
|
|
package/package.json
CHANGED
|
@@ -1,14 +1,32 @@
|
|
|
1
1
|
const { logger } = require('@dotenvx/dotenvx')
|
|
2
|
-
|
|
3
2
|
const Session = require('./../../../db/session')
|
|
3
|
+
const ArmorUp = require('./../../../lib/services/armorUp')
|
|
4
|
+
const createSpinner = require('../../../lib/helpers/createSpinner2')
|
|
5
|
+
|
|
6
|
+
async function up () {
|
|
7
|
+
const options = this.opts()
|
|
8
|
+
const spinner = await createSpinner({ ...options, text: 'armoring' })
|
|
9
|
+
|
|
10
|
+
logger.debug(`options: ${JSON.stringify(options)}`)
|
|
11
|
+
|
|
12
|
+
const sesh = new Session()
|
|
13
|
+
sesh.notifyUpdate()
|
|
14
|
+
const hostname = options.hostname || sesh.hostname()
|
|
15
|
+
const token = options.token || sesh.token()
|
|
4
16
|
|
|
5
|
-
function up () {
|
|
6
17
|
try {
|
|
7
|
-
const
|
|
8
|
-
|
|
18
|
+
const devicePublicKey = sesh.devicePublicKey()
|
|
19
|
+
|
|
20
|
+
const { changed, privateKeyName } = await new ArmorUp(hostname, token, devicePublicKey, options.envFile).run()
|
|
9
21
|
|
|
10
|
-
|
|
22
|
+
if (spinner) spinner.stop()
|
|
23
|
+
if (changed) {
|
|
24
|
+
logger.success(`◈ armored ${privateKeyName} (armored ⛨)`)
|
|
25
|
+
} else {
|
|
26
|
+
logger.info(`○ no change ${privateKeyName} (armored ⛨)`)
|
|
27
|
+
}
|
|
11
28
|
} catch (error) {
|
|
29
|
+
if (spinner) spinner.stop()
|
|
12
30
|
logger.error(error.message)
|
|
13
31
|
process.exit(1)
|
|
14
32
|
}
|
|
@@ -16,7 +16,8 @@ armor
|
|
|
16
16
|
const upAction = require('./../actions/armor/up')
|
|
17
17
|
armor
|
|
18
18
|
.command('up')
|
|
19
|
-
.description('
|
|
19
|
+
.description('armor private key')
|
|
20
|
+
.option('-f, --env-file <path>', 'path to your env file')
|
|
20
21
|
.action(upAction)
|
|
21
22
|
|
|
22
23
|
// dotenvx-ops armor down
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
const { http } = require('../../lib/helpers/http')
|
|
2
|
+
const buildApiError = require('../../lib/helpers/buildApiError')
|
|
3
|
+
const packageJson = require('../../lib/helpers/packageJson')
|
|
4
|
+
const normalizeToken = require('../../lib/helpers/normalizeToken')
|
|
5
|
+
|
|
6
|
+
class PostArmorUp {
|
|
7
|
+
constructor (hostname, token, devicePublicKey, publicKey, privateKey) {
|
|
8
|
+
this.hostname = hostname || 'https://ops.dotenvx.com'
|
|
9
|
+
this.token = token
|
|
10
|
+
this.devicePublicKey = devicePublicKey
|
|
11
|
+
this.publicKey = publicKey
|
|
12
|
+
this.privateKey = privateKey
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
async run () {
|
|
16
|
+
const token = normalizeToken(this.token)
|
|
17
|
+
const devicePublicKey = this.devicePublicKey
|
|
18
|
+
const publicKey = this.publicKey
|
|
19
|
+
const privateKey = this.privateKey
|
|
20
|
+
const url = `${this.hostname}/api/armor/up`
|
|
21
|
+
|
|
22
|
+
const body = {
|
|
23
|
+
device_public_key: devicePublicKey,
|
|
24
|
+
cli_version: packageJson.version,
|
|
25
|
+
public_key: publicKey,
|
|
26
|
+
private_key: privateKey
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
const resp = await http(url, {
|
|
30
|
+
method: 'POST',
|
|
31
|
+
headers: {
|
|
32
|
+
Authorization: `Bearer ${token}`,
|
|
33
|
+
'Content-Type': 'application/json'
|
|
34
|
+
},
|
|
35
|
+
body: JSON.stringify(body)
|
|
36
|
+
})
|
|
37
|
+
|
|
38
|
+
const json = await resp.body.json()
|
|
39
|
+
|
|
40
|
+
if (resp.statusCode >= 400) {
|
|
41
|
+
throw buildApiError(resp.statusCode, json)
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
return json
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
module.exports = PostArmorUp
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
const fs = require('fs')
|
|
2
|
+
const path = require('path')
|
|
3
|
+
|
|
4
|
+
function escapeForRegex (value) {
|
|
5
|
+
return value.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
function removeEnvKey (key, keysFilepath = '.env.keys') {
|
|
9
|
+
const resolvedKeysFilepath = path.resolve(keysFilepath)
|
|
10
|
+
|
|
11
|
+
if (!fs.existsSync(resolvedKeysFilepath)) {
|
|
12
|
+
return {
|
|
13
|
+
changed: false,
|
|
14
|
+
key,
|
|
15
|
+
filepath: keysFilepath
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
const src = fs.readFileSync(resolvedKeysFilepath, 'utf8')
|
|
20
|
+
const eol = src.includes('\r\n') ? '\r\n' : '\n'
|
|
21
|
+
const keyPattern = new RegExp(`^\\s*(?:export\\s+)?${escapeForRegex(key)}\\s*=`)
|
|
22
|
+
const envKeyPattern = /^\s*(?:export\s+)?[A-Za-z_][A-Za-z0-9_]*\s*=/
|
|
23
|
+
const lines = src.length > 0 ? src.split(/\r?\n/) : []
|
|
24
|
+
|
|
25
|
+
while (lines.length > 0 && lines[lines.length - 1] === '') {
|
|
26
|
+
lines.pop()
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
const nextLines = lines.filter((line) => !keyPattern.test(line))
|
|
30
|
+
const changed = nextLines.length !== lines.length
|
|
31
|
+
|
|
32
|
+
if (changed) {
|
|
33
|
+
const hasRemainingKeys = nextLines.some((line) => envKeyPattern.test(line))
|
|
34
|
+
|
|
35
|
+
if (hasRemainingKeys) {
|
|
36
|
+
const nextSrc = `${nextLines.join(eol)}${eol}`
|
|
37
|
+
fs.writeFileSync(resolvedKeysFilepath, nextSrc, 'utf8')
|
|
38
|
+
} else {
|
|
39
|
+
fs.rmSync(resolvedKeysFilepath, { force: true })
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
return {
|
|
44
|
+
changed,
|
|
45
|
+
key,
|
|
46
|
+
filepath: keysFilepath
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
module.exports = removeEnvKey
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
const dotenvx = require('@dotenvx/dotenvx')
|
|
2
|
+
const PostArmorUp = require('../api/postArmorUp')
|
|
3
|
+
const keyNamesForEnvFile = require('../helpers/keyNamesForEnvFile')
|
|
4
|
+
const removeEnvKey = require('../helpers/removeEnvKey')
|
|
5
|
+
|
|
6
|
+
class ArmorUp {
|
|
7
|
+
constructor (hostname, token, devicePublicKey, envFile = '.env') {
|
|
8
|
+
this.hostname = hostname
|
|
9
|
+
this.token = token
|
|
10
|
+
this.devicePublicKey = devicePublicKey
|
|
11
|
+
this.envFile = envFile
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
async run () {
|
|
15
|
+
const hostname = this.hostname
|
|
16
|
+
const token = this.token
|
|
17
|
+
const devicePublicKey = this.devicePublicKey
|
|
18
|
+
const envFile = this.envFile
|
|
19
|
+
|
|
20
|
+
const {
|
|
21
|
+
publicKeyName,
|
|
22
|
+
privateKeyName
|
|
23
|
+
} = keyNamesForEnvFile(envFile)
|
|
24
|
+
|
|
25
|
+
const publicKey = dotenvx.get(publicKeyName, { path: envFile, strict: true, ignore: ['MISSING_PRIVATE_KEY'] })
|
|
26
|
+
const privateKey = dotenvx.get(privateKeyName, { path: '.env.keys', strict: true, ignore: ['MISSING_KEY'] })
|
|
27
|
+
const json = await new PostArmorUp(hostname, token, devicePublicKey, publicKey, privateKey).run()
|
|
28
|
+
removeEnvKey(privateKeyName)
|
|
29
|
+
|
|
30
|
+
return {
|
|
31
|
+
...json,
|
|
32
|
+
changed: json.changed,
|
|
33
|
+
privateKeyName,
|
|
34
|
+
privateKeyValue: json.private_key
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
module.exports = ArmorUp
|