Added remove & add commands
All checks were successful
Test before pr merge / test-lint (pull_request) Successful in 7s

This commit is contained in:
2025-10-11 11:05:04 +02:00
parent 104b736778
commit 45dcb400da
12 changed files with 164 additions and 15 deletions

View File

@@ -1,8 +1,8 @@
{
"name": "cpl",
"projects": [
"src/cpl-core/cpl.project.json",
"src/cpl-cli/cpl.project.json"
"src/cli/cpl.project.json",
"src/core/cpl.project.json"
],
"defaultProject": "cpl-cli",
"scripts": {

View File

@@ -11,11 +11,9 @@
"click": "~8.3.0"
},
"devDependencies": {
"black": "~25.9"
"black": "~25.9.0"
},
"references": [
"../cpl/cpl.project.json"
],
"references": [],
"main": "cpl/cli/main.py",
"directory": "cpl/cli"
}

View File

@@ -1,5 +1,9 @@
import traceback
import click
from cpl.core.console import Console
class AliasedGroup(click.Group):
def command(self, *args, **kwargs):
@@ -7,6 +11,7 @@ class AliasedGroup(click.Group):
def decorator(f):
cmd = super(AliasedGroup, self).command(*args, **kwargs)(f)
cmd.callback = self._handle_errors(cmd.callback)
for alias in aliases:
self.add_command(cmd, alias)
@@ -28,6 +33,23 @@ class AliasedGroup(click.Group):
with formatter.section("Commands"):
formatter.write_dl(commands)
@staticmethod
def _handle_errors(f):
def wrapper(*args, **kwargs):
try:
res = f(*args, **kwargs)
Console.write_line()
return res
except Exception as e:
tb = None
if "verbose" in kwargs and kwargs["verbose"]:
tb = traceback.format_exc()
Console.error(str(e), tb)
Console.write_line()
exit(-1)
return wrapper
@click.group(cls=AliasedGroup)
def cli(): ...

View File

@@ -0,0 +1,51 @@
from pathlib import Path
import click
from cpl.cli.cli import cli
from cpl.cli.model.project import Project
from cpl.cli.model.workspace import Workspace
from cpl.core.configuration import Configuration
from cpl.core.console import Console
@cli.command("add", aliases=["a"])
@click.argument("reference", type=click.STRING, required=True)
@click.argument("target", type=click.STRING, required=True)
@click.option("--verbose", is_flag=True, help="Enable verbose output")
def add(reference: str, target: str, verbose: bool):
workspace = None
if Configuration.get("workspace_path") is not None:
workspace = Workspace.from_file(Configuration.get("workspace_path"))
reference_project = _get_project_by_name_or_path(reference, workspace)
target_project = _get_project_by_name_or_path(target, workspace)
if reference_project.name == target_project.name:
raise ValueError("Cannot add a project as a dependency to itself!")
if reference_project.path in target_project.references:
raise ValueError(f"Project '{reference_project.name}' is already a reference of '{target_project.name}'")
target_project.references.append(reference_project.path)
target_project.save()
Console.write_line(f"Added '{reference_project.name}' to '{target_project.name}' project")
def _get_project_by_name_or_path(project: str, workspace=None) -> Project:
path = Path(project)
if path.exists() and path.is_dir():
return Project.from_file(path / "cpl.project.json")
if path.exists() and path.is_file():
if not path.name.endswith("cpl.project.json"):
raise ValueError(f"File '{path}' is not a valid cpl.project.json file.")
return Project.from_file(path)
if workspace is not None:
for p in workspace.actual_projects:
if p.name == project:
return Project.from_file(Path(p.path))
raise ValueError(f"Project '{project}' not found.")

View File

@@ -24,6 +24,7 @@ from cpl.core.console import Console
def install(package: str, project_path: str, project_name: str, dev: bool, verbose: bool):
project = resolve_project(Path(project_path), project_name)
if package is not None:
Console.write_line(dev)
Console.write_line(f"Installing {package} to '{project.name}':")
try:
Pip.command("install", package, verbose=verbose, path=project_path)
@@ -36,7 +37,9 @@ def install(package: str, project_path: str, project_name: str, dev: bool, verbo
Console.error(f"Package '{package_name}' not found after installation.")
return
project.dependencies[package_name] = Pip.apply_prefix(installed_version, Pip.get_package_full_version(package))
deps = project.dependencies if not dev else project.dev_dependencies
deps[package_name] = Pip.apply_prefix(installed_version, Pip.get_package_full_version(package))
project.save()
Console.write_line(f"Added {package_name}~{installed_version} to project dependencies.")
return

View File

@@ -0,0 +1,52 @@
from pathlib import Path
import click
from cpl.cli.cli import cli
from cpl.cli.model.project import Project
from cpl.cli.model.workspace import Workspace
from cpl.core.configuration import Configuration
from cpl.core.console import Console
@cli.command("remove", aliases=["rm"])
@click.argument("reference", type=click.STRING, required=True)
@click.argument("target", type=click.STRING, required=True)
@click.option("--verbose", is_flag=True, help="Enable verbose output")
def remove(reference: str, target: str, verbose: bool):
workspace = None
if Configuration.get("workspace_path") is not None:
workspace = Workspace.from_file(Configuration.get("workspace_path"))
reference_project = _get_project_by_name_or_path(reference, workspace)
target_project = _get_project_by_name_or_path(target, workspace)
if reference_project.name == target_project.name:
raise ValueError("Cannot add a project as a dependency to itself!")
if reference_project.path not in target_project.references:
raise ValueError(f"Project '{reference_project.name}' isn't a reference of '{target_project.name}'")
target_project.references.remove(reference_project.path)
target_project.save()
Console.write_line(f"Removed '{reference_project.name}' from '{target_project.name}' project")
def _get_project_by_name_or_path(project: str, workspace=None) -> Project:
path = Path(project)
if path.exists() and path.is_dir():
return Project.from_file(path / "cpl.project.json")
if path.exists() and path.is_file():
if not path.name.endswith("cpl.project.json"):
raise ValueError(f"File '{path}' is not a valid cpl.project.json file.")
return Project.from_file(path)
if workspace is not None:
for p in workspace.actual_projects:
if p.name == project:
return Project.from_file(Path(p.path))
raise ValueError(f"Project '{project}' not found.")

View File

@@ -29,7 +29,7 @@ def uninstall(package: str, project_path: str, project_name: str, dev: bool, ver
deps = project.dependencies if not dev else project.dev_dependencies
try:
Pip.command("install", package, verbose=verbose, path=project_path)
Pip.command("uninstall -y", package, verbose=verbose, path=project_path)
except subprocess.CalledProcessError as e:
Console.error(f"Failed to uninstall {package}: exit code {e.returncode}")
return
@@ -38,3 +38,6 @@ def uninstall(package: str, project_path: str, project_name: str, dev: bool, ver
del deps[package]
project.save()
Console.write_line(f"Removed {package} from project dependencies.")
return
Console.write_line(f"Package {package} was not found in project dependencies.")

View File

@@ -2,10 +2,12 @@ import os
from pathlib import Path
from cpl.cli.cli import cli
from cpl.cli.command.init import init
from cpl.cli.command.install import install
from cpl.cli.command.uninstall import uninstall
from cpl.cli.command.update import update
from cpl.cli.command.package.remove import remove
from cpl.cli.command.package.add import add
from cpl.cli.command.structure.init import init
from cpl.cli.command.package.install import install
from cpl.cli.command.package.uninstall import uninstall
from cpl.cli.command.package.update import update
from cpl.cli.command.version import version
from cpl.cli.model.workspace import Workspace
from cpl.cli.utils.custom_command import script_command
@@ -54,8 +56,8 @@ def configure():
cli.add_command(install)
cli.add_command(uninstall)
cli.add_command(update)
# cli.add_command(add)
# cli.add_command(remove)
cli.add_command(add)
cli.add_command(remove)
# run
# cli.add_command(run)

View File

@@ -32,7 +32,7 @@ class CPLStructureModel:
kwargs: Dict[str, Any] = {}
for name, param in list(sig.parameters.items())[1:]:
if name == "path":
kwargs[name] = path
kwargs[name] = str(path)
continue
if name in data:

View File

@@ -1,6 +1,7 @@
from typing import Optional, List, Dict
from cpl.cli.model.cpl_structure_model import CPLStructureModel
from cpl.cli.model.project import Project
class Workspace(CPLStructureModel):
@@ -45,6 +46,10 @@ class Workspace(CPLStructureModel):
def projects(self, value: List[str]):
self._projects = self._require_list_of_str(value, "projects")
@property
def actual_projects(self) -> List[Project]:
return [Project.from_file(p) for p in self._projects]
@property
def default_project(self) -> Optional[str]:
return self._default_project

View File

@@ -1,4 +1,5 @@
import json
import os
from pathlib import Path
from cpl.cli.model.project import Project

12
src/cli/run Executable file
View File

@@ -0,0 +1,12 @@
#!/bin/bash
set -e
cd ../
ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
export PYTHONPATH="$ROOT_DIR/core:$ROOT_DIR/cli:$PYTHONPATH"
old_dir="$(pwd)"
cd ../
python -m cpl.cli.main "$@"
cd "$old_dir"