Skip to content

feat(deploy): unify image + chart publishing under Docker Hub OCI#71

Merged
lucasmundim merged 2 commits into
mainfrom
feat/dockerhub-publish-and-chart-oci
Jun 6, 2026
Merged

feat(deploy): unify image + chart publishing under Docker Hub OCI#71
lucasmundim merged 2 commits into
mainfrom
feat/dockerhub-publish-and-chart-oci

Conversation

@lucasmundim

@lucasmundim lucasmundim commented Jun 5, 2026

Copy link
Copy Markdown
Contributor

Opening as draft for coordination — see the note about @stwilt's in-flight PRs at the bottom. Happy to rebase / merge / wait based on what makes the most sense for the deploy-flow direction.

Summary

Replaces the GAR-coupled deployments.yml workflow with a single docker-publish.yml modeled on the plainsight-deployment-agent workflow but extended to cover this repo's two-image build (controller + claimer) and to publish the Helm chart as an OCI artifact alongside the images.

End state, per release:

  • plainsightai/openfilter-pipelines-controller:<sha> + :vX.Y.Z + :latest (multi-arch)
  • plainsightai/openfilter-pipelines-claimer:<sha> + :vX.Y.Z + :latest (multi-arch)
  • oci://registry-1.docker.io/plainsightai/openfilter-pipelines-controller:X.Y.Z (Helm chart, version-pinned to the git tag)

Changes

.github/workflows/

  • deployments.yml removed, docker-publish.yml added. The new workflow:
    • Builds both images (controller, claimer) across amd64 + arm64, pushes-by-digest to the two Docker Hub repos. PR triggers build-but-no-push so reviewers get the same compile signal as main without leaking PR digests to a public registry.
    • Merges per-arch digests into a multi-arch manifest tagged with the commit SHA, the semver tag (on tag pushes), and latest (on tag pushes onlymain never bumps latest, matching the deployment-agent contract so prod doesn't accidentally pull a dev SHA).
    • On refs/tags/v*.*.*, packages the Helm chart under deployment/openfilter-pipelines-controller and pushes it to oci://registry-1.docker.io/plainsightai/openfilter-pipelines-controller. The chart's --version and --app-version both track the git tag (vX.Y.ZX.Y.Z), keeping image + chart releases in lock-step so a single git tag produces a self-consistent install.
    • Preserves the existing deploy + deploy-lab jobs on main pushes — same reusable PlainsightAI/gh-actions-public workflows, same service-name: openfilter-pipelines-controller + manifests-path: deployment contract. The override-bump automation downstream is unchanged.

Makefile

  • Default IMG / CLAIMER_IMG flipped from a GAR-prefixed pattern (gated on an env var) to plain plainsightai/openfilter-pipelines-controller / plainsightai/openfilter-pipelines-claimer. A local make docker-build now produces the same image name a release would, so OSS contributors don't need any env variable to reproduce the published shape.

deployment/openfilter-pipelines-controller/overrides/{development,lab,staging,production}.yaml

  • image.repository and claimerImage.repository switched from us-west1-docker.pkg.dev/plainsightai-prod/oci/openfilter-pipelines-* to plainsightai/openfilter-pipelines-*. The tag pins (with the $imagepolicy Flux annotation) are unchanged — the image-policy automation only rewrites the tag field, not the repository, so this commit doesn't perturb the bump-PR cadence.

deployment/openfilter-pipelines-controller/README.md

  • New top-level "Install from Docker Hub (recommended)" section documenting:
    helm install openfilter-pipelines-controller \
      oci://registry-1.docker.io/plainsightai/openfilter-pipelines-controller \
      --version 0.5.1
    with links to the two Docker Hub image repos. The local-chart install instructions are kept as the fallback for contributors hacking on the chart.

Validation

  • helm dependency update + helm lint clean against the chart (only the pre-existing icon-recommended INFO).
  • helm template against overrides/development.yaml renders image: plainsightai/openfilter-pipelines-controller:<sha> as expected; no remaining us-west1-docker.pkg.dev strings in the rendered manifests.

Migration / wire compatibility

  • Image registry switch is one-way for new pushes. After this PR lands, GAR stops receiving new tags from CI. Existing GAR tags stay readable (cluster nodes already cached the layers), but anything that pulls a new SHA needs the Docker Hub credentials wired into the node pool image-pull secret. Worth confirming the prod cluster has access to plainsightai/* on Docker Hub before merging.
  • No Helm chart consumers exist yet at the OCI endpoint — this PR is what bootstraps the registry. The first published chart will be whatever v*.*.* tag we cut next.
  • The deploy + deploy-lab jobs use the same PlainsightAI/gh-actions-public reusable workflows, so the override-bump automation in the deployment repos is untouched.

Coordination

Two of @stwilt's PRs are in flight and overlap with the deploy surface:

This PR intentionally does not touch the gitops side and keeps the deploy jobs as-is. Once @stwilt's PRs settle, we should align on (a) whether this docker-publish.yml keeps deploy / deploy-lab or hands them off to the new flow, and (b) whether the ArgoCD Image Updater annotations in gitops want a different repository path now that we're on Docker Hub. Marking this draft to leave room for that conversation.

Test plan

  • helm lint passes
  • helm template renders Docker Hub paths
  • (post-merge or via dry-run) Trigger a workflow_dispatch on main to confirm the build + push + merge + deploy chain end-to-end against Docker Hub credentials
  • (post-first-tag) Verify the published chart installs cleanly via helm install oci://...

View with Codesmith Autofix with Codesmith
Need help on this PR? Tag /codesmith with what you need. Autofix is disabled.

@lucasmundim lucasmundim self-assigned this Jun 5, 2026
Replaces the GAR-coupled `deployments.yml` workflow with a single
`docker-publish.yml` modeled on the plainsight-deployment-agent
workflow but extended to cover this repo's two-image build (controller
+ claimer) and to publish the Helm chart as an OCI artifact alongside
the images.

Changes:

- `.github/workflows/deployments.yml` removed; `.github/workflows/
  docker-publish.yml` added. The new workflow:
  - Builds both images (controller, claimer) across amd64 + arm64,
    pushes-by-digest to `plainsightai/openfilter-pipelines-controller`
    and `plainsightai/openfilter-pipelines-claimer`. PR triggers
    build-but-no-push so reviewers get the same compile signal as
    main without leaking PR digests.
  - Merges per-arch digests into a multi-arch manifest tagged with
    the commit SHA, the semver tag (on tag pushes), and `latest`
    (on tag pushes only — `main` never bumps `latest`, matching the
    deployment-agent contract).
  - On `refs/tags/v*.*.*`, packages the Helm chart under
    `deployment/openfilter-pipelines-controller` and pushes it to
    `oci://registry-1.docker.io/plainsightai/...` so consumers can
    `helm install` from Docker Hub OCI without a chart repo. The
    chart `--version` and `--app-version` both track the git tag
    (`vX.Y.Z` → `X.Y.Z`), keeping image and chart releases in
    lock-step.
  - Preserves the existing deploy + deploy-lab jobs on main pushes
    (still call the reusable `PlainsightAI/gh-actions-public`
    workflows; same `service-name` and `manifests-path` contract).

- `Makefile`: default `IMG` / `CLAIMER_IMG` flipped from a
  GAR-prefixed pattern (gated on an env var) to plain
  `plainsightai/openfilter-pipelines-controller` /
  `plainsightai/openfilter-pipelines-claimer`. Local builds without
  any env override now match what CI publishes, so a `make
  docker-build` produces the same image name a release would.

- `deployment/openfilter-pipelines-controller/overrides/{dev,lab,
  staging,production}.yaml`: `image.repository` and
  `claimerImage.repository` switched from
  `us-west1-docker.pkg.dev/plainsightai-prod/oci/...` to
  `plainsightai/...`. Tag pins (with the `$imagepolicy` annotation)
  are unchanged — the image-policy automation only rewrites the
  `tag` field, not the repository.

- `deployment/openfilter-pipelines-controller/README.md`: documents
  the new `helm install ... oci://registry-1.docker.io/plainsightai/
  openfilter-pipelines-controller` flow and points readers at the
  two Docker Hub repos for the container images. The local-chart
  install instructions are kept as the fallback.

Validation:
- `helm dependency update` + `helm lint` clean (only the
  pre-existing icon-recommended INFO).
- `helm template` against `overrides/development.yaml` renders
  `image: plainsightai/openfilter-pipelines-controller:<sha>` as
  expected; no remaining GAR strings in the rendered manifests.

Coordination note: there are two of @stwilt's PRs in flight
(openfilter-pipelines-controller#62 — DT-164 deployments workflow
refactor — and gitops#130 — DT-166 ArgoCD Image Updater
annotations). This PR intentionally does NOT touch the gitops side;
once both PRs settle we'll align on whether the deploy/deploy-lab
jobs here stay or migrate to the new flow.
@lucasmundim lucasmundim force-pushed the feat/dockerhub-publish-and-chart-oci branch from be32778 to d990d18 Compare June 5, 2026 16:04
@lucasmundim lucasmundim marked this pull request as ready for review June 5, 2026 16:05

@stwilt stwilt left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Clean migration — the GAR→Docker Hub swap is minimal and correct, the multi-arch digest-based build pattern carries over intact, and deploy/publish-chart gates are properly conditioned. Minor items below.

Comment thread .github/workflows/docker-publish.yml
Comment thread deployment/openfilter-pipelines-controller/README.md Outdated
- deploy-lab gains an explicit `permissions: contents: read` block so
  it stops inheriting the workflow-level `contents: write, id-token:
  write` it never needs. Matches the other jobs that scope down.
- README chart-install snippet swaps the hard-coded `--version 0.5.1`
  for a `<version>` placeholder plus a note that chart tags match the
  git release tags (`vX.Y.Z` → `X.Y.Z`), so the docs do not silently
  rot to a stale pin on the next release.
@lucasmundim lucasmundim merged commit fe613cc into main Jun 6, 2026
12 checks passed
lucasmundim added a commit that referenced this pull request Jun 7, 2026
Hoists the load-bearing piece of #62 (DT-164) so the drift hole closes
before gitops#130 (ArgoCD Image Updater) reconciles in clusters.

Today both `image.tag` and `claimerImage.tag` carry `$imagepolicy`
markers and image-policy-applier bumps both — controller and claimer
stay in lockstep. Once gitops#130 lands, Image Updater takes over and
only rewrites `image.tag` (the `image-list` annotation covers
`openfilter-pipelines-controller`, not the claimer). `claimerImage.tag`
would freeze at its last set value while the controller advances,
silently drifting until the next claimer-side flag or API change makes
the gap user-visible.

Fix:
- `templates/deployment.yaml:121` — `CLAIMER_IMAGE` rendering gains a
  `default .Values.image.tag` fallback so the claimer follows the
  controller's tag whenever its own is unset. `Chart.AppVersion` stays
  as the last-resort floor for fresh installs.
- 4 overrides drop the static `claimerImage.tag: <sha>` line (with the
  `$imagepolicy` marker). `claimerImage.repository: plainsightai/*`
  stays — that's the post-#71 Docker Hub path. With no `tag:` set, the
  template's fallback chain takes over.

Validated by `helm template` against `overrides/production.yaml`:
`CLAIMER_IMAGE=plainsightai/openfilter-pipelines-claimer:<image.tag>`
as expected.

Cross-refs: #71 (Docker Hub publish that introduced the drift hole),
#62 (Sam's larger DT-164 refactor that originally bundled this fix),
gitops#130 (the Image Updater rollout this guards against).
lucasmundim added a commit that referenced this pull request Jun 15, 2026
…all docs

The chart-publishing migration (Pages -> Docker Hub -> GHCR, PRs #53/#71/#75)
moved the artifact but left the release surface disconnected:

- No GitHub Release is created on a tag since chart-releaser was removed in
  #53, so the Releases page is frozen at 0.5.1 while 0.6.x ships to GHCR.
- The parity lint only checked version==appVersion and pointed at
  update-helm-version.yml, a workflow #53 deleted.

Changes (no rendered-manifest changes -> zero impact on lab/staging/prod):
- docker-publish.yml: create a GitHub Release per tag with the packaged chart
  .tgz attached (contents: write).
- lint.yml: parity check now also asserts Chart.yaml matches the release tag on
  vX.Y.Z pushes; dropped the dead update-helm-version.yml reference; skip Go
  lint on tags.
- README.md / chart README: GHCR install as the primary path at the correct
  published version (0.6.1), documented the tag-driven release process.

Deliberately NOT bumping Chart.yaml here: version/appVersion feed the
app.kubernetes.io/version pod-template label, so an out-of-band bump would roll
the controller in every env on the next ArgoCD sync. External GHCR installs are
already deterministic because the publish job bakes --app-version from the git
tag (values.yaml image.tag is "" -> defaults to appVersion). The parity gate
now forces the in-repo bump at actual release time, where a rollout is expected.
lucasmundim added a commit that referenced this pull request Jun 16, 2026
* fix(release): reconnect GitHub Releases, enforce tag-parity, fix install docs

The chart-publishing migration (Pages -> Docker Hub -> GHCR, PRs #53/#71/#75)
moved the artifact but left the release surface disconnected:

- No GitHub Release is created on a tag since chart-releaser was removed in
  #53, so the Releases page is frozen at 0.5.1 while 0.6.x ships to GHCR.
- The parity lint only checked version==appVersion and pointed at
  update-helm-version.yml, a workflow #53 deleted.

Changes (no rendered-manifest changes -> zero impact on lab/staging/prod):
- docker-publish.yml: create a GitHub Release per tag with the packaged chart
  .tgz attached (contents: write).
- lint.yml: parity check now also asserts Chart.yaml matches the release tag on
  vX.Y.Z pushes; dropped the dead update-helm-version.yml reference; skip Go
  lint on tags.
- README.md / chart README: GHCR install as the primary path at the correct
  published version (0.6.1), documented the tag-driven release process.

Deliberately NOT bumping Chart.yaml here: version/appVersion feed the
app.kubernetes.io/version pod-template label, so an out-of-band bump would roll
the controller in every env on the next ArgoCD sync. External GHCR installs are
already deterministic because the publish job bakes --app-version from the git
tag (values.yaml image.tag is "" -> defaults to appVersion). The parity gate
now forces the in-repo bump at actual release time, where a rollout is expected.

* refactor(ci): make tag-parity an authoritative publish gate

Per review: a failing lint job does not block the separate publish workflow,
so asserting tag-parity in lint.yml only produced an advisory red check while
the mistagged chart still shipped. Move the Chart.yaml-==-tag assertion into the
publish-chart job (the only step that runs on the tag AND gates publishing) so a
mistagged release fails to publish. lint.yml keeps version==appVersion for
pre-merge PR feedback; dropped the tag trigger and tag-only lint skip.

* docs(release): credit the publish-chart gate, not lint.yml, for tag-parity

Per review (leandrobmarinho): the Releasing section said lint.yml's
chart-version-parity 'on a release tag, asserts they match the tag' — but that
check only does version==appVersion on PRs and never sees the tag. The actual
tag-parity gate is the 'Assert Chart.yaml matches the release tag' step in
docker-publish.yml's publish-chart job, which exits non-zero before package/push.
Re-point the prose so a future maintainer doesn't remove the real gate believing
lint.yml covers it.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants