Skip to content
This repository was archived by the owner on Jun 2, 2026. It is now read-only.

Commit 3c8396a

Browse files
committed
Split CLI into a separate wrapper package
1 parent 8af8ef2 commit 3c8396a

10 files changed

Lines changed: 121 additions & 25 deletions

File tree

CONTRIBUTING.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
This project uses `uv`, so set up the virtualenv by running
44

55
```
6-
uv sync --dev --extra cli
6+
uv sync --dev
77
```
88

99
Use `make test` to make sure all tests pass before pushing.

README.md

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ Multiple changes done on top of default openapi-generator:
1616
* Remove most of pydantic/schema validations due to inconsistencies with actual database schema/requirements (tracked in https://github.com/fopina/defectdojo-api-generated/issues/39)
1717
* *Iterator* methods for every *list* API method to handle pagination automatically
1818
* A nice CLI exposing all the API methods <3
19-
* installed only as an extra, to keep everything clean when package is used as library only
19+
* published as a separate package, to keep library-only installs free of console-script conflicts
2020

2121
## Example
2222

@@ -49,7 +49,7 @@ pip install defectojo-api-generated
4949
> [uv](https://docs.astral.sh/uv/) recommended or [pipx](https://github.com/pypa/pipx)
5050
5151
```
52-
uv tool install 'defectojo-api-generated[cli]'
52+
uv tool install defectdojo-api-generated-cli
5353
```
5454

5555
```
@@ -69,7 +69,7 @@ Commands:
6969
You can also skip tool install and just run it with:
7070

7171
```
72-
$ uvx 'defectojo-api-generated[cli]'
72+
$ uvx defectdojo-api-generated-cli
7373
Usage: dojo [OPTIONS] COMMAND [ARGS]...
7474
...
7575
```

packages/cli/.gitignore

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
.venv/
2+
dist/
3+
src/*.egg-info/
4+
uv.lock

packages/cli/README.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
# defectdojo-api-generated-cli
2+
3+
Thin CLI wrapper distribution for [`defectdojo-api-generated`](https://pypi.org/project/defectdojo-api-generated/).
4+
5+
This package owns the `dojo` console entrypoint and pulls in the optional CLI dependencies.

packages/cli/pyproject.toml

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
[build-system]
2+
requires = ["setuptools>=69.5.0,<80.0.0"]
3+
build-backend = "setuptools.build_meta"
4+
5+
[project]
6+
name = "defectdojo-api-generated-cli"
7+
version = "1.0.0"
8+
description = "CLI wrapper for defectdojo-api-generated"
9+
readme = "README.md"
10+
requires-python = ">=3.9,<4"
11+
dependencies = [
12+
"classyclick==1.0.0",
13+
"defectdojo-api-generated==1.0.0",
14+
"jmespath>=1.0.1",
15+
]
16+
17+
[project.scripts]
18+
dojo = "defectdojo_api_generated_cli.__main__:main"
19+
defectdojo-api-generated = "defectdojo_api_generated_cli.__main__:main"
20+
21+
[project.urls]
22+
Homepage = "https://github.com/fopina/defectdojo-api-generated"
23+
24+
[tool.uv.sources]
25+
defectdojo-api-generated = { path = "../.." }
26+
27+
[tool.setuptools.packages.find]
28+
where = ["src"]
29+
include = ["defectdojo_api_generated_cli"]
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
"""Thin wrapper distribution for the DefectDojo CLI."""
2+
3+
__version__ = '1.0.0'
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
"""Entrypoint owned by the separate CLI distribution."""
2+
3+
from defectdojo_api_generated.cli.__main__ import main
4+
5+
6+
if __name__ == '__main__':
7+
main()

pyproject.toml

Lines changed: 3 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -16,20 +16,11 @@ dependencies = [
1616
"typing-extensions>= 4.7.1",
1717
]
1818

19-
[project.optional-dependencies]
20-
cli = [
21-
"classyclick==1.0.0",
22-
"jmespath>=1.0.1",
23-
]
24-
25-
[project.scripts]
26-
dojo = "defectdojo_api_generated.cli.__main__:main"
27-
# duplicated entrypoint to be able to use uvx without `--from`
28-
defectdojo-api-generated = "defectdojo_api_generated.cli.__main__:main"
29-
3019
[dependency-groups]
3120
dev = [
3221
"build",
22+
"classyclick==1.0.0",
23+
"jmespath>=1.0.1",
3324
"mkdocs>=1.6.1",
3425
"mkdocs-click>=0.9.0",
3526
"pytest",
@@ -44,7 +35,7 @@ dev = [
4435
Homepage = "https://github.com/fopina/defectdojo-api-generated"
4536

4637
[tool.setuptools.packages.find]
47-
include = ["defectdojo_api_generated*"]
38+
include = ["defectdojo_api_generated", "defectdojo_api_generated.*"]
4839

4940
[tool.setuptools.dynamic]
5041
version = {attr = "defectdojo_api_generated.__version__"}

tests/unit/test_packaging.py

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
import pathlib
2+
import re
3+
import tomllib
4+
import unittest
5+
6+
import defectdojo_api_generated
7+
8+
9+
ROOT = pathlib.Path(__file__).resolve().parents[2]
10+
11+
12+
class TestPackagingLayout(unittest.TestCase):
13+
def test_library_package_has_no_console_scripts(self):
14+
pyproject = tomllib.loads((ROOT / 'pyproject.toml').read_text())
15+
16+
self.assertNotIn('scripts', pyproject['project'])
17+
18+
def test_cli_wrapper_package_owns_console_scripts(self):
19+
pyproject = tomllib.loads((ROOT / 'packages' / 'cli' / 'pyproject.toml').read_text())
20+
21+
self.assertEqual(
22+
pyproject['project']['scripts'],
23+
{
24+
'dojo': 'defectdojo_api_generated_cli.__main__:main',
25+
'defectdojo-api-generated': 'defectdojo_api_generated_cli.__main__:main',
26+
},
27+
)
28+
29+
def test_cli_wrapper_version_matches_library_version(self):
30+
wrapper_init = (ROOT / 'packages' / 'cli' / 'src' / 'defectdojo_api_generated_cli' / '__init__.py').read_text()
31+
match = re.search(r"__version__ = '([^']+)'", wrapper_init)
32+
33+
self.assertIsNotNone(match)
34+
self.assertEqual(match.group(1), defectdojo_api_generated.__version__)
35+
36+
def test_cli_wrapper_dependency_matches_library_version(self):
37+
pyproject = tomllib.loads((ROOT / 'packages' / 'cli' / 'pyproject.toml').read_text())
38+
39+
self.assertIn(
40+
f'defectdojo-api-generated=={defectdojo_api_generated.__version__}',
41+
pyproject['project']['dependencies'],
42+
)

0 commit comments

Comments
 (0)