okruch-kodu

Frontendowy pre-commit hook - czyli lintowanie plików przed commitem.

Lepiej zapobiegać niż leczyć

Samodyscyplina oraz czujne oczy koleżanek i kolegów nie zawsze wystarczą, żeby ustrzec się błędów w kodzie.
Z drugiej strony nawet najbardziej uważnemu programiście zdarza się przegapić złe formatowanie kodu, zbędne puste linie, nieużywane importy i inne rzeczy, które mogą spowodować, że commit nie przejdzie pomyślnie przez code review.
Dlatego przy dbaniu o jakość i czytelność kodu warto wspomagać się narzędziami.

Wczesne wykrywanie błędów

Najlepiej sprawdzić zmieniany kod jeszcze przed jego wysłaniem do repozytorium. Można łatwo zrobić, przed commitem uruchamiając tslint, eslint lub inne narzędzia, które mamy użyte w projekcie.
Jednak taki manualny proces jest po pierwsze uciążliwy, bo zawsze trzeba pamiętąć, po drugie lintowanie całego kodu może trwać długo i być zupełnie niepotrzebne (np. jeśli commitujemy zmiany w plikach CSS nie ma sensu lintowanie wszystkich plików JS Eslintem).
Dlatego polecam korzystanie ze skryptu, który uruchomiony jako git-hook przed commitem sprawdzi pliki dodane do tego commita odpowiadającymi im linterami. Innymi słowi jeśli commituję pliki .js lub .ts to będą one (tylko te, które dodałem do commita) “przejechane” tslintem i eslintem, a pliki .scss stylelintem. Poniżej pokażę skrypt, którego używam na co dzień i opiszę jego działanie.

Pre-commit hook

Git dzięki git-hooks umożliwia urachamianie skryptów przy okazji występowania różnych zdarzeń w (commit, push, rebase itp) repozytorium.
Jednym z takich zdarzeń jest commit. Hook pre-commit jest to skrypt lub polecenie uruchamiane przed commitem i jeśli zakończy swoje działanie niepowodzeniem to commit się nie wykona.

Frontendowy pre-commit hook

Gotowy skrypt wygląda następująco:

Założenia

Powyższego skryptu używam w projektach pisanych w Angularze, z użyciem Typescriptu i SCSS.
Pliki .ts są lintowane tslintem oraz eslintem, gdyż w tslincie brakuje mi reguł np. dotyczących grupowania importów.
Pliki .scss są lintowanie stylelintem.
Wszystkie trzy lintery są dodane do devDependencies projektu i uruchamiane są ich lokalne wersje katalogu node_modules.
Dodatkowo skrypt wykrywa użycie słów console i debugger.

Omówienie skryptu

Pierwsze linijki skryptu:

1
2
3
4
COMMIT_TS_FILES=`git diff --cached --diff-filter=MA --name-only | grep \.ts$`;
COMMIT_STYLE_FILES=`git diff --cached --diff-filter=MA --name-only | grep \.scss$`;
COMMIT_FILES=`git diff --cached --diff-filter=MA --name-only | grep \.ts$`;
UNCOMMITED_CHANGES_IN_COMMIT_FILES=$(git diff --name-only -- `git diff --cached --name-only`);

to deklaracje zmniennych zawierających nazwy plików które zostały dodane do commita (COMMIT_TS_FILES i COMMIT_STYLE_FILES) oraz lista plików, które zostały dodane do commita a następnie zmienione (np. zostały porpawione w nich błędy).

W linii 8. znajduje się blok, który sprawdza czy nie zapomnieliśmy dodać poprawek w commitowanych plikach do commita.
Dzięki temu nie dojdzie do sytuacji, w której znalezione błędy zostaną poprawione ale nie scommitowane.

Fragment

1
2
3
echo "[ check for ugly words]"
git diff --cached --diff-filter=MA | grep console\. | grep ^\+
git diff --cached --diff-filter=MA | grep debugger | grep ^\+

wypisuje ewentualne wystąpienia słów console i debugger w commitowanych plikach.

W dalszej części skryptu pliki .ts (o ile są w commicie) są lintowane eslintem i tslintem a ewentualne pliki .scss - stylelintem.
[[ "$?" -eq "0" ]] || exit 1; oznacza, że jeśli ostatnio wykonane polecenie (np. tslint,eslint,stylelint) zwróci błąd to cały skrypt zakończy działanie z kodem błędu i commit się nie powiedzie.

Kiedy i jak ten skrypt jest wykonywany?

Skrypt jest dodany do repozytorium, a jest wykonywany przed każdym commitem jako git-hook dzięku użyciu husky. W ten sposób mogą z niego korzystać wszyscy, którzy edytują kod.

Z moich osobistych doświadczeń wynika, że korzystanie z takiego rozwiązania pozwala na wczesne wykrycie możliwych błędów, zmiejsza ilość “cofek” z code review i pomaga pisać kod zgodny z ustalonymi konwencjami.

wpis dodany 2018-03-20
z serii:
tematy: