Shape: Container Image

A container image ships as a versioned OCI artifact to a registry -- Docker Hub, GHCR (GitHub Container Registry), Amazon ECR, Google Artifact Registry, Azure Container Registry. Downstream consumers pull by tag. The release surface is a tag push to the registry.

When this shape applies

  • You build a Docker / OCI image others run (not a service you deploy yourself).
  • You want a versioned tag pushed to a registry on every release.
  • Optionally, the same image is later deployed by a consumer's /gaia-deploy -- but THIS project's release is the registry push, not the deployment.

project-config.yaml

environments:
  - id: registry
    kind: distribution-only

distribution:
  channel: container-registry
  registry: ghcr.io/myorg
  manifest: Dockerfile
  release_workflow: gaia-release.yml
  image_name: myorg/myapp
  tag_strategy: semver

The container-registry channel requires image_name and tag_strategy as per-channel sub-fields. Missing either is rejected by /gaia-config-validate.

For Docker Hub, swap the registry: registry: docker.io/myorg. For ECR / GAR / ACR, swap to the cloud's registry hostname.

Tag strategy

  • semver -- the tag matches the SemVer in your release pipeline (e.g., 1.4.2). Most common; clean rollback story.
  • sha -- the tag matches the git commit SHA (e.g., abc1234). Useful when every build is an artifact; pairs well with GitOps consumers.
  • latest -- the tag is the literal string latest, updated on every release. Discouraged for production but common for development streams.

Phase 5 routing

With kind: distribution-only + distribution:, the Phase 5 router emits publish-primary and suggests /gaia-publish. /gaia-deploy refuses (a container image is not a deployed service from THIS project's perspective; consumers deploy it themselves).

Publish to Docker Hub / GHCR

/gaia-publish

The orchestrator dispatches publish-container-registry, which invokes the configured release_workflow (typically a workflow that runs docker buildx build --push -t $REGISTRY/$IMAGE_NAME:$TAG). The adapter resolves the tag from tag_strategy:

  • semver -> reads from your manifest (e.g., VERSION file or package.json version) and pushes that exact tag.
  • sha -> uses ${GITHUB_SHA:0:7} for short SHA or the full hash.
  • latest -> pushes the literal latest tag.

Credentials are read from the environment (REGISTRY_USER + REGISTRY_PASSWORD for Docker Hub, GITHUB_TOKEN for GHCR). See the per-channel adapter schema at plugins/gaia/scripts/adapters/publish-container-registry/schema.yaml for the full sub-field contract.

You'll know it worked when

  • /gaia-config-validate exits 0 with image_name + tag_strategy both present.
  • /gaia-help suggests /gaia-publish on the Phase 5 row.
  • A /gaia-publish run pushes the image; docker pull $REGISTRY/$IMAGE_NAME:$TAG succeeds from a clean machine.
  • The registry web UI shows the new tag with the expected manifest digest.