Compare commits

...

2 Commits

Author SHA1 Message Date
f1b66d7996 Nix fmt doesn't complain anymore 2023-10-23 01:30:47 +02:00
7a354875c9 Fully working ui and cli 2023-10-23 01:23:06 +02:00
11 changed files with 60 additions and 219 deletions

View File

@@ -1,23 +1,27 @@
from typing import Dict, Optional, Tuple, Callable, Any, Mapping, List
from pathlib import Path
import ipdb
import logging
import multiprocessing as mp
import os
import shlex
import stat
import subprocess
from .dirs import find_git_repo_root
import multiprocessing as mp
from .types import FlakeName
import logging
import sys
import shlex
import time
from pathlib import Path
from typing import Any, Callable, Dict, List, Optional
import ipdb
log = logging.getLogger(__name__)
def command_exec(cmd: List[str], work_dir: Path, env: Dict[str, str]) -> None:
subprocess.run(cmd, check=True, env=env, cwd=work_dir.resolve())
def repro_env_break(work_dir: Path, env: Optional[Dict[str, str]] = None, cmd: Optional[List[str]] = None) -> None:
def repro_env_break(
work_dir: Path,
env: Optional[Dict[str, str]] = None,
cmd: Optional[List[str]] = None,
) -> None:
if env is None:
env = os.environ.copy()
else:
@@ -40,6 +44,7 @@ def repro_env_break(work_dir: Path, env: Optional[Dict[str, str]] = None, cmd: O
finally:
proc.terminate()
def write_command(command: str, loc: Path) -> None:
with open(loc, "w") as f:
f.write("#!/usr/bin/env bash\n")
@@ -47,6 +52,7 @@ def write_command(command: str, loc:Path) -> None:
st = os.stat(loc)
os.chmod(loc, st.st_mode | stat.S_IEXEC)
def spawn_process(func: Callable, **kwargs: Any) -> mp.Process:
mp.set_start_method(method="spawn")
proc = mp.Process(target=func, kwargs=kwargs)
@@ -59,7 +65,7 @@ def dump_env(env: Dict[str, str], loc: Path) -> None:
with open(loc, "w") as f:
f.write("#!/usr/bin/env bash\n")
for k, v in cenv.items():
if v.count('\n') > 0 or v.count("\"") > 0 or v.count("'") > 0:
if v.count("\n") > 0 or v.count('"') > 0 or v.count("'") > 0:
continue
f.write(f"export {k}='{v}'\n")
st = os.stat(loc)

View File

@@ -1,88 +0,0 @@
import shlex
import subprocess
from pathlib import Path
from typing import Optional
from clan_cli.dirs import find_git_repo_root
from clan_cli.errors import ClanError
from clan_cli.nix import nix_shell
# generic vcs agnostic commit function
def commit_file(
file_path: Path,
repo_dir: Optional[Path] = None,
commit_message: Optional[str] = None,
) -> None:
if repo_dir is None:
repo_dir = find_git_repo_root()
if repo_dir is None:
return
# check that the file is in the git repository and exists
if not Path(file_path).resolve().is_relative_to(repo_dir.resolve()):
raise ClanError(f"File {file_path} is not in the git repository {repo_dir}")
if not file_path.exists():
raise ClanError(f"File {file_path} does not exist")
# generate commit message if not provided
if commit_message is None:
# ensure that mentioned file path is relative to repo
commit_message = f"Add {file_path.relative_to(repo_dir)}"
# check if the repo is a git repo and commit
if (repo_dir / ".git").exists():
_commit_file_to_git(repo_dir, file_path, commit_message)
else:
return
def _commit_file_to_git(repo_dir: Path, file_path: Path, commit_message: str) -> None:
"""Commit a file to a git repository.
:param repo_dir: The path to the git repository.
:param file_path: The path to the file to commit.
:param commit_message: The commit message.
:raises ClanError: If the file is not in the git repository.
"""
cmd = nix_shell(
["git"],
["git", "-C", str(repo_dir), "add", str(file_path)],
)
# add the file to the git index
try:
subprocess.run(cmd, check=True)
except subprocess.CalledProcessError as e:
raise ClanError(
f"Failed to add {file_path} to git repository {repo_dir}:\n{shlex.join(cmd)}\n exited with {e.returncode}"
) from e
# check if there is a diff
cmd = nix_shell(
["git"],
["git", "-C", str(repo_dir), "diff", "--cached", "--exit-code"],
)
result = subprocess.run(cmd, cwd=repo_dir)
# if there is no diff, return
if result.returncode == 0:
return
# commit only that file
cmd = nix_shell(
["git"],
[
"git",
"-C",
str(repo_dir),
"commit",
"-m",
commit_message,
str(file_path.relative_to(repo_dir)),
],
)
try:
subprocess.run(
cmd,
check=True,
)
except subprocess.CalledProcessError as e:
raise ClanError(
f"Failed to commit {file_path} to git repository {repo_dir}:\n{shlex.join(cmd)}\n exited with {e.returncode}"
) from e

View File

@@ -1,25 +0,0 @@
import sys
from typing import IO, Any, Callable
def is_interactive() -> bool:
"""Returns true if the current process is interactive"""
return sys.stdin.isatty() and sys.stdout.isatty()
def color_text(code: int, file: IO[Any] = sys.stdout) -> Callable[[str], None]:
"""
Print with color if stderr is a tty
"""
def wrapper(text: str) -> None:
if file.isatty():
print(f"\x1b[{code}m{text}\x1b[0m", file=file)
else:
print(text, file=file)
return wrapper
warn = color_text(91, file=sys.stderr)
info = color_text(92, file=sys.stderr)

View File

@@ -3,11 +3,13 @@ from pathlib import Path
from typing import Any
from pydantic import AnyUrl, BaseModel, validator
from pydantic.tools import parse_obj_as
from ..dirs import clan_data_dir, clan_flakes_dir
from ..flakes.create import DEFAULT_URL
from ..types import validate_path
DEFAULT_URL = parse_obj_as(AnyUrl, "http://localhost:8000")
log = logging.getLogger(__name__)

View File

@@ -1,11 +1,6 @@
from enum import Enum
from typing import Dict, List
from pydantic import BaseModel, Field
from ..async_cmd import CmdOut
from ..task_manager import TaskStatus
from ..vms.inspect import VmConfig
from pydantic import BaseModel
class Status(Enum):
@@ -17,54 +12,3 @@ class Status(Enum):
class Machine(BaseModel):
name: str
status: Status
class MachineCreate(BaseModel):
name: str
class MachinesResponse(BaseModel):
machines: list[Machine]
class MachineResponse(BaseModel):
machine: Machine
class ConfigResponse(BaseModel):
config: dict
class SchemaResponse(BaseModel):
schema_: dict = Field(alias="schema")
class VmStatusResponse(BaseModel):
error: str | None
status: TaskStatus
class VmCreateResponse(BaseModel):
uuid: str
class FlakeAttrResponse(BaseModel):
flake_attrs: list[str]
class VmInspectResponse(BaseModel):
config: VmConfig
class FlakeAction(BaseModel):
id: str
uri: str
class FlakeCreateResponse(BaseModel):
cmd_out: Dict[str, CmdOut]
class FlakeResponse(BaseModel):
content: str
actions: List[FlakeAction]

View File

@@ -29,7 +29,6 @@ def setup_app() -> FastAPI:
app.include_router(health.router)
# Needs to be last in register. Because of wildcard route
app.include_router(root.router)
app.add_exception_handler(ClanError, clan_error_handler)

View File

@@ -13,6 +13,7 @@ from typing import Iterator
import uvicorn
from pydantic import AnyUrl, IPvAnyAddress
from pydantic.tools import parse_obj_as
from clan_cli.errors import ClanError
log = logging.getLogger(__name__)
@@ -25,9 +26,7 @@ def open_browser(base_url: AnyUrl, sub_url: str) -> None:
break
except OSError:
time.sleep(i)
url = parse_obj_as(
AnyUrl, f"{base_url}/{sub_url.removeprefix('/')}"
)
url = parse_obj_as(AnyUrl, f"{base_url}/{sub_url.removeprefix('/')}")
_open_browser(url)

View File

@@ -37,6 +37,10 @@ exclude = "clan_cli.nixpkgs"
module = "argcomplete.*"
ignore_missing_imports = true
[[tool.mypy.overrides]]
module = "ipdb.*"
ignore_missing_imports = true
[[tool.mypy.overrides]]
module = "jsonschema.*"
ignore_missing_imports = true
@@ -52,7 +56,7 @@ ignore_missing_imports = true
[tool.ruff]
line-length = 88
select = [ "E", "F", "I", "U", "N"]
select = [ "E", "F", "I", "N"]
ignore = [ "E501" ]
[tool.black]

View File

@@ -6,7 +6,7 @@ import {
CssBaseline,
IconButton,
ThemeProvider,
useMediaQuery
useMediaQuery,
} from "@mui/material";
import { StyledEngineProvider } from "@mui/material/styles";
import axios from "axios";