Appearance
Nuxt.js 项目 GitHub Actions 自动化 CI/CD 实践(一):构建与发布
前言
在现代化的前端开发流程中,自动化 CI/CD 已成为标配。然而,很多开发者在手动完成以下操作时感到繁琐:
- 每次发布都要手动执行构建、打包
- 构建产物需要手动上传到 Release
- 部署到服务器需要手动拷贝文件、重启服务
- 无法感知 CI/CD 的执行状态
为了解决这些痛点,本系列文章将带你从零开始,使用 GitHub Actions 为 Nuxt.js 项目搭建一套完整的自动化 CI/CD 流水线。
本系列文章共分为三部分:
| 部分 | 目标 | 预期效果 |
|---|---|---|
| 第一部分(本文) | 实现创建 Release 时自动构建、打包并上传到 GitHub Release | 创建 Release 后,自动生成 tar.gz/zip 附件,并自动更新 package.json 版本号 |
| 第二部分 | 实现自动部署到远程服务器 | 构建产物自动传输到服务器,通过 PM2 重启服务,完成上线 |
| 第三部分 | 实现 CI/CD 邮箱通知 | 构建和部署成功后发送邮件通知,失败时及时告警 |
达成效果:
当你在 GitHub 上创建一个新的 Release 时,整个流程将全自动运行:
- 代码自动检出 → 依赖安装 → 代码检查 → 项目构建
- 生成压缩包并上传到 Release 页面
- 构建产物自动传输到服务器
- PM2 自动重启服务
- 最终你将收到一封包含部署结果的邮件
NOTICE
本文为系列文章的第一部分,聚焦于构建与发布环节。
第一部分目标
当你在 GitHub 上创建一个新的 Release 时,自动完成:
- 代码检出与依赖安装
- 代码检查(Lint + Typecheck)
- Nuxt.js 项目构建
- 生成压缩包(tar.gz + zip)
- 上传到 GitHub Release
- 保存构建产物供后续部署使用
- 自动提交版本号更新
目录结构
.github/
└── workflows/
└── release-bump.yaml完整配置文件
yaml
name: Auto Bump Version on Release
on:
release:
types: [created] # 仅在创建新 Release 时触发
jobs:
# Job 1: 构建和发布
build-and-release:
runs-on: ubuntu-latest
permissions:
contents: write # 允许推送代码和创建 Release
outputs:
version: ${{ steps.extract-version.outputs.version }}
project_name: ${{ steps.project-name.outputs.project_name }} # 传递给后续部署 Job
steps:
# 1. 检出代码
- uses: actions/checkout@v6
with:
ref: ${{ github.event.release.target_commitish }}
fetch-depth: 0
# 2. 安装 pnpm
- name: Install pnpm
uses: pnpm/action-setup@v4
# 3. 设置 Node.js
- name: Setup Node.js
uses: actions/setup-node@v5
with:
node-version: 20
cache: 'pnpm'
# 4. 安装依赖
- name: Install dependencies
run: pnpm install --frozen-lockfile
# 5. 代码检查
- name: Lint
run: pnpm run lint
# 6. 类型检查
- name: Typecheck
run: pnpm run typecheck
# 7. 提取版本号
- name: Extract Version
id: extract-version
run: |
TAG=${GITHUB_REF#refs/tags/}
VERSION=$(echo "$TAG" | sed 's/^v//')
echo "version=$VERSION" >> $GITHUB_OUTPUT
# 8. 提取项目名称(从 package.json 动态获取)
- name: Extract Project Name
id: project-name
run: |
PROJECT_NAME=$(node -p "require('./package.json').name")
echo "project_name=$PROJECT_NAME" >> $GITHUB_OUTPUT
# 9. 更新 package.json 版本号
- name: Update Version
run: |
pnpm version ${{ steps.extract-version.outputs.version }} --no-git-tag-version
cat package.json | grep version
# 10. 构建 Nuxt.js 项目
- name: Build Nuxt.js
env:
NODE_OPTIONS: "--max-old-space-size=4096"
run: pnpm run build
# 11. 创建压缩包(使用动态项目名称)
- name: Create Distribution Archive
run: |
if [ ! -d ".output" ]; then
echo "Error: .output directory not found!"
exit 1
fi
echo "Contents of .output directory:"
ls -la .output/
PROJECT_NAME="${{ steps.project-name.outputs.project_name }}"
VERSION="${{ steps.extract-version.outputs.version }}"
tar -czf ${PROJECT_NAME}-${VERSION}.tar.gz -C .output .
cd .output && zip -r ../${PROJECT_NAME}-${VERSION}.zip . && cd ..
echo "Archive created successfully!"
ls -la *.tar.gz *.zip
# 12. 上传到 GitHub Release
- name: Upload Release Assets
uses: ncipollo/release-action@v1
with:
artifacts: "./${{ steps.project-name.outputs.project_name }}-${{ steps.extract-version.outputs.version }}.tar.gz,./${{ steps.project-name.outputs.project_name }}-${{ steps.extract-version.outputs.version }}.zip"
allowUpdates: true
omitBody: true
omitName: true
token: ${{ secrets.ACCESS_TOKEN }}
# 13. 上传构建产物供后续 Job 使用
- name: Upload Build Artifact
uses: actions/upload-artifact@v7
with:
name: build-output
path: ${{ steps.project-name.outputs.project_name }}-${{ steps.extract-version.outputs.version }}.tar.gz
retention-days: 1
if-no-files-found: error
overwrite: true
# 14. 提交版本号变更
- name: Commit Changes
run: |
git config user.name "GitHub Actions"
git config user.email "actions@github.com"
git add package.json pnpm-lock.yaml
git commit -m "chore(release): bump to ${{ steps.extract-version.outputs.version }} [skip ci]"
git push origin HEAD:${{ github.event.release.target_commitish }}必需的 Secrets 配置
在 GitHub 仓库的 Settings -> Secrets and variables -> Actions 中添加:
| Secret 名称 | 说明 |
|---|---|
ACCESS_TOKEN | 具有写入权限的 Personal Access Token(用于上传 Release 资产) |
💡 注意
默认的 GITHUB_TOKEN 在某些场景下权限不足,建议使用 Fine-grained Personal Access Token,赋予 contents: write 和 actions: write 权限。
关键步骤详解
1. 触发条件
yaml
on:
release:
types: [created]只在用户手动创建 Release 时触发,避免每次 push 都触发构建。
2. 版本号提取
yaml
TAG=${GITHUB_REF#refs/tags/}
VERSION=$(echo "$TAG" | sed 's/^v//')从 Release 标签(如 v1.0.0)中提取纯净版本号(1.0.0)。
3. 项目名称提取
yaml
PROJECT_NAME=$(node -p "require('./package.json').name")从 package.json 中动态读取项目名称,避免在配置文件中硬编码,提高配置的通用性和可维护性。
💡 延伸说明:提取的
project_name会通过 Job 的outputs字段传递给后续的部署 Job(第二部分内容)。这样在部署阶段可以动态获取项目名称,用于构建服务器上的部署路径和文件名,实现 Job 间的数据共享与解耦。
4. 版本号更新
yaml
pnpm version ${{ steps.extract-version.outputs.version }} --no-git-tag-version更新 package.json 中的 version 字段,但不创建 git tag(tag 已由 Release 创建)。
5. 构建优化
yaml
env:
NODE_OPTIONS: "--max-old-space-size=4096"分配 4GB 内存给 Node.js,避免大型项目构建时内存溢出。
6. 压缩包创建
yaml
PROJECT_NAME="${{ steps.project-name.outputs.project_name }}"
VERSION="${{ steps.extract-version.outputs.version }}"
tar -czf ${PROJECT_NAME}-${VERSION}.tar.gz -C .output .打包 .output 目录的内容,而不是目录本身。这样解压后直接得到文件,无需额外处理目录层级。
7. 构建产物保存
yaml
- name: Upload Build Artifact
uses: actions/upload-artifact@v7
with:
name: build-output
path: ${{ steps.project-name.outputs.project_name }}-${{ steps.extract-version.outputs.version }}.tar.gz将 tar.gz 文件保存为 artifact,供后续部署 Job 使用。使用 overwrite: true 避免名称冲突。
💡 踩坑提醒
upload-artifact@v4 上传目录时存在 bug,建议上传文件(如 tar.gz)而不是目录。
运行流程
- 在 GitHub 仓库中创建新的 Release,填写版本号(如
v0.4.0-alpha11) - GitHub Actions 自动触发 workflow
- 执行检出 → 安装依赖 → 检查 → 构建 → 打包 → 上传
- 版本号自动更新并推送到仓库
- Release 页面出现可下载的
tar.gz和zip附件
常见问题
Q: upload-artifact 报错 "No files found"?
A: 使用 v7 版本并上传文件而不是目录:
yaml
path: ${{ steps.project-name.outputs.project_name }}-${{ steps.extract-version.outputs.version }}.tar.gzQ: 为什么需要 ACCESS_TOKEN?
A: 默认的 GITHUB_TOKEN 在某些权限场景下无法上传 Release 资产,使用 Personal Access Token 更可靠。
Q: fetch-depth: 0 的作用?
A: 获取完整的 git 历史,确保版本号提交能正确推送。
总结
本文完成了系列文章的第一部分——构建与发布的自动化。通过配置 GitHub Actions,我们实现了:
- 创建 Release 时自动触发构建
- 自动生成 tar.gz 和 zip 压缩包
- 自动上传到 Release 页面
- 自动更新 package.json 版本号
下一篇预告:
第二部分将讲解如何将构建产物自动部署到远程服务器,涵盖:
- 使用
scp-action传输文件到服务器 - 通过
ssh-action执行远程命令 - 使用 PM2 重启服务实现无缝上线
- 完整的部署脚本解析
敬请期待!
💡 确认点
运行此 workflow 后,Release 页面应有 tar.gz 和 zip 附件,且仓库中 package.json 的版本号已更新。