pytest-selective 1.1.1__tar.gz → 1.2.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-1.1.1 → pytest_selective-1.2.0}/.github/workflows/ci.yml +4 -3
- pytest_selective-1.2.0/.github/workflows/dry-lint-audit.yml +23 -0
- pytest_selective-1.2.0/.release-please-manifest.json +3 -0
- {pytest_selective-1.1.1 → pytest_selective-1.2.0}/CHANGELOG.md +14 -0
- {pytest_selective-1.1.1 → pytest_selective-1.2.0}/CONTRIBUTING.md +5 -3
- {pytest_selective-1.1.1 → pytest_selective-1.2.0}/PKG-INFO +5 -5
- {pytest_selective-1.1.1 → pytest_selective-1.2.0}/README.md +4 -4
- {pytest_selective-1.1.1 → pytest_selective-1.2.0}/actions/plan-lanes/action.yml +4 -2
- {pytest_selective-1.1.1 → pytest_selective-1.2.0}/contracts/selection/conftest.py +2 -1
- {pytest_selective-1.1.1 → pytest_selective-1.2.0}/contracts/selection/runtime_deps.py +4 -0
- {pytest_selective-1.1.1 → pytest_selective-1.2.0}/docs/actions.md +4 -0
- {pytest_selective-1.1.1 → pytest_selective-1.2.0}/docs/ci.md +3 -4
- {pytest_selective-1.1.1 → pytest_selective-1.2.0}/docs/configuration.md +53 -5
- {pytest_selective-1.1.1 → pytest_selective-1.2.0}/docs/selection-health.md +2 -1
- pytest_selective-1.2.0/docs/shared-plan-execution.md +43 -0
- {pytest_selective-1.1.1 → pytest_selective-1.2.0}/pyproject.toml +1 -1
- {pytest_selective-1.1.1 → pytest_selective-1.2.0}/scripts/ci_file_config.py +1 -8
- {pytest_selective-1.1.1 → pytest_selective-1.2.0}/scripts/measure_ci_runs.py +4 -0
- pytest_selective-1.2.0/scripts/run_ci_tests.py +8 -0
- {pytest_selective-1.1.1 → pytest_selective-1.2.0}/skills/pytest-selective/SKILL.md +137 -81
- {pytest_selective-1.1.1 → pytest_selective-1.2.0}/src/pytest_selective/ci.py +8 -0
- pytest_selective-1.1.1/scripts/run_ci_tests.py → pytest_selective-1.2.0/src/pytest_selective/execution.py +18 -3
- {pytest_selective-1.1.1 → pytest_selective-1.2.0}/src/pytest_selective/impact_audit.py +9 -4
- pytest_selective-1.2.0/src/pytest_selective/impact_audit_declarations.py +148 -0
- {pytest_selective-1.1.1 → pytest_selective-1.2.0}/src/pytest_selective/impact_cli.py +58 -3
- {pytest_selective-1.1.1 → pytest_selective-1.2.0}/src/pytest_selective/impact_graph.py +148 -22
- {pytest_selective-1.1.1 → pytest_selective-1.2.0}/src/pytest_selective/impact_models.py +21 -0
- {pytest_selective-1.1.1 → pytest_selective-1.2.0}/tests/test_ci_admission.py +1 -0
- {pytest_selective-1.1.1 → pytest_selective-1.2.0}/tests/test_ci_test_execution.py +5 -5
- {pytest_selective-1.1.1 → pytest_selective-1.2.0}/tests/test_public_actions.py +8 -0
- {pytest_selective-1.1.1 → pytest_selective-1.2.0}/tests/test_repository_ci.py +28 -3
- {pytest_selective-1.1.1 → pytest_selective-1.2.0}/tests/testing/test_impact_audit.py +34 -0
- pytest_selective-1.2.0/tests/testing/test_impact_audit_declarations.py +114 -0
- {pytest_selective-1.1.1 → pytest_selective-1.2.0}/tests/testing/test_impact_graph.py +100 -0
- {pytest_selective-1.1.1 → pytest_selective-1.2.0}/uv.lock +1 -1
- pytest_selective-1.1.1/.github/workflows/dry-lint-audit.yml +0 -91
- pytest_selective-1.1.1/.release-please-manifest.json +0 -3
- {pytest_selective-1.1.1 → pytest_selective-1.2.0}/.dry-lint/baseline.json +0 -0
- {pytest_selective-1.1.1 → pytest_selective-1.2.0}/.dry-lint/progress.json +0 -0
- {pytest_selective-1.1.1 → pytest_selective-1.2.0}/.dry-lint.toml +0 -0
- {pytest_selective-1.1.1 → pytest_selective-1.2.0}/.gitattributes +0 -0
- {pytest_selective-1.1.1 → pytest_selective-1.2.0}/.github/ISSUE_TEMPLATE/bug_report.yml +0 -0
- {pytest_selective-1.1.1 → pytest_selective-1.2.0}/.github/PULL_REQUEST_TEMPLATE.md +0 -0
- {pytest_selective-1.1.1 → pytest_selective-1.2.0}/.github/dependabot.yml +0 -0
- {pytest_selective-1.1.1 → pytest_selective-1.2.0}/.github/test-lanes.json +0 -0
- {pytest_selective-1.1.1 → pytest_selective-1.2.0}/.github/validation-inputs.json +0 -0
- {pytest_selective-1.1.1 → pytest_selective-1.2.0}/.github/workflows/docs.yml +0 -0
- {pytest_selective-1.1.1 → pytest_selective-1.2.0}/.github/workflows/dry-lint-judge.yml +0 -0
- {pytest_selective-1.1.1 → pytest_selective-1.2.0}/.github/workflows/dry-lint.yml +0 -0
- {pytest_selective-1.1.1 → pytest_selective-1.2.0}/.github/workflows/observe-ci.yml +0 -0
- {pytest_selective-1.1.1 → pytest_selective-1.2.0}/.github/workflows/pr-title.yml +0 -0
- {pytest_selective-1.1.1 → pytest_selective-1.2.0}/.github/workflows/public-actions.yml +0 -0
- {pytest_selective-1.1.1 → pytest_selective-1.2.0}/.github/workflows/release-please.yml +0 -0
- {pytest_selective-1.1.1 → pytest_selective-1.2.0}/.github/workflows/release.yml +0 -0
- {pytest_selective-1.1.1 → pytest_selective-1.2.0}/.github/workflows/windows-performance.yml +0 -0
- {pytest_selective-1.1.1 → pytest_selective-1.2.0}/.gitignore +0 -0
- {pytest_selective-1.1.1 → pytest_selective-1.2.0}/.pre-commit-config.yaml +0 -0
- {pytest_selective-1.1.1 → pytest_selective-1.2.0}/.readthedocs.yaml +0 -0
- {pytest_selective-1.1.1 → pytest_selective-1.2.0}/AGENTS.md +0 -0
- {pytest_selective-1.1.1 → pytest_selective-1.2.0}/CLAUDE.md +0 -0
- {pytest_selective-1.1.1 → pytest_selective-1.2.0}/LICENSE +0 -0
- {pytest_selective-1.1.1 → pytest_selective-1.2.0}/PROVENANCE.md +0 -0
- {pytest_selective-1.1.1 → pytest_selective-1.2.0}/RELEASING.md +0 -0
- {pytest_selective-1.1.1 → pytest_selective-1.2.0}/action.yml +0 -0
- {pytest_selective-1.1.1 → pytest_selective-1.2.0}/actions/README.md +0 -0
- {pytest_selective-1.1.1 → pytest_selective-1.2.0}/actions/pytest-expansion/action.yml +0 -0
- {pytest_selective-1.1.1 → pytest_selective-1.2.0}/actions/test-impact/action.yml +0 -0
- {pytest_selective-1.1.1 → pytest_selective-1.2.0}/actions/validation-gate/action.yml +0 -0
- {pytest_selective-1.1.1 → pytest_selective-1.2.0}/agentsync.toml +0 -0
- {pytest_selective-1.1.1 → pytest_selective-1.2.0}/contracts/selection/README.md +0 -0
- {pytest_selective-1.1.1 → pytest_selective-1.2.0}/contracts/selection/quint-connect.json +0 -0
- {pytest_selective-1.1.1 → pytest_selective-1.2.0}/contracts/selection/quint.ini +0 -0
- {pytest_selective-1.1.1 → pytest_selective-1.2.0}/contracts/selection/selection.qnt +0 -0
- {pytest_selective-1.1.1 → pytest_selective-1.2.0}/contracts/selection/selection_driver.py +0 -0
- {pytest_selective-1.1.1 → pytest_selective-1.2.0}/contracts/selection/test_selection_contract.py +0 -0
- {pytest_selective-1.1.1 → pytest_selective-1.2.0}/docs/README.md +0 -0
- {pytest_selective-1.1.1 → pytest_selective-1.2.0}/docs/agents.md +0 -0
- {pytest_selective-1.1.1 → pytest_selective-1.2.0}/docs/assets/banner.png +0 -0
- {pytest_selective-1.1.1 → pytest_selective-1.2.0}/docs/assets/banner.svg +0 -0
- {pytest_selective-1.1.1 → pytest_selective-1.2.0}/docs/assets/brand.json +0 -0
- {pytest_selective-1.1.1 → pytest_selective-1.2.0}/docs/assets/icon.png +0 -0
- {pytest_selective-1.1.1 → pytest_selective-1.2.0}/docs/assets/logo.svg +0 -0
- {pytest_selective-1.1.1 → pytest_selective-1.2.0}/docs/assets/raster-sources.json +0 -0
- {pytest_selective-1.1.1 → pytest_selective-1.2.0}/docs/assets/social-preview.png +0 -0
- {pytest_selective-1.1.1 → pytest_selective-1.2.0}/docs/assets/social-preview.svg +0 -0
- {pytest_selective-1.1.1 → pytest_selective-1.2.0}/docs/benchmarks/2026-10-22-due-ci.json +0 -0
- {pytest_selective-1.1.1 → pytest_selective-1.2.0}/docs/benchmarks/2026-10-22-due-docs-ci.json +0 -0
- {pytest_selective-1.1.1 → pytest_selective-1.2.0}/docs/benchmarks/2026-10-22-due-docs-widget.json +0 -0
- {pytest_selective-1.1.1 → pytest_selective-1.2.0}/docs/benchmarks/2026-10-22-due-widget.json +0 -0
- {pytest_selective-1.1.1 → pytest_selective-1.2.0}/docs/benchmarks/2026-10-22-input-replays.json +0 -0
- {pytest_selective-1.1.1 → pytest_selective-1.2.0}/docs/benchmarks/2026-10-22-obligation-docs.json +0 -0
- {pytest_selective-1.1.1 → pytest_selective-1.2.0}/docs/benchmarks/2026-10-22-obligation.json +0 -0
- {pytest_selective-1.1.1 → pytest_selective-1.2.0}/docs/benchmarks/2026-10-22-routing.md +0 -0
- {pytest_selective-1.1.1 → pytest_selective-1.2.0}/docs/benchmarks/2026-10-22-selective-docs.json +0 -0
- {pytest_selective-1.1.1 → pytest_selective-1.2.0}/docs/benchmarks/2026-10-22.md +0 -0
- {pytest_selective-1.1.1 → pytest_selective-1.2.0}/docs/benchmarks/2026-10-23-windows-saleor/obligation-baseline.json +0 -0
- {pytest_selective-1.1.1 → pytest_selective-1.2.0}/docs/benchmarks/2026-10-23-windows-saleor/selective-after-3.11.xml +0 -0
- {pytest_selective-1.1.1 → pytest_selective-1.2.0}/docs/benchmarks/2026-10-23-windows-saleor/selective-after-3.14.xml +0 -0
- {pytest_selective-1.1.1 → pytest_selective-1.2.0}/docs/benchmarks/2026-10-23-windows-saleor/selective-after.json +0 -0
- {pytest_selective-1.1.1 → pytest_selective-1.2.0}/docs/benchmarks/2026-10-23-windows-saleor/selective-control-3.11.xml +0 -0
- {pytest_selective-1.1.1 → pytest_selective-1.2.0}/docs/benchmarks/2026-10-23-windows-saleor/selective-control-3.14.xml +0 -0
- {pytest_selective-1.1.1 → pytest_selective-1.2.0}/docs/benchmarks/2026-10-23-windows-saleor/selective-control.json +0 -0
- {pytest_selective-1.1.1 → pytest_selective-1.2.0}/docs/benchmarks/2026-10-23-windows-saleor/selective-earlier.json +0 -0
- {pytest_selective-1.1.1 → pytest_selective-1.2.0}/docs/benchmarks/2026-10-23-windows-saleor.md +0 -0
- {pytest_selective-1.1.1 → pytest_selective-1.2.0}/docs/benchmarks/windows-runtime.md +0 -0
- {pytest_selective-1.1.1 → pytest_selective-1.2.0}/docs/ci-toolkit.md +0 -0
- {pytest_selective-1.1.1 → pytest_selective-1.2.0}/docs/comparison-pytest-impacted.md +0 -0
- {pytest_selective-1.1.1 → pytest_selective-1.2.0}/docs/evidence.md +0 -0
- {pytest_selective-1.1.1 → pytest_selective-1.2.0}/docs/local-development.md +0 -0
- {pytest_selective-1.1.1 → pytest_selective-1.2.0}/docs/pytest-hygiene.md +0 -0
- {pytest_selective-1.1.1 → pytest_selective-1.2.0}/docs/tutorial.md +0 -0
- {pytest_selective-1.1.1 → pytest_selective-1.2.0}/docs/tutorials/adoption-checklist.md +0 -0
- {pytest_selective-1.1.1 → pytest_selective-1.2.0}/docs/tutorials/sidecar-dependencies.md +0 -0
- {pytest_selective-1.1.1 → pytest_selective-1.2.0}/docs/tutorials/skip-unrelated-ci.md +0 -0
- {pytest_selective-1.1.1 → pytest_selective-1.2.0}/docs/work-in-progress/tickets/actions-directory-test-plan.md +0 -0
- {pytest_selective-1.1.1 → pytest_selective-1.2.0}/docs/work-in-progress/tickets/codex-selector-correctness-performance-test-plan.md +0 -0
- {pytest_selective-1.1.1 → pytest_selective-1.2.0}/docs/work-in-progress/tickets/codex-windows-git-performance-test-plan.md +0 -0
- {pytest_selective-1.1.1 → pytest_selective-1.2.0}/docs/work-in-progress/tickets/graph-based-inputs-plan.md +0 -0
- {pytest_selective-1.1.1 → pytest_selective-1.2.0}/docs/work-in-progress/tickets/impacted-improvements-test-plan.md +0 -0
- {pytest_selective-1.1.1 → pytest_selective-1.2.0}/docs/work-in-progress/tickets/move-action-implementations-test-plan.md +0 -0
- {pytest_selective-1.1.1 → pytest_selective-1.2.0}/docs/work-in-progress/tickets/public-action-onboarding-test-plan.md +0 -0
- {pytest_selective-1.1.1 → pytest_selective-1.2.0}/docs/work-in-progress/tickets/ratchet-duplicates-zero-test-plan.md +0 -0
- {pytest_selective-1.1.1 → pytest_selective-1.2.0}/docs/work-in-progress/tickets/rerun-selection-test-plan.md +0 -0
- {pytest_selective-1.1.1 → pytest_selective-1.2.0}/documentation/assets/routing-comparison.svg +0 -0
- {pytest_selective-1.1.1 → pytest_selective-1.2.0}/documentation/check_site.py +0 -0
- {pytest_selective-1.1.1 → pytest_selective-1.2.0}/documentation/ecosystem.md +0 -0
- {pytest_selective-1.1.1 → pytest_selective-1.2.0}/documentation/hooks.py +0 -0
- {pytest_selective-1.1.1 → pytest_selective-1.2.0}/documentation/index.md +0 -0
- {pytest_selective-1.1.1 → pytest_selective-1.2.0}/documentation/llms.txt +0 -0
- {pytest_selective-1.1.1 → pytest_selective-1.2.0}/documentation/overrides/main.html +0 -0
- {pytest_selective-1.1.1 → pytest_selective-1.2.0}/documentation/publishing.md +0 -0
- {pytest_selective-1.1.1 → pytest_selective-1.2.0}/documentation/requirements.in +0 -0
- {pytest_selective-1.1.1 → pytest_selective-1.2.0}/documentation/requirements.txt +0 -0
- {pytest_selective-1.1.1 → pytest_selective-1.2.0}/mkdocs.yml +0 -0
- {pytest_selective-1.1.1 → pytest_selective-1.2.0}/release-please-config.json +0 -0
- {pytest_selective-1.1.1 → pytest_selective-1.2.0}/scripts/benchmark_graph.py +0 -0
- {pytest_selective-1.1.1 → pytest_selective-1.2.0}/scripts/benchmark_parsing.py +0 -0
- {pytest_selective-1.1.1 → pytest_selective-1.2.0}/scripts/benchmark_rust.py +0 -0
- {pytest_selective-1.1.1 → pytest_selective-1.2.0}/scripts/check_docs.py +0 -0
- {pytest_selective-1.1.1 → pytest_selective-1.2.0}/scripts/check_metadata.py +0 -0
- {pytest_selective-1.1.1 → pytest_selective-1.2.0}/scripts/compare_impacted.py +0 -0
- {pytest_selective-1.1.1 → pytest_selective-1.2.0}/scripts/public_actions.py +0 -0
- {pytest_selective-1.1.1 → pytest_selective-1.2.0}/scripts/render_brand.py +0 -0
- {pytest_selective-1.1.1 → pytest_selective-1.2.0}/scripts/smoke_distribution.py +0 -0
- {pytest_selective-1.1.1 → pytest_selective-1.2.0}/scripts/tutorial.py +0 -0
- {pytest_selective-1.1.1 → pytest_selective-1.2.0}/skills/pytest-selective/agents/openai.yaml +0 -0
- {pytest_selective-1.1.1 → pytest_selective-1.2.0}/src/pytest_selective/__init__.py +0 -0
- {pytest_selective-1.1.1 → pytest_selective-1.2.0}/src/pytest_selective/__main__.py +0 -0
- {pytest_selective-1.1.1 → pytest_selective-1.2.0}/src/pytest_selective/ast_imports.py +0 -0
- {pytest_selective-1.1.1 → pytest_selective-1.2.0}/src/pytest_selective/checkpoint.py +0 -0
- {pytest_selective-1.1.1 → pytest_selective-1.2.0}/src/pytest_selective/gate.py +0 -0
- {pytest_selective-1.1.1 → pytest_selective-1.2.0}/src/pytest_selective/git_operations.py +0 -0
- {pytest_selective-1.1.1 → pytest_selective-1.2.0}/src/pytest_selective/hygiene.py +0 -0
- {pytest_selective-1.1.1 → pytest_selective-1.2.0}/src/pytest_selective/impact_audit_plugin.py +0 -0
- {pytest_selective-1.1.1 → pytest_selective-1.2.0}/src/pytest_selective/impact_declarations.py +0 -0
- {pytest_selective-1.1.1 → pytest_selective-1.2.0}/src/pytest_selective/impact_explanation.py +0 -0
- {pytest_selective-1.1.1 → pytest_selective-1.2.0}/src/pytest_selective/impact_first_party.py +0 -0
- {pytest_selective-1.1.1 → pytest_selective-1.2.0}/src/pytest_selective/impact_name_scope.py +0 -0
- {pytest_selective-1.1.1 → pytest_selective-1.2.0}/src/pytest_selective/impact_selection.py +0 -0
- {pytest_selective-1.1.1 → pytest_selective-1.2.0}/src/pytest_selective/input_policy.py +0 -0
- {pytest_selective-1.1.1 → pytest_selective-1.2.0}/src/pytest_selective/local.py +0 -0
- {pytest_selective-1.1.1 → pytest_selective-1.2.0}/src/pytest_selective/parse_batch.py +0 -0
- {pytest_selective-1.1.1 → pytest_selective-1.2.0}/src/pytest_selective/plugin.py +0 -0
- {pytest_selective-1.1.1 → pytest_selective-1.2.0}/src/pytest_selective/py.typed +0 -0
- {pytest_selective-1.1.1 → pytest_selective-1.2.0}/src/pytest_selective/pytest_expansion.py +0 -0
- {pytest_selective-1.1.1 → pytest_selective-1.2.0}/src/pytest_selective/pytest_plugins.py +0 -0
- {pytest_selective-1.1.1 → pytest_selective-1.2.0}/src/pytest_selective/pytest_sharding.py +0 -0
- {pytest_selective-1.1.1 → pytest_selective-1.2.0}/src/pytest_selective/test_inventory.py +0 -0
- {pytest_selective-1.1.1 → pytest_selective-1.2.0}/src/pytest_selective/typecheck_scope.py +0 -0
- {pytest_selective-1.1.1 → pytest_selective-1.2.0}/src/pytest_selective/working_tree.py +0 -0
- {pytest_selective-1.1.1 → pytest_selective-1.2.0}/tests/__init__.py +0 -0
- {pytest_selective-1.1.1 → pytest_selective-1.2.0}/tests/test_checkpoint_policy.py +0 -0
- {pytest_selective-1.1.1 → pytest_selective-1.2.0}/tests/test_ci_measurements.py +0 -0
- {pytest_selective-1.1.1 → pytest_selective-1.2.0}/tests/test_distribution_metadata.py +0 -0
- {pytest_selective-1.1.1 → pytest_selective-1.2.0}/tests/test_hygiene.py +0 -0
- {pytest_selective-1.1.1 → pytest_selective-1.2.0}/tests/test_pytest_expansion.py +0 -0
- {pytest_selective-1.1.1 → pytest_selective-1.2.0}/tests/test_release_workflow.py +0 -0
- {pytest_selective-1.1.1 → pytest_selective-1.2.0}/tests/test_repository_hygiene.py +0 -0
- {pytest_selective-1.1.1 → pytest_selective-1.2.0}/tests/test_repository_hygiene.py.gv-dependency.yaml +0 -0
- {pytest_selective-1.1.1 → pytest_selective-1.2.0}/tests/test_tutorial.py +0 -0
- {pytest_selective-1.1.1 → pytest_selective-1.2.0}/tests/testing/__init__.py +0 -0
- {pytest_selective-1.1.1 → pytest_selective-1.2.0}/tests/testing/git_repo.py +0 -0
- {pytest_selective-1.1.1 → pytest_selective-1.2.0}/tests/testing/test_ast_imports.py +0 -0
- {pytest_selective-1.1.1 → pytest_selective-1.2.0}/tests/testing/test_ci.py +0 -0
- {pytest_selective-1.1.1 → pytest_selective-1.2.0}/tests/testing/test_gate.py +0 -0
- {pytest_selective-1.1.1 → pytest_selective-1.2.0}/tests/testing/test_git_repo.py +0 -0
- {pytest_selective-1.1.1 → pytest_selective-1.2.0}/tests/testing/test_global_inputs.py +0 -0
- {pytest_selective-1.1.1 → pytest_selective-1.2.0}/tests/testing/test_impact_explanation.py +0 -0
- {pytest_selective-1.1.1 → pytest_selective-1.2.0}/tests/testing/test_impact_first_party.py +0 -0
- {pytest_selective-1.1.1 → pytest_selective-1.2.0}/tests/testing/test_impact_name_scope.py +0 -0
- {pytest_selective-1.1.1 → pytest_selective-1.2.0}/tests/testing/test_parse_batch.py +0 -0
- {pytest_selective-1.1.1 → pytest_selective-1.2.0}/tests/testing/test_portability.py +0 -0
- {pytest_selective-1.1.1 → pytest_selective-1.2.0}/tests/testing/test_pytest_plugin.py +0 -0
- {pytest_selective-1.1.1 → pytest_selective-1.2.0}/tests/testing/test_pytest_plugins.py +0 -0
- {pytest_selective-1.1.1 → pytest_selective-1.2.0}/tests/testing/test_pytest_sharding.py +0 -0
- {pytest_selective-1.1.1 → pytest_selective-1.2.0}/tests/testing/test_typecheck_scope.py +0 -0
- {pytest_selective-1.1.1 → pytest_selective-1.2.0}/tests/testing/test_working_tree.py +0 -0
|
@@ -149,12 +149,13 @@ jobs:
|
|
|
149
149
|
- name: Record coverage runtime
|
|
150
150
|
run: uv run --locked python -m coverage --version
|
|
151
151
|
- name: Execute the shared affected-file plan
|
|
152
|
-
run: uv run --locked
|
|
152
|
+
run: uv run --locked pytest-selective execute-plan -- -q -n 4 --dist=worksteal --durations=20 --junitxml=test-timings.xml
|
|
153
153
|
env:
|
|
154
154
|
CI_PLANNER_RESULT: ${{ needs.select-tests.result }}
|
|
155
155
|
CI_TEST_MODE: ${{ needs.select-tests.outputs.mode }}
|
|
156
156
|
CI_TEST_FILES: ${{ needs.select-tests.outputs.files }}
|
|
157
|
-
|
|
157
|
+
# Full coverage on each OS; 3.11 retains the same compatibility tests without duplicate instrumentation.
|
|
158
|
+
CI_COVERAGE: ${{ matrix.python == '3.14' }}
|
|
158
159
|
# Keep temporary Git repositories beside the checkout on the work disk.
|
|
159
160
|
TMP: ${{ runner.temp }}
|
|
160
161
|
TEMP: ${{ runner.temp }}
|
|
@@ -186,7 +187,7 @@ jobs:
|
|
|
186
187
|
python-version: '3.11'
|
|
187
188
|
- run: uv pip install 'pytest==8.0.0'
|
|
188
189
|
- name: Execute the same file plan on minimum pytest
|
|
189
|
-
run: uv run --no-sync
|
|
190
|
+
run: uv run --no-sync pytest-selective execute-plan -- -q -n 4 --dist=worksteal --durations=20
|
|
190
191
|
env:
|
|
191
192
|
CI_PLANNER_RESULT: ${{ needs.select-tests.result }}
|
|
192
193
|
CI_TEST_MODE: ${{ needs.select-tests.outputs.mode }}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
name: dry-lint audit
|
|
2
|
+
# Weekly, or dispatched after merging a cleanup: dry-lint's own audit workflow sweeps the repository against the
|
|
3
|
+
# accepted backlog and opens or updates one dry-lint/ratchet pull request recording what was cleaned. Its steps ship
|
|
4
|
+
# with dry-lint releases; this file only says when it runs and with which credentials.
|
|
5
|
+
on:
|
|
6
|
+
schedule:
|
|
7
|
+
- cron: '23 6 * * 1'
|
|
8
|
+
workflow_dispatch:
|
|
9
|
+
# dry-lint.yml calls this file on a manual dispatch, to exercise the audit with the rest of the checks.
|
|
10
|
+
workflow_call:
|
|
11
|
+
permissions:
|
|
12
|
+
contents: read
|
|
13
|
+
jobs:
|
|
14
|
+
audit:
|
|
15
|
+
uses: gigaverse-app/dry-lint/.github/workflows/audit.yml@v1
|
|
16
|
+
with:
|
|
17
|
+
python-version: '3.13'
|
|
18
|
+
budget-usd: '5'
|
|
19
|
+
plugins: anthropic==2.4.0
|
|
20
|
+
secrets:
|
|
21
|
+
ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }}
|
|
22
|
+
# The organization credential makes the ratchet pull request run ordinary CI.
|
|
23
|
+
DRY_LINT_RATCHET_TOKEN: ${{ secrets.GV_CI_CROSS_REPO_TOKEN }}
|
|
@@ -1,5 +1,19 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## [1.2.0](https://github.com/gigaverse-app/pytest-selective/compare/v1.1.2...v1.2.0) (2026-11-09)
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
### Features
|
|
7
|
+
|
|
8
|
+
* **ci:** share plan execution and reduce matrix instrumentation ([1fe0f36](https://github.com/gigaverse-app/pytest-selective/commit/1fe0f36463d4354a99ea3f45406825fa728a12c2))
|
|
9
|
+
|
|
10
|
+
## [1.1.2](https://github.com/gigaverse-app/pytest-selective/compare/v1.1.1...v1.1.2) (2026-11-09)
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
### Bug fixes
|
|
14
|
+
|
|
15
|
+
* agent setup round 2, from a second adoption trial ([#114](https://github.com/gigaverse-app/pytest-selective/issues/114)) ([45d4afe](https://github.com/gigaverse-app/pytest-selective/commit/45d4afe76cde9f6093519d97000244c6c06b157d))
|
|
16
|
+
|
|
3
17
|
## [1.1.1](https://github.com/gigaverse-app/pytest-selective/compare/v1.1.0...v1.1.1) (2026-11-09)
|
|
4
18
|
|
|
5
19
|
|
|
@@ -79,7 +79,9 @@ split indexes and linked worktrees without modifying the user's index or HEAD.
|
|
|
79
79
|
CI tests Python 3.11 and 3.14 on Linux, Windows, and macOS. A separate job checks
|
|
80
80
|
pytest 8.0.0 on Python 3.11, the plugin's lower bound. It also checks hooks, builds both distribution
|
|
81
81
|
formats, validates PyPI metadata, and installs each distribution in an isolated
|
|
82
|
-
environment on both Python versions.
|
|
82
|
+
environment on both Python versions. JUnit retains results and timings on every platform and Python version.
|
|
83
|
+
Full runs collect coverage on Python 3.14 on all three operating systems; Python 3.11 runs the same compatibility
|
|
84
|
+
tests without a duplicate instrumentation pass. Each runner uses four work-stealing workers.
|
|
83
85
|
|
|
84
86
|
Use `uv lock --upgrade-package NAME` for a targeted dependency update and run the
|
|
85
87
|
affected checks. Dependabot proposes weekly uv and GitHub Actions updates.
|
|
@@ -116,8 +118,8 @@ File planning derives its graph from that same lane config and merges its declar
|
|
|
116
118
|
global execution inputs with the graph's environment inputs. Declare subprocess
|
|
117
119
|
and non-import dependencies at their readers. Full plans collect whole-suite
|
|
118
120
|
coverage; scoped plans produce JUnit timing evidence without presenting partial
|
|
119
|
-
coverage as a whole-suite result. The
|
|
120
|
-
|
|
121
|
+
coverage as a whole-suite result. The public [shared-plan executor](docs/shared-plan-execution.md)
|
|
122
|
+
consumes the action's JSON file list without implementing another selector.
|
|
121
123
|
The outer pytest process disables the selective plugin to avoid a second planner
|
|
122
124
|
and a hidden dependency of every test on plugin startup. Plugin integration tests
|
|
123
125
|
still load it normally in subprocesses and declare that dependency explicitly.
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.5
|
|
2
2
|
Name: pytest-selective
|
|
3
|
-
Version: 1.
|
|
3
|
+
Version: 1.2.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/
|
|
@@ -60,9 +60,9 @@ the affected test files, and explains the result. Use it directly as a pytest
|
|
|
60
60
|
plugin, or plan before collection and service startup with the CLI and CI actions.
|
|
61
61
|
|
|
62
62
|
Built from the selector used in Gigaverse's backend, AI, and shared Python
|
|
63
|
-
infrastructure.
|
|
64
|
-
|
|
65
|
-
|
|
63
|
+
infrastructure. Use the actions at the moving `@v1` tag, which follows the newest
|
|
64
|
+
release, and the package at `pytest-selective>=X.Y.Z,<2`. Where your tooling rejects
|
|
65
|
+
tags, pin both to one release instead: `@<sha> # vX.Y.Z` and `pytest-selective==X.Y.Z`.
|
|
66
66
|
[Provenance and extraction scope](https://github.com/gigaverse-app/pytest-selective/blob/main/PROVENANCE.md).
|
|
67
67
|
|
|
68
68
|
[Quick tutorial](https://github.com/gigaverse-app/pytest-selective/blob/main/docs/tutorial.md) ·
|
|
@@ -237,7 +237,7 @@ skip pytest, because invoking pytest without paths runs the whole suite.
|
|
|
237
237
|
| --- | --- |
|
|
238
238
|
| A transitive dependency | Import chains and package initialization |
|
|
239
239
|
| A deleted or renamed module | Dependencies in both the base and head trees |
|
|
240
|
-
| Fixtures or startup code | Ancestor conftests, plugins named by `-p` in `addopts`, and configured global modules |
|
|
240
|
+
| Fixtures or startup code | Ancestor conftests, plugins named by `-p` in `addopts`, the project's own `pytest11` entry points, and configured global modules |
|
|
241
241
|
| Templates, schemas, or data files | [Reader-owned declarations and YAML sidecars](docs/tutorials/sidecar-dependencies.md) |
|
|
242
242
|
| A surprisingly large selection | Dependency paths, startup-floor and blast-radius explanations |
|
|
243
243
|
| A large CI suite | Cached parsing, graph reuse, and duration-based shard planning |
|
|
@@ -29,9 +29,9 @@ the affected test files, and explains the result. Use it directly as a pytest
|
|
|
29
29
|
plugin, or plan before collection and service startup with the CLI and CI actions.
|
|
30
30
|
|
|
31
31
|
Built from the selector used in Gigaverse's backend, AI, and shared Python
|
|
32
|
-
infrastructure.
|
|
33
|
-
|
|
34
|
-
|
|
32
|
+
infrastructure. Use the actions at the moving `@v1` tag, which follows the newest
|
|
33
|
+
release, and the package at `pytest-selective>=X.Y.Z,<2`. Where your tooling rejects
|
|
34
|
+
tags, pin both to one release instead: `@<sha> # vX.Y.Z` and `pytest-selective==X.Y.Z`.
|
|
35
35
|
[Provenance and extraction scope](https://github.com/gigaverse-app/pytest-selective/blob/main/PROVENANCE.md).
|
|
36
36
|
|
|
37
37
|
[Quick tutorial](https://github.com/gigaverse-app/pytest-selective/blob/main/docs/tutorial.md) ·
|
|
@@ -206,7 +206,7 @@ skip pytest, because invoking pytest without paths runs the whole suite.
|
|
|
206
206
|
| --- | --- |
|
|
207
207
|
| A transitive dependency | Import chains and package initialization |
|
|
208
208
|
| A deleted or renamed module | Dependencies in both the base and head trees |
|
|
209
|
-
| Fixtures or startup code | Ancestor conftests, plugins named by `-p` in `addopts`, and configured global modules |
|
|
209
|
+
| Fixtures or startup code | Ancestor conftests, plugins named by `-p` in `addopts`, the project's own `pytest11` entry points, and configured global modules |
|
|
210
210
|
| Templates, schemas, or data files | [Reader-owned declarations and YAML sidecars](docs/tutorials/sidecar-dependencies.md) |
|
|
211
211
|
| A surprisingly large selection | Dependency paths, startup-floor and blast-radius explanations |
|
|
212
212
|
| A large CI suite | Cached parsing, graph reuse, and duration-based shard planning |
|
|
@@ -12,8 +12,10 @@ inputs:
|
|
|
12
12
|
default: auto
|
|
13
13
|
artifact-name:
|
|
14
14
|
description: >
|
|
15
|
-
Upload the lane plan under this name, where
|
|
16
|
-
the sticky's lane section.
|
|
15
|
+
Upload the lane plan (test-lanes.json) as an artifact under this name, where
|
|
16
|
+
CI-esta's report renders it as the sticky's lane section. Give each planner job
|
|
17
|
+
in one workflow run its own name. Empty skips the upload; planning and the
|
|
18
|
+
outputs are unchanged.
|
|
17
19
|
default: test-lane-selection
|
|
18
20
|
outputs:
|
|
19
21
|
lanes:
|
|
@@ -305,7 +305,8 @@ class RepositoryDriver(SelectionDriver):
|
|
|
305
305
|
changed = set(git_text(self.root, "diff", "--name-only", "--no-renames", base, self.head).splitlines())
|
|
306
306
|
tests = self._tests(self.head)
|
|
307
307
|
at_head = self._runtime(self.head, tests)
|
|
308
|
-
|
|
308
|
+
base_tests = set(self._tests(base))
|
|
309
|
+
at_base = self._runtime(base, [test for test in tests if test in base_tests])
|
|
309
310
|
needed = {test for test in tests if (at_head[test] | at_base.get(test, frozenset())) & changed}
|
|
310
311
|
return {NAMES[test] for test in needed} - set(selection["selected"])
|
|
311
312
|
|
|
@@ -9,12 +9,16 @@ Modules and sys.path are restored between test files, so each file's dependencie
|
|
|
9
9
|
import json
|
|
10
10
|
import os
|
|
11
11
|
import sys
|
|
12
|
+
from functools import cache
|
|
12
13
|
from pathlib import Path
|
|
13
14
|
|
|
14
15
|
import pytest
|
|
15
16
|
|
|
16
17
|
|
|
18
|
+
@cache
|
|
17
19
|
def _under(root: Path, path: str | None) -> str | None:
|
|
20
|
+
# Each process inspects one immutable exported tree. Cache path resolution,
|
|
21
|
+
# never the per-test import/open observations, which remain isolated below.
|
|
18
22
|
if not path:
|
|
19
23
|
return None
|
|
20
24
|
try:
|
|
@@ -28,6 +28,10 @@ supported on GitHub.com with runner 2.336.0 or newer. Individual `actions/NAME`
|
|
|
28
28
|
entry points call the implementations directly and do not require that syntax.
|
|
29
29
|
[GitHub reference documentation](https://docs.github.com/en/actions/reference/workflows-and-actions/workflow-syntax).
|
|
30
30
|
|
|
31
|
+
`plan-lanes` uploads its plan as the `test-lane-selection` artifact by default. Set
|
|
32
|
+
`artifact-name` to rename it, which a second planner job in the same workflow run needs, or to
|
|
33
|
+
`''` to skip the upload.
|
|
34
|
+
|
|
31
35
|
## Action reference
|
|
32
36
|
|
|
33
37
|
Each link below is the public action's complete input/output contract.
|
|
@@ -253,11 +253,10 @@ job per workflow the required check and run
|
|
|
253
253
|
gated job to its lanes. It fails unless each prerequisite succeeded or was skipped while
|
|
254
254
|
the planner succeeded and every mapped lane is `false`; a failed planner fails it.
|
|
255
255
|
|
|
256
|
-
|
|
257
|
-
skipping it skips publishing.
|
|
258
|
-
(`github.event_name != 'pull_request' || ...`), or plan inside the job with
|
|
256
|
+
Never gate a job that a publish or deploy job `needs:`, directly or transitively:
|
|
257
|
+
skipping it skips publishing. Leave it ungated, or plan inside the job with
|
|
259
258
|
`continue-on-error: true` and admit only its expensive steps, as the `distributions`
|
|
260
|
-
job in this repository's CI does. A branch's first push has no previous commit, so
|
|
259
|
+
job in this repository's CI does. Gate every other job that installs, builds or tests. A branch's first push has no previous commit, so
|
|
261
260
|
`on: push` for every branch runs every lane once per new branch; prefer pull-request
|
|
262
261
|
triggers plus pushes to the default branch.
|
|
263
262
|
The default event policy handles manual/release checkpoints, schedules and
|
|
@@ -24,9 +24,10 @@ select. The default filenames are `test_*.py`, `*_test.py`, `*_tests.py`, and
|
|
|
24
24
|
|
|
25
25
|
## Review and record collection policy
|
|
26
26
|
|
|
27
|
-
If your repository has `pyproject.toml`, `pytest.ini`, `tox.ini
|
|
28
|
-
|
|
29
|
-
|
|
27
|
+
If your repository has `pyproject.toml`, `pytest.ini`, a `tox.ini` with a `[pytest]`
|
|
28
|
+
section, or a `setup.cfg` with a `[tool:pytest]` section, record its committed
|
|
29
|
+
pytest-policy fingerprints. A `pyproject.toml` or `pytest.ini` with no pytest settings
|
|
30
|
+
still has a fingerprint, because pytest can still choose it. This review prevents a new
|
|
30
31
|
collection rule from silently invalidating the inventory.
|
|
31
32
|
|
|
32
33
|
```bash
|
|
@@ -96,7 +97,7 @@ patterns to your actual inventory. Central-rule reader patterns use shell-style
|
|
|
96
97
|
matching: here `*` also covers nested paths under `tests/`. Common lockfiles, dependency manifests and pytest configuration already belong
|
|
97
98
|
to the default `global_inputs`. Keep those defaults unless you have reviewed a
|
|
98
99
|
narrower environment contract; these reader-specific rules are for additional
|
|
99
|
-
inputs. Other non-Python files are not automatically global inputs. Plugins named by `-p NAME` in a pytest configuration's `addopts` are global modules automatically
|
|
100
|
+
inputs. Other non-Python files are not automatically global inputs. Plugins named by `-p NAME` in a pytest configuration's `addopts` are global modules automatically, and so are the project's own `pytest11` entry points (`[project.entry-points.pytest11]` or `[tool.poetry.plugins.pytest11]` in any tracked `pyproject.toml`) whose module is a tracked file. An entry point stays global unless every pytest configuration disables it, with `-p no:NAME` (`NAME` is the entry point's name) or `--disable-plugin-autoload` in `addopts`. Plugins passed only on the command line or in `PYTEST_ADDOPTS`, and `PYTEST_DISABLE_PLUGIN_AUTOLOAD`, are not visible to the planner. Use `global_modules` for those and other modules loaded by every test and
|
|
100
101
|
`always_run` for explicit canary test paths. Retain full-suite checkpoints.
|
|
101
102
|
|
|
102
103
|
## Explain and audit
|
|
@@ -120,6 +121,49 @@ pytest-selective audit --config .github/test-impact.json --records impact-audit
|
|
|
120
121
|
Audit results cover executed paths, not every possible execution. Declare missing
|
|
121
122
|
edges at their readers. See [evidence and limitations](evidence.md).
|
|
122
123
|
|
|
124
|
+
### Write the missing declarations
|
|
125
|
+
|
|
126
|
+
`audit --write-declarations` writes a rule for every undeclared read into the
|
|
127
|
+
reading test's sidecar, `<test>.gv-dependency.yaml`, creating it or appending to
|
|
128
|
+
the one it already has:
|
|
129
|
+
|
|
130
|
+
```bash
|
|
131
|
+
pytest-selective audit --config .github/test-impact.json --records impact-audit --write-declarations
|
|
132
|
+
pytest-selective audit --config .github/test-impact.json --records impact-audit --working-tree # confirms nothing is left before you commit
|
|
133
|
+
```
|
|
134
|
+
|
|
135
|
+
```yaml
|
|
136
|
+
dependencies:
|
|
137
|
+
- inputs:
|
|
138
|
+
- "//examples/backgrounds/**"
|
|
139
|
+
- "//examples/gold.toml"
|
|
140
|
+
kind: content
|
|
141
|
+
reason: "Read at runtime; recorded by pytest-selective audit --write-declarations."
|
|
142
|
+
- inputs:
|
|
143
|
+
- "//scripts/replay_coverage.py"
|
|
144
|
+
kind: import
|
|
145
|
+
reason: "Loaded or run outside the test's imports; recorded by pytest-selective audit --write-declarations."
|
|
146
|
+
```
|
|
147
|
+
|
|
148
|
+
- A Python input becomes an `import` rule: a test that runs a script in a
|
|
149
|
+
subprocess also depends on what the script imports. Any other input becomes a
|
|
150
|
+
`content` rule. Review the kind where the test only reads Python as text.
|
|
151
|
+
- Several reads in one directory become `<directory>/**` only when the test read
|
|
152
|
+
every tracked file under it, so the glob claims nothing the run did not see.
|
|
153
|
+
It also covers files added there later.
|
|
154
|
+
- Every pattern must match a tracked file at the audited revision. A path the
|
|
155
|
+
pattern language cannot express (for example one containing `[`) is printed as
|
|
156
|
+
`not declared` and the command exits 2.
|
|
157
|
+
- Appending keeps an existing sidecar's text and comments. When the result would
|
|
158
|
+
not parse to exactly the old rules plus the new ones, the file is left alone
|
|
159
|
+
and the rules are printed for you to add by hand.
|
|
160
|
+
|
|
161
|
+
The command prints each sidecar and pattern it wrote and exits 0 when every
|
|
162
|
+
undeclared read was declared. The files are in the working tree, so the plain
|
|
163
|
+
audit (which reads `--head`) still reports them until you commit; use
|
|
164
|
+
`--working-tree` to check first. Prefer a hand-written comment or directory
|
|
165
|
+
manifest where it reads better; the writer only removes the transcription step.
|
|
166
|
+
|
|
123
167
|
Audit discovery uses the requested repository even when a calling Git hook exports
|
|
124
168
|
location variables for another checkout. It shares repository isolation with the
|
|
125
169
|
planner and expansion guard.
|
|
@@ -135,7 +179,11 @@ Unsupported checker patterns retain the full-check fallback.
|
|
|
135
179
|
|
|
136
180
|
The policy fingerprints cover conventional pytest configuration names, including
|
|
137
181
|
`pytest.toml`, `.pytest.toml`, `.pytest.ini` and nested candidates. Empty candidates
|
|
138
|
-
are included because their presence can change pytest's root and precedence.
|
|
182
|
+
are included because their presence can change pytest's root and precedence. A
|
|
183
|
+
`tox.ini` without `[pytest]` and a `setup.cfg` without `[tool:pytest]` are not: pytest
|
|
184
|
+
skips them. Adding that section later changes the fingerprints and needs a review.
|
|
185
|
+
Releases up to 1.1.1 recorded such a file as the hash of an empty policy; that entry
|
|
186
|
+
is still accepted and needs no refresh.
|
|
139
187
|
Changes require reviewing and refreshing `pytest_policy`; until then planning
|
|
140
188
|
fails safely and the CI action runs the complete configured roots.
|
|
141
189
|
|
|
@@ -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. `audit --write-declarations` writes those sidecars for you; see
|
|
34
|
+
[write the missing declarations](configuration.md#write-the-missing-declarations). [Work through the sidecar tutorial](tutorials/sidecar-dependencies.md), or consult
|
|
34
35
|
[declaration syntax](configuration.md#declare-inputs-that-imports-cannot-describe).
|
|
35
36
|
The audit covers observed paths; keep representative full runs for paths it has
|
|
36
37
|
not seen, and explicitly model dynamic imports and external inputs.
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
# Plan once, execute across compatibility lanes
|
|
2
|
+
|
|
3
|
+
Use `pytest-selective execute-plan` to run a file plan emitted by the public
|
|
4
|
+
test-impact action. Every matrix job consumes the same plan; it does not need
|
|
5
|
+
Git history or a second dependency analysis.
|
|
6
|
+
|
|
7
|
+
```yaml
|
|
8
|
+
- run: uv run --locked pytest-selective execute-plan -- -q -n 4 --dist=worksteal
|
|
9
|
+
env:
|
|
10
|
+
CI_PLANNER_RESULT: ${{ needs.plan.result }}
|
|
11
|
+
CI_TEST_MODE: ${{ needs.plan.outputs.mode }}
|
|
12
|
+
CI_TEST_FILES: ${{ needs.plan.outputs.files }}
|
|
13
|
+
CI_COVERAGE: 'true'
|
|
14
|
+
```
|
|
15
|
+
|
|
16
|
+
`files` must be the action's `test_roots_json` output. A successful `none` plan
|
|
17
|
+
does not start pytest. Failed planning, malformed JSON, unknown modes or an
|
|
18
|
+
empty scoped selection execute the complete roots. Roots default to `tests`;
|
|
19
|
+
override with `--full-roots tests/unit tests/integration -- ...`. Paths with
|
|
20
|
+
spaces remain single arguments, and pytest receives its normal exit status.
|
|
21
|
+
|
|
22
|
+
`CI_COVERAGE=true` adds `--cov --cov-report=term-missing --cov-report=xml` only
|
|
23
|
+
for full execution. The environment must have pytest-cov installed. Keep
|
|
24
|
+
coverage thresholds in your project configuration; do not enforce a full-suite
|
|
25
|
+
threshold on selected files. The outer execution disables the selective plugin
|
|
26
|
+
because planning already happened. Integration tests can still load it explicitly.
|
|
27
|
+
|
|
28
|
+
When lane and file selection share a graph, derive the file config:
|
|
29
|
+
|
|
30
|
+
```sh
|
|
31
|
+
pytest-selective file-config --lanes .github/test-lanes.json --output /tmp/test-impact.json
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
This combines the graph's global inputs with the lane config's explicit global
|
|
35
|
+
inputs. Declare `global_inputs` before using it. Lane-specific `inputs` remain
|
|
36
|
+
lane admission rules; they do not automatically make every test file affected.
|
|
37
|
+
Declare non-import dependencies in the graph as usual.
|
|
38
|
+
|
|
39
|
+
pytest-selective's own CI uses four work-stealing workers on each existing
|
|
40
|
+
runner. Its full checkpoints retain all Python 3.11 and 3.14 compatibility tests
|
|
41
|
+
on Linux, Windows and macOS. Coverage runs on Python 3.14 on all three systems,
|
|
42
|
+
avoiding a second instrumentation pass on Python 3.11. The minimum-pytest lane,
|
|
43
|
+
Quint contract and installed-distribution smoke remain independent checks.
|
|
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "pytest-selective"
|
|
7
|
-
version = "1.
|
|
7
|
+
version = "1.2.0"
|
|
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"
|
|
@@ -4,15 +4,8 @@ import sys
|
|
|
4
4
|
from pathlib import Path
|
|
5
5
|
|
|
6
6
|
from pytest_selective.ci import LaneConfig
|
|
7
|
-
from pytest_selective.impact_models import ImpactConfig
|
|
8
7
|
|
|
9
|
-
|
|
10
|
-
def file_config(lanes: LaneConfig) -> ImpactConfig:
|
|
11
|
-
if lanes.global_inputs is None:
|
|
12
|
-
raise ValueError("This repository must declare its shared CI execution inputs")
|
|
13
|
-
return lanes.graph.model_copy(
|
|
14
|
-
update={"global_inputs": sorted(set(lanes.graph.global_inputs) | set(lanes.global_inputs))}
|
|
15
|
-
)
|
|
8
|
+
file_config = LaneConfig.file_config
|
|
16
9
|
|
|
17
10
|
|
|
18
11
|
if __name__ == "__main__":
|
|
@@ -5,6 +5,10 @@ head. A side may hold several workflow runs (for example CI and Docs triggered b
|
|
|
5
5
|
push): runner-seconds add up and elapsed runs from the first run's creation to the last job's
|
|
6
6
|
completion. `--expected-failure JOB` accepts that job's deliberate failure (a probe) as executed
|
|
7
7
|
work instead of rejecting the run; any other failed, cancelled or unfinished job still does.
|
|
8
|
+
|
|
9
|
+
The sides may come from different events. A `pull_request` run records the branch head as its
|
|
10
|
+
`head_sha` (although it checks out the merge commit), so a `workflow_dispatch` or `push` run on
|
|
11
|
+
that branch is the same head: the usual full control beside a selected pull-request run.
|
|
8
12
|
"""
|
|
9
13
|
|
|
10
14
|
import argparse
|