Desenvolvimento

11 fev, 2015

Composer: é quase sempre sobre o arquivo de bloqueio

Publicidade

Davey Shafik escreveu um ótimo artigo para EngineYard chamado Composer: é tudo sobre o arquivo de bloqueio. O foco principal do texto é sugerir que as pessoas deveriam utilizar mais o commit para enviar seus arquivos de bloqueio de aplicativo composer.lock.

Por favor, vá ler o artigo dele agora e, pelo amor de Deus, faça o commit dos seus arquivos de bloqueio de aplicativos. Se você e seus colaboradores costumam ser um pouco vagos com suas especificações composer.json e não têm um arquivo composer.lock, então acabarão tendo versões diferentes do mesmo aplicativo. Teoricamente, se os desenvolvedores de componentes estão usando SemVer e você está sendo cuidadoso, então tudo deve estar bem, mas manter o bloqueio no controle de versão vai garantir que está sendo utilizada a mesma versão nos computadores de toda equipe de desenvolvimento. Isso vai acontecer cada vez que for executado o comando $ composer install. Se você estiver utilizando Heroku ou EngineYard, então poderá utilizá-los para auxiliar no deploy de seus componentes de produção, construindo assim um gancho, o que é impressionante.

Bônus: Isso faz com que o composer instale muito mais rápido e verifique seus checksum quando necessário para que você não tenha problemas com novas versões do componente Muppet. Essas coisas acontecem.

Você pode aprender muito mais sobre a cuidadosa seleção de versão e outras dicas sobre o Composer com Rafael Dohms, que escreveu recentemente o artigo Instalando pacotes composer.

Agora eu tinha um pouco de embasamento para argumentar sobre o artigo de Davey:

  • Sempre dê commit nos arquivos composer.lock de aplicativos.
  • Nunca dê commit nos arquivos composer.lock de componentes.

Quando lhe pediram para explicar como isso deveria funcionar, acabei percebendo que isso era muito parecido com a lógica Ruby que eu vinha aprendendo desde 2010, quando estava construindo alguns projetos com Ruby on Rails.

O conselho da comunidade Ruby sobre arquivos de bloqueio tem sido sempre dar commit no arquivo Gemfile para aplicativos, mas ignorar o arquivo Gemfile.lock para a construção de Gems. Isso resolve um monte de problemas com dependências.

Minha percepção era de que eu estava apenas repetindo essa lógica Ruby, então pensei um pouco mais e passei a perceber que era melhor mudar de:

Nunca dê commit nos arquivos composer.lock para os componentes.

… para:

Talvez seja necessário dar commit em arquivos composer.lock para os componentes.

Por que somente algumas vezes? Bem, há dois problemas principais aqui. Dar commit em um arquivo composer.lock vai resolver um problema, mas criar outro.

Desenvolvimento de componentes

Quando desenvolver e contribuir para pacotes Composer, você provavelmente estará trabalhando em algum diretório como ~ / src / some-package, no qual você acabou de dar o check-out do repositório. Você também pode fazê-lo dentro de um aplicativo que esteja usando componentes do Composer, por isso, se seu componente estiver alocado em ~ /src/some-app/vendor/phil /some-package, então seria possível executar $ composer install lá mesmo e isso seria como instalar ~ /src/some-app/vendor/phil/some-package /vendor/phil/another-package.

O Composer não se preocupa com um nível extra de itens aninhados, assim como acontece em uma estrutura de pastas. Onde você estiver, executar o comando $ composer install irá procurar pelo arquivo composer.lock ou então pelo arquivo composer.json e fazer uma varredura no diretório ./vendor/ para buscar suas dependências.

Se você tiver um arquivo composer.lock dentro da base de código de some-package e o arquivo de bloqueio exigir a versão 1.1.5, não importa se todas as dependências de seu some-app estão exigindo a versão 1.1.5 ou 2.0.0 do mesmo pacote.

Isso significa que você pode ter um bloqueio para o componente, e isso só afetará aqueles que trabalham diretamente com esse componente. Ele não força ninguém a instalar esse componente em sua aplicativo para, por sua vez, usar a versão de uma dependência que esteja especificada no arquivo composer.lock. Essa pode ser uma boa ideia para a equipe de desenvolvimento enquanto trabalha em um componente, mas pode causar um outro problema.

Quão restrito é o Strict?

Um componente deve funcionar com uma gama razoável de dependências. Por exemplo, um componente utilizando Guzzle deve ser capaz de trabalhar com as versões 4.0, 4.1 ou 4.2, sem qualquer dúvida sobre se isso funciona.

Agora, se eu der commit em um arquivo de bloqueio da versão 4.2.0, ele passará a conter uma alteração de quebra de pacotes – apesar do cumprimento de requisitos do SemVer, vou começar a receber reclamações de usuários dizendo que meu pacote não funciona com a versão 4.2.0. Se eu estiver viajando por um mês, isso será um grande problema. Eu nem saberia que o pacote falhou porque meu componente exige especificamente a versão 4.1.2 para executar seus testes no Travis-CI e como essa é a última versão lançada quando meu arquivo composer.lock foi escrito, eu não teria tido a chance de tentar baixar a versão 4.2.0. Outros usuários que dependessem do meu pacote iriam obter a versão mais recente, porque seu comando $ composer install não está olhando com profundidade suficiente para os diretórios a fim de encontrar meus arquivos de componente composer.lock, portanto, eles estão à frente dos meus requisitos mais rigorosos.

Se eu tivesse sido um pouco menos rigoroso com minhas dependências de componentes, poderia de ter visto esse erro muito mais cedo. Possivelmente, na primeira solicitação de recebimento que um colaborador escreveu e, quem sabe, talvez eles poderiam corrigir esse problema para mim também como parte do seu PR. Então eu poderia apenas clicar no botão verde, aceitar e continuar com meu dia, em vez de peneirar changelogs para descobrir onde o pacote quebrou.

Depois que a mudança de ruptura é atendida, eu preciso checar meus requisitos para o Guzzle de ~ 4.1 para 4.2 ~ no arquivo composer.json, o que obrigaria os usuários a atualizar seus arquivos também, o que por sua vez poderia acabar levando a problemas de dependência.

Minha única solução seria evitar criar uma dependência para ~ 4.1 e manter a dependência universal de 4.1. * para tudo, o que é uma porcaria para os 99% dos casos em que pequenas atualizações não quebram a API.

Isso não é uma forma inédita de projetar software de forma a quebrar a compatibilidade com versões anteriores dos componentes. O SemVer é ainda uma promessa, que pode ser boa ou não. Algumas vezes intencionalmente ,você apenas não percebe a dependência e nem se preocupa com o SemVer, ou algumas vezes por uma dependência que o SemVer promete resolver, mas nem sempre consegue.

Resumo

Eu acho que dar commit no arquivo composer.lock é sempre uma ideia incrivelmente boa no que tange a aplicativos.

Mas acho muito difícil saber se o mesmo vale para seus componentes. Talvez você queira realmente bloquear determinada versão e gosta da ideia de especificar a exata versão de uma dependência que o componente necessita. Certamente poderíamos fazer o teste mais fácil, reduzir tudo à: “funciona na minha máquina” forçar as pessoas a enviar uma atualização como parte do PR. Na minha opinião, isso é um nível de rigor que não estou interessado em ter para meus componentes.

Talvez isso mude para mim ao longo do tempo. Mas, por agora, vou manter a entrada .gitignore nos arquivos composer.lock de meus componentes.

***

Artigo traduzido pela Redação iMasters, com autorização do autor. Publicado originalmente em https://philsturgeon.uk/php/2014/11/04/composer-its-almost-always-about-the-lock-file/