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.
- {pytest_selective-0.3.0 → pytest_selective-0.4.1}/.github/workflows/ci.yml +1 -17
- {pytest_selective-0.3.0 → pytest_selective-0.4.1}/.github/workflows/public-actions.yml +0 -2
- pytest_selective-0.4.1/.release-please-manifest.json +3 -0
- {pytest_selective-0.3.0 → pytest_selective-0.4.1}/CHANGELOG.md +14 -0
- {pytest_selective-0.3.0 → pytest_selective-0.4.1}/PKG-INFO +5 -2
- {pytest_selective-0.3.0 → pytest_selective-0.4.1}/README.md +4 -1
- {pytest_selective-0.3.0 → pytest_selective-0.4.1}/action.yml +12 -3
- {pytest_selective-0.3.0 → pytest_selective-0.4.1}/actions/plan-lanes/action.yml +4 -4
- {pytest_selective-0.3.0 → pytest_selective-0.4.1}/actions/test-impact/action.yml +13 -5
- {pytest_selective-0.3.0 → pytest_selective-0.4.1}/docs/README.md +1 -0
- {pytest_selective-0.3.0 → pytest_selective-0.4.1}/docs/ci-toolkit.md +3 -2
- {pytest_selective-0.3.0 → pytest_selective-0.4.1}/docs/ci.md +45 -13
- {pytest_selective-0.3.0 → pytest_selective-0.4.1}/docs/configuration.md +3 -0
- {pytest_selective-0.3.0 → pytest_selective-0.4.1}/docs/selection-health.md +2 -1
- {pytest_selective-0.3.0 → pytest_selective-0.4.1}/docs/tutorial.md +3 -0
- pytest_selective-0.4.1/docs/tutorials/adoption-checklist.md +244 -0
- pytest_selective-0.4.1/docs/tutorials/sidecar-dependencies.md +270 -0
- {pytest_selective-0.3.0 → pytest_selective-0.4.1}/docs/tutorials/skip-unrelated-ci.md +5 -6
- pytest_selective-0.4.1/docs/work-in-progress/tickets/rerun-selection-test-plan.md +7 -0
- {pytest_selective-0.3.0 → pytest_selective-0.4.1}/mkdocs.yml +6 -0
- {pytest_selective-0.3.0 → pytest_selective-0.4.1}/pyproject.toml +1 -1
- {pytest_selective-0.3.0 → pytest_selective-0.4.1}/skills/pytest-selective/SKILL.md +7 -1
- {pytest_selective-0.3.0 → pytest_selective-0.4.1}/skills/pytest-selective/references/adoption.md +7 -3
- pytest_selective-0.4.1/src/pytest_selective/checkpoint.py +55 -0
- {pytest_selective-0.3.0 → pytest_selective-0.4.1}/src/pytest_selective/ci.py +7 -2
- {pytest_selective-0.3.0 → pytest_selective-0.4.1}/src/pytest_selective/impact_cli.py +20 -3
- {pytest_selective-0.3.0 → pytest_selective-0.4.1}/src/pytest_selective/impact_explanation.py +1 -0
- {pytest_selective-0.3.0 → pytest_selective-0.4.1}/src/pytest_selective/impact_models.py +3 -0
- pytest_selective-0.4.1/tests/test_checkpoint_policy.py +140 -0
- {pytest_selective-0.3.0 → pytest_selective-0.4.1}/tests/test_ci_admission.py +2 -2
- {pytest_selective-0.3.0 → pytest_selective-0.4.1}/tests/test_repository_ci.py +1 -1
- {pytest_selective-0.3.0 → pytest_selective-0.4.1}/uv.lock +1 -1
- pytest_selective-0.3.0/.release-please-manifest.json +0 -3
- {pytest_selective-0.3.0 → pytest_selective-0.4.1}/.gitattributes +0 -0
- {pytest_selective-0.3.0 → pytest_selective-0.4.1}/.github/ISSUE_TEMPLATE/bug_report.yml +0 -0
- {pytest_selective-0.3.0 → pytest_selective-0.4.1}/.github/PULL_REQUEST_TEMPLATE.md +0 -0
- {pytest_selective-0.3.0 → pytest_selective-0.4.1}/.github/dependabot.yml +0 -0
- {pytest_selective-0.3.0 → pytest_selective-0.4.1}/.github/test-lanes.json +0 -0
- {pytest_selective-0.3.0 → pytest_selective-0.4.1}/.github/workflows/docs.yml +0 -0
- {pytest_selective-0.3.0 → pytest_selective-0.4.1}/.github/workflows/observe-ci.yml +0 -0
- {pytest_selective-0.3.0 → pytest_selective-0.4.1}/.github/workflows/pr-title.yml +0 -0
- {pytest_selective-0.3.0 → pytest_selective-0.4.1}/.github/workflows/release-please.yml +0 -0
- {pytest_selective-0.3.0 → pytest_selective-0.4.1}/.github/workflows/release.yml +0 -0
- {pytest_selective-0.3.0 → pytest_selective-0.4.1}/.github/workflows/windows-performance.yml +0 -0
- {pytest_selective-0.3.0 → pytest_selective-0.4.1}/.gitignore +0 -0
- {pytest_selective-0.3.0 → pytest_selective-0.4.1}/.pre-commit-config.yaml +0 -0
- {pytest_selective-0.3.0 → pytest_selective-0.4.1}/.readthedocs.yaml +0 -0
- {pytest_selective-0.3.0 → pytest_selective-0.4.1}/AGENTS.md +0 -0
- {pytest_selective-0.3.0 → pytest_selective-0.4.1}/CLAUDE.md +0 -0
- {pytest_selective-0.3.0 → pytest_selective-0.4.1}/CONTRIBUTING.md +0 -0
- {pytest_selective-0.3.0 → pytest_selective-0.4.1}/LICENSE +0 -0
- {pytest_selective-0.3.0 → pytest_selective-0.4.1}/PROVENANCE.md +0 -0
- {pytest_selective-0.3.0 → pytest_selective-0.4.1}/RELEASING.md +0 -0
- {pytest_selective-0.3.0 → pytest_selective-0.4.1}/actions/README.md +0 -0
- {pytest_selective-0.3.0 → pytest_selective-0.4.1}/actions/pytest-expansion/action.yml +0 -0
- {pytest_selective-0.3.0 → pytest_selective-0.4.1}/agentsync.toml +0 -0
- {pytest_selective-0.3.0 → pytest_selective-0.4.1}/docs/actions.md +0 -0
- {pytest_selective-0.3.0 → pytest_selective-0.4.1}/docs/agents.md +0 -0
- {pytest_selective-0.3.0 → pytest_selective-0.4.1}/docs/assets/banner.png +0 -0
- {pytest_selective-0.3.0 → pytest_selective-0.4.1}/docs/assets/banner.svg +0 -0
- {pytest_selective-0.3.0 → pytest_selective-0.4.1}/docs/assets/brand.json +0 -0
- {pytest_selective-0.3.0 → pytest_selective-0.4.1}/docs/assets/icon.png +0 -0
- {pytest_selective-0.3.0 → pytest_selective-0.4.1}/docs/assets/logo.svg +0 -0
- {pytest_selective-0.3.0 → pytest_selective-0.4.1}/docs/assets/raster-sources.json +0 -0
- {pytest_selective-0.3.0 → pytest_selective-0.4.1}/docs/assets/social-preview.png +0 -0
- {pytest_selective-0.3.0 → pytest_selective-0.4.1}/docs/assets/social-preview.svg +0 -0
- {pytest_selective-0.3.0 → pytest_selective-0.4.1}/docs/benchmarks/2026-10-22-due-ci.json +0 -0
- {pytest_selective-0.3.0 → pytest_selective-0.4.1}/docs/benchmarks/2026-10-22-due-docs-ci.json +0 -0
- {pytest_selective-0.3.0 → pytest_selective-0.4.1}/docs/benchmarks/2026-10-22-due-docs-widget.json +0 -0
- {pytest_selective-0.3.0 → pytest_selective-0.4.1}/docs/benchmarks/2026-10-22-due-widget.json +0 -0
- {pytest_selective-0.3.0 → pytest_selective-0.4.1}/docs/benchmarks/2026-10-22-input-replays.json +0 -0
- {pytest_selective-0.3.0 → pytest_selective-0.4.1}/docs/benchmarks/2026-10-22-obligation-docs.json +0 -0
- {pytest_selective-0.3.0 → pytest_selective-0.4.1}/docs/benchmarks/2026-10-22-obligation.json +0 -0
- {pytest_selective-0.3.0 → pytest_selective-0.4.1}/docs/benchmarks/2026-10-22-routing.md +0 -0
- {pytest_selective-0.3.0 → pytest_selective-0.4.1}/docs/benchmarks/2026-10-22-selective-docs.json +0 -0
- {pytest_selective-0.3.0 → pytest_selective-0.4.1}/docs/benchmarks/2026-10-22.md +0 -0
- {pytest_selective-0.3.0 → pytest_selective-0.4.1}/docs/benchmarks/2026-10-23-windows-saleor/obligation-baseline.json +0 -0
- {pytest_selective-0.3.0 → pytest_selective-0.4.1}/docs/benchmarks/2026-10-23-windows-saleor/selective-after-3.11.xml +0 -0
- {pytest_selective-0.3.0 → pytest_selective-0.4.1}/docs/benchmarks/2026-10-23-windows-saleor/selective-after-3.14.xml +0 -0
- {pytest_selective-0.3.0 → pytest_selective-0.4.1}/docs/benchmarks/2026-10-23-windows-saleor/selective-after.json +0 -0
- {pytest_selective-0.3.0 → pytest_selective-0.4.1}/docs/benchmarks/2026-10-23-windows-saleor/selective-control-3.11.xml +0 -0
- {pytest_selective-0.3.0 → pytest_selective-0.4.1}/docs/benchmarks/2026-10-23-windows-saleor/selective-control-3.14.xml +0 -0
- {pytest_selective-0.3.0 → pytest_selective-0.4.1}/docs/benchmarks/2026-10-23-windows-saleor/selective-control.json +0 -0
- {pytest_selective-0.3.0 → pytest_selective-0.4.1}/docs/benchmarks/2026-10-23-windows-saleor/selective-earlier.json +0 -0
- {pytest_selective-0.3.0 → pytest_selective-0.4.1}/docs/benchmarks/2026-10-23-windows-saleor.md +0 -0
- {pytest_selective-0.3.0 → pytest_selective-0.4.1}/docs/benchmarks/windows-runtime.md +0 -0
- {pytest_selective-0.3.0 → pytest_selective-0.4.1}/docs/comparison-pytest-impacted.md +0 -0
- {pytest_selective-0.3.0 → pytest_selective-0.4.1}/docs/evidence.md +0 -0
- {pytest_selective-0.3.0 → pytest_selective-0.4.1}/docs/local-development.md +0 -0
- {pytest_selective-0.3.0 → pytest_selective-0.4.1}/docs/pytest-hygiene.md +0 -0
- {pytest_selective-0.3.0 → pytest_selective-0.4.1}/docs/work-in-progress/tickets/actions-directory-test-plan.md +0 -0
- {pytest_selective-0.3.0 → pytest_selective-0.4.1}/docs/work-in-progress/tickets/codex-selector-correctness-performance-test-plan.md +0 -0
- {pytest_selective-0.3.0 → pytest_selective-0.4.1}/docs/work-in-progress/tickets/codex-windows-git-performance-test-plan.md +0 -0
- {pytest_selective-0.3.0 → pytest_selective-0.4.1}/docs/work-in-progress/tickets/impacted-improvements-test-plan.md +0 -0
- {pytest_selective-0.3.0 → pytest_selective-0.4.1}/docs/work-in-progress/tickets/move-action-implementations-test-plan.md +0 -0
- {pytest_selective-0.3.0 → pytest_selective-0.4.1}/docs/work-in-progress/tickets/public-action-onboarding-test-plan.md +0 -0
- {pytest_selective-0.3.0 → pytest_selective-0.4.1}/documentation/assets/routing-comparison.svg +0 -0
- {pytest_selective-0.3.0 → pytest_selective-0.4.1}/documentation/check_site.py +0 -0
- {pytest_selective-0.3.0 → pytest_selective-0.4.1}/documentation/ecosystem.md +0 -0
- {pytest_selective-0.3.0 → pytest_selective-0.4.1}/documentation/hooks.py +0 -0
- {pytest_selective-0.3.0 → pytest_selective-0.4.1}/documentation/index.md +0 -0
- {pytest_selective-0.3.0 → pytest_selective-0.4.1}/documentation/overrides/main.html +0 -0
- {pytest_selective-0.3.0 → pytest_selective-0.4.1}/documentation/publishing.md +0 -0
- {pytest_selective-0.3.0 → pytest_selective-0.4.1}/documentation/requirements.in +0 -0
- {pytest_selective-0.3.0 → pytest_selective-0.4.1}/documentation/requirements.txt +0 -0
- {pytest_selective-0.3.0 → pytest_selective-0.4.1}/release-please-config.json +0 -0
- {pytest_selective-0.3.0 → pytest_selective-0.4.1}/scripts/benchmark_graph.py +0 -0
- {pytest_selective-0.3.0 → pytest_selective-0.4.1}/scripts/benchmark_parsing.py +0 -0
- {pytest_selective-0.3.0 → pytest_selective-0.4.1}/scripts/benchmark_rust.py +0 -0
- {pytest_selective-0.3.0 → pytest_selective-0.4.1}/scripts/check_docs.py +0 -0
- {pytest_selective-0.3.0 → pytest_selective-0.4.1}/scripts/check_metadata.py +0 -0
- {pytest_selective-0.3.0 → pytest_selective-0.4.1}/scripts/ci_file_config.py +0 -0
- {pytest_selective-0.3.0 → pytest_selective-0.4.1}/scripts/compare_impacted.py +0 -0
- {pytest_selective-0.3.0 → pytest_selective-0.4.1}/scripts/measure_ci_runs.py +0 -0
- {pytest_selective-0.3.0 → pytest_selective-0.4.1}/scripts/public_actions.py +0 -0
- {pytest_selective-0.3.0 → pytest_selective-0.4.1}/scripts/render_brand.py +0 -0
- {pytest_selective-0.3.0 → pytest_selective-0.4.1}/scripts/run_ci_tests.py +0 -0
- {pytest_selective-0.3.0 → pytest_selective-0.4.1}/scripts/smoke_distribution.py +0 -0
- {pytest_selective-0.3.0 → pytest_selective-0.4.1}/scripts/tutorial.py +0 -0
- {pytest_selective-0.3.0 → pytest_selective-0.4.1}/skills/pytest-selective/agents/openai.yaml +0 -0
- {pytest_selective-0.3.0 → pytest_selective-0.4.1}/src/pytest_selective/__init__.py +0 -0
- {pytest_selective-0.3.0 → pytest_selective-0.4.1}/src/pytest_selective/__main__.py +0 -0
- {pytest_selective-0.3.0 → pytest_selective-0.4.1}/src/pytest_selective/ast_imports.py +0 -0
- {pytest_selective-0.3.0 → pytest_selective-0.4.1}/src/pytest_selective/hygiene.py +0 -0
- {pytest_selective-0.3.0 → pytest_selective-0.4.1}/src/pytest_selective/impact_audit.py +0 -0
- {pytest_selective-0.3.0 → pytest_selective-0.4.1}/src/pytest_selective/impact_audit_plugin.py +0 -0
- {pytest_selective-0.3.0 → pytest_selective-0.4.1}/src/pytest_selective/impact_declarations.py +0 -0
- {pytest_selective-0.3.0 → pytest_selective-0.4.1}/src/pytest_selective/impact_first_party.py +0 -0
- {pytest_selective-0.3.0 → pytest_selective-0.4.1}/src/pytest_selective/impact_graph.py +0 -0
- {pytest_selective-0.3.0 → pytest_selective-0.4.1}/src/pytest_selective/impact_name_scope.py +0 -0
- {pytest_selective-0.3.0 → pytest_selective-0.4.1}/src/pytest_selective/impact_selection.py +0 -0
- {pytest_selective-0.3.0 → pytest_selective-0.4.1}/src/pytest_selective/input_policy.py +0 -0
- {pytest_selective-0.3.0 → pytest_selective-0.4.1}/src/pytest_selective/local.py +0 -0
- {pytest_selective-0.3.0 → pytest_selective-0.4.1}/src/pytest_selective/parse_batch.py +0 -0
- {pytest_selective-0.3.0 → pytest_selective-0.4.1}/src/pytest_selective/plugin.py +0 -0
- {pytest_selective-0.3.0 → pytest_selective-0.4.1}/src/pytest_selective/py.typed +0 -0
- {pytest_selective-0.3.0 → pytest_selective-0.4.1}/src/pytest_selective/pytest_expansion.py +0 -0
- {pytest_selective-0.3.0 → pytest_selective-0.4.1}/src/pytest_selective/pytest_plugins.py +0 -0
- {pytest_selective-0.3.0 → pytest_selective-0.4.1}/src/pytest_selective/pytest_sharding.py +0 -0
- {pytest_selective-0.3.0 → pytest_selective-0.4.1}/src/pytest_selective/test_inventory.py +0 -0
- {pytest_selective-0.3.0 → pytest_selective-0.4.1}/src/pytest_selective/typecheck_scope.py +0 -0
- {pytest_selective-0.3.0 → pytest_selective-0.4.1}/src/pytest_selective/working_tree.py +0 -0
- {pytest_selective-0.3.0 → pytest_selective-0.4.1}/tests/__init__.py +0 -0
- {pytest_selective-0.3.0 → pytest_selective-0.4.1}/tests/test_ci_measurements.py +0 -0
- {pytest_selective-0.3.0 → pytest_selective-0.4.1}/tests/test_ci_test_execution.py +0 -0
- {pytest_selective-0.3.0 → pytest_selective-0.4.1}/tests/test_distribution_metadata.py +0 -0
- {pytest_selective-0.3.0 → pytest_selective-0.4.1}/tests/test_hygiene.py +0 -0
- {pytest_selective-0.3.0 → pytest_selective-0.4.1}/tests/test_public_actions.py +0 -0
- {pytest_selective-0.3.0 → pytest_selective-0.4.1}/tests/test_pytest_expansion.py +0 -0
- {pytest_selective-0.3.0 → pytest_selective-0.4.1}/tests/test_repository_hygiene.py +0 -0
- {pytest_selective-0.3.0 → pytest_selective-0.4.1}/tests/test_repository_hygiene.py.gv-dependency.yaml +0 -0
- {pytest_selective-0.3.0 → pytest_selective-0.4.1}/tests/test_tutorial.py +0 -0
- {pytest_selective-0.3.0 → pytest_selective-0.4.1}/tests/testing/__init__.py +0 -0
- {pytest_selective-0.3.0 → pytest_selective-0.4.1}/tests/testing/git_repo.py +0 -0
- {pytest_selective-0.3.0 → pytest_selective-0.4.1}/tests/testing/test_ast_imports.py +0 -0
- {pytest_selective-0.3.0 → pytest_selective-0.4.1}/tests/testing/test_ci.py +0 -0
- {pytest_selective-0.3.0 → pytest_selective-0.4.1}/tests/testing/test_git_repo.py +0 -0
- {pytest_selective-0.3.0 → pytest_selective-0.4.1}/tests/testing/test_global_inputs.py +0 -0
- {pytest_selective-0.3.0 → pytest_selective-0.4.1}/tests/testing/test_impact_audit.py +0 -0
- {pytest_selective-0.3.0 → pytest_selective-0.4.1}/tests/testing/test_impact_explanation.py +0 -0
- {pytest_selective-0.3.0 → pytest_selective-0.4.1}/tests/testing/test_impact_first_party.py +0 -0
- {pytest_selective-0.3.0 → pytest_selective-0.4.1}/tests/testing/test_impact_graph.py +0 -0
- {pytest_selective-0.3.0 → pytest_selective-0.4.1}/tests/testing/test_impact_name_scope.py +0 -0
- {pytest_selective-0.3.0 → pytest_selective-0.4.1}/tests/testing/test_parse_batch.py +0 -0
- {pytest_selective-0.3.0 → pytest_selective-0.4.1}/tests/testing/test_portability.py +0 -0
- {pytest_selective-0.3.0 → pytest_selective-0.4.1}/tests/testing/test_pytest_plugin.py +0 -0
- {pytest_selective-0.3.0 → pytest_selective-0.4.1}/tests/testing/test_pytest_plugins.py +0 -0
- {pytest_selective-0.3.0 → pytest_selective-0.4.1}/tests/testing/test_pytest_sharding.py +0 -0
- {pytest_selective-0.3.0 → pytest_selective-0.4.1}/tests/testing/test_typecheck_scope.py +0 -0
- {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.
|
|
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
|
|
@@ -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
|
+
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
|
-
|
|
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:
|
|
22
|
-
|
|
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
|
-
|
|
9
|
+
default: ${{ github.event.pull_request.base.sha || github.event.before }}
|
|
10
10
|
full:
|
|
11
|
-
description:
|
|
12
|
-
default:
|
|
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
|
-
|
|
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
|
-
|
|
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:
|
|
21
|
-
default:
|
|
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
|
-
|
|
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
|
|
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,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
|
-
|
|
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.
|
|
50
|
+
TEST_ROOTS: ${{ steps.impact.outputs.test_roots_json }}
|
|
38
51
|
shell: bash
|
|
39
52
|
run: |
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
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.
|
|
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
|
-
|
|
197
|
-
full
|
|
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. [
|
|
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+,
|