Template pipeline
Nadat we hadden besloten om een automatische rebase
uit te voeren, gingen we aan de slag met het schrijven van een script. Omdat deze rebase
in meerdere Azure DevOps repository's uitgevoerd moet worden, gaan we dit script in een template yml file plaatsen. Deze template moet een checkout stap bevatten en een stap waarin een aantal git commando's uitgevoerd gaan worden.
Om het script toegang te geven tot de OAuth token, hebben we in de checkout stap de property persistCredentials
op true gezet. Met de git commando's in het script zullen we de twee betreffende branches ophalen, een rebase
uitvoeren en de changes weer pushen. Bij een rebase
is het nodig om de push
te forceren. We hebben ervoor gekozen om --force-with-lease
te gebruiken. Dit is veiliger dan --force
en is mogelijk omdat we wisten dat de geschiedenis van de development
branch gelijk is aan wat we hebben gerebased.
Problemen en uitdagingen
Bij het ontwikkelen en testen van ons script liepen we tegen verschillende problemen en uitdagingen aan.
Rechten pipeline gebruiker
Allereerst bleek dat de gebruiker die door Azure wordt gebruikt om de pipeline uit te voeren (de Project Collection Build Service
gebruiker), onvoldoende rechten had om de git commando's uit te voeren. Voor ons script waren de volgende rechten nodig:
- Contribute
- Bypass policies when pushing
- Force push (rewrite history, delete branches and tags)
Deze rechten kun je op verschillende niveaus zetten (op project-, repository- en branchniveau). Ga op het gewenste niveau naar ‘Project settings’ > 'Security' en zet de benodigde rechten op 'Allow' voor de Project Collection Build Service
gebruiker.
Lokaal vs. Azure
Let op, er kan verschil zitten in hoe een rebase
lokaal en in een Azure pipeline wordt uitgevoerd. Het kan zijn dat een rebase
lokaal probleemloos verloopt, terwijl deze op Azure bijvoorbeeld Auto-merging
errors geeft. Waarom een rebase
in een pipeline faalt, is vaak niet meteen duidelijk. In ons geval was het probleem dat op Azure standaard niet een full fetch gedaan wordt waardoor alleen de recente geschiedenis gebruikt werd. Dit leidt bij een rebase
al snel tot problemen. De oplossing is gelukkig simpel: voeg fetchDepth: 0
toe aan de checkout stap.
Reset branches
Om de rebase
correct uit te voeren, moeten we van beide branches de laatste versie ophalen, dus ook van de master
branch. We halen de laatste versie op met een git reset --hard
om er zeker van te zijn dat we daar geen merge problemen hebben.
Foutafhandeling
Een rebase
kan altijd mislukken, bijvoorbeeld door problemen zoals hierboven beschreven, maar er kan ook een merge conflict optreden. Zorg er in zulke gevallen voor dat de rebase
netjes afgerond wordt, anders kan dit problemen veroorzaken bij de volgende run. Zo kan de volgende run een error geven dat het lijkt alsof er een rebase gaande is (omdat de map .git/rebase-merge
nog bestaat). In dit geval kiezen we ervoor om een git rebase --abort
uit te voeren zodat de rebase
netjes wordt teruggedraaid en alles wordt opgeruimd. Dit doen we in een aparte stap en alleen als de rebase
gefaald is. De git push
die nodig is om daarna naar de development
branch te pushen doen we daardoor ook in een aparte stap. We wilden graag dat de pipeline faalt als het rebasen of pushen mislukt. Daarom hebben we || exit 1
toegevoegd aan de rebase
en push
commando's.