Skip to content

Commit 13bf49d

Browse files
committed
enh: refactor to reduce duplicate code, fix uninstall for isolated_venv, add install support for type local,
Signed-off-by: habeck <habeck@us.ibm.com>
1 parent 27e04c5 commit 13bf49d

6 files changed

Lines changed: 1941 additions & 78 deletions

File tree

cpex/framework/models.py

Lines changed: 101 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1387,7 +1387,7 @@ class Monorepo(BaseModel):
13871387
package_folder: str
13881388

13891389

1390-
class PiPyRepo(BaseModel):
1390+
class PyPiRepo(BaseModel):
13911391
"""PyPi model.
13921392
Attributes:
13931393
name (str): The name of the pypi package.
@@ -1475,6 +1475,99 @@ def validate_version_constraint(cls, version_constraint: str | None) -> str | No
14751475
return version_constraint if version_constraint != "" else None
14761476

14771477

1478+
class GitRepo(BaseModel):
1479+
"""Git repository model.
1480+
Attributes:
1481+
git_repository: The URL of the git repository.
1482+
git_branch_tag_commit: The branch, tag or commit of the git repository.
1483+
"""
1484+
1485+
git_repository: str = Field(
1486+
title="URL",
1487+
description='The URL of the git repository. (e.g., "https://github.com/example/plugin.git")',
1488+
)
1489+
git_branch_tag_commit: Optional[str] = Field(
1490+
title="Branch, tag or commit",
1491+
description="The branch, tag or commit of the git repository.",
1492+
)
1493+
1494+
@field_validator("git_repository", mode="after")
1495+
@classmethod
1496+
def validate_git_repository(cls, git_repository: str | None) -> str | None:
1497+
"""Validate Git repository URL format.
1498+
1499+
Args:
1500+
git_repository: The Git repository URL to validate.
1501+
1502+
Returns:
1503+
The validated repository URL or None if none is set.
1504+
1505+
Raises:
1506+
ValueError: If the repository URL is invalid.
1507+
"""
1508+
if git_repository is not None and git_repository != "":
1509+
if not git_repository.strip():
1510+
raise ValueError("Git repository URL cannot be empty or whitespace")
1511+
1512+
# Support common Git URL formats: https://, git://, ssh://, git@
1513+
git_url_pattern = re.compile(
1514+
r"^(https?://|git://|git@)" r"[a-zA-Z0-9._-]+" r"(/|:)" r"[a-zA-Z0-9._/-]+" r"(\.git)?$"
1515+
)
1516+
1517+
if not git_url_pattern.match(git_repository):
1518+
raise ValueError(
1519+
f"Invalid Git repository URL '{git_repository}'. "
1520+
"Must be a valid Git URL (e.g., https://github.com/user/repo.git, "
1521+
"git@github.com:user/repo.git)"
1522+
)
1523+
1524+
# Additional validation for https/http URLs using existing validator
1525+
if git_repository.startswith(("http://", "https://")):
1526+
validate_plugin_url(git_repository, "Git repository URL")
1527+
1528+
return git_repository if git_repository != "" else None
1529+
1530+
@field_validator("git_branch_tag_commit", mode="after")
1531+
@classmethod
1532+
def validate_git_branch_tag_commit(cls, git_branch_tag_commit: str | None) -> str | None:
1533+
"""Validate Git branch, tag, or commit reference.
1534+
1535+
Args:
1536+
git_branch_tag_commit: The Git reference to validate.
1537+
1538+
Returns:
1539+
The validated reference or None if none is set.
1540+
1541+
Raises:
1542+
ValueError: If the reference is invalid.
1543+
"""
1544+
if git_branch_tag_commit is not None and git_branch_tag_commit != "":
1545+
if not git_branch_tag_commit.strip():
1546+
raise ValueError("Git branch/tag/commit cannot be empty or whitespace")
1547+
1548+
# Git refs can contain alphanumeric characters, hyphens, underscores, slashes, and periods
1549+
# Commit hashes are typically 7-40 hex characters
1550+
if not re.match(r"^[a-zA-Z0-9._/-]+$", git_branch_tag_commit):
1551+
raise ValueError(
1552+
f"Invalid Git branch/tag/commit '{git_branch_tag_commit}'. "
1553+
"Must contain only alphanumeric characters, hyphens, underscores, slashes, and periods."
1554+
)
1555+
1556+
# Check for common invalid patterns
1557+
if git_branch_tag_commit.startswith(("/", ".", "-")) or git_branch_tag_commit.endswith(("/", ".")):
1558+
raise ValueError(
1559+
f"Invalid Git branch/tag/commit '{git_branch_tag_commit}'. "
1560+
"Cannot start with /, ., or - or end with / or ."
1561+
)
1562+
1563+
if len(git_branch_tag_commit) > 255:
1564+
raise ValueError(
1565+
f"Git branch/tag/commit '{git_branch_tag_commit}' exceeds maximum length of 255 characters"
1566+
)
1567+
1568+
return git_branch_tag_commit if git_branch_tag_commit != "" else None
1569+
1570+
14781571
class PluginManifest(BaseModel):
14791572
"""Plugin manifest.
14801573
@@ -1487,6 +1580,10 @@ class PluginManifest(BaseModel):
14871580
tags (list[str]): a list of tags for making the plugin searchable.
14881581
available_hooks (list[str]): a list of the hook points where the plugin is callable.
14891582
default_config (dict[str, Any]): the default configurations.
1583+
monorepo (Monorepo): A git monorepo where the plugin originates (Initialized by cepx cli during plugin installation)
1584+
package_info: (PyPiRepo): The package name and version constraint of the package (Initialized by cepx cli during plugin installation)
1585+
local: The path to the locally installed plugin (Initialized by cepx cli during plugin installation)
1586+
git_repo: GitRepo: The git repo where the plugin originates (Initialized by cepx cli during plugin installation)
14901587
"""
14911588

14921589
name: str
@@ -1498,7 +1595,9 @@ class PluginManifest(BaseModel):
14981595
available_hooks: list[str]
14991596
default_config: dict[str, Any]
15001597
monorepo: Optional[Monorepo] = None
1501-
package_info: Optional[PiPyRepo] = None
1598+
package_info: Optional[PyPiRepo] = None
1599+
local: Optional[str] = None
1600+
git_repo: Optional[GitRepo] = None
15021601

15031602
def suggest_instance_name(self) -> str:
15041603
"""Suggest a name for the plugin instance.

0 commit comments

Comments
 (0)