From de7464303da4df455b0e3370115d34e9ceb10221 Mon Sep 17 00:00:00 2001 From: bkellam Date: Mon, 20 Oct 2025 22:14:12 -0700 Subject: [PATCH 1/3] wip --- .github/workflows/test.yaml | 40 ++ .github/workflows/validate.yaml | 46 ++ README.md | 272 +++++++- charts/Chart.lock | 11 +- charts/Chart.yaml | 15 +- charts/README.md | 167 ++--- .../examples/minimal-installation/secret.yaml | 9 + .../examples/minimal-installation/values.yaml | 33 + charts/templates/NOTES.txt | 22 - charts/templates/_helpers.tpl | 103 ++- charts/templates/config.yaml | 2 +- charts/templates/deployment.yaml | 93 ++- charts/templates/ingress.yaml | 14 +- charts/templates/pdb.yaml | 12 +- charts/templates/pvc.yaml | 29 + charts/templates/service.yaml | 10 +- charts/templates/serviceaccount.yaml | 6 +- charts/tests/basic_test.yaml | 186 ++++++ charts/values.schema.json | 515 ++++----------- charts/values.yaml | 586 ++++++++++-------- 20 files changed, 1352 insertions(+), 819 deletions(-) create mode 100644 .github/workflows/test.yaml create mode 100644 .github/workflows/validate.yaml create mode 100644 charts/examples/minimal-installation/secret.yaml create mode 100644 charts/examples/minimal-installation/values.yaml delete mode 100644 charts/templates/NOTES.txt create mode 100644 charts/templates/pvc.yaml create mode 100644 charts/tests/basic_test.yaml diff --git a/.github/workflows/test.yaml b/.github/workflows/test.yaml new file mode 100644 index 0000000..e9e29bd --- /dev/null +++ b/.github/workflows/test.yaml @@ -0,0 +1,40 @@ +name: Unit Tests +on: + pull_request: + push: + branches: + - main + +jobs: + unit-tests: + name: Run Helm Unit Tests + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v4 + with: + fetch-depth: 0 + + - name: Set up Helm + uses: azure/setup-helm@v3 + with: + version: v3.13.2 + + - name: Install helm-unittest plugin + run: | + helm plugin install https://github.com/helm-unittest/helm-unittest.git --version v1.0.0 + + - name: Update dependencies + run: helm dependency update charts/sourcebot/ + + - name: Run unit tests + run: | + helm unittest charts/sourcebot/ --color --output-type JUnit --output-file test-results.xml + + - name: Publish Test Results + uses: EnricoMi/publish-unit-test-result-action@v2 + if: always() + with: + files: test-results.xml + check_name: "Helm Unit Test Results" + comment_title: "Helm Unit Test Results" diff --git a/.github/workflows/validate.yaml b/.github/workflows/validate.yaml new file mode 100644 index 0000000..33d1339 --- /dev/null +++ b/.github/workflows/validate.yaml @@ -0,0 +1,46 @@ +name: Validate Helm Chart +on: + pull_request: + paths: + - 'charts/**' + push: + branches: + - main + +jobs: + validate: + name: Validate Helm Chart + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v4 + with: + fetch-depth: 0 + + - name: Set up Helm + uses: azure/setup-helm@v3 + with: + version: v3.13.2 + + - name: Install helm-docs + run: | + cd /tmp + curl -L https://github.com/norwoodj/helm-docs/releases/download/v1.14.2/helm-docs_1.14.2_Linux_x86_64.tar.gz | tar xz + sudo mv helm-docs /usr/local/bin/ + + - name: Update dependencies + run: helm dependency update charts/ + + - name: Run helm lint + run: helm lint charts/ + + - name: Validate helm-docs + run: | + helm-docs charts/ + if [[ $(git diff --stat) != '' ]]; then + echo 'Chart documentation is out of date. Please run helm-docs and commit the changes.' + echo 'Git diff:' + git diff + exit 1 + fi + diff --git a/README.md b/README.md index d0f78ea..0f4b25d 100644 --- a/README.md +++ b/README.md @@ -1,2 +1,270 @@ -# sourcebot-k8s -Kubernetes config and Helm chart for Sourcebot +# Sourcebot Helm Chart + +![Version: 0.1.0](https://img.shields.io/badge/Version-0.1.0-informational?style=flat-square) ![Type: application](https://img.shields.io/badge/Type-application-informational?style=flat-square) ![AppVersion: v4.7.3](https://img.shields.io/badge/AppVersion-v4.7.3-informational?style=flat-square) + +The open source Sourcegraph alternative. Sourcebot gives you a powerful interface to search through all your repos and branches across multiple code hosts. + +**Homepage:** + +## TL;DR + +```bash +# Create a secret with your credentials +kubectl create secret generic sourcebot \ + --from-literal=postgresql-password=your-secure-password \ + --from-literal=redis-password=your-secure-password + +# Add the Helm repository +helm repo add sourcebot https://sourcebot-dev.github.io/sourcebot-helm-chart +helm repo update + +# Install Sourcebot +helm install sourcebot sourcebot/sourcebot \ + --set postgresql.auth.existingSecret=sourcebot \ + --set redis.auth.existingSecret=sourcebot +``` + +## Introduction + +This chart bootstraps a Sourcebot deployment on a Kubernetes cluster using the Helm package manager. + +By default, this chart deploys: +- Sourcebot application +- PostgreSQL database (via Bitnami subchart) +- Redis/Valkey cache (via Bitnami subchart) + +## Prerequisites + +- Kubernetes 1.19+ +- Helm 3.x +- PV provisioner support in the underlying infrastructure (for PostgreSQL and Sourcebot data persistence) + +## Installing the Chart + +### Quick Start + +See the [minimal installation example](./examples/minimal-installation/) for a complete quick start guide. + +### Step-by-Step Installation + +1. **Create a Secret** with your database and Redis passwords: + +```bash +kubectl create secret generic sourcebot \ + --from-literal=postgresql-password=your-secure-password \ + --from-literal=redis-password=your-secure-password +``` + +2. **Add the Helm Repository**: + +```bash +helm repo add sourcebot https://sourcebot-dev.github.io/sourcebot-helm-chart +helm repo update +``` + +3. **Install the Chart**: + +```bash +helm install sourcebot sourcebot/sourcebot \ + --set postgresql.auth.existingSecret=sourcebot \ + --set redis.auth.existingSecret=sourcebot +``` + +Or using a values file: + +```bash +helm install sourcebot sourcebot/sourcebot -f values.yaml +``` + +## Configuration + +### Database Configuration + +By default, PostgreSQL is deployed as a subchart. The chart automatically configures the connection using component-based environment variables (`DATABASE_HOST`, `DATABASE_USERNAME`, `DATABASE_PASSWORD`, etc.), which are assembled into `DATABASE_URL` by the application's entrypoint script. + +#### Using the Deployed PostgreSQL Subchart + +```yaml +postgresql: + deploy: true # Default + auth: + username: sourcebot + database: sourcebot + existingSecret: sourcebot + secretKeys: + userPasswordKey: postgresql-password + adminPasswordKey: postgresql-password + primary: + persistence: + enabled: true + size: 8Gi +``` + +#### Using an External PostgreSQL Instance + +```yaml +postgresql: + deploy: false + host: your-postgres-host.example.com + port: 5432 + auth: + username: sourcebot + database: sourcebot + existingSecret: sourcebot + secretKeys: + userPasswordKey: postgresql-password +``` + +### Redis Configuration + +Similar to PostgreSQL, Redis/Valkey can be deployed as a subchart or configured to use an external instance. + +#### Using the Deployed Redis Subchart + +```yaml +redis: + deploy: true # Default + auth: + username: default + existingSecret: sourcebot + existingSecretPasswordKey: redis-password +``` + +#### Using an External Redis Instance + +```yaml +redis: + deploy: false + host: your-redis-host.example.com + port: 6379 + auth: + username: default + existingSecret: sourcebot + existingSecretPasswordKey: redis-password +``` + +### Sourcebot Configuration + +Configure your code repositories and other settings: + +```yaml +sourcebot: + config: + $schema: https://raw.githubusercontent.com/sourcebot-dev/sourcebot/main/schemas/v3/index.json + connections: + github-repos: + type: github + token: + env: GITHUB_TOKEN + repos: + - owner/repo1 + - owner/repo2 + settings: + reindexIntervalMs: 86400000 # 24 hours +``` + +### Persistence Configuration + +By default, Sourcebot uses persistent storage to retain repository data and search indexes across pod restarts: + +```yaml +sourcebot: + persistence: + enabled: true # Default + size: 10Gi + storageClass: "" # Uses cluster default + accessModes: + - ReadWriteOnce +``` + +To use an existing PersistentVolumeClaim: + +```yaml +sourcebot: + persistence: + enabled: true + existingClaim: my-existing-pvc +``` + +To disable persistence (not recommended for production): + +```yaml +sourcebot: + persistence: + enabled: false +``` + +### Ingress Configuration + +Enable ingress to expose Sourcebot: + +```yaml +sourcebot: + ingress: + enabled: true + className: nginx + annotations: + cert-manager.io/cluster-issuer: letsencrypt-prod + hosts: + - host: sourcebot.example.com + paths: + - path: / + pathType: Prefix + tls: + - hosts: + - sourcebot.example.com + secretName: sourcebot-tls +``` + +### Resource Limits + +Set appropriate resource limits for production: + +```yaml +sourcebot: + resources: + requests: + cpu: 1000m + memory: 2Gi + limits: + cpu: 2000m + memory: 4Gi +``` + +## Examples + +Check out the [examples directory](./examples/) for complete configuration examples: + +- [Minimal Installation](./examples/minimal-installation/) - Basic setup with subcharts +- More examples coming soon! + +## Upgrading + +### To a New Version + +```bash +helm upgrade sourcebot sourcebot/sourcebot -f values.yaml +``` + +### Major Version Upgrades + +Always check the [CHANGELOG](../../CHANGELOG.md) for breaking changes before upgrading between major versions. + +## Uninstalling + +To uninstall/delete the `sourcebot` deployment: + +```bash +helm uninstall sourcebot +``` + +This removes all Kubernetes components associated with the chart but **preserves PersistentVolumeClaims (PVCs) by default**. This includes: +- `sourcebot-data` - Sourcebot repository data and search indexes +- `data-sourcebot-postgresql-0` - PostgreSQL data (if deployed) +- `valkey-data-sourcebot-redis-*` - Redis data (if deployed) + +To also remove all PVCs (⚠️ **this will delete all your data**): + +```bash +kubectl delete pvc -l app.kubernetes.io/instance=sourcebot +``` \ No newline at end of file diff --git a/charts/Chart.lock b/charts/Chart.lock index a16ac8f..8ed2986 100644 --- a/charts/Chart.lock +++ b/charts/Chart.lock @@ -1,6 +1,9 @@ dependencies: - name: postgresql - repository: https://charts.bitnami.com/bitnami - version: 16.7.27 -digest: sha256:ab3541612e8857c2d08d46f83cab79fa87b6ae6148e2d64d7d2b06729a520ce1 -generated: "2025-09-25T12:57:56.528976+01:00" + repository: oci://registry-1.docker.io/bitnamicharts + version: 16.4.9 +- name: valkey + repository: oci://registry-1.docker.io/bitnamicharts + version: 2.2.4 +digest: sha256:28d343e1aa714ee58c72ef16adba0ba062f34adb9b4003a3eb2b0d9bef4684cc +generated: "2025-10-20T14:50:50.647988-07:00" diff --git a/charts/Chart.yaml b/charts/Chart.yaml index 81682c8..651768d 100644 --- a/charts/Chart.yaml +++ b/charts/Chart.yaml @@ -2,8 +2,8 @@ apiVersion: v2 type: application name: sourcebot version: 0.1.0 -appVersion: v4.5.1 -description: The open source Sourcegraph alternative. Sourcebot gives you a powerful interface to search though all your repos and branches across multiple code hosts. +appVersion: v4.7.3 +description: Sourcebot is a self-hosted tool that helps you understand your codebase. icon: https://raw.githubusercontent.com/sourcebot-dev/sourcebot/ebf6721836b8f878d42bb8c1e844bdc7867a74fe/packages/web/public/logo_512.png keywords: - code-search @@ -15,6 +15,11 @@ sources: - https://github.com/sourcebot-dev/sourcebot/kubernetes/chart dependencies: - name: postgresql - version: 16.7.27 - repository: https://charts.bitnami.com/bitnami - condition: postgresql.enabled + version: 16.4.9 + repository: oci://registry-1.docker.io/bitnamicharts + condition: postgresql.deploy + - name: valkey + version: 2.2.4 + repository: oci://registry-1.docker.io/bitnamicharts + condition: redis.deploy + alias: redis \ No newline at end of file diff --git a/charts/README.md b/charts/README.md index 60da926..13f692a 100644 --- a/charts/README.md +++ b/charts/README.md @@ -1,8 +1,8 @@ # sourcebot -![Version: 0.1.0](https://img.shields.io/badge/Version-0.1.0-informational?style=flat-square) ![Type: application](https://img.shields.io/badge/Type-application-informational?style=flat-square) ![AppVersion: v4.5.1](https://img.shields.io/badge/AppVersion-v4.5.1-informational?style=flat-square) +![Version: 0.1.0](https://img.shields.io/badge/Version-0.1.0-informational?style=flat-square) ![Type: application](https://img.shields.io/badge/Type-application-informational?style=flat-square) ![AppVersion: v4.7.3](https://img.shields.io/badge/AppVersion-v4.7.3-informational?style=flat-square) -The open source Sourcegraph alternative. Sourcebot gives you a powerful interface to search though all your repos and branches across multiple code hosts. +Sourcebot is a self-hosted tool that helps you understand your codebase. **Homepage:** @@ -15,78 +15,103 @@ The open source Sourcegraph alternative. Sourcebot gives you a powerful interfac | Repository | Name | Version | |------------|------|---------| -| https://charts.bitnami.com/bitnami | postgresql | 16.7.27 | +| oci://registry-1.docker.io/bitnamicharts | postgresql | 16.4.9 | +| oci://registry-1.docker.io/bitnamicharts | redis(valkey) | 2.2.4 | ## Values | Key | Type | Default | Description | |-----|------|---------|-------------| -| additionalLabels | object | `{}` | Add extra labels to all resources. | -| affinity | object | `{}` | Set affinity rules for pod scheduling. Defaults to soft anti-affinity if not set. See: https://kubernetes.io/docs/concepts/scheduling-eviction/assign-pod-node/ | -| args | list | `[]` | Override the default arguments of the container. | -| command | list | `[]` | Override the default command of the container. | -| config | object | `{"$schema":"https://raw.githubusercontent.com/sourcebot-dev/sourcebot/main/schemas/v3/index.json","connections":{},"settings":{}}` | Configure Sourcebot-specific application settings. | -| containerSecurityContext | object | `{}` | Set the container-level security context. | -| database | object | `{}` | Configure the database secret by providing database.secretName and database.secretKey to use a Kubernetes secret. | -| envSecrets | list | `[]` | Set environment variables from Kubernetes secrets. | -| envs | list | `[]` | Set additional environment variables. | -| fullnameOverride | string | `""` | Override the full name of the chart. | -| image | object | `{"pullPolicy":"IfNotPresent","repository":"ghcr.io/sourcebot-dev/sourcebot"}` | Configure the container image. | -| image.pullPolicy | string | `"IfNotPresent"` | Image pull policy. | -| image.repository | string | `"ghcr.io/sourcebot-dev/sourcebot"` | Container image repository. | -| imagePullSecrets | list | `[]` | Configure image pull secrets for private registries. | -| ingress | object | `{"annotations":{},"className":"","enabled":false,"hosts":[],"tls":[]}` | Configure ingress for Sourcebot. | -| ingress.annotations | object | `{}` | Ingress annotations. | -| ingress.className | string | `""` | Ingress class name. | -| ingress.enabled | bool | `false` | Enable or disable ingress. | -| ingress.hosts | list | `[]` | List of hostnames and paths for ingress rules. The first host will be used as the default host. | -| ingress.tls | list | `[]` | TLS settings for ingress. | -| initContainers | list | `[]` | Configure init containers to run before the main container. | -| license | object | `{}` | Configure the enterprise license key secret by providing license.secretName and license.secretKey to use a Kubernetes secret. | -| livenessProbe | object | `{"failureThreshold":5,"httpGet":{"path":"/","port":"http"},"initialDelaySeconds":10,"periodSeconds":10}` | Liveness probe to check if the container is alive. | -| livenessProbe.failureThreshold | int | `5` | Number of consecutive failures before marking the container as unhealthy. | -| livenessProbe.httpGet | object | `{"path":"/","port":"http"}` | Http GET request to check if the container is alive. | -| livenessProbe.httpGet.path | string | `"/"` | Path to check. | -| livenessProbe.httpGet.port | string | `"http"` | Port to check. | -| livenessProbe.initialDelaySeconds | int | `10` | Initial delay before the first probe. | -| livenessProbe.periodSeconds | int | `10` | Frequency of the probe. | -| nameOverride | string | `""` | Override the name of the chart. | -| nodeSelector | object | `{}` | Set node selector constraints. See: https://kubernetes.io/docs/concepts/scheduling-eviction/assign-pod-node/#nodeselector | -| podAnnotations | object | `{}` | Add annotations to the pod metadata. | -| podDisruptionBudget | object | `{"enabled":true,"maxUnavailable":1,"minAvailable":1}` | Configure Pod Disruption Budget. | -| podDisruptionBudget.enabled | bool | `true` | Enable Pod Disruption Budget. | -| podDisruptionBudget.maxUnavailable | int | `1` | Maximum number of pods that can be unavailable. | -| podDisruptionBudget.minAvailable | int | `1` | Minimum number of pods that must be available. | -| podSecurityContext | object | `{}` | Set the pod-level security context. | -| postgresql | object | `{"enabled":false}` | Configure the Bitnami PostgreSQL sub-chart. See: https://artifacthub.io/packages/helm/bitnami/postgresql | -| priorityClassName | string | `""` | Set the priority class name for pods. See: https://kubernetes.io/docs/concepts/scheduling-eviction/pod-priority-preemption/ | -| readinessProbe | object | `{"failureThreshold":5,"httpGet":{"path":"/","port":"http"},"initialDelaySeconds":10,"periodSeconds":10}` | Readiness probe to check if the container is ready to serve traffic. | -| readinessProbe.failureThreshold | int | `5` | Number of consecutive failures before marking the container as not ready. | -| readinessProbe.httpGet | object | `{"path":"/","port":"http"}` | Http GET request to check if the container is ready. | -| readinessProbe.httpGet.path | string | `"/"` | Path to check. | -| readinessProbe.httpGet.port | string | `"http"` | Port to check. | -| readinessProbe.initialDelaySeconds | int | `10` | Initial delay before the first probe. | -| readinessProbe.periodSeconds | int | `10` | Frequency of the probe. | -| redis | object | `{}` | Configure the Redis secret by providing redis.secretName and redis.secretKey to use a Kubernetes secret. | -| replicaCount | int | `1` | Set the number of replicas for the deployment. | -| resources | object | `{}` | Configure resource requests and limits for the container. | -| service | object | `{"annotations":{},"containerPort":3000,"port":3000,"type":"ClusterIP"}` | Configure the Sourcebot Kubernetes service. | -| service.annotations | object | `{}` | Service annotations. | -| service.containerPort | int | `3000` | Internal container port. | -| service.port | int | `3000` | External service port. | -| service.type | string | `"ClusterIP"` | Type of the Kubernetes service (e.g., ClusterIP, NodePort, LoadBalancer). | -| serviceAccount | object | `{"annotations":{},"automount":false,"create":true,"name":""}` | Configure the ServiceAccount. | -| serviceAccount.annotations | object | `{}` | Add annotations to the ServiceAccount. | -| serviceAccount.automount | bool | `false` | Enable or disable automatic ServiceAccount mounting. | -| serviceAccount.create | bool | `true` | Create a new ServiceAccount. | -| serviceAccount.name | string | `""` | Use an existing ServiceAccount (if set). | -| startupProbe | object | `{"failureThreshold":30,"httpGet":{"path":"/","port":"http"},"periodSeconds":30}` | Startup probe to check if the container has started successfully. | -| startupProbe.failureThreshold | int | `30` | Number of seconds to wait before starting the probe. | -| startupProbe.httpGet | object | `{"path":"/","port":"http"}` | Http GET request to check if the container has started. | -| startupProbe.httpGet.path | string | `"/"` | Path to check. | -| startupProbe.httpGet.port | string | `"http"` | Port to check. | -| startupProbe.periodSeconds | int | `30` | Initial delay before the first probe. | -| tolerations | list | `[]` | Set tolerations for pod scheduling. See: https://kubernetes.io/docs/concepts/scheduling-eviction/taint-and-toleration/ | -| volumeMounts | list | `[]` | Define volume mounts for the container. See: https://kubernetes.io/docs/concepts/storage/volumes/ | -| volumes | list | `[]` | Define additional volumes. See: https://kubernetes.io/docs/concepts/storage/volumes/ | +| fullnameOverride | string | `""` | Override the full name of the deployed resources, defaults to a combination of the release name and the name for the selector labels | +| global.security.allowInsecureImages | bool | `true` | Allow insecure images to use bitnami legacy repository. Can be set to false if secure images are being used (Paid). | +| nameOverride | string | `""` | Override the name for the selector labels, defaults to the chart name | +| postgresql.auth.args | string | `""` | Additional database connection arguments | +| postgresql.auth.database | string | `"sourcebot"` | Database name | +| postgresql.auth.existingSecret | string | `""` | Use an existing secret for PostgreSQL password | +| postgresql.auth.password | string | `""` | Password for PostgreSQL user (only used if existingSecret is not set) | +| postgresql.auth.secretKeys | object | `{"adminPasswordKey":"postgresql-password","userPasswordKey":"postgresql-password"}` | Keys in the existing secret | +| postgresql.auth.username | string | `"sourcebot"` | Username to connect to PostgreSQL | +| postgresql.deploy | bool | `true` | Deploy PostgreSQL subchart. Set to false to use an external PostgreSQL instance. | +| postgresql.host | string | `""` | PostgreSQL host (only used if deploy is false) | +| postgresql.image.repository | string | `"bitnamilegacy/postgresql"` | Overwrite default repository of helm chart to point to non-paid bitnami images | +| postgresql.port | int | `5432` | PostgreSQL port | +| postgresql.primary.persistence.enabled | bool | `true` | | +| postgresql.primary.persistence.size | string | `"8Gi"` | | +| redis.auth.database | int | `0` | Redis database number | +| redis.auth.existingSecret | string | `""` | Use an existing secret for Redis password | +| redis.auth.existingSecretPasswordKey | string | `"redis-password"` | Key in the existing secret that contains the Redis password | +| redis.auth.password | string | `""` | Password for Redis user (only used if existingSecret is not set) | +| redis.auth.username | string | `"default"` | Username for Redis connection | +| redis.deploy | bool | `true` | Deploy Redis/Valkey subchart. Set to false to use an external Redis instance. | +| redis.host | string | `""` | Redis host (only used if deploy is false) | +| redis.image.repository | string | `"bitnamilegacy/valkey"` | Overwrite default repository of helm chart to point to non-paid bitnami images | +| redis.port | int | `6379` | Redis port | +| sourcebot.additionalEnv | list | `[]` | Set additional environment variables | +| sourcebot.additionalLabels | object | `{}` | Add extra labels to all resources | +| sourcebot.additionalPorts | list | `[]` | Configure additional ports to expose on the container and service | +| sourcebot.affinity | object | `{}` | Set affinity rules for pod scheduling Defaults to soft anti-affinity if not set See: https://kubernetes.io/docs/concepts/scheduling-eviction/assign-pod-node/ | +| sourcebot.args | list | `[]` | Override the default arguments of the container | +| sourcebot.command | list | `[]` | Override the default command of the container | +| sourcebot.config | object | `{"$schema":"https://raw.githubusercontent.com/sourcebot-dev/sourcebot/main/schemas/v3/index.json","connections":{},"settings":{}}` | Configure Sourcebot-specific application settings | +| sourcebot.containerSecurityContext | object | `{}` | Set the container-level security context | +| sourcebot.envSecrets | list | `[]` | Set environment variables from Kubernetes secrets | +| sourcebot.extraVolumeMounts | list | `[]` | Define volume mounts for the container See: https://kubernetes.io/docs/concepts/storage/volumes/ | +| sourcebot.extraVolumes | list | `[]` | Define additional volumes See: https://kubernetes.io/docs/concepts/storage/volumes/ | +| sourcebot.image.digest | string | `""` | Container image digest (used instead of tag if set) | +| sourcebot.image.pullPolicy | string | `"IfNotPresent"` | Image pull policy | +| sourcebot.image.pullSecrets | list | `[]` | Configure image pull secrets for private registries | +| sourcebot.image.repository | string | `"ghcr.io/sourcebot-dev/sourcebot"` | Container image repository | +| sourcebot.image.tag | string | `""` | Container image tag. Falls back to appVersion if not set. | +| sourcebot.ingress.annotations | object | `{}` | Ingress annotations | +| sourcebot.ingress.className | string | `""` | Ingress class name | +| sourcebot.ingress.enabled | bool | `false` | Enable or disable ingress | +| sourcebot.ingress.hosts | list | `[]` | List of hostnames and paths for ingress rules. The first host will be used as the default host. | +| sourcebot.ingress.tls | list | `[]` | TLS settings for ingress | +| sourcebot.initContainers | list | `[]` | Configure init containers to run before the main container | +| sourcebot.license.existingSecret | string | `""` | Use an existing secret for the license key | +| sourcebot.license.existingSecretKey | string | `"key"` | Key in the existing secret that contains the license key | +| sourcebot.license.value | string | `""` | License key value (or use existingSecret) | +| sourcebot.livenessProbe.failureThreshold | int | `5` | Number of consecutive failures before marking the container as unhealthy | +| sourcebot.livenessProbe.httpGet | object | `{"path":"/api/health","port":"http"}` | Http GET request to check if the container is alive | +| sourcebot.livenessProbe.httpGet.path | string | `"/api/health"` | Path to check | +| sourcebot.livenessProbe.httpGet.port | string | `"http"` | Port to check | +| sourcebot.livenessProbe.initialDelaySeconds | int | `20` | Initial delay before the first probe | +| sourcebot.livenessProbe.periodSeconds | int | `10` | Frequency of the probe | +| sourcebot.nodeSelector | object | `{}` | Set node selector constraints See: https://kubernetes.io/docs/concepts/scheduling-eviction/assign-pod-node/#nodeselector | +| sourcebot.persistence.accessModes | list | `["ReadWriteOnce"]` | Access modes for the persistent volume | +| sourcebot.persistence.annotations | object | `{}` | Annotations for the PersistentVolumeClaim | +| sourcebot.persistence.enabled | bool | `true` | Enable persistent storage for repository data and search indexes | +| sourcebot.persistence.existingClaim | string | `""` | Use an existing PersistentVolumeClaim (if set, other persistence settings are ignored) | +| sourcebot.persistence.size | string | `"10Gi"` | Size of the persistent volume | +| sourcebot.persistence.storageClass | string | `""` | Storage class name. If not set, uses the cluster default storage class | +| sourcebot.podAnnotations | object | `{}` | Add annotations to the pod metadata | +| sourcebot.podDisruptionBudget.enabled | bool | `true` | Enable Pod Disruption Budget | +| sourcebot.podDisruptionBudget.maxUnavailable | int | `1` | Maximum number of pods that can be unavailable | +| sourcebot.podDisruptionBudget.minAvailable | int | `1` | Minimum number of pods that must be available | +| sourcebot.podSecurityContext | object | `{}` | Set the pod-level security context | +| sourcebot.priorityClassName | string | `""` | Set the priority class name for pods See: https://kubernetes.io/docs/concepts/scheduling-eviction/pod-priority-preemption/ | +| sourcebot.readinessProbe.failureThreshold | int | `5` | Number of consecutive failures before marking the container as not ready | +| sourcebot.readinessProbe.httpGet | object | `{"path":"/api/health","port":"http"}` | Http GET request to check if the container is ready | +| sourcebot.readinessProbe.httpGet.path | string | `"/api/health"` | Path to check | +| sourcebot.readinessProbe.httpGet.port | string | `"http"` | Port to check | +| sourcebot.readinessProbe.initialDelaySeconds | int | `20` | Initial delay before the first probe | +| sourcebot.readinessProbe.periodSeconds | int | `10` | Frequency of the probe | +| sourcebot.replicaCount | int | `1` | Set the number of replicas for the deployment | +| sourcebot.resources | object | `{}` | Configure resource requests and limits for the container | +| sourcebot.service.annotations | object | `{}` | Service annotations | +| sourcebot.service.containerPort | int | `3000` | Internal container port | +| sourcebot.service.port | int | `3000` | External service port | +| sourcebot.service.type | string | `"ClusterIP"` | Type of the Kubernetes service (e.g., ClusterIP, NodePort, LoadBalancer) | +| sourcebot.serviceAccount.annotations | object | `{}` | Add annotations to the ServiceAccount | +| sourcebot.serviceAccount.automount | bool | `false` | Enable or disable automatic ServiceAccount mounting | +| sourcebot.serviceAccount.create | bool | `true` | Create a new ServiceAccount | +| sourcebot.serviceAccount.name | string | `""` | Use an existing ServiceAccount (if set) | +| sourcebot.startupProbe.failureThreshold | int | `30` | Number of seconds to wait before starting the probe | +| sourcebot.startupProbe.httpGet | object | `{"path":"/api/health","port":"http"}` | Http GET request to check if the container has started | +| sourcebot.startupProbe.httpGet.path | string | `"/api/health"` | Path to check | +| sourcebot.startupProbe.httpGet.port | string | `"http"` | Port to check | +| sourcebot.startupProbe.periodSeconds | int | `30` | Initial delay before the first probe | +| sourcebot.tolerations | list | `[]` | Set tolerations for pod scheduling See: https://kubernetes.io/docs/concepts/scheduling-eviction/taint-and-toleration/ | +---------------------------------------------- +Autogenerated from chart metadata using [helm-docs v1.14.2](https://github.com/norwoodj/helm-docs/releases/v1.14.2) diff --git a/charts/examples/minimal-installation/secret.yaml b/charts/examples/minimal-installation/secret.yaml new file mode 100644 index 0000000..7f5d2de --- /dev/null +++ b/charts/examples/minimal-installation/secret.yaml @@ -0,0 +1,9 @@ +apiVersion: v1 +kind: Secret +type: Opaque +metadata: + name: sourcebot +stringData: + postgresql-password: password + redis-password: password + diff --git a/charts/examples/minimal-installation/values.yaml b/charts/examples/minimal-installation/values.yaml new file mode 100644 index 0000000..1151bf7 --- /dev/null +++ b/charts/examples/minimal-installation/values.yaml @@ -0,0 +1,33 @@ +# Minimal Sourcebot Installation Example +# This configuration uses a single secret for all credentials and deploys PostgreSQL and Redis as subcharts. + +# Sourcebot application configuration +sourcebot: + image: + tag: "main" + + # Sourcebot application configuration + config: + $schema: https://raw.githubusercontent.com/sourcebot-dev/sourcebot/main/schemas/v3/index.json + connections: + # Example: GitHub repositories + github-repos: + type: github + repos: + - sourcebot-dev/sourcebot + +# PostgreSQL configuration +postgresql: + auth: + # Reference the existing secret created separately + existingSecret: sourcebot + secretKeys: + userPasswordKey: postgresql-password + adminPasswordKey: postgresql-password + +# Redis configuration +redis: + auth: + # Reference the existing secret created separately + existingSecret: sourcebot + existingSecretPasswordKey: redis-password diff --git a/charts/templates/NOTES.txt b/charts/templates/NOTES.txt deleted file mode 100644 index 2adfa79..0000000 --- a/charts/templates/NOTES.txt +++ /dev/null @@ -1,22 +0,0 @@ -1. Get the application URL by running these commands: -{{- if $.Values.ingress.enabled }} -{{- range $host := $.Values.ingress.hosts }} - {{- range .paths }} - http{{ if $.Values.ingress.tls }}s{{ end }}://{{ $host.host }}{{ .path }} - {{- end }} -{{- end }} -{{- else if contains "NodePort" $.Values.service.type }} - export NODE_PORT=$(kubectl get --namespace {{ $.Release.Namespace }} -o jsonpath="{.spec.ports[0].nodePort}" services {{ include "sourcebot.fullname" $ }}) - export NODE_IP=$(kubectl get nodes --namespace {{ $.Release.Namespace }} -o jsonpath="{.items[0].status.addresses[0].address}") - echo http://$NODE_IP:$NODE_PORT -{{- else if contains "LoadBalancer" $.Values.service.type }} - NOTE: It may take a few minutes for the LoadBalancer IP to be available. - You can watch its status by running 'kubectl get --namespace {{ $.Release.Namespace }} svc -w {{ include "sourcebot.fullname" $ }}' - export SERVICE_IP=$(kubectl get svc --namespace {{ $.Release.Namespace }} {{ include "sourcebot.fullname" $ }} --template "{{"{{ range (index .status.loadBalancer.ingress 0) }}{{.}}{{ end }}"}}") - echo http://$SERVICE_IP:{{ $.Values.service.port }} -{{- else if contains "ClusterIP" $.Values.service.type }} - export POD_NAME=$(kubectl get pods --namespace {{ $.Release.Namespace }} -l "app.kubernetes.io/name={{ include "sourcebot.name" $ }},app.kubernetes.io/instance={{ $.Release.Name }}" -o jsonpath="{.items[0].metadata.name}") - export CONTAINER_PORT=$(kubectl get pod --namespace {{ $.Release.Namespace }} $POD_NAME -o jsonpath="{.spec.containers[0].ports[0].containerPort}") - echo "Visit http://127.0.0.1:3000 to use Sourcebot after forwarding" - kubectl --namespace {{ $.Release.Namespace }} port-forward $POD_NAME 3000:$CONTAINER_PORT -{{- end }} diff --git a/charts/templates/_helpers.tpl b/charts/templates/_helpers.tpl index 7ff85c0..561824f 100644 --- a/charts/templates/_helpers.tpl +++ b/charts/templates/_helpers.tpl @@ -57,12 +57,12 @@ app.kubernetes.io/instance: {{ $.Release.Name }} Create the image to use for the container. */}} {{- define "sourcebot.image" -}} -{{- if $.Values.image.digest -}} -"{{ $.Values.image.repository }}@{{ $.Values.image.digest }}" -{{- else if $.Values.image.tag -}} -"{{ $.Values.image.repository }}:{{ $.Values.image.tag }}" +{{- if $.Values.sourcebot.image.digest -}} +"{{ $.Values.sourcebot.image.repository }}@{{ $.Values.sourcebot.image.digest }}" +{{- else if $.Values.sourcebot.image.tag -}} +"{{ $.Values.sourcebot.image.repository }}:{{ $.Values.sourcebot.image.tag }}" {{- else -}} -"{{ $.Values.image.repository }}:{{ $.Chart.AppVersion }}" +"{{ $.Values.sourcebot.image.repository }}:{{ $.Chart.AppVersion }}" {{- end -}} {{- end }} @@ -70,9 +70,96 @@ Create the image to use for the container. Create the name of the service account to use */}} {{- define "sourcebot.serviceAccountName" -}} -{{- if $.Values.serviceAccount.create }} -{{- default (include "sourcebot.fullname" $) $.Values.serviceAccount.name }} +{{- if $.Values.sourcebot.serviceAccount.create }} +{{- default (include "sourcebot.fullname" $) $.Values.sourcebot.serviceAccount.name }} {{- else }} -{{- default "default" $.Values.serviceAccount.name }} +{{- default "default" $.Values.sourcebot.serviceAccount.name }} {{- end }} {{- end }} + +{{/* +Return PostgreSQL hostname +*/}} +{{- define "sourcebot.postgresql.hostname" -}} +{{- if .Values.postgresql.host }} +{{- .Values.postgresql.host }} +{{- else if .Values.postgresql.deploy }} +{{- printf "%s-postgresql" (include "sourcebot.fullname" .) -}} +{{- end }} +{{- end }} + +{{/* +Return Redis hostname +*/}} +{{- define "sourcebot.redis.hostname" -}} +{{- if .Values.redis.host }} +{{- .Values.redis.host }} +{{- else if .Values.redis.deploy }} +{{- printf "%s-redis-primary" (include "sourcebot.fullname" .) -}} +{{- end }} +{{- end }} + +{{/* +Helper to get value or secret reference +Returns either a direct value or a valueFrom secretKeyRef +*/}} +{{- define "sourcebot.getValueOrSecret" -}} +{{- if .value.existingSecret }} +valueFrom: + secretKeyRef: + name: {{ .value.existingSecret }} + key: {{ .value.existingSecretKey }} +{{- else if .value.value }} +value: {{ .value.value | quote }} +{{- end }} +{{- end }} + +{{/* +Database environment variables +Builds DATABASE_HOST, DATABASE_USERNAME, DATABASE_PASSWORD, DATABASE_NAME, DATABASE_ARGS +These will be assembled into DATABASE_URL by the entrypoint.sh script +*/}} +{{- define "sourcebot.databaseEnv" -}} +- name: DATABASE_HOST + value: {{ include "sourcebot.postgresql.hostname" . | quote }} +- name: DATABASE_PORT + value: {{ .Values.postgresql.port | quote }} +- name: DATABASE_USERNAME + value: {{ .Values.postgresql.auth.username | quote }} +- name: DATABASE_PASSWORD +{{- if .Values.postgresql.auth.existingSecret }} + valueFrom: + secretKeyRef: + name: {{ .Values.postgresql.auth.existingSecret }} + key: {{ .Values.postgresql.auth.secretKeys.userPasswordKey }} +{{- else }} + value: {{ required "postgresql.auth.password is required when existingSecret is not set" .Values.postgresql.auth.password | quote }} +{{- end }} +- name: DATABASE_NAME + value: {{ .Values.postgresql.auth.database | quote }} +{{- if .Values.postgresql.auth.args }} +- name: DATABASE_ARGS + value: {{ .Values.postgresql.auth.args | quote }} +{{- end }} +{{- end -}} + +{{/* +Redis environment variables +Builds REDIS_URL connection string +*/}} +{{- define "sourcebot.redisEnv" -}} +{{- $redisPassword := "" -}} +{{- if .Values.redis.auth.existingSecret }} +- name: REDIS_PASSWORD + valueFrom: + secretKeyRef: + name: {{ .Values.redis.auth.existingSecret }} + key: {{ .Values.redis.auth.existingSecretPasswordKey }} +- name: REDIS_URL + value: "redis://{{ .Values.redis.auth.username }}:$(REDIS_PASSWORD)@{{ include "sourcebot.redis.hostname" . }}:{{ .Values.redis.port }}/{{ .Values.redis.auth.database }}" +{{- else }} +{{- $redisPassword = required "redis.auth.password is required when existingSecret is not set" .Values.redis.auth.password -}} +- name: REDIS_URL + value: "redis://{{ .Values.redis.auth.username }}:{{ $redisPassword }}@{{ include "sourcebot.redis.hostname" . }}:{{ .Values.redis.port }}/{{ .Values.redis.auth.database }}" +{{- end }} +{{- end -}} diff --git a/charts/templates/config.yaml b/charts/templates/config.yaml index 43cbf16..7a65e1b 100644 --- a/charts/templates/config.yaml +++ b/charts/templates/config.yaml @@ -7,4 +7,4 @@ metadata: {{- include "sourcebot.labels" $ | nindent 4 }} data: config.json: | - {{- toJson $.Values.config | nindent 4 }} + {{- toJson $.Values.sourcebot.config | nindent 4 }} diff --git a/charts/templates/deployment.yaml b/charts/templates/deployment.yaml index 732f9e7..e3e1250 100644 --- a/charts/templates/deployment.yaml +++ b/charts/templates/deployment.yaml @@ -6,104 +6,94 @@ metadata: labels: {{- include "sourcebot.labels" $ | nindent 4 }} spec: - replicas: {{ $.Values.replicaCount }} + replicas: {{ $.Values.sourcebot.replicaCount }} selector: matchLabels: {{- include "sourcebot.selectorLabels" $ | nindent 6 }} template: metadata: - {{- with $.Values.podAnnotations }} annotations: + checksum/config: {{ include (print $.Template.BasePath "/config.yaml") . | sha256sum }} + {{- with $.Values.sourcebot.podAnnotations }} {{- toYaml . | nindent 8 }} - {{- end }} + {{- end }} labels: {{- include "sourcebot.labels" $ | nindent 8 }} - {{- with $.Values.podLabels }} - {{- toYaml . | nindent 8 }} - {{- end }} spec: - {{- with $.Values.imagePullSecrets }} + {{- with $.Values.sourcebot.image.pullSecrets }} imagePullSecrets: {{- toYaml . | nindent 8 }} {{- end }} serviceAccountName: {{ include "sourcebot.serviceAccountName" $ }} - {{- with $.Values.podSecurityContext }} + {{- with $.Values.sourcebot.podSecurityContext }} securityContext: {{- toYaml . | nindent 8 }} {{- end }} - {{- with $.Values.initContainers }} + {{- with $.Values.sourcebot.initContainers }} initContainers: {{- toYaml . | nindent 8 }} {{- end }} containers: - name: {{ .Chart.Name }} - {{- with $.Values.containerSecurityContext }} + {{- with $.Values.sourcebot.containerSecurityContext }} securityContext: {{- toYaml . | nindent 12 }} {{- end }} image: {{ include "sourcebot.image" $ }} - imagePullPolicy: {{ $.Values.image.pullPolicy }} - {{- with $.Values.command }} + imagePullPolicy: {{ $.Values.sourcebot.image.pullPolicy }} + {{- with $.Values.sourcebot.command }} command: {{ toYaml . | nindent 12 }} {{- end }} - {{- with $.Values.args }} + {{- with $.Values.sourcebot.args }} args: {{ toYaml . | nindent 12 }} {{- end }} env: - {{- if and $.Values.ingress.enabled (gt (len $.Values.ingress.hosts) 0) }} + {{- if and $.Values.sourcebot.ingress.enabled (gt (len $.Values.sourcebot.ingress.hosts) 0) }} - name: AUTH_URL - value: {{ (index $.Values.ingress.hosts 0).host | printf "https://%s" }} + value: {{ (index $.Values.sourcebot.ingress.hosts 0).host | printf "https://%s" }} {{- end }} - name: CONFIG_PATH value: /etc/sourcebot/config.json - {{- if $.Values.license }} + {{- if or $.Values.sourcebot.license.value $.Values.sourcebot.license.existingSecret }} - name: SOURCEBOT_EE_LICENSE_KEY - valueFrom: - secretKeyRef: - name: {{ $.Values.license.secretName }} - key: {{ $.Values.license.secretKey }} + {{- with (include "sourcebot.getValueOrSecret" (dict "value" $.Values.sourcebot.license)) }} + {{- . | nindent 14 }} + {{- end }} {{- end }} - {{- if $.Values.database }} - - name: DATABASE_URL - valueFrom: - secretKeyRef: - name: {{ $.Values.database.secretName }} - key: {{ $.Values.database.secretKey }} - {{- end }} - {{- if $.Values.redis }} - - name: REDIS_URL - valueFrom: - secretKeyRef: - name: {{ $.Values.redis.secretName }} - key: {{ $.Values.redis.secretKey }} - {{- end }} - {{- range $.Values.envSecrets }} + {{- include "sourcebot.databaseEnv" . | nindent 12 }} + {{- include "sourcebot.redisEnv" . | nindent 12 }} + {{- range $.Values.sourcebot.envSecrets }} - name: {{ .envName }} valueFrom: secretKeyRef: name: {{ .secretName }} key: {{ .secretKey }} {{- end }} - {{- with $.Values.envs }} + {{- with $.Values.sourcebot.additionalEnv }} {{- toYaml . | nindent 12 }} {{- end }} ports: - name: http - containerPort: {{ $.Values.service.containerPort }} + containerPort: {{ $.Values.sourcebot.service.containerPort }} protocol: TCP - {{- with $.Values.livenessProbe }} + {{- range $.Values.sourcebot.additionalPorts }} + - name: {{ .name }} + containerPort: {{ .containerPort }} + protocol: {{ .protocol | default "TCP" }} + {{- end }} + {{- with $.Values.sourcebot.livenessProbe }} livenessProbe: {{- toYaml . | nindent 12 }} {{- end }} - {{- with $.Values.readinessProbe }} + {{- with $.Values.sourcebot.readinessProbe }} readinessProbe: {{- toYaml . | nindent 12 }} {{- end }} - {{- with $.Values.startupProbe }} + {{- with $.Values.sourcebot.startupProbe }} startupProbe: {{- toYaml . | nindent 12 }} {{- end }} - {{- with $.Values.resources }} + {{- with $.Values.sourcebot.resources }} resources: {{- toYaml . | nindent 12 }} {{- end }} @@ -112,10 +102,14 @@ spec: mountPath: /etc/sourcebot/config.json subPath: config.json readOnly: true - {{- with $.Values.volumeMounts }} + {{- if .Values.sourcebot.persistence.enabled }} + - name: sourcebot-data + mountPath: /data + {{- end }} + {{- with $.Values.sourcebot.extraVolumeMounts }} {{- toYaml . | nindent 12 }} {{- end }} - {{- with $.Values.priorityClassName }} + {{- with $.Values.sourcebot.priorityClassName }} priorityClassName: {{ . }} {{- end }} volumes: @@ -125,18 +119,23 @@ spec: items: - key: config.json path: config.json - {{- with $.Values.volumes }} + {{- if .Values.sourcebot.persistence.enabled }} + - name: sourcebot-data + persistentVolumeClaim: + claimName: {{ .Values.sourcebot.persistence.existingClaim | default (printf "%s-data" (include "sourcebot.fullname" .)) }} + {{- end }} + {{- with $.Values.sourcebot.extraVolumes }} {{- toYaml . | nindent 8 }} {{- end }} - {{- with $.Values.nodeSelector }} + {{- with $.Values.sourcebot.nodeSelector }} nodeSelector: {{- toYaml . | nindent 8 }} {{- end }} - {{- with $.Values.affinity }} + {{- with $.Values.sourcebot.affinity }} affinity: {{- toYaml . | nindent 8 }} {{- end }} - {{- with $.Values.tolerations }} + {{- with $.Values.sourcebot.tolerations }} tolerations: {{- toYaml . | nindent 8 }} {{- end }} diff --git a/charts/templates/ingress.yaml b/charts/templates/ingress.yaml index e0f563e..7157ccc 100644 --- a/charts/templates/ingress.yaml +++ b/charts/templates/ingress.yaml @@ -1,4 +1,4 @@ -{{- if $.Values.ingress.enabled -}} +{{- if $.Values.sourcebot.ingress.enabled -}} --- apiVersion: networking.k8s.io/v1 kind: Ingress @@ -6,17 +6,17 @@ metadata: name: {{ include "sourcebot.fullname" $ }} labels: {{- include "sourcebot.labels" $ | nindent 4 }} - {{- with $.Values.ingress.annotations }} + {{- with $.Values.sourcebot.ingress.annotations }} annotations: {{- toYaml . | nindent 4 }} {{- end }} spec: - {{- with $.Values.ingress.className }} + {{- with $.Values.sourcebot.ingress.className }} ingressClassName: {{ . }} {{- end }} - {{- if $.Values.ingress.tls }} + {{- if $.Values.sourcebot.ingress.tls }} tls: - {{- range $.Values.ingress.tls }} + {{- range $.Values.sourcebot.ingress.tls }} - hosts: {{- range .hosts }} - {{ . | quote }} @@ -25,7 +25,7 @@ spec: {{- end }} {{- end }} rules: - {{- range $.Values.ingress.hosts }} + {{- range $.Values.sourcebot.ingress.hosts }} - host: {{ .host | quote }} http: paths: @@ -38,7 +38,7 @@ spec: service: name: {{ include "sourcebot.fullname" $ }} port: - number: {{ $.Values.service.port }} + number: {{ $.Values.sourcebot.service.port }} {{- end }} {{- end }} {{- end }} diff --git a/charts/templates/pdb.yaml b/charts/templates/pdb.yaml index 1cc85f5..804b8cc 100644 --- a/charts/templates/pdb.yaml +++ b/charts/templates/pdb.yaml @@ -1,16 +1,16 @@ -{{- if and $.Values.podDisruptionBudget.enabled (gt (int $.Values.replicaCount) 1) }} -apiVersion: {{ include "common.capabilities.policy.apiVersion" . }} +{{- if and $.Values.sourcebot.podDisruptionBudget.enabled (gt (int $.Values.sourcebot.replicaCount) 1) }} +apiVersion: policy/v1 kind: PodDisruptionBudget metadata: name: {{ include "sourcebot.fullname" $ }} labels: {{- include "sourcebot.labels" $ | nindent 4 }} spec: - {{- if $.Values.podDisruptionBudget.minAvailable }} - minAvailable: {{ $.Values.podDisruptionBudget.minAvailable }} + {{- if $.Values.sourcebot.podDisruptionBudget.minAvailable }} + minAvailable: {{ $.Values.sourcebot.podDisruptionBudget.minAvailable }} {{- end }} - {{- if $.Values.podDisruptionBudget.maxUnavailable }} - maxUnavailable: {{ $.Values.podDisruptionBudget.maxUnavailable }} + {{- if $.Values.sourcebot.podDisruptionBudget.maxUnavailable }} + maxUnavailable: {{ $.Values.sourcebot.podDisruptionBudget.maxUnavailable }} {{- end }} selector: matchLabels: diff --git a/charts/templates/pvc.yaml b/charts/templates/pvc.yaml new file mode 100644 index 0000000..e40dc1c --- /dev/null +++ b/charts/templates/pvc.yaml @@ -0,0 +1,29 @@ +{{- if and .Values.sourcebot.persistence.enabled (not .Values.sourcebot.persistence.existingClaim) }} +--- +apiVersion: v1 +kind: PersistentVolumeClaim +metadata: + name: {{ include "sourcebot.fullname" . }}-data + labels: + {{- include "sourcebot.labels" . | nindent 4 }} + {{- with .Values.sourcebot.persistence.annotations }} + annotations: + {{- toYaml . | nindent 4 }} + {{- end }} +spec: + accessModes: + {{- range .Values.sourcebot.persistence.accessModes }} + - {{ . }} + {{- end }} + resources: + requests: + storage: {{ .Values.sourcebot.persistence.size }} + {{- if .Values.sourcebot.persistence.storageClass }} + {{- if (eq "-" .Values.sourcebot.persistence.storageClass) }} + storageClassName: "" + {{- else }} + storageClassName: {{ .Values.sourcebot.persistence.storageClass }} + {{- end }} + {{- end }} +{{- end }} + diff --git a/charts/templates/service.yaml b/charts/templates/service.yaml index 8c9c5bb..3e004b0 100644 --- a/charts/templates/service.yaml +++ b/charts/templates/service.yaml @@ -6,11 +6,17 @@ metadata: labels: {{- include "sourcebot.labels" $ | nindent 4 }} spec: - type: {{ $.Values.service.type }} + type: {{ $.Values.sourcebot.service.type }} ports: - - port: {{ $.Values.service.port }} + - port: {{ $.Values.sourcebot.service.port }} targetPort: http protocol: TCP name: http + {{- range $.Values.sourcebot.additionalPorts }} + - port: {{ .port }} + targetPort: {{ .name }} + protocol: {{ .protocol | default "TCP" }} + name: {{ .name }} + {{- end }} selector: {{- include "sourcebot.selectorLabels" $ | nindent 4 }} diff --git a/charts/templates/serviceaccount.yaml b/charts/templates/serviceaccount.yaml index fcf0a21..b0c117a 100644 --- a/charts/templates/serviceaccount.yaml +++ b/charts/templates/serviceaccount.yaml @@ -1,4 +1,4 @@ -{{- if $.Values.serviceAccount.create -}} +{{- if $.Values.sourcebot.serviceAccount.create -}} --- apiVersion: v1 kind: ServiceAccount @@ -6,9 +6,9 @@ metadata: name: {{ include "sourcebot.serviceAccountName" . }} labels: {{- include "sourcebot.labels" . | nindent 4 }} - {{- with $.Values.serviceAccount.annotations }} + {{- with $.Values.sourcebot.serviceAccount.annotations }} annotations: {{- toYaml . | nindent 4 }} {{- end }} -automountServiceAccountToken: {{ $.Values.serviceAccount.automount }} +automountServiceAccountToken: {{ $.Values.sourcebot.serviceAccount.automount }} {{- end }} diff --git a/charts/tests/basic_test.yaml b/charts/tests/basic_test.yaml new file mode 100644 index 0000000..023da4c --- /dev/null +++ b/charts/tests/basic_test.yaml @@ -0,0 +1,186 @@ +suite: test basic chart functionality +templates: + - deployment.yaml + - service.yaml + - serviceaccount.yaml + - config.yaml + - pvc.yaml +tests: + - it: should render with default values + values: + - ../values.lint.yaml + asserts: + # Test that deployment is created + - hasDocuments: + count: 1 + template: deployment.yaml + - isKind: + of: Deployment + template: deployment.yaml + + # Test that service is created + - hasDocuments: + count: 1 + template: service.yaml + - isKind: + of: Service + template: service.yaml + + # Test that serviceaccount is created + - hasDocuments: + count: 1 + template: serviceaccount.yaml + - isKind: + of: ServiceAccount + template: serviceaccount.yaml + + # Test that configmap is created + - hasDocuments: + count: 1 + template: config.yaml + - isKind: + of: ConfigMap + template: config.yaml + + # Test that PVC is created (persistence enabled by default) + - hasDocuments: + count: 1 + template: pvc.yaml + - isKind: + of: PersistentVolumeClaim + template: pvc.yaml + + - it: should set correct app labels + values: + - ../values.lint.yaml + asserts: + - equal: + path: metadata.labels["app.kubernetes.io/name"] + value: sourcebot + template: deployment.yaml + - equal: + path: metadata.labels["app.kubernetes.io/instance"] + value: RELEASE-NAME + template: deployment.yaml + - equal: + path: metadata.labels["app.kubernetes.io/managed-by"] + value: Helm + template: deployment.yaml + + - it: should use correct image when tag is specified + values: + - ../values.lint.yaml + set: + sourcebot.image.tag: "v1.2.3" + asserts: + - matchRegex: + path: spec.template.spec.containers[0].image + pattern: "ghcr.io/sourcebot-dev/sourcebot:v1.2.3$" + template: deployment.yaml + + - it: should use digest when specified + values: + - ../values.lint.yaml + set: + sourcebot.image.digest: "sha256:abcdef123456" + asserts: + - matchRegex: + path: spec.template.spec.containers[0].image + pattern: "ghcr.io/sourcebot-dev/sourcebot@sha256:abcdef123456$" + template: deployment.yaml + + - it: should set replica count + values: + - ../values.lint.yaml + set: + sourcebot.replicaCount: 3 + asserts: + - equal: + path: spec.replicas + value: 3 + template: deployment.yaml + + - it: should mount persistence volume when enabled + values: + - ../values.lint.yaml + asserts: + - contains: + path: spec.template.spec.volumes + content: + name: sourcebot-data + persistentVolumeClaim: + claimName: RELEASE-NAME-sourcebot-data + template: deployment.yaml + - contains: + path: spec.template.spec.containers[0].volumeMounts + content: + name: sourcebot-data + mountPath: /data + template: deployment.yaml + + - it: should not create PVC when persistence is disabled + values: + - ../values.lint.yaml + set: + sourcebot.persistence.enabled: false + asserts: + - hasDocuments: + count: 0 + template: pvc.yaml + + - it: should not mount volume when persistence is disabled + values: + - ../values.lint.yaml + set: + sourcebot.persistence.enabled: false + asserts: + - notContains: + path: spec.template.spec.volumes + content: + name: sourcebot-data + template: deployment.yaml + + - it: should use existingClaim when specified + values: + - ../values.lint.yaml + set: + sourcebot.persistence.existingClaim: "my-existing-pvc" + asserts: + # PVC should not be created + - hasDocuments: + count: 0 + template: pvc.yaml + # But volume should reference the existing claim + - contains: + path: spec.template.spec.volumes + content: + name: sourcebot-data + persistentVolumeClaim: + claimName: my-existing-pvc + template: deployment.yaml + + - it: should set service type correctly + values: + - ../values.lint.yaml + set: + sourcebot.service.type: "LoadBalancer" + asserts: + - equal: + path: spec.type + value: LoadBalancer + template: service.yaml + + - it: should include config in configmap + values: + - ../values.lint.yaml + set: + sourcebot.config: + $schema: "https://example.com/schema.json" + connections: + test: + type: github + asserts: + - isNotEmpty: + path: data["config.json"] + template: config.yaml + diff --git a/charts/values.schema.json b/charts/values.schema.json index 10832e6..471d611 100644 --- a/charts/values.schema.json +++ b/charts/values.schema.json @@ -4,455 +4,196 @@ "additionalProperties": false, "type": "object", "properties": { + "global": { + "type": "object", + "properties": { + "security": { + "type": "object", + "properties": { + "allowInsecureImages": { + "type": "boolean" + } + } + } + } + }, "nameOverride": { "type": "string" }, "fullnameOverride": { "type": "string" }, - "replicaCount": { - "type": "integer", - "minimum": 1 - }, - "image": { + "sourcebot": { "type": "object", "properties": { - "repository": { - "type": "string" + "replicaCount": { + "type": "integer", + "minimum": 1 }, - "tag": { - "type": "string" + "image": { + "type": "object", + "properties": { + "repository": { + "type": "string" + }, + "tag": { + "type": "string" + }, + "digest": { + "type": "string" + }, + "pullPolicy": { + "type": "string", + "enum": ["Always", "IfNotPresent", "Never"] + }, + "pullSecrets": { + "type": "array" + } + } }, - "digest": { - "type": "string" + "command": { + "type": "array" }, - "pullPolicy": { - "type": "string", - "enum": [ - "Always", - "IfNotPresent", - "Never" - ] - } - }, - "required": [ - "repository" - ] - }, - "imagePullSecrets": { - "type": "array", - "items": { - "type": "string" - } - }, - "command": { - "type": "array", - "items": { - "type": "string" - } - }, - "args": { - "type": "array", - "items": { - "type": "string" - } - }, - "database": { - "type": "object" - }, - "redis": { - "type": "object" - }, - "license": { - "type": "object" - }, - "envSecrets": { - "type": "array", - "items": { - "type": "object", - "properties": { - "secretName": { - "type": "string" - }, - "secretKey": { - "type": "string" - }, - "envName": { - "type": "string" - } + "args": { + "type": "array" }, - "required": [ - "secretName", - "secretKey", - "envName" - ] - } - }, - "envs": { - "type": "array", - "items": { - "type": "object", - "properties": { - "name": { - "type": "string" - }, - "value": { - "type": "string" - } + "license": { + "type": "object" }, - "required": [ - "name", - "value" - ] - } - }, - "config": { - "type": "object", - "properties": { - "connections": { + "envSecrets": { + "type": "array" + }, + "additionalEnv": { + "type": "array" + }, + "config": { "type": "object" }, - "settings": { + "serviceAccount": { "type": "object" - } - } - }, - "serviceAccount": { - "type": "object", - "properties": { - "create": { - "type": "boolean" }, - "name": { - "type": "string" + "podSecurityContext": { + "type": "object" }, - "annotations": { + "containerSecurityContext": { "type": "object" }, - "automount": { - "type": "boolean" - } - } - }, - "podSecurityContext": { - "type": "object", - "properties": { - "runAsUser": { - "type": "integer" + "service": { + "type": "object" }, - "runAsGroup": { - "type": "integer" + "additionalPorts": { + "type": "array" }, - "runAsNonRoot": { - "type": "boolean" + "ingress": { + "type": "object" }, - "fsGroup": { - "type": "integer" - } - } - }, - "containerSecurityContext": { - "type": "object", - "properties": { - "allowPrivilegeEscalation": { - "type": "boolean" + "initContainers": { + "type": "array" }, - "privileged": { - "type": "boolean" + "resources": { + "type": "object" }, - "readOnlyRootFilesystem": { - "type": "boolean" + "livenessProbe": { + "type": "object" }, - "runAsUser": { - "type": "integer" + "readinessProbe": { + "type": "object" }, - "runAsGroup": { - "type": "integer" + "startupProbe": { + "type": "object" }, - "capabilities": { - "type": "object", - "properties": { - "drop": { - "type": "array", - "items": { - "type": "string" - } - } - } + "extraVolumes": { + "type": "array" }, - "seccompProfile": { - "type": "object", - "properties": { - "type": { - "type": "string", - "enum": [ - "RuntimeDefault", - "Unconfined", - "Localhost" - ] - } - } - } - } - }, - "service": { - "type": "object", - "properties": { - "type": { - "type": "string", - "enum": [ - "ClusterIP", - "NodePort", - "LoadBalancer", - "ExternalName" - ] - }, - "containerPort": { - "type": "integer" + "extraVolumeMounts": { + "type": "array" }, - "port": { - "type": "integer" + "podDisruptionBudget": { + "type": "object" }, - "annotations": { + "podAnnotations": { "type": "object" - } - } - }, - "ingress": { - "type": "object", - "properties": { - "enabled": { - "type": "boolean" }, - "className": { - "type": "string" + "additionalLabels": { + "type": "object" }, - "annotations": { + "nodeSelector": { "type": "object" }, - "hosts": { - "type": "array", - "items": { - "type": "object", - "properties": { - "host": { - "type": "string" - }, - "paths": { - "type": "array", - "items": { - "type": "object", - "properties": { - "path": { - "type": "string" - }, - "pathType": { - "type": "string", - "enum": [ - "ImplementationSpecific", - "Exact", - "Prefix" - ] - } - } - } - } - } - }, - "tls": { - "type": "object", - "properties": { - "secretName": { - "type": "string" - }, - "hosts": { - "type": "array", - "items": { - "type": "string" - } - } - }, - "required": [ - "secretName", - "hosts" - ] - } - } - } - }, - "initContainers": { - "type": "array", - "items": { - "type": "object", - "properties": { - "name": { - "type": "string" - }, - "image": { - "type": "string" - }, - "args": { - "type": "array", - "items": { - "type": "string" - } - } - } - } - }, - "resources": { - "type": "object", - "properties": { - "limits": { + "tolerations": { + "type": "array" + }, + "affinity": { + "type": "object" + }, + "priorityClassName": { + "type": "string" + }, + "persistence": { "type": "object", "properties": { - "cpu": { - "type": "string" + "enabled": { + "type": "boolean" }, - "memory": { + "existingClaim": { "type": "string" - } - } - }, - "requests": { - "type": "object", - "properties": { - "cpu": { + }, + "size": { "type": "string" }, - "memory": { + "storageClass": { "type": "string" + }, + "accessModes": { + "type": "array" + }, + "annotations": { + "type": "object" } } } } }, - "livenessProbe": { - "$ref": "#/definitions/probe" - }, - "readinessProbe": { - "$ref": "#/definitions/probe" - }, - "startupProbe": { - "$ref": "#/definitions/probe" - }, - "volumes": { - "type": "array", - "items": { - "type": "object" - } - }, - "volumeMounts": { - "type": "array", - "items": { - "type": "object", - "properties": { - "name": { - "type": "string" - }, - "mountPath": { - "type": "string" - }, - "readOnly": { - "type": "boolean" - } - } - } - }, - "podDisruptionBudget": { + "postgresql": { "type": "object", "properties": { - "enabled": { + "deploy": { "type": "boolean" }, - "minAvailable": { - "type": [ - "integer", - "string" - ] - }, - "maxUnavailable": { - "type": [ - "integer", - "string" - ] + "host": { + "type": "string" + }, + "port": { + "type": "integer" + }, + "auth": { + "type": "object" + }, + "image": { + "type": "object" + }, + "primary": { + "type": "object" } } }, - "podAnnotations": { - "type": "object" - }, - "additionalLabels": { - "type": "object" - }, - "nodeSelector": { - "type": "object" - }, - "tolerations": { - "type": "array", - "items": { - "type": "object" - } - }, - "affinity": { - "type": "object" - }, - "priorityClassName": { - "type": "string" - }, - "postgresql": { + "redis": { "type": "object", "properties": { - "enabled": { + "deploy": { "type": "boolean" - } - }, - "required": [ - "enabled" - ] - } - }, - "definitions": { - "probe": { - "type": "object", - "properties": { - "httpGet": { - "type": "object", - "properties": { - "path": { - "type": "string" - }, - "port": { - "type": [ - "string", - "integer" - ] - } - }, - "required": [ - "path", - "port" - ] }, - "initialDelaySeconds": { - "type": "integer", - "minimum": 0 - }, - "periodSeconds": { - "type": "integer", - "minimum": 1 + "host": { + "type": "string" }, - "timeoutSeconds": { - "type": "integer", - "minimum": 1 + "port": { + "type": "integer" }, - "successThreshold": { - "type": "integer", - "minimum": 1 + "auth": { + "type": "object" }, - "failureThreshold": { - "type": "integer", - "minimum": 1 + "image": { + "type": "object" } } } diff --git a/charts/values.yaml b/charts/values.yaml index e095fc9..61da367 100644 --- a/charts/values.yaml +++ b/charts/values.yaml @@ -1,260 +1,338 @@ -# -- Sourcebot Helm Chart Values +# Sourcebot Helm Chart Values -# -- Override the name of the chart. +global: + security: + # -- Allow insecure images to use bitnami legacy repository. Can be set to false if secure images are being used (Paid). + allowInsecureImages: true + +# -- Override the name for the selector labels, defaults to the chart name nameOverride: "" -# -- Override the full name of the chart. +# -- Override the full name of the deployed resources, defaults to a combination of the release name and the name for the selector labels fullnameOverride: "" -# -- Set the number of replicas for the deployment. -replicaCount: 1 - -# -- Configure the container image. -image: - # -- Container image repository. - repository: ghcr.io/sourcebot-dev/sourcebot - # -- Container image tag. - # tag: "" - # -- Container image digest (used instead of tag if set). - # digest: "" - # -- Image pull policy. - pullPolicy: IfNotPresent - -# -- Configure image pull secrets for private registries. -imagePullSecrets: [] - -# -- Override the default command of the container. -command: [] - -# -- Override the default arguments of the container. -args: [] - -# -- Configure the database secret by providing database.secretName and database.secretKey to use a Kubernetes secret. -database: {} -# secretName: sourcebot-database-url -# secretKey: url - -# -- Configure the Redis secret by providing redis.secretName and redis.secretKey to use a Kubernetes secret. -redis: {} -# secretName: sourcebot-redis-url -# secretKey: url - -# -- Configure the enterprise license key secret by providing license.secretName and license.secretKey to use a Kubernetes secret. -license: {} -# secretName: sourcebot-ee-license-key -# secretKey: key - -# -- Set environment variables from Kubernetes secrets. -envSecrets: [] -# - secretName: sourcebot-github-token -# secretKey: token -# envName: GITHUB_TOKEN - -# -- Set additional environment variables. -envs: [] -# # Disable sourcebot telemetry. -# - name: SOURCEBOT_TELEMETRY_DISABLED -# value: "1" - -# -- Configure Sourcebot-specific application settings. -config: - # Schema version of the Sourcebot configuration. - $schema: https://raw.githubusercontent.com/sourcebot-dev/sourcebot/main/schemas/v3/index.json - connections: {} - # github-repos: - # type: github - # token: - # env: GITHUB_TOKEN - # repos: - # - sourcebot/sourcebot - # - sourcebot/sourcebot-plugins - settings: {} - # reindexIntervalMs: 86400000 - # enablePublicAccess: true - -# -- Configure the ServiceAccount. -serviceAccount: - # -- Create a new ServiceAccount. - create: true - # -- Use an existing ServiceAccount (if set). - name: "" - # -- Add annotations to the ServiceAccount. - annotations: {} - # -- Enable or disable automatic ServiceAccount mounting. - automount: false - -# -- Set the pod-level security context. -podSecurityContext: {} -# runAsUser: 1000 -# runAsGroup: 1000 -# runAsNonRoot: true -# fsGroup: 1000 - -# -- Set the container-level security context. -containerSecurityContext: {} -# allowPrivilegeEscalation: false -# privileged: false -# readOnlyRootFilesystem: true -# runAsUser: 1000 -# runAsGroup: 1000 -# capabilities: -# drop: -# - ALL -# seccompProfile: -# type: RuntimeDefault - -# -- Configure the Sourcebot Kubernetes service. -service: - # -- Type of the Kubernetes service (e.g., ClusterIP, NodePort, LoadBalancer). - type: ClusterIP - # -- Internal container port. - containerPort: 3000 - # -- External service port. - port: 3000 - # -- Service annotations. - annotations: {} - -# -- Configure ingress for Sourcebot. -ingress: - # -- Enable or disable ingress. - enabled: false - # -- Ingress class name. - className: "" - # -- Ingress annotations. - annotations: {} - # kubernetes.io/ingress.class: nginx - # kubernetes.io/tls-acme: "true" - - # -- List of hostnames and paths for ingress rules. The first host will be used as the default host. - hosts: [] - # - host: chart-example.local - # paths: - # - path: / - # pathType: ImplementationSpecific - - # -- TLS settings for ingress. - tls: [] - # - hosts: - # - chart-example.local - # secretName: chart-example-tls - -# -- Configure init containers to run before the main container. -initContainers: [] -# - name: sleeper -# image: busybox -# args: -# - sleep -# - "10" - -# -- Configure resource requests and limits for the container. -resources: {} -## It is recommended to set resources explicitly in production environments. -# limits: -# cpu: 100m -# memory: 128Mi -# requests: -# cpu: 100m -# memory: 128Mi - -# -- Liveness probe to check if the container is alive. -livenessProbe: - # -- Http GET request to check if the container is alive. - httpGet: - # -- Path to check. - path: / - # -- Port to check. - port: http - # -- Initial delay before the first probe. - initialDelaySeconds: 10 - # -- Frequency of the probe. - periodSeconds: 10 - # -- Number of consecutive failures before marking the container as unhealthy. - failureThreshold: 5 - -# -- Readiness probe to check if the container is ready to serve traffic. -readinessProbe: - # -- Http GET request to check if the container is ready. - httpGet: - # -- Path to check. - path: / - # -- Port to check. - port: http - # -- Initial delay before the first probe. - initialDelaySeconds: 10 - # -- Frequency of the probe. - periodSeconds: 10 - # -- Number of consecutive failures before marking the container as not ready. - failureThreshold: 5 - -# -- Startup probe to check if the container has started successfully. -startupProbe: - # -- Http GET request to check if the container has started. - httpGet: - # -- Path to check. - path: / - # -- Port to check. - port: http - # -- Number of seconds to wait before starting the probe. - failureThreshold: 30 - # -- Initial delay before the first probe. - periodSeconds: 30 - -# -- Define additional volumes. -# See: https://kubernetes.io/docs/concepts/storage/volumes/ -volumes: [] -# - name: foo -# secret: -# secretName: mysecret -# optional: false - -# -- Define volume mounts for the container. -# See: https://kubernetes.io/docs/concepts/storage/volumes/ -volumeMounts: [] -# - name: foo -# mountPath: "/etc/foo" -# readOnly: true - -# -- Configure Pod Disruption Budget. -podDisruptionBudget: - # -- Enable Pod Disruption Budget. - enabled: true - # -- Minimum number of pods that must be available. - minAvailable: 1 - # -- Maximum number of pods that can be unavailable. - maxUnavailable: 1 - -# -- Add annotations to the pod metadata. -podAnnotations: {} -# prometheus.io/scrape: "true" -# prometheus.io/path: "/metrics" -# prometheus.io/port: "9102" - -# -- Add extra labels to all resources. -additionalLabels: {} -# team: sourcebot - -# -- Set node selector constraints. -# See: https://kubernetes.io/docs/concepts/scheduling-eviction/assign-pod-node/#nodeselector -nodeSelector: {} - -# -- Set tolerations for pod scheduling. -# See: https://kubernetes.io/docs/concepts/scheduling-eviction/taint-and-toleration/ -tolerations: [] -# - effect: NoSchedule -# key: "key" -# operator: Equal -# value: "value" - -# -- Set affinity rules for pod scheduling. -# Defaults to soft anti-affinity if not set. -# See: https://kubernetes.io/docs/concepts/scheduling-eviction/assign-pod-node/ -affinity: {} - -# -- Set the priority class name for pods. -# See: https://kubernetes.io/docs/concepts/scheduling-eviction/pod-priority-preemption/ -priorityClassName: "" - -# -- Configure the Bitnami PostgreSQL sub-chart. -# See: https://artifacthub.io/packages/helm/bitnami/postgresql +# Core Sourcebot Configuration +sourcebot: + # -- Set the number of replicas for the deployment + replicaCount: 1 + + # Image configuration + image: + # -- Container image repository + repository: ghcr.io/sourcebot-dev/sourcebot + # -- Container image tag. Falls back to appVersion if not set. + tag: "" + # -- Container image digest (used instead of tag if set) + digest: "" + # -- Image pull policy + pullPolicy: IfNotPresent + # -- Configure image pull secrets for private registries + pullSecrets: [] + + # -- Override the default command of the container + command: [] + + # -- Override the default arguments of the container + args: [] + + # License configuration + license: + # -- License key value (or use existingSecret) + value: "" + # -- Use an existing secret for the license key + existingSecret: "" + # -- Key in the existing secret that contains the license key + existingSecretKey: key + + # -- Set environment variables from Kubernetes secrets + envSecrets: [] + # - secretName: sourcebot-github-token + # secretKey: token + # envName: GITHUB_TOKEN + + # -- Set additional environment variables + additionalEnv: [] + # - name: SOURCEBOT_TELEMETRY_DISABLED + # value: "1" + + # -- Configure Sourcebot-specific application settings + config: + # Schema version of the Sourcebot configuration + $schema: https://raw.githubusercontent.com/sourcebot-dev/sourcebot/main/schemas/v3/index.json + connections: {} + # github-repos: + # type: github + # token: + # env: GITHUB_TOKEN + # repos: + # - sourcebot/sourcebot + settings: {} + # reindexIntervalMs: 86400000 + # enablePublicAccess: true + + # ServiceAccount configuration + serviceAccount: + # -- Create a new ServiceAccount + create: true + # -- Use an existing ServiceAccount (if set) + name: "" + # -- Add annotations to the ServiceAccount + annotations: {} + # -- Enable or disable automatic ServiceAccount mounting + automount: false + + # -- Set the pod-level security context + podSecurityContext: {} + # runAsUser: 1000 + # runAsGroup: 1000 + # runAsNonRoot: true + # fsGroup: 1000 + + # -- Set the container-level security context + containerSecurityContext: {} + # allowPrivilegeEscalation: false + # privileged: false + # readOnlyRootFilesystem: true + # runAsUser: 1000 + # runAsGroup: 1000 + # capabilities: + # drop: + # - ALL + # seccompProfile: + # type: RuntimeDefault + + # Service configuration + service: + # -- Type of the Kubernetes service (e.g., ClusterIP, NodePort, LoadBalancer) + type: ClusterIP + # -- Internal container port + containerPort: 3000 + # -- External service port + port: 3000 + # -- Service annotations + annotations: {} + + # -- Configure additional ports to expose on the container and service + additionalPorts: [] + # - name: metrics + # port: 9090 + # containerPort: 9090 + # protocol: TCP + + # Ingress configuration + ingress: + # -- Enable or disable ingress + enabled: false + # -- Ingress class name + className: "" + # -- Ingress annotations + annotations: {} + # kubernetes.io/ingress.class: nginx + # kubernetes.io/tls-acme: "true" + # -- List of hostnames and paths for ingress rules. The first host will be used as the default host. + hosts: [] + # - host: chart-example.local + # paths: + # - path: / + # pathType: ImplementationSpecific + # -- TLS settings for ingress + tls: [] + # - hosts: + # - chart-example.local + # secretName: chart-example-tls + + # -- Configure init containers to run before the main container + initContainers: [] + # - name: sleeper + # image: busybox + # args: + # - sleep + # - "10" + + # -- Configure resource requests and limits for the container + resources: {} + ## It is recommended to set resources explicitly in production environments. + # limits: + # cpu: 100m + # memory: 128Mi + # requests: + # cpu: 100m + # memory: 128Mi + + # Liveness probe to check if the container is alive + livenessProbe: + # -- Http GET request to check if the container is alive + httpGet: + # -- Path to check + path: /api/health + # -- Port to check + port: http + # -- Initial delay before the first probe + initialDelaySeconds: 20 + # -- Frequency of the probe + periodSeconds: 10 + # -- Number of consecutive failures before marking the container as unhealthy + failureThreshold: 5 + + # Readiness probe to check if the container is ready to serve traffic + readinessProbe: + # -- Http GET request to check if the container is ready + httpGet: + # -- Path to check + path: /api/health + # -- Port to check + port: http + # -- Initial delay before the first probe + initialDelaySeconds: 20 + # -- Frequency of the probe + periodSeconds: 10 + # -- Number of consecutive failures before marking the container as not ready + failureThreshold: 5 + + # Startup probe to check if the container has started successfully + startupProbe: + # -- Http GET request to check if the container has started + httpGet: + # -- Path to check + path: /api/health + # -- Port to check + port: http + # -- Number of seconds to wait before starting the probe + failureThreshold: 30 + # -- Initial delay before the first probe + periodSeconds: 30 + + # -- Define additional volumes + # See: https://kubernetes.io/docs/concepts/storage/volumes/ + extraVolumes: [] + # - name: foo + # secret: + # secretName: mysecret + # optional: false + + # -- Define volume mounts for the container + # See: https://kubernetes.io/docs/concepts/storage/volumes/ + extraVolumeMounts: [] + # - name: foo + # mountPath: "/etc/foo" + # readOnly: true + + # Pod Disruption Budget configuration + podDisruptionBudget: + # -- Enable Pod Disruption Budget + enabled: true + # -- Minimum number of pods that must be available + minAvailable: 1 + # -- Maximum number of pods that can be unavailable + maxUnavailable: 1 + + # -- Add annotations to the pod metadata + podAnnotations: {} + # prometheus.io/scrape: "true" + # prometheus.io/path: "/metrics" + # prometheus.io/port: "9102" + + # -- Add extra labels to all resources + additionalLabels: {} + # team: sourcebot + + # -- Set node selector constraints + # See: https://kubernetes.io/docs/concepts/scheduling-eviction/assign-pod-node/#nodeselector + nodeSelector: {} + + # -- Set tolerations for pod scheduling + # See: https://kubernetes.io/docs/concepts/scheduling-eviction/taint-and-toleration/ + tolerations: [] + # - effect: NoSchedule + # key: "key" + # operator: Equal + # value: "value" + + # -- Set affinity rules for pod scheduling + # Defaults to soft anti-affinity if not set + # See: https://kubernetes.io/docs/concepts/scheduling-eviction/assign-pod-node/ + affinity: {} + + # -- Set the priority class name for pods + # See: https://kubernetes.io/docs/concepts/scheduling-eviction/pod-priority-preemption/ + priorityClassName: "" + + # Persistent storage configuration + persistence: + # -- Enable persistent storage for repository data and search indexes + enabled: true + # -- Use an existing PersistentVolumeClaim (if set, other persistence settings are ignored) + existingClaim: "" + # -- Size of the persistent volume + size: 10Gi + # -- Storage class name. If not set, uses the cluster default storage class + storageClass: "" + # -- Access modes for the persistent volume + accessModes: + - ReadWriteOnce + # -- Annotations for the PersistentVolumeClaim + annotations: {} + +# PostgreSQL Configuration postgresql: - enabled: false + # -- Deploy PostgreSQL subchart. Set to false to use an external PostgreSQL instance. + deploy: true + + # -- PostgreSQL host (only used if deploy is false) + host: "" + # -- PostgreSQL port + port: 5432 + + image: + # -- Overwrite default repository of helm chart to point to non-paid bitnami images + repository: bitnamilegacy/postgresql + + # Authentication configuration + auth: + # -- Username to connect to PostgreSQL + username: sourcebot + # -- Password for PostgreSQL user (only used if existingSecret is not set) + password: "" + # -- Use an existing secret for PostgreSQL password + existingSecret: "" + # -- Keys in the existing secret + secretKeys: + userPasswordKey: postgresql-password + adminPasswordKey: postgresql-password + # -- Database name + database: sourcebot + # -- Additional database connection arguments + args: "" + + # Subchart specific settings (only applies when deploy is true) + primary: + persistence: + enabled: true + size: 8Gi + +# Redis Configuration +redis: + # -- Deploy Redis/Valkey subchart. Set to false to use an external Redis instance. + deploy: true + + # -- Redis host (only used if deploy is false) + host: "" + # -- Redis port + port: 6379 + + image: + # -- Overwrite default repository of helm chart to point to non-paid bitnami images + repository: bitnamilegacy/valkey + + # Authentication configuration + auth: + # -- Username for Redis connection + username: "default" + # -- Password for Redis user (only used if existingSecret is not set) + password: "" + # -- Use an existing secret for Redis password + existingSecret: "" + # -- Key in the existing secret that contains the Redis password + existingSecretPasswordKey: redis-password + # -- Redis database number + database: 0 From 432c702b692db6b49b7dd01a28538796e11ee5c4 Mon Sep 17 00:00:00 2001 From: bkellam Date: Mon, 20 Oct 2025 22:26:53 -0700 Subject: [PATCH 2/3] move chart into subdirectory. Add release workflow --- .github/workflows/release.yaml | 68 +++++++++++++++++++ .github/workflows/validate.yaml | 6 +- charts/{ => sourcebot}/.helmignore | 0 charts/{ => sourcebot}/Chart.lock | 0 charts/{ => sourcebot}/Chart.yaml | 0 charts/{ => sourcebot}/README.md | 0 charts/{ => sourcebot}/templates/_helpers.tpl | 0 charts/{ => sourcebot}/templates/config.yaml | 0 .../{ => sourcebot}/templates/deployment.yaml | 0 charts/{ => sourcebot}/templates/ingress.yaml | 0 charts/{ => sourcebot}/templates/pdb.yaml | 0 charts/{ => sourcebot}/templates/pvc.yaml | 0 charts/{ => sourcebot}/templates/service.yaml | 0 .../templates/serviceaccount.yaml | 0 charts/{ => sourcebot}/tests/basic_test.yaml | 0 charts/{ => sourcebot}/values.schema.json | 0 charts/{ => sourcebot}/values.yaml | 0 .../minimal-installation/secret.yaml | 0 .../minimal-installation/values.yaml | 0 19 files changed, 71 insertions(+), 3 deletions(-) create mode 100644 .github/workflows/release.yaml rename charts/{ => sourcebot}/.helmignore (100%) rename charts/{ => sourcebot}/Chart.lock (100%) rename charts/{ => sourcebot}/Chart.yaml (100%) rename charts/{ => sourcebot}/README.md (100%) rename charts/{ => sourcebot}/templates/_helpers.tpl (100%) rename charts/{ => sourcebot}/templates/config.yaml (100%) rename charts/{ => sourcebot}/templates/deployment.yaml (100%) rename charts/{ => sourcebot}/templates/ingress.yaml (100%) rename charts/{ => sourcebot}/templates/pdb.yaml (100%) rename charts/{ => sourcebot}/templates/pvc.yaml (100%) rename charts/{ => sourcebot}/templates/service.yaml (100%) rename charts/{ => sourcebot}/templates/serviceaccount.yaml (100%) rename charts/{ => sourcebot}/tests/basic_test.yaml (100%) rename charts/{ => sourcebot}/values.schema.json (100%) rename charts/{ => sourcebot}/values.yaml (100%) rename {charts/examples => examples}/minimal-installation/secret.yaml (100%) rename {charts/examples => examples}/minimal-installation/values.yaml (100%) diff --git a/.github/workflows/release.yaml b/.github/workflows/release.yaml new file mode 100644 index 0000000..2ffe813 --- /dev/null +++ b/.github/workflows/release.yaml @@ -0,0 +1,68 @@ +name: Release Charts + +on: + push: + branches: + - main + paths: + - 'charts/*/Chart.yaml' + +jobs: + release: + name: Release Helm Chart + runs-on: ubuntu-latest + permissions: + contents: write + packages: write + steps: + - name: Checkout + uses: actions/checkout@v4 + with: + fetch-depth: 0 + + - name: Configure Git + run: | + git config user.name "$GITHUB_ACTOR" + git config user.email "$GITHUB_ACTOR@users.noreply.github.com" + + - name: Install Helm + uses: azure/setup-helm@v3 + + - name: Update dependencies + run: helm dependency update charts/sourcebot/ + + - name: Set Chart Version + id: chart-version + run: | + # Extract chart version from the Chart.yaml + CHART_DIR=$(find ./charts -name "Chart.yaml" -not -path "*/\.git/*" | head -1 | xargs dirname) + CHART_NAME=$(yq e '.name' "$CHART_DIR/Chart.yaml") + CHART_VERSION=$(yq e '.version' "$CHART_DIR/Chart.yaml") + REPO_LOWER=$(echo "${{ github.repository }}" | tr '[:upper:]' '[:lower:]') + echo "chart_dir=$CHART_DIR" >> $GITHUB_OUTPUT + echo "chart_name=$CHART_NAME" >> $GITHUB_OUTPUT + echo "chart_version=$CHART_VERSION" >> $GITHUB_OUTPUT + echo "repo_lower=$REPO_LOWER" >> $GITHUB_OUTPUT + echo "Chart: $CHART_NAME:$CHART_VERSION in $CHART_DIR" + + - name: Login to GitHub Container Registry + uses: docker/login-action@v3 + with: + registry: ghcr.io + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} + + - name: Package Helm Chart + run: | + helm package ${{ steps.chart-version.outputs.chart_dir }} --version ${{ steps.chart-version.outputs.chart_version }} + + - name: Push to GitHub Container Registry + run: | + helm push ${{ steps.chart-version.outputs.chart_name }}-${{ steps.chart-version.outputs.chart_version }}.tgz oci://ghcr.io/${{ steps.chart-version.outputs.repo_lower }}/charts + + - name: Run chart-releaser + uses: helm/chart-releaser-action@v1.6.0 + env: + CR_TOKEN: "${{ secrets.GITHUB_TOKEN }}" + CR_GENERATE_RELEASE_NOTES: "true" + diff --git a/.github/workflows/validate.yaml b/.github/workflows/validate.yaml index 33d1339..d093d6b 100644 --- a/.github/workflows/validate.yaml +++ b/.github/workflows/validate.yaml @@ -29,14 +29,14 @@ jobs: sudo mv helm-docs /usr/local/bin/ - name: Update dependencies - run: helm dependency update charts/ + run: helm dependency update charts/sourcebot/ - name: Run helm lint - run: helm lint charts/ + run: helm lint charts/sourcebot/ - name: Validate helm-docs run: | - helm-docs charts/ + helm-docs charts/sourcebot/ if [[ $(git diff --stat) != '' ]]; then echo 'Chart documentation is out of date. Please run helm-docs and commit the changes.' echo 'Git diff:' diff --git a/charts/.helmignore b/charts/sourcebot/.helmignore similarity index 100% rename from charts/.helmignore rename to charts/sourcebot/.helmignore diff --git a/charts/Chart.lock b/charts/sourcebot/Chart.lock similarity index 100% rename from charts/Chart.lock rename to charts/sourcebot/Chart.lock diff --git a/charts/Chart.yaml b/charts/sourcebot/Chart.yaml similarity index 100% rename from charts/Chart.yaml rename to charts/sourcebot/Chart.yaml diff --git a/charts/README.md b/charts/sourcebot/README.md similarity index 100% rename from charts/README.md rename to charts/sourcebot/README.md diff --git a/charts/templates/_helpers.tpl b/charts/sourcebot/templates/_helpers.tpl similarity index 100% rename from charts/templates/_helpers.tpl rename to charts/sourcebot/templates/_helpers.tpl diff --git a/charts/templates/config.yaml b/charts/sourcebot/templates/config.yaml similarity index 100% rename from charts/templates/config.yaml rename to charts/sourcebot/templates/config.yaml diff --git a/charts/templates/deployment.yaml b/charts/sourcebot/templates/deployment.yaml similarity index 100% rename from charts/templates/deployment.yaml rename to charts/sourcebot/templates/deployment.yaml diff --git a/charts/templates/ingress.yaml b/charts/sourcebot/templates/ingress.yaml similarity index 100% rename from charts/templates/ingress.yaml rename to charts/sourcebot/templates/ingress.yaml diff --git a/charts/templates/pdb.yaml b/charts/sourcebot/templates/pdb.yaml similarity index 100% rename from charts/templates/pdb.yaml rename to charts/sourcebot/templates/pdb.yaml diff --git a/charts/templates/pvc.yaml b/charts/sourcebot/templates/pvc.yaml similarity index 100% rename from charts/templates/pvc.yaml rename to charts/sourcebot/templates/pvc.yaml diff --git a/charts/templates/service.yaml b/charts/sourcebot/templates/service.yaml similarity index 100% rename from charts/templates/service.yaml rename to charts/sourcebot/templates/service.yaml diff --git a/charts/templates/serviceaccount.yaml b/charts/sourcebot/templates/serviceaccount.yaml similarity index 100% rename from charts/templates/serviceaccount.yaml rename to charts/sourcebot/templates/serviceaccount.yaml diff --git a/charts/tests/basic_test.yaml b/charts/sourcebot/tests/basic_test.yaml similarity index 100% rename from charts/tests/basic_test.yaml rename to charts/sourcebot/tests/basic_test.yaml diff --git a/charts/values.schema.json b/charts/sourcebot/values.schema.json similarity index 100% rename from charts/values.schema.json rename to charts/sourcebot/values.schema.json diff --git a/charts/values.yaml b/charts/sourcebot/values.yaml similarity index 100% rename from charts/values.yaml rename to charts/sourcebot/values.yaml diff --git a/charts/examples/minimal-installation/secret.yaml b/examples/minimal-installation/secret.yaml similarity index 100% rename from charts/examples/minimal-installation/secret.yaml rename to examples/minimal-installation/secret.yaml diff --git a/charts/examples/minimal-installation/values.yaml b/examples/minimal-installation/values.yaml similarity index 100% rename from charts/examples/minimal-installation/values.yaml rename to examples/minimal-installation/values.yaml From af021b48f88bd778c0ee8bd24a6662544619043f Mon Sep 17 00:00:00 2001 From: bkellam Date: Mon, 20 Oct 2025 22:35:42 -0700 Subject: [PATCH 3/3] Add lint file --- .github/workflows/validate.yaml | 2 +- charts/sourcebot/values.lint.yaml | 11 +++++++++++ 2 files changed, 12 insertions(+), 1 deletion(-) create mode 100644 charts/sourcebot/values.lint.yaml diff --git a/.github/workflows/validate.yaml b/.github/workflows/validate.yaml index d093d6b..68cadc2 100644 --- a/.github/workflows/validate.yaml +++ b/.github/workflows/validate.yaml @@ -32,7 +32,7 @@ jobs: run: helm dependency update charts/sourcebot/ - name: Run helm lint - run: helm lint charts/sourcebot/ + run: helm lint charts/sourcebot/ -f charts/sourcebot/values.lint.yaml - name: Validate helm-docs run: | diff --git a/charts/sourcebot/values.lint.yaml b/charts/sourcebot/values.lint.yaml new file mode 100644 index 0000000..aca8111 --- /dev/null +++ b/charts/sourcebot/values.lint.yaml @@ -0,0 +1,11 @@ +# This file is used to validate the Chart and sets some values for required fields. It is not used for deployment. +# Example usage: `helm lint charts/sourcebot/ --values charts/sourcebot/values.lint.yaml` + +postgresql: + auth: + password: "lintPassword" + +redis: + auth: + password: "lintPassword" +