Go: comandos CLI, parte 2

Dando sequência do artigo anterior, veremos mais detalhes de outros comandos da ferramenta CLI.

go env [-json] [-u] [-w] [var ...]

O comando env serve para descrever informações importantes da versão de Go em execução, como por exemplo variáveis de ambiente, informações de sistema operacional, entre muitas outras.

O comando permite algumas opções:

-json define o formato de saída das informações. Por padrão, as informações são exibidas no formato shell, como uma série de variáveis de ambiente, por exemplo. Usando essa opção, os valores são escritos no formato JSON, chave e valor.

As opções -u e -w são as mais interessantes deste comando: servem para definir ou desfazer valores das variáveis de ambiente usadas pela linguagem. Para explicar melhor, cabe aqui um exemplo.

Vamos tomar por exemplo a variável GOINSECURE (introduzida na versão 1.14, serve para indicar domínios onde deve-se ignorar a ausência e validade de certificados HTTPS no ato de obter módulos do mundo externo). Por padrão, na versão 1.15, ela vem definida da seguinte forma:

GOINSECURE=""

Caso você quiser inserir um domínio a ser incluído nessa regra, você pode usar a opção -w, que vai inserir o valor desejado conforme o exemplo:

# A sintaxe deve ser VARIAVEL=valor
go env -w GOINSECURE="site.com"

# Agora, go env vai exibir GOINSECURE="site.com"

Podemos passar uma lista de variáveis para alterar o valor.

Já a opção -u serve para devolver à variável editada por -w seu valor original:

# A sintaxe aqui deve ser só VARIAVEL
go env -u GOINSECURE

# Agora, go env vai exibir o valor original, GOINSECURE=""

Lembrando que -u só funciona para variáveis editadas por -w 😉

go fix [packages]

Este é um comando muito importante quando desejamos fazer atualizações de versão com segurança de que mudanças na interface de packages não serão quebradas. O fix identifica no pacote passado o uso de APIs antigas da linguagem e as atualiza para versões novas.

O comando oferece algumas opções extras, mas que para serem usadas, devemos usar o comando fix através do comando tool, na forma go tool fix. Dessa forma, a sintaxe do comando pode ser lida da seguinte forma:

go tool fix [-diff] [-r fixname,...] [-force fixname,...] [path ...]

A opção -diff exibe as alterações a fazer sem fazê-las de fato. Uma excelente forma de verificar antes se há mudanças e quais serão feitas!

A opção -r aqui não se trata de recursividade, mas sim de qual grupo de rescritas deverão ser feitas. O comando go tool fix -help exibe a lista de opções e, por padrão, todos os grupos são utilizados, mas podemos passar somente um ou mais grupos se desejamos uma ação mais específica:

Já a opção -force é como sugere: força a aplicação de fix, mesmo se o código já foi atualizado.

Um detalhe importante: assim como muitas ferramentas, o comando não oferece nenhuma forma de backup. Ou seja, use um controle de versão (git, svn, o que preferir) antes de rodar o comando.

go fmt [-n] [-x] [packages]

O comando fmt executa uma análise e correção de sintaxe padrão da linguagem. Ao ser executado, o comando gofmt -l -w é disparado nos pacotes passados no comando, onde -l orienta a escrever o nome dos pacotes identificados para alteração e -w orienta a sobrescrever o arquivo quando encontrado. Ou seja, o comando é como um atalho para identificação e aplicação de outra ferramenta, gofmt, que é a real ferramenta padrão de formatação.

A opção -n serve para exibir quais mudanças poderão ser feitas, sem aplicá-las.

Já a opção -x escreve os comandos conforme são executados.

Há ainda uma terceira opção, -mod, que funciona nas mesmas regras usadas no comando go build só que aplicadas no contexto de fmt: define qual o tipo de módulo onde se aplicar, readonly ou vendor.

go generate [-run regexp] [-n] [-v] [-x] [build flags] [file.go... | packages]

O comando generate serve para executar comandos descritos nas diretivas contidas dentro do seu código Go. Por diretivas, estamos aqui falando de instruções contidas no código, na forma //go:generate comando argumento.

Esses comandos podem executar quaisquer tarefas, mas no geral, são usados para alterar ou criar arquivos de código Go. O generate faz uma análise estática dos arquivos procurando por essas diretivas e, ao encontrá-las, as executa. Lembrando que o comando não é executado de forma automática por nenhum outro, como build por exemplo; logo, se seu código tem essas diretivas, este comando deve ser sempre executado de forma explícita. Os comandos a serem executados pelo generate podem ser chamadas à binários que podem estar declarados em $PATH, por caminho completo ou um atalho (ou alias).

Um detalhe importante: durante a execução, o comando generate pode definir algumas variáveis de ambiente (daquela lista do go env, lembra?), como $GOARCH, $GOOS entre outras, necessárias à sua execução.

Pode-se também usar a sintaxe //go:generate -command binário argumentos, onde -command é como uma flag indicando que a string binário é um comando seguido dos argumentos. Isso permite a criação de um atalho, que poderá ser usado em outras diretivas. Funciona assim: primeiro, usamos uma diretiva como //go:generate -command xablau go tool fix, por exemplo. Com essa diretiva declarada, podemos usar então a chamada à xablau através da diretiva //go:generate -command xablau ./daora.go, como um atalho.

Um detalhe bacana: se a operação de uma diretiva sobre um dado package retorna um erro, todas as execuções desse package são puladas, e o comando parte para o próximo package, se houver mais de um package declarado como argumento.

Dado todas essas informações, vale lembrar que o comando tem uma única opção, -run. Por padrão, a opção assume uma string vazia (-run=""); mas, se passamos algum valor, esse deve ser uma expressão regular para identificar algum padrão de arquivos a servirem como objeto da execução. Há outras opções aceitas pelo comando, como -v, -n e -x, que funcionam da mesma forma como são declaradas nos demais comandos:

Há também a opção de usar as mesmas opções do comando build.

go get [-d] [-f] [-t] [-u] [-v] [-fix] [-insecure] [build flags] [packages]

O comando get serve para fazer o download de packages com base em seus nomes completos de importação, e também suas dependências, assim como o go install. Este comando possui algumas opções próprias, que valem a pena ser entendidas:

A opção -d instrui o comando a somente baixar os packages, sem instalá-los.

A opção -u diz para o comando usar a rede para atualizar os packages e suas dependências. Nota-se que por padrão o get usa a rede para buscar packages faltando, mas não busca atualizar os que já tem. Logo, esta opção é bem poderosa e deve ser usada com ponderação.

A opção -f só funciona em conjunto com a opção -u, forçando get -u a não verificar se os packages envolvidos na ação vieram de fato da origem descrita no seu caminho de import. Isso permite pegar referências locais ao invés de dados vindos do mundo externo, se assim desejarmos.

A opção -fix aplica go tool fix nos packages baixados antes de resolver suas dependências ou de seguir para o build.

A opção -insecure instrui o comando a ignorar a ausência e/ou invalidade de HTTPS nas origens do que você está baixando, semelhante à GOINSECURE de que falamos antes.

A opção -t instrui para que, além dos packages serem baixados, que os packages necessários para os testes do que foi baixado sejam baixados junto.

A opção -v habilita a verbosidade do comando, escrevendo na saída o que está sendo executado para o download.

Vale lembrar também que get aceita as mesmas opções do comando build.

Quando executado para baixar um novo package, get cria o diretório de download em GOPATH/src/<import-path>, usando sempre a primeira entrada presente em GOPATH, se tiver mais de uma. Já quando get é usado para conferir ou atualizar um package, get procura na dependência um branch ou tag cujo nome confere com a versão de Go em execução. Se não encontrar, o branch principal do repositório é utilizado. Essa pode ser uma estratégia interessante para libs que desejam fazer experimentos usando go2 por exemplo, deixando uma branch com esse nome para ser identificada na execução de get, sem quebrar o que já estiver em main por exemplo. Um outro detalhe é que se o package a ser baixado usar algum submódulo de git (uma forma de vincular repositórios externos ao projeto como dependências diretas), esses submódulos também serão baixados.

Há mais informações relevantes sobre get. Por exemplo, o comando nunca confere nem atualiza código localizado dentro da pasta vendor. Além disso, se a execução está no modo module-aware, o comportamento de get e suas opções pode mudar. Como o uso de GOPATH é mais antigo e considerado legado, é importante conhecer as diferenças de execução do comando dependendo de seu contexto de execução.

Dentro do contexto de module-aware, a primeira ação de get é decidir quais dependências serão adicionadas. Para isso, o comando observa cada package e busca por primeiro as informações de tags mais recentes. Se não tiver tag nenhuma no repositório do package, o comando vai atrás do mais recente commit. Claro, tudo isso se a versão de módulo não for definida logo de início. Toda esse mecanismo de escolha de versão pode ser suprimido se colocamos um @versão no final do nome do package (go get go.uber.org/zap@v1.15.0, por exemplo), ou ter somente parte da tag (go get -u go.uber.org/zap@v1, por exemplo), fazendo assim que a última versão cuja tag começa com v1 seja instalada (essa sintaxe é chamada de Module Queries). A informação de versão de módulo pode também usar o hash de commit, nome de branch, no caso de packages hospedados em algum controle de versão, como GitHub por exemplo. Claro, desde que o sufixo (tag ou branch) não tenha conflito com a sintaxe de Module Queries.

Há muitas outras diferenças a se explorar entre contexto de GOPATH e de module-aware, que por si só valem todo um texto individual. Se você tem curiosidade, todas essas informações podem ser lidas ao executar o comando go help module-get.

go install [-i] [build flags] [packages]

O comando install compila e instala os packages nomeados pelo caminho de import.

A opção -i indica para o comando instalar também as dependências dos packages a instalar.

Além dessa opção, o comando também aceita as mesmas opções do comando build.


No próximo artigo, fecharei com os últimos comandos: list, mod, run, test, tool, version e vet.

Referências

Davi Marcondes Moreira avatar
Sobre Davi Marcondes Moreira
Desenvolvedor de software, palestrante, evangelista de trabalho remoto e home office, amante de MTB/XCO e entusiasta de café. Ele/Dele.