From 3ef99a8dea80aa4397f954b315ab2fc8749eb19f Mon Sep 17 00:00:00 2001 From: jangrui Date: Sat, 16 May 2026 08:28:05 +0800 Subject: [PATCH 1/5] =?UTF-8?q?feat(chart):=20=E6=B7=BB=E5=8A=A0=20SkillHu?= =?UTF-8?q?b=20Helm=20Chart=20=E9=83=A8=E7=BD=B2=E6=96=B9=E6=A1=88?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 新增 Helm Chart 支持完整的 SkillHub 私有化部署,包括: - PostgreSQL/Redis 内置 StatefulSet 及外部模式切换 - 零依赖设计,无需 Bitnami 子 Chart - 支持 standalone/cluster 数据库架构 - NodePort/LoadBalancer/ClusterIP 多种服务类型 - HPA、PDB、ServiceMonitor 完整运维支持 - cert-manager 证书自动签发 - initContainer 等待数据库和 Redis 就绪 - PVC 卸载保护 (helm.sh/resource-policy: keep) - GitHub Actions: PR 校验 + 发布到 GHCR OCI --- .github/workflows/pr-helm-chart.yml | 146 ++++++++ .github/workflows/publish-helm-chart.yml | 81 +++++ charts/skillhub/.helmignore | 24 ++ charts/skillhub/Chart.yaml | 13 + charts/skillhub/templates/_helpers.tpl | 127 +++++++ .../templates/backend-deployment.yaml | 236 +++++++++++++ charts/skillhub/templates/certificate.yaml | 17 + charts/skillhub/templates/configmap.yaml | 34 ++ .../templates/frontend-deployment.yaml | 54 +++ charts/skillhub/templates/hpa.yaml | 101 ++++++ charts/skillhub/templates/ingress.yaml | 47 +++ charts/skillhub/templates/pdb.yaml | 44 +++ .../templates/postgres-statefulset.yaml | 100 ++++++ charts/skillhub/templates/pvc.yaml | 19 ++ .../skillhub/templates/redis-statefulset.yaml | 83 +++++ .../templates/scanner-deployment.yaml | 66 ++++ charts/skillhub/templates/secret.yaml | 28 ++ charts/skillhub/templates/services.yaml | 83 +++++ charts/skillhub/values.yaml | 312 ++++++++++++++++++ 19 files changed, 1615 insertions(+) create mode 100644 .github/workflows/pr-helm-chart.yml create mode 100644 .github/workflows/publish-helm-chart.yml create mode 100644 charts/skillhub/.helmignore create mode 100644 charts/skillhub/Chart.yaml create mode 100644 charts/skillhub/templates/_helpers.tpl create mode 100644 charts/skillhub/templates/backend-deployment.yaml create mode 100644 charts/skillhub/templates/certificate.yaml create mode 100644 charts/skillhub/templates/configmap.yaml create mode 100644 charts/skillhub/templates/frontend-deployment.yaml create mode 100644 charts/skillhub/templates/hpa.yaml create mode 100644 charts/skillhub/templates/ingress.yaml create mode 100644 charts/skillhub/templates/pdb.yaml create mode 100644 charts/skillhub/templates/postgres-statefulset.yaml create mode 100644 charts/skillhub/templates/pvc.yaml create mode 100644 charts/skillhub/templates/redis-statefulset.yaml create mode 100644 charts/skillhub/templates/scanner-deployment.yaml create mode 100644 charts/skillhub/templates/secret.yaml create mode 100644 charts/skillhub/templates/services.yaml create mode 100644 charts/skillhub/values.yaml diff --git a/.github/workflows/pr-helm-chart.yml b/.github/workflows/pr-helm-chart.yml new file mode 100644 index 000000000..42e0b2c63 --- /dev/null +++ b/.github/workflows/pr-helm-chart.yml @@ -0,0 +1,146 @@ +name: PR Helm Chart + +on: + pull_request: + paths: + - charts/skillhub/** + types: + - opened + - synchronize + - reopened + - ready_for_review + workflow_dispatch: + +concurrency: + group: pr-helm-chart-${{ github.event.pull_request.number || github.ref }} + cancel-in-progress: true + +permissions: + contents: read + +jobs: + lint: + name: Lint Chart + if: ${{ github.event_name != 'pull_request' || !github.event.pull_request.draft }} + runs-on: ubuntu-latest + defaults: + run: + working-directory: charts/skillhub + + steps: + - name: Check out repository + uses: actions/checkout@v4 + + - name: Set up Helm + uses: azure/setup-helm@v4 + with: + version: latest + + - name: Lint chart + run: helm lint + + - name: Validate chart metadata + run: | + CHART_VERSION=$(helm show chart . | grep '^version:' | awk '{print $2}') + APP_VERSION=$(helm show chart . | grep '^appVersion:' | awk '{print $2}') + echo "Chart version: $CHART_VERSION" + echo "App version: $APP_VERSION" + if [ -z "$CHART_VERSION" ]; then + echo "ERROR: Chart version is empty" + exit 1 + fi + + template: + name: Template Validation + if: ${{ github.event_name != 'pull_request' || !github.event.pull_request.draft }} + runs-on: ubuntu-latest + defaults: + run: + working-directory: charts/skillhub + + strategy: + fail-fast: false + matrix: + scenario: + - name: internal-default + description: 内置 PostgreSQL + Redis + args: "" + - name: external-db-redis + description: 外置 PostgreSQL + Redis + args: > + --set database.mode=external + --set redis.mode=external + - name: external-sentinel + description: 外置 DB + Redis 哨兵模式 + args: > + --set database.mode=external + --set redis.mode=external + --set redis.external.sentinel.enabled=true + --set redis.external.sentinel.nodes="{10.0.0.1:26379,10.0.0.2:26379}" + - name: ingress-tls-certmanager + description: Ingress + TLS + cert-manager + args: > + --set ingress.enabled=true + --set ingress.tls.enabled=true + --set ingress.certManager.enabled=true + - name: s3-storage + description: S3 存储 + args: > + --set storage.provider=s3 + --set storage.s3.bucket=test-bucket + --set storage.s3.endpoint=s3.amazonaws.com + --set storage.s3.region=us-east-1 + - name: external-secret + description: 引用已有 Secret + args: --set existingSecret=my-custom-secret + - name: scanner-disabled + description: 禁用 Scanner + args: --set scanner.enabled=false + - name: db-cluster + description: PostgreSQL cluster 模式 + args: --set database.architecture=cluster + - name: hpa-pdb + description: HPA + PDB 开启 + args: > + --set server.autoscaling.enabled=true + --set web.autoscaling.enabled=true + --set scanner.autoscaling.enabled=true + --set server.podDisruptionBudget.enabled=true + --set web.podDisruptionBudget.enabled=true + --set scanner.podDisruptionBudget.enabled=true + + steps: + - name: Check out repository + uses: actions/checkout@v4 + + - name: Set up Helm + uses: azure/setup-helm@v4 + with: + version: latest + + - name: Render template - ${{ matrix.scenario.name }} + run: | + echo "## ${{ matrix.scenario.description }}" + helm template test-release . ${{ matrix.scenario.args }} > /dev/null + echo "✅ Template rendered successfully" + + - name: Validate no empty resources + run: | + RESOURCES=$(helm template test-release . ${{ matrix.scenario.args }} | grep -c '^kind:') + echo "Rendered $RESOURCES resources for ${{ matrix.scenario.name }}" + if [ "$RESOURCES" -eq 0 ]; then + echo "ERROR: No resources rendered for ${{ matrix.scenario.name }}" + exit 1 + fi + + - name: Validate resource names are well-formed + run: | + helm template test-release . ${{ matrix.scenario.args }} | \ + grep -E '^ name:' | \ + while read -r line; do + if echo "$line" | grep -qP '\{\{'; then + echo "ERROR: Unrendered template in name: $line" + exit 1 + fi + done + echo "✅ All resource names properly rendered" diff --git a/.github/workflows/publish-helm-chart.yml b/.github/workflows/publish-helm-chart.yml new file mode 100644 index 000000000..7508143da --- /dev/null +++ b/.github/workflows/publish-helm-chart.yml @@ -0,0 +1,81 @@ +name: Publish Helm Chart + +on: + release: + types: [published] + workflow_dispatch: + +concurrency: + group: publish-helm-chart-${{ github.ref }} + cancel-in-progress: true + +permissions: + contents: read + packages: write + +jobs: + package: + name: Package and Publish + runs-on: ubuntu-latest + defaults: + run: + working-directory: charts/skillhub + + steps: + - name: Check out repository + uses: actions/checkout@v4 + + - name: Set up Helm + uses: azure/setup-helm@v4 + with: + version: latest + + - name: Extract chart version from tag + id: chartver + run: | + REF="${{ github.ref_name }}" + # Support helm-vX.Y.Z or just vX.Y.Z tags + if [[ "$REF" =~ ^helm-v?([0-9]+\.[0-9]+\.[0-9]+)$ ]]; then + VERSION="${BASH_REMATCH[1]}" + elif [[ "$REF" =~ ^v?([0-9]+\.[0-9]+\.[0-9]+)$ ]]; then + VERSION="${BASH_REMATCH[1]}" + else + # Fallback: use chart.yaml version + VERSION=$(helm show chart . | grep '^version:' | awk '{print $2}') + fi + echo "version=$VERSION" >> "$GITHUB_OUTPUT" + echo "Packaging chart version: $VERSION" + + - name: Lint chart + run: helm lint + + - name: Package chart + run: | + helm package . --version "${{ steps.chartver.outputs.version }}" \ + --destination /tmp/helm-charts + echo "Packaged:" + ls -la /tmp/helm-charts/ + + - name: Log in to GHCR + uses: docker/login-action@v3 + with: + registry: ghcr.io + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} + + - name: Push chart to GHCR OCI + run: | + helm push /tmp/helm-charts/skillhub-${{ steps.chartver.outputs.version }}.tgz \ + oci://ghcr.io/${{ github.repository_owner }}/charts + + - name: Generate artifact attestation + uses: actions/attest-build-provenance@v2 + with: + subject-path: /tmp/helm-charts/skillhub-${{ steps.chartver.outputs.version }}.tgz + + - name: Upload chart artifact + uses: actions/upload-artifact@v4 + with: + name: skillhub-${{ steps.chartver.outputs.version }}.tgz + path: /tmp/helm-charts/skillhub-${{ steps.chartver.outputs.version }}.tgz + retention-days: 90 diff --git a/charts/skillhub/.helmignore b/charts/skillhub/.helmignore new file mode 100644 index 000000000..0df7bb7c2 --- /dev/null +++ b/charts/skillhub/.helmignore @@ -0,0 +1,24 @@ +# OS files +.DS_Store +Thumbs.db + +# Editors / IDEs +.idea/ +.vscode/ +*.swp +*.swo + +# Local tooling +.claude/ +CLAUDE.md + +# Git +.git/ +.gitignore +.gitattributes + +# CI +.github/ + +# Template artifacts +*.tgz diff --git a/charts/skillhub/Chart.yaml b/charts/skillhub/Chart.yaml new file mode 100644 index 000000000..dd34d1fcb --- /dev/null +++ b/charts/skillhub/Chart.yaml @@ -0,0 +1,13 @@ +apiVersion: v2 +name: skillhub +description: Self-hosted, open-source agent skill registry for enterprises. +type: application +version: 0.1.0 +appVersion: 0.2.8 +keywords: + - skillhub + - ai + - skills +home: https://github.com/iflytek/skillhub +sources: + - https://github.com/iflytek/skillhub diff --git a/charts/skillhub/templates/_helpers.tpl b/charts/skillhub/templates/_helpers.tpl new file mode 100644 index 000000000..a70a01f16 --- /dev/null +++ b/charts/skillhub/templates/_helpers.tpl @@ -0,0 +1,127 @@ +{{- /* +SkillHub Helm Chart 模板辅助函数 +*/}} + +{{- /* 名称 */}} +{{- define "skillhub.name" -}} +{{- default "skillhub" .Values.nameOverride | trunc 63 | trimSuffix "-" }} +{{- end }} + +{{- /* 完整名称 */}} +{{- define "skillhub.fullname" -}} +{{- if .Values.fullnameOverride }} +{{- .Values.fullnameOverride | trunc 63 | trimSuffix "-" }} +{{- else }} +{{- $name := default "skillhub" .Values.nameOverride }} +{{- if contains $name .Release.Name }} +{{- .Release.Name | trunc 63 | trimSuffix "-" }} +{{- else }} +{{- printf "%s-%s" .Release.Name $name | trunc 63 | trimSuffix "-" }} +{{- end }} +{{- end }} +{{- end }} + +{{- /* Chart 标签 */}} +{{- define "skillhub.chart" -}} +{{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" }} +{{- end }} + +{{- /* 通用标签 */}} +{{- define "skillhub.labels" -}} +helm.sh/chart: {{ include "skillhub.chart" . }} +{{ include "skillhub.selectorLabels" . }} +{{- if .Chart.AppVersion }} +app.kubernetes.io/version: {{ .Chart.AppVersion | quote }} +{{- end }} +app.kubernetes.io/managed-by: {{ .Release.Service }} +app.kubernetes.io/part-of: skillhub +{{- end }} + +{{- /* 选择器标签 */}} +{{- define "skillhub.selectorLabels" -}} +app.kubernetes.io/name: {{ include "skillhub.name" . }} +{{- end }} + +{{- /* 组件标签 */}} +{{- define "skillhub.server.labels" -}} +{{ include "skillhub.labels" . }} +app.kubernetes.io/component: server +{{- end }} +{{- define "skillhub.server.selectorLabels" -}} +{{ include "skillhub.selectorLabels" . }} +app.kubernetes.io/component: server +{{- end }} + +{{- define "skillhub.web.labels" -}} +{{ include "skillhub.labels" . }} +app.kubernetes.io/component: web +{{- end }} +{{- define "skillhub.web.selectorLabels" -}} +{{ include "skillhub.selectorLabels" . }} +app.kubernetes.io/component: web +{{- end }} + +{{- define "skillhub.scanner.labels" -}} +{{ include "skillhub.labels" . }} +app.kubernetes.io/component: scanner +{{- end }} +{{- define "skillhub.scanner.selectorLabels" -}} +{{ include "skillhub.selectorLabels" . }} +app.kubernetes.io/component: scanner +{{- end }} + +{{- /* 镜像地址 */}} +{{- define "skillhub.image" -}} +{{- $registry := .registry | default .global.registry }} +{{- printf "%s/%s:%s" $registry .name .tag }} +{{- end }} + +{{- /* JDBC Host */}} +{{- define "skillhub.jdbcHost" -}} +{{- if eq .Values.database.mode "internal" -}} +{{ include "skillhub.fullname" . }}-postgres +{{- else -}} +{{ .Values.database.external.host }} +{{- end -}} +{{- end }} + +{{- /* JDBC Port */}} +{{- define "skillhub.jdbcPort" -}} +{{- if eq .Values.database.mode "internal" -}}5432{{- else -}} +{{ .Values.database.external.port | default "5432" }} +{{- end -}} +{{- end }} + +{{- /* Secret 名称 */}} +{{- define "skillhub.secretName" -}} +{{- .Values.existingSecret | default (printf "%s-secret" (include "skillhub.fullname" .)) }} +{{- end }} + +{{- /* Redis Host */}} +{{- define "skillhub.redisHost" -}} +{{- if eq .Values.redis.mode "internal" -}} +{{ include "skillhub.fullname" . }}-redis +{{- else -}} +{{ .Values.redis.external.host }} +{{- end -}} +{{- end }} + +{{- /* Redis Port */}} +{{- define "skillhub.redisPort" -}} +{{- if eq .Values.redis.mode "internal" -}}6379{{- else -}} +{{ .Values.redis.external.port | default "6379" }} +{{- end -}} +{{- end }} + +{{- /* 数据库 JDBC URL */}} +{{- define "skillhub.jdbcUrl" -}} +{{- if eq .Values.database.mode "internal" -}} +jdbc:postgresql://{{ include "skillhub.fullname" . }}-postgres:5432/skillhub +{{- else -}} +{{- if .Values.database.external.jdbcUrl -}} +{{ .Values.database.external.jdbcUrl }} +{{- else -}} +jdbc:postgresql://{{ .Values.database.external.host }}:{{ .Values.database.external.port }}/{{ .Values.database.external.database }}{{ if .Values.database.external.parameters }}?{{ .Values.database.external.parameters }}{{ end }} +{{- end }} +{{- end }} +{{- end }} diff --git a/charts/skillhub/templates/backend-deployment.yaml b/charts/skillhub/templates/backend-deployment.yaml new file mode 100644 index 000000000..b0a1d8325 --- /dev/null +++ b/charts/skillhub/templates/backend-deployment.yaml @@ -0,0 +1,236 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: {{ include "skillhub.fullname" . }}-server + labels: + {{- include "skillhub.server.labels" . | nindent 4 }} +spec: + replicas: {{ .Values.replicaCount }} + selector: + matchLabels: + {{- include "skillhub.server.selectorLabels" . | nindent 6 }} + template: + metadata: + labels: + {{- include "skillhub.server.selectorLabels" . | nindent 8 }} + annotations: + {{- toYaml .Values.server.podAnnotations | nindent 8 }} + spec: + {{- $secrets := .Values.server.imagePullSecrets | default .Values.global.imagePullSecrets }} + {{- if $secrets }} + imagePullSecrets: + {{- toYaml $secrets | nindent 8 }} + {{- end }} + initContainers: + - name: wait-for-dependencies + image: busybox:latest + env: + - name: DB_HOST + value: {{ include "skillhub.jdbcHost" . }} + - name: DB_PORT + value: {{ include "skillhub.jdbcPort" . | quote }} + - name: REDIS_HOST + value: {{ include "skillhub.redisHost" . }} + - name: REDIS_PORT + value: {{ include "skillhub.redisPort" . | quote }} + command: + - sh + - -c + - | + echo "Waiting for PostgreSQL at ${DB_HOST}:${DB_PORT}..." + until nc -z -w 2 "${DB_HOST}" "${DB_PORT}"; do sleep 2; done + echo "PostgreSQL is ready!" + echo "Waiting for Redis at ${REDIS_HOST}:${REDIS_PORT}..." + until nc -z -w 2 "${REDIS_HOST}" "${REDIS_PORT}"; do sleep 2; done + echo "Redis is ready!" + containers: + - name: server + image: {{ .Values.images.registry }}/skillhub-server:{{ .Values.images.tag }} + imagePullPolicy: {{ .Values.images.pullPolicy }} + ports: + - containerPort: {{ .Values.service.serverPort }} + name: http + env: + - name: SPRING_PROFILES_ACTIVE + value: {{ .Values.springProfilesActive }} + + # Database + - name: SPRING_DATASOURCE_URL + valueFrom: + secretKeyRef: + name: {{ include "skillhub.secretName" . }} + key: spring-datasource-url + - name: SPRING_DATASOURCE_USERNAME + valueFrom: + secretKeyRef: + name: {{ include "skillhub.secretName" . }} + key: spring-datasource-username + - name: SPRING_DATASOURCE_PASSWORD + valueFrom: + secretKeyRef: + name: {{ include "skillhub.secretName" . }} + key: spring-datasource-password + + # Redis + - name: SPRING_DATA_REDIS_HOST + valueFrom: + configMapKeyRef: + name: {{ include "skillhub.fullname" . }}-config + key: redis-host + - name: SPRING_DATA_REDIS_PORT + valueFrom: + configMapKeyRef: + name: {{ include "skillhub.fullname" . }}-config + key: redis-port + + {{- if and (eq .Values.redis.mode "external") .Values.redis.external.password }} + - name: SPRING_DATA_REDIS_PASSWORD + value: {{ .Values.redis.external.password }} + {{- end }} + + {{- if .Values.redis.external.sentinel.enabled }} + - name: SPRING_DATA_REDIS_SENTINEL_MASTER + value: {{ .Values.redis.external.sentinel.masterSet }} + - name: SPRING_DATA_REDIS_SENTINEL_NODES + value: {{ join "," .Values.redis.external.sentinel.nodes }} + {{- end }} + + # Storage + - name: STORAGE_BASE_PATH + valueFrom: + configMapKeyRef: + name: {{ include "skillhub.fullname" . }}-config + key: storage-base-path + - name: SKILLHUB_STORAGE_PROVIDER + valueFrom: + configMapKeyRef: + name: {{ include "skillhub.fullname" . }}-config + key: skillhub-storage-provider + + {{- if eq .Values.storage.provider "s3" }} + - name: SKILLHUB_S3_BUCKET + value: {{ .Values.storage.s3.bucket }} + - name: SKILLHUB_S3_ENDPOINT + value: {{ .Values.storage.s3.endpoint }} + - name: SKILLHUB_S3_REGION + value: {{ .Values.storage.s3.region }} + {{- if .Values.storage.s3.accessKey }} + - name: SKILLHUB_S3_ACCESS_KEY + value: {{ .Values.storage.s3.accessKey }} + {{- end }} + {{- if .Values.storage.s3.secretKey }} + - name: SKILLHUB_S3_SECRET_KEY + value: {{ .Values.storage.s3.secretKey }} + {{- end }} + {{- end }} + + # Scanner + - name: SKILLHUB_SECURITY_SCANNER_ENABLED + valueFrom: + configMapKeyRef: + name: {{ include "skillhub.fullname" . }}-config + key: skill-scanner-enabled + - name: SKILLHUB_SECURITY_SCANNER_URL + valueFrom: + configMapKeyRef: + name: {{ include "skillhub.fullname" . }}-config + key: skill-scanner-url + - name: SKILLHUB_SECURITY_SCANNER_MODE + valueFrom: + configMapKeyRef: + name: {{ include "skillhub.fullname" . }}-config + key: skill-scanner-mode + + # Session + - name: SESSION_COOKIE_SECURE + valueFrom: + configMapKeyRef: + name: {{ include "skillhub.fullname" . }}-config + key: session-cookie-secure + + # Bootstrap Admin + - name: BOOTSTRAP_ADMIN_ENABLED + valueFrom: + configMapKeyRef: + name: {{ include "skillhub.fullname" . }}-config + key: bootstrap-admin-enabled + - name: BOOTSTRAP_ADMIN_USER_ID + valueFrom: + configMapKeyRef: + name: {{ include "skillhub.fullname" . }}-config + key: bootstrap-admin-user-id + - name: BOOTSTRAP_ADMIN_USERNAME + valueFrom: + configMapKeyRef: + name: {{ include "skillhub.fullname" . }}-config + key: bootstrap-admin-username + - name: BOOTSTRAP_ADMIN_DISPLAY_NAME + valueFrom: + configMapKeyRef: + name: {{ include "skillhub.fullname" . }}-config + key: bootstrap-admin-display-name + - name: BOOTSTRAP_ADMIN_EMAIL + valueFrom: + configMapKeyRef: + name: {{ include "skillhub.fullname" . }}-config + key: bootstrap-admin-email + - name: BOOTSTRAP_ADMIN_PASSWORD + valueFrom: + secretKeyRef: + name: {{ include "skillhub.secretName" . }} + key: bootstrap-admin-password + optional: true + + # OAuth2 GitHub (optional) + - name: OAUTH2_GITHUB_CLIENT_ID + valueFrom: + secretKeyRef: + name: {{ include "skillhub.secretName" . }} + key: oauth2-github-client-id + optional: true + - name: OAUTH2_GITHUB_CLIENT_SECRET + valueFrom: + secretKeyRef: + name: {{ include "skillhub.secretName" . }} + key: oauth2-github-client-secret + optional: true + + {{- if .Values.server.javaOpts }} + - name: JAVA_OPTS + value: {{ .Values.server.javaOpts }} + {{- end }} + + {{- with .Values.server.extraEnv }} + {{- toYaml . | nindent 12 }} + {{- end }} + + volumeMounts: + - name: skillhub-storage + mountPath: /var/lib/skillhub/storage + + resources: + {{- toYaml .Values.server.resources | nindent 12 }} + + startupProbe: + {{- toYaml .Values.server.probes.startup | nindent 12 }} + readinessProbe: + {{- toYaml .Values.server.probes.readiness | nindent 12 }} + livenessProbe: + {{- toYaml .Values.server.probes.liveness | nindent 12 }} + + volumes: + - name: skillhub-storage + persistentVolumeClaim: + claimName: {{ include "skillhub.fullname" . }}-storage-pvc + {{- with .Values.server.nodeSelector }} + nodeSelector: + {{- toYaml . | nindent 8 }} + {{- end }} + {{- with .Values.server.tolerations }} + tolerations: + {{- toYaml . | nindent 8 }} + {{- end }} + {{- with .Values.server.affinity }} + affinity: + {{- toYaml . | nindent 8 }} + {{- end }} diff --git a/charts/skillhub/templates/certificate.yaml b/charts/skillhub/templates/certificate.yaml new file mode 100644 index 000000000..95543ed31 --- /dev/null +++ b/charts/skillhub/templates/certificate.yaml @@ -0,0 +1,17 @@ +{{- if and .Values.ingress.enabled .Values.ingress.certManager.enabled }} +apiVersion: cert-manager.io/v1 +kind: Certificate +metadata: + name: {{ include "skillhub.fullname" . }}-tls + labels: + {{- include "skillhub.labels" . | nindent 4 }} +spec: + secretName: {{ include "skillhub.fullname" . }}-tls + duration: 2160h + renewBefore: 360h + dnsNames: + - {{ .Values.ingress.host }} + issuerRef: + name: {{ .Values.ingress.certManager.issuerName }} + kind: {{ .Values.ingress.certManager.issuerKind }} +{{- end }} diff --git a/charts/skillhub/templates/configmap.yaml b/charts/skillhub/templates/configmap.yaml new file mode 100644 index 000000000..a7600e95f --- /dev/null +++ b/charts/skillhub/templates/configmap.yaml @@ -0,0 +1,34 @@ +apiVersion: v1 +kind: ConfigMap +metadata: + name: {{ include "skillhub.fullname" . }}-config + labels: + {{- include "skillhub.labels" . | nindent 4 }} +data: + # Redis 配置 + redis-host: {{ include "skillhub.redisHost" . }} + redis-port: {{ include "skillhub.redisPort" . | quote }} + + # 存储路径 + storage-base-path: /var/lib/skillhub/storage + + # 存储提供者: local | s3 + skillhub-storage-provider: {{ .Values.storage.provider }} + + # 技能扫描器 + skill-scanner-enabled: {{ .Values.scanner.enabled | quote }} + skill-scanner-url: http://{{ include "skillhub.fullname" . }}-scanner:8000 + skill-scanner-mode: upload + + # Bootstrap 管理员 + bootstrap-admin-enabled: {{ .Values.bootstrapAdmin.enabled | quote }} + bootstrap-admin-user-id: {{ .Values.bootstrapAdmin.userId }} + bootstrap-admin-username: {{ .Values.bootstrapAdmin.username }} + bootstrap-admin-display-name: {{ .Values.bootstrapAdmin.displayName }} + bootstrap-admin-email: {{ .Values.bootstrapAdmin.email }} + + # Session + session-cookie-secure: {{ .Values.session.cookieSecure | quote }} + + # Spring Profiles + spring-profiles-active: {{ .Values.springProfilesActive }} diff --git a/charts/skillhub/templates/frontend-deployment.yaml b/charts/skillhub/templates/frontend-deployment.yaml new file mode 100644 index 000000000..9b2942dd5 --- /dev/null +++ b/charts/skillhub/templates/frontend-deployment.yaml @@ -0,0 +1,54 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: {{ include "skillhub.fullname" . }}-web + labels: + {{- include "skillhub.web.labels" . | nindent 4 }} +spec: + replicas: {{ .Values.replicaCount }} + selector: + matchLabels: + {{- include "skillhub.web.selectorLabels" . | nindent 6 }} + template: + metadata: + labels: + {{- include "skillhub.web.selectorLabels" . | nindent 8 }} + annotations: + {{- toYaml .Values.web.podAnnotations | nindent 8 }} + spec: + {{- $secrets := .Values.web.imagePullSecrets | default .Values.global.imagePullSecrets }} + {{- if $secrets }} + imagePullSecrets: + {{- toYaml $secrets | nindent 8 }} + {{- end }} + containers: + - name: web + image: {{ .Values.images.registry }}/skillhub-web:{{ .Values.images.tag }} + imagePullPolicy: {{ .Values.images.pullPolicy }} + env: + - name: SKILLHUB_API_UPSTREAM + value: http://{{ include "skillhub.fullname" . }}-server:{{ .Values.service.serverPort }} + {{- with .Values.web.extraEnv }} + {{- toYaml . | nindent 12 }} + {{- end }} + ports: + - containerPort: {{ .Values.service.webPort }} + name: http + resources: + {{- toYaml .Values.web.resources | nindent 12 }} + readinessProbe: + {{- toYaml .Values.web.probes.readiness | nindent 12 }} + livenessProbe: + {{- toYaml .Values.web.probes.liveness | nindent 12 }} + {{- with .Values.web.nodeSelector }} + nodeSelector: + {{- toYaml . | nindent 8 }} + {{- end }} + {{- with .Values.web.tolerations }} + tolerations: + {{- toYaml . | nindent 8 }} + {{- end }} + {{- with .Values.web.affinity }} + affinity: + {{- toYaml . | nindent 8 }} + {{- end }} diff --git a/charts/skillhub/templates/hpa.yaml b/charts/skillhub/templates/hpa.yaml new file mode 100644 index 000000000..0e2de9334 --- /dev/null +++ b/charts/skillhub/templates/hpa.yaml @@ -0,0 +1,101 @@ +{{- if .Values.server.autoscaling.enabled }} +--- +apiVersion: autoscaling/v2 +kind: HorizontalPodAutoscaler +metadata: + name: {{ include "skillhub.fullname" . }}-server + labels: + {{- include "skillhub.server.labels" . | nindent 4 }} +spec: + scaleTargetRef: + apiVersion: apps/v1 + kind: Deployment + name: {{ include "skillhub.fullname" . }}-server + minReplicas: {{ .Values.server.autoscaling.minReplicas }} + maxReplicas: {{ .Values.server.autoscaling.maxReplicas }} + metrics: + {{- if .Values.server.autoscaling.targetCPUUtilizationPercentage }} + - type: Resource + resource: + name: cpu + target: + type: Utilization + averageUtilization: {{ .Values.server.autoscaling.targetCPUUtilizationPercentage }} + {{- end }} + {{- if .Values.server.autoscaling.targetMemoryUtilizationPercentage }} + - type: Resource + resource: + name: memory + target: + type: Utilization + averageUtilization: {{ .Values.server.autoscaling.targetMemoryUtilizationPercentage }} + {{- end }} +{{- end }} + +{{- if .Values.web.autoscaling.enabled }} +--- +apiVersion: autoscaling/v2 +kind: HorizontalPodAutoscaler +metadata: + name: {{ include "skillhub.fullname" . }}-web + labels: + {{- include "skillhub.web.labels" . | nindent 4 }} +spec: + scaleTargetRef: + apiVersion: apps/v1 + kind: Deployment + name: {{ include "skillhub.fullname" . }}-web + minReplicas: {{ .Values.web.autoscaling.minReplicas }} + maxReplicas: {{ .Values.web.autoscaling.maxReplicas }} + metrics: + {{- if .Values.web.autoscaling.targetCPUUtilizationPercentage }} + - type: Resource + resource: + name: cpu + target: + type: Utilization + averageUtilization: {{ .Values.web.autoscaling.targetCPUUtilizationPercentage }} + {{- end }} + {{- if .Values.web.autoscaling.targetMemoryUtilizationPercentage }} + - type: Resource + resource: + name: memory + target: + type: Utilization + averageUtilization: {{ .Values.web.autoscaling.targetMemoryUtilizationPercentage }} + {{- end }} +{{- end }} + +{{- if and .Values.scanner.enabled .Values.scanner.autoscaling.enabled }} +--- +apiVersion: autoscaling/v2 +kind: HorizontalPodAutoscaler +metadata: + name: {{ include "skillhub.fullname" . }}-scanner + labels: + {{- include "skillhub.scanner.labels" . | nindent 4 }} +spec: + scaleTargetRef: + apiVersion: apps/v1 + kind: Deployment + name: {{ include "skillhub.fullname" . }}-scanner + minReplicas: {{ .Values.scanner.autoscaling.minReplicas }} + maxReplicas: {{ .Values.scanner.autoscaling.maxReplicas }} + metrics: + {{- if .Values.scanner.autoscaling.targetCPUUtilizationPercentage }} + - type: Resource + resource: + name: cpu + target: + type: Utilization + averageUtilization: {{ .Values.scanner.autoscaling.targetCPUUtilizationPercentage }} + {{- end }} + {{- if .Values.scanner.autoscaling.targetMemoryUtilizationPercentage }} + - type: Resource + resource: + name: memory + target: + type: Utilization + averageUtilization: {{ .Values.scanner.autoscaling.targetMemoryUtilizationPercentage }} + {{- end }} +{{- end }} diff --git a/charts/skillhub/templates/ingress.yaml b/charts/skillhub/templates/ingress.yaml new file mode 100644 index 000000000..30bded0f9 --- /dev/null +++ b/charts/skillhub/templates/ingress.yaml @@ -0,0 +1,47 @@ +{{- if .Values.ingress.enabled }} +{{- $secretName := .Values.ingress.tls.secretName | default (printf "%s-tls" (include "skillhub.fullname" .)) }} +apiVersion: networking.k8s.io/v1 +kind: Ingress +metadata: + name: {{ include "skillhub.fullname" . }} + labels: + {{- include "skillhub.labels" . | nindent 4 }} + annotations: + {{- if .Values.ingress.annotations }} + {{- toYaml .Values.ingress.annotations | nindent 4 }} + {{- end }} + {{- if .Values.ingress.certManager.enabled }} + {{- if eq .Values.ingress.certManager.issuerKind "ClusterIssuer" }} + cert-manager.io/cluster-issuer: {{ .Values.ingress.certManager.issuerName }} + {{- else }} + cert-manager.io/issuer: {{ .Values.ingress.certManager.issuerName }} + {{- end }} + cert-manager.io/issuer-kind: {{ .Values.ingress.certManager.issuerKind }} + {{- end }} +spec: + ingressClassName: {{ .Values.ingress.className }} + {{- if or .Values.ingress.tls.enabled .Values.ingress.certManager.enabled }} + tls: + - hosts: + - {{ .Values.ingress.host }} + secretName: {{ $secretName }} + {{- end }} + rules: + - host: {{ .Values.ingress.host }} + http: + paths: + - path: /api + pathType: Prefix + backend: + service: + name: {{ include "skillhub.fullname" . }}-server + port: + number: {{ .Values.service.serverPort }} + - path: / + pathType: Prefix + backend: + service: + name: {{ include "skillhub.fullname" . }}-web + port: + number: {{ .Values.service.webPort }} +{{- end }} diff --git a/charts/skillhub/templates/pdb.yaml b/charts/skillhub/templates/pdb.yaml new file mode 100644 index 000000000..ed705197f --- /dev/null +++ b/charts/skillhub/templates/pdb.yaml @@ -0,0 +1,44 @@ +{{- if .Values.server.podDisruptionBudget.enabled }} +--- +apiVersion: policy/v1 +kind: PodDisruptionBudget +metadata: + name: {{ include "skillhub.fullname" . }}-server + labels: + {{- include "skillhub.server.labels" . | nindent 4 }} +spec: + selector: + matchLabels: + {{- include "skillhub.server.selectorLabels" . | nindent 6 }} + minAvailable: {{ .Values.server.podDisruptionBudget.minAvailable }} +{{- end }} + +{{- if .Values.web.podDisruptionBudget.enabled }} +--- +apiVersion: policy/v1 +kind: PodDisruptionBudget +metadata: + name: {{ include "skillhub.fullname" . }}-web + labels: + {{- include "skillhub.web.labels" . | nindent 4 }} +spec: + selector: + matchLabels: + {{- include "skillhub.web.selectorLabels" . | nindent 6 }} + minAvailable: {{ .Values.web.podDisruptionBudget.minAvailable }} +{{- end }} + +{{- if and .Values.scanner.enabled .Values.scanner.podDisruptionBudget.enabled }} +--- +apiVersion: policy/v1 +kind: PodDisruptionBudget +metadata: + name: {{ include "skillhub.fullname" . }}-scanner + labels: + {{- include "skillhub.scanner.labels" . | nindent 4 }} +spec: + selector: + matchLabels: + {{- include "skillhub.scanner.selectorLabels" . | nindent 6 }} + minAvailable: {{ .Values.scanner.podDisruptionBudget.minAvailable }} +{{- end }} diff --git a/charts/skillhub/templates/postgres-statefulset.yaml b/charts/skillhub/templates/postgres-statefulset.yaml new file mode 100644 index 000000000..49c38654a --- /dev/null +++ b/charts/skillhub/templates/postgres-statefulset.yaml @@ -0,0 +1,100 @@ +{{- if eq .Values.database.mode "internal" }} +--- +apiVersion: apps/v1 +kind: StatefulSet +metadata: + name: {{ include "skillhub.fullname" . }}-postgres + labels: + {{- include "skillhub.labels" . | nindent 4 }} + app.kubernetes.io/component: database +spec: + serviceName: {{ include "skillhub.fullname" . }}-postgres + replicas: {{ if eq .Values.database.architecture "cluster" }}3{{ else }}1{{ end }} + selector: + matchLabels: + {{- include "skillhub.selectorLabels" . | nindent 6 }} + app.kubernetes.io/component: database + template: + metadata: + labels: + {{- include "skillhub.selectorLabels" . | nindent 8 }} + app.kubernetes.io/component: database + spec: + containers: + - name: postgres + image: {{ .Values.database.internal.registry }}/{{ .Values.database.internal.image }} + ports: + - containerPort: 5432 + name: postgres + env: + - name: POSTGRES_DB + value: skillhub + - name: POSTGRES_USER + valueFrom: + secretKeyRef: + name: {{ include "skillhub.secretName" . }} + key: spring-datasource-username + - name: POSTGRES_PASSWORD + valueFrom: + secretKeyRef: + name: {{ include "skillhub.secretName" . }} + key: spring-datasource-password + - name: PGDATA + value: /var/lib/postgresql/data/pgdata + volumeMounts: + - name: postgres-data + mountPath: /var/lib/postgresql/data + resources: + {{- toYaml .Values.database.internal.resources | nindent 12 }} + readinessProbe: + exec: + command: + - pg_isready + - -U + - skillhub + - -h + - localhost + initialDelaySeconds: 10 + periodSeconds: 10 + livenessProbe: + exec: + command: + - pg_isready + - -U + - skillhub + - -h + - localhost + initialDelaySeconds: 30 + periodSeconds: 15 + volumeClaimTemplates: + - metadata: + name: postgres-data + labels: + {{- include "skillhub.labels" . | nindent 10 }} + spec: + accessModes: + - {{ .Values.storage.local.accessMode }} + {{- if .Values.database.internal.storageClassName }} + storageClassName: {{ .Values.database.internal.storageClassName }} + {{- end }} + resources: + requests: + storage: {{ .Values.database.internal.storage }} +--- +apiVersion: v1 +kind: Service +metadata: + name: {{ include "skillhub.fullname" . }}-postgres + labels: + {{- include "skillhub.labels" . | nindent 4 }} + app.kubernetes.io/component: database +spec: + type: ClusterIP + ports: + - port: 5432 + targetPort: postgres + name: postgres + selector: + {{- include "skillhub.selectorLabels" . | nindent 4 }} + app.kubernetes.io/component: database +{{- end }} diff --git a/charts/skillhub/templates/pvc.yaml b/charts/skillhub/templates/pvc.yaml new file mode 100644 index 000000000..0b5b49c74 --- /dev/null +++ b/charts/skillhub/templates/pvc.yaml @@ -0,0 +1,19 @@ +{{- if eq .Values.storage.provider "local" }} +apiVersion: v1 +kind: PersistentVolumeClaim +metadata: + name: {{ include "skillhub.fullname" . }}-storage-pvc + labels: + {{- include "skillhub.labels" . | nindent 4 }} + annotations: + helm.sh/resource-policy: keep +spec: + accessModes: + - {{ .Values.storage.local.accessMode }} + {{- if .Values.storage.local.storageClassName }} + storageClassName: {{ .Values.storage.local.storageClassName }} + {{- end }} + resources: + requests: + storage: {{ .Values.storage.local.storage }} +{{- end }} diff --git a/charts/skillhub/templates/redis-statefulset.yaml b/charts/skillhub/templates/redis-statefulset.yaml new file mode 100644 index 000000000..1a5815092 --- /dev/null +++ b/charts/skillhub/templates/redis-statefulset.yaml @@ -0,0 +1,83 @@ +{{- if eq .Values.redis.mode "internal" }} +--- +apiVersion: apps/v1 +kind: StatefulSet +metadata: + name: {{ include "skillhub.fullname" . }}-redis + labels: + {{- include "skillhub.labels" . | nindent 4 }} + app.kubernetes.io/component: cache +spec: + serviceName: {{ include "skillhub.fullname" . }}-redis + replicas: 1 + selector: + matchLabels: + {{- include "skillhub.selectorLabels" . | nindent 6 }} + app.kubernetes.io/component: cache + template: + metadata: + labels: + {{- include "skillhub.selectorLabels" . | nindent 8 }} + app.kubernetes.io/component: cache + spec: + containers: + - name: redis + image: {{ .Values.redis.internal.registry }}/{{ .Values.redis.internal.image }} + ports: + - containerPort: 6379 + name: redis + command: + - redis-server + - --appendonly + - "yes" + volumeMounts: + - name: redis-data + mountPath: /data + resources: + {{- toYaml .Values.redis.internal.resources | nindent 12 }} + readinessProbe: + exec: + command: + - redis-cli + - ping + initialDelaySeconds: 5 + periodSeconds: 10 + livenessProbe: + exec: + command: + - redis-cli + - ping + initialDelaySeconds: 10 + periodSeconds: 15 + volumeClaimTemplates: + - metadata: + name: redis-data + labels: + {{- include "skillhub.labels" . | nindent 10 }} + spec: + accessModes: + - {{ .Values.storage.local.accessMode }} + {{- if .Values.redis.internal.storageClassName }} + storageClassName: {{ .Values.redis.internal.storageClassName }} + {{- end }} + resources: + requests: + storage: {{ .Values.redis.internal.storage }} +--- +apiVersion: v1 +kind: Service +metadata: + name: {{ include "skillhub.fullname" . }}-redis + labels: + {{- include "skillhub.labels" . | nindent 4 }} + app.kubernetes.io/component: cache +spec: + type: ClusterIP + ports: + - port: 6379 + targetPort: redis + name: redis + selector: + {{- include "skillhub.selectorLabels" . | nindent 4 }} + app.kubernetes.io/component: cache +{{- end }} diff --git a/charts/skillhub/templates/scanner-deployment.yaml b/charts/skillhub/templates/scanner-deployment.yaml new file mode 100644 index 000000000..5269783d3 --- /dev/null +++ b/charts/skillhub/templates/scanner-deployment.yaml @@ -0,0 +1,66 @@ +{{- if .Values.scanner.enabled }} +apiVersion: apps/v1 +kind: Deployment +metadata: + name: {{ include "skillhub.fullname" . }}-scanner + labels: + {{- include "skillhub.scanner.labels" . | nindent 4 }} +spec: + replicas: {{ .Values.replicaCount }} + selector: + matchLabels: + {{- include "skillhub.scanner.selectorLabels" . | nindent 6 }} + template: + metadata: + labels: + {{- include "skillhub.scanner.selectorLabels" . | nindent 8 }} + annotations: + {{- toYaml .Values.scanner.podAnnotations | nindent 8 }} + spec: + {{- $secrets := .Values.scanner.imagePullSecrets | default .Values.global.imagePullSecrets }} + {{- if $secrets }} + imagePullSecrets: + {{- toYaml $secrets | nindent 8 }} + {{- end }} + containers: + - name: scanner + image: {{ .Values.images.registry }}/skillhub-scanner:{{ .Values.images.tag }} + imagePullPolicy: {{ .Values.images.pullPolicy }} + ports: + - containerPort: {{ .Values.service.scannerPort }} + name: http + env: + - name: SKILL_SCANNER_LLM_API_KEY + valueFrom: + secretKeyRef: + name: {{ include "skillhub.secretName" . }} + key: skill-scanner-llm-api-key + optional: true + - name: SKILL_SCANNER_LLM_MODEL + valueFrom: + secretKeyRef: + name: {{ include "skillhub.secretName" . }} + key: skill-scanner-llm-model + optional: true + {{- with .Values.scanner.extraEnv }} + {{- toYaml . | nindent 12 }} + {{- end }} + resources: + {{- toYaml .Values.scanner.resources | nindent 12 }} + readinessProbe: + {{- toYaml .Values.scanner.probes.readiness | nindent 12 }} + livenessProbe: + {{- toYaml .Values.scanner.probes.liveness | nindent 12 }} + {{- with .Values.scanner.nodeSelector }} + nodeSelector: + {{- toYaml . | nindent 8 }} + {{- end }} + {{- with .Values.scanner.tolerations }} + tolerations: + {{- toYaml . | nindent 8 }} + {{- end }} + {{- with .Values.scanner.affinity }} + affinity: + {{- toYaml . | nindent 8 }} + {{- end }} +{{- end }} diff --git a/charts/skillhub/templates/secret.yaml b/charts/skillhub/templates/secret.yaml new file mode 100644 index 000000000..8ef69132a --- /dev/null +++ b/charts/skillhub/templates/secret.yaml @@ -0,0 +1,28 @@ +{{- if not .Values.existingSecret }} +apiVersion: v1 +kind: Secret +metadata: + name: {{ include "skillhub.fullname" . }}-secret + labels: + {{- include "skillhub.labels" . | nindent 4 }} +type: Opaque +stringData: + spring-datasource-url: {{ include "skillhub.jdbcUrl" . | quote }} + spring-datasource-username: {{ if eq .Values.database.mode "internal" }}skillhub{{ else }}{{ .Values.database.external.username }}{{ end }} + spring-datasource-password: {{ if eq .Values.database.mode "internal" }}{{ default (randAlphaNum 16) .Values.secrets.springDatasourcePassword }}{{ else }}{{ .Values.database.external.password }}{{ end }} + + bootstrap-admin-password: {{ .Values.secrets.bootstrapAdminPassword | default .Values.bootstrapAdmin.password | default (randAlphaNum 16) }} + + {{- if .Values.secrets.oauth2GithubClientId }} + oauth2-github-client-id: {{ .Values.secrets.oauth2GithubClientId }} + {{- end }} + {{- if .Values.secrets.oauth2GithubClientSecret }} + oauth2-github-client-secret: {{ .Values.secrets.oauth2GithubClientSecret }} + {{- end }} + {{- if .Values.secrets.scannerLlmApiKey }} + skill-scanner-llm-api-key: {{ .Values.secrets.scannerLlmApiKey }} + {{- end }} + {{- if .Values.secrets.scannerLlmModel }} + skill-scanner-llm-model: {{ .Values.secrets.scannerLlmModel }} + {{- end }} +{{- end }} diff --git a/charts/skillhub/templates/services.yaml b/charts/skillhub/templates/services.yaml new file mode 100644 index 000000000..2ac0e1200 --- /dev/null +++ b/charts/skillhub/templates/services.yaml @@ -0,0 +1,83 @@ +--- +apiVersion: v1 +kind: Service +metadata: + name: {{ include "skillhub.fullname" . }}-server + labels: + {{- include "skillhub.server.labels" . | nindent 4 }} +spec: + type: {{ .Values.service.type }} + {{- if eq .Values.service.type "LoadBalancer" }} + {{- if .Values.service.loadBalancerIP }} + loadBalancerIP: {{ .Values.service.loadBalancerIP }} + {{- end }} + {{- if .Values.service.loadBalancerSourceRanges }} + loadBalancerSourceRanges: + {{- toYaml .Values.service.loadBalancerSourceRanges | nindent 4 }} + {{- end }} + {{- end }} + ports: + - name: http + port: {{ .Values.service.serverPort }} + targetPort: http + {{- if and (eq .Values.service.type "NodePort") .Values.service.serverNodePort }} + nodePort: {{ .Values.service.serverNodePort }} + {{- end }} + selector: + {{- include "skillhub.server.selectorLabels" . | nindent 4 }} +--- +apiVersion: v1 +kind: Service +metadata: + name: {{ include "skillhub.fullname" . }}-web + labels: + {{- include "skillhub.web.labels" . | nindent 4 }} +spec: + type: {{ .Values.service.type }} + {{- if eq .Values.service.type "LoadBalancer" }} + {{- if .Values.service.loadBalancerIP }} + loadBalancerIP: {{ .Values.service.loadBalancerIP }} + {{- end }} + {{- if .Values.service.loadBalancerSourceRanges }} + loadBalancerSourceRanges: + {{- toYaml .Values.service.loadBalancerSourceRanges | nindent 4 }} + {{- end }} + {{- end }} + ports: + - name: http + port: {{ .Values.service.webPort }} + targetPort: http + {{- if and (eq .Values.service.type "NodePort") .Values.service.webNodePort }} + nodePort: {{ .Values.service.webNodePort }} + {{- end }} + selector: + {{- include "skillhub.web.selectorLabels" . | nindent 4 }} +{{- if .Values.scanner.enabled }} +--- +apiVersion: v1 +kind: Service +metadata: + name: {{ include "skillhub.fullname" . }}-scanner + labels: + {{- include "skillhub.scanner.labels" . | nindent 4 }} +spec: + type: {{ .Values.service.type }} + {{- if eq .Values.service.type "LoadBalancer" }} + {{- if .Values.service.loadBalancerIP }} + loadBalancerIP: {{ .Values.service.loadBalancerIP }} + {{- end }} + {{- if .Values.service.loadBalancerSourceRanges }} + loadBalancerSourceRanges: + {{- toYaml .Values.service.loadBalancerSourceRanges | nindent 4 }} + {{- end }} + {{- end }} + ports: + - name: http + port: {{ .Values.service.scannerPort }} + targetPort: http + {{- if and (eq .Values.service.type "NodePort") .Values.service.scannerNodePort }} + nodePort: {{ .Values.service.scannerNodePort }} + {{- end }} + selector: + {{- include "skillhub.scanner.selectorLabels" . | nindent 4 }} +{{- end }} diff --git a/charts/skillhub/values.yaml b/charts/skillhub/values.yaml new file mode 100644 index 000000000..181b99e90 --- /dev/null +++ b/charts/skillhub/values.yaml @@ -0,0 +1,312 @@ +# ============================================================================ +# SkillHub Helm Chart 全局配置 +# ============================================================================ + +# ============================================================================ +# Global +# ============================================================================ +global: + imageRegistry: "" + imagePullSecrets: [] + +# ============================================================================ +# 镜像配置 +# ============================================================================ +images: + registry: ghcr.io/iflytek + tag: latest + pullPolicy: IfNotPresent + +# ============================================================================ +# 副本数 +# ============================================================================ +replicaCount: 1 + +nameOverride: "" +fullnameOverride: "" + +# ============================================================================ +# 服务配置 +# ============================================================================ +service: + # ClusterIP | NodePort | LoadBalancer + type: ClusterIP + serverPort: 8080 + webPort: 80 + scannerPort: 8000 + # type: NodePort 时指定 nodePort(不指定则由集群分配) + serverNodePort: "" + webNodePort: "" + scannerNodePort: "" + # type: LoadBalancer 时可选固定 IP + loadBalancerIP: "" + loadBalancerSourceRanges: [] + +# ============================================================================ +# Ingress 配置 +# ============================================================================ +ingress: + enabled: false + className: nginx + host: skills.example.com + annotations: + nginx.ingress.kubernetes.io/proxy-body-size: 100m + tls: + enabled: false + secretName: "" + certManager: + enabled: false + issuerName: letsencrypt-prod + issuerKind: ClusterIssuer + +# ============================================================================ +# 应用组件配置 +# ============================================================================ +server: + resources: + requests: + cpu: 500m + memory: 512Mi + limits: + cpu: 1000m + memory: 1Gi + javaOpts: "" + extraEnv: [] + podAnnotations: {} + imagePullSecrets: [] + nodeSelector: {} + tolerations: [] + affinity: {} + # HPA(自动扩缩容) + autoscaling: + enabled: false + minReplicas: 1 + maxReplicas: 10 + targetCPUUtilizationPercentage: 80 + targetMemoryUtilizationPercentage: 80 + # PDB(自愿干扰预算,多副本时保证最少可用实例) + podDisruptionBudget: + enabled: false + minAvailable: 1 + probes: + startup: + httpGet: + path: /actuator/health + port: http + initialDelaySeconds: 10 + periodSeconds: 10 + failureThreshold: 30 + readiness: + httpGet: + path: /actuator/health + port: http + initialDelaySeconds: 20 + periodSeconds: 10 + liveness: + httpGet: + path: /actuator/health + port: http + initialDelaySeconds: 30 + periodSeconds: 15 + +web: + resources: + requests: + cpu: 100m + memory: 128Mi + limits: + cpu: 200m + memory: 256Mi + extraEnv: [] + podAnnotations: {} + imagePullSecrets: [] + nodeSelector: {} + tolerations: [] + affinity: {} + # HPA(自动扩缩容) + autoscaling: + enabled: false + minReplicas: 1 + maxReplicas: 5 + targetCPUUtilizationPercentage: 80 + targetMemoryUtilizationPercentage: 80 + # PDB(自愿干扰预算,多副本时保证最少可用实例) + podDisruptionBudget: + enabled: false + minAvailable: 1 + probes: + readiness: + httpGet: + path: /nginx-health + port: http + initialDelaySeconds: 5 + periodSeconds: 10 + liveness: + httpGet: + path: /nginx-health + port: http + initialDelaySeconds: 10 + periodSeconds: 15 + +scanner: + enabled: true + resources: + requests: + cpu: 100m + memory: 256Mi + limits: + cpu: 500m + memory: 512Mi + extraEnv: [] + podAnnotations: {} + imagePullSecrets: [] + nodeSelector: {} + tolerations: [] + affinity: {} + # HPA(自动扩缩容) + autoscaling: + enabled: false + minReplicas: 1 + maxReplicas: 5 + targetCPUUtilizationPercentage: 80 + targetMemoryUtilizationPercentage: 80 + # PDB(自愿干扰预算,多副本时保证最少可用实例) + podDisruptionBudget: + enabled: false + minAvailable: 1 + probes: + readiness: + httpGet: + path: /health + port: http + initialDelaySeconds: 10 + periodSeconds: 10 + liveness: + httpGet: + path: /health + port: http + initialDelaySeconds: 20 + periodSeconds: 15 + +# ============================================================================ +# 数据库配置(PostgreSQL) +# ============================================================================ +# mode: internal(内置)| external(外置) +# architecture: standalone(单实例)| cluster(高可用集群) +# - standalone + internal: 部署单实例 PostgreSQL +# - standalone + external: 连接外置单节点 PostgreSQL +# - cluster 时建议使用 external 模式 +database: + mode: internal + architecture: standalone + + internal: + image: postgres:16-alpine + registry: docker.io + storage: 10Gi + storageClassName: "" + resources: + requests: + cpu: 100m + memory: 256Mi + limits: + cpu: 500m + memory: 512Mi + + external: + host: postgres.example.com + port: 5432 + database: skillhub + username: skillhub + password: "" + parameters: "" + # 自定义 JDBC URL(非空时覆盖 host/port/database/parameters 的拼接结果) + # 用于连接外部 PostgreSQL 集群(如 Patroni、JDBC 多主机等) + jdbcUrl: "" + +# ============================================================================ +# Redis 配置 +# ============================================================================ +# mode: internal(内置单实例)| external(外置) +# 注意: Redis Cluster 模式不支持 +redis: + mode: internal + + internal: + image: redis:7-alpine + registry: docker.io + storage: 5Gi + storageClassName: "" + resources: + requests: + cpu: 50m + memory: 128Mi + limits: + cpu: 200m + memory: 256Mi + + external: + host: redis.example.com + port: 6379 + password: "" + sentinel: + enabled: false + masterSet: mymaster + nodes: [] + +# ============================================================================ +# 存储配置 +# ============================================================================ +storage: + provider: local + local: + # 多副本 (replicaCount > 1) 时必须设为 ReadWriteMany,底层需支持 RWX(如 NFS/Longhorn) + accessMode: ReadWriteOnce + storage: 10Gi + storageClassName: "" + s3: + bucket: skillhub-storage + endpoint: "" + region: "" + accessKey: "" + secretKey: "" + +# ============================================================================ +# Bootstrap 管理员 +# ============================================================================ +bootstrapAdmin: + enabled: true + userId: docker-admin + username: admin + displayName: "Platform Admin" + email: admin@example.com + password: "" + +# ============================================================================ +# Session 配置 +# ============================================================================ +session: + cookieSecure: false + +# ============================================================================ +# Spring Profiles +# ============================================================================ +springProfilesActive: docker + +# ============================================================================ +# Secret 配置 +# ============================================================================ +# 使用已有 Secret(优先级高于下方 secrets.* 字段) +# 设置后 chart 不会创建 Secret,而是直接引用该名称 +existingSecret: "" + +secrets: + springDatasourceUrl: "" + springDatasourceUsername: "" + springDatasourcePassword: "" + bootstrapAdminPassword: "" + oauth2GithubClientId: "" + oauth2GithubClientSecret: "" + scannerLlmApiKey: "" + scannerLlmModel: "" + From a511253fa9ae5bfd3a27e49da2b0398ccc5f499a Mon Sep 17 00:00:00 2001 From: jangrui Date: Sat, 16 May 2026 08:40:58 +0800 Subject: [PATCH 2/5] =?UTF-8?q?fix(chart):=20=E4=BF=AE=E5=A4=8D=E5=AE=89?= =?UTF-8?q?=E5=85=A8=E3=80=81=E5=85=BC=E5=AE=B9=E5=8F=8A=E5=8F=AF=E7=BB=B4?= =?UTF-8?q?=E6=8A=A4=E6=80=A7=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - secret.yaml: lookup 检查现有 Secret 避免 upgrade 重新生成密码 - secret.yaml: Redis/S3 凭据通过 Secret 引用,移除明文环境变量 - backend/frontend/scanner: 新增 checksum 注解,配置变更自动触发滚动更新 - backend/frontend/scanner: 镜像地址支持 global.imageRegistry 覆盖 - postgres: internal 模式仅支持单副本,移除伪集群配置 - postgres: 探针用户名改用 POSTGRES_USER 环境变量 - values.yaml: accessMode 默认 ReadWriteMany,tag 指定 v0.2.8 --- .../templates/backend-deployment.yaml | 32 +++++++---- .../templates/frontend-deployment.yaml | 8 ++- .../templates/postgres-statefulset.yaml | 18 +++---- .../templates/scanner-deployment.yaml | 8 ++- charts/skillhub/templates/secret.yaml | 53 +++++++++++++++++-- charts/skillhub/values.yaml | 4 +- 6 files changed, 93 insertions(+), 30 deletions(-) diff --git a/charts/skillhub/templates/backend-deployment.yaml b/charts/skillhub/templates/backend-deployment.yaml index b0a1d8325..a215afae6 100644 --- a/charts/skillhub/templates/backend-deployment.yaml +++ b/charts/skillhub/templates/backend-deployment.yaml @@ -14,7 +14,11 @@ spec: labels: {{- include "skillhub.server.selectorLabels" . | nindent 8 }} annotations: - {{- toYaml .Values.server.podAnnotations | nindent 8 }} + checksum/config: {{ toYaml (dict "redis" .Values.redis "storage" .Values.storage "database" .Values.database "session" .Values.session "springProfilesActive" .Values.springProfilesActive "bootstrapAdmin" .Values.bootstrapAdmin) | sha256sum }} + checksum/secret: {{ toYaml (dict "secrets" .Values.secrets "bootstrapAdmin" .Values.bootstrapAdmin) | sha256sum }} + {{- range $key, $val := .Values.server.podAnnotations }} + {{ $key }}: {{ $val }} + {{- end }} spec: {{- $secrets := .Values.server.imagePullSecrets | default .Values.global.imagePullSecrets }} {{- if $secrets }} @@ -45,7 +49,7 @@ spec: echo "Redis is ready!" containers: - name: server - image: {{ .Values.images.registry }}/skillhub-server:{{ .Values.images.tag }} + image: {{ .Values.global.imageRegistry | default .Values.images.registry }}/skillhub-server:{{ .Values.images.tag }} imagePullPolicy: {{ .Values.images.pullPolicy }} ports: - containerPort: {{ .Values.service.serverPort }} @@ -83,9 +87,13 @@ spec: name: {{ include "skillhub.fullname" . }}-config key: redis-port - {{- if and (eq .Values.redis.mode "external") .Values.redis.external.password }} + {{- if eq .Values.redis.mode "external" }} - name: SPRING_DATA_REDIS_PASSWORD - value: {{ .Values.redis.external.password }} + valueFrom: + secretKeyRef: + name: {{ include "skillhub.secretName" . }} + key: redis-password + optional: true {{- end }} {{- if .Values.redis.external.sentinel.enabled }} @@ -114,14 +122,18 @@ spec: value: {{ .Values.storage.s3.endpoint }} - name: SKILLHUB_S3_REGION value: {{ .Values.storage.s3.region }} - {{- if .Values.storage.s3.accessKey }} - name: SKILLHUB_S3_ACCESS_KEY - value: {{ .Values.storage.s3.accessKey }} - {{- end }} - {{- if .Values.storage.s3.secretKey }} + valueFrom: + secretKeyRef: + name: {{ include "skillhub.secretName" . }} + key: s3-access-key + optional: true - name: SKILLHUB_S3_SECRET_KEY - value: {{ .Values.storage.s3.secretKey }} - {{- end }} + valueFrom: + secretKeyRef: + name: {{ include "skillhub.secretName" . }} + key: s3-secret-key + optional: true {{- end }} # Scanner diff --git a/charts/skillhub/templates/frontend-deployment.yaml b/charts/skillhub/templates/frontend-deployment.yaml index 9b2942dd5..9129a00a4 100644 --- a/charts/skillhub/templates/frontend-deployment.yaml +++ b/charts/skillhub/templates/frontend-deployment.yaml @@ -14,7 +14,11 @@ spec: labels: {{- include "skillhub.web.selectorLabels" . | nindent 8 }} annotations: - {{- toYaml .Values.web.podAnnotations | nindent 8 }} + checksum/config: {{ toYaml (dict "redis" .Values.redis "storage" .Values.storage "database" .Values.database "session" .Values.session "springProfilesActive" .Values.springProfilesActive "bootstrapAdmin" .Values.bootstrapAdmin) | sha256sum }} + checksum/secret: {{ toYaml (dict "secrets" .Values.secrets "bootstrapAdmin" .Values.bootstrapAdmin) | sha256sum }} + {{- range $key, $val := .Values.web.podAnnotations }} + {{ $key }}: {{ $val }} + {{- end }} spec: {{- $secrets := .Values.web.imagePullSecrets | default .Values.global.imagePullSecrets }} {{- if $secrets }} @@ -23,7 +27,7 @@ spec: {{- end }} containers: - name: web - image: {{ .Values.images.registry }}/skillhub-web:{{ .Values.images.tag }} + image: {{ .Values.global.imageRegistry | default .Values.images.registry }}/skillhub-web:{{ .Values.images.tag }} imagePullPolicy: {{ .Values.images.pullPolicy }} env: - name: SKILLHUB_API_UPSTREAM diff --git a/charts/skillhub/templates/postgres-statefulset.yaml b/charts/skillhub/templates/postgres-statefulset.yaml index 49c38654a..d76167412 100644 --- a/charts/skillhub/templates/postgres-statefulset.yaml +++ b/charts/skillhub/templates/postgres-statefulset.yaml @@ -9,7 +9,7 @@ metadata: app.kubernetes.io/component: database spec: serviceName: {{ include "skillhub.fullname" . }}-postgres - replicas: {{ if eq .Values.database.architecture "cluster" }}3{{ else }}1{{ end }} + replicas: 1 selector: matchLabels: {{- include "skillhub.selectorLabels" . | nindent 6 }} @@ -49,21 +49,17 @@ spec: readinessProbe: exec: command: - - pg_isready - - -U - - skillhub - - -h - - localhost + - sh + - -c + - pg_isready -U "${POSTGRES_USER}" -h localhost initialDelaySeconds: 10 periodSeconds: 10 livenessProbe: exec: command: - - pg_isready - - -U - - skillhub - - -h - - localhost + - sh + - -c + - pg_isready -U "${POSTGRES_USER}" -h localhost initialDelaySeconds: 30 periodSeconds: 15 volumeClaimTemplates: diff --git a/charts/skillhub/templates/scanner-deployment.yaml b/charts/skillhub/templates/scanner-deployment.yaml index 5269783d3..461d846b7 100644 --- a/charts/skillhub/templates/scanner-deployment.yaml +++ b/charts/skillhub/templates/scanner-deployment.yaml @@ -15,7 +15,11 @@ spec: labels: {{- include "skillhub.scanner.selectorLabels" . | nindent 8 }} annotations: - {{- toYaml .Values.scanner.podAnnotations | nindent 8 }} + checksum/config: {{ toYaml (dict "redis" .Values.redis "storage" .Values.storage "database" .Values.database "session" .Values.session "springProfilesActive" .Values.springProfilesActive "bootstrapAdmin" .Values.bootstrapAdmin) | sha256sum }} + checksum/secret: {{ toYaml (dict "secrets" .Values.secrets "bootstrapAdmin" .Values.bootstrapAdmin) | sha256sum }} + {{- range $key, $val := .Values.scanner.podAnnotations }} + {{ $key }}: {{ $val }} + {{- end }} spec: {{- $secrets := .Values.scanner.imagePullSecrets | default .Values.global.imagePullSecrets }} {{- if $secrets }} @@ -24,7 +28,7 @@ spec: {{- end }} containers: - name: scanner - image: {{ .Values.images.registry }}/skillhub-scanner:{{ .Values.images.tag }} + image: {{ .Values.global.imageRegistry | default .Values.images.registry }}/skillhub-scanner:{{ .Values.images.tag }} imagePullPolicy: {{ .Values.images.pullPolicy }} ports: - containerPort: {{ .Values.service.scannerPort }} diff --git a/charts/skillhub/templates/secret.yaml b/charts/skillhub/templates/secret.yaml index 8ef69132a..1a739137e 100644 --- a/charts/skillhub/templates/secret.yaml +++ b/charts/skillhub/templates/secret.yaml @@ -1,17 +1,64 @@ {{- if not .Values.existingSecret }} +{{- $secretName := printf "%s-secret" (include "skillhub.fullname" .) }} +{{- $existingSecret := (lookup "v1" "Secret" .Release.Namespace $secretName) }} apiVersion: v1 kind: Secret metadata: - name: {{ include "skillhub.fullname" . }}-secret + name: {{ $secretName }} labels: {{- include "skillhub.labels" . | nindent 4 }} type: Opaque stringData: spring-datasource-url: {{ include "skillhub.jdbcUrl" . | quote }} spring-datasource-username: {{ if eq .Values.database.mode "internal" }}skillhub{{ else }}{{ .Values.database.external.username }}{{ end }} - spring-datasource-password: {{ if eq .Values.database.mode "internal" }}{{ default (randAlphaNum 16) .Values.secrets.springDatasourcePassword }}{{ else }}{{ .Values.database.external.password }}{{ end }} - bootstrap-admin-password: {{ .Values.secrets.bootstrapAdminPassword | default .Values.bootstrapAdmin.password | default (randAlphaNum 16) }} + {{- $dsPwd := "" }} + {{- if $existingSecret }} + {{- $dsPwd = $existingSecret.data.springDatasourcePassword | b64dec }} + {{- else if eq .Values.database.mode "internal" }} + {{- $dsPwd = default (randAlphaNum 16) .Values.secrets.springDatasourcePassword }} + {{- else }} + {{- $dsPwd = .Values.database.external.password }} + {{- end }} + spring-datasource-password: {{ $dsPwd | quote }} + + {{- $redisPwd := "" }} + {{- if $existingSecret }} + {{- $redisPwd = $existingSecret.data.redisPassword | b64dec }} + {{- else }} + {{- $redisPwd = .Values.redis.external.password | default "" }} + {{- end }} + {{- if $redisPwd }} + redis-password: {{ $redisPwd | quote }} + {{- end }} + + {{- $s3Key := "" }} + {{- if $existingSecret }} + {{- $s3Key = $existingSecret.data.s3AccessKey | b64dec }} + {{- else }} + {{- $s3Key = .Values.storage.s3.accessKey | default "" }} + {{- end }} + {{- if $s3Key }} + s3-access-key: {{ $s3Key | quote }} + {{- end }} + + {{- $s3Secret := "" }} + {{- if $existingSecret }} + {{- $s3Secret = $existingSecret.data.s3SecretKey | b64dec }} + {{- else }} + {{- $s3Secret = .Values.storage.s3.secretKey | default "" }} + {{- end }} + {{- if $s3Secret }} + s3-secret-key: {{ $s3Secret | quote }} + {{- end }} + + {{- $baPwd := "" }} + {{- if $existingSecret }} + {{- $baPwd = $existingSecret.data.bootstrapAdminPassword | b64dec }} + {{- else }} + {{- $baPwd = .Values.secrets.bootstrapAdminPassword | default .Values.bootstrapAdmin.password | default (randAlphaNum 16) }} + {{- end }} + bootstrap-admin-password: {{ $baPwd | quote }} {{- if .Values.secrets.oauth2GithubClientId }} oauth2-github-client-id: {{ .Values.secrets.oauth2GithubClientId }} diff --git a/charts/skillhub/values.yaml b/charts/skillhub/values.yaml index 181b99e90..813284955 100644 --- a/charts/skillhub/values.yaml +++ b/charts/skillhub/values.yaml @@ -14,7 +14,7 @@ global: # ============================================================================ images: registry: ghcr.io/iflytek - tag: latest + tag: v0.2.8 pullPolicy: IfNotPresent # ============================================================================ @@ -261,7 +261,7 @@ storage: provider: local local: # 多副本 (replicaCount > 1) 时必须设为 ReadWriteMany,底层需支持 RWX(如 NFS/Longhorn) - accessMode: ReadWriteOnce + accessMode: ReadWriteMany storage: 10Gi storageClassName: "" s3: From fce0de774dd324fb8c55572fa08b0884feb7bb92 Mon Sep 17 00:00:00 2001 From: jangrui Date: Sat, 16 May 2026 08:53:38 +0800 Subject: [PATCH 3/5] =?UTF-8?q?feat(chart):=20=E9=95=9C=E5=83=8F=20tag=20?= =?UTF-8?q?=E4=B8=8E=20Chart.yaml=20appVersion=20=E8=81=94=E5=8A=A8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit values.yaml 中 images.tag 留空时自动取 Chart.yaml 的 appVersion, 格式为 v{appVersion}(如 0.2.8 → v0.2.8)。 用户仍可通过 --set images.tag=xxx 显式覆盖。 --- charts/skillhub/templates/backend-deployment.yaml | 2 +- charts/skillhub/templates/frontend-deployment.yaml | 2 +- charts/skillhub/templates/scanner-deployment.yaml | 2 +- charts/skillhub/values.yaml | 3 ++- 4 files changed, 5 insertions(+), 4 deletions(-) diff --git a/charts/skillhub/templates/backend-deployment.yaml b/charts/skillhub/templates/backend-deployment.yaml index a215afae6..ff5974776 100644 --- a/charts/skillhub/templates/backend-deployment.yaml +++ b/charts/skillhub/templates/backend-deployment.yaml @@ -49,7 +49,7 @@ spec: echo "Redis is ready!" containers: - name: server - image: {{ .Values.global.imageRegistry | default .Values.images.registry }}/skillhub-server:{{ .Values.images.tag }} + image: {{ .Values.global.imageRegistry | default .Values.images.registry }}/skillhub-server:{{ .Values.images.tag | default (printf "v%s" .Chart.AppVersion) }} imagePullPolicy: {{ .Values.images.pullPolicy }} ports: - containerPort: {{ .Values.service.serverPort }} diff --git a/charts/skillhub/templates/frontend-deployment.yaml b/charts/skillhub/templates/frontend-deployment.yaml index 9129a00a4..808654f4c 100644 --- a/charts/skillhub/templates/frontend-deployment.yaml +++ b/charts/skillhub/templates/frontend-deployment.yaml @@ -27,7 +27,7 @@ spec: {{- end }} containers: - name: web - image: {{ .Values.global.imageRegistry | default .Values.images.registry }}/skillhub-web:{{ .Values.images.tag }} + image: {{ .Values.global.imageRegistry | default .Values.images.registry }}/skillhub-web:{{ .Values.images.tag | default (printf "v%s" .Chart.AppVersion) }} imagePullPolicy: {{ .Values.images.pullPolicy }} env: - name: SKILLHUB_API_UPSTREAM diff --git a/charts/skillhub/templates/scanner-deployment.yaml b/charts/skillhub/templates/scanner-deployment.yaml index 461d846b7..6e91920d7 100644 --- a/charts/skillhub/templates/scanner-deployment.yaml +++ b/charts/skillhub/templates/scanner-deployment.yaml @@ -28,7 +28,7 @@ spec: {{- end }} containers: - name: scanner - image: {{ .Values.global.imageRegistry | default .Values.images.registry }}/skillhub-scanner:{{ .Values.images.tag }} + image: {{ .Values.global.imageRegistry | default .Values.images.registry }}/skillhub-scanner:{{ .Values.images.tag | default (printf "v%s" .Chart.AppVersion) }} imagePullPolicy: {{ .Values.images.pullPolicy }} ports: - containerPort: {{ .Values.service.scannerPort }} diff --git a/charts/skillhub/values.yaml b/charts/skillhub/values.yaml index 813284955..d2cd5d21a 100644 --- a/charts/skillhub/values.yaml +++ b/charts/skillhub/values.yaml @@ -14,7 +14,8 @@ global: # ============================================================================ images: registry: ghcr.io/iflytek - tag: v0.2.8 + # 留空时自动使用 Chart.yaml 中的 appVersion(带 v 前缀) + tag: "" pullPolicy: IfNotPresent # ============================================================================ From 703f946da8a6223223ee26f47238bd52313e0431 Mon Sep 17 00:00:00 2001 From: jangrui Date: Sat, 16 May 2026 09:59:33 +0800 Subject: [PATCH 4/5] =?UTF-8?q?feat(chart):=20=E6=B7=BB=E5=8A=A0=20Helm=20?= =?UTF-8?q?Chart=20=E5=8F=91=E5=B8=83=E5=B7=A5=E4=BD=9C=E6=B5=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/workflows/publish-helm-chart.yml | 6 +- .github/workflows/release-chart.yml | 89 ++++++++++++++++++++++++ 2 files changed, 92 insertions(+), 3 deletions(-) create mode 100644 .github/workflows/release-chart.yml diff --git a/.github/workflows/publish-helm-chart.yml b/.github/workflows/publish-helm-chart.yml index 7508143da..ae34678d3 100644 --- a/.github/workflows/publish-helm-chart.yml +++ b/.github/workflows/publish-helm-chart.yml @@ -34,9 +34,9 @@ jobs: id: chartver run: | REF="${{ github.ref_name }}" - # Support helm-vX.Y.Z or just vX.Y.Z tags - if [[ "$REF" =~ ^helm-v?([0-9]+\.[0-9]+\.[0-9]+)$ ]]; then - VERSION="${BASH_REMATCH[1]}" + # Support helm-vX.Y.Z, chart-vX.Y.Z, or just vX.Y.Z tags + if [[ "$REF" =~ ^(helm|chart)-v?([0-9]+\.[0-9]+\.[0-9]+)$ ]]; then + VERSION="${BASH_REMATCH[2]}" elif [[ "$REF" =~ ^v?([0-9]+\.[0-9]+\.[0-9]+)$ ]]; then VERSION="${BASH_REMATCH[1]}" else diff --git a/.github/workflows/release-chart.yml b/.github/workflows/release-chart.yml new file mode 100644 index 000000000..03cec0946 --- /dev/null +++ b/.github/workflows/release-chart.yml @@ -0,0 +1,89 @@ +name: Release Helm Chart + +on: + push: + tags: + - 'v*' + workflow_dispatch: + +concurrency: + group: release-helm-chart + cancel-in-progress: true + +permissions: + contents: write + packages: write + +jobs: + release: + if: ${{ !startsWith(github.ref_name, 'chart-v') }} + runs-on: ubuntu-latest + + steps: + - name: Check out repository + uses: actions/checkout@v4 + with: + fetch-depth: 0 + + - name: Set up Helm + uses: azure/setup-helm@v4 + with: + version: latest + + - name: Extract version + id: version + run: | + REF="${{ github.ref_name }}" + APP="${REF#v}" + echo "app=$APP" >> "$GITHUB_OUTPUT" + + - name: Update Chart.yaml appVersion + id: chart + working-directory: charts/skillhub + run: | + CHART_VER=$(helm show chart . | grep '^version:' | awk '{print $2}') + echo "chartVersion=$CHART_VER" >> "$GITHUB_OUTPUT" + echo "appVersion=${{ steps.version.outputs.app }}" >> "$GITHUB_OUTPUT" + + sed -i "s/^appVersion:.*/appVersion: ${{ steps.version.outputs.app }}/" Chart.yaml + + - name: Validate chart + working-directory: charts/skillhub + run: helm lint + + - name: Commit and push to main + run: | + git config user.name "skillhub-bot" + git config user.email "bot@skillhub.dev" + git checkout -b release-tmp + git add charts/skillhub/Chart.yaml + git commit -m "chore: update appVersion to ${{ steps.version.outputs.app }}" + git push origin HEAD:main + + - name: Log in to GHCR + uses: docker/login-action@v3 + with: + registry: ghcr.io + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} + + - name: Package and publish chart + working-directory: charts/skillhub + run: | + helm package . --version "${{ steps.chart.outputs.chartVersion }}" \ + --destination /tmp/helm-charts + helm push /tmp/helm-charts/skillhub-${{ steps.chart.outputs.chartVersion }}.tgz \ + oci://ghcr.io/${{ github.repository_owner }}/charts + + - name: Generate artifact attestation + uses: actions/attest-build-provenance@v2 + with: + subject-path: /tmp/helm-charts/skillhub-${{ steps.chart.outputs.chartVersion }}.tgz + + - name: Upload chart artifact + uses: actions/upload-artifact@v4 + with: + name: skillhub-${{ steps.chart.outputs.chartVersion }}.tgz + path: /tmp/helm-charts/skillhub-${{ steps.chart.outputs.chartVersion }}.tgz + retention-days: 90 + From b06a5875eebc6b7ecf227322766578dac26faec5 Mon Sep 17 00:00:00 2001 From: jangrui Date: Mon, 18 May 2026 01:15:01 +0800 Subject: [PATCH 5/5] =?UTF-8?q?feat(chart):=20=E6=B7=BB=E5=8A=A0=E7=BB=84?= =?UTF-8?q?=E4=BB=B6=E7=BA=A7=E9=95=9C=E5=83=8F=E6=A0=87=E7=AD=BE=E9=85=8D?= =?UTF-8?q?=E7=BD=AE=E4=BB=A5=E6=94=AF=E6=8C=81=E4=B8=AA=E6=80=A7=E5=8C=96?= =?UTF-8?q?=E9=95=9C=E5=83=8F=E7=89=88=E6=9C=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- charts/skillhub/templates/backend-deployment.yaml | 2 +- charts/skillhub/templates/frontend-deployment.yaml | 2 +- charts/skillhub/templates/scanner-deployment.yaml | 2 +- charts/skillhub/values.yaml | 9 +++++++++ 4 files changed, 12 insertions(+), 3 deletions(-) diff --git a/charts/skillhub/templates/backend-deployment.yaml b/charts/skillhub/templates/backend-deployment.yaml index ff5974776..f277681e7 100644 --- a/charts/skillhub/templates/backend-deployment.yaml +++ b/charts/skillhub/templates/backend-deployment.yaml @@ -49,7 +49,7 @@ spec: echo "Redis is ready!" containers: - name: server - image: {{ .Values.global.imageRegistry | default .Values.images.registry }}/skillhub-server:{{ .Values.images.tag | default (printf "v%s" .Chart.AppVersion) }} + image: {{ .Values.global.imageRegistry | default .Values.images.registry }}/skillhub-server:{{ .Values.server.image.tag | default .Values.images.tag | default (printf "v%s" .Chart.AppVersion) }} imagePullPolicy: {{ .Values.images.pullPolicy }} ports: - containerPort: {{ .Values.service.serverPort }} diff --git a/charts/skillhub/templates/frontend-deployment.yaml b/charts/skillhub/templates/frontend-deployment.yaml index 808654f4c..89fe2f052 100644 --- a/charts/skillhub/templates/frontend-deployment.yaml +++ b/charts/skillhub/templates/frontend-deployment.yaml @@ -27,7 +27,7 @@ spec: {{- end }} containers: - name: web - image: {{ .Values.global.imageRegistry | default .Values.images.registry }}/skillhub-web:{{ .Values.images.tag | default (printf "v%s" .Chart.AppVersion) }} + image: {{ .Values.global.imageRegistry | default .Values.images.registry }}/skillhub-web:{{ .Values.web.image.tag | default .Values.images.tag | default (printf "v%s" .Chart.AppVersion) }} imagePullPolicy: {{ .Values.images.pullPolicy }} env: - name: SKILLHUB_API_UPSTREAM diff --git a/charts/skillhub/templates/scanner-deployment.yaml b/charts/skillhub/templates/scanner-deployment.yaml index 6e91920d7..a3496fb9b 100644 --- a/charts/skillhub/templates/scanner-deployment.yaml +++ b/charts/skillhub/templates/scanner-deployment.yaml @@ -28,7 +28,7 @@ spec: {{- end }} containers: - name: scanner - image: {{ .Values.global.imageRegistry | default .Values.images.registry }}/skillhub-scanner:{{ .Values.images.tag | default (printf "v%s" .Chart.AppVersion) }} + image: {{ .Values.global.imageRegistry | default .Values.images.registry }}/skillhub-scanner:{{ .Values.scanner.image.tag | default .Values.images.tag | default (printf "v%s" .Chart.AppVersion) }} imagePullPolicy: {{ .Values.images.pullPolicy }} ports: - containerPort: {{ .Values.service.scannerPort }} diff --git a/charts/skillhub/values.yaml b/charts/skillhub/values.yaml index d2cd5d21a..015c913ef 100644 --- a/charts/skillhub/values.yaml +++ b/charts/skillhub/values.yaml @@ -64,6 +64,9 @@ ingress: # 应用组件配置 # ============================================================================ server: + # 组件级镜像标签(留空时使用全局 images.tag) + image: + tag: "" resources: requests: cpu: 500m @@ -111,6 +114,9 @@ server: periodSeconds: 15 web: + # 组件级镜像标签(留空时使用全局 images.tag) + image: + tag: "" resources: requests: cpu: 100m @@ -151,6 +157,9 @@ web: scanner: enabled: true + # 组件级镜像标签(留空时使用全局 images.tag) + image: + tag: "" resources: requests: cpu: 100m