sdr-visualizer 1.2.6__tar.gz → 1.2.7__tar.gz
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.
- {sdr_visualizer-1.2.6 → sdr_visualizer-1.2.7}/AGENTS.md +2 -1
- {sdr_visualizer-1.2.6 → sdr_visualizer-1.2.7}/CHANGELOG.md +36 -1
- {sdr_visualizer-1.2.6 → sdr_visualizer-1.2.7}/PKG-INFO +19 -14
- {sdr_visualizer-1.2.6 → sdr_visualizer-1.2.7}/README.md +8 -12
- {sdr_visualizer-1.2.6 → sdr_visualizer-1.2.7}/docs/ADAPTER_GUIDE.md +4 -0
- {sdr_visualizer-1.2.6 → sdr_visualizer-1.2.7}/docs/DEPENDABOT_AUTOMATION.md +1 -1
- {sdr_visualizer-1.2.6 → sdr_visualizer-1.2.7}/docs/LINEAGE.md +7 -10
- {sdr_visualizer-1.2.6 → sdr_visualizer-1.2.7}/docs/PRODUCT_CONTRACT.md +8 -2
- {sdr_visualizer-1.2.6 → sdr_visualizer-1.2.7}/docs/RELEASE_EVIDENCE_TEMPLATE.md +1 -0
- {sdr_visualizer-1.2.6 → sdr_visualizer-1.2.7}/docs/RELEASING.md +10 -2
- {sdr_visualizer-1.2.6 → sdr_visualizer-1.2.7}/docs/WORKSPACE_USAGE.md +1 -1
- {sdr_visualizer-1.2.6 → sdr_visualizer-1.2.7}/pyproject.toml +14 -2
- {sdr_visualizer-1.2.6 → sdr_visualizer-1.2.7}/scripts/check_workflow_policy.py +18 -1
- {sdr_visualizer-1.2.6 → sdr_visualizer-1.2.7}/scripts/dependabot_review.py +1 -0
- {sdr_visualizer-1.2.6 → sdr_visualizer-1.2.7}/src/sdr_visualizer/__init__.py +1 -1
- {sdr_visualizer-1.2.6 → sdr_visualizer-1.2.7}/src/sdr_visualizer/adapters/aa.py +1 -1
- {sdr_visualizer-1.2.6 → sdr_visualizer-1.2.7}/src/sdr_visualizer/adapters/cja.py +1 -1
- {sdr_visualizer-1.2.6 → sdr_visualizer-1.2.7}/src/sdr_visualizer/cli/lineage.py +6 -2
- sdr_visualizer-1.2.7/src/sdr_visualizer/input/json_io.py +19 -0
- {sdr_visualizer-1.2.6 → sdr_visualizer-1.2.7}/src/sdr_visualizer/input/lineage_discovery.py +36 -5
- {sdr_visualizer-1.2.6 → sdr_visualizer-1.2.7}/src/sdr_visualizer/input/loader.py +7 -2
- {sdr_visualizer-1.2.6 → sdr_visualizer-1.2.7}/src/sdr_visualizer/input/shell_out.py +4 -1
- {sdr_visualizer-1.2.6 → sdr_visualizer-1.2.7}/src/sdr_visualizer/usage/normalize.py +1 -1
- {sdr_visualizer-1.2.6 → sdr_visualizer-1.2.7}/tests/test_adapters_cja.py +1 -1
- {sdr_visualizer-1.2.6 → sdr_visualizer-1.2.7}/tests/test_cli.py +4 -4
- {sdr_visualizer-1.2.6 → sdr_visualizer-1.2.7}/tests/test_input_modes.py +71 -3
- {sdr_visualizer-1.2.6 → sdr_visualizer-1.2.7}/tests/test_input_series.py +13 -0
- {sdr_visualizer-1.2.6 → sdr_visualizer-1.2.7}/tests/test_lineage_cli.py +16 -2
- {sdr_visualizer-1.2.6 → sdr_visualizer-1.2.7}/tests/test_lineage_discovery.py +66 -12
- {sdr_visualizer-1.2.6 → sdr_visualizer-1.2.7}/tests/test_workflow_policy.py +42 -0
- {sdr_visualizer-1.2.6 → sdr_visualizer-1.2.7}/tests/test_workspace_usage_aa_sdk.py +1 -1
- {sdr_visualizer-1.2.6 → sdr_visualizer-1.2.7}/.gitignore +0 -0
- {sdr_visualizer-1.2.6 → sdr_visualizer-1.2.7}/CODE_OF_CONDUCT.md +0 -0
- {sdr_visualizer-1.2.6 → sdr_visualizer-1.2.7}/CONTRIBUTING.md +0 -0
- {sdr_visualizer-1.2.6 → sdr_visualizer-1.2.7}/LICENSE +0 -0
- {sdr_visualizer-1.2.6 → sdr_visualizer-1.2.7}/SECURITY.md +0 -0
- {sdr_visualizer-1.2.6 → sdr_visualizer-1.2.7}/THIRD_PARTY_LICENSES +0 -0
- {sdr_visualizer-1.2.6 → sdr_visualizer-1.2.7}/docs/ADAPTER_CORRECTNESS_1.1.2.md +0 -0
- {sdr_visualizer-1.2.6 → sdr_visualizer-1.2.7}/docs/ARCHITECTURE.md +0 -0
- {sdr_visualizer-1.2.6 → sdr_visualizer-1.2.7}/docs/EMBEDDED_DATA_FORMAT.md +0 -0
- {sdr_visualizer-1.2.6 → sdr_visualizer-1.2.7}/docs/PERFORMANCE.md +0 -0
- {sdr_visualizer-1.2.6 → sdr_visualizer-1.2.7}/docs/SHOWCASE.md +0 -0
- {sdr_visualizer-1.2.6 → sdr_visualizer-1.2.7}/docs/payload-schema.json +0 -0
- {sdr_visualizer-1.2.6 → sdr_visualizer-1.2.7}/docs/workspace-usage-schema.json +0 -0
- {sdr_visualizer-1.2.6 → sdr_visualizer-1.2.7}/scripts/capture_screenshot.py +0 -0
- {sdr_visualizer-1.2.6 → sdr_visualizer-1.2.7}/scripts/check_color_pack_parity.py +0 -0
- {sdr_visualizer-1.2.6 → sdr_visualizer-1.2.7}/scripts/check_markdown_links.py +0 -0
- {sdr_visualizer-1.2.6 → sdr_visualizer-1.2.7}/scripts/corpus_check.py +0 -0
- {sdr_visualizer-1.2.6 → sdr_visualizer-1.2.7}/scripts/generate_aa_large_fixture.py +0 -0
- {sdr_visualizer-1.2.6 → sdr_visualizer-1.2.7}/scripts/generate_cja_lineage_poc.py +0 -0
- {sdr_visualizer-1.2.6 → sdr_visualizer-1.2.7}/scripts/generate_examples.py +0 -0
- {sdr_visualizer-1.2.6 → sdr_visualizer-1.2.7}/scripts/generate_large_fixture.py +0 -0
- {sdr_visualizer-1.2.6 → sdr_visualizer-1.2.7}/scripts/mutate_fixture.py +0 -0
- {sdr_visualizer-1.2.6 → sdr_visualizer-1.2.7}/scripts/package_smoke_check.py +0 -0
- {sdr_visualizer-1.2.6 → sdr_visualizer-1.2.7}/scripts/perf_browser_check.py +0 -0
- {sdr_visualizer-1.2.6 → sdr_visualizer-1.2.7}/scripts/perf_check.py +0 -0
- {sdr_visualizer-1.2.6 → sdr_visualizer-1.2.7}/scripts/perf_lineage_poc.py +0 -0
- {sdr_visualizer-1.2.6 → sdr_visualizer-1.2.7}/scripts/workspace_usage_fixture.py +0 -0
- {sdr_visualizer-1.2.6 → sdr_visualizer-1.2.7}/src/sdr_visualizer/__main__.py +0 -0
- {sdr_visualizer-1.2.6 → sdr_visualizer-1.2.7}/src/sdr_visualizer/adapters/__init__.py +0 -0
- {sdr_visualizer-1.2.6 → sdr_visualizer-1.2.7}/src/sdr_visualizer/adapters/base.py +0 -0
- {sdr_visualizer-1.2.6 → sdr_visualizer-1.2.7}/src/sdr_visualizer/adapters/cja_lineage.py +0 -0
- {sdr_visualizer-1.2.6 → sdr_visualizer-1.2.7}/src/sdr_visualizer/analysis/__init__.py +0 -0
- {sdr_visualizer-1.2.6 → sdr_visualizer-1.2.7}/src/sdr_visualizer/analysis/diff.py +0 -0
- {sdr_visualizer-1.2.6 → sdr_visualizer-1.2.7}/src/sdr_visualizer/analysis/formula_tree.py +0 -0
- {sdr_visualizer-1.2.6 → sdr_visualizer-1.2.7}/src/sdr_visualizer/analysis/lineage_layout.py +0 -0
- {sdr_visualizer-1.2.6 → sdr_visualizer-1.2.7}/src/sdr_visualizer/analysis/references.py +0 -0
- {sdr_visualizer-1.2.6 → sdr_visualizer-1.2.7}/src/sdr_visualizer/analysis/segment_tree.py +0 -0
- {sdr_visualizer-1.2.6 → sdr_visualizer-1.2.7}/src/sdr_visualizer/analysis/trend.py +0 -0
- {sdr_visualizer-1.2.6 → sdr_visualizer-1.2.7}/src/sdr_visualizer/cli/__init__.py +0 -0
- {sdr_visualizer-1.2.6 → sdr_visualizer-1.2.7}/src/sdr_visualizer/cli/exit_codes.py +0 -0
- {sdr_visualizer-1.2.6 → sdr_visualizer-1.2.7}/src/sdr_visualizer/cli/lineage_output.py +0 -0
- {sdr_visualizer-1.2.6 → sdr_visualizer-1.2.7}/src/sdr_visualizer/cli/main.py +0 -0
- {sdr_visualizer-1.2.6 → sdr_visualizer-1.2.7}/src/sdr_visualizer/cli/output_safety.py +0 -0
- {sdr_visualizer-1.2.6 → sdr_visualizer-1.2.7}/src/sdr_visualizer/cli/workspace_usage.py +0 -0
- {sdr_visualizer-1.2.6 → sdr_visualizer-1.2.7}/src/sdr_visualizer/core/__init__.py +0 -0
- {sdr_visualizer-1.2.6 → sdr_visualizer-1.2.7}/src/sdr_visualizer/core/exceptions.py +0 -0
- {sdr_visualizer-1.2.6 → sdr_visualizer-1.2.7}/src/sdr_visualizer/core/lineage.py +0 -0
- {sdr_visualizer-1.2.6 → sdr_visualizer-1.2.7}/src/sdr_visualizer/core/models.py +0 -0
- {sdr_visualizer-1.2.6 → sdr_visualizer-1.2.7}/src/sdr_visualizer/core/structure_limits.py +0 -0
- {sdr_visualizer-1.2.6 → sdr_visualizer-1.2.7}/src/sdr_visualizer/core/visualizer.py +0 -0
- {sdr_visualizer-1.2.6 → sdr_visualizer-1.2.7}/src/sdr_visualizer/core/workspace_usage.py +0 -0
- {sdr_visualizer-1.2.6 → sdr_visualizer-1.2.7}/src/sdr_visualizer/input/__init__.py +0 -0
- {sdr_visualizer-1.2.6 → sdr_visualizer-1.2.7}/src/sdr_visualizer/input/detect.py +0 -0
- {sdr_visualizer-1.2.6 → sdr_visualizer-1.2.7}/src/sdr_visualizer/input/series.py +0 -0
- {sdr_visualizer-1.2.6 → sdr_visualizer-1.2.7}/src/sdr_visualizer/input/workspace_usage.py +0 -0
- {sdr_visualizer-1.2.6 → sdr_visualizer-1.2.7}/src/sdr_visualizer/render/__init__.py +0 -0
- {sdr_visualizer-1.2.6 → sdr_visualizer-1.2.7}/src/sdr_visualizer/render/color_packs.py +0 -0
- {sdr_visualizer-1.2.6 → sdr_visualizer-1.2.7}/src/sdr_visualizer/render/data_payload.py +0 -0
- {sdr_visualizer-1.2.6 → sdr_visualizer-1.2.7}/src/sdr_visualizer/render/lineage_payload.py +0 -0
- {sdr_visualizer-1.2.6 → sdr_visualizer-1.2.7}/src/sdr_visualizer/render/lineage_renderer.py +0 -0
- {sdr_visualizer-1.2.6 → sdr_visualizer-1.2.7}/src/sdr_visualizer/render/renderer.py +0 -0
- {sdr_visualizer-1.2.6 → sdr_visualizer-1.2.7}/src/sdr_visualizer/render/static/cja_lineage.css +0 -0
- {sdr_visualizer-1.2.6 → sdr_visualizer-1.2.7}/src/sdr_visualizer/render/static/cja_lineage.js +0 -0
- {sdr_visualizer-1.2.6 → sdr_visualizer-1.2.7}/src/sdr_visualizer/render/static/cja_lineage_graph.js +0 -0
- {sdr_visualizer-1.2.6 → sdr_visualizer-1.2.7}/src/sdr_visualizer/render/static/cja_lineage_roles.js +0 -0
- {sdr_visualizer-1.2.6 → sdr_visualizer-1.2.7}/src/sdr_visualizer/render/static/d3.min.js +0 -0
- {sdr_visualizer-1.2.6 → sdr_visualizer-1.2.7}/src/sdr_visualizer/render/static/visualizer.css +0 -0
- {sdr_visualizer-1.2.6 → sdr_visualizer-1.2.7}/src/sdr_visualizer/render/static/visualizer.js +0 -0
- {sdr_visualizer-1.2.6 → sdr_visualizer-1.2.7}/src/sdr_visualizer/render/static/visualizer_trend.js +0 -0
- {sdr_visualizer-1.2.6 → sdr_visualizer-1.2.7}/src/sdr_visualizer/render/templates/catalog.html.j2 +0 -0
- {sdr_visualizer-1.2.6 → sdr_visualizer-1.2.7}/src/sdr_visualizer/render/templates/changes.html.j2 +0 -0
- {sdr_visualizer-1.2.6 → sdr_visualizer-1.2.7}/src/sdr_visualizer/render/templates/cja_lineage.html.j2 +0 -0
- {sdr_visualizer-1.2.6 → sdr_visualizer-1.2.7}/src/sdr_visualizer/render/templates/graph.html.j2 +0 -0
- {sdr_visualizer-1.2.6 → sdr_visualizer-1.2.7}/src/sdr_visualizer/render/templates/index.html.j2 +0 -0
- {sdr_visualizer-1.2.6 → sdr_visualizer-1.2.7}/src/sdr_visualizer/render/templates/trend.html.j2 +0 -0
- {sdr_visualizer-1.2.6 → sdr_visualizer-1.2.7}/src/sdr_visualizer/render/trend_charts.py +0 -0
- {sdr_visualizer-1.2.6 → sdr_visualizer-1.2.7}/src/sdr_visualizer/render/workspace_usage.py +0 -0
- {sdr_visualizer-1.2.6 → sdr_visualizer-1.2.7}/src/sdr_visualizer/usage/__init__.py +0 -0
- {sdr_visualizer-1.2.6 → sdr_visualizer-1.2.7}/src/sdr_visualizer/usage/aa_api.py +0 -0
- {sdr_visualizer-1.2.6 → sdr_visualizer-1.2.7}/src/sdr_visualizer/usage/aa_sdk.py +0 -0
- {sdr_visualizer-1.2.6 → sdr_visualizer-1.2.7}/src/sdr_visualizer/usage/auth.py +0 -0
- {sdr_visualizer-1.2.6 → sdr_visualizer-1.2.7}/src/sdr_visualizer/usage/cja_api.py +0 -0
- {sdr_visualizer-1.2.6 → sdr_visualizer-1.2.7}/src/sdr_visualizer/usage/cja_sdk.py +0 -0
- {sdr_visualizer-1.2.6 → sdr_visualizer-1.2.7}/src/sdr_visualizer/usage/collector.py +0 -0
- {sdr_visualizer-1.2.6 → sdr_visualizer-1.2.7}/src/sdr_visualizer/usage/sdk_worker.py +0 -0
- {sdr_visualizer-1.2.6 → sdr_visualizer-1.2.7}/src/sdr_visualizer/usage/transport.py +0 -0
- {sdr_visualizer-1.2.6 → sdr_visualizer-1.2.7}/tests/__init__.py +0 -0
- {sdr_visualizer-1.2.6 → sdr_visualizer-1.2.7}/tests/adapter_cases.py +0 -0
- {sdr_visualizer-1.2.6 → sdr_visualizer-1.2.7}/tests/browser_support.py +0 -0
- {sdr_visualizer-1.2.6 → sdr_visualizer-1.2.7}/tests/conftest.py +0 -0
- {sdr_visualizer-1.2.6 → sdr_visualizer-1.2.7}/tests/fixtures/__init__.py +0 -0
- {sdr_visualizer-1.2.6 → sdr_visualizer-1.2.7}/tests/fixtures/aa_snapshot_clean.json +0 -0
- {sdr_visualizer-1.2.6 → sdr_visualizer-1.2.7}/tests/fixtures/aa_snapshot_messy.json +0 -0
- {sdr_visualizer-1.2.6 → sdr_visualizer-1.2.7}/tests/fixtures/cja_lineage_degraded.json +0 -0
- {sdr_visualizer-1.2.6 → sdr_visualizer-1.2.7}/tests/fixtures/cja_lineage_enriched.json +0 -0
- {sdr_visualizer-1.2.6 → sdr_visualizer-1.2.7}/tests/fixtures/cja_lineage_hostile.json +0 -0
- {sdr_visualizer-1.2.6 → sdr_visualizer-1.2.7}/tests/fixtures/cja_lineage_shared.json +0 -0
- {sdr_visualizer-1.2.6 → sdr_visualizer-1.2.7}/tests/fixtures/cja_snapshot_clean.json +0 -0
- {sdr_visualizer-1.2.6 → sdr_visualizer-1.2.7}/tests/fixtures/cja_snapshot_hostile.json +0 -0
- {sdr_visualizer-1.2.6 → sdr_visualizer-1.2.7}/tests/fixtures/cja_snapshot_messy.json +0 -0
- {sdr_visualizer-1.2.6 → sdr_visualizer-1.2.7}/tests/fixtures/cja_snapshot_minimal.json +0 -0
- {sdr_visualizer-1.2.6 → sdr_visualizer-1.2.7}/tests/fixtures/workspace_usage_aa_all_types.json +0 -0
- {sdr_visualizer-1.2.6 → sdr_visualizer-1.2.7}/tests/fixtures/workspace_usage_cja_all_types.json +0 -0
- {sdr_visualizer-1.2.6 → sdr_visualizer-1.2.7}/tests/test_adapter_correctness.py +0 -0
- {sdr_visualizer-1.2.6 → sdr_visualizer-1.2.7}/tests/test_adapter_fuzz.py +0 -0
- {sdr_visualizer-1.2.6 → sdr_visualizer-1.2.7}/tests/test_adapters_aa.py +0 -0
- {sdr_visualizer-1.2.6 → sdr_visualizer-1.2.7}/tests/test_analysis_diff.py +0 -0
- {sdr_visualizer-1.2.6 → sdr_visualizer-1.2.7}/tests/test_analysis_formula_tree.py +0 -0
- {sdr_visualizer-1.2.6 → sdr_visualizer-1.2.7}/tests/test_analysis_references.py +0 -0
- {sdr_visualizer-1.2.6 → sdr_visualizer-1.2.7}/tests/test_analysis_segment_tree.py +0 -0
- {sdr_visualizer-1.2.6 → sdr_visualizer-1.2.7}/tests/test_analysis_trend.py +0 -0
- {sdr_visualizer-1.2.6 → sdr_visualizer-1.2.7}/tests/test_browser_functional.py +0 -0
- {sdr_visualizer-1.2.6 → sdr_visualizer-1.2.7}/tests/test_browser_support.py +0 -0
- {sdr_visualizer-1.2.6 → sdr_visualizer-1.2.7}/tests/test_cja_lineage.py +0 -0
- {sdr_visualizer-1.2.6 → sdr_visualizer-1.2.7}/tests/test_cja_lineage_driver.py +0 -0
- {sdr_visualizer-1.2.6 → sdr_visualizer-1.2.7}/tests/test_cli_flags.py +0 -0
- {sdr_visualizer-1.2.6 → sdr_visualizer-1.2.7}/tests/test_color_pack_parity.py +0 -0
- {sdr_visualizer-1.2.6 → sdr_visualizer-1.2.7}/tests/test_color_packs.py +0 -0
- {sdr_visualizer-1.2.6 → sdr_visualizer-1.2.7}/tests/test_corpus_check.py +0 -0
- {sdr_visualizer-1.2.6 → sdr_visualizer-1.2.7}/tests/test_data_payload.py +0 -0
- {sdr_visualizer-1.2.6 → sdr_visualizer-1.2.7}/tests/test_dependabot_review.py +0 -0
- {sdr_visualizer-1.2.6 → sdr_visualizer-1.2.7}/tests/test_entrypoint.py +0 -0
- {sdr_visualizer-1.2.6 → sdr_visualizer-1.2.7}/tests/test_generate_examples.py +0 -0
- {sdr_visualizer-1.2.6 → sdr_visualizer-1.2.7}/tests/test_lineage_browser.py +0 -0
- {sdr_visualizer-1.2.6 → sdr_visualizer-1.2.7}/tests/test_lineage_performance.py +0 -0
- {sdr_visualizer-1.2.6 → sdr_visualizer-1.2.7}/tests/test_lineage_renderer.py +0 -0
- {sdr_visualizer-1.2.6 → sdr_visualizer-1.2.7}/tests/test_markdown_links.py +0 -0
- {sdr_visualizer-1.2.6 → sdr_visualizer-1.2.7}/tests/test_minimal_fixture.py +0 -0
- {sdr_visualizer-1.2.6 → sdr_visualizer-1.2.7}/tests/test_mutate_fixture.py +0 -0
- {sdr_visualizer-1.2.6 → sdr_visualizer-1.2.7}/tests/test_output_safety.py +0 -0
- {sdr_visualizer-1.2.6 → sdr_visualizer-1.2.7}/tests/test_package_smoke_check.py +0 -0
- {sdr_visualizer-1.2.6 → sdr_visualizer-1.2.7}/tests/test_payload_schema.py +0 -0
- {sdr_visualizer-1.2.6 → sdr_visualizer-1.2.7}/tests/test_perf.py +0 -0
- {sdr_visualizer-1.2.6 → sdr_visualizer-1.2.7}/tests/test_perf_browser_check.py +0 -0
- {sdr_visualizer-1.2.6 → sdr_visualizer-1.2.7}/tests/test_release_documentation.py +0 -0
- {sdr_visualizer-1.2.6 → sdr_visualizer-1.2.7}/tests/test_renderer.py +0 -0
- {sdr_visualizer-1.2.6 → sdr_visualizer-1.2.7}/tests/test_renderer_anatomy.py +0 -0
- {sdr_visualizer-1.2.6 → sdr_visualizer-1.2.7}/tests/test_renderer_graph.py +0 -0
- {sdr_visualizer-1.2.6 → sdr_visualizer-1.2.7}/tests/test_semantic_properties.py +0 -0
- {sdr_visualizer-1.2.6 → sdr_visualizer-1.2.7}/tests/test_structure_limits.py +0 -0
- {sdr_visualizer-1.2.6 → sdr_visualizer-1.2.7}/tests/test_trend_charts.py +0 -0
- {sdr_visualizer-1.2.6 → sdr_visualizer-1.2.7}/tests/test_workspace_usage.py +0 -0
- {sdr_visualizer-1.2.6 → sdr_visualizer-1.2.7}/tests/test_workspace_usage_auth.py +0 -0
- {sdr_visualizer-1.2.6 → sdr_visualizer-1.2.7}/tests/test_workspace_usage_cja_sdk.py +0 -0
- {sdr_visualizer-1.2.6 → sdr_visualizer-1.2.7}/tests/test_workspace_usage_cli.py +0 -0
- {sdr_visualizer-1.2.6 → sdr_visualizer-1.2.7}/tests/test_workspace_usage_collection.py +0 -0
- {sdr_visualizer-1.2.6 → sdr_visualizer-1.2.7}/tests/test_workspace_usage_offline.py +0 -0
- {sdr_visualizer-1.2.6 → sdr_visualizer-1.2.7}/tests/test_workspace_usage_payload.py +0 -0
- {sdr_visualizer-1.2.6 → sdr_visualizer-1.2.7}/tests/test_workspace_usage_perf.py +0 -0
- {sdr_visualizer-1.2.6 → sdr_visualizer-1.2.7}/tests/test_workspace_usage_sdk.py +0 -0
- {sdr_visualizer-1.2.6 → sdr_visualizer-1.2.7}/tests/test_workspace_usage_transport.py +0 -0
|
@@ -47,7 +47,8 @@ only requires Jinja2. The `dev` group supplies Python test/lint tooling and
|
|
|
47
47
|
`browser` supplies Playwright. On Linux, use
|
|
48
48
|
`uv run playwright install --with-deps chromium webkit` if browser system libraries
|
|
49
49
|
are missing. `tests/conftest.py` generates missing large CJA/AA fixtures on demand.
|
|
50
|
-
Do not commit generated performance fixtures or caches.
|
|
50
|
+
Do not commit generated performance fixtures or caches. Add `-n auto` to any
|
|
51
|
+
pytest command below to run it in parallel with `pytest-xdist`, as CI does.
|
|
51
52
|
|
|
52
53
|
Use `uv run ruff format` to format changes. Use a focused `uv run pytest tests/test_*.py`
|
|
53
54
|
selection while iterating, then complete the PR checks below. Offline CLI smoke:
|
|
@@ -4,6 +4,40 @@ All notable changes to `sdr-visualizer` will be documented here. The format foll
|
|
|
4
4
|
|
|
5
5
|
## [Unreleased]
|
|
6
6
|
|
|
7
|
+
## [1.2.7] - 2026-10-30
|
|
8
|
+
|
|
9
|
+
### Changed
|
|
10
|
+
|
|
11
|
+
- Advance the catalog tested-through generator markers to `cja_auto_sdr`
|
|
12
|
+
3.13.6 and `aa_auto_sdr` 1.24.5. Snapshots that declare those versions no
|
|
13
|
+
longer print a newer-than-tested warning.
|
|
14
|
+
- Accept any `cja_auto_sdr` release at 3.11.8 or newer for live `cja-lineage`
|
|
15
|
+
discovery, instead of an exact list of versions. A version newer than 3.13.6,
|
|
16
|
+
the newest version exercised for this release, still runs and prints a
|
|
17
|
+
warning. Pre-release, local, or decorated version strings, other tools, and
|
|
18
|
+
versions older than 3.11.8 still fail closed before credentials are used.
|
|
19
|
+
Live output keeps the same shape validation as saved discovery.
|
|
20
|
+
- Reject a snapshot whose JSON repeats a key within one object, instead of
|
|
21
|
+
silently keeping the last value. This applies to snapshot files, stdin, live
|
|
22
|
+
generator output, comparison baselines, and trend members, and matches
|
|
23
|
+
sdr-grader. The error does not echo the key. A trend member with a repeated
|
|
24
|
+
key is skipped with a warning.
|
|
25
|
+
- Verify Python 3.13 in CI and list Python 3.11 through 3.14 in the package
|
|
26
|
+
classifiers. The installation requirement is still Python 3.11 or newer.
|
|
27
|
+
- Maintainer tooling only: CI pins uv 0.13.6, matching sdr-grader, so CI and
|
|
28
|
+
contributors resolve the lockfile with the same uv.
|
|
29
|
+
- Update the optional AA Workspace SDK to `aanalytics2==0.5.5.post3` and align
|
|
30
|
+
its collection version guard with the package pin. The upstream change is
|
|
31
|
+
limited to the knowledge graph module, which collection does not use.
|
|
32
|
+
- Speed up CI. Tests run in parallel with `pytest-xdist`, coverage is measured
|
|
33
|
+
once on Python 3.14, superseded pull request runs are cancelled, and every
|
|
34
|
+
job has a timeout. The `version-sync` check now runs inside the `lint`
|
|
35
|
+
workflow, and Pages publishes the committed examples.
|
|
36
|
+
- Allow a manual `release.yml` dry run that runs every release gate and the
|
|
37
|
+
build but skips the tag-version check, PyPI publication, and the GitHub
|
|
38
|
+
release. The workflow policy now requires that guard on `publish` whenever
|
|
39
|
+
the release workflow has a trigger other than a tag push.
|
|
40
|
+
|
|
7
41
|
## [1.2.6] - 2026-10-27
|
|
8
42
|
|
|
9
43
|
### Changed
|
|
@@ -781,7 +815,8 @@ The following are explicitly internal and may change without notice:
|
|
|
781
815
|
- Workspace project visualization (v0.4)
|
|
782
816
|
- Schema map view (v0.5)
|
|
783
817
|
|
|
784
|
-
[Unreleased]: https://github.com/brian-a-au/sdr-visualizer/compare/v1.2.
|
|
818
|
+
[Unreleased]: https://github.com/brian-a-au/sdr-visualizer/compare/v1.2.7...HEAD
|
|
819
|
+
[1.2.7]: https://github.com/brian-a-au/sdr-visualizer/releases/tag/v1.2.7
|
|
785
820
|
[1.2.6]: https://github.com/brian-a-au/sdr-visualizer/releases/tag/v1.2.6
|
|
786
821
|
[1.2.5]: https://github.com/brian-a-au/sdr-visualizer/releases/tag/v1.2.5
|
|
787
822
|
[1.2.4]: https://github.com/brian-a-au/sdr-visualizer/releases/tag/v1.2.4
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.5
|
|
2
2
|
Name: sdr-visualizer
|
|
3
|
-
Version: 1.2.
|
|
3
|
+
Version: 1.2.7
|
|
4
4
|
Summary: Static-output visual catalog generator for Adobe Customer Journey Analytics and Adobe Analytics implementations.
|
|
5
5
|
Project-URL: Homepage, https://brian-a-au.github.io/sdr-visualizer/
|
|
6
6
|
Project-URL: Documentation, https://github.com/brian-a-au/sdr-visualizer#documentation
|
|
@@ -11,10 +11,19 @@ Author: Brian Au
|
|
|
11
11
|
License-Expression: MIT
|
|
12
12
|
License-File: LICENSE
|
|
13
13
|
License-File: THIRD_PARTY_LICENSES
|
|
14
|
+
Classifier: Environment :: Console
|
|
15
|
+
Classifier: Intended Audience :: Developers
|
|
16
|
+
Classifier: Programming Language :: Python :: 3
|
|
17
|
+
Classifier: Programming Language :: Python :: 3 :: Only
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
19
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
20
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
21
|
+
Classifier: Programming Language :: Python :: 3.14
|
|
22
|
+
Classifier: Topic :: Scientific/Engineering :: Visualization
|
|
14
23
|
Requires-Python: >=3.11
|
|
15
24
|
Requires-Dist: jinja2>=3.1
|
|
16
25
|
Provides-Extra: workspace-aa
|
|
17
|
-
Requires-Dist: aanalytics2==0.5.5.
|
|
26
|
+
Requires-Dist: aanalytics2==0.5.5.post3; extra == 'workspace-aa'
|
|
18
27
|
Requires-Dist: requests>=2.32; extra == 'workspace-aa'
|
|
19
28
|
Provides-Extra: workspace-cja
|
|
20
29
|
Requires-Dist: cjapy==0.3.1; extra == 'workspace-cja'
|
|
@@ -26,7 +35,6 @@ Description-Content-Type: text/markdown
|
|
|
26
35
|
[](https://pypi.org/project/sdr-visualizer/)
|
|
27
36
|
[](https://github.com/brian-a-au/sdr-visualizer/actions/workflows/test.yml)
|
|
28
37
|
[](https://github.com/brian-a-au/sdr-visualizer/actions/workflows/lint.yml)
|
|
29
|
-
[](https://github.com/brian-a-au/sdr-visualizer/actions/workflows/version-sync.yml)
|
|
30
38
|
[](https://www.python.org/downloads/)
|
|
31
39
|
[](https://github.com/brian-a-au/sdr-visualizer/tree/main/tests)
|
|
32
40
|
[](https://github.com/astral-sh/ruff)
|
|
@@ -62,19 +70,16 @@ confidentiality and data-handling policy.
|
|
|
62
70
|
|
|
63
71
|
## Public preview scope
|
|
64
72
|
|
|
65
|
-
This project is in public preview. A report
|
|
66
|
-
|
|
67
|
-
configuration is correct, complete, or working at runtime. Review the details
|
|
73
|
+
This project is in public preview. A report shows what a snapshot configures. It
|
|
74
|
+
does not grade the implementation or confirm that it works. Check the details
|
|
68
75
|
against the source implementation before you act on them.
|
|
69
76
|
|
|
70
|
-
The report embeds the supported
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
and later Python versions are not yet verified. The package installs on Python
|
|
77
|
-
3.11 or newer.
|
|
77
|
+
- The report embeds the supported components, not every platform field. Keep the
|
|
78
|
+
original generator snapshot for anything the report does not embed.
|
|
79
|
+
- Workspace project references are unverified. An empty result does not mean a
|
|
80
|
+
component is unused.
|
|
81
|
+
- The tool is verified on Ubuntu with Python 3.11 through 3.14. It installs on
|
|
82
|
+
Python 3.11 or newer.
|
|
78
83
|
|
|
79
84
|
## Install
|
|
80
85
|
|
|
@@ -3,7 +3,6 @@
|
|
|
3
3
|
[](https://pypi.org/project/sdr-visualizer/)
|
|
4
4
|
[](https://github.com/brian-a-au/sdr-visualizer/actions/workflows/test.yml)
|
|
5
5
|
[](https://github.com/brian-a-au/sdr-visualizer/actions/workflows/lint.yml)
|
|
6
|
-
[](https://github.com/brian-a-au/sdr-visualizer/actions/workflows/version-sync.yml)
|
|
7
6
|
[](https://www.python.org/downloads/)
|
|
8
7
|
[](https://github.com/brian-a-au/sdr-visualizer/tree/main/tests)
|
|
9
8
|
[](https://github.com/astral-sh/ruff)
|
|
@@ -39,19 +38,16 @@ confidentiality and data-handling policy.
|
|
|
39
38
|
|
|
40
39
|
## Public preview scope
|
|
41
40
|
|
|
42
|
-
This project is in public preview. A report
|
|
43
|
-
|
|
44
|
-
configuration is correct, complete, or working at runtime. Review the details
|
|
41
|
+
This project is in public preview. A report shows what a snapshot configures. It
|
|
42
|
+
does not grade the implementation or confirm that it works. Check the details
|
|
45
43
|
against the source implementation before you act on them.
|
|
46
44
|
|
|
47
|
-
The report embeds the supported
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
and later Python versions are not yet verified. The package installs on Python
|
|
54
|
-
3.11 or newer.
|
|
45
|
+
- The report embeds the supported components, not every platform field. Keep the
|
|
46
|
+
original generator snapshot for anything the report does not embed.
|
|
47
|
+
- Workspace project references are unverified. An empty result does not mean a
|
|
48
|
+
component is unused.
|
|
49
|
+
- The tool is verified on Ubuntu with Python 3.11 through 3.14. It installs on
|
|
50
|
+
Python 3.11 or newer.
|
|
55
51
|
|
|
56
52
|
## Install
|
|
57
53
|
|
|
@@ -178,6 +178,10 @@ The fixtures may diverge from sdr-grader's over time — the visualizer wants mo
|
|
|
178
178
|
and mapping keys before normalization. Embedded definition, tag, and
|
|
179
179
|
reference helpers repeat that Unicode-scalar check after decoding, when
|
|
180
180
|
escaped surrogates first materialize.
|
|
181
|
+
- `input/json_io.py` (`unique_object`, `DuplicateJSONKeyError`) — snapshot
|
|
182
|
+
files, stdin, and generator output reject a JSON object that repeats a member
|
|
183
|
+
name, mirrored from sdr-grader #127 (`c8e7316`). The helper body is
|
|
184
|
+
identical; only the module docstring differs.
|
|
181
185
|
- `_optional_list` (AA) — an absent/null optional section is `[]`, but a present non-list value raises `InvalidSnapshotError` (a malformed export, not an empty one). CJA gets the same guarantee through `_section_records`.
|
|
182
186
|
- `generator_version_warning` / `_version_tuple` / `TESTED_THROUGH_GENERATOR_VERSION` — the Q5 version-compat warning mechanism (1.0.0). The helper bodies are behavior-identical; the constant's *value* is per-platform and per-release by design (the newest generator version that release was validated against).
|
|
183
187
|
- `_optional_timestamp` — guards `created_at`/`modified_at`: keeps the value only if it's already a non-empty string, else `None`, so `_compact` drops a non-string timestamp (an epoch int, say) instead of leaking it into the payload. Present in both `cja.py` and `aa.py` here, and mirrored into the grader's copies of both adapters — a non-string timestamp is *missing*, not a value worth coercing to a numeric string.
|
|
@@ -57,7 +57,7 @@ Adobe credential, or external review service is needed.
|
|
|
57
57
|
The deployment enables **Allow auto-merge** and adds `dependabot-policy` to the
|
|
58
58
|
existing required checks, with the GitHub Actions app as the check source:
|
|
59
59
|
|
|
60
|
-
`lint`, `test (3.11)`, `test (3.12)`, `test (3.14)`, `browser-perf`, `check`, `verify`, `analyze (python, none)`, `analyze (javascript-typescript, none)`, `dependency-review`, `actionlint`, `lockfile`, `dependabot-policy`.
|
|
60
|
+
`lint`, `test (3.11)`, `test (3.12)`, `test (3.13)`, `test (3.14)`, `browser-perf`, `check`, `verify`, `analyze (python, none)`, `analyze (javascript-typescript, none)`, `dependency-review`, `actionlint`, `lockfile`, `dependabot-policy`.
|
|
61
61
|
|
|
62
62
|
Branch protection continues to require up-to-date branches and enforce the
|
|
63
63
|
requirements for administrators. Existing review requirements are preserved;
|
|
@@ -42,15 +42,12 @@ cja-lineage --live --binary /absolute/path/to/cja_auto_sdr \
|
|
|
42
42
|
Alternatively use `--profile profile-name`, or omit both credential selectors
|
|
43
43
|
to use the upstream default credential route. `--profile` and `--config-file`
|
|
44
44
|
are mutually exclusive and apply only to `--live`. The executable and explicit
|
|
45
|
-
configuration paths must be absolute. Live discovery
|
|
46
|
-
`cja_auto_sdr` 3.11.8
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
generator is installed automatically. Version 3.12.4 was qualified with fresh
|
|
52
|
-
read-only dataset discovery and saved-shape validation; this does not imply
|
|
53
|
-
complete visibility into every dataset or Connection.
|
|
45
|
+
configuration paths must be absolute. Live discovery accepts
|
|
46
|
+
`cja_auto_sdr` 3.11.8 or newer, reported as a plain `X.Y.Z` version. Older
|
|
47
|
+
versions, pre-release or local builds, and other tools fail closed before
|
|
48
|
+
credentials are used. Versions newer than 3.13.6 run with a warning. Live and
|
|
49
|
+
saved discovery output are both validated by shape. No generator is installed
|
|
50
|
+
automatically.
|
|
54
51
|
|
|
55
52
|
The command currently targets macOS and Linux, using POSIX process and
|
|
56
53
|
owner-only output protections. Windows execution is not qualified. The generated
|
|
@@ -120,7 +117,7 @@ Keep live reports private to their authorized audience. The repository
|
|
|
120
117
|
| `invalid-json` / `invalid-utf8` | Save complete UTF-8 JSON output, without terminal messages mixed into it. |
|
|
121
118
|
| `saved-read-failed` | Check that the saved file exists and is readable. |
|
|
122
119
|
| `executable-invalid` | Use an absolute, current-user-owned executable that is not group/world writable. |
|
|
123
|
-
| `executable-version-mismatch` | Use a
|
|
120
|
+
| `executable-version-mismatch` | Use a `cja_auto_sdr` release at 3.11.8 or newer that reports a plain `X.Y.Z` version. |
|
|
124
121
|
| `selector-invalid` | Check the profile or absolute configuration path and use one credential selector. |
|
|
125
122
|
| `process-failed` / `timeout` | Check upstream authentication and discovery independently; acquisition has a 600-second limit. |
|
|
126
123
|
| `saved-too-large` / `stdout-limit-exceeded` / `stderr-limit-exceeded` | Reduce the discovery scope; capture is bounded at 64 MiB for saved/stdout and 1 MiB for stderr. |
|
|
@@ -171,6 +171,12 @@ or coercion; decoder resource-limit failures and structure-limit violations
|
|
|
171
171
|
are invalid input. Values decoded from embedded JSON are checked again because
|
|
172
172
|
escaped surrogates first materialize at that boundary.
|
|
173
173
|
|
|
174
|
+
A JSON object that repeats a member name is invalid input, even when the
|
|
175
|
+
repeated values are identical. This applies to snapshot files, stdin, live
|
|
176
|
+
generator output, comparison baselines, and trend members. The error does not
|
|
177
|
+
echo the key or its values. A trend member with a repeated key is skipped with
|
|
178
|
+
a warning, like any other unreadable member.
|
|
179
|
+
|
|
174
180
|
The graph includes directed edges only when both source and target exist.
|
|
175
181
|
CJA-derived-field component references are normalized and contribute to those
|
|
176
182
|
edges and degree counts. Dangling references do not create graph edges.
|
|
@@ -207,8 +213,8 @@ unusable, and capped-out members do not warn. The current markers are:
|
|
|
207
213
|
|
|
208
214
|
| Platform | Warning threshold |
|
|
209
215
|
|---|---:|
|
|
210
|
-
| CJA (`cja_auto_sdr`) | 3.13.
|
|
211
|
-
| AA (`aa_auto_sdr`) | 1.24.
|
|
216
|
+
| CJA (`cja_auto_sdr`) | 3.13.6 |
|
|
217
|
+
| AA (`aa_auto_sdr`) | 1.24.5 |
|
|
212
218
|
|
|
213
219
|
A marker is evidence of exercised compatibility, not a promise about every
|
|
214
220
|
possible snapshot produced by that version. It may advance only after a
|
|
@@ -214,6 +214,7 @@ cannot be closed by recording the current delta as diff-backed N/A.
|
|
|
214
214
|
| Exact-SHA test matrix run(s) | pending |
|
|
215
215
|
| `test (3.11)` | pending |
|
|
216
216
|
| `test (3.12)` | pending |
|
|
217
|
+
| `test (3.13)` | pending |
|
|
217
218
|
| `test (3.14)` | pending |
|
|
218
219
|
| `browser-perf` | pending |
|
|
219
220
|
| `analyze (python, none)` | pending |
|
|
@@ -289,7 +289,8 @@ Inspect repository settings immediately before the go/no-go decision and record
|
|
|
289
289
|
the result:
|
|
290
290
|
|
|
291
291
|
- `main` requires pull requests and the expected test/lint checks, including
|
|
292
|
-
`test (3.11)`, `test (3.12)`, `test (3.14)`, and
|
|
292
|
+
`test (3.11)`, `test (3.12)`, `test (3.13)`, `test (3.14)`, and
|
|
293
|
+
`browser-perf`;
|
|
293
294
|
- the candidate has successful `analyze (python, none)` and
|
|
294
295
|
`analyze (javascript-typescript, none)` CodeQL checks, and `main` requires
|
|
295
296
|
both emitted contexts before tag authorization;
|
|
@@ -310,7 +311,7 @@ the result:
|
|
|
310
311
|
|
|
311
312
|
Inspect the emitted hosted check names and branch-protection/ruleset required
|
|
312
313
|
contexts; editing the workflow does not update repository settings. Retain the
|
|
313
|
-
established required contexts and add `test (3.14)` through the authorized
|
|
314
|
+
established required contexts and add `test (3.13)` and `test (3.14)` through the authorized
|
|
314
315
|
hosted-control workflow. Record the settings inspection and exact-SHA run links.
|
|
315
316
|
Each Python matrix lane must pass its runtime-version assertion, non-browser
|
|
316
317
|
coverage gate, and Python performance gate. The test and release browser jobs,
|
|
@@ -437,6 +438,13 @@ versions, and one live generation path against the frozen candidate. Any drift
|
|
|
437
438
|
from candidate evidence stops publication and requires complete
|
|
438
439
|
requalification; it is not patched into the release after tagging.
|
|
439
440
|
|
|
441
|
+
Before tagging, a maintainer may dispatch `release.yml` manually on the
|
|
442
|
+
candidate commit (`gh workflow run release.yml --ref <branch>`) as a dry run.
|
|
443
|
+
It runs the browser and performance gates, the build, and the installed-artifact
|
|
444
|
+
smoke run, and it skips the tag-version check, PyPI publication, and the GitHub
|
|
445
|
+
release. A dry run's `dist` artifact is not the authoritative release artifact;
|
|
446
|
+
only the tag-triggered run produces that.
|
|
447
|
+
|
|
440
448
|
After the candidate is approved, a separate explicit tag authorization starts
|
|
441
449
|
the release workflow. Record the workflow run ID and attempt, event, tag,
|
|
442
450
|
`headSha`, job conclusions, retained artifact ID, artifact expiry, distribution
|
|
@@ -183,7 +183,7 @@ bounded selection; an empty catalog cannot request collection.
|
|
|
183
183
|
|
|
184
184
|
## Collection behavior and limits
|
|
185
185
|
|
|
186
|
-
Collection uses `cjapy==0.3.1` for CJA and `aanalytics2==0.5.5.
|
|
186
|
+
Collection uses `cjapy==0.3.1` for CJA and `aanalytics2==0.5.5.post3` for AA.
|
|
187
187
|
Each SDK's `findComponentsUsage` runs locally against sanitized fetched project
|
|
188
188
|
definitions in a separate, credential-free process. Characterized helpers can
|
|
189
189
|
miss structures or return false positives. **All SDK results remain unverified
|
|
@@ -1,19 +1,30 @@
|
|
|
1
1
|
[project]
|
|
2
2
|
name = "sdr-visualizer"
|
|
3
|
-
version = "1.2.
|
|
3
|
+
version = "1.2.7"
|
|
4
4
|
description = "Static-output visual catalog generator for Adobe Customer Journey Analytics and Adobe Analytics implementations."
|
|
5
5
|
readme = "README.md"
|
|
6
6
|
license = "MIT"
|
|
7
7
|
license-files = ["LICENSE", "THIRD_PARTY_LICENSES"]
|
|
8
8
|
authors = [{ name = "Brian Au" }]
|
|
9
9
|
requires-python = ">=3.11"
|
|
10
|
+
classifiers = [
|
|
11
|
+
"Environment :: Console",
|
|
12
|
+
"Intended Audience :: Developers",
|
|
13
|
+
"Programming Language :: Python :: 3",
|
|
14
|
+
"Programming Language :: Python :: 3 :: Only",
|
|
15
|
+
"Programming Language :: Python :: 3.11",
|
|
16
|
+
"Programming Language :: Python :: 3.12",
|
|
17
|
+
"Programming Language :: Python :: 3.13",
|
|
18
|
+
"Programming Language :: Python :: 3.14",
|
|
19
|
+
"Topic :: Scientific/Engineering :: Visualization",
|
|
20
|
+
]
|
|
10
21
|
dependencies = [
|
|
11
22
|
"jinja2>=3.1",
|
|
12
23
|
]
|
|
13
24
|
|
|
14
25
|
[project.optional-dependencies]
|
|
15
26
|
workspace-cja = ["cjapy==0.3.1", "requests>=2.32"]
|
|
16
|
-
workspace-aa = ["aanalytics2==0.5.5.
|
|
27
|
+
workspace-aa = ["aanalytics2==0.5.5.post3", "requests>=2.32"]
|
|
17
28
|
|
|
18
29
|
[project.scripts]
|
|
19
30
|
sdr-visualizer = "sdr_visualizer.cli.main:main"
|
|
@@ -35,6 +46,7 @@ dev = [
|
|
|
35
46
|
"jsonschema>=4.21",
|
|
36
47
|
"pytest-cov>=5.0",
|
|
37
48
|
"pyyaml>=6.0",
|
|
49
|
+
"pytest-xdist>=3.8.0",
|
|
38
50
|
]
|
|
39
51
|
browser = [
|
|
40
52
|
"playwright>=1.43",
|
|
@@ -29,6 +29,9 @@ EXPECTED_CODEQL_MATRIX = {
|
|
|
29
29
|
}
|
|
30
30
|
EXPECTED_CODEQL_CONFIG_FILE = "./.github/codeql/codeql-config.yml"
|
|
31
31
|
EXPECTED_CODEQL_IGNORES = ["examples/**"]
|
|
32
|
+
# The one job guard release.yml may use: it skips publication on manual dry
|
|
33
|
+
# runs and, lacking a status function, still implies success().
|
|
34
|
+
RELEASE_PUBLISH_GUARD = "github.event_name == 'push'"
|
|
32
35
|
|
|
33
36
|
|
|
34
37
|
def _error(path: Path, message: str) -> str:
|
|
@@ -243,12 +246,26 @@ def _release_errors(path: Path, workflow: dict[str, Any]) -> list[str]:
|
|
|
243
246
|
errors.append(_error(path, "github-release must need publish"))
|
|
244
247
|
for job_name, job in jobs.items():
|
|
245
248
|
if isinstance(job, dict):
|
|
249
|
+
item = job
|
|
250
|
+
if job_name == "publish" and job.get("if") == RELEASE_PUBLISH_GUARD:
|
|
251
|
+
item = {key: value for key, value in job.items() if key != "if"}
|
|
246
252
|
_require_default_success(
|
|
247
253
|
errors,
|
|
248
254
|
path=path,
|
|
249
255
|
label=f"job {job_name!r}",
|
|
250
|
-
item=
|
|
256
|
+
item=item,
|
|
251
257
|
)
|
|
258
|
+
# PyYAML reads the bare `on` key as boolean True.
|
|
259
|
+
triggers = workflow.get("on", workflow.get(True))
|
|
260
|
+
trigger_names = set(triggers) if isinstance(triggers, (dict, list)) else {triggers}
|
|
261
|
+
if trigger_names != {"push"} and publish.get("if") != RELEASE_PUBLISH_GUARD:
|
|
262
|
+
errors.append(
|
|
263
|
+
_error(
|
|
264
|
+
path,
|
|
265
|
+
f"publish must use 'if: {RELEASE_PUBLISH_GUARD}' when release has "
|
|
266
|
+
"triggers other than push",
|
|
267
|
+
)
|
|
268
|
+
)
|
|
252
269
|
|
|
253
270
|
build_distribution = _step_index(
|
|
254
271
|
build, lambda step: _run_is(step, "uv build --out-dir dist/packages")
|
|
@@ -356,7 +356,7 @@ def _walk_segment_definition(definition: Any) -> tuple[int, list[str]]:
|
|
|
356
356
|
# and the historical compatibility corpus; snapshot declarations alone do not
|
|
357
357
|
# establish this marker. Undeclared versions stay unknown and do not warn.
|
|
358
358
|
# The compatibility policy warns about newer declared versions, never refuses.
|
|
359
|
-
TESTED_THROUGH_GENERATOR_VERSION = "1.24.
|
|
359
|
+
TESTED_THROUGH_GENERATOR_VERSION = "1.24.5"
|
|
360
360
|
|
|
361
361
|
|
|
362
362
|
def generator_version_warning(adapter_version: str) -> str | None:
|
|
@@ -368,7 +368,7 @@ def _walk_for_contexts(node: Any, out: list[str]) -> None:
|
|
|
368
368
|
# and the historical compatibility corpus; snapshot declarations alone do not
|
|
369
369
|
# establish this marker. Undeclared versions stay unknown and do not warn.
|
|
370
370
|
# The compatibility policy warns about newer declared versions, never refuses.
|
|
371
|
-
TESTED_THROUGH_GENERATOR_VERSION = "3.13.
|
|
371
|
+
TESTED_THROUGH_GENERATOR_VERSION = "3.13.6"
|
|
372
372
|
|
|
373
373
|
|
|
374
374
|
def generator_version_warning(adapter_version: str) -> str | None:
|
|
@@ -34,7 +34,7 @@ def main(argv: list[str] | None = None) -> int:
|
|
|
34
34
|
parser.add_argument(
|
|
35
35
|
"--binary",
|
|
36
36
|
type=Path,
|
|
37
|
-
help="Absolute path to cja_auto_sdr 3.11.8
|
|
37
|
+
help="Absolute path to cja_auto_sdr 3.11.8 or newer (live only)",
|
|
38
38
|
)
|
|
39
39
|
auth = parser.add_mutually_exclusive_group()
|
|
40
40
|
auth.add_argument("--profile", help="Named upstream credential profile (live only)")
|
|
@@ -80,7 +80,11 @@ def main(argv: list[str] | None = None) -> int:
|
|
|
80
80
|
return 3
|
|
81
81
|
topology = (
|
|
82
82
|
acquire_live(
|
|
83
|
-
args.binary,
|
|
83
|
+
args.binary,
|
|
84
|
+
scope_label=label,
|
|
85
|
+
profile=args.profile,
|
|
86
|
+
config_file=args.config_file,
|
|
87
|
+
on_warning=lambda message: print(f"cja-lineage: warning: {message}", file=sys.stderr),
|
|
84
88
|
)
|
|
85
89
|
if args.live
|
|
86
90
|
else acquire_saved(args.saved, scope_label=label)
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
"""Object construction for external JSON read as snapshots."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from typing import Any
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class DuplicateJSONKeyError(ValueError):
|
|
9
|
+
"""A serialized object repeats a decoded member name."""
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
def unique_object(pairs: list[tuple[str, Any]]) -> dict[str, Any]:
|
|
13
|
+
"""Reject duplicate names without exposing input keys or values in errors."""
|
|
14
|
+
result: dict[str, Any] = {}
|
|
15
|
+
for key, value in pairs:
|
|
16
|
+
if key in result:
|
|
17
|
+
raise DuplicateJSONKeyError("JSON contains duplicate object keys")
|
|
18
|
+
result[key] = value
|
|
19
|
+
return result
|
|
@@ -12,6 +12,7 @@ import signal
|
|
|
12
12
|
import stat
|
|
13
13
|
import subprocess
|
|
14
14
|
import time
|
|
15
|
+
from collections.abc import Callable
|
|
15
16
|
from contextlib import suppress
|
|
16
17
|
from dataclasses import dataclass
|
|
17
18
|
from enum import StrEnum
|
|
@@ -27,7 +28,12 @@ DEFAULT_SAVED_LIMIT = DEFAULT_STDOUT_LIMIT
|
|
|
27
28
|
DEFAULT_TIMEOUT_SECONDS = 600.0
|
|
28
29
|
|
|
29
30
|
_READ_SIZE = 64 * 1024
|
|
30
|
-
|
|
31
|
+
# Live discovery accepts any plain X.Y.Z release at or above the first
|
|
32
|
+
# qualified version. Output is shape-validated like saved discovery, so a
|
|
33
|
+
# version newer than the last one exercised warns instead of failing.
|
|
34
|
+
MINIMUM_LIVE_GENERATOR_VERSION = "3.11.8"
|
|
35
|
+
TESTED_THROUGH_LIVE_GENERATOR_VERSION = "3.13.6"
|
|
36
|
+
_VERSION_OUTPUT = re.compile(rb"\Acja_auto_sdr (\d{1,6})\.(\d{1,6})\.(\d{1,6})(?:\r?\n)?\Z")
|
|
31
37
|
_VERSION_OUTPUT_LIMIT = 8 * 1024
|
|
32
38
|
_VERSION_TIMEOUT_SECONDS = 10.0
|
|
33
39
|
_PROFILE = re.compile(r"\A[A-Za-z0-9][A-Za-z0-9_.-]{0,127}\Z")
|
|
@@ -124,11 +130,13 @@ def acquire_live(
|
|
|
124
130
|
stdout_limit: int = DEFAULT_STDOUT_LIMIT,
|
|
125
131
|
stderr_limit: int = DEFAULT_STDERR_LIMIT,
|
|
126
132
|
timeout_seconds: float = DEFAULT_TIMEOUT_SECONDS,
|
|
133
|
+
on_warning: Callable[[str], None] | None = None,
|
|
127
134
|
) -> LineageDiscoveryResult:
|
|
128
135
|
"""Run verified CJA discovery and normalize its bounded stdout.
|
|
129
136
|
|
|
130
137
|
Executable identity and version are checked in a credential-free process
|
|
131
138
|
before profile/config selectors or the live environment are constructed.
|
|
139
|
+
A version newer than the tested-through marker is passed to ``on_warning``.
|
|
132
140
|
"""
|
|
133
141
|
if (
|
|
134
142
|
not _positive_limit(stdout_limit)
|
|
@@ -147,8 +155,11 @@ def acquire_live(
|
|
|
147
155
|
|
|
148
156
|
executable_path = Path(executable)
|
|
149
157
|
verification = _verify_executable(executable_path)
|
|
150
|
-
if verification
|
|
158
|
+
if isinstance(verification, LineageDiscoveryFailure):
|
|
151
159
|
return verification
|
|
160
|
+
warning = _live_version_warning(verification)
|
|
161
|
+
if warning is not None and on_warning is not None:
|
|
162
|
+
on_warning(warning)
|
|
152
163
|
|
|
153
164
|
argv = [str(executable_path)]
|
|
154
165
|
if normalized_profile is not None:
|
|
@@ -174,7 +185,7 @@ def acquire_live(
|
|
|
174
185
|
return _decode_and_normalize(captured.stdout, scope_label=scope_label)
|
|
175
186
|
|
|
176
187
|
|
|
177
|
-
def _verify_executable(path: Path) -> LineageDiscoveryFailure |
|
|
188
|
+
def _verify_executable(path: Path) -> LineageDiscoveryFailure | tuple[int, int, int]:
|
|
178
189
|
if not path.is_absolute():
|
|
179
190
|
return LineageDiscoveryFailure(LineageDiscoveryFailureCode.EXECUTABLE_INVALID)
|
|
180
191
|
try:
|
|
@@ -198,9 +209,29 @@ def _verify_executable(path: Path) -> LineageDiscoveryFailure | None:
|
|
|
198
209
|
)
|
|
199
210
|
if isinstance(captured, LineageDiscoveryFailure) or captured.returncode != 0:
|
|
200
211
|
return LineageDiscoveryFailure(LineageDiscoveryFailureCode.EXECUTABLE_INVALID)
|
|
201
|
-
|
|
212
|
+
match = _VERSION_OUTPUT.fullmatch(captured.stdout)
|
|
213
|
+
if match is None:
|
|
202
214
|
return LineageDiscoveryFailure(LineageDiscoveryFailureCode.EXECUTABLE_VERSION_MISMATCH)
|
|
203
|
-
|
|
215
|
+
version = (int(match[1]), int(match[2]), int(match[3]))
|
|
216
|
+
if version < _parse_version(MINIMUM_LIVE_GENERATOR_VERSION):
|
|
217
|
+
return LineageDiscoveryFailure(LineageDiscoveryFailureCode.EXECUTABLE_VERSION_MISMATCH)
|
|
218
|
+
return version
|
|
219
|
+
|
|
220
|
+
|
|
221
|
+
def _parse_version(text: str) -> tuple[int, int, int]:
|
|
222
|
+
major, minor, patch = (int(part) for part in text.split("."))
|
|
223
|
+
return major, minor, patch
|
|
224
|
+
|
|
225
|
+
|
|
226
|
+
def _live_version_warning(version: tuple[int, int, int]) -> str | None:
|
|
227
|
+
if version <= _parse_version(TESTED_THROUGH_LIVE_GENERATOR_VERSION):
|
|
228
|
+
return None
|
|
229
|
+
return (
|
|
230
|
+
f"cja_auto_sdr {'.'.join(map(str, version))} is newer than the newest version "
|
|
231
|
+
f"this release was tested against for live lineage "
|
|
232
|
+
f"({TESTED_THROUGH_LIVE_GENERATOR_VERSION}); newer discovery fields may not be "
|
|
233
|
+
"represented"
|
|
234
|
+
)
|
|
204
235
|
|
|
205
236
|
|
|
206
237
|
def _validate_selectors(
|
|
@@ -20,6 +20,7 @@ from pathlib import Path
|
|
|
20
20
|
from typing import Any
|
|
21
21
|
|
|
22
22
|
from sdr_visualizer.core.exceptions import InvalidSnapshotError
|
|
23
|
+
from sdr_visualizer.input.json_io import DuplicateJSONKeyError, unique_object
|
|
23
24
|
|
|
24
25
|
STDIN_TOKEN = "-"
|
|
25
26
|
|
|
@@ -60,7 +61,9 @@ def _load_stdin() -> tuple[dict[str, Any], str]:
|
|
|
60
61
|
if not raw.strip():
|
|
61
62
|
raise InvalidSnapshotError("stdin is empty; expected JSON snapshot")
|
|
62
63
|
try:
|
|
63
|
-
snapshot = json.loads(raw)
|
|
64
|
+
snapshot = json.loads(raw, object_pairs_hook=unique_object)
|
|
65
|
+
except DuplicateJSONKeyError:
|
|
66
|
+
raise InvalidSnapshotError("stdin JSON contains duplicate object keys") from None
|
|
64
67
|
except json.JSONDecodeError as exc:
|
|
65
68
|
raise InvalidSnapshotError(f"stdin is not valid JSON: {exc}") from exc
|
|
66
69
|
except ValueError as exc:
|
|
@@ -76,7 +79,9 @@ def _load_from_file(path: Path) -> tuple[dict[str, Any], str]:
|
|
|
76
79
|
except (OSError, UnicodeError) as exc:
|
|
77
80
|
raise InvalidSnapshotError(f"could not read {path}: {exc}") from exc
|
|
78
81
|
try:
|
|
79
|
-
snapshot = json.loads(text)
|
|
82
|
+
snapshot = json.loads(text, object_pairs_hook=unique_object)
|
|
83
|
+
except DuplicateJSONKeyError:
|
|
84
|
+
raise InvalidSnapshotError(f"{path}: JSON contains duplicate object keys") from None
|
|
80
85
|
except json.JSONDecodeError as exc:
|
|
81
86
|
raise InvalidSnapshotError(f"{path}: not valid JSON: {exc}") from exc
|
|
82
87
|
except ValueError as exc:
|
|
@@ -15,6 +15,7 @@ from pathlib import Path
|
|
|
15
15
|
from typing import Any
|
|
16
16
|
|
|
17
17
|
from sdr_visualizer.core.exceptions import InvalidSnapshotError
|
|
18
|
+
from sdr_visualizer.input.json_io import DuplicateJSONKeyError, unique_object
|
|
18
19
|
|
|
19
20
|
SHELL_OUT_TIMEOUT_SECONDS = 600
|
|
20
21
|
|
|
@@ -107,7 +108,9 @@ def _shell_out(
|
|
|
107
108
|
raise InvalidSnapshotError(f"{tool} JSON output could not be read: {exc}") from exc
|
|
108
109
|
|
|
109
110
|
try:
|
|
110
|
-
snapshot = json.loads(snapshot_text)
|
|
111
|
+
snapshot = json.loads(snapshot_text, object_pairs_hook=unique_object)
|
|
112
|
+
except DuplicateJSONKeyError:
|
|
113
|
+
raise InvalidSnapshotError(f"{tool} output contains duplicate object keys") from None
|
|
111
114
|
except json.JSONDecodeError as exc:
|
|
112
115
|
raise InvalidSnapshotError(f"{tool} produced output that is not valid JSON: {exc}") from exc
|
|
113
116
|
except ValueError as exc:
|
|
@@ -14,7 +14,7 @@ from sdr_visualizer.core.structure_limits import (
|
|
|
14
14
|
)
|
|
15
15
|
|
|
16
16
|
MAX_IPC_BYTES = 16 * 1024 * 1024
|
|
17
|
-
SDK_VERSIONS = {"cja": ("cjapy", "0.3.1"), "aa": ("aanalytics2", "0.5.5.
|
|
17
|
+
SDK_VERSIONS = {"cja": ("cjapy", "0.3.1"), "aa": ("aanalytics2", "0.5.5.post3")}
|
|
18
18
|
EXCLUDED_PROJECTS_LIMITATION = "Malformed or unsupported projects were excluded from SDK matching"
|
|
19
19
|
CANDIDATE_LIMITATION = (
|
|
20
20
|
"SDK matching is incomplete; exact component and environment match remain unverified"
|
|
@@ -858,7 +858,7 @@ def test_equal_older_or_unparseable_versions_do_not_warn():
|
|
|
858
858
|
|
|
859
859
|
|
|
860
860
|
def test_tuple_length_mismatch_versions_compare_correctly():
|
|
861
|
-
# (3, 14) > (3, 13,
|
|
861
|
+
# (3, 14) > (3, 13, 6), while the shorter (3, 13) sorts below the
|
|
862
862
|
# longer tested tuple. This pins Python tuple ordering as the contract.
|
|
863
863
|
from sdr_visualizer.adapters.cja import generator_version_warning
|
|
864
864
|
|
|
@@ -1101,10 +1101,10 @@ def test_tested_generator_version_does_not_warn(tmp_path, capsys):
|
|
|
1101
1101
|
@pytest.mark.parametrize(
|
|
1102
1102
|
("platform", "version", "warns"),
|
|
1103
1103
|
[
|
|
1104
|
-
("cja", "3.13.
|
|
1105
|
-
("cja", "3.13.
|
|
1106
|
-
("aa", "1.24.
|
|
1107
|
-
("aa", "1.24.
|
|
1104
|
+
("cja", "3.13.6", False),
|
|
1105
|
+
("cja", "3.13.7", True),
|
|
1106
|
+
("aa", "1.24.5", False),
|
|
1107
|
+
("aa", "1.24.6", True),
|
|
1108
1108
|
],
|
|
1109
1109
|
)
|
|
1110
1110
|
def test_catalog_generator_warning_boundary(platform, version, warns, tmp_path, capsys):
|