前端工程化:基于Node.js的自动化版本管理与发布说明生成工具

本文介绍一个用于自动化管理项目版本号和生成发布说明的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"
  }
}
相关推荐
Healer9182 小时前
纯css实现高度0-auto动画过度interpolate-size 和 height: calc-size(auto,size)
前端
智慧源点2 小时前
解决 Vite + React 项目部署 GitHub Pages 的完整指南:从 404 到成功部署
前端·react.js·github
葡萄城技术团队3 小时前
浏览器端音视频处理新选择:Mediabunny 让 Web 媒体开发飞起来
前端·音视频·媒体
FogLetter3 小时前
深入浅出 JavaScript 闭包:从背包理论到实战应用
前端·javascript
前端大卫3 小时前
表单与上传组件校验
前端·javascript·vue.js
伊织code3 小时前
Cap‘n Web - JavaScript原生RPC系统
前端·javascript·rpc
周尛先森4 小时前
匠心管控 package.json:让前端依赖告别臃肿与混乱
前端
90后的晨仔4 小时前
Vue3 事件处理详解:从入门到精通
前端·vue.js
西洼工作室4 小时前
设计模式与原则精要
前端·javascript·设计模式·vue