views:

1544

answers:

1

How can I commit a single artifact like a file or a directory to an arbitrary location in a svn repository in the deploy lifecycle phase? With repository I mean a plain Subversion repository here, not a Maven repository held in a Subversion repository!

I have already evaluated the wagon-svn but it seems it can just be used to commit a whole module build to a Maven repository held in a Subversion repository.

This is my use case: I generate a JAR-file including all dependencies during build. This is to be used in Python scripts, so outside the Maven world. And I want to provide the current release JAR always at the same location in the repository holding the Python framework.

+1  A: 

Before you do this I would carefully consider your reasons for doing so. Derived artifacts shouldn't be put into SCM as they can easily be rebuilt, instead you could consider attaching the artifact to your build so it is deployed alongside it.

This can be done with the build-helper-maven-plugin. The example config below will attach src/assembly/archive.xml as an additional artifact with the classifier "archive".

  <plugin>
    <inherited>true</inherited>
    <groupId>org.codehaus.mojo</groupId>
    <artifactId>build-helper-maven-plugin</artifactId>
    <executions>
      <execution>
        <id>attach-artifacts</id>
        <phase>deploy</phase>
        <goals>
          <goal>attach-artifact</goal>
        </goals>
        <configuration>
            <artifacts>
              <artifact>
                <file>src/assembly/archive.xml</file>
                <type>xml</type>
                <classifier>archive</classifier>
              </artifact>
            </artifacts>
        </configuration>
      </execution>
    </executions>
  </plugin>
</plugins>

This artifact can be referenced directly by specifying the classifier and type in your dependency declaration. For example:

<dependency>
  <groupId>my.group.id</groupId>
  <artifactId>artifact-id</artifactId>
  <version>1.0.0</version>
  <type>xml</type>
  <classifier>archive</classifier>
</dependency>


If you are set on doing it this way, the Maven SCM API provides an abstraction layer for common SCM providers and operations. The code below can be added to a mojo. It expects to be provided two parameters, the file to be committed to Subversion and the scm url. When bound to your project, it will commit the file to the Subversion repository.

package name.seller.rich;

import java.io.File;

import org.apache.maven.plugin.AbstractMojo;
import org.apache.maven.plugin.MojoExecutionException;
import org.apache.maven.plugin.MojoFailureException;
import org.apache.maven.scm.ScmException;
import org.apache.maven.scm.ScmFileSet;
import org.apache.maven.scm.command.add.AddScmResult;
import org.apache.maven.scm.manager.ScmManager;
import org.apache.maven.scm.provider.ScmProvider;
import org.apache.maven.scm.provider.svn.repository.SvnScmProviderRepository;
import org.apache.maven.scm.repository.ScmRepository;

/**
 * @goal checkin-file
 */
public class SVNCheckinMojo extends AbstractMojo {

    /**
     * @component
     * @required
     */
    private ScmManager manager;

    /**
     * @component
     * @required
     */
    private ScmProvider provider;

    /**
     * @parameter
     * @required
     */
    private String connectionUrl;

    /**
     * @parameter
     * @required
     */
    private File svnFile;

    /**
     * Obtain the SVN repository.
     */
    public ScmRepository getRepository() throws MojoExecutionException {
        try {
            ScmRepository repository = manager.makeScmRepository(connectionUrl);

            if (!(repository.getProviderRepository() instanceof SvnScmProviderRepository)) {
                throw new MojoExecutionException(
                        "the scm provider is not an SVN provider");
            }

            return repository;
        } catch (Exception e) {
            throw new MojoExecutionException(
                    "Unable to obtain SCM repositorys", e);
        }
    }

    public void execute() throws MojoExecutionException, MojoFailureException {
        ScmRepository repository = getRepository();

        File dir = svnFile.getParentFile();
        File file = new File(svnFile.getName());
        ScmFileSet fileSet = new ScmFileSet(dir, file);
        try {

            AddScmResult result = provider.add(repository, fileSet);

            if (!result.isSuccess()) {
                throw new MojoExecutionException("unable to add file \""
                        + svnFile + "\" to SCM URL \"" + connectionUrl + "\"");
            }
        } catch (ScmException e) {
            throw new MojoExecutionException(
                    "failed to commit file to repository", e);
        }
    }
}

Here's an example pom for the plugin, note the maven-plugin packaging:

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd"&gt;
  <modelVersion>4.0.0</modelVersion>
  <groupId>org.apache.maven.plugins</groupId>
  <artifactId>maven-svn-hack-plugin</artifactId>
  <packaging>maven-plugin</packaging>
  <version>0.0.1</version>
  <dependencies>
    <dependency>
      <groupId>org.apache.maven</groupId>
      <artifactId>maven-plugin-api</artifactId>
      <version>2.0</version>
    </dependency>
    <dependency>
      <groupId>org.apache.maven</groupId>
      <artifactId>maven-project</artifactId>
      <version>2.0</version>
    </dependency>
    <dependency>
      <groupId>org.apache.maven.scm</groupId>
      <artifactId>maven-scm-api</artifactId>
      <version>1.2</version>
    </dependency>
    <dependency>
      <groupId>org.apache.maven.scm</groupId>
      <artifactId>maven-scm-provider-svnexe</artifactId>
      <version>1.2</version>
    </dependency>
    <dependency>
      <groupId>org.apache.maven.scm</groupId>
      <artifactId>maven-scm-manager-plexus</artifactId>
      <version>1.2</version>
    </dependency>
    <dependency>
      <groupId>org.codehaus.plexus</groupId>
      <artifactId>plexus-utils</artifactId>
      <version>1.5.1</version>
    </dependency>
  </dependencies>
</project>

Here's an example configuration that will check the file in during the package phase.

<build>
  <plugins>
    <plugin>
  <groupId>org.apache.maven.plugins</groupId>
  <artifactId>maven-svn-hack-plugin</artifactId>
  <version>0.0.1</version>
  <configuration>
    <connectionUrl>scm:svn:http://svn.apache.org/svn/root/module&lt;/connectionUrl&gt;
    <svnFile>${project.build.directory}/${artifactId}-${version}-test.txt</svnFile>
  </configuration>
  <executions>
    <execution>
      <id>checkin-file</id>
      <phase>package</phase>
      <goals>
        <goal>checkin-file</goal>
      </goals>
    </execution>
  </executions>
    </plugin>
  </plugins>
</build>

Or at a pinch you could use the antrun plugin to invoke the ant subversion library

Rich Seller
This is not exactly what I want to do. Could you review my edited question?
desolat
The second part of my answer describes how to obtain the SVNScmProviderRepository. With that you can invoke arbitrary commands on Subversion. If that doesn't meet your needs I'm not clear what your edit means. Could you elaborate?
Rich Seller
@Rich: This was really helpful, but I could not get it running up to now. Here is the problem description:As the add() constantly fails, I added a provider.validateScmUrl() call, which also fails. Which of the following could be the reason for this?1. I have to use auth with the svn, so I added username:passwort@ before the hostname.2. I have to use https.So the URL is like scm:svn:https://username:[email protected]/path .Is that wrong? How to do auth then? Any links to documentation? The scm project one is quite sparse.
desolat
You're on the right lines. There's some patterns here: http://maven.apache.org/scm/subversion.htmlBased on what you're saying, you want something like scm:svn:svn+ssh://username:[email protected]/path
Rich Seller
@Rich: Got it working! My URL was correct, but svn.exe was not on the PATH. Thanks a lot for this Mojo introduction. Btw. validateScmUrl() still fails, but add and checkIn work. Now I just would like to get the username and the password from the settings.xml. Can you point me to the libs needed for this?
desolat
Glad to help, you can define the Subversion settings outside the POM, I'd say that's probably a separate question though.
Rich Seller