Von einem einzigen Repository zu Multi-Repos, zu Monorepo, zu Multi-Monorepo

Avatar of Leonardo Losoviz
Leonardo Losoviz am

DigitalOcean bietet Cloud-Produkte für jede Phase Ihrer Reise. Starten Sie mit 200 $ kostenlosem Guthaben!

Ich arbeite seit mehreren Jahren am selben Projekt. Seine ursprüngliche Version war eine riesige monolithische Anwendung mit Tausenden von Dateien. Sie war schlecht architektonisch und nicht wiederverwendbar, aber sie wurde in einem einzigen Repository gehostet, was die Arbeit damit erleichterte. Später habe ich das Chaos im Projekt "behoben", indem ich die Codebasis in autonome Pakete aufgeteilt, jedes davon in einem eigenen Repository gehostet und sie mit Composer verwaltet habe. Die Codebasis wurde ordnungsgemäß architektonisch und wiederverwendbar, aber die Aufteilung auf mehrere Repositories machte die Arbeit damit wesentlich schwieriger.

Da der Code immer wieder neu formatiert wurde, musste sich auch sein Hosting im Repository anpassen, von dem anfänglichen einzelnen Repository über mehrere Repositories zu einem Monorepo bis hin zu dem, was man als "Multi-Monorepo" bezeichnen könnte.

Ich nehme Sie mit auf die Reise, wie dies geschah, und erkläre, warum und wann ich das Gefühl hatte, zu einem neuen Ansatz wechseln zu müssen. Die Reise besteht aus vier (bisherigen!) Etappen, also lassen Sie sie uns so aufteilen.

Etappe 1: Einzelnes Repository

Das Projekt ist leoloso/PoP und hat mehrere Hosting-Schemata durchlaufen, entsprechend der Neugestaltung seines Codes zu verschiedenen Zeiten.

Es wurde als diese WordPress-Seite geboren und umfasste ein Theme und mehrere Plugins. Der gesamte Code wurde im selben Repository zusammen gehostet.

Einige Zeit später benötigte ich eine weitere Website mit ähnlichen Funktionen, also ging ich den schnellen und einfachen Weg: Ich duplizierte das Theme und fügte eigene benutzerdefinierte Plugins hinzu, alles im selben Repository. Ich bekam die neue Website im Handumdrehen zum Laufen.

Das Gleiche tat ich für eine weitere Website, dann für eine weitere, und noch eine. Schließlich beherbergte das Repository etwa 10 Websites mit Tausenden von Dateien.

Ein einziges Repository, das unseren gesamten Code hostet.

Probleme mit dem einzelnen Repository

Während dieses Setup die einfache Erstellung neuer Websites ermöglichte, skalierte es überhaupt nicht gut. Das Wichtigste ist, dass eine einzelne Änderung das Suchen nach demselben String über alle 10 Websites hinweg beinhaltete. Das war völlig unüberschaubar. Sagen wir einfach, Kopieren/Einfügen/Suchen/Ersetzen wurde für mich zur Routine.

Es war also an der Zeit, mit PHP den richtigen Weg zu gehen.

Etappe 2: Multi-Repository

Schneller Vorlauf zwei Jahre. Ich habe die Anwendung vollständig in PHP-Pakete aufgeteilt, die über Composer und Dependency Injection verwaltet werden.

Composer verwendet Packagist als sein Haupt-PHP-Paket-Repository. Um ein Paket zu veröffentlichen, benötigt Packagist eine composer.json-Datei am Stammverzeichnis des Repositorys des Pakets. Das bedeutet, dass wir nicht mehrere PHP-Pakete haben können, von denen jedes seine eigene composer.json im selben Repository hostet.

Folglich musste ich vom Hosten des gesamten Codes im einzelnen leoloso/PoP-Repository auf die Verwendung mehrerer Repositories umsteigen, mit einem Repository pro PHP-Paket. Um sie zu verwalten, erstellte ich die Organisation "PoP" auf GitHub und hostete dort alle Repositories, einschließlich getpop/root, getpop/component-model, getpop/engine und viele andere.

Im Multi-Repository wird jedes Paket in seinem eigenen Repository gehostet.

Probleme mit dem Multi-Repository

Die Verwaltung eines Multi-Repositorys kann einfach sein, wenn Sie eine Handvoll PHP-Pakete haben. Aber in meinem Fall umfasste die Codebasis über 200 PHP-Pakete. Ihre Verwaltung war kein Vergnügen.

Der Grund dafür, dass das Projekt in so viele Pakete aufgeteilt wurde, ist, dass ich den Code auch von WordPress entkoppelt habe (damit diese auch mit anderen CMSs verwendet werden könnten), weshalb jedes Paket sehr granular sein muss und sich mit einem einzigen Ziel befasst.

Nun, 200 Pakete sind nicht gewöhnlich. Aber selbst wenn ein Projekt nur 10 Pakete umfasst, kann es schwierig sein, diese über 10 Repositories hinweg zu verwalten. Das liegt daran, dass jedes Paket versioniert werden muss und jede Version eines Pakets von einer bestimmten Version eines anderen Pakets abhängt. Beim Erstellen von Pull-Requests müssen wir die composer.json-Datei jedes Pakets so konfigurieren, dass sie den entsprechenden Entwicklungszweig seiner Abhängigkeiten verwendet. Das ist umständlich und bürokratisch.

Ich habe es schließlich vermieden, Feature-Branches überhaupt zu verwenden, zumindest in meinem Fall, und habe einfach jedes Paket auf die dev-master-Version seiner Abhängigkeiten verweisen lassen (d.h. ich habe Pakete nicht versioniert). Es würde mich nicht überraschen zu erfahren, dass dies eine gängige Praxis ist, eher häufig als nicht.

Es gibt Werkzeuge, die bei der Verwaltung mehrerer Repositories helfen, wie z. B. meta. Es erstellt ein Projekt, das aus mehreren Repositories besteht, und git commit -m "einige Nachricht" im Projekt führt einen Befehl git commit -m "einige Nachricht" in jedem Repository aus, sodass sie synchron zueinander gehalten werden können.

Meta hilft jedoch nicht bei der Verwaltung der Versionierung jeder Abhängigkeit in ihrer composer.json-Datei. Obwohl es hilft, den Schmerz zu lindern, ist es keine endgültige Lösung.

Also war es an der Zeit, alle Pakete in dasselbe Repository zu bringen.

Etappe 3: Monorepo

Das Monorepo ist ein einziges Repository, das den Code für mehrere Projekte hostet. Da es verschiedene Pakete zusammen hostet, können wir sie auch gemeinsam versionieren. Auf diese Weise können alle Pakete mit derselben Version veröffentlicht und über Abhängigkeiten verknüpft werden. Dies macht Pull-Requests sehr einfach.

Das Monorepo hostet mehrere Pakete.

Wie ich bereits erwähnt habe, können wir PHP-Pakete nicht auf Packagist veröffentlichen, wenn sie im selben Repository gehostet werden. Aber wir können diese Einschränkung überwinden, indem wir Entwicklung und Verteilung des Codes entkoppeln: Wir verwenden das Monorepo, um den Quellcode zu hosten und zu bearbeiten, und mehrere Repositories (ein Repository pro Paket), um sie zur Verteilung und zum Konsum auf Packagist zu veröffentlichen.

Das Monorepo hostet den Quellcode, mehrere Repositories verteilen ihn.

Umstieg auf das Monorepo

Der Wechsel zum Monorepo-Ansatz umfasste die folgenden Schritte

Zuerst habe ich die Ordnerstruktur in leoloso/PoP erstellt, um die verschiedenen Projekte zu beherbergen. Ich entschied mich für eine zweistufige Hierarchie, zuerst unter layers/, um das breitere Projekt anzudeuten, und dann unter packages/, plugins/, clients/ und anderes, um die Kategorie anzudeuten.

Showing the HitHub repo for a project called PoP. The screen in is dark mode, so the background is near black and the text is off-white, except for blue links.
Die Monorepo-Schichten deuten das breitere Projekt an.

Dann kopierte ich den gesamten Quellcode aus allen Repositories (getpop/engine, getpop/component-model, etc.) an den entsprechenden Ort für dieses Paket im Monorepo (d.h. layers/Engine/packages/engine, layers/Engine/packages/component-model, etc).

Ich brauchte die Git-Historie der Pakete nicht zu behalten, also kopierte ich die Dateien einfach mit Finder. Andernfalls können wir hraban/tomono oder shopsys/monorepo-tools verwenden, um Repos in das Monorepo zu portieren und dabei ihre Git-Historie und Commit-Hashes zu erhalten.

Als Nächstes aktualisierte ich die Beschreibung aller nachgelagerten Repositories, beginnend mit [READ ONLY], wie z. B. dieses hier.

Showing the GitHub repo for the component-model project. The screen is in dark mode, so the background is near black and the text is off-white, except for blue links. There is a sidebar to the right of the screen that is next to the list of files in the repo. The sidebar has an About heading with a description that reads: Read only, component model for Pop, over which the component-based architecture is based." This is highlighted in red.
Der "READ ONLY"-Hinweis im Beschreibungsfeld des nachgelagerten Repositories.

Ich habe diese Aufgabe in Stapeln über die GraphQL-API von GitHub ausgeführt. Zuerst habe ich alle Beschreibungen aus allen Repositories mit dieser Abfrage abgerufen

{
  repositoryOwner(login: "getpop") {
    repositories(first: 100) {
      nodes {
        id
        name
        description
      }
    }
  }
}

...was eine Liste wie diese zurückgab

{
  "data": {
    "repositoryOwner": {
      "repositories": {
        "nodes": [
          {
            "id": "MDEwOlJlcG9zaXRvcnkxODQ2OTYyODc=",
            "name": "hooks",
            "description": "Contracts to implement hooks (filters and actions) for PoP"
          },
          {
            "id": "MDEwOlJlcG9zaXRvcnkxODU1NTQ4MDE=",
            "name": "root",
            "description": "Declaration of dependencies shared by all PoP components"
          },
          {
            "id": "MDEwOlJlcG9zaXRvcnkxODYyMjczNTk=",
            "name": "engine",
            "description": "Engine for PoP"
          }
        ]
      }
    }
  }
}

Von dort kopierte ich alle Beschreibungen, fügte [READ ONLY] hinzu und generierte für jedes Repository eine neue Abfrage, die die GraphQL-Mutation updateRepository ausführt

mutation {
  updateRepository(
    input: {
      repositoryId: "MDEwOlJlcG9zaXRvcnkxODYyMjczNTk="
      description: "[READ ONLY] Engine for PoP"
    }
  ) {
    repository {
      description
    }
  }
}

Schließlich führte ich Werkzeuge ein, die beim "Aufteilen des Monorepos" helfen. Die Verwendung eines Monorepos beruht auf der Synchronisierung des Codes zwischen dem vorgelagerten Monorepo und den nachgelagerten Repositories, ausgelöst jedes Mal, wenn ein Pull-Request zusammengeführt wird. Diese Aktion wird als "Aufteilen des Monorepos" bezeichnet. Das Aufteilen des Monorepos kann mit einem git subtree split-Befehl erreicht werden, aber da ich faul bin, verwende ich lieber ein Werkzeug.

Ich wählte Monorepo builder, der in PHP geschrieben ist. Dieses Werkzeug gefällt mir, weil ich es mit eigener Funktionalität anpassen kann. Andere beliebte Werkzeuge sind der Git Subtree Splitter (in Go geschrieben) und Git Subsplit (Bash-Skript).

Was mir am Monorepo gefällt

Mit dem Monorepo fühle ich mich wohl. Die Entwicklungsgeschwindigkeit hat sich verbessert, da die Arbeit mit 200 Paketen sich ziemlich wie die Arbeit mit nur einem anfühlt. Der Schub ist am deutlichsten beim Refactoring der Codebasis, d. h. bei der Durchführung von Aktualisierungen über viele Pakete hinweg.

Das Monorepo ermöglicht es mir auch, mehrere WordPress-Plugins gleichzeitig zu veröffentlichen. Alles, was ich tun muss, ist, GitHub Actions über PHP-Code (bei Verwendung des Monorepo-Builders) eine Konfiguration bereitzustellen, anstatt sie fest in YAML zu codieren.

Um ein WordPress-Plugin zur Verteilung zu generieren, hatte ich einen generate_plugins.yml Workflow erstellt, der beim Erstellen einer Veröffentlichung ausgelöst wird. Mit dem Monorepo habe ich ihn angepasst, um nicht nur ein, sondern mehrere Plugins zu generieren, die über PHP über einen benutzerdefinierten Befehl in plugin-config-entries-json konfiguriert und wie hier in GitHub Actions aufgerufen werden.

- id: output_data
  run: |
    echo "quot;::set-output name=plugin_config_entries::$(vendor/bin/monorepo-builder plugin-config-entries-json)"

Auf diese Weise kann ich mein GraphQL API-Plugin und andere im Monorepo gehostete Plugins auf einmal generieren. Die über PHP definierte Konfiguration ist diese hier.

class PluginDataSource
{
  public function getPluginConfigEntries(): array
  {
    return [
      // GraphQL API for WordPress
      [
        'path' => 'layers/GraphQLAPIForWP/plugins/graphql-api-for-wp',
        'zip_file' => 'graphql-api.zip',
        'main_file' => 'graphql-api.php',
        'dist_repo_organization' => 'GraphQLAPI',
        'dist_repo_name' => 'graphql-api-for-wp-dist',
      ],
      // GraphQL API - Extension Demo
      [
        'path' => 'layers/GraphQLAPIForWP/plugins/extension-demo',
        'zip_file' => 'graphql-api-extension-demo.zip',
        'main_file' =>; 'graphql-api-extension-demo.php',
        'dist_repo_organization' => 'GraphQLAPI',
        'dist_repo_name' => 'extension-demo-dist',
      ],
    ];
  }
}

Beim Erstellen einer Veröffentlichung werden die Plugins über GitHub Actions generiert.

Dark mode screen in GitHub showing the actions for the project.
Diese Abbildung zeigt Plugins, die generiert werden, wenn eine Veröffentlichung erstellt wird.

Wenn ich in Zukunft den Code für ein weiteres Plugin zum Repository hinzufüge, wird es auch ohne Probleme generiert. Zeit und Energie in dieses Setup zu investieren, spart zukünftig definitiv viel Zeit und Energie.

Probleme mit dem Monorepo

Ich glaube, dass das Monorepo besonders nützlich ist, wenn alle Pakete in derselben Programmiersprache kodiert sind, eng gekoppelt sind und dasselbe Tooling verwenden. Wenn wir stattdessen mehrere Projekte haben, die auf verschiedenen Programmiersprachen basieren (wie JavaScript und PHP), aus nicht zusammenhängenden Teilen bestehen (wie der Code der Hauptwebsite und eine Subdomain, die das Abonnieren von Newslettern verwaltet) oder unterschiedliches Tooling verwenden (wie PHPUnit und Jest), dann glaube ich nicht, dass das Monorepo einen großen Vorteil bietet.

Dennoch gibt es Nachteile beim Monorepo

  • Wir müssen für den gesamten im Monorepo gehosteten Code dieselbe Lizenz verwenden. Andernfalls können wir keine LICENSE.md-Datei am Stammverzeichnis des Monorepos hinzufügen, damit GitHub sie automatisch erkennt. Tatsächlich stellte leoloso/PoP anfangs mehrere Bibliotheken unter MIT und das Plugin unter GPLv2 zur Verfügung. Also entschied ich mich, es unter Verwendung des kleinsten gemeinsamen Nenners zwischen ihnen zu vereinfachen, was GPLv2 ist.
  • Es gibt viel Code, viel Dokumentation und viele Issues, alles von verschiedenen Projekten. Daher können potenzielle Mitwirkende, die von einem bestimmten Projekt angezogen wurden, leicht verwirrt werden.
  • Beim Taggen des Codes werden alle Pakete unabhängig mit diesem Tag versioniert, unabhängig davon, ob ihr spezifischer Code aktualisiert wurde oder nicht. Dies ist ein Problem mit dem Monorepo-Builder und nicht unbedingt mit dem Monorepo-Ansatz (Symfony hat dieses Problem gelöst für sein Monorepo).
  • Das Issues-Board benötigt ordnungsgemäße Verwaltung. Insbesondere erfordert es Labels, um Issues dem entsprechenden Projekt zuzuweisen, oder es droht, chaotisch zu werden.
Showing the list of reported issues for the project in GitHub in dark mode. The image shows just how crowded and messy the screen looks when there are a bunch of issues from different projects in the same list without a way to differentiate them.
Das Issues-Board kann ohne Labels, die Projekten zugeordnet sind, chaotisch werden.

All diese Probleme sind jedoch keine Hindernisse. Ich kann damit umgehen. Es gibt jedoch ein Problem, das das Monorepo nicht lösen kann: das gemeinsame Hosten von öffentlichem und privatem Code.

Ich plane, eine "PRO"-Version meines Plugins zu erstellen, die ich in einem privaten Repository hosten möchte. Der Code im Repository ist jedoch entweder öffentlich oder privat, sodass ich meinen privaten Code nicht im öffentlichen leoloso/PoP-Repository hosten kann. Gleichzeitig möchte ich mein Setup auch für das private Repository beibehalten, insbesondere den generate_plugins.yml-Workflow (der das Plugin bereits scoptet und seinen Code von PHP 8.0 auf 7.1 herabstuft) und seine Möglichkeit, ihn über PHP zu konfigurieren. Und ich möchte es DRY halten, indem ich Copy/Paste vermeide.

Es war an der Zeit, zum Multi-Monorepo zu wechseln.

Etappe 4: Multi-Monorepo

Der Multi-Monorepo-Ansatz besteht darin, dass verschiedene Monorepos ihre Dateien miteinander teilen, verknüpft über Git-Submodule. Ganz grundlegend besteht ein Multi-Monorepo aus zwei Monorepos: einem autonomen vorgelagerten Monorepo und einem nachgelagerten Monorepo, das das vorgelagerte Repo als Git-Submodul einbettet, das in der Lage ist, auf seine Dateien zuzugreifen.

A giant red folder illustration is labeled as the downstream monorepo and it contains a smaller green folder showing the upstream monorepo.
Das vorgelagerte Monorepo ist im nachgelagerten Monorepo enthalten.

Dieser Ansatz erfüllt meine Anforderungen durch

  • das öffentliche Repository leoloso/PoP als vorgelagertes Monorepo und
  • die Erstellung eines privaten Repositories leoloso/GraphQLAPI-PRO, das als nachgelagertes Monorepo dient.
The same illustration as before, but now the large folder is a bright pink and is labeled as with the project name, and the smaller folder is a purplish-blue and labeled with the name of the public downstream module,.
Ein privates Monorepo kann auf die Dateien eines öffentlichen Monorepos zugreifen.

leoloso/GraphQLAPI-PRO bettet leoloso/PoP im Unterordner submodules/PoP ein (beachten Sie, wie GitHub auf den spezifischen Commit des eingebetteten Repos verlinkt)

Diese Abbildung zeigt, wie das öffentliche Monorepo im privaten Monorepo im GitHub-Projekt eingebettet ist.

Nun kann leoloso/GraphQLAPI-PRO auf alle Dateien von leoloso/PoP zugreifen. Zum Beispiel kann das Skript ci/downgrade/downgrade_code.sh aus leoloso/PoP (das den Code von PHP 8.0 auf 7.1 herabstuft) unter submodules/PoP/ci/downgrade/downgrade_code.sh aufgerufen werden.

Zusätzlich kann das nachgelagerte Repository den PHP-Code aus dem vorgelagerten Repository laden und sogar erweitern. Auf diese Weise kann die Konfiguration zur Generierung der öffentlichen WordPress-Plugins überschrieben werden, um stattdessen die PRO-Plugin-Versionen zu erstellen.

class PluginDataSource extends UpstreamPluginDataSource
{
  public function getPluginConfigEntries(): array
  {
    return [
      // GraphQL API PRO
      [
        'path' => 'layers/GraphQLAPIForWP/plugins/graphql-api-pro',
        'zip_file' => 'graphql-api-pro.zip',
        'main_file' => 'graphql-api-pro.php',
        'dist_repo_organization' => 'GraphQLAPI-PRO',
        'dist_repo_name' => 'graphql-api-pro-dist',
      ],
      // GraphQL API Extensions
      // Google Translate
      [
        'path' => 'layers/GraphQLAPIForWP/plugins/google-translate',
        'zip_file' => 'graphql-api-google-translate.zip',
        'main_file' => 'graphql-api-google-translate.php',
        'dist_repo_organization' => 'GraphQLAPI-PRO',
        'dist_repo_name' => 'graphql-api-google-translate-dist',
      ],
      // Events Manager
      [
        'path' => 'layers/GraphQLAPIForWP/plugins/events-manager',
        'zip_file' => 'graphql-api-events-manager.zip',
        'main_file' => 'graphql-api-events-manager.php',
        'dist_repo_organization' => 'GraphQLAPI-PRO',
        'dist_repo_name' => 'graphql-api-events-manager-dist',
      ],
    ];
  }
}

GitHub Actions lädt Workflows nur aus .github/workflows, und die vorgelagerten Workflows befinden sich unter submodules/PoP/.github/workflows; daher müssen wir sie kopieren. Das ist nicht ideal, obwohl wir vermeiden können, die kopierten Workflows zu bearbeiten und die vorgelagerten Dateien als einzige Wahrheitsquelle zu behandeln.

Zum Kopieren der Workflows kann ein einfacher Composer-Skript verwendet werden.

{
  "scripts": {
    "copy-workflows": [
      "php -r \"copy('submodules/PoP/.github/workflows/generate_plugins.yml', '.github/workflows/generate_plugins.yml');\"",
      "php -r \"copy('submodules/PoP/.github/workflows/split_monorepo.yaml', '.github/workflows/split_monorepo.yaml');\""
    ]
  }
}

Dann, jedes Mal, wenn ich die Workflows im vorgelagerten Monorepo bearbeite, kopiere ich sie auch in das nachgelagerte Monorepo, indem ich den folgenden Befehl ausführe.

composer copy-workflows

Sobald dieses Setup vorhanden ist, generiert das private Repository seine eigenen Plugins, indem es den Workflow aus dem öffentlichen Repository wiederverwendet.

Diese Abbildung zeigt die PRO-Plugins, die in GitHub Actions generiert werden.

Ich bin mit diesem Ansatz äußerst zufrieden. Ich habe das Gefühl, dass er mir die gesamte Last der Projektverwaltung abgenommen hat. Ich habe von einem WordPress-Plugin-Autor gelesen, der sich beschwerte, dass die Verwaltung der Veröffentlichungen seiner 10+ Plugins eine beträchtliche Zeit in Anspruch nahm. Das passiert hier nicht – nachdem ich meinen Pull-Request zusammengeführt habe, werden sowohl öffentliche als auch private Plugins automatisch generiert, wie Magie.

Probleme mit dem Multi-Monorepo

Zunächst einmal, es leckt. Idealerweise sollte leoloso/PoP vollständig autonom sein und nicht wissen, dass es als vorgelagertes Monorepo in einem größeren Schema verwendet wird – aber das ist nicht der Fall.

Beim Ausführen von git checkout muss das nachgelagerte Monorepo die Option --recurse-submodules übergeben, um auch die Submodule auszuchecken. In den GitHub Actions-Workflows für das private Repo muss der Checkout so erfolgen

- uses: actions/checkout@v2
  with:
    submodules: recursive

Daher müssen wir submodules: recursive zum nachgelagerten Workflow hinzufügen, aber nicht zum vorgelagerten, obwohl beide dieselbe Quelldatei verwenden.

Um dies zu lösen und das öffentliche Monorepo als einzige Wahrheitsquelle beizubehalten, werden die Workflows in leoloso/PoP der Wert für submodules über eine Umgebungsvariable CHECKOUT_SUBMODULES injiziert, wie hier.

env:
  CHECKOUT_SUBMODULES: "";

jobs:
  provide_data:
    steps:
      - uses: actions/checkout@v2
        with:
          submodules: ${{ env.CHECKOUT_SUBMODULES }}

Der Umgebungswert ist für das vorgelagerte Monorepo leer, daher funktioniert submodules: "" gut. Und dann, beim Kopieren der Workflows vom vorgelagerten zum nachgelagerten System, ersetze ich den Wert der Umgebungsvariable durch "recursive", sodass es wird

env:
  CHECKOUT_SUBMODULES: "recursive"

(Ich habe einen PHP-Befehl, um die Ersetzung durchzuführen, aber wir könnten auch sed im copy-workflows Composer-Skript verwenden.)

Dieses Leck offenbart ein weiteres Problem mit diesem Setup: Ich muss alle Beiträge zum öffentlichen Repository überprüfen, bevor sie zusammengeführt werden, sonst könnten sie etwas im nachgelagerten System kaputt machen. Die Mitwirkenden wären sich dieser Lecks völlig unbewusst (und könnten dafür nicht beschuldigt werden). Diese Situation ist spezifisch für das öffentliche/private Monorepo-Setup, bei dem ich die einzige Person bin, die sich des vollständigen Setups bewusst ist. Während ich Zugriff auf das öffentliche Repository teile, bin ich der Einzige, der auf das private zugreift.

Als Beispiel dafür, wie die Dinge schiefgehen könnten, könnte ein Mitwirkender von leoloso/PoP CHECKOUT_SUBMODULES: "" entfernen, da es überflüssig ist. Was der Mitwirkende nicht weiß, ist, dass diese Zeile zwar nicht benötigt wird, aber das Entfernen das private Repository kaputt macht.

Ich denke, ich muss eine Warnung hinzufügen!

env:
  ### ☠️ Do not delete this line! Or bad things will happen! ☠️
  CHECKOUT_SUBMODULES: ""

Zusammenfassung

Mein Repository hat eine ziemliche Reise hinter sich und wurde im Laufe der Zeit an die neuen Anforderungen meines Codes und meiner Anwendung angepasst.

  • Es begann als ein einziges Repository, das eine monolithische App hostete.
  • Es wurde zu einem Multi-Repository, als die App in Pakete aufgeteilt wurde.
  • Es wurde auf ein Monorepo umgestellt, um alle Pakete besser verwalten zu können.
  • Es wurde zu einem Multi-Monorepo aufgerüstet, um Dateien mit einem privaten Monorepo zu teilen.

Kontext ist alles, also gibt es hier keinen "besten" Ansatz – nur Lösungen, die für verschiedene Szenarien mehr oder weniger geeignet sind.

Hat mein Repository das Ende seiner Reise erreicht? Wer weiß? Das Multi-Monorepo erfüllt meine aktuellen Anforderungen, aber es hostet alle privaten Plugins zusammen. Wenn ich jemals Auftragnehmern den Zugriff auf ein bestimmtes privates Plugin gewähren muss, während ich ihnen den Zugriff auf anderen Code verweigere, dann ist das Monorepo möglicherweise nicht mehr die ideale Lösung für mich, und ich muss erneut iterieren.

Ich hoffe, die Reise hat Ihnen gefallen. Und wenn Sie Ideen oder Beispiele aus Ihren eigenen Erfahrungen haben, würde ich mich freuen, sie in den Kommentaren zu hören.