Add methods for versioning

Added methods which allow to work with SemVer2 compatible versions
in GIT repositories.

Change-Id: I7b8c5ed289f0d4adbe500416d296511a0f6a52df
Related-Prod: https://mirantis.jira.com/browse/PROD-30728
Related-Prod: https://mirantis.jira.com/browse/PROD-31021
diff --git a/src/com/mirantis/mk/Git.groovy b/src/com/mirantis/mk/Git.groovy
index d9006fa..9d2bdb7 100644
--- a/src/com/mirantis/mk/Git.groovy
+++ b/src/com/mirantis/mk/Git.groovy
@@ -239,3 +239,122 @@
     return branchesList.tokenize('\n')
 }
 
+/**
+ * Method for preparing a tag to be SemVer 2 compatible, and can handle next cases:
+ * - length of tag splitted by dots is more than 3
+ * - first part of splitted tag starts not from digit
+ * - length of tag is lower than 3
+ *
+ * @param   tag       String which contains a git tag from repository
+ * @return  HashMap   HashMap in the form: ['version': 'x.x.x', 'extra': 'x.x.x'], extra
+ *                    is added only if size of original tag splitted by dots is more than 3
+ */
+
+def prepareTag(tag){
+    def parts = tag.tokenize('.')
+    def res = [:]
+    // Handle case with tags like v1.1.1
+    parts[0] = parts[0].replaceFirst("[^\\d.]", '')
+    // handle different sizes of tags - 1.1.1.1 or 1.1.1.1rc1
+    if (parts.size() > 3){
+        res['extra'] = parts[3..-1].join('.')
+    } else if (parts.size() < 3){
+        (parts.size()..2).each {
+            parts[it] = '0'
+        }
+    }
+    res['version'] = "${parts[0]}.${parts[1]}.${parts[2]}"
+    return res
+}
+
+/**
+ * Method for incrementing SemVer 2 compatible version
+ *
+ * @param version  String which contains main part of SemVer2 version - '2.1.0'
+ * @return string  String conaining version with Patch part of version incremented by 1
+ */
+
+def incrementVersion(version){
+    def parts = checkVersion(version)
+    return "${parts[0]}.${parts[1]}.${parts[2].toInteger() + 1}"
+}
+
+/**
+ * Method for checking whether version is compatible with Sem Ver 2
+ *
+ * @param version  String which contains main part of SemVer2 version - '2.1.0'
+ * @return list    With 3 strings as result of splitting version by dots
+ */
+
+def checkVersion(version) {
+    def parts = version.tokenize('.')
+    if (parts.size() != 3 || !(parts[0] ==~ /^\d+/)) {
+        error "Bad version ${version}"
+    }
+    return parts
+}
+
+/**
+ * Method for constructing SemVer2 compatible version from tag in Git repository:
+ * - if current commit matches the last tag, last tag will be returned as version
+ * - if no tag found assuming no release was done, version will be 0.0.1 with pre release metadata
+ * - if tag found - patch part of version will be incremented and pre-release metadata will be added
+ *
+ *
+ * @param repoDir          String which contains path to directory with git repository
+ * @param allowNonSemVer2  Bool   whether to allow working with tags which aren't compatible
+ *                                with Sem Ver 2 (not in form X.Y.Z). if set to true tag will be
+*                                 converted to Sem Ver 2 version e.g tag 1.1.1.1rc1 -> version 1.1.1-1rc1
+ * @return version  String
+ */
+def getVersion(repoDir, allowNonSemVer2 = false) {
+    def common = new com.mirantis.mk.Common()
+    dir(repoDir){
+        def cmd = common.shCmdStatus('git describe --tags --first-parent --abbrev=0')
+        def tag_data = [:]
+        def last_tag = cmd['stdout'].trim()
+        def commits_since_tag
+        if (cmd['status'] != 0){
+            if (cmd['stderr'].contains('fatal: No names found, cannot describe anything')){
+                common.warningMsg('No parent tag found, using initial version 0.0.0')
+                tag_data['version'] = '0.0.0'
+                commits_since_tag = sh(script: 'git rev-list --count HEAD', returnStdout: true).trim()
+            } else {
+                error("Something went wrong, cannot find git information ${cmd['stderr']}")
+            }
+        } else {
+            tag_data['version'] = last_tag
+            commits_since_tag = sh(script: "git rev-list --count ${last_tag}..HEAD", returnStdout: true).trim()
+        }
+        try {
+            checkVersion(tag_data['version'])
+        } catch (Exception e) {
+            if (allowNonSemVer2){
+                common.errorMsg(
+    """Git tag isn't compatible with SemVer2, but allowNonSemVer2 is set.
+    Trying to convert git tag to Sem Ver 2 compatible version
+    ${e.message}""")
+                tag_data = prepareTag(tag_data['version'])
+            } else {
+                error("Git tag isn't compatible with SemVer2\n${e.message}")
+            }
+        }
+        // If current commit is exact match to the first parent tag than return it
+        def pre_release_meta = []
+        if (tag_data.get('extra')){
+            pre_release_meta.add(tag_data['extra'])
+        }
+        if (common.shCmdStatus('git describe --tags --first-parent --exact-match')['status'] == 0){
+            if (pre_release_meta){
+                return "${tag_data['version']}-${pre_release_meta[0]}"
+            } else {
+                return tag_data['version']
+            }
+        }
+        // If we away from last tag for some number of commits - add additional metadata and increment version
+        pre_release_meta.add(commits_since_tag)
+        def next_version = incrementVersion(tag_data['version'])
+        def commit_sha = sh(script: 'git rev-parse --short=7 HEAD', returnStdout: true).trim()
+        return "${next_version}-${pre_release_meta.join('.')}-${commit_sha}"
+    }
+}