Unified deploy commands
Implement the following plan:
Unified deploy commands
Goal
Replace the multi-step deployment process with bun run deploy:dev and bun run deploy:prod. Each command runs the full pipeline (migrate → build → push → restart) with quiet output on success and full output on failure.
New file: scripts/deploy.sh
Single shell script, takes dev or prod as first argument. Runs these steps in order:
- Migrate database — port-forward to K8s postgres, run drizzle migrations against the appropriate database (dev or prod)
- Build Docker images —
docker buildfor web, api, mail with the appropriate tag (devorlatest+version) - Push Docker images —
docker pushall built images - Restart deployments —
kubectl rollout restartfor web and api, wait for rollout completion
Output strategy
Each step prints a single-line status while running (e.g., Migrating dev database...). On success, overwrite with a checkmark line. On failure, dump the captured output and exit.
Pattern per step:
step "Migrating database" migrate_db
Where step is a helper that:
- Prints "● Step name..."
- Runs the function, capturing stdout/stderr to a temp file
- On success: prints "✓ Step name"
- On failure: prints "✗ Step name", dumps captured output, exits 1
Key details
- Gets
DB_PASSWORDfrom K8s secret, constructs DATABASE_URL for the target db - Port-forward started/stopped automatically within the migrate step (background process with trap cleanup)
- Docker tag:
devfor dev,latest+version for prod (version fromapps/web/package.json) - Checks Docker Hub login before push step
- Restarts web + api deployments (not mail — it rarely changes; can add
--allflag later if needed)
Changes to package.json
Add two scripts, remove obsolete individual k8s scripts:
"deploy:dev": "./scripts/deploy.sh dev",
"deploy:prod": "./scripts/deploy.sh prod",
"restart:dev": "kubectl rollout restart deployment/dev-web deployment/dev-api -n usingandunderstandingai && kubectl rollout status deployment/dev-web deployment/dev-api -n usingandunderstandingai",
"restart:prod": "kubectl rollout restart deployment/web deployment/api -n usingandunderstandingai && kubectl rollout status deployment/web deployment/api -n usingandunderstandingai",
Remove these scripts (the underlying files stay for now, but they're no longer the primary interface):
k8s:buildk8s:pushk8s:build-push
Keep these (used for initial cluster setup, not regular deploys):
k8s:deployk8s:teardown
Changes to CLAUDE.md
Update the deployment section to show the new commands as the primary interface:
bun run deploy:dev # Full deploy to dev (migrate + build + push + restart)
bun run deploy:prod # Full deploy to prod
bun run restart:dev # Quick restart (no rebuild)
bun run restart:prod # Quick restart (no rebuild)
Files changed
| File | Change |
|---|---|
scripts/deploy.sh | New — unified deploy script |
package.json | Add deploy:dev, deploy:prod, restart:dev, restart:prod; remove k8s:build, k8s:push, k8s:build-push |
CLAUDE.md | Update deployment docs |
Verification
bun run deploy:dev— should migrate dev db, build dev images, push, restart dev deployments with minimal outputbun run deploy:prod— same for prodbun run restart:dev/restart:prod— quick restart without rebuild- Intentionally break a step (e.g., kill port-forward) to verify failure output is shown
If you need specific details from before exiting plan mode (like exact code snippets, error messages, or content you generated), read the full transcript at: ~/.claude/projects/-Users-challen-www-usingandunderstanding-ai/06692091-d3c7-4fd9-993c-4d51d96caa44.jsonl
I'll start by reading the existing files I need to understand and modify.
Now let me read the existing migration scripts to understand how they work.
Now I have all the context I need. Let me check the existing scripts/ directory and create the deploy script.
Now I'll create all three files in parallel.
Let me verify the final state of all three files.
Everything looks correct. Here's a summary of the changes:
New file: scripts/deploy.sh — Unified deploy script that takes dev or prod as argument and runs four steps sequentially:
- Migrate database (port-forwards to K8s postgres, runs drizzle migrations)
- Build Docker images (web, api, mail with appropriate tags)
- Push Docker images (checks Docker Hub login first)
- Restart deployments (web + api, waits for rollout completion)
Each step uses the step helper for clean output: one line while running, checkmark on success, full captured output dumped on failure.
Modified: package.json — Added four scripts, removed three:
- Added:
deploy:dev,deploy:prod,restart:dev,restart:prod - Removed:
k8s:build,k8s:push,k8s:build-push - Kept:
k8s:deploy,k8s:teardown(cluster setup)
Modified: CLAUDE.md — Replaced the multi-step deployment instructions with the new commands and reorganized the build scripts section as "Cluster Setup Scripts".
Your task
First, run git status, git diff, and git log --oneline -10 to understand the current state.
Then add and commit all modified files with a descriptive message. Break the changes into multiple commits if appropriate. If there are untracked files, ask if they should be added or added to the appropriate .gitignore file. The user may also want to take over at this point, so provide an abort option as well. Always check changes for unnecessary debugging statements or logging that might have been accidentally included. Prompt the user if you find any, and offer to remove each one by one. Do not use interactive git staging (git add -p) as it does not work.
No debugging statements or unnecessary logging in the changes. The untracked file scripts/deploy.sh is the new deploy script and should be included. This is a single logical change, so one commit is appropriate.
Committed as c618c57 on branch deploy. All three files in a single commit — clean working tree.
OK. The deployment script should also offer the option of bumping the project versions, similar to what's done in ../cs124.org/k8s/deploy.ts. Can it do that?
(no content)
Let me check which packages have versions in this project.
All 21 workspace packages share the same version (2026.2.0). Now I have a clear picture. The version bump should prompt the user, compute the next version using the YYYY.M.MINOR scheme, and update all workspace package.json files.
Now add the version bump prompt before the deploy steps run:
Wait, what? Why is this a shell script? Implement it as a ts script please. Bun can run those directly.
(no content)
Good, I can see the project conventions: #!/usr/bin/env bun, use $ from "bun" for shell commands, parseArgs from "util". Let me rewrite the deploy script in TypeScript.
This script does not need to be able to be run directly. It should only be run by using bun run deploy:dev.
(no content)
Now delete the shell script and update package.json references.
Done. Replaced scripts/deploy.sh with scripts/deploy.ts. The script:
- Uses Bun's
$shell helper for all subprocess commands - Prompts
Bump version 2026.2.0 → 2026.2.1? [y/N]before deploying, updating all 21 workspace package.json files if accepted - Computes the next version using the YYYY.M.MINOR scheme (increments minor in same month, resets to 0 on new month)
- Runs the same four steps (migrate, build, push, restart) with quiet-on-success output
Let's migrate the bin shell scripts to be .ts scripts that are called from package.json commands, accepting arguments as appropriate. There's a lot of duplication there.
(no content)
This is a different task. Let me explore the bin/ directory to understand all the scripts there.
Now let me read the embed scripts and the current deploy.ts for the full picture.
I have a complete picture of all 11 bin/ scripts and the scripts/db-forward.sh. Now let me write the plan.