vibecarbon 0.1.4 → 0.1.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -53,7 +53,7 @@
53
53
  "@base-ui/react": "^1.5.0",
54
54
  "@fontsource-variable/noto-sans": "^5.2.10",
55
55
  "@fontsource/jetbrains-mono": "^5.2.8",
56
- "@hono/node-server": "^1.19.13",
56
+ "@hono/node-server": "^2.0.4",
57
57
  "@hono/zod-openapi": "^1.4.0",
58
58
  "@hookform/resolvers": "^5.4.0",
59
59
  "@mdx-js/react": "^3.1.1",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "vibecarbon",
3
- "version": "0.1.4",
3
+ "version": "0.1.5",
4
4
  "description": "Create and manage production-ready Vibecarbon applications",
5
5
  "type": "module",
6
6
  "bin": "./src/cli.js",
@@ -61,7 +61,7 @@
61
61
  "@pulumi/pulumi": "^3.231.0",
62
62
  "bcryptjs": "^3.0.3",
63
63
  "undici": "^8.1.0",
64
- "which": "^6.0.1"
64
+ "which": "^7.0.0"
65
65
  },
66
66
  "pnpm": {
67
67
  "onlyBuiltDependencies": [
@@ -87,9 +87,9 @@
87
87
  "better-sqlite3": "^12.8.0",
88
88
  "conventional-changelog-conventionalcommits": "^9.3.1",
89
89
  "msw": "^2.12.14",
90
- "semantic-release": "^23.0.8",
90
+ "semantic-release": "^25.0.3",
91
91
  "tsx": "^4.21.0",
92
- "vite": "^7.3.2",
92
+ "vite": "^8.0.14",
93
93
  "vitest": "^4.1.1",
94
94
  "zod": "^4.3.6"
95
95
  }
@@ -207,6 +207,12 @@ export function renderBundle(projectName, options = {}) {
207
207
  copyDirIfExist('volumes/db', 'volumes/db');
208
208
  copyDirIfExist('supabase/migrations', 'supabase/migrations');
209
209
  copyDirIfExist('volumes/kong', 'volumes/kong');
210
+ // Edge functions: docker-compose.prod.yml bind-mounts ./functions and starts
211
+ // the edge-runtime with `--main-service /home/deno/functions/main`. If this
212
+ // isn't bundled, the mount auto-creates an empty dir on the server and the
213
+ // runtime crash-loops with "could not find an appropriate entrypoint". The
214
+ // template ships a stub at functions/main/index.ts. RCA: prod-1 2026-05-26.
215
+ copyDirIfExist('functions', 'functions');
210
216
 
211
217
  // volumes/traefik/middlewares.yml
212
218
  const traefikMiddlewares = join(cwd, 'volumes', 'traefik', 'middlewares.yml');
@@ -734,17 +734,40 @@ export async function pullComposeImages(ip, sshKeyPath, projectName, options = {
734
734
  export async function runMigrations(ip, sshKeyPath, projectName) {
735
735
  const remoteDir = `/opt/${projectName}`;
736
736
 
737
- // Wait for PostgreSQL to be ready, then run every migration all in one
738
- // SSH round-trip. A `for f in *.sql; do ...; done` on the remote shell
739
- // executes the migrations sequentially without N+1 SSH handshakes (one
740
- // ls, one psql per file = N+1 trips before). `|| true` per-file keeps a
741
- // bad migration from halting the rest (matches prior behaviour).
737
+ // On a fresh volume, supabase/postgres runs its own first-boot initdb scripts
738
+ // (creating the supabase_admin role + auth/storage/realtime schemas) before
739
+ // our app migrations can apply. `pg_isready` flips true as soon as PG accepts
740
+ // connections which can be *during* that init, before supabase_admin
741
+ // exists. Running migrations then errors (missing role/extension). Previously
742
+ // those errors were piped to /dev/null with `|| true`, so a fully-failed
743
+ // migration was indistinguishable from success and an empty schema shipped to
744
+ // prod (RCA prod-1 2026-05-26: 0 public tables, every DB feature 500'd).
745
+ //
746
+ // Gate on supabase_admin actually being able to run a query (not just
747
+ // pg_isready), polling up to ~3 min.
742
748
  await sshRunAsync(
743
749
  ip,
744
750
  sshKeyPath,
745
- `cd ${remoteDir} && docker compose exec -T db pg_isready -U postgres && ` +
751
+ `cd ${remoteDir} && ` +
752
+ `for i in $(seq 1 60); do ` +
753
+ `docker compose exec -T db psql -U supabase_admin -d postgres -c 'SELECT 1' >/dev/null 2>&1 && exit 0; ` +
754
+ `echo "[migrate] waiting for supabase_admin to accept queries (attempt $i/60)"; sleep 3; ` +
755
+ `done; echo "[migrate] supabase_admin never became ready" >&2; exit 1`,
756
+ { timeout: 240_000 },
757
+ );
758
+
759
+ // Apply each migration with ON_ERROR_STOP=1 and abort on the first failure.
760
+ // A failed migration MUST fail the deploy — silently shipping an empty schema
761
+ // is far worse than a deploy the operator can see failed and retry. Errors
762
+ // propagate (no 2>/dev/null, no `|| true`); sshRunAsync throws on non-zero.
763
+ await sshRunAsync(
764
+ ip,
765
+ sshKeyPath,
766
+ `cd ${remoteDir} && ` +
746
767
  `for f in $(ls supabase/migrations/ 2>/dev/null | sort); do ` +
747
- `cat "supabase/migrations/$f" | docker compose exec -T db psql -U supabase_admin -d postgres 2>/dev/null || true; ` +
768
+ `echo "[migrate] applying $f"; ` +
769
+ `cat "supabase/migrations/$f" | docker compose exec -T db psql -U supabase_admin -d postgres -v ON_ERROR_STOP=1 || ` +
770
+ `{ echo "[migrate] FAILED applying $f" >&2; exit 1; }; ` +
748
771
  `done`,
749
772
  { timeout: 300_000 },
750
773
  );
@@ -809,20 +832,28 @@ export async function deployCompose(options) {
809
832
  onProgress('Starting services...');
810
833
  await startComposeStack(ip, sshKeyPath, projectName, serviceOpts);
811
834
 
812
- // Step 5: Run migrations
835
+ // Step 5: Run migrations. A failure here means an empty/partial schema, which
836
+ // makes every DB-backed feature 500 — so let it abort the deploy rather than
837
+ // swallowing it (RCA prod-1 2026-05-26: the previous blanket try/catch hid a
838
+ // fully-failed migration and shipped a schema-less prod that reported success).
813
839
  onProgress('Running database migrations...');
814
840
  const remoteDir = `/opt/${projectName}`;
815
- try {
816
- await runMigrations(ip, sshKeyPath, projectName);
817
- // Reload PostgREST schema cache so it sees newly created tables
818
- await sshRunAsync(
819
- ip,
820
- sshKeyPath,
821
- `cd ${remoteDir} && docker compose exec -T rest kill -s SIGUSR1 1 2>/dev/null || true`,
822
- );
823
- } catch {
824
- // Migrations may partially fail on first run, non-fatal
825
- }
841
+ await runMigrations(ip, sshKeyPath, projectName);
842
+ // Reload PostgREST schema cache so it sees newly created tables. The old
843
+ // `docker compose exec rest kill -s SIGUSR1 1` never worked — the
844
+ // postgrest/postgrest image has no `kill` binary ("exec: kill: executable
845
+ // file not found in $PATH"), so the reload silently no-op'd and fresh tables
846
+ // stayed invisible (PGRST205) until rest happened to restart. RCA prod-1
847
+ // 2026-05-26. Use the canonical SQL NOTIFY on the pgrst channel instead
848
+ // (db-channel-enabled defaults on); fall back to restarting rest if NOTIFY
849
+ // doesn't land. Best-effort: a missed reload self-heals on the next deploy.
850
+ await sshRunAsync(
851
+ ip,
852
+ sshKeyPath,
853
+ `cd ${remoteDir} && ` +
854
+ `docker compose exec -T db psql -U postgres -d postgres -c "NOTIFY pgrst, 'reload schema'" 2>/dev/null || ` +
855
+ `docker compose restart rest 2>/dev/null || true`,
856
+ );
826
857
 
827
858
  return {
828
859
  success: true,