feat: add helm chart support (#1)
feat: enhance Helm chart workflows and update image references
This commit is contained in:
parent
34653e4baf
commit
6635d70170
12
.github/labeler.yml
vendored
12
.github/labeler.yml
vendored
@ -220,3 +220,15 @@
|
||||
- changed-files:
|
||||
- any-glob-to-any-file:
|
||||
- "extensions/qwen-portal-auth/**"
|
||||
|
||||
"kubernetes":
|
||||
- changed-files:
|
||||
- any-glob-to-any-file:
|
||||
- "charts/**"
|
||||
- "docs/install/kubernetes.md"
|
||||
- ".github/workflows/helm-test.yml"
|
||||
|
||||
"helm":
|
||||
- changed-files:
|
||||
- any-glob-to-any-file:
|
||||
- "charts/**"
|
||||
|
||||
124
.github/workflows/helm-release.yml
vendored
Normal file
124
.github/workflows/helm-release.yml
vendored
Normal file
@ -0,0 +1,124 @@
|
||||
name: Release Helm Chart
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- main
|
||||
paths:
|
||||
- 'charts/**'
|
||||
tags:
|
||||
- 'v*'
|
||||
workflow_dispatch:
|
||||
|
||||
env:
|
||||
REGISTRY: ghcr.io
|
||||
IMAGE_NAME: ${{ github.repository }}
|
||||
|
||||
jobs:
|
||||
# Validate chart on all triggers
|
||||
validate:
|
||||
name: Validate Helm Chart
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Install Helm
|
||||
uses: azure/setup-helm@v4
|
||||
with:
|
||||
version: v3.13.0
|
||||
|
||||
- name: Lint chart
|
||||
run: helm lint charts/clawdbot
|
||||
|
||||
- name: Lint with production values
|
||||
run: helm lint charts/clawdbot -f charts/clawdbot/values.yaml
|
||||
|
||||
- name: Template validation
|
||||
run: helm template test charts/clawdbot --debug
|
||||
|
||||
# Release chart only on tag push
|
||||
release:
|
||||
name: Release Helm Chart
|
||||
runs-on: ubuntu-latest
|
||||
needs: validate
|
||||
if: startsWith(github.ref, 'refs/tags/')
|
||||
permissions:
|
||||
contents: write
|
||||
pages: write
|
||||
id-token: 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@v4
|
||||
with:
|
||||
version: v3.13.0
|
||||
|
||||
- name: Install yq
|
||||
uses: mikefarah/yq@v4
|
||||
|
||||
- name: Extract version from tag
|
||||
id: version
|
||||
run: |
|
||||
VERSION=${GITHUB_REF#refs/tags/v}
|
||||
echo "version=$VERSION" >> $GITHUB_OUTPUT
|
||||
echo "Extracted version: $VERSION"
|
||||
|
||||
- name: Update Chart.yaml with version
|
||||
run: |
|
||||
VERSION="${{ steps.version.outputs.version }}"
|
||||
echo "Updating Chart.yaml with version: $VERSION"
|
||||
yq e '.version = strenv(VERSION)' -i charts/clawdbot/Chart.yaml
|
||||
yq e '.appVersion = strenv(VERSION)' -i charts/clawdbot/Chart.yaml
|
||||
cat charts/clawdbot/Chart.yaml
|
||||
env:
|
||||
VERSION: ${{ steps.version.outputs.version }}
|
||||
|
||||
- name: Update values.yaml with image repository
|
||||
run: |
|
||||
echo "Updating image repository to: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}"
|
||||
yq e '.image.registry = "${{ env.REGISTRY }}"' -i charts/clawdbot/values.yaml
|
||||
yq e '.image.repository = "${{ env.IMAGE_NAME }}"' -i charts/clawdbot/values.yaml
|
||||
cat charts/clawdbot/values.yaml | head -20
|
||||
|
||||
- name: Package Helm chart
|
||||
run: |
|
||||
mkdir -p .cr-release-packages
|
||||
helm package charts/clawdbot -d .cr-release-packages
|
||||
ls -la .cr-release-packages
|
||||
|
||||
- name: Run chart-releaser
|
||||
uses: helm/chart-releaser-action@v1.6.0
|
||||
with:
|
||||
charts_dir: charts
|
||||
skip_existing: true
|
||||
env:
|
||||
CR_TOKEN: "${{ secrets.GITHUB_TOKEN }}"
|
||||
|
||||
# Update GitHub Pages index
|
||||
publish-pages:
|
||||
name: Publish to GitHub Pages
|
||||
runs-on: ubuntu-latest
|
||||
needs: release
|
||||
if: startsWith(github.ref, 'refs/tags/')
|
||||
steps:
|
||||
- name: Checkout gh-pages branch
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
ref: gh-pages
|
||||
fetch-depth: 0
|
||||
|
||||
- name: Verify index updated
|
||||
run: |
|
||||
echo "Chart published successfully"
|
||||
echo "Helm repo: https://clawdbot.github.io/clawdbot"
|
||||
echo "Install with: helm repo add clawdbot https://clawdbot.github.io/clawdbot"
|
||||
125
.github/workflows/helm-test.yml
vendored
Normal file
125
.github/workflows/helm-test.yml
vendored
Normal file
@ -0,0 +1,125 @@
|
||||
name: Helm Chart Tests
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
paths:
|
||||
- 'charts/**'
|
||||
- 'Dockerfile'
|
||||
- '.github/workflows/helm-test.yml'
|
||||
push:
|
||||
branches:
|
||||
- main
|
||||
paths:
|
||||
- 'charts/**'
|
||||
- 'Dockerfile'
|
||||
|
||||
jobs:
|
||||
lint-test:
|
||||
name: Lint and Test Helm Chart
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v6
|
||||
|
||||
- name: Set up Helm
|
||||
uses: azure/setup-helm@v4
|
||||
with:
|
||||
version: v3.13.0
|
||||
|
||||
- name: Lint Helm chart
|
||||
run: |
|
||||
helm lint charts/clawdbot
|
||||
helm lint charts/clawdbot --values charts/clawdbot/examples/values-basic.yaml
|
||||
helm lint charts/clawdbot --values charts/clawdbot/examples/values-production.yaml
|
||||
|
||||
- name: Template validation
|
||||
run: |
|
||||
helm template test charts/clawdbot \
|
||||
--values charts/clawdbot/examples/values-basic.yaml \
|
||||
--set secrets.data.anthropicApiKey=test-key \
|
||||
> /tmp/manifests.yaml
|
||||
cat /tmp/manifests.yaml
|
||||
|
||||
kind-test:
|
||||
name: Test on Kind
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v6
|
||||
|
||||
- name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@v3
|
||||
|
||||
- name: Build Docker image
|
||||
uses: docker/build-push-action@v6
|
||||
with:
|
||||
context: .
|
||||
file: ./Dockerfile
|
||||
tags: clawdbot:test
|
||||
load: true
|
||||
cache-from: type=gha
|
||||
cache-to: type=gha,mode=max
|
||||
|
||||
- name: Create Kind cluster
|
||||
uses: helm/kind-action@v1.8.0
|
||||
with:
|
||||
cluster_name: clawdbot-test
|
||||
config: |
|
||||
kind: Cluster
|
||||
apiVersion: kind.x-k8s.io/v1alpha4
|
||||
nodes:
|
||||
- role: control-plane
|
||||
|
||||
- name: Load image into Kind
|
||||
run: |
|
||||
kind load docker-image clawdbot:test --name clawdbot-test
|
||||
|
||||
- name: Set up Helm
|
||||
uses: azure/setup-helm@v4
|
||||
with:
|
||||
version: v3.13.0
|
||||
|
||||
- name: Install Helm chart
|
||||
run: |
|
||||
helm install test-clawdbot charts/clawdbot \
|
||||
--values charts/clawdbot/examples/values-basic.yaml \
|
||||
--set image.repository=clawdbot \
|
||||
--set image.tag=test \
|
||||
--set image.pullPolicy=Never \
|
||||
--set secrets.data.anthropicApiKey=test-key \
|
||||
--set secrets.data.gatewayToken=$(openssl rand -hex 32) \
|
||||
--wait --timeout=3m
|
||||
|
||||
- name: Verify deployment
|
||||
run: |
|
||||
kubectl get all -l app.kubernetes.io/name=clawdbot
|
||||
kubectl wait --for=condition=ready pod -l app.kubernetes.io/name=clawdbot --timeout=120s
|
||||
kubectl get pvc -l app.kubernetes.io/name=clawdbot
|
||||
|
||||
- name: Check pod logs
|
||||
if: always()
|
||||
run: |
|
||||
POD_NAME=$(kubectl get pod -l app.kubernetes.io/name=clawdbot -o jsonpath='{.items[0].metadata.name}')
|
||||
kubectl logs $POD_NAME --tail=100
|
||||
|
||||
- name: Run Helm tests
|
||||
run: |
|
||||
helm test test-clawdbot
|
||||
|
||||
- name: Test health command
|
||||
run: |
|
||||
POD_NAME=$(kubectl get pod -l app.kubernetes.io/name=clawdbot -o jsonpath='{.items[0].metadata.name}')
|
||||
GATEWAY_TOKEN=$(kubectl get secret test-clawdbot -o jsonpath='{.data.gatewayToken}' | base64 -d)
|
||||
kubectl exec $POD_NAME -- node dist/index.js health --token "$GATEWAY_TOKEN" || true
|
||||
|
||||
- name: Verify persistence
|
||||
run: |
|
||||
POD_NAME=$(kubectl get pod -l app.kubernetes.io/name=clawdbot -o jsonpath='{.items[0].metadata.name}')
|
||||
kubectl exec $POD_NAME -- ls -la /home/node/.clawdbot
|
||||
kubectl exec $POD_NAME -- ls -la /home/node/clawd
|
||||
|
||||
- name: Cleanup
|
||||
if: always()
|
||||
run: |
|
||||
helm uninstall test-clawdbot || true
|
||||
kubectl delete pvc -l app.kubernetes.io/name=clawdbot || true
|
||||
@ -21,7 +21,7 @@ It answers you on the channels you already use (WhatsApp, Telegram, Slack, Disco
|
||||
|
||||
If you want a personal, single-user assistant that feels local, fast, and always-on, this is it.
|
||||
|
||||
[Website](https://molt.bot) · [Docs](https://docs.molt.bot) · [Getting Started](https://docs.molt.bot/start/getting-started) · [Updating](https://docs.molt.bot/install/updating) · [Showcase](https://docs.molt.bot/start/showcase) · [FAQ](https://docs.molt.bot/start/faq) · [Wizard](https://docs.molt.bot/start/wizard) · [Nix](https://github.com/moltbot/nix-clawdbot) · [Docker](https://docs.molt.bot/install/docker) · [Discord](https://discord.gg/clawd)
|
||||
[Website](https://molt.bot) · [Docs](https://docs.molt.bot) · [Getting Started](https://docs.molt.bot/start/getting-started) · [Updating](https://docs.molt.bot/install/updating) · [Showcase](https://docs.molt.bot/start/showcase) · [FAQ](https://docs.molt.bot/start/faq) · [Wizard](https://docs.molt.bot/start/wizard) · [Nix](https://github.com/moltbot/nix-clawdbot) · [Docker](https://docs.molt.bot/install/docker) · [Kubernetes](https://docs.molt.bot/install/kubernetes) · [Discord](https://discord.gg/clawd)
|
||||
|
||||
Preferred setup: run the onboarding wizard (`moltbot onboard`). It walks through gateway, workspace, channels, and skills. The CLI wizard is the recommended path and works on **macOS, Linux, and Windows (via WSL2; strongly recommended)**.
|
||||
Works with npm, pnpm, or bun.
|
||||
@ -164,7 +164,7 @@ Run `moltbot doctor` to surface risky/misconfigured DM policies.
|
||||
### Ops + packaging
|
||||
- [Control UI](https://docs.molt.bot/web) + [WebChat](https://docs.molt.bot/web/webchat) served directly from the Gateway.
|
||||
- [Tailscale Serve/Funnel](https://docs.molt.bot/gateway/tailscale) or [SSH tunnels](https://docs.molt.bot/gateway/remote) with token/password auth.
|
||||
- [Nix mode](https://docs.molt.bot/install/nix) for declarative config; [Docker](https://docs.molt.bot/install/docker)-based installs.
|
||||
- [Nix mode](https://docs.molt.bot/install/nix) for declarative config; [Docker](https://docs.molt.bot/install/docker) and [Kubernetes (Helm)](https://docs.molt.bot/install/kubernetes) deployments.
|
||||
- [Doctor](https://docs.molt.bot/gateway/doctor) migrations, [logging](https://docs.molt.bot/logging).
|
||||
|
||||
## How it works (short)
|
||||
|
||||
23
charts/clawdbot/.helmignore
Normal file
23
charts/clawdbot/.helmignore
Normal file
@ -0,0 +1,23 @@
|
||||
# Patterns to ignore when building packages.
|
||||
# This supports shell glob matching, relative path matching, and
|
||||
# negation (prefixed with !). Only one pattern per line.
|
||||
.DS_Store
|
||||
# Common VCS dirs
|
||||
.git/
|
||||
.gitignore
|
||||
.bzr/
|
||||
.bzrignore
|
||||
.hg/
|
||||
.hgignore
|
||||
.svn/
|
||||
# Common backup files
|
||||
*.swp
|
||||
*.bak
|
||||
*.tmp
|
||||
*.orig
|
||||
*~
|
||||
# Various IDEs
|
||||
.project
|
||||
.idea/
|
||||
*.tmproj
|
||||
.vscode/
|
||||
21
charts/clawdbot/Chart.yaml
Normal file
21
charts/clawdbot/Chart.yaml
Normal file
@ -0,0 +1,21 @@
|
||||
apiVersion: v2
|
||||
name: clawdbot
|
||||
description: Personal AI assistant with multi-channel support
|
||||
type: application
|
||||
version: 1.0.0
|
||||
appVersion: "2026.1.25"
|
||||
keywords:
|
||||
- ai
|
||||
- assistant
|
||||
- whatsapp
|
||||
- telegram
|
||||
- discord
|
||||
- claude
|
||||
- chatgpt
|
||||
home: https://github.com/clawdbot/clawdbot
|
||||
sources:
|
||||
- https://github.com/clawdbot/clawdbot
|
||||
maintainers:
|
||||
- name: Clawdbot Team
|
||||
url: https://docs.clawd.bot
|
||||
icon: https://github.com/clawdbot/clawdbot/raw/main/README-header.png
|
||||
253
charts/clawdbot/PUBLISHING.md
Normal file
253
charts/clawdbot/PUBLISHING.md
Normal file
@ -0,0 +1,253 @@
|
||||
# Publishing the Clawdbot Helm Chart
|
||||
|
||||
This guide covers how to publish the Helm chart to make it available for users.
|
||||
|
||||
## Quick Start (Automated)
|
||||
|
||||
The chart is automatically published to GitHub Pages when you:
|
||||
|
||||
1. Update `charts/clawdbot/Chart.yaml` version
|
||||
2. Commit and push to `main` branch
|
||||
3. GitHub Actions automatically packages and publishes
|
||||
|
||||
## Publishing Methods
|
||||
|
||||
### Option 1: GitHub Pages (Recommended)
|
||||
|
||||
#### Initial Setup
|
||||
|
||||
1. **Enable GitHub Pages:**
|
||||
- Go to repository Settings → Pages
|
||||
- Source: Deploy from a branch
|
||||
- Branch: `gh-pages` → `/ (root)`
|
||||
- Click Save
|
||||
|
||||
2. **Create gh-pages branch (first time only):**
|
||||
```bash
|
||||
# Create empty gh-pages branch
|
||||
git checkout --orphan gh-pages
|
||||
git rm -rf .
|
||||
echo "# Clawdbot Helm Charts" > README.md
|
||||
git add README.md
|
||||
git commit -m "Initial gh-pages"
|
||||
git push origin gh-pages
|
||||
git checkout main
|
||||
```
|
||||
|
||||
3. **The workflow will automatically:**
|
||||
- Package the chart
|
||||
- Create a GitHub release
|
||||
- Update the chart repository index
|
||||
- Publish to GitHub Pages
|
||||
|
||||
#### Manual Publishing (if needed)
|
||||
|
||||
```bash
|
||||
# 1. Package the chart
|
||||
helm package charts/clawdbot -d .cr-release-packages
|
||||
|
||||
# 2. Create index
|
||||
helm repo index .cr-release-packages --url https://clawdbot.github.io/clawdbot
|
||||
|
||||
# 3. Commit to gh-pages branch
|
||||
git checkout gh-pages
|
||||
cp .cr-release-packages/* .
|
||||
git add .
|
||||
git commit -m "Release chart version X.Y.Z"
|
||||
git push origin gh-pages
|
||||
git checkout main
|
||||
```
|
||||
|
||||
#### Usage for End Users
|
||||
|
||||
Once published, users can install via:
|
||||
|
||||
```bash
|
||||
# Add repo
|
||||
helm repo add clawdbot https://clawdbot.github.io/clawdbot
|
||||
helm repo update
|
||||
|
||||
# Install
|
||||
helm install my-clawdbot clawdbot/clawdbot
|
||||
```
|
||||
|
||||
### Option 2: OCI Registry (GitHub Container Registry)
|
||||
|
||||
Modern approach using OCI registries:
|
||||
|
||||
#### Setup
|
||||
|
||||
```bash
|
||||
# Login to GHCR
|
||||
echo $GITHUB_TOKEN | helm registry login ghcr.io -u USERNAME --password-stdin
|
||||
|
||||
# Package chart
|
||||
helm package charts/clawdbot
|
||||
|
||||
# Push to GHCR
|
||||
helm push clawdbot-1.0.0.tgz oci://ghcr.io/clawdbot
|
||||
```
|
||||
|
||||
#### Automate in GitHub Actions
|
||||
|
||||
```yaml
|
||||
- name: Login to GHCR
|
||||
uses: docker/login-action@v3
|
||||
with:
|
||||
registry: ghcr.io
|
||||
username: ${{ github.actor }}
|
||||
password: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
- name: Push chart to GHCR
|
||||
run: |
|
||||
helm package charts/clawdbot
|
||||
helm push clawdbot-*.tgz oci://ghcr.io/${{ github.repository_owner }}
|
||||
```
|
||||
|
||||
#### Usage for End Users
|
||||
|
||||
```bash
|
||||
# Install directly from OCI
|
||||
helm install my-clawdbot oci://ghcr.io/clawdbot/clawdbot --version 1.0.0
|
||||
```
|
||||
|
||||
### Option 3: Artifact Hub
|
||||
|
||||
Make your chart discoverable on [Artifact Hub](https://artifacthub.io).
|
||||
|
||||
#### Prerequisites
|
||||
|
||||
- Chart published to GitHub Pages or OCI registry
|
||||
- Artifact Hub metadata file
|
||||
|
||||
#### Add Artifact Hub metadata
|
||||
|
||||
Create `charts/clawdbot/artifacthub-repo.yml`:
|
||||
|
||||
```yaml
|
||||
repositoryID: <your-repo-id>
|
||||
owners:
|
||||
- name: Clawdbot Team
|
||||
email: team@clawdbot.com
|
||||
```
|
||||
|
||||
#### Submit to Artifact Hub
|
||||
|
||||
1. Go to https://artifacthub.io
|
||||
2. Sign in with GitHub
|
||||
3. Add repository
|
||||
4. Provide repository URL: `https://clawdbot.github.io/clawdbot`
|
||||
5. Wait for verification
|
||||
|
||||
### Option 4: ChartMuseum (Self-Hosted)
|
||||
|
||||
For private/internal charts:
|
||||
|
||||
```bash
|
||||
# Run ChartMuseum
|
||||
docker run -d \
|
||||
-p 8080:8080 \
|
||||
-v $(pwd)/charts:/charts \
|
||||
ghcr.io/helm/chartmuseum:latest \
|
||||
--storage local \
|
||||
--storage-local-rootdir /charts
|
||||
|
||||
# Upload chart
|
||||
curl --data-binary "@clawdbot-1.0.0.tgz" http://localhost:8080/api/charts
|
||||
```
|
||||
|
||||
## Versioning
|
||||
|
||||
Follow Semantic Versioning:
|
||||
|
||||
- **Chart version** (`version` in Chart.yaml): Chart changes
|
||||
- **App version** (`appVersion` in Chart.yaml): Clawdbot version
|
||||
|
||||
### Bumping Versions
|
||||
|
||||
```bash
|
||||
# Update chart version
|
||||
vim charts/clawdbot/Chart.yaml
|
||||
# Change version: 1.0.0 → 1.1.0
|
||||
|
||||
# Update app version (when clawdbot version changes)
|
||||
vim charts/clawdbot/Chart.yaml
|
||||
# Change appVersion: "2026.1.25" → "2026.1.26"
|
||||
```
|
||||
|
||||
### Version Guidelines
|
||||
|
||||
- **Major** (1.0.0 → 2.0.0): Breaking changes to values.yaml or behavior
|
||||
- **Minor** (1.0.0 → 1.1.0): New features, non-breaking changes
|
||||
- **Patch** (1.0.0 → 1.0.1): Bug fixes, documentation
|
||||
|
||||
## Release Checklist
|
||||
|
||||
- [ ] Update `version` in `Chart.yaml`
|
||||
- [ ] Update `appVersion` if clawdbot version changed
|
||||
- [ ] Update `CHANGELOG.md` (if you have one)
|
||||
- [ ] Test chart locally: `./scripts/test-helm-local.sh`
|
||||
- [ ] Lint chart: `helm lint charts/clawdbot`
|
||||
- [ ] Commit changes
|
||||
- [ ] Push to main (triggers automated release)
|
||||
- [ ] Verify GitHub Pages deployment
|
||||
- [ ] Test installation from published repo
|
||||
|
||||
## Testing Published Chart
|
||||
|
||||
```bash
|
||||
# Add your published repo
|
||||
helm repo add clawdbot https://clawdbot.github.io/clawdbot
|
||||
helm repo update
|
||||
|
||||
# Search for chart
|
||||
helm search repo clawdbot
|
||||
|
||||
# Install from published repo
|
||||
helm install test clawdbot/clawdbot --dry-run --debug
|
||||
```
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
### Chart not appearing after publish
|
||||
|
||||
1. Check GitHub Actions logs
|
||||
2. Verify gh-pages branch exists
|
||||
3. Check GitHub Pages settings are enabled
|
||||
4. Wait 5-10 minutes for GitHub Pages to deploy
|
||||
|
||||
### Users getting "not found" error
|
||||
|
||||
```bash
|
||||
# Check index.yaml exists
|
||||
curl https://clawdbot.github.io/clawdbot/index.yaml
|
||||
|
||||
# Verify chart package exists
|
||||
curl https://clawdbot.github.io/clawdbot/clawdbot-1.0.0.tgz
|
||||
```
|
||||
|
||||
### Permission denied during publishing
|
||||
|
||||
Ensure GitHub Actions has write permissions:
|
||||
- Settings → Actions → General → Workflow permissions
|
||||
- Select "Read and write permissions"
|
||||
|
||||
## Advanced: Multi-Chart Repository
|
||||
|
||||
If you add more charts:
|
||||
|
||||
```
|
||||
charts/
|
||||
clawdbot/
|
||||
clawdbot-operator/
|
||||
clawdbot-monitoring/
|
||||
```
|
||||
|
||||
The `helm/chart-releaser-action` automatically handles multiple charts.
|
||||
|
||||
## References
|
||||
|
||||
- [Helm Chart Repository Guide](https://helm.sh/docs/topics/chart_repository/)
|
||||
- [Chart Releaser Action](https://github.com/helm/chart-releaser-action)
|
||||
- [Artifact Hub](https://artifacthub.io/docs/topics/repositories/)
|
||||
- [OCI Registry Support](https://helm.sh/docs/topics/registries/)
|
||||
302
charts/clawdbot/README.md
Normal file
302
charts/clawdbot/README.md
Normal file
@ -0,0 +1,302 @@
|
||||
# Clawdbot Helm Chart
|
||||
|
||||
Personal AI assistant with multi-channel support (WhatsApp, Telegram, Discord, and more) running on Kubernetes.
|
||||
|
||||
## TL;DR
|
||||
|
||||
```bash
|
||||
helm install my-clawdbot ./clawdbot \
|
||||
--set secrets.data.anthropicApiKey=sk-ant-xxx \
|
||||
--set secrets.data.gatewayToken=$(openssl rand -hex 32)
|
||||
```
|
||||
|
||||
## Introduction
|
||||
|
||||
This chart deploys Clawdbot Gateway on a Kubernetes cluster using the Helm package manager.
|
||||
|
||||
**Important:** Clawdbot is designed as a single-user personal assistant. The chart enforces `replicas: 1` and does not support horizontal scaling.
|
||||
|
||||
## Prerequisites
|
||||
|
||||
- Kubernetes 1.19+
|
||||
- Helm 3.x
|
||||
- PV provisioner support in the underlying infrastructure (for persistent storage)
|
||||
- **Optional:** Ingress controller (NGINX, Traefik, etc.) for external access
|
||||
- **Optional:** cert-manager for automatic TLS certificates
|
||||
|
||||
## Installing the Chart
|
||||
|
||||
### Basic Installation
|
||||
|
||||
```bash
|
||||
helm install my-clawdbot ./clawdbot
|
||||
```
|
||||
|
||||
### With Custom Values
|
||||
|
||||
```bash
|
||||
helm install my-clawdbot ./clawdbot \
|
||||
--values examples/values-production.yaml \
|
||||
--set secrets.data.anthropicApiKey=sk-ant-xxx \
|
||||
--set ingress.hosts[0].host=assistant.example.com
|
||||
```
|
||||
|
||||
### From Examples
|
||||
|
||||
```bash
|
||||
# Basic (local testing)
|
||||
helm install my-clawdbot ./clawdbot -f examples/values-basic.yaml
|
||||
|
||||
# Production
|
||||
helm install my-clawdbot ./clawdbot -f examples/values-production.yaml
|
||||
|
||||
# Fly.io-like setup
|
||||
helm install my-clawdbot ./clawdbot -f examples/values-fly-like.yaml
|
||||
```
|
||||
|
||||
## Uninstalling the Chart
|
||||
|
||||
```bash
|
||||
helm uninstall my-clawdbot
|
||||
|
||||
# Also delete PVCs (data will be lost)
|
||||
kubectl delete pvc -l app.kubernetes.io/instance=my-clawdbot
|
||||
```
|
||||
|
||||
## Configuration
|
||||
|
||||
### Key Parameters
|
||||
|
||||
| Parameter | Description | Default |
|
||||
|-----------|-------------|---------|
|
||||
| `replicaCount` | Number of replicas (must be 1) | `1` |
|
||||
| `image.repository` | Clawdbot image repository | `clawdbot/clawdbot` |
|
||||
| `image.tag` | Image tag | Chart appVersion |
|
||||
| `gateway.bind` | Gateway binding mode (`loopback`, `lan`, `auto`) | `lan` |
|
||||
| `gateway.port` | Gateway port | `18789` |
|
||||
| `secrets.data.anthropicApiKey` | Anthropic API key | `""` |
|
||||
| `secrets.data.openaiApiKey` | OpenAI API key | `""` |
|
||||
| `secrets.data.gatewayToken` | Gateway authentication token (auto-generated if empty) | `""` |
|
||||
| `persistence.enabled` | Enable persistent storage | `true` |
|
||||
| `persistence.size` | PVC size | `10Gi` |
|
||||
| `ingress.enabled` | Enable Ingress | `false` |
|
||||
| `ingress.className` | Ingress class | `nginx` |
|
||||
| `resources.limits.memory` | Memory limit | `2Gi` |
|
||||
| `resources.requests.memory` | Memory request | `512Mi` |
|
||||
|
||||
### Full Values Reference
|
||||
|
||||
See [values.yaml](values.yaml) for all available parameters.
|
||||
|
||||
## Storage
|
||||
|
||||
The chart creates a StatefulSet with a persistent volume claim template that mounts storage at two locations:
|
||||
|
||||
- `/home/node/.clawdbot` (subPath: `clawdbot-state`) - Configuration, sessions, device identity, SQLite databases
|
||||
- `/home/node/clawd` (subPath: `clawdbot-workspace`) - Agent workspace files
|
||||
|
||||
**Storage Class:** By default, uses the cluster's default storage class. Override with `persistence.storageClass`.
|
||||
|
||||
**Size Recommendations:**
|
||||
- Development/Testing: 5-10Gi
|
||||
- Production (single user): 20Gi+
|
||||
- Depends on session history and workspace usage
|
||||
|
||||
## Secrets Management
|
||||
|
||||
### Option 1: Inline Secrets (Development Only)
|
||||
|
||||
```bash
|
||||
helm install my-clawdbot ./clawdbot \
|
||||
--set secrets.data.anthropicApiKey=sk-ant-xxx \
|
||||
--set secrets.data.gatewayToken=$(openssl rand -hex 32)
|
||||
```
|
||||
|
||||
### Option 2: External Secret (Production)
|
||||
|
||||
Create a Kubernetes Secret:
|
||||
|
||||
```bash
|
||||
kubectl create secret generic clawdbot-secrets \
|
||||
--from-literal=gatewayToken=$(openssl rand -hex 32) \
|
||||
--from-literal=anthropicApiKey=sk-ant-xxx \
|
||||
--from-literal=discordBotToken=MTQ...
|
||||
```
|
||||
|
||||
Install with existing secret:
|
||||
|
||||
```bash
|
||||
helm install my-clawdbot ./clawdbot \
|
||||
--set secrets.create=false \
|
||||
--set secrets.existingSecret=clawdbot-secrets
|
||||
```
|
||||
|
||||
### Option 3: External Secrets Operator
|
||||
|
||||
Use External Secrets Operator to sync from Vault, AWS Secrets Manager, etc.
|
||||
|
||||
## Ingress
|
||||
|
||||
### NGINX Ingress Controller
|
||||
|
||||
```yaml
|
||||
ingress:
|
||||
enabled: true
|
||||
className: nginx
|
||||
annotations:
|
||||
cert-manager.io/cluster-issuer: letsencrypt-prod
|
||||
nginx.ingress.kubernetes.io/proxy-read-timeout: "3600"
|
||||
nginx.ingress.kubernetes.io/proxy-send-timeout: "3600"
|
||||
nginx.ingress.kubernetes.io/websocket-services: "{{ include \"clawdbot.fullname\" . }}"
|
||||
hosts:
|
||||
- host: assistant.example.com
|
||||
paths:
|
||||
- path: /
|
||||
pathType: Prefix
|
||||
tls:
|
||||
- secretName: clawdbot-tls
|
||||
hosts:
|
||||
- assistant.example.com
|
||||
```
|
||||
|
||||
**Important:** WebSocket support requires extended timeouts (3600s recommended).
|
||||
|
||||
### Traefik
|
||||
|
||||
```yaml
|
||||
ingress:
|
||||
className: traefik
|
||||
annotations:
|
||||
traefik.ingress.kubernetes.io/router.entrypoints: websecure
|
||||
traefik.ingress.kubernetes.io/router.tls: "true"
|
||||
```
|
||||
|
||||
## Health Checks
|
||||
|
||||
The chart uses exec-based probes that run the CLI health command:
|
||||
|
||||
```yaml
|
||||
livenessProbe:
|
||||
exec:
|
||||
command:
|
||||
- sh
|
||||
- -c
|
||||
- node dist/index.js health --token "${CLAWDBOT_GATEWAY_TOKEN}" || exit 1
|
||||
```
|
||||
|
||||
## Upgrading
|
||||
|
||||
```bash
|
||||
# Pull latest chart changes
|
||||
git pull
|
||||
|
||||
# Upgrade with current values
|
||||
helm upgrade my-clawdbot ./clawdbot --reuse-values
|
||||
|
||||
# Upgrade with new values
|
||||
helm upgrade my-clawdbot ./clawdbot -f values-production.yaml
|
||||
```
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
### Pod Not Starting
|
||||
|
||||
```bash
|
||||
# Check pod events
|
||||
kubectl describe pod my-clawdbot-0
|
||||
|
||||
# Check logs
|
||||
kubectl logs my-clawdbot-0
|
||||
```
|
||||
|
||||
### OOM (Out of Memory)
|
||||
|
||||
Increase memory limits:
|
||||
|
||||
```yaml
|
||||
resources:
|
||||
limits:
|
||||
memory: 4Gi
|
||||
requests:
|
||||
memory: 1Gi
|
||||
```
|
||||
|
||||
**Note:** 512MB is too small for production. 2GB recommended minimum.
|
||||
|
||||
### PVC Not Binding
|
||||
|
||||
```bash
|
||||
# Check PVC status
|
||||
kubectl get pvc
|
||||
|
||||
# Check storage class
|
||||
kubectl get storageclass
|
||||
|
||||
# Describe for events
|
||||
kubectl describe pvc data-my-clawdbot-0
|
||||
```
|
||||
|
||||
### WebSocket Connections Timing Out
|
||||
|
||||
Ensure Ingress has WebSocket annotations:
|
||||
|
||||
```yaml
|
||||
annotations:
|
||||
nginx.ingress.kubernetes.io/proxy-read-timeout: "3600"
|
||||
nginx.ingress.kubernetes.io/proxy-send-timeout: "3600"
|
||||
```
|
||||
|
||||
### Gateway Lock File Issues
|
||||
|
||||
If the gateway won't start due to stale lock files:
|
||||
|
||||
```bash
|
||||
kubectl exec my-clawdbot-0 -- rm -f /home/node/.clawdbot/gateway.*.lock
|
||||
kubectl delete pod my-clawdbot-0 # Restart pod
|
||||
```
|
||||
|
||||
## Examples
|
||||
|
||||
### Local Testing (Minikube/Kind/Docker Desktop)
|
||||
|
||||
```bash
|
||||
# Build local image
|
||||
docker build -t clawdbot:local .
|
||||
|
||||
# Load into cluster (example for Minikube)
|
||||
minikube image load clawdbot:local
|
||||
|
||||
# Install chart
|
||||
helm install test ./clawdbot \
|
||||
-f examples/values-basic.yaml \
|
||||
--set image.repository=clawdbot \
|
||||
--set image.tag=local \
|
||||
--set image.pullPolicy=Never
|
||||
```
|
||||
|
||||
### Production Deployment
|
||||
|
||||
```bash
|
||||
# Create external secret first
|
||||
kubectl create secret generic clawdbot-secrets \
|
||||
--from-literal=gatewayToken=$(openssl rand -hex 32) \
|
||||
--from-literal=anthropicApiKey=$ANTHROPIC_API_KEY
|
||||
|
||||
# Install with production values
|
||||
helm install my-clawdbot ./clawdbot -f examples/values-production.yaml
|
||||
```
|
||||
|
||||
## Documentation
|
||||
|
||||
- [Clawdbot Documentation](https://docs.clawd.bot)
|
||||
- [Kubernetes Installation Guide](https://docs.clawd.bot/install/kubernetes)
|
||||
- [GitHub Repository](https://github.com/clawdbot/clawdbot)
|
||||
|
||||
## Support
|
||||
|
||||
- [GitHub Issues](https://github.com/clawdbot/clawdbot/issues)
|
||||
- [Documentation](https://docs.clawd.bot)
|
||||
|
||||
## License
|
||||
|
||||
See [LICENSE](https://github.com/clawdbot/clawdbot/blob/main/LICENSE)
|
||||
33
charts/clawdbot/examples/values-basic.yaml
Normal file
33
charts/clawdbot/examples/values-basic.yaml
Normal file
@ -0,0 +1,33 @@
|
||||
# Basic configuration for local testing or development
|
||||
|
||||
image:
|
||||
repository: clawdbot
|
||||
tag: local
|
||||
pullPolicy: Never
|
||||
|
||||
gateway:
|
||||
bind: lan
|
||||
port: 18789
|
||||
allowUnconfigured: false
|
||||
|
||||
secrets:
|
||||
create: true
|
||||
data:
|
||||
# Set via --set or environment variables
|
||||
anthropicApiKey: ""
|
||||
gatewayToken: ""
|
||||
|
||||
persistence:
|
||||
enabled: true
|
||||
size: 5Gi
|
||||
|
||||
ingress:
|
||||
enabled: false
|
||||
|
||||
resources:
|
||||
limits:
|
||||
memory: 2Gi
|
||||
cpu: 1000m
|
||||
requests:
|
||||
memory: 512Mi
|
||||
cpu: 250m
|
||||
58
charts/clawdbot/examples/values-fly-like.yaml
Normal file
58
charts/clawdbot/examples/values-fly-like.yaml
Normal file
@ -0,0 +1,58 @@
|
||||
# Configuration similar to Fly.io deployment
|
||||
# Mimics fly.toml settings
|
||||
|
||||
image:
|
||||
repository: ghcr.io/clawdbot/clawdbot
|
||||
tag: "2026.1.25"
|
||||
pullPolicy: IfNotPresent
|
||||
|
||||
gateway:
|
||||
bind: lan
|
||||
port: 3000 # Fly.io uses port 3000
|
||||
allowUnconfigured: true
|
||||
extraArgs: []
|
||||
|
||||
env:
|
||||
NODE_ENV: production
|
||||
CLAWDBOT_STATE_DIR: /home/node/.clawdbot
|
||||
CLAWDBOT_WORKSPACE_DIR: /home/node/clawd
|
||||
NODE_OPTIONS: "--max-old-space-size=1536" # Fly.io recommendation
|
||||
|
||||
secrets:
|
||||
create: false
|
||||
existingSecret: clawdbot-secrets
|
||||
|
||||
persistence:
|
||||
enabled: true
|
||||
size: 1Gi # Similar to Fly.io volume size
|
||||
|
||||
resources:
|
||||
limits:
|
||||
memory: 2Gi # shared-cpu-2x on Fly.io
|
||||
cpu: 1000m
|
||||
requests:
|
||||
memory: 512Mi
|
||||
cpu: 250m
|
||||
|
||||
service:
|
||||
type: ClusterIP
|
||||
port: 3000
|
||||
|
||||
ingress:
|
||||
enabled: true
|
||||
className: nginx
|
||||
annotations:
|
||||
cert-manager.io/cluster-issuer: letsencrypt-prod
|
||||
nginx.ingress.kubernetes.io/force-ssl-redirect: "true"
|
||||
nginx.ingress.kubernetes.io/proxy-read-timeout: "3600"
|
||||
nginx.ingress.kubernetes.io/proxy-send-timeout: "3600"
|
||||
nginx.ingress.kubernetes.io/websocket-services: "clawdbot"
|
||||
hosts:
|
||||
- host: my-clawdbot.example.com
|
||||
paths:
|
||||
- path: /
|
||||
pathType: Prefix
|
||||
tls:
|
||||
- secretName: clawdbot-tls
|
||||
hosts:
|
||||
- my-clawdbot.example.com
|
||||
92
charts/clawdbot/examples/values-production.yaml
Normal file
92
charts/clawdbot/examples/values-production.yaml
Normal file
@ -0,0 +1,92 @@
|
||||
# Production configuration with Ingress and external secrets
|
||||
|
||||
image:
|
||||
repository: ghcr.io/clawdbot/clawdbot
|
||||
tag: "2026.1.25"
|
||||
pullPolicy: IfNotPresent
|
||||
|
||||
gateway:
|
||||
bind: lan
|
||||
port: 18789
|
||||
allowUnconfigured: false
|
||||
|
||||
# Use external secret in production
|
||||
secrets:
|
||||
create: false
|
||||
existingSecret: clawdbot-secrets
|
||||
|
||||
config:
|
||||
create: true
|
||||
data:
|
||||
agents:
|
||||
defaults:
|
||||
model:
|
||||
primary: "anthropic/claude-opus-4-5"
|
||||
fallbacks:
|
||||
- "anthropic/claude-sonnet-4-5"
|
||||
maxConcurrent: 4
|
||||
sandbox:
|
||||
mode: "off"
|
||||
list:
|
||||
- id: main
|
||||
default: true
|
||||
auth:
|
||||
profiles:
|
||||
"anthropic:default":
|
||||
mode: token
|
||||
provider: anthropic
|
||||
gateway:
|
||||
mode: local
|
||||
bind: auto
|
||||
auth:
|
||||
mode: token
|
||||
controlUi:
|
||||
enabled: true
|
||||
channels:
|
||||
discord:
|
||||
enabled: true
|
||||
meta:
|
||||
lastTouchedVersion: "2026.1.25"
|
||||
|
||||
persistence:
|
||||
enabled: true
|
||||
storageClass: fast-ssd
|
||||
size: 20Gi
|
||||
accessMode: ReadWriteOnce
|
||||
|
||||
resources:
|
||||
limits:
|
||||
memory: 4Gi
|
||||
cpu: 2000m
|
||||
requests:
|
||||
memory: 1Gi
|
||||
cpu: 500m
|
||||
|
||||
ingress:
|
||||
enabled: true
|
||||
className: nginx
|
||||
annotations:
|
||||
cert-manager.io/cluster-issuer: letsencrypt-prod
|
||||
nginx.ingress.kubernetes.io/proxy-read-timeout: "3600"
|
||||
nginx.ingress.kubernetes.io/proxy-send-timeout: "3600"
|
||||
nginx.ingress.kubernetes.io/websocket-services: "clawdbot"
|
||||
hosts:
|
||||
- host: assistant.example.com
|
||||
paths:
|
||||
- path: /
|
||||
pathType: Prefix
|
||||
tls:
|
||||
- secretName: clawdbot-tls
|
||||
hosts:
|
||||
- assistant.example.com
|
||||
|
||||
affinity:
|
||||
nodeAffinity:
|
||||
preferredDuringSchedulingIgnoredDuringExecution:
|
||||
- weight: 100
|
||||
preference:
|
||||
matchExpressions:
|
||||
- key: workload-type
|
||||
operator: In
|
||||
values:
|
||||
- stateful
|
||||
46
charts/clawdbot/templates/NOTES.txt
Normal file
46
charts/clawdbot/templates/NOTES.txt
Normal file
@ -0,0 +1,46 @@
|
||||
Thank you for installing {{ .Chart.Name }}!
|
||||
|
||||
Your Clawdbot gateway is starting up. This may take 1-2 minutes.
|
||||
|
||||
1. Check the gateway status:
|
||||
|
||||
kubectl get statefulset {{ include "clawdbot.fullname" . }} -n {{ .Release.Namespace }}
|
||||
kubectl logs -f {{ include "clawdbot.fullname" . }}-0 -n {{ .Release.Namespace }}
|
||||
|
||||
2. Access the Control UI:
|
||||
|
||||
{{- if .Values.ingress.enabled }}
|
||||
{{- range .Values.ingress.hosts }}
|
||||
https://{{ .host }}
|
||||
{{- end }}
|
||||
{{- else }}
|
||||
# Port-forward to access locally:
|
||||
kubectl port-forward {{ include "clawdbot.fullname" . }}-0 {{ .Values.gateway.port }}:{{ .Values.gateway.port }} -n {{ .Release.Namespace }}
|
||||
|
||||
Then visit: http://localhost:{{ .Values.gateway.port }}
|
||||
{{- end }}
|
||||
|
||||
3. Your gateway token (for authentication):
|
||||
|
||||
kubectl get secret {{ include "clawdbot.secretName" . }} -n {{ .Release.Namespace }} -o jsonpath='{.data.gatewayToken}' | base64 -d && echo
|
||||
|
||||
4. Configure channels:
|
||||
|
||||
kubectl exec -it {{ include "clawdbot.fullname" . }}-0 -n {{ .Release.Namespace }} -- node dist/index.js channels add --channel discord --token YOUR_BOT_TOKEN
|
||||
|
||||
5. Check health:
|
||||
|
||||
kubectl exec -it {{ include "clawdbot.fullname" . }}-0 -n {{ .Release.Namespace }} -- node dist/index.js health
|
||||
|
||||
Documentation: https://docs.clawd.bot
|
||||
Support: https://github.com/clawdbot/clawdbot/issues
|
||||
|
||||
{{- if not .Values.persistence.enabled }}
|
||||
|
||||
WARNING: Persistence is disabled. State will be lost on pod restart.
|
||||
{{- end }}
|
||||
|
||||
{{- if eq .Values.gateway.bind "loopback" }}
|
||||
|
||||
WARNING: Gateway is bound to loopback. External access will not work.
|
||||
{{- end }}
|
||||
84
charts/clawdbot/templates/_helpers.tpl
Normal file
84
charts/clawdbot/templates/_helpers.tpl
Normal file
@ -0,0 +1,84 @@
|
||||
{{/*
|
||||
Expand the name of the chart.
|
||||
*/}}
|
||||
{{- define "clawdbot.name" -}}
|
||||
{{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" }}
|
||||
{{- end }}
|
||||
|
||||
{{/*
|
||||
Create a default fully qualified app name.
|
||||
We truncate at 63 chars because some Kubernetes name fields are limited to this (by the DNS naming spec).
|
||||
If release name contains chart name it will be used as a full name.
|
||||
*/}}
|
||||
{{- define "clawdbot.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 as used by the chart label.
|
||||
*/}}
|
||||
{{- define "clawdbot.chart" -}}
|
||||
{{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" }}
|
||||
{{- end }}
|
||||
|
||||
{{/*
|
||||
Common labels
|
||||
*/}}
|
||||
{{- define "clawdbot.labels" -}}
|
||||
helm.sh/chart: {{ include "clawdbot.chart" . }}
|
||||
{{ include "clawdbot.selectorLabels" . }}
|
||||
{{- if .Chart.AppVersion }}
|
||||
app.kubernetes.io/version: {{ .Chart.AppVersion | quote }}
|
||||
{{- end }}
|
||||
app.kubernetes.io/managed-by: {{ .Release.Service }}
|
||||
{{- end }}
|
||||
|
||||
{{/*
|
||||
Selector labels
|
||||
*/}}
|
||||
{{- define "clawdbot.selectorLabels" -}}
|
||||
app.kubernetes.io/name: {{ include "clawdbot.name" . }}
|
||||
app.kubernetes.io/instance: {{ .Release.Name }}
|
||||
{{- end }}
|
||||
|
||||
{{/*
|
||||
Create the name of the service account to use
|
||||
*/}}
|
||||
{{- define "clawdbot.serviceAccountName" -}}
|
||||
{{- if .Values.serviceAccount.create }}
|
||||
{{- default (include "clawdbot.fullname" .) .Values.serviceAccount.name }}
|
||||
{{- else }}
|
||||
{{- default "default" .Values.serviceAccount.name }}
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
|
||||
{{/*
|
||||
Create the name of the secret to use
|
||||
*/}}
|
||||
{{- define "clawdbot.secretName" -}}
|
||||
{{- if .Values.secrets.existingSecret }}
|
||||
{{- .Values.secrets.existingSecret }}
|
||||
{{- else }}
|
||||
{{- include "clawdbot.fullname" . }}
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
|
||||
{{/*
|
||||
Create the name of the config map to use
|
||||
*/}}
|
||||
{{- define "clawdbot.configMapName" -}}
|
||||
{{- if .Values.config.existingConfigMap }}
|
||||
{{- .Values.config.existingConfigMap }}
|
||||
{{- else }}
|
||||
{{- printf "%s-config" (include "clawdbot.fullname" .) }}
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
11
charts/clawdbot/templates/configmap.yaml
Normal file
11
charts/clawdbot/templates/configmap.yaml
Normal file
@ -0,0 +1,11 @@
|
||||
{{- if .Values.config.create -}}
|
||||
apiVersion: v1
|
||||
kind: ConfigMap
|
||||
metadata:
|
||||
name: {{ include "clawdbot.configMapName" . }}
|
||||
labels:
|
||||
{{- include "clawdbot.labels" . | nindent 4 }}
|
||||
data:
|
||||
clawdbot.json: |
|
||||
{{ .Values.config.data | toJson | nindent 4 }}
|
||||
{{- end }}
|
||||
41
charts/clawdbot/templates/ingress.yaml
Normal file
41
charts/clawdbot/templates/ingress.yaml
Normal file
@ -0,0 +1,41 @@
|
||||
{{- if .Values.ingress.enabled -}}
|
||||
apiVersion: networking.k8s.io/v1
|
||||
kind: Ingress
|
||||
metadata:
|
||||
name: {{ include "clawdbot.fullname" . }}
|
||||
labels:
|
||||
{{- include "clawdbot.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 "clawdbot.fullname" $ }}
|
||||
port:
|
||||
number: {{ $.Values.service.port }}
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
23
charts/clawdbot/templates/secret.yaml
Normal file
23
charts/clawdbot/templates/secret.yaml
Normal file
@ -0,0 +1,23 @@
|
||||
{{- if .Values.secrets.create -}}
|
||||
apiVersion: v1
|
||||
kind: Secret
|
||||
metadata:
|
||||
name: {{ include "clawdbot.fullname" . }}
|
||||
labels:
|
||||
{{- include "clawdbot.labels" . | nindent 4 }}
|
||||
type: Opaque
|
||||
stringData:
|
||||
gatewayToken: {{ .Values.secrets.data.gatewayToken | default (randAlphaNum 32) | quote }}
|
||||
{{- with .Values.secrets.data.anthropicApiKey }}
|
||||
anthropicApiKey: {{ . | quote }}
|
||||
{{- end }}
|
||||
{{- with .Values.secrets.data.openaiApiKey }}
|
||||
openaiApiKey: {{ . | quote }}
|
||||
{{- end }}
|
||||
{{- with .Values.secrets.data.discordBotToken }}
|
||||
discordBotToken: {{ . | quote }}
|
||||
{{- end }}
|
||||
{{- with .Values.secrets.data.telegramBotToken }}
|
||||
telegramBotToken: {{ . | quote }}
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
19
charts/clawdbot/templates/service.yaml
Normal file
19
charts/clawdbot/templates/service.yaml
Normal file
@ -0,0 +1,19 @@
|
||||
apiVersion: v1
|
||||
kind: Service
|
||||
metadata:
|
||||
name: {{ include "clawdbot.fullname" . }}
|
||||
labels:
|
||||
{{- include "clawdbot.labels" . | nindent 4 }}
|
||||
{{- with .Values.service.annotations }}
|
||||
annotations:
|
||||
{{- toYaml . | nindent 4 }}
|
||||
{{- end }}
|
||||
spec:
|
||||
type: {{ .Values.service.type }}
|
||||
ports:
|
||||
- port: {{ .Values.service.port }}
|
||||
targetPort: gateway
|
||||
protocol: TCP
|
||||
name: gateway
|
||||
selector:
|
||||
{{- include "clawdbot.selectorLabels" . | nindent 4 }}
|
||||
12
charts/clawdbot/templates/serviceaccount.yaml
Normal file
12
charts/clawdbot/templates/serviceaccount.yaml
Normal file
@ -0,0 +1,12 @@
|
||||
{{- if .Values.serviceAccount.create -}}
|
||||
apiVersion: v1
|
||||
kind: ServiceAccount
|
||||
metadata:
|
||||
name: {{ include "clawdbot.serviceAccountName" . }}
|
||||
labels:
|
||||
{{- include "clawdbot.labels" . | nindent 4 }}
|
||||
{{- with .Values.serviceAccount.annotations }}
|
||||
annotations:
|
||||
{{- toYaml . | nindent 4 }}
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
202
charts/clawdbot/templates/statefulset.yaml
Normal file
202
charts/clawdbot/templates/statefulset.yaml
Normal file
@ -0,0 +1,202 @@
|
||||
apiVersion: apps/v1
|
||||
kind: StatefulSet
|
||||
metadata:
|
||||
name: {{ include "clawdbot.fullname" . }}
|
||||
labels:
|
||||
{{- include "clawdbot.labels" . | nindent 4 }}
|
||||
spec:
|
||||
serviceName: {{ include "clawdbot.fullname" . }}
|
||||
replicas: {{ .Values.replicaCount }}
|
||||
selector:
|
||||
matchLabels:
|
||||
{{- include "clawdbot.selectorLabels" . | nindent 6 }}
|
||||
template:
|
||||
metadata:
|
||||
annotations:
|
||||
{{- if .Values.config.create }}
|
||||
checksum/config: {{ include (print $.Template.BasePath "/configmap.yaml") . | sha256sum }}
|
||||
{{- end }}
|
||||
{{- if .Values.secrets.create }}
|
||||
checksum/secret: {{ include (print $.Template.BasePath "/secret.yaml") . | sha256sum }}
|
||||
{{- end }}
|
||||
{{- with .Values.podAnnotations }}
|
||||
{{- toYaml . | nindent 8 }}
|
||||
{{- end }}
|
||||
labels:
|
||||
{{- include "clawdbot.selectorLabels" . | nindent 8 }}
|
||||
spec:
|
||||
{{- with .Values.imagePullSecrets }}
|
||||
imagePullSecrets:
|
||||
{{- toYaml . | nindent 8 }}
|
||||
{{- end }}
|
||||
serviceAccountName: {{ include "clawdbot.serviceAccountName" . }}
|
||||
securityContext:
|
||||
{{- toYaml .Values.podSecurityContext | nindent 8 }}
|
||||
{{- if or .Values.config.create .Values.initContainers }}
|
||||
initContainers:
|
||||
{{- if .Values.config.create }}
|
||||
- name: init-config
|
||||
image: "{{ .Values.image.registry }}/{{ .Values.image.repository }}:{{ .Values.image.tag | default .Chart.AppVersion }}"
|
||||
imagePullPolicy: {{ .Values.image.pullPolicy }}
|
||||
command:
|
||||
- sh
|
||||
- -c
|
||||
- |
|
||||
echo "Initializing configuration..."
|
||||
mkdir -p /home/node/.clawdbot
|
||||
if [ ! -f /home/node/.clawdbot/clawdbot.json ]; then
|
||||
echo "Copying default config..."
|
||||
cp /config/clawdbot.json /home/node/.clawdbot/clawdbot.json
|
||||
echo "Config initialized"
|
||||
else
|
||||
echo "Config already exists, skipping"
|
||||
fi
|
||||
volumeMounts:
|
||||
- name: config
|
||||
mountPath: /config
|
||||
- name: data
|
||||
mountPath: /home/node/.clawdbot
|
||||
subPath: clawdbot-state
|
||||
securityContext:
|
||||
{{- toYaml .Values.securityContext | nindent 12 }}
|
||||
{{- end }}
|
||||
{{- with .Values.initContainers }}
|
||||
{{- toYaml . | nindent 8 }}
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
containers:
|
||||
- name: gateway
|
||||
image: "{{ .Values.image.registry }}/{{ .Values.image.repository }}:{{ .Values.image.tag | default .Chart.AppVersion }}"
|
||||
imagePullPolicy: {{ .Values.image.pullPolicy }}
|
||||
command:
|
||||
- node
|
||||
- dist/index.js
|
||||
- gateway
|
||||
- --bind
|
||||
- {{ .Values.gateway.bind }}
|
||||
- --port
|
||||
- {{ .Values.gateway.port | quote }}
|
||||
{{- if .Values.gateway.allowUnconfigured }}
|
||||
- --allow-unconfigured
|
||||
{{- end }}
|
||||
{{- range .Values.gateway.extraArgs }}
|
||||
- {{ . }}
|
||||
{{- end }}
|
||||
env:
|
||||
{{- range $key, $value := .Values.env }}
|
||||
- name: {{ $key }}
|
||||
value: {{ $value | quote }}
|
||||
{{- end }}
|
||||
- name: CLAWDBOT_GATEWAY_PORT
|
||||
value: {{ .Values.gateway.port | quote }}
|
||||
- name: CLAWDBOT_GATEWAY_TOKEN
|
||||
valueFrom:
|
||||
secretKeyRef:
|
||||
name: {{ include "clawdbot.secretName" . }}
|
||||
key: gatewayToken
|
||||
{{- if .Values.secrets.data.anthropicApiKey }}
|
||||
- name: ANTHROPIC_API_KEY
|
||||
valueFrom:
|
||||
secretKeyRef:
|
||||
name: {{ include "clawdbot.secretName" . }}
|
||||
key: anthropicApiKey
|
||||
optional: true
|
||||
{{- end }}
|
||||
{{- if .Values.secrets.data.openaiApiKey }}
|
||||
- name: OPENAI_API_KEY
|
||||
valueFrom:
|
||||
secretKeyRef:
|
||||
name: {{ include "clawdbot.secretName" . }}
|
||||
key: openaiApiKey
|
||||
optional: true
|
||||
{{- end }}
|
||||
{{- if .Values.secrets.data.discordBotToken }}
|
||||
- name: DISCORD_BOT_TOKEN
|
||||
valueFrom:
|
||||
secretKeyRef:
|
||||
name: {{ include "clawdbot.secretName" . }}
|
||||
key: discordBotToken
|
||||
optional: true
|
||||
{{- end }}
|
||||
{{- if .Values.secrets.data.telegramBotToken }}
|
||||
- name: TELEGRAM_BOT_TOKEN
|
||||
valueFrom:
|
||||
secretKeyRef:
|
||||
name: {{ include "clawdbot.secretName" . }}
|
||||
key: telegramBotToken
|
||||
optional: true
|
||||
{{- end }}
|
||||
ports:
|
||||
- name: gateway
|
||||
containerPort: {{ .Values.gateway.port }}
|
||||
protocol: TCP
|
||||
volumeMounts:
|
||||
- name: data
|
||||
mountPath: /home/node/.clawdbot
|
||||
subPath: clawdbot-state
|
||||
- name: data
|
||||
mountPath: /home/node/clawd
|
||||
subPath: clawdbot-workspace
|
||||
{{- if .Values.livenessProbe.enabled }}
|
||||
livenessProbe:
|
||||
{{- omit .Values.livenessProbe "enabled" | toYaml | nindent 12 }}
|
||||
{{- end }}
|
||||
{{- if .Values.readinessProbe.enabled }}
|
||||
readinessProbe:
|
||||
{{- omit .Values.readinessProbe "enabled" | toYaml | nindent 12 }}
|
||||
{{- end }}
|
||||
{{- if .Values.startupProbe.enabled }}
|
||||
startupProbe:
|
||||
{{- omit .Values.startupProbe "enabled" | toYaml | nindent 12 }}
|
||||
{{- end }}
|
||||
{{- with .Values.lifecycle }}
|
||||
lifecycle:
|
||||
{{- toYaml . | nindent 12 }}
|
||||
{{- end }}
|
||||
resources:
|
||||
{{- toYaml .Values.resources | nindent 12 }}
|
||||
securityContext:
|
||||
{{- toYaml .Values.securityContext | nindent 12 }}
|
||||
{{- with .Values.extraContainers }}
|
||||
{{- toYaml . | nindent 8 }}
|
||||
{{- end }}
|
||||
volumes:
|
||||
{{- if .Values.config.create }}
|
||||
- name: config
|
||||
configMap:
|
||||
name: {{ include "clawdbot.configMapName" . }}
|
||||
{{- 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 }}
|
||||
{{- if .Values.persistence.enabled }}
|
||||
volumeClaimTemplates:
|
||||
- metadata:
|
||||
name: data
|
||||
{{- with .Values.persistence.annotations }}
|
||||
annotations:
|
||||
{{- toYaml . | nindent 10 }}
|
||||
{{- end }}
|
||||
spec:
|
||||
accessModes:
|
||||
- {{ .Values.persistence.accessMode }}
|
||||
{{- if .Values.persistence.storageClass }}
|
||||
storageClassName: {{ .Values.persistence.storageClass }}
|
||||
{{- end }}
|
||||
resources:
|
||||
requests:
|
||||
storage: {{ .Values.persistence.size }}
|
||||
{{- with .Values.persistence.selector }}
|
||||
selector:
|
||||
{{- toYaml . | nindent 10 }}
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
15
charts/clawdbot/templates/tests/test-connection.yaml
Normal file
15
charts/clawdbot/templates/tests/test-connection.yaml
Normal file
@ -0,0 +1,15 @@
|
||||
apiVersion: v1
|
||||
kind: Pod
|
||||
metadata:
|
||||
name: "{{ include "clawdbot.fullname" . }}-test-connection"
|
||||
labels:
|
||||
{{- include "clawdbot.labels" . | nindent 4 }}
|
||||
annotations:
|
||||
"helm.sh/hook": test
|
||||
spec:
|
||||
containers:
|
||||
- name: wget
|
||||
image: busybox
|
||||
command: ['wget']
|
||||
args: ['{{ include "clawdbot.fullname" . }}:{{ .Values.service.port }}']
|
||||
restartPolicy: Never
|
||||
74
charts/clawdbot/values.schema.json
Normal file
74
charts/clawdbot/values.schema.json
Normal file
@ -0,0 +1,74 @@
|
||||
{
|
||||
"$schema": "https://json-schema.org/draft-07/schema#",
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"replicaCount": {
|
||||
"type": "integer",
|
||||
"minimum": 1,
|
||||
"maximum": 1,
|
||||
"default": 1,
|
||||
"description": "Must be 1 for single-user architecture. Clawdbot does not support horizontal scaling."
|
||||
},
|
||||
"image": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"repository": {
|
||||
"type": "string"
|
||||
},
|
||||
"pullPolicy": {
|
||||
"type": "string",
|
||||
"enum": ["Always", "IfNotPresent", "Never"]
|
||||
},
|
||||
"tag": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
"gateway": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"bind": {
|
||||
"type": "string",
|
||||
"enum": ["loopback", "lan", "auto"],
|
||||
"description": "Gateway binding mode"
|
||||
},
|
||||
"port": {
|
||||
"type": "integer",
|
||||
"minimum": 1,
|
||||
"maximum": 65535
|
||||
}
|
||||
}
|
||||
},
|
||||
"persistence": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"enabled": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"size": {
|
||||
"type": "string",
|
||||
"pattern": "^[0-9]+(Gi|Mi|Ti)$"
|
||||
},
|
||||
"accessMode": {
|
||||
"type": "string",
|
||||
"enum": ["ReadWriteOnce", "ReadOnlyMany", "ReadWriteMany"]
|
||||
}
|
||||
}
|
||||
},
|
||||
"service": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"type": {
|
||||
"type": "string",
|
||||
"enum": ["ClusterIP", "NodePort", "LoadBalancer"]
|
||||
},
|
||||
"port": {
|
||||
"type": "integer",
|
||||
"minimum": 1,
|
||||
"maximum": 65535
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"required": ["replicaCount"]
|
||||
}
|
||||
227
charts/clawdbot/values.yaml
Normal file
227
charts/clawdbot/values.yaml
Normal file
@ -0,0 +1,227 @@
|
||||
# Default values for clawdbot.
|
||||
# This is a YAML-formatted file.
|
||||
# Declare variables to be passed into your templates.
|
||||
|
||||
# Image configuration
|
||||
image:
|
||||
registry: ghcr.io
|
||||
repository: clawdbot/clawdbot
|
||||
pullPolicy: IfNotPresent
|
||||
# Overrides the image tag whose default is the chart appVersion.
|
||||
tag: ""
|
||||
|
||||
imagePullSecrets: []
|
||||
nameOverride: ""
|
||||
fullnameOverride: ""
|
||||
|
||||
# Replica count - MUST be 1 for single-user architecture
|
||||
replicaCount: 1
|
||||
|
||||
# Service account
|
||||
serviceAccount:
|
||||
# Specifies whether a service account should be created
|
||||
create: true
|
||||
# Annotations to add to the service account
|
||||
annotations: {}
|
||||
# The name of the service account to use.
|
||||
# If not set and create is true, a name is generated using the fullname template
|
||||
name: ""
|
||||
|
||||
# Pod annotations
|
||||
podAnnotations: {}
|
||||
|
||||
# Pod security context
|
||||
podSecurityContext:
|
||||
runAsNonRoot: true
|
||||
runAsUser: 1000
|
||||
runAsGroup: 1000
|
||||
fsGroup: 1000
|
||||
|
||||
# Container security context
|
||||
securityContext:
|
||||
allowPrivilegeEscalation: false
|
||||
capabilities:
|
||||
drop:
|
||||
- ALL
|
||||
readOnlyRootFilesystem: false # Node needs write access to /tmp
|
||||
|
||||
# Gateway configuration
|
||||
gateway:
|
||||
# Binding mode: loopback, lan, auto
|
||||
bind: lan
|
||||
# Gateway port
|
||||
port: 18789
|
||||
# Allow unconfigured startup (creates minimal config)
|
||||
allowUnconfigured: false
|
||||
# Additional CLI arguments
|
||||
extraArgs: []
|
||||
|
||||
# Environment variables (non-sensitive)
|
||||
env:
|
||||
NODE_ENV: production
|
||||
CLAWDBOT_STATE_DIR: /home/node/.clawdbot
|
||||
CLAWDBOT_WORKSPACE_DIR: /home/node/clawd
|
||||
NODE_OPTIONS: "--max-old-space-size=2048"
|
||||
|
||||
# Secrets configuration
|
||||
secrets:
|
||||
# Create secret from values (dev/testing only - use existingSecret in production)
|
||||
create: true
|
||||
# Use existing secret (production)
|
||||
existingSecret: ""
|
||||
# Secret data (only used if create is true)
|
||||
data:
|
||||
anthropicApiKey: ""
|
||||
openaiApiKey: ""
|
||||
discordBotToken: ""
|
||||
telegramBotToken: ""
|
||||
gatewayToken: "" # Auto-generated if empty
|
||||
|
||||
# Config file (clawdbot.json)
|
||||
config:
|
||||
# Create ConfigMap from inline config
|
||||
create: true
|
||||
# Use existing ConfigMap
|
||||
existingConfigMap: ""
|
||||
# Config data (JSON5 format)
|
||||
data:
|
||||
agents:
|
||||
defaults:
|
||||
model:
|
||||
primary: "anthropic/claude-opus-4-5"
|
||||
fallbacks:
|
||||
- "anthropic/claude-sonnet-4-5"
|
||||
- "openai/gpt-4o"
|
||||
maxConcurrent: 4
|
||||
sandbox:
|
||||
mode: "off" # Disable Docker-in-Docker for Kubernetes
|
||||
list:
|
||||
- id: main
|
||||
default: true
|
||||
auth:
|
||||
profiles:
|
||||
"anthropic:default":
|
||||
mode: token
|
||||
provider: anthropic
|
||||
"openai:default":
|
||||
mode: token
|
||||
provider: openai
|
||||
gateway:
|
||||
mode: local
|
||||
bind: auto
|
||||
auth:
|
||||
mode: token
|
||||
controlUi:
|
||||
enabled: true
|
||||
channels: {}
|
||||
meta:
|
||||
lastTouchedVersion: "2026.1.25"
|
||||
|
||||
# Persistence
|
||||
persistence:
|
||||
enabled: true
|
||||
# Storage class (use cluster default if empty)
|
||||
storageClass: ""
|
||||
# Access mode
|
||||
accessMode: ReadWriteOnce
|
||||
# Volume size
|
||||
size: 10Gi
|
||||
# Annotations
|
||||
annotations: {}
|
||||
# Selector
|
||||
selector: {}
|
||||
|
||||
# Service configuration
|
||||
service:
|
||||
type: ClusterIP
|
||||
port: 18789
|
||||
annotations: {}
|
||||
|
||||
# Ingress configuration
|
||||
ingress:
|
||||
enabled: false
|
||||
className: nginx
|
||||
annotations:
|
||||
# cert-manager.io/cluster-issuer: letsencrypt-prod
|
||||
# nginx.ingress.kubernetes.io/proxy-read-timeout: "3600"
|
||||
# nginx.ingress.kubernetes.io/proxy-send-timeout: "3600"
|
||||
# nginx.ingress.kubernetes.io/websocket-services: "{{ include \"clawdbot.fullname\" . }}"
|
||||
hosts:
|
||||
- host: clawdbot.example.com
|
||||
paths:
|
||||
- path: /
|
||||
pathType: Prefix
|
||||
tls: []
|
||||
# - secretName: clawdbot-tls
|
||||
# hosts:
|
||||
# - clawdbot.example.com
|
||||
|
||||
# Resource limits/requests
|
||||
resources:
|
||||
limits:
|
||||
memory: 2Gi
|
||||
cpu: 1000m
|
||||
requests:
|
||||
memory: 512Mi
|
||||
cpu: 250m
|
||||
|
||||
# Node selector
|
||||
nodeSelector: {}
|
||||
|
||||
# Tolerations
|
||||
tolerations: []
|
||||
|
||||
# Affinity rules
|
||||
affinity: {}
|
||||
|
||||
# Init containers (for setup tasks)
|
||||
initContainers: []
|
||||
|
||||
# Extra containers (sidecars)
|
||||
extraContainers: []
|
||||
|
||||
# Lifecycle hooks
|
||||
lifecycle:
|
||||
preStop:
|
||||
exec:
|
||||
command:
|
||||
- sh
|
||||
- -c
|
||||
- rm -f /home/node/.clawdbot/gateway.*.lock; sleep 10
|
||||
|
||||
# Probes
|
||||
livenessProbe:
|
||||
enabled: true
|
||||
exec:
|
||||
command:
|
||||
- node
|
||||
- dist/index.js
|
||||
- health
|
||||
initialDelaySeconds: 30
|
||||
periodSeconds: 30
|
||||
timeoutSeconds: 10
|
||||
failureThreshold: 3
|
||||
|
||||
readinessProbe:
|
||||
enabled: true
|
||||
exec:
|
||||
command:
|
||||
- node
|
||||
- dist/index.js
|
||||
- health
|
||||
initialDelaySeconds: 15
|
||||
periodSeconds: 10
|
||||
timeoutSeconds: 5
|
||||
failureThreshold: 3
|
||||
|
||||
startupProbe:
|
||||
enabled: true
|
||||
exec:
|
||||
command:
|
||||
- node
|
||||
- dist/index.js
|
||||
- health
|
||||
initialDelaySeconds: 10
|
||||
periodSeconds: 5
|
||||
timeoutSeconds: 5
|
||||
failureThreshold: 12 # 60 seconds max startup time
|
||||
@ -101,6 +101,7 @@ Tip: if you don’t have a global install yet, run repo commands via `pnpm moltb
|
||||
### 4) Other install options
|
||||
|
||||
- Docker: [Docker](/install/docker)
|
||||
- Kubernetes: [Kubernetes (Helm)](/install/kubernetes)
|
||||
- Nix: [Nix](/install/nix)
|
||||
- Ansible: [Ansible](/install/ansible)
|
||||
- Bun (CLI only): [Bun](/install/bun)
|
||||
|
||||
482
docs/install/kubernetes.md
Normal file
482
docs/install/kubernetes.md
Normal file
@ -0,0 +1,482 @@
|
||||
---
|
||||
title: Kubernetes (Helm)
|
||||
description: Deploy Clawdbot on Kubernetes using Helm charts
|
||||
---
|
||||
|
||||
# Kubernetes Deployment
|
||||
|
||||
**Goal:** Clawdbot Gateway running on Kubernetes with Helm, persistent storage, automatic HTTPS, and channel access.
|
||||
|
||||
## What you need
|
||||
|
||||
- Kubernetes cluster (1.19+)
|
||||
- kubectl CLI configured
|
||||
- Helm 3.x
|
||||
- Model auth: Anthropic API key (or other provider keys)
|
||||
- Channel credentials: Discord bot token, Telegram token, etc.
|
||||
- **Optional:** Ingress controller (NGINX, Traefik) for external access
|
||||
- **Optional:** cert-manager for automatic TLS certificates
|
||||
|
||||
## Beginner quick path
|
||||
|
||||
1. Install Helm chart from source
|
||||
2. Configure secrets (API keys)
|
||||
3. Access Control UI via Ingress or port-forward
|
||||
4. Configure channels
|
||||
|
||||
## Prerequisites
|
||||
|
||||
### 1) Kubernetes Cluster
|
||||
|
||||
You need a running Kubernetes cluster. Options:
|
||||
|
||||
**Local development:**
|
||||
- Docker Desktop (macOS/Windows) - Enable Kubernetes in settings
|
||||
- Minikube - `brew install minikube && minikube start`
|
||||
- Kind - `brew install kind && kind create cluster`
|
||||
|
||||
**Cloud providers:**
|
||||
- GKE (Google Kubernetes Engine)
|
||||
- EKS (Amazon Elastic Kubernetes Service)
|
||||
- AKS (Azure Kubernetes Service)
|
||||
- DigitalOcean Kubernetes
|
||||
- Linode Kubernetes Engine
|
||||
|
||||
### 2) kubectl
|
||||
|
||||
Install kubectl:
|
||||
|
||||
```bash
|
||||
# macOS
|
||||
brew install kubectl
|
||||
|
||||
# Linux
|
||||
curl -LO "https://dl.k8s.io/release/$(curl -L -s https://dl.k8s.io/release/stable.txt)/bin/linux/amd64/kubectl"
|
||||
sudo install -o root -g root -m 0755 kubectl /usr/local/bin/kubectl
|
||||
|
||||
# Verify
|
||||
kubectl version --client
|
||||
kubectl cluster-info
|
||||
```
|
||||
|
||||
### 3) Helm
|
||||
|
||||
Install Helm 3.x:
|
||||
|
||||
```bash
|
||||
# macOS
|
||||
brew install helm
|
||||
|
||||
# Linux
|
||||
curl https://raw.githubusercontent.com/helm/helm/main/scripts/get-helm-3 | bash
|
||||
|
||||
# Verify
|
||||
helm version
|
||||
```
|
||||
|
||||
## Installation
|
||||
|
||||
### 1) Clone the repository
|
||||
|
||||
```bash
|
||||
git clone https://github.com/clawdbot/clawdbot.git
|
||||
cd clawdbot
|
||||
```
|
||||
|
||||
### 2) Install Helm chart
|
||||
|
||||
**Basic installation (development):**
|
||||
|
||||
```bash
|
||||
helm install my-clawdbot charts/clawdbot \
|
||||
--set secrets.data.anthropicApiKey=sk-ant-xxx \
|
||||
--set secrets.data.gatewayToken=$(openssl rand -hex 32)
|
||||
```
|
||||
|
||||
**With custom values file:**
|
||||
|
||||
```bash
|
||||
helm install my-clawdbot charts/clawdbot \
|
||||
--values charts/clawdbot/examples/values-production.yaml \
|
||||
--set secrets.data.anthropicApiKey=sk-ant-xxx \
|
||||
--set ingress.hosts[0].host=assistant.example.com
|
||||
```
|
||||
|
||||
**Create external secret (recommended for production):**
|
||||
|
||||
```bash
|
||||
# Create secret
|
||||
kubectl create secret generic clawdbot-secrets \
|
||||
--from-literal=gatewayToken=$(openssl rand -hex 32) \
|
||||
--from-literal=anthropicApiKey=sk-ant-xxx \
|
||||
--from-literal=discordBotToken=YOUR_DISCORD_TOKEN
|
||||
|
||||
# Install with external secret
|
||||
helm install my-clawdbot charts/clawdbot \
|
||||
--values charts/clawdbot/examples/values-production.yaml \
|
||||
--set secrets.create=false \
|
||||
--set secrets.existingSecret=clawdbot-secrets
|
||||
```
|
||||
|
||||
### 3) Verify installation
|
||||
|
||||
```bash
|
||||
# Check deployment status
|
||||
helm status my-clawdbot
|
||||
kubectl get all -l app.kubernetes.io/instance=my-clawdbot
|
||||
|
||||
# Wait for pod to be ready
|
||||
kubectl wait --for=condition=ready pod -l app.kubernetes.io/instance=my-clawdbot --timeout=120s
|
||||
|
||||
# Check logs
|
||||
kubectl logs -f my-clawdbot-0
|
||||
```
|
||||
|
||||
## Access the Gateway
|
||||
|
||||
### Option 1: Port-forward (local access)
|
||||
|
||||
```bash
|
||||
kubectl port-forward my-clawdbot-0 18789:18789
|
||||
```
|
||||
|
||||
Then visit: http://localhost:18789
|
||||
|
||||
### Option 2: Ingress (external access)
|
||||
|
||||
**Install NGINX Ingress Controller (if not already installed):**
|
||||
|
||||
```bash
|
||||
# For cloud providers
|
||||
kubectl apply -f https://raw.githubusercontent.com/kubernetes/ingress-nginx/controller-v1.9.0/deploy/static/provider/cloud/deploy.yaml
|
||||
|
||||
# For bare metal/local
|
||||
kubectl apply -f https://raw.githubusercontent.com/kubernetes/ingress-nginx/controller-v1.9.0/deploy/static/provider/baremetal/deploy.yaml
|
||||
|
||||
# For Minikube
|
||||
minikube addons enable ingress
|
||||
|
||||
# For Kind
|
||||
kubectl apply -f https://raw.githubusercontent.com/kubernetes/ingress-nginx/main/deploy/static/provider/kind/deploy.yaml
|
||||
```
|
||||
|
||||
**Enable Ingress in values:**
|
||||
|
||||
```yaml
|
||||
ingress:
|
||||
enabled: true
|
||||
className: nginx
|
||||
annotations:
|
||||
cert-manager.io/cluster-issuer: letsencrypt-prod # If using cert-manager
|
||||
nginx.ingress.kubernetes.io/proxy-read-timeout: "3600"
|
||||
nginx.ingress.kubernetes.io/proxy-send-timeout: "3600"
|
||||
nginx.ingress.kubernetes.io/websocket-services: "my-clawdbot"
|
||||
hosts:
|
||||
- host: assistant.example.com
|
||||
paths:
|
||||
- path: /
|
||||
pathType: Prefix
|
||||
tls:
|
||||
- secretName: clawdbot-tls
|
||||
hosts:
|
||||
- assistant.example.com
|
||||
```
|
||||
|
||||
**Upgrade with Ingress:**
|
||||
|
||||
```bash
|
||||
helm upgrade my-clawdbot charts/clawdbot \
|
||||
--reuse-values \
|
||||
--set ingress.enabled=true \
|
||||
--set ingress.hosts[0].host=assistant.example.com \
|
||||
--set ingress.hosts[0].paths[0].path=/ \
|
||||
--set ingress.hosts[0].paths[0].pathType=Prefix
|
||||
```
|
||||
|
||||
### 3) Get gateway token
|
||||
|
||||
```bash
|
||||
kubectl get secret my-clawdbot -o jsonpath='{.data.gatewayToken}' | base64 -d && echo
|
||||
```
|
||||
|
||||
Use this token to authenticate in the Control UI.
|
||||
|
||||
## Configuration
|
||||
|
||||
### Configure channels
|
||||
|
||||
#### Discord
|
||||
|
||||
```bash
|
||||
# Exec into pod
|
||||
kubectl exec -it my-clawdbot-0 -- sh
|
||||
|
||||
# Inside pod
|
||||
node dist/index.js channels add --channel discord --token YOUR_DISCORD_BOT_TOKEN
|
||||
```
|
||||
|
||||
Or set via secret:
|
||||
|
||||
```bash
|
||||
kubectl create secret generic clawdbot-secrets \
|
||||
--from-literal=discordBotToken=YOUR_DISCORD_BOT_TOKEN \
|
||||
--dry-run=client -o yaml | kubectl apply -f -
|
||||
|
||||
# Restart pod to apply
|
||||
kubectl delete pod my-clawdbot-0
|
||||
```
|
||||
|
||||
#### Telegram
|
||||
|
||||
```bash
|
||||
kubectl exec -it my-clawdbot-0 -- node dist/index.js channels add \
|
||||
--channel telegram \
|
||||
--token YOUR_TELEGRAM_BOT_TOKEN
|
||||
```
|
||||
|
||||
#### WhatsApp (QR code)
|
||||
|
||||
```bash
|
||||
# Exec into pod
|
||||
kubectl exec -it my-clawdbot-0 -- node dist/index.js channels login
|
||||
```
|
||||
|
||||
Scan the QR code with WhatsApp on your phone.
|
||||
|
||||
### Update configuration
|
||||
|
||||
Edit the config:
|
||||
|
||||
```bash
|
||||
# Get current config
|
||||
kubectl get configmap my-clawdbot-config -o yaml > clawdbot-config.yaml
|
||||
|
||||
# Edit clawdbot-config.yaml
|
||||
|
||||
# Apply changes
|
||||
kubectl apply -f clawdbot-config.yaml
|
||||
|
||||
# Restart gateway to reload config
|
||||
kubectl delete pod my-clawdbot-0
|
||||
```
|
||||
|
||||
## Storage
|
||||
|
||||
The chart creates a persistent volume claim that stores:
|
||||
|
||||
- `/home/node/.clawdbot` - Config, sessions, device identity, SQLite databases
|
||||
- `/home/node/clawd` - Agent workspace files
|
||||
|
||||
**Check storage:**
|
||||
|
||||
```bash
|
||||
kubectl get pvc
|
||||
kubectl describe pvc data-my-clawdbot-0
|
||||
```
|
||||
|
||||
**Increase storage size:**
|
||||
|
||||
```yaml
|
||||
persistence:
|
||||
size: 20Gi
|
||||
```
|
||||
|
||||
## Upgrading
|
||||
|
||||
```bash
|
||||
# Pull latest changes
|
||||
cd clawdbot
|
||||
git pull
|
||||
|
||||
# Upgrade with current values
|
||||
helm upgrade my-clawdbot charts/clawdbot --reuse-values
|
||||
|
||||
# Upgrade with new values
|
||||
helm upgrade my-clawdbot charts/clawdbot -f values-production.yaml
|
||||
```
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
### Pod not starting
|
||||
|
||||
```bash
|
||||
# Check events
|
||||
kubectl describe pod my-clawdbot-0
|
||||
|
||||
# Check logs
|
||||
kubectl logs my-clawdbot-0
|
||||
|
||||
# If pod crashed
|
||||
kubectl logs my-clawdbot-0 --previous
|
||||
```
|
||||
|
||||
### OOM (Out of Memory)
|
||||
|
||||
Container keeps restarting. Signs: `SIGABRT`, `v8::internal::Runtime_AllocateInYoungGeneration`, or silent restarts.
|
||||
|
||||
**Fix:** Increase memory in values.yaml:
|
||||
|
||||
```yaml
|
||||
resources:
|
||||
limits:
|
||||
memory: 4Gi
|
||||
requests:
|
||||
memory: 1Gi
|
||||
```
|
||||
|
||||
**Note:** 512MB is too small. 2GB recommended minimum.
|
||||
|
||||
### PVC not binding
|
||||
|
||||
```bash
|
||||
# Check PVC status
|
||||
kubectl get pvc
|
||||
|
||||
# Check events
|
||||
kubectl describe pvc data-my-clawdbot-0
|
||||
|
||||
# Check if storage provisioner is available
|
||||
kubectl get storageclass
|
||||
```
|
||||
|
||||
**Fix for Minikube:**
|
||||
|
||||
```bash
|
||||
minikube addons enable storage-provisioner
|
||||
minikube addons enable default-storageclass
|
||||
```
|
||||
|
||||
### Gateway lock issues
|
||||
|
||||
Gateway refuses to start with "already running" errors.
|
||||
|
||||
```bash
|
||||
# Delete lock file
|
||||
kubectl exec my-clawdbot-0 -- rm -f /home/node/.clawdbot/gateway.*.lock
|
||||
|
||||
# Restart pod
|
||||
kubectl delete pod my-clawdbot-0
|
||||
```
|
||||
|
||||
### WebSocket connections timing out
|
||||
|
||||
Ensure Ingress has proper annotations:
|
||||
|
||||
```yaml
|
||||
annotations:
|
||||
nginx.ingress.kubernetes.io/proxy-read-timeout: "3600"
|
||||
nginx.ingress.kubernetes.io/proxy-send-timeout: "3600"
|
||||
nginx.ingress.kubernetes.io/websocket-services: "my-clawdbot"
|
||||
```
|
||||
|
||||
### Config not being read
|
||||
|
||||
If using `--allow-unconfigured`, the gateway creates a minimal config. Your custom config should be read on restart.
|
||||
|
||||
```bash
|
||||
# Verify config exists
|
||||
kubectl exec my-clawdbot-0 -- cat /home/node/.clawdbot/clawdbot.json
|
||||
|
||||
# Verify ConfigMap
|
||||
kubectl get configmap my-clawdbot-config -o yaml
|
||||
```
|
||||
|
||||
### Image pull errors (local testing)
|
||||
|
||||
When testing with local images:
|
||||
|
||||
```bash
|
||||
# Ensure pullPolicy is Never
|
||||
--set image.pullPolicy=Never
|
||||
|
||||
# Load image into cluster
|
||||
# Docker Desktop: Image already available
|
||||
# Minikube: minikube image load clawdbot:local
|
||||
# Kind: kind load docker-image clawdbot:local
|
||||
```
|
||||
|
||||
## Local Testing
|
||||
|
||||
Test the Helm chart locally before deploying to production:
|
||||
|
||||
### Using Docker Desktop Kubernetes
|
||||
|
||||
```bash
|
||||
# Enable Kubernetes in Docker Desktop settings
|
||||
|
||||
# Run automated test script
|
||||
./scripts/test-helm-local.sh
|
||||
```
|
||||
|
||||
### Using Minikube
|
||||
|
||||
```bash
|
||||
# Start Minikube
|
||||
minikube start --memory=4096 --cpus=2
|
||||
|
||||
# Build and load image
|
||||
docker build -t clawdbot:local .
|
||||
minikube image load clawdbot:local
|
||||
|
||||
# Install chart
|
||||
helm install test charts/clawdbot \
|
||||
-f charts/clawdbot/examples/values-basic.yaml \
|
||||
--set image.repository=clawdbot \
|
||||
--set image.tag=local \
|
||||
--set image.pullPolicy=Never
|
||||
|
||||
# Access via port-forward
|
||||
kubectl port-forward test-clawdbot-0 18789:18789
|
||||
```
|
||||
|
||||
### Using Kind
|
||||
|
||||
```bash
|
||||
# Create cluster
|
||||
kind create cluster
|
||||
|
||||
# Build and load image
|
||||
docker build -t clawdbot:local .
|
||||
kind load docker-image clawdbot:local
|
||||
|
||||
# Install chart
|
||||
helm install test charts/clawdbot \
|
||||
-f charts/clawdbot/examples/values-basic.yaml \
|
||||
--set image.repository=clawdbot \
|
||||
--set image.tag=local \
|
||||
--set image.pullPolicy=Never
|
||||
```
|
||||
|
||||
## Uninstall
|
||||
|
||||
```bash
|
||||
# Uninstall Helm release
|
||||
helm uninstall my-clawdbot
|
||||
|
||||
# Delete PVCs (data will be lost)
|
||||
kubectl delete pvc -l app.kubernetes.io/instance=my-clawdbot
|
||||
```
|
||||
|
||||
## Notes
|
||||
|
||||
- Clawdbot is a **single-user application**. The chart enforces `replicas: 1`.
|
||||
- WebSocket gateway requires long-lived connections (use proper Ingress timeouts).
|
||||
- Persistent storage is required to preserve state across restarts.
|
||||
- Docker-in-Docker sandboxing is disabled by default in Kubernetes deployments.
|
||||
- For production, use external secret management (Kubernetes Secrets, External Secrets Operator, or Vault).
|
||||
|
||||
## Documentation
|
||||
|
||||
- [Chart README](https://github.com/clawdbot/clawdbot/tree/main/charts/clawdbot)
|
||||
- [Clawdbot Documentation](https://docs.clawd.bot)
|
||||
- [GitHub Repository](https://github.com/clawdbot/clawdbot)
|
||||
|
||||
## Cost
|
||||
|
||||
Kubernetes cluster costs vary by provider:
|
||||
|
||||
- **Local (free):** Docker Desktop, Minikube, Kind
|
||||
- **Cloud providers:** $50-200/month depending on node size and region
|
||||
- **Recommended resources:** 2 CPU, 4GB RAM minimum
|
||||
|
||||
See your cloud provider's pricing for details.
|
||||
144
scripts/publish-helm-chart.sh
Normal file
144
scripts/publish-helm-chart.sh
Normal file
@ -0,0 +1,144 @@
|
||||
#!/usr/bin/env bash
|
||||
# Publish Helm chart to GitHub Pages
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
# Colors
|
||||
RED='\033[0;31m'
|
||||
GREEN='\033[0;32m'
|
||||
YELLOW='\033[1;33m'
|
||||
BLUE='\033[0;34m'
|
||||
NC='\033[0m'
|
||||
|
||||
log() { echo -e "${GREEN}[PUBLISH]${NC} $*"; }
|
||||
warn() { echo -e "${YELLOW}[WARN]${NC} $*"; }
|
||||
error() { echo -e "${RED}[ERROR]${NC} $*"; exit 1; }
|
||||
info() { echo -e "${BLUE}[INFO]${NC} $*"; }
|
||||
|
||||
# Configuration
|
||||
CHART_PATH="charts/clawdbot"
|
||||
REPO_URL="https://clawdbot.github.io/clawdbot"
|
||||
OUTPUT_DIR=".cr-release-packages"
|
||||
|
||||
# Check prerequisites
|
||||
command -v helm >/dev/null 2>&1 || error "helm not found. Install it first."
|
||||
command -v git >/dev/null 2>&1 || error "git not found."
|
||||
|
||||
# Get chart version
|
||||
CHART_VERSION=$(grep '^version:' "$CHART_PATH/Chart.yaml" | awk '{print $2}')
|
||||
APP_VERSION=$(grep '^appVersion:' "$CHART_PATH/Chart.yaml" | awk '{print $2}' | tr -d '"')
|
||||
|
||||
log "Publishing Helm Chart"
|
||||
info "Chart version: $CHART_VERSION"
|
||||
info "App version: $APP_VERSION"
|
||||
|
||||
# Confirm
|
||||
read -p "Continue with publishing? (y/N) " -n 1 -r
|
||||
echo
|
||||
if [[ ! $REPLY =~ ^[Yy]$ ]]; then
|
||||
warn "Publishing cancelled"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
# Step 1: Lint chart
|
||||
log "Step 1: Linting chart..."
|
||||
helm lint "$CHART_PATH" || error "Chart lint failed"
|
||||
|
||||
# Step 2: Package chart
|
||||
log "Step 2: Packaging chart..."
|
||||
mkdir -p "$OUTPUT_DIR"
|
||||
helm package "$CHART_PATH" -d "$OUTPUT_DIR" || error "Chart packaging failed"
|
||||
info "Packaged: $OUTPUT_DIR/clawdbot-${CHART_VERSION}.tgz"
|
||||
|
||||
# Step 3: Generate index
|
||||
log "Step 3: Generating repository index..."
|
||||
|
||||
# Check if we're on gh-pages branch
|
||||
CURRENT_BRANCH=$(git branch --show-current)
|
||||
if [ "$CURRENT_BRANCH" != "gh-pages" ]; then
|
||||
warn "Not on gh-pages branch. Switching..."
|
||||
|
||||
# Stash current changes if any
|
||||
if ! git diff-index --quiet HEAD --; then
|
||||
log "Stashing current changes..."
|
||||
git stash push -m "publish-helm-chart: stash before gh-pages"
|
||||
fi
|
||||
|
||||
# Switch to gh-pages
|
||||
git checkout gh-pages || {
|
||||
error "Failed to checkout gh-pages branch. Create it first:
|
||||
|
||||
git checkout --orphan gh-pages
|
||||
git rm -rf .
|
||||
echo '# Clawdbot Helm Charts' > README.md
|
||||
git add README.md
|
||||
git commit -m 'Initial gh-pages'
|
||||
git push origin gh-pages
|
||||
git checkout main"
|
||||
}
|
||||
fi
|
||||
|
||||
# Copy package to gh-pages root
|
||||
log "Step 4: Copying package to gh-pages..."
|
||||
cp "$OUTPUT_DIR/clawdbot-${CHART_VERSION}.tgz" .
|
||||
|
||||
# Update or create index.yaml
|
||||
if [ -f "index.yaml" ]; then
|
||||
log "Updating existing index.yaml..."
|
||||
helm repo index . --url "$REPO_URL" --merge index.yaml
|
||||
else
|
||||
log "Creating new index.yaml..."
|
||||
helm repo index . --url "$REPO_URL"
|
||||
fi
|
||||
|
||||
# Step 5: Commit and push
|
||||
log "Step 5: Committing to gh-pages..."
|
||||
git add "clawdbot-${CHART_VERSION}.tgz" index.yaml
|
||||
|
||||
if git diff --staged --quiet; then
|
||||
warn "No changes to commit. Chart version $CHART_VERSION may already be published."
|
||||
else
|
||||
git commit -m "Release chart version ${CHART_VERSION}
|
||||
|
||||
Chart: clawdbot ${CHART_VERSION}
|
||||
App: ${APP_VERSION}
|
||||
|
||||
Published via scripts/publish-helm-chart.sh"
|
||||
|
||||
log "Step 6: Pushing to origin/gh-pages..."
|
||||
git push origin gh-pages || error "Failed to push to gh-pages"
|
||||
|
||||
log "✓ Chart published successfully!"
|
||||
info "Version: $CHART_VERSION"
|
||||
info "URL: $REPO_URL/clawdbot-${CHART_VERSION}.tgz"
|
||||
fi
|
||||
|
||||
# Step 7: Switch back to original branch
|
||||
log "Switching back to $CURRENT_BRANCH..."
|
||||
git checkout "$CURRENT_BRANCH"
|
||||
|
||||
# Restore stash if we created one
|
||||
if git stash list | grep -q "publish-helm-chart: stash before gh-pages"; then
|
||||
log "Restoring stashed changes..."
|
||||
git stash pop
|
||||
fi
|
||||
|
||||
# Clean up
|
||||
rm -rf "$OUTPUT_DIR"
|
||||
|
||||
echo
|
||||
log "============================================"
|
||||
log "Chart published successfully! ✓"
|
||||
log "============================================"
|
||||
echo
|
||||
info "Users can now install with:"
|
||||
echo
|
||||
echo " helm repo add clawdbot $REPO_URL"
|
||||
echo " helm repo update"
|
||||
echo " helm install my-clawdbot clawdbot/clawdbot"
|
||||
echo
|
||||
info "Chart URL: $REPO_URL/clawdbot-${CHART_VERSION}.tgz"
|
||||
info "Wait 5-10 minutes for GitHub Pages to deploy"
|
||||
echo
|
||||
info "Verify deployment:"
|
||||
echo " curl $REPO_URL/index.yaml"
|
||||
167
scripts/test-helm-local.sh
Executable file
167
scripts/test-helm-local.sh
Executable file
@ -0,0 +1,167 @@
|
||||
#!/usr/bin/env bash
|
||||
# Test Helm chart locally on Docker Desktop K8s, Minikube, or Kind
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
# Colors for output
|
||||
RED='\033[0;31m'
|
||||
GREEN='\033[0;32m'
|
||||
YELLOW='\033[1;33m'
|
||||
BLUE='\033[0;34m'
|
||||
NC='\033[0m' # No Color
|
||||
|
||||
log() { echo -e "${GREEN}[TEST]${NC} $*"; }
|
||||
warn() { echo -e "${YELLOW}[WARN]${NC} $*"; }
|
||||
error() { echo -e "${RED}[ERROR]${NC} $*"; exit 1; }
|
||||
info() { echo -e "${BLUE}[INFO]${NC} $*"; }
|
||||
|
||||
# Configuration
|
||||
CHART_PATH="${CHART_PATH:-charts/clawdbot}"
|
||||
IMAGE_NAME="${IMAGE_NAME:-clawdbot}"
|
||||
IMAGE_TAG="${IMAGE_TAG:-local-test}"
|
||||
RELEASE_NAME="${RELEASE_NAME:-test-clawdbot}"
|
||||
NAMESPACE="${NAMESPACE:-default}"
|
||||
ANTHROPIC_API_KEY="${ANTHROPIC_API_KEY:-sk-ant-test-dummy}"
|
||||
GATEWAY_TOKEN="${GATEWAY_TOKEN:-$(openssl rand -hex 32)}"
|
||||
|
||||
# Detect K8s environment
|
||||
detect_k8s() {
|
||||
if kubectl config current-context | grep -q docker-desktop 2>/dev/null; then
|
||||
echo "docker-desktop"
|
||||
elif kubectl config current-context | grep -q minikube 2>/dev/null; then
|
||||
echo "minikube"
|
||||
elif kubectl config current-context | grep -q kind 2>/dev/null; then
|
||||
echo "kind"
|
||||
else
|
||||
echo "unknown"
|
||||
fi
|
||||
}
|
||||
|
||||
K8S_ENV=$(detect_k8s)
|
||||
info "Detected Kubernetes environment: $K8S_ENV"
|
||||
|
||||
# Step 1: Lint chart
|
||||
log "Step 1: Linting Helm chart..."
|
||||
helm lint "$CHART_PATH" || error "Helm lint failed"
|
||||
helm lint "$CHART_PATH" --values "$CHART_PATH/examples/values-basic.yaml" || error "Helm lint with values-basic.yaml failed"
|
||||
log "✓ Lint passed"
|
||||
|
||||
# Step 2: Template validation
|
||||
log "Step 2: Validating templates..."
|
||||
helm template test "$CHART_PATH" \
|
||||
--values "$CHART_PATH/examples/values-basic.yaml" \
|
||||
--set image.repository="$IMAGE_NAME" \
|
||||
--set image.tag="$IMAGE_TAG" \
|
||||
--set secrets.data.anthropicApiKey="$ANTHROPIC_API_KEY" \
|
||||
> /tmp/clawdbot-manifests.yaml || error "Template validation failed"
|
||||
log "✓ Template validation passed"
|
||||
|
||||
# Step 3: Build Docker image
|
||||
log "Step 3: Building Docker image..."
|
||||
docker build -t "$IMAGE_NAME:$IMAGE_TAG" -f Dockerfile . || error "Docker build failed"
|
||||
log "✓ Image built: $IMAGE_NAME:$IMAGE_TAG"
|
||||
|
||||
# Step 4: Load image into cluster
|
||||
log "Step 4: Loading image into cluster..."
|
||||
case "$K8S_ENV" in
|
||||
docker-desktop)
|
||||
info "Docker Desktop: Image already available"
|
||||
;;
|
||||
minikube)
|
||||
minikube image load "$IMAGE_NAME:$IMAGE_TAG" || error "Failed to load image into Minikube"
|
||||
;;
|
||||
kind)
|
||||
kind load docker-image "$IMAGE_NAME:$IMAGE_TAG" || error "Failed to load image into Kind"
|
||||
;;
|
||||
*)
|
||||
warn "Unknown K8s environment, skipping image load (may fail if image not available)"
|
||||
;;
|
||||
esac
|
||||
log "✓ Image loaded"
|
||||
|
||||
# Step 5: Install Helm chart
|
||||
log "Step 5: Installing Helm chart..."
|
||||
helm install "$RELEASE_NAME" "$CHART_PATH" \
|
||||
--namespace "$NAMESPACE" \
|
||||
--values "$CHART_PATH/examples/values-basic.yaml" \
|
||||
--set image.repository="$IMAGE_NAME" \
|
||||
--set image.tag="$IMAGE_TAG" \
|
||||
--set image.pullPolicy=Never \
|
||||
--set secrets.data.anthropicApiKey="$ANTHROPIC_API_KEY" \
|
||||
--set secrets.data.gatewayToken="$GATEWAY_TOKEN" \
|
||||
--wait --timeout=3m || error "Helm install failed"
|
||||
log "✓ Chart installed"
|
||||
|
||||
# Step 6: Verify deployment
|
||||
log "Step 6: Verifying deployment..."
|
||||
|
||||
# Check StatefulSet
|
||||
info "Checking StatefulSet..."
|
||||
kubectl get statefulset -l app.kubernetes.io/instance="$RELEASE_NAME" -n "$NAMESPACE"
|
||||
|
||||
# Check Pod
|
||||
info "Waiting for pod to be ready..."
|
||||
kubectl wait --for=condition=ready pod -l app.kubernetes.io/instance="$RELEASE_NAME" \
|
||||
-n "$NAMESPACE" --timeout=120s || error "Pod did not become ready"
|
||||
log "✓ Pod is ready"
|
||||
|
||||
# Check PVC
|
||||
info "Checking PVC..."
|
||||
kubectl get pvc -l app.kubernetes.io/instance="$RELEASE_NAME" -n "$NAMESPACE"
|
||||
log "✓ PVC created"
|
||||
|
||||
# Step 7: Health check
|
||||
log "Step 7: Running health check..."
|
||||
POD_NAME=$(kubectl get pod -l app.kubernetes.io/instance="$RELEASE_NAME" -n "$NAMESPACE" -o jsonpath='{.items[0].metadata.name}')
|
||||
kubectl exec "$POD_NAME" -n "$NAMESPACE" -- node dist/index.js health --token "$GATEWAY_TOKEN" && log "✓ Health check passed" || warn "Health check failed (may be OK for test env)"
|
||||
|
||||
# Step 8: Check logs
|
||||
log "Step 8: Checking logs (last 50 lines)..."
|
||||
kubectl logs "$POD_NAME" -n "$NAMESPACE" --tail=50
|
||||
|
||||
# Step 9: Run Helm tests
|
||||
log "Step 9: Running Helm tests..."
|
||||
helm test "$RELEASE_NAME" -n "$NAMESPACE" && log "✓ Helm tests passed" || warn "Helm tests failed"
|
||||
|
||||
# Step 10: Verify persistence
|
||||
log "Step 10: Testing persistence..."
|
||||
info "Checking state directory..."
|
||||
kubectl exec "$POD_NAME" -n "$NAMESPACE" -- ls -la /home/node/.clawdbot
|
||||
info "Checking workspace directory..."
|
||||
kubectl exec "$POD_NAME" -n "$NAMESPACE" -- ls -la /home/node/clawd
|
||||
info "Checking config file..."
|
||||
kubectl exec "$POD_NAME" -n "$NAMESPACE" -- cat /home/node/.clawdbot/clawdbot.json || warn "Config file not found (may be OK)"
|
||||
log "✓ Persistence verified"
|
||||
|
||||
# Step 11: Port-forward test
|
||||
log "Step 11: Testing port-forward (5 seconds)..."
|
||||
kubectl port-forward "$POD_NAME" -n "$NAMESPACE" 18789:18789 &
|
||||
PF_PID=$!
|
||||
sleep 5
|
||||
kill $PF_PID 2>/dev/null || true
|
||||
log "✓ Port-forward test complete"
|
||||
|
||||
# Success summary
|
||||
echo
|
||||
log "============================================"
|
||||
log "All tests passed! ✓"
|
||||
log "============================================"
|
||||
info "Release: $RELEASE_NAME"
|
||||
info "Namespace: $NAMESPACE"
|
||||
info "Pod: $POD_NAME"
|
||||
info "Gateway token: $GATEWAY_TOKEN"
|
||||
echo
|
||||
|
||||
# Cleanup prompt
|
||||
read -p "Clean up resources? (y/N) " -n 1 -r
|
||||
echo
|
||||
if [[ $REPLY =~ ^[Yy]$ ]]; then
|
||||
log "Cleaning up..."
|
||||
helm uninstall "$RELEASE_NAME" -n "$NAMESPACE"
|
||||
kubectl delete pvc -l app.kubernetes.io/instance="$RELEASE_NAME" -n "$NAMESPACE"
|
||||
log "✓ Cleanup complete"
|
||||
else
|
||||
info "Skipping cleanup. To clean up later, run:"
|
||||
echo " helm uninstall $RELEASE_NAME -n $NAMESPACE"
|
||||
echo " kubectl delete pvc -l app.kubernetes.io/instance=$RELEASE_NAME -n $NAMESPACE"
|
||||
fi
|
||||
Loading…
Reference in New Issue
Block a user