Skip to content
Open
Show file tree
Hide file tree
Changes from all 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
170 changes: 170 additions & 0 deletions .github/workflows/build-helm-release.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,170 @@
name: Helm Release

on:
workflow_dispatch:
push:
branches:
- main

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

release the chart if a new tag created

@spencercjh spencercjh Jul 16, 2026

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Implemented v* tag-triggered releases in aa8cc74.

One warning is that repository tags and Chart.yaml.version are independent, so a new repository tag may point to a chart version already published by an earlier main branch version bump. The workflow therefore treats chart artifacts as immutable: chart-releaser uses skip_existing, and GHCR checks the exact OCI chart version before pushing. Existing versions are skipped instead of overwritten; publishing new chart content still requires updating Chart.yaml.version.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Can we modify this version via parameters during helm packaging?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Theoretically, of course, it’s possible, but I think that when someone modifies the contents of a chart and wants to publish it for others to use, they should explicitly update the version in Chart.yaml. When templates change frequently, it’s perfectly normal for the patch version in the chart version to be very high. Of course, the maintainer can require restrictions on version increments and decide for themselves what the chart version should be.

I think implementing version updates by passing the version number into a GitHub workflow would make the whole process quite complicated. We’d have to modify the file first, commit and push it, and then perform the Helm packaging and release.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

When templates change frequently, it’s perfectly normal for the patch version in the chart version to be very high.

Do you mean that the chart version should be independent from the entire project?

I think implementing version updates by passing the version number into a GitHub workflow would make the whole process quite complicated.

Getting the tag version in the workflow isn't complicated. Also, creating a new tag already means releasing a new version. If we require the release owner to update the version manually, it's easy to forget and end up with inconsistent version information.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Yes, the chart version can be independent from the application or repository version. However, I am not insisting that this project must use independent versioning. If the maintainers prefer to keep the repository tag, appVersion, and chart version aligned, that is a valid project policy.

I also agree that relying entirely on the release owner to remember updating Chart.yaml is error-prone. My concern is only that silently overriding the chart version during packaging may create a difference between the checked-in Chart.yaml and the published artifact.

I think CI validation would solve the problem more clearly.

For example, on a v1.3.1 tag, the workflow could verify that:

tag version        = 1.3.1
Chart.yaml version = 1.3.1
Chart.yaml appVersion = v1.3.1

If they do not match, the release should fail with a clear error asking the release owner to update Chart.yaml.

This gives us both properties:

  1. The project can keep the chart and application versions strongly aligned.
  2. Forgetting to update the version cannot result in an incorrect or missing release.

In other words, automation should prevent an inconsistent release, rather than silently changing the version of the packaged artifact.

If the project later needs to publish a chart-only fix without releasing a new application version, we can reconsider independent chart versioning at that point. But that is a separate versioning-policy decision.

The issue regarding chart versions has actually already come to light in HAMi. HAMi’s charts are already quite complex, and when we discover bugs in chart templates or default values, there’s currently no way to release a quick fix because it must be tied to the entire HAMi release. This causes users to miss out on many minor chart fixes, forcing them to fork the source code and release their own charts.

In the real world, there are numerous well-known open-source projects that use independent chart versions.

Project Chart version appVersion
kube-prometheus-stack 87.16.1 v0.92.1
Argo CD 10.1.3 v3.4.5
Grafana Loki 7.1.0 3.6.8
Traefik 41.0.2 v3.7.7
ingress-nginx 4.15.1 1.15.1
ExternalDNS 1.21.1 0.21.0
Bitnami PostgreSQL 17.1.0 17.6.0

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

The chart version is part of the source and release metadata. Choosing and updating it correctly is one of the responsibilities of maintaining and releasing the chart, just like updating release notes or documenting compatibility changes. The maintainer should still decide whether a chart change requires a patch, minor, or major version bump.

Automation can help us enforce that responsibility. For example, CI can fail when a release tag is created but the expected fields in Chart.yaml were not updated. However, that validation should be a guardrail, not a replacement for maintaining the version correctly.

Automatically injecting the Git tag during packaging may prevent one kind of mistake, but it also hides the fact that the version recorded in the repository is stale. It makes the published artifact differ from the checked-in source and treats the symptom rather than fixing the release process.

So I would separate these two questions:

Should this project keep the chart version aligned with the application version?
That is a valid project policy.
How should we prevent maintainers from forgetting to update the version?
CI should detect the inconsistency and fail the release.

Regardless of whether the two versions are aligned or independent, correctly maintaining the chart version is something we should do properly, rather than something we should avoid through packaging-time overrides.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

I'll open an issue on HAMi in a few days to elaborate on the above points again.

tags:
- "v*"
paths:
- charts/ascend-device-plugin/Chart.yaml

env:
HELM_VERSION: "v3.20.0"
CHART_PATH: charts/ascend-device-plugin
CHART_NAME: ascend-device-plugin

concurrency:
group: helm-release
cancel-in-progress: false

jobs:
detect-chart-version:
Comment thread
spencercjh marked this conversation as resolved.
if: github.event_name == 'push' && github.ref == 'refs/heads/main'
runs-on: ubuntu-latest
outputs:
version_changed: ${{ steps.compare.outputs.version_changed }}
previous_version: ${{ steps.compare.outputs.previous_version }}
current_version: ${{ steps.compare.outputs.current_version }}
steps:
- name: Checkout
uses: actions/checkout@v6
with:
fetch-depth: 0

- name: Compare chart version
id: compare
shell: bash
run: |
set -euo pipefail

before_sha="${{ github.event.before }}"
if [[ -z "${before_sha}" || "${before_sha}" == "0000000000000000000000000000000000000000" ]]; then
echo "github.event.before is not available for this push" >&2
exit 1
fi

current_version="$(awk '$1 == "version:" { print $2; exit }' "${{ env.CHART_PATH }}/Chart.yaml")"
if [[ -z "${current_version}" ]]; then
echo "failed to resolve current chart version from ${{ env.CHART_PATH }}/Chart.yaml" >&2
exit 1
fi

if git cat-file -e "${before_sha}:${{ env.CHART_PATH }}/Chart.yaml" 2>/dev/null; then
previous_version="$(git show "${before_sha}:${{ env.CHART_PATH }}/Chart.yaml" | awk '$1 == "version:" { print $2; exit }')"
else
previous_version=""
fi

if [[ -z "${previous_version}" || "${previous_version}" != "${current_version}" ]]; then
version_changed=true
else
version_changed=false
fi

{
echo "previous_version=${previous_version}"
echo "current_version=${current_version}"
echo "version_changed=${version_changed}"
} >>"$GITHUB_OUTPUT"

helm-release:
needs:
- detect-chart-version
if: >-
always() &&
(github.event_name == 'workflow_dispatch' ||
startsWith(github.ref, 'refs/tags/') ||
needs.detect-chart-version.outputs.version_changed == 'true')
permissions:
contents: write
packages: write
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v6
with:
fetch-depth: 0

- name: Configure Git
run: |
git config user.name "$GITHUB_ACTOR"
git config user.email "$GITHUB_ACTOR@users.noreply.github.com"

- name: Ensure gh-pages branch exists
shell: bash
run: |
set -euo pipefail

if git ls-remote --exit-code --heads origin gh-pages >/dev/null 2>&1; then
echo "origin/gh-pages already exists"
exit 0
fi

workdir="$(mktemp -d)"
git worktree add --detach "${workdir}" HEAD

pushd "${workdir}"
git switch --orphan gh-pages
git rm -rf . >/dev/null 2>&1 || true
touch .nojekyll
git add .nojekyll
git commit -m "chore: initialize gh-pages branch"
git push origin gh-pages
popd

git worktree remove --force "${workdir}"
git fetch origin gh-pages

- name: Set up Helm
uses: azure/setup-helm@v5.0.0
with:
version: ${{ env.HELM_VERSION }}

- name: Package Helm chart
run: |
mkdir -p .cr-release-packages
helm package "${{ env.CHART_PATH }}" --destination .cr-release-packages

- name: Run chart-releaser
uses: helm/chart-releaser-action@v1.6.0
with:
charts_dir: charts
skip_existing: true
skip_packaging: true
env:
CR_TOKEN: ${{ secrets.GITHUB_TOKEN }}

- name: Publish Helm chart to GHCR
shell: bash
env:
GHCR_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
set -euo pipefail

namespace="${GITHUB_REPOSITORY_OWNER,,}"
chart_version="$(awk '$1 == "version:" { print $2; exit }' "${{ env.CHART_PATH }}/Chart.yaml")"
package_path=".cr-release-packages/${{ env.CHART_NAME }}-${chart_version}.tgz"

if [[ -z "${chart_version}" || ! -f "${package_path}" ]]; then
echo "failed to resolve packaged chart for ${{ env.CHART_NAME }}" >&2
exit 1
fi

echo "${GHCR_TOKEN}" | helm registry login ghcr.io --username "${GITHUB_ACTOR}" --password-stdin
chart_ref="oci://ghcr.io/${namespace}/charts/${{ env.CHART_NAME }}"
lookup_error="$(mktemp)"

if helm show chart "${chart_ref}" --version "${chart_version}" >/dev/null 2>"${lookup_error}"; then
echo "${chart_ref}:${chart_version} already exists; skipping GHCR push"
exit 0
fi

if ! grep -Eqi 'not found|manifest unknown' "${lookup_error}"; then
echo "failed to query ${chart_ref}:${chart_version}" >&2
cat "${lookup_error}" >&2
exit 1
fi

helm push "${package_path}" "oci://ghcr.io/${namespace}/charts"
85 changes: 85 additions & 0 deletions .github/workflows/helm-lint.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
name: Helm Lint

on:
pull_request:
branches:
- main

env:
HELM_VERSION: "v3.20.0"

permissions:
contents: read
pull-requests: read

concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true

jobs:
changes:
runs-on: ubuntu-latest
outputs:
helm: ${{ steps.filter.outputs.helm }}
steps:
- name: Detect Helm changes
uses: dorny/paths-filter@v4
id: filter
with:
filters: |
helm:
- 'charts/ascend-device-plugin/**'
- 'Makefile'
- '.github/workflows/helm-lint.yaml'
- '.github/workflows/build-helm-release.yaml'

lint-test:
runs-on: ubuntu-latest
needs: changes
if: needs.changes.outputs.helm == 'true'
steps:
- name: Checkout
uses: actions/checkout@v6
Comment thread
spencercjh marked this conversation as resolved.

- name: Set up Helm
uses: azure/setup-helm@v5.0.0
with:
version: ${{ env.HELM_VERSION }}

- name: Set up Go
uses: actions/setup-go@v6
with:
go-version-file: go.mod

- name: Set up helm-docs
uses: gabe565/setup-helm-docs-action@v1
with:
version: v1.14.2

- name: Run Helm chart verification
run: make verify-helm-chart

helm-lint-required:
runs-on: ubuntu-latest
needs:
- changes
- lint-test
if: always()
steps:
- name: Check Helm workflow result
shell: bash
run: |
if [[ "${{ needs.changes.result }}" != "success" ]]; then
echo "Helm change detection did not complete successfully: ${{ needs.changes.result }}"
exit 1
fi

if [[ "${{ needs.changes.outputs.helm }}" != "true" ]]; then
echo "No Helm-relevant changes."
exit 0
fi

if [[ "${{ needs.lint-test.result }}" != "success" ]]; then
echo "Helm verification did not complete successfully: ${{ needs.lint-test.result }}"
exit 1
fi
75 changes: 75 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
### Linux template
*~

# temporary files which can be created if a process still has a handle open of a deleted file
.fuse_hidden*

# KDE directory preferences
.directory

# Linux trash folder which might appear on any partition or disk
.Trash-*

# .nfs files are created when an open file is removed but is still being accessed
.nfs*

### Go template
# If you prefer the allow list template instead of the deny list, see community template:
# https://github.com/github/gitignore/blob/main/community/Golang/Go.AllowList.gitignore
#
# Binaries for programs and plugins
*.exe
*.exe~
*.dll
*.so
*.dylib

# Test binary, built with `go test -c`
*.test

# Output of the go coverage tool, specifically when used with LiteIDE
*.out

# Dependency directories (remove the comment below to include it)
# vendor/

# Go workspace file
go.work
go.work.sum

# env file
.env

### macOS template
# General
.DS_Store
.AppleDouble
.LSOverride

# Icon must end with two \r
Icon

# Thumbnails
._*

# Files that might appear in the root of a volume
.DocumentRevisions-V100
.fseventsd
.Spotlight-V100
.TemporaryItems
.Trashes
.VolumeIcon.icns
.com.apple.timemachine.donotpresent

# Directories potentially created on remote AFP share
.AppleDB
.AppleDesktop
Network Trash Folder
Temporary Items
.apdisk


### Custom
.idea/
.vscode/
.worktree/
14 changes: 13 additions & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,19 @@ lint:
ascend-device-plugin:
$(GO) build $(BUILDARGS) -o ./ascend-device-plugin ./cmd/main.go

.PHONY: update-chart-docs
update-chart-docs:
cd charts/ascend-device-plugin && helm-docs --skip-version-footer
cd charts/ascend-device-plugin && $(GO) run github.com/losisin/helm-values-schema-json@v1.9.2 -input values.yaml -output values.schema.json

.PHONY: verify-helm-chart
verify-helm-chart:
$(MAKE) update-chart-docs
git diff --exit-code -- charts/ascend-device-plugin/README.md charts/ascend-device-plugin/values.schema.json
helm lint charts/ascend-device-plugin
helm template ascend-device-plugin charts/ascend-device-plugin >/dev/null

clean:
rm -rf ./ascend-device-plugin

.PHONY: all tidy test lint clean
.PHONY: all tidy test lint clean update-chart-docs verify-helm-chart
Loading
Loading