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