Parcourir la source

refactor(scripts): 重构发布和部署脚本并更新文档

- 删除冗余的 release-message.js 和 version-script.js
- 新增 deploy.js 实现自动化部署流程
- 优化 release.js 控制台输出信息
- 更新 package.json 中的脚本命令和依赖
- 重写 README.md 提供更简洁的使用说明
liujia il y a 2 mois
Parent
commit
9c046fc
7 fichiers modifiés avec 321 ajouts et 716 suppressions
  1. 29 88
      README.md
  2. 11 8
      package.json
  3. 183 335
      pnpm-lock.yaml
  4. 98 0
      scripts/deploy.js
  5. 0 17
      scripts/release-message.js
  6. 0 6
      scripts/release.js
  7. 0 262
      version-script.js

+ 29 - 88
README.md

@@ -1,108 +1,49 @@
 # 📖 项目脚本使用说明
 
 本项目在 `package.json` 中定义了一系列常用脚本,用于本地开发、构建、发布和部署。  
-以下是每个脚本的功能及使用方法。
+以下是脚本的功能使用说明:
 
 ---
 
 ## 📝 常用工作流
 
-**本地开发**  
+**🟢 开发**  
 ```bash
-npm install # 安装依赖
-npm run dev # 启动开发环境
+npm install           # 安装依赖
+npm run dev           # 启动开发环境
+npm run dev:test      # 启动测试环境
+npm run dev:pro       # 启动生产环境
 ```
 
-**打包并部署测试环境**
+**📦 构建**
 ```bash
-npm run deploy:test # 单机版环境
+npm run build:dev     # 构建开发环境
+npm run build:test    # 构建测试环境
+npm run build:prod    # 构建生产环境
+npm run build         # 构建生产环境
 ```
-
-**发布新版本**
+**📤 部署**
 ```bash
-npm run release # 发布新版本 + 生成发布日志
+npm run deploy        # 执行部署脚本
+npm run deploy:dev    # 部署开发环境
+npm run deploy:test   # 部署测试环境
+npm run deploy:prod   # 部署生产环境
 ```
 
-**同步分支代码**
+**🚀 发布版本**
 ```bash
-bash scripts/merge_dev_to_pro.sh # 同步dev分支到prod分支
+npm run release       # 发布版本并生成日志
 ```
 
----
-## 🎯 命令速查表
-
-| 环境 | 命令 | 端口 / 说明 |
-|------|------|------------|
-| 🟢 开发环境 | `npm run dev` | 3000 |
-| 🟡 测试环境 | `npm run dev:test` | 4000 |
-| 🔴 生产环境 | `npm run dev:pro` | 5000 |
-| 📦 构建开发环境 | `npm run build:dev` | - |
-| 📦 构建测试环境 | `npm run build:test` | - |
-| 📦 构建生产环境 | `npm run build` | - |
-| 🚀 发布版本 | `npm run release` | 发布版本 + 生成日志 |
-| 📤 部署测试环境 | `npm run deploy:test` | 打包 + 部署 |
-| 🔍 类型检查 | `npm run type-check` | TypeScript 检查 |
-| 🧹 代码修复 | `npm run lint` | ESLint 自动修复 |
-| 🎨 格式化代码 | `npm run format` | Prettier 格式化 |
-
-> 说明:目前分为开发、测试/单机版、生产/集成环境
----
-
-## 📦 开发相关
-
-| 命令 | 说明 | 运行示例 |
-|------|------|----------|
-| `npm run dev` | 启动开发环境(development),端口 3000 | `npm run dev` |
-| `npm run dev:test` | 启动测试环境(test),端口 4000 | `npm run dev:test` |
-| `npm run dev:pro` | 启动生产环境(production)本地调试,端口 5000 | `npm run dev:pro` |
-
----
-
-## 🏗 构建相关
-
-| 命令 | 说明 | 运行示例 |
-|------|------|----------|
-| `npm run build:dev` | 打包 **开发环境** 构建产物 | Jenkins代替 |
-| `npm run build:test` | 打包 **测试环境** 构建产物 | `npm run build:test` |
-| `npm run build` | 打包 **生产环境** 构建产物 | Jenkins代替 |
-
----
-
-## 🚀 发布相关
-
-| 命令 | 说明 | 运行示例 |
-|------|------|----------|
-| `npm run release` | 发布版本 + 生成日志 | `npm run release` |
-
----
-
-## 📤 部署相关
-
-| 命令 | 说明 | 运行示例 |
-|------|------|----------|
-| `npm run deploy:test` | **打包测试环境** 并执行部署脚本 | `npm run deploy:test` |
-| `npm run run:deploy` | 直接执行部署脚本(不打包) | `npm run run:deploy` |
-
-> ⚠ `run:deploy` 会调用 `scripts/deploy.ps1`(PowerShell 脚本),需确保本地已安装 PowerShell 且允许执行脚本。
-
----
-
-## 👀 预览构建产物
-
-| 命令 | 说明 | 运行示例 |
-|------|------|----------|
-| `npm run preview` | 本地预览已构建的生产环境包(默认端口 4173) | `npm run preview` |
-
----
-
-## 🔍 代码检查与格式化
-
-| 命令 | 说明 | 运行示例 |
-|------|------|----------|
-| `npm run type-check` | TypeScript 类型检查 | `npm run type-check` |
-| `npm run lint` | ESLint 代码规范检查并自动修复问题 | `npm run lint` |
-| `npm run format` | 使用 Prettier 格式化 `src/` 目录下的代码 | `npm run format` |
-
----
-
+**🔄 同步分支代码**
+```bash
+# 同步 dev 分支到 prod 分支
+bash scripts/merge_dev_to_pro.sh 
+```
 
+**🚀 发布版本并部署**
+```bash
+npm run release:dev   # 发布开发版本并部署
+npm run release:test  # 发布测试版本并部署
+npm run release:prod  # 发布生产版本并部署
+```

+ 11 - 8
package.json

@@ -7,14 +7,18 @@
     "dev": "vite --host --mode development --port 3000",
     "dev:test": "vite --host --mode test --port 4000",
     "dev:pro": "vite --host --mode production --port 5000",
+    "build": "vite build",
     "build:dev": "vite build --mode development",
     "build:test": "vite build --mode test",
-    "build": "vite build",
+    "build:prod": "vite build --mode production",
+    "deploy": "node scripts/deploy.js",
+    "deploy:dev": "npm run build:dev && node scripts/deploy.js dev",
+    "deploy:test": "npm run build:test && node scripts/deploy.js test",
+    "deploy:prod": "npm run build:prod && node scripts/deploy.js prod",
     "release": "node scripts/release.js",
-    "changelog": "npx conventional-changelog -p angular -i CHANGELOG.md -s",
-    "changelog:all": "npx conventional-changelog -p angular -i CHANGELOG.md -s -r 0",
-    "deploy:test": "npm-run-all2 build:test run:deploy",
-    "run:deploy": "pwsh -ExecutionPolicy Bypass -File scripts/deploy.ps1",
+    "release:dev": "npm run release && npm run deploy:dev",
+    "release:test": "npm run release && npm run deploy:test",
+    "release:prod": "npm run release && npm run deploy:prod",
     "preview": "vite preview",
     "type-check": "vue-tsc --build",
     "lint": "eslint . --fix",
@@ -26,6 +30,7 @@
     "@vueuse/components": "^13.5.0",
     "@vueuse/core": "^13.5.0",
     "ant-design-vue": "~4.2.6",
+    "archiver": "^7.0.1",
     "axios": "^1.10.0",
     "dayjs": "^1.11.13",
     "echarts": "^5.6.0",
@@ -47,7 +52,6 @@
     "@vue/eslint-config-prettier": "^10.2.0",
     "@vue/eslint-config-typescript": "^14.5.1",
     "@vue/tsconfig": "^0.7.0",
-    "conventional-changelog-cli": "^5.0.0",
     "eslint": "^9.29.0",
     "eslint-import-resolver-alias": "^1.1.2",
     "eslint-plugin-import": "^2.32.0",
@@ -56,7 +60,6 @@
     "inquirer": "^12.9.1",
     "jiti": "^2.4.2",
     "less": "^4.3.0",
-    "npm-run-all2": "^8.0.4",
     "prettier": "3.5.3",
     "typescript": "~5.8.0",
     "unplugin-vue-components": "^28.8.0",
@@ -64,4 +67,4 @@
     "vite-plugin-vue-devtools": "^7.7.7",
     "vue-tsc": "^2.2.10"
   }
-}
+}

Fichier diff supprimé car celui-ci est trop grand
+ 183 - 335
pnpm-lock.yaml


+ 98 - 0
scripts/deploy.js

@@ -0,0 +1,98 @@
+#!/usr/bin/env node
+import inquirer from 'inquirer'
+import { execSync } from 'child_process'
+import fs from 'fs'
+import { join } from 'path'
+import { tmpdir } from 'os'
+import archiver from 'archiver'
+import dayjs from 'dayjs'
+
+// ---------- 环境配置 ----------
+const ENV_CONFIG = {
+  dev: { remoteUser: 'root', remoteIP: '192.168.2.140', remoteAppDir: '/work/web/dist' },
+  test: { remoteUser: 'liujia', remoteIP: '43.137.10.199', remoteAppDir: '/work/web/dist' },
+  prod: { remoteUser: 'root', remoteIP: '192.168.2.140', remoteAppDir: '/work/web/dist-prod' },
+}
+const SSH_PORT = 22
+const LOCAL_DIST = 'D:/project/ln-web/dist'
+
+// ---------- 处理环境参数 ----------
+let env = process.argv[2] // npm run deploy:dev 传入 dev/test/prod
+if (!env || !ENV_CONFIG[env]) {
+  const answer = await inquirer.prompt([
+    {
+      type: 'list',
+      name: 'env',
+      message: '请选择要部署的环境:',
+      choices: Object.keys(ENV_CONFIG),
+      default: 'dev',
+    },
+  ])
+  env = answer.env
+}
+const { remoteUser, remoteIP, remoteAppDir } = ENV_CONFIG[env]
+console.log(`\n你选择的环境是: ${env}, 远程目录: ${remoteAppDir}\n`)
+
+// ---------- 压缩 dist ----------
+const timestamp = dayjs().format('YYYYMMDDHHmmss')
+const zipFileName = `dist-${timestamp}.zip`
+const zipPath = join(tmpdir(), zipFileName)
+console.log('📦 正在压缩 dist...')
+if (!fs.existsSync(LOCAL_DIST)) {
+  console.error(`❌ 本地 dist 目录不存在: ${LOCAL_DIST}`)
+  process.exit(1)
+}
+await new Promise((resolve, reject) => {
+  const output = fs.createWriteStream(zipPath)
+  const archive = archiver('zip', { zlib: { level: 9 } })
+  output.on('close', () => resolve())
+  archive.on('error', (err) => reject(err))
+  archive.pipe(output)
+  archive.directory(LOCAL_DIST, false)
+  archive.finalize()
+})
+const zipSizeMB = (fs.statSync(zipPath).size / 1024 / 1024).toFixed(2)
+console.log(`✅ 压缩完成: ${zipSizeMB} MB, 文件: ${zipPath}`)
+
+// ---------- 上传 & 部署脚本生成 ----------
+const remoteZip = `/tmp/dist-latest-${timestamp}.zip`
+execSync(`scp -P ${SSH_PORT} "${zipPath}" ${remoteUser}@${remoteIP}:${remoteZip}`, {
+  stdio: 'inherit',
+})
+const remoteScriptContent = `#!/bin/bash
+set -e
+timestamp="${timestamp}"
+tmpdir="/tmp/dist-temp-$timestamp"
+target="${remoteAppDir}"
+zipfile="${remoteZip}"
+echo "📦 正在服务器上原子部署 ..."
+echo "📁 创建临时目录 $tmpdir"
+mkdir -p "$tmpdir"
+echo "📦 解压上传文件"
+unzip -qo "$zipfile" -d "$tmpdir"
+echo "🔒 设置权限"
+chown -R root:root "$tmpdir"
+echo "🚀 替换新目录"
+rm -rf "$target"
+mv "$tmpdir" "$target"
+echo "🧹 删除压缩包"
+rm -f "$zipfile"
+echo "✅ 远程部署完成: $target 替换成功"
+`
+const localScriptPath = join(tmpdir(), `deploy-temp-${timestamp}.sh`)
+fs.writeFileSync(localScriptPath, remoteScriptContent, { encoding: 'utf8' })
+const remoteScriptPath = `/tmp/deploy-temp-${timestamp}.sh`
+execSync(`scp -P ${SSH_PORT} "${localScriptPath}" ${remoteUser}@${remoteIP}:${remoteScriptPath}`, {
+  stdio: 'inherit',
+})
+console.log('\n🚀 执行远程部署脚本...')
+execSync(`ssh -p ${SSH_PORT} ${remoteUser}@${remoteIP} "bash ${remoteScriptPath}"`, {
+  stdio: 'inherit',
+})
+console.log('\n🧹 清理本地/远程临时文件...')
+fs.unlinkSync(zipPath)
+fs.unlinkSync(localScriptPath)
+execSync(`ssh -p ${SSH_PORT} ${remoteUser}@${remoteIP} "rm -f ${remoteScriptPath} ${remoteZip}"`, {
+  stdio: 'inherit',
+})
+console.log(`\n✅ [${env}] 环境部署完成!`)

+ 0 - 17
scripts/release-message.js

@@ -1,17 +0,0 @@
-#!/usr/bin/env node
-import pkg from '../package.json' assert { type: 'json' }
-
-const version = pkg.version
-
-console.log('\x1b[36m=========================\x1b[0m')
-console.log('\x1b[32m🎉 发布成功!\x1b[0m')
-console.log(`\x1b[33m📦 版本号:v${version}\x1b[0m`)
-console.log('\x1b[35m⚙️  部署:\x1b[0m')
-console.log('\x1b[34m  * 打包并部署到测试环境(单机版):npm run deploy:test\x1b[0m')
-console.log('\x1b[35m⚙️  打包:\x1b[0m')
-console.log('\x1b[34m  * 开发环境:npm run build:dev\x1b[0m')
-console.log('\x1b[34m  * 测试环境:npm run build:test\x1b[0m')
-console.log('\x1b[34m  * 生产环境:npm run build\x1b[0m')
-console.log('\x1b[35m⚙️  同步代码:\x1b[0m')
-console.log('\x1b[34m  * 同步dev分支代码到prod分支:bash scripts/merge_dev_to_pro.sh\x1b[0m')
-console.log('\x1b[36m=========================\x1b[0m')

+ 0 - 6
scripts/release.js

@@ -123,12 +123,6 @@ async function main() {
   console.log('\x1b[36m=========================\x1b[0m')
   console.log('\x1b[32m🎉 发布成功!\x1b[0m')
   console.log(`\x1b[33m📦 版本号:v${version}\x1b[0m`)
-  console.log('\x1b[35m⚙️  部署:\x1b[0m')
-  console.log('\x1b[34m  * 打包并部署到测试环境(单机版):npm run deploy:test\x1b[0m')
-  console.log('\x1b[35m⚙️  打包:\x1b[0m')
-  console.log('\x1b[34m  * 开发环境:npm run build:dev\x1b[0m')
-  console.log('\x1b[34m  * 测试环境:npm run build:test\x1b[0m')
-  console.log('\x1b[34m  * 生产环境:npm run build\x1b[0m')
   console.log('\x1b[35m⚙️  同步代码:\x1b[0m')
   console.log('\x1b[34m  * 同步dev分支代码到prod分支:bash scripts/merge_dev_to_pro.sh\x1b[0m')
   console.log('\x1b[36m=========================\x1b[0m')

+ 0 - 262
version-script.js

@@ -1,262 +0,0 @@
-#!/usr/bin/env node
-import fs from 'fs'
-import path from 'path'
-import { fileURLToPath } from 'url'
-import { execSync } from 'child_process'
-import readline from 'readline'
-
-// 获取当前脚本所在目录
-const __filename = fileURLToPath(import.meta.url)
-const __dirname = path.dirname(__filename)
-
-// 读取package.json
-const packageJson = JSON.parse(fs.readFileSync(path.join(__dirname, 'package.json'), 'utf8'))
-const currentVersion = packageJson.version
-
-// 获取当前分支名
-function getCurrentBranch() {
-  try {
-    return execSync('git rev-parse --abbrev-ref HEAD', { encoding: 'utf8' }).trim()
-  } catch (error) {
-    console.error('❌❌❌ getCurrentBranch Failed:', error.message)
-    return 'unknown-branch'
-  }
-}
-
-// 获取当前时间戳
-function getTimestamp() {
-  const now = new Date()
-  // 精确到秒 + 3位随机数(1/1000概率),确保同一秒内多次构建也能生成唯一的tag
-  return (
-    now.getFullYear() +
-    String(now.getMonth() + 1).padStart(2, '0') +
-    String(now.getDate()).padStart(2, '0') +
-    String(now.getHours()).padStart(2, '0') +
-    String(now.getMinutes()).padStart(2, '0') +
-    String(now.getSeconds()).padStart(2, '0') +
-    Math.floor(Math.random() * 1000)
-      .toString()
-      .padStart(3, '0')
-  )
-}
-
-// 验证版本号格式
-function isValidVersion(version) {
-  const versionRegex = /^(\d+)(\.(\d+))?(\.(\d+))?$/
-  return versionRegex.test(version)
-}
-
-// 比较版本号
-function compareVersions(version1, version2) {
-  const v1 = version1.split('.').map(Number)
-  const v2 = version2.split('.').map(Number)
-
-  for (let i = 0; i < Math.max(v1.length, v2.length); i++) {
-    const num1 = v1[i] || 0
-    const num2 = v2[i] || 0
-
-    if (num1 > num2) return 1
-    if (num1 < num2) return -1
-  }
-
-  return 0
-}
-
-// 格式化日期为YYYY-MM-DD HH:mm:ss
-function formatDateTime(date) {
-  const year = date.getFullYear()
-  const month = String(date.getMonth() + 1).padStart(2, '0')
-  const day = String(date.getDate()).padStart(2, '0')
-  const hours = String(date.getHours()).padStart(2, '0')
-  const minutes = String(date.getMinutes()).padStart(2, '0')
-  const seconds = String(date.getSeconds()).padStart(2, '0')
-
-  return `${year}-${month}-${day} ${hours}:${minutes}:${seconds}`
-}
-
-// 创建版本信息文件
-function createVersionInfoFile(version, type, branch, tag) {
-  const versionInfo = {
-    version: version,
-    branch: branch,
-    tag: tag,
-    buildType: type,
-    buildTime: formatDateTime(new Date()),
-  }
-
-  const distDir = path.join(__dirname, 'dist')
-  // 确保dist目录存在
-  if (!fs.existsSync(distDir)) {
-    fs.mkdirSync(distDir, { recursive: true })
-  }
-
-  // 写入version.json文件
-  fs.writeFileSync(path.join(distDir, 'version.json'), JSON.stringify(versionInfo, null, 2))
-  console.log(`✅✅✅ version.json has been generated: ${path.join(distDir, 'version.json')}`)
-}
-
-// 处理测试构建
-function handleTestBuild() {
-  const branch = getCurrentBranch()
-  const timestamp = getTimestamp()
-  const tag = `test-${timestamp}`
-
-  console.log(
-    `🚀🚀🚀 A test version is being created... branchName:${branch},productionVersion:${currentVersion}`
-  )
-
-  try {
-    // 执行构建命令
-    execSync('vite build --mode test', { stdio: 'inherit' })
-
-    // 创建tag
-    execSync(`git tag ${tag}`, { stdio: 'inherit' })
-    console.log(`✅✅✅ Test Version Build Success! Test Version: ${tag}`)
-
-    // 生成版本信息文件
-    createVersionInfoFile(currentVersion, 'test', branch, tag)
-  } catch (error) {
-    console.error('❌❌❌ Test Version Build Failed:', error.message)
-    process.exit(1)
-  }
-}
-
-// 处理发布构建
-function handlePublishBuild() {
-  const rl = readline.createInterface({
-    input: process.stdin,
-    output: process.stdout,
-  })
-
-  rl.question(`请输入需要发布的新版本号(当前生产版本号: ${currentVersion}): `, (newVersion) => {
-    newVersion = newVersion.trim()
-
-    if (!isValidVersion(newVersion)) {
-      console.error('无效的发布版本号格式,请使用x.x.x格式')
-      rl.close()
-      process.exit(1)
-    }
-
-    if (compareVersions(newVersion, currentVersion) <= 0) {
-      console.error(`发布版本号必须大于当前版本(${currentVersion})`)
-      rl.close()
-      process.exit(1)
-    }
-
-    const branch = getCurrentBranch()
-    const tag = `publish-v${newVersion}`
-    console.log(
-      `🚀🚀🚀 A publish version is being created... branchName:${branch}、productionVersion: ${newVersion}`
-    )
-
-    try {
-      // 更新package.json中的版本
-      packageJson.version = newVersion
-      fs.writeFileSync(path.join(__dirname, 'package.json'), JSON.stringify(packageJson, null, 2))
-
-      // 执行构建命令
-      execSync('vite build', { stdio: 'inherit' })
-
-      // 创建tag
-      execSync(`git tag ${tag}`, { stdio: 'inherit' })
-      console.log(`✅✅✅ Publish version ${tag} build success! Version number: ${newVersion}`)
-
-      // 生成版本信息文件
-      createVersionInfoFile(newVersion, 'publish', branch, tag)
-    } catch (error) {
-      console.error('❌❌❌ Publish version build failed:', error.message)
-      process.exit(1)
-    } finally {
-      rl.close()
-    }
-  })
-}
-
-// 基于特定tag构建 测试与生产环境 版本
-function handleBuildByTag(modeType) {
-  if (modeType !== 'test' && modeType !== 'pro') {
-    console.error('无效的构建类型,请确认构建类型为 test 或 pro')
-    process.exit(1)
-  }
-  console.log(`🚀🚀🚀 正在基于${modeType}构建...`)
-  const rl = readline.createInterface({
-    input: process.stdin,
-    output: process.stdout,
-  })
-
-  // 保存原始分支
-  const originalBranch = execSync('git rev-parse --abbrev-ref HEAD', { encoding: 'utf8' }).trim()
-
-  rl.question('请输入要构建的tag名称(test-xxxx 或 publish-xxxx):', (tag) => {
-    tag = tag.trim()
-
-    try {
-      // 检查tag是否存在
-      execSync(`git rev-parse ${tag}`, { stdio: 'ignore' })
-      console.log(`🚀🚀🚀 正在基于tag ${tag} 构建...`)
-
-      // 切换到该tag
-      execSync(`git checkout ${tag}`, { stdio: 'inherit' })
-
-      // 读取当前分支名
-      const branch = getCurrentBranch()
-
-      // 读取package.json中的版本号
-      const pkgJson = JSON.parse(fs.readFileSync(path.join(__dirname, 'package.json'), 'utf8'))
-      const version = pkgJson.version
-
-      // 执行构建命令
-      if (modeType === 'test') {
-        execSync('vite build --mode test', { stdio: 'inherit' })
-      } else if (modeType === 'pro') {
-        execSync('vite build', { stdio: 'inherit' })
-      }
-
-      // 生成版本信息文件
-      const buildType = tag.startsWith('test-') ? 'test' : 'publish'
-      createVersionInfoFile(version, buildType, branch, tag)
-
-      console.log(`✅✅✅ 基于tag ${tag} 构建成功!`)
-
-      // 切换回原分支
-      execSync(`git checkout ${originalBranch}`, { stdio: 'inherit' })
-      console.log(`✅✅✅ 已切换回原分支: ${originalBranch}`)
-    } catch (error) {
-      console.error(`❌❌❌ 基于tag ${tag} 构建失败: ${error.message}`)
-      // 即使构建失败也尝试切换回原分支
-      try {
-        execSync(`git checkout ${originalBranch}`, { stdio: 'inherit' })
-        console.log(`✅✅✅ 已切换回原分支: ${originalBranch}`)
-      } catch (checkoutError) {
-        console.error(`❌❌❌ 切换回原分支失败,请手动处理: ${checkoutError.message}`)
-      }
-      process.exit(1)
-    } finally {
-      rl.close()
-    }
-  })
-}
-
-// 主函数
-function main() {
-  const command = process.argv[2]
-
-  switch (command) {
-    case 'test':
-      handleTestBuild()
-      break
-    case 'publish':
-      handlePublishBuild()
-      break
-    case 'test-by-tag':
-      handleBuildByTag('test')
-      break
-    case 'pro-by-tag':
-      handleBuildByTag('pro')
-      break
-    default:
-      console.log('用法: node version-script.js [test|publish|build-by-tag]')
-      process.exit(1)
-  }
-}
-main()

Certains fichiers n'ont pas été affichés car il y a eu trop de fichiers modifiés dans ce diff