-
Notifications
You must be signed in to change notification settings - Fork 39
chore(claude): add dev container skills for Claude Code #2741
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Changes from 1 commit
Commits
Show all changes
6 commits
Select commit
Hold shift + click to select a range
61cf21e
chore(claude): add dev container skills for Claude Code
hvn2k1 f53ffa2
fix: harden devc-exec.sh detection and status reporting
hvn2k1 9cb9185
chore: remove unrelated datadog-agent manifest from this branch
hvn2k1 19dbe51
refactor: rename devc skills to devcontainer-* and split docs by audi…
hvn2k1 a6ce77a
Update SKILL.md
hvn2k1 ab6feeb
fix(claude): pin dev-container skills to the minikube context
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,87 @@ | ||
| --- | ||
| name: devc-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 | ||
| "devc-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. | ||
| --- | ||
|
|
||
| # devc-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. All commands go | ||
| through the devc wrapper (see the `devc` skill): | ||
|
|
||
| ```bash | ||
| DEVC="bash .claude/skills/devc/scripts/devc-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 rollout restart deployment <name> && kubectl rollout status deployment <name>' | ||
| ``` | ||
|
|
||
| The Bucketeer deployments are `api`, `web`, `batch-server`, and `subscriber` | ||
| (confirm with `$DEVC 'kubectl get deployments'`). Chart-level | ||
| changes instead: `helm upgrade bucketeer manifests/bucketeer/ --values | ||
| manifests/bucketeer/values.dev.yaml`. | ||
|
|
||
| ## 4. Verify | ||
|
|
||
| ```bash | ||
| $DEVC 'kubectl get pods' # everything Running/Completed, restarts not climbing | ||
| $DEVC 'curl -sk https://web-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 logs deploy/<name> --tail=100'` and | ||
| `$DEVC 'kubectl 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`. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,62 @@ | ||
| --- | ||
| name: devc-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 "devc-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. | ||
| --- | ||
|
|
||
| # devc-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. | ||
|
|
||
| All commands below go through the devc wrapper (see the `devc` skill for how | ||
| detection works): | ||
|
|
||
| ```bash | ||
| DEVC="bash .claude/skills/devc/scripts/devc-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`: | ||
| `grep -m1 "protoc " proto/<domain>/<file>.pb.go` | ||
| - 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 devc script tells you which mode you're in. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,75 @@ | ||
| --- | ||
| name: devc | ||
| description: >- | ||
| 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 "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). devc-generate and devc-deploy build on this skill. | ||
| --- | ||
|
|
||
| # devc — 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). | ||
|
|
||
| ## 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/devc/scripts/devc-exec.sh status | ||
|
|
||
| # Run any command in /workspaces/bucketeer inside the container | ||
| bash .claude/skills/devc/scripts/devc-exec.sh 'make build-api' | ||
| bash .claude/skills/devc/scripts/devc-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`. | ||
|
|
||
| Exit code 2 means no container was found; 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 | ||
|
|
||
| - 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/devc/scripts/devc-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. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,120 @@ | ||
| #!/usr/bin/env bash | ||
| # Locate the Bucketeer dev container and run a command inside it. | ||
| # | ||
| # Usage: | ||
| # devc-exec.sh status Report where the dev container is and its health | ||
| # devc-exec.sh <command ...> Run a command in /workspaces/bucketeer inside it | ||
| # | ||
| # Exit codes: 0 success, 2 no dev container found, otherwise the command's exit code. | ||
| set -euo pipefail | ||
|
|
||
| WORKDIR=/workspaces/bucketeer | ||
| # go-tools live in a persistent volume; not on PATH in non-login shells | ||
| SETUP_PATH='export PATH=/home/codespace/go-tools/bin:$PATH' | ||
|
|
||
| MODE="" | ||
| CID="" | ||
| CODESPACE="" | ||
|
|
||
| detect() { | ||
| # Case 1: this shell is already inside the dev container | ||
| if [ -d "$WORKDIR" ] && [ "$(id -un)" = "codespace" ]; then | ||
| MODE=inside | ||
| return | ||
| fi | ||
|
|
||
| # Case 2: local devcontainer (VS Code "Reopen in Container" / devcontainer CLI) | ||
| local repo_root | ||
| repo_root="$(git rev-parse --show-toplevel 2>/dev/null || pwd)" | ||
| CID="$(docker ps -q --filter "label=devcontainer.local_folder=$repo_root" 2>/dev/null | head -1 || true)" | ||
| if [ -z "$CID" ]; then | ||
| # Fallback: any running devcontainer whose local folder looks like a bucketeer checkout | ||
| CID="$(docker ps --filter "label=devcontainer.local_folder" \ | ||
| --format '{{.ID}} {{.Label "devcontainer.local_folder"}}' 2>/dev/null \ | ||
| | awk '$2 ~ /bucketeer/ {print $1; exit}' || true)" | ||
| fi | ||
| if [ -n "$CID" ]; then | ||
| MODE=local | ||
| return | ||
| fi | ||
|
|
||
| # Case 3: GitHub Codespace (requires `gh` with the codespace scope) | ||
| CODESPACE="$(gh codespace list --json name,repository,state \ | ||
| -q '.[] | select((.repository | endswith("/bucketeer")) and .state == "Available") | .name' 2>/dev/null \ | ||
| | head -1 || true)" | ||
| if [ -n "$CODESPACE" ]; then | ||
| MODE=codespace | ||
| return | ||
| fi | ||
| } | ||
|
|
||
| run_inside() { | ||
| local cmd="$1" | ||
| case "$MODE" in | ||
| inside) | ||
| bash -c "cd $WORKDIR && $SETUP_PATH && $cmd" | ||
| ;; | ||
| local) | ||
| docker exec -u codespace -w "$WORKDIR" "$CID" bash -c "$SETUP_PATH && $cmd" | ||
| ;; | ||
| codespace) | ||
| gh codespace ssh -c "$CODESPACE" -- "cd $WORKDIR && $SETUP_PATH && $cmd" | ||
| ;; | ||
| esac | ||
| } | ||
|
|
||
| not_found() { | ||
| echo "No running Bucketeer dev container found." >&2 | ||
| echo "" >&2 | ||
| echo "Checked: this shell, local devcontainers (docker label devcontainer.local_folder)," >&2 | ||
| echo "and GitHub Codespaces (gh codespace list)." >&2 | ||
| echo "" >&2 | ||
| echo "To start one:" >&2 | ||
| echo " - VS Code: 'Dev Containers: Reopen in Container' on this repo" >&2 | ||
| echo " - CLI: devcontainer up --workspace-folder ." >&2 | ||
| echo " - Codespace: gh codespace create -R bucketeer-io/bucketeer" >&2 | ||
| if ! gh codespace list >/dev/null 2>&1; then | ||
| echo "" >&2 | ||
| echo "Note: 'gh codespace list' failed — if you use Codespaces, grant the scope with:" >&2 | ||
| echo " gh auth refresh -h github.com -s codespace" >&2 | ||
| fi | ||
| exit 2 | ||
| } | ||
|
|
||
| status_report() { | ||
| case "$MODE" in | ||
| inside) echo "mode: inside (this shell is already in the dev container)" ;; | ||
| local) echo "mode: local devcontainer (docker exec, container $CID)" | ||
| echo "workspace: bind-mounted from the host — file changes appear in the host repo directly" ;; | ||
| codespace) echo "mode: GitHub Codespace '$CODESPACE' (gh codespace ssh)" | ||
| echo "workspace: SEPARATE clone — changes made inside do NOT appear in the host repo" ;; | ||
| esac | ||
| echo "---" | ||
| run_inside ' | ||
| echo "user: $(id -un) workdir: $(pwd)" | ||
| echo "protoc: $(protoc --version 2>/dev/null || echo MISSING)" | ||
| command -v mockgen >/dev/null && echo "go-tools: OK" || echo "go-tools: MISSING (run bash .devcontainer/setup.sh)" | ||
| docker info >/dev/null 2>&1 && echo "dockerd: running" || echo "dockerd: NOT running (start with: nohup sudo dockerd > /tmp/dockerd.log 2>&1 &)" | ||
| if minikube status >/dev/null 2>&1; then | ||
| # Ignore transient states (Pending/ContainerCreating/Init) — batch CronJobs | ||
| # constantly spawn short-lived pods and would make the count flap. | ||
| pods=$(kubectl get pods --no-headers 2>/dev/null) | ||
| total=$(echo "$pods" | grep -c . || true) | ||
| failing=$(echo "$pods" | grep -cE "CrashLoopBackOff|ImagePull|ErrImage|Error|OOMKilled|Evicted" || true) | ||
| echo "minikube: running ($total pods, $failing failing)" | ||
| [ "$failing" -gt 0 ] && echo "$pods" | grep -E "CrashLoopBackOff|ImagePull|ErrImage|Error|OOMKilled|Evicted" | ||
|
hvn2k1 marked this conversation as resolved.
Outdated
|
||
| else | ||
| echo "minikube: NOT running (start with: make start-minikube — never minikube start)" | ||
| fi | ||
| echo "git: $(git status --porcelain | wc -l | tr -d " ") modified files on branch $(git branch --show-current)" | ||
| ' | ||
| } | ||
|
|
||
| detect | ||
| [ -z "$MODE" ] && not_found | ||
|
|
||
| if [ "$#" -eq 0 ] || [ "$1" = "status" ]; then | ||
| status_report | ||
| else | ||
| run_inside "$*" | ||
| fi | ||
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.