Gestion des packages avec les modules Go: un guide pragmatique

Bonjour à tous. En prévision du début du cours de développeur de Golang, nous avons préparé une autre traduction intéressante pour vous.



Les modules sont un moyen de gérer les dépendances dans Go. Initialement présentés comme une expérience, les modules sont censés être introduits sur le terrain en tant que nouveau standard de gestion des packages à partir de la version 1.13.

Je trouve ce sujet assez inhabituel pour les débutants venant d'autres langues, et j'ai donc décidé de rassembler ici quelques réflexions et conseils pour aider d'autres comme moi à se faire une idée de la gestion des packages dans Go. Nous commencerons par une introduction générale, puis passerons à des aspects moins évidents, notamment l'utilisation du dossier du fournisseur, l'utilisation de modules avec Docker en développement, les dépendances des outils, etc.

Si vous êtes déjà familier avec les modules Go et connaissez le Wiki, comme le dos de votre main, cet article ne vous sera probablement pas très utile. Mais pour le reste, cependant, cela peut économiser plusieurs heures d'essais et d'erreurs.

Donc, si vous êtes en route, sautez et profitez de la balade.



Démarrage rapide


Si le contrôle de version est déjà intégré dans votre projet, vous pouvez simplement exécuter

go mod init

Ou spécifiez le chemin d'accès au module manuellement. C'est quelque chose comme un nom, une URL et un chemin d'importation pour votre package:

go mod init github.com/you/hello

Cela créera un fichier go.mod, qui définit également les exigences du projet et le lochit en fonction de leur version correcte (par analogie pour vous, c'est comme package.json, et package-lock.jsoncombiné en un seul fichier):

module github.com/you/hello
go 1.12

Exécutez go getpour ajouter une nouvelle dépendance à votre projet:

notez que bien que vous ne puissiez pas spécifier une plage de versions avec go get, ce que vous définissez ici n'est pas une version spécifique, mais une version minimale. Comme nous le verrons plus tard, il existe un moyen de mettre à jour les dépendances avec élégance selon semver.

# 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

Maintenant, notre dossier est go.modle suivant:

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

Le suffixe est +incompatibleajouté à tous les packages qui ne sont pas encore configurés pour les modules Go ou violent leurs règles de contrôle de version.

Comme nous n'avons importé ce package nulle part dans notre projet, il a été marqué comme // indirect. Nous pouvons ranger cela avec la commande suivante:

go mod tidy

Selon l'état actuel de votre référentiel, il supprimera un module inutilisé ou supprimera un commentaire // indirect.

Si aucune dépendance en elle-même n'en a go.mod(par exemple, elle n'est pas encore configurée pour les modules), alors toutes ses dépendances seront écrites dans le fichier parent go.mod(en option, votre fichier go.mod)avec un commentaire // indirectpour indiquer qu'elles ne proviennent pas de l'importation directe Dans votre

plan global, l'objectif go mod tidyest également d'ajouter toutes les dépendances nécessaires pour d'autres combinaisons de systèmes d'exploitation, d'architectures et de balises de génération. Assurez-vous de l'exécuter avant chaque version.

Assurez-vous également qu'un fichier est créé après l'ajout de la dépendancego.sum. Vous pourriez penser qu'il s'agit d'un fichier de verrouillage. Mais en fait, il go.modfournit déjà suffisamment d'informations pour des versions 100% reproductibles. Le fichier go.sumest créé à des fins de vérification: il contient les sommes de contrôle cryptographiques attendues du contenu des versions individuelles du module.

En partie parce qu'il go.sumne s'agit pas d'un fichier de verrouillage, il enregistre les sommes de contrôle écrites pour la version du module même après avoir cessé d'utiliser ce module. Cela vous permet de vérifier les sommes de contrôle si vous recommencez à l'utiliser plus tard, ce qui offre une sécurité supplémentaire.


Mkcert vient de migrer vers les modules (avec le fournisseur / pour la compatibilité descendante) et tout s'est bien passé
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: Dois-je m'engager go.sumdans git?
R: Certainement oui. Avec lui, les propriétaires de vos sources n'ont pas besoin de faire confiance aux autres référentiels GitHub et aux propriétaires de chemins d'importation personnalisés. Déjà en route pour nous, quelque chose de mieux, mais pour l'instant c'est le même modèle que les hachages dans les fichiers de verrouillage.

Les commandes go buildet go testchargeront automatiquement toutes les dépendances manquantes, bien que vous puissiez le faire explicitement à l'aide de go mod downloadpréremplissage de caches locaux qui peuvent être utiles pour CI.

Par défaut, tous nos packages de tous les projets sont chargés dans le répertoire $GOPATH/pkg/mod. Nous en discuterons plus en détail ultérieurement.

Mise à niveau des versions de package


Vous pouvez utiliser go get -usoit go get -u=patchpour mettre à jour les dépendances vers la dernière version mineure ou le dernier correctif, respectivement.

Mais vous ne pouvez pas passer à des versions majeures comme celle-ci. Le code inclus dans les modules Go doit respecter techniquement les règles suivantes:

  • Match semver (exemple de balise VCS v1.2.3).
  • Si le module est la version v2 ou supérieure, la version principale du module doit être incluse /vNà la fin du chemin du module utilisé dans le fichier go.modet dans le chemin d'importation du package:

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

Apparemment, cela est fait pour que différentes versions de packages puissent être importées dans un même assemblage (voir problème de dépendance au diamant ).

En un mot, Go s'attend à ce que vous soyez très prudent lors de l'introduction de versions majeures.

Remplacement des modules importés


Vous pouvez spécifier le module nécessaire pour votre propre fork ou même le chemin local vers le fichier en utilisant la directive replace:

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

Résultat:

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

Vous pouvez supprimer la ligne manuellement ou exécuter:

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

Gestion des dépendances du projet


Historiquement, tout le code Go était stocké dans un seul référentiel géant, car c'est ainsi que Google organise sa base de code, et cela affecte la conception du langage.

Les modules Go sont un départ de cette approche. Vous n'avez plus besoin de conserver tous vos projets $GOPATH.

Cependant, techniquement, toutes vos dépendances téléchargées sont toujours placées $GOPATH/pkg/mod. Si vous utilisez des conteneurs Docker pour le développement local, cela peut être un problème, car les dépendances sont stockées en dehors du projet. Par défaut, ils ne sont tout simplement pas visibles dans votre IDE.



Ce n'est généralement pas un problème pour les autres langues, mais c'est ce que j'ai rencontré pour la première fois en travaillant avec la base de code Go.

Heureusement, il existe plusieurs façons (non documentées) de résoudre ce problème.

Option 1. Installez GOPATH dans votre répertoire de projet.


À première vue, cela peut sembler contre-intuitif, mais si vous exécutez Go à partir d'un conteneur , vous pouvez remplacer GOPATH pour qu'il pointe vers le répertoire du projet afin que les packages soient accessibles depuis l'hôte:

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

Les IDE populaires devraient pouvoir installer GOPATH au niveau du projet (espace de travail):



le seul inconvénient de cette approche est le manque d'interaction avec le runtime Go sur l'ordinateur hôte. Vous devez exécuter toutes les commandes Go à l'intérieur du conteneur.

Option 2: vente de vos dépendances


Une autre façon consiste à copier les dépendances de votre projet dans un dossier vendor:

go mod vendor

Il convient de le noter tout de suite: nous n'autorisons PAS Go à télécharger directement des documents dans le dossier du fournisseur: ce n'est pas possible avec les modules. Nous copions simplement les packages déjà téléchargés.

De plus, si vous détachez vos dépendances, comme dans l'exemple ci-dessus, désactivez $GOPATH/pkg/modpuis essayez d'ajouter plusieurs nouvelles dépendances à votre projet, vous verrez ce qui suit:

  1. Go reconstruira le cache de téléchargement pour tous les packages logiciels $GOPATH/pkg/mod/cache.
  2. Tous les modules chargés seront copiés dans $GOPATH/pkg/mod.
  3. Enfin, Go copiera ces modules dans un vendordossier, supprimant les exemples, les tests et certains autres fichiers dont vous ne dépendez pas directement.

De plus, il y a beaucoup de choses manquantes dans ce dossier de fournisseur nouvellement créé:



Un fichier Docker Compose typique ressemble à ceci (notez les liaisons de volume):

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

Veuillez noter que je ne BD pas ce dossier de fournisseur dans le système de contrôle de version ou que je ne vais pas l'utiliser en production. Il s'agit d'un script de développement strictement local, que l'on trouve généralement dans d'autres langues.

Cependant, lorsque j'ai lu les commentaires de certains responsables de Go et certaines offres liées à la vente partielle (CHE?), J'ai l'impression que cette fonctionnalité n'était initialement pas destinée à ce cas d'utilisation.

L'un des commentateurs de reddit m'a aidé à faire la lumière sur ce point:

Habituellement, les gens vendent leurs dépendances pour des raisons telles que le désir d'avoir des assemblages serrés sans accès au réseau, ainsi que la disponibilité d'une copie des dépendances prédéfinies en cas de défaillance du github ou de la disparition du référentiel, et la possibilité d'un audit plus facile des changements de dépendances à l'aide d'outils VCS standard, etc. .

Oui, il ne ressemble pas à quoi que ce soit du fait que je pourrais être intéressé.

Selon la commande Go, vous pouvez facilement activer la vente en définissant une variable d'environnement GOFLAGS=-mod=vendor. Je ne recommande pas de faire cela. L'utilisation de drapeaux se brisera simplement go getsans fournir d'autres avantages à votre flux de travail quotidien:



En fait, le seul endroit dont vous avez besoin pour activer la vente est votre IDE:



Après quelques essais et erreurs, j'ai trouvé la procédure suivante pour ajouter des dépendances de fournisseur dans cette approche.

Étape 1. Exigence


Vous pouvez exiger une dépendance avec go get:

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

Étape 2. Importer


Importez-le ensuite quelque part dans votre code:

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

Étape 3. Vente


Enfin, rouvrez vos dépendances:

go mod vendor

Il existe une proposition en attente pour autoriser le fournisseur de go mod à accepter certains modèles de module qui peuvent (ou non) résoudre certains des problèmes associés à ce flux de travail.

go mod vendorrequiert déjà automatiquement des importations manquées, donc l' étape 1 est facultative dans ce flux de travail (si vous ne souhaitez pas spécifier de restrictions de version). Cependant, sans l'étape 2, il ne récupérera pas le package téléchargé.

Cette approche fonctionne mieux avec le système hôte, mais elle est plutôt déroutante lorsqu'il s'agit de modifier vos dépendances.



Personnellement, je pense que redéfinir GOPATH est une approche plus propre car elle ne sacrifie pas la fonctionnalité go get. Néanmoins, je voulais montrer les deux stratégies, car le dossier du fournisseur peut être plus familier aux personnes venant d'autres langues, telles que PHP, Ruby, Javascript, etc. Comme vous pouvez le voir d'après la fraude décrite dans cet article, cette pas un choix particulièrement bon pour Go.

Dépendances des outils


Il se peut que nous devions installer certains outils basés sur Go qui ne sont pas importés, mais utilisés dans le cadre de l'environnement de développement de projet. Un exemple simple d'un tel outil est CompileDaemon , qui peut surveiller votre code pour les modifications et redémarrer votre application.

L' approche officiellement recommandée consiste à ajouter un tools.gofichier (le nom n'a pas d'importance) avec le contenu suivant:

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

  • Cette limitation // +build toolsempêche vos assemblages standard d'importer réellement votre outil.
  • L'expression d'importation permet aux commandes go d'écrire avec précision les informations de version de vos outils dans go.modvotre fichier de module.

Voilà donc tout. J'espère que vous ne serez pas aussi confus que moi quand j'ai commencé à utiliser les modules Go. Vous pouvez visiter le wiki Go Modules pour plus de détails.



Prenez le cap.



All Articles