Skip to content

Publish

lcp.publish

Publish LCP manifests to the registry via GitHub Pull Request.

PublishError

Bases: Exception

Error during publish operation.

Source code in src/lcp/publish.py
class PublishError(Exception):
    """Error during publish operation."""

PublishResult dataclass

Result of a publish operation.

Source code in src/lcp/publish.py
@dataclass
class PublishResult:
    """Result of a publish operation."""

    pr_url: str
    pr_number: int
    manifest_path: str
    package_name: str
    package_version: str
    language: str

publish_manifest

publish_manifest(document: LCPDocument, token: str, registry_repo: str = _DEFAULT_REGISTRY_REPO) -> PublishResult

Publish an LCP manifest to the registry via GitHub Pull Request.

This function performs the full publish workflow:

  1. Authenticates with GitHub using the provided token
  2. Forks the registry repository (if not already forked)
  3. Creates a branch for the new manifest
  4. Uploads the manifest file to the correct registry path
  5. Opens a pull request with structured content and labels

Parameters:

Name Type Description Default
document LCPDocument

Validated LCP document to publish.

required
token str

GitHub personal access token with repo or public_repo scope.

required
registry_repo str

Registry repository in owner/name format (default: zazza123/lcp-registry).

_DEFAULT_REGISTRY_REPO

Returns:

Type Description
PublishResult

PublishResult with the PR URL and metadata.

Raises:

Type Description
PublishError

If any step of the publish workflow fails.

ValueError

If the registry repo format is invalid.

Source code in src/lcp/publish.py
def publish_manifest(
    document: LCPDocument,
    token: str,
    registry_repo: str = _DEFAULT_REGISTRY_REPO,
) -> PublishResult:
    """Publish an LCP manifest to the registry via GitHub Pull Request.

    This function performs the full publish workflow:

    1. Authenticates with GitHub using the provided token
    2. Forks the registry repository (if not already forked)
    3. Creates a branch for the new manifest
    4. Uploads the manifest file to the correct registry path
    5. Opens a pull request with structured content and labels

    Args:
        document: Validated LCP document to publish.
        token: GitHub personal access token with ``repo`` or
            ``public_repo`` scope.
        registry_repo: Registry repository in ``owner/name`` format
            (default: ``zazza123/lcp-registry``).

    Returns:
        ``PublishResult`` with the PR URL and metadata.

    Raises:
        PublishError: If any step of the publish workflow fails.
        ValueError: If the registry repo format is invalid.
    """
    # Validate registry repo format
    if "/" not in registry_repo or registry_repo.count("/") != 1:
        raise ValueError(
            f"Invalid registry repo format: '{registry_repo}'. "
            "Expected 'owner/name'."
        )

    lib = document.manifest.library
    name = lib.name
    version = lib.version
    language = lib.language

    # Prevent path traversal in package name and reject empty names
    if not name:
        raise PublishError("Package name must not be empty")
    if ".." in name or "/" in name or "\\" in name:
        raise PublishError(f"Invalid package name: '{name}'")

    manifest_path = f"manifests/{language}/{name[0].lower()}/{name}/{version}.lcp.json.gz"
    branch_name = f"lcp/add/{name}/{version}"

    from . import __version__ as lcp_version

    # Step 1: Authenticate and get username
    username = _get_authenticated_user(token)

    # Step 2: Fork the registry repo
    fork_repo = _ensure_fork(username, registry_repo, token)

    # Step 3: Create a branch
    _create_branch(fork_repo, branch_name, token)

    # Step 4: Upload the manifest (gzip-compressed)
    manifest_bytes = gzip.compress(document.to_json(indent=2).encode("utf-8"))
    _upload_manifest(
        fork_repo,
        branch_name,
        manifest_path,
        manifest_bytes,
        token,
        name,
        version,
    )

    # Step 5: Create the PR
    pr_body = _build_pr_body(document, manifest_path, lcp_version)
    pr_data = _create_pull_request(
        registry_repo,
        fork_repo,
        branch_name,
        name,
        version,
        language,
        pr_body,
        token,
    )

    pr_url = pr_data.get("html_url", "")
    pr_number = pr_data.get("number", 0)

    # Step 6: Try to add labels (best-effort)
    labels = _PR_LABELS + [language]
    _try_add_labels(registry_repo, pr_number, labels, token)

    return PublishResult(
        pr_url=pr_url,
        pr_number=pr_number,
        manifest_path=manifest_path,
        package_name=name,
        package_version=version,
        language=language,
    )