Architecture

Design principles

  • Opt-in reporting - only workloads with github-deployment-bridge.io/auto-report=true are reported; others are skipped quietly.
  • OCI labels are canonical for build metadata - repository and commit come from standard image labels.
  • Kubernetes annotations are optional overrides - deployment-specific environment, URLs, and opt-outs.
  • Zero per-app mapping database - no repository-specific configuration beyond annotations on the workload.
  • Observe only - the bridge never triggers deployments or mutates cluster workloads.
  • Full GitHub Deployments lifecycle - one Deployment per (owner, repo, environment, commit, deploymentName) with status updates as Flux progresses.
  • Safe reconcile loop - missing/invalid metadata skips a workload with a warning; transient GitHub/OCI errors retry with backoff (GitHub Retry-After / rate-reset honored).
  • Single-writer cache - SQLite on a PVC is the intentional HA model: one replica, Recreate upgrades, node loss means reschedule downtime (not active-active). Multi-writer would require a different store.

Flux sources

The bridge watches:

KindAPIInventory
Kustomizationkustomize.toolkit.fluxcd.io/v1.status.inventory
HelmReleasehelm.toolkit.fluxcd.io/v2.status.inventory (Flux ≥ 2.8 / helm-controller ≥ 1.5)

Events fire when conditions, observedGeneration, or revision fields change. Reporting runs only when inventory yields at least one resolvable workload image.

Phase derivation

Desired phaseFlux signal
successReady=True and generation == observedGeneration
failureReady=False (observed) or Stalled=True, or known failure reasons (HealthCheckFailed, InstallFailed, …)
in_progressReconciling=True, or Ready not True while not yet a failure

The reporter maps desired phases onto GitHub statuses with an idempotent state machine:

  • Catch-up: if the cache is empty and Flux is already terminal, emit only that terminal status (no synthetic history).
  • Early states: queued then in_progress when first observing an in-progress reconcile for a new commit.
  • Never transition successin_progress. Never send duplicate identical statuses.

Workload discovery

  1. Parse .status.inventory for Deployment, StatefulSet, and DaemonSet.
  2. Resolve ReplicaSet entries via owner references to their controlling Deployment.
  3. Ignore Job / CronJob.
  4. Collect github-deployment-bridge.io/* annotations from the workload (and pod template as fallback).

Empty inventory (including HelmRelease on Flux before 2.8) → skip.

Metadata resolution

Priority for every field:

  1. Kubernetes annotation
  2. OCI label
  3. Controller default (if applicable)

OCI labels

LabelRequiredPurpose
org.opencontainers.image.sourceyes*GitHub owner/repository
org.opencontainers.image.revisionyes*Git commit SHA (Deployment ref)
org.opencontainers.image.versionnoLogging / payload
org.opencontainers.image.titlenoLogging
org.opencontainers.image.creatednoDiagnostics

*Required unless overridden by the matching Kubernetes annotation.

Kubernetes annotations

Prefix: github-deployment-bridge.io/

AnnotationOverridesPurpose
repositoryOCI sourceowner/repo when multiple apps share an image
commitOCI revisionExceptional commit override
environmentENVIRONMENTGitHub Deployment environment
environment-urlENVIRONMENT_URLDeployment Status environment_url
log-urlLOG_URL_TEMPLATEDeployment Status log_url
descriptiondefault textDeployment description
productionderived from env nameproduction_environment (true/false)
auto-report(none)Opt-in. Must be true to report; absent/false ignores the workload (no OCI fetch, no warning spam)
deployment-namerepository nameIndependent reports for monorepo workloads (also GitHub task)
clusterCLUSTER_NAMEDeployment payload cluster
team(none)Deployment payload team
service(none)Deployment payload service
component(none)Deployment payload component
slack-channel(none)Deployment payload slackChannel
owner(none)Deployment payload owner (service owner, not GitHub repo owner)
release(none)Deployment payload release
tag(none)Deployment payload tag

Validation

FieldRule
RepositoryMust resolve to owner/repository
CommitValid Git SHA (7-40 hex)
EnvironmentNon-empty
Environment / log URLAbsolute https:// URL when set

Missing or invalid required metadata → skip reporting and emit a warning. Never fail reconciliation for that reason.

GitHub Deployment mapping

Resolved fieldGitHub field
RepositoryDeployment repository
CommitDeployment ref
EnvironmentDeployment environment
Productionproduction_environment
DescriptionDeployment description
Deployment name (when annotated)Deployment task
Environment URLStatus environment_url
Log URLStatus log_url

Deployment payload includes cluster, namespace (workload), sourceNamespace (Flux Kustomization / HelmRelease namespace), source name (kustomization / helmRelease), deploymentName, image, optional digest / version, controllerVersion, and any optional annotation fields (team, service, component, slackChannel, owner, release, tag). The cluster annotation overrides the controller CLUSTER_NAME env. Crash recovery also matches older payloads that used the Flux source namespace as namespace and omitted sourceNamespace.

Status updates set auto_inactive=true. When a newer commit reaches success for the same identity, prior cached success deployments are explicitly marked inactive.

Deduplication cache key: (owner, repo, environment, commitSHA, deploymentName). Before creating a Deployment, the bridge writes a provisional cache row (deployment_id=0). It then searches GitHub for an existing Deployment with the same ref, environment, and payload (crash recovery) and only creates when none is found. The resolved deployment_id is persisted before status updates.