csrd-utils 0.3.81__tar.gz → 0.3.85__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.81 → csrd_utils-0.3.85}/PKG-INFO +3 -1
- {csrd_utils-0.3.81 → csrd_utils-0.3.85}/pyproject.toml +3 -1
- {csrd_utils-0.3.81 → csrd_utils-0.3.85}/src/csrd_utils/__main__.py +192 -0
- csrd_utils-0.3.85/src/csrd_utils/v2/__init__.py +1 -0
- csrd_utils-0.3.85/src/csrd_utils/v2/compose/__init__.py +180 -0
- csrd_utils-0.3.85/src/csrd_utils/v2/compose/git.py +17 -0
- csrd_utils-0.3.85/src/csrd_utils/v2/compose/infra.py +178 -0
- csrd_utils-0.3.85/src/csrd_utils/v2/compose/loader.py +38 -0
- csrd_utils-0.3.85/src/csrd_utils/v2/compose/presets.py +58 -0
- csrd_utils-0.3.85/src/csrd_utils/v2/compose/renderer.py +226 -0
- csrd_utils-0.3.85/src/csrd_utils/v2/compose/spec.py +15 -0
- csrd_utils-0.3.85/src/csrd_utils/v2/compose/yaml_editor.py +134 -0
- csrd_utils-0.3.85/src/csrd_utils/v2/generate/AGENTS.md +180 -0
- csrd_utils-0.3.85/src/csrd_utils/v2/generate/__init__.py +5 -0
- csrd_utils-0.3.85/src/csrd_utils/v2/generate/handlers.py +694 -0
- csrd_utils-0.3.85/src/csrd_utils/v2/generate/helpers.py +569 -0
- csrd_utils-0.3.85/src/csrd_utils/v2/generate/menu.py +170 -0
- csrd_utils-0.3.85/src/csrd_utils/v2/models/__init__.py +39 -0
- csrd_utils-0.3.85/src/csrd_utils/v2/models/base.py +10 -0
- csrd_utils-0.3.85/src/csrd_utils/v2/models/spec.py +103 -0
- csrd_utils-0.3.85/src/csrd_utils/v2/models/types.py +12 -0
- {csrd_utils-0.3.81 → csrd_utils-0.3.85}/.gitignore +0 -0
- {csrd_utils-0.3.81 → csrd_utils-0.3.85}/README.md +0 -0
- {csrd_utils-0.3.81 → csrd_utils-0.3.85}/src/csrd_utils/__init__.py +0 -0
- {csrd_utils-0.3.81 → csrd_utils-0.3.85}/src/csrd_utils/audit.py +0 -0
- {csrd_utils-0.3.81 → csrd_utils-0.3.85}/src/csrd_utils/augmentor.py +0 -0
- {csrd_utils-0.3.81 → csrd_utils-0.3.85}/src/csrd_utils/cluster.py +0 -0
- {csrd_utils-0.3.81 → csrd_utils-0.3.85}/src/csrd_utils/doctor.py +0 -0
- {csrd_utils-0.3.81 → csrd_utils-0.3.85}/src/csrd_utils/features/caching/conftest.fragment.py +0 -0
- {csrd_utils-0.3.81 → csrd_utils-0.3.85}/src/csrd_utils/features/caching/docker-compose.fragment.yaml +0 -0
- {csrd_utils-0.3.81 → csrd_utils-0.3.85}/src/csrd_utils/features/caching/manifest.yaml +0 -0
- {csrd_utils-0.3.81 → csrd_utils-0.3.85}/src/csrd_utils/features/caching/requirements.fragment +0 -0
- {csrd_utils-0.3.81 → csrd_utils-0.3.85}/src/csrd_utils/features/caching/settings.fragment.py +0 -0
- {csrd_utils-0.3.81 → csrd_utils-0.3.85}/src/csrd_utils/features/caching/src/app/cache.py +0 -0
- {csrd_utils-0.3.81 → csrd_utils-0.3.85}/src/csrd_utils/features/caching/tests/test_cache.py +0 -0
- {csrd_utils-0.3.81 → csrd_utils-0.3.85}/src/csrd_utils/features/logging/conftest.fragment.py +0 -0
- {csrd_utils-0.3.81 → csrd_utils-0.3.85}/src/csrd_utils/features/logging/manifest.yaml +0 -0
- {csrd_utils-0.3.81 → csrd_utils-0.3.85}/src/csrd_utils/features/logging/requirements.fragment +0 -0
- {csrd_utils-0.3.81 → csrd_utils-0.3.85}/src/csrd_utils/features/logging/settings.fragment.py +0 -0
- {csrd_utils-0.3.81 → csrd_utils-0.3.85}/src/csrd_utils/features/logging/src/app/middleware/logging.py +0 -0
- {csrd_utils-0.3.81 → csrd_utils-0.3.85}/src/csrd_utils/features/logging/tests/test_logging_middleware.py +0 -0
- {csrd_utils-0.3.81 → csrd_utils-0.3.85}/src/csrd_utils/features/metrics/conftest.fragment.py +0 -0
- {csrd_utils-0.3.81 → csrd_utils-0.3.85}/src/csrd_utils/features/metrics/docker-compose.fragment.yaml +0 -0
- {csrd_utils-0.3.81 → csrd_utils-0.3.85}/src/csrd_utils/features/metrics/manifest.yaml +0 -0
- {csrd_utils-0.3.81 → csrd_utils-0.3.85}/src/csrd_utils/features/metrics/requirements.fragment +0 -0
- {csrd_utils-0.3.81 → csrd_utils-0.3.85}/src/csrd_utils/features/metrics/settings.fragment.py +0 -0
- {csrd_utils-0.3.81 → csrd_utils-0.3.85}/src/csrd_utils/features/metrics/src/app/middleware/metrics.py +0 -0
- {csrd_utils-0.3.81 → csrd_utils-0.3.85}/src/csrd_utils/features/metrics/tests/test_metrics_middleware.py +0 -0
- {csrd_utils-0.3.81 → csrd_utils-0.3.85}/src/csrd_utils/features/tracing/conftest.fragment.py +0 -0
- {csrd_utils-0.3.81 → csrd_utils-0.3.85}/src/csrd_utils/features/tracing/manifest.yaml +0 -0
- {csrd_utils-0.3.81 → csrd_utils-0.3.85}/src/csrd_utils/features/tracing/requirements.fragment +0 -0
- {csrd_utils-0.3.81 → csrd_utils-0.3.85}/src/csrd_utils/features/tracing/settings.fragment.py +0 -0
- {csrd_utils-0.3.81 → csrd_utils-0.3.85}/src/csrd_utils/features/tracing/src/app/middleware/tracing.py +0 -0
- {csrd_utils-0.3.81 → csrd_utils-0.3.85}/src/csrd_utils/features/tracing/tests/test_tracing_middleware.py +0 -0
- {csrd_utils-0.3.81 → csrd_utils-0.3.85}/src/csrd_utils/features/workers/conftest.fragment.py +0 -0
- {csrd_utils-0.3.81 → csrd_utils-0.3.85}/src/csrd_utils/features/workers/docker-compose.fragment.yaml +0 -0
- {csrd_utils-0.3.81 → csrd_utils-0.3.85}/src/csrd_utils/features/workers/manifest.yaml +0 -0
- {csrd_utils-0.3.81 → csrd_utils-0.3.85}/src/csrd_utils/features/workers/requirements.fragment +0 -0
- {csrd_utils-0.3.81 → csrd_utils-0.3.85}/src/csrd_utils/features/workers/settings.fragment.py +0 -0
- {csrd_utils-0.3.81 → csrd_utils-0.3.85}/src/csrd_utils/features/workers/src/app/workers/__init__.py +0 -0
- {csrd_utils-0.3.81 → csrd_utils-0.3.85}/src/csrd_utils/features/workers/src/app/workers/celery_app.py +0 -0
- {csrd_utils-0.3.81 → csrd_utils-0.3.85}/src/csrd_utils/features/workers/src/app/workers/tasks.py +0 -0
- {csrd_utils-0.3.81 → csrd_utils-0.3.85}/src/csrd_utils/features/workers/tests/test_workers.py +0 -0
- {csrd_utils-0.3.81 → csrd_utils-0.3.85}/src/csrd_utils/fragments.py +0 -0
- {csrd_utils-0.3.81 → csrd_utils-0.3.85}/src/csrd_utils/generator.py +0 -0
- {csrd_utils-0.3.81 → csrd_utils-0.3.85}/src/csrd_utils/resources.py +0 -0
- {csrd_utils-0.3.81 → csrd_utils-0.3.85}/src/csrd_utils/templates/cookiecutter-service/README.md +0 -0
- {csrd_utils-0.3.81 → csrd_utils-0.3.85}/src/csrd_utils/templates/cookiecutter-service/cookiecutter.json +0 -0
- {csrd_utils-0.3.81 → csrd_utils-0.3.85}/src/csrd_utils/templates/cookiecutter-service/hooks/post_gen_project.py +0 -0
- {csrd_utils-0.3.81 → csrd_utils-0.3.85}/src/csrd_utils/templates/cookiecutter-service/{{cookiecutter.__service_name_snake}}/.cookiecutterignore +0 -0
- {csrd_utils-0.3.81 → csrd_utils-0.3.85}/src/csrd_utils/templates/cookiecutter-service/{{cookiecutter.__service_name_snake}}/.dockerignore +0 -0
- {csrd_utils-0.3.81 → csrd_utils-0.3.85}/src/csrd_utils/templates/cookiecutter-service/{{cookiecutter.__service_name_snake}}/Dockerfile +0 -0
- {csrd_utils-0.3.81 → csrd_utils-0.3.85}/src/csrd_utils/templates/cookiecutter-service/{{cookiecutter.__service_name_snake}}/README.md +0 -0
- {csrd_utils-0.3.81 → csrd_utils-0.3.85}/src/csrd_utils/templates/cookiecutter-service/{{cookiecutter.__service_name_snake}}/__init__.py +0 -0
- {csrd_utils-0.3.81 → csrd_utils-0.3.85}/src/csrd_utils/templates/cookiecutter-service/{{cookiecutter.__service_name_snake}}/cache.py +0 -0
- {csrd_utils-0.3.81 → csrd_utils-0.3.85}/src/csrd_utils/templates/cookiecutter-service/{{cookiecutter.__service_name_snake}}/delegates/__init__.py +0 -0
- {csrd_utils-0.3.81 → csrd_utils-0.3.85}/src/csrd_utils/templates/cookiecutter-service/{{cookiecutter.__service_name_snake}}/delegates/upstreams.py +0 -0
- {csrd_utils-0.3.81 → csrd_utils-0.3.85}/src/csrd_utils/templates/cookiecutter-service/{{cookiecutter.__service_name_snake}}/dependencies/__init__.py +0 -0
- {csrd_utils-0.3.81 → csrd_utils-0.3.85}/src/csrd_utils/templates/cookiecutter-service/{{cookiecutter.__service_name_snake}}/dependencies/auth_service.py +0 -0
- {csrd_utils-0.3.81 → csrd_utils-0.3.85}/src/csrd_utils/templates/cookiecutter-service/{{cookiecutter.__service_name_snake}}/dependencies/delegates.py +0 -0
- {csrd_utils-0.3.81 → csrd_utils-0.3.85}/src/csrd_utils/templates/cookiecutter-service/{{cookiecutter.__service_name_snake}}/dependencies/item_repository.py +0 -0
- {csrd_utils-0.3.81 → csrd_utils-0.3.85}/src/csrd_utils/templates/cookiecutter-service/{{cookiecutter.__service_name_snake}}/dependencies/worker_broker.py +0 -0
- {csrd_utils-0.3.81 → csrd_utils-0.3.85}/src/csrd_utils/templates/cookiecutter-service/{{cookiecutter.__service_name_snake}}/docker-compose.yml +0 -0
- {csrd_utils-0.3.81 → csrd_utils-0.3.85}/src/csrd_utils/templates/cookiecutter-service/{{cookiecutter.__service_name_snake}}/middleware/__init__.py +0 -0
- {csrd_utils-0.3.81 → csrd_utils-0.3.85}/src/csrd_utils/templates/cookiecutter-service/{{cookiecutter.__service_name_snake}}/middleware/logging.py +0 -0
- {csrd_utils-0.3.81 → csrd_utils-0.3.85}/src/csrd_utils/templates/cookiecutter-service/{{cookiecutter.__service_name_snake}}/repositories/__init__.py +0 -0
- {csrd_utils-0.3.81 → csrd_utils-0.3.85}/src/csrd_utils/templates/cookiecutter-service/{{cookiecutter.__service_name_snake}}/repositories/item_repository.py +0 -0
- {csrd_utils-0.3.81 → csrd_utils-0.3.85}/src/csrd_utils/templates/cookiecutter-service/{{cookiecutter.__service_name_snake}}/requirements.txt +0 -0
- {csrd_utils-0.3.81 → csrd_utils-0.3.85}/src/csrd_utils/templates/cookiecutter-service/{{cookiecutter.__service_name_snake}}/settings.py +0 -0
- {csrd_utils-0.3.81 → csrd_utils-0.3.85}/src/csrd_utils/templates/cookiecutter-service/{{cookiecutter.__service_name_snake}}/tests/__init__.py +0 -0
- {csrd_utils-0.3.81 → csrd_utils-0.3.85}/src/csrd_utils/templates/cookiecutter-service/{{cookiecutter.__service_name_snake}}/tests/acceptance/__init__.py +0 -0
- {csrd_utils-0.3.81 → csrd_utils-0.3.85}/src/csrd_utils/templates/cookiecutter-service/{{cookiecutter.__service_name_snake}}/tests/acceptance/test_logging_middleware.py +0 -0
- {csrd_utils-0.3.81 → csrd_utils-0.3.85}/src/csrd_utils/templates/cookiecutter-service/{{cookiecutter.__service_name_snake}}/tests/conftest.py +0 -0
- {csrd_utils-0.3.81 → csrd_utils-0.3.85}/src/csrd_utils/templates/cookiecutter-service/{{cookiecutter.__service_name_snake}}/tests/test_items.py +0 -0
- {csrd_utils-0.3.81 → csrd_utils-0.3.85}/src/csrd_utils/templates/cookiecutter-service/{{cookiecutter.__service_name_snake}}/tests/unit/__init__.py +0 -0
- {csrd_utils-0.3.81 → csrd_utils-0.3.85}/src/csrd_utils/templates/cookiecutter-service/{{cookiecutter.__service_name_snake}}/tests/unit/test_cache.py +0 -0
- {csrd_utils-0.3.81 → csrd_utils-0.3.85}/src/csrd_utils/templates/cookiecutter-service/{{cookiecutter.__service_name_snake}}/tests/unit/test_database_settings.py +0 -0
- {csrd_utils-0.3.81 → csrd_utils-0.3.85}/src/csrd_utils/templates/cookiecutter-service/{{cookiecutter.__service_name_snake}}/tests/unit/test_delegates.py +0 -0
- {csrd_utils-0.3.81 → csrd_utils-0.3.85}/src/csrd_utils/templates/cookiecutter-service/{{cookiecutter.__service_name_snake}}/tests/unit/test_settings_contracts.py +0 -0
- {csrd_utils-0.3.81 → csrd_utils-0.3.85}/src/csrd_utils/templates/cookiecutter-service/{{cookiecutter.__service_name_snake}}/tests/unit/test_worker_broker.py +0 -0
- {csrd_utils-0.3.81 → csrd_utils-0.3.85}/src/csrd_utils/templates/cookiecutter-service/{{cookiecutter.__service_name_snake}}/views/__init__.py +0 -0
- {csrd_utils-0.3.81 → csrd_utils-0.3.85}/src/csrd_utils/templates/cookiecutter-service/{{cookiecutter.__service_name_snake}}/views/unversioned/__init__.py +0 -0
- {csrd_utils-0.3.81 → csrd_utils-0.3.85}/src/csrd_utils/templates/cookiecutter-service/{{cookiecutter.__service_name_snake}}/views/unversioned/health_view.py +0 -0
- {csrd_utils-0.3.81 → csrd_utils-0.3.85}/src/csrd_utils/templates/cookiecutter-service/{{cookiecutter.__service_name_snake}}/views/unversioned/items_view.py +0 -0
- {csrd_utils-0.3.81 → csrd_utils-0.3.85}/src/csrd_utils/templates/cookiecutter-service/{{cookiecutter.__service_name_snake}}/workers/__init__.py +0 -0
- {csrd_utils-0.3.81 → csrd_utils-0.3.85}/src/csrd_utils/templates/cookiecutter-service/{{cookiecutter.__service_name_snake}}/workers/tasks.py +0 -0
- {csrd_utils-0.3.81 → csrd_utils-0.3.85}/src/csrd_utils/templates/platform-app-service/cookiecutter.json +0 -0
- {csrd_utils-0.3.81 → csrd_utils-0.3.85}/src/csrd_utils/templates/platform-app-service/{{cookiecutter.__service_name_snake}}/.dockerignore +0 -0
- {csrd_utils-0.3.81 → csrd_utils-0.3.85}/src/csrd_utils/templates/platform-app-service/{{cookiecutter.__service_name_snake}}/Dockerfile +0 -0
- {csrd_utils-0.3.81 → csrd_utils-0.3.85}/src/csrd_utils/templates/platform-app-service/{{cookiecutter.__service_name_snake}}/README.md +0 -0
- {csrd_utils-0.3.81 → csrd_utils-0.3.85}/src/csrd_utils/templates/platform-app-service/{{cookiecutter.__service_name_snake}}/__init__.py +0 -0
- {csrd_utils-0.3.81 → csrd_utils-0.3.85}/src/csrd_utils/templates/platform-app-service/{{cookiecutter.__service_name_snake}}/delegates/__init__.py +0 -0
- {csrd_utils-0.3.81 → csrd_utils-0.3.85}/src/csrd_utils/templates/platform-app-service/{{cookiecutter.__service_name_snake}}/delegates/auth_delegate.py +0 -0
- {csrd_utils-0.3.81 → csrd_utils-0.3.85}/src/csrd_utils/templates/platform-app-service/{{cookiecutter.__service_name_snake}}/delegates/identity_delegate.py +0 -0
- {csrd_utils-0.3.81 → csrd_utils-0.3.85}/src/csrd_utils/templates/platform-app-service/{{cookiecutter.__service_name_snake}}/dependencies/__init__.py +0 -0
- {csrd_utils-0.3.81 → csrd_utils-0.3.85}/src/csrd_utils/templates/platform-app-service/{{cookiecutter.__service_name_snake}}/dependencies/auth_passthrough.py +0 -0
- {csrd_utils-0.3.81 → csrd_utils-0.3.85}/src/csrd_utils/templates/platform-app-service/{{cookiecutter.__service_name_snake}}/dependencies/auth_service.py +0 -0
- {csrd_utils-0.3.81 → csrd_utils-0.3.85}/src/csrd_utils/templates/platform-app-service/{{cookiecutter.__service_name_snake}}/dependencies/item_repository.py +0 -0
- {csrd_utils-0.3.81 → csrd_utils-0.3.85}/src/csrd_utils/templates/platform-app-service/{{cookiecutter.__service_name_snake}}/models/__init__.py +0 -0
- {csrd_utils-0.3.81 → csrd_utils-0.3.85}/src/csrd_utils/templates/platform-app-service/{{cookiecutter.__service_name_snake}}/models/auth_service.py +0 -0
- {csrd_utils-0.3.81 → csrd_utils-0.3.85}/src/csrd_utils/templates/platform-app-service/{{cookiecutter.__service_name_snake}}/models/items.py +0 -0
- {csrd_utils-0.3.81 → csrd_utils-0.3.85}/src/csrd_utils/templates/platform-app-service/{{cookiecutter.__service_name_snake}}/repositories/__init__.py +0 -0
- {csrd_utils-0.3.81 → csrd_utils-0.3.85}/src/csrd_utils/templates/platform-app-service/{{cookiecutter.__service_name_snake}}/repositories/item_repository.py +0 -0
- {csrd_utils-0.3.81 → csrd_utils-0.3.85}/src/csrd_utils/templates/platform-app-service/{{cookiecutter.__service_name_snake}}/requirements.txt +0 -0
- {csrd_utils-0.3.81 → csrd_utils-0.3.85}/src/csrd_utils/templates/platform-app-service/{{cookiecutter.__service_name_snake}}/settings.py +0 -0
- {csrd_utils-0.3.81 → csrd_utils-0.3.85}/src/csrd_utils/templates/platform-app-service/{{cookiecutter.__service_name_snake}}/views/__init__.py +0 -0
- {csrd_utils-0.3.81 → csrd_utils-0.3.85}/src/csrd_utils/templates/platform-app-service/{{cookiecutter.__service_name_snake}}/views/unversioned/__init__.py +0 -0
- {csrd_utils-0.3.81 → csrd_utils-0.3.85}/src/csrd_utils/templates/platform-app-service/{{cookiecutter.__service_name_snake}}/views/unversioned/auth_service_view.py +0 -0
- {csrd_utils-0.3.81 → csrd_utils-0.3.85}/src/csrd_utils/templates/platform-app-service/{{cookiecutter.__service_name_snake}}/views/unversioned/health_view.py +0 -0
- {csrd_utils-0.3.81 → csrd_utils-0.3.85}/src/csrd_utils/templates/platform-app-service/{{cookiecutter.__service_name_snake}}/views/unversioned/items_view.py +0 -0
- {csrd_utils-0.3.81 → csrd_utils-0.3.85}/src/csrd_utils/templates/platform-auth-service/cookiecutter.json +0 -0
- {csrd_utils-0.3.81 → csrd_utils-0.3.85}/src/csrd_utils/templates/platform-auth-service/{{cookiecutter.__service_name_snake}}/.dockerignore +0 -0
- {csrd_utils-0.3.81 → csrd_utils-0.3.85}/src/csrd_utils/templates/platform-auth-service/{{cookiecutter.__service_name_snake}}/Dockerfile +0 -0
- {csrd_utils-0.3.81 → csrd_utils-0.3.85}/src/csrd_utils/templates/platform-auth-service/{{cookiecutter.__service_name_snake}}/README.md +0 -0
- {csrd_utils-0.3.81 → csrd_utils-0.3.85}/src/csrd_utils/templates/platform-auth-service/{{cookiecutter.__service_name_snake}}/__init__.py +0 -0
- {csrd_utils-0.3.81 → csrd_utils-0.3.85}/src/csrd_utils/templates/platform-auth-service/{{cookiecutter.__service_name_snake}}/dependencies/__init__.py +0 -0
- {csrd_utils-0.3.81 → csrd_utils-0.3.85}/src/csrd_utils/templates/platform-auth-service/{{cookiecutter.__service_name_snake}}/models/__init__.py +0 -0
- {csrd_utils-0.3.81 → csrd_utils-0.3.85}/src/csrd_utils/templates/platform-auth-service/{{cookiecutter.__service_name_snake}}/models/auth.py +0 -0
- {csrd_utils-0.3.81 → csrd_utils-0.3.85}/src/csrd_utils/templates/platform-auth-service/{{cookiecutter.__service_name_snake}}/repositories/__init__.py +0 -0
- {csrd_utils-0.3.81 → csrd_utils-0.3.85}/src/csrd_utils/templates/platform-auth-service/{{cookiecutter.__service_name_snake}}/repositories/token_repository.py +0 -0
- {csrd_utils-0.3.81 → csrd_utils-0.3.85}/src/csrd_utils/templates/platform-auth-service/{{cookiecutter.__service_name_snake}}/requirements.txt +0 -0
- {csrd_utils-0.3.81 → csrd_utils-0.3.85}/src/csrd_utils/templates/platform-auth-service/{{cookiecutter.__service_name_snake}}/settings.py +0 -0
- {csrd_utils-0.3.81 → csrd_utils-0.3.85}/src/csrd_utils/templates/platform-auth-service/{{cookiecutter.__service_name_snake}}/views/__init__.py +0 -0
- {csrd_utils-0.3.81 → csrd_utils-0.3.85}/src/csrd_utils/templates/platform-auth-service/{{cookiecutter.__service_name_snake}}/views/unversioned/__init__.py +0 -0
- {csrd_utils-0.3.81 → csrd_utils-0.3.85}/src/csrd_utils/templates/platform-auth-service/{{cookiecutter.__service_name_snake}}/views/unversioned/auth_view.py +0 -0
- {csrd_utils-0.3.81 → csrd_utils-0.3.85}/src/csrd_utils/templates/platform-auth-service/{{cookiecutter.__service_name_snake}}/views/unversioned/health_view.py +0 -0
- {csrd_utils-0.3.81 → csrd_utils-0.3.85}/src/csrd_utils/templates/platform-identity-service/cookiecutter.json +0 -0
- {csrd_utils-0.3.81 → csrd_utils-0.3.85}/src/csrd_utils/templates/platform-identity-service/{{cookiecutter.__service_name_snake}}/.dockerignore +0 -0
- {csrd_utils-0.3.81 → csrd_utils-0.3.85}/src/csrd_utils/templates/platform-identity-service/{{cookiecutter.__service_name_snake}}/Dockerfile +0 -0
- {csrd_utils-0.3.81 → csrd_utils-0.3.85}/src/csrd_utils/templates/platform-identity-service/{{cookiecutter.__service_name_snake}}/README.md +0 -0
- {csrd_utils-0.3.81 → csrd_utils-0.3.85}/src/csrd_utils/templates/platform-identity-service/{{cookiecutter.__service_name_snake}}/__init__.py +0 -0
- {csrd_utils-0.3.81 → csrd_utils-0.3.85}/src/csrd_utils/templates/platform-identity-service/{{cookiecutter.__service_name_snake}}/dependencies/__init__.py +0 -0
- {csrd_utils-0.3.81 → csrd_utils-0.3.85}/src/csrd_utils/templates/platform-identity-service/{{cookiecutter.__service_name_snake}}/models/__init__.py +0 -0
- {csrd_utils-0.3.81 → csrd_utils-0.3.85}/src/csrd_utils/templates/platform-identity-service/{{cookiecutter.__service_name_snake}}/models/users.py +0 -0
- {csrd_utils-0.3.81 → csrd_utils-0.3.85}/src/csrd_utils/templates/platform-identity-service/{{cookiecutter.__service_name_snake}}/repositories/__init__.py +0 -0
- {csrd_utils-0.3.81 → csrd_utils-0.3.85}/src/csrd_utils/templates/platform-identity-service/{{cookiecutter.__service_name_snake}}/repositories/user_repository.py +0 -0
- {csrd_utils-0.3.81 → csrd_utils-0.3.85}/src/csrd_utils/templates/platform-identity-service/{{cookiecutter.__service_name_snake}}/requirements.txt +0 -0
- {csrd_utils-0.3.81 → csrd_utils-0.3.85}/src/csrd_utils/templates/platform-identity-service/{{cookiecutter.__service_name_snake}}/settings.py +0 -0
- {csrd_utils-0.3.81 → csrd_utils-0.3.85}/src/csrd_utils/templates/platform-identity-service/{{cookiecutter.__service_name_snake}}/views/__init__.py +0 -0
- {csrd_utils-0.3.81 → csrd_utils-0.3.85}/src/csrd_utils/templates/platform-identity-service/{{cookiecutter.__service_name_snake}}/views/unversioned/__init__.py +0 -0
- {csrd_utils-0.3.81 → csrd_utils-0.3.85}/src/csrd_utils/templates/platform-identity-service/{{cookiecutter.__service_name_snake}}/views/unversioned/health_view.py +0 -0
- {csrd_utils-0.3.81 → csrd_utils-0.3.85}/src/csrd_utils/templates/platform-identity-service/{{cookiecutter.__service_name_snake}}/views/unversioned/users_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.85
|
|
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
|
|
@@ -8,7 +8,9 @@ Project-URL: Changelog, https://github.com/csrd-api/fastapi-common/blob/main/CHA
|
|
|
8
8
|
License: MIT
|
|
9
9
|
Requires-Python: >=3.12
|
|
10
10
|
Requires-Dist: cookiecutter<3,>=2.6
|
|
11
|
+
Requires-Dist: pydantic<3,>=2.6
|
|
11
12
|
Requires-Dist: pyyaml<7,>=6
|
|
13
|
+
Requires-Dist: ruamel-yaml<1,>=0.18
|
|
12
14
|
Description-Content-Type: text/markdown
|
|
13
15
|
|
|
14
16
|
# csrd-utils
|
|
@@ -1,12 +1,14 @@
|
|
|
1
1
|
[project]
|
|
2
2
|
name = "csrd-utils"
|
|
3
|
-
version = "0.3.
|
|
3
|
+
version = "0.3.85"
|
|
4
4
|
description = "CLI utilities for csrd service generation and feature augmentation"
|
|
5
5
|
license = { text = "MIT" }
|
|
6
6
|
requires-python = ">=3.12"
|
|
7
7
|
dependencies = [
|
|
8
8
|
"cookiecutter>=2.6,<3",
|
|
9
9
|
"PyYAML>=6,<7",
|
|
10
|
+
"pydantic>=2.6,<3",
|
|
11
|
+
"ruamel.yaml>=0.18,<1",
|
|
10
12
|
]
|
|
11
13
|
readme = "README.md"
|
|
12
14
|
|
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
import argparse
|
|
4
4
|
import json
|
|
5
5
|
import shutil
|
|
6
|
+
import sys
|
|
6
7
|
from contextlib import suppress
|
|
7
8
|
from importlib.metadata import version
|
|
8
9
|
from pathlib import Path
|
|
@@ -24,6 +25,9 @@ from .cluster import (
|
|
|
24
25
|
from .doctor import run_doctor
|
|
25
26
|
from .generator import generate_platform_service, generate_service
|
|
26
27
|
from .resources import features_path
|
|
28
|
+
from .v2.compose import apply as v2_compose_apply
|
|
29
|
+
from .v2.compose import validate as v2_compose_validate
|
|
30
|
+
from .v2.generate import run_generate_menu as run_v2_generate_menu
|
|
27
31
|
|
|
28
32
|
|
|
29
33
|
class GenerateServiceArgs(TypedDict):
|
|
@@ -57,6 +61,7 @@ class GenerateWorkspaceArgs(TypedDict):
|
|
|
57
61
|
output_dir: Path
|
|
58
62
|
overwrite_if_exists: bool
|
|
59
63
|
generate: str
|
|
64
|
+
git_init: bool
|
|
60
65
|
|
|
61
66
|
|
|
62
67
|
class ExtractWorkspaceServiceArgs(TypedDict):
|
|
@@ -416,12 +421,14 @@ def _collect_new_workspace_config(args: argparse.Namespace) -> GenerateWorkspace
|
|
|
416
421
|
generate = args.generate or _prompt_choice(
|
|
417
422
|
"Generate in workspace now", ["none", "service", "cluster"], "service"
|
|
418
423
|
)
|
|
424
|
+
git_init = _prompt_bool("Initialize git repository", True)
|
|
419
425
|
return GenerateWorkspaceArgs(
|
|
420
426
|
{
|
|
421
427
|
"name": name,
|
|
422
428
|
"output_dir": output_dir,
|
|
423
429
|
"overwrite_if_exists": args.force,
|
|
424
430
|
"generate": generate,
|
|
431
|
+
"git_init": git_init,
|
|
425
432
|
}
|
|
426
433
|
)
|
|
427
434
|
|
|
@@ -438,6 +445,10 @@ def _create_workspace(config: GenerateWorkspaceArgs) -> Path:
|
|
|
438
445
|
)
|
|
439
446
|
|
|
440
447
|
_write_workspace_marker(workspace_root)
|
|
448
|
+
|
|
449
|
+
if config.get("git_init", False):
|
|
450
|
+
_git_init(workspace_root)
|
|
451
|
+
|
|
441
452
|
return workspace_root
|
|
442
453
|
|
|
443
454
|
|
|
@@ -577,6 +588,20 @@ def _write_workspace_marker(path: Path) -> None:
|
|
|
577
588
|
(path / WORKSPACE_MARKER).write_text("workspace: csrd\n", encoding="utf-8")
|
|
578
589
|
|
|
579
590
|
|
|
591
|
+
def _git_init(path: Path) -> None:
|
|
592
|
+
"""Run ``git init`` inside *path* if not already a git repository."""
|
|
593
|
+
import subprocess
|
|
594
|
+
|
|
595
|
+
if (path / ".git").exists():
|
|
596
|
+
return
|
|
597
|
+
subprocess.run(
|
|
598
|
+
["git", "init"],
|
|
599
|
+
cwd=path,
|
|
600
|
+
check=True,
|
|
601
|
+
capture_output=True,
|
|
602
|
+
)
|
|
603
|
+
|
|
604
|
+
|
|
580
605
|
def _is_workspace(path: Path) -> bool:
|
|
581
606
|
return (path / WORKSPACE_MARKER).is_file()
|
|
582
607
|
|
|
@@ -1199,6 +1224,76 @@ def _build_parser() -> argparse.ArgumentParser:
|
|
|
1199
1224
|
completion_sub = completion.add_subparsers(dest="completion_shell", required=True)
|
|
1200
1225
|
completion_sub.add_parser("bash", help="Print bash completion script")
|
|
1201
1226
|
|
|
1227
|
+
v2 = sub.add_parser("v2", help="Preview next-gen generator and compose workflow")
|
|
1228
|
+
v2_sub = v2.add_subparsers(dest="v2_command")
|
|
1229
|
+
|
|
1230
|
+
v2_generate = v2_sub.add_parser("generate", help="Open interactive v2 generator menu")
|
|
1231
|
+
v2_generate.add_argument(
|
|
1232
|
+
"target",
|
|
1233
|
+
nargs="?",
|
|
1234
|
+
choices=["workspace", "preset", "add-service", "add-infra", "remove-infra", "empty"],
|
|
1235
|
+
help="Direct generator target (workspace, preset, add-service, add-infra, remove-infra)",
|
|
1236
|
+
)
|
|
1237
|
+
v2_generate.add_argument(
|
|
1238
|
+
"--name",
|
|
1239
|
+
dest="generate_name",
|
|
1240
|
+
help="Workspace or service name",
|
|
1241
|
+
)
|
|
1242
|
+
v2_generate.add_argument(
|
|
1243
|
+
"--git-init",
|
|
1244
|
+
action="store_true",
|
|
1245
|
+
default=False,
|
|
1246
|
+
help="Initialize a git repository in the generated workspace",
|
|
1247
|
+
)
|
|
1248
|
+
v2_generate.add_argument(
|
|
1249
|
+
"--features",
|
|
1250
|
+
dest="features",
|
|
1251
|
+
nargs="*",
|
|
1252
|
+
choices=["database", "caching", "messaging"],
|
|
1253
|
+
default=None,
|
|
1254
|
+
help="Service capabilities to wire (for add-service)",
|
|
1255
|
+
)
|
|
1256
|
+
v2_generate.add_argument(
|
|
1257
|
+
"--port",
|
|
1258
|
+
dest="port",
|
|
1259
|
+
type=int,
|
|
1260
|
+
default=None,
|
|
1261
|
+
help="Service port (for add-service)",
|
|
1262
|
+
)
|
|
1263
|
+
v2_generate.add_argument(
|
|
1264
|
+
"--infra-type",
|
|
1265
|
+
dest="infra_type",
|
|
1266
|
+
choices=["postgres", "mariadb", "sqlite", "redis", "rabbitmq"],
|
|
1267
|
+
default=None,
|
|
1268
|
+
help="Infrastructure type (for add-infra)",
|
|
1269
|
+
)
|
|
1270
|
+
|
|
1271
|
+
v2_compose = v2_sub.add_parser("compose", help="Compose v2 operations")
|
|
1272
|
+
v2_compose_sub = v2_compose.add_subparsers(dest="v2_compose_command")
|
|
1273
|
+
|
|
1274
|
+
v2_compose_validate = v2_compose_sub.add_parser("validate", help="Validate csrd-compose.yaml")
|
|
1275
|
+
v2_compose_validate.add_argument(
|
|
1276
|
+
"--output", type=Path, default=Path.cwd(), help="Workspace root"
|
|
1277
|
+
)
|
|
1278
|
+
v2_compose_validate.add_argument(
|
|
1279
|
+
"--json", action="store_true", help="Print validated compose spec as JSON"
|
|
1280
|
+
)
|
|
1281
|
+
|
|
1282
|
+
v2_compose_plan = v2_compose_sub.add_parser("plan", help="Preview resolved compose baseline")
|
|
1283
|
+
v2_compose_plan.add_argument("--output", type=Path, default=Path.cwd(), help="Workspace root")
|
|
1284
|
+
v2_compose_plan.add_argument("--json", action="store_true", help="Print plan payload as JSON")
|
|
1285
|
+
|
|
1286
|
+
v2_compose_apply = v2_compose_sub.add_parser(
|
|
1287
|
+
"apply", help="Render baseline workspace artifacts"
|
|
1288
|
+
)
|
|
1289
|
+
v2_compose_apply.add_argument("--output", type=Path, default=Path.cwd(), help="Workspace root")
|
|
1290
|
+
v2_compose_apply.add_argument(
|
|
1291
|
+
"--git-init",
|
|
1292
|
+
action="store_true",
|
|
1293
|
+
default=False,
|
|
1294
|
+
help="Initialize a git repository in the workspace",
|
|
1295
|
+
)
|
|
1296
|
+
|
|
1202
1297
|
return parser
|
|
1203
1298
|
|
|
1204
1299
|
|
|
@@ -1323,9 +1418,106 @@ def _run_audit(service: Path | None, as_json: bool) -> int:
|
|
|
1323
1418
|
|
|
1324
1419
|
|
|
1325
1420
|
def main() -> int:
|
|
1421
|
+
# Shorthand: `csrd v2 compose <path>` => `csrd v2 compose apply --output <path>`
|
|
1422
|
+
raw_argv = sys.argv[1:]
|
|
1423
|
+
if len(raw_argv) >= 3 and raw_argv[0] == "v2" and raw_argv[1] == "compose":
|
|
1424
|
+
third = raw_argv[2]
|
|
1425
|
+
if third not in {"validate", "plan", "apply", "-h", "--help"} and not third.startswith("-"):
|
|
1426
|
+
shorthand_dir = Path(third).resolve()
|
|
1427
|
+
has_compose_spec = (shorthand_dir / "csrd-compose.yaml").is_file() or (
|
|
1428
|
+
shorthand_dir / "csrd-compose.yml"
|
|
1429
|
+
).is_file()
|
|
1430
|
+
if not has_compose_spec:
|
|
1431
|
+
print(f"ERROR: Compose shorthand requires csrd-compose.yaml in: {shorthand_dir}")
|
|
1432
|
+
print(
|
|
1433
|
+
"Hint: create a spec first (csrd v2 generate empty --name <name>) "
|
|
1434
|
+
"or use explicit apply: csrd v2 compose apply --output <path>"
|
|
1435
|
+
)
|
|
1436
|
+
return 1
|
|
1437
|
+
sys.argv = [
|
|
1438
|
+
sys.argv[0],
|
|
1439
|
+
"v2",
|
|
1440
|
+
"compose",
|
|
1441
|
+
"apply",
|
|
1442
|
+
"--output",
|
|
1443
|
+
third,
|
|
1444
|
+
*raw_argv[3:],
|
|
1445
|
+
]
|
|
1446
|
+
|
|
1326
1447
|
parser = _build_parser()
|
|
1327
1448
|
args = parser.parse_args()
|
|
1328
1449
|
|
|
1450
|
+
if args.command == "v2":
|
|
1451
|
+
if getattr(args, "v2_command", None) == "generate":
|
|
1452
|
+
return run_v2_generate_menu(
|
|
1453
|
+
target=getattr(args, "target", None),
|
|
1454
|
+
name=getattr(args, "generate_name", None),
|
|
1455
|
+
git_init=getattr(args, "git_init", False),
|
|
1456
|
+
features=getattr(args, "features", None),
|
|
1457
|
+
port=getattr(args, "port", None),
|
|
1458
|
+
infra_type=getattr(args, "infra_type", None),
|
|
1459
|
+
)
|
|
1460
|
+
|
|
1461
|
+
if getattr(args, "v2_command", None) != "compose":
|
|
1462
|
+
parser.print_help()
|
|
1463
|
+
return 0
|
|
1464
|
+
|
|
1465
|
+
compose_command = getattr(args, "v2_compose_command", None)
|
|
1466
|
+
if compose_command is None:
|
|
1467
|
+
parser.print_help()
|
|
1468
|
+
return 0
|
|
1469
|
+
|
|
1470
|
+
output_dir = Path(args.output).resolve()
|
|
1471
|
+
try:
|
|
1472
|
+
if compose_command == "apply":
|
|
1473
|
+
generated_root = v2_compose_apply(
|
|
1474
|
+
output_dir, git_init=getattr(args, "git_init", False)
|
|
1475
|
+
)
|
|
1476
|
+
print(f"Rendered compose workspace at: {generated_root}")
|
|
1477
|
+
return 0
|
|
1478
|
+
|
|
1479
|
+
if compose_command == "validate":
|
|
1480
|
+
spec = v2_compose_validate(output_dir)
|
|
1481
|
+
if args.json:
|
|
1482
|
+
print(json.dumps(spec.model_dump(mode="python"), indent=2, sort_keys=True))
|
|
1483
|
+
else:
|
|
1484
|
+
print(f"Compose spec valid: {output_dir / 'csrd-compose.yaml'}")
|
|
1485
|
+
return 0
|
|
1486
|
+
|
|
1487
|
+
if compose_command == "plan":
|
|
1488
|
+
spec = v2_compose_validate(output_dir)
|
|
1489
|
+
payload = {
|
|
1490
|
+
"workspace": spec.workspace.name,
|
|
1491
|
+
"version": spec.version,
|
|
1492
|
+
"services": len(spec.services),
|
|
1493
|
+
"infra": len(spec.infra),
|
|
1494
|
+
"presets": [preset.name for preset in spec.presets],
|
|
1495
|
+
"overrides_keys": sorted(spec.overrides.keys()),
|
|
1496
|
+
}
|
|
1497
|
+
if args.json:
|
|
1498
|
+
print(json.dumps(payload, indent=2, sort_keys=True))
|
|
1499
|
+
else:
|
|
1500
|
+
print("Compose v2 plan")
|
|
1501
|
+
print(f"- workspace: {payload['workspace']}")
|
|
1502
|
+
print(f"- version: {payload['version']}")
|
|
1503
|
+
print(f"- services: {payload['services']}")
|
|
1504
|
+
print(f"- infra: {payload['infra']}")
|
|
1505
|
+
presets = payload["presets"]
|
|
1506
|
+
overrides_keys = payload["overrides_keys"]
|
|
1507
|
+
assert isinstance(presets, list)
|
|
1508
|
+
assert isinstance(overrides_keys, list)
|
|
1509
|
+
print(f"- presets: {', '.join(presets) if presets else '<none>'}")
|
|
1510
|
+
print(
|
|
1511
|
+
f"- overrides: {', '.join(overrides_keys) if overrides_keys else '<none>'}"
|
|
1512
|
+
)
|
|
1513
|
+
return 0
|
|
1514
|
+
except ValueError as exc:
|
|
1515
|
+
print(f"ERROR: {exc}")
|
|
1516
|
+
return 1
|
|
1517
|
+
|
|
1518
|
+
parser.print_help()
|
|
1519
|
+
return 0
|
|
1520
|
+
|
|
1329
1521
|
if args.command == "new" and args.new_command == "service":
|
|
1330
1522
|
try:
|
|
1331
1523
|
service_config = _collect_new_service_config(args)
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"""Namespace package for next-gen csrd compose and generate flows."""
|
|
@@ -0,0 +1,180 @@
|
|
|
1
|
+
"""Compose package namespace."""
|
|
2
|
+
|
|
3
|
+
from pathlib import Path
|
|
4
|
+
|
|
5
|
+
from .loader import (
|
|
6
|
+
SPEC_FILENAME,
|
|
7
|
+
default_spec,
|
|
8
|
+
load_spec,
|
|
9
|
+
save_spec,
|
|
10
|
+
spec_file_path,
|
|
11
|
+
)
|
|
12
|
+
from .presets import (
|
|
13
|
+
PRESET_REGISTRY,
|
|
14
|
+
PresetDefinition,
|
|
15
|
+
list_presets,
|
|
16
|
+
members_for_category,
|
|
17
|
+
)
|
|
18
|
+
from .renderer import render_workspace
|
|
19
|
+
from .spec import (
|
|
20
|
+
INFRA_ALL_TYPES,
|
|
21
|
+
INFRA_CATEGORIES,
|
|
22
|
+
INFRA_DATABASES,
|
|
23
|
+
ComposeSpec,
|
|
24
|
+
InfraNode,
|
|
25
|
+
ServiceNode,
|
|
26
|
+
)
|
|
27
|
+
from .yaml_editor import (
|
|
28
|
+
deep_merge,
|
|
29
|
+
dumps_yaml,
|
|
30
|
+
ensure_list_item,
|
|
31
|
+
load_yaml,
|
|
32
|
+
save_yaml,
|
|
33
|
+
set_key,
|
|
34
|
+
)
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
def apply(output_dir: Path, *, git_init: bool = False) -> Path:
|
|
38
|
+
"""Apply baseline empty-workspace rendering into `output_dir`."""
|
|
39
|
+
|
|
40
|
+
return render_workspace(output_dir, git_init=git_init)
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
def validate(output_dir: Path) -> ComposeSpec:
|
|
44
|
+
"""Validate and return the canonical compose spec for a workspace."""
|
|
45
|
+
|
|
46
|
+
return load_spec(spec_file_path(output_dir))
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
def add_service(output_dir: Path, service: ServiceNode) -> ComposeSpec:
|
|
50
|
+
"""Append a service to the workspace spec and persist to disk.
|
|
51
|
+
|
|
52
|
+
Returns the updated spec.
|
|
53
|
+
"""
|
|
54
|
+
|
|
55
|
+
spec_path = spec_file_path(output_dir)
|
|
56
|
+
spec = load_spec(spec_path)
|
|
57
|
+
|
|
58
|
+
existing_names = {s.name for s in spec.services}
|
|
59
|
+
if service.name in existing_names:
|
|
60
|
+
raise ValueError(f"Service '{service.name}' already exists in workspace spec")
|
|
61
|
+
|
|
62
|
+
spec.services.append(service)
|
|
63
|
+
save_spec(spec, spec_path)
|
|
64
|
+
return spec
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
def add_infra(output_dir: Path, infra: InfraNode) -> ComposeSpec:
|
|
68
|
+
# ...existing docstring and body...
|
|
69
|
+
"""Add or replace an infrastructure entry in the workspace spec.
|
|
70
|
+
|
|
71
|
+
Database types are mutually exclusive — adding one replaces any existing
|
|
72
|
+
database entry. Other infra types (redis, rabbitmq) are singletons that
|
|
73
|
+
cannot be duplicated.
|
|
74
|
+
|
|
75
|
+
Returns the updated spec.
|
|
76
|
+
"""
|
|
77
|
+
|
|
78
|
+
spec_path = spec_file_path(output_dir)
|
|
79
|
+
spec = load_spec(spec_path)
|
|
80
|
+
|
|
81
|
+
existing_types = {i.type for i in spec.infra}
|
|
82
|
+
|
|
83
|
+
if infra.type in INFRA_DATABASES:
|
|
84
|
+
# Remove any existing database entry (mutually exclusive)
|
|
85
|
+
spec.infra = [i for i in spec.infra if i.type not in INFRA_DATABASES]
|
|
86
|
+
elif infra.type in existing_types:
|
|
87
|
+
raise ValueError(f"Infra '{infra.type}' already exists in workspace spec")
|
|
88
|
+
|
|
89
|
+
spec.infra.append(infra)
|
|
90
|
+
save_spec(spec, spec_path)
|
|
91
|
+
return spec
|
|
92
|
+
|
|
93
|
+
|
|
94
|
+
def remove_infra(output_dir: Path, infra_type: str) -> ComposeSpec:
|
|
95
|
+
"""Remove an infrastructure entry from the workspace spec.
|
|
96
|
+
|
|
97
|
+
Raises ``ValueError`` if *infra_type* is not currently configured.
|
|
98
|
+
|
|
99
|
+
Returns the updated spec.
|
|
100
|
+
"""
|
|
101
|
+
|
|
102
|
+
spec_path = spec_file_path(output_dir)
|
|
103
|
+
spec = load_spec(spec_path)
|
|
104
|
+
|
|
105
|
+
existing_types = {i.type for i in spec.infra}
|
|
106
|
+
if infra_type not in existing_types:
|
|
107
|
+
raise ValueError(f"Infra '{infra_type}' is not configured in workspace spec")
|
|
108
|
+
|
|
109
|
+
spec.infra = [i for i in spec.infra if i.type != infra_type]
|
|
110
|
+
save_spec(spec, spec_path)
|
|
111
|
+
return spec
|
|
112
|
+
|
|
113
|
+
|
|
114
|
+
def available_infra(workspace_dir: Path) -> list[str]:
|
|
115
|
+
"""Return infra types not yet present in the workspace spec.
|
|
116
|
+
|
|
117
|
+
Database types are mutually exclusive — if any database is already
|
|
118
|
+
configured, all database options are excluded.
|
|
119
|
+
"""
|
|
120
|
+
|
|
121
|
+
spec_path = spec_file_path(workspace_dir)
|
|
122
|
+
if not spec_path.is_file():
|
|
123
|
+
return sorted(INFRA_ALL_TYPES)
|
|
124
|
+
|
|
125
|
+
spec = load_spec(spec_path)
|
|
126
|
+
existing = {i.type for i in spec.infra}
|
|
127
|
+
|
|
128
|
+
available: list[str] = []
|
|
129
|
+
has_db = bool(existing & INFRA_DATABASES)
|
|
130
|
+
for infra_type in sorted(INFRA_ALL_TYPES):
|
|
131
|
+
if infra_type in existing:
|
|
132
|
+
continue
|
|
133
|
+
if has_db and infra_type in INFRA_DATABASES:
|
|
134
|
+
continue
|
|
135
|
+
available.append(infra_type)
|
|
136
|
+
return available
|
|
137
|
+
|
|
138
|
+
|
|
139
|
+
def configured_infra(workspace_dir: Path) -> list[str]:
|
|
140
|
+
"""Return infra types currently configured in the workspace spec."""
|
|
141
|
+
|
|
142
|
+
spec_path = spec_file_path(workspace_dir)
|
|
143
|
+
if not spec_path.is_file():
|
|
144
|
+
return []
|
|
145
|
+
|
|
146
|
+
spec = load_spec(spec_path)
|
|
147
|
+
return sorted(i.type for i in spec.infra)
|
|
148
|
+
|
|
149
|
+
|
|
150
|
+
__all__ = [
|
|
151
|
+
"INFRA_ALL_TYPES",
|
|
152
|
+
"INFRA_CATEGORIES",
|
|
153
|
+
"INFRA_DATABASES",
|
|
154
|
+
"PRESET_REGISTRY",
|
|
155
|
+
"SPEC_FILENAME",
|
|
156
|
+
"ComposeSpec",
|
|
157
|
+
"InfraNode",
|
|
158
|
+
"PresetDefinition",
|
|
159
|
+
"ServiceNode",
|
|
160
|
+
"add_infra",
|
|
161
|
+
"add_service",
|
|
162
|
+
"apply",
|
|
163
|
+
"available_infra",
|
|
164
|
+
"configured_infra",
|
|
165
|
+
"deep_merge",
|
|
166
|
+
"default_spec",
|
|
167
|
+
"dumps_yaml",
|
|
168
|
+
"ensure_list_item",
|
|
169
|
+
"list_presets",
|
|
170
|
+
"load_spec",
|
|
171
|
+
"load_yaml",
|
|
172
|
+
"members_for_category",
|
|
173
|
+
"remove_infra",
|
|
174
|
+
"render_workspace",
|
|
175
|
+
"save_spec",
|
|
176
|
+
"save_yaml",
|
|
177
|
+
"set_key",
|
|
178
|
+
"spec_file_path",
|
|
179
|
+
"validate",
|
|
180
|
+
]
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
"""Git integration helpers for compose workspaces."""
|
|
2
|
+
|
|
3
|
+
import subprocess
|
|
4
|
+
from pathlib import Path
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
def maybe_git_init(output_dir: Path) -> None:
|
|
8
|
+
"""Run ``git init`` inside *output_dir* if the directory is not already a git repo."""
|
|
9
|
+
|
|
10
|
+
if (output_dir / ".git").exists():
|
|
11
|
+
return
|
|
12
|
+
subprocess.run(
|
|
13
|
+
["git", "init"],
|
|
14
|
+
cwd=output_dir,
|
|
15
|
+
check=True,
|
|
16
|
+
capture_output=True,
|
|
17
|
+
)
|
|
@@ -0,0 +1,178 @@
|
|
|
1
|
+
"""Infrastructure container definitions for docker-compose rendering.
|
|
2
|
+
|
|
3
|
+
Each definition is a pure data function — no I/O, no side effects.
|
|
4
|
+
The renderer calls these to build the ``services`` and ``volumes``
|
|
5
|
+
sections of ``docker-compose.yml``.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
from __future__ import annotations
|
|
9
|
+
|
|
10
|
+
from typing import Any
|
|
11
|
+
|
|
12
|
+
from ..models import InfraRenderer
|
|
13
|
+
|
|
14
|
+
# ---------------------------------------------------------------------------
|
|
15
|
+
# Healthcheck presets
|
|
16
|
+
# ---------------------------------------------------------------------------
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
def _healthcheck(
|
|
20
|
+
test: list[str], interval: str = "5s", timeout: str = "3s", retries: int = 3
|
|
21
|
+
) -> dict[str, Any]:
|
|
22
|
+
return {
|
|
23
|
+
"test": test,
|
|
24
|
+
"interval": interval,
|
|
25
|
+
"timeout": timeout,
|
|
26
|
+
"retries": retries,
|
|
27
|
+
"start_period": "10s",
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
# ---------------------------------------------------------------------------
|
|
32
|
+
# Container definitions
|
|
33
|
+
# ---------------------------------------------------------------------------
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
def postgres_service() -> tuple[dict[str, Any], dict[str, None]]:
|
|
37
|
+
"""Return (service_dict, volumes_dict) for a Postgres container."""
|
|
38
|
+
|
|
39
|
+
service: dict[str, Any] = {
|
|
40
|
+
"image": "postgres:16-alpine",
|
|
41
|
+
"restart": "unless-stopped",
|
|
42
|
+
"environment": {
|
|
43
|
+
"POSTGRES_DB": "${DB_NAME:-service_db}",
|
|
44
|
+
"POSTGRES_USER": "${DB_USER:-service_user}",
|
|
45
|
+
"POSTGRES_PASSWORD": "${DB_PASSWORD:-change_me}",
|
|
46
|
+
},
|
|
47
|
+
"ports": ["5432:5432"],
|
|
48
|
+
"volumes": ["postgres-data:/var/lib/postgresql/data"],
|
|
49
|
+
"healthcheck": _healthcheck(
|
|
50
|
+
["CMD-SHELL", "pg_isready -U ${DB_USER:-service_user} -d ${DB_NAME:-service_db}"],
|
|
51
|
+
),
|
|
52
|
+
}
|
|
53
|
+
volumes: dict[str, None] = {"postgres-data": None}
|
|
54
|
+
return service, volumes
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
def mariadb_service() -> tuple[dict[str, Any], dict[str, None]]:
|
|
58
|
+
"""Return (service_dict, volumes_dict) for a MariaDB container."""
|
|
59
|
+
|
|
60
|
+
service: dict[str, Any] = {
|
|
61
|
+
"image": "mariadb:11",
|
|
62
|
+
"restart": "unless-stopped",
|
|
63
|
+
"environment": {
|
|
64
|
+
"MYSQL_ROOT_PASSWORD": "${MYSQL_ROOT_PASSWORD:-change_me_root}",
|
|
65
|
+
"MYSQL_DATABASE": "${DB_NAME:-service_db}",
|
|
66
|
+
"MYSQL_USER": "${DB_USER:-service_user}",
|
|
67
|
+
"MYSQL_PASSWORD": "${DB_PASSWORD:-change_me}",
|
|
68
|
+
},
|
|
69
|
+
"ports": ["3306:3306"],
|
|
70
|
+
"volumes": ["mariadb-data:/var/lib/mysql"],
|
|
71
|
+
"healthcheck": _healthcheck(
|
|
72
|
+
["CMD", "healthcheck.sh", "--connect", "--innodb_initialized"],
|
|
73
|
+
),
|
|
74
|
+
}
|
|
75
|
+
volumes: dict[str, None] = {"mariadb-data": None}
|
|
76
|
+
return service, volumes
|
|
77
|
+
|
|
78
|
+
|
|
79
|
+
def redis_service() -> tuple[dict[str, Any], dict[str, None]]:
|
|
80
|
+
"""Return (service_dict, volumes_dict) for a Redis container."""
|
|
81
|
+
|
|
82
|
+
service: dict[str, Any] = {
|
|
83
|
+
"image": "redis:7-alpine",
|
|
84
|
+
"restart": "unless-stopped",
|
|
85
|
+
"ports": ["6379:6379"],
|
|
86
|
+
"healthcheck": _healthcheck(
|
|
87
|
+
["CMD", "redis-cli", "ping"],
|
|
88
|
+
),
|
|
89
|
+
}
|
|
90
|
+
return service, {}
|
|
91
|
+
|
|
92
|
+
|
|
93
|
+
def rabbitmq_service() -> tuple[dict[str, Any], dict[str, None]]:
|
|
94
|
+
"""Return (service_dict, volumes_dict) for a RabbitMQ container."""
|
|
95
|
+
|
|
96
|
+
service: dict[str, Any] = {
|
|
97
|
+
"image": "rabbitmq:3-management-alpine",
|
|
98
|
+
"restart": "unless-stopped",
|
|
99
|
+
"environment": {
|
|
100
|
+
"RABBITMQ_DEFAULT_USER": "${RABBITMQ_USER:-service_rabbit}",
|
|
101
|
+
"RABBITMQ_DEFAULT_PASS": "${RABBITMQ_PASSWORD:-change_me}",
|
|
102
|
+
},
|
|
103
|
+
"ports": ["5672:5672", "15672:15672"],
|
|
104
|
+
"healthcheck": _healthcheck(
|
|
105
|
+
["CMD", "rabbitmq-diagnostics", "-q", "ping"],
|
|
106
|
+
),
|
|
107
|
+
}
|
|
108
|
+
return service, {}
|
|
109
|
+
|
|
110
|
+
|
|
111
|
+
# ---------------------------------------------------------------------------
|
|
112
|
+
# Registry
|
|
113
|
+
# ---------------------------------------------------------------------------
|
|
114
|
+
|
|
115
|
+
INFRA_RENDERERS: dict[str, InfraRenderer] = {
|
|
116
|
+
"postgres": postgres_service,
|
|
117
|
+
"mariadb": mariadb_service,
|
|
118
|
+
"redis": redis_service,
|
|
119
|
+
"rabbitmq": rabbitmq_service,
|
|
120
|
+
}
|
|
121
|
+
"""Map infra type name → factory that returns ``(service_dict, volumes_dict)``."""
|
|
122
|
+
|
|
123
|
+
|
|
124
|
+
def render_infra(infra_type: str) -> tuple[str, dict[str, Any], dict[str, None]]:
|
|
125
|
+
"""Render an infra type into its compose service name, service dict, and volumes.
|
|
126
|
+
|
|
127
|
+
Raises ``KeyError`` if *infra_type* has no renderer (e.g. ``sqlite``
|
|
128
|
+
which needs no container).
|
|
129
|
+
"""
|
|
130
|
+
|
|
131
|
+
factory = INFRA_RENDERERS[infra_type]
|
|
132
|
+
service_def, volumes = factory()
|
|
133
|
+
return infra_type, service_def, volumes
|
|
134
|
+
|
|
135
|
+
|
|
136
|
+
# ---------------------------------------------------------------------------
|
|
137
|
+
# Service environment wiring
|
|
138
|
+
# ---------------------------------------------------------------------------
|
|
139
|
+
|
|
140
|
+
|
|
141
|
+
def service_env_block(db_type: str) -> dict[str, str]:
|
|
142
|
+
"""Return the environment variables a service needs to connect to *db_type*.
|
|
143
|
+
|
|
144
|
+
For ``postgres``/``mariadb`` the vars point at the shared infra container.
|
|
145
|
+
For ``sqlite`` the var points at a local file path (volume-mounted).
|
|
146
|
+
"""
|
|
147
|
+
|
|
148
|
+
if db_type == "postgres":
|
|
149
|
+
return {
|
|
150
|
+
"DB_HOST": "postgres",
|
|
151
|
+
"DB_PORT": "5432",
|
|
152
|
+
"DB_NAME": "${DB_NAME:-service_db}",
|
|
153
|
+
"DB_USER": "${DB_USER:-service_user}",
|
|
154
|
+
"DB_PASSWORD": "${DB_PASSWORD:-change_me}",
|
|
155
|
+
}
|
|
156
|
+
if db_type == "mariadb":
|
|
157
|
+
return {
|
|
158
|
+
"DB_HOST": "mariadb",
|
|
159
|
+
"DB_PORT": "3306",
|
|
160
|
+
"DB_NAME": "${DB_NAME:-service_db}",
|
|
161
|
+
"DB_USER": "${DB_USER:-service_user}",
|
|
162
|
+
"DB_PASSWORD": "${DB_PASSWORD:-change_me}",
|
|
163
|
+
}
|
|
164
|
+
if db_type == "sqlite":
|
|
165
|
+
# DB_PATH is set per-service in the renderer; this is the base template.
|
|
166
|
+
return {}
|
|
167
|
+
return {}
|
|
168
|
+
|
|
169
|
+
|
|
170
|
+
def service_depends_on(db_type: str) -> dict[str, Any]:
|
|
171
|
+
"""Return ``depends_on`` entries for a service wired to *db_type*.
|
|
172
|
+
|
|
173
|
+
``sqlite`` returns empty (no container dependency).
|
|
174
|
+
"""
|
|
175
|
+
|
|
176
|
+
if db_type in {"postgres", "mariadb"}:
|
|
177
|
+
return {db_type: {"condition": "service_healthy"}}
|
|
178
|
+
return {}
|