diff --git a/.envrc b/.envrc index 503fb6b69..ace8023dc 100644 --- a/.envrc +++ b/.envrc @@ -1,5 +1,5 @@ -if ! has nix_direnv_version || ! nix_direnv_version 2.3.0; then - source_url "https://raw.githubusercontent.com/nix-community/nix-direnv/2.3.0/direnvrc" "sha256-Dmd+j63L84wuzgyjITIfSxSD57Tx7v51DMxVZOsiUD8=" +if ! has nix_direnv_version || ! nix_direnv_version 2.4.0; then + source_url "https://raw.githubusercontent.com/nix-community/nix-direnv/2.4.0/direnvrc" "sha256-Dmd+j63L84wuzgyjITIfSxSD57Tx7v51DMxVZOsiUD8=" fi use flake diff --git a/docs/contributing.md b/docs/contributing.md index 8e738e47e..342d9eddf 100644 --- a/docs/contributing.md +++ b/docs/contributing.md @@ -78,6 +78,8 @@ Let's get your development environment up and running: ``` - Wait for the frontend to build. + NOTE: If you have the error "@clan/colors.json" you executed `npm install`, please do not do that. `direnv reload` will handle dependency management. Please delete node_modules with `rm -rf node_modules`. + 9. **Start the Frontend**: - To start the frontend, execute: ```bash diff --git a/flake.nix b/flake.nix index 86afbf86b..a57719c20 100644 --- a/flake.nix +++ b/flake.nix @@ -33,7 +33,6 @@ "aarch64-darwin" ]; imports = [ - ./checks/flake-module.nix ./devShell.nix ./formatter.nix diff --git a/pkgs/clan-cli/.envrc b/pkgs/clan-cli/.envrc index 0ded7613f..0e43ca839 100644 --- a/pkgs/clan-cli/.envrc +++ b/pkgs/clan-cli/.envrc @@ -1,13 +1,6 @@ -# Because we depend on nixpkgs sources, uploading to builders takes a long time - source_up +nix_direnv_watch_file flake-module.nix default.nix -if type nix_direnv_watch_file &>/dev/null; then - nix_direnv_watch_file flake-module.nix - nix_direnv_watch_file default.nix -else - direnv watch flake-module.nix - direnv watch default.nix -fi +# Because we depend on nixpkgs sources, uploading to builders takes a long time use flake .#clan-cli --builders '' diff --git a/pkgs/clan-cli/clan_cli/nix.py b/pkgs/clan-cli/clan_cli/nix.py index e2d7c6a14..66beb4129 100644 --- a/pkgs/clan-cli/clan_cli/nix.py +++ b/pkgs/clan-cli/clan_cli/nix.py @@ -5,9 +5,12 @@ import tempfile from pathlib import Path from typing import Any +from .deal import deal from .dirs import nixpkgs_flake, nixpkgs_source +from .errors import ClanError +@deal.raises(ClanError) def nix_command(flags: list[str]) -> list[str]: return ["nix", "--extra-experimental-features", "nix-command flakes"] + flags @@ -25,6 +28,7 @@ def nix_flake_show(flake_url: str | Path) -> list[str]: ) +@deal.raises(ClanError) def nix_build( flags: list[str], ) -> list[str]: @@ -41,6 +45,7 @@ def nix_build( ) +@deal.raises(ClanError) def nix_config() -> dict[str, Any]: cmd = nix_command(["show-config", "--json"]) proc = subprocess.run(cmd, check=True, text=True, stdout=subprocess.PIPE) @@ -51,6 +56,7 @@ def nix_config() -> dict[str, Any]: return config +@deal.raises(ClanError) def nix_eval(flags: list[str]) -> list[str]: default_flags = nix_command( [ @@ -78,6 +84,7 @@ def nix_eval(flags: list[str]) -> list[str]: return default_flags + flags +@deal.raises(ClanError) def nix_shell(packages: list[str], cmd: list[str]) -> list[str]: # we cannot use nix-shell inside the nix sandbox # in our tests we just make sure we have all the packages diff --git a/pkgs/clan-cli/clan_cli/task_manager.py b/pkgs/clan-cli/clan_cli/task_manager.py index f0fcb3e62..451e0b1fb 100644 --- a/pkgs/clan-cli/clan_cli/task_manager.py +++ b/pkgs/clan-cli/clan_cli/task_manager.py @@ -187,9 +187,13 @@ def get_task(uuid: UUID) -> BaseTask: T = TypeVar("T", bound="BaseTask") +@deal.raises(ClanError) def create_task(task_type: Type[T], *args: Any) -> T: global POOL + # check if task_type is a callable + if not callable(task_type): + raise ClanError("task_type must be callable") uuid = uuid4() task = task_type(uuid, *args) diff --git a/pkgs/clan-cli/default.nix b/pkgs/clan-cli/default.nix index 263812fe0..e8ceb8c42 100644 --- a/pkgs/clan-cli/default.nix +++ b/pkgs/clan-cli/default.nix @@ -28,7 +28,6 @@ , tor , git , nixpkgs -, copyDesktopItems , qemu , gnupg , e2fsprogs @@ -36,7 +35,7 @@ , deal , rope , clan-core-path -, schemathesis +, schemathesis ? null }: let @@ -129,7 +128,6 @@ python3.pkgs.buildPythonApplication { nativeBuildInputs = [ setuptools installShellFiles - copyDesktopItems ]; propagatedBuildInputs = dependencies; diff --git a/pkgs/clan-cli/pyproject.toml b/pkgs/clan-cli/pyproject.toml index 927de1d01..0979e0180 100644 --- a/pkgs/clan-cli/pyproject.toml +++ b/pkgs/clan-cli/pyproject.toml @@ -38,6 +38,11 @@ exclude = "clan_cli.nixpkgs" module = "argcomplete.*" ignore_missing_imports = true +[[tool.mypy.overrides]] +module = "gi.*" +ignore_missing_imports = true + + [[tool.mypy.overrides]] module = "jsonschema.*" ignore_missing_imports = true @@ -58,4 +63,4 @@ ignore_missing_imports = true line-length = 88 select = [ "E", "F", "I", "N"] -ignore = [ "E501" ] +ignore = [ "E501", "E402" ] diff --git a/pkgs/clan-cli/shell.nix b/pkgs/clan-cli/shell.nix index bce7119ab..055154739 100644 --- a/pkgs/clan-cli/shell.nix +++ b/pkgs/clan-cli/shell.nix @@ -38,14 +38,12 @@ mkShell { --prefix "$tmp_path/python" \ --editable $repo_root - rm -f clan_cli/nixpkgs clan_cli/webui/assets - ln -sf ${clan-cli.nixpkgs} clan_cli/nixpkgs - ln -sf ${ui-assets} clan_cli/webui/assets + ln -sfT ${clan-cli.nixpkgs} clan_cli/nixpkgs + ln -sfT ${ui-assets} clan_cli/webui/assets 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 \ diff --git a/pkgs/clan-cli/tests/test_with_deal.py b/pkgs/clan-cli/tests/test_with_deal.py index 705d401f6..8d1637901 100644 --- a/pkgs/clan-cli/tests/test_with_deal.py +++ b/pkgs/clan-cli/tests/test_with_deal.py @@ -1,8 +1,38 @@ import deal -from clan_cli.task_manager import get_task +from clan_cli import nix, task_manager -@deal.cases(get_task) +@deal.cases(task_manager.get_task) def test_get_task(case: deal.TestCase) -> None: case() + + +@deal.cases(task_manager.create_task) +def test_create_task(case: deal.TestCase) -> None: + case() + + +@deal.cases(nix.nix_command) +def test_nix_command(case: deal.TestCase) -> None: + case() + + +@deal.cases(nix.nix_build) +def test_nix_build(case: deal.TestCase) -> None: + case() + + +@deal.cases(nix.nix_config) +def test_nix_config(case: deal.TestCase) -> None: + case() + + +@deal.cases(nix.nix_eval) +def test_nix_eval(case: deal.TestCase) -> None: + case() + + +@deal.cases(nix.nix_shell) +def test_nix_shell(case: deal.TestCase) -> None: + case() diff --git a/pkgs/clan-vm-manager/.envrc b/pkgs/clan-vm-manager/.envrc new file mode 100644 index 000000000..27620706b --- /dev/null +++ b/pkgs/clan-vm-manager/.envrc @@ -0,0 +1,6 @@ +source_up + +nix_direnv_watch_file flake-module.nix default.nix + +# Because we depend on nixpkgs sources, uploading to builders takes a long time +use flake .#clan-vm-manager --builders '' diff --git a/pkgs/clan-vm-manager/bin/clan-vm-manager b/pkgs/clan-vm-manager/bin/clan-vm-manager new file mode 100755 index 000000000..a6c6a17ba --- /dev/null +++ b/pkgs/clan-vm-manager/bin/clan-vm-manager @@ -0,0 +1,13 @@ +#!/usr/bin/env python3 +import sys +from pathlib import Path + +module_path = Path(__file__).parent.parent.absolute() + +sys.path.insert(0, str(module_path)) +sys.path.insert(0, str(module_path.parent / "clan_cli")) + +from clan_vm_manager import main # NOQA + +if __name__ == "__main__": + main() diff --git a/pkgs/clan-vm-manager/clan_vm_manager/__init__.py b/pkgs/clan-vm-manager/clan_vm_manager/__init__.py new file mode 100644 index 000000000..3e568be66 --- /dev/null +++ b/pkgs/clan-vm-manager/clan_vm_manager/__init__.py @@ -0,0 +1,13 @@ +import argparse +from typing import Callable, Optional + +start_app: Optional[Callable] = None + +from .app import start_app + + +def main() -> None: + parser = argparse.ArgumentParser(description="clan-vm-manager") + parser.set_defaults(func=start_app) + args = parser.parse_args() + args.func(args) diff --git a/pkgs/clan-vm-manager/clan_vm_manager/app.glade b/pkgs/clan-vm-manager/clan_vm_manager/app.glade new file mode 100644 index 000000000..65a645d0a --- /dev/null +++ b/pkgs/clan-vm-manager/clan_vm_manager/app.glade @@ -0,0 +1,49 @@ + + + + + + False + + + asdasd + True + False + + + May I help you? + asdasd + 100 + 80 + True + True + True + top + + + + 21 + 21 + + + + + 100 + 80 + True + False + Get me some coffe! + 0 + + + 178 + 120 + + + + + + + diff --git a/pkgs/clan-vm-manager/clan_vm_manager/app.py b/pkgs/clan-vm-manager/clan_vm_manager/app.py new file mode 100644 index 000000000..0122b9df3 --- /dev/null +++ b/pkgs/clan-vm-manager/clan_vm_manager/app.py @@ -0,0 +1,19 @@ +# !/usr/bin/env python3 + +import argparse # noqa +from pathlib import Path # noqa + +import gi # noqa + +gi.require_version("Gtk", "3.0") # noqa +from gi.repository import Gtk # noqa + + +def start_app(args: argparse.Namespace) -> None: + builder = Gtk.Builder() + glade_file = Path(__file__).parent / "app.glade" + builder.add_from_file(str(glade_file)) + window = builder.get_object("main-window") + window.show_all() + + Gtk.main() diff --git a/pkgs/clan-vm-manager/default.nix b/pkgs/clan-vm-manager/default.nix new file mode 100644 index 000000000..1ffcbdb6c --- /dev/null +++ b/pkgs/clan-vm-manager/default.nix @@ -0,0 +1,68 @@ +{ python3 +, runCommand +, setuptools +, copyDesktopItems +, pygobject3 +, wrapGAppsHook +, gtk3 +, gnome +, gobject-introspection +, clan-cli +, makeDesktopItem +}: +let + source = ./.; +in +python3.pkgs.buildPythonApplication { + name = "clan-vm-manager"; + 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" + ]; + + nativeBuildInputs = [ + setuptools + copyDesktopItems + wrapGAppsHook + gobject-introspection + ]; + + buildInputs = [ gtk3 gnome.adwaita-icon-theme ]; + propagatedBuildInputs = [ pygobject3 clan-cli ]; + + # also re-expose dependencies so we test them in CI + passthru.tests = { + clan-vm-manager-no-breakpoints = runCommand "clan-vm-manager-no-breakpoints" { } '' + if grep --include \*.py -Rq "breakpoint()" ${source}; then + echo "breakpoint() found in ${source}:" + grep --include \*.py -Rn "breakpoint()" ${source} + exit 1 + fi + touch $out + ''; + }; + + # 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 = '' + rm $out/nix-support/propagated-build-inputs + ''; + checkPhase = '' + PYTHONPATH= $out/bin/clan-vm-manager --help + ''; + meta.mainProgram = "clan"; + desktopItems = [ + (makeDesktopItem { + name = "clan-vm-manager"; + # TODO: this subcommand is not implemented yet + exec = "clan-vm-manager join %u"; + desktopName = "CLan VM Manager"; + startupWMClass = "clan"; + mimeTypes = [ "x-scheme-handler/clan" ]; + }) + ]; +} diff --git a/pkgs/clan-vm-manager/flake-module.nix b/pkgs/clan-vm-manager/flake-module.nix new file mode 100644 index 000000000..7264c1302 --- /dev/null +++ b/pkgs/clan-vm-manager/flake-module.nix @@ -0,0 +1,12 @@ +{ ... }: { + perSystem = { config, pkgs, ... }: { + devShells.clan-vm-manager = pkgs.callPackage ./shell.nix { + inherit (config.packages) clan-cli clan-vm-manager; + }; + packages.clan-vm-manager = pkgs.python3.pkgs.callPackage ./default.nix { + inherit (config.packages) clan-cli; + }; + + checks = config.packages.clan-vm-manager.tests; + }; +} diff --git a/pkgs/clan-vm-manager/pyproject.toml b/pkgs/clan-vm-manager/pyproject.toml new file mode 100644 index 000000000..0ec43fce1 --- /dev/null +++ b/pkgs/clan-vm-manager/pyproject.toml @@ -0,0 +1,28 @@ +[build-system] +requires = ["setuptools"] +build-backend = "setuptools.build_meta" + +[project] +name = "clan-vm-manager" +dynamic = ["version"] +scripts = { clan-vm-manager = "clan_vm_manager:main" } + +[tool.setuptools.package-data] +clan_vm_manager = ["*.glade"] + +[tool.mypy] +python_version = "3.10" +warn_redundant_casts = true +disallow_untyped_calls = true +disallow_untyped_defs = true +no_implicit_optional = true + +[[tool.mypy.overrides]] +module = "gi.*" +ignore_missing_imports = true + +[tool.ruff] +line-length = 88 + +select = ["E", "F", "I", "N"] +ignore = ["E501", "E402"] diff --git a/pkgs/clan-vm-manager/shell.nix b/pkgs/clan-vm-manager/shell.nix new file mode 100644 index 000000000..11296fb73 --- /dev/null +++ b/pkgs/clan-vm-manager/shell.nix @@ -0,0 +1,14 @@ +{ clan-vm-manager, clan-cli, mkShell, ruff }: +mkShell { + inherit (clan-vm-manager) propagatedBuildInputs buildInputs; + nativeBuildInputs = [ + ruff + ] ++ clan-vm-manager.nativeBuildInputs; + + shellHook = '' + ln -sfT ${clan-cli.nixpkgs} ../clan-cli/clan_cli/nixpkgs + + # prepend clan-cli for development + export PYTHONPATH=../clan-cli:$PYTHONPATH + ''; +} diff --git a/pkgs/flake-module.nix b/pkgs/flake-module.nix index 5173b5dbe..b3792a8f0 100644 --- a/pkgs/flake-module.nix +++ b/pkgs/flake-module.nix @@ -1,6 +1,7 @@ { ... }: { imports = [ ./clan-cli/flake-module.nix + ./clan-vm-manager/flake-module.nix ./installer/flake-module.nix ./ui/flake-module.nix ./theme/flake-module.nix