Architecture
Design principles
- Opt-in reporting - only workloads with
github-deployment-bridge.io/auto-report=trueare 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:
Events fire when conditions, observedGeneration, or revision fields change. Reporting runs only when inventory yields at least one resolvable workload image.
Phase derivation
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:
queuedthenin_progresswhen first observing an in-progress reconcile for a new commit. - Never transition
success→in_progress. Never send duplicate identical statuses.
Workload discovery
- Parse
.status.inventoryforDeployment,StatefulSet, andDaemonSet. - Resolve
ReplicaSetentries via owner references to their controllingDeployment. - Ignore
Job/CronJob. - 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:
- Kubernetes annotation
- OCI label
- Controller default (if applicable)
OCI labels
*Required unless overridden by the matching Kubernetes annotation.
Kubernetes annotations
Prefix: github-deployment-bridge.io/
Validation
Missing or invalid required metadata → skip reporting and emit a warning. Never fail reconciliation for that reason.
GitHub Deployment mapping
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.
GitHub UI does not render custom payload fields. Values such as team, service,
component, slackChannel, owner, release, tag, and cluster are stored on the
Deployment and returned by the API / webhooks for integrations — they do not appear as
labels in the repository Deployments page. Prefer environment, description, or
environment-url when you need something visible in the UI.
Crash recovery also matches older payloads that used the Flux source namespace as
namespace and omitted sourceNamespace.
Status updates set auto_inactive=false. GitHub's environment-scoped auto-inactive would
otherwise deactivate sibling monorepo deployments that share an environment. When a newer
commit reaches success for the same identity (deploymentName included), the bridge
explicitly marks prior cached success deployments 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.
Monorepos
When multiple workloads in one GitHub repository should report independently (for example frontend and backend), you have two options:
Recommendation: use different environment values for the best Deployments UI
experience. Use deployment-name when you want independent tracking (and correct
active supersession) while keeping a shared environment name; combine both when useful
(environment for UI grouping, deployment-name for API identity).
Without deployment-name, both workloads default to the repository name and share one
cache identity — only one active deployment is tracked for that key.