Git i zależności projektu
NICOLA PAOLUCCI
Deweloper Advocate
Zastanówmy się nad następującymi pytaniami:
Jak obsługujecie zależności projektu za pomocą git
?
Nasz projekt obejmuje wiele współzależnych repozytoriów. Obecnie zarządzamy nimi za pomocą svn:externals
. A jak najlepiej postępować z nimi przy użyciu git
?
Jak podzielić bardzo duże repozytorium na mniejsze komponenty za pomocą Git
?
Oto kilka najczęściej zadawanych pytań.
Ten temat wydaje się sporą bolączką dla wielu zespołów zajmujących się tworzeniem oprogramowania, które wdrażają git
, dlatego w tym artykule postaram się rzucić nieco światła na to zagadnienie.
Oczywiście zależności w projekcie oraz infrastruktura kompilacji to dwa przenikające się wzajemnie obszary, a nawet wewnętrznie w Atlassian dyskusja zeszła na temat „przyszłości kompilacji”.
Tworzenie odrębnych repozytoriów, w odróżnieniu od pracy na jednym repozytorium, może utrudnić wiele rzeczy. Jest to jednak stosunkowo naturalny, a czasami wręcz obowiązkowy krok w ewolucji projektu oprogramowania z co najmniej dwóch powodów: wydłużających się czasów kompilacji oraz współdzielonych zależności między projektami.
Ogólny zarys: wytyczne i nieoptymalne rozwiązania
Wróćmy do pytania: jak śledzić zależności w projekcie i zarządzać nimi przy użyciu git
?
W miarę możliwości wcale tego nie robić!
Żarty żartami, ale poznajmy najpierw odpowiedź ogólną, zanim zagłębimy się w szczegóły. Pamiętaj, że nie istnieje żadne panaceum — ani w git
ani w żadnym innym rozwiązaniu — które w bezbolesny sposób pozwoli rozwiązać wszystkie problemy związane z zależnościami w projekcie.
W miarę jak projekt rozrasta się powyżej pewnego rozmiaru, rozsądnym rozwiązaniem jest podzielenie go na składniki logiczne, ale nie warto z tym czekać do momentu, gdy zostaniemy z jednym repozytorium zawierającym ponad 100 milionów wierszy kodu. Poniżej przedstawiam jedynie wskazówki, na podstawie których możesz opracować własne podejście.
materiały pokrewne
Instalacja środowiska Git
POZNAJ ROZWIĄZANIE
Poznaj środowisko Git z rozwiązaniem Bitbucket Cloud
Pierwszy wybór: użyć zamiast Git odpowiedniego narzędzia do obsługi kompilacji/zależności
Narzędzie do zarządzania zależnościami jest obecnie moim zalecanym sposobem radzenia sobie z narastającymi problemami i czasem kompilowania dużych projektów.
Warto rozdzielać moduły w poszczególnych repozytoriach i zarządzać ich wzajemnymi zależnościami przy użyciu narzędzia specjalnie przystosowanego do tego zadania. Istnieje takie narzędzie dla (niemal) każdego zestawu technologii. Oto przykłady:
- Maven (lub Gradle), jeśli używasz języka Java
- Npm w przypadku aplikacji Node.js
- Bower, Component.io itp., jeśli używasz języka JavaScript (zaktualizowane!)
- Pip i requirements.txt, jeśli używasz języka Python
- RubyGems, Bundler, jeśli używasz Ruby
- NuGet w przypadku .NET
- Ivy (lub niestandardowe działanie CMake) w przypadku C++ (zaktualizowane!)
- CocoaPods w przypadku aplikacji Cocoa dla systemu iOS
- Composer lub Phing w przypadku PHP (dodane!)
- W Go infrastruktura kompilacji/zależności jest w pewnym sensie wbudowana w język (choć pracowano już nad bardziej kompleksowym rozwiązaniem, patrz godep). Na potrzeby naszego serwera Git (Bitbucket) wykorzystujemy rozwiązania Maven i Bower. W momencie kompilowania wybrane narzędzie ściągnie właściwe wersje zależności, umożliwiając skompilowanie głównego projektu. Niektóre z tych narzędzi mają ograniczenia i przyjmują mało optymalne założenia, ale są sprawdzone i przydatne.
Bolączki związane z dzieleniem projektu
W uproszczeniu, na początku projektu wszystko jest spakowane w jednej kompilacji. Jednak w miarę rozrastania się projektu kompilacja może się stawać powolna. Wówczas konieczne jest zastosowanie „buforowania” i na tym etapie wkracza zarządzanie zależnościami. Przy okazji oznacza to, że moduły podrzędne (patrz niżej) doskonale sprawdzają się na przykład w językach dynamicznych. Zasadniczo uważam, że na pewnym etapie dla większości ludzi czasy kompilacji są zmartwieniem, dlatego należy korzystać z narzędzia do zarządzania zależnościami.
Dzielenie komponentów na odrębne repozytoria wiąże się z pewnymi trudnościami. Wymieńmy je w przypadkowej kolejności:
- Dokonanie zmiany w komponencie wymaga wydania.
- Wymaga czasu i może się nie powieść z wielu błahych powodów.
- Wydaje się głupie w przypadku drobnych zmian.
- Wymaga ręcznego konfigurowania nowych kompilacji dla każdego komponentu.
- Utrudnia wykrywalność repozytoriów.
- Refaktoryzacja, gdy nie cały kod źródłowy jest dostępny w jednym repozytorium.
- W niektórych konfiguracjach (takich jak nasza) zaktualizowanie interfejsów API wymaga wydania kamienia milowego produktu, następnie wtyczki, a potem znów produktu. Prawdopodobnie pominęliśmy kilka rzeczy, ale masz chociaż ogólne pojęcie. Jeszcze daleko nam do idealnego rozwiązania.
Drugi wybór: użycie polecenia git submodule
Jeśli nie możesz lub nie chcesz używać narzędzia do obsługi zależności, git
ma funkcję umożliwiającą obsługiwanie modułów podrzędnych (submodule
). Moduły podrzędne bywają wygodne, zwłaszcza w przypadku języków dynamicznych. Niekoniecznie jednak pozwolą wyeliminować problem powolnych kompilacji. Pisałem już o pewnych wytycznych i poradach na ich temat, a także przedstawiłem przegląd alternatyw. W Internecie można też znaleźć argumenty świadczące przeciwko nim.
Zgodność 1:1 między svn:external i git
ALE! Jeśli potrzebujesz zgodności 1:1 między svn:externals
a git
, użyj modułów podrzędnych
, upewniając się, żeśledzą one jedynie gałęzie wydań, a nie losowe commity.
Trzeci wybór: używanie innych narzędzi do obsługi kompilacji i zależności między zestawami technologii
Nie zawsze mamy do czynienia z projektem całkowicie jednorodnym, który można kompilować i asemblować przy użyciu jednego narzędzia. Przykładowo niektóre projekty dla urządzeń mobilnych wymagają obsługi zależności między obiektami Java i C++ albo stosowania autorskich narzędzi do generowania zasobów. W przypadku tych bardziej złożonych sytuacji można rozbudować git
o dodatkową warstwę na wyższym poziomie. Doskonałym przykładem takiej sytuacji jest repozytorium systemu Android.
Inne narzędzia do obsługi kompilacji, którym warto się przyjrzeć:
Wnioski i dodatkowe materiały
Dodatkowe materiały na temat budowy infrastruktury (i Maven) proponowane przez Charlesa O'Farrella:
- W poszukiwaniu ostatecznego narzędzia do obsługi kompilacji
- I kontynuacja — Maven jest z założenia uszkodzony
- Rekurencyjny Maven uznany za szkodliwy
Na zakończenie posłużę się fantastycznym cytatem z ostatniego spośród wspomnianych powyżej artykułów. Choć odnosi się on do narzędzia Maven, równie dobrze można zastosować go do innych narzędzi do obsługi kompilacji i zależności:
„Jedynym zadaniem pamięci podręcznej jest przyspieszanie działania. Możesz ją całkowicie wyeliminować, a otaczający ją system będzie działał tak samo, tylko wolniej. Pamięć podręczna nie ma też żadnych skutków ubocznych. Bez względu na to, co robiono z nią w przeszłości, konkretne zapytanie skierowane do pamięci podręcznej ponowione w przyszłości zwróci taką samą wartość.
Środowisko Maven zdecydowanie różni się od tego, co opisuję. Repozytoria Maven są używane jak pamięć podręczna, ale nie mają właściwości pamięci podręcznych. Gdy tworzysz zapytanie do repozytorium Maven, bardzo istotne są czynności, które wykonano w przeszłości. Zapytanie zwraca najnowszy wynik przekazany do repozytorium. Jeśli wykonasz zapytanie o element, zanim umieścisz go w repozytorium, może nawet wystąpić błąd”.
Udostępnij ten artykuł
Następny temat
Zalecane lektury
Dodaj te zasoby do zakładek, aby dowiedzieć się więcej na temat rodzajów zespołów DevOps lub otrzymywać aktualności na temat metodyki DevOps w Atlassian.