editorconfig na CI – automatyczna weryfikacja jednolitego stylu

błędy raportowane przez editorconfig-check

Większość języków programowania ma swój style checker tudzież linter. Czy to wystarcza? Jeśli do tej pory w repozytorium miałeś/aś tylko CheckStyle, to mam zamiar przekonać Cię, że warto dorzucić też plik .editorconfig. Napiszę też, czemu należy standard editorconfig wspierać na serwerze CI i jak to zrobić – moją propozycją jest narzędzie editorconfig-checker. Jego największą zaletą jest niezależność od typu naszego projektu. Czy to rozbudowana aplikacja w Javie albo TypeScripcie, czy repozytorium zbierające luźno powiązane skrypty w Pythonie i Bashu – zawsze będzie przydatne.

Mnie do wpięcia tego narzędzia do continuous integration przekonała typowa sytuacja – w pewnym repozytorium edytowałem dwa skrypty shellowe, w jednym ktoś narobił wcięcia spacjami, w drugim ktoś inny tabulacjami. Nie widać różnicy na pierwszy rzut oka, wstawiłem linijkę tu, linijkę tam. Patrzę w git diff, a tam moje linijki nie równają się z otoczeniem, do tego w zmianach pojawiło się \ No newline at end of file, czyli ktoś nie używał poprawnie ustawionego edytora tekstu. Potem znalazłem Dockerfile wcinany raz to spacjami, a raz to tabulacjami.

Co to editorconfig

Strona editorconfig.org opisuje schemat, którym możemy ustalić formatowanie dowolnej zawartości repozytorium. Dodajemy do katalogu głównego plik o nazwie .editorconfig. Tam możemy ustalać reguły obowiązujące zawsze, albo tylko dla określonego rozszerzenia:

root = true
[*]
charset = utf-8
end_of_line = lf
indent_style = space
indent_size = 4
insert_final_newline = true
max_line_length = 120
trim_trailing_whitespace = true

[*.yml]
indent_size = 2

Bardzo dużo narzędzi rozumie ten format i często nie musimy nic w tym celu robić. IntelliJ dostosuje swoje zachowanie, żeby go przestrzegać. Tak samo Kate, standardowy edytor w KDE. Idąc w zupełnie inną stronę; Visual Studio – nie ma problemu. Nawet vim to potrafi, chociaż potrzebny jest plugin.

Więcej informacji o narzędziach można znaleźć w https://editorconfig.org/#pre-installed.

Czemu editorconfig

Problemem typowych linterów jest, że każdy język programowania ma swój. Każdy ma specyficzny sposób konfiguracji, czyli jak jesteś programistą Javy i trafiasz do repozytorium .Neta, to nawet nie wiesz, gdzie zacząć szukać ustawień formatowania. Zazwyczaj też linter sprawdza tylko źródła jednego konkretnego języka programowania.

Tymczasem współcześnie w repozytorium mieszamy wiele rzeczy: są pliki w głównym języku programowania, ale też jakieś JSON-y z konfiguracją, jakiś YAML do deploymentu albo Continuous Integration. Te „poboczne” pliki są edytowane ręcznie przez ludzi, więc z jednej strony grożą im programiści z widzimisię i źle ustawionymi edytorami, a z drugiej są ważne dla poprawnego działania, więc dobrze, żeby dały się łatwo czytać, zrozumieć i zmodyfikować. Im też przydałby się linter.

Dlatego uważam, że każde repozytorium, bez wyjątku, powinno zawierać plik .editorconfig. To powinien być pierwszy krok, nawet zanim napiszecie tam pierwszą linijkę kodu.

Czemu CI

Mogło by się wydawać, że jesteśmy kryci. Mamy jeden plik konfiguracyjny pokrywający wszystko, co tylko chcemy objąć kontrolą wersji. Rozumie go mnóstwo edytorów. Wrzucamy 1 plik do repozytorium i zapominamy o problemach z formatowaniem? Nic z tego.

Ciągle trafiam na ludzi, którzy używają edytora tak, że łamie zasady zapisane w pliku editorconfig. Czy to zły edytor, czy źle skonfigurowany – nieważne. Takie przypadki zawsze będą, tak jak zawsze ludzie będą pushować commity, gdzie kod się nie kompiluje. Od wyłapywania takich rzeczy jest CI, a my jesteśmy od odpowiedniego konfigurowania tegoż CI.

editorconfig-checker

editorconfig-checker to konsolowe narzędzie kontrolujące przestrzeganie zasad zapisanych w pliku editorconfig. Obsługuje tylko część specyfikacji (trailing spaces, wcięcia i parę innych – aktualny stan znajdziecie na stronie).

Jest proste i napisane w Go. Co najfajniejsze, dostępny jest bardzo mały obraz Dockera opakowujący całość. Łatwo więc użyć go na CI i lokalnie, bez przejmowania się instalacją i kompatybilnością z systemem. Mogę wejść do katalogu ze źródłami i uruchomić jedną linijkę:

docker run --rm --volume=${PWD}:/check mstruebing/editorconfig-checker
błędy znalezione i wypisane przez narzędzie editorconfig-checker

Na GitLabie za to mam dzielony między repozytoriami job wyglądający mniej więcej tak:

editorconfig:
  image: ${CI_DEPENDENCY_PROXY_GROUP_IMAGE_PREFIX}/mstruebing/editorconfig-checker:2.7.2
  script:
    - cd $CI_PROJECT_DIR
    - ec

Często do repozytorium dodaję plik .ecrc, by zmienić domyślny sposób działania. Na przykład, w projektach otwieranych przez IntelliJ wyłączam sprawdzanie plików generowanych przez to IDE (łamią każde ograniczenia długości linii, jakie ustawię). Podobnie do wyjątków dodaję katalogi Gradle’a.

Czasem wyłączam kontrolowanie rozmiaru wcięcia, gdy używany jest specyficzny formater. Format tego pliku to JSON.

{
  "Exclude": [
    "build/",
    "\\.git/",
    "\\.gradle/",
    "\\.idea/",
    "\\.iml$",
    "gradlew"
  ],
  "Disable": {
    "IndentSize": true
  }
}

Ile linterów

To narzędzie nie zastępuje klasycznych linterów. Jeśli w repozytorium jest projekt w Kotlinie, uruchamiam tam też ktlint. Tak, oznacza to, że w jednym pliku .editorconfig jest konfiguracja 2 narzędzi robiących podobne rzeczy. Gdybym miał repozytorium w Javie, miałbym w nim pewne reguły napisane w pliku .editorconfig, a inne w checkstyle.xml, czyli nie miałbym jednego źródła prawdy o formatowaniu. Niezależnie od wybranego języka programowania, „marnuję” czas na CI, uruchamiając 2 różne narzędzia do „tego samego”, tzn. sprawdzania formatowania: mam klasyczny linter i dodatkowo editorconfig-checker.

Tak musi być i uważam, że i tak robię dobry interes. Klasyczny linter pozwala na wyegzekwowanie bardzo szczegółowych reguł specyficznych dla danego języka. Dla pozostałych typów plików mam narzędzie editorconfig-checker, które potrafi tylko proste rzeczy, ale za to zadziała wszędzie. Przez swoją prostotę działa też bardzo szybko, więc narzut jest pomijalny.

Creative Commons License
Except where otherwise noted, the content by Piotr Kubowicz is licensed under a Creative Commons Attribution 4.0 International License.