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,174 @@
|
|
|
1
|
+
"""Abstract repository interfaces for persistence.
|
|
2
|
+
|
|
3
|
+
These interfaces define contracts for data access and external integrations.
|
|
4
|
+
Implementations are in the infrastructure layer.
|
|
5
|
+
|
|
6
|
+
This follows the Dependency Inversion Principle:
|
|
7
|
+
- Core services depend on these abstractions
|
|
8
|
+
- Infrastructure implements these abstractions
|
|
9
|
+
- Services are decoupled from implementation details
|
|
10
|
+
"""
|
|
11
|
+
|
|
12
|
+
import builtins
|
|
13
|
+
from abc import ABC, abstractmethod
|
|
14
|
+
|
|
15
|
+
from roadmap.core.domain import Issue, Milestone, Project
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
class IssueRepository(ABC):
|
|
19
|
+
"""Abstract repository for issue persistence and retrieval.
|
|
20
|
+
|
|
21
|
+
Implementations handle:
|
|
22
|
+
- File-based storage
|
|
23
|
+
- Database operations
|
|
24
|
+
- Git integration
|
|
25
|
+
- Synchronization
|
|
26
|
+
|
|
27
|
+
Core services use this interface and don't care about implementation.
|
|
28
|
+
"""
|
|
29
|
+
|
|
30
|
+
@abstractmethod
|
|
31
|
+
def get(self, issue_id: str) -> Issue | None:
|
|
32
|
+
"""Retrieve a single issue by ID."""
|
|
33
|
+
pass
|
|
34
|
+
|
|
35
|
+
@abstractmethod
|
|
36
|
+
def list(self, _filters: dict | None = None) -> list[Issue]:
|
|
37
|
+
"""List issues with optional filtering."""
|
|
38
|
+
pass
|
|
39
|
+
|
|
40
|
+
@abstractmethod
|
|
41
|
+
def create(self, issue: Issue) -> Issue:
|
|
42
|
+
"""Create a new issue."""
|
|
43
|
+
pass
|
|
44
|
+
|
|
45
|
+
@abstractmethod
|
|
46
|
+
def update(self, issue: Issue) -> Issue:
|
|
47
|
+
"""Update an existing issue."""
|
|
48
|
+
pass
|
|
49
|
+
|
|
50
|
+
@abstractmethod
|
|
51
|
+
def delete(self, issue_id: str) -> bool:
|
|
52
|
+
"""Delete an issue by ID."""
|
|
53
|
+
pass
|
|
54
|
+
|
|
55
|
+
@abstractmethod
|
|
56
|
+
def bulk_update(self, issues: builtins.list[Issue]) -> builtins.list[Issue]:
|
|
57
|
+
"""Update multiple issues at once."""
|
|
58
|
+
pass
|
|
59
|
+
|
|
60
|
+
@abstractmethod
|
|
61
|
+
def find_by_milestone(self, milestone_id: str) -> builtins.list[Issue]:
|
|
62
|
+
"""Find all issues for a milestone."""
|
|
63
|
+
pass
|
|
64
|
+
|
|
65
|
+
@abstractmethod
|
|
66
|
+
def find_by_status(self, status: str) -> builtins.list[Issue]:
|
|
67
|
+
"""Find issues by status."""
|
|
68
|
+
pass
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
class MilestoneRepository(ABC):
|
|
72
|
+
"""Abstract repository for milestone persistence and retrieval.
|
|
73
|
+
|
|
74
|
+
Implementations handle:
|
|
75
|
+
- Milestone data storage
|
|
76
|
+
- Progress tracking
|
|
77
|
+
- Status management
|
|
78
|
+
- Synchronization
|
|
79
|
+
|
|
80
|
+
Core services use this interface and don't care about implementation.
|
|
81
|
+
"""
|
|
82
|
+
|
|
83
|
+
@abstractmethod
|
|
84
|
+
def get(self, milestone_id: str) -> Milestone | None:
|
|
85
|
+
"""Retrieve a single milestone by ID."""
|
|
86
|
+
pass
|
|
87
|
+
|
|
88
|
+
@abstractmethod
|
|
89
|
+
def list(self, project_id: str | None = None) -> list[Milestone]:
|
|
90
|
+
"""List milestones, optionally filtered by project."""
|
|
91
|
+
pass
|
|
92
|
+
|
|
93
|
+
@abstractmethod
|
|
94
|
+
def create(self, milestone: Milestone) -> Milestone:
|
|
95
|
+
"""Create a new milestone."""
|
|
96
|
+
pass
|
|
97
|
+
|
|
98
|
+
@abstractmethod
|
|
99
|
+
def update(self, milestone: Milestone) -> Milestone:
|
|
100
|
+
"""Update an existing milestone."""
|
|
101
|
+
pass
|
|
102
|
+
|
|
103
|
+
@abstractmethod
|
|
104
|
+
def delete(self, milestone_id: str) -> bool:
|
|
105
|
+
"""Delete a milestone by ID."""
|
|
106
|
+
pass
|
|
107
|
+
|
|
108
|
+
@abstractmethod
|
|
109
|
+
def get_progress(self, milestone_id: str) -> dict:
|
|
110
|
+
"""Get progress metrics for a milestone."""
|
|
111
|
+
pass
|
|
112
|
+
|
|
113
|
+
@abstractmethod
|
|
114
|
+
def find_by_status(self, status: str) -> builtins.list[Milestone]:
|
|
115
|
+
"""Find milestones by status."""
|
|
116
|
+
pass
|
|
117
|
+
|
|
118
|
+
@abstractmethod
|
|
119
|
+
def close(self, milestone_id: str) -> Milestone:
|
|
120
|
+
"""Close/complete a milestone."""
|
|
121
|
+
pass
|
|
122
|
+
|
|
123
|
+
|
|
124
|
+
class ProjectRepository(ABC):
|
|
125
|
+
"""Abstract repository for project persistence and retrieval.
|
|
126
|
+
|
|
127
|
+
Implementations handle:
|
|
128
|
+
- Project data storage
|
|
129
|
+
- Configuration management
|
|
130
|
+
- File system operations
|
|
131
|
+
- Synchronization
|
|
132
|
+
|
|
133
|
+
Core services use this interface and don't care about implementation.
|
|
134
|
+
"""
|
|
135
|
+
|
|
136
|
+
@abstractmethod
|
|
137
|
+
def get(self, project_id: str) -> Project | None:
|
|
138
|
+
"""Retrieve a single project by ID."""
|
|
139
|
+
pass
|
|
140
|
+
|
|
141
|
+
@abstractmethod
|
|
142
|
+
def list(self) -> list[Project]:
|
|
143
|
+
"""List all projects."""
|
|
144
|
+
pass
|
|
145
|
+
|
|
146
|
+
@abstractmethod
|
|
147
|
+
def get_current(self) -> Project | None:
|
|
148
|
+
"""Get the currently active project."""
|
|
149
|
+
pass
|
|
150
|
+
|
|
151
|
+
@abstractmethod
|
|
152
|
+
def create(self, project: Project) -> Project: # noqa: F841
|
|
153
|
+
"""Create a new project."""
|
|
154
|
+
pass
|
|
155
|
+
|
|
156
|
+
@abstractmethod
|
|
157
|
+
def update(self, project: Project) -> Project: # noqa: F841
|
|
158
|
+
"""Update an existing project."""
|
|
159
|
+
pass
|
|
160
|
+
|
|
161
|
+
@abstractmethod
|
|
162
|
+
def delete(self, project_id: str) -> bool:
|
|
163
|
+
"""Delete a project by ID."""
|
|
164
|
+
pass
|
|
165
|
+
|
|
166
|
+
@abstractmethod
|
|
167
|
+
def get_status(self, project_id: str) -> dict:
|
|
168
|
+
"""Get project status/health information."""
|
|
169
|
+
pass
|
|
170
|
+
|
|
171
|
+
@abstractmethod
|
|
172
|
+
def validate(self, project_id: str) -> bool:
|
|
173
|
+
"""Validate project structure and integrity."""
|
|
174
|
+
pass
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
"""Protocol definitions for focused state managers.
|
|
2
|
+
|
|
3
|
+
Splits the monolithic StateManager into focused, single-responsibility
|
|
4
|
+
interfaces for better testability and maintainability.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
from typing import Any, Protocol
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
class ProjectStateManager(Protocol):
|
|
11
|
+
"""Protocol for project state persistence."""
|
|
12
|
+
|
|
13
|
+
def create_project(self, project_data: dict[str, Any]) -> str:
|
|
14
|
+
"""Create a new project."""
|
|
15
|
+
...
|
|
16
|
+
|
|
17
|
+
def get_project(self, project_id: str) -> dict[str, Any] | None:
|
|
18
|
+
"""Get project by ID."""
|
|
19
|
+
...
|
|
20
|
+
|
|
21
|
+
def list_projects(self) -> list[dict[str, Any]]:
|
|
22
|
+
"""List all projects."""
|
|
23
|
+
...
|
|
24
|
+
|
|
25
|
+
def update_project(self, project_id: str, updates: dict[str, Any]) -> bool:
|
|
26
|
+
"""Update project."""
|
|
27
|
+
...
|
|
28
|
+
|
|
29
|
+
def delete_project(self, project_id: str) -> bool:
|
|
30
|
+
"""Delete project."""
|
|
31
|
+
...
|
|
32
|
+
|
|
33
|
+
def mark_project_archived(self, project_id: str, archived: bool = True) -> bool:
|
|
34
|
+
"""Mark a project as archived or unarchived."""
|
|
35
|
+
...
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
class MilestoneStateManager(Protocol):
|
|
39
|
+
"""Protocol for milestone state persistence."""
|
|
40
|
+
|
|
41
|
+
def create_milestone(self, milestone_data: dict[str, Any]) -> str:
|
|
42
|
+
"""Create a new milestone."""
|
|
43
|
+
...
|
|
44
|
+
|
|
45
|
+
def get_milestone(self, milestone_id: str) -> dict[str, Any] | None:
|
|
46
|
+
"""Get milestone by ID."""
|
|
47
|
+
...
|
|
48
|
+
|
|
49
|
+
def update_milestone(self, milestone_id: str, updates: dict[str, Any]) -> bool:
|
|
50
|
+
"""Update milestone."""
|
|
51
|
+
...
|
|
52
|
+
|
|
53
|
+
def mark_milestone_archived(self, milestone_id: str, archived: bool = True) -> bool:
|
|
54
|
+
"""Mark a milestone as archived or unarchived."""
|
|
55
|
+
...
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
class IssueStateManager(Protocol):
|
|
59
|
+
"""Protocol for issue state persistence."""
|
|
60
|
+
|
|
61
|
+
def create_issue(self, issue_data: dict[str, Any]) -> str:
|
|
62
|
+
"""Create a new issue."""
|
|
63
|
+
...
|
|
64
|
+
|
|
65
|
+
def get_issue(self, issue_id: str) -> dict[str, Any] | None:
|
|
66
|
+
"""Get issue by ID."""
|
|
67
|
+
...
|
|
68
|
+
|
|
69
|
+
def update_issue(self, issue_id: str, updates: dict[str, Any]) -> bool:
|
|
70
|
+
"""Update issue."""
|
|
71
|
+
...
|
|
72
|
+
|
|
73
|
+
def delete_issue(self, issue_id: str) -> bool:
|
|
74
|
+
"""Delete issue."""
|
|
75
|
+
...
|
|
76
|
+
|
|
77
|
+
def mark_issue_archived(self, issue_id: str, archived: bool = True) -> bool:
|
|
78
|
+
"""Mark an issue as archived or unarchived."""
|
|
79
|
+
...
|
|
80
|
+
|
|
81
|
+
|
|
82
|
+
class SyncStateManager(Protocol):
|
|
83
|
+
"""Protocol for sync state persistence."""
|
|
84
|
+
|
|
85
|
+
def get_sync_state(self, key: str) -> str | None:
|
|
86
|
+
"""Get sync state value."""
|
|
87
|
+
...
|
|
88
|
+
|
|
89
|
+
def set_sync_state(self, key: str, value: str):
|
|
90
|
+
"""Set sync state value."""
|
|
91
|
+
...
|
|
92
|
+
|
|
93
|
+
|
|
94
|
+
class QueryStateManager(Protocol):
|
|
95
|
+
"""Protocol for complex query operations on state."""
|
|
96
|
+
|
|
97
|
+
def get_all_issues(self) -> list[dict[str, Any]]:
|
|
98
|
+
"""Get all issues from database."""
|
|
99
|
+
...
|
|
100
|
+
|
|
101
|
+
def get_all_milestones(self) -> list[dict[str, Any]]:
|
|
102
|
+
"""Get all milestones from database."""
|
|
103
|
+
...
|
|
104
|
+
|
|
105
|
+
def get_milestone_progress(self, milestone_name: str) -> dict[str, int]:
|
|
106
|
+
"""Get progress stats for a milestone."""
|
|
107
|
+
...
|
|
108
|
+
|
|
109
|
+
def get_issues_by_status(self) -> dict[str, int]:
|
|
110
|
+
"""Get issue counts by status."""
|
|
111
|
+
...
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
"""State manager interface for sync state operations.
|
|
2
|
+
|
|
3
|
+
Defines contract for managing sync state without importing storage implementations.
|
|
4
|
+
Breaks circular dependencies between core services and persistence layer.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
from abc import ABC, abstractmethod
|
|
8
|
+
|
|
9
|
+
from roadmap.core.services.sync.sync_state import SyncState
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
class SyncStateStorageInterface(ABC):
|
|
13
|
+
"""Contract for sync state persistence operations."""
|
|
14
|
+
|
|
15
|
+
@abstractmethod
|
|
16
|
+
def load_sync_state(self) -> SyncState | None:
|
|
17
|
+
"""Load the current sync state.
|
|
18
|
+
|
|
19
|
+
Returns:
|
|
20
|
+
SyncState object or None if not found
|
|
21
|
+
"""
|
|
22
|
+
pass
|
|
23
|
+
|
|
24
|
+
@abstractmethod
|
|
25
|
+
def save_sync_state(self, state: SyncState) -> bool:
|
|
26
|
+
"""Save sync state to storage.
|
|
27
|
+
|
|
28
|
+
Args:
|
|
29
|
+
state: SyncState object to save
|
|
30
|
+
|
|
31
|
+
Returns:
|
|
32
|
+
True if successful, False otherwise
|
|
33
|
+
"""
|
|
34
|
+
pass
|
|
35
|
+
|
|
36
|
+
@abstractmethod
|
|
37
|
+
def clear_sync_state(self) -> bool:
|
|
38
|
+
"""Clear the sync state.
|
|
39
|
+
|
|
40
|
+
Returns:
|
|
41
|
+
True if successful, False otherwise
|
|
42
|
+
"""
|
|
43
|
+
pass
|
|
44
|
+
|
|
45
|
+
@abstractmethod
|
|
46
|
+
def get_last_sync_time(self) -> str | None:
|
|
47
|
+
"""Get the last sync timestamp.
|
|
48
|
+
|
|
49
|
+
Returns:
|
|
50
|
+
ISO format datetime string or None if never synced
|
|
51
|
+
"""
|
|
52
|
+
pass
|
|
53
|
+
|
|
54
|
+
@abstractmethod
|
|
55
|
+
def update_last_sync_time(self, timestamp: str) -> bool:
|
|
56
|
+
"""Update the last sync timestamp.
|
|
57
|
+
|
|
58
|
+
Args:
|
|
59
|
+
timestamp: ISO format datetime string
|
|
60
|
+
|
|
61
|
+
Returns:
|
|
62
|
+
True if successful, False otherwise
|
|
63
|
+
"""
|
|
64
|
+
pass
|
|
@@ -0,0 +1,275 @@
|
|
|
1
|
+
"""Protocol definitions for pluggable sync backends.
|
|
2
|
+
|
|
3
|
+
This module defines the SyncBackendInterface that all sync backends must implement,
|
|
4
|
+
enabling support for multiple sync targets (GitHub, vanilla Git, GitLab, etc.)
|
|
5
|
+
without coupling the core sync logic to specific implementations.
|
|
6
|
+
|
|
7
|
+
Updated to use Result<T, SyncError> pattern for explicit error handling.
|
|
8
|
+
"""
|
|
9
|
+
|
|
10
|
+
from typing import Any, Protocol
|
|
11
|
+
|
|
12
|
+
from roadmap.common.result import Result
|
|
13
|
+
from roadmap.core.domain.issue import Issue
|
|
14
|
+
from roadmap.core.models.sync_models import SyncIssue, SyncMilestone, SyncProject
|
|
15
|
+
from roadmap.core.services.sync.sync_errors import SyncError
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
class SyncConflict:
|
|
19
|
+
"""Represents a conflict during sync operations."""
|
|
20
|
+
|
|
21
|
+
def __init__(
|
|
22
|
+
self,
|
|
23
|
+
issue_id: str,
|
|
24
|
+
local_version: Issue | None,
|
|
25
|
+
remote_version: dict[str, Any] | None,
|
|
26
|
+
conflict_type: str,
|
|
27
|
+
):
|
|
28
|
+
"""Initialize a sync conflict.
|
|
29
|
+
|
|
30
|
+
Args:
|
|
31
|
+
issue_id: ID of the conflicting issue
|
|
32
|
+
local_version: Local Issue object (may be None if deleted remotely)
|
|
33
|
+
remote_version: Remote issue dict (may be None if deleted locally)
|
|
34
|
+
conflict_type: Type of conflict ('both_modified', 'deleted_locally', 'deleted_remotely')
|
|
35
|
+
"""
|
|
36
|
+
self.issue_id = issue_id
|
|
37
|
+
self.local_version = local_version
|
|
38
|
+
self.remote_version = remote_version
|
|
39
|
+
self.conflict_type = conflict_type
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
class SyncReport:
|
|
43
|
+
"""Report of sync operations with status and conflicts."""
|
|
44
|
+
|
|
45
|
+
def __init__(self):
|
|
46
|
+
"""Initialize an empty sync report."""
|
|
47
|
+
self.pushed: list[str] = [] # Issue IDs successfully pushed
|
|
48
|
+
self.pulled: list[str] = [] # Issue IDs successfully pulled
|
|
49
|
+
self.conflicts: list[SyncConflict] = [] # Issues with conflicts
|
|
50
|
+
self.errors: dict[str, str] = {} # Issue ID -> error message
|
|
51
|
+
self.error: str | None = None # Overall sync error (if fatal)
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
class SyncBackendInterface(Protocol):
|
|
55
|
+
"""Abstract interface for sync backends.
|
|
56
|
+
|
|
57
|
+
Defines the contract that all sync backends must implement to enable
|
|
58
|
+
pluggable sync functionality. Allows support for GitHub, vanilla Git,
|
|
59
|
+
GitLab, Jira, and other sync targets.
|
|
60
|
+
|
|
61
|
+
All methods should handle errors gracefully and return status/report objects
|
|
62
|
+
rather than raising exceptions, enabling dry-run and conflict resolution
|
|
63
|
+
workflows.
|
|
64
|
+
"""
|
|
65
|
+
|
|
66
|
+
def authenticate(self) -> Result[bool, SyncError]:
|
|
67
|
+
"""Verify credentials and remote connectivity.
|
|
68
|
+
|
|
69
|
+
Returns:
|
|
70
|
+
Ok(True) if authentication succeeds
|
|
71
|
+
Err(SyncError) with authentication error details
|
|
72
|
+
|
|
73
|
+
Notes:
|
|
74
|
+
- No longer raises exceptions
|
|
75
|
+
- Returns Err with AUTHENTICATION_FAILED error type on failure
|
|
76
|
+
- May return TOKEN_EXPIRED or PERMISSION_DENIED error types
|
|
77
|
+
"""
|
|
78
|
+
...
|
|
79
|
+
|
|
80
|
+
def get_backend_name(self) -> str:
|
|
81
|
+
"""Get the canonical name of this backend.
|
|
82
|
+
|
|
83
|
+
Returns:
|
|
84
|
+
Backend name (e.g., 'github', 'git', 'gitlab')
|
|
85
|
+
Used as key in Issue.remote_ids dict to track remote issue IDs.
|
|
86
|
+
"""
|
|
87
|
+
...
|
|
88
|
+
|
|
89
|
+
def get_issues(self) -> Result[dict[str, SyncIssue], SyncError]:
|
|
90
|
+
"""Fetch all issues from remote.
|
|
91
|
+
|
|
92
|
+
Returns:
|
|
93
|
+
Ok(dict) mapping issue_id -> SyncIssue on success
|
|
94
|
+
Err(SyncError) with error details on failure
|
|
95
|
+
|
|
96
|
+
Notes:
|
|
97
|
+
- Backends normalize their API responses to SyncIssue format
|
|
98
|
+
- All fields are populated from backend data, including raw_response
|
|
99
|
+
- May return NETWORK_ERROR, API_RATE_LIMIT, or other error types
|
|
100
|
+
"""
|
|
101
|
+
...
|
|
102
|
+
|
|
103
|
+
def push_issues(self, local_issues: list[Issue]) -> Result[SyncReport, SyncError]:
|
|
104
|
+
"""Push multiple local issues to remote.
|
|
105
|
+
|
|
106
|
+
Args:
|
|
107
|
+
local_issues: List of Issue objects to push
|
|
108
|
+
|
|
109
|
+
Returns:
|
|
110
|
+
Ok(SyncReport) with pushed, conflicts, and errors on success
|
|
111
|
+
Err(SyncError) with fatal error details if operation cannot proceed
|
|
112
|
+
|
|
113
|
+
Notes:
|
|
114
|
+
- Should not stop on first error; attempt all issues
|
|
115
|
+
- Individual issue errors go in report.errors dict
|
|
116
|
+
- Fatal errors (auth, network) return Err
|
|
117
|
+
- Populate report.pushed with successful issue IDs
|
|
118
|
+
- Populate report.conflicts with conflicting issues
|
|
119
|
+
"""
|
|
120
|
+
...
|
|
121
|
+
|
|
122
|
+
def push_issue(self, local_issue: Issue) -> Result[bool, SyncError]:
|
|
123
|
+
"""Push a single local issue to remote.
|
|
124
|
+
|
|
125
|
+
Args:
|
|
126
|
+
local_issue: The Issue object to push to remote
|
|
127
|
+
|
|
128
|
+
Returns:
|
|
129
|
+
Ok(True) if push succeeds
|
|
130
|
+
Err(SyncError) with error details if push fails
|
|
131
|
+
|
|
132
|
+
Notes:
|
|
133
|
+
- Default implementation: delegates to push_issues()
|
|
134
|
+
- Backends may override for optimization
|
|
135
|
+
- Idempotent: pushing same issue twice should be safe
|
|
136
|
+
"""
|
|
137
|
+
report_result = self.push_issues([local_issue])
|
|
138
|
+
if report_result.is_err():
|
|
139
|
+
return report_result # type: ignore[return-value]
|
|
140
|
+
|
|
141
|
+
report = report_result.unwrap()
|
|
142
|
+
if len(report.pushed) > 0 and len(report.errors) == 0:
|
|
143
|
+
from roadmap.common.result import Ok
|
|
144
|
+
|
|
145
|
+
return Ok(True)
|
|
146
|
+
else:
|
|
147
|
+
from roadmap.common.result import Err
|
|
148
|
+
from roadmap.core.services.sync.sync_errors import SyncError, SyncErrorType
|
|
149
|
+
|
|
150
|
+
error_msg = report.errors.get(str(local_issue.id), "Push failed")
|
|
151
|
+
return Err(
|
|
152
|
+
SyncError(
|
|
153
|
+
error_type=SyncErrorType.UNKNOWN_ERROR,
|
|
154
|
+
message=error_msg,
|
|
155
|
+
entity_type="Issue",
|
|
156
|
+
entity_id=str(local_issue.id),
|
|
157
|
+
)
|
|
158
|
+
)
|
|
159
|
+
|
|
160
|
+
def pull_issues(self, issue_ids: list[str]) -> Result[SyncReport, SyncError]:
|
|
161
|
+
"""Pull specified remote issues to local.
|
|
162
|
+
|
|
163
|
+
Args:
|
|
164
|
+
issue_ids: List of remote issue IDs to pull. Empty list means pull nothing.
|
|
165
|
+
|
|
166
|
+
Returns:
|
|
167
|
+
Ok(SyncReport) with pulled, conflicts, and errors on success
|
|
168
|
+
Err(SyncError) with fatal error details if operation cannot proceed
|
|
169
|
+
|
|
170
|
+
Notes:
|
|
171
|
+
- Should detect conflicts with existing local issues
|
|
172
|
+
- Individual issue errors go in report.errors dict
|
|
173
|
+
- Fatal errors (auth, network) return Err
|
|
174
|
+
- Populate report.pulled with issue IDs successfully integrated
|
|
175
|
+
- Populate report.conflicts with conflicting issues
|
|
176
|
+
- Backend can optimize this (batch API calls, parallel processing, etc.)
|
|
177
|
+
"""
|
|
178
|
+
...
|
|
179
|
+
|
|
180
|
+
def pull_issue(self, issue_id: str) -> Result[bool, SyncError]:
|
|
181
|
+
"""Pull a single remote issue to local.
|
|
182
|
+
|
|
183
|
+
Args:
|
|
184
|
+
issue_id: The remote issue ID to pull
|
|
185
|
+
|
|
186
|
+
Returns:
|
|
187
|
+
Ok(True) if pull succeeds
|
|
188
|
+
Err(SyncError) with error details if pull fails
|
|
189
|
+
|
|
190
|
+
Notes:
|
|
191
|
+
- Default implementation: delegates to pull_issues()
|
|
192
|
+
- Backends may override for optimization
|
|
193
|
+
- Fetches the remote issue and updates local
|
|
194
|
+
"""
|
|
195
|
+
report_result = self.pull_issues([issue_id])
|
|
196
|
+
if report_result.is_err():
|
|
197
|
+
return report_result # type: ignore[return-value]
|
|
198
|
+
|
|
199
|
+
report = report_result.unwrap()
|
|
200
|
+
if len(report.pulled) > 0 and len(report.errors) == 0:
|
|
201
|
+
from roadmap.common.result import Ok
|
|
202
|
+
|
|
203
|
+
return Ok(True)
|
|
204
|
+
else:
|
|
205
|
+
from roadmap.common.result import Err
|
|
206
|
+
from roadmap.core.services.sync.sync_errors import SyncError, SyncErrorType
|
|
207
|
+
|
|
208
|
+
error_msg = report.errors.get(issue_id, "Pull failed")
|
|
209
|
+
return Err(
|
|
210
|
+
SyncError(
|
|
211
|
+
error_type=SyncErrorType.UNKNOWN_ERROR,
|
|
212
|
+
message=error_msg,
|
|
213
|
+
entity_type="Issue",
|
|
214
|
+
entity_id=issue_id,
|
|
215
|
+
)
|
|
216
|
+
)
|
|
217
|
+
|
|
218
|
+
def get_conflict_resolution_options(self, conflict: SyncConflict) -> list[str]:
|
|
219
|
+
"""Get available resolution strategies for a conflict.
|
|
220
|
+
|
|
221
|
+
Args:
|
|
222
|
+
conflict: The SyncConflict to resolve
|
|
223
|
+
|
|
224
|
+
Returns:
|
|
225
|
+
List of resolution option codes (e.g., ['use_local', 'use_remote', 'merge'])
|
|
226
|
+
|
|
227
|
+
Notes:
|
|
228
|
+
- Different backends may support different strategies
|
|
229
|
+
- At minimum should support 'use_local' and 'use_remote'
|
|
230
|
+
- Merge strategies optional and backend-specific
|
|
231
|
+
"""
|
|
232
|
+
...
|
|
233
|
+
|
|
234
|
+
def resolve_conflict(self, conflict: SyncConflict, resolution: str) -> bool:
|
|
235
|
+
"""Resolve a sync conflict using specified strategy.
|
|
236
|
+
|
|
237
|
+
Args:
|
|
238
|
+
conflict: The SyncConflict to resolve
|
|
239
|
+
resolution: The resolution strategy code (from get_conflict_resolution_options)
|
|
240
|
+
|
|
241
|
+
Returns:
|
|
242
|
+
True if resolution succeeds, False otherwise.
|
|
243
|
+
|
|
244
|
+
Notes:
|
|
245
|
+
- Should not raise exceptions; return False on failure
|
|
246
|
+
- After resolution, issue should be in consistent state
|
|
247
|
+
- May involve local or remote modifications
|
|
248
|
+
"""
|
|
249
|
+
...
|
|
250
|
+
|
|
251
|
+
def get_milestones(self) -> Result[dict[str, SyncMilestone], SyncError]:
|
|
252
|
+
"""Fetch all milestones from remote.
|
|
253
|
+
|
|
254
|
+
Returns:
|
|
255
|
+
Ok(dict) mapping milestone_id -> SyncMilestone on success
|
|
256
|
+
Err(SyncError) with error details on failure
|
|
257
|
+
|
|
258
|
+
Notes:
|
|
259
|
+
- Empty dict in Ok indicates no milestones exist
|
|
260
|
+
- Network errors return Err
|
|
261
|
+
"""
|
|
262
|
+
...
|
|
263
|
+
|
|
264
|
+
def get_projects(self) -> Result[dict[str, SyncProject], SyncError]:
|
|
265
|
+
"""Fetch all projects from remote.
|
|
266
|
+
|
|
267
|
+
Returns:
|
|
268
|
+
Ok(dict) mapping project_id -> SyncProject on success
|
|
269
|
+
Err(SyncError) with error details on failure
|
|
270
|
+
|
|
271
|
+
Notes:
|
|
272
|
+
- Empty dict in Ok indicates no projects exist
|
|
273
|
+
- Network errors return Err
|
|
274
|
+
"""
|
|
275
|
+
...
|