Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 17 additions & 7 deletions .claude/CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -89,10 +89,26 @@ make mockgen # Regenerate only mocks (after changing interfaces that h
- Run `make proto-all` after any `.proto` file change — this regenerates Go bindings and OpenAPI/Swagger specs.
- Run `make mockgen` after changing any Go interface that has generated mocks in a `mock/` directory.
- The generated files are committed to the repo. `protoc` v23.4 must be on PATH ahead of any other version for the version header in `.pb.go` files to stay at `v4.23.4`.
- Prefer running generation inside the dev container via the `devcontainer-generate` skill — the container guarantees protoc v23.4, while a host version mismatch churns every generated file.

### Local development

**Docker Compose (recommended for most development):**
**Dev container + Minikube (preferred):**

Development actions — proto generation, builds, deploys, kubectl/helm — should run **inside the Bucketeer dev container**, where tool versions are guaranteed (protoc v23.4, go-tools, minikube/helm/kubectl). Check for a running dev container first before running these on the host. Use the project skills:

- `devcontainer-run` — detect the running dev container (local devcontainer or Codespace) and run any command inside it: `bash .claude/skills/devcontainer-run/scripts/exec.sh status`
- `devcontainer-generate` — proto/mock generation inside the container
- `devcontainer-deploy` — deploy to the minikube cluster inside the container

```bash
make start-minikube # Inside the dev container. Always use this, not `minikube start` directly
make deploy-bucketeer # Deploy all Helm charts to minikube
```

Never run kubectl/helm bare on the host — the host kubectl context may point at a real GKE cluster, not minikube.

**Docker Compose (host-based alternative):**
```bash
make docker-compose-up # Start all services
make docker-compose-status # Check status
Expand All @@ -107,12 +123,6 @@ Add to `/etc/hosts`:
127.0.0.1 api-gateway.bucketeer.io
```

**Minikube (Kubernetes-based):**
```bash
make start-minikube # Always use this, not `minikube start` directly
make deploy-bucketeer # Deploy all Helm charts
```

### Database migrations
```bash
make migration-validate # Validate migration files with Atlas
Expand Down
95 changes: 95 additions & 0 deletions .claude/skills/devcontainer-deploy/SKILL.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
---
name: devcontainer-deploy
description: >-
Build and deploy Bucketeer to the minikube cluster inside the dev container,
or redeploy/restart a single service there. Use this whenever the user wants
to deploy locally, run "make deploy-bucketeer", start minikube, get their
code changes running in the dev cluster, restart a crashing pod, or says
"devcontainer-deploy", "deploy to minikube", "redeploy the backend". Also use it to
check deployment health (pods not ready, gateway not responding) in the dev
container environment.
---

# devcontainer-deploy — deploy Bucketeer inside the dev container

Deployment target is the minikube cluster *inside* the dev container (helm
charts in `manifests/`), not the host docker-compose stack. (Human-facing
docs for these flows: `DEVELOPMENT.md` § "Deploy Bucketeer".) All commands go
through the devcontainer-run wrapper (see the `devcontainer-run` skill):

```bash
DEVC="bash .claude/skills/devcontainer-run/scripts/exec.sh"
```

## 1. Preflight

```bash
$DEVC status
```

- `dockerd` not running → start it (command is in the status output) — image
builds need it.
- `minikube` not running → `$DEVC 'make start-minikube'`. Never `minikube start`
directly. If minikube IS already running, skip this: the target intentionally
exits 1 with "minikube is already running" — that is not an error to fix.

## 2. Full deploy

```bash
$DEVC 'make deploy-bucketeer'
```

What it does, so failures are diagnosable: uninstalls the existing `bucketeer`
helm release → regenerates cert/token/oauth secrets → builds all Go binaries →
builds docker images with `TAG=localenv` → loads them into minikube → helm
install/upgrade `localenv` (MySQL, Redis, Pub/Sub emulator, optionally
Postgres/BigQuery emulator) → helm install `bucketeer` with
`manifests/bucketeer/values.dev.yaml`.

This takes many minutes. Run it with a 600000 timeout or `run_in_background`
and monitor. Postgres/BigQuery enablement is auto-detected from
`dataWarehouse` in `values.dev.yaml` — don't set it manually, but remember the
invariant: `web` and `subscriber` must use the same event store, so data
warehouse changes belong in `values.dev.yaml`, not ad-hoc helm flags.

## 3. Single service — faster than a full deploy

For a code change to one service (e.g. backend):

```bash
$DEVC 'make build-go-embed && TAG=localenv make build-docker-images && TAG=localenv make minikube-load-images'
$DEVC 'kubectl --context minikube rollout restart deployment <name> && kubectl --context minikube rollout status deployment <name>'
```

The Bucketeer deployments are `api`, `web`, `batch-server`, and `subscriber`
(confirm with `$DEVC 'kubectl --context minikube get deployments'`). Chart-level
changes instead:

```bash
$DEVC 'helm upgrade bucketeer manifests/bucketeer/ --kube-context minikube --values manifests/bucketeer/values.dev.yaml'
```

Always go through `$DEVC` and always name the context: a bare `helm`/`kubectl`
runs against whatever context is currently active, which on the host is often a
real cluster. `$DEVC status` warns when the active context is not `minikube`.

## 4. Verify

```bash
$DEVC 'kubectl --context minikube get pods' # everything Running/Completed, restarts not climbing
$DEVC 'curl -sk https://api-gateway.bucketeer.io/health' # must run INSIDE the container
```

The `*.bucketeer.io` hosts entries live in the container's `/etc/hosts`
(pointed at `minikube ip`) — curl from the host proves nothing. For a failing
pod: `$DEVC 'kubectl --context minikube logs deploy/<name> --tail=100'` and
`$DEVC 'kubectl --context minikube describe pod <pod>'`.

## Related dev-cluster chores

- Bootstrap e2e accounts (after a fresh deploy, before e2e tests):
`$DEVC 'make create-dev-container-e2e-accounts'`
- Wipe e2e data: `$DEVC 'make delete-dev-container-mysql-data'` (or the
`-postgres-` variant). These are destructive — confirm with the user first.
- MySQL from inside the container: host `$(minikube ip)`, port 32000,
user/pass `bucketeer`, db `bucketeer`.
65 changes: 65 additions & 0 deletions .claude/skills/devcontainer-generate/SKILL.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
---
Comment thread
t-kikuc marked this conversation as resolved.
name: devcontainer-generate
description: >-
Regenerate Bucketeer protobuf Go bindings, OpenAPI/Swagger specs, and gomock
files inside the dev container, where protoc is guaranteed to be exactly
v23.4. Use this whenever a .proto file changed, generated *.pb.go /
*.pb.gw.go / swagger files need regenerating, a mocked Go interface changed
(mockgen), or the user says "generate proto", "regen protos", "make
proto-all", "make mockgen", or "devcontainer-generate". Prefer this over running
protoc or make proto-all on the host — a host protoc version mismatch
silently rewrites every generated file's header.
---

# devcontainer-generate — code generation inside the dev container

Generated files are committed to the repo, and their headers record the protoc
version (`protoc v4.23.4`). The dev container ships exactly protoc 23.4, so
generation must happen there; a different host protoc churns every `.pb.go`
file and the PR becomes unreviewable. (Human-facing background:
`DEVELOPMENT.md` § "Working with the dev container from the host".)

All commands below go through the devcontainer-run wrapper (see the `devcontainer-run` skill for how
detection works):

```bash
DEVC="bash .claude/skills/devcontainer-run/scripts/exec.sh"
```

## 1. Pick the right target

| What changed | Target |
|---|---|
| `.proto` files | `make proto-all` |
| A Go interface that has generated mocks in a `mock/` dir | `make mockgen` |
| Both, or unsure | `make generate-all` |

## 2. Run it

```bash
$DEVC 'make proto-all' # or make mockgen / make generate-all
```

This is minutes-long; use a generous Bash timeout (600000). If the run fails
on protolock, the `.proto` change broke backward compatibility — read the
error; don't force it without flagging the compatibility break to the user.

## 3. Verify before declaring success

- `git status --porcelain` (on the host for a local devcontainer; inside via
`$DEVC 'git status --porcelain'` for a codespace) — the changed files should
be only the ones related to your proto/interface change plus their generated
outputs. **A diff touching every `.pb.go` in the repo means a wrong protoc
version — abort and check `$DEVC 'protoc --version'`.**
- Spot-check one regenerated file's header still says `protoc v4.23.4`:
`$DEVC 'grep -m1 "protoc " proto/<domain>/<file>.pb.go'` — run it through
`$DEVC`, not bare: in codespace mode the host clone is a *different* checkout,
so a bare `grep` would validate a stale file that was never regenerated.
- Build still compiles: `$DEVC 'make build-go'` (or the affected
`make build-<service>`), and `$DEVC 'make gofmt'` after any Go changes.

## Codespace caveat

In a codespace the regenerated files land in the codespace's clone, not the
host repo. Commit/push from inside, or copy back with `gh codespace cp`. The
`status` subcommand of the devcontainer-run script tells you which mode you're in.
85 changes: 85 additions & 0 deletions .claude/skills/devcontainer-run/SKILL.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
---
name: devcontainer-run
Comment thread
hvn2k1 marked this conversation as resolved.
description: >-
Comment thread
t-kikuc marked this conversation as resolved.
Detect the running Bucketeer dev container (local VS Code devcontainer or
GitHub Codespace) and run commands inside it. Use this whenever a task should
run in the dev container environment — make targets, builds, tests, kubectl /
helm / minikube commands, checking whether the container is up — or when the
user says "devcontainer-run", "devc", "dev container", "devcontainer", "codespace", or "run this
inside the container". Also use it when a task needs tools the container
guarantees but the host may lack (protoc 23.4, mockgen, protolock, helm,
kubectl, minikube). devcontainer-generate and devcontainer-deploy build on this skill.
---

# devcontainer-run — run commands inside the Bucketeer dev container

The dev container is the canonical Bucketeer development environment: Ubuntu with
docker-in-docker, minikube + helm + kubectl, protoc v23.4, and Go tooling in
`/home/codespace/go-tools/bin` (a persistent volume, NOT on PATH in plain
non-login shells). The workspace is `/workspaces/bucketeer`, the user is
`codespace` (passwordless sudo).

Human-facing documentation for this environment lives in `DEVELOPMENT.md`
("Working with the dev container from the host" and the Minikube sections);
this skill is the Claude-oriented operational version — when changing one,
keep the other in sync.

## How to run anything inside it

Always go through the wrapper script — it finds the container and sets up PATH:

```bash
# Where is the container, and is the environment healthy?
bash .claude/skills/devcontainer-run/scripts/exec.sh status

# Run any command in /workspaces/bucketeer inside the container
bash .claude/skills/devcontainer-run/scripts/exec.sh 'make build-api'
bash .claude/skills/devcontainer-run/scripts/exec.sh 'kubectl get pods'
```

Detection order (the script handles all of this):
1. Already inside the container (`/workspaces/bucketeer` exists, user `codespace`) → run directly.
2. Local devcontainer → `docker ps` filtered by label `devcontainer.local_folder=<repo root>`, exec via `docker exec`.
3. GitHub Codespace → `gh codespace list` (needs the `codespace` auth scope), exec via `gh codespace ssh`.

If more than one available Bucketeer codespace matches (a fork is also named
`bucketeer`, or you keep several), the script refuses to guess and exits 2 with
the list — pick one with `export BUCKETEER_CODESPACE=<name>`.

Exit code 2 means no container was found (or the codespace was ambiguous); the
script prints how to start one.
Don't fall back to running the command on the host in that case — tell the user
and let them choose, because host tool versions (especially protoc) may differ.

## Local devcontainer vs Codespace — the one difference that matters

- **Local devcontainer**: `/workspaces/bucketeer` is a bind mount of the host
repo. Files generated inside appear in the host working tree immediately.
- **Codespace**: a separate clone. Generated or edited files stay in the
codespace. To get them back: commit and push from inside, or
`gh codespace cp 'remote:/workspaces/bucketeer/<path>' <local-path>`.
Always tell the user which mode you're in when file changes are involved
(`status` prints it).

## Environment facts and gotchas

Comment thread
t-kikuc marked this conversation as resolved.
- Long commands (image builds, deploys) can take many minutes — use a generous
Bash timeout (600000) or `run_in_background`.
- `dockerd` inside the container is started by the post-attach hook, but that
only fires when an editor attaches. If `status` says it's not running:
`bash .claude/skills/devcontainer-run/scripts/exec.sh 'nohup sudo dockerd > /tmp/dockerd.log 2>&1 & sleep 5 && docker info > /dev/null && echo ok'`
- minikube must be started with `make start-minikube`, never `minikube start`
directly (the make target restores the cluster config and localenv services).
Note: `make start-minikube` intentionally **exits 1 if minikube is already
running** — check `minikube status` first instead of treating that as failure.
- `web-gateway.bucketeer.io` / `api-gateway.bucketeer.io` resolve via the
container's own `/etc/hosts` (pointed at `minikube ip`). Health checks with
curl against those hosts must run *inside* the container, not on the host.
- If go-tools are missing or permissions look broken, the fix is the setup
script: `bash .devcontainer/setup.sh` (idempotent, cache-aware).
- **Never run kubectl/helm bare on the host for dev work.** The host's kubectl
context may point at a real GKE cluster, not minikube — always go through the
wrapper so commands hit the cluster inside the container.
- The host may also run a docker-compose Bucketeer stack in parallel
(`docker-compose/compose.yml`). That is a different environment — this skill
is only about the dev container / minikube world.
Loading
Loading