From 69b91875987ab054a735d0a039c05776d6c22a7a Mon Sep 17 00:00:00 2001 From: Johannes Kirschbauer Date: Mon, 30 Jun 2025 09:21:45 +0200 Subject: [PATCH 1/7] Chore: bump nixpkgs --- flake.lock | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/flake.lock b/flake.lock index 8460b729d..997733c48 100644 --- a/flake.lock +++ b/flake.lock @@ -164,10 +164,10 @@ "nixpkgs": { "locked": { "lastModified": 315532800, - "narHash": "sha256-VgDAFPxHNhCfC7rI5I5wFqdiVJBH43zUefVo8hwo7cI=", - "rev": "41da1e3ea8e23e094e5e3eeb1e6b830468a7399e", + "narHash": "sha256-0HRxGUoOMtOYnwlMWY0AkuU88WHaI3Q5GEILmsWpI8U=", + "rev": "a48741b083d4f36dd79abd9f760c84da6b4dc0e5", "type": "tarball", - "url": "https://releases.nixos.org/nixpkgs/nixpkgs-25.11pre814815.41da1e3ea8e2/nixexprs.tar.xz" + "url": "https://releases.nixos.org/nixpkgs/nixpkgs-25.11pre823094.a48741b083d4/nixexprs.tar.xz" }, "original": { "type": "tarball", From 0d1e1d979696ce9a40cfd660e92032fefc6bb290 Mon Sep 17 00:00:00 2001 From: Qubasa Date: Mon, 30 Jun 2025 15:28:15 +0700 Subject: [PATCH 2/7] treefmt/ruff: Set python lint version to 3.13. Fix all new lints coming up. --- pkgs/clan-app/clan_app/api/file_gtk.py | 1 - pkgs/clan-cli/clan_cli/tests/fixtures_flakes.py | 11 ++++++++--- pkgs/clan-cli/clan_cli/tests/helpers/nixos_config.py | 11 ----------- pkgs/clan-cli/clan_cli/vars/check.py | 5 ++--- pkgs/clan-cli/clan_lib/api/__init__.py | 4 ++-- pkgs/clan-cli/clan_lib/machines/delete.py | 5 ++++- pkgs/clan-cli/clan_lib/ssh/remote.py | 1 - pkgs/clan-cli/docs.py | 4 +--- pkgs/clan-cli/pyproject.toml | 5 ++++- pkgs/classgen/main.py | 1 - 10 files changed, 21 insertions(+), 27 deletions(-) delete mode 100644 pkgs/clan-cli/clan_cli/tests/helpers/nixos_config.py diff --git a/pkgs/clan-app/clan_app/api/file_gtk.py b/pkgs/clan-app/clan_app/api/file_gtk.py index aef9a06d7..d3deef616 100644 --- a/pkgs/clan-app/clan_app/api/file_gtk.py +++ b/pkgs/clan-app/clan_app/api/file_gtk.py @@ -1,4 +1,3 @@ -# ruff: noqa: N801 import gi gi.require_version("Gtk", "4.0") diff --git a/pkgs/clan-cli/clan_cli/tests/fixtures_flakes.py b/pkgs/clan-cli/clan_cli/tests/fixtures_flakes.py index fefd8a262..59667d2f3 100644 --- a/pkgs/clan-cli/clan_cli/tests/fixtures_flakes.py +++ b/pkgs/clan-cli/clan_cli/tests/fixtures_flakes.py @@ -5,9 +5,9 @@ import shutil import subprocess as sp import tempfile from collections import defaultdict -from collections.abc import Callable, Iterator +from collections.abc import Iterator from pathlib import Path -from typing import Any, NamedTuple +from typing import NamedTuple import pytest from clan_cli.tests import age_keys @@ -38,7 +38,12 @@ def def_value() -> defaultdict: return defaultdict(def_value) -nested_dict: Callable[[], dict[str, Any]] = lambda: defaultdict(def_value) +def nested_dict() -> defaultdict: + """ + Creates a defaultdict that allows for arbitrary levels of nesting. + For example: d['a']['b']['c'] = value + """ + return defaultdict(def_value) # Substitutes strings in a file. diff --git a/pkgs/clan-cli/clan_cli/tests/helpers/nixos_config.py b/pkgs/clan-cli/clan_cli/tests/helpers/nixos_config.py deleted file mode 100644 index b922c6bf9..000000000 --- a/pkgs/clan-cli/clan_cli/tests/helpers/nixos_config.py +++ /dev/null @@ -1,11 +0,0 @@ -from collections import defaultdict -from collections.abc import Callable -from typing import Any - - -def def_value() -> defaultdict: - return defaultdict(def_value) - - -# allows defining nested dictionary in a single line -nested_dict: Callable[[], dict[str, Any]] = lambda: defaultdict(def_value) diff --git a/pkgs/clan-cli/clan_cli/vars/check.py b/pkgs/clan-cli/clan_cli/vars/check.py index e7087f73d..09f0dd0c9 100644 --- a/pkgs/clan-cli/clan_cli/vars/check.py +++ b/pkgs/clan-cli/clan_cli/vars/check.py @@ -4,14 +4,13 @@ import logging from clan_cli.completions import add_dynamic_completer, complete_machines from clan_lib.errors import ClanError from clan_lib.machines.machines import Machine - -log = logging.getLogger(__name__) - from typing import TYPE_CHECKING if TYPE_CHECKING: from .generate import Var +log = logging.getLogger(__name__) + class VarStatus: def __init__( diff --git a/pkgs/clan-cli/clan_lib/api/__init__.py b/pkgs/clan-cli/clan_lib/api/__init__.py index 789101f15..76c813936 100644 --- a/pkgs/clan-cli/clan_lib/api/__init__.py +++ b/pkgs/clan-cli/clan_lib/api/__init__.py @@ -16,14 +16,14 @@ from typing import ( ) from clan_lib.api.util import JSchemaTypeError +from clan_lib.errors import ClanError +from .serde import dataclass_to_dict, from_dict, sanitize_string log = logging.getLogger(__name__) -from .serde import dataclass_to_dict, from_dict, sanitize_string __all__ = ["dataclass_to_dict", "from_dict", "sanitize_string"] -from clan_lib.errors import ClanError T = TypeVar("T") diff --git a/pkgs/clan-cli/clan_lib/machines/delete.py b/pkgs/clan-cli/clan_lib/machines/delete.py index 29bf5c473..68f210cb8 100644 --- a/pkgs/clan-cli/clan_lib/machines/delete.py +++ b/pkgs/clan-cli/clan_lib/machines/delete.py @@ -42,7 +42,10 @@ def delete_machine(machine: Machine) -> None: # louis@(2025-02-04): clean-up legacy (pre-vars) secrets: sops_folder = sops_secrets_folder(machine.flake.path) - filter_fn = lambda secret_name: secret_name.startswith(f"{machine.name}-") + + def filter_fn(secret_name: str) -> bool: + return secret_name.startswith(f"{machine.name}-") + for secret_name in list_secrets(machine.flake.path, filter_fn): secret_path = sops_folder / secret_name changed_paths.append(secret_path) diff --git a/pkgs/clan-cli/clan_lib/ssh/remote.py b/pkgs/clan-cli/clan_lib/ssh/remote.py index de486d80f..8f4d4f8af 100644 --- a/pkgs/clan-cli/clan_lib/ssh/remote.py +++ b/pkgs/clan-cli/clan_lib/ssh/remote.py @@ -1,4 +1,3 @@ -# ruff: noqa: SLF001 import ipaddress import logging import os diff --git a/pkgs/clan-cli/docs.py b/pkgs/clan-cli/docs.py index 746fa9ed7..abd6aeadc 100644 --- a/pkgs/clan-cli/docs.py +++ b/pkgs/clan-cli/docs.py @@ -1,5 +1,6 @@ import argparse import sys +import re from dataclasses import dataclass from pathlib import Path @@ -118,9 +119,6 @@ def epilog_to_md(text: str) -> str: return md -import re - - def contains_https_link(line: str) -> bool: pattern = r"https://\S+" return re.search(pattern, line) is not None diff --git a/pkgs/clan-cli/pyproject.toml b/pkgs/clan-cli/pyproject.toml index 905caa974..c203368e0 100644 --- a/pkgs/clan-cli/pyproject.toml +++ b/pkgs/clan-cli/pyproject.toml @@ -49,9 +49,12 @@ filterwarnings = "default::ResourceWarning" python_files = ["test_*.py", "*_test.py"] [tool.mypy] -python_version = "3.12" +python_version = "3.13" warn_redundant_casts = true disallow_untyped_calls = true disallow_untyped_defs = true no_implicit_optional = true exclude = "clan_lib.nixpkgs" + +[tool.ruff] +target-version = "py313" \ No newline at end of file diff --git a/pkgs/classgen/main.py b/pkgs/classgen/main.py index 6eeab97b1..46cc22880 100644 --- a/pkgs/classgen/main.py +++ b/pkgs/classgen/main.py @@ -1,4 +1,3 @@ -# ruff: noqa: RUF001 import argparse import json import logging From cb89457731a67a44e296e158b8f0fe74a270dd9f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B6rg=20Thalheim?= Date: Fri, 4 Jul 2025 17:18:13 +0200 Subject: [PATCH 3/7] migrate all projects to python 3.13 linting --- lib/test/container-test-driver/pyproject.toml | 2 +- pkgs/clan-app/pyproject.toml | 2 +- pkgs/clan-app/tests/wayland.py | 4 ++-- pkgs/clan-vm-manager/clan_vm_manager/components/gkvstore.py | 4 ++-- pkgs/clan-vm-manager/clan_vm_manager/components/vmobj.py | 2 +- pkgs/clan-vm-manager/clan_vm_manager/views/details.py | 2 +- pkgs/clan-vm-manager/clan_vm_manager/views/list.py | 2 +- pkgs/clan-vm-manager/pyproject.toml | 2 +- pkgs/clan-vm-manager/tests/wayland.py | 4 ++-- pkgs/generate-test-vars/pyproject.toml | 2 +- 10 files changed, 13 insertions(+), 13 deletions(-) diff --git a/lib/test/container-test-driver/pyproject.toml b/lib/test/container-test-driver/pyproject.toml index c9c3abf54..4a4c3e062 100644 --- a/lib/test/container-test-driver/pyproject.toml +++ b/lib/test/container-test-driver/pyproject.toml @@ -15,7 +15,7 @@ find = {} [tool.setuptools.package-data] test_driver = ["py.typed"] [tool.mypy] -python_version = "3.12" +python_version = "3.13" warn_redundant_casts = true disallow_untyped_calls = true disallow_untyped_defs = true diff --git a/pkgs/clan-app/pyproject.toml b/pkgs/clan-app/pyproject.toml index 60083f92a..25147331f 100644 --- a/pkgs/clan-app/pyproject.toml +++ b/pkgs/clan-app/pyproject.toml @@ -30,7 +30,7 @@ norecursedirs = "tests/helpers" markers = ["impure"] [tool.mypy] -python_version = "3.12" +python_version = "3.13" warn_redundant_casts = true disallow_untyped_calls = true disallow_untyped_defs = true diff --git a/pkgs/clan-app/tests/wayland.py b/pkgs/clan-app/tests/wayland.py index 9fa73961c..2c4ee1ee9 100644 --- a/pkgs/clan-app/tests/wayland.py +++ b/pkgs/clan-app/tests/wayland.py @@ -7,7 +7,7 @@ import pytest @pytest.fixture(scope="session") -def wayland_compositor() -> Generator[Popen, None, None]: +def wayland_compositor() -> Generator[Popen]: # Start the Wayland compositor (e.g., Weston) # compositor = Popen(["weston", "--backend=headless-backend.so"]) compositor = Popen(["weston"]) @@ -20,7 +20,7 @@ GtkProc = NewType("GtkProc", Popen) @pytest.fixture -def app() -> Generator[GtkProc, None, None]: +def app() -> Generator[GtkProc]: cmd = [sys.executable, "-m", "clan_app"] print(f"Running: {cmd}") rapp = Popen( diff --git a/pkgs/clan-vm-manager/clan_vm_manager/components/gkvstore.py b/pkgs/clan-vm-manager/clan_vm_manager/components/gkvstore.py index 9d4d0abab..f830b1073 100644 --- a/pkgs/clan-vm-manager/clan_vm_manager/components/gkvstore.py +++ b/pkgs/clan-vm-manager/clan_vm_manager/components/gkvstore.py @@ -1,6 +1,6 @@ import logging from collections.abc import Callable -from typing import Any, Generic, TypeVar +from typing import Any, TypeVar import gi @@ -22,7 +22,7 @@ V = TypeVar( # clan_vm_manager/components/gkvstore.py:21: error: Definition of "newv" in base class "Object" is incompatible with definition in base class "GInterface" [misc] # clan_vm_manager/components/gkvstore.py:21: error: Definition of "install_properties" in base class "Object" is incompatible with definition in base class "GInterface" [misc] # clan_vm_manager/components/gkvstore.py:21: error: Definition of "getv" in base class "Object" is incompatible with definition in base class "GInterface" [misc] -class GKVStore(GObject.GObject, Gio.ListModel, Generic[K, V]): # type: ignore[misc] +class GKVStore[K, V: GObject.Object](GObject.GObject, Gio.ListModel): # type: ignore[misc] """ A simple key-value store that implements the Gio.ListModel interface, with generic types for keys and values. Only use self[key] and del self[key] for accessing the items for better performance. diff --git a/pkgs/clan-vm-manager/clan_vm_manager/components/vmobj.py b/pkgs/clan-vm-manager/clan_vm_manager/components/vmobj.py index 0be941739..1c4c60f75 100644 --- a/pkgs/clan-vm-manager/clan_vm_manager/components/vmobj.py +++ b/pkgs/clan-vm-manager/clan_vm_manager/components/vmobj.py @@ -143,7 +143,7 @@ class VMObject(GObject.Object): # We use a context manager to create the machine object # and make sure it is destroyed when the context is exited @contextmanager - def _create_machine(self) -> Generator[Machine, None, None]: + def _create_machine(self) -> Generator[Machine]: uri = ClanURI.from_str( url=str(self.data.flake.flake_url), machine_name=self.data.flake.flake_attr ) diff --git a/pkgs/clan-vm-manager/clan_vm_manager/views/details.py b/pkgs/clan-vm-manager/clan_vm_manager/views/details.py index c9ec2f93f..df2d8fa17 100644 --- a/pkgs/clan-vm-manager/clan_vm_manager/views/details.py +++ b/pkgs/clan-vm-manager/clan_vm_manager/views/details.py @@ -12,7 +12,7 @@ from gi.repository import Adw, Gio, GObject, Gtk ListItem = TypeVar("ListItem", bound=GObject.Object) -def create_details_list( +def create_details_list[ListItem: GObject.Object]( model: Gio.ListStore, render_row: Callable[[Gtk.ListBox, ListItem], Gtk.Widget] ) -> Gtk.ListBox: boxed_list = Gtk.ListBox() diff --git a/pkgs/clan-vm-manager/clan_vm_manager/views/list.py b/pkgs/clan-vm-manager/clan_vm_manager/views/list.py index b0ff077c5..5f104989b 100644 --- a/pkgs/clan-vm-manager/clan_vm_manager/views/list.py +++ b/pkgs/clan-vm-manager/clan_vm_manager/views/list.py @@ -32,7 +32,7 @@ ListItem = TypeVar("ListItem", bound=GObject.Object) CustomStore = TypeVar("CustomStore", bound=Gio.ListModel) -def create_boxed_list( +def create_boxed_list[CustomStore: Gio.ListModel, ListItem: GObject.Object]( model: CustomStore, render_row: Callable[[Gtk.ListBox, ListItem], Gtk.Widget], ) -> Gtk.ListBox: diff --git a/pkgs/clan-vm-manager/pyproject.toml b/pkgs/clan-vm-manager/pyproject.toml index 6e39b3fec..4de277719 100644 --- a/pkgs/clan-vm-manager/pyproject.toml +++ b/pkgs/clan-vm-manager/pyproject.toml @@ -30,7 +30,7 @@ norecursedirs = "tests/helpers" markers = ["impure"] [tool.mypy] -python_version = "3.12" +python_version = "3.13" warn_redundant_casts = true disallow_untyped_calls = true disallow_untyped_defs = true diff --git a/pkgs/clan-vm-manager/tests/wayland.py b/pkgs/clan-vm-manager/tests/wayland.py index d2515ba86..c9563cd6e 100644 --- a/pkgs/clan-vm-manager/tests/wayland.py +++ b/pkgs/clan-vm-manager/tests/wayland.py @@ -7,7 +7,7 @@ import pytest @pytest.fixture(scope="session") -def wayland_compositor() -> Generator[Popen, None, None]: +def wayland_compositor() -> Generator[Popen]: # Start the Wayland compositor (e.g., Weston) # compositor = Popen(["weston", "--backend=headless-backend.so"]) compositor = Popen(["weston"]) @@ -20,7 +20,7 @@ GtkProc = NewType("GtkProc", Popen) @pytest.fixture -def app() -> Generator[GtkProc, None, None]: +def app() -> Generator[GtkProc]: rapp = Popen([sys.executable, "-m", "clan_vm_manager"], text=True) yield GtkProc(rapp) # Cleanup: Terminate your application diff --git a/pkgs/generate-test-vars/pyproject.toml b/pkgs/generate-test-vars/pyproject.toml index aede95ecb..e3d0472e2 100644 --- a/pkgs/generate-test-vars/pyproject.toml +++ b/pkgs/generate-test-vars/pyproject.toml @@ -26,7 +26,7 @@ addopts = "--durations 5 --color=yes --new-first" # Add --pdb for debugging norecursedirs = "tests/helpers" [tool.mypy] -python_version = "3.12" +python_version = "3.13" warn_redundant_casts = true disallow_untyped_calls = true disallow_untyped_defs = true From e8247533a91b8a479311871bf25dc418cf2c405b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B6rg=20Thalheim?= Date: Fri, 4 Jul 2025 17:50:49 +0200 Subject: [PATCH 4/7] clan-cli: handle None in union types to prevent TypeError Add comprehensive test coverage for union types with None to prevent regression of the issubclass() TypeError that was occurring when checking if None is in a union type. --- pkgs/clan-cli/clan_lib/api/serde.py | 27 ++++++++++++- .../clan_lib/api/serde_deserialize_test.py | 39 +++++++++++++++++++ 2 files changed, 64 insertions(+), 2 deletions(-) diff --git a/pkgs/clan-cli/clan_lib/api/serde.py b/pkgs/clan-cli/clan_lib/api/serde.py index d85700dee..f5940e065 100644 --- a/pkgs/clan-cli/clan_lib/api/serde.py +++ b/pkgs/clan-cli/clan_lib/api/serde.py @@ -146,8 +146,31 @@ def is_union_type(type_hint: type | UnionType) -> bool: def is_type_in_union(union_type: type | UnionType, target_type: type) -> bool: - if get_origin(union_type) is UnionType: - return any(issubclass(arg, target_type) for arg in get_args(union_type)) + # Check for Union from typing module (Union[str, None]) or UnionType (str | None) + if get_origin(union_type) in (Union, UnionType): + args = get_args(union_type) + for arg in args: + # Handle None type specially since it's not a class + if arg is None or arg is type(None): + if target_type is type(None): + return True + # For generic types like dict[str, str], check their origin + elif get_origin(arg) is not None: + if get_origin(arg) == target_type: + return True + # Also check if target_type is a generic with same origin + elif get_origin(target_type) is not None and get_origin( + arg + ) == get_origin(target_type): + return True + # For actual classes, use issubclass + elif inspect.isclass(arg) and inspect.isclass(target_type): + if issubclass(arg, target_type): + return True + # For non-class types, use direct comparison + elif arg == target_type: + return True + return False return union_type == target_type diff --git a/pkgs/clan-cli/clan_lib/api/serde_deserialize_test.py b/pkgs/clan-cli/clan_lib/api/serde_deserialize_test.py index 6b8065154..817e5a86c 100644 --- a/pkgs/clan-cli/clan_lib/api/serde_deserialize_test.py +++ b/pkgs/clan-cli/clan_lib/api/serde_deserialize_test.py @@ -8,6 +8,7 @@ import pytest from clan_lib.api import dataclass_to_dict, from_dict from clan_lib.errors import ClanError from clan_lib.machines import machines +from clan_lib.api.serde import is_type_in_union def test_simple() -> None: @@ -216,6 +217,44 @@ def test_none_or_string() -> None: assert checked3 is None +def test_union_with_none_edge_cases() -> None: + """ + Test various union types with None to ensure issubclass() error is avoided. + This specifically tests the fix for the TypeError in is_type_in_union. + """ + # Test basic types with None + assert from_dict(str | None, None) is None + assert from_dict(str | None, "hello") == "hello" + + # Test dict with None - this was the specific case that failed + assert from_dict(dict[str, str] | None, None) is None + assert from_dict(dict[str, str] | None, {"key": "value"}) == {"key": "value"} + + # Test list with None + assert from_dict(list[str] | None, None) is None + assert from_dict(list[str] | None, ["a", "b"]) == ["a", "b"] + + # Test dataclass with None + @dataclass + class TestClass: + value: str + + assert from_dict(TestClass | None, None) is None + assert from_dict(TestClass | None, {"value": "test"}) == TestClass(value="test") + + # Test Path with None (since it's used in the original failing test) + assert from_dict(Path | None, None) is None + assert from_dict(Path | None, "/home/test") == Path("/home/test") + + # Test that the is_type_in_union function works correctly + # This is the core of what was fixed - ensuring None doesn't cause issubclass error + # These should not raise TypeError anymore + assert is_type_in_union(str | None, type(None)) is True + assert is_type_in_union(dict[str, str] | None, type(None)) is True + assert is_type_in_union(list[str] | None, type(None)) is True + assert is_type_in_union(Path | None, type(None)) is True + + def test_roundtrip_escape() -> None: assert from_dict(str, "\n") == "\n" assert dataclass_to_dict("\n") == "\n" From 3e2459fa408987e336188b608dcf208291773f6f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B6rg=20Thalheim?= Date: Fri, 4 Jul 2025 17:18:13 +0200 Subject: [PATCH 5/7] migrate all projects to python 3.13 linting --- pyproject.toml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index cac6c997d..7c3b24e4b 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,5 +1,5 @@ [tool.mypy] -python_version = "3.12" +python_version = "3.13" pretty = true warn_redundant_casts = true disallow_untyped_calls = true @@ -8,7 +8,7 @@ no_implicit_optional = true exclude = "clan_cli.nixpkgs" [tool.ruff] -target-version = "py311" +target-version = "py313" line-length = 88 lint.select = [ "A", From c5bc1a225db1b66eae95a4c4b0a1f5803ad54b1e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B6rg=20Thalheim?= Date: Fri, 4 Jul 2025 17:46:48 +0200 Subject: [PATCH 6/7] terminate_process_group: also properly yield iterator when we return early --- pkgs/clan-cli/clan_lib/cmd/__init__.py | 1 + 1 file changed, 1 insertion(+) diff --git a/pkgs/clan-cli/clan_lib/cmd/__init__.py b/pkgs/clan-cli/clan_lib/cmd/__init__.py index 49d630d09..d31ec65d1 100644 --- a/pkgs/clan-cli/clan_lib/cmd/__init__.py +++ b/pkgs/clan-cli/clan_lib/cmd/__init__.py @@ -179,6 +179,7 @@ def terminate_process_group(process: subprocess.Popen) -> Iterator[None]: try: process_group = os.getpgid(process.pid) except ProcessLookupError: + yield return if process_group == os.getpgid(os.getpid()): msg = "Bug! Refusing to terminate the current process group" From e383a8ddf489a57a4fe3820b8527cfc58ddbd24f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B6rg=20Thalheim?= Date: Fri, 4 Jul 2025 18:19:47 +0200 Subject: [PATCH 7/7] zerotierone: disable tests on macos --- pkgs/zerotierone/default.nix | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/pkgs/zerotierone/default.nix b/pkgs/zerotierone/default.nix index bca52047f..d90278743 100644 --- a/pkgs/zerotierone/default.nix +++ b/pkgs/zerotierone/default.nix @@ -1,7 +1,14 @@ -{ zerotierone, lib }: +{ + zerotierone, + stdenv, + lib, +}: # halalify zerotierone -zerotierone.overrideAttrs (_old: { - meta = _old.meta // { +zerotierone.overrideAttrs (old: { + # Maybe a sandbox issue? + # zerotierone> [phy] Binding UDP listen socket to 127.0.0.1/60002... FAILED. + doCheck = old.doCheck && !stdenv.hostPlatform.isDarwin; + meta = old.meta // { license = lib.licenses.apsl20; }; })