create-turbo-mono 1.0.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 (54) hide show
  1. package/.claude/settings.local.json +14 -0
  2. package/README.md +182 -0
  3. package/dist/index.d.ts +3 -0
  4. package/dist/index.d.ts.map +1 -0
  5. package/dist/index.js +118 -0
  6. package/dist/index.js.map +1 -0
  7. package/dist/scaffold.d.ts +8 -0
  8. package/dist/scaffold.d.ts.map +1 -0
  9. package/dist/scaffold.js +42 -0
  10. package/dist/scaffold.js.map +1 -0
  11. package/dist/templates/backend.d.ts +2 -0
  12. package/dist/templates/backend.d.ts.map +1 -0
  13. package/dist/templates/backend.js +424 -0
  14. package/dist/templates/backend.js.map +1 -0
  15. package/dist/templates/cicd.d.ts +3 -0
  16. package/dist/templates/cicd.d.ts.map +1 -0
  17. package/dist/templates/cicd.js +307 -0
  18. package/dist/templates/cicd.js.map +1 -0
  19. package/dist/templates/docker.d.ts +3 -0
  20. package/dist/templates/docker.d.ts.map +1 -0
  21. package/dist/templates/docker.js +458 -0
  22. package/dist/templates/docker.js.map +1 -0
  23. package/dist/templates/docs.d.ts +3 -0
  24. package/dist/templates/docs.d.ts.map +1 -0
  25. package/dist/templates/docs.js +71 -0
  26. package/dist/templates/docs.js.map +1 -0
  27. package/dist/templates/frontend.d.ts +2 -0
  28. package/dist/templates/frontend.d.ts.map +1 -0
  29. package/dist/templates/frontend.js +441 -0
  30. package/dist/templates/frontend.js.map +1 -0
  31. package/dist/templates/root.d.ts +3 -0
  32. package/dist/templates/root.d.ts.map +1 -0
  33. package/dist/templates/root.js +210 -0
  34. package/dist/templates/root.js.map +1 -0
  35. package/dist/templates/shared.d.ts +2 -0
  36. package/dist/templates/shared.d.ts.map +1 -0
  37. package/dist/templates/shared.js +696 -0
  38. package/dist/templates/shared.js.map +1 -0
  39. package/dist/utils.d.ts +5 -0
  40. package/dist/utils.d.ts.map +1 -0
  41. package/dist/utils.js +34 -0
  42. package/dist/utils.js.map +1 -0
  43. package/package.json +40 -0
  44. package/src/index.ts +138 -0
  45. package/src/scaffold.ts +51 -0
  46. package/src/templates/backend.ts +460 -0
  47. package/src/templates/cicd.ts +334 -0
  48. package/src/templates/docker.ts +503 -0
  49. package/src/templates/docs.ts +74 -0
  50. package/src/templates/frontend.ts +469 -0
  51. package/src/templates/root.ts +216 -0
  52. package/src/templates/shared.ts +820 -0
  53. package/src/utils.ts +31 -0
  54. package/tsconfig.json +20 -0
@@ -0,0 +1,334 @@
1
+ import fs from 'fs-extra';
2
+ import path from 'path';
3
+ import { ProjectConfig } from '../scaffold';
4
+
5
+ export async function createCICDFiles(config: ProjectConfig): Promise<void> {
6
+ const { targetDir, backends, frontends } = config;
7
+
8
+ await fs.ensureDir(path.join(targetDir, '.github', 'workflows'));
9
+ await fs.ensureDir(path.join(targetDir, 'scripts'));
10
+
11
+ await createGitHubWorkflow(targetDir, backends, frontends);
12
+ await createDeployScript(targetDir, backends, frontends);
13
+ await createRollbackScript(targetDir, backends, frontends);
14
+ await createEnvExample(targetDir);
15
+ }
16
+
17
+ async function createGitHubWorkflow(
18
+ targetDir: string,
19
+ backends: string[],
20
+ frontends: string[]
21
+ ): Promise<void> {
22
+ const services = [...backends, ...frontends];
23
+ const matrixServices = services.map((s) => `'${s}'`).join(', ');
24
+
25
+ // Using template literals with escaped ${ for GitHub Actions syntax
26
+ const workflow = `name: CI/CD (main)
27
+
28
+ on:
29
+ push:
30
+ branches: ['main']
31
+ pull_request:
32
+ branches: ['main']
33
+
34
+ env:
35
+ REGISTRY: ghcr.io
36
+ IMAGE_NAME: \${{ github.repository }}
37
+
38
+ jobs:
39
+ build-and-push:
40
+ runs-on: ubuntu-latest
41
+ permissions:
42
+ contents: read
43
+ packages: write
44
+ strategy:
45
+ fail-fast: false
46
+ matrix:
47
+ service: [${matrixServices}]
48
+
49
+ steps:
50
+ - name: ๐Ÿ“ฅ Checkout repository
51
+ uses: actions/checkout@v4
52
+
53
+ - name: ๐Ÿณ Set up QEMU
54
+ uses: docker/setup-qemu-action@v3
55
+
56
+ - name: ๐Ÿ—๏ธ Set up Docker Buildx
57
+ uses: docker/setup-buildx-action@v3
58
+
59
+ - name: ๐Ÿ” Login to GitHub Container Registry
60
+ uses: docker/login-action@v3
61
+ with:
62
+ registry: \${{ env.REGISTRY }}
63
+ username: \${{ github.actor }}
64
+ password: \${{ secrets.GITHUB_TOKEN }}
65
+
66
+ - name: ๐Ÿท๏ธ Extract metadata
67
+ id: meta
68
+ uses: docker/metadata-action@v5
69
+ with:
70
+ images: \${{ env.REGISTRY }}/\${{ env.IMAGE_NAME }}/\${{ matrix.service }}
71
+ tags: |
72
+ type=ref,event=branch
73
+ type=ref,event=pr
74
+ type=sha,prefix={{branch}}-
75
+ type=raw,value=latest,enable={{is_default_branch}}
76
+
77
+ - name: ๐Ÿ”จ Build and push Docker image
78
+ uses: docker/build-push-action@v5
79
+ with:
80
+ context: .
81
+ file: Dockerfile
82
+ target: \${{ matrix.service }}-production
83
+ push: \${{ github.event_name != 'pull_request' }}
84
+ tags: \${{ steps.meta.outputs.tags }}
85
+ labels: \${{ steps.meta.outputs.labels }}
86
+ cache-from: type=gha
87
+ cache-to: type=gha,mode=max
88
+
89
+ deploy:
90
+ needs: [build-and-push]
91
+ runs-on: ubuntu-latest
92
+ if: github.ref == 'refs/heads/main' && github.event_name == 'push'
93
+
94
+ steps:
95
+ - name: ๐Ÿ“ฅ Checkout repository
96
+ uses: actions/checkout@v4
97
+
98
+ - name: ๐Ÿท๏ธ Compute deployment tag
99
+ id: tag
100
+ run: echo "TAG=\${GITHUB_SHA::7}" >> $GITHUB_OUTPUT
101
+
102
+ - name: ๐Ÿš€ Deploy to server
103
+ uses: appleboy/ssh-action@v1.0.0
104
+ env:
105
+ TAG: \${{ steps.tag.outputs.TAG }}
106
+ APP_DIR: \${{ secrets.DEPLOY_PATH }}
107
+ with:
108
+ host: \${{ secrets.SSH_HOST }}
109
+ username: \${{ secrets.SSH_USER }}
110
+ key: \${{ secrets.SSH_KEY }}
111
+ port: \${{ secrets.SSH_PORT || 22 }}
112
+ script_stop: true
113
+ envs: TAG,APP_DIR
114
+ script: |
115
+ set -euo pipefail
116
+ echo "๐Ÿ“ฆ Deploying tag: \${TAG}"
117
+ echo "๐Ÿ“ APP_DIR: \${APP_DIR}"
118
+ cd "\${APP_DIR}"
119
+ chmod +x scripts/deploy.sh
120
+ ./scripts/deploy.sh "\${TAG}"
121
+
122
+ - name: โœ‰๏ธ Send deployment notification
123
+ if: always()
124
+ uses: dawidd6/action-send-mail@v3
125
+ with:
126
+ server_address: \${{ secrets.SMTP_SERVER }}
127
+ server_port: \${{ secrets.SMTP_PORT }}
128
+ secure: \${{ secrets.SMTP_SECURE }}
129
+ username: \${{ secrets.SMTP_USERNAME }}
130
+ password: \${{ secrets.SMTP_PASSWORD }}
131
+ subject: 'Deployment #\${{ github.run_number }} - \${{ job.status }}'
132
+ from: 'CI/CD <\${{ secrets.SMTP_USERNAME }}>'
133
+ to: \${{ secrets.DEPLOY_NOTIFY_TO }}
134
+ html_body: |
135
+ <div style="font-family:'Segoe UI',Helvetica,Arial,sans-serif;background:#f6f8fa;padding:20px;">
136
+ <div style="max-width:600px;margin:0 auto;background:#fff;border-radius:10px;box-shadow:0 2px 6px rgba(0,0,0,0.1);overflow:hidden;">
137
+ <div style="background:linear-gradient(90deg,#2563eb,#1e3a8a);padding:16px 24px;color:#fff;">
138
+ <h2 style="margin:0;font-size:20px;">๐Ÿš€ Deployment Notification</h2>
139
+ </div>
140
+ <div style="padding:24px;">
141
+ <table style="width:100%;border-collapse:collapse;margin-top:8px;">
142
+ <tr><td style="padding:6px 0;color:#555;font-weight:600;">Repository</td><td>\${{ github.repository }}</td></tr>
143
+ <tr style="background:#f9fafb;"><td style="padding:6px 0;color:#555;font-weight:600;">Branch</td><td>\${{ github.ref_name }}</td></tr>
144
+ <tr><td style="padding:6px 0;color:#555;font-weight:600;">Commit</td><td><code>\${{ github.sha }}</code></td></tr>
145
+ <tr style="background:#f9fafb;"><td style="padding:6px 0;color:#555;font-weight:600;">Tag</td><td>\${{ steps.tag.outputs.TAG }}</td></tr>
146
+ <tr><td style="padding:6px 0;color:#555;font-weight:600;">Status</td>
147
+ <td><span style="padding:3px 8px;border-radius:6px;background:#2563eb;color:#fff;font-size:12px;">\${{ job.status }}</span></td></tr>
148
+ </table>
149
+ <p style="margin:16px 0 8px;color:#444;">View full deployment logs:</p>
150
+ <a href="\${{ github.server_url }}/\${{ github.repository }}/actions/runs/\${{ github.run_id }}"
151
+ style="display:inline-block;background:#2563eb;color:#fff;padding:10px 16px;border-radius:6px;text-decoration:none;font-weight:500;">๐Ÿ” View Details</a>
152
+ <p style="margin-top:20px;font-size:12px;color:#888;">โ€” DevOps Pipeline</p>
153
+ </div>
154
+ </div>
155
+ </div>
156
+ `;
157
+
158
+ await fs.writeFile(
159
+ path.join(targetDir, '.github', 'workflows', 'ci-cd.yml'),
160
+ workflow
161
+ );
162
+ }
163
+
164
+ async function createDeployScript(
165
+ targetDir: string,
166
+ backends: string[],
167
+ frontends: string[]
168
+ ): Promise<void> {
169
+ const services = [...backends, ...frontends].join(' ');
170
+
171
+ const deployScript = `#!/usr/bin/env bash
172
+ set -euo pipefail
173
+
174
+ APP_DIR="\${APP_DIR:-/home/$(whoami)/app}"
175
+ COMPOSE_FILE="docker-compose.prod.yml"
176
+ PROJECT_NAME="\${PROJECT_NAME:-app}"
177
+
178
+ TAG="\${1:-}"
179
+ if [[ -z "\${TAG}" ]]; then
180
+ echo "โŒ Usage: ./scripts/deploy.sh <tag>"
181
+ exit 1
182
+ fi
183
+
184
+ echo "๐Ÿš€ Deploying tag \${TAG} in \${APP_DIR}"
185
+ cd "\${APP_DIR}"
186
+
187
+ export RELEASE_TAG="\${TAG}"
188
+
189
+ # Keep a pointer to the previous tag for rollback
190
+ if [[ -f .current_release_tag ]]; then
191
+ cp .current_release_tag .prev_release_tag
192
+ fi
193
+ echo "\${TAG}" > .current_release_tag
194
+
195
+ # 1) Pull latest images
196
+ echo "๐Ÿ“ฅ Pulling images for tag \${TAG}..."
197
+ docker compose -f "\${COMPOSE_FILE}" pull ${services}
198
+
199
+ # 2) Run database migrations
200
+ echo "๐Ÿงฉ Running database migrations..."
201
+ FIRST_BACKEND="${backends[0]}"
202
+ if ! docker compose -f "\${COMPOSE_FILE}" run --rm \${FIRST_BACKEND} sh -c 'pnpm prisma migrate deploy'; then
203
+ echo "โŒ Migration failed. Rolling back to previous tag..."
204
+ if [[ -f .prev_release_tag ]]; then
205
+ ./scripts/rollback.sh
206
+ fi
207
+ exit 1
208
+ fi
209
+
210
+ # 3) Start/Update services
211
+ echo "๐Ÿ”„ Starting containers..."
212
+ docker compose -f "\${COMPOSE_FILE}" up -d ${services}
213
+
214
+ # 4) Wait for services to be healthy
215
+ echo "๐Ÿ” Waiting for services to be healthy..."
216
+ for SERVICE in ${services}; do
217
+ CONTAINER_NAME="\${PROJECT_NAME}-\${SERVICE}"
218
+ for i in {1..30}; do
219
+ STATUS="$(docker inspect --format '{{json .State.Health.Status}}' "\${CONTAINER_NAME}" 2>/dev/null | tr -d '"')" || true
220
+ if [[ "\${STATUS}" == "healthy" ]] || [[ -z "\${STATUS}" ]]; then
221
+ echo "โœ… \${SERVICE} is healthy"
222
+ break
223
+ fi
224
+ if [[ \${i} -eq 30 ]]; then
225
+ echo "โš ๏ธ \${SERVICE} did not become healthy (timeout)"
226
+ fi
227
+ sleep 3
228
+ done
229
+ done
230
+
231
+ # 5) Cleanup old images
232
+ echo "๐Ÿงน Cleaning up old images..."
233
+ docker image prune -f
234
+
235
+ echo "โœ… Deployment successful for tag \${TAG}"
236
+ echo "๐Ÿ“Š Running services:"
237
+ docker compose -f "\${COMPOSE_FILE}" ps
238
+ `;
239
+
240
+ await fs.writeFile(path.join(targetDir, 'scripts', 'deploy.sh'), deployScript, {
241
+ mode: 0o755,
242
+ });
243
+ }
244
+
245
+ async function createRollbackScript(
246
+ targetDir: string,
247
+ backends: string[],
248
+ frontends: string[]
249
+ ): Promise<void> {
250
+ const services = [...backends, ...frontends].join(' ');
251
+
252
+ const rollbackScript = `#!/usr/bin/env bash
253
+ set -euo pipefail
254
+
255
+ APP_DIR="\${APP_DIR:-/home/$(whoami)/app}"
256
+ COMPOSE_FILE="docker-compose.prod.yml"
257
+
258
+ cd "\${APP_DIR}"
259
+
260
+ if [[ ! -f .prev_release_tag ]]; then
261
+ echo "โŒ No .prev_release_tag found; cannot rollback."
262
+ exit 1
263
+ fi
264
+
265
+ export RELEASE_TAG="$(cat .prev_release_tag)"
266
+ echo "๐Ÿ”„ Rolling back to \${RELEASE_TAG}"
267
+
268
+ # Pull previous images
269
+ echo "๐Ÿ“ฅ Pulling images for tag \${RELEASE_TAG}..."
270
+ docker compose -f "\${COMPOSE_FILE}" pull ${services}
271
+
272
+ # Restart services
273
+ echo "๐Ÿ”„ Restarting services..."
274
+ docker compose -f "\${COMPOSE_FILE}" up -d ${services}
275
+
276
+ # Swap tag pointers
277
+ cp .prev_release_tag .current_release_tag
278
+
279
+ echo "โœ… Rolled back to \${RELEASE_TAG}"
280
+ echo "๐Ÿ“Š Running services:"
281
+ docker compose -f "\${COMPOSE_FILE}" ps
282
+ `;
283
+
284
+ await fs.writeFile(
285
+ path.join(targetDir, 'scripts', 'rollback.sh'),
286
+ rollbackScript,
287
+ { mode: 0o755 }
288
+ );
289
+ }
290
+
291
+ async function createEnvExample(targetDir: string): Promise<void> {
292
+ const envExample = `# Project Configuration
293
+ PROJECT_NAME=my-app
294
+ GITHUB_REPOSITORY=your-org/your-repo
295
+
296
+ # Database Configuration
297
+ DATABASE_USER=postgres
298
+ DATABASE_PASSWORD=postgres
299
+ DATABASE_NAME=app_db
300
+ DATABASE_PORT=5432
301
+ DATABASE_URL=postgresql://\${DATABASE_USER}:\${DATABASE_PASSWORD}@postgres:5432/\${DATABASE_NAME}
302
+
303
+ # Redis Configuration
304
+ REDIS_URL=redis://redis:6379
305
+ REDIS_PORT=6379
306
+
307
+ # Application Ports
308
+ # Backend services will use 4000+
309
+ # Frontend services will use 3000+
310
+
311
+ # Production Deployment
312
+ RELEASE_TAG=latest
313
+
314
+ # API Configuration
315
+ NEXT_PUBLIC_API_URL=http://localhost:4000
316
+
317
+ # Email Notifications (for CI/CD)
318
+ # SMTP_SERVER=smtp.gmail.com
319
+ # SMTP_PORT=587
320
+ # SMTP_SECURE=true
321
+ # SMTP_USERNAME=your-email@gmail.com
322
+ # SMTP_PASSWORD=your-app-password
323
+ # DEPLOY_NOTIFY_TO=team@example.com
324
+
325
+ # Deployment Server (for CI/CD)
326
+ # SSH_HOST=your-server.com
327
+ # SSH_USER=deploy
328
+ # SSH_KEY=<your-private-key>
329
+ # SSH_PORT=22
330
+ # DEPLOY_PATH=/home/deploy/app
331
+ `;
332
+
333
+ await fs.writeFile(path.join(targetDir, '.env.example'), envExample);
334
+ }