Repository: packit/packit (python)

Issue:
`packit validate-config` ignores the `-c`/`--config` option
`packit validate-config` looks for the config file in the current directory and ignores the `-c`/`--config` option.

Pull request:
Add -c option to validate-config 
Fixes #2451

## Description
Fixes #2451
 i added a `-c / --config` option to the `validate-config` command.  

## How to Test (So Far)
```bash
# Default behavior (should use packit.yaml in working_dir)
packit validate-config

# New behavior (pass a custom config file)
packit validate-config -c /home/mohamed/custom-packit.yaml 

# Added Test Cases


pytest -v ./tests/functional/test_validate_config.py   
```

RELEASE NOTES BEGIN

You can now validate a Packit config passed to the Packit CLI via a path, e.g., `packit config validate -c /tmp/my-custom-packit-config.yml`.

RELEASE NOTES END


Patch:
diff --git a/packit/api.py b/packit/api.py
index c236e29b..05a48f83 100644
--- a/packit/api.py
+++ b/packit/api.py
@@ -30,6 +30,7 @@ from typing import (
 import bugzilla
 import click
 import git
+import yaml
 from git.exc import GitCommandError
 from ogr.abstract import PullRequest
 from ogr.exceptions import PagureAPIException
@@ -2569,11 +2570,17 @@ The first dist-git commit to be synced is '{short_hash}'.
             self.up.local_project.free_resources()
 
     @staticmethod
-    def validate_package_config(working_dir: Path, offline: bool = False) -> str:
+    def validate_package_config(
+        working_dir: Path,
+        offline: bool = False,
+        config: Optional[str] = None,
+    ) -> Optional[str]:
         """Validate package config.
 
         Args:
             working_dir: Directory with the package config.
+            config: Optional path to a specific Packit configuration file to validate.
+            If not provided, the default config file in the working directory will be used.
 
         Returns:
             String that the config is valid.
@@ -2581,14 +2588,31 @@ The first dist-git commit to be synced is '{short_hash}'.
         Raises:
             PackitConfigException: when the config is not valid
         """
+        config_path = (
+            Path(config)
+            if config
+            else find_packit_yaml(working_dir, try_local_dir_last=True)
+        )
+
+        if not config_path.exists():
+            logger.error(f"Configuration file not found: {config_path}")
+            return None
+
+        logger.info(f"Validating config file: {config_path}")
+
+        try:
+            config_content = load_packit_yaml(config_path)
+        except yaml.YAMLError as e:
+            logger.error(f"Failed to parse YAML file: {e}")
+            return None
 
-        config_path = find_packit_yaml(
+        validator = PackageConfigValidator(
+            config_path,
+            config_content,
             working_dir,
-            try_local_dir_last=True,
+            offline,
         )
-        config_content = load_packit_yaml(config_path)
-        v = PackageConfigValidator(config_path, config_content, working_dir, offline)
-        return v.validate()
+        return validator.validate()
 
     def init_source_git(
         self,
diff --git a/packit/cli/validate_config.py b/packit/cli/validate_config.py
index 890f2ba6..9e71cb9d 100644
--- a/packit/cli/validate_config.py
+++ b/packit/cli/validate_config.py
@@ -7,6 +7,7 @@ Validate PackageConfig
 
 import logging
 import os
+from typing import Optional
 
 import click
 
@@ -27,8 +28,18 @@ logger = logging.getLogger(__name__)
     is_flag=True,
     help="Do not make remote API calls requiring network access.",
 )
+@click.option(
+    "-c",
+    "--config-path",
+    type=click.Path(exists=True),
+    help="Path to a specific Packit configuration file.",
+)
 @cover_packit_exception
-def validate_config(path_or_url: LocalProject, offline: bool):
+def validate_config(
+    path_or_url: LocalProject,
+    offline: bool,
+    config_path: Optional[str] = None,
+):
     """
     Validate PackageConfig.
 
@@ -38,7 +49,13 @@ def validate_config(path_or_url: LocalProject, offline: bool):
     - checks whether monitoring is enabled if 'pull_from_upstream` is used
 
     PATH_OR_URL argument is a local path or a URL to a git repository with packit configuration file
+    config: Optional path to a specific Packit configuration file.
     """
-    output = PackitAPI.validate_package_config(path_or_url.working_dir, offline)
+
+    output = PackitAPI.validate_package_config(
+        path_or_url.working_dir,
+        offline,
+        config_path,
+    )
     logger.info(output)
     # TODO: print more if config.debug


Test patch:
diff --git a/tests/functional/test_validate_config.py b/tests/functional/test_validate_config.py
index 6f9f34f8..7f45b2c2 100644
--- a/tests/functional/test_validate_config.py
+++ b/tests/functional/test_validate_config.py
@@ -5,6 +5,14 @@
 Functional tests the validate-config command
 """
 
+import logging
+from unittest.mock import patch
+
+import pytest
+import yaml
+from flexmock import flexmock
+
+from packit.api import PackitAPI
 from packit.utils.commands import cwd
 from tests.functional.spellbook import call_real_packit_and_return_exit_code
 
@@ -14,3 +22,120 @@ def test_srpm_command_for_path(upstream_or_distgit_path, tmp_path):
         call_real_packit_and_return_exit_code(
             parameters=["--debug", "validate-config", str(upstream_or_distgit_path)],
         )
+
+
+@pytest.fixture
+def dummy_local_project(tmp_path):
+    """Fixture that creates a mock local project with a working directory."""
+    return flexmock(working_dir=tmp_path)
+
+
+def test_valid_config_option(tmp_path, dummy_local_project, caplog):
+    """
+    Test that validate_package_config correctly validates a proper configuration file.
+    """
+    config_path = tmp_path / ".packit.yaml"
+    config_path.write_text(
+        "specfile_path: valid.spec\n"
+        "upstream_project_url: https://example.com/repo\n"
+        "downstream_package_name: valid-package\n",
+    )
+
+    with (
+        patch("packit.api.load_packit_yaml") as mock_load,
+        patch("packit.api.PackageConfigValidator") as mock_validator_cls,
+        caplog.at_level(logging.INFO),
+    ):
+        mock_load.return_value = {
+            "specfile_path": "valid.spec",
+            "upstream_project_url": "https://example.com/repo",
+            "downstream_package_name": "valid-package",
+        }
+        instance = mock_validator_cls.return_value
+        instance.validate.return_value = "Configuration is valid"
+
+        result = PackitAPI.validate_package_config(
+            working_dir=dummy_local_project.working_dir,
+            offline=False,
+            config=str(config_path),
+        )
+
+    assert result == "Configuration is valid"
+    assert f"Validating config file: {config_path}" in caplog.text
+
+
+def test_missing_config_file(tmp_path, dummy_local_project, caplog):
+    """
+    Test that validate_package_config logs an error and returns None when no config file is found.
+    """
+    missing_config = tmp_path / "nonexistent.yaml"
+
+    with (
+        patch("packit.api.find_packit_yaml", return_value=missing_config),
+        caplog.at_level(logging.ERROR),
+    ):
+        result = PackitAPI.validate_package_config(
+            working_dir=dummy_local_project.working_dir,
+            offline=False,
+        )
+
+    assert result is None
+    assert f"Configuration file not found: {missing_config}" in caplog.text
+
+
+def test_yaml_syntax_error(tmp_path, dummy_local_project, caplog):
+    """
+    Test that validate_package_config handles a YAML syntax error correctly.
+    """
+    config_path = tmp_path / ".packit.yaml"
+    config_path.write_text("invalid: [yaml")
+
+    with (
+        patch("packit.api.load_packit_yaml") as mock_load,
+        caplog.at_level(logging.ERROR),
+    ):
+        mock_load.side_effect = yaml.YAMLError("Simulated YAML syntax error")
+
+        result = PackitAPI.validate_package_config(
+            working_dir=dummy_local_project.working_dir,
+            offline=False,
+            config=str(config_path),
+        )
+
+    assert result is None
+    assert "Failed to parse YAML file: Simulated YAML syntax error" in caplog.text
+
+
+def test_default_config_discovery(tmp_path, dummy_local_project, caplog):
+    """
+    Test that validate_package_config finds and validates a default .packit.yaml file.
+    """
+    config_path = tmp_path / ".packit.yaml"
+    config_path.write_text(
+        "specfile_path: valid.spec\n"
+        "upstream_project_url: https://example.com/repo\n"
+        "downstream_package_name: valid-package\n",
+    )
+
+    with (
+        patch("packit.api.find_packit_yaml", return_value=config_path),
+        patch("packit.api.load_packit_yaml") as mock_load,
+        patch("packit.api.PackageConfigValidator") as mock_validator_cls,
+        caplog.at_level(logging.INFO),
+    ):
+        mock_load.return_value = {
+            "specfile_path": "valid.spec",
+            "upstream_project_url": "https://example.com/repo",
+            "downstream_package_name": "valid-package",
+        }
+        instance = mock_validator_cls.return_value
+        instance.validate.return_value = "Configuration is valid"
+
+        result = PackitAPI.validate_package_config(
+            working_dir=dummy_local_project.working_dir,
+            offline=False,
+            config=None,
+        )
+
+    assert result == "Configuration is valid"
+    assert f"Validating config file: {config_path}" in caplog.text