^.vn$ czyli gdy komend jest dużo

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

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
\project
\tags
\trunk
\branches

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 ""

Committed revision 1.
koziolek@koziolek-desktop:~/repo$ svn mkdir file:///usr/local/repo/example/trunk/ -m ""

Committed revision 2.
koziolek@koziolek-desktop:~/repo$ svn mkdir file:///usr/local/repo/example/branches/ -m ""

Committed revision 3.
koziolek@koziolek-desktop:~/repo$ svn mkdir file:///usr/local/repo/example/tags/ -m ""

Committed revision 4.

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

mkdir tmp
cd tmp

mkdir -p $1/trunk
mkdir -p $1/branches
mkdir -p $1/tags

svn import $1 $2/$1 -m "repository struct import"

cd ..
rm -r tmp

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
#....
svn import $1 $2/$1/trunk/ -m "initial project import"

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
#....
rm -r $1
svn checkout $2/$1/trunk/ $1

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

if [ $1="--help" ]
then
echo "skrypt mvn2svn służy do tworzenia projektu w maven2 oraz łączenia go z repozytorium svn."
echo "Użycie"
echo "mvn2svn NAME REPO"
echo "NAME - nazwa projektu"
echo "REPO - lokalizacja repozytorium svn"
exit
fi


mvn archetype:create -DartifactId=$1 -DgroupId=eu.runelord.$1 -Dversion=0.1

mkdir tmp

cd tmp

mkdir -p $1/trunk
mkdir -p $1/branches
mkdir -p $1/tags

svn import $1 $2/$1 -m "repository struct import"

cd ..
rm -r tmp

svn import $1 $2/$1/trunk/ -m "initial project import"

rm -r $1

svn checkout $2/$1/trunk/ $1

cd $1
mvn clean compile
svn propset svn:ignore target .
svn status --no-ignore

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
if "%1"=="--help" goto help
if "%3"=="maven" goto maven
if "%3"=="svn_c" goto svn_c
if "%3"=="svn_i" goto svn_i
if "%3"=="svn_cp" goto svn_cp
if "%3"=="svn_ign" goto svn_ign

:start
start /w mvn2svn.bat %1 %2 maven
start /w mvn2svn.bat %1 %2 svn_c
start /w mvn2svn.bat %1 %2 svn_i
start /w mvn2svn.bat %1 %2 svn_cp
start /w mvn2svn.bat %1 %2 svn_ign
goto :end

:maven
mvn archetype:create -DartifactId=%1 -DgroupId=test.%1
cd %1
exit

:svn_c
mkdir tmp/%1/trunk
mkdir tmp/%1/branches
mkdir tmp/%1/tags
cd tmp
svn import %1 %2/%1 -m "repository struct import"
cd ..
rd /q/s tmp
exit

:svn_i
svn import %1 %2/%1/trunk -m "initial project import"
exit

:svn_cp
rd /q/s %1
svn checkout %2/%1/trunk %1
exit

:svn_ign
cd %1
svn propset svn:ignore target .
cd ..
exit

:help
echo "skrypt mvn2svn służy do tworzenia projektu w maven2 oraz łączenia go z repozytorium svn."
echo "Użycie"
echo "mvn2svn NAME REPO"
echo "NAME - nazwa projektu"
echo "REPO - lokalizacja repozytorium svn"
goto end

:end

Napisz odpowiedź

Twój adres email nie zostanie opublikowany. Pola, których wypełnienie jest wymagane, są oznaczone symbolem *

To create code blocks or other preformatted text, indent by four spaces:

    This will be displayed in a monospaced font. The first four 
    spaces will be stripped off, but all other whitespace
    will be preserved.
    
    Markdown is turned off in code blocks:
     [This is not a link](http://example.com)

To create not a block, but an inline code span, use backticks:

Here is some inline `code`.

For more help see http://daringfireball.net/projects/markdown/syntax