Desenvolvimento

14 set, 2016

Dependências consistentes no NPM com NPM Shrinkwrap

Publicidade

Desde o inicio da era dos desenvolvedores, buscamos cada vez mais a consistência no processo de desenvolvimento. A consistência facilita a automatização de etapas como entrega contínua, integração contínua e deploy contínuo.

Uma das coisas mais importantes para manter a consistência é saber gerenciar as dependências de uma aplicação, ou seja, garantir que as mesmas dependências usadas em desenvolvimento irão também ser usadas por todo o tipo de processo que pegar esse código, inclusive produção.

O Twelve-Factor App fala sobre dependências consistentes em um dos tópicos disponíveis em https://12factor.net/pt_br/dependencies.

NPM e dependências

Como sabemos, o NPM (Node Package Manager) é o gerenciador de dependências mais usado no ambiente Node.JS e também está tomando cada vez mais espaço nos frameworks front-end. Nele, é possível descrever quais dependências serão utilizadas, tanto para produção quanto para desenvolvimento. Ele usa o padrão de versionamento chamado semantic versioning, no qual é possível definir algumas regras para as dependências, como aceitar versões apenas do tipo path/bugfix ou minor.

O problema

O package.json a seguir possui uma lista de dependências, todas elas usam o versionamento semântico.

"dependencies": {

   "babel": "^6.5.0",

   "babel-cli": "^6.11.4",

   "babel-preset-es2015": "^6.9.0",

   "body-parser": "^1.15.2",

   "express": "^4.14.0",

   "http-status": "^0.2.3",

   "sequelize": "^3.23.6",

   "sqlite3": "^3.1.4"

 }

Notem que antes de cada versão temos um ^ esse símbolo significa que vamos aceitar qualquer minor release desses módulos. Por exemplo o “babel”: “^6.5.0” seria o mesmo que “babel”: “^6.*”.

Isso significa que toda a vez que eu rodar npm install, ele vai instalar a versão mais nova dessas dependências.

Imagine que terminamos nossa task de sexta feira e commitamos, os testes passaram, tudo lindo, quando atualizamos a produção BUMMMMM!!! alguma dependência atualizou e quebrou nossa aplicação. Ué, mas em desenvolvimento estava funcionando? Como assim? Aí vamos checar o repositório da dependência e vemos que saiu uma versão durante o intervalo que estávamos fazendo deploy em produção. Parece louco, né? Improvável? Na verdade, é bem comum.

Como solucionar isso? Devo fixar minhas versões no package.json e garantir que sempre que rodar o npm install vai instalar as mesmas versões? Algo como “babel”: “6.5.0” ?

  • Não, essa ainda não é a solução, pois além de ser muito trabalho manual devemos lembrar que dependências possuem dependências, e elas também podem quebrar nossa aplicação.

A solução

Queremos que quando alguém ou algum processo pegar nossa aplicação e rodar o comando npm install, a aplicação busque exatamente as mesmas versões que foram instaladas em modo de desenvolvimento, as mesmas dependências e versões, inclusive das dependências das dependências.

Uma solução comum para isso é ter um arquivo de .lock, que guarda as versões exatas de cada uma das dependências instaladas para que quando for rodado o comando de install ele instale as versões exatas. O npm não possuía isso há algum, mas de tanto o pessoal reclamar e pressionar foi desenvolvido o npm shrikwrap.

Fixando versões com o NPM Shrinkwrap

O npm shrinkwrap é um comando nativo do npm que cria um arquivo chamado npm-shrinkwrap.json com as versões atuais instaladas de cada pacote, como no exemplo abaixo:

npm

npm-1

O npm shrinkwrap foi feito para ser usado em modo de produção. Para usar em desenvolvimento, faça como no exemplo abaixo:

npm shrinkwrap --dev //adiciona também as devDependencies

Caso eu delete minha pasta node_modules ou faça a instalação do projeto do zero e rode npm install, o npm vai ler o npm-shrinkwrap.json e instalar as versões salvas nele.

Atualizando dependências

Para atualizar dependências, rodamos o comando npm update, que vai verificar as versões que podem ser atualizadas e irá instalar suas atualizações. Veja a imagem abaixo:

npm-2

O exemplo acima mostra que o babel foi atualizado para a versão 6.5.1; agora o npm-shrinkwrap.json está desatualizado; para que ele volte a refletir as versões corretas, vamos novamente rodar o comando npm shrinkwrap para gerar um novo npm-shrinkwrap.json atualizado.

Deploy de aplicações e versionamento

Devemos sempre commitar o package.json e também o npm-shrinkwrap.json para o controle de versão, seja ele git, mercurial ou qualquer outro, pois assim todo mundo que pegar esse código terá a lista de versões utilizadas na ultima atualização do código.

Até mais, pessoal!