a845cba925
tracker-policy-pr / check-pr (pull_request) Failing after 11s
test / integration-docker (pull_request) Successful in 20s
lint / lint (push) Successful in 59s
test / unit (pull_request) Successful in 2m10s
test / integration-firecracker (pull_request) Successful in 3m29s
test / coverage (pull_request) Successful in 38s
test / publish-infra (pull_request) Has been skipped
Move the nine root-level manifest modules into a bot_bottle/manifest/ package, dropping the redundant manifest_ prefix: the facade (manifest.py) becomes the package __init__ and keeps re-exporting Manifest / ManifestIndex and the piece types, while manifest_<x>.py become manifest/<x>.py (agent, bottle, egress, extends, git, loader, schema, util). Because the facade stays the package __init__, the ~13 callers that import `from bot_bottle.manifest import ...` are unchanged; only direct-piece imports move to `bot_bottle.manifest.<name>`. Inside the package, intra-manifest imports drop the prefix and the non-manifest siblings (log, yaml_subset, agent_provider) move to `..`. No behavior change; full unit suite green. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
57 lines
2.0 KiB
Python
57 lines
2.0 KiB
Python
"""Deploy-key provisioner interface and factory (PRD 0048).
|
|
|
|
The core defines the abstract contract; concrete implementations live
|
|
in `bot_bottle/contrib/<provider>/deploy_key_provisioner.py`. The
|
|
factory `get_provisioner` imports contrib modules lazily so that a
|
|
missing optional dependency in one provider doesn't break unrelated
|
|
features."""
|
|
|
|
from __future__ import annotations
|
|
|
|
from abc import ABC, abstractmethod
|
|
|
|
|
|
class DeployKeyCollisionError(RuntimeError):
|
|
"""Raised when a deploy key title or public key already exists on the repo."""
|
|
|
|
|
|
class DeployKeyProvisioner(ABC):
|
|
"""Manages a single deploy-key lifecycle on a remote forge."""
|
|
|
|
@abstractmethod
|
|
def create(self, owner_repo: str, title: str) -> tuple[str, bytes]:
|
|
"""Generate a keypair and register the public half as a
|
|
deploy key on the forge.
|
|
|
|
`owner_repo` is the `<owner>/<repo>` path (no `.git` suffix).
|
|
`title` is the human-readable label shown in the forge UI.
|
|
|
|
Returns `(key_id, private_key_bytes)` where `key_id` is opaque
|
|
to the caller and is only ever passed back to `delete`."""
|
|
|
|
@abstractmethod
|
|
def delete(self, owner_repo: str, key_id: str) -> None:
|
|
"""Delete the registered deploy key.
|
|
|
|
Must not raise if the key is already absent (HTTP 404 is
|
|
success). Must raise for all other failures so teardown halts."""
|
|
|
|
|
|
def get_provisioner(
|
|
provider: str, token: str, api_url: str
|
|
) -> DeployKeyProvisioner:
|
|
"""Instantiate the contrib provisioner for `provider`.
|
|
|
|
Raises `ManifestError` for unknown providers so the error surfaces
|
|
at parse time rather than at runtime."""
|
|
if provider == "gitea":
|
|
from bot_bottle.contrib.gitea.deploy_key_provisioner import (
|
|
GiteaDeployKeyProvisioner,
|
|
)
|
|
return GiteaDeployKeyProvisioner(token=token, api_url=api_url)
|
|
from .manifest.util import ManifestError
|
|
raise ManifestError(
|
|
f"unknown provisioned_key provider: {provider!r}; "
|
|
f"available: gitea"
|
|
)
|