Verwalten von Paketen mit Go-Modulen: Ein pragmatischer Leitfaden

Hallo alle zusammen. Im Vorfeld des Beginns des Golang-Entwicklerkurses haben wir eine weitere interessante Übersetzung für Sie vorbereitet.



Module sind eine Möglichkeit, mit Abhängigkeiten in Go umzugehen. Zunächst als Experiment vorgestellt, sollen die Module als neuer Standard für die Verwaltung von Paketen ab Version 1.13 ins Feld eingeführt werden.

Ich finde dieses Thema ungewöhnlich genug für Anfänger aus anderen Sprachen. Deshalb habe ich beschlossen, hier einige Gedanken und Tipps zu sammeln, um anderen wie mir zu helfen, sich ein Bild von der Paketverwaltung in Go zu machen. Wir beginnen mit einer allgemeinen Einführung und gehen dann zu den weniger offensichtlichen Aspekten über, einschließlich der Verwendung des Herstellerordners, der Verwendung von Modulen mit Docker in der Entwicklung, Werkzeugabhängigkeiten usw.

Wenn Sie bereits mit Go-Modulen vertraut sind und das Wiki kennenWie Ihr Handrücken wird dieser Artikel für Sie wahrscheinlich nicht sehr hilfreich sein. Im Übrigen können jedoch mehrere Stunden Versuch und Irrtum eingespart werden.

Wenn Sie unterwegs sind, springen Sie hinein und genießen Sie die Fahrt.



Schnellstart


Wenn die Versionskontrolle bereits in Ihr Projekt integriert ist, können Sie sie einfach ausführen

go mod init

Oder geben Sie den Pfad zum Modul manuell an. Dies ist so etwas wie ein Name, eine URL und ein Importpfad für Ihr Paket:

go mod init github.com/you/hello

Dadurch wird eine Datei erstellt go.mod, in der auch die Projektanforderungen und das Lochit in Abhängigkeit von der richtigen Version definiert werden (als Analogie für Sie ist es wie package.jsonund package-lock.jsonin einer einzigen Datei zusammengefasst):

module github.com/you/hello
go 1.12

Ausführen go get, um Ihrem Projekt eine neue Abhängigkeit hinzuzufügen:

Beachten Sie, dass Sie mit go get zwar keinen Versionsbereich angeben können, hier jedoch keine bestimmte Version, sondern eine Mindestversion definieren. Wie wir später sehen werden, gibt es eine Möglichkeit, Abhängigkeiten entsprechend dem Semver ordnungsgemäß zu aktualisieren.

# use Git tags
go get github.com/go-chi/chi@v4.0.1
# or Git branch name
go get github.com/go-chi/chi@master
# or Git commit hash
go get github.com/go-chi/chi@08c92af

Jetzt ist unsere Datei go.modwie folgt:

module github.com/you/hello
go 1.12
require github.com/go-chi/chi v4.0.2+incompatible // indirect

Das Suffix wird +incompatibleallen Paketen hinzugefügt, die noch nicht für Go-Module konfiguriert sind oder deren Versionskontrollregeln verletzen.

Da wir dieses Paket nirgendwo in unserem Projekt importiert haben, wurde es als markiert // indirect. Wir können dies mit dem folgenden Befehl aufräumen:

go mod tidy

Abhängig vom aktuellen Status Ihres Repositorys wird entweder ein nicht verwendetes Modul oder ein Kommentar gelöscht // indirect.

Wenn eine Abhängigkeit für sich genommen keine hat go.mod(z. B. ist sie noch nicht für Module konfiguriert), werden alle ihre Abhängigkeiten in die übergeordnete Datei geschrieben go.mod(optional wird Ihre Datei go.mod)zusammen mit einem Kommentar // indirectangezeigt, um anzuzeigen, dass sie nicht direkt importiert werden In Ihrem

globalen Plan besteht das Ziel go mod tidyauch darin, Abhängigkeiten hinzuzufügen, die für andere Kombinationen von Betriebssystemen, Architekturen und Build-Tags erforderlich sind. Führen Sie diese vor jeder Version aus.

Stellen Sie außerdem sicher, dass nach dem Hinzufügen der Abhängigkeit eine Datei erstellt wirdgo.sum. Sie könnten denken, dass dies eine Sperrdatei ist. Tatsächlich go.modbietet es jedoch bereits genügend Informationen für 100% reproduzierbare Builds. Die Datei go.sumwird zu Überprüfungszwecken erstellt: Sie enthält die erwarteten kryptografischen Prüfsummen des Inhalts einzelner Versionen des Moduls.

Zum Teil, weil es sich go.sumnicht um eine Sperrdatei handelt, werden die schriftlichen Prüfsummen für die Modulversion gespeichert, auch wenn Sie dieses Modul nicht mehr verwenden. Auf diese Weise können Sie die Prüfsummen überprüfen, wenn Sie sie später wieder verwenden, was zusätzliche Sicherheit bietet.


Mkcert ist gerade auf die Module migriert (mit Hersteller / aus Gründen der Abwärtskompatibilität) und alles verlief reibungslos.
Https://github.com/FiloSottile/mkcert/commit/26ac5f35395fb9cba3805faf1a5a04d260271291

$ GO111MODULE=on go1.11rc1 mod init
$ GO111MODULE=on go1.11rc1 mod vendor
$ git add go.mod go.sum vendor
$ git rm Gopkg.lock Gopkg.toml Makefile



FAQ: Soll ich mich go.sumin Git verpflichten?
A: Auf jeden Fall ja. Damit müssen die Eigentümer Ihrer Quellen anderen GitHub-Repositorys und Eigentümern von benutzerdefinierten Importpfaden nicht vertrauen. Schon auf dem Weg zu uns, etwas Besseres, aber im Moment ist dies das gleiche Modell wie die Hashes in den Sperrdateien.

Die go buildund Befehle go testwerden alle fehlenden Abhängigkeiten automatisch laden, obwohl Sie dies explizit mit Hilfe tun können go mod downloadpre-bevölkern lokalen Caches , die für CI nützlich sein kann.

Standardmäßig werden alle unsere Pakete aus allen Projekten in das Verzeichnis geladen $GOPATH/pkg/mod. Wir werden dies später genauer besprechen.

Aktualisieren von Paketversionen


Sie können go get -uentweder verwenden go get -u=patch, um Abhängigkeiten auf die neueste Nebenversion bzw. den neuesten Patch zu aktualisieren.

Sie können jedoch nicht auf solche Hauptversionen aktualisieren. Der in Go-Modulen enthaltene Code muss technisch den folgenden Regeln entsprechen:

  • Match Semver (Beispiel-Tag VCS v1.2.3).
  • Wenn das Modul Version v2 oder höher ist, sollte die Hauptversion des Moduls sowohl /vNam Ende des in der Datei verwendeten Modulpfads go.modals auch im Paketimportpfad enthalten sein:

import "github.com/you/hello/v2"

Anscheinend geschieht dies, damit verschiedene Versionen von Paketen in eine Assembly importiert werden können (siehe Problem mit der Diamantabhängigkeit ).

Kurz gesagt, Go erwartet von Ihnen, dass Sie bei der Einführung von Hauptversionen sehr vorsichtig sind.

Importierte Module ersetzen


Sie können das erforderliche Modul für Ihre eigene Verzweigung oder sogar den lokalen Pfad zur Datei mithilfe der folgenden Anweisung angeben replace:

go mod edit -replace github.com/go-chi/chi=./packages/chi

Ergebnis:

module github.com/you/hello
go 1.12
require github.com/go-chi/chi v4.0.2+incompatible
replace github.com/go-chi/chi => ./packages/chi

Sie können die Zeile manuell löschen oder ausführen:

go mod edit -dropreplace github.com/go-chi/chi

Projektabhängigkeitsmanagement


In der Vergangenheit wurde der gesamte Go-Code in einem riesigen Mono-Repository gespeichert, da Google auf diese Weise seine Codebasis organisiert. Dies wirkt sich auf das Sprachdesign aus.

Go-Module weichen von diesem Ansatz ab. Sie müssen nicht mehr alle Ihre Projekte behalten $GOPATH.

Technisch gesehen sind jedoch alle heruntergeladenen Abhängigkeiten weiterhin vorhanden $GOPATH/pkg/mod. Wenn Sie Docker-Container für die lokale Entwicklung verwenden, kann dies ein Problem sein, da Abhängigkeiten außerhalb des Projekts gespeichert werden. Standardmäßig sind sie in Ihrer IDE einfach nicht sichtbar.



Dies ist normalerweise kein Problem für andere Sprachen, aber dies ist das, was ich zum ersten Mal bei der Arbeit mit der Go-Codebasis festgestellt habe.

Glücklicherweise gibt es mehrere (undokumentierte) Möglichkeiten, um dieses Problem zu lösen.

Option 1. Installieren Sie GOPATH in Ihrem Projektverzeichnis.


Auf den ersten Blick mag dies nicht intuitiv erscheinen, aber wenn Sie Go aus einem Container ausführen, können Sie GOPATH überschreiben, sodass es auf das Projektverzeichnis verweist, sodass auf Pakete vom Host aus zugegriffen werden kann:

version: '3.7'

services:
  app:
    command: tail -f /dev/null
    image: golang:1.12.6-stretch
    environment:
      #        - /code/.go/pkg/mod
      - GOPATH=/code/.go
    ports:
      - 8000:8000
    volumes:
      - ./:/code:cached
    working_dir: /code

Beliebte IDEs sollten in der Lage sein, GOPATH auf Projektebene (Arbeitsbereich) zu installieren:



Der einzige Nachteil dieses Ansatzes ist die mangelnde Interaktion mit der Go-Laufzeit auf dem Host-Computer. Sie müssen alle Go-Befehle im Container ausführen.

Option 2: Verkauf Ihrer Abhängigkeiten


Eine andere Möglichkeit besteht darin, die Abhängigkeiten Ihres Projekts in einen Ordner zu kopieren vendor:

go mod vendor

Es sollte sofort beachtet werden: Wir erlauben Go NICHT, Materialien direkt in den Lieferantenordner hochzuladen. Dies ist mit Modulen nicht möglich. Wir kopieren einfach bereits heruntergeladene Pakete.

Wenn Sie Ihre Abhängigkeiten wie im obigen Beispiel lösen, dann löschen $GOPATH/pkg/modund dann versuchen, Ihrem Projekt mehrere neue Abhängigkeiten hinzuzufügen, wird Folgendes angezeigt :

  1. Go erstellt den Download-Cache für alle Softwarepakete neu $GOPATH/pkg/mod/cache.
  2. Alle geladenen Module werden kopiert $GOPATH/pkg/mod.
  3. Schließlich kopiert Go diese Module in einen vendorOrdner und löscht Beispiele, Tests und einige andere Dateien, von denen Sie nicht direkt abhängig sind.

Darüber hinaus fehlen in diesem neu erstellten Herstellerordner viele Dinge:



Eine typische Docker Compose-Datei sieht folgendermaßen aus (beachten Sie die Datenträgerbindungen):

version: '3.7'

services:
  app:
    command: tail -f /dev/null
    image: golang:1.12.6-stretch
    ports:
      - 8000:8000
    volumes:
     #    go,           
      - modules:/go/pkg/mod/cache
      - ./:/code:cached
    working_dir: /code 

volumes:
  modules:
    driver: local

Bitte beachten Sie, dass ich diesen Herstellerordner im Versionskontrollsystem NICHT komisch mache oder ihn nicht in der Produktion verwenden werde. Dies ist ein streng lokales Entwicklungsskript, das normalerweise in einigen anderen Sprachen verfügbar ist.

Wenn ich jedoch Kommentare von einigen Go-Betreuern und einige Angebote im Zusammenhang mit Teilverkäufen (?) Lies, habe ich den Eindruck, dass diese Funktion ursprünglich nicht für diesen Benutzerfall gedacht war.

Einer der Kommentatoren von reddit hat mir dabei geholfen, etwas Licht ins Dunkel zu bringen:

Normalerweise bieten die Benutzer ihre Abhängigkeiten aus Gründen wie dem Wunsch nach engen Assemblys ohne Zugriff auf das Netzwerk sowie einer Kopie vorgefertigter Abhängigkeiten im Falle eines Github-Fehlers oder des Verschwindens des Repositorys und der Möglichkeit einer einfacheren Prüfung von Abhängigkeitsänderungen mithilfe von Standard-VCS-Tools usw. an. .

Ja, es sieht nicht wie etwas aus der Tatsache , dass ich interessiert sein könnte.

Gemäß dem Befehl Go können Sie den Verkauf einfach aktivieren, indem Sie eine Umgebungsvariable festlegen GOFLAGS=-mod=vendor. Ich empfehle dies nicht. Die Verwendung von Flags wird einfach unterbrochen, go getohne dass Ihr täglicher Workflow weitere Vorteile bietet:



Der einzige Ort, an dem Sie den Verkauf aktivieren müssen, ist Ihre IDE:



Nach einigem Ausprobieren habe ich das folgende Verfahren zum Hinzufügen von Lieferantenabhängigkeiten in diesem Ansatz entwickelt.

Schritt 1. Anforderung


Sie können eine Abhängigkeit verlangen mit go get:

go get github.com/rs/zerolog@v1.14.3

Schritt 2. Importieren


Importieren Sie es dann irgendwo in Ihren Code:

import (
   _ "github.com/rs/zerolog"
)

Schritt 3. Verkauf


Öffnen Sie abschließend Ihre Abhängigkeiten erneut:

go mod vendor

Es steht ein Vorschlag aus , dem Go-Mod-Anbieter zu erlauben, bestimmte Modulvorlagen zu akzeptieren, die möglicherweise einige der mit diesem Workflow verbundenen Probleme lösen (oder auch nicht).

go mod vendorerfordert bereits automatisch verpasste Importe, daher ist Schritt 1 in diesem Workflow optional (wenn Sie keine Versionsbeschränkungen angeben möchten). Ohne Schritt 2 wird das heruntergeladene Paket jedoch nicht abgerufen.

Dieser Ansatz funktioniert besser mit dem Hostsystem, ist jedoch ziemlich verwirrend, wenn es darum geht, Ihre Abhängigkeiten zu bearbeiten.



Persönlich denke ich, dass die Neudefinition von GOPATH ein sauberer Ansatz ist, da die Funktionalität nicht beeinträchtigt wird go get. Trotzdem wollte ich beide Strategien zeigen, da der Herstellerordner möglicherweise Personen aus anderen Sprachen wie PHP, Ruby, Javascript usw. besser bekannt ist. Wie Sie dem in diesem Artikel beschriebenen Betrug entnehmen können, ist dies der Fall keine besonders gute Wahl für Go.

Werkzeugabhängigkeiten


Möglicherweise müssen wir einige Go-basierte Tools installieren, die nicht importiert, sondern als Teil der Projektentwicklungsumgebung verwendet werden. Ein einfaches Beispiel für ein solches Tool ist CompileDaemon , mit dem Sie Ihren Code auf Änderungen überwachen und Ihre Anwendung neu starten können.

Der offiziell empfohlene Ansatz besteht darin, eine tools.goDatei (der Name spielt keine Rolle) mit folgendem Inhalt hinzuzufügen :

// +build tools
package tools
import (
_ "github.com/githubnemo/CompileDaemon"
)

  • Die Einschränkung // +build toolsverhindert, dass Ihre regulären Baugruppen Ihr Werkzeug tatsächlich importieren.
  • Mit dem Importausdruck können go-Befehle Versionsinformationen Ihrer Tools genau in go.modIhre Moduldatei schreiben .

Das ist also alles. Ich hoffe, Sie werden nicht so verwirrt sein wie zu Beginn meiner Verwendung von Go-Modulen. Weitere Informationen finden Sie im Go Modules-Wiki .



Steig auf den Kurs.



All Articles