从零搭建CI集成流程并解决实际项目中的痛点问题
我的写法,亲测靠谱
先说下我的背景吧。这几年我大大小小的项目都搞过CI集成,从GitHub Actions到GitLab CI,再到Jenkins,踩过的坑数都数不过来。最开始也是照着文档生搬硬套,结果经常是本地跑得好好的,一到CI就各种报错。
经过几次折腾,我总结出一套比较靠谱的写法:
name: CI Pipeline
on:
push:
branches:
- main
pull_request:
branches:
- main
jobs:
build:
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v3
- name: Setup Node.js
uses: actions/setup-node@v3
with:
node-version: '16'
- name: Install dependencies
run: npm ci
- name: Run tests
run: npm test
- name: Build project
run: npm run build
这套配置看似简单,但每个细节都是踩坑踩出来的。比如用npm ci而不是npm install,前者会严格根据package-lock.json安装依赖,避免版本浮动问题。还有就是把测试和构建分开,这样即使测试挂了也能快速定位问题。
这几种错误写法,别再踩坑了
讲真,网上很多教程都有问题,我刚开始就被坑惨了。最常见的几个错误写法:
1. 直接用npm install。这个命令会在node_modules不存在时重新生成,而且可能会更新依赖版本,导致本地和CI环境不一致。我就遇到过好几次,本地没问题,CI上各种奇怪的错误。
2. 把所有命令揉在一个脚本里:
npm install && npm test && npm run build
看着简洁,实际上只要前面一个命令失败,后面的都不会执行,排查问题特别麻烦。记得有次build失败了,我还以为是测试的问题,折腾了大半天。
3. 不锁定Node版本:
steps:
- name: Setup Node.js
uses: actions/setup-node@v3
这种写法太随意了,不同版本的Node行为差异可能很大。有次CI莫名其妙报错,最后发现是因为默认用了新版本的Node,而项目代码还没适配。
实际项目中的坑
在真实项目中,有几个地方特别容易出问题:
首先是缓存处理。很多人觉得加个缓存能加快构建速度,但其实很容易出问题。比如这种写法:
- name: Cache node modules
uses: actions/cache@v3
with:
path: ~/.npm
key: ${{ runner.os }}-node-${{ hashFiles('**/package-lock.json') }}
`>
<p>看着没问题,但如果你的项目依赖了二进制模块(比如sharp),缓存可能会导致构建失败。我就遇到过这种情况,最后只能放弃缓存node_modules,改用pnpm的store缓存。</p>
<p>其次是环境变量管理。千万别把敏感信息直接写在配置文件里:</p></code></pre>yaml
env:
API_KEY: "your_api_key_here"
<pre class="pure-highlightjs line-numbers language-none"><code class="no-highlight language-none"><p>正确的方式是使用GitHub的Secrets功能:</p></code></pre>yaml
env:
API_KEY: ${{ secrets.API_KEY }}
`>
还有就是要注意不同操作系统的差异。比如在Windows上路径分隔符是反斜杠,而在Linux上是正斜杠。建议统一使用path模块来处理路径问题:
const path = require('path');
const filePath = path.join(__dirname, 'dist', 'index.html');
另外提醒下,不要滥用matrix。虽然并行构建看起来很美好,但如果项目依赖关系复杂,可能会导致各种奇怪的问题。我就试过因为matrix配置不当,导致构建结果不一致。
一些实用的小技巧
分享几个我觉得特别实用的小技巧:
1. 添加超时检测。有时候第三方服务响应慢,会导致CI卡住。可以在调用API时设置timeout:
const axios = require('axios');
async function fetchData() {
try {
const response = await axios.get('https://jztheme.com/api/data', { timeout: 5000 });
return response.data;
} catch (error) {
console.error('Request timed out');
process.exit(1);
}
}
2. 善用continue-on-error。对于非关键步骤,可以允许失败继续执行:
- name: Run optional script
run: npm run optional-script
continue-on-error: true
3. 添加构建产物大小检查。防止不小心引入过大依赖:
du -sh dist/
以上是我总结的最佳实践
CI集成这块水挺深的,我也还在不断学习中。上面这些经验都是实战中踩坑踩出来的,希望能帮到大家。有更好的方案欢迎评论区交流,或者你有什么特殊场景的需求也可以留言,我们一起讨论。

暂无评论