Módulos
Toda linguagem moderna possui sua própria ferramenta madura de gerenciamento de dependências, como Gradle do Java, Pip do Python, Npm do NodeJs, etc. Uma boa ferramenta de gerenciamento de dependências pode economizar muito tempo para os desenvolvedores e aumentar a eficiência do desenvolvimento. No entanto, no início, Go não tinha uma solução madura de gerenciamento de dependências. Naquela época, todo o código era armazenado no diretório GOPATH, o que era muito inadequado para projetos de engenharia, com versões caóticas e dependências difíceis de gerenciar. Para resolver esse problema, desenvolvedores de várias comunidades apresentaram suas soluções, criando uma situação confusa. Durante esse período, surgiram algumas soluções destacadas como Vendor, até que o Go1.11 finalmente lançou o Go Mod, a ferramenta oficial de gerenciamento de dependências, encerrando o caos anterior e continuamente aprimorando nas atualizações subsequentes, eliminando as ferramentas antigas. Hoje, ao escrever este artigo, a versão de distribuição do Go chegou à 1.20, e quase todos os projetos Go estão usando Go Mod. Portanto, neste artigo, apresentaremos apenas o Go Mod. A equipe oficial do Go também escreveu documentação muito detalhada sobre módulos Go: Go Modules Reference.
Escrevendo Módulos
O Go Module é essencialmente baseado em VCS (Sistema de Controle de Versão). Quando você baixa dependências, na verdade está executando comandos VCS, como git. Portanto, se você quiser compartilhar uma biblioteca que escreveu, precisa apenas cumprir os seguintes três pontos:
- O repositório de código-fonte deve ser publicamente acessível e o VCS deve ser um dos seguintes:
- git
- hg (Mercurial)
- bzr (Bazaar)
- svn
- fossil
- Ser um projeto go mod que esteja em conformidade com as especificações
- Estar em conformidade com as especificações de versionamento semântico
Portanto, você só precisa desenvolver normalmente usando VCS e criar tags conformes aos padrões para suas versões específicas. Outras pessoas poderão baixar sua biblioteca através do nome do módulo. Abaixo, demonstraremos as etapas do desenvolvimento de módulos através de um exemplo.
Repositório de exemplo: 246859/hello: say hello (github.com)
Preparação
Antes de começar, certifique-se de que sua versão seja suficiente para suportar completamente o go mod (go >= 1.17) e que o Go Module esteja habilitado. Verifique se está ativado com o seguinte comando:
$ go env GO111MODULESe não estiver ativado, habilite o Go Module com o seguinte comando:
$ go env -w GO111MODULE=onCriação
Primeiro, você precisa de um repositório de código-fonte acessível publicamente. Há muitas opções, mas recomendo o Github. Crie um novo projeto lá, nomeando-o como hello. Embora não haja restrições especiais para nomes de repositórios, é recomendável não usar caracteres especiais, pois isso pode afetar o nome do módulo.

Após a criação, você pode ver que a URL do repositório é https://github.com/246859/hello, e o nome do módulo go correspondente é github.com/246859/hello.

Em seguida, clone-o localmente e inicialize o módulo com o comando go mod init:
$ git clone git@github.com:246859/hello.git
Cloning into 'hello'...
remote: Enumerating objects: 5, done.
remote: Counting objects: 100% (5/5), done.
remote: Compressing objects: 100% (4/4), done.
remote: Total 5 (delta 0), reused 0 (delta 0), pack-reused 0
Receiving objects: 100% (5/5), done.
$ cd hello && go mod init github.com/246859/hello
go: creating new go.mod: module github.com/246859/helloEscrita
Agora você pode começar o desenvolvimento. A funcionalidade é muito simples, com apenas uma função:
// hello.go
package hello
import "fmt"
// Hello returns hello message
func Hello(name string) string {
if name == "" {
name = "world"
}
return fmt.Sprintf("hello %s!", name)
}Aproveite para escrever um arquivo de teste para teste unitário:
// hello_test.go
package hello_test
import (
"testing"
"fmt"
"github.com/246859/hello"
)
func TestHello(t *testing.T) {
data := "jack"
expected := fmt.Sprintf("hello %s!", data)
result := hello.Hello(data)
if result != expected {
t.Fatalf("expected result %s, but got %s", expected, result)
}
}Em seguida, continue escrevendo um programa de linha de comando para output hello. Sua funcionalidade também é muito simples. Para programas de linha de comando, de acordo com as especificações, eles devem ser criados em cmd/app_name/ do projeto. Portanto, os arquivos do programa de linha de comando hello são armazenados no diretório cmd/hello/, onde você escreve o código relacionado.
// cmd/hello/main.go
package main
import (
"flag"
"github.com/246859/hello"
"os"
)
var name string
func init() {
flag.StringVar(&name, "name", "world", "name to say hello")
}
func main() {
flag.Parse()
msg := hello.Hello(name)
_, err := os.Stdout.WriteString(msg)
if err != nil {
os.Stderr.WriteString(err.Error())
}
}Teste
Após escrever, formate o código-fonte e teste:
$ go fmt && go vet ./...
$ go test -v .
=== RUN TestHello
--- PASS: TestHello (0.00s)
PASS
ok github.com/246859/hello 0.023sExecute o programa de linha de comando:
$ go run ./cmd/hello -name jack
hello jack!Documentação
Por fim, é necessário escrever um README conciso e claro para esta biblioteca, para que outros desenvolvedores saibam como usá-la com apenas uma olhada:
# hello
just say hello
## Install
import code
```bash
go get github.com/246859/hello@latest
```
install cmd
```bash
go install github.com/246859/hello/cmd/hello@latest
```
## Example
Here's a simple example as follows:
```go
package main
import (
"fmt"
"github.com/246859/hello"
)
func main() {
result := hello.Hello("jack")
fmt.Println(result)
}
```Este é um documento README muito simples. Você pode enriquecê-lo conforme desejar.
Upload
Quando todo o código estiver escrito e testado, você pode enviar as modificações e fazer push para o repositório remoto.
$ git add go.mod hello.go hello_test.go cmd/ example/ README.md
$ git commit -m "chore(mod): mod init" go.mod
[main 5087fa2] chore(mod): mod init
1 file changed, 3 insertions(+)
create mode 100644 go.mod
$ git commit -m "feat(hello): complete Hello func" hello.go
[main 099a8bf] feat(hello): complete Hello func
1 file changed, 11 insertions(+)
create mode 100644 hello.go
$ git commit -m "test(hello): complete hello testcase" hello_test.go
[main 76e8c1e] test(hello): complete hello testcase
1 file changed, 17 insertions(+)
create mode 100644 hello_test.go
$ git commit -m "feat(hello): complete hello cmd" cmd/hello/
[main a62a605] feat(hello): complete hello cmd
1 file changed, 22 insertions(+)
create mode 100644 cmd/hello/main.go
$ git commit -m "docs(example): add hello example" example/
[main 5c51ce4] docs(example): add hello example
1 file changed, 11 insertions(+)
create mode 100644 example/main.go
$ git commit -m "docs(README): update README" README.md
[main e6fbc62] docs(README): update README
1 file changed, 27 insertions(+), 1 deletion(-)Seis commits no total não é muito. Após o envio, crie uma tag para o commit mais recente:
$ git tag v1.0.0
$ git tag -l
v1.0.0
$ git log --oneline
e6fbc62 (HEAD -> main, tag: v1.0.0, origin/main, origin/HEAD) docs(README): update README
5c51ce4 docs(example): add hello example
a62a605 feat(hello): complete hello cmd
76e8c1e test(hello): complete hello testcase
099a8bf feat(hello): complete Hello func
5087fa2 chore(mod): mod init
1f422d1 Initial commitFinalmente, faça push para o repositório remoto:
$ git push --tags
Enumerating objects: 23, done.
Counting objects: 100% (23/23), done.
Delta compression using up to 16 threads
Compressing objects: 100% (17/17), done.
Writing objects: 100% (21/21), 2.43 KiB | 1.22 MiB/s, done.
Total 21 (delta 5), reused 0 (delta 0), pack-reused 0
remote: Resolving deltas: 100% (5/5), done.
To github.com:246859/hello.git
1f422d1..e6fbc62 main -> main
* [new tag] v1.0.0 -> v1.0.0Após o push, crie um release (ter uma tag é suficiente, o release é apenas para conformidade com as especificações do github):

Assim, a escrita do módulo está completa. Acima está o fluxo básico de desenvolvimento de módulos. Outros desenvolvedores podem importar o código ou instalar ferramentas de linha de comando através do nome do módulo.
Importação
Importe a biblioteca com go get:
$ go get github.com/246859/hello@latest
go: downloading github.com/246859/hello v1.0.0
go: added github.com/246859/hello v1.0.0Instale o programa de linha de comando com go install:
$ go install github.com/246859/hello/cmd/hello@latest && hello -name jack
hello jack!Ou execute diretamente com go run:
$ go run -mod=mod github.com/246859/hello/cmd/hello -name jack
hello jack!Quando uma biblioteca é importada, o Go Package criará uma página para ela automaticamente. Este processo é automático e não requer trabalho do desenvolvedor. Por exemplo, a biblioteca hello tem uma página de documentação exclusiva, como mostrado abaixo.

Para mais detalhes sobre upload de módulos, consulte Add a package.
Para informações sobre como excluir módulos, consulte Removing a package.
Configurando Proxy
Embora o Go não tenha um repositório central como Maven Repo, PyPi ou NPM, ele possui um repositório de proxy oficial: Go modules services (golang.org), que armazena em cache os módulos baixados pelos desenvolvedores com base na versão e no nome do módulo. No entanto, como seus servidores estão localizados no exterior, a velocidade de acesso não é amigável para usuários domésticos (na China). Portanto, precisamos modificar o endereço de proxy de módulo padrão. Atualmente, os seguintes são os melhores na China:

Aqui escolhemos o proxy da Qiniu Cloud. Execute o seguinte comando para modificar o proxy do Go, onde direct significa que, se o download do proxy falhar, ele acessará diretamente o repositório de código-fonte, ignorando o cache do proxy:
$ go env -w GOPROXY=https://goproxy.cn,directApós modificar o proxy com sucesso, o download de dependências será muito rápido no futuro.
Baixando Dependências
Após modificar o proxy, vamos tentar instalar uma dependência de terceiros. O Go oficial tem um site especializado em consulta de dependências: Go Packages.
Importação de Código
Pesquise pelo famoso framework Web Gin nele.

Aparecerão muitos resultados de pesquisa. Ao usar dependências de terceiros, é necessário decidir se adota a dependência com base no número de citações e no tempo de atualização. Aqui, escolhemos diretamente o primeiro:

Após entrar na página correspondente, podemos ver que esta é uma página de documentação da dependência, com muitas informações detalhadas sobre ela. Você também pode vir aqui para consultar a documentação posteriormente.

Agora, basta copiar seu endereço e usar o comando go get no projeto criado anteriormente:
$ go get github.com/gin-gonic/ginDurante o processo, muitas dependências serão baixadas. Desde que não haja erros, o download foi bem-sucedido:
$ go get github.com/gin-gonic/gin
go: added github.com/bytedance/sonic v1.8.0
go: added github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311
go: added github.com/gin-contrib/sse v0.1.0
go: added github.com/gin-gonic/gin v1.9.0
go: added github.com/go-playground/locales v0.14.1
go: added github.com/go-playground/universal-translator v0.18.1
go: added github.com/go-playground/validator/v10 v10.11.2
go: added github.com/goccy/go-json v0.10.0
go: added github.com/json-iterator/go v1.1.12
go: added github.com/klauspost/cpuid/v2 v2.0.9
go: added github.com/leodido/go-urn v1.2.1
go: added github.com/mattn/go-isatty v0.0.17
go: added github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421
go: added github.com/modern-go/reflect2 v1.0.2
go: added github.com/pelletier/go-toml/v2 v2.0.6
go: added github.com/twitchyliquid64/golang-asm v0.15.1
go: added github.com/ugorji/go/codec v1.2.9
go: added golang.org/x/arch v0.0.0-20210923205945-b76863e36670
go: added golang.org/x/crypto v0.5.0
go: added golang.org/x/net v0.7.0
go: added golang.org/x/sys v0.5.0
go: added golang.org/x/text v0.7.0
go: added google.golang.org/protobuf v1.28.1
go: added gopkg.in/yaml.v3 v3.0.1Após concluir, verifique o arquivo go.mod:
$ cat go.mod
module golearn
go 1.20
require github.com/gin-gonic/gin v1.9.0
require (
github.com/bytedance/sonic v1.8.0 // indirect
github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311 // indirect
github.com/gin-contrib/sse v0.1.0 // indirect
github.com/go-playground/locales v0.14.1 // indirect
github.com/go-playground/universal-translator v0.18.1 // indirect
github.com/go-playground/validator/v10 v10.11.2 // indirect
github.com/goccy/go-json v0.10.0 // indirect
github.com/json-iterator/go v1.1.12 // indirect
github.com/klauspost/cpuid/v2 v2.0.9 // indirect
github.com/leodido/go-urn v1.2.1 // indirect
github.com/mattn/go-isatty v0.0.17 // indirect
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421 // indirect
github.com/modern-go/reflect2 v1.0.2 // indirect
github.com/pelletier/go-toml/v2 v2.0.6 // indirect
github.com/twitchyliquid64/golang-asm v0.15.1 // indirect
github.com/ugorji/go/codec v1.2.9 // indirect
golang.org/x/arch v0.0.0-20210923205945-b76863e36670 // indirect
golang.org/x/crypto v0.5.0 // indirect
golang.org/x/net v0.7.0 // indirect
golang.org/x/sys v0.5.0 // indirect
golang.org/x/text v0.7.0 // indirect
google.golang.org/protobuf v1.28.1 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
)Podemos ver que há muitas coisas novas em comparação com antes. Também notamos que há um novo arquivo chamado go.sum no diretório:
$ ls
go.mod go.sum main.goDeixaremos isso de lado por enquanto. Modifique o arquivo main.go com o seguinte código:
package main
import (
"github.com/gin-gonic/gin"
)
func main() {
gin.Default().Run()
}Execute o projeto novamente:
$ go run golearn
[GIN-debug] [WARNING] Creating an Engine instance with the Logger and Recovery middleware already attached.
[GIN-debug] [WARNING] Running in "debug" mode. Switch to "release" mode in production.
- using env: export GIN_MODE=release
- using code: gin.SetMode(gin.ReleaseMode)
[GIN-debug] [WARNING] You trusted all proxies, this is NOT safe. We recommend you to set a value.
Please check https://pkg.go.dev/github.com/gin-gonic/gin#readme-don-t-trust-all-proxies for details.
[GIN-debug] Environment variable PORT is undefined. Using port :8080 by default
[GIN-debug] Listening and serving HTTP on :8080Assim, com apenas uma linha de código, executamos um servidor Web mais simples. Quando não precisar mais de uma determinada dependência, também pode usar o comando go get para removê-la. Aqui, usamos a remoção do Gin como exemplo:
$ go get github.com/gin-gonic/gin@none
go: removed github.com/gin-gonic/gin v1.9.0Basta adicionar @none após o endereço da dependência para removê-la. O resultado também indica que a remoção foi bem-sucedida. Ao verificar o arquivo go.mod novamente, veremos que não há mais a dependência do Gin:
$ cat go.mod | grep github.com/gin-gonic/ginQuando precisar atualizar para a versão mais recente, adicione o sufixo @latest ou consulte manualmente os números de versão Release disponíveis:
$ go get -u github.com/gin-gonic/gin@latestInstalação de Linha de Comando
O comando go install baixará dependências de terceiros localmente e as compilará em arquivos binários. Graças à velocidade de compilação do Go, esse processo geralmente não leva muito tempo. Em seguida, o Go os armazenará no diretório $GOPATH/bin ou $GOBIN, para que o arquivo binário possa ser executado globalmente (desde que você tenha adicionado esses caminhos às variáveis de ambiente).
TIP
Ao usar o comando install, é necessário especificar o número da versão.
Por exemplo, baixe o depurador delve escrito em Go:
$ go install github.com/go-delve/delve/cmd/dlv@latest
go: downloading github.com/go-delve/delve v1.22.1
go: downloading github.com/cosiner/argv v0.1.0
go: downloading github.com/derekparker/trie v0.0.0-20230829180723-39f4de51ef7d
go: downloading github.com/go-delve/liner v1.2.3-0.20231231155935-4726ab1d7f62
go: downloading github.com/google/go-dap v0.11.0
go: downloading github.com/hashicorp/golang-lru v1.0.2
go: downloading golang.org/x/arch v0.6.0
go: downloading github.com/cpuguy83/go-md2man/v2 v2.0.2
go: downloading go.starlark.net v0.0.0-20231101134539-556fd59b42f6
go: downloading github.com/cilium/ebpf v0.11.0
go: downloading github.com/mattn/go-runewidth v0.0.13
go: downloading github.com/russross/blackfriday/v2 v2.1.0
go: downloading github.com/rivo/uniseg v0.2.0
go: downloading golang.org/x/exp v0.0.0-20230224173230-c95f2b4c22f2
$ dlv -v
Error: unknown shorthand flag: 'v' in -v
Usage:
dlv [command]
Available Commands:
attach Attach to running process and begin debugging.
completion Generate the autocompletion script for the specified shell
connect Connect to a headless debug server with a terminal client.
core Examine a core dump.
dap Starts a headless TCP server communicating via Debug Adaptor Protocol (DAP).
debug Compile and begin debugging main package in current directory, or the package specified.
exec Execute a precompiled binary, and begin a debug session.
help Help about any command
test Compile test binary and begin debugging program.
trace Compile and begin tracing program.
version Prints version.
Additional help topics:
dlv backend Help about the --backend flag.
dlv log Help about logging flags.
dlv redirect Help about file redirection.
Use "dlv [command] --help" for more information about a command.Gerenciamento de Módulos
Todo o conteúdo acima trata apenas do uso básico do Go Mod, mas na verdade, aprender apenas isso não é suficiente. A definição oficial de módulo é: um conjunto de pacotes marcados com versão. Na definição acima, o conceito de pacote deve ser bastante familiar, e a versão deve seguir o versionamento semântico, definido no formato v(major).(minor).(patch). Por exemplo, a versão do Go v1.20.1, onde a versão principal é 1, a versão menor é 20 e a versão de patch é 1, resultando em v1.20.1. Abaixo está uma explicação mais detalhada:
major: Quando a versão principal muda, indica que o projeto sofreu alterações incompatíveis. Projetos de versões antigas provavelmente não funcionarão corretamente após a atualização para a nova versão.minor: Quando a versãominormuda, indica que o projeto adicionou novos recursos, apenas adicionando novas funcionalidades à base da versão anterior.patch: Quando a versãopatchmuda, indica que apenas bugs foram corrigidos, sem adicionar novas funcionalidades.
Comandos Comuns
| Comando | Descrição |
|---|---|
go mod download | Baixa os pacotes de dependência do projeto |
go mod edit | Edita o arquivo go.mod |
go mod graph | Output o gráfico de dependências do módulo |
go mod init | Inicializa go mod no diretório atual |
go mod tidy | Limpa os módulos do projeto |
go mod verify | Verifica a legalidade das dependências |
go mod why | Explica onde o projeto usa dependências |
go clean -modcache | Usado para excluir cache de dependências |
go list -m | Lista módulos |
Consulte go mod cmd para mais informações sobre comandos.
Armazenamento de Módulos
Ao usar Go Mod para gerenciamento de projetos, o cache de módulos é armazenado por padrão no diretório $GOPATH/pkg/mod. Você também pode modificar $GOMODCACHE para especificar outro local:
$ go env -w GOMODCACHE=seu/caminho/de/cache/de/modulosTodos os projetos Go Module na mesma máquina compartilham o cache neste diretório. O cache não tem limite de tamanho e não é excluído automaticamente. Os arquivos de origem de dependências descompactados no cache são somente leitura. Para limpar o cache, execute o seguinte comando:
$ go clean -modcacheNo diretório $GOMODCACHE/cache/download estão armazenados os arquivos originais das dependências, incluindo arquivos de hash, pacotes compactados originais, etc. Por exemplo:
$ ls $(go env GOMODCACHE)/cache/download/github.com/246859/hello/@v -1
list
v1.0.0.info
v1.0.0.lock
v1.0.0.mod
v1.0.0.zip
v1.0.0.ziphashA organização das dependências após descompactação é mostrada abaixo, que é o código-fonte do módulo especificado:
$ ls $(go env GOMODCACHE)/github.com/246859/hello@v1.0.0 -1
LICENSE
README.md
cmd/
example/
go.mod
hello.go
hello_test.goSeleção de Versão
Ao selecionar versões de dependências, o Go segue o princípio de seleção de versão mínima. Abaixo está um exemplo do site oficial: o módulo principal referencia a versão 1.2 do módulo A e a versão 1.2 do módulo B. Ao mesmo tempo, a versão 1.2 do módulo A referencia a versão 1.3 do módulo C, a versão 1.2 do módulo B referencia a versão 1.4 do módulo C, e as versões 1.3 e 1.4 do módulo C ambas referenciam a versão 1.2 do módulo D. De acordo com o princípio da versão mínima utilizável, o Go finalmente selecionará as versões A1.2, B1.2, C1.4 e D1.2. O azul claro indica carregado pelo arquivo go.mod, e o selecionado indica a versão finalmente escolhida.
O site oficial também fornece outros exemplos, que são basicamente semelhantes.
go.mod
Cada vez que um projeto Go Mod é criado, um arquivo go.mod é gerado. Portanto, é muito necessário se familiarizar com o arquivo go.mod, embora na maioria dos casos não seja necessário modificá-lo manualmente.
module golearn
go 1.20
require github.com/gin-gonic/gin v1.9.0
require (
github.com/bytedance/sonic v1.8.0 // indirect
github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311 // indirect
github.com/gin-contrib/sse v0.1.0 // indirect
github.com/go-playground/locales v0.14.1 // indirect
github.com/go-playground/universal-translator v0.18.1 // indirect
github.com/go-playground/validator/v10 v10.11.2 // indirect
github.com/goccy/go-json v0.10.0 // indirect
github.com/json-iterator/go v1.1.12 // indirect
github.com/klauspost/cpuid/v2 v2.0.9 // indirect
github.com/leodido/go-urn v1.2.1 // indirect
github.com/mattn/go-isatty v0.0.17 // indirect
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421 // indirect
github.com/modern-go/reflect2 v1.0.2 // indirect
github.com/pelletier/go-toml/v2 v2.0.6 // indirect
github.com/twitchyliquid64/golang-asm v0.15.1 // indirect
github.com/ugorji/go/codec v1.2.9 // indirect
golang.org/x/arch v0.0.0-20210923205945-b76863e36670 // indirect
golang.org/x/crypto v0.5.0 // indirect
golang.org/x/net v0.7.0 // indirect
golang.org/x/sys v0.5.0 // indirect
golang.org/x/text v0.7.0 // indirect
google.golang.org/protobuf v1.28.1 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
)No arquivo, podemos descobrir que a maioria dos endereços de dependência contém palavras como github. Isso ocorre porque o Go não tem um repositório de dependências público. A maioria dos projetos de código aberto está hospedada no GitHub, embora alguns tenham seus próprios repositórios, como google.golang.org/protobuf e golang.org/x/crypto. Normalmente, essa sequência de URL é também o nome do módulo do projeto Go. Isso gera um problema: URLs não diferenciam maiúsculas de minúsculas, mas os diretórios de armazenamento de dependências diferenciam. Portanto, go get github.com/gin-gonic/gin e go get github.com/gin-gonic/Gin referenciam a mesma dependência, mas o caminho de armazenamento local é diferente. Quando isso ocorre, o Go não usa diretamente as letras maiúsculas como caminho de armazenamento, mas as escapa como !letrasminúsculas. Por exemplo, github.com\BurntSushi será finalmente escapado para github.com\!burnt!sushi.
module
A palavra-chave module declara o nome do módulo do projeto atual. Apenas uma palavra-chave module pode aparecer em um arquivo go.mod. No exemplo:
module golearnIsso representa que o nome do módulo atual é golearn. Por exemplo, ao abrir o arquivo go.mod da dependência do Gin, podemos ver seu nome module:
module github.com/gin-gonic/ginO nome do módulo do Gin é o endereço usado ao baixar dependências. Este é também o formato recomendado para nomes de módulo: domínio/usuário/nome-do-repositório.
TIP
Um ponto a ser observado é que, quando a versão principal é maior que 1, o número da versão principal deve ser refletido no nome do módulo. Por exemplo:
github.com/my/exampleSe a versão for atualizada para v2.0.0, o nome do módulo precisará ser modificado para:
github.com/my/example/v2Se projetos existentes referenciarem a versão antiga e a nova versão não for diferenciada, ao importar dependências, como os caminhos são consistentes, os usuários não poderão distinguir as alterações incompatíveis causadas pela mudança da versão principal, o que pode causar erros no programa.
Deprecation
Comente Deprecated no início da linha acima de module para indicar que o módulo está obsoleto. Por exemplo:
// Deprecated: use example.com/mod/v2 instead.
module example.com/modgo
A palavra-chave go indica a versão do Go usada para escrever o projeto atual. O número da versão deve seguir as regras de versionamento semântico. Dependendo da versão do Go, o Go Mod se comportará de maneira diferente. Abaixo está um exemplo simples. Consulte a documentação oficial para versões disponíveis do Go:
go 1.20require
A palavra-chave require indica que uma dependência externa foi importada. Por exemplo:
require github.com/gin-gonic/gin v1.9.0O formato é require nome-do-módulo versão. Quando há múltiplas importações, elas podem ser agrupadas entre parênteses:
require (
github.com/bytedance/sonic v1.8.0 // indirect
)Dependências com o comentário // indirect indicam que não foram diretamente importadas pelo projeto atual. Pode ser que uma dependência diretamente importada pelo projeto importe essa dependência, portanto, para o projeto atual, é uma importação indireta. Como mencionado anteriormente, mudanças na versão principal devem ser refletidas no nome do módulo. Módulos que não seguem essa regra são chamados de módulos não conformes e, ao usar require, será adicionado o comentário incompatible:
require example.com/m v4.1.2+incompatibleVersões Pseudo
No arquivo go.mod acima, podemos descobrir que algumas versões de pacotes de dependência não são números de versão semânticos, mas sim uma sequência de caracteres incompreensíveis. Na verdade, este é o CommitID da versão correspondente. Versões semânticas geralmente se referem a um Release específico. Versões pseudo podem especificar um Commit específico. O formato usual é vx.y.z-yyyyMMddHHmmss-CommitId. Como seu vx.y.z pode não existir realmente, é chamado de versão pseudo. Por exemplo, no exemplo abaixo, v0.0.0 não existe; o que é realmente válido são os 12 dígitos do CommitID seguintes:
// CommitID geralmente usa os primeiros 12 dígitos
github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311 // indirectDa mesma forma, ao baixar dependências, você pode especificar o CommitID para substituir o número de versão semântico:
go get github.com/chenzhuoyu/base64x@fe3a3abad311exclude
A palavra-chave exclude indica que uma versão específica de dependência não deve ser carregada. Se houver require importando a mesma versão, também será ignorado. Esta palavra-chave só funciona no módulo principal. Por exemplo:
exclude golang.org/x/net v1.2.3
exclude (
golang.org/x/crypto v1.4.5
golang.org/x/text v1.6.7
)replace
replace substituirá a versão especificada da dependência. Pode usar caminho e versão do módulo para substituir, ou caminho de arquivo local especificado. Exemplo:
replace golang.org/x/net v1.2.3 => example.com/fork/net v1.4.5
replace (
golang.org/x/net v1.2.3 => example.com/fork/net v1.4.5
golang.org/x/net => example.com/fork/net v1.4.5
golang.org/x/net v1.2.3 => ./fork/net
golang.org/x/net => ./fork/net
)Apenas a versão à esquerda de => é substituída; outras versões da mesma dependência ainda podem ser acessadas normalmente. Seja usando caminho local ou caminho de módulo para especificar substituição, se o módulo de substituição tiver um arquivo go.mod, sua diretiva module deve corresponder ao caminho do módulo substituído.
retract
A diretiva retract indica que não se deve depender da versão ou intervalo de versões especificado por retract. Por exemplo, se um problema grave for descoberto após o lançamento de uma nova versão, pode-se usar a diretiva retract.
Retratar algumas versões:
retract (
v1.0.0 // Published accidentally.
v1.0.1 // Contains retractions only.
)Retratar intervalo de versões:
retract v1.0.0
retract [v1.0.0, v1.9.9]
retract (
v1.0.0
[v1.0.0, v1.9.9]
)go.sum
O arquivo go.sum não existe no início da criação do projeto. Ele só é gerado quando dependências externas são realmente importadas. O arquivo go.sum não é adequado para leitura humana e não é recomendado modificá-lo manualmente. Sua função principal é resolver problemas de construção consistente, ou seja, as dependências importadas devem ser exatamente as mesmas quando pessoas diferentes usam o mesmo projeto em ambientes diferentes. Isso não pode ser garantido apenas com um arquivo go.mod.
Vamos ver o que o Go faz do início ao fim ao baixar uma dependência. Primeiro, use o seguinte comando para baixar uma dependência:
go get github.com/bytedance/sonic v1.8.0O comando go get primeiro baixará o pacote de dependência para o diretório de cache local, geralmente $GOMODCACHE/cache/download/. Este diretório é dividido por nome de domínio para dependências de diferentes sites, então você pode ver a seguinte estrutura de diretórios:
$ ls
cloud.google.com/ go.opencensus.io/ gopkg.in/ nhooyr.io/
dmitri.shuralyov.com/ go.opentelemetry.io/ gorm.io/ rsc.io/
github.com/ go.uber.org/ honnef.co/ sumdb/
go.etcd.io/ golang.org/ lukechampine.com/
go.mongodb.org/ google.golang.org/ modernc.org/Então, o caminho onde a dependência baixada no exemplo acima é armazenada está em:
$GOMODCACHE/cache/download/github.com/bytedance/sonic/@v/A possível estrutura de diretórios é a seguinte, com vários arquivos nomeados por versão:
$ ls
list v1.8.0.lock v1.8.0.ziphash v1.8.3.mod
v1.5.0.mod v1.8.0.mod v1.8.3.info v1.8.3.zip
v1.8.0.info v1.8.0.zip v1.8.3.lock v1.8.3.ziphashNormalmente, deve haver um arquivo list neste diretório, usado para registrar os números de versão conhecidos da dependência. Para cada versão, haverá os seguintes arquivos:
zip: pacote compactado do código-fonte da dependênciaziphash: valor hash calculado com base no pacote compactado da dependênciainfo: metadados de versão em formato jsonmod: arquivogo.moddesta versãolock: arquivo temporário (a documentação oficial não explica para que serve)
Geralmente, o Go calculará os valores hash de dois arquivos: o pacote compactado e o arquivo go.mod. Em seguida, consultará o valor hash da dependência no servidor especificado pelo GOSUMDB (padrão é sum.golang.org). Se o valor hash calculado localmente não corresponder ao resultado da consulta, a execução não continuará. Se corresponder, atualizará o arquivo go.mod e inserirá dois registros no arquivo go.sum, aproximadamente assim:
github.com/bytedance/sonic v1.8.0 h1:ea0Xadu+sHlu7x5O3gKhRpQ1IKiMrSiHttPF0ybECuA=
github.com/bytedance/sonic v1.8.0/go.mod h1:i736AoUSYt75HyZLoJW9ERYxcy6eaN6h4BZXU064P/U=TIP
Se o GOSUMDB estiver desabilitado, o Go escreverá diretamente o valor hash calculado localmente no arquivo go.sum. Geralmente, não é recomendado fazer isso.
Normalmente, cada dependência terá dois registros: o primeiro é o valor hash do pacote compactado e o segundo é o valor hash do arquivo go.mod da dependência. O formato do registro é nome-do-módulo versão nome-do-algoritmo:valor-hash. Alguns pacotes de dependência mais antigos podem não ter arquivo go.mod, então não haverá segundo registro de hash. Quando este projeto é construído em outro ambiente, o Go calculará o valor hash da dependência local especificada no go.mod e o comparará com o valor hash registrado no go.sum. Se os valores hash não corresponderem, indica que as versões de dependência são diferentes e a construção será recusada. Quando isso ocorre, tanto a dependência local quanto o arquivo go.sum podem ter sido modificados. Mas como o go.sum é registrado após consulta ao GOSUMDB, tende-se a confiar mais no arquivo go.sum.
Módulos Privados
A maioria das ferramentas do Go Mod é voltada para projetos de código aberto, mas o Go também oferece suporte a módulos privados. Para projetos privados, geralmente é necessário configurar as seguintes variáveis de ambiente para lidar com módulos privados:
GOPROXY: conjunto de servidores proxy de dependênciasGOPRIVATE: lista de padrões genéricos de prefixos de caminho de módulo para módulos privados. Se o nome do módulo corresponder às regras, indica que o módulo é privado. O comportamento específico é consistente com GONOPROXY e GONOSUMDB.GONOPROXY: lista de padrões genéricos de prefixos de caminho de módulo que não são baixados do proxy. Se corresponder, ao baixar módulos, não usará GOPROXY, tentando baixar diretamente do sistema de controle de versão.GONOSUMDB: lista de padrões genéricos de prefixos de caminho de módulo que não passam pela verificação pública do GOSUMDB. Se corresponder, ao verificar módulos, não usará o banco de dados público de checksum.GOINSECURE: lista de padrões genéricos de prefixos de caminho de módulo que podem ser recuperados via HTTP e outros protocolos inseguros.
Workspaces
Como mencionado anteriormente, o arquivo go.mod suporta a diretiva replace, o que nos permite usar temporariamente modificações locais que ainda não foram lançadas:
replace (
github.com/246859/hello v1.0.1 => ./hello
)Ao compilar, o Go usará o módulo hello local, removendo-o após lançar uma nova versão no futuro.
Mas usar a diretiva replace modificará o conteúdo do arquivo go.mod, e essa modificação pode ser enviada acidentalmente ao repositório remoto. Isso não é desejável, pois o target especificado pela diretiva replace é um caminho de arquivo, não uma URL de rede. Um caminho que funciona nesta máquina pode não funcionar em outra, e caminhos de arquivo também são um grande problema para compatibilidade entre plataformas. Para resolver esses problemas, surgiram os workspaces.
Workspace é uma nova solução para gerenciamento de múltiplos módulos introduzida no Go 1.18, visando facilitar melhor o desenvolvimento local de múltiplos módulos. Abaixo, explicaremos através de um exemplo.
Repositório de exemplo: 246859/work: go work example (github.com)
Exemplo
Primeiro, há dois módulos go independentes no projeto: auth e user:
$ ls -1
LICENSE
README.md
auth
go.work
userO módulo auth depende da estrutura User do módulo user. O conteúdo é o seguinte:
package auth
import (
"errors"
"github.com/246859/work/user"
)
// Verify user credentials if is ok
func Verify(user user.User) (bool, error) {
password, err := query(user.Name)
if err != nil {
return false, err
}
if password != user.Password {
return false, errors.New("authentication failed")
}
return true, nil
}
func query(username string) (string, error) {
if username == "jack" {
return "jack123456", nil
}
return "", errors.New("user not found")
}O conteúdo do módulo user é o seguinte:
package user
type User struct {
Name string
Password string
Age int
}Neste projeto, podemos escrever o arquivo go.work assim:
go 1.22
use (
./auth
./user
)Seu conteúdo é muito fácil de entender: usa a diretiva use para especificar quais módulos participam da compilação. Em seguida, execute o código no módulo auth:
// auth/example/main.go
package main
import (
"fmt"
"github.com/246859/work/auth"
"github.com/246859/work/user"
)
func main() {
ok, err := auth.Verify(user.User{Name: "jack", Password: "jack123456"})
if err != nil {
panic(err)
}
fmt.Printf("%v", ok)
}Execute o seguinte comando. Pelo resultado, sabemos que o módulo foi importado com sucesso:
$ go run ./auth/example
trueEm versões anteriores, para esses dois módulos independentes, se o módulo auth quisesse usar o código do módulo user, havia apenas duas opções:
- Enviar as modificações do módulo user para o repositório remoto, lançar uma nova versão e modificar o arquivo
go.modpara a versão especificada - Modificar o arquivo
go.modpara redirecionar a dependência para o arquivo local
Ambos os métodos requerem modificar o arquivo go.mod. A existência do workspace é justamente para permitir importar outros módulos sem modificar o arquivo go.mod. No entanto, é importante entender que o arquivo go.work é usado apenas durante o desenvolvimento. Sua existência é apenas para facilitar o desenvolvimento local, não para gerenciamento de dependências. Ele apenas permite que você pule temporariamente o processo de envio e lançamento, permitindo que você use imediatamente as novas modificações do módulo user sem esperar. Quando o módulo user for testado, ainda será necessário lançar uma nova versão, e o módulo auth ainda precisará modificar o arquivo go.mod para referenciar a versão mais recente (este processo pode ser concluído com o comando go work sync). Portanto, no desenvolvimento normal em Go, go.work também não deve ser enviado para o VCS (o go.work no repositório de exemplo é apenas para demonstração), pois seu conteúdo depende de arquivos locais e sua funcionalidade é limitada ao desenvolvimento local.
Comandos
Abaixo estão alguns comandos de workspace:
| Comando | Descrição |
|---|---|
| edit | Edita go.work |
| init | Inicializa um novo workspace |
| sync | Sincroniza dependências de módulos do workspace |
| use | Adiciona um novo módulo ao go.work |
| vendor | Copia dependências no formato vendor |
Consulte go work cmd para mais informações sobre comandos.
Diretivas
O conteúdo do arquivo go.work é muito simples, com apenas três diretivas:
go: especifica a versão do gouse: especifica os módulos a serem usadosreplace: especifica módulos de substituição
Exceto pela diretiva use, as outras duas são basicamente equivalentes às diretivas em go.mod. A diferença é que a diretiva replace em go.work afetará todos os módulos. Um go.work completo é mostrado abaixo:
go 1.22
use(
./auth
./user
)
replace github.com/246859/hello v1.0.0 => /home/jack/code/hello