Go: cmd/go: adiciona suporte à versão do pacote à cadeia de ferramentas Go

Criado em 7 mar. 2018  ·  242Comentários  ·  Fonte: golang/go

proposta: adicionar suporte à versão do pacote à cadeia de ferramentas Go

Já passou da hora de adicionar versões ao vocabulário de trabalho dos desenvolvedores Go e de nossas ferramentas.
A proposta vinculada descreve uma maneira de fazer isso. Veja especialmente a seção Racional para uma discussão de alternativas.

Esta edição do GitHub é para discussão sobre a substância da proposta.

Outras referências:

Proposal Proposal-Accepted modules

Comentários muito úteis

Esta proposta está aberta com discussões ativas há mais de dois meses: @rsc e @spf13 conduziram sessões de feedback e reuniram informações valiosas da comunidade que resultaram em revisões da proposta. @rsc também realizou reuniões semanais com @sdboyer para obter mais feedback. Houve feedback valioso fornecido sobre a proposta que resultou em revisões adicionais. Cada vez mais, esse feedback é sobre a implementação que acompanha e não sobre a proposta. Após uma análise considerável, sentimos que é hora de aceitar esta proposta e permitir que o amplo ecossistema de implementadores de ferramentas da Go comece a fazer ajustes críticos para que nossa base de usuários possa ter a melhor experiência possível.

Houve duas objeções a esta proposta que sentimos que devemos falar:

  1. A proposta exigirá que as pessoas mudem algumas de suas práticas em relação ao uso e liberação de bibliotecas.
  2. A proposta não oferece uma solução técnica para todos os possíveis cenários que possam surgir envolvendo incompatibilidades.

Estes são precisos em sua observação, mas funcionam como pretendido. Autores e usuários de código _will_ terão que mudar algumas de suas práticas em relação ao uso e liberação de bibliotecas, assim como os desenvolvedores se adaptaram a outros detalhes do Go, como a execução do gofmt. Mudar as melhores práticas às vezes é a solução certa. Da mesma forma, vgo não precisa lidar com todas as situações possíveis envolvendo incompatibilidades. Como Russ apontou em sua recente palestra na Gophercon Singapore , a única solução permanente para a incompatibilidade é trabalhar em conjunto para corrigir a incompatibilidade e manter o ecossistema de pacotes Go. Soluções temporárias em uma ferramenta como vgo ou dep só precisam funcionar o tempo suficiente para dar aos desenvolvedores tempo para resolver o problema real, e vgo faz esse trabalho bem o suficiente.

Agradecemos todos os comentários e a paixão que você trouxe para esta questão crítica. A proposta foi aceita.

— O Comitê de Revisão da Proposta Go

Todos 242 comentários

perguntas frequentes

Este comentário de problema responde às perguntas mais frequentes, seja da discussão abaixo ou de outras discussões. Outras questões da discussão estão no comentário da próxima edição.

Por que a proposta não é “usar Dep ”?

No início da jornada que levou a esta proposta, quase dois anos atrás, todos acreditávamos que a resposta seria seguir a abordagem de versionamento de pacotes exemplificada pelo Ruby's Bundler e depois pelo Rust's Cargo: versões semânticas marcadas, um arquivo de restrição de dependência editado à mão conhecido como manifesto, uma descrição de dependência transitiva gerada por máquina separada conhecida como arquivo de bloqueio, um solucionador de versão para calcular um arquivo de bloqueio que satisfaça o manifesto e repositórios como a unidade de controle de versão. O Dep segue esse plano aproximado quase exatamente e foi originalmente planejado para servir como modelo para a integração do comando go. No entanto, quanto mais eu entendia os detalhes da abordagem do Bundler/Cargo/Dep e o que eles significariam para o Go, especialmente embutido no comando go, e quanto mais eu discutia esses detalhes com outras pessoas da equipe Go, alguns dos detalhes parecia cada vez menos adequado para Go. A proposta ajusta esses detalhes na esperança de entregar um sistema que seja mais fácil para os desenvolvedores entenderem e usarem. Consulte a seção de justificativa da proposta para saber mais sobre os detalhes específicos que desejamos alterar e também a postagem do blog anunciando a proposta .

Por que os principais números de versão devem aparecer nos caminhos de importação?

Para seguir a regra de compatibilidade de importação , o que simplifica drasticamente o resto do sistema. Veja também o post do blog anunciando a proposta , que fala mais sobre a motivação e justificativa para a regra de compatibilidade de importação.

Por que as versões principais v0, v1 são omitidas dos caminhos de importação?

v1 é omitido dos caminhos de importação por dois motivos. Primeiro, muitos desenvolvedores criarão pacotes que nunca farão uma mudança importante quando chegarem à v1, o que é algo que incentivamos desde o início. Não acreditamos que todos esses desenvolvedores devam ser forçados a ter uma v1 explícita quando podem não ter a intenção de lançar a v2. O v1 torna-se apenas ruído. Se esses desenvolvedores eventualmente criarem uma v2, a precisão extra entrará em ação, para distinguir do padrão, v1. Existem bons argumentos sobre a estabilidade visível para colocar a v1 em todos os lugares, e se estivéssemos projetando um sistema do zero, talvez isso o tornasse uma decisão difícil. Mas o peso do código existente inclina a balança fortemente a favor da omissão da v1.

v0 é omitido dos caminhos de importação porque - de acordo com semver - não há garantias de compatibilidade para essas versões. Exigir um elemento v0 explícito faria pouco para garantir a compatibilidade; você teria que dizer v0.1.2 para ser completamente preciso, atualizando todos os caminhos de importação em cada atualização da biblioteca. Isso parece exagero. Em vez disso, esperamos que os desenvolvedores simplesmente olhem a lista de módulos dos quais dependem e sejam adequadamente cautelosos com qualquer versão v0.xy que encontrarem.

Isso tem o efeito de não distinguir v0 de v1 nos caminhos de importação, mas geralmente v0 é uma sequência de alterações que levam a v1, portanto, faz sentido tratar v1 como a etapa final dessa sequência de interrupção, não algo que precise ser distinguido de v0 . Como o @Merovius colocou (https://github.com/golang/go/issues/24301#issuecomment-376213693):

Ao usar v0.x, você aceita que v0.(x+1) pode forçá-lo a corrigir seu código. Por que é um problema se v0.(x+1) é chamado de v1.0?

Finalmente, omitir as versões principais v0 e v1 é obrigatório - não opcional - para que haja um único caminho de importação canônico para cada pacote.

Por que devo criar um novo branch para v2 em vez de continuar trabalhando no master?

Você não precisa criar uma nova ramificação. A postagem dos módulos vgo infelizmente dá essa impressão em sua discussão sobre o layout do repositório "major branch". Mas vgo não se importa com ramos. Ele apenas procura tags e resolve para quais commits específicos eles apontam. Se você desenvolve v1 no master, decide que terminou completamente com v1 e quer começar a fazer commits v2 no master, tudo bem: comece a marcar master com tags v2.xy. Mas observe que alguns de seus usuários continuarão usando a v1 e, ocasionalmente, você pode querer emitir uma pequena correção de bug da v1. Você pode pelo menos querer fazer um fork de uma nova ramificação v1 para esse trabalho no ponto em que começar a usar master para v2.

A seleção de versão mínima não impedirá os desenvolvedores de obter atualizações importantes?

Este é um medo comum, mas eu realmente acho que se algo acontecer o contrário. Citando a seção "Upgrade Speed" de https://research.swtch.com/vgo-mvs :

Dado que a seleção de versão mínima leva a versão mínima permitida de cada dependência, é fácil pensar que isso levaria ao uso de cópias muito antigas de pacotes, o que, por sua vez, poderia levar a bugs desnecessários ou problemas de segurança. Na prática, no entanto, acho que acontecerá o contrário, porque a versão mínima permitida é a máxima de todas as restrições, então a única alavanca de controle disponibilizada para todos os módulos em uma compilação é a capacidade de forçar o uso de uma versão mais recente de uma dependência do que seria usado de outra forma. Espero que os usuários de seleção de versão mínima acabem com programas quase tão atualizados quanto seus amigos usando sistemas mais agressivos como o Cargo.

Por exemplo, suponha que você esteja escrevendo um programa que depende de vários outros módulos, todos os quais dependem de algum módulo muito comum, como gopkg.in/yaml.v2. A compilação do seu programa usará a versão YAML mais recente entre as solicitadas pelo seu módulo e esse punhado de dependências. Mesmo apenas uma dependência consciente pode forçar sua compilação a atualizar muitas outras dependências. Este é o oposto do problema do cliente Kubernetes Go que mencionei anteriormente.

Se alguma coisa, a seleção de versão mínima sofreria o problema oposto, que essa resposta “máximo dos mínimos” serve como uma catraca que força as dependências a avançar muito rapidamente. Mas acho que, na prática, as dependências avançarão na velocidade certa, o que acaba sendo a quantidade certa mais lenta do que Cargo e amigos.

Por "quantidade certa mais lenta" eu estava me referindo à propriedade principal de que as atualizações acontecem apenas quando você as solicita, não quando você não as faz. Isso significa que o código só muda (de maneiras potencialmente inesperadas e importantes) quando você espera que isso aconteça e esteja pronto para testá-lo, depurá-lo e assim por diante.

Veja também a resposta https://github.com/golang/go/issues/24301#issuecomment -375992900 por @Merovius.

Se $GOPATH estiver obsoleto, onde o código baixado fica?

O código que você verifica, trabalha e modifica pode ser armazenado em qualquer lugar em seu sistema de arquivos, assim como acontece com praticamente todas as outras ferramentas de desenvolvedor.

O Vgo precisa de algum espaço para armazenar o código-fonte baixado e instalar os binários e, para isso, ainda usa $ GOPATH, que a partir do Go 1.9 é padronizado para $ HOME/go. Portanto, os desenvolvedores nunca precisarão definir $GOPATH, a menos que desejem que esses arquivos estejam em um diretório diferente. Para alterar apenas o local de instalação do binário, eles podem definir $GOBIN (como sempre).

Por que você está introduzindo o comentário // import ?

Não estivessem. Essa era uma convenção pré-existente . O objetivo desse exemplo no tour foi mostrar como go.mod pode deduzir os caminhos corretos do módulo de comentários de importação, se eles existirem. Uma vez que todos os projetos usam arquivos go.mod , os comentários de importação serão completamente redundantes e provavelmente obsoletos.

Resumo da discussão (última atualização em 25/04/2017)

Este comentário de questão contém um resumo da discussão abaixo.

Como podemos lidar com a migração?

[ https://github.com/golang/go/issues/24301#issuecomment -374739116 por @ChrisHines.]

Resposta https://github.com/golang/go/issues/24301#issuecomment -377529520 por @rsc. A proposta original assume que a migração é feita por autores movendo-se para subdiretórios quando a compatibilidade é importante para eles, mas é claro que essa motivação está errada. A compatibilidade é mais importante para os usuários, que têm pouca influência sobre a mudança de autores. E isso não ajuda as versões mais antigas. O comentário vinculado, agora também #25069, propõe uma alteração mínima no antigo "go build" para poder consumir e construir código com reconhecimento de módulo.

Como podemos lidar com registros singleton?

[ https://github.com/golang/go/issues/24301#issuecomment -374791885 por @jimmyfrasche.]

Resposta https://github.com/golang/go/issues/24301#issuecomment -377527249 por @rsc. As colisões de registro singleton (como http.Handle do mesmo caminho) entre módulos completamente diferentes não são afetadas pela proposta. Para colisões entre diferentes versões principais de um único módulo, os autores podem escrever as diferentes versões principais para coordenar, geralmente fazendo v1 chamar v2 e, em seguida, usar um ciclo de requisitos para garantir que v2 não seja usado com v1 mais antiga que não Não sei sobre a coordenação.

Como devemos instalar um comando versionado?

[ https://github.com/golang/go/issues/24301#issuecomment -375106068 por @leonklingele.]

Resposta https://github.com/golang/go/issues/24301#issuecomment -377417565 por @rsc. Resumindo, use go get. Ainda usamos $GOPATH/bin para o local de instalação. Lembre-se de que $GOPATH agora tem como padrão $HOME/go, então os comandos terminarão em $HOME/go/bin, e $GOBIN pode substituir isso.

Por que v0, v1 são omitidos nos caminhos de importação? Por que os outros devem aparecer? Por que v0, v1 nunca deve aparecer?

[ https://github.com/golang/go/issues/24301#issuecomment -374818326 por @justinian.]
[ https://github.com/golang/go/issues/24301#issuecomment -374831822 por @jayschwa.]
[ https://github.com/golang/go/issues/24301#issuecomment -375437150 por @mrkanister.]
[ https://github.com/golang/go/issues/24301#issuecomment -376093912 por @mrkanister.]
[ https://github.com/golang/go/issues/24301#issuecomment -376135447 por @kaikuehne.]
[ https://github.com/golang/go/issues/24301#issuecomment -376141888 por @kaikuehne.]
[ https://github.com/golang/go/issues/24301#issuecomment -376213693 por @Merovius.]
[ https://github.com/golang/go/issues/24301#issuecomment -376247926 por @kaikuehne.]

Adicionado ao FAQ acima.

Por que os arquivos zip são mencionados na proposta?

[ https://github.com/golang/go/issues/24301#issuecomment -374839409 por @nightlyone.]

O ecossistema se beneficiará com a definição de um formato concreto de intercâmbio. Isso habilitará proxies e outras ferramentas. Ao mesmo tempo, estamos abandonando o uso direto do controle de versão (veja a justificativa no início deste post ). Ambos motivam a descrição do formato específico. A maioria dos desenvolvedores não precisará pensar em arquivos zip; nenhum desenvolvedor precisará olhar dentro deles, a menos que estejam construindo algo como godoc.org.

Veja também #24057 sobre zip vs tar.

Colocar versões principais em caminhos de importação não viola DRY?

[ https://github.com/golang/go/issues/24301#issuecomment -374831822 por @jayschwa.]

Não, porque a semântica de uma importação deve ser compreensível sem referência ao arquivo go.mod. O arquivo go.mod está apenas especificando detalhes mais sutis. Veja a segunda metade da seção de versões de importação semântica da proposta , começando pela cotação em bloco.

Além disso, se você SECA demais, acaba com sistemas frágeis. A redundância pode ser uma coisa boa. Portanto, "violar o DRY" - isto é, limitar-se a repetir-se - nem sempre é ruim. Por exemplo, colocamos a cláusula package em cada arquivo .go no diretório, não apenas em um. Isso detectou erros honestos no início e depois se transformou em uma maneira fácil de distinguir pacotes de teste externos (pacote x vs pacote x_test). Há um equilíbrio a ser atingido.

Qual fuso horário é usado para o carimbo de data/hora em pseudo-versões?

[ https://github.com/golang/go/issues/24301#issuecomment -374882685 por @tpng.]

UTC. Observe também que você nunca precisa digitar uma pseudo-versão. Você pode digitar um hash de confirmação do git (ou prefixo de hash) e o vgo calculará e substituirá a pseudo-versão apropriada.

O vgo abordará dependências não-Go, como C ou buffers de protocolo? Código gerado?

[ https://github.com/golang/go/issues/24301#issuecomment -374907338 por @AlexRouSg.]
[ https://github.com/golang/go/issues/24301#issuecomment -376606788 por @stevvooe.]
[ https://github.com/golang/go/issues/24301#issuecomment -377186949 por @nim-nim.]

O desenvolvimento Non-Go continua sendo um não-objetivo do comando go , então não haverá suporte para gerenciamento de bibliotecas C e tal, nem haverá suporte explícito para buffers de protocolo.

Dito isso, certamente entendemos que usar buffers de protocolo com Go é muito difícil e gostaríamos de ver isso abordado separadamente.

Quanto ao código gerado de forma mais geral, um sistema de compilação real entre linguagens é a resposta, especificamente porque não queremos que todos os usuários precisem ter os geradores corretos instalados. Melhor para o autor rodar os geradores e conferir o resultado.

A seleção de versão mínima não impedirá os desenvolvedores de obter atualizações importantes?

[ https://github.com/golang/go/issues/24301#issuecomment -375090551 por @TocarIP.]
[ https://github.com/golang/go/issues/24301#issuecomment -375985244 por @nim-nim.]
[ https://github.com/golang/go/issues/24301#issuecomment -375992900 por @Merovius.]

Adicionado ao FAQ.

### Posso usar master para desenvolver v1 e depois reutilizá-lo para desenvolver v2?

[ https://github.com/golang/go/issues/24301#issuecomment -375248753 por @mrkanister.]
[ https://github.com/golang/go/issues/24301#issuecomment -375989173 por @aarondl.]

sim. Adicionado ao FAQ.

Qual é o cronograma para isso?

[ https://github.com/golang/go/issues/24301#issuecomment -375415904 por @flibustenet.]

Resposta em https://github.com/golang/go/issues/24301#issuecomment -377413777 por @rsc. Em suma, o objetivo é conseguir uma "prévia de tecnologia" no Go 1.11; o trabalho pode continuar algumas semanas após o congelamento, mas não mais. Provavelmente não envie PRs adicionando go.mod a todas as bibliotecas que você encontrar até que a proposta seja marcada como aceita e a cópia de desenvolvimento do cmd/go tenha sido atualizada.

Como posso fazer uma alteração de segurança incompatível com versões anteriores?

[ https://github.com/golang/go/issues/24301#issuecomment -376236546 por @buro9.]

Resposta em https://github.com/golang/go/issues/24301#issuecomment -377415652 por @rsc. Resumindo, as diretrizes de compatibilidade do Go 1 permitem alterações de quebra por motivos de segurança para evitar o aumento da versão principal, mas é sempre melhor fazê-lo de uma maneira que mantenha o código existente funcionando o máximo possível. Por exemplo, não remova uma função. Em vez disso, torne a função panic ou log.Fatal somente se chamada incorretamente.

Se um repositório contém módulos diferentes em subdiretórios (digamos, v2, v3, v4), o vgo pode misturar e combinar diferentes commits?

[ https://github.com/golang/go/issues/24301#issuecomment -376266648 por @jimmyfrasche.]
[ https://github.com/golang/go/issues/24301#issuecomment -376270750 por @AlexRouSg.]

sim. Ele trata cada tag de versão como correspondendo apenas a uma subárvore do repositório geral e pode usar uma tag diferente (e, portanto, um commit diferente) para cada decisão.

E se os projetos fizerem uso indevido de sempre? Devemos permitir versões secundárias nos caminhos de importação?

[ https://github.com/golang/go/issues/24301#issuecomment -376640804 por @pbx0.]
[ https://github.com/golang/go/issues/24301#issuecomment -376645212 por @powerman.]
[ https://github.com/golang/go/issues/24301#issuecomment -376650153 por @pbx0.]
[ https://github.com/golang/go/issues/24301#issuecomment -376660236 por @powerman.]

Como observa @powerman , definitivamente precisamos fornecer um verificador de consistência de API para que os projetos pelo menos possam ser informados quando estão prestes a lançar uma mudança obviamente importante.

Você pode determinar se tem mais de um pacote em uma compilação?

[ https://github.com/golang/go/issues/24301#issuecomment -376640804 por @pbx0.]

A coisa mais fácil a fazer seria usar goversion -m no binário resultante. Devemos fazer uma opção go para mostrar a mesma coisa sem construir o binário.

Preocupações sobre a dependência do vgo em proxy versus fornecedor, especialmente código aberto versus empresa.

[ https://github.com/golang/go/issues/24301#issuecomment -376925845 por @joeshaw.]
[ https://github.com/golang/go/issues/24301#issuecomment -376936614 por @kardianos.]
[ https://github.com/golang/go/issues/24301#issuecomment -376947621 por @Merovius.]
[ https://github.com/golang/go/issues/24301#issuecomment -376979054 por @joeshaw.]
[ https://github.com/golang/go/issues/24301#issuecomment -376988873 por @jamiethermo.]
[ https://github.com/golang/go/issues/24301#issuecomment -377134575 por @Merovius.]

Resposta: [ https://github.com/golang/go/issues/24301#issuecomment -377411175 por @rsc.] Proxy e fornecedor serão suportados. O proxy é muito importante para a empresa e o fornecedor é muito importante para o código aberto. Também queremos construir uma rede de espelhos confiável, mas apenas quando o vgo se tornar ativo.

Preocupações sobre protobuild dependendo da semântica do GOPATH.

[ https://github.com/golang/go/issues/24301#issuecomment -377601170 por @stevvooe.]

Resposta [ https://github.com/golang/go/issues/24301#issuecomment -377602765 by @rsc] solicitou mais detalhes em um novo problema, mas esse problema não parece ter sido arquivado.

Sugestão para adicionar tag especial vgo-v1-lock .

[ https://github.com/golang/go/issues/24301#issuecomment -377662150 por @kybin.]

Parece atraente no começo, mas leva a casos especiais que provavelmente não valem a pena. Resposta completa em https://github.com/golang/go/issues/24301#issuecomment -384344659.

Como corrigir uma dependência profunda sem vender?

[ https://github.com/golang/go/issues/24301#issuecomment -378255833 por @chirino.]

Resposta [ https://github.com/golang/go/issues/24301#issuecomment -378261916 por @kardianos.] Usando uma diretiva de substituição.

O que faremos sobre a mudança de nomes de módulos?

[ https://github.com/golang/go/issues/24301#issuecomment -379020339 por @jimmyfrasche.]

Resposta [ https://github.com/golang/go/issues/24301#issuecomment -379307176 por @rsc.]
Esses são problemas reais e pré-existentes que a proposta vgo não tenta abordar diretamente, mas claramente devemos abordá-los eventualmente. A resposta para o desaparecimento do código é ter proxies de cache (espelhos) junto com um motivo para confiar neles; isso é trabalho futuro. (Ou use a venda no projeto de nível superior, se preferir.) A resposta para a movimentação de código é adicionar um conceito explícito de redirecionamento de módulo ou pacote, assim como os aliases de tipo são redirecionamentos de tipo; isso também é trabalho futuro.

Que tal se conectar a servidores Git locais?

[ https://github.com/golang/go/issues/24301#issuecomment -383168012 por @korya.]

O acesso direto ao git foi adicionado de volta ao plano. Veja #24915.

E quanto aos pacotes somente binários?

[ https://github.com/golang/go/issues/24301#issuecomment -382793364 por @sdwarwick.]

Pacotes somente binários só foram suportados na circunstância limitada de algum tipo de instalação fora de banda no GOPATH/pkg. Go get nunca suportou buscar e instalar um pacote somente binário, e continuará a não suportar isso. Um pacote somente binário funciona apenas com um compilador específico e uma cópia específica das dependências, o que limita severamente o quão bem ele pode ser suportado. A resposta certa é quase sempre usar o código-fonte.

Devemos usar a sintaxe path@version no arquivo go.mod?

[ https://github.com/golang/go/issues/24301#issuecomment -382791513 por @sdwarwick.]

Este é o nº 24119. Parecia uma boa ideia no início, mas, depois de cuidadosa consideração, não.

.

.

Alterar https://golang.org/cl/101678 menciona este problema: design: add 24301-versioned-go

Esta proposta é impressionante e eu gosto de quase tudo nela. No entanto, postei a seguinte preocupação na lista de discussão, mas nunca recebi nenhuma resposta. Enquanto isso, vi esse problema levantado por outros no canal de folga Gophers para vgo e também não vi uma resposta satisfatória lá.

De: https://groups.google.com/d/msg/golang-dev/Plc42fslQEk/rlfeNlazAgAJ

Estou mais preocupado com o caminho da migração entre um mundo pré-vgo e um mundo vgo indo mal. Acho que corremos o risco de causar um grande sofrimento à comunidade Go se não houver um caminho de migração tranquilo. Claramente, a migração não pode ser atômica em toda a comunidade, mas se eu entendi tudo o que você escreveu sobre o vgo até agora, pode haver algumas situações em que os pacotes amplamente usados ​​existentes não serão utilizáveis ​​​​pelas ferramentas pré-vgo e pós -vgo ferramentas.

Especificamente, acredito que os pacotes existentes que já possuem lançamentos marcados com versões principais >= 2 não funcionarão com vgo até que tenham um arquivo go.mod e também sejam importados com um caminho de importação aumentado /vN. No entanto, uma vez que essas alterações sejam feitas no repositório, ele interromperá os usos pré-vgo do pacote.

Isso parece criar um tipo diferente de problema de importação de diamantes em que os dois pacotes irmãos no meio do diamante importam um pacote v2+ comum. Estou preocupado que os pacotes irmãos devam adotar caminhos de importação vgo atomicamente para evitar que o pacote na parte superior do diamante fique em um estado não compilável, seja usando ferramentas vgo ou pré-vgo.

Ainda não vi nada que explique o caminho da migração neste cenário.

A proposta afirma:

As compilações com reconhecimento de módulo podem importar pacotes sem reconhecimento de módulo (aqueles fora de uma árvore com um arquivo go.mod) desde que sejam marcados com uma versão semântica v0 ou v1. Eles também podem se referir a qualquer commit específico usando uma “pseudo-versão” no formato v0.0.0-yyyymmddhhmmss-commit. O formulário de pseudo-versão permite fazer referência a commits não marcados, bem como a commits marcados com versões semânticas na v2 ou superior, mas que não seguem a convenção de versionamento de importação semântica.

Mas não vejo uma maneira de pacotes sem reconhecimento de módulo importarem pacotes com reconhecimento de módulo com dependências transitivas >= v2. Isso parece causar a fragmentação do ecossistema de uma forma ainda não abordada. Uma vez que você tenha uma dependência com reconhecimento de módulo que tenha um pacote >= v2 em algum lugar em suas dependências transitivas que parecem forçar todos os seus dependentes a também adotar vgo para manter a construção funcionando.

Atualização: veja também https://github.com/golang/go/issues/24454

O projeto Go encorajou essa convenção desde o início do projeto, mas esta proposta lhe dá mais força: atualizações por usuários de pacotes terão sucesso ou falharão apenas na medida em que os autores de pacotes seguirem a regra de compatibilidade de importação.

Não está claro para mim o que isso significa e como isso muda da situação atual. Parece-me que isso também descreve a situação atual: se eu quebrar essa regra, as atualizações e o go-get falharão. AIUI nada realmente muda e eu sugiro remover pelo menos a menção de "mais dentes". A menos, é claro, que este parágrafo insinue que existem mecanismos adicionais para penalizar/prevenir quebras?

Isso também afetaria coisas como drivers de banco de dados e formatos de imagem que se registram em outro pacote durante a inicialização, já que várias versões principais do mesmo pacote podem acabar fazendo isso. Não está claro para mim quais seriam todas as repercussões disso.

Se a versão principal for v0 ou v1, o elemento de número de versão deve ser omitido; caso contrário, deve ser incluído.

Por que é isso? Na postagem vinculada, vejo apenas a lógica de que é isso que os desenvolvedores fazem atualmente para criar caminhos alternativos quando fazem alterações importantes - mas essa é uma solução alternativa para o fato de que eles não planejam inicialmente as ferramentas que não lidam com versões para eles . Se estamos mudando para uma nova prática, por que não permitir e incentivar (ou até mesmo obrigar) que novos pacotes habilitados para vgo incluam v0 ou v1 ? Parece que os caminhos sem versões são apenas oportunidades para confusão. (Este é um pacote no estilo vgo? Onde está o limite do módulo? etc.)

Eu geralmente gosto da proposta, mas estou preocupado em exigir versões principais nos caminhos de importação:

  1. Viola o princípio DRY quando a versão principal já pode ser conhecida a partir do go.mod . Entender o que acontecerá se houver uma incompatibilidade entre os dois também é difícil de intuir.
  2. A irregularidade de permitir a ausência de v0 e v1 também não é intuitiva.
  3. Alterar todos os caminhos de importação ao atualizar uma dependência parece potencialmente tedioso.

Eu entendo que cenários como o exemplo moauth precisam ser viáveis, mas espero que não à custa de manter as coisas simples para cenários mais comuns.

Primeiro de tudo: trabalho impressionante!

Uma coisa que não está totalmente clara para mim e parece um pouco subespecificada:

Por que há um arquivo zip nesta proposta?

Layout, restrições e vários casos de uso, como quando é criado e como seu ciclo de vida é gerenciado, quais ferramentas precisam de suporte, como ferramentas como linters devem interagir com ele também não são claros, porque não são abordados na proposta.

Portanto, sugiro que você faça referência a uma proposta posterior, ainda não escrita, aqui e remova a palavra zip ou remova toda a parte do texto da proposta, se você planeja não discuti-la no escopo desta proposta.

Discutir isso mais tarde também permite que um público diferente contribua melhor aqui.

Qual fuso horário é usado para o carimbo de data/hora na pseudo-versão (v0.0.0-yyyymmddhhmmss-commit)?

Editar:
Está em UTC conforme indicado em https://research.swtch.com/vgo-module.

@rsc Você abordará dependências C?

Parece que a seleção de versão mínima torna a propagação de alterações ininterruptas muito lenta. Suponha que tenhamos uma biblioteca popular Foo, que é usada pelos projetos A, B e C. Alguém melhora o desempenho do Foo sem alterar a API. Atualmente, receber atualizações é um processo de desativação. Se o projeto A forneceu Foo, mas B e C não, o autor só precisa enviar pr com atualização para dependência de fornecedor para A. Portanto, as contribuições de quebra de API não terão tanto efeito na comunidade e são um pouco desencorajadas em comparação com as atuais situação. Isso é ainda mais problemático para atualizações de segurança. Se algum projeto abandonado/pequeno/não muito ativo (não biblioteca) declarar dependência direta da versão antiga de, por exemplo, x/crypto, todos os usuários desse projeto estarão vulneráveis ​​a falhas no x/crypto até que o projeto seja atualizado, potencialmente para sempre. Atualmente, os usuários desses projetos receberão a versão corrigida mais recente, o que piora a situação de segurança. IIRC houve algumas sugestões de como corrigir isso na discussão da lista de e-mails, mas, pelo que posso dizer, esta proposta não menciona isso.

IIRC houve algumas sugestões de como corrigir [obtendo patches de segurança] na discussão da lista de e-mails, mas, pelo que posso dizer, esta proposta não menciona isso.

Veja a menção de go get -p .

Veja a menção de go get -p.

Eu já vi, mas isso ainda é um mecanismo opt-in.
Eu estava pensando em uma maneira de a biblioteca marcar todas as versões anteriores como inseguras, para forçar o usuário a executar go get -p ou optar explicitamente pela biblioteca insegura.

Se o suporte para go get como o conhecemos hoje for obsoleto e eventualmente removido, qual é a maneira recomendada de buscar e instalar (sem etiqueta) os binários Go? Requer git clone 'ing the project primeiro, seguido por um manual go install para instalar o binário?
Se $GOPATH for obsoleto, onde esses binários serão instalados?

@leonklingele : do meu entendimento, go get não será preterido, pelo contrário.
Ele será aprimorado com recursos de controle de versão automáticos e transparentes. Se um projeto depende de um projeto não marcado, ele apenas pegaria o mestre e o "venderia" nesta versão exata.
Novamente, meu próprio entendimento de ler um pouco sobre vgo. Ainda estou no processo de entendê-lo completamente.

Eu me pergunto como isso afetará o fluxo de trabalho com um repositório Git em geral, também com base nesta frase da proposta:

Se a versão principal for v0 ou v1, o elemento de número de versão deve ser omitido; caso contrário, deve ser incluído.

No momento, parece comum trabalhar no master (para mim, isso inclui ramificações de recursos de curta duração) e marcar um commit com uma nova versão de vez em quando. Sinto que esse fluxo de trabalho fica mais confuso com os módulos Go assim que libero a v2 da minha biblioteca, porque agora tenho uma ramificação master e v2 . Eu esperaria que master fosse o branch atual e v2 um branch de manutenção, mas é exatamente o contrário.

Eu sei que o branch padrão pode ser alterado de master para v2 , mas isso ainda me deixa com a tarefa de atualizar isso toda vez que eu lançar uma nova versão principal. Pessoalmente, eu preferiria ter um ramo master e um v1 , mas não tenho certeza de como exatamente isso se encaixaria na proposta.

Novos lançamentos importantes causam churn. Se você tiver que alterar uma configuração em seu repositório Git (o branch padrão) sempre que fizer uma nova versão, esse é um custo muito menor em comparação com os usuários da sua biblioteca que mudam para a nova versão.

Acho que esse aspecto da proposta define o incentivo certo: incentiva os autores upstream a pensar em como podem fazer mudanças de maneira compatível com versões anteriores, reduzindo a rotatividade geral do ecossistema.

agora eu tenho um master e um branch v2

Em vez disso, você pode criar um subdiretório v2/ no master.

@mrkanister

Eu preferiria ter um master e um branch v1, mas não tenho certeza de como exatamente isso se encaixaria na proposta.

De acordo com o meu entendimento de https://research.swtch.com/vgo-module , o vgo usa tags e não ramificações para identificar as versões. Assim, você pode manter o desenvolvimento no master e branch off v1, desde que as tags apontem para o branch correto e confirmem.

Novos lançamentos importantes causam churn. Se você tiver que alterar uma configuração em seu repositório Git (o branch padrão) sempre que fizer uma nova versão, esse é um custo muito menor em comparação com os usuários da sua biblioteca que mudam para a nova versão.

Este é um estilo de pensamento problemático que eu acho que mordeu Go hard no passado. Para uma pessoa em um projeto, mudar qual branch é o padrão é simples no momento, sim. Mas ir contra as convenções de fluxo de trabalho significa que as pessoas esquecem, especialmente quando trabalham em vários idiomas. E será mais um exemplo peculiar de como o Go faz as coisas de forma totalmente diferente que os recém-chegados precisam aprender. Ir contra as convenções comuns de fluxo de trabalho do programador _não é um custo menor.

Ir contra as convenções comuns de fluxo de trabalho do programador não é um custo menor.

Não seguir o caminho convencional às vezes é a condição necessária para a inovação.

Se eu entendi algumas partes da proposta corretamente, você nunca precisará criar um subdiretório ou um novo branch. Você pode ter apenas um branch master e git marcar seu repositório de 0.0, 1.0, 2.0 e assim por diante , contanto que você atualize seu go.module para o caminho de importação correto para sua biblioteca.

@mrkanister Eu acho que, para dev, você clona seu master (ou qualquer branch dev) e usa a diretiva "replace" (consulte vgo-tour) para apontar para ele. (se eu entendi o que você quis dizer, não tenho certeza).

@rsc Gostaria de pedir que você seja mais preciso sobre o roteiro e o que devemos fazer agora.
Ele seguirá a política Go e congelará o vgo em 3 meses (2 agora)?
Devemos agora seguir com nosso bastão de peregrino pedindo a cada mantenedor de libs para adicionar um arquivo go.mod ou devemos esperar que a proposta seja oficialmente aceita (para ter certeza de que o nome e a sintaxe não serão alterados) ?

As ferramentas @flibustenet não são cobertas pela política 1.0, então tudo pode mudar.

https://golang.org/doc/go1compat

Finalmente, a cadeia de ferramentas Go (compiladores, vinculadores, ferramentas de construção e assim por diante) está em desenvolvimento ativo e pode mudar o comportamento. Isso significa, por exemplo, que scripts que dependem da localização e propriedades das ferramentas podem ser quebrados por uma liberação pontual.

Também da proposta

O plano, sujeito à aprovação da proposta, é liberar o suporte ao módulo no Go 1.11 como um recurso opcional que ainda pode ser alterado. A versão Go 1.11 dará aos usuários a chance de usar módulos “de verdade” e fornecer feedback crítico. Mesmo que os detalhes possam mudar, versões futuras poderão consumir árvores de origem compatíveis com Go 1.11. Por exemplo, o Go 1.12 entenderá como consumir a sintaxe do arquivo go.mod do Go 1.11, mesmo que até então a sintaxe do arquivo ou mesmo o nome do arquivo tenha mudado. Em uma versão posterior (digamos, Go 1.12), declararemos o suporte ao módulo concluído. Em uma versão posterior (digamos, Go 1.13), encerraremos o suporte para go get de não módulos. O suporte para trabalhar no GOPATH continuará indefinidamente.

Obrigado pelo feedback.

@AlexRouSg

De acordo com o meu entendimento de https://research.swtch.com/vgo-module , o vgo usa tags e não ramificações para identificar as versões. Assim, você pode manter o desenvolvimento no master e branch off v1, desde que as tags apontem para o branch correto e confirmem.

Você está correto, isso continuará funcionando como antes (apenas verifique novamente para ter certeza), boa captura!

Com isso fora do caminho, o que eu (e aparentemente outros) não entendo é o raciocínio por trás de não permitir a existência de um pacote v1 . Eu tentei importar um usando /v1 no final da importação e também adicionando isso ao go.mod do pacote que está sendo importado, mas vgo irá procurar uma pasta chamada v1 em vez disso.

@mrkanister
Acho que a principal razão para não permitir v1 ou v0 no caminho de importação é garantir que haja apenas um caminho de importação para cada versão compatível de um pacote.
Usar o caminho de importação simples em vez de /v1 é para facilitar a transição, para que você não precise atualizar todos os caminhos de importação para adicionar /v1 no final.

Oi,

Embora muitos dos pontos da proposta sejam mais do que bem-vindos e ajudem a domar as grandes bases de código Go que surgiram ao longo do tempo, a regra "usar versão mínima" é bastante prejudicial:

  • você quer que seu ecossistema de código progrida. Isso significa que você quer que as pessoas testem e usem novas versões e detectem problemas antes que eles se acumulem.
  • você deseja que novos lançamentos de módulos, que corrijam problemas de segurança, sejam aplicados o mais rápido possível
  • você deseja poder aplicar novas versões de módulo, que corrijam problemas de segurança, o mais rápido possível. Eles nem sempre são marcados em correções de segurança. Se você evitar novos lançamentos, também evitará essas correções
  • mesmo quando uma nova versão não contém correções de segurança, aplicar suas alterações antecipadamente significa que haverá menos alterações a serem verificadas quando a próxima versão que contém correções de segurança for publicada (e a última coisa que você deseja quando essa versão for publicada e você precisar ser rápido é ficar atolado em mudanças intermediárias que você não viu antes).
  • aplicar versões intermediárias só é prejudicial se elas quebrarem a compatibilidade, e não devem quebrar a compatibilidade, e se quebrarem a compatibilidade, é melhor detectá-lo e informar aos autores do módulo antes que eles se tornem um hábito para os próximos lançamentos, você acabará absolutamente necessidade.
  • você não quer que pedaços antigos de código o arrastem para baixo porque eles ainda especificam uma versão de dependência antiga e ninguém encontra tempo para atualizar seu manifesto. Usar a versão mais recente de uma versão principal atende a essa necessidade social em outros ecossistemas de código: forçar os desenvolvedores a testar a versão mais recente e não adiar até que seja tarde demais porque “há coisas mais importantes” (ou seja, mais divertidas) para fazer.

    • enquanto na teoria você pode enviar um número ilimitado de versões de módulo para que cada pedaço de código possa usar o que quiser, na prática assim que você compõe dois módulos que usam o mesmo dep você tem que escolher uma versão para que mais complexo seja o seu software, menos você tolerará várias versões. Então você logo se depara com o velho problema do que fazer com retardatários que retardam todo o comboio. Eu nunca conheci uma cultura humana que administrou esse problema dizendo aos retardatários "você está certo, vá tão devagar quanto quiser, todo mundo vai esperar por você". Pode ser bom e altruísta, mas não é produtivo.

Lutar contra a inércia humana é difícil e doloroso, e estamos lutando porque é necessário para progredir, não porque é agradável. Fazer ferramentas agradáveis ​​que evitam o problema e incitam os humanos a procrastinar um pouco mais não ajuda em nada, apenas acelerará a sedimentação do projeto e o acúmulo de dívidas técnicas. Já existem dezenas de projetos Go no github com a maior parte de seu readme dedicado ao autor implorando a seus usuários para atualizar porque ele fez correções importantes, o padrão para a versão mais antiga generalizará o problema.

Uma boa regra seria "usar a versão mais recente que corresponda à versão principal, não a todos os commits intermediários". Isso seria um compromisso daqui para frente e estabilidade. Ele coloca o projeto original no comando, que conhece melhor a base de código e pode decidir com sensatez quando mudar seus usuários para um novo estado de código.

Minha pergunta não respondida copiada da lista de discussão:

Esperamos que a maioria dos desenvolvedores prefira seguir a convenção usual de “ramificação principal”, na qual diferentes versões principais residem em ramificações diferentes. Nesse caso, o diretório raiz em uma ramificação v2 teria um go.mod indicando v2, assim:

Parece que há subdiretórios e essa convenção de ramificação principal que são suportadas pelo vgo. Na minha experiência anedótica, nenhum repositório segue essa convenção em Go ou em outras linguagens (não consigo pensar em nenhum outro além dos forçados pelo gopkg.in, que parece relativamente não utilizado nos dias de hoje). O branch master é o mais recente e possui tags v2.3.4 em seu histórico. Tags existem para separar tudo (não apenas versões menores). Se for necessário corrigir uma versão antiga, uma ramificação é criada temporariamente a partir da última tag v1, os commits são enviados, uma nova tag é enviada e a ramificação é excluída sumariamente. Não há branch para versões, são apenas branches master/dev/feature atuais + tags de versão. Eu sei que "tudo é uma referência" no Git, mas para outros VCS a distinção pode não ser tão confusa.

Dito isso, testei o fluxo de trabalho descrito acima com vgo (apenas com tags que dizem v2.0.0, v2.0.1 e sem ramificações) e parece funcionar. Então, minha pergunta é: Embora isso funcione agora, é pretendido? Como não parece tão completamente descrito quanto os outros dois fluxos de trabalho no blog, e quero garantir que trabalhar sem um branch v2/v3... não seja uma funcionalidade acidental que desaparecerá, pois, como expliquei acima, nunca vi este (ou outro) fluxo de trabalho descrito no post para ser adotado massivamente por qualquer pessoa (especialmente fora da comunidade Go).

É claro que meu argumento se resume a preferências e anedotas, então eu estaria disposto a fazer alguns repo-scraping para provar isso em todos os idiomas, se necessário. Até agora eu realmente gostei das postagens da proposta e geralmente estou de acordo com as mudanças, continuarei acompanhando e brincando com o vgo.

Obrigado por todos os seus esforços.

Alguém pode esclarecer como o modelo alternativo proposto ao MVS funcionaria para melhorar a cadência de atualização? Porque não está claro para mim. Minha compreensão do modelo alternativo (amplamente usado) é

  • Desenvolvedor cria manifesto artesanal, listando restrições de versão para todas as dependências usadas
  • O desenvolvedor executa o $solver, que cria um arquivo de bloqueio, listando alguns subconjuntos escolhidos de versões de dependência transitiva que satisfazem as restrições especificadas
  • Este arquivo de bloqueio é confirmado e é usado no momento da compilação e instalação para garantir compilações reproduzíveis
  • Quando uma nova versão de uma dependência é lançada e usada, o desenvolvedor potencialmente atualiza o manifesto, executa novamente o solucionador e confirma novamente o novo arquivo de bloqueio

O modelo MVS proposto como eu o entendo é

  • O desenvolvedor gera automaticamente go.mod , com base no conjunto de caminhos de importação no módulo, selecionando a versão mais recente de qualquer dependência transitiva
  • go.mod é confirmado e é usado para obter limites inferiores nas versões no momento da compilação e instalação. MVS garante compilações reproduzíveis
  • Quando uma nova versão de uma dependência é lançada e usada, o desenvolvedor executa vgo get -u , que busca as versões mais recentes de dependências transitivas e substitui go.mod pelos novos limites inferiores. Que então é submetido.

Parece que devo ignorar algo grosseiramente e seria útil se alguém apontasse o quê. Como esse entendimento parece implicar que devido aos arquivos de bloqueio que especificam as versões exatas e aquelas que estão sendo usadas na compilação real, o MVS é melhor para aumentar a cadência de atualização - pois não permite reter versões, em geral.

Claramente estou perdendo alguma coisa (e me sentirei estúpido em cerca de 5m), o que é isso?

@tpng

Usar o caminho de importação simples em vez de /v1 é para facilitar a transição, para que você não precise atualizar todos os caminhos de importação para adicionar /v1 no final.

Na verdade, isso não deveria ser necessário. Deixe-me dar um exemplo:

Um usuário está usando, por exemplo v1.0.0 de uma biblioteca, fixada por um gerenciador de dependências e a tag no repositório upstream. Agora o upstream decide criar um go.mod e também chama o módulo /v1 . Isso deve resultar em um novo commit e uma nova tag (ex. v1.0.1 ). Como vgo nunca tentará atualizar as dependências por conta própria, isso não deve prejudicar nada para o usuário, mas ele pode atualizar conscientemente alterando também o caminho de importação (pr vgo pode fazer isso para ele/ela).

Acho que a principal razão para não permitir v1 ou v0 no caminho de importação é garantir que haja apenas um caminho de importação para cada versão compatível de um pacote.

Sim, acho que realmente posso ver esse ponto para não confundir os novos usuários de uma biblioteca.

Se a versão principal for v0 ou v1, o elemento de número de versão deve ser omitido; caso contrário, deve ser incluído.

Alguém pode explicar o raciocínio por trás disso? O que eu faço, como usuário de uma biblioteca, quando não quero usar a v1 ainda, porque ela introduziu uma mudança de última hora, que ficaria totalmente bem com versionamento semântico (nova versão principal)?

Prefiro omitir apenas a versão anterior à versão 1, que indica um pacote instável/inacabado. A partir da versão 1, quero poder contar com o fato de obter uma versão estável. Omitir a v1 no caminho de importação é confuso, porque você não sabe se está rastreando uma biblioteca em constante mudança ou uma versão estável. Eu acho que isso também não funciona bem com o esquema de versionamento semântico, onde v1 identifica a primeira versão estável, que é usada para distinguir claramente essa versão das versões 0.x.

@kaikuehne

O que eu faço, como usuário de uma biblioteca, quando não quero usar a v1 ainda, porque ela introduziu uma mudança de última hora, que ficaria totalmente bem com versionamento semântico (nova versão principal)?

Pelo que entendi, vgo nunca atualizará as dependências por conta própria, nem mesmo para uma versão de patch, mas deixará isso como uma decisão consciente para você tomar. Então, se você depende da v0.4.5 de uma biblioteca (que tem uma tag para ela), teoricamente você pode continuar usando isso para sempre. Você também poderá fixar a versão manualmente em seu arquivo go.mod .

E se outra dependência que eu uso depender do mesmo pacote, mas da versão v1? Ambos os pacotes são importados usando o mesmo caminho de importação. Não há um conflito ao compilá-los no mesmo binário?

Se exigissemos que a v1 também fizesse parte do caminho de importação, ambos seriam tratados como pacotes diferentes, o que eles são.

@kaikuehne , ele atualizaria para a versão comum mínima que funciona. (no meu entendimento)

@kaikuehne , não entendo seu raciocínio. Você está usando v0, então, presumivelmente, você está bem com alterações de quebra; por que seria um problema se a v1 quebrar, já que você já está usando uma versão que não tem garantia de estabilidade? Além disso, digamos, em vez de ir de v0.1->v1.0 com uma alteração de interrupção, o upstream adicionaria a interrupção à v0.2 e , em seguida, liberaria uma v1.0 (sem interrupção). Isso parece estar dentro das expectativas em torno das versões semânticas, mas equivaleria exatamente à mesma quantidade de trabalho para você (não importa o gerenciador de pacotes usado). Ou seja, eu realmente não entendo como "o autor de repente parou de quebrar sua API" constitui um problema que não é causado pelo uso de uma versão pré-1.0.

Em outras palavras: Ao usar v0.x, você está aceitando que v0.(x+1) pode forçá-lo a corrigir seu código. Por que é um problema se v0.(x+1) é chamado de v1.0?

No ponto de discussão 4: Adotando explicitamente a regra de compatibilidade de importação e "o novo pacote deve ser compatível com o pacote antigo"...

No meu caso, eu tenho um pacote de segurança https://github.com/microcosm-cc/bluemonday e recentemente (no final do ano passado) tive um cenário em que aprendi que uma função pública fundamentalmente não era adequada para o propósito. Então eu removi.

Sob esta proposta, se eu removesse o func, ele aumentaria a versão e o código inseguro/inseguro nunca seria removido.

Para evitar isso, eu provavelmente faria o log.Fatal() dentro da função que desejava remover, puramente para garantir que o código existente não usasse um endpoint inseguro, mas para preservar a compatibilidade.

Dado que nenhum deles é ideal... como prevemos que correções de segurança que exigem que uma função pública seja obsoleta com dificuldade sendo tratadas? (se não surpreender o desenvolvedor com um log de tempo de execução.Fatal()?)

Para aqueles que querem ver o commit para isso: https://github.com/microcosm-cc/bluemonday/commit/a5d7ef6b249a7c01e66856b585a359970f03502c

@Merovius Obrigado pelo esclarecimento ao usar as versões 0.x. Como você disse, não há garantias para se confiar ao usar uma versão anterior a 1.0 e que as versões 0.x são um caso especial em semver. Se entendi corretamente, as regras descritas na verdade não se aplicam às versões 0.x. Minha pergunta é se faria sentido refletir essa distinção também no código - especialmente ao nomear pacotes. Por exemplo, se um pacote importa apenas pacotes sem uma versão, você pode ver rapidamente que ele é construído sobre código instável. Se todas as importações contiverem uma versão v1, você verá que ela usa versões estáveis.

@buro9 A proposta sugere seguir aproximadamente as garantias de compatibilidade go1, que contêm isenções para quebras de API relacionadas à segurança.

@kaikuehne Obrigado, isso esclarece sua preocupação.

Há uma interação de recursos que me preocupa (supondo que eu entenda as coisas corretamente).

Se você tem um módulo M que usa versões reificadas (diretórios vN literais na fonte, não elementos sintéticos de caminho de importação derivados de tags), e você está construindo um programa P que depende de várias versões principais de M de forma transitiva, isso não tem que violar a seleção de versão mínima em alguns cenários?

Ou seja, digamos que P dependa das versões principais 2, 3 e 4 de M. Para cada versão principal de M, há uma versão completa mínima especificada. Como as versões de M compartilham a origem com o propósito expresso de poder fazer coisas como usar de forma transparente a mesma definição de tipo com aliases de tipo, apenas uma cópia de M pode ser incluída para todas as três versões principais em vez de uma cópia por versão principal. Qualquer escolha da versão completa para uma versão principal corrige a escolha da versão completa das outras duas versões principais e pode levar à seleção de uma versão não mínima para uma ou ambas as outras versões principais.

Como vgo lida com isso? Isso pode causar algum problema além de às vezes ser um pouco menor que o mínimo? (Como ser possível construir acidentalmente um conjunto de módulos que não produz solução ou faz com que o solucionador faça um loop?)

@jimmyfrasche

Se você estiver usando diretórios de versões principais, o vgo ainda usará tags e obterá apenas a pasta de versão correspondente dessa tag. por exemplo, você está dependendo das versões 2, 3, 4. vgo verificará a tag v2.nm, v3.nm, v4.nm
E então da tag v2.nm só pegue a pasta v2 e assim por diante. Então, no final, tudo ainda está seguindo as tags.

Fiz uma pergunta neste post da lista de discussão , mas não vi uma resposta concreta. Vou repetir aqui:

o que acontecerá com recursos não-Go, como protobufs ou arquivos c? Desculpe se isso já foi respondido em suas postagens, mas usamos o caminho do fornecedor para distribuir e definir o caminho de importação para arquivos protobuf. Enquanto compilamos os pacotes Go contra a saída protobuf pré-compilada, também temos que considerar o caso de compilar novos pacotes Go a partir de arquivos protobuf dependentes de dependências fornecidas (ou seja, referências rpc.Status de outro arquivo protobuf). Posso fornecer alguns exemplos mais concretos se essa descrição for muito densa.

Especificamente, eu tenho um projeto chamado protobuild que permite mapear a importação do arquivo protobuf para os locais do GOPATH. Não estou vendo como esta proposta irá lidar com a resolução de recursos no GOPATH e como podemos mapear isso em outros espaços de importação do compilador.

Este foi um grande ponto de dor para trabalhar com protobufs para nós e este projeto aliviou muitos desses problemas. Seria uma pena se isso fosse completamente incompatível com a proposta.

Mais uma vez, peço desculpas se há algo que eu perdi.

adorei a proposta. Eu só me preocupo que muitos projetos frequentemente introduzam pequenas mudanças de ruptura entre versões secundárias e reservem solavancos de versão principal apenas para mudanças de ruptura muito grandes. Se pequenas alterações de quebra de versão forem frequentes, surgirão problemas de dependência de diamante, pois viola a suposição de SIV. Gostaria de saber se incluir a versão secundária na declaração de importação ajudaria mais projetos a cumprir o contrato de alterações ininterruptas. Eu acho que as desvantagens são a rotatividade de importação extra e a atualização de versões menores fica mais difícil (e mais explícita).

Isso traz outra pergunta que tenho: é fácil (hoje) com o vgo determinar se você tem mais de 1 versão de um pacote em uma compilação? Embora permitir duas versões do mesmo pacote seja crucial para avançar às vezes, parece que a maioria dos projetos gostaria de evitar isso, a menos que seja temporário devido a possíveis efeitos colaterais imprevistos do init. Ter uma maneira fácil de verificar isso pode ser útil e alguns projetos podem querer aplicá-lo durante o check-in do código.

Eu só me preocupo que muitos projetos frequentemente introduzam pequenas mudanças de ruptura entre versões secundárias e reservem solavancos de versão principal apenas para mudanças de ruptura muito grandes.

Mudança de ruptura é uma mudança de ruptura. Não pode ser pequeno ou grande. Suponho que tais pacotes eventualmente serão rejeitados pela comunidade como muito pouco confiáveis ​​e não seguindo o semver. Mesmo que eles sigam semver e cheguem rapidamente a um grande número maior como 42.xx, eles serão muito inconvenientes de usar. Então, se você quiser fazer muitas mudanças importantes, continue usando o número principal 0. Dessa forma, ele continuará funcionando como o go get atual, com os mesmos problemas. Se você quiser experimentar a API depois de liberar a versão principal não-0 - mova esses experimentos para separar o pacote "experimental" com o maior 0 para sempre e incremente o principal do pacote principal quando você terminar com "pequenas mudanças de ruptura" e, finalmente, obter a próxima API estável.

Incluir menor no caminho de importação viola a ideologia de sempre e não fornece nenhum novo recurso além da inclusão de major.

Embora eu entenda que é isso que os autores devem fazer, dada a semântica de semver, meu medo é que pequenas mudanças entre versões menores sejam um caso comum e tentador para alguém que já passou da versão 1. Em minha experiência anedótica, os autores de pacotes não seguem a semântica exata do semver e muitas vezes as empresas reservam as versões principais dos solavancos para fins de marketing. Então, meu apelo não é sobre o que é ideal, mas sobre se pode ser prático fazer certas mudanças para lidar melhor com a realidade humana confusa que é sempre.

Talvez seja até possível incluir opcionalmente a versão menor para pacotes que não obedecem semver, talvez isso estrague toda a proposta, eu precisaria pensar mais sobre isso. Não estou propondo que esta seja uma maneira melhor, apenas estou interessado em explorar mais as vantagens de incluir opcionalmente ou obrigar a inclusão da versão secundária nas importações.

É provável que seja possível gerar dados melhores aqui a partir do corpus go (e uma ferramenta que procura por mudanças óbvias) para determinar com que frequência o semver é seguido principalmente entre os projetos populares existentes que usam tags semver.

A proposta vgo permite que as principais versões interrompam as alterações da API com facilidade. Ele (por si só) não faz nada para impedi-los.

A proposta vgo permite que uma versão principal se refira a outra versão principal, permitindo a reutilização de código graciosa. Não faz nada para forçar isso.

A proposta vgo MVS permite a atualização para pacotes mais recentes. Não o força a atualizar.


Aqui estão coisas que podemos construir com muito mais facilidade em um mundo vgo:

  1. Ferramentas para capturar alterações de frenagem da API antes de enviar para um host de módulo. Cada versão está em um zip e pode ser comparada sem muitas ferramentas e comandos de vcs diferentes.
  2. Registro de problemas de segurança. Hospede ao lado da hospedagem do módulo para agregar valor. As ferramentas podem consultá-los de forma semelhante a go list manualmente ou automaticamente e ser notificados de problemas filtrados por uma consulta.

vgo torna isso mais fácil por:

  • Restringindo o espaço do problema (trabalhe apenas com arquivos zip, não há necessidade de armazenar o histórico git/hg/svn arbitrário para fazer comparações de API).

    • Definindo o espaço do problema (MVS, definição de versão)

  • Ferramenta da parte de construção.

vgo opera em um princípio principal: um único conjunto de entradas deve sempre construir a mesma coisa. Se estamos confiando em reconstruções aleatórias para capturar atualizações de segurança , estamos fazendo isso errado . Como mantenedor de projetos, alguns projetos Go são executados por anos sem uma reconstrução. Concordo que atualizações de segurança oportunas são importantes: vgo satisfaz a parte de construção dessa necessidade.

Não devemos confundir o que o vgo permite com o que o vgo é.

@pbx0 Não tenho certeza se este é o lugar certo para essa discussão, talvez a lista de emails seja mais adequada. Em qualquer caso, ocorre a quebra de alterações sem alterar o número principal. Mesmo ocasionalmente. Semver tem este caso respondido no FAQ:

Assim que você perceber que quebrou a especificação de versão semântica, corrija o problema e lance uma nova versão secundária que corrija o problema e restaure a compatibilidade com versões anteriores.

Mas eu acho que há um lugar para melhorar aqui. Suponho que deve ser fácil implementar o verificador de API automatizado (talvez já exista um), que pegará todos os pacotes listados no godoc.org e verificará periodicamente se há novas versões e, caso uma nova versão seja detectada, verifique se a nova versão é compatível com a anterior API da versão caso use tags semver e major não foi alterada. Em seguida, tente entrar em contato com o autor em caso de alteração incompatível detectada - talvez problemas de abertura automática no github ou use o email da conta do github. Isso não abrange todos os casos possíveis, mas pode ser muito útil para a comunidade.

Eu acredito firmemente no uso de caminhos para as versões principais do escopo e queria expressar meu apoio, FWIW.

Um ano atrás, eu acreditava que colocar versões em caminhos de importação como esse era feio, indesejável e provavelmente evitável. Mas ao longo do ano passado, entendi quanta clareza e simplicidade eles trazem para o sistema.

Quase vinte anos atrás eu precisava de um VCS para minha startup e tentei cerca de dez diferentes. Eu imediatamente descartei o Perforce porque ele expunha ramificações usando diretórios. Portanto, a ramificação v2 seria apenas o mesmo código, mas em uma pasta v2. Eu odiei essa ideia. Mas depois de executar pilotos com os outros nove, descobri que muitas vezes queria ter as duas versões de uma ramificação no meu sistema de arquivos local, e descobri que o Perforce tornou isso trivialmente fácil, enquanto os outros o tornaram surpreendentemente complicado. O sistema de arquivos é nossa metáfora de contenção. Vamos usá-lo.

Da proposta:

Defina um esquema de URL para buscar módulos Go de proxies, usado tanto para instalar módulos usando nomes de domínio personalizados quanto quando a variável de ambiente $GOPROXY estiver configurada. Este último permite que empresas e indivíduos enviem todas as solicitações de download de módulos por meio de um proxy por segurança, disponibilidade ou outros motivos.

Eu mencionei isso em outro lugar, mas minha maior preocupação com a proposta é que ela parece descartar (ou pelo menos não abordar) um dos maiores pontos fortes da venda hoje: código sempre disponível e compilações perfeitamente reproduzíveis. Eu não acho que o sistema de proxy proposto endereça ou substitui esses pontos fortes.

Um projeto que usa a venda hoje requer apenas a cadeia de ferramentas Go e o Git (ou outro sistema de controle de versão). Existe apenas um único ponto de falha: o repositório git. Uma vez que o código é verificado, ele pode ser construído e reconstruído perfeitamente sem a necessidade de tocar na rede novamente – uma importante consideração de segurança. Para a maioria de nós, um projeto de fornecedor não requer infraestrutura adicional – apenas seu computador local e uma conta gratuita do GitHub.

A dependência de vgo de proxies introduz uma sobrecarga de infraestrutura não trivial que acredito não ser suficientemente enfatizada. A nova infraestrutura deve ser provisionada, implementada, mantida e monitorada. Isso pode ser razoável para organizações de software, mas é um fardo para indivíduos e projetos de código aberto descentralizados. Além disso, coisas como CI geralmente são terceirizadas para terceiros, como Travis CI, que não são executados na mesma infraestrutura do ambiente de desenvolvimento da empresa. Isso dificulta a reutilização da infraestrutura de proxy.

Outras linguagens permitem o uso de proxies de cache. Python é aquele com o qual tenho mais experiência e, na minha experiência, raramente é usado devido à sobrecarga de configuração de um. Talvez o projeto Go possa tornar isso mais simples, mas se não for o padrão, veremos que é usado com menos frequência e a disponibilidade de compilações Go em estado selvagem diminuirá substancialmente. O impacto do evento NPM do lado esquerdo é um ótimo estudo de caso a esse respeito.

Se o módulo e o sistema de proxy nos permitirem verificar as dependências junto com o código do nosso próprio projeto (semelhante à venda atual, mas não necessariamente a mesma implementação) e a implementação do proxy vgo puder usá-lo, isso resolveria minhas preocupações. Mas se essa é a intenção, acho que precisa ser abordada de forma muito mais completa na proposta.

@joeshaw Você continuará a poder usar um diretório de fornecedores para criar uma compilação independente.

Da proposta:

Proibir o uso de diretórios de fornecedores, exceto em um uso limitado: um diretório de fornecedores no topo da árvore de arquivos do módulo de nível superior que está sendo compilado ainda é aplicado à compilação, para continuar a permitir repositórios de aplicativos independentes. (Ignorar outros diretórios de fornecedores garante que Go retorne a compilações nas quais cada caminho de importação tenha o mesmo significado em toda a compilação e estabelece que apenas uma cópia de um pacote com um determinado caminho de importação seja usada em uma determinada compilação.)

Um projeto que usa a venda hoje requer apenas a cadeia de ferramentas Go e o Git (ou outro sistema de controle de versão). Existe apenas um único ponto de falha: o repositório git.

Hoje, se você está hospedando pacotes sob seu próprio domínio, você precisa a) hospedar o repositório git eb) servir as tags <meta> necessárias para o go-get encontrá-lo. No futuro, você precisa a) servir os arquivos .zip e .json necessários para o vgo buscar o código. Parece haver menos pontos de falhas e menos infraestrutura necessária para hospedagem pura. Claro que você ainda precisa hospedar o repositório para desenvolvimento, mas isso não apenas nos leva, na pior das hipóteses, ao mesmo nível de antes, o próprio repositório também requer uma hospedagem muito menos escalável e confiável, pois é usado apenas por pessoas que estão realmente desenvolvendo.

Portanto, para importações de vaidade, não parece haver muita diferença em termos de despesas gerais.

Se, OTOH, você não estiver usando importações vanity, estará desconsiderando toda a infraestrutura que o github está executando para você. Portanto, isso não parece ser uma comparação de maçãs com maçãs: a única maneira de você sair por cima é porque deixa outras pessoas resolverem os problemas difíceis. Mas não há nada que impeça você de fazer isso no futuro. AIUI A Microsoft e outros já se ofereceram para investir horas de engenharia e capacidade de serviço nessa tarefa. Eu esperaria que, no futuro, o esforço de hospedar pacotes Go fosse mais ou menos limitado pelo esforço de hospedar pacotes npm ou gems ou crates: você tem um repositório github e clica em um botão "tornar disponível" em algum serviço gerenciado para hospedar os zips.

vgo em proxies

Pessoalmente, não gosto da palavra "proxy" para o que a infraestrutura necessária faz. "Espelho" parece mais apropriado. O espelho pode ser implementado como um proxy, mas também pode ser apenas um monte de arquivos servidos por um servidor web de sua escolha, escondidos atrás do cloudflare para escalabilidade e tempo de atividade aproximadamente infinitos.

Outras linguagens permitem o uso de proxies de cache.

Eu diria que outras linguagens servem como um modelo perfeito de por que isso não é realmente um problema. Eles tendem a confiar na hospedagem centralizada para seus pacotes para hospedar pacotes - vgo não apenas suporta esse modelo extremamente bem, mas também o torna opcional (para que você obtenha todas as vantagens sem nenhuma desvantagem) e muito simples implementar, dimensionar e operar.


IMO, se você comparar diretamente o que está acontecendo antes e depois e em Go e em outros idiomas, deve ficar claro que há muitas equivalências 1:1. E a única razão pela qual parece que haveria mais esforço no futuro é porque consideramos a infraestrutura existente como garantida e vemos que a nova infraestrutura ainda não existe. Mas não acho que tenhamos boas razões para duvidar que isso aconteça.

Portanto, para importações de vaidade, não parece haver muita diferença em termos de despesas gerais.

É verdade, mas a _vast_ minoria de pacotes usa domínios de vaidade, então não acho que isso seja um contraponto forte. (Também tenho outros problemas práticos com domínios de vaidade, que é que sua disponibilidade tende a ser _muito pior_ do que apenas usar o GitHub.)

Se, OTOH, você não estiver usando importações vanity, estará desconsiderando toda a infraestrutura que o github está executando para você.

Sim, exatamente! Eu sinto que isso faz o meu ponto para mim. Você obtém todo esse trabalho maravilhoso em infraestrutura e manutenção do tempo de atividade gratuitamente ou a um custo razoável. Mais importante, não envolve nenhum de seu próprio tempo.

Se o GitHub acabou executando um espelho compatível com vgo , talvez isso seja menos preocupante, embora eu goste da elegância de uma única busca do Git - uma ação atômica da perspectiva do usuário - contendo todo o código Preciso construir um projeto.

Eu diria que outras linguagens servem como um modelo perfeito de por que isso não é realmente um problema. Eles tendem a confiar em hospedagem centralizada para seus pacotes para hospedar pacotes - vgo não só suporta este modelo extremamente bem, mas também o torna opcional (para que você obtenha todas as vantagens sem nenhuma desvantagem) e muito simples de implementar, dimensionar e operar.

O problema é que isso adiciona pontos únicos de falha (SPOFs) à construção de um projeto. O código vai viver em um VCS não importa o que aconteça (e provavelmente no GitHub), então esse é um SPOF. Em outras linguagens, um repositório centralizado é um segundo SPOF. Para Go, cada caminho de importação é um SPOF adicional (github.com, golang.org, honnef.co, rsc.io, etc.) e o aumento de SPOFs reduz a disponibilidade geral.

A execução de um espelho pode reduzir isso de volta a dois, com certeza, mas é a infraestrutura que eu argumento que não precisa existir. Vendendo seus deps (ou ter um espelho local no disco) reduz isso de volta para apenas um: seu VCS.

Em qualquer caso, este pode ser um ponto discutível. Originalmente, eu não entendia a parte da proposta sobre o diretório vendor de nível superior que parece resolver minhas principais preocupações - obrigado por apontar isso, @kardianos - embora uma ruptura clara com o antigo sistema de venda pode ser melhor.

Estou feliz que " vendor de nível superior " ainda será uma configuração suportada porque eu amo muito git grep .

A execução de um espelho pode reduzir isso de volta a dois, com certeza, mas é a infraestrutura que eu argumento que não precisa existir.

Desenvolvedor corporativo aqui. Talvez não sejamos o público-alvo, mas gostaríamos de usar o Go. Não consigo ver isso acontecendo sem que tenhamos um espelho dentro de nossa empresa, como fazemos para java e javascript, para garantir que tudo o que construímos hoje possa ser construído amanhã.

Desenvolvedor corporativo aqui. Talvez não sejamos o público-alvo, mas gostaríamos de usar o Go. Não consigo ver isso acontecendo sem que tenhamos um espelho dentro de nossa empresa, como fazemos para java e javascript, para garantir que tudo o que construímos hoje possa ser construído amanhã.

@jamiethermo A venda não resolve isso para você hoje?

O suporte para proxies ou espelhos é bom e, se isso ajudar na adoção do Go, sou a favor. Minha preocupação era com os espelhos como substitutos para árvores de origem locais e independentes (o que chamamos de venda hoje).

@joeshaw

A comercialização não resolve isso para você hoje?

Bem, devo confessar a ignorância, e uma certa frustração com "como devo organizar meu código". Por "árvore de origem independente", você quer dizer que eu tenho um repositório git gigante que contém todo o meu código, além de todo o código na árvore do fornecedor? Então eu não preciso de um espelho porque eu verifiquei todos os outros códigos em meu próprio repositório?

Com relação a "como devo organizar meu código", esta página, How to Write Go Code , instrui sobre a estrutura de diretórios, mas não faz menção a fornecedores.

Adote o versionamento de importação semântica, no qual cada versão principal tem um caminho de importação distinto. Especificamente, um caminho de importação contém um caminho de módulo, um número de versão e o caminho para um pacote específico dentro do módulo. Se a versão principal for v0 ou v1 , o elemento de número de versão deve ser omitido; caso contrário, deve ser incluído.

Os pacotes importados como my/thing/sub/pkg , my/thing/v2/sub/pkg e my/thing/v3/sub/pkg vêm das principais versões v1 , v2 e v3 do módulo my/thing, mas a compilação os trata simplesmente como três pacotes diferentes.

my/thing/sub/pkg também pode ser v0.

Eu acompanhei o passeio . O exemplo é construído fora da árvore GOPATH e funciona, enquanto dep init barfs nessa situação. Eu trabalho com, eu acho, 80 equipes agora, e seria muito bom não ter que ter uma árvore gigante. (Enquanto eu estava digitando isso, alguém se aproximou com seu laptop e uma cara carrancuda, e "Eu tenho esse bug muito estranho ...")

@joeshaw

Você obtém todo esse trabalho maravilhoso em infraestrutura e manutenção do tempo de atividade gratuitamente ou a um custo razoável.

Eu expressei isso mal. Eu quis dizer "se você alegar que costumava precisar de menos infraestrutura se não estiver usando importações de vaidade, está desconsiderando toda a infraestrutura que o github executa para você". Eu quis salientar que a infraestrutura ainda está lá e ainda precisa ser executada. E também pode continuar a ser executado para você no futuro.

Portanto, sua preocupação seria abordada no que considero o cenário mais provável: que um órgão financiado (atualmente a Microsoft está se preparando para ser isso) está hospedando os arquivos zip.

embora eu goste da elegância de uma única busca do Git - uma ação atômica da perspectiva do usuário

vgo get é tanto uma ação atômica e menos trabalhosa, da mesma natureza (solicitações HTTP) que é mais fácil de entender.

O problema é que isso adiciona pontos únicos de falha (SPOFs) à construção de um projeto.

Onde meu último comentário está incorreto, então? Porque parece bastante óbvio para mim que o número de pontos de falhas é, no mínimo, menor , eles são mais simples, mais fáceis de escalar e mais baratos de executar.

Em outras linguagens, um repositório centralizado é um segundo SPOF. Para Go, cada caminho de importação é um SPOF adicional (github.com, golang.org, honnef.co, rsc.io, etc.) e o aumento de SPOFs reduz a disponibilidade geral.

Acho que isso é simplificado demais. Se você hospedar seu código em um espelho que fica inativo, você ainda pode desenvolver (o github ainda está ativo) e os usuários ainda podem instalar (usando um espelho diferente). O host de uma de suas dependências caindo não importa, pois você espelha suas dependências. Se você não confia que o espelho "centralizado" administrado pela comunidade permaneça ativo, você pode executar o seu próprio ou pagar alguém para fazê-lo, sem diferença para os usuários, dando a você controle total sobre o quanto você é afetado pelo tempo de inatividade do o repositório centralizado.

Nenhuma das coisas que você menciona na verdade é um SPOF, pois você ainda obtém pelo menos uma operação degradada. Acrescente a isso, que os espelhos reais são comparativamente triviais de operar (já que são apenas servidores HTTP sem estado) e não estou convencido de que a confiabilidade e a disponibilidade diminuiriam significativamente.

E FTR, agora seu git-hosting é um SPOF: se seu git-host estiver inativo, os usuários não poderão instalar e você não poderá desenvolver.

A execução de um espelho pode reduzir isso de volta a dois, com certeza, mas é uma infraestrutura que eu argumento que não precisa existir.

E meu ponto (mal formulado) acima foi que já acontece :) Você está simplesmente a) ignorando que isso acontece agora eb) assumindo que não acontecerá no futuro :)

Para dar uma perspectiva diferente:

  • estamos construindo muitos projetos Go dentro do nosso gerenciador de componentes de software de vários idiomas (rpm/dnf no Fedora, Centos e RHEL)
  • ele nos permite usar muitos dos mesmos truques que a proposta vgo, jogando no namespace do componente da camada rpm (normalmente, renomeando um projeto no nível do namespace rpm de project para compat-project-x para permitir distinguir entre versões incompatíveis do mesmo caminho de importação, exatamente como vgo fará).
  • esses truques são definitivamente úteis para ajudar projetos Go complexos a construir
  • embora não seja tão completo e robusto quanto fazê-lo no nível da linguagem
  • preferimos retransmitir restrições de linguagem para rpm/dnf do que adicionar uma sobreposição de restrições de rpm/dnf sobre o código original.
  • estamos bastante cansados ​​de todas as soluções alternativas do sistema de arquivos atualmente necessárias para convencer as ferramentas go a examinar as fontes do projeto Go. O GOPATH físico não fará falta.

Portanto, estamos entusiasmados com o vgo e esperamos que ele seja implantado em breve (precisávamos dele anos atrás).

Dito isto, nosso uso de fluxos de trabalho do tipo vgo expôs as seguintes dificuldades.

A proposta vgo se congratula por ter tornado desnecessários os makefiles. Isso não é bem verdade.

  • muitos projetos Go se apaixonaram por arquivos go autogerados e não existe uma maneira padrão de ir para exigir regenerá-los ou expressar o que a geração precisa para ter sucesso
  • é tão ruim que muitos projetos precisam ser enviados com arquivos pré-gerados (às vezes criando repositórios de arquivos pré-gerados separados e completos)
  • esses arquivos são uma grande fonte de incompatibilidades de versão. Os humanos se preocupam em escrever códigos que não precisem ser alterados todos os dias quando expostos a novas versões de dependência. Arquivos gerados automaticamente OTOH geralmente estão intimamente ligados a um ambiente de geração exato, porque os autores da ferramenta geradora assumem que você apenas os regenerará conforme necessário.
  • para que o vgo seja bem-sucedido, ele precisa de uma maneira de identificar esses arquivos, retirá-los dos módulos .zip e expressar como regenerá-los para os usuários dos módulos .zip

Isso é agravado pela regra atual "tudo deve estar no GOPATH"

  • tudo não pode estar em GOPATH quando a geração depende de arquivos proto publicados fora do GOPATH por projetos multilíngue
  • a cadeia de ferramentas Go precisa aprender a ler recursos fora de seu GOPATH
  • a cadeia de ferramentas Go precisa aprender a publicar recursos fora do GOPATH, quando são expostos a projetos não necessariamente escritos em Go
  • a solução atual de duplicar recursos em cópias dentro do GOPATH é outra fonte de incompatibilidades de versão
  • todos não copiarão os mesmos arquivos ao mesmo tempo
  • às vezes os projetos copiam de várias fontes, criando mixagens únicas.
  • cada projeto fará o controle de qualidade em sua própria cópia, tornando a composição do projeto Go perigosa
  • o solucionador de versão vgo não funcionará como pretendido se computar os relacionamentos entre as versões de código, mas ignorar as relações entre as versões de recursos.
  • vgo precisa fazer cópias privadas de coisas publicadas fora do GOPATH desnecessárias para evitar uma explosão de versões de módulos incompatíveis.

Muitos projetos exploraram a regra "todo diretório Go é um pacote separado" para publicar projetos com código incompatível ou completamente quebrado em subdiretórios separados

  • muitos outros projetos Go usaram a mesma regra para selecionar subdiretórios específicos e usá-los em um contexto incompatível com os testes de unidade e controle de qualidade do projeto original
  • que "funciona" e produz "compilações reproduzíveis", desde que nenhuma alteração no projeto original ou no consumidor do projeto faça uso de alguma outra parte do projeto original. Quando isso acontece, todo o castelo de areia desmorona sob a dívida técnica acumulada,
  • você vê projetos se recusando a atualizar para versões mais recentes do projeto porque isso requer a limpeza de seus usos incorretos anteriores de outros projetos.
  • você tem uma esquizofrenia estranha onde os projetos Go se consideram não responsáveis ​​por bugs nas bases de código que reutilizam, e ao mesmo tempo se recusam a aplicar as correções publicadas por essas mesmas bases de código (acho que somos todos humanos e a negação da realidade desagradável é hardwired no cérebro humano)
  • as correções não se propagam, e tudo está "bem" até que você encontre os bugs que exigiram as correções em primeiro lugar ou os testes de unidade que foram invalidados pela seleção seletiva.
  • um verificador de segurança automatizado que comparasse o estado de segurança dos projetos, com o estado de segurança dos mesmos projetos dentro dos diretórios do fornecedor, teria um dia de campo
  • quanto maior o projeto, maior a probabilidade de ter essas bombas-relógio escondidas dentro de seus fornecedores
  • ao definir claramente os limites do projeto dentro dos módulos zip, o vgo acabará com essas práticas
  • no entanto, também implicará uma grande limpeza de muitas bases de código Go
  • para que o vgo seja bem-sucedido, ele precisa fornecer ferramentas para ajudar os projetos Go existentes a refatorar e dividir sua base de código em módulos que façam sentido do ponto de vista da compatibilidade de software. Caso contrário, os projetos que usam vgo produzirão restrições irreconciliáveis ​​ou apenas bloquearão o status quo (o que funciona para o código existente, mas é terrível do ponto de vista da evolução do código).

A prática atual de expor toda a base de código Go em uma única árvore GOPATH com pequenos limites entre projetos produziu efeitos colaterais deletérios de um POW de engenharia de software

  • o código não é comprometido no projeto onde faz sentido técnico, mas no repositório mais conveniente para o autor do código (o repositório onde ele tem acesso)
  • Os projetos Go sangram uns nos outros, com partes de um projeto dependendo de partes de outros projetos e parte desse outro projeto dependendo do projeto original
  • que produz afetivamente gráficos de restrição de versão bloqueada onde você não pode mover uma parte sem mover todas as outras
  • ninguém tem recursos para mudar todos os outros em uma única operação
  • projetos travam e param de progredir
  • usar módulos com limites claros e tentar tornar as restrições de versão entre módulos explícitas irão expor essas situações
  • que será massivamente impopular (mesmo que o problema exista hoje, escondido sob o véu da venda)
  • muitas vezes não é apenas uma questão de separar um subdiretório: as dependências do projeto problemáticas podem ocorrer em uma árvore de diretórios A/B/C/D nos níveis A e D, mas não nos níveis B e C.
  • para que o vgo seja bem-sucedido, ele precisa fornecer ferramentas para ajudar os projetos Go existentes a refatorar e dividir suas bases de código em módulos separados que seguem as linhas do gráfico de dependência

Testfing/fixtures, example e testdata são uma outra lata de worms, com suas próprias necessidades de dependência e versão de dependência

  • eles precisam ser isolados de uma forma ou de outra em módulos separados ou suas necessidades irão envenenar a resolução da versão do solver
  • se você ignorar as necessidades deles, você efetivamente decidirá que ninguém além do projeto original executará testes de unidade
  • isso é bastante perigoso quando você permite atualizações de versão. Problemas vão acontecer. Você precisa detectá-los
  • testes de integração que dependem não apenas de algum outro código, mas de um ambiente específico que ninguém além do projeto original pode replicar (por exemplo, um servidor ou site específico para conversar), provavelmente precisam de isolamento específico

Provavelmente esqueci muitas outras coisas, mas essas são as mais críticas.

Por último:

  • Os projetos Go não vivem isolados
  • alguns projetos Go publicam conectores para outras linguagens
  • alguns projetos em outros idiomas publicam conectores Go
  • isso torna o tratamento de todas as outras linguagens um problema.
  • você não pode ter tudo, exceto a parte Go, atualizando para a versão secundária mais recente quando um projeto multilíngue é lançado.
  • isso seria um inferno para um prisioneiro de guerra de gerenciamento operacional.
  • a atualização automática para a subversão mais recente desempenha um grande papel na simplificação dos gráficos de restrição de software, fazendo com que todos aspirem versões mais antigas (o benefício é técnico, o mecanismo é social).
  • reconstruções de alta fidelidade são semelhantes a sites com pixels perfeitos. Eles parecem uma ideia obviamente boa, eles recompensam o ego do autor original, mas eles são um PITA para qualquer um que realmente tente usar o resultado porque eles não são evolutivos e adaptáveis ​​ao contexto local (não original).

Em servidores proxy:

para facilitar a depuração, $GOPROXY pode até ser uma URL file:/// apontando para uma árvore local.

Por favor, faça funcionar com um arquivo:/// de https:/// URL apontando para um diretório contendo módulos de terceiros em um formato zip (e exclua todo o resto)

Essa é a maneira mais simples de uma entidade coordenar o trabalho de várias equipes de desenvolvimento que dependem de subconjuntos da mesma lista de projetos de terceiros: ter um responsável pelo controle de qualidade/legal/segurança responsável por verificar "boas" versões de terceiros e implantá-las em um diretório comum e fazer com que todos os outros tenham seu trabalho dependendo dos módulos disponíveis neste diretório comum.

Dessa forma você tem certeza que ninguém começa a trabalhar em um rev incompatível com o trabalho de outros, e ninguém fica em um rev com bugs ou perigoso já identificado por outra equipe, e suas estações de desenvolvimento não ficam continuamente esperando o download das mesmas cópias de software já disponível localmente.

@Merovius Eu discordo, mas acho que estamos nos desviando do tópico e não queremos atrapalhar a discussão do problema. Estou feliz em acompanhar em um meio diferente, no entanto. (Meu e-mail está no meu perfil do github e sou joeshaw na folga dos Gophers.)

https://github.com/golang/go/issues/24301#issuecomment -374882685, @tpng :

Qual fuso horário é usado para o carimbo de data/hora na pseudo-versão (v0.0.0-yyyymmddhhmmss-commit)?

Como você observou em sua edição, UTC. Mas observe também que você nunca precisa digitá-los. Você pode simplesmente digitar um git commit hash (prefixo) e vgo irá calcular e substituir a pseudo-versão correta.

https://github.com/golang/go/issues/24301#issuecomment -374907338, @AlexRouSg :

Você estará abordando dependências C?

https://github.com/golang/go/issues/24301#issuecomment -376606788, @stevvooe :

o que acontecerá com recursos não-Go, como protobufs ou arquivos c?

https://github.com/golang/go/issues/24301#issuecomment -377186949, @nim-nim:

A proposta vgo se congratula por ter tornado desnecessários os makefiles. Isso não é bem verdade. [Discussão do código gerado.]

O desenvolvimento Non-Go continua sendo um não-objetivo do comando go , então não haverá suporte para gerenciamento de bibliotecas C e tal, nem haverá suporte explícito para buffers de protocolo.

Dito isso, certamente entendemos que usar buffers de protocolo com Go é muito difícil e gostaríamos de ver isso abordado separadamente.

Quanto ao código gerado de forma mais geral, um sistema de compilação real entre linguagens é a resposta, especificamente porque não queremos que todos os usuários precisem ter os geradores corretos instalados. Melhor para o autor rodar os geradores e conferir o resultado.

https://github.com/golang/go/issues/24301#issuecomment -375248753, @mrkanister :

Eu sei que a ramificação padrão pode ser alterada de master para v2, mas isso ainda me deixa com a tarefa de atualizá-la toda vez que eu lançar uma nova versão principal. Pessoalmente, eu preferiria ter um master e um branch v1, mas não tenho certeza de como exatamente isso se encaixaria na proposta.

Como @AlexRouSg e talvez outros apontaram, você pode fazer isso. Eu só queria confirmar suas respostas. Eu também vou adicionar isso ao FAQ.

https://github.com/golang/go/issues/24301#issuecomment -375989173, @aarondl :

Embora isso funcione agora, é pretendido?

Absolutamente sim.

@jamiethermo , muito obrigado por comentar sobre Perforce e ramificações em diferentes diretórios. Eu tinha esquecido desse recurso, mas talvez tenha sido isso que me convenceu de que era importante permitir o vgo.

Houve uma boa discussão começando em https://github.com/golang/go/issues/24301#issuecomment -376925845 sobre fornecedores versus proxies. Há claramente dois conjuntos distintos de preocupações aqui.

Desenvolvedores de código aberto tendem a evitar depender de infraestrutura, então eles querem fornecedores, como @joeshaw escreveu. Para confirmar, manteremos isso funcionando, de forma limitada (apenas o diretório do fornecedor no nível superior do módulo de destino geral onde você está executando os comandos go).

Os desenvolvedores corporativos não têm problemas em confiar na infraestrutura - esse é apenas outro custo - especialmente se isso trouxer uma redução de custo maior, como não duplicar todo o código fornecido em cada repositório e ter que gastar tempo mantendo tudo sincronizado. Essencialmente, todas as empresas com as quais conversamos querem proxies/espelhos, não fornecedores, como @jamiethermo pediu. Vamos garantir que funcione também.

Também queremos muito construir uma rede espelhada compartilhada na qual os desenvolvedores tenham motivos para confiar e confiar, para que todos os desenvolvedores de código aberto não sintam que devem ser fornecedores. Mas isso é mais tarde. Primeiro vgo precisa se tornar go.

https://github.com/golang/go/issues/24301#issuecomment -377220411, @nim-nim:

Por favor, faça funcionar com um arquivo:/// de https:/// URL apontando para um diretório contendo módulos de terceiros em um formato zip (e exclua todo o resto)

Não sei exatamente o que você está pedindo dizendo "excluir todo o resto". Se $GOPROXY estiver definido, vgo pergunta a esse proxy. Ele nunca volta para qualquer outro lugar. Esse proxy pode ser servido por uma árvore de arquivos estática, que é principalmente módulos de terceiros em formato zip. A árvore de arquivos também deve conter alguns outros arquivos de metadados, para navegação e pesquisa. Esses arquivos extras são inevitáveis, já que o HTTP não nos dá uma maneira padrão de fazer coisas como listas de diretórios.

https://github.com/golang/go/issues/24301#issuecomment -377186949, @nim-nim:

Uau, isso é um longo comentário. Acho que concordo com boa parte do que você escreveu. Quero responder ao último marcador do seu post:

  • reconstruções de alta fidelidade são semelhantes a sites com pixels perfeitos. Eles parecem uma ideia obviamente boa, eles recompensam o ego do autor original, mas eles são um PITA para qualquer um que realmente tente usar o resultado porque eles não são evolutivos e adaptáveis ​​ao contexto local (não original).

Acho que argumentaria que os arquivos de bloqueio são como sites com pixels perfeitos. As compilações de alta fidelidade, por outro lado, degradam-se graciosamente à medida que o contexto muda. Por padrão, uma compilação usará, digamos, B 1.2 e C 1.4. Mas então, se for parte de uma compilação maior que precisa de B 1.3, tudo bem, ele se dará bem com B 1.3, mas manterá C 1.4 (mesmo que existam mais novos) na ausência de um requisito concreto para atualização. Portanto, as compilações realmente de alta fidelidade são o melhor dos dois mundos: fiéis ao original na medida do possível, mas não insistem em pixels perfeitos quando isso não é possível.

https://github.com/golang/go/issues/24301#issuecomment -375415904, @flibustenet :

@rsc Gostaria de pedir que você seja mais preciso sobre o roteiro e o que devemos fazer agora.
Ele seguirá a política Go e congelará o vgo em 3 meses (2 agora)?

Vgo é um protótipo e nunca será lançado por conta própria. Não está sujeito ao congelamento ou qualquer outra coisa. Mas esta proposta é mover as ideias e a maior parte do código para o cmd/go principal. Como parte do lançamento, o cmd/go certamente está sujeito ao congelamento. Porque é opt-in e porque o código específico do vgo é bastante bem isolado do resto da operação do comando go, trabalhar em partes específicas do vgo é de baixo risco e eu poderia ver um pouco dele continuando por algumas semanas no congelador. No momento estou focado na discussão e ajustes da proposta. Uma vez que a proposta pareça estar em boa forma sem problemas significativos, passaremos a mover o código para cmd/go.

Devemos agora seguir com nosso bastão de peregrino pedindo a cada mantenedor de libs para adicionar um arquivo go.mod ou devemos esperar que a proposta seja oficialmente aceita (para ter certeza de que o nome e a sintaxe não serão alterados) ?

Acho que a sintaxe go.mod provavelmente mudará (observe este problema). Mas, como observei na proposta, continuaremos aceitando sintaxes antigas para sempre, e o vgo apenas atualizará os arquivos existentes, então não é um grande problema. Dito isso, eu não sairia tentando enviar PRs para todas as bibliotecas que você encontrar até que o código chegasse à cópia de desenvolvimento do cmd/go.

https://github.com/golang/go/issues/24301#issuecomment -376640804, @pbx0 :

é fácil (hoje) com o vgo determinar se você tem mais de 1 versão de um pacote em uma compilação?

A coisa mais fácil de fazer hoje é construir o binário e então executar goversion -m nele (veja https://research.swtch.com/vgo-repro). Quando temos uma lista go mais geral com reconhecimento de módulo, ela deve ser capaz de fazer a mesma coisa sem construir o binário primeiro.

https://github.com/golang/go/issues/24301#issuecomment -376236546, @buro9 :

[Posso fazer uma alteração de segurança incompatível com versões anteriores, como microcosm-cc/ bluemonday@a5d7ef6? ]

Como o @Merovius disse, se adotarmos as diretrizes de compatibilidade do Go 1 , as alterações de segurança serão explicitamente permitidas sem afetar a versão principal.

Dito isso, mesmo quando você precisa fazer algo por segurança, provavelmente ainda deve se esforçar para obter o mínimo de interrupção. O commit ao qual você vinculou provavelmente é mais perturbador do que o necessário e, em uma situação futura, eu o encorajo a abordar essa mudança do ponto de vista de "como faço para quebrar o menor número possível de clientes enquanto ainda elimino o problema de segurança? "

Por exemplo, não remova uma função. Em vez disso, torne a função panic ou log.Fatal somente se chamada incorretamente.

Nesse caso, em vez de excluir AllowDocType, eu o manteria e certamente continuaria aceitando AllowDocType(false), já que essa é a configuração segura. Se alguém tivesse escrito um wrapper para sua biblioteca, talvez como um programa de linha de comando com um sinalizador -allowdoctype, pelo menos os usos do programa sem esse sinalizador continuariam funcionando.

E além disso, parece que a preocupação era que o doctype estava completamente desmarcado, mas eu provavelmente teria feito uma verificação mínima para manter os usos mais comuns funcionando e, em seguida, rejeitar conservadoramente outros. Por exemplo, pelo menos, eu continuaria permitindo e talvez também me incomodasse em permitir ..> com strings entre aspas sem &#<>\ chars.

https://github.com/golang/go/issues/24301#issuecomment -375090551, @TocarIP :

[Preocupações sobre não receber atualizações prontamente.]

Eu realmente acho que o oposto acontecerá, que os programas podem estar mais atualizados, já que na seleção de versão mínima é _impossível_ para uma dependência impedir que a compilação geral seja atualizada para uma versão mais recente.

O que @Merovius escreveu em https://github.com/golang/go/issues/24301#issuecomment -375992900 parece exatamente certo para mim. O ponto-chave é que você só recebe atualizações quando as solicita, então as coisas (potencialmente) quebram apenas quando você espera isso e está pronto para testar, depurar e assim por diante. Você precisa solicitá-los, mas não com uma frequência significativamente maior do que em outros sistemas com arquivos de bloqueio. E também queremos facilitar a exibição de avisos como "você está compilando com uma versão obsoleta/insegura". Mas é importante não apenas atualizar silenciosamente como um efeito colateral de operações sem atualização.

Também adicionado ao FAQ.

Obrigado a todos pela ótima discussão até agora e por responderem às perguntas uns dos outros. Respostas realmente ótimas de muitas pessoas, mas agradecimentos especiais a @Merovius e @kardianos. Atualizei as Perguntas frequentes https://github.com/golang/go/issues/24301#issuecomment -371228664 e o Resumo da discussão https://github.com/golang/go/issues/24301#issuecomment -371228742. Há três questões importantes ainda não respondidas (dizem TODO no resumo), nas quais trabalharei a seguir. :-)

@rsc , #24057 tem alguma discussão sobre o uso de tar em vez de zip.

https://github.com/golang/go/issues/24301#issuecomment -375106068, @leonklingele :

Se o suporte para go get como o conhecemos hoje for obsoleto e eventualmente removido, qual é a maneira recomendada de buscar e instalar (sem etiqueta) os binários do Go?

Ainda será ir buscar. Se o repositório do binário não estiver marcado, go get usará o commit mais recente. Mas realmente as pessoas que publicam binários devem ser encorajadas a marcar os repositórios da mesma forma que os repositórios que contêm bibliotecas (ou uma mistura).

Se $GOPATH estiver obsoleto, onde esses binários serão instalados?

Você não precisa mais trabalhar em $GOPATH, mas o código ainda é gravado no primeiro diretório listado em $GOPATH - é o cache de origem, veja $GOPATH/src/v depois de usar vgo. Os binários são instalados em $GOPATH/bin. A partir de alguns lançamentos atrás, você não precisa definir $GOPATH - ele tem um padrão, $HOME/go. Então, o que deve acontecer é que os desenvolvedores parem de se preocupar em definir $GOPATH ou mesmo saber o que é, e eles apenas descobrem que seus binários estão em $HOME/go/bin. Eles podem usar $GOBIN para substituir esse local.

@dsnet , obrigado, adicionei um link no resumo da discussão. Vamos manter essa discussão por lá.

Se $GOPROXY estiver definido, vgo pergunta a esse proxy. Ele nunca volta para qualquer outro lugar. Esse proxy pode ser servido por uma árvore de arquivos estática, que é principalmente módulos de terceiros em formato zip. A árvore de arquivos também deve conter alguns outros arquivos de metadados, para navegação e pesquisa. Esses arquivos extras são inevitáveis, já que o HTTP não nos dá uma maneira padrão de fazer coisas como listas de diretórios.

Contanto que os módulos originais sejam mantidos em formato zip, eliminando qualquer tentação de adulterá-los, e o indexador seja robusto e leve, tudo bem.

Embora as restrições de listagem não se apliquem a arquivos e utilitários como o lftp foram capazes de listar diretórios http por muito tempo (não importa se não é padrão se funciona nos principais servidores http). Assim, uma operação index-less é provavelmente possível e preferível para pequenas entidades que não desejam investir em infra. yum/dnf/zipper também depende de índices personalizados e obter um diretório compartilhado indexado nem sempre é tão simples quanto você pode pensar em algumas organizações.

Desenvolvedores de código aberto tendem a evitar depender de infraestrutura, então eles querem fornecedores, como @joeshaw escreveu

Na verdade, os desenvolvedores de código aberto geralmente querem que todo o processo seja aberto e transparente, e não tenham que depender dos bons desejos de outra pessoa. Portanto, infra está perfeitamente bem, desde que seja de código aberto e fácil e barato de implantar localmente. Confiar em grandes sites proprietários de caixa preta como o github claramente não se enquadra nessa categoria, mas isso não é a mesma coisa que infra. As pessoas de código aberto fizeram espelhos décadas antes de todos os outros. O que eles não aceitam são espelhos fechados e caros (em termos de código aberto, caro é medido em tempo humano)

A natureza aberta, fácil e barata da venda é apreciada, o processo de venda em si e a maneira como ela incentiva a fossilização progressiva de bases de código em versões obsoletas de código de terceiros, nem tanto.

Os desenvolvedores corporativos não têm problemas em confiar na infraestrutura - esse é apenas outro custo

Deve ser muito bom trabalhar no Google :(. Exceto para o pequeno número de empresas com uma grande operação Go existente, onde investir em infra Go é um acéfalo, todos os outros terão que passar por longos e tediosos processos de aprovação , nem que seja para justificar o pagamento de alguém para olhar o problema, portanto, qualquer custo de infra-estrutura reduzirá o alcance do Go e impedirá sua adoção por novas estruturas.

As empresas são como os opensourcers, eles se preocupam com o barato e o fácil. Eles costumavam não se importar com o aberto, mas isso está mudando lentamente agora que eles percebem que se correlaciona com o barato (para o desânimo dos fornecedores corporativos tradicionais que costumavam se especializar em soluções de caixa preta caras com consultoria cara para ajudar nas implantações).

As empresas que contratam TI interna para o menor lance definitivamente insistirão em espelhos, pois definitivamente não querem que seus desenvolvedores baratos baixem códigos quebrados ou perigosos que esses desenvolvedores não entendem da Internet. Eles pagarão humanos e ferramentas para verificar se há problemas no conteúdo do espelho local e forçarão a TI interna a usá-lo exclusivamente.

Também queremos muito construir uma rede espelhada compartilhada na qual os desenvolvedores tenham motivos para confiar e confiar, para que todos os desenvolvedores de código aberto não sintam que devem ser fornecedores.

Basta publicar uma cópia de referência de um diretório indexado contendo módulos em algum lugar. Esqueça qualquer configuração semelhante a proxy que exija configuração específica do servidor web. Isso é o que os open sourcers fazem e eles não têm dificuldade em se espelhar. Contanto que o espelhamento seja apenas copiar o conteúdo de um diretório e não exija uma configuração de servidor web específica, há muitas organizações dispostas a espelhar.

Quanto ao código gerado de forma mais geral, um sistema de compilação real de várias linguagens é a resposta,

Você sabe tão bem quanto eu que isso nunca vai acontecer, alguém sempre vai querer inventar uma nova linguagem que faça suas próprias coisas. Isso é um argumento de espantalho.

Isso não impede que o Go normalize um comando padrão que inicie qualquer processo específico do projeto que gere o código, com diretrizes rígidas sobre o que ele pode e não pode fazer (normalmente, ele não deve fazer nada já coberto pelos comandos Go padrão, porque esses comandos devem já estar bem como está).

especificamente porque não queremos que todos os usuários precisem ter os geradores corretos instalados. Melhor para o autor rodar os geradores e conferir o resultado.

Isso exigiria um grande repensar sobre como os geradores existentes são implementados, porque agora eles não se importam com a portabilidade da versão e esperam que o ambiente do software seja congelado antes da geração. Com o efeito direto de que a geração torna qualquer alteração de versão posterior sem regeneração perigosa. Não importa se o resultado é verificado por humanos ou não, pois os humanos apenas verificarão a versão original definida. vgo depende de poder fazer alterações de versão posteriores.

Então vgo terá que enfrentar a regeneração mais cedo ou mais tarde. Mais tarde significa esperar que os projetos descubram que as atualizações vgo são perigosas na presença de código gerado.

https://github.com/golang/go/issues/24301#issuecomment -374791885, @jimmyfrasche :

Isso também afetaria coisas como drivers de banco de dados e formatos de imagem que se registram em outro pacote durante a inicialização, já que várias versões principais do mesmo pacote podem acabar fazendo isso. Não está claro para mim quais seriam todas as repercussões disso.

Sim, o problema do código que assume que "haverá apenas um desses em um programa" é real, e é algo que todos nós teremos que trabalhar para estabelecer novas (melhores) melhores práticas e convenções. Eu não acho que esse problema esteja sendo introduzido pelo vgo, e o vgo sem dúvida torna a situação melhor do que antes.

Entendo que algumas pessoas argumentam que o vgo deveria adotar a regra do Dep de que não pode nem haver um 1.xe 2.x juntos, mas isso claramente não se adapta às grandes bases de código que estamos mirando com Go. É impraticável esperar que grandes programas inteiros atualizem de uma API para outra de uma só vez, como mostra o post vgo-import . Acredito que essencialmente todos os outros gerenciadores de pacotes permitem 1.xe 2.x juntos pelo mesmo motivo. Certamente a Cargo faz.

Em geral, o vgo reduz a duplicação em comparação com a venda. Com a venda é fácil acabar com 1.2, 1.3 e 1.4 de um determinado pacote em um binário, sem perceber, ou talvez até três cópias de 1.2. Pelo menos vgo corta a duplicação possível para um 1.x, um 2.x e assim por diante.

Já acontece que autores de pacotes diferentes precisam ter certeza de não tentar registrar a mesma coisa. Por exemplo, expvar faz http.Handle("/debug/vars") e tem essencialmente uma reivindicação para esse caminho. Espero que todos concordemos que um pacote de terceiros como awesome.io/supervars não deve tentar registrar o mesmo caminho. Isso deixa conflitos entre várias versões de um único pacote.

Se introduzirmos expvar/v2, isso acaba sendo um segundo pacote diferente, assim como awesome.io/supervars, e pode entrar em conflito com expvar em uma compilação grande. Ao contrário dos supervars, no entanto, o expvar/v2 é de propriedade da mesma pessoa ou equipe que o expvar, então os dois pacotes podem se coordenar para compartilhar o registro. Isso funcionaria da seguinte forma. Suponha que expvar v1.5.0 seja o último antes de decidirmos escrever v2, então v1.5.0 tem um http.Handle nele. Queremos que a v2 seja a substituição da v1, então vamos mover o http.Handle para a v2.0.0 e adicionar a API na v2 que permite que a v1 encaminhe suas chamadas para a v2. Em seguida, criaremos a v1.6.0 implementada com esse encaminhamento. v1.6.0 não chama http.Handle; ele delega isso para a v2.0.0. Agora expvar v1.6.0 e expvar/v2 podem coexistir, porque planejamos dessa forma. O único problema que resta é o que acontece se uma compilação usa expvar v1.5.0 com expvar/v2? Precisamos garantir que isso não aconteça. Fazemos isso fazendo expvar/v2 exigir expvar na v1.6.0 (ou posterior), mesmo que não haja importação nessa direção e, claro, expvar v1.6.0 também requer expvar/v2 na v2.0.0 ou posterior para chamar suas APIs . Esse ciclo de requisitos nos permite garantir que v1.5.0 e v2 nunca sejam misturados. O planejamento para esse tipo de coordenação entre versões principais é exatamente o motivo pelo qual a seleção de versão mínima permite ciclos no gráfico de requisitos.

A regra "apenas uma versão principal" combinada com a permissão de ciclos no gráfico de requisitos fornece aos autores as ferramentas necessárias para gerenciar a coordenação adequada de singletons (e migração de propriedade de singleton) entre diferentes versões principais de seus módulos. Não podemos eliminar o problema, mas podemos dar aos autores as ferramentas necessárias para resolvê-lo.

Eu sei que os buffers de protocolo em particular têm problemas de registro, e esses problemas são exacerbados pela maneira muito ad-hoc que os arquivos de protocolo .pb.go são passados ​​e copiados em projetos. Isso é novamente algo que é principalmente independente do vgo. Devemos corrigi-lo, mas provavelmente alterando a maneira como os buffers de protocolo Go são usados, não vgo.

https://github.com/golang/go/issues/24301#issuecomment -374739116, @ChrisHines :

Estou mais preocupado com o caminho da migração entre um mundo pré-vgo e um mundo vgo indo mal. [Mais detalhes]

Estou muito feliz que este foi o primeiro comentário sobre a proposta. É claramente a coisa mais importante para acertar.

A transição do antigo go get e das inúmeras ferramentas de fornecedores para o novo sistema de módulos precisa ser incrivelmente suave. Eu tenho pensado muito recentemente sobre exatamente como isso vai funcionar.

A proposta original permite aos desenvolvedores a opção de colocar a versão 2 de um módulo em um subdiretório de repositório chamado v2/. Exercitar essa opção permitiria aos desenvolvedores criar um repositório que usa versionamento de importação semântica, é compatível com módulos e ainda é compatível com versões anteriores com o antigo “go get”. O post que descreve essa opção admite que a grande maioria dos projetos não desejará exercer essa opção, o que é bom. Só é necessário para compatibilidade se um projeto já estiver na v2 ou posterior. Ao mesmo tempo, subestimei o número de projetos grandes e amplamente usados ​​que estão na v2 ou posterior. Por exemplo:

  • github.com/godbus/dbus está na v4.1.0 (importado por 462 pacotes).
  • github.com/coreos/etcd/clientv3 está na v3.3.3 (importado por 799 pacotes).
  • k8s.io/client-go/kubernetes está na v6.0.0 (importado por 1928 pacotes).

Para evitar quebrar seus clientes, esses projetos precisariam ser movidos para um subdiretório até que todos os clientes pudessem usar módulos e, em seguida, voltar para a raiz. Isso é pedir muito.

Outra opção para brincar com os submódulos git. O antigo comando go get prefere uma tag ou branch chamado “go1” sobre o branch padrão do repositório, então um projeto que quisesse habilitar uma transição suave poderia criar um commit em um branch “go1” que tivesse apenas um subdiretório “v2” configurado como um submódulo git apontando de volta para a raiz real do repositório. Quando “go get” verificasse a ramificação go1 e os submódulos preenchidos, obteria o layout correto da árvore de arquivos. Isso é meio horrível, porém, e os ponteiros do submódulo precisariam ser atualizados toda vez que um novo lançamento fosse feito.

Ambos têm o efeito infeliz de que os autores devem tomar medidas para evitar quebrar seus usuários e, mesmo assim, apenas para novos lançamentos. Em vez disso, gostaríamos que o código dos usuários continuasse funcionando mesmo sem trabalho adicional dos autores e, idealmente, até mesmo para versões antigas.

Mas essas são as únicas maneiras em que consigo pensar para manter inalterado, o velho vai trabalhar enquanto fazemos a transição para o mundo do módulo. Se não forem esses, então a alternativa é modificar o go get antigo, o que realmente significa modificar o go build antigo.

Fundamentalmente, existem duas convenções de caminho de importação diferentes: a antiga, sem versões principais, e a nova, com versões principais na v2 ou posterior. O código em uma árvore antiga provavelmente usa a convenção antiga, enquanto o código em uma nova árvore - sob um diretório contendo um arquivo go.mod - provavelmente usa a nova convenção. Precisamos de uma maneira de fazer com que as duas convenções se sobreponham durante uma transição. Se ensinarmos o old go get um pouco sobre versionamento de importação semântica, então podemos aumentar consideravelmente a quantidade de sobreposição.


Alteração proposta: Defina o código “novo” como código com um arquivo go.mod no mesmo diretório ou em um diretório pai. O antigo go get deve continuar baixando o código exatamente como sempre fez. Proponho que a etapa “go build” ajuste sua manipulação de importações em código “novo”. Especificamente, se uma importação no novo código diz x/y/v2/z mas x/y/v2/z não existe e x/y/go.mod diz “módulo x/y/v2”, então go build lerá a importação como x/y/z em vez disso. Gostaríamos de empurrar esta atualização como uma versão pontual para Go 1.9 e Go 1.10.

Atualização : Copiado para #25069.


Essa mudança deve possibilitar que pacotes sem conhecimento de módulo usem versões mais recentes de pacotes com reconhecimento de módulo, independentemente de esses pacotes escolherem a abordagem “ramificação principal” ou “subdiretório principal” para estruturar seus repositórios. Eles não seriam mais forçados a usar a abordagem de subdiretório, mas ainda seria uma opção de trabalho. E os desenvolvedores que usam versões mais antigas do Go ainda podem criar o código (pelo menos após a atualização para a versão pontual).

@rsc Estou tentando descobrir como poderíamos fazer a transição para o vgo funcionar também, cheguei às mesmas conclusões que você expôs em sua resposta e sua sugestão corresponde à melhor abordagem que encontrei por mim mesmo. Eu gosto da sua mudança proposta.

https://github.com/golang/go/issues/24301#issuecomment -377527249, @rsc :

Em seguida, criaremos a v1.6.0 implementada com esse encaminhamento. v1.6.0 não chama http.Handle; ele delega isso para a v2.0.0. Agora expvar v1.6.0 e expvar/v2 podem coexistir, porque planejamos dessa forma.

Isso soa mais fácil do que é. Na realidade, na maioria dos casos, isso significa que v1.6.0 deve ser uma reescrita completa de v1 na forma de wrapper v2 _(chamada encaminhada para http.Handle resultará no registro de outro manipulador - um de v2 - que por sua vez significa todos os relacionados o código também deve ser da v2 para interagir corretamente com o manipulador registrado)_.

Isso provavelmente mudará detalhes sutis sobre o comportamento da v1, especialmente com o tempo, à medida que a v2 evoluir. Mesmo se formos capazes de compensar essas mudanças sutis de detalhes e emular v1 bem o suficiente na v1.6.x - ainda assim, é muito trabalho extra e muito provavelmente faz suporte futuro da ramificação v1 (quero dizer sucessores da v1.5.0 aqui) sem sentido.

@powerman , absolutamente não estou dizendo que isso é trivial. E você só precisa coordenar na medida em que v1 e v2 disputam algum recurso compartilhado como um registro http. Mas os desenvolvedores que participam desse ecossistema de empacotamento precisam entender que v1 e v2 de seus pacotes precisarão coexistir em grandes programas. Muitos pacotes não precisarão de nenhum trabalho - yaml e blackfriday, por exemplo, estão ambos na v2 que são completamente diferentes da v1, mas não há estado compartilhado para disputar, portanto, não há necessidade de coordenação explícita - mas outros sim.

@powerman @rsc
Estou desenvolvendo um pacote GUI, o que significa que não posso ter mais de 2 instâncias devido ao uso do thread "principal". Então, vindo do pior cenário de singleton, é isso que eu decidi fazer.

  • Só tem uma versão v0/v1, então é impossível importar 2+ versões

  • Tenha o código público em sua própria pasta de versão api, por exemplo, v1/v2 assumindo que vgo permite isso ou talvez api1/api2.

  • Esses pacotes de API públicos dependerão de um pacote interno, portanto, em vez de ter que reescrever em uma v2, é uma reescrita contínua à medida que o pacote cresce e é muito mais fácil de manusear.

Em https://github.com/golang/go/issues/24301#issuecomment -377529520, a alteração proposta define o código "novo" como código com um arquivo go.mod no mesmo diretório ou em um diretório pai. Isso inclui arquivos go.mod "sintetizados" criados a partir da leitura de dependências de um Gopkg.toml, por exemplo?

@zeebo , sim. Se você tiver um arquivo go.mod em sua árvore, a suposição é que seu código realmente é construído com vgo. Se não, então rm go.mod (ou pelo menos não verifique em seu repositório onde outros possam encontrá-lo).

@AlexRouSg , seu plano para o seu pacote GUI faz sentido para mim.

@rsc hmm .. Não tenho certeza se entendi e desculpe se não fui claro. Um pacote com apenas um Gopkg.toml na árvore de arquivos conta como "novo" para a definição?

@rsc

Quanto ao código gerado de forma mais geral, um sistema de compilação real entre linguagens é a resposta, especificamente porque não queremos que todos os usuários precisem ter os geradores corretos instalados. Melhor para o autor rodar os geradores e conferir o resultado.

Conseguimos resolver isso mapeando o protobuf no GOPATH. Sim, temos isso para que usuários casuais não precisem das ferramentas para atualizar, mas para aqueles que modificam e regeneram protobufs, a solução em protobuild funciona extremamente bem.

A resposta aqui é bastante decepcionante. Encontrar um novo sistema de compilação que não existe é apenas uma não resposta. A realidade aqui é que não vamos reconstruir esses sistemas de compilação e continuaremos usando o que funciona, evitando a adoção do novo sistema vgo.

O vgo simplesmente declara falência para aqueles que gostaram e adotaram o GOPATH e resolveram seus problemas?

@zeebo , não, ter um Gopkg.toml não conta como novo; aqui "novo" significa que se espera usar importações no estilo vgo (versão de importação semântica).

@stevvooe :

Conseguimos resolver isso mapeando o protobuf no GOPATH. ...
O vgo acaba de declarar falência para aqueles que gostaram e adotaram o GOPATH e resolveram seus problemas?

Eu não olhei para o seu protótipo, mas em geral, sim, estamos mudando para um modelo não GOPATH, e alguns dos truques que o GOPATH pode ter ativado serão deixados para trás. Por exemplo, o GOPATH habilitou o Godep original para simular a venda sem ter suporte de fornecedor. Isso não será mais possível. Em uma rápida olhada, parece que o protobuild é baseado na suposição de que ele pode soltar arquivos (pb.go) em outros pacotes que você não possui. Esse tipo de operação global não terá mais suporte, não. Eu sou completamente sério e sincero sobre querer ter certeza de que os protobufs são bem suportados, separados do vgo. @neild provavelmente estaria interessado em ouvir sugestões, mas talvez não sobre esse assunto.

@stevvooe dado os comentários de @rsc em https://github.com/golang/go/issues/24301#issuecomment -377602765 Fiz referência cruzada https://github.com/golang/protobuf/issues/526 no caso essa questão acaba cobrindo o ângulo vgo . Se as coisas acabarem sendo tratadas em outro lugar, tenho certeza que @dsnet et al nos indicarão.

Nota: não vi os comentários anteriores de perto, parece que o problema foi resolvido com abordagem diferente. Abaixo estava a minha ideia.

Apenas uma ideia.

Que tal criar uma tag específica para vgo get como vgo-v1-lock?
Quando um repositório tem a tag, ele pode ignorar outras tags de versão e ser fixado na tag.

Então, quando um repositório marcou v2.1.3 como última versão,
mas também o proprietário envia a tag vgo-v1-lock para o mesmo commit que está marcado como v2.1.3
poderia escrever go.mod

require (
    "github.com/owner/repo" vgo-v1-lock
)

Ele não deve ser atualizado mesmo que vgo get -u , até que o proprietário do repositório altere ou remova a tag.
Poderia tornar os grandes repositórios mais fáceis de preparar sua movimentação.

Quando um autor de biblioteca é preparado, o autor pode anunciar ao usuário
que eles poderiam atualizar manualmente colocando "/v2" no caminho de importação.

Como lidamos com o caso em que precisamos corrigir uma dependência profunda (por exemplo, para aplicar uma correção de CVE que o autor original ainda não lançou em uma tag). Parece que a estratégia do fornecedor pode lidar com isso, pois você pode aplicar um patch à versão original do autor. Não vejo como vgo pode lidar com isso.

@chirino você pode usar a diretiva replace no arquivo go.mod para apontar para o pacote corrigido.

@rsc

Em uma rápida olhada, parece que o protobuild é baseado na suposição de que ele pode soltar arquivos (pb.go) em outros pacotes que você não possui.

Isso não é, de forma alguma, o que o projeto faz. Ele cria um caminho de importação a partir do diretório GOPATH e do fornecedor. Quaisquer arquivos protobuf em seu projeto serão gerados com esse caminho de importação. Ele também faz coisas como importar mapas para pacotes Go específicos.

O benefício disso é que permite gerar protobufs em um projeto folha que são dependentes de outros protobufs definidos em dependências sem regenerar tudo. O GOPATH efetivamente se torna os caminhos de importação para os arquivos protobuf.

O grande problema com esta proposta é que perdemos completamente a capacidade de resolver arquivos em projetos relativos a pacotes Go no sistema de arquivos. A maioria dos sistemas de embalagem tem a capacidade de fazer isso, embora dificultem. GOPATH é único porque é muito fácil fazer isso.

@stevvooe Desculpe, mas acho que ainda estou confuso sobre o que o protobuild faz. Você pode registrar um novo problema "x/vgo: não compatível com protobuild" e dar um exemplo simples e trabalhado de uma árvore de arquivos que existe hoje, qual protobuild adiciona à árvore e por que isso não funciona com vgo? Obrigado.

E se o nome do módulo tiver que mudar (domínio perdido, mudança de propriedade, disputa de marca registrada, etc.)?

@jimmyfrasche

Como o usuário:
Então, como uma correção temporária, você pode editar o arquivo go.mod para substituir o módulo antigo por um novo, mantendo os mesmos caminhos de importação. https://research.swtch.com/vgo-tour

Mas, a longo prazo, você gostaria de alterar todos os caminhos de importação e editar o arquivo go.mod para usar o novo módulo. Basicamente a mesma coisa que você teria que fazer com ou sem vgo.

Como o mantenedor do pacote:
Basta atualizar o arquivo go.mod para alterar o caminho de importação do módulo e informar seus usuários sobre a alteração.

@jimmyfrasche ,

E se o nome do módulo tiver que mudar (domínio perdido, mudança de propriedade, disputa de marca registrada, etc.)?

Esses são problemas reais e pré-existentes que a proposta vgo não tenta abordar diretamente, mas claramente devemos abordá-los eventualmente. A resposta para o desaparecimento do código é ter proxies de cache (espelhos) junto com um motivo para confiar neles; isso é trabalho futuro. A resposta para a movimentação de código é adicionar um conceito explícito de redirecionamento de módulo ou pacote, assim como os aliases de tipo são redirecionamentos de tipo; isso também é trabalho futuro.

A resposta para o desaparecimento do código é ter proxies de cache (espelhos)

IMO que realmente é para as empresas. A maioria das pequenas empresas e outras ficariam perfeitamente bem em vender e comprometer todas as dependências no mesmo repositório

Arquivado # 24916 para a compatibilidade que mencionei no comentário acima.
Também arquivado #24915 propondo voltar a usar git etc diretamente em vez de insistir no acesso HTTPS. Parece claro que as configurações de hospedagem de código ainda não estão prontas apenas para API.

proposta menor para criar consistência em arquivos mod com o comando planejado vgo get

No documento "vgo-tour", o comando vgo get é mostrado como:

vgo get rsc.io/[email protected]

Que tal espelhar esse formato no arquivo mod? Por exemplo:

module "github.com/you/hello"
require (
    "golang.org/x/text" v0.0.0-20180208041248-4e4a3210bb54
    "rsc.io/quote" v1.5.2
)

poderia ser simplesmente:

module "github.com/you/hello"
require (
    "golang.org/x/[email protected]"
    "rsc.io/[email protected]"
)
  • melhora a consistência com a linha de comando
  • identificador único define o item completamente
  • melhor estrutura para suportar operações definidas no arquivo mod que requerem vários identificadores de pacote com versão

Buscando mais clareza sobre como esta proposta trata da distribuição de pacotes "somente binários".

o versionamento/distribuição da biblioteca binária não parece aparecer em nenhum dos documentos de descrição em torno do vgo. há necessidade de olhar para isso com mais cuidado?

Do jeito que funciona hoje, se eu puder usar a ferramenta simples git , go get funcionará bem. Não importa se é um repositório Github privado ou meu próprio servidor Git. Eu realmente amo isso.

Pelo que entendi, vai ser impossível continuar trabalhando assim. Isso é verdade? Se sim, é possível manter a opção de usar um binário git instalado localmente para fazer o checkout do código? (caso esteja usando um sinalizador CLI explícito)

@korya Por favor, veja o problema recentemente arquivado https://github.com/golang/go/issues/24915

@sdwarwick , re https://github.com/golang/go/issues/24301#issuecomment -382791513 (sintaxe go.mod), consulte #24119.

re https://github.com/golang/go/issues/24301#issuecomment -382793364, é possível que eu não entenda o que você quer dizer, mas go get nunca suportou pacotes somente binários e não estamos planejando para adicionar suporte. É muito difícil enumerar todos os possíveis binários diferentes que alguém pode precisar. Melhor exigir a fonte e poder recompilar quando as dependências ou o compilador forem alterados.

@rsc acredito que https://github.com/golang/go/issues/24301#issuecomment -382793364 esteja se referindo a
https://golang.org/pkg/go/build/#hdr -Binary_Only_Packages

@AlexRouSg Sim. Eles não são suportados por "go get" (mas "go build" os suporta como um tipo de compilação), que é o que @rsc está se referindo. A distribuição desses tipos de pacotes deve ser feita externamente ao ferramental "go get" e, portanto, provavelmente o mesmo para esta nova proposta.

Espero que o suporte atual para pacotes somente binários continue a funcionar tão mal quanto sempre funcionou. Eu não vou sair do meu caminho para removê-lo.

Atualizei o resumo da discussão novamente. Eu também arquivei #25069 para minha sugestão anterior sobre o mínimo de reconhecimento de módulo no antigo cmd/go.

@kybin , re https://github.com/golang/go/issues/24301#issuecomment -377662150 e a tag vgo-v1-lock , vejo o recurso, mas adicionar um caso especial para isso significa adicionar mais casos especiais por toda parte o resto do suporte do módulo. Eu não acho que o benefício é proporcional ao custo neste caso. As pessoas já podem usar pseudo-versões para obter um efeito semelhante. Também me preocupo que a tag seja movida e/ou as pessoas não respeitem adequadamente a compatibilidade com versões anteriores (por exemplo, movendo vgo1-v1-lock da v2.3.4 para v3.0.0 apenas para evitar o versionamento de importação semântica). Então, no geral, acho que provavelmente não deveríamos fazer isso.

Acho que é hora de marcar esta proposta como aceita.

Nunca houve qualquer suspense sobre se seria aceito de alguma forma. Em vez disso, o objetivo da discussão aqui era descobrir a forma exata, para identificar o que deveríamos ajustar. Como escrevi no post do blog :

Eu sei que há problemas com isso que a equipe Go e eu não podemos ver, porque os desenvolvedores Go usam Go de muitas maneiras inteligentes que não conhecemos. O objetivo do processo de feedback da proposta é que todos trabalhemos juntos para identificar e resolver os problemas na proposta atual, para garantir que a implementação final que será enviada em uma versão futura do Go funcione bem para o maior número possível de desenvolvedores. Por favor, aponte problemas na questão da discussão da proposta. Manterei o resumo da discussão e as perguntas frequentes atualizadas à medida que o feedback chegar.

O resumo da discussão e as perguntas frequentes estão atualizados a partir de agora. A discussão aqui e fora do assunto provocou as seguintes mudanças importantes:

  • consciência mínima do módulo no antigo cmd/go, #25069.
  • restaurando o suporte mínimo do fornecedor, #25073.
  • restaurando o suporte para acesso direto ao Git, #24915.
  • coloque aspas em go.mod, #24641.
  • melhor suporte para gopkg.in, #24099, outros.
  • suporte para nomear commits por identificadores de branch, #24045.

Isso também gerou discussões sobre a possibilidade de alterar o nome e a sintaxe do arquivo go.mod, mas a única alteração resultante foi a eliminação das aspas.

A discussão acabou, e também ficou longa o suficiente para ser bastante dolorosa para carregar no GitHub (aparentemente 100 comentários é demais!). Ao marcar a proposta como aceita, podemos nos concentrar em usar vgo e prepará-lo para inclusão no Go 1.11 como uma "visualização", e podemos passar para problemas que o GitHub pode carregar mais rapidamente.

Há, é claro, ainda mais bugs a serem encontrados e corrigidos, e mais ajustes de design a serem feitos. As discussões desses detalhes podem ser feitas em novas questões específicas para esses detalhes. Use um prefixo "x/vgo:" e o marco vgo .

Obrigado a todos.

@rsc Qual é a maneira de experimentar o vgo agora? Devemos buscar e construir github.com/golang/vgo ou github.com/golang/go?

@ngrilly continue usando go get -u golang.org/x/vgo .

@rsc obrigado pelo trabalho árduo e pelo tempo dedicado a esta proposta. Eu realmente espero que eventualmente cheguemos a um ponto em que haja uma boa história sobre o gerenciamento de dependências do Go.

Acho que é hora de marcar esta proposta como aceita.

Não acho apropriado que um indivíduo que envia uma proposta declare quando ela está pronta para ser aceita. Acho que aqueles que apresentam uma proposta, especialmente uma tão grande e opinativa, não deveriam ter uma opinião sobre ela ser aceita. Sua apresentação da proposta comunica seus preconceitos. Por outro lado, acho que os autores definitivamente deveriam ter uma palavra a dizer ao rejeitar a proposta se mudarem de ideia depois de apresentá-la.

Neste momento, parece que há divergências profundas em torno de decisões técnicas dentro de vgo , que temo que fragmentem o ecossistema _e_ a comunidade. Com isso em mente, acho que devemos dar um pouco mais de tempo para que as propostas concorrentes sejam concluídas e tenham seus holofotes. Quando isso acontecer, precisamos ter um partido _neutro_ que consista na representação de várias empresas e da comunidade, para facilitar a discussão e a decisão final.

Tenho uma preocupação crescente de que uma boa parte da liderança do Go tenha se tornado muito isolada da comunidade por trás do Go (incluindo outras empresas que o usam) e que a linguagem e o ecossistema estejam começando a prejudicar como resultado. Por isso acho que precisamos ter um grupo neutro, com representação que reflita os usuários da linguagem de programação Go, ajude a ratificar propostas como essa.

Em última análise, trabalhar como engenheiro de software dentro do Google resulta em uma perspectiva diferente em comparação com grande parte do setor. Ter a massa crítica dos principais desenvolvedores no Google não contribui para a diversidade de que precisamos ao impulsionar o Go Forward.

@theckman Sua linha de pensamento parece ser:

  1. As decisões sobre o Go são tomadas por uma pequena equipe composta principalmente por engenheiros do Google.
  2. Os engenheiros do Google estão isolados do resto da comunidade, porque as necessidades do Google são muito específicas.
  3. Isso leva a decisões que favorecem a perspectiva do Google e não são adaptadas a outros usuários do Go.
  4. Isso pode prejudicar e fragmentar a comunidade Go.
  5. Para resolver este problema, Go deve adotar um processo de decisão formal onde as decisões são tomadas colegialmente por um grupo que reflete a diversidade da comunidade Go (algo "democrático").

Em teoria, eu estaria inclinado a gostar de qualquer coisa "democrática", mas na prática:

  • Não acho que as decisões tomadas pela equipe do Go sejam tendenciosas para as necessidades do Google, em detrimento dos "pequenos" usuários do Go. Como desenvolvedor em uma loja muito pequena (bem o oposto do Google), sinto que o Go está muito bem adaptado às nossas necessidades e conheço outras pequenas equipes ao meu redor que usam Go com prazer. Pessoalmente, as únicas queixas que tenho com a linguagem Go foram reconhecidas pela equipe Go (gerenciamento de dependências, falta de genéricos, tratamento detalhado de erros) e estou confiante de que estão trabalhando nisso. Você poderia fornecer exemplos de decisões tomadas pela equipe Go que teriam sido diferentes e serviriam melhor "o resto da comunidade" se tivéssemos um processo "democrático"?

  • Não estou convencido de que adotar um processo de decisão "democrático" resolveria automaticamente os problemas que você mencionou e eliminaria o risco de "fragmentar" a comunidade. Pode ser uma melhoria em relação ao modelo BDFL , mas não é garantia de estabilidade per se. A história do código aberto e a história humana em geral fornecem muitos exemplos de sociedades democráticas que foram arruinadas por divergências e conflitos.

@theckman , enquanto @ngrilly estava tentando ser educado, serei concreto: se você vir algum problema técnico por que a proposta vgo não está pronta para ser aceita - diga-nos o mais rápido possível e aqui! Se você acredita que alguns problemas conhecidos não foram abordados adequadamente, informe-nos. Se não houver tais casos - qual é a diferença de quem dirá "é hora de aceitar a proposta"? Tivemos dois meses para discuti-lo, se você acredita que há uma razão técnica para que não seja suficiente - diga-nos.

Parece que você quer adicionar mais política aqui sem motivo, o que só vai desacelerar tudo, e isso se tivermos sorte o suficiente e não causar nenhum outro dano. E mesmo se você acredita que há uma razão para isso - este não é o lugar certo para discutir isso - por favor, inicie esta discussão na lista de e-mails.

Desculpem mais um comentário offtopic, pessoal!

@powerman Desculpe ser idiota e citar meu comentário anterior:

Neste momento, parece que há desacordos profundos em torno de decisões técnicas dentro do vgo, que temo que fragmentem o ecossistema e a comunidade. Com isso em mente, acho que devemos dar um pouco mais de tempo para que as propostas concorrentes sejam concluídas e tenham seus holofotes.

Eu sei que estão chegando propostas alternativas, e meu pedido foi colocar o kibosh em aceitar isso até que eles tenham a hora do dia. Não temos gerenciamento de dependências há algum tempo, então acho que não é razoável pedir uma estadia curta enquanto permite os retoques finais em outras propostas.

Não tenho certeza se é fácil articulá-lo bem nesta edição, apenas porque o formato é um pouco restrito. Mesmo assim, eu hesitaria, porque basicamente estaria duplicando partes das propostas do WIP. Eu não gostaria de fragmentar as ideias ou roubar o trovão dos autores.

No entanto, sabendo que essas propostas estão em andamento e quem está trabalhando nelas, parecia que a declaração de que isso estava pronto era uma tentativa ativa de sufocar essas opiniões concorrentes. Senti-me compelido a levantar essas preocupações por causa dos padrões que observei ao longo dos meus anos como Esquilo. Eu realmente quero que a linguagem, o ecossistema e a comunidade sejam bem-sucedidos, e eles estão sendo criados apenas com esse objetivo em mente.

Edit: Para esclarecer, por estadia curta não quero dizer dois meses ou algo assim. Quero dizer algumas semanas.

@ngrilly Desculpe, não quis ignorar seu comentário. Eu planejava abordar isso para o anterior, mas acabei sendo mais detalhado do que eu queria.

Acho que são duas questões. Embora eu sinta que há algo a ser discutido em um fórum diferente sobre como essas decisões são tomadas, e é por isso que adicionei algum contexto em torno disso, eu realmente quero me concentrar em colocar uma pausa temporária na aceitação desta proposta até que as outras propostas tenham teve a chance de se tornar público.

Eu sei que estão chegando propostas alternativas, e meu pedido foi colocar o kibosh em aceitar isso até que eles tenham a hora do dia. ... [Sabendo] que essas propostas estão em andamento e quem está trabalhando nelas, parecia que a declaração de que isso estava pronto era uma tentativa ativa de sufocar essas opiniões concorrentes.

Garanto-lhe que não foi. Eu fui bem claro sobre a linha do tempo aqui. Meu primeiro post de meados de fevereiro diz que o objetivo é integrar a proposta vgo no Go 1.11; as abordagens de congelamento de desenvolvimento. Não sei nada sobre outras propostas em andamento ou quem está trabalhando nelas. Isso é novidade para mim. Se as pessoas quiserem se engajar com essa proposta ou fazer contrapropostas, essa proposta está aberta há um mês e meio, então está ficando um pouco tarde.

Para ser claro, não marquei a proposta como aceita, apesar do que Golang Weekly e talvez outros relataram. Eu só disse que acho que é hora de fazê-lo. Ou seja, não apliquei o rótulo Proposta Aceita, exatamente porque queria verificar se havia consenso geral para fazê-lo primeiro. E o consenso geral parece ser a favor da aceitação, pelo menos a julgar pela discussão geral aqui, bem como pelos contadores de emoji em https://github.com/golang/go/issues/24301#issuecomment -384349642.

Eu sei que estão chegando propostas alternativas

@theckman , se você conhece algo assim, provavelmente é o único que sabe. Até agora, não vi ninguém levantando essa questão até agora. Acho que as declarações de Russ sobre querer tentar isso para o Go 1.11 foram muito claras desde o início, então se alguém estiver trabalhando em propostas alternativas, eles tiveram cerca de 2 meses para apresentá-las, mesmo como um aviso prévio.

Também acho que podemos aceitar o fato de que a Go Team tem um bom histórico de não tomar decisões por capricho, e se olharmos apenas como eles tiraram o Alias ​​no último momento do Go 1.8, pois não era o certo coisa a fazer no momento, então provavelmente deveríamos dar a eles a cortesia de pelo menos permitir que eles construíssem seu próprio experimento/solução.

No final das contas, a proposta traz muito mais do que apenas um algoritmo de como selecionar qual versão da dependência é utilizada. Se alguém descobrir uma maneira de melhorá-lo, há duas opções: submetê-lo através do processo regular de CL OU criar sua própria ferramenta e deixar a comunidade usá-la, caso opte por fazê-lo. A Go Team ainda pode fornecer sua própria versão da ferramenta imho, então não vejo que isso tenha um problema fechado.

No entanto, lembre-se de que a maior parte da ação divisória foi tomada até agora pela comunidade, não pela Go Team. Vamos dar ao Go Team a chance de construir uma ferramenta, e então avaliá-la, quando for viável fazê-lo, então trazer argumentos para a mesa sobre como melhorá-la e seguir em frente ao invés de escrever sobre o quão ruim ela é.

Por favor, considere isso como parte de um tipo diferente de relato de experiência: aquele em que eu era um oponente _muito_ vocal da proposta do Alias ​​para então entender e agora ver a proposta em ação.

Edit: a mensagem original tinha uma omissão muito infeliz record of *not* making decisions on a whim deveria ter sido o texto, infelizmente a parte "não" estava faltando. Minhas desculpas por isso.

eu tenho uma descrição detalhada das preocupações fundamentais com a proposta. Eu tenho tentado terminar este artigo para que eu possa apresentá-los todos de uma vez - este é um domínio complexo e sutil e, como tal, essas questões devem ser tratadas em sua totalidade - mas a vida e o trabalho tornaram isso difícil.

Embora eu tenha aludido a este artigo no Slack, bem como discutido partes dele diretamente com @rsc , optei por não mencioná-los aqui até agora. Pareceu-me que anunciar este artigo antes de estar preparado para lançá-lo totalmente não seria muito construtivo. Mas, como foi notado, já se passaram dois meses, então vou fazer um grande esforço para começar a série na próxima semana.

(edit: essas são as "alternativas" a que @theckman estava se referindo)

@sdboyer Você mencionou que tem várias preocupações. Você poderia, por favor, pelo menos publicar a lista deles agora?
Estou trabalhando com vários sistemas que levam o inferno da dependência para outro nível (chef, go, npm, composer) e por experiência própria esta proposta é solução para todos eles em relação ao go.

@theckman , você pode confirmar que estava se referindo apenas ao feedback de @sdboyer ? Isso não é segredo. Sam mencionou isso literalmente no primeiro dia em que o vgo foi lançado ("estou escrevendo documentos mais detalhados sobre minhas preocupações" - https://sdboyer.io/blog/vgo-and-dep/). Mas isso é um feedback, não uma proposta, e você se referiu várias vezes a "outras propostas", no plural. Existe mais que você sabe?

Quais são as implicações do vgo para usuários da API go/types? Qual é o status atual do suporte a go/types?

Recebi um PR mdempsky/gocode#26 para adicionar uma implementação vgo-aware go/types.Importer, mas não está claro para mim se/por que isso é necessário.

Supondo que seja necessário, podemos adicionar um go/types.Importer canônico com reconhecimento de vgo em outro lugar (por exemplo, o repositório x/vgo ou x/tools) para que as ferramentas baseadas em go/types não precisem reimplementar esse suporte ?

Eu realmente não segui os detalhes do vgo, então talvez isso seja simplesmente "sem impacto", mas não vejo nenhuma menção de go/types acima. As pesquisas no Google por "vgo go/types golang" também não são informativas.

Obrigado.

@mdempsky , o plano é ter um carregador de pacotes com reconhecimento de vgo (e, nesse caso, com reconhecimento de cache), provavelmente golang.org/x/tools/go/packages, mas ainda não existe. As pessoas devem esperar por esse pacote em vez de escrever código que precisará ser jogado fora. Não mescle o PR. Eu comentei sobre isso.

@mdempsky ia responder em https://github.com/mdempsky/gocode/pull/26 mas vou responder aqui por enquanto.

https://github.com/mdempsky/gocode/pull/26 é totalmente descartável; apenas uma prova de conceito que usa um CL agora abandonado contra vgo .

Acabei de ver @rsc responder, então vou simplesmente apontar que também há uma discussão em https://github.com/golang/go/issues/14120#issuecomment -383994980.

Resumo: após alguns anos de experiência com versões, a última melhor tentativa foi um algoritmo muito complexo, um solucionador sat. Mas se você fizer algumas modificações simples nos dados de entrada, o problema de decisão NP-completo se torna algo não apenas gerenciável, mas muito rápido.

Como usuário de Go de pequena equipe com muita experiência no uso de NPM, gosto muito da proposta vgo. FWIW, estou ansioso para que o vgo seja implementado em go e quanto mais cedo isso acontecer, melhor para minha equipe e para mim.

Não que eu seja alguém especial, só que, desde que vi a discussão sobre problemas de equipe pequena, pensei em entrar na conversa.

Aqui está a minha revisão.

Na superfície, a seção _Proposta_ da última revisão do documento da proposta parece boa, se não muito específica (por exemplo, não está claro até que ponto o Subversion pode ser suportado). Uma exceção é que “Proibir o uso de diretórios de fornecedores, exceto em um uso limitado” parece dizer que os diretórios de fornecedores não serão suportados em pacotes que não sejam módulos; é assim mesmo?

Por outro lado, a proposta implica certas decisões de desenho e implementação que prejudicam vários benefícios do atual go get . As perdas podem ser aceitáveis, algumas podem ser evitadas, mas se vgo get for substituir go get , elas devem ser tratadas como considerações de design e discutidas, porque senão podemos acabar com uma ferramenta que é não é um substituto adequado, e vgo não será mesclado em go ou go get terá que ser ressuscitado como uma ferramenta de terceiros.

A seção _Implementation_ diz: “Em uma versão posterior (digamos, Go 1.13), encerraremos o suporte para go get de não-módulos. O suporte para trabalhar no GOPATH continuará indefinidamente.” Isso é problemático. Primeiro, há muitos projetos bons que não são atualizados há anos. Eles ainda funcionam graças à promessa de compatibilidade Go 1, mas muitos não adicionarão arquivos go.mod . Segundo, força os desenvolvedores de projetos sem dependências ou aqueles que não se importam com versões a adicionar arquivos de módulo ou se abster do novo ecossistema go get . Você pode ter razão para querer isso, mas por favor explique o porquê. (Para mim parece desnecessariamente complicado; prefiro usar o fork do antigo go get . Concordo que gerenciadores de pacotes para outras linguagens são ainda mais complicados, e tenho certeza de que vgo é melhor do que eles, mas não lida com meus casos de uso melhor do que o atual go get , com ajuda ocasional do govendor ).

Minha principal preocupação sobre vgo vs go é com o fluxo de trabalho que eles suportam. Eu havia expressado isso no post vgo-intro. Isso pode pertencer amplamente à seção _Compatibilidade_ da proposta, ou pode estar fora de seu escopo, mas corresponde a outras questões e questões levantadas aqui.

Para referência, aqui está uma cópia do meu comentário vgo-intro.

Em algumas versões posteriores, removeremos o suporte para o go get antigo e não versionado.

Embora outros aspectos da proposta pareçam bons, este é lamentável. (Tanto que se eu tivesse que escolher entre incorporar versionamento na cadeia de ferramentas go e continuar trabalhando com ferramentas de controle de versão, eu escolheria a última.) A vantagem do vgo é que ele facilita compilações reproduzíveis e atrasa a quebra do seu projeto devido a atualizações incompatíveis até que você como autor do projeto (com o arquivo go.mod) queira enfrentá-lo; mas a vantagem do go get é que ele traz os benefícios de um monorepo para o mundo multi-repositório: devido aos clones completos das dependências, você pode trabalhar com eles tão facilmente quanto com seu próprio projeto (inspecionar histórico, editar, diff changes; ir para a definição de qualquer coisa e culpá-lo), facilita a colaboração (você simplesmente empurra e propõe suas mudanças) e geralmente impõe a visão de que a qualquer momento há apenas um estado atual do mundo - a ponta de cada projeto - e tudo outra coisa é história. Eu acho que essa abordagem única (fora dos monorepos reais) é uma vantagem distinta do ecossistema Go que fez mais bem do que mal, e não deve ser abandonada.

Uma consequência negativa mais sutil da proposta é que ela torna o controle de versão incurável e herdável: uma vez que um projeto marca uma versão, ele não pode esperar que mudanças futuras cheguem aos usuários sem marcar novas versões. Mesmo que o autor original permaneça determinado a manter a marcação, os autores dos forks agora são forçados a marcá-los também (o que é particularmente estranho se o projeto de origem ainda estiver ativo) ou a excluir tags antigas.

No geral, quero enfatizar que a abordagem atual do Go para gerenciamento de dependências é superior ao controle de versão. Ele se alinha melhor com o código aberto moderno, dinâmico e colaborativo que espera que todos os commits sejam visíveis, e publicar apenas as fontes de lançamentos (ou "integrar mudanças internas" em grandes commits não descritivos) não é suficiente (porque reduz severamente a visibilidade, colaboração e dinamismo). Pode ser visto tanto em monorepos quanto no atual ecossistema Go que a maioria dos projetos não precisa de versões. Claro que essa abordagem não é a melhor, ela tem desvantagens, e é importante dar suporte a projetos versionados também, mas isso não deve ser feito em detrimento dos sem versão.

Para resumir, o atual go get (com ferramentas auxiliares, por exemplo, godef ) suporta o fluxo de trabalho que apresenta:

  • código fonte editável das dependências
  • código-fonte das dependências sob seu VCS
  • últimas revisões das dependências

Acho que posso assumir que o código-fonte das dependências permanecerá editável, ou seja, godef será vinculado a _arquivos_ que _não são protegidos contra gravação_ e _usados ​​durante a compilação_. No entanto, vgo vai renegar os outros dois pontos. Com relação ao segundo ponto, #24915 prolongou o suporte para ferramentas VCS, mas ainda declara o objetivo de abandoná-lo; e o fluxo de trabalho requer não apenas que as dependências sejam retiradas do VCS, mas também que o checkout seja útil para desenvolvedores (por exemplo, não um checkout git superficial, não um checkout git com .git removido) e seja usado durante a compilação , mas vgo pode não atender a esse requisito. Com relação ao terceiro ponto, justifiquei seu valor no comentário vgo-intro, mas vgo parece abandoná-lo completamente.

Uma ferramenta de versão Go não precisa abandonar o suporte para o fluxo de trabalho atual e não deve abandoná-lo para manter os benefícios exclusivos de trabalhar no ecossistema Go e ser um substituto adequado para go get . O design de vgo torna isso desafiador, mas não obviamente inviável. A seção _Proposta_ da proposta, por outro lado, parece quase compatível com o fluxo de trabalho atual. O único desafio que apresenta para apoiar o terceiro ponto (verificar a última revisão) — e é um grande problema — é que torna difícil decidir para os módulos ≥v2.0.0 se eles podem ser verificados em master , ou se eles precisam ser retirados conforme especificado porque master está em outra versão principal. Este não é um desafio para o atual go get com gopkg.in porque tudo é verificado em master por padrão, e o material em gopkg.in é verificado na tag ou branch correspondente; mas vgo borra essa distinção e espalha o modelo gopkg.in em todos os pacotes. (Além disso, ele para de fazer a correspondência de ramificações.) Na verdade, torna-se impossível saber com certeza e é necessário adivinhar como obter a revisão mais recente da versão principal especificada.

Eu posso ter perdido, mas como vgo funcionaria neste cenário?

  • Eu trabalho no serviço A e no serviço B, ambos dependendo da lib X (na qual também trabalho)
  • Eu preciso de uma grande mudança na lib X

Com as formas atuais de fazer, eu apenas faço minhas alterações, compilo o serviço A e o serviço B, eles pegam o que estiver no meu $ GOPATH para a lib X, eu corrijo coisas, então eu empurro a lib X com um grande semver bump e, em seguida, empurro os dois serviços A e B dizendo a eles para usar o novo major da lib X em seu Gopkg.toml .

Agora, quando o vgo assumir, go build nos meus serviços tentará encontrar uma nova versão não existente da lib X do github, e posso prever todos os tipos de problemas.

Então, estou perdendo algo óbvio?

Você pode usar a diretiva replace para esse tipo de coisa.

Em segunda-feira, 30 de abril de 2018, 12:15 Antoine [email protected] escreveu:

Eu posso ter perdido, mas como vgo funcionaria neste cenário?

  • Eu trabalho no serviço A e no serviço B ambos dependendo da lib X (que eu
    também trabalhar)
  • Eu preciso de uma grande mudança na lib X

Com as formas atuais de fazer, apenas faço minhas alterações, compile o serviço A e
serviço B, eles pegam o que está no meu $ GOPATH para lib X, eu conserto coisas,
então eu empurro lib X com um grande semver bump e, em seguida, empurro os dois serviços A e B
dizendo a eles para usar o novo major da lib X em seu Gopkg.toml.

Agora, quando o vgo assumir, vá construir nos meus serviços, tentará encontrar não
nova versão existente da lib X do github, e posso prever todo tipo de
problemas.

Então, estou perdendo algo óbvio?


Você está recebendo isso porque foi mencionado.
Responda a este e-mail diretamente, visualize-o no GitHub
https://github.com/golang/go/issues/24301#issuecomment-385499702 ou silenciar
o segmento
https://github.com/notifications/unsubscribe-auth/AAuFsfnD8_kbUj8fSXvGgeN77ki6KYM6ks5tt2LLgaJpZM4Sg3bp
.

@kardianos sim, mas isso ainda significa que eu tenho que empurrar minhas alterações de lib para tentar?

EDIT: parece que você pode usar caminhos para substituir (https://github.com/golang/go/issues/24110), o que é bom. Mas também posso prever que isso acabará sendo cometido muitas vezes.

Algum plano para poder criar um arquivo adicional, como go.mod.replace ou algo assim, para que possamos definir as substituições em ambientes dev e ignorá-las?

@primalmotion Para evitar commits ruins, você provavelmente deveria usar um git hook.

Mas suponho que a resposta certa seja apenas não faça isso (substitua para o caminho local) com muita frequência. Se sua biblioteca é tão fortemente acoplada, ela não deve viver em um repositório separado (e, portanto, fingir que é fracamente acoplada). Em geral, você deve ser capaz de corrigir, testar e liberar essa lib sem levar em conta a implementação atual desses serviços. Especialmente com vgo, que garante que ambos os serviços continuarão usando a versão mais antiga da lib (que eles estavam usando antes) até que você os atualize manualmente para a versão mais recente dessa lib. Se você ocasionalmente comprometer a substituição para caminhos locais uma vez por ano - não é grande coisa, o CI ajudará você a perceber e corrigir isso em minutos.

@primalmotion com um go.mod.replace não versionado, você pode confirmar um projeto que não será reproduzível. Em vez disso, com replace em go.mod você tem certeza de que confirma exatamente o que você usa e testa atualmente.
Pode ser um erro se comprometer com uma substituição, mas você notará e corrigirá.
Ou pode ser voluntário, eu faço isso com submódulos, tudo bem quando você trabalha em um projeto e uma lib juntos e é reproduzível se você confirmar o submódulo. Eu fiz isso com frequência em Python e perdi com Go. Com vgo estou feliz por poder trabalhar assim novamente. _(espero ser claro com meu mau inglês, desculpe)._

Bem, o problema é que não nos importamos com compilações reproduzíveis até decidirmos que nos importamos (ou seja, quando preparamos lançamentos). Temos um ambiente de desenvolvimento muito ágil onde atualizar uma lib é apenas fazer check-out, reconstruir, executar testes. Nós não submetemos o Gopkg.lock nas ramificações master de nossos serviços, apenas o toml com versões fixas para libs externas, e grandes restrições nas nossas. Uma vez que criamos uma ramificação de lançamento, fazemos o commit do Gopkg.lock e só então temos compilações reproduzíveis (isso é feito pelo nosso ci).

O Vgo basicamente quebra todos os fluxos de trabalho que construímos ao longo dos anos. Agora apenas para tentar algo tão idiota quanto uma pequena depuração de impressão em uma lib (porque todos nós fazemos isso, não minta :)), ou pouca otimização, teremos que passar por dezenas de serviços, adicionar replace diretivas em todos os lugares, teste, depois volte e remova todas elas.

O que poderia fazer o vgo funcionar para nós:

  • um sistema de substituição como mencionei
  • uma maneira de não usá-lo e voltar ao bom e velho GOPATH para desenvolvimento.

E nós realmente queremos que funcione para nós, porque é incrível.

Podemos ter algo para testar em go 1.11 (está em freeze agora) ou talvez ir 1.12 ?
Já está marcado como experimental. E acho que quanto mais pessoas testarem em desenvolvimento real, mais valioso será o feedback.

Eu li sobre o problema em relação a pacotes versionados. Para um cenário simples, se eu escrever uma biblioteca que diga usar um dep para analisar plist chamado foo-plist . Para a consequência da análise, essa biblioteca plist expõe certos tipos próprios. Agora, essa biblioteca é atualizada para v2, e minha biblioteca é forçada a atualizar para v2 se eu retornar quaisquer objetos desses tipos de plist.

Isso parece ser bastante difícil de resolver, por exemplo, se eu desejar que minha biblioteca suporte tanto v1 quanto v2 da dita biblioteca plist sob a impressão desta proposta.

Sob npm, por exemplo, minha biblioteca pode simplesmente especificar uma dependência de pares para dizer >=1.0.0|>=2.0.0 e cabe ao usuário da minha biblioteca decidir qual versão do plist usar. Portanto, se o usuário da minha biblioteca foo-plist também usar outra biblioteca que dependa de plist , se ambas as bibliotecas estiverem satisfeitas com v1 e v2 de plist, o usuário poderá escolher quais realmente importar. Mais importante, se ambas as bibliotecas exportarem tipos plist, esses tipos serão realmente compatíveis.

Se eles acabarem sendo caminhos de importação diferentes, não acho que haja maneiras de oferecer suporte a isso.

@itsnotvalid foo-plist.2 pode importar foo-plist e reexportar seus tipos usando aliases de tipo. Uma boa descrição desta técnica pode ser encontrada aqui https://github.com/dtolnay/semver-trick

Aram fez uma observação aqui sobre a dificuldade de retroportar alterações para ramificações (ou diretórios) de versões anteriores. Como as referências "próprias" a fontes dentro do mesmo módulo também incluem a versão no caminho de importação, os patches não serão importados de forma limpa ou pode-se introduzir inadvertidamente importações entre versões.

Pensamentos?

Para ser claro, estou totalmente confortável com importações de módulos cruzados usando versões no caminho de importação; Acho que os argumentos de Russ foram muito convincentes. Estou menos claro sobre eles para importações dentro do módulo. Eu entendo o objetivo de ter um único caminho de importação para um determinado pacote em uma compilação (independentemente de estar sendo importado entre módulos ou dentro de seu próprio módulo), mas se eu tivesse que escolher, preferiria ter uma solução para problema de Aram do que manter esta propriedade. (Além disso, imagino que o sistema de compilação possa injetar a versão do arquivo mod ao construir um módulo de nível superior e, literalmente, injetá-lo na fonte após baixar uma dependência de módulo)

@rsc existe alguma chance de avançarmos com isso? Não vejo nada de importante impedindo isso.

Desculpe se pareço impaciente com isso, mas há mais e mais ferramentas trabalhando no suporte ao vgo e quanto mais adiamos isso, mais vai atrapalhar para nós, mantenedores de ferramentas, ir e voltar sobre isso .

@sdboyer planeja publicar seus artigos esta semana. Eu diria que é justo esperar por eles.

Eu acho que o algoritmo de resolução para selecionar quais versões de dependência não deve ser a causa de bloquear toda a proposta, que também contém coisas como módulos, suporte a proxy, versionamento e assim por diante.

E se, mais tarde, decidirmos melhorar/alterar o algoritmo para baixar essas dependências, podemos fazê-lo sem afetar mais nada.

Concordo com @dlsniper - já estamos bem no congelamento e já se passaram quase três meses desde que o design vgo foi introduzido. Se seu trabalho em 1.11 atrasar ainda mais, eu me preocupo que ele seja adiado para 1.12.

O primeiro post do que será uma série finalmente está no ar. Minhas desculpas por ter demorado tanto para ser publicado, e que ainda demorará mais para que a série seja concluída. Mas, o primeiro post fornece uma ampla visão geral dos tópicos que pretendo cobrir em toda a série, para que as pessoas possam pelo menos ter uma noção do escopo.

Resumidamente: há muitas coisas boas no vgo, muitas das quais nós, como comunidade, desejamos há muito tempo. No entanto, acredito que o MVS é inadequado para o propósito e não deve chegar à produção. É uma pena que tantas dessas coisas que queremos tenham vindo em torno do MVS, especialmente quando tão poucas delas são específicas para ele. estou trabalhando em uma abordagem alternativa, que é referenciada para fins de comparação em toda a série do blog e será articulada no post final.

O algoritmo central alternativo em que estou trabalhando provavelmente será bastante simples para migrar go.mod , então não prevejo que teríamos problemas, provavelmente seria possível deixar isso avançar como está, se um arquivo de bloqueio separado foi adicionado que contém o fechamento transitivo de dependências e tem _that_ ser o que o compilador lê, em vez do algoritmo da lista de compilação. (Há outras razões para um arquivo de bloqueio também, embora isso esteja no post 5.) No mínimo, isso nos dá uma válvula de escape.

No entanto, se dissermos que o MVS está OK, mesmo como um paliativo, ele entra e ganha a vantagem da inércia. Nesse ponto, temos que provar que é inadequado para @rsc (embora, na verdade, ele tenha definido isso como o padrão antes mesmo de ser mesclado), e ele acredita que esta é uma afirmação verdadeira sobre go get , agora:

Hoje, muitos programadores não prestam atenção ao controle de versão, e tudo funciona bem.

Dado tudo o que foi dito acima, meu medo é que deixar isso seguir em frente criará “genéricos, segunda rodada” – exceto que desta vez é em torno de regras que governam como interagimos uns com os outros, não com máquinas.

No entanto, se dissermos que o MVS está OK, mesmo como um paliativo, ele entra e ganha a vantagem da inércia.

Observe que agora o dep tem a vantagem da inércia (tanto por ser construído nas mesmas premissas que os gerenciadores de versão de outros idiomas quanto por existir por mais tempo com amplo suporte da comunidade). Pelo menos para mim, a proposta vgo ainda conseguiu superar essa inércia por ser um bom design, sustentado por bons argumentos.

Pessoalmente, não me importo se o versionamento em Go atrasar, sou a favor de fazer algo certo em vez de rápido. Mas pelo menos no momento, vgo ainda parece a solução certa para mim (e AIUI muitas pessoas na comunidade percebem isso como um problema urgente).

@mvdan Essa mudança não deixa de ter repercussões. Todos nós queremos isso, mas acho que também é sábio dedicar um tempo extra para acabar com as dúvidas, especialmente quando a pessoa que levanta essas dúvidas claramente pensou bastante no problema.

Eu continuo ouvindo menção ao congelamento e seu impacto na obtenção de uma visualização de vgo em 1.11 . Qual é a linha oficial neste momento sobre se isso será integrado para 1.11 ? Esta questão parece-me surpreendentemente tranquila, considerando o impacto potencial.

@sdboyer qual é a sua posição sobre se isso deve ser fundido em 1.11 considerando que você apenas _apenas_ declarou oficialmente, publicamente, sua posição no MVS?

Se isso não chegar a 1.11 , não teremos isso oficialmente disponível para visualização até 1.12 em fevereiro de 2019 e lançado oficialmente até pelo menos 1.13 em agosto 2019. Isso coloca o lançamento em potencial mais cedo 18 meses depois que @rsc começou a discuti-lo. É claro que não devemos apressar isso desnecessariamente, mas como @Merovius afirmou acima, muitas pessoas, inclusive eu, consideram uma resposta oficial ao gerenciamento de dependências uma questão “urgente”. Esperar 18 meses parece excessivo.

Certamente parecia que dep seria a resposta oficial e nós convertemos nossos repositórios para ela (e ficamos satisfeitos com seus resultados). Com esta proposta efetivamente depreciando dep para uso a longo prazo, mas ainda sem uma maneira oficial de começar a integrar vgo (pelo menos até que #25069 seja mesclado), ficamos na posição insatisfatória de sermos forçados a use uma ferramenta (com dep ) que sabemos que tem uma vida útil muito limitada.

FWIW, eu absolutamente acho que devemos avançar com isso integrando vgo como uma proposta em 1.11 e incluindo #25069 em 1.11 (e como versões de patch para 1.9 e 1.10 no lançamento de 1.11 ).

Eu honestamente não groco todas as implicações das preocupações de MVS e @sdboyer sobre isso. No entanto, considerando sua experiência neste espaço, acho que essas preocupações merecem séria consideração. Dito isto, se ele está a bordo com a integração vgo com MVS em 1.11 (enquanto entende que sua proposta [ainda em evolução], se aceita [por 1.12 ], não deve quebrar módulos projetado originalmente para MVS), então não vejo razão para não seguir em frente.

Também gostaria de agradecer a @rsc por esta proposta. Eu aprecio que Go não apenas copiou a abordagem de outra ferramenta e está tentando resolver esse problema de uma maneira que parece idiomática. Embora o gerenciamento de dependências nunca seja divertido, certamente parece que, com essa proposta, o Go tem o potencial de impulsionar o setor e possivelmente até saltar sistemas que atualmente são considerados os melhores.

Apenas para adicionar meus $.02, minha opinião é que o MVS é um momento “ah ha” para gerenciamento de dependências. Eu aprecio a quantidade de pensamento que as pessoas colocaram sobre isso, mas continuo convencido de que o MVS é onde isso precisa ir.

Eu concordo especialmente com os pontos que outros levantaram: “correções de segurança automáticas” são um sonho impossível na melhor das hipóteses e uma enorme lata de worms na pior das hipóteses.

Além disso, estou com @joshuarubin : uma resposta oficial ao gerenciamento de dependências é uma questão urgente. Outros comentaram que poderíamos avançar com o MVS agora e depois mudar para outras soluções, se necessário; se isso for realmente possível, acho que é o melhor caminho a seguir.

Proponho desacoplar as versões principais dos caminhos de importação da seguinte maneira. (Acredito que expliquei o raciocínio no vgo-import e que não degradei as conquistas do vgo declaradas lá.) Isso é inspirado na ideia de #25069 de que go build em Go 1.9 e 1.10 devem aprender a interpretar de forma criativa os caminhos de importação (soltando a parte da versão); na minha proposta o velho go build não muda, mas vgo aprende um truque similar.


Sintaticamente, as únicas mudanças são que:

  1. Em arquivos .go , import "repo/v2/pkg" permanece import "repo/v2/pkg" se v2 for um diretório, mas se tornará import "repo/pkg" caso contrário. Isso mantém a compatibilidade com o atual go get .
  2. Em arquivos go.mod , module "repo/v2" permanece o mesmo se estiver no subdiretório v2 , mas se torna module "repo" se estiver no nível superior. (Este é o prefixo de importação canônico.)
  3. Em arquivos go.mod , você também pode escrever require repo v2.3.4 as repo2 . Então em arquivos .go você usará import "repo2/pkg" (ou import "repo2/v2/pkg" se v2 for um diretório). Isso não será importável pelo atual go get (a menos que você use algo como require github.com/owner/project v2.3.4 as gopkg.in/owner/project.v2 ), mas isso só é necessário quando você deseja usar várias versões principais no mesmo módulo e a dependência não armazene as versões principais em subdiretórios, que não podem ser suportados pelo atual go get qualquer maneira.

Tecnicamente, isso permite que você escreva go.mod com:

require repo v1.0.0
require repo v1.1.1 as repo1
require repo v2.2.2 as repo2
require repo v2.3.3 as repo3

mas a seleção de versão mínima resolverá isso de forma que repo e repo1 se refiram ao repositório em v1.1.1 e repo2 e repo3 a v2.3.3 . Não sei se esse aliasing deve ser permitido ou proibido.


Vantagens:

  • o código com reconhecimento de módulo será compatível com o go get atual, mesmo após a v2.0.0; consequentemente:

    • não há necessidade de tornar go get minimamente ciente do módulo (#25069)

    • projetos anteriores à v2.0.0 não terão que quebrar a compatibilidade com o módulo que não reconhece go get

  • os projetos não terão que esperar que suas dependências se tornem módulos antes de se tornarem módulos [1]
  • não há necessidade de descontinuar projetos que não reconhecem o módulo ou desencorajar os autores de iniciar novos projetos que não reconhecem o módulo
  • mais fácil manter o suporte para o fluxo de trabalho sem versão do atual go get (explicado aqui e acima )

Desvantagens:

  • pode ser inconveniente manter a promessa de que os arquivos go.mod já escritos continuarão a funcionar (a menos que o novo arquivo do módulo tenha um nome diferente de go.mod )

Ambivalências:

  • o mesmo caminho de importação em módulos diferentes pode se referir a diferentes versões principais

    • bom: mais fácil de manter após a v2.0.0 e na mudança de versão principal

    • ruim: você não sabe qual versão principal você usa sem olhar para go.mod

  • módulos podem definir prefixos de importação arbitrários para uso em seu código

    • alguns usuários escolherão importar tudo por um nome curto (por exemplo import "yaml" com require gopkg.in/yaml.v2 v2.2.1 as yaml )

[1] Atualmente vgo pode suportar adequadamente dependências não modulares apenas desde que nenhuma dependência transitiva não modular de um módulo tenha passado da v2.0.0. Caso contrário, o projeto terá que esperar que todas as dependências que dependem indiretamente de um projeto anterior à v2.0.0 se tornem módulos.

Fiz uma análise dos arquivos Gopkg.toml que consegui encontrar em pacotes em https://github.com/rsc/corpus e escrevi um resumo em https://github.com/zeebo/dep-analysis. Com base nos dados, parece que não há muitas evidências de que o vgo não seria capaz de lidar com quase todos os casos de uso identificados.

Eu realmente espero que isso ajude a reduzir o medo na comunidade e ajude-a a chegar a um acordo de que devemos avançar com a proposta como está, lembrando que haverá mais 6 meses para obter experiência real com a ferramenta e tornar quaisquer alterações necessárias para corrigir quaisquer problemas que possam surgir.

Se eu citar você:

Quase metade de todas as restrições não são realmente restrições: elas apontam para o branch master.

Isso provavelmente ocorre porque apenas o mestre existe sem tags ou "ramificações nomeadas como v2, v3" Se for esse o caso, a comparação não é justa porque você não tem escolha!

@mvrhov Não tenho certeza do que você quer dizer com "não é justo". Parece-me que vgo e dep lidariam bem com esse caso. Ou melhor, qualquer alternativa realista teria que lidar bem com esse caso. Em particular: Se ainda não houver uma versão lançada, em um mundo vgo eles poderiam ser marcados apenas como v1.0/v0.xe nenhuma mudança em nenhum caminho de importação (a principal idiossincrasia do vgo) seria necessária.

O objetivo da análise, até onde posso dizer, é tentar estimar a dor do mundo real causada pelas diferentes abordagens. Não vejo como este caso introduz dor real para alguém.

Esta proposta está aberta com discussões ativas há mais de dois meses: @rsc e @spf13 conduziram sessões de feedback e reuniram informações valiosas da comunidade que resultaram em revisões da proposta. @rsc também realizou reuniões semanais com @sdboyer para obter mais feedback. Houve feedback valioso fornecido sobre a proposta que resultou em revisões adicionais. Cada vez mais, esse feedback é sobre a implementação que acompanha e não sobre a proposta. Após uma análise considerável, sentimos que é hora de aceitar esta proposta e permitir que o amplo ecossistema de implementadores de ferramentas da Go comece a fazer ajustes críticos para que nossa base de usuários possa ter a melhor experiência possível.

Houve duas objeções a esta proposta que sentimos que devemos falar:

  1. A proposta exigirá que as pessoas mudem algumas de suas práticas em relação ao uso e liberação de bibliotecas.
  2. A proposta não oferece uma solução técnica para todos os possíveis cenários que possam surgir envolvendo incompatibilidades.

Estes são precisos em sua observação, mas funcionam como pretendido. Autores e usuários de código _will_ terão que mudar algumas de suas práticas em relação ao uso e liberação de bibliotecas, assim como os desenvolvedores se adaptaram a outros detalhes do Go, como a execução do gofmt. Mudar as melhores práticas às vezes é a solução certa. Da mesma forma, vgo não precisa lidar com todas as situações possíveis envolvendo incompatibilidades. Como Russ apontou em sua recente palestra na Gophercon Singapore , a única solução permanente para a incompatibilidade é trabalhar em conjunto para corrigir a incompatibilidade e manter o ecossistema de pacotes Go. Soluções temporárias em uma ferramenta como vgo ou dep só precisam funcionar o tempo suficiente para dar aos desenvolvedores tempo para resolver o problema real, e vgo faz esse trabalho bem o suficiente.

Agradecemos todos os comentários e a paixão que você trouxe para esta questão crítica. A proposta foi aceita.

— O Comitê de Revisão da Proposta Go

Para adicionar um pouco de cor ao registro, as reuniões semanais com @sdboyer não devem ser vistas como um endosso. Sam recentemente começou a escrever sobre os problemas com o MVS junto com as coisas que ele gosta no vgo . Estou adicionando isso para garantir que não haja alguma falha de comunicação com qualquer outra pessoa que apareça. Se você quer a opinião dele, por favor leia suas palavras. Minha opinião é que eles contêm uma quantidade razoável de desacordo com a abordagem atual pretendida.

@mattfarina FWIW, li essa frase mais como "estamos cientes de sua crítica (como ele a expressou em particular) e isso não mudou nossa opinião". É lamentável que suas opiniões e argumentos não sejam públicos a essa altura.

Parece irresponsável aceitar uma proposta enquanto ainda existem preocupações fundamentais pendentes sobre a abordagem. O consenso entre o autor e o especialista em domínio da comunidade @sdboyer parece ser um padrão mínimo razoável a ser alcançado antes que a proposta seja considerada aceita.

@merovius Vários de nós compartilhamos opiniões públicas e privadas. Muitas pessoas acham que os problemas levantados foram superados (às vezes de forma grosseira) em vez de receberem soluções suficientes. Estou começando a compartilhar problemas práticos publicamente para que possamos tentar resolvê-los. Por exemplo, hoje eu fragmento alguns detalhes sobre um problema prático aqui . Nota lateral engraçada, isso estava na primeira página das notícias sobre hackers ao mesmo tempo em que foi marcado como aceito.

@peterbourgon Como uma pessoa que fez a pesquisa Go Dependency Management e que trabalhou no Glide, onde ouvi as necessidades das pessoas e tentei fazer isso funcionar, posso mostrar problemas práticos (em vez de apenas opiniões). Ou seja, posso combinar desejos, necessidades e expectativas dos usuários para soluções para esses problemas. Minha preocupação é a incompatibilidade disso para o caminho atual do vgo. Existem necessidades não atendidas e problemas pragmáticos devido a diferenças em como as pessoas fazem o gerenciamento de dependências.

Uma maneira fácil de começar a aliviar minhas preocupações é fazer o vgo funcionar para o Kubernetes para a satisfação de Tim Hockins.

Uma maneira fácil de começar a aliviar minhas preocupações é fazer o vgo funcionar para o Kubernetes para a satisfação de Tim Hockins.

Fazer o vgo funcionar para o Kubernetes hoje ou fazer o vgo funcionar para o Kubernetes nos próximos anos? Pelo que entendi, uma das divergências de design fundamentais entre vgo e dep é se precisamos trabalhar com o ecossistema como ele existe hoje (suposição do dep) ou se podemos mudar a comunidade para fazer lançamentos marcados e manter a compatibilidade (suposição do vgo) .

Portanto, é possível que o vgo não funcione para Kubernetes usando muitas dependências por algum tempo, até que as normas da comunidade Go mudem.

@mattfarina Claro. Pessoalmente, acho muito frustrante pintar isso como um "rolo de vapor", no entanto. @sdboyer se absteve amplamente da discussão pública por meses e ainda não há argumentos reais e concretos dele. Ele tinha suas razões e isso é justo. Mas o consenso ainda requer discussão e, pelo menos no que diz respeito ao registro público, pessoalmente não estou ciente de quaisquer questões que foram levantadas e claramente ignoradas (ainda não li seu post).

No que me diz respeito, qualquer discussão acontecia a portas fechadas. E dado que não temos nenhuma informação de qualquer forma, eu consideraria justo supor que ambos os lados receberam a devida consideração.

@bradfitz SemVer é usado por pacotes Go hoje, geralmente em PHP, em node.js, em Rust e em várias outras linguagens. É uma coisa bem comum. Encontrei problemas nesses idiomas e mais em que os pacotes quebravam com problemas de compatibilidade do SemVer. Às vezes intencionalmente e às vezes por acidente. O que Go fará diferente para evitar um problema presente em todas essas outras linguagens porque as pessoas são falíveis?

Se não pudermos articular isso, é uma suposição ruim que a compatibilidade sempre será mantida e os desenvolvedores não devem ter botões acessíveis a eles para ajustar isso e passar essas informações para a árvore de dependências.

Acho que todos vão concordar: a situação atual com o gerenciamento de dependências é horrível, em qualquer linguagem/plataforma. Acredito que @bradfitz explicou corretamente o principal ponto de conflito. Talvez o vgo não tenha sucesso, mas para mim é óbvio que temos que mudar alguma coisa (quer dizer, não apenas no Go, mas em geral), e o vgo parece muito promissor para tentar.

@mattfarina Planejamos tentar implementar um serviço que controlará automaticamente a compatibilidade realmente mantida. Integrando-o com godoc.org, fornecendo crachás para README, usando-o como um proxy para go get - há muitas maneiras de tentar fazê-lo funcionar bem o suficiente. Claro, @sdboyer está certo sobre a compatibilidade da API não garante compatibilidade real, mas isso é um bom começo e deve funcionar bem o suficiente na maioria dos casos.

Portanto, é possível que o vgo não funcione para Kubernetes usando muitas dependências por algum tempo, até que as normas da comunidade Go mudem.

A esperança não é uma estratégia, principalmente quando os comportamentos e expectativas existentes já estão bem estabelecidos. Go poderia ter fichas de inovação para gastar aqui se tivéssemos essa discussão cinco anos atrás, e as coisas fossem mais passíveis de influência. Mas como consequência de ignorar o problema por tanto tempo, parece claro para mim que qualquer ferramenta proposta agora deve atender os usuários onde eles estão.

O que Go fará diferente para evitar um problema presente em todas essas outras linguagens porque as pessoas são falíveis?

Estamos discutindo algum tipo de comando go release que facilita os lançamentos/marcações, mas também verifica a compatibilidade da API (como o verificador go tool api interno do Go que escrevi para os lançamentos do Go). Ele também pode consultar godoc.org e encontrar chamadores de seu pacote e executar seus testes em sua nova versão também no momento do pré-lançamento, antes que qualquer tag seja enviada. etc.

encontre os chamadores do seu pacote e execute seus testes em sua nova versão também no momento do pré-lançamento, antes que qualquer tag seja enviada. etc.

Isso não é realmente prático para quem não é o Google.

Isso não é realmente prático para quem não é o Google.

Com todos os provedores de nuvem começando a oferecer contêineres como serviço de pagamento por segundo, não vejo motivo para não fornecermos isso como uma ferramenta de código aberto que qualquer pessoa pode executar e pagar os US$ 0,57 ou US$ 1,34 de que precisam para executar um zilhão de testes em vários hosts por alguns minutos.

Não há muito molho secreto do Google quando se trata de executar testes.

Com todos os provedores de nuvem começando a oferecer contêineres como serviço de pagamento por segundo, não vejo motivo para não fornecermos isso como uma ferramenta de código aberto que qualquer pessoa pode executar e pagar os US$ 0,57 ou US$ 1,34 de que precisam para executar um zilhão de testes em vários hosts por alguns minutos.

Isso requer uma conta com um provedor de nuvem específico, exige que você aceite os termos de serviço de um provedor de nuvem específico (o que você pode ou não ser capaz de fazer por motivos legais, mesmo que a maior parte do mundo trate isso como se não assunto), exige que você more em uma área que o provedor de nuvem atende (por exemplo, se você estiver no Irã e o provedor de nuvem estiver nos Estados Unidos, talvez não possa usá-lo devido a leis de exportação) e exige que você tem dinheiro para gastar para pagar o provedor de nuvem (presumivelmente toda vez que fizer um lançamento). Pode não ser muito dinheiro, mas isso não significa que todos poderão pagá-lo. Se queremos que o Go seja inclusivo e utilizável por um público diversificado, isso não parece uma boa solução.

/dois centavos

@SamWhited MeteorJS tinha isso trabalhando com galaxy, um comando meteor publish embutido para executar seu projeto em um provedor de nuvem. Talvez eu esteja entendendo mal o problema proposto, mas o jeito deles parecia bem.

@bradfitz E se a API não mudar, mas o comportamento por trás dela mudar? Esse é um caso que rompe com o SemVer e impacta quem o importa. Como você detecta essa situação? Eu pergunto porque eu experimentei isso em mais de uma ocasião.

ferramenta de código aberto que qualquer um pode executar e pagar os US$ 0,57 ou US$ 1,34 necessários para executar um zilhão de testes em vários hosts por alguns minutos.

Isso agora toca no custo. Isso pode parecer bom para as pessoas em uma cidade de tecnologia nos EUA. E as pessoas na África, América Central ou outros lugares que estão distribuídos globalmente. Como ferramentas como essa geralmente são acessíveis fora dos círculos da "elite tecnológica"?

E quanto a todos os casos de não fazer o trabalho de nuvem pública? No local (muitas pessoas fazem isso) ou proprietário e com problemas de confiança. Como essas coisas vão funcionar para eles? Se você tiver uma ferramenta interna, talvez não queira vazar para os serviços públicos quais importações você está usando. Digamos que você obtenha suas importações do GitHub, mas esse serviço é executado no Google. As pessoas se sentem bem em entregar sua árvore de dependências ao Google? Um monte não vai.

Ele também pode consultar godoc.org e encontrar chamadores de seu pacote e executar seus testes em sua nova versão também no momento do pré-lançamento, antes que qualquer tag seja enviada. etc.

Vamos usar o Kubernetes como exemplo disso. Alguém escreve um pacote que é importado para o Kubernetes. Portanto, uma ferramenta precisa obter isso e executar todos os testes. Partes dele são projetadas para serem executadas no Windows e no POSIX. Podemos testar para multi-OS/multi-arch (já que o Go lida com isso). O que isso vai realmente parecer (e custo)?

--

Acho que ferramentas como essa podem ser úteis. Não quero que as pessoas pensem o contrário. Eu só não vejo como eles resolvem o problema para muitas pessoas. Eles não são práticos o suficiente ou não se encaixam em todas as configurações.

Parece que estamos tentando resolver um problema matemático com restrições conhecidas e controláveis. Mas, as pessoas são confusas, então precisamos de soluções tolerantes a falhas.

Para citar @technosophos hoje cedo:

"gerenciadores de versão não são realmente ferramentas para compiladores ou linkers ou qualquer coisa... gerenciadores de versão são para pessoas que colaboram."

Se vale a pena, ele escreveu mais de um gerenciador de dependências, estudou outros e conversou com pessoas que escreveram ainda mais.

Parece irresponsável aceitar uma proposta enquanto ainda existem preocupações fundamentais pendentes sobre a abordagem. O consenso entre o autor e o especialista em domínio da comunidade @sdboyer parece ser um padrão mínimo razoável a ser alcançado antes que a proposta seja considerada aceita.

Apenas para acumular um pouco aqui: temos uma história em torno de embalagens que não é padrão e não é ideal (o ecossistema go get ). Seria melhor simplesmente desistir de um gerenciador de pacotes padrão do que ter outro go get lançado, o que leva as pessoas a práticas ruins em nome da compatibilidade com a ferramenta "padrão". Como alguém que usa Go desde seu lançamento público, acho esta conversa frustrante e desanimadora, pois parece que a liderança da equipe Go não aprendeu as lições dos erros cometidos com go get (nee goinstall ).

Há problemas razoáveis ​​e práticos que se manifestaram sobre esta proposta. Devemos mudar a proposta para abordá-los e não simplesmente dizer: "trabalhando como pretendido". Se não pudermos fazer isso direito para 1.11 ou 1.12 ou 1.13, devemos esperar até que possa ser feito corretamente. Esta proposta propõe um sistema que se comporta significativamente diferente da maioria dos outros sistemas e não de uma maneira boa.

A razão motivadora por trás do MVS parece ser que a abordagem tradicional é NP-Complete. Acho isso uma motivação muito fraca. Ao lidar com problemas NP-Complete, a questão principal é: "Com que frequência surgem as instâncias difíceis ?" Com o gerenciamento de pacotes, a resposta parece ser "muito raramente". Não devemos nos contentar com uma formulação incompleta e inútil do problema apenas para evitar o rótulo NP-HARD no problema.

A maioria dos pontos concretos foi expressa por outras pessoas mais próximas da questão ( @sdboyer @peterbourgon @mattfarina etc...). A minha queixa principal é que estamos a aceitar esta proposta quando estes pontos concretos não foram devidamente abordados.

@SamWhited , você está tendo problemas com um recurso opcional de um design hipotético. O usuário hipotético que não confia em nenhum provedor de nuvem ou não pode usar nenhum provedor de nuvem dentro do firewall de seu país ou não quer pagar sempre pode executar testes (ou uma fração deles) em sua própria máquina durante a noite. Ou apenas use a verificação de assinatura go release , que permite 95% do caminho até lá de graça.

@mattfarina , @SamWhited , vamos mover a discussão para https://github.com/golang/go/issues/25483.

@mattfarina

Encontrei problemas nesses idiomas e mais em que os pacotes quebravam com problemas de compatibilidade do SemVer. Às vezes intencionalmente e às vezes por acidente. O que Go fará diferente para evitar um problema presente em todas essas outras linguagens porque as pessoas são falíveis?

Ainda não está claro para mim, por que se presume que o vgo tenha um desempenho pior nesses casos do que o dep. Pelo contrário, parece-me que o vgo tem um desempenho estritamente melhor . Em seu blog , você menciona um problema específico com o leme, como evidência do fracasso do modelo vgo. No entanto, no mundo vgo, existem dois cenários pelos quais vgo teria escolhido usar v1.4.0 para grpc:

  1. Os desenvolvedores do helm escolheram especificar grpc >= v1.4.0 em go.mod como requisito. Nesse caso, eles podem simplesmente reverter esse requisito e, assim, reverter para uma versão anterior de grpc que funcione para eles.
  2. Os desenvolvedores de uma dependência do helm escolheram especificar grpc >= v1.4.0 . Nesse caso, o dep teria instalado isso também e, se o helm tentasse reverter restringindo grpc < v1.4.0 , o dep teria que coaxar por causa de requisitos conflitantes.

Portanto, parece-me que o vgo resolve esse problema pelo menos tão bem quanto o dep. Enquanto isso, em um mundo dep, há outra opção:

  1. As dependências transitivas do helm exigiam grpc >= v1.x.0 , com alguns x < 4 , então grpc liberou v1.4.0 e o dep decidiu usar a versão mais recente na instalação, sem ser solicitado . Nesse caso, o leme estaria quebrado e precisaria se reunir para fazer um lançamento de correção de bug (conforme descrito em seu post), criando toil. Enquanto isso, vgo teria simplesmente ignorado a nova versão e as coisas continuariam funcionando bem.

Estou aparentemente ignorando algo básico. Mas da última vez que pedi esclarecimentos sobre isso no slack, fui adiado para um post hipotético futuro no blog. Daí minha frustração, porque eu realmente não entendo de onde vem a ideia de que vgo de alguma forma tem mais problemas com pessoas quebrando a semântica de suas versões do que o dep teria.

Para esclarecer, não se trata de dep . É uma análise de como o MVS (vgo) se comporta em comparação com outras soluções de gerenciamento de pacotes baseadas em solucionador de SAT (dep incluído).

Outro exemplo de Matt afirma o seguinte:

Você é o desenvolvedor do módulo app e tem duas dependências:

  • grpc [>= 1.8 ]
  • helm , que possuem a seguinte dependência:

    • grpc [>= 1.0, < 1.4] (porque grpc introduziu uma mudança importante em 1.4).

A maioria das pessoas não saberá sobre os requisitos de dependência transitiva ( helm -> grpc [>= 1.0, < 1.4]) porque isso exigiria estar ciente de que helm quebra quando usando grpc >= 1.4. Eu diria que não é algo que a maioria das pessoas vai se importar com isso ou gastar tempo e energia investigando isso.

Se você estiver usando vgo (especificamente contando com MVS), você obterá:

  • helm
  • grpc [>= 1.8 ]

Essa deve ser uma combinação inválida (já que os requisitos helm não são satisfeitos) e a maioria dos gerentes dep daria uma mensagem de erro informando que você tem um conflito (dado que helm declarou seus requisitos em alguns formato).

Este é um ponto significativo de crítica ao MVS e vgo. Ele não quer permitir que as dependências indiquem quando têm um problema com uma versão específica para evitar a necessidade de uma solução full-SAT . Você pode consultar as seções Theory e Excluding Modules no artigo do MVS para obter mais explicações.

O MVS não quer reconhecer essa possibilidade (ou pelo menos quer restringir a capacidade de ditar isso para o módulo atual) sob a suposição de que SemVer é sempre respeitado e, portanto, isso não é necessário, o que praticamente nem sempre é o caso. Refiro-me ao seu artigo sobre compatibilidade com versões anteriores para mostrar por que aderir à compatibilidade com versões anteriores é difícil e impraticável ao ser forçado da maneira vgo.

A solução MVS é pedir ao desenvolvedor do módulo que especifique quais versões excluir, ignorando o conhecimento que pode ser compartilhado pelos autores das dependências sobre essas incompatibilidades. Assim, transferindo a responsabilidade para o desenvolvedor de conhecer e fazer a lógica complicada pela qual a maioria dos gerentes de dep passa para descobrir quais versões de quais dependências são compatíveis entre si (ou seja, uma solução SAT).

A menos que as pessoas magicamente comecem a se adequar ao SemVer, eu imagino instalar uma dependência ao usar vgo para transformar em um exercício excêntrico onde depois de importar um módulo você teria que verificar um arquivo README para copiar a lista de versões que não são compatíveis para incluir em seu arquivo go.mod . Tenha em mente que a instrução exclude tem apenas uma versão atualmente.

@Merovius

Na postagem do blog de @mattfarina , eles descrevem a atualização do Helm para grpc v1.4.0 intencionalmente, ou seja, o equivalente a atualizar go.mod . A questão não é como o dep ou o vgo evitam esse problema, mas sim como eles permitem que o autor da biblioteca se recupere dele. No caso do dep, eles podem lançar um patch que anuncia a restrição grpc<v1.4.0 . Para usuários que não possuem outra dependência do grpc, isso funcionará. Se um usuário já atualizou para a versão mais recente do helm e possui outra dependência em grpc>=v1.4.0 , essa restrição fará com que sua compilação falhe; não é o ideal, mas melhor do que o comportamento de tempo de execução sutilmente quebrado.

Com vgo , os mantenedores do Helm têm opções equivalentes disponíveis? Se um usuário atualizou grpc para v1.4.0 por qualquer motivo, o MVS sempre escolherá [email protected] , o Helm será quebrado e não há nada que os mantenedores do Helm possam fazer sobre isso (sem ajustar para o comportamento alterado, que geralmente é demorado). Não posso falar por @mattfarina , mas é assim que li a preocupação levantada e é uma que compartilho. Com dep e sistemas similares, bibliotecas intermediárias podem se defender contra (ou mais frequentemente, recuperar) violações de compatibilidade com restrições de limites superiores.

@ibrasho @mattfarina

Parece que um dos exemplos de Russ na palestra do GopherconSG cobre um cenário muito semelhante (talvez idêntico?).

No post acima, você diz que o leme depende de "grpc [>= 1.0, < 1.4]". Mas isso não é estritamente preciso. Eu acho que o que você quer dizer é que alguma versão específica do leme depende de "grpc [>= 1.0, < 1.4]". Digamos que a versão do leme seja 2.1.3; presumivelmente, esta versão do leme foi lançada após o grpc 1.4 (caso contrário, ele não saberia se marcar como incompatível com o grpc 1.4). O ponto de Russ nessa palestra é que a versão anterior do helm (digamos 2.1.2), presumivelmente (ainda) não teria se marcado como incompatível com o grpc 1.4.

Em outras palavras, uma atribuição satisfatória (de acordo com dep) seria helm = 2.1.2, grpc = 1.8. O Dep pode escolher corretamente essa atribuição de versão sem erro, pois atende a todas as restrições nas versões fornecidas.

@balasanjay

A versão de Helm não muda fundamentalmente o exemplo. [email protected] pode declarar uma dependência do [email protected] e o usuário do helm pode ter uma dependência do [email protected] diretamente ou através de algum outro pacote. Nesse cenário, o MVS falha e instala [email protected] e [email protected]. Além disso, este exemplo assume que o usuário atualiza intencionalmente para [email protected] (e, portanto, [email protected]) e talvez até mesmo [email protected] , [email protected] , etc, antes que o problema seja descoberto.

O MVS intencionalmente não permite exclusões de dependências intermediárias para atingir seus objetivos teóricos:

Implicações negativas (X → ¬ Y, equivalentemente ¬ X ∨ ¬ Y: se X está instalado, então Y não deve ser instalado) não podem ser adicionadas...

Para este exemplo, precisamos expressar, se X= helm>= 2.1.3 estiver instalado, então Y= grpc>=1.4.0 não deve ser instalado, o que não podemos fazer por design.

Na verdade, apenas o pacote de nível superior (corrigindo a quebra) e os usuários de nível inferior (adicionando exclusões manualmente) têm algum recurso quando as regras de compatibilidade são quebradas. Intermediários como o Helm devem trabalhar com todas as versões futuras de quaisquer dependências, o que, para mim, reduz significativamente a proposta de valor dessas bibliotecas.

Talvez isso encoraje um melhor comportamento em bibliotecas de uso intenso como grpc e aws-sdk-go, porque o impacto total de qualquer quebra será sentido por todas as partes e será mais difícil de contornar. No entanto, nem sempre é tão fácil. E se a "quebra" não for realmente uma quebra, mas uma mudança legítima no comportamento que tem consequências não intencionais para alguns usuários, mas é útil para outros? Esse tipo de coisa não é incomum e os autores upstream hesitariam com razão em reverter uma mudança nesse cenário.

Acho que não fui tão claro quanto Russ. Deixe-me tentar de novo.

Inicialmente, o estado de deps se parece com isso.
Helm 2.1.2: grpc >= 1.0

Em seguida, o grpc 1.4 é lançado. Os desenvolvedores do Helm percebem que há uma incompatibilidade e, assim, lançam uma nova versão do Helm:
Helm 2.1.3: grpc >= 1,0, < 1,4

Eu começo um novo aplicativo que depende do Helm e de algum novo recurso do grpc (para fins de argumento). Eu listo os seguintes deps:
Elmo >= 2.0.0
grpc >= 1,4

A alegação acima era que o dep notaria o conflito inerente e relataria um erro.

Mas Russ apontou que isso não é verdade, porque há uma atribuição de versão sem conflitos. Especificamente, o dep pode optar por usar helm 2.1.2 e grpc 1.4. De acordo com dep, esta é uma atribuição válida, porque o leme 2.1. 3 é o que é incompatível com grpc 1.4, enquanto 2.1. 2 é compatível com todos os grpc >= 1.0.

Em outras palavras, o dep poderia _validamente_ escolher "consertar" um conflito, fazendo o downgrade para uma versão que ainda não havia registrado o conflito. Se você tiver versões imutáveis, isso parece inevitável. Portanto, parece que o dep também lidaria mal https://codeengineered.com/blog/2018/golang-vgo-broken-dep-tree/.

@balasanjay Claro, mas eventualmente o Helm lança o 2.2.0 com um novo recurso legal que você deseja, e você tenta atualizar e o problema aparece.

(Estou cautelosamente otimista de que isso funcionará bem se o vgo tiver um UX bem projetado para conscientizar as pessoas de que seu resultado MVS tem um "conflito" dessa forma. Por "bem projetado" quero dizer que incentiva as pessoas a lidar com o problema em vez de apenas ficar bem com muitos avisos o tempo todo.)

Eu certamente concordo que existem situações em que o dep detectará um conflito, mas só queria deixar claro que _não_ é o apresentado naquele post do blog (ou pelo menos, não com os requisitos de versão apresentados atualmente).

E FWIW, acho razoável questionar a eficácia de uma salvaguarda se ela quebrar em um exemplo relativamente simples de conflito.

Para constar, localizei o exemplo de Russ (é praticamente idêntico a este, em um grau impressionante): https://youtu.be/F8nrpe0XWRg?t=31m28s

Re: seleção de versão mínima, estou tendo problemas para descobrir como resolver a seguinte situação:

Imagine uma biblioteca popular no modo de manutenção, por exemplo, v1.2.3 foi lançada há mais de um ano sem alterações. Muitas outras bibliotecas dependem dele.
Um desenvolvedor vem e realiza uma otimização de velocidade para fazer na v1.2.3. Ele acelera a função principal da biblioteca em 10x, sem alteração de API! Isso é lançado como v1.3.0. Não são encontrados mais bugs neste pacote para o ano seguinte.

Como os dependentes acabam recebendo essa atualização? Eles podem trabalhar com v1.2.3, mas v1.3.0 é evidentemente melhor.

@daurnimator

Como os dependentes acabam recebendo essa atualização?

Cada aplicativo precisaria definir 1.3.0 como a versão mínima a ser usada.

Bem, que dia bobo é hoje! 😄

Esta proposta está aberta com discussões ativas há mais de dois meses

Esse problema está em aberto há anos, e a linha do tempo rsc definida para tudo isso era artificialmente curta, considerando todas as coisas.

Houve duas objeções a esta proposta que sentimos que devemos falar:

  1. A proposta não oferece uma solução técnica para todos os possíveis cenários que possam surgir envolvendo incompatibilidades.

Se isso é uma alusão às posições que delineei para Russ, é uma caricatura enganosa. Uma razão pela qual demorei tanto para organizar meus pensamentos sobre esse tópico é porque o uso repetido de argumentos e declarações enganosas é... francamente, desestabilizador.

Literalmente ninguém pensa que TODOS os cenários possíveis podem ser cobertos. O problema é, e sempre foi, que o MVS só resolve um problema _ilusório_ (evitando SAT), cria _novos_ problemas em seu lugar que importam muito mais na prática e não pode funcionar efetivamente como uma camada intermediária na qual podemos operar razoavelmente.

Estes são precisos em sua observação, mas funcionam como pretendido. Autores e usuários de código terão que mudar algumas de suas práticas em relação ao uso e liberação de bibliotecas, assim como os desenvolvedores se adaptaram a outros detalhes do Go, como executar o gofmt.

Nada é ajudado banalizando o grau de mudanças propostas aqui, comparando-as com a execução do gofmt - possivelmente a atividade mais irracional que realizamos como desenvolvedores Go.

Como Russ apontou em sua recente palestra na Gophercon Singapore, a única solução permanente para a incompatibilidade é trabalhar em conjunto para corrigir a incompatibilidade e manter o ecossistema de pacotes Go. Soluções temporárias em uma ferramenta como vgo ou dep só precisam funcionar o tempo suficiente para dar aos desenvolvedores tempo para resolver o problema real, e vgo faz esse trabalho bem o suficiente.

Vamos ser claros - o que importa é a _community_. Nós. As pessoas que produzem o ecossistema de software. Chegamos a um ecossistema melhor criando ferramentas que nos ajudam a gastar nossos tokens limitados em colaborações úteis - não criando ferramentas frágeis que nos punem por participar em primeiro lugar.

@Merovio :

Fui adiado para um post hipotético futuro no blog. Daí minha frustração, porque eu realmente não entendo de onde vem a ideia de que vgo de alguma forma tem mais problemas com pessoas quebrando a semântica de suas versões do que o dep teria.

Reunir todos esses argumentos leva tempo, e não é meu trabalho diário. Eu pretendia terminar esta noite, mas ainda estou no meu passe final de edição. 😢Amanhã de manhã...? Nada está gravado em pedra!

O problema é, e sempre foi, que o MVS só resolve um problema ilusório (evitando SAT), ...

Respeitosamente, IMHO a solução SAT é uma não solução, portanto, para mim, o MVS resolve um problema muito real.

IMHO a solução SAT é uma não solução, portanto, para mim, o MVS resolve um problema muito real.

Gostaria de entender o raciocínio por trás disso, se possível?

Esse problema está em aberto há anos, e a linha do tempo rsc definida para tudo isso era artificialmente curta, considerando todas as coisas.

Eu vejo duas idéias claramente conflitantes aqui. As pesquisas mostraram repetidamente que o gerenciamento de dependências precisa de uma solução mais cedo ou mais tarde. No entanto, um prazo de seis meses não é suficiente para analisar e aprovar uma proposta.

Entendo que não devemos escolher a primeira solução a ser implementada. Mas o tempo é um dos fatores. A contraproposta ao vgo ainda não está bem definida e provavelmente levaria anos para se concretizar. Eu escolheria o vgo no Go 1.11 em vez de uma solução super-incrível baseada em SAT no Go 1.14 qualquer dia.

Isso também está assumindo que o que o vgo implementa é imutável. Pelo que sabemos, vgo pode mudar consideravelmente durante as janelas 1.12 e 1.13.

Gostaria de entender o raciocínio por trás disso, se possível?

É o mesmo raciocínio que está por trás de preferir o regexp do Go ao PCRE, ou seja. não permitir que um programa dependa de um algoritmo com complexidade quadrática/exponencial de pior caso.

Devemos ter em mente que vgo apenas substituirá go get nada mais nada mais. Devemos comparar vgo com go get e não vgo com algo que não existe. "Pior é melhor"! Vamos nos concentrar agora nos detalhes de implementação.

FWIW, é IMO completamente possível introduzir limites superiores sem ter um solucionador SAT completo - simplesmente não é possível que a solução gerada seja ótima ou garantir que uma solução existente será encontrada. Se precisar, você pode

  1. Tornar possível especificar limites superiores
  2. Ignore-os ao resolver via MVS
  3. Verifique a solução encontrada em relação a todos os limites e resmungue se ela não se encaixar (essa é a principal diferença: as abordagens tradicionais tentariam encontrar uma solução diferente)

Isso significa que você obtém as vantagens do MVS (um algoritmo simples sem tempos de execução patológicos), preservando a possibilidade de marcar incompatibilidades e detectá-las estaticamente, mas você está perdendo a garantia de sempre encontrar uma solução, se existir. Embora se possa argumentar que a solução que você encontraria, não é realmente uma solução, porque a incompatibilidade ainda está lá, apenas desconhecida para o algoritmo.

Então, pelo menos para mim, parece que é perfeitamente possível adaptar alguma maneira de obter limites superiores para o MVS. Ainda não estou convencido de que seja realmente necessário, mas se for o caso, sempre poderá ser adicionado mais tarde. Tornar problemática a afirmação "MVS é fundamentalmente imprópria". Mas eu ainda posso entender mal o problema. E pode haver um cenário de falha ilustrando melhor isso?

@sdboyer Não se preocupe. Como eu disse, entendo perfeitamente que este não é o seu trabalho diário e agradeço o tempo que você está levando para participar. Ainda é uma questão prática de "se não conhecemos os argumentos, não podemos falar sobre eles". Eu posso imaginar que você está tão frustrado com toda a situação quanto eu :)

É o mesmo raciocínio que está por trás de preferir o regexp do Go ao PCRE, ou seja. não permitir que um programa dependa de um algoritmo com complexidade quadrática/exponencial de pior caso.

@cznic Sou da opinião de que o gerenciamento de dependências não é apenas um problema técnico, está entrelaçado com restrições e considerações sociais. Portanto, não tenho certeza de compará-lo com expressões regulares (um problema de implementação puramente técnico) e favorecer um algoritmo baseado basicamente na complexidade do tempo é uma abordagem justa.

Vejo algumas pessoas favorecendo o MVS porque é mais simples e fácil de raciocinar e essa é uma consideração compreensível se resolver os outros aspectos do problema em questão.

Gostaria de saber se as pessoas têm outras razões para preferir um algoritmo baseado em SAT.

Eu queria saber se as pessoas têm outras razões para preferir um algoritmo baseado em SAT.

@ibrasho MVS não precisa de um arquivo de bloqueio.

Eu estou querendo saber se as pessoas têm outras razões para preferir um algoritmo baseado em SAT.

Pessoalmente, minhas razões são

  1. Ele unifica o arquivo de bloqueio e o manifesto em um arquivo, que pode ser amplamente editado por computador. Significando menos ruído e ainda sendo reproduzível (ou "alta fidelidade") no caso comum. AFAICT isso é algo exclusivo do MVS. Como uma das minhas principais frustrações com outros gerenciadores de pacotes é a labuta de ter que editar arquivos de manifesto, isso é enorme para mim.
  2. O reparo gradual será mais fácil/possível. Como acredito fortemente nessa abordagem para resolver o problema de desenvolvimento distribuído, isso também é importante para mim.
  3. Pode ajudar o ecossistema a permanecer coletivamente próximo ao HEAD para a maioria das bibliotecas. Isso é em grande parte especulação e depende um pouco das ferramentas disponíveis. Mas se "puxar tudo para a versão mais recente" for suficientemente simples, pode aumentar a cadência com que as versões mais recentes das bibliotecas são testadas umas contra as outras na produção.
  4. Consegue tudo isso preservando ou superando as qualidades desejáveis ​​de outras abordagens, AFAICT. As atualizações só acontecem se desejado. E os pacotes binários continuarão sendo compilados mesmo que a compilação de algumas de suas dependências seja quebrada em uma nova versão, dando ao autor tempo para trabalhar em uma correção.

Especialmente esta última parte é meio irônica. Porque essa qualidade exata foi pintada como extremamente importante no último post de @sdboyer - mas, neste momento, ainda acredito que o vgo seja estritamente melhor a esse respeito do que as abordagens tradicionais.

@Merovius
"Ele unifica o arquivo de bloqueio e o manifesto em um arquivo"

  • isso não é totalmente verdade, pois os manifestos estão efetivamente espalhados por todos os seus arquivos. As instruções de importação atuam efetivamente como manifestos.

@docmerlin Quando eu disse "manifesto", eu quis dizer um arquivo listando todos os módulos dos quais você depende com suas versões apropriadas (em particular, ele contém estritamente mais informações do que as instruções de importação, caso contrário você não precisaria). No caso de vgo seria go.mod , no caso de dep é Gopkg.toml . Você também pode chamar isso de arquivo de configuração para o solucionador de dependências. Se você considerar um termo diferente mais apropriado, sinta-se à vontade para substituí-lo por manifesto, quando estiver lendo meu comentário.

Observe que é bastante normal que haja um arquivo listando todas as dependências e uma lista de importações explícitas (que são um subconjunto potencialmente estrito das dependências) por arquivo. Em particular, todos os gerenciadores de dependência Go que conheço fazem isso. Outras abordagens também usam um arquivo de bloqueio que descreve versões específicas e precisas a serem usadas para garantir compilações reproduzíveis. E a característica distintiva do MVS que eu estava me referindo é que este arquivo não é necessário, porque ele pode ser derivado exclusivamente do arquivo manifest/dependency-solver-config/go.mod.

(excluído + republicado da minha conta pessoal)

@cznic o foco nas classes de complexidade do MVC versus uma abordagem baseada em SAT isoladamente não faz sentido (como acho que @sdboyer escreve em algum lugar - somos pegos falando sobre isso porque é uma das poucas coisas que podemos nome/identificar).

A sugestão do @bradfitz em #25483 para resolver algumas das preocupações com o vgo (que eu acho que é uma solução inicialmente maluca, mas talvez ótima) envolve a execução de testes de usuários arbitrários de sua API antes de um lançamento. Este é um problema NP-completo em geral, mas na prática pode ser uma ótima solução (assim como gps2).

Então, por um lado, temos algoritmos de gerenciamento de pacotes baseados em SAT e, por outro lado, temos um algoritmo em NL que nos obriga a fazer um trabalho NP-completo mais tarde (ou tempo limite, que é o que qualquer solucionador SAT prático faria para arquivos de bloqueio adversários).

o foco nas classes de complexidade do MVC versus uma abordagem baseada em SAT isoladamente não faz sentido ...

Não tenho certeza de onde vem o termo 'foco'. É só que se houver dois algoritmos disponíveis, o pior caso de um é quadrático ou pior e o outro é linear ou melhor, eu escolho o último e evito o primeiro. Eu chamaria isso de princípio, não foco.

... sugestão em #25483 para resolver algumas das preocupações com vgo ...

Parece que o problema nº 25483 não está relacionado ao vgo. Erro de digitação?

É só que se houver dois algoritmos disponíveis, o pior caso de um é quadrático ou pior e o outro é linear ou melhor, eu escolho o último e evito o primeiro.

@cznic claro, geralmente se dois algoritmos fornecerem os mesmos resultados, você deseja aquele com menor complexidade (embora isso nem sempre seja tão simples - o Python usa a classificação por inserção para pequenas entradas porque, apesar de ter limites de complexidade piores, tem melhores constantes + tempo de execução até um ponto).

Nesse caso (algoritmos baseados em MVS vs. SAT), os resultados diferem de propósito e têm amplas consequências. Estou sugerindo que por causa disso você não pode apenas comparar a complexidade algorítmica, você precisa considerar seus impactos mais amplos.

Parece que o problema nº 25483 não está relacionado ao vgo

A primeira linha nessa edição é um link para o comentário de Brad nesta edição: https://github.com/golang/go/issues/24301#issuecomment -390788506 . Embora essa ferramenta seja útil fora do vgo, parece que está sendo considerada em grande parte para mitigar algumas das desvantagens do MVS (na minha opinião/entendimento).

Caramba, tantos comentários. Vou tentar adicionar alguns detalhes e histórico para ajudar.

Alguns anos atrás, havia várias soluções de gerenciamento de dependências diferentes (por exemplo, godep, glide, etc). Para descobrir um caminho a seguir, algumas coisas aconteceram:

  1. Um grupo de pessoas investidas e conhecedoras se reuniu em um comitê . Observe que um membro da equipe go do Google fazia parte desse grupo.
  2. Um segundo grupo de pessoas que criaram gerenciadores de dependências ou tinham informações sobre eles apoiou esse primeiro grupo.
  3. Foi realizado um levantamento da comunidade sobre as necessidades e pensamentos sobre as ferramentas existentes (em Go e fora de Go) . Observe que alguns resultados foram privados apenas para os olhos do comitê.
  4. As empresas que utilizam o Go para produção foram entrevistadas para obter detalhes sobre suas necessidades. Os detalhes disso não são públicos para que as pessoas possam falar livremente.

Os dados da pesquisa e da entrevista foram todos devolvidos ao comitê. Depois de analisar os dados e debater, eles decidiram que precisávamos de uma solução com determinados recursos e criaram uma especificação . Depois que o desenvolvimento em dep começou a atender a essas necessidades.

Na Gophercon, no ano passado, houve uma cúpula de contribuidores que incluiu pessoas que investiram em gerenciamento de dependências falando sobre isso. Perto do final daquelas conversas, Russ veio até a mesa e disse algo como, eu posso fazer melhor se eu for sozinho e construir algo. Ele fez isso e veio com vgo. Foi feito separadamente da comunidade.

O vgo atende às necessidades expressas pelas pessoas na pesquisa e nas entrevistas? Pelo que entendi de Russ, ele não leu os resultados, mas criticou o processo. Pode valer a pena alguém fazer um mapeamento.

Oh, na cúpula Russ compartilhou uma das razões, naquela época, ele não gostava de um solucionador SAT. Ele queria uma solução com menos linhas de código porque não queria que a equipe Go do Google fosse responsável por manter tanto código. Lembro-me disso especificamente porque fiz algumas comparações de LOC entre dep, glide e outras ferramentas depois disso para obter uma imagem melhor das diferenças.

Go é um projeto de propriedade e executado do Google. Se a questão é de recursos, a Go deve estar sob uma fundação e pessoas de outras organizações se envolverem na propriedade dela? Essa é outra maneira de adicionar recursos para manter a cadeia de ferramentas.

Não, #25483 não está relacionado ao vgo. Eu apenas listei isso de improviso como outro tipo de coisa que um comando auxiliar hipotético go release poderia fazer. Mas seria útil a qualquer momento.

Meu ponto maior foi que Go nunca tornou super fácil fazer lançamentos de pacotes Go. Em uma vida anterior eu escrevi http://search.cpan.org/dist/ShipIt/ para automatizar lançamentos de pacotes Perl CPAN e isso fez uma enorme diferença quando você tem ferramentas em torno dessas coisas versus fazê-lo manualmente.

Eu só mencionei um go release hipotético porque me perguntaram o que o Go poderia fazer diferente para ajudar os humanos a não cometerem erros.

O único problema real que vejo com o vgo parece trivial para corrigir .... o problema da versão máxima. Se uma biblioteca quebrar com a v1.7 de um dep, não há como especificar isso além de uma exclusão... o que só ajuda até que a v1.8 seja lançada, que provavelmente ainda está quebrada exatamente da mesma maneira.

Parece que seria trivial adicionar a versão max ao vgo apenas como uma maneira de ajudar o vgo a determinar quando duas bibliotecas são incompatíveis. Se uma biblioteca diz que precisa de pelo menos v1.7 e uma biblioteca diz que precisa

Sem isso, o vgo usará apenas 1.7 e uma das bibliotecas quebrará. Se você tiver sorte, há um erro de compilador... mais provavelmente há apenas um bug de comportamento sutil que pode não ser notado até o caminho. E o que é insidioso é que provavelmente é alguma dependência transitiva que você nem está chamando diretamente.

As restrições de versão máxima do @natefinch empurram o MVS para fora de seu território de solução de SMT muito inteligente.

Acho que @natefinch significa versões máximas como uma verificação/filtro final. Depois que o MVS fizer seu trabalho, o vgo apresentará um erro se alguma dessas restrições de versão máxima não for atendida. Isso ainda não nos leva ao território dos solucionadores SAT.

Exatamente. Não há solução. Há apenas "vgo diz que 1.7 é a coisa certa a usar, mas o módulo X afirma que não funciona com essa versão". Isso já é algo que pode acontecer hoje com vgo se você tiver algo que diga require foo 1.7 e outra coisa diga exclude foo 1.7 , se não houver uma versão foo superior.

E então o que você deve fazer?

Você usa seu cérebro humano para descobrir uma solução, porque não há solução mecânica. Você está tentando usar duas bibliotecas que afirmam categoricamente que exigem versões incompatíveis de uma biblioteca. A vai quebrar com 1,6, B vai quebrar com 1,7. O fim.

Você precisa convencer o autor da dependência a corrigir o bug (se for um bug), convencer um dos autores da biblioteca a lançar uma nova versão que seja compatível com a nova versão da dependência ou bifurcar um monte de coisas e corrigi-lo você mesmo .

Não há uma boa resposta. Mas também não há ferramentas no mundo que possam consertar isso para você.

@sdboyer

O problema é, e sempre foi, que o MVS só resolve um problema ilusório (evitando SAT),

Eu não posso falar com suas conversas particulares com Russ, mas “evitar o SAT” parece um argumento de espantalho para mim. “Evitar o SAT” não é mais um objetivo do que “usar o SAT”: nenhum deles tem qualquer impacto concreto nos usuários.

A meu ver, os problemas concretos que o MVS resolve são:

  • tornando as compilações reproduzíveis por padrão,
  • construir o mais próximo possível das versões testadas, e
  • evitando rejeições espúrias.

Chegamos a um ecossistema melhor criando ferramentas que nos ajudam a gastar nossos tokens limitados em colaborações úteis - não criando ferramentas frágeis que nos punem por participar em primeiro lugar.

Eu concordo. Mas quero dar uma olhada mais de perto em suas suposições: quais ferramentas “punem” a participação e por quê?

Um dos exemplos que você deu em seu post foi: “Nosso projeto depende de [email protected] agora, mas não funciona com [email protected] ou mais recente. Queremos ser bons cidadãos e nos adaptar, mas não temos largura de banda no momento.”


A primeira suposição (implícita) que você está fazendo é que você (ou seus usuários) tiveram tempo e largura de banda para testar seu projeto exaustivamente em relação a todas as versões de suas dependências.¹ Para alguns projetos, essa suposição por si só pode não ser válida : ao definir a expectativa de que todos marquem limites superiores quando descobrirem incompatibilidades, você acaba “punindo” os mantenedores que não têm largura de banda para testar regressões e atualizar limites em potencialmente todos os seus lançamentos.

Em contraste, sob o MVS, os mantenedores são obrigados apenas a declarar uma coisa: “Testamos exaustivamente contra [email protected] e funcionou”. Se seus usuários não fizerem nada para interromper isso, eles construirão em [email protected] e tudo continuará funcionando como antes.

No MVS, _breakage ocorre apenas no momento de uma atualização explícita._ Isso é um benefício significativo: se seu usuário atualizar alguma outra dependência para que ele puxe [email protected] e interrompa o uso do seu pacote, então eles podem simplesmente desfaça essa atualização até que você tenha tempo para corrigi-la, _ou até que o mantenedor de X tenha tempo para corrigir a incompatibilidade._ E dadas as expectativas corretas, a última pode ser muito mais provável do que a primeira: cortar [email protected] pode se tornar um trabalho desnecessário se [email protected] restaurar a compatibilidade.


A segunda suposição que você está fazendo é que toda incompatibilidade que afeta _qualquer parte_ do seu módulo afeta _todos os usuários_ do seu módulo. Se a parte do seu pacote que quebra em [email protected] é uma função pouco usada, por que impedir que seus usuários atualizem X em seu programa que nem a chama?

Você poderia argumentar que se algumas partes do módulo não dependem de outras partes, elas devem ser módulos separados, mas isso novamente força um trabalho extra para os mantenedores de pacotes: se eu tiver apenas a largura de banda para testar e atualizar periodicamente, talvez eu tenha apenas a largura de banda para manter um módulo, não todo um complexo de módulos refinados com dependências refinadas.


¹ Em geral, detectar incompatibilidades com dependências requer que você teste contra _o produto cruzado de todas as versões_ dessas dependências: é possível que seu uso particular de [email protected] seja bom, mas quebre em combinação com algum outro pacote que você depende sobre. Por exemplo, talvez você e sua dependência exijam opções incompatíveis em uma configuração global.

eles podem simplesmente cancelar essa atualização

Isso pressupõe que este é um erro do compilador ou algo similarmente altamente visível. É muito mais provável que seja um bug de comportamento sutil que não é imediatamente aparente. Você atualiza, executa seus testes e tudo fica bem. Talvez um tempo limite que era de 10 segundos agora seja 30, e isso acaba com os tempos limite no resto de sua pilha quando sob carga. Você não notaria até que ele estivesse em produção (algo semelhante aconteceu conosco com uma mudança no mgo alguns anos atrás).

Se a parte do seu pacote que quebra no [email protected] é uma função pouco usada, por que impedir seus usuários de atualizarem o X em seu programa que nem mesmo o chama?

Porque eles provavelmente não saberão se estão ou não usando essa função se for uma dependência transitiva, e quem pode dizer que algum caminho de código não utilizado não será ativado de repente quando você alterar seu código? Então, essa função quebrada agora está sendo chamada. É muito melhor confiar no mantenedor da biblioteca que é especialista em entender sua própria biblioteca, que se eles disserem que não funciona com 1.7, não construa com 1.7.

Isso pressupõe que este é um erro do compilador ou algo similarmente altamente visível. É muito mais provável que seja um bug de comportamento sutil que não é imediatamente aparente.

Existem bugs sutis à espreita em quase todas as versões de quase todos os pacotes de software. Geralmente não marcamos lançamentos como completamente inutilizáveis ​​só porque encontramos um desses bugs; em vez disso, corrigimos em uma versão posterior.

O que torna essa classe particular de bug especial?

quem pode dizer que algum caminho de código não utilizado não será ativado de repente quando você alterar seu código?

Esse é exatamente o argumento do MVS: se você alterar seu código, detectará a falha _naquela revisão_ e poderá atualizar a restrição de versão nessa revisão também.

Isso implica que, como usuário, é mais fácil dividir falhas se você atualizar suas dependências uma de cada vez em vez de em massa, mas isso vale para as alterações em geral: quanto menores suas alterações, mais precisamente você pode separe-os, independentemente de serem alterações em seu código ou em suas dependências.

O que torna essa classe particular de bug especial?

Não é apenas um bug. É maior do que isso. O autor da biblioteca, que é o maior especialista em seu código, disse ao mundo "yo, a versão 1.7 do X quebra minhas coisas de maneiras que são tão ruins, apenas nem construa com elas".

Claramente, a menos que seja um erro do compilador, é um julgamento. Se 99 de suas funções entrarem em pânico, mas uma não... isso é apenas um bug? Ou é uma incompatibilidade total? E se for apenas uma função que entra em pânico?

Em algum momento, um humano tem que tomar essa decisão. Prefiro lidar com uma incompatibilidade declarada preventiva que ocorre em tempo de compilação, do que me preocupar com um grande problema não declarado em produção.

Não é apenas um bug. É maior do que isso. O autor da biblioteca, que é o maior especialista em seu código, disse ao mundo "yo, a versão 1.7 do X quebra minhas coisas de maneiras que são tão ruins, apenas nem construa com elas".

Há outra maneira de um autor de biblioteca expressar isso: Escreva um teste que falhe quando usado com uma versão incompatível do X. Isso terá a vantagem de não exigir alterações na biblioteca se o X lançar uma versão fixa, além de capturar qualquer futuras regressões.

Escreva um teste que falhe quando usado com uma versão incompatível do X.

Sim, eu pensei nisso. Mas então você está pedindo a cada binário de nível superior para executar todos os testes para todas as dependências transitivas, testes que podem exigir infraestrutura que você não possui. Nem todos os conjuntos de testes são 100% isolados.

Há uma razão pela qual go test ./... não executa testes no diretório do fornecedor.

Para resumir (provavelmente mal), parece que o principal argumento técnico contra o vgo é que precisa haver uma maneira de as bibliotecas declararem restrições globalmente respeitadas em suas próprias dependências.

Por que não ter ambos os lados concordando em sancionar uma proposta concorrente que explora como o GPS2 poderia se encaixar com as partes do vgo que ambos os lados gostam? Então, mescle o experimento vgo enquanto isso e revisite a proposta GPS2 antes que o vgo mescle a linha principal?

Como alguém que lutou com muitas ferramentas de dependência no passado, estou empolgado com o vgo. FWIW, como membro da "comunidade", sinto-me bem representado pela solução vgo até agora. No entanto, continuo aberto a considerar os argumentos a favor da adição de restrições de versão global e espero que esse argumento se desenvolva ainda mais.

Apenas jogando isso lá fora:

Isso pressupõe que este seja um erro do compilador ou algo similarmente altamente visível. É muito mais provável que seja um bug de comportamento sutil que não é imediatamente aparente. Você atualiza, executa seus testes e tudo fica bem. Talvez um tempo limite que era de 10 segundos agora seja 30, e isso acaba com os tempos limite no resto da sua pilha quando sob carga.

Este é um problema no software em geral. O NPM (a outra ferramenta de versionamento com a qual eu pessoalmente tenho mais familiaridade) usa um solucionador SAT combinado com uma comunidade que abraçou fortemente o semver, e esse problema ainda existe.

No que diz respeito à discussão da dependência transitiva, a realidade é que não há varinha mágica: em algum momento o desenvolvedor deve estar ciente das dependências que usa e dedicar tempo para testar adequadamente o código pelo qual é responsável. Meu empregador não pode ser o único em que somos obrigados a justificar todas as bibliotecas que usamos, incluindo bibliotecas transitivas, por motivos legais e outros.

Honestamente, a meu ver, muitas das reclamações em relação ao vgo parecem ser "não é perfeito, portanto não funcionará". Não vamos deixar de implementar uma boa solução por falta de uma solução perfeita.

Para mim, parece muito alinhado com a filosofia geral do Go para a equipe principal fornecer uma ferramenta que seja boa na maioria das situações, deixando para a comunidade fornecer ferramentas mais avançadas e/ou específicas.

@malexdev Pode haver um problema com seus atores no argumento de que:

Não vamos deixar de implementar uma boa solução por falta de uma solução perfeita.

dep é uma boa solução hoje. Um que foi criado pela comunidade trabalhando em conjunto. vgo, como iniciado anteriormente, faz algumas suposições:

  1. Que ninguém nunca vai quebrar semver, mesmo por acidente
  2. Que precisaremos retrabalhar as bases de código existentes no ecossistema existente

vgo é mais baseado na "solução perfeita" em um "mundo perfeito" enquanto o dep funciona hoje seguindo o que já funciona no ecossistema de outras linguagens de programação sendo tolerante a falhas.

Você pode ver isso na história do espaço do problema de gerenciamento de pacotes que acabei de escrever .

Para resumir (provavelmente mal), parece que o principal argumento técnico contra o vgo é que precisa haver uma maneira de as bibliotecas declararem restrições globalmente respeitadas em suas próprias dependências.

Concordo com este resumo.

Por que não ter ambos os lados concordando em sancionar uma proposta concorrente que explora como o GPS2 poderia se encaixar com as partes do vgo que ambos os lados gostam?

Neste ponto, continuo não convencido de que o GPS2 seja uma necessidade ou mesmo uma boa ideia. A habilidade que você delineou acima pode ser adaptada para vgo em cima do MVS (como este ou este ). Pessoalmente, espero que a próxima postagem do blog de @sdboyer contenha bons argumentos contra o próprio MVS, mas, no momento, não vejo nenhum motivo para um algoritmo diferente - especialmente um que custaria vantagens significativas de UX do vgo.

Embora, para ser justo, também não veja nenhuma razão contra experimentar isso.

dep é uma boa solução hoje.

Não tenho certeza se concordo. Eu mesmo não o usei, mas no slack há várias pessoas reclamando de problemas que parecem diretamente rastreáveis ​​​​à sua abordagem de solução SAT. E eu sei que estou muito insatisfeito com o design geral do deps (deixando de lado a questão do próprio solucionador de dependências, fluxo de trabalho geral e UX).

  1. Que ninguém nunca vai quebrar semver, mesmo por acidente

Lamento, mas pedi repetidamente justificativas para esta afirmação e não obtive nenhuma. Por que vgo assumiria isso de alguma forma, forma ou forma mais do que dep? Eu discordo fundamentalmente dessa afirmação. É uma suposição feita para explicar o algoritmo. Assim como se você explicasse o algoritmo do dep, explicaria as suposições construídas nas versões semânticas. Na pior das hipóteses, eles mostram as mesmas quebras se você não cumprir essas semânticas.


Acho que, em geral, faz sentido distinguir entre diferentes partes do que estamos falando. Algumas das reclamações são em relação ao SIV, alguns MVS e alguns ao vgo-shell geral. Por exemplo, é verdade que o MVS não pode lidar com os limites da versão superior, mas isso não significa que o vgo não possa lidar com os limites da versão superior. O SIV requer a alteração de muitos códigos por aí (reescrevendo as instruções de importação), mas, novamente, isso não significa necessariamente que o vgo exigiria isso. Embora para ser claro, também não acho que seja grande coisa, desde que possamos migrar. Qual AFAICT podemos.

Acabei de assistir a palestra de abertura do GopherConSG do @rsc sobre versionamento. Ele abordou o cenário em que uma dependência introduz uma mudança de ruptura e comparou como vgo e dep lidariam com isso, o que parece ser a principal preocupação aqui. (É um ótimo relógio.)

Se eu entendi seu ponto corretamente, o dep pode quebrar a compilação também se uma limitação de versão máxima for usada para evitar uma versão ruim . Eu estaria muito interessado em ver este ponto abordado por aqueles aqui que estão preocupados com o fato de o vgo ficar aquém da profundidade a esse respeito.

@willfaught Para nós, quebrar a compilação quando uma limitação de versão máxima é usada para evitar uma versão ruim é considerada um sucesso! Isto é o que queremos que aconteça. Russ observa corretamente que esse problema não é resolvido automaticamente. Uma restrição em Helm>2.0.0 não atualizará o usuário automaticamente para " [email protected] ", mas funcionaria com sucesso (faça downgrade do grpc ou acionaria uma falha de compilação se isso for impossível) se o usuário dependesse explicitamente de "Helm ==2.1.4". Pessoalmente, a primeira coisa que costumo tentar ao encontrar um problema com uma biblioteca é forçar uma atualização para a versão mais recente. Com o Dep, isso me informaria sobre a falha introduzida pelo GRPC 1.4.0. Com vgo , a única maneira de o Helm me comunicar isso é através da documentação.

Repetindo o ponto porque continua a não ser entendido: nem dep nem vgo podem evitar que esse problema ocorra ou fornecer uma solução infalível. Em vez disso, dep permite que o Helm comunique que o problema existe, enquanto vgo não.

Para resumir (provavelmente mal), parece que o principal argumento técnico contra o vgo é que precisa haver uma maneira de as bibliotecas declararem restrições globalmente respeitadas em suas próprias dependências.

Eu adicionaria um pouco de cor a este resumo, pois essas restrições são necessárias para lidar com o caminho infeliz de quando as regras de compatibilidade são violadas. vgo estabelece a regra de compatibilidade de importação e então prossegue para desenvolver uma solução assumindo que essa regra seja sempre seguida. Neste mundo idealizado, a necessidade de limites superiores é limitada ou mesmo inexistente. No mundo real, isso não acontecerá: os desenvolvedores lançarão atualizações que quebram a compatibilidade, intencionalmente ou não.

Acho que o @Merovius está em uma solução viável. O MVS prosseguiria conforme especificado e, após a conclusão da resolução, o vgo verificaria cada pacote resolvido quanto a exclusões. Se algum for encontrado, eles serão relatados ao usuário final, que pode optar por alterar suas dependências para atender a essas restrições ou optar por substituir e ignorar as restrições. Eu também estive no outro lado disso e às vezes você sabe melhor do que o mantenedor; ele funciona para o seu aplicativo e isso é tudo o que importa.

Isso restaura um caminho para que as bibliotecas intermediárias comuniquem uma incompatibilidade aos usuários finais. Para repetir o exemplo do leme mais uma vez:

Usuá[email protected] : leme @2.1.2
[email protected] : GRPC @1.3.5

==> Usuário atualiza intencionalmente o [email protected] .

Usuário: Helm @2.1.3, [email protected] ("Helm atualizado, para que eu possa finalmente usar grpc 1.4!")
Leme: GRPC @1.4.0

==> Erro detectado! O usuário está vendo alguns problemas com o Helm, portanto, verifique se há uma nova versão.

Usuário: [email protected] , [email protected]
Leme: GRPC @1.3.5, GRPC fraco <1.4.0

O usuário vê um aviso de que o GRPC 1.4.0 foi rejeitado pela instalação do [email protected]. Como o Helm está quebrado para mim e é isso que estou tentando consertar, removo minha dependência do GRPC 1.4.0 (infelizmente perdendo algumas funcionalidades úteis) e reexecuto o MVS. Desta vez, o MVS resolve o GRPC para 1.3.5 e o Helm para 2.1.4, verifica novamente todas as restrições fracas, descobre que elas são válidas e pronto.

Não espero que nenhuma ferramenta resolva esses problemas magicamente, mas espero algum recurso como uma biblioteca de middleware. Até onde eu sei, a única opção em vgo é bifurcar e renomear todas as minhas dependências upstream (ou equivalentemente, copiá-las para o meu projeto) se eu quiser isolar meus usuários de problemas de compatibilidade com essas dependências. Acho que ninguém quer isso.

@willfaught Para nós, quebrar a compilação quando uma limitação de versão máxima é usada para evitar uma versão ruim é considerada um sucesso! Isto é o que queremos que aconteça.

O ponto levantado na palestra é que vgo não é pior que dep nesse cenário. Em seu exemplo , a compilação não está quebrada no sentido de que o dep não consegue encontrar uma solução; está quebrado no sentido de que o dep encontra uma solução, e essa solução inclui a versão ruim, resultando na mesma situação ruim que queríamos evitar.

Você realmente deveria ver o vídeo, que mostra um excelente exemplo, mas aqui está a essência como eu entendi/lembro:

  • As versões do pacote (incluindo seus requisitos de dependência) são imutáveis
  • Para adicionar uma limitação máxima de versão a uma dependência existente do seu pacote, você deve publicar uma nova versão do seu pacote.
  • É possível que o dep escolha a versão anterior do seu pacote para atender a todos os requisitos, caso em que sua nova limitação máxima de versão não estará presente. Isso permite que a versão ruim seja usada afinal.

Concordo principalmente com a proposta de adicionar exclusões máximas de versão, mas tenho esta preocupação: suponha que eu coloque em minha biblioteca "use gRPC > 1.4, <1.8" e, em gRPC 1.9, os autores decidam: "sabe, Helm foi certo, fizemos uma mudança radical no 1.8, estamos voltando ao nosso comportamento anterior no 1.9.0." Agora, as pessoas que tentam importar o Helm+gRPC não poderão usar o 1.9 até que o Helm lance uma versão que diz "use gRPC > 1.4, exceto 1.8.0, 1.8.1, 1.8.2, mas 1.9+ é legal".

Em outras palavras, talvez exclude grpc 1.8 seja suficiente porque não saberemos se o gRPC 1.9 será incompatível ou não até que seja publicado, quando o Helm poderá adicioná-lo à lista de exclusão ou não.

Eu não sei essencialmente nada sobre este espaço. Mas ao ler a discussão aqui, parece que o maior desacordo se resume a como detectar casos errôneos. Ou seja, tanto o MVS (vgo) quanto o SAT (dep) lidam com situações normais mais ou menos bem, talvez não de forma idêntica, mas bem o suficiente.

SAT fornece uma capacidade que o MVS não oferece: usando SAT, o autor do pacote de biblioteca P1 pode declarar "P1 requer P2 e funciona com P2Version > 1.1 e P2Version < 1.4". O MVS só pode declarar "P1 requer P2 e funciona com P2Version > 1.1", e não pode expressar a restrição "P2Version < 1.4". No caso normal, isso não importa. Só importa se alguma operação tentar atualizar o P2 para a versão 1.4. Nesse caso, o SAT relatará um erro, enquanto o MVS não. Ao usar o MVS, se a incompatibilidade não for um erro de compilação, pode causar uma falha muito depois do fato.

Sem dúvida, os apoiadores do SAT veem outros grandes problemas com o MVS, mas até agora este é o que eu entendo.

Acho que vale a pena notar que, se as expressões de restrição forem versionadas - se fizerem parte de uma versão específica do P1 -, no curso normal dos eventos, antes do lançamento da versão 1.4 do P2, a versão 2.2 do P1 dirá alegremente "P2Version > 1,1". Somente quando o P2 versão 1.4 for lançado que os autores do P1 perceberão a incompatibilidade, e lançarão o P1 versão 2.3 com "P2Version > 1.1 e P2Version < 1.4". Portanto, se você estiver usando o P1 versão 2.2, nem o SAT nem o MVS relatarão qualquer problema com a atualização do P2 para a versão 1.4, embora ele falhe de alguma maneira possivelmente sutil.

Em outras palavras, embora faça todo o sentido que um lançamento do P1 liste as versões mínimas compatíveis do P2, se o lançamento funcionar corretamente com a versão mais recente do P2, não faz sentido que um lançamento liste o máximo de versões compatíveis . A versão máxima compatível será conservadora e, portanto, cada vez mais errada à medida que versões mais novas e melhores do P2 aparecerem, ou se o P2 mudar de alguma forma incompatível no futuro, não especificará esse requisito, pois no momento do lançamento não t existir.

Portanto, se quisermos ter um sistema que defina algo além dos requisitos mínimos de versão, esses requisitos não devem fazer parte de uma versão específica, mas devem fazer parte de algum tipo de metadados associados ao pacote, metadados que podem ser obtidos em a qualquer momento sem atualizar o próprio pacote. E isso significa que a operação "atualizar este pacote" deve ser separada da operação "verificar se as versões atuais do meu pacote são compatíveis".

Eu diria ainda - e isso é definitivamente mais tênue do que o acima - que, se "verificar se minhas versões atuais do pacote são compatíveis" falhar, é geralmente imprudente confiar em qualquer ferramenta para resolver o problema. Se o problema de compatibilidade não puder ser resolvido pela simples operação "atualize todos os pacotes relevantes para a versão atual", então é necessário pensar. Uma ferramenta pode orientar nesse pensamento, mas em geral não pode substituí-lo. Em particular, parece muito imprudente para uma ferramenta iniciar o downgrade de pacotes automaticamente.

Então, se pensarmos em termos de

  1. uma maneira de definir metadados de pacotes descrevendo incompatibilidades de pacotes
  2. com base nisso, uma ferramenta que informa se seus pacotes atuais são compatíveis

então talvez algumas das principais diferenças entre MVS e SAT se tornem menos importantes.

Obrigado por dizer isso tão bem Ian. Para acompanhar, uma vez que estabelecemos versões e vgo, queremos absolutamente ter um novo godoc.org (talvez um nome diferente) que registre informações adicionais sobre pacotes, informações que o comando go pode consultar. E algumas dessas informações seriam incompatibilidade de pares que o comando go poderia relatar como avisos ou erros em qualquer compilação específica (ou seja, relatar o dano, não tentar escondê-lo contornando-o). Mas ter versões na cadeia de ferramentas principal é o primeiro passo, e isso, junto com os requisitos mínimos de versão e controle de versão de importação semântica, é o que foi aceito nesta edição.


Estamos comprometidos em fazer isso da maneira mais tranquila possível. Isso exigirá ferramentas adicionais, mais alcance educacional e relações públicas para corrigir problemas nos pacotes existentes. Tudo isso foi impedido de aceitar esta proposta, pois parecia presunçoso avançar sem que a abordagem geral fosse aceita. Mas a proposta é aceita e o trabalho começará a desembarcar de forma mais agressiva agora que a incerteza acabou.

Eu tive o mesmo pensamento sobre informações externas para compatibilidade de versão ... como a compatibilidade de versão deve ser constante em , ela não precisa estar no controle de origem (e, de fato, estar no controle de origem é uma desvantagem definitiva, conforme declarado acima). Seria bom se houvesse uma solução proposta para isso, já que definitivamente parece ser o maior problema com o MVS conforme proposto.

É incrível ver a discussão se movendo organicamente nessa direção. Tem sido um ponto central das minhas preocupações e torna muito mais fácil explicar questões fundamentais quando as pessoas já estão na maior parte do caminho para isso.

@ianlancetaylor , acho que você está certo com essa observação sobre a necessidade de fazer alterações nas informações de restrição em versões já lançadas. Como @rsc indicou, esse serviço é algo que discutimos/sugerimos em nossas reuniões. Poderíamos fazer isso com godoc.org, ou qualquer outra coisa, com certeza. Mas eu realmente não acho que isso envolva um serviço separado, e seria melhor sem um. Fiz uma referência rápida a isso no artigo que publiquei na sexta-feira (apenas a partir dessa âncora). Se nada mais, em um serviço, há perguntas que precisam ser respondidas sobre qual declaração de incompatibilidade deve aparecer em avisos, o que significa lidar com identidade e como escopo declarações para situações específicas no depgraph. Manter as declarações dentro de arquivos de metadados significa que não precisamos nos preocupar com nada disso. Mas mais sobre isso em um segundo.

O que é realmente importante aqui é este ponto, embora talvez não da maneira que você pretendia:

talvez algumas das principais diferenças entre MVS e SAT se tornem menos importantes.

A sugestão de uma meta-ferramenta que faça essa pesquisa - sim, é uma pesquisa SAT - como solução para os problemas que as pessoas estão identificando é reveladora. É exatamente o que teremos que transformar em dep, se o MVS continuar conforme descrito. E a primeira coisa a notar é que, se estamos tão preocupados com essas incompatibilidades que estamos falando de uma ferramenta de busca, então o que estamos realmente dizendo é que o MVS se torna apenas um passo em um algoritmo maior, e o benefícios de grokkability vão direto para a janela.

Exceto que é pior do que isso, porque nenhuma quantidade de meta-ferramentas pode contornar o problema embutido de perda de informações que surge da compactação das versões mínima e atual juntas. O grande resultado disso são as reversões em cascata , o que significa que tentar corrigir qualquer uma das incompatibilidades nesta lista muito provavelmente acabará jogando de volta outras partes do gráfico de dependência não necessariamente relacionadas ao seu problema. E os desenvolvedores não poderão seguir uma estratégia de atualização que não seja prejudicial a outros. . (Ah, e regras fantasmas , mas isso é apenas um efeito colateral do MVS em geral).

É por isso que afirmei que o MVS é uma camada intermediária inadequada para construir uma ferramenta de ordem superior como esta - "não adequada ao propósito". É claro que as pessoas acreditam que essas incompatibilidades ocorrerão, então o MVS está apenas pegando um problema difícil e tornando-o mais difícil.

Se, em vez disso, unificarmos o problema de um "serviço de incompatibilidade" de volta em um arquivo de metadados, acredito que seja possível , usando apenas um conjunto simples de declarações de pares, obter o mesmo efeito. (Este é um rascunho do conceito, mas cada vez mais parece estar unido)

Isso implicaria que partes do MVS fossem alteradas, mas o MVS ainda poderia ser executado sobre as informações codificadas lá. Isso seria útil principalmente se as incompatibilidades realmente enlouquecerem e você quiser evitar todas elas. Mas o algoritmo primário começaria a partir de uma linha de base que se parece com o MVS, depois mudaria para uma pesquisa mais ampla (para ser claro, o próprio MVS ainda deve ser considerado pesquisa), sem a possibilidade de passar para versões absurdamente antigas.

(observe, estarei de férias esta semana, então não responderei até o próximo final de semana)

@sdboyer

A sugestão de uma meta-ferramenta que faça essa pesquisa - sim, é uma pesquisa SAT

Você pode ser mais específico? A frase que você citou é logo após Ian sugerir uma ferramenta para informar se as versões selecionadas são compatíveis - e até onde sei, essa é a principal alternativa sugerida aqui (certamente é o que eu pretendia acima). Esse problema definitivamente não é uma pesquisa e é trivial e não requer a solução de SAT (é apenas avaliar uma fórmula booleana para um determinado conjunto de valores, não tentando encontrar valores que o satisfaçam).

Certo, simplesmente relatar que existem alguns valores incompatíveis conhecidos na fórmula não requer a resolução de SAT. Tomando qualquer ação com base nisso, como por uma ferramenta que auxilia no processo de encontrar um resultado sem tais valores nele.

Citei essa frase não porque acho que é um indicativo de que as pessoas aceitaram a busca como sempre necessária, mas porque se acreditamos que relatar essas condições é importante, é porque acreditamos que é provável que encontremos esses cenários.

o problema é que, uma vez estabelecida a plausibilidade e a importância de abordar esses casos, parece que as pessoas dão o salto errôneo de que "podemos fazer todas as coisas de pesquisa em cima do MVS, e tudo ficará bem". podemos, mas tais tentativas se tornam muito mais complicadas de lidar por causa dos caminhos possíveis úteis que o MVS corta, por design.

Em 28 de maio de 2018 16:02:13 EDT, Axel Wagner [email protected] escreveu:

@sdboyer

A sugestão de uma meta-ferramenta que faça essa busca - sim, é uma
Pesquisa SAT

Você pode ser mais específico? A frase que você citou é logo após Ian
sugerindo uma ferramenta para informar se as versões selecionadas sãocompatível - e até onde sei, essa é a principal
alternativa sugerida aqui (certamente é o que eu pretendia acima).
Esse problema definitivamente não é uma pesquisa e é trivial e
não requer resolver SAT (é apenas avaliar uma fórmula booleana
para um determinado conjunto de valores, não tentando encontrar valores que o satisfaçam).

--
Você está recebendo isso porque foi mencionado.
Responda a este e-mail diretamente ou visualize-o no GitHub:
https://github.com/golang/go/issues/24301#issuecomment -392595150

Citei essa frase não porque acho que é um indicativo de que as pessoas aceitaram a busca como sempre necessária, mas porque se acreditamos que relatar essas condições é importante, é porque acreditamos que é provável que encontremos esses cenários.

Para ser claro: a sugestão de ajustar os limites superiores dessa maneira é puramente reativa às preocupações levantadas e para mostrar que isso pode ser feito (para questionar criticamente a afirmação de que o MVS é fundamentalmente inadequado para o propósito). Parece um pouco injusto tomar essa concessão e vontade de se comprometer como prova de que achamos que você estava certo o tempo todo.

Para mim, essa afirmação (de que o MVS é inadequado e um passo essencialmente irreversível na direção errada) é o que estou desafiando pessoalmente e a lente pela qual estou lendo seus argumentos. Um desses argumentos era que é um recurso se pudermos declarar incompatibilidades e fazer com que o algoritmo de seleção de versão falhe quando elas forem encontradas. Outro argumento justo é que, se eles ocorrerem, seria bom ter o algoritmo capaz de resolvê-los para nós (o que realmente exigiria um solucionador SAT).

No entanto, embora eu ache que essas preocupações sejam válidas e justas, não acredito que elas passem a prova de que o MVS é fundamentalmente inadequado. Ainda acredito que o MVS como ponto de partida traz recursos bons e importantes para a mesa. E se essas preocupações acabarem causando dor significativa na prática, ainda há muitas maneiras de iterar sobre isso - adicionando limites superiores (seja como parte de go.mod ou como um serviço separado) com falhas puras até e incluindo a adição de um solucionador SAT completo e arquivos de bloqueio em algum momento. Ou seja, eu concordo com você que essas coisas vão acontecer, mas a) estou (talvez ingenuamente) otimista de que elas não causarão tanta dor quanto você está antecipando eb) que são problemas solucionáveis ​​mesmo quando começamos com o MVS.

Ocorre-me que algo fora do controle de origem determinando a compatibilidade mudaria o determinismo de um sistema MVS. Se você tiver foo >= 1.5.0 como uma restrição em uma lib e outra lib tiver foo >= 1.6.0. Em seguida, coloque esses dois em um binário e eles escolhem 1.6.0. No MVS, isso é tudo o que você precisa para uma compilação repetível. sempre vai escolher 1.6

Mas se você adicionar compatibilidade externa à mistura, poderá atualizar essa primeira biblioteca para dizer que não é compatível com 1.6 e, em seguida, o algoritmo escolherá 1.7, mesmo que o código não tenha mudado ... O que significa que você precisaria um arquivo de bloqueio novamente.

Para referência, não acho que um arquivo de bloqueio seja uma coisa ruim. É bom ter uma lista explícita de exatamente o que você precisa construir. E isso deve torná-lo rápido. Nenhuma lógica mágica necessária.

@natefinch Se o arquivo go.mod do aplicativo foi atualizado para exigir a v1.7.0 porque a ferramenta de compatibilidade externa indicou que a v1.6.0 era incompatível, você não precisaria de um arquivo de bloqueio. Como a especificação da v1.7.0 reside no arquivo go.mod, o autor também pode adicionar um comentário dizendo por que a v1.7.0 está sendo usada e essa informação seria útil para os leitores.

@leighmcculloch , se algum arquivo no aplicativo for atualizado, é uma compilação diferente e totalmente fora do escopo do problema de "compilação reproduzível sem arquivo de bloqueio".

informações de compatibilidade fora de banda são propostas para refletir como o conhecimento se desenvolve: nenhuma incompatibilidade era conhecida no momento do lançamento, mas depois se tornou aparente e informações extras são publicadas sobre as versões já lançadas. IMHO, por definição, essa abordagem leva a uma mudança na forma como as dependências são extraídas, caso contrário, por que essas informações de incompatibilidade de extração?

@redbaron @natefinch

O objetivo das informações de incompatibilidade é que os autores de bibliotecas comuniquem informações de incompatibilidade a seus usuários. Se essas informações são usadas na compilação ou no momento do lançamento é uma questão diferente.

No caso do vgo, a ideia atual é exibir apenas avisos (ou potencialmente coaxar). Mas notadamente para não deixar que isso influencie a escolha das versões usadas (pois isso exigiria resolver o SAT). Então, na verdade, não importa, você pode usá-lo em um ou em ambos e ele cumprirá seu dever perfeitamente, mantendo a propriedade de repetibilidade¹.

Em dep, essas informações são usadas apenas no momento da liberação e, em seguida, gravadas em um arquivo de bloqueio, que é usado no momento da compilação. Portanto, parece que estamos considerando um uso de tempo de lançamento "bom o suficiente" de qualquer maneira, pelo menos quando se trata de preocupações de vgo vs. dep.

Eu ainda não acho que temos que realmente responder a essas perguntas agora, no entanto.


[1] Pessoalmente, eu diria que usá-lo em tempo de lançamento e somente se -v for usado em tempo de compilação é melhor, porque um usuário não deveria ter que decidir se um aviso é acionável ou não.

@rsc escreveu:

Para acompanhar, uma vez que estabelecemos versões e vgo, queremos absolutamente ter um novo godoc.org (talvez um nome diferente) que registre informações adicionais sobre pacotes, informações que o comando go pode consultar. E algumas dessas informações seriam incompatibilidade de pares que o comando go poderia relatar como avisos ou erros em qualquer compilação específica (ou seja, relatar o dano, não tentar escondê-lo contornando-o).

Eu estou querendo saber se é necessário gravar incompatibilidade de pares. A maneira como vejo atualmente é que qualquer incompatibilidade entre o módulo A@vN e o módulo B@vM é realmente porque B fez uma alteração incompatível de alguma versão vL onde L < M.

Se o módulo B não fez uma alteração incompatível, então o módulo A tem apenas um bug. Se sim, então a questão é sobre B em si, não sobre o emparelhamento de A e B.

Assim, ISTM que qualquer repositório público de metadados de módulo pode registrar apenas incompatibilidades de qualquer módulo com versões anteriores de si mesmo, o que pode tornar o problema mais tratável. Esses relatórios de incompatibilidade são bastante semelhantes aos relatórios de bugs, embora não possam ser resolvidos porque uma vez que uma versão é publicada, ela não pode ser alterada.

Quando você atualiza suas versões de módulo, a ferramenta go pode considerar os metadados e se recusar a considerar uma versão incompatível com qualquer versão atualmente escolhida. Acho que isso evita a necessidade de resolver SAT. Ele também pode decidir que um determinado módulo tem muitos relatórios de incompatibilidade e se recusar a adicioná-lo como uma dependência.

Um conjunto de tuplas do formulário ( module , oldVersion , newVersion , description ) pode ser suficiente.

a ferramenta go pode considerar os metadados e se recusar a considerar uma versão incompatível com qualquer versão atualmente escolhida

Claro, isso não funciona quando você está adicionando várias dependências, que entre elas acabam exigindo o uso de versões mutuamente incompatíveis, pois as novas versões não fazem parte do módulo existente, mas pode haver uma heurística razoável disponível. Não é AFAICS crucial, porque as dependências devem ser adicionadas relativamente raramente.

Eu me preocupo que go release esteja se tornando o "compilador suficientemente inteligente" desta discussão. O que os usuários podem esperar concretamente de go release no Go 1.11/12? Acho que isso faz a diferença para as expectativas razoáveis ​​em torno do MVS/SIV.

Obrigado pela energia que muitos de vocês trouxeram para o Go e esta proposta em particular.

O primeiro objetivo do processo de proposta é "[gar]rer que as propostas obtenham uma avaliação apropriada, justa, oportuna e registrada com uma resposta clara". Essa proposta foi amplamente discutida e publicamos um resumo da discussão. Após seis semanas e muita discussão, o comitê de revisão da proposta - intervindo como árbitro porque eu escrevi a proposta - aceitou a proposta.

Um único problema do GitHub é um lugar difícil para se ter uma discussão ampla, porque o GitHub não tem encadeamento para diferentes vertentes da conversa e nem exibe mais todos os comentários. A única maneira de uma discussão como essa funcionar é pela curadoria ativa do resumo da discussão. Até mesmo o resumo ficou pesado quando a proposta foi aceita.

Agora que a proposta foi aceita, esta questão não é mais o lugar certo para discussão e não estamos mais atualizando o resumo. Em vez disso, registre novas questões específicas sobre problemas que você está tendo ou sugestões concretas de mudanças, para que possamos ter discussões focadas sobre cada tópico específico. Por favor, prefixe esses novos problemas com “x/vgo:”. Se você mencionar o nº 24301 no texto da nova edição, ele será referenciado aqui para que outros o encontrem.

Um último ponto é que aceitar a proposta significa aceitar a ideia, não os bugs de implementação do protótipo e tudo mais. Ainda há detalhes a serem resolvidos e bugs a serem corrigidos, e continuaremos a fazer isso juntos.

Obrigado novamente por toda a sua ajuda.

Há mais trabalho a ser feito (veja o rótulo dos módulos ), mas a implementação do módulo inicial, conforme proposto neste problema, foi comprometida com a árvore principal, então estou encerrando este problema.

Esta página foi útil?
0 / 5 - 0 avaliações