@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,255 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""
|
|
3
|
+
Move audit artifact pages under an 'Audit History' child page.
|
|
4
|
+
|
|
5
|
+
Target: Department page 88716694 (Payment department)
|
|
6
|
+
Pages to move:
|
|
7
|
+
- "Project Analysis - 88718879"
|
|
8
|
+
- "Project Analysis - 88718884"
|
|
9
|
+
- "Project Analysis - 88718886"
|
|
10
|
+
- "Project Analysis - 88718943"
|
|
11
|
+
- "Project Analysis - 88718977"
|
|
12
|
+
- "Project Audit - 88718879"
|
|
13
|
+
- "Project Audit - 88718884"
|
|
14
|
+
- "Project Audit - 88718886"
|
|
15
|
+
- "Project Audit - 88718943"
|
|
16
|
+
- "Project Audit - 88718977"
|
|
17
|
+
|
|
18
|
+
Usage:
|
|
19
|
+
uv run --project audit_orchestrator python3 scripts/move_audit_artifact_pages.py
|
|
20
|
+
|
|
21
|
+
Prerequisites:
|
|
22
|
+
- ~/.vds/.env must be populated with VDS_USERNAME, VDS_PASSWORD
|
|
23
|
+
- CONFLUENCE_INTERNAL_URL must be accessible (requires VPN/internal network)
|
|
24
|
+
"""
|
|
25
|
+
|
|
26
|
+
from __future__ import annotations
|
|
27
|
+
|
|
28
|
+
import asyncio
|
|
29
|
+
import json
|
|
30
|
+
import sys
|
|
31
|
+
from pathlib import Path
|
|
32
|
+
|
|
33
|
+
# ---------------------------------------------------------------------------
|
|
34
|
+
# Bootstrap environment
|
|
35
|
+
# ---------------------------------------------------------------------------
|
|
36
|
+
env_path = Path("/Users/vds-ai/.vds/.env")
|
|
37
|
+
if env_path.exists():
|
|
38
|
+
from dotenv import load_dotenv
|
|
39
|
+
load_dotenv(env_path)
|
|
40
|
+
|
|
41
|
+
from vds_audit_orchestrator.clients.confluence_cli_client import ConfluenceCliClient
|
|
42
|
+
|
|
43
|
+
# ---------------------------------------------------------------------------
|
|
44
|
+
# Configuration
|
|
45
|
+
# ---------------------------------------------------------------------------
|
|
46
|
+
DEPARTMENT_PAGE_ID = "88716694"
|
|
47
|
+
AUDIT_HISTORY_TITLE = "Audit History"
|
|
48
|
+
|
|
49
|
+
# Pages to move: (title, page_id_in_title) - the IDs embedded in the titles
|
|
50
|
+
ARTIFACT_PAGE_IDS = [
|
|
51
|
+
"88718879",
|
|
52
|
+
"88718884",
|
|
53
|
+
"88718886",
|
|
54
|
+
"88718943",
|
|
55
|
+
"88718977",
|
|
56
|
+
]
|
|
57
|
+
|
|
58
|
+
ARTIFACT_PAGE_TITLES = (
|
|
59
|
+
[f"Project Analysis - {pid}" for pid in ARTIFACT_PAGE_IDS]
|
|
60
|
+
+ [f"Project Audit - {pid}" for pid in ARTIFACT_PAGE_IDS]
|
|
61
|
+
)
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
async def connectivity_check(client: ConfluenceCliClient) -> bool:
|
|
65
|
+
"""Verify Confluence is reachable by fetching the department page."""
|
|
66
|
+
try:
|
|
67
|
+
page = await asyncio.wait_for(
|
|
68
|
+
client.get_page(DEPARTMENT_PAGE_ID, expand="version,space"),
|
|
69
|
+
timeout=15,
|
|
70
|
+
)
|
|
71
|
+
if page and page.get("title"):
|
|
72
|
+
print(f"[OK] Connected to Confluence. Department page: {page['title']!r}")
|
|
73
|
+
return True
|
|
74
|
+
print(f"[WARN] Page {DEPARTMENT_PAGE_ID} not found or no permission.")
|
|
75
|
+
return False
|
|
76
|
+
except asyncio.TimeoutError:
|
|
77
|
+
print("[ERROR] Confluence connection timed out. Check VPN/network access.")
|
|
78
|
+
return False
|
|
79
|
+
except Exception as exc:
|
|
80
|
+
print(f"[ERROR] Confluence connectivity check failed: {exc}")
|
|
81
|
+
return False
|
|
82
|
+
|
|
83
|
+
|
|
84
|
+
async def find_or_create_audit_history_page(
|
|
85
|
+
client: ConfluenceCliClient,
|
|
86
|
+
space_key: str,
|
|
87
|
+
) -> str:
|
|
88
|
+
"""Return the page ID of 'Audit History' under the department page, creating it if absent."""
|
|
89
|
+
children = await client.get_child_pages(DEPARTMENT_PAGE_ID)
|
|
90
|
+
for child in children:
|
|
91
|
+
if child.get("title") == AUDIT_HISTORY_TITLE:
|
|
92
|
+
page_id = child["id"]
|
|
93
|
+
print(f"[OK] Found existing 'Audit History' page: {page_id}")
|
|
94
|
+
return page_id
|
|
95
|
+
|
|
96
|
+
print(f"[INFO] 'Audit History' page not found under {DEPARTMENT_PAGE_ID}. Creating...")
|
|
97
|
+
body_file = Path("/tmp/audit_history_body.html")
|
|
98
|
+
body_file.write_text(
|
|
99
|
+
"<p>This page archives historical audit artifacts for the Payment department.</p>",
|
|
100
|
+
encoding="utf-8",
|
|
101
|
+
)
|
|
102
|
+
result = await client.create_page(
|
|
103
|
+
space_key=space_key,
|
|
104
|
+
title=AUDIT_HISTORY_TITLE,
|
|
105
|
+
body_file=body_file,
|
|
106
|
+
parent_id=DEPARTMENT_PAGE_ID,
|
|
107
|
+
)
|
|
108
|
+
new_id = result.get("id") or result.get("page_id")
|
|
109
|
+
if not new_id:
|
|
110
|
+
raise RuntimeError(f"Failed to create 'Audit History' page. Response: {result}")
|
|
111
|
+
print(f"[OK] Created 'Audit History' page: {new_id}")
|
|
112
|
+
return str(new_id)
|
|
113
|
+
|
|
114
|
+
|
|
115
|
+
async def get_pages_to_move(client: ConfluenceCliClient) -> list[dict]:
|
|
116
|
+
"""Get the current children of the department page that match the artifact pattern."""
|
|
117
|
+
children = await client.get_child_pages(DEPARTMENT_PAGE_ID)
|
|
118
|
+
pages_to_move = []
|
|
119
|
+
for child in children:
|
|
120
|
+
title = child.get("title", "")
|
|
121
|
+
if title in ARTIFACT_PAGE_TITLES:
|
|
122
|
+
pages_to_move.append(child)
|
|
123
|
+
print(f" Found artifact page: {title!r} (id={child['id']})")
|
|
124
|
+
return pages_to_move
|
|
125
|
+
|
|
126
|
+
|
|
127
|
+
async def move_pages(
|
|
128
|
+
client: ConfluenceCliClient,
|
|
129
|
+
pages: list[dict],
|
|
130
|
+
audit_history_page_id: str,
|
|
131
|
+
space_key: str,
|
|
132
|
+
) -> dict[str, list[str]]:
|
|
133
|
+
"""Move all artifact pages under the Audit History page."""
|
|
134
|
+
results: dict[str, list[str]] = {"moved": [], "failed": []}
|
|
135
|
+
|
|
136
|
+
for page in pages:
|
|
137
|
+
page_id = page["id"]
|
|
138
|
+
title = page.get("title", page_id)
|
|
139
|
+
try:
|
|
140
|
+
# Use PUT to update ancestors (move page)
|
|
141
|
+
# The move_page method uses content.move_page which moves by title
|
|
142
|
+
# We need to reparent using update_page's ancestor field
|
|
143
|
+
# Use the REST API approach: update with new ancestors
|
|
144
|
+
await reparent_page(client, page_id, audit_history_page_id, title)
|
|
145
|
+
print(f" [OK] Moved: {title!r} -> Audit History")
|
|
146
|
+
results["moved"].append(title)
|
|
147
|
+
except Exception as exc:
|
|
148
|
+
print(f" [FAIL] Could not move {title!r}: {exc}")
|
|
149
|
+
results["failed"].append(f"{title}: {exc}")
|
|
150
|
+
|
|
151
|
+
return results
|
|
152
|
+
|
|
153
|
+
|
|
154
|
+
async def reparent_page(
|
|
155
|
+
client: ConfluenceCliClient,
|
|
156
|
+
page_id: str,
|
|
157
|
+
new_parent_id: str,
|
|
158
|
+
title: str,
|
|
159
|
+
) -> None:
|
|
160
|
+
"""Move a page to a new parent by calling atlassian update_page with parent_id."""
|
|
161
|
+
# Get current page body (required to not wipe content on update)
|
|
162
|
+
page = await client.get_page(page_id, expand="version,body.storage,space")
|
|
163
|
+
if not page:
|
|
164
|
+
raise ValueError(f"Page {page_id} not found")
|
|
165
|
+
|
|
166
|
+
body_content = page.get("body", {}).get("storage", {}).get("value", "")
|
|
167
|
+
|
|
168
|
+
# Use the atlassian-python-api client directly — it supports parent_id
|
|
169
|
+
# client._http._client is the atlassian.Confluence instance
|
|
170
|
+
result = await asyncio.to_thread(
|
|
171
|
+
client._http._client.update_page,
|
|
172
|
+
page_id,
|
|
173
|
+
title,
|
|
174
|
+
body_content,
|
|
175
|
+
parent_id=new_parent_id,
|
|
176
|
+
representation="storage",
|
|
177
|
+
always_update=True,
|
|
178
|
+
version_comment="Moved to Audit History archive",
|
|
179
|
+
)
|
|
180
|
+
if not result:
|
|
181
|
+
raise RuntimeError(f"update_page returned falsy result for page {page_id}")
|
|
182
|
+
|
|
183
|
+
|
|
184
|
+
async def main() -> int:
|
|
185
|
+
print("=" * 60)
|
|
186
|
+
print("Audit Artifact Page Migration")
|
|
187
|
+
print(f"Department page: {DEPARTMENT_PAGE_ID}")
|
|
188
|
+
print(f"Target: '{AUDIT_HISTORY_TITLE}' child page")
|
|
189
|
+
print("=" * 60)
|
|
190
|
+
|
|
191
|
+
client = ConfluenceCliClient(timeout=30)
|
|
192
|
+
|
|
193
|
+
# Step 1: Connectivity check
|
|
194
|
+
print("\n[1/4] Checking Confluence connectivity...")
|
|
195
|
+
if not await connectivity_check(client):
|
|
196
|
+
print("\nABORTED: Cannot reach Confluence. Ensure you have VPN/network access.")
|
|
197
|
+
return 1
|
|
198
|
+
|
|
199
|
+
# Step 2: Get space key for creating the Audit History page
|
|
200
|
+
dept_page = await client.get_page(DEPARTMENT_PAGE_ID, expand="space")
|
|
201
|
+
space_key = dept_page.get("space", {}).get("key", "") if dept_page else ""
|
|
202
|
+
if not space_key:
|
|
203
|
+
print("[ERROR] Could not determine space key from department page.")
|
|
204
|
+
return 1
|
|
205
|
+
print(f"[OK] Space key: {space_key}")
|
|
206
|
+
|
|
207
|
+
# Step 3: Find pages to move
|
|
208
|
+
print("\n[2/4] Scanning children of department page for artifact pages...")
|
|
209
|
+
pages_to_move = await get_pages_to_move(client)
|
|
210
|
+
if not pages_to_move:
|
|
211
|
+
print("[INFO] No artifact pages found. Already moved or not present.")
|
|
212
|
+
return 0
|
|
213
|
+
print(f"Found {len(pages_to_move)} pages to move.")
|
|
214
|
+
|
|
215
|
+
# Step 4: Ensure Audit History page exists
|
|
216
|
+
print("\n[3/4] Ensuring 'Audit History' page exists...")
|
|
217
|
+
audit_history_id = await find_or_create_audit_history_page(client, space_key)
|
|
218
|
+
|
|
219
|
+
# Step 5: Move pages
|
|
220
|
+
print(f"\n[4/4] Moving {len(pages_to_move)} pages to Audit History ({audit_history_id})...")
|
|
221
|
+
results = await move_pages(client, pages_to_move, audit_history_id, space_key)
|
|
222
|
+
|
|
223
|
+
# Summary
|
|
224
|
+
print("\n" + "=" * 60)
|
|
225
|
+
print("MIGRATION SUMMARY")
|
|
226
|
+
print(f" Moved: {len(results['moved'])}")
|
|
227
|
+
print(f" Failed: {len(results['failed'])}")
|
|
228
|
+
if results["moved"]:
|
|
229
|
+
print("\nSuccessfully moved:")
|
|
230
|
+
for t in results["moved"]:
|
|
231
|
+
print(f" - {t}")
|
|
232
|
+
if results["failed"]:
|
|
233
|
+
print("\nFailed:")
|
|
234
|
+
for t in results["failed"]:
|
|
235
|
+
print(f" - {t}")
|
|
236
|
+
print("=" * 60)
|
|
237
|
+
|
|
238
|
+
# Verify final state
|
|
239
|
+
print("\n[Verify] Remaining children of department page...")
|
|
240
|
+
remaining = await client.get_child_pages(DEPARTMENT_PAGE_ID)
|
|
241
|
+
artifact_remaining = [
|
|
242
|
+
c for c in remaining if c.get("title", "") in ARTIFACT_PAGE_TITLES
|
|
243
|
+
]
|
|
244
|
+
if artifact_remaining:
|
|
245
|
+
print(f"[WARN] {len(artifact_remaining)} artifact pages still under department page:")
|
|
246
|
+
for c in artifact_remaining:
|
|
247
|
+
print(f" - {c.get('title')} (id={c['id']})")
|
|
248
|
+
else:
|
|
249
|
+
print("[OK] No artifact pages remain directly under the department page.")
|
|
250
|
+
|
|
251
|
+
return 0 if not results["failed"] else 1
|
|
252
|
+
|
|
253
|
+
|
|
254
|
+
if __name__ == "__main__":
|
|
255
|
+
sys.exit(asyncio.run(main()))
|
|
@@ -0,0 +1,165 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""
|
|
3
|
+
Move audit artifact pages under an 'Audit History' child page.
|
|
4
|
+
Uses ConfluenceCliClient (vds-scripts SDK) — no raw HTTP calls.
|
|
5
|
+
|
|
6
|
+
Usage:
|
|
7
|
+
uv run --project audit_orchestrator python3 scripts/move_audit_artifact_pages_rest.py
|
|
8
|
+
"""
|
|
9
|
+
|
|
10
|
+
from __future__ import annotations
|
|
11
|
+
|
|
12
|
+
import asyncio
|
|
13
|
+
import sys
|
|
14
|
+
import tempfile
|
|
15
|
+
from pathlib import Path
|
|
16
|
+
|
|
17
|
+
# ---------------------------------------------------------------------------
|
|
18
|
+
# Configuration
|
|
19
|
+
# ---------------------------------------------------------------------------
|
|
20
|
+
DEPARTMENT_PAGE_ID = "88716694"
|
|
21
|
+
AUDIT_HISTORY_TITLE = "Audit History"
|
|
22
|
+
|
|
23
|
+
ARTIFACT_PAGE_IDS = ["88718879", "88718884", "88718886", "88718943", "88718977"]
|
|
24
|
+
ARTIFACT_PAGE_TITLES = set(
|
|
25
|
+
[f"Project Analysis - {pid}" for pid in ARTIFACT_PAGE_IDS] + [f"Project Audit - {pid}" for pid in ARTIFACT_PAGE_IDS]
|
|
26
|
+
)
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
async def main() -> int:
|
|
30
|
+
from vds_audit_orchestrator.clients.confluence_cli_client import ConfluenceCliClient
|
|
31
|
+
|
|
32
|
+
print("=" * 60)
|
|
33
|
+
print("Audit Artifact Page Migration (ConfluenceCliClient SDK)")
|
|
34
|
+
print(f" Department page: {DEPARTMENT_PAGE_ID}")
|
|
35
|
+
print(f" Target container: '{AUDIT_HISTORY_TITLE}'")
|
|
36
|
+
print("=" * 60)
|
|
37
|
+
|
|
38
|
+
client = ConfluenceCliClient()
|
|
39
|
+
|
|
40
|
+
# 1. Connectivity check
|
|
41
|
+
print("\n[1/4] Checking Confluence connectivity...")
|
|
42
|
+
reachable, detail = await client.probe_reachable()
|
|
43
|
+
if not reachable:
|
|
44
|
+
print(f" [FAIL] Confluence not reachable: {detail}")
|
|
45
|
+
print(" Check VPN/credentials in ~/.vds/.env")
|
|
46
|
+
return 1
|
|
47
|
+
|
|
48
|
+
dept_page = await client.get_page(DEPARTMENT_PAGE_ID, expand="version,space")
|
|
49
|
+
if not dept_page or not dept_page.get("title"):
|
|
50
|
+
print(f" [FAIL] Could not fetch department page {DEPARTMENT_PAGE_ID}.")
|
|
51
|
+
return 1
|
|
52
|
+
|
|
53
|
+
space_key = dept_page.get("space", {}).get("key", "")
|
|
54
|
+
if not space_key:
|
|
55
|
+
print("[ERROR] Cannot determine space key.")
|
|
56
|
+
return 1
|
|
57
|
+
print(f" [OK] Department page: {dept_page['title']!r} (space: {space_key})")
|
|
58
|
+
|
|
59
|
+
# 2. List current children
|
|
60
|
+
print("\n[2/4] Listing children of department page...")
|
|
61
|
+
children = await client.list_child_pages(DEPARTMENT_PAGE_ID)
|
|
62
|
+
print(f" Total children found: {len(children)}")
|
|
63
|
+
|
|
64
|
+
pages_to_move = [c for c in children if c.get("title", "") in ARTIFACT_PAGE_TITLES]
|
|
65
|
+
audit_history_existing = [c for c in children if c.get("title", "") == AUDIT_HISTORY_TITLE]
|
|
66
|
+
|
|
67
|
+
print(f" Artifact pages to move: {len(pages_to_move)}")
|
|
68
|
+
for p in pages_to_move:
|
|
69
|
+
print(f" - {p.get('title')!r} (id={p['id']})")
|
|
70
|
+
|
|
71
|
+
if not pages_to_move:
|
|
72
|
+
print(" [INFO] No artifact pages found. Already moved or not present.")
|
|
73
|
+
return 0
|
|
74
|
+
|
|
75
|
+
# 3. Get or create Audit History page
|
|
76
|
+
print("\n[3/4] Getting/creating 'Audit History' page...")
|
|
77
|
+
if audit_history_existing:
|
|
78
|
+
audit_history_id = audit_history_existing[0]["id"]
|
|
79
|
+
print(f" [OK] Found existing: id={audit_history_id}")
|
|
80
|
+
else:
|
|
81
|
+
print(" Creating 'Audit History' page...")
|
|
82
|
+
body_html = (
|
|
83
|
+
"<p>This page archives historical audit artifact pages "
|
|
84
|
+
"for the <strong>3.2.4 Nền tảng thanh toán</strong> department.</p>"
|
|
85
|
+
)
|
|
86
|
+
with tempfile.NamedTemporaryFile(mode="w", suffix=".html", delete=False, encoding="utf-8") as tmp:
|
|
87
|
+
tmp.write(body_html)
|
|
88
|
+
tmp_path = Path(tmp.name)
|
|
89
|
+
|
|
90
|
+
try:
|
|
91
|
+
new_page = await client.create_page(
|
|
92
|
+
space_key=space_key,
|
|
93
|
+
title=AUDIT_HISTORY_TITLE,
|
|
94
|
+
body_file=tmp_path,
|
|
95
|
+
parent_id=DEPARTMENT_PAGE_ID,
|
|
96
|
+
)
|
|
97
|
+
finally:
|
|
98
|
+
tmp_path.unlink(missing_ok=True)
|
|
99
|
+
|
|
100
|
+
if not new_page or not new_page.get("id"):
|
|
101
|
+
print(f" [ERROR] Failed to create 'Audit History' page. Response: {new_page}")
|
|
102
|
+
return 1
|
|
103
|
+
audit_history_id = str(new_page["id"])
|
|
104
|
+
print(f" [OK] Created: id={audit_history_id}")
|
|
105
|
+
|
|
106
|
+
# 4. Move pages using move_page (by target title)
|
|
107
|
+
print(f"\n[4/4] Moving {len(pages_to_move)} pages to '{AUDIT_HISTORY_TITLE}' ({audit_history_id})...")
|
|
108
|
+
moved: list[str] = []
|
|
109
|
+
failed: list[str] = []
|
|
110
|
+
|
|
111
|
+
for page_meta in pages_to_move:
|
|
112
|
+
page_id = page_meta["id"]
|
|
113
|
+
title = page_meta.get("title", page_id)
|
|
114
|
+
|
|
115
|
+
result = await client.move_page(
|
|
116
|
+
page_id,
|
|
117
|
+
AUDIT_HISTORY_TITLE,
|
|
118
|
+
position="append",
|
|
119
|
+
)
|
|
120
|
+
|
|
121
|
+
if result or result == {}:
|
|
122
|
+
# move_page returns {} on success (no body) or a dict
|
|
123
|
+
print(f" [OK] Moved: {title!r}")
|
|
124
|
+
moved.append(title)
|
|
125
|
+
else:
|
|
126
|
+
print(f" [FAIL] Could not move: {title!r}")
|
|
127
|
+
failed.append(f"{title}: move_page returned no result")
|
|
128
|
+
|
|
129
|
+
# Summary
|
|
130
|
+
print("\n" + "=" * 60)
|
|
131
|
+
print("MIGRATION SUMMARY")
|
|
132
|
+
print(f" Moved: {len(moved)}")
|
|
133
|
+
print(f" Failed: {len(failed)}")
|
|
134
|
+
if moved:
|
|
135
|
+
print("\nSuccessfully moved:")
|
|
136
|
+
for t in moved:
|
|
137
|
+
print(f" - {t}")
|
|
138
|
+
if failed:
|
|
139
|
+
print("\nFailed:")
|
|
140
|
+
for t in failed:
|
|
141
|
+
print(f" - {t}")
|
|
142
|
+
print("=" * 60)
|
|
143
|
+
|
|
144
|
+
# Verify
|
|
145
|
+
print("\n[Verify] Remaining artifact pages under department page...")
|
|
146
|
+
remaining_children = await client.list_child_pages(DEPARTMENT_PAGE_ID)
|
|
147
|
+
still_there = [c for c in remaining_children if c.get("title", "") in ARTIFACT_PAGE_TITLES]
|
|
148
|
+
if still_there:
|
|
149
|
+
print(f"[WARN] {len(still_there)} artifact pages still directly under department:")
|
|
150
|
+
for c in still_there:
|
|
151
|
+
print(f" - {c.get('title')} (id={c['id']})")
|
|
152
|
+
else:
|
|
153
|
+
print("[OK] No artifact pages remain directly under the department page.")
|
|
154
|
+
|
|
155
|
+
print(f"\n[Verify] Children of 'Audit History' page ({audit_history_id})...")
|
|
156
|
+
ah_children = await client.list_child_pages(audit_history_id)
|
|
157
|
+
print(f" {len(ah_children)} pages now under 'Audit History':")
|
|
158
|
+
for c in ah_children:
|
|
159
|
+
print(f" - {c.get('title')} (id={c['id']})")
|
|
160
|
+
|
|
161
|
+
return 0 if not failed else 1
|
|
162
|
+
|
|
163
|
+
|
|
164
|
+
if __name__ == "__main__":
|
|
165
|
+
sys.exit(asyncio.run(main()))
|
|
@@ -0,0 +1,216 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""
|
|
3
|
+
Move 14 wrong-department pages from Payment dept (88716694) to their correct
|
|
4
|
+
department parent pages. Also renames pages with '- 88716694' suffix and
|
|
5
|
+
archives a duplicate.
|
|
6
|
+
|
|
7
|
+
Usage:
|
|
8
|
+
uv run --project audit_orchestrator python3 scripts/move_wrong_dept_pages.py
|
|
9
|
+
"""
|
|
10
|
+
|
|
11
|
+
from __future__ import annotations
|
|
12
|
+
|
|
13
|
+
import asyncio
|
|
14
|
+
import sys
|
|
15
|
+
|
|
16
|
+
# ---------------------------------------------------------------------------
|
|
17
|
+
# Move plan: (page_id, target_parent_id)
|
|
18
|
+
# ---------------------------------------------------------------------------
|
|
19
|
+
PAGES_TO_MOVE = [
|
|
20
|
+
# Admin Products (88716677)
|
|
21
|
+
("93487312", "88716677"), # Project Analysis - 88718856
|
|
22
|
+
("93487308", "88716677"), # Project Audit - 88718856
|
|
23
|
+
("93487326", "88716677"), # Project Analysis - 88718889
|
|
24
|
+
("93487285", "88716677"), # Project Audit - 88718884 - 88716694
|
|
25
|
+
("93487290", "88716677"), # Project Audit - 88718886 - 88716694
|
|
26
|
+
("93487294", "88716677"), # Project Audit - 88718879 - 88716694
|
|
27
|
+
("93487299", "88716677"), # Project Audit - 88718877 - 88716694
|
|
28
|
+
# App Platform (88716679)
|
|
29
|
+
("93487303", "88716679"), # Project Audit - 88718882 - 88716694
|
|
30
|
+
# Finance (88716692)
|
|
31
|
+
("93487316", "88716692"), # Project Analysis - 88718866
|
|
32
|
+
("93487321", "88716692"), # Project Analysis - 88718868
|
|
33
|
+
("93487314", "88716692"), # Project Audit - 88718866
|
|
34
|
+
("93487319", "88716692"), # Project Audit - 88718868
|
|
35
|
+
# Core Tech (88718815)
|
|
36
|
+
("93487328", "88718815"), # Project Analysis - 88718983
|
|
37
|
+
("93127125", "88718815"), # Project Audit - 88718940 - 88716694
|
|
38
|
+
]
|
|
39
|
+
|
|
40
|
+
# ---------------------------------------------------------------------------
|
|
41
|
+
# Rename plan: (page_id, new_title)
|
|
42
|
+
# ---------------------------------------------------------------------------
|
|
43
|
+
PAGES_TO_RENAME = [
|
|
44
|
+
("93126787", "Project Audit - 88718943"),
|
|
45
|
+
("93127090", "Project Audit - 88718975"),
|
|
46
|
+
("93126805", "Project Audit - 88718977"),
|
|
47
|
+
("93126810", "Project Audit - 88719105"),
|
|
48
|
+
("93487279", "Project Audit - 91335346"),
|
|
49
|
+
]
|
|
50
|
+
|
|
51
|
+
# ---------------------------------------------------------------------------
|
|
52
|
+
# Archive duplicate: (page_id, new_title, target_parent_id)
|
|
53
|
+
# ---------------------------------------------------------------------------
|
|
54
|
+
ARCHIVE_PAGE = ("93487283", "[ARCHIVED] Project Analysis - 91335346 (duplicate)", "88727274")
|
|
55
|
+
|
|
56
|
+
SLEEP_BETWEEN_OPS = 5 # seconds
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
async def main() -> int:
|
|
60
|
+
from vds_audit_orchestrator.clients.confluence_cli_client import ConfluenceCliClient
|
|
61
|
+
|
|
62
|
+
print("=" * 70)
|
|
63
|
+
print("Wrong-Department Page Migration & Cleanup")
|
|
64
|
+
print("=" * 70)
|
|
65
|
+
|
|
66
|
+
client = ConfluenceCliClient()
|
|
67
|
+
|
|
68
|
+
# 1. Connectivity check — use get_page on a known page instead of probe_reachable
|
|
69
|
+
# (probe_reachable calls get_all_groups which times out on this server)
|
|
70
|
+
print("\n[Step 1] Checking Confluence connectivity via get_page(88716694)...")
|
|
71
|
+
test_page = await client.get_page("88716694", expand="version,space")
|
|
72
|
+
if not test_page or not test_page.get("title"):
|
|
73
|
+
print(" [FAIL] Could not fetch Payment dept page 88716694.")
|
|
74
|
+
print(" Check VPN/LAN and credentials in ~/.vds/.env")
|
|
75
|
+
return 1
|
|
76
|
+
space_key = test_page.get("space", {}).get("key", "")
|
|
77
|
+
print(f" [OK] Reachable — page: {test_page['title']!r} (space: {space_key})")
|
|
78
|
+
|
|
79
|
+
# 2. Resolve target parent titles (needed by move_page which takes a title)
|
|
80
|
+
print("\n[Step 2] Resolving target parent page titles...")
|
|
81
|
+
unique_parent_ids = list({parent_id for _, parent_id in PAGES_TO_MOVE} | {ARCHIVE_PAGE[2]})
|
|
82
|
+
parent_titles: dict[str, str] = {}
|
|
83
|
+
|
|
84
|
+
for pid in unique_parent_ids:
|
|
85
|
+
page = await client.get_page(pid, expand="version")
|
|
86
|
+
if page and page.get("title"):
|
|
87
|
+
parent_titles[pid] = page["title"]
|
|
88
|
+
print(f" [OK] {pid} -> {page['title']!r}")
|
|
89
|
+
else:
|
|
90
|
+
print(f" [FAIL] Could not fetch parent page {pid}")
|
|
91
|
+
return 1
|
|
92
|
+
|
|
93
|
+
# 3. Move pages
|
|
94
|
+
print(f"\n[Step 3] Moving {len(PAGES_TO_MOVE)} pages to correct departments...")
|
|
95
|
+
move_success = 0
|
|
96
|
+
move_fail = 0
|
|
97
|
+
|
|
98
|
+
for page_id, target_parent_id in PAGES_TO_MOVE:
|
|
99
|
+
target_title = parent_titles[target_parent_id]
|
|
100
|
+
|
|
101
|
+
# Get current page info for logging
|
|
102
|
+
page_info = await client.get_page(page_id, expand="version")
|
|
103
|
+
current_title = page_info.get("title", page_id) if page_info else page_id
|
|
104
|
+
|
|
105
|
+
print(f"\n Moving: {current_title!r}")
|
|
106
|
+
print(f" page_id={page_id} -> parent={target_parent_id} ({target_title!r})")
|
|
107
|
+
|
|
108
|
+
try:
|
|
109
|
+
result = await client.move_page(page_id, target_title, position="append")
|
|
110
|
+
# move_page returns {} on success (empty body from Confluence REST)
|
|
111
|
+
print(f" [OK] Moved successfully (result={result!r})")
|
|
112
|
+
move_success += 1
|
|
113
|
+
except Exception as exc:
|
|
114
|
+
print(f" [FAIL] Exception: {exc}")
|
|
115
|
+
# Retry once after 30s
|
|
116
|
+
print(" Waiting 30s and retrying once...")
|
|
117
|
+
await asyncio.sleep(30)
|
|
118
|
+
try:
|
|
119
|
+
result = await client.move_page(page_id, target_title, position="append")
|
|
120
|
+
print(f" [OK] Moved on retry (result={result!r})")
|
|
121
|
+
move_success += 1
|
|
122
|
+
except Exception as exc2:
|
|
123
|
+
print(f" [FAIL] Retry also failed: {exc2}")
|
|
124
|
+
move_fail += 1
|
|
125
|
+
continue
|
|
126
|
+
|
|
127
|
+
await asyncio.sleep(SLEEP_BETWEEN_OPS)
|
|
128
|
+
|
|
129
|
+
# 4. Rename pages (remove '- 88716694' suffix)
|
|
130
|
+
print(f"\n[Step 4] Renaming {len(PAGES_TO_RENAME)} pages (remove '- 88716694' suffix)...")
|
|
131
|
+
rename_success = 0
|
|
132
|
+
rename_fail = 0
|
|
133
|
+
|
|
134
|
+
for page_id, new_title in PAGES_TO_RENAME:
|
|
135
|
+
page_info = await client.get_page(page_id, expand="version")
|
|
136
|
+
current_title = page_info.get("title", page_id) if page_info else page_id
|
|
137
|
+
|
|
138
|
+
print(f"\n Renaming: {current_title!r}")
|
|
139
|
+
print(f" page_id={page_id} -> new title: {new_title!r}")
|
|
140
|
+
|
|
141
|
+
try:
|
|
142
|
+
result = await client.rename_page(page_id, new_title)
|
|
143
|
+
print(f" [OK] Renamed successfully (result keys={list(result.keys()) if result else result!r})")
|
|
144
|
+
rename_success += 1
|
|
145
|
+
except Exception as exc:
|
|
146
|
+
print(f" [FAIL] Exception: {exc}")
|
|
147
|
+
print(" Waiting 30s and retrying once...")
|
|
148
|
+
await asyncio.sleep(30)
|
|
149
|
+
try:
|
|
150
|
+
result = await client.rename_page(page_id, new_title)
|
|
151
|
+
print(" [OK] Renamed on retry")
|
|
152
|
+
rename_success += 1
|
|
153
|
+
except Exception as exc2:
|
|
154
|
+
print(f" [FAIL] Retry also failed: {exc2}")
|
|
155
|
+
rename_fail += 1
|
|
156
|
+
continue
|
|
157
|
+
|
|
158
|
+
await asyncio.sleep(SLEEP_BETWEEN_OPS)
|
|
159
|
+
|
|
160
|
+
# 5. Archive duplicate
|
|
161
|
+
print(f"\n[Step 5] Archiving duplicate page {ARCHIVE_PAGE[0]}...")
|
|
162
|
+
archive_id, archive_new_title, archive_parent_id = ARCHIVE_PAGE
|
|
163
|
+
archive_parent_title = parent_titles[archive_parent_id]
|
|
164
|
+
|
|
165
|
+
page_info = await client.get_page(archive_id, expand="version")
|
|
166
|
+
current_title = page_info.get("title", archive_id) if page_info else archive_id
|
|
167
|
+
print(f" Current title: {current_title!r}")
|
|
168
|
+
print(f" New title: {archive_new_title!r}")
|
|
169
|
+
print(f" Target parent: {archive_parent_id} ({archive_parent_title!r})")
|
|
170
|
+
|
|
171
|
+
archive_success = True
|
|
172
|
+
|
|
173
|
+
# Step 5a: Rename first
|
|
174
|
+
try:
|
|
175
|
+
result = await client.rename_page(archive_id, archive_new_title)
|
|
176
|
+
print(" [OK] Renamed archive page")
|
|
177
|
+
except Exception as exc:
|
|
178
|
+
print(f" [FAIL] Could not rename archive page: {exc}")
|
|
179
|
+
archive_success = False
|
|
180
|
+
|
|
181
|
+
if archive_success:
|
|
182
|
+
await asyncio.sleep(SLEEP_BETWEEN_OPS)
|
|
183
|
+
# Step 5b: Move to archive parent
|
|
184
|
+
try:
|
|
185
|
+
result = await client.move_page(archive_id, archive_parent_title, position="append")
|
|
186
|
+
print(f" [OK] Moved archive page to {archive_parent_title!r}")
|
|
187
|
+
except Exception as exc:
|
|
188
|
+
print(f" [FAIL] Could not move archive page: {exc}")
|
|
189
|
+
print(" Waiting 30s and retrying once...")
|
|
190
|
+
await asyncio.sleep(30)
|
|
191
|
+
try:
|
|
192
|
+
result = await client.move_page(archive_id, archive_parent_title, position="append")
|
|
193
|
+
print(" [OK] Moved archive page on retry")
|
|
194
|
+
except Exception as exc2:
|
|
195
|
+
print(f" [FAIL] Retry also failed: {exc2}")
|
|
196
|
+
archive_success = False
|
|
197
|
+
|
|
198
|
+
# 6. Summary
|
|
199
|
+
print("\n" + "=" * 70)
|
|
200
|
+
print("SUMMARY")
|
|
201
|
+
print("=" * 70)
|
|
202
|
+
print(f" Moves: {move_success} succeeded, {move_fail} failed (of {len(PAGES_TO_MOVE)})")
|
|
203
|
+
print(f" Renames: {rename_success} succeeded, {rename_fail} failed (of {len(PAGES_TO_RENAME)})")
|
|
204
|
+
print(f" Archive: {'OK' if archive_success else 'FAILED'}")
|
|
205
|
+
|
|
206
|
+
total_failures = move_fail + rename_fail + (0 if archive_success else 1)
|
|
207
|
+
if total_failures > 0:
|
|
208
|
+
print(f"\n[WARN] {total_failures} operation(s) failed. Review output above.")
|
|
209
|
+
else:
|
|
210
|
+
print("\n[OK] All operations completed successfully.")
|
|
211
|
+
|
|
212
|
+
return 0 if total_failures == 0 else 1
|
|
213
|
+
|
|
214
|
+
|
|
215
|
+
if __name__ == "__main__":
|
|
216
|
+
sys.exit(asyncio.run(main()))
|