feat(chart): 添加 SkillHub Helm Chart 部署方案

新增 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

Signed-off-by: jangrui <admin@jangrui.com>
This commit is contained in:
jangrui 2026-05-16 08:28:05 +08:00
parent 098616dcb6
commit dd1e87f1f3
19 changed files with 1615 additions and 0 deletions

146
.github/workflows/pr-helm-chart.yml vendored Normal file
View file

@ -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"

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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 }}

View file

@ -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 }}

View file

@ -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 }}

View file

@ -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 }}

View file

@ -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 }}

View file

@ -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 }}

View file

@ -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 }}

View file

@ -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 }}

View file

@ -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 }}

View file

@ -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 }}

View file

@ -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 }}

View file

@ -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 }}

View file

@ -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 }}

View file

@ -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 }}

312
charts/skillhub/values.yaml Normal file
View file

@ -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: ""