claude-dev-env 1.36.2 → 1.37.1
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.
- package/_shared/pr-loop/scripts/config/preflight_constants.py +29 -8
- package/_shared/pr-loop/scripts/preflight.py +242 -20
- package/_shared/pr-loop/scripts/tests/test_preflight.py +362 -25
- package/_shared/pr-loop/scripts/tests/test_preflight_constants.py +9 -14
- package/hooks/blocking/code_rules_enforcer.py +269 -23
- package/hooks/blocking/test_code_rules_enforcer_unused_imports.py +157 -1
- package/hooks/config/test_unused_module_import_constants.py +48 -0
- package/hooks/config/unused_module_import_constants.py +41 -0
- package/package.json +1 -1
- package/rules/gh-paginate.md +4 -50
- package/rules/no-historical-clutter.md +36 -0
- package/skills/bg-agent/SKILL.md +69 -0
- package/skills/bugteam/CONSTRAINTS.md +10 -19
- package/skills/bugteam/PROMPTS.md +21 -14
- package/skills/bugteam/SKILL.md +122 -208
- package/skills/bugteam/SKILL_EVALS.md +75 -114
- package/skills/bugteam/reference/README.md +2 -4
- package/skills/bugteam/reference/audit-and-teammates.md +21 -48
- package/skills/bugteam/reference/audit-contract.md +7 -7
- package/skills/bugteam/reference/design-rationale.md +3 -8
- package/skills/bugteam/reference/team-setup.md +11 -19
- package/skills/bugteam/reference/teardown-publish-permissions.md +2 -14
- package/skills/bugteam/scripts/config/__init__.py +0 -0
- package/skills/bugteam/scripts/config/reflow_skill_md_constants.py +12 -0
- package/skills/bugteam/scripts/reflow_skill_md.py +51 -47
- package/skills/bugteam/sources.md +1 -25
- package/skills/bugteam/test_skill_additions.py +4 -13
- package/skills/fresh-branch/SKILL.md +71 -0
- package/skills/gotcha/SKILL.md +73 -0
- package/skills/monitor-open-prs/SKILL.md +4 -37
- package/skills/monitor-open-prs/test_skill_contract.py +0 -5
- package/skills/pr-converge/SKILL.md +60 -1298
- package/skills/pr-converge/reference/convergence-gates.md +122 -0
- package/skills/pr-converge/reference/examples.md +76 -0
- package/skills/pr-converge/reference/fix-protocol.md +56 -0
- package/skills/pr-converge/reference/ground-rules.md +13 -0
- package/skills/pr-converge/reference/multi-pr-orchestration.md +204 -0
- package/skills/pr-converge/reference/per-tick.md +204 -0
- package/skills/pr-converge/reference/state-schema.md +19 -0
- package/skills/pr-converge/reference/stop-conditions.md +26 -0
- package/skills/pr-converge/scripts/README.md +36 -9
- package/skills/pr-converge/scripts/check_pr_mergeability.py +1 -2
- package/skills/pr-converge/scripts/config/pr_converge_constants.py +74 -5
- package/skills/pr-converge/scripts/config/reflow_skill_md_constants.py +13 -0
- package/skills/pr-converge/scripts/config/test_pr_converge_constants.py +0 -24
- package/skills/pr-converge/scripts/cursor-agents-continue.ahk +22 -2
- package/skills/pr-converge/scripts/fetch_bugbot_inline_comments.py +19 -59
- package/skills/pr-converge/scripts/fetch_bugbot_reviews.py +15 -61
- package/skills/pr-converge/scripts/fetch_claude_inline_comments.py +70 -0
- package/skills/pr-converge/scripts/fetch_claude_reviews.py +61 -0
- package/skills/pr-converge/scripts/fetch_copilot_inline_comments.py +19 -61
- package/skills/pr-converge/scripts/fetch_copilot_reviews.py +14 -74
- package/skills/pr-converge/scripts/reflow_skill_md.py +71 -50
- package/skills/pr-converge/scripts/reviewer_fetch_core.py +153 -0
- package/skills/pr-converge/scripts/reviewer_specs.py +98 -0
- package/skills/pr-converge/scripts/test_cursor_agents_continue.py +65 -0
- package/skills/pr-converge/scripts/test_fetch_bugbot_inline_comments.py +107 -6
- package/skills/pr-converge/scripts/test_fetch_bugbot_reviews.py +85 -6
- package/skills/pr-converge/scripts/test_fetch_claude_inline_comments.py +485 -0
- package/skills/pr-converge/scripts/test_fetch_claude_reviews.py +368 -0
- package/skills/pr-converge/scripts/test_fetch_copilot_inline_comments.py +74 -6
- package/skills/pr-converge/scripts/test_fetch_copilot_reviews.py +94 -8
- package/skills/pr-converge/scripts/test_reflow_skill_md.py +162 -0
- package/skills/pr-converge/scripts/test_reviewer_fetch_core.py +448 -0
- package/skills/pr-converge/scripts/test_reviewer_specs.py +107 -0
- package/skills/pr-converge/scripts/test_view_pr_context.py +44 -0
- package/skills/pr-converge/scripts/view_pr_context.py +35 -4
- package/skills/pr-converge/workflows/schedule-wakeup-loop.md +24 -22
- package/skills/bugteam/reference/workflow-path-a-orchestrated-teams.md +0 -113
- package/skills/bugteam/reference/workflow-path-b-task-harness.md +0 -48
- package/skills/bugteam/test_team_lifecycle.py +0 -103
- package/skills/monitor-open-prs/test_team_lifecycle.py +0 -46
- package/skills/pr-converge/scripts/open_followup_copilot_pr.py +0 -136
- package/skills/pr-converge/scripts/test_open_followup_copilot_pr.py +0 -236
- package/skills/pr-converge/test_team_lifecycle.py +0 -56
- package/skills/pr-converge/workflows/ahk-auto-continue-loop.md +0 -108
|
@@ -8,8 +8,6 @@ GIT_DIRECTORY_NAME: str = ".git"
|
|
|
8
8
|
|
|
9
9
|
CLAUDE_DIRECTORY_NAME: str = ".claude"
|
|
10
10
|
|
|
11
|
-
VENV_DIRECTORY_NAME: str = ".venv"
|
|
12
|
-
|
|
13
11
|
PYTEST_INI_FILENAME: str = "pytest.ini"
|
|
14
12
|
|
|
15
13
|
PYPROJECT_TOML_FILENAME: str = "pyproject.toml"
|
|
@@ -18,13 +16,16 @@ PRE_COMMIT_CONFIG_YAML_FILENAME: str = ".pre-commit-config.yaml"
|
|
|
18
16
|
|
|
19
17
|
PYTEST_TOML_TABLE_PREFIX: str = "[tool.pytest"
|
|
20
18
|
|
|
21
|
-
|
|
22
|
-
"test_*.py",
|
|
23
|
-
"*_test.py",
|
|
24
|
-
)
|
|
19
|
+
PYTEST_FAILED_FIRST_FLAG: str = "--ff"
|
|
25
20
|
|
|
26
|
-
|
|
27
|
-
|
|
21
|
+
ALL_GIT_LS_FILES_TEST_DISCOVERY_SUBCOMMAND: tuple[str, ...] = (
|
|
22
|
+
"ls-files",
|
|
23
|
+
"--cached",
|
|
24
|
+
"--others",
|
|
25
|
+
"--exclude-standard",
|
|
26
|
+
"--",
|
|
27
|
+
"**/test_*.py",
|
|
28
|
+
"**/*_test.py",
|
|
28
29
|
)
|
|
29
30
|
|
|
30
31
|
ALL_REPOSITORY_ROOT_MARKER_FILENAMES: tuple[str, str] = (
|
|
@@ -44,4 +45,24 @@ ALL_PRE_COMMIT_RUN_ALL_FILES_COMMAND: tuple[str, str, str] = (
|
|
|
44
45
|
"--all-files",
|
|
45
46
|
)
|
|
46
47
|
|
|
48
|
+
ALL_GIT_DIFF_NAME_ONLY_SUBCOMMAND: tuple[str, str] = (
|
|
49
|
+
"diff",
|
|
50
|
+
"--name-only",
|
|
51
|
+
)
|
|
52
|
+
|
|
53
|
+
PYTEST_SCOPE_ALL: str = "all"
|
|
54
|
+
|
|
55
|
+
PYTEST_SCOPE_CHANGED: str = "changed"
|
|
56
|
+
|
|
57
|
+
ALL_PYTEST_SCOPE_CHOICES: tuple[str, str] = (PYTEST_SCOPE_ALL, PYTEST_SCOPE_CHANGED)
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
PYTHON_FILE_SUFFIX: str = ".py"
|
|
61
|
+
|
|
62
|
+
PYTEST_TEST_FILENAME_PREFIX: str = "test_"
|
|
63
|
+
|
|
64
|
+
PYTEST_TEST_FILENAME_SUFFIX: str = "_test"
|
|
65
|
+
|
|
47
66
|
PYTEST_NO_TESTS_COLLECTED_EXIT_CODE: int = 5
|
|
67
|
+
|
|
68
|
+
TESTS_DIRECTORY_NAME: str = "tests"
|
|
@@ -5,23 +5,57 @@ import sys
|
|
|
5
5
|
from pathlib import Path
|
|
6
6
|
|
|
7
7
|
sys.modules.pop("config", None)
|
|
8
|
-
|
|
9
|
-
|
|
8
|
+
_script_directory_resolved = Path(__file__).resolve().parent
|
|
9
|
+
_script_directory_absolute = Path(__file__).absolute().parent
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
def _entry_points_at_preflight_script_directory(each_path_entry: str) -> bool:
|
|
13
|
+
if each_path_entry in (
|
|
14
|
+
str(_script_directory_resolved),
|
|
15
|
+
str(_script_directory_absolute),
|
|
16
|
+
):
|
|
17
|
+
return True
|
|
18
|
+
try:
|
|
19
|
+
candidate_path = Path(each_path_entry)
|
|
20
|
+
except (OSError, ValueError):
|
|
21
|
+
return False
|
|
22
|
+
if candidate_path.exists():
|
|
23
|
+
try:
|
|
24
|
+
return os.path.samefile(candidate_path, _script_directory_resolved)
|
|
25
|
+
except OSError:
|
|
26
|
+
return False
|
|
27
|
+
return False
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
for each_index in range(len(sys.path) - 1, -1, -1):
|
|
31
|
+
if _entry_points_at_preflight_script_directory(sys.path[each_index]):
|
|
32
|
+
sys.path.pop(each_index)
|
|
33
|
+
_preflight_scripts_path_entry = str(_script_directory_absolute)
|
|
34
|
+
if _preflight_scripts_path_entry not in sys.path:
|
|
35
|
+
sys.path.insert(0, _preflight_scripts_path_entry)
|
|
10
36
|
|
|
11
37
|
from config.fix_hookspath_constants import HOOKS_PATH_VERIFICATION_SUFFIX
|
|
12
38
|
from config.preflight_constants import (
|
|
13
39
|
ALL_GIT_CONFIG_GET_CORE_HOOKS_PATH_SUBCOMMAND,
|
|
40
|
+
ALL_GIT_DIFF_NAME_ONLY_SUBCOMMAND,
|
|
41
|
+
ALL_GIT_LS_FILES_TEST_DISCOVERY_SUBCOMMAND,
|
|
14
42
|
ALL_PRE_COMMIT_RUN_ALL_FILES_COMMAND,
|
|
15
|
-
ALL_TEST_FILE_PATTERNS_FOR_DISCOVERY,
|
|
16
|
-
ALL_TESTS_DIRECTORY_IGNORE_PARTS,
|
|
17
43
|
BUGTEAM_PREFLIGHT_SKIP_ENABLED_VALUE,
|
|
18
44
|
BUGTEAM_PREFLIGHT_SKIP_ENV_VAR_NAME,
|
|
19
45
|
GIT_DIRECTORY_NAME,
|
|
20
46
|
PRE_COMMIT_CONFIG_YAML_FILENAME,
|
|
21
47
|
PYPROJECT_TOML_FILENAME,
|
|
48
|
+
PYTEST_FAILED_FIRST_FLAG,
|
|
22
49
|
PYTEST_INI_FILENAME,
|
|
50
|
+
ALL_PYTEST_SCOPE_CHOICES,
|
|
23
51
|
PYTEST_NO_TESTS_COLLECTED_EXIT_CODE,
|
|
52
|
+
PYTEST_SCOPE_ALL,
|
|
53
|
+
PYTEST_SCOPE_CHANGED,
|
|
54
|
+
PYTEST_TEST_FILENAME_PREFIX,
|
|
55
|
+
PYTEST_TEST_FILENAME_SUFFIX,
|
|
24
56
|
PYTEST_TOML_TABLE_PREFIX,
|
|
57
|
+
PYTHON_FILE_SUFFIX,
|
|
58
|
+
TESTS_DIRECTORY_NAME,
|
|
25
59
|
)
|
|
26
60
|
|
|
27
61
|
|
|
@@ -111,18 +145,41 @@ def has_pytest_configuration(root: Path) -> bool:
|
|
|
111
145
|
return PYTEST_TOML_TABLE_PREFIX in text
|
|
112
146
|
|
|
113
147
|
|
|
114
|
-
def has_discoverable_tests(root: Path) -> bool:
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
for each_path in root.rglob(test_filename_glob):
|
|
118
|
-
if any(each_part in all_ignored_parts for each_part in each_path.parts):
|
|
119
|
-
continue
|
|
148
|
+
def has_discoverable_tests(root: Path) -> bool | None:
|
|
149
|
+
git_marker = root / GIT_DIRECTORY_NAME
|
|
150
|
+
if not (git_marker.is_dir() or git_marker.is_file()):
|
|
120
151
|
return True
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
152
|
+
command = ["git", "-C", str(root), *ALL_GIT_LS_FILES_TEST_DISCOVERY_SUBCOMMAND]
|
|
153
|
+
try:
|
|
154
|
+
completed = subprocess.run(
|
|
155
|
+
command,
|
|
156
|
+
capture_output=True,
|
|
157
|
+
text=True,
|
|
158
|
+
encoding="utf-8",
|
|
159
|
+
errors="replace",
|
|
160
|
+
check=True,
|
|
161
|
+
)
|
|
162
|
+
except FileNotFoundError:
|
|
163
|
+
print(
|
|
164
|
+
"bugteam_preflight: git is not installed or not available on PATH.",
|
|
165
|
+
file=sys.stderr,
|
|
166
|
+
)
|
|
167
|
+
return None
|
|
168
|
+
except subprocess.CalledProcessError as error:
|
|
169
|
+
error_detail = (error.stderr or "").strip()
|
|
170
|
+
print(
|
|
171
|
+
f"bugteam_preflight: git ls-files failed (exit {error.returncode}):"
|
|
172
|
+
+ (f"\n{error_detail}" if error_detail else ""),
|
|
173
|
+
file=sys.stderr,
|
|
174
|
+
)
|
|
175
|
+
return None
|
|
176
|
+
except OSError as error:
|
|
177
|
+
print(
|
|
178
|
+
f"bugteam_preflight: failed to run git ls-files: {error}",
|
|
179
|
+
file=sys.stderr,
|
|
180
|
+
)
|
|
181
|
+
return None
|
|
182
|
+
return bool(completed.stdout.strip())
|
|
126
183
|
|
|
127
184
|
|
|
128
185
|
def _pytest_exit_code_no_tests_collected() -> int:
|
|
@@ -130,10 +187,17 @@ def _pytest_exit_code_no_tests_collected() -> int:
|
|
|
130
187
|
return pytest_no_tests_collected_exit_code
|
|
131
188
|
|
|
132
189
|
|
|
133
|
-
def run_pytest(
|
|
134
|
-
|
|
190
|
+
def run_pytest(
|
|
191
|
+
repository_root: Path,
|
|
192
|
+
verbose: bool,
|
|
193
|
+
all_test_paths: list[Path] | None = None,
|
|
194
|
+
) -> int:
|
|
195
|
+
command = [sys.executable, "-m", "pytest", PYTEST_FAILED_FIRST_FLAG]
|
|
135
196
|
if not verbose:
|
|
136
197
|
command.append("-q")
|
|
198
|
+
if all_test_paths is not None:
|
|
199
|
+
command.append("--")
|
|
200
|
+
command.extend(str(each_path) for each_path in all_test_paths)
|
|
137
201
|
completed = subprocess.run(
|
|
138
202
|
command,
|
|
139
203
|
cwd=str(repository_root),
|
|
@@ -144,6 +208,99 @@ def run_pytest(repository_root: Path, verbose: bool) -> int:
|
|
|
144
208
|
return completed.returncode
|
|
145
209
|
|
|
146
210
|
|
|
211
|
+
def get_changed_files(repository_root: Path, base_ref: str) -> list[Path] | None:
|
|
212
|
+
if base_ref.startswith("-"):
|
|
213
|
+
print(
|
|
214
|
+
f"bugteam_preflight: invalid base_ref '{base_ref}' starts "
|
|
215
|
+
f"with hyphen; falling back to full suite.",
|
|
216
|
+
file=sys.stderr,
|
|
217
|
+
)
|
|
218
|
+
return None
|
|
219
|
+
command = [
|
|
220
|
+
"git",
|
|
221
|
+
*ALL_GIT_DIFF_NAME_ONLY_SUBCOMMAND,
|
|
222
|
+
f"{base_ref}...HEAD",
|
|
223
|
+
]
|
|
224
|
+
try:
|
|
225
|
+
completed = subprocess.run(
|
|
226
|
+
command,
|
|
227
|
+
cwd=str(repository_root),
|
|
228
|
+
capture_output=True,
|
|
229
|
+
text=True,
|
|
230
|
+
encoding="utf-8",
|
|
231
|
+
errors="replace",
|
|
232
|
+
check=False,
|
|
233
|
+
)
|
|
234
|
+
except FileNotFoundError:
|
|
235
|
+
print(
|
|
236
|
+
"bugteam_preflight: git is not installed or not available on PATH.\n"
|
|
237
|
+
f"bugteam_preflight: cannot determine changed files against "
|
|
238
|
+
f"{base_ref}; falling back to full suite.",
|
|
239
|
+
file=sys.stderr,
|
|
240
|
+
)
|
|
241
|
+
return None
|
|
242
|
+
except OSError as os_error:
|
|
243
|
+
print(
|
|
244
|
+
f"bugteam_preflight: failed to run git: {os_error}\n"
|
|
245
|
+
f"bugteam_preflight: cannot determine changed files against "
|
|
246
|
+
f"{base_ref}; falling back to full suite.",
|
|
247
|
+
file=sys.stderr,
|
|
248
|
+
)
|
|
249
|
+
return None
|
|
250
|
+
if completed.returncode != 0:
|
|
251
|
+
print(
|
|
252
|
+
f"bugteam_preflight: git diff against {base_ref} failed "
|
|
253
|
+
f"(exit {completed.returncode}); falling back to full suite.\n"
|
|
254
|
+
f"{completed.stderr.strip()}",
|
|
255
|
+
file=sys.stderr,
|
|
256
|
+
)
|
|
257
|
+
return None
|
|
258
|
+
return [
|
|
259
|
+
Path(each_line.strip())
|
|
260
|
+
for each_line in completed.stdout.splitlines()
|
|
261
|
+
if each_line.strip()
|
|
262
|
+
]
|
|
263
|
+
|
|
264
|
+
|
|
265
|
+
def _find_related_test_files(changed_path: Path, repository_root: Path) -> list[Path]:
|
|
266
|
+
if changed_path.suffix != PYTHON_FILE_SUFFIX:
|
|
267
|
+
return []
|
|
268
|
+
stem = changed_path.stem
|
|
269
|
+
test_prefix = PYTEST_TEST_FILENAME_PREFIX
|
|
270
|
+
test_suffix = PYTEST_TEST_FILENAME_SUFFIX
|
|
271
|
+
if (stem.startswith(test_prefix) or stem.endswith(test_suffix)) and (
|
|
272
|
+
repository_root / changed_path
|
|
273
|
+
).is_file():
|
|
274
|
+
return [repository_root / changed_path]
|
|
275
|
+
full_path = repository_root / changed_path
|
|
276
|
+
parent = full_path.parent
|
|
277
|
+
adjacent_tests = parent / TESTS_DIRECTORY_NAME
|
|
278
|
+
top_tests = repository_root / TESTS_DIRECTORY_NAME
|
|
279
|
+
relative_parent = changed_path.parent
|
|
280
|
+
python_suffix = PYTHON_FILE_SUFFIX
|
|
281
|
+
all_candidates = [
|
|
282
|
+
parent / f"{test_prefix}{stem}{python_suffix}",
|
|
283
|
+
parent / f"{stem}{test_suffix}{python_suffix}",
|
|
284
|
+
adjacent_tests / f"{test_prefix}{stem}{python_suffix}",
|
|
285
|
+
adjacent_tests / f"{stem}{test_suffix}{python_suffix}",
|
|
286
|
+
]
|
|
287
|
+
if relative_parent != Path("."):
|
|
288
|
+
all_candidates.extend([
|
|
289
|
+
top_tests / relative_parent / f"{test_prefix}{stem}{python_suffix}",
|
|
290
|
+
top_tests / relative_parent / f"{stem}{test_suffix}{python_suffix}",
|
|
291
|
+
])
|
|
292
|
+
return sorted({each_candidate for each_candidate in all_candidates if each_candidate.is_file()})
|
|
293
|
+
|
|
294
|
+
|
|
295
|
+
def discover_related_tests(
|
|
296
|
+
all_changed_files: list[Path], repository_root: Path
|
|
297
|
+
) -> list[Path]:
|
|
298
|
+
related: set[Path] = set()
|
|
299
|
+
for each_file in all_changed_files:
|
|
300
|
+
related.update(_find_related_test_files(each_file, repository_root))
|
|
301
|
+
return sorted(related)
|
|
302
|
+
|
|
303
|
+
|
|
147
304
|
def run_pre_commit(repository_root: Path) -> int:
|
|
148
305
|
completed = subprocess.run(
|
|
149
306
|
list(ALL_PRE_COMMIT_RUN_ALL_FILES_COMMAND),
|
|
@@ -179,6 +336,26 @@ def parse_arguments(all_arguments: list[str]) -> argparse.Namespace:
|
|
|
179
336
|
action="store_true",
|
|
180
337
|
help="Verbose pytest output.",
|
|
181
338
|
)
|
|
339
|
+
parser.add_argument(
|
|
340
|
+
"--base-ref",
|
|
341
|
+
type=str,
|
|
342
|
+
default=None,
|
|
343
|
+
help=(
|
|
344
|
+
"Git base ref for scoped test selection (e.g., origin/main). "
|
|
345
|
+
"When set, only tests related to files changed vs this ref are run."
|
|
346
|
+
),
|
|
347
|
+
)
|
|
348
|
+
parser.add_argument(
|
|
349
|
+
"--scope",
|
|
350
|
+
type=str,
|
|
351
|
+
choices=list(ALL_PYTEST_SCOPE_CHOICES),
|
|
352
|
+
default=None,
|
|
353
|
+
help=(
|
|
354
|
+
"Test selection scope. 'all' runs the full suite. "
|
|
355
|
+
"'changed' runs only tests related to changed files (requires --base-ref). "
|
|
356
|
+
"Defaults to 'changed' when --base-ref is provided, 'all' otherwise."
|
|
357
|
+
),
|
|
358
|
+
)
|
|
182
359
|
return parser.parse_args(all_arguments)
|
|
183
360
|
|
|
184
361
|
|
|
@@ -202,13 +379,58 @@ def main(all_arguments: list[str]) -> int:
|
|
|
202
379
|
if hooks_path_exit_code != 0:
|
|
203
380
|
return hooks_path_exit_code
|
|
204
381
|
if not arguments.no_pytest and has_pytest_configuration(repository_root):
|
|
205
|
-
|
|
382
|
+
discovery_result = has_discoverable_tests(repository_root)
|
|
383
|
+
if discovery_result is None:
|
|
384
|
+
print(
|
|
385
|
+
"bugteam_preflight: test discovery failed; running full suite anyway.",
|
|
386
|
+
file=sys.stderr,
|
|
387
|
+
)
|
|
388
|
+
elif not discovery_result:
|
|
206
389
|
print(
|
|
207
390
|
"bugteam_preflight: pytest configured but no tests found; skipping pytest.",
|
|
208
391
|
file=sys.stderr,
|
|
209
392
|
)
|
|
210
|
-
|
|
211
|
-
|
|
393
|
+
if discovery_result is not False:
|
|
394
|
+
effective_scope = arguments.scope
|
|
395
|
+
if discovery_result is None:
|
|
396
|
+
effective_scope = PYTEST_SCOPE_ALL
|
|
397
|
+
if effective_scope is None:
|
|
398
|
+
effective_scope = (
|
|
399
|
+
PYTEST_SCOPE_CHANGED
|
|
400
|
+
if arguments.base_ref is not None
|
|
401
|
+
else PYTEST_SCOPE_ALL
|
|
402
|
+
)
|
|
403
|
+
if effective_scope == PYTEST_SCOPE_CHANGED and arguments.base_ref is None:
|
|
404
|
+
print(
|
|
405
|
+
"bugteam_preflight: --scope changed requires --base-ref; "
|
|
406
|
+
"falling back to full suite.",
|
|
407
|
+
file=sys.stderr,
|
|
408
|
+
)
|
|
409
|
+
effective_scope = PYTEST_SCOPE_ALL
|
|
410
|
+
if effective_scope == PYTEST_SCOPE_CHANGED and arguments.base_ref is not None:
|
|
411
|
+
all_changed = get_changed_files(repository_root, arguments.base_ref)
|
|
412
|
+
if all_changed is None:
|
|
413
|
+
exit_code = run_pytest(repository_root, arguments.verbose)
|
|
414
|
+
else:
|
|
415
|
+
all_related = discover_related_tests(all_changed, repository_root)
|
|
416
|
+
if all_related:
|
|
417
|
+
print(
|
|
418
|
+
f"bugteam_preflight: running {len(all_related)} test(s) "
|
|
419
|
+
f"related to changed files (scope=changed).",
|
|
420
|
+
file=sys.stderr,
|
|
421
|
+
)
|
|
422
|
+
exit_code = run_pytest(
|
|
423
|
+
repository_root, arguments.verbose, all_related
|
|
424
|
+
)
|
|
425
|
+
else:
|
|
426
|
+
print(
|
|
427
|
+
"bugteam_preflight: no related tests found; "
|
|
428
|
+
"running full suite.",
|
|
429
|
+
file=sys.stderr,
|
|
430
|
+
)
|
|
431
|
+
exit_code = run_pytest(repository_root, arguments.verbose)
|
|
432
|
+
else:
|
|
433
|
+
exit_code = run_pytest(repository_root, arguments.verbose)
|
|
212
434
|
if exit_code != 0:
|
|
213
435
|
return exit_code
|
|
214
436
|
elif not arguments.no_pytest:
|