Git en projectafhankelijkheden
Nicola Paolucci
Developer Advocate
Denk na over de volgende vragen:
Hoe ga je om met projectafhankelijkheden met git
?
Ons project bestaat uit meerdere onderling afhankelijke repository's. Op dit moment beheren we die met svn:externals
. Wat is de beste manier om die met git
te verwerken?
Hoe verdeel je een zeer grote repository in kleinere componenten met behulp van git
?
Dit zijn onze meest gestelde vragen.
Dit onderwerp lijkt een groot pijnpunt te zijn voor veel softwareteams die git
gebruiken. Daarom zal ik in dit artikel proberen om wat licht op het probleem te werpen.
Het is duidelijk dat projectafhankelijkheden en de infrastructuur van builds twee met elkaar verweven gebieden zijn, en zelfs intern bij Atlassian ontstond er een discussie over de 'toekomst van builds'.
Het hebben van afzonderlijke repository's in plaats van één repository kan sommige dingen moeilijker maken. Maar het is een relatief natuurlijke (en soms verplicht) stap in de evolutie van een softwareproject minstens twee belangrijke redenen: langere buildtijden en gedeelde afhankelijkheden tussen projecten.
Schilderen met grove kwasten: richtlijnen en suboptimale oplossingen
Dus terug naar de vraag: Hoe volg en beheer je projectafhankelijkheden met git
?
Indien mogelijk, doe je dat niet!
Grapjes terzijde, laat me eerst in grote lijnen antwoorden en daarna dieper hierop ingaan. Realiseer je alsjeblieft dat er geen wondermiddel bestaat (in git
of anderszins) waarmee alle problemen die verband houden met projectafhankelijkheden pijnloos kunnen worden opgelost.
Als een project eenmaal groter is dan een bepaalde omvang, is het verstandig om het op te splitsen in logische componenten, maar wacht niet tot je meer dan 100 miljoen regels code in één repository hebt voordat je dat doet. Het volgende zijn dus slechts richtlijnen, zodat je je eigen aanpak kunt bedenken.
gerelateerd materiaal
Git installeren
Oplossing bekijken
Git leren met Bitbucket Cloud
Eerste keuze: gebruik een geschikte build-/afhankelijkhedentool in plaats van git
Een hulpmiddel voor het beheer van onderlinge relaties is op dit moment mijn aanbevolen oplossing om de groeipijnen en de buildtijden van omvangrijke projecten aan te pakken.
Houd je modules gescheiden in afzonderlijke repository's en beheer hun onderlinge afhankelijkheid met een tool die daarvoor speciaal ontwikkeld is. Er is er een voor (bijna) elke bestaande technologiestack. Enkele voorbeelden:
- Maven (of Gradle) als je Java gebruikt
- Npm voor node-apps
- Bower, Component.io, enz. als je Javascript gebruikt (bijgewerkt!)
- Pip en requirements.txt als je Python gebruikt
- RubyGems, Bundler als je Ruby gebruikt
- NuGet voor .NET
- Ivy (of een aangepaste CMake-actie) voor C++ (bijgewerkt!)
- CocoaPods voor Cocoa iOS-apps
- Composer of Phing voor PHP (toegevoegd!)
- In Go is de build/afhankelijkheden-infrastructuur in zekere mate ingebouwd in de taal (hoewel er is gewerkt aan een completere oplossing, zie godep). Voor onze Git-server (Bitbucket) gebruiken we zowel Maven als Bower. Tijdens de build zal de tool naar keuze de juiste versies van de afhankelijkheden pullen, zodat je hoofdproject kan worden gebouwd. Sommige van deze tools hebben beperkingen en maken aannames die niet optimaal, maar wel bewezen en haalbaar zijn.
De pijn van het splitsen van je project
Simpel gezegd, aan het begin van een project zit alles in één build. Maar naarmate het project groeit, kan dat ertoe leiden dat je build te traag wordt. Op dat moment heb je 'caching' nodig, en dat is waar beheer van onderlinge relaties een rol speelt. Dit betekent trouwens dat submodules (zie hieronder) zich heel goed lenen voor bijvoorbeeld dynamische talen. In feite denk ik dat de meeste mensen zich op een gegeven moment zorgen moeten maken over de buildtijden. Daarom moet je een tool voor het beheer van onderlinge relaties gebruiken.
Componenten opsplitsen in afzonderlijke repository's brengt nogal wat pijn met zich mee. In willekeurige volgorde:
- Om een component te wijzigen is een release vereist
- Kost tijd en kan om veel stomme redenen mislukken
- Het voelt stom voor kleine veranderingen
- Het vereist het handmatig opzetten van nieuwe builds voor elk component
- Belemmert de vindbaarheid van repository's
- Refactoring wanneer niet de hele bron beschikbaar is in één repository
- In sommige configuraties (zoals de onze) is voor het updaten van API's een mijlpaalrelease van het product vereist, en vervolgens de plug-in, en dan weer het product. We hebben waarschijnlijk een paar dingen gemist, maar je snapt het wel. Een perfecte oplossing voor het probleem is verre van beschikbaar.
Tweede keuze: git-submodule gebruiken
Als je geen afhankelijkhedentool kunt of wilt gebruiken, heeft git
de mogelijkheid om submodules
te verwerken. Submodules kunnen handig zijn, vooral voor dynamische talen. Maar ze zullen je niet noodzakelijkerwijs behoeden tegen langzame buildtijden. Ik heb er al enkele richtlijnen en tips over geschreven en ik heb ook alternatieven onderzocht. Het internet in het algemeen heeft er ook argumenten tegen.
1:1 match tussen svn:external en git
MAAR! Als je op zoek bent naar een 1-op-1 match tussen svn:externals
en git
, dan wil je submodules
gebruiken om ervoor te zorgen dat de submodules
alleen releasebranches bijhouden en geen willekeurige commits.
Derde keuze: gebruik andere tools voor builds en cross-stack afhankelijkheden
Je zult niet altijd plezier beleven aan een project dat volledig uniform is en dat met één tool gebouwd en samengesteld kan worden. Sommige mobiele projecten zullen bijvoorbeeld moeten schakelen tussen Java- en C++-afhankelijkheden, of eigendomsrechtelijke tools moeten gebruiken om assets te genereren. Voor complexere situaties kun je git
uitbreiden met een extra laag er bovenop. Een goed voorbeeld op dit gebied is de repo van Android.
Andere buildtools die het ontdekken waard zijn:
Conclusie en verder lezen
Suggesties voor verder lezen over het onderwerp bouwinfrastructuur (en Maven) van Charles O'Farrell:
- In quest of the ultimate build tool
- En zijn opvolger Maven is broken by design
- Recursive Maven considered harmful
Ik wil afsluiten met dit uitstekende citaat uit het laatste artikel hierboven. Ook al gaat het om Maven, het kan evengoed worden toegepast op andere build- en afhankelijkhedentools:
"Een cache doet niets anders dan dingen versnellen. Je zou een cache volledig kunnen verwijderen en het omringende systeem zou hetzelfde blijven werken, alleen langzamer. Een cache heeft ook geen bijwerkingen. Het maakt niet uit wat je in het verleden met een cache hebt gedaan, een bepaalde zoekopdracht naar de cache zal in de toekomst dezelfde waarde retourneren naar dezelfde zoekopdracht.
De Maven-ervaring is heel anders dan wat ik beschrijf! Maven-repository's worden gebruikt als caches, maar zonder de eigenschappen van caches. Als je iets vraagt in een Maven-repository, maakt het erg uit wat je in het verleden hebt gedaan. Het retourneert het meest recente ding dat je erin hebt gestopt. Het kan zelfs mislukken, als je om iets vraagt voordat je het erin stopt."
Deel dit artikel
Volgend onderwerp
Aanbevolen artikelen
Bookmark deze resources voor meer informatie over soorten DevOps-teams of voor voortdurende updates over DevOps bij Atlassian.