@dotenvx/dotenvx-ops 0.17.0 → 0.18.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,19 @@
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.17.0...main)
5
+ [Unreleased](https://github.com/dotenvx/dotenvx-ops/compare/v0.18.0...main)
6
+
7
+ ## [0.18.0](https://github.com/dotenvx/dotenvx-ops/compare/v0.17.1...v0.18.0) (2025-10-06)
8
+
9
+ ### Added
10
+
11
+ * Display conflict diff ([#2](https://github.com/dotenvx/dotenvx-ops/pull/2))
12
+
13
+ ## [0.17.1](https://github.com/dotenvx/dotenvx-ops/compare/v0.17.0...v0.17.1) (2025-10-01)
14
+
15
+ ### Changed
16
+
17
+ * API sync output is `files` not `envFiles`
6
18
 
7
19
  ## [0.17.0](https://github.com/dotenvx/dotenvx-ops/compare/v0.16.1...v0.17.0) (2025-10-01)
8
20
 
package/package.json CHANGED
@@ -1,5 +1,5 @@
1
1
  {
2
- "version": "0.17.0",
2
+ "version": "0.18.0",
3
3
  "name": "@dotenvx/dotenvx-ops",
4
4
  "description": "Dotenvx Ops",
5
5
  "author": "@motdotla",
@@ -8,17 +8,25 @@ const smartMask = require('./../../lib/helpers/smartMask')
8
8
 
9
9
  const Sync = require('./../../lib/services/sync')
10
10
 
11
+ const GetSynchronization = require('./../../lib/api/getSynchronization')
12
+
11
13
  const spinner = createSpinner('syncing')
12
14
 
15
+ // move soon
16
+ const Session = require('./../../db/session')
17
+ const sesh = new Session()
18
+ const token = sesh.token()
19
+
13
20
  function sha256File (filepath) {
14
21
  const buf = fs.readFileSync(filepath)
15
22
  return crypto.createHash('sha256').update(buf).digest('hex')
16
23
  }
17
24
 
18
25
  async function sync () {
26
+ // debug opts
27
+ const options = this.opts()
28
+
19
29
  try {
20
- // debug opts
21
- const options = this.opts()
22
30
  logger.debug(`options: ${JSON.stringify(options)}`)
23
31
 
24
32
  spinner.start()
@@ -26,12 +34,12 @@ async function sync () {
26
34
  const {
27
35
  dotenvxProjectId,
28
36
  projectUsernameName,
29
- envFiles
37
+ files
30
38
  } = await new Sync(options.hostname).run()
31
- logger.debug(`envFiles: ${JSON.stringify(envFiles)}`)
39
+ logger.debug(`files: ${JSON.stringify(files)}`)
32
40
 
33
- // write envFiles
34
- for (const file of envFiles) {
41
+ // write output files
42
+ for (const file of files) {
35
43
  const { filepath, src, sha } = file
36
44
 
37
45
  let needsWrite = true
@@ -64,6 +72,20 @@ async function sync () {
64
72
  if (error.stack) {
65
73
  logger.debug(error.stack)
66
74
  }
75
+
76
+ // display conflict diff
77
+ if (error.code === 'DOTENVX_SYNC_CONFLICT') {
78
+ const synchronizationId = error.meta.synchronization_id
79
+ const data = await new GetSynchronization(options.hostname, token, synchronizationId).run()
80
+ const conflictedFile = data.files.find(f => f.status === 'conflicted')
81
+ if (conflictedFile) {
82
+ console.log(conflictedFile.filepath)
83
+ console.log(conflictedFile.diff_ansi)
84
+ } else {
85
+ console.log('No conflicted files found.')
86
+ }
87
+ }
88
+
67
89
  process.exit(1)
68
90
  }
69
91
  }
@@ -0,0 +1,34 @@
1
+ const { http } = require('../../lib/helpers/http')
2
+ const buildApiError = require('../../lib/helpers/buildApiError')
3
+
4
+ class GetSynchronization {
5
+ constructor (hostname, token, synchronizationId) {
6
+ this.hostname = hostname || 'https://ops.dotenvx.com'
7
+ this.token = token
8
+ this.synchronizationId = synchronizationId
9
+ }
10
+
11
+ async run () {
12
+ const token = this.token
13
+ const synchronizationId = this.synchronizationId
14
+ const url = `${this.hostname}/api/synchronization/${synchronizationId}`
15
+
16
+ const resp = await http(url, {
17
+ method: 'GET',
18
+ headers: {
19
+ Authorization: `Bearer ${token}`,
20
+ 'Content-Type': 'application/json'
21
+ }
22
+ })
23
+
24
+ const json = await resp.body.json()
25
+
26
+ if (resp.statusCode >= 400) {
27
+ throw buildApiError(resp.statusCode, json)
28
+ }
29
+
30
+ return json
31
+ }
32
+ }
33
+
34
+ module.exports = GetSynchronization
@@ -2,10 +2,12 @@ function buildApiError (statusCode, json) {
2
2
  const code = json.error.code || statusCode.toString()
3
3
  const message = `[${code}] ${json.error.message}`
4
4
  const help = `[${code}] ${json.error.help || JSON.stringify(json)}`
5
+ const meta = json.error.meta
5
6
 
6
7
  const error = new Error(message)
7
8
  error.code = code
8
9
  error.help = help
10
+ error.meta = meta
9
11
 
10
12
  return error
11
13
  }
@@ -48,7 +48,7 @@ class Sync {
48
48
  id: data.id,
49
49
  dotenvxProjectId: data.dotenvx_project_id,
50
50
  projectUsernameName: data.project_username_name,
51
- envFiles: data.env_files
51
+ files: data.files
52
52
  }
53
53
  }
54
54