pytest-selective 0.3.0__tar.gz → 0.4.1__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.
Files changed (170) hide show
  1. {pytest_selective-0.3.0 → pytest_selective-0.4.1}/.github/workflows/ci.yml +1 -17
  2. {pytest_selective-0.3.0 → pytest_selective-0.4.1}/.github/workflows/public-actions.yml +0 -2
  3. pytest_selective-0.4.1/.release-please-manifest.json +3 -0
  4. {pytest_selective-0.3.0 → pytest_selective-0.4.1}/CHANGELOG.md +14 -0
  5. {pytest_selective-0.3.0 → pytest_selective-0.4.1}/PKG-INFO +5 -2
  6. {pytest_selective-0.3.0 → pytest_selective-0.4.1}/README.md +4 -1
  7. {pytest_selective-0.3.0 → pytest_selective-0.4.1}/action.yml +12 -3
  8. {pytest_selective-0.3.0 → pytest_selective-0.4.1}/actions/plan-lanes/action.yml +4 -4
  9. {pytest_selective-0.3.0 → pytest_selective-0.4.1}/actions/test-impact/action.yml +13 -5
  10. {pytest_selective-0.3.0 → pytest_selective-0.4.1}/docs/README.md +1 -0
  11. {pytest_selective-0.3.0 → pytest_selective-0.4.1}/docs/ci-toolkit.md +3 -2
  12. {pytest_selective-0.3.0 → pytest_selective-0.4.1}/docs/ci.md +45 -13
  13. {pytest_selective-0.3.0 → pytest_selective-0.4.1}/docs/configuration.md +3 -0
  14. {pytest_selective-0.3.0 → pytest_selective-0.4.1}/docs/selection-health.md +2 -1
  15. {pytest_selective-0.3.0 → pytest_selective-0.4.1}/docs/tutorial.md +3 -0
  16. pytest_selective-0.4.1/docs/tutorials/adoption-checklist.md +244 -0
  17. pytest_selective-0.4.1/docs/tutorials/sidecar-dependencies.md +270 -0
  18. {pytest_selective-0.3.0 → pytest_selective-0.4.1}/docs/tutorials/skip-unrelated-ci.md +5 -6
  19. pytest_selective-0.4.1/docs/work-in-progress/tickets/rerun-selection-test-plan.md +7 -0
  20. {pytest_selective-0.3.0 → pytest_selective-0.4.1}/mkdocs.yml +6 -0
  21. {pytest_selective-0.3.0 → pytest_selective-0.4.1}/pyproject.toml +1 -1
  22. {pytest_selective-0.3.0 → pytest_selective-0.4.1}/skills/pytest-selective/SKILL.md +7 -1
  23. {pytest_selective-0.3.0 → pytest_selective-0.4.1}/skills/pytest-selective/references/adoption.md +7 -3
  24. pytest_selective-0.4.1/src/pytest_selective/checkpoint.py +55 -0
  25. {pytest_selective-0.3.0 → pytest_selective-0.4.1}/src/pytest_selective/ci.py +7 -2
  26. {pytest_selective-0.3.0 → pytest_selective-0.4.1}/src/pytest_selective/impact_cli.py +20 -3
  27. {pytest_selective-0.3.0 → pytest_selective-0.4.1}/src/pytest_selective/impact_explanation.py +1 -0
  28. {pytest_selective-0.3.0 → pytest_selective-0.4.1}/src/pytest_selective/impact_models.py +3 -0
  29. pytest_selective-0.4.1/tests/test_checkpoint_policy.py +140 -0
  30. {pytest_selective-0.3.0 → pytest_selective-0.4.1}/tests/test_ci_admission.py +2 -2
  31. {pytest_selective-0.3.0 → pytest_selective-0.4.1}/tests/test_repository_ci.py +1 -1
  32. {pytest_selective-0.3.0 → pytest_selective-0.4.1}/uv.lock +1 -1
  33. pytest_selective-0.3.0/.release-please-manifest.json +0 -3
  34. {pytest_selective-0.3.0 → pytest_selective-0.4.1}/.gitattributes +0 -0
  35. {pytest_selective-0.3.0 → pytest_selective-0.4.1}/.github/ISSUE_TEMPLATE/bug_report.yml +0 -0
  36. {pytest_selective-0.3.0 → pytest_selective-0.4.1}/.github/PULL_REQUEST_TEMPLATE.md +0 -0
  37. {pytest_selective-0.3.0 → pytest_selective-0.4.1}/.github/dependabot.yml +0 -0
  38. {pytest_selective-0.3.0 → pytest_selective-0.4.1}/.github/test-lanes.json +0 -0
  39. {pytest_selective-0.3.0 → pytest_selective-0.4.1}/.github/workflows/docs.yml +0 -0
  40. {pytest_selective-0.3.0 → pytest_selective-0.4.1}/.github/workflows/observe-ci.yml +0 -0
  41. {pytest_selective-0.3.0 → pytest_selective-0.4.1}/.github/workflows/pr-title.yml +0 -0
  42. {pytest_selective-0.3.0 → pytest_selective-0.4.1}/.github/workflows/release-please.yml +0 -0
  43. {pytest_selective-0.3.0 → pytest_selective-0.4.1}/.github/workflows/release.yml +0 -0
  44. {pytest_selective-0.3.0 → pytest_selective-0.4.1}/.github/workflows/windows-performance.yml +0 -0
  45. {pytest_selective-0.3.0 → pytest_selective-0.4.1}/.gitignore +0 -0
  46. {pytest_selective-0.3.0 → pytest_selective-0.4.1}/.pre-commit-config.yaml +0 -0
  47. {pytest_selective-0.3.0 → pytest_selective-0.4.1}/.readthedocs.yaml +0 -0
  48. {pytest_selective-0.3.0 → pytest_selective-0.4.1}/AGENTS.md +0 -0
  49. {pytest_selective-0.3.0 → pytest_selective-0.4.1}/CLAUDE.md +0 -0
  50. {pytest_selective-0.3.0 → pytest_selective-0.4.1}/CONTRIBUTING.md +0 -0
  51. {pytest_selective-0.3.0 → pytest_selective-0.4.1}/LICENSE +0 -0
  52. {pytest_selective-0.3.0 → pytest_selective-0.4.1}/PROVENANCE.md +0 -0
  53. {pytest_selective-0.3.0 → pytest_selective-0.4.1}/RELEASING.md +0 -0
  54. {pytest_selective-0.3.0 → pytest_selective-0.4.1}/actions/README.md +0 -0
  55. {pytest_selective-0.3.0 → pytest_selective-0.4.1}/actions/pytest-expansion/action.yml +0 -0
  56. {pytest_selective-0.3.0 → pytest_selective-0.4.1}/agentsync.toml +0 -0
  57. {pytest_selective-0.3.0 → pytest_selective-0.4.1}/docs/actions.md +0 -0
  58. {pytest_selective-0.3.0 → pytest_selective-0.4.1}/docs/agents.md +0 -0
  59. {pytest_selective-0.3.0 → pytest_selective-0.4.1}/docs/assets/banner.png +0 -0
  60. {pytest_selective-0.3.0 → pytest_selective-0.4.1}/docs/assets/banner.svg +0 -0
  61. {pytest_selective-0.3.0 → pytest_selective-0.4.1}/docs/assets/brand.json +0 -0
  62. {pytest_selective-0.3.0 → pytest_selective-0.4.1}/docs/assets/icon.png +0 -0
  63. {pytest_selective-0.3.0 → pytest_selective-0.4.1}/docs/assets/logo.svg +0 -0
  64. {pytest_selective-0.3.0 → pytest_selective-0.4.1}/docs/assets/raster-sources.json +0 -0
  65. {pytest_selective-0.3.0 → pytest_selective-0.4.1}/docs/assets/social-preview.png +0 -0
  66. {pytest_selective-0.3.0 → pytest_selective-0.4.1}/docs/assets/social-preview.svg +0 -0
  67. {pytest_selective-0.3.0 → pytest_selective-0.4.1}/docs/benchmarks/2026-10-22-due-ci.json +0 -0
  68. {pytest_selective-0.3.0 → pytest_selective-0.4.1}/docs/benchmarks/2026-10-22-due-docs-ci.json +0 -0
  69. {pytest_selective-0.3.0 → pytest_selective-0.4.1}/docs/benchmarks/2026-10-22-due-docs-widget.json +0 -0
  70. {pytest_selective-0.3.0 → pytest_selective-0.4.1}/docs/benchmarks/2026-10-22-due-widget.json +0 -0
  71. {pytest_selective-0.3.0 → pytest_selective-0.4.1}/docs/benchmarks/2026-10-22-input-replays.json +0 -0
  72. {pytest_selective-0.3.0 → pytest_selective-0.4.1}/docs/benchmarks/2026-10-22-obligation-docs.json +0 -0
  73. {pytest_selective-0.3.0 → pytest_selective-0.4.1}/docs/benchmarks/2026-10-22-obligation.json +0 -0
  74. {pytest_selective-0.3.0 → pytest_selective-0.4.1}/docs/benchmarks/2026-10-22-routing.md +0 -0
  75. {pytest_selective-0.3.0 → pytest_selective-0.4.1}/docs/benchmarks/2026-10-22-selective-docs.json +0 -0
  76. {pytest_selective-0.3.0 → pytest_selective-0.4.1}/docs/benchmarks/2026-10-22.md +0 -0
  77. {pytest_selective-0.3.0 → pytest_selective-0.4.1}/docs/benchmarks/2026-10-23-windows-saleor/obligation-baseline.json +0 -0
  78. {pytest_selective-0.3.0 → pytest_selective-0.4.1}/docs/benchmarks/2026-10-23-windows-saleor/selective-after-3.11.xml +0 -0
  79. {pytest_selective-0.3.0 → pytest_selective-0.4.1}/docs/benchmarks/2026-10-23-windows-saleor/selective-after-3.14.xml +0 -0
  80. {pytest_selective-0.3.0 → pytest_selective-0.4.1}/docs/benchmarks/2026-10-23-windows-saleor/selective-after.json +0 -0
  81. {pytest_selective-0.3.0 → pytest_selective-0.4.1}/docs/benchmarks/2026-10-23-windows-saleor/selective-control-3.11.xml +0 -0
  82. {pytest_selective-0.3.0 → pytest_selective-0.4.1}/docs/benchmarks/2026-10-23-windows-saleor/selective-control-3.14.xml +0 -0
  83. {pytest_selective-0.3.0 → pytest_selective-0.4.1}/docs/benchmarks/2026-10-23-windows-saleor/selective-control.json +0 -0
  84. {pytest_selective-0.3.0 → pytest_selective-0.4.1}/docs/benchmarks/2026-10-23-windows-saleor/selective-earlier.json +0 -0
  85. {pytest_selective-0.3.0 → pytest_selective-0.4.1}/docs/benchmarks/2026-10-23-windows-saleor.md +0 -0
  86. {pytest_selective-0.3.0 → pytest_selective-0.4.1}/docs/benchmarks/windows-runtime.md +0 -0
  87. {pytest_selective-0.3.0 → pytest_selective-0.4.1}/docs/comparison-pytest-impacted.md +0 -0
  88. {pytest_selective-0.3.0 → pytest_selective-0.4.1}/docs/evidence.md +0 -0
  89. {pytest_selective-0.3.0 → pytest_selective-0.4.1}/docs/local-development.md +0 -0
  90. {pytest_selective-0.3.0 → pytest_selective-0.4.1}/docs/pytest-hygiene.md +0 -0
  91. {pytest_selective-0.3.0 → pytest_selective-0.4.1}/docs/work-in-progress/tickets/actions-directory-test-plan.md +0 -0
  92. {pytest_selective-0.3.0 → pytest_selective-0.4.1}/docs/work-in-progress/tickets/codex-selector-correctness-performance-test-plan.md +0 -0
  93. {pytest_selective-0.3.0 → pytest_selective-0.4.1}/docs/work-in-progress/tickets/codex-windows-git-performance-test-plan.md +0 -0
  94. {pytest_selective-0.3.0 → pytest_selective-0.4.1}/docs/work-in-progress/tickets/impacted-improvements-test-plan.md +0 -0
  95. {pytest_selective-0.3.0 → pytest_selective-0.4.1}/docs/work-in-progress/tickets/move-action-implementations-test-plan.md +0 -0
  96. {pytest_selective-0.3.0 → pytest_selective-0.4.1}/docs/work-in-progress/tickets/public-action-onboarding-test-plan.md +0 -0
  97. {pytest_selective-0.3.0 → pytest_selective-0.4.1}/documentation/assets/routing-comparison.svg +0 -0
  98. {pytest_selective-0.3.0 → pytest_selective-0.4.1}/documentation/check_site.py +0 -0
  99. {pytest_selective-0.3.0 → pytest_selective-0.4.1}/documentation/ecosystem.md +0 -0
  100. {pytest_selective-0.3.0 → pytest_selective-0.4.1}/documentation/hooks.py +0 -0
  101. {pytest_selective-0.3.0 → pytest_selective-0.4.1}/documentation/index.md +0 -0
  102. {pytest_selective-0.3.0 → pytest_selective-0.4.1}/documentation/overrides/main.html +0 -0
  103. {pytest_selective-0.3.0 → pytest_selective-0.4.1}/documentation/publishing.md +0 -0
  104. {pytest_selective-0.3.0 → pytest_selective-0.4.1}/documentation/requirements.in +0 -0
  105. {pytest_selective-0.3.0 → pytest_selective-0.4.1}/documentation/requirements.txt +0 -0
  106. {pytest_selective-0.3.0 → pytest_selective-0.4.1}/release-please-config.json +0 -0
  107. {pytest_selective-0.3.0 → pytest_selective-0.4.1}/scripts/benchmark_graph.py +0 -0
  108. {pytest_selective-0.3.0 → pytest_selective-0.4.1}/scripts/benchmark_parsing.py +0 -0
  109. {pytest_selective-0.3.0 → pytest_selective-0.4.1}/scripts/benchmark_rust.py +0 -0
  110. {pytest_selective-0.3.0 → pytest_selective-0.4.1}/scripts/check_docs.py +0 -0
  111. {pytest_selective-0.3.0 → pytest_selective-0.4.1}/scripts/check_metadata.py +0 -0
  112. {pytest_selective-0.3.0 → pytest_selective-0.4.1}/scripts/ci_file_config.py +0 -0
  113. {pytest_selective-0.3.0 → pytest_selective-0.4.1}/scripts/compare_impacted.py +0 -0
  114. {pytest_selective-0.3.0 → pytest_selective-0.4.1}/scripts/measure_ci_runs.py +0 -0
  115. {pytest_selective-0.3.0 → pytest_selective-0.4.1}/scripts/public_actions.py +0 -0
  116. {pytest_selective-0.3.0 → pytest_selective-0.4.1}/scripts/render_brand.py +0 -0
  117. {pytest_selective-0.3.0 → pytest_selective-0.4.1}/scripts/run_ci_tests.py +0 -0
  118. {pytest_selective-0.3.0 → pytest_selective-0.4.1}/scripts/smoke_distribution.py +0 -0
  119. {pytest_selective-0.3.0 → pytest_selective-0.4.1}/scripts/tutorial.py +0 -0
  120. {pytest_selective-0.3.0 → pytest_selective-0.4.1}/skills/pytest-selective/agents/openai.yaml +0 -0
  121. {pytest_selective-0.3.0 → pytest_selective-0.4.1}/src/pytest_selective/__init__.py +0 -0
  122. {pytest_selective-0.3.0 → pytest_selective-0.4.1}/src/pytest_selective/__main__.py +0 -0
  123. {pytest_selective-0.3.0 → pytest_selective-0.4.1}/src/pytest_selective/ast_imports.py +0 -0
  124. {pytest_selective-0.3.0 → pytest_selective-0.4.1}/src/pytest_selective/hygiene.py +0 -0
  125. {pytest_selective-0.3.0 → pytest_selective-0.4.1}/src/pytest_selective/impact_audit.py +0 -0
  126. {pytest_selective-0.3.0 → pytest_selective-0.4.1}/src/pytest_selective/impact_audit_plugin.py +0 -0
  127. {pytest_selective-0.3.0 → pytest_selective-0.4.1}/src/pytest_selective/impact_declarations.py +0 -0
  128. {pytest_selective-0.3.0 → pytest_selective-0.4.1}/src/pytest_selective/impact_first_party.py +0 -0
  129. {pytest_selective-0.3.0 → pytest_selective-0.4.1}/src/pytest_selective/impact_graph.py +0 -0
  130. {pytest_selective-0.3.0 → pytest_selective-0.4.1}/src/pytest_selective/impact_name_scope.py +0 -0
  131. {pytest_selective-0.3.0 → pytest_selective-0.4.1}/src/pytest_selective/impact_selection.py +0 -0
  132. {pytest_selective-0.3.0 → pytest_selective-0.4.1}/src/pytest_selective/input_policy.py +0 -0
  133. {pytest_selective-0.3.0 → pytest_selective-0.4.1}/src/pytest_selective/local.py +0 -0
  134. {pytest_selective-0.3.0 → pytest_selective-0.4.1}/src/pytest_selective/parse_batch.py +0 -0
  135. {pytest_selective-0.3.0 → pytest_selective-0.4.1}/src/pytest_selective/plugin.py +0 -0
  136. {pytest_selective-0.3.0 → pytest_selective-0.4.1}/src/pytest_selective/py.typed +0 -0
  137. {pytest_selective-0.3.0 → pytest_selective-0.4.1}/src/pytest_selective/pytest_expansion.py +0 -0
  138. {pytest_selective-0.3.0 → pytest_selective-0.4.1}/src/pytest_selective/pytest_plugins.py +0 -0
  139. {pytest_selective-0.3.0 → pytest_selective-0.4.1}/src/pytest_selective/pytest_sharding.py +0 -0
  140. {pytest_selective-0.3.0 → pytest_selective-0.4.1}/src/pytest_selective/test_inventory.py +0 -0
  141. {pytest_selective-0.3.0 → pytest_selective-0.4.1}/src/pytest_selective/typecheck_scope.py +0 -0
  142. {pytest_selective-0.3.0 → pytest_selective-0.4.1}/src/pytest_selective/working_tree.py +0 -0
  143. {pytest_selective-0.3.0 → pytest_selective-0.4.1}/tests/__init__.py +0 -0
  144. {pytest_selective-0.3.0 → pytest_selective-0.4.1}/tests/test_ci_measurements.py +0 -0
  145. {pytest_selective-0.3.0 → pytest_selective-0.4.1}/tests/test_ci_test_execution.py +0 -0
  146. {pytest_selective-0.3.0 → pytest_selective-0.4.1}/tests/test_distribution_metadata.py +0 -0
  147. {pytest_selective-0.3.0 → pytest_selective-0.4.1}/tests/test_hygiene.py +0 -0
  148. {pytest_selective-0.3.0 → pytest_selective-0.4.1}/tests/test_public_actions.py +0 -0
  149. {pytest_selective-0.3.0 → pytest_selective-0.4.1}/tests/test_pytest_expansion.py +0 -0
  150. {pytest_selective-0.3.0 → pytest_selective-0.4.1}/tests/test_repository_hygiene.py +0 -0
  151. {pytest_selective-0.3.0 → pytest_selective-0.4.1}/tests/test_repository_hygiene.py.gv-dependency.yaml +0 -0
  152. {pytest_selective-0.3.0 → pytest_selective-0.4.1}/tests/test_tutorial.py +0 -0
  153. {pytest_selective-0.3.0 → pytest_selective-0.4.1}/tests/testing/__init__.py +0 -0
  154. {pytest_selective-0.3.0 → pytest_selective-0.4.1}/tests/testing/git_repo.py +0 -0
  155. {pytest_selective-0.3.0 → pytest_selective-0.4.1}/tests/testing/test_ast_imports.py +0 -0
  156. {pytest_selective-0.3.0 → pytest_selective-0.4.1}/tests/testing/test_ci.py +0 -0
  157. {pytest_selective-0.3.0 → pytest_selective-0.4.1}/tests/testing/test_git_repo.py +0 -0
  158. {pytest_selective-0.3.0 → pytest_selective-0.4.1}/tests/testing/test_global_inputs.py +0 -0
  159. {pytest_selective-0.3.0 → pytest_selective-0.4.1}/tests/testing/test_impact_audit.py +0 -0
  160. {pytest_selective-0.3.0 → pytest_selective-0.4.1}/tests/testing/test_impact_explanation.py +0 -0
  161. {pytest_selective-0.3.0 → pytest_selective-0.4.1}/tests/testing/test_impact_first_party.py +0 -0
  162. {pytest_selective-0.3.0 → pytest_selective-0.4.1}/tests/testing/test_impact_graph.py +0 -0
  163. {pytest_selective-0.3.0 → pytest_selective-0.4.1}/tests/testing/test_impact_name_scope.py +0 -0
  164. {pytest_selective-0.3.0 → pytest_selective-0.4.1}/tests/testing/test_parse_batch.py +0 -0
  165. {pytest_selective-0.3.0 → pytest_selective-0.4.1}/tests/testing/test_portability.py +0 -0
  166. {pytest_selective-0.3.0 → pytest_selective-0.4.1}/tests/testing/test_pytest_plugin.py +0 -0
  167. {pytest_selective-0.3.0 → pytest_selective-0.4.1}/tests/testing/test_pytest_plugins.py +0 -0
  168. {pytest_selective-0.3.0 → pytest_selective-0.4.1}/tests/testing/test_pytest_sharding.py +0 -0
  169. {pytest_selective-0.3.0 → pytest_selective-0.4.1}/tests/testing/test_typecheck_scope.py +0 -0
  170. {pytest_selective-0.3.0 → pytest_selective-0.4.1}/tests/testing/test_working_tree.py +0 -0
@@ -18,7 +18,7 @@ jobs:
18
18
  outputs:
19
19
  lanes: ${{ steps.plan.outputs.lanes }}
20
20
  mode: ${{ steps.files.outputs.mode }}
21
- files: ${{ steps.paths.outputs.files }}
21
+ files: ${{ steps.files.outputs.test_roots_json }}
22
22
  steps:
23
23
  - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
24
24
  with:
@@ -27,8 +27,6 @@ jobs:
27
27
  uses: ./actions/plan-lanes
28
28
  with:
29
29
  config: .github/test-lanes.json
30
- base: ${{ github.event.pull_request.base.sha || github.event.before }}
31
- full: ${{ github.run_attempt > 1 || contains(fromJSON('["workflow_dispatch","workflow_call","schedule","release"]'), github.event_name) || contains(github.event.pull_request.labels.*.name, 'test:full') }}
32
30
  - name: Share the repository graph and execution inputs with file selection
33
31
  env:
34
32
  PYTHONPATH: src
@@ -39,21 +37,7 @@ jobs:
39
37
  uses: ./
40
38
  with:
41
39
  config: ${{ runner.temp }}/test-impact.json
42
- base: ${{ github.event.pull_request.base.sha || github.event.before }}
43
40
  full-roots: tests
44
- full: ${{ github.run_attempt > 1 || contains(fromJSON('["workflow_dispatch","workflow_call","schedule","release"]'), github.event_name) || contains(github.event.pull_request.labels.*.name, 'test:full') }}
45
- - id: paths
46
- name: Forward exact selected paths without shell splitting
47
- env:
48
- MODE: ${{ steps.files.outputs.mode }}
49
- run: |
50
- python - <<'PY'
51
- import json, os
52
- from pathlib import Path
53
- files = json.loads(Path('test-impact-report.json').read_text())['selected'] if os.environ['MODE'] == 'scoped' else []
54
- with open(os.environ['GITHUB_OUTPUT'], 'a') as output:
55
- output.write('files=' + json.dumps(files) + '\n')
56
- PY
57
41
  quality:
58
42
  runs-on: ubuntu-latest
59
43
  steps:
@@ -19,8 +19,6 @@ jobs:
19
19
  uses: ./actions/plan-lanes
20
20
  with:
21
21
  config: .github/test-lanes.json
22
- base: ${{ github.event.pull_request.base.sha || github.event.before }}
23
- full: ${{ github.run_attempt > 1 || contains(fromJSON('["workflow_dispatch","workflow_call","schedule","release"]'), github.event_name) || contains(github.event.pull_request.labels.*.name, 'test:full') }}
24
22
  artifact-name: public-action-selection
25
23
  public-actions:
26
24
  needs: select-tests
@@ -0,0 +1,3 @@
1
+ {
2
+ ".": "0.4.1"
3
+ }
@@ -1,5 +1,19 @@
1
1
  # Changelog
2
2
 
3
+ ## [0.4.1](https://github.com/gigaverse-app/pytest-selective/compare/v0.4.0...v0.4.1) (2026-10-24)
4
+
5
+
6
+ ### Bug fixes
7
+
8
+ * **selection:** preserve rerun scope and record planner reasons ([29d1df8](https://github.com/gigaverse-app/pytest-selective/commit/29d1df806816203bad1e7a4a53f38b7e75c1d268))
9
+
10
+ ## [0.4.0](https://github.com/gigaverse-app/pytest-selective/compare/v0.3.0...v0.4.0) (2026-10-24)
11
+
12
+
13
+ ### Features
14
+
15
+ * **actions:** simplify selection adoption with shared defaults ([#63](https://github.com/gigaverse-app/pytest-selective/issues/63)) ([85e082d](https://github.com/gigaverse-app/pytest-selective/commit/85e082dfdbcf2f8c02ac3de38613c818cb749807))
16
+
3
17
  ## [0.3.0](https://github.com/gigaverse-app/pytest-selective/compare/v0.2.0...v0.3.0) (2026-10-23)
4
18
 
5
19
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: pytest-selective
3
- Version: 0.3.0
3
+ Version: 0.4.1
4
4
  Summary: Explainable Python test impact analysis: select affected pytest files from Git changes
5
5
  Project-URL: Homepage, https://github.com/gigaverse-app/pytest-selective
6
6
  Project-URL: Documentation, https://pytest-selective.readthedocs.io/en/latest/
@@ -71,6 +71,9 @@ and slack. [See the source runs and method](https://github.com/gigaverse-app/ci-
71
71
 
72
72
  ## Choose how to use it
73
73
 
74
+ Follow the [adoption tutorial and checklist](docs/tutorials/adoption-checklist.md)
75
+ for local execution, CI routing, dependency audits, timing feedback and measured rollout.
76
+
74
77
  | You want to… | Use | What it saves |
75
78
  | --- | --- | --- |
76
79
  | Test local edits with your existing pytest command | `uv add --dev pytest-selective`, then `uv run pytest --selective` | Unaffected test execution; staged, unstaged and untracked changes are included |
@@ -231,7 +234,7 @@ skip pytest, because invoking pytest without paths runs the whole suite.
231
234
  | A transitive dependency | Import chains and package initialization |
232
235
  | A deleted or renamed module | Dependencies in both the base and head trees |
233
236
  | Fixtures or startup code | Ancestor conftests and configured global modules |
234
- | Templates, schemas, or data files | Reader-owned declarations and YAML sidecars |
237
+ | Templates, schemas, or data files | [Reader-owned declarations and YAML sidecars](docs/tutorials/sidecar-dependencies.md) |
235
238
  | A surprisingly large selection | Dependency paths, startup-floor and blast-radius explanations |
236
239
  | A large CI suite | Cached parsing, graph reuse, and duration-based shard planning |
237
240
  | Undeclared runtime reads | An opt-in pytest recorder and audit command |
@@ -40,6 +40,9 @@ and slack. [See the source runs and method](https://github.com/gigaverse-app/ci-
40
40
 
41
41
  ## Choose how to use it
42
42
 
43
+ Follow the [adoption tutorial and checklist](docs/tutorials/adoption-checklist.md)
44
+ for local execution, CI routing, dependency audits, timing feedback and measured rollout.
45
+
43
46
  | You want to… | Use | What it saves |
44
47
  | --- | --- | --- |
45
48
  | Test local edits with your existing pytest command | `uv add --dev pytest-selective`, then `uv run pytest --selective` | Unaffected test execution; staged, unstaged and untracked changes are included |
@@ -200,7 +203,7 @@ skip pytest, because invoking pytest without paths runs the whole suite.
200
203
  | A transitive dependency | Import chains and package initialization |
201
204
  | A deleted or renamed module | Dependencies in both the base and head trees |
202
205
  | Fixtures or startup code | Ancestor conftests and configured global modules |
203
- | Templates, schemas, or data files | Reader-owned declarations and YAML sidecars |
206
+ | Templates, schemas, or data files | [Reader-owned declarations and YAML sidecars](docs/tutorials/sidecar-dependencies.md) |
204
207
  | A surprisingly large selection | Dependency paths, startup-floor and blast-radius explanations |
205
208
  | A large CI suite | Cached parsing, graph reuse, and duration-based shard planning |
206
209
  | Undeclared runtime reads | An opt-in pytest recorder and audit command |
@@ -10,7 +10,7 @@ inputs:
10
10
  required: true
11
11
  base:
12
12
  description: Complete change-set base (a validated ancestor, PR base, or push before SHA).
13
- required: true
13
+ default: ${{ github.event.pull_request.base.sha || github.event.before }}
14
14
  head:
15
15
  description: Exact checked-out commit.
16
16
  default: HEAD
@@ -18,8 +18,9 @@ inputs:
18
18
  description: Space-separated complete test roots used if planning cannot prove a narrower scope.
19
19
  required: true
20
20
  full:
21
- description: Force the complete suite for schedules, explicit labels, or coverage checkpoints.
22
- default: 'false'
21
+ description: auto preserves selection on PR/push reruns and runs full for manual/scheduled/release checkpoints or test:full.
22
+ Override with true or false.
23
+ default: auto
23
24
  extra-files:
24
25
  description: Optional newline file containing prior failure canaries or additional changed paths.
25
26
  default: ''
@@ -58,6 +59,10 @@ inputs:
58
59
  with `head`, so they stay visible after `base` moves to a validated tree. Without it, or without the history, they compare
59
60
  against `base`.
60
61
  default: ${{ github.event.pull_request.base.sha }}
62
+ explanation-format:
63
+ description: 1 preserves the legacy explanation schema; 2 includes the actual planner reasons. Use 2 with a compatible
64
+ reporter.
65
+ default: '1'
61
66
  outputs:
62
67
  base:
63
68
  description: The base the selection was made from; `alternate-base` when it selected fewer tests.
@@ -68,6 +73,9 @@ outputs:
68
73
  test_roots:
69
74
  description: Exact selected files, or complete roots on fallback.
70
75
  value: ${{ steps.action.outputs.test_roots }}
76
+ test_roots_json:
77
+ description: JSON array of selected files or fallback roots; preserves spaces in paths. Empty array for none.
78
+ value: ${{ steps.action.outputs.test_roots_json }}
71
79
  shard_count:
72
80
  description: Number of shards.
73
81
  value: ${{ steps.action.outputs.shard_count }}
@@ -104,3 +112,4 @@ runs:
104
112
  dependency-token: ${{ inputs.dependency-token }}
105
113
  explanation-artifact: ${{ inputs.explanation-artifact }}
106
114
  graph-base: ${{ inputs.graph-base }}
115
+ explanation-format: ${{ inputs.explanation-format }}
@@ -6,10 +6,10 @@ inputs:
6
6
  required: true
7
7
  base:
8
8
  description: Base commit containing the complete change set.
9
- required: true
9
+ default: ${{ github.event.pull_request.base.sha || github.event.before }}
10
10
  full:
11
- description: Run every lane for an explicit full-test checkpoint; ordinary PRs and pushes use selection.
12
- default: 'false'
11
+ description: 'auto preserves selection on PR/push reruns and runs full for manual/scheduled/release checkpoints or test:full. Override with true or false.'
12
+ default: auto
13
13
  artifact-name:
14
14
  description: >
15
15
  Upload the lane plan under this name, where CI-esta's report renders it as
@@ -41,7 +41,7 @@ runs:
41
41
  run: |
42
42
  export PYTHONPATH="$SELECTIVE_ACTION/../../src${PYTHONPATH:+:$PYTHONPATH}"
43
43
  args=(--config "$SELECTIVE_CONFIG" --base "$SELECTIVE_BASE" --github-output "$GITHUB_OUTPUT")
44
- if [ "$SELECTIVE_FULL" = true ]; then args+=(--full); fi
44
+ args+=(--full "$SELECTIVE_FULL")
45
45
  uv run --no-project --python 3.14 --with pydantic==2.13.4 --with pyyaml==6.0.3 \
46
46
  python -m pytest_selective.ci "${args[@]}" | tee test-lanes.json
47
47
  {
@@ -9,7 +9,7 @@ inputs:
9
9
  required: true
10
10
  base:
11
11
  description: Complete change-set base (a validated ancestor, PR base, or push before SHA).
12
- required: true
12
+ default: ${{ github.event.pull_request.base.sha || github.event.before }}
13
13
  head:
14
14
  description: Exact checked-out commit.
15
15
  default: HEAD
@@ -17,8 +17,8 @@ inputs:
17
17
  description: Space-separated complete test roots used if planning cannot prove a narrower scope.
18
18
  required: true
19
19
  full:
20
- description: Force the complete suite for schedules, explicit labels, or coverage checkpoints.
21
- default: 'false'
20
+ description: 'auto preserves selection on PR/push reruns and runs full for manual/scheduled/release checkpoints or test:full. Override with true or false.'
21
+ default: auto
22
22
  extra-files:
23
23
  description: Optional newline file containing prior failure canaries or additional changed paths.
24
24
  default: ''
@@ -60,6 +60,9 @@ inputs:
60
60
  its merge base with `head`, so they stay visible after `base` moves to a validated tree.
61
61
  Without it, or without the history, they compare against `base`.
62
62
  default: ${{ github.event.pull_request.base.sha }}
63
+ explanation-format:
64
+ description: '1 preserves the legacy explanation schema; 2 includes the actual planner reasons. Use 2 with a compatible reporter.'
65
+ default: '1'
63
66
  outputs:
64
67
  base:
65
68
  value: ${{ steps.select.outputs.base }}
@@ -70,6 +73,9 @@ outputs:
70
73
  test_roots:
71
74
  value: ${{ steps.select.outputs.test_roots }}
72
75
  description: Exact selected files, or complete roots on fallback.
76
+ test_roots_json:
77
+ value: ${{ steps.select.outputs.test_roots_json }}
78
+ description: JSON array of selected files or fallback roots; preserves spaces in paths. Empty array for none.
73
79
  shard_count:
74
80
  value: ${{ steps.select.outputs.shard_count }}
75
81
  description: Number of shards.
@@ -120,6 +126,7 @@ runs:
120
126
  # Environment only: the CLI never takes it on argv, and git gets it per process, not in config.
121
127
  IMPACT_DEPENDENCY_TOKEN: ${{ inputs.dependency-token }}
122
128
  IMPACT_GRAPH_BASE: ${{ inputs.graph-base }}
129
+ IMPACT_EXPLANATION_FORMAT: ${{ inputs.explanation-format }}
123
130
  SHARED_ACTION_PATH: ${{ github.action_path }}
124
131
  run: |
125
132
  export PYTHONPATH="$SHARED_ACTION_PATH/../../src${PYTHONPATH:+:$PYTHONPATH}"
@@ -136,10 +143,10 @@ runs:
136
143
  args=(affected --config "$IMPACT_CONFIG" --base "$IMPACT_BASE" --head "$IMPACT_HEAD"
137
144
  --max-shards "$IMPACT_SHARDS" --minimum-files "$IMPACT_MINIMUM" --github-output "$GITHUB_OUTPUT" --output test-impact-report.json
138
145
  --durations "$IMPACT_DURATIONS" --shard-seconds "$IMPACT_SHARD_SECONDS"
139
- --explanation-output test-impact-explanation.json)
146
+ --explanation-output test-impact-explanation.json --explanation-format "${IMPACT_EXPLANATION_FORMAT:-1}")
140
147
  # A reused workspace must not upload a previous plan's explanation.
141
148
  rm -f test-impact-explanation.json
142
- if [ "$IMPACT_FULL" = true ]; then args+=(--full); fi
149
+ args+=(--full "$IMPACT_FULL")
143
150
  if [ -n "$IMPACT_EXTRA" ]; then args+=(--extra-files "$IMPACT_EXTRA"); fi
144
151
  if [ -n "${IMPACT_GRAPH_BASE:-}" ]; then args+=(--graph-base "$IMPACT_GRAPH_BASE"); fi
145
152
  # Optional: an unfetchable alternate only forgoes a smaller selection.
@@ -170,6 +177,7 @@ runs:
170
177
  echo "base=$IMPACT_BASE"
171
178
  echo "mode=full"
172
179
  echo "test_roots=$IMPACT_ROOTS"
180
+ echo "test_roots_json=$(python3 -c 'import json,os,shlex; print(json.dumps(shlex.split(os.environ["IMPACT_ROOTS"])))')"
173
181
  echo "shard_count=$IMPACT_SHARDS"
174
182
  echo "groups=$(python3 -c 'import json,os; print(json.dumps(list(range(1,int(os.environ["IMPACT_SHARDS"])+1))))')"
175
183
  echo "reason=Test impact proof unavailable; complete suite fallback"
@@ -4,6 +4,7 @@
4
4
  | --- | --- |
5
5
  | Have a coding agent optimize your test/CI workflow | [Agent prompt, skill and AGENTS.md instructions](agents.md) |
6
6
  | See a real regression caught by a smaller selection | [Runnable tutorial](tutorial.md) |
7
+ | Declare fixture, schema, scanner, or subprocess inputs | [Sidecar tutorial](tutorials/sidecar-dependencies.md) |
7
8
  | Configure an existing Python project | [Configuration and dependency declarations](configuration.md) |
8
9
  | Use `pytest --selective` or run local edits before collection | [Native plugin and local runner](local-development.md) |
9
10
  | Run the selected files safely in CI | [GitHub Actions and runner integration](ci.md) |
@@ -60,8 +60,9 @@ benchmark pairs can be declared with exact run/attempt/head references through
60
60
  Never turn the fraction of files or lanes skipped into a measured time saving.
61
61
 
62
62
  Ordinary label edits no longer launch the CI validation workflows. Add `test:full`
63
- before the next code update to force full validation, or use an explicit rerun
64
- or manual dispatch for immediate full execution. Ordinary PRs and main pushes
63
+ before the next code update to force full validation, or use a manual dispatch
64
+ for immediate full execution. A PR/push rerun preserves selection and uses the
65
+ original event payload; adding a label afterward may not change that payload. Ordinary PRs and main pushes
65
66
  use dependency selection, including for contract and installed-package tests.
66
67
  Unreferenced documentation runs zero tests; explicit full checkpoints and
67
68
  failed-planning fallbacks retain complete coverage and required gates.
@@ -4,12 +4,26 @@ Plan on committed revisions with complete Git history and a known passing base.
4
4
  Keep a scheduled full-suite run. Do not drop unresolved failures merely by moving
5
5
  the base forward after a failed run.
6
6
 
7
+ PR and push reruns preserve the selection policy for the same base, head and
8
+ inputs. Rerunning does not request the full suite. Use `full: 'true'`, a
9
+ `test:full` label present in the event, or a scheduled/manual/release checkpoint
10
+ for full validation. `full: 'false'` overrides event defaults, while missing
11
+ history and operational uncertainty still retain conservative execution.
12
+
13
+ The planner records its actual reasons once and carries them into its report.
14
+ The CLI explanation format 2 includes `selection_reasons`; `--explanation-format 1`
15
+ omits that field for older strict readers. The public `actions/test-impact@v1`
16
+ action defaults to format 1 for compatibility. A compatible reporter can request
17
+ `explanation-format: '2'`; CI-esta's wrapper does this automatically. Older
18
+ artifacts cannot identify a trigger they never recorded, so readers must say
19
+ that the reason is unavailable rather than infer it from the attempt number.
20
+
7
21
  ## GitHub Actions
8
22
 
9
23
  This job uses the repository's composite action and runs the plan on the checked-out
10
24
  head. Use the moving `v1` release tag and adapt dependency installation to
11
- your project. The example assumes a uv project
12
- and test paths without spaces (the action's `test_roots` output is space-separated).
25
+ your project. The example assumes a uv project and passes the planned paths as
26
+ JSON, so spaces in filenames survive execution.
13
27
 
14
28
  ```yaml
15
29
  name: Affected tests
@@ -28,26 +42,44 @@ jobs:
28
42
  uses: gigaverse-app/pytest-selective@v1
29
43
  with:
30
44
  config: .github/test-impact.json
31
- base: ${{ github.event.pull_request.base.sha }}
32
45
  full-roots: tests
33
46
  - if: steps.impact.outputs.mode != 'none'
34
47
  run: uv sync --locked
35
48
  - if: steps.impact.outputs.mode != 'none'
36
49
  env:
37
- TEST_ROOTS: ${{ steps.impact.outputs.test_roots }}
50
+ TEST_ROOTS: ${{ steps.impact.outputs.test_roots_json }}
38
51
  shell: bash
39
52
  run: |
40
- read -r -a roots <<< "$TEST_ROOTS"
41
- if [ "${#roots[@]}" -eq 0 ]; then
42
- echo "Expected test roots for a nonempty plan" >&2
43
- exit 1
44
- fi
45
- uv run --locked pytest "${roots[@]}"
53
+ uv run --locked python - <<'PY'
54
+ import json, os, subprocess, sys
55
+ roots = json.loads(os.environ['TEST_ROOTS'])
56
+ if not roots:
57
+ raise SystemExit('Expected test roots for a nonempty plan')
58
+ raise SystemExit(subprocess.call([sys.executable, '-m', 'pytest', '-p', 'no:selective', *roots]))
59
+ PY
46
60
  ```
47
61
 
48
62
  The action creates its own Python 3.14 planner environment. Your uv project
49
63
  controls the test interpreter. `full-roots` must contain the complete roots, not
50
- the previous selection. With `full: 'true'`, the action requests a complete run.
64
+ the previous selection. `base` defaults to the PR base or the push's previous
65
+ commit. Override it only when you have validated a different comparison base.
66
+
67
+ Both file and lane actions default to `full: auto`:
68
+
69
+ | Trigger | Default |
70
+ | --- | --- |
71
+ | First ordinary PR or push attempt | Select affected work |
72
+ | Rerun, manual, schedule or release | Full checkpoint |
73
+ | PR carrying `test:full` | Full checkpoint |
74
+ | Unknown event or unreadable required event metadata | Full checkpoint |
75
+
76
+ Use `full: 'true'` to always run everything, or `full: 'false'` to request
77
+ selection even for a manual experiment. Operational uncertainty still falls back
78
+ to full execution. Existing explicit expressions keep their behavior; remove a
79
+ copied standard expression to adopt the shared policy. Updating an action does
80
+ not add missing jobs, schedules, permissions or test dependency declarations.
81
+ GitHub reusable workflows retain the caller's event, so pass `full: 'true'` when
82
+ the reusable invocation itself must be a checkpoint.
51
83
 
52
84
  | Planner outcome | Action behavior |
53
85
  | --- | --- |
@@ -193,8 +225,8 @@ when the README is embedded in the built package.
193
225
  The action returns a `lanes` JSON object. Expose it as a planning-job output,
194
226
  then gate each expensive job with `always() && !cancelled()` and either a true
195
227
  lane value **or a failed planning job**. This preserves tests if planning fails.
196
- Run the action with `full: true` for manual/release checkpoints, schedules, and
197
- full-test overrides. Ordinary PRs and main pushes use dependency selection.
228
+ The default event policy handles manual/release checkpoints, schedules and
229
+ `test:full`. Ordinary PRs and main pushes use dependency selection.
198
230
  Selected jobs retain all of their existing tests and evidence checks.
199
231
 
200
232
 
@@ -54,6 +54,9 @@ runs the complete configured roots.
54
54
 
55
55
  ## Declare inputs that imports cannot describe
56
56
 
57
+ Start with the [sidecar tutorial](tutorials/sidecar-dependencies.md) for a runnable
58
+ fixture example, directory manifests, dependency comments, and dynamic Python inputs.
59
+
57
60
  For a test that reads a tracked JSON fixture, add a reader-owned comment:
58
61
 
59
62
  ```python
@@ -30,7 +30,8 @@ uv run pytest-selective audit --config .github/test-impact.json --records impact
30
30
  The recorder observes real file reads in the tests you execute. The audit can
31
31
  expose a JSON fixture, template or schema that the graph did not declare. Add an
32
32
  edge at the reader using a dependency comment or YAML sidecar, then recheck the
33
- route. [Declaration syntax](configuration.md#declare-inputs-that-imports-cannot-describe).
33
+ route. [Work through the sidecar tutorial](tutorials/sidecar-dependencies.md), or consult
34
+ [declaration syntax](configuration.md#declare-inputs-that-imports-cannot-describe).
34
35
  The audit covers observed paths; keep representative full runs for paths it has
35
36
  not seen, and explicitly model dynamic imports and external inputs.
36
37
 
@@ -1,5 +1,8 @@
1
1
  # Start using pytest-selective
2
2
 
3
+ Use the [adoption checklist](tutorials/adoption-checklist.md) to work through
4
+ configuration, CI integration, selection health and measurement after this quickstart.
5
+
3
6
  ## Try your existing project
4
7
 
5
8
  Start in a Git repository whose full pytest suite passes. You need Python 3.11+,