specshift 1.0.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.
- specshift-1.0.0/LICENSE +21 -0
- specshift-1.0.0/PKG-INFO +340 -0
- specshift-1.0.0/README.md +301 -0
- specshift-1.0.0/pyproject.toml +62 -0
- specshift-1.0.0/setup.cfg +4 -0
- specshift-1.0.0/specshift/__init__.py +20 -0
- specshift-1.0.0/specshift/ai_summary.py +201 -0
- specshift-1.0.0/specshift/cli.py +256 -0
- specshift-1.0.0/specshift/config.py +79 -0
- specshift-1.0.0/specshift/differ.py +721 -0
- specshift-1.0.0/specshift/git_utils.py +82 -0
- specshift-1.0.0/specshift/models.py +110 -0
- specshift-1.0.0/specshift/notifier.py +66 -0
- specshift-1.0.0/specshift/reporter.py +167 -0
- specshift-1.0.0/specshift/spec_loader.py +122 -0
- specshift-1.0.0/specshift.egg-info/PKG-INFO +340 -0
- specshift-1.0.0/specshift.egg-info/SOURCES.txt +23 -0
- specshift-1.0.0/specshift.egg-info/dependency_links.txt +1 -0
- specshift-1.0.0/specshift.egg-info/entry_points.txt +2 -0
- specshift-1.0.0/specshift.egg-info/requires.txt +11 -0
- specshift-1.0.0/specshift.egg-info/top_level.txt +1 -0
- specshift-1.0.0/tests/test_cli.py +46 -0
- specshift-1.0.0/tests/test_differ.py +225 -0
- specshift-1.0.0/tests/test_reporter.py +38 -0
- specshift-1.0.0/tests/test_spec_loader.py +48 -0
specshift-1.0.0/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Lethe044
|
|
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.
|
specshift-1.0.0/PKG-INFO
ADDED
|
@@ -0,0 +1,340 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: specshift
|
|
3
|
+
Version: 1.0.0
|
|
4
|
+
Summary: Detects, classifies, and optionally summarizes breaking changes in OpenAPI/Swagger contracts using AI
|
|
5
|
+
Author: Lethe044
|
|
6
|
+
License: MIT
|
|
7
|
+
Project-URL: Homepage, https://github.com/Lethe044/specshift
|
|
8
|
+
Project-URL: Repository, https://github.com/Lethe044/specshift
|
|
9
|
+
Project-URL: Issues, https://github.com/Lethe044/specshift/issues
|
|
10
|
+
Project-URL: Changelog, https://github.com/Lethe044/specshift/blob/main/CHANGELOG.md
|
|
11
|
+
Keywords: openapi,swagger,api,breaking-changes,contract-testing,api-diff,devtools,cli,ci-cd
|
|
12
|
+
Classifier: Development Status :: 5 - Production/Stable
|
|
13
|
+
Classifier: Environment :: Console
|
|
14
|
+
Classifier: Intended Audience :: Developers
|
|
15
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
16
|
+
Classifier: Operating System :: OS Independent
|
|
17
|
+
Classifier: Programming Language :: Python :: 3
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.9
|
|
19
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
20
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
21
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
22
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
23
|
+
Classifier: Topic :: Software Development :: Testing
|
|
24
|
+
Classifier: Topic :: Software Development :: Quality Assurance
|
|
25
|
+
Classifier: Topic :: Internet :: WWW/HTTP
|
|
26
|
+
Requires-Python: >=3.9
|
|
27
|
+
Description-Content-Type: text/markdown
|
|
28
|
+
License-File: LICENSE
|
|
29
|
+
Requires-Dist: pyyaml>=6.0
|
|
30
|
+
Requires-Dist: requests>=2.28
|
|
31
|
+
Provides-Extra: pretty
|
|
32
|
+
Requires-Dist: rich>=13.0; extra == "pretty"
|
|
33
|
+
Provides-Extra: dev
|
|
34
|
+
Requires-Dist: pytest>=7.4; extra == "dev"
|
|
35
|
+
Requires-Dist: pytest-cov>=4.1; extra == "dev"
|
|
36
|
+
Requires-Dist: responses>=0.23; extra == "dev"
|
|
37
|
+
Requires-Dist: rich>=13.0; extra == "dev"
|
|
38
|
+
Dynamic: license-file
|
|
39
|
+
|
|
40
|
+
# SpecShift
|
|
41
|
+
|
|
42
|
+
[](https://github.com/Lethe044/specshift/actions/workflows/ci.yml)
|
|
43
|
+
[](https://pypi.org/project/specshift/)
|
|
44
|
+
[](https://pypi.org/project/specshift/)
|
|
45
|
+
[](LICENSE)
|
|
46
|
+
|
|
47
|
+
SpecShift detects changes in OpenAPI and Swagger contracts, classifies each
|
|
48
|
+
one as breaking, warning, or info, and can optionally summarize them in
|
|
49
|
+
plain language.
|
|
50
|
+
|
|
51
|
+
When an API changes from one version to the next, the real question isn't
|
|
52
|
+
"what changed" but "will this break me". SpecShift is built to answer
|
|
53
|
+
exactly that: it takes two specifications, evaluates every difference
|
|
54
|
+
between them individually, and tells you which ones actually matter.
|
|
55
|
+
|
|
56
|
+
```
|
|
57
|
+
$ specshift diff examples/old_api.yaml examples/new_api.yaml
|
|
58
|
+
|
|
59
|
+
Bookstore API : 1.0.0 -> 2.0.0
|
|
60
|
+
13 breaking, 2 warning, 3 info changes found.
|
|
61
|
+
|
|
62
|
+
[BREAKING] DELETE /books/{bookId} :: HTTP method removed
|
|
63
|
+
[BREAKING] GET /books > parameter 'category' :: Parameter 'category' is now required
|
|
64
|
+
[BREAKING] GET /books > response 200 > field 'author' :: field removed from response
|
|
65
|
+
...
|
|
66
|
+
|
|
67
|
+
Result: 13 breaking change(s) make this update risky.
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
## Why SpecShift
|
|
71
|
+
|
|
72
|
+
Every team that keeps evolving its API eventually hits the same problem:
|
|
73
|
+
a field gets removed, a parameter becomes required, an enum value
|
|
74
|
+
disappears, and nobody notices until a client breaks in production.
|
|
75
|
+
Most existing diff tools just show you a raw JSON diff and leave it up to
|
|
76
|
+
you to figure out what actually matters.
|
|
77
|
+
|
|
78
|
+
SpecShift doesn't do that. It evaluates every change based on its context:
|
|
79
|
+
|
|
80
|
+
- Removing a field from a **response** is **breaking**, because clients
|
|
81
|
+
may depend on it being there.
|
|
82
|
+
- Removing the same field from a **request body** is usually just a
|
|
83
|
+
**warning**, because clients that send it are simply ignored, not broken.
|
|
84
|
+
- Adding a new required field to a request is **breaking**, but adding a
|
|
85
|
+
new field to a response is just **informational**.
|
|
86
|
+
|
|
87
|
+
These context-aware rules are the core of SpecShift, and they work
|
|
88
|
+
completely free, with no API key required. The optional AI-powered
|
|
89
|
+
natural-language summary is an additional layer on top, never a
|
|
90
|
+
requirement.
|
|
91
|
+
|
|
92
|
+
## Features
|
|
93
|
+
|
|
94
|
+
- **Comprehensive structural diff**: deep comparison at the path, HTTP
|
|
95
|
+
method, parameter, request body, response, and schema level.
|
|
96
|
+
- **Context-aware classification**: the same change is weighted
|
|
97
|
+
differently depending on whether it occurs in a request or a response.
|
|
98
|
+
- **`$ref` resolution and `allOf` merging**: correctly follows the
|
|
99
|
+
reference and composition patterns common in real-world specifications.
|
|
100
|
+
- **Detects enum, format, nullable, and security scheme changes**.
|
|
101
|
+
- **Works entirely for free**: no API key or paid service is required.
|
|
102
|
+
- **Optional AI summary**: can generate a natural-language summary using
|
|
103
|
+
Groq, the Gemini free tier, or any OpenAI-compatible endpoint. If no key
|
|
104
|
+
is set, it automatically falls back to a rule-based summary and never
|
|
105
|
+
stops working.
|
|
106
|
+
- **CI/CD integration**: the `specshift check` command compares the
|
|
107
|
+
current specification against a branch and fails the build if a
|
|
108
|
+
breaking change is found.
|
|
109
|
+
- **Live monitoring**: the `specshift watch` command periodically checks
|
|
110
|
+
a remote API's specification and sends a Slack or Discord notification
|
|
111
|
+
when it changes.
|
|
112
|
+
- **Three output formats**: a colored console table, a Markdown report
|
|
113
|
+
(ideal for PR comments), and JSON (for integrating with other tools).
|
|
114
|
+
|
|
115
|
+
## Installation
|
|
116
|
+
|
|
117
|
+
```bash
|
|
118
|
+
pip install specshift
|
|
119
|
+
```
|
|
120
|
+
|
|
121
|
+
For colored console output (optional, works fine without it too):
|
|
122
|
+
|
|
123
|
+
```bash
|
|
124
|
+
pip install "specshift[pretty]"
|
|
125
|
+
```
|
|
126
|
+
|
|
127
|
+
Installing from source:
|
|
128
|
+
|
|
129
|
+
```bash
|
|
130
|
+
git clone https://github.com/Lethe044/specshift.git
|
|
131
|
+
cd specshift
|
|
132
|
+
pip install -e .
|
|
133
|
+
```
|
|
134
|
+
|
|
135
|
+
## Quick start
|
|
136
|
+
|
|
137
|
+
Compare two specifications directly:
|
|
138
|
+
|
|
139
|
+
```bash
|
|
140
|
+
specshift diff old_openapi.yaml new_openapi.yaml
|
|
141
|
+
```
|
|
142
|
+
|
|
143
|
+
You can also compare specifications from URLs:
|
|
144
|
+
|
|
145
|
+
```bash
|
|
146
|
+
specshift diff https://api.example.com/v1/openapi.json https://api.example.com/v2/openapi.json
|
|
147
|
+
```
|
|
148
|
+
|
|
149
|
+
To use it in CI, create a configuration file in your repo:
|
|
150
|
+
|
|
151
|
+
```bash
|
|
152
|
+
specshift init
|
|
153
|
+
```
|
|
154
|
+
|
|
155
|
+
This produces a `.specshift.yml` file similar to:
|
|
156
|
+
|
|
157
|
+
```yaml
|
|
158
|
+
spec_path: openapi.yaml
|
|
159
|
+
base_ref: main
|
|
160
|
+
fail_on: breaking
|
|
161
|
+
```
|
|
162
|
+
|
|
163
|
+
Then, in your CI pipeline:
|
|
164
|
+
|
|
165
|
+
```bash
|
|
166
|
+
specshift check
|
|
167
|
+
```
|
|
168
|
+
|
|
169
|
+
This command compares the current `openapi.yaml` file against its version
|
|
170
|
+
on the `main` branch and returns exit code 1 if a breaking change is found.
|
|
171
|
+
|
|
172
|
+
## AI summary (optional)
|
|
173
|
+
|
|
174
|
+
SpecShift can use free-tier AI services to generate a natural-language
|
|
175
|
+
summary of the changes. This never requires any payment:
|
|
176
|
+
|
|
177
|
+
```bash
|
|
178
|
+
export GROQ_API_KEY="your-groq-api-key"
|
|
179
|
+
specshift diff old.yaml new.yaml --ai
|
|
180
|
+
```
|
|
181
|
+
|
|
182
|
+
You can also use Google Gemini's free tier instead of Groq:
|
|
183
|
+
|
|
184
|
+
```bash
|
|
185
|
+
export GEMINI_API_KEY="your-gemini-api-key"
|
|
186
|
+
specshift diff old.yaml new.yaml --ai --ai-provider gemini
|
|
187
|
+
```
|
|
188
|
+
|
|
189
|
+
If you want to use a more powerful (paid) model, you can connect any
|
|
190
|
+
OpenAI-compatible endpoint:
|
|
191
|
+
|
|
192
|
+
```bash
|
|
193
|
+
export SPECSHIFT_API_KEY="your-api-key"
|
|
194
|
+
export SPECSHIFT_OPENAI_BASE_URL="https://api.openai.com/v1"
|
|
195
|
+
specshift diff old.yaml new.yaml --ai --ai-provider openai_compatible --ai-model gpt-4o-mini
|
|
196
|
+
```
|
|
197
|
+
|
|
198
|
+
If no key is configured, the `--ai` flag still works, it simply produces a
|
|
199
|
+
rule-based summary instead of waiting on a network call. AI support is an
|
|
200
|
+
optional enhancement, never a requirement.
|
|
201
|
+
|
|
202
|
+
## Commands
|
|
203
|
+
|
|
204
|
+
### `specshift diff <old> <new>`
|
|
205
|
+
|
|
206
|
+
Compares two specifications. `<old>` and `<new>` can be a file path, an
|
|
207
|
+
http(s) URL, or raw JSON/YAML text.
|
|
208
|
+
|
|
209
|
+
Useful options:
|
|
210
|
+
|
|
211
|
+
| Option | Description |
|
|
212
|
+
|---|---|
|
|
213
|
+
| `--format console\|markdown\|json` | Output format (default: console) |
|
|
214
|
+
| `--output <file>` | Writes the output to a file |
|
|
215
|
+
| `--ai` | Adds a natural-language summary |
|
|
216
|
+
| `--ai-provider groq\|gemini\|openai_compatible` | Chooses the AI provider |
|
|
217
|
+
| `--fail-on breaking\|warning\|none` | Determines at which level exit code 1 is returned |
|
|
218
|
+
| `--quiet` | Only prints the summary line |
|
|
219
|
+
|
|
220
|
+
### `specshift check`
|
|
221
|
+
|
|
222
|
+
Designed for CI/CD. Compares the current specification file against a git
|
|
223
|
+
reference (branch, tag, or commit) defined in `.specshift.yml`.
|
|
224
|
+
|
|
225
|
+
```bash
|
|
226
|
+
specshift check --spec openapi.yaml --base-ref origin/main
|
|
227
|
+
```
|
|
228
|
+
|
|
229
|
+
### `specshift watch <url>`
|
|
230
|
+
|
|
231
|
+
Periodically checks a remote specification, compares it against the
|
|
232
|
+
previous snapshot, and sends a notification if a difference is found.
|
|
233
|
+
|
|
234
|
+
```bash
|
|
235
|
+
specshift watch https://api.example.com/openapi.json \
|
|
236
|
+
--interval 600 \
|
|
237
|
+
--slack-webhook "$SLACK_WEBHOOK_URL"
|
|
238
|
+
```
|
|
239
|
+
|
|
240
|
+
### `specshift init`
|
|
241
|
+
|
|
242
|
+
Creates a sample `.specshift.yml` file.
|
|
243
|
+
|
|
244
|
+
## Using it with GitHub Actions
|
|
245
|
+
|
|
246
|
+
The workflow below checks your API contract against the `main` branch on
|
|
247
|
+
every pull request and fails the build if a breaking change is found:
|
|
248
|
+
|
|
249
|
+
```yaml
|
|
250
|
+
name: API Contract Check
|
|
251
|
+
|
|
252
|
+
on:
|
|
253
|
+
pull_request:
|
|
254
|
+
paths:
|
|
255
|
+
- "openapi.yaml"
|
|
256
|
+
|
|
257
|
+
jobs:
|
|
258
|
+
contract-check:
|
|
259
|
+
runs-on: ubuntu-latest
|
|
260
|
+
steps:
|
|
261
|
+
- uses: actions/checkout@v4
|
|
262
|
+
with:
|
|
263
|
+
fetch-depth: 0
|
|
264
|
+
|
|
265
|
+
- uses: actions/setup-python@v5
|
|
266
|
+
with:
|
|
267
|
+
python-version: "3.12"
|
|
268
|
+
|
|
269
|
+
- run: pip install specshift
|
|
270
|
+
|
|
271
|
+
- run: specshift check --base-ref origin/${{ github.base_ref }}
|
|
272
|
+
```
|
|
273
|
+
|
|
274
|
+
If you want to add AI-powered PR comments, you can generate a Markdown
|
|
275
|
+
report with `specshift check --ai --format markdown --output report.md`
|
|
276
|
+
and post it as a PR comment using an action like
|
|
277
|
+
`peter-evans/create-or-update-comment`.
|
|
278
|
+
|
|
279
|
+
## Configuration file (`.specshift.yml`)
|
|
280
|
+
|
|
281
|
+
```yaml
|
|
282
|
+
spec_path: openapi.yaml
|
|
283
|
+
base_ref: main
|
|
284
|
+
fail_on: breaking
|
|
285
|
+
|
|
286
|
+
# optional
|
|
287
|
+
ai_provider: groq
|
|
288
|
+
ai_model: llama-3.3-70b-versatile
|
|
289
|
+
slack_webhook: https://hooks.slack.com/services/...
|
|
290
|
+
discord_webhook: https://discord.com/api/webhooks/...
|
|
291
|
+
ignore_paths: []
|
|
292
|
+
```
|
|
293
|
+
|
|
294
|
+
## When SpecShift calls something breaking
|
|
295
|
+
|
|
296
|
+
The table below summarizes which severity level applies in the most
|
|
297
|
+
common scenarios:
|
|
298
|
+
|
|
299
|
+
| Change | In a request | In a response |
|
|
300
|
+
|---|---|---|
|
|
301
|
+
| Field removed | Warning | Breaking |
|
|
302
|
+
| New required field added | Breaking | Info |
|
|
303
|
+
| New optional field added | Info | Info |
|
|
304
|
+
| Field no longer required | Info | Breaking |
|
|
305
|
+
| Field became required | Breaking | Info |
|
|
306
|
+
| Data type changed | Breaking | Breaking |
|
|
307
|
+
| Enum value removed | Breaking | Breaking |
|
|
308
|
+
| Endpoint or method removed | Breaking | Breaking |
|
|
309
|
+
|
|
310
|
+
## Comparison with other tools
|
|
311
|
+
|
|
312
|
+
| | SpecShift | Raw JSON/YAML diff | oasdiff / openapi-diff style tools |
|
|
313
|
+
|---|---|---|---|
|
|
314
|
+
| Context-aware classification | Yes | No | Partially |
|
|
315
|
+
| Natural-language summary | Yes (optional) | No | No |
|
|
316
|
+
| Free to use | Fully free | Free | Usually free |
|
|
317
|
+
| CI integration | Built-in (`check`) | Manual | Varies |
|
|
318
|
+
| Live URL monitoring | Built-in (`watch`) | No | Rarely |
|
|
319
|
+
|
|
320
|
+
## Roadmap
|
|
321
|
+
|
|
322
|
+
This project is under active development. Some planned areas:
|
|
323
|
+
|
|
324
|
+
- Support for gRPC/Protobuf contracts
|
|
325
|
+
- GraphQL schema diffing
|
|
326
|
+
- An official GitHub Action for posting automatic PR comments
|
|
327
|
+
- A web-based result viewer
|
|
328
|
+
- More semantic rules (path parameter pattern changes, content-type
|
|
329
|
+
changes, etc.)
|
|
330
|
+
|
|
331
|
+
Feel free to open an issue if you have a feature request.
|
|
332
|
+
|
|
333
|
+
## Contributing
|
|
334
|
+
|
|
335
|
+
See [CONTRIBUTING.md](CONTRIBUTING.md) for the contribution guide. Bug
|
|
336
|
+
reports, feature requests, and pull requests are always welcome.
|
|
337
|
+
|
|
338
|
+
## License
|
|
339
|
+
|
|
340
|
+
This project is licensed under the [MIT License](LICENSE).
|
|
@@ -0,0 +1,301 @@
|
|
|
1
|
+
# SpecShift
|
|
2
|
+
|
|
3
|
+
[](https://github.com/Lethe044/specshift/actions/workflows/ci.yml)
|
|
4
|
+
[](https://pypi.org/project/specshift/)
|
|
5
|
+
[](https://pypi.org/project/specshift/)
|
|
6
|
+
[](LICENSE)
|
|
7
|
+
|
|
8
|
+
SpecShift detects changes in OpenAPI and Swagger contracts, classifies each
|
|
9
|
+
one as breaking, warning, or info, and can optionally summarize them in
|
|
10
|
+
plain language.
|
|
11
|
+
|
|
12
|
+
When an API changes from one version to the next, the real question isn't
|
|
13
|
+
"what changed" but "will this break me". SpecShift is built to answer
|
|
14
|
+
exactly that: it takes two specifications, evaluates every difference
|
|
15
|
+
between them individually, and tells you which ones actually matter.
|
|
16
|
+
|
|
17
|
+
```
|
|
18
|
+
$ specshift diff examples/old_api.yaml examples/new_api.yaml
|
|
19
|
+
|
|
20
|
+
Bookstore API : 1.0.0 -> 2.0.0
|
|
21
|
+
13 breaking, 2 warning, 3 info changes found.
|
|
22
|
+
|
|
23
|
+
[BREAKING] DELETE /books/{bookId} :: HTTP method removed
|
|
24
|
+
[BREAKING] GET /books > parameter 'category' :: Parameter 'category' is now required
|
|
25
|
+
[BREAKING] GET /books > response 200 > field 'author' :: field removed from response
|
|
26
|
+
...
|
|
27
|
+
|
|
28
|
+
Result: 13 breaking change(s) make this update risky.
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
## Why SpecShift
|
|
32
|
+
|
|
33
|
+
Every team that keeps evolving its API eventually hits the same problem:
|
|
34
|
+
a field gets removed, a parameter becomes required, an enum value
|
|
35
|
+
disappears, and nobody notices until a client breaks in production.
|
|
36
|
+
Most existing diff tools just show you a raw JSON diff and leave it up to
|
|
37
|
+
you to figure out what actually matters.
|
|
38
|
+
|
|
39
|
+
SpecShift doesn't do that. It evaluates every change based on its context:
|
|
40
|
+
|
|
41
|
+
- Removing a field from a **response** is **breaking**, because clients
|
|
42
|
+
may depend on it being there.
|
|
43
|
+
- Removing the same field from a **request body** is usually just a
|
|
44
|
+
**warning**, because clients that send it are simply ignored, not broken.
|
|
45
|
+
- Adding a new required field to a request is **breaking**, but adding a
|
|
46
|
+
new field to a response is just **informational**.
|
|
47
|
+
|
|
48
|
+
These context-aware rules are the core of SpecShift, and they work
|
|
49
|
+
completely free, with no API key required. The optional AI-powered
|
|
50
|
+
natural-language summary is an additional layer on top, never a
|
|
51
|
+
requirement.
|
|
52
|
+
|
|
53
|
+
## Features
|
|
54
|
+
|
|
55
|
+
- **Comprehensive structural diff**: deep comparison at the path, HTTP
|
|
56
|
+
method, parameter, request body, response, and schema level.
|
|
57
|
+
- **Context-aware classification**: the same change is weighted
|
|
58
|
+
differently depending on whether it occurs in a request or a response.
|
|
59
|
+
- **`$ref` resolution and `allOf` merging**: correctly follows the
|
|
60
|
+
reference and composition patterns common in real-world specifications.
|
|
61
|
+
- **Detects enum, format, nullable, and security scheme changes**.
|
|
62
|
+
- **Works entirely for free**: no API key or paid service is required.
|
|
63
|
+
- **Optional AI summary**: can generate a natural-language summary using
|
|
64
|
+
Groq, the Gemini free tier, or any OpenAI-compatible endpoint. If no key
|
|
65
|
+
is set, it automatically falls back to a rule-based summary and never
|
|
66
|
+
stops working.
|
|
67
|
+
- **CI/CD integration**: the `specshift check` command compares the
|
|
68
|
+
current specification against a branch and fails the build if a
|
|
69
|
+
breaking change is found.
|
|
70
|
+
- **Live monitoring**: the `specshift watch` command periodically checks
|
|
71
|
+
a remote API's specification and sends a Slack or Discord notification
|
|
72
|
+
when it changes.
|
|
73
|
+
- **Three output formats**: a colored console table, a Markdown report
|
|
74
|
+
(ideal for PR comments), and JSON (for integrating with other tools).
|
|
75
|
+
|
|
76
|
+
## Installation
|
|
77
|
+
|
|
78
|
+
```bash
|
|
79
|
+
pip install specshift
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
For colored console output (optional, works fine without it too):
|
|
83
|
+
|
|
84
|
+
```bash
|
|
85
|
+
pip install "specshift[pretty]"
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
Installing from source:
|
|
89
|
+
|
|
90
|
+
```bash
|
|
91
|
+
git clone https://github.com/Lethe044/specshift.git
|
|
92
|
+
cd specshift
|
|
93
|
+
pip install -e .
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
## Quick start
|
|
97
|
+
|
|
98
|
+
Compare two specifications directly:
|
|
99
|
+
|
|
100
|
+
```bash
|
|
101
|
+
specshift diff old_openapi.yaml new_openapi.yaml
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
You can also compare specifications from URLs:
|
|
105
|
+
|
|
106
|
+
```bash
|
|
107
|
+
specshift diff https://api.example.com/v1/openapi.json https://api.example.com/v2/openapi.json
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
To use it in CI, create a configuration file in your repo:
|
|
111
|
+
|
|
112
|
+
```bash
|
|
113
|
+
specshift init
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
This produces a `.specshift.yml` file similar to:
|
|
117
|
+
|
|
118
|
+
```yaml
|
|
119
|
+
spec_path: openapi.yaml
|
|
120
|
+
base_ref: main
|
|
121
|
+
fail_on: breaking
|
|
122
|
+
```
|
|
123
|
+
|
|
124
|
+
Then, in your CI pipeline:
|
|
125
|
+
|
|
126
|
+
```bash
|
|
127
|
+
specshift check
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
This command compares the current `openapi.yaml` file against its version
|
|
131
|
+
on the `main` branch and returns exit code 1 if a breaking change is found.
|
|
132
|
+
|
|
133
|
+
## AI summary (optional)
|
|
134
|
+
|
|
135
|
+
SpecShift can use free-tier AI services to generate a natural-language
|
|
136
|
+
summary of the changes. This never requires any payment:
|
|
137
|
+
|
|
138
|
+
```bash
|
|
139
|
+
export GROQ_API_KEY="your-groq-api-key"
|
|
140
|
+
specshift diff old.yaml new.yaml --ai
|
|
141
|
+
```
|
|
142
|
+
|
|
143
|
+
You can also use Google Gemini's free tier instead of Groq:
|
|
144
|
+
|
|
145
|
+
```bash
|
|
146
|
+
export GEMINI_API_KEY="your-gemini-api-key"
|
|
147
|
+
specshift diff old.yaml new.yaml --ai --ai-provider gemini
|
|
148
|
+
```
|
|
149
|
+
|
|
150
|
+
If you want to use a more powerful (paid) model, you can connect any
|
|
151
|
+
OpenAI-compatible endpoint:
|
|
152
|
+
|
|
153
|
+
```bash
|
|
154
|
+
export SPECSHIFT_API_KEY="your-api-key"
|
|
155
|
+
export SPECSHIFT_OPENAI_BASE_URL="https://api.openai.com/v1"
|
|
156
|
+
specshift diff old.yaml new.yaml --ai --ai-provider openai_compatible --ai-model gpt-4o-mini
|
|
157
|
+
```
|
|
158
|
+
|
|
159
|
+
If no key is configured, the `--ai` flag still works, it simply produces a
|
|
160
|
+
rule-based summary instead of waiting on a network call. AI support is an
|
|
161
|
+
optional enhancement, never a requirement.
|
|
162
|
+
|
|
163
|
+
## Commands
|
|
164
|
+
|
|
165
|
+
### `specshift diff <old> <new>`
|
|
166
|
+
|
|
167
|
+
Compares two specifications. `<old>` and `<new>` can be a file path, an
|
|
168
|
+
http(s) URL, or raw JSON/YAML text.
|
|
169
|
+
|
|
170
|
+
Useful options:
|
|
171
|
+
|
|
172
|
+
| Option | Description |
|
|
173
|
+
|---|---|
|
|
174
|
+
| `--format console\|markdown\|json` | Output format (default: console) |
|
|
175
|
+
| `--output <file>` | Writes the output to a file |
|
|
176
|
+
| `--ai` | Adds a natural-language summary |
|
|
177
|
+
| `--ai-provider groq\|gemini\|openai_compatible` | Chooses the AI provider |
|
|
178
|
+
| `--fail-on breaking\|warning\|none` | Determines at which level exit code 1 is returned |
|
|
179
|
+
| `--quiet` | Only prints the summary line |
|
|
180
|
+
|
|
181
|
+
### `specshift check`
|
|
182
|
+
|
|
183
|
+
Designed for CI/CD. Compares the current specification file against a git
|
|
184
|
+
reference (branch, tag, or commit) defined in `.specshift.yml`.
|
|
185
|
+
|
|
186
|
+
```bash
|
|
187
|
+
specshift check --spec openapi.yaml --base-ref origin/main
|
|
188
|
+
```
|
|
189
|
+
|
|
190
|
+
### `specshift watch <url>`
|
|
191
|
+
|
|
192
|
+
Periodically checks a remote specification, compares it against the
|
|
193
|
+
previous snapshot, and sends a notification if a difference is found.
|
|
194
|
+
|
|
195
|
+
```bash
|
|
196
|
+
specshift watch https://api.example.com/openapi.json \
|
|
197
|
+
--interval 600 \
|
|
198
|
+
--slack-webhook "$SLACK_WEBHOOK_URL"
|
|
199
|
+
```
|
|
200
|
+
|
|
201
|
+
### `specshift init`
|
|
202
|
+
|
|
203
|
+
Creates a sample `.specshift.yml` file.
|
|
204
|
+
|
|
205
|
+
## Using it with GitHub Actions
|
|
206
|
+
|
|
207
|
+
The workflow below checks your API contract against the `main` branch on
|
|
208
|
+
every pull request and fails the build if a breaking change is found:
|
|
209
|
+
|
|
210
|
+
```yaml
|
|
211
|
+
name: API Contract Check
|
|
212
|
+
|
|
213
|
+
on:
|
|
214
|
+
pull_request:
|
|
215
|
+
paths:
|
|
216
|
+
- "openapi.yaml"
|
|
217
|
+
|
|
218
|
+
jobs:
|
|
219
|
+
contract-check:
|
|
220
|
+
runs-on: ubuntu-latest
|
|
221
|
+
steps:
|
|
222
|
+
- uses: actions/checkout@v4
|
|
223
|
+
with:
|
|
224
|
+
fetch-depth: 0
|
|
225
|
+
|
|
226
|
+
- uses: actions/setup-python@v5
|
|
227
|
+
with:
|
|
228
|
+
python-version: "3.12"
|
|
229
|
+
|
|
230
|
+
- run: pip install specshift
|
|
231
|
+
|
|
232
|
+
- run: specshift check --base-ref origin/${{ github.base_ref }}
|
|
233
|
+
```
|
|
234
|
+
|
|
235
|
+
If you want to add AI-powered PR comments, you can generate a Markdown
|
|
236
|
+
report with `specshift check --ai --format markdown --output report.md`
|
|
237
|
+
and post it as a PR comment using an action like
|
|
238
|
+
`peter-evans/create-or-update-comment`.
|
|
239
|
+
|
|
240
|
+
## Configuration file (`.specshift.yml`)
|
|
241
|
+
|
|
242
|
+
```yaml
|
|
243
|
+
spec_path: openapi.yaml
|
|
244
|
+
base_ref: main
|
|
245
|
+
fail_on: breaking
|
|
246
|
+
|
|
247
|
+
# optional
|
|
248
|
+
ai_provider: groq
|
|
249
|
+
ai_model: llama-3.3-70b-versatile
|
|
250
|
+
slack_webhook: https://hooks.slack.com/services/...
|
|
251
|
+
discord_webhook: https://discord.com/api/webhooks/...
|
|
252
|
+
ignore_paths: []
|
|
253
|
+
```
|
|
254
|
+
|
|
255
|
+
## When SpecShift calls something breaking
|
|
256
|
+
|
|
257
|
+
The table below summarizes which severity level applies in the most
|
|
258
|
+
common scenarios:
|
|
259
|
+
|
|
260
|
+
| Change | In a request | In a response |
|
|
261
|
+
|---|---|---|
|
|
262
|
+
| Field removed | Warning | Breaking |
|
|
263
|
+
| New required field added | Breaking | Info |
|
|
264
|
+
| New optional field added | Info | Info |
|
|
265
|
+
| Field no longer required | Info | Breaking |
|
|
266
|
+
| Field became required | Breaking | Info |
|
|
267
|
+
| Data type changed | Breaking | Breaking |
|
|
268
|
+
| Enum value removed | Breaking | Breaking |
|
|
269
|
+
| Endpoint or method removed | Breaking | Breaking |
|
|
270
|
+
|
|
271
|
+
## Comparison with other tools
|
|
272
|
+
|
|
273
|
+
| | SpecShift | Raw JSON/YAML diff | oasdiff / openapi-diff style tools |
|
|
274
|
+
|---|---|---|---|
|
|
275
|
+
| Context-aware classification | Yes | No | Partially |
|
|
276
|
+
| Natural-language summary | Yes (optional) | No | No |
|
|
277
|
+
| Free to use | Fully free | Free | Usually free |
|
|
278
|
+
| CI integration | Built-in (`check`) | Manual | Varies |
|
|
279
|
+
| Live URL monitoring | Built-in (`watch`) | No | Rarely |
|
|
280
|
+
|
|
281
|
+
## Roadmap
|
|
282
|
+
|
|
283
|
+
This project is under active development. Some planned areas:
|
|
284
|
+
|
|
285
|
+
- Support for gRPC/Protobuf contracts
|
|
286
|
+
- GraphQL schema diffing
|
|
287
|
+
- An official GitHub Action for posting automatic PR comments
|
|
288
|
+
- A web-based result viewer
|
|
289
|
+
- More semantic rules (path parameter pattern changes, content-type
|
|
290
|
+
changes, etc.)
|
|
291
|
+
|
|
292
|
+
Feel free to open an issue if you have a feature request.
|
|
293
|
+
|
|
294
|
+
## Contributing
|
|
295
|
+
|
|
296
|
+
See [CONTRIBUTING.md](CONTRIBUTING.md) for the contribution guide. Bug
|
|
297
|
+
reports, feature requests, and pull requests are always welcome.
|
|
298
|
+
|
|
299
|
+
## License
|
|
300
|
+
|
|
301
|
+
This project is licensed under the [MIT License](LICENSE).
|