Release job for JAXB Spec and API (#105)

Signed-off-by: Tomas Kraus <Tomas.Kraus@oracle.com>
diff --git a/etc/jenkins/release.groovy b/etc/jenkins/release.groovy
new file mode 100644
index 0000000..31b47da
--- /dev/null
+++ b/etc/jenkins/release.groovy
@@ -0,0 +1,80 @@
+// Job input parameters:
+//   SPEC_VERSION      - Specification version to release
+//   NEXT_SPEC_VERSION - Next specification snapshot version to set (e.g. 1.2.4-SNAPSHOT)
+//   API_VERSION       - API version to release
+//   NEXT_API_VERSION  - Next API snapshot version to set (e.g. 1.2.4-SNAPSHOT)
+//   BRANCH            - Branch to release
+//   DRY_RUN           - Do not publish artifacts to OSSRH and code changes to GitHub
+//   OVERWRITE         - Allows to overwrite existing version in git and OSSRH staging repositories
+
+// Job internal argumets:
+//   GIT_USER_NAME       - Git user name (for commits)
+//   GIT_USER_EMAIL      - Git user e-mail (for commits)
+//   SSH_CREDENTIALS_ID  - Jenkins ID of SSH credentials
+//   GPG_CREDENTIALS_ID  - Jenkins ID of GPG credentials (stored as KEYRING variable)
+//   SETTINGS_XML_ID     - Jenkins ID of settings.xml file
+//   SETTINGS_SEC_XML_ID - Jenkins ID of settings-security.xml file
+
+pipeline {
+    
+    agent any
+
+    tools {
+        jdk 'openjdk-jdk11-latest'
+        maven 'apache-maven-latest'
+    }
+
+    environment {
+        SPEC_DIR="${WORKSPACE}/spec"
+        API_DIR="${WORKSPACE}"
+    }
+
+    stages {
+        // Initialize build environment
+        stage('Init') {
+            steps {
+                git branch: BRANCH, credentialsId: SSH_CREDENTIALS_ID, url: GIT_URL
+                // GPG initialization
+                withCredentials([file(credentialsId: GPG_CREDENTIALS_ID, variable: 'KEYRING')]) {
+                    sh '''
+                        gpg --batch --import ${KEYRING}
+                        for fpr in $(gpg --list-keys --with-colons  | awk -F: '/fpr:/ {print $10}' | sort -u);
+                        do
+                          echo -e "5\ny\n" |  gpg --batch --command-fd 0 --expert --edit-key $fpr trust;
+                        done
+
+                    '''
+                }
+                // Git configuration
+                sh '''
+                    git config --global user.name "${GIT_USER_NAME}"
+                    git config --global user.email "${GIT_USER_EMAIL}"
+                '''
+            }
+        }
+        // Perform release
+        stage('Release') {
+            steps {
+                configFileProvider([
+                        configFile(
+                            fileId: SETTINGS_XML_ID,
+                            targetLocation: '/home/jenkins/.m2/settings.xml'
+                        ), 
+                        configFile(
+                            fileId: SETTINGS_SEC_XML_ID, 
+                            targetLocation: '/home/jenkins/.m2/'
+                        )]) {
+                    sshagent([SSH_CREDENTIALS_ID]) {
+                        sh '''
+                            etc/jenkins/release.sh "${SPEC_VERSION}" "${NEXT_SPEC_VERSION}" \
+                                                   "${API_VERSION}" "${NEXT_API_VERSION}" \
+                                                   "${DRY_RUN}" "${OVERWRITE}"
+                        '''
+                    }
+                }
+            }
+        }
+      
+    }
+
+}
diff --git a/etc/jenkins/release.sh b/etc/jenkins/release.sh
new file mode 100755
index 0000000..f0592c1
--- /dev/null
+++ b/etc/jenkins/release.sh
@@ -0,0 +1,119 @@
+#!/bin/bash -ex
+#
+# Arguments:
+#  $1 - SPEC_VERSION
+#  $2 - NEXT_SPEC_VERSION
+#  $3 - API_VERSION
+#  $4 - NEXT_API_VERSION
+#  $5 - DRY_RUN
+#  $6 - OVERWRITE
+
+SPEC_VERSION="${1}"
+NEXT_SPEC_VERSION="${2}"
+API_VERSION="${3}"
+NEXT_API_VERSION="${4}"
+DRY_RUN="${5}"
+OVERWRITE="${6}"
+
+. etc/scripts/mvn.sh
+. etc/scripts/nexus.sh
+
+read_version 'SPEC' "${SPEC_DIR}"
+read_version 'API' "${API_DIR}"
+
+if [ -z "${API_RELEASE_VERSION}" ]; then
+  echo '-[ Missing required API release version number! ]-------------------------------'
+  exit 1
+fi
+if [ -z "${SPEC_RELEASE_VERSION}" ]; then
+  echo '-[ Missing required specification release version number! ]---------------------'
+  exit 1
+fi
+
+RELEASE_TAG="SPEC-${SPEC_RELEASE_VERSION}_API-${API_RELEASE_VERSION}"
+RELEASE_BRANCH="SPEC-${SPEC_RELEASE_VERSION}_API-${API_RELEASE_VERSION}_RELEASE"
+
+if [ ${DRY_RUN} = 'true' ]; then
+  echo '-[ Dry run turned on ]----------------------------------------------------------'
+  MVN_DEPLOY_ARGS='install'
+  echo '-[ Skipping GitHub branch and tag checks ]--------------------------------------'
+else
+  MVN_DEPLOY_ARGS='deploy'
+  GIT_ORIGIN=`git remote`
+  echo '-[ Prepare branch ]-------------------------------------------------------------'
+  if [[ -n `git branch -r | grep "${GIT_ORIGIN}/${RELEASE_BRANCH}"` ]]; then
+    if [ "${OVERWRITE}" = 'true' ]; then
+      echo "${GIT_ORIGIN}/${RELEASE_BRANCH} branch already exists, deleting"
+      git push --delete origin "${RELEASE_BRANCH}" && true
+    else
+      echo "Error: ${GIT_ORIGIN}/${RELEASE_BRANCH} branch already exists"
+      exit 1
+    fi
+  fi
+  echo '-[ Release tag cleanup ]--------------------------------------------------------'
+  if [[ -n `git ls-remote --tags ${GIT_ORIGIN} | grep "${RELEASE_TAG}"` ]]; then
+    if [ "${OVERWRITE}" = 'true' ]; then
+      echo "${RELEASE_TAG} tag already exists, deleting"
+      git push --delete origin "${RELEASE_TAG}" && true
+    else
+      echo "Error: ${RELEASE_TAG} tag already exists"
+      exit 1
+    fi
+  fi
+fi
+
+# Always delete local branch if exists
+git branch --delete "${RELEASE_BRANCH}" && true
+git checkout -b ${RELEASE_BRANCH}
+
+# Always delete local tag if exists
+git tag --delete "${RELEASE_TAG}" && true
+
+# Read Maven identifiers
+read_mvn_id 'SPEC' "${SPEC_DIR}"
+read_mvn_id 'API' "${API_DIR}/jaxb-api"
+
+# Set Nexus identifiers
+SPEC_STAGING_DESC="${SPEC_GROUP_ID}:${SPEC_ARTIFACT_ID}:${SPEC_RELEASE_VERSION}"
+SPEC_STAGING_KEY=$(echo ${SPEC_STAGING_DESC} | sed -e 's/\./\\\./g')
+API_STAGING_DESC="${API_GROUP_ID}:${API_ARTIFACT_ID}:${API_RELEASE_VERSION}"
+API_STAGING_KEY=$(echo ${API_STAGING_DESC} | sed -e 's/\./\\\./g')
+
+# Set release versions
+echo '-[ SPEC release version ]-------------------------------------------------------'
+set_version 'SPEC' "${SPEC_DIR}" "${SPEC_RELEASE_VERSION}" "${SPEC_GROUP_ID}" "${SPEC_ARTIFACT_ID}" ''
+echo '-[ API release version ]--------------------------------------------------------'
+set_version 'API' "${API_DIR}" "${API_RELEASE_VERSION}" "${API_GROUP_ID}" "${API_ARTIFACT_ID}" ''
+
+drop_artifacts "${SPEC_STAGING_KEY}" "${SPEC_DIR}"
+drop_artifacts "${API_STAGING_KEY}" "${API_DIR}"
+
+echo '-[ Deploy artifacts to staging repository ]-----------------------------'
+# Verify, sign and deploy release
+(cd ${SPEC_DIR} && \
+  mvn -U -C \
+      -Poss-release,staging -DskipTests \
+      -DstagingDescription="${SPEC_STAGING_DESC}" \
+      clean ${MVN_DEPLOY_ARGS})
+(cd ${API_DIR} && \
+  mvn -U -C \
+      -Poss-release,staging -DskipTests \
+      -DstagingDescription="${API_STAGING_DESC}" \
+      clean ${MVN_DEPLOY_ARGS})
+
+echo '-[ Tag release ]----------------------------------------------------------------'
+git tag "${RELEASE_TAG}" -m "JSON-B Specification and API release"
+
+# Set next release cycle snapshot version
+echo '-[ SPEC next snapshot version ]-------------------------------------------------'
+set_version 'SPEC' "${SPEC_DIR}" "${SPEC_NEXT_SNAPSHOT}" "${SPEC_GROUP_ID}" "${SPEC_ARTIFACT_ID}" ''
+echo '-[ API next snapshot version ]--------------------------------------------------'
+set_version 'API' "${API_DIR}" "${API_NEXT_SNAPSHOT}" "${API_GROUP_ID}" "${API_ARTIFACT_ID}" ''
+
+if [ ${DRY_RUN} = 'true' ]; then
+  echo '-[ Skipping GitHub update ]-----------------------------------------------------'
+else
+  echo '-[ Push branch and tag to GitHub ]----------------------------------------------'
+  git push origin "${RELEASE_BRANCH}"
+  git push origin "${RELEASE_TAG}"
+fi
diff --git a/etc/scripts/mvn.sh b/etc/scripts/mvn.sh
new file mode 100644
index 0000000..47a3ab2
--- /dev/null
+++ b/etc/scripts/mvn.sh
@@ -0,0 +1,87 @@
+# Maven plugins
+VERSIONS_PLUGIN='org.codehaus.mojo:versions-maven-plugin:2.7'
+HELP_PLUGIN='org.apache.maven.plugins:maven-help-plugin:3.2.0'
+
+# Compute version strings for next development cycle.
+# Version strings are set as new shell variables with provided prefix.
+# Arguments:
+#  $1 - Variable prefix
+#  $2 - Source version
+# Variables set:
+#  "${1}_NEXT_VERSION"  - Next version string: Source string with last component increased by 1
+#  "${1}_NEXT_SNAPSHOT" - Next snapshot string: Next version string with '-SNAPSHOT' suffix
+next_version() {
+  set -f
+  local NEXT_COMPONENTS=(${2//\./ })
+  local LAST_INDEX=$((${#NEXT_COMPONENTS[@]} - 1))
+  local NEXT_COMPONENTS[${LAST_INDEX}]=$((${NEXT_COMPONENTS[${LAST_INDEX}]} + 1))
+  local COMPONENTS_STR="${NEXT_COMPONENTS[@]}"
+  local NEXT_VERSION="${COMPONENTS_STR// /.}"
+  local NEXT_SNAPSHOT="${NEXT_VERSION}-SNAPSHOT"
+  echo "${1} Next Version:    ${NEXT_VERSION}"
+  echo "${1} Next Snapshot:   ${NEXT_SNAPSHOT}"
+  eval "${1}_NEXT_VERSION"="${NEXT_VERSION}"
+  eval "${1}_NEXT_SNAPSHOT"="${NEXT_SNAPSHOT}"
+}
+
+# Prepare release version string and next development cycle versions.
+# Version strings are set as new shell variables with provided prefix.
+# Arguments:
+#  $1 - Variable prefix
+#  $2 - Build directory
+# Source variables:
+#  "${1}_VERSION" - Release version override (optional)
+# Variables set:
+#  "${1}_RELEASE_VERSION" - Release version
+read_version() {
+  local VERSION_VAR="${1}_VERSION"
+  local SNAPSHOT_VERSION=`(cd ${2} && mvn -B ${HELP_PLUGIN}:evaluate -Dexpression=project.version 2> /dev/null | grep -E '^[0-9]+(\.[0-9]+)+-SNAPSHOT$')`
+  if [ -z "${!VERSION_VAR}" ]; then
+    local RELEASE_VERSION="${SNAPSHOT_VERSION/-SNAPSHOT/}"
+  else
+    local RELEASE_VERSION="${!VERSION_VAR}"
+  fi
+  echo "${1} Release Version: ${RELEASE_VERSION}"
+  eval "${1}_RELEASE_VERSION"="${RELEASE_VERSION}"
+  next_version "${1}" "${RELEASE_VERSION}"
+}
+
+# Read Maven identifier (groupId and artifactId).
+# Maven identifier is set as new shell variables with provided prefix.
+# Arguments:
+#  $1 - Variable prefix
+#  $2 - Build directory
+# Variables set:
+#  "${1}_GROUP_ID" - Maven groupId
+#  "${1}_ARTIFACT_ID" - Maven artifactId
+read_mvn_id() {
+  local GROUP_ID=`(cd ${2} && mvn -B ${HELP_PLUGIN}:evaluate -Dexpression=project.groupId | grep -Ev '(^\[)')`
+  local ARTIFACT_ID=`(cd ${2} && mvn -B ${HELP_PLUGIN}:evaluate -Dexpression=project.artifactId | grep -Ev '(^\[)')`
+  echo "${1} Group ID:    ${GROUP_ID}"
+  echo "${1} Artifact ID: ${ARTIFACT_ID}"
+  eval "${1}_GROUP_ID"="${GROUP_ID}"
+  eval "${1}_ARTIFACT_ID"="${ARTIFACT_ID}"
+}
+
+# Set Maven artifact version.
+# Arguments:
+#  $1 - Artifact identifier (e.g. 'SPEC', 'API', 'RI')
+#  $2 - Build directory
+#  $3 - Version to set
+#  $4 - Group ID
+#  $5 - Artifact ID
+#  $6 - Additional Maven arguments
+set_version() {
+  echo '--[ Set version ]---------------------------------------------------------------'
+  # Set release version
+  (cd ${2} && \
+    mvn -U -C \
+        ${6} \
+        -DnewVersion="${3}" \
+        -DgenerateBackupPoms=false \
+        clean ${VERSIONS_PLUGIN}:set)
+  echo '--[ Commit modified pom.xml files ]---------------------------------------------'
+  local POM_FILES=`git status | grep -E 'modified:.*pom\.xml' | sed -e 's/[[:space:]][[:space:]]*modified:[[:space:]][[:space:]]*//'`
+  git add ${POM_FILES} && \
+  git commit -m "Update ${1} version of ${4}:${5} to ${3}"
+}
diff --git a/etc/scripts/nexus.sh b/etc/scripts/nexus.sh
new file mode 100644
index 0000000..f1e717d
--- /dev/null
+++ b/etc/scripts/nexus.sh
@@ -0,0 +1,14 @@
+# Drop old artifacts from staging repository
+# Arguments:
+#  $1 - Staging key value with grep REGEX prefixes
+#  $2 - Build directory
+drop_artifacts() {
+  echo '-[ Drop old staging repository deployments ]------------------------------------'
+  for staging_key in `(cd ${2} && mvn -B nexus-staging:rc-list | egrep "^\[INFO\] [A-Z,a-z,-]+-[0-9]+\s+[A-Z]+\s+${1}" | awk '{print $2}')`; do
+    echo "Repository ID: ${staging_key}"
+    (cd ${2} && \
+      mvn -U -C \
+          -DstagingRepositoryId="${staging_key}" \
+          nexus-staging:rc-drop)
+  done
+}