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,329 @@
|
|
|
1
|
+
"""Service for managing remote-to-local issue linking."""
|
|
2
|
+
|
|
3
|
+
from typing import TYPE_CHECKING
|
|
4
|
+
|
|
5
|
+
from structlog import get_logger
|
|
6
|
+
|
|
7
|
+
from roadmap.core.domain.issue import Issue
|
|
8
|
+
from roadmap.core.models.sync_models import SyncIssue
|
|
9
|
+
|
|
10
|
+
if TYPE_CHECKING:
|
|
11
|
+
from roadmap.adapters.persistence.repositories.remote_link_repository import ( # noqa: F401
|
|
12
|
+
RemoteLinkRepository,
|
|
13
|
+
)
|
|
14
|
+
from roadmap.infrastructure.coordination.core import RoadmapCore # noqa: F401
|
|
15
|
+
|
|
16
|
+
logger = get_logger(__name__)
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
class SyncLinkingService:
|
|
20
|
+
"""Handles linking between remote backend issues and local issues.
|
|
21
|
+
|
|
22
|
+
Manages:
|
|
23
|
+
- Database linking (RemoteLinkRepository)
|
|
24
|
+
- Duplicate detection by title matching
|
|
25
|
+
- Reverse lookups (find local ID from backend ID)
|
|
26
|
+
|
|
27
|
+
Centralizes all remote link operations that were duplicated in pull_issue()
|
|
28
|
+
and push_issue().
|
|
29
|
+
"""
|
|
30
|
+
|
|
31
|
+
@staticmethod
|
|
32
|
+
def link_issue_in_database(
|
|
33
|
+
repo: "RemoteLinkRepository | None",
|
|
34
|
+
local_issue_id: str,
|
|
35
|
+
backend_name: str,
|
|
36
|
+
remote_issue_id: int | str,
|
|
37
|
+
) -> bool:
|
|
38
|
+
"""Create a link in the database between a local issue and remote issue.
|
|
39
|
+
|
|
40
|
+
Args:
|
|
41
|
+
repo: RemoteLinkRepository instance (may be None)
|
|
42
|
+
local_issue_id: Local issue UUID/ID
|
|
43
|
+
backend_name: Backend name (e.g., "github")
|
|
44
|
+
remote_issue_id: ID from remote backend
|
|
45
|
+
|
|
46
|
+
Returns:
|
|
47
|
+
True if link was created or repo is None, False if creation failed
|
|
48
|
+
"""
|
|
49
|
+
if repo is None:
|
|
50
|
+
logger.debug(
|
|
51
|
+
"remote_link_repo_not_configured",
|
|
52
|
+
backend_name=backend_name,
|
|
53
|
+
)
|
|
54
|
+
return True # Not an error if repo not configured
|
|
55
|
+
|
|
56
|
+
if not local_issue_id:
|
|
57
|
+
logger.warning(
|
|
58
|
+
"cannot_link_issue_missing_local_id",
|
|
59
|
+
backend_name=backend_name,
|
|
60
|
+
remote_issue_id=remote_issue_id,
|
|
61
|
+
)
|
|
62
|
+
return False
|
|
63
|
+
|
|
64
|
+
if not remote_issue_id:
|
|
65
|
+
logger.warning(
|
|
66
|
+
"cannot_link_issue_missing_remote_id",
|
|
67
|
+
local_issue_id=local_issue_id,
|
|
68
|
+
backend_name=backend_name,
|
|
69
|
+
)
|
|
70
|
+
return False
|
|
71
|
+
|
|
72
|
+
try:
|
|
73
|
+
# Some implementations/mocks expect different kwarg names.
|
|
74
|
+
# Try the most common test-friendly names first, then fall
|
|
75
|
+
# back to the repository's canonical signature.
|
|
76
|
+
try:
|
|
77
|
+
repo.link_issue(
|
|
78
|
+
issue_uuid=local_issue_id,
|
|
79
|
+
backend_name=backend_name,
|
|
80
|
+
remote_id=str(remote_issue_id),
|
|
81
|
+
)
|
|
82
|
+
except TypeError:
|
|
83
|
+
# Fallback to the repository's expected parameter names
|
|
84
|
+
repo.link_issue(local_issue_id, backend_name, str(remote_issue_id))
|
|
85
|
+
|
|
86
|
+
logger.info(
|
|
87
|
+
"issue_linked_in_database",
|
|
88
|
+
local_issue_id=local_issue_id,
|
|
89
|
+
backend_name=backend_name,
|
|
90
|
+
remote_issue_id=remote_issue_id,
|
|
91
|
+
)
|
|
92
|
+
return True
|
|
93
|
+
|
|
94
|
+
except ValueError as e:
|
|
95
|
+
logger.warning(
|
|
96
|
+
"link_issue_validation_error",
|
|
97
|
+
local_issue_id=local_issue_id,
|
|
98
|
+
backend_name=backend_name,
|
|
99
|
+
remote_issue_id=remote_issue_id,
|
|
100
|
+
error=str(e),
|
|
101
|
+
)
|
|
102
|
+
return False
|
|
103
|
+
except Exception as e:
|
|
104
|
+
logger.error(
|
|
105
|
+
"link_issue_failed_in_database",
|
|
106
|
+
local_issue_id=local_issue_id,
|
|
107
|
+
backend_name=backend_name,
|
|
108
|
+
remote_issue_id=remote_issue_id,
|
|
109
|
+
error=str(e),
|
|
110
|
+
error_type=type(e).__name__,
|
|
111
|
+
exc_info=True,
|
|
112
|
+
)
|
|
113
|
+
return False
|
|
114
|
+
|
|
115
|
+
@staticmethod
|
|
116
|
+
def find_duplicate_by_title(
|
|
117
|
+
title: str,
|
|
118
|
+
backend_name: str,
|
|
119
|
+
core: "RoadmapCore",
|
|
120
|
+
) -> Issue | None:
|
|
121
|
+
"""Find a local issue by title that might be a duplicate of a remote issue.
|
|
122
|
+
|
|
123
|
+
Uses exact title matching to detect if a remote issue already exists locally.
|
|
124
|
+
This prevents creating duplicates when syncing from remote.
|
|
125
|
+
|
|
126
|
+
Args:
|
|
127
|
+
title: Title to search for
|
|
128
|
+
backend_name: Backend name for logging context
|
|
129
|
+
core: RoadmapCore for accessing repositories
|
|
130
|
+
|
|
131
|
+
Returns:
|
|
132
|
+
Issue if found, None if not found or search fails
|
|
133
|
+
"""
|
|
134
|
+
if not title or not title.strip():
|
|
135
|
+
logger.debug(
|
|
136
|
+
"cannot_find_duplicate_empty_title",
|
|
137
|
+
backend_name=backend_name,
|
|
138
|
+
)
|
|
139
|
+
return None
|
|
140
|
+
|
|
141
|
+
try:
|
|
142
|
+
repo = core.issue_service.repository
|
|
143
|
+
if not repo:
|
|
144
|
+
logger.warning(
|
|
145
|
+
"issue_repository_not_available_for_duplicate_search",
|
|
146
|
+
backend_name=backend_name,
|
|
147
|
+
title=title,
|
|
148
|
+
)
|
|
149
|
+
return None
|
|
150
|
+
|
|
151
|
+
issues = repo.list()
|
|
152
|
+
if not issues:
|
|
153
|
+
logger.debug(
|
|
154
|
+
"no_issues_in_repo_duplicate_search",
|
|
155
|
+
backend_name=backend_name,
|
|
156
|
+
)
|
|
157
|
+
return None
|
|
158
|
+
|
|
159
|
+
# Search for exact title match (case-insensitive)
|
|
160
|
+
title_lower = title.lower()
|
|
161
|
+
for issue in issues:
|
|
162
|
+
if issue.title.lower() == title_lower:
|
|
163
|
+
logger.info(
|
|
164
|
+
"duplicate_issue_found_by_title",
|
|
165
|
+
title=title,
|
|
166
|
+
found_issue_id=issue.id,
|
|
167
|
+
backend_name=backend_name,
|
|
168
|
+
)
|
|
169
|
+
return issue
|
|
170
|
+
|
|
171
|
+
logger.debug(
|
|
172
|
+
"no_duplicate_found_by_title",
|
|
173
|
+
title=title,
|
|
174
|
+
backend_name=backend_name,
|
|
175
|
+
total_issues_searched=len(issues),
|
|
176
|
+
)
|
|
177
|
+
return None
|
|
178
|
+
|
|
179
|
+
except AttributeError as e:
|
|
180
|
+
logger.error(
|
|
181
|
+
"duplicate_search_attribute_error",
|
|
182
|
+
title=title,
|
|
183
|
+
backend_name=backend_name,
|
|
184
|
+
error=str(e),
|
|
185
|
+
)
|
|
186
|
+
return None
|
|
187
|
+
except Exception as e:
|
|
188
|
+
logger.error(
|
|
189
|
+
"duplicate_search_failed",
|
|
190
|
+
title=title,
|
|
191
|
+
backend_name=backend_name,
|
|
192
|
+
error=str(e),
|
|
193
|
+
error_type=type(e).__name__,
|
|
194
|
+
exc_info=True,
|
|
195
|
+
)
|
|
196
|
+
return None
|
|
197
|
+
|
|
198
|
+
@staticmethod
|
|
199
|
+
def get_local_id_from_remote(
|
|
200
|
+
backend_name: str,
|
|
201
|
+
remote_issue_id: int | str,
|
|
202
|
+
repo: "RemoteLinkRepository | None",
|
|
203
|
+
) -> str | None:
|
|
204
|
+
"""Retrieve a local issue ID from a remote backend ID using database link.
|
|
205
|
+
|
|
206
|
+
This is a fast lookup via the RemoteLinkRepository.
|
|
207
|
+
|
|
208
|
+
Args:
|
|
209
|
+
backend_name: Backend name (e.g., "github")
|
|
210
|
+
remote_issue_id: ID from remote backend
|
|
211
|
+
repo: RemoteLinkRepository instance (may be None)
|
|
212
|
+
|
|
213
|
+
Returns:
|
|
214
|
+
Local issue ID if found and linked, None otherwise
|
|
215
|
+
"""
|
|
216
|
+
if repo is None:
|
|
217
|
+
logger.debug(
|
|
218
|
+
"remote_link_repo_not_available_for_reverse_lookup",
|
|
219
|
+
backend_name=backend_name,
|
|
220
|
+
remote_issue_id=remote_issue_id,
|
|
221
|
+
)
|
|
222
|
+
return None
|
|
223
|
+
|
|
224
|
+
if not remote_issue_id:
|
|
225
|
+
logger.warning(
|
|
226
|
+
"cannot_lookup_empty_remote_issue_id",
|
|
227
|
+
backend_name=backend_name,
|
|
228
|
+
)
|
|
229
|
+
return None
|
|
230
|
+
|
|
231
|
+
try:
|
|
232
|
+
local_id = repo.get_issue_uuid(
|
|
233
|
+
backend_name=backend_name,
|
|
234
|
+
remote_id=str(remote_issue_id),
|
|
235
|
+
)
|
|
236
|
+
|
|
237
|
+
if local_id:
|
|
238
|
+
logger.debug(
|
|
239
|
+
"found_local_id_from_remote",
|
|
240
|
+
backend_name=backend_name,
|
|
241
|
+
remote_issue_id=remote_issue_id,
|
|
242
|
+
local_issue_id=local_id,
|
|
243
|
+
)
|
|
244
|
+
return local_id
|
|
245
|
+
|
|
246
|
+
logger.debug(
|
|
247
|
+
"local_id_not_found_for_remote",
|
|
248
|
+
backend_name=backend_name,
|
|
249
|
+
remote_issue_id=remote_issue_id,
|
|
250
|
+
)
|
|
251
|
+
return None
|
|
252
|
+
|
|
253
|
+
except (ValueError, KeyError) as e:
|
|
254
|
+
logger.debug(
|
|
255
|
+
"reverse_lookup_validation_error",
|
|
256
|
+
backend_name=backend_name,
|
|
257
|
+
remote_issue_id=remote_issue_id,
|
|
258
|
+
error=str(e),
|
|
259
|
+
)
|
|
260
|
+
return None
|
|
261
|
+
except Exception as e:
|
|
262
|
+
logger.error(
|
|
263
|
+
"get_local_id_from_remote_failed",
|
|
264
|
+
backend_name=backend_name,
|
|
265
|
+
remote_issue_id=remote_issue_id,
|
|
266
|
+
error=str(e),
|
|
267
|
+
error_type=type(e).__name__,
|
|
268
|
+
exc_info=True,
|
|
269
|
+
)
|
|
270
|
+
return None
|
|
271
|
+
|
|
272
|
+
@staticmethod
|
|
273
|
+
def link_sync_issue(
|
|
274
|
+
sync_issue: SyncIssue,
|
|
275
|
+
local_issue_id: str,
|
|
276
|
+
repo: "RemoteLinkRepository | None",
|
|
277
|
+
) -> bool:
|
|
278
|
+
"""Create a link for a SyncIssue in the database.
|
|
279
|
+
|
|
280
|
+
Uses backend_name and backend_id from the SyncIssue to create the link.
|
|
281
|
+
|
|
282
|
+
Args:
|
|
283
|
+
sync_issue: SyncIssue with backend_name and backend_id
|
|
284
|
+
local_issue_id: Local issue ID to link to
|
|
285
|
+
repo: RemoteLinkRepository instance
|
|
286
|
+
|
|
287
|
+
Returns:
|
|
288
|
+
True if link created or repo is None, False otherwise
|
|
289
|
+
"""
|
|
290
|
+
if sync_issue.backend_id is None:
|
|
291
|
+
return False
|
|
292
|
+
|
|
293
|
+
return SyncLinkingService.link_issue_in_database(
|
|
294
|
+
repo=repo,
|
|
295
|
+
local_issue_id=local_issue_id,
|
|
296
|
+
backend_name=sync_issue.backend_name,
|
|
297
|
+
remote_issue_id=sync_issue.backend_id,
|
|
298
|
+
)
|
|
299
|
+
|
|
300
|
+
@staticmethod
|
|
301
|
+
def is_linked(
|
|
302
|
+
local_issue_id: str,
|
|
303
|
+
backend_name: str,
|
|
304
|
+
repo: "RemoteLinkRepository | None",
|
|
305
|
+
) -> bool:
|
|
306
|
+
"""Check if a local issue is linked to a remote backend.
|
|
307
|
+
|
|
308
|
+
Args:
|
|
309
|
+
local_issue_id: Local issue ID
|
|
310
|
+
backend_name: Backend name to check
|
|
311
|
+
repo: RemoteLinkRepository instance
|
|
312
|
+
|
|
313
|
+
Returns:
|
|
314
|
+
True if linked, False otherwise
|
|
315
|
+
"""
|
|
316
|
+
if repo is None:
|
|
317
|
+
return False
|
|
318
|
+
|
|
319
|
+
try:
|
|
320
|
+
# Try to get any remote ID for this local issue + backend
|
|
321
|
+
remote_id = repo.get_remote_id(
|
|
322
|
+
issue_uuid=local_issue_id,
|
|
323
|
+
backend_name=backend_name,
|
|
324
|
+
)
|
|
325
|
+
return bool(remote_id)
|
|
326
|
+
|
|
327
|
+
except Exception as e:
|
|
328
|
+
logger.debug("remote_id_lookup_failed", error=str(e))
|
|
329
|
+
return False
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
"""Service for populating and managing sync reports."""
|
|
2
|
+
|
|
3
|
+
from roadmap.core.services.sync.sync_report import SyncReport
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class SyncReportService:
|
|
7
|
+
"""Handles populating sync report fields from analyzed changes."""
|
|
8
|
+
|
|
9
|
+
@staticmethod
|
|
10
|
+
def populate_report_fields(
|
|
11
|
+
report: SyncReport,
|
|
12
|
+
local_issues,
|
|
13
|
+
active_issues_count,
|
|
14
|
+
archived_issues_count,
|
|
15
|
+
all_milestones,
|
|
16
|
+
active_milestones_count,
|
|
17
|
+
archived_milestones_count,
|
|
18
|
+
remote_issues_count,
|
|
19
|
+
remote_open_count,
|
|
20
|
+
remote_closed_count,
|
|
21
|
+
remote_milestones_count,
|
|
22
|
+
conflicts,
|
|
23
|
+
up_to_date,
|
|
24
|
+
local_only_changes,
|
|
25
|
+
remote_only_changes,
|
|
26
|
+
changes,
|
|
27
|
+
):
|
|
28
|
+
"""Populate the SyncReport fields from computed values.
|
|
29
|
+
|
|
30
|
+
Args:
|
|
31
|
+
report: SyncReport to populate
|
|
32
|
+
local_issues: List of local Issue objects
|
|
33
|
+
active_issues_count: Count of active local issues
|
|
34
|
+
archived_issues_count: Count of archived local issues
|
|
35
|
+
all_milestones: List of all milestones
|
|
36
|
+
active_milestones_count: Count of active milestones
|
|
37
|
+
archived_milestones_count: Count of archived milestones
|
|
38
|
+
remote_issues_count: Count of all remote issues
|
|
39
|
+
remote_open_count: Count of open remote issues
|
|
40
|
+
remote_closed_count: Count of closed remote issues
|
|
41
|
+
remote_milestones_count: Count of remote milestones
|
|
42
|
+
conflicts: List of conflicting changes
|
|
43
|
+
up_to_date: List of up-to-date issues
|
|
44
|
+
local_only_changes: List of local-only changes
|
|
45
|
+
remote_only_changes: List of remote-only changes
|
|
46
|
+
changes: All detected changes
|
|
47
|
+
"""
|
|
48
|
+
report.total_issues = len(local_issues)
|
|
49
|
+
report.active_issues = active_issues_count
|
|
50
|
+
report.archived_issues = archived_issues_count
|
|
51
|
+
report.total_milestones = len(all_milestones)
|
|
52
|
+
report.active_milestones = active_milestones_count
|
|
53
|
+
report.archived_milestones = archived_milestones_count
|
|
54
|
+
report.remote_total_issues = remote_issues_count
|
|
55
|
+
report.remote_open_issues = remote_open_count
|
|
56
|
+
report.remote_closed_issues = remote_closed_count
|
|
57
|
+
report.remote_total_milestones = remote_milestones_count
|
|
58
|
+
report.conflicts_detected = len(conflicts)
|
|
59
|
+
report.issues_up_to_date = len(up_to_date)
|
|
60
|
+
report.issues_needs_push = len(local_only_changes)
|
|
61
|
+
report.issues_needs_pull = len(remote_only_changes)
|
|
62
|
+
report.changes = changes
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
"""Service for tracking and updating baseline sync state."""
|
|
2
|
+
|
|
3
|
+
from datetime import UTC
|
|
4
|
+
|
|
5
|
+
from structlog import get_logger
|
|
6
|
+
|
|
7
|
+
from roadmap.core.services.sync.sync_state_manager import SyncStateManager
|
|
8
|
+
|
|
9
|
+
logger = get_logger()
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
class SyncStateUpdateService:
|
|
13
|
+
"""Handles updating and persisting sync state after operations."""
|
|
14
|
+
|
|
15
|
+
def __init__(self, state_manager: SyncStateManager):
|
|
16
|
+
"""Initialize state update service.
|
|
17
|
+
|
|
18
|
+
Args:
|
|
19
|
+
state_manager: SyncStateManager for persistence
|
|
20
|
+
"""
|
|
21
|
+
self.state_manager = state_manager
|
|
22
|
+
|
|
23
|
+
def update_baseline_for_pulled(self, pulled_remote_ids: list[str]) -> None:
|
|
24
|
+
"""Update baseline state after pulling remote issues.
|
|
25
|
+
|
|
26
|
+
Marks pulled issues as processed in the baseline state.
|
|
27
|
+
|
|
28
|
+
Args:
|
|
29
|
+
pulled_remote_ids: List of remote issue IDs that were pulled
|
|
30
|
+
"""
|
|
31
|
+
try:
|
|
32
|
+
base_state = self.state_manager.load_sync_state_from_db()
|
|
33
|
+
if not base_state:
|
|
34
|
+
return
|
|
35
|
+
|
|
36
|
+
# Mark pulled issues as updated in baseline
|
|
37
|
+
from datetime import datetime
|
|
38
|
+
|
|
39
|
+
now = datetime.now(UTC)
|
|
40
|
+
for remote_id in pulled_remote_ids:
|
|
41
|
+
if remote_id in base_state.base_issues:
|
|
42
|
+
base_state.base_issues[remote_id].updated_at = now
|
|
43
|
+
|
|
44
|
+
# Save updated state
|
|
45
|
+
self.state_manager.save_sync_state_to_db(base_state)
|
|
46
|
+
|
|
47
|
+
except Exception as e:
|
|
48
|
+
# Log but don't raise - state update failures shouldn't break sync
|
|
49
|
+
logger.error(
|
|
50
|
+
"update_sync_state_failed", error=str(e), severity="operational"
|
|
51
|
+
)
|