Skip to content

Commit 993f795

Browse files
authored
feat: support default-status-checks at repository level (#1112)
## Problem `default-status-checks` was only accepted at the global config level. Different repos need different external CI checks required for `can-be-merged`, but there was no way to configure this per-repo. ## Solution The runtime code (`Config.get_value()`) already resolves per-repo → global config, so per-repo `default-status-checks` already worked at runtime. Only the YAML schema was missing the property at the per-repo level, causing schema validation to reject it. ### Changes - `webhook_server/config/schema.yaml` — added `default-status-checks` to per-repo properties - `examples/config.yaml` — added per-repo usage example - `webhook_server/tests/test_config_schema.py` — added test validating schema acceptance and runtime resolution via `Config()` constructor - `docs/repository-overrides.md` — added row to overrides table ### Config example ```yaml repositories: my-repo: name: my-org/my-repo default-status-checks: - "WIP" - "can-be-merged" - "ci/my-external-check" ``` Closes #1111 Assisted-by: Claude <noreply@anthropic.com> --------- Signed-off-by: rnetser <rnetser@redhat.com>
1 parent 2715a80 commit 993f795

5 files changed

Lines changed: 94 additions & 0 deletions

File tree

docs/repository-overrides.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ For runtime settings that the webhook reads from the repository, precedence is:
2626
| `github-tokens` | No | Yes | Needed before the repo-local file can be read. |
2727
| `protected-branches` and branch protection sync | No | Yes | Applied by the server when it configures repositories. |
2828
| `branch-protection.required_conversation_resolution` | Yes | Yes | Also affects the runtime `can-be-merged` gate. |
29+
| `default-status-checks` | No | Yes | Override global default-status-checks for this repository. |
2930
| `events`, `test-oracle`, `allow-commands-on-draft-prs` | No | Yes | Current code reads these from `config.yaml`. |
3031

3132
## Labels

examples/config.yaml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -157,6 +157,10 @@ repositories:
157157
log-level: DEBUG # Override global log-level for repository
158158
log-file: my-repository.log # Override global log-file for repository
159159
mask-sensitive-data: false # Override global setting - disable masking for debugging this specific repo (NOT recommended in production)
160+
default-status-checks: # Override global default-status-checks for this repository
161+
- "WIP"
162+
- "can-be-merged"
163+
- "ci/my-external-check"
160164
slack-webhook-url: <Slack webhook url> # Send notification to slack on several operations
161165
verified-job: true
162166
pypi:

webhook_server/config/schema.yaml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -333,6 +333,11 @@ properties:
333333
type: boolean
334334
description: Override global mask-sensitive-data setting for this repository
335335
default: true
336+
default-status-checks:
337+
type: array
338+
items:
339+
type: string
340+
description: Override global default-status-checks for this repository
336341
slack-webhook-url:
337342
type: string
338343
description: Slack webhook URL

webhook_server/tests/test_config_schema.py

Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -274,6 +274,89 @@ def test_repository_structure_validation(self, valid_minimal_config: dict[str, A
274274
finally:
275275
shutil.rmtree(temp_dir)
276276

277+
def test_per_repo_default_status_checks(
278+
self, valid_minimal_config: dict[str, Any], monkeypatch: pytest.MonkeyPatch
279+
) -> None:
280+
"""Test that default-status-checks is accepted at per-repo level and resolved at runtime."""
281+
config = valid_minimal_config.copy()
282+
config["repositories"] = {
283+
"repo1": {
284+
"name": "org/repo1",
285+
"default-status-checks": ["WIP", "can-be-merged", "ci/my-external-check"],
286+
},
287+
}
288+
289+
temp_dir = self.create_temp_config_dir_and_data(config)
290+
291+
try:
292+
monkeypatch.setenv("WEBHOOK_SERVER_DATA_DIR", temp_dir)
293+
294+
repo_config = Config(repository="repo1")
295+
assert repo_config.get_value("default-status-checks") == [
296+
"WIP",
297+
"can-be-merged",
298+
"ci/my-external-check",
299+
]
300+
finally:
301+
shutil.rmtree(temp_dir)
302+
303+
def test_per_repo_default_status_checks_falls_back_to_global(
304+
self, valid_minimal_config: dict[str, Any], monkeypatch: pytest.MonkeyPatch
305+
) -> None:
306+
"""Test that repo without default-status-checks falls back to global value."""
307+
config = valid_minimal_config.copy()
308+
config["default-status-checks"] = ["WIP", "global-check"]
309+
config["repositories"] = {
310+
"repo1": {
311+
"name": "org/repo1",
312+
},
313+
}
314+
315+
temp_dir = self.create_temp_config_dir_and_data(config)
316+
317+
try:
318+
monkeypatch.setenv("WEBHOOK_SERVER_DATA_DIR", temp_dir)
319+
320+
repo_config = Config(repository="repo1")
321+
assert repo_config.get_value("default-status-checks") == ["WIP", "global-check"]
322+
finally:
323+
shutil.rmtree(temp_dir)
324+
325+
def test_per_repo_default_status_checks_overrides_global(
326+
self, valid_minimal_config: dict[str, Any], monkeypatch: pytest.MonkeyPatch
327+
) -> None:
328+
"""Test that per-repo default-status-checks overrides global value."""
329+
config = valid_minimal_config.copy()
330+
config["default-status-checks"] = ["WIP", "global-check"]
331+
config["repositories"] = {
332+
"repo1": {
333+
"name": "org/repo1",
334+
"default-status-checks": ["WIP", "repo-specific-check"],
335+
},
336+
}
337+
338+
temp_dir = self.create_temp_config_dir_and_data(config)
339+
340+
try:
341+
monkeypatch.setenv("WEBHOOK_SERVER_DATA_DIR", temp_dir)
342+
343+
repo_config = Config(repository="repo1")
344+
result = repo_config.get_value("default-status-checks")
345+
assert result == ["WIP", "repo-specific-check"]
346+
assert "global-check" not in result
347+
finally:
348+
shutil.rmtree(temp_dir)
349+
350+
def test_schema_allows_per_repo_default_status_checks(self) -> None:
351+
"""Test that schema.yaml defines default-status-checks in per-repo properties."""
352+
schema_path = os.path.join(os.path.dirname(__file__), "..", "config", "schema.yaml")
353+
with open(schema_path) as f:
354+
schema = yaml.safe_load(f)
355+
repo_props = schema["properties"]["repositories"]["additionalProperties"]["properties"]
356+
assert "default-status-checks" in repo_props
357+
assert repo_props["default-status-checks"]["type"] == "array"
358+
assert repo_props["default-status-checks"]["items"]["type"] == "string"
359+
277360
def test_tox_configuration_flexibility(self, valid_minimal_config: dict[str, Any]) -> None:
278361
"""Test that tox configuration accepts both string and array values."""
279362
config = valid_minimal_config.copy()

webhook_server/tests/test_schema_validator.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -188,6 +188,7 @@ def _validate_single_repository(self, repo_name: str, repo_config: Any) -> None:
188188
array_fields = [
189189
"events",
190190
"auto-verified-and-merged-users",
191+
"default-status-checks",
191192
"github-tokens",
192193
"set-auto-merge-prs",
193194
"can-be-merged-required-labels",

0 commit comments

Comments
 (0)