From 8f9fc55c8eaae08482b8ae15f93075089f3dbd2d Mon Sep 17 00:00:00 2001 From: ARUNAVO RAY Date: Wed, 2 Sep 2026 18:08:30 +0530 Subject: [PATCH] feat(helm): publish the chart to ghcr.io and keep its version in step with releases (#383) The chart pinned appVersion 3.24.0 and nothing bumped it on release, so a default install ran an image six minor versions old, and the docs example pointed at a tag without the v prefix that does not exist on the registry. The chart version now equals the application version. The tag build's sync job updates Chart.yaml alongside package.json, and a new job in the tag build packages the chart after the image is pushed and publishes it to oci://ghcr.io/raylabshq/charts/gitea-mirror. Docs and the website install tab show the OCI install. Claude-Session: https://claude.ai/code/session_01Tp9pmi65a8k5jLMQFLf4JX --- .github/workflows/docker-build.yml | 84 ++++++++++++++++++++++++++--- docs/DEVELOPMENT_WORKFLOW.md | 5 +- helm/gitea-mirror/Chart.yaml | 4 +- helm/gitea-mirror/README.md | 23 ++++++-- helm/gitea-mirror/values.yaml | 4 +- www/src/components/Installation.tsx | 12 ++--- www/src/pages/docs/deployment.mdx | 16 +++--- 7 files changed, 116 insertions(+), 32 deletions(-) diff --git a/.github/workflows/docker-build.yml b/.github/workflows/docker-build.yml index 58b95e4..a3e2a4b 100644 --- a/.github/workflows/docker-build.yml +++ b/.github/workflows/docker-build.yml @@ -321,7 +321,7 @@ jobs: sarif_file: scout-results.sarif sync-version-main: - name: Sync package.json version back to main + name: Sync package.json and Chart.yaml versions back to main if: startsWith(github.ref, 'refs/tags/v') runs-on: ubuntu-latest needs: docker @@ -334,7 +334,7 @@ jobs: with: ref: ${{ github.event.repository.default_branch }} - - name: Update package.json version on main + - name: Update package.json and Chart.yaml versions on main env: TAG_VERSION: ${{ github.ref_name }} TARGET_BRANCH: ${{ github.event.repository.default_branch }} @@ -345,18 +345,90 @@ jobs: fi APP_VERSION="${TAG_VERSION#v}" - echo "Syncing ${TARGET_BRANCH}/package.json to ${APP_VERSION}" + echo "Syncing ${TARGET_BRANCH} package.json and Chart.yaml to ${APP_VERSION}" jq --arg version "${APP_VERSION}" '.version = $version' package.json > package.json.tmp mv package.json.tmp package.json - if git diff --quiet -- package.json; then - echo "package.json on ${TARGET_BRANCH} already at ${APP_VERSION}; nothing to commit." + # The chart version tracks the app version so the default image tag + # always points at the release the chart was published with. + sed -i -E \ + -e "s/^version: .*/version: ${APP_VERSION}/" \ + -e "s/^appVersion: .*/appVersion: \"${APP_VERSION}\"/" \ + helm/gitea-mirror/Chart.yaml + + if git diff --quiet -- package.json helm/gitea-mirror/Chart.yaml; then + echo "Versions on ${TARGET_BRANCH} already at ${APP_VERSION}; nothing to commit." exit 0 fi git config user.name "github-actions[bot]" git config user.email "41898282+github-actions[bot]@users.noreply.github.com" - git add package.json + git add package.json helm/gitea-mirror/Chart.yaml git commit -m "chore: sync version to ${APP_VERSION}" git push origin "HEAD:${TARGET_BRANCH}" + + publish-helm-chart: + name: Publish Helm chart to ghcr.io + # Runs after the image for this tag is on the registry, so the chart never + # points at an image that does not exist yet. The chart version and + # appVersion both follow the release version. + if: startsWith(github.ref, 'refs/tags/v') + runs-on: ubuntu-latest + timeout-minutes: 15 + needs: docker + permissions: + contents: read + packages: write + + steps: + - name: Checkout tag + uses: actions/checkout@v4 + + - name: Resolve version + id: version + env: + TAG_VERSION: ${{ github.ref_name }} + run: | + if [[ ! "$TAG_VERSION" =~ ^v[0-9]+\.[0-9]+\.[0-9]+([.-][0-9A-Za-z.-]+)?(\+[0-9A-Za-z.-]+)?$ ]]; then + echo "::error::Release tag '${TAG_VERSION}' is invalid. Expected semver tag format like v1.2.3 or v1.2.3-rc.1" + exit 1 + fi + echo "version=${TAG_VERSION#v}" >> "$GITHUB_OUTPUT" + + - name: Setup Helm + uses: azure/setup-helm@v4 + with: + version: v3.19.0 + + - name: Lint and package chart + env: + VERSION: ${{ steps.version.outputs.version }} + run: | + helm lint ./helm/gitea-mirror + helm package ./helm/gitea-mirror \ + --version "$VERSION" \ + --app-version "$VERSION" \ + --destination dist + + - name: Log into registry + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + ACTOR: ${{ github.actor }} + run: | + echo "$GITHUB_TOKEN" | helm registry login "$REGISTRY" --username "$ACTOR" --password-stdin + + - name: Push chart + env: + VERSION: ${{ steps.version.outputs.version }} + OWNER: ${{ github.repository_owner }} + run: | + OWNER_LC="$(echo "$OWNER" | tr '[:upper:]' '[:lower:]')" + helm push "dist/gitea-mirror-${VERSION}.tgz" "oci://${REGISTRY}/${OWNER_LC}/charts" + { + echo "### Helm chart ${VERSION} published" + echo + echo '```bash' + echo "helm upgrade --install gitea-mirror oci://${REGISTRY}/${OWNER_LC}/charts/gitea-mirror --version ${VERSION}" + echo '```' + } >> "$GITHUB_STEP_SUMMARY" diff --git a/docs/DEVELOPMENT_WORKFLOW.md b/docs/DEVELOPMENT_WORKFLOW.md index 3c86dd6..cfaa989 100644 --- a/docs/DEVELOPMENT_WORKFLOW.md +++ b/docs/DEVELOPMENT_WORKFLOW.md @@ -326,9 +326,10 @@ git push origin vX.Y.Z 4. **Create GitHub release** -5. **CI version sync (automatic)**: +5. **CI version sync and chart publish (automatic)**: - On `v*` tags, release CI updates `package.json` version in the build context from the tag (`vX.Y.Z` -> `X.Y.Z`), so Docker release images always report the correct app version. -- After the release build succeeds, CI commits the same `package.json` version back to `main` automatically. +- After the release build succeeds, CI packages the Helm chart with the same version as chart version and `appVersion` and pushes it to `oci://ghcr.io/raylabshq/charts/gitea-mirror`. The chart's default image tag is `v`, so a default install runs that release. +- CI then commits the same version to `package.json` and `helm/gitea-mirror/Chart.yaml` on `main` automatically. ## Contributing diff --git a/helm/gitea-mirror/Chart.yaml b/helm/gitea-mirror/Chart.yaml index ee513d4..8bf48bb 100644 --- a/helm/gitea-mirror/Chart.yaml +++ b/helm/gitea-mirror/Chart.yaml @@ -2,8 +2,8 @@ apiVersion: v2 name: gitea-mirror description: Kubernetes helm chart for gitea-mirror type: application -version: 0.0.2 -appVersion: "3.24.0" +version: 3.30.0 +appVersion: "3.30.0" icon: https://github.com/RayLabsHQ/gitea-mirror/blob/main/.github/assets/logo.png keywords: - git diff --git a/helm/gitea-mirror/README.md b/helm/gitea-mirror/README.md index c682fcb..46f9202 100644 --- a/helm/gitea-mirror/README.md +++ b/helm/gitea-mirror/README.md @@ -4,7 +4,9 @@ Deploy **gitea-mirror** to Kubernetes using Helm. The chart packages a Deploymen - **Chart name:** `gitea-mirror` - **Type:** `application` -- **App version:** `3.7.2` (default image tag, can be overridden) +- **Chart version:** the same number as the application release it deploys +- **App version:** the default image tag (`v`), can be overridden +- **Registry:** `oci://ghcr.io/raylabshq/charts/gitea-mirror` --- @@ -19,16 +21,25 @@ Deploy **gitea-mirror** to Kubernetes using Helm. The chart packages a Deploymen ## Quick start -From the repo root (chart path: `helm/gitea-mirror`): +The chart is published to GitHub Container Registry as an OCI package on every release. Each chart version deploys the application release with the same number. ```bash # Create a namespace (optional) kubectl create namespace gitea-mirror # Install with minimal required secrets/values -helm upgrade --install gitea-mirror ./helm/gitea-mirror --namespace gitea-mirror --set "gitea-mirror.github.username=" --set "gitea-mirror.github.token=" --set "gitea-mirror.gitea.url=https://gitea.example.com" --set "gitea-mirror.gitea.token=" +helm upgrade --install gitea-mirror oci://ghcr.io/raylabshq/charts/gitea-mirror \ + --namespace gitea-mirror \ + --set "gitea-mirror.github.username=" \ + --set "gitea-mirror.github.token=" \ + --set "gitea-mirror.gitea.url=https://gitea.example.com" \ + --set "gitea-mirror.gitea.token=" ``` +Add `--version ` to pin a release. Without it Helm installs the newest published chart. + +To install from a clone instead, run the same command from the repo root with `./helm/gitea-mirror` in place of the OCI reference. + The default Service is `ClusterIP` on port `4321`. You can expose it via Ingress or Gateway API; see below. --- @@ -38,9 +49,11 @@ The default Service is `ClusterIP` on port `4321`. You can expose it via Ingress Standard Helm upgrade: ```bash -helm upgrade gitea-mirror ./helm/gitea-mirror -n gitea-mirror +helm upgrade gitea-mirror oci://ghcr.io/raylabshq/charts/gitea-mirror -n gitea-mirror ``` +Each upgrade moves to the newest chart, and with it the newest application image. Pass `--version` to stay on a specific release. + If you change persistence settings or storage class, a rollout may require PVC recreation. --- @@ -63,7 +76,7 @@ If you enabled persistence with a PVC the data may persist; delete the PVC manua | --- | --- | --- | --- | | `image.registry` | string | `ghcr.io` | Container registry. | | `image.repository` | string | `raylabshq/gitea-mirror` | Image repository. | -| `image.tag` | string | `""` | Image tag; when empty, uses the chart `appVersion` (`3.7.2`). | +| `image.tag` | string | `""` | Image tag; when empty, uses `v`, the release this chart was published with. Registry tags carry the `v` prefix, for example `v3.30.0`. | | `image.pullPolicy` | string | `IfNotPresent` | K8s image pull policy. | | `imagePullSecrets` | list | `[]` | Image pull secrets. | | `podSecurityContext.runAsUser` | int | `1001` | UID. | diff --git a/helm/gitea-mirror/values.yaml b/helm/gitea-mirror/values.yaml index deb702f..13d1575 100644 --- a/helm/gitea-mirror/values.yaml +++ b/helm/gitea-mirror/values.yaml @@ -1,7 +1,9 @@ image: registry: ghcr.io repository: raylabshq/gitea-mirror - # Leave blank to use the Appversion tag + # Leave blank to run v, the release this chart was published + # with. If you set a tag, include the v prefix (registry tags look like + # v3.30.0); the value is used as is. tag: "" pullPolicy: IfNotPresent diff --git a/www/src/components/Installation.tsx b/www/src/components/Installation.tsx index d2f140d..5e27f59 100644 --- a/www/src/components/Installation.tsx +++ b/www/src/components/Installation.tsx @@ -43,14 +43,14 @@ export function Installation() { description: "Deploy to Kubernetes", steps: [ { - title: "Clone the repository", - command: "git clone https://github.com/RayLabsHQ/gitea-mirror.git && cd gitea-mirror", - id: "helm-clone" + title: "Install the chart from ghcr.io", + command: "helm upgrade --install gitea-mirror oci://ghcr.io/raylabshq/charts/gitea-mirror \\\n --namespace gitea-mirror --create-namespace", + id: "helm-install" }, { - title: "Install the chart", - command: "helm upgrade --install gitea-mirror ./helm/gitea-mirror \\\n --namespace gitea-mirror --create-namespace", - id: "helm-install" + title: "Upgrade to the newest release later", + command: "helm upgrade gitea-mirror oci://ghcr.io/raylabshq/charts/gitea-mirror -n gitea-mirror", + id: "helm-upgrade" }, { title: "Access the application", diff --git a/www/src/pages/docs/deployment.mdx b/www/src/pages/docs/deployment.mdx index 4ce8ddc..4635ada 100644 --- a/www/src/pages/docs/deployment.mdx +++ b/www/src/pages/docs/deployment.mdx @@ -110,29 +110,25 @@ Database migrations run automatically at startup. Keep the data volume attached ## Kubernetes with Helm -A Helm chart lives in the repository at `helm/gitea-mirror`. It is not published to a chart repository, so install it from a clone. +The chart is published as an OCI package at `oci://ghcr.io/raylabshq/charts/gitea-mirror`. Every release publishes a chart with the same version number as the application, and that chart's default image tag is the matching release. The chart source lives in the repository at `helm/gitea-mirror` if you would rather install from a clone. The chart deploys a Deployment, a Service, a ConfigMap, a Secret, an optional PVC, and optionally an Ingress or Gateway API HTTPRoutes. It needs Kubernetes 1.23 or newer and Helm 3.8 or newer. ### Installing ```bash -git clone https://github.com/RayLabsHQ/gitea-mirror.git -cd gitea-mirror - kubectl create namespace gitea-mirror -helm upgrade --install gitea-mirror ./helm/gitea-mirror \ +helm upgrade --install gitea-mirror oci://ghcr.io/raylabshq/charts/gitea-mirror \ --namespace gitea-mirror \ --values my-values.yaml ``` +Add `--version 3.30.0` (or any published release) to pin the chart. Without it Helm installs the newest one, and a later `helm upgrade` moves you to the newest release. To install from a clone, run the same command from the repository root with `./helm/gitea-mirror` in place of the OCI reference. + A minimal `my-values.yaml`: ```yaml -image: - tag: "3.24.0" - gitea-mirror: core: betterAuthSecret: "your-32-character-minimum-secret" @@ -165,13 +161,13 @@ ingress: - mirror.example.com ``` -> **Note:** The chart's `appVersion` trails the application, so leaving `image.tag` empty pins an older image than you probably want. Set `image.tag` explicitly to the release you intend to run. +> **Note:** Leave `image.tag` empty to run the release the chart was published for. If you do set it, use the tag as it appears on the registry, with the `v` prefix, for example `v3.30.0`. ### Values that matter | Value | Default | Why it matters | |---|---|---| -| `image.tag` | `""` | Falls back to the chart `appVersion`. Set it explicitly. | +| `image.tag` | `""` | Falls back to `v`, the release the chart shipped with. Set it only to run a different release. | | `gitea-mirror.core.betterAuthSecret` | `""` | Session signing secret. | | `gitea-mirror.core.encryptionSecret` | `""` | Token encryption secret, stored in the chart's Secret. | | `gitea-mirror.core.betterAuthUrl` | `http://localhost:4321` | Must be the external URL users reach. |