Gestión de paquetes con módulos Go: una guía pragmática

Hola a todos. En previsión del comienzo del curso para desarrolladores de Golang, hemos preparado otra traducción interesante para usted.



Los módulos son una forma de lidiar con las dependencias en Go. Inicialmente presentados como un experimento, se supone que los módulos deben introducirse en el campo como un nuevo estándar para administrar paquetes desde la versión 1.13.

Considero que este tema es bastante inusual para principiantes que vienen de otros idiomas, por lo que decidí recopilar algunas ideas y consejos aquí para ayudar a otros como yo a tener una idea de la administración de paquetes en Go. Comenzaremos con una introducción general y luego pasaremos a los aspectos menos obvios, incluido el uso de la carpeta del proveedor, el uso de módulos con Docker en desarrollo, dependencias de herramientas, etc.

Si ya está familiarizado con los módulos Go y conoce el Wiki, como el dorso de tu mano, este artículo probablemente no te sea muy útil. Pero por lo demás, sin embargo, puede ahorrar varias horas de prueba y error.

Entonces, si estás en camino, súbete y disfruta del viaje.



Inicio rápido


Si el control de versiones ya está integrado en su proyecto, simplemente puede ejecutar

go mod init

O especifique la ruta al módulo manualmente. Esto es algo así como un nombre, URL y ruta de importación para su paquete:

go mod init github.com/you/hello

Esto creará un archivo go.mod, que también define los requisitos del proyecto y lochit dependiendo de su versión correcta (como una analogía para usted, es como package.json, y se package-lock.jsoncombina en un solo archivo):

module github.com/you/hello
go 1.12

Ejecute go getpara agregar una nueva dependencia a su proyecto: tenga en

cuenta que aunque no puede especificar un rango de versiones con go get, lo que define aquí no es una versión específica, sino una versión mínima. Como veremos más adelante, hay una manera de actualizar correctamente las dependencias según 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

Ahora nuestro archivo es el go.modsiguiente:

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

El sufijo se +incompatibleagrega a todos los paquetes que aún no están configurados para los módulos Go o que infringen sus reglas de control de versiones.

Como no hemos importado este paquete en ningún lugar de nuestro proyecto, se marcó como // indirect. Podemos ordenar esto con el siguiente comando:

go mod tidy

Dependiendo del estado actual de su repositorio, eliminará un módulo no utilizado o eliminará un comentario // indirect.

Si alguna dependencia por sí misma no tiene go.mod(por ejemplo, aún no está configurada para módulos), entonces todas sus dependencias se escribirán en el archivo padre go.mod(como opción, su archivo go.mod)junto con un comentario // indirectpara indicar que no son de importación directa En su

plan global, el objetivo go mod tidytambién es agregar las dependencias necesarias para otras combinaciones de SO, arquitecturas y etiquetas de compilación. Asegúrese de ejecutarlo antes de cada lanzamiento.

También asegúrese de crear un archivo después de agregar la dependenciago.sum. Puede pensar que este es un archivo de bloqueo. Pero, de hecho, go.modya proporciona suficiente información para compilaciones 100% reproducibles. El archivo go.sumse crea con fines de verificación: contiene las sumas de verificación criptográficas esperadas del contenido de las versiones individuales del módulo.

En parte porque no es go.sumun archivo de bloqueo, guarda las sumas de verificación escritas para la versión del módulo, incluso después de dejar de usar este módulo. Esto le permite verificar las sumas de verificación si reanuda su uso más tarde, lo que proporciona seguridad adicional.


Mkcert acaba de migrar a los módulos (con proveedor / para compatibilidad con versiones anteriores) y todo salió sin problemas
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



Preguntas frecuentes: ¿Debería comprometerme go.sumen git?
A: definitivamente sí. Con él, los propietarios de sus fuentes no necesitan confiar en otros repositorios de GitHub y propietarios de rutas de importación personalizadas. Ya en camino a nosotros, algo mejor, pero por ahora este es el mismo modelo que los hashes en los archivos de bloqueo.

Los comandos go buildy go testcargarán automáticamente todas las dependencias que faltan, aunque puede hacerlo explícitamente con la ayuda de go mod downloadrellenar previamente cachés locales que pueden ser útiles para CI.

Por defecto, todos nuestros paquetes de todos los proyectos se cargan en el directorio $GOPATH/pkg/mod. Discutiremos esto con más detalle más adelante.

Actualización de versiones de paquetes


Puede usar go get -ucualquiera go get -u=patchpara actualizar las dependencias a la última versión menor o parche, respectivamente.

Pero no puede actualizar a versiones principales como esa. El código incluido en los módulos Go debe cumplir técnicamente con las siguientes reglas:

  • Match semver (ejemplo, etiqueta VCS v1.2.3).
  • Si el módulo es la versión v2 o superior, la versión principal del módulo debe incluirse tanto /vNal final de la ruta del módulo utilizada en el archivo go.modcomo en la ruta de importación del paquete:

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

Aparentemente, esto se hace para que se puedan importar diferentes versiones de paquetes en un ensamblaje (consulte el problema de dependencia del diamante ).

En pocas palabras, Go espera que seas muy cuidadoso al presentar versiones principales.

Sustitución de módulos importados


Puede especificar el módulo necesario para su propia bifurcación o incluso la ruta local al archivo utilizando la directiva replace:

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

Resultado:

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

Puede eliminar la línea manualmente o ejecutar:

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

Gestión de dependencias del proyecto


Históricamente, todo el código de Go se almacenaba en un mono-repositorio gigante, porque así es como Google organiza su base de código, y esto afecta el diseño del lenguaje.

Los módulos Go son una desviación de este enfoque. Ya no necesita mantener todos sus proyectos $GOPATH.

Sin embargo, técnicamente todas sus dependencias descargadas todavía están ubicadas $GOPATH/pkg/mod. Si usa contenedores Docker para el desarrollo local, esto puede ser un problema, ya que las dependencias se almacenan fuera del proyecto. Por defecto, simplemente no son visibles en su IDE.



Esto generalmente no es un problema para otros idiomas, pero esto es lo que encontré por primera vez al trabajar con la base de código Go.

Afortunadamente, hay varias formas (indocumentadas) de resolver este problema.

Opción 1. Instale GOPATH dentro del directorio de su proyecto.


A primera vista, esto puede parecer contradictorio, pero si ejecuta Go desde un contenedor , puede anular GOPATH para que apunte al directorio del proyecto para que los paquetes sean accesibles desde el host:

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

Los IDE populares deberían poder instalar GOPATH en el nivel del proyecto (espacio de trabajo):



el único inconveniente de este enfoque es la falta de interacción con el tiempo de ejecución de Go en la computadora host. Debe ejecutar todos los comandos Go dentro del contenedor.

Opción 2: vender sus dependencias


Otra forma es copiar las dependencias de su proyecto en una carpeta vendor:

go mod vendor

Debe notarse de inmediato: NO permitimos que Go cargue directamente materiales a la carpeta del proveedor: esto no es posible con los módulos. Simplemente copiamos los paquetes ya descargados.

Además, si desata sus dependencias, como en el ejemplo anterior, borre $GOPATH/pkg/mody luego intente agregar varias dependencias nuevas a su proyecto, verá lo siguiente:

  1. Go reconstruirá el caché de descarga para todos los paquetes de software $GOPATH/pkg/mod/cache.
  2. Todos los módulos cargados se copiarán a $GOPATH/pkg/mod.
  3. Y finalmente, Go copiará estos módulos a una vendorcarpeta, eliminando ejemplos, pruebas y algunos otros archivos de los que no depende directamente.

Además, hay muchas cosas que faltan en esta carpeta de proveedor recién creada:



un archivo típico de Docker Compose tiene este aspecto (tenga en cuenta los enlaces de volumen):

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

Tenga en cuenta que NO hago cómics con esta carpeta del proveedor en el sistema de control de versiones o no la voy a usar en producción. Este es un script de desarrollo estrictamente local, que generalmente se puede encontrar en otros idiomas.

Sin embargo, cuando leo los comentarios de algunos mantenedores de Go y algunas ofertas relacionadas con la venta parcial (?), Tengo la impresión de que esta función originalmente no estaba destinada a este caso de usuario.

Uno de los comentaristas en reddit me ayudó a arrojar algo de luz sobre esto:

Por lo general, las personas venden sus dependencias por razones como el deseo de tener ensamblajes ajustados sin acceso a la red, así como la disponibilidad de una copia de las dependencias preparadas en caso de falla de github o la desaparición del repositorio, y la posibilidad de una auditoría más fácil de los cambios en las dependencias utilizando herramientas estándar de VCS, etc. .

Sí, no parece nada del hecho de que yo podría estar interesado.

De acuerdo con el comando Ir, puede habilitar fácilmente la venta mediante la configuración de una variable de entorno GOFLAGS=-mod=vendor. No recomiendo hacer esto. El uso de indicadores simplemente se romperá go getsin proporcionar ningún otro beneficio a su flujo de trabajo diario: de



hecho, el único lugar que necesita para habilitar la venta es su IDE:



Después de algunas pruebas y errores, se me ocurrió el siguiente procedimiento para agregar dependencias de proveedores en este enfoque.

Paso 1. Requisito


Puede requerir una dependencia con go get:

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

Paso 2. Importar


Luego impórtelo en algún lugar de su código:

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

Paso 3. Venta


Finalmente, vuelve a abrir tus dependencias:

go mod vendor

Hay una propuesta pendiente para permitir que el proveedor go mod acepte ciertas plantillas de módulos que pueden (o no) resolver algunos de los problemas asociados con este flujo de trabajo.

go mod vendorya requiere automáticamente las importaciones perdidas, por lo que el paso 1 es opcional en este flujo de trabajo (si no desea especificar restricciones de versión). Sin embargo, sin el paso 2, no recogerá el paquete descargado.

Este enfoque funciona mejor con el sistema host, pero es bastante confuso cuando se trata de editar sus dependencias.



Personalmente, creo que redefinir GOPATH es un enfoque más limpio, ya que no sacrifica la funcionalidad go get. Sin embargo, quería mostrar ambas estrategias, porque la carpeta del proveedor puede ser más familiar para las personas que vienen de otros lenguajes, como PHP, Ruby, Javascript, etc. Como puede ver en el fraude descrito en este artículo, este No es una opción particularmente buena para ir.

Dependencias de herramientas


Es posible que necesitemos instalar algunas herramientas basadas en Go que no se importan, sino que se usan como parte del entorno de desarrollo del proyecto. Un ejemplo simple de dicha herramienta es CompileDaemon , que puede monitorear su código en busca de cambios y reiniciar su aplicación.

El enfoque oficialmente recomendado es agregar un tools.goarchivo (el nombre no importa) con el siguiente contenido:

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

  • La limitación // +build toolsevita que sus ensamblajes regulares realmente importen su herramienta.
  • La expresión de importación permite que los comandos go escriban con precisión la información de la versión de sus herramientas en go.modsu archivo de módulo.

Entonces eso es todo. Espero que no estés tan confundido como yo cuando comencé a usar los módulos Go. Puede visitar el wiki de Go Modules para obtener más detalles.



Súbete al curso.



All Articles