Skip to content

Instantly share code, notes, and snippets.

@tomasznajda
Last active November 27, 2019 08:45
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save tomasznajda/5c3574cbff663f4e2db61545ad07fa83 to your computer and use it in GitHub Desktop.
Save tomasznajda/5c3574cbff663f4e2db61545ad07fa83 to your computer and use it in GitHub Desktop.
enum BuildType {
PROD,
RC,
SNAPSHOT,
TASK,
DEV
}
/*
-------------------- PRODUCTION BUILD --------------------
command: ./gradlew assembleProdRelease -Ptype=PROD
branch: master
----------------- RELEASE CANDIDATE BUILD ----------------
command: ./gradlew assembleProdRelease -Ptype=RC
branch: release or hotfix
--------------------- SNAPSHOT BUILD ---------------------
command: ./gradlew assemble -Ptype=SNAPSHOT
branch: develop
----------------------- TASK BUILD -----------------------
command: ./gradlew assemble -Ptype=TASK
branch: feature or bugfix
------------------------- OTHER --------------------------
Q: How to debug versioning scripts?
A: Add -Pdebug=true to the build command
*/
ext {
printVersionDetails = { ->
println("----------------------------------")
println("VERSION CODE: " + provideVersionCode())
println("VERSION NAME: " + provideVersionName())
println("RELEASE NOTES:")
println(provideReleaseNotes())
println("----------------------------------")
}
provideVersionCode = { ->
switch (getBuildType()) {
case BuildType.PROD:
case BuildType.RC:
return prodVersionCode
case BuildType.SNAPSHOT:
return getMergesCountOnCurrentBranch().toInteger()
default:
return 1
}
}
provideVersionName = { ->
def suffix
switch (getBuildType()) {
case BuildType.PROD:
suffix = ""
break
case BuildType.RC:
suffix = "-rc" + getCommitsCountOnCurrentBranch()
break
case BuildType.SNAPSHOT:
suffix = "-SNAPSHOT"
break
case BuildType.TASK:
suffix = "-" + getCurrentTaskId() + "-c" + getCommitsCountOnCurrentBranch()
break
default:
suffix = "-dev"
}
return prodVersionName + suffix
}
provideReleaseNotes = { ->
def branch = getCurrentBranchName().toString()
switch (getBuildType()) {
case BuildType.PROD:
return getProdVariantNotes()
case BuildType.RC:
if (branch.contains("hotfix")) return getHotfixBranchNotes()
else return getReleaseBranchNotes()
case BuildType.SNAPSHOT:
return getSnapshotVariantNotes()
case BuildType.TASK:
return getTaskVariantNotes()
default:
return ""
}
}
provideTestersGroup = { ->
switch (getBuildType()) {
case BuildType.PROD:
return "prod-testers"
case BuildType.RC:
return "rc-testers"
case BuildType.SNAPSHOT:
return "snapshot-testers"
case BuildType.TASK:
return "task-testers"
default:
return ""
}
}
getBuildType = { ->
if (project.hasProperty("type")) {
def branch = getCurrentBranchName().toString()
if (type as BuildType == BuildType.PROD && branch != "master") throw new IllegalArgumentException("Cannot build PROD version from " + branch)
else if (type as BuildType == BuildType.RC && !(branch.contains("release") || branch.contains("hotfix"))) throw new IllegalArgumentException("Cannot build RC version from " + branch)
else if (type as BuildType == BuildType.SNAPSHOT && branch != "develop") throw new IllegalArgumentException("Cannot build SNAPSHOT version from " + branch)
else if (type as BuildType == BuildType.TASK && !(branch.contains("feature") || branch.contains("bugfix"))) throw new IllegalArgumentException("Cannot build SNAPSHOT version from " + branch)
type as BuildType
} else {
BuildType.DEV
}
}
getCurrentTaskId = { ->
def parts = getCurrentBranchName().split("-")
if (parts.length > 1) return parts[parts.length - 1]
else return "UNKNOWN"
}
getCurrentBranchName = { ->
logd("Method", "getCurrentBranchName")
return git(["rev-parse", "--abbrev-ref", "HEAD"])
}
getCommitsCountOnCurrentBranch = { ->
logd("Method", "getCommitsCountOnCurrentBranch")
return git(["rev-list", "--count", "HEAD", "^origin/develop", "^origin/master"]).trim()
}
getMergesCountOnCurrentBranch = { ->
logd("Method", "getMergesCountOnCurrentBranch")
return git(["rev-list", "--merges", "--count", "HEAD"]).trim()
}
getProdVariantNotes = { ->
def tag = getAllTagsDescending().split('\n')[0]
def tagCommitHash = getCommitHashForTag(tag)
def latestReleaseCandidateCommit = getCommitParentsAndBody(tagCommitHash)[0].split(' ')[1]
def notes = new ArrayList<String>()
notes.addAll(getMessagesSinceBranchWasCreated(latestReleaseCandidateCommit))
notes.addAll(getDevelopMergeMessagesSinceLastRelease(latestReleaseCandidateCommit))
return notes.join("\n")
}
getReleaseBranchNotes = { ->
def latestCurrentBranchCommit = getCommitHash(0, "HEAD")
def notes = new ArrayList<String>()
notes.add(getCurrentBranchName())
notes.addAll(getMessagesSinceBranchWasCreated(latestCurrentBranchCommit))
notes.addAll(getDevelopMergeMessagesSinceLastRelease(latestCurrentBranchCommit))
return notes.join("\n")
}
getHotfixBranchNotes = { ->
def latestCurrentBranchCommit = getCommitHash(0, "HEAD")
def notes = new ArrayList<String>()
notes.add(getCurrentBranchName())
notes.addAll(getMessagesSinceBranchWasCreated(latestCurrentBranchCommit))
return notes.join("\n")
}
getSnapshotVariantNotes = { ->
def latestCurrentBranchCommit = getCommitHash(0, "HEAD")
def notes = new ArrayList<String>()
notes.add(getCurrentBranchName())
notes.addAll(getDevelopMergeMessagesSinceLastRelease(latestCurrentBranchCommit))
return notes.join("\n")
}
getTaskVariantNotes = { ->
def latestCurrentBranchCommit = getCommitHash(0, "HEAD")
def notes = new ArrayList<String>()
notes.add(getCurrentBranchName())
notes.addAll(getMessagesSinceCurrentBranchWasCreated(latestCurrentBranchCommit))
return notes.join("\n")
}
getDevelopMergeMessagesSinceLastRelease = { startCommitHash ->
def latestTag = getAllTagsDescending().split('\n')[0]
def endCommitHash = findFirstCommitHashOnParentPath(getCommitHashForTag(latestTag))
return getMergeCommitsMessages(startCommitHash, endCommitHash)
.split('\n')
.findAll { it.contains("feature") || it.contains("bugfix") }
.collect { it.substring(it.indexOf('/') + 1, it.length()) }
.collect { "- " + it }
}
getMessagesSinceBranchWasCreated = { startCommitHash ->
def endCommitHash = findFirstCommitHashOnParentPath(startCommitHash)
return getAllCommitsMessages(startCommitHash, endCommitHash)
.split('\n')
.findAll { !it.contains("Merge branch") && !isVersionBumpMessage(it) && it.length() > 0 }
.collect { "- " + it }
}
getMessagesSinceCurrentBranchWasCreated = {
return git(["log", "--pretty=format:'%s'", "HEAD", "^origin/master", "^origin/develop"])
.split('\n')
.findAll { !it.contains("Merge branch") && !isVersionBumpMessage(it) && it.length() > 0 }
.collect { "- " + it }
}
getCommitHashForTag = { tag ->
logd("Method", "getCommitHashForTag")
return git(["rev-list", "-n 1", tag])
}
getAllTagsDescending = { ->
logd("Method", "getAllTagsDescending")
return git(["tag", "--sort=-refname"])
}
getCommitHash = { index, branch ->
logd("Method", "getCommitHash")
return git(["rev-list", "--max-count=1", "--skip=" + index, branch])
}
getMergeCommitsMessages = { startCommitHash, endCommitHash ->
logd("Method", "getMergeCommitsMessages")
return git(["log", "--merges", "--first-parent", "--pretty=format:'%s'", endCommitHash + ".." + startCommitHash])
}
getAllCommitsMessages = { startCommitHash, endCommitHash ->
logd("Method", "getAllCommitsMessages")
return git(["log", "--pretty=format:'%s'", endCommitHash + ".." + startCommitHash])
}
findFirstCommitHashOnParentPath = { initialCommitHash ->
def commitHash = initialCommitHash
while (true) {
def info = getCommitParentsAndBody(commitHash)
if (info[1] != null && (info[1].contains("develop") || info[1].contains("master"))) return commitHash
commitHash = info[0].split(' ')[0]
}
}
getCommitParentsAndBody = { commit ->
logd("Method", "getCommitParentsAndBody")
def result = git(["show", "--quiet", "--pretty=format:'%P%n%b'", commit]).split('\n')
if(result.size() > 1)
return [result[result.size() - 2], result[result.size() - 3]]
else
return [result[result.size() - 1], ""]
}
isVersionBumpMessage = { message ->
return (message =~ /v\d+\.\d+\.\d+\s\(\d+\)/).size() > 0
}
git = { arguments ->
new ByteArrayOutputStream().withStream { os ->
logd("Git", "--> git " + arguments.join(' '))
exec {
executable = "git"
args = arguments
standardOutput = os
}
def output = formatOutput(os.toString())
logd("Git", output)
return output
}
}
logd = { tag, message ->
if (project.hasProperty("debug") && debug) {
message.split('\n').each { println("D/" + tag + ": " + it) }
}
}
formatOutput = { stream ->
return stream.toString().split('\n').collect { formatLine(it) }.join('\n')
}
formatLine = { line ->
if (line.startsWith("'"))
line = line.substring(1)
if (line.endsWith("'"))
line = line.substring(0, line.length() - 1)
return line.trim()
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment