Danger JS 开源项目教程:自动化代码审查的革命性工具
引言:告别繁琐的手动代码审查
你是否曾经在代码审查(Code Review)过程中遇到过这些问题?
- 重复检查相同的编码规范问题
- 忘记要求同事添加 CHANGELOG 条目
- 需要手动验证依赖项更新
- 花费大量时间检查基础的质量问题
Danger JS 正是为了解决这些痛点而生!它是一个开源的自动化代码审查工具,能够帮助团队规范化代码审查流程,将重复性工作自动化,让开发者专注于真正重要的技术决策。
通过本教程,你将掌握:
- ✅ Danger JS 的核心概念和工作原理
- ✅ 如何快速搭建和配置 Danger JS
- ✅ 编写强大的 Dangerfile 规则文件
- ✅ 集成到主流 CI/CD 平台的最佳实践
- ✅ 实际项目中的高级应用技巧
什么是 Danger JS?
Danger JS 是一个基于 Node.js 的自动化代码审查工具,它能够在 CI/CD 流程中运行,对 Pull Request(PR)或 Merge Request(MR)进行自动化检查。它支持 GitHub、GitLab、BitBucket Server 和 BitBucket Cloud 等主流代码托管平台。
核心特性
快速开始:5分钟搭建 Danger JS
环境要求
- Node.js 18+
- npm 或 yarn
- Git 仓库(GitHub/GitLab/BitBucket)
安装步骤
# 全局安装 danger
npm install -g danger
# 或者在项目中安装
npm install --save-dev danger
# 使用 yarn
yarn add --dev danger
基础配置
在项目根目录创建 dangerfile.js 或 dangerfile.ts:
// dangerfile.js
import { danger, warn, fail, message } from "danger"
// 检查 CHANGELOG 是否更新
const hasChangelog = danger.git.modified_files.includes("CHANGELOG.md")
const isTrivial = (danger.github.pr.body + danger.github.pr.title).includes("#trivial")
if (!hasChangelog && !isTrivial) {
fail("请添加 CHANGELOG 条目来描述你的更改")
}
// 检查测试文件是否随代码更改而更新
const jsFiles = danger.git.modified_files.filter(path => path.endsWith(".js"))
const testFiles = danger.git.modified_files.filter(path => path.includes("__tests__"))
if (jsFiles.length > 0 && testFiles.length === 0) {
warn("检测到 JavaScript 文件更改但没有相应的测试文件更新")
}
// 鼓励文档贡献者
const docs = danger.git.fileMatch("**/*.md")
if (docs.edited) {
message("感谢文档贡献!我们 ❤️ 文档维护者")
}
CI/CD 集成示例
GitHub Actions 配置
# .github/workflows/danger.yml
name: Danger JS
on: [pull_request]
jobs:
danger:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Setup Node.js
uses: actions/setup-node@v3
with:
node-version: '18'
- name: Install dependencies
run: npm install
- name: Run Danger
env:
DANGER_GITHUB_API_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: npx danger ci
GitLab CI 配置
# .gitlab-ci.yml
danger:
image: node:18
script:
- npm install
- npx danger ci
only:
- merge_requests
Danger DSL 深度解析
Danger 提供了丰富的 DSL(Domain Specific Language)来访问 PR/MR 的元数据。
Git DSL
// 获取修改的文件信息
const modifiedFiles = danger.git.modified_files
const createdFiles = danger.git.created_files
const deletedFiles = danger.git.deleted_files
// 文件匹配模式
const srcFiles = danger.git.fileMatch("src/**/*.ts")
const testFiles = danger.git.fileMatch("**/__tests__/**/*.ts")
if (srcFiles.modified && !testFiles.modified) {
warn("源代码已修改但测试文件未更新")
}
// 获取文件差异
const packageDiff = await danger.git.JSONDiffForFile("package.json")
if (packageDiff.dependencies) {
message("检测到依赖项更新")
}
GitHub DSL(针对 GitHub 平台)
if (danger.github) {
// PR 基本信息
const pr = danger.github.pr
console.log(`PR #${pr.number}: ${pr.title}`)
// 评论和审查
const comments = danger.github.reviews
const requestedReviewers = danger.github.requested_reviewers
// 标签和里程碑
const labels = danger.github.issue.labels
const milestone = danger.github.issue.milestone
}
实用工具函数
// 数组转句子
const files = danger.git.modified_files
message(`修改的文件:${danger.utils.sentence(files)}`)
// 创建链接
const fileLinks = danger.git.modified_files.map(file =>
danger.utils.href(danger.github.utils.fileLinks(file), file)
)
message(`文件链接:${danger.utils.sentence(fileLinks)}`)
高级应用场景
1. 依赖项管理
import { danger, warn, fail } from "danger"
const packageDiff = await danger.git.JSONDiffForFile("package.json")
if (packageDiff.dependencies) {
const newDeps = Object.keys(packageDiff.dependencies.after || {})
const removedDeps = Object.keys(packageDiff.dependencies.before || {})
.filter(dep => !packageDiff.dependencies.after?.[dep])
if (newDeps.length > 0) {
message(`新增依赖:${danger.utils.sentence(newDeps)}`)
}
if (removedDeps.length > 0) {
message(`移除依赖:${danger.utils.sentence(removedDeps)}`)
}
}
2. 代码质量检查
// 检查大文件提交
const largeFiles = danger.git.modified_files.filter(file => {
const diff = danger.git.diffForFile(file)
return diff && diff.added.length > 500
})
if (largeFiles.length > 0) {
warn(`以下文件更改较大,请考虑拆分:${danger.utils.sentence(largeFiles)}`)
}
// 禁止直接修改 package.json 版本号
if (packageDiff.version && danger.github.pr.user.login !== "maintainer") {
fail("请不要直接修改 package.json 中的版本号,使用 npm version 命令")
}
3. 文档和测试验证
// 验证文档更新
const docsModified = danger.git.modified_files.some(file =>
file.endsWith('.md') || file.includes('docs/')
)
const codeModified = danger.git.modified_files.some(file =>
file.endsWith('.js') || file.endsWith('.ts') || file.endsWith('.jsx') || file.endsWith('.tsx')
)
if (codeModified && !docsModified) {
warn("代码已修改但文档未更新,请考虑更新相关文档")
}
// 测试覆盖率检查
const sourceFiles = danger.git.fileMatch('src/**/*.{js,ts}')
const testFiles = danger.git.fileMatch('{src,test}/**/*.{spec,test}.{js,ts}')
if (sourceFiles.modified && !testFiles.modified) {
fail("源代码已修改但未添加相应测试")
}
插件生态系统
Danger JS 拥有丰富的插件系统,可以扩展其功能:
常用插件
| 插件名称 | 功能描述 | 安装命令 |
|---|---|---|
| danger-plugin-yarn | 检查 yarn.lock 更新 | npm install --save-dev danger-plugin-yarn |
| danger-plugin-jest | Jest 测试结果分析 | npm install --save-dev danger-plugin-jest |
| danger-plugin-eslint | ESLint 结果集成 | npm install --save-dev danger-plugin-eslint |
| danger-plugin-spellcheck | 拼写检查 | npm install --save-dev danger-plugin-spellcheck |
自定义插件开发
// 简单插件示例
export default function myPlugin() {
const { danger, warn } = require('danger')
// 检查提交信息格式
const commitMessages = danger.git.commits.map(commit => commit.message)
const invalidCommits = commitMessages.filter(msg => !msg.match(/^(feat|fix|docs|style|refactor|test|chore):/))
if (invalidCommits.length > 0) {
warn(`发现不符合约定式提交规范的提交信息:${invalidCommits.join(', ')}`)
}
}
最佳实践和注意事项
最佳实践
- 渐进式采用:从简单的规则开始,逐步增加复杂度
- 明确反馈:提供清晰的错误信息和修复建议
- 团队共识:确保所有团队成员理解并同意设置的规则
- 定期审查:定期回顾和更新 Dangerfile 规则
注意事项
性能优化建议
// 使用 fileMatch 而不是直接操作文件数组
// ❌ 不推荐
const allFiles = danger.git.modified_files
const testFiles = allFiles.filter(f => f.includes('__tests__'))
// ✅ 推荐
const testFiles = danger.git.fileMatch('**/__tests__/**/*')
// 使用异步操作处理大文件
async function checkLargeFile(filePath) {
const diff = await danger.git.diffForFile(filePath)
if (diff && diff.added.length > 1000) {
warn(`文件 ${filePath} 更改较大`)
}
}
// 并行处理多个文件检查
await Promise.all(
danger.git.modified_files.map(file => checkLargeFile(file))
)
故障排除和调试
本地测试
# 使用 danger pr 命令本地测试
DANGER_GITHUB_API_TOKEN=your_token_here npx danger pr https://github.com/owner/repo/pull/123
# 模拟 CI 环境测试
export DANGER_FAKE_CI="YEP"
export DANGER_TEST_REPO='owner/repo'
DANGER_TEST_PR='123' npx danger ci
常见错误处理
// 添加错误处理
try {
const packageDiff = await danger.git.JSONDiffForFile("package.json")
if (packageDiff && packageDiff.dependencies) {
// 处理依赖更新
}
} catch (error) {
console.warn("无法解析 package.json 差异", error)
}
// 平台特定代码的安全访问
if (danger.github) {
// GitHub 特定逻辑
} else if (danger.gitlab) {
// GitLab 特定逻辑
} else {
warn("未知的代码托管平台")
}
总结
Danger JS 是一个强大的自动化代码审查工具,它能够:
- 🚀 自动化团队代码规范检查
- 📝 确保文档和测试的完整性
- 🔄 集成到现有的 CI/CD 流程中
- 🎯 提供可定制的规则系统
- 🤖 支持多平台代码托管服务
通过本教程,你应该已经掌握了 Danger JS 的核心概念、安装配置、高级用法和最佳实践。现在就开始在你的项目中引入 Danger JS,让代码审查变得更加高效和规范吧!
记住,好的工具需要配合好的流程,Danger JS 只是一个辅助工具,真正的价值在于它如何帮助团队建立和维护高质量的代码标准。
下一步行动:
- 在你的项目中安装 Danger JS
- 从简单的 CHANGELOG 检查开始
- 逐步添加更多自动化规则
- 与团队分享并收集反馈
- 定期优化和更新你的 Dangerfile
Happy coding! 🎉
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考



