Commit e63a9de2 by chunhong.mu

chore(root): use Changesets for versioning

parent 35cd3d68
{
"$schema": "https://unpkg.com/@changesets/config@3.1.0/schema.json",
"changelog": "@changesets/cli/changelog",
"commit": false,
"fixed": [],
"linked": [],
"access": "restricted",
"baseBranch": "dev",
"updateInternalDependencies": "patch",
"ignore": [],
"privatePackages": {
"version": true,
"tag": true
},
"___experimentalUnsafeOptions_WILL_CHANGE_IN_PATCH": {
"onlyUpdatePeerDependentsWhenOutOfRange": true
}
}
...@@ -504,12 +504,25 @@ pnpm git:dev-done official-site-web # 合并回 dev ...@@ -504,12 +504,25 @@ pnpm git:dev-done official-site-web # 合并回 dev
pnpm git:deploy-staging # dev → staging pnpm git:deploy-staging # dev → staging
``` ```
**3. 发布生产** **3. 发布生产**(Changesets 独立版本)
```bash ```bash
pnpm git:deploy-release v1.0.0 # staging → release,自动打 tag # 日常开发时记录变更
pnpm changeset
# 选择变更的包和版本类型(major/minor/patch)
# 填写变更说明
# 发布到生产(自动 bump 版本 + 创建独立 tag)
pnpm git:deploy-release
# 或分步执行:
pnpm release # = pnpm changeset:version && pnpm changeset:publish
``` ```
发布后会自动为每个包创建独立 tag,例如:
- `official-site-web@1.2.0`
- `@common/utils@1.0.1`
- `@common/vue-kit@2.0.0`
**4. 紧急修复** **4. 紧急修复**
```bash ```bash
...@@ -526,7 +539,101 @@ pnpm git:refactor-start # 创建/切换到 refactor 分支 ...@@ -526,7 +539,101 @@ pnpm git:refactor-start # 创建/切换到 refactor 分支
pnpm git:refactor-done # 合并到 dev pnpm git:refactor-done # 合并到 dev
``` ```
### 6.6 提交规范 ### 6.7 Changesets 独立版本管理
#### 6.7.1 核心概念
本项目采用 **Changesets** 实现 Monorepo 下子包的独立版本管理:
- **独立版本号**:每个子包独立 bump 版本,互不影响
- **独立 Git Tag**:每个包发布时创建独立 tag(如 `official-site-web@1.2.0`
- **按需构建**:CI/CD 只构建有变更的子包
- **独立回滚**:可单独回滚某个包到指定版本,不影响其他包
#### 6.7.2 日常开发流程
**1. 开发时记录变更**
```bash
pnpm changeset
```
交互式选择:
- 选择哪些包有变更
- 版本类型:`major`(主版本)、`minor`(次版本)、`patch`(补丁)
- 填写变更说明(会生成 CHANGELOG)
这会生成 `.changeset/xxx.md` 文件,例如:
```markdown
---
"official-site-web": minor
"@common/utils": patch
---
feat: 添加首页 banner 功能
```
**2. 只构建变更的包**
```bash
pnpm build:changed
```
自动检测自上次发布以来有变更的包,并只构建它们。
**3. 发布到生产**
```bash
pnpm git:deploy-release
```
执行流程:
1. `changeset version` - 自动 bump 版本号,生成 CHANGELOG
2. 提交版本变更到 staging 分支
3. 合并 staging 到 release
4. `changeset publish` - 发布包并创建独立 tag
5. 推送 release 分支和所有 tag
#### 6.7.3 版本回滚
```bash
# 只回滚官网到 v1.0.0,不影响其他包
git checkout official-site-web@1.0.0 -- packages/official-site-web/
# 回滚后重新构建部署
pnpm --filter=official-site-web build
```
#### 6.7.4 Tag 命名规范
| 包名 | Tag 格式 | 示例 |
|------|---------|------|
| `official-site-web` | `official-site-web@<version>` | `official-site-web@1.2.0` |
| `@common/utils` | `@common/utils@<version>` | `@common/utils@1.0.1` |
| `@common/vue-kit` | `@common/vue-kit@<version>` | `@common/vue-kit@2.0.0` |
#### 6.7.5 CI/CD 集成示例
```yaml
# GitHub Actions 示例
jobs:
build:
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0 # 获取完整 git 历史用于 changeset 检测
- name: Detect changed packages
run: pnpm build:changed
- name: Publish
if: github.ref == 'refs/heads/release'
run: |
pnpm changeset publish
git push --follow-tags
```
### 6.8 提交规范
Commit 信息格式:`type(scope): description` Commit 信息格式:`type(scope): description`
......
...@@ -18,10 +18,16 @@ ...@@ -18,10 +18,16 @@
"git:hotfix-done": "tsx scripts/git-workflow.ts hotfix-done", "git:hotfix-done": "tsx scripts/git-workflow.ts hotfix-done",
"git:refactor-start": "tsx scripts/git-workflow.ts refactor-start", "git:refactor-start": "tsx scripts/git-workflow.ts refactor-start",
"git:refactor-done": "tsx scripts/git-workflow.ts refactor-done", "git:refactor-done": "tsx scripts/git-workflow.ts refactor-done",
"new:project": "tsx scripts/new-project.ts" "new:project": "tsx scripts/new-project.ts",
"changeset": "changeset",
"changeset:version": "changeset version",
"changeset:publish": "changeset publish",
"release": "pnpm changeset:version && pnpm changeset:publish",
"build:changed": "tsx scripts/build-changed.ts"
}, },
"devDependencies": { "devDependencies": {
"@antfu/eslint-config": "^3.9.2", "@antfu/eslint-config": "^3.9.2",
"@changesets/cli": "^2.31.0",
"@commitlint/cli": "^19.5.0", "@commitlint/cli": "^19.5.0",
"@commitlint/config-conventional": "^19.5.0", "@commitlint/config-conventional": "^19.5.0",
"eslint-plugin-format": "^0.1.2", "eslint-plugin-format": "^0.1.2",
......
import { execSync } from 'node:child_process'
import { readFileSync } from 'node:fs'
import { resolve } from 'node:path'
/**
* 构建自上次发布以来有变更的子包
* 原理:通过 changeset status 获取变更包列表,然后逐个构建
*/
function run(cmd: string): string
{
return execSync(cmd, { encoding: 'utf-8' }).trim()
}
function main()
{
console.log('Detecting changed packages since last release...\n')
try
{
// 获取 changeset 状态(JSON 格式)
const statusOutput = run('pnpm changeset status --output=json')
const status = JSON.parse(statusOutput)
if (!status.releases || status.releases.length === 0)
{
console.log('No changed packages detected. Nothing to build.')
return
}
const changedPackages = status.releases.map((r: { name: string }) => r.name)
console.log(`Found ${changedPackages.length} changed package(s):`)
changedPackages.forEach((pkg: string) => console.log(` - ${pkg}`))
console.log('')
// 读取 pnpm-workspace.yaml 获取包路径映射
const workspaceConfig = readFileSync(resolve(process.cwd(), 'pnpm-workspace.yaml'), 'utf-8')
console.log('Building changed packages...\n')
// 使用 pnpm filter 只构建变更的包
for (const pkg of changedPackages)
{
console.log(`\n>>> Building ${pkg}...`)
try
{
run(`pnpm --filter=${pkg} run build`)
console.log(`>>> ${pkg} built successfully`)
}
catch (error: any)
{
console.error(`>>> Failed to build ${pkg}:`, error.message)
process.exit(1)
}
}
console.log('\nAll changed packages built successfully!')
}
catch (error: any)
{
if (error.message?.includes('No unreleased changesets found'))
{
console.log('No unreleased changesets found. Nothing to build.')
}
else
{
console.error('Error detecting changed packages:', error.message)
process.exit(1)
}
}
}
main()
...@@ -118,32 +118,39 @@ const commands = { ...@@ -118,32 +118,39 @@ const commands = {
console.log('Deployed to staging') console.log('Deployed to staging')
}, },
/** /**
* 部署到生产环境(release) * 部署到生产环境(release)- 集成 Changesets 独立版本
* 步骤: * 步骤:
* 1. 检查工作区是否干净 * 1. 检查工作区是否干净
* 2. 切换到 release 分支并拉取最新代码 * 2. 执行 changeset version 自动 bump 版本
* 3. 合并 staging 到 release * 3. 切换到 release 分支并拉取最新代码
* 4. 打版本 tag * 4. 合并 staging 到 release
* 5. 推送 release 分支和 tag 到远程 * 5. 执行 changeset publish 发布(自动为每个包创建独立 tag)
* 6. 推送 release 分支和所有 tag 到远程
*/ */
'deploy-release': () => 'deploy-release': () =>
{ {
checkCleanWorkingTree() checkCleanWorkingTree()
const version = process.argv[3] console.log('Deploying staging to release with Changesets...')
if (!version)
{ // 先执行 changeset version 更新版本号
console.error('Usage: pnpm git:deploy-release <version>') console.log('Running changeset version...')
console.error('Example: pnpm git:deploy-release v1.0.0') run('pnpm changeset version')
process.exit(1)
} // 提交版本变更
console.log(`Deploying staging to release (${version})...`) run('git add .')
run('git commit -m "chore(root): version packages [skip ci]"')
console.log('Switching to release branch...')
run('git checkout release') run('git checkout release')
run('git pull origin release') run('git pull origin release')
run('git merge staging --no-ff --no-edit') 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 release`)
run(`git push origin ${version}`) // 推送所有新创建的 tag
console.log(`Deployed to release with tag ${version}`) console.log('Pushing tags...')
run('git push --follow-tags')
console.log('Deployed to release with independent package tags')
}, },
/** /**
* 创建或切换到 hotfix 分支(唯一分支) * 创建或切换到 hotfix 分支(唯一分支)
...@@ -242,11 +249,18 @@ if (!command || !commands[command]) ...@@ -242,11 +249,18 @@ if (!command || !commands[command])
console.log(' pnpm git:dev-start <project> - Create or switch to dev/<project> branch') console.log(' pnpm git:dev-start <project> - Create or switch to dev/<project> branch')
console.log(' pnpm git:dev-done <project> - Merge dev/<project> into dev') console.log(' pnpm git:dev-done <project> - Merge dev/<project> into dev')
console.log(' pnpm git:deploy-staging - Deploy dev to staging') console.log(' pnpm git:deploy-staging - Deploy dev to staging')
console.log(' pnpm git:deploy-release <version> - Deploy staging to release') console.log(' pnpm git:deploy-release - Deploy staging to release (with Changesets)')
console.log(' pnpm git:hotfix-start - Create or switch to hotfix branch') console.log(' pnpm git:hotfix-start - Create or switch to hotfix branch')
console.log(' pnpm git:hotfix-done - Complete hotfix, merge to release + dev') console.log(' pnpm git:hotfix-done - Complete hotfix, merge to release + dev')
console.log(' pnpm git:refactor-start - Create or switch to refactor branch') console.log(' pnpm git:refactor-start - Create or switch to refactor branch')
console.log(' pnpm git:refactor-done - Complete refactor, merge to dev') console.log(' pnpm git:refactor-done - Complete refactor, merge to dev')
console.log('')
console.log('Changesets Commands:')
console.log(' pnpm changeset - Create a new changeset')
console.log(' pnpm changeset:version - Apply changesets and bump versions')
console.log(' pnpm changeset:publish - Publish packages and create tags')
console.log(' pnpm release - Version + Publish (combined)')
console.log(' pnpm build:changed - Build only changed packages since last release')
process.exit(0) process.exit(0)
} }
......
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