Skip to content
Merged
Show file tree
Hide file tree
Changes from 9 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
53 changes: 53 additions & 0 deletions .cursor/rules/regen-helm-schema.mdc
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
---
description: Regenerate the Helm chart's values.schema.json after editing deploy/helm/values.yaml or when the Helm Values Schema CI check is failing.
globs: deploy/helm/values.yaml,deploy/helm/values.schema.json
alwaysApply: false
---
# Regenerate the Helm chart values schema

The Helm chart's `values.schema.json` is auto-generated from `values.yaml` via inline `# @schema` annotations. The CI workflow `.github/workflows/helm-schema.yml` regenerates it on PR and fails if the committed copy drifts.

## When this rule applies

- After editing `deploy/helm/values.yaml` (annotations or default values)
- When the `Helm Values Schema` GitHub check is failing on a PR
- When asked to regenerate, update, or refresh the chart's values schema

## Prerequisites

Install the [helm-values-schema-json](https://github.com/losisin/helm-values-schema-json) plugin once:

```bash
helm plugin list | grep -q '^schema' || \
helm plugin install https://github.com/losisin/helm-values-schema-json.git
```

## Regenerate

From the repo root:

```bash
cd deploy/helm && helm schema \
--schema-root.title "Appsmith Helm chart values" \
--schema-root.id "https://helm.appsmith.com/values.schema.json" \
-o values.schema.json
```

The flags must match `.github/workflows/helm-schema.yml` exactly or CI will report drift.

## After regenerating

1. `git diff deploy/helm/values.schema.json` — review the diff
2. If it reflects only the intended `values.yaml` edits, commit it alongside them
3. If unexpected fields changed, double-check the annotations you added — likely a typo

## Annotation gotchas

- Descriptions cannot contain `;` — it's the annotation separator inside `# @schema`. Rephrase.
- Item-level enums for arrays use `item: <type>; itemEnum: [...]`, **not** the nested `item: {enum: [...]}` form.
- Dependency pass-throughs (`redis`, `mongodb`, `postgresql`, `prometheus`, `mongodbOperator`) use the pattern: `# @schema additionalProperties: true` on the parent + `# @schema hidden: true` on each child the chart doesn't own. Each chart-owned `enabled` flag is left typed (boolean) since `Chart.yaml` uses it as a `condition`.

## Reference

- Plugin docs: https://github.com/losisin/helm-values-schema-json
- Annotation reference (after plugin install): `~/Library/helm/plugins/helm-values-schema-json.git/docs/README.md`
Comment thread
coderabbitai[bot] marked this conversation as resolved.
Outdated
145 changes: 114 additions & 31 deletions .github/workflows/helm-release.yml
Original file line number Diff line number Diff line change
@@ -1,15 +1,37 @@
name: Helm Charts Publish

# Package & publish new version for Helm chart to S3 bucket.
#
# Channels (single repo, branch-driven):
# - master -> stable release. Uses the version in Chart.yaml verbatim.
# Fails if that version was already published.
# - release -> pre-release. Uses "<chart-yaml-version>-release.<short-sha>"
# so every commit produces a unique chart. Pre-release suffix
# hides these from default `helm install` (clients must pass
# --devel to install). S3 lifecycle (configured on the bucket,
# not in this workflow) prunes -release.* tarballs after N days.
#
# Pull requests touching deploy/helm/** also run, but skip the publish
# steps. The version-collision check still fires, so a PR that forgets to
# bump Chart.yaml's version (when the on-disk version was already
# published) fails CI before merge.
#
# Variables (set as repository variables, not secrets — both are public
# values and we want them readable by fork PRs):
# - vars.HELM_S3_BUCKET bucket name for `aws s3 ...` operations
# - vars.HELM_REPO_URL full URL (incl. scheme) clients fetch charts from

on:
push:
branches:
- master
- release
paths:
- "deploy/helm/**"

workflow_dispatch:
pull_request:
paths:
- "deploy/helm/**"

jobs:
publish:
Expand All @@ -25,50 +47,111 @@ jobs:
uses: actions/checkout@v4

- name: Setup Helm
uses: azure/setup-helm@v1
uses: azure/setup-helm@v4
with:
version: v3.6.3
version: v4.1.4

- name: Get the chart version
- name: Compute chart version
id: chart-version
run: |
set -o xtrace
cat "${{ github.workspace }}/deploy/helm/Chart.yaml"
chart_version="$(awk '$1 == "version:" {print $2}' "${{ github.workspace }}/deploy/helm/Chart.yaml" | head -1 )"
echo "version=$chart_version" >> $GITHUB_OUTPUT
# Scan the S3 bucket with the Helm version retrieved from Chart.yaml
# If an archive with this version already exists in the bucket, fail immediately.
- name: Check chart version is not already published
env:
AWS_ACCESS_KEY_ID: "${{ secrets.HELM_AWS_ACCESS_KEY_ID }}"
AWS_SECRET_ACCESS_KEY: "${{ secrets.HELM_AWS_SECRET_ACCESS_KEY }}"
AWS_EC2_METADATA_DISABLED: true
run: |
set -o xtrace
chart_version="${{ steps.chart-version.outputs.version }}"
if [[ -z $chart_version ]]; then
echo "Empty chart version from Chart.yaml. Exiting." >&2
set -o pipefail
base="$(yq -r '.version' Chart.yaml)"
if [[ -z "$base" || "$base" == "null" ]]; then
echo "::error::Empty 'version:' in deploy/helm/Chart.yaml."
exit 1
fi
archive="appsmith-$chart_version.tgz"
if [[ "$archive" == "$(aws s3api list-objects --bucket '${{ secrets.HELM_S3_BUCKET }}' --prefix "$archive" --query 'Contents[0].Key' --output text)" ]]; then
echo "$archive is already present in the Helm bucket. Please change the version number in 'Chart.yaml' file." >&2
# Suffix only on the release branch. PRs (ref=refs/pull/...) and
# master both fall through to the base version + stable channel
# (the collision check below catches anyone who forgot to bump).
if [[ "${{ github.ref }}" == "refs/heads/release" ]]; then
sha="$(git rev-parse --short HEAD)"
version="${base}-release.${sha}"
channel="release"
else
version="${base}"
channel="stable"
fi
echo "base=${base}" >> "$GITHUB_OUTPUT"
echo "version=${version}" >> "$GITHUB_OUTPUT"
echo "channel=${channel}" >> "$GITHUB_OUTPUT"

# Stable releases are immutable — fail if a tarball at this version
# already exists at the public chart URL. Skipped for the release
# channel because pre-release versions are SHA-suffixed and never
# collide with already-published tarballs.
#
# HEAD against the public URL needs no credentials, so this check
# works identically on PRs (incl. from forks) and on push events.
- name: Check chart version is not already published
if: steps.chart-version.outputs.channel == 'stable'
run: |
base="${{ steps.chart-version.outputs.base }}"
tarball_url="${{ vars.HELM_REPO_URL }}/appsmith-${base}.tgz"
if curl -fsIL -o /dev/null "${tarball_url}"; then
echo "::error::Chart version ${base} is already published at ${tarball_url}."
echo "::error::Bump 'version:' in deploy/helm/Chart.yaml before merging."
exit 1
fi
echo "OK: chart version ${base} is not yet published."

- name: Publish Helm
# Only publish on push (or manual dispatch) against master or release.
# PRs and dispatches against feature branches run the version-collision
# check above and stop there.
if: github.ref == 'refs/heads/master' || github.ref == 'refs/heads/release'
env:
AWS_ACCESS_KEY_ID: "${{ secrets.HELM_AWS_ACCESS_KEY_ID }}"
AWS_SECRET_ACCESS_KEY: "${{ secrets.HELM_AWS_SECRET_ACCESS_KEY }}"
# Not really sure why this is needed, but without it, we see the error:
# <botocore.awsrequest.AWSRequest object at 0x7fde607adac0>
# Error: Process completed with exit code 255.
AWS_SECRET_ACCESS_KEY: "${{ secrets.HELM_AWS_SECRET_ACCESS_KEY }}"
AWS_EC2_METADATA_DISABLED: true
run: |
echo "Publishing new Helm chart version"
helm repo add bitnami https://charts.bitnami.com/bitnami
set -o xtrace
version="${{ steps.chart-version.outputs.version }}"
channel="${{ steps.chart-version.outputs.channel }}"
tarball="appsmith-${version}.tgz"

echo "Publishing chart version ${version} to channel=${channel}"

helm dep build
helm package .
aws s3 cp s3://${{ secrets.HELM_S3_BUCKET }}/index.yaml .
helm repo index . --url https://${{ secrets.HELM_S3_BUCKET }} --merge index.yaml
aws s3 cp appsmith-${{ steps.chart-version.outputs.version }}.tgz s3://${{ secrets.HELM_S3_BUCKET }}
aws s3 cp index.yaml s3://${{ secrets.HELM_S3_BUCKET }}

# Override the on-disk Chart.yaml version for release-channel builds.
helm package . --version "${version}"

# Upload the tarball. Tag release-channel objects so an S3 lifecycle
# rule can expire them after N days; stable objects stay forever.
if [[ "${channel}" == "release" ]]; then
aws s3 cp "${tarball}" "s3://${{ vars.HELM_S3_BUCKET }}/" \
--tagging "channel=release"
else
aws s3 cp "${tarball}" "s3://${{ vars.HELM_S3_BUCKET }}/"
fi

# Publish the bare values.schema.json on stable releases only.
# The schema is also bundled inside every chart .tgz (that's
# what Helm itself validates against on install). The bare URL
# exists for IDEs that fetch via the `# yaml-language-server:
# $schema=...` directive — keeping it tied to stable means
# IDE schema reflects what default `helm install` actually
# delivers, not whatever release-tip happens to ship.
if [[ "${channel}" == "stable" ]]; then
aws s3 cp values.schema.json "s3://${{ vars.HELM_S3_BUCKET }}/values.schema.json" \
--cache-control "public, max-age=60"
fi
Comment thread
coderabbitai[bot] marked this conversation as resolved.

# Regenerate index.yaml from current bucket state rather than
# appending via --merge. This way, when the lifecycle rule deletes
# expired -release.* tarballs, the next publish naturally drops
# them from the index — no separate cleanup workflow needed.
mkdir -p /tmp/reindex
aws s3 sync "s3://${{ vars.HELM_S3_BUCKET }}/" /tmp/reindex/ \
--exclude '*' --include '*.tgz'
helm repo index /tmp/reindex/ --url "${{ vars.HELM_REPO_URL }}"

# Short cache-control on index.yaml so `helm repo update` sees
# new versions promptly even if the bucket is fronted by a CDN.
# Tarballs are content-addressed (digest in index) so they can
# cache aggressively — leave their default cache-control alone.
aws s3 cp /tmp/reindex/index.yaml "s3://${{ vars.HELM_S3_BUCKET }}/index.yaml" \
--cache-control "public, max-age=60"
51 changes: 51 additions & 0 deletions .github/workflows/helm-schema.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
name: Helm Values Schema

on:
pull_request:
branches:
- release
paths:
- "deploy/helm/values.yaml"
- "deploy/helm/values.schema.json"
- ".github/workflows/helm-schema.yml"
workflow_dispatch:

jobs:
check:
runs-on: ubuntu-latest

defaults:
run:
working-directory: deploy/helm
shell: bash

steps:
- name: Checkout the code
uses: actions/checkout@v4

- name: Install Helm
uses: azure/setup-helm@v4
with:
# Helm 4 — pinned so we don't silently track future major bumps.
version: v4.1.4

- name: Install helm-values-schema-json plugin
# --verify=false is required by Helm 4's stricter plugin install path
# (the plugin source doesn't ship verification metadata).
run: helm plugin install --verify=false https://github.com/losisin/helm-values-schema-json.git

- name: Regenerate schema from values.yaml
run: |
helm schema \
--schema-root.title "Appsmith Helm chart values" \
--schema-root.id "https://helm.appsmith.com/values.schema.json" \
-o values.schema.json.regenerated

- name: Fail if committed schema is out of date
run: |
if ! diff -u values.schema.json values.schema.json.regenerated; then
echo ""
echo "::error::values.schema.json is out of date. Regenerate locally with:"
echo "::error:: cd deploy/helm && helm schema --schema-root.title 'Appsmith Helm chart values' --schema-root.id 'https://helm.appsmith.com/values.schema.json' -o values.schema.json"
exit 1
fi
2 changes: 1 addition & 1 deletion .github/workflows/helm-unittest.yml
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ on:
workflow_dispatch:

jobs:
publish:
unittest:
runs-on: ubuntu-latest

defaults:
Expand Down
Loading
Loading