Alexei Znamensky

Oh no!! It's Dist::Zilla!!
Publicado em 01/03/2011

Oh no!! It's Dist::Zilla!!

O que nós fizemos para merecer isso?

A manutenção de um módulo Perl é cheia de tarefas repetitivas: configurar o Makefile.PL, atualizar documentação, atualizar versão de dependências, copiar as mais diversas suítes de testes para a árvore, formatar o código, "commitar" o código do controle de versão - eventualmente aplicar tags nesse controle de versão e, por fim, fazer o upload do módulo para o PAUSE[1], e de lá para o CPAN[2].

Especificamente do ponto de vista de documentação, há a chata tarefa de atualizar a versão do módulo em todos os arquivos que fazem parte dele - se forem muitos, a chateação também será muita. Adicionar seções com informações "repetidas", como AUTHOR, LICENSE, SUPPORT, entre outras, é também algo repetitivo e sujeito a erros bobos se feito manualmente. Alguns módulos fornecem um arquivo README que é a renderização em texto do POD do arquivo principal do módulo - também é uma tarefa que poderia ser facilmente automatizada.

Por fim, quando estamos trabalhando em equipe, precisamos ter um maior cuidado com o sistema de controle de versões, criando branches específicos para times e/ou tarefas específicas, e realizando o merge dos mesmos depois. O sistema de controle de versão que, se ainda não é, está se tornando o mais popular na comunidade Perl (e de outras linguagens também) é o git[3], software originalmente escrito pelo próprio Linus Torvalds[4].

As principais vantagens do git são:

* Sistema distribuído: não é necessário realizar todas as operações contra um repositório central (ainda que ter um repositório central seja algo bom, em algum ponto do processo, e o git não impede isso).
* Facilidade para fazer branches e merges: o git permite criar branches novos de desenvolvimento e realizar o merge dos mesmos com rapidez e segurança.
* O modelo de funcionamento do git viabilizou serviços como o github[5], um serviço público de hospedagem de repositórios git, oferecido gratuitamente.
* Software Livre e Aberto: o git é Free Software, Open Source. Os fontes do git estão disponíveis publicamente[6].

E agora, quem irá nos defender?

Em Maio de 2008(?)[7], Ricardo Signes, (RJBS), desenvolvedor norte-americano, membro atuante da comunidade Perl, contribuidor do CPAN, com dezenas de módulos[8] publicados, começou um projeto ambicioso, o Dist::Zilla[9]. O Dist::Zilla, como descrito em seu site[10], é um "programa para facilitar a escrita, o empacotamento, o gerenciamento e a publicação de software livre". Para nós programadores e batalhadores cotidianos, isso significa: menos tempo gasto com códigos, arquivos e configurações boilerplate, e mais tempo disponível para dedicar-se à codificação propriamente dita. Naturalmente, aqui, estamos falando de Perl - iremos usar o Dist::Zilla para escrever módulos e/ou aplicativos em Perl.

Este artigo não pretende ser um guia completo sobre o Dist::Zilla. Para uma documentação mais abrangente, sugerimos ler os tutoriais do Dist::Zilla[11]. Vamos mostrar aqui o básico, e um pouco além.

O que faz?

O Dist::Zilla permite várias ações. Para executar essas ações, usamos a aplicação de linha de comando que vem com o Dist::Zilla, que é o comando dzil. Alguns dos usos mais freqüentes:

Para executar toda a suíte de testes do módulo:

	$ dzil test
	[DZ] building test distribution under .build/nQGOr0w7RL
	...

Para gerar um build (tar-ball) do módulo:

	$ dzil build
	...

Para fazer um release: build, (opcionalmente) rodar todos os testes e, (idem) fazer o upload do módulo para o CPAN:

	$ dzil release
	...

Como começar?

O Dist::Zilla é controlado pelas configurações em arquivos ".ini", tanto para configurações do usuário quanto para configurações do projeto.

Para poder ter o Dist::Zilla funcionando, você deve, antes de mais nada, instalá-lo:

	$ cpan Dist::Zilla

Uma vez que o cpan termine a instalação, você terá disponível o comando dzil no seu shell. É preciso então fazer um setup do Dist::Zilla:

	dzil setup

Esse comando irá gerar um arquivo ~/.dzil/config.ini, contendo as informações solicitadas. Por exemplo:

	[%User]
	name  = Alexei Znamensky
	email = russoz@cpan.org

	[%Rights]
	license_class    = Perl_5
	copyright_holder = Alexei Znamensky

Note que o dzil setup perguntará sobre usuário e senha do CPAN. Caso você escolha por colocar essa informação no Dist::Zilla, ela estará presente no config.ini. Opcionalmente você pode colocar essa informação separadamente no arquivo ~/.pause:

	user russoz
	password p0t@toe5

Você não é obrigado a ter essa informação em nenhum desses arquivos, isso é estritamente opcional, mas, naturalmente, não será possível fazer o upload automático do módulo para o CPAN sem esses dados.

Com isso você já está pronto para usar o Dist::Zilla no seu módulo.

Convertendo um módulo

Vamos exemplificar o uso do Dist::Zilla com a conversão de um módulo, pois fica mais fácil de visualizar o que substitui o quê. Esta seção é adaptada do tutorial escrito pelo próprio RJBS sobre conversão de um módulo[12].

No começo deste ano este autor que aqui escreve se tornou co-mantenedor do módulo Queue::Base[13]. Além de alguns ajustes no código do módulo, o Queue::Base foi utilizado como cobaia para a conversão para Dist::Zilla.

Para começarmos a utilizar o Dist::Zilla, precisamos criar, no diretório raiz da distribuição/módulo/aplicação, um arquivo dist.ini.

Basicamente os passos para conversão foram:

Eliminando o Makefile.PL

O Makefile.PL continha o seguinte código:

	use inc::Module::Install;

	name     'Queue-Base';
	all_from 'lib/Queue/Base.pm';
	author   q{Alexei Znamensky };
	license  'perl';

	requires 'version' => 0.77;

	build_requires 'Test::More';

	auto_install;

	WriteAll;

Esse arquivo pode ser removido. Não será mais necessário. Ao invés dele, teremos um dist.init:

	name    = Queue-Base
	version = 2.0_2
	author  = Farkas Arpad 
	author  = Alexei Znamensky 
	license = Perl_5
	copyright_holder = Alexei Znamensky

	[GatherDir]
	[MetaYAML]
	[ModuleInstall]        <<<<<<<<< AQUI
	[Manifest]
	...

Um dist.ini irá conter tags de seções e definições, como informações sobre o módulo e o autor.

Notem nesse trecho anotado do primeiro dist.ini do Queue::Base[14], que colocamos uma tag de seção na qual especificamos o uso do Module::Install[15].

Todas essas tags de seções (com exceção aos que começarem com "@" e outros que contém uma barra "/"), correspondem a classes no namespace Dist::Zilla::Plugin::, isto é, no trecho acima estamos referenciando as classes:

* Dist::Zilla::Plugin::GatherDir
* Dist::Zilla::Plugin::MetaYAML
* Dist::Zilla::Plugin::ModuleInstall
* Dist::Zilla::Plugin::Manifest

Dessas quatro classes[16][17][18][19], Dist::Zilla::Plugin::ModuleInstall não faz parte do próprio Dist::Zilla, ela foi desenvolvida por Kent Fredric[20].

Eliminando outros arquivos

Existem vários plugins prontos, para realizar uma infinidade de diferentes tarefas[21]. Destacamos aqui alguns dos mais importantes (na opinião desde autor):

	[GatherDir]     - lista os arquivos
	[Manifest]      - gera arquivo MANIFEST
	[Readme]        - gera arquivo README
	[License]       - gera arquivo LICENSE
	[MakeMaker]     - gera um Makefile.PL (ExtUtils::MakeMaker)
	[ModuleInstall] - idem (Module::Install)
	[ModuleBuild]   - gera um Build.PL (Module::Build)
	[PreReqs]       - permite especificar dependências
	[AutoPrereqs]   - detecta dependências automaticamente
	[PodVersion]	- acrescenta VERSION a cada arquivo .pm
	[OurPodVersion] - idem, usando our $VERSION

A lista completa seria finita, mas enorme. Para facilitar um pouco a nossa vida, existem os bundles, que agrupam vários plugins em uma única seção. O mais imediato é o Basic, que é fornecido pelo próprio Dist::Zilla. Para usá-lo, basta colocar no dist.ini:

	[@Basic]

Esse bundle é definido na classe Dist::Zilla::PluginBundle::Basic[22], e corresponde aos plugins: [GatherDir], [PruneCruft], [ManifestSkip], [MetaYAML], [License], [Readme], [ExtraTests], [ExecDir], [ShareDir], [MakeMaker], [Manifest], [TestRelease], [ConfirmRelease], [UploadToCPAN].

Utilizando o [@Basic] podemos (devemos!) remover os arquivos: README, LICENSE, MANIFEST e, se estivermos usando o [ModuleInstall], podemos também remover o diretório inc/ - ele será gerado automaticamente durante o build com os arquivos necessários.

Caçando Plugins

Alguns dos plugins utilizados podem não estar instalados. Para saber quais plugins você precisa instalar, basta rodar:

	dzil authordeps

É possível criar novos bundles, e é uma prática comum que os autores criem bundles com seus grupos de plugins prediletos.

Existem plugins que realizam a mesma tarefa que algum plugin que já faz parte de um bundle que esteja em uso. Nesse caso, o plugin declarado no bundle será ignorado e o plugin declarado no dist.ini será utilizado.

A escolha do conjunto de plugins a utilizar depende de muitas coisas, mas o fator decisivo é a preferência do desenvolvedor.

Exemplo Comentado

Vamos colar aqui o dist.ini atual do projeto Queue::Base e comentar cada trecho.

Informações básicas do projeto

	name    = Queue-Base
	version = 2.200
	author  = Alexei Znamensky 
	license = Perl_5
	copyright_holder = Farkas Arpad

Várias informações específicas do projeto: nome, autor, tipo de licença, etc.. Particularmente, o número da versão poderia ser automatizado, e será algum dia para o Queue::Base - mas enquanto isso não ocorre, ele continua sendo declarado aqui. Essas informações podem depois ser utilizadas para gerar documentação POD automaticamente.

Meta-recursos

	[MetaResources]
	bugtracker.web  = http://github.com/russoz/Queue-Base/issues
	repository.web  = http://github.com/russoz/Queue-Base
	repository.url  = git://github.com/russoz/Queue-Base.git
	repository.type = git

Meta-informações sobre o probjeto, como por exemplo o endereço da index.tpage, onde reportar erros, o repositório de controle de versões.

Itens "Básicos"

	[@Basic]
	[MetaJSON]
	[ReadmeFromPod]
	[InstallGuide]
	[GitFmtChanges]
	max_age    = 365
	tag_regexp = ^.*$
	file_name  = Changes
	log_format = short

Usando a tag [@Basic] incluímos todos aqueles plugins listados acima, mas também:

* [MetaJSON]

Escolhemos gerar também um arquivo META.json com as meta-informações do pacote para indexação no CPAN;

* [ReadmeFromPod]

Geramos o arquivo README automaticamente a partir do POD do arquivo principal do módulo (no caso do Queue::Base, seria o arquivo lib/Queue/Base.pm);

* [InstallGuide]

Geramos um arquivo INSTALL com instruções de instalação (de acordo com o builder utilizado: MakeMaker, Module::Install ou Module::Build);

* [GitFmtChanges]

Geramos o arquivo Changes com a listagem das modificações realizadas no projeto, a partir dos logs do git.

Versão

	[OurPkgVersion]

Optamos por utilizar a tag [OurPkgVersion] para inserir o número de versão nos arquivos, tanto em código (our $VERSION = x.yz) quanto em POD (=head1 VERSION).

Documentação

	[PodWeaver]

Ao utilizar o [PodWeaver] no Dist::Zilla, estamos na verdade utilizando o Pod::Weaver[23] para gerar trechos da documentação em formato POD. Este artigo pretendia, em sua incepção explanar mais sobre o Pod::Weaver, mas decidimos nos manter somente com o Dist::Zilla para não virar um livro. Entre outras coisas, podem ser atualizadas automaticamente no POD dos arquivos as seções: NAME (com descrição, em formato padrão), VERSION, AUTHOR, COPYRIGHT & LICENSE, SUPPORT (com informação sobre Perldoc, websites), AVAILABILITY, DISCLAIMER OF WARRANTY. O Pod::Weaver possui um arquivo de configuração específico, o weaver.ini. Veja o weaver.ini[24] utilizado no Queue::Base, para exemplo.

Dependências

	[AutoPrereqs]

O jeito mais simple é utilizar o [AutoPrereqs], que irá percorrer os seus arquivos levantando as dependências. Naturalmente pode haver pequenas divergências, ou podemos querer forçar uma versão mínima específica de algum módulo. Para esses e outros casos, podemos usar a tag [Prereqs], declarando explicitamente as versões necessárias.

Formatação

	[PerlTidy]

Incluindo a tag [PerlTidy] no seu dist.ini, você terá a garantia de que o código do seu módulo será formatado de uma maneira consistente ANTES do empacotamento. Você pode usar as definições padrão do Perl::Tidy[25] ou pode fornecer o seu próprio perltidyrc.

Controle de Versões

	[@Git]

O bundle [@Git], criado por Jerome Quelin[26], engloba os plugins: [Git::Check], [Git::Commit], [Git::CommitBuild], [Git::Init], [Git::NextVersion], [Git::Push], [Git::Tag]. Obviamente, com ele podemos automatizar várias atividade relacionadas ao git, das quais destacam-se a "checagem" (se houver arquivos não "commitados" no git, ele irá interromper o release), a aplicação de tag com o número da versão, e o push automático para o repositório remoto. Similarmente, existem plugins para o Subversion[27] e o Mercurial[28].

Testes

	[ReportVersions]
	[CompileTests]
	[EOLTests]
	[PodCoverageTests]
	[UnusedVarsTests]
	[CriticTests]
	[HasVersionTests]
	[KwaliteeTests]
	[MetaTests]
	[PodSyntaxTests]

Podemos incluir testes padronizados de código, compilação, documentação e estilo, adicionando tags no dist.ini, como pode ser visto acima. Duas das principais vantagens são: não termos de copiar arquivos de teste manualmente e não termos de mantê-los atualizados (sempre que fazemos o release são utilizadas as versões atuais (presumindo que mantemos nossa instalação atualizada) dos módulos.

Conclusão

Ainda existe espaço para várias melhorias, e para vários plugins, o Dist::Zilla é um projeto em andamento. Mas é uma ferramenta indispensável para um desenvolvedor em Perl Moderno, que não pode ou não tem mais tempo para ficar repetindo as mesmas tarefas "burocráticas" a cada novo módulo.

O Dist::Zilla é uma fantástica ferramenta de apoio. Ela facilita o processo de desenvolvimento, testes, release, e até mesmo o de documentar seu código. E o melhor é que você não precisa jogar fora tudo o que tem pronto para começar a usá-lo: a conversão pode ser feita de forma gradual.

Referências

[1] PAUSE - The [Perl programming] Authors Upload Server - http://pause.perl.org/

[2] CPAN - Comprehensive Perl Archive Network - http://www.cpan.org

[3] GIT - The fast version control system - http://git-scm.com

[4] Linus Torvalds - Artigo na Wikipedia - http://en.wikipedia.org/wiki/Linus_Torvalds

[5] github - social coding - http://github.com

[6] git/git - repositório do git - https://github.com/git/git

[7] Dist::Zilla - primeiro commit? - https://github.com/rjbs/dist-zilla/commit/f3854f7c675a11c6b3832dc111f72e6d705c09fa

[8] Ricardo Signes - Módulos no CPAN - http://search.cpan.org/~rjbs/

[9] Dist::Zilla - "distribution builder; installer not included!" - http://search.cpan.org/perldoc?Dist::Zilla

[10] Dist::Zilla - index.t Page - http://dzil.org/

[11] Dist::Zilla - Tutorials - http://dzil.org/tutorial/start.html

[12] Dist::Zilla - "Converting a Dist to Dist::Zilla" - http://dzil.org/tutorial/convert-dist.html

[13] Queue::Base - "Simple OO style queue implementation" - http://search.cpan.org/perldoc?Queue::Base

[14] Queue::Base/dist.ini - primeira versão do dist.ini no Queue::Base - https://github.com/russoz/Queue-Base/blob/3702c381595c0dfa6e7644bd945987b95af2e20c/dist.ini

[15] Module::Install - "Standalone, extensible Perl module installer" - http://search.cpan.org/perldoc?Module::Install

[16] Dist::Zilla::Plugin::GatherDir - "gather all the files in a directory" - http://search.cpan.org/perldoc?Dist::Zilla::Plugin::GatherDir

[17] Dist::Zilla::Plugin::MetaYAML - "produce a META.yml" - http://search.cpan.org/perldoc?Dist::Zilla::Plugin::MetaYAML

[18] Dist::Zilla::Plugin::ModuleInstall - "Build Module::Install based Distributions with Dist::Zilla" - http://search.cpan.org/perldoc?Dist::Zilla::Plugin::ModuleInstall

[19] Dist::Zilla::Plugin::Manifest - "build a MANIFEST file" - http://search.cpan.org/perldoc?Dist::Zilla::Plugin::Manifest

[20] Kent Frederic - Módulos no CPAN - http://search.cpan.org/~kentnl/

[21] CPAN - Search "Dist::Zilla::Plugin" - http://search.cpan.org/search?query=Dist::Zilla::Plugin&mode=all

[22] Dist::Zilla::PluginBundle::Basic - "the basic plugins to maintain and release CPAN dists" - http://search.cpan.org/perldoc?Dist::Zilla::PluginBundle::Basic

[23] Pod::Weaver - "weave together a Pod document from an outline" - http://search.cpan.org/perldoc?Pod::Weaver

[24] Queue::Base/weaver.ini - exemplo de arquivo weaver.ini - https://github.com/russoz/Queue-Base/blob/master/weaver.ini

[25] Perl::Tidy - "Parses and beautifies perl source" - http://search.cpan.org/perldoc?Perl::Tidy

[26] Jerome Quelin - Módulos no CPAN - http://search.cpan.org/~jquelin/

[27] Subversion - "Enterprise-class centralized version control for the masses" - http://subversion.apache.org/

[28] Mercurial - "Mercurial is a free, distributed source control management tool. It efficiently handles projects of any size and offers an easy and intuitive interface" - http://mercurial.selenic.com/

Agradecimentos

Ricardo Signes (RJBS)

Pelo Dist::Zilla e pela sua prestatividade.

Comunidade São Paulo Perl Mongers

Pelo companheirismo, pelas infinitas risadas, pela dedicação com que todos zelam pela nossa linguagem de programação predileta.

Blabos de Blebe

Obrigado pelo olho clínico, revisando este artigo.

Autor

Alexei "Russo" Znamensky < russoz no cpan org >

* Twitter: russoz
* Blog: http://russoz.wordpress.com/
* LinkedIn: http://www.linkedin.com/profile?viewProfile=&key=754668&trk=tab_pro

Fonte

Licença

Este texto está licenciado sob os termos da Creative Commons by-sa, http://creativecommons.org/licenses/by-sa/3.0/br/

Licença Creative Commons
This work is licensed under a Creative Commons Attribution-ShareAlike License.
blog comments powered by Disqus