Naar de hoofdinhoud

Merge-holds

Een merge-hold houdt merges naar één branch tegen, met een reden, tot een bepaald tijdstip. Je gebruikt er een bij het snijden van een release-trein, bij een incident of bij een migratie die niet tegelijk met andere wijzigingen mag lopen. Release-treinen plaatsen en vrijgeven hun eigen holds (source: train), op naam van de eigenaar van de trein.

Een hold geldt voor één van deze:

  • elke repository van de organisatie;
  • een lijst repository’s (maximaal 50);
  • één repository.

Hij geldt altijd voor één branch: main, tenzij je een andere noemt.

Hoe een hold een merge tegenhoudt

fremforge zet een commitstatus, fremforge/hold, op de head-commit van elke open pull request naar een branch met een hold:

StatusBetekenis
pendingEr geldt een hold voor deze pull request. De beschrijving noemt de eigenaar, de vervaltijd en de reden.
successEr geldt geen hold voor, of een beheerder heeft deze pull request doorgelaten (zie Uitzondering door een beheerder).

Op een branch waar fremforge/hold een verplichte check is, houdt een hold met status pending elke manier van mergen tegen:

  • De mergeknop en het merge-endpoint van de API weigeren met “Not all required status checks successful”.
  • Auto-merge wacht. Wordt de hold vrijgegeven, dan wordt de status success en merget elke pull request die voor auto-merge klaarstond. Een pull request die tijdens een bevriezing klaar wordt gezet, landt daarna.
  • De merge queue houdt de pull request buiten een landingsgroep. Duurt de hold langer dan de check-time-out van de queue, dan zet de queue de pull request apart en plaatst hem één keer terug, zodra zijn checks weer groen zijn.

Push je naar een pull request waarop een hold geldt, of open je een nieuwe, dan krijgt die binnen enkele seconden de status. Elke 5 minuten herstelt een reconcile-job elke status die ontbreekt, verouderd is of door iemand anders is gezet.

Holds falen gesloten. Kan fremforge de status niet zetten, dan wacht een nieuwe pull request naar een branch met afgedwongen holds tot dat wel lukt.

In de console

Eigenaren van een organisatie beheren holds onder Bouwen en uitrollen → Merge-holds (https://frem.sh/<org>/_admin/merge-holds): de actieve holds met hun eigenaar, vervaltijd en waar de mergeknop wacht, knoppen om een hold vrij te geven, te verlengen of één pull request door te laten, een formulier om een hold te plaatsen, de schakelaars voor handhaving en de holds die onlangs zijn beëindigd. Alles op de pagina zit ook in de API hieronder.

Handhaving aanzetten

fremforge/hold verplicht maken is opt-in, per repository of voor de hele organisatie, per branch. Tot je dat doet, worden holds wel getoond en door tooling gelezen, maar wacht de mergeknop er niet op.

curl -X PUT https://frem.sh/_app/api/v1/orgs/<org>/merge-holds/enforcement \
  -H "Authorization: Bearer $FREMFORGE_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"repo": "*", "branch": "main"}'

repo is de naam van een repository, of * voor elke repository, ook repository’s die later worden aangemaakt. Het token heeft policy:write nodig.

Zet je het aan, dan zet fremforge eerst fremforge/hold op elke open pull request naar de branch. Pas daarna voegt het de check toe aan de beveiligingsregel van de branch. Geen enkele pull request wacht dus ooit op een check die door niets is gezet.

Het antwoord bevat:

  • required_on: repository’s waar de check nu verplicht is.
  • unprotected: repository’s waarvan de branch geen beveiligingsregel heeft. fremforge maakt er geen aan; voeg zelf een regel toe en de reconcile-job maakt de check binnen 5 minuten verplicht.
  • failed: repository’s waarvan de pull requests niet konden worden gelezen. Hun regel blijft ongemoeid en de job probeert het opnieuw.

Om het weer uit te zetten, stuur je dezelfde body met DELETE. fremforge verwijdert de check overal waar geen andere opt-in de repository nog dekt.

Een hold plaatsen

curl -X POST https://frem.sh/_app/api/v1/orgs/<org>/merge-holds \
  -H "Authorization: Bearer $FREMFORGE_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
        "repos": ["platform", "infrastructure-azure"],
        "reason": "Train 42 cut: no merges until the tst probe passes",
        "duration_minutes": 120
      }'
VeldVerplichtOpmerkingen
reasonjaEén regel, maximaal 500 tekens. Staat in de status op elke pull request met een hold.
expires_at of duration_minutesprecies éénMinstens 5 minuten en hoogstens 7 dagen vooruit. expires_at is ISO 8601 met tijdzone.
reposneeLaat weg om elke repository tegen te houden.
branchneeEén branchnaam, geen wildcards. Standaard main.
owneralleen voor een token dat voor geen persoon handeltEen lid van de organisatie.

Het token heeft deployments:write nodig, de scope waarmee je ook deployments pauzeert. Holds lezen vereist deployments:read.

Eigenaar

Elke hold hoort bij een persoon. Plaats jij een hold, of een agent die namens jou handelt, dan ben jij de eigenaar. Een token dat voor geen persoon handelt, zoals een CI-token, moet owner opgeven.

Vervaltijd en de melding

Geen enkele hold blokkeert voor altijd. Bereikt een hold zijn vervaltijd, dan geeft fremforge hem binnen 5 minuten vrij, legt merge_hold.expired vast in het auditlog en mailt de eigenaar. Moeten merges tegengehouden blijven, verleng de hold dan voor die tijd:

curl -X PATCH https://frem.sh/_app/api/v1/orgs/<org>/merge-holds/<id> \
  -H "Authorization: Bearer $FREMFORGE_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"duration_minutes": 60}'

Holds bekijken en vrijgeven

# Live holds, and where enforcement is on
curl https://frem.sh/_app/api/v1/orgs/<org>/merge-holds \
  -H "Authorization: Bearer $FREMFORGE_TOKEN"

# Release one
curl -X POST https://frem.sh/_app/api/v1/orgs/<org>/merge-holds/<id>/release \
  -H "Authorization: Bearer $FREMFORGE_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"note": "Train 42 is out"}'

Voeg ?state=all toe aan de lijst om ook vrijgegeven holds te zien. Pull requests met een hold worden binnen enkele seconden na het vrijgeven success.

Het plaatsen en vrijgeven van een hold verschijnt ook in de eventfeed als hold.placed en hold.released, één event per repository waarvoor de hold geldt.

AI-assistenten die via de MCP-server van fremforge zijn gekoppeld, kunnen holds opvragen met de tool list_merge_holds.

Uitzondering door een beheerder

Er zijn twee uitzonderingen, en beide worden vastgelegd in het auditlog:

  • De hold van iemand anders vrijgeven. Elke eigenaar van de organisatie kan elke hold vrijgeven. Geeft de eigenaar van de hold hem vrij, dan legt het auditlog merge_hold.released vast. Doet iemand anders het, dan legt het merge_hold.override vast.
  • Eén pull request doorlaten. Voor een hotfix tijdens een bevriezing maak je één pull request een uitzondering. De status wordt success en de hold blijft voor al het andere gelden. Een reden is verplicht. Het auditlog legt merge_hold.exempted vast.
curl -X POST https://frem.sh/_app/api/v1/orgs/<org>/merge-holds/<id>/exemptions \
  -H "Authorization: Bearer $FREMFORGE_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"repo": "platform", "pr_number": 812, "reason": "INC-7 hotfix"}'

Een beheerder van een repository kan nog steeds met de hand mergen als de branchregel niet voor beheerders geldt. Dat is de noodroute, en Forgejo legt die vast zoals elke andere merge.

Releasebevriezingen vanuit een issue

Bevriest je team een repository door een issue met het label release-freeze te openen, dan blijft dat werken. In een repository waar handhaving aanstaat, maakt fremforge van elk open release-freeze-issue een hold op die repository:

  • de auteur van het issue is de eigenaar;
  • de reden is de titel van het issue;
  • het issue sluiten of het label verwijderen geeft de hold binnen enkele seconden vrij.

Zo respecteert de mergeknop de bevriezing nu ook. Een bevriezing heeft geen eigen eindtijd, dus de hold loopt telkens 24 uur: zolang het issue open is, verlengt fremforge de hold als die verloopt en mailt de eigenaar dat de bevriezing nog steeds geldt.

Wat een hold niet doet

  • Hij houdt geen directe pushes naar de branch tegen. Gebruik branchbeveiliging om pull requests verplicht te maken.
  • Hij houdt geen deployments tegen. Pauzeer daarvoor een deployomgeving.
  • Iedereen met schrijftoegang tot een repository kan een commitstatus onder elke naam zetten. Zet iemand anders fremforge/hold, dan zet fremforge de status terug en legt merge_hold.status_foreign vast in het auditlog. Dat dicht het gat binnen seconden tot minuten, niet direct.

Auditgebeurtenissen

GebeurtenisWanneer
merge_hold.createdEr is een hold geplaatst, ook een hold die een release-freeze-issue weerspiegelt.
merge_hold.updatedDe eigenaar heeft de hold verlengd of de reden gewijzigd.
merge_hold.releasedDe eigenaar heeft de hold vrijgegeven, of het bijbehorende release-freeze-issue is gesloten.
merge_hold.overrideIemand anders dan de eigenaar heeft de hold vrijgegeven of gewijzigd.
merge_hold.exemptedEén pull request is doorgelaten.
merge_hold.expiredDe hold bereikte zijn vervaltijd en is vrijgegeven.
merge_hold.renewedEen release-freeze-hold verliep terwijl het issue nog open was.
merge_hold.enforcement_enabled / merge_hold.enforcement_disabledHandhaving is aan- of uitgezet.
merge_hold.status_foreignIemand anders heeft fremforge/hold gezet, en fremforge heeft de status teruggezet.