pytest-selective 0.1.1__tar.gz → 0.3.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.
Files changed (169) hide show
  1. {pytest_selective-0.1.1 → pytest_selective-0.3.0}/.github/dependabot.yml +4 -0
  2. {pytest_selective-0.1.1 → pytest_selective-0.3.0}/.github/test-lanes.json +8 -5
  3. {pytest_selective-0.1.1 → pytest_selective-0.3.0}/.github/workflows/ci.yml +59 -16
  4. pytest_selective-0.3.0/.github/workflows/docs.yml +30 -0
  5. {pytest_selective-0.1.1 → pytest_selective-0.3.0}/.github/workflows/observe-ci.yml +2 -1
  6. pytest_selective-0.3.0/.github/workflows/pr-title.yml +45 -0
  7. pytest_selective-0.3.0/.github/workflows/public-actions.yml +76 -0
  8. pytest_selective-0.3.0/.github/workflows/release-please.yml +54 -0
  9. {pytest_selective-0.1.1 → pytest_selective-0.3.0}/.github/workflows/release.yml +2 -1
  10. {pytest_selective-0.1.1 → pytest_selective-0.3.0}/.gitignore +4 -0
  11. pytest_selective-0.3.0/.readthedocs.yaml +11 -0
  12. pytest_selective-0.3.0/.release-please-manifest.json +3 -0
  13. pytest_selective-0.3.0/AGENTS.md +69 -0
  14. {pytest_selective-0.1.1 → pytest_selective-0.3.0}/CHANGELOG.md +35 -0
  15. pytest_selective-0.3.0/CLAUDE.md +1 -0
  16. pytest_selective-0.3.0/CONTRIBUTING.md +154 -0
  17. {pytest_selective-0.1.1 → pytest_selective-0.3.0}/PKG-INFO +42 -24
  18. {pytest_selective-0.1.1 → pytest_selective-0.3.0}/README.md +40 -22
  19. pytest_selective-0.3.0/RELEASING.md +121 -0
  20. pytest_selective-0.3.0/action.yml +106 -0
  21. pytest_selective-0.3.0/actions/README.md +13 -0
  22. {pytest_selective-0.1.1/.github → pytest_selective-0.3.0}/actions/plan-lanes/action.yml +2 -2
  23. {pytest_selective-0.1.1/.github → pytest_selective-0.3.0}/actions/pytest-expansion/action.yml +1 -1
  24. {pytest_selective-0.1.1/.github → pytest_selective-0.3.0}/actions/test-impact/action.yml +2 -2
  25. pytest_selective-0.3.0/agentsync.toml +18 -0
  26. pytest_selective-0.3.0/docs/actions.md +45 -0
  27. {pytest_selective-0.1.1 → pytest_selective-0.3.0}/docs/agents.md +6 -12
  28. pytest_selective-0.3.0/docs/assets/banner.png +0 -0
  29. pytest_selective-0.3.0/docs/assets/banner.svg +28 -0
  30. pytest_selective-0.3.0/docs/assets/brand.json +13 -0
  31. pytest_selective-0.3.0/docs/assets/icon.png +0 -0
  32. pytest_selective-0.3.0/docs/assets/logo.svg +10 -0
  33. pytest_selective-0.3.0/docs/assets/raster-sources.json +9 -0
  34. pytest_selective-0.3.0/docs/assets/social-preview.png +0 -0
  35. pytest_selective-0.3.0/docs/assets/social-preview.svg +30 -0
  36. {pytest_selective-0.1.1 → pytest_selective-0.3.0}/docs/benchmarks/2026-10-22.md +1 -1
  37. {pytest_selective-0.1.1 → pytest_selective-0.3.0}/docs/ci-toolkit.md +18 -0
  38. {pytest_selective-0.1.1 → pytest_selective-0.3.0}/docs/ci.md +58 -9
  39. {pytest_selective-0.1.1 → pytest_selective-0.3.0}/docs/comparison-pytest-impacted.md +84 -7
  40. {pytest_selective-0.1.1 → pytest_selective-0.3.0}/docs/configuration.md +5 -3
  41. {pytest_selective-0.1.1 → pytest_selective-0.3.0}/docs/evidence.md +3 -1
  42. {pytest_selective-0.1.1 → pytest_selective-0.3.0}/docs/pytest-hygiene.md +2 -2
  43. pytest_selective-0.3.0/docs/selection-health.md +47 -0
  44. {pytest_selective-0.1.1 → pytest_selective-0.3.0}/docs/tutorial.md +37 -1
  45. pytest_selective-0.3.0/docs/tutorials/skip-unrelated-ci.md +140 -0
  46. pytest_selective-0.3.0/docs/work-in-progress/tickets/actions-directory-test-plan.md +10 -0
  47. pytest_selective-0.3.0/docs/work-in-progress/tickets/codex-selector-correctness-performance-test-plan.md +16 -0
  48. pytest_selective-0.3.0/docs/work-in-progress/tickets/move-action-implementations-test-plan.md +10 -0
  49. pytest_selective-0.3.0/docs/work-in-progress/tickets/public-action-onboarding-test-plan.md +7 -0
  50. pytest_selective-0.3.0/documentation/assets/routing-comparison.svg +1 -0
  51. pytest_selective-0.3.0/documentation/check_site.py +77 -0
  52. pytest_selective-0.3.0/documentation/ecosystem.md +31 -0
  53. pytest_selective-0.3.0/documentation/hooks.py +76 -0
  54. pytest_selective-0.3.0/documentation/index.md +72 -0
  55. pytest_selective-0.3.0/documentation/overrides/main.html +14 -0
  56. pytest_selective-0.3.0/documentation/publishing.md +107 -0
  57. pytest_selective-0.3.0/documentation/requirements.in +2 -0
  58. pytest_selective-0.3.0/documentation/requirements.txt +562 -0
  59. pytest_selective-0.3.0/mkdocs.yml +106 -0
  60. {pytest_selective-0.1.1 → pytest_selective-0.3.0}/pyproject.toml +2 -2
  61. pytest_selective-0.3.0/release-please-config.json +76 -0
  62. pytest_selective-0.3.0/scripts/benchmark_graph.py +67 -0
  63. pytest_selective-0.3.0/scripts/benchmark_rust.py +68 -0
  64. pytest_selective-0.3.0/scripts/ci_file_config.py +20 -0
  65. pytest_selective-0.3.0/scripts/public_actions.py +53 -0
  66. pytest_selective-0.3.0/scripts/render_brand.py +140 -0
  67. pytest_selective-0.3.0/scripts/run_ci_tests.py +40 -0
  68. {pytest_selective-0.1.1 → pytest_selective-0.3.0}/scripts/smoke_distribution.py +4 -1
  69. {pytest_selective-0.1.1 → pytest_selective-0.3.0}/scripts/tutorial.py +3 -0
  70. {pytest_selective-0.1.1 → pytest_selective-0.3.0}/skills/pytest-selective/SKILL.md +31 -4
  71. {pytest_selective-0.1.1 → pytest_selective-0.3.0}/skills/pytest-selective/references/adoption.md +20 -5
  72. {pytest_selective-0.1.1 → pytest_selective-0.3.0}/src/pytest_selective/ci.py +14 -3
  73. {pytest_selective-0.1.1 → pytest_selective-0.3.0}/src/pytest_selective/impact_graph.py +100 -60
  74. {pytest_selective-0.1.1 → pytest_selective-0.3.0}/src/pytest_selective/impact_models.py +8 -0
  75. {pytest_selective-0.1.1 → pytest_selective-0.3.0}/src/pytest_selective/impact_selection.py +6 -0
  76. pytest_selective-0.3.0/src/pytest_selective/parse_batch.py +72 -0
  77. pytest_selective-0.3.0/src/pytest_selective/pytest_plugins.py +108 -0
  78. {pytest_selective-0.1.1 → pytest_selective-0.3.0}/src/pytest_selective/pytest_sharding.py +10 -0
  79. pytest_selective-0.3.0/tests/test_ci_admission.py +64 -0
  80. pytest_selective-0.3.0/tests/test_ci_test_execution.py +71 -0
  81. pytest_selective-0.3.0/tests/test_public_actions.py +34 -0
  82. {pytest_selective-0.1.1 → pytest_selective-0.3.0}/tests/test_pytest_expansion.py +4 -1
  83. pytest_selective-0.3.0/tests/test_repository_ci.py +147 -0
  84. pytest_selective-0.3.0/tests/test_repository_hygiene.py.gv-dependency.yaml +4 -0
  85. {pytest_selective-0.1.1 → pytest_selective-0.3.0}/tests/test_tutorial.py +2 -0
  86. {pytest_selective-0.1.1 → pytest_selective-0.3.0}/tests/testing/test_ci.py +86 -1
  87. {pytest_selective-0.1.1 → pytest_selective-0.3.0}/tests/testing/test_impact_explanation.py +2 -2
  88. {pytest_selective-0.1.1 → pytest_selective-0.3.0}/tests/testing/test_impact_graph.py +36 -9
  89. pytest_selective-0.3.0/tests/testing/test_parse_batch.py +43 -0
  90. {pytest_selective-0.1.1 → pytest_selective-0.3.0}/tests/testing/test_pytest_plugin.py +3 -0
  91. pytest_selective-0.3.0/tests/testing/test_pytest_plugins.py +168 -0
  92. {pytest_selective-0.1.1 → pytest_selective-0.3.0}/tests/testing/test_working_tree.py +3 -0
  93. {pytest_selective-0.1.1 → pytest_selective-0.3.0}/uv.lock +1 -1
  94. pytest_selective-0.1.1/AGENTS.md +0 -23
  95. pytest_selective-0.1.1/CONTRIBUTING.md +0 -63
  96. pytest_selective-0.1.1/RELEASING.md +0 -63
  97. pytest_selective-0.1.1/docs/pytest-impacted.md +0 -69
  98. pytest_selective-0.1.1/tests/test_repository_ci.py +0 -46
  99. {pytest_selective-0.1.1 → pytest_selective-0.3.0}/.gitattributes +0 -0
  100. {pytest_selective-0.1.1 → pytest_selective-0.3.0}/.github/ISSUE_TEMPLATE/bug_report.yml +0 -0
  101. {pytest_selective-0.1.1 → pytest_selective-0.3.0}/.github/PULL_REQUEST_TEMPLATE.md +0 -0
  102. {pytest_selective-0.1.1 → pytest_selective-0.3.0}/.github/workflows/windows-performance.yml +0 -0
  103. {pytest_selective-0.1.1 → pytest_selective-0.3.0}/.pre-commit-config.yaml +0 -0
  104. {pytest_selective-0.1.1 → pytest_selective-0.3.0}/LICENSE +0 -0
  105. {pytest_selective-0.1.1 → pytest_selective-0.3.0}/PROVENANCE.md +0 -0
  106. {pytest_selective-0.1.1 → pytest_selective-0.3.0}/docs/README.md +0 -0
  107. {pytest_selective-0.1.1 → pytest_selective-0.3.0}/docs/benchmarks/2026-10-22-due-ci.json +0 -0
  108. {pytest_selective-0.1.1 → pytest_selective-0.3.0}/docs/benchmarks/2026-10-22-due-docs-ci.json +0 -0
  109. {pytest_selective-0.1.1 → pytest_selective-0.3.0}/docs/benchmarks/2026-10-22-due-docs-widget.json +0 -0
  110. {pytest_selective-0.1.1 → pytest_selective-0.3.0}/docs/benchmarks/2026-10-22-due-widget.json +0 -0
  111. {pytest_selective-0.1.1 → pytest_selective-0.3.0}/docs/benchmarks/2026-10-22-input-replays.json +0 -0
  112. {pytest_selective-0.1.1 → pytest_selective-0.3.0}/docs/benchmarks/2026-10-22-obligation-docs.json +0 -0
  113. {pytest_selective-0.1.1 → pytest_selective-0.3.0}/docs/benchmarks/2026-10-22-obligation.json +0 -0
  114. {pytest_selective-0.1.1 → pytest_selective-0.3.0}/docs/benchmarks/2026-10-22-routing.md +0 -0
  115. {pytest_selective-0.1.1 → pytest_selective-0.3.0}/docs/benchmarks/2026-10-22-selective-docs.json +0 -0
  116. {pytest_selective-0.1.1 → pytest_selective-0.3.0}/docs/benchmarks/2026-10-23-windows-saleor/obligation-baseline.json +0 -0
  117. {pytest_selective-0.1.1 → pytest_selective-0.3.0}/docs/benchmarks/2026-10-23-windows-saleor/selective-after-3.11.xml +0 -0
  118. {pytest_selective-0.1.1 → pytest_selective-0.3.0}/docs/benchmarks/2026-10-23-windows-saleor/selective-after-3.14.xml +0 -0
  119. {pytest_selective-0.1.1 → pytest_selective-0.3.0}/docs/benchmarks/2026-10-23-windows-saleor/selective-after.json +0 -0
  120. {pytest_selective-0.1.1 → pytest_selective-0.3.0}/docs/benchmarks/2026-10-23-windows-saleor/selective-control-3.11.xml +0 -0
  121. {pytest_selective-0.1.1 → pytest_selective-0.3.0}/docs/benchmarks/2026-10-23-windows-saleor/selective-control-3.14.xml +0 -0
  122. {pytest_selective-0.1.1 → pytest_selective-0.3.0}/docs/benchmarks/2026-10-23-windows-saleor/selective-control.json +0 -0
  123. {pytest_selective-0.1.1 → pytest_selective-0.3.0}/docs/benchmarks/2026-10-23-windows-saleor/selective-earlier.json +0 -0
  124. {pytest_selective-0.1.1 → pytest_selective-0.3.0}/docs/benchmarks/2026-10-23-windows-saleor.md +0 -0
  125. {pytest_selective-0.1.1 → pytest_selective-0.3.0}/docs/benchmarks/windows-runtime.md +0 -0
  126. {pytest_selective-0.1.1 → pytest_selective-0.3.0}/docs/local-development.md +0 -0
  127. {pytest_selective-0.1.1 → pytest_selective-0.3.0}/docs/work-in-progress/tickets/codex-windows-git-performance-test-plan.md +0 -0
  128. {pytest_selective-0.1.1 → pytest_selective-0.3.0}/docs/work-in-progress/tickets/impacted-improvements-test-plan.md +0 -0
  129. {pytest_selective-0.1.1 → pytest_selective-0.3.0}/scripts/benchmark_parsing.py +0 -0
  130. {pytest_selective-0.1.1 → pytest_selective-0.3.0}/scripts/check_docs.py +0 -0
  131. {pytest_selective-0.1.1 → pytest_selective-0.3.0}/scripts/check_metadata.py +0 -0
  132. {pytest_selective-0.1.1 → pytest_selective-0.3.0}/scripts/compare_impacted.py +0 -0
  133. {pytest_selective-0.1.1 → pytest_selective-0.3.0}/scripts/measure_ci_runs.py +0 -0
  134. {pytest_selective-0.1.1 → pytest_selective-0.3.0}/skills/pytest-selective/agents/openai.yaml +0 -0
  135. {pytest_selective-0.1.1 → pytest_selective-0.3.0}/src/pytest_selective/__init__.py +0 -0
  136. {pytest_selective-0.1.1 → pytest_selective-0.3.0}/src/pytest_selective/__main__.py +0 -0
  137. {pytest_selective-0.1.1 → pytest_selective-0.3.0}/src/pytest_selective/ast_imports.py +0 -0
  138. {pytest_selective-0.1.1 → pytest_selective-0.3.0}/src/pytest_selective/hygiene.py +0 -0
  139. {pytest_selective-0.1.1 → pytest_selective-0.3.0}/src/pytest_selective/impact_audit.py +0 -0
  140. {pytest_selective-0.1.1 → pytest_selective-0.3.0}/src/pytest_selective/impact_audit_plugin.py +0 -0
  141. {pytest_selective-0.1.1 → pytest_selective-0.3.0}/src/pytest_selective/impact_cli.py +0 -0
  142. {pytest_selective-0.1.1 → pytest_selective-0.3.0}/src/pytest_selective/impact_declarations.py +0 -0
  143. {pytest_selective-0.1.1 → pytest_selective-0.3.0}/src/pytest_selective/impact_explanation.py +0 -0
  144. {pytest_selective-0.1.1 → pytest_selective-0.3.0}/src/pytest_selective/impact_first_party.py +0 -0
  145. {pytest_selective-0.1.1 → pytest_selective-0.3.0}/src/pytest_selective/impact_name_scope.py +0 -0
  146. {pytest_selective-0.1.1 → pytest_selective-0.3.0}/src/pytest_selective/input_policy.py +0 -0
  147. {pytest_selective-0.1.1 → pytest_selective-0.3.0}/src/pytest_selective/local.py +0 -0
  148. {pytest_selective-0.1.1 → pytest_selective-0.3.0}/src/pytest_selective/plugin.py +0 -0
  149. {pytest_selective-0.1.1 → pytest_selective-0.3.0}/src/pytest_selective/py.typed +0 -0
  150. {pytest_selective-0.1.1 → pytest_selective-0.3.0}/src/pytest_selective/pytest_expansion.py +0 -0
  151. {pytest_selective-0.1.1 → pytest_selective-0.3.0}/src/pytest_selective/test_inventory.py +0 -0
  152. {pytest_selective-0.1.1 → pytest_selective-0.3.0}/src/pytest_selective/typecheck_scope.py +0 -0
  153. {pytest_selective-0.1.1 → pytest_selective-0.3.0}/src/pytest_selective/working_tree.py +0 -0
  154. {pytest_selective-0.1.1 → pytest_selective-0.3.0}/tests/__init__.py +0 -0
  155. {pytest_selective-0.1.1 → pytest_selective-0.3.0}/tests/test_ci_measurements.py +0 -0
  156. {pytest_selective-0.1.1 → pytest_selective-0.3.0}/tests/test_distribution_metadata.py +0 -0
  157. {pytest_selective-0.1.1 → pytest_selective-0.3.0}/tests/test_hygiene.py +0 -0
  158. {pytest_selective-0.1.1 → pytest_selective-0.3.0}/tests/test_repository_hygiene.py +0 -0
  159. {pytest_selective-0.1.1 → pytest_selective-0.3.0}/tests/testing/__init__.py +0 -0
  160. {pytest_selective-0.1.1 → pytest_selective-0.3.0}/tests/testing/git_repo.py +0 -0
  161. {pytest_selective-0.1.1 → pytest_selective-0.3.0}/tests/testing/test_ast_imports.py +0 -0
  162. {pytest_selective-0.1.1 → pytest_selective-0.3.0}/tests/testing/test_git_repo.py +0 -0
  163. {pytest_selective-0.1.1 → pytest_selective-0.3.0}/tests/testing/test_global_inputs.py +0 -0
  164. {pytest_selective-0.1.1 → pytest_selective-0.3.0}/tests/testing/test_impact_audit.py +0 -0
  165. {pytest_selective-0.1.1 → pytest_selective-0.3.0}/tests/testing/test_impact_first_party.py +0 -0
  166. {pytest_selective-0.1.1 → pytest_selective-0.3.0}/tests/testing/test_impact_name_scope.py +0 -0
  167. {pytest_selective-0.1.1 → pytest_selective-0.3.0}/tests/testing/test_portability.py +0 -0
  168. {pytest_selective-0.1.1 → pytest_selective-0.3.0}/tests/testing/test_pytest_sharding.py +0 -0
  169. {pytest_selective-0.1.1 → pytest_selective-0.3.0}/tests/testing/test_typecheck_scope.py +0 -0
@@ -2,6 +2,8 @@ version: 2
2
2
  updates:
3
3
  - package-ecosystem: github-actions
4
4
  directory: /
5
+ commit-message:
6
+ prefix: ci
5
7
  schedule:
6
8
  interval: weekly
7
9
  groups:
@@ -9,6 +11,8 @@ updates:
9
11
  patterns: ["*"]
10
12
  - package-ecosystem: uv
11
13
  directory: /
14
+ commit-message:
15
+ prefix: build
12
16
  schedule:
13
17
  interval: weekly
14
18
  groups:
@@ -18,14 +18,14 @@
18
18
  ],
19
19
  "inputs": [
20
20
  "src/**",
21
- "scripts/**",
22
- "tests/**",
23
- ".pre-commit-config.yaml",
24
- ".python-version"
21
+ "tests/**"
25
22
  ]
26
23
  }
27
24
  },
28
25
  "global_inputs": [
26
+ "scripts/**",
27
+ ".pre-commit-config.yaml",
28
+ ".python-version",
29
29
  "**/pyproject.toml",
30
30
  "pyproject.toml",
31
31
  "**/uv.lock",
@@ -33,6 +33,9 @@
33
33
  ".github/test-lanes.json",
34
34
  ".github/workflows/ci.yml",
35
35
  ".github/workflows/release.yml",
36
- ".github/actions/**"
36
+ "actions/**",
37
+ "action.yml",
38
+ "examples/**",
39
+ ".github/workflows/public-actions.yml"
37
40
  ]
38
41
  }
@@ -3,7 +3,7 @@ on:
3
3
  push:
4
4
  branches: [main]
5
5
  pull_request:
6
- types: [opened, synchronize, reopened, labeled, unlabeled]
6
+ types: [opened, synchronize, reopened]
7
7
  workflow_dispatch:
8
8
  workflow_call:
9
9
  permissions:
@@ -17,31 +17,56 @@ jobs:
17
17
  runs-on: ubuntu-latest
18
18
  outputs:
19
19
  lanes: ${{ steps.plan.outputs.lanes }}
20
+ mode: ${{ steps.files.outputs.mode }}
21
+ files: ${{ steps.paths.outputs.files }}
20
22
  steps:
21
23
  - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
22
24
  with:
23
25
  fetch-depth: 0
24
26
  - id: plan
25
- uses: ./.github/actions/plan-lanes
27
+ uses: ./actions/plan-lanes
26
28
  with:
27
29
  config: .github/test-lanes.json
28
30
  base: ${{ github.event.pull_request.base.sha || github.event.before }}
29
- full: ${{ github.event_name != 'pull_request' || contains(github.event.pull_request.labels.*.name, 'test:full') }}
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
+ - name: Share the repository graph and execution inputs with file selection
33
+ env:
34
+ PYTHONPATH: src
35
+ run: |
36
+ uv run --no-project --python 3.14 --with pydantic==2.13.4 --with pyyaml==6.0.3 \
37
+ python scripts/ci_file_config.py "$RUNNER_TEMP/test-impact.json"
38
+ - id: files
39
+ uses: ./
40
+ with:
41
+ config: ${{ runner.temp }}/test-impact.json
42
+ base: ${{ github.event.pull_request.base.sha || github.event.before }}
43
+ 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
30
57
  quality:
31
58
  runs-on: ubuntu-latest
32
59
  steps:
33
60
  - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
34
- - uses: gigaverse-app/ci-esta/.github/actions/setup-cached-uv@v0.1
61
+ - uses: gigaverse-app/ci-esta/actions/setup-cached-uv@v1
35
62
  with:
36
63
  python-version: '3.14'
37
64
  - run: uv run --locked pre-commit run --all-files
38
65
  - name: Check aggregate gate against the execution lanes
39
66
  run: >-
40
67
  uv run --no-project --python 3.14
41
- --with "ci-esta>=0.1.0,<0.2.0"
68
+ --with "ci-esta>=1.1.0,<2.0.0"
42
69
  ci-esta check-workflow --workflow .github/workflows/ci.yml --lanes .github/test-lanes.json
43
- - name: Validate routing even when the platform matrix is skipped
44
- run: uv run --locked pytest -q tests/test_repository_ci.py
45
70
  - name: Validate GitHub Actions
46
71
  run: docker run --rm -v "$PWD:/repo" -w /repo rhysd/actionlint:1.7.7 -color
47
72
  tests:
@@ -57,20 +82,26 @@ jobs:
57
82
  runs-on: ${{ matrix.os }}
58
83
  steps:
59
84
  - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
60
- - uses: gigaverse-app/ci-esta/.github/actions/setup-cached-uv@v0.1
85
+ - uses: gigaverse-app/ci-esta/actions/setup-cached-uv@v1
61
86
  with:
62
87
  python-version: ${{ matrix.python }}
63
88
  - name: Use native Git for Windows
64
- uses: gigaverse-app/ci-esta/.github/actions/native-windows-git@v0.1
89
+ uses: gigaverse-app/ci-esta/actions/native-windows-git@v1
65
90
  - name: Record coverage runtime
66
91
  run: uv run --locked python -m coverage --version
67
- - run: uv run --locked pytest -q -n 4 --durations=20 --junitxml=test-timings.xml --cov --cov-report=term-missing --cov-report=xml
92
+ - name: Execute the shared affected-file plan
93
+ run: uv run --locked python scripts/run_ci_tests.py -q -n 4 --durations=20 --junitxml=test-timings.xml
68
94
  env:
95
+ CI_PLANNER_RESULT: ${{ needs.select-tests.result }}
96
+ CI_TEST_MODE: ${{ needs.select-tests.outputs.mode }}
97
+ CI_TEST_FILES: ${{ needs.select-tests.outputs.files }}
98
+ CI_COVERAGE: 'true'
69
99
  # Keep temporary Git repositories beside the checkout on the work disk.
70
100
  TMP: ${{ runner.temp }}
71
101
  TEMP: ${{ runner.temp }}
72
102
  TMPDIR: ${{ runner.temp }}
73
- - uses: gigaverse-app/ci-esta/.github/actions/learn-test-timings@v0.1
103
+ - uses: gigaverse-app/ci-esta/actions/learn-test-timings@v1
104
+ if: ${{ hashFiles('test-timings.xml') != '' }}
74
105
  with:
75
106
  junit-pattern: test-timings.xml
76
107
  artifact-name: test-timings-${{ matrix.python }}-${{ matrix.os }}
@@ -89,22 +120,34 @@ jobs:
89
120
  runs-on: ubuntu-latest
90
121
  steps:
91
122
  - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
92
- - uses: gigaverse-app/ci-esta/.github/actions/setup-cached-uv@v0.1
123
+ - uses: gigaverse-app/ci-esta/actions/setup-cached-uv@v1
93
124
  with:
94
125
  python-version: '3.11'
95
126
  - run: uv pip install 'pytest==8.0.0'
96
- - run: uv run --no-sync pytest -q -n 4 --durations=20
127
+ - name: Execute the same file plan on minimum pytest
128
+ run: uv run --no-sync python scripts/run_ci_tests.py -q -n 4 --durations=20
129
+ env:
130
+ CI_PLANNER_RESULT: ${{ needs.select-tests.result }}
131
+ CI_TEST_MODE: ${{ needs.select-tests.outputs.mode }}
132
+ CI_TEST_FILES: ${{ needs.select-tests.outputs.files }}
97
133
  distributions:
134
+ needs: select-tests
135
+ # README changes still need build/metadata checks; planner failure must not skip them.
136
+ if: ${{ !cancelled() }}
98
137
  runs-on: ubuntu-latest
99
138
  steps:
100
139
  - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
101
- - uses: gigaverse-app/ci-esta/.github/actions/setup-cached-uv@v0.1
140
+ - uses: gigaverse-app/ci-esta/actions/setup-cached-uv@v1
102
141
  with:
103
142
  python-version: '3.14'
104
143
  - run: uv build
105
144
  - run: uv run --locked twine check --strict dist/*
106
145
  - run: uv run --locked python scripts/check_metadata.py
107
- - run: uv run --locked python scripts/smoke_distribution.py
146
+ - name: Smoke installed distributions when runtime inputs change
147
+ if: >-
148
+ ${{ needs.select-tests.result != 'success' ||
149
+ fromJSON(needs.select-tests.outputs.lanes).tests }}
150
+ run: uv run --locked python scripts/smoke_distribution.py
108
151
  - uses: actions/upload-artifact@cf430e030ddbb5b0abf93d22962f4752f3646cd9 # v7.0.2
109
152
  with:
110
153
  name: distributions
@@ -117,7 +160,7 @@ jobs:
117
160
  if: ${{ !cancelled() }}
118
161
  runs-on: ubuntu-latest
119
162
  steps:
120
- - uses: gigaverse-app/ci-esta/.github/actions/validation-gate@v0.1
163
+ - uses: gigaverse-app/ci-esta/actions/validation-gate@v1
121
164
  with:
122
165
  needs: ${{ toJSON(needs) }}
123
166
  lanes: ${{ needs.select-tests.outputs.lanes || '{}' }}
@@ -0,0 +1,30 @@
1
+ name: Documentation
2
+ on:
3
+ pull_request:
4
+ push:
5
+ branches: [main]
6
+ workflow_dispatch:
7
+ permissions:
8
+ contents: read
9
+ concurrency:
10
+ group: docs-${{ github.event.pull_request.number || github.ref }}
11
+ cancel-in-progress: true
12
+ jobs:
13
+ docs:
14
+ runs-on: ubuntu-latest
15
+ steps:
16
+ - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
17
+ - uses: actions/setup-python@v6
18
+ with:
19
+ python-version: '3.13'
20
+ cache: pip
21
+ cache-dependency-path: documentation/requirements.txt
22
+ - run: python -m pip install -r documentation/requirements.txt
23
+ - run: python scripts/render_brand.py --check
24
+ - run: python -m mkdocs build --strict
25
+ - run: python documentation/check_site.py
26
+ - uses: actions/upload-artifact@cf430e030ddbb5b0abf93d22962f4752f3646cd9 # v7.0.2
27
+ with:
28
+ name: documentation-site
29
+ path: site/
30
+ if-no-files-found: error
@@ -13,7 +13,7 @@ permissions:
13
13
 
14
14
  jobs:
15
15
  report:
16
- uses: gigaverse-app/ci-esta/.github/workflows/report.yml@v0.1
16
+ uses: gigaverse-app/ci-esta/.github/workflows/report.yml@v1
17
17
  with:
18
18
  run-id: ${{ github.event.workflow_run.id }}
19
19
  run-attempt: ${{ github.event.workflow_run.run_attempt }}
@@ -22,3 +22,4 @@ jobs:
22
22
  post-sticky: true
23
23
  required-gates-json: '[{"kind":"actions_job","job_name":"Validation gate"}]'
24
24
  analyze-critical-path: true
25
+ test-selection-artifact: test-impact-explanation
@@ -0,0 +1,45 @@
1
+ name: PR title
2
+
3
+ # Squash merges use this checked title as the Conventional Commit header.
4
+ on:
5
+ pull_request_target:
6
+ types: [opened, edited, synchronize, reopened, ready_for_review]
7
+
8
+ permissions:
9
+ pull-requests: read
10
+
11
+ jobs:
12
+ conventional-commit:
13
+ runs-on: ubuntu-latest
14
+ timeout-minutes: 5
15
+ steps:
16
+ # Only reads PR metadata. Never check out or execute PR code in this workflow.
17
+ - uses: amannn/action-semantic-pull-request@48f256284bd46cdaab1048c3721360e808335d50 # v6.1.1
18
+ with:
19
+ types: |
20
+ feat
21
+ fix
22
+ perf
23
+ revert
24
+ docs
25
+ style
26
+ refactor
27
+ test
28
+ build
29
+ ci
30
+ chore
31
+ requireScope: false
32
+ env:
33
+ GITHUB_TOKEN: ${{ github.token }}
34
+ - name: Explain how to repair the title
35
+ if: failure()
36
+ run: |
37
+ cat >> "$GITHUB_STEP_SUMMARY" <<'EOF'
38
+ Rename the PR to `type(optional-scope): description`, for example
39
+ `fix(parser): handle empty input` or `docs: clarify installation`.
40
+ Use `!` before the colon for a breaking change, for example
41
+ `feat(api)!: remove the deprecated argument`.
42
+ Choose the type that describes the change; do not hide features or fixes under `chore:`.
43
+ Editing the PR title reruns this check. No commit rewrite or replacement PR is needed.
44
+ If the title is already valid, inspect the action log for an API or runner failure.
45
+ EOF
@@ -0,0 +1,76 @@
1
+ name: Public action smoke
2
+ on:
3
+ pull_request:
4
+ push:
5
+ branches: [main]
6
+ workflow_dispatch:
7
+ permissions:
8
+ contents: read
9
+ jobs:
10
+ select-tests:
11
+ runs-on: ubuntu-latest
12
+ outputs:
13
+ lanes: ${{ steps.select.outputs.lanes }}
14
+ steps:
15
+ - uses: actions/checkout@v7
16
+ with:
17
+ fetch-depth: 0
18
+ - id: select
19
+ uses: ./actions/plan-lanes
20
+ with:
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
+ artifact-name: public-action-selection
25
+ public-actions:
26
+ needs: select-tests
27
+ if: >-
28
+ ${{ !cancelled() && (needs.select-tests.result != 'success' ||
29
+ fromJSON(needs.select-tests.outputs.lanes).tests) }}
30
+ runs-on: ubuntu-latest
31
+ steps:
32
+ - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7
33
+ with:
34
+ fetch-depth: 0
35
+ - name: Use the reviewed repository graph
36
+ run: |
37
+ python - <<'PY'
38
+ import json
39
+ from pathlib import Path
40
+ graph = json.loads(Path('.github/test-lanes.json').read_text())['graph']
41
+ Path('smoke-impact.json').write_text(json.dumps(graph))
42
+ PY
43
+ - uses: ./
44
+ id: impact
45
+ with:
46
+ config: smoke-impact.json
47
+ base: ${{ github.sha }}
48
+ full: 'true'
49
+ full-roots: tests
50
+ max-shards: '2'
51
+ explanation-artifact: ''
52
+ - name: Verify forwarded file-plan outputs
53
+ env:
54
+ MODE: ${{ steps.impact.outputs.mode }}
55
+ ROOTS: ${{ steps.impact.outputs.test_roots }}
56
+ run: |
57
+ test "$MODE" = full
58
+ test -n "$ROOTS"
59
+ - uses: ./actions/plan-lanes
60
+ id: lanes
61
+ with:
62
+ config: .github/test-lanes.json
63
+ base: ${{ github.sha }}
64
+ full: 'true'
65
+ artifact-name: ''
66
+ - name: Verify forwarded lane outputs
67
+ env:
68
+ LANES: ${{ steps.lanes.outputs.lanes }}
69
+ run: |
70
+ python - <<'PY'
71
+ import json, os
72
+ assert json.loads(os.environ['LANES']) == {'tests': True}
73
+ PY
74
+ - name: Require successful planning after fallback execution
75
+ if: ${{ !cancelled() && needs.select-tests.result != 'success' }}
76
+ run: exit 1
@@ -0,0 +1,54 @@
1
+ name: Release Please
2
+
3
+ # Release PRs accumulate changes; merging one is the publishing decision.
4
+ on:
5
+ workflow_run:
6
+ workflows: [CI]
7
+ branches: [main]
8
+ types: [completed]
9
+
10
+ permissions: {}
11
+
12
+ concurrency:
13
+ group: release-please
14
+ cancel-in-progress: false
15
+
16
+ jobs:
17
+ release-please:
18
+ if: >-
19
+ github.event.workflow_run.event == 'push'
20
+ && github.event.workflow_run.conclusion == 'success'
21
+ && github.event.workflow_run.head_repository.full_name == github.repository
22
+ runs-on: ubuntu-latest
23
+ environment: release-automation
24
+ timeout-minutes: 10
25
+ permissions:
26
+ contents: write
27
+ pull-requests: write
28
+ steps:
29
+ - name: Only process the tested main head
30
+ id: current
31
+ uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
32
+ with:
33
+ script: |
34
+ const {data: branch} = await github.rest.repos.getBranch({...context.repo, branch: 'main'});
35
+ core.setOutput('matches', branch.commit.sha === context.payload.workflow_run.head_sha);
36
+ - name: Require release bot authentication
37
+ if: steps.current.outputs.matches == 'true'
38
+ env:
39
+ HAS_RELEASE_TOKEN: ${{ secrets.GV_CI_CROSS_REPO_TOKEN != '' }}
40
+ run: |
41
+ if [[ "$HAS_RELEASE_TOKEN" != "true" ]]; then
42
+ echo "::error::Share the organization GV_CI_CROSS_REPO_TOKEN secret with this repository."
43
+ exit 1
44
+ fi
45
+ - name: Prepare release PR or publish its GitHub release
46
+ if: steps.current.outputs.matches == 'true'
47
+ uses: googleapis/release-please-action@45996ed1f6d02564a971a2fa1b5860e934307cf7 # v5.0.0
48
+ with:
49
+ # This credential triggers CI on release PRs and Publish on releases.
50
+ # GITHUB_TOKEN would suppress those follow-up workflow events.
51
+ token: ${{ secrets.GV_CI_CROSS_REPO_TOKEN }}
52
+ target-branch: main
53
+ config-file: release-please-config.json
54
+ manifest-file: .release-please-manifest.json
@@ -54,7 +54,8 @@ jobs:
54
54
  if [[ ! "$RELEASE_TAG" =~ ^v([0-9]+)\.([0-9]+)\.([0-9]+)$ ]]; then
55
55
  exit 0
56
56
  fi
57
- alias="v${BASH_REMATCH[1]}.${BASH_REMATCH[2]}"
57
+ # The Actions API has its own major, independent of the Python package.
58
+ alias="v1"
58
59
  if gh api "repos/$GH_REPO/git/ref/tags/$alias" --silent; then
59
60
  gh api --method PATCH "repos/$GH_REPO/git/refs/tags/$alias" \
60
61
  -f sha="$RELEASE_SHA" -F force=true --silent
@@ -11,3 +11,7 @@ dist/
11
11
  coverage.xml
12
12
  htmlcov/
13
13
  tutorial-report.json
14
+
15
+ # Built documentation
16
+ /site/
17
+ /.venv-docs/
@@ -0,0 +1,11 @@
1
+ version: 2
2
+ build:
3
+ os: ubuntu-24.04
4
+ tools:
5
+ python: "3.13"
6
+ python:
7
+ install:
8
+ - requirements: documentation/requirements.txt
9
+ mkdocs:
10
+ configuration: mkdocs.yml
11
+ fail_on_warning: true
@@ -0,0 +1,3 @@
1
+ {
2
+ ".": "0.3.0"
3
+ }
@@ -0,0 +1,69 @@
1
+ # Working on pytest-selective
2
+
3
+ For adopting the tool in another repository, use
4
+ [the portable skill](skills/pytest-selective/SKILL.md). This file describes work
5
+ on pytest-selective itself.
6
+
7
+ - Read [CONTRIBUTING.md](CONTRIBUTING.md) for setup and validation. Use the locked
8
+ environment: `uv sync --locked`, `uv run pre-commit run --all-files`,
9
+ `uv run pytest -q -n 4`. For packaging changes also build, run strict Twine,
10
+ `scripts/check_metadata.py` and `scripts/smoke_distribution.py`.
11
+ - Reproduce selection bugs using real temporary Git repositories and existing
12
+ `tests/testing/git_repo.py` helpers. Cover base and head trees, deleted inputs,
13
+ explicit declarations and the relevant fallback. Do not mock away selection.
14
+ - Planning supports committed revisions and explicit working-tree snapshots. Preserve full execution on
15
+ operational uncertainty and diagnostics for invalid declarations. An empty
16
+ selection is not an empty pytest argument list.
17
+ - Select consumer routing regressions by their declared execution/configuration inputs. Update
18
+ `.github/test-lanes.json` when adding execution inputs; preserve explicit full/manual
19
+ checkpoints and the `test:full` override.
20
+ - The README's performance numbers are scenario-specific observations. Keep
21
+ claims linked to [raw evidence](docs/benchmarks/2026-10-22-routing.md); distinguish
22
+ runner-seconds, elapsed time and billing. Skill/example edits need usable links
23
+ and verified commands, not tests that merely assert prose wording.
24
+
25
+ ## Commits and pull requests
26
+
27
+ - Use Conventional Commits for commits you create and PR titles:
28
+ `type(optional-scope): description`, for example
29
+ `fix(parser): handle empty input`.
30
+ - Allowed types: `feat`, `fix`, `perf`, `revert`, `docs`, `style`,
31
+ `refactor`, `test`, `build`, `ci`, and `chore`. Choose the type for the
32
+ actual change. Mark breaking changes with `!` before the colon and explain
33
+ the migration in the PR body.
34
+ - Repair an invalid PR title in place. Preserve useful work; do not close or
35
+ replace the PR, or rewrite existing commits just to satisfy the title check.
36
+ - Squash merges use the checked PR title. Keep any `BREAKING CHANGE:` details
37
+ in the body. Follow [CONTRIBUTING.md](CONTRIBUTING.md) and
38
+ [RELEASING.md](RELEASING.md).
39
+
40
+ ## Releases
41
+
42
+ Release Please derives versions and changelogs from Conventional Commits. Let
43
+ it prepare the release PR; do not hand-edit versions for ordinary changes.
44
+ Merging a release PR authorizes PyPI publication after CI. Follow RELEASING.md.
45
+
46
+ ## Shared agent instructions
47
+
48
+ Edit this root `AGENTS.md` as the single source for Codex and Claude.
49
+ `CLAUDE.md` is an AgentSync-managed relative symlink to this file, not a
50
+ separate instruction document. The configuration is
51
+ [agentsync.toml](agentsync.toml). To restore and verify the link:
52
+
53
+ ```bash
54
+ npx --yes @dallay/agentsync@1.52.3 apply
55
+ npx --yes @dallay/agentsync@1.52.3 status
56
+ ```
57
+
58
+ Keep the symlink tracked in Git. See CONTRIBUTING.md for platform setup.
59
+
60
+ ## Documentation-only CI
61
+
62
+ Documentation-only PRs execute zero tests unless a test or lane explicitly
63
+ declares that documentation as an input. This includes CI contract tests,
64
+ installed-wheel smoke and public-action smoke. Metadata/link/build checks may
65
+ still validate documentation without running runtime tests. Declare execution
66
+ workflows, selector configuration, helpers, locks and non-import inputs; missing
67
+ history or failed planning must execute the full affected lanes and fail the
68
+ required planning gate. Ordinary PRs and pushes use selection; manual/release/scheduled/full overrides
69
+ remain complete checkpoints.
@@ -1,5 +1,40 @@
1
1
  # Changelog
2
2
 
3
+ ## [0.3.0](https://github.com/gigaverse-app/pytest-selective/compare/v0.2.0...v0.3.0) (2026-10-23)
4
+
5
+
6
+ ### ⚠ BREAKING CHANGES
7
+
8
+ * **actions:** named action callers upgrading from older layouts must replace `.github/actions/NAME` or `NAME` with `actions/NAME` and select a revision containing that path.
9
+
10
+ ### Features
11
+
12
+ * **actions:** group public entry points under actions directory ([#44](https://github.com/gigaverse-app/pytest-selective/issues/44)) ([236aefc](https://github.com/gigaverse-app/pytest-selective/commit/236aefc83d9f0ee1696a5ffa29fc83cbcc4a53dc))
13
+
14
+
15
+ ### Bug fixes
16
+
17
+ * **ci:** dogfood dependency-based test selection ([#57](https://github.com/gigaverse-app/pytest-selective/issues/57)) ([76afb69](https://github.com/gigaverse-app/pytest-selective/commit/76afb69cea84681bd661ec2bfff7be857b434129))
18
+ * **ci:** select all runtime tests by declared dependencies ([9673e50](https://github.com/gigaverse-app/pytest-selective/commit/9673e50dd8a709db9347d2e325da242aca96cece))
19
+ * **docs:** refresh cached artwork when branding changes ([5726530](https://github.com/gigaverse-app/pytest-selective/commit/5726530a445099e7cf24a1a7a89016fed0270308))
20
+
21
+
22
+ ### Refactoring
23
+
24
+ * **actions:** move implementations into the public actions directory ([#46](https://github.com/gigaverse-app/pytest-selective/issues/46)) ([bd5aae3](https://github.com/gigaverse-app/pytest-selective/commit/bd5aae32176ae83b121d2353f40b562642184cd8))
25
+
26
+ ## [0.2.0](https://github.com/gigaverse-app/pytest-selective/compare/v0.1.1...v0.2.0) (2026-10-23)
27
+
28
+
29
+ ### Features
30
+
31
+ * add public action entry points and task-focused onboarding ([#41](https://github.com/gigaverse-app/pytest-selective/issues/41)) ([94da1e6](https://github.com/gigaverse-app/pytest-selective/commit/94da1e6d4088d9ded93ce491dc88993ac140e0ea))
32
+
33
+
34
+ ### Bug fixes
35
+
36
+ * **selection:** preserve plugin sessions and accelerate graph construction ([#42](https://github.com/gigaverse-app/pytest-selective/issues/42)) ([2994c88](https://github.com/gigaverse-app/pytest-selective/commit/2994c887d916a965b78f5ff8d48476abed705e37))
37
+
3
38
  ## 0.1.1 (2026-10-23)
4
39
 
5
40
  - Register a native pytest plugin: `pytest --selective`, ini defaults, optional existing impact JSON, an exact comparison base, and `--no-selective` for full execution.
@@ -0,0 +1 @@
1
+ AGENTS.md