csrd-utils 0.3.86__tar.gz → 0.3.90__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.86 → csrd_utils-0.3.90}/PKG-INFO +1 -1
- {csrd_utils-0.3.86 → csrd_utils-0.3.90}/pyproject.toml +3 -2
- {csrd_utils-0.3.86 → csrd_utils-0.3.90}/src/csrd_utils/v2/compose/__init__.py +24 -1
- csrd_utils-0.3.90/src/csrd_utils/v2/compose/augments.py +166 -0
- {csrd_utils-0.3.86 → csrd_utils-0.3.90}/src/csrd_utils/v2/compose/infra.py +0 -2
- csrd_utils-0.3.90/src/csrd_utils/v2/compose/operations.py +358 -0
- {csrd_utils-0.3.86 → csrd_utils-0.3.90}/src/csrd_utils/v2/compose/presets.py +15 -11
- {csrd_utils-0.3.86 → csrd_utils-0.3.90}/src/csrd_utils/v2/compose/renderer.py +122 -3
- csrd_utils-0.3.90/src/csrd_utils/v2/compose/scaffolder.py +265 -0
- csrd_utils-0.3.90/src/csrd_utils/v2/compose/service_renderers.py +335 -0
- {csrd_utils-0.3.86 → csrd_utils-0.3.90}/src/csrd_utils/v2/compose/yaml_editor.py +0 -2
- {csrd_utils-0.3.86 → csrd_utils-0.3.90}/src/csrd_utils/v2/generate/handlers.py +9 -2
- csrd_utils-0.3.90/src/csrd_utils/v2/generate/helpers.py +71 -0
- {csrd_utils-0.3.86 → csrd_utils-0.3.90}/src/csrd_utils/v2/generate/menu.py +2 -3
- {csrd_utils-0.3.86 → csrd_utils-0.3.90}/src/csrd_utils/v2/models/__init__.py +8 -3
- {csrd_utils-0.3.86 → csrd_utils-0.3.90}/src/csrd_utils/v2/models/spec.py +20 -3
- csrd_utils-0.3.90/src/csrd_utils/v2/models/types.py +8 -0
- csrd_utils-0.3.90/src/csrd_utils/v2/templates/augments/db-config/dependencies/db.py +21 -0
- csrd_utils-0.3.90/src/csrd_utils/v2/templates/augments/db-config/migrations.py +21 -0
- csrd_utils-0.3.90/src/csrd_utils/v2/templates/augments/jwt-auth-consumer/dependencies/auth.py +24 -0
- csrd_utils-0.3.90/src/csrd_utils/v2/templates/augments/jwt-auth-provider/dependencies/db.py +31 -0
- csrd_utils-0.3.90/src/csrd_utils/v2/templates/augments/jwt-auth-provider/migrations.py +52 -0
- csrd_utils-0.3.90/src/csrd_utils/v2/templates/augments/jwt-auth-provider/repositories/user_repository.py +108 -0
- csrd_utils-0.3.90/src/csrd_utils/v2/templates/augments/jwt-auth-provider/views/auth_view.py +121 -0
- csrd_utils-0.3.90/src/csrd_utils/v2/templates/augments/jwt-auth-provider/views/jwks_view.py +27 -0
- csrd_utils-0.3.90/src/csrd_utils/v2/templates/augments/jwt-auth-provider/views/users_view.py +77 -0
- csrd_utils-0.3.90/src/tui_wizard/__init__.py +45 -0
- csrd_utils-0.3.90/src/tui_wizard/exceptions.py +5 -0
- csrd_utils-0.3.90/src/tui_wizard/menu.py +128 -0
- csrd_utils-0.3.90/src/tui_wizard/models.py +34 -0
- csrd_utils-0.3.90/src/tui_wizard/prompts.py +311 -0
- csrd_utils-0.3.90/src/tui_wizard/terminal.py +74 -0
- csrd_utils-0.3.90/src/tui_wizard/wizard.py +67 -0
- csrd_utils-0.3.86/src/csrd_utils/v2/compose/operations.py +0 -126
- csrd_utils-0.3.86/src/csrd_utils/v2/compose/scaffolder.py +0 -89
- csrd_utils-0.3.86/src/csrd_utils/v2/generate/helpers.py +0 -647
- csrd_utils-0.3.86/src/csrd_utils/v2/models/types.py +0 -6
- {csrd_utils-0.3.86 → csrd_utils-0.3.90}/.gitignore +0 -0
- {csrd_utils-0.3.86 → csrd_utils-0.3.90}/README.md +0 -0
- {csrd_utils-0.3.86 → csrd_utils-0.3.90}/src/csrd_utils/__init__.py +0 -0
- {csrd_utils-0.3.86 → csrd_utils-0.3.90}/src/csrd_utils/__main__.py +0 -0
- {csrd_utils-0.3.86 → csrd_utils-0.3.90}/src/csrd_utils/audit.py +0 -0
- {csrd_utils-0.3.86 → csrd_utils-0.3.90}/src/csrd_utils/augmentor.py +0 -0
- {csrd_utils-0.3.86 → csrd_utils-0.3.90}/src/csrd_utils/cluster.py +0 -0
- {csrd_utils-0.3.86 → csrd_utils-0.3.90}/src/csrd_utils/doctor.py +0 -0
- {csrd_utils-0.3.86 → csrd_utils-0.3.90}/src/csrd_utils/features/caching/conftest.fragment.py +0 -0
- {csrd_utils-0.3.86 → csrd_utils-0.3.90}/src/csrd_utils/features/caching/docker-compose.fragment.yaml +0 -0
- {csrd_utils-0.3.86 → csrd_utils-0.3.90}/src/csrd_utils/features/caching/manifest.yaml +0 -0
- {csrd_utils-0.3.86 → csrd_utils-0.3.90}/src/csrd_utils/features/caching/requirements.fragment +0 -0
- {csrd_utils-0.3.86 → csrd_utils-0.3.90}/src/csrd_utils/features/caching/settings.fragment.py +0 -0
- {csrd_utils-0.3.86 → csrd_utils-0.3.90}/src/csrd_utils/features/caching/src/app/cache.py +0 -0
- {csrd_utils-0.3.86 → csrd_utils-0.3.90}/src/csrd_utils/features/caching/tests/test_cache.py +0 -0
- {csrd_utils-0.3.86 → csrd_utils-0.3.90}/src/csrd_utils/features/logging/conftest.fragment.py +0 -0
- {csrd_utils-0.3.86 → csrd_utils-0.3.90}/src/csrd_utils/features/logging/manifest.yaml +0 -0
- {csrd_utils-0.3.86 → csrd_utils-0.3.90}/src/csrd_utils/features/logging/requirements.fragment +0 -0
- {csrd_utils-0.3.86 → csrd_utils-0.3.90}/src/csrd_utils/features/logging/settings.fragment.py +0 -0
- {csrd_utils-0.3.86 → csrd_utils-0.3.90}/src/csrd_utils/features/logging/src/app/middleware/logging.py +0 -0
- {csrd_utils-0.3.86 → csrd_utils-0.3.90}/src/csrd_utils/features/logging/tests/test_logging_middleware.py +0 -0
- {csrd_utils-0.3.86 → csrd_utils-0.3.90}/src/csrd_utils/features/metrics/conftest.fragment.py +0 -0
- {csrd_utils-0.3.86 → csrd_utils-0.3.90}/src/csrd_utils/features/metrics/docker-compose.fragment.yaml +0 -0
- {csrd_utils-0.3.86 → csrd_utils-0.3.90}/src/csrd_utils/features/metrics/manifest.yaml +0 -0
- {csrd_utils-0.3.86 → csrd_utils-0.3.90}/src/csrd_utils/features/metrics/requirements.fragment +0 -0
- {csrd_utils-0.3.86 → csrd_utils-0.3.90}/src/csrd_utils/features/metrics/settings.fragment.py +0 -0
- {csrd_utils-0.3.86 → csrd_utils-0.3.90}/src/csrd_utils/features/metrics/src/app/middleware/metrics.py +0 -0
- {csrd_utils-0.3.86 → csrd_utils-0.3.90}/src/csrd_utils/features/metrics/tests/test_metrics_middleware.py +0 -0
- {csrd_utils-0.3.86 → csrd_utils-0.3.90}/src/csrd_utils/features/tracing/conftest.fragment.py +0 -0
- {csrd_utils-0.3.86 → csrd_utils-0.3.90}/src/csrd_utils/features/tracing/manifest.yaml +0 -0
- {csrd_utils-0.3.86 → csrd_utils-0.3.90}/src/csrd_utils/features/tracing/requirements.fragment +0 -0
- {csrd_utils-0.3.86 → csrd_utils-0.3.90}/src/csrd_utils/features/tracing/settings.fragment.py +0 -0
- {csrd_utils-0.3.86 → csrd_utils-0.3.90}/src/csrd_utils/features/tracing/src/app/middleware/tracing.py +0 -0
- {csrd_utils-0.3.86 → csrd_utils-0.3.90}/src/csrd_utils/features/tracing/tests/test_tracing_middleware.py +0 -0
- {csrd_utils-0.3.86 → csrd_utils-0.3.90}/src/csrd_utils/features/workers/conftest.fragment.py +0 -0
- {csrd_utils-0.3.86 → csrd_utils-0.3.90}/src/csrd_utils/features/workers/docker-compose.fragment.yaml +0 -0
- {csrd_utils-0.3.86 → csrd_utils-0.3.90}/src/csrd_utils/features/workers/manifest.yaml +0 -0
- {csrd_utils-0.3.86 → csrd_utils-0.3.90}/src/csrd_utils/features/workers/requirements.fragment +0 -0
- {csrd_utils-0.3.86 → csrd_utils-0.3.90}/src/csrd_utils/features/workers/settings.fragment.py +0 -0
- {csrd_utils-0.3.86 → csrd_utils-0.3.90}/src/csrd_utils/features/workers/src/app/workers/__init__.py +0 -0
- {csrd_utils-0.3.86 → csrd_utils-0.3.90}/src/csrd_utils/features/workers/src/app/workers/celery_app.py +0 -0
- {csrd_utils-0.3.86 → csrd_utils-0.3.90}/src/csrd_utils/features/workers/src/app/workers/tasks.py +0 -0
- {csrd_utils-0.3.86 → csrd_utils-0.3.90}/src/csrd_utils/features/workers/tests/test_workers.py +0 -0
- {csrd_utils-0.3.86 → csrd_utils-0.3.90}/src/csrd_utils/fragments.py +0 -0
- {csrd_utils-0.3.86 → csrd_utils-0.3.90}/src/csrd_utils/generator.py +0 -0
- {csrd_utils-0.3.86 → csrd_utils-0.3.90}/src/csrd_utils/resources.py +0 -0
- {csrd_utils-0.3.86 → csrd_utils-0.3.90}/src/csrd_utils/templates/cookiecutter-service/README.md +0 -0
- {csrd_utils-0.3.86 → csrd_utils-0.3.90}/src/csrd_utils/templates/cookiecutter-service/cookiecutter.json +0 -0
- {csrd_utils-0.3.86 → csrd_utils-0.3.90}/src/csrd_utils/templates/cookiecutter-service/hooks/post_gen_project.py +0 -0
- {csrd_utils-0.3.86 → csrd_utils-0.3.90}/src/csrd_utils/templates/cookiecutter-service/{{cookiecutter.__service_name_snake}}/.cookiecutterignore +0 -0
- {csrd_utils-0.3.86 → csrd_utils-0.3.90}/src/csrd_utils/templates/cookiecutter-service/{{cookiecutter.__service_name_snake}}/.dockerignore +0 -0
- {csrd_utils-0.3.86 → csrd_utils-0.3.90}/src/csrd_utils/templates/cookiecutter-service/{{cookiecutter.__service_name_snake}}/Dockerfile +0 -0
- {csrd_utils-0.3.86 → csrd_utils-0.3.90}/src/csrd_utils/templates/cookiecutter-service/{{cookiecutter.__service_name_snake}}/README.md +0 -0
- {csrd_utils-0.3.86 → csrd_utils-0.3.90}/src/csrd_utils/templates/cookiecutter-service/{{cookiecutter.__service_name_snake}}/__init__.py +0 -0
- {csrd_utils-0.3.86 → csrd_utils-0.3.90}/src/csrd_utils/templates/cookiecutter-service/{{cookiecutter.__service_name_snake}}/cache.py +0 -0
- {csrd_utils-0.3.86 → csrd_utils-0.3.90}/src/csrd_utils/templates/cookiecutter-service/{{cookiecutter.__service_name_snake}}/delegates/__init__.py +0 -0
- {csrd_utils-0.3.86 → csrd_utils-0.3.90}/src/csrd_utils/templates/cookiecutter-service/{{cookiecutter.__service_name_snake}}/delegates/upstreams.py +0 -0
- {csrd_utils-0.3.86 → csrd_utils-0.3.90}/src/csrd_utils/templates/cookiecutter-service/{{cookiecutter.__service_name_snake}}/dependencies/__init__.py +0 -0
- {csrd_utils-0.3.86 → csrd_utils-0.3.90}/src/csrd_utils/templates/cookiecutter-service/{{cookiecutter.__service_name_snake}}/dependencies/auth_service.py +0 -0
- {csrd_utils-0.3.86 → csrd_utils-0.3.90}/src/csrd_utils/templates/cookiecutter-service/{{cookiecutter.__service_name_snake}}/dependencies/delegates.py +0 -0
- {csrd_utils-0.3.86 → csrd_utils-0.3.90}/src/csrd_utils/templates/cookiecutter-service/{{cookiecutter.__service_name_snake}}/dependencies/item_repository.py +0 -0
- {csrd_utils-0.3.86 → csrd_utils-0.3.90}/src/csrd_utils/templates/cookiecutter-service/{{cookiecutter.__service_name_snake}}/dependencies/worker_broker.py +0 -0
- {csrd_utils-0.3.86 → csrd_utils-0.3.90}/src/csrd_utils/templates/cookiecutter-service/{{cookiecutter.__service_name_snake}}/docker-compose.yml +0 -0
- {csrd_utils-0.3.86 → csrd_utils-0.3.90}/src/csrd_utils/templates/cookiecutter-service/{{cookiecutter.__service_name_snake}}/middleware/__init__.py +0 -0
- {csrd_utils-0.3.86 → csrd_utils-0.3.90}/src/csrd_utils/templates/cookiecutter-service/{{cookiecutter.__service_name_snake}}/middleware/logging.py +0 -0
- {csrd_utils-0.3.86 → csrd_utils-0.3.90}/src/csrd_utils/templates/cookiecutter-service/{{cookiecutter.__service_name_snake}}/repositories/__init__.py +0 -0
- {csrd_utils-0.3.86 → csrd_utils-0.3.90}/src/csrd_utils/templates/cookiecutter-service/{{cookiecutter.__service_name_snake}}/repositories/item_repository.py +0 -0
- {csrd_utils-0.3.86 → csrd_utils-0.3.90}/src/csrd_utils/templates/cookiecutter-service/{{cookiecutter.__service_name_snake}}/requirements.txt +0 -0
- {csrd_utils-0.3.86 → csrd_utils-0.3.90}/src/csrd_utils/templates/cookiecutter-service/{{cookiecutter.__service_name_snake}}/settings.py +0 -0
- {csrd_utils-0.3.86 → csrd_utils-0.3.90}/src/csrd_utils/templates/cookiecutter-service/{{cookiecutter.__service_name_snake}}/tests/__init__.py +0 -0
- {csrd_utils-0.3.86 → csrd_utils-0.3.90}/src/csrd_utils/templates/cookiecutter-service/{{cookiecutter.__service_name_snake}}/tests/acceptance/__init__.py +0 -0
- {csrd_utils-0.3.86 → csrd_utils-0.3.90}/src/csrd_utils/templates/cookiecutter-service/{{cookiecutter.__service_name_snake}}/tests/acceptance/test_logging_middleware.py +0 -0
- {csrd_utils-0.3.86 → csrd_utils-0.3.90}/src/csrd_utils/templates/cookiecutter-service/{{cookiecutter.__service_name_snake}}/tests/conftest.py +0 -0
- {csrd_utils-0.3.86 → csrd_utils-0.3.90}/src/csrd_utils/templates/cookiecutter-service/{{cookiecutter.__service_name_snake}}/tests/test_items.py +0 -0
- {csrd_utils-0.3.86 → csrd_utils-0.3.90}/src/csrd_utils/templates/cookiecutter-service/{{cookiecutter.__service_name_snake}}/tests/unit/__init__.py +0 -0
- {csrd_utils-0.3.86 → csrd_utils-0.3.90}/src/csrd_utils/templates/cookiecutter-service/{{cookiecutter.__service_name_snake}}/tests/unit/test_cache.py +0 -0
- {csrd_utils-0.3.86 → csrd_utils-0.3.90}/src/csrd_utils/templates/cookiecutter-service/{{cookiecutter.__service_name_snake}}/tests/unit/test_database_settings.py +0 -0
- {csrd_utils-0.3.86 → csrd_utils-0.3.90}/src/csrd_utils/templates/cookiecutter-service/{{cookiecutter.__service_name_snake}}/tests/unit/test_delegates.py +0 -0
- {csrd_utils-0.3.86 → csrd_utils-0.3.90}/src/csrd_utils/templates/cookiecutter-service/{{cookiecutter.__service_name_snake}}/tests/unit/test_settings_contracts.py +0 -0
- {csrd_utils-0.3.86 → csrd_utils-0.3.90}/src/csrd_utils/templates/cookiecutter-service/{{cookiecutter.__service_name_snake}}/tests/unit/test_worker_broker.py +0 -0
- {csrd_utils-0.3.86 → csrd_utils-0.3.90}/src/csrd_utils/templates/cookiecutter-service/{{cookiecutter.__service_name_snake}}/views/__init__.py +0 -0
- {csrd_utils-0.3.86 → csrd_utils-0.3.90}/src/csrd_utils/templates/cookiecutter-service/{{cookiecutter.__service_name_snake}}/views/unversioned/__init__.py +0 -0
- {csrd_utils-0.3.86 → csrd_utils-0.3.90}/src/csrd_utils/templates/cookiecutter-service/{{cookiecutter.__service_name_snake}}/views/unversioned/health_view.py +0 -0
- {csrd_utils-0.3.86 → csrd_utils-0.3.90}/src/csrd_utils/templates/cookiecutter-service/{{cookiecutter.__service_name_snake}}/views/unversioned/items_view.py +0 -0
- {csrd_utils-0.3.86 → csrd_utils-0.3.90}/src/csrd_utils/templates/cookiecutter-service/{{cookiecutter.__service_name_snake}}/workers/__init__.py +0 -0
- {csrd_utils-0.3.86 → csrd_utils-0.3.90}/src/csrd_utils/templates/cookiecutter-service/{{cookiecutter.__service_name_snake}}/workers/tasks.py +0 -0
- {csrd_utils-0.3.86 → csrd_utils-0.3.90}/src/csrd_utils/templates/platform-app-service/cookiecutter.json +0 -0
- {csrd_utils-0.3.86 → csrd_utils-0.3.90}/src/csrd_utils/templates/platform-app-service/{{cookiecutter.__service_name_snake}}/.dockerignore +0 -0
- {csrd_utils-0.3.86 → csrd_utils-0.3.90}/src/csrd_utils/templates/platform-app-service/{{cookiecutter.__service_name_snake}}/Dockerfile +0 -0
- {csrd_utils-0.3.86 → csrd_utils-0.3.90}/src/csrd_utils/templates/platform-app-service/{{cookiecutter.__service_name_snake}}/README.md +0 -0
- {csrd_utils-0.3.86 → csrd_utils-0.3.90}/src/csrd_utils/templates/platform-app-service/{{cookiecutter.__service_name_snake}}/__init__.py +0 -0
- {csrd_utils-0.3.86 → csrd_utils-0.3.90}/src/csrd_utils/templates/platform-app-service/{{cookiecutter.__service_name_snake}}/delegates/__init__.py +0 -0
- {csrd_utils-0.3.86 → csrd_utils-0.3.90}/src/csrd_utils/templates/platform-app-service/{{cookiecutter.__service_name_snake}}/delegates/auth_delegate.py +0 -0
- {csrd_utils-0.3.86 → csrd_utils-0.3.90}/src/csrd_utils/templates/platform-app-service/{{cookiecutter.__service_name_snake}}/delegates/identity_delegate.py +0 -0
- {csrd_utils-0.3.86 → csrd_utils-0.3.90}/src/csrd_utils/templates/platform-app-service/{{cookiecutter.__service_name_snake}}/dependencies/__init__.py +0 -0
- {csrd_utils-0.3.86 → csrd_utils-0.3.90}/src/csrd_utils/templates/platform-app-service/{{cookiecutter.__service_name_snake}}/dependencies/auth_passthrough.py +0 -0
- {csrd_utils-0.3.86 → csrd_utils-0.3.90}/src/csrd_utils/templates/platform-app-service/{{cookiecutter.__service_name_snake}}/dependencies/auth_service.py +0 -0
- {csrd_utils-0.3.86 → csrd_utils-0.3.90}/src/csrd_utils/templates/platform-app-service/{{cookiecutter.__service_name_snake}}/dependencies/item_repository.py +0 -0
- {csrd_utils-0.3.86 → csrd_utils-0.3.90}/src/csrd_utils/templates/platform-app-service/{{cookiecutter.__service_name_snake}}/models/__init__.py +0 -0
- {csrd_utils-0.3.86 → csrd_utils-0.3.90}/src/csrd_utils/templates/platform-app-service/{{cookiecutter.__service_name_snake}}/models/auth_service.py +0 -0
- {csrd_utils-0.3.86 → csrd_utils-0.3.90}/src/csrd_utils/templates/platform-app-service/{{cookiecutter.__service_name_snake}}/models/items.py +0 -0
- {csrd_utils-0.3.86 → csrd_utils-0.3.90}/src/csrd_utils/templates/platform-app-service/{{cookiecutter.__service_name_snake}}/repositories/__init__.py +0 -0
- {csrd_utils-0.3.86 → csrd_utils-0.3.90}/src/csrd_utils/templates/platform-app-service/{{cookiecutter.__service_name_snake}}/repositories/item_repository.py +0 -0
- {csrd_utils-0.3.86 → csrd_utils-0.3.90}/src/csrd_utils/templates/platform-app-service/{{cookiecutter.__service_name_snake}}/requirements.txt +0 -0
- {csrd_utils-0.3.86 → csrd_utils-0.3.90}/src/csrd_utils/templates/platform-app-service/{{cookiecutter.__service_name_snake}}/settings.py +0 -0
- {csrd_utils-0.3.86 → csrd_utils-0.3.90}/src/csrd_utils/templates/platform-app-service/{{cookiecutter.__service_name_snake}}/views/__init__.py +0 -0
- {csrd_utils-0.3.86 → csrd_utils-0.3.90}/src/csrd_utils/templates/platform-app-service/{{cookiecutter.__service_name_snake}}/views/unversioned/__init__.py +0 -0
- {csrd_utils-0.3.86 → csrd_utils-0.3.90}/src/csrd_utils/templates/platform-app-service/{{cookiecutter.__service_name_snake}}/views/unversioned/auth_service_view.py +0 -0
- {csrd_utils-0.3.86 → csrd_utils-0.3.90}/src/csrd_utils/templates/platform-app-service/{{cookiecutter.__service_name_snake}}/views/unversioned/health_view.py +0 -0
- {csrd_utils-0.3.86 → csrd_utils-0.3.90}/src/csrd_utils/templates/platform-app-service/{{cookiecutter.__service_name_snake}}/views/unversioned/items_view.py +0 -0
- {csrd_utils-0.3.86 → csrd_utils-0.3.90}/src/csrd_utils/templates/platform-auth-service/cookiecutter.json +0 -0
- {csrd_utils-0.3.86 → csrd_utils-0.3.90}/src/csrd_utils/templates/platform-auth-service/{{cookiecutter.__service_name_snake}}/.dockerignore +0 -0
- {csrd_utils-0.3.86 → csrd_utils-0.3.90}/src/csrd_utils/templates/platform-auth-service/{{cookiecutter.__service_name_snake}}/Dockerfile +0 -0
- {csrd_utils-0.3.86 → csrd_utils-0.3.90}/src/csrd_utils/templates/platform-auth-service/{{cookiecutter.__service_name_snake}}/README.md +0 -0
- {csrd_utils-0.3.86 → csrd_utils-0.3.90}/src/csrd_utils/templates/platform-auth-service/{{cookiecutter.__service_name_snake}}/__init__.py +0 -0
- {csrd_utils-0.3.86 → csrd_utils-0.3.90}/src/csrd_utils/templates/platform-auth-service/{{cookiecutter.__service_name_snake}}/dependencies/__init__.py +0 -0
- {csrd_utils-0.3.86 → csrd_utils-0.3.90}/src/csrd_utils/templates/platform-auth-service/{{cookiecutter.__service_name_snake}}/models/__init__.py +0 -0
- {csrd_utils-0.3.86 → csrd_utils-0.3.90}/src/csrd_utils/templates/platform-auth-service/{{cookiecutter.__service_name_snake}}/models/auth.py +0 -0
- {csrd_utils-0.3.86 → csrd_utils-0.3.90}/src/csrd_utils/templates/platform-auth-service/{{cookiecutter.__service_name_snake}}/repositories/__init__.py +0 -0
- {csrd_utils-0.3.86 → csrd_utils-0.3.90}/src/csrd_utils/templates/platform-auth-service/{{cookiecutter.__service_name_snake}}/repositories/token_repository.py +0 -0
- {csrd_utils-0.3.86 → csrd_utils-0.3.90}/src/csrd_utils/templates/platform-auth-service/{{cookiecutter.__service_name_snake}}/requirements.txt +0 -0
- {csrd_utils-0.3.86 → csrd_utils-0.3.90}/src/csrd_utils/templates/platform-auth-service/{{cookiecutter.__service_name_snake}}/settings.py +0 -0
- {csrd_utils-0.3.86 → csrd_utils-0.3.90}/src/csrd_utils/templates/platform-auth-service/{{cookiecutter.__service_name_snake}}/views/__init__.py +0 -0
- {csrd_utils-0.3.86 → csrd_utils-0.3.90}/src/csrd_utils/templates/platform-auth-service/{{cookiecutter.__service_name_snake}}/views/unversioned/__init__.py +0 -0
- {csrd_utils-0.3.86 → csrd_utils-0.3.90}/src/csrd_utils/templates/platform-auth-service/{{cookiecutter.__service_name_snake}}/views/unversioned/auth_view.py +0 -0
- {csrd_utils-0.3.86 → csrd_utils-0.3.90}/src/csrd_utils/templates/platform-auth-service/{{cookiecutter.__service_name_snake}}/views/unversioned/health_view.py +0 -0
- {csrd_utils-0.3.86 → csrd_utils-0.3.90}/src/csrd_utils/templates/platform-identity-service/cookiecutter.json +0 -0
- {csrd_utils-0.3.86 → csrd_utils-0.3.90}/src/csrd_utils/templates/platform-identity-service/{{cookiecutter.__service_name_snake}}/.dockerignore +0 -0
- {csrd_utils-0.3.86 → csrd_utils-0.3.90}/src/csrd_utils/templates/platform-identity-service/{{cookiecutter.__service_name_snake}}/Dockerfile +0 -0
- {csrd_utils-0.3.86 → csrd_utils-0.3.90}/src/csrd_utils/templates/platform-identity-service/{{cookiecutter.__service_name_snake}}/README.md +0 -0
- {csrd_utils-0.3.86 → csrd_utils-0.3.90}/src/csrd_utils/templates/platform-identity-service/{{cookiecutter.__service_name_snake}}/__init__.py +0 -0
- {csrd_utils-0.3.86 → csrd_utils-0.3.90}/src/csrd_utils/templates/platform-identity-service/{{cookiecutter.__service_name_snake}}/dependencies/__init__.py +0 -0
- {csrd_utils-0.3.86 → csrd_utils-0.3.90}/src/csrd_utils/templates/platform-identity-service/{{cookiecutter.__service_name_snake}}/models/__init__.py +0 -0
- {csrd_utils-0.3.86 → csrd_utils-0.3.90}/src/csrd_utils/templates/platform-identity-service/{{cookiecutter.__service_name_snake}}/models/users.py +0 -0
- {csrd_utils-0.3.86 → csrd_utils-0.3.90}/src/csrd_utils/templates/platform-identity-service/{{cookiecutter.__service_name_snake}}/repositories/__init__.py +0 -0
- {csrd_utils-0.3.86 → csrd_utils-0.3.90}/src/csrd_utils/templates/platform-identity-service/{{cookiecutter.__service_name_snake}}/repositories/user_repository.py +0 -0
- {csrd_utils-0.3.86 → csrd_utils-0.3.90}/src/csrd_utils/templates/platform-identity-service/{{cookiecutter.__service_name_snake}}/requirements.txt +0 -0
- {csrd_utils-0.3.86 → csrd_utils-0.3.90}/src/csrd_utils/templates/platform-identity-service/{{cookiecutter.__service_name_snake}}/settings.py +0 -0
- {csrd_utils-0.3.86 → csrd_utils-0.3.90}/src/csrd_utils/templates/platform-identity-service/{{cookiecutter.__service_name_snake}}/views/__init__.py +0 -0
- {csrd_utils-0.3.86 → csrd_utils-0.3.90}/src/csrd_utils/templates/platform-identity-service/{{cookiecutter.__service_name_snake}}/views/unversioned/__init__.py +0 -0
- {csrd_utils-0.3.86 → csrd_utils-0.3.90}/src/csrd_utils/templates/platform-identity-service/{{cookiecutter.__service_name_snake}}/views/unversioned/health_view.py +0 -0
- {csrd_utils-0.3.86 → csrd_utils-0.3.90}/src/csrd_utils/templates/platform-identity-service/{{cookiecutter.__service_name_snake}}/views/unversioned/users_view.py +0 -0
- {csrd_utils-0.3.86 → csrd_utils-0.3.90}/src/csrd_utils/v2/AGENTS.md +0 -0
- {csrd_utils-0.3.86 → csrd_utils-0.3.90}/src/csrd_utils/v2/__init__.py +0 -0
- {csrd_utils-0.3.86 → csrd_utils-0.3.90}/src/csrd_utils/v2/compose/git.py +0 -0
- {csrd_utils-0.3.86 → csrd_utils-0.3.90}/src/csrd_utils/v2/compose/loader.py +0 -0
- {csrd_utils-0.3.86 → csrd_utils-0.3.90}/src/csrd_utils/v2/generate/__init__.py +0 -0
- {csrd_utils-0.3.86 → csrd_utils-0.3.90}/src/csrd_utils/v2/models/base.py +0 -0
- {csrd_utils-0.3.86 → csrd_utils-0.3.90}/src/csrd_utils/v2/templates/service/cookiecutter.json +0 -0
- {csrd_utils-0.3.86 → csrd_utils-0.3.90}/src/csrd_utils/v2/templates/service/{{cookiecutter.__service_name_snake}}/Dockerfile +0 -0
- {csrd_utils-0.3.86 → csrd_utils-0.3.90}/src/csrd_utils/v2/templates/service/{{cookiecutter.__service_name_snake}}/README.md +0 -0
- {csrd_utils-0.3.86 → csrd_utils-0.3.90}/src/csrd_utils/v2/templates/service/{{cookiecutter.__service_name_snake}}/__init__.py +0 -0
- {csrd_utils-0.3.86 → csrd_utils-0.3.90}/src/csrd_utils/v2/templates/service/{{cookiecutter.__service_name_snake}}/requirements.txt +0 -0
- {csrd_utils-0.3.86 → csrd_utils-0.3.90}/src/csrd_utils/v2/templates/service/{{cookiecutter.__service_name_snake}}/settings.py +0 -0
- {csrd_utils-0.3.86 → csrd_utils-0.3.90}/src/csrd_utils/v2/templates/service/{{cookiecutter.__service_name_snake}}/tests/__init__.py +0 -0
- {csrd_utils-0.3.86 → csrd_utils-0.3.90}/src/csrd_utils/v2/templates/service/{{cookiecutter.__service_name_snake}}/tests/conftest.py +0 -0
- {csrd_utils-0.3.86 → csrd_utils-0.3.90}/src/csrd_utils/v2/templates/service/{{cookiecutter.__service_name_snake}}/tests/test_health.py +0 -0
- {csrd_utils-0.3.86 → csrd_utils-0.3.90}/src/csrd_utils/v2/templates/service/{{cookiecutter.__service_name_snake}}/views/__init__.py +0 -0
- {csrd_utils-0.3.86 → csrd_utils-0.3.90}/src/csrd_utils/v2/templates/service/{{cookiecutter.__service_name_snake}}/views/health_view.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.90
|
|
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
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[project]
|
|
2
2
|
name = "csrd-utils"
|
|
3
|
-
version = "0.3.
|
|
3
|
+
version = "0.3.90"
|
|
4
4
|
description = "CLI utilities for csrd service generation and feature augmentation"
|
|
5
5
|
license = { text = "MIT" }
|
|
6
6
|
requires-python = ">=3.12"
|
|
@@ -26,11 +26,12 @@ requires = ["hatchling"]
|
|
|
26
26
|
build-backend = "hatchling.build"
|
|
27
27
|
|
|
28
28
|
[tool.hatch.build.targets.wheel]
|
|
29
|
-
packages = ["src/csrd_utils"]
|
|
29
|
+
packages = ["src/csrd_utils", "src/tui_wizard"]
|
|
30
30
|
|
|
31
31
|
[tool.hatch.build.targets.sdist]
|
|
32
32
|
include = [
|
|
33
33
|
"src/csrd_utils/**",
|
|
34
|
+
"src/tui_wizard/**",
|
|
34
35
|
"README.md",
|
|
35
36
|
"pyproject.toml",
|
|
36
37
|
]
|
|
@@ -8,7 +8,15 @@ from ..models import (
|
|
|
8
8
|
ComposeSpec,
|
|
9
9
|
InfraNode,
|
|
10
10
|
PresetDefinition,
|
|
11
|
+
ServiceAugment,
|
|
11
12
|
ServiceNode,
|
|
13
|
+
WorkspaceAugment,
|
|
14
|
+
)
|
|
15
|
+
from .augments import (
|
|
16
|
+
AUGMENT_REGISTRY,
|
|
17
|
+
AugmentDescriptor,
|
|
18
|
+
augment_for,
|
|
19
|
+
augments_for_scope,
|
|
12
20
|
)
|
|
13
21
|
from .infra import (
|
|
14
22
|
INFRA_REGISTRY,
|
|
@@ -29,10 +37,14 @@ from .loader import (
|
|
|
29
37
|
from .operations import (
|
|
30
38
|
add_infra,
|
|
31
39
|
add_service,
|
|
40
|
+
add_service_augment,
|
|
41
|
+
add_workspace_augment,
|
|
32
42
|
apply,
|
|
33
43
|
available_infra,
|
|
44
|
+
available_service_augments,
|
|
34
45
|
configured_infra,
|
|
35
46
|
remove_infra,
|
|
47
|
+
remove_service_augment,
|
|
36
48
|
validate,
|
|
37
49
|
)
|
|
38
50
|
from .presets import (
|
|
@@ -41,7 +53,7 @@ from .presets import (
|
|
|
41
53
|
members_for_category,
|
|
42
54
|
)
|
|
43
55
|
from .renderer import render_workspace
|
|
44
|
-
from .scaffolder import scaffold_service, scaffold_services
|
|
56
|
+
from .scaffolder import scaffold_augment, scaffold_service, scaffold_services
|
|
45
57
|
from .yaml_editor import (
|
|
46
58
|
deep_merge,
|
|
47
59
|
dumps_yaml,
|
|
@@ -52,6 +64,7 @@ from .yaml_editor import (
|
|
|
52
64
|
)
|
|
53
65
|
|
|
54
66
|
__all__ = [
|
|
67
|
+
"AUGMENT_REGISTRY",
|
|
55
68
|
"INFRA_ALL_TYPES",
|
|
56
69
|
"INFRA_CATEGORIES",
|
|
57
70
|
"INFRA_DATABASES",
|
|
@@ -59,15 +72,23 @@ __all__ = [
|
|
|
59
72
|
"INFRA_RENDERERS",
|
|
60
73
|
"PRESET_REGISTRY",
|
|
61
74
|
"SPEC_FILENAME",
|
|
75
|
+
"AugmentDescriptor",
|
|
62
76
|
"ComposeSpec",
|
|
63
77
|
"InfraDescriptor",
|
|
64
78
|
"InfraNode",
|
|
65
79
|
"PresetDefinition",
|
|
80
|
+
"ServiceAugment",
|
|
66
81
|
"ServiceNode",
|
|
82
|
+
"WorkspaceAugment",
|
|
67
83
|
"add_infra",
|
|
68
84
|
"add_service",
|
|
85
|
+
"add_service_augment",
|
|
86
|
+
"add_workspace_augment",
|
|
69
87
|
"apply",
|
|
88
|
+
"augment_for",
|
|
89
|
+
"augments_for_scope",
|
|
70
90
|
"available_infra",
|
|
91
|
+
"available_service_augments",
|
|
71
92
|
"configured_infra",
|
|
72
93
|
"deep_merge",
|
|
73
94
|
"default_spec",
|
|
@@ -81,10 +102,12 @@ __all__ = [
|
|
|
81
102
|
"load_yaml",
|
|
82
103
|
"members_for_category",
|
|
83
104
|
"remove_infra",
|
|
105
|
+
"remove_service_augment",
|
|
84
106
|
"render_infra",
|
|
85
107
|
"render_workspace",
|
|
86
108
|
"save_spec",
|
|
87
109
|
"save_yaml",
|
|
110
|
+
"scaffold_augment",
|
|
88
111
|
"scaffold_service",
|
|
89
112
|
"scaffold_services",
|
|
90
113
|
"set_key",
|
|
@@ -0,0 +1,166 @@
|
|
|
1
|
+
"""Augment descriptors for application-level service capabilities.
|
|
2
|
+
|
|
3
|
+
Each augment is described by an ``AugmentDescriptor`` dataclass that
|
|
4
|
+
encapsulates everything the renderer and scaffolder need: prerequisite
|
|
5
|
+
checks, compose wiring, package dependencies, settings fields, and
|
|
6
|
+
template references. Adding a new augment requires only adding a new
|
|
7
|
+
descriptor to ``AUGMENT_REGISTRY``.
|
|
8
|
+
|
|
9
|
+
Mirrors the ``InfraDescriptor`` pattern in ``infra.py``.
|
|
10
|
+
"""
|
|
11
|
+
|
|
12
|
+
from dataclasses import dataclass, field
|
|
13
|
+
from typing import Literal
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
@dataclass(frozen=True)
|
|
17
|
+
class AugmentDescriptor:
|
|
18
|
+
"""Complete description of an augment capability.
|
|
19
|
+
|
|
20
|
+
Parameters
|
|
21
|
+
----------
|
|
22
|
+
name
|
|
23
|
+
Unique augment identifier used in specs and registry lookups.
|
|
24
|
+
scope
|
|
25
|
+
``"workspace"`` for cluster-wide facts, ``"service"`` for
|
|
26
|
+
per-service capabilities.
|
|
27
|
+
description
|
|
28
|
+
Human-readable description shown in menus and docs.
|
|
29
|
+
requires_infra
|
|
30
|
+
Infra feature categories that must be present in the spec
|
|
31
|
+
(e.g. ``["database"]``).
|
|
32
|
+
requires_workspace_augment
|
|
33
|
+
A workspace-scope augment that must exist for this augment to
|
|
34
|
+
be valid (e.g. ``"jwt-auth-provider"``).
|
|
35
|
+
requires_service_in_spec
|
|
36
|
+
A service role that must exist in the spec.
|
|
37
|
+
compose_env
|
|
38
|
+
Static compose environment variables contributed by this augment.
|
|
39
|
+
compose_depends_on
|
|
40
|
+
Static compose ``depends_on`` entries.
|
|
41
|
+
packages
|
|
42
|
+
Python package names added to the service's ``requirements.txt``.
|
|
43
|
+
settings_fields
|
|
44
|
+
Static settings fields (name → default value). Dynamic fields
|
|
45
|
+
are resolved in the renderer.
|
|
46
|
+
template_dir
|
|
47
|
+
Relative name under ``v2/templates/augments/`` for scaffolded
|
|
48
|
+
files. ``None`` for workspace-scope augments that don't scaffold.
|
|
49
|
+
conflicts_with
|
|
50
|
+
Augment names that cannot coexist with this one.
|
|
51
|
+
needs_target_selection
|
|
52
|
+
Whether the wizard should prompt the user to pick target
|
|
53
|
+
services from the spec (e.g. delegate targets).
|
|
54
|
+
"""
|
|
55
|
+
|
|
56
|
+
name: str
|
|
57
|
+
scope: Literal["workspace", "service"]
|
|
58
|
+
description: str
|
|
59
|
+
|
|
60
|
+
# Prerequisites
|
|
61
|
+
requires_infra: list[str] = field(default_factory=list)
|
|
62
|
+
requires_workspace_augment: str | None = None
|
|
63
|
+
requires_service_in_spec: str | None = None
|
|
64
|
+
|
|
65
|
+
# What it contributes to compose (static values only)
|
|
66
|
+
compose_env: dict[str, str] = field(default_factory=dict)
|
|
67
|
+
compose_depends_on: list[str] = field(default_factory=list)
|
|
68
|
+
|
|
69
|
+
# What it contributes to scaffolded code
|
|
70
|
+
packages: list[str] = field(default_factory=list)
|
|
71
|
+
settings_fields: dict[str, str] = field(default_factory=dict)
|
|
72
|
+
template_dir: str | None = None
|
|
73
|
+
|
|
74
|
+
# Conflict prevention
|
|
75
|
+
conflicts_with: list[str] = field(default_factory=list)
|
|
76
|
+
|
|
77
|
+
# Does this augment resolve values dynamically from the spec?
|
|
78
|
+
needs_target_selection: bool = False
|
|
79
|
+
|
|
80
|
+
# For workspace-scope augments: scaffold template files into the
|
|
81
|
+
# service with this role. ``None`` means no scaffolding.
|
|
82
|
+
applies_to_role: str | None = None
|
|
83
|
+
|
|
84
|
+
|
|
85
|
+
# ---------------------------------------------------------------------------
|
|
86
|
+
# Registry
|
|
87
|
+
# ---------------------------------------------------------------------------
|
|
88
|
+
|
|
89
|
+
AUGMENT_REGISTRY: dict[str, AugmentDescriptor] = {
|
|
90
|
+
# ── Workspace-scope ────────────────────────────────────────────
|
|
91
|
+
"jwt-auth-provider": AugmentDescriptor(
|
|
92
|
+
name="jwt-auth-provider",
|
|
93
|
+
scope="workspace",
|
|
94
|
+
description="Cluster provides JWT authentication via an auth service",
|
|
95
|
+
requires_infra=["database"],
|
|
96
|
+
packages=[
|
|
97
|
+
"csrd-auth",
|
|
98
|
+
"csrd-models",
|
|
99
|
+
"csrd-repository",
|
|
100
|
+
"csrd-migration",
|
|
101
|
+
"PyJWT[crypto]",
|
|
102
|
+
],
|
|
103
|
+
template_dir="jwt-auth-provider",
|
|
104
|
+
applies_to_role="auth",
|
|
105
|
+
),
|
|
106
|
+
# ── Service-scope ──────────────────────────────────────────────
|
|
107
|
+
"db-config": AugmentDescriptor(
|
|
108
|
+
name="db-config",
|
|
109
|
+
scope="service",
|
|
110
|
+
description="Database repository with migrations and compose wiring",
|
|
111
|
+
requires_infra=["database"],
|
|
112
|
+
packages=["csrd-repository", "csrd-migration"],
|
|
113
|
+
# settings_fields resolved dynamically per database type in renderer
|
|
114
|
+
template_dir="db-config",
|
|
115
|
+
),
|
|
116
|
+
"delegate-config": AugmentDescriptor(
|
|
117
|
+
name="delegate-config",
|
|
118
|
+
scope="service",
|
|
119
|
+
description="Inter-service delegation with delegate classes",
|
|
120
|
+
packages=["csrd-delegate", "csrd-models"],
|
|
121
|
+
template_dir="delegate-config",
|
|
122
|
+
needs_target_selection=True,
|
|
123
|
+
# compose_env and settings_fields are dynamic per target
|
|
124
|
+
),
|
|
125
|
+
"jwt-auth-consumer": AugmentDescriptor(
|
|
126
|
+
name="jwt-auth-consumer",
|
|
127
|
+
scope="service",
|
|
128
|
+
description="JWT token validation using cluster auth service",
|
|
129
|
+
requires_workspace_augment="jwt-auth-provider",
|
|
130
|
+
packages=["csrd-auth", "csrd-models"],
|
|
131
|
+
template_dir="jwt-auth-consumer",
|
|
132
|
+
# compose_env, compose_depends_on, settings_fields resolved from spec
|
|
133
|
+
),
|
|
134
|
+
"context-logging": AugmentDescriptor(
|
|
135
|
+
name="context-logging",
|
|
136
|
+
scope="service",
|
|
137
|
+
description="Request context middleware and structured logging",
|
|
138
|
+
packages=["csrd-logging", "csrd-context"],
|
|
139
|
+
template_dir="context-logging",
|
|
140
|
+
),
|
|
141
|
+
"service-layer": AugmentDescriptor(
|
|
142
|
+
name="service-layer",
|
|
143
|
+
scope="service",
|
|
144
|
+
description="BaseService + ServiceError hierarchy + exception handler",
|
|
145
|
+
packages=["csrd-service", "csrd-models"],
|
|
146
|
+
template_dir="service-layer",
|
|
147
|
+
),
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
|
|
151
|
+
# ---------------------------------------------------------------------------
|
|
152
|
+
# Helpers
|
|
153
|
+
# ---------------------------------------------------------------------------
|
|
154
|
+
|
|
155
|
+
|
|
156
|
+
def augment_for(name: str) -> AugmentDescriptor:
|
|
157
|
+
"""Look up an augment descriptor by name.
|
|
158
|
+
|
|
159
|
+
Raises ``KeyError`` if the name is not registered.
|
|
160
|
+
"""
|
|
161
|
+
return AUGMENT_REGISTRY[name]
|
|
162
|
+
|
|
163
|
+
|
|
164
|
+
def augments_for_scope(scope: Literal["workspace", "service"]) -> list[AugmentDescriptor]:
|
|
165
|
+
"""Return all augment descriptors matching the given scope."""
|
|
166
|
+
return [d for d in AUGMENT_REGISTRY.values() if d.scope == scope]
|
|
@@ -0,0 +1,358 @@
|
|
|
1
|
+
"""Workspace mutation operations: add/remove services and infra.
|
|
2
|
+
|
|
3
|
+
These functions load, mutate, and persist the compose spec. They are
|
|
4
|
+
the canonical way to change a workspace's ``csrd-compose.yaml`` from
|
|
5
|
+
library code or CLI handlers.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
from pathlib import Path
|
|
9
|
+
|
|
10
|
+
from ..models import (
|
|
11
|
+
INFRA_ALL_TYPES,
|
|
12
|
+
INFRA_DATABASES,
|
|
13
|
+
ComposeSpec,
|
|
14
|
+
InfraNode,
|
|
15
|
+
ServiceAugment,
|
|
16
|
+
ServiceNode,
|
|
17
|
+
WorkspaceAugment,
|
|
18
|
+
)
|
|
19
|
+
from .augments import AUGMENT_REGISTRY, augment_for
|
|
20
|
+
from .infra import INFRA_REGISTRY
|
|
21
|
+
from .loader import load_spec, save_spec, spec_file_path
|
|
22
|
+
from .renderer import render_workspace
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
def apply(output_dir: Path, *, git_init: bool = False) -> Path:
|
|
26
|
+
"""Apply baseline empty-workspace rendering into *output_dir*."""
|
|
27
|
+
|
|
28
|
+
return render_workspace(output_dir, git_init=git_init)
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
def validate(output_dir: Path) -> ComposeSpec:
|
|
32
|
+
"""Validate and return the canonical compose spec for a workspace."""
|
|
33
|
+
|
|
34
|
+
return load_spec(spec_file_path(output_dir))
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
def add_service(output_dir: Path, service: ServiceNode) -> ComposeSpec:
|
|
38
|
+
"""Append a service to the workspace spec and persist to disk.
|
|
39
|
+
|
|
40
|
+
Raises ``ValueError`` if *service.name* already exists.
|
|
41
|
+
Returns the updated spec.
|
|
42
|
+
"""
|
|
43
|
+
|
|
44
|
+
spec_path = spec_file_path(output_dir)
|
|
45
|
+
spec = load_spec(spec_path)
|
|
46
|
+
|
|
47
|
+
existing_names = {s.name for s in spec.services}
|
|
48
|
+
if service.name in existing_names:
|
|
49
|
+
raise ValueError(f"Service '{service.name}' already exists in workspace spec")
|
|
50
|
+
|
|
51
|
+
spec.services.append(service)
|
|
52
|
+
save_spec(spec, spec_path)
|
|
53
|
+
return spec
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
def add_infra(output_dir: Path, infra: InfraNode) -> ComposeSpec:
|
|
57
|
+
"""Add or replace an infrastructure entry in the workspace spec.
|
|
58
|
+
|
|
59
|
+
Database types are mutually exclusive — adding one replaces any existing
|
|
60
|
+
database entry. Other infra types (redis, rabbitmq) are singletons that
|
|
61
|
+
cannot be duplicated.
|
|
62
|
+
|
|
63
|
+
Returns the updated spec.
|
|
64
|
+
"""
|
|
65
|
+
|
|
66
|
+
spec_path = spec_file_path(output_dir)
|
|
67
|
+
spec = load_spec(spec_path)
|
|
68
|
+
|
|
69
|
+
existing_types = {i.type for i in spec.infra}
|
|
70
|
+
|
|
71
|
+
if infra.type in INFRA_DATABASES:
|
|
72
|
+
# Remove any existing database entry (mutually exclusive)
|
|
73
|
+
spec.infra = [i for i in spec.infra if i.type not in INFRA_DATABASES]
|
|
74
|
+
elif infra.type in existing_types:
|
|
75
|
+
raise ValueError(f"Infra '{infra.type}' already exists in workspace spec")
|
|
76
|
+
|
|
77
|
+
spec.infra.append(infra)
|
|
78
|
+
save_spec(spec, spec_path)
|
|
79
|
+
return spec
|
|
80
|
+
|
|
81
|
+
|
|
82
|
+
def remove_infra(output_dir: Path, infra_type: str) -> ComposeSpec:
|
|
83
|
+
"""Remove an infrastructure entry from the workspace spec.
|
|
84
|
+
|
|
85
|
+
Raises ``ValueError`` if *infra_type* is not currently configured.
|
|
86
|
+
Returns the updated spec.
|
|
87
|
+
"""
|
|
88
|
+
|
|
89
|
+
spec_path = spec_file_path(output_dir)
|
|
90
|
+
spec = load_spec(spec_path)
|
|
91
|
+
|
|
92
|
+
existing_types = {i.type for i in spec.infra}
|
|
93
|
+
if infra_type not in existing_types:
|
|
94
|
+
raise ValueError(f"Infra '{infra_type}' is not configured in workspace spec")
|
|
95
|
+
|
|
96
|
+
spec.infra = [i for i in spec.infra if i.type != infra_type]
|
|
97
|
+
save_spec(spec, spec_path)
|
|
98
|
+
return spec
|
|
99
|
+
|
|
100
|
+
|
|
101
|
+
def available_infra(workspace_dir: Path) -> list[str]:
|
|
102
|
+
"""Return infra types not yet present in the workspace spec.
|
|
103
|
+
|
|
104
|
+
Database types are mutually exclusive — if any database is already
|
|
105
|
+
configured, all database options are excluded.
|
|
106
|
+
"""
|
|
107
|
+
|
|
108
|
+
spec_path = spec_file_path(workspace_dir)
|
|
109
|
+
if not spec_path.is_file():
|
|
110
|
+
return sorted(INFRA_ALL_TYPES)
|
|
111
|
+
|
|
112
|
+
spec = load_spec(spec_path)
|
|
113
|
+
existing = {i.type for i in spec.infra}
|
|
114
|
+
|
|
115
|
+
available: list[str] = []
|
|
116
|
+
has_db = bool(existing & INFRA_DATABASES)
|
|
117
|
+
for infra_type in sorted(INFRA_ALL_TYPES):
|
|
118
|
+
if infra_type in existing:
|
|
119
|
+
continue
|
|
120
|
+
if has_db and infra_type in INFRA_DATABASES:
|
|
121
|
+
continue
|
|
122
|
+
available.append(infra_type)
|
|
123
|
+
return available
|
|
124
|
+
|
|
125
|
+
|
|
126
|
+
def configured_infra(workspace_dir: Path) -> list[str]:
|
|
127
|
+
"""Return infra types currently configured in the workspace spec."""
|
|
128
|
+
|
|
129
|
+
spec_path = spec_file_path(workspace_dir)
|
|
130
|
+
if not spec_path.is_file():
|
|
131
|
+
return []
|
|
132
|
+
|
|
133
|
+
spec = load_spec(spec_path)
|
|
134
|
+
return sorted(i.type for i in spec.infra)
|
|
135
|
+
|
|
136
|
+
|
|
137
|
+
# ---------------------------------------------------------------------------
|
|
138
|
+
# Augment operations
|
|
139
|
+
# ---------------------------------------------------------------------------
|
|
140
|
+
|
|
141
|
+
|
|
142
|
+
def _infra_features_in_spec(spec: ComposeSpec) -> set[str]:
|
|
143
|
+
"""Return the set of infra feature categories present in the spec."""
|
|
144
|
+
features: set[str] = set()
|
|
145
|
+
for infra_node in spec.infra:
|
|
146
|
+
desc = INFRA_REGISTRY.get(infra_node.type)
|
|
147
|
+
if desc is not None:
|
|
148
|
+
features.add(desc.feature)
|
|
149
|
+
return features
|
|
150
|
+
|
|
151
|
+
|
|
152
|
+
def _validate_augment_prerequisites(
|
|
153
|
+
augment_name: str,
|
|
154
|
+
spec: ComposeSpec,
|
|
155
|
+
*,
|
|
156
|
+
service_name: str | None = None,
|
|
157
|
+
) -> None:
|
|
158
|
+
"""Validate that all prerequisites for an augment are met.
|
|
159
|
+
|
|
160
|
+
Raises ``ValueError`` with a descriptive message on failure.
|
|
161
|
+
"""
|
|
162
|
+
desc = augment_for(augment_name)
|
|
163
|
+
|
|
164
|
+
# Check required infra features
|
|
165
|
+
if desc.requires_infra:
|
|
166
|
+
spec_features = _infra_features_in_spec(spec)
|
|
167
|
+
missing = set(desc.requires_infra) - spec_features
|
|
168
|
+
if missing:
|
|
169
|
+
raise ValueError(
|
|
170
|
+
f"Augment '{augment_name}' requires infra features "
|
|
171
|
+
f"{sorted(missing)}, but they are not configured. "
|
|
172
|
+
f"Add the required infra first."
|
|
173
|
+
)
|
|
174
|
+
|
|
175
|
+
# Check required workspace augment
|
|
176
|
+
if desc.requires_workspace_augment:
|
|
177
|
+
ws_augment_names = {a.name for a in spec.workspace.augments}
|
|
178
|
+
if desc.requires_workspace_augment not in ws_augment_names:
|
|
179
|
+
raise ValueError(
|
|
180
|
+
f"Augment '{augment_name}' requires workspace augment "
|
|
181
|
+
f"'{desc.requires_workspace_augment}', but it is not present. "
|
|
182
|
+
f"Add the workspace augment first."
|
|
183
|
+
)
|
|
184
|
+
|
|
185
|
+
# Check required service role in spec
|
|
186
|
+
if desc.requires_service_in_spec:
|
|
187
|
+
roles = {s.role for s in spec.services}
|
|
188
|
+
if desc.requires_service_in_spec not in roles:
|
|
189
|
+
raise ValueError(
|
|
190
|
+
f"Augment '{augment_name}' requires a service with role "
|
|
191
|
+
f"'{desc.requires_service_in_spec}' in the spec."
|
|
192
|
+
)
|
|
193
|
+
|
|
194
|
+
# Check conflicts (only relevant for service-scope augments)
|
|
195
|
+
if service_name is not None and desc.conflicts_with:
|
|
196
|
+
svc = next((s for s in spec.services if s.name == service_name), None)
|
|
197
|
+
if svc is not None:
|
|
198
|
+
existing = {a.name for a in svc.augments}
|
|
199
|
+
conflicts = set(desc.conflicts_with) & existing
|
|
200
|
+
if conflicts:
|
|
201
|
+
raise ValueError(
|
|
202
|
+
f"Augment '{augment_name}' conflicts with "
|
|
203
|
+
f"{sorted(conflicts)} on service '{service_name}'."
|
|
204
|
+
)
|
|
205
|
+
|
|
206
|
+
|
|
207
|
+
def add_workspace_augment(
|
|
208
|
+
output_dir: Path,
|
|
209
|
+
augment_name: str,
|
|
210
|
+
options: dict[str, str | list[str]] | None = None,
|
|
211
|
+
) -> ComposeSpec:
|
|
212
|
+
"""Add a workspace-scoped augment to the spec.
|
|
213
|
+
|
|
214
|
+
Validates that the augment exists in the registry and has scope
|
|
215
|
+
``"workspace"``. Raises ``ValueError`` on validation failure.
|
|
216
|
+
Returns the updated spec.
|
|
217
|
+
"""
|
|
218
|
+
spec_path = spec_file_path(output_dir)
|
|
219
|
+
spec = load_spec(spec_path)
|
|
220
|
+
|
|
221
|
+
# Validate augment exists and has correct scope
|
|
222
|
+
try:
|
|
223
|
+
desc = augment_for(augment_name)
|
|
224
|
+
except KeyError:
|
|
225
|
+
raise ValueError(f"Unknown augment '{augment_name}'") from None
|
|
226
|
+
|
|
227
|
+
if desc.scope != "workspace":
|
|
228
|
+
raise ValueError(
|
|
229
|
+
f"Augment '{augment_name}' has scope '{desc.scope}', "
|
|
230
|
+
f"not 'workspace'. Use add_service_augment() instead."
|
|
231
|
+
)
|
|
232
|
+
|
|
233
|
+
# Check for duplicates
|
|
234
|
+
existing = {a.name for a in spec.workspace.augments}
|
|
235
|
+
if augment_name in existing:
|
|
236
|
+
raise ValueError(f"Workspace augment '{augment_name}' is already present.")
|
|
237
|
+
|
|
238
|
+
_validate_augment_prerequisites(augment_name, spec)
|
|
239
|
+
|
|
240
|
+
spec.workspace.augments.append(WorkspaceAugment(name=augment_name, options=options or {}))
|
|
241
|
+
save_spec(spec, spec_path)
|
|
242
|
+
return spec
|
|
243
|
+
|
|
244
|
+
|
|
245
|
+
def add_service_augment(
|
|
246
|
+
output_dir: Path,
|
|
247
|
+
service_name: str,
|
|
248
|
+
augment_name: str,
|
|
249
|
+
options: dict[str, str | list[str]] | None = None,
|
|
250
|
+
) -> ComposeSpec:
|
|
251
|
+
"""Add a service-scoped augment to a specific service in the spec.
|
|
252
|
+
|
|
253
|
+
Validates prerequisites, scope, duplicates, and conflicts.
|
|
254
|
+
Raises ``ValueError`` on validation failure.
|
|
255
|
+
Returns the updated spec.
|
|
256
|
+
"""
|
|
257
|
+
spec_path = spec_file_path(output_dir)
|
|
258
|
+
spec = load_spec(spec_path)
|
|
259
|
+
|
|
260
|
+
# Validate augment exists and has correct scope
|
|
261
|
+
try:
|
|
262
|
+
desc = augment_for(augment_name)
|
|
263
|
+
except KeyError:
|
|
264
|
+
raise ValueError(f"Unknown augment '{augment_name}'") from None
|
|
265
|
+
|
|
266
|
+
if desc.scope != "service":
|
|
267
|
+
raise ValueError(
|
|
268
|
+
f"Augment '{augment_name}' has scope '{desc.scope}', "
|
|
269
|
+
f"not 'service'. Use add_workspace_augment() instead."
|
|
270
|
+
)
|
|
271
|
+
|
|
272
|
+
# Find the target service
|
|
273
|
+
svc = next((s for s in spec.services if s.name == service_name), None)
|
|
274
|
+
if svc is None:
|
|
275
|
+
raise ValueError(f"Service '{service_name}' not found in workspace spec.")
|
|
276
|
+
|
|
277
|
+
# Check for duplicates
|
|
278
|
+
existing = {a.name for a in svc.augments}
|
|
279
|
+
if augment_name in existing:
|
|
280
|
+
raise ValueError(
|
|
281
|
+
f"Augment '{augment_name}' is already present on service '{service_name}'."
|
|
282
|
+
)
|
|
283
|
+
|
|
284
|
+
# Validate target selection requirement
|
|
285
|
+
if desc.needs_target_selection:
|
|
286
|
+
targets = (options or {}).get("targets", [])
|
|
287
|
+
if not targets:
|
|
288
|
+
raise ValueError(
|
|
289
|
+
f"Augment '{augment_name}' requires target selection. "
|
|
290
|
+
f'Pass options={{"targets": [...]}}.'
|
|
291
|
+
)
|
|
292
|
+
|
|
293
|
+
_validate_augment_prerequisites(augment_name, spec, service_name=service_name)
|
|
294
|
+
|
|
295
|
+
svc.augments.append(ServiceAugment(name=augment_name, options=options or {}))
|
|
296
|
+
save_spec(spec, spec_path)
|
|
297
|
+
return spec
|
|
298
|
+
|
|
299
|
+
|
|
300
|
+
def remove_service_augment(
|
|
301
|
+
output_dir: Path,
|
|
302
|
+
service_name: str,
|
|
303
|
+
augment_name: str,
|
|
304
|
+
) -> ComposeSpec:
|
|
305
|
+
"""Remove a service-scoped augment from a service.
|
|
306
|
+
|
|
307
|
+
Only updates the spec — scaffolded files are NOT removed (they may
|
|
308
|
+
contain user edits). Returns the updated spec.
|
|
309
|
+
Raises ``ValueError`` if the augment is not present.
|
|
310
|
+
"""
|
|
311
|
+
spec_path = spec_file_path(output_dir)
|
|
312
|
+
spec = load_spec(spec_path)
|
|
313
|
+
|
|
314
|
+
svc = next((s for s in spec.services if s.name == service_name), None)
|
|
315
|
+
if svc is None:
|
|
316
|
+
raise ValueError(f"Service '{service_name}' not found in workspace spec.")
|
|
317
|
+
|
|
318
|
+
original_count = len(svc.augments)
|
|
319
|
+
svc.augments = [a for a in svc.augments if a.name != augment_name]
|
|
320
|
+
|
|
321
|
+
if len(svc.augments) == original_count:
|
|
322
|
+
raise ValueError(f"Augment '{augment_name}' is not present on service '{service_name}'.")
|
|
323
|
+
|
|
324
|
+
save_spec(spec, spec_path)
|
|
325
|
+
return spec
|
|
326
|
+
|
|
327
|
+
|
|
328
|
+
def available_service_augments(
|
|
329
|
+
output_dir: Path,
|
|
330
|
+
service_name: str,
|
|
331
|
+
) -> list[str]:
|
|
332
|
+
"""Return augment names that can be added to a service.
|
|
333
|
+
|
|
334
|
+
Filters out augments already present, those with unmet prerequisites,
|
|
335
|
+
and those with conflicts.
|
|
336
|
+
"""
|
|
337
|
+
spec_path = spec_file_path(output_dir)
|
|
338
|
+
spec = load_spec(spec_path)
|
|
339
|
+
|
|
340
|
+
svc = next((s for s in spec.services if s.name == service_name), None)
|
|
341
|
+
if svc is None:
|
|
342
|
+
return []
|
|
343
|
+
|
|
344
|
+
existing = {a.name for a in svc.augments}
|
|
345
|
+
available: list[str] = []
|
|
346
|
+
|
|
347
|
+
for name, desc in sorted(AUGMENT_REGISTRY.items()):
|
|
348
|
+
if desc.scope != "service":
|
|
349
|
+
continue
|
|
350
|
+
if name in existing:
|
|
351
|
+
continue
|
|
352
|
+
try:
|
|
353
|
+
_validate_augment_prerequisites(name, spec, service_name=service_name)
|
|
354
|
+
except ValueError:
|
|
355
|
+
continue
|
|
356
|
+
available.append(name)
|
|
357
|
+
|
|
358
|
+
return available
|