Skip to content

Commit 7962b9b

Browse files
Merge pull request #4 from forkspacer/feat/add-helm-chart-and-release-automation
feat: add Helm chart and release automation
2 parents 1f92230 + 0204387 commit 7962b9b

File tree

9 files changed

+896
-2
lines changed

9 files changed

+896
-2
lines changed

.github/templates/helm-page.html

Lines changed: 522 additions & 0 deletions
Large diffs are not rendered by default.

.github/workflows/release.yml

Lines changed: 39 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,8 @@ on:
88
permissions:
99
contents: write
1010
packages: write
11+
pages: write
12+
id-token: write
1113

1214
env:
1315
IMAGE_REPO: ghcr.io/forkspacer/operator-ui
@@ -27,7 +29,12 @@ jobs:
2729
cache: 'npm'
2830

2931
- name: Extract release tag
30-
run: echo "VERSION=${GITHUB_REF#refs/tags/}" >> $GITHUB_ENV
32+
run: |
33+
echo "VERSION=${GITHUB_REF#refs/tags/}" >> $GITHUB_ENV
34+
echo "CHART_VERSION=${GITHUB_REF#refs/tags/v}" >> $GITHUB_ENV
35+
36+
- name: Update Helm Chart version
37+
run: make update-version VERSION=$VERSION
3138

3239
- name: Install dependencies
3340
run: npm ci
@@ -56,6 +63,37 @@ jobs:
5663
cache-from: type=gha
5764
cache-to: type=gha,mode=max
5865

66+
- name: Install Helm
67+
uses: azure/[email protected]
68+
69+
- name: Create charts directory
70+
run: |
71+
make build-charts-site
72+
73+
- name: Setup Pages
74+
uses: actions/configure-pages@v5
75+
76+
- name: Upload Pages artifact
77+
uses: actions/upload-pages-artifact@v3
78+
with:
79+
path: charts-site
80+
81+
- name: Deploy to GitHub Pages
82+
uses: actions/deploy-pages@v4
83+
84+
- name: Notify forkspacer repository of new version
85+
uses: peter-evans/repository-dispatch@v3
86+
with:
87+
token: ${{ secrets.DEPENDENCY_UPDATE_TOKEN }}
88+
repository: forkspacer/forkspacer
89+
event-type: dependency-update
90+
client-payload: |
91+
{
92+
"component": "operator-ui",
93+
"version": "${{ env.VERSION }}",
94+
"chart_version": "${{ env.CHART_VERSION }}"
95+
}
96+
5997
- name: Create GitHub Release
6098
uses: actions/create-release@v1
6199
env:

Makefile

Lines changed: 87 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,4 +13,90 @@ build: ## Build for production
1313

1414
.PHONY: install
1515
install: ## Install dependencies
16-
npm install
16+
npm install
17+
18+
##@ Version Management
19+
20+
.PHONY: update-version
21+
update-version: ## Update version in Helm chart. Usage: make update-version VERSION=v1.0.1
22+
@if [ -z "$(VERSION)" ]; then \
23+
echo "❌ Error: VERSION not specified. Usage: make update-version VERSION=v1.0.1"; \
24+
exit 1; \
25+
fi
26+
@echo "🔄 Updating operator-ui to $(VERSION)..."
27+
@CHART_VERSION=$${VERSION#v}; \
28+
sed -i.bak "s/^version: .*/version: $$CHART_VERSION/" helm/Chart.yaml
29+
@sed -i.bak "s/^appVersion: .*/appVersion: \"$(VERSION)\"/" helm/Chart.yaml
30+
@sed -i.bak "s/tag: \".*\"/tag: \"$(VERSION)\"/" helm/values.yaml
31+
@sed -i.bak "s/^VERSION ?= .*/VERSION ?= $(VERSION)/" Makefile
32+
@rm -f helm/Chart.yaml.bak helm/values.yaml.bak Makefile.bak
33+
@echo "✅ Updated operator-ui to $(VERSION)"
34+
35+
##@ Helm Charts
36+
37+
.PHONY: helm-package
38+
helm-package: ## Package Helm chart
39+
@echo "📦 Packaging Helm chart..."
40+
@CHART_VERSION=$$(grep '^version:' helm/Chart.yaml | awk '{print $$2}' | tr -d '"' | tr -d "'"); \
41+
helm package helm/
42+
@echo "✅ Helm chart packaged"
43+
44+
.PHONY: build-charts-site
45+
build-charts-site: helm-package ## Create charts directory for GitHub Pages
46+
@CHART_VERSION=$$(grep '^version:' helm/Chart.yaml | awk '{print $$2}' | tr -d '"' | tr -d "'"); \
47+
CHART_FILE="operator-ui-$$CHART_VERSION.tgz"; \
48+
CHARTS_DIR="charts-site"; \
49+
echo "📦 Preparing charts site..."; \
50+
mkdir -p "$$CHARTS_DIR"
51+
@echo "📥 Fetching existing charts from GitHub Pages..."
52+
@if curl -fsSL https://forkspacer.github.io/operator-ui/index.yaml -o /tmp/current-index.yaml 2>/dev/null; then \
53+
echo "✅ Found existing Helm repository"; \
54+
else \
55+
echo "ℹ️ No existing charts found (first deployment)"; \
56+
fi
57+
@CHART_VERSION=$$(grep '^version:' helm/Chart.yaml | awk '{print $$2}' | tr -d '"' | tr -d "'"); \
58+
CHART_FILE="operator-ui-$$CHART_VERSION.tgz"; \
59+
CHARTS_DIR="charts-site"; \
60+
if [ -f /tmp/current-index.yaml ]; then \
61+
grep -oP 'https://forkspacer\.github\.io/operator-ui/operator-ui-[0-9]+\.[0-9]+\.[0-9]+(-[a-zA-Z0-9.]+)?\.tgz' /tmp/current-index.yaml | sort -u | while read url; do \
62+
filename=$$(basename "$$url"); \
63+
if [ "$$filename" = "$$CHART_FILE" ]; then \
64+
echo " ⏭️ Skipping $$filename (will be replaced)"; \
65+
continue; \
66+
fi; \
67+
echo " 📥 Downloading $$filename..."; \
68+
if curl -fsSL "$$url" -o "$$CHARTS_DIR/$$filename"; then \
69+
echo " ✅ Downloaded $$filename"; \
70+
else \
71+
echo " ⚠️ Failed to download $$filename"; \
72+
fi; \
73+
done; \
74+
fi
75+
@CHART_VERSION=$$(grep '^version:' helm/Chart.yaml | awk '{print $$2}' | tr -d '"' | tr -d "'"); \
76+
CHART_FILE="operator-ui-$$CHART_VERSION.tgz"; \
77+
CHARTS_DIR="charts-site"; \
78+
echo "✅ Downloaded $$(ls $$CHARTS_DIR/operator-ui-*.tgz 2>/dev/null | wc -l) existing chart(s)"; \
79+
cp "$$CHART_FILE" "$$CHARTS_DIR/"; \
80+
echo "✅ Added new chart: $$CHART_FILE"
81+
@CHARTS_DIR="charts-site"; \
82+
echo "📄 Generating Helm repo index..."; \
83+
helm repo index "$$CHARTS_DIR" --url https://forkspacer.github.io/operator-ui
84+
@CHARTS_DIR="charts-site"; \
85+
if [ -f ".github/templates/helm-page.html" ]; then \
86+
cp .github/templates/helm-page.html "$$CHARTS_DIR/index.html"; \
87+
else \
88+
echo "⚠️ Warning: .github/templates/helm-page.html not found, skipping HTML generation"; \
89+
fi
90+
@CHART_VERSION=$$(grep '^version:' helm/Chart.yaml | awk '{print $$2}' | tr -d '"' | tr -d "'"); \
91+
APP_VERSION=$$(grep '^appVersion:' helm/Chart.yaml | awk '{print $$2}' | tr -d '"' | tr -d "'"); \
92+
CHARTS_DIR="charts-site"; \
93+
if [ -f "$$CHARTS_DIR/index.html" ]; then \
94+
sed -i "s/{{VERSION_TAG}}/$$APP_VERSION/g" "$$CHARTS_DIR/index.html"; \
95+
sed -i "s/{{VERSION_NUMBER}}/$$CHART_VERSION/g" "$$CHARTS_DIR/index.html"; \
96+
echo "✅ Generated index.html from template"; \
97+
fi
98+
@CHARTS_DIR="charts-site"; \
99+
echo "✅ Charts site ready with $$(ls $$CHARTS_DIR/operator-ui-*.tgz 2>/dev/null | wc -l) chart version(s)"; \
100+
echo ""; \
101+
echo "📦 Available versions:"; \
102+
ls -lh $$CHARTS_DIR/operator-ui-*.tgz 2>/dev/null || echo "No charts found"

helm/Chart.yaml

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
apiVersion: v2
2+
name: operator-ui
3+
description: Web UI for Forkspacer operator
4+
type: application
5+
version: 0.1.1
6+
appVersion: "v0.1.1"
7+
keywords:
8+
- ui
9+
- frontend
10+
- forkspacer
11+
home: https://github.com/forkspacer/operator-ui
12+
sources:
13+
- https://github.com/forkspacer/operator-ui
14+
maintainers:
15+
- name: Forkspacer Team
16+

helm/templates/_helpers.tpl

Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
{{/*
2+
Expand the name of the chart.
3+
*/}}
4+
{{- define "operator-ui.name" -}}
5+
{{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" }}
6+
{{- end }}
7+
8+
{{/*
9+
Create a default fully qualified app name.
10+
*/}}
11+
{{- define "operator-ui.fullname" -}}
12+
{{- if .Values.fullnameOverride }}
13+
{{- .Values.fullnameOverride | trunc 63 | trimSuffix "-" }}
14+
{{- else }}
15+
{{- $name := default .Chart.Name .Values.nameOverride }}
16+
{{- if contains $name .Release.Name }}
17+
{{- .Release.Name | trunc 63 | trimSuffix "-" }}
18+
{{- else }}
19+
{{- printf "%s-%s" .Release.Name $name | trunc 63 | trimSuffix "-" }}
20+
{{- end }}
21+
{{- end }}
22+
{{- end }}
23+
24+
{{/*
25+
Create chart name and version as used by the chart label.
26+
*/}}
27+
{{- define "operator-ui.chart" -}}
28+
{{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" }}
29+
{{- end }}
30+
31+
{{/*
32+
Common labels
33+
*/}}
34+
{{- define "operator-ui.labels" -}}
35+
helm.sh/chart: {{ include "operator-ui.chart" . }}
36+
{{ include "operator-ui.selectorLabels" . }}
37+
{{- if .Chart.AppVersion }}
38+
app.kubernetes.io/version: {{ .Chart.AppVersion | quote }}
39+
{{- end }}
40+
app.kubernetes.io/managed-by: {{ .Release.Service }}
41+
{{- end }}
42+
43+
{{/*
44+
Selector labels
45+
*/}}
46+
{{- define "operator-ui.selectorLabels" -}}
47+
app.kubernetes.io/name: {{ include "operator-ui.name" . }}
48+
app.kubernetes.io/instance: {{ .Release.Name }}
49+
app.kubernetes.io/component: frontend
50+
{{- end }}
51+
52+
{{/*
53+
Create the name of the service account to use
54+
*/}}
55+
{{- define "operator-ui.serviceAccountName" -}}
56+
{{- if .Values.serviceAccount.create }}
57+
{{- default (include "operator-ui.fullname" .) .Values.serviceAccount.name }}
58+
{{- else }}
59+
{{- default "default" .Values.serviceAccount.name }}
60+
{{- end }}
61+
{{- end }}
62+
63+
{{/*
64+
Get the API server URL
65+
*/}}
66+
{{- define "operator-ui.apiUrl" -}}
67+
{{- if .Values.env.REACT_APP_API_BASE_URL }}
68+
{{- .Values.env.REACT_APP_API_BASE_URL }}
69+
{{- else }}
70+
{{- printf "http://%s-api-server.%s.svc.cluster.local:8080" .Release.Name (.Values.global.namespace | default .Release.Namespace) }}
71+
{{- end }}
72+
{{- end }}

helm/templates/deployment.yaml

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
apiVersion: apps/v1
2+
kind: Deployment
3+
metadata:
4+
name: {{ include "operator-ui.fullname" . }}
5+
namespace: {{ .Values.global.namespace | default .Release.Namespace }}
6+
labels:
7+
{{- include "operator-ui.labels" . | nindent 4 }}
8+
spec:
9+
replicas: {{ .Values.replicaCount }}
10+
selector:
11+
matchLabels:
12+
{{- include "operator-ui.selectorLabels" . | nindent 6 }}
13+
template:
14+
metadata:
15+
labels:
16+
{{- include "operator-ui.selectorLabels" . | nindent 8 }}
17+
spec:
18+
{{- with .Values.global.imagePullSecrets }}
19+
imagePullSecrets:
20+
{{- toYaml . | nindent 8 }}
21+
{{- end }}
22+
serviceAccountName: {{ include "operator-ui.serviceAccountName" . }}
23+
securityContext:
24+
{{- toYaml .Values.podSecurityContext | nindent 8 }}
25+
containers:
26+
- name: ui
27+
securityContext:
28+
{{- toYaml .Values.securityContext | nindent 12 }}
29+
image: "{{ .Values.global.operatorUI.image.repository | default .Values.image.repository }}:{{ .Values.global.operatorUI.image.tag | default .Values.image.tag | default .Chart.AppVersion }}"
30+
imagePullPolicy: {{ .Values.global.operatorUI.image.pullPolicy | default .Values.image.pullPolicy }}
31+
ports:
32+
- name: http
33+
containerPort: {{ .Values.service.targetPort }}
34+
protocol: TCP
35+
env:
36+
- name: REACT_APP_API_BASE_URL
37+
value: {{ include "operator-ui.apiUrl" . | quote }}
38+
{{- range $key, $value := .Values.env }}
39+
{{- if ne $key "REACT_APP_API_BASE_URL" }}
40+
- name: {{ $key }}
41+
value: {{ $value | quote }}
42+
{{- end }}
43+
{{- end }}
44+
livenessProbe:
45+
{{- toYaml .Values.livenessProbe | nindent 10 }}
46+
readinessProbe:
47+
{{- toYaml .Values.readinessProbe | nindent 10 }}
48+
resources:
49+
{{- toYaml .Values.resources | nindent 10 }}
50+
{{- with .Values.nodeSelector }}
51+
nodeSelector:
52+
{{- toYaml . | nindent 8 }}
53+
{{- end }}
54+
{{- with .Values.affinity }}
55+
affinity:
56+
{{- toYaml . | nindent 8 }}
57+
{{- end }}
58+
{{- with .Values.tolerations }}
59+
tolerations:
60+
{{- toYaml . | nindent 8 }}
61+
{{- end }}

helm/templates/service.yaml

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
apiVersion: v1
2+
kind: Service
3+
metadata:
4+
name: {{ include "operator-ui.fullname" . }}
5+
namespace: {{ .Values.global.namespace | default .Release.Namespace }}
6+
labels:
7+
{{- include "operator-ui.labels" . | nindent 4 }}
8+
spec:
9+
type: {{ .Values.service.type }}
10+
ports:
11+
- port: {{ .Values.service.port }}
12+
targetPort: {{ .Values.service.targetPort }}
13+
protocol: TCP
14+
name: http
15+
{{- if and (eq .Values.service.type "NodePort") .Values.service.nodePort }}
16+
nodePort: {{ .Values.service.nodePort }}
17+
{{- end }}
18+
selector:
19+
{{- include "operator-ui.selectorLabels" . | nindent 4 }}

helm/templates/serviceaccount.yaml

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
{{- if .Values.serviceAccount.create -}}
2+
apiVersion: v1
3+
kind: ServiceAccount
4+
metadata:
5+
name: {{ include "operator-ui.serviceAccountName" . }}
6+
namespace: {{ .Values.global.namespace | default .Release.Namespace }}
7+
labels:
8+
{{- include "operator-ui.labels" . | nindent 4 }}
9+
{{- with .Values.serviceAccount.annotations }}
10+
annotations:
11+
{{- toYaml . | nindent 4 }}
12+
{{- end }}
13+
{{- end }}

0 commit comments

Comments
 (0)