pytest-selective 0.3.0__tar.gz → 0.4.0__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.0}/.github/workflows/ci.yml +1 -17
- {pytest_selective-0.3.0 → pytest_selective-0.4.0}/.github/workflows/public-actions.yml +0 -2
- pytest_selective-0.4.0/.release-please-manifest.json +3 -0
- {pytest_selective-0.3.0 → pytest_selective-0.4.0}/CHANGELOG.md +7 -0
- {pytest_selective-0.3.0 → pytest_selective-0.4.0}/PKG-INFO +5 -2
- {pytest_selective-0.3.0 → pytest_selective-0.4.0}/README.md +4 -1
- {pytest_selective-0.3.0 → pytest_selective-0.4.0}/action.yml +7 -3
- {pytest_selective-0.3.0 → pytest_selective-0.4.0}/actions/plan-lanes/action.yml +4 -4
- {pytest_selective-0.3.0 → pytest_selective-0.4.0}/actions/test-impact/action.yml +8 -4
- {pytest_selective-0.3.0 → pytest_selective-0.4.0}/docs/README.md +1 -0
- {pytest_selective-0.3.0 → pytest_selective-0.4.0}/docs/ci.md +31 -13
- {pytest_selective-0.3.0 → pytest_selective-0.4.0}/docs/configuration.md +3 -0
- {pytest_selective-0.3.0 → pytest_selective-0.4.0}/docs/selection-health.md +2 -1
- {pytest_selective-0.3.0 → pytest_selective-0.4.0}/docs/tutorial.md +3 -0
- pytest_selective-0.4.0/docs/tutorials/adoption-checklist.md +244 -0
- pytest_selective-0.4.0/docs/tutorials/sidecar-dependencies.md +270 -0
- {pytest_selective-0.3.0 → pytest_selective-0.4.0}/docs/tutorials/skip-unrelated-ci.md +0 -2
- {pytest_selective-0.3.0 → pytest_selective-0.4.0}/mkdocs.yml +6 -0
- {pytest_selective-0.3.0 → pytest_selective-0.4.0}/pyproject.toml +1 -1
- {pytest_selective-0.3.0 → pytest_selective-0.4.0}/skills/pytest-selective/SKILL.md +5 -0
- {pytest_selective-0.3.0 → pytest_selective-0.4.0}/skills/pytest-selective/references/adoption.md +7 -3
- pytest_selective-0.4.0/src/pytest_selective/checkpoint.py +44 -0
- {pytest_selective-0.3.0 → pytest_selective-0.4.0}/src/pytest_selective/ci.py +2 -1
- {pytest_selective-0.3.0 → pytest_selective-0.4.0}/src/pytest_selective/impact_cli.py +7 -1
- pytest_selective-0.4.0/tests/test_checkpoint_policy.py +106 -0
- {pytest_selective-0.3.0 → pytest_selective-0.4.0}/tests/test_ci_admission.py +2 -2
- {pytest_selective-0.3.0 → pytest_selective-0.4.0}/tests/test_repository_ci.py +1 -1
- {pytest_selective-0.3.0 → pytest_selective-0.4.0}/uv.lock +1 -1
- pytest_selective-0.3.0/.release-please-manifest.json +0 -3
- {pytest_selective-0.3.0 → pytest_selective-0.4.0}/.gitattributes +0 -0
- {pytest_selective-0.3.0 → pytest_selective-0.4.0}/.github/ISSUE_TEMPLATE/bug_report.yml +0 -0
- {pytest_selective-0.3.0 → pytest_selective-0.4.0}/.github/PULL_REQUEST_TEMPLATE.md +0 -0
- {pytest_selective-0.3.0 → pytest_selective-0.4.0}/.github/dependabot.yml +0 -0
- {pytest_selective-0.3.0 → pytest_selective-0.4.0}/.github/test-lanes.json +0 -0
- {pytest_selective-0.3.0 → pytest_selective-0.4.0}/.github/workflows/docs.yml +0 -0
- {pytest_selective-0.3.0 → pytest_selective-0.4.0}/.github/workflows/observe-ci.yml +0 -0
- {pytest_selective-0.3.0 → pytest_selective-0.4.0}/.github/workflows/pr-title.yml +0 -0
- {pytest_selective-0.3.0 → pytest_selective-0.4.0}/.github/workflows/release-please.yml +0 -0
- {pytest_selective-0.3.0 → pytest_selective-0.4.0}/.github/workflows/release.yml +0 -0
- {pytest_selective-0.3.0 → pytest_selective-0.4.0}/.github/workflows/windows-performance.yml +0 -0
- {pytest_selective-0.3.0 → pytest_selective-0.4.0}/.gitignore +0 -0
- {pytest_selective-0.3.0 → pytest_selective-0.4.0}/.pre-commit-config.yaml +0 -0
- {pytest_selective-0.3.0 → pytest_selective-0.4.0}/.readthedocs.yaml +0 -0
- {pytest_selective-0.3.0 → pytest_selective-0.4.0}/AGENTS.md +0 -0
- {pytest_selective-0.3.0 → pytest_selective-0.4.0}/CLAUDE.md +0 -0
- {pytest_selective-0.3.0 → pytest_selective-0.4.0}/CONTRIBUTING.md +0 -0
- {pytest_selective-0.3.0 → pytest_selective-0.4.0}/LICENSE +0 -0
- {pytest_selective-0.3.0 → pytest_selective-0.4.0}/PROVENANCE.md +0 -0
- {pytest_selective-0.3.0 → pytest_selective-0.4.0}/RELEASING.md +0 -0
- {pytest_selective-0.3.0 → pytest_selective-0.4.0}/actions/README.md +0 -0
- {pytest_selective-0.3.0 → pytest_selective-0.4.0}/actions/pytest-expansion/action.yml +0 -0
- {pytest_selective-0.3.0 → pytest_selective-0.4.0}/agentsync.toml +0 -0
- {pytest_selective-0.3.0 → pytest_selective-0.4.0}/docs/actions.md +0 -0
- {pytest_selective-0.3.0 → pytest_selective-0.4.0}/docs/agents.md +0 -0
- {pytest_selective-0.3.0 → pytest_selective-0.4.0}/docs/assets/banner.png +0 -0
- {pytest_selective-0.3.0 → pytest_selective-0.4.0}/docs/assets/banner.svg +0 -0
- {pytest_selective-0.3.0 → pytest_selective-0.4.0}/docs/assets/brand.json +0 -0
- {pytest_selective-0.3.0 → pytest_selective-0.4.0}/docs/assets/icon.png +0 -0
- {pytest_selective-0.3.0 → pytest_selective-0.4.0}/docs/assets/logo.svg +0 -0
- {pytest_selective-0.3.0 → pytest_selective-0.4.0}/docs/assets/raster-sources.json +0 -0
- {pytest_selective-0.3.0 → pytest_selective-0.4.0}/docs/assets/social-preview.png +0 -0
- {pytest_selective-0.3.0 → pytest_selective-0.4.0}/docs/assets/social-preview.svg +0 -0
- {pytest_selective-0.3.0 → pytest_selective-0.4.0}/docs/benchmarks/2026-10-22-due-ci.json +0 -0
- {pytest_selective-0.3.0 → pytest_selective-0.4.0}/docs/benchmarks/2026-10-22-due-docs-ci.json +0 -0
- {pytest_selective-0.3.0 → pytest_selective-0.4.0}/docs/benchmarks/2026-10-22-due-docs-widget.json +0 -0
- {pytest_selective-0.3.0 → pytest_selective-0.4.0}/docs/benchmarks/2026-10-22-due-widget.json +0 -0
- {pytest_selective-0.3.0 → pytest_selective-0.4.0}/docs/benchmarks/2026-10-22-input-replays.json +0 -0
- {pytest_selective-0.3.0 → pytest_selective-0.4.0}/docs/benchmarks/2026-10-22-obligation-docs.json +0 -0
- {pytest_selective-0.3.0 → pytest_selective-0.4.0}/docs/benchmarks/2026-10-22-obligation.json +0 -0
- {pytest_selective-0.3.0 → pytest_selective-0.4.0}/docs/benchmarks/2026-10-22-routing.md +0 -0
- {pytest_selective-0.3.0 → pytest_selective-0.4.0}/docs/benchmarks/2026-10-22-selective-docs.json +0 -0
- {pytest_selective-0.3.0 → pytest_selective-0.4.0}/docs/benchmarks/2026-10-22.md +0 -0
- {pytest_selective-0.3.0 → pytest_selective-0.4.0}/docs/benchmarks/2026-10-23-windows-saleor/obligation-baseline.json +0 -0
- {pytest_selective-0.3.0 → pytest_selective-0.4.0}/docs/benchmarks/2026-10-23-windows-saleor/selective-after-3.11.xml +0 -0
- {pytest_selective-0.3.0 → pytest_selective-0.4.0}/docs/benchmarks/2026-10-23-windows-saleor/selective-after-3.14.xml +0 -0
- {pytest_selective-0.3.0 → pytest_selective-0.4.0}/docs/benchmarks/2026-10-23-windows-saleor/selective-after.json +0 -0
- {pytest_selective-0.3.0 → pytest_selective-0.4.0}/docs/benchmarks/2026-10-23-windows-saleor/selective-control-3.11.xml +0 -0
- {pytest_selective-0.3.0 → pytest_selective-0.4.0}/docs/benchmarks/2026-10-23-windows-saleor/selective-control-3.14.xml +0 -0
- {pytest_selective-0.3.0 → pytest_selective-0.4.0}/docs/benchmarks/2026-10-23-windows-saleor/selective-control.json +0 -0
- {pytest_selective-0.3.0 → pytest_selective-0.4.0}/docs/benchmarks/2026-10-23-windows-saleor/selective-earlier.json +0 -0
- {pytest_selective-0.3.0 → pytest_selective-0.4.0}/docs/benchmarks/2026-10-23-windows-saleor.md +0 -0
- {pytest_selective-0.3.0 → pytest_selective-0.4.0}/docs/benchmarks/windows-runtime.md +0 -0
- {pytest_selective-0.3.0 → pytest_selective-0.4.0}/docs/ci-toolkit.md +0 -0
- {pytest_selective-0.3.0 → pytest_selective-0.4.0}/docs/comparison-pytest-impacted.md +0 -0
- {pytest_selective-0.3.0 → pytest_selective-0.4.0}/docs/evidence.md +0 -0
- {pytest_selective-0.3.0 → pytest_selective-0.4.0}/docs/local-development.md +0 -0
- {pytest_selective-0.3.0 → pytest_selective-0.4.0}/docs/pytest-hygiene.md +0 -0
- {pytest_selective-0.3.0 → pytest_selective-0.4.0}/docs/work-in-progress/tickets/actions-directory-test-plan.md +0 -0
- {pytest_selective-0.3.0 → pytest_selective-0.4.0}/docs/work-in-progress/tickets/codex-selector-correctness-performance-test-plan.md +0 -0
- {pytest_selective-0.3.0 → pytest_selective-0.4.0}/docs/work-in-progress/tickets/codex-windows-git-performance-test-plan.md +0 -0
- {pytest_selective-0.3.0 → pytest_selective-0.4.0}/docs/work-in-progress/tickets/impacted-improvements-test-plan.md +0 -0
- {pytest_selective-0.3.0 → pytest_selective-0.4.0}/docs/work-in-progress/tickets/move-action-implementations-test-plan.md +0 -0
- {pytest_selective-0.3.0 → pytest_selective-0.4.0}/docs/work-in-progress/tickets/public-action-onboarding-test-plan.md +0 -0
- {pytest_selective-0.3.0 → pytest_selective-0.4.0}/documentation/assets/routing-comparison.svg +0 -0
- {pytest_selective-0.3.0 → pytest_selective-0.4.0}/documentation/check_site.py +0 -0
- {pytest_selective-0.3.0 → pytest_selective-0.4.0}/documentation/ecosystem.md +0 -0
- {pytest_selective-0.3.0 → pytest_selective-0.4.0}/documentation/hooks.py +0 -0
- {pytest_selective-0.3.0 → pytest_selective-0.4.0}/documentation/index.md +0 -0
- {pytest_selective-0.3.0 → pytest_selective-0.4.0}/documentation/overrides/main.html +0 -0
- {pytest_selective-0.3.0 → pytest_selective-0.4.0}/documentation/publishing.md +0 -0
- {pytest_selective-0.3.0 → pytest_selective-0.4.0}/documentation/requirements.in +0 -0
- {pytest_selective-0.3.0 → pytest_selective-0.4.0}/documentation/requirements.txt +0 -0
- {pytest_selective-0.3.0 → pytest_selective-0.4.0}/release-please-config.json +0 -0
- {pytest_selective-0.3.0 → pytest_selective-0.4.0}/scripts/benchmark_graph.py +0 -0
- {pytest_selective-0.3.0 → pytest_selective-0.4.0}/scripts/benchmark_parsing.py +0 -0
- {pytest_selective-0.3.0 → pytest_selective-0.4.0}/scripts/benchmark_rust.py +0 -0
- {pytest_selective-0.3.0 → pytest_selective-0.4.0}/scripts/check_docs.py +0 -0
- {pytest_selective-0.3.0 → pytest_selective-0.4.0}/scripts/check_metadata.py +0 -0
- {pytest_selective-0.3.0 → pytest_selective-0.4.0}/scripts/ci_file_config.py +0 -0
- {pytest_selective-0.3.0 → pytest_selective-0.4.0}/scripts/compare_impacted.py +0 -0
- {pytest_selective-0.3.0 → pytest_selective-0.4.0}/scripts/measure_ci_runs.py +0 -0
- {pytest_selective-0.3.0 → pytest_selective-0.4.0}/scripts/public_actions.py +0 -0
- {pytest_selective-0.3.0 → pytest_selective-0.4.0}/scripts/render_brand.py +0 -0
- {pytest_selective-0.3.0 → pytest_selective-0.4.0}/scripts/run_ci_tests.py +0 -0
- {pytest_selective-0.3.0 → pytest_selective-0.4.0}/scripts/smoke_distribution.py +0 -0
- {pytest_selective-0.3.0 → pytest_selective-0.4.0}/scripts/tutorial.py +0 -0
- {pytest_selective-0.3.0 → pytest_selective-0.4.0}/skills/pytest-selective/agents/openai.yaml +0 -0
- {pytest_selective-0.3.0 → pytest_selective-0.4.0}/src/pytest_selective/__init__.py +0 -0
- {pytest_selective-0.3.0 → pytest_selective-0.4.0}/src/pytest_selective/__main__.py +0 -0
- {pytest_selective-0.3.0 → pytest_selective-0.4.0}/src/pytest_selective/ast_imports.py +0 -0
- {pytest_selective-0.3.0 → pytest_selective-0.4.0}/src/pytest_selective/hygiene.py +0 -0
- {pytest_selective-0.3.0 → pytest_selective-0.4.0}/src/pytest_selective/impact_audit.py +0 -0
- {pytest_selective-0.3.0 → pytest_selective-0.4.0}/src/pytest_selective/impact_audit_plugin.py +0 -0
- {pytest_selective-0.3.0 → pytest_selective-0.4.0}/src/pytest_selective/impact_declarations.py +0 -0
- {pytest_selective-0.3.0 → pytest_selective-0.4.0}/src/pytest_selective/impact_explanation.py +0 -0
- {pytest_selective-0.3.0 → pytest_selective-0.4.0}/src/pytest_selective/impact_first_party.py +0 -0
- {pytest_selective-0.3.0 → pytest_selective-0.4.0}/src/pytest_selective/impact_graph.py +0 -0
- {pytest_selective-0.3.0 → pytest_selective-0.4.0}/src/pytest_selective/impact_models.py +0 -0
- {pytest_selective-0.3.0 → pytest_selective-0.4.0}/src/pytest_selective/impact_name_scope.py +0 -0
- {pytest_selective-0.3.0 → pytest_selective-0.4.0}/src/pytest_selective/impact_selection.py +0 -0
- {pytest_selective-0.3.0 → pytest_selective-0.4.0}/src/pytest_selective/input_policy.py +0 -0
- {pytest_selective-0.3.0 → pytest_selective-0.4.0}/src/pytest_selective/local.py +0 -0
- {pytest_selective-0.3.0 → pytest_selective-0.4.0}/src/pytest_selective/parse_batch.py +0 -0
- {pytest_selective-0.3.0 → pytest_selective-0.4.0}/src/pytest_selective/plugin.py +0 -0
- {pytest_selective-0.3.0 → pytest_selective-0.4.0}/src/pytest_selective/py.typed +0 -0
- {pytest_selective-0.3.0 → pytest_selective-0.4.0}/src/pytest_selective/pytest_expansion.py +0 -0
- {pytest_selective-0.3.0 → pytest_selective-0.4.0}/src/pytest_selective/pytest_plugins.py +0 -0
- {pytest_selective-0.3.0 → pytest_selective-0.4.0}/src/pytest_selective/pytest_sharding.py +0 -0
- {pytest_selective-0.3.0 → pytest_selective-0.4.0}/src/pytest_selective/test_inventory.py +0 -0
- {pytest_selective-0.3.0 → pytest_selective-0.4.0}/src/pytest_selective/typecheck_scope.py +0 -0
- {pytest_selective-0.3.0 → pytest_selective-0.4.0}/src/pytest_selective/working_tree.py +0 -0
- {pytest_selective-0.3.0 → pytest_selective-0.4.0}/tests/__init__.py +0 -0
- {pytest_selective-0.3.0 → pytest_selective-0.4.0}/tests/test_ci_measurements.py +0 -0
- {pytest_selective-0.3.0 → pytest_selective-0.4.0}/tests/test_ci_test_execution.py +0 -0
- {pytest_selective-0.3.0 → pytest_selective-0.4.0}/tests/test_distribution_metadata.py +0 -0
- {pytest_selective-0.3.0 → pytest_selective-0.4.0}/tests/test_hygiene.py +0 -0
- {pytest_selective-0.3.0 → pytest_selective-0.4.0}/tests/test_public_actions.py +0 -0
- {pytest_selective-0.3.0 → pytest_selective-0.4.0}/tests/test_pytest_expansion.py +0 -0
- {pytest_selective-0.3.0 → pytest_selective-0.4.0}/tests/test_repository_hygiene.py +0 -0
- {pytest_selective-0.3.0 → pytest_selective-0.4.0}/tests/test_repository_hygiene.py.gv-dependency.yaml +0 -0
- {pytest_selective-0.3.0 → pytest_selective-0.4.0}/tests/test_tutorial.py +0 -0
- {pytest_selective-0.3.0 → pytest_selective-0.4.0}/tests/testing/__init__.py +0 -0
- {pytest_selective-0.3.0 → pytest_selective-0.4.0}/tests/testing/git_repo.py +0 -0
- {pytest_selective-0.3.0 → pytest_selective-0.4.0}/tests/testing/test_ast_imports.py +0 -0
- {pytest_selective-0.3.0 → pytest_selective-0.4.0}/tests/testing/test_ci.py +0 -0
- {pytest_selective-0.3.0 → pytest_selective-0.4.0}/tests/testing/test_git_repo.py +0 -0
- {pytest_selective-0.3.0 → pytest_selective-0.4.0}/tests/testing/test_global_inputs.py +0 -0
- {pytest_selective-0.3.0 → pytest_selective-0.4.0}/tests/testing/test_impact_audit.py +0 -0
- {pytest_selective-0.3.0 → pytest_selective-0.4.0}/tests/testing/test_impact_explanation.py +0 -0
- {pytest_selective-0.3.0 → pytest_selective-0.4.0}/tests/testing/test_impact_first_party.py +0 -0
- {pytest_selective-0.3.0 → pytest_selective-0.4.0}/tests/testing/test_impact_graph.py +0 -0
- {pytest_selective-0.3.0 → pytest_selective-0.4.0}/tests/testing/test_impact_name_scope.py +0 -0
- {pytest_selective-0.3.0 → pytest_selective-0.4.0}/tests/testing/test_parse_batch.py +0 -0
- {pytest_selective-0.3.0 → pytest_selective-0.4.0}/tests/testing/test_portability.py +0 -0
- {pytest_selective-0.3.0 → pytest_selective-0.4.0}/tests/testing/test_pytest_plugin.py +0 -0
- {pytest_selective-0.3.0 → pytest_selective-0.4.0}/tests/testing/test_pytest_plugins.py +0 -0
- {pytest_selective-0.3.0 → pytest_selective-0.4.0}/tests/testing/test_pytest_sharding.py +0 -0
- {pytest_selective-0.3.0 → pytest_selective-0.4.0}/tests/testing/test_typecheck_scope.py +0 -0
- {pytest_selective-0.3.0 → pytest_selective-0.4.0}/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,12 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## [0.4.0](https://github.com/gigaverse-app/pytest-selective/compare/v0.3.0...v0.4.0) (2026-10-24)
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
### Features
|
|
7
|
+
|
|
8
|
+
* **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))
|
|
9
|
+
|
|
3
10
|
## [0.3.0](https://github.com/gigaverse-app/pytest-selective/compare/v0.2.0...v0.3.0) (2026-10-23)
|
|
4
11
|
|
|
5
12
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.5
|
|
2
2
|
Name: pytest-selective
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.4.0
|
|
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 selects on PRs/pushes and runs full on reruns, manual/scheduled/release events or test:full. Override
|
|
22
|
+
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: ''
|
|
@@ -68,6 +69,9 @@ outputs:
|
|
|
68
69
|
test_roots:
|
|
69
70
|
description: Exact selected files, or complete roots on fallback.
|
|
70
71
|
value: ${{ steps.action.outputs.test_roots }}
|
|
72
|
+
test_roots_json:
|
|
73
|
+
description: JSON array of selected files or fallback roots; preserves spaces in paths. Empty array for none.
|
|
74
|
+
value: ${{ steps.action.outputs.test_roots_json }}
|
|
71
75
|
shard_count:
|
|
72
76
|
description: Number of shards.
|
|
73
77
|
value: ${{ steps.action.outputs.shard_count }}
|
|
@@ -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 selects on PRs/pushes and runs full on reruns, manual/scheduled/release events 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 selects on PRs/pushes and runs full on reruns, manual/scheduled/release events 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: ''
|
|
@@ -70,6 +70,9 @@ outputs:
|
|
|
70
70
|
test_roots:
|
|
71
71
|
value: ${{ steps.select.outputs.test_roots }}
|
|
72
72
|
description: Exact selected files, or complete roots on fallback.
|
|
73
|
+
test_roots_json:
|
|
74
|
+
value: ${{ steps.select.outputs.test_roots_json }}
|
|
75
|
+
description: JSON array of selected files or fallback roots; preserves spaces in paths. Empty array for none.
|
|
73
76
|
shard_count:
|
|
74
77
|
value: ${{ steps.select.outputs.shard_count }}
|
|
75
78
|
description: Number of shards.
|
|
@@ -139,7 +142,7 @@ runs:
|
|
|
139
142
|
--explanation-output test-impact-explanation.json)
|
|
140
143
|
# A reused workspace must not upload a previous plan's explanation.
|
|
141
144
|
rm -f test-impact-explanation.json
|
|
142
|
-
|
|
145
|
+
args+=(--full "$IMPACT_FULL")
|
|
143
146
|
if [ -n "$IMPACT_EXTRA" ]; then args+=(--extra-files "$IMPACT_EXTRA"); fi
|
|
144
147
|
if [ -n "${IMPACT_GRAPH_BASE:-}" ]; then args+=(--graph-base "$IMPACT_GRAPH_BASE"); fi
|
|
145
148
|
# Optional: an unfetchable alternate only forgoes a smaller selection.
|
|
@@ -170,6 +173,7 @@ runs:
|
|
|
170
173
|
echo "base=$IMPACT_BASE"
|
|
171
174
|
echo "mode=full"
|
|
172
175
|
echo "test_roots=$IMPACT_ROOTS"
|
|
176
|
+
echo "test_roots_json=$(python3 -c 'import json,os,shlex; print(json.dumps(shlex.split(os.environ["IMPACT_ROOTS"])))')"
|
|
173
177
|
echo "shard_count=$IMPACT_SHARDS"
|
|
174
178
|
echo "groups=$(python3 -c 'import json,os; print(json.dumps(list(range(1,int(os.environ["IMPACT_SHARDS"])+1))))')"
|
|
175
179
|
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) |
|
|
@@ -8,8 +8,8 @@ the base forward after a failed run.
|
|
|
8
8
|
|
|
9
9
|
This job uses the repository's composite action and runs the plan on the checked-out
|
|
10
10
|
head. Use the moving `v1` release tag and adapt dependency installation to
|
|
11
|
-
your project. The example assumes a uv project
|
|
12
|
-
|
|
11
|
+
your project. The example assumes a uv project and passes the planned paths as
|
|
12
|
+
JSON, so spaces in filenames survive execution.
|
|
13
13
|
|
|
14
14
|
```yaml
|
|
15
15
|
name: Affected tests
|
|
@@ -28,26 +28,44 @@ jobs:
|
|
|
28
28
|
uses: gigaverse-app/pytest-selective@v1
|
|
29
29
|
with:
|
|
30
30
|
config: .github/test-impact.json
|
|
31
|
-
base: ${{ github.event.pull_request.base.sha }}
|
|
32
31
|
full-roots: tests
|
|
33
32
|
- if: steps.impact.outputs.mode != 'none'
|
|
34
33
|
run: uv sync --locked
|
|
35
34
|
- if: steps.impact.outputs.mode != 'none'
|
|
36
35
|
env:
|
|
37
|
-
TEST_ROOTS: ${{ steps.impact.outputs.
|
|
36
|
+
TEST_ROOTS: ${{ steps.impact.outputs.test_roots_json }}
|
|
38
37
|
shell: bash
|
|
39
38
|
run: |
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
39
|
+
uv run --locked python - <<'PY'
|
|
40
|
+
import json, os, subprocess, sys
|
|
41
|
+
roots = json.loads(os.environ['TEST_ROOTS'])
|
|
42
|
+
if not roots:
|
|
43
|
+
raise SystemExit('Expected test roots for a nonempty plan')
|
|
44
|
+
raise SystemExit(subprocess.call([sys.executable, '-m', 'pytest', '-p', 'no:selective', *roots]))
|
|
45
|
+
PY
|
|
46
46
|
```
|
|
47
47
|
|
|
48
48
|
The action creates its own Python 3.14 planner environment. Your uv project
|
|
49
49
|
controls the test interpreter. `full-roots` must contain the complete roots, not
|
|
50
|
-
the previous selection.
|
|
50
|
+
the previous selection. `base` defaults to the PR base or the push's previous
|
|
51
|
+
commit. Override it only when you have validated a different comparison base.
|
|
52
|
+
|
|
53
|
+
Both file and lane actions default to `full: auto`:
|
|
54
|
+
|
|
55
|
+
| Trigger | Default |
|
|
56
|
+
| --- | --- |
|
|
57
|
+
| First ordinary PR or push attempt | Select affected work |
|
|
58
|
+
| Rerun, manual, schedule or release | Full checkpoint |
|
|
59
|
+
| PR carrying `test:full` | Full checkpoint |
|
|
60
|
+
| Unknown event or unreadable required event metadata | Full checkpoint |
|
|
61
|
+
|
|
62
|
+
Use `full: 'true'` to always run everything, or `full: 'false'` to request
|
|
63
|
+
selection even for a manual experiment. Operational uncertainty still falls back
|
|
64
|
+
to full execution. Existing explicit expressions keep their behavior; remove a
|
|
65
|
+
copied standard expression to adopt the shared policy. Updating an action does
|
|
66
|
+
not add missing jobs, schedules, permissions or test dependency declarations.
|
|
67
|
+
GitHub reusable workflows retain the caller's event, so pass `full: 'true'` when
|
|
68
|
+
the reusable invocation itself must be a checkpoint.
|
|
51
69
|
|
|
52
70
|
| Planner outcome | Action behavior |
|
|
53
71
|
| --- | --- |
|
|
@@ -193,8 +211,8 @@ when the README is embedded in the built package.
|
|
|
193
211
|
The action returns a `lanes` JSON object. Expose it as a planning-job output,
|
|
194
212
|
then gate each expensive job with `always() && !cancelled()` and either a true
|
|
195
213
|
lane value **or a failed planning job**. This preserves tests if planning fails.
|
|
196
|
-
|
|
197
|
-
full
|
|
214
|
+
The default event policy handles manual/release checkpoints, schedules and
|
|
215
|
+
`test:full`. Ordinary PRs and main pushes use dependency selection.
|
|
198
216
|
Selected jobs retain all of their existing tests and evidence checks.
|
|
199
217
|
|
|
200
218
|
|
|
@@ -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+,
|
|
@@ -0,0 +1,244 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Get the most benefit from pytest-selective
|
|
3
|
+
description: A staged adoption tutorial and checklist for local pytest, pre-collection planning, CI lanes, dependency audits, hygiene, timing feedback and measured rollout.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Get the most benefit from pytest-selective
|
|
7
|
+
|
|
8
|
+
Choose the level that avoids your actual cost. The native plugin avoids
|
|
9
|
+
unaffected test execution. The pre-collection runner also avoids importing and
|
|
10
|
+
collecting unrelated files. Whole-lane CI planning can avoid starting runners,
|
|
11
|
+
services and application installation altogether.
|
|
12
|
+
|
|
13
|
+
Use this checklist in your adoption PR. Mark each stage **verified**, **deferred**
|
|
14
|
+
or **not applicable**, with a reason. Pair it with the
|
|
15
|
+
[CI-esta adoption checklist](https://github.com/gigaverse-app/ci-esta/blob/6a694957568c81929a2b3021064ad9674516fb7b/docs/tutorials/adoption-checklist.md)
|
|
16
|
+
for reporting, setup, sharding, history, evidence reuse and security integration.
|
|
17
|
+
Selection remains opt-in; installing the package alone does not narrow pytest.
|
|
18
|
+
|
|
19
|
+
## 1. Run the disposable example
|
|
20
|
+
|
|
21
|
+
Use Python 3.11+ and Git 2.24+. Plan with an interpreter at least as new as your
|
|
22
|
+
source syntax; the planner need not install your application dependencies.
|
|
23
|
+
|
|
24
|
+
```bash
|
|
25
|
+
git clone https://github.com/gigaverse-app/pytest-selective.git
|
|
26
|
+
cd pytest-selective
|
|
27
|
+
uv sync --locked
|
|
28
|
+
uv run python scripts/tutorial.py --output tutorial-report.json
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
- [ ] The transitive regression selects its reader and catches the introduced failure.
|
|
32
|
+
- [ ] An unrelated README selects no tests and does not launch pytest accidentally.
|
|
33
|
+
- [ ] A shared conftest change retains the full configured inventory.
|
|
34
|
+
- [ ] The tutorial exits successfully despite its intentionally failing pytest controls.
|
|
35
|
+
|
|
36
|
+
**Done when:** you understand the difference between `scoped`, `none` and `full`
|
|
37
|
+
plans. This demonstrates behavior, not a wall-time speedup. The script uses a
|
|
38
|
+
temporary Git repository; it does not alter your project or staging area.
|
|
39
|
+
|
|
40
|
+
## 2. Choose local collection or pre-collection execution
|
|
41
|
+
|
|
42
|
+
In your application repository, install the selector in the test environment:
|
|
43
|
+
|
|
44
|
+
```bash
|
|
45
|
+
uv add --dev pytest-selective
|
|
46
|
+
uv run pytest --selective
|
|
47
|
+
uv run pytest --no-selective
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
Commit the dependency and lockfile change through your normal workflow before
|
|
51
|
+
trying a narrow source edit. Uncommitted environment changes intentionally
|
|
52
|
+
select everything and would hide the benefit you are trying to verify.
|
|
53
|
+
|
|
54
|
+
The plugin collects normally and then deselects. Its default includes staged,
|
|
55
|
+
unstaged and nonignored untracked edits relative to HEAD. An explicit
|
|
56
|
+
`--selective-base origin/main` also includes changes since that exact ref.
|
|
57
|
+
Keep normal test paths, marker filters, fixtures and assertion plugins.
|
|
58
|
+
|
|
59
|
+
If collection or startup is expensive, create `.github/test-impact.json`:
|
|
60
|
+
|
|
61
|
+
```json
|
|
62
|
+
{
|
|
63
|
+
"module_roots": ["src", "."],
|
|
64
|
+
"test_roots": ["tests"]
|
|
65
|
+
}
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
Review and record [collection-policy fingerprints](../configuration.md#review-and-record-collection-policy)
|
|
69
|
+
before relying on selection in a repository with pytest configuration. Then use:
|
|
70
|
+
|
|
71
|
+
```bash
|
|
72
|
+
uv run pytest-selective run --config .github/test-impact.json -- -q
|
|
73
|
+
uv run pytest-selective run --config .github/test-impact.json --full -- -q
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
- [ ] Inventory every intended test root and any custom filename/collection policy.
|
|
77
|
+
- [ ] Check both local edits and committed base/head changes.
|
|
78
|
+
- [ ] Use the native plugin for custom collection/node-oriented workflows unless
|
|
79
|
+
you have verified that pre-collection file planning models them correctly.
|
|
80
|
+
- [ ] Keep explicit full execution for whole-suite coverage and conformance requirements.
|
|
81
|
+
- [ ] Do not enable selection globally while validating adoption.
|
|
82
|
+
|
|
83
|
+
**Done when:** an actual small source edit runs the intended tests, while a full
|
|
84
|
+
control still passes. **Rollback:** `--no-selective` for the plugin or `run --full`
|
|
85
|
+
for the runner. Remove a global selective setting if your project previously added one.
|
|
86
|
+
|
|
87
|
+
## 3. Declare dependencies that Python imports cannot reveal
|
|
88
|
+
|
|
89
|
+
Follow the [reader-owned sidecar tutorial](sidecar-dependencies.md). For example,
|
|
90
|
+
place this beside the test that reads the fixture, as `test_prices.py.gv-dependency.yaml`:
|
|
91
|
+
|
|
92
|
+
```yaml
|
|
93
|
+
dependencies:
|
|
94
|
+
- inputs: ["//tests/data/prices.json"]
|
|
95
|
+
kind: content
|
|
96
|
+
reason: The test reads expected prices from this JSON fixture.
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
- [ ] Declare templates, fixtures, schema files, AST/text scans and subprocess helpers.
|
|
100
|
+
- [ ] Use `content` for bytes read as data, and `import` when the reader executes
|
|
101
|
+
Python and needs that module's transitive dependencies.
|
|
102
|
+
- [ ] Keep declarations beside readers; use central rules for actual shared policy.
|
|
103
|
+
- [ ] Review locks, environment configuration, external checkout pins and plugin startup.
|
|
104
|
+
- [ ] Retain default global inputs until a narrower environment contract is verified.
|
|
105
|
+
- [ ] Review nested/custom pytest configuration before refreshing fingerprints.
|
|
106
|
+
- [ ] Repair unmatched paths, invalid sidecars and duplicate keys; do not suppress them.
|
|
107
|
+
|
|
108
|
+
Inspect the plan before executing it:
|
|
109
|
+
|
|
110
|
+
```bash
|
|
111
|
+
uv run pytest-selective affected --config .github/test-impact.json \
|
|
112
|
+
--base origin/main --working-tree --output selection.json \
|
|
113
|
+
--explanation-output explanation.json
|
|
114
|
+
uv run pytest-selective explain --config .github/test-impact.json \
|
|
115
|
+
--test tests/test_prices.py --source src/shop/prices.py
|
|
116
|
+
```
|
|
117
|
+
|
|
118
|
+
Adapt the test/source paths. **Done when:** real edits to each declared input
|
|
119
|
+
select its readers, and an unrelated file does not broaden the plan. Deleted
|
|
120
|
+
inputs and both sides of a rename belong in your controls.
|
|
121
|
+
|
|
122
|
+
## 4. Move CI selection ahead of expensive setup
|
|
123
|
+
|
|
124
|
+
Use the [complete CI recipe](../ci.md), or the
|
|
125
|
+
[whole-lane tutorial](skip-unrelated-ci.md) when a suite must run intact.
|
|
126
|
+
|
|
127
|
+
| Interface | Choose it when | Override / contract |
|
|
128
|
+
| --- | --- | --- |
|
|
129
|
+
| `gigaverse-app/pytest-selective@v1` | Plan ordinary pytest files before collection | Root convenience entry point; same implementation as `actions/test-impact` |
|
|
130
|
+
| `actions/test-impact@v1` | Use the direct action, including runners without root self-reference support | Shared `full: auto` checkpoints; consume `mode` and `test_roots_json` without reparsing reports |
|
|
131
|
+
| `actions/plan-lanes@v1` | Skip complete unaffected jobs while preserving coverage/conformance | Shared `full: auto` checkpoints; `lanes` is a boolean JSON map |
|
|
132
|
+
| `actions/pytest-expansion@v1` | Reject unexplained global conftest expansion before application setup | Configure roots/prefixes to your project; this does not itself run tests |
|
|
133
|
+
|
|
134
|
+
Specialized action paths above are relative to `gigaverse-app/pytest-selective/`.
|
|
135
|
+
See every input and output in the [action reference](../actions.md). Python
|
|
136
|
+
package versions, the moving Actions major and installed skills update separately.
|
|
137
|
+
|
|
138
|
+
- [ ] Collect a complete change set with enough Git history before selecting.
|
|
139
|
+
- [ ] Model lane execution inputs, including called actions/reusable workflows.
|
|
140
|
+
- [ ] Avoid broad `.github/**` globals when only execution workflows affect tests.
|
|
141
|
+
- [ ] Consume one successful plan consistently across matrix workers; do not run
|
|
142
|
+
a second hidden selector against an already selected exact-file manifest.
|
|
143
|
+
- [ ] Keep services, coverage and evidence validators intact inside admitted lanes.
|
|
144
|
+
- [ ] Do not invoke bare pytest on a `none` plan; it runs the whole suite.
|
|
145
|
+
- [ ] Missing history/operational uncertainty runs the full configured roots.
|
|
146
|
+
Invalid declarations remain visible failures to repair.
|
|
147
|
+
- [ ] Make the planner required and validate skips through an aggregate gate.
|
|
148
|
+
- [ ] Keep ordinary PRs and main pushes selective, with explicit full/manual and
|
|
149
|
+
scheduled checkpoints. Verify label-trigger behavior instead of assuming it.
|
|
150
|
+
|
|
151
|
+
**Done when:** the acceptance matrix below passes using actual commits and the
|
|
152
|
+
real workflow. A local plan alone does not establish which hosted jobs execute.
|
|
153
|
+
|
|
154
|
+
| Control | Expected result |
|
|
155
|
+
| --- | --- |
|
|
156
|
+
| Unreferenced README or PR-template change | Zero tests, including routing, tutorial and installed-package smoke lanes unless they declare that document as an input; metadata/docs checks may still run |
|
|
157
|
+
| Source or transitive helper | All affected reader tests or complete lanes run |
|
|
158
|
+
| Fixture, sidecar, subprocess entry point | Declared readers run |
|
|
159
|
+
| Shared conftest, lock, collection policy or execution workflow | Required broad/full work runs or the policy fails visibly for review |
|
|
160
|
+
| Deleted input / renamed module | Dependents from both Git trees remain covered |
|
|
161
|
+
| Missing history | Full execution, with the fallback explained |
|
|
162
|
+
| Failed planner / invalid configuration | Cannot yield a green skipped required gate |
|
|
163
|
+
| Explicit full override | Complete configured suite and existing validators run |
|
|
164
|
+
|
|
165
|
+
## 5. Keep the graph narrow and check its blind spots
|
|
166
|
+
|
|
167
|
+
Run a representative full workload with the optional file-read recorder, then
|
|
168
|
+
audit the resulting records:
|
|
169
|
+
|
|
170
|
+
```bash
|
|
171
|
+
uv run pytest --no-selective -p pytest_selective.impact_audit_plugin --impact-audit-dir impact-audit
|
|
172
|
+
uv run pytest-selective audit --config .github/test-impact.json --records impact-audit
|
|
173
|
+
```
|
|
174
|
+
|
|
175
|
+
- [ ] Declare missing observed inputs at their reader and repeat the affected route.
|
|
176
|
+
- [ ] Keep full checkpoints for paths the recorder has not observed.
|
|
177
|
+
- [ ] Use `explain`, `dependencies` and `conftest-impact` to investigate unexpectedly broad selections.
|
|
178
|
+
- [ ] Move helpers/fixtures to the narrowest consuming scope before accepting a new startup floor.
|
|
179
|
+
- [ ] Review and set `floor_budget`; verify a deliberately excessive floor fails visibly.
|
|
180
|
+
- [ ] Add the [expansion and test-file isolation guards](../pytest-hygiene.md)
|
|
181
|
+
where needed; do not hide new global dependencies behind unexplained exceptions.
|
|
182
|
+
- [ ] Consider `warm` only after measuring graph construction; cache misses must
|
|
183
|
+
rebuild safely and must never become proof that tests passed.
|
|
184
|
+
|
|
185
|
+
Use the [selection-health guide](../selection-health.md) for complete commands.
|
|
186
|
+
Audits observe executed paths; they cannot prove that every possible runtime
|
|
187
|
+
dependency has been declared. Keep intentional external-state checks separate.
|
|
188
|
+
|
|
189
|
+
## 6. Balance selected work without changing eligibility
|
|
190
|
+
|
|
191
|
+
Use [CI-esta timing retention](https://github.com/gigaverse-app/ci-esta/blob/main/docs/learned-test-timings.md)
|
|
192
|
+
when partial whole-file runs would otherwise erase useful costs. CI-esta owns
|
|
193
|
+
the lifecycle; pytest-selective owns inventory and partitioning.
|
|
194
|
+
|
|
195
|
+
- [ ] Use separate timing histories per environment and fixed marker lane.
|
|
196
|
+
- [ ] Validate complete files and all expected reports before learning.
|
|
197
|
+
- [ ] Restore once, then freeze the plan or duration input for every worker.
|
|
198
|
+
- [ ] Verify that every selected file is assigned exactly once and unknown files remain included.
|
|
199
|
+
- [ ] Compare existing xdist with proposed shard/pool layouts including startup and service costs.
|
|
200
|
+
- [ ] Keep timing retention, job evidence reuse and test eligibility as separate opt-ins.
|
|
201
|
+
|
|
202
|
+
Setting `--shard-seconds`, `--max-shards` or a duration manifest changes scheduling
|
|
203
|
+
estimates, not the dependency contract. A case-filtered local run may be valid for
|
|
204
|
+
the plugin while still being unsuitable as a complete-file timing observation.
|
|
205
|
+
|
|
206
|
+
## 7. Know which commands and scripts to use
|
|
207
|
+
|
|
208
|
+
| Surface | Purpose / boundary |
|
|
209
|
+
| --- | --- |
|
|
210
|
+
| `pytest --selective`, `--no-selective`; `pytest-selective run` | Local execution, including uncommitted changes; choose collection behavior explicitly. |
|
|
211
|
+
| `affected`, `explain`, `dependencies`, `conftest-impact` | Plan and inspect dependency routes; preserve structured explanations for review. |
|
|
212
|
+
| `fingerprints`, `floor-budget`, `audit`, `warm` | Collection-policy review, breadth budgets, observed-read checks and cache preparation. A fingerprint refresh is a review step, not an automatic fix for every fallback. |
|
|
213
|
+
| `typecheck-scope` | Scope type checking with its own checker/global-input contract; retain a full fallback. Inspect `pytest-selective typecheck-scope --help` and the [scope implementation](../../src/pytest_selective/typecheck_scope.py). |
|
|
214
|
+
| `python -m pytest_selective.ci` | Whole-lane planning outside the GitHub action; the [CI recipe](../ci.md) documents its JSON and full override. There is no `pytest-selective lanes` command. |
|
|
215
|
+
| `pytest-selective-expansion` | Standalone expansion guard; use the [hygiene recipe](../pytest-hygiene.md). |
|
|
216
|
+
| [scripts/tutorial.py](../../scripts/tutorial.py) | Executable Git/selection/pytest example used in stage 1. |
|
|
217
|
+
| [scripts/benchmark_graph.py](../../scripts/benchmark_graph.py) | Compare cold, warm and incremental graph construction on fixed revisions. Its in-memory fingerprint refresh is for measurement, not adoption approval. |
|
|
218
|
+
| [scripts/benchmark_parsing.py](../../scripts/benchmark_parsing.py), [benchmark_rust.py](../../scripts/benchmark_rust.py) | Maintainer parser experiments; they do not enable a production alternate parser or establish hosted savings. |
|
|
219
|
+
| [scripts/compare_impacted.py](../../scripts/compare_impacted.py) | Optional comparison experiment; needs a separately installed pytest-impacted revision. Not a runtime dependency. |
|
|
220
|
+
| [scripts/measure_ci_runs.py](../../scripts/measure_ci_runs.py) | Compare recorded GitHub runs with explicit identities; inspect `--help` and the [measurement limits](../evidence.md). |
|
|
221
|
+
| [scripts/ci_file_config.py](../../scripts/ci_file_config.py), [run_ci_tests.py](../../scripts/run_ci_tests.py) | This toolkit's CI adapters; consumers normally use the public action/runner, not copied wrappers. |
|
|
222
|
+
| [scripts/public_actions.py](../../scripts/public_actions.py), [check_docs.py](../../scripts/check_docs.py), [check_metadata.py](../../scripts/check_metadata.py), [smoke_distribution.py](../../scripts/smoke_distribution.py), [render_brand.py](../../scripts/render_brand.py); `documentation/*.py` | Toolkit generation, documentation and release validation. These are maintainer tools, not extra adopter jobs. |
|
|
223
|
+
|
|
224
|
+
The Python graph/configuration models, inventory, sharding and hygiene helpers
|
|
225
|
+
are available for integrations, but reuse the public runner/actions for ordinary
|
|
226
|
+
adoption. Do not copy selection logic or create a second dependency declaration
|
|
227
|
+
format in an application helper.
|
|
228
|
+
|
|
229
|
+
## 8. Measure and hand over an adoption that stays useful
|
|
230
|
+
|
|
231
|
+
- [ ] Compare successful full and selected runs on the same source head and tested
|
|
232
|
+
checkout, with unchanged environment and required checks.
|
|
233
|
+
- [ ] Record planning, collection, installation, services, artifacts and reporting
|
|
234
|
+
alongside pytest execution. Fewer files does not by itself mean lower latency.
|
|
235
|
+
- [ ] Retain run/attempt IDs, elapsed seconds and summed runner-seconds; repeat
|
|
236
|
+
before claiming an average. Keep cancelled experiments visible separately.
|
|
237
|
+
- [ ] Record configuration assumptions, full-run override, rollback and ownership.
|
|
238
|
+
- [ ] Route the adoption regression tests through their declared real inputs.
|
|
239
|
+
- [ ] Link these results from the PR and update your installed
|
|
240
|
+
[agent skill](../../skills/pytest-selective/SKILL.md) separately from package/action pins.
|
|
241
|
+
|
|
242
|
+
Use [CI-esta's measurement recipe](https://github.com/gigaverse-app/ci-esta/blob/main/docs/measured-impact.md)
|
|
243
|
+
to report the result. Installing a newer selector does not automatically configure
|
|
244
|
+
lane routing, runtime audits, hygiene policies, timing retention or evidence reuse.
|