fe-build-cli 1.2.5 → 1.6.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.
- package/README.md +310 -11
- package/package.json +1 -1
- package/src/cli.js +249 -20
- package/src/config-template.js +14 -1
- package/src/deploy-core.js +310 -64
- package/src/dingtalk.js +31 -17
- package/src/git-branch.js +191 -34
- package/src/index.js +22 -1
- package/src/logger.js +381 -0
- package/src/ssh-client.js +69 -0
- package/src/update.js +136 -0
package/src/cli.js
CHANGED
|
@@ -5,10 +5,12 @@ import { execSync } from 'node:child_process';
|
|
|
5
5
|
import fs from 'node:fs';
|
|
6
6
|
import path from 'node:path';
|
|
7
7
|
import { fileURLToPath, pathToFileURL } from 'node:url';
|
|
8
|
-
import { deployToServer, rollbackDeployment } from './deploy-core.js';
|
|
8
|
+
import { deployToServer, rollbackDeployment, getServerBackupList, getLocalBackupList, rollbackFromLocal } from './deploy-core.js';
|
|
9
|
+
import SSHClient from './ssh-client.js';
|
|
9
10
|
import {
|
|
10
11
|
getCurrentBranch,
|
|
11
12
|
getGitSha,
|
|
13
|
+
getGitCommitMessage,
|
|
12
14
|
executeMainBranchFlow,
|
|
13
15
|
executeCurrentBranchFlow,
|
|
14
16
|
executeTestBranchFlow,
|
|
@@ -20,6 +22,8 @@ import {
|
|
|
20
22
|
sendDeployFailureNotification,
|
|
21
23
|
sendRollbackNotification
|
|
22
24
|
} from './dingtalk.js';
|
|
25
|
+
import { DeployLogger } from './logger.js';
|
|
26
|
+
import { checkForUpdate, performUpdate, showUpdateInfo, getCurrentVersion } from './update.js';
|
|
23
27
|
|
|
24
28
|
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
25
29
|
|
|
@@ -164,7 +168,11 @@ fe-build-cli - 前端项目打包部署工具
|
|
|
164
168
|
|
|
165
169
|
命令:
|
|
166
170
|
deploy [环境] 部署到指定环境(默认命令)
|
|
167
|
-
rollback [环境]
|
|
171
|
+
rollback [环境] 回滚到指定版本(交互选择备份来源)
|
|
172
|
+
update 检查并更新到最新版本
|
|
173
|
+
update --force 自动更新(无需确认)
|
|
174
|
+
check-update 仅检查是否有新版本
|
|
175
|
+
version 显示当前版本号
|
|
168
176
|
help 显示帮助信息
|
|
169
177
|
|
|
170
178
|
选项:
|
|
@@ -176,6 +184,9 @@ fe-build-cli - 前端项目打包部署工具
|
|
|
176
184
|
--no-merge test 发布时不合并,使用 stash 储藏本地改动
|
|
177
185
|
--skip-build 跳过构建步骤
|
|
178
186
|
--no-push 发布时不推送到远程
|
|
187
|
+
--server 回滚时使用服务器备份(默认)
|
|
188
|
+
--local 回滚时使用本地备份
|
|
189
|
+
--version <版本号> 回滚到指定版本
|
|
179
190
|
|
|
180
191
|
示例:
|
|
181
192
|
fe-build # 交互式选择环境部署
|
|
@@ -186,7 +197,9 @@ fe-build-cli - 前端项目打包部署工具
|
|
|
186
197
|
fe-build --test-branch --no-merge # test 发布,stash 储藏改动
|
|
187
198
|
fe-build --current-branch # 当前分支发布
|
|
188
199
|
fe-build --main-branch # 主分支发布流程
|
|
189
|
-
fe-build rollback production
|
|
200
|
+
fe-build rollback production # 回滚生产环境(交互选择)
|
|
201
|
+
fe-build rollback production --server # 回滚生产环境(服务器备份)
|
|
202
|
+
fe-build rollback production --local # 回滚生产环境(本地备份)
|
|
190
203
|
|
|
191
204
|
配置文件 (fe-build.config.js):
|
|
192
205
|
export default {
|
|
@@ -295,6 +308,12 @@ async function deployCommand(config) {
|
|
|
295
308
|
}
|
|
296
309
|
}
|
|
297
310
|
|
|
311
|
+
// 创建日志记录器(在分支操作之前)
|
|
312
|
+
const logDir = config.logDir || 'logs';
|
|
313
|
+
const localBackupDir = config.localBackupDir || 'D:\\备份';
|
|
314
|
+
const logger = new DeployLogger({ logDir, localBackupDir });
|
|
315
|
+
logger.start();
|
|
316
|
+
|
|
298
317
|
// 执行分支发布流程
|
|
299
318
|
let branchResult = null;
|
|
300
319
|
let originalBranch = getCurrentBranch(); // 记录原始分支
|
|
@@ -320,7 +339,8 @@ async function deployCommand(config) {
|
|
|
320
339
|
testBranch: branches.test,
|
|
321
340
|
mergeChanges,
|
|
322
341
|
pushToRemote: !noPush,
|
|
323
|
-
prompt
|
|
342
|
+
prompt,
|
|
343
|
+
logger // 传递 logger
|
|
324
344
|
});
|
|
325
345
|
originalBranch = branchResult.originalBranch;
|
|
326
346
|
needRestore = branchResult.needRestore;
|
|
@@ -339,19 +359,21 @@ async function deployCommand(config) {
|
|
|
339
359
|
const confirmAnswer = await prompt('确认执行主分支发布流程? (y/n): ');
|
|
340
360
|
if (confirmAnswer.toLowerCase() !== 'y') {
|
|
341
361
|
console.log('已取消发布');
|
|
362
|
+
logger.end('cancelled');
|
|
342
363
|
process.exit(0);
|
|
343
364
|
}
|
|
344
365
|
|
|
345
366
|
branchResult = executeMainBranchFlow({
|
|
346
367
|
testBranch: branches.test,
|
|
347
368
|
mainBranch: branches.main,
|
|
348
|
-
pushToRemote: !noPush
|
|
369
|
+
pushToRemote: !noPush,
|
|
370
|
+
logger // 传递 logger
|
|
349
371
|
});
|
|
350
372
|
originalBranch = branchResult.originalBranch;
|
|
351
373
|
needRestore = true;
|
|
352
374
|
} else if (deployMode === 'current') {
|
|
353
375
|
// 当前分支发布模式
|
|
354
|
-
branchResult = executeCurrentBranchFlow();
|
|
376
|
+
branchResult = executeCurrentBranchFlow(logger);
|
|
355
377
|
originalBranch = branchResult.currentBranch;
|
|
356
378
|
needRestore = false;
|
|
357
379
|
console.log('📌 当前分支发布模式:不切换分支');
|
|
@@ -359,7 +381,10 @@ async function deployCommand(config) {
|
|
|
359
381
|
} catch (branchError) {
|
|
360
382
|
// 分支流程失败,发送钉钉通知
|
|
361
383
|
console.error(`❌ 分支流程失败:`, branchError.message);
|
|
384
|
+
logger.log('ERROR', '分支流程失败', branchError.message);
|
|
385
|
+
logger.end('failed');
|
|
362
386
|
|
|
387
|
+
const commitMessage = getGitCommitMessage();
|
|
363
388
|
if (config.dingtalk && config.dingtalk.enabled && config.dingtalk.webhook) {
|
|
364
389
|
console.log('\n发送钉钉失败通知...');
|
|
365
390
|
const envConfig = getServerConfig(config, selectedServers[0] || serverNames[0]);
|
|
@@ -368,13 +393,14 @@ async function deployCommand(config) {
|
|
|
368
393
|
buildVersion: '未完成',
|
|
369
394
|
serverHost: envConfig?.sshHost || '未知',
|
|
370
395
|
branch: originalBranch,
|
|
396
|
+
commitMessage,
|
|
371
397
|
error: `分支流程失败: ${branchError.message}`,
|
|
372
398
|
keyword: config.dingtalk.keyword || '部署'
|
|
373
399
|
});
|
|
374
400
|
}
|
|
375
401
|
|
|
376
402
|
// 切回原分支
|
|
377
|
-
restoreBranch(originalBranch, hasStash);
|
|
403
|
+
restoreBranch(originalBranch, hasStash, logger);
|
|
378
404
|
process.exit(1);
|
|
379
405
|
}
|
|
380
406
|
|
|
@@ -404,18 +430,25 @@ async function deployCommand(config) {
|
|
|
404
430
|
}
|
|
405
431
|
console.log('========================================');
|
|
406
432
|
|
|
433
|
+
// 部署到服务器(使用前面创建的 logger)
|
|
407
434
|
try {
|
|
408
435
|
await deployToServer({
|
|
409
436
|
environment: serverName,
|
|
410
437
|
envConfig,
|
|
411
438
|
buildVersion,
|
|
412
439
|
skipBuild: skipBuild || !isFirst,
|
|
413
|
-
skipLocalCleanup: i < selectedServers.length - 1
|
|
440
|
+
skipLocalCleanup: i < selectedServers.length - 1,
|
|
441
|
+
logger,
|
|
442
|
+
localBackupDir
|
|
414
443
|
});
|
|
415
444
|
|
|
445
|
+
// 部署成功,结束日志记录
|
|
446
|
+
logger.end('success');
|
|
447
|
+
|
|
416
448
|
// 部署成功,发送钉钉通知
|
|
417
449
|
const duration = Math.round((Date.now() - startTime) / 1000);
|
|
418
450
|
const currentBranch = getCurrentBranch();
|
|
451
|
+
const commitMessage = getGitCommitMessage();
|
|
419
452
|
|
|
420
453
|
if (config.dingtalk && config.dingtalk.enabled && config.dingtalk.webhook) {
|
|
421
454
|
console.log('\n发送钉钉通知...');
|
|
@@ -426,15 +459,21 @@ async function deployCommand(config) {
|
|
|
426
459
|
deployUrl: envConfig.deployUrl,
|
|
427
460
|
branch: currentBranch,
|
|
428
461
|
deployMode,
|
|
462
|
+
commitMessage,
|
|
429
463
|
duration: `${duration}秒`,
|
|
430
464
|
keyword: config.dingtalk.keyword || '部署'
|
|
431
465
|
});
|
|
466
|
+
logger.logDingTalk(true);
|
|
432
467
|
}
|
|
433
468
|
} catch (error) {
|
|
434
469
|
console.error(`❌ 部署到 ${serverName} 失败:`, error.message);
|
|
435
470
|
|
|
471
|
+
// 部署失败,结束日志记录
|
|
472
|
+
logger.end('failed');
|
|
473
|
+
|
|
436
474
|
// 部署失败,发送钉钉通知
|
|
437
475
|
const currentBranch = getCurrentBranch();
|
|
476
|
+
const commitMessage = getGitCommitMessage();
|
|
438
477
|
if (config.dingtalk && config.dingtalk.enabled && config.dingtalk.webhook) {
|
|
439
478
|
console.log('\n发送钉钉失败通知...');
|
|
440
479
|
await sendDeployFailureNotification(config.dingtalk.webhook, {
|
|
@@ -442,14 +481,16 @@ async function deployCommand(config) {
|
|
|
442
481
|
buildVersion,
|
|
443
482
|
serverHost: envConfig.sshHost,
|
|
444
483
|
branch: currentBranch,
|
|
484
|
+
commitMessage,
|
|
445
485
|
error: error.message,
|
|
446
486
|
keyword: config.dingtalk.keyword || '部署'
|
|
447
487
|
});
|
|
488
|
+
logger.logDingTalk(false, error.message);
|
|
448
489
|
}
|
|
449
490
|
|
|
450
491
|
// 出错时切回原分支
|
|
451
492
|
if (needRestore && originalBranch) {
|
|
452
|
-
restoreBranch(originalBranch, hasStash);
|
|
493
|
+
restoreBranch(originalBranch, hasStash, logger);
|
|
453
494
|
}
|
|
454
495
|
process.exit(1);
|
|
455
496
|
}
|
|
@@ -460,13 +501,13 @@ async function deployCommand(config) {
|
|
|
460
501
|
// 合并模式:自动切回原分支
|
|
461
502
|
if (autoRestore) {
|
|
462
503
|
console.log('\n📌 自动切回原分支...');
|
|
463
|
-
restoreBranch(originalBranch, false);
|
|
504
|
+
restoreBranch(originalBranch, false, logger);
|
|
464
505
|
console.log(`✅ 已切回 ${originalBranch},可继续开发`);
|
|
465
506
|
} else {
|
|
466
507
|
// stash 模式:询问是否切回
|
|
467
508
|
const returnAnswer = await prompt('\n是否切回原分支? (y/n): ');
|
|
468
509
|
if (returnAnswer.toLowerCase() === 'y') {
|
|
469
|
-
restoreBranch(originalBranch, hasStash);
|
|
510
|
+
restoreBranch(originalBranch, hasStash, logger);
|
|
470
511
|
} else if (hasStash) {
|
|
471
512
|
console.log('\n💡 提示: 本地改动已储藏,执行以下命令恢复:');
|
|
472
513
|
console.log(' git stash pop');
|
|
@@ -491,10 +532,12 @@ async function rollbackCommand(config) {
|
|
|
491
532
|
const environment = args.find(arg => arg !== 'rollback' && !arg.startsWith('--'));
|
|
492
533
|
const versionIndex = args.indexOf('--version');
|
|
493
534
|
const specifiedVersion = versionIndex !== -1 ? args[versionIndex + 1] : undefined;
|
|
535
|
+
const useLocalBackup = args.includes('--local'); // 是否使用本地备份
|
|
536
|
+
const useServerBackup = args.includes('--server'); // 是否使用服务器备份
|
|
494
537
|
|
|
495
538
|
if (!environment || !serverNames.includes(environment)) {
|
|
496
539
|
console.error(`❌ 请指定服务器: ${serverNames.join(' 或 ')}`);
|
|
497
|
-
console.error(`用法: fe-build rollback [${serverNames.join('|')}] [--version <版本号>]`);
|
|
540
|
+
console.error(`用法: fe-build rollback [${serverNames.join('|')}] [--server|--local] [--version <版本号>]`);
|
|
498
541
|
process.exit(1);
|
|
499
542
|
}
|
|
500
543
|
|
|
@@ -505,22 +548,134 @@ async function rollbackCommand(config) {
|
|
|
505
548
|
process.exit(1);
|
|
506
549
|
}
|
|
507
550
|
|
|
551
|
+
// 创建日志记录器
|
|
552
|
+
const logDir = config.logDir || 'logs';
|
|
553
|
+
const localBackupDir = config.localBackupDir || 'D:\\备份';
|
|
554
|
+
const logger = new DeployLogger({ logDir, localBackupDir });
|
|
555
|
+
logger.start();
|
|
556
|
+
|
|
557
|
+
console.log('========================================');
|
|
558
|
+
console.log(`开始回滚 ${environment} 环境`);
|
|
559
|
+
console.log(`服务器: ${envConfig.sshHost}`);
|
|
560
|
+
console.log('========================================');
|
|
561
|
+
|
|
562
|
+
// 连接服务器
|
|
563
|
+
const ssh = new SSHClient(envConfig);
|
|
564
|
+
await ssh.connect();
|
|
565
|
+
logger.logSSHConnect(envConfig.sshHost, true);
|
|
566
|
+
|
|
508
567
|
let backupFile = '';
|
|
509
|
-
let
|
|
568
|
+
let selectedBackup = null;
|
|
569
|
+
let backupSource = 'server'; // 默认服务器备份
|
|
510
570
|
|
|
511
571
|
try {
|
|
512
|
-
//
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
572
|
+
// 如果指定了版本号,直接使用
|
|
573
|
+
if (specifiedVersion) {
|
|
574
|
+
backupFile = `${envConfig.backupDir}/${envConfig.backupPrefix}-${specifiedVersion}.tar.gz`;
|
|
575
|
+
console.log(`\n使用指定版本: ${specifiedVersion}`);
|
|
576
|
+
logger.log('INFO', '回滚版本', `指定版本: ${specifiedVersion}`);
|
|
577
|
+
} else {
|
|
578
|
+
// 获取备份列表
|
|
579
|
+
let serverBackups = [];
|
|
580
|
+
let localBackups = [];
|
|
581
|
+
|
|
582
|
+
// 获取服务器备份列表
|
|
583
|
+
console.log('\n[步骤 1] 获取服务器备份列表...');
|
|
584
|
+
serverBackups = await getServerBackupList(ssh, envConfig);
|
|
585
|
+
console.log(`找到 ${serverBackups.length} 个服务器备份`);
|
|
586
|
+
|
|
587
|
+
// 获取本地备份列表
|
|
588
|
+
console.log('\n[步骤 2] 获取本地备份列表...');
|
|
589
|
+
localBackups = getLocalBackupList(localBackupDir, envConfig.backupPrefix);
|
|
590
|
+
console.log(`找到 ${localBackups.length} 个本地备份`);
|
|
591
|
+
|
|
592
|
+
// 如果没有备份
|
|
593
|
+
if (serverBackups.length === 0 && localBackups.length === 0) {
|
|
594
|
+
logger.log('ERROR', '获取备份', '未找到任何备份文件');
|
|
595
|
+
console.error('❌ 未找到任何备份文件!');
|
|
596
|
+
await ssh.disconnect();
|
|
597
|
+
logger.end('failed');
|
|
598
|
+
process.exit(1);
|
|
599
|
+
}
|
|
600
|
+
|
|
601
|
+
// 确定备份来源
|
|
602
|
+
if (useLocalBackup && localBackups.length > 0) {
|
|
603
|
+
backupSource = 'local';
|
|
604
|
+
} else if (useServerBackup && serverBackups.length > 0) {
|
|
605
|
+
backupSource = 'server';
|
|
606
|
+
} else if (!useLocalBackup && !useServerBackup) {
|
|
607
|
+
// 交互选择备份来源(默认服务器)
|
|
608
|
+
console.log('\n========================================');
|
|
609
|
+
console.log(' 📦 选择备份来源');
|
|
610
|
+
console.log('========================================');
|
|
611
|
+
console.log(` 1. 服务器备份 (${serverBackups.length} 个) - 默认`);
|
|
612
|
+
if (localBackups.length > 0) {
|
|
613
|
+
console.log(` 2. 本地备份 (${localBackups.length} 个)`);
|
|
614
|
+
}
|
|
615
|
+
console.log('========================================');
|
|
616
|
+
|
|
617
|
+
const sourceAnswer = await prompt(`请选择备份来源 (1${localBackups.length > 0 ? '/2' : ''}): `);
|
|
618
|
+
if (sourceAnswer === '2' && localBackups.length > 0) {
|
|
619
|
+
backupSource = 'local';
|
|
620
|
+
} else {
|
|
621
|
+
backupSource = 'server';
|
|
622
|
+
}
|
|
623
|
+
}
|
|
624
|
+
|
|
625
|
+
// 显示备份列表供选择
|
|
626
|
+
const backups = backupSource === 'server' ? serverBackups : localBackups;
|
|
627
|
+
|
|
628
|
+
console.log(`\n========================================`);
|
|
629
|
+
console.log(` 📦 ${backupSource === 'server' ? '服务器' : '本地'}备份列表`);
|
|
630
|
+
console.log(`========================================`);
|
|
631
|
+
|
|
632
|
+
backups.forEach((backup, index) => {
|
|
633
|
+
const sizeStr = backup.size ? ` (${formatFileSize(backup.size)})` : '';
|
|
634
|
+
const timeStr = backup.mtime ? ` - ${backup.mtime.toLocaleDateString('zh-CN')}` : '';
|
|
635
|
+
console.log(` ${index + 1}. ${backup.version}${sizeStr}${timeStr}`);
|
|
636
|
+
});
|
|
637
|
+
console.log(`========================================`);
|
|
516
638
|
|
|
639
|
+
const backupAnswer = await prompt(`请选择要回滚的备份 (1-${backups.length}): `);
|
|
640
|
+
const selectedIndex = parseInt(backupAnswer, 10) - 1;
|
|
641
|
+
|
|
642
|
+
if (selectedIndex < 0 || selectedIndex >= backups.length) {
|
|
643
|
+
console.error('❌ 无效选择');
|
|
644
|
+
await ssh.disconnect();
|
|
645
|
+
logger.end('failed');
|
|
646
|
+
process.exit(1);
|
|
647
|
+
}
|
|
648
|
+
|
|
649
|
+
selectedBackup = backups[selectedIndex];
|
|
650
|
+
backupFile = selectedBackup.file;
|
|
651
|
+
|
|
652
|
+
console.log(`\n已选择: ${selectedBackup.version}`);
|
|
653
|
+
logger.log('INFO', '选择备份', `来源: ${backupSource}, 版本: ${selectedBackup.version}`);
|
|
654
|
+
}
|
|
655
|
+
|
|
656
|
+
// 如果是本地备份,需要先上传到服务器
|
|
657
|
+
if (backupSource === 'local' && selectedBackup) {
|
|
658
|
+
const remoteFile = await rollbackFromLocal({
|
|
659
|
+
ssh,
|
|
660
|
+
envConfig,
|
|
661
|
+
localBackupFile: backupFile,
|
|
662
|
+
logger
|
|
663
|
+
});
|
|
664
|
+
backupFile = remoteFile;
|
|
665
|
+
}
|
|
666
|
+
|
|
667
|
+
// 执行回滚
|
|
517
668
|
await rollbackDeployment({
|
|
518
669
|
environment,
|
|
519
670
|
envConfig,
|
|
520
|
-
specifiedVersion
|
|
671
|
+
specifiedVersion: specifiedVersion || (selectedBackup ? selectedBackup.version : undefined),
|
|
672
|
+
backupFile,
|
|
673
|
+
logger,
|
|
674
|
+
ssh // 传递已连接的 ssh
|
|
521
675
|
});
|
|
522
676
|
|
|
523
|
-
|
|
677
|
+
await ssh.disconnect();
|
|
678
|
+
logger.end('success');
|
|
524
679
|
|
|
525
680
|
// 回滚成功,发送钉钉通知
|
|
526
681
|
if (config.dingtalk && config.dingtalk.enabled && config.dingtalk.webhook) {
|
|
@@ -533,10 +688,18 @@ async function rollbackCommand(config) {
|
|
|
533
688
|
success: true,
|
|
534
689
|
keyword: config.dingtalk.keyword || '部署'
|
|
535
690
|
});
|
|
691
|
+
logger.logDingTalk(true);
|
|
536
692
|
}
|
|
537
693
|
} catch (error) {
|
|
538
694
|
console.error('❌ 回滚失败:', error.message);
|
|
539
|
-
|
|
695
|
+
logger.log('ERROR', '回滚失败', error.message);
|
|
696
|
+
logger.end('failed');
|
|
697
|
+
|
|
698
|
+
try {
|
|
699
|
+
await ssh.disconnect();
|
|
700
|
+
} catch (e) {
|
|
701
|
+
// 忽略
|
|
702
|
+
}
|
|
540
703
|
|
|
541
704
|
// 回滚失败,发送钉钉通知
|
|
542
705
|
if (config.dingtalk && config.dingtalk.enabled && config.dingtalk.webhook) {
|
|
@@ -549,12 +712,42 @@ async function rollbackCommand(config) {
|
|
|
549
712
|
success: false,
|
|
550
713
|
keyword: config.dingtalk.keyword || '部署'
|
|
551
714
|
});
|
|
715
|
+
logger.logDingTalk(false, error.message);
|
|
552
716
|
}
|
|
553
717
|
|
|
554
718
|
process.exit(1);
|
|
555
719
|
}
|
|
556
720
|
}
|
|
557
721
|
|
|
722
|
+
/**
|
|
723
|
+
* 格式化文件大小
|
|
724
|
+
*/
|
|
725
|
+
function formatFileSize(bytes) {
|
|
726
|
+
if (bytes < 1024) return bytes + ' B';
|
|
727
|
+
if (bytes < 1024 * 1024) return (bytes / 1024).toFixed(1) + ' KB';
|
|
728
|
+
return (bytes / (1024 * 1024)).toFixed(2) + ' MB';
|
|
729
|
+
}
|
|
730
|
+
|
|
731
|
+
/**
|
|
732
|
+
* 启动时检查更新
|
|
733
|
+
*/
|
|
734
|
+
async function checkUpdateOnStart() {
|
|
735
|
+
try {
|
|
736
|
+
const info = await checkForUpdate();
|
|
737
|
+
if (info && info.hasUpdate) {
|
|
738
|
+
console.log('\n========================================');
|
|
739
|
+
console.log(' 🔄 发现新版本');
|
|
740
|
+
console.log('========================================');
|
|
741
|
+
console.log(`当前版本: ${info.currentVersion}`);
|
|
742
|
+
console.log(`最新版本: ${info.latestVersion}`);
|
|
743
|
+
console.log('\n更新命令: fe-build update --force');
|
|
744
|
+
console.log('========================================\n');
|
|
745
|
+
}
|
|
746
|
+
} catch (error) {
|
|
747
|
+
// 检查更新失败,不影响主流程
|
|
748
|
+
}
|
|
749
|
+
}
|
|
750
|
+
|
|
558
751
|
/**
|
|
559
752
|
* 主入口
|
|
560
753
|
*/
|
|
@@ -568,6 +761,42 @@ async function main() {
|
|
|
568
761
|
process.exit(0);
|
|
569
762
|
}
|
|
570
763
|
|
|
764
|
+
// 显示版本
|
|
765
|
+
if (command === 'version' || args.includes('--version') || args.includes('-v')) {
|
|
766
|
+
const version = getCurrentVersion();
|
|
767
|
+
console.log(`fe-build-cli v${version}`);
|
|
768
|
+
process.exit(0);
|
|
769
|
+
}
|
|
770
|
+
|
|
771
|
+
// 检查更新
|
|
772
|
+
if (command === 'check-update') {
|
|
773
|
+
await showUpdateInfo();
|
|
774
|
+
process.exit(0);
|
|
775
|
+
}
|
|
776
|
+
|
|
777
|
+
// 执行更新
|
|
778
|
+
if (command === 'update') {
|
|
779
|
+
const forceUpdate = args.includes('--force') || args.includes('--auto');
|
|
780
|
+
const info = await showUpdateInfo();
|
|
781
|
+
|
|
782
|
+
if (info && info.hasUpdate) {
|
|
783
|
+
if (forceUpdate) {
|
|
784
|
+
// 自动更新,无需确认
|
|
785
|
+
await performUpdate(true);
|
|
786
|
+
} else {
|
|
787
|
+
// 交互确认
|
|
788
|
+
const answer = await prompt('\n是否立即更新? (y/n): ');
|
|
789
|
+
if (answer.toLowerCase() === 'y') {
|
|
790
|
+
await performUpdate(true);
|
|
791
|
+
}
|
|
792
|
+
}
|
|
793
|
+
}
|
|
794
|
+
process.exit(0);
|
|
795
|
+
}
|
|
796
|
+
|
|
797
|
+
// 其他命令启动时检查更新(静默检查)
|
|
798
|
+
await checkUpdateOnStart();
|
|
799
|
+
|
|
571
800
|
// 加载配置
|
|
572
801
|
const config = await loadConfig();
|
|
573
802
|
|
package/src/config-template.js
CHANGED
|
@@ -80,5 +80,18 @@ export default {
|
|
|
80
80
|
webhook: 'https://oapi.dingtalk.com/robot/send?access_token=your-token', // 钉钉机器人 webhook URL
|
|
81
81
|
enabled: true, // 是否启用钉钉通知,默认 true
|
|
82
82
|
keyword: '部署' // 安全设置关键词(如果机器人设置了关键词,必须配置此项)
|
|
83
|
-
}
|
|
83
|
+
},
|
|
84
|
+
|
|
85
|
+
/**
|
|
86
|
+
* 日志配置(可选)
|
|
87
|
+
* 部署日志存储目录,默认项目根目录下的 logs 目录
|
|
88
|
+
*/
|
|
89
|
+
logDir: 'logs',
|
|
90
|
+
|
|
91
|
+
/**
|
|
92
|
+
* 本地备份目录(可选)
|
|
93
|
+
* 线上备份下载到本地的存储目录,默认 D:\备份
|
|
94
|
+
* 保留 7 天内的备份,自动清理旧备份
|
|
95
|
+
*/
|
|
96
|
+
localBackupDir: 'D:\\备份'
|
|
84
97
|
};
|