Skip to content

代码质量门禁

源:Spotless | detekt | Android Lint

通过自动化工具强制执行代码规范,提升代码质量和团队协作效率。

代码质量工具

工具对比

工具用途语言自动修复
Spotless代码格式化Kotlin/Java/XML
ktlintKotlin 代码规范Kotlin
detekt静态代码分析Kotlin⚠️ 部分
Android LintAndroid 专用检查Kotlin/Java⚠️ 部分

Spotless

安装配置

build.gradle.kts

kotlin
plugins {
    id("com.diffplug.spotless") version "6.25.0"
}

spotless {
    kotlin {
        target("**/*.kt")
        targetExclude("**/build/**/*.kt")
        
        ktlint("1.1.1")
            .editorConfigOverride(mapOf(
                "max_line_length" to "120",
                "disabled_rules" to "no-wildcard-imports"
            ))
        
        trimTrailingWhitespace()
        indentWithSpaces(4)
        endWithNewline()
    }
    
    kotlinGradle {
        target("**/*.gradle.kts")
        ktlint()
    }
    
    format("xml") {
        target("**/*.xml")
        targetExclude("**/build/**/*.xml")
        
        indentWithSpaces(4)
        trimTrailingWhitespace()
        endWithNewline()
    }
}

常用命令

bash
# 检查格式
./gradlew spotlessCheck

# 自动修复
./gradlew spotlessApply

# 只检查 Kotlin
./gradlew spotlessKotlinCheck

# 只修复 Kotlin
./gradlew spotlessKotlinApply

Git Hooks

安装 pre-commit hook

kotlin
tasks.register<Copy>("installGitHooks") {
    from("scripts/pre-commit")
    into(".git/hooks")
    fileMode = 0b111101101  // 0755
}

tasks.named("build") {
    dependsOn("installGitHooks")
}

scripts/pre-commit

bash
#!/bin/bash

./gradlew spotlessCheck

if [ $? -ne 0 ]; then
    echo "❌ Code format check failed"
    echo "Run: ./gradlew spotlessApply"
    exit 1
fi

detekt

安装配置

build.gradle.kts

kotlin
plugins {
    id("io.gitlab.arturbosch.detekt") version "1.23.4"
}

detekt {
    buildUponDefaultConfig = true
    allRules = false
    config.setFrom("$projectDir/config/detekt.yml")
}

dependencies {
    detektPlugins("io.gitlab.arturbosch.detekt:detekt-formatting:1.23.4")
}

config/detekt.yml

yaml
complexity:
  LongMethod:
    threshold: 50
  LongParameterList:
    functionThreshold: 6
    constructorThreshold: 7
  CyclomaticComplexMethod:
    threshold: 15

style:
  MaxLineLength:
    maxLineLength: 120
  MagicNumber:
    ignoreNumbers: [-1, 0, 1, 2]

naming:
  FunctionName:
    functionPattern: '[a-z][a-zA-Z0-9]*'

使用

bash
# 检查
./gradlew detekt

# 生成报告
./gradlew detekt --reports html

# 查看报告
open build/reports/detekt/detekt.html

Android Lint

配置 Lint

build.gradle.kts

kotlin
android {
    lint {
        // 错误视为失败
        abortOnError = true
        
        // 警告视为错误
        warningsAsErrors = false
        
        // 检查所有问题
        checkAllWarnings = true
        
        // 生成 HTML 报告
        htmlReport = true
        htmlOutput = file("$buildDir/reports/lint-results.html")
        
        // 生成 XML 报告
        xmlReport = true
        xmlOutput = file("$buildDir/reports/lint-results.xml")
        
        // 禁用特定检查
        disable += setOf(
            "TypographyFractions",
            "TypographyQuotes"
        )
        
        // 启用特定检查
        enable += setOf(
            "RtlHardcoded",
            "RtlCompat"
        )
        
        // 设置严重性
        error += setOf("NewApi", "InlinedApi")
        warning += setOf("UnusedResources")
        ignore += setOf("GoogleAppIndexingWarning")
    }
}

Lint 配置文件

lint.xml

xml
<?xml version="1.0" encoding="UTF-8"?>
<lint>
    <!-- 忽略特定警告 -->
    <issue id="IconMissingDensityFolder" severity="ignore" />
    
    <!-- 设置为错误 -->
    <issue id="NewApi" severity="error" />
    
    <!-- 忽略特定文件 -->
    <issue id="UnusedResources">
        <ignore path="src/main/res/values/donottranslate.xml" />
    </issue>
</lint>

baseline

创建 baseline

bash
./gradlew lintDebug --create-baseline

使用 baseline

kotlin
android {
    lint {
        baseline = file("lint-baseline.xml")
    }
}

组合使用

统一质量检查

build.gradle.kts

kotlin
tasks.register("check质量") {
    group = "verification"
    description = "运行所有代码质量检查"
    
    dependsOn("spotlessCheck")
    dependsOn("detekt")
    dependsOn("lintDebug")
    dependsOn("test")
}

使用

bash
./gradlew check质量

CI 门禁

GitHub Actions

yaml
name: Code Quality

on: [push, pull_request]

jobs:
  quality:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v3
      
      - name: Setup JDK
        uses: actions/setup-java@v3
        with:
          java-version: '17'
      
      - name: Spotless Check
        run: ./gradlew spotlessCheck
      
      - name: detekt
        run: ./gradlew detekt
      
      - name: Lint
        run: ./gradlew lintDebug
      
      - name: Upload Reports
        if: failure()
        uses: actions/upload-artifact@v3
        with:
          name: quality-reports
          path: |
            **/build/reports/detekt/
            **/build/reports/lint-*.html

约定插件集成

质量检查插件

kotlin
class QualityConventionPlugin : Plugin<Project> {
    override fun apply(target: Project) {
        with(target) {
            // 应用 Spotless
            pluginManager.apply("com.diffplug.spotless")
            extensions.configure<SpotlessExtension> {
                kotlin {
                    target("**/*.kt")
                    ktlint()
                }
            }
            
            // 应用 detekt
            pluginManager.apply("io.gitlab.arturbosch.detekt")
            extensions.configure<DetektExtension> {
                buildUponDefaultConfig = true
            }
            
            // 配置 Android Lint
            pluginManager.withPlugin("com.android.application") {
                extensions.configure<ApplicationExtension> {
                    lint {
                        abortOnError = true
                        checkAllWarnings = true
                    }
                }
            }
            
            // 创建统一任务
            tasks.register("checkQuality") {
                group = "verification"
                dependsOn("spotlessCheck", "detekt", "lintDebug")
            }
        }
    }
}

使用

kotlin
// build.gradle.kts
plugins {
    id("my.quality")
}

实战案例

案例1:完整的质量门禁

build.gradle.kts

kotlin
plugins {
    id("com.diffplug.spotless") version "6.25.0"
    id("io.gitlab.arturbosch.detekt") version "1.23.4"
}

spotless {
    kotlin {
        target("**/*.kt")
        ktlint("1.1.1")
        trimTrailingWhitespace()
        endWithNewline()
    }
}

detekt {
    config.setFrom("$projectDir/config/detekt.yml")
}

android {
    lint {
        abortOnError = true
        baseline = file("lint-baseline.xml")
    }
}

tasks.register("qualityGate") {
    group = "verification"
    dependsOn(
        "spotlessCheck",
        "detekt",
        "lintDebug",
        "testDebugUnitTest"
    )
}

案例2:自动修复工作流

fix-code.sh

bash
#!/bin/bash

echo "🔧 Fixing code quality issues..."

# 修复格式
./gradlew spotlessApply

# 运行检查
./gradlew detekt

# 提交修复
git add -A
git commit -m "chore: fix code quality issues"

echo "✅ Done!"

最佳实践

CI 强制检查

yaml
- name: Quality Gate
  run: ./gradlew spotlessCheck detekt lintDebug

本地 Git Hooks

bash
# pre-commit: 提交前检查
./gradlew spotlessCheck

# pre-push: 推送前完整检查
./gradlew qualityGate

统一配置

kotlin
// 在约定插件中统一配置
plugins {
    id("my.quality")  // 一行搞定
}

Baseline 管理

bash
# 初始项目创建 baseline
./gradlew lintDebug --create-baseline

# 定期更新 baseline
./gradlew lintDebug --update-baseline

团队规范

  • PR 必须通过所有检查
  • 不接受降低质量的代码
  • 定期更新工具版本