Skip to content

Commit 9302560

Browse files
committed
chore(release): añadir doctor de preflight para build/empaquetado e integrarlo en CI
1 parent 4a8bc96 commit 9302560

File tree

3 files changed

+111
-1
lines changed

3 files changed

+111
-1
lines changed

.github/workflows/release.yml

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,9 @@ jobs:
6565
- name: Instalar dependencias npm
6666
run: npm ci
6767

68+
- name: Doctor release (preflight)
69+
run: npm run doctor:release
70+
6871
- name: Setup Rust
6972
uses: dtolnay/rust-toolchain@stable
7073

@@ -131,6 +134,9 @@ jobs:
131134
- name: Instalar dependencias npm
132135
run: npm ci
133136

137+
- name: Doctor release (preflight)
138+
run: npm run doctor:release
139+
134140
- name: Setup Rust
135141
uses: dtolnay/rust-toolchain@stable
136142

package.json

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,10 @@
88
"build": "vite build",
99
"preview": "vite preview",
1010
"check": "svelte-check --tsconfig ./tsconfig.app.json && tsc -p tsconfig.node.json",
11-
"tauri": "tauri"
11+
"tauri": "tauri",
12+
"doctor:spawn": "node scripts/doctor-node-spawn.cjs",
13+
"doctor:release": "node scripts/doctor-release.cjs",
14+
"postinstall": "node scripts/patch-vite-windows-eperm.cjs"
1215
},
1316
"devDependencies": {
1417
"@sveltejs/vite-plugin-svelte": "^6.2.1",

scripts/doctor-release.cjs

Lines changed: 101 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,101 @@
1+
const fs = require('node:fs');
2+
const path = require('node:path');
3+
const os = require('node:os');
4+
const { spawnSync } = require('node:child_process');
5+
6+
const root = process.cwd();
7+
const results = [];
8+
9+
function addResult(check, ok, detail, critical = true) {
10+
results.push({ check, ok, detail, critical });
11+
const icon = ok ? 'OK ' : 'FAIL';
12+
console.log(`[doctor:release] ${icon} ${check}: ${detail}`);
13+
}
14+
15+
function checkFileExists(relPath, critical = true) {
16+
const fullPath = path.join(root, relPath);
17+
const ok = fs.existsSync(fullPath);
18+
addResult(
19+
`file:${relPath}`,
20+
ok,
21+
ok ? fullPath : `No existe: ${fullPath}`,
22+
critical
23+
);
24+
}
25+
26+
function checkWritableDir(relPath, critical = true) {
27+
const fullPath = path.join(root, relPath);
28+
try {
29+
fs.mkdirSync(fullPath, { recursive: true });
30+
fs.accessSync(fullPath, fs.constants.W_OK);
31+
const probe = path.join(fullPath, `.doctor-write-${Date.now()}.tmp`);
32+
fs.writeFileSync(probe, 'ok');
33+
fs.unlinkSync(probe);
34+
addResult(`writable:${relPath}`, true, fullPath, critical);
35+
} catch (error) {
36+
addResult(`writable:${relPath}`, false, String(error.message || error), critical);
37+
}
38+
}
39+
40+
function checkSpawn(cmd, args, critical = true) {
41+
try {
42+
const out = spawnSync(cmd, args, { encoding: 'utf8' });
43+
if (out.error) {
44+
addResult(`spawn:${cmd}`, false, `${out.error.code || 'ERR'} ${out.error.message}`, critical);
45+
return;
46+
}
47+
if (out.status !== 0) {
48+
addResult(`spawn:${cmd}`, false, `exit ${out.status} ${out.stderr || ''}`.trim(), critical);
49+
return;
50+
}
51+
const line = (out.stdout || '').trim().split(/\r?\n/)[0] || 'ok';
52+
addResult(`spawn:${cmd}`, true, line, critical);
53+
} catch (error) {
54+
addResult(`spawn:${cmd}`, false, String(error.message || error), critical);
55+
}
56+
}
57+
58+
console.log('[doctor:release] Iniciando chequeos de release...');
59+
console.log(`[doctor:release] Platform=${process.platform} Node=${process.version} CWD=${root}`);
60+
61+
// 1) Integridad de proyecto
62+
checkFileExists('package.json');
63+
checkFileExists('src-tauri/tauri.conf.json');
64+
checkFileExists('src-tauri/bin/yt-dlp.exe', false);
65+
checkFileExists('src-tauri/bin/ffmpeg.exe', false);
66+
67+
// 2) Permisos de escritura para build
68+
checkWritableDir('dist');
69+
checkWritableDir('src-tauri/target');
70+
71+
// 3) Capacidad de spawn (requisito duro para vite/esbuild)
72+
checkSpawn('cmd.exe', ['/c', 'echo', 'spawn-ok']);
73+
checkSpawn('node', ['-v']);
74+
75+
// 4) Sugerencia para bundles Linux
76+
if (process.platform !== 'linux') {
77+
addResult(
78+
'target:linux-bundles',
79+
false,
80+
'AppImage/deb deben generarse en Linux (host o CI Linux)',
81+
false
82+
);
83+
}
84+
85+
const criticalFailures = results.filter((r) => r.critical && !r.ok);
86+
const failures = results.filter((r) => !r.ok);
87+
88+
console.log('');
89+
console.log(`[doctor:release] Resumen: ${results.length - failures.length}/${results.length} checks OK`);
90+
91+
if (criticalFailures.length > 0) {
92+
console.error(`[doctor:release] BLOQUEADO: ${criticalFailures.length} check(s) criticos fallaron.`);
93+
console.error('[doctor:release] No intentes empaquetar hasta resolverlos.');
94+
process.exit(1);
95+
}
96+
97+
if (failures.length > 0) {
98+
console.warn(`[doctor:release] Advertencias: ${failures.length} check(s) no criticos.`);
99+
}
100+
101+
console.log('[doctor:release] Entorno listo para avanzar con release.');

0 commit comments

Comments
 (0)