Automatically tag latest commit deployed

As we continue working and increasing our Salesforce projects, even the dynamic deployments can grow a lot between releases (around 1 month of development). So the build at the beginning of the release may take 5 to 10 minutes to deploy and run the specific tests, but by the end of the deployment it may be taking up to 3 hours (deploying almost 10k components and running 8k unit tests). In order to be able to keep a more “decent” build time we came with the idea of build only the things that changed since the last successful deployment. Bamboo doesn’t have something like this out of the box so I created a simple script that runs at the end of the build (after Salesforce returns a successful message) and creates a tag on the last commit that was deployed. I also updated the script that generates the package to be deployed in order to pick up from that last tag instead of the hard-coded commit. By doing this we are having an average build time of 20 minutes. The key here is to keep your branch up to date with the latest changes and run the builds often, so even if you didn’t merge anything from your side today, you still deploy what the other teams did, and tomorrow when you merge and run your build you will be deploying only your changes.
Let’s take a look at the script:

#/usr/bin/env bash
# $PLANKEY ${bamboo.planKey} 
# $REVISION ${bamboo.planRepository.revision}
# $PLANNAME ${bamboo.planName}
# $BUILDNUMBER ${bamboo.buildNumber}
# $REPOSITORYURL ${bamboo.planRepository.repositoryUrl}
# $BBPASSWORD ${bamboo_bitbucket_password}

function usage() { echo "Usage: $0 -k  -r  -n  -b  -u  -p " 1>&2; exit 1; }

while getopts :k:r:n:b:u:p option
do
    case "${option}"
    in
        k) PLANKEY=${OPTARG};;
        r) REVISION=${OPTARG};;
        n) PLANNAME=${OPTARG};;
        b) BUILDNUMBER=${OPTARG};;
        u) REPOSITORYURL=${OPTARG};;
        p) BBPASSWORD=${OPTARG};;
        *) usage;; # Invalid argument
    esac
done

if [[ -z "${BBPASSWORD// }" ]]
then
    BBPASSWORD=${bamboo_bitbucket_password}
fi

echo creating tag for commit: $REVISION
DATE=`date '+%Y%m%d%H%M%S'`
TAGNAME="$PLANKEY-$DATE"
echo final tag name: $TAGNAME

echo creating repo to create and push tag
ACTUALURL=$REPOSITORYURL
USERNAME=[username]
PASSWORD=$BBPASSWORD
REPOURL=${ACTUALURL#*https://}
REPOURL="https://"$USERNAME:$PASSWORD@$REPOURL
git remote add central2 $REPOURL
git config --global user.email [email address]
git config --global user.name $USERNAME
git config --global push.default simple

echo deleting previous tags
if [[ ! -z "${PLANKEY// }" ]]
then
    echo finding if there is a tag
    echo tags: `git tag -l --sort=v:refname $PLANKEY* | head -n -1`
    TEMP=`git tag -l --sort=v:refname $PLANKEY* | head -n -1`
    printf "\n"
    if [[ ! -z "${TEMP// }" ]]
    then
    for tag in `git tag -l --sort=v:refname $PLANKEY* | head -n -1`
        do
            git tag -d $tag
            git push central2 :refs/tags/$tag
        done
    fi
fi

echo pushing new tag
git tag $TAGNAME -m "$PLANNAME build number $BUILDNUMBER passed build." $REVISION
git push central2 $TAGNAME
git ls-remote --exit-code --tags central2 $BUILDNUMBER
git remote remove central2

As you can see the script is pretty simple and straight forward. There are some key things to take into account do. Bamboo doesn’t have a way to pass the connection created to a repository, so you need to create one inside the script. That’s what this piece is:

echo creating repo to create and push tag
ACTUALURL=$REPOSITORYURL
USERNAME=[username]
PASSWORD=$BBPASSWORD
REPOURL=${ACTUALURL#*https://}
REPOURL="https://"$USERNAME:$PASSWORD@$REPOURL
git remote add central2 $REPOURL
git config --global user.email [email address]
git config --global user.name $USERNAME
git config --global push.default simple

As you can see I had to even create one with a different name (central2) to prevent having conflicts. And just in case at the end we remove it:

git remote remove central2

The following step is to basically delete all the existing tags related to this plan, as we don’t want any of the previous one, but the last one (just in case). This helps keep the repo cleaner from non-useful tags:

echo deleting previous tags
if [[ ! -z "${PLANKEY// }" ]]
then
    echo finding if there is a tag
    echo tags: `git tag -l --sort=v:refname $PLANKEY* | head -n -1`
    TEMP=`git tag -l --sort=v:refname $PLANKEY* | head -n -1`
    printf "\n"
    if [[ ! -z "${TEMP// }" ]]
    then
    for tag in `git tag -l --sort=v:refname $PLANKEY* | head -n -1`
        do
            git tag -d $tag
            git push central2 :refs/tags/$tag
        done
    fi
fi

And then we create the tag we want and push it back to the repo:

echo creating tag for commit: $REVISION
DATE=`date '+%Y%m%d%H%M%S'`
TAGNAME="$PLANKEY-$DATE"
echo final tag name: $TAGNAME

echo pushing new tag
git tag $TAGNAME -m "$PLANNAME build number $BUILDNUMBER passed build." $REVISION
git push central2 $TAGNAME
git ls-remote --exit-code --tags central2 $BUILDNUMBER
git remote remove central2

And then the changes we did to our script that generates the package dynamically. First we added a new parameter to pass the pattern that the tag names follow for that plan:

# Read command line args
while getopts :l:p:t:v option
do
    case "${option}"
    in
        l) LCOMMIT=${OPTARG};;
        p) PREVRSA=${OPTARG};;
        t) TAGNAME=${OPTARG};;
        v) VERBOSE=1;;
        *) usage;; # Invalid argument
    esac
done

And then we check if the tag exists to use the latest one, if not we still use the commit that was sent as a parameter:

if [[ ! -z "${TAGNAME// }" ]]
then
    echo finding if there is a tag
    # git tag -l 
    echo tags: `git tag -l $TAGNAME*`
    TEMP=`git tag -l $TAGNAME*`
    printf "\n"
    if [[ ! -z "${TEMP// }" ]]
    then
        LASTTAG=`git tag -l --sort=-v:refname $TAGNAME* | head -1`
        echo last tag: $LASTTAG
        printf "\n"
        # git show mylabel --pretty=format:"%H" --quiet
        echo git show $LASTTAG --pretty=format:\"%H\" --quiet
        TAGCOMMIT=`git show $LASTTAG --pretty=format:\"%H\" --quiet | tail -n1`
        echo tag commit found: $TAGCOMMIT
        TAGCOMMIT="${TAGCOMMIT%\"}"
        TAGCOMMIT="${TAGCOMMIT#\"}"
        echo trimming commit: $TAGCOMMIT
    fi
fi

if [[ ! -z "${TAGCOMMIT// }" ]]
then
    echo found tag commit and going to use it
    PREVRSA=$TAGCOMMIT
    echo commit to use: $PREVRSA
fi

The rest of the script remains the same.
Hope this is helpful and let me know in the comments if you are using this or similar solutions!

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google photo

You are commenting using your Google account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s