@plasosdk/client-sdk
Version:
Plaso教育平台的客户端SDK
312 lines (273 loc) • 9.76 kB
JavaScript
/**
* Plaso SDK 发布脚本
*
* 功能:
* - 自动升级版本号(major/minor/patch)
* - 检查登录状态
* - 发布包
*
* 使用方法:
* - 基本用法: npm run release
* (无参数时会进入交互模式,询问是否跳过版本升级)
* - 跳过版本升级(推荐): npm run release-skip
* - 跳过版本升级(手动传参): npm run release -- --skip-version-upgrade
* - 跳过版本升级(简写): npm run release -- -s
*
* 注意:
* - 使用跳过版本选项时,会检查当前版本是否已发布,如已发布会提示确认
* - 如果您手动修改了版本号,建议使用跳过版本升级选项
* - 此脚本假设您已经手动设置了正确的npm源,不会自动切换源
*/
const { execSync } = require('child_process');
const readline = require('readline');
const fs = require('fs');
const path = require('path');
// 创建readline接口
const rl = readline.createInterface({
input: process.stdin,
output: process.stdout
});
// 添加跨平台休眠函数
function sleep(seconds) {
// 根据操作系统选择合适的休眠方法
const isWindows = process.platform === 'win32';
try {
if (isWindows) {
// Windows 使用 timeout 命令
execSync(`timeout /t ${seconds}`, { stdio: 'ignore' });
} else {
// Unix 系统使用 sleep 命令
execSync(`sleep ${seconds}`, { stdio: 'ignore' });
}
} catch (error) {
// 如果命令执行失败,使用JavaScript的setTimeout作为备选
console.log(`系统休眠命令失败,使用JavaScript休眠 ${seconds} 秒`);
const start = Date.now();
while (Date.now() - start < seconds * 1000) {
// 空循环等待
}
}
}
// 执行命令的函数
function executeCommand(command) {
try {
execSync(command, { stdio: 'inherit' });
return true;
} catch (error) {
// 检查是否是版本已存在的错误
if (error.message.includes('You cannot publish over the previously published versions')) {
console.log('\n该版本已经发布过了,无需重复发布');
return true;
}
console.error(`执行命令失败: ${command}`);
console.error(error.message);
return false;
}
}
// 检查npm登录状态
function checkNpmLoginStatus() {
try {
// 直接使用npm命令而不是npx
const output = execSync('npm whoami', { encoding: 'utf8' });
const username = output.trim();
if (username.length > 0) {
console.log(`当前登录用户: ${username}`);
return true;
}
return false;
} catch (error) {
console.log('npm登录状态检查失败,您可能尚未登录');
return false;
}
}
// 确保npm登录
async function ensureNpmLogin() {
console.log('\n检查npm登录状态...');
// 检查登录状态
const isLoggedIn = checkNpmLoginStatus();
if (!isLoggedIn) {
console.log('未登录,开始登录流程...');
// 登录命令
const loginCmd = 'npm login';
if (!executeCommand(loginCmd)) {
throw new Error('npm登录失败');
}
// 再次检查登录状态
if (!checkNpmLoginStatus()) {
throw new Error('登录失败,请重试');
}
console.log('登录成功!');
return true;
} else {
console.log('已经登录,跳过登录步骤');
return true;
}
}
// 提示用户输入
function prompt(question) {
return new Promise((resolve) => {
rl.question(question, (answer) => {
resolve(answer);
});
});
}
// 读取package.json文件
function readPackageJson() {
const packagePath = path.join(process.cwd(), 'package.json');
const packageContent = fs.readFileSync(packagePath, 'utf8');
return JSON.parse(packageContent);
}
// 写入package.json文件
function writePackageJson(packageJson) {
const packagePath = path.join(process.cwd(), 'package.json');
fs.writeFileSync(packagePath, JSON.stringify(packageJson, null, 2) + '\n');
}
// 升级版本号
function upgradeVersion(currentVersion, type) {
const [major, minor, patch] = currentVersion.split('.').map(Number);
switch (type) {
case 'major':
return `${major + 1}.0.0`;
case 'minor':
return `${major}.${minor + 1}.0`;
case 'patch':
default:
return `${major}.${minor}.${patch + 1}`;
}
}
// 检查版本是否已发布
async function checkVersionExists(packageName, version) {
try {
console.log(`检查版本 ${packageName}@${version} 是否已发布...`);
const cmd = `npx npm view ${packageName}@${version} version --json`;
try {
execSync(cmd, { stdio: 'ignore' });
console.log(`版本 ${version} 已存在于npm仓库`);
return true;
} catch (e) {
// 如果命令执行失败,通常意味着版本不存在
console.log(`版本 ${version} 不存在于npm仓库,可以发布`);
return false;
}
} catch (error) {
console.error('检查版本失败:', error.message);
// 发生错误时,默认允许继续(让npm publish时再判断)
return false;
}
}
// 解析命令行参数
function parseArgs() {
const args = process.argv.slice(2);
const options = {
skipVersionUpgrade: false,
interactive: args.length === 0 // 如果没有传入参数,则启用交互模式
};
for (let i = 0; i < args.length; i++) {
const arg = args[i];
if (arg === '--skip-version-upgrade' || arg === '-s') {
options.skipVersionUpgrade = true;
}
}
return options;
}
async function main() {
// 解析命令行参数
const options = parseArgs();
try {
console.log('开始发布SDK...');
// 交互模式:如果没有传入命令行参数,则询问是否要跳过版本升级
if (options.interactive) {
const skipVersionAnswer = await prompt('是否跳过版本升级? (y/n): ');
if (skipVersionAnswer.toLowerCase() === 'y') {
options.skipVersionUpgrade = true;
console.log('已选择跳过版本升级');
}
}
// 0. 升级版本号
console.log('\n0. 升级版本号...');
const packageJson = readPackageJson();
const currentVersion = packageJson.version;
let newVersion;
// 如果命令行参数指定跳过版本升级
if (options.skipVersionUpgrade) {
console.log('检测到--skip-version-upgrade参数,跳过版本升级');
newVersion = currentVersion;
} else {
console.log(`当前版本: ${currentVersion}`);
console.log('请选择版本升级类型:');
console.log('1. major - 主版本号升级 (不兼容的API变更)');
console.log('2. minor - 次版本号升级 (向后兼容的功能性新增)');
console.log('3. patch - 修订号升级 (向后兼容的问题修正)');
console.log('4. skip - 跳过版本升级,保持当前版本');
const versionType = await prompt('请输入选项 (1/2/3/4,默认为3): ');
let type;
switch (versionType.trim()) {
case '1':
type = 'major';
newVersion = upgradeVersion(currentVersion, type);
break;
case '2':
type = 'minor';
newVersion = upgradeVersion(currentVersion, type);
break;
case '4':
console.log('已选择跳过版本升级');
newVersion = currentVersion;
break;
case '3':
default:
type = 'patch';
newVersion = upgradeVersion(currentVersion, type);
break;
}
}
console.log(`版本: ${newVersion}`);
// 如果版本没有变化,直接询问是否确认发布
const confirmMessage = newVersion === currentVersion
? `确认发布当前版本 ${currentVersion}? (y/n): `
: `确认将版本从 ${currentVersion} 升级到 ${newVersion}? (y/n): `;
const confirm = await prompt(confirmMessage);
if (confirm.toLowerCase() !== 'y') {
console.log('已取消发布');
return;
}
// 只有当版本变化时才写入package.json
if (newVersion !== currentVersion) {
packageJson.version = newVersion;
writePackageJson(packageJson);
console.log(`版本已更新为 ${newVersion}`);
} else {
console.log(`继续使用当前版本 ${currentVersion}`);
// 当用户选择继续使用当前版本时,检查该版本是否已发布
const versionExists = await checkVersionExists(packageJson.name, currentVersion);
if (versionExists) {
console.log(`\n警告: 版本 ${currentVersion} 已经发布,继续操作会失败。`);
const forceContinue = await prompt('是否仍要继续? (y/n): ');
if (forceContinue.toLowerCase() !== 'y') {
console.log('已取消发布');
return;
}
console.log('用户选择强制继续,可能会在发布时报错');
}
}
// 1. 确保登录
console.log('\n1. 检查登录状态...');
if (!await ensureNpmLogin()) {
throw new Error('登录失败');
}
// 2. 发布包
console.log('\n2. 开始发布包...');
const publishCmd = 'npm publish --ignore-scripts';
if (!executeCommand(publishCmd)) {
throw new Error('发布包失败');
}
console.log(`\n恭喜!${packageJson.name}@${newVersion} 已成功发布到npm!`);
} catch (error) {
console.error('\n发布失败:', error.message);
process.exit(1);
} finally {
rl.close();
}
}
main();