novatec-cli 3.0.7 → 3.0.8

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.
@@ -2,101 +2,9 @@ import path from 'path';
2
2
  import fse from 'fs-extra';
3
3
  import chalk from 'chalk';
4
4
  import boxen from 'boxen';
5
- import { execSync } from 'child_process';
5
+ import { execSync, spawnSync } from 'child_process';
6
6
  import { isInstalled } from '../utils.js';
7
7
 
8
- // ── GitHub Actions CI ─────────────────────────────────────────────────────────
9
- export async function generateDeploy(config, root) {
10
- const ciDir = path.join(root, '.github', 'workflows');
11
- await fse.ensureDir(ciDir);
12
- const isPython = ['fastapi','django','flask'].includes(config.backend);
13
-
14
- await fse.writeFile(path.join(ciDir, 'ci.yml'),
15
- `name: CI/CD Pipeline
16
-
17
- on:
18
- push:
19
- branches: [main, develop]
20
- pull_request:
21
- branches: [main]
22
-
23
- jobs:
24
- frontend:
25
- name: Frontend — Build & Test
26
- runs-on: ubuntu-latest
27
- defaults:
28
- run:
29
- working-directory: frontend
30
- steps:
31
- - uses: actions/checkout@v4
32
- - uses: actions/setup-node@v4
33
- with:
34
- node-version: 20
35
- cache: npm
36
- cache-dependency-path: frontend/package-lock.json
37
- - run: npm ci
38
- - run: npm run lint --if-present
39
- - run: npm test --if-present
40
- - run: npm run build
41
-
42
- backend:
43
- name: Backend — Lint & Test
44
- runs-on: ubuntu-latest
45
- defaults:
46
- run:
47
- working-directory: backend
48
- steps:
49
- - uses: actions/checkout@v4
50
- ${isPython
51
- ? ` - uses: actions/setup-python@v5
52
- with:
53
- python-version: '3.11'
54
- - run: pip install -r requirements.txt
55
- - run: python -m pytest --if-present`
56
- : ` - uses: actions/setup-node@v4
57
- with:
58
- node-version: 20
59
- - run: npm ci
60
- - run: npm run lint --if-present
61
- - run: npm test --if-present`}
62
- `);
63
-
64
- await fse.writeFile(path.join(ciDir, 'deploy.yml'),
65
- `name: Deploy
66
-
67
- on:
68
- push:
69
- branches: [main]
70
-
71
- jobs:
72
- deploy-frontend:
73
- name: Deploy Frontend → Vercel
74
- runs-on: ubuntu-latest
75
- steps:
76
- - uses: actions/checkout@v4
77
- - uses: actions/setup-node@v4
78
- with:
79
- node-version: 20
80
- - run: npm install -g vercel
81
- - run: cd frontend && npm ci && npm run build
82
- - run: vercel --prod --token \${{ secrets.VERCEL_TOKEN }} --yes
83
- env:
84
- VERCEL_ORG_ID: \${{ secrets.VERCEL_ORG_ID }}
85
- VERCEL_PROJECT_ID: \${{ secrets.VERCEL_PROJECT_ID }}
86
-
87
- deploy-backend:
88
- name: Deploy Backend → Railway
89
- runs-on: ubuntu-latest
90
- needs: deploy-frontend
91
- steps:
92
- - uses: actions/checkout@v4
93
- - uses: railwayapp/railway-github-action@v1
94
- with:
95
- railway-token: \${{ secrets.RAILWAY_TOKEN }}
96
- service: backend
97
- `);
98
- }
99
-
100
8
  // ── novatec build ─────────────────────────────────────────────────────────────
101
9
  export async function runBuild(options) {
102
10
  const root = process.cwd();
@@ -107,12 +15,13 @@ export async function runBuild(options) {
107
15
  console.log(chalk.white(' Building proyecto...\n'));
108
16
 
109
17
  if ((options.frontend || !options.backend) && hasFe) {
110
- const spinner = (await import('ora')).default({ text: chalk.white('Build frontend...'), spinner: 'dots', color: 'white' }).start();
18
+ const ora = (await import('ora')).default;
19
+ const spinner = ora({ text: chalk.white('Build frontend...'), spinner: 'dots', color: 'white' }).start();
111
20
  try {
112
21
  execSync('npm run build', { cwd: path.join(root, 'frontend'), stdio: 'pipe', encoding: 'utf8' });
113
- spinner.succeed(chalk.white('Frontend build OK'));
22
+ spinner.succeed(chalk.white('Frontend build '));
114
23
  } catch (err) {
115
- spinner.fail(chalk.red('Frontend build falló\n' + (err.stderr || '')));
24
+ spinner.fail(chalk.red('Frontend build falló: ' + (err.stderr || err.message || '').split('\n')[0]));
116
25
  }
117
26
  }
118
27
 
@@ -121,13 +30,16 @@ export async function runBuild(options) {
121
30
  if (await fse.pathExists(pkgPath)) {
122
31
  const pkg = await fse.readJSON(pkgPath);
123
32
  if (pkg.scripts?.build) {
124
- const spinner = (await import('ora')).default({ text: chalk.white('Build backend...'), spinner: 'dots', color: 'white' }).start();
33
+ const ora = (await import('ora')).default;
34
+ const spinner = ora({ text: chalk.white('Build backend...'), spinner: 'dots', color: 'white' }).start();
125
35
  try {
126
36
  execSync('npm run build', { cwd: path.join(root, 'backend'), stdio: 'pipe', encoding: 'utf8' });
127
- spinner.succeed(chalk.white('Backend build OK'));
128
- } catch {
129
- spinner.fail(chalk.red('Backend build falló'));
37
+ spinner.succeed(chalk.white('Backend build '));
38
+ } catch (err) {
39
+ spinner.fail(chalk.red('Backend build falló: ' + (err.stderr || err.message || '').split('\n')[0]));
130
40
  }
41
+ } else {
42
+ console.log(chalk.gray(' Backend: no tiene script build, omitiendo.'));
131
43
  }
132
44
  }
133
45
  }
@@ -139,36 +51,145 @@ export async function runBuild(options) {
139
51
 
140
52
  // ── novatec deploy ────────────────────────────────────────────────────────────
141
53
  export async function runDeploy(options) {
54
+ const root = process.cwd();
55
+ const hasFe = await fse.pathExists(path.join(root, 'frontend'));
56
+ const hasBe = await fse.pathExists(path.join(root, 'backend'));
57
+
58
+ const deployFe = options.frontend || (!options.frontend && !options.backend);
59
+ const deployBe = options.backend || (!options.frontend && !options.backend);
60
+
61
+ // Verificar que estamos en un proyecto novatec
62
+ if (!hasFe && !hasBe) {
63
+ console.log(boxen(
64
+ chalk.yellow(' Ejecuta este comando dentro de un proyecto NovaTec\n\n') +
65
+ chalk.gray(' cd mi-proyecto\n') +
66
+ chalk.gray(' novatec deploy'),
67
+ { padding: 1, margin: { left: 2 }, borderStyle: 'round', borderColor: 'yellow', dimBorder: true }
68
+ ));
69
+ process.exit(1);
70
+ }
71
+
142
72
  console.log();
143
73
  console.log(boxen(
144
74
  chalk.bold.white(' DEPLOY — NovaTec CLI\n\n') +
145
- chalk.white(' Frontend Vercel\n') +
146
- chalk.white(' Backend Railway\n\n') +
147
- chalk.gray(' Asegúrate de tener instalados:\n') +
148
- chalk.gray(' npm install -g vercel\n') +
149
- chalk.gray(' npm install -g @railway/cli'),
75
+ (deployFe && hasFe ? chalk.white(' Frontend Vercel\n') : '') +
76
+ (deployBe && hasBe ? chalk.white(' Backend Railway\n') : ''),
150
77
  { padding: 1, margin: { left: 2 }, borderStyle: 'round', borderColor: 'white', dimBorder: true }
151
78
  ));
152
79
 
153
- const root = process.cwd();
80
+ // ── FRONTEND → VERCEL ───────────────────────────────────────────────────────
81
+ if (deployFe && hasFe) {
82
+ console.log(chalk.white('\n ▲ Desplegando frontend en Vercel...\n'));
154
83
 
155
- if ((options.frontend || !options.backend)) {
84
+ // Instalar Vercel CLI si no está
156
85
  if (!isInstalled('vercel')) {
157
- console.log(chalk.yellow('\n Instalando Vercel CLI...'));
158
- execSync('npm install -g vercel', { stdio: 'inherit' });
86
+ console.log(chalk.gray(' Instalando Vercel CLI...'));
87
+ try {
88
+ execSync('npm install -g vercel', { stdio: 'pipe' });
89
+ console.log(chalk.green(' ✔ Vercel CLI instalado'));
90
+ } catch (e) {
91
+ console.log(chalk.red(' ✖ Error instalando Vercel CLI: ' + e.message));
92
+ console.log(chalk.gray(' Instala manualmente: npm install -g vercel'));
93
+ process.exit(1);
94
+ }
95
+ }
96
+
97
+ // Verificar login
98
+ try {
99
+ execSync('vercel whoami', { stdio: 'pipe' });
100
+ } catch {
101
+ console.log(chalk.yellow(' No estás logueado en Vercel. Iniciando login...\n'));
102
+ spawnSync('vercel', ['login'], { cwd: path.join(root, 'frontend'), stdio: 'inherit' });
103
+ }
104
+
105
+ // Deploy
106
+ console.log(chalk.gray(' Ejecutando: vercel --prod\n'));
107
+ const r = spawnSync('vercel', ['--prod'], {
108
+ cwd: path.join(root, 'frontend'),
109
+ stdio: 'inherit',
110
+ });
111
+
112
+ if (r.status === 0) {
113
+ console.log(chalk.green('\n ✔ Frontend desplegado en Vercel'));
114
+ } else {
115
+ console.log(chalk.red('\n ✖ Error desplegando frontend'));
159
116
  }
160
- console.log(chalk.white('\n Desplegando frontend en Vercel...'));
161
- execSync('vercel --prod', { cwd: path.join(root, 'frontend'), stdio: 'inherit' });
162
117
  }
163
118
 
164
- if ((options.backend || !options.frontend)) {
119
+ // ── BACKEND RAILWAY ───────────────────────────────────────────────────────
120
+ if (deployBe && hasBe) {
121
+ console.log(chalk.white('\n ◆ Desplegando backend en Railway...\n'));
122
+
123
+ // Instalar Railway CLI si no está
165
124
  if (!isInstalled('railway')) {
166
- console.log(chalk.yellow('\n Instalando Railway CLI...'));
167
- execSync('npm install -g @railway/cli', { stdio: 'inherit' });
125
+ console.log(chalk.gray(' Instalando Railway CLI...'));
126
+ try {
127
+ execSync('npm install -g @railway/cli', { stdio: 'pipe' });
128
+ console.log(chalk.green(' ✔ Railway CLI instalado'));
129
+ } catch (e) {
130
+ console.log(chalk.red(' ✖ Error instalando Railway CLI: ' + e.message));
131
+ console.log(chalk.gray(' Instala manualmente: npm install -g @railway/cli'));
132
+ process.exit(1);
133
+ }
134
+ }
135
+
136
+ // Verificar login
137
+ try {
138
+ execSync('railway whoami', { stdio: 'pipe' });
139
+ } catch {
140
+ console.log(chalk.yellow(' No estás logueado en Railway. Iniciando login...\n'));
141
+ spawnSync('railway', ['login'], { stdio: 'inherit' });
142
+ }
143
+
144
+ // Verificar si hay proyecto Railway vinculado
145
+ const railwayJson = path.join(root, 'backend', '.railway');
146
+ const hasRailwayProject = await fse.pathExists(railwayJson);
147
+
148
+ if (!hasRailwayProject) {
149
+ console.log(chalk.yellow(' No hay proyecto Railway vinculado.'));
150
+ console.log(chalk.gray(' Vinculando proyecto (se abrirá selector)...\n'));
151
+ const link = spawnSync('railway', ['link'], {
152
+ cwd: path.join(root, 'backend'),
153
+ stdio: 'inherit',
154
+ });
155
+ if (link.status !== 0) {
156
+ console.log(chalk.gray('\n O crea uno nuevo con: railway init'));
157
+ console.log(chalk.gray(' Luego ejecuta: novatec deploy --backend'));
158
+ process.exit(1);
159
+ }
160
+ }
161
+
162
+ // Deploy
163
+ console.log(chalk.gray(' Ejecutando: railway up\n'));
164
+ const r = spawnSync('railway', ['up', '--detach'], {
165
+ cwd: path.join(root, 'backend'),
166
+ stdio: 'inherit',
167
+ });
168
+
169
+ if (r.status === 0) {
170
+ console.log(chalk.green('\n ✔ Backend desplegado en Railway'));
171
+ console.log(chalk.gray(' Ver logs: railway logs'));
172
+ console.log(chalk.gray(' Abrir: railway open'));
173
+ } else {
174
+ console.log(chalk.red('\n ✖ Error desplegando backend'));
175
+ console.log(chalk.gray(' Ver logs: railway logs'));
168
176
  }
169
- console.log(chalk.white('\n Desplegando backend en Railway...'));
170
- execSync('railway up', { cwd: path.join(root, 'backend'), stdio: 'inherit' });
171
177
  }
172
178
 
179
+ // ── RESUMEN ─────────────────────────────────────────────────────────────────
180
+ console.log();
181
+ console.log(boxen(
182
+ chalk.bold.white(' GUÍA RÁPIDA\n\n') +
183
+ chalk.white(' Vercel (Frontend)\n') +
184
+ chalk.gray(' 1. vercel login\n') +
185
+ chalk.gray(' 2. cd frontend && vercel --prod\n') +
186
+ chalk.gray(' 3. Variables de entorno → vercel.com/dashboard\n\n') +
187
+ chalk.white(' Railway (Backend)\n') +
188
+ chalk.gray(' 1. railway login\n') +
189
+ chalk.gray(' 2. cd backend && railway init\n') +
190
+ chalk.gray(' 3. railway up\n') +
191
+ chalk.gray(' 4. Variables de entorno → railway.app/dashboard'),
192
+ { padding: 1, margin: { left: 2 }, borderStyle: 'single', borderColor: 'gray', dimBorder: true }
193
+ ));
173
194
  console.log();
174
195
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "novatec-cli",
3
- "version": "3.0.7",
3
+ "version": "3.0.8",
4
4
  "description": "🚀 NOVATEC FULLSTACK CLI — Generador profesional de proyectos full stack | React, Next.js, Vue, Express, NestJS, FastAPI y más",
5
5
  "type": "module",
6
6
  "main": "./lib/create.js",