15 KiB
15 KiB
发布脚本使用说明
📋 概述
发布脚本 (scripts/publish.js) 用于自动化应用的构建和发布流程,包括:
- 清理构建目录
- 构建应用(NSIS 或便携版)
- 调用接口创建版本记录
- 上传压缩包到 OSS
🚀 快速开始
安装依赖
确保已安装 form-data 依赖:
npm install form-data --save-dev
基本使用
# 发布 NSIS 安装包(默认)
npm run publish
# 或直接运行脚本
node scripts/publish.js
📝 命令选项
构建类型
# 发布 NSIS 安装包
npm run publish:nsis
# 或
node scripts/publish.js --type nsis
# 发布便携版
npm run publish:portable
# 或
node scripts/publish.js --type portable
发布说明
node scripts/publish.js --notes "修复了若干bug,优化了性能"
强制更新
node scripts/publish.js --force
跳过步骤
# 仅上传已构建的文件(跳过构建)
node scripts/publish.js --skip-build
# 仅构建不上传
node scripts/publish.js --skip-upload
查看帮助
node scripts/publish.js --help
🔧 配置要求
1. 配置文件
确保 config/appConfig.js 中包含有效的配置:
module.exports = {
api_urls: {
dev: "http://work.light120.com/api",
prod: "http://work.light120.com/api"
},
token: "your-token-here", // 必须配置有效的 token
// ...
};
2. API 接口
脚本需要以下 API 接口:
接口 1:创建版本记录
- 接口地址:
POST /api/version/create - 请求头:
Content-Type: application/json Authorization: Bearer ${token} - 请求参数:
{ "version": "1.0.0", // 必填:版本号(x.y.z 格式) "platform": "win32", // 必填:平台类型(win32/darwin/linux) "arch": "x64", // 必填:架构类型(x64/ia32/arm64) "download_url": "https://...", // 必填:下载地址(上传后更新) "file_path": "/path/to/file", // 必填:服务器文件路径 "file_size": 12345678, // 可选:文件大小(字节),不提供会自动计算 "file_hash": "sha256-hash", // 可选:SHA256 哈希值,不提供会自动计算 "release_notes": "发布说明", // 可选:更新日志 "force_update": 0, // 可选:是否强制更新(1:是 0:否),默认 0 "status": 1 // 可选:状态(1:启用 0:禁用),默认 1 } - 响应示例:
{ "code": 0, "message": "版本创建成功", "data": { "id": 123, "version": "1.0.0", "platform": "win32", "arch": "x64", "download_url": "https://oss.example.com/path/to/file.exe", "file_path": "/path/to/file.exe", "file_size": 12345678, "file_hash": "sha256-hash-value", "release_notes": "发布说明", "force_update": 0, "status": 1, "create_time": "2024-01-01 12:00:00" } } - 错误响应:
{ "code": 400, "message": "版本号不能为空" // 或其他错误信息 } - 注意事项:
- 版本号格式必须为
x.y.z(如:1.0.0) - 平台类型必须为:
win32、darwin或linux - 架构类型必须为:
x64、ia32或arm64 - 如果版本已存在(相同 version + platform + arch),会返回错误
- 如果提供了
file_path但未提供file_size或file_hash,接口会自动计算
- 版本号格式必须为
接口 2:上传文件到 OSS
- 接口地址:
POST /api/file/upload_version - 请求头:
Content-Type: multipart/form-data Authorization: Bearer ${token} - 请求参数(Form Data):
参数名 类型 必填 说明 fileFile 是 文件内容(二进制) versionString 是 版本号(如:1.0.0) platformString 是 平台类型(win32/darwin/linux) archString 是 架构类型(x64/ia32/arm64) file_hashString 是 SHA256 哈希值 file_sizeNumber 是 文件大小(字节) version_idNumber 否 版本记录 ID(如果已创建版本记录) build_typeString 否 构建类型(nsis/portable) - 请求示例(使用 form-data):
const FormData = require('form-data'); const fs = require('fs'); const form = new FormData(); form.append('file', fs.createReadStream('./dist/app.exe')); form.append('version', '1.0.0'); form.append('platform', 'win32'); form.append('arch', 'x64'); form.append('file_hash', 'sha256-hash-value'); form.append('file_size', 12345678); form.append('version_id', 123); // 可选 // 发送请求 form.submit('http://api.example.com/api/file/upload_version', { headers: { 'Authorization': `Bearer ${token}` } }, callback); - 响应示例:
{ "code": 0, "message": "文件上传成功", "data": { "download_url": "https://oss.example.com/versions/win32/x64/app-1.0.0.exe", "file_path": "versions/win32/x64/app-1.0.0.exe", "oss_path": "https://oss.example.com/versions/win32/x64/app-1.0.0.exe", "file_size": 12345678, "file_hash": "sha256-hash-value" } } - 错误响应:
{ "code": 400, "message": "文件上传失败:文件大小不匹配" // 或其他错误信息 } - 注意事项:
- 文件会按照
versions/{platform}/{arch}/{filename}的路径结构上传到 OSS - 上传前会验证文件哈希值,确保文件完整性
- 上传成功后,建议调用
/version/update接口更新版本记录的下载地址 - 大文件上传建议设置较长的超时时间(建议 10 分钟以上)
- 文件会按照
接口 3:更新版本下载地址(可选)
- 接口地址:
POST /api/version/update - 请求头:
Content-Type: application/json Authorization: Bearer ${token} - 请求参数:
{ "id": 123, // 必填:版本记录 ID "download_url": "https://...", // 可选:下载地址 "file_path": "/path/to/file", // 可选:文件路径 "file_hash": "sha256-hash", // 可选:文件哈希值 "file_size": 12345678, // 可选:文件大小 "release_notes": "更新说明", // 可选:更新日志 "force_update": 1, // 可选:是否强制更新 "status": 1 // 可选:状态 } - 响应示例:
{ "code": 0, "message": "版本更新成功", "data": { "id": 123, "version": "1.0.0", "download_url": "https://oss.example.com/path/to/file.exe", // ... 其他字段 } } - 使用场景:
- 文件上传成功后,更新版本记录的下载地址
- 修改版本的发布说明或强制更新标志
- 启用或禁用某个版本
📦 发布流程
-
清理构建目录
- 删除
dist目录及其所有内容
- 删除
-
构建应用
- 根据构建类型执行
electron-builder - NSIS:
electron-builder --win nsis - Portable:
electron-builder --win portable
- 根据构建类型执行
-
查找构建产物
- 在
dist目录中查找.exe文件 - 按文件大小排序,优先处理主安装包
- 在
-
创建版本记录
- 调用
POST /api/version/create接口 - 传递版本信息、平台、架构等
- 获取版本记录 ID(用于后续更新)
- 调用
-
上传文件到 OSS
- 计算文件 SHA256 哈希值
- 使用
multipart/form-data格式调用POST /api/file/upload_version接口 - 传递文件、版本信息、哈希值等参数
- 获取上传后的下载地址
-
更新版本记录
- 使用获取到的下载地址调用
POST /api/version/update接口 - 更新版本记录的
download_url和file_path字段
- 使用获取到的下载地址调用
⚠️ 注意事项
-
Token 配置
- 确保
config/appConfig.js中有有效的token - Token 用于 API 认证,格式为
Bearer ${token} - 所有接口请求都需要在请求头中包含
Authorization: Bearer ${token}
- 确保
-
接口路径
- 所有接口路径前缀为
/api - 完整路径示例:
- 创建版本:
POST http://api.example.com/api/version/create - 上传文件:
POST http://api.example.com/api/file/upload_version - 更新版本:
POST http://api.example.com/api/version/update
- 创建版本:
- 所有接口路径前缀为
-
文件大小
- 大文件上传可能需要较长时间
- 脚本默认超时时间为 10 分钟(600000ms)
- 建议大文件(>100MB)增加超时时间到 30 分钟
-
网络连接
- 确保能够访问 API 服务器和 OSS
- 上传大文件时建议使用稳定的网络连接
- 建议在网络稳定的环境下执行发布流程
-
版本号
- 版本号从
package.json的version字段读取 - 发布前确保版本号已更新
- 版本号格式必须符合
x.y.z格式(如:1.0.0)
- 版本号从
-
构建产物
- 脚本会自动查找
dist目录中的.exe文件 - 排除
builder和helper相关的辅助文件 - 确保构建产物文件名清晰,便于识别
- 脚本会自动查找
-
文件哈希验证
- 上传前必须计算文件的 SHA256 哈希值
- 上传接口会验证文件哈希,确保文件完整性
- 哈希值不匹配会导致上传失败
-
接口调用顺序
- 建议先创建版本记录(获取 version_id)
- 然后上传文件(可传递 version_id)
- 最后更新版本记录的下载地址
- 也可以先上传文件,再创建版本记录并更新下载地址
🔍 故障排查
问题:上传失败
可能原因:
- Token 无效或过期
- API 接口地址不正确
- 网络连接问题
- 文件过大,超时
解决方法:
- 检查
config/appConfig.js中的token是否有效 - 检查 API 地址是否正确
- 检查网络连接
- 如果文件很大,可以增加超时时间
问题:构建失败
可能原因:
- 缺少依赖
- electron-builder 配置错误
- 磁盘空间不足
解决方法:
- 运行
npm install安装所有依赖 - 检查
package.json中的build配置 - 确保有足够的磁盘空间
问题:找不到构建产物
可能原因:
- 构建未成功完成
- 构建产物在其他位置
解决方法:
- 检查构建日志,确认构建成功
- 手动检查
dist目录 - 使用
--skip-build选项,手动指定文件路径(需要修改脚本)
📝 示例
完整发布流程
# 1. 更新版本号(在 package.json 中)
# "version": "1.0.1"
# 2. 发布
npm run publish -- --notes "新功能:支持自动投递简历" --force
仅上传已构建的文件
# 1. 手动构建
npm run build:nsis
# 2. 仅上传
node scripts/publish.js --skip-build
测试发布(不上传)
# 构建并创建版本记录,但不上传文件
node scripts/publish.js --skip-upload
💻 接口调用示例
Node.js 示例代码
1. 创建版本记录
const axios = require('axios');
const crypto = require('crypto');
const fs = require('fs');
async function createVersion() {
const filePath = './dist/app-1.0.0.exe';
const stats = fs.statSync(filePath);
const fileSize = stats.size;
// 计算文件哈希
const fileBuffer = fs.readFileSync(filePath);
const fileHash = crypto.createHash('sha256').update(fileBuffer).digest('hex');
const response = await axios.post('http://api.example.com/api/version/create', {
version: '1.0.0',
platform: 'win32',
arch: 'x64',
download_url: '', // 上传后更新
file_path: filePath,
file_size: fileSize,
file_hash: fileHash,
release_notes: '修复了若干bug,优化了性能',
force_update: 0,
status: 1
}, {
headers: {
'Authorization': `Bearer ${token}`,
'Content-Type': 'application/json'
}
});
return response.data.data; // 返回版本记录,包含 id
}
2. 上传文件到 OSS
const FormData = require('form-data');
const axios = require('axios');
const fs = require('fs');
const crypto = require('crypto');
async function uploadVersionFile(versionId) {
const filePath = './dist/app-1.0.0.exe';
const stats = fs.statSync(filePath);
const fileSize = stats.size;
// 计算文件哈希
const fileBuffer = fs.readFileSync(filePath);
const fileHash = crypto.createHash('sha256').update(fileBuffer).digest('hex');
// 创建 FormData
const form = new FormData();
form.append('file', fs.createReadStream(filePath));
form.append('version', '1.0.0');
form.append('platform', 'win32');
form.append('arch', 'x64');
form.append('file_hash', fileHash);
form.append('file_size', fileSize);
if (versionId) {
form.append('version_id', versionId);
}
const response = await axios.post('http://api.example.com/api/file/upload_version', form, {
headers: {
'Authorization': `Bearer ${token}`,
...form.getHeaders()
},
maxContentLength: Infinity,
maxBodyLength: Infinity,
timeout: 600000 // 10 分钟超时
});
return response.data.data; // 返回上传结果,包含 download_url
}
3. 更新版本下载地址
async function updateVersionDownloadUrl(versionId, downloadUrl, fileHash) {
const response = await axios.post('http://api.example.com/api/version/update', {
id: versionId,
download_url: downloadUrl,
file_hash: fileHash
}, {
headers: {
'Authorization': `Bearer ${token}`,
'Content-Type': 'application/json'
}
});
return response.data.data;
}
4. 完整发布流程示例
async function publishVersion() {
try {
// 1. 创建版本记录
console.log('创建版本记录...');
const version = await createVersion();
console.log('版本记录创建成功,ID:', version.id);
// 2. 上传文件
console.log('上传文件到 OSS...');
const uploadResult = await uploadVersionFile(version.id);
console.log('文件上传成功,下载地址:', uploadResult.download_url);
// 3. 更新版本下载地址
console.log('更新版本下载地址...');
await updateVersionDownloadUrl(version.id, uploadResult.download_url, uploadResult.file_hash);
console.log('版本发布完成!');
} catch (error) {
console.error('发布失败:', error.response?.data || error.message);
throw error;
}
}
cURL 示例
创建版本记录
curl -X POST http://api.example.com/api/version/create \
-H "Authorization: Bearer your-token-here" \
-H "Content-Type: application/json" \
-d '{
"version": "1.0.0",
"platform": "win32",
"arch": "x64",
"download_url": "",
"file_path": "/path/to/file.exe",
"file_size": 12345678,
"file_hash": "sha256-hash-value",
"release_notes": "发布说明",
"force_update": 0,
"status": 1
}'
上传文件
curl -X POST http://api.example.com/api/file/upload_version \
-H "Authorization: Bearer your-token-here" \
-F "file=@./dist/app-1.0.0.exe" \
-F "version=1.0.0" \
-F "platform=win32" \
-F "arch=x64" \
-F "file_hash=sha256-hash-value" \
-F "file_size=12345678" \
-F "version_id=123"