Skip to content

Commit e4a2ace

Browse files
silehtclaude
andauthored
feat(ci): auto-detect Mergify config file path (#922)
Add get_mergify_config_path() helper that looks for the config file in multiple locations (.mergify.yml, .mergify/config.yml, .github/mergify.yml) and returns None if no config file is found. The scopes command now raises a clear error message listing all checked locations when no config file exists. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
1 parent bacf00f commit e4a2ace

File tree

3 files changed

+93
-4
lines changed

3 files changed

+93
-4
lines changed

mergify_cli/ci/cli.py

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -212,9 +212,8 @@ def git_refs() -> None:
212212
@click.option(
213213
"--config",
214214
"config_path",
215-
required=True,
216-
type=click.Path(exists=True),
217-
default=".mergify.yml",
215+
type=click.Path(),
216+
default=detector.get_mergify_config_path,
218217
help="Path to YAML config file.",
219218
)
220219
@click.option(
@@ -232,11 +231,16 @@ def git_refs() -> None:
232231
help="Write the detected scopes to a file (json).",
233232
)
234233
def scopes(
235-
config_path: str,
234+
config_path: str | None,
236235
write: str | None = None,
237236
head: str | None = None,
238237
base: str | None = None,
239238
) -> None:
239+
if config_path is None:
240+
locations = ", ".join(detector.MERGIFY_CONFIG_PATHS)
241+
msg = f"Mergify configuration file not found. Looked in: {locations}"
242+
raise click.ClickException(msg)
243+
240244
if base or head:
241245
ref = git_refs_detector.References(
242246
base=base,

mergify_cli/ci/detector.py

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -202,3 +202,26 @@ def get_github_repository() -> str | None:
202202
return _get_github_repository_from_env("GIT_URL")
203203
case _:
204204
return None
205+
206+
207+
MERGIFY_CONFIG_PATHS = (
208+
".mergify.yml",
209+
".mergify/config.yml",
210+
".github/mergify.yml",
211+
)
212+
213+
214+
def get_mergify_config_path() -> str | None:
215+
"""Detect the Mergify configuration file path.
216+
217+
Looks for the config file in these locations in order:
218+
- .mergify.yml
219+
- .mergify/config.yml
220+
- .github/mergify.yml
221+
222+
Returns None if no config file is found.
223+
"""
224+
for config_path in MERGIFY_CONFIG_PATHS:
225+
if pathlib.Path(config_path).is_file():
226+
return config_path
227+
return None

mergify_cli/tests/ci/test_detector.py

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -163,3 +163,65 @@ def test_get_github_pull_request_number_unsupported_ci(
163163

164164
result = detector.get_github_pull_request_number()
165165
assert result is None
166+
167+
168+
def test_get_mergify_config_path_default_file(
169+
tmp_path: pathlib.Path,
170+
monkeypatch: pytest.MonkeyPatch,
171+
) -> None:
172+
monkeypatch.chdir(tmp_path)
173+
(tmp_path / ".mergify.yml").touch()
174+
175+
result = detector.get_mergify_config_path()
176+
assert result == ".mergify.yml"
177+
178+
179+
def test_get_mergify_config_path_mergify_dir(
180+
tmp_path: pathlib.Path,
181+
monkeypatch: pytest.MonkeyPatch,
182+
) -> None:
183+
monkeypatch.chdir(tmp_path)
184+
(tmp_path / ".mergify").mkdir()
185+
(tmp_path / ".mergify" / "config.yml").touch()
186+
187+
result = detector.get_mergify_config_path()
188+
assert result == ".mergify/config.yml"
189+
190+
191+
def test_get_mergify_config_path_github_dir(
192+
tmp_path: pathlib.Path,
193+
monkeypatch: pytest.MonkeyPatch,
194+
) -> None:
195+
monkeypatch.chdir(tmp_path)
196+
(tmp_path / ".github").mkdir()
197+
(tmp_path / ".github" / "mergify.yml").touch()
198+
199+
result = detector.get_mergify_config_path()
200+
assert result == ".github/mergify.yml"
201+
202+
203+
def test_get_mergify_config_path_priority(
204+
tmp_path: pathlib.Path,
205+
monkeypatch: pytest.MonkeyPatch,
206+
) -> None:
207+
"""Test that .mergify.yml takes priority over other locations."""
208+
monkeypatch.chdir(tmp_path)
209+
(tmp_path / ".mergify.yml").touch()
210+
(tmp_path / ".mergify").mkdir()
211+
(tmp_path / ".mergify" / "config.yml").touch()
212+
(tmp_path / ".github").mkdir()
213+
(tmp_path / ".github" / "mergify.yml").touch()
214+
215+
result = detector.get_mergify_config_path()
216+
assert result == ".mergify.yml"
217+
218+
219+
def test_get_mergify_config_path_none_when_missing(
220+
tmp_path: pathlib.Path,
221+
monkeypatch: pytest.MonkeyPatch,
222+
) -> None:
223+
"""Test that None is returned when no config file exists."""
224+
monkeypatch.chdir(tmp_path)
225+
226+
result = detector.get_mergify_config_path()
227+
assert result is None

0 commit comments

Comments
 (0)