Configuring Bamboo for Salesforce Integration
A continuation of Zone Leader John Vester's article series on incorporating Salesforce into your DevOps processes.
Join the DZone community and get the full member experience.
Join For FreeIn my last article, the steps necessary to configure the Force IDE for Eclipse were documented. Once in place, the plug-in was used to pull metadata from the Production Salesforce Org. The extracted code was then checked into Atlassian Stash - which is a git-based repository.
In this article, Altassian Bamboo, Apache Ant and the Force Migration Tool will be used to automate the deployment into Salesforce Org using the code checked into Stash.
Force Migration Tool Configuration
The first step is to configure the Force Migration Tool within Stash. As you may recall, the base folder name for my Salesforce repository is called sfdc. Within the sfdc folder, I created a folder called "build." Place the following files inside the build folder:
ant-salesforce.jar (which is the core code for the Force Migration Tool)
build.properties
build.xml
Additionally, in order to perform deletions as part of the C/I process, create a folder within build called "unDeployCode." Place the following files inside the unDeployCode folder:
destructiveChanges.xml
package.xml
The completed file structure should appear as shown below:
Configuring Files
The next step is to configure some of the files created above. Please note - the text provided below contains sample files, which may not work properly in your Org. They are intended for illustration purposes and I certainly recommend reviewing the Force Migration Tool documentation more before putting these ideas into place.
build.properties
The first file is the build.properties file, which should be configured similar to what is listed below:
sfdc.maxPoll = 100
sfdc.pollWaitMillis = 100000
These values were the default values, which have worked well with my configuration.
build.xml
The build.xml is where the majority of the work will be configured. For this article, I am only going to include a few Ant targets. Otherwise, this article would become quite lengthy.
<project name="Retrieve and Deploy SFDC metadata" default="deployEmptyCheckOnly" basedir=".." xmlns:sf="antlib:com.salesforce">
<taskdef uri="antlib:com.salesforce"
resource="com/salesforce/antlib.xml"
classpath="${basedir}/build/ant-salesforce.jar"/>
<property file="${basedir}/build/build.properties"/>
<property environment="env"/>
<target name="deployEmptyCheckOnly" depends="delete_unmigrateable_files">
<echo level="info">Testing the deploy</echo>
<sf:deploy
checkOnly="true"
logType="Debugonly"
username="${sfdc.username}"
password="${sfdc.password}"
serverurl="${sfdc.serverurl}"
deployRoot="${basedir}/src"
pollWaitMillis="${sfdc.pollWaitMillis}"
maxPoll="${sfdc.maxPoll}"
testLevel="NoTestRun"
allowMissingFiles="false"
autoUpdatePackage="false"
rollbackOnError="true"
ignoreWarnings="true"/>
</target>
<target name="deployCode" depends="delete_unmigrateable_files">
<echo level="info">Performing the deploy (do not run tests)</echo>
<sf:deploy
username="${sfdc.username}"
password="${sfdc.password}"
serverurl="${sfdc.serverurl}"
deployRoot="${basedir}/src"
pollWaitMillis="${sfdc.pollWaitMillis}"
maxPoll="${sfdc.maxPoll}"
allowMissingFiles="false"
autoUpdatePackage="false"
rollbackOnError="true"
ignoreWarnings="true"
testLevel="NoTestRun"
logType="Debugonly"/>
</target>
<target name="undeployCode">
<echo level="info">Undeploying code</echo>
<sf:deploy
username="${sfdc.username}"
password="${sfdc.password}"
serverurl="${sfdc.serverurl}"
maxPoll="${sfdc.maxPoll}"
rollbackOnError="true"
allowMissingFiles="false"
autoUpdatePackage="false"
ignoreWarnings="true"
logType="Debugonly"
purgeOnDelete="true"
zipFile="unDeployCode.zip"/>
</target>
<target name="remove_profile_references">
<echo level="info">Removing references that cannot be migrated from all profiles</echo>
<echo level="info"> - Social Persona</echo>
<replaceregexp
match="^ <tabVisibilities>\n <tab>standard-SocialPersona</tab>\n <visibility>DefaultOff</visibility>\n </tabVisibilities>$"
replace=""
flags="gm"
byline="false">
<fileset
dir="${basedir}/src/profiles"
includes="**/*.profile"
/>
</replaceregexp>
<echo level="info">Removing references that cannot be migrated from all objects</echo>
<echo level="info"> - MapHighlightAction</echo>
<replaceregexp
match="^ <actionOverrides>\n <actionName>MapHighlightAction</actionName>\n <type>Default</type>\n </actionOverrides>$"
replace=""
flags="gm"
byline="false">
<fileset
dir="${basedir}/src/objects"
includes="**/*.object"
/>
</replaceregexp>
<echo level="info">Cleaning up build.xml for references that cannot be migrated</echo>
<echo level="info"> - unfiled$public</echo>
<replaceregexp
match="^ <members>unfiled\$public</members>$"
replace=""
flags="gm"
byline="false">
<fileset
dir="${basedir}/src"
includes="**/*.xml"
/>
</replaceregexp>
</target>
<target name="delete_unmigrateable_files" depends="remove_profile_references">
<echo level="info">Removing files that cannot be migrated</echo>
<echo level="info"> - Applications</echo>
<delete file="${basedir}/src/applications/standard__Work.app"/>
<echo level="info"> - Layouts</echo>
<delete file="${basedir}/src/layouts/FeedItem-Feed Item Layout.layout"/>
<echo level="info"> - Settings</echo>
<delete file="${basedir}/src/settings/PersonalJourney.settings"/>
<echo level="info"> - Workflows</echo>
<delete file="${basedir}/src/workflows/ExternalEventMapping.workflow"/>
</target>
</project>
In the build.xml above, standard Ant project, task definition and property tags were created. There are also four targets, which are noted as follows:
deloyEmptyCheckOnly - is used to sanity check the code being checked in. It depends on "delete_unmigrateable_files" which is documented below.
deployCode - is used to deploy code to a Salesforce Org. It depends on "delete_unmigrateable_files" which is documented below. This will be referenced in my next article.
undeployCode - is used to undeploy code from a Salesforce Org. Since there is not a way to back-out changes successfully deployed, this file comes in handy to automatically remove elements from a target Org. This will be referenced in my next article.
delete_unmigrateable_files - is used to delete files which cannot be migrated. In the example above, the standard__Work.app is removed from the working directory so that the code is not pushed to the target Org. The list of these files depends on your environment, but are mostly found through Google searches and attempting the migrations yourself. It depends on "remove_profile_references" which is documented below.
remove_profile_references - is used to clean up elements from the profiles. This is similar to delete_unmigrateable_file (above) and are the result of either Google searches or trial/error tests within your test deployments.
The build.xml configuration is, by far, the most complicated factor with the deployment. This is because Salesforce isn't always forthcoming with providing the details on what can and cannot be deployed ... or what cannot exist in the deployment files themselves. Fortunately, Apache Ant allows the ability to clean-up or delete files as part of the core process.
Next, the files in the unDeployCode folder are configured.
destructiveChanges.xml
Below, is a sample destructiveChanges.xml file:
<?xml version="1.0" encoding="UTF-8"?>
<Package xmlns="http://soap.sforce.com/2006/04/metadata">
<types>
<members>Account.AllAccounts</members>
<members>Account.NewThisWeek</members>
<name>ListView</name>
</types>
<types>
<members>Account.Classification__c</members>
<name>CustomField</name>
</types>
<types>
<members>ConversionPageController</members>
<members>testConversionPageController</members>
<members>ImportUtils</members>
<name>ApexClass</name>
</types>
<types>
<members>Conversion_Page</members>
<name>ApexPage</name>
</types>
<types>
<members>TriggerWeNoLongerNeed</members>
<name>ApexTrigger</name>
</types>
<types>
<members>Business Design Manager</members>
<name>Profile</name>
</types>
<types>
<members>CampaignMember-Campaign Member Page Layout</members>
<name>Layout</name>
</types>
<types>
<members>unfiled$public/ContactFollowUpSAMPLE</members>
<members>unfiled$public/LeadsNewassignmentnotificationSAMPLE</members>
<members>unfiled$public/LeadsWebtoLeademailresponseSAMPLE</members>
<members>unfiled$public/SUPPORTCaseResponsewithSolutionSAMPLE</members>
<members>unfiled$public/SUPPORTCaseescalationnotificationSAMPLE</members>
<members>unfiled$public/SUPPORTNewassignmentnotificationSAMPLE</members>
<members>unfiled$public/SUPPORTWebtoCaseemailresponseSAMPLE</members>
<name>EmailTemplate</name>
</types>
<version>34.0</version>
</Package>
In the destructiveChanges.xml file, the following items are being removed:
two list views from the Account section
a custom field from the Account object
two classes from our conversion project
one Apex Page from our conversion project
one trigger
one profile
one layout
several of the same Email templates
While it is possible to manually delete elements from your Orgs, you may not have the same ability in the Production instance. Also, elements that are part of the Salesforce default design (like Email templates) may find their way back into your Orgs - which can cause problems if they find their way into Production.
package.xml
Finally, a sample package.xml is shown below:
<?xml version="1.0" encoding="UTF-8"?>
<Package xmlns="http://soap.sforce.com/2006/04/metadata">
<version>34.0</version>
</Package>
Since this is basically an empty package.xml file, I haven't seen a need to change this file ... other than the version attribute.
After finishing the configuration of the files above, the files listed above need to be checked-in to Stash. Once checked-in and merged, you should see your files in your branch.
Creating The Build Process
Atlassian Bamboo will handle the remainder of the steps for the Continuous Integration with Salesforce.
Setting Up Global Variables
While not required, I recommend using Global Variables within Bamboo to avoid hard-coding references in the deployment process. For each target Org, the following Global Variables should be created:
username (sfdc.sandbox_name.username)
password (sfdc.sandbox_name.password)
securitytoken (sfdc.sandbox_name.securitytoken)
serverurl (sfdc.sandbox_name.serverurl)
In the remainder of my examples, I will be using the dev01 Salesforce org. As a result, my Global Variables will appear as shown below:
Creating the Project and Plan
Next, a new project called Salesforce was created in Bamboo. Following our corporate standards, a new plan was created called develop under the Salesforce Project. The plan has the following information:
Other Plan Items
Before we get to the Stages of the plan, we need to make sure the following items are set:
Repositories - should include the Stash repository which includes the Salesforce source code
Triggers - should be set to Stash Repository Triggered to automatically start the process
Creating The Stages
Next, we need to create the stages for the C/I process. This is where the automation begins to take shape. Below, is the configuration for the Run tests stage:
The Requirements tab should include Ant and a JDK (which we are using 1.8).
The Tasks are defined as shown below:
The Source Code Checkout is a standard task, which will pull the code from the target branch in Stash. The Script task is simply echoing a "git status" to the logs. The heart of this process is within the Ant task, which has the following attributes:
Description = Call deployEmptyCheckOnly (run tests only)
Executable = Ant
Build file = build.xml
Target = deployEmptyCheckOnly -Dsfdc.username=${bamboo.sfdc.dev01.username} -Dsfdc.password=${bamboo.sfdc.dev01.password}${bamboo.sfdc.dev01.securitytoken} -Dsfdc.serverurl=${bamboo.sfdc.dev01.serverurl}
Build JDK = Java 1.8
Working Subdirectory = build
The stage will fire the "depoloyEmptyCheckOnly" task in the build.xml file using Apache Ant. Instead of relying on hard-coding values, the Global Variables will be substituted accordingly.
When called and finished properly the screen will display as shown below:
What's Next?
This article provided the instructions necessary with getting the Force Migration Tool files into a Stash repository. A new project and plan were created in Bamboo, with a stage (called Run tests) automatically firing when changes are checked-in.
The next article will take things one-step further, by showing how a Release Candidate can be created. This release candidate becomes a snapshot of the code base, which can be pushed to any configured Salesforce Org.
Have a really great day!
Opinions expressed by DZone contributors are their own.
Trending
-
5 Key Concepts for MQTT Broker in Sparkplug Specification
-
Chaining API Requests With API Gateway
-
Building a Flask Web Application With Docker: A Step-by-Step Guide
-
Writing a Vector Database in a Week in Rust
Comments