@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,284 @@
|
|
|
1
|
+
"""Output Manager for unified CLI output across VDS CLIs.
|
|
2
|
+
|
|
3
|
+
This module provides OutputManager, a class that handles:
|
|
4
|
+
- JSON output (compact/pretty based on mode)
|
|
5
|
+
- Rich tables, panels, and formatted output
|
|
6
|
+
- Spinners for long-running operations
|
|
7
|
+
- Progress bars for batch operations
|
|
8
|
+
- Error messages with hints
|
|
9
|
+
|
|
10
|
+
Usage:
|
|
11
|
+
ctx = CLIContext.from_options(json_only=False)
|
|
12
|
+
out = OutputManager(ctx)
|
|
13
|
+
|
|
14
|
+
with out.spinner("Connecting..."):
|
|
15
|
+
result = client.ping()
|
|
16
|
+
|
|
17
|
+
out.output_json({"status": "ok"})
|
|
18
|
+
"""
|
|
19
|
+
|
|
20
|
+
from __future__ import annotations
|
|
21
|
+
|
|
22
|
+
import json
|
|
23
|
+
import sys
|
|
24
|
+
from collections.abc import Iterator
|
|
25
|
+
from contextlib import contextmanager
|
|
26
|
+
from typing import TYPE_CHECKING, Any
|
|
27
|
+
|
|
28
|
+
if TYPE_CHECKING:
|
|
29
|
+
from rich.progress import Progress
|
|
30
|
+
from rich.table import Table
|
|
31
|
+
|
|
32
|
+
from .context import CLIContext
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
class NullProgress:
|
|
36
|
+
"""No-op progress bar for non-TTY environments."""
|
|
37
|
+
|
|
38
|
+
def add_task(self, description: str, total: float = 100.0, **kwargs: Any) -> int:
|
|
39
|
+
"""Add a task (no-op)."""
|
|
40
|
+
return 0
|
|
41
|
+
|
|
42
|
+
def update(self, task_id: int, advance: float = 1.0, **kwargs: Any) -> None:
|
|
43
|
+
"""Update task progress (no-op)."""
|
|
44
|
+
|
|
45
|
+
def __enter__(self) -> NullProgress:
|
|
46
|
+
"""Enter context."""
|
|
47
|
+
return self
|
|
48
|
+
|
|
49
|
+
def __exit__(self, *args: Any) -> None:
|
|
50
|
+
"""Exit context."""
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
class OutputManager:
|
|
54
|
+
"""Unified output manager for VDS CLIs.
|
|
55
|
+
|
|
56
|
+
Handles all output operations with context-aware formatting:
|
|
57
|
+
- JSON output (always clean, parseable)
|
|
58
|
+
- Rich tables (TTY only)
|
|
59
|
+
- Spinners (TTY only, respects quiet mode)
|
|
60
|
+
- Progress bars (TTY only, respects quiet mode)
|
|
61
|
+
- Error messages (always shown, styled for TTY)
|
|
62
|
+
- Success/info/warning messages (respects quiet mode)
|
|
63
|
+
|
|
64
|
+
Args:
|
|
65
|
+
ctx: CLIContext instance for output settings
|
|
66
|
+
"""
|
|
67
|
+
|
|
68
|
+
def __init__(self, ctx: CLIContext) -> None:
|
|
69
|
+
"""Initialize OutputManager with CLI context."""
|
|
70
|
+
|
|
71
|
+
self.ctx: CLIContext = ctx
|
|
72
|
+
self._console = ctx.console
|
|
73
|
+
self._err_console = ctx.err_console
|
|
74
|
+
|
|
75
|
+
def output_json(self, data: dict[str, Any] | list[Any], pretty: bool = True) -> None:
|
|
76
|
+
"""Output data as JSON to stdout.
|
|
77
|
+
|
|
78
|
+
In json_only mode, always outputs compact JSON.
|
|
79
|
+
Otherwise, outputs pretty-printed JSON or uses Rich JSON highlighting.
|
|
80
|
+
|
|
81
|
+
Args:
|
|
82
|
+
data: Data to output as JSON
|
|
83
|
+
pretty: Whether to pretty-print (ignored in json_only mode)
|
|
84
|
+
"""
|
|
85
|
+
if self.ctx.json_only:
|
|
86
|
+
# Compact JSON for piping/parsing
|
|
87
|
+
print(json.dumps(data, default=str, ensure_ascii=False))
|
|
88
|
+
elif pretty and self.ctx.use_rich_output:
|
|
89
|
+
# Pretty JSON with syntax highlighting
|
|
90
|
+
from rich.json import JSON
|
|
91
|
+
|
|
92
|
+
self._console.print(JSON.from_data(data))
|
|
93
|
+
elif pretty:
|
|
94
|
+
print(json.dumps(data, indent=2, default=str, ensure_ascii=False))
|
|
95
|
+
else:
|
|
96
|
+
print(json.dumps(data, default=str, ensure_ascii=False))
|
|
97
|
+
|
|
98
|
+
def output_error(
|
|
99
|
+
self,
|
|
100
|
+
message: str,
|
|
101
|
+
*,
|
|
102
|
+
hint: str | None = None,
|
|
103
|
+
details: dict[str, Any] | None = None,
|
|
104
|
+
) -> None:
|
|
105
|
+
"""Output error message to stderr.
|
|
106
|
+
|
|
107
|
+
In json_only mode, outputs JSON error object.
|
|
108
|
+
Otherwise, outputs styled error with optional hint.
|
|
109
|
+
|
|
110
|
+
Args:
|
|
111
|
+
message: Error message
|
|
112
|
+
hint: Optional hint for resolution
|
|
113
|
+
details: Optional additional details
|
|
114
|
+
"""
|
|
115
|
+
if self.ctx.json_only:
|
|
116
|
+
error_obj: dict[str, Any] = {"error": True, "message": message}
|
|
117
|
+
if hint:
|
|
118
|
+
error_obj["hint"] = hint
|
|
119
|
+
if details:
|
|
120
|
+
error_obj["details"] = details
|
|
121
|
+
print(json.dumps(error_obj), file=sys.stderr)
|
|
122
|
+
else:
|
|
123
|
+
self._err_console.print(f"[bold red]✗ Error:[/bold red] {message}")
|
|
124
|
+
if hint:
|
|
125
|
+
self._err_console.print(f"[yellow] 💡 Hint:[/yellow] {hint}")
|
|
126
|
+
if details:
|
|
127
|
+
for key, value in details.items():
|
|
128
|
+
self._err_console.print(f"[dim] {key}:[/dim] {value}")
|
|
129
|
+
|
|
130
|
+
def output_success(self, message: str) -> None:
|
|
131
|
+
"""Output success message (suppressed in quiet/json mode).
|
|
132
|
+
|
|
133
|
+
Args:
|
|
134
|
+
message: Success message to display
|
|
135
|
+
"""
|
|
136
|
+
if self.ctx.show_hints:
|
|
137
|
+
self._console.print(f"[bold green]✓[/bold green] {message}")
|
|
138
|
+
|
|
139
|
+
def output_warning(self, message: str) -> None:
|
|
140
|
+
"""Output warning message (suppressed in quiet/json mode).
|
|
141
|
+
|
|
142
|
+
Args:
|
|
143
|
+
message: Warning message to display
|
|
144
|
+
"""
|
|
145
|
+
if self.ctx.show_hints:
|
|
146
|
+
self._console.print(f"[bold yellow]⚠[/bold yellow] {message}")
|
|
147
|
+
|
|
148
|
+
def output_info(self, message: str) -> None:
|
|
149
|
+
"""Output info message (suppressed in quiet/json mode).
|
|
150
|
+
|
|
151
|
+
Args:
|
|
152
|
+
message: Info message to display
|
|
153
|
+
"""
|
|
154
|
+
if self.ctx.show_hints:
|
|
155
|
+
self._console.print(f"[blue]ℹ[/blue] {message}")
|
|
156
|
+
|
|
157
|
+
def suggest_next(self, commands: list[str]) -> None:
|
|
158
|
+
"""Suggest next commands to run (suppressed in quiet/json mode).
|
|
159
|
+
|
|
160
|
+
Args:
|
|
161
|
+
commands: List of suggested commands
|
|
162
|
+
"""
|
|
163
|
+
if self.ctx.show_hints and commands:
|
|
164
|
+
suggestions = ", ".join(f"[cyan]{cmd}[/cyan]" for cmd in commands)
|
|
165
|
+
self._console.print(f"\n[dim]Try next:[/dim] {suggestions}")
|
|
166
|
+
|
|
167
|
+
@contextmanager
|
|
168
|
+
def spinner(self, message: str) -> Iterator[None]:
|
|
169
|
+
"""Show a spinner while executing a block.
|
|
170
|
+
|
|
171
|
+
The spinner is only shown when:
|
|
172
|
+
- stdout is a TTY
|
|
173
|
+
- not in json_only mode
|
|
174
|
+
- not in quiet mode
|
|
175
|
+
|
|
176
|
+
Args:
|
|
177
|
+
message: Message to display with spinner
|
|
178
|
+
|
|
179
|
+
Yields:
|
|
180
|
+
None
|
|
181
|
+
"""
|
|
182
|
+
if not self.ctx.use_spinner:
|
|
183
|
+
yield
|
|
184
|
+
return
|
|
185
|
+
|
|
186
|
+
with self._err_console.status(f"[bold green]{message}", spinner="dots"):
|
|
187
|
+
yield
|
|
188
|
+
|
|
189
|
+
@contextmanager
|
|
190
|
+
def progress(
|
|
191
|
+
self,
|
|
192
|
+
description: str,
|
|
193
|
+
total: float = 100.0,
|
|
194
|
+
) -> Iterator[Progress | NullProgress]:
|
|
195
|
+
"""Create a progress bar context.
|
|
196
|
+
|
|
197
|
+
Returns NullProgress if progress should be hidden.
|
|
198
|
+
|
|
199
|
+
Args:
|
|
200
|
+
description: Description of the progress
|
|
201
|
+
total: Total number of steps
|
|
202
|
+
|
|
203
|
+
Yields:
|
|
204
|
+
Progress bar instance (or NullProgress if hidden)
|
|
205
|
+
"""
|
|
206
|
+
if not self.ctx.use_progress:
|
|
207
|
+
yield NullProgress()
|
|
208
|
+
return
|
|
209
|
+
|
|
210
|
+
from rich.progress import (
|
|
211
|
+
BarColumn,
|
|
212
|
+
MofNCompleteColumn,
|
|
213
|
+
Progress,
|
|
214
|
+
SpinnerColumn,
|
|
215
|
+
TaskProgressColumn,
|
|
216
|
+
TextColumn,
|
|
217
|
+
TimeElapsedColumn,
|
|
218
|
+
TimeRemainingColumn,
|
|
219
|
+
)
|
|
220
|
+
|
|
221
|
+
progress = Progress(
|
|
222
|
+
SpinnerColumn(),
|
|
223
|
+
TextColumn("[bold blue]{task.description}"),
|
|
224
|
+
BarColumn(),
|
|
225
|
+
TaskProgressColumn(),
|
|
226
|
+
MofNCompleteColumn(),
|
|
227
|
+
TimeElapsedColumn(),
|
|
228
|
+
TimeRemainingColumn(),
|
|
229
|
+
console=self._err_console,
|
|
230
|
+
transient=True,
|
|
231
|
+
)
|
|
232
|
+
|
|
233
|
+
with progress:
|
|
234
|
+
yield progress
|
|
235
|
+
|
|
236
|
+
def table(
|
|
237
|
+
self,
|
|
238
|
+
title: str | None = None,
|
|
239
|
+
columns: list[str] | None = None,
|
|
240
|
+
rows: list[list[Any]] | None = None,
|
|
241
|
+
) -> Table | None:
|
|
242
|
+
"""Create and optionally populate a Rich table for output.
|
|
243
|
+
|
|
244
|
+
Returns None if tables should not be used (json_only or not TTY).
|
|
245
|
+
|
|
246
|
+
Args:
|
|
247
|
+
title: Optional table title
|
|
248
|
+
columns: Optional list of column names
|
|
249
|
+
rows: Optional list of row data
|
|
250
|
+
|
|
251
|
+
Returns:
|
|
252
|
+
Rich Table instance or None if tables shouldn't be used
|
|
253
|
+
"""
|
|
254
|
+
if not self.ctx.use_rich_output:
|
|
255
|
+
return None
|
|
256
|
+
|
|
257
|
+
from rich.table import Table
|
|
258
|
+
|
|
259
|
+
table = Table(title=title)
|
|
260
|
+
if columns:
|
|
261
|
+
for col in columns:
|
|
262
|
+
table.add_column(col)
|
|
263
|
+
if rows:
|
|
264
|
+
for row in rows:
|
|
265
|
+
table.add_row(*[str(cell) for cell in row])
|
|
266
|
+
return table
|
|
267
|
+
|
|
268
|
+
def print(self, *args: Any, **kwargs: Any) -> None:
|
|
269
|
+
"""Print to console (respects color settings).
|
|
270
|
+
|
|
271
|
+
Args:
|
|
272
|
+
*args: Arguments to print
|
|
273
|
+
**kwargs: Keyword arguments for console.print
|
|
274
|
+
"""
|
|
275
|
+
self._console.print(*args, **kwargs)
|
|
276
|
+
|
|
277
|
+
def print_err(self, *args: Any, **kwargs: Any) -> None:
|
|
278
|
+
"""Print to stderr (respects color settings).
|
|
279
|
+
|
|
280
|
+
Args:
|
|
281
|
+
*args: Arguments to print
|
|
282
|
+
**kwargs: Keyword arguments for console.print
|
|
283
|
+
"""
|
|
284
|
+
self._err_console.print(*args, **kwargs)
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
"""Path and configuration helpers for VDS scripts."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import os
|
|
6
|
+
from pathlib import Path
|
|
7
|
+
|
|
8
|
+
APP_NAME = "vds"
|
|
9
|
+
APP_AUTHOR = "VDS"
|
|
10
|
+
_PROJECT_ROOT = Path(__file__).resolve().parents[3]
|
|
11
|
+
_SCRIPTS_DIR = _PROJECT_ROOT.parent
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
def _default_vds_home() -> Path:
|
|
15
|
+
return Path.home() / ".vds"
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
def _optional_override(env_key: str) -> Path | None:
|
|
19
|
+
raw = (os.getenv(env_key) or "").strip()
|
|
20
|
+
if not raw:
|
|
21
|
+
return None
|
|
22
|
+
return Path(raw).expanduser().resolve()
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
def get_vds_config_root() -> Path:
|
|
26
|
+
override = _optional_override("VDS_CONFIG_DIR")
|
|
27
|
+
if override is not None:
|
|
28
|
+
return override
|
|
29
|
+
return _default_vds_home()
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
def get_vds_cache_root() -> Path:
|
|
33
|
+
override = _optional_override("VDS_CACHE_DIR")
|
|
34
|
+
if override is not None:
|
|
35
|
+
return override
|
|
36
|
+
return get_vds_config_root() / "cache"
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
def get_vds_data_root() -> Path:
|
|
40
|
+
override = _optional_override("VDS_DATA_DIR")
|
|
41
|
+
if override is not None:
|
|
42
|
+
return override
|
|
43
|
+
return get_vds_config_root() / "data"
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
def get_vds_log_root() -> Path:
|
|
47
|
+
override = _optional_override("VDS_LOG_DIR")
|
|
48
|
+
if override is not None:
|
|
49
|
+
return override
|
|
50
|
+
return get_vds_config_root() / "logs"
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
def get_shared_env_path() -> Path:
|
|
54
|
+
override = _optional_override("VDS_ENV_FILE")
|
|
55
|
+
if override is not None:
|
|
56
|
+
return override
|
|
57
|
+
return get_vds_config_root() / ".env"
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
def get_scripts_dir() -> Path:
|
|
61
|
+
return _SCRIPTS_DIR
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
def get_default_env_paths() -> tuple[Path, ...]:
|
|
65
|
+
return (get_shared_env_path(),)
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
__all__ = [
|
|
69
|
+
"APP_AUTHOR",
|
|
70
|
+
"APP_NAME",
|
|
71
|
+
"get_default_env_paths",
|
|
72
|
+
"get_scripts_dir",
|
|
73
|
+
"get_shared_env_path",
|
|
74
|
+
"get_vds_cache_root",
|
|
75
|
+
"get_vds_config_root",
|
|
76
|
+
"get_vds_data_root",
|
|
77
|
+
"get_vds_log_root",
|
|
78
|
+
]
|
|
@@ -0,0 +1,213 @@
|
|
|
1
|
+
"""Test fixtures and helpers for VDS CLI testing.
|
|
2
|
+
|
|
3
|
+
This module provides pytest fixtures and helper functions for testing
|
|
4
|
+
CLI components with TTY mocking, output capture, and environment control.
|
|
5
|
+
|
|
6
|
+
Usage:
|
|
7
|
+
from vds_cli_common.testing import mock_tty, mock_pipe, create_test_context
|
|
8
|
+
|
|
9
|
+
def test_tty_detection(mock_tty):
|
|
10
|
+
ctx = CLIContext()
|
|
11
|
+
assert ctx.is_tty is True
|
|
12
|
+
|
|
13
|
+
def test_pipe_mode(mock_pipe):
|
|
14
|
+
ctx = CLIContext()
|
|
15
|
+
assert ctx.is_tty is False
|
|
16
|
+
"""
|
|
17
|
+
|
|
18
|
+
from __future__ import annotations
|
|
19
|
+
|
|
20
|
+
import os
|
|
21
|
+
import sys
|
|
22
|
+
from collections.abc import Iterator
|
|
23
|
+
from contextlib import contextmanager
|
|
24
|
+
from typing import TYPE_CHECKING
|
|
25
|
+
from unittest.mock import patch
|
|
26
|
+
|
|
27
|
+
if TYPE_CHECKING:
|
|
28
|
+
from .context import CLIContext
|
|
29
|
+
from .output import OutputManager
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
@contextmanager
|
|
33
|
+
def mock_tty() -> Iterator[None]:
|
|
34
|
+
"""Context manager to mock stdout as a TTY.
|
|
35
|
+
|
|
36
|
+
Usage:
|
|
37
|
+
with mock_tty():
|
|
38
|
+
ctx = CLIContext()
|
|
39
|
+
assert ctx.is_tty is True
|
|
40
|
+
|
|
41
|
+
Yields:
|
|
42
|
+
None
|
|
43
|
+
"""
|
|
44
|
+
with patch.object(sys.stdout, "isatty", return_value=True):
|
|
45
|
+
yield
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
@contextmanager
|
|
49
|
+
def mock_pipe() -> Iterator[None]:
|
|
50
|
+
"""Context manager to mock stdout as a pipe (non-TTY).
|
|
51
|
+
|
|
52
|
+
Usage:
|
|
53
|
+
with mock_pipe():
|
|
54
|
+
ctx = CLIContext()
|
|
55
|
+
assert ctx.is_tty is False
|
|
56
|
+
|
|
57
|
+
Yields:
|
|
58
|
+
None
|
|
59
|
+
"""
|
|
60
|
+
with patch.object(sys.stdout, "isatty", return_value=False):
|
|
61
|
+
yield
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
@contextmanager
|
|
65
|
+
def mock_no_color_env() -> Iterator[None]:
|
|
66
|
+
"""Context manager to mock NO_COLOR environment variable.
|
|
67
|
+
|
|
68
|
+
Usage:
|
|
69
|
+
with mock_no_color_env():
|
|
70
|
+
ctx = CLIContext()
|
|
71
|
+
assert ctx.use_color is False
|
|
72
|
+
|
|
73
|
+
Yields:
|
|
74
|
+
None
|
|
75
|
+
"""
|
|
76
|
+
with patch.dict(os.environ, {"NO_COLOR": "1"}):
|
|
77
|
+
yield
|
|
78
|
+
|
|
79
|
+
|
|
80
|
+
@contextmanager
|
|
81
|
+
def mock_force_color_env() -> Iterator[None]:
|
|
82
|
+
"""Context manager to mock FORCE_COLOR environment variable.
|
|
83
|
+
|
|
84
|
+
Usage:
|
|
85
|
+
with mock_force_color_env():
|
|
86
|
+
ctx = CLIContext()
|
|
87
|
+
assert ctx.use_color is True
|
|
88
|
+
|
|
89
|
+
Yields:
|
|
90
|
+
None
|
|
91
|
+
"""
|
|
92
|
+
with patch.dict(os.environ, {"FORCE_COLOR": "1"}):
|
|
93
|
+
yield
|
|
94
|
+
|
|
95
|
+
|
|
96
|
+
@contextmanager
|
|
97
|
+
def capture_output() -> Iterator[dict[str, list[str]]]:
|
|
98
|
+
"""Context manager to capture stdout and stderr for testing.
|
|
99
|
+
|
|
100
|
+
Usage:
|
|
101
|
+
with capture_output() as captured:
|
|
102
|
+
print("Hello")
|
|
103
|
+
assert "Hello" in captured["stdout"][0]
|
|
104
|
+
|
|
105
|
+
Yields:
|
|
106
|
+
Dictionary with "stdout" and "stderr" lists
|
|
107
|
+
"""
|
|
108
|
+
captured: dict[str, list[str]] = {"stdout": [], "stderr": []}
|
|
109
|
+
|
|
110
|
+
original_stdout_write = sys.stdout.write
|
|
111
|
+
original_stderr_write = sys.stderr.write
|
|
112
|
+
|
|
113
|
+
def mock_stdout_write(text: str) -> int:
|
|
114
|
+
if text.strip(): # Skip empty/whitespace-only
|
|
115
|
+
captured["stdout"].append(text)
|
|
116
|
+
return len(text)
|
|
117
|
+
|
|
118
|
+
def mock_stderr_write(text: str) -> int:
|
|
119
|
+
if text.strip(): # Skip empty/whitespace-only
|
|
120
|
+
captured["stderr"].append(text)
|
|
121
|
+
return len(text)
|
|
122
|
+
|
|
123
|
+
with patch.object(sys.stdout, "write", mock_stdout_write), patch.object(sys.stderr, "write", mock_stderr_write):
|
|
124
|
+
yield captured
|
|
125
|
+
|
|
126
|
+
|
|
127
|
+
def create_test_context(
|
|
128
|
+
*,
|
|
129
|
+
json_only: bool = False,
|
|
130
|
+
quiet: bool = False,
|
|
131
|
+
no_color: bool = False,
|
|
132
|
+
force_color: bool = False,
|
|
133
|
+
is_tty: bool = True,
|
|
134
|
+
) -> CLIContext:
|
|
135
|
+
"""Create a CLIContext for testing with specified settings.
|
|
136
|
+
|
|
137
|
+
Args:
|
|
138
|
+
json_only: Enable JSON-only mode
|
|
139
|
+
quiet: Enable quiet mode
|
|
140
|
+
no_color: Disable colors
|
|
141
|
+
force_color: Force colors
|
|
142
|
+
is_tty: Mock TTY detection result
|
|
143
|
+
|
|
144
|
+
Returns:
|
|
145
|
+
Configured CLIContext instance
|
|
146
|
+
"""
|
|
147
|
+
from .context import CLIContext
|
|
148
|
+
|
|
149
|
+
with patch.object(sys.stdout, "isatty", return_value=is_tty):
|
|
150
|
+
return CLIContext.from_options(
|
|
151
|
+
json_only=json_only,
|
|
152
|
+
quiet=quiet,
|
|
153
|
+
no_color=no_color,
|
|
154
|
+
force_color=force_color,
|
|
155
|
+
)
|
|
156
|
+
|
|
157
|
+
|
|
158
|
+
def create_test_output(ctx: CLIContext | None = None) -> OutputManager:
|
|
159
|
+
"""Create an OutputManager for testing.
|
|
160
|
+
|
|
161
|
+
Args:
|
|
162
|
+
ctx: Optional CLIContext (creates default if not provided)
|
|
163
|
+
|
|
164
|
+
Returns:
|
|
165
|
+
Configured OutputManager instance
|
|
166
|
+
"""
|
|
167
|
+
from .output import OutputManager
|
|
168
|
+
|
|
169
|
+
if ctx is None:
|
|
170
|
+
ctx = create_test_context()
|
|
171
|
+
return OutputManager(ctx)
|
|
172
|
+
|
|
173
|
+
|
|
174
|
+
# Pytest fixtures (can be used directly or as reference)
|
|
175
|
+
def pytest_fixtures() -> str:
|
|
176
|
+
"""Return pytest fixture definitions for copy-paste into conftest.py.
|
|
177
|
+
|
|
178
|
+
Returns:
|
|
179
|
+
String containing pytest fixture code
|
|
180
|
+
"""
|
|
181
|
+
return '''
|
|
182
|
+
import pytest
|
|
183
|
+
from vds_cli_common.testing import (
|
|
184
|
+
mock_tty as _mock_tty,
|
|
185
|
+
mock_pipe as _mock_pipe,
|
|
186
|
+
mock_no_color_env as _mock_no_color_env,
|
|
187
|
+
mock_force_color_env as _mock_force_color_env,
|
|
188
|
+
)
|
|
189
|
+
|
|
190
|
+
@pytest.fixture
|
|
191
|
+
def mock_tty():
|
|
192
|
+
"""Mock stdout as a TTY."""
|
|
193
|
+
with _mock_tty():
|
|
194
|
+
yield
|
|
195
|
+
|
|
196
|
+
@pytest.fixture
|
|
197
|
+
def mock_pipe():
|
|
198
|
+
"""Mock stdout as a pipe (non-TTY)."""
|
|
199
|
+
with _mock_pipe():
|
|
200
|
+
yield
|
|
201
|
+
|
|
202
|
+
@pytest.fixture
|
|
203
|
+
def mock_no_color_env():
|
|
204
|
+
"""Mock NO_COLOR environment variable."""
|
|
205
|
+
with _mock_no_color_env():
|
|
206
|
+
yield
|
|
207
|
+
|
|
208
|
+
@pytest.fixture
|
|
209
|
+
def mock_force_color_env():
|
|
210
|
+
"""Mock FORCE_COLOR environment variable."""
|
|
211
|
+
with _mock_force_color_env():
|
|
212
|
+
yield
|
|
213
|
+
'''
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
"""Version callback factory for VDS CLIs.
|
|
2
|
+
|
|
3
|
+
This module provides a factory function for creating --version flag callbacks
|
|
4
|
+
that work consistently across all VDS CLIs.
|
|
5
|
+
|
|
6
|
+
Usage:
|
|
7
|
+
from vds_cli_common import create_version_callback
|
|
8
|
+
|
|
9
|
+
version_callback = create_version_callback("vds-elastic-orchestrator")
|
|
10
|
+
|
|
11
|
+
@app.callback()
|
|
12
|
+
def main(
|
|
13
|
+
version: bool = typer.Option(
|
|
14
|
+
False,
|
|
15
|
+
"--version",
|
|
16
|
+
"-V",
|
|
17
|
+
callback=version_callback,
|
|
18
|
+
is_eager=True,
|
|
19
|
+
help="Show version and exit",
|
|
20
|
+
),
|
|
21
|
+
):
|
|
22
|
+
pass
|
|
23
|
+
"""
|
|
24
|
+
|
|
25
|
+
from __future__ import annotations
|
|
26
|
+
|
|
27
|
+
import importlib.metadata
|
|
28
|
+
from collections.abc import Callable
|
|
29
|
+
|
|
30
|
+
import typer
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
def create_version_callback(package_name: str) -> Callable[[bool], None]:
|
|
34
|
+
"""Create a version callback for a package.
|
|
35
|
+
|
|
36
|
+
The callback prints the package version and exits when --version is provided.
|
|
37
|
+
|
|
38
|
+
Args:
|
|
39
|
+
package_name: The package name to look up version for
|
|
40
|
+
|
|
41
|
+
Returns:
|
|
42
|
+
A callback function suitable for typer.Option
|
|
43
|
+
|
|
44
|
+
Example:
|
|
45
|
+
version_callback = create_version_callback("vds-elastic-orchestrator")
|
|
46
|
+
|
|
47
|
+
@app.callback()
|
|
48
|
+
def main(
|
|
49
|
+
version: bool = typer.Option(
|
|
50
|
+
False,
|
|
51
|
+
"--version",
|
|
52
|
+
"-V",
|
|
53
|
+
callback=version_callback,
|
|
54
|
+
is_eager=True,
|
|
55
|
+
help="Show version and exit",
|
|
56
|
+
),
|
|
57
|
+
):
|
|
58
|
+
pass
|
|
59
|
+
"""
|
|
60
|
+
|
|
61
|
+
def callback(value: bool) -> None:
|
|
62
|
+
if value:
|
|
63
|
+
try:
|
|
64
|
+
version = importlib.metadata.version(package_name)
|
|
65
|
+
except importlib.metadata.PackageNotFoundError:
|
|
66
|
+
version = "unknown"
|
|
67
|
+
typer.echo(f"{package_name} {version}")
|
|
68
|
+
raise typer.Exit(0)
|
|
69
|
+
|
|
70
|
+
return callback
|
|
71
|
+
|
|
72
|
+
|
|
73
|
+
def get_package_version(package_name: str) -> str:
|
|
74
|
+
"""Get the version of an installed package.
|
|
75
|
+
|
|
76
|
+
Args:
|
|
77
|
+
package_name: The package name to look up
|
|
78
|
+
|
|
79
|
+
Returns:
|
|
80
|
+
Version string or "unknown" if not found
|
|
81
|
+
"""
|
|
82
|
+
try:
|
|
83
|
+
return importlib.metadata.version(package_name)
|
|
84
|
+
except importlib.metadata.PackageNotFoundError:
|
|
85
|
+
return "unknown"
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"""Tests for vds_cli_common package."""
|