Skip to content

Deployment Capability

DeploymentCapability is the contract base for a capability that acts on a deployable repository: enumerate refs and commits, compare them, cut tags and releases, and trigger CI workflow runs. Bind it with a Capability(kind='deployment', handler=...) in the plugin's manifest. Deployment is typically paired with an identity capability (set requires_identity=True) so deploy actions run as the human user rather than a shared service principal.

Surfaces: ui, api.

See Authoring Plugins for the manifest, capabilities, context, credential decryption, and error conventions shared by every plugin.

from imbi.common.plugins import (
    CompareResult,
    Commit,
    DeploymentCapability,
    DeploymentRun,
    PluginContext,
    Ref,
)


class GitHubDeployment(DeploymentCapability):
    async def list_refs(
        self,
        ctx: PluginContext,
        credentials: dict[str, str],
        kind: str = 'all',
        query: str | None = None,
    ) -> list[Ref]:
        ...

    async def list_commits(
        self,
        ctx: PluginContext,
        credentials: dict[str, str],
        ref: str,
        limit: int = 25,
    ) -> list[Commit]:
        ...

    async def resolve_committish(
        self,
        ctx: PluginContext,
        credentials: dict[str, str],
        committish: str,
    ) -> Commit:
        ...

    async def compare(
        self,
        ctx: PluginContext,
        credentials: dict[str, str],
        base: str,
        head: str,
    ) -> CompareResult:
        ...

    async def trigger_deployment(
        self,
        ctx: PluginContext,
        credentials: dict[str, str],
        ref_or_sha: str,
        inputs: dict[str, str] | None = None,
    ) -> DeploymentRun:
        ...

    async def get_deployment_status(
        self,
        ctx: PluginContext,
        credentials: dict[str, str],
        run_id: str,
    ) -> DeploymentRun:
        ...

Required methods

  • list_refs — enumerate branches / tags / the default ref, optionally filtered by kind and a query substring.
  • list_commits — list commits reachable from ref, newest first.
  • resolve_committish — hydrate a single branch / tag / SHA into a Commit.
  • compare — return a CompareResult for base..head (ahead / behind counts, commits, diffstat).
  • trigger_deployment — dispatch a CI workflow / pipeline run for ref_or_sha with optional inputs, returning a DeploymentRun.
  • get_deployment_status — poll a previously triggered run by run_id.

Optional methods

These default to returning 'unknown' or None, or raising NotImplementedError; implement only what the remote supports.

  • get_check_status — aggregate CI check-runs into a single CheckStatus (pass / fail / warn / unknown) for a ref. Powers the release-train green/red dot.
  • create_tag / create_release — mint an annotated tag and a release on the remote; required only for the Promote flow.
  • list_workflows — list CI workflow files so the UI can populate a workflow dropdown when an operator wires up the per-environment dispatch edge.
  • list_recent_deployments — return the most recent deployments per environment for the resync flow that backfills Release nodes and DEPLOYED_TO edges when webhook delivery has lapsed. Capabilities that set the supports_deployment_sync hint must implement this; each returned RemoteDeployment must carry a stable external_run_id so the host can dedupe. Status values use the host's canonical DeploymentEventStatus vocabulary (pending, in_progress, success, failed, rolled_back). To attribute the deployer to an Imbi user, populate creator (the remote login, for display) and creator_subject (the remote's stable identity subject — e.g. the numeric GitHub user id); the host resolves the latter through the Integration's identity capability. When the deployment targets a tagged release, populate release_notes with the release's notes body (e.g. a GitHub release's "What's Changed" markdown); the host persists it as the Release node's notes, distinct from the short description deploy note.
  • get_release_notes — return the remote release's notes body for a given tag. The tag-keyed counterpart to list_recent_deployments' release_notes field: it lets the host enrich a Release node's notes on paths that only know the tag — a webhook that created the release from a deployment event (which carries no body), or a resync whose deployment ref was a raw SHA. Best-effort: capabilities without a release concept, or that cannot resolve one for the tag, return None (the default) so the host never fails a write on a missing or unreadable release.

Hints

  • supports_deployment_sync — the capability implements list_recent_deployments for the resync flow.
  • cacheable — the host may cache reads from this capability.

When a deployment call creates, renames, or discovers the project's canonical repository URL, set ctx.link_writeback so the host self-heals the stored project link. See Plugin Context.

API reference

DeploymentCapability

Bases: CapabilityHandler

Act on a repo: enumerate refs/commits, compare them, create tags/releases, and trigger CI workflow runs.

create_deployment_artifact async

create_deployment_artifact(
    ctx: PluginContext,
    credentials: dict[str, str],
    ref: str,
    version: str,
    inputs: dict[str, str] | None = None,
) -> ArtifactRun

Start the build that produces the deployable artifact.

The step :meth:trigger_deployment currently assumes has already happened. ref is the committish to build from and version the release identity the build is expected to publish under -- the remote build is what cuts the tag and pushes any version-bump commits, so neither exists yet when this returns.

Optional and deliberately separate from :meth:trigger_deployment: the two are distinct stages with host handling between them, not two halves of one call. Returns as soon as the build is accepted; the host observes completion via :meth:get_artifact_run_status (or a webhook) and only then proceeds to deploy.

Capabilities whose remote has no build concept raise :class:NotImplementedError.

Source code in libraries/common/src/imbi/common/plugins/base.py
async def create_deployment_artifact(
    self,
    ctx: PluginContext,
    credentials: dict[str, str],
    ref: str,
    version: str,
    inputs: dict[str, str] | None = None,
) -> ArtifactRun:
    """Start the build that *produces* the deployable artifact.

    The step :meth:`trigger_deployment` currently assumes has already
    happened.  ``ref`` is the committish to build from and ``version``
    the release identity the build is expected to publish under -- the
    remote build is what cuts the tag and pushes any version-bump
    commits, so neither exists yet when this returns.

    Optional and deliberately separate from
    :meth:`trigger_deployment`: the two are distinct stages with host
    handling between them, not two halves of one call.  Returns as
    soon as the build is *accepted*; the host observes completion via
    :meth:`get_artifact_run_status` (or a webhook) and only then
    proceeds to deploy.

    Capabilities whose remote has no build concept raise
    :class:`NotImplementedError`.
    """
    del ctx, credentials, ref, version, inputs
    raise NotImplementedError

create_release async

create_release(
    ctx: PluginContext,
    credentials: dict[str, str],
    tag: str,
    name: str,
    body_markdown: str,
    prerelease: bool = False,
) -> ReleaseInfo

Create a release on the remote (e.g. a GitHub Release).

Optional — paired with :meth:create_tag. Capabilities without a release concept raise :class:NotImplementedError.

Source code in libraries/common/src/imbi/common/plugins/base.py
async def create_release(
    self,
    ctx: PluginContext,
    credentials: dict[str, str],
    tag: str,
    name: str,
    body_markdown: str,
    prerelease: bool = False,
) -> ReleaseInfo:
    """Create a release on the remote (e.g. a GitHub Release).

    Optional — paired with :meth:`create_tag`.  Capabilities without a
    release concept raise :class:`NotImplementedError`.
    """
    raise NotImplementedError

create_tag async

create_tag(
    ctx: PluginContext,
    credentials: dict[str, str],
    sha: str,
    tag: str,
    message: str,
) -> RefInfo

Create an annotated tag on the remote.

Optional — only required for the Promote flow. Capabilities that cannot mint tags raise :class:NotImplementedError; the host surfaces the error to the caller.

Source code in libraries/common/src/imbi/common/plugins/base.py
async def create_tag(
    self,
    ctx: PluginContext,
    credentials: dict[str, str],
    sha: str,
    tag: str,
    message: str,
) -> RefInfo:
    """Create an annotated tag on the remote.

    Optional — only required for the Promote flow.  Capabilities that
    cannot mint tags raise :class:`NotImplementedError`; the host
    surfaces the error to the caller.
    """
    raise NotImplementedError

get_artifact_run_status async

get_artifact_run_status(
    ctx: PluginContext,
    credentials: dict[str, str],
    run_id: str,
) -> ArtifactRun

Report a build run's status.

The :class:ArtifactRun counterpart to :meth:get_deployment_status; run_id is what :meth:create_deployment_artifact returned, resolved against the remote's build endpoint rather than its deployment one.

Optional -- paired with :meth:create_deployment_artifact.

Source code in libraries/common/src/imbi/common/plugins/base.py
async def get_artifact_run_status(
    self,
    ctx: PluginContext,
    credentials: dict[str, str],
    run_id: str,
) -> ArtifactRun:
    """Report a build run's status.

    The :class:`ArtifactRun` counterpart to
    :meth:`get_deployment_status`; ``run_id`` is what
    :meth:`create_deployment_artifact` returned, resolved against the
    remote's *build* endpoint rather than its deployment one.

    Optional -- paired with :meth:`create_deployment_artifact`.
    """
    del ctx, credentials, run_id
    raise NotImplementedError

get_check_status async

get_check_status(
    ctx: PluginContext,
    credentials: dict[str, str],
    committish: str,
) -> CheckStatus

Aggregate CI check-runs status for a ref / SHA / tag.

Optional — used by the release-train read path to surface a green/red dot per env's currently-deployed version. Capabilities without a CI concept return 'unknown' (the default).

Source code in libraries/common/src/imbi/common/plugins/base.py
async def get_check_status(
    self,
    ctx: PluginContext,
    credentials: dict[str, str],
    committish: str,
) -> CheckStatus:
    """Aggregate CI check-runs status for a ref / SHA / tag.

    Optional — used by the release-train read path to surface a
    green/red dot per env's currently-deployed version.  Capabilities
    without a CI concept return ``'unknown'`` (the default).
    """
    del ctx, credentials, committish
    return 'unknown'

get_release async

get_release(
    ctx: PluginContext,
    credentials: dict[str, str],
    tag: str,
) -> RemoteRelease | None

Return the remote release for tag, metadata included.

The richer counterpart to :meth:get_release_notes: same lookup, but it also carries who cut the release on the remote, its title, and its URL, so the host can attribute a release it only observed to a person instead of to the process that recorded it.

Optional. Capabilities without a release concept, or that cannot resolve one for tag, return None (the default); hosts fall back to :meth:get_release_notes for the body alone.

Source code in libraries/common/src/imbi/common/plugins/base.py
async def get_release(
    self,
    ctx: PluginContext,
    credentials: dict[str, str],
    tag: str,
) -> RemoteRelease | None:
    """Return the remote release for ``tag``, metadata included.

    The richer counterpart to :meth:`get_release_notes`: same lookup,
    but it also carries who cut the release on the remote, its title,
    and its URL, so the host can attribute a release it only observed
    to a person instead of to the process that recorded it.

    Optional. Capabilities without a release concept, or that cannot
    resolve one for ``tag``, return ``None`` (the default); hosts fall
    back to :meth:`get_release_notes` for the body alone.
    """
    del ctx, credentials, tag
    return None

get_release_notes async

get_release_notes(
    ctx: PluginContext,
    credentials: dict[str, str],
    tag: str,
) -> str | None

Return the remote release's notes body for tag.

Optional -- lets the host enrich a Release node's notes with the remote release body (e.g. a GitHub release's "What's Changed" markdown) on paths that only know the tag, such as a webhook that created the release from a deployment event (which carries no body) or a resync whose deployment ref was a raw SHA. This is the tag-keyed counterpart to the release_notes field :meth:list_recent_deployments populates from a deployment's ref.

Best-effort: capabilities without a release concept, or that cannot resolve one for tag, return None (the default) so the host never fails a write on a missing or unreadable release.

Source code in libraries/common/src/imbi/common/plugins/base.py
async def get_release_notes(
    self,
    ctx: PluginContext,
    credentials: dict[str, str],
    tag: str,
) -> str | None:
    """Return the remote release's notes body for ``tag``.

    Optional -- lets the host enrich a ``Release`` node's notes with
    the remote release body (e.g. a GitHub release's "What's Changed"
    markdown) on paths that only know the tag, such as a webhook that
    created the release from a deployment event (which carries no
    body) or a resync whose deployment ``ref`` was a raw SHA.  This is
    the tag-keyed counterpart to the ``release_notes`` field
    :meth:`list_recent_deployments` populates from a deployment's ref.

    Best-effort: capabilities without a release concept, or that
    cannot resolve one for ``tag``, return ``None`` (the default) so
    the host never fails a write on a missing or unreadable release.
    """
    del ctx, credentials, tag
    return None

list_recent_deployments async

list_recent_deployments(
    ctx: PluginContext,
    credentials: dict[str, str],
    environments: list[str],
    limit: int = 1,
) -> list[RemoteDeployment]

Return the most recent limit deployments per environment.

Optional -- powers the deployment resync flow that backfills Release nodes and DEPLOYED_TO edges when webhook delivery has lapsed. Capabilities that advertise the supports_deployment_sync hint MUST implement this; others raise :class:NotImplementedError (the host treats that as "skip resync").

environments is the remote-facing list of environment names the host wants populated, in the project's preferred order. Capabilities should ignore environments their remote does not know about (rather than raising) so a partial resync still succeeds. Returned events MUST carry a stable external_run_id so the host can dedupe. When a deployment targets a tagged release, populate release_notes with the release's notes body (distinct from the short description deploy note) so the host can persist it as the Release node's notes.

Source code in libraries/common/src/imbi/common/plugins/base.py
async def list_recent_deployments(
    self,
    ctx: PluginContext,
    credentials: dict[str, str],
    environments: list[str],
    limit: int = 1,
) -> list[RemoteDeployment]:
    """Return the most recent ``limit`` deployments per environment.

    Optional -- powers the deployment resync flow that backfills
    ``Release`` nodes and ``DEPLOYED_TO`` edges when webhook delivery
    has lapsed.  Capabilities that advertise the
    ``supports_deployment_sync`` hint MUST implement this; others
    raise :class:`NotImplementedError` (the host treats that as
    "skip resync").

    ``environments`` is the remote-facing list of environment names
    the host wants populated, in the project's preferred order.
    Capabilities should ignore environments their remote does not know
    about (rather than raising) so a partial resync still succeeds.
    Returned events MUST carry a stable ``external_run_id`` so the
    host can dedupe.  When a deployment targets a tagged release,
    populate ``release_notes`` with the release's notes body (distinct
    from the short ``description`` deploy note) so the host can persist
    it as the ``Release`` node's notes.
    """
    del ctx, credentials, environments, limit
    raise NotImplementedError

list_workflows async

list_workflows(
    ctx: PluginContext, credentials: dict[str, str]
) -> list[WorkflowFile]

List CI workflow files defined in the project's remote repo.

Optional — used by the UI to populate a workflow dropdown when an operator configures assignment env_payloads. Capabilities without a workflow concept raise :class:NotImplementedError.

Source code in libraries/common/src/imbi/common/plugins/base.py
async def list_workflows(
    self,
    ctx: PluginContext,
    credentials: dict[str, str],
) -> list[WorkflowFile]:
    """List CI workflow files defined in the project's remote repo.

    Optional — used by the UI to populate a workflow dropdown when an
    operator configures assignment ``env_payloads``.  Capabilities
    without a workflow concept raise :class:`NotImplementedError`.
    """
    del ctx, credentials
    raise NotImplementedError

Ref

Bases: BaseModel

A branch, tag, or default-ref pointer on a deployable repo.

Commit

Bases: BaseModel

A commit on a deployable repo, hydrated for UI display.

CompareResult

Bases: BaseModel

Result of comparing two commit-ish refs (base..head).

RefInfo

Bases: BaseModel

Metadata returned after creating a ref (e.g. an annotated tag).

ReleaseInfo

Bases: BaseModel

Metadata returned after creating a release on the remote.

WorkflowFile

Bases: BaseModel

A CI workflow file discoverable in the project's remote repo.

Returned by :meth:DeploymentCapability.list_workflows so the UI can populate a workflow dropdown when an operator wires up the per-environment dispatch edge. id is the remote's stable identifier (e.g. the GitHub workflow id); path is the repo- relative file path; name is the human label from the workflow file's name: field.

DeploymentRun

Bases: BaseModel

A workflow / pipeline run triggered by trigger_deployment.

RemoteDeployment

Bases: BaseModel

A deployment observed on the remote for resync.

Returned by :meth:DeploymentCapability.list_recent_deployments so the host can backfill Release nodes and DEPLOYED_TO edges when webhook delivery has lapsed or when bringing a project online for the first time. environment is the remote's environment name as reported (callers map it to the project's local environment slug). sha is the commit the deployment was created against; ref is the human-facing label the deploy was triggered with (branch / tag / SHA prefix) and may be None for older deploys. external_run_id MUST be a stable identifier (e.g. the GitHub deployment id) so the host can dedupe re-runs of resync without appending duplicate events.