specshift 1.1.0__tar.gz → 1.2.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.
Files changed (31) hide show
  1. {specshift-1.1.0/specshift.egg-info → specshift-1.2.0}/PKG-INFO +49 -5
  2. {specshift-1.1.0 → specshift-1.2.0}/README.md +46 -2
  3. {specshift-1.1.0 → specshift-1.2.0}/pyproject.toml +4 -2
  4. {specshift-1.1.0 → specshift-1.2.0}/specshift/__init__.py +4 -1
  5. {specshift-1.1.0 → specshift-1.2.0}/specshift/cli.py +33 -2
  6. specshift-1.2.0/specshift/protobuf_differ.py +622 -0
  7. specshift-1.2.0/specshift/webapp.py +335 -0
  8. {specshift-1.1.0 → specshift-1.2.0/specshift.egg-info}/PKG-INFO +49 -5
  9. {specshift-1.1.0 → specshift-1.2.0}/specshift.egg-info/SOURCES.txt +5 -1
  10. specshift-1.2.0/tests/test_protobuf_differ.py +168 -0
  11. specshift-1.2.0/tests/test_webapp.py +62 -0
  12. {specshift-1.1.0 → specshift-1.2.0}/LICENSE +0 -0
  13. {specshift-1.1.0 → specshift-1.2.0}/setup.cfg +0 -0
  14. {specshift-1.1.0 → specshift-1.2.0}/specshift/ai_summary.py +0 -0
  15. {specshift-1.1.0 → specshift-1.2.0}/specshift/config.py +0 -0
  16. {specshift-1.1.0 → specshift-1.2.0}/specshift/differ.py +0 -0
  17. {specshift-1.1.0 → specshift-1.2.0}/specshift/git_utils.py +0 -0
  18. {specshift-1.1.0 → specshift-1.2.0}/specshift/graphql_differ.py +0 -0
  19. {specshift-1.1.0 → specshift-1.2.0}/specshift/models.py +0 -0
  20. {specshift-1.1.0 → specshift-1.2.0}/specshift/notifier.py +0 -0
  21. {specshift-1.1.0 → specshift-1.2.0}/specshift/reporter.py +0 -0
  22. {specshift-1.1.0 → specshift-1.2.0}/specshift/spec_loader.py +0 -0
  23. {specshift-1.1.0 → specshift-1.2.0}/specshift.egg-info/dependency_links.txt +0 -0
  24. {specshift-1.1.0 → specshift-1.2.0}/specshift.egg-info/entry_points.txt +0 -0
  25. {specshift-1.1.0 → specshift-1.2.0}/specshift.egg-info/requires.txt +0 -0
  26. {specshift-1.1.0 → specshift-1.2.0}/specshift.egg-info/top_level.txt +0 -0
  27. {specshift-1.1.0 → specshift-1.2.0}/tests/test_cli.py +0 -0
  28. {specshift-1.1.0 → specshift-1.2.0}/tests/test_differ.py +0 -0
  29. {specshift-1.1.0 → specshift-1.2.0}/tests/test_graphql_differ.py +0 -0
  30. {specshift-1.1.0 → specshift-1.2.0}/tests/test_reporter.py +0 -0
  31. {specshift-1.1.0 → specshift-1.2.0}/tests/test_spec_loader.py +0 -0
@@ -1,14 +1,14 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: specshift
3
- Version: 1.1.0
4
- Summary: Detects, classifies, and optionally summarizes breaking changes in OpenAPI/Swagger and GraphQL contracts using AI
3
+ Version: 1.2.0
4
+ Summary: Detects, classifies, and optionally summarizes breaking changes in OpenAPI/Swagger, GraphQL, and Protobuf/gRPC contracts using AI
5
5
  Author: Lethe044
6
6
  License: MIT
7
7
  Project-URL: Homepage, https://github.com/Lethe044/specshift
8
8
  Project-URL: Repository, https://github.com/Lethe044/specshift
9
9
  Project-URL: Issues, https://github.com/Lethe044/specshift/issues
10
10
  Project-URL: Changelog, https://github.com/Lethe044/specshift/blob/main/CHANGELOG.md
11
- Keywords: openapi,swagger,graphql,api,breaking-changes,contract-testing,api-diff,devtools,cli,ci-cd,github-actions
11
+ Keywords: openapi,swagger,graphql,grpc,protobuf,api,breaking-changes,contract-testing,api-diff,devtools,cli,ci-cd,github-actions
12
12
  Classifier: Development Status :: 5 - Production/Stable
13
13
  Classifier: Environment :: Console
14
14
  Classifier: Intended Audience :: Developers
@@ -96,6 +96,10 @@ requirement.
96
96
  - **GraphQL support**: diff GraphQL SDL schemas with `specshift diff-graphql`,
97
97
  covering types, fields, arguments, enum values, interfaces, unions, and
98
98
  deprecations, no external GraphQL library required.
99
+ - **Protobuf/gRPC support**: diff `.proto` files with `specshift diff-proto`,
100
+ using protobuf's own documented wire-compatibility rules (field numbers,
101
+ wire type categories, zigzag encoding) rather than generic JSON-shape
102
+ heuristics, so severity reflects what will actually break on the wire.
99
103
  - **Context-aware classification**: the same change is weighted
100
104
  differently depending on whether it occurs in a request or a response
101
105
  (or, for GraphQL, an input type versus an object type).
@@ -116,6 +120,10 @@ requirement.
116
120
  - **Live monitoring**: the `specshift watch` command periodically checks
117
121
  a remote API's specification and sends a Slack or Discord notification
118
122
  when it changes.
123
+ - **Local web dashboard**: `specshift serve` opens a browser-based tool
124
+ for comparing specs interactively (drag in files or paste text), with
125
+ zero extra dependencies since it runs entirely on Python's standard
126
+ library and stays on localhost.
119
127
  - **Four output formats**: a colored console table, a Markdown report
120
128
  (ideal for PR comments), JSON (for integrating with other tools), and a
121
129
  standalone, filterable HTML report you can open in any browser.
@@ -235,6 +243,33 @@ an http(s) URL, or raw SDL text. Supports the same `--format`, `--output`,
235
243
  specshift diff-graphql old_schema.graphql new_schema.graphql
236
244
  ```
237
245
 
246
+ ### `specshift diff-proto <old> <new>`
247
+
248
+ Compares two Protobuf/gRPC `.proto` schemas using protobuf's own
249
+ wire-compatibility rules (field numbers, wire type categories) rather
250
+ than generic JSON-shape heuristics. Supports the same `--format`,
251
+ `--output`, `--ai`, and `--fail-on` options as `diff`.
252
+
253
+ ```bash
254
+ specshift diff-proto old_service.proto new_service.proto
255
+ ```
256
+
257
+ ### `specshift serve`
258
+
259
+ Starts a local, dependency-free web dashboard for comparing specs
260
+ interactively. Opens a browser tab where you can drag in or paste two
261
+ files (OpenAPI, GraphQL, or Protobuf, auto-detected), and view the same
262
+ styled HTML report the CLI produces, with a short history of past
263
+ comparisons in the current session.
264
+
265
+ ```bash
266
+ specshift serve
267
+ # or: specshift serve --port 9000 --no-browser
268
+ ```
269
+
270
+ Everything runs in-process on your machine; nothing is uploaded anywhere,
271
+ and the server binds to `127.0.0.1` by default.
272
+
238
273
  ### `specshift check`
239
274
 
240
275
  Designed for CI/CD. Compares the current specification file against a git
@@ -368,14 +403,21 @@ common scenarios:
368
403
  The same table applies to GraphQL: `input` types behave like requests,
369
404
  `type`/`interface` fields behave like responses.
370
405
 
406
+ Protobuf/gRPC follows a different model, based on the wire format rather
407
+ than request/response direction: adding fields or rpc methods is always
408
+ safe, changing a field's number is always breaking, and type changes are
409
+ judged by wire type category (see the `diff-proto` section above).
410
+
371
411
  ## Comparison with other tools
372
412
 
373
413
  | | SpecShift | Raw JSON/YAML diff | oasdiff / openapi-diff style tools |
374
414
  |---|---|---|---|
375
415
  | Context-aware classification | Yes | No | Partially |
376
416
  | GraphQL support | Yes | No | Rarely |
417
+ | Protobuf/gRPC support (wire-aware) | Yes | No | Rarely |
377
418
  | Natural-language summary | Yes (optional) | No | No |
378
419
  | Standalone HTML report | Yes | No | Rarely |
420
+ | Local interactive dashboard | Yes | No | Rarely |
379
421
  | Free to use | Fully free | Free | Usually free |
380
422
  | CI integration | Built-in (`check` + official Action) | Manual | Varies |
381
423
  | Live URL monitoring | Built-in (`watch`) | No | Rarely |
@@ -384,11 +426,13 @@ The same table applies to GraphQL: `input` types behave like requests,
384
426
 
385
427
  This project is under active development. Some planned areas:
386
428
 
387
- - Support for gRPC/Protobuf contracts
388
429
  - Path parameter pattern (regex) and discriminator-level rule refinements
389
- - A hosted, shareable web viewer (beyond the offline HTML report)
390
430
  - Deeper GraphQL support: directive-aware deprecation reasons, custom
391
431
  scalar compatibility hints, and federation-aware schema composition
432
+ - Protobuf `reserved` statement awareness, so a properly reserved field
433
+ number isn't flagged the same way as an unreserved removal
434
+ - Optional shared/hosted history for the `serve` dashboard (currently
435
+ in-memory and per-session by design)
392
436
 
393
437
  Feel free to open an issue if you have a feature request.
394
438
 
@@ -57,6 +57,10 @@ requirement.
57
57
  - **GraphQL support**: diff GraphQL SDL schemas with `specshift diff-graphql`,
58
58
  covering types, fields, arguments, enum values, interfaces, unions, and
59
59
  deprecations, no external GraphQL library required.
60
+ - **Protobuf/gRPC support**: diff `.proto` files with `specshift diff-proto`,
61
+ using protobuf's own documented wire-compatibility rules (field numbers,
62
+ wire type categories, zigzag encoding) rather than generic JSON-shape
63
+ heuristics, so severity reflects what will actually break on the wire.
60
64
  - **Context-aware classification**: the same change is weighted
61
65
  differently depending on whether it occurs in a request or a response
62
66
  (or, for GraphQL, an input type versus an object type).
@@ -77,6 +81,10 @@ requirement.
77
81
  - **Live monitoring**: the `specshift watch` command periodically checks
78
82
  a remote API's specification and sends a Slack or Discord notification
79
83
  when it changes.
84
+ - **Local web dashboard**: `specshift serve` opens a browser-based tool
85
+ for comparing specs interactively (drag in files or paste text), with
86
+ zero extra dependencies since it runs entirely on Python's standard
87
+ library and stays on localhost.
80
88
  - **Four output formats**: a colored console table, a Markdown report
81
89
  (ideal for PR comments), JSON (for integrating with other tools), and a
82
90
  standalone, filterable HTML report you can open in any browser.
@@ -196,6 +204,33 @@ an http(s) URL, or raw SDL text. Supports the same `--format`, `--output`,
196
204
  specshift diff-graphql old_schema.graphql new_schema.graphql
197
205
  ```
198
206
 
207
+ ### `specshift diff-proto <old> <new>`
208
+
209
+ Compares two Protobuf/gRPC `.proto` schemas using protobuf's own
210
+ wire-compatibility rules (field numbers, wire type categories) rather
211
+ than generic JSON-shape heuristics. Supports the same `--format`,
212
+ `--output`, `--ai`, and `--fail-on` options as `diff`.
213
+
214
+ ```bash
215
+ specshift diff-proto old_service.proto new_service.proto
216
+ ```
217
+
218
+ ### `specshift serve`
219
+
220
+ Starts a local, dependency-free web dashboard for comparing specs
221
+ interactively. Opens a browser tab where you can drag in or paste two
222
+ files (OpenAPI, GraphQL, or Protobuf, auto-detected), and view the same
223
+ styled HTML report the CLI produces, with a short history of past
224
+ comparisons in the current session.
225
+
226
+ ```bash
227
+ specshift serve
228
+ # or: specshift serve --port 9000 --no-browser
229
+ ```
230
+
231
+ Everything runs in-process on your machine; nothing is uploaded anywhere,
232
+ and the server binds to `127.0.0.1` by default.
233
+
199
234
  ### `specshift check`
200
235
 
201
236
  Designed for CI/CD. Compares the current specification file against a git
@@ -329,14 +364,21 @@ common scenarios:
329
364
  The same table applies to GraphQL: `input` types behave like requests,
330
365
  `type`/`interface` fields behave like responses.
331
366
 
367
+ Protobuf/gRPC follows a different model, based on the wire format rather
368
+ than request/response direction: adding fields or rpc methods is always
369
+ safe, changing a field's number is always breaking, and type changes are
370
+ judged by wire type category (see the `diff-proto` section above).
371
+
332
372
  ## Comparison with other tools
333
373
 
334
374
  | | SpecShift | Raw JSON/YAML diff | oasdiff / openapi-diff style tools |
335
375
  |---|---|---|---|
336
376
  | Context-aware classification | Yes | No | Partially |
337
377
  | GraphQL support | Yes | No | Rarely |
378
+ | Protobuf/gRPC support (wire-aware) | Yes | No | Rarely |
338
379
  | Natural-language summary | Yes (optional) | No | No |
339
380
  | Standalone HTML report | Yes | No | Rarely |
381
+ | Local interactive dashboard | Yes | No | Rarely |
340
382
  | Free to use | Fully free | Free | Usually free |
341
383
  | CI integration | Built-in (`check` + official Action) | Manual | Varies |
342
384
  | Live URL monitoring | Built-in (`watch`) | No | Rarely |
@@ -345,11 +387,13 @@ The same table applies to GraphQL: `input` types behave like requests,
345
387
 
346
388
  This project is under active development. Some planned areas:
347
389
 
348
- - Support for gRPC/Protobuf contracts
349
390
  - Path parameter pattern (regex) and discriminator-level rule refinements
350
- - A hosted, shareable web viewer (beyond the offline HTML report)
351
391
  - Deeper GraphQL support: directive-aware deprecation reasons, custom
352
392
  scalar compatibility hints, and federation-aware schema composition
393
+ - Protobuf `reserved` statement awareness, so a properly reserved field
394
+ number isn't flagged the same way as an unreserved removal
395
+ - Optional shared/hosted history for the `serve` dashboard (currently
396
+ in-memory and per-session by design)
353
397
 
354
398
  Feel free to open an issue if you have a feature request.
355
399
 
@@ -4,8 +4,8 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "specshift"
7
- version = "1.1.0"
8
- description = "Detects, classifies, and optionally summarizes breaking changes in OpenAPI/Swagger and GraphQL contracts using AI"
7
+ version = "1.2.0"
8
+ description = "Detects, classifies, and optionally summarizes breaking changes in OpenAPI/Swagger, GraphQL, and Protobuf/gRPC contracts using AI"
9
9
  readme = "README.md"
10
10
  requires-python = ">=3.9"
11
11
  license = { text = "MIT" }
@@ -14,6 +14,8 @@ keywords = [
14
14
  "openapi",
15
15
  "swagger",
16
16
  "graphql",
17
+ "grpc",
18
+ "protobuf",
17
19
  "api",
18
20
  "breaking-changes",
19
21
  "contract-testing",
@@ -6,15 +6,18 @@ summarizes changes in OpenAPI and Swagger contracts using AI.
6
6
  from specshift.differ import diff_specs
7
7
  from specshift.graphql_differ import diff_graphql, load_sdl as load_graphql_sdl
8
8
  from specshift.models import Change, Severity, ChangeType, DiffResult
9
+ from specshift.protobuf_differ import diff_protos, load_proto
9
10
  from specshift.spec_loader import load_spec
10
11
 
11
- __version__ = "1.1.0"
12
+ __version__ = "1.2.0"
12
13
 
13
14
  __all__ = [
14
15
  "diff_specs",
15
16
  "diff_graphql",
17
+ "diff_protos",
16
18
  "load_spec",
17
19
  "load_graphql_sdl",
20
+ "load_proto",
18
21
  "Change",
19
22
  "Severity",
20
23
  "ChangeType",
@@ -18,6 +18,7 @@ from specshift.git_utils import GitError, load_spec_from_git
18
18
  from specshift.graphql_differ import GraphQLLoadError, diff_graphql, load_sdl
19
19
  from specshift.models import DiffResult
20
20
  from specshift.notifier import NotifyError, notify_discord, notify_slack
21
+ from specshift.protobuf_differ import ProtoLoadError, diff_protos, load_proto
21
22
  from specshift.reporter import (
22
23
  print_console_report,
23
24
  render_html_report,
@@ -32,7 +33,7 @@ CACHE_DIR = Path(".specshift_cache")
32
33
  def build_parser() -> argparse.ArgumentParser:
33
34
  parser = argparse.ArgumentParser(
34
35
  prog="specshift",
35
- description="Detects and classifies changes in OpenAPI/Swagger and GraphQL contracts.",
36
+ description="Detects and classifies changes in OpenAPI/Swagger, GraphQL, and Protobuf/gRPC contracts.",
36
37
  )
37
38
  parser.add_argument("--version", action="version", version=f"specshift {__version__}")
38
39
 
@@ -48,6 +49,11 @@ def build_parser() -> argparse.ArgumentParser:
48
49
  graphql_parser.add_argument("new", help="New GraphQL schema: file path or URL")
49
50
  _add_common_diff_args(graphql_parser)
50
51
 
52
+ proto_parser = subparsers.add_parser("diff-proto", help="Compare two Protobuf/gRPC .proto schemas")
53
+ proto_parser.add_argument("old", help="Old .proto schema: file path or URL")
54
+ proto_parser.add_argument("new", help="New .proto schema: file path or URL")
55
+ _add_common_diff_args(proto_parser)
56
+
51
57
  check_parser = subparsers.add_parser(
52
58
  "check", help="For CI: compares the current specification against a git reference"
53
59
  )
@@ -71,6 +77,13 @@ def build_parser() -> argparse.ArgumentParser:
71
77
  init_parser.add_argument("--path", default=".specshift.yml", help="Path of the file to create")
72
78
  init_parser.add_argument("--force", action="store_true", help="Overwrite an existing file")
73
79
 
80
+ serve_parser = subparsers.add_parser(
81
+ "serve", help="Starts a local web dashboard for comparing specs interactively in the browser"
82
+ )
83
+ serve_parser.add_argument("--host", default="127.0.0.1", help="Host to bind to (default: 127.0.0.1)")
84
+ serve_parser.add_argument("--port", type=int, default=8787, help="Port to listen on (default: 8787)")
85
+ serve_parser.add_argument("--no-browser", action="store_true", help="Don't automatically open a browser tab")
86
+
74
87
  return parser
75
88
 
76
89
 
@@ -102,13 +115,17 @@ def main(argv: Optional[list[str]] = None) -> int:
102
115
  return _run_diff(args)
103
116
  if args.command == "diff-graphql":
104
117
  return _run_diff_graphql(args)
118
+ if args.command == "diff-proto":
119
+ return _run_diff_proto(args)
105
120
  if args.command == "check":
106
121
  return _run_check(args)
107
122
  if args.command == "watch":
108
123
  return _run_watch(args)
109
124
  if args.command == "init":
110
125
  return _run_init(args)
111
- except (SpecLoadError, GitError, GraphQLLoadError) as exc:
126
+ if args.command == "serve":
127
+ return _run_serve(args)
128
+ except (SpecLoadError, GitError, GraphQLLoadError, ProtoLoadError) as exc:
112
129
  print(f"Error: {exc}", file=sys.stderr)
113
130
  return 2
114
131
  except KeyboardInterrupt:
@@ -133,6 +150,13 @@ def _run_diff_graphql(args: argparse.Namespace) -> int:
133
150
  return _emit_result(result, args)
134
151
 
135
152
 
153
+ def _run_diff_proto(args: argparse.Namespace) -> int:
154
+ old_proto = load_proto(args.old)
155
+ new_proto = load_proto(args.new)
156
+ result = diff_protos(old_proto, new_proto)
157
+ return _emit_result(result, args)
158
+
159
+
136
160
  def _run_check(args: argparse.Namespace) -> int:
137
161
  config = SpecShiftConfig.load(args.config)
138
162
  spec_path = args.spec or config.spec_path
@@ -219,6 +243,13 @@ def _run_init(args: argparse.Namespace) -> int:
219
243
  return 0
220
244
 
221
245
 
246
+ def _run_serve(args: argparse.Namespace) -> int:
247
+ from specshift.webapp import run_server
248
+
249
+ run_server(host=args.host, port=args.port, open_browser=not args.no_browser)
250
+ return 0
251
+
252
+
222
253
  def _run_watch(args: argparse.Namespace) -> int:
223
254
  CACHE_DIR.mkdir(exist_ok=True)
224
255
  cache_key = hashlib.sha256(args.url.encode("utf-8")).hexdigest()[:16]