Potrzebuję pomocy w zakresie filozofii i projektu konfiguracji ciągłej integracji.
Nasza obecna konfiguracja CI używa buildbota. Kiedy zacząłem go projektować, odziedziczyłem (cóż, niekoniecznie, ponieważ rok wcześniej byłem zaangażowany w jego projektowanie), niestandardowego konstruktora CI, który został dostosowany do uruchamiania całej wersji naraz. Po pewnym czasie zdecydowaliśmy, że to jest niewystarczające, i zaczęliśmy badać różne frameworki CI, ostatecznie wybierając buildbot. Jednym z moich celów w przejściu na buildbota (oprócz cieszenia się wszystkimi dodatkami typu whiz-bang) było przezwyciężenie niektórych niedociągnięć naszego na zamówienie nocnego konstruktora.
Żartujcie mi przez chwilę i pozwólcie mi wyjaśnić, co odziedziczyłem. Bazą kodu dla mojej firmy jest prawie 150 unikalnych aplikacji systemu Windows w języku c ++, z których każda jest zależna od jednej lub kilkunastu bibliotek wewnętrznych (a także wielu od bibliotek stron trzecich). Niektóre z tych bibliotek są współzależne i mają różne aplikacje, które (choć nie mają ze sobą nic wspólnego) muszą być zbudowane z tej samej kompilacji tej biblioteki. Połowa z tych aplikacji i bibliotek jest uważana za „starszą” i nieprzenośną i musi być zbudowana z kilku różnych konfiguracji kompilatora IBM (dla których napisałem unikalne podklasy Compile
), a druga połowa jest zbudowana ze studiem wizualnym.ShellCommand
s, ponieważ nie ma obsługi VSS).
Nasz oryginalny nocny konstruktor po prostu usunął źródło wszystkiego i zbudował rzeczy w określonej kolejności. Nie było sposobu na zbudowanie tylko jednej aplikacji, wybranie wersji lub grupowanie rzeczy. Uruchomiłoby maszyny wirtualne, aby zbudować wiele aplikacji. Nie był bardzo solidny, nie można go było dystrybuować. To nie było strasznie rozszerzalne. Chciałem być w stanie pokonać wszystkie te ograniczenia w buildbocie.
Sposób, w jaki to zrobiłem pierwotnie, polegał na tworzeniu wpisów dla każdej aplikacji, którą chcieliśmy zbudować (wszystkie 150 z nich), następnie tworzeniu wyzwalanych harmonogramów, które mogłyby budować różne aplikacje jako grupy, a następnie subskrybować te grupy w ramach ogólnego harmonogramu budowania na noc. Mogą one działać na dedykowanych urządzeniach podrzędnych (koniec z maszyną wirtualną), a gdybym chciał, mógłbym po prostu dodać nowych urządzeń podrzędnych. Teraz, jeśli chcemy wykonać pełną kompilację poza harmonogramem, jest to jedno kliknięcie, ale możemy również zbudować tylko jedną aplikację, jeśli chcemy.
Istnieją jednak cztery słabości tego podejścia. Jednym z nich jest złożona sieć zależności naszego drzewa źródłowego. W celu uproszczenia konserwacji konfiguracji wszystkie generatory są generowane z dużego słownika. Zależności są pobierane i budowane w niezbyt solidny sposób (mianowicie poprzez wyłączenie pewnych rzeczy z mojego słownika docelowego). Po drugie, każda kompilacja ma od 15 do 21 kroków kompilacji, które są trudne do przeglądania i przeglądania w interfejsie internetowym, a ponieważ istnieje około 150 kolumn, ładowanie trwa wiecznie (pomyśl od 30 sekund do wielu minut). Po trzecie, nie mamy już automatycznego wykrywania celów kompilacji (chociaż chociaż jeden z moich współpracowników dręczy mnie na ten temat, nie widzę, co nas do tego doprowadziło). Wreszcie,
Teraz, przechodząc do nowego rozwoju, zaczynamy używać g ++ i subversion (nie przenosząc starego repozytorium, pamiętaj - tylko dla nowych rzeczy). Zaczynamy też przeprowadzać więcej testów jednostkowych („więcej” może dać zły obraz ... bardziej jak każdy inny ) oraz testów integracyjnych (przy użyciu Pythona). Trudno mi znaleźć sposób, aby dopasować je do mojej istniejącej konfiguracji.
Więc gdzie popełniłem błąd filozoficznie tutaj? Jak najlepiej przejść dalej (dzięki buildbot - to jedyny element układanki, na którym mam licencję), aby moja konfiguracja była możliwa do utrzymania? Jak rozwiązać niektóre słabości mojego projektu? Co tak naprawdę działa w zakresie strategii CI dla dużych (prawdopodobnie nadmiernie) złożonych baz kodowych?
EDYTOWAĆ:
Myślałem, że wyjaśniłem swój problem, ale oczywiście nie byłem wystarczająco jasny. Ja nie szuka sugestii zmianę platformy CI. To się nie stanie, a odpowiedzi sugerujące, że nie zostaną zaakceptowane. Co ja chcę wiedzieć, jak inni ludzie zarządzać skomplikowanymi codebases wykorzystaniem CI. Mam tuzin różnych produktów i mam zależności rozrzucone na wiatr i wszystkie są różne. To jest to, co chcę wiedzieć, jak sobie z tym poradzić.
Odpowiedzi:
Chociaż nie spotkałem się z sytuacją tak złą, jak to opisaliście, utrzymuję konfigurację CI z dziesiątkami komponentów, między którymi istnieją pewne „proste” zależności. Mam nadzieję, że moje podejście może dać ci wskazówki, jak postępować. Problem zdecydowanie nie jest związany tylko z wyborem serwera CI, ale także z ogólnym procesem kompilacji i strukturą projektu.
Podzielę problem na dwie części: budynek i CI.
Budynek
Dla „Budowania” mam na myśli proces zmiany kodu źródłowego na końcowy artefakt. Nasza firma używa głównie Java do programowania, a narzędziem budowania, którego użyliśmy, jest Maven. Prawdopodobnie nie będziesz mógł tego użyć ze względu na naturę swojego projektu, ale warto wspomnieć o pewnej wartościowej koncepcji w Maven:
1) W świecie Maven każdy artefakt (lib, prawdziwy program itp.) Musi być wyraźnie odizolowany i oddzielony. Zależności między artefaktami powinny być jasne. Powinienem podkreślić, że niechlujna zależność, zwłaszcza zależności cykliczne między artefaktami kompilacji, spowodują, że proces kompilacji będzie bałaganem.
Na przykład widziałem wcześniej kilka projektów Java, chociaż po całym procesie kompilacji jest kilka gotowych plików JAR (można to uznać za lib / dll w Javie), są one w rzeczywistości wzajemnie zależne. Podobnie, A.jar używa rzeczy w B.jar i na odwrót. Tego rodzaju „modularyzacja” jest całkowicie bez znaczenia. A.jar i B.jar zawsze muszą być wdrażane i używane razem. Implikacja jest taka, że później, jeśli chcesz je rozdzielić na różne projekty (na przykład do ponownego wykorzystania w innych projektach), nie będziesz w stanie tego zrobić, ponieważ nie możesz określić, który projekt A lub B zbudować jako pierwszy.
Tak, musi to być uwzględnione w projekcie oprogramowania, ale zawsze wierzę, że zamiast poświęcać czas na skomplikowane narzędzia budowlane / model działa w bałaganiarskim projekcie, wolałbym poświęcić czas na reorganizację projektu, aby użyć prosty model budynku.
2) Zależności powinny być deklaratywne. Widziałem wiele procesów kompilacji, które obejmują wszystkie biblioteki potrzebne lokalnie w projekcie. Sprawi, że proces kompilacji będzie wyjątkowo kłopotliwy, jeśli niektóre biblioteki są w rzeczywistości innymi artefaktami, które musisz zbudować.
3) „Scentralizowana” pamięć dla artefaktów w celu uzyskania zależności lub wdrożenia artefaktów po ich skompilowaniu. Nie musi być „scentralizowany” dla całego biura (będzie świetnie, jeśli tak jest), tylko lokalny katalog już będzie w porządku.
Więcej szczegółów na temat 2 i 3. Aby dać przykład, natknąłem się na projekt obejmujący 3 niezależne projekty. Każdy projekt jest zbudowany w oparciu o źródło, a także biblioteki lib w katalogu lib / w katalogu źródłowym. Projekt A zbuduje kilka bibliotek, które z kolei są wykorzystywane w projektach B i C. Istnieją pewne wady: procedura kompilacji jest skomplikowana i trudniejsza do zautomatyzowania; kontrola źródła jest rozdęta niepotrzebnymi zduplikowanymi plikami JAR ponownie wykorzystywanymi w innym projekcie
W świecie Mavena projekt B i C tak naprawdę nie zawiera A.jar (i innych zależności, takich jak inne biblioteki stron trzecich) w źródle projektu. To jest deklaratywne. Na przykład, w konfiguracji kompilacji projektu B, po prostu deklaruje, że potrzebuje: A.lib v1.0, xyz.lib v2.1 itd., A skrypt kompilacji wyszuka lib z /A/1.0/A. jar i /xyz/2.1/xyz.lib
W świecie innym niż Maven katalog artefaktów musi być tylko jednym lub dwoma katalogami o uzgodnionej strukturze katalogów. Możesz umieścić wszystkie biblioteki stron trzecich w lokalizacji udostępniania i zezwolić programistom na synchronizację lub kopiowanie na ich lokalnych komputerach. W moich projektach C ++, które robiłem wiele lat wcześniej, robię to ustawienie biblioteki lib i nagłówka na $ {artefact_dir} / lib_name / ver, z artefact_id zadeklarowanym jako zmienna środowiskowa.
Kiedy projekt A jest budowany, będzie miał kopię swojego wyniku w tym katalogu artefaktów, tak że kiedy budujemy projekt B, B może uzyskać wynik A automatycznie, bez ręcznego kopiowania.
4) Niepowtarzalne wydanie. Po wydaniu A.lib 1.0, to wszystko, nie spodziewasz się, że zawartość A.lib 1.0 zmieni się po 1 miesiącu, tylko dlatego, że są pewne poprawki błędów. W takim przypadku powinien to być A.lib 1.1. Artefakt zmieniającej się bazy kodu powinien uwzględniać specjalną „wersję”, dla której w Maven nazywamy ją migawką.
Wydanie niezmienne jest bardziej kwestią etyczną. Ale to, co rozwiązało, jest jasne: kiedy masz wiele projektów, korzystających z tej samej biblioteki lib, wiesz, z której wersji biblioteki korzystasz i masz pewność, że ta sama wersja biblioteki lib używana w różnych projektach jest rzeczywiście taka sama . Myślę, że wielu z nas przeszło przez problem: dlaczego zarówno X, jak i Y używają lib A, ale wynik kompilacji jest inny? (i okazuje się, że użycie lib A przez X i Y jest w rzeczywistości różne po wkopaniu w zawartość lub rozmiar pliku lib).
Wszystkie te zapewniają, że twoje projekty mogą budować niezależnie, bez wielu ręcznych sztuczek, takich jak, najpierw zbuduj projekt A, skopiuj A.lib do projektu B, a następnie zbuduj projekt B ...
Po przejściu takiego ćwiczenia, na przykład podczas tworzenia projektu A, spróbuje on uzyskać zależności ze scentralizowanego magazynu artefaktów. Jeśli nie zostaną znalezione pewne zależności (które są innymi projektami Twojej firmy, np. Projekt B), musisz uzyskać źródło projektu B, zbudować go (który zostanie wdrożony w scentralizowanej pamięci masowej po sukcesie) i następnie ponownie zbuduj projekt A.
CI
Dzięki łatwemu systemowi kompilacji i wyraźnym zależnościom CI będzie znacznie łatwiejsze. Spodziewam się, że Twój serwer CI spełnia następujące wymagania:
1) Monitoruj kontrolę źródła, kasa + kompilacja tylko w przypadku zmian w źródle
2) Potrafi skonfigurować zależności między projektami
Z wyraźną zależnością dla projektów, wystarczy skonfigurować projekty w CI zgodnie z rzeczywistymi projektami, skonfigurować zależność projektu CI zgodnie z zależnością twoich projektów. Twój serwer CI powinien być ustawiony na budowanie projektów zależnych przed budowaniem jakiegokolwiek projektu (i oczywiście kompilacja ma miejsce tylko wtedy, gdy źródło projektu naprawdę się zmieniło).
Jeśli wszystko pójdzie dobrze, powinieneś mieć ogromny, złożony, ale wciąż możliwy do zarządzania CI (a co ważniejsze, zarządzalną strukturę projektu)
źródło
Co zadziałało w podobnych sytuacjach dla mnie:
Sugerowałbym, abyś traktował samą kompilację jako projekt rozwoju oprogramowania. Oznacza to, że musisz zmodularyzować bazę kodu kompilacji i napisać dla niej kilka zautomatyzowanych testów (np. Zbuduj znany numer wersji i sprawdź, czy daje poprawne wyniki).
źródło
Uważam Jenkins za serwer CI, możesz zobaczyć funkcje tutaj:
https://wiki.jenkins-ci.org/display/JENKINS/Meet+Jenkins
Dlaczego? Jest łatwy w instalacji, ma wspaniały interfejs, jest łatwy do skonfigurowania i rozszerzenia, i jest WIELE gotowych wtyczek do prawie wszystkiego:
https://wiki.jenkins-ci.org/display/JENKINS/Plugins
Spróbuj :)
źródło
Wcześniej korzystaliśmy z Go by ThoughtWorks , korzystaliśmy z CruiseControl.Net . Jest to przydatne, biorąc pod uwagę, że mamy dwie drużyny w połowie świata od siebie i między nami jest duża różnica stref czasowych.
Zarządzamy wieloma projektami, a większość zadań obejmuje nakładanie się dwóch programistów po obu stronach globu, dlatego musimy pamiętać, że wszystkie pliki nie powinny powodować awarii kompilacji innej osoby.
Również zarządzanie jest łatwiejsze dzięki Go, a ponieważ jest agresywnym procesem, dało nam mniejsze bóle głowy po zainstalowaniu. Testy zostały również zintegrowane z Go.
źródło