csrd-utils 0.3.36__tar.gz → 0.3.38__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.
- {csrd_utils-0.3.36 → csrd_utils-0.3.38}/PKG-INFO +1 -1
- {csrd_utils-0.3.36 → csrd_utils-0.3.38}/pyproject.toml +1 -1
- {csrd_utils-0.3.36 → csrd_utils-0.3.38}/src/csrd_utils/__main__.py +13 -7
- {csrd_utils-0.3.36 → csrd_utils-0.3.38}/src/csrd_utils/augmentor.py +41 -8
- {csrd_utils-0.3.36 → csrd_utils-0.3.38}/src/csrd_utils/doctor.py +31 -6
- {csrd_utils-0.3.36 → csrd_utils-0.3.38}/src/csrd_utils/generator.py +1 -1
- {csrd_utils-0.3.36 → csrd_utils-0.3.38}/src/csrd_utils/templates/cookiecutter-service/hooks/post_gen_project.py +12 -12
- csrd_utils-0.3.38/src/csrd_utils/templates/cookiecutter-service/{{cookiecutter.__service_name_snake}}/.cookiecutterignore +1 -0
- {csrd_utils-0.3.36/src/csrd_utils/templates/cookiecutter-service/{{cookiecutter.service_name}} → csrd_utils-0.3.38/src/csrd_utils/templates/cookiecutter-service/{{cookiecutter.__service_name_snake}}}/Dockerfile +2 -2
- {csrd_utils-0.3.36/src/csrd_utils/templates/cookiecutter-service/{{cookiecutter.service_name}} → csrd_utils-0.3.38/src/csrd_utils/templates/cookiecutter-service/{{cookiecutter.__service_name_snake}}}/README.md +1 -2
- csrd_utils-0.3.36/src/csrd_utils/templates/cookiecutter-service/{{cookiecutter.service_name}}/.env.example +0 -44
- csrd_utils-0.3.36/src/csrd_utils/templates/cookiecutter-service/{{cookiecutter.service_name}}/.gitignore +0 -64
- {csrd_utils-0.3.36 → csrd_utils-0.3.38}/.gitignore +0 -0
- {csrd_utils-0.3.36 → csrd_utils-0.3.38}/README.md +0 -0
- {csrd_utils-0.3.36 → csrd_utils-0.3.38}/src/csrd_utils/__init__.py +0 -0
- {csrd_utils-0.3.36 → csrd_utils-0.3.38}/src/csrd_utils/audit.py +0 -0
- {csrd_utils-0.3.36 → csrd_utils-0.3.38}/src/csrd_utils/cluster.py +0 -0
- {csrd_utils-0.3.36 → csrd_utils-0.3.38}/src/csrd_utils/features/caching/conftest.fragment.py +0 -0
- {csrd_utils-0.3.36 → csrd_utils-0.3.38}/src/csrd_utils/features/caching/docker-compose.fragment.yaml +0 -0
- {csrd_utils-0.3.36 → csrd_utils-0.3.38}/src/csrd_utils/features/caching/manifest.yaml +0 -0
- {csrd_utils-0.3.36 → csrd_utils-0.3.38}/src/csrd_utils/features/caching/requirements.fragment +0 -0
- {csrd_utils-0.3.36 → csrd_utils-0.3.38}/src/csrd_utils/features/caching/settings.fragment.py +0 -0
- {csrd_utils-0.3.36 → csrd_utils-0.3.38}/src/csrd_utils/features/caching/src/app/cache.py +0 -0
- {csrd_utils-0.3.36 → csrd_utils-0.3.38}/src/csrd_utils/features/caching/tests/test_cache.py +0 -0
- {csrd_utils-0.3.36 → csrd_utils-0.3.38}/src/csrd_utils/features/logging/conftest.fragment.py +0 -0
- {csrd_utils-0.3.36 → csrd_utils-0.3.38}/src/csrd_utils/features/logging/manifest.yaml +0 -0
- {csrd_utils-0.3.36 → csrd_utils-0.3.38}/src/csrd_utils/features/logging/requirements.fragment +0 -0
- {csrd_utils-0.3.36 → csrd_utils-0.3.38}/src/csrd_utils/features/logging/settings.fragment.py +0 -0
- {csrd_utils-0.3.36 → csrd_utils-0.3.38}/src/csrd_utils/features/logging/src/app/middleware/logging.py +0 -0
- {csrd_utils-0.3.36 → csrd_utils-0.3.38}/src/csrd_utils/features/logging/tests/test_logging_middleware.py +0 -0
- {csrd_utils-0.3.36 → csrd_utils-0.3.38}/src/csrd_utils/features/metrics/conftest.fragment.py +0 -0
- {csrd_utils-0.3.36 → csrd_utils-0.3.38}/src/csrd_utils/features/metrics/docker-compose.fragment.yaml +0 -0
- {csrd_utils-0.3.36 → csrd_utils-0.3.38}/src/csrd_utils/features/metrics/manifest.yaml +0 -0
- {csrd_utils-0.3.36 → csrd_utils-0.3.38}/src/csrd_utils/features/metrics/requirements.fragment +0 -0
- {csrd_utils-0.3.36 → csrd_utils-0.3.38}/src/csrd_utils/features/metrics/settings.fragment.py +0 -0
- {csrd_utils-0.3.36 → csrd_utils-0.3.38}/src/csrd_utils/features/metrics/src/app/middleware/metrics.py +0 -0
- {csrd_utils-0.3.36 → csrd_utils-0.3.38}/src/csrd_utils/features/metrics/tests/test_metrics_middleware.py +0 -0
- {csrd_utils-0.3.36 → csrd_utils-0.3.38}/src/csrd_utils/features/tracing/conftest.fragment.py +0 -0
- {csrd_utils-0.3.36 → csrd_utils-0.3.38}/src/csrd_utils/features/tracing/manifest.yaml +0 -0
- {csrd_utils-0.3.36 → csrd_utils-0.3.38}/src/csrd_utils/features/tracing/requirements.fragment +0 -0
- {csrd_utils-0.3.36 → csrd_utils-0.3.38}/src/csrd_utils/features/tracing/settings.fragment.py +0 -0
- {csrd_utils-0.3.36 → csrd_utils-0.3.38}/src/csrd_utils/features/tracing/src/app/middleware/tracing.py +0 -0
- {csrd_utils-0.3.36 → csrd_utils-0.3.38}/src/csrd_utils/features/tracing/tests/test_tracing_middleware.py +0 -0
- {csrd_utils-0.3.36 → csrd_utils-0.3.38}/src/csrd_utils/features/workers/conftest.fragment.py +0 -0
- {csrd_utils-0.3.36 → csrd_utils-0.3.38}/src/csrd_utils/features/workers/docker-compose.fragment.yaml +0 -0
- {csrd_utils-0.3.36 → csrd_utils-0.3.38}/src/csrd_utils/features/workers/manifest.yaml +0 -0
- {csrd_utils-0.3.36 → csrd_utils-0.3.38}/src/csrd_utils/features/workers/requirements.fragment +0 -0
- {csrd_utils-0.3.36 → csrd_utils-0.3.38}/src/csrd_utils/features/workers/settings.fragment.py +0 -0
- {csrd_utils-0.3.36 → csrd_utils-0.3.38}/src/csrd_utils/features/workers/src/app/workers/__init__.py +0 -0
- {csrd_utils-0.3.36 → csrd_utils-0.3.38}/src/csrd_utils/features/workers/src/app/workers/celery_app.py +0 -0
- {csrd_utils-0.3.36 → csrd_utils-0.3.38}/src/csrd_utils/features/workers/src/app/workers/tasks.py +0 -0
- {csrd_utils-0.3.36 → csrd_utils-0.3.38}/src/csrd_utils/features/workers/tests/test_workers.py +0 -0
- {csrd_utils-0.3.36 → csrd_utils-0.3.38}/src/csrd_utils/fragments.py +0 -0
- {csrd_utils-0.3.36 → csrd_utils-0.3.38}/src/csrd_utils/resources.py +0 -0
- {csrd_utils-0.3.36 → csrd_utils-0.3.38}/src/csrd_utils/templates/cookiecutter-service/README.md +0 -0
- {csrd_utils-0.3.36 → csrd_utils-0.3.38}/src/csrd_utils/templates/cookiecutter-service/cookiecutter.json +0 -0
- {csrd_utils-0.3.36/src/csrd_utils/templates/cookiecutter-service/{{cookiecutter.service_name}} → csrd_utils-0.3.38/src/csrd_utils/templates/cookiecutter-service/{{cookiecutter.__service_name_snake}}}/.dockerignore +0 -0
- {csrd_utils-0.3.36/src/csrd_utils/templates/cookiecutter-service/{{cookiecutter.service_name}} → csrd_utils-0.3.38/src/csrd_utils/templates/cookiecutter-service/{{cookiecutter.__service_name_snake}}}/docker-compose.yml +0 -0
- {csrd_utils-0.3.36/src/csrd_utils/templates/cookiecutter-service/{{cookiecutter.service_name}} → csrd_utils-0.3.38/src/csrd_utils/templates/cookiecutter-service/{{cookiecutter.__service_name_snake}}}/pyproject.toml +0 -0
- {csrd_utils-0.3.36/src/csrd_utils/templates/cookiecutter-service/{{cookiecutter.service_name}} → csrd_utils-0.3.38/src/csrd_utils/templates/cookiecutter-service/{{cookiecutter.__service_name_snake}}}/requirements.txt +0 -0
- {csrd_utils-0.3.36/src/csrd_utils/templates/cookiecutter-service/{{cookiecutter.service_name}} → csrd_utils-0.3.38/src/csrd_utils/templates/cookiecutter-service/{{cookiecutter.__service_name_snake}}/src}/{{cookiecutter.__service_name_snake}}/__init__.py +0 -0
- {csrd_utils-0.3.36/src/csrd_utils/templates/cookiecutter-service/{{cookiecutter.service_name}} → csrd_utils-0.3.38/src/csrd_utils/templates/cookiecutter-service/{{cookiecutter.__service_name_snake}}/src}/{{cookiecutter.__service_name_snake}}/cache.py +0 -0
- {csrd_utils-0.3.36/src/csrd_utils/templates/cookiecutter-service/{{cookiecutter.service_name}} → csrd_utils-0.3.38/src/csrd_utils/templates/cookiecutter-service/{{cookiecutter.__service_name_snake}}/src}/{{cookiecutter.__service_name_snake}}/delegates/__init__.py +0 -0
- {csrd_utils-0.3.36/src/csrd_utils/templates/cookiecutter-service/{{cookiecutter.service_name}} → csrd_utils-0.3.38/src/csrd_utils/templates/cookiecutter-service/{{cookiecutter.__service_name_snake}}/src}/{{cookiecutter.__service_name_snake}}/delegates/upstreams.py +0 -0
- {csrd_utils-0.3.36/src/csrd_utils/templates/cookiecutter-service/{{cookiecutter.service_name}} → csrd_utils-0.3.38/src/csrd_utils/templates/cookiecutter-service/{{cookiecutter.__service_name_snake}}/src}/{{cookiecutter.__service_name_snake}}/dependencies/__init__.py +0 -0
- {csrd_utils-0.3.36/src/csrd_utils/templates/cookiecutter-service/{{cookiecutter.service_name}} → csrd_utils-0.3.38/src/csrd_utils/templates/cookiecutter-service/{{cookiecutter.__service_name_snake}}/src}/{{cookiecutter.__service_name_snake}}/dependencies/delegates.py +0 -0
- {csrd_utils-0.3.36/src/csrd_utils/templates/cookiecutter-service/{{cookiecutter.service_name}} → csrd_utils-0.3.38/src/csrd_utils/templates/cookiecutter-service/{{cookiecutter.__service_name_snake}}/src}/{{cookiecutter.__service_name_snake}}/dependencies/item_repository.py +0 -0
- {csrd_utils-0.3.36/src/csrd_utils/templates/cookiecutter-service/{{cookiecutter.service_name}} → csrd_utils-0.3.38/src/csrd_utils/templates/cookiecutter-service/{{cookiecutter.__service_name_snake}}/src}/{{cookiecutter.__service_name_snake}}/dependencies/worker_broker.py +0 -0
- {csrd_utils-0.3.36/src/csrd_utils/templates/cookiecutter-service/{{cookiecutter.service_name}} → csrd_utils-0.3.38/src/csrd_utils/templates/cookiecutter-service/{{cookiecutter.__service_name_snake}}/src}/{{cookiecutter.__service_name_snake}}/middleware/__init__.py +0 -0
- {csrd_utils-0.3.36/src/csrd_utils/templates/cookiecutter-service/{{cookiecutter.service_name}} → csrd_utils-0.3.38/src/csrd_utils/templates/cookiecutter-service/{{cookiecutter.__service_name_snake}}/src}/{{cookiecutter.__service_name_snake}}/middleware/logging.py +0 -0
- {csrd_utils-0.3.36/src/csrd_utils/templates/cookiecutter-service/{{cookiecutter.service_name}} → csrd_utils-0.3.38/src/csrd_utils/templates/cookiecutter-service/{{cookiecutter.__service_name_snake}}/src}/{{cookiecutter.__service_name_snake}}/py.typed +0 -0
- {csrd_utils-0.3.36/src/csrd_utils/templates/cookiecutter-service/{{cookiecutter.service_name}} → csrd_utils-0.3.38/src/csrd_utils/templates/cookiecutter-service/{{cookiecutter.__service_name_snake}}/src}/{{cookiecutter.__service_name_snake}}/repositories/__init__.py +0 -0
- {csrd_utils-0.3.36/src/csrd_utils/templates/cookiecutter-service/{{cookiecutter.service_name}} → csrd_utils-0.3.38/src/csrd_utils/templates/cookiecutter-service/{{cookiecutter.__service_name_snake}}/src}/{{cookiecutter.__service_name_snake}}/repositories/item_repository.py +0 -0
- {csrd_utils-0.3.36/src/csrd_utils/templates/cookiecutter-service/{{cookiecutter.service_name}} → csrd_utils-0.3.38/src/csrd_utils/templates/cookiecutter-service/{{cookiecutter.__service_name_snake}}/src}/{{cookiecutter.__service_name_snake}}/settings.py +0 -0
- {csrd_utils-0.3.36/src/csrd_utils/templates/cookiecutter-service/{{cookiecutter.service_name}} → csrd_utils-0.3.38/src/csrd_utils/templates/cookiecutter-service/{{cookiecutter.__service_name_snake}}/src}/{{cookiecutter.__service_name_snake}}/views/__init__.py +0 -0
- {csrd_utils-0.3.36/src/csrd_utils/templates/cookiecutter-service/{{cookiecutter.service_name}} → csrd_utils-0.3.38/src/csrd_utils/templates/cookiecutter-service/{{cookiecutter.__service_name_snake}}/src}/{{cookiecutter.__service_name_snake}}/views/unversioned/__init__.py +0 -0
- {csrd_utils-0.3.36/src/csrd_utils/templates/cookiecutter-service/{{cookiecutter.service_name}} → csrd_utils-0.3.38/src/csrd_utils/templates/cookiecutter-service/{{cookiecutter.__service_name_snake}}/src}/{{cookiecutter.__service_name_snake}}/views/unversioned/health_view.py +0 -0
- {csrd_utils-0.3.36/src/csrd_utils/templates/cookiecutter-service/{{cookiecutter.service_name}} → csrd_utils-0.3.38/src/csrd_utils/templates/cookiecutter-service/{{cookiecutter.__service_name_snake}}/src}/{{cookiecutter.__service_name_snake}}/views/unversioned/items_view.py +0 -0
- {csrd_utils-0.3.36/src/csrd_utils/templates/cookiecutter-service/{{cookiecutter.service_name}} → csrd_utils-0.3.38/src/csrd_utils/templates/cookiecutter-service/{{cookiecutter.__service_name_snake}}/src}/{{cookiecutter.__service_name_snake}}/workers/__init__.py +0 -0
- {csrd_utils-0.3.36/src/csrd_utils/templates/cookiecutter-service/{{cookiecutter.service_name}} → csrd_utils-0.3.38/src/csrd_utils/templates/cookiecutter-service/{{cookiecutter.__service_name_snake}}/src}/{{cookiecutter.__service_name_snake}}/workers/tasks.py +0 -0
- {csrd_utils-0.3.36/src/csrd_utils/templates/cookiecutter-service/{{cookiecutter.service_name}} → csrd_utils-0.3.38/src/csrd_utils/templates/cookiecutter-service/{{cookiecutter.__service_name_snake}}}/tests/__init__.py +0 -0
- {csrd_utils-0.3.36/src/csrd_utils/templates/cookiecutter-service/{{cookiecutter.service_name}} → csrd_utils-0.3.38/src/csrd_utils/templates/cookiecutter-service/{{cookiecutter.__service_name_snake}}}/tests/acceptance/test_logging_middleware.py +0 -0
- {csrd_utils-0.3.36/src/csrd_utils/templates/cookiecutter-service/{{cookiecutter.service_name}} → csrd_utils-0.3.38/src/csrd_utils/templates/cookiecutter-service/{{cookiecutter.__service_name_snake}}}/tests/conftest.py +0 -0
- {csrd_utils-0.3.36/src/csrd_utils/templates/cookiecutter-service/{{cookiecutter.service_name}} → csrd_utils-0.3.38/src/csrd_utils/templates/cookiecutter-service/{{cookiecutter.__service_name_snake}}}/tests/test_items.py +0 -0
- {csrd_utils-0.3.36/src/csrd_utils/templates/cookiecutter-service/{{cookiecutter.service_name}} → csrd_utils-0.3.38/src/csrd_utils/templates/cookiecutter-service/{{cookiecutter.__service_name_snake}}}/tests/unit/test_cache.py +0 -0
- {csrd_utils-0.3.36/src/csrd_utils/templates/cookiecutter-service/{{cookiecutter.service_name}} → csrd_utils-0.3.38/src/csrd_utils/templates/cookiecutter-service/{{cookiecutter.__service_name_snake}}}/tests/unit/test_database_settings.py +0 -0
- {csrd_utils-0.3.36/src/csrd_utils/templates/cookiecutter-service/{{cookiecutter.service_name}} → csrd_utils-0.3.38/src/csrd_utils/templates/cookiecutter-service/{{cookiecutter.__service_name_snake}}}/tests/unit/test_delegates.py +0 -0
- {csrd_utils-0.3.36/src/csrd_utils/templates/cookiecutter-service/{{cookiecutter.service_name}} → csrd_utils-0.3.38/src/csrd_utils/templates/cookiecutter-service/{{cookiecutter.__service_name_snake}}}/tests/unit/test_settings_contracts.py +0 -0
- {csrd_utils-0.3.36/src/csrd_utils/templates/cookiecutter-service/{{cookiecutter.service_name}} → csrd_utils-0.3.38/src/csrd_utils/templates/cookiecutter-service/{{cookiecutter.__service_name_snake}}}/tests/unit/test_worker_broker.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: csrd-utils
|
|
3
|
-
Version: 0.3.
|
|
3
|
+
Version: 0.3.38
|
|
4
4
|
Summary: CLI utilities for csrd service generation and feature augmentation
|
|
5
5
|
Project-URL: Repository, https://github.com/csrd-api/fastapi-common
|
|
6
6
|
Project-URL: Documentation, https://github.com/csrd-api/fastapi-common/tree/main/packages/utils
|
|
@@ -580,6 +580,16 @@ def _append_service_to_workspace_cluster(
|
|
|
580
580
|
return normalized
|
|
581
581
|
|
|
582
582
|
|
|
583
|
+
def _is_service_root(path: Path) -> bool:
|
|
584
|
+
"""Return True if path looks like a csrd service (flat layout)."""
|
|
585
|
+
return (
|
|
586
|
+
path.is_dir()
|
|
587
|
+
and (path / "requirements.txt").is_file()
|
|
588
|
+
and (path / "tests").is_dir()
|
|
589
|
+
and (path / "pyproject.toml").is_file()
|
|
590
|
+
)
|
|
591
|
+
|
|
592
|
+
|
|
583
593
|
def _workspace_service_roots(workspace_root: Path) -> list[Path]:
|
|
584
594
|
roots: set[Path] = set()
|
|
585
595
|
|
|
@@ -591,11 +601,7 @@ def _workspace_service_roots(workspace_root: Path) -> list[Path]:
|
|
|
591
601
|
continue
|
|
592
602
|
if skip_names and child.name in skip_names:
|
|
593
603
|
continue
|
|
594
|
-
if (
|
|
595
|
-
(child / "src" / "app").is_dir()
|
|
596
|
-
and (child / "tests").is_dir()
|
|
597
|
-
and (child / "requirements.txt").is_file()
|
|
598
|
-
):
|
|
604
|
+
if _is_service_root(child):
|
|
599
605
|
roots.add(child.resolve())
|
|
600
606
|
|
|
601
607
|
services_root = _workspace_services_dir(workspace_root)
|
|
@@ -632,12 +638,12 @@ def _workspace_root_for_service(service_root: Path) -> Path | None:
|
|
|
632
638
|
def _validate_service_root(service_root: Path) -> None:
|
|
633
639
|
if not service_root.is_dir():
|
|
634
640
|
raise ValueError(f"Service path is not a directory: {service_root}")
|
|
635
|
-
if not (service_root / "src" / "app").is_dir():
|
|
636
|
-
raise ValueError(f"Missing required directory: {service_root / 'src' / 'app'}")
|
|
637
641
|
if not (service_root / "tests").is_dir():
|
|
638
642
|
raise ValueError(f"Missing required directory: {service_root / 'tests'}")
|
|
639
643
|
if not (service_root / "requirements.txt").is_file():
|
|
640
644
|
raise ValueError(f"Missing required file: {service_root / 'requirements.txt'}")
|
|
645
|
+
if not (service_root / "pyproject.toml").is_file():
|
|
646
|
+
raise ValueError(f"Missing required file: {service_root / 'pyproject.toml'}")
|
|
641
647
|
|
|
642
648
|
|
|
643
649
|
def _collect_workspace_extract_config(args: argparse.Namespace) -> ExtractWorkspaceServiceArgs:
|
|
@@ -37,6 +37,35 @@ class ServiceAugmentor:
|
|
|
37
37
|
self.applied_changes: list[str] = []
|
|
38
38
|
self.backups: dict[str, str] = {}
|
|
39
39
|
|
|
40
|
+
@property
|
|
41
|
+
def _pkg_dir(self) -> Path:
|
|
42
|
+
"""Return the service package directory — src/ layout first, then flat."""
|
|
43
|
+
src_dir = self.service_root / "src"
|
|
44
|
+
if src_dir.is_dir():
|
|
45
|
+
candidate = next(
|
|
46
|
+
(
|
|
47
|
+
p
|
|
48
|
+
for p in src_dir.iterdir()
|
|
49
|
+
if p.is_dir() and not p.name.startswith(".") and (p / "__init__.py").is_file()
|
|
50
|
+
),
|
|
51
|
+
None,
|
|
52
|
+
)
|
|
53
|
+
if candidate is not None:
|
|
54
|
+
return candidate
|
|
55
|
+
# Flat layout fallback
|
|
56
|
+
candidate = next(
|
|
57
|
+
(
|
|
58
|
+
p
|
|
59
|
+
for p in self.service_root.iterdir()
|
|
60
|
+
if p.is_dir()
|
|
61
|
+
and not p.name.startswith(".")
|
|
62
|
+
and p.name not in {"tests", "src"}
|
|
63
|
+
and (p / "__init__.py").is_file()
|
|
64
|
+
),
|
|
65
|
+
None,
|
|
66
|
+
)
|
|
67
|
+
return candidate if candidate is not None else self.service_root / "src" / "app"
|
|
68
|
+
|
|
40
69
|
def load_feature(self, feature_name: str) -> bool:
|
|
41
70
|
"""
|
|
42
71
|
Load and validate feature manifest.
|
|
@@ -72,14 +101,14 @@ class ServiceAugmentor:
|
|
|
72
101
|
"""
|
|
73
102
|
Validate that service has required structure.
|
|
74
103
|
|
|
75
|
-
Checks for
|
|
104
|
+
Checks for <pkg>/, docker-compose.yml, etc.
|
|
76
105
|
|
|
77
106
|
Returns
|
|
78
107
|
-------
|
|
79
108
|
bool
|
|
80
109
|
True if valid
|
|
81
110
|
"""
|
|
82
|
-
required_dirs = [
|
|
111
|
+
required_dirs = [str(self._pkg_dir.relative_to(self.service_root)), "tests"]
|
|
83
112
|
required_files = ["requirements.txt"]
|
|
84
113
|
|
|
85
114
|
for d in required_dirs:
|
|
@@ -245,7 +274,7 @@ class ServiceAugmentor:
|
|
|
245
274
|
return True # Not required
|
|
246
275
|
|
|
247
276
|
try:
|
|
248
|
-
settings_path = self.
|
|
277
|
+
settings_path = self._pkg_dir / "settings.py"
|
|
249
278
|
if not settings_path.exists():
|
|
250
279
|
print("⚠ settings.py not found, skipping")
|
|
251
280
|
return True
|
|
@@ -330,12 +359,11 @@ class ServiceAugmentor:
|
|
|
330
359
|
return True
|
|
331
360
|
|
|
332
361
|
try:
|
|
333
|
-
# Copy entire tree
|
|
362
|
+
# Copy entire tree into the service package directory
|
|
334
363
|
for src_file in feature_dir.rglob("*"):
|
|
335
364
|
if src_file.is_file():
|
|
336
|
-
# Map to destination
|
|
337
365
|
rel_path = src_file.relative_to(feature_dir)
|
|
338
|
-
dest_file = self.
|
|
366
|
+
dest_file = self._pkg_dir / rel_path
|
|
339
367
|
|
|
340
368
|
# Create parent dirs
|
|
341
369
|
dest_file.parent.mkdir(parents=True, exist_ok=True)
|
|
@@ -417,8 +445,13 @@ class ServiceAugmentor:
|
|
|
417
445
|
# Step 2.5: Check if feature already applied (idempotency)
|
|
418
446
|
creates = self.manifest.get("creates", [])
|
|
419
447
|
if creates:
|
|
420
|
-
|
|
421
|
-
|
|
448
|
+
first_created_path = Path(creates[0])
|
|
449
|
+
# Feature manifests use src/app/ prefix — remap to flat pkg layout
|
|
450
|
+
try:
|
|
451
|
+
rel = first_created_path.relative_to("src/app")
|
|
452
|
+
full_path = self._pkg_dir / rel
|
|
453
|
+
except ValueError:
|
|
454
|
+
full_path = self.service_root / first_created_path
|
|
422
455
|
if full_path.exists():
|
|
423
456
|
print(f"✅ Feature '{feature_name}' already applied (detected {creates[0]})")
|
|
424
457
|
return True, []
|
|
@@ -19,8 +19,8 @@ def run_doctor(service_root: Path) -> DoctorReport:
|
|
|
19
19
|
errors: list[str] = []
|
|
20
20
|
warnings: list[str] = []
|
|
21
21
|
|
|
22
|
-
required_dirs = ["
|
|
23
|
-
required_files = ["requirements.txt"]
|
|
22
|
+
required_dirs = ["tests"]
|
|
23
|
+
required_files = ["requirements.txt", "pyproject.toml"]
|
|
24
24
|
|
|
25
25
|
compose_candidates = [root / "docker-compose.yml"]
|
|
26
26
|
if root.parent.name == "src" and (root.parent.parent / ".csrd-workspace").is_file():
|
|
@@ -37,10 +37,35 @@ def run_doctor(service_root: Path) -> DoctorReport:
|
|
|
37
37
|
if not any(path.is_file() for path in compose_candidates):
|
|
38
38
|
errors.append("Missing required file: docker-compose.yml")
|
|
39
39
|
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
40
|
+
# Find the service package dir — check src/ layout first, then flat layout
|
|
41
|
+
src_dir = root / "src"
|
|
42
|
+
pkg_dir = None
|
|
43
|
+
if src_dir.is_dir():
|
|
44
|
+
pkg_dir = next(
|
|
45
|
+
(
|
|
46
|
+
p
|
|
47
|
+
for p in src_dir.iterdir()
|
|
48
|
+
if p.is_dir() and not p.name.startswith(".") and (p / "__init__.py").is_file()
|
|
49
|
+
),
|
|
50
|
+
None,
|
|
51
|
+
)
|
|
52
|
+
if pkg_dir is None:
|
|
53
|
+
pkg_dir = next(
|
|
54
|
+
(
|
|
55
|
+
p
|
|
56
|
+
for p in root.iterdir()
|
|
57
|
+
if p.is_dir()
|
|
58
|
+
and not p.name.startswith(".")
|
|
59
|
+
and p.name not in {"tests", "src"}
|
|
60
|
+
and (p / "__init__.py").is_file()
|
|
61
|
+
),
|
|
62
|
+
None,
|
|
63
|
+
)
|
|
64
|
+
|
|
65
|
+
marker_files: dict[str, str] = {}
|
|
66
|
+
if pkg_dir is not None:
|
|
67
|
+
marker_files[f"{pkg_dir.name}/settings.py"] = "# [INSERT: fields]"
|
|
68
|
+
marker_files["tests/conftest.py"] = "# [INSERT: fixtures]"
|
|
44
69
|
for rel, marker in marker_files.items():
|
|
45
70
|
path = root / rel
|
|
46
71
|
if path.exists():
|
|
@@ -67,7 +67,7 @@ def generate_service(
|
|
|
67
67
|
"""Generate a service from the bundled cookiecutter-service template."""
|
|
68
68
|
output_root = output_dir.resolve()
|
|
69
69
|
output_root.mkdir(parents=True, exist_ok=True)
|
|
70
|
-
target = output_root / name
|
|
70
|
+
target = output_root / name.replace("-", "_")
|
|
71
71
|
|
|
72
72
|
if target.exists() and any(target.iterdir()) and not overwrite_if_exists:
|
|
73
73
|
raise FileExistsError(
|
|
@@ -31,32 +31,32 @@ def _prune_disabled_files() -> None:
|
|
|
31
31
|
path.rmdir()
|
|
32
32
|
|
|
33
33
|
if not HAS_DATABASE:
|
|
34
|
-
_unlink(f"{PKG}/dependencies/item_repository.py")
|
|
35
|
-
_unlink(f"{PKG}/repositories/item_repository.py")
|
|
34
|
+
_unlink(f"src/{PKG}/dependencies/item_repository.py")
|
|
35
|
+
_unlink(f"src/{PKG}/repositories/item_repository.py")
|
|
36
36
|
_unlink("tests/unit/test_database_settings.py")
|
|
37
37
|
|
|
38
38
|
if not HAS_DELEGATES:
|
|
39
|
-
_unlink(f"{PKG}/dependencies/delegates.py")
|
|
40
|
-
_unlink(f"{PKG}/delegates/upstreams.py")
|
|
39
|
+
_unlink(f"src/{PKG}/dependencies/delegates.py")
|
|
40
|
+
_unlink(f"src/{PKG}/delegates/upstreams.py")
|
|
41
41
|
_unlink("tests/unit/test_delegates.py")
|
|
42
42
|
|
|
43
43
|
if not HAS_WORKERS:
|
|
44
|
-
_unlink(f"{PKG}/dependencies/worker_broker.py")
|
|
45
|
-
_unlink(f"{PKG}/workers/tasks.py")
|
|
44
|
+
_unlink(f"src/{PKG}/dependencies/worker_broker.py")
|
|
45
|
+
_unlink(f"src/{PKG}/workers/tasks.py")
|
|
46
46
|
_unlink("tests/unit/test_worker_broker.py")
|
|
47
47
|
|
|
48
48
|
if not HAS_CACHING:
|
|
49
|
-
_unlink(f"{PKG}/cache.py")
|
|
49
|
+
_unlink(f"src/{PKG}/cache.py")
|
|
50
50
|
_unlink("tests/unit/test_cache.py")
|
|
51
51
|
|
|
52
52
|
if not HAS_STRUCTURED_LOGGING:
|
|
53
|
-
_unlink(f"{PKG}/middleware/logging.py")
|
|
53
|
+
_unlink(f"src/{PKG}/middleware/logging.py")
|
|
54
54
|
_unlink("tests/acceptance/test_logging_middleware.py")
|
|
55
55
|
|
|
56
|
-
_rmdir_if_empty(f"{PKG}/repositories")
|
|
57
|
-
_rmdir_if_empty(f"{PKG}/delegates")
|
|
58
|
-
_rmdir_if_empty(f"{PKG}/workers")
|
|
59
|
-
_rmdir_if_empty(f"{PKG}/middleware")
|
|
56
|
+
_rmdir_if_empty(f"src/{PKG}/repositories")
|
|
57
|
+
_rmdir_if_empty(f"src/{PKG}/delegates")
|
|
58
|
+
_rmdir_if_empty(f"src/{PKG}/workers")
|
|
59
|
+
_rmdir_if_empty(f"src/{PKG}/middleware")
|
|
60
60
|
_rmdir_if_empty("tests/unit")
|
|
61
61
|
_rmdir_if_empty("tests/acceptance")
|
|
62
62
|
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
.ruff_cache/
|
|
@@ -8,6 +8,6 @@ RUN pip install --no-cache-dir -r /app/requirements.txt
|
|
|
8
8
|
COPY src /app/src
|
|
9
9
|
|
|
10
10
|
ENV PYTHONPATH=/app/src
|
|
11
|
-
ENV PORT=
|
|
11
|
+
ENV PORT={{ cookiecutter.port }}
|
|
12
12
|
|
|
13
|
-
CMD uvicorn
|
|
13
|
+
CMD uvicorn {{cookiecutter.__service_name_snake}}:build_app --factory --host 0.0.0.0 --port ${PORT}
|
|
@@ -24,8 +24,7 @@
|
|
|
24
24
|
## Run locally
|
|
25
25
|
|
|
26
26
|
```bash
|
|
27
|
-
|
|
28
|
-
PYTHONPATH=src uvicorn app:build_app --factory --reload --port {{ cookiecutter.port }}
|
|
27
|
+
PYTHONPATH=src uvicorn {{cookiecutter.__service_name_snake}}:build_app --factory --reload --port {{ cookiecutter.port }}
|
|
29
28
|
```
|
|
30
29
|
|
|
31
30
|
## Run with compose
|
|
@@ -1,44 +0,0 @@
|
|
|
1
|
-
APP_NAME={{ cookiecutter.service_name }}
|
|
2
|
-
PORT={{ cookiecutter.port }}
|
|
3
|
-
APP_ENV=development
|
|
4
|
-
ENFORCE_STRONG_DB_CREDENTIALS=false
|
|
5
|
-
INCLUDE_ACTUATOR_ENDPOINTS=false
|
|
6
|
-
|
|
7
|
-
{%- if cookiecutter.has_database %}
|
|
8
|
-
DB_KIND={{ cookiecutter.database }}
|
|
9
|
-
{%- if cookiecutter.database == "sqlite" %}
|
|
10
|
-
DB_PATH=./service.db
|
|
11
|
-
{%- elif cookiecutter.database == "maria" %}
|
|
12
|
-
DB_HOST=localhost
|
|
13
|
-
DB_PORT=3306
|
|
14
|
-
DB_USER=service_user
|
|
15
|
-
DB_PASSWORD=change_me
|
|
16
|
-
DB_NAME=service_db
|
|
17
|
-
MYSQL_ROOT_PASSWORD=change_me_root
|
|
18
|
-
{%- else %}
|
|
19
|
-
DB_HOST=localhost
|
|
20
|
-
DB_PORT=5432
|
|
21
|
-
DB_USER=service_user
|
|
22
|
-
DB_PASSWORD=change_me
|
|
23
|
-
DB_NAME=service_db
|
|
24
|
-
{%- endif %}
|
|
25
|
-
{%- endif %}
|
|
26
|
-
|
|
27
|
-
{%- if cookiecutter.has_delegates %}
|
|
28
|
-
# Comma-separated upstream service URLs.
|
|
29
|
-
UPSTREAMS={{ cookiecutter.upstreams }}
|
|
30
|
-
{%- endif %}
|
|
31
|
-
|
|
32
|
-
{%- if cookiecutter.has_workers %}
|
|
33
|
-
{%- if cookiecutter.broker == "redis" %}
|
|
34
|
-
WORKER_BROKER_URL=redis://localhost:6380/1
|
|
35
|
-
{%- else %}
|
|
36
|
-
RABBITMQ_USER=service_rabbit
|
|
37
|
-
RABBITMQ_PASSWORD=change_me
|
|
38
|
-
WORKER_BROKER_URL=amqp://service_rabbit:change_me@localhost:5672/
|
|
39
|
-
{%- endif %}
|
|
40
|
-
{%- endif %}
|
|
41
|
-
|
|
42
|
-
{%- if cookiecutter.has_caching %}
|
|
43
|
-
CACHE_URL=redis://localhost:6379/0
|
|
44
|
-
{%- endif %}
|
|
@@ -1,64 +0,0 @@
|
|
|
1
|
-
# Python
|
|
2
|
-
__pycache__/
|
|
3
|
-
*.py[cod]
|
|
4
|
-
*$py.class
|
|
5
|
-
*.so
|
|
6
|
-
.Python
|
|
7
|
-
build/
|
|
8
|
-
develop-eggs/
|
|
9
|
-
dist/
|
|
10
|
-
downloads/
|
|
11
|
-
eggs/
|
|
12
|
-
.eggs/
|
|
13
|
-
lib/
|
|
14
|
-
lib64/
|
|
15
|
-
parts/
|
|
16
|
-
sdist/
|
|
17
|
-
var/
|
|
18
|
-
wheels/
|
|
19
|
-
pip-wheel-metadata/
|
|
20
|
-
share/python-wheels/
|
|
21
|
-
*.egg-info/
|
|
22
|
-
.installed.cfg
|
|
23
|
-
*.egg
|
|
24
|
-
MANIFEST
|
|
25
|
-
|
|
26
|
-
# Virtual environments
|
|
27
|
-
.venv
|
|
28
|
-
venv/
|
|
29
|
-
ENV/
|
|
30
|
-
env/
|
|
31
|
-
|
|
32
|
-
# IDEs
|
|
33
|
-
.vscode/
|
|
34
|
-
.idea/
|
|
35
|
-
*.swp
|
|
36
|
-
*.swo
|
|
37
|
-
*~
|
|
38
|
-
.DS_Store
|
|
39
|
-
|
|
40
|
-
# Testing
|
|
41
|
-
.pytest_cache/
|
|
42
|
-
.coverage
|
|
43
|
-
htmlcov/
|
|
44
|
-
.tox/
|
|
45
|
-
.hypothesis/
|
|
46
|
-
|
|
47
|
-
# Type checking
|
|
48
|
-
.mypy_cache/
|
|
49
|
-
.dmypy.json
|
|
50
|
-
dmypy.json
|
|
51
|
-
|
|
52
|
-
# Development
|
|
53
|
-
*.log
|
|
54
|
-
.env
|
|
55
|
-
.env.local
|
|
56
|
-
.env.*.local
|
|
57
|
-
|
|
58
|
-
# Docker
|
|
59
|
-
.dockerignore
|
|
60
|
-
Dockerfile
|
|
61
|
-
|
|
62
|
-
# Services
|
|
63
|
-
.csrd-workspace
|
|
64
|
-
.csrd/
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{csrd_utils-0.3.36 → csrd_utils-0.3.38}/src/csrd_utils/features/caching/conftest.fragment.py
RENAMED
|
File without changes
|
{csrd_utils-0.3.36 → csrd_utils-0.3.38}/src/csrd_utils/features/caching/docker-compose.fragment.yaml
RENAMED
|
File without changes
|
|
File without changes
|
{csrd_utils-0.3.36 → csrd_utils-0.3.38}/src/csrd_utils/features/caching/requirements.fragment
RENAMED
|
File without changes
|
{csrd_utils-0.3.36 → csrd_utils-0.3.38}/src/csrd_utils/features/caching/settings.fragment.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{csrd_utils-0.3.36 → csrd_utils-0.3.38}/src/csrd_utils/features/logging/conftest.fragment.py
RENAMED
|
File without changes
|
|
File without changes
|
{csrd_utils-0.3.36 → csrd_utils-0.3.38}/src/csrd_utils/features/logging/requirements.fragment
RENAMED
|
File without changes
|
{csrd_utils-0.3.36 → csrd_utils-0.3.38}/src/csrd_utils/features/logging/settings.fragment.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{csrd_utils-0.3.36 → csrd_utils-0.3.38}/src/csrd_utils/features/metrics/conftest.fragment.py
RENAMED
|
File without changes
|
{csrd_utils-0.3.36 → csrd_utils-0.3.38}/src/csrd_utils/features/metrics/docker-compose.fragment.yaml
RENAMED
|
File without changes
|
|
File without changes
|
{csrd_utils-0.3.36 → csrd_utils-0.3.38}/src/csrd_utils/features/metrics/requirements.fragment
RENAMED
|
File without changes
|
{csrd_utils-0.3.36 → csrd_utils-0.3.38}/src/csrd_utils/features/metrics/settings.fragment.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{csrd_utils-0.3.36 → csrd_utils-0.3.38}/src/csrd_utils/features/tracing/conftest.fragment.py
RENAMED
|
File without changes
|
|
File without changes
|
{csrd_utils-0.3.36 → csrd_utils-0.3.38}/src/csrd_utils/features/tracing/requirements.fragment
RENAMED
|
File without changes
|
{csrd_utils-0.3.36 → csrd_utils-0.3.38}/src/csrd_utils/features/tracing/settings.fragment.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{csrd_utils-0.3.36 → csrd_utils-0.3.38}/src/csrd_utils/features/workers/conftest.fragment.py
RENAMED
|
File without changes
|
{csrd_utils-0.3.36 → csrd_utils-0.3.38}/src/csrd_utils/features/workers/docker-compose.fragment.yaml
RENAMED
|
File without changes
|
|
File without changes
|
{csrd_utils-0.3.36 → csrd_utils-0.3.38}/src/csrd_utils/features/workers/requirements.fragment
RENAMED
|
File without changes
|
{csrd_utils-0.3.36 → csrd_utils-0.3.38}/src/csrd_utils/features/workers/settings.fragment.py
RENAMED
|
File without changes
|
{csrd_utils-0.3.36 → csrd_utils-0.3.38}/src/csrd_utils/features/workers/src/app/workers/__init__.py
RENAMED
|
File without changes
|
|
File without changes
|
{csrd_utils-0.3.36 → csrd_utils-0.3.38}/src/csrd_utils/features/workers/src/app/workers/tasks.py
RENAMED
|
File without changes
|
{csrd_utils-0.3.36 → csrd_utils-0.3.38}/src/csrd_utils/features/workers/tests/test_workers.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{csrd_utils-0.3.36 → csrd_utils-0.3.38}/src/csrd_utils/templates/cookiecutter-service/README.md
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|