刚开始接触GitHub Actions的时候,我心想:这不就是个CI/CD工具吗?跟Jenkins、GitLab CI有啥区别?结果真上手了,连踩三个坑,从配置语法到缓存策略,简直是”看似简单,处处是坑”。今天就把这些血泪经验写出来,保你少走弯路。
先看:基础配置,别被YAML搞晕
GitHub Actions的核心是.github/workflows目录下的YAML文件。我第一个坑就是缩进问题——YAML对空格极其敏感,一个Tab键就能让整个工作流报错。建议直接用VS Code的YAML插件,实时校验语法。
来看个最简单的部署示例:
“yaml
name: Deploy to Production
on:
push:
branches: [main] # 只有main分支推送才触发
jobs:
build-and-deploy:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Setup Node.js
uses: actions/setup-node@v3
with:
node-version: '18'
- run: npm install
- run: npm run build
- name: Deploy to Server
uses: appleboy/scp-action@v0.1.7
with:
host: ${{ secrets.SERVER_HOST }}
username: ${{ secrets.SERVER_USER }}
key: ${{ secrets.SSH_PRIVATE_KEY }}
source: "dist/
*"
target: "/var/www/myapp/"
`
这里的关键点:
— 精确控制触发分支,别让dev分支的代码直接上生产 — 敏感信息放GitHub仓库的Settings > Secrets and variables,别硬
编码
— 社区现成的SCP部署Action,省去手写SSH命令另一个坑:缓存策略,让构建速度提升10倍
刚开始我的工作流每次都要npm install,从拉依赖到装完要3.2秒?错,是3分20秒!后来用了缓存,直接降到0.8秒。
`yaml
- name: Cache Node Modules
uses: actions/cache@v3
with:
path: node_modules
key: ${{ runner.os }}-node-${{ hashFiles('package-lock.json') }}
restore-keys: |
${{ runner.os }}-node-
`
这里用hashFiles(‘package-lock.json’)作为缓存key,只要依赖没变就命中缓存。恢复键restore-keys是备选方案,比如key没匹配到,就尝试用最近的缓存。这个设计真的反人类——官方文档文档不够清晰,我试了三遍才搞明白。
另外,如果项目用pnpm,记得改路径为~/.pnpm-store,不然缓存全浪费。
多环境部署:从staging到production的优雅切换
上线前总得在staging环境测一把。我踩的第三个坑是:把staging和production的配置写成了两个独立文件,重复代码一堆。后来用矩阵策略(matrix)一把梭:
`yaml
name: Deploy to Multiple Environments
on:
push:
branches: [main, develop]
jobs:
deploy:
runs-on: ubuntu-latest
strategy:
matrix:
environment: [staging, production]
include:
- environment: staging
branch: develop
url: https://staging.example.com
- environment: production
branch: main
url: https://example.com
steps:
- uses: actions/checkout@v3
- name: Deploy to ${{ matrix.environment }}
run: |
echo "Deploying to ${{ matrix.url }}"
# 这里加你的部署脚本
# 用secrets区分环境:SERVER_HOST_${{ matrix.environment }}
env:
HOST: ${{ secrets[format('SERVER_HOST_{0}', matrix.environment)] }}
`
这个矩阵配置的好处:
- 一个工作流管两个环境,DRY(Don't Repeat Yourself)
- include
字段让每个环境有独立分支和URL - format
函数动态获取secrets,比如SERVER_HOST_STAGING和SERVER_HOST_PRODUCTION
有个小技巧:staging环境建议加if: github.ref == ‘refs/heads/develop’条件,防止push到main时误触发staging部署。
还有个技巧:Workflow Dispatch,手动触发救急
有时候需要手动跑工作流,比如修复紧急bug后不想等CI自动排队。在on里加个workflow_dispatch就行:
`yaml`
on:
workflow_dispatch:
inputs:
target:
description: 'Choose deployment target'
required: true
default: 'staging'
type: choice
options:
- staging
- production
然后在GitHub Actions界面点"Run workflow"按钮,选目标再执行。这比改代码再push优雅多了。
性能优化:并行与限速
GitHub Actions默认并发执行job,但小心API限速。我试过同时跑10个job,结果全被GitHub API限速了,错误码429。解决方法是加concurrency:
`yaml`
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true
cancel-in-progress: true会自动取消同一分支上排队的工作流,省得浪费资源。另外,对API密集型任务(比如发请求到外部服务),用github-script的core.group来分组执行,或者加retry重试逻辑。
总结一下,你可以立刻用的三个点
把node_modules或vendor目录缓存起来,构建时间从分钟级降到秒级最后提醒:GitHub Actions的免费额度是每个月2000分钟,对个人项目完全够用。但别把cron任务设成每分钟一次,不然月底发现额度被跑光了。血的教训,我上个月就是这么超额的。