csrd-utils 0.3.16__tar.gz → 0.3.18__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.16 → csrd_utils-0.3.18}/PKG-INFO +9 -9
- {csrd_utils-0.3.16 → csrd_utils-0.3.18}/README.md +8 -8
- {csrd_utils-0.3.16 → csrd_utils-0.3.18}/pyproject.toml +1 -1
- {csrd_utils-0.3.16 → csrd_utils-0.3.18}/src/csrd_utils/__main__.py +56 -13
- {csrd_utils-0.3.16 → csrd_utils-0.3.18}/src/csrd_utils/cluster.py +11 -2
- csrd_utils-0.3.18/src/csrd_utils/templates/cookiecutter-service/{{cookiecutter.service_name}}/.gitignore +64 -0
- csrd_utils-0.3.18/src/csrd_utils/templates/cookiecutter-service/{{cookiecutter.service_name}}/pyproject.toml +61 -0
- {csrd_utils-0.3.16 → csrd_utils-0.3.18}/src/csrd_utils/templates/cookiecutter-service/{{cookiecutter.service_name}}/src/app/views/unversioned/items_view.py +4 -4
- csrd_utils-0.3.18/src/csrd_utils/templates/cookiecutter-service/{{cookiecutter.service_name}}/tests/__init__.py +0 -0
- {csrd_utils-0.3.16 → csrd_utils-0.3.18}/.gitignore +0 -0
- {csrd_utils-0.3.16 → csrd_utils-0.3.18}/src/csrd_utils/__init__.py +0 -0
- {csrd_utils-0.3.16 → csrd_utils-0.3.18}/src/csrd_utils/audit.py +0 -0
- {csrd_utils-0.3.16 → csrd_utils-0.3.18}/src/csrd_utils/augmentor.py +0 -0
- {csrd_utils-0.3.16 → csrd_utils-0.3.18}/src/csrd_utils/doctor.py +0 -0
- {csrd_utils-0.3.16 → csrd_utils-0.3.18}/src/csrd_utils/features/caching/conftest.fragment.py +0 -0
- {csrd_utils-0.3.16 → csrd_utils-0.3.18}/src/csrd_utils/features/caching/docker-compose.fragment.yaml +0 -0
- {csrd_utils-0.3.16 → csrd_utils-0.3.18}/src/csrd_utils/features/caching/manifest.yaml +0 -0
- {csrd_utils-0.3.16 → csrd_utils-0.3.18}/src/csrd_utils/features/caching/requirements.fragment +0 -0
- {csrd_utils-0.3.16 → csrd_utils-0.3.18}/src/csrd_utils/features/caching/settings.fragment.py +0 -0
- {csrd_utils-0.3.16 → csrd_utils-0.3.18}/src/csrd_utils/features/caching/src/app/cache.py +0 -0
- {csrd_utils-0.3.16 → csrd_utils-0.3.18}/src/csrd_utils/features/caching/tests/test_cache.py +0 -0
- {csrd_utils-0.3.16 → csrd_utils-0.3.18}/src/csrd_utils/features/logging/conftest.fragment.py +0 -0
- {csrd_utils-0.3.16 → csrd_utils-0.3.18}/src/csrd_utils/features/logging/manifest.yaml +0 -0
- {csrd_utils-0.3.16 → csrd_utils-0.3.18}/src/csrd_utils/features/logging/requirements.fragment +0 -0
- {csrd_utils-0.3.16 → csrd_utils-0.3.18}/src/csrd_utils/features/logging/settings.fragment.py +0 -0
- {csrd_utils-0.3.16 → csrd_utils-0.3.18}/src/csrd_utils/features/logging/src/app/middleware/logging.py +0 -0
- {csrd_utils-0.3.16 → csrd_utils-0.3.18}/src/csrd_utils/features/logging/tests/test_logging_middleware.py +0 -0
- {csrd_utils-0.3.16 → csrd_utils-0.3.18}/src/csrd_utils/features/metrics/conftest.fragment.py +0 -0
- {csrd_utils-0.3.16 → csrd_utils-0.3.18}/src/csrd_utils/features/metrics/docker-compose.fragment.yaml +0 -0
- {csrd_utils-0.3.16 → csrd_utils-0.3.18}/src/csrd_utils/features/metrics/manifest.yaml +0 -0
- {csrd_utils-0.3.16 → csrd_utils-0.3.18}/src/csrd_utils/features/metrics/requirements.fragment +0 -0
- {csrd_utils-0.3.16 → csrd_utils-0.3.18}/src/csrd_utils/features/metrics/settings.fragment.py +0 -0
- {csrd_utils-0.3.16 → csrd_utils-0.3.18}/src/csrd_utils/features/metrics/src/app/middleware/metrics.py +0 -0
- {csrd_utils-0.3.16 → csrd_utils-0.3.18}/src/csrd_utils/features/metrics/tests/test_metrics_middleware.py +0 -0
- {csrd_utils-0.3.16 → csrd_utils-0.3.18}/src/csrd_utils/features/tracing/conftest.fragment.py +0 -0
- {csrd_utils-0.3.16 → csrd_utils-0.3.18}/src/csrd_utils/features/tracing/manifest.yaml +0 -0
- {csrd_utils-0.3.16 → csrd_utils-0.3.18}/src/csrd_utils/features/tracing/requirements.fragment +0 -0
- {csrd_utils-0.3.16 → csrd_utils-0.3.18}/src/csrd_utils/features/tracing/settings.fragment.py +0 -0
- {csrd_utils-0.3.16 → csrd_utils-0.3.18}/src/csrd_utils/features/tracing/src/app/middleware/tracing.py +0 -0
- {csrd_utils-0.3.16 → csrd_utils-0.3.18}/src/csrd_utils/features/tracing/tests/test_tracing_middleware.py +0 -0
- {csrd_utils-0.3.16 → csrd_utils-0.3.18}/src/csrd_utils/features/workers/conftest.fragment.py +0 -0
- {csrd_utils-0.3.16 → csrd_utils-0.3.18}/src/csrd_utils/features/workers/docker-compose.fragment.yaml +0 -0
- {csrd_utils-0.3.16 → csrd_utils-0.3.18}/src/csrd_utils/features/workers/manifest.yaml +0 -0
- {csrd_utils-0.3.16 → csrd_utils-0.3.18}/src/csrd_utils/features/workers/requirements.fragment +0 -0
- {csrd_utils-0.3.16 → csrd_utils-0.3.18}/src/csrd_utils/features/workers/settings.fragment.py +0 -0
- {csrd_utils-0.3.16 → csrd_utils-0.3.18}/src/csrd_utils/features/workers/src/app/workers/__init__.py +0 -0
- {csrd_utils-0.3.16 → csrd_utils-0.3.18}/src/csrd_utils/features/workers/src/app/workers/celery_app.py +0 -0
- {csrd_utils-0.3.16 → csrd_utils-0.3.18}/src/csrd_utils/features/workers/src/app/workers/tasks.py +0 -0
- {csrd_utils-0.3.16 → csrd_utils-0.3.18}/src/csrd_utils/features/workers/tests/test_workers.py +0 -0
- {csrd_utils-0.3.16 → csrd_utils-0.3.18}/src/csrd_utils/fragments.py +0 -0
- {csrd_utils-0.3.16 → csrd_utils-0.3.18}/src/csrd_utils/generator.py +0 -0
- {csrd_utils-0.3.16 → csrd_utils-0.3.18}/src/csrd_utils/resources.py +0 -0
- {csrd_utils-0.3.16 → csrd_utils-0.3.18}/src/csrd_utils/templates/cookiecutter-service/README.md +0 -0
- {csrd_utils-0.3.16 → csrd_utils-0.3.18}/src/csrd_utils/templates/cookiecutter-service/cookiecutter.json +0 -0
- {csrd_utils-0.3.16 → csrd_utils-0.3.18}/src/csrd_utils/templates/cookiecutter-service/hooks/post_gen_project.py +0 -0
- {csrd_utils-0.3.16 → csrd_utils-0.3.18}/src/csrd_utils/templates/cookiecutter-service/{{cookiecutter.service_name}}/.dockerignore +0 -0
- {csrd_utils-0.3.16 → csrd_utils-0.3.18}/src/csrd_utils/templates/cookiecutter-service/{{cookiecutter.service_name}}/.env.example +0 -0
- {csrd_utils-0.3.16 → csrd_utils-0.3.18}/src/csrd_utils/templates/cookiecutter-service/{{cookiecutter.service_name}}/Dockerfile +0 -0
- {csrd_utils-0.3.16 → csrd_utils-0.3.18}/src/csrd_utils/templates/cookiecutter-service/{{cookiecutter.service_name}}/README.md +0 -0
- {csrd_utils-0.3.16 → csrd_utils-0.3.18}/src/csrd_utils/templates/cookiecutter-service/{{cookiecutter.service_name}}/docker-compose.yml +0 -0
- {csrd_utils-0.3.16 → csrd_utils-0.3.18}/src/csrd_utils/templates/cookiecutter-service/{{cookiecutter.service_name}}/requirements.txt +0 -0
- {csrd_utils-0.3.16 → csrd_utils-0.3.18}/src/csrd_utils/templates/cookiecutter-service/{{cookiecutter.service_name}}/src/app/__init__.py +0 -0
- {csrd_utils-0.3.16 → csrd_utils-0.3.18}/src/csrd_utils/templates/cookiecutter-service/{{cookiecutter.service_name}}/src/app/cache.py +0 -0
- {csrd_utils-0.3.16 → csrd_utils-0.3.18}/src/csrd_utils/templates/cookiecutter-service/{{cookiecutter.service_name}}/src/app/delegates/__init__.py +0 -0
- {csrd_utils-0.3.16 → csrd_utils-0.3.18}/src/csrd_utils/templates/cookiecutter-service/{{cookiecutter.service_name}}/src/app/delegates/upstreams.py +0 -0
- {csrd_utils-0.3.16 → csrd_utils-0.3.18}/src/csrd_utils/templates/cookiecutter-service/{{cookiecutter.service_name}}/src/app/dependencies/__init__.py +0 -0
- {csrd_utils-0.3.16 → csrd_utils-0.3.18}/src/csrd_utils/templates/cookiecutter-service/{{cookiecutter.service_name}}/src/app/dependencies/delegates.py +0 -0
- {csrd_utils-0.3.16 → csrd_utils-0.3.18}/src/csrd_utils/templates/cookiecutter-service/{{cookiecutter.service_name}}/src/app/dependencies/item_repository.py +0 -0
- {csrd_utils-0.3.16 → csrd_utils-0.3.18}/src/csrd_utils/templates/cookiecutter-service/{{cookiecutter.service_name}}/src/app/dependencies/worker_broker.py +0 -0
- {csrd_utils-0.3.16 → csrd_utils-0.3.18}/src/csrd_utils/templates/cookiecutter-service/{{cookiecutter.service_name}}/src/app/middleware/__init__.py +0 -0
- {csrd_utils-0.3.16 → csrd_utils-0.3.18}/src/csrd_utils/templates/cookiecutter-service/{{cookiecutter.service_name}}/src/app/middleware/logging.py +0 -0
- /csrd_utils-0.3.16/src/csrd_utils/templates/cookiecutter-service/{{cookiecutter.service_name}}/tests/__init__.py → /csrd_utils-0.3.18/src/csrd_utils/templates/cookiecutter-service/{{cookiecutter.service_name}}/src/app/py.typed +0 -0
- {csrd_utils-0.3.16 → csrd_utils-0.3.18}/src/csrd_utils/templates/cookiecutter-service/{{cookiecutter.service_name}}/src/app/repositories/__init__.py +0 -0
- {csrd_utils-0.3.16 → csrd_utils-0.3.18}/src/csrd_utils/templates/cookiecutter-service/{{cookiecutter.service_name}}/src/app/repositories/item_repository.py +0 -0
- {csrd_utils-0.3.16 → csrd_utils-0.3.18}/src/csrd_utils/templates/cookiecutter-service/{{cookiecutter.service_name}}/src/app/settings.py +0 -0
- {csrd_utils-0.3.16 → csrd_utils-0.3.18}/src/csrd_utils/templates/cookiecutter-service/{{cookiecutter.service_name}}/src/app/views/__init__.py +0 -0
- {csrd_utils-0.3.16 → csrd_utils-0.3.18}/src/csrd_utils/templates/cookiecutter-service/{{cookiecutter.service_name}}/src/app/views/unversioned/__init__.py +0 -0
- {csrd_utils-0.3.16 → csrd_utils-0.3.18}/src/csrd_utils/templates/cookiecutter-service/{{cookiecutter.service_name}}/src/app/views/unversioned/health_view.py +0 -0
- {csrd_utils-0.3.16 → csrd_utils-0.3.18}/src/csrd_utils/templates/cookiecutter-service/{{cookiecutter.service_name}}/src/app/workers/__init__.py +0 -0
- {csrd_utils-0.3.16 → csrd_utils-0.3.18}/src/csrd_utils/templates/cookiecutter-service/{{cookiecutter.service_name}}/src/app/workers/tasks.py +0 -0
- {csrd_utils-0.3.16 → csrd_utils-0.3.18}/src/csrd_utils/templates/cookiecutter-service/{{cookiecutter.service_name}}/tests/acceptance/test_logging_middleware.py +0 -0
- {csrd_utils-0.3.16 → csrd_utils-0.3.18}/src/csrd_utils/templates/cookiecutter-service/{{cookiecutter.service_name}}/tests/conftest.py +0 -0
- {csrd_utils-0.3.16 → csrd_utils-0.3.18}/src/csrd_utils/templates/cookiecutter-service/{{cookiecutter.service_name}}/tests/test_items.py +0 -0
- {csrd_utils-0.3.16 → csrd_utils-0.3.18}/src/csrd_utils/templates/cookiecutter-service/{{cookiecutter.service_name}}/tests/unit/test_cache.py +0 -0
- {csrd_utils-0.3.16 → csrd_utils-0.3.18}/src/csrd_utils/templates/cookiecutter-service/{{cookiecutter.service_name}}/tests/unit/test_database_settings.py +0 -0
- {csrd_utils-0.3.16 → csrd_utils-0.3.18}/src/csrd_utils/templates/cookiecutter-service/{{cookiecutter.service_name}}/tests/unit/test_delegates.py +0 -0
- {csrd_utils-0.3.16 → csrd_utils-0.3.18}/src/csrd_utils/templates/cookiecutter-service/{{cookiecutter.service_name}}/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.18
|
|
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
|
|
@@ -70,19 +70,19 @@ csrd new workspace --name local-dev --output . --generate none
|
|
|
70
70
|
csrd new service --name inventory --output ./local-dev --database postgres --workers
|
|
71
71
|
|
|
72
72
|
# 1) Verify service compatibility
|
|
73
|
-
csrd doctor --service ./local-dev/inventory
|
|
73
|
+
csrd doctor --service ./local-dev/src/inventory
|
|
74
74
|
|
|
75
75
|
# 1b) Audit for weak/insecure defaults
|
|
76
|
-
csrd audit --service ./local-dev/inventory
|
|
76
|
+
csrd audit --service ./local-dev/src/inventory
|
|
77
77
|
|
|
78
78
|
# 2) Inspect available bundled features
|
|
79
79
|
csrd feature list
|
|
80
80
|
|
|
81
81
|
# 3) Dry-run a feature
|
|
82
|
-
csrd feature plan workers --service ./local-dev/inventory
|
|
82
|
+
csrd feature plan workers --service ./local-dev/src/inventory
|
|
83
83
|
|
|
84
84
|
# 4) Apply feature files/merges
|
|
85
|
-
csrd feature add workers --service ./local-dev/inventory
|
|
85
|
+
csrd feature add workers --service ./local-dev/src/inventory
|
|
86
86
|
|
|
87
87
|
# 5) Add a second service — workspace auto-upgrades to cluster
|
|
88
88
|
csrd new service --name pricing --output ./local-dev --database postgres
|
|
@@ -135,8 +135,8 @@ Then run a clean smoke flow:
|
|
|
135
135
|
mkdir -p /tmp/csrd-smoke
|
|
136
136
|
csrd new workspace --name ws --output /tmp/csrd-smoke --generate none
|
|
137
137
|
csrd new service --name demo-svc --output /tmp/csrd-smoke/ws --no-interactive
|
|
138
|
-
csrd doctor --service /tmp/csrd-smoke/ws/demo-svc
|
|
139
|
-
csrd audit --service /tmp/csrd-smoke/ws/demo-svc
|
|
140
|
-
csrd feature plan workers --service /tmp/csrd-smoke/ws/demo-svc
|
|
141
|
-
csrd feature add workers --service /tmp/csrd-smoke/ws/demo-svc
|
|
138
|
+
csrd doctor --service /tmp/csrd-smoke/ws/src/demo-svc
|
|
139
|
+
csrd audit --service /tmp/csrd-smoke/ws/src/demo-svc
|
|
140
|
+
csrd feature plan workers --service /tmp/csrd-smoke/ws/src/demo-svc
|
|
141
|
+
csrd feature add workers --service /tmp/csrd-smoke/ws/src/demo-svc
|
|
142
142
|
```
|
|
@@ -57,19 +57,19 @@ csrd new workspace --name local-dev --output . --generate none
|
|
|
57
57
|
csrd new service --name inventory --output ./local-dev --database postgres --workers
|
|
58
58
|
|
|
59
59
|
# 1) Verify service compatibility
|
|
60
|
-
csrd doctor --service ./local-dev/inventory
|
|
60
|
+
csrd doctor --service ./local-dev/src/inventory
|
|
61
61
|
|
|
62
62
|
# 1b) Audit for weak/insecure defaults
|
|
63
|
-
csrd audit --service ./local-dev/inventory
|
|
63
|
+
csrd audit --service ./local-dev/src/inventory
|
|
64
64
|
|
|
65
65
|
# 2) Inspect available bundled features
|
|
66
66
|
csrd feature list
|
|
67
67
|
|
|
68
68
|
# 3) Dry-run a feature
|
|
69
|
-
csrd feature plan workers --service ./local-dev/inventory
|
|
69
|
+
csrd feature plan workers --service ./local-dev/src/inventory
|
|
70
70
|
|
|
71
71
|
# 4) Apply feature files/merges
|
|
72
|
-
csrd feature add workers --service ./local-dev/inventory
|
|
72
|
+
csrd feature add workers --service ./local-dev/src/inventory
|
|
73
73
|
|
|
74
74
|
# 5) Add a second service — workspace auto-upgrades to cluster
|
|
75
75
|
csrd new service --name pricing --output ./local-dev --database postgres
|
|
@@ -122,8 +122,8 @@ Then run a clean smoke flow:
|
|
|
122
122
|
mkdir -p /tmp/csrd-smoke
|
|
123
123
|
csrd new workspace --name ws --output /tmp/csrd-smoke --generate none
|
|
124
124
|
csrd new service --name demo-svc --output /tmp/csrd-smoke/ws --no-interactive
|
|
125
|
-
csrd doctor --service /tmp/csrd-smoke/ws/demo-svc
|
|
126
|
-
csrd audit --service /tmp/csrd-smoke/ws/demo-svc
|
|
127
|
-
csrd feature plan workers --service /tmp/csrd-smoke/ws/demo-svc
|
|
128
|
-
csrd feature add workers --service /tmp/csrd-smoke/ws/demo-svc
|
|
125
|
+
csrd doctor --service /tmp/csrd-smoke/ws/src/demo-svc
|
|
126
|
+
csrd audit --service /tmp/csrd-smoke/ws/src/demo-svc
|
|
127
|
+
csrd feature plan workers --service /tmp/csrd-smoke/ws/src/demo-svc
|
|
128
|
+
csrd feature add workers --service /tmp/csrd-smoke/ws/src/demo-svc
|
|
129
129
|
```
|
|
@@ -61,6 +61,10 @@ WORKSPACE_MARKER = ".csrd-workspace"
|
|
|
61
61
|
WORKSPACE_CLUSTER_SPEC = Path(".csrd") / "cluster.yaml"
|
|
62
62
|
|
|
63
63
|
|
|
64
|
+
def _workspace_services_dir(workspace_root: Path) -> Path:
|
|
65
|
+
return workspace_root / "src"
|
|
66
|
+
|
|
67
|
+
|
|
64
68
|
def _prompt_text(label: str, default: str) -> str:
|
|
65
69
|
value = input(f"{label} [{default}]: ").strip()
|
|
66
70
|
return value or default
|
|
@@ -82,7 +86,15 @@ def _prompt_choice(label: str, choices: list[str], default: str) -> str:
|
|
|
82
86
|
return default
|
|
83
87
|
if value in choices:
|
|
84
88
|
return value
|
|
85
|
-
|
|
89
|
+
# Try prefix matching
|
|
90
|
+
matching = [choice for choice in choices if choice.startswith(value)]
|
|
91
|
+
if len(matching) == 1:
|
|
92
|
+
return matching[0]
|
|
93
|
+
if len(matching) > 1:
|
|
94
|
+
matching_str = ", ".join(matching)
|
|
95
|
+
print(f"ERROR: Ambiguous choice '{value}'. Matches: {matching_str}")
|
|
96
|
+
else:
|
|
97
|
+
print(f"ERROR: Invalid choice '{value}'. Expected one of: {rendered}")
|
|
86
98
|
|
|
87
99
|
|
|
88
100
|
def _prompt_int(label: str, default: int) -> int:
|
|
@@ -478,7 +490,11 @@ def _append_service_to_workspace_cluster(
|
|
|
478
490
|
|
|
479
491
|
def _workspace_service_roots(workspace_root: Path) -> list[Path]:
|
|
480
492
|
roots: list[Path] = []
|
|
481
|
-
|
|
493
|
+
services_root = _workspace_services_dir(workspace_root)
|
|
494
|
+
if not services_root.is_dir():
|
|
495
|
+
return roots
|
|
496
|
+
|
|
497
|
+
for child in services_root.iterdir():
|
|
482
498
|
if not child.is_dir() or child.name.startswith("."):
|
|
483
499
|
continue
|
|
484
500
|
if (
|
|
@@ -491,6 +507,24 @@ def _workspace_service_roots(workspace_root: Path) -> list[Path]:
|
|
|
491
507
|
return sorted(roots)
|
|
492
508
|
|
|
493
509
|
|
|
510
|
+
def _resolve_feature_service_root(service_arg: Path) -> Path:
|
|
511
|
+
candidate = service_arg.resolve()
|
|
512
|
+
if not _is_workspace(candidate):
|
|
513
|
+
return candidate
|
|
514
|
+
|
|
515
|
+
service_roots = _workspace_service_roots(candidate)
|
|
516
|
+
if len(service_roots) == 1:
|
|
517
|
+
print(f"Detected workspace service target: {service_roots[0]}")
|
|
518
|
+
return service_roots[0]
|
|
519
|
+
if not service_roots:
|
|
520
|
+
raise ValueError(
|
|
521
|
+
"No services found under workspace src/. Pass --service with a service root path."
|
|
522
|
+
)
|
|
523
|
+
raise ValueError(
|
|
524
|
+
"Multiple services found under workspace src/. Pass --service with a specific service root path."
|
|
525
|
+
)
|
|
526
|
+
|
|
527
|
+
|
|
494
528
|
def _infer_port_from_service_root(service_root: Path) -> int:
|
|
495
529
|
try:
|
|
496
530
|
payload = yaml.safe_load((service_root / "docker-compose.yml").read_text(encoding="utf-8"))
|
|
@@ -555,12 +589,9 @@ def _maybe_update_workspace_cluster(
|
|
|
555
589
|
spec_path = _workspace_cluster_spec_path(workspace_root)
|
|
556
590
|
if spec_path.is_file():
|
|
557
591
|
updated_spec = _append_service_to_workspace_cluster(workspace_root, service_config)
|
|
558
|
-
return generate_cluster(updated_spec, workspace_root, force=True)
|
|
592
|
+
return generate_cluster(updated_spec, workspace_root, force=True, workspace_mode=True)
|
|
559
593
|
|
|
560
594
|
service_roots = _workspace_service_roots(workspace_root)
|
|
561
|
-
if len(service_roots) < 2:
|
|
562
|
-
return None
|
|
563
|
-
|
|
564
595
|
generated_name = generated_service.name
|
|
565
596
|
services: list[ServiceSpec] = [_service_config_to_cluster_service(service_config)]
|
|
566
597
|
for service_root in service_roots:
|
|
@@ -575,7 +606,7 @@ def _maybe_update_workspace_cluster(
|
|
|
575
606
|
}
|
|
576
607
|
normalized = normalize_cluster_spec(bootstrap_spec)
|
|
577
608
|
_save_workspace_cluster_spec(workspace_root, normalized)
|
|
578
|
-
return generate_cluster(normalized, workspace_root, force=True)
|
|
609
|
+
return generate_cluster(normalized, workspace_root, force=True, workspace_mode=True)
|
|
579
610
|
|
|
580
611
|
|
|
581
612
|
def _build_parser() -> argparse.ArgumentParser:
|
|
@@ -808,12 +839,13 @@ def main() -> int:
|
|
|
808
839
|
if args.command == "new" and args.new_command == "service":
|
|
809
840
|
try:
|
|
810
841
|
service_config = _collect_new_service_config(args)
|
|
811
|
-
|
|
842
|
+
workspace_root = _ensure_workspace_for_output(
|
|
812
843
|
service_config["output_dir"], allow_prompt=_service_is_interactive(args)
|
|
813
844
|
)
|
|
845
|
+
service_config["output_dir"] = _workspace_services_dir(workspace_root)
|
|
814
846
|
generated = generate_service(**service_config)
|
|
815
847
|
generated_cluster = _maybe_update_workspace_cluster(
|
|
816
|
-
|
|
848
|
+
workspace_root, service_config, generated
|
|
817
849
|
)
|
|
818
850
|
if generated_cluster is not None:
|
|
819
851
|
print(f"Refreshed cluster scaffold at: {generated_cluster}")
|
|
@@ -856,10 +888,15 @@ def main() -> int:
|
|
|
856
888
|
generate_mode = workspace_config["generate"]
|
|
857
889
|
if generate_mode == "service":
|
|
858
890
|
service_config = _collect_new_service_config(
|
|
859
|
-
_default_service_namespace(workspace_root)
|
|
891
|
+
_default_service_namespace(_workspace_services_dir(workspace_root))
|
|
860
892
|
)
|
|
861
|
-
service_config["output_dir"] = workspace_root
|
|
893
|
+
service_config["output_dir"] = _workspace_services_dir(workspace_root)
|
|
862
894
|
generated_service = generate_service(**service_config)
|
|
895
|
+
generated_cluster = _maybe_update_workspace_cluster(
|
|
896
|
+
workspace_root, service_config, generated_service
|
|
897
|
+
)
|
|
898
|
+
if generated_cluster is not None:
|
|
899
|
+
print(f"Refreshed cluster scaffold at: {generated_cluster}")
|
|
863
900
|
print(f"Generated service at: {generated_service}")
|
|
864
901
|
elif generate_mode == "cluster":
|
|
865
902
|
cluster_config = _collect_new_cluster_config(
|
|
@@ -893,8 +930,14 @@ def main() -> int:
|
|
|
893
930
|
if args.feature_command == "list":
|
|
894
931
|
return _list_features()
|
|
895
932
|
|
|
933
|
+
try:
|
|
934
|
+
resolved_service = _resolve_feature_service_root(args.service)
|
|
935
|
+
except ValueError as exc:
|
|
936
|
+
print(f"ERROR: {exc}")
|
|
937
|
+
return 1
|
|
938
|
+
|
|
896
939
|
with features_path() as feature_lib:
|
|
897
|
-
augmentor = ServiceAugmentor(
|
|
940
|
+
augmentor = ServiceAugmentor(resolved_service, feature_lib)
|
|
898
941
|
if args.feature_command == "plan" and args.json:
|
|
899
942
|
plan = augmentor.build_plan(args.feature)
|
|
900
943
|
if plan is None:
|
|
@@ -903,7 +946,7 @@ def main() -> int:
|
|
|
903
946
|
json.dumps(
|
|
904
947
|
{
|
|
905
948
|
"feature": args.feature,
|
|
906
|
-
"service": str(
|
|
949
|
+
"service": str(resolved_service),
|
|
907
950
|
"modifies": plan["modifies"],
|
|
908
951
|
"creates": plan["creates"],
|
|
909
952
|
},
|
|
@@ -452,9 +452,18 @@ def _build_smoke(spec: ClusterSpec) -> str:
|
|
|
452
452
|
return "\n".join(lines) + "\n"
|
|
453
453
|
|
|
454
454
|
|
|
455
|
-
def generate_cluster(
|
|
455
|
+
def generate_cluster(
|
|
456
|
+
spec: ClusterSpec, output_dir: Path, force: bool = False, workspace_mode: bool = False
|
|
457
|
+
) -> Path:
|
|
456
458
|
normalized = normalize_cluster_spec(spec)
|
|
457
|
-
|
|
459
|
+
|
|
460
|
+
if workspace_mode:
|
|
461
|
+
# In workspace mode, write files directly to output_dir (workspace root)
|
|
462
|
+
cluster_root = output_dir.resolve()
|
|
463
|
+
else:
|
|
464
|
+
# In standalone mode, create a subdirectory with cluster name
|
|
465
|
+
cluster_root = output_dir.resolve() / normalized["cluster"]["name"]
|
|
466
|
+
|
|
458
467
|
cluster_root.mkdir(parents=True, exist_ok=True)
|
|
459
468
|
|
|
460
469
|
if any(cluster_root.iterdir()) and not force:
|
|
@@ -0,0 +1,64 @@
|
|
|
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/
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["hatchling"]
|
|
3
|
+
build-backend = "hatchling.build"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "{{ cookiecutter.service_name }}"
|
|
7
|
+
version = "0.1.0"
|
|
8
|
+
description = "{{ cookiecutter.service_description }}"
|
|
9
|
+
readme = "README.md"
|
|
10
|
+
requires-python = ">=3.12"
|
|
11
|
+
license = {text = "MIT"}
|
|
12
|
+
authors = [{name = "Generated by csrd", email = "generated@csrd.local"}]
|
|
13
|
+
keywords = ["fastapi", "microservice", "csrd"]
|
|
14
|
+
|
|
15
|
+
dependencies = [
|
|
16
|
+
"fastapi>=0.110",
|
|
17
|
+
"uvicorn[standard]>=0.27",
|
|
18
|
+
"pydantic[email]>=2.5",
|
|
19
|
+
"csrd-versioning @ git+https://github.com/csrd-api/fastapi-common.git#subdirectory=packages/versioning",
|
|
20
|
+
"csrd-lifespan @ git+https://github.com/csrd-api/fastapi-common.git#subdirectory=packages/lifespan",
|
|
21
|
+
"csrd-repository @ git+https://github.com/csrd-api/fastapi-common.git#subdirectory=packages/repository",
|
|
22
|
+
"csrd-context @ git+https://github.com/csrd-api/fastapi-common.git#subdirectory=packages/context",
|
|
23
|
+
"httpx>=0.25",
|
|
24
|
+
"celery[redis]>=5.3",
|
|
25
|
+
"celery[amqp]>=5.3",
|
|
26
|
+
"redis>=5.0",
|
|
27
|
+
"csrd-logging @ git+https://github.com/csrd-api/fastapi-common.git#subdirectory=packages/logging",
|
|
28
|
+
]
|
|
29
|
+
|
|
30
|
+
[project.optional-dependencies]
|
|
31
|
+
dev = [
|
|
32
|
+
"pytest>=7.4",
|
|
33
|
+
"pytest-asyncio>=0.21",
|
|
34
|
+
"mypy>=1.7",
|
|
35
|
+
"ruff>=0.1",
|
|
36
|
+
]
|
|
37
|
+
|
|
38
|
+
[tool.hatch.build.targets.wheel]
|
|
39
|
+
packages = ["src/app"]
|
|
40
|
+
|
|
41
|
+
[tool.pytest.ini_options]
|
|
42
|
+
asyncio_mode = "auto"
|
|
43
|
+
testpaths = ["tests"]
|
|
44
|
+
|
|
45
|
+
[tool.mypy]
|
|
46
|
+
python_version = "3.12"
|
|
47
|
+
warn_return_any = true
|
|
48
|
+
warn_unused_configs = true
|
|
49
|
+
disallow_untyped_defs = true
|
|
50
|
+
strict = true
|
|
51
|
+
|
|
52
|
+
[tool.ruff]
|
|
53
|
+
target-version = "py312"
|
|
54
|
+
line-length = 100
|
|
55
|
+
|
|
56
|
+
[tool.ruff.lint]
|
|
57
|
+
select = ["E", "F", "W", "I", "N", "UP", "BLE", "FBT", "A", "C4", "PIE", "SIM", "RUF"]
|
|
58
|
+
ignore = ["E501"]
|
|
59
|
+
|
|
60
|
+
[tool.ruff.lint.isort]
|
|
61
|
+
known-first-party = ["app"]
|
|
@@ -104,7 +104,7 @@ async def list_{{ cookiecutter.model_name_plural | lower | replace(' ', '_') }}(
|
|
|
104
104
|
{%- endif %}
|
|
105
105
|
|
|
106
106
|
|
|
107
|
-
@router.get(f"{ITEMS_ROUTE}/{entity_id}")
|
|
107
|
+
@router.get(f"{ITEMS_ROUTE}" + "/{entity_id}")
|
|
108
108
|
async def get_{{ cookiecutter.model_name | lower }}(
|
|
109
109
|
entity_id: str,
|
|
110
110
|
{%- if cookiecutter.has_database %}
|
|
@@ -128,7 +128,7 @@ async def get_{{ cookiecutter.model_name | lower }}(
|
|
|
128
128
|
return item
|
|
129
129
|
|
|
130
130
|
|
|
131
|
-
@router.put(f"{ITEMS_ROUTE}/{entity_id}")
|
|
131
|
+
@router.put(f"{ITEMS_ROUTE}" + "/{entity_id}")
|
|
132
132
|
async def update_{{ cookiecutter.model_name | lower }}(
|
|
133
133
|
entity_id: str,
|
|
134
134
|
payload: Update{{ cookiecutter.model_name }}Request,
|
|
@@ -161,7 +161,7 @@ async def update_{{ cookiecutter.model_name | lower }}(
|
|
|
161
161
|
return {"id": entity_id, "name": payload.name, "updated": True}
|
|
162
162
|
|
|
163
163
|
|
|
164
|
-
@router.put(f"{ITEMS_ROUTE}/{entity_id}/upsert")
|
|
164
|
+
@router.put(f"{ITEMS_ROUTE}" + "/{entity_id}/upsert")
|
|
165
165
|
async def upsert_{{ cookiecutter.model_name | lower }}(
|
|
166
166
|
entity_id: str,
|
|
167
167
|
payload: Update{{ cookiecutter.model_name }}Request,
|
|
@@ -194,7 +194,7 @@ async def upsert_{{ cookiecutter.model_name | lower }}(
|
|
|
194
194
|
return {"id": entity_id, "name": payload.name, "rows_affected": rows}
|
|
195
195
|
|
|
196
196
|
|
|
197
|
-
@router.delete(f"{ITEMS_ROUTE}/{entity_id}")
|
|
197
|
+
@router.delete(f"{ITEMS_ROUTE}" + "/{entity_id}")
|
|
198
198
|
async def delete_{{ cookiecutter.model_name | lower }}(
|
|
199
199
|
entity_id: str,
|
|
200
200
|
{%- if cookiecutter.has_database %}
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{csrd_utils-0.3.16 → csrd_utils-0.3.18}/src/csrd_utils/features/caching/conftest.fragment.py
RENAMED
|
File without changes
|
{csrd_utils-0.3.16 → csrd_utils-0.3.18}/src/csrd_utils/features/caching/docker-compose.fragment.yaml
RENAMED
|
File without changes
|
|
File without changes
|
{csrd_utils-0.3.16 → csrd_utils-0.3.18}/src/csrd_utils/features/caching/requirements.fragment
RENAMED
|
File without changes
|
{csrd_utils-0.3.16 → csrd_utils-0.3.18}/src/csrd_utils/features/caching/settings.fragment.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{csrd_utils-0.3.16 → csrd_utils-0.3.18}/src/csrd_utils/features/logging/conftest.fragment.py
RENAMED
|
File without changes
|
|
File without changes
|
{csrd_utils-0.3.16 → csrd_utils-0.3.18}/src/csrd_utils/features/logging/requirements.fragment
RENAMED
|
File without changes
|
{csrd_utils-0.3.16 → csrd_utils-0.3.18}/src/csrd_utils/features/logging/settings.fragment.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{csrd_utils-0.3.16 → csrd_utils-0.3.18}/src/csrd_utils/features/metrics/conftest.fragment.py
RENAMED
|
File without changes
|
{csrd_utils-0.3.16 → csrd_utils-0.3.18}/src/csrd_utils/features/metrics/docker-compose.fragment.yaml
RENAMED
|
File without changes
|
|
File without changes
|
{csrd_utils-0.3.16 → csrd_utils-0.3.18}/src/csrd_utils/features/metrics/requirements.fragment
RENAMED
|
File without changes
|
{csrd_utils-0.3.16 → csrd_utils-0.3.18}/src/csrd_utils/features/metrics/settings.fragment.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{csrd_utils-0.3.16 → csrd_utils-0.3.18}/src/csrd_utils/features/tracing/conftest.fragment.py
RENAMED
|
File without changes
|
|
File without changes
|
{csrd_utils-0.3.16 → csrd_utils-0.3.18}/src/csrd_utils/features/tracing/requirements.fragment
RENAMED
|
File without changes
|
{csrd_utils-0.3.16 → csrd_utils-0.3.18}/src/csrd_utils/features/tracing/settings.fragment.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{csrd_utils-0.3.16 → csrd_utils-0.3.18}/src/csrd_utils/features/workers/conftest.fragment.py
RENAMED
|
File without changes
|
{csrd_utils-0.3.16 → csrd_utils-0.3.18}/src/csrd_utils/features/workers/docker-compose.fragment.yaml
RENAMED
|
File without changes
|
|
File without changes
|
{csrd_utils-0.3.16 → csrd_utils-0.3.18}/src/csrd_utils/features/workers/requirements.fragment
RENAMED
|
File without changes
|
{csrd_utils-0.3.16 → csrd_utils-0.3.18}/src/csrd_utils/features/workers/settings.fragment.py
RENAMED
|
File without changes
|
{csrd_utils-0.3.16 → csrd_utils-0.3.18}/src/csrd_utils/features/workers/src/app/workers/__init__.py
RENAMED
|
File without changes
|
|
File without changes
|
{csrd_utils-0.3.16 → csrd_utils-0.3.18}/src/csrd_utils/features/workers/src/app/workers/tasks.py
RENAMED
|
File without changes
|
{csrd_utils-0.3.16 → csrd_utils-0.3.18}/src/csrd_utils/features/workers/tests/test_workers.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{csrd_utils-0.3.16 → csrd_utils-0.3.18}/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
|
|
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
|