@ngocsangairvds/vsaf 3.1.27 → 3.2.1
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.
- package/package.json +2 -2
- package/src/global.js +70 -10
- package/tools/skills/vds-scripts-skill/.openskills.json +6 -0
- package/tools/skills/vds-scripts-skill/QUALITY.md +44 -0
- package/tools/skills/vds-scripts-skill/SKILL.md +135 -0
- package/tools/skills/vds-scripts-skill/references/audit-commands.md +171 -0
- package/tools/skills/vds-scripts-skill/references/capability-index.md +34 -0
- package/tools/skills/vds-scripts-skill/references/development-commands.md +12 -0
- package/tools/skills/vds-scripts-skill/references/google-sheets.md +73 -0
- package/tools/skills/vds-scripts-skill/references/integration-commands.md +17 -0
- package/tools/skills/vds-scripts-skill/references/platform-bootstrap.md +31 -0
- package/tools/skills/vds-scripts-skill/references/specialist-routing.md +14 -0
- package/tools/skills/vds-scripts-skill/references/validation-commands.md +15 -0
- package/tools/skills/vsaf-build/SKILL.md +32 -2
- package/tools/skills/vsaf-ship/SKILL.md +41 -10
- package/tools/skills/vsaf-test/SKILL.md +8 -0
- package/tools/vds-scripts/.mcp.json +11 -0
- package/tools/vds-scripts/.secrets.baseline +133 -0
- package/tools/vds-scripts/AGENTS.md +152 -0
- package/tools/vds-scripts/CLAUDE.md +101 -0
- package/tools/vds-scripts/CLI_COMMAND_OPTIMIZATION.md +156 -0
- package/tools/vds-scripts/PACKAGE_P125B_IMPLEMENTATION_SUMMARY.md +131 -0
- package/tools/vds-scripts/PROJECT_COMPLETION_SUMMARY.md +45 -0
- package/tools/vds-scripts/README.md +97 -0
- package/tools/vds-scripts/bitbucket_manifest_mapping.toml +34 -0
- package/tools/vds-scripts/bitbucket_orchestrator/ARCHITECTURE_ANALYSIS.md +258 -0
- package/tools/vds-scripts/bitbucket_orchestrator/BITBUCKET_API_PRACTICES.md +393 -0
- package/tools/vds-scripts/bitbucket_orchestrator/EVALUATION_REPORT.md +61 -0
- package/tools/vds-scripts/bitbucket_orchestrator/FEATURES.md +908 -0
- package/tools/vds-scripts/bitbucket_orchestrator/README.md +687 -0
- package/tools/vds-scripts/bitbucket_orchestrator/pyproject.toml +40 -0
- package/tools/vds-scripts/bitbucket_orchestrator/src/vds_bitbucket_orchestrator/__init__.py +20 -0
- package/tools/vds-scripts/bitbucket_orchestrator/src/vds_bitbucket_orchestrator/async_client.py +657 -0
- package/tools/vds-scripts/bitbucket_orchestrator/src/vds_bitbucket_orchestrator/cli.py +2108 -0
- package/tools/vds-scripts/bitbucket_orchestrator/src/vds_bitbucket_orchestrator/client.py +2534 -0
- package/tools/vds-scripts/bitbucket_orchestrator/src/vds_bitbucket_orchestrator/config.py +171 -0
- package/tools/vds-scripts/bitbucket_orchestrator/src/vds_bitbucket_orchestrator/errors.py +67 -0
- package/tools/vds-scripts/bitbucket_orchestrator/src/vds_bitbucket_orchestrator/factory.py +185 -0
- package/tools/vds-scripts/bitbucket_orchestrator/src/vds_bitbucket_orchestrator/protocols.py +244 -0
- package/tools/vds-scripts/bitbucket_orchestrator/tests/__init__.py +8 -0
- package/tools/vds-scripts/bitbucket_orchestrator/tests/conftest.py +65 -0
- package/tools/vds-scripts/bitbucket_orchestrator/tests/test_advanced_search.py +151 -0
- package/tools/vds-scripts/bitbucket_orchestrator/tests/test_async_client.py +546 -0
- package/tools/vds-scripts/bitbucket_orchestrator/tests/test_branch_permissions.py +145 -0
- package/tools/vds-scripts/bitbucket_orchestrator/tests/test_cli.py +115 -0
- package/tools/vds-scripts/bitbucket_orchestrator/tests/test_client.py +157 -0
- package/tools/vds-scripts/bitbucket_orchestrator/tests/test_client_branch_conditions.py +79 -0
- package/tools/vds-scripts/bitbucket_orchestrator/tests/test_client_code_advanced.py +163 -0
- package/tools/vds-scripts/bitbucket_orchestrator/tests/test_client_code_file.py +32 -0
- package/tools/vds-scripts/bitbucket_orchestrator/tests/test_client_deployment_environments.py +194 -0
- package/tools/vds-scripts/bitbucket_orchestrator/tests/test_client_issues.py +164 -0
- package/tools/vds-scripts/bitbucket_orchestrator/tests/test_client_pipelines_advanced.py +179 -0
- package/tools/vds-scripts/bitbucket_orchestrator/tests/test_client_pr_blockers.py +119 -0
- package/tools/vds-scripts/bitbucket_orchestrator/tests/test_client_repository_variables.py +156 -0
- package/tools/vds-scripts/bitbucket_orchestrator/tests/test_code.py +98 -0
- package/tools/vds-scripts/bitbucket_orchestrator/tests/test_code_advanced.py +282 -0
- package/tools/vds-scripts/bitbucket_orchestrator/tests/test_code_insights.py +335 -0
- package/tools/vds-scripts/bitbucket_orchestrator/tests/test_conditions.py +147 -0
- package/tools/vds-scripts/bitbucket_orchestrator/tests/test_config.py +131 -0
- package/tools/vds-scripts/bitbucket_orchestrator/tests/test_deployment_env.py +352 -0
- package/tools/vds-scripts/bitbucket_orchestrator/tests/test_factory.py +371 -0
- package/tools/vds-scripts/bitbucket_orchestrator/tests/test_fork_operations.py +204 -0
- package/tools/vds-scripts/bitbucket_orchestrator/tests/test_issue_cli.py +261 -0
- package/tools/vds-scripts/bitbucket_orchestrator/tests/test_pipeline_advanced.py +270 -0
- package/tools/vds-scripts/bitbucket_orchestrator/tests/test_pr_blocker.py +204 -0
- package/tools/vds-scripts/bitbucket_orchestrator/tests/test_protocols.py +334 -0
- package/tools/vds-scripts/bitbucket_orchestrator/tests/test_repo_settings.py +343 -0
- package/tools/vds-scripts/bitbucket_orchestrator/tests/test_repo_variables.py +270 -0
- package/tools/vds-scripts/bitbucket_orchestrator/tests/test_webhooks.py +189 -0
- package/tools/vds-scripts/bitbucket_orchestrator/tests/test_workspace.py +233 -0
- package/tools/vds-scripts/bitbucket_orchestrator/uv.lock +742 -0
- package/tools/vds-scripts/confluence_orchestrator/Dockerfile +19 -0
- package/tools/vds-scripts/confluence_orchestrator/README.md +412 -0
- package/tools/vds-scripts/confluence_orchestrator/SYNC_SCRIPTS.md +127 -0
- package/tools/vds-scripts/confluence_orchestrator/SYNC_STANDARDIZATION.md +108 -0
- package/tools/vds-scripts/confluence_orchestrator/pyproject.toml +48 -0
- package/tools/vds-scripts/confluence_orchestrator/src/confluence_orchestrator/__init__.py +20 -0
- package/tools/vds-scripts/confluence_orchestrator/src/confluence_orchestrator/cli.py +2532 -0
- package/tools/vds-scripts/confluence_orchestrator/src/confluence_orchestrator/config.py +175 -0
- package/tools/vds-scripts/confluence_orchestrator/src/confluence_orchestrator/content.py +290 -0
- package/tools/vds-scripts/confluence_orchestrator/src/confluence_orchestrator/content_v2.py +94 -0
- package/tools/vds-scripts/confluence_orchestrator/src/confluence_orchestrator/crawl_tree.py +1835 -0
- package/tools/vds-scripts/confluence_orchestrator/src/confluence_orchestrator/errors.py +80 -0
- package/tools/vds-scripts/confluence_orchestrator/src/confluence_orchestrator/eventing.py +109 -0
- package/tools/vds-scripts/confluence_orchestrator/src/confluence_orchestrator/http.py +1114 -0
- package/tools/vds-scripts/confluence_orchestrator/src/confluence_orchestrator/orchestration.py +165 -0
- package/tools/vds-scripts/confluence_orchestrator/src/confluence_orchestrator/reporting.py +78 -0
- package/tools/vds-scripts/confluence_orchestrator/src/confluence_orchestrator/tree.py +121 -0
- package/tools/vds-scripts/confluence_orchestrator/sync_pdfs_from_markdown.py +213 -0
- package/tools/vds-scripts/confluence_orchestrator/sync_pdfs_to_confluence.py +305 -0
- package/tools/vds-scripts/confluence_orchestrator/sync_png_attachments.py +305 -0
- package/tools/vds-scripts/confluence_orchestrator/tests/__init__.py +0 -0
- package/tools/vds-scripts/confluence_orchestrator/tests/conftest.py +8 -0
- package/tools/vds-scripts/confluence_orchestrator/tests/test_advanced_content.py +224 -0
- package/tools/vds-scripts/confluence_orchestrator/tests/test_advanced_search.py +188 -0
- package/tools/vds-scripts/confluence_orchestrator/tests/test_cache_management.py +247 -0
- package/tools/vds-scripts/confluence_orchestrator/tests/test_cli.py +499 -0
- package/tools/vds-scripts/confluence_orchestrator/tests/test_config.py +83 -0
- package/tools/vds-scripts/confluence_orchestrator/tests/test_content.py +186 -0
- package/tools/vds-scripts/confluence_orchestrator/tests/test_content_flags.py +27 -0
- package/tools/vds-scripts/confluence_orchestrator/tests/test_crawl_tree.py +2250 -0
- package/tools/vds-scripts/confluence_orchestrator/tests/test_draft_management.py +223 -0
- package/tools/vds-scripts/confluence_orchestrator/tests/test_eventing.py +71 -0
- package/tools/vds-scripts/confluence_orchestrator/tests/test_eventing_chaos.py +37 -0
- package/tools/vds-scripts/confluence_orchestrator/tests/test_eventing_rate_limit.py +44 -0
- package/tools/vds-scripts/confluence_orchestrator/tests/test_eventing_timeout.py +49 -0
- package/tools/vds-scripts/confluence_orchestrator/tests/test_export.py +230 -0
- package/tools/vds-scripts/confluence_orchestrator/tests/test_history.py +204 -0
- package/tools/vds-scripts/confluence_orchestrator/tests/test_http.py +117 -0
- package/tools/vds-scripts/confluence_orchestrator/tests/test_orchestration.py +91 -0
- package/tools/vds-scripts/confluence_orchestrator/tests/test_reporting.py +24 -0
- package/tools/vds-scripts/confluence_orchestrator/tests/test_search_cql.py +34 -0
- package/tools/vds-scripts/confluence_orchestrator/tests/test_space_management.py +237 -0
- package/tools/vds-scripts/confluence_orchestrator/tests/test_space_permissions.py +332 -0
- package/tools/vds-scripts/confluence_orchestrator/tests/test_user_group_management.py +388 -0
- package/tools/vds-scripts/confluence_orchestrator/uv.lock +1023 -0
- package/tools/vds-scripts/git_orchestrator/ENHANCEMENT_SUMMARY.md +119 -0
- package/tools/vds-scripts/git_orchestrator/README.md +280 -0
- package/tools/vds-scripts/git_orchestrator/VERIFICATION_REPORT.md +152 -0
- package/tools/vds-scripts/git_orchestrator/pyproject.toml +35 -0
- package/tools/vds-scripts/git_orchestrator/src/vds_git_orchestrator/__init__.py +7 -0
- package/tools/vds-scripts/git_orchestrator/src/vds_git_orchestrator/__main__.py +4 -0
- package/tools/vds-scripts/git_orchestrator/src/vds_git_orchestrator/cli.py +847 -0
- package/tools/vds-scripts/git_orchestrator/src/vds_git_orchestrator/logging_config.py +63 -0
- package/tools/vds-scripts/git_orchestrator/src/vds_git_orchestrator/manifest.py +129 -0
- package/tools/vds-scripts/git_orchestrator/src/vds_git_orchestrator/orchestrator.py +819 -0
- package/tools/vds-scripts/git_orchestrator/src/vds_git_orchestrator/reporting.py +53 -0
- package/tools/vds-scripts/git_orchestrator/tests/__init__.py +0 -0
- package/tools/vds-scripts/git_orchestrator/tests/test_cli_settings.py +21 -0
- package/tools/vds-scripts/git_orchestrator/tests/test_integration.py +74 -0
- package/tools/vds-scripts/git_orchestrator/tests/test_manifest.py +79 -0
- package/tools/vds-scripts/git_orchestrator/tests/test_orchestrator.py +204 -0
- package/tools/vds-scripts/git_orchestrator/tests/test_public_api.py +236 -0
- package/tools/vds-scripts/git_orchestrator/tests/test_resilience.py +345 -0
- package/tools/vds-scripts/git_orchestrator/uv.lock +271 -0
- package/tools/vds-scripts/jira_orchestrator/README.md +770 -0
- package/tools/vds-scripts/jira_orchestrator/pyproject.toml +39 -0
- package/tools/vds-scripts/jira_orchestrator/src/vds_jira_orchestrator/__init__.py +1 -0
- package/tools/vds-scripts/jira_orchestrator/src/vds_jira_orchestrator/adapter.py +1320 -0
- package/tools/vds-scripts/jira_orchestrator/src/vds_jira_orchestrator/cli.py +2271 -0
- package/tools/vds-scripts/jira_orchestrator/src/vds_jira_orchestrator/config.py +138 -0
- package/tools/vds-scripts/jira_orchestrator/src/vds_jira_orchestrator/errors.py +67 -0
- package/tools/vds-scripts/jira_orchestrator/src/vds_jira_orchestrator/reporting.py +65 -0
- package/tools/vds-scripts/jira_orchestrator/tests/__init__.py +1 -0
- package/tools/vds-scripts/jira_orchestrator/tests/conftest.py +86 -0
- package/tools/vds-scripts/jira_orchestrator/tests/test_adapter_agile_list_payloads.py +54 -0
- package/tools/vds-scripts/jira_orchestrator/tests/test_adapter_bulk_operations.py +69 -0
- package/tools/vds-scripts/jira_orchestrator/tests/test_adapter_components.py +57 -0
- package/tools/vds-scripts/jira_orchestrator/tests/test_adapter_createmeta.py +45 -0
- package/tools/vds-scripts/jira_orchestrator/tests/test_adapter_dashboard.py +117 -0
- package/tools/vds-scripts/jira_orchestrator/tests/test_adapter_issue_properties.py +54 -0
- package/tools/vds-scripts/jira_orchestrator/tests/test_adapter_permissions_compat.py +42 -0
- package/tools/vds-scripts/jira_orchestrator/tests/test_adapter_reindex.py +42 -0
- package/tools/vds-scripts/jira_orchestrator/tests/test_adapter_remote_links.py +76 -0
- package/tools/vds-scripts/jira_orchestrator/tests/test_adapter_transitions.py +91 -0
- package/tools/vds-scripts/jira_orchestrator/tests/test_adapter_user_management.py +110 -0
- package/tools/vds-scripts/jira_orchestrator/tests/test_adapter_version_management.py +133 -0
- package/tools/vds-scripts/jira_orchestrator/tests/test_adapter_watchers.py +41 -0
- package/tools/vds-scripts/jira_orchestrator/tests/test_advanced_search.py +164 -0
- package/tools/vds-scripts/jira_orchestrator/tests/test_agile.py +256 -0
- package/tools/vds-scripts/jira_orchestrator/tests/test_application_properties.py +193 -0
- package/tools/vds-scripts/jira_orchestrator/tests/test_backlog.py +91 -0
- package/tools/vds-scripts/jira_orchestrator/tests/test_bulk_operations.py +277 -0
- package/tools/vds-scripts/jira_orchestrator/tests/test_cli.py +106 -0
- package/tools/vds-scripts/jira_orchestrator/tests/test_components.py +106 -0
- package/tools/vds-scripts/jira_orchestrator/tests/test_config.py +164 -0
- package/tools/vds-scripts/jira_orchestrator/tests/test_dashboard.py +122 -0
- package/tools/vds-scripts/jira_orchestrator/tests/test_discover_fields.py +207 -0
- package/tools/vds-scripts/jira_orchestrator/tests/test_filter_management.py +333 -0
- package/tools/vds-scripts/jira_orchestrator/tests/test_issue_archiving.py +164 -0
- package/tools/vds-scripts/jira_orchestrator/tests/test_issue_links.py +257 -0
- package/tools/vds-scripts/jira_orchestrator/tests/test_issue_properties.py +171 -0
- package/tools/vds-scripts/jira_orchestrator/tests/test_link_types.py +314 -0
- package/tools/vds-scripts/jira_orchestrator/tests/test_parse_set.py +37 -0
- package/tools/vds-scripts/jira_orchestrator/tests/test_permissions.py +273 -0
- package/tools/vds-scripts/jira_orchestrator/tests/test_reindex.py +81 -0
- package/tools/vds-scripts/jira_orchestrator/tests/test_remote_links.py +254 -0
- package/tools/vds-scripts/jira_orchestrator/tests/test_security_schemes.py +170 -0
- package/tools/vds-scripts/jira_orchestrator/tests/test_transitions_changelog.py +114 -0
- package/tools/vds-scripts/jira_orchestrator/tests/test_user_management.py +226 -0
- package/tools/vds-scripts/jira_orchestrator/tests/test_version_management.py +339 -0
- package/tools/vds-scripts/jira_orchestrator/tests/test_watchers.py +101 -0
- package/tools/vds-scripts/jira_orchestrator/tests/test_worklog.py +223 -0
- package/tools/vds-scripts/jira_orchestrator/uv.lock +738 -0
- package/tools/vds-scripts/mcp_server/Dockerfile +34 -0
- package/tools/vds-scripts/mcp_server/README.md +140 -0
- package/tools/vds-scripts/mcp_server/pyproject.toml +42 -0
- package/tools/vds-scripts/mcp_server/src/vds_mcp_server/__init__.py +4 -0
- package/tools/vds-scripts/mcp_server/src/vds_mcp_server/config.py +36 -0
- package/tools/vds-scripts/mcp_server/src/vds_mcp_server/server.py +66 -0
- package/tools/vds-scripts/mcp_server/src/vds_mcp_server/tools/__init__.py +14 -0
- package/tools/vds-scripts/mcp_server/src/vds_mcp_server/tools/bitbucket_tools.py +47 -0
- package/tools/vds-scripts/mcp_server/src/vds_mcp_server/tools/confluence_tools.py +59 -0
- package/tools/vds-scripts/mcp_server/src/vds_mcp_server/tools/git_tools.py +71 -0
- package/tools/vds-scripts/mcp_server/src/vds_mcp_server/tools/jira_tools.py +63 -0
- package/tools/vds-scripts/mcp_server/tests/__init__.py +2 -0
- package/tools/vds-scripts/mcp_server/tests/conftest.py +29 -0
- package/tools/vds-scripts/mcp_server/tests/unit/__init__.py +2 -0
- package/tools/vds-scripts/mcp_server/tests/unit/test_bitbucket_tools.py +25 -0
- package/tools/vds-scripts/mcp_server/tests/unit/test_confluence_tools.py +25 -0
- package/tools/vds-scripts/mcp_server/tests/unit/test_git_tools.py +32 -0
- package/tools/vds-scripts/mcp_server/tests/unit/test_jira_tools.py +32 -0
- package/tools/vds-scripts/mcp_server/tests/verification/__init__.py +2 -0
- package/tools/vds-scripts/mcp_server/tests/verification/test_mcp_confluence_tools.py +40 -0
- package/tools/vds-scripts/mcp_server/tests/verification/test_mcp_jira_tools.py +37 -0
- package/tools/vds-scripts/mcp_server/tests/verification/test_mcp_tool_registration.py +47 -0
- package/tools/vds-scripts/mcp_server/uv.lock +1032 -0
- package/tools/vds-scripts/mypy.ini +5 -0
- package/tools/vds-scripts/pyproject.toml +29 -0
- package/tools/vds-scripts/repo-manifest.yaml +273 -0
- package/tools/vds-scripts/repo-manifest.yaml.example +25 -0
- package/tools/vds-scripts/scripts/BRD-Validation-API.postman_collection.json +706 -0
- package/tools/vds-scripts/scripts/BRD-Validation-README.md +308 -0
- package/tools/vds-scripts/scripts/README.md +162 -0
- package/tools/vds-scripts/scripts/bootstrap_uv.sh +30 -0
- package/tools/vds-scripts/scripts/brd-validation-environment.json +51 -0
- package/tools/vds-scripts/scripts/brd-validation-test-results.json +13023 -0
- package/tools/vds-scripts/scripts/brd_coverage_report.json +276 -0
- package/tools/vds-scripts/scripts/create_memory_session.py +35 -0
- package/tools/vds-scripts/scripts/deployment/load_docker_images_offline.sh +90 -0
- package/tools/vds-scripts/scripts/final_completion_report.md +139 -0
- package/tools/vds-scripts/scripts/folder_structure_report.json +321 -0
- package/tools/vds-scripts/scripts/generate_completion_report.py +125 -0
- package/tools/vds-scripts/scripts/generate_intellij_modules.py +150 -0
- package/tools/vds-scripts/scripts/link_integrity_report.json +807 -0
- package/tools/vds-scripts/scripts/move_audit_artifact_pages.py +255 -0
- package/tools/vds-scripts/scripts/move_audit_artifact_pages_rest.py +165 -0
- package/tools/vds-scripts/scripts/move_wrong_dept_pages.py +216 -0
- package/tools/vds-scripts/scripts/save_intellij_memories.py +120 -0
- package/tools/vds-scripts/scripts/save_memories_to_vds_ai.py +83 -0
- package/tools/vds-scripts/scripts/save_memories_vds_style.py +129 -0
- package/tools/vds-scripts/scripts/search_intellij_memories.py +50 -0
- package/tools/vds-scripts/scripts/setup_intellij_workspace.py +65 -0
- package/tools/vds-scripts/scripts/target-state-automation/README.md +89 -0
- package/tools/vds-scripts/scripts/target-state-automation/confluence_sync_coordinator.sh +27 -0
- package/tools/vds-scripts/scripts/target-state-automation/coordination.sh +114 -0
- package/tools/vds-scripts/scripts/target-state-automation/diagram_coordinator.sh +25 -0
- package/tools/vds-scripts/scripts/target-state-automation/docs_root.sh +22 -0
- package/tools/vds-scripts/scripts/target-state-automation/generate_diagrams.sh +22 -0
- package/tools/vds-scripts/scripts/target-state-automation/markdown_coordinator.sh +25 -0
- package/tools/vds-scripts/scripts/target-state-automation/progress_dashboard.sh +17 -0
- package/tools/vds-scripts/scripts/target-state-automation/schema_coordinator.sh +25 -0
- package/tools/vds-scripts/scripts/target-state-automation/sync_confluence.sh +30 -0
- package/tools/vds-scripts/scripts/target-state-automation/update_dependencies.sh +19 -0
- package/tools/vds-scripts/scripts/target-state-automation/validate_links.sh +86 -0
- package/tools/vds-scripts/scripts/target-state-automation/validate_markdown.sh +52 -0
- package/tools/vds-scripts/scripts/target-state-automation/validate_schemas.sh +26 -0
- package/tools/vds-scripts/scripts/target-state-automation/validate_structure.sh +98 -0
- package/tools/vds-scripts/scripts/update_modules_xml.py +190 -0
- package/tools/vds-scripts/scripts/uv-workspace-alignment-verification-2026-03-25.md +128 -0
- package/tools/vds-scripts/scripts/validate_brd_coverage.py +179 -0
- package/tools/vds-scripts/scripts/validate_folder_structure.py +240 -0
- package/tools/vds-scripts/scripts/validate_link_integrity.py +272 -0
- package/tools/vds-scripts/scripts/vds_sh_helpers.sh +180 -0
- package/tools/vds-scripts/scripts/verification/phase2_portable_paths_ubuntu_docker.sh +26 -0
- package/tools/vds-scripts/scripts/worktree_uv.sh +48 -0
- package/tools/vds-scripts/uv.lock +8 -0
- package/tools/vds-scripts/vds_cli/README.md +126 -0
- package/tools/vds-scripts/vds_cli/VERIFICATION_REPORT.md +41 -0
- package/tools/vds-scripts/vds_cli/pyproject.toml +38 -0
- package/tools/vds-scripts/vds_cli/src/vds_cli/__init__.py +3 -0
- package/tools/vds-scripts/vds_cli/src/vds_cli/cli.py +173 -0
- package/tools/vds-scripts/vds_cli/src/vds_cli/docs_sync.py +1203 -0
- package/tools/vds-scripts/vds_cli/src/vds_cli/env.py +41 -0
- package/tools/vds-scripts/vds_cli/src/vds_cli/google_sheets_orchestrator/__init__.py +3 -0
- package/tools/vds-scripts/vds_cli/src/vds_cli/google_sheets_orchestrator/google_sheets_orchestrator.py +198 -0
- package/tools/vds-scripts/vds_cli/src/vds_cli/router.py +93 -0
- package/tools/vds-scripts/vds_cli/src/vds_cli/sync_api.py +647 -0
- package/tools/vds-scripts/vds_cli/src/vds_cli/sync_service.py +266 -0
- package/tools/vds-scripts/vds_cli/tests/__init__.py +2 -0
- package/tools/vds-scripts/vds_cli/tests/conftest.py +49 -0
- package/tools/vds-scripts/vds_cli/tests/unit/__init__.py +2 -0
- package/tools/vds-scripts/vds_cli/tests/unit/test_cli.py +143 -0
- package/tools/vds-scripts/vds_cli/tests/unit/test_docs_sync.py +422 -0
- package/tools/vds-scripts/vds_cli/tests/unit/test_env.py +51 -0
- package/tools/vds-scripts/vds_cli/tests/unit/test_router.py +72 -0
- package/tools/vds-scripts/vds_cli/tests/unit/test_sync_api.py +357 -0
- package/tools/vds-scripts/vds_cli/tests/unit/test_sync_service.py +160 -0
- package/tools/vds-scripts/vds_cli/tests/verification/__init__.py +2 -0
- package/tools/vds-scripts/vds_cli/tests/verification/test_bitbucket_real.py +33 -0
- package/tools/vds-scripts/vds_cli/tests/verification/test_confluence_real.py +35 -0
- package/tools/vds-scripts/vds_cli/tests/verification/test_jira_real.py +41 -0
- package/tools/vds-scripts/vds_cli/uv.lock +524 -0
- package/tools/vds-scripts/vds_cli_common/README.md +190 -0
- package/tools/vds-scripts/vds_cli_common/pyproject.toml +92 -0
- package/tools/vds-scripts/vds_cli_common/src/vds_cli_common/__init__.py +34 -0
- package/tools/vds-scripts/vds_cli_common/src/vds_cli_common/completers.py +139 -0
- package/tools/vds-scripts/vds_cli_common/src/vds_cli_common/context.py +201 -0
- package/tools/vds-scripts/vds_cli_common/src/vds_cli_common/env.py +119 -0
- package/tools/vds-scripts/vds_cli_common/src/vds_cli_common/errors.py +318 -0
- package/tools/vds-scripts/vds_cli_common/src/vds_cli_common/output.py +284 -0
- package/tools/vds-scripts/vds_cli_common/src/vds_cli_common/paths.py +78 -0
- package/tools/vds-scripts/vds_cli_common/src/vds_cli_common/testing.py +213 -0
- package/tools/vds-scripts/vds_cli_common/src/vds_cli_common/version.py +85 -0
- package/tools/vds-scripts/vds_cli_common/tests/__init__.py +1 -0
- package/tools/vds-scripts/vds_cli_common/tests/test_completers.py +148 -0
- package/tools/vds-scripts/vds_cli_common/tests/test_context.py +192 -0
- package/tools/vds-scripts/vds_cli_common/tests/test_env.py +102 -0
- package/tools/vds-scripts/vds_cli_common/tests/test_errors.py +186 -0
- package/tools/vds-scripts/vds_cli_common/tests/test_output.py +229 -0
- package/tools/vds-scripts/vds_cli_common/tests/test_paths.py +61 -0
- package/tools/vds-scripts/vds_cli_common/tests/test_testing.py +138 -0
- package/tools/vds-scripts/vds_cli_common/tests/test_version.py +64 -0
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import json
|
|
4
|
+
from collections.abc import Iterable
|
|
5
|
+
from datetime import datetime
|
|
6
|
+
from pathlib import Path
|
|
7
|
+
|
|
8
|
+
from rich.console import Console
|
|
9
|
+
from rich.table import Table
|
|
10
|
+
|
|
11
|
+
from .orchestrator import RepoResult, RunSummary
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
def print_summary(summary: RunSummary) -> None:
|
|
15
|
+
console = Console()
|
|
16
|
+
|
|
17
|
+
header = (
|
|
18
|
+
f"Total: {summary.total} | "
|
|
19
|
+
f"Successes: {summary.successes} | "
|
|
20
|
+
f"Updated: {summary.updates} | "
|
|
21
|
+
f"Failures: {summary.failures}"
|
|
22
|
+
)
|
|
23
|
+
console.print(f"[bold cyan]Git operation summary[/bold cyan] :: {header}")
|
|
24
|
+
|
|
25
|
+
if summary.failures == 0:
|
|
26
|
+
return
|
|
27
|
+
|
|
28
|
+
console.print("[bold red]Failures:[/bold red]")
|
|
29
|
+
table = Table("Repository", "Project", "Message", show_lines=False)
|
|
30
|
+
for result in _failed(summary.results.values()):
|
|
31
|
+
table.add_row(result.repo.name, result.repo.project, result.message or "")
|
|
32
|
+
console.print(table)
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
def _failed(results: Iterable[RepoResult]) -> list[RepoResult]:
|
|
36
|
+
return [result for result in results if not result.success]
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
def write_json_report(
|
|
40
|
+
summary: RunSummary,
|
|
41
|
+
report_dir: Path,
|
|
42
|
+
metadata: dict | None = None,
|
|
43
|
+
) -> Path:
|
|
44
|
+
report_dir.mkdir(parents=True, exist_ok=True)
|
|
45
|
+
timestamp = datetime.utcnow().strftime("%Y%m%dT%H%M%SZ")
|
|
46
|
+
payload = {
|
|
47
|
+
"timestamp": timestamp,
|
|
48
|
+
"metadata": metadata or {},
|
|
49
|
+
"summary": summary.to_dict(),
|
|
50
|
+
}
|
|
51
|
+
path = report_dir / f"git-run-{timestamp}.json"
|
|
52
|
+
path.write_text(json.dumps(payload, indent=2, ensure_ascii=False))
|
|
53
|
+
return path
|
|
File without changes
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import os
|
|
2
|
+
|
|
3
|
+
from vds_git_orchestrator.cli import _default_concurrency
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
def test_default_concurrency_from_env(monkeypatch):
|
|
7
|
+
monkeypatch.setenv("VDS_GIT_CONCURRENCY_DEFAULT", "12")
|
|
8
|
+
assert _default_concurrency() == 12
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
def test_default_concurrency_cpu_fallback(monkeypatch):
|
|
12
|
+
monkeypatch.delenv("VDS_GIT_CONCURRENCY_DEFAULT", raising=False)
|
|
13
|
+
monkeypatch.setattr(os, "cpu_count", lambda: 6)
|
|
14
|
+
assert _default_concurrency() == 3 # max(2, 6//2)=3 within cap
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
def test_default_concurrency_invalid_env(monkeypatch):
|
|
18
|
+
monkeypatch.setenv("VDS_GIT_CONCURRENCY_DEFAULT", "bad")
|
|
19
|
+
monkeypatch.setattr(os, "cpu_count", lambda: 32)
|
|
20
|
+
# cpu fallback -> max(2, 16)=16, capped at 8
|
|
21
|
+
assert _default_concurrency() == 8
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
import os
|
|
2
|
+
import subprocess
|
|
3
|
+
from pathlib import Path
|
|
4
|
+
|
|
5
|
+
import pytest
|
|
6
|
+
from vds_git_orchestrator.manifest import Manifest, ManifestFilters, Repository
|
|
7
|
+
from vds_git_orchestrator.orchestrator import GitOperation, orchestrate_repositories
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
def _create_remote_repo(base: Path, project: str, name: str) -> None:
|
|
11
|
+
remote_path = base / project / f"{name}.git"
|
|
12
|
+
remote_path.mkdir(parents=True, exist_ok=True)
|
|
13
|
+
subprocess.run(["git", "init", "--bare", str(remote_path)], check=True, stdout=subprocess.DEVNULL)
|
|
14
|
+
|
|
15
|
+
seed = base.parent / "seed"
|
|
16
|
+
subprocess.run(["git", "init", "-b", "release", str(seed)], check=True, stdout=subprocess.DEVNULL)
|
|
17
|
+
readme = seed / "README.md"
|
|
18
|
+
readme.write_text("seed repo")
|
|
19
|
+
|
|
20
|
+
env = {
|
|
21
|
+
**os.environ,
|
|
22
|
+
"GIT_AUTHOR_NAME": "VDS Bot",
|
|
23
|
+
"GIT_AUTHOR_EMAIL": "vds@example.com",
|
|
24
|
+
"GIT_COMMITTER_NAME": "VDS Bot",
|
|
25
|
+
"GIT_COMMITTER_EMAIL": "vds@example.com",
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
subprocess.run(["git", "-C", str(seed), "add", "README.md"], check=True, env=env, stdout=subprocess.DEVNULL)
|
|
29
|
+
subprocess.run(["git", "-C", str(seed), "commit", "-m", "seed"], check=True, env=env, stdout=subprocess.DEVNULL)
|
|
30
|
+
subprocess.run(["git", "-C", str(seed), "remote", "add", "origin", str(remote_path)], check=True, stdout=subprocess.DEVNULL)
|
|
31
|
+
subprocess.run(["git", "-C", str(seed), "push", "origin", "release"], check=True, env=env, stdout=subprocess.DEVNULL)
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
@pytest.mark.asyncio
|
|
35
|
+
async def test_clone_against_local_bare_repo(tmp_path: Path, monkeypatch: pytest.MonkeyPatch) -> None:
|
|
36
|
+
remote_root = tmp_path / "remote"
|
|
37
|
+
_create_remote_repo(remote_root, "lep", "service-a")
|
|
38
|
+
|
|
39
|
+
monkeypatch.setenv("VDS_BITBUCKET_HTTP_BASE", remote_root.as_uri())
|
|
40
|
+
monkeypatch.delenv("VDS_USERNAME", raising=False)
|
|
41
|
+
monkeypatch.delenv("VDS_PASSWORD", raising=False)
|
|
42
|
+
|
|
43
|
+
workspace = tmp_path / "workspace"
|
|
44
|
+
repo = Repository(
|
|
45
|
+
name="service-a",
|
|
46
|
+
path=workspace / "service-a",
|
|
47
|
+
project="lep",
|
|
48
|
+
branch="release",
|
|
49
|
+
tags=set(),
|
|
50
|
+
)
|
|
51
|
+
|
|
52
|
+
manifest = Manifest([repo])
|
|
53
|
+
summary = await orchestrate_repositories(
|
|
54
|
+
manifest=manifest,
|
|
55
|
+
operation=GitOperation.CLONE,
|
|
56
|
+
filters=ManifestFilters(),
|
|
57
|
+
concurrency=2,
|
|
58
|
+
dry_run=False,
|
|
59
|
+
run_id="integration",
|
|
60
|
+
)
|
|
61
|
+
|
|
62
|
+
assert summary.total == 1
|
|
63
|
+
assert summary.successes == 1
|
|
64
|
+
assert summary.updates == 1
|
|
65
|
+
assert (workspace / "service-a/.git").exists()
|
|
66
|
+
|
|
67
|
+
# Ensure the release branch was checked out
|
|
68
|
+
result = subprocess.run(
|
|
69
|
+
["git", "-C", str(workspace / "service-a"), "rev-parse", "--abbrev-ref", "HEAD"],
|
|
70
|
+
check=True,
|
|
71
|
+
capture_output=True,
|
|
72
|
+
text=True,
|
|
73
|
+
)
|
|
74
|
+
assert result.stdout.strip() == "release"
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
from pathlib import Path
|
|
2
|
+
|
|
3
|
+
from vds_git_orchestrator.manifest import Manifest, ManifestFilters, Repository, load_manifest
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
def test_load_manifest_roundtrip(tmp_path: Path) -> None:
|
|
7
|
+
manifest_path = tmp_path / "repos.yaml"
|
|
8
|
+
manifest_path.write_text(
|
|
9
|
+
"""
|
|
10
|
+
repositories:
|
|
11
|
+
- name: service-a
|
|
12
|
+
path: services/service-a
|
|
13
|
+
project: lep
|
|
14
|
+
branch: release
|
|
15
|
+
tags: ["core", "backend"]
|
|
16
|
+
hooks:
|
|
17
|
+
pre_pull:
|
|
18
|
+
- "./scripts/check.sh"
|
|
19
|
+
- name: web-ui
|
|
20
|
+
path: web/web-ui
|
|
21
|
+
project: insurance
|
|
22
|
+
branch: main
|
|
23
|
+
"""
|
|
24
|
+
)
|
|
25
|
+
|
|
26
|
+
manifest = load_manifest(manifest_path)
|
|
27
|
+
|
|
28
|
+
assert len(manifest.repositories) == 2
|
|
29
|
+
repo_a = manifest.repositories[0]
|
|
30
|
+
assert repo_a.name == "service-a"
|
|
31
|
+
assert repo_a.path == Path("services/service-a")
|
|
32
|
+
assert repo_a.project == "lep"
|
|
33
|
+
assert repo_a.branch == "release"
|
|
34
|
+
assert repo_a.tags == {"core", "backend"}
|
|
35
|
+
assert repo_a.hooks["pre_pull"] == ["./scripts/check.sh"]
|
|
36
|
+
|
|
37
|
+
repo_b = manifest.repositories[1]
|
|
38
|
+
assert repo_b.name == "web-ui"
|
|
39
|
+
assert repo_b.branch == "main"
|
|
40
|
+
assert repo_b.tags == set()
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
def test_manifest_filtering_by_project_tag_and_name(tmp_path: Path) -> None:
|
|
44
|
+
repo_root = tmp_path / "workspace"
|
|
45
|
+
repo_root.mkdir()
|
|
46
|
+
|
|
47
|
+
manifest = Manifest(
|
|
48
|
+
repositories=[
|
|
49
|
+
Repository(
|
|
50
|
+
name="service-a",
|
|
51
|
+
path=repo_root / "service-a",
|
|
52
|
+
project="lep",
|
|
53
|
+
branch="release",
|
|
54
|
+
tags={"core", "backend"},
|
|
55
|
+
),
|
|
56
|
+
Repository(
|
|
57
|
+
name="service-b",
|
|
58
|
+
path=repo_root / "service-b",
|
|
59
|
+
project="insurance",
|
|
60
|
+
branch="release",
|
|
61
|
+
tags={"event"},
|
|
62
|
+
),
|
|
63
|
+
Repository(
|
|
64
|
+
name="web-ui",
|
|
65
|
+
path=repo_root / "web-ui",
|
|
66
|
+
project="insurance",
|
|
67
|
+
branch="main",
|
|
68
|
+
tags={"frontend"},
|
|
69
|
+
),
|
|
70
|
+
]
|
|
71
|
+
)
|
|
72
|
+
|
|
73
|
+
filters = ManifestFilters(project="insurance", include_tags={"frontend"})
|
|
74
|
+
results = manifest.filter(filters)
|
|
75
|
+
assert [repo.name for repo in results] == ["web-ui"]
|
|
76
|
+
|
|
77
|
+
filters = ManifestFilters(name_match="service-*", exclude_tags={"event"})
|
|
78
|
+
results = manifest.filter(filters)
|
|
79
|
+
assert [repo.name for repo in results] == ["service-a"]
|
|
@@ -0,0 +1,204 @@
|
|
|
1
|
+
from pathlib import Path
|
|
2
|
+
|
|
3
|
+
import pytest
|
|
4
|
+
from vds_git_orchestrator.manifest import Manifest, ManifestFilters, Repository
|
|
5
|
+
from vds_git_orchestrator.orchestrator import GitOperation, orchestrate_repositories
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
@pytest.mark.asyncio
|
|
9
|
+
async def test_orchestrate_repositories_dry_run_skips_hooks(tmp_path: Path) -> None:
|
|
10
|
+
repo_dir = tmp_path / "dry-run"
|
|
11
|
+
repo_dir.mkdir()
|
|
12
|
+
repo = Repository(
|
|
13
|
+
name="dry-run-repo",
|
|
14
|
+
path=repo_dir,
|
|
15
|
+
project="lep",
|
|
16
|
+
branch="release",
|
|
17
|
+
tags=set(),
|
|
18
|
+
hooks={
|
|
19
|
+
"pre_pull": ["touch pre.txt"],
|
|
20
|
+
"post_pull": ["touch post.txt"],
|
|
21
|
+
},
|
|
22
|
+
)
|
|
23
|
+
|
|
24
|
+
manifest = Manifest([repo])
|
|
25
|
+
summary = await orchestrate_repositories(
|
|
26
|
+
manifest=manifest,
|
|
27
|
+
operation=GitOperation.PULL,
|
|
28
|
+
filters=ManifestFilters(),
|
|
29
|
+
concurrency=2,
|
|
30
|
+
dry_run=True,
|
|
31
|
+
run_id="test",
|
|
32
|
+
)
|
|
33
|
+
|
|
34
|
+
assert summary.total == 1
|
|
35
|
+
assert summary.successes == 1
|
|
36
|
+
assert summary.failures == 0
|
|
37
|
+
assert summary.serial_retries == 0
|
|
38
|
+
assert summary.results["dry-run-repo"].message == "dry-run"
|
|
39
|
+
assert not (repo_dir / "pre.txt").exists()
|
|
40
|
+
assert not (repo_dir / "post.txt").exists()
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
def _success_git_result() -> tuple[int, str, str]:
|
|
44
|
+
return 0, "Fast-forward", ""
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
@pytest.mark.asyncio
|
|
48
|
+
async def test_orchestrate_repositories_runs_hooks_on_success(monkeypatch: pytest.MonkeyPatch, tmp_path: Path) -> None:
|
|
49
|
+
repo_dir = tmp_path / "real-run"
|
|
50
|
+
repo_dir.mkdir()
|
|
51
|
+
|
|
52
|
+
pre_marker = repo_dir / "pre.marker"
|
|
53
|
+
post_marker = repo_dir / "post.marker"
|
|
54
|
+
|
|
55
|
+
repo = Repository(
|
|
56
|
+
name="real-run-repo",
|
|
57
|
+
path=repo_dir,
|
|
58
|
+
project="insurance",
|
|
59
|
+
branch="release",
|
|
60
|
+
tags=set(),
|
|
61
|
+
hooks={
|
|
62
|
+
"pre_pull": [f"touch {pre_marker.name}"],
|
|
63
|
+
"post_pull": [f"touch {post_marker.name}"],
|
|
64
|
+
},
|
|
65
|
+
)
|
|
66
|
+
|
|
67
|
+
# Patch _run_git to avoid invoking the real git binary.
|
|
68
|
+
from vds_git_orchestrator import orchestrator as orchestrator_module
|
|
69
|
+
|
|
70
|
+
async def fake_run_git(*args, **kwargs): # type: ignore[no-untyped-def]
|
|
71
|
+
return _success_git_result()
|
|
72
|
+
|
|
73
|
+
monkeypatch.setattr(orchestrator_module, "_run_git", fake_run_git)
|
|
74
|
+
|
|
75
|
+
manifest = Manifest([repo])
|
|
76
|
+
summary = await orchestrate_repositories(
|
|
77
|
+
manifest=manifest,
|
|
78
|
+
operation=GitOperation.PULL,
|
|
79
|
+
filters=ManifestFilters(),
|
|
80
|
+
concurrency=1,
|
|
81
|
+
dry_run=False,
|
|
82
|
+
run_id="run",
|
|
83
|
+
retries=0,
|
|
84
|
+
)
|
|
85
|
+
|
|
86
|
+
assert summary.total == 1
|
|
87
|
+
assert summary.successes == 1
|
|
88
|
+
assert summary.updates == 1
|
|
89
|
+
assert summary.serial_retries == 0
|
|
90
|
+
assert pre_marker.exists()
|
|
91
|
+
assert post_marker.exists()
|
|
92
|
+
|
|
93
|
+
|
|
94
|
+
@pytest.mark.asyncio
|
|
95
|
+
async def test_orchestrate_repositories_serial_retry(monkeypatch: pytest.MonkeyPatch, tmp_path: Path) -> None:
|
|
96
|
+
repo_dir = tmp_path / "net"
|
|
97
|
+
repo_dir.mkdir()
|
|
98
|
+
|
|
99
|
+
repo = Repository(
|
|
100
|
+
name="net-repo",
|
|
101
|
+
path=repo_dir,
|
|
102
|
+
project="lep",
|
|
103
|
+
branch="release",
|
|
104
|
+
tags=set(),
|
|
105
|
+
)
|
|
106
|
+
|
|
107
|
+
from vds_git_orchestrator import orchestrator as orchestrator_module
|
|
108
|
+
|
|
109
|
+
attempts = {repo.name: 0}
|
|
110
|
+
|
|
111
|
+
async def fake_run_git(target_repo: Repository, args: list[str]): # type: ignore[no-untyped-def]
|
|
112
|
+
idx = attempts.setdefault(target_repo.name, 0)
|
|
113
|
+
attempts[target_repo.name] = idx + 1
|
|
114
|
+
if idx == 0:
|
|
115
|
+
return 1, "", "Failed to connect to server"
|
|
116
|
+
return 0, "Already up to date", ""
|
|
117
|
+
|
|
118
|
+
monkeypatch.setattr(orchestrator_module, "_run_git", fake_run_git)
|
|
119
|
+
|
|
120
|
+
manifest = Manifest([repo])
|
|
121
|
+
summary = await orchestrate_repositories(
|
|
122
|
+
manifest=manifest,
|
|
123
|
+
operation=GitOperation.PULL,
|
|
124
|
+
filters=ManifestFilters(),
|
|
125
|
+
concurrency=2,
|
|
126
|
+
dry_run=False,
|
|
127
|
+
run_id="serial",
|
|
128
|
+
retries=0,
|
|
129
|
+
)
|
|
130
|
+
|
|
131
|
+
assert summary.total == 1
|
|
132
|
+
assert summary.successes == 1
|
|
133
|
+
assert summary.failures == 0
|
|
134
|
+
assert summary.serial_retries == 1
|
|
135
|
+
# two attempts: first parallel, second serial fallback
|
|
136
|
+
assert attempts[repo.name] == 2
|
|
137
|
+
|
|
138
|
+
|
|
139
|
+
@pytest.mark.asyncio
|
|
140
|
+
async def test_clone_dry_run(tmp_path: Path) -> None:
|
|
141
|
+
repo = Repository(
|
|
142
|
+
name="service-a",
|
|
143
|
+
path=tmp_path / "service-a",
|
|
144
|
+
project="lep",
|
|
145
|
+
branch="release",
|
|
146
|
+
tags=set(),
|
|
147
|
+
)
|
|
148
|
+
|
|
149
|
+
manifest = Manifest([repo])
|
|
150
|
+
summary = await orchestrate_repositories(
|
|
151
|
+
manifest=manifest,
|
|
152
|
+
operation=GitOperation.CLONE,
|
|
153
|
+
filters=ManifestFilters(),
|
|
154
|
+
concurrency=2,
|
|
155
|
+
dry_run=True,
|
|
156
|
+
run_id="clone-dry",
|
|
157
|
+
)
|
|
158
|
+
|
|
159
|
+
assert summary.total == 1
|
|
160
|
+
assert summary.successes == 1
|
|
161
|
+
assert summary.results["service-a"].message == "dry-run"
|
|
162
|
+
assert summary.serial_retries == 0
|
|
163
|
+
assert not repo.absolute_path.exists()
|
|
164
|
+
|
|
165
|
+
|
|
166
|
+
@pytest.mark.asyncio
|
|
167
|
+
async def test_clone_executes_git(monkeypatch: pytest.MonkeyPatch, tmp_path: Path) -> None:
|
|
168
|
+
repo = Repository(
|
|
169
|
+
name="service-a",
|
|
170
|
+
path=tmp_path / "service-a",
|
|
171
|
+
project="lep",
|
|
172
|
+
branch="release",
|
|
173
|
+
tags=set(),
|
|
174
|
+
)
|
|
175
|
+
|
|
176
|
+
calls: list[tuple[list[str], Path]] = []
|
|
177
|
+
|
|
178
|
+
from vds_git_orchestrator import orchestrator as orchestrator_module
|
|
179
|
+
|
|
180
|
+
async def fake_run_process(command: list[str], cwd: Path): # type: ignore[no-untyped-def]
|
|
181
|
+
calls.append((command, cwd))
|
|
182
|
+
repo.absolute_path.mkdir(parents=True, exist_ok=True)
|
|
183
|
+
return 0, "cloned", ""
|
|
184
|
+
|
|
185
|
+
monkeypatch.setattr(orchestrator_module, "_run_process", fake_run_process)
|
|
186
|
+
|
|
187
|
+
manifest = Manifest([repo])
|
|
188
|
+
summary = await orchestrate_repositories(
|
|
189
|
+
manifest=manifest,
|
|
190
|
+
operation=GitOperation.CLONE,
|
|
191
|
+
filters=ManifestFilters(),
|
|
192
|
+
concurrency=1,
|
|
193
|
+
dry_run=False,
|
|
194
|
+
run_id="clone",
|
|
195
|
+
)
|
|
196
|
+
|
|
197
|
+
assert summary.successes == 1
|
|
198
|
+
assert summary.updates == 1
|
|
199
|
+
assert summary.serial_retries == 0
|
|
200
|
+
assert repo.absolute_path.parent.exists()
|
|
201
|
+
# first argument should be git clone
|
|
202
|
+
assert calls
|
|
203
|
+
assert calls[0][0][0] == "git"
|
|
204
|
+
assert "clone" in calls[0][0]
|
|
@@ -0,0 +1,236 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Tests for vds_git_orchestrator public API exports (TSK-221).
|
|
3
|
+
|
|
4
|
+
Validates FR-21.1.2: Export Repository, Manifest, ManifestFilters, GitOperation,
|
|
5
|
+
RunSummary, orchestrate_repositories from vds_git_orchestrator.__init__
|
|
6
|
+
|
|
7
|
+
These tests verify that:
|
|
8
|
+
1. All public symbols can be imported from the package root
|
|
9
|
+
2. The __all__ list contains all expected symbols
|
|
10
|
+
3. Imported symbols are the correct types/classes
|
|
11
|
+
4. Basic functionality works when using package-level imports
|
|
12
|
+
"""
|
|
13
|
+
|
|
14
|
+
from pathlib import Path
|
|
15
|
+
|
|
16
|
+
import pytest
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
class TestPublicAPIExports:
|
|
20
|
+
"""Test that all public symbols are exported from package root."""
|
|
21
|
+
|
|
22
|
+
def test_import_repository_from_package_root(self) -> None:
|
|
23
|
+
"""Verify Repository can be imported from vds_git_orchestrator."""
|
|
24
|
+
from vds_git_orchestrator import Repository
|
|
25
|
+
|
|
26
|
+
# Verify it's the correct class by checking it's a dataclass
|
|
27
|
+
assert hasattr(Repository, "__dataclass_fields__")
|
|
28
|
+
assert "name" in Repository.__dataclass_fields__
|
|
29
|
+
assert "path" in Repository.__dataclass_fields__
|
|
30
|
+
assert "project" in Repository.__dataclass_fields__
|
|
31
|
+
assert "branch" in Repository.__dataclass_fields__
|
|
32
|
+
|
|
33
|
+
def test_import_manifest_from_package_root(self) -> None:
|
|
34
|
+
"""Verify Manifest can be imported from vds_git_orchestrator."""
|
|
35
|
+
from vds_git_orchestrator import Manifest
|
|
36
|
+
|
|
37
|
+
# Verify it's the correct class
|
|
38
|
+
assert hasattr(Manifest, "__dataclass_fields__")
|
|
39
|
+
assert "repositories" in Manifest.__dataclass_fields__
|
|
40
|
+
assert hasattr(Manifest, "merge")
|
|
41
|
+
assert hasattr(Manifest, "filter")
|
|
42
|
+
|
|
43
|
+
def test_import_manifest_filters_from_package_root(self) -> None:
|
|
44
|
+
"""Verify ManifestFilters can be imported from vds_git_orchestrator."""
|
|
45
|
+
from vds_git_orchestrator import ManifestFilters
|
|
46
|
+
|
|
47
|
+
# Verify it's the correct class
|
|
48
|
+
assert hasattr(ManifestFilters, "__dataclass_fields__")
|
|
49
|
+
assert "project" in ManifestFilters.__dataclass_fields__
|
|
50
|
+
assert "name_match" in ManifestFilters.__dataclass_fields__
|
|
51
|
+
assert "include_tags" in ManifestFilters.__dataclass_fields__
|
|
52
|
+
assert "exclude_tags" in ManifestFilters.__dataclass_fields__
|
|
53
|
+
|
|
54
|
+
def test_import_git_operation_from_package_root(self) -> None:
|
|
55
|
+
"""Verify GitOperation can be imported from vds_git_orchestrator."""
|
|
56
|
+
from vds_git_orchestrator import GitOperation
|
|
57
|
+
|
|
58
|
+
# Verify it's an Enum with expected values
|
|
59
|
+
assert hasattr(GitOperation, "FETCH")
|
|
60
|
+
assert hasattr(GitOperation, "PULL")
|
|
61
|
+
assert hasattr(GitOperation, "CLONE")
|
|
62
|
+
assert hasattr(GitOperation, "STATUS")
|
|
63
|
+
assert GitOperation.PULL.value == "pull"
|
|
64
|
+
|
|
65
|
+
def test_import_run_summary_from_package_root(self) -> None:
|
|
66
|
+
"""Verify RunSummary can be imported from vds_git_orchestrator."""
|
|
67
|
+
from vds_git_orchestrator import RunSummary
|
|
68
|
+
|
|
69
|
+
# Verify it's the correct class
|
|
70
|
+
assert hasattr(RunSummary, "__dataclass_fields__")
|
|
71
|
+
assert "total" in RunSummary.__dataclass_fields__
|
|
72
|
+
assert "successes" in RunSummary.__dataclass_fields__
|
|
73
|
+
assert "failures" in RunSummary.__dataclass_fields__
|
|
74
|
+
assert hasattr(RunSummary, "from_results")
|
|
75
|
+
assert hasattr(RunSummary, "to_dict")
|
|
76
|
+
|
|
77
|
+
def test_import_orchestrate_repositories_from_package_root(self) -> None:
|
|
78
|
+
"""Verify orchestrate_repositories can be imported from vds_git_orchestrator."""
|
|
79
|
+
from vds_git_orchestrator import orchestrate_repositories
|
|
80
|
+
|
|
81
|
+
# Verify it's a callable (async function)
|
|
82
|
+
import inspect
|
|
83
|
+
|
|
84
|
+
assert callable(orchestrate_repositories)
|
|
85
|
+
assert inspect.iscoroutinefunction(orchestrate_repositories)
|
|
86
|
+
|
|
87
|
+
|
|
88
|
+
class TestAllList:
|
|
89
|
+
"""Test that __all__ contains all expected public symbols."""
|
|
90
|
+
|
|
91
|
+
def test_all_list_contains_manifest_symbols(self) -> None:
|
|
92
|
+
"""Verify __all__ includes Repository, Manifest, ManifestFilters."""
|
|
93
|
+
import vds_git_orchestrator
|
|
94
|
+
|
|
95
|
+
assert "Repository" in vds_git_orchestrator.__all__
|
|
96
|
+
assert "Manifest" in vds_git_orchestrator.__all__
|
|
97
|
+
assert "ManifestFilters" in vds_git_orchestrator.__all__
|
|
98
|
+
|
|
99
|
+
def test_all_list_contains_orchestrator_symbols(self) -> None:
|
|
100
|
+
"""Verify __all__ includes GitOperation, RunSummary, orchestrate_repositories."""
|
|
101
|
+
import vds_git_orchestrator
|
|
102
|
+
|
|
103
|
+
assert "GitOperation" in vds_git_orchestrator.__all__
|
|
104
|
+
assert "RunSummary" in vds_git_orchestrator.__all__
|
|
105
|
+
assert "orchestrate_repositories" in vds_git_orchestrator.__all__
|
|
106
|
+
|
|
107
|
+
def test_all_list_has_exactly_six_symbols(self) -> None:
|
|
108
|
+
"""Verify __all__ contains exactly the 6 expected public symbols."""
|
|
109
|
+
import vds_git_orchestrator
|
|
110
|
+
|
|
111
|
+
expected_symbols = {
|
|
112
|
+
"Repository",
|
|
113
|
+
"Manifest",
|
|
114
|
+
"ManifestFilters",
|
|
115
|
+
"GitOperation",
|
|
116
|
+
"RunSummary",
|
|
117
|
+
"orchestrate_repositories",
|
|
118
|
+
}
|
|
119
|
+
assert set(vds_git_orchestrator.__all__) == expected_symbols
|
|
120
|
+
|
|
121
|
+
|
|
122
|
+
class TestImportEquivalence:
|
|
123
|
+
"""Test that package-level imports are equivalent to module-level imports."""
|
|
124
|
+
|
|
125
|
+
def test_repository_import_equivalence(self) -> None:
|
|
126
|
+
"""Verify package import equals module import for Repository."""
|
|
127
|
+
from vds_git_orchestrator import Repository as PkgRepository
|
|
128
|
+
from vds_git_orchestrator.manifest import Repository as ModRepository
|
|
129
|
+
|
|
130
|
+
assert PkgRepository is ModRepository
|
|
131
|
+
|
|
132
|
+
def test_manifest_import_equivalence(self) -> None:
|
|
133
|
+
"""Verify package import equals module import for Manifest."""
|
|
134
|
+
from vds_git_orchestrator import Manifest as PkgManifest
|
|
135
|
+
from vds_git_orchestrator.manifest import Manifest as ModManifest
|
|
136
|
+
|
|
137
|
+
assert PkgManifest is ModManifest
|
|
138
|
+
|
|
139
|
+
def test_manifest_filters_import_equivalence(self) -> None:
|
|
140
|
+
"""Verify package import equals module import for ManifestFilters."""
|
|
141
|
+
from vds_git_orchestrator import ManifestFilters as PkgFilters
|
|
142
|
+
from vds_git_orchestrator.manifest import ManifestFilters as ModFilters
|
|
143
|
+
|
|
144
|
+
assert PkgFilters is ModFilters
|
|
145
|
+
|
|
146
|
+
def test_git_operation_import_equivalence(self) -> None:
|
|
147
|
+
"""Verify package import equals module import for GitOperation."""
|
|
148
|
+
from vds_git_orchestrator import GitOperation as PkgOp
|
|
149
|
+
from vds_git_orchestrator.orchestrator import GitOperation as ModOp
|
|
150
|
+
|
|
151
|
+
assert PkgOp is ModOp
|
|
152
|
+
|
|
153
|
+
def test_run_summary_import_equivalence(self) -> None:
|
|
154
|
+
"""Verify package import equals module import for RunSummary."""
|
|
155
|
+
from vds_git_orchestrator import RunSummary as PkgSummary
|
|
156
|
+
from vds_git_orchestrator.orchestrator import RunSummary as ModSummary
|
|
157
|
+
|
|
158
|
+
assert PkgSummary is ModSummary
|
|
159
|
+
|
|
160
|
+
def test_orchestrate_repositories_import_equivalence(self) -> None:
|
|
161
|
+
"""Verify package import equals module import for orchestrate_repositories."""
|
|
162
|
+
from vds_git_orchestrator import orchestrate_repositories as pkg_func
|
|
163
|
+
from vds_git_orchestrator.orchestrator import orchestrate_repositories as mod_func
|
|
164
|
+
|
|
165
|
+
assert pkg_func is mod_func
|
|
166
|
+
|
|
167
|
+
|
|
168
|
+
class TestBasicFunctionality:
|
|
169
|
+
"""Test basic functionality using package-level imports."""
|
|
170
|
+
|
|
171
|
+
def test_create_repository_from_package_import(self, tmp_path: Path) -> None:
|
|
172
|
+
"""Verify Repository can be instantiated using package import."""
|
|
173
|
+
from vds_git_orchestrator import Repository
|
|
174
|
+
|
|
175
|
+
repo = Repository(
|
|
176
|
+
name="test-repo",
|
|
177
|
+
path=tmp_path / "test-repo",
|
|
178
|
+
project="insurance",
|
|
179
|
+
branch="release",
|
|
180
|
+
tags={"core", "backend"},
|
|
181
|
+
)
|
|
182
|
+
|
|
183
|
+
assert repo.name == "test-repo"
|
|
184
|
+
assert repo.project == "insurance"
|
|
185
|
+
assert repo.branch == "release"
|
|
186
|
+
assert repo.tags == {"core", "backend"}
|
|
187
|
+
|
|
188
|
+
def test_create_manifest_from_package_import(self, tmp_path: Path) -> None:
|
|
189
|
+
"""Verify Manifest can be instantiated using package import."""
|
|
190
|
+
from vds_git_orchestrator import Manifest, Repository
|
|
191
|
+
|
|
192
|
+
repo = Repository(
|
|
193
|
+
name="test-repo",
|
|
194
|
+
path=tmp_path / "test-repo",
|
|
195
|
+
project="insurance",
|
|
196
|
+
branch="release",
|
|
197
|
+
)
|
|
198
|
+
manifest = Manifest(repositories=[repo])
|
|
199
|
+
|
|
200
|
+
assert len(manifest.repositories) == 1
|
|
201
|
+
assert manifest.repositories[0].name == "test-repo"
|
|
202
|
+
|
|
203
|
+
def test_filter_manifest_from_package_import(self, tmp_path: Path) -> None:
|
|
204
|
+
"""Verify ManifestFilters works with package imports."""
|
|
205
|
+
from vds_git_orchestrator import Manifest, ManifestFilters, Repository
|
|
206
|
+
|
|
207
|
+
repos = [
|
|
208
|
+
Repository(
|
|
209
|
+
name="service-a",
|
|
210
|
+
path=tmp_path / "service-a",
|
|
211
|
+
project="insurance",
|
|
212
|
+
branch="release",
|
|
213
|
+
),
|
|
214
|
+
Repository(
|
|
215
|
+
name="service-b",
|
|
216
|
+
path=tmp_path / "service-b",
|
|
217
|
+
project="lep",
|
|
218
|
+
branch="release",
|
|
219
|
+
),
|
|
220
|
+
]
|
|
221
|
+
manifest = Manifest(repositories=repos)
|
|
222
|
+
filters = ManifestFilters(project="insurance")
|
|
223
|
+
|
|
224
|
+
filtered = manifest.filter(filters)
|
|
225
|
+
|
|
226
|
+
assert len(filtered) == 1
|
|
227
|
+
assert filtered[0].name == "service-a"
|
|
228
|
+
|
|
229
|
+
def test_git_operation_enum_from_package_import(self) -> None:
|
|
230
|
+
"""Verify GitOperation enum works with package import."""
|
|
231
|
+
from vds_git_orchestrator import GitOperation
|
|
232
|
+
|
|
233
|
+
assert GitOperation.PULL.value == "pull"
|
|
234
|
+
assert GitOperation.FETCH.value == "fetch"
|
|
235
|
+
assert GitOperation.CLONE.value == "clone"
|
|
236
|
+
assert GitOperation.STATUS.value == "status"
|