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,219 @@
|
|
|
1
|
+
"""Service for consistent file enumeration and parsing.
|
|
2
|
+
|
|
3
|
+
Consolidates repeated patterns from services:
|
|
4
|
+
- Directory walking with rglob("*.md")
|
|
5
|
+
- Backup file filtering
|
|
6
|
+
- Consistent error handling and logging
|
|
7
|
+
- File-to-object parsing with retry logic
|
|
8
|
+
|
|
9
|
+
This service eliminates ~200 lines of duplicated enumeration logic
|
|
10
|
+
across IssueService, MilestoneService, ProjectService, and validators.
|
|
11
|
+
|
|
12
|
+
Usage:
|
|
13
|
+
|
|
14
|
+
# Simple enumeration and parsing
|
|
15
|
+
issues = FileEnumerationService.enumerate_and_parse(
|
|
16
|
+
directory=issues_dir,
|
|
17
|
+
parser_func=IssueParser.parse_issue_file
|
|
18
|
+
)
|
|
19
|
+
|
|
20
|
+
# Enumeration with filtering
|
|
21
|
+
open_issues = FileEnumerationService.enumerate_with_filter(
|
|
22
|
+
directory=issues_dir,
|
|
23
|
+
parser_func=IssueParser.parse_issue_file,
|
|
24
|
+
filter_func=lambda issue: issue.status == Status.OPEN
|
|
25
|
+
)
|
|
26
|
+
|
|
27
|
+
# Find single item by ID
|
|
28
|
+
issue = FileEnumerationService.find_by_id(
|
|
29
|
+
directory=issues_dir,
|
|
30
|
+
id_value="abc12345",
|
|
31
|
+
parser_func=IssueParser.parse_issue_file
|
|
32
|
+
)
|
|
33
|
+
"""
|
|
34
|
+
|
|
35
|
+
from collections.abc import Callable
|
|
36
|
+
from pathlib import Path
|
|
37
|
+
from typing import Any
|
|
38
|
+
|
|
39
|
+
from roadmap.common.errors.error_standards import OperationType, safe_operation
|
|
40
|
+
from roadmap.common.logging import get_logger
|
|
41
|
+
|
|
42
|
+
logger = get_logger(__name__)
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
class FileEnumerationService:
|
|
46
|
+
"""Unified service for enumerating and filtering markdown files.
|
|
47
|
+
|
|
48
|
+
Provides consistent patterns for:
|
|
49
|
+
- Walking directories recursively
|
|
50
|
+
- Filtering backup files
|
|
51
|
+
- Parsing files with error handling
|
|
52
|
+
- Finding items by ID pattern
|
|
53
|
+
"""
|
|
54
|
+
|
|
55
|
+
@staticmethod
|
|
56
|
+
@safe_operation(OperationType.READ, "File")
|
|
57
|
+
def enumerate_and_parse(
|
|
58
|
+
directory: Path,
|
|
59
|
+
parser_func: Callable[[Path], Any],
|
|
60
|
+
backup_filter: bool = True,
|
|
61
|
+
warn_on_parse_error: bool = False,
|
|
62
|
+
) -> list[Any]:
|
|
63
|
+
"""Enumerate files and parse with consistent error handling.
|
|
64
|
+
|
|
65
|
+
Args:
|
|
66
|
+
directory: Directory to enumerate
|
|
67
|
+
parser_func: Parser function (e.g., IssueParser.parse_issue_file)
|
|
68
|
+
backup_filter: Skip .backup files if True
|
|
69
|
+
warn_on_parse_error: If True, emit WARNING instead of DEBUG when a
|
|
70
|
+
file fails to parse. Use for entity types where a silent skip
|
|
71
|
+
would cause confusing inconsistencies (e.g., milestones).
|
|
72
|
+
|
|
73
|
+
Returns:
|
|
74
|
+
List of parsed objects (failed items skipped with logging)
|
|
75
|
+
"""
|
|
76
|
+
logger.info("enumerate_and_parse_start", directory=str(directory))
|
|
77
|
+
if not directory.exists():
|
|
78
|
+
logger.debug(
|
|
79
|
+
"enumerate_and_parse_skip",
|
|
80
|
+
reason="directory_not_found",
|
|
81
|
+
directory=str(directory),
|
|
82
|
+
)
|
|
83
|
+
return []
|
|
84
|
+
|
|
85
|
+
results = []
|
|
86
|
+
# Use rglob to recursively search subdirectories (e.g., issues organized by milestone)
|
|
87
|
+
for file_path in directory.rglob("*.md"):
|
|
88
|
+
# Filter backup files if requested
|
|
89
|
+
if backup_filter and ".backup" in file_path.name:
|
|
90
|
+
continue
|
|
91
|
+
|
|
92
|
+
try:
|
|
93
|
+
obj = parser_func(file_path)
|
|
94
|
+
# Preserve the file path on the object for later updates
|
|
95
|
+
if obj is not None:
|
|
96
|
+
try:
|
|
97
|
+
obj.file_path = str(file_path)
|
|
98
|
+
except (AttributeError, TypeError):
|
|
99
|
+
# Object doesn't support attribute assignment (e.g., dict)
|
|
100
|
+
# This can happen in tests with mocks
|
|
101
|
+
pass
|
|
102
|
+
results.append(obj)
|
|
103
|
+
except Exception as e:
|
|
104
|
+
log_event = "enumerate_and_parse_skip_file"
|
|
105
|
+
log_kwargs = {
|
|
106
|
+
"file": str(file_path),
|
|
107
|
+
"error": str(e),
|
|
108
|
+
"error_type": type(e).__name__,
|
|
109
|
+
}
|
|
110
|
+
if warn_on_parse_error:
|
|
111
|
+
logger.warning(log_event, **log_kwargs)
|
|
112
|
+
else:
|
|
113
|
+
logger.debug(log_event, **log_kwargs)
|
|
114
|
+
continue
|
|
115
|
+
|
|
116
|
+
logger.debug(
|
|
117
|
+
"enumerate_and_parse_complete",
|
|
118
|
+
directory=str(directory),
|
|
119
|
+
count=len(results),
|
|
120
|
+
)
|
|
121
|
+
return results
|
|
122
|
+
|
|
123
|
+
@staticmethod
|
|
124
|
+
@safe_operation(OperationType.READ, "File")
|
|
125
|
+
def enumerate_with_filter(
|
|
126
|
+
directory: Path,
|
|
127
|
+
parser_func: Callable[[Path], Any],
|
|
128
|
+
filter_func: Callable[[Any], bool],
|
|
129
|
+
warn_on_parse_error: bool = False,
|
|
130
|
+
) -> list[Any]:
|
|
131
|
+
"""Enumerate, parse, and apply filter predicate.
|
|
132
|
+
|
|
133
|
+
Args:
|
|
134
|
+
directory: Directory to enumerate
|
|
135
|
+
parser_func: Parser function (e.g., IssueParser.parse_issue_file)
|
|
136
|
+
filter_func: Predicate function (return True to include)
|
|
137
|
+
warn_on_parse_error: Passed through to enumerate_and_parse.
|
|
138
|
+
|
|
139
|
+
Returns:
|
|
140
|
+
List of parsed and filtered objects
|
|
141
|
+
"""
|
|
142
|
+
logger.info(
|
|
143
|
+
"enumerate_with_filter_start",
|
|
144
|
+
directory=str(directory),
|
|
145
|
+
)
|
|
146
|
+
items = FileEnumerationService.enumerate_and_parse(
|
|
147
|
+
directory, parser_func, warn_on_parse_error=warn_on_parse_error
|
|
148
|
+
)
|
|
149
|
+
filtered = [item for item in items if filter_func(item)]
|
|
150
|
+
|
|
151
|
+
logger.debug(
|
|
152
|
+
"enumerate_with_filter_complete",
|
|
153
|
+
directory=str(directory),
|
|
154
|
+
total=len(items),
|
|
155
|
+
filtered=len(filtered),
|
|
156
|
+
)
|
|
157
|
+
return filtered
|
|
158
|
+
|
|
159
|
+
@staticmethod
|
|
160
|
+
@safe_operation(OperationType.READ, "File")
|
|
161
|
+
def find_by_id(
|
|
162
|
+
directory: Path,
|
|
163
|
+
id_value: str,
|
|
164
|
+
parser_func: Callable[[Path], Any],
|
|
165
|
+
) -> Any | None:
|
|
166
|
+
"""Find a single file by ID pattern.
|
|
167
|
+
|
|
168
|
+
Searches for files matching pattern: {id_value}-*.md
|
|
169
|
+
Returns first match or None if not found.
|
|
170
|
+
|
|
171
|
+
Args:
|
|
172
|
+
directory: Directory to search
|
|
173
|
+
id_value: ID to search for (first 8 chars, e.g., "abc12345")
|
|
174
|
+
parser_func: Parser function
|
|
175
|
+
|
|
176
|
+
Returns:
|
|
177
|
+
First matching object or None
|
|
178
|
+
"""
|
|
179
|
+
logger.info("find_by_id_start", id=id_value, directory=str(directory))
|
|
180
|
+
if not directory.exists():
|
|
181
|
+
logger.debug(
|
|
182
|
+
"find_by_id_skip",
|
|
183
|
+
reason="directory_not_found",
|
|
184
|
+
id=id_value,
|
|
185
|
+
)
|
|
186
|
+
return None
|
|
187
|
+
|
|
188
|
+
pattern = f"{id_value}-*.md"
|
|
189
|
+
for file_path in directory.rglob(pattern):
|
|
190
|
+
try:
|
|
191
|
+
obj = parser_func(file_path)
|
|
192
|
+
# Preserve the file path on the object for later updates
|
|
193
|
+
if obj is not None:
|
|
194
|
+
try:
|
|
195
|
+
obj.file_path = str(file_path)
|
|
196
|
+
except (AttributeError, TypeError):
|
|
197
|
+
# Object doesn't support attribute assignment
|
|
198
|
+
pass
|
|
199
|
+
logger.debug(
|
|
200
|
+
"find_by_id_found",
|
|
201
|
+
id=id_value,
|
|
202
|
+
file=file_path.name,
|
|
203
|
+
)
|
|
204
|
+
return obj
|
|
205
|
+
except Exception as e:
|
|
206
|
+
logger.debug(
|
|
207
|
+
"find_by_id_parse_failed",
|
|
208
|
+
id=id_value,
|
|
209
|
+
file=file_path.name,
|
|
210
|
+
error=str(e),
|
|
211
|
+
)
|
|
212
|
+
continue
|
|
213
|
+
|
|
214
|
+
logger.debug(
|
|
215
|
+
"find_by_id_not_found",
|
|
216
|
+
id=id_value,
|
|
217
|
+
directory=str(directory),
|
|
218
|
+
)
|
|
219
|
+
return None
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
"""GitHub-specific assignee validator implementation.
|
|
2
|
+
|
|
3
|
+
Validates assignees against GitHub repository collaborators and
|
|
4
|
+
provides canonical name resolution.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
from roadmap.common.logging import get_logger
|
|
8
|
+
|
|
9
|
+
logger = get_logger(__name__)
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
class GitHubAssigneeValidator:
|
|
13
|
+
"""Validates assignees against GitHub repository configuration.
|
|
14
|
+
|
|
15
|
+
Delegates to GitHubIntegrationService for the actual validation logic,
|
|
16
|
+
while implementing the AssigneeValidator protocol.
|
|
17
|
+
"""
|
|
18
|
+
|
|
19
|
+
def __init__(self, github_service):
|
|
20
|
+
"""Initialize with a GitHub integration service.
|
|
21
|
+
|
|
22
|
+
Args:
|
|
23
|
+
github_service: GitHubIntegrationService instance for validation
|
|
24
|
+
"""
|
|
25
|
+
self.github_service = github_service
|
|
26
|
+
|
|
27
|
+
def validate(self, assignee: str) -> tuple[bool, str]:
|
|
28
|
+
"""Validate assignee against GitHub.
|
|
29
|
+
|
|
30
|
+
Args:
|
|
31
|
+
assignee: Username to validate
|
|
32
|
+
|
|
33
|
+
Returns:
|
|
34
|
+
Tuple of (is_valid, error_message)
|
|
35
|
+
"""
|
|
36
|
+
return self.github_service.validate_assignee(assignee)
|
|
37
|
+
|
|
38
|
+
def get_canonical_assignee(self, assignee: str) -> str:
|
|
39
|
+
"""Get the canonical form of an assignee name from GitHub.
|
|
40
|
+
|
|
41
|
+
Args:
|
|
42
|
+
assignee: Input assignee name
|
|
43
|
+
|
|
44
|
+
Returns:
|
|
45
|
+
Canonical assignee name
|
|
46
|
+
"""
|
|
47
|
+
return self.github_service.get_canonical_assignee(assignee)
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
"""GitHub-specific assignee validation implementation.
|
|
2
|
+
|
|
3
|
+
Provides concrete GitHub-based validator implementation without coupling
|
|
4
|
+
core services directly to GitHub adapter.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
from roadmap.common.logging import get_logger
|
|
8
|
+
from roadmap.core.services.issue.assignee_validation_service import (
|
|
9
|
+
AssigneeValidationResult,
|
|
10
|
+
)
|
|
11
|
+
from roadmap.infrastructure.validation_gateway import ValidationGateway
|
|
12
|
+
|
|
13
|
+
logger = get_logger(__name__)
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
class GitHubAssigneeValidator:
|
|
17
|
+
"""Validates assignees against GitHub repository access.
|
|
18
|
+
|
|
19
|
+
This is the infrastructure implementation that can be injected
|
|
20
|
+
into core validation services.
|
|
21
|
+
"""
|
|
22
|
+
|
|
23
|
+
def __init__(
|
|
24
|
+
self,
|
|
25
|
+
token: str,
|
|
26
|
+
owner: str,
|
|
27
|
+
repo: str,
|
|
28
|
+
cached_members: set[str] | list[str] | None = None,
|
|
29
|
+
):
|
|
30
|
+
"""Initialize with GitHub configuration.
|
|
31
|
+
|
|
32
|
+
Args:
|
|
33
|
+
token: GitHub API token
|
|
34
|
+
owner: Repository owner
|
|
35
|
+
repo: Repository name
|
|
36
|
+
cached_members: Optional cached team members for performance
|
|
37
|
+
"""
|
|
38
|
+
self.token = token
|
|
39
|
+
self.owner = owner
|
|
40
|
+
self.repo = repo
|
|
41
|
+
self.cached_members = (
|
|
42
|
+
set(cached_members)
|
|
43
|
+
if isinstance(cached_members, list)
|
|
44
|
+
else (cached_members or set())
|
|
45
|
+
)
|
|
46
|
+
|
|
47
|
+
def validate(self, assignee: str) -> AssigneeValidationResult:
|
|
48
|
+
"""Validate assignee against GitHub repository.
|
|
49
|
+
|
|
50
|
+
Args:
|
|
51
|
+
assignee: Username to validate
|
|
52
|
+
|
|
53
|
+
Returns:
|
|
54
|
+
AssigneeValidationResult with validation status
|
|
55
|
+
"""
|
|
56
|
+
# Check cache first
|
|
57
|
+
if self.cached_members and assignee in self.cached_members:
|
|
58
|
+
return AssigneeValidationResult(is_valid=True, canonical_id=assignee)
|
|
59
|
+
|
|
60
|
+
# Do full validation via API
|
|
61
|
+
try:
|
|
62
|
+
client = ValidationGateway.get_github_client(
|
|
63
|
+
token=self.token, org=self.owner
|
|
64
|
+
)
|
|
65
|
+
github_valid, github_error = client.validate_assignee(assignee) # type: ignore[attr-defined]
|
|
66
|
+
|
|
67
|
+
if github_valid:
|
|
68
|
+
return AssigneeValidationResult(is_valid=True, canonical_id=assignee)
|
|
69
|
+
else:
|
|
70
|
+
return AssigneeValidationResult(is_valid=False, message=github_error)
|
|
71
|
+
|
|
72
|
+
except Exception as e:
|
|
73
|
+
error_msg = f"GitHub validation failed: {e}"
|
|
74
|
+
logger.warning(
|
|
75
|
+
"github_validation_error",
|
|
76
|
+
assignee=assignee,
|
|
77
|
+
error=str(e),
|
|
78
|
+
severity="infrastructure",
|
|
79
|
+
)
|
|
80
|
+
return AssigneeValidationResult(is_valid=False, message=error_msg)
|
|
81
|
+
|
|
82
|
+
def get_team_members(self) -> list[str]:
|
|
83
|
+
"""Get list of valid team members from GitHub.
|
|
84
|
+
|
|
85
|
+
Returns:
|
|
86
|
+
List of GitHub repository team members
|
|
87
|
+
"""
|
|
88
|
+
try:
|
|
89
|
+
client = ValidationGateway.get_github_client(
|
|
90
|
+
token=self.token, org=self.owner
|
|
91
|
+
)
|
|
92
|
+
members = client.get_team_members() # type: ignore[attr-defined]
|
|
93
|
+
logger.debug("github_team_members_retrieved", count=len(members))
|
|
94
|
+
return members
|
|
95
|
+
except Exception as e:
|
|
96
|
+
logger.warning(
|
|
97
|
+
"github_team_members_retrieval_failed",
|
|
98
|
+
error=str(e),
|
|
99
|
+
severity="infrastructure",
|
|
100
|
+
)
|
|
101
|
+
return []
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
"""Milestone Consistency Validation Module.
|
|
2
|
+
|
|
3
|
+
Handles validation and fixing of milestone filename and name field consistency.
|
|
4
|
+
Extracted from RoadmapCore to reduce god object complexity.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
from __future__ import annotations
|
|
8
|
+
|
|
9
|
+
from pathlib import Path
|
|
10
|
+
|
|
11
|
+
from roadmap.infrastructure.validation_gateway import ValidationGateway
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
class MilestoneConsistencyValidator:
|
|
15
|
+
"""Validates and fixes milestone naming consistency."""
|
|
16
|
+
|
|
17
|
+
def __init__(self, milestones_dir: Path):
|
|
18
|
+
"""Initialize validator with milestones directory.
|
|
19
|
+
|
|
20
|
+
Args:
|
|
21
|
+
milestones_dir: Path to milestones directory
|
|
22
|
+
"""
|
|
23
|
+
self.milestones_dir = milestones_dir
|
|
24
|
+
|
|
25
|
+
def validate(self) -> list[dict[str, str]]:
|
|
26
|
+
"""Check for inconsistencies between milestone filenames and name fields.
|
|
27
|
+
|
|
28
|
+
Returns:
|
|
29
|
+
List of dictionaries with inconsistency details
|
|
30
|
+
"""
|
|
31
|
+
inconsistencies = []
|
|
32
|
+
|
|
33
|
+
for milestone_file in sorted(self.milestones_dir.rglob("*.md"), key=str):
|
|
34
|
+
try:
|
|
35
|
+
milestone = ValidationGateway.parse_milestone_for_validation(
|
|
36
|
+
milestone_file
|
|
37
|
+
)
|
|
38
|
+
expected_filename = milestone.filename
|
|
39
|
+
actual_filename = milestone_file.name
|
|
40
|
+
|
|
41
|
+
if expected_filename != actual_filename:
|
|
42
|
+
inconsistencies.append(
|
|
43
|
+
{
|
|
44
|
+
"file": actual_filename,
|
|
45
|
+
"name": milestone.name,
|
|
46
|
+
"expected_filename": expected_filename,
|
|
47
|
+
"type": "filename_mismatch",
|
|
48
|
+
}
|
|
49
|
+
)
|
|
50
|
+
except Exception as e:
|
|
51
|
+
inconsistencies.append(
|
|
52
|
+
{
|
|
53
|
+
"file": milestone_file.name,
|
|
54
|
+
"name": "PARSE_ERROR",
|
|
55
|
+
"expected_filename": "N/A",
|
|
56
|
+
"type": "parse_error",
|
|
57
|
+
"error": str(e),
|
|
58
|
+
}
|
|
59
|
+
)
|
|
60
|
+
|
|
61
|
+
return inconsistencies
|
|
62
|
+
|
|
63
|
+
def fix(self) -> dict[str, list[str]]:
|
|
64
|
+
"""Fix milestone filename inconsistencies by renaming files to match name fields.
|
|
65
|
+
|
|
66
|
+
Returns:
|
|
67
|
+
Dictionary with 'renamed' and 'errors' lists
|
|
68
|
+
"""
|
|
69
|
+
results = {"renamed": [], "errors": []}
|
|
70
|
+
inconsistencies = self.validate()
|
|
71
|
+
|
|
72
|
+
for issue in inconsistencies:
|
|
73
|
+
if issue["type"] == "filename_mismatch":
|
|
74
|
+
old_path = self.milestones_dir / issue["file"]
|
|
75
|
+
new_path = self.milestones_dir / issue["expected_filename"]
|
|
76
|
+
|
|
77
|
+
try:
|
|
78
|
+
# Check if target filename already exists
|
|
79
|
+
if new_path.exists():
|
|
80
|
+
results["errors"].append(
|
|
81
|
+
f"Cannot rename {issue['file']} -> {issue['expected_filename']}: target exists"
|
|
82
|
+
)
|
|
83
|
+
continue
|
|
84
|
+
|
|
85
|
+
old_path.rename(new_path)
|
|
86
|
+
results["renamed"].append(
|
|
87
|
+
f"{issue['file']} -> {issue['expected_filename']}"
|
|
88
|
+
)
|
|
89
|
+
except Exception as e:
|
|
90
|
+
results["errors"].append(
|
|
91
|
+
f"Failed to rename {issue['file']}: {str(e)}"
|
|
92
|
+
)
|
|
93
|
+
else:
|
|
94
|
+
results["errors"].append(f"Cannot fix {issue['file']}: {issue['type']}")
|
|
95
|
+
|
|
96
|
+
return results
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
"""Vanilla/Git assignee validator for git sync backend.
|
|
2
|
+
|
|
3
|
+
Used when the sync backend is vanilla/git, this validator accepts
|
|
4
|
+
any assignee without validation since there's no centralized user registry.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class VanillaAssigneeValidator:
|
|
9
|
+
"""Validator that accepts any assignee without validation.
|
|
10
|
+
|
|
11
|
+
Used for git/vanilla sync backend where there's no centralized
|
|
12
|
+
user registry to validate against.
|
|
13
|
+
"""
|
|
14
|
+
|
|
15
|
+
def validate(self, assignee: str) -> tuple[bool, str]:
|
|
16
|
+
"""Accept any non-empty assignee without validation.
|
|
17
|
+
|
|
18
|
+
Args:
|
|
19
|
+
assignee: Username to validate
|
|
20
|
+
|
|
21
|
+
Returns:
|
|
22
|
+
(True, "") if assignee is non-empty, otherwise (False, error message)
|
|
23
|
+
"""
|
|
24
|
+
if not assignee or not assignee.strip():
|
|
25
|
+
return False, "Assignee cannot be empty"
|
|
26
|
+
return True, ""
|
|
27
|
+
|
|
28
|
+
def get_canonical_assignee(self, assignee: str) -> str:
|
|
29
|
+
"""Return the assignee as-is (no mapping).
|
|
30
|
+
|
|
31
|
+
Args:
|
|
32
|
+
assignee: Input assignee name
|
|
33
|
+
|
|
34
|
+
Returns:
|
|
35
|
+
Same assignee name unchanged
|
|
36
|
+
"""
|
|
37
|
+
return assignee
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
"""Gateway to validation infrastructure adapter access for core services.
|
|
2
|
+
|
|
3
|
+
This module mediates core service access to validation-related adapters,
|
|
4
|
+
ensuring proper layer separation between Core and Infrastructure.
|
|
5
|
+
|
|
6
|
+
All imports from roadmap.adapters accessed via validation are localized here.
|
|
7
|
+
Core services use this gateway instead of importing validation modules directly
|
|
8
|
+
or accessing adapters through validation.
|
|
9
|
+
"""
|
|
10
|
+
|
|
11
|
+
import os
|
|
12
|
+
from typing import Any
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
class _NullGitHubClient:
|
|
16
|
+
"""Fallback client for validation when no GitHub token is configured."""
|
|
17
|
+
|
|
18
|
+
def validate_assignee(self, assignee: str) -> tuple[bool, str]:
|
|
19
|
+
return False, "GitHub token not configured"
|
|
20
|
+
|
|
21
|
+
def get_team_members(self) -> list[str]:
|
|
22
|
+
return []
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
class ValidationGateway:
|
|
26
|
+
"""Gateway for validation operations.
|
|
27
|
+
|
|
28
|
+
Provides a centralized interface for core services to access validation
|
|
29
|
+
functionality that internally uses adapters (GitHub, persistence parsers, etc.)
|
|
30
|
+
without direct adapter imports in core.
|
|
31
|
+
"""
|
|
32
|
+
|
|
33
|
+
@staticmethod
|
|
34
|
+
def get_github_client(token: str | None = None, org: str | None = None) -> Any:
|
|
35
|
+
"""Get GitHub API client for validation operations.
|
|
36
|
+
|
|
37
|
+
Args:
|
|
38
|
+
token: GitHub API token (optional)
|
|
39
|
+
org: GitHub organization (optional)
|
|
40
|
+
|
|
41
|
+
Returns:
|
|
42
|
+
GitHub client instance from adapters
|
|
43
|
+
"""
|
|
44
|
+
from roadmap.adapters.github.github import GitHubClient
|
|
45
|
+
from roadmap.infrastructure.security.credentials import get_credential_manager
|
|
46
|
+
|
|
47
|
+
resolved_token = token or os.getenv("GITHUB_TOKEN")
|
|
48
|
+
if not resolved_token:
|
|
49
|
+
try:
|
|
50
|
+
resolved_token = get_credential_manager().get_token()
|
|
51
|
+
except Exception:
|
|
52
|
+
resolved_token = None
|
|
53
|
+
|
|
54
|
+
if not resolved_token:
|
|
55
|
+
return _NullGitHubClient()
|
|
56
|
+
|
|
57
|
+
return GitHubClient(token=resolved_token, owner=org)
|
|
58
|
+
|
|
59
|
+
@staticmethod
|
|
60
|
+
def parse_issue_for_validation(file_path: Any) -> Any:
|
|
61
|
+
"""Parse issue from file for validation.
|
|
62
|
+
|
|
63
|
+
Args:
|
|
64
|
+
file_path: Path to issue file
|
|
65
|
+
|
|
66
|
+
Returns:
|
|
67
|
+
Parsed Issue object
|
|
68
|
+
"""
|
|
69
|
+
from roadmap.adapters.persistence.parser import IssueParser
|
|
70
|
+
|
|
71
|
+
return IssueParser.parse_issue_file(file_path)
|
|
72
|
+
|
|
73
|
+
@staticmethod
|
|
74
|
+
def parse_milestone_for_validation(file_path: Any) -> Any:
|
|
75
|
+
"""Parse milestone from file for validation.
|
|
76
|
+
|
|
77
|
+
Args:
|
|
78
|
+
file_path: Path to milestone file
|
|
79
|
+
|
|
80
|
+
Returns:
|
|
81
|
+
Parsed Milestone object
|
|
82
|
+
"""
|
|
83
|
+
from roadmap.adapters.persistence.parser import MilestoneParser
|
|
84
|
+
|
|
85
|
+
return MilestoneParser.parse_milestone_file(file_path)
|
|
86
|
+
|
|
87
|
+
@staticmethod
|
|
88
|
+
def get_parser_module() -> Any:
|
|
89
|
+
"""Get the parser module for custom parsing operations.
|
|
90
|
+
|
|
91
|
+
Returns:
|
|
92
|
+
Parser module from adapters
|
|
93
|
+
"""
|
|
94
|
+
from roadmap.adapters.persistence import parser
|
|
95
|
+
|
|
96
|
+
return parser
|