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,264 @@
|
|
|
1
|
+
"""Configuration loader and manager.
|
|
2
|
+
|
|
3
|
+
Handles loading and merging user-level and project-level configurations
|
|
4
|
+
with support for YAML files and sensible defaults.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
from pathlib import Path
|
|
8
|
+
from typing import Any
|
|
9
|
+
|
|
10
|
+
import yaml
|
|
11
|
+
from structlog import get_logger
|
|
12
|
+
|
|
13
|
+
from roadmap.common.models import RoadmapConfig
|
|
14
|
+
|
|
15
|
+
logger = get_logger()
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
class ConfigLoader:
|
|
19
|
+
"""Loads and manages roadmap configuration."""
|
|
20
|
+
|
|
21
|
+
# Default configuration
|
|
22
|
+
DEFAULT_CONFIG = RoadmapConfig()
|
|
23
|
+
|
|
24
|
+
@classmethod
|
|
25
|
+
def get_user_config_path(cls) -> Path:
|
|
26
|
+
"""Get the user-level config file path.
|
|
27
|
+
|
|
28
|
+
Returns:
|
|
29
|
+
Path to ~/.roadmap/config.yaml
|
|
30
|
+
"""
|
|
31
|
+
home = Path.home()
|
|
32
|
+
return home / ".roadmap" / "config.yaml"
|
|
33
|
+
|
|
34
|
+
@classmethod
|
|
35
|
+
def get_project_config_path(cls, project_root: Path | None = None) -> Path:
|
|
36
|
+
"""Get the project-level config file path.
|
|
37
|
+
|
|
38
|
+
Args:
|
|
39
|
+
project_root: Project root directory (defaults to cwd)
|
|
40
|
+
|
|
41
|
+
Returns:
|
|
42
|
+
Path to .roadmap/config.yaml in project
|
|
43
|
+
"""
|
|
44
|
+
if project_root is None:
|
|
45
|
+
project_root = Path.cwd()
|
|
46
|
+
return project_root / ".roadmap" / "config.yaml"
|
|
47
|
+
|
|
48
|
+
@classmethod
|
|
49
|
+
def load_user_config(cls) -> RoadmapConfig | None:
|
|
50
|
+
"""Load user-level configuration.
|
|
51
|
+
|
|
52
|
+
Returns:
|
|
53
|
+
RoadmapConfig from user config file, or None if not found
|
|
54
|
+
"""
|
|
55
|
+
path = cls.get_user_config_path()
|
|
56
|
+
return cls._load_config_file(path)
|
|
57
|
+
|
|
58
|
+
@classmethod
|
|
59
|
+
def load_project_config(
|
|
60
|
+
cls, project_root: Path | None = None
|
|
61
|
+
) -> RoadmapConfig | None:
|
|
62
|
+
"""Load project-level configuration.
|
|
63
|
+
|
|
64
|
+
Args:
|
|
65
|
+
project_root: Project root directory (defaults to cwd)
|
|
66
|
+
|
|
67
|
+
Returns:
|
|
68
|
+
RoadmapConfig from project config file, or None if not found
|
|
69
|
+
"""
|
|
70
|
+
path = cls.get_project_config_path(project_root)
|
|
71
|
+
return cls._load_config_file(path)
|
|
72
|
+
|
|
73
|
+
@classmethod
|
|
74
|
+
def _load_config_file(cls, path: Path) -> RoadmapConfig | None:
|
|
75
|
+
"""Load a configuration file.
|
|
76
|
+
|
|
77
|
+
Args:
|
|
78
|
+
path: Path to config file
|
|
79
|
+
|
|
80
|
+
Returns:
|
|
81
|
+
RoadmapConfig if file exists and is valid, None otherwise
|
|
82
|
+
"""
|
|
83
|
+
if not path.exists():
|
|
84
|
+
return None
|
|
85
|
+
|
|
86
|
+
try:
|
|
87
|
+
with open(path, encoding="utf-8") as f:
|
|
88
|
+
data = yaml.safe_load(f) or {}
|
|
89
|
+
|
|
90
|
+
# Ensure data is a dictionary
|
|
91
|
+
if not isinstance(data, dict):
|
|
92
|
+
logger.warning(
|
|
93
|
+
"invalid_config_format",
|
|
94
|
+
path=str(path),
|
|
95
|
+
error="config is not a mapping",
|
|
96
|
+
severity="data_error",
|
|
97
|
+
)
|
|
98
|
+
return None
|
|
99
|
+
|
|
100
|
+
# Validate and create config from data
|
|
101
|
+
config = RoadmapConfig(**data)
|
|
102
|
+
logger.info("loaded_config", path=str(path))
|
|
103
|
+
return config
|
|
104
|
+
except Exception as e:
|
|
105
|
+
logger.warning(
|
|
106
|
+
"failed_to_load_config",
|
|
107
|
+
path=str(path),
|
|
108
|
+
error=str(e),
|
|
109
|
+
severity="data_error",
|
|
110
|
+
)
|
|
111
|
+
return None
|
|
112
|
+
|
|
113
|
+
@classmethod
|
|
114
|
+
def load_config(cls, project_root: Path | None = None) -> RoadmapConfig:
|
|
115
|
+
"""Load and merge all configurations.
|
|
116
|
+
|
|
117
|
+
Precedence: defaults < user config < project config
|
|
118
|
+
|
|
119
|
+
Args:
|
|
120
|
+
project_root: Project root directory (defaults to cwd)
|
|
121
|
+
|
|
122
|
+
Returns:
|
|
123
|
+
Merged RoadmapConfig with all layers applied
|
|
124
|
+
"""
|
|
125
|
+
# Start with defaults
|
|
126
|
+
config = cls.DEFAULT_CONFIG
|
|
127
|
+
|
|
128
|
+
# Merge user config
|
|
129
|
+
user_config = cls.load_user_config()
|
|
130
|
+
if user_config:
|
|
131
|
+
config = config.merge(user_config)
|
|
132
|
+
|
|
133
|
+
# Merge project config
|
|
134
|
+
project_config = cls.load_project_config(project_root)
|
|
135
|
+
if project_config:
|
|
136
|
+
config = config.merge(project_config)
|
|
137
|
+
|
|
138
|
+
return config
|
|
139
|
+
|
|
140
|
+
@classmethod
|
|
141
|
+
def save_user_config(cls, config: RoadmapConfig) -> bool:
|
|
142
|
+
"""Save user-level configuration.
|
|
143
|
+
|
|
144
|
+
Args:
|
|
145
|
+
config: Configuration to save
|
|
146
|
+
|
|
147
|
+
Returns:
|
|
148
|
+
True if successful, False otherwise
|
|
149
|
+
"""
|
|
150
|
+
path = cls.get_user_config_path()
|
|
151
|
+
return cls._save_config_file(path, config)
|
|
152
|
+
|
|
153
|
+
@classmethod
|
|
154
|
+
def save_project_config(
|
|
155
|
+
cls, config: RoadmapConfig, project_root: Path | None = None
|
|
156
|
+
) -> bool:
|
|
157
|
+
"""Save project-level configuration.
|
|
158
|
+
|
|
159
|
+
Args:
|
|
160
|
+
config: Configuration to save
|
|
161
|
+
project_root: Project root directory (defaults to cwd)
|
|
162
|
+
|
|
163
|
+
Returns:
|
|
164
|
+
True if successful, False otherwise
|
|
165
|
+
"""
|
|
166
|
+
path = cls.get_project_config_path(project_root)
|
|
167
|
+
return cls._save_config_file(path, config)
|
|
168
|
+
|
|
169
|
+
@classmethod
|
|
170
|
+
def _save_config_file(cls, path: Path, config: RoadmapConfig) -> bool:
|
|
171
|
+
"""Save a configuration file.
|
|
172
|
+
|
|
173
|
+
Args:
|
|
174
|
+
path: Path to config file
|
|
175
|
+
config: Configuration to save
|
|
176
|
+
|
|
177
|
+
Returns:
|
|
178
|
+
True if successful, False otherwise
|
|
179
|
+
"""
|
|
180
|
+
try:
|
|
181
|
+
# Create directory if needed
|
|
182
|
+
path.parent.mkdir(parents=True, exist_ok=True)
|
|
183
|
+
|
|
184
|
+
# Convert config to dict and save as YAML
|
|
185
|
+
data = config.model_dump(exclude_none=True)
|
|
186
|
+
with open(path, "w", encoding="utf-8") as f:
|
|
187
|
+
yaml.dump(data, f, default_flow_style=False, sort_keys=False)
|
|
188
|
+
|
|
189
|
+
logger.info("saved_config", path=str(path))
|
|
190
|
+
return True
|
|
191
|
+
except Exception as e:
|
|
192
|
+
logger.error(
|
|
193
|
+
"failed_to_save_config",
|
|
194
|
+
path=str(path),
|
|
195
|
+
error=str(e),
|
|
196
|
+
severity="system_error",
|
|
197
|
+
)
|
|
198
|
+
return False
|
|
199
|
+
|
|
200
|
+
@classmethod
|
|
201
|
+
def get_config_value(cls, key_path: str, project_root: Path | None = None) -> Any:
|
|
202
|
+
"""Get a configuration value by path (e.g., 'export.directory').
|
|
203
|
+
|
|
204
|
+
Args:
|
|
205
|
+
key_path: Dot-separated path to config value
|
|
206
|
+
project_root: Project root directory (defaults to cwd)
|
|
207
|
+
|
|
208
|
+
Returns:
|
|
209
|
+
Configuration value, or None if not found
|
|
210
|
+
"""
|
|
211
|
+
config = cls.load_config(project_root)
|
|
212
|
+
keys = key_path.split(".")
|
|
213
|
+
value = config
|
|
214
|
+
|
|
215
|
+
for key in keys:
|
|
216
|
+
if hasattr(value, key):
|
|
217
|
+
value = getattr(value, key)
|
|
218
|
+
elif isinstance(value, dict) and key in value:
|
|
219
|
+
value = value[key]
|
|
220
|
+
else:
|
|
221
|
+
return None
|
|
222
|
+
|
|
223
|
+
return value
|
|
224
|
+
|
|
225
|
+
@classmethod
|
|
226
|
+
def set_config_value(
|
|
227
|
+
cls,
|
|
228
|
+
key_path: str,
|
|
229
|
+
value: Any,
|
|
230
|
+
project_level: bool = False,
|
|
231
|
+
project_root: Path | None = None,
|
|
232
|
+
) -> bool:
|
|
233
|
+
"""Set a configuration value by path (e.g., 'export.directory').
|
|
234
|
+
|
|
235
|
+
Args:
|
|
236
|
+
key_path: Dot-separated path to config value
|
|
237
|
+
value: Value to set
|
|
238
|
+
project_level: If True, save to project config; else user config
|
|
239
|
+
project_root: Project root directory (defaults to cwd)
|
|
240
|
+
|
|
241
|
+
Returns:
|
|
242
|
+
True if successful, False otherwise
|
|
243
|
+
"""
|
|
244
|
+
# Load existing config
|
|
245
|
+
if project_level:
|
|
246
|
+
config = cls.load_project_config(project_root) or RoadmapConfig()
|
|
247
|
+
else:
|
|
248
|
+
config = cls.load_user_config() or RoadmapConfig()
|
|
249
|
+
|
|
250
|
+
# Set value in config
|
|
251
|
+
keys = key_path.split(".")
|
|
252
|
+
target = config
|
|
253
|
+
for key in keys[:-1]:
|
|
254
|
+
if not hasattr(target, key):
|
|
255
|
+
setattr(target, key, {})
|
|
256
|
+
target = getattr(target, key)
|
|
257
|
+
|
|
258
|
+
setattr(target, keys[-1], value)
|
|
259
|
+
|
|
260
|
+
# Save config
|
|
261
|
+
if project_level:
|
|
262
|
+
return cls.save_project_config(config, project_root)
|
|
263
|
+
else:
|
|
264
|
+
return cls.save_user_config(config)
|
|
@@ -0,0 +1,164 @@
|
|
|
1
|
+
"""Configuration management for roadmap."""
|
|
2
|
+
|
|
3
|
+
import subprocess
|
|
4
|
+
from pathlib import Path
|
|
5
|
+
|
|
6
|
+
import yaml
|
|
7
|
+
|
|
8
|
+
from .config_schema import GitHubConfig, PathsConfig, RoadmapConfig, UserConfig
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
class ConfigManager:
|
|
12
|
+
"""Manages roadmap configuration files.
|
|
13
|
+
|
|
14
|
+
Supports two-level configuration:
|
|
15
|
+
- config.yaml: Team-level configuration (committed to git)
|
|
16
|
+
- config.yaml.local: User-level overrides (gitignored, local only)
|
|
17
|
+
|
|
18
|
+
When both exist, local overrides shared settings.
|
|
19
|
+
"""
|
|
20
|
+
|
|
21
|
+
def __init__(self, config_file: Path):
|
|
22
|
+
"""Initialize config manager with config file path."""
|
|
23
|
+
self.config_file = config_file
|
|
24
|
+
self.local_config_file = Path(str(config_file).replace(".yaml", ".yaml.local"))
|
|
25
|
+
|
|
26
|
+
def load(self) -> RoadmapConfig:
|
|
27
|
+
"""Load configuration from file(s).
|
|
28
|
+
|
|
29
|
+
Loads team config first, then merges local overrides if present.
|
|
30
|
+
"""
|
|
31
|
+
config_data: dict = {}
|
|
32
|
+
|
|
33
|
+
# Load shared config (team-level)
|
|
34
|
+
if self.config_file.exists():
|
|
35
|
+
with open(self.config_file) as f:
|
|
36
|
+
loaded = yaml.safe_load(f)
|
|
37
|
+
config_data = loaded if isinstance(loaded, dict) else {}
|
|
38
|
+
|
|
39
|
+
# Load and merge local config (user-level overrides)
|
|
40
|
+
if self.local_config_file.exists():
|
|
41
|
+
with open(self.local_config_file) as f:
|
|
42
|
+
loaded = yaml.safe_load(f)
|
|
43
|
+
local_data = loaded if isinstance(loaded, dict) else {}
|
|
44
|
+
# Deep merge: local overrides shared
|
|
45
|
+
config_data = self._deep_merge(config_data, local_data)
|
|
46
|
+
|
|
47
|
+
return RoadmapConfig.from_dict(config_data)
|
|
48
|
+
|
|
49
|
+
def save(self, config: RoadmapConfig, is_local: bool = False) -> None:
|
|
50
|
+
"""Save configuration to file.
|
|
51
|
+
|
|
52
|
+
Args:
|
|
53
|
+
config: Configuration to save
|
|
54
|
+
is_local: If True, save to .local file (user overrides only)
|
|
55
|
+
"""
|
|
56
|
+
target_file = self.local_config_file if is_local else self.config_file
|
|
57
|
+
target_file.parent.mkdir(parents=True, exist_ok=True)
|
|
58
|
+
|
|
59
|
+
with open(target_file, "w") as f:
|
|
60
|
+
yaml.dump(config.to_dict(), f, default_flow_style=False, sort_keys=False)
|
|
61
|
+
|
|
62
|
+
@staticmethod
|
|
63
|
+
def _deep_merge(base: dict, override: dict) -> dict:
|
|
64
|
+
"""Deep merge override dict into base dict.
|
|
65
|
+
|
|
66
|
+
Override values take precedence over base values.
|
|
67
|
+
"""
|
|
68
|
+
result = base.copy()
|
|
69
|
+
for key, value in override.items():
|
|
70
|
+
if (
|
|
71
|
+
key in result
|
|
72
|
+
and isinstance(result[key], dict)
|
|
73
|
+
and isinstance(value, dict)
|
|
74
|
+
):
|
|
75
|
+
result[key] = ConfigManager._deep_merge(result[key], value)
|
|
76
|
+
else:
|
|
77
|
+
result[key] = value
|
|
78
|
+
return result
|
|
79
|
+
|
|
80
|
+
@staticmethod
|
|
81
|
+
def auto_detect_user() -> str | None:
|
|
82
|
+
"""Auto-detect user from git config or environment."""
|
|
83
|
+
try:
|
|
84
|
+
# Try git config user.name
|
|
85
|
+
result = subprocess.run(
|
|
86
|
+
["git", "config", "user.name"],
|
|
87
|
+
capture_output=True,
|
|
88
|
+
text=True,
|
|
89
|
+
timeout=5,
|
|
90
|
+
)
|
|
91
|
+
if result.returncode == 0 and result.stdout.strip():
|
|
92
|
+
return result.stdout.strip()
|
|
93
|
+
except (subprocess.TimeoutExpired, FileNotFoundError):
|
|
94
|
+
pass
|
|
95
|
+
|
|
96
|
+
# Try git config user.email
|
|
97
|
+
try:
|
|
98
|
+
result = subprocess.run(
|
|
99
|
+
["git", "config", "user.email"],
|
|
100
|
+
capture_output=True,
|
|
101
|
+
text=True,
|
|
102
|
+
timeout=5,
|
|
103
|
+
)
|
|
104
|
+
if result.returncode == 0 and result.stdout.strip():
|
|
105
|
+
email = result.stdout.strip()
|
|
106
|
+
# Extract name part before @
|
|
107
|
+
if "@" in email:
|
|
108
|
+
return email.split("@")[0]
|
|
109
|
+
except (subprocess.TimeoutExpired, FileNotFoundError):
|
|
110
|
+
pass
|
|
111
|
+
|
|
112
|
+
return None
|
|
113
|
+
|
|
114
|
+
@staticmethod
|
|
115
|
+
def auto_detect_github_username() -> str | None:
|
|
116
|
+
"""Auto-detect GitHub username from git remote."""
|
|
117
|
+
try:
|
|
118
|
+
result = subprocess.run(
|
|
119
|
+
["git", "config", "user.github"],
|
|
120
|
+
capture_output=True,
|
|
121
|
+
text=True,
|
|
122
|
+
timeout=5,
|
|
123
|
+
)
|
|
124
|
+
if result.returncode == 0 and result.stdout.strip():
|
|
125
|
+
return result.stdout.strip()
|
|
126
|
+
except (subprocess.TimeoutExpired, FileNotFoundError):
|
|
127
|
+
pass
|
|
128
|
+
|
|
129
|
+
# Try to extract from git remote origin url
|
|
130
|
+
try:
|
|
131
|
+
result = subprocess.run(
|
|
132
|
+
["git", "config", "remote.origin.url"],
|
|
133
|
+
capture_output=True,
|
|
134
|
+
text=True,
|
|
135
|
+
timeout=5,
|
|
136
|
+
)
|
|
137
|
+
if result.returncode == 0:
|
|
138
|
+
url = result.stdout.strip()
|
|
139
|
+
# Parse github.com:owner/repo.git or https://github.com/owner/repo.git
|
|
140
|
+
if "github.com" in url:
|
|
141
|
+
parts = url.split("/")[-2:] # Get last 2 parts
|
|
142
|
+
if parts and parts[0]:
|
|
143
|
+
return parts[0]
|
|
144
|
+
except (subprocess.TimeoutExpired, FileNotFoundError):
|
|
145
|
+
pass
|
|
146
|
+
|
|
147
|
+
return None
|
|
148
|
+
|
|
149
|
+
@staticmethod
|
|
150
|
+
def create_default_config(
|
|
151
|
+
user_name: str,
|
|
152
|
+
user_email: str | None = None,
|
|
153
|
+
github_owner: str | None = None,
|
|
154
|
+
github_repo: str | None = None,
|
|
155
|
+
github_enabled: bool = False,
|
|
156
|
+
) -> RoadmapConfig:
|
|
157
|
+
"""Create a default configuration."""
|
|
158
|
+
user = UserConfig(name=user_name, email=user_email)
|
|
159
|
+
paths = PathsConfig()
|
|
160
|
+
github = GitHubConfig(
|
|
161
|
+
owner=github_owner, repo=github_repo, enabled=github_enabled
|
|
162
|
+
)
|
|
163
|
+
|
|
164
|
+
return RoadmapConfig(user=user, paths=paths, github=github)
|
|
@@ -0,0 +1,145 @@
|
|
|
1
|
+
"""Configuration schema for roadmap projects.
|
|
2
|
+
|
|
3
|
+
SECURITY NOTICE:
|
|
4
|
+
- The config.yaml file is LOCAL ONLY and should NOT be committed to version control
|
|
5
|
+
- It is added to .gitignore to prevent accidental exposure
|
|
6
|
+
- It should contain only local user preferences, NOT secrets
|
|
7
|
+
- GitHub tokens and other credentials should be stored in environment variables or .env files
|
|
8
|
+
- The config.yaml file should have restrictive permissions (e.g., mode 0600)
|
|
9
|
+
"""
|
|
10
|
+
|
|
11
|
+
from dataclasses import asdict, dataclass, field
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
@dataclass
|
|
15
|
+
class UserConfig:
|
|
16
|
+
"""User configuration - LOCAL ONLY, NEVER share or commit this file.
|
|
17
|
+
|
|
18
|
+
Contains only non-sensitive user identification for the roadmap.
|
|
19
|
+
|
|
20
|
+
SECURITY WARNING:
|
|
21
|
+
- Do NOT store passwords, API keys, or other secrets in this config
|
|
22
|
+
- Do NOT commit config.yaml to version control
|
|
23
|
+
- Use environment variables or .env files for sensitive credentials
|
|
24
|
+
- Keep config.yaml file permissions restrictive (mode 0600)
|
|
25
|
+
"""
|
|
26
|
+
|
|
27
|
+
name: str # Local username/identifier
|
|
28
|
+
email: str | None = None
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
@dataclass
|
|
32
|
+
class PathsConfig:
|
|
33
|
+
"""Path configuration (LOCAL ONLY - relative to project root).
|
|
34
|
+
|
|
35
|
+
These are local paths on the user's machine and should never be shared.
|
|
36
|
+
"""
|
|
37
|
+
|
|
38
|
+
roadmap_dir: str = ".roadmap"
|
|
39
|
+
logs_dir: str = ".roadmap/logs"
|
|
40
|
+
db_dir: str = ".roadmap/db"
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
@dataclass
|
|
44
|
+
class GitHubConfig:
|
|
45
|
+
"""GitHub integration configuration - DOES NOT store credentials.
|
|
46
|
+
|
|
47
|
+
Contains only repository metadata and sync preferences.
|
|
48
|
+
|
|
49
|
+
SECURITY WARNING:
|
|
50
|
+
- Do NOT store GitHub tokens or PATs (Personal Access Tokens) in this config
|
|
51
|
+
- Use GITHUB_TOKEN environment variable for authentication instead
|
|
52
|
+
- webhook_secret is stored locally but should be kept secure
|
|
53
|
+
- Never commit config.yaml with webhook_secret to version control
|
|
54
|
+
"""
|
|
55
|
+
|
|
56
|
+
owner: str | None = None
|
|
57
|
+
repo: str | None = None
|
|
58
|
+
enabled: bool = False
|
|
59
|
+
sync_enabled: bool = False
|
|
60
|
+
sync_backend: str = "github" # "github" or "git"
|
|
61
|
+
webhook_secret: str | None = None
|
|
62
|
+
sync_settings: dict = field(
|
|
63
|
+
default_factory=lambda: {
|
|
64
|
+
"bidirectional": True,
|
|
65
|
+
"auto_close": True,
|
|
66
|
+
"sync_labels": True,
|
|
67
|
+
"sync_milestones": True,
|
|
68
|
+
}
|
|
69
|
+
)
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
@dataclass
|
|
73
|
+
class DisplayConfig:
|
|
74
|
+
"""Display preferences and UI customization (LOCAL ONLY).
|
|
75
|
+
|
|
76
|
+
User-specific display settings for command output and formatting.
|
|
77
|
+
These are local preferences and should never be shared.
|
|
78
|
+
"""
|
|
79
|
+
|
|
80
|
+
default_milestone: str | None = None
|
|
81
|
+
table_width: int = 100
|
|
82
|
+
|
|
83
|
+
|
|
84
|
+
@dataclass
|
|
85
|
+
class BehaviorConfig:
|
|
86
|
+
"""Application behavior preferences.
|
|
87
|
+
|
|
88
|
+
User-specific behavior settings for CLI operations.
|
|
89
|
+
"""
|
|
90
|
+
|
|
91
|
+
auto_branch_on_start: bool = False
|
|
92
|
+
confirm_destructive: bool = True
|
|
93
|
+
show_tips: bool = True
|
|
94
|
+
include_closed_in_critical_path: bool = False
|
|
95
|
+
default_project_id: str | None = None
|
|
96
|
+
|
|
97
|
+
|
|
98
|
+
@dataclass
|
|
99
|
+
class RoadmapConfig:
|
|
100
|
+
"""Complete roadmap configuration - LOCAL ONLY, NEVER commit to version control.
|
|
101
|
+
|
|
102
|
+
This is the root configuration object stored in .roadmap/config.yaml.
|
|
103
|
+
It aggregates user, paths, GitHub, display, and behavior settings.
|
|
104
|
+
|
|
105
|
+
SECURITY REMINDER:
|
|
106
|
+
- This entire config object is user-specific and must not be shared
|
|
107
|
+
- It is added to .gitignore to prevent accidental commits
|
|
108
|
+
- Secrets (tokens, PATs) must go in environment variables, NOT here
|
|
109
|
+
- Keep the config.yaml file with restrictive permissions (mode 0600)
|
|
110
|
+
"""
|
|
111
|
+
|
|
112
|
+
user: UserConfig
|
|
113
|
+
paths: PathsConfig = field(default_factory=PathsConfig)
|
|
114
|
+
github: GitHubConfig = field(default_factory=GitHubConfig)
|
|
115
|
+
display: DisplayConfig = field(default_factory=DisplayConfig)
|
|
116
|
+
behavior: BehaviorConfig = field(default_factory=BehaviorConfig)
|
|
117
|
+
|
|
118
|
+
def to_dict(self):
|
|
119
|
+
"""Convert to dictionary for YAML serialization."""
|
|
120
|
+
return asdict(self)
|
|
121
|
+
|
|
122
|
+
@staticmethod
|
|
123
|
+
def from_dict(data: dict) -> "RoadmapConfig":
|
|
124
|
+
"""Create from dictionary (e.g., from YAML)."""
|
|
125
|
+
user_data = data.get("user", {})
|
|
126
|
+
user = UserConfig(**user_data) if user_data else None
|
|
127
|
+
|
|
128
|
+
paths_data = data.get("paths", {}) or {}
|
|
129
|
+
paths = PathsConfig(**paths_data)
|
|
130
|
+
|
|
131
|
+
github_data = data.get("github", {}) or {}
|
|
132
|
+
github = GitHubConfig(**github_data)
|
|
133
|
+
|
|
134
|
+
display_data = data.get("display", {}) or {}
|
|
135
|
+
display = DisplayConfig(**display_data)
|
|
136
|
+
|
|
137
|
+
behavior_data = data.get("behavior", {}) or {}
|
|
138
|
+
behavior = BehaviorConfig(**behavior_data)
|
|
139
|
+
|
|
140
|
+
if not user:
|
|
141
|
+
raise ValueError("User configuration is required")
|
|
142
|
+
|
|
143
|
+
return RoadmapConfig(
|
|
144
|
+
user=user, paths=paths, github=github, display=display, behavior=behavior
|
|
145
|
+
)
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
"""GitHub configuration management."""
|
|
2
|
+
|
|
3
|
+
from roadmap.common.configuration.github.config_manager import GitHubConfigManager
|
|
4
|
+
from roadmap.common.configuration.github.token_resolver import GitHubTokenResolver
|
|
5
|
+
|
|
6
|
+
__all__ = [
|
|
7
|
+
"GitHubTokenResolver",
|
|
8
|
+
"GitHubConfigManager",
|
|
9
|
+
]
|