view scripts/release.groovy @ 216:b628d49d2891

DEP-11: change name of lock file for release branch. update release script to depend on new resolution and dependency locking
author smith@nwoca.org
date Wed, 29 Jun 2016 13:57:17 +0100
parents 5bebb590b30e
children 49a220a1bde0
line wrap: on
line source
#!groovy
import groovy.transform.Sortable
import groovy.transform.ToString
import groovy.transform.TupleConstructor

/**
This script implements the SSDT branching strategy based on hg flow
 and dependency resolution locking.

The intention is to automate of creation of correctly configured release branches.
 The script tries to do the right thing based on standard SSDT project structures,
 but it is the user's responsibility to ensure it's correct.

The script does NOT "hg push --new-branch".   That step is left for you
if the branch was created correctly.

*/

def branch = new BranchInfo()

println "Project:\n"

println "\trepo\t${("hg path".execute().text.split('=') ?: ['', ''])[1].trim()}"
println "\tbranch\t$branch"
println "\tversion\t$branch.version"

println "-" * 40
println ""

if (args.size() < 1) {
	println """	
	usage:  release.groovy {releaseVersion}\n
	  e.g:  release.groovy 1.6.0
	  
	If release ends in ".0", then will create 'release' stream, otherwise 'hotfix'.
	For hotfix, current working branch should be the release branch being hotfix'ed.
	
	Recommend that this script be executed in a fresh clone of the repo.
	
	** Any uncommitted changes in the working directory will be committed with
	   the initial setting of the version. These are assumed to be
	   'latest.integration' changes.
"""	

	System.exit(0)
}

def releaseVersion = args[0]

def hotfix = !releaseVersion.endsWith('.0')
def stream = hotfix ? 'hotfix' : 'release'

println "Creating $stream branch for $releaseVersion"

checkForSnapshots()

println "hg flow $stream start v${releaseVersion} --dry-run".execute().text

println "Continue? Enter = Yes, ^C to cancel"
System.in.newReader().readLine()

println "hg flow ${stream} start v${releaseVersion} --dirty".execute().text

println "hg update ${stream}/v${releaseVersion}".execute().text

println "Starting dependency lock via gradle... (please wait)"
println "cmd /c gradlew.bat deleteGLobalLock generateGlobalLock saveGlobalLock".execute().text

println 'hg commit -A release.lock -m "lock dynamic dependencies for release"'.execute().text

println "Created $releaseVersion $stream branch with locked dynamic dependencies."
println "   Verify the branch and release.lock file created correctly then push the new branch."
println "   If any problems, then delete repo and clone a fresh copy repository."

def checkForSnapshots() {
		def lines = new File('gradle.properties').readLines() + new File('build.gradle').readLines()
		def snapshots = lines.collect { it.trim() }.findAll{ 
			it.contains('.SNAPSHOT') && !it.startsWith('version=') ||
			it.contains('latest.') && (it.startsWith('compile') || it.startsWith('runtime'))
		}
		if (snapshots) {
			println "project contains SNAPSHOT dependencies: \n\t" + snapshots.join('\n\t')
			System.exit(1)
		}
}


@TupleConstructor
@Sortable
class Version {

	Integer major = 0
	Integer minor = 0
	Integer patch = 0
	Boolean snapshot = true

	Version nextVersion() {
		new Version(major, minor + 1, 0)
	}

	String toString() {
		"${major}.${minor}.${patch}${snapshot ? '.SNAPSHOT' : ''}"
	}

}


@ToString(includes=['name','shortName','buildVersion','imageId','deployName'],includeNames= true)
class BranchInfo {
	def name
	def stream = "none"
	def buildNumber = ""
	def changeset = ""
	def version

	BranchInfo(name = null) {
		this.name = name
		if (!name) {
			this.name = determineName() ?: ''
		}
		this.name = this.name.replace('@', '-')
		determineStream()
		buildNumber = System.getenv('bamboo_buildNumber') ?: ""
		changeset = System.getenv('bamboo_planRepository_revision') ?: ""
	}

	String getDefaultDependencyStatus() {
		return isRelease() ? 'release' : 'integration'
	}

	private boolean isRelease() {
		return stream in ['release', 'hotfix']
	}

	def getShortName() {
		def result = name.contains('/') ? name.split('/')[1] : name
	}

	String getBuildVersion() {
		def v = isRelease() ? shortName - "v": ""
		return v
	}

	def Version getVersion() {
		if (!version) {
			if (isRelease()) {
				version = new Version(*getBuildVersion().split('\\.')*.toInteger(), false)
			} else {
				version = findSnapshotVersion()
			}
		}
		return version
	}

	def getImageId() {
		(buildVersion ?:  shortName.take(25)) + (buildNumber ? "-${buildNumber}" : "-0")
	}

	def getDeployName() {
		(buildVersion ?:  shortName.take(25)).toLowerCase().collectReplacements {
			('a'..'z').contains(it) || ('0'..'9').contains(it) || it == "-" ? null : '-'
		}
	}

	def getHash() {
		generateMD5(name)
	}
	def generateMD5(String s) {
		def digest = java.security.MessageDigest.getInstance("MD5")
		digest.update(s.bytes);
		new BigInteger(1, digest.digest()).toString(16).padLeft(32, '0')
	}

	private findSnapshotVersion() {
		def versions = "hg branches --closed".execute().text.split('\n').findAll {
			it.startsWith( 'release') || it.startsWith( 'hotfix')
		}.collect {
			it.replaceAll('\\s+',' ').split(' ')[0].split('/')[1] - 'v'
		}.collect {
			new Version(*it.split('\\.')*.toInteger())
		}.sort { v1, v2 -> v2 <=> v1 }

		return versions ?  versions.first().nextVersion() : new Version().nextVersion()

	}


	def determineName()  {
		try {
			def branch = "hg branch".execute().text.trim()
			def rawParents = 'hg parents'.execute().text
			def parent = rawParents.split('\n').find { it.startsWith("branch") }?.split(":")?.getAt(1)?.trim()
			return parent ?: branch
		} catch (e) {
			['.hg/branch', '../.hg/branch'].findResult {
				new File(it).exists() ? new File(it).text : null
			}
		}
	}

	void determineStream() {
		def flowConfig = new File('.hgflow').exists() ? new File('.hgflow') : new File('../.hgflow')
		if (flowConfig.exists()) {
			def flows = new Properties()
			flows.load(flowConfig.newReader())
			flows.stringPropertyNames().each {
				if (!it.startsWith("[") && name.startsWith(flows.getProperty(it))) {
					stream = it
				}
			}
		}
	}

}