主题切换
CI/CD 构建与部署
预计阅读 20 分钟
版本信息
最后更新:2026-07-09 · 适用基础库:≥ 3.9.0(最低兼容 ≥ 2.30.0) · 微信开发者工具:稳定版 1.06+
持续集成和部署是小游戏开发效率的关键。本章讲解如何搭建自动构建、上传的 CI/CD 流水线,让每一次 git push 都能自动产出可测试的小游戏版本。
微信开发者工具 CLI 上传
微信提供了命令行上传能力,需要配合 CI 密钥使用:
bash
# 开发者工具 CLI 上传命令(V2 子命令语法)
# 开发者工具路径 + upload 子命令 + 项目路径 + 版本号 + 描述
"/Applications/wechatwebdevtools.app/Contents/MacOS/cli" \
upload \
--project /path/to/minigame \
--version 1.0.0 \
--desc "CI 自动构建"获取 CI 密钥
- 登录 微信公众平台。
- 进入「开发 → 开发管理 → 开发设置」。
- 生成「小程序代码上传密钥」,下载
private.xxx.key。 - 将 IP 白名单设置为 CI 服务器的出口 IP。
GitHub Actions 完整配置
项目结构
minigame/
├── .github/
│ └── workflows/
│ └── deploy.yml
├── minigame/
│ ├── game.js
│ └── game.json
├── scripts/
│ └── ci-upload.js # 上传脚本
├── package.json
└── project.config.jsonWorkflow 配置
.github/workflows/deploy.yml:
yaml
name: Deploy WeChat Mini Game
on:
push:
branches: [main, release/*]
workflow_dispatch:
jobs:
build-and-upload:
runs-on: macos-latest # 微信开发者工具仅支持 macOS/Windows
timeout-minutes: 20
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: '20'
- name: Install pnpm
uses: pnpm/action-setup@v2
with:
version: 9
- name: Install dependencies
run: pnpm install
- name: Build game
run: |
# 根据引擎调整构建命令
# Cocos Creator:
# /Applications/CocosCreator.app/Contents/MacOS/CocosCreator \
# --project ./ --build "platform=wechatgame;buildPath=dist"
#
# LayaAir:
# npx layaair2-cmd publish -p wechat-game -o dist
#
# 原生 JS:
pnpm build
- name: Bump version
id: version
run: |
# 基于时间戳生成版本号
VERSION="1.0.$(date +%Y%m%d%H%M)"
echo "version=$VERSION" >> $GITHUB_OUTPUT
- name: Upload to WeChat
env:
WECHAT_APPID: ${{ secrets.WECHAT_APPID }}
WECHAT_PRIVATE_KEY: ${{ secrets.WECHAT_PRIVATE_KEY }}
run: node scripts/ci-upload.js --version ${{ steps.version.outputs.version }}
- name: Upload build artifacts
uses: actions/upload-artifact@v4
with:
name: minigame-build-${{ steps.version.outputs.version }}
path: dist/
retention-days: 30CI 上传脚本
scripts/ci-upload.js:
javascript
const { execSync } = require('child_process')
const fs = require('fs')
const path = require('path')
const APPID = process.env.WECHAT_APPID
const PROJECT_PATH = path.resolve(__dirname, '..', 'dist')
async function main() {
// 1. 验证必要环境变量
if (!APPID) throw new Error('缺少 WECHAT_APPID 环境变量')
if (!process.env.WECHAT_PRIVATE_KEY) throw new Error('缺少 WECHAT_PRIVATE_KEY')
// 2. 写入 CI 密钥文件
const keyDir = path.resolve(__dirname, '..', '.ci-keys')
fs.mkdirSync(keyDir, { recursive: true })
fs.writeFileSync(path.join(keyDir, `private.${APPID}.key`), process.env.WECHAT_PRIVATE_KEY)
// 3. 确认 project.config.json 存在
const projectConfig = path.join(PROJECT_PATH, 'project.config.json')
if (!fs.existsSync(projectConfig)) {
throw new Error('未找到 project.config.json,请确认构建产出了该文件')
}
// 4. 获取当前版本号
const version = process.argv.includes('--version')
? process.argv[process.argv.indexOf('--version') + 1]
: 'dev-' + Date.now()
console.log(`🚀 上传版本: ${version}`)
// 5. 调用微信 CLI 上传
const cliPath = '/Applications/wechatwebdevtools.app/Contents/MacOS/cli'
// Windows 路径:
// const cliPath = 'C:\\Program Files (x86)\\Tencent\\微信web开发者工具\\cli.bat'
try {
execSync(
`"${cliPath}" upload --project "${PROJECT_PATH}" --version "${version}" --desc "CI 自动构建 #${process.env.GITHUB_RUN_NUMBER || 'local'}"`,
{ stdio: 'inherit', timeout: 5 * 60 * 1000 }
)
console.log('✅ 上传成功')
} catch (err) {
console.error('❌ 上传失败', err.message)
process.exit(1)
}
}
main().catch((err) => {
console.error(err)
process.exit(1)
})版本号自动管理
typescript
// 版本号工具:scripts/version.js
import { execSync } from 'node:child_process'
import * as fs from 'node:fs'
const versionType = process.argv[2] || 'patch'
// 从 package.json 读取当前版本
const pkg = JSON.parse(fs.readFileSync('package.json', 'utf8'))
const [major, minor, patch] = pkg.version.split('.').map(Number)
let newVersion: string
switch (versionType) {
case 'major':
newVersion = `${major + 1}.0.0`
break
case 'minor':
newVersion = `${major}.${minor + 1}.0`
break
default:
newVersion = `${major}.${minor}.${patch + 1}`
}
pkg.version = newVersion
fs.writeFileSync('package.json', JSON.stringify(pkg, null, 2) + '\n')
// Git 提交版本号更新
execSync(`git add package.json`)
execSync(`git commit -m "release: v${newVersion}"`)
execSync(`git tag v${newVersion}`)
console.log(`📦 版本号已更新: v${newVersion}`)回滚策略
bash
# 紧急回滚到上一个稳定版本
# 1. 在微信公众平台 → 版本管理 → 找到上一个稳定版本
# 2. 点击「取消发布」撤销当前有问题的版本
# 3. 重新发布上一个版本
# 也可以直接切回 git tag
git checkout v1.0.5
git push origin HEAD:main --force # 极端情况下强制回滚主线CI/CD 最佳实践
- 分支策略 —
main分支自动上传为体验版,release/*分支上传为正式版。 - 构建缓存 — 将
node_modules、Cocos Creator 引擎目录等加入 GitHub Actions cache。 - 密钥安全 — CI 密钥、微信 key 等敏感信息使用 GitHub Secrets 管理,绝不写入代码仓库。
- 通知机制 — 构建成功/失败时发送企业微信或钉钉通知。
- 构件保留 — 每次构建产物保留 30 天,便于回滚和问题排查。
多云部署(CDN 资源)
yaml
# .github/workflows/deploy-cdn.yml
# 将静态资源部署到 CDN(配合热更新使用)
name: Deploy Assets to CDN
on:
push:
paths:
- 'assets/**'
- 'public/**'
jobs:
build-and-deploy:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: '20'
- name: Install pnpm
uses: pnpm/action-setup@v4
with:
version: 9
- name: Install dependencies
run: pnpm install
- name: Build assets
run: pnpm run build:assets # 根据项目实际构建脚本调整
- name: Generate manifest
run: node scripts/generateManifest.js
- name: Upload to Tencent COS
uses: tencentyun/cos-action@v1
with:
secret_id: ${{ secrets.TENCENT_SECRET_ID }}
secret_key: ${{ secrets.TENCENT_SECRET_KEY }}
cos_bucket: game-assets-1250000000
cos_region: ap-guangzhou
local_path: dist/assets
remote_path: /game/assets
- name: Purge CDN cache
env:
TENCENT_SECRET_ID: ${{ secrets.TENCENT_SECRET_ID }}
TENCENT_SECRET_KEY: ${{ secrets.TENCENT_SECRET_KEY }}
run: node scripts/refreshCdn.jsCDN 刷新脚本 scripts/refreshCdn.js(使用腾讯云 SDK 生成签名后调用):
javascript
// scripts/refreshCdn.js
const tencentcloud = require('tencentcloud-sdk-nodejs')
const CdnClient = tencentcloud.cdn.v20180606.Client
const client = new CdnClient({
credential: {
secretId: process.env.TENCENT_SECRET_ID,
secretKey: process.env.TENCENT_SECRET_KEY,
},
region: '',
profile: { signMethod: 'TC3-HMAC-SHA256' },
})
client.RefreshCdnUrl({
Urls: ['https://cdn.example.com/game/manifest.json'],
}).then(() => console.log('CDN 刷新成功'))
.catch((err) => {
console.error('CDN 刷新失败', err)
process.exit(1)
})云厂商差异
不同云厂商的 CDN 刷新接口不同(腾讯云、阿里云、AWS CloudFront、Cloudflare 等),请根据实际使用的云厂商替换刷新脚本。核心原则是:不要直接在 workflow 中硬编码密钥,统一通过 GitHub Secrets 注入。
📝 课后练习
练习 1:GitHub Actions 缓存优化
题目: 为示例 workflow 增加 actions/cache,缓存 node_modules 与 Cocos 引擎目录,减少构建时间。
参考答案要点: 使用 hashFiles('pnpm-lock.yaml') 作为缓存 key;路径包含 ~/.cocos 或引擎安装目录。
练习 2:版本号策略
题目: 为什么建议 main 自动上传体验版、release/* 上传正式版?会带来什么风险?
参考答案: 避免未经验证的代码直接进入正式版审核;风险是分支策略需要团队遵守,且误推 tag 可能导致非预期发布。