WIP: dev into master #184
@@ -11,7 +11,7 @@
|
|||||||
"click": "~8.3.0"
|
"click": "~8.3.0"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"black": "~25.9.0"
|
"black": "~25.9"
|
||||||
},
|
},
|
||||||
"references": [],
|
"references": [],
|
||||||
"main": "cpl/cli/main.py",
|
"main": "cpl/cli/main.py",
|
||||||
|
|||||||
@@ -1,26 +1,16 @@
|
|||||||
from pathlib import Path
|
|
||||||
|
|
||||||
import click
|
import click
|
||||||
|
|
||||||
from cpl.cli.cli import cli
|
from cpl.cli.cli import cli
|
||||||
from cpl.cli.model.project import Project
|
from cpl.cli.utils.structure import get_project_by_name_or_path
|
||||||
from cpl.cli.model.workspace import Workspace
|
|
||||||
from cpl.core.configuration import Configuration
|
|
||||||
from cpl.core.console import Console
|
from cpl.core.console import Console
|
||||||
|
|
||||||
|
|
||||||
@cli.command("add", aliases=["a"])
|
@cli.command("add", aliases=["a"])
|
||||||
@click.argument("reference", type=click.STRING, required=True)
|
@click.argument("reference", type=click.STRING, required=True)
|
||||||
@click.argument("target", 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):
|
||||||
def add(reference: str, target: str, verbose: bool):
|
reference_project = get_project_by_name_or_path(reference)
|
||||||
workspace = None
|
target_project = get_project_by_name_or_path(target)
|
||||||
|
|
||||||
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:
|
if reference_project.name == target_project.name:
|
||||||
raise ValueError("Cannot add a project as a dependency to itself!")
|
raise ValueError("Cannot add a project as a dependency to itself!")
|
||||||
@@ -30,22 +20,3 @@ def add(reference: str, target: str, verbose: bool):
|
|||||||
target_project.references.append(reference_project.path)
|
target_project.references.append(reference_project.path)
|
||||||
target_project.save()
|
target_project.save()
|
||||||
Console.write_line(f"Added '{reference_project.name}' to '{target_project.name}' project")
|
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.")
|
|
||||||
|
|||||||
@@ -1,38 +1,31 @@
|
|||||||
import subprocess
|
import subprocess
|
||||||
from pathlib import Path
|
|
||||||
|
|
||||||
import click
|
import click
|
||||||
|
|
||||||
from cpl.cli.cli import cli
|
from cpl.cli.cli import cli
|
||||||
from cpl.cli.utils.pip import Pip
|
from cpl.cli.utils.pip import Pip
|
||||||
from cpl.cli.utils.structure import resolve_project
|
from cpl.cli.utils.structure import get_project_by_name_or_path
|
||||||
from cpl.core.console import Console
|
from cpl.core.console import Console
|
||||||
|
|
||||||
|
|
||||||
@cli.command("install", aliases=["i"])
|
@cli.command("install", aliases=["i"])
|
||||||
@click.argument("package", type=click.STRING, required=False)
|
@click.argument("package", type=click.STRING, required=False)
|
||||||
@click.option(
|
@click.argument("project", type=click.STRING, required=False)
|
||||||
"--path",
|
|
||||||
"project_path",
|
|
||||||
type=click.Path(file_okay=False, exists=False),
|
|
||||||
default=".",
|
|
||||||
help="Path to project directory",
|
|
||||||
)
|
|
||||||
@click.option("--name", "project_name", help="Project name (lookup via workspace)")
|
|
||||||
@click.option("--dev", is_flag=True, help="Include dev dependencies")
|
@click.option("--dev", is_flag=True, help="Include dev dependencies")
|
||||||
@click.option("--verbose", is_flag=True, help="Enable verbose output")
|
@click.option("--verbose", is_flag=True, help="Enable verbose output")
|
||||||
def install(package: str, project_path: str, project_name: str, dev: bool, verbose: bool):
|
def install(package: str, project: str, dev: bool, verbose: bool):
|
||||||
project = resolve_project(Path(project_path), project_name)
|
project = get_project_by_name_or_path(project or "./")
|
||||||
|
|
||||||
if package is not None:
|
if package is not None:
|
||||||
Console.write_line(dev)
|
|
||||||
Console.write_line(f"Installing {package} to '{project.name}':")
|
Console.write_line(f"Installing {package} to '{project.name}':")
|
||||||
try:
|
try:
|
||||||
Pip.command("install", package, verbose=verbose, path=project_path)
|
Pip.command("install", package, verbose=verbose, path=project.path)
|
||||||
except subprocess.CalledProcessError as e:
|
except subprocess.CalledProcessError as e:
|
||||||
Console.error(f"Failed to install {package}: exit code {e.returncode}")
|
Console.error(f"Failed to install {package}: exit code {e.returncode}")
|
||||||
|
return
|
||||||
|
|
||||||
package_name = Pip.get_package_without_version(package)
|
package_name = Pip.get_package_without_version(package)
|
||||||
installed_version = Pip.get_package_version(package_name, path=project_path)
|
installed_version = Pip.get_package_version(package_name, path=project.path)
|
||||||
if installed_version is None:
|
if installed_version is None:
|
||||||
Console.error(f"Package '{package_name}' not found after installation.")
|
Console.error(f"Package '{package_name}' not found after installation.")
|
||||||
return
|
return
|
||||||
@@ -58,6 +51,6 @@ def install(package: str, project_path: str, project_name: str, dev: bool, verbo
|
|||||||
dep = Pip.normalize_dep(name, version)
|
dep = Pip.normalize_dep(name, version)
|
||||||
Console.write_line(f" -> {dep}")
|
Console.write_line(f" -> {dep}")
|
||||||
try:
|
try:
|
||||||
Pip.command("install", dep, verbose=verbose, path=project_path)
|
Pip.command("install", dep, verbose=verbose, path=project.path)
|
||||||
except subprocess.CalledProcessError as e:
|
except subprocess.CalledProcessError as e:
|
||||||
Console.error(f"Failed to install {dep}: exit code {e.returncode}")
|
Console.error(f"Failed to install {dep}: exit code {e.returncode}")
|
||||||
|
|||||||
@@ -1,26 +1,16 @@
|
|||||||
from pathlib import Path
|
|
||||||
|
|
||||||
import click
|
import click
|
||||||
|
|
||||||
from cpl.cli.cli import cli
|
from cpl.cli.cli import cli
|
||||||
from cpl.cli.model.project import Project
|
from cpl.cli.utils.structure import get_project_by_name_or_path
|
||||||
from cpl.cli.model.workspace import Workspace
|
|
||||||
from cpl.core.configuration import Configuration
|
|
||||||
from cpl.core.console import Console
|
from cpl.core.console import Console
|
||||||
|
|
||||||
|
|
||||||
@cli.command("remove", aliases=["rm"])
|
@cli.command("remove", aliases=["rm"])
|
||||||
@click.argument("reference", type=click.STRING, required=True)
|
@click.argument("reference", type=click.STRING, required=True)
|
||||||
@click.argument("target", 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):
|
||||||
def remove(reference: str, target: str, verbose: bool):
|
reference_project = get_project_by_name_or_path(reference)
|
||||||
workspace = None
|
target_project = get_project_by_name_or_path(target)
|
||||||
|
|
||||||
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:
|
if reference_project.name == target_project.name:
|
||||||
raise ValueError("Cannot add a project as a dependency to itself!")
|
raise ValueError("Cannot add a project as a dependency to itself!")
|
||||||
@@ -31,22 +21,3 @@ def remove(reference: str, target: str, verbose: bool):
|
|||||||
target_project.references.remove(reference_project.path)
|
target_project.references.remove(reference_project.path)
|
||||||
target_project.save()
|
target_project.save()
|
||||||
Console.write_line(f"Removed '{reference_project.name}' from '{target_project.name}' project")
|
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.")
|
|
||||||
|
|||||||
@@ -1,35 +1,28 @@
|
|||||||
import subprocess
|
import subprocess
|
||||||
from pathlib import Path
|
|
||||||
|
|
||||||
import click
|
import click
|
||||||
|
|
||||||
from cpl.cli.cli import cli
|
from cpl.cli.cli import cli
|
||||||
from cpl.cli.utils.pip import Pip
|
from cpl.cli.utils.pip import Pip
|
||||||
from cpl.cli.utils.structure import resolve_project
|
from cpl.cli.utils.structure import get_project_by_name_or_path
|
||||||
from cpl.core.console import Console
|
from cpl.core.console import Console
|
||||||
|
|
||||||
|
|
||||||
@cli.command("uninstall", aliases=["ui"])
|
@cli.command("uninstall", aliases=["ui"])
|
||||||
@click.argument("package", required=False)
|
@click.argument("package", required=False)
|
||||||
@click.option(
|
@click.argument("project", type=click.STRING, required=False)
|
||||||
"--path",
|
|
||||||
"project_path",
|
|
||||||
type=click.Path(file_okay=False, exists=False),
|
|
||||||
default=".",
|
|
||||||
help="Path to project directory",
|
|
||||||
)
|
|
||||||
@click.option("--name", "project_name", help="Project name (lookup via workspace)")
|
|
||||||
@click.option("--dev", is_flag=True, help="Include dev dependencies")
|
@click.option("--dev", is_flag=True, help="Include dev dependencies")
|
||||||
@click.option("--verbose", is_flag=True, help="Enable verbose output")
|
@click.option("--verbose", is_flag=True, help="Enable verbose output")
|
||||||
def uninstall(package: str, project_path: str, project_name: str, dev: bool, verbose: bool):
|
def uninstall(package: str, project: str, dev: bool, verbose: bool):
|
||||||
if package is None:
|
if package is None:
|
||||||
package = Console.read("Package name to uninstall: ").strip()
|
package = Console.read("Package name to uninstall: ").strip()
|
||||||
|
|
||||||
project = resolve_project(Path(project_path), project_name)
|
project = get_project_by_name_or_path(project or "./")
|
||||||
|
|
||||||
deps = project.dependencies if not dev else project.dev_dependencies
|
deps = project.dependencies if not dev else project.dev_dependencies
|
||||||
|
|
||||||
try:
|
try:
|
||||||
Pip.command("uninstall -y", package, verbose=verbose, path=project_path)
|
Pip.command("uninstall -y", package, verbose=verbose, path=project.path)
|
||||||
except subprocess.CalledProcessError as e:
|
except subprocess.CalledProcessError as e:
|
||||||
Console.error(f"Failed to uninstall {package}: exit code {e.returncode}")
|
Console.error(f"Failed to uninstall {package}: exit code {e.returncode}")
|
||||||
return
|
return
|
||||||
|
|||||||
@@ -1,28 +1,21 @@
|
|||||||
import subprocess
|
import subprocess
|
||||||
from pathlib import Path
|
|
||||||
|
|
||||||
import click
|
import click
|
||||||
|
|
||||||
from cpl.cli.cli import cli
|
from cpl.cli.cli import cli
|
||||||
from cpl.cli.utils.pip import Pip
|
from cpl.cli.utils.pip import Pip
|
||||||
from cpl.cli.utils.structure import resolve_project
|
from cpl.cli.utils.structure import get_project_by_name_or_path
|
||||||
from cpl.core.console import Console
|
from cpl.core.console import Console
|
||||||
|
|
||||||
|
|
||||||
@cli.command("update", aliases=["u"])
|
@cli.command("update", aliases=["u"])
|
||||||
@click.argument("package", type=click.STRING, required=False)
|
@click.argument("package", type=click.STRING, required=False)
|
||||||
@click.option(
|
@click.argument("project", type=click.STRING, required=False)
|
||||||
"--path",
|
|
||||||
"project_path",
|
|
||||||
type=click.Path(file_okay=False, exists=False),
|
|
||||||
default=".",
|
|
||||||
help="Path to project directory",
|
|
||||||
)
|
|
||||||
@click.option("--name", "project_name", help="Project name (lookup via workspace)")
|
|
||||||
@click.option("--dev", is_flag=True, help="Include dev dependencies")
|
@click.option("--dev", is_flag=True, help="Include dev dependencies")
|
||||||
@click.option("--verbose", is_flag=True, help="Enable verbose output")
|
@click.option("--verbose", is_flag=True, help="Enable verbose output")
|
||||||
def update(package: str, project_path: str, project_name: str, dev: bool, verbose: bool):
|
def update(package: str, project: str, dev: bool, verbose: bool):
|
||||||
project = resolve_project(Path(project_path), project_name)
|
project = get_project_by_name_or_path(project or "./")
|
||||||
|
|
||||||
deps: dict = project.dependencies
|
deps: dict = project.dependencies
|
||||||
if dev:
|
if dev:
|
||||||
deps = project.dev_dependencies
|
deps = project.dev_dependencies
|
||||||
@@ -40,13 +33,13 @@ def update(package: str, project_path: str, project_name: str, dev: bool, verbos
|
|||||||
"install --upgrade",
|
"install --upgrade",
|
||||||
f"{Pip.normalize_dep(package, old_spec)}",
|
f"{Pip.normalize_dep(package, old_spec)}",
|
||||||
verbose=verbose,
|
verbose=verbose,
|
||||||
path=project_path,
|
path=project.path,
|
||||||
)
|
)
|
||||||
except subprocess.CalledProcessError as e:
|
except subprocess.CalledProcessError as e:
|
||||||
Console.error(f"Failed to install {package}: exit code {e.returncode}")
|
Console.error(f"Failed to install {package}: exit code {e.returncode}")
|
||||||
return
|
return
|
||||||
|
|
||||||
installed_version = Pip.get_package_version(package, path=project_path)
|
installed_version = Pip.get_package_version(package, path=project.path)
|
||||||
if installed_version is None:
|
if installed_version is None:
|
||||||
Console.error(f"Package '{package}' not found after update.")
|
Console.error(f"Package '{package}' not found after update.")
|
||||||
return
|
return
|
||||||
@@ -66,12 +59,12 @@ def update(package: str, project_path: str, project_name: str, dev: bool, verbos
|
|||||||
dep = Pip.normalize_dep(name, version)
|
dep = Pip.normalize_dep(name, version)
|
||||||
Console.write_line(f" -> {dep}")
|
Console.write_line(f" -> {dep}")
|
||||||
try:
|
try:
|
||||||
Pip.command("install --upgrade", dep, verbose=verbose, path=project_path)
|
Pip.command("install --upgrade", dep, verbose=verbose, path=project.path)
|
||||||
except subprocess.CalledProcessError as e:
|
except subprocess.CalledProcessError as e:
|
||||||
Console.error(f"Failed to update {dep}: exit code {e.returncode}")
|
Console.error(f"Failed to update {dep}: exit code {e.returncode}")
|
||||||
return
|
return
|
||||||
|
|
||||||
installed_version = Pip.get_package_version(name, path=project_path)
|
installed_version = Pip.get_package_version(name, path=project.path)
|
||||||
if installed_version is None:
|
if installed_version is None:
|
||||||
Console.error(f"Package '{name}' not found after update.")
|
Console.error(f"Package '{name}' not found after update.")
|
||||||
continue
|
continue
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
import os.path
|
||||||
import re
|
import re
|
||||||
import subprocess
|
import subprocess
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
@@ -97,19 +98,22 @@ class Pip:
|
|||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def command(command: str, *args, verbose: bool = False, path: str = None):
|
def command(command: str, *args, verbose: bool = False, path: str = None):
|
||||||
|
if path is not None and Path(path).is_file():
|
||||||
|
path = os.path.dirname(path)
|
||||||
|
|
||||||
venv_path = ensure_venv(Path(path or "./"))
|
venv_path = ensure_venv(Path(path or "./"))
|
||||||
pip = get_venv_pip(venv_path)
|
pip = get_venv_pip(venv_path)
|
||||||
|
|
||||||
if verbose:
|
if verbose:
|
||||||
Console.write_line()
|
Console.write_line()
|
||||||
|
Console.write_line(f"Running: {pip} {command} {''.join(args)}")
|
||||||
|
|
||||||
subprocess.run(
|
subprocess.run(
|
||||||
f"{pip} {command} {''.join(args)}",
|
[*pip.split(), command, *args],
|
||||||
shell=True,
|
|
||||||
check=True,
|
check=True,
|
||||||
stdin=subprocess.DEVNULL if not verbose else None,
|
stdin=subprocess.DEVNULL if not verbose else None,
|
||||||
stdout=subprocess.DEVNULL if not verbose else None,
|
stdout=subprocess.DEVNULL if not verbose else None,
|
||||||
stderr=subprocess.STDOUT if not verbose else None,
|
stderr=subprocess.DEVNULL if not verbose else None,
|
||||||
)
|
)
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
@@ -119,8 +123,7 @@ class Pip:
|
|||||||
|
|
||||||
try:
|
try:
|
||||||
result = subprocess.run(
|
result = subprocess.run(
|
||||||
f"{pip} show {package}",
|
[*pip.split(), "show", package],
|
||||||
shell=True,
|
|
||||||
check=True,
|
check=True,
|
||||||
capture_output=True,
|
capture_output=True,
|
||||||
text=True,
|
text=True,
|
||||||
@@ -140,8 +143,7 @@ class Pip:
|
|||||||
pip = get_venv_pip(venv_path)
|
pip = get_venv_pip(venv_path)
|
||||||
try:
|
try:
|
||||||
result = subprocess.run(
|
result = subprocess.run(
|
||||||
f"{pip} list --format=freeze",
|
[*pip.split(), "list", "--format=freeze"],
|
||||||
shell=True,
|
|
||||||
check=True,
|
check=True,
|
||||||
capture_output=True,
|
capture_output=True,
|
||||||
text=True,
|
text=True,
|
||||||
@@ -163,8 +165,7 @@ class Pip:
|
|||||||
|
|
||||||
try:
|
try:
|
||||||
result = subprocess.run(
|
result = subprocess.run(
|
||||||
f"{pip} --version",
|
[*pip.split(), "--version"],
|
||||||
shell=True,
|
|
||||||
check=True,
|
check=True,
|
||||||
capture_output=True,
|
capture_output=True,
|
||||||
text=True,
|
text=True,
|
||||||
|
|||||||
@@ -1,9 +1,8 @@
|
|||||||
import json
|
|
||||||
import os
|
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
|
|
||||||
from cpl.cli.model.project import Project
|
from cpl.cli.model.project import Project
|
||||||
from cpl.cli.model.workspace import Workspace
|
from cpl.cli.model.workspace import Workspace
|
||||||
|
from cpl.core.configuration import Configuration
|
||||||
from cpl.core.console import Console
|
from cpl.core.console import Console
|
||||||
|
|
||||||
|
|
||||||
@@ -29,3 +28,33 @@ def resolve_project(path: Path, name: str | None) -> Project:
|
|||||||
|
|
||||||
Console.error(f"Could not find project file '{path}'")
|
Console.error(f"Could not find project file '{path}'")
|
||||||
exit(1)
|
exit(1)
|
||||||
|
|
||||||
|
def get_project_by_name_or_path(project: str) -> Project:
|
||||||
|
if project is None:
|
||||||
|
raise ValueError("Project name or path must be provided.")
|
||||||
|
|
||||||
|
workspace = None
|
||||||
|
if Configuration.get("workspace_path") is not None:
|
||||||
|
workspace = Workspace.from_file(Configuration.get("workspace_path"))
|
||||||
|
|
||||||
|
path = Path(project)
|
||||||
|
if path.exists() and path.is_dir() and (path / "cpl.project.json").exists():
|
||||||
|
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))
|
||||||
|
|
||||||
|
if workspace.default_project is not None:
|
||||||
|
for p in workspace.actual_projects:
|
||||||
|
if p.name == workspace.default_project:
|
||||||
|
return Project.from_file(Path(p.path))
|
||||||
|
|
||||||
|
raise ValueError(f"Project '{project}' not found.")
|
||||||
Reference in New Issue
Block a user