A Custom SCM for Maven artifacts
A Capistrano SCM plugin is supposed to be a client of a proper Source
Control system, right? Well, what if you’re working in a compiled
language and your source has already been versioned and a compiled
artifact made from it? That’s where this plugin comes in. It complies
with the interface of an SCM plugin (pretty well, I think) but the
“repo” is a tarball that contains your application.
I chose Maven for the name because it is the artifact repository
standard that Maven created (and Artifactory and Nexus both
implement). I doesn’t refer to building Java source with the Maven’s
mvn tool. Thanks to jmpage’s Capistrano SCM Nexus
plugin for ideas and
reference, which in some cases resulted in outright theivery.
My team is made up of Java developers and Ruby developers. Looking
over the fence from the Java side of things, I was jealous of the Ruby
team’s ability to use Capistrano - a great automated deployment
tool. Some of our Java projects use Python’s Fabric which is very nice
but it’s the only python tool we use. So, in the interest of
standardizing on one tool, especially with an eye for automating
deploys in a similar way in a Gitlab pipeline, I decided to see if
Capistrano would work for Java deployments.
(differences from other SCM plugins)
Because we’re deploying a binary tarball, the directory structure
within the artifact is important. In short, it needs to match the
final expanded directory structure to work. I call this structure
the maven_artifact_style
and default it to ‘cap’. For Java projects,
this means that the executable jar within the tarball should not have
the version on it so scripts and/or systemd can find it
consistently. In fact nothing in the tarball should be versioned. This
doesn’t cause version confusion because the tarball is versioned and
the extracted version of the executable jar will already be contained
within a specific release in the normal Capistrano releases
directory. I discuss below how to use Maven’s Assembly plugin to
create a tarball artifact compliant with this gem.
When I refer to the term repo in this documentation, I mean the
artifact repository. But in the plugin framework code, repo means the
artifact contents.
Install the gem manually:
gem install capistrano-scm-maven
or add it to your Gemfile
when using Bundler:
gem 'capistrano-scm-maven'
Require the gem in your Capfile
:
require 'capistrano_scm_maven'
Configure the gem’s variables
set :maven_endpoint, 'http://agoodno.com:8081/artifactory'
set :maven_repository, 'libs-snapshot'
set :maven_group_id, 'com.agoodno'
set :maven_artifact_version, '0.0.1-SNAPSHOT'
set :maven_artifact_name, 'sample'
set :maven_artifact_style, 'cap'
set :maven_artifact_ext, 'tar.gz'
The configuration above would result in an attempt to retrieve the
artfact at:
`http://agoodno.com:8081/artifactory/libs-snapshot/com/agoodno/sample/0.0.1-SNAPSHOT/sample-0.0.1-SNAPSHOT-cap.tar.gz`
Add Maven’s Assembly
Plugin to
your Maven POM file.
pom.xml
...
<build>
<plugins>
...
<plugin>
<artifactId>maven-assembly-plugin</artifactId>
<version>3.1.0</version>
<configuration>
<descriptors>
<descriptor>src/assembly/cap.xml</descriptor>
</descriptors>
</configuration>
<executions>
<execution>
<id>make-assembly</id>
<phase>package</phase>
<goals>
<goal>single</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
...
The assembly file referenced in the POM above describes how to build
the tarball with your application’s final directory structure. SettingincludeBaseDirectory
to false
below removes the versioned
container directory that is created by default. Also, destName
is
set to remove the version from the aplication jar. I’ve only added a
README.md in addition to the application jar. See the Assembly
plugin’s documentation to build out your application’s whole
structure.
src/assembly/cap.xml
<?xml version="1.0" encoding="UTF-8"?>
<assembly xmlns="http://maven.apache.org/plugins/maven-assembly-plugin/assembly/1.1.2"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/plugins/maven-assembly-plugin/assembly/1.1.2 http://maven.apache.org/xsd/assembly-1.1.2.xsd">
<id>cap</id>
<formats>
<format>tar.gz</format>
</formats>
<includeBaseDirectory>false</includeBaseDirectory>
<files>
<file>
<source>target/${artifactId}-${version}.${packaging}</source>
<destName>${artifactId}.${packaging}</destName>
</file>
<file>
<source>README.md</source>
</file>
</files>
</assembly>
You can verify that the gem is installed by listing the tasks
available to capistrano with:
cap -T
You should see 3 maven namespaced commands in the list.
Because this gem takes part in the normal capistrano deploy lifecycle,
to run it just run the deploy task as you normally would:
cap staging deploy