Files
tauri-meeting/vite/scripts/build.ts
O昵称重要吗O 78af453fe1 chore: 初始化项目基础结构和资源文件
- 添加项目图标文件(app-icon.png、各平台图标)
- 配置开发环境文件(.env、.nvmrc、.npmrc)
- 添加静态资源文件(背景图片、字体、音频)
- 初始化Tauri后端结构(build.rs、main.rs、模块文件)
- 配置前端项目结构(TypeScript、Vue组件、样式)
- 添加Node.js API服务基础结构
- 配置构建和开发工具(vite、prettier、gitignore)
2026-03-13 10:03:05 +08:00

247 lines
9.7 KiB
JavaScript
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#!/usr/bin/env node
import { execSync, spawn } from 'child_process';
import { platform } from 'os';
import { existsSync, readFileSync, readdirSync, rmSync, writeFileSync } from 'fs';
import { join } from 'path';
import {
APPLE_CERTIFICATE,
APPLE_CERTIFICATE_PASSWORD,
APPLE_ID,
APPLE_PASSWORD,
APPLE_SIGNING_IDENTITY,
APPLE_TEAM_ID,
TAURI_SIGNING_PRIVATE_KEY,
TAURI_SIGNING_PRIVATE_KEY_PASSWORD,
} from './utils/constant.ts';
// ==================== 解析命令行参数 ====================
// 从命令行参数中获取 --features 的值
const args: string[] = process.argv.slice(2);
const featuresIndex: number = args.findIndex((arg: string) => arg === '--features');
const buildFeature: string = featuresIndex !== -1 && args[featuresIndex + 1] ? (args[featuresIndex + 1] as string) : 'production';
// 根据 feature 设置对应的环境模式
const modeMap: Record<string, string> = {
production: 'production',
development: 'development',
prod_150_8080: 'prod_150_8080',
};
const buildMode: string = (buildFeature in modeMap ? modeMap[buildFeature as keyof typeof modeMap] : modeMap.production) || 'production';
console.log('\x1b[36m%s\x1b[0m', `构建配置: --features ${buildFeature}, --mode ${buildMode}`);
// ==================== updater签名环境变量配置 ====================
// 私匙
process.env.TAURI_SIGNING_PRIVATE_KEY = TAURI_SIGNING_PRIVATE_KEY;
// 私匙对应的密码,没有就不配置
process.env.TAURI_SIGNING_PRIVATE_KEY_PASSWORD = TAURI_SIGNING_PRIVATE_KEY_PASSWORD;
// Mac 公证
if (platform() === 'darwin') {
/** 签名证书在钥匙串中的名称(签名标识) */
process.env.APPLE_SIGNING_IDENTITY = APPLE_SIGNING_IDENTITY;
/** 从钥匙串导出的 .p12 证书的 base64 字符串(适用于 CI 或没有本地证书时) */
process.env.APPLE_CERTIFICATE = APPLE_CERTIFICATE;
/** .p12 证书的密码。 */
process.env.APPLE_CERTIFICATE_PASSWORD = APPLE_CERTIFICATE_PASSWORD;
/** 你的 Apple 账号邮箱(用于公证) */
process.env.APPLE_ID = APPLE_ID;
/** Apple 账号的 App 专用密码(用于公证) */
process.env.APPLE_PASSWORD = APPLE_PASSWORD;
/** 你的 Apple 开发者团队 ID用于公证 */
process.env.APPLE_TEAM_ID = APPLE_TEAM_ID;
}
// 运行版本更新脚本,同步 tauri.conf.json 和 package.json 的版本号
console.log('\x1b[36m%s\x1b[0m', '正在同步版本号...');
try {
const updateVersionScriptPath = join('.', 'vite', 'scripts', 'update-version.ts');
execSync(`node "${updateVersionScriptPath}"`, { stdio: 'inherit' });
console.log('\x1b[32m%s\x1b[0m', '版本号同步完成');
} catch (error: any) {
console.error('\x1b[31m%s\x1b[0m', '版本号同步失败:', error.message);
process.exit(1);
}
// 读取更新后的 package.json 获取版本号
interface PackageJson {
version: string;
[key: string]: any;
}
const packageJsonPath = join('package.json');
let packageJson: PackageJson = {} as PackageJson;
try {
packageJson = JSON.parse(readFileSync(packageJsonPath, 'utf8'));
} catch (error: any) {
console.error('\x1b[31m%s\x1b[0m', '读取 package.json 失败:', error.message);
process.exit(1);
}
// 获取当前版本号
const newVersion: string = packageJson.version;
// 在构建前删除 src-tauri/target/release/bundle 目录内容
const bundleDir = join('src-tauri', 'target', 'release', 'bundle');
if (existsSync(bundleDir)) {
console.log('\x1b[33m%s\x1b[0m', `正在删除 ${bundleDir} 目录内容...`);
rmSync(bundleDir, { recursive: true, force: true });
}
// 先执行 Vite 默认配置构建
console.log('\x1b[36m%s\x1b[0m', '正在构建 index.html入口...');
const viteBuild = spawn('vite', ['build', '--mode', buildMode], {
stdio: 'inherit',
shell: true,
env: { ...process.env },
});
viteBuild.on('close', (code) => {
if (code !== 0) {
console.error('\x1b[31m%s\x1b[0m', `Vite 打包 index.html失败退出码: ${code}`);
process.exit(code);
}
console.log('\x1b[36m%s\x1b[0m', '正在构建 其他.html入口...');
const viteSimplifiedBuild = spawn('vite', ['build', '--config', 'vite.simplified.config.ts', '--mode', buildMode], {
stdio: 'inherit',
shell: true,
env: { ...process.env },
});
viteSimplifiedBuild.on('close', (simplifiedCode) => {
if (simplifiedCode !== 0) {
console.error('\x1b[31m%s\x1b[0m', `Vite 打包 其他.html入口构建失败退出码: ${simplifiedCode}`);
process.exit(simplifiedCode);
}
console.log('\x1b[36m%s\x1b[0m', 'Vite 构建完成,开始执行 Tauri 构建...');
const tauriBuild = spawn('tauri', ['build', '--features', buildFeature], {
stdio: 'inherit',
shell: true,
env: process.env, // 传递所有环境变量
});
tauriBuild.on('close', (tauriCode) => {
if (tauriCode === 0) {
// 写入 latest.json 到 bundle 目录
const latestJsonPath = join(bundleDir, 'latest.json');
// 为不同平台生成相应的 latest.json
if (platform() === 'win32') {
const latestJson = getLatestJson('windows', newVersion, 'nsis', 'exe');
writeFileSync(latestJsonPath, JSON.stringify(latestJson, null, 2));
console.log('\x1b[32m%s\x1b[0m', `已生成 latest.json 文件: ${latestJsonPath}`);
} else if (platform() === 'darwin') {
const latestJson = getLatestJson('macos', newVersion, 'macos', 'app.tar.gz');
writeFileSync(latestJsonPath, JSON.stringify(latestJson, null, 2));
console.log('\x1b[32m%s\x1b[0m', `已生成 latest.json 文件: ${latestJsonPath}`);
} else if (platform() === 'linux') {
// 为 AppImage 生成 latest.json
const appImageLatest = getLatestJson('linux', newVersion, 'appimage', 'AppImage');
writeFileSync(join(bundleDir, 'appimage', 'latest.json'), JSON.stringify(appImageLatest, null, 2));
console.log('\x1b[32m%s\x1b[0m', `已生成 appimage-latest.json 文件`);
// 为 deb 生成 latest.json
const debLatest = getLatestJson('linux', newVersion, 'deb', 'deb');
writeFileSync(join(bundleDir, 'deb', 'latest.json'), JSON.stringify(debLatest, null, 2));
console.log('\x1b[32m%s\x1b[0m', `已生成 deb-latest.json 文件`);
// 为 rpm 生成 latest.json
const rpmLatest = getLatestJson('linux', newVersion, 'rpm', 'rpm');
writeFileSync(join(bundleDir, 'rpm', 'latest.json'), JSON.stringify(rpmLatest, null, 2));
console.log('\x1b[32m%s\x1b[0m', `已生成 rpm-latest.json 文件`);
}
// Tauri 构建成功后执行自动部署
console.log('\x1b[36m%s\x1b[0m', '开始执行自动部署...');
try {
const autoDeployScriptPath = join('.', 'vite', 'scripts', 'auto-deply.ts');
execSync(`npx tsx "${autoDeployScriptPath}"`, { stdio: 'inherit' });
console.log('\x1b[32m%s\x1b[0m', '自动部署执行完成');
} catch (error: any) {
console.error('\x1b[31m%s\x1b[0m', '自动部署执行失败:', error.message);
// 注意:这里不退出进程,因为构建本身是成功的
}
} else {
console.error('\x1b[31m%s\x1b[0m', `Tauri 构建失败,退出码: ${tauriCode}`);
process.exit(tauriCode);
}
});
});
});
/**
* 得到最新版本信息
* @param ostype - 平台类型,如 'windows'、'macos'、'linux'
* @param version - 新版本号
* @param catalog - 包的目录名
* @param extension - 包的扩展名
*/
interface LatestJson {
version: string;
pub_date: string;
url: string;
signature: string;
notes: string;
downloadLink?: string;
}
/**
* 获取最新版本信息
* @param ostype - 平台类型,如 'windows'、'macos'、'linux'
* @param version - 新版本号
* @param catalog - 包的目录名
* @param extension - 包的扩展名
*/
function getLatestJson(ostype: string, version: string, catalog: string, extension: string): LatestJson {
const pubDate = new Date().toISOString();
const latestJson: LatestJson = {
version,
pub_date: pubDate,
url: '',
signature: '',
notes: '1. 修复BUG \n 2. 优化部分功能',
};
const baseUrl = `https://file.qyzhjy.com/app/zpkt_desktop_app/${ostype}`;
try {
if (ostype === 'windows') {
const nsisDir = join(bundleDir, catalog);
const files = readdirSync(nsisDir);
const exeFile = files.find((file) => file.endsWith('exe'));
latestJson.downloadLink = `${baseUrl}/${catalog}/${exeFile}`;
} else if (ostype === 'macos') {
const dmgDir = join(bundleDir, 'dmg');
const files = readdirSync(dmgDir);
const dmgFile = files.find((file) => file.endsWith('dmg'));
latestJson.downloadLink = `${baseUrl}/dmg/${dmgFile}`;
} else if (ostype === 'linux') {
const appImageDir = join(bundleDir, catalog);
const files = readdirSync(appImageDir);
const appImageFile = files.find((file) => file.endsWith(extension));
latestJson.downloadLink = `${baseUrl}/${catalog}/${appImageFile}`;
}
// 查找 nsis 目录下的 exe 文件
const nsisDir = join(bundleDir, catalog);
if (existsSync(nsisDir)) {
const files = readdirSync(nsisDir);
const exeFile = files.find((file) => file.endsWith(extension));
const sigFile = files.find((file) => file.endsWith(`${extension}.sig`));
if (exeFile) {
// 设置 Windows 平台的 URL
latestJson.url = `${baseUrl}/${catalog}/${exeFile}`;
// 如果存在对应的 .sig 文件,读取签名内容
if (sigFile) {
const sigFilePath = join(nsisDir, sigFile);
latestJson.signature = readFileSync(sigFilePath, 'utf8').trim();
}
}
}
return latestJson;
} catch (error: any) {
console.warn('\x1b[33m%s\x1b[0m', '警告: 无法读取 Windows 安装包信息:', error.message);
return latestJson;
}
}