自动化篇:用GitHub Actions打造你的“私人前端CI/CD流水线”
自动化篇:用GitHub Actions打造你的“私人前端CI/CD流水线”
引子:那个让你成为“机器人”的重复性工作
作为工程师,我们热爱创造,热爱解决复杂的问题。但日常工作中,有相当一部分时间,却被一些高度重复、毫无创造性的“机器人”工作所占据:
- 每次提交代码前,都得记得在本地运行一遍
lint
和test
,确保没有引入低级错误。 - 当一个功能开发完成,准备发布时,你需要手动执行
build
命令,然后手动将产物上传到服务器,或者手动发布到NPM。 - 在Code Review时,你无法确定同事提交的代码,是否真的能在干净的环境中跑通所有测试。
这些手动操作,不仅繁琐、耗时,更是错误的温床。你可能会忘记运行测试,可能会在上传文件时操作失误,可能会因为本地环境与线上环境的差异而导致部署失败。
我们需要一个真正的“机器人”,一个不知疲倦、绝对忠诚、严格执行指令的“自动化助理”,来把我们从这些重复性劳动中解放出来。这个“机器人”,就是CI/CD(持续集成/持续部署)。
而在当今的开源世界,尤其是对于托管在GitHub上的项目来说,实现CI/CD的最佳工具,无疑就是GitHub Actions。
GitHub Actions是GitHub官方推出的、深度集成在代码仓库中的自动化工作流服务。它允许你通过在仓库中添加一个简单的YAML
配置文件,来响应各种GitHub事件(如push
, pull_request
),并在GitHub提供的虚拟服务器上,执行你预设的一系列命令。
今天,我们将一起动手,为我们之前在Monorepo中构建的项目,打造一条专属的、功能完备的CI/CD流水线。这条流水线将成为我们项目的“质量门卫”和“自动化发布官”。
第一幕:CI/CD的核心理念
在动手之前,我们先要理解CI/CD这两个词的含义。
-
持续集成(Continuous Integration, CI)
CI的核心思想是:频繁地、自动化地将所有开发者的代码变更,集成到主干分支中。
每一次集成,都会自动触发构建和测试。这能让我们尽早地发现代码冲突和集成错误,避免问题被隐藏到项目后期,导致修复成本剧增。一个良好的CI流程,能确保主干分支的代码,在任何时候都处于一个“基本可用”的状态。 -
持续部署(Continuous Deployment, CD)
CD是CI的延伸。它的核心思想是:将通过了所有自动化测试的代码变更,自动地、安全地部署到生产环境。
这意味着,从git push
到代码上线,整个过程可以是完全自动化的,无需人工干预。这极大地加快了价值交付的速度,实现了真正的“小步快跑,快速迭代”。
我们今天的目标,是先完美地实现CI,并为CD打下基础。
第二幕:GitHub Actions的核心概念
要使用GitHub Actions,你需要了解几个核心概念。它们都在一个.yml
文件中定义。
- Workflow(工作流): 整个自动化过程的蓝图。一个仓库可以有多个工作流。每个工作流对应一个
.yml
文件。 - Event(事件): 触发工作流运行的条件。比如
on: [push, pull_request]
就表示当有代码push
或创建pull_request
时,触发该工作流。 - Job(任务): 一个工作流由一个或多个任务组成。一个任务是一系列在同一个虚拟环境(Runner)中运行的步骤。默认情况下,多个任务是并行执行的。
- Runner(运行器): 执行任务的虚拟服务器。GitHub提供了多种预装好常用软件的虚拟环境(如
ubuntu-latest
,windows-latest
,macos-latest
)。 - Step(步骤): 任务中的最小执行单元。一个步骤可以是一个shell命令(
run: npm install
),也可以是引用一个预设好的“动作”(uses: actions/checkout@v3
)。 - Action(动作): 一个可复用的、独立的代码单元,用来执行常见的、标准化的任务。比如
actions/checkout
这个动作,专门用来拉取你的仓库代码到虚拟环境中;actions/setup-node
则专门用来安装指定版本的Node.js。
理解了这些,我们就可以开始编写我们的第一个Workflow文件了。
第三幕:实战!编写我们的CI流水线
我们的目标是创建一个CI流水线,它能在每次向main
分支推送代码,或创建指向main
分支的Pull Request时,自动完成以下检查:
- 拉取最新代码。
- 安装指定版本的Node.js和pnpm。
- 安装项目所有依赖。
- 运行代码风格检查(Lint)。
- 运行所有自动化测试。
步骤一:创建Workflow文件
在你的项目根目录下,创建.github/workflows/
文件夹。然后,在其中创建一个名为ci.yml
的文件。
.github/workflows/ci.yml
# 工作流的名称,会显示在GitHub的Actions页面name: CSDN Invisible App CI# 触发工作流的事件on: # 当向main分支push代码时触发 push: branches: [ main ] # 当创建或更新一个指向main分支的Pull Request时触发 pull_request: branches: [ main ]# 定义工作流中的所有任务jobs: # 我们定义一个名为\'build-and-test\'的任务 build-and-test: # 指定运行此任务的虚拟环境 runs-on: ubuntu-latest # 定义此任务的一系列步骤 steps: # 第一步:检出代码 # 使用官方的actions/checkout@v3动作,将仓库代码拉取到Runner中 - name: Checkout repository uses: actions/checkout@v3 # 第二步:安装pnpm # 使用官方的pnpm/action-setup动作 - name: Setup pnpm uses: pnpm/action-setup@v2 with: version: 8 # 指定pnpm版本 # 第三步:安装Node.js # 使用官方的actions/setup-node@v3动作 - name: Setup Node.js uses: actions/setup-node@v3 with: node-version: \'18\' # 指定Node.js版本 cache: \'pnpm\' # 关键!让pnpm的依赖被缓存,后续运行会更快 # 第四步:安装依赖 # \'run\'关键字用于执行shell命令 - name: Install dependencies run: pnpm install # 第五步:运行Lint检查 # 我们需要在根package.json中预先定义好\"lint\"脚本 - name: Run Lint run: pnpm lint # 第六步:运行测试 # 我们需要在根package.json中预先定义好\"test\"脚本 - name: Run Tests run: pnpm test
步骤二:在package.json
中添加脚本
确保你的根package.json
中,包含了CI流程需要用到的脚本:
// 根目录 /package.json{ \"scripts\": { \"lint\": \"eslint . --ext .ts,.tsx\", // 假设你配置了ESLint \"test\": \"vitest run\" // 使用\'vitest run\'而不是\'vitest\',确保在CI环境中能自动退出 }}
步骤三:提交并观察
现在,将.github/workflows/ci.yml
和修改后的package.json
提交到你的GitHub仓库。
然后,尝试创建一个新的分支,修改一些代码,再创建一个指向main
分支的Pull Request。
神奇的事情发生了!
你会看到在你的Pull Request页面下方,出现了一个“Checks”区域。GitHub Actions已经自动检测到了你的ci.yml
文件,并开始运行你定义的工作流。
你可以点击“Details”链接,实时地查看每一步的执行日志。
- 如果所有步骤都成功执行(命令退出码为0),整个Job会成功,PR页面上会显示一个醒目的绿色对勾。
- 如果任何一步失败(比如Lint不通过,或某个测试用例失败),该步骤会立即中止,整个Job会失败,PR页面上会显示一个红色大叉。
这条CI流水线,现在成为了你项目代码质量的“忠诚守卫”。任何不符合规范、破坏了既有功能的代码,都休想在无人察觉的情况下,被合并到主分支中。
第四幕:展望CD - 自动化部署
有了CI作为基础,实现CD就水到渠成了。我们可以再定义一个deploy.yml
工作流,或者在ci.yml
中增加一个新的deploy
任务。
这个任务可以这样设计:
# 在ci.yml中增加一个deploy任务jobs: build-and-test: # ... 省略 ... deploy: # \'needs\'关键字表示,此任务必须在\'build-and-test\'成功后才能开始 needs: build-and-test runs-on: ubuntu-latest # \'if\'条件表示,仅当事件是向main分支push时才运行此任务 # (我们不希望每次PR都触发部署) if: github.event_name == \'push\' && github.ref == \'refs/heads/main\' steps: - name: Checkout repository uses: actions/checkout@v3 # ... 安装Node.js和pnpm的步骤 ... - name: Build all packages run: pnpm --recursive build - name: Publish to NPM # 这里会使用一个专门的action来发布到NPM # 需要配置NPM的认证token uses: JS-DevTools/npm-publish@v1 with: token: ${{ secrets.NPM_TOKEN }} # 从仓库的Secrets中读取token - name: Deploy to Server # 这里会使用一个action(如appleboy/ssh-action) # 通过SSH连接到你的服务器,并执行部署脚本 uses: appleboy/ssh-action@master with: host: ${{ secrets.SERVER_HOST }} username: ${{ secrets.SERVER_USERNAME }} key: ${{ secrets.SSH_PRIVATE_KEY }} script: | cd /var/www/my-app git pull pm2 restart my-app
这个deploy
任务演示了如何在代码合并到主分支后,自动地构建项目、发布到NPM,并通过SSH连接到服务器来完成最终的部署。通过配置secrets
,我们可以安全地管理各种敏感的认证信息。
结论:让机器人做机器人该做的事
GitHub Actions是一个极其强大的工具,它将复杂的CI/CD流程,简化为了编写一个声明式的YAML文件。
通过为我们的项目配置CI/CD流水线,我们获得了:
- 质量保证的自动化:每一次代码提交,都经过了严格的、一致的质量检查。
- 开发效率的提升:将开发者从重复的、易错的手动操作中解放出来,专注于创造性的工作。
- 更快的反馈循环:在代码合入主干前,就能发现潜在的问题,极大地降低了修复成本。
- 团队协作的规范化:CI/CD流程成为了团队统一的开发规范和质量基线。
掌握CI/CD和GitHub Actions,是每一位现代工程师必备的核心技能。它标志着你开始从一个只关心“代码实现”的开发者,向一个关注“软件交付全周期”的工程化专家转变。
在下一章,也是本系列的终章,我们将进行最后一次思想的升华。我们将探讨技术之外,那些真正决定一个工程师能走多远的终极软技能:编写“可被理解”的代码。