solokit 0.1.1__py3-none-any.whl
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.
- solokit/__init__.py +10 -0
- solokit/__version__.py +3 -0
- solokit/cli.py +374 -0
- solokit/core/__init__.py +1 -0
- solokit/core/cache.py +102 -0
- solokit/core/command_runner.py +278 -0
- solokit/core/config.py +453 -0
- solokit/core/config_validator.py +204 -0
- solokit/core/constants.py +291 -0
- solokit/core/error_formatter.py +279 -0
- solokit/core/error_handlers.py +346 -0
- solokit/core/exceptions.py +1567 -0
- solokit/core/file_ops.py +309 -0
- solokit/core/logging_config.py +166 -0
- solokit/core/output.py +99 -0
- solokit/core/performance.py +57 -0
- solokit/core/protocols.py +141 -0
- solokit/core/types.py +312 -0
- solokit/deployment/__init__.py +1 -0
- solokit/deployment/executor.py +411 -0
- solokit/git/__init__.py +1 -0
- solokit/git/integration.py +619 -0
- solokit/init/__init__.py +41 -0
- solokit/init/claude_commands_installer.py +87 -0
- solokit/init/dependency_installer.py +313 -0
- solokit/init/docs_structure.py +90 -0
- solokit/init/env_generator.py +160 -0
- solokit/init/environment_validator.py +334 -0
- solokit/init/git_hooks_installer.py +71 -0
- solokit/init/git_setup.py +188 -0
- solokit/init/gitignore_updater.py +195 -0
- solokit/init/initial_commit.py +145 -0
- solokit/init/initial_scans.py +109 -0
- solokit/init/orchestrator.py +246 -0
- solokit/init/readme_generator.py +207 -0
- solokit/init/session_structure.py +239 -0
- solokit/init/template_installer.py +424 -0
- solokit/learning/__init__.py +1 -0
- solokit/learning/archiver.py +115 -0
- solokit/learning/categorizer.py +126 -0
- solokit/learning/curator.py +428 -0
- solokit/learning/extractor.py +352 -0
- solokit/learning/reporter.py +351 -0
- solokit/learning/repository.py +254 -0
- solokit/learning/similarity.py +342 -0
- solokit/learning/validator.py +144 -0
- solokit/project/__init__.py +1 -0
- solokit/project/init.py +1162 -0
- solokit/project/stack.py +436 -0
- solokit/project/sync_plugin.py +438 -0
- solokit/project/tree.py +375 -0
- solokit/quality/__init__.py +1 -0
- solokit/quality/api_validator.py +424 -0
- solokit/quality/checkers/__init__.py +25 -0
- solokit/quality/checkers/base.py +114 -0
- solokit/quality/checkers/context7.py +221 -0
- solokit/quality/checkers/custom.py +162 -0
- solokit/quality/checkers/deployment.py +323 -0
- solokit/quality/checkers/documentation.py +179 -0
- solokit/quality/checkers/formatting.py +161 -0
- solokit/quality/checkers/integration.py +394 -0
- solokit/quality/checkers/linting.py +159 -0
- solokit/quality/checkers/security.py +261 -0
- solokit/quality/checkers/spec_completeness.py +127 -0
- solokit/quality/checkers/tests.py +184 -0
- solokit/quality/env_validator.py +306 -0
- solokit/quality/gates.py +655 -0
- solokit/quality/reporters/__init__.py +10 -0
- solokit/quality/reporters/base.py +25 -0
- solokit/quality/reporters/console.py +98 -0
- solokit/quality/reporters/json_reporter.py +34 -0
- solokit/quality/results.py +98 -0
- solokit/session/__init__.py +1 -0
- solokit/session/briefing/__init__.py +245 -0
- solokit/session/briefing/documentation_loader.py +53 -0
- solokit/session/briefing/formatter.py +476 -0
- solokit/session/briefing/git_context.py +282 -0
- solokit/session/briefing/learning_loader.py +212 -0
- solokit/session/briefing/milestone_builder.py +78 -0
- solokit/session/briefing/orchestrator.py +137 -0
- solokit/session/briefing/stack_detector.py +51 -0
- solokit/session/briefing/tree_generator.py +52 -0
- solokit/session/briefing/work_item_loader.py +209 -0
- solokit/session/briefing.py +353 -0
- solokit/session/complete.py +1188 -0
- solokit/session/status.py +246 -0
- solokit/session/validate.py +452 -0
- solokit/templates/.claude/commands/end.md +109 -0
- solokit/templates/.claude/commands/init.md +159 -0
- solokit/templates/.claude/commands/learn-curate.md +88 -0
- solokit/templates/.claude/commands/learn-search.md +62 -0
- solokit/templates/.claude/commands/learn-show.md +69 -0
- solokit/templates/.claude/commands/learn.md +136 -0
- solokit/templates/.claude/commands/start.md +114 -0
- solokit/templates/.claude/commands/status.md +22 -0
- solokit/templates/.claude/commands/validate.md +27 -0
- solokit/templates/.claude/commands/work-delete.md +119 -0
- solokit/templates/.claude/commands/work-graph.md +139 -0
- solokit/templates/.claude/commands/work-list.md +26 -0
- solokit/templates/.claude/commands/work-new.md +114 -0
- solokit/templates/.claude/commands/work-next.md +25 -0
- solokit/templates/.claude/commands/work-show.md +24 -0
- solokit/templates/.claude/commands/work-update.md +141 -0
- solokit/templates/CHANGELOG.md +17 -0
- solokit/templates/WORK_ITEM_TYPES.md +141 -0
- solokit/templates/__init__.py +1 -0
- solokit/templates/bug_spec.md +217 -0
- solokit/templates/config.schema.json +150 -0
- solokit/templates/dashboard_refine/base/.gitignore +36 -0
- solokit/templates/dashboard_refine/base/app/(dashboard)/layout.tsx +22 -0
- solokit/templates/dashboard_refine/base/app/(dashboard)/page.tsx +68 -0
- solokit/templates/dashboard_refine/base/app/(dashboard)/users/page.tsx +77 -0
- solokit/templates/dashboard_refine/base/app/globals.css +60 -0
- solokit/templates/dashboard_refine/base/app/layout.tsx +23 -0
- solokit/templates/dashboard_refine/base/app/page.tsx +9 -0
- solokit/templates/dashboard_refine/base/components/client-refine-wrapper.tsx +21 -0
- solokit/templates/dashboard_refine/base/components/layout/header.tsx +44 -0
- solokit/templates/dashboard_refine/base/components/layout/sidebar.tsx +82 -0
- solokit/templates/dashboard_refine/base/components/ui/button.tsx +53 -0
- solokit/templates/dashboard_refine/base/components/ui/card.tsx +78 -0
- solokit/templates/dashboard_refine/base/components/ui/table.tsx +116 -0
- solokit/templates/dashboard_refine/base/components.json +16 -0
- solokit/templates/dashboard_refine/base/lib/refine.tsx +65 -0
- solokit/templates/dashboard_refine/base/lib/utils.ts +13 -0
- solokit/templates/dashboard_refine/base/next.config.ts +10 -0
- solokit/templates/dashboard_refine/base/package.json.template +40 -0
- solokit/templates/dashboard_refine/base/postcss.config.mjs +8 -0
- solokit/templates/dashboard_refine/base/providers/refine-provider.tsx +26 -0
- solokit/templates/dashboard_refine/base/tailwind.config.ts +57 -0
- solokit/templates/dashboard_refine/base/tsconfig.json +27 -0
- solokit/templates/dashboard_refine/docker/Dockerfile +57 -0
- solokit/templates/dashboard_refine/docker/docker-compose.prod.yml +31 -0
- solokit/templates/dashboard_refine/docker/docker-compose.yml +21 -0
- solokit/templates/dashboard_refine/tier-1-essential/.eslintrc.json +7 -0
- solokit/templates/dashboard_refine/tier-1-essential/jest.config.ts +17 -0
- solokit/templates/dashboard_refine/tier-1-essential/jest.setup.ts +1 -0
- solokit/templates/dashboard_refine/tier-1-essential/package.json.tier1.template +57 -0
- solokit/templates/dashboard_refine/tier-1-essential/tests/setup.ts +26 -0
- solokit/templates/dashboard_refine/tier-1-essential/tests/unit/example.test.tsx +73 -0
- solokit/templates/dashboard_refine/tier-2-standard/package.json.tier2.template +62 -0
- solokit/templates/dashboard_refine/tier-3-comprehensive/eslint.config.mjs +22 -0
- solokit/templates/dashboard_refine/tier-3-comprehensive/package.json.tier3.template +79 -0
- solokit/templates/dashboard_refine/tier-3-comprehensive/playwright.config.ts +66 -0
- solokit/templates/dashboard_refine/tier-3-comprehensive/stryker.conf.json +38 -0
- solokit/templates/dashboard_refine/tier-3-comprehensive/tests/e2e/dashboard.spec.ts +88 -0
- solokit/templates/dashboard_refine/tier-3-comprehensive/tests/e2e/user-management.spec.ts +102 -0
- solokit/templates/dashboard_refine/tier-3-comprehensive/tests/integration/dashboard.test.tsx +90 -0
- solokit/templates/dashboard_refine/tier-3-comprehensive/type-coverage.json +16 -0
- solokit/templates/dashboard_refine/tier-4-production/instrumentation.ts +9 -0
- solokit/templates/dashboard_refine/tier-4-production/k6/dashboard-load-test.js +70 -0
- solokit/templates/dashboard_refine/tier-4-production/next.config.ts +46 -0
- solokit/templates/dashboard_refine/tier-4-production/package.json.tier4.template +89 -0
- solokit/templates/dashboard_refine/tier-4-production/sentry.client.config.ts +26 -0
- solokit/templates/dashboard_refine/tier-4-production/sentry.edge.config.ts +11 -0
- solokit/templates/dashboard_refine/tier-4-production/sentry.server.config.ts +11 -0
- solokit/templates/deployment_spec.md +500 -0
- solokit/templates/feature_spec.md +248 -0
- solokit/templates/fullstack_nextjs/base/.gitignore +36 -0
- solokit/templates/fullstack_nextjs/base/app/api/example/route.ts +65 -0
- solokit/templates/fullstack_nextjs/base/app/globals.css +27 -0
- solokit/templates/fullstack_nextjs/base/app/layout.tsx +20 -0
- solokit/templates/fullstack_nextjs/base/app/page.tsx +32 -0
- solokit/templates/fullstack_nextjs/base/components/example-component.tsx +20 -0
- solokit/templates/fullstack_nextjs/base/lib/prisma.ts +17 -0
- solokit/templates/fullstack_nextjs/base/lib/utils.ts +13 -0
- solokit/templates/fullstack_nextjs/base/lib/validations.ts +20 -0
- solokit/templates/fullstack_nextjs/base/next.config.ts +7 -0
- solokit/templates/fullstack_nextjs/base/package.json.template +32 -0
- solokit/templates/fullstack_nextjs/base/postcss.config.mjs +8 -0
- solokit/templates/fullstack_nextjs/base/prisma/schema.prisma +21 -0
- solokit/templates/fullstack_nextjs/base/tailwind.config.ts +19 -0
- solokit/templates/fullstack_nextjs/base/tsconfig.json +27 -0
- solokit/templates/fullstack_nextjs/docker/Dockerfile +60 -0
- solokit/templates/fullstack_nextjs/docker/docker-compose.prod.yml +57 -0
- solokit/templates/fullstack_nextjs/docker/docker-compose.yml +47 -0
- solokit/templates/fullstack_nextjs/tier-1-essential/.eslintrc.json +7 -0
- solokit/templates/fullstack_nextjs/tier-1-essential/jest.config.ts +17 -0
- solokit/templates/fullstack_nextjs/tier-1-essential/jest.setup.ts +1 -0
- solokit/templates/fullstack_nextjs/tier-1-essential/package.json.tier1.template +48 -0
- solokit/templates/fullstack_nextjs/tier-1-essential/tests/api/example.test.ts +88 -0
- solokit/templates/fullstack_nextjs/tier-1-essential/tests/setup.ts +22 -0
- solokit/templates/fullstack_nextjs/tier-1-essential/tests/unit/example.test.tsx +22 -0
- solokit/templates/fullstack_nextjs/tier-2-standard/package.json.tier2.template +52 -0
- solokit/templates/fullstack_nextjs/tier-3-comprehensive/eslint.config.mjs +39 -0
- solokit/templates/fullstack_nextjs/tier-3-comprehensive/package.json.tier3.template +68 -0
- solokit/templates/fullstack_nextjs/tier-3-comprehensive/playwright.config.ts +66 -0
- solokit/templates/fullstack_nextjs/tier-3-comprehensive/stryker.conf.json +33 -0
- solokit/templates/fullstack_nextjs/tier-3-comprehensive/tests/e2e/flow.spec.ts +59 -0
- solokit/templates/fullstack_nextjs/tier-3-comprehensive/tests/integration/api.test.ts +165 -0
- solokit/templates/fullstack_nextjs/tier-3-comprehensive/type-coverage.json +12 -0
- solokit/templates/fullstack_nextjs/tier-4-production/instrumentation.ts +9 -0
- solokit/templates/fullstack_nextjs/tier-4-production/k6/load-test.js +45 -0
- solokit/templates/fullstack_nextjs/tier-4-production/next.config.ts +46 -0
- solokit/templates/fullstack_nextjs/tier-4-production/package.json.tier4.template +77 -0
- solokit/templates/fullstack_nextjs/tier-4-production/sentry.client.config.ts +26 -0
- solokit/templates/fullstack_nextjs/tier-4-production/sentry.edge.config.ts +11 -0
- solokit/templates/fullstack_nextjs/tier-4-production/sentry.server.config.ts +11 -0
- solokit/templates/git-hooks/prepare-commit-msg +24 -0
- solokit/templates/integration_test_spec.md +363 -0
- solokit/templates/learnings.json +15 -0
- solokit/templates/ml_ai_fastapi/base/.gitignore +104 -0
- solokit/templates/ml_ai_fastapi/base/alembic/env.py +96 -0
- solokit/templates/ml_ai_fastapi/base/alembic.ini +114 -0
- solokit/templates/ml_ai_fastapi/base/pyproject.toml.template +91 -0
- solokit/templates/ml_ai_fastapi/base/requirements.txt.template +28 -0
- solokit/templates/ml_ai_fastapi/base/src/__init__.py +5 -0
- solokit/templates/ml_ai_fastapi/base/src/api/__init__.py +3 -0
- solokit/templates/ml_ai_fastapi/base/src/api/dependencies.py +20 -0
- solokit/templates/ml_ai_fastapi/base/src/api/routes/__init__.py +3 -0
- solokit/templates/ml_ai_fastapi/base/src/api/routes/example.py +134 -0
- solokit/templates/ml_ai_fastapi/base/src/api/routes/health.py +66 -0
- solokit/templates/ml_ai_fastapi/base/src/core/__init__.py +3 -0
- solokit/templates/ml_ai_fastapi/base/src/core/config.py +64 -0
- solokit/templates/ml_ai_fastapi/base/src/core/database.py +50 -0
- solokit/templates/ml_ai_fastapi/base/src/main.py +64 -0
- solokit/templates/ml_ai_fastapi/base/src/models/__init__.py +7 -0
- solokit/templates/ml_ai_fastapi/base/src/models/example.py +61 -0
- solokit/templates/ml_ai_fastapi/base/src/services/__init__.py +3 -0
- solokit/templates/ml_ai_fastapi/base/src/services/example.py +115 -0
- solokit/templates/ml_ai_fastapi/docker/Dockerfile +59 -0
- solokit/templates/ml_ai_fastapi/docker/docker-compose.prod.yml +112 -0
- solokit/templates/ml_ai_fastapi/docker/docker-compose.yml +77 -0
- solokit/templates/ml_ai_fastapi/tier-1-essential/pyproject.toml.tier1.template +112 -0
- solokit/templates/ml_ai_fastapi/tier-1-essential/pyrightconfig.json +41 -0
- solokit/templates/ml_ai_fastapi/tier-1-essential/pytest.ini +69 -0
- solokit/templates/ml_ai_fastapi/tier-1-essential/requirements-dev.txt +17 -0
- solokit/templates/ml_ai_fastapi/tier-1-essential/ruff.toml +81 -0
- solokit/templates/ml_ai_fastapi/tier-1-essential/tests/__init__.py +3 -0
- solokit/templates/ml_ai_fastapi/tier-1-essential/tests/conftest.py +72 -0
- solokit/templates/ml_ai_fastapi/tier-1-essential/tests/test_main.py +49 -0
- solokit/templates/ml_ai_fastapi/tier-1-essential/tests/unit/__init__.py +3 -0
- solokit/templates/ml_ai_fastapi/tier-1-essential/tests/unit/test_example.py +113 -0
- solokit/templates/ml_ai_fastapi/tier-2-standard/pyproject.toml.tier2.template +130 -0
- solokit/templates/ml_ai_fastapi/tier-3-comprehensive/locustfile.py +99 -0
- solokit/templates/ml_ai_fastapi/tier-3-comprehensive/mutmut_config.py +53 -0
- solokit/templates/ml_ai_fastapi/tier-3-comprehensive/pyproject.toml.tier3.template +150 -0
- solokit/templates/ml_ai_fastapi/tier-3-comprehensive/tests/integration/__init__.py +3 -0
- solokit/templates/ml_ai_fastapi/tier-3-comprehensive/tests/integration/conftest.py +74 -0
- solokit/templates/ml_ai_fastapi/tier-3-comprehensive/tests/integration/test_api.py +131 -0
- solokit/templates/ml_ai_fastapi/tier-4-production/pyproject.toml.tier4.template +162 -0
- solokit/templates/ml_ai_fastapi/tier-4-production/requirements-prod.txt +25 -0
- solokit/templates/ml_ai_fastapi/tier-4-production/src/api/routes/metrics.py +19 -0
- solokit/templates/ml_ai_fastapi/tier-4-production/src/core/logging.py +74 -0
- solokit/templates/ml_ai_fastapi/tier-4-production/src/core/monitoring.py +68 -0
- solokit/templates/ml_ai_fastapi/tier-4-production/src/core/sentry.py +66 -0
- solokit/templates/ml_ai_fastapi/tier-4-production/src/middleware/__init__.py +3 -0
- solokit/templates/ml_ai_fastapi/tier-4-production/src/middleware/logging.py +79 -0
- solokit/templates/ml_ai_fastapi/tier-4-production/src/middleware/tracing.py +60 -0
- solokit/templates/refactor_spec.md +287 -0
- solokit/templates/saas_t3/base/.gitignore +36 -0
- solokit/templates/saas_t3/base/app/api/trpc/[trpc]/route.ts +33 -0
- solokit/templates/saas_t3/base/app/globals.css +27 -0
- solokit/templates/saas_t3/base/app/layout.tsx +23 -0
- solokit/templates/saas_t3/base/app/page.tsx +31 -0
- solokit/templates/saas_t3/base/lib/api.tsx +77 -0
- solokit/templates/saas_t3/base/lib/utils.ts +13 -0
- solokit/templates/saas_t3/base/next.config.ts +7 -0
- solokit/templates/saas_t3/base/package.json.template +38 -0
- solokit/templates/saas_t3/base/postcss.config.mjs +8 -0
- solokit/templates/saas_t3/base/prisma/schema.prisma +20 -0
- solokit/templates/saas_t3/base/server/api/root.ts +19 -0
- solokit/templates/saas_t3/base/server/api/routers/example.ts +28 -0
- solokit/templates/saas_t3/base/server/api/trpc.ts +52 -0
- solokit/templates/saas_t3/base/server/db.ts +17 -0
- solokit/templates/saas_t3/base/tailwind.config.ts +19 -0
- solokit/templates/saas_t3/base/tsconfig.json +27 -0
- solokit/templates/saas_t3/docker/Dockerfile +60 -0
- solokit/templates/saas_t3/docker/docker-compose.prod.yml +59 -0
- solokit/templates/saas_t3/docker/docker-compose.yml +49 -0
- solokit/templates/saas_t3/tier-1-essential/.eslintrc.json +7 -0
- solokit/templates/saas_t3/tier-1-essential/jest.config.ts +17 -0
- solokit/templates/saas_t3/tier-1-essential/jest.setup.ts +1 -0
- solokit/templates/saas_t3/tier-1-essential/package.json.tier1.template +54 -0
- solokit/templates/saas_t3/tier-1-essential/tests/setup.ts +22 -0
- solokit/templates/saas_t3/tier-1-essential/tests/unit/example.test.tsx +24 -0
- solokit/templates/saas_t3/tier-2-standard/package.json.tier2.template +58 -0
- solokit/templates/saas_t3/tier-3-comprehensive/eslint.config.mjs +39 -0
- solokit/templates/saas_t3/tier-3-comprehensive/package.json.tier3.template +74 -0
- solokit/templates/saas_t3/tier-3-comprehensive/playwright.config.ts +66 -0
- solokit/templates/saas_t3/tier-3-comprehensive/stryker.conf.json +34 -0
- solokit/templates/saas_t3/tier-3-comprehensive/tests/e2e/home.spec.ts +41 -0
- solokit/templates/saas_t3/tier-3-comprehensive/tests/integration/api.test.ts +44 -0
- solokit/templates/saas_t3/tier-3-comprehensive/type-coverage.json +12 -0
- solokit/templates/saas_t3/tier-4-production/instrumentation.ts +9 -0
- solokit/templates/saas_t3/tier-4-production/k6/load-test.js +51 -0
- solokit/templates/saas_t3/tier-4-production/next.config.ts +46 -0
- solokit/templates/saas_t3/tier-4-production/package.json.tier4.template +83 -0
- solokit/templates/saas_t3/tier-4-production/sentry.client.config.ts +26 -0
- solokit/templates/saas_t3/tier-4-production/sentry.edge.config.ts +11 -0
- solokit/templates/saas_t3/tier-4-production/sentry.server.config.ts +11 -0
- solokit/templates/saas_t3/tier-4-production/vercel.json +37 -0
- solokit/templates/security_spec.md +287 -0
- solokit/templates/stack-versions.yaml +617 -0
- solokit/templates/status_update.json +6 -0
- solokit/templates/template-registry.json +257 -0
- solokit/templates/work_items.json +11 -0
- solokit/testing/__init__.py +1 -0
- solokit/testing/integration_runner.py +550 -0
- solokit/testing/performance.py +637 -0
- solokit/visualization/__init__.py +1 -0
- solokit/visualization/dependency_graph.py +788 -0
- solokit/work_items/__init__.py +1 -0
- solokit/work_items/creator.py +217 -0
- solokit/work_items/delete.py +264 -0
- solokit/work_items/get_dependencies.py +185 -0
- solokit/work_items/get_dependents.py +113 -0
- solokit/work_items/get_metadata.py +121 -0
- solokit/work_items/get_next_recommendations.py +133 -0
- solokit/work_items/manager.py +235 -0
- solokit/work_items/milestones.py +137 -0
- solokit/work_items/query.py +376 -0
- solokit/work_items/repository.py +267 -0
- solokit/work_items/scheduler.py +184 -0
- solokit/work_items/spec_parser.py +838 -0
- solokit/work_items/spec_validator.py +493 -0
- solokit/work_items/updater.py +157 -0
- solokit/work_items/validator.py +205 -0
- solokit-0.1.1.dist-info/METADATA +640 -0
- solokit-0.1.1.dist-info/RECORD +323 -0
- solokit-0.1.1.dist-info/WHEEL +5 -0
- solokit-0.1.1.dist-info/entry_points.txt +2 -0
- solokit-0.1.1.dist-info/licenses/LICENSE +21 -0
- solokit-0.1.1.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,411 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""
|
|
3
|
+
Deployment execution framework.
|
|
4
|
+
|
|
5
|
+
Provides automated deployment execution with:
|
|
6
|
+
- Pre-deployment validation
|
|
7
|
+
- Deployment execution with comprehensive logging
|
|
8
|
+
- Smoke test execution
|
|
9
|
+
- Rollback automation on failure
|
|
10
|
+
- Deployment state tracking
|
|
11
|
+
"""
|
|
12
|
+
|
|
13
|
+
from __future__ import annotations
|
|
14
|
+
|
|
15
|
+
import json
|
|
16
|
+
from datetime import datetime
|
|
17
|
+
from pathlib import Path
|
|
18
|
+
from typing import Any
|
|
19
|
+
|
|
20
|
+
from solokit.core.error_handlers import convert_file_errors, log_errors
|
|
21
|
+
from solokit.core.exceptions import (
|
|
22
|
+
DeploymentStepError,
|
|
23
|
+
ErrorCode,
|
|
24
|
+
FileOperationError,
|
|
25
|
+
PreDeploymentCheckError,
|
|
26
|
+
RollbackError,
|
|
27
|
+
SmokeTestError,
|
|
28
|
+
)
|
|
29
|
+
from solokit.core.output import get_output
|
|
30
|
+
|
|
31
|
+
output = get_output()
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
class DeploymentExecutor:
|
|
35
|
+
"""Deployment execution and validation."""
|
|
36
|
+
|
|
37
|
+
def __init__(self, work_item: dict[str, Any], config_path: Path | None = None) -> None:
|
|
38
|
+
"""Initialize deployment executor."""
|
|
39
|
+
if config_path is None:
|
|
40
|
+
config_path = Path(".session/config.json")
|
|
41
|
+
self.work_item = work_item
|
|
42
|
+
self.config = self._load_config(config_path)
|
|
43
|
+
self.deployment_log: list[dict[str, Any]] = []
|
|
44
|
+
|
|
45
|
+
@convert_file_errors
|
|
46
|
+
def _load_config(self, config_path: Path) -> dict[str, Any]:
|
|
47
|
+
"""
|
|
48
|
+
Load deployment configuration.
|
|
49
|
+
|
|
50
|
+
Args:
|
|
51
|
+
config_path: Path to configuration file
|
|
52
|
+
|
|
53
|
+
Returns:
|
|
54
|
+
dict: Deployment configuration
|
|
55
|
+
|
|
56
|
+
Raises:
|
|
57
|
+
FileOperationError: If config file cannot be read or parsed
|
|
58
|
+
"""
|
|
59
|
+
if not config_path.exists():
|
|
60
|
+
return self._default_config()
|
|
61
|
+
|
|
62
|
+
try:
|
|
63
|
+
with open(config_path) as f:
|
|
64
|
+
config = json.load(f)
|
|
65
|
+
except json.JSONDecodeError as e:
|
|
66
|
+
raise FileOperationError(
|
|
67
|
+
operation="parse", file_path=str(config_path), details=f"Invalid JSON: {e}", cause=e
|
|
68
|
+
) from e
|
|
69
|
+
|
|
70
|
+
deployment_config = config.get("deployment", self._default_config())
|
|
71
|
+
return deployment_config # type: ignore[no-any-return]
|
|
72
|
+
|
|
73
|
+
def _default_config(self) -> dict[str, Any]:
|
|
74
|
+
"""Default deployment configuration."""
|
|
75
|
+
return {
|
|
76
|
+
"pre_deployment_checks": {
|
|
77
|
+
"integration_tests": True,
|
|
78
|
+
"security_scans": True,
|
|
79
|
+
"environment_validation": True,
|
|
80
|
+
},
|
|
81
|
+
"smoke_tests": {"enabled": True, "timeout": 300, "retry_count": 3},
|
|
82
|
+
"rollback": {
|
|
83
|
+
"automatic": True,
|
|
84
|
+
"on_smoke_test_failure": True,
|
|
85
|
+
"on_error_threshold": True,
|
|
86
|
+
"error_threshold_percent": 5,
|
|
87
|
+
},
|
|
88
|
+
"environments": {
|
|
89
|
+
"staging": {"auto_deploy": True, "require_approval": False},
|
|
90
|
+
"production": {"auto_deploy": False, "require_approval": True},
|
|
91
|
+
},
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
@log_errors()
|
|
95
|
+
def pre_deployment_validation(self) -> dict[str, Any]:
|
|
96
|
+
"""
|
|
97
|
+
Run pre-deployment validation checks.
|
|
98
|
+
|
|
99
|
+
Returns:
|
|
100
|
+
dict: Validation results with checks and status
|
|
101
|
+
|
|
102
|
+
Raises:
|
|
103
|
+
PreDeploymentCheckError: If any validation check fails
|
|
104
|
+
"""
|
|
105
|
+
results: dict[str, Any] = {"checks": [], "passed": True}
|
|
106
|
+
failed_checks: list[str] = []
|
|
107
|
+
|
|
108
|
+
# Check integration tests
|
|
109
|
+
if self.config["pre_deployment_checks"].get("integration_tests"):
|
|
110
|
+
tests_passed = self._check_integration_tests()
|
|
111
|
+
results["checks"].append({"name": "Integration Tests", "passed": tests_passed})
|
|
112
|
+
if not tests_passed:
|
|
113
|
+
results["passed"] = False
|
|
114
|
+
failed_checks.append("Integration Tests")
|
|
115
|
+
|
|
116
|
+
# Check security scans
|
|
117
|
+
if self.config["pre_deployment_checks"].get("security_scans"):
|
|
118
|
+
scans_passed = self._check_security_scans()
|
|
119
|
+
results["checks"].append({"name": "Security Scans", "passed": scans_passed})
|
|
120
|
+
if not scans_passed:
|
|
121
|
+
results["passed"] = False
|
|
122
|
+
failed_checks.append("Security Scans")
|
|
123
|
+
|
|
124
|
+
# Check environment readiness
|
|
125
|
+
if self.config["pre_deployment_checks"].get("environment_validation"):
|
|
126
|
+
env_ready = self._check_environment_readiness()
|
|
127
|
+
results["checks"].append({"name": "Environment Readiness", "passed": env_ready})
|
|
128
|
+
if not env_ready:
|
|
129
|
+
results["passed"] = False
|
|
130
|
+
failed_checks.append("Environment Readiness")
|
|
131
|
+
|
|
132
|
+
self._log("Pre-deployment validation", results)
|
|
133
|
+
|
|
134
|
+
# Raise exception if any checks failed
|
|
135
|
+
if not results["passed"]:
|
|
136
|
+
raise PreDeploymentCheckError(
|
|
137
|
+
check_name=", ".join(failed_checks),
|
|
138
|
+
details=f"{len(failed_checks)} check(s) failed",
|
|
139
|
+
context={"results": results, "failed_checks": failed_checks},
|
|
140
|
+
)
|
|
141
|
+
|
|
142
|
+
return results
|
|
143
|
+
|
|
144
|
+
@log_errors()
|
|
145
|
+
def execute_deployment(self, dry_run: bool = False) -> dict[str, Any]:
|
|
146
|
+
"""
|
|
147
|
+
Execute deployment procedure.
|
|
148
|
+
|
|
149
|
+
Args:
|
|
150
|
+
dry_run: If True, simulate deployment without actual execution
|
|
151
|
+
|
|
152
|
+
Returns:
|
|
153
|
+
dict: Deployment results with steps and timestamps
|
|
154
|
+
|
|
155
|
+
Raises:
|
|
156
|
+
DeploymentStepError: If any deployment step fails
|
|
157
|
+
"""
|
|
158
|
+
results: dict[str, Any] = {
|
|
159
|
+
"started_at": datetime.now().isoformat(),
|
|
160
|
+
"steps": [],
|
|
161
|
+
"success": True,
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
self._log("Deployment started", {"dry_run": dry_run})
|
|
165
|
+
|
|
166
|
+
# Parse deployment steps from work item
|
|
167
|
+
deployment_steps = self._parse_deployment_steps()
|
|
168
|
+
|
|
169
|
+
for i, step in enumerate(deployment_steps, 1):
|
|
170
|
+
self._log(f"Executing step {i}", {"step": step})
|
|
171
|
+
|
|
172
|
+
if not dry_run:
|
|
173
|
+
step_success = self._execute_deployment_step(step)
|
|
174
|
+
else:
|
|
175
|
+
step_success = True # Simulate success in dry run
|
|
176
|
+
|
|
177
|
+
results["steps"].append({"number": i, "description": step, "success": step_success})
|
|
178
|
+
|
|
179
|
+
if not step_success:
|
|
180
|
+
results["success"] = False
|
|
181
|
+
results["failed_at_step"] = i
|
|
182
|
+
self._log("Deployment failed", {"step": i, "description": step})
|
|
183
|
+
raise DeploymentStepError(
|
|
184
|
+
step_number=i, step_description=step, context={"results": results}
|
|
185
|
+
)
|
|
186
|
+
|
|
187
|
+
results["completed_at"] = datetime.now().isoformat()
|
|
188
|
+
return results
|
|
189
|
+
|
|
190
|
+
@log_errors()
|
|
191
|
+
def run_smoke_tests(self) -> dict[str, Any]:
|
|
192
|
+
"""
|
|
193
|
+
Run smoke tests to verify deployment.
|
|
194
|
+
|
|
195
|
+
Returns:
|
|
196
|
+
dict: Test results with status
|
|
197
|
+
|
|
198
|
+
Raises:
|
|
199
|
+
SmokeTestError: If any smoke test fails
|
|
200
|
+
"""
|
|
201
|
+
config = self.config["smoke_tests"]
|
|
202
|
+
results: dict[str, Any] = {"tests": [], "passed": True}
|
|
203
|
+
|
|
204
|
+
if not config.get("enabled"):
|
|
205
|
+
return {"status": "skipped"}
|
|
206
|
+
|
|
207
|
+
self._log("Running smoke tests", config)
|
|
208
|
+
|
|
209
|
+
# Parse smoke tests from work item
|
|
210
|
+
smoke_tests = self._parse_smoke_tests()
|
|
211
|
+
failed_tests = []
|
|
212
|
+
|
|
213
|
+
for test in smoke_tests:
|
|
214
|
+
test_passed = self._execute_smoke_test(
|
|
215
|
+
test,
|
|
216
|
+
timeout=config.get("timeout", 300),
|
|
217
|
+
retry_count=config.get("retry_count", 3),
|
|
218
|
+
)
|
|
219
|
+
|
|
220
|
+
test_name = test.get("name", "unknown")
|
|
221
|
+
results["tests"].append({"name": test_name, "passed": test_passed})
|
|
222
|
+
|
|
223
|
+
if not test_passed:
|
|
224
|
+
results["passed"] = False
|
|
225
|
+
failed_tests.append(test_name)
|
|
226
|
+
|
|
227
|
+
self._log("Smoke tests completed", results)
|
|
228
|
+
|
|
229
|
+
# Raise exception if any tests failed
|
|
230
|
+
if not results["passed"]:
|
|
231
|
+
raise SmokeTestError(
|
|
232
|
+
test_name=", ".join(failed_tests),
|
|
233
|
+
details=f"{len(failed_tests)} test(s) failed",
|
|
234
|
+
context={"results": results, "failed_tests": failed_tests},
|
|
235
|
+
)
|
|
236
|
+
|
|
237
|
+
return results
|
|
238
|
+
|
|
239
|
+
@log_errors()
|
|
240
|
+
def rollback(self) -> dict[str, Any]:
|
|
241
|
+
"""
|
|
242
|
+
Execute rollback procedure.
|
|
243
|
+
|
|
244
|
+
Returns:
|
|
245
|
+
dict: Rollback results with steps and timestamps
|
|
246
|
+
|
|
247
|
+
Raises:
|
|
248
|
+
RollbackError: If any rollback step fails
|
|
249
|
+
"""
|
|
250
|
+
results: dict[str, Any] = {
|
|
251
|
+
"started_at": datetime.now().isoformat(),
|
|
252
|
+
"steps": [],
|
|
253
|
+
"success": True,
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
self._log("Rollback started", {})
|
|
257
|
+
|
|
258
|
+
# Parse rollback steps from work item
|
|
259
|
+
rollback_steps = self._parse_rollback_steps()
|
|
260
|
+
|
|
261
|
+
for i, step in enumerate(rollback_steps, 1):
|
|
262
|
+
self._log(f"Executing rollback step {i}", {"step": step})
|
|
263
|
+
|
|
264
|
+
step_success = self._execute_rollback_step(step)
|
|
265
|
+
|
|
266
|
+
results["steps"].append({"number": i, "description": step, "success": step_success})
|
|
267
|
+
|
|
268
|
+
if not step_success:
|
|
269
|
+
results["success"] = False
|
|
270
|
+
results["failed_at_step"] = i
|
|
271
|
+
raise RollbackError(
|
|
272
|
+
step=step,
|
|
273
|
+
details=f"Rollback failed at step {i}",
|
|
274
|
+
context={"results": results, "step_number": i},
|
|
275
|
+
)
|
|
276
|
+
|
|
277
|
+
results["completed_at"] = datetime.now().isoformat()
|
|
278
|
+
self._log("Rollback completed", results)
|
|
279
|
+
|
|
280
|
+
return results
|
|
281
|
+
|
|
282
|
+
def _check_integration_tests(self) -> bool:
|
|
283
|
+
"""Check if integration tests passed."""
|
|
284
|
+
# NOTE: Framework stub - Integrate with quality_gates.py for actual test execution
|
|
285
|
+
# In production, this should call QualityGates.run_tests() and check results
|
|
286
|
+
return True
|
|
287
|
+
|
|
288
|
+
def _check_security_scans(self) -> bool:
|
|
289
|
+
"""Check if security scans passed."""
|
|
290
|
+
# NOTE: Framework stub - Integrate with quality_gates.py for actual security scanning
|
|
291
|
+
# In production, this should call QualityGates.run_security_gate() and check results
|
|
292
|
+
return True
|
|
293
|
+
|
|
294
|
+
def _check_environment_readiness(self) -> bool:
|
|
295
|
+
"""Check if environment is ready."""
|
|
296
|
+
# NOTE: Framework stub - Integrate with environment_validator.py for actual validation
|
|
297
|
+
# In production, this should instantiate EnvironmentValidator and call validate_all()
|
|
298
|
+
return True
|
|
299
|
+
|
|
300
|
+
def _parse_deployment_steps(self) -> list[str]:
|
|
301
|
+
"""Parse deployment steps from work item specification."""
|
|
302
|
+
# NOTE: Framework stub - Parse deployment steps from spec file
|
|
303
|
+
# Use spec_parser.py to extract "## Deployment Steps" section
|
|
304
|
+
# and convert to structured list of commands/actions
|
|
305
|
+
return []
|
|
306
|
+
|
|
307
|
+
def _execute_deployment_step(self, step: str) -> bool:
|
|
308
|
+
"""Execute a single deployment step."""
|
|
309
|
+
# NOTE: Framework stub - Implement project-specific deployment step execution
|
|
310
|
+
# Step types might include: shell commands, API calls, file operations, etc.
|
|
311
|
+
# Should handle errors, logging, and timeouts appropriately
|
|
312
|
+
return True
|
|
313
|
+
|
|
314
|
+
def _parse_smoke_tests(self) -> list[dict]:
|
|
315
|
+
"""Parse smoke tests from work item specification."""
|
|
316
|
+
# NOTE: Framework stub - Parse smoke test definitions from spec file
|
|
317
|
+
# Use spec_parser.py to extract "## Smoke Tests" section
|
|
318
|
+
# and convert to list of {name, command, expected_result} dicts
|
|
319
|
+
return []
|
|
320
|
+
|
|
321
|
+
def _execute_smoke_test(self, test: dict, timeout: int, retry_count: int) -> bool:
|
|
322
|
+
"""Execute a single smoke test with retries."""
|
|
323
|
+
# NOTE: Framework stub - Implement project-specific smoke test execution
|
|
324
|
+
# Should execute test command, check result, handle retries and timeouts
|
|
325
|
+
# Common tests: HTTP endpoint checks, database queries, cache reads
|
|
326
|
+
return True
|
|
327
|
+
|
|
328
|
+
def _parse_rollback_steps(self) -> list[str]:
|
|
329
|
+
"""Parse rollback steps from work item specification."""
|
|
330
|
+
# NOTE: Framework stub - Parse rollback procedure from spec file
|
|
331
|
+
# Use spec_parser.py to extract "## Rollback Procedure" section
|
|
332
|
+
# and convert to ordered list of rollback commands/actions
|
|
333
|
+
return []
|
|
334
|
+
|
|
335
|
+
def _execute_rollback_step(self, step: str) -> bool:
|
|
336
|
+
"""Execute a single rollback step."""
|
|
337
|
+
# NOTE: Framework stub - Implement project-specific rollback step execution
|
|
338
|
+
# Must be highly reliable since this runs during failure scenarios
|
|
339
|
+
# Should handle partial rollbacks and verification of rollback success
|
|
340
|
+
return True
|
|
341
|
+
|
|
342
|
+
def _log(self, event: str, data: dict[str, Any]) -> None:
|
|
343
|
+
"""Log deployment event."""
|
|
344
|
+
log_entry = {
|
|
345
|
+
"timestamp": datetime.now().isoformat(),
|
|
346
|
+
"event": event,
|
|
347
|
+
"data": data,
|
|
348
|
+
}
|
|
349
|
+
self.deployment_log.append(log_entry)
|
|
350
|
+
|
|
351
|
+
def get_deployment_log(self) -> list[dict[str, Any]]:
|
|
352
|
+
"""Get deployment log."""
|
|
353
|
+
return self.deployment_log
|
|
354
|
+
|
|
355
|
+
|
|
356
|
+
def main() -> None:
|
|
357
|
+
"""
|
|
358
|
+
CLI entry point.
|
|
359
|
+
|
|
360
|
+
Raises:
|
|
361
|
+
ValidationError: If no work item ID provided
|
|
362
|
+
PreDeploymentCheckError: If pre-deployment checks fail
|
|
363
|
+
DeploymentStepError: If deployment fails
|
|
364
|
+
SmokeTestError: If smoke tests fail
|
|
365
|
+
RollbackError: If rollback fails
|
|
366
|
+
"""
|
|
367
|
+
import sys
|
|
368
|
+
|
|
369
|
+
from solokit.core.exceptions import ValidationError
|
|
370
|
+
|
|
371
|
+
if len(sys.argv) < 2:
|
|
372
|
+
raise ValidationError(
|
|
373
|
+
message="Missing required argument: work_item_id",
|
|
374
|
+
code=ErrorCode.INVALID_COMMAND,
|
|
375
|
+
remediation="Usage: deployment_executor.py <work_item_id>",
|
|
376
|
+
)
|
|
377
|
+
|
|
378
|
+
# Load work item
|
|
379
|
+
# NOTE: Framework stub - Should load work item by ID from work_items.json
|
|
380
|
+
# Use work_item_manager.WorkItemManager to load actual work item data
|
|
381
|
+
|
|
382
|
+
executor = DeploymentExecutor(work_item={})
|
|
383
|
+
|
|
384
|
+
try:
|
|
385
|
+
# Pre-deployment validation
|
|
386
|
+
executor.pre_deployment_validation()
|
|
387
|
+
|
|
388
|
+
# Execute deployment
|
|
389
|
+
executor.execute_deployment()
|
|
390
|
+
|
|
391
|
+
# Run smoke tests
|
|
392
|
+
executor.run_smoke_tests()
|
|
393
|
+
|
|
394
|
+
output.info("Deployment successful!")
|
|
395
|
+
|
|
396
|
+
except (DeploymentStepError, SmokeTestError) as e:
|
|
397
|
+
# Attempt rollback on deployment or smoke test failure
|
|
398
|
+
output.info(f"Error: {e.message}")
|
|
399
|
+
output.info("Initiating rollback...")
|
|
400
|
+
try:
|
|
401
|
+
executor.rollback()
|
|
402
|
+
output.info("Rollback completed successfully")
|
|
403
|
+
except RollbackError as rollback_err:
|
|
404
|
+
output.info(f"Rollback failed: {rollback_err.message}")
|
|
405
|
+
raise
|
|
406
|
+
# Re-raise original error after successful rollback
|
|
407
|
+
raise
|
|
408
|
+
|
|
409
|
+
|
|
410
|
+
if __name__ == "__main__":
|
|
411
|
+
main()
|
solokit/git/__init__.py
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"""Git integration for branch management and status tracking."""
|