feat(scripts): 新增环境检测脚本(用于检测用户当前系统环境) (#166)

This commit is contained in:
Dawn
2025-01-08 15:07:59 +08:00
committed by GitHub
parent 0dd59a5f87
commit 3983bf71fc
6 changed files with 288 additions and 13 deletions

62
scripts/check-all.js Normal file
View File

@@ -0,0 +1,62 @@
import { execSync } from 'child_process'
import chalk from 'chalk'
import { fileURLToPath } from 'url'
import { dirname, join } from 'path'
const __filename = fileURLToPath(import.meta.url)
const __dirname = dirname(__filename)
/**
* 执行单个检查脚本
* @param {string} scriptPath - 脚本路径
* @param {string} description - 检查描述
*/
async function runScript(scriptPath, description) {
const startTime = Date.now()
console.log(chalk.blue(`\n[${new Date().toLocaleTimeString()}] 开始${description}...\n`))
try {
execSync(`node ${scriptPath}`, { stdio: 'inherit' })
const duration = ((Date.now() - startTime) / 1000).toFixed(2)
console.log(chalk.green(`\n${description}完成 (${duration}s)\n`))
return true
} catch (error) {
console.error(chalk.red(`\n${description}失败`))
return false
}
}
async function main() {
console.log(chalk.cyan('正在检查HuLa需要的环境配置...\n'))
/** @type {CheckItem[]} */
const checks = [
{
script: join(__dirname, 'check-env.js'),
description: '配置文件检查'
},
{
script: join(__dirname, 'check-dependencies.js'),
description: '环境检查'
}
]
const startTime = Date.now()
for (const check of checks) {
const success = await runScript(check.script, check.description)
if (!success) {
console.error(chalk.red(`\n${check.description}未通过,终止检查流程\n`))
process.exit(1)
}
}
const totalDuration = ((Date.now() - startTime) / 1000).toFixed(2)
console.log(chalk.green(`\n✨ 所有检查通过!总用时:${totalDuration}s\n`))
}
main().catch((error) => {
console.error(chalk.red('\n检查过程中发生错误'))
console.error(chalk.yellow(error.stack || error))
process.exit(1)
})

View File

@@ -0,0 +1,208 @@
import { execSync } from 'child_process'
import chalk from 'chalk'
import { platform } from 'os'
import { existsSync } from 'fs'
// 环境安装指南
const INSTALL_GUIDES = {
'Node.js': 'https://nodejs.org/zh-cn/download/',
pnpm: 'https://pnpm.io/zh/installation',
Rust: 'https://www.rust-lang.org/tools/install',
'Visual C++ Build Tools': 'https://visualstudio.microsoft.com/visual-cpp-build-tools/',
'WebView2 Runtime': 'https://developer.microsoft.com/microsoft-edge/webview2/'
}
// Windows 特定的检查路径
const WINDOWS_PATHS = {
'Visual C++ Build Tools': [
'C:\\Program Files (x86)\\Microsoft Visual Studio\\',
'C:\\Program Files\\Microsoft Visual Studio\\',
'C:\\Program Files (x86)\\Windows Kits\\10\\bin'
],
'WebView2 Runtime': [
'C:\\Program Files (x86)\\Microsoft\\EdgeWebView\\Application',
'C:\\Program Files\\Microsoft\\EdgeWebView\\Application',
'C:\\Windows\\SystemApps\\Microsoft.Win32WebViewHost_cw5n1h2txyewy'
]
}
// 错误信息映射
const ERROR_MESSAGES = {
ENOENT: '命令未找到',
EPERM: '权限不足',
EACCES: '访问被拒绝'
}
const checks = [
{
name: 'Node.js',
command: 'node --version',
versionExtractor: (output) => output.replace('v', ''),
minVersion: '18.0.0',
isRequired: true
},
{
name: 'pnpm',
command: 'pnpm --version',
versionExtractor: (output) => output.trim(),
minVersion: '9.0.0',
isRequired: true
},
{
name: 'Rust',
command: 'rustc --version',
versionExtractor: (output) => output.split(' ')[1],
minVersion: '1.78.0',
isRequired: true
}
]
/**
* 检查 Visual C++ Build Tools 是否安装
* @returns {boolean}
*/
const checkVCBuildTools = () => {
try {
// 检查 cl.exe 是否存在
execSync('where cl.exe', { stdio: 'ignore' })
// 检查是否能正常执行
const output = execSync('cl.exe 2>&1', { encoding: 'utf8' })
return output.includes('Microsoft') && output.includes('Compiler')
} catch {
return false
}
}
/**
* 检查 WebView2 是否安装
* @returns {boolean}
*/
const checkWebView2 = () => {
try {
// 检查注册表
const regQuery =
'reg query "HKLM\\SOFTWARE\\WOW6432Node\\Microsoft\\EdgeUpdate\\Clients\\{F3017226-FE2A-4295-8BDF-00C3A9A7E4C5}" /v pv'
execSync(regQuery, { stdio: 'ignore' })
return true
} catch {
// 如果注册表查询失败,检查文件路径
return WINDOWS_PATHS['WebView2 Runtime'].some((path) => existsSync(path))
}
}
// Windows 特定的检查
const windowsChecks = [
{
name: 'Visual C++ Build Tools',
checkInstalled: checkVCBuildTools,
isRequired: true
},
{
name: 'WebView2 Runtime',
checkInstalled: checkWebView2,
isRequired: true
}
]
/**
* 获取友好的错误提示
* @param {Error} error 错误对象
* @returns {string} 错误提示
*/
const getFriendlyErrorMessage = (error) => {
const code = error.code || ''
return ERROR_MESSAGES[code] || error.message || '未知错误'
}
/**
* 比较版本号
* @param {string} version1 当前版本
* @param {string} version2 所需版本
* @returns {number} 1: version1 大, -1: version2 大, 0: 相等
*/
const compareVersions = (version1, version2) => {
const v1 = version1.replace(/[^0-9.]/g, '').split('.')
const v2 = version2.replace(/[^0-9.]/g, '').split('.')
for (let i = 0; i < Math.max(v1.length, v2.length); i++) {
const num1 = parseInt(v1[i] || '0')
const num2 = parseInt(v2[i] || '0')
if (num1 > num2) return 1
if (num1 < num2) return -1
}
return 0
}
function checkDependency(check) {
try {
const output = execSync(check.command).toString().trim()
const version = check.versionExtractor(output)
const isVersionValid = compareVersions(version, check.minVersion) >= 0
if (isVersionValid) {
console.log(chalk.green(`${check.name} 版本 ${output} 已安装`))
return true
} else {
console.log(chalk.yellow(`⚠️ ${check.name} 版本过低`))
console.log(chalk.yellow(` 当前版本: ${output}`))
console.log(chalk.yellow(` 需要版本: >=${check.minVersion}`))
console.log(chalk.gray(` 👉 升级指南: ${INSTALL_GUIDES[check.name]}`))
return false
}
} catch (error) {
const errorMessage = getFriendlyErrorMessage(error)
console.log(chalk.red(`${check.name} 未安装`))
console.log(chalk.red(` 原因: ${errorMessage}`))
console.log(chalk.gray(` 👉 安装指南: ${INSTALL_GUIDES[check.name]}`))
return false
}
}
/**
* 检查 Windows 特定的依赖
* @param {Object} check 检查项
* @returns {boolean} 是否通过检查
*/
function checkWindowsDependency(check) {
try {
const isInstalled = check.checkInstalled()
if (isInstalled) {
console.log(chalk.green(`${check.name} 已安装`))
return true
} else {
console.log(chalk.red(`${check.name} 未安装`))
console.log(chalk.gray(` 👉 安装指南: ${INSTALL_GUIDES[check.name]}`))
return false
}
} catch (error) {
const errorMessage = getFriendlyErrorMessage(error)
console.log(chalk.red(`${check.name} 检查失败`))
console.log(chalk.red(` 原因: ${errorMessage}`))
return false
}
}
function main() {
const isWindows = platform() === 'win32'
// 执行基本检查
const results = checks.map(checkDependency)
// 在 Windows 上执行额外检查
if (isWindows) {
console.log(chalk.blue('\n正在检查 Windows 开发环境...'))
const windowsResults = windowsChecks.map(checkWindowsDependency)
results.push(...windowsResults)
}
if (results.every(Boolean)) {
console.log(chalk.green('\n✓ 所有环境检查通过!'))
process.exit(0)
} else {
console.log(chalk.red('\n✗ 环境依赖检查失败,请按照上述提示安装或更新依赖。'))
process.exit(1)
}
}
main()

View File

@@ -1,5 +1,6 @@
import { existsSync, writeFileSync } from 'fs'
import { join } from 'path'
import chalk from 'chalk'
// 用于写入.env.local配置文件该文件默认不会被git管理所以不必担心会提交到远程仓库
const envPath = join(process.cwd(), '.env.local')
@@ -15,11 +16,11 @@ VITE_TENCENT_SECRET_ID=
try {
writeFileSync(envPath, defaultEnvContent, 'utf8')
console.log('✨ 成功创建.env.local文件')
console.log(chalk.green('✨ 成功创建.env.local文件'))
} catch (error) {
console.error(' 创建.env.local文件失败:', error)
console.log(chalk.red('\n✗ 创建.env.local文件失败'))
process.exit(1)
}
} else {
console.log(' .env.local文件已存在')
console.log(chalk.green(' .env.local文件已创建'))
}