From 606aae7212e050ef36e958b5cc3f46394aa1d7f6 Mon Sep 17 00:00:00 2001 From: Qubasa Date: Fri, 15 Aug 2025 12:58:22 +0700 Subject: [PATCH] flake.py: Error messages are now always ClanSelectErrors. Improved error messages flake.py: Fix unbound variable flake.py: Fix test_create.py test --- pkgs/clan-cli/clan_lib/flake/flake.py | 46 ++++++++++++--------- pkgs/clan-cli/clan_lib/tests/test_create.py | 9 ++-- 2 files changed, 33 insertions(+), 22 deletions(-) diff --git a/pkgs/clan-cli/clan_lib/flake/flake.py b/pkgs/clan-cli/clan_lib/flake/flake.py index ed3db7b2b..6d01a6f9a 100644 --- a/pkgs/clan-cli/clan_lib/flake/flake.py +++ b/pkgs/clan-cli/clan_lib/flake/flake.py @@ -153,30 +153,35 @@ class ClanSelectError(ClanError): flake_identifier: str, selectors: list[str], cmd_error: ClanCmdError | None = None, + description: str | None = None, ) -> None: attribute = None - if cmd_error: - attribute_match = re.search(r"error: attribute '([^']+)'", str(cmd_error)) - attribute = attribute_match.group(1) if attribute_match else None + if cmd_error and description is None: + # Match for "error: " + error_match = re.search(r"error: (.+)", str(cmd_error)) + if error_match: + description = error_match.group(1).strip() if selectors == []: msg = "failed to select []\n" - if len(selectors) == 1: - msg = f"failed to select {selectors[0]}\n" + elif len(selectors) == 1: + msg = f"Error on: $ clan select '{selectors[0]}'\n" else: - msg = f"failed to select: {'\n'.join(selectors)}\n" - msg += f" from {flake_identifier}\n" - if attribute: - msg += f" '{attribute}' is missing\n" + msg = "Error while executing:" + for selector in selectors: + msg += f"$ clan select '{selector}'\n" + self.selectors = selectors self.failed_attr = attribute self.flake_identifier = flake_identifier - super().__init__(msg) + super().__init__(msg, description=description, location=flake_identifier) def __str__(self) -> str: + if self.description: + return f"{self.msg} Reason: {self.description}" return self.msg def __repr__(self) -> str: - return f"ClanSelectError({self.failed_attr})" + return f"ClanSelectError({self})" def selectors_as_dict(selectors: list[Selector]) -> list[dict[str, Any]]: @@ -909,20 +914,23 @@ class Flake: except ClanCmdError as e: if "error: attribute 'clan' missing" in str(e): msg = ("This flake does not export the 'clan' attribute. \n" - "Please write 'clan = clan.config' into your flake.nix.") - raise ClanError(msg) from e - if "error: attribute" in str(e): - # If the error is about a missing attribute, we raise a ClanSelectError - # with the failed selectors and the flake identifier. + "Please write 'clan = clan.config;' into the outputs of your flake.nix.") raise ClanSelectError( flake_identifier=self.identifier, selectors=selectors, cmd_error=e, + description=msg, ) from e - # If the error is not about a missing attribute, we re-raise it as a ClanCmdError - # to preserve the original error context. - raise + # If the error is about a missing attribute, we raise a ClanSelectError + # with the failed selectors and the flake identifier. + raise ClanSelectError( + flake_identifier=self.identifier, + selectors=selectors, + cmd_error=e, + ) from e + + if tmp_store := nix_test_store(): build_output = tmp_store.joinpath(*build_output.parts[1:]) diff --git a/pkgs/clan-cli/clan_lib/tests/test_create.py b/pkgs/clan-cli/clan_lib/tests/test_create.py index 0a6d4b62d..b1508367f 100644 --- a/pkgs/clan-cli/clan_lib/tests/test_create.py +++ b/pkgs/clan-cli/clan_lib/tests/test_create.py @@ -19,7 +19,7 @@ from clan_cli.vars.generate import get_generators, run_generators from clan_lib.cmd import RunOpts, run from clan_lib.dirs import specific_machine_dir from clan_lib.errors import ClanCmdError, ClanError -from clan_lib.flake import Flake +from clan_lib.flake import ClanSelectError, Flake from clan_lib.machines.machines import Machine from clan_lib.network.network import get_network_overview, networks_from_flake from clan_lib.nix import nix_command @@ -283,10 +283,13 @@ def test_clan_create_api( if in_sandbox: # In sandbox: expect build to fail due to network restrictions - with pytest.raises(ClanCmdError) as select_error: + with pytest.raises(ClanSelectError) as select_error: Path(machine.select("config.system.build.toplevel")) # The error should be a select_error without a failed_attr - assert "nixos-system-test-clan" in str(select_error.value.cmd.stderr) + cmd_error = select_error.value.__cause__ + assert cmd_error is not None + assert isinstance(cmd_error, ClanCmdError) + assert "nixos-system-test-clan" in str(cmd_error.cmd.stderr) else: # Outside sandbox: build should succeed toplevel_path = Path(machine.select("config.system.build.toplevel"))