本文介绍一个用于自动化管理项目版本号和生成发布说明的Node.js脚本工具。该工具可以帮助开发者在构建过程中自动更新多个配置文件中的版本信息,并交互式地生成发布说明文档。
核心功能
1. 版本号管理
- 自动更新
package.json
中的版本号 - 同步更新应用配置文件中的版本信息
- 支持通过环境变量指定版本号
2. 发布说明生成
- 交互式命令行输入发布说明内容
- 自动生成版本对应的Markdown文档
- 支持多行输入,空行结束输入
3. 增量更新支持
- 生成唯一的增量更新标识符
- 适用于热更新或增量发布场景
核心代码解析
交互式输入函数
javascript
async function prompt(query, requisite = false) {
const rl = readline.createInterface({
input: process.stdin,
output: process.stdout
})
console.log(`\x1b[36m🤖 ${query}`)
console.log("\x1b[33m (input an empty line to end the input, press Ctrl+C to exit) \x1b[0m")
const lines = []
return new Promise((resolve) => {
const recursiveRead = () => {
rl.question("release text: ", (answer) => {
if (answer.trim() === "") {
rl.close()
resolve(lines.join("\n"))
} else {
lines.push(answer)
recursiveRead()
}
})
}
recursiveRead()
rl.on("close", () => {
if (lines.length === 0) {
if (requisite) {
throw new Error("Please provide the updated version text.")
} else {
console.log("\n输入已取消")
resolve("")
}
}
})
})
}
使用示例
示例1:标准版本发布流程
javascript
const fs = require("fs")
const path = require("path")
const readline = require("readline")
// 设置版本号和环境
process.env.NODE_ENV = "production"
process.env.npm_config_setv = "1.2.0"
async function standardRelease() {
try {
console.log("🚀 开始标准版本发布流程")
// 更新package.json并生成发布说明
await updatePackageJson("1.2.0")
// 更新其他配置文件
updateConfigFiles("1.2.0")
console.log("✅ 版本发布准备完成")
} catch (error) {
console.error("❌ 发布流程出错:", error.message)
}
}
async function updatePackageJson(version) {
const packageJsonPath = path.join(process.cwd(), "package.json")
const packageJson = JSON.parse(fs.readFileSync(packageJsonPath, "utf-8"))
// 更新版本号
packageJson.version = version
// 生成发布说明文件
const releaseNotesFile = `docs/releases/v${version}.md`
const releaseNotes = await prompt(`请输入版本 v${version} 的发布说明:`, true)
// 确保目录存在
const dir = path.dirname(releaseNotesFile)
if (!fs.existsSync(dir)) {
fs.mkdirSync(dir, { recursive: true })
}
// 写入发布说明
fs.writeFileSync(releaseNotesFile, `# 版本 v${version} 发布说明\n\n${releaseNotes}`)
// 更新package.json中的发布信息
packageJson.releaseNotes = releaseNotesFile
packageJson.buildDate = new Date().toISOString()
// 保存更新后的package.json
fs.writeFileSync(packageJsonPath, JSON.stringify(packageJson, null, 2))
console.log(`📝 已更新 package.json 版本为 v${version}`)
}
function updateConfigFiles(version) {
// 更新应用配置文件
const configFiles = [
"src/config/app.json",
"app.config.js"
]
configFiles.forEach(configFile => {
if (fs.existsSync(configFile)) {
try {
const content = fs.readFileSync(configFile, "utf-8")
const updatedContent = content.replace(
/("version":\s*")[^"]*"/,
`$1${version}"`
)
fs.writeFileSync(configFile, updatedContent)
console.log(`✅ 已更新 ${configFile} 版本信息`)
} catch (error) {
console.log(`⚠️ ${configFile} 更新跳过:`, error.message)
}
}
})
}
// 执行发布流程
standardRelease()
示例2:增量热更新流程
javascript
const fs = require("fs")
const path = require("path")
// 增量更新配置
process.env.NODE_ENV = "production"
process.env.npm_config_delta = "true"
async function incrementalUpdate() {
try {
console.log("🔄 开始增量更新流程")
const baseVersion = "1.2.0"
const incrementalId = generateIncrementalId()
const fullVersion = `${baseVersion}+${incrementalId}`
console.log(`📦 增量更新ID: ${incrementalId}`)
console.log(`🔢 完整版本号: ${fullVersion}`)
// 更新版本信息
await updateVersionInfo(baseVersion, incrementalId)
// 生成增量更新说明
await generateDeltaReleaseNotes(baseVersion, incrementalId)
// 创建更新清单
createUpdateManifest(baseVersion, incrementalId)
console.log("✅ 增量更新准备完成")
} catch (error) {
console.error("❌ 增量更新出错:", error.message)
}
}
function generateIncrementalId() {
const chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"
return Array.from({ length: 6 }, () =>
chars[Math.floor(Math.random() * chars.length)]
).join('')
}
async function updateVersionInfo(baseVersion, incrementalId) {
const packageJsonPath = path.join(process.cwd(), "package.json")
const packageJson = JSON.parse(fs.readFileSync(packageJsonPath, "utf-8"))
// 更新版本信息
packageJson.version = baseVersion
packageJson.incrementalId = incrementalId
packageJson.fullVersion = `${baseVersion}+${incrementalId}`
packageJson.buildInfo = {
type: "incremental",
id: incrementalId,
timestamp: new Date().toISOString(),
baseVersion: baseVersion
}
fs.writeFileSync(packageJsonPath, JSON.stringify(packageJson, null, 2))
}
async function generateDeltaReleaseNotes(baseVersion, incrementalId) {
const changes = await prompt("请描述本次增量更新的具体变更内容:", true)
const releaseNotes = {
version: baseVersion,
incrementalId: incrementalId,
timestamp: new Date().toISOString(),
changes: changes.split('\n').filter(line => line.trim()),
type: "hotfix"
}
const notesPath = path.join(process.cwd(), `delta-updates/${incrementalId}.json`)
const dir = path.dirname(notesPath)
if (!fs.existsSync(dir)) {
fs.mkdirSync(dir, { recursive: true })
}
fs.writeFileSync(notesPath, JSON.stringify(releaseNotes, null, 2))
console.log(`📋 增量更新说明已保存: ${notesPath}`)
}
function createUpdateManifest(baseVersion, incrementalId) {
const manifest = {
updateId: incrementalId,
baseVersion: baseVersion,
timestamp: new Date().toISOString(),
files: [
"dist/bundle.js",
"dist/styles.css"
],
requirements: {
minVersion: "1.0.0",
maxVersion: baseVersion
}
}
const manifestPath = path.join(process.cwd(), `update-manifests/${incrementalId}.json`)
const dir = path.dirname(manifestPath)
if (!fs.existsSync(dir)) {
fs.mkdirSync(dir, { recursive: true })
}
fs.writeFileSync(manifestPath, JSON.stringify(manifest, null, 2))
console.log(`📄 更新清单已创建: ${manifestPath}`)
}
// 执行增量更新流程
incrementalUpdate()
运行命令
bash
# 标准发布
npm run build --setv=1.2.0
# 增量更新
npm run build --delta
# 开发环境
npm run dev
配置文件示例
package.json 配置
json
{
"name": "your-project",
"version": "1.2.0",
"build": {
"releaseInfo": {
"releaseNotesFile": "docs/releases/v1.2.0.md",
"releaseName": "feature-release"
}
},
"scripts": {
"build": "node build-script.js",
"dev": "NODE_ENV=development node build-script.js"
}
}