Registry Publishing

Publishing a module means two things: pushing its image to a container registry, and listing that image in a module repo index so consumers can discover and install it. There is no chart publishing and no marketplace approval step — a repo index is just a static file.

The Pipeline

git tag vX.Y.Z
   └─> GitHub workflow builds ghcr.io/<org>/<module>:<X.Y.Z>   (v stripped)
          └─> update module.yaml (add the version)
          └─> regenerate components.yaml + values.yaml from the image
                 └─> tiny repo index . -o index.yaml
                        └─> consumers: tiny repo add / platform workspace repos

1. Tag a release

git tag v1.0.0
git push origin v1.0.0

The template's workflow (.github/workflows/release.yml) triggers on v[0-9]+.[0-9]+.[0-9]+ tags, strips the v (version=${GITHUB_REF_NAME#v}), and builds a multi-arch image:

ghcr.io/my-org/my-module:1.0.0    # bare version tag — this is what the index references
ghcr.io/my-org/my-module:latest

GHCR packages are private on first push. Make the package public once (repo → Packages → settings → visibility) so clusters can pull anonymously.

2. List it in a module repo

A module repo is a directory of per-module manifests plus a generated index.yaml, hosted as static files (GitHub Pages works). For each module:

my-module/
+-- module.yaml       # identity + versions (hand-maintained)
+-- components.yaml   # discovery info (generated from the image)
+-- values.yaml       # rbac overlay (generated; only if the module declares RBAC)

module.yaml:

name: my-module
source: github.com/my-org/my-module
description: What it does
category: core
versions:
  - version: 1.0.0
    image: ghcr.io/my-org/my-module:1.0.0
    chart: tinysystems/tinysystems-operator
    chartVersion: 0.2.10

Generate the sibling files from the built image — never by hand:

# Discovery info (name/description/info/tags per component; no ports —
# a running node answers for those). Folded into the index so consumers
# can browse components before installing.
docker run --rm --entrypoint /manager ghcr.io/my-org/my-module:1.0.0 \
  tools components-info --json > my-module/components.yaml

# RBAC overlay derived from registry.SetRequirements. Emits nothing if the
# module declares no RBAC — then there is no values.yaml at all.
docker run --rm --entrypoint /manager ghcr.io/my-org/my-module:1.0.0 \
  tools rbac-values > my-module/values.yaml

3. Regenerate the index

tiny repo index . -o index.yaml

Like helm repo index: walks the directory for module.yaml files, folds in each sibling components.yaml, and writes a single index.yaml. Host the result anywhere static — no server, no platform involvement.

4. How consumers find it

With the tiny CLI:

tiny repo add myrepo https://my-org.github.io/modules/index.yaml
tiny repo update
tiny install my-module          # or my-module@1.0.0

The public first-party index is https://tiny-systems.github.io/modules/index.yaml. On the platform, workspaces register their own module repos the same way — the workspace's repo list is what feeds its module catalog and installs. The platform is a consumer of indexes, not their owner.

Private Registries

If the image is not public, give the namespace a pull secret and reference it in the install values:

kubectl create secret docker-registry regcred \
  --docker-server=ghcr.io \
  --docker-username=myuser \
  --docker-password=$GITHUB_TOKEN \
  -n tinysystems
# values overlay
imagePullSecrets:
  - name: regcred

Release Checklist

  1. Tests green; tools rbac-check --strict passes if the module touches the Kubernetes API.
  2. Tag vX.Y.Z, push; wait for the workflow to publish ghcr.io/<org>/<module>:X.Y.Z.
  3. First release only: make the GHCR package public.
  4. Add the version to module.yaml; regenerate components.yaml and values.yaml from the new image.
  5. tiny repo index . -o index.yaml; commit and publish the repo.
  6. Verify: tiny repo update && tiny install my-module@X.Y.Z on a test cluster.

Next Steps