diff --git a/docs/repository-overrides.md b/docs/repository-overrides.md index 159bd3b7b..8697e35ca 100644 --- a/docs/repository-overrides.md +++ b/docs/repository-overrides.md @@ -26,6 +26,7 @@ For runtime settings that the webhook reads from the repository, precedence is: | `github-tokens` | No | Yes | Needed before the repo-local file can be read. | | `protected-branches` and branch protection sync | No | Yes | Applied by the server when it configures repositories. | | `branch-protection.required_conversation_resolution` | Yes | Yes | Also affects the runtime `can-be-merged` gate. | +| `default-status-checks` | No | Yes | Override global default-status-checks for this repository. | | `events`, `test-oracle`, `allow-commands-on-draft-prs` | No | Yes | Current code reads these from `config.yaml`. | ## Labels diff --git a/examples/config.yaml b/examples/config.yaml index c8529020e..14d219499 100644 --- a/examples/config.yaml +++ b/examples/config.yaml @@ -157,6 +157,10 @@ repositories: log-level: DEBUG # Override global log-level for repository log-file: my-repository.log # Override global log-file for repository mask-sensitive-data: false # Override global setting - disable masking for debugging this specific repo (NOT recommended in production) + default-status-checks: # Override global default-status-checks for this repository + - "WIP" + - "can-be-merged" + - "ci/my-external-check" slack-webhook-url: # Send notification to slack on several operations verified-job: true pypi: diff --git a/webhook_server/config/schema.yaml b/webhook_server/config/schema.yaml index 7d0dd7157..bfdf84129 100644 --- a/webhook_server/config/schema.yaml +++ b/webhook_server/config/schema.yaml @@ -333,6 +333,11 @@ properties: type: boolean description: Override global mask-sensitive-data setting for this repository default: true + default-status-checks: + type: array + items: + type: string + description: Override global default-status-checks for this repository slack-webhook-url: type: string description: Slack webhook URL diff --git a/webhook_server/tests/test_config_schema.py b/webhook_server/tests/test_config_schema.py index 1ebb9fc6c..6a6002570 100644 --- a/webhook_server/tests/test_config_schema.py +++ b/webhook_server/tests/test_config_schema.py @@ -274,6 +274,89 @@ def test_repository_structure_validation(self, valid_minimal_config: dict[str, A finally: shutil.rmtree(temp_dir) + def test_per_repo_default_status_checks( + self, valid_minimal_config: dict[str, Any], monkeypatch: pytest.MonkeyPatch + ) -> None: + """Test that default-status-checks is accepted at per-repo level and resolved at runtime.""" + config = valid_minimal_config.copy() + config["repositories"] = { + "repo1": { + "name": "org/repo1", + "default-status-checks": ["WIP", "can-be-merged", "ci/my-external-check"], + }, + } + + temp_dir = self.create_temp_config_dir_and_data(config) + + try: + monkeypatch.setenv("WEBHOOK_SERVER_DATA_DIR", temp_dir) + + repo_config = Config(repository="repo1") + assert repo_config.get_value("default-status-checks") == [ + "WIP", + "can-be-merged", + "ci/my-external-check", + ] + finally: + shutil.rmtree(temp_dir) + + def test_per_repo_default_status_checks_falls_back_to_global( + self, valid_minimal_config: dict[str, Any], monkeypatch: pytest.MonkeyPatch + ) -> None: + """Test that repo without default-status-checks falls back to global value.""" + config = valid_minimal_config.copy() + config["default-status-checks"] = ["WIP", "global-check"] + config["repositories"] = { + "repo1": { + "name": "org/repo1", + }, + } + + temp_dir = self.create_temp_config_dir_and_data(config) + + try: + monkeypatch.setenv("WEBHOOK_SERVER_DATA_DIR", temp_dir) + + repo_config = Config(repository="repo1") + assert repo_config.get_value("default-status-checks") == ["WIP", "global-check"] + finally: + shutil.rmtree(temp_dir) + + def test_per_repo_default_status_checks_overrides_global( + self, valid_minimal_config: dict[str, Any], monkeypatch: pytest.MonkeyPatch + ) -> None: + """Test that per-repo default-status-checks overrides global value.""" + config = valid_minimal_config.copy() + config["default-status-checks"] = ["WIP", "global-check"] + config["repositories"] = { + "repo1": { + "name": "org/repo1", + "default-status-checks": ["WIP", "repo-specific-check"], + }, + } + + temp_dir = self.create_temp_config_dir_and_data(config) + + try: + monkeypatch.setenv("WEBHOOK_SERVER_DATA_DIR", temp_dir) + + repo_config = Config(repository="repo1") + result = repo_config.get_value("default-status-checks") + assert result == ["WIP", "repo-specific-check"] + assert "global-check" not in result + finally: + shutil.rmtree(temp_dir) + + def test_schema_allows_per_repo_default_status_checks(self) -> None: + """Test that schema.yaml defines default-status-checks in per-repo properties.""" + schema_path = os.path.join(os.path.dirname(__file__), "..", "config", "schema.yaml") + with open(schema_path) as f: + schema = yaml.safe_load(f) + repo_props = schema["properties"]["repositories"]["additionalProperties"]["properties"] + assert "default-status-checks" in repo_props + assert repo_props["default-status-checks"]["type"] == "array" + assert repo_props["default-status-checks"]["items"]["type"] == "string" + def test_tox_configuration_flexibility(self, valid_minimal_config: dict[str, Any]) -> None: """Test that tox configuration accepts both string and array values.""" config = valid_minimal_config.copy() diff --git a/webhook_server/tests/test_schema_validator.py b/webhook_server/tests/test_schema_validator.py index 1784e9bf3..d768d6165 100644 --- a/webhook_server/tests/test_schema_validator.py +++ b/webhook_server/tests/test_schema_validator.py @@ -188,6 +188,7 @@ def _validate_single_repository(self, repo_name: str, repo_config: Any) -> None: array_fields = [ "events", "auto-verified-and-merged-users", + "default-status-checks", "github-tokens", "set-auto-merge-prs", "can-be-merged-required-labels",