roadmap-cli 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.
- roadmap/__init__.py +27 -0
- roadmap/adapters/__init__.py +1 -0
- roadmap/adapters/base_paginated_adapter.py +117 -0
- roadmap/adapters/cli/__init__.py +249 -0
- roadmap/adapters/cli/analysis/__init__.py +5 -0
- roadmap/adapters/cli/analysis/commands.py +370 -0
- roadmap/adapters/cli/analysis/presenter.py +125 -0
- roadmap/adapters/cli/archive_operations.py +86 -0
- roadmap/adapters/cli/cli_command_helpers.py +109 -0
- roadmap/adapters/cli/cli_confirmations.py +74 -0
- roadmap/adapters/cli/cli_error_handlers.py +146 -0
- roadmap/adapters/cli/cli_validators.py +39 -0
- roadmap/adapters/cli/commands/sync_status.py +308 -0
- roadmap/adapters/cli/comment/__init__.py +5 -0
- roadmap/adapters/cli/comment/commands.py +106 -0
- roadmap/adapters/cli/config/__init__.py +5 -0
- roadmap/adapters/cli/config/commands.py +198 -0
- roadmap/adapters/cli/console_exports.py +10 -0
- roadmap/adapters/cli/core.py +26 -0
- roadmap/adapters/cli/crud/__init__.py +47 -0
- roadmap/adapters/cli/crud/base_archive.py +208 -0
- roadmap/adapters/cli/crud/base_create.py +154 -0
- roadmap/adapters/cli/crud/base_delete.py +155 -0
- roadmap/adapters/cli/crud/base_restore.py +283 -0
- roadmap/adapters/cli/crud/base_update.py +193 -0
- roadmap/adapters/cli/crud/crud_helpers.py +206 -0
- roadmap/adapters/cli/crud/crud_utils.py +152 -0
- roadmap/adapters/cli/crud/entity_builders.py +374 -0
- roadmap/adapters/cli/data/__init__.py +8 -0
- roadmap/adapters/cli/data/commands.py +149 -0
- roadmap/adapters/cli/decorators.py +208 -0
- roadmap/adapters/cli/dtos/__init__.py +155 -0
- roadmap/adapters/cli/exception_handler.py +105 -0
- roadmap/adapters/cli/git/__init__.py +8 -0
- roadmap/adapters/cli/git/commands.py +475 -0
- roadmap/adapters/cli/git/handlers/__init__.py +1 -0
- roadmap/adapters/cli/git/handlers/git_authentication_handler.py +124 -0
- roadmap/adapters/cli/git/handlers/git_branch_handler.py +238 -0
- roadmap/adapters/cli/git/handlers/git_connectivity_handler.py +122 -0
- roadmap/adapters/cli/git/handlers/git_hooks_handler.py +105 -0
- roadmap/adapters/cli/git/hooks_config.py +327 -0
- roadmap/adapters/cli/git/status_display.py +158 -0
- roadmap/adapters/cli/health/__init__.py +8 -0
- roadmap/adapters/cli/health/db_integrity.py +204 -0
- roadmap/adapters/cli/health/enhancer.py +227 -0
- roadmap/adapters/cli/health/fixer.py +295 -0
- roadmap/adapters/cli/health/fixers/__init__.py +10 -0
- roadmap/adapters/cli/health/fixers/corrupted_comments_fixer.py +231 -0
- roadmap/adapters/cli/health/fixers/data_integrity_fixer.py +137 -0
- roadmap/adapters/cli/health/fixers/duplicate_issues_fixer.py +138 -0
- roadmap/adapters/cli/health/fixers/folder_structure_fixer.py +114 -0
- roadmap/adapters/cli/health/fixers/label_normalization_fixer.py +162 -0
- roadmap/adapters/cli/health/fixers/milestone_name_normalization_fixer.py +181 -0
- roadmap/adapters/cli/health/fixers/milestone_naming_compliance_fixer.py +244 -0
- roadmap/adapters/cli/health/fixers/milestone_parse_error_fixer.py +162 -0
- roadmap/adapters/cli/health/fixers/milestone_validation_fixer.py +211 -0
- roadmap/adapters/cli/health/fixers/old_backups_fixer.py +135 -0
- roadmap/adapters/cli/health/fixers/orphaned_issues_fixer.py +252 -0
- roadmap/adapters/cli/health/formatter.py +250 -0
- roadmap/adapters/cli/health/formatters.py +427 -0
- roadmap/adapters/cli/health/scan.py +293 -0
- roadmap/adapters/cli/init/__init__.py +5 -0
- roadmap/adapters/cli/init/commands.py +587 -0
- roadmap/adapters/cli/init.py +9 -0
- roadmap/adapters/cli/issues/__init__.py +70 -0
- roadmap/adapters/cli/issues/archive.py +137 -0
- roadmap/adapters/cli/issues/archive_class.py +253 -0
- roadmap/adapters/cli/issues/block.py +39 -0
- roadmap/adapters/cli/issues/close.py +216 -0
- roadmap/adapters/cli/issues/comment.py +178 -0
- roadmap/adapters/cli/issues/create.py +162 -0
- roadmap/adapters/cli/issues/delete.py +31 -0
- roadmap/adapters/cli/issues/deps.py +161 -0
- roadmap/adapters/cli/issues/issue_status_helpers.py +111 -0
- roadmap/adapters/cli/issues/link.py +308 -0
- roadmap/adapters/cli/issues/list.py +402 -0
- roadmap/adapters/cli/issues/lookup.py +96 -0
- roadmap/adapters/cli/issues/progress.py +102 -0
- roadmap/adapters/cli/issues/restore.py +96 -0
- roadmap/adapters/cli/issues/restore_class.py +57 -0
- roadmap/adapters/cli/issues/start.py +165 -0
- roadmap/adapters/cli/issues/sync_status.py +374 -0
- roadmap/adapters/cli/issues/unblock.py +43 -0
- roadmap/adapters/cli/issues/unlink.py +104 -0
- roadmap/adapters/cli/issues/update.py +120 -0
- roadmap/adapters/cli/issues/view.py +36 -0
- roadmap/adapters/cli/layout.py +220 -0
- roadmap/adapters/cli/mappers.py +200 -0
- roadmap/adapters/cli/milestones/__init__.py +52 -0
- roadmap/adapters/cli/milestones/archive.py +125 -0
- roadmap/adapters/cli/milestones/archive_class.py +236 -0
- roadmap/adapters/cli/milestones/assign.py +62 -0
- roadmap/adapters/cli/milestones/close.py +177 -0
- roadmap/adapters/cli/milestones/create.py +131 -0
- roadmap/adapters/cli/milestones/delete.py +27 -0
- roadmap/adapters/cli/milestones/kanban.py +123 -0
- roadmap/adapters/cli/milestones/list.py +87 -0
- roadmap/adapters/cli/milestones/recalculate.py +79 -0
- roadmap/adapters/cli/milestones/restore.py +87 -0
- roadmap/adapters/cli/milestones/restore_class.py +99 -0
- roadmap/adapters/cli/milestones/update.py +61 -0
- roadmap/adapters/cli/milestones/view.py +142 -0
- roadmap/adapters/cli/output_manager.py +319 -0
- roadmap/adapters/cli/presentation/base_presenter.py +121 -0
- roadmap/adapters/cli/presentation/cleanup_presenter.py +235 -0
- roadmap/adapters/cli/presentation/core_initialization_presenter.py +277 -0
- roadmap/adapters/cli/presentation/crud_presenter.py +134 -0
- roadmap/adapters/cli/presentation/daily_summary_presenter.py +266 -0
- roadmap/adapters/cli/presentation/issue_presenter.py +258 -0
- roadmap/adapters/cli/presentation/milestone_list_presenter.py +135 -0
- roadmap/adapters/cli/presentation/milestone_presenter.py +297 -0
- roadmap/adapters/cli/presentation/project_presenter.py +233 -0
- roadmap/adapters/cli/presentation/project_status_presenter.py +186 -0
- roadmap/adapters/cli/presentation/table_builders.py +95 -0
- roadmap/adapters/cli/projects/__init__.py +42 -0
- roadmap/adapters/cli/projects/archive.py +127 -0
- roadmap/adapters/cli/projects/archive_class.py +96 -0
- roadmap/adapters/cli/projects/close.py +94 -0
- roadmap/adapters/cli/projects/create.py +50 -0
- roadmap/adapters/cli/projects/delete.py +27 -0
- roadmap/adapters/cli/projects/list.py +227 -0
- roadmap/adapters/cli/projects/restore.py +87 -0
- roadmap/adapters/cli/projects/restore_class.py +56 -0
- roadmap/adapters/cli/projects/update.py +57 -0
- roadmap/adapters/cli/projects/view.py +157 -0
- roadmap/adapters/cli/services/__init__.py +1 -0
- roadmap/adapters/cli/services/daily_summary_service.py +313 -0
- roadmap/adapters/cli/services/export_manager.py +268 -0
- roadmap/adapters/cli/services/milestone_list_service.py +266 -0
- roadmap/adapters/cli/services/project_initialization_service.py +29 -0
- roadmap/adapters/cli/services/project_status_service.py +462 -0
- roadmap/adapters/cli/services/sync_service.py +62 -0
- roadmap/adapters/cli/status.py +408 -0
- roadmap/adapters/cli/styling.py +21 -0
- roadmap/adapters/cli/sync.py +1117 -0
- roadmap/adapters/cli/sync_context.py +432 -0
- roadmap/adapters/cli/sync_handlers/__init__.py +77 -0
- roadmap/adapters/cli/sync_handlers/apply_ops.py +522 -0
- roadmap/adapters/cli/sync_handlers/baseline_ops.py +366 -0
- roadmap/adapters/cli/sync_handlers/conflict_ops.py +161 -0
- roadmap/adapters/cli/sync_handlers/dry_run_display.py +237 -0
- roadmap/adapters/cli/sync_handlers/interactive_resolver.py +396 -0
- roadmap/adapters/cli/sync_handlers/progress_tracker.py +128 -0
- roadmap/adapters/cli/sync_metrics_command.py +190 -0
- roadmap/adapters/cli/sync_presenter.py +58 -0
- roadmap/adapters/cli/sync_validation.py +295 -0
- roadmap/adapters/cli/today.py +52 -0
- roadmap/adapters/cli/utils/click_options.py +38 -0
- roadmap/adapters/git/__init__.py +1 -0
- roadmap/adapters/git/git.py +208 -0
- roadmap/adapters/git/git_branch_manager.py +400 -0
- roadmap/adapters/git/git_command_executor.py +56 -0
- roadmap/adapters/git/git_commit_analyzer.py +334 -0
- roadmap/adapters/git/git_hooks.py +22 -0
- roadmap/adapters/git/git_hooks_manager.py +455 -0
- roadmap/adapters/git/git_repository_info.py +65 -0
- roadmap/adapters/git/hook_installer.py +106 -0
- roadmap/adapters/git/hook_registry.py +97 -0
- roadmap/adapters/git/hook_script_generator.py +58 -0
- roadmap/adapters/git/sync_monitor.py +409 -0
- roadmap/adapters/git/workflow_automation.py +286 -0
- roadmap/adapters/github/__init__.py +1 -0
- roadmap/adapters/github/github.py +360 -0
- roadmap/adapters/github/handlers/__init__.py +17 -0
- roadmap/adapters/github/handlers/base.py +337 -0
- roadmap/adapters/github/handlers/collaborators.py +165 -0
- roadmap/adapters/github/handlers/comments.py +118 -0
- roadmap/adapters/github/handlers/issues.py +150 -0
- roadmap/adapters/github/handlers/labels.py +164 -0
- roadmap/adapters/github/handlers/milestones.py +104 -0
- roadmap/adapters/persistence/__init__.py +35 -0
- roadmap/adapters/persistence/conflict_resolver.py +190 -0
- roadmap/adapters/persistence/database_manager.py +492 -0
- roadmap/adapters/persistence/entity_sync_coordinators.py +672 -0
- roadmap/adapters/persistence/file_locking.py +348 -0
- roadmap/adapters/persistence/file_parser.py +92 -0
- roadmap/adapters/persistence/file_synchronizer.py +107 -0
- roadmap/adapters/persistence/focused_managers.py +140 -0
- roadmap/adapters/persistence/git_history.py +330 -0
- roadmap/adapters/persistence/parser/__init__.py +20 -0
- roadmap/adapters/persistence/parser/frontmatter.py +152 -0
- roadmap/adapters/persistence/parser/issue.py +245 -0
- roadmap/adapters/persistence/parser/milestone.py +146 -0
- roadmap/adapters/persistence/parser/project.py +106 -0
- roadmap/adapters/persistence/persistence.py +288 -0
- roadmap/adapters/persistence/repositories/__init__.py +15 -0
- roadmap/adapters/persistence/repositories/issue_repository.py +223 -0
- roadmap/adapters/persistence/repositories/milestone_repository.py +140 -0
- roadmap/adapters/persistence/repositories/project_repository.py +168 -0
- roadmap/adapters/persistence/repositories/remote_link_repository.py +355 -0
- roadmap/adapters/persistence/repositories/sync_state_repository.py +66 -0
- roadmap/adapters/persistence/storage/__init__.py +36 -0
- roadmap/adapters/persistence/storage/conflicts.py +101 -0
- roadmap/adapters/persistence/storage/connection_manager.py +79 -0
- roadmap/adapters/persistence/storage/issue_storage.py +89 -0
- roadmap/adapters/persistence/storage/milestone_storage.py +80 -0
- roadmap/adapters/persistence/storage/project_storage.py +102 -0
- roadmap/adapters/persistence/storage/queries.py +227 -0
- roadmap/adapters/persistence/storage/state_manager.py +652 -0
- roadmap/adapters/persistence/storage/sync_state_storage.py +207 -0
- roadmap/adapters/persistence/sync_metrics_repository.py +342 -0
- roadmap/adapters/persistence/sync_orchestrator.py +293 -0
- roadmap/adapters/persistence/sync_state_tracker.py +86 -0
- roadmap/adapters/persistence/yaml_repositories.py +711 -0
- roadmap/adapters/sync/__init__.py +21 -0
- roadmap/adapters/sync/backend_factory.py +108 -0
- roadmap/adapters/sync/backends/__init__.py +15 -0
- roadmap/adapters/sync/backends/converters.py +209 -0
- roadmap/adapters/sync/backends/github_backend_helpers.py +380 -0
- roadmap/adapters/sync/backends/github_client.py +99 -0
- roadmap/adapters/sync/backends/github_sync_backend.py +792 -0
- roadmap/adapters/sync/backends/github_sync_ops.py +908 -0
- roadmap/adapters/sync/backends/services/__init__.py +1 -0
- roadmap/adapters/sync/backends/services/github_authentication_service.py +105 -0
- roadmap/adapters/sync/backends/services/github_issue_delete_service.py +423 -0
- roadmap/adapters/sync/backends/services/github_issue_dependency_service.py +63 -0
- roadmap/adapters/sync/backends/services/github_issue_fetch_service.py +158 -0
- roadmap/adapters/sync/backends/services/github_label_sync_service.py +126 -0
- roadmap/adapters/sync/backends/services/github_local_issue_sync_service.py +229 -0
- roadmap/adapters/sync/backends/services/github_milestone_fetch_service.py +204 -0
- roadmap/adapters/sync/backends/vanilla_git_sync_backend.py +198 -0
- roadmap/adapters/sync/services/__init__.py +11 -0
- roadmap/adapters/sync/services/baseline_state_handler.py +83 -0
- roadmap/adapters/sync/services/conflict_converter.py +85 -0
- roadmap/adapters/sync/services/issue_persistence_service.py +263 -0
- roadmap/adapters/sync/services/issue_state_service.py +243 -0
- roadmap/adapters/sync/services/local_change_filter.py +129 -0
- roadmap/adapters/sync/services/pull_result_processor.py +71 -0
- roadmap/adapters/sync/services/remote_issue_creation_service.py +112 -0
- roadmap/adapters/sync/services/sync_analysis_service.py +250 -0
- roadmap/adapters/sync/services/sync_authentication_service.py +57 -0
- roadmap/adapters/sync/services/sync_data_fetch_service.py +212 -0
- roadmap/adapters/sync/services/sync_linking_service.py +329 -0
- roadmap/adapters/sync/services/sync_report_service.py +62 -0
- roadmap/adapters/sync/services/sync_state_update_service.py +51 -0
- roadmap/adapters/sync/sync_cache_orchestrator.py +454 -0
- roadmap/adapters/sync/sync_merge_engine.py +416 -0
- roadmap/adapters/sync/sync_merge_orchestrator.py +1313 -0
- roadmap/adapters/sync/sync_retrieval_orchestrator.py +810 -0
- roadmap/application/services/__init__.py +1 -0
- roadmap/application/services/deduplicate_service.py +517 -0
- roadmap/application/use_cases/__init__.py +1 -0
- roadmap/common/__init__.py +308 -0
- roadmap/common/cache.py +209 -0
- roadmap/common/cli_errors.py +147 -0
- roadmap/common/configuration/__init__.py +33 -0
- roadmap/common/configuration/config_loader.py +264 -0
- roadmap/common/configuration/config_manager.py +164 -0
- roadmap/common/configuration/config_schema.py +145 -0
- roadmap/common/configuration/github/__init__.py +9 -0
- roadmap/common/configuration/github/config_manager.py +154 -0
- roadmap/common/configuration/github/token_resolver.py +84 -0
- roadmap/common/console.py +184 -0
- roadmap/common/constants.py +134 -0
- roadmap/common/datetime_parser.py +319 -0
- roadmap/common/error_formatter.py +84 -0
- roadmap/common/errors/__init__.py +143 -0
- roadmap/common/errors/error_base.py +73 -0
- roadmap/common/errors/error_file.py +82 -0
- roadmap/common/errors/error_git.py +67 -0
- roadmap/common/errors/error_handler.py +166 -0
- roadmap/common/errors/error_network.py +90 -0
- roadmap/common/errors/error_security.py +78 -0
- roadmap/common/errors/error_standards.py +561 -0
- roadmap/common/errors/error_validation.py +111 -0
- roadmap/common/errors/exceptions.py +432 -0
- roadmap/common/formatters/__init__.py +29 -0
- roadmap/common/formatters/base_table_formatter.py +96 -0
- roadmap/common/formatters/export/__init__.py +7 -0
- roadmap/common/formatters/export/issue_exporter.py +97 -0
- roadmap/common/formatters/helpers.py +28 -0
- roadmap/common/formatters/kanban/__init__.py +9 -0
- roadmap/common/formatters/kanban/layout.py +48 -0
- roadmap/common/formatters/kanban/organizer.py +81 -0
- roadmap/common/formatters/output/__init__.py +15 -0
- roadmap/common/formatters/tables/__init__.py +11 -0
- roadmap/common/formatters/tables/column_factory.py +312 -0
- roadmap/common/formatters/tables/issue_table.py +276 -0
- roadmap/common/formatters/tables/milestone_table.py +247 -0
- roadmap/common/formatters/tables/project_table.py +195 -0
- roadmap/common/formatters/text/__init__.py +57 -0
- roadmap/common/formatters/text/basic.py +217 -0
- roadmap/common/formatters/text/display.py +42 -0
- roadmap/common/formatters/text/duration.py +36 -0
- roadmap/common/formatters/text/operations.py +325 -0
- roadmap/common/formatters/text/status_badges.py +57 -0
- roadmap/common/initialization/github/__init__.py +11 -0
- roadmap/common/initialization/github/setup_service.py +238 -0
- roadmap/common/initialization/github/setup_validator.py +78 -0
- roadmap/common/logging/__init__.py +113 -0
- roadmap/common/logging/audit_logging.py +5 -0
- roadmap/common/logging/correlation.py +45 -0
- roadmap/common/logging/decorators.py +267 -0
- roadmap/common/logging/error_logging.py +217 -0
- roadmap/common/logging/formatters.py +124 -0
- roadmap/common/logging/loggers.py +68 -0
- roadmap/common/logging/performance_tracking.py +267 -0
- roadmap/common/logging/utils.py +335 -0
- roadmap/common/models/__init__.py +44 -0
- roadmap/common/models/cli_models.py +118 -0
- roadmap/common/models/config_models.py +199 -0
- roadmap/common/models/output_models.py +381 -0
- roadmap/common/observability/__init__.py +28 -0
- roadmap/common/observability/instrumentation.py +103 -0
- roadmap/common/observability/observability.py +76 -0
- roadmap/common/observability/otel_init.py +109 -0
- roadmap/common/output_formatter.py +393 -0
- roadmap/common/progress.py +379 -0
- roadmap/common/result.py +396 -0
- roadmap/common/security/__init__.py +48 -0
- roadmap/common/security/exceptions.py +13 -0
- roadmap/common/security/export_cleanup.py +79 -0
- roadmap/common/security/file_operations.py +104 -0
- roadmap/common/security/filename_sanitization.py +54 -0
- roadmap/common/security/logging.py +27 -0
- roadmap/common/security/path_validation.py +137 -0
- roadmap/common/security/temp_files.py +41 -0
- roadmap/common/services/__init__.py +65 -0
- roadmap/common/services/decorators.py +183 -0
- roadmap/common/services/logging_utils.py +208 -0
- roadmap/common/services/metrics.py +161 -0
- roadmap/common/services/performance.py +219 -0
- roadmap/common/services/profiling.py +288 -0
- roadmap/common/services/retry.py +288 -0
- roadmap/common/status_style_manager.py +114 -0
- roadmap/common/union_find.py +88 -0
- roadmap/common/update_constants.py +13 -0
- roadmap/common/utils/__init__.py +65 -0
- roadmap/common/utils/cli_helpers.py +381 -0
- roadmap/common/utils/file_utils.py +440 -0
- roadmap/common/utils/path_utils.py +38 -0
- roadmap/common/utils/status_utils.py +130 -0
- roadmap/common/utils/timezone_utils.py +480 -0
- roadmap/common/validation/__init__.py +37 -0
- roadmap/common/validation/field_validator.py +104 -0
- roadmap/common/validation/result.py +37 -0
- roadmap/common/validation/roadmap_validator.py +310 -0
- roadmap/common/validation/schema_validator.py +36 -0
- roadmap/common/validation/validators.py +50 -0
- roadmap/core/__init__.py +1 -0
- roadmap/core/domain/__init__.py +30 -0
- roadmap/core/domain/comment.py +24 -0
- roadmap/core/domain/health.py +15 -0
- roadmap/core/domain/issue.py +274 -0
- roadmap/core/domain/milestone.py +173 -0
- roadmap/core/domain/ports/__init__.py +1 -0
- roadmap/core/domain/ports/baseline_repository.py +24 -0
- roadmap/core/domain/ports/issue_repository.py +34 -0
- roadmap/core/domain/ports/remote_backend_port.py +31 -0
- roadmap/core/domain/project.py +130 -0
- roadmap/core/interfaces/__init__.py +109 -0
- roadmap/core/interfaces/assignee_validator.py +40 -0
- roadmap/core/interfaces/backend_factory.py +175 -0
- roadmap/core/interfaces/github.py +102 -0
- roadmap/core/interfaces/parsers.py +140 -0
- roadmap/core/interfaces/persistence.py +45 -0
- roadmap/core/interfaces/repositories.py +174 -0
- roadmap/core/interfaces/state_managers.py +111 -0
- roadmap/core/interfaces/state_storage.py +64 -0
- roadmap/core/interfaces/sync_backend.py +275 -0
- roadmap/core/interfaces/sync_services.py +119 -0
- roadmap/core/interfaces/sync_validators.py +180 -0
- roadmap/core/models/__init__.py +31 -0
- roadmap/core/models/sync_models.py +162 -0
- roadmap/core/models.py +115 -0
- roadmap/core/observability/__init__.py +17 -0
- roadmap/core/observability/sync_metrics.py +699 -0
- roadmap/core/repositories.py +200 -0
- roadmap/core/services/__init__.py +313 -0
- roadmap/core/services/baseline/__init__.py +1 -0
- roadmap/core/services/baseline/baseline_builder_progress.py +274 -0
- roadmap/core/services/baseline/baseline_retriever.py +63 -0
- roadmap/core/services/baseline/baseline_selector.py +248 -0
- roadmap/core/services/baseline/baseline_state_retriever.py +284 -0
- roadmap/core/services/baseline/optimized_baseline_builder.py +425 -0
- roadmap/core/services/comment/__init__.py +1 -0
- roadmap/core/services/comment/comment_service.py +277 -0
- roadmap/core/services/git/__init__.py +1 -0
- roadmap/core/services/git/git_hook_auto_sync_service.py +403 -0
- roadmap/core/services/github/__init__.py +1 -0
- roadmap/core/services/github/github_change_detector.py +162 -0
- roadmap/core/services/github/github_config_validator.py +148 -0
- roadmap/core/services/github/github_conflict_detector.py +179 -0
- roadmap/core/services/github/github_entity_classifier.py +51 -0
- roadmap/core/services/github/github_integration_service.py +439 -0
- roadmap/core/services/github/github_issue_client.py +346 -0
- roadmap/core/services/health/__init__.py +1 -0
- roadmap/core/services/health/backup_cleanup_service.py +202 -0
- roadmap/core/services/health/data_integrity_validator_service.py +90 -0
- roadmap/core/services/health/entity_health_scanner.py +460 -0
- roadmap/core/services/health/file_repair_service.py +193 -0
- roadmap/core/services/health/health_check_service.py +166 -0
- roadmap/core/services/health/health_models.py +80 -0
- roadmap/core/services/health/infrastructure_validator_service.py +328 -0
- roadmap/core/services/health/issue_health_scanner.py +263 -0
- roadmap/core/services/helpers/__init__.py +18 -0
- roadmap/core/services/helpers/status_change_helpers.py +88 -0
- roadmap/core/services/initialization/__init__.py +19 -0
- roadmap/core/services/initialization/utils.py +104 -0
- roadmap/core/services/initialization/validator.py +78 -0
- roadmap/core/services/initialization/workflow.py +182 -0
- roadmap/core/services/initialization_service.py +111 -0
- roadmap/core/services/issue/__init__.py +1 -0
- roadmap/core/services/issue/assignee_validation_service.py +312 -0
- roadmap/core/services/issue/issue_creation_service.py +309 -0
- roadmap/core/services/issue/issue_filter_service.py +302 -0
- roadmap/core/services/issue/issue_matching_service.py +127 -0
- roadmap/core/services/issue/issue_service.py +765 -0
- roadmap/core/services/issue/issue_update_service.py +144 -0
- roadmap/core/services/issue/start_issue_service.py +174 -0
- roadmap/core/services/issue_helpers/__init__.py +18 -0
- roadmap/core/services/issue_helpers/issue_filters.py +296 -0
- roadmap/core/services/milestone_service.py +438 -0
- roadmap/core/services/project/__init__.py +1 -0
- roadmap/core/services/project/project_service.py +434 -0
- roadmap/core/services/project/project_status_service.py +125 -0
- roadmap/core/services/project_init/__init__.py +22 -0
- roadmap/core/services/project_init/context_detection.py +146 -0
- roadmap/core/services/project_init/creation.py +76 -0
- roadmap/core/services/project_init/detection.py +51 -0
- roadmap/core/services/project_init/template.py +228 -0
- roadmap/core/services/status_change_service.py +88 -0
- roadmap/core/services/sync/__init__.py +1 -0
- roadmap/core/services/sync/batch_processor.py +290 -0
- roadmap/core/services/sync/conflict_resolver.py +154 -0
- roadmap/core/services/sync/dependency_resolver.py +367 -0
- roadmap/core/services/sync/duplicate_detector.py +771 -0
- roadmap/core/services/sync/duplicate_resolver.py +315 -0
- roadmap/core/services/sync/error_classification.py +470 -0
- roadmap/core/services/sync/performance.py +263 -0
- roadmap/core/services/sync/sync_change_computer.py +211 -0
- roadmap/core/services/sync/sync_checkpoint.py +348 -0
- roadmap/core/services/sync/sync_conflict_detector.py +147 -0
- roadmap/core/services/sync/sync_conflict_resolver.py +448 -0
- roadmap/core/services/sync/sync_errors.py +383 -0
- roadmap/core/services/sync/sync_key_normalizer.py +222 -0
- roadmap/core/services/sync/sync_metadata_service.py +361 -0
- roadmap/core/services/sync/sync_plan.py +171 -0
- roadmap/core/services/sync/sync_plan_executor.py +447 -0
- roadmap/core/services/sync/sync_report.py +296 -0
- roadmap/core/services/sync/sync_state.py +173 -0
- roadmap/core/services/sync/sync_state_comparator.py +690 -0
- roadmap/core/services/sync/sync_state_manager.py +431 -0
- roadmap/core/services/sync/sync_state_normalizer.py +98 -0
- roadmap/core/services/sync/sync_three_way.py +78 -0
- roadmap/core/services/sync/three_way_merger.py +178 -0
- roadmap/core/services/utils/__init__.py +1 -0
- roadmap/core/services/utils/configuration_service.py +113 -0
- roadmap/core/services/utils/critical_path_calculator.py +418 -0
- roadmap/core/services/utils/dependency_analyzer.py +360 -0
- roadmap/core/services/utils/field_conflict_detector.py +126 -0
- roadmap/core/services/utils/remote_fetcher.py +202 -0
- roadmap/core/services/utils/remote_state_normalizer.py +110 -0
- roadmap/core/services/utils/retry_policy.py +51 -0
- roadmap/core/services/validator_base.py +130 -0
- roadmap/core/services/validators/__init__.py +28 -0
- roadmap/core/services/validators/_utils.py +20 -0
- roadmap/core/services/validators/archivable_issues_validator.py +71 -0
- roadmap/core/services/validators/archivable_milestones_validator.py +69 -0
- roadmap/core/services/validators/backup_validator.py +89 -0
- roadmap/core/services/validators/data_integrity_validator.py +84 -0
- roadmap/core/services/validators/duplicate_issues_validator.py +75 -0
- roadmap/core/services/validators/duplicate_milestones_validator.py +141 -0
- roadmap/core/services/validators/folder_structure_validator.py +165 -0
- roadmap/core/services/validators/health_status_utils.py +28 -0
- roadmap/core/services/validators/milestone_naming_validator.py +146 -0
- roadmap/core/services/validators/missing_headlines_validator.py +97 -0
- roadmap/core/services/validators/orphaned_issues_validator.py +142 -0
- roadmap/core/services/validators/orphaned_milestones_validator.py +85 -0
- roadmap/core/utils/git_remote_parser.py +83 -0
- roadmap/core/utils/github_urls.py +66 -0
- roadmap/domain/validation.py +30 -0
- roadmap/infrastructure/__init__.py +55 -0
- roadmap/infrastructure/coordination/__init__.py +8 -0
- roadmap/infrastructure/coordination/coordinator_params.py +82 -0
- roadmap/infrastructure/coordination/core.py +446 -0
- roadmap/infrastructure/coordination/git_coordinator.py +160 -0
- roadmap/infrastructure/coordination/initialization.py +348 -0
- roadmap/infrastructure/coordination/issue_coordinator.py +154 -0
- roadmap/infrastructure/coordination/issue_operations.py +456 -0
- roadmap/infrastructure/coordination/milestone_coordinator.py +135 -0
- roadmap/infrastructure/coordination/milestone_operations.py +201 -0
- roadmap/infrastructure/coordination/project_coordinator.py +113 -0
- roadmap/infrastructure/coordination/project_operations.py +144 -0
- roadmap/infrastructure/coordination/team_coordinator.py +107 -0
- roadmap/infrastructure/coordination/user_operations.py +150 -0
- roadmap/infrastructure/coordination/validation_coordinator.py +249 -0
- roadmap/infrastructure/coordination_gateway.py +152 -0
- roadmap/infrastructure/git/__init__.py +6 -0
- roadmap/infrastructure/git/git_integration_ops.py +234 -0
- roadmap/infrastructure/git_gateway.py +35 -0
- roadmap/infrastructure/github_gateway.py +52 -0
- roadmap/infrastructure/maintenance/__init__.py +5 -0
- roadmap/infrastructure/maintenance/cleanup.py +483 -0
- roadmap/infrastructure/observability/__init__.py +7 -0
- roadmap/infrastructure/observability/health.py +196 -0
- roadmap/infrastructure/observability/health_formatter.py +73 -0
- roadmap/infrastructure/observability/specialized_health_checkers.py +188 -0
- roadmap/infrastructure/persistence_gateway.py +164 -0
- roadmap/infrastructure/security/__init__.py +1 -0
- roadmap/infrastructure/security/credentials.py +489 -0
- roadmap/infrastructure/sync_gateway.py +94 -0
- roadmap/infrastructure/validation/__init__.py +7 -0
- roadmap/infrastructure/validation/file_enumeration.py +219 -0
- roadmap/infrastructure/validation/github_assignee_validator.py +47 -0
- roadmap/infrastructure/validation/github_validator.py +101 -0
- roadmap/infrastructure/validation/milestone_consistency_validator.py +96 -0
- roadmap/infrastructure/validation/vanilla_assignee_validator.py +37 -0
- roadmap/infrastructure/validation_gateway.py +96 -0
- roadmap/presentation/formatters/sync_metrics_formatter.py +355 -0
- roadmap/settings.py +345 -0
- roadmap/templates/github_workflows/ci-cd-roadmap.yml +241 -0
- roadmap/templates/github_workflows/issue-lifecycle.yml +236 -0
- roadmap/templates/github_workflows/roadmap-integration.yml +137 -0
- roadmap/templates/github_workflows/roadmap-starter.yml +59 -0
- roadmap/version.py +381 -0
- roadmap_cli-0.1.1.dist-info/METADATA +448 -0
- roadmap_cli-0.1.1.dist-info/RECORD +521 -0
- roadmap_cli-0.1.1.dist-info/WHEEL +4 -0
- roadmap_cli-0.1.1.dist-info/entry_points.txt +2 -0
- roadmap_cli-0.1.1.dist-info/licenses/LICENSE.md +21 -0
|
@@ -0,0 +1,166 @@
|
|
|
1
|
+
"""Health check service for roadmap CLI.
|
|
2
|
+
|
|
3
|
+
This module handles system health checking and reporting, providing
|
|
4
|
+
component status information and overall system health assessment.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
from typing import Any
|
|
8
|
+
|
|
9
|
+
from roadmap.common.logging import get_logger
|
|
10
|
+
from roadmap.core.domain.health import HealthStatus
|
|
11
|
+
from roadmap.infrastructure.coordination.core import RoadmapCore
|
|
12
|
+
from roadmap.infrastructure.observability.health import HealthCheck
|
|
13
|
+
|
|
14
|
+
logger = get_logger(__name__)
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
class HealthCheckService:
|
|
18
|
+
"""Manages system health checks and reporting."""
|
|
19
|
+
|
|
20
|
+
def __init__(self, core: RoadmapCore):
|
|
21
|
+
"""Initialize the service.
|
|
22
|
+
|
|
23
|
+
Args:
|
|
24
|
+
core: RoadmapCore instance
|
|
25
|
+
"""
|
|
26
|
+
self.core = core
|
|
27
|
+
|
|
28
|
+
def run_all_checks(self) -> dict[str, tuple[HealthStatus, str]]:
|
|
29
|
+
"""Run all system health checks.
|
|
30
|
+
|
|
31
|
+
Returns:
|
|
32
|
+
Dictionary mapping check names to (status, message) tuples
|
|
33
|
+
"""
|
|
34
|
+
try:
|
|
35
|
+
checks = HealthCheck.run_all_checks(self.core)
|
|
36
|
+
logger.debug("health_checks_completed", check_count=len(checks))
|
|
37
|
+
return checks
|
|
38
|
+
except Exception as e:
|
|
39
|
+
logger.error("health_checks_failed", error=str(e), severity="system_error")
|
|
40
|
+
return {}
|
|
41
|
+
|
|
42
|
+
def get_overall_status(
|
|
43
|
+
self, checks: dict[str, tuple[HealthStatus, str]] | None = None
|
|
44
|
+
) -> HealthStatus:
|
|
45
|
+
"""Get overall system health status.
|
|
46
|
+
|
|
47
|
+
Args:
|
|
48
|
+
checks: Optional dict of checks. If None, runs checks first.
|
|
49
|
+
|
|
50
|
+
Returns:
|
|
51
|
+
Overall HealthStatus
|
|
52
|
+
"""
|
|
53
|
+
try:
|
|
54
|
+
if checks is None:
|
|
55
|
+
checks = self.run_all_checks()
|
|
56
|
+
|
|
57
|
+
overall = HealthCheck.get_overall_status(checks)
|
|
58
|
+
logger.debug("overall_status_calculated", status=overall.value)
|
|
59
|
+
return overall
|
|
60
|
+
except Exception as e:
|
|
61
|
+
logger.error("overall_status_failed", error=str(e), severity="system_error")
|
|
62
|
+
return HealthStatus.UNHEALTHY
|
|
63
|
+
|
|
64
|
+
def get_check_status(self, check_name: str) -> tuple[HealthStatus, str] | None:
|
|
65
|
+
"""Get status of a specific health check.
|
|
66
|
+
|
|
67
|
+
Args:
|
|
68
|
+
check_name: Name of the check to retrieve
|
|
69
|
+
|
|
70
|
+
Returns:
|
|
71
|
+
Tuple of (status, message) or None if check not found
|
|
72
|
+
"""
|
|
73
|
+
try:
|
|
74
|
+
checks = self.run_all_checks()
|
|
75
|
+
return checks.get(check_name)
|
|
76
|
+
except Exception as e:
|
|
77
|
+
logger.error(
|
|
78
|
+
"get_check_status_failed",
|
|
79
|
+
check_name=check_name,
|
|
80
|
+
error=str(e),
|
|
81
|
+
severity="system_error",
|
|
82
|
+
)
|
|
83
|
+
return None
|
|
84
|
+
|
|
85
|
+
def get_health_summary(self) -> dict[str, Any]:
|
|
86
|
+
"""Get comprehensive health summary.
|
|
87
|
+
|
|
88
|
+
Returns:
|
|
89
|
+
Dictionary with health summary data
|
|
90
|
+
"""
|
|
91
|
+
try:
|
|
92
|
+
checks = self.run_all_checks()
|
|
93
|
+
overall_status = self.get_overall_status(checks)
|
|
94
|
+
|
|
95
|
+
healthy_count = sum(
|
|
96
|
+
1 for _, (status, _) in checks.items() if status == HealthStatus.HEALTHY
|
|
97
|
+
)
|
|
98
|
+
degraded_count = sum(
|
|
99
|
+
1
|
|
100
|
+
for _, (status, _) in checks.items()
|
|
101
|
+
if status == HealthStatus.DEGRADED
|
|
102
|
+
)
|
|
103
|
+
unhealthy_count = sum(
|
|
104
|
+
1
|
|
105
|
+
for _, (status, _) in checks.items()
|
|
106
|
+
if status == HealthStatus.UNHEALTHY
|
|
107
|
+
)
|
|
108
|
+
|
|
109
|
+
return {
|
|
110
|
+
"overall_status": overall_status.value,
|
|
111
|
+
"checks": checks,
|
|
112
|
+
"summary": {
|
|
113
|
+
"total": len(checks),
|
|
114
|
+
"healthy": healthy_count,
|
|
115
|
+
"degraded": degraded_count,
|
|
116
|
+
"unhealthy": unhealthy_count,
|
|
117
|
+
},
|
|
118
|
+
}
|
|
119
|
+
except Exception as e:
|
|
120
|
+
logger.error("health_summary_failed", error=str(e), severity="system_error")
|
|
121
|
+
return {"error": str(e)}
|
|
122
|
+
|
|
123
|
+
def is_healthy(self) -> bool:
|
|
124
|
+
"""Check if system is in healthy state.
|
|
125
|
+
|
|
126
|
+
Returns:
|
|
127
|
+
True if overall status is HEALTHY
|
|
128
|
+
"""
|
|
129
|
+
try:
|
|
130
|
+
overall_status = self.get_overall_status()
|
|
131
|
+
return overall_status == HealthStatus.HEALTHY
|
|
132
|
+
except Exception as e:
|
|
133
|
+
logger.error(
|
|
134
|
+
"is_healthy_check_failed", error=str(e), severity="system_error"
|
|
135
|
+
)
|
|
136
|
+
return False
|
|
137
|
+
|
|
138
|
+
def is_degraded(self) -> bool:
|
|
139
|
+
"""Check if system is in degraded state.
|
|
140
|
+
|
|
141
|
+
Returns:
|
|
142
|
+
True if overall status is DEGRADED
|
|
143
|
+
"""
|
|
144
|
+
try:
|
|
145
|
+
overall_status = self.get_overall_status()
|
|
146
|
+
return overall_status == HealthStatus.DEGRADED
|
|
147
|
+
except Exception as e:
|
|
148
|
+
logger.error(
|
|
149
|
+
"is_degraded_check_failed", error=str(e), severity="system_error"
|
|
150
|
+
)
|
|
151
|
+
return False
|
|
152
|
+
|
|
153
|
+
def is_unhealthy(self) -> bool:
|
|
154
|
+
"""Check if system is in unhealthy state.
|
|
155
|
+
|
|
156
|
+
Returns:
|
|
157
|
+
True if overall status is UNHEALTHY
|
|
158
|
+
"""
|
|
159
|
+
try:
|
|
160
|
+
overall_status = self.get_overall_status()
|
|
161
|
+
return overall_status == HealthStatus.UNHEALTHY
|
|
162
|
+
except Exception as e:
|
|
163
|
+
logger.error(
|
|
164
|
+
"is_unhealthy_check_failed", error=str(e), severity="system_error"
|
|
165
|
+
)
|
|
166
|
+
return True # Fail safe: assume unhealthy on error
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
"""Shared health scanning models.
|
|
2
|
+
|
|
3
|
+
Holds dataclasses and enums used by the various health scanners.
|
|
4
|
+
"""
|
|
5
|
+
|
|
6
|
+
from dataclasses import dataclass, field
|
|
7
|
+
from enum import StrEnum
|
|
8
|
+
from typing import Any
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
class EntityType(StrEnum):
|
|
12
|
+
"""Types of entities that can be scanned."""
|
|
13
|
+
|
|
14
|
+
ISSUE = "issue"
|
|
15
|
+
MILESTONE = "milestone"
|
|
16
|
+
PROJECT = "project"
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
class HealthSeverity(StrEnum):
|
|
20
|
+
"""Severity levels for health issues."""
|
|
21
|
+
|
|
22
|
+
INFO = "info"
|
|
23
|
+
WARNING = "warning"
|
|
24
|
+
ERROR = "error"
|
|
25
|
+
CRITICAL = "critical"
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
@dataclass
|
|
29
|
+
class HealthIssue:
|
|
30
|
+
"""A single health issue found during scanning."""
|
|
31
|
+
|
|
32
|
+
code: str
|
|
33
|
+
message: str
|
|
34
|
+
severity: HealthSeverity
|
|
35
|
+
category: str
|
|
36
|
+
details: dict[str, Any] = field(default_factory=dict)
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
@dataclass
|
|
40
|
+
class EntityHealthReport:
|
|
41
|
+
"""Health report for a single entity."""
|
|
42
|
+
|
|
43
|
+
entity_id: str
|
|
44
|
+
entity_type: EntityType
|
|
45
|
+
entity_title: str
|
|
46
|
+
status: str
|
|
47
|
+
issues: list[HealthIssue] = field(default_factory=list)
|
|
48
|
+
|
|
49
|
+
@property
|
|
50
|
+
def issue_count(self) -> int:
|
|
51
|
+
"""Return the total number of health issues."""
|
|
52
|
+
return len(self.issues)
|
|
53
|
+
|
|
54
|
+
@property
|
|
55
|
+
def error_count(self) -> int:
|
|
56
|
+
"""Return the count of error-severity health issues."""
|
|
57
|
+
return sum(1 for i in self.issues if i.severity == HealthSeverity.ERROR)
|
|
58
|
+
|
|
59
|
+
@property
|
|
60
|
+
def warning_count(self) -> int:
|
|
61
|
+
"""Return the count of warning-severity health issues."""
|
|
62
|
+
return sum(1 for i in self.issues if i.severity == HealthSeverity.WARNING)
|
|
63
|
+
|
|
64
|
+
@property
|
|
65
|
+
def info_count(self) -> int:
|
|
66
|
+
"""Return the count of info-severity health issues."""
|
|
67
|
+
return sum(1 for i in self.issues if i.severity == HealthSeverity.INFO)
|
|
68
|
+
|
|
69
|
+
@property
|
|
70
|
+
def is_healthy(self) -> bool:
|
|
71
|
+
"""Check if entity is healthy (no errors or critical issues)."""
|
|
72
|
+
return not any(
|
|
73
|
+
i.severity in (HealthSeverity.ERROR, HealthSeverity.CRITICAL)
|
|
74
|
+
for i in self.issues
|
|
75
|
+
)
|
|
76
|
+
|
|
77
|
+
@property
|
|
78
|
+
def is_degraded(self) -> bool:
|
|
79
|
+
"""Check if entity has any warnings."""
|
|
80
|
+
return any(i.severity == HealthSeverity.WARNING for i in self.issues)
|
|
@@ -0,0 +1,328 @@
|
|
|
1
|
+
"""Infrastructure validators for health checks.
|
|
2
|
+
|
|
3
|
+
Handles validation of:
|
|
4
|
+
- Roadmap directory structure
|
|
5
|
+
- State database file
|
|
6
|
+
- Issues and milestones directories
|
|
7
|
+
- Git repository status
|
|
8
|
+
- Database integrity
|
|
9
|
+
|
|
10
|
+
Uses BaseValidator abstract class to eliminate boilerplate and ensure
|
|
11
|
+
consistent error handling and logging across all validators.
|
|
12
|
+
"""
|
|
13
|
+
|
|
14
|
+
from pathlib import Path
|
|
15
|
+
|
|
16
|
+
import structlog
|
|
17
|
+
|
|
18
|
+
from roadmap.common.logging import get_logger
|
|
19
|
+
from roadmap.common.logging.error_logging import (
|
|
20
|
+
log_error_with_context,
|
|
21
|
+
)
|
|
22
|
+
from roadmap.common.observability.instrumentation import traced
|
|
23
|
+
from roadmap.core.services.validator_base import BaseValidator, HealthStatus
|
|
24
|
+
from roadmap.core.services.validators.health_status_utils import (
|
|
25
|
+
get_overall_status,
|
|
26
|
+
)
|
|
27
|
+
from roadmap.infrastructure.persistence_gateway import PersistenceGateway
|
|
28
|
+
|
|
29
|
+
logger = structlog.get_logger()
|
|
30
|
+
|
|
31
|
+
logger = get_logger(__name__)
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
class RoadmapDirectoryValidator(BaseValidator):
|
|
35
|
+
"""Validator for .roadmap directory."""
|
|
36
|
+
|
|
37
|
+
@staticmethod
|
|
38
|
+
def get_check_name() -> str:
|
|
39
|
+
"""Get the name of this check.
|
|
40
|
+
|
|
41
|
+
Returns:
|
|
42
|
+
String identifier for the roadmap_directory check.
|
|
43
|
+
"""
|
|
44
|
+
return "roadmap_directory"
|
|
45
|
+
|
|
46
|
+
@staticmethod
|
|
47
|
+
def perform_check() -> tuple[str, str]:
|
|
48
|
+
"""Check if .roadmap directory exists and is accessible.
|
|
49
|
+
|
|
50
|
+
Returns:
|
|
51
|
+
Tuple of (status, message) describing the health check result
|
|
52
|
+
"""
|
|
53
|
+
roadmap_dir = Path(".roadmap")
|
|
54
|
+
if not roadmap_dir.exists():
|
|
55
|
+
return HealthStatus.DEGRADED, ".roadmap directory not initialized"
|
|
56
|
+
|
|
57
|
+
if not roadmap_dir.is_dir():
|
|
58
|
+
return (
|
|
59
|
+
HealthStatus.UNHEALTHY,
|
|
60
|
+
".roadmap exists but is not a directory",
|
|
61
|
+
)
|
|
62
|
+
|
|
63
|
+
# Check if directory is writable
|
|
64
|
+
test_file = roadmap_dir / ".health_check"
|
|
65
|
+
try:
|
|
66
|
+
test_file.touch()
|
|
67
|
+
test_file.unlink()
|
|
68
|
+
except OSError as e:
|
|
69
|
+
logger.error(
|
|
70
|
+
"roadmap_dir_not_writable",
|
|
71
|
+
error=str(e),
|
|
72
|
+
action="check_writable",
|
|
73
|
+
severity="system_error",
|
|
74
|
+
)
|
|
75
|
+
return HealthStatus.DEGRADED, ".roadmap directory is not writable"
|
|
76
|
+
|
|
77
|
+
return (
|
|
78
|
+
HealthStatus.HEALTHY,
|
|
79
|
+
".roadmap directory is accessible and writable",
|
|
80
|
+
)
|
|
81
|
+
|
|
82
|
+
|
|
83
|
+
class StateFileValidator(BaseValidator):
|
|
84
|
+
"""Validator for state database file."""
|
|
85
|
+
|
|
86
|
+
@staticmethod
|
|
87
|
+
def get_check_name() -> str:
|
|
88
|
+
"""Get the name of this check.
|
|
89
|
+
|
|
90
|
+
Returns:
|
|
91
|
+
String identifier for the state_file check.
|
|
92
|
+
"""
|
|
93
|
+
return "state_file"
|
|
94
|
+
|
|
95
|
+
@staticmethod
|
|
96
|
+
def perform_check() -> tuple[str, str]:
|
|
97
|
+
"""Check if state database exists and is readable.
|
|
98
|
+
|
|
99
|
+
Returns:
|
|
100
|
+
Tuple of (status, message) describing the health check result
|
|
101
|
+
"""
|
|
102
|
+
state_db = Path(".roadmap/db/state.db")
|
|
103
|
+
|
|
104
|
+
if not state_db.exists():
|
|
105
|
+
return (
|
|
106
|
+
HealthStatus.DEGRADED,
|
|
107
|
+
"state.db not found (project not initialized)",
|
|
108
|
+
)
|
|
109
|
+
|
|
110
|
+
# Check if file is readable and has content
|
|
111
|
+
try:
|
|
112
|
+
size = state_db.stat().st_size
|
|
113
|
+
if size == 0:
|
|
114
|
+
return HealthStatus.DEGRADED, "state.db is empty"
|
|
115
|
+
|
|
116
|
+
# Try to open it to verify it's accessible
|
|
117
|
+
with open(state_db, "rb") as f:
|
|
118
|
+
f.read(16) # Read SQLite header
|
|
119
|
+
|
|
120
|
+
except OSError as e:
|
|
121
|
+
return HealthStatus.UNHEALTHY, f"Cannot read state.db: {e}"
|
|
122
|
+
|
|
123
|
+
return HealthStatus.HEALTHY, "state.db is accessible and readable"
|
|
124
|
+
|
|
125
|
+
|
|
126
|
+
class IssuesDirectoryValidator(BaseValidator):
|
|
127
|
+
"""Validator for issues directory."""
|
|
128
|
+
|
|
129
|
+
@staticmethod
|
|
130
|
+
def get_check_name() -> str:
|
|
131
|
+
"""Get the name of this check.
|
|
132
|
+
|
|
133
|
+
Returns:
|
|
134
|
+
String identifier for the issues_directory check.
|
|
135
|
+
"""
|
|
136
|
+
return "issues_directory"
|
|
137
|
+
|
|
138
|
+
@staticmethod
|
|
139
|
+
def perform_check() -> tuple[str, str]:
|
|
140
|
+
"""Check if issues directory exists and is accessible.
|
|
141
|
+
|
|
142
|
+
Returns:
|
|
143
|
+
Tuple of (status, message) describing the health check result
|
|
144
|
+
"""
|
|
145
|
+
issues_dir = Path(".roadmap/issues")
|
|
146
|
+
|
|
147
|
+
if not issues_dir.exists():
|
|
148
|
+
return HealthStatus.DEGRADED, "issues directory not found"
|
|
149
|
+
|
|
150
|
+
if not issues_dir.is_dir():
|
|
151
|
+
return (
|
|
152
|
+
HealthStatus.UNHEALTHY,
|
|
153
|
+
"issues path exists but is not a directory",
|
|
154
|
+
)
|
|
155
|
+
|
|
156
|
+
# Check if directory is readable
|
|
157
|
+
try:
|
|
158
|
+
list(issues_dir.iterdir())
|
|
159
|
+
except OSError as e:
|
|
160
|
+
return HealthStatus.UNHEALTHY, f"Cannot read issues directory: {e}"
|
|
161
|
+
|
|
162
|
+
return HealthStatus.HEALTHY, "issues directory is accessible"
|
|
163
|
+
|
|
164
|
+
|
|
165
|
+
class MilestonesDirectoryValidator(BaseValidator):
|
|
166
|
+
"""Validator for milestones directory."""
|
|
167
|
+
|
|
168
|
+
@staticmethod
|
|
169
|
+
def get_check_name() -> str:
|
|
170
|
+
"""Get the name of this check.
|
|
171
|
+
|
|
172
|
+
Returns:
|
|
173
|
+
String identifier for the milestones_directory check.
|
|
174
|
+
"""
|
|
175
|
+
return "milestones_directory"
|
|
176
|
+
|
|
177
|
+
@staticmethod
|
|
178
|
+
def perform_check() -> tuple[str, str]:
|
|
179
|
+
"""Check if milestones directory exists and is accessible.
|
|
180
|
+
|
|
181
|
+
Returns:
|
|
182
|
+
Tuple of (status, message) describing the health check result
|
|
183
|
+
"""
|
|
184
|
+
milestones_dir = Path(".roadmap/milestones")
|
|
185
|
+
|
|
186
|
+
if not milestones_dir.exists():
|
|
187
|
+
return HealthStatus.DEGRADED, "milestones directory not found"
|
|
188
|
+
|
|
189
|
+
if not milestones_dir.is_dir():
|
|
190
|
+
return (
|
|
191
|
+
HealthStatus.UNHEALTHY,
|
|
192
|
+
"milestones path exists but is not a directory",
|
|
193
|
+
)
|
|
194
|
+
|
|
195
|
+
# Check if directory is readable
|
|
196
|
+
try:
|
|
197
|
+
list(milestones_dir.iterdir())
|
|
198
|
+
except OSError as e:
|
|
199
|
+
return (
|
|
200
|
+
HealthStatus.UNHEALTHY,
|
|
201
|
+
f"Cannot read milestones directory: {e}",
|
|
202
|
+
)
|
|
203
|
+
|
|
204
|
+
return HealthStatus.HEALTHY, "milestones directory is accessible"
|
|
205
|
+
|
|
206
|
+
|
|
207
|
+
class GitRepositoryValidator(BaseValidator):
|
|
208
|
+
"""Validator for git repository."""
|
|
209
|
+
|
|
210
|
+
@staticmethod
|
|
211
|
+
def get_check_name() -> str:
|
|
212
|
+
"""Get the name of this check.
|
|
213
|
+
|
|
214
|
+
Returns:
|
|
215
|
+
String identifier for the git_repository check.
|
|
216
|
+
"""
|
|
217
|
+
return "git_repository"
|
|
218
|
+
|
|
219
|
+
@staticmethod
|
|
220
|
+
def perform_check() -> tuple[str, str]:
|
|
221
|
+
"""Check if Git repository exists and is accessible.
|
|
222
|
+
|
|
223
|
+
Returns:
|
|
224
|
+
Tuple of (status, message) describing the health check result
|
|
225
|
+
"""
|
|
226
|
+
git_dir = Path(".git")
|
|
227
|
+
|
|
228
|
+
if not git_dir.exists():
|
|
229
|
+
return (
|
|
230
|
+
HealthStatus.DEGRADED,
|
|
231
|
+
"not a Git repository (.git not found)",
|
|
232
|
+
)
|
|
233
|
+
|
|
234
|
+
if not git_dir.is_dir():
|
|
235
|
+
return (
|
|
236
|
+
HealthStatus.UNHEALTHY,
|
|
237
|
+
".git exists but is not a directory",
|
|
238
|
+
)
|
|
239
|
+
|
|
240
|
+
return HealthStatus.HEALTHY, "Git repository is accessible"
|
|
241
|
+
|
|
242
|
+
|
|
243
|
+
class DatabaseIntegrityValidator(BaseValidator):
|
|
244
|
+
"""Validator for database integrity."""
|
|
245
|
+
|
|
246
|
+
@staticmethod
|
|
247
|
+
def get_check_name() -> str:
|
|
248
|
+
"""Get the name of this check.
|
|
249
|
+
|
|
250
|
+
Returns:
|
|
251
|
+
String identifier for the database_integrity check.
|
|
252
|
+
"""
|
|
253
|
+
return "database_integrity"
|
|
254
|
+
|
|
255
|
+
@staticmethod
|
|
256
|
+
def perform_check() -> tuple[str, str]:
|
|
257
|
+
"""Check if database is accessible and can be queried.
|
|
258
|
+
|
|
259
|
+
Returns:
|
|
260
|
+
Tuple of (status, message) describing the health check result
|
|
261
|
+
"""
|
|
262
|
+
# Import here to avoid circular imports
|
|
263
|
+
try:
|
|
264
|
+
state_mgr = PersistenceGateway.get_state_manager()
|
|
265
|
+
# Try a simple query to verify DB is healthy
|
|
266
|
+
conn = state_mgr._get_connection()
|
|
267
|
+
conn.execute("SELECT 1")
|
|
268
|
+
return HealthStatus.HEALTHY, "Database is accessible and responsive"
|
|
269
|
+
except Exception as e:
|
|
270
|
+
return (
|
|
271
|
+
HealthStatus.UNHEALTHY,
|
|
272
|
+
f"Database query failed: {e}",
|
|
273
|
+
)
|
|
274
|
+
|
|
275
|
+
|
|
276
|
+
class InfrastructureValidator:
|
|
277
|
+
"""Orchestrator for infrastructure validation checks."""
|
|
278
|
+
|
|
279
|
+
def __init__(self):
|
|
280
|
+
"""Initialize infrastructure validator."""
|
|
281
|
+
# Note: This service uses static methods from validators.
|
|
282
|
+
# Instance creation is kept for potential future stateful functionality.
|
|
283
|
+
pass
|
|
284
|
+
|
|
285
|
+
@traced("run_all_infrastructure_checks")
|
|
286
|
+
def run_all_infrastructure_checks(self) -> dict[str, tuple[str, str]]:
|
|
287
|
+
"""Run all infrastructure validators.
|
|
288
|
+
|
|
289
|
+
Returns:
|
|
290
|
+
Dictionary mapping check names to (status, message) tuples
|
|
291
|
+
"""
|
|
292
|
+
checks = {}
|
|
293
|
+
|
|
294
|
+
try:
|
|
295
|
+
checks["roadmap_directory"] = RoadmapDirectoryValidator.check()
|
|
296
|
+
checks["state_file"] = StateFileValidator.check()
|
|
297
|
+
checks["issues_directory"] = IssuesDirectoryValidator.check()
|
|
298
|
+
checks["milestones_directory"] = MilestonesDirectoryValidator.check()
|
|
299
|
+
checks["git_repository"] = GitRepositoryValidator.check()
|
|
300
|
+
checks["database_integrity"] = DatabaseIntegrityValidator.check()
|
|
301
|
+
|
|
302
|
+
return checks
|
|
303
|
+
except Exception as e:
|
|
304
|
+
log_error_with_context(
|
|
305
|
+
e,
|
|
306
|
+
operation="run_all_infrastructure_checks",
|
|
307
|
+
entity_type="Infrastructure",
|
|
308
|
+
)
|
|
309
|
+
logger.error(
|
|
310
|
+
"infrastructure_validation_failed",
|
|
311
|
+
error=str(e),
|
|
312
|
+
severity="system_error",
|
|
313
|
+
)
|
|
314
|
+
return {
|
|
315
|
+
"error": (
|
|
316
|
+
HealthStatus.UNHEALTHY,
|
|
317
|
+
f"Infrastructure validation failed: {e}",
|
|
318
|
+
)
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
@traced("get_overall_status")
|
|
322
|
+
def get_overall_status(self, checks: dict) -> str:
|
|
323
|
+
"""Get overall status from all checks.
|
|
324
|
+
|
|
325
|
+
Returns:
|
|
326
|
+
Overall status: 'healthy', 'degraded', or 'unhealthy'
|
|
327
|
+
"""
|
|
328
|
+
return get_overall_status(checks)
|