1
0
forked from clan/clan-core

Added clan flakes inspect command. Improved ClanURI. Added democlan as dependency in flake.nix

This commit is contained in:
Luis Hebendanz 2023-12-09 00:04:56 +01:00
parent 9f4ab67fc2
commit d4b8cef242
14 changed files with 406 additions and 41 deletions

View File

@ -1,6 +1,71 @@
{
"nodes": {
"clan-core": {
"inputs": {
"disko": "disko",
"flake-parts": "flake-parts",
"floco": "floco",
"nixos-generators": "nixos-generators",
"nixpkgs": "nixpkgs",
"nixpkgs-for-deal": "nixpkgs-for-deal",
"sops-nix": "sops-nix",
"treefmt-nix": "treefmt-nix"
},
"locked": {
"lastModified": 1702060438,
"narHash": "sha256-+qJl3phTehVueyv20bxUORcrtdDSTmYOp3PKewatrqQ=",
"ref": "refs/heads/main",
"rev": "9f4ab67fc2da999efe1013cab28e43e330a4507d",
"revCount": 1550,
"type": "git",
"url": "https://git.clan.lol/clan/clan-core"
},
"original": {
"type": "git",
"url": "https://git.clan.lol/clan/clan-core"
}
},
"democlan": {
"inputs": {
"clan-core": "clan-core"
},
"locked": {
"lastModified": 1702060701,
"narHash": "sha256-IkvQvxrLnTF9RFMusav3xuvI+MwOPr7XvzpfD2FckpE=",
"ref": "refs/heads/main",
"rev": "88970bacaf4503c9a51ad3baaf85a478ce7a37f7",
"revCount": 50,
"type": "git",
"url": "https://git.clan.lol/clan/democlan.git"
},
"original": {
"type": "git",
"url": "https://git.clan.lol/clan/democlan.git"
}
},
"disko": {
"inputs": {
"nixpkgs": [
"democlan",
"clan-core",
"nixpkgs"
]
},
"locked": {
"lastModified": 1700927249,
"narHash": "sha256-iqmIWiEng890/ru7ZBf4nUezFPyRm2fjRTvuwwxqk2o=",
"owner": "nix-community",
"repo": "disko",
"rev": "3cb78c93e6a02f494aaf6aeb37481c27a2e2ee22",
"type": "github"
},
"original": {
"owner": "nix-community",
"repo": "disko",
"type": "github"
}
},
"disko_2": {
"inputs": {
"nixpkgs": [
"nixpkgs"
@ -21,6 +86,28 @@
}
},
"flake-parts": {
"inputs": {
"nixpkgs-lib": [
"democlan",
"clan-core",
"nixpkgs"
]
},
"locked": {
"lastModified": 1701473968,
"narHash": "sha256-YcVE5emp1qQ8ieHUnxt1wCZCC3ZfAS+SRRWZ2TMda7E=",
"owner": "hercules-ci",
"repo": "flake-parts",
"rev": "34fed993f1674c8d06d58b37ce1e0fe5eebcb9f5",
"type": "github"
},
"original": {
"owner": "hercules-ci",
"repo": "flake-parts",
"type": "github"
}
},
"flake-parts_2": {
"inputs": {
"nixpkgs-lib": [
"nixpkgs"
@ -41,6 +128,28 @@
}
},
"floco": {
"inputs": {
"nixpkgs": [
"democlan",
"clan-core",
"nixpkgs"
]
},
"locked": {
"lastModified": 1694873346,
"narHash": "sha256-Uvh03bg0a6ZnNWiX1Gb8g+m343wSJ/wb8ryUASt0loc=",
"owner": "aakropotkin",
"repo": "floco",
"rev": "d16bd444ab9d29a6640f52ee4e43a66528e07515",
"type": "github"
},
"original": {
"owner": "aakropotkin",
"repo": "floco",
"type": "github"
}
},
"floco_2": {
"inputs": {
"nixpkgs": [
"nixpkgs"
@ -75,9 +184,47 @@
"type": "github"
}
},
"nixlib_2": {
"locked": {
"lastModified": 1693701915,
"narHash": "sha256-waHPLdDYUOHSEtMKKabcKIMhlUOHPOOPQ9UyFeEoovs=",
"owner": "nix-community",
"repo": "nixpkgs.lib",
"rev": "f5af57d3ef9947a70ac86e42695231ac1ad00c25",
"type": "github"
},
"original": {
"owner": "nix-community",
"repo": "nixpkgs.lib",
"type": "github"
}
},
"nixos-generators": {
"inputs": {
"nixlib": "nixlib",
"nixpkgs": [
"democlan",
"clan-core",
"nixpkgs"
]
},
"locked": {
"lastModified": 1696058303,
"narHash": "sha256-eNqKWpF5zG0SrgbbtljFOrRgFgRzCc4++TMFADBMLnc=",
"owner": "nix-community",
"repo": "nixos-generators",
"rev": "150f38bd1e09e20987feacb1b0d5991357532fb5",
"type": "github"
},
"original": {
"owner": "nix-community",
"repo": "nixos-generators",
"type": "github"
}
},
"nixos-generators_2": {
"inputs": {
"nixlib": "nixlib_2",
"nixpkgs": [
"nixpkgs"
]
@ -128,19 +275,78 @@
"type": "github"
}
},
"nixpkgs-for-deal_2": {
"locked": {
"lastModified": 1700490099,
"narHash": "sha256-IDZGtMvddtEdYr7BUtXsQNPhVXcLzWj6AC9s1wRkZlo=",
"owner": "Luis-Hebendanz",
"repo": "nixpkgs",
"rev": "32ea44ece77a5bad662685acf4652cc984e2df3f",
"type": "github"
},
"original": {
"owner": "Luis-Hebendanz",
"ref": "fix_python_deal",
"repo": "nixpkgs",
"type": "github"
}
},
"nixpkgs_2": {
"locked": {
"lastModified": 1701609850,
"narHash": "sha256-6oxM84kaQT0H/+aurIcj2ON+asWYQ96zlMUIsfhKpFE=",
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "0b62f5adfd6635f8013d800ceb0cf39411a8216f",
"type": "github"
},
"original": {
"owner": "NixOS",
"ref": "nixos-unstable-small",
"repo": "nixpkgs",
"type": "github"
}
},
"root": {
"inputs": {
"disko": "disko",
"flake-parts": "flake-parts",
"floco": "floco",
"nixos-generators": "nixos-generators",
"nixpkgs": "nixpkgs",
"nixpkgs-for-deal": "nixpkgs-for-deal",
"sops-nix": "sops-nix",
"treefmt-nix": "treefmt-nix"
"democlan": "democlan",
"disko": "disko_2",
"flake-parts": "flake-parts_2",
"floco": "floco_2",
"nixos-generators": "nixos-generators_2",
"nixpkgs": "nixpkgs_2",
"nixpkgs-for-deal": "nixpkgs-for-deal_2",
"sops-nix": "sops-nix_2",
"treefmt-nix": "treefmt-nix_2"
}
},
"sops-nix": {
"inputs": {
"nixpkgs": [
"democlan",
"clan-core",
"nixpkgs"
],
"nixpkgs-stable": [
"democlan",
"clan-core"
]
},
"locked": {
"lastModified": 1701572436,
"narHash": "sha256-0anfOQqDend6kSuF8CmOSAZsiAS1nwOsin5VQukh6Q4=",
"owner": "Mic92",
"repo": "sops-nix",
"rev": "8bca48cb9a12bbd8766f359ad00336924e91b7f7",
"type": "github"
},
"original": {
"owner": "Mic92",
"repo": "sops-nix",
"type": "github"
}
},
"sops-nix_2": {
"inputs": {
"nixpkgs": [
"nixpkgs"
@ -162,6 +368,28 @@
}
},
"treefmt-nix": {
"inputs": {
"nixpkgs": [
"democlan",
"clan-core",
"nixpkgs"
]
},
"locked": {
"lastModified": 1699786194,
"narHash": "sha256-3h3EH1FXQkIeAuzaWB+nK0XK54uSD46pp+dMD3gAcB4=",
"owner": "numtide",
"repo": "treefmt-nix",
"rev": "e82f32aa7f06bbbd56d7b12186d555223dc399d1",
"type": "github"
},
"original": {
"owner": "numtide",
"repo": "treefmt-nix",
"type": "github"
}
},
"treefmt-nix_2": {
"inputs": {
"nixpkgs": [
"nixpkgs"

View File

@ -22,6 +22,7 @@
flake-parts.url = "github:hercules-ci/flake-parts";
flake-parts.inputs.nixpkgs-lib.follows = "nixpkgs";
treefmt-nix.url = "github:numtide/treefmt-nix";
democlan.url = "git+https://git.clan.lol/clan/democlan.git";
treefmt-nix.inputs.nixpkgs.follows = "nixpkgs";
};

View File

@ -88,7 +88,13 @@ class ClanURI:
raise ClanError(f"Unsupported uri components: {comb}")
def get_internal(self) -> str:
return self._nested_uri
match self.scheme:
case ClanScheme.FILE.value(path):
return str(path) # type: ignore
case ClanScheme.HTTP.value(url):
return url # type: ignore
case _:
raise ClanError(f"Unsupported uri components: {self.scheme}")
@classmethod
def from_path(cls, path: Path, params: ClanParameters) -> Self: # noqa

View File

@ -3,6 +3,7 @@ import argparse
from clan_cli.flakes.add import register_add_parser
from clan_cli.flakes.history import register_list_parser
from clan_cli.flakes.inspect import register_inspect_parser
from .create import register_create_parser
@ -21,3 +22,5 @@ def register_parser(parser: argparse.ArgumentParser) -> None:
register_add_parser(add_parser)
list_parser = subparser.add_parser("list", help="List recently used flakes")
register_list_parser(list_parser)
inspect_parser = subparser.add_parser("inspect", help="Inspect a clan flake")
register_inspect_parser(inspect_parser)

View File

@ -0,0 +1,83 @@
import argparse
import shlex
import subprocess
from dataclasses import dataclass
from pathlib import Path
from ..errors import ClanError
from ..nix import nix_config, nix_eval, nix_metadata
@dataclass
class FlakeConfig:
flake_url: str | Path
flake_attr: str
icon: str | None
description: str | None
last_updated: str
revision: str | None
def inspect_flake(flake_url: str | Path, flake_attr: str) -> FlakeConfig:
config = nix_config()
system = config["system"]
cmd = nix_eval(
[
f'{flake_url}#clanInternals.machines."{system}"."{flake_attr}".config.clanCore.clanIcon'
]
)
proc = subprocess.run(cmd, check=True, text=True, stdout=subprocess.PIPE)
assert proc.stdout is not None
if proc.returncode != 0:
raise ClanError(
f"""
command: {shlex.join(cmd)}
exit code: {proc.returncode}
stdout:
{proc.stdout}
"""
)
res = proc.stdout.strip()
if res == "null":
icon_path = None
else:
icon_path = res
meta = nix_metadata(flake_url)
return FlakeConfig(
flake_url=flake_url,
flake_attr=flake_attr,
icon=icon_path,
description=meta.get("description"),
last_updated=meta["lastModified"],
revision=meta.get("revision"),
)
@dataclass
class InspectOptions:
machine: str
flake: Path
def inspect_command(args: argparse.Namespace) -> None:
inspect_options = InspectOptions(
machine=args.machine,
flake=args.flake or Path.cwd(),
)
res = inspect_flake(
flake_url=inspect_options.flake, flake_attr=inspect_options.machine
)
print("Icon:", res.icon)
print("Description:", res.description)
print("Last updated:", res.last_updated)
print("Revision:", res.revision)
def register_inspect_parser(parser: argparse.ArgumentParser) -> None:
parser.add_argument("--machine", type=str, default="defaultVM")
parser.set_defaults(func=inspect_command)

View File

@ -10,7 +10,6 @@ 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]
@ -28,7 +27,6 @@ def nix_flake_show(flake_url: str | Path) -> list[str]:
)
@deal.raises(ClanError)
def nix_build(
flags: list[str],
) -> list[str]:
@ -45,7 +43,6 @@ 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)
@ -56,7 +53,6 @@ def nix_config() -> dict[str, Any]:
return config
@deal.raises(ClanError)
def nix_eval(flags: list[str]) -> list[str]:
default_flags = nix_command(
[
@ -82,9 +78,8 @@ def nix_eval(flags: list[str]) -> list[str]:
return default_flags + flags
@deal.raises(ClanError)
def nix_metadata(flake: str) -> dict[str, Any]:
cmd = nix_command(["flake", "metadata", "--json", flake])
def nix_metadata(flake_url: str | Path) -> dict[str, Any]:
cmd = nix_command(["flake", "metadata", "--json", f"{flake_url}"])
proc = subprocess.run(cmd, check=True, text=True, stdout=subprocess.PIPE)
data = json.loads(proc.stdout)
return data

View File

@ -30,6 +30,7 @@ def inspect_vm(flake_url: str | Path, flake_attr: str) -> VmConfig:
f'{flake_url}#clanInternals.machines."{system}"."{flake_attr}".config.system.clan.vm.config'
]
)
proc = subprocess.run(cmd, check=True, text=True, stdout=subprocess.PIPE)
assert proc.stdout is not None
if proc.returncode != 0:
@ -65,5 +66,5 @@ def inspect_command(args: argparse.Namespace) -> None:
def register_inspect_parser(parser: argparse.ArgumentParser) -> None:
parser.add_argument("machine", type=str)
parser.add_argument("machine", type=str, default="defaultVM")
parser.set_defaults(func=inspect_command)

View File

@ -37,7 +37,7 @@
, clan-core-path
, writeShellScriptBin
, nodePackages
, schemathesis ? null
, democlan
}:
let
@ -155,6 +155,18 @@ python3.pkgs.buildPythonApplication {
${checkPython}/bin/python -m pytest -m "not impure and with_core" -s ./tests
touch $out
'';
clan-pytest-with-democlan = runCommand "clan-pytest-with-democlan" { nativeBuildInputs = [ checkPython ] ++ pytestDependencies; } ''
cp -r ${source} ./src
chmod +w -R ./src
cd ./src
export DEMOCLAN_ROOT=${democlan}
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_democlan" -s ./tests
touch $out
'';
clan-pytest = runCommand "clan-pytest" { } ''
echo ${clan-pytest-without-core}
echo ${clan-pytest-with-core}

View File

@ -31,14 +31,15 @@
{
devShells.clan-cli = pkgs.callPackage ./shell.nix {
inherit (self'.packages) clan-cli ui-assets nix-unit;
democlan = inputs.democlan;
};
packages = {
clan-cli = pkgs.python3.pkgs.callPackage ./default.nix {
inherit (self'.packages) ui-assets;
inherit (inputs) nixpkgs;
inherit (inputs) democlan;
inherit (inputs.nixpkgs-for-deal.legacyPackages.${system}.python3Packages) deal;
inherit (inputs.nixpkgs-for-deal.legacyPackages.${system}.python3Packages) schemathesis;
#inherit (inputs.nixpkgs-for-deal.legacyPackages.${system}.python3Packages) schemathesis;
clan-core-path = clanCoreWithVendoredDeps;
};
default = self'.packages.clan-cli;

View File

@ -1,4 +1,4 @@
{ nix-unit, clan-cli, ui-assets, system, mkShell, writeScriptBin, openssh, ruff, python3 }:
{ nix-unit, clan-cli, ui-assets, system, mkShell, writeScriptBin, openssh, ruff, python3, democlan }:
let
checkScript = writeScriptBin "check" ''
nix build .#checks.${system}.{treefmt,clan-pytest} -L "$@"
@ -24,6 +24,7 @@ mkShell {
shellHook = ''
tmp_path=$(realpath ./.direnv)
export DEMOCLAN_ROOT=${democlan}
repo_root=$(realpath .)
mkdir -p "$tmp_path/python/${pythonWithDeps.sitePackages}"

View File

@ -118,6 +118,24 @@ def test_flake_with_core(
)
@pytest.fixture
def test_local_democlan(
monkeypatch: pytest.MonkeyPatch, temporary_home: Path
) -> Iterator[FlakeForTest]:
democlan = os.getenv(key="DEMOCLAN_ROOT")
if democlan is None:
raise Exception(
"DEMOCLAN_ROOT not set. This test requires the democlan flake to be present"
)
democlan_p = Path(democlan).resolve()
if not democlan_p.is_dir():
raise Exception(
f"DEMOCLAN_ROOT ({democlan_p}) is not a directory. This test requires the democlan directory to be present"
)
yield FlakeForTest(democlan_p)
@pytest.fixture
def test_democlan_url(
monkeypatch: pytest.MonkeyPatch, temporary_home: Path

View File

@ -6,6 +6,21 @@ from clan_cli.clan_uri import ClanParameters, ClanScheme, ClanURI
from clan_cli.errors import ClanError
def test_get_internal() -> None:
# Create a ClanURI object from a remote URI with parameters
uri = ClanURI("clan://https://example.com?flake_attr=myVM&password=1234")
assert uri.get_internal() == "https://example.com?password=1234"
uri = ClanURI("clan://~/Downloads")
assert uri.get_internal() == "~/Downloads"
uri = ClanURI("clan:///home/user/Downloads")
assert uri.get_internal() == "/home/user/Downloads"
uri = ClanURI("clan://file:///home/user/Downloads")
assert uri.get_internal() == "/home/user/Downloads"
def test_local_uri() -> None:
# Create a ClanURI object from a local URI
uri = ClanURI("clan://file:///home/user/Downloads")

View File

@ -1,6 +1,7 @@
import json
from typing import TYPE_CHECKING
import pytest
from cli import Cli
from fixtures_flakes import FlakeForTest
from pytest import CaptureFixture
@ -46,3 +47,23 @@ def test_flakes_list(
cli.run(["flakes", "add", str(test_flake.path)])
cli.run(cmd)
assert str(test_flake.path) in capsys.readouterr().out
@pytest.mark.impure
def test_flakes_inspect(
test_flake_with_core: FlakeForTest, capsys: pytest.CaptureFixture
) -> None:
cli = Cli()
cli.run(
[
"--flake",
str(test_flake_with_core.path),
"flakes",
"inspect",
"--machine",
"vm1",
]
)
out = capsys.readouterr() # empty the buffer
assert "Icon" in out.out

View File

@ -3,26 +3,6 @@ import deal
from clan_cli import nix
@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()