From 2fcb8cb5d4c79d15939f2dc480a1d11bff76fc2c Mon Sep 17 00:00:00 2001 From: yashGoyal40 Date: Mon, 6 Apr 2026 17:44:47 +0530 Subject: [PATCH 01/16] feat: add Helm chart for Kubernetes deployment Adds a production-ready Helm chart under charts/repowise/ that enables deploying Repowise to any Kubernetes cluster. Includes templates for Deployment, Service, PVC, Ingress, Secret, and ServiceAccount with full configurability via values.yaml. Closes #49 Co-Authored-By: Claude Opus 4.6 (1M context) --- charts/repowise/.helmignore | 13 ++ charts/repowise/Chart.yaml | 18 +++ charts/repowise/README.md | 87 +++++++++++ charts/repowise/templates/NOTES.txt | 19 +++ charts/repowise/templates/_helpers.tpl | 71 +++++++++ charts/repowise/templates/deployment.yaml | 106 +++++++++++++ charts/repowise/templates/ingress.yaml | 41 +++++ charts/repowise/templates/pvc.yaml | 21 +++ charts/repowise/templates/secret.yaml | 13 ++ charts/repowise/templates/service.yaml | 19 +++ charts/repowise/templates/serviceaccount.yaml | 12 ++ charts/repowise/values.yaml | 142 ++++++++++++++++++ 12 files changed, 562 insertions(+) create mode 100644 charts/repowise/.helmignore create mode 100644 charts/repowise/Chart.yaml create mode 100644 charts/repowise/README.md create mode 100644 charts/repowise/templates/NOTES.txt create mode 100644 charts/repowise/templates/_helpers.tpl create mode 100644 charts/repowise/templates/deployment.yaml create mode 100644 charts/repowise/templates/ingress.yaml create mode 100644 charts/repowise/templates/pvc.yaml create mode 100644 charts/repowise/templates/secret.yaml create mode 100644 charts/repowise/templates/service.yaml create mode 100644 charts/repowise/templates/serviceaccount.yaml create mode 100644 charts/repowise/values.yaml diff --git a/charts/repowise/.helmignore b/charts/repowise/.helmignore new file mode 100644 index 0000000..ea11244 --- /dev/null +++ b/charts/repowise/.helmignore @@ -0,0 +1,13 @@ +.DS_Store +.git/ +.gitignore +.bzr/ +.bzrignore +.hg/ +.hgignore +.svn/ +*.swp +*.bak +*.tmp +*.orig +*~ diff --git a/charts/repowise/Chart.yaml b/charts/repowise/Chart.yaml new file mode 100644 index 0000000..3d45811 --- /dev/null +++ b/charts/repowise/Chart.yaml @@ -0,0 +1,18 @@ +apiVersion: v2 +name: repowise +description: A Helm chart for deploying Repowise — AI-powered codebase documentation and context engine +type: application +version: 0.1.0 +appVersion: "0.1.0" +home: https://github.com/repowise-dev/repowise +sources: + - https://github.com/repowise-dev/repowise +maintainers: + - name: repowise-dev + url: https://github.com/repowise-dev +keywords: + - repowise + - code-documentation + - ai + - mcp + - codebase-context diff --git a/charts/repowise/README.md b/charts/repowise/README.md new file mode 100644 index 0000000..a61da18 --- /dev/null +++ b/charts/repowise/README.md @@ -0,0 +1,87 @@ +# Repowise Helm Chart + +Deploy [Repowise](https://github.com/repowise-dev/repowise) on Kubernetes. + +## Prerequisites + +- Kubernetes 1.24+ +- Helm 3.x +- A container image built from `docker/Dockerfile` pushed to your registry + +## Quick Start + +```bash +# Build and push the image +docker build -t your-registry/repowise:0.1.0 -f docker/Dockerfile . +docker push your-registry/repowise:0.1.0 + +# Install the chart +helm install repowise ./charts/repowise \ + --set image.repository=your-registry/repowise \ + --set image.tag=0.1.0 \ + --set apiKeys.anthropic=sk-ant-... +``` + +## Configuration + +| Parameter | Description | Default | +|-----------|-------------|---------| +| `replicaCount` | Number of replicas (only 1 supported with SQLite) | `1` | +| `image.repository` | Container image repository | `repowise/repowise` | +| `image.tag` | Image tag (defaults to appVersion) | `""` | +| `repowise.embedder` | Embedder backend (`mock`, `openai`, `gemini`) | `mock` | +| `repowise.dbUrl` | Database connection URL | `sqlite+aiosqlite:////data/wiki.db` | +| `repowise.backendPort` | API server port | `7337` | +| `repowise.frontendPort` | Web UI port | `3000` | +| `apiKeys.anthropic` | Anthropic API key | `""` | +| `apiKeys.openai` | OpenAI API key | `""` | +| `apiKeys.gemini` | Gemini API key | `""` | +| `existingSecret` | Use an existing Secret for API keys | `""` | +| `persistence.enabled` | Enable PVC for `/data` | `true` | +| `persistence.size` | PVC size | `10Gi` | +| `persistence.storageClass` | Storage class (empty = cluster default) | `""` | +| `ingress.enabled` | Enable Ingress | `false` | +| `ingress.className` | Ingress class name | `""` | +| `ingress.hosts` | Ingress host rules | see `values.yaml` | + +## Using an Existing Secret + +If you manage secrets externally (e.g., Sealed Secrets, External Secrets): + +```bash +kubectl create secret generic my-repowise-keys \ + --from-literal=ANTHROPIC_API_KEY=sk-ant-... \ + --from-literal=OPENAI_API_KEY= \ + --from-literal=GEMINI_API_KEY= + +helm install repowise ./charts/repowise \ + --set existingSecret=my-repowise-keys +``` + +## Ingress Example + +```yaml +ingress: + enabled: true + className: nginx + annotations: + cert-manager.io/cluster-issuer: letsencrypt-prod + hosts: + - host: repowise.example.com + paths: + - path: / + pathType: Prefix + servicePort: frontend + tls: + - secretName: repowise-tls + hosts: + - repowise.example.com +``` + +## Persistence + +Repowise stores its SQLite database and indexed repository data under `/data`. The chart creates a PVC by default. To disable (data lost on pod restart): + +```bash +helm install repowise ./charts/repowise --set persistence.enabled=false +``` diff --git a/charts/repowise/templates/NOTES.txt b/charts/repowise/templates/NOTES.txt new file mode 100644 index 0000000..dbccc23 --- /dev/null +++ b/charts/repowise/templates/NOTES.txt @@ -0,0 +1,19 @@ +Repowise has been deployed! + +1. Get the application URLs: +{{- if .Values.ingress.enabled }} +{{- range $host := .Values.ingress.hosts }} + http{{ if $.Values.ingress.tls }}s{{ end }}://{{ $host.host }} +{{- end }} +{{- else if contains "NodePort" .Values.service.type }} + export NODE_PORT_FRONTEND=$(kubectl get --namespace {{ .Release.Namespace }} -o jsonpath="{.spec.ports[1].nodePort}" services {{ include "repowise.fullname" . }}) + export NODE_IP=$(kubectl get nodes --namespace {{ .Release.Namespace }} -o jsonpath="{.items[0].status.addresses[0].address}") + echo "Web UI: http://$NODE_IP:$NODE_PORT_FRONTEND" +{{- else if contains "ClusterIP" .Values.service.type }} + kubectl --namespace {{ .Release.Namespace }} port-forward svc/{{ include "repowise.fullname" . }} 3000:{{ .Values.service.frontendPort }} 7337:{{ .Values.service.backendPort }} + echo "Web UI: http://localhost:3000" + echo "API: http://localhost:7337" +{{- end }} + +2. To use as an MCP server, point your client at the API: + http://{{ include "repowise.fullname" . }}.{{ .Release.Namespace }}.svc.cluster.local:{{ .Values.service.backendPort }} diff --git a/charts/repowise/templates/_helpers.tpl b/charts/repowise/templates/_helpers.tpl new file mode 100644 index 0000000..885b529 --- /dev/null +++ b/charts/repowise/templates/_helpers.tpl @@ -0,0 +1,71 @@ +{{/* +Expand the name of the chart. +*/}} +{{- define "repowise.name" -}} +{{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" }} +{{- end }} + +{{/* +Create a default fully qualified app name. +*/}} +{{- define "repowise.fullname" -}} +{{- if .Values.fullnameOverride }} +{{- .Values.fullnameOverride | trunc 63 | trimSuffix "-" }} +{{- else }} +{{- $name := default .Chart.Name .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 }} + +{{/* +Create chart name and version for the chart label. +*/}} +{{- define "repowise.chart" -}} +{{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" }} +{{- end }} + +{{/* +Common labels. +*/}} +{{- define "repowise.labels" -}} +helm.sh/chart: {{ include "repowise.chart" . }} +{{ include "repowise.selectorLabels" . }} +{{- if .Chart.AppVersion }} +app.kubernetes.io/version: {{ .Chart.AppVersion | quote }} +{{- end }} +app.kubernetes.io/managed-by: {{ .Release.Service }} +{{- end }} + +{{/* +Selector labels. +*/}} +{{- define "repowise.selectorLabels" -}} +app.kubernetes.io/name: {{ include "repowise.name" . }} +app.kubernetes.io/instance: {{ .Release.Name }} +{{- end }} + +{{/* +Create the name of the service account to use. +*/}} +{{- define "repowise.serviceAccountName" -}} +{{- if .Values.serviceAccount.create }} +{{- default (include "repowise.fullname" .) .Values.serviceAccount.name }} +{{- else }} +{{- default "default" .Values.serviceAccount.name }} +{{- end }} +{{- end }} + +{{/* +Name of the Secret holding API keys. +*/}} +{{- define "repowise.secretName" -}} +{{- if .Values.existingSecret }} +{{- .Values.existingSecret }} +{{- else }} +{{- include "repowise.fullname" . }} +{{- end }} +{{- end }} diff --git a/charts/repowise/templates/deployment.yaml b/charts/repowise/templates/deployment.yaml new file mode 100644 index 0000000..1d989b6 --- /dev/null +++ b/charts/repowise/templates/deployment.yaml @@ -0,0 +1,106 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: {{ include "repowise.fullname" . }} + labels: + {{- include "repowise.labels" . | nindent 4 }} +spec: + replicas: {{ .Values.replicaCount }} + strategy: + type: Recreate + selector: + matchLabels: + {{- include "repowise.selectorLabels" . | nindent 6 }} + template: + metadata: + {{- with .Values.podAnnotations }} + annotations: + {{- toYaml . | nindent 8 }} + {{- end }} + labels: + {{- include "repowise.selectorLabels" . | nindent 8 }} + spec: + {{- with .Values.imagePullSecrets }} + imagePullSecrets: + {{- toYaml . | nindent 8 }} + {{- end }} + serviceAccountName: {{ include "repowise.serviceAccountName" . }} + securityContext: + {{- toYaml .Values.podSecurityContext | nindent 8 }} + containers: + - name: {{ .Chart.Name }} + securityContext: + {{- toYaml .Values.securityContext | nindent 12 }} + image: "{{ .Values.image.repository }}:{{ .Values.image.tag | default .Chart.AppVersion }}" + imagePullPolicy: {{ .Values.image.pullPolicy }} + ports: + - name: backend + containerPort: {{ .Values.repowise.backendPort }} + protocol: TCP + - name: frontend + containerPort: {{ .Values.repowise.frontendPort }} + protocol: TCP + env: + - name: REPOWISE_DB_URL + value: {{ .Values.repowise.dbUrl | quote }} + - name: REPOWISE_EMBEDDER + value: {{ .Values.repowise.embedder | quote }} + - name: PORT_BACKEND + value: {{ .Values.repowise.backendPort | quote }} + - name: PORT_FRONTEND + value: {{ .Values.repowise.frontendPort | quote }} + - name: HOSTNAME + value: "0.0.0.0" + - name: ANTHROPIC_API_KEY + valueFrom: + secretKeyRef: + name: {{ include "repowise.secretName" . }} + key: ANTHROPIC_API_KEY + optional: true + - name: OPENAI_API_KEY + valueFrom: + secretKeyRef: + name: {{ include "repowise.secretName" . }} + key: OPENAI_API_KEY + optional: true + - name: GEMINI_API_KEY + valueFrom: + secretKeyRef: + name: {{ include "repowise.secretName" . }} + key: GEMINI_API_KEY + optional: true + {{- with .Values.livenessProbe }} + livenessProbe: + {{- toYaml . | nindent 12 }} + {{- end }} + {{- with .Values.readinessProbe }} + readinessProbe: + {{- toYaml . | nindent 12 }} + {{- end }} + volumeMounts: + - name: data + mountPath: /data + {{- with .Values.resources }} + resources: + {{- toYaml . | nindent 12 }} + {{- end }} + volumes: + - name: data + {{- if .Values.persistence.enabled }} + persistentVolumeClaim: + claimName: {{ include "repowise.fullname" . }} + {{- else }} + emptyDir: {} + {{- end }} + {{- with .Values.nodeSelector }} + nodeSelector: + {{- toYaml . | nindent 8 }} + {{- end }} + {{- with .Values.affinity }} + affinity: + {{- toYaml . | nindent 8 }} + {{- end }} + {{- with .Values.tolerations }} + tolerations: + {{- toYaml . | nindent 8 }} + {{- end }} diff --git a/charts/repowise/templates/ingress.yaml b/charts/repowise/templates/ingress.yaml new file mode 100644 index 0000000..7745a03 --- /dev/null +++ b/charts/repowise/templates/ingress.yaml @@ -0,0 +1,41 @@ +{{- if .Values.ingress.enabled -}} +apiVersion: networking.k8s.io/v1 +kind: Ingress +metadata: + name: {{ include "repowise.fullname" . }} + labels: + {{- include "repowise.labels" . | nindent 4 }} + {{- with .Values.ingress.annotations }} + annotations: + {{- toYaml . | nindent 4 }} + {{- end }} +spec: + {{- if .Values.ingress.className }} + ingressClassName: {{ .Values.ingress.className }} + {{- end }} + {{- if .Values.ingress.tls }} + tls: + {{- range .Values.ingress.tls }} + - hosts: + {{- range .hosts }} + - {{ . | quote }} + {{- end }} + secretName: {{ .secretName }} + {{- end }} + {{- end }} + rules: + {{- range .Values.ingress.hosts }} + - host: {{ .host | quote }} + http: + paths: + {{- range .paths }} + - path: {{ .path }} + pathType: {{ .pathType }} + backend: + service: + name: {{ include "repowise.fullname" $ }} + port: + name: {{ .servicePort | default "frontend" }} + {{- end }} + {{- end }} +{{- end }} diff --git a/charts/repowise/templates/pvc.yaml b/charts/repowise/templates/pvc.yaml new file mode 100644 index 0000000..869441b --- /dev/null +++ b/charts/repowise/templates/pvc.yaml @@ -0,0 +1,21 @@ +{{- if .Values.persistence.enabled }} +apiVersion: v1 +kind: PersistentVolumeClaim +metadata: + name: {{ include "repowise.fullname" . }} + labels: + {{- include "repowise.labels" . | nindent 4 }} + {{- with .Values.persistence.annotations }} + annotations: + {{- toYaml . | nindent 4 }} + {{- end }} +spec: + accessModes: + - {{ .Values.persistence.accessMode }} + {{- if .Values.persistence.storageClass }} + storageClassName: {{ .Values.persistence.storageClass | quote }} + {{- end }} + resources: + requests: + storage: {{ .Values.persistence.size }} +{{- end }} diff --git a/charts/repowise/templates/secret.yaml b/charts/repowise/templates/secret.yaml new file mode 100644 index 0000000..7c8d1a4 --- /dev/null +++ b/charts/repowise/templates/secret.yaml @@ -0,0 +1,13 @@ +{{- if not .Values.existingSecret }} +apiVersion: v1 +kind: Secret +metadata: + name: {{ include "repowise.fullname" . }} + labels: + {{- include "repowise.labels" . | nindent 4 }} +type: Opaque +data: + ANTHROPIC_API_KEY: {{ .Values.apiKeys.anthropic | b64enc | quote }} + OPENAI_API_KEY: {{ .Values.apiKeys.openai | b64enc | quote }} + GEMINI_API_KEY: {{ .Values.apiKeys.gemini | b64enc | quote }} +{{- end }} diff --git a/charts/repowise/templates/service.yaml b/charts/repowise/templates/service.yaml new file mode 100644 index 0000000..5e62740 --- /dev/null +++ b/charts/repowise/templates/service.yaml @@ -0,0 +1,19 @@ +apiVersion: v1 +kind: Service +metadata: + name: {{ include "repowise.fullname" . }} + labels: + {{- include "repowise.labels" . | nindent 4 }} +spec: + type: {{ .Values.service.type }} + ports: + - port: {{ .Values.service.backendPort }} + targetPort: backend + protocol: TCP + name: backend + - port: {{ .Values.service.frontendPort }} + targetPort: frontend + protocol: TCP + name: frontend + selector: + {{- include "repowise.selectorLabels" . | nindent 4 }} diff --git a/charts/repowise/templates/serviceaccount.yaml b/charts/repowise/templates/serviceaccount.yaml new file mode 100644 index 0000000..19cce09 --- /dev/null +++ b/charts/repowise/templates/serviceaccount.yaml @@ -0,0 +1,12 @@ +{{- if .Values.serviceAccount.create -}} +apiVersion: v1 +kind: ServiceAccount +metadata: + name: {{ include "repowise.serviceAccountName" . }} + labels: + {{- include "repowise.labels" . | nindent 4 }} + {{- with .Values.serviceAccount.annotations }} + annotations: + {{- toYaml . | nindent 4 }} + {{- end }} +{{- end }} diff --git a/charts/repowise/values.yaml b/charts/repowise/values.yaml new file mode 100644 index 0000000..9bdfaa7 --- /dev/null +++ b/charts/repowise/values.yaml @@ -0,0 +1,142 @@ +# -- Number of replicas (only 1 supported with SQLite) +replicaCount: 1 + +image: + # -- Container image repository + repository: repowise/repowise + # -- Image pull policy + pullPolicy: IfNotPresent + # -- Override the image tag (defaults to Chart appVersion) + tag: "" + +# -- Image pull secrets for private registries +imagePullSecrets: [] +# -- Override the release name +nameOverride: "" +# -- Override the full release name +fullnameOverride: "" + +serviceAccount: + # -- Create a ServiceAccount + create: true + # -- Annotations for the ServiceAccount + annotations: {} + # -- Override the ServiceAccount name + name: "" + +# -- Pod-level annotations +podAnnotations: {} + +# -- Pod-level security context +podSecurityContext: + fsGroup: 1000 + +# -- Container-level security context +securityContext: + runAsNonRoot: true + runAsUser: 1000 + +# -- Repowise configuration +repowise: + # -- Embedder to use: mock, openai, gemini, etc. + embedder: mock + # -- Database URL (uses the PVC-backed SQLite by default) + dbUrl: "sqlite+aiosqlite:////data/wiki.db" + # -- Backend API port + backendPort: 7337 + # -- Frontend Web UI port + frontendPort: 3000 + +# -- API keys for LLM providers (stored in a Secret) +apiKeys: + # -- Anthropic API key + anthropic: "" + # -- OpenAI API key + openai: "" + # -- Gemini API key + gemini: "" + +# -- Reference an existing Secret instead of creating one. +# The Secret must contain keys: ANTHROPIC_API_KEY, OPENAI_API_KEY, GEMINI_API_KEY +existingSecret: "" + +service: + # -- Service type + type: ClusterIP + # -- Backend API port + backendPort: 7337 + # -- Frontend Web UI port + frontendPort: 3000 + +ingress: + # -- Enable Ingress + enabled: false + # -- Ingress class name + className: "" + # -- Ingress annotations + annotations: {} + # kubernetes.io/ingress.class: nginx + # cert-manager.io/cluster-issuer: letsencrypt-prod + # -- Ingress hosts + hosts: + - host: repowise.local + paths: + - path: / + pathType: Prefix + # -- Which port to route to: "frontend" or "backend" + servicePort: frontend + # -- TLS configuration + tls: [] + # - secretName: repowise-tls + # hosts: + # - repowise.local + +persistence: + # -- Enable persistent storage for /data (SQLite DB + indexed repos) + enabled: true + # -- Storage class (leave empty for cluster default) + storageClass: "" + # -- Access mode + accessMode: ReadWriteOnce + # -- Volume size + size: 10Gi + # -- Annotations for the PVC + annotations: {} + +# -- Resource requests and limits +resources: {} + # limits: + # cpu: 1000m + # memory: 2Gi + # requests: + # cpu: 250m + # memory: 512Mi + +# -- Node selector +nodeSelector: {} + +# -- Tolerations +tolerations: [] + +# -- Affinity rules +affinity: {} + +# -- Liveness probe configuration +livenessProbe: + httpGet: + path: /health + port: backend + initialDelaySeconds: 15 + periodSeconds: 30 + timeoutSeconds: 5 + failureThreshold: 3 + +# -- Readiness probe configuration +readinessProbe: + httpGet: + path: /health + port: backend + initialDelaySeconds: 10 + periodSeconds: 10 + timeoutSeconds: 5 + failureThreshold: 3 From 2410e741fb7b9bb5bdbc0b8ee4de0adb9881a527 Mon Sep 17 00:00:00 2001 From: yashGoyal40 Date: Mon, 6 Apr 2026 18:14:02 +0530 Subject: [PATCH 02/16] fix: route /api, /health, /metrics to backend in HTTPProxy The HTTPProxy was sending all traffic to the frontend (port 3000). Now /api/*, /health, and /metrics are routed directly to the backend (port 7337), while everything else goes to the frontend. Also replaced the Ingress template with Contour HTTPProxy with wildcard TLS support. Co-Authored-By: Claude Opus 4.6 (1M context) --- charts/repowise/templates/NOTES.txt | 6 +-- charts/repowise/templates/ingress.yaml | 63 ++++++++++++-------------- charts/repowise/values.yaml | 30 ++++-------- 3 files changed, 40 insertions(+), 59 deletions(-) diff --git a/charts/repowise/templates/NOTES.txt b/charts/repowise/templates/NOTES.txt index dbccc23..78f85a5 100644 --- a/charts/repowise/templates/NOTES.txt +++ b/charts/repowise/templates/NOTES.txt @@ -1,10 +1,8 @@ Repowise has been deployed! 1. Get the application URLs: -{{- if .Values.ingress.enabled }} -{{- range $host := .Values.ingress.hosts }} - http{{ if $.Values.ingress.tls }}s{{ end }}://{{ $host.host }} -{{- end }} +{{- if .Values.httpProxy.enabled }} + https://{{ .Values.httpProxy.fqdn }} {{- else if contains "NodePort" .Values.service.type }} export NODE_PORT_FRONTEND=$(kubectl get --namespace {{ .Release.Namespace }} -o jsonpath="{.spec.ports[1].nodePort}" services {{ include "repowise.fullname" . }}) export NODE_IP=$(kubectl get nodes --namespace {{ .Release.Namespace }} -o jsonpath="{.items[0].status.addresses[0].address}") diff --git a/charts/repowise/templates/ingress.yaml b/charts/repowise/templates/ingress.yaml index 7745a03..9825456 100644 --- a/charts/repowise/templates/ingress.yaml +++ b/charts/repowise/templates/ingress.yaml @@ -1,41 +1,36 @@ -{{- if .Values.ingress.enabled -}} -apiVersion: networking.k8s.io/v1 -kind: Ingress +{{- if .Values.httpProxy.enabled -}} +apiVersion: projectcontour.io/v1 +kind: HTTPProxy metadata: name: {{ include "repowise.fullname" . }} labels: {{- include "repowise.labels" . | nindent 4 }} - {{- with .Values.ingress.annotations }} - annotations: - {{- toYaml . | nindent 4 }} - {{- end }} spec: - {{- if .Values.ingress.className }} - ingressClassName: {{ .Values.ingress.className }} - {{- end }} - {{- if .Values.ingress.tls }} - tls: - {{- range .Values.ingress.tls }} - - hosts: - {{- range .hosts }} - - {{ . | quote }} - {{- end }} - secretName: {{ .secretName }} - {{- end }} - {{- end }} - rules: - {{- range .Values.ingress.hosts }} - - host: {{ .host | quote }} - http: - paths: - {{- range .paths }} - - path: {{ .path }} - pathType: {{ .pathType }} - backend: - service: - name: {{ include "repowise.fullname" $ }} - port: - name: {{ .servicePort | default "frontend" }} - {{- end }} + virtualhost: + fqdn: {{ .Values.httpProxy.fqdn }} + {{- if .Values.httpProxy.tls.enabled }} + tls: + secretName: {{ .Values.httpProxy.tls.secretName }} {{- end }} + routes: + - conditions: + - prefix: /api/ + services: + - name: {{ include "repowise.fullname" . }} + port: {{ .Values.service.backendPort }} + - conditions: + - prefix: /health + services: + - name: {{ include "repowise.fullname" . }} + port: {{ .Values.service.backendPort }} + - conditions: + - prefix: /metrics + services: + - name: {{ include "repowise.fullname" . }} + port: {{ .Values.service.backendPort }} + - conditions: + - prefix: / + services: + - name: {{ include "repowise.fullname" . }} + port: {{ .Values.service.frontendPort }} {{- end }} diff --git a/charts/repowise/values.yaml b/charts/repowise/values.yaml index 9bdfaa7..0f4a1d5 100644 --- a/charts/repowise/values.yaml +++ b/charts/repowise/values.yaml @@ -68,28 +68,16 @@ service: # -- Frontend Web UI port frontendPort: 3000 -ingress: - # -- Enable Ingress +httpProxy: + # -- Enable Contour HTTPProxy enabled: false - # -- Ingress class name - className: "" - # -- Ingress annotations - annotations: {} - # kubernetes.io/ingress.class: nginx - # cert-manager.io/cluster-issuer: letsencrypt-prod - # -- Ingress hosts - hosts: - - host: repowise.local - paths: - - path: / - pathType: Prefix - # -- Which port to route to: "frontend" or "backend" - servicePort: frontend - # -- TLS configuration - tls: [] - # - secretName: repowise-tls - # hosts: - # - repowise.local + # -- FQDN for the virtual host + fqdn: repowise.local + tls: + # -- Enable TLS + enabled: false + # -- Name of the TLS secret (e.g. wildcard cert) + secretName: "" persistence: # -- Enable persistent storage for /data (SQLite DB + indexed repos) From af52058f1a9310c0951dfc1eabaf354673344f4a Mon Sep 17 00:00:00 2001 From: yashGoyal40 Date: Mon, 6 Apr 2026 18:19:41 +0530 Subject: [PATCH 03/16] fix: provider section health check calls /health instead of /api/health The backend exposes /health, not /api/health. The provider-section component was calling the wrong endpoint causing "Server returned non-healthy status" on every self-hosted deployment. Co-Authored-By: Claude Opus 4.6 (1M context) --- packages/web/src/components/settings/provider-section.tsx | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/web/src/components/settings/provider-section.tsx b/packages/web/src/components/settings/provider-section.tsx index 338deb6..119095a 100644 --- a/packages/web/src/components/settings/provider-section.tsx +++ b/packages/web/src/components/settings/provider-section.tsx @@ -56,8 +56,8 @@ export function ProviderSection() { setProvider(config.getProvider()); setModel(config.getModel()); setEmbedder(config.getEmbedder()); - // Fetch current server config from /api/health - fetch("/api/health") + // Fetch current server config from /health + fetch("/health") .then((r) => r.json()) .then((data) => { if (data?.provider) setServerProvider(data.provider); @@ -80,7 +80,7 @@ export function ProviderSection() { setTestStatus("testing"); setTestError(""); try { - const res = await fetch("/api/health"); + const res = await fetch("/health"); const data = await res.json(); if (res.ok && data.status === "healthy") { setTestStatus("ok"); From c4aec6c234c38ec3f66290904e219c07e33a16c1 Mon Sep 17 00:00:00 2001 From: yashGoyal40 Date: Mon, 6 Apr 2026 18:26:08 +0530 Subject: [PATCH 04/16] Revert "fix: provider section health check calls /health instead of /api/health" This reverts commit af52058f1a9310c0951dfc1eabaf354673344f4a. --- packages/web/src/components/settings/provider-section.tsx | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/web/src/components/settings/provider-section.tsx b/packages/web/src/components/settings/provider-section.tsx index 119095a..338deb6 100644 --- a/packages/web/src/components/settings/provider-section.tsx +++ b/packages/web/src/components/settings/provider-section.tsx @@ -56,8 +56,8 @@ export function ProviderSection() { setProvider(config.getProvider()); setModel(config.getModel()); setEmbedder(config.getEmbedder()); - // Fetch current server config from /health - fetch("/health") + // Fetch current server config from /api/health + fetch("/api/health") .then((r) => r.json()) .then((data) => { if (data?.provider) setServerProvider(data.provider); @@ -80,7 +80,7 @@ export function ProviderSection() { setTestStatus("testing"); setTestError(""); try { - const res = await fetch("/health"); + const res = await fetch("/api/health"); const data = await res.json(); if (res.ok && data.status === "healthy") { setTestStatus("ok"); From a95eb3b5119c775418c2600668629f8363ed0eb1 Mon Sep 17 00:00:00 2001 From: yashGoyal40 Date: Mon, 6 Apr 2026 18:28:07 +0530 Subject: [PATCH 05/16] revert: use standard k8s Ingress instead of Contour HTTPProxy Restores the standard networking.k8s.io/v1 Ingress template so the chart works out of the box on any Kubernetes cluster, not just those running Contour. Co-Authored-By: Claude Opus 4.6 (1M context) --- charts/repowise/templates/NOTES.txt | 6 ++- charts/repowise/templates/ingress.yaml | 63 ++++++++++++++------------ charts/repowise/values.yaml | 30 ++++++++---- 3 files changed, 59 insertions(+), 40 deletions(-) diff --git a/charts/repowise/templates/NOTES.txt b/charts/repowise/templates/NOTES.txt index 78f85a5..dbccc23 100644 --- a/charts/repowise/templates/NOTES.txt +++ b/charts/repowise/templates/NOTES.txt @@ -1,8 +1,10 @@ Repowise has been deployed! 1. Get the application URLs: -{{- if .Values.httpProxy.enabled }} - https://{{ .Values.httpProxy.fqdn }} +{{- if .Values.ingress.enabled }} +{{- range $host := .Values.ingress.hosts }} + http{{ if $.Values.ingress.tls }}s{{ end }}://{{ $host.host }} +{{- end }} {{- else if contains "NodePort" .Values.service.type }} export NODE_PORT_FRONTEND=$(kubectl get --namespace {{ .Release.Namespace }} -o jsonpath="{.spec.ports[1].nodePort}" services {{ include "repowise.fullname" . }}) export NODE_IP=$(kubectl get nodes --namespace {{ .Release.Namespace }} -o jsonpath="{.items[0].status.addresses[0].address}") diff --git a/charts/repowise/templates/ingress.yaml b/charts/repowise/templates/ingress.yaml index 9825456..7745a03 100644 --- a/charts/repowise/templates/ingress.yaml +++ b/charts/repowise/templates/ingress.yaml @@ -1,36 +1,41 @@ -{{- if .Values.httpProxy.enabled -}} -apiVersion: projectcontour.io/v1 -kind: HTTPProxy +{{- if .Values.ingress.enabled -}} +apiVersion: networking.k8s.io/v1 +kind: Ingress metadata: name: {{ include "repowise.fullname" . }} labels: {{- include "repowise.labels" . | nindent 4 }} + {{- with .Values.ingress.annotations }} + annotations: + {{- toYaml . | nindent 4 }} + {{- end }} spec: - virtualhost: - fqdn: {{ .Values.httpProxy.fqdn }} - {{- if .Values.httpProxy.tls.enabled }} - tls: - secretName: {{ .Values.httpProxy.tls.secretName }} + {{- if .Values.ingress.className }} + ingressClassName: {{ .Values.ingress.className }} + {{- end }} + {{- if .Values.ingress.tls }} + tls: + {{- range .Values.ingress.tls }} + - hosts: + {{- range .hosts }} + - {{ . | quote }} + {{- end }} + secretName: {{ .secretName }} + {{- end }} + {{- end }} + rules: + {{- range .Values.ingress.hosts }} + - host: {{ .host | quote }} + http: + paths: + {{- range .paths }} + - path: {{ .path }} + pathType: {{ .pathType }} + backend: + service: + name: {{ include "repowise.fullname" $ }} + port: + name: {{ .servicePort | default "frontend" }} + {{- end }} {{- end }} - routes: - - conditions: - - prefix: /api/ - services: - - name: {{ include "repowise.fullname" . }} - port: {{ .Values.service.backendPort }} - - conditions: - - prefix: /health - services: - - name: {{ include "repowise.fullname" . }} - port: {{ .Values.service.backendPort }} - - conditions: - - prefix: /metrics - services: - - name: {{ include "repowise.fullname" . }} - port: {{ .Values.service.backendPort }} - - conditions: - - prefix: / - services: - - name: {{ include "repowise.fullname" . }} - port: {{ .Values.service.frontendPort }} {{- end }} diff --git a/charts/repowise/values.yaml b/charts/repowise/values.yaml index 0f4a1d5..9bdfaa7 100644 --- a/charts/repowise/values.yaml +++ b/charts/repowise/values.yaml @@ -68,16 +68,28 @@ service: # -- Frontend Web UI port frontendPort: 3000 -httpProxy: - # -- Enable Contour HTTPProxy +ingress: + # -- Enable Ingress enabled: false - # -- FQDN for the virtual host - fqdn: repowise.local - tls: - # -- Enable TLS - enabled: false - # -- Name of the TLS secret (e.g. wildcard cert) - secretName: "" + # -- Ingress class name + className: "" + # -- Ingress annotations + annotations: {} + # kubernetes.io/ingress.class: nginx + # cert-manager.io/cluster-issuer: letsencrypt-prod + # -- Ingress hosts + hosts: + - host: repowise.local + paths: + - path: / + pathType: Prefix + # -- Which port to route to: "frontend" or "backend" + servicePort: frontend + # -- TLS configuration + tls: [] + # - secretName: repowise-tls + # hosts: + # - repowise.local persistence: # -- Enable persistent storage for /data (SQLite DB + indexed repos) From f9db5e5c800395e14a5e9f1ee2921b660491484a Mon Sep 17 00:00:00 2001 From: yashGoyal40 Date: Mon, 6 Apr 2026 18:36:18 +0530 Subject: [PATCH 06/16] feat: add repo init Job for auto-cloning and registering repositories Adds a post-install/upgrade Kubernetes Job that clones repos declared in values.yaml into /data/repos/, registers them with the Repowise API, and triggers an initial sync. Supports private repos via GitHub PAT or an existing git-credentials Secret. Co-Authored-By: Claude Opus 4.6 (1M context) --- charts/repowise/README.md | 55 ++++++++ .../templates/git-credentials-secret.yaml | 14 ++ charts/repowise/templates/repo-init-job.yaml | 125 ++++++++++++++++++ charts/repowise/values.yaml | 19 +++ 4 files changed, 213 insertions(+) create mode 100644 charts/repowise/templates/git-credentials-secret.yaml create mode 100644 charts/repowise/templates/repo-init-job.yaml diff --git a/charts/repowise/README.md b/charts/repowise/README.md index a61da18..ca59539 100644 --- a/charts/repowise/README.md +++ b/charts/repowise/README.md @@ -78,6 +78,61 @@ ingress: - repowise.example.com ``` +## Auto-Cloning Repositories + +You can declare repos in `values.yaml` and the chart will automatically clone them, register with the API, and trigger indexing via a post-install/upgrade Job. + +### Public repos + +```bash +helm install repowise ./charts/repowise \ + --set repos[0].name=my-app \ + --set repos[0].url=https://github.com/org/my-app.git \ + --set repos[0].branch=main +``` + +### Private repos (GitHub PAT) + +```bash +helm install repowise ./charts/repowise \ + --set repos[0].name=my-private-app \ + --set repos[0].url=https://github.com/org/my-private-app.git \ + --set gitCredentials.github.username=my-user \ + --set gitCredentials.github.token=ghp_... +``` + +### Private repos (existing Secret) + +```bash +kubectl create secret generic git-creds \ + --from-literal=git-credentials='https://my-user:ghp_token@github.com' + +helm install repowise ./charts/repowise \ + --set repos[0].name=my-private-app \ + --set repos[0].url=https://github.com/org/my-private-app.git \ + --set gitCredentials.secretName=git-creds +``` + +### Multiple repos + +```yaml +repos: + - name: frontend + url: https://github.com/org/frontend.git + branch: main + - name: backend + url: https://github.com/org/backend.git + branch: develop + - name: infra + url: https://github.com/org/infra.git +``` + +The Job clones repos to `/data/repos/`, waits for the API to be healthy, registers each repo, and triggers a sync. Monitor progress with: + +```bash +kubectl logs job/repowise-repo-init -n +``` + ## Persistence Repowise stores its SQLite database and indexed repository data under `/data`. The chart creates a PVC by default. To disable (data lost on pod restart): diff --git a/charts/repowise/templates/git-credentials-secret.yaml b/charts/repowise/templates/git-credentials-secret.yaml new file mode 100644 index 0000000..ed15ec9 --- /dev/null +++ b/charts/repowise/templates/git-credentials-secret.yaml @@ -0,0 +1,14 @@ +{{- if and .Values.gitCredentials.github.token (not .Values.gitCredentials.secretName) }} +apiVersion: v1 +kind: Secret +metadata: + name: {{ include "repowise.fullname" . }}-git-credentials + labels: + {{- include "repowise.labels" . | nindent 4 }} + annotations: + "helm.sh/hook": pre-install,pre-upgrade + "helm.sh/hook-weight": "0" +type: Opaque +stringData: + git-credentials: "https://{{ .Values.gitCredentials.github.username }}:{{ .Values.gitCredentials.github.token }}@github.com" +{{- end }} diff --git a/charts/repowise/templates/repo-init-job.yaml b/charts/repowise/templates/repo-init-job.yaml new file mode 100644 index 0000000..b355962 --- /dev/null +++ b/charts/repowise/templates/repo-init-job.yaml @@ -0,0 +1,125 @@ +{{- if .Values.repos }} +apiVersion: batch/v1 +kind: Job +metadata: + name: {{ include "repowise.fullname" . }}-repo-init + labels: + {{- include "repowise.labels" . | nindent 4 }} + annotations: + "helm.sh/hook": post-install,post-upgrade + "helm.sh/hook-weight": "5" + "helm.sh/hook-delete-policy": before-hook-creation +spec: + backoffLimit: 3 + template: + metadata: + labels: + {{- include "repowise.selectorLabels" . | nindent 8 }} + app.kubernetes.io/component: repo-init + spec: + restartPolicy: OnFailure + {{- with .Values.imagePullSecrets }} + imagePullSecrets: + {{- toYaml . | nindent 8 }} + {{- end }} + containers: + - name: repo-init + image: bitnami/git:latest + command: ["/bin/bash", "-c"] + args: + - | + set -e + + API_URL="http://{{ include "repowise.fullname" . }}.{{ .Release.Namespace }}.svc.cluster.local:{{ .Values.service.backendPort }}" + REPOS_DIR="/data/repos" + mkdir -p "$REPOS_DIR" + + {{- if or .Values.gitCredentials.secretName .Values.gitCredentials.github.token }} + # Configure git credentials + git config --global credential.helper 'store --file /tmp/.git-credentials' + cp /git-credentials/git-credentials /tmp/.git-credentials + chmod 600 /tmp/.git-credentials + {{- end }} + + # Wait for Repowise API to be healthy + echo "Waiting for Repowise API at $API_URL ..." + until curl -sf "$API_URL/health" > /dev/null 2>&1; do + echo " API not ready, retrying in 5s..." + sleep 5 + done + echo "API is healthy!" + + {{- range .Values.repos }} + + echo "=========================================" + echo "Processing repo: {{ .name }}" + echo "=========================================" + + REPO_DIR="$REPOS_DIR/{{ .name }}" + + # Clone or pull + if [ ! -d "$REPO_DIR/.git" ]; then + echo "Cloning {{ .url }} -> $REPO_DIR" + git clone --branch {{ .branch | default "main" }} --single-branch {{ .url }} "$REPO_DIR" + else + echo "Repo exists, pulling latest..." + cd "$REPO_DIR" + git fetch origin {{ .branch | default "main" }} + git reset --hard origin/{{ .branch | default "main" }} + fi + + # Register repo with API + echo "Registering repo with Repowise API..." + curl -sf -X POST "$API_URL/api/repos" \ + -H "Content-Type: application/json" \ + -d '{ + "name": "{{ .name }}", + "local_path": "'"$REPO_DIR"'", + "url": "{{ .url }}", + "default_branch": "{{ .branch | default "main" }}" + }' || echo " (repo may already exist, continuing)" + + # Get repo ID and trigger sync + echo "Triggering sync..." + REPO_ID=$(curl -sf "$API_URL/api/repos" | grep -o '"id":"[^"]*","name":"{{ .name }}"' | grep -o '"id":"[^"]*"' | cut -d'"' -f4) + if [ -n "$REPO_ID" ]; then + curl -sf -X POST "$API_URL/api/repos/$REPO_ID/sync" || echo " (sync may already be running)" + echo "Sync triggered for {{ .name }} (id: $REPO_ID)" + else + echo " WARNING: Could not find repo ID for {{ .name }}" + fi + + {{- end }} + + echo "" + echo "All repos processed!" + volumeMounts: + - name: data + mountPath: /data + {{- if or .Values.gitCredentials.secretName .Values.gitCredentials.github.token }} + - name: git-credentials + mountPath: /git-credentials + readOnly: true + {{- end }} + volumes: + - name: data + {{- if .Values.persistence.enabled }} + persistentVolumeClaim: + claimName: {{ include "repowise.fullname" . }} + {{- else }} + emptyDir: {} + {{- end }} + {{- if or .Values.gitCredentials.secretName .Values.gitCredentials.github.token }} + - name: git-credentials + secret: + secretName: {{ .Values.gitCredentials.secretName | default (printf "%s-git-credentials" (include "repowise.fullname" .)) }} + {{- end }} + {{- with .Values.nodeSelector }} + nodeSelector: + {{- toYaml . | nindent 8 }} + {{- end }} + {{- with .Values.tolerations }} + tolerations: + {{- toYaml . | nindent 8 }} + {{- end }} +{{- end }} diff --git a/charts/repowise/values.yaml b/charts/repowise/values.yaml index 9bdfaa7..07259cf 100644 --- a/charts/repowise/values.yaml +++ b/charts/repowise/values.yaml @@ -103,6 +103,25 @@ persistence: # -- Annotations for the PVC annotations: {} +# -- Repositories to clone and register on install/upgrade. +# A Kubernetes Job runs after deploy to clone each repo into /data/repos/, +# register it with the Repowise API, and trigger an initial sync. +repos: [] + # - name: my-app + # url: https://github.com/org/my-app.git + # branch: main + +# -- Git credentials for cloning private repositories +gitCredentials: + # -- Use an existing Secret with a key "git-credentials" containing: + # https://:@github.com + secretName: "" + github: + # -- GitHub username (used if secretName is empty) + username: "" + # -- GitHub personal access token (used if secretName is empty) + token: "" + # -- Resource requests and limits resources: {} # limits: From 5c4e2a443446332c5b91bd6f0b5ad574048f6099 Mon Sep 17 00:00:00 2001 From: yashGoyal40 Date: Mon, 6 Apr 2026 18:57:12 +0530 Subject: [PATCH 07/16] feat: initContainer clones repos, sidecar registers with API - initContainer (bitnami/git) clones repos into /data/repos/ before the main app starts - Sidecar container (curlimages/curl) waits for API health, registers each repo via POST /api/repos, and triggers sync - Supports private repos via GitHub PAT or existing git-credentials Secret - Removed the post-install Job approach (PVC ReadWriteOnce conflict) Co-Authored-By: Claude Opus 4.6 (1M context) --- charts/repowise/templates/deployment.yaml | 62 +++++++++ charts/repowise/templates/repo-init-job.yaml | 125 ------------------- 2 files changed, 62 insertions(+), 125 deletions(-) delete mode 100644 charts/repowise/templates/repo-init-job.yaml diff --git a/charts/repowise/templates/deployment.yaml b/charts/repowise/templates/deployment.yaml index 1d989b6..b79c605 100644 --- a/charts/repowise/templates/deployment.yaml +++ b/charts/repowise/templates/deployment.yaml @@ -27,6 +27,40 @@ spec: serviceAccountName: {{ include "repowise.serviceAccountName" . }} securityContext: {{- toYaml .Values.podSecurityContext | nindent 8 }} + {{- if .Values.repos }} + initContainers: + - name: clone-repos + image: bitnami/git:latest + command: ["/bin/bash", "-c"] + args: + - | + set -e + mkdir -p /data/repos + {{- if or .Values.gitCredentials.secretName .Values.gitCredentials.github.token }} + git config --global credential.helper 'store --file /tmp/.git-credentials' + cp /git-credentials/git-credentials /tmp/.git-credentials + chmod 600 /tmp/.git-credentials + {{- end }} + {{- range .Values.repos }} + echo "=== {{ .name }} ===" + if [ ! -d "/data/repos/{{ .name }}/.git" ]; then + git clone --branch {{ .branch | default "main" }} --single-branch {{ .url }} "/data/repos/{{ .name }}" + else + cd "/data/repos/{{ .name }}" + git fetch origin {{ .branch | default "main" }} + git reset --hard origin/{{ .branch | default "main" }} + fi + {{- end }} + echo "Done." + volumeMounts: + - name: data + mountPath: /data + {{- if or .Values.gitCredentials.secretName .Values.gitCredentials.github.token }} + - name: git-credentials + mountPath: /git-credentials + readOnly: true + {{- end }} + {{- end }} containers: - name: {{ .Chart.Name }} securityContext: @@ -84,6 +118,29 @@ spec: resources: {{- toYaml . | nindent 12 }} {{- end }} + {{- if .Values.repos }} + - name: register-repos + image: curlimages/curl:latest + command: ["/bin/sh", "-c"] + args: + - | + API="http://localhost:{{ .Values.repowise.backendPort }}" + echo "Waiting for API..." + until curl -sf "$API/health" > /dev/null 2>&1; do sleep 3; done + echo "API ready!" + {{- range .Values.repos }} + echo "--- Registering {{ .name }} ---" + curl -sf -X POST "$API/api/repos" \ + -H "Content-Type: application/json" \ + -d '{"name":"{{ .name }}","local_path":"/data/repos/{{ .name }}","url":"{{ .url }}","default_branch":"{{ .branch | default "main" }}"}' \ + && echo " registered" || echo " (exists)" + REPO_ID=$(curl -sf "$API/api/repos" | sed -n 's/.*"id":"\([^"]*\)","name":"{{ .name }}".*/\1/p') + if [ -n "$REPO_ID" ]; then + curl -sf -X POST "$API/api/repos/$REPO_ID/sync" && echo " sync triggered" || echo " (sync running)" + fi + {{- end }} + echo "All repos registered. Exiting." + {{- end }} volumes: - name: data {{- if .Values.persistence.enabled }} @@ -92,6 +149,11 @@ spec: {{- else }} emptyDir: {} {{- end }} + {{- if or .Values.gitCredentials.secretName .Values.gitCredentials.github.token }} + - name: git-credentials + secret: + secretName: {{ .Values.gitCredentials.secretName | default (printf "%s-git-credentials" (include "repowise.fullname" .)) }} + {{- end }} {{- with .Values.nodeSelector }} nodeSelector: {{- toYaml . | nindent 8 }} diff --git a/charts/repowise/templates/repo-init-job.yaml b/charts/repowise/templates/repo-init-job.yaml deleted file mode 100644 index b355962..0000000 --- a/charts/repowise/templates/repo-init-job.yaml +++ /dev/null @@ -1,125 +0,0 @@ -{{- if .Values.repos }} -apiVersion: batch/v1 -kind: Job -metadata: - name: {{ include "repowise.fullname" . }}-repo-init - labels: - {{- include "repowise.labels" . | nindent 4 }} - annotations: - "helm.sh/hook": post-install,post-upgrade - "helm.sh/hook-weight": "5" - "helm.sh/hook-delete-policy": before-hook-creation -spec: - backoffLimit: 3 - template: - metadata: - labels: - {{- include "repowise.selectorLabels" . | nindent 8 }} - app.kubernetes.io/component: repo-init - spec: - restartPolicy: OnFailure - {{- with .Values.imagePullSecrets }} - imagePullSecrets: - {{- toYaml . | nindent 8 }} - {{- end }} - containers: - - name: repo-init - image: bitnami/git:latest - command: ["/bin/bash", "-c"] - args: - - | - set -e - - API_URL="http://{{ include "repowise.fullname" . }}.{{ .Release.Namespace }}.svc.cluster.local:{{ .Values.service.backendPort }}" - REPOS_DIR="/data/repos" - mkdir -p "$REPOS_DIR" - - {{- if or .Values.gitCredentials.secretName .Values.gitCredentials.github.token }} - # Configure git credentials - git config --global credential.helper 'store --file /tmp/.git-credentials' - cp /git-credentials/git-credentials /tmp/.git-credentials - chmod 600 /tmp/.git-credentials - {{- end }} - - # Wait for Repowise API to be healthy - echo "Waiting for Repowise API at $API_URL ..." - until curl -sf "$API_URL/health" > /dev/null 2>&1; do - echo " API not ready, retrying in 5s..." - sleep 5 - done - echo "API is healthy!" - - {{- range .Values.repos }} - - echo "=========================================" - echo "Processing repo: {{ .name }}" - echo "=========================================" - - REPO_DIR="$REPOS_DIR/{{ .name }}" - - # Clone or pull - if [ ! -d "$REPO_DIR/.git" ]; then - echo "Cloning {{ .url }} -> $REPO_DIR" - git clone --branch {{ .branch | default "main" }} --single-branch {{ .url }} "$REPO_DIR" - else - echo "Repo exists, pulling latest..." - cd "$REPO_DIR" - git fetch origin {{ .branch | default "main" }} - git reset --hard origin/{{ .branch | default "main" }} - fi - - # Register repo with API - echo "Registering repo with Repowise API..." - curl -sf -X POST "$API_URL/api/repos" \ - -H "Content-Type: application/json" \ - -d '{ - "name": "{{ .name }}", - "local_path": "'"$REPO_DIR"'", - "url": "{{ .url }}", - "default_branch": "{{ .branch | default "main" }}" - }' || echo " (repo may already exist, continuing)" - - # Get repo ID and trigger sync - echo "Triggering sync..." - REPO_ID=$(curl -sf "$API_URL/api/repos" | grep -o '"id":"[^"]*","name":"{{ .name }}"' | grep -o '"id":"[^"]*"' | cut -d'"' -f4) - if [ -n "$REPO_ID" ]; then - curl -sf -X POST "$API_URL/api/repos/$REPO_ID/sync" || echo " (sync may already be running)" - echo "Sync triggered for {{ .name }} (id: $REPO_ID)" - else - echo " WARNING: Could not find repo ID for {{ .name }}" - fi - - {{- end }} - - echo "" - echo "All repos processed!" - volumeMounts: - - name: data - mountPath: /data - {{- if or .Values.gitCredentials.secretName .Values.gitCredentials.github.token }} - - name: git-credentials - mountPath: /git-credentials - readOnly: true - {{- end }} - volumes: - - name: data - {{- if .Values.persistence.enabled }} - persistentVolumeClaim: - claimName: {{ include "repowise.fullname" . }} - {{- else }} - emptyDir: {} - {{- end }} - {{- if or .Values.gitCredentials.secretName .Values.gitCredentials.github.token }} - - name: git-credentials - secret: - secretName: {{ .Values.gitCredentials.secretName | default (printf "%s-git-credentials" (include "repowise.fullname" .)) }} - {{- end }} - {{- with .Values.nodeSelector }} - nodeSelector: - {{- toYaml . | nindent 8 }} - {{- end }} - {{- with .Values.tolerations }} - tolerations: - {{- toYaml . | nindent 8 }} - {{- end }} -{{- end }} From 5b3f3bfd2301773fe168a6fcc2ee7fe4df292a33 Mon Sep 17 00:00:00 2001 From: yashGoyal40 Date: Mon, 6 Apr 2026 19:21:48 +0530 Subject: [PATCH 08/16] feat: relax health probes, sequential repo sync, skip already-indexed - Increase liveness probe timeout to 15s and failureThreshold to 10 to prevent pod kills during CPU-intensive indexing - Sidecar registers repos one-by-one, waits for each sync to complete before starting the next (prevents SQLite database lock) - Skip sync for repos that already have a head_commit (already indexed) - Remove old repo-init-scripts ConfigMap (script is now inline) Co-Authored-By: Claude Opus 4.6 (1M context) --- charts/repowise/templates/deployment.yaml | 30 ++++++++++++++++++----- charts/repowise/values.yaml | 16 ++++++------ 2 files changed, 33 insertions(+), 13 deletions(-) diff --git a/charts/repowise/templates/deployment.yaml b/charts/repowise/templates/deployment.yaml index b79c605..aa95bc2 100644 --- a/charts/repowise/templates/deployment.yaml +++ b/charts/repowise/templates/deployment.yaml @@ -129,17 +129,35 @@ spec: until curl -sf "$API/health" > /dev/null 2>&1; do sleep 3; done echo "API ready!" {{- range .Values.repos }} - echo "--- Registering {{ .name }} ---" + echo "--- {{ .name }} ---" + # Register (upsert) curl -sf -X POST "$API/api/repos" \ -H "Content-Type: application/json" \ -d '{"name":"{{ .name }}","local_path":"/data/repos/{{ .name }}","url":"{{ .url }}","default_branch":"{{ .branch | default "main" }}"}' \ - && echo " registered" || echo " (exists)" - REPO_ID=$(curl -sf "$API/api/repos" | sed -n 's/.*"id":"\([^"]*\)","name":"{{ .name }}".*/\1/p') - if [ -n "$REPO_ID" ]; then - curl -sf -X POST "$API/api/repos/$REPO_ID/sync" && echo " sync triggered" || echo " (sync running)" + > /dev/null 2>&1 && echo " registered" || echo " (exists)" + # Check if already indexed (has head_commit) + REPO_DATA=$(curl -sf "$API/api/repos" 2>/dev/null || echo "[]") + REPO_ID=$(echo "$REPO_DATA" | sed -n 's/.*"id":"\([^"]*\)","name":"{{ .name }}".*/\1/p') + HEAD=$(echo "$REPO_DATA" | sed -n 's/.*"name":"{{ .name }}".*"head_commit":"\([^"]*\)".*/\1/p') + if [ -n "$REPO_ID" ] && [ -z "$HEAD" ]; then + echo " not yet indexed, triggering sync..." + curl -sf -X POST "$API/api/repos/$REPO_ID/sync" > /dev/null 2>&1 && echo " sync triggered" || echo " (sync already running)" + # Wait for this sync to finish before next repo (SQLite can't handle concurrent writes) + echo " waiting for sync to complete..." + while true; do + sleep 10 + JOBS=$(curl -sf "$API/api/jobs?repo_id=$REPO_ID&status=running" 2>/dev/null || echo "[]") + PENDING=$(curl -sf "$API/api/jobs?repo_id=$REPO_ID&status=pending" 2>/dev/null || echo "[]") + if [ "$JOBS" = "[]" ] && [ "$PENDING" = "[]" ]; then + echo " sync done." + break + fi + done + elif [ -n "$HEAD" ]; then + echo " already indexed (head: $HEAD), skipping sync." fi {{- end }} - echo "All repos registered. Exiting." + echo "All repos processed. Exiting." {{- end }} volumes: - name: data diff --git a/charts/repowise/values.yaml b/charts/repowise/values.yaml index 07259cf..44003a3 100644 --- a/charts/repowise/values.yaml +++ b/charts/repowise/values.yaml @@ -141,14 +141,16 @@ tolerations: [] affinity: {} # -- Liveness probe configuration +# Note: indexing large repos is CPU-intensive and can starve the health +# endpoint, so keep timeouts and failure thresholds generous. livenessProbe: httpGet: path: /health port: backend - initialDelaySeconds: 15 - periodSeconds: 30 - timeoutSeconds: 5 - failureThreshold: 3 + initialDelaySeconds: 30 + periodSeconds: 60 + timeoutSeconds: 15 + failureThreshold: 10 # -- Readiness probe configuration readinessProbe: @@ -156,6 +158,6 @@ readinessProbe: path: /health port: backend initialDelaySeconds: 10 - periodSeconds: 10 - timeoutSeconds: 5 - failureThreshold: 3 + periodSeconds: 15 + timeoutSeconds: 10 + failureThreshold: 10 From d84875dabedeb6ec025d6e6f9501f132603c06a7 Mon Sep 17 00:00:00 2001 From: yashGoyal40 Date: Mon, 6 Apr 2026 19:29:40 +0530 Subject: [PATCH 09/16] fix: disable liveness probe to prevent restarts during indexing MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Indexing large repos is so CPU-intensive that the /health endpoint becomes unresponsive, causing the liveness probe to kill the container repeatedly. Disabled liveness probe by default — readiness probe is kept (it only removes from service, doesn't restart). Co-Authored-By: Claude Opus 4.6 (1M context) --- charts/repowise/values.yaml | 15 +++++---------- 1 file changed, 5 insertions(+), 10 deletions(-) diff --git a/charts/repowise/values.yaml b/charts/repowise/values.yaml index 44003a3..774357a 100644 --- a/charts/repowise/values.yaml +++ b/charts/repowise/values.yaml @@ -141,16 +141,11 @@ tolerations: [] affinity: {} # -- Liveness probe configuration -# Note: indexing large repos is CPU-intensive and can starve the health -# endpoint, so keep timeouts and failure thresholds generous. -livenessProbe: - httpGet: - path: /health - port: backend - initialDelaySeconds: 30 - periodSeconds: 60 - timeoutSeconds: 15 - failureThreshold: 10 +# Disabled by default: indexing large repos is CPU-intensive and can starve +# the health endpoint, causing unnecessary restarts. Use readiness probe +# instead (it only removes from service, doesn't restart). +# Uncomment below to enable if you don't use heavy indexing. +livenessProbe: {} # -- Readiness probe configuration readinessProbe: From d6e4c8f21024d7d76b938a79696d55e42947b0d4 Mon Sep 17 00:00:00 2001 From: yashGoyal40 Date: Mon, 6 Apr 2026 19:53:11 +0530 Subject: [PATCH 10/16] feat: add PostgreSQL support to Helm chart Adds optional PostgreSQL deployment (pgvector/pgvector:pg16) that replaces SQLite, eliminating "database is locked" errors during heavy indexing. Repowise app code already supports PostgreSQL natively. - StatefulSet with PVC for PostgreSQL data - Conditional REPOWISE_DB_URL (asyncpg when PG enabled, aiosqlite otherwise) - wait-for-postgres initContainer ensures DB is ready before app starts - pgvector image includes vector extension for semantic search - Fully backward compatible: postgresql.enabled=false keeps SQLite Co-Authored-By: Claude Opus 4.6 (1M context) --- charts/repowise/templates/deployment.yaml | 20 +++- charts/repowise/templates/postgresql.yaml | 112 ++++++++++++++++++++++ charts/repowise/values.yaml | 20 ++++ 3 files changed, 151 insertions(+), 1 deletion(-) create mode 100644 charts/repowise/templates/postgresql.yaml diff --git a/charts/repowise/templates/deployment.yaml b/charts/repowise/templates/deployment.yaml index aa95bc2..46f005c 100644 --- a/charts/repowise/templates/deployment.yaml +++ b/charts/repowise/templates/deployment.yaml @@ -27,8 +27,21 @@ spec: serviceAccountName: {{ include "repowise.serviceAccountName" . }} securityContext: {{- toYaml .Values.podSecurityContext | nindent 8 }} - {{- if .Values.repos }} + {{- if or .Values.repos .Values.postgresql.enabled }} initContainers: + {{- if .Values.postgresql.enabled }} + - name: wait-for-postgres + image: postgres:16-alpine + command: ["/bin/sh", "-c"] + args: + - | + echo "Waiting for PostgreSQL..." + until pg_isready -h {{ include "repowise.fullname" . }}-postgresql -p 5432 -U {{ .Values.postgresql.auth.username }}; do + sleep 2 + done + echo "PostgreSQL is ready!" + {{- end }} + {{- if .Values.repos }} - name: clone-repos image: bitnami/git:latest command: ["/bin/bash", "-c"] @@ -60,6 +73,7 @@ spec: mountPath: /git-credentials readOnly: true {{- end }} + {{- end }} {{- end }} containers: - name: {{ .Chart.Name }} @@ -76,7 +90,11 @@ spec: protocol: TCP env: - name: REPOWISE_DB_URL + {{- if .Values.postgresql.enabled }} + value: "postgresql+asyncpg://{{ .Values.postgresql.auth.username }}:{{ .Values.postgresql.auth.password }}@{{ include "repowise.fullname" . }}-postgresql:5432/{{ .Values.postgresql.auth.database }}" + {{- else }} value: {{ .Values.repowise.dbUrl | quote }} + {{- end }} - name: REPOWISE_EMBEDDER value: {{ .Values.repowise.embedder | quote }} - name: PORT_BACKEND diff --git a/charts/repowise/templates/postgresql.yaml b/charts/repowise/templates/postgresql.yaml new file mode 100644 index 0000000..be72060 --- /dev/null +++ b/charts/repowise/templates/postgresql.yaml @@ -0,0 +1,112 @@ +{{- if .Values.postgresql.enabled }} +--- +apiVersion: v1 +kind: Secret +metadata: + name: {{ include "repowise.fullname" . }}-postgresql + labels: + {{- include "repowise.labels" . | nindent 4 }} +type: Opaque +data: + POSTGRES_USER: {{ .Values.postgresql.auth.username | b64enc | quote }} + POSTGRES_PASSWORD: {{ .Values.postgresql.auth.password | b64enc | quote }} + POSTGRES_DB: {{ .Values.postgresql.auth.database | b64enc | quote }} +--- +apiVersion: v1 +kind: Service +metadata: + name: {{ include "repowise.fullname" . }}-postgresql + labels: + {{- include "repowise.labels" . | nindent 4 }} + app.kubernetes.io/component: postgresql +spec: + type: ClusterIP + ports: + - port: 5432 + targetPort: 5432 + protocol: TCP + name: postgresql + selector: + {{- include "repowise.selectorLabels" . | nindent 4 }} + app.kubernetes.io/component: postgresql +--- +apiVersion: apps/v1 +kind: StatefulSet +metadata: + name: {{ include "repowise.fullname" . }}-postgresql + labels: + {{- include "repowise.labels" . | nindent 4 }} + app.kubernetes.io/component: postgresql +spec: + serviceName: {{ include "repowise.fullname" . }}-postgresql + replicas: 1 + selector: + matchLabels: + {{- include "repowise.selectorLabels" . | nindent 6 }} + app.kubernetes.io/component: postgresql + template: + metadata: + labels: + {{- include "repowise.selectorLabels" . | nindent 8 }} + app.kubernetes.io/component: postgresql + spec: + containers: + - name: postgresql + image: pgvector/pgvector:pg16 + ports: + - containerPort: 5432 + protocol: TCP + env: + - name: POSTGRES_USER + valueFrom: + secretKeyRef: + name: {{ include "repowise.fullname" . }}-postgresql + key: POSTGRES_USER + - name: POSTGRES_PASSWORD + valueFrom: + secretKeyRef: + name: {{ include "repowise.fullname" . }}-postgresql + key: POSTGRES_PASSWORD + - name: POSTGRES_DB + valueFrom: + secretKeyRef: + name: {{ include "repowise.fullname" . }}-postgresql + key: POSTGRES_DB + - name: PGDATA + value: /var/lib/postgresql/data/pgdata + volumeMounts: + - name: pgdata + mountPath: /var/lib/postgresql/data + livenessProbe: + exec: + command: ["pg_isready", "-U", {{ .Values.postgresql.auth.username | quote }}] + initialDelaySeconds: 15 + periodSeconds: 20 + timeoutSeconds: 5 + readinessProbe: + exec: + command: ["pg_isready", "-U", {{ .Values.postgresql.auth.username | quote }}] + initialDelaySeconds: 5 + periodSeconds: 10 + timeoutSeconds: 5 + resources: + requests: + cpu: 250m + memory: 512Mi + limits: + cpu: 1000m + memory: 1Gi + {{- if .Values.postgresql.primary.persistence.enabled }} + volumeClaimTemplates: + - metadata: + name: pgdata + spec: + accessModes: ["ReadWriteOnce"] + {{- if .Values.postgresql.primary.persistence.storageClass }} + storageClassName: {{ .Values.postgresql.primary.persistence.storageClass | quote }} + {{- end }} + resources: + requests: + storage: {{ .Values.postgresql.primary.persistence.size }} + {{- end }} +{{- end }} diff --git a/charts/repowise/values.yaml b/charts/repowise/values.yaml index 774357a..0a1eed0 100644 --- a/charts/repowise/values.yaml +++ b/charts/repowise/values.yaml @@ -103,6 +103,26 @@ persistence: # -- Annotations for the PVC annotations: {} +# -- PostgreSQL database (replaces SQLite, eliminates "database is locked" errors) +postgresql: + # -- Enable PostgreSQL instead of SQLite + enabled: false + auth: + # -- PostgreSQL username + username: repowise + # -- PostgreSQL password + password: repowise + # -- PostgreSQL database name + database: repowise + primary: + persistence: + # -- Enable persistent storage for PostgreSQL + enabled: true + # -- Storage class (leave empty for cluster default) + storageClass: "" + # -- Volume size + size: 10Gi + # -- Repositories to clone and register on install/upgrade. # A Kubernetes Job runs after deploy to clone each repo into /data/repos/, # register it with the Repowise API, and trigger an initial sync. From 23a6900bd6c242d51f94506fd60d50cdec59969a Mon Sep 17 00:00:00 2001 From: yashGoyal40 Date: Mon, 6 Apr 2026 20:05:33 +0530 Subject: [PATCH 11/16] feat: make PostgreSQL the default database backend PostgreSQL eliminates SQLite's "database is locked" errors during heavy indexing and enables concurrent API access. Uses pgvector image for vector search support. SQLite still available via postgresql.enabled=false. Co-Authored-By: Claude Opus 4.6 (1M context) --- charts/repowise/values.yaml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/charts/repowise/values.yaml b/charts/repowise/values.yaml index 0a1eed0..160682f 100644 --- a/charts/repowise/values.yaml +++ b/charts/repowise/values.yaml @@ -1,4 +1,4 @@ -# -- Number of replicas (only 1 supported with SQLite) +# -- Number of replicas (only 1 supported with SQLite; PostgreSQL allows more) replicaCount: 1 image: @@ -103,10 +103,10 @@ persistence: # -- Annotations for the PVC annotations: {} -# -- PostgreSQL database (replaces SQLite, eliminates "database is locked" errors) +# -- PostgreSQL database (default). Set enabled=false to use SQLite instead. postgresql: - # -- Enable PostgreSQL instead of SQLite - enabled: false + # -- Enable PostgreSQL (recommended). Disable for SQLite (single-user/dev only). + enabled: true auth: # -- PostgreSQL username username: repowise From aab5f6ffa58dd0c3b7b9cbbe4b78cc6f86817231 Mon Sep 17 00:00:00 2001 From: yashGoyal40 Date: Mon, 6 Apr 2026 20:12:06 +0530 Subject: [PATCH 12/16] refactor: remove sequential sync wait (PostgreSQL handles concurrency) With PostgreSQL as default, there's no SQLite lock issue. Repos now trigger sync in parallel without waiting for each to complete. Still skips already-indexed repos (head_commit check). Co-Authored-By: Claude Opus 4.6 (1M context) --- charts/repowise/templates/deployment.yaml | 11 ----------- 1 file changed, 11 deletions(-) diff --git a/charts/repowise/templates/deployment.yaml b/charts/repowise/templates/deployment.yaml index 46f005c..9fda801 100644 --- a/charts/repowise/templates/deployment.yaml +++ b/charts/repowise/templates/deployment.yaml @@ -160,17 +160,6 @@ spec: if [ -n "$REPO_ID" ] && [ -z "$HEAD" ]; then echo " not yet indexed, triggering sync..." curl -sf -X POST "$API/api/repos/$REPO_ID/sync" > /dev/null 2>&1 && echo " sync triggered" || echo " (sync already running)" - # Wait for this sync to finish before next repo (SQLite can't handle concurrent writes) - echo " waiting for sync to complete..." - while true; do - sleep 10 - JOBS=$(curl -sf "$API/api/jobs?repo_id=$REPO_ID&status=running" 2>/dev/null || echo "[]") - PENDING=$(curl -sf "$API/api/jobs?repo_id=$REPO_ID&status=pending" 2>/dev/null || echo "[]") - if [ "$JOBS" = "[]" ] && [ "$PENDING" = "[]" ]; then - echo " sync done." - break - fi - done elif [ -n "$HEAD" ]; then echo " already indexed (head: $HEAD), skipping sync." fi From 3c555c6c847c15737f28607e5453789c215200a7 Mon Sep 17 00:00:00 2001 From: yashGoyal40 Date: Mon, 6 Apr 2026 20:14:59 +0530 Subject: [PATCH 13/16] fix: set git safe.directory so app can read git history initContainer clones repos as root but app runs as uid 1000. Git refuses to read repos with different ownership. Fix: write a .gitconfig with safe.directory=* into /data and set HOME for the app container. This enables hotspots, ownership, and architecture graph features. Co-Authored-By: Claude Opus 4.6 (1M context) --- charts/repowise/templates/deployment.yaml | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/charts/repowise/templates/deployment.yaml b/charts/repowise/templates/deployment.yaml index 9fda801..9474435 100644 --- a/charts/repowise/templates/deployment.yaml +++ b/charts/repowise/templates/deployment.yaml @@ -49,6 +49,11 @@ spec: - | set -e mkdir -p /data/repos + # Allow git to work in repos owned by different users (initContainer runs + # as root, main container as uid 1000). Write gitconfig for both users. + git config --global --add safe.directory '*' + mkdir -p /data/.gitconfig-home + printf '[safe]\n\tdirectory = *\n' > /data/.gitconfig-home/.gitconfig {{- if or .Values.gitCredentials.secretName .Values.gitCredentials.github.token }} git config --global credential.helper 'store --file /tmp/.git-credentials' cp /git-credentials/git-credentials /tmp/.git-credentials @@ -103,6 +108,10 @@ spec: value: {{ .Values.repowise.frontendPort | quote }} - name: HOSTNAME value: "0.0.0.0" + {{- if .Values.repos }} + - name: HOME + value: "/data/.gitconfig-home" + {{- end }} - name: ANTHROPIC_API_KEY valueFrom: secretKeyRef: From eae207a587302fb87bd6271ec858d6cea7b68380 Mon Sep 17 00:00:00 2001 From: yashGoyal40 Date: Mon, 6 Apr 2026 21:06:08 +0530 Subject: [PATCH 14/16] fix: sidecar sleeps after registering repos instead of restarting The register-repos sidecar now sleeps forever after completing its work. This prevents k8s from restarting it in a loop (containers that exit get restarted by default in a pod). Also bumps PostgreSQL to max_connections=4000, shared_buffers=2GB, 8Gi memory limit for heavy indexing workloads. Co-Authored-By: Claude Opus 4.6 (1M context) --- charts/repowise/templates/deployment.yaml | 3 ++- charts/repowise/templates/postgresql.yaml | 11 +++++++---- .../core/src/repowise/core/persistence/database.py | 5 ++++- 3 files changed, 13 insertions(+), 6 deletions(-) diff --git a/charts/repowise/templates/deployment.yaml b/charts/repowise/templates/deployment.yaml index 9474435..7e36c9f 100644 --- a/charts/repowise/templates/deployment.yaml +++ b/charts/repowise/templates/deployment.yaml @@ -173,7 +173,8 @@ spec: echo " already indexed (head: $HEAD), skipping sync." fi {{- end }} - echo "All repos processed. Exiting." + echo "All repos processed. Sleeping forever." + sleep infinity {{- end }} volumes: - name: data diff --git a/charts/repowise/templates/postgresql.yaml b/charts/repowise/templates/postgresql.yaml index be72060..841ba4c 100644 --- a/charts/repowise/templates/postgresql.yaml +++ b/charts/repowise/templates/postgresql.yaml @@ -74,6 +74,9 @@ spec: key: POSTGRES_DB - name: PGDATA value: /var/lib/postgresql/data/pgdata + - name: POSTGRES_INITDB_ARGS + value: "--auth-host=md5" + args: ["-c", "max_connections=4000", "-c", "shared_buffers=2GB", "-c", "work_mem=16MB"] volumeMounts: - name: pgdata mountPath: /var/lib/postgresql/data @@ -91,11 +94,11 @@ spec: timeoutSeconds: 5 resources: requests: - cpu: 250m - memory: 512Mi - limits: cpu: 1000m - memory: 1Gi + memory: 4Gi + limits: + cpu: 4000m + memory: 8Gi {{- if .Values.postgresql.primary.persistence.enabled }} volumeClaimTemplates: - metadata: diff --git a/packages/core/src/repowise/core/persistence/database.py b/packages/core/src/repowise/core/persistence/database.py index c29cd50..e09c432 100644 --- a/packages/core/src/repowise/core/persistence/database.py +++ b/packages/core/src/repowise/core/persistence/database.py @@ -141,8 +141,11 @@ def create_engine( else: kwargs["poolclass"] = NullPool else: - # PostgreSQL — asyncpg handles its own connection pool + # PostgreSQL — increase pool for heavy indexing workloads kwargs["pool_pre_ping"] = True + kwargs["pool_size"] = 20 + kwargs["max_overflow"] = 30 + kwargs["pool_timeout"] = 120 return create_async_engine(db_url, **kwargs) From 9b17b6aa8401dcc2b0f21e702ce3df7e104e457a Mon Sep 17 00:00:00 2001 From: Yash Date: Mon, 6 Apr 2026 21:07:55 +0530 Subject: [PATCH 15/16] Increase database connection pool settings --- packages/core/src/repowise/core/persistence/database.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/core/src/repowise/core/persistence/database.py b/packages/core/src/repowise/core/persistence/database.py index e09c432..17f5e00 100644 --- a/packages/core/src/repowise/core/persistence/database.py +++ b/packages/core/src/repowise/core/persistence/database.py @@ -143,9 +143,9 @@ def create_engine( else: # PostgreSQL — increase pool for heavy indexing workloads kwargs["pool_pre_ping"] = True - kwargs["pool_size"] = 20 - kwargs["max_overflow"] = 30 - kwargs["pool_timeout"] = 120 + kwargs["pool_size"] = 200 + kwargs["max_overflow"] = 300 + kwargs["pool_timeout"] = 1200 return create_async_engine(db_url, **kwargs) From 4414cb5ee07b928f4769c22763c36401136c277d Mon Sep 17 00:00:00 2001 From: Yash Date: Mon, 6 Apr 2026 21:08:18 +0530 Subject: [PATCH 16/16] Update database.py --- packages/core/src/repowise/core/persistence/database.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/core/src/repowise/core/persistence/database.py b/packages/core/src/repowise/core/persistence/database.py index 17f5e00..88284e2 100644 --- a/packages/core/src/repowise/core/persistence/database.py +++ b/packages/core/src/repowise/core/persistence/database.py @@ -145,7 +145,7 @@ def create_engine( kwargs["pool_pre_ping"] = True kwargs["pool_size"] = 200 kwargs["max_overflow"] = 300 - kwargs["pool_timeout"] = 1200 + kwargs["pool_timeout"] = 120 return create_async_engine(db_url, **kwargs)