名称: ameba-integration 用户可调用: false 描述: 在将 Ameba 集成到开发工作流时使用,包括 CI/CD 管道、预提交钩子、GitHub Actions 和自动化代码审查过程。 允许的工具:
- Bash
- Read
Ameba 集成
将 Ameba 集成到您的开发工作流中,用于在 CI/CD 管道、预提交钩子和代码审查过程中进行自动化 Crystal 代码质量检查。
集成概述
Ameba 可以在您的开发工作流的多个点集成:
- 预提交钩子 - 在提交前捕获问题
- CI/CD 管道 - 在自动化构建中强制执行质量门
- GitHub Actions - 自动化 PR 审查和状态检查
- 编辑器集成 - 编码时的实时反馈
- 代码审查 - 在拉取请求上的自动化评论
- 预推送钩子 - 推送到远程前的最终检查
命令行使用
基本命令
# 在整个项目上运行 Ameba
ameba
# 在特定文件上运行
ameba src/models/user.cr
# 在特定目录上运行
ameba src/services/
# 使用特定配置运行
ameba --config .ameba.custom.yml
# 生成默认配置
ameba --gen-config
# 自动修复可纠正的问题
ameba --fix
# 仅检查特定规则
ameba --only Style/RedundantReturn
# 排除特定规则
ameba --except Style/LargeNumbers
# 格式化输出
ameba --format json
ameba --format junit
ameba --format flycheck
# 解释特定位置的问题
ameba --explain src/models/user.cr:10:5
# 运行所有输出
ameba --all
# 无问题时静默失败
ameba --silent
输出格式
# 默认:人类可读
ameba
# 输出:
# src/user.cr:10:5: Style/RedundantReturn: 检测到冗余返回
# JSON 格式(用于解析)
ameba --format json
# 输出: {"sources": [...], "summary": {...}}
# JUnit XML(用于 CI 集成)
ameba --format junit > ameba-results.xml
# Flycheck 格式(用于 Emacs)
ameba --format flycheck
高级使用
# 仅检查更改的文件(git)
git diff --name-only --diff-filter=ACM | grep '\.cr$' | xargs ameba
# 仅检查暂存文件
git diff --cached --name-only --diff-filter=ACM | grep '\.cr$' | xargs ameba
# 使用并行处理运行(如果可用)
ameba --parallel
# 基于严重性设置退出代码
ameba --fail-level error # 仅对错误失败
ameba --fail-level warning # 对警告和错误失败
ameba --fail-level convention # 对所有问题失败
# 生成格式化报告
ameba --format json | jq '.summary'
预提交钩子
Git 钩子设置
创建 .git/hooks/pre-commit:
#!/bin/sh
# .git/hooks/pre-commit - 在暂存的 Crystal 文件上运行 Ameba
echo "在暂存文件上运行 Ameba..."
# 获取暂存的 Crystal 文件
STAGED_FILES=$(git diff --cached --name-only --diff-filter=ACM | grep '\.cr$')
if [ -z "$STAGED_FILES" ]; then
echo "没有暂存的 Crystal 文件,跳过 Ameba"
exit 0
fi
# 在暂存文件上运行 Ameba
echo "$STAGED_FILES" | xargs ameba
# 捕获退出代码
AMEBA_EXIT=$?
if [ $AMEBA_EXIT -ne 0 ]; then
echo "❌ Ameba 发现问题。请在提交前修复它们。"
echo "运行 'ameba --fix' 自动纠正一些问题。"
exit 1
fi
echo "✅ Ameba 检查通过"
exit 0
使其可执行:
chmod +x .git/hooks/pre-commit
高级预提交钩子
#!/bin/sh
# 带自动修复选项的高级预提交钩子
echo "在暂存文件上运行 Ameba..."
STAGED_FILES=$(git diff --cached --name-only --diff-filter=ACM | grep '\.cr$')
if [ -z "$STAGED_FILES" ]; then
exit 0
fi
# 运行 Ameba
echo "$STAGED_FILES" | xargs ameba
AMEBA_EXIT=$?
if [ $AMEBA_EXIT -ne 0 ]; then
echo ""
echo "❌ Ameba 发现问题。"
echo ""
read -p "是否要自动修复可纠正的问题?(y/n) " -n 1 -r
echo
if [[ $REPLY =~ ^[Yy]$ ]]; then
echo "运行 ameba --fix..."
echo "$STAGED_FILES" | xargs ameba --fix
# 重新添加修复后的文件
echo "$STAGED_FILES" | xargs git add
echo "✅ 自动修复问题并重新暂存文件"
echo "⚠️ 请在再次提交前审查更改"
exit 1 # 退出以允许审查
else
echo "请在提交前手动修复问题"
exit 1
fi
fi
echo "✅ Ameba 检查通过"
exit 0
预提交框架集成
使用 pre-commit 框架:
创建 .pre-commit-config.yaml:
# .pre-commit-config.yaml
存储库:
- 存储库: 本地
钩子:
- id: ameba
名称: Ameba (Crystal 代码检查器)
入口: ameba
语言: 系统
文件: \.cr$
传递文件名: true
- 存储库: 本地
钩子:
- id: crystal-format
名称: Crystal 格式化
入口: crystal tool format
语言: 系统
文件: \.cr$
传递文件名: true
安装和使用:
# 安装 pre-commit
pip install pre-commit # 或 brew install pre-commit
# 安装钩子
pre-commit install
# 手动运行
pre-commit run --all-files
# 在特定文件上运行
pre-commit run --files src/user.cr
预提交配置选项
# .pre-commit-config.yaml 带选项
存储库:
- 存储库: 本地
钩子:
- id: ameba
名称: Ameba
入口: ameba
语言: 系统
文件: \.cr$
传递文件名: true
- id: ameba-strict
名称: Ameba (严格)
入口: ameba --fail-level convention
语言: 系统
文件: ^src/.*\.cr$ # 仅 src 目录
传递文件名: true
- id: ameba-autofix
名称: Ameba 自动修复
入口: ameba --fix
语言: 系统
文件: \.cr$
传递文件名: true
GitHub Actions 集成
基本 GitHub Actions 工作流
创建 .github/workflows/ameba.yml:
名称: Ameba
用户可调用: false
触发:
推送:
分支: [ main, develop ]
拉取请求:
分支: [ main, develop ]
任务:
代码检查:
运行在: ubuntu-latest
步骤:
- 名称: 检出代码
使用: actions/checkout@v4
- 名称: 安装 Crystal
使用: crystal-lang/install-crystal@v1
带:
crystal: latest
- 名称: 安装依赖
运行: shards install
- 名称: 运行 Ameba
使用: crystal-ameba/github-action@v0.12.0
环境:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
高级 GitHub Actions 配置
名称: 代码质量
用户可调用: false
触发:
推送:
分支: [ main ]
拉取请求:
类型: [ opened, synchronize, reopened ]
任务:
ameba:
名称: Ameba 代码检查
运行在: ubuntu-latest
步骤:
- 名称: 检出代码
使用: actions/checkout@v4
带:
获取深度: 0 # 完整历史以进行更好分析
- 名称: 安装 Crystal
使用: crystal-lang/install-crystal@v1
带:
crystal: 1.11.0 # 固定版本以保持一致性
- 名称: 缓存 shards
使用: actions/cache@v3
带:
路径: lib
键: ${{ runner.os }}-shards-${{ hashFiles('shard.lock') }}
恢复键: |
${{ runner.os }}-shards-
- 名称: 安装依赖
运行: shards install
- 名称: 运行 Ameba
使用: crystal-ameba/github-action@v0.12.0
环境:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- 名称: 上传 Ameba 结果
如果: always()
使用: actions/upload-artifact@v3
带:
名称: ameba-results
路径: ameba-results.json
跨 Crystal 版本的矩阵测试
名称: 跨版本质量
用户可调用: false
触发: [推送, 拉取请求]
任务:
ameba:
策略:
矩阵:
crystal: [1.10.0, 1.11.0, latest]
os: [ubuntu-latest, macos-latest]
运行在: ${{ matrix.os }}
步骤:
- 使用: actions/checkout@v4
- 名称: 安装 Crystal ${{ matrix.crystal }}
使用: crystal-lang/install-crystal@v1
带:
crystal: ${{ matrix.crystal }}
- 名称: 安装依赖
运行: shards install
- 名称: 运行 Ameba
运行: |
crystal run bin/ameba.cr -- --format json > ameba-results.json
- 名称: 检查结果
运行: |
if [ $(jq '.summary.issues_count' ameba-results.json) -gt 0 ]; then
echo "❌ 发现问题"
jq '.summary' ameba-results.json
exit 1
fi
拉取请求审查集成
名称: PR 代码审查
用户可调用: false
触发:
拉取请求:
类型: [ opened, synchronize ]
任务:
审查:
运行在: ubuntu-latest
权限:
内容: 读取
拉取请求: 写入
步骤:
- 使用: actions/checkout@v4
带:
获取深度: 0
- 名称: 安装 Crystal
使用: crystal-lang/install-crystal@v1
- 名称: 安装依赖
运行: shards install
- 名称: 获取更改文件
id: changed-files
使用: tj-actions/changed-files@v40
带:
文件: |
**/*.cr
- 名称: 在更改文件上运行 Ameba
如果: steps.changed-files.outputs.any_changed == 'true'
运行: |
echo "${{ steps.changed-files.outputs.all_changed_files }}" | \
xargs ameba --format json > ameba-results.json
- 名称: 评论 PR
如果: steps.changed-files.outputs.any_changed == 'true'
使用: actions/github-script@v7
带:
脚本: |
const fs = require('fs');
const results = JSON.parse(fs.readFileSync('ameba-results.json', 'utf8'));
if (results.summary.issues_count > 0) {
const body = `## Ameba 报告
发现 ${results.summary.issues_count} 个问题:
${results.sources.flatMap(s =>
s.issues.map(i =>
\`- \${s.path}:\${i.location.line}:\${i.location.column} - \${i.rule.name}: \${i.message}\`
)
).join('\
')}`;
await github.rest.issues.createComment({
所有者: context.repo.owner,
仓库: context.repo.repo,
问题编号: context.issue.number,
正文: body
});
}
CI/CD 管道集成
GitLab CI/CD
# .gitlab-ci.yml
阶段:
- 质量
- 测试
- 构建
ameba:
阶段: 质量
镜像: crystallang/crystal:latest
前脚本:
- shards install
脚本:
- crystal run bin/ameba.cr -- --format junit > ameba-results.xml
制品:
报告:
junit: ameba-results.xml
路径:
- ameba-results.xml
当: always
过期于: 1 周
规则:
- 如果: '$CI_PIPELINE_SOURCE == "merge_request_event"'
- 如果: '$CI_COMMIT_BRANCH == "main"'
ameba-strict:
扩展: ameba
脚本:
- crystal run bin/ameba.cr -- --fail-level convention
仅:
- main
CircleCI
# .circleci/config.yml
版本: 2.1
球:
crystal: manastech/crystal@1.0
任务:
ameba:
执行器:
名称: crystal/default
标签: "1.11"
步骤:
- 检出
- 恢复缓存:
键:
- shards-v1-{{ checksum "shard.lock" }}
- shards-v1-
- 运行:
名称: 安装依赖
命令: shards install
- 保存缓存:
键: shards-v1-{{ checksum "shard.lock" }}
路径:
- lib
- 运行:
名称: 运行 Ameba
命令: |
crystal run bin/ameba.cr -- --format junit > ameba-results.xml
- 存储测试结果:
路径: ameba-results.xml
- 存储制品:
路径: ameba-results.xml
工作流:
版本: 2
质量:
任务:
- ameba
Jenkins 管道
// Jenkinsfile
管道 {
代理 {
码头 {
镜像 'crystallang/crystal:latest'
}
}
阶段 {
阶段('设置') {
步骤 {
sh 'shards install'
}
}
阶段('Ameba') {
步骤 {
sh '''
crystal run bin/ameba.cr -- --format junit > ameba-results.xml || true
'''
}
后 {
总是 {
junit 'ameba-results.xml'
}
}
}
阶段('Ameba 严格') {
当 {
分支 'main'
}
步骤 {
sh 'crystal run bin/ameba.cr -- --fail-level error'
}
}
}
后 {
失败 {
emailext(
主题: "${env.JOB_NAME} 中的 Ameba 失败",
正文: "检查控制台输出在 ${env.BUILD_URL}",
到: "${env.CHANGE_AUTHOR_EMAIL}"
)
}
}
}
Travis CI
# .travis.yml
语言: crystal
crystal:
- latest
- 1.11.0
安装:
- shards install
脚本:
- crystal spec
- crystal run bin/ameba.cr -- --fail-level warning
缓存:
目录:
- lib
通知:
电子邮件:
成功时: never
失败时: change
编辑器集成
VS Code
安装 Crystal 语言扩展并配置:
// .vscode/settings.json
{
"crystal-lang.server": "crystalline",
"crystal-lang.problems": "build",
// 保存时运行 Ameba
"emeraldwalk.runonsave": {
"命令": [
{
"匹配": "\\.cr$",
"命令": "ameba ${file}"
}
]
},
// 保存时格式化
"editor.formatOnSave": true,
"[crystal]": {
"editor.defaultFormatter": "crystal-lang-tools.crystal-lang"
}
}
创建 .vscode/tasks.json:
{
"版本": "2.0.0",
"任务": [
{
"标签": "Ameba",
"类型": "shell",
"命令": "ameba",
"问题匹配器": {
"所有者": "crystal",
"文件位置": ["relative", "${workspaceFolder}"],
"模式": {
"正则表达式": "^(.+):(\\d+):(\\d+):\\s+(.+):\\s+(.+)$",
"文件": 1,
"行": 2,
"列": 3,
"严重性": 4,
"消息": 5
}
},
"组": {
"种类": "build",
"isDefault": true
}
},
{
"标签": "Ameba 修复",
"类型": "shell",
"命令": "ameba --fix",
"组": "build"
}
]
}
Vim/Neovim
使用 ALE(异步检查引擎):
" .vimrc 或 init.vim
让 g:ale_linters = {
\ 'crystal': ['ameba', 'crystal'],
\}
让 g:ale_fixers = {
\ 'crystal': ['ameba'],
\}
" 启用保存时自动修复
让 g:ale_fix_on_save = 1
" Ameba 选项
让 g:ale_crystal_ameba_executable = 'ameba'
Emacs
;; .emacs 或 init.el
(require 'flycheck)
(flycheck-define-checker crystal-ameba
"使用 Ameba 的 Crystal 检查器。"
:command ("ameba" "--format" "flycheck" source)
:error-patterns
((错误 行开始 (文件名称) ":" 行 ":" 列 ": E: " (消息) 行结束)
(警告 行开始 (文件名称) ":" 行 ":" 列 ": W: " (消息) 行结束)
(信息 行开始 (文件名称) ":" 行 ":" 列 ": I: " (消息) 行结束))
:modes crystal-mode)
(添加-到-列表 'flycheck-checkers 'crystal-ameba)
质量门和策略
快速失败策略
#!/bin/bash
# scripts/quality-gate.sh
echo "运行质量门..."
# 门 1: 仅关键错误
echo "门 1: 关键错误"
ameba --only Lint/Syntax,Lint/UnreachableCode --fail-level error
if [ $? -ne 0 ]; then
echo "❌ 发现关键错误"
exit 1
fi
# 门 2: 所有错误
echo "门 2: 所有错误"
ameba --fail-level error
if [ $? -ne 0 ]; then
echo "❌ 发现错误"
exit 1
fi
# 门 3: 警告(当前非阻塞)
echo "门 3: 警告(信息性)"
ameba --fail-level warning || echo "⚠️ 发现警告(非阻塞)"
echo "✅ 所有质量门通过"
渐进严格性
# .github/workflows/quality-gates.yml
名称: 质量门
用户可调用: false
触发: [推送, 拉取请求]
任务:
关键:
名称: 关键问题
运行在: ubuntu-latest
步骤:
- 使用: actions/checkout@v4
- 使用: crystal-lang/install-crystal@v1
- 运行: shards install
- 名称: 检查关键
运行: ameba --only Lint/Syntax --fail-level error
错误:
名称: 所有错误
需要: 关键
运行在: ubuntu-latest
步骤:
- 使用: actions/checkout@v4
- 使用: crystal-lang/install-crystal@v1
- 运行: shards install
- 名称: 检查错误
运行: ameba --fail-level error
警告:
名称: 警告(仅主分支)
需要: 错误
如果: github.ref == 'refs/heads/main'
运行在: ubuntu-latest
步骤:
- 使用: actions/checkout@v4
- 使用: crystal-lang/install-crystal@v1
- 运行: shards install
- 名称: 检查警告
运行: ameba --fail-level warning
渐进改进(防止新问题)
#!/bin/bash
# scripts/ameba-ratchet.sh
# 仅在新问题上失败,不是现有问题
# 获取基线问题计数(从主分支)
git fetch origin main
BASELINE=$(git show origin/main:.ameba-baseline.json 2>/dev/null || echo '{"count": 0}')
BASELINE_COUNT=$(echo "$BASELINE" | jq '.count')
# 运行 Ameba 并计数当前问题
ameba --format json > current-results.json
CURRENT_COUNT=$(jq '.summary.issues_count' current-results.json)
echo "基线问题: $BASELINE_COUNT"
echo "当前问题: $CURRENT_COUNT"
if [ "$CURRENT_COUNT" -gt "$BASELINE_COUNT" ]; then
echo "❌ 引入新问题($((CURRENT_COUNT - BASELINE_COUNT)) 个新问题)"
exit 1
fi
if [ "$CURRENT_COUNT" -lt "$BASELINE_COUNT" ]; then
echo "✅ 问题减少!($((BASELINE_COUNT - CURRENT_COUNT)) 个更少问题)"
fi
echo "✅ 无新问题"
报告和监控
生成 HTML 报告
#!/bin/bash
# scripts/generate-report.sh
ameba --format json > ameba-results.json
# 使用 jq 和模板转换为 HTML
cat > ameba-report.html <<'EOF'
<!DOCTYPE html>
<html>
<head>
<title>Ameba 报告</title>
<style>
body { font-family: Arial, sans-serif; margin: 20px; }
.summary { background: #f0f0f0; padding: 20px; margin-bottom: 20px; }
.issue { border-left: 3px solid red; padding: 10px; margin: 10px 0; }
.error { border-color: #d32f2f; }
.warning { border-color: #f57c00; }
.convention { border-color: #fbc02d; }
</style>
</head>
<body>
<h1>Ameba 代码质量报告</h1>
<div class="summary">
EOF
jq -r '.summary | "
<h2>摘要</h2>
<p>总问题数: \(.issues_count)</p>
<p>分析文件数: \(.target_sources_count)</p>
"' ameba-results.json >> ameba-report.html
echo '<h2>问题</h2>' >> ameba-report.html
jq -r '.sources[] | select(.issues | length > 0) | .path as $path | .issues[] | "
<div class=\"issue \(.rule.severity | ascii_downcase)\">
<strong>\($path):\(.location.line):\(.location.column)</strong><br>
\(.rule.name): \(.message)
</div>
"' ameba-results.json >> ameba-report.html
echo '</body></html>' >> ameba-report.html
echo "报告生成: ameba-report.html"
指标跟踪
#!/bin/bash
# scripts/track-metrics.sh
# 随时间跟踪 Ameba 指标
TIMESTAMP=$(date +%Y-%m-%d)
ameba --format json > "metrics/ameba-$TIMESTAMP.json"
# 提取关键指标
jq '{
日期: "'$TIMESTAMP'",
问题: .summary.issues_count,
文件: .summary.target_sources_count,
错误: [.sources[].issues[] | select(.rule.severity == "Error")] | length,
警告: [.sources[].issues[] | select(.rule.severity == "Warning")] | length
}' "metrics/ameba-$TIMESTAMP.json" >> metrics/history.jsonl
# 生成趋势图(需要 gnuplot 或类似)
echo "指标已跟踪于 $TIMESTAMP"
何时使用此技能
在以下情况下使用 ameba-integration 技能:
- 为 Crystal 项目设置 CI/CD 管道
- 实现自动化代码审查过程
- 为部署建立质量门
- 为团队开发配置预提交钩子
- 将静态分析集成到 GitHub Actions
- 创建自动化 PR 审查工作流
- 设置编辑器集成以获取实时反馈
- 实现渐进质量改进(渐进改进)
- 为利益相关者生成代码质量报告
- 从手动代码审查迁移到自动化检查
- 建立编码标准执行
- 为新团队成员提供自动化反馈
最佳实践
- 从 CI/CD 开始 - 首先在 CI 管道中实施,然后再本地钩子
- 使用缓存 - 缓存依赖和 Ameba 结果以加快构建
- 适当失败 - 使用
--fail-level匹配管道需求 - 提供反馈 - 在 PR 上生成报告和评论
- 使其快速 - 在预提交钩子中仅检查更改文件
- 允许绕过 - 提供
--no-verify选项用于紧急情况 - 渐进执行 - 开始时宽松,随时间增加严格性
- 监控指标 - 跟踪问题以衡量改进
- 分离关注点 - 不同环境使用不同规则/严重性
- 记录过程 - 为团队提供清晰的本场运行说明
- 使用制品 - 存储结果用于后期分析和趋势
- 尽可能自动修复 - 在交互式环境中提供自动修复
- 固定版本 - 在 CI 中使用特定 Ameba 版本以保持一致性
- 优雅处理失败 - 提供有帮助的错误消息
- 保持维护 - 定期更新集成和配置
常见陷阱
- 阻塞所有提交 - 过于严格的预提交钩子让开发人员沮丧
- 无缓存 - 每次重新下载依赖导致 CI 构建缓慢
- 分析生成文件 - 浪费时间在自动生成的代码上
- 未固定版本 - 不同 Ameba 版本产生不同结果
- 缺少更改文件检测 - 在每个 PR 中运行整个代码库
- 无失败上下文 - 没有指导的模糊错误消息
- 不一致配置 - 本地与 CI 设置不同
- 长反馈循环 - 开发人员发现问题太晚
- 无自动修复选项 - 可纠正问题需要手动修复
- 静默失败 - CI 通过但 Ameba 未实际运行
- 过多通知 - 用每个小问题骚扰团队
- 无绕过机制 - 需要时无法提交紧急修复
- 忽略性能 - 缓慢分析导致 CI 超时
- 未使用并行任务 - 顺序执行减慢管道
- 缺少测试覆盖 - 未验证集成实际工作