terça-feira, 29 de outubro de 2013

Declarar "repositories" no seu pom faz um tigre chorar na China


Totalmente roubado de:
http://blog.sonatype.com/people/2009/02/why-putting-repositories-in-your-poms-is-a-bad-idea/#.Um5hYiS9CAI


Adicionar os repositórios no seu pom.xml ao invés do settings.xml pode ser catastrófico a longo prazo. Principalmente se você espera que pessoas utilizem esse artefato como uma dependência do projeto delas. Quando e se a URL sair do ar, as pessoas não vão mais conseguir utilizar as versões antigas da sua biblioteca.

Lembre-se, o pom, depois de deployado, é imutável  - a URL ficará gravada para sempre e sejá quem esteja tentando utilizar essa versão vai ter muita dor de cabeça.





Digamos que você tem um projeto que tem como dependência (direta ou transitiva, mesma coisa nesse caso) uma biblioteca do jersey, que possui este pom.xml. No fim do arquivo você pode encontrar:


<repository>
<id>glassfish-repository</id>
<name>Repository for Glassfish</name>
<url>
http://maven.glassfish.org/content/groups/glassfish
</url>
</repository>
</repositories> 

E de repente, não mais que de repente, o repositório sai do ar. Assim, foi desligado, mudou de endereço, qualquer coisa assim. Anos se passaram, né?



Você inocentemente pensa que só precisa definir no seu projeto outro "repository" apontando pro novo endereço, né? Ledo engano!

Maven requer que todos os repositórios de todos os pom.xml que ele encontrar sejam válidos. Mesmo que todos os artefatos necessários estivessem disponíveis em outros repositórios válidos você simplesmente não consegue mais utilizar aquela versão do artefato. O build não falha por falta de artefato, falha por que um dos repositórios está dando "404 - not found".


Pior: o maven procurará as dependências SNAPSHOTs/LATESTs/RELEASEs em cada um dos repositórios definidos no seu settings.xml, nos seus poms, e em qualquer um dos poms incluídos transitivamente.  


Então, se não for pra usar no pom, o que você deve fazer?
  • Defina no settings.xml 
  • Crie um repositório no Nexus que agregue todos os proxies que você necessite, e aponte o seu settings.xml apenas para esse repositório

Se por acaso um repositório morrer, você pode corrigir o seu proxy (ou corrigir o settings.xml), e todas as versões antigas da sua biblioteca serão (mais) facilmente utilizáveis.




Soluções nesse caso de sumiço de repositório de uma biblioteca que você utilize:
  • Faça upgrade pra uma versão que não tenha um repositório inválido. Simplesmente faça.
  • Caso isso não seja possível, a única gambiarra é definir um mirror no settings.xml de todo mundo, apontando pra onde achar as dependências. Nojento de feio.

Ciclo de vida do maven: ou porque fazer "mvn verify deploy" é estúpido

Minha definição de maven é:

Maven serve para gerenciar dependências e executar plugins do maven

Ok, eu sei que é meio recursivo, mas sinceramente? O maven faz ~apenas~ isso. "Apenas".

Gerenciar dependências vocês entendem, né? Fazer download disso, calcular a versão a baixar, etc, etc. Super complexo de implementar, mas simples de entender.


Entenda um plugin do maven como um executável qualquer, que tenha qualquer objetivo. Um plugin tem um "nome" e a lista de "goals", comandos.
Então, quando vossa senhoria executa mvn dependency:tree, é uma simples invocação do goal "tree" do plugin "dependency". Mole?

Acontece que o maven tem os seus ciclos de vida. São apenas três: clean, default, site. Vou colar aqui as fases do "default", que é o que usamos mais:

validate
initialize
generate-sources
process-sources
generate-resources
process-resources
compile
process-classes
generate-test-sources
process-test-sources
generate-test-resources
process-test-resources
test-compile
process-test-classes
test
prepare-package
package
pre-integration-test
integration-test
post-integration-test
verify
install
deploy

É uma sequência, isto é "mvn install" fará tudo o que "mvn verify" faz e mais alguma coisa. Assim como o "deploy" apenas ocorre após o "install"

 
Ciclo de vida é apenas uma maneira de simplificar e ordenar a chamada dos plugins.
Entenda que a cada fase, um ou mais plugins poderão ser chamados. Por padrão, o maven já faz a ligação de alguns plugins a determinadas fases (e vc não consegue desfazer essa ligação. Você pode adicionar plugins, nunca retirar). Dê uma olha em Lifecycle Bindings


Para adicionar plugins ao seu ciclo de vida não é difícil: existe uma seção chamada "plugins" dentro do seu pom.xml; na configuração do plugin, você pode dizer a que fase ele está ligado. Caso você não especifique, o plugin/goal já virá com uma fase padrão.


A saber: o ciclo de vida de "site" é utilizado pelo plugin de release.

domingo, 27 de outubro de 2013

Plugin de release: ruim com ele, pior sem ele

Das certezas da vida: você odiará o plugin de release. (Aqui para ler a documentação do bonito).

A teoria é linda e fofa. O goal release:prepare irá:
  1. Criar uma tag com o nome da versão
  2. Mudar todos os seus pom.xml pra versão que vc fará release
  3. Fazer um "mvn clean install" pra ver se os testes passam todos
  4. Incrementar o seus pom.xml pra nova versão SNAPSHOT
  5. Fazer commit&push dessas parada tudo 
Depois disso, vc pode executar release:perform:
  1. Fazer checkout daquela tag
  2. Fazer OUTRO "mvn clean install" (tipo por que não, né?)
  3. Fazer "deploy" dos seus binários todos (pro nexus ou qualquer lugar que vc configurou no seu "distributionManagement")
  4. Gerar javadoc, sources e fazer deploy também

Parece bem legal se você pensar que você terá a tag no seu SCM, e ela bate com o artefato que foi para seu maven remoto, etc. Uma coisa engenhosa e linda.


Para que funcione no seu projeto, a teoria diz que você apenas precisa definir adequadamente a tag "scm" no seu pom.xml.

Além disso, você obviamente precisa configurar para onde as coisas serão deployadas:


Simples, né? Vamos aos detalhes, porque sabemos que "o demônio mora nos detalhes".

  • Por padrão, o goal prepare irá pedir para que você confirme a versão nova, a versão a fazer release, o nome da tag. Invocar o maven em batch mode (-B) usará os valores default.
  • O release:perform irá criar uns arquivos temporários para que o release:perform seja executado. Isso é, você não pode fazer um prepare em uma máquina e o perform em outra. 
  • Rollback is a lie. Aceite. Se falhar por QUALQUER motivo, o release:rollback não vai te ajudar, vai te deixar em estágios intermediários que não dá nem pra corrigir e ir pra frente nem voltar pra trás. Váaaarias vezes vc tem que consertar tudo manualmente.
  • Você tem a opção de usar "https" ao invés de "ssh" no scm. Mas não faça. O plugin de release vai te pedir para colocar username/password a cada mínima mudança. Faça por ssh. 
  • Tenha certeza de já ter feito commit, push, pull de toda e qualquer alteração no seu repositório
  • Não podem ocorrer commits entre o começo do release e o fim. Isso, manda todo mundo parar de commitar. 
  • Os "mvn clean install" rodam em uma outra invocação do maven, que não recebe os mesmos argumentos que você passa na linha de comando. Isto é, aqueles "-Dalgumacoisa" que seu build precisa não serão recebidos. Como opção, passe-os dentro do "-Darguments" ou como variáveis de ambiente
  • Caso você deseje ardentemente pular todos os testes (por sua conta e risco), utilize "-Darguments='-DskipTests'", e nunca "-Darguments='-Dmaven.test.skip'". O último faria com que os jars de testes não fossem nem gerados, nem deployados.
  • Por padrão o perform irá fazer o deploy e deploy-site, além de habilitar o profile "release-profile". Isso é totalmente configurável no plugin, então sinta-se em casa.

Como a documentação é sua melhor amiga, vá lá e sinta-se à vontade. 
E claro, nada te impede de fazer release a partir do CI ;) Apenas garanta que o usuário tem permissão de escrita, que não é "shallow clone" (se for um checkout git), e pronto.