Skip to content

Commit 12c1a35

Browse files
committed
feat(transformer): add Redocly bundling support with jentic-openapi-bundler-redocly
Introduce `jentic-openapi-bundler-redocly` package to enable Redocly-based OpenAPI bundling: - Implement `RedoclyBundler` bundling strategy within the new package. - Add CLI and entry-point configuration to support plugin discovery and extensibility. - Update `jentic-openapi-transformer` with Redocly bundling integration. - Introduce comprehensive test coverage for Redocly bundling edge cases and CLI compatibility. - Update workflows (release/pre-release) and `pyproject.toml` to include new package. - Add documentation and changelog entries for Redocly support. - Adapt Spectral Validator and Validator packages to changes in subprocess result
1 parent d619635 commit 12c1a35

File tree

31 files changed

+1707
-23
lines changed

31 files changed

+1707
-23
lines changed

.github/workflows/ci.yml

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,8 @@ jobs:
7373
uses: astral-sh/setup-uv@v4
7474
- name: Set up Python
7575
run: uv python install ${{ matrix.python }}
76+
- name: Install Redocly CLI
77+
run: npm install -g @redocly/cli@latest
7678
- name: Install deps (with dev)
7779
run: uv sync
7880
- name: Pytest (transformer)
@@ -123,3 +125,26 @@ jobs:
123125
run: uv sync
124126
- name: Pytest (validator-spectral)
125127
run: uv run --package jentic-openapi-validator-spectral pytest -q packages/jentic-openapi-validator-spectral/tests
128+
129+
test_bundler_redocly:
130+
name: Test (jentic-openapi-bundler-redocly)
131+
runs-on: ubuntu-latest
132+
strategy:
133+
matrix:
134+
python: ["3.11", "3.12"]
135+
steps:
136+
- uses: actions/checkout@v4
137+
- name: Install uv
138+
uses: astral-sh/setup-uv@v4
139+
- name: Set up Python
140+
run: uv python install ${{ matrix.python }}
141+
- name: Set up Node.js
142+
uses: actions/setup-node@v4
143+
with:
144+
node-version: '20'
145+
- name: Install Redocly CLI
146+
run: npm install -g @redocly/cli@latest
147+
- name: Install deps (with dev)
148+
run: uv sync
149+
- name: Pytest (bundler-redocly)
150+
run: uv run --package jentic-openapi-bundler-redocly -q pytest packages/jentic-openapi-bundler-redocly/tests

.github/workflows/pre-release.yml

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,9 @@ jobs:
3535
- name: Install Spectral CLI
3636
run: npm install -g @stoplight/spectral-cli
3737

38+
- name: Install Redocly CLI
39+
run: npm install -g @redocly/cli@latest
40+
3841
- name: Install dependencies
3942
run: uv sync
4043

@@ -48,6 +51,7 @@ jobs:
4851
uv run --package jentic-openapi-transformer pytest packages/jentic-openapi-transformer/tests -v
4952
uv run --package jentic-openapi-validator pytest packages/jentic-openapi-validator/tests -v
5053
uv run --package jentic-openapi-validator-spectral pytest packages/jentic-openapi-validator-spectral/tests -v
54+
uv run --package jentic-openapi-bundler-redocly pytest packages/jentic-openapi-bundler-redocly/tests -v
5155
5256
- name: Get next version for pre-release
5357
id: next_version
@@ -64,6 +68,7 @@ jobs:
6468
uv build --package jentic-openapi-transformer
6569
uv build --package jentic-openapi-validator
6670
uv build --package jentic-openapi-validator-spectral
71+
uv build --package jentic-openapi-bundler-redocly
6772
6873
- name: Create pre-release
6974
env:
@@ -84,4 +89,5 @@ jobs:
8489
uv publish --package jentic-openapi-parser
8590
uv publish --package jentic-openapi-transformer
8691
uv publish --package jentic-openapi-validator
87-
uv publish --package jentic-openapi-validator-spectral
92+
uv publish --package jentic-openapi-validator-spectral
93+
uv publish --package jentic-openapi-bundler-redocly

.github/workflows/release.yml

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,9 @@ jobs:
5050
- name: Install Spectral CLI
5151
run: npm install -g @stoplight/spectral-cli
5252

53+
- name: Install Redocly CLI
54+
run: npm install -g @redocly/cli@latest
55+
5356
- name: Install dependencies
5457
run: uv sync
5558

@@ -130,6 +133,7 @@ jobs:
130133
uv run --package jentic-openapi-transformer pytest packages/jentic-openapi-transformer/tests -v
131134
uv run --package jentic-openapi-validator pytest packages/jentic-openapi-validator/tests -v
132135
uv run --package jentic-openapi-validator-spectral pytest packages/jentic-openapi-validator-spectral/tests -v
136+
uv run --package jentic-openapi-bundler-redocly pytest packages/jentic-openapi-bundler-redocly/tests -v
133137
134138
- name: Simulate version bump (DRY RUN)
135139
if: steps.check_release.outputs.release_needed == 'true' && env.DRY_RUN == 'true'
@@ -141,6 +145,7 @@ jobs:
141145
echo " - packages/jentic-openapi-transformer/pyproject.toml"
142146
echo " - packages/jentic-openapi-validator/pyproject.toml"
143147
echo " - packages/jentic-openapi-validator-spectral/pyproject.toml"
148+
echo " - packages/jentic-openapi-bundler-redocly/pyproject.toml"
144149
echo "🔍 DRY RUN: Would create git tag: v${{ steps.version_info.outputs.next_version }}"
145150
146151
- name: Handle first release (DRY RUN)
@@ -176,6 +181,7 @@ jobs:
176181
echo "- jentic-openapi-transformer ${CURRENT_VERSION}" >> CHANGELOG.md
177182
echo "- jentic-openapi-validator ${CURRENT_VERSION}" >> CHANGELOG.md
178183
echo "- jentic-openapi-validator-spectral ${CURRENT_VERSION}" >> CHANGELOG.md
184+
echo "- jentic-openapi-bundler-redocly ${CURRENT_VERSION}" >> CHANGELOG.md
179185
echo "" >> CHANGELOG.md
180186
echo "### Features" >> CHANGELOG.md
181187
echo "- Initial release of Jentic OpenAPI Tools" >> CHANGELOG.md
@@ -209,6 +215,7 @@ jobs:
209215
uv build --package jentic-openapi-transformer
210216
uv build --package jentic-openapi-validator
211217
uv build --package jentic-openapi-validator-spectral
218+
uv build --package jentic-openapi-bundler-redocly
212219
213220
- name: Show build artifacts
214221
if: steps.check_release.outputs.release_needed == 'true'
@@ -231,6 +238,7 @@ jobs:
231238
echo " - jentic-openapi-transformer"
232239
echo " - jentic-openapi-validator"
233240
echo " - jentic-openapi-validator-spectral"
241+
echo " - jentic-openapi-bundler-redocly"
234242
235243
- name: Create GitHub Release (REAL)
236244
if: steps.check_release.outputs.release_needed == 'true' && env.DRY_RUN == 'false'
@@ -259,6 +267,7 @@ jobs:
259267
uv publish --package jentic-openapi-transformer
260268
uv publish --package jentic-openapi-validator
261269
uv publish --package jentic-openapi-validator-spectral
270+
uv publish --package jentic-openapi-bundler-redocly
262271
263272
- name: Release Summary (DRY RUN)
264273
if: steps.check_release.outputs.release_needed == 'true' && env.DRY_RUN == 'true'

README.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ uv run --package jentic-openapi-parser pytest packages/jentic-openapi-pars
1818
uv run --package jentic-openapi-transformer pytest packages/jentic-openapi-transformer/tests -q
1919
uv run --package jentic-openapi-validator pytest packages/jentic-openapi-validator/tests -q
2020
uv run --package jentic-openapi-validator-spectral pytest packages/jentic-openapi-validator-spectral/tests -q
21+
uv run --package jentic-openapi-bundler-redocly pytest packages/jentic-openapi-bundler-redocly/tests -q
2122
```
2223

2324
### Run linting
@@ -32,6 +33,7 @@ uv build --package jentic-openapi-parser
3233
uv build --package jentic-openapi-transformer
3334
uv build --package jentic-openapi-validator
3435
uv build --package jentic-openapi-validator-spectral
36+
uv build --package jentic-openapi-bundler-redocly
3537
```
3638

3739
### Conventional Commits
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
# jentic-openapi-bundler-redocly
2+
3+
## Running the tests
4+
To run these integration tests, you would need both packages installed. You can test this setup by:
5+
6+
1. **Installing both packages in development mode:**
7+
8+
```
9+
# From the project root
10+
uv run pip install -e packages/jentic-openapi-transformer
11+
uv run pip install -e packages/jentic-openapi-bundler-redocly
12+
```
13+
14+
2. **Running the integration test:**
15+
16+
```
17+
uv run --package jentic-openapi-transformer pytest packages/jentic-openapi-transformer/tests/test_bundle_redocly.py -v
18+
```
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
[project]
2+
name = "jentic-openapi-bundler-redocly"
3+
version = "0.1.0"
4+
description = "Jentic OpenAPI Redocly Bundler Strategy"
5+
readme = "README.md"
6+
authors = [{ name = "Jentic", email = "hello@jentic.com" }]
7+
license = { text = "Apache-2.0" }
8+
requires-python = ">=3.11"
9+
dependencies = [
10+
"jentic-openapi-common",
11+
"jentic-openapi-transformer",
12+
"importlib-resources>=1.3.0;python_version<'3.9'"
13+
]
14+
15+
[project.urls]
16+
Homepage = "https://github.com/jentic/jentic-openapi-tools"
17+
18+
[tool.uv.sources]
19+
jentic-openapi-transformer = { workspace = true }
20+
21+
[build-system]
22+
requires = ["uv_build>=0.8.15,<0.9.0"]
23+
build-backend = "uv_build"
24+
25+
[project.entry-points."jentic.openapi_bundler_strategies"]
26+
redocly = "jentic_openapi_bundler_redocly.redocly_bundler:RedoclyBundler"
27+
28+
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
from .redocly_bundler import RedoclyBundler
2+
3+
__all__ = ["RedoclyBundler"]
Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
from typing import Any
2+
from urllib.parse import urlparse
3+
from urllib.request import url2pathname
4+
5+
from jentic_openapi_transformer.strategies.base import BaseBundlerStrategy
6+
7+
from jentic_openapi_common import run_checked, SubprocessExecutionError
8+
9+
10+
class RedoclyBundler(BaseBundlerStrategy):
11+
def __init__(self, redocly_path: str = "redocly"):
12+
"""
13+
Initialize the RedoclyBundler.
14+
15+
Args:
16+
redocly_path: Path to the redocly CLI executable (default: "redocly")
17+
"""
18+
self.redocly_path = redocly_path
19+
20+
def accepts(self) -> list[str]:
21+
return ["uri"]
22+
23+
def bundle(self, document: str | dict, base_url: str | None = None) -> Any:
24+
try:
25+
assert isinstance(document, str)
26+
return self._bundle(document, base_url)
27+
except Exception as e:
28+
# TODO add to parser/validation chain result
29+
raise e
30+
31+
def _bundle(self, document: str, base_url: str | None = None) -> str:
32+
try:
33+
parsed_doc_url = urlparse(document)
34+
doc_path = url2pathname(parsed_doc_url.path)
35+
# Build redocly command
36+
cmd = [
37+
self.redocly_path,
38+
"bundle",
39+
doc_path,
40+
"--ext",
41+
"json",
42+
"--lint-config",
43+
"off",
44+
"--force",
45+
# "--remove-unused-components", # TODO, raises errors in redocly for some reason
46+
]
47+
result = run_checked(cmd)
48+
except SubprocessExecutionError as e:
49+
# only timeout and OS errors, as run_checked has default `fail_on_error = False`
50+
raise e
51+
52+
if result.stdout:
53+
# When using `--force` Redocly tries bundling even on errors
54+
output_lines = result.stdout.splitlines()
55+
if output_lines and output_lines[-1].startswith("Created a bundle"):
56+
output_lines = output_lines[:-1]
57+
58+
if result.returncode != 0 and result.stderr:
59+
# TODO we need to add any errors to the parser/validation chain result
60+
print(f"Error parsing document: {result.stderr}")
61+
62+
return "\n".join(output_lines)
63+
else:
64+
err = (result.stderr or "").strip()
65+
msg = err or f"Redocly exited with code {result.returncode}"
66+
raise Exception(msg)

0 commit comments

Comments
 (0)