Ostatni trochę mavenowałem projekty i kilka z nich musiałem ponownie powrzucać do svna. Natrafiłem na kilka ciekawych problemów związanych z svnem i mavenem. Czas zatem opisać je i podać rozwiązanie 🙂

Uwaga! Zakładam, że masz już zainstalowanego mavena i przynajmniej lokalnego svna

Krok 1. stwórz projekt w mavenie

Pierwszym dość prozaicznym problemem było zmuszenie się do nie mylenia nazw. Różnica jest w pierwszej literze co może prowadzić do błędów „z rozpędu”. Tyle tytulem wstępu, a teraz zaczynamy zabawę 🙂

Na początek stwórzmy projekt mavenowy:

Listing 1. Dobrze znany wszystkim widok

koziolek@koziolek-desktop:~/repo$ mvn archetype:create -DartifactId=example -DgroupId=eu.runelord.example -Dversion=0.1<br></br>[INFO] Scanning for projects...<br></br>[INFO] Searching repository for plugin with prefix: 'archetype'.<br></br>[INFO] ------------------------------------------------------------------------<br></br>[INFO] Building Maven Default Project<br></br>[INFO]    task-segment: [archetype:create] (aggregator-style)<br></br>[INFO] ------------------------------------------------------------------------<br></br>[INFO] Setting property: classpath.resource.loader.class => 'org.codehaus.plexus.velocity.ContextClassLoaderResourceLoader'.<br></br>[INFO] Setting property: velocimacro.messages.on => 'false'.<br></br>[INFO] Setting property: resource.loader => 'classpath'.<br></br>[INFO] Setting property: resource.manager.logwhenfound => 'false'.<br></br>[INFO] ************************************************************** <br></br>[INFO] Starting Jakarta Velocity v1.4<br></br>[INFO] RuntimeInstance initializing.<br></br>[INFO] Default Properties File: org/apache/velocity/runtime/defaults/velocity.properties<br></br>[INFO] Default ResourceManager initializing. (class org.apache.velocity.runtime.resource.ResourceManagerImpl)<br></br>[INFO] Resource Loader Instantiated: org.codehaus.plexus.velocity.ContextClassLoaderResourceLoader<br></br>[INFO] ClasspathResourceLoader : initialization starting.<br></br>[INFO] ClasspathResourceLoader : initialization complete.<br></br>[INFO] ResourceCache : initialized. (class org.apache.velocity.runtime.resource.ResourceCacheImpl)<br></br>[INFO] Default ResourceManager initialization complete.<br></br>[INFO] Loaded System Directive: org.apache.velocity.runtime.directive.Literal<br></br>[INFO] Loaded System Directive: org.apache.velocity.runtime.directive.Macro<br></br>[INFO] Loaded System Directive: org.apache.velocity.runtime.directive.Parse<br></br>[INFO] Loaded System Directive: org.apache.velocity.runtime.directive.Include<br></br>[INFO] Loaded System Directive: org.apache.velocity.runtime.directive.Foreach<br></br>[INFO] Created: 20 parsers.<br></br>[INFO] Velocimacro : initialization starting.<br></br>[INFO] Velocimacro : adding VMs from VM library template : VM_global_library.vm<br></br>[ERROR] ResourceManager : unable to find resource 'VM_global_library.vm' in any resource loader.<br></br>[INFO] Velocimacro : error using  VM library template VM_global_library.vm : org.apache.velocity.exception.ResourceNotFoundException: Unable to find resource 'VM_global_library.vm'<br></br>[INFO] Velocimacro :  VM library template macro registration complete.<br></br>[INFO] Velocimacro : allowInline = true : VMs can be defined inline in templates<br></br>[INFO] Velocimacro : allowInlineToOverride = false : VMs defined inline may NOT replace previous VM definitions<br></br>[INFO] Velocimacro : allowInlineLocal = false : VMs defined inline will be  global in scope if allowed.<br></br>[INFO] Velocimacro : initialization complete.<br></br>[INFO] Velocity successfully started.<br></br>[INFO] [archetype:create]<br></br>[INFO] Defaulting package to group ID: eu.runelord.example<br></br>[INFO] artifact org.apache.maven.archetypes:maven-archetype-quickstart: checking for updates from central<br></br>[INFO] ----------------------------------------------------------------------------<br></br>[INFO] Using following parameters for creating Archetype: maven-archetype-quickstart:RELEASE<br></br>[INFO] ----------------------------------------------------------------------------<br></br>[INFO] Parameter: groupId, Value: eu.runelord.example<br></br>[INFO] Parameter: packageName, Value: eu.runelord.example<br></br>[INFO] Parameter: package, Value: eu.runelord.example<br></br>[INFO] Parameter: artifactId, Value: example<br></br>[INFO] Parameter: basedir, Value: /home/koziolek/repo<br></br>[INFO] Parameter: version, Value: 0.1<br></br>[INFO] ********************* End of debug info from resources from generated POM ***********************<br></br>[INFO] Archetype created in dir: /home/koziolek/repo/example<br></br>[INFO] ------------------------------------------------------------------------<br></br>[INFO] BUILD SUCCESSFUL<br></br>[INFO] ------------------------------------------------------------------------<br></br>[INFO] Total time: 2 seconds<br></br>[INFO] Finished at: Thu Jan 31 08:56:29 GMT 2008<br></br>[INFO] Final Memory: 5M/9M<br></br>[INFO] ------------------------------------------------------------------------<br></br>

Mam nadzieję że wszyscy sobie poradzili 🙂 Skoro mamy już projekt to czas na przygotowanie repozytorium svn.

Z założenia powinno ono mieć szablonową strukturę dla wszytkich naszych projektów. Pozwoli to na zautomatyzowanie pracy w przyszłości. Zalecaną przez twórców jest taka oto struktura:

Listing 2. Zalecana struktura repozytorium svn

\repo<br></br>   \project<br></br>      \tags<br></br>      \trunk<br></br>      \branches<br></br>

Jak widać trochę tego jest 🙂 Czas na…

Krok 2. Konfiguracja repozytorium svn

Na początek należy stworzyć odpowiednią strukturę w repozytorium svn. Można do tego problemu podejść na dwa sposoby. Pierwszy, zły i niepolecany przeze mnie, to ręczne tworzenie wszystkich katalogów jeden za drugim. Przykładowy kod:

Listing 3. Ręczne tworzenie katalogów w svnie to ZŁO

koziolek@koziolek-desktop:~/repo$ svn mkdir file:///usr/local/repo/example/ -m ""<br></br><br></br>Committed revision 1.<br></br>koziolek@koziolek-desktop:~/repo$ svn mkdir file:///usr/local/repo/example/trunk/ -m ""<br></br><br></br>Committed revision 2.<br></br>koziolek@koziolek-desktop:~/repo$ svn mkdir file:///usr/local/repo/example/branches/ -m ""<br></br><br></br>Committed revision 3.<br></br>koziolek@koziolek-desktop:~/repo$ svn mkdir file:///usr/local/repo/example/tags/ -m ""<br></br><br></br>Committed revision 4.<br></br>

Widać dlaczego? Otóż dodanie czterech katalogów to cztery wersje (rewizje), a tym samym niepotrzebne nikomu do niczego nadmiarowe dane. Podejście drugie, i jedyne słuszne, to napisanie odpowiedniego skryptu, który wykona tylko jeden commit. Stwórzmy zatem skrypt mvn2svn, którego zadaniem będzie stworzenie odpowiednich struktur w svnie:

Listing 4. mvn2svn, czyli słuszna idea

#!/bin/bash<br></br><br></br>mkdir tmp<br></br>cd tmp<br></br><br></br>mkdir -p $1/trunk<br></br>mkdir -p $1/branches<br></br>mkdir -p $1/tags<br></br><br></br>svn import $1 $2/$1 -m "repository struct import"<br></br><br></br>cd ..<br></br>rm -r tmp<br></br>

Na tym można by było zakończyć, ale jak narazie nie wykonaliśmy jeszcze najważniejszej czynności.

Krok 3. Połączenie projektu z repozytorium

Nie ukrywam, że nie jest to skomplikowana czynności, ale wymaga trochę wiedzy i wprawy. Na początek należy wykonać import projektu do repozytorium:

Listing 5. Polecenie import wysyła projekt do svna.

#!/bin/bash<br></br>#....<br></br>svn import $1 $2/$1/trunk/ -m "initial project import"<br></br>

Nazwa operacji jest mało intuicyjna, ale ujdzie. I teraz pierwsza pułapka. Jeżeli wylistujemy katalog z projektem (nazwa ukryta pod zmienną $1) to nie zobaczymy żadnego katalogu .svn. Operacja wysłania projektu do svna nie wiąże go automatycznie z repozytorium. Należy wykonać jeszcze checkout projektu. Tu są dwa kroki. Pierwszy to usunięcie lokalnego katalogu z projektem, a drugi to pobranie projektu. Pierwszy krok jest konieczny ponieważ jeżeli będzie istniał katalog o takiej samej nazwie jak to co chcemy pobrać to svn powie nam że jest nie dobrze. Kolejna część skryptu:

Listing 6. Usuwamy projekt by go pobrać…

#!/bin/bash<br></br>#....<br></br>rm -r $1<br></br>svn checkout $2/$1/trunk/ $1<br></br>

I na tym można zakończyć część właściwą. Pozostają nam jeszcze sprawy kosmetyczne. Pierwsza z nich to dodanie katalogu target do listy ignorowanych, a druga to złożenie razem skryptu tworzącego projekt ze skryptem do konfiguracji svna. Przydał by się też jakiś krótki manual. Kompletny skrypt:

Listing 7. gotowy skrypt w pełnej krasie

#!/bin/bash<br></br><br></br>if [ $1="--help"  ]<br></br>then<br></br>   echo "skrypt mvn2svn służy do tworzenia projektu w maven2 oraz łączenia go z repozytorium svn."<br></br>   echo "Użycie"<br></br>   echo "mvn2svn NAME REPO"<br></br>   echo "NAME - nazwa projektu"<br></br>   echo "REPO - lokalizacja repozytorium svn"<br></br>   exit <br></br>fi<br></br><br></br><br></br>mvn archetype:create -DartifactId=$1 -DgroupId=eu.runelord.$1 -Dversion=0.1<br></br><br></br>mkdir tmp<br></br><br></br>cd tmp<br></br><br></br>mkdir -p $1/trunk<br></br>mkdir -p $1/branches<br></br>mkdir -p $1/tags<br></br><br></br>svn import $1 $2/$1 -m "repository struct import"<br></br><br></br>cd ..<br></br>rm -r tmp<br></br><br></br>svn import $1 $2/$1/trunk/ -m "initial project import"<br></br><br></br>rm -r $1<br></br><br></br>svn checkout $2/$1/trunk/ $1<br></br><br></br>cd $1<br></br>mvn clean compile<br></br>svn propset svn:ignore target .<br></br>svn status --no-ignore<br></br>

koniec…

ps. wieczorkiem postaram się dodać wersję dla winzgrozy

Update 1:

Niestety winda okazała się dość odporna na moje pomysły, ale nadal walczę 🙂

Update 2:

Udało się niestety po stworzeniu projektu przez mavena trzeba napisać w konsoli exit. Pełen kod:

Listing 8. Skrypt wersja na windę

echo off<br></br>if "%1"=="--help" goto help<br></br>if "%3"=="maven" goto maven<br></br>if "%3"=="svn_c" goto svn_c<br></br>if "%3"=="svn_i" goto svn_i<br></br>if "%3"=="svn_cp" goto svn_cp<br></br>if "%3"=="svn_ign" goto svn_ign<br></br><br></br>:start<br></br>start /w mvn2svn.bat %1 %2 maven<br></br>start /w mvn2svn.bat %1 %2 svn_c<br></br>start /w mvn2svn.bat %1 %2 svn_i<br></br>start /w mvn2svn.bat %1 %2 svn_cp<br></br>start /w mvn2svn.bat %1 %2 svn_ign<br></br>goto :end<br></br><br></br>:maven<br></br>mvn archetype:create -DartifactId=%1 -DgroupId=test.%1<br></br>cd %1<br></br>exit<br></br><br></br>:svn_c<br></br>mkdir tmp/%1/trunk<br></br>mkdir tmp/%1/branches<br></br>mkdir tmp/%1/tags<br></br>cd tmp<br></br>svn import %1 %2/%1 -m "repository struct import"<br></br>cd ..<br></br>rd /q/s tmp<br></br>exit<br></br><br></br>:svn_i<br></br>svn import %1 %2/%1/trunk -m "initial project import"<br></br>exit<br></br><br></br>:svn_cp<br></br>rd /q/s %1<br></br>svn checkout %2/%1/trunk %1<br></br>exit<br></br><br></br>:svn_ign<br></br>cd %1<br></br>svn propset svn:ignore target .<br></br>cd ..<br></br>exit<br></br><br></br>:help<br></br>echo "skrypt mvn2svn służy do tworzenia projektu w maven2 oraz łączenia go z repozytorium svn."<br></br>echo "Użycie"<br></br>echo "mvn2svn NAME REPO"<br></br>echo "NAME - nazwa projektu"<br></br>echo "REPO - lokalizacja repozytorium svn"<br></br>goto end<br></br><br></br>:end