<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>j2ee.pl - związani z javą &#187; ci</title>
	<atom:link href="http://j2ee.pl/tag/ci/feed/" rel="self" type="application/rss+xml" />
	<link>http://j2ee.pl</link>
	<description>związani z Javą</description>
	<lastBuildDate>Mon, 24 Aug 2009 11:45:27 +0000</lastBuildDate>
	<generator>http://wordpress.org/?v=2.8</generator>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
			<item>
		<title>Hudson jako przykład implementacji koncepcji Automated Continuous Integration</title>
		<link>http://j2ee.pl/2007/12/01/hudson-jako-przyklad-implementacji-koncepcji-automated-continuous-integration/</link>
		<comments>http://j2ee.pl/2007/12/01/hudson-jako-przyklad-implementacji-koncepcji-automated-continuous-integration/#comments</comments>
		<pubDate>Sat, 01 Dec 2007 04:33:53 +0000</pubDate>
		<dc:creator>Michał Mally</dc:creator>
				<category><![CDATA[Inne]]></category>
		<category><![CDATA[Jakość]]></category>
		<category><![CDATA[ci]]></category>
		<category><![CDATA[continuous integration]]></category>
		<category><![CDATA[emma]]></category>
		<category><![CDATA[hudson]]></category>

		<guid isPermaLink="false">http://j2ee.pl/2007/12/01/hudson-jako-przyklad-implementacji-koncepcji-automated-continuous-integration/</guid>
		<description><![CDATA[
Szczypta teorii
W żadnej poważnej publikacji nie może oczywiście zabraknąć części czysto teoretycznej! Pomimo faktu, że ta publikacja do tak zaszczytnego grona nie pretenduje, to z przykrością stwierdzam, że i tutaj teoretyczny wstęp znaleźć się niestety musi. Osoby niezainteresowane proszone są o przesunięcie paska przewijania odrobinę niżej.
Z roku na rok wdrażane systemy informatyczne stają się funkcjonalnie [...]]]></description>
			<content:encoded><![CDATA[<p><a href="http://j2ee.pl/wp-content/uploads/2007/12/hudson.png" title="Hudson"><img src="http://j2ee.pl/wp-content/uploads/2007/12/hudson.thumbnail.png" alt="Hudson" /></a></p>
<h3>Szczypta teorii</h3>
<p>W żadnej poważnej publikacji nie może oczywiście zabraknąć części czysto teoretycznej! Pomimo faktu, że ta publikacja do tak zaszczytnego grona nie pretenduje, to z przykrością stwierdzam, że i tutaj teoretyczny wstęp znaleźć się niestety musi. Osoby niezainteresowane proszone są o przesunięcie paska przewijania odrobinę niżej.</p>
<p>Z roku na rok wdrażane systemy informatyczne stają się funkcjonalnie bardziej złożone, terminy ich oddania krótsze, a stawiane im wymagania jakościowe surowsze. Sprostanie ciągle rosnącym oczekiwaniom klientów biznesowych wymaga od firm rynku IT stosowania coraz większej ilości narzędzi formalizujących i automatyzujących proces wytwarzania aplikacji.</p>
<p><span id="more-102"></span></p>
<p>Już w tym momencie do grona narzędzi uważanych za niezbędne w cyklu wytwarzania oprogramowania zalicza się, i to  z coraz większą stanowczością, aplikacje wspomagające proces <em>ciągłej integracji</em> (ang. <strong>Continuous Integration</strong>).</p>
<p>Uważam, że koncepcja <strong>Continuous Integration</strong> (<em>dalej</em>: <strong>CI</strong>) jest szczególnym przypadkiem wzorca projektowego i jako taka obecna była w informatyce w niesformalizowanej formie już od dawna. Jednak jej znacząca popularyzacja miała miejsce dopiero w okresie upowszechnienia się metodyki <strong>Extreme Programming</strong>. Z pewnością ogromny wpływ na taki stan rzeczy miały publikacje <strong>Martina Fowler</strong>&#8216;a oraz <strong>Kenta Beck</strong>&#8216;a, które nadały <strong>CI</strong> realne kształty i obudziły dyskusję na ten temat w światku informatycznym. Przedstawiona idea odbiła się dużym echem i w wielu projektach zaczęto wdrażać przedstawioną koncepcję.</p>
<p>Główna myśl stojącą za <strong>CI</strong> to wymuszenie zbudowania całego projektu w wyniku każdej najmniejszej nawet zmiany. Proces budowania powinien obejmować między innymi kompilację kodu źródłowego, sprawdzenie jego jakości przy wykorzystaniu różnych kryteriów (np. <a href="http://checkstyle.sourceforge.net/">checkstyle</a>&#8216;a),  wykonanie testów jednostkowych (np. <a href="http://www.junit.org/">JUnit</a>) oraz weryfikacja stopnia w jaki pokrywają one kod źródłowy (np. <a href="http://emma.sourceforge.net/">Emma</a>), wytworzenie artefaktów (w tym np. pliki <em>war</em>, dokumentacja). Wszystkie te działania koordynowane mogą być oczywiście przy pomocy <a href="http://ant.apache.org/">Apache Ant</a>, <a href="http://maven.apache.org/">Apache Maven</a>, czy też innego narzędzia przeznaczonego do budowania, chociaż dwa wymienione jako pierwsze wydają się być w tym momencie bezkonkurencyjne.</p>
<p>Widać więc zatem, że <strong>CI</strong> nie stara się wymyślać koła na nowo, a jedynie stanowi kolejną warstwę abstrakcji ponad  szeroko już wykorzystywanymi narzędziami.</p>
<h4>Zalety i wady</h4>
<p>Podstawowe zalety <strong>CI</strong> wymieniane na większości stron internetowych związanych z tematyką, to:</p>
<ul>
<li>możliwość dokładnego określenia która z wprowadzonych zmian spowodowała dezintegrację rozwijanego systemu poprzez określenie w którym momencie któryś z testów jednostkowych przestał poprawnie przechodzić</li>
<li>problemy związane z integracją są wykrywane i naprawiane na bieżąco</li>
<li>natychmiastowa informacja na temat <em>popsutego</em> kodu</li>
<li>natychmiastowe wykonywanie testów jednostkowych</li>
<li>dostępność najnowszej wersji zbudowanej aplikacji bez konieczności jej ręcznego budowania</li>
<li>możliwość śledzenia w czasie wartości różnych wskaźników opisujących różne parametry wytwarzanej aplikacji (np. stopień pokrycia kodu testami jednostkowymi)</li>
<li>programiści zmobilizowani są do tworzenia lepszych rozwiązań; są bardziej świadomi, że każda wprowadzana przez nich zmiana może zostać poddana obiektywnej ocenie</li>
</ul>
<p>Obok w/w zalet istnieją zapewne inne pomniejsze, które każdy z nas na swój sposób może w <strong>CI</strong> odnaleźć. Jakie są zatem minusy wdrożenia <em>procesu ciągłej integracji</em>? O wadach <strong>CI</strong> Internet dosyć <em>głośno milczy</em> i mnie również trudno jest znaleźć przynajmniej kilka wad, które mogłyby mieć generalny i warty wspominania charakter. Nie oznacza to jednak, że <strong>CI</strong> nie może nam przysporzyć dodatkowych problemów. Analizę potencjalnych minusów należy jednak przeprowadzić indywidualnie przy okazji wyboru konkretnego modelu realizacji <strong>CI</strong>. W większości jednak projektów używanie <strong>CI</strong> będzie sprawiać mniejsze problemy, niż te, które mogłyby wystąpić w wyniku jego nieużywania.</p>
<h4>Manual vs. Automated</h4>
<h5>Manual Continuous Integration</h5>
<p>Najprostszym możliwym sposobem wdrożenia <strong>CI</strong> w projekcie jest przerzucenie odpowiedzialności za jego stosowanie na programistów. Najprostszym i zarazem chyba najgorszym z możliwych. Mogłoby to polegać na zobowiązaniu osób związanych z procesem wytwarzania aplikacji do każdorazowego wykonywania serii żmudnych i mechanicznych czynności po każdej pojedynczej zmianie wprowadzonej do repozytorium. Niestety takie podejście ma bardzo wiele wad i wydaje się, że może się jedynie sprawdzać w bardzo małych projektach.</p>
<p>Podstawowe problemy związane z <em>manualnym</em> stosowaniem <strong>CI</strong>, które jestem w stanie w tym momencie wymienić, to:</p>
<ul>
<li>developerzy muszą poświęcić dodatkowy czas na procedury związane z <strong>CI</strong></li>
<li>występuje możliwość niedbałego podejścia developerów do stosowania <strong>CI</strong></li>
<li>obniżenie kreatywności developerów poprzez wykonywanie żmudnych i powtarzalnych procedur</li>
<li>niedeterministyczne opóźnienia w publikacji artefaktów związane z koniecznością manualnego wykonania wszystkich procedur</li>
</ul>
<p>Wymienione powyżej problemy, to jedynie niektóre z którymi możemy mieć do czynienia przy <em>ręcznym</em> stosowaniu koncepcji <strong>CI</strong>. Przedstawiona lista stanowi jednak dla mnie wystarczający powód, by <strong>CI</strong> w miarę możliwości starać się stosować jedynie w zautomatyzowanej formie. Tym bardziej, że proces przeprowadzania <em>ciągłej integracji</em> wytwarzanego projektu można bez większych przeszkód opisać w prostej sformalizowanej postaci. Umożliwi to jego automatyczne wykonywanie w wyniku konkretnego zdarzenia zachodzącego w naszym systemie (w klasycznej i najczęściej stosowanej postaci będzie to wprowadzenie przez developera zmian do kodu źródłowego projektu).</p>
<h5>Automated Continuous Integration</h5>
<p>Na szczęście, by wyrwać nas z marazmu związanego z <em>ręcznym</em> stosowaniem <strong>CI</strong>, przychodzą nam wyspecjalizowane narzędzia automatyzujące ten proces. Jeżeli nasze zakłopotanie nie mija, to tylko dlatego, że wybór odpowiedniego narzędzia, ze względu na ich mnogość, nie jest wcale prosty. Dostępnych jest przynajmniej kilkadziesiąt produktów tego rodzaju. Wybieranie aplikacji spełniającej nasze oczekiwania (w sposób możliwie najbardziej kompletny) powinno obejmować rozważenie licencji produktu, zastanowienie się nad czynnościami, które będziemy chcieli włączyć do procesu <strong>CI</strong> oraz z jakimi narzędziami chcielibyśmy, aby wybrana implementacja <em>ACI</em> współpracowała. Poza tym, co zostało wymienione,  powinniśmy kierować się jak zawsze zdroworozsądkowymi kryteriami &#8211; podobnie jak przy doborze innych narzędzi.</p>
<p>W miarę kompletną listę narzędzi do automatyzacji procesu <em>ciągłej integracji</em> można znaleźć <a href="http://en.wikipedia.org/wiki/Continuous_Integration">tutaj</a>.</p>
<h3>Gram praktyki</h3>
<p>Kiedy już poznaliśmy teoretyczne podstawy <strong>CI</strong> najwyższy czas, aby przejść do jego praktycznego wykorzystania. Zaczniemy od określenia szczegółowych wymagań dotyczących modelu procesu <em>ciągłej integracji</em>, który chcielibyśmy zaimplementować w naszym przykładowym projekcie. By tego jednak dokonać wcześniej postaram się opisać w kilku słowach środowisko wytwórcze powiązane z naszym przykładowym projektem nazwanym przeze mnie <em>Validator</em>.</p>
<p>Elementy środowiska wytwórczego dla projektu <em>Validator</em>:</p>
<ul>
<li>Wiodący język programowania: <strong>Java</strong></li>
<li>Repozytorium: <strong>Subversion</strong></li>
<li>Narzędzie automatyzujące proces budowania: <strong>Apache Ant</strong></li>
<li>Silnik testów jednostkowych: <strong>JUnit</strong></li>
<li>Narzędzie do weryfikacji pokrycia kodu: <strong>Emma</strong></li>
</ul>
<p>A teraz cele, które chcielibyśmy osiągnąć dzięki zastosowaniu <strong>Automated Continuous Integration</strong>. Każde wprowadzenie zmiany do repozytorium powinno skutkować:</p>
<ul>
<li>kompilacją kodu źródłowego</li>
<li>wykonaniem wszystkich testów jednostkowych (wraz z utrwaleniem rezultatów)</li>
<li>obliczeniem stopnia pokrycia kodu testami na poziomie klas, metod oraz linii (wraz z utrwaleniem rezultatów)</li>
<li>sprawdzeniem jakości kodu przy użyciu <em>checkstyle</em> (wraz z utrwaleniem rezultatów)</li>
<li>zbudowaniem biblioteki w postaci pliku <em>jar</em> oraz udostępnieniu go do pobrania</li>
<li>wysłaniem pod wskazany adres informacji w postaci maila o niepowodzeniu na jakimkolwiek etapie procesu</li>
</ul>
<p>Jak łatwo można zauważyć jest to bardzo prosty przykład procesu <em>ciągłej integracji</em> &#8211; można wręcz rzec, że <em>szkoleniowy</em>. W przypadku prawdziwych projektów prawdopodobnie będziemy mieli do czynienia z wielokrotnie bardziej złożonymi mechanizmami. Nie należy się jednak tym przerażać, gdyż w większości sytuacji dodatkowa praca, jaką należy wykonać jest związana, nie z samym narzędziem <strong>ACI</strong>, ale z narzędziem służącym do budowy aplikacji (np. <strong>Apache Ant</strong>).</p>
<h4>Hudson</h4>
<p>Wpisując w <strong>Google</strong> <em>hudson</em> można by pomyśleć, że <strong>Hudson</strong>, to rzeka. Nic bardziej mylnego ;) <strong>Hudson</strong> nie jest może najbardziej popularnym narzędziem <strong>ACI</strong>, ale z pewnością jest projektem, który bardzo dynamicznie się w ostatnim czasie rozwija i posiada podatną na rozszerzalność architekturę. Istnieje wiele <em>wtyczek</em> rozszerzających funkcjonalność <strong>Hudson</strong>&#8216;a o dodatkowe możliwości, a i tak ciągle pojawiają się nowe, a już istniejące są rozwijane. Widać, że projekt <em>żyje</em> &#8211; wróży mu to dobrą przyszłość. <strong>Hudson</strong> ma także jeszcze jedną niezaprzeczalną zaletę – osobiście bardzo lubię go używać i nie ukrywam, że mam nadzieję, że ten artykuł w jakiś, chociaż nieznaczny,  sposób wpłynie na jego szybszą popularyzację.</p>
<h5>Instalacja</h5>
<p>Instalacja <strong>Hudson</strong>&#8216;a jest wyjątkowo prosta. Wystarczy posiadać kontener <em>servlet</em>&#8216;ów – może to być na przykład <strong>Apache Tomcat</strong>. W przypadku <strong>Tomcat</strong>&#8216;a wystarczy jedynie przekopiować ściągnięty ze strony producenta plik <em>hudson.war</em> do katalogu <em>webapps</em>. Następnie uruchamiamy <strong>Tomcat</strong>&#8216;a i to już wszystko! Przy standardowej konfiguracji <strong>Hudson</strong> powinien być dostępny pod adresem <a href="http://localhost:8080/hudson">http://localhost:8080/hudson</a>.</p>
<p>Konfiguracja <strong>Hudson</strong>&#8216;a odbywa się poprzez <em>Manage Hudson/System Configuration</em> dostępne z głównej stronie. Minimalne ustawienia, jakie należy wprowadzić, to między innymi:</p>
<ul>
<li>lokalizacja instalacji <strong>JDK</strong></li>
<li>lokalizacja instalacji <strong>Ant</strong>/<strong>Maven</strong></li>
<li>konfiguracja serwera <em>SMTP</em> z którego będzie korzystał <strong>Hudson</strong></li>
</ul>
<p>Dodatkowo, aby umożliwić spełnienie wymagań, które sobie wcześniej postawiliśmy konieczne jest jeszcze ściągnięcie ze <a href="http://hudson.gotdns.com/wiki/display/HUDSON/Plugins">strony</a> odpowiednich wtyczek i ich zainstalowanie:</p>
<ul>
<li><strong>Emma</strong> &#8211; posłuży nam do śledzenia postępu pokrycia kodu testami (pomiędzy kolejnymi <em>build</em>&#8216;ami)</li>
<li><strong>Violations</strong> &#8211; posłuży nam do obrazowania zastrzeżeń, jakie <em>checkstyle</em> ma w stosunku do naszego kodu</li>
</ul>
<p>Instalacji <em>plugin</em>&#8216;ów należy dokonać poprzez opcję <em>Manage Hudson/Manage Plugins</em>. Po zainstalowaniu <em>wtyczek</em> należy zrestartować serwer aplikacji. W tym momencie powinniśmy posiadać już <strong>Hudson</strong>&#8216;a w postaci pozwalającej nam na osiągnięcie wyżej wskazanych celów. Teraz czeka nas jeszcze trochę pracy poza <strong>ACI</strong> związanej z czynnościami, które należy wykonać nawet, jeżeliby projekt nie korzystał z dobrodziejstw <strong>CI</strong>.</p>
<h4>Skrypt budujący</h4>
<p>Poniżej pozwoliłem sobie zamieścić używany w projekcie skrypt budujący <strong>Apache Ant</strong>. Jak łatwo można zauważyć jest to zwykły skrypt budujący i nic nie wskazuje na to, że był on przygotowany w celu późniejszej współpracy z <strong>Hudson</strong>&#8216;em. Nie posiada on także żadnych <em>magicznych</em> powiązań z systemem <strong>ACI</strong>. Nic zatem nie stoi na przeszkodzie by wywołać go bezpośrednio za pomocą <strong>Ant</strong>&#8216;a. Kompletne źródła projektu <em>Validator</em> można ściągnąć <a href="http://j2ee.pl/wp-content/uploads/2007/12/validator.zip">stąd</a>.</p>
<pre class="prettyprint">&lt;?xml version="1.0"?&gt;
&lt;project name="Validator" default="build_jar"&gt;

  &lt;property name="main.source.dir" location="src" /&gt;
  &lt;property name="test.source.dir" location="test" /&gt;
  &lt;property name="lib.dir" location="lib" /&gt;
  &lt;property name="build.dir" location="build" /&gt;
  &lt;property name="dist.dir" location="${build.dir}/dist" /&gt;
  &lt;property name="main.source.build.dir" location="${build.dir}/classes/source" /&gt;
  &lt;property name="main.source.debug.dir" location="${build.dir}/classes/source_debug" /&gt;
  &lt;property name="main.source.instrumented.dir" location="${build.dir}/classes/source_instrumented" /&gt;
  &lt;property name="test.source.build.dir" location="${build.dir}/classes/test" /&gt;
  &lt;property name="test.result.dir" location="${build.dir}/test" /&gt;
  &lt;property name="violations.result.dir" location="${build.dir}/violations" /&gt;
  &lt;property name="coverage.result.dir" location="${build.dir}/coverage" /&gt;
  &lt;property name="code.coverage.metadata.file" location="${coverage.result.dir}/coverage.em" /&gt;
  &lt;property name="code.coverage.xml.file" location="${coverage.result.dir}/coverage.xml" /&gt;
  &lt;property name="code.coverage.html.file" location="${coverage.result.dir}/coverage.html" /&gt;
  &lt;property name="checkstyle.result.file" location="${violations.result.dir}/checkstyle.xml" /&gt;
  &lt;property name="main.jar.file" value="validator.jar" /&gt;

  &lt;taskdef name="emma" classname="com.vladium.emma.emmaTask"&gt;
    &lt;classpath&gt;
      &lt;fileset dir="${lib.dir}"&gt;
        &lt;include name="emma_ant.jar" /&gt;
        &lt;include name="emma.jar" /&gt;
      &lt;/fileset&gt;
    &lt;/classpath&gt;
  &lt;/taskdef&gt;

  &lt;taskdef resource="checkstyletask.properties" classpath="${lib.dir}/checkstyle-all-4.3.jar" /&gt;

  &lt;path id="test.build.classpath"&gt;
    &lt;pathelement location="${main.source.build.dir}" /&gt;
    &lt;fileset dir="${lib.dir}"&gt;
      &lt;include name="junit.jar" /&gt;
    &lt;/fileset&gt;
  &lt;/path&gt;

  &lt;path id="test.runtime.classpath"&gt;
    &lt;pathelement location="${main.source.instrumented.dir}/classes" /&gt;
    &lt;pathelement location="${test.source.build.dir}" /&gt;
    &lt;fileset dir="${lib.dir}"&gt;
      &lt;include name="junit.jar" /&gt;
      &lt;include name="emma.jar" /&gt;
    &lt;/fileset&gt;
  &lt;/path&gt;

  &lt;target name="clean"&gt;
    &lt;delete dir="${build.dir}" /&gt;
  &lt;/target&gt;

  &lt;target name="compile_main"&gt;
    &lt;mkdir dir="${main.source.build.dir}" /&gt;
    &lt;javac srcdir="${main.source.dir}" destdir="${main.source.build.dir}" /&gt;
  &lt;/target&gt;

  &lt;target name="compile_main_debug"&gt;
    &lt;mkdir dir="${main.source.debug.dir}" /&gt;
    &lt;javac srcdir="${main.source.dir}" destdir="${main.source.debug.dir}" debug="true" /&gt;
  &lt;/target&gt;

  &lt;target name="compile_test"&gt;
    &lt;mkdir dir="${test.source.build.dir}" /&gt;
    &lt;javac srcdir="${test.source.dir}" destdir="${test.source.build.dir}"&gt;
      &lt;classpath refid="test.build.classpath" /&gt;
    &lt;/javac&gt;
  &lt;/target&gt;

  &lt;target name="run_test" depends="compile_main,compile_main_debug,compile_test"&gt;
    &lt;mkdir dir="${test.result.dir}" /&gt;
    &lt;delete dir="${main.source.instrumented.dir}" /&gt;
    &lt;mkdir dir="${main.source.instrumented.dir}" /&gt;
    &lt;emma&gt;
      &lt;instr outdir="${main.source.instrumented.dir}" mode="fullcopy" merge="false" metadatafile="${code.coverage.metadata.file}"&gt;
        &lt;instrpath&gt;
          &lt;pathelement location="${main.source.debug.dir}" /&gt;
        &lt;/instrpath&gt;
      &lt;/instr&gt;
    &lt;/emma&gt;
    &lt;junit fork="yes"&gt;
      &lt;formatter type="xml" /&gt;
      &lt;classpath refid="test.runtime.classpath" /&gt;
      &lt;sysproperty key="emma.coverage.out.file" value="${code.coverage.metadata.file}" /&gt;
      &lt;sysproperty key="emma.coverage.out.merge" value="true" /&gt;
      &lt;batchtest todir="${test.result.dir}"&gt;
        &lt;fileset dir="${test.source.dir}" /&gt;
      &lt;/batchtest&gt;
    &lt;/junit&gt;
    &lt;emma&gt;
      &lt;report sourcepath="${main.source.dir}"&gt;
        &lt;infileset file="${code.coverage.metadata.file}" /&gt;
        &lt;xml outfile="${code.coverage.xml.file}" /&gt;
        &lt;html outfile="${code.coverage.html.file}" /&gt;
      &lt;/report&gt;
    &lt;/emma&gt;
  &lt;/target&gt;

  &lt;target name="violations"&gt;
    &lt;mkdir dir="${violations.result.dir}" /&gt;
    &lt;checkstyle config="${lib.dir}/sun_checks_eclipse.xml"&gt;
      &lt;fileset dir="${main.source.dir}" includes="**/*.java" /&gt;
      &lt;formatter type="xml" toFile="${checkstyle.result.file}" /&gt;
    &lt;/checkstyle&gt;
  &lt;/target&gt;

  &lt;target name="build_jar" depends="run_test,violations"&gt;
    &lt;mkdir dir="${dist.dir}" /&gt;
    &lt;jar basedir="${main.source.build.dir}" destfile="${dist.dir}/${main.jar.file}" /&gt;
  &lt;/target&gt;

&lt;/project&gt;</pre>
<p>Z punktu widzenia <strong>Hudson</strong>&#8216;a i konfiguracji, której musimy dokonać istotne będą jedynie wymienione poniżej elementy:</p>
<ul>
<li>położenie pliku <em>validator.jar</em> będącego efektem końcowym procesu budowania, który chcemy udostępnić jako artefakt</li>
<li>położenie pliku XML zawierającego wyniki wykonania testów jednostkowych</li>
<li>położenie pliku XML zawierającego informacje na temat pokrycia kodu testami jednostkowymi</li>
</ul>
<h4>Tworzenie nowego zadania <strong>Hudson</strong>&#8216;a</h4>
<p>Powoli zbliżamy się do meritum. Mając już gotowy projekt wraz z działającym skryptem budującym nie pozostało nam już wiele do zrobienia, by objąć go procesem <em>ciągłej integracji</em>. Właściwie dzieli nas od finału już tylko jeden krok, którym będzie utworzenie nowego zadania w <strong>Hudson</strong>&#8216;ie.</p>
<p>Dokonanie tego nie będzie na szczęście niczym skomplikowanym:</p>
<ol>
<li>z głównego panelu sterowania <strong>Hudson</strong> wybieramy opcję <em>New Job</em></li>
<li>określamy nazwę dla tworzonego zadania</li>
<li>wybieramy <em>free-style</em> jako rodzaj projektu i przechodzimy do następnego ekranu, gdzie wpiszemy pozostałe szczegóły</li>
<li>wybieramy system kontroli wersji z którego będzie korzystał <strong>Hudson</strong> (w naszym przypadku będzie to <strong>Subversion</strong>)</li>
<li>jako warunek wywołania procesu integracji (<em>Build Triggers</em>) wybieramy cykliczne odpytywanie repozytorium (<em>Poll SCM</em>)</li>
<li>w harmonogramie wpisujemy łańcuch znaków zgodny z formatem <strong>crond</strong> (np. <em>*/2 * * * *</em> &#8211; odpytywanie co 2 minuty)</li>
<li>następnie definiujemy, co właściwie będzie oznaczać dla <strong>Hudson</strong>&#8216;a zbudowanie aplikacji – w naszym przypadku chcemy, aby było to wywołanie przy pomocy <strong>Ant</strong>&#8216;a domyślnego celu zdefiniowanego w skrypcie budowania (zaznaczamy <em>Invoke Ant</em>)</li>
<li>przyszedł czas na określenie czynności, które zostaną wykonane po zakończeniu budowania aplikacji &#8211; zgodnie z wcześniejszymi ustaleniami będzie interesowało nas:
<ol>
<li>zachowanie artefaktów (<em>validator.jar</em>) z procesu budowania (<em>Archive the artifacts</em>)</li>
<li>opublikowanie wyników testów jednostkowych (<em>Publish JUnit test result report</em>)</li>
<li>opublikowanie wyników pokrycia kodu testami (<em>Record Emma coverage report</em>)</li>
<li>opublikowanie wyników sprawdzenia jakości kodu przy użyciu <em>checkstyle</em>&#8216;a (<em>Report violations</em>)</li>
<li>wysłanie informacji o integracji, która się nie powiodła (<em>E-mail Notification</em>)</li>
</ol>
</li>
</ol>
<p>Tym razem to już naprawdę wszystko! Po zatwierdzeniu konfiguracji (jeżeli wszystko zrobiłeś dobrze ;)), to możesz się już cieszyć działającym procesem <em>ciągłej integracji</em>.</p>
<p>Od tej pory możesz zacząć regularną pracę zapominając zupełnie o istnieniu <strong>Hudson</strong>&#8216;a i sięgać do niego jedynie wtedy kiedy uznasz to za przydatne. A sytuacji takich może być co najmniej kilka:</p>
<ul>
<li>chęć uzyskania zbudowanej wersji aplikacji z konkretnego <em>build</em>&#8216;a</li>
<li>analiza zmienności w czasie różnych parametrów opisujących aplikację w celu określenia trendów (np. procentowe pokrycie testami kodu źródłowego)</li>
<li>zatwierdzenie zmian w repozytorium, które spowodują niebudowanie się aplikacji spowoduje wysłanie maila na wskazany adres</li>
</ul>
<h3>Podsumowanie</h3>
<p>Rozważany przez nas przykład był raczej przykładem trywialnym. Z tego też powodu może się wydawać, że <strong>Hudson</strong> nie pełni w nim znaczącej roli. Muszę się zgodzić z tą opinią – czym jednak system staje się większy i bardziej skomplikowany, tym <strong>ACI</strong> staje się bardziej niezbędny.</p>
<p>Miałem przyjemność pracować nad ogromnym systemem z wieloma podprojektami (repozytorium wielkości około 3GB), gdzie <strong>Hudson</strong> stanowił bardzo ważne ogniwo w procesie wytwarzania. Wydaje się więc, że jeżeli był w stanie poradzić sobie w takich warunkach, to dla większości standardowych zastosowań powinien być odpowiedni. Osobiście używam <strong>Hudson</strong>&#8216;a, gdyż pozostając bardzo intuicyjny oraz łatwy w konfiguracji dostarcza możliwości na zupełnie wystarczającym dla mnie poziomie zaawansowania.</p>
<p>Mam nadzieję, że mimo ograniczonych środków przekazu udało mi się przekonać Cię do wypróbowania <strong>Hudson</strong>&#8216;a na własnej <em>skórze</em>. Świadom jestem niestety, że artykuł ten to zaledwie czubek góry lodowej zarówno jeżeli chodzi o <strong>CI</strong>, jak i o samego<strong>Hudson</strong>&#8216;a.</p>
<h3>Bibliografia</h3>
<ol>
<li><a href="http://en.wikipedia.org/wiki/Continuous_Integration">http://en.wikipedia.org/wiki/Continuous_Integration</a></li>
<li><a href="http://www.extremeprogramming.org/">http://www.extremeprogramming.org/</a></li>
<li><a href="http://hudson.gotdns.com/wiki/display/HUDSON/Home">http://hudson.gotdns.com/wiki/display/HUDSON/Home</a></li>
<li><a href="http://martinfowler.com/">http://martinfowler.com/</a></li>
</ol>
]]></content:encoded>
			<wfw:commentRss>http://j2ee.pl/2007/12/01/hudson-jako-przyklad-implementacji-koncepcji-automated-continuous-integration/feed/</wfw:commentRss>
		<slash:comments>3</slash:comments>
		</item>
	</channel>
</rss>
