Restructured project to version builds and move CSS into src.

* Added a new version implementation to the build. The build task now triggers
  an increment to the build version.
* Reconfigured the war plugin to filter source files and replace the @version@
  token with the project version and to rename css and js files to include the
  version number in the filename.
This commit is contained in:
Jonathan Bernard 2015-03-17 18:15:13 -05:00
parent f6f3222855
commit 29f00f806b
14 changed files with 122 additions and 115 deletions

View File

@ -1,10 +1,17 @@
import org.apache.tools.ant.filters.ReplaceTokens
apply plugin: "groovy"
apply plugin: "maven"
apply plugin: "war"
apply plugin: "jetty"
apply from: 'shell.gradle'
group = "com.jdbernard"
version = "2.0"
version = new ProjectVersion()
// webAppDirName = "build/webapp/main"
repositories {
mavenLocal()
@ -30,27 +37,47 @@ dependencies {
testCompile 'junit:junit:4.12'
testRuntime 'com.h2database:h2:1.4.186'
/*
compile 'org.hibernate:hibernate-core:4.3.8.Final'
compile 'org.hibernate:hibernate-validator:5.1.3.Final'
copmile 'javax.el:javax.el-api:2.2.4'
compile 'org.glassfish.web:javax.el:2.2.4'
compile 'org.modelmapper:modelmapper:0.7.3'
compile 'org.springframework:spring-context:4.1.4.RELEASE'
compile 'com.mchange:c3p0:0.9.5'
*/
}
war { from "resources/webapp" }
war {
from "resources/webapp"
filter(ReplaceTokens, tokens: [version: version])
rename '(.+)(\\..*(css|js))', '$1-' + version + '$2'
webInf { from 'src/main/webapp/WEB-INF' }
}
test { testLogging { events 'failed' } }
task testWar(type: War) {
from 'resources/webapp'
webInf { from 'src/test/webapp/WEB-INF' }
webXml = file('src/test/webapp/WEB-INF/web.xml')
filter(ReplaceTokens, tokens: [version: version])
rename '(.+)(\\..*(css|js))', '$1-' + version + '$2'
webInf { from 'resources/test/webapp/WEB-INF' }
classifier 'test' }
// ## Build Versioning task
task incrementBuildNumber(
group: 'versioning',
description: "Increment the project's build number."
) << { ++version.build }
task incrementMinorNumber(
group: 'versioning',
description: "Increment the project's minor version number."
) << { ++version.minor }
task incrementMajorNumber(
group: 'versioning',
description: "Increment the project's major version number."
) << { ++version.major }
task markReleaseBuild(
group: 'versioning',
description: "Mark this version of the project as a release version."
) << { version.release = true }
build.dependsOn << incrementBuildNumber
// ## Custom tasks for local deployment
task deployLocal(dependsOn: ['build']) << {
@ -67,45 +94,55 @@ task deployLocal(dependsOn: ['build']) << {
task killJettyLocal() << {
def pidFile = new File(System.properties['user.home'] + "/temp/jetty.pid")
if (pidFile.exists()) {
println "Killing old Jetty instance."
shell_(["kill", pidFile.text.trim().split(/\n/)].flatten())
pidFile.delete() } }
println "Killing old Jetty instance."
shell_("sh", "-c", 'kill $(jps -l | grep start.jar | cut -f 1 -d " ")') }
task localJetty(dependsOn: ['killJettyLocal', 'deployLocal']) << {
spawn(["java", "-jar", "start.jar"], new File(jettyHome))
shell("sh", "-c",
'jps -l | grep start.jar | cut -f 1 -d " " | sort -n | tail -n 1 > ${HOME}/temp/jetty.pid') }
spawn(["java", "-jar", "start.jar"], new File(jettyHome)) }
// ## Utilitye methods for working with processes.
// ## Project Version
class ProjectVersion {
def shell_(List<String> cmd) { shell(cmd, null, false) }
def shell_(String... cmd) { shell(cmd, null, false) }
def shell(String... cmd) { shell(cmd, null, true) }
private File versionFile
def shell(List<String> cmd, File workingDir, boolean checkExit) {
shell(cmd as String[], workingDir, checkExit) }
int major
int minor
int build
boolean release
def shell(String[] cmd, File workingDir, boolean checkExit) {
def pb = new ProcessBuilder(cmd)
if (workingDir) pb.directory(workingDir)
def process = pb.start()
process.waitForProcessOutput(System.out, System.err)
public ProjectVersion() { this(new File('version.properties')) }
if (process.exitValue() != 0)
println "Command $cmd exited with non-zero result code."
if (checkExit) assert process.exitValue() == 0 : "Not ignoring failed command." }
public ProjectVersion(File versionFile) {
this.versionFile = versionFile
def shell(List<List<String>> cmds, File workingDir) {
cmds.each {
ProcessBuilder pb = new ProcessBuilder(it)
pb.directory(workingDir)
pb.start().waitForProcessOutput(System.out, System.err) } }
if (!versionFile.exists()) {
versionFile.createNewFile()
this.major = this.minor = this.build = 0
this.save() }
def spawn(String... cmd) { spawn(cmd, null) }
def spawn(List<String> cmd, File workingDir) { spawn(cmd as String[], workingDir) }
def spawn(String[] cmd, File workingDir) {
def pb = new ProcessBuilder(cmd)
if (workingDir) pb.directory(workingDir)
def process = pb.start() }
else this.load() }
@Override String toString() { "$major.$minor${release ? '' : '-build' + build}" }
public void setMajor(int major) {
this.major = major; minor = build = 0; release = false; save() }
public void setMinor(int minor) {
this.minor = minor; build = 0; release = false; save() }
public void setBuild(int build) { this.build = build; save() }
private void save() {
def props = new Properties()
versionFile.withInputStream { props.load(it) }
["major", "minor", "build"].each { props[it] = this[it].toString() }
props["version.release"] = release.toString()
versionFile.withOutputStream { props.store(it, "") } }
private void load() {
def props = new Properties()
versionFile.withInputStream { props.load(it) }
["major", "minor", "build"].each {
this[it] = props[it] ? props[it] as int : 0 }
release = Boolean.parseBoolean(props["version.release"]) }
}

View File

@ -1,62 +0,0 @@
/**
* # New Life Songs DB
* @author Jonathan Bernard <jdb@jdb-labs.com>
*/
/** ### forSize
* This mixin allows us to apply some rules selectively based on the screen
* size. There are three primary sizes: `small`, `medium`, and `large`, which
* are mutually exclusive. Additionally there are two additional sizes:
* `notSmall` and `ultraLarge`. `notSmall`, as the name implies matches any
* value which is not the small screen size, so it overlaps with medium,
* large, and ultraLarge. `ultraLarge` defines a wider minimum screen size
* than large, but neither large nor ultraLarge specify maximum widths,
* so ultraLarge is a strict subset of large. A screen large enough to match
* ultraLarge will also match large (compare with medium and large: matching
* medium means it will not match large, and vice versa). */
* {
-moz-box-sizing: border-box;
-webkit-box-sizing: border-box;
box-sizing: border-box;
margin: 0;
padding: 0; }
/* HTML5 elements */
article, aside, details, figcaption, figure,
footer, header, hgroup, menu, nav, section {
display: block; }
body {
color: #333;
font-family: Cantarell;
margin: 2rem auto;
width: 60rem; }
header {
position: relative; }
header > h1, header > h2 {
font-family: "Roboto Condensed";
margin-bottom: 1.5em; }
header nav {
position: absolute;
top: 0;
right: 0; }
header nav ul {
list-style: none; }
header nav ul li {
display: block;
float: right;
padding: 0.4rem 0.6rem; }
header nav ul li a {
color: #333;
display: block;
padding: 0.1rem 0.4rem;
text-decoration: none; }
header nav ul li a:hover {
background-color: #333;
border-radius: 3px;
color: white; }
th {
font-family: "Roboto Condensed"; }
/*# sourceMappingURL=new-life-songs.css.map */

View File

@ -1,7 +0,0 @@
{
"version": 3,
"mappings": ";;;;;;;;;;;;;;;AACA,CAAE;EACE,eAAe,EAAE,UAAU;EAC3B,kBAAkB,EAAE,UAAU;EAC9B,UAAU,EAAE,UAAU;EACtB,MAAM,EAAE,CAAC;EACT,OAAO,EAAE,CAAC;;;AAGd;0CACsC;EAClC,OAAO,EAAC,KAAK;;ACAjB,IAAK;EACD,KAAK,EANF,IAAI;EAOP,WAAW,EAAE,SAAS;EACzB,MAAM,EAAE,SAAS;EACjB,KAAK,EAAE,KAAK;;AAEb,MAAO;EACH,QAAQ,EAAE,QAAQ;EAElB,wBAAe;IACX,WAAW,EAAE,kBAAkB;IAC/B,aAAa,EAAE,KAAK;EAExB,UAAI;IACA,QAAQ,EAAE,QAAQ;IAClB,GAAG,EAAE,CAAC;IACN,KAAK,EAAE,CAAC;IAER,aAAG;MACC,UAAU,EAAE,IAAI;MAEhB,gBAAG;QACC,OAAO,EAAE,KAAK;QACd,KAAK,EAAE,KAAK;QACZ,OAAO,EAAE,aAAa;QAEtB,kBAAE;UACE,KAAK,EAhClB,IAAI;UAiCS,OAAO,EAAE,KAAK;UACd,OAAO,EAAE,aAAa;UACtB,eAAe,EAAE,IAAI;QAEzB,wBAAQ;UACJ,gBAAgB,EAtC7B,IAAI;UAuCS,aAAa,EAAE,GAAG;UAClB,KAAK,EAAE,KAAK;;AAIhC,EAAG;EAAE,WAAW,EAAE,kBAAkB",
"sources": ["reset.scss","new-life-songs.scss"],
"names": [],
"file": "new-life-songs.css"
}

33
shell.gradle Normal file
View File

@ -0,0 +1,33 @@
// ## Utility methods for working with processes.
def shell_(List<String> cmd) { shell(cmd, null, false) }
def shell_(String... cmd) { shell(cmd, null, false) }
def shell(String... cmd) { shell(cmd, null, true) }
def shell(List<String> cmd, File workingDir, boolean checkExit) {
shell(cmd as String[], workingDir, checkExit) }
def shell(String[] cmd, File workingDir, boolean checkExit) {
def pb = new ProcessBuilder(cmd)
if (workingDir) pb.directory(workingDir)
def process = pb.start()
process.waitForProcessOutput(System.out, System.err)
if (process.exitValue() != 0)
println "Command $cmd exited with non-zero result code."
if (checkExit) assert process.exitValue() == 0 : "Not ignoring failed command." }
def shell(List<List<String>> cmds, File workingDir) {
cmds.each {
ProcessBuilder pb = new ProcessBuilder(it)
pb.directory(workingDir)
pb.start().waitForProcessOutput(System.out, System.err) } }
def spawn(String... cmd) { spawn(cmd, null) }
def spawn(List<String> cmd, File workingDir) { spawn(cmd as String[], workingDir) }
def spawn(String[] cmd, File workingDir) {
def pb = new ProcessBuilder(cmd)
if (workingDir) pb.directory(workingDir)
def process = pb.start() }

View File

@ -85,7 +85,7 @@ public class NLSongsDBTest {
// Create Hikari datasource
HikariConfig hcfg = new HikariConfig(
"src/test/webapp/WEB-INF/classes/datasource.test.properties")
"resources/test/WEB-INF/classes/datasource.test.properties")
HikariDataSource dataSource = new HikariDataSource(hcfg)

6
version.properties Normal file
View File

@ -0,0 +1,6 @@
#
#Tue Mar 17 18:13:28 CDT 2015
major=2
version.release=false
minor=0
build=13