diffora 0.1.0__tar.gz

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.
diffora-0.1.0/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 HashPalace
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
diffora-0.1.0/PKG-INFO ADDED
@@ -0,0 +1,292 @@
1
+ Metadata-Version: 2.4
2
+ Name: diffora
3
+ Version: 0.1.0
4
+ Summary: Secret-aware semantic configuration diffs for JSON, YAML, TOML and .env files.
5
+ Author: HashPalace
6
+ License: MIT
7
+ Project-URL: Homepage, https://github.com/hashpalace/ConfigDiff
8
+ Project-URL: Repository, https://github.com/hashpalace/ConfigDiff
9
+ Project-URL: Issues, https://github.com/hashpalace/ConfigDiff/issues
10
+ Project-URL: Documentation, https://github.com/hashpalace/ConfigDiff#readme
11
+ Keywords: config,configuration,configuration-diff,config-drift,diff,diff-tool,semantic-diff,devops,infrastructure,json,yaml,toml,dotenv,secrets,debugging,ci,github-actions
12
+ Classifier: Development Status :: 3 - Alpha
13
+ Classifier: Environment :: Console
14
+ Classifier: Intended Audience :: Developers
15
+ Classifier: Operating System :: OS Independent
16
+ Classifier: Topic :: Software Development
17
+ Classifier: Topic :: Utilities
18
+ Classifier: License :: OSI Approved :: MIT License
19
+ Classifier: Programming Language :: Python :: 3
20
+ Classifier: Programming Language :: Python :: 3.9
21
+ Classifier: Programming Language :: Python :: 3.10
22
+ Classifier: Programming Language :: Python :: 3.11
23
+ Classifier: Programming Language :: Python :: 3.12
24
+ Classifier: Programming Language :: Python :: 3.13
25
+ Requires-Python: >=3.9
26
+ Description-Content-Type: text/markdown
27
+ License-File: LICENSE
28
+ Requires-Dist: PyYAML>=6.0
29
+ Requires-Dist: orjson>=3.9
30
+ Requires-Dist: tomli>=2.0; python_version < "3.11"
31
+ Provides-Extra: test
32
+ Requires-Dist: build>=1.2; extra == "test"
33
+ Requires-Dist: coverage>=7.6; extra == "test"
34
+ Requires-Dist: hypothesis>=6.100; extra == "test"
35
+ Requires-Dist: ruff>=0.9; extra == "test"
36
+ Requires-Dist: twine>=6; extra == "test"
37
+ Dynamic: license-file
38
+
39
+ # Diffora
40
+
41
+ [![Tests](https://github.com/hashpalace/ConfigDiff/actions/workflows/tests.yml/badge.svg)](https://github.com/hashpalace/ConfigDiff/actions/workflows/tests.yml)
42
+ [![Python](https://img.shields.io/badge/python-3.9%2B-blue)](https://www.python.org/)
43
+ [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](LICENSE)
44
+ [![GitHub stars](https://img.shields.io/github/stars/hashpalace/ConfigDiff?style=social)](https://github.com/hashpalace/ConfigDiff/stargazers)
45
+ [![GitHub issues](https://img.shields.io/github/issues/hashpalace/ConfigDiff)](https://github.com/hashpalace/ConfigDiff/issues)
46
+
47
+ **Stop guessing what changed between configs.** Diffora compares parsed configuration values and pinpoints added, removed, and changed fields—even across different formats. Pass two directories to compare their file inventories and the semantic contents of supported configuration files.
48
+
49
+ JSON · YAML · TOML · `.env` · cross-format · directory comparison · secret-aware · CI-friendly
50
+
51
+ ## Why Diffora?
52
+
53
+ - **Custom redaction:** add `--redact PATH` rules for sensitive fields that don't use conventional secret-like names.
54
+
55
+ If this saves you time, [star the repository](https://github.com/hashpalace/ConfigDiff) and share it with another developer. Stars help other people discover useful open-source tools.
56
+
57
+ - **Useful paths, not noisy text diffs:** `services[0].port` or `services[id="api"].port`.
58
+ - **Added and removed objects are expanded:** see the individual leaf values instead of one giant object blob.
59
+ - **Match lists by identity:** reordering services won't create fake changes when you use `--list-key id`.
60
+ - **Ignore known differences:** repeat `--ignore` or use glob patterns.
61
+ - **Safer output by default:** values under sensitive-looking keys are masked.
62
+ - **Automation-ready:** JSON output and predictable exit codes.
63
+ - **Safe by default:** comparisons never modify input files; the separate `apply` command only writes when explicitly requested.
64
+
65
+ ## Install
66
+
67
+ From a local clone:
68
+
69
+ ```bash
70
+ python -m pip install .
71
+ ```
72
+
73
+ Or install the latest code directly from GitHub:
74
+
75
+ ```bash
76
+ python -m pip install "git+https://github.com/hashpalace/ConfigDiff.git"
77
+ ```
78
+
79
+ Install from PyPI with `python -m pip install diffora`. Requires Python 3.9+. YAML parsing uses PyYAML. Python 3.9 and 3.10 use `tomli` for TOML; Python 3.11+ uses the standard library. The `configdiff` command remains available as a compatibility alias.
80
+
81
+ ## Quick start
82
+
83
+ ```bash
84
+ diffora old.json new.json
85
+ diffora config.yaml config.toml
86
+ diffora .env.production .env.staging
87
+ diffora ./config/staging ./config/production
88
+ ```
89
+
90
+ Example output:
91
+
92
+ ```diff
93
+ + database.pool_size = 20
94
+ - legacy_endpoint = "https://old.example"
95
+ ~ database.port: 5432 -> 5433
96
+ ```
97
+
98
+ Try the included demo files from the repository root:
99
+
100
+ ```bash
101
+ diffora examples/before.json examples/after.json --summary
102
+ ```
103
+
104
+ Formats are detected from file extensions. For files with custom names, specify each format:
105
+
106
+ ```bash
107
+ diffora before.conf after.conf --left-format json --right-format yaml
108
+ ```
109
+
110
+ Supported format names: `json`, `yaml`, `toml`, `env`, and `dotenv`.
111
+
112
+ ## Compare lists by a stable key
113
+
114
+ By default, lists are compared by index. If entries are objects with a stable identifier, match them by that field to avoid noisy diffs when entries are reordered. `--list-key` also accepts comma-separated nested fields, such as `apiVersion,kind,metadata.name` for Kubernetes resources. Alternatively, `--ignore-order` compares lists as multisets: order is ignored but duplicate counts still matter; unmatched changed objects are represented as removals and additions:
115
+
116
+ ```bash
117
+ diffora old.json new.json --list-key id
118
+ diffora old.json new.json --ignore-order
119
+ ```
120
+
121
+ For example, changes are reported as `services[id="api"].port` instead of depending on the item's array position. The option applies to lists of objects that contain a unique, non-null value for the chosen key. If either list doesn't meet that requirement, Diffora safely falls back to index comparison. For Kubernetes resource lists, use --list-key kubernetes: resources are matched by apiVersion, kind, metadata.namespace, and metadata.name; a missing namespace is treated as the empty namespace for cluster-scoped resources.
122
+
123
+ ## Ignore paths
124
+
125
+ Pass `--ignore` multiple times to skip fields that are expected to differ:
126
+
127
+ ```bash
128
+ diffora old.yaml new.yaml --ignore metadata.generated_at --ignore 'services[*].replicas'
129
+ ```
130
+
131
+ Patterns support `*` (any sequence of characters) and `?` (one character). An exact path also ignores its descendants. For example, ignoring `database` ignores all of `database.host`, `database.port`, and nested paths below it.
132
+
133
+ ## JSON output and exit codes
134
+
135
+ Use machine-readable output in scripts. JSON output is a versioned object with `schema_version` and `differences`:
136
+
137
+ ```bash
138
+ diffora old.json new.json --format json
139
+ diffora old.json new.json --format json --summary
140
+ ```
141
+
142
+ Each difference includes `path`, `kind`, `old`, `new`, `old_missing`, and `new_missing`. The missing flags distinguish an absent value from an explicit JSON `null`. `kind` is `added`, `removed`, or `changed`. The output contract is documented in [`schemas/differences.schema.json`](schemas/differences.schema.json); consumers should check `schema_version` before relying on the structure.
143
+
144
+ | Exit code | Meaning |
145
+ | --- | --- |
146
+ | `0` | No differences found |
147
+ | `1` | Differences found |
148
+ | `2` | File, input, or parsing error |
149
+
150
+ For a concise text summary:
151
+
152
+ ```bash
153
+ diffora old.yaml new.yaml --summary
154
+ ```
155
+
156
+ Disable terminal colors with `--no-color`. Colors are otherwise enabled only when writing to an interactive terminal. Set the `NO_COLOR` environment variable to disable colors without changing your command.
157
+
158
+ ## Stdin, quiet mode, and output formats
159
+
160
+ Read one side from a pipe by using `-` and explicitly setting that side's format. A single stdin stream can supply only one side per invocation.
161
+
162
+ ```bash
163
+ cat new.json | diffora old.json - --right-format json
164
+ git show HEAD:config.yaml | diffora - config.yaml --left-format yaml
165
+ ```
166
+
167
+ For CI checks that only need a pass/fail result, `--quiet` suppresses standard output while preserving exit codes: `0` means identical, `1` means differences, and `2` means an input or parsing error.
168
+
169
+ ```bash
170
+ diffora production.yaml staging.yaml --quiet
171
+ ```
172
+
173
+ Choose `--format markdown` for a readable table in pull-request comments, `--format unified` for a unified diff of normalized paths and values, `--format github-actions` to emit escaped workflow warning annotations, or `--format json-patch` to generate an RFC 6902 patch:
174
+
175
+ ```bash
176
+ diffora before.json after.json --format markdown
177
+ diffora before.json after.json --format unified
178
+ diffora before.json after.json --format github-actions
179
+ diffora before.json after.json --format json-patch --show-secrets > patch.json
180
+ diffora apply patch.json before.json
181
+ ```
182
+
183
+ A unified output is a semantic diff of parsed paths and values, not a byte-for-byte diff of the source files.
184
+
185
+ ## `.env` parsing notes
186
+
187
+ Diffora handles common dotenv lines: `KEY=value`, optional `export KEY=value`, surrounding whitespace, blank values, quoted values, and comments. For quoted values, text after the closing quote must be whitespace or a comment. Values are compared literally: Diffora does not expand variables, execute shell syntax, or interpret escape sequences. Duplicate keys use the last value. This is a practical parser for comparison, not a full shell or dotenv runtime.
188
+
189
+ ## Secret handling
190
+
191
+ Built-in key-name masking is best-effort. Add project-specific paths or glob patterns with repeatable `--redact`; for example:
192
+
193
+ ```bash
194
+ diffora before.yaml after.yaml --redact database.url --redact 'services[*].endpoint'
195
+ ```
196
+
197
+ Exact custom paths also mask their descendants. Glob patterns use `*` and `?`. These rules are added to the built-in sensitive-key detection. `--show-secrets` disables both built-in and custom masking, so use it deliberately.
198
+
199
+
200
+
201
+ Values whose path names look sensitive (such as `password`, `token`, `secret`, `api_key`, or `private_key`) are masked by default in both text and JSON output.
202
+
203
+ ```bash
204
+ diffora old.env new.env
205
+ diffora old.env new.env --show-secrets
206
+ ```
207
+
208
+ Only use `--show-secrets` when you are certain the output won't be exposed. Redaction is a best-effort key-name safeguard, not a complete secret scanner.
209
+
210
+ ## Input safety limits
211
+
212
+ Diffora reads at most 16 MiB from each input, including standard input. YAML aliases that create cyclic references are rejected; YAML structures are limited to 500,000 nodes and 256 levels of nesting. Direct calls to the Python `compare_values` API receive the same cycle, node-count, and nesting checks. These safeguards help avoid runaway recursion and excessive resource use when comparing untrusted configuration files. Parser diagnostics report the format and location when available without echoing source values.
213
+
214
+ ## Custom redaction
215
+
216
+ Add project-specific paths or glob patterns when a sensitive field does not have an obvious secret-like name:
217
+
218
+ ```bash
219
+ diffora before.yaml after.yaml --redact database.url --redact 'services[*].endpoint'
220
+ ```
221
+
222
+ Exact custom paths also mask descendants. Glob patterns support `*` and `?`. These rules are added to built-in sensitive-key detection and apply to text, JSON, Markdown, and unified output. `--show-secrets` disables both built-in and custom masking; use it deliberately.
223
+
224
+ ## CLI reference
225
+
226
+ ```text
227
+ diffora LEFT RIGHT [options]
228
+
229
+ Options:
230
+ --left-format FORMAT Format of the original file
231
+ --right-format FORMAT Format of the comparison file
232
+ --format FORMAT Output: text, json, markdown, unified, github-actions, or json-patch
233
+ --show-secrets Display values under sensitive-looking keys
234
+ --redact PATH Redact an additional path or glob pattern; repeatable
235
+ --no-color Disable ANSI colors
236
+ --list-key KEY Match lists by stable key; use kubernetes for Kubernetes manifests
237
+ --ignore-order Ignore list order while preserving duplicate counts
238
+ --ignore PATH Ignore a path or glob pattern; repeatable
239
+ --summary Print counts of added, removed, and changed values
240
+ -h, --help Show help
241
+ ```
242
+
243
+ JSON Patch output may contain secret values needed to reproduce the target exactly, so `--format json-patch` requires the explicit `--show-secrets` opt-in. Review the patch before sharing it. `diffora apply PATCH.json TARGET.json` applies add/remove/replace operations to a JSON target file; the target is only rewritten after the patch validates and applies successfully. Small lists use bounded LCS matching to generate minimal add/remove operations; very large list comparisons fall back to replacing the array to keep patch generation predictable.
244
+
245
+ ## Shell and CI examples
246
+
247
+ Fail a shell script when configuration values differ:
248
+
249
+ ```bash
250
+ set -eu
251
+ diffora config.example.yaml config.production.yaml --quiet
252
+ ```
253
+
254
+ GitHub Actions step that fails only when differences exist (parse errors also fail):
255
+
256
+ ```yaml
257
+ - name: Check configuration drift
258
+ run: diffora config/staging.yaml config/production.yaml --quiet
259
+ ```
260
+
261
+ If you want to show the differences in a CI log, omit `--quiet`; use `--format json` when a later step consumes the structured output.
262
+
263
+ ## Development
264
+
265
+ ```bash
266
+ git clone https://github.com/hashpalace/ConfigDiff.git
267
+ cd ConfigDiff
268
+ python -m pip install -e .
269
+ python -m unittest discover -s tests -v
270
+ ```
271
+
272
+ GitHub Actions tests Python 3.9–3.13.
273
+
274
+ ## Changelog
275
+
276
+ See [CHANGELOG.md](CHANGELOG.md) for version history.
277
+
278
+ ## Real-world examples
279
+
280
+ The `examples/` directory includes a small service configuration comparison. Run `diffora examples/services-before.yaml examples/services-after.yaml --list-key id --summary` to see it. The `benchmarks/` directory contains paired 100-service JSON fixtures for repeatable performance checks; run `diffora benchmarks/before.json benchmarks/after.json --list-key id --summary`. To measure Diffora's core latency and enforce the 5 ms median budget, use `python -m benchmarks.bench_compare --runs 50 --budget-ms 5`. To compare the same synthetic changes against DeepDiff, jd, and diffyml, install those tools and run `python -m benchmarks.bench_competitors --runs 30 --require-all`. The cross-tool report separates in-process library timings from end-to-end CLI timings (including process startup and parsing); diffyml receives an equivalent YAML serialization of the same dataset. All values are synthetic and illustrative, not production configuration.
281
+
282
+ ## Roadmap
283
+
284
+ Milestones and contribution ideas live in [ROADMAP.md](ROADMAP.md).
285
+
286
+ ## Contributing
287
+
288
+ Bug reports, ideas, and pull requests are welcome. Please include a focused test for behavior changes and read [CONTRIBUTING.md](CONTRIBUTING.md) before submitting a PR.
289
+
290
+ ## License
291
+
292
+ MIT — see [LICENSE](LICENSE).
@@ -0,0 +1,254 @@
1
+ # Diffora
2
+
3
+ [![Tests](https://github.com/hashpalace/ConfigDiff/actions/workflows/tests.yml/badge.svg)](https://github.com/hashpalace/ConfigDiff/actions/workflows/tests.yml)
4
+ [![Python](https://img.shields.io/badge/python-3.9%2B-blue)](https://www.python.org/)
5
+ [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](LICENSE)
6
+ [![GitHub stars](https://img.shields.io/github/stars/hashpalace/ConfigDiff?style=social)](https://github.com/hashpalace/ConfigDiff/stargazers)
7
+ [![GitHub issues](https://img.shields.io/github/issues/hashpalace/ConfigDiff)](https://github.com/hashpalace/ConfigDiff/issues)
8
+
9
+ **Stop guessing what changed between configs.** Diffora compares parsed configuration values and pinpoints added, removed, and changed fields—even across different formats. Pass two directories to compare their file inventories and the semantic contents of supported configuration files.
10
+
11
+ JSON · YAML · TOML · `.env` · cross-format · directory comparison · secret-aware · CI-friendly
12
+
13
+ ## Why Diffora?
14
+
15
+ - **Custom redaction:** add `--redact PATH` rules for sensitive fields that don't use conventional secret-like names.
16
+
17
+ If this saves you time, [star the repository](https://github.com/hashpalace/ConfigDiff) and share it with another developer. Stars help other people discover useful open-source tools.
18
+
19
+ - **Useful paths, not noisy text diffs:** `services[0].port` or `services[id="api"].port`.
20
+ - **Added and removed objects are expanded:** see the individual leaf values instead of one giant object blob.
21
+ - **Match lists by identity:** reordering services won't create fake changes when you use `--list-key id`.
22
+ - **Ignore known differences:** repeat `--ignore` or use glob patterns.
23
+ - **Safer output by default:** values under sensitive-looking keys are masked.
24
+ - **Automation-ready:** JSON output and predictable exit codes.
25
+ - **Safe by default:** comparisons never modify input files; the separate `apply` command only writes when explicitly requested.
26
+
27
+ ## Install
28
+
29
+ From a local clone:
30
+
31
+ ```bash
32
+ python -m pip install .
33
+ ```
34
+
35
+ Or install the latest code directly from GitHub:
36
+
37
+ ```bash
38
+ python -m pip install "git+https://github.com/hashpalace/ConfigDiff.git"
39
+ ```
40
+
41
+ Install from PyPI with `python -m pip install diffora`. Requires Python 3.9+. YAML parsing uses PyYAML. Python 3.9 and 3.10 use `tomli` for TOML; Python 3.11+ uses the standard library. The `configdiff` command remains available as a compatibility alias.
42
+
43
+ ## Quick start
44
+
45
+ ```bash
46
+ diffora old.json new.json
47
+ diffora config.yaml config.toml
48
+ diffora .env.production .env.staging
49
+ diffora ./config/staging ./config/production
50
+ ```
51
+
52
+ Example output:
53
+
54
+ ```diff
55
+ + database.pool_size = 20
56
+ - legacy_endpoint = "https://old.example"
57
+ ~ database.port: 5432 -> 5433
58
+ ```
59
+
60
+ Try the included demo files from the repository root:
61
+
62
+ ```bash
63
+ diffora examples/before.json examples/after.json --summary
64
+ ```
65
+
66
+ Formats are detected from file extensions. For files with custom names, specify each format:
67
+
68
+ ```bash
69
+ diffora before.conf after.conf --left-format json --right-format yaml
70
+ ```
71
+
72
+ Supported format names: `json`, `yaml`, `toml`, `env`, and `dotenv`.
73
+
74
+ ## Compare lists by a stable key
75
+
76
+ By default, lists are compared by index. If entries are objects with a stable identifier, match them by that field to avoid noisy diffs when entries are reordered. `--list-key` also accepts comma-separated nested fields, such as `apiVersion,kind,metadata.name` for Kubernetes resources. Alternatively, `--ignore-order` compares lists as multisets: order is ignored but duplicate counts still matter; unmatched changed objects are represented as removals and additions:
77
+
78
+ ```bash
79
+ diffora old.json new.json --list-key id
80
+ diffora old.json new.json --ignore-order
81
+ ```
82
+
83
+ For example, changes are reported as `services[id="api"].port` instead of depending on the item's array position. The option applies to lists of objects that contain a unique, non-null value for the chosen key. If either list doesn't meet that requirement, Diffora safely falls back to index comparison. For Kubernetes resource lists, use --list-key kubernetes: resources are matched by apiVersion, kind, metadata.namespace, and metadata.name; a missing namespace is treated as the empty namespace for cluster-scoped resources.
84
+
85
+ ## Ignore paths
86
+
87
+ Pass `--ignore` multiple times to skip fields that are expected to differ:
88
+
89
+ ```bash
90
+ diffora old.yaml new.yaml --ignore metadata.generated_at --ignore 'services[*].replicas'
91
+ ```
92
+
93
+ Patterns support `*` (any sequence of characters) and `?` (one character). An exact path also ignores its descendants. For example, ignoring `database` ignores all of `database.host`, `database.port`, and nested paths below it.
94
+
95
+ ## JSON output and exit codes
96
+
97
+ Use machine-readable output in scripts. JSON output is a versioned object with `schema_version` and `differences`:
98
+
99
+ ```bash
100
+ diffora old.json new.json --format json
101
+ diffora old.json new.json --format json --summary
102
+ ```
103
+
104
+ Each difference includes `path`, `kind`, `old`, `new`, `old_missing`, and `new_missing`. The missing flags distinguish an absent value from an explicit JSON `null`. `kind` is `added`, `removed`, or `changed`. The output contract is documented in [`schemas/differences.schema.json`](schemas/differences.schema.json); consumers should check `schema_version` before relying on the structure.
105
+
106
+ | Exit code | Meaning |
107
+ | --- | --- |
108
+ | `0` | No differences found |
109
+ | `1` | Differences found |
110
+ | `2` | File, input, or parsing error |
111
+
112
+ For a concise text summary:
113
+
114
+ ```bash
115
+ diffora old.yaml new.yaml --summary
116
+ ```
117
+
118
+ Disable terminal colors with `--no-color`. Colors are otherwise enabled only when writing to an interactive terminal. Set the `NO_COLOR` environment variable to disable colors without changing your command.
119
+
120
+ ## Stdin, quiet mode, and output formats
121
+
122
+ Read one side from a pipe by using `-` and explicitly setting that side's format. A single stdin stream can supply only one side per invocation.
123
+
124
+ ```bash
125
+ cat new.json | diffora old.json - --right-format json
126
+ git show HEAD:config.yaml | diffora - config.yaml --left-format yaml
127
+ ```
128
+
129
+ For CI checks that only need a pass/fail result, `--quiet` suppresses standard output while preserving exit codes: `0` means identical, `1` means differences, and `2` means an input or parsing error.
130
+
131
+ ```bash
132
+ diffora production.yaml staging.yaml --quiet
133
+ ```
134
+
135
+ Choose `--format markdown` for a readable table in pull-request comments, `--format unified` for a unified diff of normalized paths and values, `--format github-actions` to emit escaped workflow warning annotations, or `--format json-patch` to generate an RFC 6902 patch:
136
+
137
+ ```bash
138
+ diffora before.json after.json --format markdown
139
+ diffora before.json after.json --format unified
140
+ diffora before.json after.json --format github-actions
141
+ diffora before.json after.json --format json-patch --show-secrets > patch.json
142
+ diffora apply patch.json before.json
143
+ ```
144
+
145
+ A unified output is a semantic diff of parsed paths and values, not a byte-for-byte diff of the source files.
146
+
147
+ ## `.env` parsing notes
148
+
149
+ Diffora handles common dotenv lines: `KEY=value`, optional `export KEY=value`, surrounding whitespace, blank values, quoted values, and comments. For quoted values, text after the closing quote must be whitespace or a comment. Values are compared literally: Diffora does not expand variables, execute shell syntax, or interpret escape sequences. Duplicate keys use the last value. This is a practical parser for comparison, not a full shell or dotenv runtime.
150
+
151
+ ## Secret handling
152
+
153
+ Built-in key-name masking is best-effort. Add project-specific paths or glob patterns with repeatable `--redact`; for example:
154
+
155
+ ```bash
156
+ diffora before.yaml after.yaml --redact database.url --redact 'services[*].endpoint'
157
+ ```
158
+
159
+ Exact custom paths also mask their descendants. Glob patterns use `*` and `?`. These rules are added to the built-in sensitive-key detection. `--show-secrets` disables both built-in and custom masking, so use it deliberately.
160
+
161
+
162
+
163
+ Values whose path names look sensitive (such as `password`, `token`, `secret`, `api_key`, or `private_key`) are masked by default in both text and JSON output.
164
+
165
+ ```bash
166
+ diffora old.env new.env
167
+ diffora old.env new.env --show-secrets
168
+ ```
169
+
170
+ Only use `--show-secrets` when you are certain the output won't be exposed. Redaction is a best-effort key-name safeguard, not a complete secret scanner.
171
+
172
+ ## Input safety limits
173
+
174
+ Diffora reads at most 16 MiB from each input, including standard input. YAML aliases that create cyclic references are rejected; YAML structures are limited to 500,000 nodes and 256 levels of nesting. Direct calls to the Python `compare_values` API receive the same cycle, node-count, and nesting checks. These safeguards help avoid runaway recursion and excessive resource use when comparing untrusted configuration files. Parser diagnostics report the format and location when available without echoing source values.
175
+
176
+ ## Custom redaction
177
+
178
+ Add project-specific paths or glob patterns when a sensitive field does not have an obvious secret-like name:
179
+
180
+ ```bash
181
+ diffora before.yaml after.yaml --redact database.url --redact 'services[*].endpoint'
182
+ ```
183
+
184
+ Exact custom paths also mask descendants. Glob patterns support `*` and `?`. These rules are added to built-in sensitive-key detection and apply to text, JSON, Markdown, and unified output. `--show-secrets` disables both built-in and custom masking; use it deliberately.
185
+
186
+ ## CLI reference
187
+
188
+ ```text
189
+ diffora LEFT RIGHT [options]
190
+
191
+ Options:
192
+ --left-format FORMAT Format of the original file
193
+ --right-format FORMAT Format of the comparison file
194
+ --format FORMAT Output: text, json, markdown, unified, github-actions, or json-patch
195
+ --show-secrets Display values under sensitive-looking keys
196
+ --redact PATH Redact an additional path or glob pattern; repeatable
197
+ --no-color Disable ANSI colors
198
+ --list-key KEY Match lists by stable key; use kubernetes for Kubernetes manifests
199
+ --ignore-order Ignore list order while preserving duplicate counts
200
+ --ignore PATH Ignore a path or glob pattern; repeatable
201
+ --summary Print counts of added, removed, and changed values
202
+ -h, --help Show help
203
+ ```
204
+
205
+ JSON Patch output may contain secret values needed to reproduce the target exactly, so `--format json-patch` requires the explicit `--show-secrets` opt-in. Review the patch before sharing it. `diffora apply PATCH.json TARGET.json` applies add/remove/replace operations to a JSON target file; the target is only rewritten after the patch validates and applies successfully. Small lists use bounded LCS matching to generate minimal add/remove operations; very large list comparisons fall back to replacing the array to keep patch generation predictable.
206
+
207
+ ## Shell and CI examples
208
+
209
+ Fail a shell script when configuration values differ:
210
+
211
+ ```bash
212
+ set -eu
213
+ diffora config.example.yaml config.production.yaml --quiet
214
+ ```
215
+
216
+ GitHub Actions step that fails only when differences exist (parse errors also fail):
217
+
218
+ ```yaml
219
+ - name: Check configuration drift
220
+ run: diffora config/staging.yaml config/production.yaml --quiet
221
+ ```
222
+
223
+ If you want to show the differences in a CI log, omit `--quiet`; use `--format json` when a later step consumes the structured output.
224
+
225
+ ## Development
226
+
227
+ ```bash
228
+ git clone https://github.com/hashpalace/ConfigDiff.git
229
+ cd ConfigDiff
230
+ python -m pip install -e .
231
+ python -m unittest discover -s tests -v
232
+ ```
233
+
234
+ GitHub Actions tests Python 3.9–3.13.
235
+
236
+ ## Changelog
237
+
238
+ See [CHANGELOG.md](CHANGELOG.md) for version history.
239
+
240
+ ## Real-world examples
241
+
242
+ The `examples/` directory includes a small service configuration comparison. Run `diffora examples/services-before.yaml examples/services-after.yaml --list-key id --summary` to see it. The `benchmarks/` directory contains paired 100-service JSON fixtures for repeatable performance checks; run `diffora benchmarks/before.json benchmarks/after.json --list-key id --summary`. To measure Diffora's core latency and enforce the 5 ms median budget, use `python -m benchmarks.bench_compare --runs 50 --budget-ms 5`. To compare the same synthetic changes against DeepDiff, jd, and diffyml, install those tools and run `python -m benchmarks.bench_competitors --runs 30 --require-all`. The cross-tool report separates in-process library timings from end-to-end CLI timings (including process startup and parsing); diffyml receives an equivalent YAML serialization of the same dataset. All values are synthetic and illustrative, not production configuration.
243
+
244
+ ## Roadmap
245
+
246
+ Milestones and contribution ideas live in [ROADMAP.md](ROADMAP.md).
247
+
248
+ ## Contributing
249
+
250
+ Bug reports, ideas, and pull requests are welcome. Please include a focused test for behavior changes and read [CONTRIBUTING.md](CONTRIBUTING.md) before submitting a PR.
251
+
252
+ ## License
253
+
254
+ MIT — see [LICENSE](LICENSE).
@@ -0,0 +1,5 @@
1
+ __version__ = "0.1.0"
2
+
3
+ from .core import ConfigDiffError, Difference, compare_files, compare_values, load_config
4
+
5
+ __all__ = ["ConfigDiffError", "Difference", "compare_files", "compare_values", "load_config", "__version__"]