pytest-selective 0.4.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.
- pytest_selective-0.4.1/.release-please-manifest.json +3 -0
- {pytest_selective-0.4.0 → pytest_selective-0.4.1}/CHANGELOG.md +7 -0
- {pytest_selective-0.4.0 → pytest_selective-0.4.1}/PKG-INFO +1 -1
- {pytest_selective-0.4.0 → pytest_selective-0.4.1}/action.yml +7 -2
- {pytest_selective-0.4.0 → pytest_selective-0.4.1}/actions/plan-lanes/action.yml +1 -1
- {pytest_selective-0.4.0 → pytest_selective-0.4.1}/actions/test-impact/action.yml +6 -2
- {pytest_selective-0.4.0 → pytest_selective-0.4.1}/docs/ci-toolkit.md +3 -2
- {pytest_selective-0.4.0 → pytest_selective-0.4.1}/docs/ci.md +14 -0
- {pytest_selective-0.4.0 → pytest_selective-0.4.1}/docs/tutorials/adoption-checklist.md +1 -1
- {pytest_selective-0.4.0 → pytest_selective-0.4.1}/docs/tutorials/skip-unrelated-ci.md +5 -4
- pytest_selective-0.4.1/docs/work-in-progress/tickets/rerun-selection-test-plan.md +7 -0
- {pytest_selective-0.4.0 → pytest_selective-0.4.1}/pyproject.toml +1 -1
- {pytest_selective-0.4.0 → pytest_selective-0.4.1}/skills/pytest-selective/SKILL.md +2 -1
- {pytest_selective-0.4.0 → pytest_selective-0.4.1}/skills/pytest-selective/references/adoption.md +2 -2
- {pytest_selective-0.4.0 → pytest_selective-0.4.1}/src/pytest_selective/checkpoint.py +20 -9
- {pytest_selective-0.4.0 → pytest_selective-0.4.1}/src/pytest_selective/ci.py +7 -3
- {pytest_selective-0.4.0 → pytest_selective-0.4.1}/src/pytest_selective/impact_cli.py +15 -4
- {pytest_selective-0.4.0 → pytest_selective-0.4.1}/src/pytest_selective/impact_explanation.py +1 -0
- {pytest_selective-0.4.0 → pytest_selective-0.4.1}/src/pytest_selective/impact_models.py +3 -0
- {pytest_selective-0.4.0 → pytest_selective-0.4.1}/tests/test_checkpoint_policy.py +38 -4
- {pytest_selective-0.4.0 → pytest_selective-0.4.1}/uv.lock +1 -1
- pytest_selective-0.4.0/.release-please-manifest.json +0 -3
- {pytest_selective-0.4.0 → pytest_selective-0.4.1}/.gitattributes +0 -0
- {pytest_selective-0.4.0 → pytest_selective-0.4.1}/.github/ISSUE_TEMPLATE/bug_report.yml +0 -0
- {pytest_selective-0.4.0 → pytest_selective-0.4.1}/.github/PULL_REQUEST_TEMPLATE.md +0 -0
- {pytest_selective-0.4.0 → pytest_selective-0.4.1}/.github/dependabot.yml +0 -0
- {pytest_selective-0.4.0 → pytest_selective-0.4.1}/.github/test-lanes.json +0 -0
- {pytest_selective-0.4.0 → pytest_selective-0.4.1}/.github/workflows/ci.yml +0 -0
- {pytest_selective-0.4.0 → pytest_selective-0.4.1}/.github/workflows/docs.yml +0 -0
- {pytest_selective-0.4.0 → pytest_selective-0.4.1}/.github/workflows/observe-ci.yml +0 -0
- {pytest_selective-0.4.0 → pytest_selective-0.4.1}/.github/workflows/pr-title.yml +0 -0
- {pytest_selective-0.4.0 → pytest_selective-0.4.1}/.github/workflows/public-actions.yml +0 -0
- {pytest_selective-0.4.0 → pytest_selective-0.4.1}/.github/workflows/release-please.yml +0 -0
- {pytest_selective-0.4.0 → pytest_selective-0.4.1}/.github/workflows/release.yml +0 -0
- {pytest_selective-0.4.0 → pytest_selective-0.4.1}/.github/workflows/windows-performance.yml +0 -0
- {pytest_selective-0.4.0 → pytest_selective-0.4.1}/.gitignore +0 -0
- {pytest_selective-0.4.0 → pytest_selective-0.4.1}/.pre-commit-config.yaml +0 -0
- {pytest_selective-0.4.0 → pytest_selective-0.4.1}/.readthedocs.yaml +0 -0
- {pytest_selective-0.4.0 → pytest_selective-0.4.1}/AGENTS.md +0 -0
- {pytest_selective-0.4.0 → pytest_selective-0.4.1}/CLAUDE.md +0 -0
- {pytest_selective-0.4.0 → pytest_selective-0.4.1}/CONTRIBUTING.md +0 -0
- {pytest_selective-0.4.0 → pytest_selective-0.4.1}/LICENSE +0 -0
- {pytest_selective-0.4.0 → pytest_selective-0.4.1}/PROVENANCE.md +0 -0
- {pytest_selective-0.4.0 → pytest_selective-0.4.1}/README.md +0 -0
- {pytest_selective-0.4.0 → pytest_selective-0.4.1}/RELEASING.md +0 -0
- {pytest_selective-0.4.0 → pytest_selective-0.4.1}/actions/README.md +0 -0
- {pytest_selective-0.4.0 → pytest_selective-0.4.1}/actions/pytest-expansion/action.yml +0 -0
- {pytest_selective-0.4.0 → pytest_selective-0.4.1}/agentsync.toml +0 -0
- {pytest_selective-0.4.0 → pytest_selective-0.4.1}/docs/README.md +0 -0
- {pytest_selective-0.4.0 → pytest_selective-0.4.1}/docs/actions.md +0 -0
- {pytest_selective-0.4.0 → pytest_selective-0.4.1}/docs/agents.md +0 -0
- {pytest_selective-0.4.0 → pytest_selective-0.4.1}/docs/assets/banner.png +0 -0
- {pytest_selective-0.4.0 → pytest_selective-0.4.1}/docs/assets/banner.svg +0 -0
- {pytest_selective-0.4.0 → pytest_selective-0.4.1}/docs/assets/brand.json +0 -0
- {pytest_selective-0.4.0 → pytest_selective-0.4.1}/docs/assets/icon.png +0 -0
- {pytest_selective-0.4.0 → pytest_selective-0.4.1}/docs/assets/logo.svg +0 -0
- {pytest_selective-0.4.0 → pytest_selective-0.4.1}/docs/assets/raster-sources.json +0 -0
- {pytest_selective-0.4.0 → pytest_selective-0.4.1}/docs/assets/social-preview.png +0 -0
- {pytest_selective-0.4.0 → pytest_selective-0.4.1}/docs/assets/social-preview.svg +0 -0
- {pytest_selective-0.4.0 → pytest_selective-0.4.1}/docs/benchmarks/2026-10-22-due-ci.json +0 -0
- {pytest_selective-0.4.0 → pytest_selective-0.4.1}/docs/benchmarks/2026-10-22-due-docs-ci.json +0 -0
- {pytest_selective-0.4.0 → pytest_selective-0.4.1}/docs/benchmarks/2026-10-22-due-docs-widget.json +0 -0
- {pytest_selective-0.4.0 → pytest_selective-0.4.1}/docs/benchmarks/2026-10-22-due-widget.json +0 -0
- {pytest_selective-0.4.0 → pytest_selective-0.4.1}/docs/benchmarks/2026-10-22-input-replays.json +0 -0
- {pytest_selective-0.4.0 → pytest_selective-0.4.1}/docs/benchmarks/2026-10-22-obligation-docs.json +0 -0
- {pytest_selective-0.4.0 → pytest_selective-0.4.1}/docs/benchmarks/2026-10-22-obligation.json +0 -0
- {pytest_selective-0.4.0 → pytest_selective-0.4.1}/docs/benchmarks/2026-10-22-routing.md +0 -0
- {pytest_selective-0.4.0 → pytest_selective-0.4.1}/docs/benchmarks/2026-10-22-selective-docs.json +0 -0
- {pytest_selective-0.4.0 → pytest_selective-0.4.1}/docs/benchmarks/2026-10-22.md +0 -0
- {pytest_selective-0.4.0 → pytest_selective-0.4.1}/docs/benchmarks/2026-10-23-windows-saleor/obligation-baseline.json +0 -0
- {pytest_selective-0.4.0 → pytest_selective-0.4.1}/docs/benchmarks/2026-10-23-windows-saleor/selective-after-3.11.xml +0 -0
- {pytest_selective-0.4.0 → pytest_selective-0.4.1}/docs/benchmarks/2026-10-23-windows-saleor/selective-after-3.14.xml +0 -0
- {pytest_selective-0.4.0 → pytest_selective-0.4.1}/docs/benchmarks/2026-10-23-windows-saleor/selective-after.json +0 -0
- {pytest_selective-0.4.0 → pytest_selective-0.4.1}/docs/benchmarks/2026-10-23-windows-saleor/selective-control-3.11.xml +0 -0
- {pytest_selective-0.4.0 → pytest_selective-0.4.1}/docs/benchmarks/2026-10-23-windows-saleor/selective-control-3.14.xml +0 -0
- {pytest_selective-0.4.0 → pytest_selective-0.4.1}/docs/benchmarks/2026-10-23-windows-saleor/selective-control.json +0 -0
- {pytest_selective-0.4.0 → pytest_selective-0.4.1}/docs/benchmarks/2026-10-23-windows-saleor/selective-earlier.json +0 -0
- {pytest_selective-0.4.0 → pytest_selective-0.4.1}/docs/benchmarks/2026-10-23-windows-saleor.md +0 -0
- {pytest_selective-0.4.0 → pytest_selective-0.4.1}/docs/benchmarks/windows-runtime.md +0 -0
- {pytest_selective-0.4.0 → pytest_selective-0.4.1}/docs/comparison-pytest-impacted.md +0 -0
- {pytest_selective-0.4.0 → pytest_selective-0.4.1}/docs/configuration.md +0 -0
- {pytest_selective-0.4.0 → pytest_selective-0.4.1}/docs/evidence.md +0 -0
- {pytest_selective-0.4.0 → pytest_selective-0.4.1}/docs/local-development.md +0 -0
- {pytest_selective-0.4.0 → pytest_selective-0.4.1}/docs/pytest-hygiene.md +0 -0
- {pytest_selective-0.4.0 → pytest_selective-0.4.1}/docs/selection-health.md +0 -0
- {pytest_selective-0.4.0 → pytest_selective-0.4.1}/docs/tutorial.md +0 -0
- {pytest_selective-0.4.0 → pytest_selective-0.4.1}/docs/tutorials/sidecar-dependencies.md +0 -0
- {pytest_selective-0.4.0 → pytest_selective-0.4.1}/docs/work-in-progress/tickets/actions-directory-test-plan.md +0 -0
- {pytest_selective-0.4.0 → pytest_selective-0.4.1}/docs/work-in-progress/tickets/codex-selector-correctness-performance-test-plan.md +0 -0
- {pytest_selective-0.4.0 → pytest_selective-0.4.1}/docs/work-in-progress/tickets/codex-windows-git-performance-test-plan.md +0 -0
- {pytest_selective-0.4.0 → pytest_selective-0.4.1}/docs/work-in-progress/tickets/impacted-improvements-test-plan.md +0 -0
- {pytest_selective-0.4.0 → pytest_selective-0.4.1}/docs/work-in-progress/tickets/move-action-implementations-test-plan.md +0 -0
- {pytest_selective-0.4.0 → pytest_selective-0.4.1}/docs/work-in-progress/tickets/public-action-onboarding-test-plan.md +0 -0
- {pytest_selective-0.4.0 → pytest_selective-0.4.1}/documentation/assets/routing-comparison.svg +0 -0
- {pytest_selective-0.4.0 → pytest_selective-0.4.1}/documentation/check_site.py +0 -0
- {pytest_selective-0.4.0 → pytest_selective-0.4.1}/documentation/ecosystem.md +0 -0
- {pytest_selective-0.4.0 → pytest_selective-0.4.1}/documentation/hooks.py +0 -0
- {pytest_selective-0.4.0 → pytest_selective-0.4.1}/documentation/index.md +0 -0
- {pytest_selective-0.4.0 → pytest_selective-0.4.1}/documentation/overrides/main.html +0 -0
- {pytest_selective-0.4.0 → pytest_selective-0.4.1}/documentation/publishing.md +0 -0
- {pytest_selective-0.4.0 → pytest_selective-0.4.1}/documentation/requirements.in +0 -0
- {pytest_selective-0.4.0 → pytest_selective-0.4.1}/documentation/requirements.txt +0 -0
- {pytest_selective-0.4.0 → pytest_selective-0.4.1}/mkdocs.yml +0 -0
- {pytest_selective-0.4.0 → pytest_selective-0.4.1}/release-please-config.json +0 -0
- {pytest_selective-0.4.0 → pytest_selective-0.4.1}/scripts/benchmark_graph.py +0 -0
- {pytest_selective-0.4.0 → pytest_selective-0.4.1}/scripts/benchmark_parsing.py +0 -0
- {pytest_selective-0.4.0 → pytest_selective-0.4.1}/scripts/benchmark_rust.py +0 -0
- {pytest_selective-0.4.0 → pytest_selective-0.4.1}/scripts/check_docs.py +0 -0
- {pytest_selective-0.4.0 → pytest_selective-0.4.1}/scripts/check_metadata.py +0 -0
- {pytest_selective-0.4.0 → pytest_selective-0.4.1}/scripts/ci_file_config.py +0 -0
- {pytest_selective-0.4.0 → pytest_selective-0.4.1}/scripts/compare_impacted.py +0 -0
- {pytest_selective-0.4.0 → pytest_selective-0.4.1}/scripts/measure_ci_runs.py +0 -0
- {pytest_selective-0.4.0 → pytest_selective-0.4.1}/scripts/public_actions.py +0 -0
- {pytest_selective-0.4.0 → pytest_selective-0.4.1}/scripts/render_brand.py +0 -0
- {pytest_selective-0.4.0 → pytest_selective-0.4.1}/scripts/run_ci_tests.py +0 -0
- {pytest_selective-0.4.0 → pytest_selective-0.4.1}/scripts/smoke_distribution.py +0 -0
- {pytest_selective-0.4.0 → pytest_selective-0.4.1}/scripts/tutorial.py +0 -0
- {pytest_selective-0.4.0 → pytest_selective-0.4.1}/skills/pytest-selective/agents/openai.yaml +0 -0
- {pytest_selective-0.4.0 → pytest_selective-0.4.1}/src/pytest_selective/__init__.py +0 -0
- {pytest_selective-0.4.0 → pytest_selective-0.4.1}/src/pytest_selective/__main__.py +0 -0
- {pytest_selective-0.4.0 → pytest_selective-0.4.1}/src/pytest_selective/ast_imports.py +0 -0
- {pytest_selective-0.4.0 → pytest_selective-0.4.1}/src/pytest_selective/hygiene.py +0 -0
- {pytest_selective-0.4.0 → pytest_selective-0.4.1}/src/pytest_selective/impact_audit.py +0 -0
- {pytest_selective-0.4.0 → pytest_selective-0.4.1}/src/pytest_selective/impact_audit_plugin.py +0 -0
- {pytest_selective-0.4.0 → pytest_selective-0.4.1}/src/pytest_selective/impact_declarations.py +0 -0
- {pytest_selective-0.4.0 → pytest_selective-0.4.1}/src/pytest_selective/impact_first_party.py +0 -0
- {pytest_selective-0.4.0 → pytest_selective-0.4.1}/src/pytest_selective/impact_graph.py +0 -0
- {pytest_selective-0.4.0 → pytest_selective-0.4.1}/src/pytest_selective/impact_name_scope.py +0 -0
- {pytest_selective-0.4.0 → pytest_selective-0.4.1}/src/pytest_selective/impact_selection.py +0 -0
- {pytest_selective-0.4.0 → pytest_selective-0.4.1}/src/pytest_selective/input_policy.py +0 -0
- {pytest_selective-0.4.0 → pytest_selective-0.4.1}/src/pytest_selective/local.py +0 -0
- {pytest_selective-0.4.0 → pytest_selective-0.4.1}/src/pytest_selective/parse_batch.py +0 -0
- {pytest_selective-0.4.0 → pytest_selective-0.4.1}/src/pytest_selective/plugin.py +0 -0
- {pytest_selective-0.4.0 → pytest_selective-0.4.1}/src/pytest_selective/py.typed +0 -0
- {pytest_selective-0.4.0 → pytest_selective-0.4.1}/src/pytest_selective/pytest_expansion.py +0 -0
- {pytest_selective-0.4.0 → pytest_selective-0.4.1}/src/pytest_selective/pytest_plugins.py +0 -0
- {pytest_selective-0.4.0 → pytest_selective-0.4.1}/src/pytest_selective/pytest_sharding.py +0 -0
- {pytest_selective-0.4.0 → pytest_selective-0.4.1}/src/pytest_selective/test_inventory.py +0 -0
- {pytest_selective-0.4.0 → pytest_selective-0.4.1}/src/pytest_selective/typecheck_scope.py +0 -0
- {pytest_selective-0.4.0 → pytest_selective-0.4.1}/src/pytest_selective/working_tree.py +0 -0
- {pytest_selective-0.4.0 → pytest_selective-0.4.1}/tests/__init__.py +0 -0
- {pytest_selective-0.4.0 → pytest_selective-0.4.1}/tests/test_ci_admission.py +0 -0
- {pytest_selective-0.4.0 → pytest_selective-0.4.1}/tests/test_ci_measurements.py +0 -0
- {pytest_selective-0.4.0 → pytest_selective-0.4.1}/tests/test_ci_test_execution.py +0 -0
- {pytest_selective-0.4.0 → pytest_selective-0.4.1}/tests/test_distribution_metadata.py +0 -0
- {pytest_selective-0.4.0 → pytest_selective-0.4.1}/tests/test_hygiene.py +0 -0
- {pytest_selective-0.4.0 → pytest_selective-0.4.1}/tests/test_public_actions.py +0 -0
- {pytest_selective-0.4.0 → pytest_selective-0.4.1}/tests/test_pytest_expansion.py +0 -0
- {pytest_selective-0.4.0 → pytest_selective-0.4.1}/tests/test_repository_ci.py +0 -0
- {pytest_selective-0.4.0 → pytest_selective-0.4.1}/tests/test_repository_hygiene.py +0 -0
- {pytest_selective-0.4.0 → pytest_selective-0.4.1}/tests/test_repository_hygiene.py.gv-dependency.yaml +0 -0
- {pytest_selective-0.4.0 → pytest_selective-0.4.1}/tests/test_tutorial.py +0 -0
- {pytest_selective-0.4.0 → pytest_selective-0.4.1}/tests/testing/__init__.py +0 -0
- {pytest_selective-0.4.0 → pytest_selective-0.4.1}/tests/testing/git_repo.py +0 -0
- {pytest_selective-0.4.0 → pytest_selective-0.4.1}/tests/testing/test_ast_imports.py +0 -0
- {pytest_selective-0.4.0 → pytest_selective-0.4.1}/tests/testing/test_ci.py +0 -0
- {pytest_selective-0.4.0 → pytest_selective-0.4.1}/tests/testing/test_git_repo.py +0 -0
- {pytest_selective-0.4.0 → pytest_selective-0.4.1}/tests/testing/test_global_inputs.py +0 -0
- {pytest_selective-0.4.0 → pytest_selective-0.4.1}/tests/testing/test_impact_audit.py +0 -0
- {pytest_selective-0.4.0 → pytest_selective-0.4.1}/tests/testing/test_impact_explanation.py +0 -0
- {pytest_selective-0.4.0 → pytest_selective-0.4.1}/tests/testing/test_impact_first_party.py +0 -0
- {pytest_selective-0.4.0 → pytest_selective-0.4.1}/tests/testing/test_impact_graph.py +0 -0
- {pytest_selective-0.4.0 → pytest_selective-0.4.1}/tests/testing/test_impact_name_scope.py +0 -0
- {pytest_selective-0.4.0 → pytest_selective-0.4.1}/tests/testing/test_parse_batch.py +0 -0
- {pytest_selective-0.4.0 → pytest_selective-0.4.1}/tests/testing/test_portability.py +0 -0
- {pytest_selective-0.4.0 → pytest_selective-0.4.1}/tests/testing/test_pytest_plugin.py +0 -0
- {pytest_selective-0.4.0 → pytest_selective-0.4.1}/tests/testing/test_pytest_plugins.py +0 -0
- {pytest_selective-0.4.0 → pytest_selective-0.4.1}/tests/testing/test_pytest_sharding.py +0 -0
- {pytest_selective-0.4.0 → pytest_selective-0.4.1}/tests/testing/test_typecheck_scope.py +0 -0
- {pytest_selective-0.4.0 → pytest_selective-0.4.1}/tests/testing/test_working_tree.py +0 -0
|
@@ -1,5 +1,12 @@
|
|
|
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
|
+
|
|
3
10
|
## [0.4.0](https://github.com/gigaverse-app/pytest-selective/compare/v0.3.0...v0.4.0) (2026-10-24)
|
|
4
11
|
|
|
5
12
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.5
|
|
2
2
|
Name: pytest-selective
|
|
3
|
-
Version: 0.4.
|
|
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/
|
|
@@ -18,8 +18,8 @@ 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: auto
|
|
22
|
-
with true or 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
23
|
default: auto
|
|
24
24
|
extra-files:
|
|
25
25
|
description: Optional newline file containing prior failure canaries or additional changed paths.
|
|
@@ -59,6 +59,10 @@ inputs:
|
|
|
59
59
|
with `head`, so they stay visible after `base` moves to a validated tree. Without it, or without the history, they compare
|
|
60
60
|
against `base`.
|
|
61
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'
|
|
62
66
|
outputs:
|
|
63
67
|
base:
|
|
64
68
|
description: The base the selection was made from; `alternate-base` when it selected fewer tests.
|
|
@@ -108,3 +112,4 @@ runs:
|
|
|
108
112
|
dependency-token: ${{ inputs.dependency-token }}
|
|
109
113
|
explanation-artifact: ${{ inputs.explanation-artifact }}
|
|
110
114
|
graph-base: ${{ inputs.graph-base }}
|
|
115
|
+
explanation-format: ${{ inputs.explanation-format }}
|
|
@@ -8,7 +8,7 @@ inputs:
|
|
|
8
8
|
description: Base commit containing the complete change set.
|
|
9
9
|
default: ${{ github.event.pull_request.base.sha || github.event.before }}
|
|
10
10
|
full:
|
|
11
|
-
description: 'auto
|
|
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
12
|
default: auto
|
|
13
13
|
artifact-name:
|
|
14
14
|
description: >
|
|
@@ -17,7 +17,7 @@ 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: 'auto
|
|
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
21
|
default: auto
|
|
22
22
|
extra-files:
|
|
23
23
|
description: Optional newline file containing prior failure canaries or additional changed paths.
|
|
@@ -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 }}
|
|
@@ -123,6 +126,7 @@ runs:
|
|
|
123
126
|
# Environment only: the CLI never takes it on argv, and git gets it per process, not in config.
|
|
124
127
|
IMPACT_DEPENDENCY_TOKEN: ${{ inputs.dependency-token }}
|
|
125
128
|
IMPACT_GRAPH_BASE: ${{ inputs.graph-base }}
|
|
129
|
+
IMPACT_EXPLANATION_FORMAT: ${{ inputs.explanation-format }}
|
|
126
130
|
SHARED_ACTION_PATH: ${{ github.action_path }}
|
|
127
131
|
run: |
|
|
128
132
|
export PYTHONPATH="$SHARED_ACTION_PATH/../../src${PYTHONPATH:+:$PYTHONPATH}"
|
|
@@ -139,7 +143,7 @@ runs:
|
|
|
139
143
|
args=(affected --config "$IMPACT_CONFIG" --base "$IMPACT_BASE" --head "$IMPACT_HEAD"
|
|
140
144
|
--max-shards "$IMPACT_SHARDS" --minimum-files "$IMPACT_MINIMUM" --github-output "$GITHUB_OUTPUT" --output test-impact-report.json
|
|
141
145
|
--durations "$IMPACT_DURATIONS" --shard-seconds "$IMPACT_SHARD_SECONDS"
|
|
142
|
-
--explanation-output test-impact-explanation.json)
|
|
146
|
+
--explanation-output test-impact-explanation.json --explanation-format "${IMPACT_EXPLANATION_FORMAT:-1}")
|
|
143
147
|
# A reused workspace must not upload a previous plan's explanation.
|
|
144
148
|
rm -f test-impact-explanation.json
|
|
145
149
|
args+=(--full "$IMPACT_FULL")
|
|
@@ -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
|
|
64
|
-
|
|
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,6 +4,20 @@ 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
|
|
@@ -12,7 +12,7 @@ services and application installation altogether.
|
|
|
12
12
|
|
|
13
13
|
Use this checklist in your adoption PR. Mark each stage **verified**, **deferred**
|
|
14
14
|
or **not applicable**, with a reason. Pair it with the
|
|
15
|
-
[CI-esta adoption checklist](https://github.com/gigaverse-app/ci-esta/blob/
|
|
15
|
+
[CI-esta adoption checklist](https://github.com/gigaverse-app/ci-esta/blob/main/docs/tutorials/adoption-checklist.md)
|
|
16
16
|
for reporting, setup, sharding, history, evidence reuse and security integration.
|
|
17
17
|
Selection remains opt-in; installing the package alone does not narrow pytest.
|
|
18
18
|
|
|
@@ -113,7 +113,8 @@ Exercise actual Git commits, not a function that classifies extensions:
|
|
|
113
113
|
| Missing history or operational uncertainty | Conservative full execution |
|
|
114
114
|
| Invalid configuration | Diagnostic and failed required gate |
|
|
115
115
|
| Ordinary PR or main push containing only unreferenced docs | Zero tests |
|
|
116
|
-
|
|
|
116
|
+
| PR/push rerun | Same selection policy as the first attempt |
|
|
117
|
+
| Manual/release checkpoint or explicit `test:full` request | Full execution |
|
|
117
118
|
|
|
118
119
|
Select these routing tests through declared dependencies on configuration,
|
|
119
120
|
execution workflows, helpers and the inputs they inspect. Include wheel smoke,
|
|
@@ -127,9 +128,9 @@ you can enumerate every job, action, and reusable workflow that affects a lane.
|
|
|
127
128
|
## 4. Measure one equivalent pair
|
|
128
129
|
|
|
129
130
|
Open a README-only probe PR. Save the source SHA, tested merge, run ID and attempt
|
|
130
|
-
of its successful selected run.
|
|
131
|
-
|
|
132
|
-
|
|
131
|
+
of its successful selected run. Use a manual full-checkpoint dispatch at that revision.
|
|
132
|
+
A PR/push rerun alone preserves selection; its event payload may also retain the original labels.
|
|
133
|
+
Do not change source or workflow settings between the paired runs. Compare successful completed runs;
|
|
133
134
|
keep cancelled experiments and observer overhead separate.
|
|
134
135
|
|
|
135
136
|
Use [CI-esta's critical-path tutorial](https://ci-esta.readthedocs.io/en/latest/docs/tutorials/measure-critical-path/)
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
# Rerun selection and explanation reasons
|
|
2
|
+
|
|
3
|
+
- [x] Real Git file and lane CLI controls: PR/push attempts preserve empty and nonempty selection, including paths with spaces.
|
|
4
|
+
- [x] Explicit full input, label and checkpoint event controls retain full work; uncertain event metadata remains conservative.
|
|
5
|
+
- [x] File explanations carry the resolved planner reasons; format 1 omits the additive field for strict legacy readers.
|
|
6
|
+
- [x] Real action-shell tests preserve operational fallback and explanation replacement.
|
|
7
|
+
- [ ] Hosted platform checks and released-action consumer reruns preserve checks and selective scope.
|
|
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "pytest-selective"
|
|
7
|
-
version = "0.4.
|
|
7
|
+
version = "0.4.1"
|
|
8
8
|
description = "Explainable Python test impact analysis: select affected pytest files from Git changes"
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
requires-python = ">=3.11"
|
|
@@ -100,7 +100,8 @@ before/after table with evidence. Follow the user's existing PR/merge scope.
|
|
|
100
100
|
and overlapping runs separately; receipt reuse is a distinct optional decision.
|
|
101
101
|
Keep the [measured impact](https://github.com/gigaverse-app/ci-esta/blob/main/docs/measured-impact.md)
|
|
102
102
|
visible in the report, with linked full/selected controls and a collapsed timeline.
|
|
103
|
-
File-count reduction alone cannot establish time savings.
|
|
103
|
+
File-count reduction alone cannot establish time savings. PR/push reruns preserve selection;
|
|
104
|
+
request a full checkpoint explicitly. Preserve explicit full requests
|
|
104
105
|
when removing broad label triggers. Bun projects use their native runner and
|
|
105
106
|
CI-esta's Bun evidence adapter; pytest-selective stays optional for CI-esta.
|
|
106
107
|
The [92.3% README runner-time reduction](https://github.com/gigaverse-app/pytest-selective/blob/main/docs/benchmarks/2026-10-22-routing.md)
|
{pytest_selective-0.4.0 → pytest_selective-0.4.1}/skills/pytest-selective/references/adoption.md
RENAMED
|
@@ -83,8 +83,8 @@ python -m pytest_selective.ci --repo . --config .github/test-lanes.json \
|
|
|
83
83
|
|
|
84
84
|
For Actions, use `plan-lanes` with full history and expose its `lanes` job output.
|
|
85
85
|
An admitted job runs when its lane is true **or planning failed**. Prefer the
|
|
86
|
-
shared `full: auto` and `base` defaults: reruns
|
|
87
|
-
and `test:full` become complete checkpoints
|
|
86
|
+
shared `full: auto` and `base` defaults: PR/push reruns preserve selection;
|
|
87
|
+
manual/release runs, schedules and `test:full` become complete checkpoints. Ordinary main pushes use the changed
|
|
88
88
|
base/head trees so docs-only merges also execute zero tests. Preserve deliberate
|
|
89
89
|
consumer overrides with explicit `full: 'true'` or `full: 'false'`. Use
|
|
90
90
|
`test_roots_json` when forwarding a file plan; do not split paths on spaces or
|
|
@@ -19,26 +19,37 @@ class _PullRequestEvent(BaseModel):
|
|
|
19
19
|
pull_request: _PullRequest
|
|
20
20
|
|
|
21
21
|
|
|
22
|
-
def
|
|
22
|
+
def full_checkpoint_reason(value: str) -> str | None:
|
|
23
23
|
"""Resolve auto once at planning; uncertain event metadata requires full work.
|
|
24
24
|
|
|
25
25
|
Explicit booleans bypass event policy, but never bypass the planners' missing
|
|
26
26
|
history/configuration fallbacks. CLI callers remain selective unless they
|
|
27
27
|
request ``--full`` or ``--full auto``.
|
|
28
28
|
"""
|
|
29
|
-
if value
|
|
30
|
-
return
|
|
29
|
+
if value == "true":
|
|
30
|
+
return "full suite explicitly requested (--full true)"
|
|
31
|
+
if value == "false":
|
|
32
|
+
return None
|
|
31
33
|
if value != "auto":
|
|
32
34
|
raise argparse.ArgumentTypeError("full must be auto, true, or false")
|
|
33
35
|
try:
|
|
34
|
-
if int(os.environ.get("GITHUB_RUN_ATTEMPT", "0")) != 1:
|
|
35
|
-
return True
|
|
36
36
|
event = os.environ.get("GITHUB_EVENT_NAME", "")
|
|
37
37
|
if event == "push":
|
|
38
|
-
return
|
|
38
|
+
return None
|
|
39
|
+
if event in {"workflow_dispatch", "workflow_call", "schedule", "release"}:
|
|
40
|
+
return f"full suite required by {event} checkpoint"
|
|
39
41
|
if event != "pull_request":
|
|
40
|
-
return
|
|
42
|
+
return "full suite fallback: unsupported or missing event metadata"
|
|
41
43
|
payload = _PullRequestEvent.model_validate_json(Path(os.environ["GITHUB_EVENT_PATH"]).read_text())
|
|
42
|
-
return
|
|
44
|
+
return (
|
|
45
|
+
"full suite requested by test:full label"
|
|
46
|
+
if any(label.name == "test:full" for label in payload.pull_request.labels)
|
|
47
|
+
else None
|
|
48
|
+
)
|
|
43
49
|
except (KeyError, OSError, ValueError, ValidationError):
|
|
44
|
-
return
|
|
50
|
+
return "full suite fallback: pull request event metadata unavailable or invalid"
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
def full_checkpoint(value: str) -> bool:
|
|
54
|
+
"""Compatibility boolean API; the planners retain the reason as well."""
|
|
55
|
+
return full_checkpoint_reason(value) is not None
|
|
@@ -11,7 +11,7 @@ from typing import Any
|
|
|
11
11
|
import yaml
|
|
12
12
|
from pydantic import Field, field_validator, model_validator
|
|
13
13
|
|
|
14
|
-
from pytest_selective.checkpoint import
|
|
14
|
+
from pytest_selective.checkpoint import full_checkpoint_reason
|
|
15
15
|
from pytest_selective.impact_graph import DependencyDeclarationError, GraphCache, git, matches, within
|
|
16
16
|
from pytest_selective.impact_models import ImpactConfig, ImpactModel
|
|
17
17
|
from pytest_selective.impact_selection import affected
|
|
@@ -232,12 +232,16 @@ def main(argv: list[str] | None = None) -> int:
|
|
|
232
232
|
parser.add_argument("--repo", type=Path, default=Path("."))
|
|
233
233
|
parser.add_argument("--base", required=True)
|
|
234
234
|
parser.add_argument("--head", default="HEAD")
|
|
235
|
-
parser.add_argument(
|
|
235
|
+
parser.add_argument(
|
|
236
|
+
"--full", nargs="?", const="true", default="false", type=full_checkpoint_reason, metavar="auto|true|false"
|
|
237
|
+
)
|
|
236
238
|
parser.add_argument("--github-output", type=Path)
|
|
237
239
|
args = parser.parse_args(argv)
|
|
238
240
|
try:
|
|
239
241
|
config = LaneConfig.model_validate_json(args.config.read_text(encoding="utf-8"))
|
|
240
|
-
result = plan(args.repo, config, args.base, args.head, full=args.full)
|
|
242
|
+
result = plan(args.repo, config, args.base, args.head, full=args.full is not None)
|
|
243
|
+
if args.full is not None:
|
|
244
|
+
result.reason = args.full
|
|
241
245
|
except (ValueError, OSError, DependencyDeclarationError) as error:
|
|
242
246
|
print(f"Invalid lane configuration/dependency: {error}", file=sys.stderr)
|
|
243
247
|
return 2
|
|
@@ -14,7 +14,7 @@ import time
|
|
|
14
14
|
from collections.abc import Callable
|
|
15
15
|
from pathlib import Path
|
|
16
16
|
|
|
17
|
-
from pytest_selective.checkpoint import
|
|
17
|
+
from pytest_selective.checkpoint import full_checkpoint_reason
|
|
18
18
|
from pytest_selective.impact_audit import audit, load_records, summary
|
|
19
19
|
from pytest_selective.impact_explanation import dependent_tests, explain
|
|
20
20
|
from pytest_selective.impact_graph import (
|
|
@@ -161,7 +161,8 @@ def _write_explanation(
|
|
|
161
161
|
print(f"::warning::Test selection explanation unavailable: {error}", file=sys.stderr)
|
|
162
162
|
return
|
|
163
163
|
args.explanation_output.parent.mkdir(parents=True, exist_ok=True)
|
|
164
|
-
args.
|
|
164
|
+
excluded = {"selection_reasons"} if args.explanation_format == "1" else set()
|
|
165
|
+
args.explanation_output.write_text(explanation.model_dump_json(indent=2, exclude=excluded) + "\n", encoding="utf-8")
|
|
165
166
|
|
|
166
167
|
|
|
167
168
|
def _typecheck_scope(args: argparse.Namespace) -> int:
|
|
@@ -291,11 +292,19 @@ def main(argv: list[str] | None = None) -> int:
|
|
|
291
292
|
parser.add_argument("--output", type=Path)
|
|
292
293
|
# affected: why the selection is this large, for the PR sticky (never changes the selection).
|
|
293
294
|
parser.add_argument("--explanation-output", type=Path)
|
|
295
|
+
parser.add_argument(
|
|
296
|
+
"--explanation-format",
|
|
297
|
+
choices=["1", "2"],
|
|
298
|
+
default="2",
|
|
299
|
+
help="format 1 supports legacy readers; format 2 includes planner reasons",
|
|
300
|
+
)
|
|
294
301
|
# affected: the PR's base, which floor and widening deltas compare against (its merge base).
|
|
295
302
|
parser.add_argument("--graph-base", default="")
|
|
296
303
|
parser.add_argument("--extra-files", type=Path)
|
|
297
304
|
parser.add_argument("--github-output", type=Path)
|
|
298
|
-
parser.add_argument(
|
|
305
|
+
parser.add_argument(
|
|
306
|
+
"--full", nargs="?", const="true", default="false", type=full_checkpoint_reason, metavar="auto|true|false"
|
|
307
|
+
)
|
|
299
308
|
parser.add_argument("--minimum-files", type=int, default=0)
|
|
300
309
|
parser.add_argument("--max-shards", type=int, default=8)
|
|
301
310
|
parser.add_argument("--durations", type=Path, default=Path(".test_durations"))
|
|
@@ -316,6 +325,8 @@ def main(argv: list[str] | None = None) -> int:
|
|
|
316
325
|
# floor-budget: the explanation `affected --explanation-output` wrote for this plan.
|
|
317
326
|
parser.add_argument("--explanation", type=Path, default=Path("test-impact-explanation.json"))
|
|
318
327
|
args = parser.parse_args(argv)
|
|
328
|
+
full_reason = args.full
|
|
329
|
+
args.full = full_reason is not None
|
|
319
330
|
args.base = args.base or ("HEAD" if args.working_tree else "HEAD^")
|
|
320
331
|
if args.working_tree and args.command not in {"affected", "warm", "dependencies", "explain", "conftest-impact"}:
|
|
321
332
|
parser.error("--working-tree is supported for affected, warm, dependencies, explain and conftest-impact")
|
|
@@ -355,7 +366,7 @@ def main(argv: list[str] | None = None) -> int:
|
|
|
355
366
|
snapshot = cache.snapshot(args.repo, args.head, config)
|
|
356
367
|
report.selected = snapshot.tests
|
|
357
368
|
report.mode = "full"
|
|
358
|
-
report.reasons.append(
|
|
369
|
+
report.reasons.append(full_reason)
|
|
359
370
|
if report.selected and len(report.selected) < args.minimum_files:
|
|
360
371
|
snapshot = cache.snapshot(args.repo, args.head, config)
|
|
361
372
|
extras = [test for test in snapshot.tests if test not in report.selected]
|
{pytest_selective-0.4.0 → pytest_selective-0.4.1}/src/pytest_selective/impact_explanation.py
RENAMED
|
@@ -448,6 +448,7 @@ def explain(
|
|
|
448
448
|
selected=len(report.selected),
|
|
449
449
|
total=report.total_files,
|
|
450
450
|
forced_full=forced_full,
|
|
451
|
+
selection_reasons=report.reasons,
|
|
451
452
|
graph_base=graph_before.tree,
|
|
452
453
|
floor_seeds=floor_seeds,
|
|
453
454
|
routes=_routes(union_edges(before, after), report.seeds, report.selected, floor_seeds, reach_after, after),
|
|
@@ -458,6 +458,9 @@ class SelectionExplanation(ImpactModel):
|
|
|
458
458
|
#: The complete suite ran because it was requested (a schedule, a dispatch, coverage, a label),
|
|
459
459
|
#: not because the change reaches every test. Such a run times something other than a PR run.
|
|
460
460
|
forced_full: bool = False
|
|
461
|
+
#: Planner decisions, including the exact full override, fallback or padding reason.
|
|
462
|
+
#: Older artifacts omit this field; readers must not guess the missing trigger.
|
|
463
|
+
selection_reasons: list[str] = Field(default_factory=list)
|
|
461
464
|
#: The tree floor and widening deltas compare against: the PR's merge base, else `base`.
|
|
462
465
|
graph_base: str
|
|
463
466
|
#: Traversal seeds that are startup-floor files: each alone selects nearly every test.
|
|
@@ -15,14 +15,16 @@ from tests.testing.git_repo import commit, init, write
|
|
|
15
15
|
[
|
|
16
16
|
("pull_request", "1", [], "auto", False),
|
|
17
17
|
("push", "1", [], "auto", False),
|
|
18
|
-
("pull_request", "2", [], "auto",
|
|
18
|
+
("pull_request", "2", [], "auto", False),
|
|
19
|
+
("push", "3", [], "auto", False),
|
|
20
|
+
("pull_request", "2", ["test:full"], "auto", True),
|
|
19
21
|
("pull_request", "1", ["test:full"], "auto", True),
|
|
20
22
|
("workflow_dispatch", "1", [], "auto", True),
|
|
21
23
|
("workflow_call", "1", [], "auto", True),
|
|
22
24
|
("schedule", "1", [], "auto", True),
|
|
23
25
|
("release", "1", [], "auto", True),
|
|
24
26
|
("unknown", "1", [], "auto", True),
|
|
25
|
-
("pull_request", "invalid", [], "auto",
|
|
27
|
+
("pull_request", "invalid", [], "auto", False),
|
|
26
28
|
("pull_request", "1", None, "auto", True),
|
|
27
29
|
("schedule", "2", [], "false", False),
|
|
28
30
|
("push", "1", [], "true", True),
|
|
@@ -60,7 +62,27 @@ def test_checkpoint_policy_drives_real_selection(tmp_path, monkeypatch, planner,
|
|
|
60
62
|
str(output),
|
|
61
63
|
]
|
|
62
64
|
if planner == "files":
|
|
63
|
-
|
|
65
|
+
explanation = tmp_path / "explanation.json"
|
|
66
|
+
assert (
|
|
67
|
+
files_main(
|
|
68
|
+
[
|
|
69
|
+
"affected",
|
|
70
|
+
*args,
|
|
71
|
+
"--cache",
|
|
72
|
+
str(tmp_path / "cache.sqlite3"),
|
|
73
|
+
"--explanation-output",
|
|
74
|
+
str(explanation),
|
|
75
|
+
]
|
|
76
|
+
)
|
|
77
|
+
== 0
|
|
78
|
+
)
|
|
79
|
+
reasons = json.loads(explanation.read_text())["selection_reasons"]
|
|
80
|
+
if policy == "true":
|
|
81
|
+
assert "full suite explicitly requested (--full true)" in reasons
|
|
82
|
+
elif policy == "auto" and labels == ["test:full"]:
|
|
83
|
+
assert "full suite requested by test:full label" in reasons
|
|
84
|
+
elif policy == "auto" and event in {"schedule", "workflow_dispatch", "workflow_call", "release"}:
|
|
85
|
+
assert f"full suite required by {event} checkpoint" in reasons
|
|
64
86
|
assert ("mode=full" if full else "mode=none") in output.read_text()
|
|
65
87
|
roots = next(
|
|
66
88
|
line.removeprefix("test_roots_json=")
|
|
@@ -73,7 +95,11 @@ def test_checkpoint_policy_drives_real_selection(tmp_path, monkeypatch, planner,
|
|
|
73
95
|
assert f"tests={str(full).lower()}" in output.read_text()
|
|
74
96
|
|
|
75
97
|
|
|
76
|
-
|
|
98
|
+
@pytest.mark.parametrize("attempt", ["1", "2", "7"])
|
|
99
|
+
@pytest.mark.parametrize("explanation_format", ["1", "2"])
|
|
100
|
+
def test_json_output_preserves_selected_paths_with_spaces(tmp_path, monkeypatch, attempt, explanation_format):
|
|
101
|
+
monkeypatch.setenv("GITHUB_EVENT_NAME", "push")
|
|
102
|
+
monkeypatch.setenv("GITHUB_RUN_ATTEMPT", attempt)
|
|
77
103
|
init(tmp_path)
|
|
78
104
|
test = "tests/test_with spaces.py"
|
|
79
105
|
write(tmp_path, test, "def test_example(): assert True")
|
|
@@ -83,6 +109,7 @@ def test_json_output_preserves_selected_paths_with_spaces(tmp_path):
|
|
|
83
109
|
commit(tmp_path)
|
|
84
110
|
write(tmp_path, "config.json", json.dumps({"module_roots": ["."], "test_roots": ["tests"]}))
|
|
85
111
|
output = tmp_path / "output"
|
|
112
|
+
explanation = tmp_path / "explanation.json"
|
|
86
113
|
assert (
|
|
87
114
|
files_main(
|
|
88
115
|
[
|
|
@@ -93,6 +120,12 @@ def test_json_output_preserves_selected_paths_with_spaces(tmp_path):
|
|
|
93
120
|
str(tmp_path / "config.json"),
|
|
94
121
|
"--base",
|
|
95
122
|
base,
|
|
123
|
+
"--full",
|
|
124
|
+
"auto",
|
|
125
|
+
"--explanation-output",
|
|
126
|
+
str(explanation),
|
|
127
|
+
"--explanation-format",
|
|
128
|
+
explanation_format,
|
|
96
129
|
"--github-output",
|
|
97
130
|
str(output),
|
|
98
131
|
"--cache",
|
|
@@ -104,3 +137,4 @@ def test_json_output_preserves_selected_paths_with_spaces(tmp_path):
|
|
|
104
137
|
values = dict(line.split("=", 1) for line in output.read_text().splitlines())
|
|
105
138
|
assert values["mode"] == "scoped"
|
|
106
139
|
assert json.loads(values["test_roots_json"]) == [test]
|
|
140
|
+
assert ("selection_reasons" in json.loads(explanation.read_text())) == (explanation_format == "2")
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{pytest_selective-0.4.0 → pytest_selective-0.4.1}/docs/benchmarks/2026-10-22-due-docs-ci.json
RENAMED
|
File without changes
|
{pytest_selective-0.4.0 → pytest_selective-0.4.1}/docs/benchmarks/2026-10-22-due-docs-widget.json
RENAMED
|
File without changes
|
{pytest_selective-0.4.0 → pytest_selective-0.4.1}/docs/benchmarks/2026-10-22-due-widget.json
RENAMED
|
File without changes
|
{pytest_selective-0.4.0 → pytest_selective-0.4.1}/docs/benchmarks/2026-10-22-input-replays.json
RENAMED
|
File without changes
|
{pytest_selective-0.4.0 → pytest_selective-0.4.1}/docs/benchmarks/2026-10-22-obligation-docs.json
RENAMED
|
File without changes
|
{pytest_selective-0.4.0 → pytest_selective-0.4.1}/docs/benchmarks/2026-10-22-obligation.json
RENAMED
|
File without changes
|
|
File without changes
|
{pytest_selective-0.4.0 → pytest_selective-0.4.1}/docs/benchmarks/2026-10-22-selective-docs.json
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{pytest_selective-0.4.0 → pytest_selective-0.4.1}/docs/benchmarks/2026-10-23-windows-saleor.md
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{pytest_selective-0.4.0 → pytest_selective-0.4.1}/documentation/assets/routing-comparison.svg
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{pytest_selective-0.4.0 → pytest_selective-0.4.1}/skills/pytest-selective/agents/openai.yaml
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{pytest_selective-0.4.0 → pytest_selective-0.4.1}/src/pytest_selective/impact_audit_plugin.py
RENAMED
|
File without changes
|
{pytest_selective-0.4.0 → pytest_selective-0.4.1}/src/pytest_selective/impact_declarations.py
RENAMED
|
File without changes
|
{pytest_selective-0.4.0 → pytest_selective-0.4.1}/src/pytest_selective/impact_first_party.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|