Как подружить Ivy и Jenkins

Урок посвящён тем, кто не хочет переводить свой проект c ant на maven и при этом не хранить сторонние библиотеки в репозитории.

  1. Знакомимся с Ivy.

Ivy - это менеджер сторонних библиотек, от которых зависит ваш проект. Заходим на сайт продукта и скачиваем необходимые бинарники. Далее копируем ivy-хххх-yy.jar из бандла в ваш $ANT_HOME/lib. Всё, вы готовы к автоматической закачке библиотек.

Открываем build.xml вашего проекта и добавляем следующую строчку в тег project:

xmlns:ivy="antlib:org.apache.ivy.ant"

а также создаём новый таргет:

<!-- Resolving Ivy Task -->
<target name="resolve">
	<ivy:retrieve>
</ivy:retrieve></target>

В каталоге проекта вам понадобится файл описывающий зависимости, очень напоминает формат maven. Итак - ivy.xml, предположим, что вы используете log4j и testng, тогда конфигурационный файл будет выглядеть примерно следующим образом:

<ivy-module version="2.0">
	<info module="testing" organisation="s--h">
	<dependencies>
		<dependency name="log4j" org="log4j" rev="1.2.17">
		<dependency name="testng" org="org.testng" rev="6.7">
	</dependency></dependency></dependencies>
</info></ivy-module>

Детали конфигурации вы можете прочесть на сайте (так, например, можно указать закачивать только библиотеки - без sources/javadoc jar файлов).

OK. Теперь проверяем нашу работу - набираем запускаем ant с параметром resolve и смотрим в консоль, получим примерно следующее (вы должны быть подключены к интернету):

resolve:
[ivy:retrieve] :: Apache Ivy 2.3.0-rc1 - 20120416000235 :: http://ant.apache.org/ivy/ ::
[ivy:retrieve] :: loading settings :: url = jar:file:/usr/share/ant/lib/ivy-2.3.0-rc1.jar!/org/apache/ivy/core/settings/ivysettings.xml
[ivy:retrieve] :: resolving dependencies :: s--h#testing;working@a
[ivy:retrieve] 	confs: [default]
[ivy:retrieve] 	found log4j#log4j;1.2.17 in public
[ivy:retrieve] 	found org.apache.openejb#javaee-api;5.0-2 in public
[ivy:retrieve] 	found javax.mail#mail;1.4.3 in public
[ivy:retrieve] 	found javax.activation#activation;1.1 in public
[ivy:retrieve] 	found org.apache.geronimo.specs#geronimo-jms_1.1_spec;1.0 in public
[ivy:retrieve] 	found org.testng#testng;6.7 in public
[ivy:retrieve] 	found junit#junit;4.10 in public
[ivy:retrieve] 	found org.hamcrest#hamcrest-core;1.1 in public
[ivy:retrieve] 	found org.beanshell#bsh;2.0b4 in public
[ivy:retrieve] 	found com.beust#jcommander;1.12 in public
[ivy:retrieve] 	found org.yaml#snakeyaml;1.6 in public
[ivy:retrieve] 	found com.google.inject#guice;2.0 in public
[ivy:retrieve] 	found aopalliance#aopalliance;1.0 in public
[ivy:retrieve] 	found org.apache.ant#ant;1.7.0 in public
[ivy:retrieve] 	found org.apache.ant#ant-launcher;1.7.0 in public
[ivy:retrieve] downloading http://repo1.maven.org/maven2/log4j/log4j/1.2.17/log4j-1.2.17-javadoc.jar ...
--- погрызли бобры ---
[ivy:retrieve] 	[SUCCESSFUL ] org.apache.ant#ant;1.7.0!ant.jar (2613ms)
[ivy:retrieve] downloading http://repo1.maven.org/maven2/org/apache/ant/ant-launcher/1.7.0/ant-launcher-1.7.0.jar ...
[ivy:retrieve] ......... (11kB)
[ivy:retrieve] .. (0kB)
[ivy:retrieve] 	[SUCCESSFUL ] org.apache.ant#ant-launcher;1.7.0!ant-launcher.jar (192ms)
[ivy:retrieve] :: resolution report :: resolve 11765ms :: artifacts dl 18944ms
	---------------------------------------------------------------------
	|                  |            modules            ||   artifacts   |
	|       conf       | number| search|dwnlded|evicted|| number|dwnlded|
	---------------------------------------------------------------------
	|      default     |   15  |   15  |   15  |   0   ||   19  |   19  |
	---------------------------------------------------------------------
[ivy:retrieve] :: retrieving :: s--h#testing
[ivy:retrieve] 	confs: [default]
[ivy:retrieve] 	0 artifacts copied, 19 already retrieved (0kB/13ms)

Как бонус мы получаем все необходимые библиотеки в каталоге lib нашего проекта. Кстати, если вы удалите этот каталог и запустите ant resolve ещё раз, то библиотеки не будут качаться из интернета, а будут взяты из кэша.

  1. Подключаем Ivy в Jenkins.

Собственно повторяем шаг, описаный в пункте 1, но на сервере CI. Запускаем проект и наслаждаемся удачной сборкой. Всё хорошо. Проблемы могу возникнуть у вас в момент, когда несколько проектов с настройками по умолчанию, использующие ivy попытаются запустить свои сборки в одно и тоже время. В этом случае билды отметятся как Failed, а в логе вы получите примерно следующую ошибку:

[ivy:retrieve] :: resolution report :: resolve 2430ms :: artifacts dl 428ms
[ivy:retrieve] 	:: evicted modules:
[ivy:retrieve] 	com.beust#jcommander;1.2.7 by [com.beust#jcommander;1.12] in [default]
	---------------------------------------------------------------------
	|                  |            modules            ||   artifacts   |
	|       conf       | number| search|dwnlded|evicted|| number|dwnlded|
	---------------------------------------------------------------------
	|      default     |   37  |   0   |   0   |   1   ||   45  |   0   |
	---------------------------------------------------------------------
[ivy:retrieve] 
[ivy:retrieve] :: problems summary ::
[ivy:retrieve] :::: ERRORS
[ivy:retrieve] 	unknown resolver null
[ivy:retrieve] 	unknown resolver null
[ivy:retrieve] 
[ivy:retrieve] :: USE VERBOSE OR DEBUG MESSAGE LEVEL FOR MORE DETAILS
[ivy:retrieve] :: retrieving :: s--h#testing
[ivy:retrieve] 	confs: [default]

BUILD FAILED
/var/lib/jenkins/jobs/L-TS/workspace/build.xml:30: impossible to ivy retrieve: java.lang.RuntimeException: problem during retrieve of s--h#testing: java.text.ParseException: failed to parse report: /var/lib/jenkins/cache/qa/l-r/s-h-testing-default.xml: Premature end of file.
	at org.apache.ivy.core.retrieve.RetrieveEngine.retrieve(RetrieveEngine.java:215)
	at org.apache.ivy.Ivy.retrieve(Ivy.java:551)
	at org.apache.ivy.ant.IvyRetrieve.doExecute(IvyRetrieve.java:97)
	at org.apache.ivy.ant.IvyTask.execute(IvyTask.java:277)
...

Проблема связана с тем, что несколько проектов на Jenkins пытаются использовать один и тот же кэш Ivy. Побороть эту проблему просто - для каждого проекта указать свой кэш для Ivy. Открываем build.xml и добавляем следующую строчку:

<property name="ivy.cache.dir" value="/path/to/my/cache"></property>

Собственно всё. Мы получили возможность собирать проект с зависимостями без перехода на maven. Удачных сборок!

Строчка потерялась :((

все подправил