基于 React Native/Expo 项目的持续集成(CI)最佳实践配置指南

目录


概述

本项目的 CI 配置包含以下核心功能:

🔍 检查项

检查项 触发时机 用途
TypeScript 类型检查 Pre-commit 确保类型安全
ESLint 代码规范 Pre-commit 代码风格和质量
Prettier 代码格式化 Pre-commit 统一代码格式
提交消息规范 Commit-msg 规范化 Git 提交
分支保护 Pre-commit 防止直接提交到主分支
依赖自动安装 Post-merge 保持依赖同步
单元测试 手动/CI 确保代码质量

📦 依赖包

json 复制代码
{
  "husky": "^9.1.5",
  "lint-staged": "^15.2.9",
  "@commitlint/cli": "^19.2.2",
  "@commitlint/config-conventional": "^19.2.2",
  "eslint": "^9.28.0",
  "prettier": "^3.3.3",
  "typescript": "^5.8.3",
  "jest": "^29.7.0"
}

Git Hooks 配置

1. Husky 设置

安装和初始化

bash 复制代码
# 安装依赖
pnpm add -D husky

# 初始化 husky
pnpm exec husky init

目录结构

bash 复制代码
.husky/
├── _/                  # Husky 内部文件
├── common.sh          # 共享脚本
├── pre-commit         # 提交前检查
├── commit-msg         # 提交消息检查
└── post-merge         # 合并后处理

2. Pre-commit Hook

文件:.husky/pre-commit

bash 复制代码
. "$(dirname "$0")/common.sh"

echo "===\n>> Checking branch name..."

# 分支保护
if [[ -z $SKIP_BRANCH_PROTECTION ]]; then
    BRANCH=$(git rev-parse --abbrev-ref HEAD)
    PROTECTED_BRANCHES="^(main|master)"

    if [[ $BRANCH =~ $PROTECTED_BRANCHES ]]; then
        echo ">> Direct commits to the $BRANCH branch are not allowed."
        exit 1
    fi
fi

echo ">> Linting your files and fixing them if needed..."

# TypeScript 类型检查
pnpm type-check

# 代码规范检查和自动修复
pnpm lint-staged

功能:

  • ✅ 阻止直接提交到 main/master 分支
  • ✅ 运行 TypeScript 类型检查
  • ✅ 对暂存文件运行 ESLint 和 Prettier

3. Commit-msg Hook

文件:.husky/commit-msg

bash 复制代码
pnpm commitlint --edit $1

功能:

  • ✅ 验证提交消息格式符合 Conventional Commits 规范

4. Post-merge Hook

文件:.husky/post-merge

bash 复制代码
function changed {
    git diff --name-only HEAD@{1} HEAD | grep "^$1" >/dev/null 2>&1
}

echo 'Checking for changes in pnpm-lock.yaml...'

if changed 'pnpm-lock.yaml'; then
    echo "📦 pnpm-lock.yaml changed. Installing dependencies..."
    pnpm install
fi

echo 'You are up to date :)'

功能:

  • ✅ 检测依赖变化,自动运行 pnpm install

5. Common Shell 脚本

文件:.husky/common.sh

bash 复制代码
command_exists() {
    command -v "$1" >/dev/null 2>&1
}

# Windows Git Bash 兼容性
if command_exists winpty && test -t 1; then
    exec </dev/tty
fi

代码质量检查

1. Lint-staged 配置

文件:lint-staged.config.js

javascript 复制代码
module.exports = {
  // TypeScript/JavaScript 文件
  '**/*.{js,jsx,ts,tsx}': (filenames) => [
    `npx eslint --fix ${filenames
      .map((filename) => `"${filename}"`)
      .join(' ')}`,
  ],

  // Markdown 和 JSON 文件
  '**/*.(md|json)': (filenames) =>
    `npx prettier --write ${filenames
      .map((filename) => `"${filename}"`)
      .join(' ')}`,

  // 翻译文件特殊处理
  'src/translations/*.(json)': (filenames) => [
    `npx eslint --fix ${filenames
      .map((filename) => `"${filename}"`)
      .join(' ')}`,
  ],
};

功能:

  • 只检查暂存的文件(提高性能)
  • 自动修复可修复的问题
  • 支持多种文件类型

2. Commitlint 配置

文件:commitlint.config.js

javascript 复制代码
module.exports = {
  extends: ['@commitlint/config-conventional'],
};

提交消息格式:

arduino 复制代码
<type>: <description>

[optional body]

[optional footer]

允许的 type 类型:

Type 说明 示例
feat 新功能 feat: 添加用户登录功能
fix Bug 修复 fix: 修复登录页面闪退问题
docs 文档更新 docs: 更新 API 文档
style 代码格式(不影响功能) style: 格式化代码缩进
refactor 重构 refactor: 重构用户服务模块
perf 性能优化 perf: 优化列表渲染性能
test 测试相关 test: 添加登录组件单元测试
chore 构建/工具相关 chore: 更新依赖版本
ci CI 配置 ci: 添加 GitHub Actions 配置
build 构建系统 build: 优化打包配置
revert 回滚提交 revert: 回滚登录功能

3. ESLint 配置

文件:eslint.config.mjs

核心规则:

javascript 复制代码
export default defineConfig([
  // 全局忽略
  globalIgnores([
    'dist/*',
    'node_modules',
    'coverage',
    'android',
    'ios',
    '.expo',
  ]),

  // 核心规则
  {
    rules: {
      'max-params': ['error', 3], // 最多3个参数
      'max-lines-per-function': ['error', 300], // 函数最多300行
      'unicorn/filename-case': [
        'error',
        {
          case: 'kebabCase',
        },
      ], // kebab-case 命名
      'simple-import-sort/imports': 'error', // 自动排序 imports
      'unused-imports/no-unused-imports': 'error', // 禁止未使用的导入
      'import/no-cycle': ['error'], // 禁止循环依赖
      '@typescript-eslint/consistent-type-imports': [
        'warn',
        { prefer: 'type-imports' },
      ], // 强制使用 type imports
    },
  },

  // TypeScript 特定配置
  {
    files: ['**/*.ts', '**/*.tsx'],
    languageOptions: {
      parser: parser,
      parserOptions: {
        project: './tsconfig.json',
      },
    },
  },

  // 测试文件配置
  {
    files: ['**/__tests__/**/*', '**/*.test.*'],
    plugins: { 'testing-library': testingLibrary },
  },
]);

4. Prettier 配置

文件:.prettierrc.js

javascript 复制代码
module.exports = {
  singleQuote: true, // 使用单引号
  endOfLine: 'auto', // 自动行尾符
  trailingComma: 'es5', // ES5 尾随逗号
};

5. TypeScript 配置

文件:tsconfig.json

json 复制代码
{
  "extends": "expo/tsconfig.base",
  "compilerOptions": {
    "strict": true, // 严格模式
    "baseUrl": ".",
    "paths": {
      "@/*": ["./src/*"] // 路径别名
    },
    "esModuleInterop": true,
    "checkJs": true // 检查 JS 文件
  },
  "exclude": ["node_modules", "android", "ios"]
}

6. Jest 配置

文件:jest.config.js

javascript 复制代码
module.exports = {
  preset: 'jest-expo',
  setupFilesAfterEnv: ['<rootDir>/jest-setup.ts'],
  testMatch: ['**/?(*.)+(spec|test).ts?(x)'],

  // 代码覆盖率收集
  collectCoverageFrom: [
    'src/**/*.{ts,tsx}',
    '!**/coverage/**',
    '!**/node_modules/**',
  ],

  // 报告器
  reporters: [
    'default',
    ['github-actions', { silent: false }],
    'summary',
    [
      'jest-junit',
      {
        outputDirectory: 'coverage',
        outputName: 'jest-junit.xml',
      },
    ],
  ],

  // 路径映射
  moduleNameMapper: {
    '^@/(.*)$': '<rootDir>/src/$1',
  },
};

配置文件详解

Package.json Scripts

json 复制代码
{
  "scripts": {
    // 代码检查
    "lint": "eslint . --ext .js,.jsx,.ts,.tsx",
    "type-check": "tsc --noEmit",
    "lint:translations": "eslint ./src/translations/ --fix --ext .json",

    // 测试
    "test": "jest",
    "test:ci": "pnpm run test --coverage",
    "test:watch": "pnpm run test --watch",

    // 完整检查
    "check-all": "pnpm run lint && pnpm run type-check && pnpm run lint:translations && pnpm run test",

    // Git hooks
    "prepare": "husky"
  }
}

快速开始

在新项目中配置 CI

1. 安装依赖

bash 复制代码
pnpm add -D husky lint-staged @commitlint/cli @commitlint/config-conventional
pnpm add -D eslint prettier typescript jest
pnpm add -D @typescript-eslint/parser @typescript-eslint/eslint-plugin
pnpm add -D eslint-plugin-prettier eslint-config-prettier

2. 初始化 Husky

bash 复制代码
pnpm exec husky init

3. 创建配置文件

复制以下配置文件到项目根目录:

bash 复制代码
# 必需的配置文件
├── .husky/
│   ├── pre-commit
│   ├── commit-msg
│   ├── post-merge
│   └── common.sh
├── lint-staged.config.js
├── commitlint.config.js
├── eslint.config.mjs
├── .prettierrc.js
├── tsconfig.json
└── jest.config.js

4. 添加 NPM Scripts

package.json 中添加:

json 复制代码
{
  "scripts": {
    "prepare": "husky",
    "lint": "eslint . --ext .js,.jsx,.ts,.tsx",
    "type-check": "tsc --noEmit",
    "test": "jest",
    "check-all": "pnpm run lint && pnpm run type-check && pnpm run test"
  }
}

5. 设置 Git Hooks 可执行权限

bash 复制代码
chmod +x .husky/pre-commit
chmod +x .husky/commit-msg
chmod +x .husky/post-merge

6. 测试配置

bash 复制代码
# 测试 lint
pnpm lint

# 测试类型检查
pnpm type-check

# 测试提交(会触发所有 hooks)
git add .
git commit -m "test: 测试 CI 配置"

提交代码流程

标准提交流程

bash 复制代码
# 1. 确保在功能分支上
git checkout -b feat/your-feature-name

# 2. 添加修改的文件
git add .

# 3. 提交(使用规范的提交消息)
git commit -m "feat: 添加新功能"

# 自动触发:
# ✓ 分支检查
# ✓ TypeScript 类型检查
# ✓ ESLint 自动修复
# ✓ Prettier 格式化
# ✓ 提交消息格式验证

绕过检查(仅紧急情况)

bash 复制代码
# 跳过分支保护
SKIP_BRANCH_PROTECTION=1 git commit -m "fix: 紧急修复"

# 跳过所有 pre-commit hooks
git commit --no-verify -m "fix: 紧急修复"

⚠️ 不推荐在正常开发中使用


常见问题

Q1: 提交时报错 "Direct commits to main branch are not allowed"

原因: 你在 mainmaster 分支上提交

解决方案:

bash 复制代码
# 创建新分支
git checkout -b feat/your-feature

# 或者跳过检查(不推荐)
SKIP_BRANCH_PROTECTION=1 git commit -m "your message"

Q2: TypeScript 类型检查失败

原因: 代码存在类型错误

解决方案:

bash 复制代码
# 查看具体错误
pnpm type-check

# 修复类型错误后重新提交

Q3: ESLint 错误无法自动修复

原因: 某些规则需要手动修复

解决方案:

bash 复制代码
# 查看详细错误
pnpm lint

# 尝试自动修复
pnpm lint --fix

# 手动修复剩余问题

Q4: 提交消息格式错误

错误示例:

bash 复制代码
git commit -m "add new feature"  # ❌ 缺少 type
git commit -m "Add new feature"  # ❌ 首字母大写

正确示例:

bash 复制代码
git commit -m "feat: add new feature"      # ✅
git commit -m "fix: resolve login issue"   # ✅

Q5: Husky hooks 不生效

解决方案:

bash 复制代码
# 重新安装 husky
rm -rf .husky
pnpm exec husky init

# 重新创建 hooks
# 复制配置文件到 .husky/

# 确保可执行权限
chmod +x .husky/*

Q6: 依赖没有自动安装

原因: post-merge hook 没有正确配置

解决方案:

bash 复制代码
# 手动安装
pnpm install

# 检查 .husky/post-merge 是否存在
ls -la .husky/post-merge

最佳实践

✅ 推荐做法

  1. 永远在功能分支上工作

    bash 复制代码
    git checkout -b feat/feature-name
  2. 提交前本地测试

    bash 复制代码
    pnpm check-all
  3. 编写清晰的提交消息

    bash 复制代码
    feat: 用户认证功能
    
    - 添加登录页面
    - 实现 JWT token 验证
    - 添加用户状态管理
  4. 小步提交,频繁提交

    • 每个提交只包含一个逻辑变更
    • 便于代码审查和回滚
  5. 定期运行完整检查

    bash 复制代码
    pnpm check-all

❌ 避免做法

  1. ❌ 频繁使用 --no-verify
  2. ❌ 在 main 分支直接提交
  3. ❌ 不遵循提交消息规范
  4. ❌ 提交未格式化的代码
  5. ❌ 跳过类型检查和测试

维护和更新

定期任务

bash 复制代码
# 每月更新依赖
pnpm update

# 检查过时的依赖
pnpm outdated

# 更新主要版本(谨慎)
pnpm update --latest

团队同步

bash 复制代码
# 拉取最新代码后
git pull
# post-merge hook 会自动安装依赖

# 如果配置文件更新
pnpm install

总结

通过这套 CI 配置,你的项目将获得:

  • 自动化代码质量检查
  • 统一的代码风格
  • 规范的提交历史
  • 早期发现问题
  • 更好的团队协作

这些配置可以直接应用于任何 React Native/Expo 项目,也可以根据项目需求进行调整。


参考资源

相关推荐
white-persist2 小时前
汇编代码详细解释:汇编语言如何转化为对应的C语言,怎么转化为对应的C代码?
java·c语言·前端·网络·汇编·安全·网络安全
张愚歌2 小时前
轻松打造个性化Leaflet地图标记
前端·javascript
华仔啊2 小时前
CSS实现高级流光按钮动画,这几行代码堪称神来之笔
前端·css
歪歪1002 小时前
详细介绍一下“集中同步+分布式入库”方案的具体实现步骤
开发语言·前端·分布式·后端·信息可视化
林太白2 小时前
rust17-部门管理模块
前端·后端·rust
_处女座程序员的日常2 小时前
如何预览常见格式word、excel、ppt、图片等格式的文档
前端·javascript·word·excel·开源软件
明月与玄武2 小时前
前端文件上传终极指南:从原理到架构实践!
前端·前端文件上传终极指南
布列瑟农的星空3 小时前
后台类项目如何挖掘前端技术亮点
前端·面试
wangbing11254 小时前
layui窗口标题
前端·javascript·layui