Maven2, release plugin a přístup do CVS přes SSH s privátním klíčem

Před tím, než jsem mohl ozkoušet maven-release-plugin, na který jsem si stěžoval v článku Co bych rád slyšel v září na CZJUG, musel jsem rozchodit přístup do našeho CVS skrze SSH s přihlašováním pomocí privátního klíče. Po zkušenostech můžu říct, že to byla práce nelehká a musím potvrdit negativní ohlasy ostatních, že v některých případech dokumentace k Mavenu (respektive k jeho konkrétním pluginům) je opravdu nedostatečná. Oříšek jsem nakonec rozlousknul díky zdrojákům a oddebugování.

Krom tohoto problému se v článku dotknu ještě site:deploy opět na server přes SSH s použitím privátního klíče. Jeden by si myslel, že zprovoznění prvního problému vyřeší všechny problémy tohoto charakteru, ale ouvej. Řada věcí je v maven pluginech řešena znovu a jinak. A toto je přesně ten případ. Řešení tu najdete také.

Především je třeba mít správně nakonfigurované připojení na SCM. Základem je tedy následující definice v pom.xml:


<scm>
	<connection>scm:cvs:${protocol}:${username}@${servername}:${CVSROOT}:${MODULE}</connection>
	<developerConnection>scm:cvs:${protocol}:${username}@${servername}:${CVSROOT}:${MODULE}</developerConnection>
	<url>http://${servername}</url>
</scm>

Kde:

  • protocol specifikuje typ připojení k CVS serveru - ext v takovém případě se plugin bude snažit použít externí SSH aplikaci pro připojení na CVS server, ssh interní implementace SSH klienta, a další, které se mne netýkaly
  • servername je název serveru, kde se nachází CVS repository (např. apache.org)
  • username v našem případě se jedná o uživatele, který bude použit pro SSH autentikaci
  • CVSROOT cesta k CVS rootu na uvedeném serveru
  • MODULE název modulu v CVS - po dalších peripetiích jsem přišel na to, že maven-release-plugin vám nebude správně fungovat, pokud přímo v rootu vycheckoutovaného modulu nebude pom.xml, pokud máte strukturu v CVS košatější (jako my) a nemůžete mít pom.xml v rootu modulu, můžete normálně za název modulu přidat za lomítkem cestu k podadresáři, kde se nachází váš projektový pom.xml (tohle mi trvalo taky nějakou hodinku :-) )

Nuže nejdříve jsem zkoušel jít cestou konfigurace maven-scm-pluginu (interní implementací SSH), který je maven-release-pluginem používán a vyzkoušet funkčnost jednoduchým zadáním "mvn scm:status". Otázka zněla, jak nakonfigurovat cestu k privátnímu klíči a passphrase?! V dokumentaci najdete leccos, ale tohle ne.

Šel jsem tedy do zdrojových kódů a zjistil jsem, že uvedené informace se zadávají přes property. Co mě ovšem překvapilo, že v celém pluginu se k propertám přistupuje přes System.getProperty("myProperty"). Samozřejmě, že jsem chtěl tyto property mít spíš někde v settings.xml a nechtěl jsem je předávat jako argument na příkazové řádce. Než jsem zjistil, že v konfiguraci pluginů je možné uvést i následující deklaraci, která nastaví i "systémové" property mi zase chvíli dalo (kupodivu o tomto se v dokumentaci moc nepíše).


<systemProperties>
    <myproperty>myvalue</myproperty>
</systemProperties>

Pozn.: možná je moje neznalost způsobená i tím, že jsem pro Maven zatím nenapsal ještě ani jeden plugin, s odstupem je mi možná jasné, proč se používá System.getProperty, jelikož takto se plugin dostane jak k argumentům z příkazové řádky, tak i k parametrům z pom.xml - jen nechápu, proč tvůrci Mavenu vše nesloučili pod jedněmi PROPERTY a proč tedy se tedy "uživatel" musí zajímat o to co musí konfigurovat přes systemProperties a co přes plugin configuration

Takže vybaven touto znalostí jsem již mohl konfiguraci závislou na lokálním prostředí externalizovat. Stačilo v deklaraci systemProperties uvést následující property:

  • maven.scm.cvs.java.cvs_rsh (cesta k externímu SSH programu - na Windowsech třebas Plink z rodiny Putty)
  • maven.scm.cvs.java.ssh.privateKey (cesta k privátnímu klíči - není nutné specifikovat, pokud se klíč nachází ve výchozím umístění jako ${user.home}/.ssh/id_dsa nebo ${user.home}/.ssh/id_rsa podle typu použité šifry)
  • maven.scm.cvs.java.ssh.passphrase (heslo k rozkódování privátního klíče)

Upozornění: id_rsa klíč musí být privátní klíč ve formátu OpenSSH, do tohoto formátu se dá klíč uložit v programu PuttyGen.exe (menu -> Conversion -> OpenSSH)

Nicméně pod daným nastavením fungoval pouze maven-scm-plugin, maven-release-plugin nechodil (zdálo se mi, že se někde po cestě ztratila informace o passphrase k privátnímu klíči).

Funkční řešení pro maven-release-plugin

Jediný způsob, který se mi podařilo úspěšně rozchodit připojení k CVS pod oběma pluginy bylo použití protokolu ext v definici scm.developerConnection a nadefinování proměnné prostředí s názvem CVS_RSH, která obsahovala cestu k externí SSH aplikaci (Plink). Před vlastním spuštěním maven goalů spouštím aplikaci PageAnt, která je dodávaná společně s aplikací Putty (Plink) a umožňuje nahrát sadu privátních klíčů do paměti s tím, že se heslo k nim zadává pouze jednou - při každém dalším požadavku na klíč PageAnt již poskytne rozšifrovanou podobu klíče. Plink a PageAnt jsou ze stejné rodiny a proto bezchybně spolupracují.

Upozornění: Plink.exe musí být uložen na cestě bez mezer, nebo musí v CVS_RSH cesta v uvozovkách.

Při tomto nastavení se spolupráce s CVS již rozběhla. Škoda, že jsem o tomhle zcela prostém řešení nevěděl už na začátku.

Problémy pokračují - jsme u site:deploy

Site deploy se provádí v rámci release procesu a typicky uploaduje vytvořenou dokumentaci na jiný server. Opět k tomu používáme SSH a kupodivu opět i privátní klíč. Jenže ouha, původní způsob, který funguje s maven-scm a maven-release pluginy zde nefunguje a celé se to konfiguruje JINAK!!! (toto je naštěstí už docela dobře zdokumentované)

Cesta k cílovému umístění v pom.xml vypadá takto:


<distributionManagement>
	<site>
		<id>website</id>
		<url>scp://${servername}/${path}</url>
	</site>
</distributionManagement>

Abych se dokázal výše uvedeným způsobem připojit na server, musím mít v setting.xml nadefinováno toto:


<servers>
    <server>
      <id>website</id>
      <username>${username}</username>
<passphrase>${passphrase}</passphrase>
    </server>
  </servers>

Než jsem prošel zmíněným postupem trvalo mi to asi dva týdny (samozřejmě, že jsem se tomu nemohl plně věnovat, takže jsem to řešil jen po chvilkách). Touto dobou jsem už byl mírně nazlobený.

Ne příliš efektivní proces releasu

Release plugin funguje tak, že v rámci svého běhu vytváří instance shellu a v nich na příkazové řádce spouští mvn s dalšími parametry. Tím pádem běží vlastně maven v mavenu, ovšem jedná se o dvě relativně nezávislé instance. Což například znamená, že při spuštění příkazu:


mvn release:prepare release:perform -Dmaven.test.skip=true

se vám použije argument pro ignorování testů jen v té "hlavní" instanci mavenu, ale do vnitřních instancích se tato property už nezpropaguje. Tzn. testy se vám stejně spustí a během celého procesu dokonce několikrát (pokud např. nemáte reporty v externím profilu, který se při releasu vynechává a ty reporty vyžadují spuštění testů).

Mě osobně se osvědčilo v pom.xml nadefinovat property u maven-surefire-pluginu, která vynutí vypnutí testů. Property na tomto místě se už použije kompletně v celém release procesu (což bych jako laik čekal už v prvním případě, nu ale což). Nastavení pro inspiraci uvedu (proměnné mám samozřejmě uvedeny v settings.xml):


<plugin>
	<groupId>org.apache.maven.plugins</groupId>
	<artifactId>maven-surefire-plugin</artifactId>
	<configuration>
		<jvm>${JAVA_1_4_HOME}${JAVA_EXECUTABLE}</jvm>
		<skip>${SKIP_TESTS}</skip>
		<excludes>
			<exclude>**/Abstract*Test.java</exclude>
			<exclude>**/Abstract*TestCase.java</exclude>
			<exclude>**/Test*.java</exclude>
		</excludes>
	</configuration>
</plugin>

Můžete namítnout, že není rozumné při releasu vynechat testy. Jenže testy mě běží na integračním serveru - tudíž mám jistotu, že release bude ok. Release ovšem obvykle dělám ručně na lokále a tam je spuštění testů s kompletním vygenerováním dokumentace prostě overkill.

Závěrem

Po výše popsaném martyriu jsem již schopen releasovat jednoduché moduly. U složených projektu s parent pomem a podmoduly se mi ještě vyskytuje další chyba, kdy při release:perform maven skončí s chybou, že nemůže resolvovat dependency na právě vytvářené moduly v remote repository (zcela logicky tam nejsou, protože ještě neproběhl deploy). Na tento problém mám zatím workaround, že ve vycheckoutované složce spustím ručně "mvn deploy", pak již perform fáze projde v pořádku.

Práce s Mavenem není vždy úplně snadná, přesto si myslím, že stojí za to jej používat. Naše firma se po půlročním zkušebním provozu rozhodla opustit Ant a nadále buildovat projekty jen Mavenem. Pomohlo nám to standardizovat hodně věcí a řekl bych, že přínosy převažují negativa. Ale někdy je to teda boj.

Dodatek k poslednímu problému:

S posledním uvedeným problémem jsem na tom nebyl sám. Po aplikování rad z diskusí:

se problém vyřešil. Škoda jen, že maven-release-plugin nefunguje správně bez dodatečné konfigurace.