Uma das melhores coisas no Perl são os módulos open source que estão disponíveis pelo CPAN. Mas mantê-los é um trabalho árduo. Toda semana centenas de novos lançamentos ocorrem e você nunca sabe quando uma nova versão de um módulo pode introduzir um bug que quebrará a sua aplicação.
This article was originally published on Pragmatic Perl
Uma estratégia para resolver este tipo de prolema é construir seu próprio repositório do CPAN, que conterá apenas as versões dos módulos que você deseja. Dessa forma você pode utilizar o construtor do CPAN para instalar módulos a partir do seu próprio repositório sem ficar exposto ao CPAN público.
Através dos anos eu construí vários repositórios personalizados do CPAN utilizando ferramentas como CPAN::Mini e CPAN::Site. Mas eles sempre me pareceram desajeitados e nunca estive satisfeito com eles. A alguns anos atrás, fui contratado por um cliente para construir outro repositório personalizado do CPAN. Mas desta vez eu tive a oportunidade de começar a partir do zero. O resultado disso tudo é o projeto Pinto.
Pinto é uma ferramenta robusta para criar e manipular repositórios CPAN personalizados. Ele possui várias características poderosas que irão, de forma segura, lhe ajudar a gerenciar todos os módulos Perl cuja sua aplicação seja dependente. Este tutorial irá lhe ajudar a criar um repositório CPAN personalizado com o Pinto e ao mesmo tempo, demonstrar algumas de suas características.
Instalando o Pinto
O módulo Pinto está disponível pelo CPAN e pode ser instalado como qualquer
outro módulo utilizando o cpan ou o cpanm
. Porém o projeto Pinto é
mais uma aplicação do que uma biblioteca. É uma ferramenta que você utiliza
para gerenciar o código de suas aplicações, isso sem se tornar parte de sua
aplicação. Portanto eu recomendo instalar o Pinto como um projeto
independente através desses dois comandos:
curl -L http://getpinto.stratopan.com | bash
source ~/opt/local/pinto/etc/bashrc
Isso irá instalar o Pinto em ~/opt/local/pinto
e adicionará
os diretórios necessários em seu PATH
e MANPATH
. Todo o conteúdo
é localizado, isso significa que ao instalar o Pinto não irá afetar o seu ambiente de
desenvolvimento, e da mesma forma, o seu ambiente de desenvolvimento também não irá
afetar o Pinto.
Explorando o Projeto Pinto
Assim como quando explora uma ferramenta nova, a primeira coisa que você deverá saber é como buscar ajuda:
pinto commands # Apresenta uma lista de comandos disponíveis
pinto help <COMMAND> # Apresenta um sumário de opções e argumentos para o <COMMAND>
pinto manual <COMMAND> # Apresenta o manual completo para <COMMAND>
O projeto também é instalado com outra documentação, incluindo um tutorial e um guia rápido para referências. Você pode acessar esses documentos através dos comandos:
man Pinto::Manual::Introduction # Explica os conceitos básicos do projeto
man Pinto::Manual::Installing # Sugestões para a instalação
man Pinto::Manual::Tutorial # Um guia narrativo pelo projeto
man Pinto::Manual::QuickStart # Um sumário de comandos comuns
Criando o Repositório
O primeiro passo ao utilizar o projeto Pinto é criar um repositório
através do comando init
:
pinto -r ~/repo init
O comando criará um novo repositório no diretório ~/repo
.
Se o diretório não existir, ele será criado para você. Se ele já existir
então deve estar vazio.
A opção -r (ou --root) especifica aonde o repositório está. Isso é necessário
para todo comando. Mas se você se cansar de digitar isso sempre, você poderá
configurar a variável de ambiente PINTO_REPOSITORY_ROOT
para que
referencie o seu repositório, dessa forma você pode omitir o -r.
Inspecionando O Repositório
Agora que você já possui um repositório, vamos ver o que há por dentro.
Para ver o seu conteúdo, use o comando list
:
pinto -r ~/repo list
A partir desse ponto, a listagem retornará vazia porque não há nada no repositório.
Mas você irá utilizar o comando list
várias vezes durante o tutorial.
Adicioando Módulos do CPAN
Suponha que você esteja trabalhando em uma aplicação My-App que contém
um módulo My::App, e que dependa do módulo URI. Você pode trazer o módulo
URI para o seu repositório utilizando o comando pull
:
pinto -r ~/repo pull URI
Você será questionado a escrever uma mensagem que descreva o motivo de tal mudança em seu repositório. No topo da mensagem inclui uma mensagem simples que você poderá editar. O rodapé da mensagem mostrará exatamente quais módulos foram adicionados. Salve o arquivo e feche o editor de texto quando terminar.
Agora você deverá ter o módulo URI em seu repositório Pinto. Então vamos
ver o que realmente temos. Novamente use o comando list
para
ver o conteúdo do repositório:
pinto -r ~/repo list
Dessa vez a listagem irá se parecer com isso:
rf URI 1.60 GAAS/URI-1.60.tar.gz
rf URI::Escape 3.31 GAAS/URI-1.60.tar.gz
rf URI::Heuristic 4.20 GAAS/URI-1.60.tar.gz
...
Você pode observar que o módulo URI foi adicionado ao repositório, assim como os requisitos para o URI, e todos os requisitos dos requisitos, e assim por diante.
Adicionando Módulos Particulares
Agora suponha que você terminou o seu trabalho no módulo My-App e você está
pronto para liberar a primeira versão. Utilizando sua ferramenta preferida de
instalação (e.g. ExUtils::MakeMaker, Module::Builder, Module::Install, etc.)
você cria o pacote da sua distribuição como My-App-1.0.tar.gz. Agora você
pode colocar a distribuição no repositório Pinto com o comando add
:
$> pinto -r ~/repo add path/to/My-App-1.0.tar.gz
Novamente será necessário descrever em uma mensagem as mudanças realizadas. Agora, quando você lista o conteúdo do repositório, verá o módulo My::App mostrando você como autor da distribuição.
rl My::App 1.0 JEFF/My-App-1.0.tar.gz
rf URI 1.60 GAAS/URI-1.60.tar.gz
rf URI::Escape 3.31 GAAS/URI-1.60.tar.gz
rf URI::Heuristic 4.20 GAAS/URI-1.60.tar.gz
...
Instalando Módulos
Agora que você já possui seus módulos dentro do repositório, o próximo passo
será construir e instalar os módulos em algum outro lugar. Por baixo dos panos
o repositório do Pinto é organizado da mesma forma que o repositório do CPAN, então
ele é totalmente compatível com o cpanm
e qualquer outro instalador Perl.
Tudo o que você precisa fazer é apontar o instalador para o seu novo repositório:
cpanm --mirror file://$HOME/repo --mirror-only My::App
Isso irá construir e instalar o módulo My::App utilizando somente os módulos em seu repositório Pinto. Isso significa que você terá exatamente as mesmas versões dos módulos toda vez que realizar a instalação, mesmo que os módulos sejam removidos ou atualizados no CPAN.
Com o cpanm, a opção --mirror-only é importante por que previne o cpanm de
realizar buscas nos CPAN quando não encontra algum módulo em seu repositório.
Quando isso acontece, normalmente significa que algumas distribuições no
repositório não possuem todas as dependências devidamente descritas em seu
arquivo META. Para corrigir o problema, apenas utilize o comando pull
para buscar os módulos faltantes.
Atualizando Módulos
Suponha que diversas semanas se passaram desde que você lançou a primeira
versão do My-App e agora o módulo URI está na versão 1.62, disponível pelo CPAN.
Ele agora possui algumas correções que você gostaria de poder ter. Novamente, podemos
trazer o novo módulo ao repositório utilizando o comando pull
. Mas já que
o seu repositório já possui uma versão do módulo URI, você agora deve indicar que
quer uma versão nova especificando a versão mínima que você deseja:
pinto -r ~/repo pull URI~1.62
Se observar agora na listagem dos módulos irá ver que a nova versão do URI já está presente:
rl My::App 1.0 JEFF/My-App-1.0.tar.gz
rf URI 1.62 GAAS/URI-1.62.tar.gz
rf URI::Escape 3.38 GAAS/URI-1.62.tar.gz
rf URI::Heuristic 4.20 GAAS/URI-1.62.tar.gz
...
Se a nova versão do URI exigir uma nova versão de algum outro módulo adicional, estes também serão atualizados. E quando instalar o My::App, você terá a versão 1.62.
Trabalhando Com Pilhas (Stacks)
Até o momento nós tratamos o repositório como uma fonte de recurso única. Então quando atualizamos o módulo URI na última seção, afetamos todas as pessoas e aplicações que possam estar utilizando do mesmo repositório. Mas esse tipo de impacto abrangente é indesejado. Você gostaria de realizar mudanças de forma isolada e testá-las antes de forçar as outras pessoas a atualizar. É justamente para isso que as pilhas servem.
Todo repositório do tipo CPAN possui um índice que mapeia a última versão de cada módulo ao arquivo que a contém. Normalmente há apenas um índice por repositório. Diferentemente, em um repositório Pinto pode haver diferentes índices. Cada índice é chamado stack (ou pilha). Isso lhe permite criar diferentes stacks de dependências dentro de um único repositório. Então você pode ter um stack "desenvolvimento" e um stack "produção", ou um stack "perl-5.8" e outro "perl-5.16". Toda vez que você atualiza ou adiciona um módulo, irá afetar apenas um único stack.
Mas antes de seguir a diante, você precisa saber sobre o stack padrão. Para a maioria das aplicações, o nome do stack é um parâmetro opcional. Então se você não especificar um stack, então o comando é aplicado para qualquer stack marcado como padrão.
Em qualquer repositório, não irá ocorrer mais de um stack padrão. Quando criamos um repositório, um stack chamado "master" também é criado e então marcado como padrão. Você pode alterar o stack padrão ou mudar o seu nome, mas eu não mexeria nisso. Apenas lembre-se que "master" é o nome do stack criado com o novo repositório.
Criando Uma Pilha
Suponha que seu repositório contenha a versão 1.60 do módulo URI mas a versão 1.62 foi liberada no CPAN, como descrevemos antes. Você quer tentar atualizar o seu repositório, mas dessa vez irá realizar isso em um stack separado.
Até o momento, tudo que você adicionou no repositório foi para o stack "master".
Então nós iremos agora criar um clone desse stack utilizando o comando copy
:
pinto -r ~/repo copy master uri_upgrade
Esse comando criará um novo stack chamado "uri_upgrade". Se você quer ver o conteúdo
apenas utilize o comando list
com a opção "--stack":
pinto -r ~/repo list --stack uri_upgrade
A listagem deverá ser idêntica ao "master":
rl My::App 1.0 JEFF/My-App-1.0.tar.gz
rf URI 1.60 GAAS/URI-1.60.tar.gz
...
Atualizando uma Pilha
Agora que você já possui um stack separado você pode tetar atualizar o módulo URI.
Assim como antes, você utilizará o comando pull
. Mas desta vez, você dirá
que utilize o stack "uri_upgrade":
pinto -r ~/repo pull --stack uri_upgrade URI~1.62
Nós podemos comparar o "master" e o "uri_upgrade" utilizando o comando diff
:
pinto -r ~/repo diff master uri_upgrade
+rf URI 1.62 GAAS/URI-1.62.tar.gz
+rf URI::Escape 3.31 GAAS/URI-1.62.tar.gz
+rf URI::Heuristic 4.20 GAAS/URI-1.62.tar.gz
...
-rf URI 1.60 GAAS/URI-1.60.tar.gz
-rf URI::Escape 3.31 GAAS/URI-1.60.tar.gz
-rf URI::Heuristic 4.20 GAAS/URI-1.60.tar.gz
A saída é similar ao comando diff(1). Registros começando por "+" foram adicionados e aqueles que começam com "-" foram removidos. Você pode notar que os módulos da distribuição URI-1.60 foram substituídos por módulos da distribuição URI-1.62.
Instalando a Partir da Pilha
Uma vez que tenha novos módulos na stack "uri_upgrade", você pode tentar construir sua aplicação ao apontar o cpanm para aquele stack. Cada stack é nada mais do que um subdiretório dentro do repositório, então tudo o que você precisa fazer é:
cpanm --mirror file://$HOME/repo/stacks/uri_upgrade --mirror-only My::App
Se todos os testes passaram, então você agora pode atualizar o módulo URI
para a versão 1.62 na stack "master", utilizando o comando pull
. Como
"master" é o stack padrão, você pode omitir o parâmetro "--stack":
pinto -r ~/repo pull URI~1.62
Trabalhando com Fixadores (Pins)
stacks são ótimas formas de você testar o efeito que ocorre com mudanças nas dependências de suas aplicações. Mas e se os testes não passarem? Se o problema reside dentro do My-App você pode rapidamente corrigi-lo, e em seguida lançar a versão 2.0 do My-App, e então, prosseguir com a atualização do URI no stack "master".
Mas e se a origem do bug está no URI então irá levar um bom tempo até que o My-App seja corrigido, e você agora tem um problema. Você não que que outra pessoa qualquer atualize o módulo URI ou que ele seja atualizado inadvertidamente para satisfazer algum outro requisito que o My-App possa ter. Até que você saiba que o problema fora corrigido, você precisará impedir que o módulo URI seja atualizado. É para isso que os pins servem.
Fixando um Módulo
Quando você utiliza um pin (fixador) em um módulo, aquela versão do módulo é
obrigada a se manter no stack. Qualquer tentativa de atualizar o módulo
(tanto diretamente quanto indiretamente) irá falhar. Para fixar um módulo,
utilize o comando pin
:
pinto -r ~/repo pin URI
Se vocẽ observar a listagem do stack "master" novamente, irá observar algo como isso:
...
rl My::App 1.0 JEFF/My-App-1.0.tar.gz
rf! URI 1.60 GAAS/URI-1.60.tar.gz
rf! URI::Escape 3.31 GAAS/URI-1.60.tar.gz
...
O símbolo "!" próximo ao início do registro indica que o módulo está fixado. Se alguém tentar atualizar o módulo URI ou adicionar uma distribuição que exige uma versão mais nova do módulo URI, então você receberá um aviso, não sendo possível adicionar a nova distribuição. Note que todo módulo na distribuição URI-1.60 está fixado, então é impossível atualizar parcialmente a distribuição (essa situação pode acontecer quando um módulo entra em uma distribuição diferente).
Desafixando um Módulo
Após um certo tempo, suponha que você resolveu o problema no módulo My-App
ou então uma nova versão do URI foi lançada, corrigindo o bug.
Nessa situação, você agora já pode desafixar o módulo URI da stack
através do comando unpin
:
pinto -r ~/repo unpin URI
A partir de agora você está livre para atualizar o módulo URI para a última versão. Da mesma forma que acontece quando você fixa um módulo, ao desafixá-lo, você libera também todos os módulos da distribuição.
Utilizando Fixadores e Pilhas ao Mesmo Tempo
As pilhas e os fixadores (pins e stacks) são comumente utilizadas juntas para ajudar a gerenciar mudanças em seu ciclo de desenvolvimento. Por exemplo você pode criar uma stack chamada "prod" que contém suas dependências de confiança. Ao mesmo tempo você pode ter também uma stack chamada "dev" que contém módulos experimentais para seu próximo lançamento. Inicialmente, a stack "dev" é apenas uma cópia da "prod".
Ao longo do processo de desenvolvimento, você pode atualizar ou adicionar vários módulos na pilha "dev". Se um módulo atualizado quebrar sua aplicação, então você pode colocar um pin naquele módulo, dentro da stack "prod", sinalizando que aquele módulo não deve ser atualizado.
Fixadores e Adendos (Patches)
Em certas situações você pode se deparar com uma nova versão de uma distribuição no CPAN com um bug cujo autor é incapaz ou impossibilitado de corrigir. Nesta situação, você pode decidir realizar um patch local para aquela distribuição.
Então suponha que você realizou um fork do código do módulo URI e
criou uma versão local chamada URI-1.60_PATCHED.tar.gz. Você pode agora
adicioná-la ao seu repositório utilizando o comando add
:
pinto -r ~/repo add path/to/URI-1.60_PATCHED.tar.gz
Nesta situação é aconselhável fixar o módulo, já que você não gostaria de atualizar o módulo até que tenha certeza de que uma nova versão do CPAN inclua o patch ou então o autor corrija o problema.
pinto -r ~/repo pin URI
Quando o autor do URI liberar a versão 1.62 você irá querer testá-la antes de
decidir se pode desafixar da sua versão local. Assim como antes, isso pode ser realizado
clonando o stack através do comando clone
.
Desta vez vamos chamar o stack de "trial":
pinto -r ~/repo copy master trial
Mas antes de você poder atualizar o URI no stack "trial", você deverá desafixá-lo:
pinto -r ~/repo unpin --stack trial URI
Agora você pode proceder com a atualização do URI na stack e tentar construir a aplicação My::App, dessa forma:
pinto -r ~/repo pull --stack trial URI~1.62
cpanm --mirror file://$HOME/repo/stacks/trial --mirror-only My::App
Se tudo der certo, remova o pin do stack "master" e puxe a nova versão do URI de volta.
pinto -r ~/repo unpin URI
pinto -r ~/repo pull URI~1.62
Revisando o Histórico de Mudanças
Como você deve ter notado, cada comando que altera o estado de um stack
exige uma mensagem que o descreva. Você pode revisar essas mensagens
através do comando log
:
pinto -r ~/repo log
Isso deve retornar algo do tipo:
revision 4a62d7ce-245c-45d4-89f8-987080a90112
Date: Mar 15, 2013 1:58:05 PM
User: jeff
Pin GAAS/URI-1.59.tar.gz
Pinning URI because it is not causes our foo.t script to fail
revision 4a62d7ce-245c-45d4-89f8-987080a90112
Date: Mar 15, 2013 1:58:05 PM
User: jeff
Pull GAAS/URI-1.59.tar.gz
URI is required for HTTP support in our application
...
O cabeçalho de cada mensagem mostra quem fez a mudança e quando ela aconteceu. é possível ver também um identificador único, similar ao SHA-1 do Git. Você pode usar esses identificadores para ver a diferença entre as revisões ou para retornar o stack para um estado anterior (isso na verdade ainda não está implementado).
Conclusões
Neste tutorial, você viu os comandos básicos para criar um repositório Pinto e como adicionar módulos a ele. Você viu também como utilizar os stacks e os pins para gerenciar as sua dependências frente a alguns problemas e obstáculos no desenvolvimento.
Cada comando possui várias opções que não foram discutidas neste tutorial, e há ainda outros comandos que não foram mencionados aqui. Então eu o encorajo a explorar o manual de cada comando e aprender mais.