diff --git a/pkgs/clan-cli/.envrc b/pkgs/clan-cli/.envrc index 1d953f4bd..4feb0e837 100644 --- a/pkgs/clan-cli/.envrc +++ b/pkgs/clan-cli/.envrc @@ -1 +1,2 @@ -use nix +# Because we depend on nixpkgs sources, uploading to builders takes a long time +use flake .#clan --builders '' diff --git a/pkgs/clan-cli/clan_cli/nix.py b/pkgs/clan-cli/clan_cli/nix.py index b5515676d..f8d3c89a4 100644 --- a/pkgs/clan-cli/clan_cli/nix.py +++ b/pkgs/clan-cli/clan_cli/nix.py @@ -1,10 +1,9 @@ import os -CLAN_NIXPKGS = os.environ.get("CLAN_NIXPKGS") - def nix_shell(packages: list[str], cmd: list[str]) -> list[str]: + nixpkgs = os.environ.get("CLAN_NIXPKGS") # in unittest we will have all binaries provided - if CLAN_NIXPKGS is None: + if nixpkgs is None: return cmd - return ["nix", "shell", "-f", CLAN_NIXPKGS] + packages + ["-c"] + cmd + return ["nix", "shell", "-f", nixpkgs] + packages + ["-c"] + cmd diff --git a/pkgs/clan-cli/clan_cli/ssh.py b/pkgs/clan-cli/clan_cli/ssh.py index b07c4d093..0eac6810b 100644 --- a/pkgs/clan-cli/clan_cli/ssh.py +++ b/pkgs/clan-cli/clan_cli/ssh.py @@ -3,6 +3,8 @@ import json import subprocess from typing import Optional +from .nix import nix_shell + def ssh( host: str, @@ -10,15 +12,10 @@ def ssh( password: Optional[str] = None, ssh_args: list[str] = [], ) -> None: - nix_shell_args = [] + packages = ["tor", "openssh"] password_args = [] if password: - nix_shell_args = [ - "nix", - "shell", - "nixpkgs#sshpass", - "-c", - ] + packages.append("sshpass") password_args = [ "sshpass", "-p", @@ -32,23 +29,22 @@ def ssh( "StrictHostKeyChecking=no", f"{user}@{host}", ] - cmd = nix_shell_args + ["torify"] + password_args + _ssh_args + cmd = nix_shell(packages, ["torify"] + password_args + _ssh_args) subprocess.run(cmd) def qrcode_scan(pictureFile: str) -> str: return ( subprocess.run( - [ - "nix", - "shell", - "nixpkgs#zbar", - "-c", - "zbarimg", - "--quiet", - "--raw", - pictureFile, - ], + nix_shell( + ["zbar"], + [ + "zbarimg", + "--quiet", + "--raw", + pictureFile, + ], + ), stdout=subprocess.PIPE, check=True, ) diff --git a/pkgs/clan-cli/default.nix b/pkgs/clan-cli/default.nix index 916f5b898..0eeb62d43 100644 --- a/pkgs/clan-cli/default.nix +++ b/pkgs/clan-cli/default.nix @@ -1,11 +1,11 @@ -{ pkgs ? import { } -, lib ? pkgs.lib -, python3 ? pkgs.python3 -, ruff ? pkgs.ruff -, runCommand ? pkgs.runCommand -, installShellFiles ? pkgs.installShellFiles -, zerotierone ? pkgs.zerotierone -, bubblewrap ? pkgs.bubblewrap +{ pkgs +, lib +, python3 +, ruff +, runCommand +, installShellFiles +, zerotierone +, bubblewrap }: let pyproject = builtins.fromTOML (builtins.readFile ./pyproject.toml); diff --git a/pkgs/clan-cli/flake-module.nix b/pkgs/clan-cli/flake-module.nix index b8df40737..3af30b89b 100644 --- a/pkgs/clan-cli/flake-module.nix +++ b/pkgs/clan-cli/flake-module.nix @@ -4,9 +4,11 @@ pyproject = builtins.fromTOML (builtins.readFile ./pyproject.toml); name = pyproject.project.name; package = pkgs.callPackage ./default.nix { }; + shell = pkgs.callPackage ./shell.nix { }; in { packages.${name} = package; + devShells.${name} = shell; packages.default = package; checks = package.tests; }; diff --git a/pkgs/clan-cli/shell.nix b/pkgs/clan-cli/shell.nix index fa99766fd..99665fb2a 100644 --- a/pkgs/clan-cli/shell.nix +++ b/pkgs/clan-cli/shell.nix @@ -1,13 +1,7 @@ -{ pkgs ? import { } -, -}: +{ pkgs }: let - lib = pkgs.lib; - python3 = pkgs.python3; - package = import ./default.nix { - inherit lib python3; - }; - pythonWithDeps = python3.withPackages ( + package = pkgs.callPackage ./default.nix { }; + pythonWithDeps = pkgs.python3.withPackages ( ps: package.propagatedBuildInputs ++ package.devDependencies @@ -18,39 +12,38 @@ let checkScript = pkgs.writeScriptBin "check" '' nix build -f . tests -L "$@" ''; - devShell = pkgs.mkShell { - packages = [ - pkgs.ruff - pythonWithDeps - ]; - # sets up an editable install and add enty points to $PATH - CLAN_NIXPKGS = pkgs.path; - shellHook = '' - tmp_path=$(realpath ./.pythonenv) - repo_root=$(realpath .) - rm -rf $tmp_path - mkdir -p "$tmp_path/${pythonWithDeps.sitePackages}" - - ${pythonWithDeps.interpreter} -m pip install \ - --quiet \ - --disable-pip-version-check \ - --no-index \ - --no-build-isolation \ - --prefix "$tmp_path" \ - --editable $repo_root - - export PATH="$tmp_path/bin:${checkScript}/bin:$PATH" - export PYTHONPATH="$repo_root:$tmp_path/${pythonWithDeps.sitePackages}" - - export XDG_DATA_DIRS="$tmp_path/share''${XDG_DATA_DIRS:+:$XDG_DATA_DIRS}" - export fish_complete_path="$tmp_path/share/fish/vendor_completions.d''${fish_complete_path:+:$fish_complete_path}" - mkdir -p \ - $tmp_path/share/fish/vendor_completions.d \ - $tmp_path/share/bash-completion/completions \ - $tmp_path/share/zsh/site-functions - register-python-argcomplete --shell fish clan > $tmp_path/share/fish/vendor_completions.d/clan.fish - register-python-argcomplete --shell bash clan > $tmp_path/share/bash-completion/completions/clan - ''; - }; in -devShell +pkgs.mkShell { + packages = [ + pkgs.ruff + pythonWithDeps + ]; + # sets up an editable install and add enty points to $PATH + CLAN_NIXPKGS = pkgs.path; + shellHook = '' + tmp_path=$(realpath ./.pythonenv) + repo_root=$(realpath .) + rm -rf $tmp_path + mkdir -p "$tmp_path/${pythonWithDeps.sitePackages}" + + ${pythonWithDeps.interpreter} -m pip install \ + --quiet \ + --disable-pip-version-check \ + --no-index \ + --no-build-isolation \ + --prefix "$tmp_path" \ + --editable $repo_root + + export PATH="$tmp_path/bin:${checkScript}/bin:$PATH" + export PYTHONPATH="$repo_root:$tmp_path/${pythonWithDeps.sitePackages}" + + export XDG_DATA_DIRS="$tmp_path/share''${XDG_DATA_DIRS:+:$XDG_DATA_DIRS}" + export fish_complete_path="$tmp_path/share/fish/vendor_completions.d''${fish_complete_path:+:$fish_complete_path}" + mkdir -p \ + $tmp_path/share/fish/vendor_completions.d \ + $tmp_path/share/bash-completion/completions \ + $tmp_path/share/zsh/site-functions + register-python-argcomplete --shell fish clan > $tmp_path/share/fish/vendor_completions.d/clan.fish + register-python-argcomplete --shell bash clan > $tmp_path/share/bash-completion/completions/clan + ''; +} diff --git a/pkgs/clan-cli/tests/test_clan_ssh.py b/pkgs/clan-cli/tests/test_clan_ssh.py index 77dcc4e91..de42ca119 100644 --- a/pkgs/clan-cli/tests/test_clan_ssh.py +++ b/pkgs/clan-cli/tests/test_clan_ssh.py @@ -1,5 +1,7 @@ +import os import sys -from typing import Union +from contextlib import contextmanager +from typing import Iterator, Union import pytest import pytest_subprocess.fake_process @@ -18,45 +20,72 @@ def test_no_args( assert captured.err.startswith("usage:") +@contextmanager +def mock_env(**environ: str) -> Iterator[None]: + original_environ = dict(os.environ) + os.environ.update(environ) + try: + yield + finally: + os.environ.clear() + os.environ.update(original_environ) + + # using fp fixture from pytest-subprocess def test_ssh_no_pass(fp: pytest_subprocess.fake_process.FakeProcess) -> None: - host = "somehost" - user = "user" - cmd: list[Union[str, utils.Any]] = [ - "torify", - "ssh", - "-o", - "UserKnownHostsFile=/dev/null", - "-o", - "StrictHostKeyChecking=no", - f"{user}@{host}", - fp.any(), - ] - fp.register(cmd) - clan_cli.ssh.ssh( - host=host, - user=user, - ) - assert fp.call_count(cmd) == 1 + with mock_env(CLAN_NIXPKGS="/mocked-nixpkgs"): + host = "somehost" + user = "user" + cmd: list[Union[str, utils.Any]] = [ + "nix", + "shell", + "-f", + "/mocked-nixpkgs", + "tor", + "openssh", + "-c", + "torify", + "ssh", + "-o", + "UserKnownHostsFile=/dev/null", + "-o", + "StrictHostKeyChecking=no", + f"{user}@{host}", + fp.any(), + ] + fp.register(cmd) + clan_cli.ssh.ssh( + host=host, + user=user, + ) + assert fp.call_count(cmd) == 1 def test_ssh_with_pass(fp: pytest_subprocess.fake_process.FakeProcess) -> None: - host = "somehost" - user = "user" - cmd: list[Union[str, utils.Any]] = [ - "nix", - "shell", - "nixpkgs#sshpass", - "-c", - fp.any(), - ] - fp.register(cmd) - clan_cli.ssh.ssh( - host=host, - user=user, - password="XXX", - ) - assert fp.call_count(cmd) == 1 + with mock_env(CLAN_NIXPKGS="/mocked-nixpkgs"): + host = "somehost" + user = "user" + cmd: list[Union[str, utils.Any]] = [ + "nix", + "shell", + "-f", + "/mocked-nixpkgs", + "tor", + "openssh", + "sshpass", + "-c", + "torify", + "sshpass", + "-p", + fp.any(), + ] + fp.register(cmd) + clan_cli.ssh.ssh( + host=host, + user=user, + password="XXX", + ) + assert fp.call_count(cmd) == 1 def test_qrcode_scan(fp: pytest_subprocess.fake_process.FakeProcess) -> None: