@dbcube/cli 4.1.2 → 4.1.4
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/bun.lock +4 -3
- package/package.json +4 -3
- package/src/commands/help.js +3 -3
- package/src/commands/update.js +417 -0
- package/src/index.js +3 -1
- package/src/commands/run/upgrade.js +0 -302
package/bun.lock
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
"": {
|
|
5
5
|
"name": "@dbcube/cli",
|
|
6
6
|
"dependencies": {
|
|
7
|
-
"@dbcube/schema-builder": "^4.1.
|
|
7
|
+
"@dbcube/schema-builder": "^4.1.4",
|
|
8
8
|
"@inquirer/prompts": "^8.0.2",
|
|
9
9
|
"alwait": "^1.0.0",
|
|
10
10
|
"chalk": "^5.6.2",
|
|
@@ -12,13 +12,14 @@
|
|
|
12
12
|
"fs-extra": "^11.3.2",
|
|
13
13
|
"glob": "^13.0.0",
|
|
14
14
|
"ora": "^9.0.0",
|
|
15
|
+
"unzipper": "^0.12.3",
|
|
15
16
|
},
|
|
16
17
|
},
|
|
17
18
|
},
|
|
18
19
|
"packages": {
|
|
19
|
-
"@dbcube/core": ["@dbcube/core@4.1.
|
|
20
|
+
"@dbcube/core": ["@dbcube/core@4.1.7", "", { "dependencies": { "chalk": "^5.6.2", "deasync": "^0.1.31", "follow-redirects": "^1.15.11", "ora": "^9.0.0", "unzipper": "^0.12.3" }, "bin": { "dbcube-core": "dist/bin.cjs" } }, "sha512-/2VwxVOHil8QJ+ZcN/l1Qj/R5wqVJG6Swk0Quf1YmD1c+OsNmW9BZt/v3Ncpb+X8YZXdxRibeJU3AMtXXy425w=="],
|
|
20
21
|
|
|
21
|
-
"@dbcube/schema-builder": ["@dbcube/schema-builder@4.1.
|
|
22
|
+
"@dbcube/schema-builder": ["@dbcube/schema-builder@4.1.4", "", { "dependencies": { "@dbcube/core": "^4.1.7", "chalk": "^5.6.2", "ora": "^9.0.0" } }, "sha512-AnjOFxd7BqKOHEIg0OUuLiEQtoH5NyDUn5dENQxiiX544Pgd3Y4f/VFj2kRR1CU/MFmwrQRGypum4AX5W9cEEA=="],
|
|
22
23
|
|
|
23
24
|
"@inquirer/ansi": ["@inquirer/ansi@2.0.2", "", {}, "sha512-SYLX05PwJVnW+WVegZt1T4Ip1qba1ik+pNJPDiqvk6zS5Y/i8PhRzLpGEtVd7sW0G8cMtkD8t4AZYhQwm8vnww=="],
|
|
24
25
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@dbcube/cli",
|
|
3
|
-
"version": "4.1.
|
|
3
|
+
"version": "4.1.4",
|
|
4
4
|
"main": "index.js",
|
|
5
5
|
"scripts": {
|
|
6
6
|
"dbcube": "node src/index.js"
|
|
@@ -13,13 +13,14 @@
|
|
|
13
13
|
"license": "ISC",
|
|
14
14
|
"description": "",
|
|
15
15
|
"dependencies": {
|
|
16
|
-
"@dbcube/schema-builder": "^4.1.
|
|
16
|
+
"@dbcube/schema-builder": "^4.1.4",
|
|
17
17
|
"@inquirer/prompts": "^8.0.2",
|
|
18
18
|
"alwait": "^1.0.0",
|
|
19
19
|
"chalk": "^5.6.2",
|
|
20
20
|
"dotenv": "^17.2.3",
|
|
21
21
|
"fs-extra": "^11.3.2",
|
|
22
22
|
"glob": "^13.0.0",
|
|
23
|
-
"ora": "^9.0.0"
|
|
23
|
+
"ora": "^9.0.0",
|
|
24
|
+
"unzipper": "^0.12.3"
|
|
24
25
|
}
|
|
25
26
|
}
|
package/src/commands/help.js
CHANGED
|
@@ -28,10 +28,10 @@ async function showHelp() {
|
|
|
28
28
|
console.log(` ${chalk.yellow('run seeder:add')} ${chalk.white('Add test data from seeder .cube files')}\n`);
|
|
29
29
|
|
|
30
30
|
console.log(`${chalk.cyan.bold('BINARY MANAGEMENT:')}`);
|
|
31
|
+
console.log(` ${chalk.yellow('update')} ${chalk.white('Check and update binaries to latest version')}`);
|
|
31
32
|
console.log(` ${chalk.yellow('run download')} ${chalk.white('Download binaries interactively')}`);
|
|
32
33
|
console.log(` ${chalk.yellow('run download <engine>')} ${chalk.white('Download specific engine (latest version)')}`);
|
|
33
34
|
console.log(` ${chalk.yellow('run download <engine> <ver>')} ${chalk.white('Download specific engine version')}`);
|
|
34
|
-
console.log(` ${chalk.yellow('run upgrade')} ${chalk.white('Update all binaries to latest version')}`);
|
|
35
35
|
console.log(` ${chalk.gray(' Engines:')} query-engine, schema-engine, sqlite-engine\n`);
|
|
36
36
|
|
|
37
37
|
console.log(`${chalk.cyan.bold('UTILITY COMMANDS:')}`);
|
|
@@ -58,8 +58,8 @@ async function showHelp() {
|
|
|
58
58
|
console.log(` ${chalk.gray('# Download a specific binary version')}`);
|
|
59
59
|
console.log(` ${chalk.white('npx dbcube run download query-engine v3.1.1')}\n`);
|
|
60
60
|
|
|
61
|
-
console.log(` ${chalk.gray('#
|
|
62
|
-
console.log(` ${chalk.white('npx dbcube
|
|
61
|
+
console.log(` ${chalk.gray('# Check and update binaries if needed')}`);
|
|
62
|
+
console.log(` ${chalk.white('npx dbcube update')}\n`);
|
|
63
63
|
|
|
64
64
|
console.log(`${chalk.cyan.bold('CONFIGURATION:')}`);
|
|
65
65
|
console.log(` ${chalk.white('Create a')} ${chalk.yellow('dbcube.config.js')} ${chalk.white('file in your project root:')}`);
|
|
@@ -0,0 +1,417 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
const { default: chalk } = require('chalk');
|
|
3
|
+
const path = require('path');
|
|
4
|
+
const fs = require('fs');
|
|
5
|
+
const https = require('https');
|
|
6
|
+
const os = require('os');
|
|
7
|
+
const unzipper = require('unzipper');
|
|
8
|
+
const ora = require('ora');
|
|
9
|
+
|
|
10
|
+
const VERSION_URLS = {
|
|
11
|
+
'query-engine': 'https://raw.githubusercontent.com/Dbcube/binaries/main/query-engines.json',
|
|
12
|
+
'schema-engine': 'https://raw.githubusercontent.com/Dbcube/binaries/main/schema-engines.json',
|
|
13
|
+
'sqlite-engine': 'https://raw.githubusercontent.com/Dbcube/binaries/main/sqlite-engines.json'
|
|
14
|
+
};
|
|
15
|
+
|
|
16
|
+
const BINARY_PREFIX_MAP = {
|
|
17
|
+
'query-engine': 'query',
|
|
18
|
+
'schema-engine': 'schema',
|
|
19
|
+
'sqlite-engine': 'sqlite'
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
const PLATFORM_MAP = {
|
|
23
|
+
win32: 'windows',
|
|
24
|
+
linux: 'linux',
|
|
25
|
+
darwin: 'macos'
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
const ARCH_MAP = {
|
|
29
|
+
x64: 'x64',
|
|
30
|
+
arm64: 'arm64'
|
|
31
|
+
};
|
|
32
|
+
|
|
33
|
+
function getPlatformInfo() {
|
|
34
|
+
const platform = PLATFORM_MAP[process.platform];
|
|
35
|
+
const arch = ARCH_MAP[process.arch];
|
|
36
|
+
|
|
37
|
+
if (!platform || !arch) {
|
|
38
|
+
throw new Error(`Plataforma no soportada: ${process.platform} ${process.arch}`);
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
return { platform, arch };
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
function getBinDir() {
|
|
45
|
+
const possibleDirs = [
|
|
46
|
+
path.resolve(process.cwd(), '.dbcube', 'bin'),
|
|
47
|
+
path.resolve(process.cwd(), 'node_modules', '.dbcube', 'bin'),
|
|
48
|
+
];
|
|
49
|
+
|
|
50
|
+
for (const dir of possibleDirs) {
|
|
51
|
+
try {
|
|
52
|
+
if (!fs.existsSync(dir)) {
|
|
53
|
+
fs.mkdirSync(dir, { recursive: true });
|
|
54
|
+
}
|
|
55
|
+
return dir;
|
|
56
|
+
} catch {
|
|
57
|
+
continue;
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
const tempDir = path.join(os.tmpdir(), '.dbcube', 'bin');
|
|
62
|
+
fs.mkdirSync(tempDir, { recursive: true });
|
|
63
|
+
return tempDir;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
/**
|
|
67
|
+
* Fetch latest version from GitHub
|
|
68
|
+
*/
|
|
69
|
+
function fetchLatestVersion(engineType) {
|
|
70
|
+
const url = VERSION_URLS[engineType];
|
|
71
|
+
|
|
72
|
+
return new Promise((resolve, reject) => {
|
|
73
|
+
https.get(url, (response) => {
|
|
74
|
+
let data = '';
|
|
75
|
+
|
|
76
|
+
response.on('data', (chunk) => {
|
|
77
|
+
data += chunk;
|
|
78
|
+
});
|
|
79
|
+
|
|
80
|
+
response.on('end', () => {
|
|
81
|
+
try {
|
|
82
|
+
const versions = JSON.parse(data);
|
|
83
|
+
if (versions && versions.length > 0) {
|
|
84
|
+
// Get the latest version (first in array)
|
|
85
|
+
resolve(versions[0].version);
|
|
86
|
+
} else {
|
|
87
|
+
reject(new Error('No versions found'));
|
|
88
|
+
}
|
|
89
|
+
} catch (error) {
|
|
90
|
+
reject(error);
|
|
91
|
+
}
|
|
92
|
+
});
|
|
93
|
+
|
|
94
|
+
response.on('error', reject);
|
|
95
|
+
}).on('error', reject);
|
|
96
|
+
});
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
/**
|
|
100
|
+
* Extract version from binary filename
|
|
101
|
+
* Example: schema-engine-v1.0.0-windows-x64.exe -> v1.0.0
|
|
102
|
+
*/
|
|
103
|
+
function extractVersionFromFilename(filename) {
|
|
104
|
+
const match = filename.match(/-(v\d+\.\d+\.\d+)-/);
|
|
105
|
+
return match ? match[1] : null;
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
/**
|
|
109
|
+
* Get local version of installed binary
|
|
110
|
+
*/
|
|
111
|
+
function getLocalVersion(binDir, prefix) {
|
|
112
|
+
try {
|
|
113
|
+
const files = fs.readdirSync(binDir);
|
|
114
|
+
const binaryPattern = new RegExp(`^${prefix}-engine-v`);
|
|
115
|
+
const binaryFile = files.find(f => binaryPattern.test(f));
|
|
116
|
+
|
|
117
|
+
if (binaryFile) {
|
|
118
|
+
return extractVersionFromFilename(binaryFile);
|
|
119
|
+
}
|
|
120
|
+
} catch (error) {
|
|
121
|
+
// Directory doesn't exist or can't read
|
|
122
|
+
}
|
|
123
|
+
return null;
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
/**
|
|
127
|
+
* Compare versions (returns true if remote is newer)
|
|
128
|
+
*/
|
|
129
|
+
function isNewerVersion(localVersion, remoteVersion) {
|
|
130
|
+
if (!localVersion) return true; // No local version, download
|
|
131
|
+
|
|
132
|
+
// Remove 'v' prefix if exists
|
|
133
|
+
const cleanLocal = localVersion.replace(/^v/, '');
|
|
134
|
+
const cleanRemote = remoteVersion.replace(/^v/, '');
|
|
135
|
+
|
|
136
|
+
const localParts = cleanLocal.split('.').map(Number);
|
|
137
|
+
const remoteParts = cleanRemote.split('.').map(Number);
|
|
138
|
+
|
|
139
|
+
for (let i = 0; i < 3; i++) {
|
|
140
|
+
if (remoteParts[i] > localParts[i]) return true;
|
|
141
|
+
if (remoteParts[i] < localParts[i]) return false;
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
return false; // Versions are equal
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
/**
|
|
148
|
+
* Delete old binary files
|
|
149
|
+
*/
|
|
150
|
+
function deleteOldBinary(binDir, prefix) {
|
|
151
|
+
try {
|
|
152
|
+
const files = fs.readdirSync(binDir);
|
|
153
|
+
const binaryPattern = new RegExp(`^${prefix}-engine-`);
|
|
154
|
+
|
|
155
|
+
files.forEach(file => {
|
|
156
|
+
if (binaryPattern.test(file)) {
|
|
157
|
+
const filePath = path.join(binDir, file);
|
|
158
|
+
try {
|
|
159
|
+
fs.unlinkSync(filePath);
|
|
160
|
+
console.log(chalk.gray(` 🗑️ Deleted: ${file}`));
|
|
161
|
+
} catch (err) {
|
|
162
|
+
console.warn(chalk.yellow(` ⚠️ Could not delete: ${file}`));
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
});
|
|
166
|
+
} catch (error) {
|
|
167
|
+
// Ignore errors
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
async function updateBinary(engineType, localVersion, remoteVersion) {
|
|
172
|
+
const spinner = ora(`Updating ${engineType}...`).start();
|
|
173
|
+
|
|
174
|
+
try {
|
|
175
|
+
const { platform, arch } = getPlatformInfo();
|
|
176
|
+
const prefix = BINARY_PREFIX_MAP[engineType];
|
|
177
|
+
|
|
178
|
+
if (!prefix) {
|
|
179
|
+
throw new Error(`Tipo de binario no soportado: ${engineType}`);
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
const baseName = `${prefix}-engine-${remoteVersion}-${platform}-${arch}`;
|
|
183
|
+
const binaryName = platform === 'windows' ? `${baseName}.exe` : baseName;
|
|
184
|
+
|
|
185
|
+
// Build download URL
|
|
186
|
+
const url = `https://github.com/Dbcube/binaries/releases/download/${prefix}-engine/${prefix}-engine-${remoteVersion}-${platform}-${arch}.zip`;
|
|
187
|
+
|
|
188
|
+
const binDir = getBinDir();
|
|
189
|
+
const finalBinaryPath = path.join(binDir, binaryName);
|
|
190
|
+
|
|
191
|
+
spinner.text = `Deleting old ${engineType} (${localVersion || 'none'})...`;
|
|
192
|
+
|
|
193
|
+
// Delete old binary first
|
|
194
|
+
deleteOldBinary(binDir, prefix);
|
|
195
|
+
|
|
196
|
+
const tempZipPath = path.join(os.tmpdir(), `dbcube-${prefix}-${Date.now()}.zip`);
|
|
197
|
+
|
|
198
|
+
spinner.text = `Downloading ${engineType} ${remoteVersion}...`;
|
|
199
|
+
|
|
200
|
+
// Download
|
|
201
|
+
await downloadFile(url, tempZipPath, (progress) => {
|
|
202
|
+
const percentage = progress.percentage.toFixed(1);
|
|
203
|
+
const downloaded = (progress.downloaded / 1024 / 1024).toFixed(1);
|
|
204
|
+
const total = (progress.total / 1024 / 1024).toFixed(1);
|
|
205
|
+
spinner.text = `Downloading ${chalk.cyan(engineType)} ${chalk.yellow(remoteVersion)}: ${percentage}% (${downloaded}/${total}MB)`;
|
|
206
|
+
});
|
|
207
|
+
|
|
208
|
+
spinner.text = `Extracting ${engineType}...`;
|
|
209
|
+
|
|
210
|
+
// Extract
|
|
211
|
+
await extractBinary(tempZipPath, finalBinaryPath);
|
|
212
|
+
|
|
213
|
+
// Cleanup
|
|
214
|
+
cleanupFile(tempZipPath);
|
|
215
|
+
|
|
216
|
+
spinner.succeed(chalk.green(`✓ ${engineType} updated: ${localVersion || 'none'} → ${remoteVersion}`));
|
|
217
|
+
console.log(chalk.gray(` Location: ${finalBinaryPath}`));
|
|
218
|
+
|
|
219
|
+
return true;
|
|
220
|
+
|
|
221
|
+
} catch (error) {
|
|
222
|
+
spinner.fail(chalk.red(`✗ Error updating ${engineType}`));
|
|
223
|
+
console.error(chalk.red(` ${error.message}`));
|
|
224
|
+
return false;
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
function downloadFile(url, outputPath, onProgress) {
|
|
229
|
+
return new Promise((resolve, reject) => {
|
|
230
|
+
https.get(url, { timeout: 30000 }, (response) => {
|
|
231
|
+
// Handle redirects
|
|
232
|
+
if (response.statusCode === 302 || response.statusCode === 301) {
|
|
233
|
+
const redirectUrl = response.headers.location;
|
|
234
|
+
if (redirectUrl) {
|
|
235
|
+
return downloadFile(redirectUrl, outputPath, onProgress).then(resolve).catch(reject);
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
if (response.statusCode !== 200) {
|
|
240
|
+
reject(new Error(`HTTP ${response.statusCode}: Binary not found for this version`));
|
|
241
|
+
return;
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
const file = fs.createWriteStream(outputPath);
|
|
245
|
+
const totalBytes = parseInt(response.headers['content-length'] || '0', 10);
|
|
246
|
+
let downloadedBytes = 0;
|
|
247
|
+
|
|
248
|
+
response.on('data', (chunk) => {
|
|
249
|
+
downloadedBytes += chunk.length;
|
|
250
|
+
file.write(chunk);
|
|
251
|
+
|
|
252
|
+
if (totalBytes > 0 && onProgress) {
|
|
253
|
+
onProgress({
|
|
254
|
+
downloaded: downloadedBytes,
|
|
255
|
+
total: totalBytes,
|
|
256
|
+
percentage: (downloadedBytes / totalBytes) * 100
|
|
257
|
+
});
|
|
258
|
+
}
|
|
259
|
+
});
|
|
260
|
+
|
|
261
|
+
response.on('end', () => {
|
|
262
|
+
file.end();
|
|
263
|
+
resolve();
|
|
264
|
+
});
|
|
265
|
+
|
|
266
|
+
response.on('error', (err) => {
|
|
267
|
+
file.close();
|
|
268
|
+
cleanupFile(outputPath);
|
|
269
|
+
reject(err);
|
|
270
|
+
});
|
|
271
|
+
|
|
272
|
+
file.on('error', (err) => {
|
|
273
|
+
file.close();
|
|
274
|
+
cleanupFile(outputPath);
|
|
275
|
+
reject(err);
|
|
276
|
+
});
|
|
277
|
+
}).on('error', reject).on('timeout', () => {
|
|
278
|
+
reject(new Error('Download timeout'));
|
|
279
|
+
});
|
|
280
|
+
});
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
function extractBinary(zipPath, outputPath) {
|
|
284
|
+
return new Promise((resolve, reject) => {
|
|
285
|
+
let extracted = false;
|
|
286
|
+
|
|
287
|
+
fs.createReadStream(zipPath)
|
|
288
|
+
.pipe(unzipper.Parse())
|
|
289
|
+
.on('entry', (entry) => {
|
|
290
|
+
if (entry.type === 'File' && !extracted) {
|
|
291
|
+
extracted = true;
|
|
292
|
+
const writeStream = fs.createWriteStream(outputPath);
|
|
293
|
+
|
|
294
|
+
entry.pipe(writeStream);
|
|
295
|
+
|
|
296
|
+
writeStream.on('finish', () => {
|
|
297
|
+
if (process.platform !== 'win32') {
|
|
298
|
+
fs.chmodSync(outputPath, 0o755);
|
|
299
|
+
}
|
|
300
|
+
resolve();
|
|
301
|
+
});
|
|
302
|
+
|
|
303
|
+
writeStream.on('error', (err) => {
|
|
304
|
+
reject(err);
|
|
305
|
+
});
|
|
306
|
+
} else {
|
|
307
|
+
entry.autodrain();
|
|
308
|
+
}
|
|
309
|
+
})
|
|
310
|
+
.on('error', (err) => {
|
|
311
|
+
reject(err);
|
|
312
|
+
})
|
|
313
|
+
.on('close', () => {
|
|
314
|
+
if (!extracted) {
|
|
315
|
+
reject(new Error('No valid file found in ZIP'));
|
|
316
|
+
}
|
|
317
|
+
});
|
|
318
|
+
});
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
function cleanupFile(filePath) {
|
|
322
|
+
try {
|
|
323
|
+
if (fs.existsSync(filePath)) {
|
|
324
|
+
fs.unlinkSync(filePath);
|
|
325
|
+
}
|
|
326
|
+
} catch {
|
|
327
|
+
// Ignore cleanup errors
|
|
328
|
+
}
|
|
329
|
+
}
|
|
330
|
+
|
|
331
|
+
async function main() {
|
|
332
|
+
console.log(chalk.blue('\n🔄 Dbcube Update - Check and Update Binaries\n'));
|
|
333
|
+
|
|
334
|
+
const binDir = getBinDir();
|
|
335
|
+
const binaries = ['query-engine', 'schema-engine', 'sqlite-engine'];
|
|
336
|
+
|
|
337
|
+
const spinner = ora('Checking versions...').start();
|
|
338
|
+
|
|
339
|
+
const updates = [];
|
|
340
|
+
|
|
341
|
+
// Check all binaries
|
|
342
|
+
for (const engineType of binaries) {
|
|
343
|
+
try {
|
|
344
|
+
const prefix = BINARY_PREFIX_MAP[engineType];
|
|
345
|
+
const localVersion = getLocalVersion(binDir, prefix);
|
|
346
|
+
const remoteVersion = await fetchLatestVersion(engineType);
|
|
347
|
+
const needsUpdate = isNewerVersion(localVersion, remoteVersion);
|
|
348
|
+
|
|
349
|
+
updates.push({
|
|
350
|
+
engineType,
|
|
351
|
+
localVersion,
|
|
352
|
+
remoteVersion,
|
|
353
|
+
needsUpdate
|
|
354
|
+
});
|
|
355
|
+
|
|
356
|
+
} catch (error) {
|
|
357
|
+
console.warn(chalk.yellow(`\n⚠️ Could not check ${engineType}: ${error.message}`));
|
|
358
|
+
}
|
|
359
|
+
}
|
|
360
|
+
|
|
361
|
+
spinner.stop();
|
|
362
|
+
|
|
363
|
+
// Show status
|
|
364
|
+
console.log(chalk.cyan('📊 Version Status:\n'));
|
|
365
|
+
|
|
366
|
+
let updatesAvailable = 0;
|
|
367
|
+
|
|
368
|
+
for (const update of updates) {
|
|
369
|
+
if (update.needsUpdate) {
|
|
370
|
+
console.log(chalk.yellow(` 📦 ${update.engineType}: ${update.localVersion || 'not installed'} → ${update.remoteVersion}`));
|
|
371
|
+
updatesAvailable++;
|
|
372
|
+
} else if (update.localVersion) {
|
|
373
|
+
console.log(chalk.green(` ✅ ${update.engineType}: ${update.localVersion} (up to date)`));
|
|
374
|
+
} else {
|
|
375
|
+
console.log(chalk.gray(` ℹ️ ${update.engineType}: not installed`));
|
|
376
|
+
}
|
|
377
|
+
}
|
|
378
|
+
|
|
379
|
+
if (updatesAvailable === 0) {
|
|
380
|
+
console.log(chalk.green('\n✨ All binaries are up to date!\n'));
|
|
381
|
+
return;
|
|
382
|
+
}
|
|
383
|
+
|
|
384
|
+
console.log(chalk.cyan(`\n🔄 Updating ${updatesAvailable} binary(ies)...\n`));
|
|
385
|
+
|
|
386
|
+
// Update binaries
|
|
387
|
+
let successCount = 0;
|
|
388
|
+
let failCount = 0;
|
|
389
|
+
|
|
390
|
+
for (const update of updates) {
|
|
391
|
+
if (update.needsUpdate) {
|
|
392
|
+
const success = await updateBinary(update.engineType, update.localVersion, update.remoteVersion);
|
|
393
|
+
if (success) {
|
|
394
|
+
successCount++;
|
|
395
|
+
} else {
|
|
396
|
+
failCount++;
|
|
397
|
+
}
|
|
398
|
+
}
|
|
399
|
+
}
|
|
400
|
+
|
|
401
|
+
// Summary
|
|
402
|
+
console.log('');
|
|
403
|
+
if (failCount === 0) {
|
|
404
|
+
console.log(chalk.green.bold(`✅ All binaries (${successCount}/${updatesAvailable}) updated successfully!\n`));
|
|
405
|
+
} else if (successCount > 0) {
|
|
406
|
+
console.log(chalk.yellow.bold(`⚠️ Partial update: ${successCount}/${updatesAvailable} binaries updated\n`));
|
|
407
|
+
process.exit(1);
|
|
408
|
+
} else {
|
|
409
|
+
console.log(chalk.red.bold(`❌ No binaries were updated\n`));
|
|
410
|
+
process.exit(1);
|
|
411
|
+
}
|
|
412
|
+
}
|
|
413
|
+
|
|
414
|
+
main().catch(err => {
|
|
415
|
+
console.error(chalk.red('\n❌ Unexpected error:'), err.message);
|
|
416
|
+
process.exit(1);
|
|
417
|
+
});
|
package/src/index.js
CHANGED
|
@@ -47,7 +47,9 @@ const commandMap = {
|
|
|
47
47
|
'run:database:create:physical': '../src/commands/run/database/create/createDatabase.js',
|
|
48
48
|
|
|
49
49
|
'run:download': '../src/commands/run/download.js',
|
|
50
|
-
'run:
|
|
50
|
+
'run:update': '../src/commands/run/update.js',
|
|
51
|
+
|
|
52
|
+
'update': '../src/commands/update.js',
|
|
51
53
|
|
|
52
54
|
'--version': '../src/commands/version.js',
|
|
53
55
|
'-v': '../src/commands/version.js',
|
|
@@ -1,302 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
const chalk = require('chalk');
|
|
3
|
-
const path = require('path');
|
|
4
|
-
const fs = require('fs');
|
|
5
|
-
const https = require('https');
|
|
6
|
-
const os = require('os');
|
|
7
|
-
const unzipper = require('unzipper');
|
|
8
|
-
const ora = require('ora');
|
|
9
|
-
|
|
10
|
-
const BINARIES = ['query-engine', 'schema-engine', 'sqlite-engine'];
|
|
11
|
-
|
|
12
|
-
const BINARY_PREFIX_MAP = {
|
|
13
|
-
'query-engine': 'query',
|
|
14
|
-
'schema-engine': 'schema',
|
|
15
|
-
'sqlite-engine': 'sqlite'
|
|
16
|
-
};
|
|
17
|
-
|
|
18
|
-
const PLATFORM_MAP = {
|
|
19
|
-
win32: 'windows',
|
|
20
|
-
linux: 'linux',
|
|
21
|
-
darwin: 'macos'
|
|
22
|
-
};
|
|
23
|
-
|
|
24
|
-
const ARCH_MAP = {
|
|
25
|
-
x64: 'x64',
|
|
26
|
-
arm64: 'arm64'
|
|
27
|
-
};
|
|
28
|
-
|
|
29
|
-
function getPlatformInfo() {
|
|
30
|
-
const platform = PLATFORM_MAP[process.platform];
|
|
31
|
-
const arch = ARCH_MAP[process.arch];
|
|
32
|
-
|
|
33
|
-
if (!platform || !arch) {
|
|
34
|
-
throw new Error(`Plataforma no soportada: ${process.platform} ${process.arch}`);
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
return { platform, arch };
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
function getBinDir() {
|
|
41
|
-
const possibleDirs = [
|
|
42
|
-
path.resolve(process.cwd(), '.dbcube', 'bin'),
|
|
43
|
-
path.resolve(process.cwd(), 'node_modules', '.dbcube', 'bin'),
|
|
44
|
-
];
|
|
45
|
-
|
|
46
|
-
for (const dir of possibleDirs) {
|
|
47
|
-
try {
|
|
48
|
-
if (!fs.existsSync(dir)) {
|
|
49
|
-
fs.mkdirSync(dir, { recursive: true });
|
|
50
|
-
}
|
|
51
|
-
return dir;
|
|
52
|
-
} catch {
|
|
53
|
-
continue;
|
|
54
|
-
}
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
const tempDir = path.join(os.tmpdir(), '.dbcube', 'bin');
|
|
58
|
-
fs.mkdirSync(tempDir, { recursive: true });
|
|
59
|
-
return tempDir;
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
function cleanBinDirectory(binDir) {
|
|
63
|
-
if (!fs.existsSync(binDir)) {
|
|
64
|
-
return 0;
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
const files = fs.readdirSync(binDir);
|
|
68
|
-
let deletedCount = 0;
|
|
69
|
-
|
|
70
|
-
for (const file of files) {
|
|
71
|
-
const filePath = path.join(binDir, file);
|
|
72
|
-
try {
|
|
73
|
-
const stat = fs.statSync(filePath);
|
|
74
|
-
if (stat.isFile()) {
|
|
75
|
-
fs.unlinkSync(filePath);
|
|
76
|
-
deletedCount++;
|
|
77
|
-
}
|
|
78
|
-
} catch (err) {
|
|
79
|
-
console.warn(chalk.yellow(`⚠️ No se pudo eliminar: ${file}`));
|
|
80
|
-
}
|
|
81
|
-
}
|
|
82
|
-
|
|
83
|
-
return deletedCount;
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
async function downloadBinary(engineType, version, spinner) {
|
|
87
|
-
const { platform, arch } = getPlatformInfo();
|
|
88
|
-
const prefix = BINARY_PREFIX_MAP[engineType];
|
|
89
|
-
|
|
90
|
-
if (!prefix) {
|
|
91
|
-
throw new Error(`Tipo de binario no soportado: ${engineType}`);
|
|
92
|
-
}
|
|
93
|
-
|
|
94
|
-
const baseName = `${prefix}-engine-${platform}-${arch}`;
|
|
95
|
-
const binaryName = platform === 'windows' ? `${baseName}.exe` : baseName;
|
|
96
|
-
|
|
97
|
-
// Build download URL
|
|
98
|
-
const versionTag = version === 'latest' ? version : `v${version}`;
|
|
99
|
-
const url = `https://github.com/Dbcube/binaries/releases/download/${prefix}-engine/${prefix}-engine-${versionTag}-${platform}-${arch}.zip`;
|
|
100
|
-
|
|
101
|
-
const binDir = getBinDir();
|
|
102
|
-
const tempZipPath = path.join(os.tmpdir(), `dbcube-${prefix}-${Date.now()}.zip`);
|
|
103
|
-
const finalBinaryPath = path.join(binDir, binaryName);
|
|
104
|
-
|
|
105
|
-
spinner.text = `📥 Descargando ${chalk.cyan(engineType)}...`;
|
|
106
|
-
|
|
107
|
-
// Download
|
|
108
|
-
await downloadFile(url, tempZipPath, (progress) => {
|
|
109
|
-
const percentage = progress.percentage.toFixed(1);
|
|
110
|
-
spinner.text = `📥 ${chalk.cyan(engineType)}: ${percentage}%`;
|
|
111
|
-
});
|
|
112
|
-
|
|
113
|
-
spinner.text = `📦 Extrayendo ${chalk.cyan(engineType)}...`;
|
|
114
|
-
|
|
115
|
-
// Extract
|
|
116
|
-
await extractBinary(tempZipPath, finalBinaryPath);
|
|
117
|
-
|
|
118
|
-
// Cleanup
|
|
119
|
-
cleanupFile(tempZipPath);
|
|
120
|
-
|
|
121
|
-
return finalBinaryPath;
|
|
122
|
-
}
|
|
123
|
-
|
|
124
|
-
function downloadFile(url, outputPath, onProgress) {
|
|
125
|
-
return new Promise((resolve, reject) => {
|
|
126
|
-
https.get(url, { timeout: 30000 }, (response) => {
|
|
127
|
-
// Handle redirects
|
|
128
|
-
if (response.statusCode === 302 || response.statusCode === 301) {
|
|
129
|
-
const redirectUrl = response.headers.location;
|
|
130
|
-
if (redirectUrl) {
|
|
131
|
-
return downloadFile(redirectUrl, outputPath, onProgress).then(resolve).catch(reject);
|
|
132
|
-
}
|
|
133
|
-
}
|
|
134
|
-
|
|
135
|
-
if (response.statusCode !== 200) {
|
|
136
|
-
reject(new Error(`HTTP ${response.statusCode}: El binario no existe`));
|
|
137
|
-
return;
|
|
138
|
-
}
|
|
139
|
-
|
|
140
|
-
const file = fs.createWriteStream(outputPath);
|
|
141
|
-
const totalBytes = parseInt(response.headers['content-length'] || '0', 10);
|
|
142
|
-
let downloadedBytes = 0;
|
|
143
|
-
|
|
144
|
-
response.on('data', (chunk) => {
|
|
145
|
-
downloadedBytes += chunk.length;
|
|
146
|
-
file.write(chunk);
|
|
147
|
-
|
|
148
|
-
if (totalBytes > 0 && onProgress) {
|
|
149
|
-
onProgress({
|
|
150
|
-
downloaded: downloadedBytes,
|
|
151
|
-
total: totalBytes,
|
|
152
|
-
percentage: (downloadedBytes / totalBytes) * 100
|
|
153
|
-
});
|
|
154
|
-
}
|
|
155
|
-
});
|
|
156
|
-
|
|
157
|
-
response.on('end', () => {
|
|
158
|
-
file.end();
|
|
159
|
-
resolve();
|
|
160
|
-
});
|
|
161
|
-
|
|
162
|
-
response.on('error', (err) => {
|
|
163
|
-
file.close();
|
|
164
|
-
cleanupFile(outputPath);
|
|
165
|
-
reject(err);
|
|
166
|
-
});
|
|
167
|
-
|
|
168
|
-
file.on('error', (err) => {
|
|
169
|
-
file.close();
|
|
170
|
-
cleanupFile(outputPath);
|
|
171
|
-
reject(err);
|
|
172
|
-
});
|
|
173
|
-
}).on('error', reject).on('timeout', () => {
|
|
174
|
-
reject(new Error('Timeout descargando el archivo'));
|
|
175
|
-
});
|
|
176
|
-
});
|
|
177
|
-
}
|
|
178
|
-
|
|
179
|
-
function extractBinary(zipPath, outputPath) {
|
|
180
|
-
return new Promise((resolve, reject) => {
|
|
181
|
-
let extracted = false;
|
|
182
|
-
|
|
183
|
-
fs.createReadStream(zipPath)
|
|
184
|
-
.pipe(unzipper.Parse())
|
|
185
|
-
.on('entry', (entry) => {
|
|
186
|
-
if (entry.type === 'File' && !extracted) {
|
|
187
|
-
extracted = true;
|
|
188
|
-
const writeStream = fs.createWriteStream(outputPath);
|
|
189
|
-
|
|
190
|
-
entry.pipe(writeStream);
|
|
191
|
-
|
|
192
|
-
writeStream.on('finish', () => {
|
|
193
|
-
if (process.platform !== 'win32') {
|
|
194
|
-
fs.chmodSync(outputPath, 0o755);
|
|
195
|
-
}
|
|
196
|
-
resolve();
|
|
197
|
-
});
|
|
198
|
-
|
|
199
|
-
writeStream.on('error', (err) => {
|
|
200
|
-
reject(err);
|
|
201
|
-
});
|
|
202
|
-
} else {
|
|
203
|
-
entry.autodrain();
|
|
204
|
-
}
|
|
205
|
-
})
|
|
206
|
-
.on('error', (err) => {
|
|
207
|
-
reject(err);
|
|
208
|
-
})
|
|
209
|
-
.on('close', () => {
|
|
210
|
-
if (!extracted) {
|
|
211
|
-
reject(new Error('No se encontró archivo válido en el ZIP'));
|
|
212
|
-
}
|
|
213
|
-
});
|
|
214
|
-
});
|
|
215
|
-
}
|
|
216
|
-
|
|
217
|
-
function cleanupFile(filePath) {
|
|
218
|
-
try {
|
|
219
|
-
if (fs.existsSync(filePath)) {
|
|
220
|
-
fs.unlinkSync(filePath);
|
|
221
|
-
}
|
|
222
|
-
} catch {
|
|
223
|
-
// Ignore cleanup errors
|
|
224
|
-
}
|
|
225
|
-
}
|
|
226
|
-
|
|
227
|
-
async function main() {
|
|
228
|
-
console.log(chalk.blue('\n🔄 Dbcube Upgrade - Actualización de Binarios\n'));
|
|
229
|
-
|
|
230
|
-
const spinner = ora('Iniciando proceso de actualización...').start();
|
|
231
|
-
|
|
232
|
-
try {
|
|
233
|
-
const binDir = getBinDir();
|
|
234
|
-
|
|
235
|
-
// Step 1: Clean bin directory
|
|
236
|
-
spinner.text = '🗑️ Limpiando directorio de binarios...';
|
|
237
|
-
const deletedCount = cleanBinDirectory(binDir);
|
|
238
|
-
|
|
239
|
-
if (deletedCount > 0) {
|
|
240
|
-
spinner.succeed(chalk.green(`✓ ${deletedCount} archivo(s) eliminado(s)`));
|
|
241
|
-
} else {
|
|
242
|
-
spinner.info(chalk.gray('No había binarios previos'));
|
|
243
|
-
}
|
|
244
|
-
|
|
245
|
-
// Step 2: Download all binaries
|
|
246
|
-
console.log(chalk.cyan('\n📦 Descargando binarios latest...\n'));
|
|
247
|
-
|
|
248
|
-
const results = [];
|
|
249
|
-
let successCount = 0;
|
|
250
|
-
let failCount = 0;
|
|
251
|
-
|
|
252
|
-
for (const binary of BINARIES) {
|
|
253
|
-
const binarySpinner = ora(`Procesando ${chalk.cyan(binary)}...`).start();
|
|
254
|
-
|
|
255
|
-
try {
|
|
256
|
-
const binaryPath = await downloadBinary(binary, 'latest', binarySpinner);
|
|
257
|
-
binarySpinner.succeed(chalk.green(`✓ ${binary} descargado`));
|
|
258
|
-
results.push({ binary, success: true, path: binaryPath });
|
|
259
|
-
successCount++;
|
|
260
|
-
} catch (error) {
|
|
261
|
-
binarySpinner.fail(chalk.red(`✗ ${binary} falló: ${error.message}`));
|
|
262
|
-
results.push({ binary, success: false, error: error.message });
|
|
263
|
-
failCount++;
|
|
264
|
-
}
|
|
265
|
-
}
|
|
266
|
-
|
|
267
|
-
// Summary
|
|
268
|
-
console.log(chalk.cyan('\n📊 Resumen de actualización:\n'));
|
|
269
|
-
|
|
270
|
-
for (const result of results) {
|
|
271
|
-
if (result.success) {
|
|
272
|
-
console.log(chalk.green(` ✓ ${result.binary}`));
|
|
273
|
-
console.log(chalk.gray(` ${result.path}`));
|
|
274
|
-
} else {
|
|
275
|
-
console.log(chalk.red(` ✗ ${result.binary}`));
|
|
276
|
-
console.log(chalk.gray(` Error: ${result.error}`));
|
|
277
|
-
}
|
|
278
|
-
}
|
|
279
|
-
|
|
280
|
-
console.log('');
|
|
281
|
-
|
|
282
|
-
if (failCount === 0) {
|
|
283
|
-
console.log(chalk.green.bold(`✅ Todos los binarios (${successCount}/${BINARIES.length}) actualizados exitosamente\n`));
|
|
284
|
-
} else if (successCount > 0) {
|
|
285
|
-
console.log(chalk.yellow.bold(`⚠️ Actualización parcial: ${successCount}/${BINARIES.length} binarios descargados\n`));
|
|
286
|
-
process.exit(1);
|
|
287
|
-
} else {
|
|
288
|
-
console.log(chalk.red.bold(`❌ No se pudo descargar ningún binario\n`));
|
|
289
|
-
process.exit(1);
|
|
290
|
-
}
|
|
291
|
-
|
|
292
|
-
} catch (error) {
|
|
293
|
-
spinner.fail(chalk.red('Error en el proceso de actualización'));
|
|
294
|
-
console.error(chalk.red(`\n❌ ${error.message}\n`));
|
|
295
|
-
process.exit(1);
|
|
296
|
-
}
|
|
297
|
-
}
|
|
298
|
-
|
|
299
|
-
main().catch(err => {
|
|
300
|
-
console.error(chalk.red('\n❌ Error inesperado:'), err.message);
|
|
301
|
-
process.exit(1);
|
|
302
|
-
});
|