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