Kite enforces code quality using ktlint (code style) and detekt (static analysis).
# Run all quality checks locally
./gradlew ktlintMainSourceSetCheck ktlintKotlinScriptCheck detekt
# Auto-fix ktlint violations
./gradlew ktlintFormat
# Run via Kite CLI (after installing — see docs/02-installation.md)
kite-cli run ktlint
kite-cli run detekt
kite-cli run quality-checks # Runs both
# Or via Docker (no install needed)
docker run --rm -v $(pwd):/workspace ghcr.io/gianluz/kite:latest run quality-checksktlint enforces Kotlin coding conventions and style guide.
# Check main sources only
./gradlew ktlintMainSourceSetCheck ktlintKotlinScriptCheck
# Check all sources (including tests)
./gradlew ktlintCheck
# Auto-fix violations
./gradlew ktlintFormat| Issue | Fix |
|---|---|
| Wildcard imports | Replace import io.kite.core.* with explicit imports |
| Import ordering | Run ktlintFormat to auto-fix |
| Trailing whitespace | Run ktlintFormat to auto-fix |
| Final newline missing | Run ktlintFormat to auto-fix |
| Max line length (120 chars) | Break long lines |
ktlint is configured in:
.editorconfig- IDE and ktlint shared configbuild.gradle.kts- ktlint plugin settings
IntelliJ IDEA:
- Install ktlint plugin:
Settings → Plugins → Marketplace → "ktlint" - Enable:
Settings → Editor → Inspections → ktlint - Auto-format on save:
Settings → Tools → Actions on Save → Reformat code
detekt performs static code analysis to find:
- Code smells
- Complexity issues
- Potential bugs
- Performance issues
# Run detekt
./gradlew detekt
# Generate reports
./gradlew detekt
# Reports in: */build/reports/detekt/detekt is configured in detekt.yml:
complexity:
LongMethod:
threshold: 60 # Max method length
ComplexMethod:
threshold: 15 # Max cyclomatic complexity
style:
MagicNumber:
active: false # Disabled - too noisy
WildcardImport:
active: false # ktlint handles this| Issue | Description | Solution |
|---|---|---|
LongMethod |
Method exceeds 60 lines | Extract smaller functions |
ComplexMethod |
Too many branches | Simplify logic |
NestedBlockDepth |
Too deeply nested | Early returns, extract functions |
TooManyFunctions |
Too many functions in file | Split into multiple files |
For intentional violations, use @Suppress:
@Suppress("LongMethod", "ComplexMethod")
fun complexLegacyCode() {
// Complex logic that can't be easily refactored
}Quality checks run automatically on:
- Triggers: Push to
main, Pull Requests, Manual - Runs: ktlint + detekt
- Reports: Artifacts uploaded, PR comments posted
- Triggers: Pull Requests
- Runs: Full MR ride (including quality checks)
- Blocks: PR merge if checks fail
- Triggers: Push to
main - Runs: Full CI ride (including quality checks)
Create .git/hooks/pre-commit:
#!/bin/sh
echo "🔍 Running code quality checks..."
# Run ktlint and detekt
./gradlew ktlintMainSourceSetCheck ktlintKotlinScriptCheck detekt
if [ $? -ne 0 ]; then
echo "❌ Code quality checks failed. Fix violations before committing."
echo "💡 Run './gradlew ktlintFormat' to auto-fix style issues."
exit 1
fi
echo "✅ Code quality checks passed!"Make executable:
chmod +x .git/hooks/pre-commitQuality checks are defined in .kite/segments/quality.kite.kts:
segments {
segment("ktlint") {
description = "Run ktlint code style checks on main sources"
dependsOn("compile")
execute {
exec("./gradlew", "ktlintMainSourceSetCheck", "ktlintKotlinScriptCheck")
}
}
segment("detekt") {
description = "Run detekt static analysis"
dependsOn("compile")
execute {
exec("./gradlew", "detekt")
}
}
segment("quality-checks") {
description = "Run all code quality checks"
dependsOn("ktlint", "detekt")
execute {
println("✅ All quality checks passed!")
}
}
}Quality checks are integrated into CI/MR rides:
.kite/rides/mr.kite.kts:
ride {
name = "MR"
flow {
segment("clean")
segment("compile")
parallel {
segment("ktlint") // ← Quality checks
segment("detekt") // ← Quality checks
segment("test-core")
// ... other tests
}
segment("build")
}
}# Quick check
./gradlew ktlintMainSourceSetCheck detekt
# Or use Kite CLI
kite-cli run quality-checks# Let ktlint fix most style issues
./gradlew ktlintFormat- Short methods: < 60 lines
- Low complexity: < 15 cyclomatic complexity
- Explicit imports: No wildcards
- No secrets in code: Use environment variables
When checks fail, review the reports:
# ktlint report
cat */build/reports/ktlint/ktlintMainSourceSetCheck/ktlintMainSourceSetCheck.txt
# detekt report (HTML)
open build/reports/detekt/detekt.htmlError: Wildcard import (cannot be auto-corrected)
Fix: Replace with explicit imports:
// ❌ Bad
import io.kite.core.*
// ✅ Good
import io.kite.core.Segment
import io.kite.core.RideError: Method exceeds 60 lines
Fix: Extract smaller functions:
// ❌ Bad
fun processData() {
// 100 lines of code
}
// ✅ Good
fun processData() {
validateInput()
transformData()
saveResults()
}Error: File contains 20 functions, limit is 15
Fix: Split into multiple files or use companion object:
// FileOperations.kt → Split into:
// - FileOperations.kt (core functions)
// - FileOperationsRead.kt (read operations)
// - FileOperationsWrite.kt (write operations)| File | Purpose |
|---|---|
.editorconfig |
Shared IDE/ktlint config (indentation, line endings) |
detekt.yml |
detekt rules and thresholds |
build.gradle.kts |
ktlint plugin configuration |
.kite/segments/quality.kite.kts |
Quality check segment definitions |