Commit 35cd3d68 by chunhong.mu

chore(root): update scripts

parent c3a40413
...@@ -31,7 +31,39 @@ common-web/ ...@@ -31,7 +31,39 @@ common-web/
### `scripts/` — 辅助脚本 ### `scripts/` — 辅助脚本
存放项目辅助脚本(如项目选择器、自动化脚本等),不参与代码检查(已在 `eslint.config.mjs` 中忽略)。 存放项目辅助脚本,不参与代码检查(已在 `eslint.config.mjs` 中忽略)。
**脚本列表**
| 脚本 | 说明 |
|------|------|
| `dev-project.ts` | 启动指定项目的开发服务器 |
| `build-project.ts` | 构建指定项目 |
| `git-workflow.ts` | Git 工作流自动化脚本 |
| `new-project.ts` | 一键创建新项目 |
**使用方式**
```bash
# 启动项目开发服务器
pnpm dev:project --project=official-site-web
# 构建指定项目
pnpm build:project --project=official-site-web
# 创建新项目
pnpm new:project admin-web vue
# Git 工作流
pnpm git:dev-start official-site-web # 创建/切换到项目开发分支
pnpm git:dev-done official-site-web # 合并项目分支到 dev
pnpm git:deploy-staging # 部署到预发环境
pnpm git:deploy-release v1.0.0 # 部署到生产环境
pnpm git:hotfix-start # 创建/切换到 hotfix 分支
pnpm git:hotfix-done # 完成 hotfix
pnpm git:refactor-start # 创建/切换到 refactor 分支
pnpm git:refactor-done # 完成 refactor
```
--- ---
...@@ -426,32 +458,32 @@ refactor ← 重构分支(唯一) ...@@ -426,32 +458,32 @@ refactor ← 重构分支(唯一)
### 6.4 工作流脚本 ### 6.4 工作流脚本
项目提供 `scripts/git-workflow.js` 脚本,可通过 npm scripts 执行: 项目提供 `scripts/git-workflow.ts` 脚本,可通过 npm scripts 执行:
```bash ```bash
# 创建项目开发分支 # 创建/切换到项目开发分支
pnpm git:feature official-site-web pnpm git:dev-start official-site-web
# 合并项目分支到 dev # 合并项目分支到 dev
pnpm git:merge official-site-web pnpm git:dev-done official-site-web
# 发布预发(dev → staging) # 发布预发(dev → staging)
pnpm git:release pnpm git:deploy-staging
# 发布生产(staging → release) # 发布生产(staging → release)
pnpm git:publish pnpm git:deploy-release v1.0.0
# 创建 hotfix 分支 # 创建/切换到 hotfix 分支
pnpm git:hotfix pnpm git:hotfix-start
# 合并 hotfix 到 release 和 dev # 完成 hotfix,合并到 release 和 dev
pnpm git:hotfix:merge pnpm git:hotfix-done
# 创建 refactor 分支 # 创建/切换到 refactor 分支
pnpm git:refactor pnpm git:refactor-start
# 合并 refactor 到 dev # 完成 refactor,合并到 dev
pnpm git:refactor:merge pnpm git:refactor-done
``` ```
### 6.5 工作流说明 ### 6.5 工作流说明
...@@ -459,39 +491,39 @@ pnpm git:refactor:merge ...@@ -459,39 +491,39 @@ pnpm git:refactor:merge
**1. 新功能开发** **1. 新功能开发**
```bash ```bash
pnpm git:feature official-site-web pnpm git:dev-start official-site-web
# 日常开发直接在 dev/official-site-web 提交 # 日常开发直接在 dev/official-site-web 提交
# 小 bug 也直接在此分支修复 # 小 bug 也直接在此分支修复
pnpm git:merge official-site-web # 合并回 dev pnpm git:dev-done official-site-web # 合并回 dev
``` ```
**2. 发布预发** **2. 发布预发**
```bash ```bash
pnpm git:release # dev → staging pnpm git:deploy-staging # dev → staging
``` ```
**3. 发布生产** **3. 发布生产**
```bash ```bash
pnpm git:publish # staging → release,自动打 tag pnpm git:deploy-release v1.0.0 # staging → release,自动打 tag
``` ```
**4. 紧急修复** **4. 紧急修复**
```bash ```bash
pnpm git:hotfix # 创建/切换到 hotfix 分支 pnpm git:hotfix-start # 创建/切换到 hotfix 分支
# 修复... # 修复...
pnpm git:hotfix:merge # 合并到 release 和 dev pnpm git:hotfix-done # 合并到 release 和 dev
``` ```
**5. 重构** **5. 重构**
```bash ```bash
pnpm git:refactor # 创建/切换到 refactor 分支 pnpm git:refactor-start # 创建/切换到 refactor 分支
# 重构... # 重构...
pnpm git:refactor:merge # 合并到 dev pnpm git:refactor-done # 合并到 dev
``` ```
### 6.6 提交规范 ### 6.6 提交规范
......
...@@ -5,19 +5,20 @@ ...@@ -5,19 +5,20 @@
"private": true, "private": true,
"packageManager": "pnpm@9.12.3", "packageManager": "pnpm@9.12.3",
"scripts": { "scripts": {
"dev:project": "tsx scripts/dev.ts", "dev:project": "tsx scripts/dev-project.ts",
"build:project": "tsx scripts/build.ts", "build:project": "tsx scripts/build-project.ts",
"lint": "eslint .", "lint": "eslint .",
"lint:fix": "eslint . --fix", "lint:fix": "eslint . --fix",
"prepare": "husky", "prepare": "husky",
"git:feature": "tsx scripts/git-workflow.ts feature", "git:dev-start": "tsx scripts/git-workflow.ts dev-start",
"git:merge": "tsx scripts/git-workflow.ts merge", "git:dev-done": "tsx scripts/git-workflow.ts dev-done",
"git:release": "tsx scripts/git-workflow.ts release", "git:deploy-staging": "tsx scripts/git-workflow.ts deploy-staging",
"git:publish": "tsx scripts/git-workflow.ts publish", "git:deploy-release": "tsx scripts/git-workflow.ts deploy-release",
"git:hotfix": "tsx scripts/git-workflow.ts hotfix", "git:hotfix-start": "tsx scripts/git-workflow.ts hotfix-start",
"git:hotfix:merge": "tsx scripts/git-workflow.ts hotfix:merge", "git:hotfix-done": "tsx scripts/git-workflow.ts hotfix-done",
"git:refactor": "tsx scripts/git-workflow.ts refactor", "git:refactor-start": "tsx scripts/git-workflow.ts refactor-start",
"git:refactor:merge": "tsx scripts/git-workflow.ts refactor:merge" "git:refactor-done": "tsx scripts/git-workflow.ts refactor-done",
"new:project": "tsx scripts/new-project.ts"
}, },
"devDependencies": { "devDependencies": {
"@antfu/eslint-config": "^3.9.2", "@antfu/eslint-config": "^3.9.2",
......
...@@ -3,101 +3,250 @@ import { execSync } from 'node:child_process' ...@@ -3,101 +3,250 @@ import { execSync } from 'node:child_process'
const command = process.argv[2] const command = process.argv[2]
const projectName = process.argv[3] const projectName = process.argv[3]
function run(cmd: string)
{
execSync(cmd, { stdio: 'inherit' })
}
/** 检查工作区是否干净,防止未提交的改动被污染 */
function checkCleanWorkingTree()
{
try
{
const status = execSync('git status --porcelain').toString().trim()
if (status)
{
console.error('Error: Working tree is not clean. Please commit or stash changes first.')
process.exit(1)
}
}
catch
{
// git not initialized yet
}
}
/** 检查本地分支是否存在 */
function branchExists(branch: string)
{
try
{
execSync(`git rev-parse --verify ${branch}`, { stdio: 'ignore' })
return true
}
catch
{
return false
}
}
const commands = { const commands = {
'feature': () => /**
* 创建或切换到项目开发分支
* 步骤:
* 1. 检查工作区是否干净
* 2. 如果分支已存在,直接切换
* 3. 如果不存在,从 dev 拉取最新代码并创建 dev/<project> 分支
*/
'dev-start': () =>
{ {
if (!projectName) if (!projectName)
{ {
console.error('Usage: pnpm git:feature <project-name>') console.error('Usage: pnpm git:dev-start <project-name>')
console.error('Example: pnpm git:feature official-site-web') console.error('Example: pnpm git:dev-start official-site-web')
process.exit(1) process.exit(1)
} }
const branchName = `dev/${projectName}` const branchName = `dev/${projectName}`
console.log(`Creating branch: ${branchName}`) checkCleanWorkingTree()
execSync('git checkout dev', { stdio: 'inherit' })
execSync('git pull', { stdio: 'inherit' }) if (branchExists(branchName))
execSync(`git checkout -b ${branchName}`, { stdio: 'inherit' }) {
console.log(`Branch ${branchName} exists, switching...`)
run(`git checkout ${branchName}`)
}
else
{
console.log(`Creating branch: ${branchName}`)
run('git checkout dev')
run('git pull origin dev')
run(`git checkout -b ${branchName}`)
}
console.log(`Switched to ${branchName}`) console.log(`Switched to ${branchName}`)
}, },
'merge': () => /**
* 合并项目开发分支到 dev
* 步骤:
* 1. 检查工作区是否干净
* 2. 切换到 dev 分支并拉取最新代码
* 3. 合并 dev/<project> 到 dev(--no-ff 保留分支历史)
* 4. 推送到远程 dev
*/
'dev-done': () =>
{ {
if (!projectName) if (!projectName)
{ {
console.error('Usage: pnpm git:merge <project-name>') console.error('Usage: pnpm git:dev-done <project-name>')
console.error('Example: pnpm git:merge official-site-web') console.error('Example: pnpm git:dev-done official-site-web')
process.exit(1) process.exit(1)
} }
const branchName = `dev/${projectName}` const branchName = `dev/${projectName}`
console.log(`Merging ${branchName} into dev`) checkCleanWorkingTree()
execSync('git checkout dev', { stdio: 'inherit' })
execSync(`git merge ${branchName} --no-ff`, { stdio: 'inherit' }) console.log(`Merging ${branchName} into dev...`)
execSync('git push origin dev', { stdio: 'inherit' }) run('git checkout dev')
run('git pull origin dev')
run(`git merge ${branchName} --no-ff --no-edit`)
run('git push origin dev')
console.log('Merge completed') console.log('Merge completed')
}, },
'release': () => /**
* 部署到预发环境(staging)
* 步骤:
* 1. 检查工作区是否干净
* 2. 切换到 staging 分支并拉取最新代码
* 3. 合并 dev 到 staging
* 4. 推送到远程 staging
*/
'deploy-staging': () =>
{ {
console.log('Releasing dev to staging...') checkCleanWorkingTree()
execSync('git checkout staging', { stdio: 'inherit' }) console.log('Deploying dev to staging...')
execSync('git merge dev --no-ff', { stdio: 'inherit' }) run('git checkout staging')
execSync('git push origin staging', { stdio: 'inherit' }) run('git pull origin staging')
console.log('Released to staging') run('git merge dev --no-ff --no-edit')
run('git push origin staging')
console.log('Deployed to staging')
}, },
'publish': () => /**
* 部署到生产环境(release)
* 步骤:
* 1. 检查工作区是否干净
* 2. 切换到 release 分支并拉取最新代码
* 3. 合并 staging 到 release
* 4. 打版本 tag
* 5. 推送 release 分支和 tag 到远程
*/
'deploy-release': () =>
{ {
console.log('Publishing staging to release...') checkCleanWorkingTree()
const version = process.argv[3] || 'v1.0.0' const version = process.argv[3]
execSync('git checkout release', { stdio: 'inherit' }) if (!version)
execSync('git merge staging --no-ff', { stdio: 'inherit' }) {
execSync(`git tag -a ${version} -m "release ${version}"`, { stdio: 'inherit' }) console.error('Usage: pnpm git:deploy-release <version>')
execSync(`git push origin release --tags`, { stdio: 'inherit' }) console.error('Example: pnpm git:deploy-release v1.0.0')
console.log(`Published to release with tag ${version}`) process.exit(1)
}
console.log(`Deploying staging to release (${version})...`)
run('git checkout release')
run('git pull origin release')
run('git merge staging --no-ff --no-edit')
run(`git tag -a ${version} -m "release ${version}"`)
run(`git push origin release`)
run(`git push origin ${version}`)
console.log(`Deployed to release with tag ${version}`)
}, },
'hotfix': () => /**
* 创建或切换到 hotfix 分支(唯一分支)
* 步骤:
* 1. 检查工作区是否干净
* 2. 如果 hotfix 分支已存在,直接切换
* 3. 如果不存在,从 release 拉取最新代码并创建 hotfix 分支
*/
'hotfix-start': () =>
{ {
console.log('Creating hotfix branch...') checkCleanWorkingTree()
execSync('git checkout release', { stdio: 'inherit' }) console.log('Starting hotfix...')
execSync('git checkout -b hotfix || git checkout hotfix', { stdio: 'inherit' }) if (branchExists('hotfix'))
{
run('git checkout hotfix')
}
else
{
run('git checkout release')
run('git pull origin release')
run('git checkout -b hotfix')
}
console.log('Switched to hotfix branch') console.log('Switched to hotfix branch')
}, },
'hotfix:merge': () => /**
* 完成 hotfix,合并到 release 和 dev
* 步骤:
* 1. 检查工作区是否干净
* 2. 切换到 release 分支并拉取最新代码
* 3. 合并 hotfix 到 release
* 4. 推送 release 到远程
* 5. 切换到 dev 分支并拉取最新代码
* 6. 合并 hotfix 到 dev
* 7. 推送 dev 到远程
*/
'hotfix-done': () =>
{ {
console.log('Merging hotfix to release and dev...') checkCleanWorkingTree()
execSync('git checkout release', { stdio: 'inherit' }) console.log('Completing hotfix...')
execSync('git merge hotfix --no-ff', { stdio: 'inherit' }) run('git checkout release')
execSync('git push origin release', { stdio: 'inherit' }) run('git pull origin release')
execSync('git checkout dev', { stdio: 'inherit' }) run('git merge hotfix --no-ff --no-edit')
execSync('git merge hotfix --no-ff', { stdio: 'inherit' }) run('git push origin release')
execSync('git push origin dev', { stdio: 'inherit' }) run('git checkout dev')
console.log('Hotfix merged to release and dev') run('git pull origin dev')
run('git merge hotfix --no-ff --no-edit')
run('git push origin dev')
console.log('Hotfix completed')
}, },
'refactor': () => /**
* 创建或切换到 refactor 分支(唯一分支)
* 步骤:
* 1. 检查工作区是否干净
* 2. 如果 refactor 分支已存在,直接切换
* 3. 如果不存在,从 dev 拉取最新代码并创建 refactor 分支
*/
'refactor-start': () =>
{ {
console.log('Creating refactor branch...') checkCleanWorkingTree()
execSync('git checkout dev', { stdio: 'inherit' }) console.log('Starting refactor...')
execSync('git checkout -b refactor || git checkout refactor', { stdio: 'inherit' }) if (branchExists('refactor'))
{
run('git checkout refactor')
}
else
{
run('git checkout dev')
run('git pull origin dev')
run('git checkout -b refactor')
}
console.log('Switched to refactor branch') console.log('Switched to refactor branch')
}, },
'refactor:merge': () => /**
* 完成 refactor,合并到 dev
* 步骤:
* 1. 检查工作区是否干净
* 2. 切换到 dev 分支并拉取最新代码
* 3. 合并 refactor 到 dev
* 4. 推送 dev 到远程
*/
'refactor-done': () =>
{ {
console.log('Merging refactor to dev...') checkCleanWorkingTree()
execSync('git checkout dev', { stdio: 'inherit' }) console.log('Completing refactor...')
execSync('git merge refactor --no-ff', { stdio: 'inherit' }) run('git checkout dev')
execSync('git push origin dev', { stdio: 'inherit' }) run('git pull origin dev')
console.log('Refactor merged to dev') run('git merge refactor --no-ff --no-edit')
run('git push origin dev')
console.log('Refactor completed')
}, },
} }
if (!command || !commands[command]) if (!command || !commands[command])
{ {
console.log('Git Workflow Commands:') console.log('Git Workflow Commands:')
console.log(' pnpm git:feature <project> - Create dev/<project> branch') console.log(' pnpm git:dev-start <project> - Create or switch to dev/<project> branch')
console.log(' pnpm git:merge <project> - Merge dev/<project> into dev') console.log(' pnpm git:dev-done <project> - Merge dev/<project> into dev')
console.log(' pnpm git:release - Release dev to staging') console.log(' pnpm git:deploy-staging - Deploy dev to staging')
console.log(' pnpm git:publish [version] - Publish staging to release') console.log(' pnpm git:deploy-release <version> - Deploy staging to release')
console.log(' pnpm git:hotfix - Create/switch to hotfix branch') console.log(' pnpm git:hotfix-start - Create or switch to hotfix branch')
console.log(' pnpm git:hotfix:merge - Merge hotfix to release and dev') console.log(' pnpm git:hotfix-done - Complete hotfix, merge to release + dev')
console.log(' pnpm git:refactor - Create/switch to refactor branch') console.log(' pnpm git:refactor-start - Create or switch to refactor branch')
console.log(' pnpm git:refactor:merge - Merge refactor to dev') console.log(' pnpm git:refactor-done - Complete refactor, merge to dev')
process.exit(0) process.exit(0)
} }
......
import { execSync } from 'node:child_process'
import { existsSync, mkdirSync, writeFileSync } from 'node:fs'
import { resolve } from 'node:path'
/** 获取命令行参数 */
const projectName = process.argv[2]
const projectType = process.argv[3] || 'nuxt'
/** 校验参数 */
if (!projectName)
{
console.error('Usage: pnpm new:project <project-name> [nuxt|vue]')
console.error('Example: pnpm new:project admin-web vue')
process.exit(1)
}
const projectDir = resolve('packages', projectName)
/** 检查项目是否已存在 */
if (existsSync(projectDir))
{
console.error(`Error: Project "${projectName}" already exists at ${projectDir}`)
process.exit(1)
}
console.log(`Creating project: ${projectName} (type: ${projectType})`)
/**
* 步骤 1:创建目录结构
* 根据项目类型创建不同的目录
*/
const dirs = [
'pages',
'components/business',
'components/layout',
'composables',
'api',
'services',
'stores',
'utils',
'config',
'types',
'assets',
]
if (projectType === 'vue')
{
dirs.push('router')
}
else
{
dirs.push('middleware')
dirs.push('plugins')
}
for (const dir of dirs)
{
mkdirSync(resolve(projectDir, dir), { recursive: true })
}
/**
* 步骤 2:创建 package.json
* 根据项目类型配置不同的依赖和脚本
*/
const packageJson = {
name: projectName,
type: 'module',
version: '1.0.0',
private: true,
scripts: projectType === 'nuxt'
? {
dev: 'nuxt dev',
build: 'nuxt build',
generate: 'nuxt generate',
preview: 'nuxt preview',
}
: {
dev: 'vite',
build: 'vue-tsc && vite build',
preview: 'vite preview',
},
dependencies: projectType === 'nuxt'
? {
nuxt: '^3.14.1',
vue: '^3.4.0',
'ant-design-vue': '^4.2.6',
axios: '^1.7.9',
pinia: '^2.1.7',
}
: {
vue: '^3.4.0',
'ant-design-vue': '^4.2.6',
axios: '^1.7.9',
pinia: '^2.1.7',
'vue-router': '^4.3.0',
},
devDependencies: projectType === 'nuxt'
? {
typescript: '^5.6.3',
unocss: '^0.58.0',
}
: {
typescript: '^5.6.3',
'vue-tsc': '^2.1.0',
vite: '^5.4.0',
'@vitejs/plugin-vue': '^5.1.0',
unocss: '^0.58.0',
},
}
writeFileSync(
resolve(projectDir, 'package.json'),
`${JSON.stringify(packageJson, null, 4)}\n`,
)
/**
* 步骤 3:创建 tsconfig.json
*/
const tsconfig = {
compilerOptions: {
target: 'ESNext',
module: 'ESNext',
moduleResolution: 'bundler',
strict: true,
jsx: 'preserve',
resolveJsonModule: true,
isolatedModules: true,
esModuleInterop: true,
lib: ['ESNext', 'DOM'],
skipLibCheck: true,
paths: {
'@/*': ['./*'],
},
},
include: ['**/*.ts', '**/*.vue'],
exclude: ['node_modules', 'dist'],
}
writeFileSync(
resolve(projectDir, 'tsconfig.json'),
`${JSON.stringify(tsconfig, null, 4)}\n`,
)
/**
* 步骤 4:创建环境文件
* 包含 dev、test、staging、production 四种环境
*/
const envFiles = {
'.env.development': 'APP_ENV=dev\nNODE_ENV=development\n',
'.env.test': 'APP_ENV=test\nNODE_ENV=production\n',
'.env.staging': 'APP_ENV=staging\nNODE_ENV=production\n',
'.env.production': 'APP_ENV=production\nNODE_ENV=production\n',
}
for (const [file, content] of Object.entries(envFiles))
{
writeFileSync(resolve(projectDir, file), content)
}
/**
* 步骤 5:创建入口文件
* Nuxt 项目创建 app.vue 和 nuxt.config.ts
* Vue 项目创建 src/App.vue、src/main.ts 和 vite.config.ts
*/
if (projectType === 'nuxt')
{
writeFileSync(
resolve(projectDir, 'app.vue'),
`<template>\n <div>\n <NuxtPage />\n </div>\n</template>\n`,
)
writeFileSync(
resolve(projectDir, 'nuxt.config.ts'),
`export default defineNuxtConfig({\n devtools: { enabled: true },\n modules: ['@unocss/nuxt'],\n})\n`,
)
}
else
{
mkdirSync(resolve(projectDir, 'src'), { recursive: true })
writeFileSync(
resolve(projectDir, 'src/App.vue'),
`<template>\n <div>\n <RouterView />\n </div>\n</template>\n`,
)
writeFileSync(
resolve(projectDir, 'src/main.ts'),
`import { createApp } from 'vue'\nimport { createPinia } from 'pinia'\nimport App from './App.vue'\n\nconst app = createApp(App)\napp.use(createPinia())\napp.mount('#app')\n`,
)
writeFileSync(
resolve(projectDir, 'vite.config.ts'),
`import { defineConfig } from 'vite'\nimport vue from '@vitejs/plugin-vue'\nimport UnoCSS from 'unocss/vite'\n\nexport default defineConfig({\n plugins: [vue(), UnoCSS()],\n})\n`,
)
}
/**
* 步骤 6:创建 index.ts 占位文件
*/
const indexFiles = [
'api/index.ts',
'api/request.ts',
'composables/index.ts',
'config/index.ts',
'services/index.ts',
'stores/index.ts',
'types/index.ts',
'utils/index.ts',
]
for (const file of indexFiles)
{
writeFileSync(resolve(projectDir, file), 'export {}\n')
}
/**
* 步骤 7:创建首页
*/
writeFileSync(
resolve(projectDir, 'pages/index.vue'),
`<template>\n <div>\n <h1>${projectName}</h1>\n </div>\n</template>\n\n<script setup lang="ts">\n//\n</script>\n`,
)
/**
* 步骤 8:安装依赖
*/
console.log('Installing dependencies...')
execSync('pnpm install', { stdio: 'inherit' })
/**
* 步骤 9:创建并切换到项目开发分支
* 从 dev 分支创建 dev/项目名 分支
*/
console.log('Creating dev branch...')
execSync('git checkout dev', { stdio: 'inherit' })
execSync(`git checkout -b dev/${projectName}`, { stdio: 'inherit' })
console.log(`Switched to dev/${projectName}`)
console.log(`\nProject "${projectName}" created successfully!`)
console.log(`\nNext steps:`)
console.log(` 1. Start developing in packages/${projectName}`)
console.log(` 2. pnpm dev:project --project=${projectName}`)
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or sign in to comment