diff --git a/devShell-python.nix b/devShell-python.nix deleted file mode 100644 index 066c4473..00000000 --- a/devShell-python.nix +++ /dev/null @@ -1,115 +0,0 @@ -{ - perSystem = - { - pkgs, - self', - lib, - ... - }: - let - python3 = pkgs.python3; - pypkgs = python3.pkgs; - clan-cli = self'.packages.clan-cli; - clan-vm-manager = self'.packages.clan-vm-manager; - pythonWithDeps = python3.withPackages ( - ps: - clan-cli.propagatedBuildInputs - ++ clan-cli.devDependencies - ++ [ ps.pip ] - ++ [ clan-vm-manager.externalPythonDeps ] - # clan-vm-manager deps - ); - linuxOnlyPackages = lib.optionals pkgs.stdenv.isLinux [ pkgs.xdg-utils ]; - in - { - devShells.python = pkgs.mkShell { - inputsFrom = [ self'.devShells.default ]; - packages = - [ - pythonWithDeps - pypkgs.mypy - pypkgs.ipdb - pkgs.desktop-file-utils - pkgs.gtk4.dev - pkgs.ruff - pkgs.libadwaita.devdoc # has the demo called 'adwaita-1-demo' - ] - ++ linuxOnlyPackages - ++ clan-vm-manager.nativeBuildInputs - ++ clan-vm-manager.buildInputs - ++ clan-cli.nativeBuildInputs; - - PYTHONBREAKPOINT = "ipdb.set_trace"; - - shellHook = '' - ln -sfT ${clan-cli.nixpkgs} ./pkgs/clan-cli/clan_cli/nixpkgs - - ## PYTHON - - tmp_path="$(realpath ./.direnv/python)" - repo_root=$(realpath .) - mkdir -p "$tmp_path/${pythonWithDeps.sitePackages}" - - # local dependencies - localPackages=( - $repo_root/pkgs/clan-cli - $repo_root/pkgs/clan-vm-manager - ) - - # Install executable wrappers for local python packages scripts - # This is done by utilizing `pip install --editable` - # As a result, executables like `clan` can be executed from within the dev-shell - # while using the current version of the code and its dependencies. - for package in "''${localPackages[@]}"; do - pname=$(basename "$package") - if - [ ! -e "$tmp_path/meta/$pname/pyproject.toml" ] \ - || [ ! -e "$package/pyproject.toml" ] \ - || ! cmp -s "$tmp_path/meta/$pname/pyproject.toml" "$package/pyproject.toml" - then - echo "==== Installing local python package $pname in editable mode ====" - mkdir -p "$tmp_path/meta/$pname" - cp $package/pyproject.toml $tmp_path/meta/$pname/pyproject.toml - ${python3.pkgs.pip}/bin/pip install \ - --quiet \ - --disable-pip-version-check \ - --no-index \ - --no-build-isolation \ - --prefix "$tmp_path" \ - --editable "$package" - fi - done - - export PATH="$tmp_path/bin:$PATH" - export PYTHONPATH="''${PYTHONPATH:+$PYTHONPATH:}$tmp_path/${pythonWithDeps.sitePackages}" - - for package in "''${localPackages[@]}"; do - export PYTHONPATH="$package:$PYTHONPATH" - done - - - - ## GUI - - if ! command -v xdg-mime &> /dev/null; then - echo "Warning: 'xdg-mime' is not available. The desktop file cannot be installed." - fi - - # install desktop file - set -eou pipefail - DESKTOP_FILE_NAME=org.clan.vm-manager.desktop - DESKTOP_DST=~/.local/share/applications/$DESKTOP_FILE_NAME - DESKTOP_SRC=${clan-vm-manager.desktop-file}/share/applications/$DESKTOP_FILE_NAME - UI_BIN="clan-vm-manager" - - cp -f $DESKTOP_SRC $DESKTOP_DST - sed -i "s|Exec=.*clan-vm-manager|Exec=$UI_BIN|" $DESKTOP_DST - xdg-mime default $DESKTOP_FILE_NAME x-scheme-handler/clan - echo "==== Validating desktop file installation ====" - set -x - desktop-file-validate $DESKTOP_DST - set +xeou pipefail - ''; - }; - }; -} diff --git a/flake.nix b/flake.nix index eb482c23..1f03f9cc 100644 --- a/flake.nix +++ b/flake.nix @@ -35,7 +35,6 @@ imports = [ ./checks/flake-module.nix ./devShell.nix - ./devShell-python.nix ./formatter.nix ./templates/flake-module.nix ./clanModules/flake-module.nix diff --git a/formatter.nix b/formatter.nix index 4aeacde3..ac96744c 100644 --- a/formatter.nix +++ b/formatter.nix @@ -9,9 +9,8 @@ treefmt.programs.mypy.enable = true; treefmt.programs.mypy.directories = { - "pkgs/clan-cli".extraPythonPackages = self'.packages.clan-cli.pytestDependencies; - "pkgs/clan-vm-manager".extraPythonPackages = - self'.packages.clan-vm-manager.externalPythonDeps ++ self'.packages.clan-cli.pytestDependencies; + "pkgs/clan-cli".extraPythonPackages = self'.packages.clan-cli.testDependencies; + "pkgs/clan-vm-manager".extraPythonPackages = self'.packages.clan-vm-manager.testDependencies; }; treefmt.settings.formatter.nix = { diff --git a/pkgs/clan-cli/.envrc b/pkgs/clan-cli/.envrc index 79a6a2d4..179c25fd 100644 --- a/pkgs/clan-cli/.envrc +++ b/pkgs/clan-cli/.envrc @@ -1,4 +1,5 @@ -source_up +# BUG: If this is enabled the devshell depends on clan_cli building successfully +# source_up watch_file flake-module.nix default.nix diff --git a/pkgs/clan-cli/clan_cli/machines/machines.py b/pkgs/clan-cli/clan_cli/machines/machines.py index 72e4b9f9..49393a25 100644 --- a/pkgs/clan-cli/clan_cli/machines/machines.py +++ b/pkgs/clan-cli/clan_cli/machines/machines.py @@ -8,7 +8,7 @@ from typing import Any from clan_cli.clan_uri import ClanURI, MachineData from clan_cli.dirs import vm_state_dir -from qemu.qmp import QEMUMonitorProtocol +from clan_cli.qemu.qmp import QEMUMonitorProtocol from ..cmd import run from ..errors import ClanError diff --git a/pkgs/clan-cli/qemu/__init__.py b/pkgs/clan-cli/clan_cli/qemu/__init__.py similarity index 100% rename from pkgs/clan-cli/qemu/__init__.py rename to pkgs/clan-cli/clan_cli/qemu/__init__.py diff --git a/pkgs/clan-cli/qemu/qga.py b/pkgs/clan-cli/clan_cli/qemu/qga.py similarity index 100% rename from pkgs/clan-cli/qemu/qga.py rename to pkgs/clan-cli/clan_cli/qemu/qga.py diff --git a/pkgs/clan-cli/qemu/qmp.py b/pkgs/clan-cli/clan_cli/qemu/qmp.py similarity index 100% rename from pkgs/clan-cli/qemu/qmp.py rename to pkgs/clan-cli/clan_cli/qemu/qmp.py diff --git a/pkgs/clan-cli/default.nix b/pkgs/clan-cli/default.nix index fa5252f1..5fd929d0 100644 --- a/pkgs/clan-cli/default.nix +++ b/pkgs/clan-cli/default.nix @@ -1,4 +1,5 @@ { + # Inputs for the package age, lib, argcomplete, @@ -10,14 +11,11 @@ pytest-xdist, pytest-subprocess, pytest-timeout, - remote-pdb, - ipdb, python3, runCommand, setuptools, sops, stdenv, - wheel, fakeroot, rsync, bash, @@ -30,33 +28,15 @@ gnupg, e2fsprogs, mypy, - rope, clan-core-path, }: let - - dependencies = [ - argcomplete # optional dependency: if not enabled, shell completion will not work + # Dependencies that are directly used in the project + pythonDependencies = [ + argcomplete # Enables shell completion; without it, this feature won't work. ]; - pytestDependencies = - runtimeDependencies - ++ dependencies - ++ [ - pytest - pytest-cov - pytest-subprocess - pytest-xdist - pytest-timeout - remote-pdb - ipdb - openssh - git - gnupg - stdenv.cc - ]; - - # Optional dependencies for clan cli, we re-expose them here to make sure they all build. + # Runtime dependencies required by the application runtimeDependencies = [ bash nix @@ -74,14 +54,31 @@ let e2fsprogs ]; + # Dependencies required for running tests + testDependencies = + runtimeDependencies + ++ [ + gnupg + stdenv.cc # Compiler used for certain native extensions + ] + ++ pythonDependencies + ++ [ + pytest # Testing framework + pytest-cov # Generate coverage reports + pytest-subprocess # fake the real subprocess behavior to make your tests more independent. + pytest-xdist # Run tests in parallel on multiple cores + pytest-timeout # Add timeouts to your tests + ]; + + # Convert runtimeDependencies into an attribute set for easier access runtimeDependenciesAsSet = builtins.listToAttrs ( builtins.map (p: lib.nameValuePair (lib.getName p.name) p) runtimeDependencies ); - checkPython = python3.withPackages (_ps: pytestDependencies); + # Setup Python environment with all dependencies for running tests + pythonWithTestDeps = python3.withPackages (_ps: testDependencies); - # - vendor the jsonschema nix lib (copy instead of symlink). - # Interesting fact: using nixpkgs from flakes instead of nixpkgs.path is reduces evaluation time by 5s. + # Prepare the source code for the project, including copying over jsonschema and nixpkgs source = runCommand "clan-cli-source" { } '' cp -r ${./.} $out chmod -R +w $out @@ -89,6 +86,8 @@ let ln -s ${nixpkgs'} $out/clan_cli/nixpkgs cp -r ${../../lib/jsonschema} $out/clan_cli/config/jsonschema ''; + + # Create a custom nixpkgs for use within the project nixpkgs' = runCommand "nixpkgs" { nativeBuildInputs = [ nix ]; } '' mkdir $out cat > $out/flake.nix << EOF @@ -114,36 +113,36 @@ python3.pkgs.buildPythonApplication { src = source; format = "pyproject"; - makeWrapperArgs = [ - # This prevents problems with mixed glibc versions that might occur when the - # cli is called through a browser built against another glibc - "--unset LD_LIBRARY_PATH" - ]; + # Arguments for the wrapper to unset LD_LIBRARY_PATH to avoid glibc version issues + makeWrapperArgs = [ "--unset LD_LIBRARY_PATH" ]; + # Build-time dependencies. nativeBuildInputs = [ setuptools installShellFiles ]; - propagatedBuildInputs = dependencies; - # also re-expose dependencies so we test them in CI + propagatedBuildInputs = pythonDependencies; + + # Define and expose the tests and checks to run in CI passthru.tests = (lib.mapAttrs' (n: lib.nameValuePair "clan-dep-${n}") runtimeDependenciesAsSet) // rec { clan-pytest-without-core = - runCommand "clan-pytest-without-core" { nativeBuildInputs = [ checkPython ] ++ pytestDependencies; } + runCommand "clan-pytest-without-core" + { nativeBuildInputs = [ pythonWithTestDeps ] ++ testDependencies; } '' cp -r ${source} ./src chmod +w -R ./src cd ./src export NIX_STATE_DIR=$TMPDIR/nix IN_NIX_SANDBOX=1 - ${checkPython}/bin/python -m pytest -m "not impure and not with_core" ./tests + ${pythonWithTestDeps}/bin/python -m pytest -m "not impure and not with_core" ./tests touch $out ''; - # separate the tests that can never be cached clan-pytest-with-core = - runCommand "clan-pytest-with-core" { nativeBuildInputs = [ checkPython ] ++ pytestDependencies; } + runCommand "clan-pytest-with-core" + { nativeBuildInputs = [ pythonWithTestDeps ] ++ testDependencies; } '' cp -r ${source} ./src chmod +w -R ./src @@ -151,15 +150,11 @@ python3.pkgs.buildPythonApplication { export CLAN_CORE=${clan-core-path} export NIX_STATE_DIR=$TMPDIR/nix IN_NIX_SANDBOX=1 - ${checkPython}/bin/python -m pytest -m "not impure and with_core" ./tests + ${pythonWithTestDeps}/bin/python -m pytest -m "not impure and with_core" ./tests touch $out ''; - clan-pytest = runCommand "clan-pytest" { } '' - echo ${clan-pytest-without-core} - echo ${clan-pytest-with-core} - touch $out - ''; + # Utility to check for leftover debugging breakpoints in the codebase check-for-breakpoints = runCommand "breakpoints" { } '' if grep --include \*.py -Rq "breakpoint()" ${source}; then echo "breakpoint() found in ${source}:" @@ -170,18 +165,13 @@ python3.pkgs.buildPythonApplication { ''; }; + # Additional pass-through attributes passthru.nixpkgs = nixpkgs'; - passthru.checkPython = checkPython; - - passthru.devDependencies = [ - rope - setuptools - wheel - ] ++ pytestDependencies; - - passthru.pytestDependencies = pytestDependencies; + passthru.testDependencies = testDependencies; + passthru.pythonWithTestDeps = pythonWithTestDeps; passthru.runtimeDependencies = runtimeDependencies; + # Install shell completions for bash and fish using the argcomplete package postInstall = '' cp -r ${nixpkgs'} $out/${python3.sitePackages}/clan_cli/nixpkgs installShellCompletion --bash --name clan \ @@ -189,13 +179,17 @@ python3.pkgs.buildPythonApplication { installShellCompletion --fish --name clan.fish \ <(${argcomplete}/bin/register-python-argcomplete --shell fish clan) ''; - # Don't leak python packages into a devshell. - # It can be very confusing if you `nix run` then load the cli from the devshell instead. + + # Clean up after the package to avoid leaking python packages into a devshell postFixup = '' rm $out/nix-support/propagated-build-inputs ''; + + # Run a basic check to ensure the application is executable checkPhase = '' PYTHONPATH= $out/bin/clan --help ''; + + # Specify the main program for this package meta.mainProgram = "clan"; } diff --git a/pkgs/clan-cli/shell.nix b/pkgs/clan-cli/shell.nix index e239355c..f0ca91b5 100644 --- a/pkgs/clan-cli/shell.nix +++ b/pkgs/clan-cli/shell.nix @@ -1,59 +1,29 @@ { nix-unit, clan-cli, - system, mkShell, - writeScriptBin, - openssh, ruff, python3, }: let - checkScript = writeScriptBin "check" '' - nix build .#checks.${system}.{treefmt,clan-pytest} -L "$@" - ''; - - pythonWithDeps = python3.withPackages ( - ps: clan-cli.propagatedBuildInputs ++ clan-cli.devDependencies ++ [ ps.pip ] - ); + devshellTestDeps = + clan-cli.passthru.testDependencies + ++ (with python3.pkgs; [ + rope + setuptools + wheel + pip + ]); in mkShell { - packages = [ + buildInputs = [ nix-unit - openssh ruff - clan-cli.checkPython - ]; + ] ++ devshellTestDeps; shellHook = '' - tmp_path=$(realpath ./.direnv) - - repo_root=$(realpath .) - mkdir -p "$tmp_path/python/${pythonWithDeps.sitePackages}" - - # Install the package in editable mode - # This allows executing `clan` from within the dev-shell using the current - # version of the code and its dependencies. - ${pythonWithDeps.interpreter} -m pip install \ - --quiet \ - --disable-pip-version-check \ - --no-index \ - --no-build-isolation \ - --prefix "$tmp_path/python" \ - --editable $repo_root + export PATH=$(pwd)/bin:$PATH ln -sfT ${clan-cli.nixpkgs} clan_cli/nixpkgs - - export PATH="$tmp_path/python/bin:${checkScript}/bin:$PATH" - export PYTHONPATH="$repo_root:$tmp_path/python/${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/conftest.py b/pkgs/clan-cli/tests/conftest.py index da275fe0..5ec8735f 100644 --- a/pkgs/clan-cli/tests/conftest.py +++ b/pkgs/clan-cli/tests/conftest.py @@ -5,11 +5,13 @@ from pathlib import Path import pytest +sys.path.append(os.path.join(os.path.dirname(__file__), "helpers")) +sys.path.append(str(Path(__file__).parent.parent)) # Also add clan_cli to PYTHONPATH + + from clan_cli.custom_logger import setup_logging from clan_cli.nix import nix_shell -sys.path.append(os.path.join(os.path.dirname(__file__), "helpers")) - pytest_plugins = [ "temporary_dir", "root", diff --git a/pkgs/clan-cli/tests/test_vms_cli.py b/pkgs/clan-cli/tests/test_vms_cli.py index 0110cf97..47dce1c4 100644 --- a/pkgs/clan-cli/tests/test_vms_cli.py +++ b/pkgs/clan-cli/tests/test_vms_cli.py @@ -12,8 +12,8 @@ from fixtures_flakes import FlakeForTest, generate_flake from root import CLAN_CORE from clan_cli.dirs import vm_state_dir -from qemu.qga import QgaSession -from qemu.qmp import QEMUMonitorProtocol +from clan_cli.qemu.qga import QgaSession +from clan_cli.qemu.qmp import QEMUMonitorProtocol if TYPE_CHECKING: from age_keys import KeyPair diff --git a/pkgs/clan-vm-manager/.envrc b/pkgs/clan-vm-manager/.envrc index 76d5ff72..3a9c84ad 100644 --- a/pkgs/clan-vm-manager/.envrc +++ b/pkgs/clan-vm-manager/.envrc @@ -1,4 +1,5 @@ -source_up +# See comment in clan-cli/.envrc +# source_up watch_file flake-module.nix default.nix diff --git a/pkgs/clan-vm-manager/clan_vm_manager/__main__.py b/pkgs/clan-vm-manager/clan_vm_manager/__main__.py new file mode 100644 index 00000000..daf509ab --- /dev/null +++ b/pkgs/clan-vm-manager/clan_vm_manager/__main__.py @@ -0,0 +1,6 @@ +import sys + +from . import main + +if __name__ == "__main__": + sys.exit(main()) diff --git a/pkgs/clan-vm-manager/default.nix b/pkgs/clan-vm-manager/default.nix index 521c6276..32ec5232 100644 --- a/pkgs/clan-vm-manager/default.nix +++ b/pkgs/clan-vm-manager/default.nix @@ -12,7 +12,11 @@ clan-cli, makeDesktopItem, libadwaita, - weston, + pytest, # Testing framework + pytest-cov, # Generate coverage reports + pytest-subprocess, # fake the real subprocess behavior to make your tests more independent. + pytest-xdist, # Run tests in parallel on multiple cores + pytest-timeout, # Add timeouts to your tests }: let source = ./.; @@ -24,6 +28,36 @@ let startupWMClass = "clan"; mimeTypes = [ "x-scheme-handler/clan" ]; }; + + # Dependencies that are directly used in the project but nor from internal python packages + externalPythonDeps = [ + pygobject3 + pygobject-stubs + gtk4 + libadwaita + gnome.adwaita-icon-theme + ]; + + # Deps including python packages from the local project + allPythonDeps = [ (python3.pkgs.toPythonModule clan-cli) ] ++ externalPythonDeps; + + # Runtime binary dependencies required by the application + runtimeDependencies = [ ]; + + # Dependencies required for running tests + testDependencies = + runtimeDependencies + ++ allPythonDeps + ++ [ + pytest # Testing framework + pytest-cov # Generate coverage reports + pytest-subprocess # fake the real subprocess behavior to make your tests more independent. + pytest-xdist # Run tests in parallel on multiple cores + pytest-timeout # Add timeouts to your tests + ]; + + # Setup Python environment with all dependencies for running tests + pythonWithTestDeps = python3.withPackages (_ps: testDependencies); in python3.pkgs.buildPythonApplication rec { name = "clan-vm-manager"; @@ -36,6 +70,7 @@ python3.pkgs.buildPythonApplication rec { "--unset LD_LIBRARY_PATH" ]; + # Deps needed only at build time nativeBuildInputs = [ setuptools copyDesktopItems @@ -43,50 +78,28 @@ python3.pkgs.buildPythonApplication rec { gobject-introspection ]; - buildInputs = [ - gtk4 - libadwaita - gnome.adwaita-icon-theme - ]; - - # We need to propagate the build inputs to nix fmt / treefmt - propagatedBuildInputs = [ - (python3.pkgs.toPythonModule clan-cli) - passthru.externalPythonDeps - ]; - - checkPython = python3.withPackages (_ps: clan-cli.passthru.pytestDependencies); - - devDependencies = [ - checkPython - weston - ] ++ nativeBuildInputs ++ buildInputs ++ propagatedBuildInputs; - - passthru.checkPython = checkPython; - passthru.devDependencies = devDependencies; + # The necessity of setting buildInputs and propagatedBuildInputs to the + # same values for your Python package within Nix largely stems from ensuring + # that all necessary dependencies are consistently available both + # at build time and runtime, + buildInputs = allPythonDeps ++ runtimeDependencies; + propagatedBuildInputs = allPythonDeps ++ runtimeDependencies; # also re-expose dependencies so we test them in CI passthru = { - inherit desktop-file; - # Keep external dependencies in a separate lists to refer to thm elsewhere - # This helps avoiding issues like dev-shells accidentally depending on - # nix derivations of local packages. - externalPythonDeps = [ - pygobject3 - pygobject-stubs - ]; tests = { clan-vm-manager-pytest = - runCommand "clan-vm-manager-pytest" { nativeBuildInputs = devDependencies; } + runCommand "clan-vm-manager-pytest" { inherit buildInputs propagatedBuildInputs nativeBuildInputs; } '' cp -r ${source} ./src chmod +w -R ./src cd ./src export NIX_STATE_DIR=$TMPDIR/nix IN_NIX_SANDBOX=1 - ${checkPython}/bin/python -m pytest -m "not impure" ./tests + ${pythonWithTestDeps}/bin/python -m pytest -s -m "not impure" ./tests touch $out ''; + clan-vm-manager-no-breakpoints = runCommand "clan-vm-manager-no-breakpoints" { } '' if grep --include \*.py -Rq "breakpoint()" ${source}; then echo "breakpoint() found in ${source}:" @@ -98,6 +111,12 @@ python3.pkgs.buildPythonApplication rec { }; }; + # Additional pass-through attributes + passthru.desktop-file = desktop-file; + passthru.externalPythonDeps = externalPythonDeps; + passthru.testDependencies = testDependencies; + passthru.runtimeDependencies = runtimeDependencies; + # Don't leak python packages into a devshell. # It can be very confusing if you `nix run` than load the cli from the devshell instead. postFixup = '' diff --git a/pkgs/clan-vm-manager/flake-module.nix b/pkgs/clan-vm-manager/flake-module.nix index 06de8e23..fe3d4548 100644 --- a/pkgs/clan-vm-manager/flake-module.nix +++ b/pkgs/clan-vm-manager/flake-module.nix @@ -4,7 +4,7 @@ { config, pkgs, ... }: { devShells.clan-vm-manager = pkgs.callPackage ./shell.nix { - inherit (config.packages) clan-cli clan-vm-manager; + inherit (config.packages) clan-vm-manager; }; packages.clan-vm-manager = pkgs.python3.pkgs.callPackage ./default.nix { inherit (config.packages) clan-cli; diff --git a/pkgs/clan-vm-manager/pyproject.toml b/pkgs/clan-vm-manager/pyproject.toml index bdacae4c..aea2ba47 100644 --- a/pkgs/clan-vm-manager/pyproject.toml +++ b/pkgs/clan-vm-manager/pyproject.toml @@ -30,10 +30,6 @@ disallow_untyped_calls = true disallow_untyped_defs = true no_implicit_optional = true -[[tool.mypy.overrides]] -module = "clan_cli.*" -ignore_missing_imports = true - [tool.ruff] target-version = "py311" line-length = 88 diff --git a/pkgs/clan-vm-manager/shell.nix b/pkgs/clan-vm-manager/shell.nix index 6d1d8565..48b8d10a 100644 --- a/pkgs/clan-vm-manager/shell.nix +++ b/pkgs/clan-vm-manager/shell.nix @@ -1,84 +1,46 @@ { lib, - runCommand, - makeWrapper, stdenv, clan-vm-manager, - gdb, - gtk4, - libadwaita, - clan-cli, mkShell, ruff, desktop-file-utils, xdg-utils, mypy, python3, - python3Packages, + gtk4, + libadwaita, }: -mkShell ( - let - pygdb = - runCommand "pygdb" - { - buildInputs = [ - gdb - python3 - makeWrapper - ]; - } - '' - mkdir -p "$out/bin" - makeWrapper "${gdb}/bin/gdb" "$out/bin/pygdb" \ - --add-flags '-ex "source ${python3}/share/gdb/libpython.py"' - ''; - in - rec { - inherit (clan-vm-manager) propagatedBuildInputs buildInputs; - linuxOnlyPackages = lib.optionals stdenv.isLinux [ - xdg-utils - pygdb - ]; - - # To debug clan-vm-manger execute pygdb --args python ./bin/clan-vm-manager - packages = [ - ruff - desktop-file-utils +let + devshellTestDeps = + clan-vm-manager.testDependencies + ++ (with python3.pkgs; [ + rope mypy - python3Packages.ipdb - gtk4.dev + ipdb + setuptools + wheel + pip + ]); +in +mkShell { + inherit (clan-vm-manager) nativeBuildInputs; + buildInputs = + [ + ruff + gtk4.dev # has the demo called 'gtk4-widget-factory' libadwaita.devdoc # has the demo called 'adwaita-1-demo' - ] ++ clan-vm-manager.devDependencies ++ linuxOnlyPackages; + ] + ++ devshellTestDeps - PYTHONBREAKPOINT = "ipdb.set_trace"; + # Dependencies for testing for linux hosts + ++ (lib.optionals stdenv.isLinux [ + xdg-utils # install desktop files + desktop-file-utils # verify desktop files + ]); - shellHook = '' - ln -sfT ${clan-cli.nixpkgs} ../clan-cli/clan_cli/nixpkgs - - # prepend clan-cli for development - export PYTHONPATH=../clan-cli:$PYTHONPATH - - - if ! command -v xdg-mime &> /dev/null; then - echo "Warning: 'xdg-mime' is not available. The desktop file cannot be installed." - fi - - # install desktop file - set -eou pipefail - DESKTOP_FILE_NAME=org.clan.vm-manager.desktop - DESKTOP_DST=~/.local/share/applications/$DESKTOP_FILE_NAME - DESKTOP_SRC=${clan-vm-manager}/share/applications/$DESKTOP_FILE_NAME - UI_BIN="${clan-vm-manager}/bin/clan-vm-manager" - - cp -f $DESKTOP_SRC $DESKTOP_DST - sleep 2 - sed -i "s|Exec=.*clan-vm-manager|Exec=$UI_BIN|" $DESKTOP_DST - xdg-mime default $DESKTOP_FILE_NAME x-scheme-handler/clan - echo "==== Validating desktop file installation ====" - set -x - desktop-file-validate $DESKTOP_DST - set +xeou pipefail - ''; - } -) + shellHook = '' + export PATH=$(pwd)/bin:$PATH + ''; +} diff --git a/pkgs/clan-vm-manager/tests/test_join.py b/pkgs/clan-vm-manager/tests/test_join.py new file mode 100644 index 00000000..fff6de20 --- /dev/null +++ b/pkgs/clan-vm-manager/tests/test_join.py @@ -0,0 +1,8 @@ +import time + +from wayland import GtkProc + + +def test_open(app: GtkProc) -> None: + time.sleep(0.5) + assert app.poll() is None diff --git a/pkgs/clan-vm-manager/tests/wayland.py b/pkgs/clan-vm-manager/tests/wayland.py index a9150dd2..1156b666 100644 --- a/pkgs/clan-vm-manager/tests/wayland.py +++ b/pkgs/clan-vm-manager/tests/wayland.py @@ -1,5 +1,7 @@ +import sys from collections.abc import Generator from subprocess import Popen +from typing import NewType import pytest @@ -7,18 +9,19 @@ import pytest @pytest.fixture(scope="session") def wayland_compositor() -> Generator[Popen, None, None]: # Start the Wayland compositor (e.g., Weston) - compositor = Popen(["weston", "--backend=headless-backend.so"]) + # compositor = Popen(["weston", "--backend=headless-backend.so"]) + compositor = Popen(["weston"]) yield compositor # Cleanup: Terminate the compositor compositor.terminate() +GtkProc = NewType("GtkProc", Popen) + + @pytest.fixture(scope="function") -def gtk_app(wayland_compositor: Popen) -> Generator[Popen, None, None]: - # Assuming your GTK4 app can be started via a command line - # It's important to ensure it uses the Wayland session initiated by the fixture - env = {"GDK_BACKEND": "wayland"} - app = Popen(["clan-vm-manager"], env=env) - yield app +def app() -> Generator[GtkProc, None, None]: + rapp = Popen([sys.executable, "-m", "clan_vm_manager"], text=True) + yield GtkProc(rapp) # Cleanup: Terminate your application - app.terminate() + rapp.terminate()