Skip to main content
Glama
publish.js7.79 kB
#!/usr/bin/env node /** * NPM包发布脚本 * 自动化版本管理和发布流程 */ const fs = require('fs'); const path = require('path'); const { execSync } = require('child_process'); const chalk = require('chalk'); // 日志函数 const log = { info: (msg) => console.log(chalk.blue('ℹ'), msg), success: (msg) => console.log(chalk.green('✅'), msg), warning: (msg) => console.log(chalk.yellow('⚠️'), msg), error: (msg) => console.log(chalk.red('❌'), msg), step: (msg) => console.log(chalk.cyan('🔄'), msg) }; // 执行命令 function runCommand(command, description) { log.step(description); try { const result = execSync(command, { encoding: 'utf8', stdio: 'pipe' }); log.success(`${description} - 完成`); return result; } catch (error) { log.error(`${description} - 失败: ${error.message}`); throw error; } } // 检查文件是否存在 function checkFile(filePath, description) { if (fs.existsSync(filePath)) { log.success(`${description} - 存在`); return true; } else { log.error(`${description} - 不存在: ${filePath}`); return false; } } // 主发布流程 async function publishPackage() { log.info('开始NPM包发布流程...'); try { // 1. 检查必要文件 log.step('检查必要文件...'); const requiredFiles = [ { path: 'package.json', desc: 'package.json' }, { path: 'README.md', desc: 'README.md' }, { path: 'LICENSE', desc: 'LICENSE' }, { path: 'index.js', desc: '主入口文件' }, { path: 'bin/remote-terminal-mcp', desc: '可执行文件' }, { path: 'python/mcp_server.py', desc: 'Python MCP服务器' }, { path: 'docker_config_manager.py', desc: 'Docker配置管理器' }, { path: 'enhanced_config_manager.py', desc: '增强配置管理器' } ]; let allFilesExist = true; for (const file of requiredFiles) { if (!checkFile(file.path, file.desc)) { allFilesExist = false; } } if (!allFilesExist) { throw new Error('缺少必要文件,无法发布'); } // 2. 检查package.json log.step('验证package.json...'); const packageJson = JSON.parse(fs.readFileSync('package.json', 'utf8')); log.info(`包名: ${packageJson.name}`); log.info(`版本: ${packageJson.version}`); log.info(`描述: ${packageJson.description}`); // 3. 检查Git状态 log.step('检查Git状态...'); try { const gitStatus = runCommand('git status --porcelain', '检查Git工作区状态'); if (gitStatus.trim()) { log.warning('工作区有未提交的更改:'); console.log(gitStatus); // 询问是否继续 const readline = require('readline'); const rl = readline.createInterface({ input: process.stdin, output: process.stdout }); const answer = await new Promise((resolve) => { rl.question('是否继续发布? (y/N): ', resolve); }); rl.close(); if (answer.toLowerCase() !== 'y') { log.info('发布已取消'); return; } } } catch (error) { log.warning('无法检查Git状态,可能不在Git仓库中'); } // 4. 运行测试 log.step('运行测试...'); try { runCommand('npm test', '运行测试套件'); } catch (error) { log.warning('测试失败,但继续发布流程'); } // 5. 构建包 log.step('构建NPM包...'); runCommand('npm pack', '创建NPM包'); // 6. 检查包内容 log.step('检查包内容...'); const tarballName = `${packageJson.name.replace('@', '').replace('/', '-')}-${packageJson.version}.tgz`; if (fs.existsSync(tarballName)) { log.success(`包文件已创建: ${tarballName}`); // 显示包大小 const stats = fs.statSync(tarballName); const sizeInMB = (stats.size / (1024 * 1024)).toFixed(2); log.info(`包大小: ${sizeInMB} MB`); } // 7. 发布到NPM log.step('发布到NPM...'); // 检查是否已登录NPM try { const whoami = runCommand('npm whoami', '检查NPM登录状态'); log.info(`当前NPM用户: ${whoami.trim()}`); } catch (error) { log.error('未登录NPM,请先运行: npm login'); return; } // 发布包 try { runCommand('npm publish --access public', '发布包到NPM'); log.success(`🎉 包 ${packageJson.name}@${packageJson.version} 发布成功!`); } catch (error) { if (error.message.includes('already exists')) { log.warning('该版本已存在,请更新版本号后重试'); } else { throw error; } } // 8. 清理临时文件 log.step('清理临时文件...'); if (fs.existsSync(tarballName)) { fs.unlinkSync(tarballName); log.success('临时文件已清理'); } // 9. 创建Git标签(如果在Git仓库中) try { const tagName = `v${packageJson.version}`; runCommand(`git tag ${tagName}`, `创建Git标签 ${tagName}`); runCommand(`git push origin ${tagName}`, '推送标签到远程仓库'); log.success(`Git标签 ${tagName} 已创建并推送`); } catch (error) { log.warning('无法创建Git标签,可能不在Git仓库中或标签已存在'); } log.success('🎉 发布流程完成!'); log.info(`安装命令: npm install ${packageJson.name}`); log.info(`使用命令: npx ${packageJson.name.split('/').pop()}`); } catch (error) { log.error(`发布失败: ${error.message}`); process.exit(1); } } // 版本管理函数 function updateVersion(type = 'patch') { log.step(`更新版本 (${type})...`); try { const result = runCommand(`npm version ${type} --no-git-tag-version`, `版本更新为 ${type}`); const newVersion = result.trim().replace('v', ''); log.success(`版本已更新为: ${newVersion}`); return newVersion; } catch (error) { log.error(`版本更新失败: ${error.message}`); throw error; } } // 命令行参数处理 const args = process.argv.slice(2); const command = args[0]; switch (command) { case 'patch': case 'minor': case 'major': updateVersion(command); break; case 'publish': publishPackage(); break; case 'version-and-publish': const versionType = args[1] || 'patch'; updateVersion(versionType); publishPackage(); break; default: console.log(` 使用方法: node scripts/publish.js patch # 更新补丁版本 node scripts/publish.js minor # 更新次要版本 node scripts/publish.js major # 更新主要版本 node scripts/publish.js publish # 发布当前版本 node scripts/publish.js version-and-publish [patch|minor|major] # 更新版本并发布 `); break; }

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/maricoxu/remote-terminal-mcp'

If you have feedback or need assistance with the MCP directory API, please join our Discord server