Skip to content

Commit 9e21656

Browse files
authored
feat: allow to pass base/head sha to ci scopes (#850)
This will help debug some scenarios locally. This adds `--base` and `--head` to `mergify ci scopes`
1 parent a8c8670 commit 9e21656

File tree

3 files changed

+55
-63
lines changed

3 files changed

+55
-63
lines changed

mergify_cli/ci/cli.py

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -217,6 +217,14 @@ def git_refs() -> None:
217217
default=".mergify.yml",
218218
help="Path to YAML config file.",
219219
)
220+
@click.option(
221+
"--base",
222+
help="The base git reference to use to look for changed files",
223+
)
224+
@click.option(
225+
"--head",
226+
help="The head git reference to use to look for changed files",
227+
)
220228
@click.option(
221229
"--write",
222230
"-w",
@@ -226,9 +234,17 @@ def git_refs() -> None:
226234
def scopes(
227235
config_path: str,
228236
write: str | None = None,
237+
head: str | None = None,
238+
base: str | None = None,
229239
) -> None:
240+
ref = git_refs_detector.detect()
230241
try:
231-
scopes = scopes_cli.detect(config_path=config_path)
242+
scopes = scopes_cli.detect(
243+
config_path=config_path,
244+
base=base or ref.base,
245+
head=head or ref.head,
246+
is_merge_queue=ref.is_merge_queue,
247+
)
232248
except scopes_exc.ScopesError as e:
233249
raise click.ClickException(str(e)) from e
234250

mergify_cli/ci/scopes/cli.py

Lines changed: 14 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,6 @@
1010
import pydantic
1111

1212
from mergify_cli import utils
13-
from mergify_cli.ci.git_refs import detector as git_refs_detector
1413
from mergify_cli.ci.scopes import changed_files
1514
from mergify_cli.ci.scopes import config
1615
from mergify_cli.ci.scopes import exceptions
@@ -100,8 +99,6 @@ class InvalidDetectedScopeError(exceptions.ScopesError):
10099

101100

102101
class DetectedScope(pydantic.BaseModel):
103-
base_ref: str
104-
head_ref: str
105102
scopes: set[str]
106103

107104
def save_to_file(self, file: str) -> None:
@@ -117,12 +114,17 @@ def load_from_file(cls, filename: str) -> DetectedScope:
117114
raise InvalidDetectedScopeError(str(e))
118115

119116

120-
def detect(config_path: str) -> DetectedScope:
117+
def detect(
118+
config_path: str,
119+
*,
120+
base: str,
121+
head: str,
122+
is_merge_queue: bool,
123+
) -> DetectedScope:
121124
cfg = config.Config.from_yaml(config_path)
122-
git_refs = git_refs_detector.detect()
123125

124-
click.echo(f"Base: {git_refs.base}")
125-
click.echo(f"Head: {git_refs.head}")
126+
click.echo(f"Base: {base}")
127+
click.echo(f"Head: {head}")
126128

127129
scopes_hit: set[str]
128130
per_scope: dict[str, list[str]]
@@ -133,7 +135,7 @@ def detect(config_path: str) -> DetectedScope:
133135
scopes_hit = set()
134136
per_scope = {}
135137
elif isinstance(source, config.SourceFiles):
136-
changed = changed_files.git_changed_files(git_refs.base, git_refs.head)
138+
changed = changed_files.git_changed_files(base, head)
137139
click.echo("Changed files detected:")
138140
for file in changed:
139141
click.echo(f"- {file}")
@@ -148,7 +150,7 @@ def detect(config_path: str) -> DetectedScope:
148150

149151
if cfg.scopes.merge_queue_scope is not None:
150152
all_scopes.add(cfg.scopes.merge_queue_scope)
151-
if git_refs.is_merge_queue:
153+
if is_merge_queue:
152154
scopes_hit.add(cfg.scopes.merge_queue_scope)
153155

154156
if scopes_hit:
@@ -161,19 +163,14 @@ def detect(config_path: str) -> DetectedScope:
161163
else:
162164
click.echo("No scopes matched.")
163165

164-
git_refs.maybe_write_to_github_outputs()
165166
maybe_write_github_outputs(all_scopes, scopes_hit)
166167
maybe_write_github_step_summary(
167-
git_refs.base,
168-
git_refs.head,
168+
base,
169+
head,
169170
all_scopes,
170171
scopes_hit,
171172
)
172-
return DetectedScope(
173-
base_ref=git_refs.base,
174-
head_ref=git_refs.head,
175-
scopes=scopes_hit,
176-
)
173+
return DetectedScope(scopes=scopes_hit)
177174

178175

179176
async def send_scopes(

mergify_cli/tests/ci/scopes/test_cli.py

Lines changed: 24 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,6 @@
88
import respx
99
import yaml
1010

11-
from mergify_cli.ci.git_refs import detector
1211
from mergify_cli.ci.scopes import cli
1312
from mergify_cli.ci.scopes import config
1413
from mergify_cli.ci.scopes import exceptions
@@ -337,13 +336,11 @@ def test_maybe_write_github_outputs_no_env(
337336
cli.maybe_write_github_outputs(["backend"], {"backend"})
338337

339338

340-
@mock.patch("mergify_cli.ci.git_refs.detector.detect")
341339
@mock.patch("mergify_cli.ci.scopes.changed_files.git_changed_files")
342340
@mock.patch("mergify_cli.ci.scopes.cli.maybe_write_github_outputs")
343341
def test_detect_with_matches(
344342
mock_github_outputs: mock.Mock,
345343
mock_git_changed: mock.Mock,
346-
mock_detect_base: mock.Mock,
347344
tmp_path: pathlib.Path,
348345
) -> None:
349346
# Setup config file
@@ -361,19 +358,18 @@ def test_detect_with_matches(
361358
config_file.write_text(yaml.dump(config_data))
362359

363360
# Setup mocks
364-
mock_detect_base.return_value = detector.References(
365-
"old",
366-
"new",
367-
is_merge_queue=True,
368-
)
369361
mock_git_changed.return_value = ["api/models.py", "other.txt"]
370362

371363
# Capture output
372364
with mock.patch("click.echo") as mock_echo:
373-
result = cli.detect(str(config_file))
365+
result = cli.detect(
366+
str(config_file),
367+
base="old",
368+
head="new",
369+
is_merge_queue=True,
370+
)
374371

375372
# Verify calls
376-
mock_detect_base.assert_called_once()
377373
mock_git_changed.assert_called_once_with("old", "new")
378374
mock_github_outputs.assert_called_once()
379375

@@ -385,16 +381,12 @@ def test_detect_with_matches(
385381
assert "- backend" in calls
386382
assert "- merge-queue" in calls
387383

388-
assert result.base_ref == "old"
389-
assert result.head_ref == "new"
390384
assert result.scopes == {"backend", "merge-queue"}
391385

392386

393-
@mock.patch("mergify_cli.ci.git_refs.detector.detect")
394387
@mock.patch("mergify_cli.ci.scopes.cli.maybe_write_github_outputs")
395388
def test_detect_manual(
396389
_: mock.Mock,
397-
mock_detect_base: mock.Mock,
398390
tmp_path: pathlib.Path,
399391
) -> None:
400392
# Setup config file
@@ -404,28 +396,24 @@ def test_detect_manual(
404396
config_file = tmp_path / ".mergify-ci.yml"
405397
config_file.write_text(yaml.dump(config_data))
406398

407-
# Setup mocks
408-
mock_detect_base.return_value = detector.References(
409-
"old",
410-
"new",
411-
is_merge_queue=False,
412-
)
413-
414399
# Capture output
415400
with pytest.raises(
416401
exceptions.ScopesError,
417402
match="source `manual` has been set, scopes must be sent with `scopes-send` or API",
418403
):
419-
cli.detect(str(config_file))
404+
cli.detect(
405+
str(config_file),
406+
base="old",
407+
head="new",
408+
is_merge_queue=False,
409+
)
420410

421411

422-
@mock.patch("mergify_cli.ci.git_refs.detector.detect")
423412
@mock.patch("mergify_cli.ci.scopes.changed_files.git_changed_files")
424413
@mock.patch("mergify_cli.ci.scopes.cli.maybe_write_github_outputs")
425414
def test_detect_no_matches(
426415
_: mock.Mock,
427416
mock_git_changed: mock.Mock,
428-
mock_detect_base: mock.Mock,
429417
tmp_path: pathlib.Path,
430418
) -> None:
431419
# Setup config file
@@ -436,32 +424,28 @@ def test_detect_no_matches(
436424
config_file.write_text(yaml.dump(config_data))
437425

438426
# Setup mocks
439-
mock_detect_base.return_value = detector.References(
440-
"old",
441-
"new",
442-
is_merge_queue=False,
443-
)
444427
mock_git_changed.return_value = ["other.txt"]
445428

446429
# Capture output
447430
with mock.patch("click.echo") as mock_echo:
448-
result = cli.detect(str(config_file))
431+
result = cli.detect(
432+
str(config_file),
433+
base="old",
434+
head="new",
435+
is_merge_queue=False,
436+
)
449437

450438
# Verify output
451439
calls = [call.args[0] for call in mock_echo.call_args_list]
452440
assert "Base: old" in calls
453441
assert "Head: new" in calls
454442
assert "No scopes matched." in calls
455443
assert result.scopes == set()
456-
assert result.base_ref == "old"
457-
assert result.head_ref == "new"
458444

459445

460-
@mock.patch("mergify_cli.ci.git_refs.detector.detect")
461446
@mock.patch("mergify_cli.ci.scopes.changed_files.git_changed_files")
462447
def test_detect_debug_output(
463448
mock_git_changed: mock.Mock,
464-
mock_detect_base: mock.Mock,
465449
tmp_path: pathlib.Path,
466450
monkeypatch: pytest.MonkeyPatch,
467451
) -> None:
@@ -476,23 +460,21 @@ def test_detect_debug_output(
476460
config_file.write_text(yaml.dump(config_data))
477461

478462
# Setup mocks
479-
mock_detect_base.return_value = detector.References(
480-
"main",
481-
"HEAD",
482-
is_merge_queue=False,
483-
)
484463
mock_git_changed.return_value = ["api/models.py", "api/views.py"]
485464

486465
# Capture output
487466
with mock.patch("click.echo") as mock_echo:
488-
result = cli.detect(str(config_file))
489-
467+
result = cli.detect(
468+
str(config_file),
469+
base="old",
470+
head="new",
471+
is_merge_queue=False,
472+
)
490473
# Verify debug output includes file details
491474
calls = [call.args[0] for call in mock_echo.call_args_list]
492475
assert any(" api/models.py" in call for call in calls)
493476
assert any(" api/views.py" in call for call in calls)
494477

495-
assert result.base_ref == "main"
496478
assert result.scopes == {"backend"}
497479

498480

@@ -535,12 +517,9 @@ async def test_upload_scopes(respx_mock: respx.MockRouter) -> None:
535517
def test_dump(tmp_path: pathlib.Path) -> None:
536518
config_file = tmp_path / "scopes.json"
537519
saved = cli.DetectedScope(
538-
base_ref="base",
539-
head_ref="head",
540520
scopes={"backend", "merge-queue"},
541521
)
542522
saved.save_to_file(str(config_file))
543523

544524
loaded = cli.DetectedScope.load_from_file(str(config_file))
545525
assert loaded.scopes == saved.scopes
546-
assert loaded.base_ref == saved.base_ref

0 commit comments

Comments
 (0)