From d8cd090c43350b4c85dcc0045d6ee72ffcdee855 Mon Sep 17 00:00:00 2001 From: Andrew Block Date: Mon, 24 Nov 2025 12:29:09 -0600 Subject: [PATCH 1/2] feat(helm): automate release process Signed-off-by: Andrew Block --- .github/workflows/helm-release.yaml | 125 ++++++++++++++++++ charts/kubernetes-mcp-server/Chart.yaml | 1 + charts/kubernetes-mcp-server/README.md | 8 +- charts/kubernetes-mcp-server/README.md.gotmpl | 4 +- 4 files changed, 132 insertions(+), 6 deletions(-) create mode 100644 .github/workflows/helm-release.yaml diff --git a/.github/workflows/helm-release.yaml b/.github/workflows/helm-release.yaml new file mode 100644 index 00000000..f1699561 --- /dev/null +++ b/.github/workflows/helm-release.yaml @@ -0,0 +1,125 @@ +name: Helm Chart Release + +on: + workflow_dispatch: + inputs: + chart_version: + description: 'Chart version to release (e.g., 0.1.0)' + required: true + type: string + app_version: + description: 'App version (defaults to latest if not specified)' + required: false + type: string + default: 'latest' + +env: + REGISTRY: ghcr.io + CHART_NAME: kubernetes-mcp-server + +concurrency: + group: helm-release-${{ github.ref }} + cancel-in-progress: false + +jobs: + validate-and-package: + runs-on: ubuntu-latest + permissions: + contents: read + packages: write + + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Install Helm + uses: azure/setup-helm@v4 + with: + version: '3.19.2' + + - name: Validate chart version format + run: | + if [[ ! "${{ inputs.chart_version }}" =~ ^[0-9]+\.[0-9]+\.[0-9]+$ ]]; then + echo "Error: Chart version must be in format X.Y.Z (e.g., 0.1.0)" + exit 1 + fi + + - name: Update Chart.yaml versions + run: | + sed -i "s/version: .*/version: ${{ inputs.chart_version }}/" charts/kubernetes-mcp-server/Chart.yaml + sed -i "s/appVersion: .*/appVersion: \"${{ inputs.app_version }}\"/" charts/kubernetes-mcp-server/Chart.yaml + + echo "Updated Chart.yaml:" + cat charts/kubernetes-mcp-server/Chart.yaml + - name: Lint Helm chart + run: | + helm lint charts/kubernetes-mcp-server + + - name: Template Helm chart (dry run) + run: | + helm template test-release charts/kubernetes-mcp-server --set ingress.host=localhost --debug + - name: Run Helm unit tests (if kubeconform is available) + run: | + # Install kubeconform for validation + curl -L https://github.com/yannh/kubeconform/releases/latest/download/kubeconform-linux-amd64.tar.gz | tar xz + sudo mv kubeconform /usr/local/bin + + # Validate generated manifests + helm template test-release charts/kubernetes-mcp-server --set ingress.host=localhost | kubeconform -strict -summary -ignore-missing-schemas + + - name: Package Helm chart + run: | + helm package charts/kubernetes-mcp-server --destination ./chart-packages + echo "Generated packages:" + ls -la ./chart-packages/ + + - name: Login to Container Registry + run: | + echo ${{ secrets.GITHUB_TOKEN }} | helm registry login ${{ env.REGISTRY }} -u ${{ github.actor }} --password-stdin + + - name: Push chart to OCI registry + run: | + chart_package=$(ls ./chart-packages/kubernetes-mcp-server-*.tgz) + echo "Pushing chart package: $chart_package" + helm push "$chart_package" oci://${{ env.REGISTRY }}/${{ github.repository_owner }}/charts + + - name: Verify chart installation + run: | + # Verify the chart we just pushed works (client-only, no cluster needed) + echo "Testing chart template rendering from OCI registry..." + helm template test-install oci://${{ env.REGISTRY }}/${{ github.repository_owner }}/charts/kubernetes-mcp-server --set ingress.host=localhost --version ${{ inputs.chart_version }} --debug + + - name: Generate release summary + run: | + echo "## Helm Chart Release Summary" >> $GITHUB_STEP_SUMMARY + echo "" >> $GITHUB_STEP_SUMMARY + echo "- **Chart Name:** ${{ env.CHART_NAME }}" >> $GITHUB_STEP_SUMMARY + echo "- **Chart Version:** ${{ inputs.chart_version }}" >> $GITHUB_STEP_SUMMARY + echo "- **App Version:** ${{ inputs.app_version }}" >> $GITHUB_STEP_SUMMARY + echo "- **Registry:** ${{ env.REGISTRY }}/${{ github.repository_owner }}/charts" >> $GITHUB_STEP_SUMMARY + echo "" >> $GITHUB_STEP_SUMMARY + echo "### Installation Command" >> $GITHUB_STEP_SUMMARY + echo "\`\`\`bash" >> $GITHUB_STEP_SUMMARY + echo "helm install kubernetes-mcp-server oci://${{ env.REGISTRY }}/${{ github.repository_owner }}/charts/kubernetes-mcp-server --set ingress.host= --version ${{ inputs.chart_version }} --create-namespace --namespace mcp-system" >> $GITHUB_STEP_SUMMARY + echo "\`\`\`" >> $GITHUB_STEP_SUMMARY + + create-git-tag: + needs: validate-and-package + runs-on: ubuntu-latest + permissions: + contents: write + + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Create and push git tag + run: | + git config user.name "github-actions[bot]" + git config user.email "github-actions[bot]@users.noreply.github.com" + + tag_name="chart-v${{ inputs.chart_version }}" + git tag -a "$tag_name" -m "Helm chart release v${{ inputs.chart_version }}" + git push origin "$tag_name" + + echo "Created git tag: $tag_name" \ No newline at end of file diff --git a/charts/kubernetes-mcp-server/Chart.yaml b/charts/kubernetes-mcp-server/Chart.yaml index a66650bb..01fc3f75 100644 --- a/charts/kubernetes-mcp-server/Chart.yaml +++ b/charts/kubernetes-mcp-server/Chart.yaml @@ -11,3 +11,4 @@ maintainers: - name: Marc Nuri email: marc.nuri@redhat.com version: 0.1.0 +appVersion: "latest" diff --git a/charts/kubernetes-mcp-server/README.md b/charts/kubernetes-mcp-server/README.md index 49b5759d..e7d27def 100644 --- a/charts/kubernetes-mcp-server/README.md +++ b/charts/kubernetes-mcp-server/README.md @@ -1,6 +1,6 @@ # kubernetes-mcp-server -![Version: 0.1.0](https://img.shields.io/badge/Version-0.1.0-informational?style=flat-square) +![Version: 0.1.0](https://img.shields.io/badge/Version-0.1.0-informational?style=flat-square) ![AppVersion: latest](https://img.shields.io/badge/AppVersion-latest-informational?style=flat-square) Helm Chart for the Kubernetes MCP Server @@ -20,12 +20,12 @@ The Chart can be installed quickly and easily to a Kubernetes cluster. Since an Install the Chart using the following command from the root of this directory: ```shell -helm upgrade -i -n kubernetes-mcp-server --create-namespace kubernetes-mcp-server . --set openshift=true --set ingress.host= +helm upgrade -i -n kubernetes-mcp-server --create-namespace kubernetes-mcp-server oci://ghcr.io/containers/charts/kubernetes-mcp-server --set ingress.host= ``` ### Optimized OpenShift Deployment -Functionality has bee added to the Chart to simplify the deployment to OpenShift Cluster. +Functionality has been added to the Chart to simplify the deployment to OpenShift Cluster. ## Values @@ -72,5 +72,5 @@ The contents of the README.md file is generated using [helm-docs](https://github Execute the following command to regenerate the documentation from within the Helm Chart directory. ```shell -helm-docs -t README.md.gotpl +helm-docs -t README.md.gotmpl ``` diff --git a/charts/kubernetes-mcp-server/README.md.gotmpl b/charts/kubernetes-mcp-server/README.md.gotmpl index b1b30d68..5f8c3ef0 100644 --- a/charts/kubernetes-mcp-server/README.md.gotmpl +++ b/charts/kubernetes-mcp-server/README.md.gotmpl @@ -20,7 +20,7 @@ The Chart can be installed quickly and easily to a Kubernetes cluster. Since an Install the Chart using the following command from the root of this directory: ```shell -helm upgrade -i -n kubernetes-mcp-server --create-namespace kubernetes-mcp-server . --set openshift=true --set ingress.host= +helm upgrade -i -n kubernetes-mcp-server --create-namespace kubernetes-mcp-server oci://ghcr.io/containers/charts/kubernetes-mcp-server --set ingress.host= ``` ### Optimized OpenShift Deployment @@ -36,5 +36,5 @@ The contents of the README.md file is generated using [helm-docs](https://github Execute the following command to regenerate the documentation from within the Helm Chart directory. ```shell -helm-docs -t README.md.gotpl +helm-docs -t README.md.gotmpl ``` From 731c311eae9d077b50810d232a1b591115983ab3 Mon Sep 17 00:00:00 2001 From: Marc Nuri Date: Fri, 28 Nov 2025 16:06:53 +0100 Subject: [PATCH 2/2] feat(helm): app versioned charts Signed-off-by: Marc Nuri --- .github/workflows/helm-release.yaml | 125 ---------------------------- .github/workflows/release-helm.yaml | 80 ++++++++++++++++++ Makefile | 7 +- build/helm.mk | 81 ++++++++++++++++++ 4 files changed, 167 insertions(+), 126 deletions(-) delete mode 100644 .github/workflows/helm-release.yaml create mode 100644 .github/workflows/release-helm.yaml create mode 100644 build/helm.mk diff --git a/.github/workflows/helm-release.yaml b/.github/workflows/helm-release.yaml deleted file mode 100644 index f1699561..00000000 --- a/.github/workflows/helm-release.yaml +++ /dev/null @@ -1,125 +0,0 @@ -name: Helm Chart Release - -on: - workflow_dispatch: - inputs: - chart_version: - description: 'Chart version to release (e.g., 0.1.0)' - required: true - type: string - app_version: - description: 'App version (defaults to latest if not specified)' - required: false - type: string - default: 'latest' - -env: - REGISTRY: ghcr.io - CHART_NAME: kubernetes-mcp-server - -concurrency: - group: helm-release-${{ github.ref }} - cancel-in-progress: false - -jobs: - validate-and-package: - runs-on: ubuntu-latest - permissions: - contents: read - packages: write - - steps: - - name: Checkout - uses: actions/checkout@v4 - - - name: Install Helm - uses: azure/setup-helm@v4 - with: - version: '3.19.2' - - - name: Validate chart version format - run: | - if [[ ! "${{ inputs.chart_version }}" =~ ^[0-9]+\.[0-9]+\.[0-9]+$ ]]; then - echo "Error: Chart version must be in format X.Y.Z (e.g., 0.1.0)" - exit 1 - fi - - - name: Update Chart.yaml versions - run: | - sed -i "s/version: .*/version: ${{ inputs.chart_version }}/" charts/kubernetes-mcp-server/Chart.yaml - sed -i "s/appVersion: .*/appVersion: \"${{ inputs.app_version }}\"/" charts/kubernetes-mcp-server/Chart.yaml - - echo "Updated Chart.yaml:" - cat charts/kubernetes-mcp-server/Chart.yaml - - name: Lint Helm chart - run: | - helm lint charts/kubernetes-mcp-server - - - name: Template Helm chart (dry run) - run: | - helm template test-release charts/kubernetes-mcp-server --set ingress.host=localhost --debug - - name: Run Helm unit tests (if kubeconform is available) - run: | - # Install kubeconform for validation - curl -L https://github.com/yannh/kubeconform/releases/latest/download/kubeconform-linux-amd64.tar.gz | tar xz - sudo mv kubeconform /usr/local/bin - - # Validate generated manifests - helm template test-release charts/kubernetes-mcp-server --set ingress.host=localhost | kubeconform -strict -summary -ignore-missing-schemas - - - name: Package Helm chart - run: | - helm package charts/kubernetes-mcp-server --destination ./chart-packages - echo "Generated packages:" - ls -la ./chart-packages/ - - - name: Login to Container Registry - run: | - echo ${{ secrets.GITHUB_TOKEN }} | helm registry login ${{ env.REGISTRY }} -u ${{ github.actor }} --password-stdin - - - name: Push chart to OCI registry - run: | - chart_package=$(ls ./chart-packages/kubernetes-mcp-server-*.tgz) - echo "Pushing chart package: $chart_package" - helm push "$chart_package" oci://${{ env.REGISTRY }}/${{ github.repository_owner }}/charts - - - name: Verify chart installation - run: | - # Verify the chart we just pushed works (client-only, no cluster needed) - echo "Testing chart template rendering from OCI registry..." - helm template test-install oci://${{ env.REGISTRY }}/${{ github.repository_owner }}/charts/kubernetes-mcp-server --set ingress.host=localhost --version ${{ inputs.chart_version }} --debug - - - name: Generate release summary - run: | - echo "## Helm Chart Release Summary" >> $GITHUB_STEP_SUMMARY - echo "" >> $GITHUB_STEP_SUMMARY - echo "- **Chart Name:** ${{ env.CHART_NAME }}" >> $GITHUB_STEP_SUMMARY - echo "- **Chart Version:** ${{ inputs.chart_version }}" >> $GITHUB_STEP_SUMMARY - echo "- **App Version:** ${{ inputs.app_version }}" >> $GITHUB_STEP_SUMMARY - echo "- **Registry:** ${{ env.REGISTRY }}/${{ github.repository_owner }}/charts" >> $GITHUB_STEP_SUMMARY - echo "" >> $GITHUB_STEP_SUMMARY - echo "### Installation Command" >> $GITHUB_STEP_SUMMARY - echo "\`\`\`bash" >> $GITHUB_STEP_SUMMARY - echo "helm install kubernetes-mcp-server oci://${{ env.REGISTRY }}/${{ github.repository_owner }}/charts/kubernetes-mcp-server --set ingress.host= --version ${{ inputs.chart_version }} --create-namespace --namespace mcp-system" >> $GITHUB_STEP_SUMMARY - echo "\`\`\`" >> $GITHUB_STEP_SUMMARY - - create-git-tag: - needs: validate-and-package - runs-on: ubuntu-latest - permissions: - contents: write - - steps: - - name: Checkout - uses: actions/checkout@v4 - - - name: Create and push git tag - run: | - git config user.name "github-actions[bot]" - git config user.email "github-actions[bot]@users.noreply.github.com" - - tag_name="chart-v${{ inputs.chart_version }}" - git tag -a "$tag_name" -m "Helm chart release v${{ inputs.chart_version }}" - git push origin "$tag_name" - - echo "Created git tag: $tag_name" \ No newline at end of file diff --git a/.github/workflows/release-helm.yaml b/.github/workflows/release-helm.yaml new file mode 100644 index 00000000..cb4360df --- /dev/null +++ b/.github/workflows/release-helm.yaml @@ -0,0 +1,80 @@ +name: Release Helm Chart + +on: + push: + tags: + - '*' + workflow_dispatch: + +env: + HELM_REGISTRY_USER: ${{ github.actor }} + HELM_REGISTRY_PASSWORD: ${{ secrets.GITHUB_TOKEN }} + HELM_REGISTRY_ORG: ${{ github.repository_owner }} + +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: false + +jobs: + release: + name: Release Helm Chart + if: github.repository == 'containers/kubernetes-mcp-server' + runs-on: ubuntu-latest + permissions: + contents: read + packages: write + + steps: + - name: Checkout + uses: actions/checkout@v6 + + - name: Install Helm + uses: azure/setup-helm@v4 + with: + version: '3.19.2' + + - name: Extract versions + id: versions + run: | + CHART_VERSION=$(make helm-print-chart-version) + echo "chart_version=$CHART_VERSION" >> $GITHUB_OUTPUT + echo "Chart version: $CHART_VERSION" + + APP_VERSION=$(make print-git-tag-version) + echo "app_version=$APP_VERSION" >> $GITHUB_OUTPUT + echo "App version: $APP_VERSION" + + HELM_REGISTRY=$(make helm-print-registry) + echo "helm_registry=$HELM_REGISTRY" >> $GITHUB_OUTPUT + echo "Helm registry: $HELM_REGISTRY" + + CHART_NAME=$(make helm-print-chart-name) + echo "chart_name=$CHART_NAME" >> $GITHUB_OUTPUT + echo "Chart name: $CHART_NAME" + + - name: Validate with kubeconform + run: make helm-validate + + - name: Login to Helm registry + run: | + echo "${{ secrets.GITHUB_TOKEN }}" | helm registry login ${{ steps.versions.outputs.helm_registry }} -u ${{ github.actor }} --password-stdin + + - name: Package Helm chart and push chart to OCI registry + run: make helm-push + + - name: Verify chart installation + run: make helm-verify + + - name: Generate release summary + run: | + echo "## Helm Chart Release Summary" >> $GITHUB_STEP_SUMMARY + echo "" >> $GITHUB_STEP_SUMMARY + echo "- **Chart Name:** ${{ steps.versions.outputs.chart_name }}" >> $GITHUB_STEP_SUMMARY + echo "- **Chart Version:** ${{ steps.versions.outputs.chart_version }}" >> $GITHUB_STEP_SUMMARY + echo "- **App Version:** ${{ steps.versions.outputs.app_version }}" >> $GITHUB_STEP_SUMMARY + echo "- **Registry:** ${{ steps.versions.outputs.helm_registry }}/${{ github.repository_owner }}/charts" >> $GITHUB_STEP_SUMMARY + echo "" >> $GITHUB_STEP_SUMMARY + echo "### Installation Command" >> $GITHUB_STEP_SUMMARY + echo "\`\`\`bash" >> $GITHUB_STEP_SUMMARY + echo "helm install ${{ steps.versions.outputs.chart_name }} oci://${{ steps.versions.outputs.helm_registry }}/${{ github.repository_owner }}/charts/${{ steps.versions.outputs.chart_name }} --set ingress.host= --version ${{ steps.versions.outputs.chart_version }} --create-namespace --namespace mcp-system" >> $GITHUB_STEP_SUMMARY + echo "\`\`\`" >> $GITHUB_STEP_SUMMARY diff --git a/Makefile b/Makefile index e0015dda..82ee91b5 100644 --- a/Makefile +++ b/Makefile @@ -70,8 +70,9 @@ format: ## Format the code tidy: ## Tidy up the go modules go mod tidy +# Download and install golangci-lint if not already installed .PHONY: golangci-lint -golangci-lint: ## Download and install golangci-lint if not already installed +golangci-lint: @[ -f $(GOLANGCI_LINT) ] || { \ set -e ;\ curl -sSfL https://raw.githubusercontent.com/golangci/golangci-lint/master/install.sh | sh -s -- -b $(shell dirname $(GOLANGCI_LINT)) $(GOLANGCI_LINT_VERSION) ;\ @@ -122,5 +123,9 @@ local-env-setup: ## Setup complete local development environment with Kind clust local-env-teardown: ## Tear down the local Kind cluster $(MAKE) kind-delete-cluster +.PHONY: print-git-tag-version +print-git-tag-version: ## Print the GIT_TAG_VERSION + @echo $(GIT_TAG_VERSION) + # Include build configuration files -include build/*.mk diff --git a/build/helm.mk b/build/helm.mk new file mode 100644 index 00000000..9aa746db --- /dev/null +++ b/build/helm.mk @@ -0,0 +1,81 @@ +##@ Helm Chart build targets + +HELM_CHART_DIR = ./charts/kubernetes-mcp-server +HELM_CHART_VERSION = $(shell grep '^version:' $(HELM_CHART_DIR)/Chart.yaml | awk '{print $$2}') +HELM_PACKAGE_DIR = ./_output/helm-packages +HELM_REGISTRY ?= ghcr.io +HELM_REGISTRY_ORG ?= containers +HELM_CHART_NAME = kubernetes-mcp-server + +KUBECONFORM = $(shell pwd)/_output/tools/bin/kubeconform +KUBECONFORM_VERSION ?= latest + +CLEAN_TARGETS += $(HELM_PACKAGE_DIR) + +.PHONY: helm-lint +helm-lint: ## Lint the Helm chart + helm lint $(HELM_CHART_DIR) + +.PHONY: helm-template +helm-template: ## Render Helm chart templates (dry run) + helm template test-release $(HELM_CHART_DIR) --set ingress.host=localhost --debug + +# Download and install kubeconform if not already installed +.PHONY: kubeconform +kubeconform: + @[ -f $(KUBECONFORM) ] || { \ + set -e ;\ + echo "Installing kubeconform to $(KUBECONFORM)..." ;\ + mkdir -p $(shell dirname $(KUBECONFORM)) ;\ + TMPDIR=$$(mktemp -d) ;\ + curl -L https://github.com/yannh/kubeconform/releases/$(KUBECONFORM_VERSION)/download/kubeconform-$(shell uname -s | tr '[:upper:]' '[:lower:]')-$(shell uname -m | sed 's/x86_64/amd64/;s/aarch64/arm64/').tar.gz | tar xz -C $$TMPDIR ;\ + mv $$TMPDIR/kubeconform $(KUBECONFORM) ;\ + rm -rf $$TMPDIR ;\ + } + +.PHONY: helm-validate +helm-validate: kubeconform ## Validate Helm chart manifests with kubeconform + helm template test-release $(HELM_CHART_DIR) --set ingress.host=localhost | $(KUBECONFORM) -strict -summary -ignore-missing-schemas + +.PHONY: helm-package +helm-package: helm-lint helm-template ## Package the Helm chart + @mkdir -p $(HELM_PACKAGE_DIR) + @echo "Updating appVersion to $(GIT_TAG_VERSION)..." + @sed -i.bak "s/appVersion: .*/appVersion: \"$(GIT_TAG_VERSION)\"/" $(HELM_CHART_DIR)/Chart.yaml + @echo "Updated Chart.yaml:" + @cat $(HELM_CHART_DIR)/Chart.yaml + helm package $(HELM_CHART_DIR) --destination $(HELM_PACKAGE_DIR) + @mv $(HELM_CHART_DIR)/Chart.yaml.bak $(HELM_CHART_DIR)/Chart.yaml + +.PHONY: helm-push +helm-push: helm-package ## Push Helm chart to OCI registry (assumes helm registry login has been performed) + @chart_package=$$(ls $(HELM_PACKAGE_DIR)/$(HELM_CHART_NAME)-*.tgz 2>/dev/null | head -n 1); \ + if [ -z "$$chart_package" ]; then echo "Error: No chart package found in $(HELM_PACKAGE_DIR)"; exit 1; fi; \ + echo "Pushing chart package: $$chart_package"; \ + helm push "$$chart_package" oci://$(HELM_REGISTRY)/$(HELM_REGISTRY_ORG)/charts + + +.PHONY: helm-verify +helm-verify: ## Verify chart installation from OCI registry + @echo "Testing chart template rendering from OCI registry..." + helm template test-install oci://$(HELM_REGISTRY)/$(HELM_REGISTRY_ORG)/charts/$(HELM_CHART_NAME) \ + --set ingress.host=localhost --version $(HELM_CHART_VERSION) --debug + +.PHONY: helm-publish +helm-publish: helm-package helm-push helm-verify ## Package, push, and verify Helm chart release + @echo "Helm chart $(HELM_CHART_NAME) version $(HELM_CHART_VERSION) published successfully" + +# Print the Helm chart version +.PHONY: helm-print-chart-version +helm-print-chart-version: + @echo $(HELM_CHART_VERSION) + +# Print the Helm chart name +.PHONY: helm-print-chart-name +helm-print-chart-name: + @echo $(HELM_CHART_NAME) + +# Print the Helm registry +.PHONY: helm-print-registry +helm-print-registry: + @echo $(HELM_REGISTRY)