qdd-framework 2.10.0-rc.4 → 2.10.0-rc.41
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.
- package/README.md +4 -1
- package/bin/qdd.js +17 -1
- package/install.js +103 -13
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -13,6 +13,9 @@
|
|
|
13
13
|
npm install -g qdd-framework --allow-scripts=qdd-framework
|
|
14
14
|
```
|
|
15
15
|
|
|
16
|
+
npm 12 no corre los scripts de instalación sin esa bandera. Si instalas sin ella, la primera vez que uses `qdd` se baja su
|
|
17
|
+
binario desde el release de GitHub de esa versión, fuera del bloqueo de npm.
|
|
18
|
+
|
|
16
19
|
> **Conflicto con el paquete `qdd` de npm:** existe un paquete ajeno llamado `qdd` (no pertenece a QDD Framework) que también instala un comando `qdd`. Si lo tienes instalado globalmente, desinstálalo antes (`npm uninstall -g qdd`) para que `qdd` apunte a QDD Framework. Desde v1.9.22 su presencia ya no rompe la instalación (antes fallaba con `EISDIR`).
|
|
17
20
|
|
|
18
21
|
---
|
|
@@ -33,7 +36,7 @@ qdd audit # corre las pruebas y las reglas de QDD; funciona desde cualquier
|
|
|
33
36
|
- `qdd doctor`: Diagnóstico determinista de salud del entorno.
|
|
34
37
|
- `qdd report`: Reporte ejecutivo consolidado en formato Texto, Markdown o JSON para CI/CD.
|
|
35
38
|
- `qdd evolution`: Recomienda la siguiente mejora del proyecto.
|
|
36
|
-
- `qdd dashboard
|
|
39
|
+
- `qdd dashboard` (obsoleto; el panel que sigue es `qdd-tareas`): Centro de Comando Web interactivo con streaming en tiempo real (SSE) y ejecución de agentes.
|
|
37
40
|
- `qdd run`: Pipeline secuencial de comandos con rollback, o modo supervisor con auto-reparación (`qdd run --keep-alive`).
|
|
38
41
|
|
|
39
42
|
---
|
package/bin/qdd.js
CHANGED
|
@@ -24,8 +24,24 @@ function versionDelPaquete() {
|
|
|
24
24
|
}
|
|
25
25
|
}
|
|
26
26
|
|
|
27
|
+
function binarioActual() {
|
|
28
|
+
return rutaDelBinario({ platform: process.platform, env: process.env, dir: __dirname, version: versionDelPaquete(), existe: fs.existsSync });
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
// Con npm 12, quien instala sin --allow-scripts=qdd-framework no corrió el postinstall (FND-143): el primer qdd baja el
|
|
32
|
+
// binario con el mismo instalador. Lo que escribe va a stderr: otro programa puede estar leyendo la salida de qdd.
|
|
33
|
+
function bajarAlPrimerUso() {
|
|
34
|
+
const instalador = path.join(__dirname, '..', 'install.js');
|
|
35
|
+
if (fs.existsSync(binarioActual()) || !fs.existsSync(instalador)) {
|
|
36
|
+
return;
|
|
37
|
+
}
|
|
38
|
+
console.error('[QDD] qdd no tiene su binario: lo bajo ahora, porque npm no corrió su instalación.');
|
|
39
|
+
spawnSync(process.execPath, [instalador, '--primer-uso'], { stdio: ['ignore', 2, 'inherit'] });
|
|
40
|
+
}
|
|
41
|
+
|
|
27
42
|
function main() {
|
|
28
|
-
|
|
43
|
+
bajarAlPrimerUso();
|
|
44
|
+
const binaryPath = binarioActual();
|
|
29
45
|
|
|
30
46
|
const result = spawnSync(binaryPath, process.argv.slice(2), {
|
|
31
47
|
stdio: 'inherit'
|
package/install.js
CHANGED
|
@@ -9,6 +9,8 @@ const VERSION = 'v' + pkg.version;
|
|
|
9
9
|
const REPO = 'trdago/qdd-framework';
|
|
10
10
|
// TOPE_VERSION_MS: el antivirus puede demorar la primera ejecución de un binario recién descargado.
|
|
11
11
|
const TOPE_VERSION_MS = 60000;
|
|
12
|
+
// TOPE_TURNO_MS: un turno más viejo que esto quedó abandonado; bajar el binario tarda mucho menos.
|
|
13
|
+
const TOPE_TURNO_MS = 5 * 60 * 1000;
|
|
12
14
|
|
|
13
15
|
const platformMap = {
|
|
14
16
|
win32: 'windows',
|
|
@@ -76,6 +78,10 @@ function main() {
|
|
|
76
78
|
fs.mkdirSync(binDir, { recursive: true });
|
|
77
79
|
}
|
|
78
80
|
|
|
81
|
+
if (process.argv.includes('--primer-uso')) {
|
|
82
|
+
pedirTurno(path.join(binDir, '.bajando'));
|
|
83
|
+
}
|
|
84
|
+
|
|
79
85
|
const binarioLocal = resolverBinarioLocal(__dirname, binaryName);
|
|
80
86
|
if (binarioLocal) {
|
|
81
87
|
console.log(`[QDD] Binario local detectado en ${binarioLocal}. Usando copia local para desarrollo...`);
|
|
@@ -109,10 +115,52 @@ function instalarBinarioIndicado({ origen, destino, carpeta }) {
|
|
|
109
115
|
}
|
|
110
116
|
}
|
|
111
117
|
|
|
118
|
+
// tomarTurno: varios qdd sin binario a la vez (un panel, por ejemplo) no bajan cada uno el suyo.
|
|
119
|
+
function tomarTurno(ruta) {
|
|
120
|
+
try {
|
|
121
|
+
fs.closeSync(fs.openSync(ruta, 'wx'));
|
|
122
|
+
return true;
|
|
123
|
+
} catch (err) {
|
|
124
|
+
if (err.code !== 'EEXIST') {
|
|
125
|
+
throw err;
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
if (Date.now() - fs.statSync(ruta).mtimeMs < TOPE_TURNO_MS) {
|
|
129
|
+
return false;
|
|
130
|
+
}
|
|
131
|
+
fs.rmSync(ruta, { force: true });
|
|
132
|
+
return tomarTurno(ruta);
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
// pedirTurno sale con 75, el «todavía no» de los pedidos, si otro qdd ya lo está bajando.
|
|
136
|
+
function pedirTurno(ruta) {
|
|
137
|
+
try {
|
|
138
|
+
if (!tomarTurno(ruta)) {
|
|
139
|
+
console.error('[QDD] Otro proceso ya está bajando el binario.');
|
|
140
|
+
process.exit(75);
|
|
141
|
+
}
|
|
142
|
+
} catch (err) {
|
|
143
|
+
fallar(`no se pudo escribir en la carpeta del paquete: ${err.message}`);
|
|
144
|
+
}
|
|
145
|
+
process.on('exit', () => borrarSinFallar(ruta));
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
// carpetaDeDescarga es propia de este proceso: un postinstall y un primer uso pueden correr a la vez.
|
|
149
|
+
function carpetaDeDescarga() {
|
|
150
|
+
try {
|
|
151
|
+
const carpeta = fs.mkdtempSync(path.join(__dirname, 'descarga-'));
|
|
152
|
+
process.on('exit', () => borrarSinFallar(carpeta));
|
|
153
|
+
return carpeta;
|
|
154
|
+
} catch (err) {
|
|
155
|
+
return fallar(`no se pudo escribir en la carpeta del paquete: ${err.message}`);
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
|
|
112
159
|
// 2. Descarga desde GitHub Releases
|
|
113
160
|
function descargarDesdeRelease({ os, arch, ext, binaryName, binDir, destino, carpeta }) {
|
|
114
161
|
const url = `https://github.com/${REPO}/releases/download/${VERSION}/qdd_${os}_${arch}.${ext}`;
|
|
115
|
-
const
|
|
162
|
+
const descarga = carpetaDeDescarga();
|
|
163
|
+
const downloadDest = path.join(descarga, `qdd-download.${ext}`);
|
|
116
164
|
|
|
117
165
|
console.log(`[QDD] Descargando binario nativo para ${os}-${arch}...`);
|
|
118
166
|
console.log(`[QDD] URL: ${url}`);
|
|
@@ -145,7 +193,7 @@ function descargarDesdeRelease({ os, arch, ext, binaryName, binDir, destino, car
|
|
|
145
193
|
function extractBinary() {
|
|
146
194
|
console.log('[QDD] Extrayendo...');
|
|
147
195
|
try {
|
|
148
|
-
const bin = instalarDescarga({ os, paquete: downloadDest, carpeta:
|
|
196
|
+
const bin = instalarDescarga({ os, paquete: downloadDest, carpeta: descarga, binaryName, binDir, env: process.env, destino });
|
|
149
197
|
console.log('[QDD] ¡Instalación nativa completada exitosamente!');
|
|
150
198
|
terminarInstalacion({ bin, carpeta });
|
|
151
199
|
} catch (err) {
|
|
@@ -191,18 +239,55 @@ function respondeBinario(ruta) {
|
|
|
191
239
|
}
|
|
192
240
|
}
|
|
193
241
|
|
|
194
|
-
//
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
242
|
+
// En Windows, un antivirus puede retener unos segundos el qdd.exe que se acaba de ejecutar (#175).
|
|
243
|
+
const OCUPADO = new Set(['EBUSY', 'EPERM']);
|
|
244
|
+
const ESPERA_OCUPADO_MS = 1000;
|
|
245
|
+
const INTENTOS_OCUPADO = 30;
|
|
246
|
+
|
|
247
|
+
function dormirSincrono(ms) {
|
|
248
|
+
Atomics.wait(new Int32Array(new SharedArrayBuffer(4)), 0, 0, ms);
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
// moverBinario copia si el destino está en otro volumen (el prefijo de npm y %LOCALAPPDATA% pueden estarlo) o si el
|
|
252
|
+
// binario sigue ocupado tras unos 30 s: copiar solo necesita leerlo.
|
|
253
|
+
function moverBinario(origen, destino, renombrar = fs.renameSync, { dormir = dormirSincrono, intentos = INTENTOS_OCUPADO } = {}) {
|
|
254
|
+
for (let i = 0; ; i++) {
|
|
255
|
+
try {
|
|
256
|
+
renombrar(origen, destino);
|
|
257
|
+
return;
|
|
258
|
+
} catch (err) {
|
|
259
|
+
if (err.code === 'EXDEV') {
|
|
260
|
+
break;
|
|
261
|
+
}
|
|
262
|
+
if (!OCUPADO.has(err.code)) {
|
|
263
|
+
throw err;
|
|
264
|
+
}
|
|
265
|
+
if (i + 1 >= intentos) {
|
|
266
|
+
break;
|
|
267
|
+
}
|
|
268
|
+
dormir(ESPERA_OCUPADO_MS);
|
|
202
269
|
}
|
|
203
270
|
}
|
|
204
271
|
copiarBinario(origen, destino);
|
|
205
|
-
|
|
272
|
+
borrarSinFallar(origen);
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
// quitarSiVacia no deja una carpeta de versión sin binario cuando la instalación falla (#175).
|
|
276
|
+
function quitarSiVacia(carpeta) {
|
|
277
|
+
try {
|
|
278
|
+
fs.rmdirSync(carpeta);
|
|
279
|
+
} catch (_) {
|
|
280
|
+
// tiene archivos (otra instalación) o ya no está: se deja.
|
|
281
|
+
}
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
// borrarSinFallar: que no se pueda borrar algo temporal no rompe la instalación ni tapa su mensaje (#175).
|
|
285
|
+
function borrarSinFallar(ruta) {
|
|
286
|
+
try {
|
|
287
|
+
fs.rmSync(ruta, { recursive: true, force: true, maxRetries: 5, retryDelay: 200 });
|
|
288
|
+
} catch (err) {
|
|
289
|
+
console.error(`[QDD] No se pudo borrar ${ruta}: ${err.message}`);
|
|
290
|
+
}
|
|
206
291
|
}
|
|
207
292
|
|
|
208
293
|
// copiarBinario nunca deja un binario a medias con el nombre final.
|
|
@@ -263,7 +348,12 @@ function instalarDescarga({ os, paquete, carpeta, binaryName, binDir, env, desti
|
|
|
263
348
|
fs.chmodSync(extraido, 0o755);
|
|
264
349
|
execFileSync(extraido, ['--version'], { stdio: 'pipe', timeout: TOPE_VERSION_MS });
|
|
265
350
|
fs.mkdirSync(path.dirname(destino), { recursive: true });
|
|
266
|
-
|
|
351
|
+
try {
|
|
352
|
+
moverBinario(extraido, destino);
|
|
353
|
+
} catch (err) {
|
|
354
|
+
quitarSiVacia(path.dirname(destino));
|
|
355
|
+
throw err;
|
|
356
|
+
}
|
|
267
357
|
fs.rmSync(paquete, { force: true });
|
|
268
358
|
return destino;
|
|
269
359
|
}
|
|
@@ -290,7 +380,7 @@ function actualizarConsola(bin) {
|
|
|
290
380
|
}
|
|
291
381
|
}
|
|
292
382
|
|
|
293
|
-
module.exports = { esArchivo, estaDentroDeNodeModules, resolverBinarioLocal, comandoExtraer, instalarDescarga, codigoDeFalla, carpetaDeVersiones, destinoDelBinario, respondeBinario, moverBinario, instalarCopia, registrarVersion };
|
|
383
|
+
module.exports = { esArchivo, estaDentroDeNodeModules, resolverBinarioLocal, comandoExtraer, instalarDescarga, codigoDeFalla, carpetaDeVersiones, destinoDelBinario, respondeBinario, moverBinario, instalarCopia, registrarVersion, tomarTurno };
|
|
294
384
|
|
|
295
385
|
if (require.main === module) {
|
|
296
386
|
main();
|