#!/bin/bash # Release process automation script. # Used to create branch/tag, update versions in pom.xml # build and push maven artifacts and docker images to Quay.io REGISTRY="quay.io" ORGANIZATION="eclipse" # KEEP RIGHT ORDER!!! DOCKER_FILES_LOCATIONS=( che/dockerfiles/endpoint-watcher che/dockerfiles/keycloak che/dockerfiles/postgres che/dockerfiles/dev che/dockerfiles/che che/dockerfiles/dashboard-dev che/dockerfiles/e2e ) IMAGES_LIST=( quay.io/eclipse/che-endpoint-watcher quay.io/eclipse/che-keycloak quay.io/eclipse/che-postgres quay.io/eclipse/che-dev quay.io/eclipse/che-server quay.io/eclipse/che-dashboard-dev quay.io/eclipse/che-e2e ) loadMvnSettingsGpgKey() { set +x mkdir $HOME/.m2 #prepare settings.xml for maven and sonatype (central maven repository) echo $CHE_MAVEN_SETTINGS | base64 -d > $HOME/.m2/settings.xml #load GPG key for sign artifacts echo $CHE_OSS_SONATYPE_GPG_KEY | base64 -d > $HOME/.m2/gpg.key #load SSH key for release process echo ${#CHE_OSS_SONATYPE_GPG_KEY} mkdir $HOME/.ssh/ echo $CHE_GITHUB_SSH_KEY | base64 -d > $HOME/.ssh/id_rsa chmod 0400 $HOME/.ssh/id_rsa ssh-keyscan github.com >> ~/.ssh/known_hosts set -x export GPG_TTY=$(tty) gpg --import $HOME/.m2/gpg.key # gpg --import --batch $HOME/.m2/gpg.key gpg --version } installDebDeps(){ set +x # TODO should this be node 12? curl -sL https://deb.nodesource.com/setup_10.x | sudo -E bash - sudo apt-get install -y nodejs } installMaven(){ set -x mkdir -p /opt/apache-maven && curl -sSL https://downloads.apache.org/maven/maven-3/3.6.3/binaries/apache-maven-3.6.3-bin.tar.gz | tar -xz --strip=1 -C /opt/apache-maven export M2_HOME="/opt/apache-maven" export PATH="/opt/apache-maven/bin:${PATH}" mvn -version || die_with "mvn not found in path: ${PATH} !" set +x } evaluateCheVariables() { echo "Che version: ${CHE_VERSION}" # derive branch from version BRANCH=${CHE_VERSION%.*}.x echo "Branch: ${BRANCH}" if [[ ${CHE_VERSION} == *".0" ]]; then BASEBRANCH="master" else BASEBRANCH="${BRANCH}" fi echo "Basebranch: ${BASEBRANCH}" echo "Release che-parent: ${RELEASE_CHE_PARENT}" echo "Version che-parent: ${VERSION_CHE_PARENT}" } checkoutProjects() { if [[ ${RELEASE_CHE_PARENT} = "true" ]]; then checkoutProject git@github.com:eclipse/che-parent fi checkoutProject git@github.com:eclipse/che } checkoutProject() { PROJECT="${1##*/}" echo "checking out project $PROJECT with ${BRANCH} branch" if [[ ! -d ${PROJECT} ]]; then echo "project not found in ${PROJECT} directory, performing 'git clone'" git clone $1 fi cd $PROJECT git checkout ${BASEBRANCH} set -x set +e if [[ "${BASEBRANCH}" != "${BRANCH}" ]]; then git branch "${BRANCH}" || git checkout "${BRANCH}" && git pull origin "${BRANCH}" git push origin "${BRANCH}" git fetch origin "${BRANCH}:${BRANCH}" git checkout "${BRANCH}" fi set -e set +x cd .. } checkoutTags() { if [[ ${RELEASE_CHE_PARENT} = "true" ]]; then cd che-parent git checkout ${CHE_VERSION} cd .. fi cd che git checkout ${CHE_VERSION} cd .. } # check for build errors, since we're using set +e above to NOT fail the build for Nexus problems checkLogForErrors () { tmplog="$1" errors_in_log="$(grep -E "FAILURE \[|BUILD FAILURE|Failed to execute goal" $tmplog || true)" if [[ ${errors_in_log} ]]; then echo "${errors_in_log}" exit 1 fi } # TODO change it to someone else? setupGitconfig() { git config --global user.name "Mykhailo Kuznietsov" git config --global user.email mkuznets@redhat.com # hub CLI configuration git config --global push.default matching # replace default GITHUB_TOKEN, that is used by GitHub export GITHUB_TOKEN="${CHE_BOT_GITHUB_TOKEN}" } commitChangeOrCreatePR() { set +e aVERSION="$1" aBRANCH="$2" PR_BRANCH="$3" COMMIT_MSG="[release] Bump to ${aVERSION} in ${aBRANCH}" # commit change into branch git commit -asm "${COMMIT_MSG}" git pull origin "${aBRANCH}" PUSH_TRY="$(git push origin "${aBRANCH}")" # shellcheck disable=SC2181 if [[ $? -gt 0 ]] || [[ $PUSH_TRY == *"protected branch hook declined"* ]]; then # create pull request for master branch, as branch is restricted git branch "${PR_BRANCH}" git checkout "${PR_BRANCH}" git pull origin "${PR_BRANCH}" git push origin "${PR_BRANCH}" lastCommitComment="$(git log -1 --pretty=%B)" hub pull-request -f -m "${lastCommitComment}" -b "${aBRANCH}" -h "${PR_BRANCH}" fi set -e } createTags() { if [[ $RELEASE_CHE_PARENT = "true" ]]; then tagAndCommit che-parent fi tagAndCommit che } tagAndCommit() { cd $1 # this branch isn't meant to be pushed git checkout -b release-${CHE_VERSION} git commit -asm "Release version ${CHE_VERSION}" if [ $(git tag -l "$CHE_VERSION") ]; then echo "tag ${CHE_VERSION} already exists! recreating ..." git tag -d ${CHE_VERSION} git push origin :${CHE_VERSION} git tag "${CHE_VERSION}" else echo "[INFO] creating new tag ${CHE_VERSION}" git tag "${CHE_VERSION}" fi git push --tags echo "[INFO] tag created and pushed for $1" cd .. } prepareRelease() { if [[ $RELEASE_CHE_PARENT = "true" ]]; then pushd che-parent >/dev/null # Install previous version, in case it is not available in central repo # which is needed for dependent projects mvn clean install mvn versions:set -DgenerateBackupPoms=false -DnewVersion=${VERSION_CHE_PARENT} mvn clean install popd >/dev/null echo "[INFO] Che Parent version has been updated to ${VERSION_CHE_PARENT}" fi pushd che >/dev/null if [[ $RELEASE_CHE_PARENT = "true" ]]; then mvn versions:update-parent -DgenerateBackupPoms=false -DallowSnapshots=false -DparentVersion=[${VERSION_CHE_PARENT}] fi mvn versions:set -DgenerateBackupPoms=false -DallowSnapshots=false -DnewVersion=${CHE_VERSION} echo "[INFO] Che Server version has been updated to ${CHE_VERSION} (parentVersion = ${VERSION_CHE_PARENT})" # Replace dependencies in che-server parent sed -i -e "s#.*<\/che.dashboard.version>#${CHE_VERSION}<\/che.dashboard.version>#" pom.xml sed -i -e "s#.*<\/che.version>#${CHE_VERSION}<\/che.version>#" pom.xml echo "[INFO] Dependencies updated in che-server parent" # TODO pull parent pom version from VERSION file, instead of being hardcoded pushd typescript-dto >/dev/null sed -i -e "s#.*<\/che.version>#${CHE_VERSION}<\/che.version>#" dto-pom.xml # Do not change the version of the parent pom, which is fixed # TODO is this correct? seems like we're setting parent to a version that doesn't exist, rather than hardcoding to VERSION_CHE_PARENT value sed -i -e "/${VERSION_CHE_PARENT}<\/version>/ ! s#.*<\/version>#${CHE_VERSION}<\/version>#" dto-pom.xml echo "[INFO] Dependencies updated in che typescript DTO (should have parent = ${VERSION_CHE_PARENT}, not ${CHE_VERSION})" popd >/dev/null # TODO more elegant way to execute these scripts pushd .ci >/dev/null ./set_tag_version_images.sh ${CHE_VERSION} echo "[INFO] Tag versions of images have been set in che-server" popd >/dev/null popd >/dev/null } releaseCheServer() { set -x tmpmvnlog=/tmp/mvn.log.txt if [[ $RELEASE_CHE_PARENT = "true" ]]; then pushd che-parent >/dev/null rm -f $tmpmvnlog || true set +e mvn clean install -ntp -U -Pcodenvy-release -Dgpg.passphrase=$CHE_OSS_SONATYPE_PASSPHRASE | tee $tmpmvnlog EXIT_CODE=$? set -e # try maven build again if Nexus dies if grep -q -E "502 - Bad Gateway|Nexus connection problem" $tmpmvnlog; then rm -f $tmpmvnlog || true mvn clean install -ntp -U -Pcodenvy-release -Dgpg.passphrase=$CHE_OSS_SONATYPE_PASSPHRASE | tee $tmpmvnlog EXIT_CODE=$? fi # check log for errors if build successful; if failed, no need to check (already failed) if [ $EXIT_CODE -eq 0 ]; then checkLogForErrors $tmpmvnlog echo 'Build of che-parent: Success!' else echo '[ERROR] 2. Build of che-parent: Failed!' exit $EXIT_CODE fi popd >/dev/null fi pushd che >/dev/null rm -f $tmpmvnlog || true set +e mvn clean install -U -Pcodenvy-release -Dgpg.passphrase=$CHE_OSS_SONATYPE_PASSPHRASE | tee $tmpmvnlog EXIT_CODE=$? set -e # try maven build again if Nexus dies if grep -q -E "502 - Bad Gateway|Nexus connection problem" $tmpmvnlog; then rm -f $tmpmvnlog || true mvn clean install -U -Pcodenvy-release -Dgpg.passphrase=$CHE_OSS_SONATYPE_PASSPHRASE | tee $tmpmvnlog EXIT_CODE=$? fi # check log for errors if build successful; if failed, no need to check (already failed) if [ $EXIT_CODE -eq 0 ]; then checkLogForErrors $tmpmvnlog echo 'Build of che-server: Success!' else echo '[ERROR] 2. Build of che-server: Failed!' exit $EXIT_CODE fi set +x popd >/dev/null } buildImages() { echo "Going to build docker images" set -e set -o pipefail TAG=$1 # stop / rm all containers if [[ $(docker ps -aq) != "" ]];then docker rm -f "$(docker ps -aq)" fi # BUILD IMAGES for image_dir in ${DOCKER_FILES_LOCATIONS[@]} do if [[ ${image_dir} == "che/dockerfiles/che" ]]; then bash "$(pwd)/${image_dir}/build.sh" --tag:${TAG} --build-arg:"CHE_DASHBOARD_VERSION=${CHE_VERSION},CHE_WORKSPACE_LOADER_VERSION=${CHE_VERSION}" else bash "$(pwd)/${image_dir}/build.sh" --tag:${TAG} fi if [[ $? -ne 0 ]]; then echo "ERROR:" echo "build of '${image_dir}' image is failed!" exit 1 fi done } tagLatestImages() { for image in ${IMAGES_LIST[@]} do echo y | docker tag "${image}:$1" "${image}:latest" if [[ $? -ne 0 ]]; then die_with "docker tag of '${image}' image is failed!" fi done } pushImagesOnQuay() { #PUSH IMAGES for image in ${IMAGES_LIST[@]} do echo y | docker push "${image}:$1" if [[ $2 == "pushLatest" ]]; then echo y | docker push "${image}:latest" fi if [[ $? -ne 0 ]]; then die_with "docker push of '${image}' image is failed!" fi done } bumpVersions() { # infer project version + commit change into ${BASEBRANCH} branch echo "${BASEBRANCH} ${BRANCH}" if [[ "${BASEBRANCH}" != "${BRANCH}" ]]; then # bump the y digit [[ ${BRANCH} =~ ^([0-9]+)\.([0-9]+)\.x ]] && BASE=${BASH_REMATCH[1]}; NEXT=${BASH_REMATCH[2]}; (( NEXT=NEXT+1 )) # for BRANCH=7.10.x, get BASE=7, NEXT=11 NEXTVERSION_Y="${BASE}.${NEXT}.0-SNAPSHOT" bumpVersion ${NEXTVERSION_Y} ${BASEBRANCH} fi # bump the z digit [[ ${CHE_VERSION} =~ ^([0-9]+)\.([0-9]+)\.([0-9]+) ]] && BASE="${BASH_REMATCH[1]}.${BASH_REMATCH[2]}"; NEXT="${BASH_REMATCH[3]}"; (( NEXT=NEXT+1 )) # for VERSION=7.7.1, get BASE=7.7, NEXT=2 NEXTVERSION_Z="${BASE}.${NEXT}-SNAPSHOT" bumpVersion ${NEXTVERSION_Z} ${BRANCH} } bumpVersion() { set -x echo "[info]bumping to version $1 in branch $2" if [[ $RELEASE_CHE_PARENT = "true" ]]; then pushd che-parent >/dev/null git checkout $2 #install previous version, in case it is not available in central repo #which is needed for dependent projects mvn clean install mvn versions:set -DgenerateBackupPoms=false -DnewVersion=${CHE_VERSION} mvn clean install commitChangeOrCreatePR ${CHE_VERSION} $2 "pr-${2}-to-${1}" popd >/dev/null fi pushd che >/dev/null git checkout $2 if [[ $RELEASE_CHE_PARENT = "true" ]]; then mvn versions:update-parent -DgenerateBackupPoms=false -DallowSnapshots=true -DparentVersion=[${VERSION_CHE_PARENT}] fi mvn versions:set -DgenerateBackupPoms=false -DallowSnapshots=true -DnewVersion=$1 sed -i -e "s#.*<\/che.dashboard.version>#$1<\/che.dashboard.version>#" pom.xml sed -i -e "s#.*<\/che.version>#$1<\/che.version>#" pom.xml pushd typescript-dto >/dev/null sed -i -e "s#.*<\/che.version>#${1}<\/che.version>#" dto-pom.xml sed -i -e "s#.*<\/version>#${1}<\/version>#" dto-pom.xml popd >/dev/null commitChangeOrCreatePR $1 $2 "pr-${2}-to-${1}" popd >/dev/null set +x } updateImageTagsInCheServer() { cd che git checkout ${BRANCH} cd .ci ./set_tag_version_images.sh ${CHE_VERSION} cd .. git commit -asm "Set ${CHE_VERSION} release image tags" git push origin ${BRANCH} } installMaven loadMvnSettingsGpgKey installDebDeps set -x setupGitconfig evaluateCheVariables checkoutProjects if [[ "${BUMP_NEXT_VERSION}" = "true" ]]; then bumpVersions updateImageTagsInCheServer # checkout back to branches to make release from checkoutProjects fi if [[ "${REBUILD_FROM_EXISTING_TAGS}" = "true" ]]; then echo "[INFO] Checking out from existing ${CHE_VERSION} tag" checkoutTags else echo "[INFO] Creating a new ${CHE_VERSION} tag" prepareRelease createTags fi releaseCheServer if [[ "${BUILD_AND_PUSH_IMAGES}" = "true" ]]; then buildImages ${CHE_VERSION} tagLatestImages ${CHE_VERSION} pushImagesOnQuay ${CHE_VERSION} pushLatest fi