@@ -1537,9 +1537,48 @@ def test_ci_selection_returns_has_plugins_contract(self) -> None:
15371537 "mutation_cargo_packages" : [],
15381538 "has_mutation_cargo_packages" : False ,
15391539 "mutation_jobs" : [],
1540+ "release_validation_tags" : [],
1541+ "has_release_validation_tags" : False ,
15401542 },
15411543 )
15421544
1545+ def test_ci_selection_detects_plugin_version_bump_for_release_validation (self ) -> None :
1546+ with tempfile .TemporaryDirectory () as tmpdir :
1547+ root = Path (tmpdir )
1548+ git = lambda * args : subprocess .run ( # noqa: E731
1549+ ["git" , * args ],
1550+ cwd = root ,
1551+ text = True ,
1552+ capture_output = True ,
1553+ check = True ,
1554+ )
1555+ git ("init" )
1556+ git ("config" , "user.name" , "Test User" )
1557+ git ("config" , "user.email" , "test@example.com" )
1558+ (root / "Cargo.toml" ).write_text (
1559+ '[workspace]\n members = ["plugins/rust/python-package/rate_limiter", "plugins/rust/python-package/pii_filter"]\n '
1560+ )
1561+ rate_limiter = self ._create_plugin (root , "rate_limiter" )
1562+ self ._create_plugin (root , "pii_filter" )
1563+ git ("add" , "." )
1564+ git ("commit" , "--no-verify" , "-m" , "seed layout" )
1565+ base_sha = git ("rev-parse" , "HEAD" ).stdout .strip ()
1566+
1567+ (rate_limiter / "Cargo.toml" ).write_text (
1568+ '[package]\n name = "rate_limiter"\n version = "0.0.2"\n repository = "https://github.com/IBM/cpex-plugins"\n '
1569+ )
1570+ (rate_limiter / "cpex_rate_limiter" / "plugin-manifest.yaml" ).write_text (
1571+ 'description: "rate_limiter"\n author: "ContextForge Team"\n version: "0.0.2"\n kind: "cpex_rate_limiter.rate_limiter.RateLimiterPlugin"\n available_hooks:\n - "tool_pre_invoke"\n '
1572+ )
1573+ git ("add" , "." )
1574+ git ("commit" , "--no-verify" , "-m" , "bump rate limiter" )
1575+
1576+ result = run_catalog ("ci-selection" , str (root ), "diff" , base_sha , "HEAD" )
1577+ self .assertEqual (result .returncode , 0 , result .stderr )
1578+ payload = json .loads (result .stdout )
1579+ self .assertEqual (payload ["release_validation_tags" ], ["rate-limiter-v0.0.2" ])
1580+ self .assertTrue (payload ["has_release_validation_tags" ])
1581+
15431582 def test_ci_selection_treats_catalog_test_change_as_not_shared (self ) -> None :
15441583 with tempfile .TemporaryDirectory () as tmpdir :
15451584 root = Path (tmpdir )
@@ -1581,6 +1620,8 @@ def test_ci_selection_treats_catalog_test_change_as_not_shared(self) -> None:
15811620 "mutation_cargo_packages" : [],
15821621 "has_mutation_cargo_packages" : False ,
15831622 "mutation_jobs" : [],
1623+ "release_validation_tags" : [],
1624+ "has_release_validation_tags" : False ,
15841625 },
15851626 )
15861627
@@ -1625,6 +1666,8 @@ def test_ci_selection_treats_shared_tool_changes_as_all_plugins(self) -> None:
16251666 "mutation_cargo_packages" : [],
16261667 "has_mutation_cargo_packages" : False ,
16271668 "mutation_jobs" : [],
1669+ "release_validation_tags" : [],
1670+ "has_release_validation_tags" : False ,
16281671 },
16291672 )
16301673
@@ -1671,6 +1714,8 @@ def test_ci_selection_treats_tooling_config_changes_as_all_plugins(self) -> None
16711714 "mutation_cargo_packages" : [],
16721715 "has_mutation_cargo_packages" : False ,
16731716 "mutation_jobs" : [],
1717+ "release_validation_tags" : [],
1718+ "has_release_validation_tags" : False ,
16741719 },
16751720 )
16761721
@@ -1749,6 +1794,8 @@ def test_ci_selection_treats_cargo_lock_change_as_all_plugins(self) -> None:
17491794 "mutation_cargo_packages" : [],
17501795 "has_mutation_cargo_packages" : False ,
17511796 "mutation_jobs" : [],
1797+ "release_validation_tags" : [],
1798+ "has_release_validation_tags" : False ,
17521799 },
17531800 )
17541801
@@ -1792,6 +1839,8 @@ def test_ci_selection_treats_deny_config_change_as_all_plugins(self) -> None:
17921839 "mutation_cargo_packages" : [],
17931840 "has_mutation_cargo_packages" : False ,
17941841 "mutation_jobs" : [],
1842+ "release_validation_tags" : [],
1843+ "has_release_validation_tags" : False ,
17951844 },
17961845 )
17971846
@@ -1837,6 +1886,8 @@ def test_changed_returns_plugin_for_plugin_integration_test_change(self) -> None
18371886 "mutation_cargo_packages" : [],
18381887 "has_mutation_cargo_packages" : False ,
18391888 "mutation_jobs" : [],
1889+ "release_validation_tags" : [],
1890+ "has_release_validation_tags" : False ,
18401891 },
18411892 )
18421893
@@ -1882,6 +1933,8 @@ def test_ci_selection_treats_shared_plugin_tests_change_as_all_plugins(self) ->
18821933 "mutation_cargo_packages" : [],
18831934 "has_mutation_cargo_packages" : False ,
18841935 "mutation_jobs" : [],
1936+ "release_validation_tags" : [],
1937+ "has_release_validation_tags" : False ,
18851938 },
18861939 )
18871940
@@ -1937,6 +1990,8 @@ def test_ci_selection_treats_shared_crate_changes_as_all_plugins(self) -> None:
19371990 "test_packages" : ["rate_limiter" ],
19381991 }
19391992 ],
1993+ "release_validation_tags" : [],
1994+ "has_release_validation_tags" : False ,
19401995 },
19411996 )
19421997
@@ -2177,6 +2232,8 @@ def test_ci_selection_reports_cargo_packages_for_single_plugin_diff(self) -> Non
21772232 "mutation_cargo_packages" : [],
21782233 "has_mutation_cargo_packages" : False ,
21792234 "mutation_jobs" : [],
2235+ "release_validation_tags" : [],
2236+ "has_release_validation_tags" : False ,
21802237 },
21812238 )
21822239
@@ -2225,6 +2282,8 @@ def test_ci_selection_reports_mutation_package_for_single_rust_diff(self) -> Non
22252282 "mutation_jobs" : [
22262283 {"cargo_package" : "rate_limiter" , "in_diff" : True , "test_packages" : []}
22272284 ],
2285+ "release_validation_tags" : [],
2286+ "has_release_validation_tags" : False ,
22282287 },
22292288 )
22302289
@@ -2271,6 +2330,14 @@ def test_ci_selection_field_prints_json_and_bool_scalars(self) -> None:
22712330 self .assertEqual (result .returncode , 0 , result .stderr )
22722331 self .assertEqual (result .stdout .strip (), "true" )
22732332
2333+ result = run_catalog ("ci-selection-field" , str (REPO_ROOT ), "all" , "" , "" , "release_validation_tags" )
2334+ self .assertEqual (result .returncode , 0 , result .stderr )
2335+ self .assertEqual (json .loads (result .stdout ), [])
2336+
2337+ result = run_catalog ("ci-selection-field" , str (REPO_ROOT ), "all" , "" , "" , "has_release_validation_tags" )
2338+ self .assertEqual (result .returncode , 0 , result .stderr )
2339+ self .assertEqual (result .stdout .strip (), "false" )
2340+
22742341 def test_ci_selection_field_supports_diff_mode (self ) -> None :
22752342 with tempfile .TemporaryDirectory () as tmpdir :
22762343 root = Path (tmpdir )
@@ -2498,7 +2565,7 @@ def test_ci_workflow_uses_make_targets_for_plugin_checks(self) -> None:
24982565 self .assertIn ("working-directory: plugins/rust/python-package/${{ matrix.plugin }}" , workflow )
24992566 self .assertIn ("release-validation:" , workflow )
25002567 self .assertIn ("uses: ./.github/workflows/release-rust-python-package.yaml" , workflow )
2501- self .assertIn ("tag: retry-with-backoff-v0.2.0 " , workflow )
2568+ self .assertIn ("tag: ${{ matrix.tag }} " , workflow )
25022569 self .assertIn ("repository: testpypi" , workflow )
25032570 self .assertIn ("publish_enabled: false" , workflow )
25042571 self .assertNotIn ("tools/plugin_catalog.py ci-selection-field" , workflow )
@@ -2574,6 +2641,9 @@ def test_ci_workflow_includes_parity_jobs_for_rust_plugin_checks(self) -> None:
25742641 documentation_section = self ._extract_workflow_job_section (
25752642 workflow , "documentation"
25762643 )
2644+ release_validation_section = self ._extract_workflow_job_section (
2645+ workflow , "release-validation"
2646+ )
25772647 detect_run = self ._extract_workflow_step_run (
25782648 workflow , "validate-and-detect" , step_id = "detect"
25792649 )
@@ -2621,6 +2691,8 @@ def test_ci_workflow_includes_parity_jobs_for_rust_plugin_checks(self) -> None:
26212691 self .assertIn ("mutation_cargo_packages: ${{ steps.detect.outputs.mutation_cargo_packages }}" , workflow )
26222692 self .assertIn ("mutation_jobs: ${{ steps.detect.outputs.mutation_jobs }}" , workflow )
26232693 self .assertIn ("has_mutation_cargo_packages: ${{ steps.detect.outputs.has_mutation_cargo_packages }}" , workflow )
2694+ self .assertIn ("release_validation_tags: ${{ steps.detect.outputs.release_validation_tags }}" , workflow )
2695+ self .assertIn ("has_release_validation_tags: ${{ steps.detect.outputs.has_release_validation_tags }}" , workflow )
26242696 self .assertIn ("security-policy:" , workflow )
26252697 self .assertIn ("mutation-testing:" , workflow )
26262698 self .assertIn ("coverage:" , workflow )
@@ -2630,6 +2702,8 @@ def test_ci_workflow_includes_parity_jobs_for_rust_plugin_checks(self) -> None:
26302702 self .assertIn ("if: github.event_name == 'pull_request' && needs.validate-and-detect.outputs.has_mutation_cargo_packages == 'true'" , mutants_section )
26312703 self .assertIn ("if: needs.validate-and-detect.outputs.has_plugins == 'true'" , coverage_section )
26322704 self .assertIn ("if: needs.validate-and-detect.outputs.has_plugins == 'true'" , documentation_section )
2705+ self .assertIn ("if: github.event_name == 'pull_request' && needs.validate-and-detect.outputs.has_release_validation_tags == 'true'" , release_validation_section )
2706+ self .assertIn ("tag: ${{ fromJson(needs.validate-and-detect.outputs.release_validation_tags) }}" , release_validation_section )
26332707 self .assertNotIn ("cargo-audit" , security_section )
26342708 self .assertNotIn ("cargo audit" , security_section )
26352709 self .assertIn ("cargo install cargo-deny" , security_section )
@@ -3259,7 +3333,9 @@ def test_release_workflow_tests_artifacts_outside_source_tree(self) -> None:
32593333 'venv_python="${tmpdir}/venv/Scripts/python.exe"' ,
32603334 workflow ,
32613335 )
3336+ self .assertIn ('uv pip install --python "${venv_python}" --group dev PyYAML' , workflow )
32623337 self .assertIn ('"${venv_python}" -m pip install' , workflow )
3338+ self .assertNotIn ('pytest pytest-asyncio PyYAML' , workflow )
32633339 self .assertIn ('"${venv_python}" -m pytest' , workflow )
32643340 self .assertIn (
32653341 "actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd" ,
0 commit comments