feat(prd-0048): implement SSH deploy-key provisioning with contrib/gitea
- manifest_git.py: add ProvisionedKeyConfig dataclass; extend GitEntry with ProvisionedKey field (optional); make IdentityFile default to "" so provisioned_key entries can be constructed without a static path; add _parse_provisioned_key_config; update from_repos_entry to accept provisioned_key as an alternative to identity (mutually exclusive, parser rejects both-or-neither) - deploy_key_provisioner.py (new): DeployKeyProvisioner ABC with create() and delete() abstract methods; get_provisioner() factory with lazy contrib import for gitea - contrib/gitea/deploy_key_provisioner.py (new): GiteaDeployKeyProvisioner generating ed25519 keypairs via ssh-keygen and managing them through the Gitea deploy-key API (POST/DELETE); 404 on delete is success; all other errors raise RuntimeError - git_gate.py: add _provision_dynamic_key() called in GitGate.prepare() for entries with ProvisionedKey — generates key, writes private key and key ID files to stage_dir, patches GitGateUpstream.identity_file; add revoke_git_gate_provisioned_keys() for teardown — raises on failure - docker/launch.py: call revoke_git_gate_provisioned_keys() in teardown() after stack.close() so revocation runs after containers stop and failures propagate (not suppressed) - smolmachines/launch.py: extract _teardown_smolmachines() helper that catches stack.close() errors (warn + re-raise) then calls revocation; same fatal-on-failure contract as docker backend - test_manifest_git.py: 9 new cases for provisioned_key parsing - test_deploy_key_provisioner.py (new): factory smoke tests - test_contrib_gitea_deploy_key.py (new): create/delete/error/split tests Closes #169
This commit is contained in:
@@ -43,6 +43,7 @@ from pathlib import Path
|
||||
from typing import Callable, Generator
|
||||
|
||||
from ...egress import egress_resolve_token_values
|
||||
from ...git_gate import revoke_git_gate_provisioned_keys
|
||||
from ...log import info, warn
|
||||
from . import network as network_mod
|
||||
from . import util as docker_mod
|
||||
@@ -51,6 +52,7 @@ from .bottle_plan import DockerBottlePlan
|
||||
from .bottle_state import (
|
||||
bottle_state_dir,
|
||||
egress_state_dir,
|
||||
git_gate_state_dir,
|
||||
pipelock_state_dir,
|
||||
)
|
||||
from .compose import (
|
||||
@@ -84,6 +86,9 @@ def launch(
|
||||
Teardown on exit."""
|
||||
stack = ExitStack()
|
||||
|
||||
_bottle_for_revoke = plan.spec.manifest.bottle_for(plan.spec.agent_name)
|
||||
_git_gate_dir_for_revoke = git_gate_state_dir(plan.slug)
|
||||
|
||||
def teardown() -> None:
|
||||
try:
|
||||
stack.close()
|
||||
@@ -92,6 +97,9 @@ def launch(
|
||||
f"teardown failed for container {plan.container_name}"
|
||||
f" (compose-down): {exc!r}"
|
||||
)
|
||||
revoke_git_gate_provisioned_keys(
|
||||
_bottle_for_revoke, _git_gate_dir_for_revoke
|
||||
)
|
||||
|
||||
try:
|
||||
# Step 1: agent image build. Sidecar images get built lazily by
|
||||
|
||||
Reference in New Issue
Block a user