Merge pull request 'fixed history allowing adding the same clan twice' (#643) from Qubasa-main into main

This commit is contained in:
clan-bot
2023-12-14 19:59:14 +00:00
5 changed files with 86 additions and 76 deletions

View File

@@ -1,8 +1,8 @@
import asyncio
import logging import logging
import shlex import shlex
from collections.abc import Callable, Coroutine from collections.abc import Callable
from pathlib import Path from pathlib import Path
from subprocess import PIPE, Popen
from typing import Any, NamedTuple from typing import Any, NamedTuple
from .custom_logger import get_caller from .custom_logger import get_caller
@@ -17,7 +17,7 @@ class CmdOut(NamedTuple):
cwd: Path | None = None cwd: Path | None = None
async def run(cmd: list[str], cwd: Path | None = None) -> CmdOut: def run(cmd: list[str], cwd: Path | None = None) -> CmdOut:
cwd_res = None cwd_res = None
if cwd is not None: if cwd is not None:
if not cwd.exists(): if not cwd.exists():
@@ -28,13 +28,14 @@ async def run(cmd: list[str], cwd: Path | None = None) -> CmdOut:
log.debug( log.debug(
f"Command: {shlex.join(cmd)}\nWorking directory: {cwd_res}\nCaller : {get_caller()}" f"Command: {shlex.join(cmd)}\nWorking directory: {cwd_res}\nCaller : {get_caller()}"
) )
proc = await asyncio.create_subprocess_exec( proc = Popen(
*cmd, args=cmd,
stdout=asyncio.subprocess.PIPE, stderr=PIPE,
stderr=asyncio.subprocess.PIPE, stdout=PIPE,
text=True,
cwd=cwd_res, cwd=cwd_res,
) )
stdout, stderr = await proc.communicate() stdout, stderr = proc.communicate()
if proc.returncode != 0: if proc.returncode != 0:
raise ClanError( raise ClanError(
@@ -43,20 +44,18 @@ command: {shlex.join(cmd)}
working directory: {cwd_res} working directory: {cwd_res}
exit code: {proc.returncode} exit code: {proc.returncode}
stderr: stderr:
{stderr.decode("utf-8")} {stderr}
stdout: stdout:
{stdout.decode("utf-8")} {stdout}
""" """
) )
return CmdOut(stdout.decode("utf-8"), stderr.decode("utf-8"), cwd=cwd) return CmdOut(stdout, stderr, cwd=cwd)
def runforcli( def runforcli(func: Callable[..., dict[str, CmdOut]], *args: Any) -> None:
func: Callable[..., Coroutine[Any, Any, dict[str, CmdOut]]], *args: Any
) -> None:
try: try:
res = asyncio.run(func(*args)) res = func(*args)
for name, out in res.items(): for name, out in res.items():
if out.stderr: if out.stderr:

View File

@@ -2,14 +2,14 @@
import argparse import argparse
from pathlib import Path from pathlib import Path
from ..async_cmd import CmdOut, run, runforcli from ..cmd import CmdOut, run, runforcli
from ..errors import ClanError from ..errors import ClanError
from ..nix import nix_command, nix_shell from ..nix import nix_command, nix_shell
DEFAULT_URL: str = "git+https://git.clan.lol/clan/clan-core?new-clan" DEFAULT_URL: str = "git+https://git.clan.lol/clan/clan-core?new-clan"
async def create_flake(directory: Path, url: str) -> dict[str, CmdOut]: def create_flake(directory: Path, url: str) -> dict[str, CmdOut]:
if not directory.exists(): if not directory.exists():
directory.mkdir() directory.mkdir()
else: else:
@@ -23,36 +23,27 @@ async def create_flake(directory: Path, url: str) -> dict[str, CmdOut]:
url, url,
] ]
) )
out = await run(command, cwd=directory) out = run(command, cwd=directory)
response["flake init"] = out response["flake init"] = out
command = nix_shell(["nixpkgs#git"], ["git", "init"]) command = nix_shell(["nixpkgs#git"], ["git", "init"])
out = await run(command, cwd=directory) out = run(command, cwd=directory)
response["git init"] = out response["git init"] = out
command = nix_shell(["nixpkgs#git"], ["git", "add", "."]) command = nix_shell(["nixpkgs#git"], ["git", "add", "."])
out = await run(command, cwd=directory) out = run(command, cwd=directory)
response["git add"] = out response["git add"] = out
# command = nix_shell(["nixpkgs#git"], ["git", "config", "init.defaultBranch", "main"])
# out = await run(command, cwd=directory)
# response["git config"] = out
command = nix_shell(["nixpkgs#git"], ["git", "config", "user.name", "clan-tool"]) command = nix_shell(["nixpkgs#git"], ["git", "config", "user.name", "clan-tool"])
out = await run(command, cwd=directory) out = run(command, cwd=directory)
response["git config"] = out response["git config"] = out
command = nix_shell( command = nix_shell(
["nixpkgs#git"], ["git", "config", "user.email", "clan@example.com"] ["nixpkgs#git"], ["git", "config", "user.email", "clan@example.com"]
) )
out = await run(command, cwd=directory) out = run(command, cwd=directory)
response["git config"] = out response["git config"] = out
# TODO: Find out why this fails on Johannes machine
# command = nix_shell(["nixpkgs#git"], ["git", "commit", "-a", "-m", "Initial commit"])
# out = await run(command, cwd=directory)
# response["git commit"] = out
return response return response

View File

@@ -6,7 +6,7 @@ from pathlib import Path
from ..errors import ClanError from ..errors import ClanError
from ..machines.list import list_machines from ..machines.list import list_machines
from ..nix import nix_config, nix_eval, nix_metadata from ..nix import nix_build, nix_config, nix_eval, nix_metadata
@dataclass @dataclass
@@ -14,6 +14,7 @@ class FlakeConfig:
flake_url: str | Path flake_url: str | Path
flake_attr: str flake_attr: str
clan_name: str
nar_hash: str nar_hash: str
icon: str | None icon: str | None
description: str | None description: str | None
@@ -56,10 +57,52 @@ stderr:
else: else:
icon_path = res.strip('"') icon_path = res.strip('"')
if not Path(icon_path).exists():
cmd = nix_build(
[
f'{flake_url}#clanInternals.machines."{system}"."{flake_attr}".config.clanCore.clanIcon'
]
)
proc = subprocess.run(cmd, text=True, capture_output=True)
assert proc.stdout is not None
if proc.returncode != 0:
raise ClanError(
f"""
command: {shlex.join(cmd)}
exit code: {proc.returncode}
stdout:
{proc.stdout}
stderr:
{proc.stderr}
"""
)
cmd = nix_eval(
[
f'{flake_url}#clanInternals.machines."{system}"."{flake_attr}".config.clanCore.clanName'
]
)
proc = subprocess.run(cmd, text=True, capture_output=True)
assert proc.stdout is not None
if proc.returncode != 0:
raise ClanError(
f"""
command: {shlex.join(cmd)}
exit code: {proc.returncode}
stdout:
{proc.stdout}
stderr:
{proc.stderr}
"""
)
clan_name = proc.stdout.strip().strip('"')
meta = nix_metadata(flake_url) meta = nix_metadata(flake_url)
return FlakeConfig( return FlakeConfig(
flake_url=flake_url, flake_url=flake_url,
clan_name=clan_name,
flake_attr=flake_attr, flake_attr=flake_attr,
nar_hash=meta["locked"]["narHash"], nar_hash=meta["locked"]["narHash"],
icon=icon_path, icon=icon_path,
@@ -83,6 +126,7 @@ def inspect_command(args: argparse.Namespace) -> None:
res = inspect_flake( res = inspect_flake(
flake_url=inspect_options.flake, flake_attr=inspect_options.machine flake_url=inspect_options.flake, flake_attr=inspect_options.machine
) )
print("cLAN name:", res.clan_name)
print("Icon:", res.icon) print("Icon:", res.icon)
print("Description:", res.description) print("Description:", res.description)
print("Last updated:", res.last_updated) print("Last updated:", res.last_updated)

View File

@@ -39,7 +39,7 @@ def list_history() -> list[HistoryEntry]:
content: str = f.read() content: str = f.read()
parsed: list[dict] = json.loads(content) parsed: list[dict] = json.loads(content)
logs = [HistoryEntry(**p) for p in parsed] logs = [HistoryEntry(**p) for p in parsed]
except json.JSONDecodeError as ex: except (json.JSONDecodeError, TypeError) as ex:
print("Failed to load history. Invalid JSON.") print("Failed to load history. Invalid JSON.")
print(f"{user_history_file()}: {ex}") print(f"{user_history_file()}: {ex}")
@@ -58,16 +58,14 @@ def add_history(uri: ClanURI) -> list[HistoryEntry]:
found = True found = True
entry.last_used = datetime.datetime.now().isoformat() entry.last_used = datetime.datetime.now().isoformat()
if found: if not found:
break flake = inspect_flake(path, machine)
flake.flake_url = str(flake.flake_url)
flake = inspect_flake(path, machine) history = HistoryEntry(
flake.flake_url = str(flake.flake_url) flake=flake,
history = HistoryEntry( last_used=datetime.datetime.now().isoformat(),
flake=flake, )
last_used=datetime.datetime.now().isoformat(), logs.append(history)
)
logs.append(history)
with locked_open(user_history_file(), "w+") as f: with locked_open(user_history_file(), "w+") as f:
f.write(json.dumps(logs, cls=EnhancedJSONEncoder, indent=4)) f.write(json.dumps(logs, cls=EnhancedJSONEncoder, indent=4))

View File

@@ -4,7 +4,7 @@ from pathlib import Path
from typing import Any from typing import Any
import gi import gi
from clan_cli import flakes, history, vms from clan_cli import history, vms
gi.require_version("GdkPixbuf", "2.0") gi.require_version("GdkPixbuf", "2.0")
from gi.repository import GdkPixbuf from gi.repository import GdkPixbuf
@@ -18,7 +18,6 @@ class VMBase:
name: str name: str
url: str url: str
status: bool status: bool
_path: Path
@staticmethod @staticmethod
def name_to_type_map() -> OrderedDict[str, type]: def name_to_type_map() -> OrderedDict[str, type]:
@@ -28,7 +27,6 @@ class VMBase:
"Name": str, "Name": str,
"URL": str, "URL": str,
"Online": bool, "Online": bool,
"_Path": str,
} }
) )
@@ -43,7 +41,6 @@ class VMBase:
"Name": self.name, "Name": self.name,
"URL": self.url, "URL": self.url,
"Online": self.status, "Online": self.status,
"_Path": str(self._path),
} }
) )
@@ -52,16 +49,10 @@ class VMBase:
import asyncio import asyncio
# raise Exception("Cannot run VMs yet") # raise Exception("Cannot run VMs yet")
vm = asyncio.run( vm = asyncio.run(vms.run.inspect_vm(flake_url=self.url, flake_attr="defaultVM"))
vms.run.inspect_vm(flake_url=self._path, flake_attr="defaultVM")
)
vms.run.run_vm(vm) vms.run.run_vm(vm)
# for line in task.log_lines():
# print(line, end="")
@dataclass(frozen=True) @dataclass(frozen=True)
class VM: class VM:
# Inheritance is bad. Lets use composition # Inheritance is bad. Lets use composition
@@ -75,32 +66,19 @@ class VM:
def get_initial_vms(start: int = 0, end: int | None = None) -> list[VM]: def get_initial_vms(start: int = 0, end: int | None = None) -> list[VM]:
vm_list = [] vm_list = []
# TODO: list_history() should return a list of dicts, not a list of paths
# Execute `clan flakes add <path>` to democlan for this to work # Execute `clan flakes add <path>` to democlan for this to work
for entry in history.list.list_history(): for entry in history.list.list_history():
flake_config = flakes.inspect.inspect_flake(entry.path, "defaultVM")
vm_config = vms.inspect.inspect_vm(entry.path, "defaultVM")
# if flake_config.icon is None:
# icon = assets.loc / "placeholder.jpeg"
# else:
# icon = flake_config.icon
icon = assets.loc / "placeholder.jpeg" icon = assets.loc / "placeholder.jpeg"
# TODO: clan flakes inspect currently points to an icon that doesn't exist if entry.flake.icon is not None:
# the reason being that the icon is not in the nix store, as the democlan has icon = entry.flake.icon
# not been built yet. Not sure how to handle this.
# I think how to do this is to add democlan as a flake.nix dependency and then
# put it into the devshell.
print(f"Icon: {icon}") base = VMBase(
new_vm = { icon=icon,
"icon": icon, name=entry.flake.clan_name,
"name": vm_config.clan_name, url=entry.flake.flake_url,
"url": flake_config.flake_url, status=False,
"_path": entry.path, )
"status": False, vm_list.append(VM(base=base))
}
vm_list.append(VM(base=VMBase(**new_vm)))
# start/end slices can be used for pagination # start/end slices can be used for pagination
return vm_list[start:end] return vm_list[start:end]