buncargo 1.0.29 → 3.2.0

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.
Files changed (246) hide show
  1. package/dist/bin.d.ts +1 -12
  2. package/dist/bin.js +261 -253
  3. package/dist/cli/bin.d.ts +13 -0
  4. package/dist/cli/bin.js +317 -0
  5. package/dist/cli/commands/help.d.ts +1 -0
  6. package/dist/cli/commands/runtime.d.ts +5 -0
  7. package/dist/cli/commands/version.d.ts +1 -0
  8. package/dist/cli/index.d.ts +1 -0
  9. package/dist/cli/index.js +14 -0
  10. package/dist/cli/run-cli.d.ts +30 -0
  11. package/dist/cli.d.ts +1 -22
  12. package/dist/cli.js +5 -13
  13. package/dist/config/config.d.ts +1 -0
  14. package/dist/config/define-config.d.ts +13 -0
  15. package/dist/config/index.d.ts +3 -0
  16. package/dist/config/index.js +15 -0
  17. package/dist/config/merge-configs.d.ts +3 -0
  18. package/dist/config/validate-config.d.ts +3 -0
  19. package/dist/config.d.ts +1 -72
  20. package/dist/config.js +12 -12
  21. package/dist/core/docker.d.ts +1 -83
  22. package/dist/core/docker.js +35 -32
  23. package/dist/core/index.d.ts +1 -1
  24. package/dist/core/index.js +123 -118
  25. package/dist/core/network.js +2 -2
  26. package/dist/core/ports.js +1 -1
  27. package/dist/core/process.js +1 -1
  28. package/dist/core/quick-tunnel/cloudflared-process.d.ts +10 -0
  29. package/dist/core/quick-tunnel/constants.d.ts +9 -0
  30. package/dist/core/quick-tunnel/index.d.ts +17 -0
  31. package/dist/core/quick-tunnel/install.d.ts +1 -0
  32. package/dist/core/tunnel.d.ts +34 -0
  33. package/dist/core/utils.js +2 -2
  34. package/dist/core/watchdog-runner.js +45 -42
  35. package/dist/core/watchdog.d.ts +1 -0
  36. package/dist/core/watchdog.js +4 -2
  37. package/dist/docker/index.d.ts +1 -0
  38. package/dist/docker/index.js +38 -0
  39. package/dist/docker/runtime.d.ts +87 -0
  40. package/dist/docker/runtime.js +37 -0
  41. package/dist/docker-compose/compose.d.ts +1 -0
  42. package/dist/docker-compose/generated-file.d.ts +7 -0
  43. package/dist/docker-compose/index.d.ts +3 -0
  44. package/dist/docker-compose/index.js +15 -0
  45. package/dist/docker-compose/model.d.ts +6 -0
  46. package/dist/docker-compose/services/clickhouse.d.ts +16 -0
  47. package/dist/docker-compose/services/define-docker-service.d.ts +41 -0
  48. package/dist/docker-compose/services/index.d.ts +23 -0
  49. package/dist/docker-compose/services/index.js +17 -0
  50. package/dist/docker-compose/services/postgres.d.ts +12 -0
  51. package/dist/docker-compose/services/redis.d.ts +12 -0
  52. package/dist/docker-compose/services/shared.d.ts +7 -0
  53. package/dist/docker-compose/yaml.d.ts +2 -0
  54. package/dist/environment/create-dev-environment.d.ts +23 -0
  55. package/dist/environment/index.d.ts +1 -0
  56. package/dist/environment/index.js +15 -0
  57. package/dist/environment/logging.d.ts +17 -0
  58. package/dist/environment/only-apps.d.ts +10 -0
  59. package/dist/environment/seeding.d.ts +9 -0
  60. package/dist/environment.d.ts +1 -23
  61. package/dist/environment.js +12 -14
  62. package/dist/index-045jksh5.js +147 -0
  63. package/dist/index-08wa79cs.js +125 -117
  64. package/dist/index-0kxnae3z.js +335 -0
  65. package/dist/index-1mdrf7nz.js +51 -43
  66. package/dist/index-1yvbwj4k.js +262 -242
  67. package/dist/index-23ev345g.js +475 -0
  68. package/dist/index-2ckr49sf.js +228 -0
  69. package/dist/index-2f47khe5.js +376 -369
  70. package/dist/index-2fr3g85b.js +220 -183
  71. package/dist/index-38xnzpa6.js +450 -0
  72. package/dist/index-3eyrdxw9.js +577 -0
  73. package/dist/index-3h3dhtf2.js +51 -43
  74. package/dist/index-42x95209.js +51 -43
  75. package/dist/index-4gp0az1g.js +145 -0
  76. package/dist/index-4xrxh8yv.js +72 -0
  77. package/dist/index-5aq985p4.js +250 -0
  78. package/dist/index-5gmws6ah.js +181 -0
  79. package/dist/index-5hka0tff.js +78 -76
  80. package/dist/index-5rfqps4b.js +3 -0
  81. package/dist/index-5t9jxqm0.js +428 -0
  82. package/dist/index-6c1w1xk5.js +101 -0
  83. package/dist/index-6cmex7m5.js +72 -0
  84. package/dist/index-6d6x175r.js +572 -0
  85. package/dist/index-6fm7mvwj.js +118 -97
  86. package/dist/index-6srpc523.js +127 -128
  87. package/dist/index-731rzzfp.js +157 -142
  88. package/dist/index-75y4cg2z.js +51 -43
  89. package/dist/index-7ja4ywyj.js +126 -127
  90. package/dist/index-7v19es2e.js +666 -0
  91. package/dist/index-8bw1cmz4.js +531 -0
  92. package/dist/index-8hbbj1mp.js +120 -121
  93. package/dist/index-8xj2p5n5.js +118 -97
  94. package/dist/index-9wyhzw0h.js +574 -0
  95. package/dist/index-ag90ry8t.js +576 -0
  96. package/dist/index-bj79tw5w.js +0 -0
  97. package/dist/index-bnk6nr0g.js +73 -0
  98. package/dist/index-brbbzyks.js +72 -0
  99. package/dist/index-byeqyjrz.js +72 -0
  100. package/dist/index-c0dr6mcv.js +123 -0
  101. package/dist/index-cty0bcry.js +235 -218
  102. package/dist/index-d8tyv5se.js +228 -0
  103. package/dist/index-d9efy0n4.js +176 -150
  104. package/dist/index-enj4zdma.js +574 -0
  105. package/dist/index-etfmqjjf.js +427 -0
  106. package/dist/index-fb29934k.js +172 -0
  107. package/dist/index-g50jw1yf.js +72 -0
  108. package/dist/index-g6eb5wdw.js +118 -117
  109. package/dist/index-ggq3yryx.js +99 -95
  110. package/dist/index-h70tce00.js +177 -0
  111. package/dist/index-hkxtfqtc.js +333 -0
  112. package/dist/index-k370bech.js +72 -0
  113. package/dist/index-kf3dhser.js +146 -143
  114. package/dist/index-ma6tgdb2.js +500 -0
  115. package/dist/index-mam0bcyz.js +123 -0
  116. package/dist/index-mm412dkp.js +274 -0
  117. package/dist/index-n8v18aeb.js +0 -0
  118. package/dist/index-ndnmnsej.js +378 -371
  119. package/dist/index-p8wty0e2.js +389 -379
  120. package/dist/index-qa8akv6y.js +666 -0
  121. package/dist/index-qfphr2fd.js +78 -76
  122. package/dist/index-qqmms8rs.js +51 -43
  123. package/dist/index-qw4093g2.js +51 -43
  124. package/dist/index-qzwpzjbx.js +121 -122
  125. package/dist/index-segbnm0h.js +146 -143
  126. package/dist/index-t0fj6gg1.js +112 -0
  127. package/dist/index-thdkwnv7.js +122 -0
  128. package/dist/index-tjbx2r2t.js +270 -0
  129. package/dist/index-tjqw9vtj.js +62 -54
  130. package/dist/index-vbpb89jy.js +248 -0
  131. package/dist/index-vg55rq0y.js +250 -0
  132. package/dist/index-vhs88xhe.js +99 -95
  133. package/dist/index-vs81yaks.js +244 -0
  134. package/dist/index-w8zxnjka.js +249 -0
  135. package/dist/index-wk2na3t9.js +385 -375
  136. package/dist/index-wz9x8g7z.js +383 -373
  137. package/dist/index-x249gyde.js +388 -378
  138. package/dist/index-x54nbgs7.js +355 -0
  139. package/dist/index-xkvd0nsd.js +187 -0
  140. package/dist/index-yedqxm1z.js +80 -0
  141. package/dist/index-yz4jfz7z.js +338 -0
  142. package/dist/index-zfjzzjkf.js +240 -199
  143. package/dist/index.d.ts +12 -8
  144. package/dist/index.js +56 -34
  145. package/dist/lint.d.ts +1 -46
  146. package/dist/lint.js +3 -7
  147. package/dist/loader/cache.d.ts +4 -0
  148. package/dist/loader/find-config-file.d.ts +2 -0
  149. package/dist/loader/index.d.ts +5 -0
  150. package/dist/loader/index.js +24 -0
  151. package/dist/loader/load-dev-env.d.ts +5 -0
  152. package/dist/loader/loader.d.ts +1 -0
  153. package/dist/loader.d.ts +1 -45
  154. package/dist/loader.js +22 -20
  155. package/dist/prisma/index.d.ts +1 -0
  156. package/dist/prisma/prisma.d.ts +29 -0
  157. package/dist/prisma.d.ts +1 -29
  158. package/dist/prisma.js +6 -10
  159. package/dist/src/bin.js +309 -0
  160. package/dist/src/cli.js +5 -0
  161. package/dist/src/config.js +15 -0
  162. package/dist/src/core/docker.js +38 -0
  163. package/dist/src/core/index.js +130 -0
  164. package/dist/src/core/network.js +9 -0
  165. package/dist/src/core/ports.js +23 -0
  166. package/dist/src/core/process.js +31 -0
  167. package/dist/src/core/utils.js +11 -0
  168. package/dist/src/core/watchdog-runner.js +69 -0
  169. package/dist/src/core/watchdog.js +28 -0
  170. package/dist/src/docker/runtime.js +37 -0
  171. package/dist/src/docker-compose/index.js +16 -0
  172. package/dist/src/docker-compose/services/index.js +17 -0
  173. package/dist/src/environment.js +12 -0
  174. package/dist/src/index.js +122 -0
  175. package/dist/src/lint.js +3 -0
  176. package/dist/src/loader.js +25 -0
  177. package/dist/src/prisma.js +6 -0
  178. package/dist/src/types.js +0 -0
  179. package/dist/typecheck/index.d.ts +1 -0
  180. package/dist/typecheck/index.js +7 -0
  181. package/dist/typecheck/typecheck.d.ts +46 -0
  182. package/dist/types/all-types.d.ts +544 -0
  183. package/dist/types/cli.d.ts +1 -0
  184. package/dist/types/config.d.ts +6 -0
  185. package/dist/types/docker.d.ts +15 -0
  186. package/dist/types/environment.d.ts +8 -0
  187. package/dist/types/hooks.d.ts +9 -0
  188. package/dist/types/index.d.ts +1 -0
  189. package/dist/types/index.js +0 -0
  190. package/dist/types/prisma.d.ts +1 -0
  191. package/dist/types.d.ts +1 -399
  192. package/package.json +55 -48
  193. package/readme.md +365 -109
  194. package/src/cli/bin.ts +77 -0
  195. package/src/cli/commands/help.ts +39 -0
  196. package/src/cli/commands/runtime.ts +72 -0
  197. package/src/cli/commands/version.ts +4 -0
  198. package/src/cli/index.ts +1 -0
  199. package/{cli.ts → src/cli/run-cli.ts} +114 -10
  200. package/src/config/define-config.ts +30 -0
  201. package/src/config/index.ts +3 -0
  202. package/src/config/merge-configs.ts +33 -0
  203. package/src/config/validate-config.ts +136 -0
  204. package/{core → src/core}/index.ts +2 -2
  205. package/{core → src/core}/ports.ts +5 -2
  206. package/{core → src/core}/process.ts +6 -2
  207. package/src/core/quick-tunnel/cloudflared-process.ts +83 -0
  208. package/src/core/quick-tunnel/constants.ts +31 -0
  209. package/src/core/quick-tunnel/index.ts +96 -0
  210. package/src/core/quick-tunnel/install.ts +160 -0
  211. package/src/core/tunnel.ts +165 -0
  212. package/{core → src/core}/utils.ts +1 -0
  213. package/{core → src/core}/watchdog.ts +5 -1
  214. package/src/docker/index.ts +1 -0
  215. package/{core/docker.ts → src/docker/runtime.ts} +11 -4
  216. package/src/docker-compose/generated-file.ts +45 -0
  217. package/src/docker-compose/index.ts +7 -0
  218. package/src/docker-compose/model.ts +197 -0
  219. package/src/docker-compose/services/clickhouse.ts +79 -0
  220. package/src/docker-compose/services/define-docker-service.ts +109 -0
  221. package/src/docker-compose/services/index.ts +67 -0
  222. package/src/docker-compose/services/postgres.ts +60 -0
  223. package/src/docker-compose/services/redis.ts +48 -0
  224. package/src/docker-compose/services/shared.ts +79 -0
  225. package/src/docker-compose/yaml.ts +88 -0
  226. package/{environment.ts → src/environment/create-dev-environment.ts} +214 -141
  227. package/src/environment/index.ts +1 -0
  228. package/src/environment/logging.ts +115 -0
  229. package/src/environment/only-apps.ts +34 -0
  230. package/src/environment/seeding.ts +57 -0
  231. package/{index.ts → src/index.ts} +52 -20
  232. package/src/loader/cache.ts +23 -0
  233. package/src/loader/find-config-file.ts +29 -0
  234. package/src/loader/index.ts +17 -0
  235. package/src/loader/load-dev-env.ts +38 -0
  236. package/src/prisma/index.ts +1 -0
  237. package/{prisma.ts → src/prisma/prisma.ts} +4 -2
  238. package/src/typecheck/index.ts +1 -0
  239. package/{types.ts → src/types/all-types.ts} +186 -8
  240. package/src/types/index.ts +1 -0
  241. package/bin.ts +0 -192
  242. package/config.ts +0 -194
  243. package/loader.ts +0 -126
  244. /package/{core → src/core}/network.ts +0 -0
  245. /package/{core → src/core}/watchdog-runner.ts +0 -0
  246. /package/{lint.ts → src/typecheck/typecheck.ts} +0 -0
package/readme.md CHANGED
@@ -1,110 +1,224 @@
1
1
  # Buncargo
2
2
 
3
- Type-safe development environment CLI for Docker Compose-based projects. Handles container lifecycle, port isolation for git worktrees, and dev server orchestration. It's easy!
3
+ A Bun-first development environment toolkit that eliminates the friction of local dev setup. Define your entire dev stack in a single typed config file—Docker services, app servers, environment variables, migrations, and more.
4
+
5
+ ## Why Buncargo?
6
+
7
+ **The problem**: Local development environments are fragile. Teams maintain separate `docker-compose.yml` files, scatter port assignments across `.env` files, manually manage container lifecycles, and struggle with port conflicts when working on multiple branches.
8
+
9
+ **The solution**: Buncargo provides a single source of truth for your dev environment. One `dev.config.ts` file defines everything. Type-safe. Auto-generated Docker Compose. Worktree-aware port isolation. Zero configuration drift.
10
+
11
+ ## Key Features
12
+
13
+ - **Single config file** — Define services, apps, ports, URLs, migrations, and hooks in one typed `dev.config.ts`
14
+ - **Auto-generated Docker Compose** — No manual compose files; buncargo generates them from your config
15
+ - **Worktree isolation** — Each git worktree gets unique ports and isolated containers automatically
16
+ - **Built-in service presets** — One-liner setup for Postgres, Redis, ClickHouse with health checks and URL templates
17
+ - **Custom service support** — Full Docker Compose escape hatch for any service
18
+ - **Dev server orchestration** — Start and monitor multiple app servers with health checks
19
+ - **Public tunnels** — Expose services via Cloudflare Quick Tunnels for webhook testing and mobile dev
20
+ - **Prisma integration** — Run Prisma commands with auto-injected `DATABASE_URL`
21
+ - **Lifecycle hooks** — Run migrations, seeders, or custom scripts at the right time
22
+ - **Programmatic API** — Access ports/URLs in tests or scripts
23
+ - **Watchdog auto-shutdown** — Containers stop automatically after inactivity
4
24
 
5
25
  ## Quick Start
6
26
 
7
- ### 1. Create `dev.config.ts` in your project root
27
+ ### 1. Install
28
+
29
+ ```bash
30
+ bun add -d buncargo
31
+ ```
32
+
33
+ ### 2. Create `dev.config.ts`
8
34
 
9
35
  ```typescript
10
- import { defineDevConfig } from 'buncargo'
36
+ import { defineDevConfig, service } from 'buncargo'
11
37
 
12
38
  export default defineDevConfig({
13
39
  projectPrefix: 'myapp',
14
40
 
15
41
  services: {
16
- postgres: {
17
- port: 5432,
18
- healthCheck: 'pg_isready',
19
- urlTemplate: ({ port }) => `postgresql://postgres:postgres@localhost:${port}/mydb`
20
- },
21
- redis: {
22
- port: 6379,
23
- healthCheck: 'redis-cli'
24
- }
42
+ postgres: service.postgres({ database: 'mydb' }),
43
+ redis: service.redis(),
25
44
  },
26
45
 
27
46
  apps: {
28
47
  api: {
29
48
  port: 3000,
30
49
  devCommand: 'bun run dev',
31
- cwd: 'apps/backend'
50
+ cwd: 'apps/backend',
32
51
  },
33
52
  web: {
34
53
  port: 5173,
35
54
  devCommand: 'bun run dev',
36
- cwd: 'apps/frontend'
37
- }
55
+ cwd: 'apps/frontend',
56
+ },
38
57
  },
39
58
 
40
59
  envVars: (ports, urls) => ({
41
60
  DATABASE_URL: urls.postgres,
42
61
  REDIS_URL: urls.redis,
43
- API_PORT: ports.api
62
+ API_PORT: ports.api,
44
63
  }),
45
-
46
- hooks: {
47
- afterContainersReady: async (ctx) => {
48
- await ctx.exec('bunx prisma migrate deploy', { cwd: 'packages/prisma' })
49
- }
50
- },
51
-
52
- prisma: {
53
- cwd: 'packages/prisma'
54
- }
55
64
  })
56
65
  ```
57
66
 
58
- ### 2. Run it
59
-
60
- ```bash
61
- bunx buncargo dev # Start containers + dev servers
62
- bunx buncargo dev --up-only # Start containers only
63
- bunx buncargo dev --down # Stop containers
64
- bunx buncargo dev --reset # Stop and remove volumes
65
- bunx buncargo typecheck # Run TypeScript typecheck across workspaces
66
- bunx buncargo prisma studio # Run prisma with correct DATABASE_URL
67
- bunx buncargo env # Print ports/urls as JSON
68
- ```
69
-
70
- Or add scripts to `package.json`:
67
+ ### 3. Add scripts to `package.json`
71
68
 
72
69
  ```json
73
70
  {
74
71
  "scripts": {
75
72
  "dev": "bunx buncargo dev",
76
- "dev:docker:down": "bunx buncargo dev --down",
77
- "typecheck": "bunx buncargo typecheck",
73
+ "dev:up": "bunx buncargo dev --up-only",
74
+ "dev:down": "bunx buncargo dev --down",
75
+ "dev:reset": "bunx buncargo dev --reset",
76
+ "dev:expose": "bunx buncargo dev --expose",
78
77
  "prisma": "bunx buncargo prisma"
79
78
  }
80
79
  }
81
80
  ```
82
81
 
83
- ## Programmatic Access
82
+ ### 4. Run
83
+
84
+ ```bash
85
+ bun run dev
86
+ ```
87
+
88
+ Buncargo will:
89
+ 1. Generate a Docker Compose file from your config
90
+ 2. Start all containers and wait for health checks
91
+ 3. Run any configured migrations
92
+ 4. Start your dev servers
93
+ 5. Print all ports and URLs
94
+
95
+ ## CLI Commands
96
+
97
+ ```bash
98
+ bunx buncargo dev # Start containers + dev servers
99
+ bunx buncargo dev --up-only # Start containers only (no dev servers)
100
+ bunx buncargo dev --down # Stop containers
101
+ bunx buncargo dev --reset # Stop containers and remove volumes
102
+ bunx buncargo dev --expose # Start with public tunnels for expose:true targets
103
+ bunx buncargo dev --expose=api # Expose specific targets
104
+ bunx buncargo dev --migrate # Run migrations only
105
+ bunx buncargo dev --seed # Run migrations and seeders
106
+ bunx buncargo prisma <args> # Run Prisma CLI with correct DATABASE_URL
107
+ bunx buncargo typecheck # Run TypeScript typecheck across workspaces
108
+ bunx buncargo env # Print ports/URLs as JSON
109
+ bunx buncargo help # Show help
110
+ bunx buncargo version # Show version
111
+ ```
112
+
113
+ ## Services
84
114
 
85
- Need ports/urls in your code (e.g., for tests)?
115
+ ### Built-in Presets
116
+
117
+ Use `service.*` helpers for common databases with sensible defaults:
86
118
 
87
119
  ```typescript
88
- import { loadDevEnv } from 'buncargo'
120
+ services: {
121
+ postgres: service.postgres({ database: 'mydb' }),
122
+ redis: service.redis(),
123
+ clickhouse: service.clickhouse({ database: 'analytics' }),
124
+ }
125
+ ```
89
126
 
90
- const env = await loadDevEnv()
91
- console.log(env.ports.postgres) // 5432 (or offset port)
92
- console.log(env.urls.api) // http://localhost:3000
93
- console.log(env.urls.postgres) // postgresql://...
127
+ Each preset includes:
128
+ - Default Docker image
129
+ - Health check configuration
130
+ - URL template (e.g., `postgresql://postgres:postgres@localhost:5432/mydb`)
131
+ - Volume for data persistence
132
+
133
+ ### Custom Services
134
+
135
+ Use `service.custom()` for any Docker service:
136
+
137
+ ```typescript
138
+ services: {
139
+ rabbitmq: service.custom({
140
+ port: 5672,
141
+ healthCheck: false,
142
+ docker: {
143
+ image: 'rabbitmq:3-management-alpine',
144
+ ports: ['${RABBITMQ_PORT:-5672}:5672', '15672:15672'],
145
+ environment: {
146
+ RABBITMQ_DEFAULT_USER: 'guest',
147
+ RABBITMQ_DEFAULT_PASS: 'guest',
148
+ },
149
+ },
150
+ }),
151
+ nats: service.custom({
152
+ port: 4222,
153
+ docker: {
154
+ image: 'nats:2-alpine',
155
+ ports: ['${NATS_PORT:-4222}:4222'],
156
+ },
157
+ }),
158
+ }
159
+ ```
160
+
161
+ ## Apps
162
+
163
+ Define dev servers to run alongside containers:
164
+
165
+ ```typescript
166
+ apps: {
167
+ api: {
168
+ port: 3000,
169
+ devCommand: 'bun run dev',
170
+ cwd: 'apps/backend',
171
+ healthEndpoint: '/health',
172
+ },
173
+ web: {
174
+ port: 5173,
175
+ devCommand: 'bun run dev',
176
+ cwd: 'apps/frontend',
177
+ healthEndpoint: '/',
178
+ },
179
+ }
180
+ ```
181
+
182
+ Use `onlyApps` on `start()` or `startServers()` to launch and wait for only those named apps (same env injection and health checks as when all apps run).
183
+
184
+ ## Environment Variables
185
+
186
+ The `envVars` function builds all env vars from computed ports and URLs:
187
+
188
+ ```typescript
189
+ envVars: (ports, urls, { localIp, publicUrls }) => ({
190
+ DATABASE_URL: urls.postgres,
191
+ REDIS_URL: urls.redis,
192
+ API_PORT: ports.api,
193
+ EXPO_API_URL: `http://${localIp}:${ports.api}`,
194
+ WEBHOOK_URL: publicUrls.api ?? urls.api,
195
+ })
94
196
  ```
95
197
 
96
- ## Features
198
+ `buildEnvVars()` always includes, for each service/app name `foo`:
199
+
200
+ - `FOO_PORT` — assigned port
201
+ - `FOO_URL` — local URL (LAN)
202
+ - `FOO_PUBLIC_URL` — only while a public tunnel is active for that name
97
203
 
98
- ### Worktree Isolation
204
+ Your `envVars` callback receives `publicUrls` and typically maps client bundles, e.g. `EXPO_PUBLIC_API_URL: publicUrls.api ?? urls.api`.
205
+
206
+ These are injected into:
207
+ - Docker Compose services
208
+ - Dev server processes
209
+ - Hook `exec()` calls
210
+ - Prisma commands
99
211
 
100
- Each git worktree automatically gets:
212
+ ## Worktree Isolation
101
213
 
102
- - Unique ports (offset 10-99)
103
- - Unique Docker Compose project names
214
+ When working in git worktrees, buncargo automatically assigns unique port offsets (10-99) so each worktree has isolated:
215
+ - Ports (e.g., postgres on 5442 instead of 5432)
216
+ - Docker Compose project names
217
+ - Containers, networks, and volumes
104
218
 
105
- This means each worktree has isolated containers, networks, and volumes by default.
219
+ This means you can run multiple branches simultaneously without conflicts.
106
220
 
107
- If you intentionally want shared Docker state across worktrees, set:
221
+ To disable isolation and share state across worktrees:
108
222
 
109
223
  ```typescript
110
224
  options: {
@@ -112,46 +226,71 @@ options: {
112
226
  }
113
227
  ```
114
228
 
115
- ### Health Checks
229
+ ## Public Tunnels
116
230
 
117
- Built-in health checks for common services:
231
+ Expose local services to the internet using Cloudflare Quick Tunnels:
118
232
 
119
- - `pg_isready` - PostgreSQL
120
- - `redis-cli` - Redis
121
- - `http` - HTTP endpoint check
122
- - `tcp` - TCP port check
233
+ ```typescript
234
+ services: {
235
+ postgres: service.postgres({ database: 'mydb' }),
236
+ },
237
+ apps: {
238
+ api: {
239
+ port: 3000,
240
+ devCommand: 'bun run dev',
241
+ expose: true, // Mark as exposable
242
+ },
243
+ }
244
+ ```
123
245
 
124
- ### URL Templates
246
+ ```bash
247
+ bun run dev --expose # Expose all targets with expose: true
248
+ bun run dev --expose=api # Expose specific targets
249
+ ```
125
250
 
126
- Define connection URLs as functions:
251
+ Public URLs are printed in the console and available via `publicUrls` in `envVars`:
127
252
 
128
253
  ```typescript
129
- urlTemplate: ({ port, host, localIp }) =>
130
- `postgresql://user:pass@${host}:${port}/db`
254
+ envVars: (_ports, urls, { publicUrls }) => ({
255
+ WEBHOOK_URL: publicUrls.api ?? urls.api,
256
+ })
131
257
  ```
132
258
 
133
- Default templates exist for: `postgres`, `redis`, `clickhouse`, `mysql`, `mongodb`
259
+ **CLI vs programmatic ordering:** `bunx buncargo dev --expose` starts Cloudflare quick tunnels after containers and migrations but **before** the interactive dev-server command (e.g. `concurrently`) runs. Until those servers are listening on their ports, tunnel traffic can briefly error. For “servers first, then public URLs,” use the API: e.g. `await dev.startServers({ onlyApps: ['api', 'platform'] })`, then `await dev.openPublicTunnels({ waitForHealthy: ['api', 'platform'] })` (optional; waits HTTP health first), then read `dev.buildEnvVars()` for spawned children.
260
+
261
+ **Expo hybrid:** buncargo is suited to exposing **API** and **platform** (Vite, etc.) for devices on cellular. **Metro** often uses Expo’s own tunnel (`expo start --tunnel`); you usually do **not** add Metro as a buncargo `app` unless you want buncargo to start it. Wire `EXPO_PUBLIC_*` from `publicUrls.* ?? urls.*` in `envVars`.
134
262
 
135
- ### Lifecycle Hooks
263
+ **Programmatic helper:** `openPublicTunnels({ names?, waitForHealthy? })` applies tunnel URLs via `setPublicUrls` and returns `close()` to stop tunnels and clear public URLs. Call `buildEnvVars()` after `openPublicTunnels` resolves so `envVars` and `*_PUBLIC_URL` see the tunnel origins.
264
+
265
+ ## Lifecycle Hooks
266
+
267
+ Run code at specific points in the startup/shutdown cycle:
136
268
 
137
269
  ```typescript
138
270
  hooks: {
139
- afterContainersReady: async (ctx) => { /* Run migrations */ },
140
- beforeServers: async (ctx) => { /* Seed database */ },
141
- afterServers: async (ctx) => { /* Post-startup tasks */ },
142
- beforeStop: async (ctx) => { /* Cleanup */ }
271
+ afterContainersReady: async (ctx) => {
272
+ await ctx.exec('bunx prisma migrate deploy', { cwd: 'packages/prisma' })
273
+ },
274
+ beforeServers: async (ctx) => {
275
+ await ctx.exec('bun run seed')
276
+ },
277
+ afterServers: async (ctx) => {
278
+ console.log(`API running at ${ctx.urls.api}`)
279
+ },
280
+ beforeStop: async (ctx) => {
281
+ await ctx.exec('bun run cleanup', { throwOnError: false })
282
+ },
143
283
  }
144
284
  ```
145
285
 
146
- ### Hook Context
147
-
148
- Hooks receive a context object with:
286
+ Hook context provides:
149
287
 
150
288
  ```typescript
151
289
  interface HookContext {
152
290
  projectName: string
153
291
  ports: { postgres: number, api: number, ... }
154
292
  urls: { postgres: string, api: string, ... }
293
+ publicUrls: { api?: string, ... }
155
294
  root: string
156
295
  isCI: boolean
157
296
  portOffset: number
@@ -160,54 +299,171 @@ interface HookContext {
160
299
  }
161
300
  ```
162
301
 
163
- ### Watchdog Auto-Shutdown
302
+ ## Migrations and Seeding
164
303
 
165
- Containers automatically stop after 10 minutes of inactivity when running via CLI.
304
+ ### Migrations
166
305
 
167
- ## CLI Reference
306
+ Run migration commands after containers are healthy:
168
307
 
308
+ ```typescript
309
+ migrations: [
310
+ { name: 'prisma', command: 'bunx prisma migrate deploy', cwd: 'packages/prisma' },
311
+ { name: 'clickhouse', command: 'bun run migrate:clickhouse' },
312
+ ]
169
313
  ```
170
- COMMANDS:
171
- dev Start the development environment
172
- typecheck Run TypeScript typecheck across workspaces
173
- prisma <args> Run Prisma CLI with correct DATABASE_URL
174
- env Print environment info as JSON
175
- help Show help
176
- version Show version
177
314
 
178
- DEV OPTIONS:
179
- --up-only Start containers only (no dev servers)
180
- --down Stop containers
181
- --reset Stop containers and remove volumes
182
- --migrate Run migrations only
183
- --seed Run seeders
315
+ ### Seeding
316
+
317
+ Seed the database with a check to avoid re-seeding:
318
+
319
+ ```typescript
320
+ seed: {
321
+ command: 'bun run seed',
322
+ check: ({ checkTable }) => checkTable('User', 'postgres'),
323
+ }
184
324
  ```
185
325
 
186
- ## Environment Variables
326
+ ## Prisma Integration
187
327
 
188
- The `envVars` function receives:
328
+ Configure Prisma to use the correct database URL:
189
329
 
190
330
  ```typescript
191
- envVars: (ports, urls, context) => ({
192
- // ports: { postgres: 5432, api: 3000, ... }
193
- // urls: { postgres: "postgresql://...", ... }
194
- // context: { localIp, portOffset, isCI, root }
195
- })
331
+ prisma: {
332
+ cwd: 'packages/prisma',
333
+ service: 'postgres', // Default: 'postgres'
334
+ urlEnvVar: 'DATABASE_URL', // Default: 'DATABASE_URL'
335
+ }
196
336
  ```
197
337
 
198
- These are injected into:
199
- - Docker Compose (via `COMPOSE_PROJECT_NAME`)
200
- - Dev server processes
201
- - Hook `exec()` calls
338
+ Then run Prisma commands through buncargo:
339
+
340
+ ```bash
341
+ bun run prisma migrate dev
342
+ bun run prisma studio
343
+ bun run prisma db push
344
+ ```
202
345
 
203
- ## Docker Compose
346
+ Buncargo ensures the database container is running and injects the correct `DATABASE_URL` with worktree-aware ports.
204
347
 
205
- Your `docker-compose.yml` should use environment variables for ports:
348
+ ## Programmatic API
206
349
 
207
- ```yaml
208
- services:
209
- postgres:
210
- image: postgres:16
211
- ports:
212
- - "${POSTGRES_PORT:-5432}:5432"
350
+ Access the dev environment from code (useful for tests):
351
+
352
+ ```typescript
353
+ import { loadDevEnv } from 'buncargo'
354
+
355
+ const env = await loadDevEnv()
356
+
357
+ console.log(env.ports.postgres) // 5432 (or offset port)
358
+ console.log(env.urls.api) // http://localhost:3000
359
+ console.log(env.urls.postgres) // postgresql://postgres:postgres@localhost:5432/mydb
360
+
361
+ // Start/stop programmatically
362
+ await env.start()
363
+ await env.stop({ removeVolumes: true })
364
+
365
+ // Build env vars for subprocess
366
+ const envVars = env.buildEnvVars()
367
+ ```
368
+
369
+ ## Docker Compose Generation
370
+
371
+ Buncargo generates Docker Compose from your config. No external `docker-compose.yml` is read.
372
+
373
+ ```typescript
374
+ docker: {
375
+ generatedFile: '.buncargo/docker-compose.generated.yml',
376
+ writeStrategy: 'always', // or 'if-missing'
377
+ volumes: {
378
+ shared_cache: {},
379
+ },
380
+ }
381
+ ```
382
+
383
+ ## Health Checks
384
+
385
+ Built-in health check types:
386
+
387
+ | Type | Description |
388
+ |------|-------------|
389
+ | `pg_isready` | PostgreSQL readiness check |
390
+ | `redis-cli` | Redis PING check |
391
+ | `http` | HTTP endpoint check |
392
+ | `tcp` | TCP port check |
393
+
394
+ Or provide a custom health check function:
395
+
396
+ ```typescript
397
+ healthCheck: async (port) => {
398
+ const res = await fetch(`http://localhost:${port}/health`)
399
+ return res.ok
400
+ }
213
401
  ```
402
+
403
+ ## Watchdog Auto-Shutdown
404
+
405
+ When running via CLI, containers automatically stop after 10 minutes of inactivity. The watchdog monitors heartbeats and shuts down orphaned environments.
406
+
407
+ ## Full Example
408
+
409
+ ```typescript
410
+ import { defineDevConfig, service } from 'buncargo'
411
+
412
+ export default defineDevConfig({
413
+ projectPrefix: 'platform',
414
+
415
+ services: {
416
+ postgres: service.postgres({ database: 'platform' }),
417
+ redis: service.redis(),
418
+ clickhouse: service.clickhouse({ database: 'platform' }),
419
+ },
420
+
421
+ apps: {
422
+ api: {
423
+ port: 3000,
424
+ expose: true,
425
+ devCommand: 'bun run dev',
426
+ cwd: 'apps/backend',
427
+ healthEndpoint: '/health',
428
+ },
429
+ web: {
430
+ port: 5173,
431
+ devCommand: 'bun run dev',
432
+ cwd: 'apps/frontend',
433
+ },
434
+ },
435
+
436
+ envVars: (ports, urls, { localIp, publicUrls }) => ({
437
+ DATABASE_URL: urls.postgres,
438
+ REDIS_URL: urls.redis,
439
+ CLICKHOUSE_URL: urls.clickhouse,
440
+ API_URL: urls.api,
441
+ VITE_API_URL: urls.api,
442
+ EXPO_API_URL: `http://${localIp}:${ports.api}`,
443
+ WEBHOOK_URL: publicUrls.api ?? urls.api,
444
+ }),
445
+
446
+ migrations: [
447
+ { name: 'prisma', command: 'bunx prisma migrate deploy', cwd: 'packages/prisma' },
448
+ ],
449
+
450
+ seed: {
451
+ command: 'bun run seed',
452
+ check: ({ checkTable }) => checkTable('User', 'postgres'),
453
+ },
454
+
455
+ prisma: {
456
+ cwd: 'packages/prisma',
457
+ },
458
+
459
+ hooks: {
460
+ afterContainersReady: async (ctx) => {
461
+ console.log(`Containers ready on port offset ${ctx.portOffset}`)
462
+ },
463
+ },
464
+ })
465
+ ```
466
+
467
+ ## License
468
+
469
+ MIT
package/src/cli/bin.ts ADDED
@@ -0,0 +1,77 @@
1
+ #!/usr/bin/env bun
2
+
3
+ /**
4
+ * CLI Entry Point for buncargo
5
+ *
6
+ * Usage:
7
+ * bunx buncargo dev # Start containers + dev servers
8
+ * bunx buncargo dev --down # Stop containers
9
+ * bunx buncargo dev --reset # Stop + remove volumes
10
+ * bunx buncargo typecheck # Run TypeScript typecheck
11
+ * bunx buncargo prisma ... # Run prisma commands
12
+ * bunx buncargo help # Show help
13
+ */
14
+
15
+ import { showHelp } from "./commands/help";
16
+ import {
17
+ handleDev,
18
+ handleEnv,
19
+ handlePrisma,
20
+ handleTypecheck,
21
+ } from "./commands/runtime";
22
+ import { showVersion } from "./commands/version";
23
+
24
+ // ═══════════════════════════════════════════════════════════════════════════
25
+ // Main
26
+ // ═══════════════════════════════════════════════════════════════════════════
27
+
28
+ async function main(): Promise<void> {
29
+ const args = process.argv.slice(2);
30
+ const command = args[0];
31
+ const commandArgs = args.slice(1);
32
+
33
+ if (
34
+ !command ||
35
+ command === "help" ||
36
+ command === "--help" ||
37
+ command === "-h"
38
+ ) {
39
+ showHelp();
40
+ process.exit(0);
41
+ }
42
+
43
+ if (command === "version" || command === "--version" || command === "-v") {
44
+ showVersion();
45
+ process.exit(0);
46
+ }
47
+
48
+ switch (command) {
49
+ case "dev":
50
+ await handleDev(commandArgs);
51
+ break;
52
+
53
+ case "typecheck":
54
+ await handleTypecheck();
55
+ break;
56
+
57
+ case "prisma":
58
+ await handlePrisma(commandArgs);
59
+ break;
60
+
61
+ case "env":
62
+ await handleEnv();
63
+ break;
64
+
65
+ default:
66
+ console.error(`❌ Unknown command: ${command}`);
67
+ console.error("");
68
+ console.error(' Run "bunx buncargo help" for available commands.');
69
+ process.exit(1);
70
+ }
71
+ }
72
+
73
+ main().catch((error) => {
74
+ const message = error instanceof Error ? error.message : String(error);
75
+ console.error(`❌ ${message}`);
76
+ process.exit(1);
77
+ });
@@ -0,0 +1,39 @@
1
+ export function showHelp(): void {
2
+ console.log(`
3
+ buncargo - Development environment CLI
4
+
5
+ USAGE:
6
+ bunx buncargo <command> [options]
7
+
8
+ COMMANDS:
9
+ dev Start the development environment
10
+ typecheck Run TypeScript typecheck across workspaces
11
+ prisma <args> Run Prisma CLI with correct DATABASE_URL
12
+ env Print environment info as JSON
13
+ help Show this help message
14
+ version Show version
15
+
16
+ EXAMPLES:
17
+ bunx buncargo dev # Start everything
18
+ bunx buncargo dev --expose # Public quick tunnel for expose:true targets
19
+ bunx buncargo dev --expose=api # Public quick tunnel for selected target
20
+ bunx buncargo dev --help # Show dev command options
21
+ bunx buncargo dev --down # Stop containers
22
+ bunx buncargo typecheck # Run typecheck
23
+ bunx buncargo prisma studio # Open Prisma Studio
24
+ bunx buncargo env # Get ports/urls as JSON
25
+
26
+ CONFIG:
27
+ Create a dev.config.ts with a default export:
28
+
29
+ import { defineDevConfig } from 'buncargo'
30
+
31
+ export default defineDevConfig({
32
+ projectPrefix: 'myapp',
33
+ services: { ... },
34
+ apps: { ... }
35
+ })
36
+
37
+ Run "bunx buncargo dev --help" for dev command options.
38
+ `);
39
+ }