Ein typischer Fall: Dein Team hat sich darauf geeinigt, dass Repository-Klassen
mit Repository enden müssen. Bisher wird das im PR-Review nur angemerkt.
Mit CodeCharter wird es vom Build durchgesetzt.
Regelmenge im Repo anlegen
Im Repo-Root:
mkdir -p rules
codecharter config add rules rules
In dieses Verzeichnis kommen alle .ccr-Dateien. Der zweite Befehl
deklariert es unter rules: in .codecharter/config.yml (und legt die
Datei bei Bedarf an): Ein Lauf liest nur die Regelverzeichnisse, die die
Config deklariert, nie einen Ordner, nur weil er existiert. Um ein
Verzeichnis ohne Deklaration auszuprobieren, gib es stattdessen mit
--rules <dir> an.
Die Regel schreiben
rules/repository-naming.ccr:
@name "Repository class must end in 'Repository'"
@severity error
@category "Naming"
@description "Implementations of the Repository pattern must have a 'Repository' suffix for discoverability"
@recommendation "Rename the class to end with 'Repository', e.g. 'UserData' -> 'UserRepository'"
from t in Types
where t.Kind == "Class"
where t.Namespace.FullName.StartsWith("Acme.Infrastructure.Repositories")
where !t.IsAbstract
where !t.Name.EndsWith("Repository")
select t
Anatomie:
@nameist der menschenlesbare Titel im Tooling. Der Slug der Regel (der in Findings und Suppressions erscheint) ist der Dateiname ohne Erweiterung, also für diese Regelrepository-naming.@severityistinfo,warnodererror. Standard istwarn, wenn weggelassen.@categorygruppiert die Regel im Output (optional).@descriptionbeschreibt, was geprüft wird (optional, aber empfohlen).@recommendationsagt, wie man es behebt (optional, aber empfohlen).- Body: eine LINQ-Query gegen das Schema. Hier suchen wir Klassen im Repository-Namespace, die nicht abstrakt sind und deren Name nicht auf "Repository" endet.
Lokal probieren
Prüfe zuerst, ob die Regel parst und typkorrekt ist:
codecharter validate rules/repository-naming.ccr
Dann startest du die Analyse. Das Argument von analyze ist der Pfad zu einer
.sln-, .slnx- oder .csproj-Datei:
codecharter analyze MySolution.sln
Wenn deine Solution eine Klasse namens UserData im Repository-Namespace
enthält, erscheint sie jetzt als error.
Hinweis: Die
Rules:-Zeile auf stderr nennt jede Quelle, die der Lauf gelesen hat, zum BeispielRules: 1 from rules. Meldet sie, dass keine Regelquelle konfiguriert ist, fehlt derrules:-Eintrag.
Ob die Regel wirklich anschlägt, prüfst du am besten mit einer Spec aus
Hit- und Miss-Fällen: codecharter test rules/repository-naming.ccr --scaffold
erzeugt eine repository-naming.spec.md neben der Regel, und
codecharter test rules/ führt alle Specs aus.
Was sich noch alles ausdrücken lässt
Die DSL ist eine Untermenge von LINQ. Du kannst auf alle Typ-, Methoden-, Property-, Feld-, Event-, Namespace- und Assembly-Eigenschaften zugreifen. Beispiele:
// Methoden ohne CancellationToken finden
from m in Methods
where m.IsAsync
where !m.Parameters.Any(p => p.TypeShortName == "CancellationToken")
where m.AccessModifier == "Public"
select m
// Layer-Verletzung: Domain referenziert Web
from t in Types
where t.Namespace.FullName.StartsWith("Acme.Domain")
where t.UsedTypes.Any(u => u.Namespace.FullName.StartsWith("Acme.Web"))
select t
// Klassen mit zu vielen Methoden
from t in Types
where t.Kind == "Class"
where t.NumberOfMethods > 25
select t
Schema-Übersicht und weitere Details unter Syntax-Übersicht.
Best Practices für eigene Regeln
- Eine Regel pro
.ccr-Datei. Das ist eine Eigenschaft des Formats: jede.ccr-Datei enthält genau eine Query, und der Dateiname wird zur Rule-ID. - Sprechende Dateinamen. Sie tauchen im Build-Log auf.
@recommendationehrlich. Wenn die Empfehlung nichts taugt, deaktivieren die Entwickler die Regel, statt sie zu beheben.- Klein anfangen. Eine Regel, die zu 50 Findings führt, lässt sich
nicht einführen. Stufe sie auf
warnab oder formuliere sie spezifischer.
Mehr unter Best Practices.