boundver 0.9.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.
- boundver-0.9.0/LICENSE +21 -0
- boundver-0.9.0/PKG-INFO +441 -0
- boundver-0.9.0/README.md +421 -0
- boundver-0.9.0/pyproject.toml +27 -0
- boundver-0.9.0/setup.cfg +4 -0
- boundver-0.9.0/src/boundver/__init__.py +12 -0
- boundver-0.9.0/src/boundver/_completions.py +259 -0
- boundver-0.9.0/src/boundver/_config.py +521 -0
- boundver-0.9.0/src/boundver/_diff.py +77 -0
- boundver-0.9.0/src/boundver/_git.py +229 -0
- boundver-0.9.0/src/boundver/_hashing.py +141 -0
- boundver-0.9.0/src/boundver/_lockfile.py +449 -0
- boundver-0.9.0/src/boundver/_output.py +347 -0
- boundver-0.9.0/src/boundver/cli.py +8 -0
- boundver-0.9.0/src/boundver/core.py +608 -0
- boundver-0.9.0/src/boundver/providers.py +671 -0
- boundver-0.9.0/src/boundver/versions.py +294 -0
- boundver-0.9.0/src/boundver.egg-info/PKG-INFO +441 -0
- boundver-0.9.0/src/boundver.egg-info/SOURCES.txt +28 -0
- boundver-0.9.0/src/boundver.egg-info/dependency_links.txt +1 -0
- boundver-0.9.0/src/boundver.egg-info/entry_points.txt +2 -0
- boundver-0.9.0/src/boundver.egg-info/requires.txt +12 -0
- boundver-0.9.0/src/boundver.egg-info/top_level.txt +1 -0
- boundver-0.9.0/tests/test_boundary_lock.py +3083 -0
- boundver-0.9.0/tests/test_cli_main.py +1346 -0
- boundver-0.9.0/tests/test_cli_output_schemas.py +146 -0
- boundver-0.9.0/tests/test_core_branches.py +2679 -0
- boundver-0.9.0/tests/test_hashing.py +802 -0
- boundver-0.9.0/tests/test_providers.py +931 -0
- boundver-0.9.0/tests/test_versions.py +782 -0
boundver-0.9.0/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 boundver contributors
|
|
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.
|
boundver-0.9.0/PKG-INFO
ADDED
|
@@ -0,0 +1,441 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: boundver
|
|
3
|
+
Version: 0.9.0
|
|
4
|
+
Summary: Automated change-type classification for components that lack static verification
|
|
5
|
+
Author: boundver contributors
|
|
6
|
+
License: MIT
|
|
7
|
+
Requires-Python: >=3.8
|
|
8
|
+
Description-Content-Type: text/markdown
|
|
9
|
+
License-File: LICENSE
|
|
10
|
+
Provides-Extra: schema
|
|
11
|
+
Requires-Dist: jsonschema>=4; extra == "schema"
|
|
12
|
+
Provides-Extra: yaml
|
|
13
|
+
Requires-Dist: PyYAML>=6; extra == "yaml"
|
|
14
|
+
Provides-Extra: dev
|
|
15
|
+
Requires-Dist: pytest<9,>=7; extra == "dev"
|
|
16
|
+
Requires-Dist: jsonschema>=4; extra == "dev"
|
|
17
|
+
Requires-Dist: build>=1.2; extra == "dev"
|
|
18
|
+
Requires-Dist: PyYAML>=6; extra == "dev"
|
|
19
|
+
Dynamic: license-file
|
|
20
|
+
|
|
21
|
+
# boundver
|
|
22
|
+
|
|
23
|
+
[](https://pypi.org/project/boundver/)
|
|
24
|
+
[](https://pypi.org/project/boundver/)
|
|
25
|
+
[](LICENSE)
|
|
26
|
+
[](pyproject.toml)
|
|
27
|
+
|
|
28
|
+
**Automated change-type classification for components that lack static verification.**
|
|
29
|
+
|
|
30
|
+
boundver answers four questions per component — *did anything change?*, *did the behavioral contract change?*, *did the declared boundary change?*, *is it still compatible?* — using content-addressed fingerprints derived from Git state and declared boundary files. No external dependencies. No build system required.
|
|
31
|
+
|
|
32
|
+
## Why
|
|
33
|
+
|
|
34
|
+
When a component has consumers but no compiler or type system verifying its interface — services exposing OpenAPI specs, Python libraries, config-driven systems, internal platforms — there's no machine that tells you whether a change is internal, boundary-affecting, or breaking.
|
|
35
|
+
|
|
36
|
+
boundver fills that gap. It lets you **declare** what constitutes your component's boundary, then **automatically classifies every change** into one of four categories:
|
|
37
|
+
|
|
38
|
+
- **Implementation-only** — internals changed, boundary stable, consumers unaffected.
|
|
39
|
+
- **Behavioral contract change** — defaults/config/migrations changed, API shape stable, consumers may need to re-verify.
|
|
40
|
+
- **Boundary change** — the declared contract changed, consumers should re-verify.
|
|
41
|
+
- **Compatibility break** — the compatibility family changed, deployment coordination required.
|
|
42
|
+
|
|
43
|
+
This is the information that CI, consumers, and operators each need — derived deterministically from repo state, not from human discipline or commit-message conventions.
|
|
44
|
+
|
|
45
|
+
### When to use boundver
|
|
46
|
+
|
|
47
|
+
boundver is for any component whose boundary has consumers but **no static verification** — no compiler checking signatures, no type system enforcing contracts. That includes most services, most Python/Go libraries, most YAML/JSON-defined APIs, and most internal platforms.
|
|
48
|
+
|
|
49
|
+
| Tool | Sweet spot | Skip if… |
|
|
50
|
+
|---|---|---|
|
|
51
|
+
| **Nx / Turborepo** | JS/TS monorepos with task graphs and caching | You have a polyglot repo or can't adopt a full task runner |
|
|
52
|
+
| **Bazel / Pants** | Large-scale build + dependency graph orchestration | Adoption cost exceeds value for your team size |
|
|
53
|
+
| **TypeScript / Rust compiler** | Statically verified API contracts within a single language | Your entire stack is one statically-typed language |
|
|
54
|
+
| **boundver** | Any language — automated change classification where no static verifier exists | You already have affected-graph + cache-key tooling that satisfies all four questions |
|
|
55
|
+
|
|
56
|
+
For full tool-selection guidance, see [docs/WHY_BOUNDVER.md](docs/WHY_BOUNDVER.md).
|
|
57
|
+
|
|
58
|
+
## How it works
|
|
59
|
+
|
|
60
|
+
Each component gets four fingerprints forming a strict containment hierarchy (`exact ⊇ behavior ⊇ boundary`):
|
|
61
|
+
|
|
62
|
+
| Fingerprint | Question it answers | What it hashes |
|
|
63
|
+
|---|---|---|
|
|
64
|
+
| `exact` | Did anything change? | All tracked files in the component path |
|
|
65
|
+
| `behavior` | Did the behavioral contract change? | Declared contract files: boundary + config + migrations + contract tests |
|
|
66
|
+
| `boundary` | Did the API surface shape change? | Only the declared boundary files (e.g. `openapi.yaml`, `__init__.py`) |
|
|
67
|
+
| `compat` | Is it still in the same compatibility family? | Derived from SemVer major version |
|
|
68
|
+
|
|
69
|
+
This gives you four distinct change classifications:
|
|
70
|
+
|
|
71
|
+
| What changed | Meaning |
|
|
72
|
+
|---|---|
|
|
73
|
+
| Only `exact` | Pure internal refactor — consumers unaffected |
|
|
74
|
+
| `exact` + `behavior` | Behavioral contract changed (defaults, config, migrations) — API shape stable but consumers may be affected |
|
|
75
|
+
| `exact` + `behavior` + `boundary` | API surface changed — consumers must re-verify |
|
|
76
|
+
| All four | Breaking change — compatibility family changed |
|
|
77
|
+
|
|
78
|
+
Components are grouped into **slices** — named subsets with their own stable fingerprints. Adding an unrelated component changes the full-project hash but leaves existing slice fingerprints untouched.
|
|
79
|
+
|
|
80
|
+
> **Note:** `boundary` and `behavior` are **declared-file fingerprints**, not semantic analysis. They detect changes in files you declare as contract-relevant. The `openapi-canonical` and `json-canonical` providers go further — they strip non-contract content (descriptions, comments, formatting) so only structural changes trigger the fingerprint.
|
|
81
|
+
|
|
82
|
+
Each component also reports `boundary_status` in lock output:
|
|
83
|
+
- `ok`: boundary paths were declared and hashed successfully
|
|
84
|
+
- `partial`: boundary provider is `implicit` and no boundary paths are declared (API fingerprint is `null`)
|
|
85
|
+
- `error`: explicit boundary provider has no paths, or declared paths produced no API digest
|
|
86
|
+
|
|
87
|
+
## Quick start
|
|
88
|
+
|
|
89
|
+
```bash
|
|
90
|
+
# Install
|
|
91
|
+
pip install boundver
|
|
92
|
+
|
|
93
|
+
# Create a starter config
|
|
94
|
+
boundver init
|
|
95
|
+
# Or auto-discover components from common manifests
|
|
96
|
+
boundver init --discover
|
|
97
|
+
# Custom path / overwrite existing
|
|
98
|
+
boundver init --out boundary.config.json --force
|
|
99
|
+
|
|
100
|
+
# Or create manually (see Config Reference below)
|
|
101
|
+
cat > boundary.config.json << 'EOF'
|
|
102
|
+
{
|
|
103
|
+
"project": "my-project",
|
|
104
|
+
"components": {
|
|
105
|
+
"auth-service": {
|
|
106
|
+
"path": "services/auth",
|
|
107
|
+
"version_source": { "file": "package.json", "field": "version" },
|
|
108
|
+
"boundary": {
|
|
109
|
+
"provider": "openapi",
|
|
110
|
+
"paths": ["openapi.yaml"]
|
|
111
|
+
},
|
|
112
|
+
"behavior": {
|
|
113
|
+
"paths": ["openapi.yaml", "config/defaults.json"]
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
},
|
|
117
|
+
"slices": {
|
|
118
|
+
"auth-api": {
|
|
119
|
+
"description": "Auth service public API",
|
|
120
|
+
"mode": "boundary",
|
|
121
|
+
"components": ["auth-service"]
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
EOF
|
|
126
|
+
|
|
127
|
+
# Generate the lockfile
|
|
128
|
+
boundver generate
|
|
129
|
+
|
|
130
|
+
# Regenerate only selected components (and affected slices)
|
|
131
|
+
boundver generate --components auth-service,billing-service
|
|
132
|
+
|
|
133
|
+
# Preview generation without writing boundary.lock.json
|
|
134
|
+
boundver generate --dry-run
|
|
135
|
+
|
|
136
|
+
# Check current status
|
|
137
|
+
boundver status
|
|
138
|
+
|
|
139
|
+
# Verify lockfile matches repo state
|
|
140
|
+
boundver verify
|
|
141
|
+
|
|
142
|
+
# Verify only selected components
|
|
143
|
+
boundver verify --components auth-service,billing-service
|
|
144
|
+
|
|
145
|
+
# Verify only components changed since main
|
|
146
|
+
boundver verify --changed-from origin/main
|
|
147
|
+
|
|
148
|
+
# JSON output for automation
|
|
149
|
+
boundver verify --format json
|
|
150
|
+
|
|
151
|
+
# Logging controls
|
|
152
|
+
boundver --quiet status
|
|
153
|
+
boundver --verbose verify
|
|
154
|
+
|
|
155
|
+
# Diff two lockfiles
|
|
156
|
+
boundver diff old.lock.json boundary.lock.json
|
|
157
|
+
|
|
158
|
+
# Inspect a specific slice
|
|
159
|
+
boundver slice auth-api
|
|
160
|
+
|
|
161
|
+
# Preview discovered components
|
|
162
|
+
boundver discover --format json
|
|
163
|
+
```
|
|
164
|
+
|
|
165
|
+
## Behavior matrix
|
|
166
|
+
|
|
167
|
+
| Event | exact | behavior | boundary | compat |
|
|
168
|
+
|---|---|---|---|---|
|
|
169
|
+
| Bug fix (no API change) | ✓ changes | unchanged | unchanged | unchanged |
|
|
170
|
+
| Config/default/migration change | ✓ changes | ✓ changes | unchanged | unchanged |
|
|
171
|
+
| New API endpoint added | ✓ changes | ✓ changes | ✓ changes | unchanged |
|
|
172
|
+
| Breaking change + major bump | ✓ changes | ✓ changes | ✓ changes | ✓ changes |
|
|
173
|
+
| Internal refactor | ✓ changes | unchanged | unchanged | unchanged |
|
|
174
|
+
| New unrelated component added | slice unchanged | slice unchanged | slice unchanged | n/a |
|
|
175
|
+
|
|
176
|
+
## Config reference
|
|
177
|
+
|
|
178
|
+
### `boundary.config.json`
|
|
179
|
+
|
|
180
|
+
Schema file: `boundary.config.schema.json` (Draft 2020-12).
|
|
181
|
+
|
|
182
|
+
```json
|
|
183
|
+
{
|
|
184
|
+
"$schema": "https://raw.githubusercontent.com/yzm1/boundver/main/boundary.config.schema.json",
|
|
185
|
+
"project": "my-project",
|
|
186
|
+
"defaults": {
|
|
187
|
+
"compat_mode": "major"
|
|
188
|
+
},
|
|
189
|
+
"components": {
|
|
190
|
+
"component-name": {
|
|
191
|
+
"path": "relative/path/from/repo/root",
|
|
192
|
+
"ecosystem": "python | typescript | cloudformation",
|
|
193
|
+
"version_source": {
|
|
194
|
+
"file": "package.json",
|
|
195
|
+
"field": "version"
|
|
196
|
+
},
|
|
197
|
+
"boundary": {
|
|
198
|
+
"provider": "openapi | python-exports | typescript-exports | leaf | implicit",
|
|
199
|
+
"paths": ["openapi.yaml"],
|
|
200
|
+
"note": "optional explanation"
|
|
201
|
+
},
|
|
202
|
+
"behavior": {
|
|
203
|
+
"paths": ["openapi.yaml", "config/defaults.json"]
|
|
204
|
+
},
|
|
205
|
+
"vendored_copies": ["path/to/vendored/copy/"]
|
|
206
|
+
}
|
|
207
|
+
},
|
|
208
|
+
"slices": {
|
|
209
|
+
"slice-name": {
|
|
210
|
+
"description": "Human-readable purpose",
|
|
211
|
+
"mode": "exact | behavior | boundary | compat",
|
|
212
|
+
"components": ["component-a", "component-b"]
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
```
|
|
217
|
+
|
|
218
|
+
### Version source options
|
|
219
|
+
|
|
220
|
+
```json
|
|
221
|
+
// From a JSON/TOML/YAML file field:
|
|
222
|
+
"version_source": { "file": "pyproject.toml", "field": "project.version" }
|
|
223
|
+
|
|
224
|
+
// From git tags:
|
|
225
|
+
"version_source": { "git_tag_prefix": "auth-service-v" }
|
|
226
|
+
|
|
227
|
+
// No version tracking:
|
|
228
|
+
"version_source": null
|
|
229
|
+
```
|
|
230
|
+
|
|
231
|
+
### Boundary providers
|
|
232
|
+
|
|
233
|
+
| Provider | Meaning |
|
|
234
|
+
|---|---|
|
|
235
|
+
| `openapi` | OpenAPI/Swagger spec defines the API surface |
|
|
236
|
+
| `python-exports` | `__init__.py` or `__all__` exports define the boundary |
|
|
237
|
+
| `typescript-exports` | `.d.ts` or `index.ts` exports define the boundary |
|
|
238
|
+
| `json-file` | Generic JSON boundary artifact defines the contract |
|
|
239
|
+
| `custom.example.service-definition.v1` | Example custom provider namespace |
|
|
240
|
+
| `leaf` | No downstream consumers — boundary is the component itself |
|
|
241
|
+
| `implicit` | No explicit boundary artifact yet (`boundary` fingerprint will be `null`) |
|
|
242
|
+
|
|
243
|
+
### Provider capability matrix
|
|
244
|
+
|
|
245
|
+
| Provider | Semantic parser? | Requires `paths` | Empty `paths` allowed | Output |
|
|
246
|
+
|---|---:|---:|---:|---|
|
|
247
|
+
| `openapi` | No (raw file digest) | Yes | No | Raw boundary digest |
|
|
248
|
+
| `python-exports` | No (raw file digest) | Yes | No | Raw boundary digest |
|
|
249
|
+
| `typescript-exports` | No (raw file digest) | Yes | No | Raw boundary digest |
|
|
250
|
+
| `json-file` | No (raw file digest) | Yes | No | Raw boundary digest |
|
|
251
|
+
| `leaf` | n/a | No | Yes | No boundary digest required |
|
|
252
|
+
| `implicit` | n/a | No | Yes | `boundary_status=partial` |
|
|
253
|
+
| `custom.*` | Depends on implementation | Usually | Depends | Raw digest by default |
|
|
254
|
+
|
|
255
|
+
> Built-in providers are currently raw-boundary artifact hashers, not semantic API diff engines.
|
|
256
|
+
|
|
257
|
+
|
|
258
|
+
## Near-term implementation focus
|
|
259
|
+
|
|
260
|
+
boundver remains a public, language-agnostic tool. Near-term work is focused on:
|
|
261
|
+
|
|
262
|
+
- strict config validation and no silent fingerprint fallback
|
|
263
|
+
- explicit source mode behavior (`head`, `index`, `working-tree`)
|
|
264
|
+
- portability for external users (no implicit dependency on internal/proprietary boundary artifacts)
|
|
265
|
+
|
|
266
|
+
Short term deliverables: `validate-config`, strict digest selection, explicit source modes, and public examples that avoid proprietary dependencies.
|
|
267
|
+
|
|
268
|
+
## CI integration
|
|
269
|
+
|
|
270
|
+
For lockfile merge conflict handling, see [docs/LOCKFILE_MERGE.md](docs/LOCKFILE_MERGE.md).
|
|
271
|
+
|
|
272
|
+
### GitHub Actions — PR verification
|
|
273
|
+
|
|
274
|
+
For a full set of patterns (conditional builds, cache keys, GitLab, pre-commit), see [docs/ci-cookbook.md](docs/ci-cookbook.md).
|
|
275
|
+
|
|
276
|
+
#### Option A: use bundled composite action
|
|
277
|
+
|
|
278
|
+
```yaml
|
|
279
|
+
name: Boundary check
|
|
280
|
+
on: [pull_request]
|
|
281
|
+
jobs:
|
|
282
|
+
verify:
|
|
283
|
+
runs-on: ubuntu-latest
|
|
284
|
+
steps:
|
|
285
|
+
- uses: actions/checkout@v4
|
|
286
|
+
with:
|
|
287
|
+
fetch-depth: 0
|
|
288
|
+
- uses: ./.github/actions/boundver
|
|
289
|
+
with:
|
|
290
|
+
config: boundary.config.json
|
|
291
|
+
lock: boundary.lock.json
|
|
292
|
+
source: head
|
|
293
|
+
show-diff-on-failure: "true"
|
|
294
|
+
```
|
|
295
|
+
|
|
296
|
+
#### Option B: explicit steps
|
|
297
|
+
|
|
298
|
+
```yaml
|
|
299
|
+
name: Boundary check
|
|
300
|
+
on: [pull_request]
|
|
301
|
+
jobs:
|
|
302
|
+
verify:
|
|
303
|
+
runs-on: ubuntu-latest
|
|
304
|
+
steps:
|
|
305
|
+
- uses: actions/checkout@v4
|
|
306
|
+
with:
|
|
307
|
+
fetch-depth: 0
|
|
308
|
+
- uses: actions/setup-python@v5
|
|
309
|
+
with:
|
|
310
|
+
python-version: '3.11'
|
|
311
|
+
- run: boundver verify
|
|
312
|
+
- name: Show diff on failure
|
|
313
|
+
if: failure()
|
|
314
|
+
run: |
|
|
315
|
+
boundver generate --out boundary.lock.new.json
|
|
316
|
+
boundver diff boundary.lock.json boundary.lock.new.json
|
|
317
|
+
```
|
|
318
|
+
|
|
319
|
+
### Conditional builds using slice fingerprints
|
|
320
|
+
|
|
321
|
+
```bash
|
|
322
|
+
# Only rebuild if the API slice actually changed
|
|
323
|
+
NEW_FP=$(python -c "
|
|
324
|
+
import json
|
|
325
|
+
lock = json.load(open('boundary.lock.json'))
|
|
326
|
+
print(lock['slices']['my-api']['fingerprint'][:12])
|
|
327
|
+
")
|
|
328
|
+
|
|
329
|
+
if [ "$NEW_FP" != "$CACHED_FP" ]; then
|
|
330
|
+
echo "API changed — rebuilding consumers"
|
|
331
|
+
# ... trigger downstream builds
|
|
332
|
+
fi
|
|
333
|
+
```
|
|
334
|
+
|
|
335
|
+
### Shell verifier (portability proof)
|
|
336
|
+
|
|
337
|
+
```bash
|
|
338
|
+
# Verifies exact/boundary fingerprints against HEAD using git + jq + sha256sum
|
|
339
|
+
scripts/boundver-verify.sh boundary.config.json boundary.lock.json
|
|
340
|
+
```
|
|
341
|
+
|
|
342
|
+
## Exit codes
|
|
343
|
+
|
|
344
|
+
`boundver verify` uses structured exit codes for reliable CI scripting:
|
|
345
|
+
|
|
346
|
+
| Code | Meaning |
|
|
347
|
+
|------|---------|
|
|
348
|
+
| `0` | Lockfile matches current repo state |
|
|
349
|
+
| `1` | Lockfile is out of date (fingerprint mismatches found) |
|
|
350
|
+
| `2` | Usage error (unknown component, config missing, etc.) |
|
|
351
|
+
|
|
352
|
+
`validate-config` exits `0` on success, `1` on validation errors.
|
|
353
|
+
`generate` exits `0` on success, `1` on config/generation error.
|
|
354
|
+
|
|
355
|
+
## Design decisions
|
|
356
|
+
|
|
357
|
+
- **No external dependencies.** Only Git and Python stdlib. Runs anywhere Python 3.8+ and Git are available.
|
|
358
|
+
- **Deterministic output.** Canonical JSON (sorted keys, compact separators) ensures two machines produce identical hashes from identical repo state.
|
|
359
|
+
- **Canonical exact hashing across source modes.** `exact` uses one canonical SHA-256 file-content digest model for `head`, `index`, and `working-tree`, enabling direct cross-source comparison.
|
|
360
|
+
- **Config/lockfile split.** Config is human-maintained (what exists). Lockfile is machine-generated (current state). Mirrors `package.json` / `package-lock.json`.
|
|
361
|
+
- **Language-agnostic boundaries.** Instead of parsing ASTs, you declare which files constitute the public boundary. Works with any language or artifact format.
|
|
362
|
+
|
|
363
|
+
## Examples
|
|
364
|
+
|
|
365
|
+
- `examples/openapi/`
|
|
366
|
+
- `examples/json-file/`
|
|
367
|
+
- `examples/implicit-and-leaf/`
|
|
368
|
+
- `examples/python-package/`
|
|
369
|
+
- `examples/typescript-package/`
|
|
370
|
+
|
|
371
|
+
## Documentation
|
|
372
|
+
|
|
373
|
+
- [Getting started](docs/getting-started.md) — install, first config, first lockfile, CI setup
|
|
374
|
+
- [Gradual adoption guide](docs/gradual-adoption.md) — incremental adoption from one component to full coverage
|
|
375
|
+
- [CI cookbook](docs/ci-cookbook.md) — GitHub Actions, cache keys, GitLab, pre-commit
|
|
376
|
+
- [Why boundver?](docs/WHY_BOUNDVER.md) — tool comparison and positioning
|
|
377
|
+
- [Custom vs public providers](docs/public-vs-custom-providers.md) — when to use `custom.*`
|
|
378
|
+
- [Lockfile merge handling](docs/LOCKFILE_MERGE.md) — resolving merge conflicts
|
|
379
|
+
|
|
380
|
+
## Validation dependencies
|
|
381
|
+
|
|
382
|
+
- **Runtime dependencies:** none (stdlib + git only).
|
|
383
|
+
- **Optional enhanced schema validation:** install `jsonschema` for stricter JSON Schema engine checks in `validate-config`.
|
|
384
|
+
- **Optional enhanced YAML extraction:** install `PyYAML` for robust YAML parsing in version extraction.
|
|
385
|
+
|
|
386
|
+
```bash
|
|
387
|
+
pip install "boundver[schema]"
|
|
388
|
+
pip install "boundver[yaml]"
|
|
389
|
+
```
|
|
390
|
+
|
|
391
|
+
Without `jsonschema`, boundver still runs and applies built-in semantic validation checks.
|
|
392
|
+
|
|
393
|
+
## Release
|
|
394
|
+
|
|
395
|
+
- PyPI publish workflow: `.github/workflows/publish.yml`
|
|
396
|
+
- Trigger: push a version tag matching `v*` (for example `v0.3.0`)
|
|
397
|
+
|
|
398
|
+
## Source modes
|
|
399
|
+
|
|
400
|
+
| Mode | File list | Content read from | Default for |
|
|
401
|
+
|------|-----------|-------------------|-------------|
|
|
402
|
+
| `head` | `git ls-tree HEAD` | committed git blobs | `generate`, `verify`, `status`, `why` |
|
|
403
|
+
| `index` | `git ls-files --cached` | staged blobs | — |
|
|
404
|
+
| `working-tree` | `git ls-files` (tracked) | disk bytes (CRLF→LF) | `explain` |
|
|
405
|
+
|
|
406
|
+
### Important: `working-tree` only sees tracked files
|
|
407
|
+
|
|
408
|
+
`--source=working-tree` hashes the **on-disk content** of files that are already tracked by git.
|
|
409
|
+
It does **not** include untracked files. If you just created a new file but haven't run
|
|
410
|
+
`git add`, that file will not appear in any fingerprint until it is tracked.
|
|
411
|
+
|
|
412
|
+
This matters most during:
|
|
413
|
+
|
|
414
|
+
- **Initial setup** — run `git add .` before `boundver generate --source working-tree`.
|
|
415
|
+
- **Adding new boundary files** — a new `openapi.yaml` won't affect digests until tracked.
|
|
416
|
+
- **CI with uncommitted generated files** — prefer `--source head` (the default) in CI.
|
|
417
|
+
|
|
418
|
+
### Ignore behavior
|
|
419
|
+
|
|
420
|
+
`--source=working-tree` prefers Git-backed tracked-file enumeration (`git ls-files`) when available.
|
|
421
|
+
In non-git fallback contexts, local file traversal is used.
|
|
422
|
+
Symlinks are hashed as link-target text (not dereferenced bytes) for cross-source consistency.
|
|
423
|
+
|
|
424
|
+
## Requirements
|
|
425
|
+
|
|
426
|
+
- Python 3.8+
|
|
427
|
+
- Git
|
|
428
|
+
- No pip packages needed
|
|
429
|
+
|
|
430
|
+
## Hash guardrails
|
|
431
|
+
|
|
432
|
+
To avoid pathological repository scans, hashing enforces built-in guardrails:
|
|
433
|
+
|
|
434
|
+
- maximum files hashed per digest: `50,000`
|
|
435
|
+
- maximum size per hashed file: `50 MiB`
|
|
436
|
+
|
|
437
|
+
If exceeded, boundver records explicit digest errors on affected components.
|
|
438
|
+
|
|
439
|
+
## License
|
|
440
|
+
|
|
441
|
+
MIT
|