Gerenciando pacotes com módulos Go: um guia pragmático

Olá a todos. Antecipando o início do curso Golang Developer, preparamos outra tradução interessante para você.



Os módulos são uma maneira de lidar com dependências no Go. Inicialmente apresentados como um experimento, os módulos devem ser introduzidos no campo como um novo padrão para gerenciar pacotes da versão 1.13.

Acho esse tópico incomum o suficiente para iniciantes vindos de outros idiomas e, por isso, decidi coletar algumas idéias e dicas aqui para ajudar outras pessoas como eu a ter uma idéia do gerenciamento de pacotes no Go. Começaremos com uma introdução geral e passaremos para os aspectos menos óbvios, incluindo o uso da pasta vendor, o uso de módulos com o Docker no desenvolvimento, as dependências de ferramentas etc.

Se você já conhece os módulos Go e conhece o Wiki, como as costas da sua mão, este artigo provavelmente não será muito útil para você. Porém, quanto ao resto, ele pode economizar várias horas de tentativa e erro.

Então, se você estiver a caminho, entre e aproveite o passeio.



Começo rápido


Se o controle de versão já estiver integrado ao seu projeto, você pode simplesmente executar

go mod init

Ou especifique o caminho para o módulo manualmente. É algo como um nome, URL e caminho de importação para o seu pacote:

go mod init github.com/you/hello

Isso criará um arquivo go.mod, que também define os requisitos e o lochit do projeto, dependendo da versão correta (como uma analogia para você, como é package.json, e package-lock.jsoncombinada em um único arquivo):

module github.com/you/hello
go 1.12

Execute go getpara adicionar uma nova dependência ao seu projeto:

Observe que, embora você não possa especificar um intervalo de versões com go get, o que você define aqui não é uma versão específica, mas uma versão mínima. Como veremos mais adiante, existe uma maneira de atualizar as dependências normalmente de acordo com sempre.

# 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

Agora, nosso arquivo é go.modo seguinte:

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

O sufixo é +incompatibleadicionado a todos os pacotes que ainda não estão configurados para os módulos Go ou violam suas regras de controle de versão.

Como não importamos este pacote em nenhum lugar do nosso projeto, ele foi marcado como // indirect. Podemos arrumar isso com o seguinte comando:

go mod tidy

Dependendo do estado atual do seu repositório, ele excluirá um módulo não utilizado ou excluirá um comentário // indirect.

Se alguma dependência por si só não tiver go.mod(por exemplo, ainda não estiver configurada para módulos), todas as dependências serão gravadas no arquivo pai go.mod(como opção, seu arquivo go.mod)juntamente com um comentário // indirectpara indicar que não são de importação direta Em seu

plano global, o objetivo go mod tidytambém é adicionar as dependências necessárias para outras combinações de SO, arquiteturas e tags de compilação. Execute-o antes de cada versão.

Certifique-se de criar um arquivo após adicionar a dependênciago.sum. Você pode pensar que este é um arquivo de bloqueio. Mas, na verdade, ele go.modjá fornece informações suficientes para compilações 100% reproduzíveis. O arquivo go.sumé criado para fins de verificação: contém as somas de verificação criptográficas esperadas do conteúdo de versões individuais do módulo.

Em parte porque não é go.sumum arquivo de bloqueio, ele salva as somas de verificação escritas para a versão do módulo, mesmo depois que você para de usá-lo. Isso permite que você verifique as somas de verificação se continuar usando-o posteriormente, o que fornece segurança adicional.


O Mkcert acabou de migrar para os módulos (com fornecedor / para compatibilidade com versões anteriores) e tudo correu bem
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: Devo cometer go.sumno git?
A: Definitivamente sim. Com isso, os proprietários de suas fontes não precisam confiar em outros repositórios do GitHub e proprietários de caminhos de importação personalizados. Já a caminho de nós, algo melhor, mas por enquanto esse é o mesmo modelo dos hashes nos arquivos de bloqueio.

Os comandos go builde go testcarregarão automaticamente todas as dependências ausentes, embora você possa fazer isso explicitamente com a ajuda de go mod downloadpreencher previamente os caches locais que podem ser úteis para o IC.

Por padrão, todos os nossos pacotes de todos os projetos são carregados para o diretório $GOPATH/pkg/mod. Discutiremos isso com mais detalhes posteriormente.

Atualizando versões do pacote


Você pode usar go get -utanto go get -u=patchpara atualizar dependências para a última versão menor ou remendo, respectivamente.

Mas você não pode atualizar para versões principais como essa. O código incluído nos módulos Go deve cumprir tecnicamente as seguintes regras:

  • Combine semver (tag de exemplo VCS v1.2.3).
  • Se o módulo for da versão v2 ou superior, a versão principal do módulo deverá ser incluída /vNno final do caminho do módulo usado no arquivo go.mode no caminho de importação do pacote:

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

Aparentemente, isso é feito para que diferentes versões dos pacotes possam ser importadas em um assembly (consulte o problema de dependência de diamante ).

Em poucas palavras, o Go espera que você seja muito cuidadoso ao apresentar as principais versões.

Substituindo Módulos Importados


Você pode especificar o módulo necessário para sua própria bifurcação ou até o caminho local para o arquivo usando a diretiva 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

Você pode excluir a linha manualmente ou executar:

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

Gerenciamento de Dependências do Projeto


Historicamente, todo o código Go foi armazenado em um repositório mono gigante, porque é assim que o Google organiza sua base de códigos e isso afeta o design da linguagem.

Os módulos Go são um desvio dessa abordagem. Você não precisa mais manter todos os seus projetos $GOPATH.

No entanto, tecnicamente todas as suas dependências baixadas ainda são inseridas $GOPATH/pkg/mod. Se você usar contêineres do Docker para desenvolvimento local, isso pode ser um problema, pois as dependências são armazenadas fora do projeto. Por padrão, eles simplesmente não são visíveis no seu IDE.



Isso geralmente não é um problema para outros idiomas, mas foi o que encontrei pela primeira vez ao trabalhar com a base de código Go.

Felizmente, existem várias maneiras (não documentadas) de resolver esse problema.

Opção 1. Instale o GOPATH dentro do diretório do projeto.


À primeira vista, isso pode parecer contra-intuitivo, mas se você executar o Go a partir de um contêiner , poderá substituir o GOPATH para que aponte para o diretório do projeto para que os pacotes fiquem acessíveis a partir do 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

Os IDEs populares devem poder instalar o GOPATH no nível do projeto (espaço de trabalho):



A única desvantagem dessa abordagem é a falta de interação com o tempo de execução do Go no computador host. Você deve executar todos os comandos Go dentro do contêiner.

Opção 2: vendendo suas dependências


Outra maneira é copiar as dependências do seu projeto para uma pasta vendor:

go mod vendor

Deve-se observar imediatamente: NÃO permitimos que o Go faça o upload direto de materiais para a pasta do fornecedor: isso não é possível com os módulos. Simplesmente copiamos pacotes já baixados.

Além disso, se você desatar as suas dependências, como no exemplo acima, limpe $GOPATH/pkg/mode tente adicionar várias novas dependências ao seu projeto, você verá o seguinte:

  1. O Go reconstruirá o cache de download de todos os pacotes de software $GOPATH/pkg/mod/cache.
  2. Todos os módulos carregados serão copiados para $GOPATH/pkg/mod.
  3. E, finalmente, o Go copiará esses módulos para uma vendorpasta, excluindo exemplos, testes e alguns outros arquivos dos quais você não depende diretamente.

Além disso, faltam muitas coisas nessa pasta de fornecedor recém-criada:



Um arquivo típico do Docker Compose se parece com isso (observe as ligações do 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

Por favor, note que eu NÃO coloque essa pasta de fornecedores em quadrinhos no sistema de controle de versão ou não a usarei na produção. Este é um script de desenvolvimento estritamente local, que geralmente pode ser encontrado em outros idiomas.

No entanto, quando leio comentários de alguns mantenedores do Go e algumas ofertas relacionadas a vendas parciais (?), Tenho a impressão de que esse recurso foi originalmente destinado não a esse caso de usuário.

Um dos comentaristas do reddit me ajudou a esclarecer isso:

Geralmente, as pessoas vendem suas dependências por motivos como o desejo de ter assemblies restritos sem acesso à rede, além da disponibilidade de uma cópia de dependências prontas no caso de uma falha ou repositório do github desaparecer, e a capacidade de auditar com mais facilidade as alterações de dependência usando as ferramentas VCS padrão, etc. .

Sim, ele não se parece com nada do fato de que eu poderia estar interessado.

De acordo com o comando Ir, você pode facilmente ativar a venda, definindo uma variável de ambiente GOFLAGS=-mod=vendor. Eu não recomendo fazer isso. O uso de sinalizadores simplesmente será interrompido go getsem fornecer outros benefícios ao seu fluxo de trabalho diário:



Na verdade, o único local necessário para ativar a venda é o seu IDE:



Após algumas tentativas e erros, criei o procedimento a seguir para adicionar dependências de fornecedores nessa abordagem.

Etapa 1. Requisito


Você pode exigir uma dependência com go get:

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

Etapa 2. Importar


Em seguida, importe-o em algum lugar do seu código:

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

Etapa 3. Venda


Por fim, reabra suas dependências:

go mod vendor

Existe uma proposta pendente para permitir que o fornecedor de mod go aceite determinados modelos de módulo que podem (ou não) resolver alguns dos problemas associados a esse fluxo de trabalho.

go mod vendorjá requer importações perdidas automaticamente, portanto, a etapa 1 é opcional neste fluxo de trabalho (se você não desejar especificar restrições de versão). No entanto, sem a etapa 2, ele não receberá o pacote baixado.

Essa abordagem funciona melhor com o sistema host, mas é bastante confusa quando se trata de editar suas dependências.



Pessoalmente, acho que redefinir o GOPATH é uma abordagem mais limpa, pois não sacrifica a funcionalidade go get. No entanto, eu queria mostrar as duas estratégias, porque a pasta do fornecedor pode ser mais familiar para pessoas provenientes de outros idiomas, como PHP, Ruby, Javascript, etc. Como você pode ver na fraude descrita neste artigo, esta não é uma escolha particularmente boa para o Go.

Dependências da ferramenta


Talvez seja necessário instalar algumas ferramentas baseadas em Go que não são importadas, mas usadas como parte do ambiente de desenvolvimento do projeto. Um exemplo simples dessa ferramenta é o CompileDaemon , que pode monitorar seu código em busca de alterações e reiniciar seu aplicativo.

A abordagem oficialmente recomendada é adicionar um tools.goarquivo (o nome não importa) com o seguinte conteúdo:

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

  • A limitação // +build toolsimpede que seus assemblies regulares realmente importem sua ferramenta.
  • A expressão import permite que os comandos go gravem com precisão as informações da versão de suas ferramentas no arquivo do go.modmódulo.

Então é só isso. Espero que você não fique tão confuso quanto eu quando comecei a usar os módulos Go. Você pode visitar o wiki Go Modules para obter mais detalhes.



Entre no curso.



All Articles