Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 11 additions & 4 deletions mergify_cli/ci/scopes/base_detector.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
from __future__ import annotations

import dataclasses
import json
import os
import pathlib
Expand Down Expand Up @@ -64,7 +65,13 @@ def _detect_base_from_event(ev: dict[str, typing.Any]) -> str | None:
return None


def detect() -> str:
@dataclasses.dataclass
class Base:
ref: str
is_merge_queue: bool


def detect() -> Base:
event_path = os.environ.get("GITHUB_EVENT_PATH")
event: dict[str, typing.Any] | None = None
if event_path and pathlib.Path(event_path).is_file():
Expand All @@ -78,17 +85,17 @@ def detect() -> str:
# 0) merge-queue PR override
mq_sha = _detect_base_from_merge_queue_payload(event)
if mq_sha:
return mq_sha
return Base(mq_sha, is_merge_queue=True)

# 1) standard event payload
event_sha = _detect_base_from_event(event)
if event_sha:
return event_sha
return Base(event_sha, is_merge_queue=False)

# 2) base ref (e.g., PR target branch)
base_ref = os.environ.get("GITHUB_BASE_REF")
if base_ref:
return base_ref
return Base(base_ref, is_merge_queue=False)

msg = (
"Could not detect base SHA. Ensure checkout has sufficient history "
Expand Down
13 changes: 10 additions & 3 deletions mergify_cli/ci/scopes/cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ class ScopeConfig(pydantic.BaseModel):

class Config(pydantic.BaseModel):
scopes: dict[ScopeName, ScopeConfig]
merge_queue_scope: str | None = "merge-queue"

@classmethod
def from_dict(cls, data: dict[str, typing.Any] | typing.Any) -> Config: # noqa: ANN401
Expand Down Expand Up @@ -113,12 +114,18 @@ def detect(config_path: str) -> None:
cfg = Config.from_yaml(config_path)
base = base_detector.detect()
try:
changed = changed_files.git_changed_files(base)
changed = changed_files.git_changed_files(base.ref)
except changed_files.ChangedFilesError as e:
raise ChangedFilesError(str(e))
scopes_hit, per_scope = match_scopes(cfg, changed)

click.echo(f"Base: {base}")
all_scopes = set(cfg.scopes.keys())
if cfg.merge_queue_scope is not None:
all_scopes.add(cfg.merge_queue_scope)
if base.is_merge_queue:
scopes_hit.add(cfg.merge_queue_scope)

click.echo(f"Base: {base.ref}")
if scopes_hit:
click.echo("Scopes touched:")
for s in sorted(scopes_hit):
Expand All @@ -129,4 +136,4 @@ def detect(config_path: str) -> None:
else:
click.echo("No scopes matched.")

maybe_write_github_outputs(cfg.scopes.keys(), scopes_hit)
maybe_write_github_outputs(all_scopes, scopes_hit)
6 changes: 3 additions & 3 deletions mergify_cli/tests/ci/scopes/test_base_detector.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ def test_detect_base_github_base_ref(

result = base_detector.detect()

assert result == "main"
assert result == base_detector.Base("main", is_merge_queue=False)


def test_detect_base_from_event_path(
Expand All @@ -35,7 +35,7 @@ def test_detect_base_from_event_path(

result = base_detector.detect()

assert result == "abc123"
assert result == base_detector.Base("abc123", is_merge_queue=False)


def test_detect_base_merge_queue_override(
Expand All @@ -56,7 +56,7 @@ def test_detect_base_merge_queue_override(

result = base_detector.detect()

assert result == "xyz789"
assert result == base_detector.Base("xyz789", is_merge_queue=True)


def test_detect_base_no_info(monkeypatch: pytest.MonkeyPatch) -> None:
Expand Down
9 changes: 6 additions & 3 deletions mergify_cli/tests/ci/scopes/test_cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
import pytest
import yaml

from mergify_cli.ci.scopes import base_detector
from mergify_cli.ci.scopes import cli


Expand All @@ -23,6 +24,7 @@ def test_from_yaml_valid(tmp_path: pathlib.Path) -> None:

config = cli.Config.from_yaml(str(config_file))
assert config.model_dump() == {
"merge_queue_scope": "merge-queue",
"scopes": {
"backend": {"include": ("api/**/*.py", "backend/**/*.py"), "exclude": ()},
"frontend": {"include": ("ui/**/*.js", "ui/**/*.tsx"), "exclude": ()},
Expand Down Expand Up @@ -223,7 +225,7 @@ def test_detect_with_matches(
config_file.write_text(yaml.dump(config_data))

# Setup mocks
mock_detect_base.return_value = "main"
mock_detect_base.return_value = base_detector.Base("main", is_merge_queue=True)
mock_git_changed.return_value = ["api/models.py", "other.txt"]

# Capture output
Expand All @@ -240,6 +242,7 @@ def test_detect_with_matches(
assert "Base: main" in calls
assert "Scopes touched:" in calls
assert "- backend" in calls
assert "- merge-queue" in calls


@mock.patch("mergify_cli.ci.scopes.cli.base_detector.detect")
Expand All @@ -257,7 +260,7 @@ def test_detect_no_matches(
config_file.write_text(yaml.dump(config_data))

# Setup mocks
mock_detect_base.return_value = "main"
mock_detect_base.return_value = base_detector.Base("main", is_merge_queue=False)
mock_git_changed.return_value = ["other.txt"]

# Capture output
Expand Down Expand Up @@ -287,7 +290,7 @@ def test_detect_debug_output(
config_file.write_text(yaml.dump(config_data))

# Setup mocks
mock_detect_base.return_value = "main"
mock_detect_base.return_value = base_detector.Base("main", is_merge_queue=False)
mock_git_changed.return_value = ["api/models.py", "api/views.py"]

# Capture output
Expand Down