ci(helm): publish chart to charts/ namespace prefix on GHCR (#4175)

* ci(helm): publish chart to charts/ namespace prefix on GHCR

Push the Helm chart to ghcr.io/<owner>/charts/deer-flow (via a `charts/`
prefix on the `helm push` target) instead of the bare `deer-flow`
package. This namespaces the chart apart from the image packages
(deer-flow-{backend,frontend,provisioner}) without renaming the chart:
Chart.yaml `name` stays `deer-flow`, so dir = chart name = in-cluster
resource/selector names, and no `nameOverride` hack is needed.

The chart is new in 2.1.0 (chart infra landed in #3987, after v2.0.0),
so 2.1.0 is the first chart release. Early nightly builds remain at the
legacy non-prefixed ghcr.io/<owner>/deer-flow.

Also refresh chart release docs:
- Replace the removed scripts/build-and-push.sh in the chart README with
  raw `docker build`/`push` commands (contexts/args match container.yaml).
- Point NOTES.txt's empty-registry warning at the README section instead
  of the removed script.
- Retarget RELEASING.md version examples from 2.2.0 to 2.1.0.
- Bump the chart README's helm prerequisite to 3+.

* docs(helm): require helm 3.8+ and note legacy chart package cleanup

Address PR #4175 review:
- bump documented minimum to helm 3.8 (OCI registry support stabilized
  there; earlier 3.x needs HELM_EXPERIMENTAL_OCI=1)
- add a post-release note to delete/revoke the legacy bare
  ghcr.io/<owner>/deer-flow chart package after 2.1.0
This commit is contained in:
Zheng Feng
2026-07-15 08:54:36 +08:00
committed by GitHub
parent 0dd90ccfde
commit f3e4e5f8f0
5 changed files with 61 additions and 35 deletions
+2 -2
View File
@@ -9,7 +9,7 @@ name: Publish Helm Chart
# stale chart fails the PR, not the release.
#
# Users then install with:
# helm install deer-flow oci://ghcr.io/${{ owner }}/deer-flow --version <ver>
# helm install deer-flow oci://ghcr.io/${{ owner }}/charts/deer-flow --version <ver>
on:
push:
@@ -83,5 +83,5 @@ jobs:
run: |
for pkg in ./packages/*.tgz; do
echo "--- pushing $pkg"
helm push "$pkg" oci://ghcr.io/${{ github.repository_owner }}
helm push "$pkg" oci://ghcr.io/${{ github.repository_owner }}/charts
done
+2 -2
View File
@@ -12,7 +12,7 @@ name: Nightly Build
# Artifacts (under the running repo's owner):
# ghcr.io/<owner>/deer-flow-{backend,frontend,provisioner}:nightly
# ghcr.io/<owner>/deer-flow-{backend,frontend,provisioner}:nightly-YYYYMMDD
# oci://ghcr.io/<owner>/deer-flow chart version <base>-nightly.YYYYMMDD-<sha>
# oci://ghcr.io/<owner>/charts/deer-flow chart version <base>-nightly.YYYYMMDD-<sha>
#
# The nightly chart defaults image.tag=nightly and image.registry=ghcr.io/<owner>
# (patched in-workflow, never committed), so installing it pulls the matching
@@ -230,5 +230,5 @@ jobs:
helm package deploy/helm/deer-flow --destination ./packages
for pkg in ./packages/*.tgz; do
echo "--- pushing $pkg"
helm push "$pkg" oci://ghcr.io/${{ github.repository_owner }}
helm push "$pkg" oci://ghcr.io/${{ github.repository_owner }}/charts
done
+29 -24
View File
@@ -33,40 +33,40 @@ distinguishes it from a release.
## Helper scripts
- `scripts/bump_version.sh <version>` — set all four fields at once, then
self-verify. Tolerates a leading `v` (e.g. `v2.2.0`).
self-verify. Tolerates a leading `v` (e.g. `v2.1.0`).
```bash
scripts/bump_version.sh 2.2.0
scripts/bump_version.sh 2.1.0
```
- `scripts/verify_versions.sh [version]` — check that all sources agree. With
no argument it requires mutual equality; with an argument it requires every
source to equal it. Exits non-zero on mismatch. Run it locally before tagging
to catch drift early:
```bash
scripts/verify_versions.sh 2.2.0
scripts/verify_versions.sh 2.1.0
```
## Release procedure
1. **Bump the version** across all sources:
```bash
scripts/bump_version.sh 2.2.0
scripts/bump_version.sh 2.1.0
```
2. **Update `CHANGELOG.md`**: rename the `## [Unreleased]` section to
`## [2.2.0] — YYYY-MM-DD` (note the em dash ``), and add a link reference
`## [2.1.0] — YYYY-MM-DD` (note the em dash ``), and add a link reference
at the bottom of the file:
```
[2.2.0]: https://github.com/bytedance/deer-flow/releases/tag/v2.2.0
[2.1.0]: https://github.com/bytedance/deer-flow/releases/tag/v2.1.0
```
Start a fresh `## [Unreleased]` section above it for the next cycle.
3. **Commit** the version + changelog changes:
```bash
git add -A
git commit -m "release: v2.2.0"
git commit -m "release: v2.1.0"
```
4. **Tag and push**:
```bash
git tag v2.2.0
git push origin v2.2.0
git tag v2.1.0
git push origin v2.1.0
```
Pushing the tag triggers the publishing workflows (below).
@@ -78,7 +78,7 @@ distinguishes it from a release.
- `.github/workflows/chart.yaml` — packages the Helm chart and pushes it as an
OCI artifact to `ghcr.io`. Users install with:
```bash
helm install deer-flow oci://ghcr.io/<owner>/deer-flow --version 2.2.0
helm install deer-flow oci://ghcr.io/<owner>/charts/deer-flow --version 2.1.0
```
## Nightly builds
@@ -96,16 +96,16 @@ Artifacts (under the running repo's owner, where `<date>` is `YYYYMMDD`):
(rolling, overwritten each run) and `:nightly-<date>` (pinned to a day, but
mutable within it - a same-day re-dispatch overwrites it). For a truly
immutable pin, use `:sha-<short>`.
- Chart: `oci://ghcr.io/<owner>/deer-flow`, version `<base>-nightly.<date>-<sha>`
(e.g. `2.2.0-nightly.20260710-77a3652`). The short SHA makes each dispatch's
- Chart: `oci://ghcr.io/<owner>/charts/deer-flow`, version `<base>-nightly.<date>-<sha>`
(e.g. `2.1.0-nightly.20260710-77a3652`). The short SHA makes each dispatch's
chart version unique, so a same-day re-dispatch re-publishes cleanly (OCI
chart versions are immutable and otherwise can't be overwritten). The
packaged chart defaults `image.registry=ghcr.io/<owner>` and
`image.tag=nightly`, so installing it pulls the matching nightly images with
no values overrides:
```bash
helm install deer-flow oci://ghcr.io/<owner>/deer-flow \
--version 2.2.0-nightly.20260710-77a3652
helm install deer-flow oci://ghcr.io/<owner>/charts/deer-flow \
--version 2.1.0-nightly.20260710-77a3652
```
The chart version is patched in-workflow only - `Chart.yaml` and `values.yaml`
@@ -122,20 +122,20 @@ When it fails, the job annotation names the offending file and suggests the
fix:
```
::error::frontend/package.json is '2.1.0' but expected '2.2.0'.
Tip: run scripts/bump_version.sh 2.2.0 to align all sources.
::error::frontend/package.json is '2.0.0' but expected '2.1.0'.
Tip: run scripts/bump_version.sh 2.1.0 to align all sources.
```
## Pre-releases (RCs)
Pre-release tags like `v2.2.0-rc1` are valid `v*` tags and trigger the same
Pre-release tags like `v2.1.0-rc1` are valid `v*` tags and trigger the same
workflows. The version sources must equal the full pre-release string
(`2.2.0-rc1`) — the gate compares exact strings. Use the same procedure with
(`2.1.0-rc1`) — the gate compares exact strings. Use the same procedure with
the rc version:
```bash
scripts/bump_version.sh 2.2.0-rc1
# update CHANGELOG, commit, tag v2.2.0-rc1, push
scripts/bump_version.sh 2.1.0-rc1
# update CHANGELOG, commit, tag v2.1.0-rc1, push
```
## Recovering from a failed gate
@@ -146,10 +146,10 @@ If the gate failed because a source was forgotten:
2. Amend or add a follow-up commit.
3. Delete and re-create the tag, then push it:
```bash
git tag -d v2.2.0
git tag v2.2.0
git push origin :refs/tags/v2.2.0
git push origin v2.2.0
git tag -d v2.1.0
git tag v2.1.0
git push origin :refs/tags/v2.1.0
git push origin v2.1.0
```
Re-pushing the tag re-triggers the workflows. Because the gate blocks **all**
@@ -161,3 +161,8 @@ is safe — no images or chart were pushed to overwrite.
Optionally draft a **GitHub Release** from the tag, pasting the corresponding
`CHANGELOG.md` section as the release notes. The changelog link references
point at these release URLs.
For the 2.1.0 chart release (the first chart release), pre-`charts/` nightly
builds remain at the legacy bare `ghcr.io/<owner>/deer-flow` package. That
package receives no new versions after 2.1.0; delete it or revoke its
visibility once nothing still pulls from it.
+27 -6
View File
@@ -11,7 +11,7 @@ Kubernetes resources. No existing repo files are modified.
## Prerequisites
- A Kubernetes cluster (Docker Desktop K8s, OrbStack, kind, k3d, or a real cluster).
- `kubectl` + `helm` 3 installed.
- `kubectl` + `helm` 3.8+ installed (OCI registry support stabilized in 3.8; earlier 3.x needs `HELM_EXPERIMENTAL_OCI=1`).
- The three DeerFlow images — either the published ones (see "Install the
published chart" below) or built locally (see step 1).
- An Ingress controller (e.g. ingress-nginx) if you enable `ingress`.
@@ -23,7 +23,7 @@ The chart and all three images are published to GHCR on every `v*` release tag
and install directly:
```bash
helm install deer-flow oci://ghcr.io/<owner>/deer-flow \
helm install deer-flow oci://ghcr.io/<owner>/charts/deer-flow \
--version <version> \
-n deer-flow --create-namespace \
-f my-values.yaml
@@ -31,7 +31,14 @@ helm install deer-flow oci://ghcr.io/<owner>/deer-flow \
where `<owner>` is the GitHub owner the chart is published from and `<version>`
matches the release tag without the leading `v` (tag `v0.1.0``--version
0.1.0`). Point the chart at the published images:
0.1.0`).
> **Note:** the helm chart is new in 2.1.0 - no chart was published before it.
> It publishes to `oci://ghcr.io/<owner>/charts/deer-flow` (the `charts/` prefix
> keeps it distinct from the `deer-flow-{backend,frontend,provisioner}` image
> packages).
Point the chart at the published images:
```yaml
image:
@@ -58,11 +65,25 @@ Skip this section if you're using the published chart above. To build the
images yourself from the existing Dockerfiles:
```bash
REGISTRY=ghcr.io/yourorg TAG=latest ./deploy/helm/deer-flow/scripts/build-and-push.sh
REGISTRY=ghcr.io/yourorg
TAG=latest
# backend - build with the `postgres` extra so multi-replica deploys can use
# shared Postgres (matches the published image)
docker build -t $REGISTRY/deer-flow-backend:$TAG --build-arg UV_EXTRAS=postgres -f backend/Dockerfile .
# frontend
docker build -t $REGISTRY/deer-flow-frontend:$TAG -f frontend/Dockerfile .
# provisioner
docker build -t $REGISTRY/deer-flow-provisioner:$TAG -f docker/provisioner/Dockerfile docker/provisioner
docker push $REGISTRY/deer-flow-backend:$TAG
docker push $REGISTRY/deer-flow-frontend:$TAG
docker push $REGISTRY/deer-flow-provisioner:$TAG
```
This produces `$REGISTRY/deer-flow-backend`, `$REGISTRY/deer-flow-frontend`,
`$REGISTRY/deer-flow-provisioner`. The chart's image-name defaults match these.
These names match the chart's `gatewayImage` / `frontendImage` /
`provisionerImage` defaults, so only `image.registry` and `image.tag` need to
point at them.
If your registry needs auth, create a pull secret:
+1 -1
View File
@@ -6,7 +6,7 @@ DeerFlow has been deployed.
⚠️ WARNING: `image.registry` is empty. The gateway/frontend/provisioner
images won't resolve. Set `image.registry` in your values and build+push the
three images (see deploy/helm/deer-flow/scripts/build-and-push.sh).
three images (see the chart README's "Build & push images" section).
{{- end }}
Get the status: