docs: update build/deploy docs for Woodpecker CI
Some checks failed
ci/woodpecker/push/woodpecker Pipeline failed

- deploying.md: Add Woodpecker CI section, update constraints
- releasing.md: Add automated releases via Woodpecker, Zot registry
- RELEASE_CHECKLIST.md: Update build/deploy commands
- CLAUDE.md: Update quick reference for automated deploys

Images now at registry.threesix.ai/rdev/* instead of ghcr.io

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
jordan 2026-02-05 23:54:00 -07:00
parent 2fd52dcfed
commit 96c9389c97
4 changed files with 368 additions and 21 deletions

View File

@ -0,0 +1,215 @@
# Deploying to k3s
**When to use:** Deploying rdev-api or claudebox pods to the k3s cluster.
## Prerequisites
- Access to orchard9 k3s cluster
- kubectl installed
- kubeconfig file at `~/.kube/orchard9-k3sf.yaml`
## Quick Deploy
```bash
# CRITICAL: Always set kubeconfig first
export KUBECONFIG=~/.kube/orchard9-k3sf.yaml
# Deploy all resources
kubectl apply -k deployments/k8s/base
# Verify deployment
kubectl get pods -n rdev
kubectl get svc -n rdev
```
## What Gets Deployed
The Kustomize base deploys:
| Resource | Name | Purpose |
|----------|------|---------|
| Namespace | `rdev` | Isolation |
| Deployment | `rdev-api` | API server |
| StatefulSet | `claudebox` | Claude Code pods |
| Service | `rdev-api` | Internal service |
| ServiceAccount | `rdev-api` | RBAC identity |
| Role/RoleBinding | `rdev-api` | Namespace permissions |
| ClusterRole/Binding | `rdev-api` | Cluster-wide permissions |
| PVC | `workspace-pvc` | Shared workspace volume |
| Secret | `claude-credentials` | Claude auth (manual) |
## Deployment Steps
### 1. Set Kubeconfig
```bash
export KUBECONFIG=~/.kube/orchard9-k3sf.yaml
# Verify connection
kubectl cluster-info
```
### 2. Create Secrets (First Time Only)
```bash
# Claude credentials secret
kubectl create secret generic claude-credentials \
-n rdev \
--from-file=credentials.json=/path/to/credentials.json
# Database credentials (if using external postgres)
kubectl create secret generic rdev-db \
-n rdev \
--from-literal=host=postgres.example.com \
--from-literal=password=secret
```
### 3. Apply Manifests
```bash
# Full deployment
kubectl apply -k deployments/k8s/base
# Or apply individually
kubectl apply -f deployments/k8s/base/namespace.yaml
kubectl apply -f deployments/k8s/base/rdev-api.yaml
kubectl apply -f deployments/k8s/base/claudebox.yaml
```
### 4. Verify
```bash
# Check pods
kubectl get pods -n rdev
# Expected: rdev-api-xxx Running, claudebox-0 Running
# Check logs
kubectl logs -n rdev deployment/rdev-api
# Test API
kubectl port-forward -n rdev svc/rdev-api 8080:8080
curl http://localhost:8080/health
```
## Updating Deployments
### Rolling Update
```bash
# Update image tag in manifests, then:
kubectl apply -k deployments/k8s/base
# Or force rollout
kubectl rollout restart deployment/rdev-api -n rdev
```
### Watch Rollout
```bash
kubectl rollout status deployment/rdev-api -n rdev
```
## Manifest Structure
```
deployments/k8s/
└── base/
├── kustomization.yaml # Kustomize config
├── namespace.yaml # rdev namespace
├── rdev-api.yaml # API deployment + RBAC
├── claudebox.yaml # StatefulSet for Claude pods
└── pvc-workspace.yaml # Shared workspace PVC
```
## RBAC Permissions
rdev-api requires:
- **Namespace-scoped:** Pod exec, ConfigMap read
- **Cluster-scoped:** Deployments, Services, Ingresses (for project deployment feature)
### Woodpecker CI Deployer RBAC
The `woodpecker-deployer-rbac.yaml` manifest grants Woodpecker CI permission to deploy projects. Without this, deploy steps fail with permission errors.
**Why it's needed:** Woodpecker pipeline steps run as the `default` ServiceAccount in the `threesix` namespace, but need to `kubectl set image` on deployments in the `projects` namespace.
**Permissions granted:**
- `get`, `list`, `patch` on `deployments` (apps API group)
This follows least-privilege principles - only the minimum permissions needed for `kubectl set image` to work.
## Troubleshooting
### Pod not starting
```bash
kubectl describe pod -n rdev <pod-name>
kubectl logs -n rdev <pod-name> --previous
```
### Permission denied
Check RBAC:
```bash
kubectl auth can-i exec pods -n rdev --as=system:serviceaccount:rdev:rdev-api
```
### Image pull error
Verify registry access:
```bash
kubectl get events -n rdev --sort-by='.lastTimestamp'
```
### Database connection failed
Check secret and network policy:
```bash
kubectl get secret rdev-db -n rdev -o yaml
kubectl exec -n rdev deployment/rdev-api -- env | grep DB_
```
## Automated CI/CD via Woodpecker
rdev now uses Woodpecker CI for automated builds and deploys:
```
git push → Gitea → Woodpecker → kaniko → registry.threesix.ai → kubectl deploy
```
### How It Works
1. Push to `git.threesix.ai/jordan/rdev` triggers Woodpecker
2. Woodpecker runs `.woodpecker.yml`:
- Tests with `go test ./...`
- Builds images via `woodpeckerci/plugin-kaniko`
- Pushes to `registry.threesix.ai/rdev/{api,worker,claudebox}`
- Deploys via `kubectl set image`
### Manual Deploy (if needed)
```bash
export KUBECONFIG=~/.kube/orchard9-k3sf.yaml
kubectl apply -f deployments/k8s/base/rdev-api.yaml
kubectl rollout restart -n rdev deployment/rdev-api
```
### Image Registry
Images are stored in Zot at `registry.threesix.ai/rdev/`:
- `rdev/api:latest` - API server
- `rdev/worker:latest` - Worker pool
- `rdev/claudebox:latest` - Claude Code container
## Constraints
- **ON-PREM k3s** - Not GKE/EKS, always use local kubeconfig
- **Kustomize only** - No ArgoCD or Helm
- **Woodpecker CI** - Automated builds on push to main
## Related
- [Releasing](./releasing.md) - Build and publish new versions
- [Database Guide](./database.md)
- [Kubernetes Adapter](../services/kubernetes.md)
- [External Health Diagnostics](./external-health-diagnostics.md) - Debug registry/CI/git issues

View File

@ -0,0 +1,135 @@
# Releasing rdev-api
**When to use:** Creating a new versioned release with changelog, container image, and git tag.
## Automated Releases (Recommended)
Push to main branch triggers Woodpecker CI to build and deploy automatically:
```bash
# Just push - Woodpecker handles the rest
git push origin main
# Or push to both remotes
GITEA_TOKEN=$(kubectl get secret rdev-credentials -n rdev -o jsonpath='{.data.GITEA_TOKEN}' | base64 -d)
git push https://jordan:${GITEA_TOKEN}@git.threesix.ai/jordan/rdev.git main
git push origin main
```
Images are built via kaniko and pushed to `registry.threesix.ai/rdev/*`.
## Prerequisites (Manual Releases)
- Go installed (for local binary builds if needed)
- KUBECONFIG set: `export KUBECONFIG=~/.kube/orchard9-k3sf.yaml`
- Access to Gitea (`git.threesix.ai/jordan/rdev`)
## Quick Start
```bash
# Release and deploy in one command
./scripts/release.sh v0.8.1 "Fix worker ID config bug" --deploy
# Release only (no deploy)
./scripts/release.sh v0.8.1 "Fix worker ID config bug"
```
## What the Release Script Does
The script (`scripts/release.sh`) performs these steps in order:
1. **Creates changelog** - Writes `changelog/<version>.md` with date and message
2. **Updates deployment** - Patches `deployments/k8s/base/rdev-api.yaml` with new image tag
3. **Commits and pushes** - Commits changelog and deployment changes to main
4. **Builds binary** - Cross-compiles for linux/amd64 with version embedded
5. **Builds container** - Creates `ghcr.io/orchard9/rdev-api:<version>` image
6. **Pushes image** - Uploads to GitHub Container Registry
7. **Tags release** - Creates annotated git tag and pushes it
With `--deploy` flag, it also:
8. **Runs migrations** - Executes all SQL migrations as `rdev` superuser
9. **Applies manifest** - `kubectl apply` the deployment YAML
10. **Restarts deployment** - Triggers rollout with new image
## Usage
```bash
./scripts/release.sh <version> "<changelog message>" [--deploy]
```
| Argument | Description | Example |
|----------|-------------|---------|
| `version` | Semver tag (with or without `v` prefix) | `v0.8.1` or `0.8.1` |
| `message` | Changelog entry describing the release | `"Add worker pool support"` |
| `--deploy` | Also run migrations and deploy to k3s | (optional flag) |
## Examples
```bash
# Full release + deploy (recommended)
./scripts/release.sh v0.9.0 "Add project templates" --deploy
# Bug fix release + deploy
./scripts/release.sh v0.8.2 "Fix nil pointer in command handler" --deploy
# Release only (deploy later manually)
./scripts/release.sh v0.8.3 "Update dependency versions"
```
## Manual Deploy (if not using --deploy)
```bash
export KUBECONFIG=~/.kube/orchard9-k3sf.yaml
kubectl apply -f deployments/k8s/base/rdev-api.yaml
kubectl rollout restart -n rdev deployment/rdev-api
```
## Artifacts Created
| Artifact | Location |
|----------|----------|
| Changelog | `changelog/<version>.md` |
| Container images | `registry.threesix.ai/rdev/{api,worker,claudebox}:<commit-sha>` |
| Git tag | `<version>` (annotated) |
| Updated deployment | `deployments/k8s/base/rdev-api.yaml` |
## Image Registry
Images are now stored in Zot (`registry.threesix.ai`) instead of ghcr.io:
| Image | Path |
|-------|------|
| API server | `registry.threesix.ai/rdev/api:latest` |
| Worker | `registry.threesix.ai/rdev/worker:latest` |
| Claudebox | `registry.threesix.ai/rdev/claudebox:latest` |
Tags: `latest` and `${CI_COMMIT_SHA:0:8}` (8-char commit hash)
## Troubleshooting
### Docker push fails
Ensure you're authenticated to ghcr.io:
```bash
echo $GITHUB_TOKEN | docker login ghcr.io -u USERNAME --password-stdin
```
### Build fails
Check Go environment:
```bash
go version
go env GOOS GOARCH
```
### Git push rejected
Ensure you have push access and main is up to date:
```bash
git pull origin main
```
## Related
- [Deploying to k3s](./deploying.md)
- [Credentials Management](./credentials.md)

View File

@ -93,18 +93,14 @@ go run ./cmd/rdev-api
# Run tests # Run tests
go test ./... go test ./...
# Release + deploy (one command) # Automated deploy (push triggers Woodpecker CI)
./scripts/release.sh v0.10.1 "Description of changes" --deploy git push origin main # Builds and deploys automatically via Woodpecker
# Release only (no deploy) # Manual deploy (if Woodpecker unavailable)
./scripts/release.sh v0.10.1 "Description of changes"
# Manual deploy (if needed)
kubectl apply -f deployments/k8s/base/rdev-api.yaml kubectl apply -f deployments/k8s/base/rdev-api.yaml
kubectl rollout restart -n rdev deployment/rdev-api kubectl rollout restart -n rdev deployment/rdev-api
# Deploy claudebox worker (when Dockerfile changes) # Images are at registry.threesix.ai/rdev/{api,worker,claudebox}
./scripts/build-push.sh v0.4.0 claudebox && kubectl apply -f deployments/k8s/base/claudebox.yaml && kubectl rollout restart -n rdev statefulset/claudebox
# Verify pods # Verify pods
kubectl get pods -n rdev kubectl get pods -n rdev

View File

@ -48,29 +48,30 @@ Note: Some coverage targets not met, but core functionality is well-tested.
## Release ## Release
### Build ### Automated (Recommended)
Push to main triggers Woodpecker CI to build and deploy:
```bash ```bash
# Build binary # Push to both remotes - Woodpecker builds and deploys automatically
GOOS=linux GOARCH=amd64 go build -o rdev-api ./cmd/rdev-api git push origin main
GITEA_TOKEN=$(kubectl get secret rdev-credentials -n rdev -o jsonpath='{.data.GITEA_TOKEN}' | base64 -d)
# Build Docker image git push https://jordan:${GITEA_TOKEN}@git.threesix.ai/jordan/rdev.git main
docker build -t ghcr.io/orchard9/rdev-api:1.0.0 .
# Push image
docker push ghcr.io/orchard9/rdev-api:1.0.0
``` ```
Images are built via kaniko and pushed to `registry.threesix.ai/rdev/*`.
### Tag ### Tag
```bash ```bash
git tag -a v1.0.0 -m "Release v1.0.0" git tag -a v1.0.0 -m "Release v1.0.0"
git push origin v1.0.0 git push origin v1.0.0
``` ```
### Deploy ### Manual Deploy (if needed)
```bash ```bash
# Update image tag in kustomization export KUBECONFIG=~/.kube/orchard9-k3sf.yaml
# Apply to cluster kubectl apply -f deployments/k8s/base/rdev-api.yaml
kubectl apply -k deployments/k8s/overlays/prod kubectl rollout restart -n rdev deployment/rdev-api
# Verify deployment # Verify deployment
kubectl -n rdev rollout status deployment/rdev-api kubectl -n rdev rollout status deployment/rdev-api