pytest-selective 0.4.2__tar.gz → 1.1.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.4.2 → pytest_selective-1.1.0}/.github/test-lanes.json +28 -2
- pytest_selective-1.1.0/.github/validation-inputs.json +11 -0
- pytest_selective-1.1.0/.github/workflows/ci.yml +245 -0
- {pytest_selective-0.4.2 → pytest_selective-1.1.0}/.github/workflows/public-actions.yml +17 -1
- {pytest_selective-0.4.2 → pytest_selective-1.1.0}/.github/workflows/release.yml +7 -0
- {pytest_selective-0.4.2 → pytest_selective-1.1.0}/.github/workflows/windows-performance.yml +29 -14
- {pytest_selective-0.4.2 → pytest_selective-1.1.0}/.gitignore +3 -0
- pytest_selective-1.1.0/.release-please-manifest.json +3 -0
- {pytest_selective-0.4.2 → pytest_selective-1.1.0}/AGENTS.md +5 -0
- {pytest_selective-0.4.2 → pytest_selective-1.1.0}/CHANGELOG.md +28 -0
- {pytest_selective-0.4.2 → pytest_selective-1.1.0}/CONTRIBUTING.md +34 -2
- {pytest_selective-0.4.2 → pytest_selective-1.1.0}/PKG-INFO +58 -13
- {pytest_selective-0.4.2 → pytest_selective-1.1.0}/README.md +57 -12
- {pytest_selective-0.4.2 → pytest_selective-1.1.0}/RELEASING.md +23 -1
- pytest_selective-1.1.0/actions/validation-gate/action.yml +32 -0
- pytest_selective-1.1.0/contracts/selection/README.md +41 -0
- pytest_selective-1.1.0/contracts/selection/conftest.py +401 -0
- pytest_selective-1.1.0/contracts/selection/quint-connect.json +147 -0
- pytest_selective-1.1.0/contracts/selection/quint.ini +8 -0
- pytest_selective-1.1.0/contracts/selection/runtime_deps.py +54 -0
- pytest_selective-1.1.0/contracts/selection/selection.qnt +388 -0
- pytest_selective-1.1.0/contracts/selection/selection_driver.py +113 -0
- pytest_selective-1.1.0/contracts/selection/test_selection_contract.py +4 -0
- {pytest_selective-0.4.2 → pytest_selective-1.1.0}/docs/actions.md +1 -0
- {pytest_selective-0.4.2 → pytest_selective-1.1.0}/docs/agents.md +12 -9
- {pytest_selective-0.4.2 → pytest_selective-1.1.0}/docs/benchmarks/windows-runtime.md +61 -0
- {pytest_selective-0.4.2 → pytest_selective-1.1.0}/docs/ci-toolkit.md +17 -1
- {pytest_selective-0.4.2 → pytest_selective-1.1.0}/docs/ci.md +38 -3
- {pytest_selective-0.4.2 → pytest_selective-1.1.0}/docs/configuration.md +3 -2
- {pytest_selective-0.4.2 → pytest_selective-1.1.0}/docs/tutorials/adoption-checklist.md +9 -1
- {pytest_selective-0.4.2 → pytest_selective-1.1.0}/docs/tutorials/skip-unrelated-ci.md +43 -3
- pytest_selective-1.1.0/docs/work-in-progress/tickets/graph-based-inputs-plan.md +58 -0
- {pytest_selective-0.4.2 → pytest_selective-1.1.0}/documentation/check_site.py +24 -1
- {pytest_selective-0.4.2 → pytest_selective-1.1.0}/documentation/hooks.py +20 -3
- {pytest_selective-0.4.2 → pytest_selective-1.1.0}/documentation/index.md +13 -0
- pytest_selective-1.1.0/documentation/llms.txt +31 -0
- {pytest_selective-0.4.2 → pytest_selective-1.1.0}/mkdocs.yml +8 -1
- {pytest_selective-0.4.2 → pytest_selective-1.1.0}/pyproject.toml +3 -1
- pytest_selective-1.1.0/scripts/measure_ci_runs.py +148 -0
- pytest_selective-1.1.0/skills/pytest-selective/SKILL.md +505 -0
- {pytest_selective-0.4.2 → pytest_selective-1.1.0}/src/pytest_selective/ci.py +21 -7
- pytest_selective-1.1.0/src/pytest_selective/gate.py +96 -0
- {pytest_selective-0.4.2 → pytest_selective-1.1.0}/src/pytest_selective/impact_graph.py +158 -13
- {pytest_selective-0.4.2 → pytest_selective-1.1.0}/src/pytest_selective/impact_models.py +3 -0
- pytest_selective-1.1.0/tests/test_ci_measurements.py +121 -0
- {pytest_selective-0.4.2 → pytest_selective-1.1.0}/tests/test_public_actions.py +10 -0
- pytest_selective-1.1.0/tests/test_release_workflow.py +75 -0
- pytest_selective-1.1.0/tests/test_repository_ci.py +217 -0
- {pytest_selective-0.4.2 → pytest_selective-1.1.0}/tests/testing/test_ci.py +94 -0
- pytest_selective-1.1.0/tests/testing/test_gate.py +123 -0
- {pytest_selective-0.4.2 → pytest_selective-1.1.0}/tests/testing/test_global_inputs.py +41 -0
- {pytest_selective-0.4.2 → pytest_selective-1.1.0}/tests/testing/test_impact_graph.py +101 -0
- {pytest_selective-0.4.2 → pytest_selective-1.1.0}/uv.lock +18 -2
- pytest_selective-0.4.2/.github/workflows/ci.yml +0 -154
- pytest_selective-0.4.2/.release-please-manifest.json +0 -3
- pytest_selective-0.4.2/scripts/measure_ci_runs.py +0 -79
- pytest_selective-0.4.2/skills/pytest-selective/SKILL.md +0 -117
- pytest_selective-0.4.2/skills/pytest-selective/references/adoption.md +0 -112
- pytest_selective-0.4.2/tests/test_ci_measurements.py +0 -70
- pytest_selective-0.4.2/tests/test_repository_ci.py +0 -147
- {pytest_selective-0.4.2 → pytest_selective-1.1.0}/.dry-lint/baseline.json +0 -0
- {pytest_selective-0.4.2 → pytest_selective-1.1.0}/.dry-lint/progress.json +0 -0
- {pytest_selective-0.4.2 → pytest_selective-1.1.0}/.dry-lint.toml +0 -0
- {pytest_selective-0.4.2 → pytest_selective-1.1.0}/.gitattributes +0 -0
- {pytest_selective-0.4.2 → pytest_selective-1.1.0}/.github/ISSUE_TEMPLATE/bug_report.yml +0 -0
- {pytest_selective-0.4.2 → pytest_selective-1.1.0}/.github/PULL_REQUEST_TEMPLATE.md +0 -0
- {pytest_selective-0.4.2 → pytest_selective-1.1.0}/.github/dependabot.yml +0 -0
- {pytest_selective-0.4.2 → pytest_selective-1.1.0}/.github/workflows/docs.yml +0 -0
- {pytest_selective-0.4.2 → pytest_selective-1.1.0}/.github/workflows/dry-lint-audit.yml +0 -0
- {pytest_selective-0.4.2 → pytest_selective-1.1.0}/.github/workflows/dry-lint-judge.yml +0 -0
- {pytest_selective-0.4.2 → pytest_selective-1.1.0}/.github/workflows/dry-lint.yml +0 -0
- {pytest_selective-0.4.2 → pytest_selective-1.1.0}/.github/workflows/observe-ci.yml +0 -0
- {pytest_selective-0.4.2 → pytest_selective-1.1.0}/.github/workflows/pr-title.yml +0 -0
- {pytest_selective-0.4.2 → pytest_selective-1.1.0}/.github/workflows/release-please.yml +0 -0
- {pytest_selective-0.4.2 → pytest_selective-1.1.0}/.pre-commit-config.yaml +0 -0
- {pytest_selective-0.4.2 → pytest_selective-1.1.0}/.readthedocs.yaml +0 -0
- {pytest_selective-0.4.2 → pytest_selective-1.1.0}/CLAUDE.md +0 -0
- {pytest_selective-0.4.2 → pytest_selective-1.1.0}/LICENSE +0 -0
- {pytest_selective-0.4.2 → pytest_selective-1.1.0}/PROVENANCE.md +0 -0
- {pytest_selective-0.4.2 → pytest_selective-1.1.0}/action.yml +0 -0
- {pytest_selective-0.4.2 → pytest_selective-1.1.0}/actions/README.md +0 -0
- {pytest_selective-0.4.2 → pytest_selective-1.1.0}/actions/plan-lanes/action.yml +0 -0
- {pytest_selective-0.4.2 → pytest_selective-1.1.0}/actions/pytest-expansion/action.yml +0 -0
- {pytest_selective-0.4.2 → pytest_selective-1.1.0}/actions/test-impact/action.yml +0 -0
- {pytest_selective-0.4.2 → pytest_selective-1.1.0}/agentsync.toml +0 -0
- {pytest_selective-0.4.2 → pytest_selective-1.1.0}/docs/README.md +0 -0
- {pytest_selective-0.4.2 → pytest_selective-1.1.0}/docs/assets/banner.png +0 -0
- {pytest_selective-0.4.2 → pytest_selective-1.1.0}/docs/assets/banner.svg +0 -0
- {pytest_selective-0.4.2 → pytest_selective-1.1.0}/docs/assets/brand.json +0 -0
- {pytest_selective-0.4.2 → pytest_selective-1.1.0}/docs/assets/icon.png +0 -0
- {pytest_selective-0.4.2 → pytest_selective-1.1.0}/docs/assets/logo.svg +0 -0
- {pytest_selective-0.4.2 → pytest_selective-1.1.0}/docs/assets/raster-sources.json +0 -0
- {pytest_selective-0.4.2 → pytest_selective-1.1.0}/docs/assets/social-preview.png +0 -0
- {pytest_selective-0.4.2 → pytest_selective-1.1.0}/docs/assets/social-preview.svg +0 -0
- {pytest_selective-0.4.2 → pytest_selective-1.1.0}/docs/benchmarks/2026-10-22-due-ci.json +0 -0
- {pytest_selective-0.4.2 → pytest_selective-1.1.0}/docs/benchmarks/2026-10-22-due-docs-ci.json +0 -0
- {pytest_selective-0.4.2 → pytest_selective-1.1.0}/docs/benchmarks/2026-10-22-due-docs-widget.json +0 -0
- {pytest_selective-0.4.2 → pytest_selective-1.1.0}/docs/benchmarks/2026-10-22-due-widget.json +0 -0
- {pytest_selective-0.4.2 → pytest_selective-1.1.0}/docs/benchmarks/2026-10-22-input-replays.json +0 -0
- {pytest_selective-0.4.2 → pytest_selective-1.1.0}/docs/benchmarks/2026-10-22-obligation-docs.json +0 -0
- {pytest_selective-0.4.2 → pytest_selective-1.1.0}/docs/benchmarks/2026-10-22-obligation.json +0 -0
- {pytest_selective-0.4.2 → pytest_selective-1.1.0}/docs/benchmarks/2026-10-22-routing.md +0 -0
- {pytest_selective-0.4.2 → pytest_selective-1.1.0}/docs/benchmarks/2026-10-22-selective-docs.json +0 -0
- {pytest_selective-0.4.2 → pytest_selective-1.1.0}/docs/benchmarks/2026-10-22.md +0 -0
- {pytest_selective-0.4.2 → pytest_selective-1.1.0}/docs/benchmarks/2026-10-23-windows-saleor/obligation-baseline.json +0 -0
- {pytest_selective-0.4.2 → pytest_selective-1.1.0}/docs/benchmarks/2026-10-23-windows-saleor/selective-after-3.11.xml +0 -0
- {pytest_selective-0.4.2 → pytest_selective-1.1.0}/docs/benchmarks/2026-10-23-windows-saleor/selective-after-3.14.xml +0 -0
- {pytest_selective-0.4.2 → pytest_selective-1.1.0}/docs/benchmarks/2026-10-23-windows-saleor/selective-after.json +0 -0
- {pytest_selective-0.4.2 → pytest_selective-1.1.0}/docs/benchmarks/2026-10-23-windows-saleor/selective-control-3.11.xml +0 -0
- {pytest_selective-0.4.2 → pytest_selective-1.1.0}/docs/benchmarks/2026-10-23-windows-saleor/selective-control-3.14.xml +0 -0
- {pytest_selective-0.4.2 → pytest_selective-1.1.0}/docs/benchmarks/2026-10-23-windows-saleor/selective-control.json +0 -0
- {pytest_selective-0.4.2 → pytest_selective-1.1.0}/docs/benchmarks/2026-10-23-windows-saleor/selective-earlier.json +0 -0
- {pytest_selective-0.4.2 → pytest_selective-1.1.0}/docs/benchmarks/2026-10-23-windows-saleor.md +0 -0
- {pytest_selective-0.4.2 → pytest_selective-1.1.0}/docs/comparison-pytest-impacted.md +0 -0
- {pytest_selective-0.4.2 → pytest_selective-1.1.0}/docs/evidence.md +0 -0
- {pytest_selective-0.4.2 → pytest_selective-1.1.0}/docs/local-development.md +0 -0
- {pytest_selective-0.4.2 → pytest_selective-1.1.0}/docs/pytest-hygiene.md +0 -0
- {pytest_selective-0.4.2 → pytest_selective-1.1.0}/docs/selection-health.md +0 -0
- {pytest_selective-0.4.2 → pytest_selective-1.1.0}/docs/tutorial.md +0 -0
- {pytest_selective-0.4.2 → pytest_selective-1.1.0}/docs/tutorials/sidecar-dependencies.md +0 -0
- {pytest_selective-0.4.2 → pytest_selective-1.1.0}/docs/work-in-progress/tickets/actions-directory-test-plan.md +0 -0
- {pytest_selective-0.4.2 → pytest_selective-1.1.0}/docs/work-in-progress/tickets/codex-selector-correctness-performance-test-plan.md +0 -0
- {pytest_selective-0.4.2 → pytest_selective-1.1.0}/docs/work-in-progress/tickets/codex-windows-git-performance-test-plan.md +0 -0
- {pytest_selective-0.4.2 → pytest_selective-1.1.0}/docs/work-in-progress/tickets/impacted-improvements-test-plan.md +0 -0
- {pytest_selective-0.4.2 → pytest_selective-1.1.0}/docs/work-in-progress/tickets/move-action-implementations-test-plan.md +0 -0
- {pytest_selective-0.4.2 → pytest_selective-1.1.0}/docs/work-in-progress/tickets/public-action-onboarding-test-plan.md +0 -0
- {pytest_selective-0.4.2 → pytest_selective-1.1.0}/docs/work-in-progress/tickets/ratchet-duplicates-zero-test-plan.md +0 -0
- {pytest_selective-0.4.2 → pytest_selective-1.1.0}/docs/work-in-progress/tickets/rerun-selection-test-plan.md +0 -0
- {pytest_selective-0.4.2 → pytest_selective-1.1.0}/documentation/assets/routing-comparison.svg +0 -0
- {pytest_selective-0.4.2 → pytest_selective-1.1.0}/documentation/ecosystem.md +0 -0
- {pytest_selective-0.4.2 → pytest_selective-1.1.0}/documentation/overrides/main.html +0 -0
- {pytest_selective-0.4.2 → pytest_selective-1.1.0}/documentation/publishing.md +0 -0
- {pytest_selective-0.4.2 → pytest_selective-1.1.0}/documentation/requirements.in +0 -0
- {pytest_selective-0.4.2 → pytest_selective-1.1.0}/documentation/requirements.txt +0 -0
- {pytest_selective-0.4.2 → pytest_selective-1.1.0}/release-please-config.json +0 -0
- {pytest_selective-0.4.2 → pytest_selective-1.1.0}/scripts/benchmark_graph.py +0 -0
- {pytest_selective-0.4.2 → pytest_selective-1.1.0}/scripts/benchmark_parsing.py +0 -0
- {pytest_selective-0.4.2 → pytest_selective-1.1.0}/scripts/benchmark_rust.py +0 -0
- {pytest_selective-0.4.2 → pytest_selective-1.1.0}/scripts/check_docs.py +0 -0
- {pytest_selective-0.4.2 → pytest_selective-1.1.0}/scripts/check_metadata.py +0 -0
- {pytest_selective-0.4.2 → pytest_selective-1.1.0}/scripts/ci_file_config.py +0 -0
- {pytest_selective-0.4.2 → pytest_selective-1.1.0}/scripts/compare_impacted.py +0 -0
- {pytest_selective-0.4.2 → pytest_selective-1.1.0}/scripts/public_actions.py +0 -0
- {pytest_selective-0.4.2 → pytest_selective-1.1.0}/scripts/render_brand.py +0 -0
- {pytest_selective-0.4.2 → pytest_selective-1.1.0}/scripts/run_ci_tests.py +0 -0
- {pytest_selective-0.4.2 → pytest_selective-1.1.0}/scripts/smoke_distribution.py +0 -0
- {pytest_selective-0.4.2 → pytest_selective-1.1.0}/scripts/tutorial.py +0 -0
- {pytest_selective-0.4.2 → pytest_selective-1.1.0}/skills/pytest-selective/agents/openai.yaml +0 -0
- {pytest_selective-0.4.2 → pytest_selective-1.1.0}/src/pytest_selective/__init__.py +0 -0
- {pytest_selective-0.4.2 → pytest_selective-1.1.0}/src/pytest_selective/__main__.py +0 -0
- {pytest_selective-0.4.2 → pytest_selective-1.1.0}/src/pytest_selective/ast_imports.py +0 -0
- {pytest_selective-0.4.2 → pytest_selective-1.1.0}/src/pytest_selective/checkpoint.py +0 -0
- {pytest_selective-0.4.2 → pytest_selective-1.1.0}/src/pytest_selective/git_operations.py +0 -0
- {pytest_selective-0.4.2 → pytest_selective-1.1.0}/src/pytest_selective/hygiene.py +0 -0
- {pytest_selective-0.4.2 → pytest_selective-1.1.0}/src/pytest_selective/impact_audit.py +0 -0
- {pytest_selective-0.4.2 → pytest_selective-1.1.0}/src/pytest_selective/impact_audit_plugin.py +0 -0
- {pytest_selective-0.4.2 → pytest_selective-1.1.0}/src/pytest_selective/impact_cli.py +0 -0
- {pytest_selective-0.4.2 → pytest_selective-1.1.0}/src/pytest_selective/impact_declarations.py +0 -0
- {pytest_selective-0.4.2 → pytest_selective-1.1.0}/src/pytest_selective/impact_explanation.py +0 -0
- {pytest_selective-0.4.2 → pytest_selective-1.1.0}/src/pytest_selective/impact_first_party.py +0 -0
- {pytest_selective-0.4.2 → pytest_selective-1.1.0}/src/pytest_selective/impact_name_scope.py +0 -0
- {pytest_selective-0.4.2 → pytest_selective-1.1.0}/src/pytest_selective/impact_selection.py +0 -0
- {pytest_selective-0.4.2 → pytest_selective-1.1.0}/src/pytest_selective/input_policy.py +0 -0
- {pytest_selective-0.4.2 → pytest_selective-1.1.0}/src/pytest_selective/local.py +0 -0
- {pytest_selective-0.4.2 → pytest_selective-1.1.0}/src/pytest_selective/parse_batch.py +0 -0
- {pytest_selective-0.4.2 → pytest_selective-1.1.0}/src/pytest_selective/plugin.py +0 -0
- {pytest_selective-0.4.2 → pytest_selective-1.1.0}/src/pytest_selective/py.typed +0 -0
- {pytest_selective-0.4.2 → pytest_selective-1.1.0}/src/pytest_selective/pytest_expansion.py +0 -0
- {pytest_selective-0.4.2 → pytest_selective-1.1.0}/src/pytest_selective/pytest_plugins.py +0 -0
- {pytest_selective-0.4.2 → pytest_selective-1.1.0}/src/pytest_selective/pytest_sharding.py +0 -0
- {pytest_selective-0.4.2 → pytest_selective-1.1.0}/src/pytest_selective/test_inventory.py +0 -0
- {pytest_selective-0.4.2 → pytest_selective-1.1.0}/src/pytest_selective/typecheck_scope.py +0 -0
- {pytest_selective-0.4.2 → pytest_selective-1.1.0}/src/pytest_selective/working_tree.py +0 -0
- {pytest_selective-0.4.2 → pytest_selective-1.1.0}/tests/__init__.py +0 -0
- {pytest_selective-0.4.2 → pytest_selective-1.1.0}/tests/test_checkpoint_policy.py +0 -0
- {pytest_selective-0.4.2 → pytest_selective-1.1.0}/tests/test_ci_admission.py +0 -0
- {pytest_selective-0.4.2 → pytest_selective-1.1.0}/tests/test_ci_test_execution.py +0 -0
- {pytest_selective-0.4.2 → pytest_selective-1.1.0}/tests/test_distribution_metadata.py +0 -0
- {pytest_selective-0.4.2 → pytest_selective-1.1.0}/tests/test_hygiene.py +0 -0
- {pytest_selective-0.4.2 → pytest_selective-1.1.0}/tests/test_pytest_expansion.py +0 -0
- {pytest_selective-0.4.2 → pytest_selective-1.1.0}/tests/test_repository_hygiene.py +0 -0
- {pytest_selective-0.4.2 → pytest_selective-1.1.0}/tests/test_repository_hygiene.py.gv-dependency.yaml +0 -0
- {pytest_selective-0.4.2 → pytest_selective-1.1.0}/tests/test_tutorial.py +0 -0
- {pytest_selective-0.4.2 → pytest_selective-1.1.0}/tests/testing/__init__.py +0 -0
- {pytest_selective-0.4.2 → pytest_selective-1.1.0}/tests/testing/git_repo.py +0 -0
- {pytest_selective-0.4.2 → pytest_selective-1.1.0}/tests/testing/test_ast_imports.py +0 -0
- {pytest_selective-0.4.2 → pytest_selective-1.1.0}/tests/testing/test_git_repo.py +0 -0
- {pytest_selective-0.4.2 → pytest_selective-1.1.0}/tests/testing/test_impact_audit.py +0 -0
- {pytest_selective-0.4.2 → pytest_selective-1.1.0}/tests/testing/test_impact_explanation.py +0 -0
- {pytest_selective-0.4.2 → pytest_selective-1.1.0}/tests/testing/test_impact_first_party.py +0 -0
- {pytest_selective-0.4.2 → pytest_selective-1.1.0}/tests/testing/test_impact_name_scope.py +0 -0
- {pytest_selective-0.4.2 → pytest_selective-1.1.0}/tests/testing/test_parse_batch.py +0 -0
- {pytest_selective-0.4.2 → pytest_selective-1.1.0}/tests/testing/test_portability.py +0 -0
- {pytest_selective-0.4.2 → pytest_selective-1.1.0}/tests/testing/test_pytest_plugin.py +0 -0
- {pytest_selective-0.4.2 → pytest_selective-1.1.0}/tests/testing/test_pytest_plugins.py +0 -0
- {pytest_selective-0.4.2 → pytest_selective-1.1.0}/tests/testing/test_pytest_sharding.py +0 -0
- {pytest_selective-0.4.2 → pytest_selective-1.1.0}/tests/testing/test_typecheck_scope.py +0 -0
- {pytest_selective-0.4.2 → pytest_selective-1.1.0}/tests/testing/test_working_tree.py +0 -0
|
@@ -7,6 +7,11 @@
|
|
|
7
7
|
"test_roots": [
|
|
8
8
|
"tests"
|
|
9
9
|
],
|
|
10
|
+
"global_inputs": [
|
|
11
|
+
"pyproject.toml",
|
|
12
|
+
"uv.lock",
|
|
13
|
+
".python-version"
|
|
14
|
+
],
|
|
10
15
|
"pytest_policy": {
|
|
11
16
|
"pyproject.toml": "7a78d68735c0e3b091962244620d3a17557951123c645970638f15d3a737d987"
|
|
12
17
|
}
|
|
@@ -20,6 +25,27 @@
|
|
|
20
25
|
"src/**",
|
|
21
26
|
"tests/**"
|
|
22
27
|
]
|
|
28
|
+
},
|
|
29
|
+
"contracts": {
|
|
30
|
+
"roots": [
|
|
31
|
+
"contracts"
|
|
32
|
+
],
|
|
33
|
+
"inputs": [
|
|
34
|
+
"src/**",
|
|
35
|
+
"contracts/**",
|
|
36
|
+
"tests/testing/git_repo.py"
|
|
37
|
+
]
|
|
38
|
+
},
|
|
39
|
+
"package": {
|
|
40
|
+
"inputs": [
|
|
41
|
+
"src/**",
|
|
42
|
+
"pyproject.toml",
|
|
43
|
+
"uv.lock",
|
|
44
|
+
"README.md",
|
|
45
|
+
"LICENSE",
|
|
46
|
+
"scripts/check_metadata.py",
|
|
47
|
+
"scripts/smoke_distribution.py"
|
|
48
|
+
]
|
|
23
49
|
}
|
|
24
50
|
},
|
|
25
51
|
"global_inputs": [
|
|
@@ -33,9 +59,9 @@
|
|
|
33
59
|
".github/test-lanes.json",
|
|
34
60
|
".github/workflows/ci.yml",
|
|
35
61
|
".github/workflows/release.yml",
|
|
36
|
-
"actions
|
|
62
|
+
"actions/*/*",
|
|
63
|
+
"actions/*.py",
|
|
37
64
|
"action.yml",
|
|
38
|
-
"examples/**",
|
|
39
65
|
".github/workflows/public-actions.yml"
|
|
40
66
|
]
|
|
41
67
|
}
|
|
@@ -0,0 +1,245 @@
|
|
|
1
|
+
name: CI
|
|
2
|
+
on:
|
|
3
|
+
push:
|
|
4
|
+
branches: [main]
|
|
5
|
+
pull_request:
|
|
6
|
+
types: [opened, synchronize, reopened]
|
|
7
|
+
workflow_dispatch:
|
|
8
|
+
workflow_call:
|
|
9
|
+
schedule:
|
|
10
|
+
- cron: '13 3 * * 1'
|
|
11
|
+
permissions:
|
|
12
|
+
contents: read
|
|
13
|
+
concurrency:
|
|
14
|
+
# Only newer revisions of the same PR supersede work; keep every main/manual run.
|
|
15
|
+
group: ci-${{ github.workflow }}-${{ github.event_name == 'pull_request' && github.run_attempt == 1 && github.event.pull_request.number || github.run_id }}
|
|
16
|
+
cancel-in-progress: ${{ github.event_name == 'pull_request' && github.run_attempt == 1 }}
|
|
17
|
+
jobs:
|
|
18
|
+
select-tests:
|
|
19
|
+
runs-on: ubuntu-latest
|
|
20
|
+
permissions:
|
|
21
|
+
contents: read
|
|
22
|
+
actions: read # Receipts are completed steps of earlier runs' jobs.
|
|
23
|
+
checks: read
|
|
24
|
+
pull-requests: read
|
|
25
|
+
env:
|
|
26
|
+
# The jobs whose passing first attempts can prove the tests lane, by exact job name.
|
|
27
|
+
TEST_LANE_JOBS: >-
|
|
28
|
+
{"py311-ubuntu":"tests (3.11, ubuntu-latest)","py311-windows":"tests (3.11, windows-latest)",
|
|
29
|
+
"py311-macos":"tests (3.11, macos-latest)","py314-ubuntu":"tests (3.14, ubuntu-latest)",
|
|
30
|
+
"py314-windows":"tests (3.14, windows-latest)","py314-macos":"tests (3.14, macos-latest)",
|
|
31
|
+
"minimum":"pytest-minimum"}
|
|
32
|
+
outputs:
|
|
33
|
+
# Planned lanes after reuse: the tests lane is false only when every one of its jobs is proven by a receipt.
|
|
34
|
+
lanes: ${{ steps.admit.outputs.lanes }}
|
|
35
|
+
# The plan before reuse, for steps that follow changed runtime inputs rather than test execution.
|
|
36
|
+
planned: ${{ steps.plan.outputs.lanes }}
|
|
37
|
+
mode: ${{ steps.files.outputs.mode }}
|
|
38
|
+
files: ${{ steps.files.outputs.test_roots_json }}
|
|
39
|
+
receipt: ${{ steps.evidence.outputs.receipt }}
|
|
40
|
+
reused: ${{ steps.evidence.outputs.reused }}
|
|
41
|
+
steps:
|
|
42
|
+
- uses: actions/checkout@v7
|
|
43
|
+
with:
|
|
44
|
+
fetch-depth: 0
|
|
45
|
+
# GitHub often omits a run's pull_requests link. This marker binds the run to its PR and head, so the push
|
|
46
|
+
# after a squash merge can use this run's receipts. CI-esta matches the exact name.
|
|
47
|
+
- name: "PR evidence #${{ github.event.pull_request.number }} head ${{ github.event.pull_request.head.sha }}"
|
|
48
|
+
if: github.event_name == 'pull_request'
|
|
49
|
+
run: 'true'
|
|
50
|
+
- id: plan
|
|
51
|
+
uses: ./actions/plan-lanes
|
|
52
|
+
with:
|
|
53
|
+
config: .github/test-lanes.json
|
|
54
|
+
- name: Share the repository graph and execution inputs with file selection
|
|
55
|
+
env:
|
|
56
|
+
PYTHONPATH: src
|
|
57
|
+
run: |
|
|
58
|
+
uv run --no-project --python 3.14 --with pydantic==2.13.4 --with pyyaml==6.0.3 \
|
|
59
|
+
python scripts/ci_file_config.py "$RUNNER_TEMP/test-impact.json"
|
|
60
|
+
- id: files
|
|
61
|
+
uses: ./
|
|
62
|
+
with:
|
|
63
|
+
config: ${{ runner.temp }}/test-impact.json
|
|
64
|
+
full-roots: tests
|
|
65
|
+
# Everything the tests lane executes or reads, bound into each receipt.
|
|
66
|
+
- id: inputs
|
|
67
|
+
run: echo "paths=$(jq -c . .github/validation-inputs.json)" >> "$GITHUB_OUTPUT"
|
|
68
|
+
# CI-esta reuses a passing first attempt of the same job when its inputs and selected files are identical:
|
|
69
|
+
# after a squash merge, on a version-only release PR, and on a PR push that changes nothing the tests read.
|
|
70
|
+
# Release (workflow_call), schedules, manual runs and the test:full label always execute.
|
|
71
|
+
- id: evidence
|
|
72
|
+
if: >-
|
|
73
|
+
${{ (github.event_name == 'pull_request' || github.event_name == 'push') &&
|
|
74
|
+
!contains(github.event.pull_request.labels.*.name, 'test:full') }}
|
|
75
|
+
uses: gigaverse-app/ci-esta/actions/resolve-lane-evidence@v1
|
|
76
|
+
with:
|
|
77
|
+
lanes: ${{ env.TEST_LANE_JOBS }}
|
|
78
|
+
execution-context: ${{ steps.files.outputs.mode }} ${{ steps.files.outputs.test_roots_json }}
|
|
79
|
+
input-paths: ${{ steps.inputs.outputs.paths }}
|
|
80
|
+
reuse-after-merge: 'true'
|
|
81
|
+
reuse-version-only-release: 'true'
|
|
82
|
+
- id: admit
|
|
83
|
+
name: Admit the tests lane unless every job is proven
|
|
84
|
+
env:
|
|
85
|
+
PLANNED_LANES: ${{ steps.plan.outputs.lanes }}
|
|
86
|
+
REUSED: ${{ steps.evidence.outputs.reused || '{}' }}
|
|
87
|
+
run: |
|
|
88
|
+
admitted=$(jq -c --argjson jobs "$TEST_LANE_JOBS" --argjson reused "$REUSED" 'if [$jobs | keys[] | $reused[.] == true] | all then .tests = false else . end' <<<"$PLANNED_LANES")
|
|
89
|
+
echo "reused=$REUSED"
|
|
90
|
+
echo "lanes=$admitted" | tee -a "$GITHUB_OUTPUT"
|
|
91
|
+
quality:
|
|
92
|
+
runs-on: ubuntu-latest
|
|
93
|
+
steps:
|
|
94
|
+
- uses: actions/checkout@v7
|
|
95
|
+
- uses: gigaverse-app/ci-esta/actions/setup-cached-uv@v1
|
|
96
|
+
with:
|
|
97
|
+
python-version: '3.14'
|
|
98
|
+
# Semantic judging owns its credentials and fork boundary in the required dry-lint job.
|
|
99
|
+
- run: uv run --locked pre-commit run --all-files
|
|
100
|
+
env:
|
|
101
|
+
SKIP: dry-lint
|
|
102
|
+
- name: Check aggregate gate against the execution lanes
|
|
103
|
+
run: >-
|
|
104
|
+
uv run --no-project --python 3.14
|
|
105
|
+
--with "ci-esta>=1.1.0,<2.0.0"
|
|
106
|
+
ci-esta check-workflow --workflow .github/workflows/ci.yml --lanes .github/test-lanes.json
|
|
107
|
+
- name: Validate GitHub Actions
|
|
108
|
+
run: docker run --rm -v "$PWD:/repo" -w /repo rhysd/actionlint:1.7.7 -color
|
|
109
|
+
tests:
|
|
110
|
+
needs: select-tests
|
|
111
|
+
if: >-
|
|
112
|
+
${{ always() && !cancelled() && (needs.select-tests.result != 'success' ||
|
|
113
|
+
fromJSON(needs.select-tests.outputs.lanes).tests) }}
|
|
114
|
+
strategy:
|
|
115
|
+
fail-fast: false
|
|
116
|
+
matrix:
|
|
117
|
+
python: ['3.11', '3.14']
|
|
118
|
+
os: [ubuntu-latest, windows-latest, macos-latest]
|
|
119
|
+
runs-on: ${{ matrix.os }}
|
|
120
|
+
steps:
|
|
121
|
+
- uses: actions/checkout@v7
|
|
122
|
+
- uses: gigaverse-app/ci-esta/actions/setup-cached-uv@v1
|
|
123
|
+
with:
|
|
124
|
+
python-version: ${{ matrix.python }}
|
|
125
|
+
- name: Use native Git for Windows
|
|
126
|
+
uses: gigaverse-app/ci-esta/actions/native-windows-git@v1
|
|
127
|
+
- name: Record coverage runtime
|
|
128
|
+
run: uv run --locked python -m coverage --version
|
|
129
|
+
- name: Execute the shared affected-file plan
|
|
130
|
+
run: uv run --locked python scripts/run_ci_tests.py -q -n 4 --durations=20 --junitxml=test-timings.xml
|
|
131
|
+
env:
|
|
132
|
+
CI_PLANNER_RESULT: ${{ needs.select-tests.result }}
|
|
133
|
+
CI_TEST_MODE: ${{ needs.select-tests.outputs.mode }}
|
|
134
|
+
CI_TEST_FILES: ${{ needs.select-tests.outputs.files }}
|
|
135
|
+
CI_COVERAGE: 'true'
|
|
136
|
+
# Keep temporary Git repositories beside the checkout on the work disk.
|
|
137
|
+
TMP: ${{ runner.temp }}
|
|
138
|
+
TEMP: ${{ runner.temp }}
|
|
139
|
+
TMPDIR: ${{ runner.temp }}
|
|
140
|
+
- uses: gigaverse-app/ci-esta/actions/learn-test-timings@v1
|
|
141
|
+
if: ${{ hashFiles('test-timings.xml') != '' }}
|
|
142
|
+
with:
|
|
143
|
+
junit-pattern: test-timings.xml
|
|
144
|
+
artifact-name: test-timings-${{ matrix.python }}-${{ matrix.os }}
|
|
145
|
+
# tests/test_tutorial.py owns the end-to-end tutorial and follows the file plan.
|
|
146
|
+
- uses: actions/upload-artifact@v7
|
|
147
|
+
if: ${{ hashFiles('coverage.xml') != '' }}
|
|
148
|
+
with:
|
|
149
|
+
name: evidence-${{ matrix.os }}-${{ matrix.python }}
|
|
150
|
+
path: coverage.xml
|
|
151
|
+
- name: ${{ needs.select-tests.outputs.receipt || 'No execution receipt' }}
|
|
152
|
+
if: ${{ needs.select-tests.result == 'success' && needs.select-tests.outputs.receipt != '' }}
|
|
153
|
+
run: echo 'Recording a completed execution of this job for this tested tree and selection.'
|
|
154
|
+
pytest-minimum:
|
|
155
|
+
needs: select-tests
|
|
156
|
+
if: >-
|
|
157
|
+
${{ always() && !cancelled() && (needs.select-tests.result != 'success' ||
|
|
158
|
+
fromJSON(needs.select-tests.outputs.lanes).tests) }}
|
|
159
|
+
runs-on: ubuntu-latest
|
|
160
|
+
steps:
|
|
161
|
+
- uses: actions/checkout@v7
|
|
162
|
+
- uses: gigaverse-app/ci-esta/actions/setup-cached-uv@v1
|
|
163
|
+
with:
|
|
164
|
+
python-version: '3.11'
|
|
165
|
+
- run: uv pip install 'pytest==8.0.0'
|
|
166
|
+
- name: Execute the same file plan on minimum pytest
|
|
167
|
+
run: uv run --no-sync python scripts/run_ci_tests.py -q -n 4 --durations=20
|
|
168
|
+
env:
|
|
169
|
+
CI_PLANNER_RESULT: ${{ needs.select-tests.result }}
|
|
170
|
+
CI_TEST_MODE: ${{ needs.select-tests.outputs.mode }}
|
|
171
|
+
CI_TEST_FILES: ${{ needs.select-tests.outputs.files }}
|
|
172
|
+
- name: ${{ needs.select-tests.outputs.receipt || 'No execution receipt' }}
|
|
173
|
+
if: ${{ needs.select-tests.result == 'success' && needs.select-tests.outputs.receipt != '' }}
|
|
174
|
+
run: echo 'Recording a completed execution of this job for this tested tree and selection.'
|
|
175
|
+
distributions:
|
|
176
|
+
needs: select-tests
|
|
177
|
+
# The package lane includes the embedded README and LICENSE; planner failure must not skip it.
|
|
178
|
+
if: >-
|
|
179
|
+
${{ always() && !cancelled() && (needs.select-tests.result != 'success' ||
|
|
180
|
+
fromJSON(needs.select-tests.outputs.lanes).package) }}
|
|
181
|
+
runs-on: ubuntu-latest
|
|
182
|
+
steps:
|
|
183
|
+
- uses: actions/checkout@v7
|
|
184
|
+
- uses: gigaverse-app/ci-esta/actions/setup-cached-uv@v1
|
|
185
|
+
with:
|
|
186
|
+
python-version: '3.14'
|
|
187
|
+
- run: uv build
|
|
188
|
+
- run: uv run --locked twine check --strict dist/*
|
|
189
|
+
- run: uv run --locked python scripts/check_metadata.py
|
|
190
|
+
- name: Smoke installed distributions when runtime inputs change
|
|
191
|
+
if: >-
|
|
192
|
+
${{ needs.select-tests.result != 'success' ||
|
|
193
|
+
fromJSON(needs.select-tests.outputs.planned).tests }}
|
|
194
|
+
run: uv run --locked python scripts/smoke_distribution.py
|
|
195
|
+
- uses: actions/upload-artifact@v7
|
|
196
|
+
with:
|
|
197
|
+
name: distributions
|
|
198
|
+
path: dist/*
|
|
199
|
+
if-no-files-found: error
|
|
200
|
+
|
|
201
|
+
selection-contract:
|
|
202
|
+
name: Selection contract (Quint)
|
|
203
|
+
needs: select-tests
|
|
204
|
+
permissions:
|
|
205
|
+
contents: read
|
|
206
|
+
# The contract report's pull-request comment.
|
|
207
|
+
pull-requests: write
|
|
208
|
+
if: >-
|
|
209
|
+
${{ always() && !cancelled() && (needs.select-tests.result != 'success' ||
|
|
210
|
+
fromJSON(needs.select-tests.outputs.lanes).contracts) }}
|
|
211
|
+
runs-on: ubuntu-latest
|
|
212
|
+
steps:
|
|
213
|
+
- uses: actions/checkout@v7
|
|
214
|
+
- uses: gigaverse-app/ci-esta/actions/setup-cached-uv@v1
|
|
215
|
+
with:
|
|
216
|
+
python-version: '3.14'
|
|
217
|
+
# pytest-quint-connect installs its locked Quint with npm on first use.
|
|
218
|
+
- uses: actions/setup-node@v7
|
|
219
|
+
with:
|
|
220
|
+
node-version: '24'
|
|
221
|
+
- name: Check the generated driver against the model
|
|
222
|
+
run: uv run --locked --group quint pytest-quint-connect codegen --check contracts/selection/quint-connect.json
|
|
223
|
+
- name: Generate histories from the model and replay them against the planner
|
|
224
|
+
run: uv run --locked --group quint pytest -c contracts/selection/quint.ini contracts/selection --quint-require-complete -q
|
|
225
|
+
# Match the README with the same locked renderer that produced these receipts.
|
|
226
|
+
# A presentation update to the floating report action must not break an immutable release.
|
|
227
|
+
- name: Check the README against the locked contract renderer
|
|
228
|
+
run: uv run --locked --group quint pytest-quint-connect report '**/.artifacts/quint-connect/results.json' --readme README.md --check
|
|
229
|
+
# The current action owns the sticky and artifacts, independently of README formatting.
|
|
230
|
+
- uses: gigaverse-app/pytest-quint-connect/report@v1
|
|
231
|
+
if: ${{ always() }}
|
|
232
|
+
with:
|
|
233
|
+
readme-mode: 'off'
|
|
234
|
+
|
|
235
|
+
validation-gate:
|
|
236
|
+
name: Validation gate
|
|
237
|
+
needs: [select-tests, quality, tests, pytest-minimum, distributions, selection-contract]
|
|
238
|
+
if: ${{ !cancelled() }}
|
|
239
|
+
runs-on: ubuntu-latest
|
|
240
|
+
steps:
|
|
241
|
+
- uses: gigaverse-app/ci-esta/actions/validation-gate@v1
|
|
242
|
+
with:
|
|
243
|
+
needs: ${{ toJSON(needs) }}
|
|
244
|
+
lanes: ${{ needs.select-tests.outputs.lanes || '{}' }}
|
|
245
|
+
lane-jobs: '{"tests":["tests"],"pytest-minimum":["tests"],"distributions":["package"],"selection-contract":["contracts"]}'
|
|
@@ -4,6 +4,8 @@ on:
|
|
|
4
4
|
push:
|
|
5
5
|
branches: [main]
|
|
6
6
|
workflow_dispatch:
|
|
7
|
+
schedule:
|
|
8
|
+
- cron: '33 3 * * 1'
|
|
7
9
|
permissions:
|
|
8
10
|
contents: read
|
|
9
11
|
jobs:
|
|
@@ -67,8 +69,22 @@ jobs:
|
|
|
67
69
|
run: |
|
|
68
70
|
python - <<'PY'
|
|
69
71
|
import json, os
|
|
70
|
-
|
|
72
|
+
# A full checkpoint admits every configured lane.
|
|
73
|
+
configured = json.load(open('.github/test-lanes.json', encoding='utf-8'))['lanes']
|
|
74
|
+
assert json.loads(os.environ['LANES']) == dict.fromkeys(configured, True)
|
|
71
75
|
PY
|
|
72
76
|
- name: Require successful planning after fallback execution
|
|
73
77
|
if: ${{ !cancelled() && needs.select-tests.result != 'success' }}
|
|
74
78
|
run: exit 1
|
|
79
|
+
gate:
|
|
80
|
+
name: Public action gate
|
|
81
|
+
needs: [select-tests, public-actions]
|
|
82
|
+
if: ${{ !cancelled() }}
|
|
83
|
+
runs-on: ubuntu-latest
|
|
84
|
+
steps:
|
|
85
|
+
- uses: actions/checkout@v7
|
|
86
|
+
# Dogfood the public gate: the smoke may be skipped only when the successful plan said so.
|
|
87
|
+
- uses: ./actions/validation-gate
|
|
88
|
+
with:
|
|
89
|
+
needs: ${{ toJSON(needs) }}
|
|
90
|
+
lane-jobs: '{"public-actions":["tests"]}'
|
|
@@ -21,6 +21,13 @@ jobs:
|
|
|
21
21
|
uv run --no-project python -c 'import os, pathlib, tomllib; version = tomllib.loads(pathlib.Path("pyproject.toml").read_text())["project"]["version"]; assert os.environ["RELEASE_REF"] == f"refs/tags/v{version}", "Run publishing from the version-matching v tag"'
|
|
22
22
|
checks:
|
|
23
23
|
needs: validate-tag
|
|
24
|
+
# A called workflow gets no more than this, and GitHub refuses to start it when a job asks for more: the Quint
|
|
25
|
+
# report needs pull-requests: write, and select-tests reads earlier runs' jobs and checks for lane receipts.
|
|
26
|
+
permissions:
|
|
27
|
+
contents: read
|
|
28
|
+
actions: read
|
|
29
|
+
checks: read
|
|
30
|
+
pull-requests: write
|
|
24
31
|
uses: ./.github/workflows/ci.yml
|
|
25
32
|
publish:
|
|
26
33
|
needs: checks
|
|
@@ -1,17 +1,19 @@
|
|
|
1
1
|
name: Matched Windows performance
|
|
2
2
|
on:
|
|
3
|
-
pull_request:
|
|
4
|
-
paths: ['.github/workflows/windows-performance.yml']
|
|
5
3
|
workflow_dispatch:
|
|
6
4
|
inputs:
|
|
7
5
|
baseline:
|
|
8
6
|
description: Immutable baseline commit to compare with the selected ref
|
|
9
7
|
required: true
|
|
8
|
+
candidate-workers:
|
|
9
|
+
description: Windows xdist worker count to compare with the existing four workers
|
|
10
|
+
type: number
|
|
11
|
+
default: 4
|
|
12
|
+
ci-esta-ref:
|
|
13
|
+
description: CI-esta ref containing the candidate native-Git action
|
|
14
|
+
default: v1
|
|
10
15
|
permissions:
|
|
11
16
|
contents: read
|
|
12
|
-
concurrency:
|
|
13
|
-
group: windows-performance-${{ github.event.pull_request.number || github.run_id }}
|
|
14
|
-
cancel-in-progress: ${{ github.event_name == 'pull_request' }}
|
|
15
17
|
jobs:
|
|
16
18
|
compare:
|
|
17
19
|
runs-on: windows-latest
|
|
@@ -23,11 +25,11 @@ jobs:
|
|
|
23
25
|
steps:
|
|
24
26
|
- uses: actions/checkout@v7
|
|
25
27
|
with:
|
|
26
|
-
ref: ${{ inputs.baseline
|
|
28
|
+
ref: ${{ inputs.baseline }}
|
|
27
29
|
path: baseline
|
|
28
30
|
- uses: actions/checkout@v7
|
|
29
31
|
with:
|
|
30
|
-
ref: ${{ github.
|
|
32
|
+
ref: ${{ github.sha }}
|
|
31
33
|
path: candidate
|
|
32
34
|
- uses: astral-sh/setup-uv@v10.3.3
|
|
33
35
|
with:
|
|
@@ -35,34 +37,47 @@ jobs:
|
|
|
35
37
|
cache-suffix: matched-windows-${{ matrix.python }}
|
|
36
38
|
- run: uv sync --directory baseline --locked
|
|
37
39
|
- run: uv sync --directory candidate --locked
|
|
38
|
-
-
|
|
40
|
+
- uses: actions/checkout@v7
|
|
41
|
+
with:
|
|
42
|
+
repository: gigaverse-app/ci-esta
|
|
43
|
+
ref: ${{ inputs.ci-esta-ref }}
|
|
44
|
+
path: ci-esta-tools
|
|
45
|
+
- name: Preserve the launcher path for the control
|
|
39
46
|
shell: pwsh
|
|
40
47
|
run: '"BASELINE_GIT_PATH=$env:PATH" | Out-File -FilePath $env:GITHUB_ENV -Encoding utf8 -Append'
|
|
41
|
-
- uses:
|
|
48
|
+
- uses: ./ci-esta-tools/actions/native-windows-git
|
|
42
49
|
- name: Run complete suites in both orders on the same worker
|
|
43
50
|
shell: pwsh
|
|
51
|
+
env:
|
|
52
|
+
CANDIDATE_WORKERS: ${{ inputs.candidate-workers }}
|
|
53
|
+
TMP: ${{ runner.temp }}
|
|
54
|
+
TEMP: ${{ runner.temp }}
|
|
55
|
+
TMPDIR: ${{ runner.temp }}
|
|
44
56
|
run: |
|
|
45
57
|
$results = Join-Path $env:GITHUB_WORKSPACE 'measurements'
|
|
46
58
|
New-Item -ItemType Directory -Path $results | Out-Null
|
|
47
59
|
$measurements = @()
|
|
48
|
-
$originalPath = $env:BASELINE_GIT_PATH
|
|
49
60
|
$nativeGitPath = $env:PATH
|
|
50
61
|
foreach ($sample in @('baseline-1', 'candidate-1', 'candidate-2', 'baseline-2')) {
|
|
51
62
|
$arm = $sample.Split('-')[0]
|
|
52
63
|
Push-Location (Join-Path $env:GITHUB_WORKSPACE $arm)
|
|
53
64
|
try {
|
|
54
|
-
#
|
|
55
|
-
$env:PATH = if ($arm -eq 'candidate') { $nativeGitPath } else { $
|
|
65
|
+
# Keep the suite, coverage and disk identical; record the actual executable.
|
|
66
|
+
$env:PATH = if ($arm -eq 'candidate') { $nativeGitPath } else { $env:BASELINE_GIT_PATH }
|
|
67
|
+
$env:PYTHONPATH = Join-Path (Get-Location) 'src'
|
|
68
|
+
$git = (Get-Command git -CommandType Application | Select-Object -First 1).Source
|
|
69
|
+
Write-Host "$sample uses $git"
|
|
70
|
+
$workers = if ($arm -eq 'candidate') { [int]$env:CANDIDATE_WORKERS } else { 4 }
|
|
56
71
|
$sha = git rev-parse HEAD
|
|
57
72
|
# Keep repeated arms from timing cleanup of an earlier arm's Git fixtures.
|
|
58
73
|
$temp = Join-Path $env:RUNNER_TEMP "matched-pytest-$sample"
|
|
59
74
|
if (Test-Path -LiteralPath $temp) { throw "Temporary path already exists: $temp" }
|
|
60
75
|
$clock = [System.Diagnostics.Stopwatch]::StartNew()
|
|
61
|
-
uv run --locked pytest -q -n
|
|
76
|
+
uv run --locked pytest -q -p no:selective -n $workers --basetemp="$temp" --durations=20 --cov --cov-report="xml:$results/$sample-coverage.xml" --junitxml="$results/$sample-tests.xml" *> "$results/$sample.log"
|
|
62
77
|
$result = $LASTEXITCODE
|
|
63
78
|
$clock.Stop()
|
|
64
79
|
Get-Content "$results/$sample.log" | Select-Object -Last 25
|
|
65
|
-
$measurements += [pscustomobject]@{ sample=$sample; sha=$sha; seconds=$clock.Elapsed.TotalSeconds; exit_code=$result }
|
|
80
|
+
$measurements += [pscustomobject]@{ sample=$sample; sha=$sha; git=$git; workers=$workers; seconds=$clock.Elapsed.TotalSeconds; exit_code=$result }
|
|
66
81
|
$measurements | ConvertTo-Json | Set-Content "$results/timings.json"
|
|
67
82
|
if ($result -ne 0) { throw "${sample} failed: $result" }
|
|
68
83
|
} finally { Pop-Location }
|
|
@@ -11,6 +11,11 @@ on pytest-selective itself.
|
|
|
11
11
|
- Reproduce selection bugs using real temporary Git repositories and existing
|
|
12
12
|
`tests/testing/git_repo.py` helpers. Cover base and head trees, deleted inputs,
|
|
13
13
|
explicit declarations and the relevant fallback. Do not mock away selection.
|
|
14
|
+
- The planner's import, conftest, declaration and content-read closure, its parse-error
|
|
15
|
+
stop, its selection against older bases and its graph cache's transparency are also a
|
|
16
|
+
Quint contract in
|
|
17
|
+
`contracts/selection`. After changing selection, run it (CONTRIBUTING.md, *The selection
|
|
18
|
+
contract*); extend the model rather than weakening it when the promise changes.
|
|
14
19
|
- Planning supports committed revisions and explicit working-tree snapshots. Preserve full execution on
|
|
15
20
|
operational uncertainty and diagnostics for invalid declarations. An empty
|
|
16
21
|
selection is not an empty pytest argument list.
|
|
@@ -1,5 +1,33 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## [1.1.0](https://github.com/gigaverse-app/pytest-selective/compare/v1.0.0...v1.1.0) (2026-11-09)
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
### Features
|
|
7
|
+
|
|
8
|
+
* one-prompt agent setup, and fixes from a real adoption trial ([#105](https://github.com/gigaverse-app/pytest-selective/issues/105)) ([27c4578](https://github.com/gigaverse-app/pytest-selective/commit/27c45786f1a6a349409eb2a083510e4e9a9180d3))
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
### Bug fixes
|
|
12
|
+
|
|
13
|
+
* **lanes:** select only lanes holding graph tests when every graph test is affected ([#102](https://github.com/gigaverse-app/pytest-selective/issues/102)) ([d6f22b8](https://github.com/gigaverse-app/pytest-selective/commit/d6f22b8f93773a4b2e60b2b8ad9d4b26762b2d2f))
|
|
14
|
+
* **selection:** resolve imports from the directories pytest's default import mode adds to sys.path ([#96](https://github.com/gigaverse-app/pytest-selective/issues/96)) ([d825176](https://github.com/gigaverse-app/pytest-selective/commit/d825176eb6d259733ec8132ba98ac327be15eb72))
|
|
15
|
+
|
|
16
|
+
## [1.0.0](https://github.com/gigaverse-app/pytest-selective/compare/v0.4.3...v1.0.0) (2026-11-08)
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
### Bug fixes
|
|
20
|
+
|
|
21
|
+
* **ci:** streamline dogfood checks and prepare the 1.0 release ([#97](https://github.com/gigaverse-app/pytest-selective/issues/97)) ([5c79edc](https://github.com/gigaverse-app/pytest-selective/commit/5c79edcc2e234ccd90902ef001d2852cf7ecfdbc))
|
|
22
|
+
* **selection:** follow star imports to the submodules a package lists in __all__ ([#94](https://github.com/gigaverse-app/pytest-selective/issues/94)) ([ea02fbf](https://github.com/gigaverse-app/pytest-selective/commit/ea02fbfe85c2d5b6e8685a4b159215d421f78653))
|
|
23
|
+
|
|
24
|
+
## [0.4.3](https://github.com/gigaverse-app/pytest-selective/compare/v0.4.2...v0.4.3) (2026-11-08)
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
### Bug fixes
|
|
28
|
+
|
|
29
|
+
* **lanes:** select lanes through the graph and match ** like gitignore ([#90](https://github.com/gigaverse-app/pytest-selective/issues/90)) ([107b021](https://github.com/gigaverse-app/pytest-selective/commit/107b0212ed741ac72c36a39cc864906eca75d1ab))
|
|
30
|
+
|
|
3
31
|
## [0.4.2](https://github.com/gigaverse-app/pytest-selective/compare/v0.4.1...v0.4.2) (2026-11-07)
|
|
4
32
|
|
|
5
33
|
|
|
@@ -32,6 +32,31 @@ Changes to collection policy, runtime auditing, or the composite action need tes
|
|
|
32
32
|
at that boundary. Update the tutorial when the public command contract changes.
|
|
33
33
|
Keep performance claims tied to reproducible measurements and their limitations.
|
|
34
34
|
|
|
35
|
+
### The selection contract
|
|
36
|
+
|
|
37
|
+
[contracts/selection](contracts/selection/README.md) states the planner's core
|
|
38
|
+
promise as a [Quint](https://quint.sh) model and checks it with
|
|
39
|
+
[pytest-quint-connect](https://github.com/gigaverse-app/pytest-quint-connect):
|
|
40
|
+
generated histories of real commits, each selected by `affected()` and compared
|
|
41
|
+
with the model. It runs as its own CI lane, outside the xdist suite, and needs
|
|
42
|
+
Node.js 22+ for Quint:
|
|
43
|
+
|
|
44
|
+
```bash
|
|
45
|
+
uv run --group quint pytest -c contracts/selection/quint.ini contracts/selection --quint-require-complete
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
When selection semantics change on purpose, change `selection.qnt` with them and
|
|
49
|
+
regenerate the driver (`pytest-quint-connect codegen contracts/selection/quint-connect.json`).
|
|
50
|
+
Never drop a witness, mutant or observed field to make it pass.
|
|
51
|
+
|
|
52
|
+
CI reports each run in one pull-request comment and checks the README's contract block
|
|
53
|
+
against it. After a change to the contract's cases, refresh the block from a complete
|
|
54
|
+
local run and commit it:
|
|
55
|
+
|
|
56
|
+
```bash
|
|
57
|
+
uv run --group quint pytest-quint-connect report '**/.artifacts/quint-connect/results.json' --readme README.md
|
|
58
|
+
```
|
|
59
|
+
|
|
35
60
|
### Shared implementation owners
|
|
36
61
|
|
|
37
62
|
| Contract | Owner |
|
|
@@ -72,8 +97,9 @@ limitations. The pull request template provides a short checklist. See
|
|
|
72
97
|
## CI lane selection
|
|
73
98
|
|
|
74
99
|
This repository uses its own `plan-lanes` action. Documentation-only PRs keep
|
|
75
|
-
quality checks (including Markdown links) and
|
|
76
|
-
|
|
100
|
+
quality checks (including Markdown links) and the documentation build, while
|
|
101
|
+
skipping every test lane and the distribution build. A README or LICENSE change
|
|
102
|
+
also runs the `package` lane, because both distributions embed them. Within an
|
|
77
103
|
admitted lane, the repository's public file-selection action selects affected
|
|
78
104
|
test files once; the platform matrix and minimum-pytest job execute that same
|
|
79
105
|
plan. CI-esta receives both lane and file explanations, learns JUnit timings,
|
|
@@ -171,6 +197,12 @@ history or failed planning must execute the full affected lanes and fail the
|
|
|
171
197
|
required planning gate. Ordinary PRs and pushes use selection; manual/release/scheduled/full overrides
|
|
172
198
|
remain complete checkpoints.
|
|
173
199
|
|
|
200
|
+
CI and public-action smoke run full checkpoints every Monday. Ordinary runs keep
|
|
201
|
+
the shared affected-file plan across all six OS/Python combinations and minimum
|
|
202
|
+
pytest. `tests/test_tutorial.py` owns the end-to-end tutorial execution and follows
|
|
203
|
+
that plan; there is no second tutorial invocation in each matrix job. JUnit retains
|
|
204
|
+
its test result and timing, and full plans also upload whole-suite coverage.
|
|
205
|
+
|
|
174
206
|
## Duplication
|
|
175
207
|
|
|
176
208
|
`uv run pre-commit install` installs both the staged dry-lint check and the
|