Versature has been building our applications using Java and deploying them on Google App Engine for some time. It has been a very effective solution for us.
Recently, I wanted to improve our release process by making it more formal and automated. After the usual battle with Maven, I managed to win.
Our current build automates the following:
- Remove the trailing
-SNAPSHOTfrom the version number in the POM - Tag the release in Subversion
- Filter the
appengine-web.xmlto contain the version number - Build and deploy to GAE
- Increment version number in the POM and add the trailing
-SNAPSHOT
All of the above is achieved with two Maven commands: release:prepare and release:perform.
Here is the POM with most of the irrelevant parts removed:
<?xml version="1.0" encoding="UTF-8"?> <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"> <modelVersion>4.0.0</modelVersion> <version>0-9-0-SNAPSHOT</version> <packaging>war</packaging> <name>Versature Dashboard</name> <scm> <connection>scm:svn:https://REMOVED/trunk</connection> </scm> <repositories> <repository> <id>google-maven-repo</id> <name>Maven Google App Engine Repository</name> <url>http://maven-gae-plugin.googlecode.com/svn/repository/</url> </repository> <repository> <id>codehaus</id> <name>codehaus</name> <url>http://repository.codehaus.org/org/codehaus/groovy/</url> </repository> </repositories> <pluginRepositories> <pluginRepository> <id>maven-gae-plugin-repo</id> <name>Maven Google App Engine Repository</name> <url>http://maven-gae-plugin.googlecode.com/svn/repository/</url> </pluginRepository> </pluginRepositories> <dependencies> REMOVED </dependencies> <build> <testOutputDirectory>target/test-classes</testOutputDirectory> <outputDirectory>${webappDirectory}/WEB-INF/classes</outputDirectory> <testSourceDirectory>src/test/java</testSourceDirectory> <plugins> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-war-plugin</artifactId> <version>2.1.1</version> <executions> <execution> <phase>compile</phase> <goals> <goal>exploded</goal> </goals> </execution> </executions> <configuration> <webResources> <resource> <directory>src/main/webapp</directory> <filtering>true</filtering> <includes> <include>**/appengine-web.xml</include> </includes> </resource> </webResources> <webappDirectory>${webappDirectory}</webappDirectory> </configuration> </plugin> <plugin> <groupId>org.codehaus.mojo</groupId> <artifactId>gwt-maven-plugin</artifactId> <version>${gwt-plugin.version}</version> <configuration> <logLevel>INFO</logLevel> <port>${gae.port}</port> <server>com.google.appengine.tools.development.gwt.AppEngineLauncher</server> <runTarget>/REMOVED</runTarget> <style>OBF</style> </configuration> <executions> <execution> <configuration> <extraJvmArgs>-Xmx1024m -Xms512m</extraJvmArgs> <module>com.versature.dashboard.Dashboard</module> </configuration> <goals> <goal>compile</goal> </goals> </execution> </executions> </plugin> <plugin> <groupId>net.kindleit</groupId> <artifactId>maven-gae-plugin</artifactId> <version>0.9.2</version> <configuration> <unpackVersion>${gae.version}</unpackVersion> <serverId>appengine.google.com</serverId> </configuration> <dependencies> <dependency> <groupId>net.kindleit</groupId> <artifactId>gae-runtime</artifactId> <version>${gae.version}</version> <type>pom</type> </dependency> </dependencies> </plugin> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-release-plugin</artifactId> <version>2.2.2</version> <configuration> <scmCommentPrefix>[RELEASE] </scmCommentPrefix> <goals>gae:deploy</goals> </configuration> </plugin> <plugin> <groupId>org.codehaus.gmaven</groupId> <artifactId>gmaven-plugin</artifactId> <version>${gmaven.version}</version> <executions> <execution> <id>sources</id> <goals> <goal>generateStubs</goal> <goal>compile</goal> <goal>generateTestStubs</goal> <goal>testCompile</goal> </goals> </execution> <execution> <id>properties</id> <phase>validate</phase> <goals> <goal>execute</goal> </goals> <configuration> <source> project.properties["gae-application.version"] = project.version.toLowerCase() </source> </configuration> </execution> </executions> </plugin> </plugins> </build> <!-- Specify hard-coded project properties here --> <properties> <gmaven.version>1.3</gmaven.version> <gae.version>1.6.2.1</gae.version> <gae.port>9090</gae.port> <webappDirectory>${project.build.directory}/${project.build.finalName}</webappDirectory> </properties> <profiles> <profile> <id>release-build</id> <activation> <property> <name>performRelease</name> <value>true</value> </property> </activation> <properties> <gwt.style>OBF</gwt.style> </properties> </profile> </profiles> <groupId>com.versature.dashboard</groupId> <artifactId>dashboard</artifactId> </project>
If you run into an error similar to this one:
com.google.appengine.tools.admin.AdminException: Unable to update app: Error posting to URL: https://appengine.google.com/api/appversion/create?app_id=versature-dashboardhr&version=0-8-1-SNAPSHOT&
400 Bad Request
Error when loading application configuration:
Unable to assign value '0-8-1-SNAPSHOT' to attribute 'version':
Value '0-8-1-SNAPSHOT' for version does not match expression '^(?!-)[a-z\d-]{1,100}$'
It is because GAE only accepts lowercase letters, digits and hyphens in version names. (Because those version names become URLs.) I solved this by using a snippet of Groovy (Using the GMaven plugin.) code to automatically create a property with all lowercase letters before filtering it into the appengine-web.xml. So version 0-8-0-SNAPSHOT becomes 0-8-0-snapshot:
1 | project.properties["gae-application.version"] = project.version.toLowerCase() |
And that’s it, it Just Works™. Gotta love Maven!







