Merge pull request 'fixed history allowing adding the same clan twice' (#643) from Qubasa-main into main
This commit is contained in:
@@ -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:
|
||||||
@@ -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
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -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)
|
||||||
|
|||||||
@@ -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,9 +58,7 @@ 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 = inspect_flake(path, machine)
|
||||||
flake.flake_url = str(flake.flake_url)
|
flake.flake_url = str(flake.flake_url)
|
||||||
history = HistoryEntry(
|
history = HistoryEntry(
|
||||||
|
|||||||
@@ -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]
|
||||||
|
|||||||
Reference in New Issue
Block a user