Using a local Maven repository other than
~/.m2/repository
You don't have to use the default repository location. It's possible to define your own in the user's
settings.xml
or even in the global settings. But I guess most people just use the default. On the other hand in an environment like DEV@cloud, all the different builds from different users must be separated. So CloudBees decided that each Jenkins job has its own Maven repository inside the job's workspace. That is good because the repository is deleted together with the project.Problem
The Testing Harness embeds Maven, i.e. forks a new Maven instance. It fails to relay the modified settings to this new process. During the execution of the integration test a new local repository is created and the original local one is used as a remote one (called "local-as-remote"). But without any hints, Maven uses
~/.m2/repository
. So the true local repository is not found and all needed artefacts are downloaded again. This takes a lot of time (and wastes bandwidth). Dependencies that exist only in the local repository, e.g. snapshots of dependent projects, are not found and the integration test fails.Solution
RepositoryTool.findLocalRepositoryDirectoy()
uses an instance of MavenSettingsBuilder
to get the settings. Its only implementing class is DefaultMavenSettingsBuilder
and it tries to determine the repository location from the value of the system property maven.repo.local
. Then it reads the user settings and in the end it uses ~/.m2/repository
. The solution is to set the maven.repo.local
system property whenever the local repository is not under ~/.m2/repository
. Add -Dmaven.repo.local=.repository
into the field for Goals and Options of the Jenkins job configuration.Using additional dependencies while building integration test projects
After the plugin under test is built and installed into the new local repository the Maven Plugin Harness runs Maven against the integration test projects inside the
src/test/resources/it
folder. The approach which I described last year forks a Maven with pom
, properties
and goals
defined by the JUnit test method.Problem
The integration tests know the location of the new local repository (because it is set explicitly) and are able to access the plugin under test. But they know nothing about the local-as-remote repository. They can only access all artefacts which have been "downloaded" from the local-as-remote repository during the build of the plugin under test. So the problem is similar to the previous problem but occurs only when an integration test project needs additional artefacts. For example a global ruleset Maven module might consists of XML ruleset configuration files. The test module depends on the Checkstyle plugin and executes it using the newly build rulesets. So the object under test (the rules XML) is tested indirectly through the invocation of Checkstyle but the ruleset module itself does not depend on Checkstyle.
Solution
All POMs used during integration test have to be "mangled", not just the POM of the plugin under test. The method
manglePomForTestModule(pom)
is defined in the ProjectTool
but it's protected
and not accessible. So I copied it to AbstractPluginITCase
and applied it to the integration test POMs.Using settings other than
~/.m2/repository/settings.xml
If you need artefacts from repositories other than Maven Central you usually add them to your
settings.xml
. Then you refer to them in the Jenkins job configuration. Behind the scenes Jenkins calls Maven with the parameter -s custom_settings.xml
.Problem
Similar to the repository location, the custom settings' path is not propagated to the embedded Maven and it uses the default settings. This causes no problems if all needed artefacts are either in the local-as-remote repository or can be downloaded from Maven Central. For example a Global Ruleset might contain some Macker Architecture Rules. The snapshot of the Macker Maven Plugin is deployed by another build job into the CloudBees snapshot repository. The test module depends on this Macker plugin and runs it using the newly built rulesets.
Solution
AbstractPluginITCase
calls BuildTool
's createBasicInvocationRequest()
to get an InvocationRequest
and subsequently executes this request. Using any system property the InvocationRequest
can be customised:if (System.getProperty(ALT_USER_SETTINGS_XML_LOCATION) != null) { File settings = new File(System.getProperty(ALT_USER_SETTINGS_XML_LOCATION)); if (settings.exists()) { request.setUserSettingsFile(settings); } }Then the value of the used system property is added into the field for Jenkins' Goals and Options:
-s custom_settings.xml -Dorg.apache.maven.user-settings=custom_settings.xml
.Alas I'm not a Maven expert and it took me quite some time to solve these problems. They are not specific to CloudBees but they result from using non default settings. Other plugins that fork Maven have similar problems.
No comments:
Post a Comment