diff --git a/src/semantic_release/cli/config.py b/src/semantic_release/cli/config.py index 586eb1923..60b739fd1 100644 --- a/src/semantic_release/cli/config.py +++ b/src/semantic_release/cli/config.py @@ -360,9 +360,12 @@ def verify_git_repo_dir(cls, dir_path: Path) -> Path: try: # Check for repository & walk up parent directories with Repo(str(dir_path), search_parent_directories=True) as git_repo: - found_path = Path( - git_repo.working_tree_dir or git_repo.working_dir - ).absolute() + found_path = ( + Path(git_repo.working_tree_dir or git_repo.working_dir) + .expanduser() + .absolute() + ) + except InvalidGitRepositoryError as err: raise InvalidGitRepositoryError("No valid git repository found!") from err @@ -370,7 +373,8 @@ def verify_git_repo_dir(cls, dir_path: Path) -> Path: logging.warning( "Found .git/ in higher parent directory rather than provided in configuration." ) - return found_path + + return found_path.resolve() @field_validator("commit_parser", mode="after") @classmethod diff --git a/tests/e2e/cmd_config/test_generate_config.py b/tests/e2e/cmd_config/test_generate_config.py index cac6778bc..bfabaa1de 100644 --- a/tests/e2e/cmd_config/test_generate_config.py +++ b/tests/e2e/cmd_config/test_generate_config.py @@ -9,14 +9,18 @@ from semantic_release.cli.commands.main import main from semantic_release.cli.config import RawConfig -from tests.const import GENERATE_CONFIG_SUBCMD, MAIN_PROG_NAME +from tests.const import GENERATE_CONFIG_SUBCMD, MAIN_PROG_NAME, VERSION_SUBCMD +from tests.fixtures.repos import repo_w_no_tags_angular_commits from tests.util import assert_successful_exit_code if TYPE_CHECKING: + from pathlib import Path from typing import Any from click.testing import CliRunner + from tests.fixtures.example_project import ExProjectDir + @pytest.fixture def raw_config_dict() -> dict[str, Any]: @@ -24,44 +28,106 @@ def raw_config_dict() -> dict[str, Any]: @pytest.mark.parametrize("args", [(), ("--format", "toml"), ("--format", "TOML")]) +@pytest.mark.usefixtures(repo_w_no_tags_angular_commits.__name__) def test_generate_config_toml( - cli_runner: CliRunner, args: tuple[str], raw_config_dict: dict[str, Any] + cli_runner: CliRunner, + args: tuple[str], + raw_config_dict: dict[str, Any], + example_project_dir: ExProjectDir, ): + # Setup: Generate the expected configuration as a TOML string expected_config_as_str = tomlkit.dumps( {"semantic_release": raw_config_dict} ).strip() + # Act: Print the generated configuration to stdout cli_cmd = [MAIN_PROG_NAME, GENERATE_CONFIG_SUBCMD, *args] - result = cli_runner.invoke(main, cli_cmd[1:]) + # Evaluate: Check that the command ran successfully and that the output matches the expected configuration assert_successful_exit_code(result, cli_cmd) assert expected_config_as_str == result.output.strip() + # Setup: Write the generated configuration to a file + config_file = "releaserc.toml" + example_project_dir.joinpath(config_file).write_text(result.output) + + # Act: Validate that the generated config is a valid configuration for PSR + cli_cmd = [ + MAIN_PROG_NAME, + "--noop", + "--strict", + "-c", + config_file, + VERSION_SUBCMD, + "--print", + ] + result = cli_runner.invoke(main, cli_cmd[1:]) + + # Evaluate: Check that the version command in noop mode ran successfully + # which means PSR loaded the configuration successfully + assert_successful_exit_code(result, cli_cmd) + @pytest.mark.parametrize("args", [("--format", "json"), ("--format", "JSON")]) +@pytest.mark.usefixtures(repo_w_no_tags_angular_commits.__name__) def test_generate_config_json( - cli_runner: CliRunner, args: tuple[str], raw_config_dict: dict[str, Any] + cli_runner: CliRunner, + args: tuple[str], + raw_config_dict: dict[str, Any], + example_project_dir: ExProjectDir, ): + # Setup: Generate the expected configuration as a JSON string expected_config_as_str = json.dumps( {"semantic_release": raw_config_dict}, indent=4 ).strip() + # Act: Print the generated configuration to stdout cli_cmd = [MAIN_PROG_NAME, GENERATE_CONFIG_SUBCMD, *args] - result = cli_runner.invoke(main, cli_cmd[1:]) + # Evaluate: Check that the command ran successfully and that the output matches the expected configuration assert_successful_exit_code(result, cli_cmd) assert expected_config_as_str == result.output.strip() + # Setup: Write the generated configuration to a file + config_file = "releaserc.json" + example_project_dir.joinpath(config_file).write_text(result.output) + # Act: Validate that the generated config is a valid configuration for PSR + cli_cmd = [ + MAIN_PROG_NAME, + "--noop", + "--strict", + "-c", + config_file, + VERSION_SUBCMD, + "--print", + ] + result = cli_runner.invoke(main, cli_cmd[1:]) + + # Evaluate: Check that the version command in noop mode ran successfully + # which means PSR loaded the configuration successfully + assert_successful_exit_code(result, cli_cmd) + + +@pytest.mark.usefixtures(repo_w_no_tags_angular_commits.__name__) def test_generate_config_pyproject_toml( - cli_runner: CliRunner, raw_config_dict: dict[str, Any] + cli_runner: CliRunner, + raw_config_dict: dict[str, Any], + example_pyproject_toml: Path, ): + # Setup: Generate the expected configuration as a TOML string according to PEP 518 expected_config_as_str = tomlkit.dumps( {"tool": {"semantic_release": raw_config_dict}} ).strip() + # Setup: Remove any current configuration from pyproject.toml + pyproject_config = tomlkit.loads(example_pyproject_toml.read_text(encoding="utf-8")) + pyproject_config.get("tool", {}).pop("semantic_release", None) + example_pyproject_toml.write_text(tomlkit.dumps(pyproject_config)) + + # Act: Print the generated configuration to stdout cli_cmd = [ MAIN_PROG_NAME, GENERATE_CONFIG_SUBCMD, @@ -69,8 +135,27 @@ def test_generate_config_pyproject_toml( "toml", "--pyproject", ] - result = cli_runner.invoke(main, cli_cmd[1:]) + # Evaluate: Check that the command ran successfully and that the output matches the expected configuration assert_successful_exit_code(result, cli_cmd) assert expected_config_as_str == result.output.strip() + + # Setup: Write the generated configuration to a file + example_pyproject_toml.write_text( + str.join( + "\n\n", + [ + example_pyproject_toml.read_text(encoding="utf-8").strip(), + result.output, + ], + ) + ) + + # Act: Validate that the generated config is a valid configuration for PSR + cli_cmd = [MAIN_PROG_NAME, "--noop", "--strict", VERSION_SUBCMD, "--print"] + result = cli_runner.invoke(main, cli_cmd[1:]) + + # Evaluate: Check that the version command in noop mode ran successfully + # which means PSR loaded the configuration successfully + assert_successful_exit_code(result, cli_cmd)