DZone
Thanks for visiting DZone today,
Edit Profile
  • Manage Email Subscriptions
  • How to Post to DZone
  • Article Submission Guidelines
Sign Out View Profile
  • Post an Article
  • Manage My Drafts
Over 2 million developers have joined DZone.
Log In / Join
Refcards Trend Reports Events Over 2 million developers have joined DZone. Join Today! Thanks for visiting DZone today,
Edit Profile Manage Email Subscriptions Moderation Admin Console How to Post to DZone Article Submission Guidelines
View Profile
Sign Out
Refcards
Trend Reports
Events
Zones
Culture and Methodologies Agile Career Development Methodologies Team Management
Data Engineering AI/ML Big Data Data Databases IoT
Software Design and Architecture Cloud Architecture Containers Integration Microservices Performance Security
Coding Frameworks Java JavaScript Languages Tools
Testing, Deployment, and Maintenance Deployment DevOps and CI/CD Maintenance Monitoring and Observability Testing, Tools, and Frameworks
Partner Zones AWS Cloud
by AWS Developer Relations
Culture and Methodologies
Agile Career Development Methodologies Team Management
Data Engineering
AI/ML Big Data Data Databases IoT
Software Design and Architecture
Cloud Architecture Containers Integration Microservices Performance Security
Coding
Frameworks Java JavaScript Languages Tools
Testing, Deployment, and Maintenance
Deployment DevOps and CI/CD Maintenance Monitoring and Observability Testing, Tools, and Frameworks
Partner Zones
AWS Cloud
by AWS Developer Relations
  1. DZone
  2. Testing, Deployment, and Maintenance
  3. DevOps and CI/CD
  4. Building Salesforce Using Atlassian Bitbucket Pipelines

Building Salesforce Using Atlassian Bitbucket Pipelines

In this tutorial, we explore using Bitbucket Pipelines, Docker, and Force Migration Tool to deploy Salesforce code, with Zone Leader John Vester.

John Vester user avatar by
John Vester
CORE ·
Apr. 19, 17 · Tutorial
Like (3)
Save
Tweet
Share
81.82K Views

Join the DZone community and get the full member experience.

Join For Free

earlier this year, i wrote an article about atlassian introducing pipelines to the bitbucket git-based repository. back in december 2015, i wrote another article focused on using atlassian bamboo to deploy to salesforce environments . this article will provide a simple example on how to use bitbucket pipelines to deploy to salesforce, leveraging what i presented in the two referenced articles.

setting up bitbucket

the first step is to make sure a repository exists within atlassian bitbucket, containing the salesforce org metadata to be deployed. while it is not possible to deploy every metadata element using the force migration tool, i still prefer to extract all the metadata into the source repository. i feel better knowing that a copy of the design/data is stored and maintained outside the salesforce ecosystem.

as a result, my src structure resembles the following screenshot:

image title

for this project, i am using the following .gitignore :

.project
.settings
.metadata
salesforce.schema
referenced packages
apex-scripts/log
config
localcopy

in addition to the standard package.xml in the src folder, i created a deploypackagecscore.xml file, which is a subset of the package.xml (created by salesforce/force migration tool) and contains a listing of the elements that are being deployed in our environment. as developers introduce new or remove unneeded objects, the changes are reflected in the deploypackagecscore.xml file.

setting up force migration tool

at the root level of the project, a build folder exists and contains the following files:

image title

below is a summary of each of the files shown above:

  • ant-salesforce.jar - force migration tool jar.

  • build.properties - contains static property data (maxpoll, pollwaitmillis and general slack information).

  • build.xml - logic used by apache ant (detailed below).

the build.xml executes the following logic:

  1. paint banner.

  2. paint deployment information.

  3. copy source (optional) and rename deploypackagecscore.xml to package.xml.

  4. delete unmigrateable files.

  5. deploy code.

  6. post to slack.

an example build.xml is shown below:

<project name="retrieve and deploy sfdc metadata" default="testdeployonly" 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 name="slackmessage" value="salesforce%20deployment%20complete!%0a%20-%20commit%20=%20${commit.id}%20%0a%20-%20branch%20=%20${branch.id}%0a%20-%20host%20=%20${sf.serverurl}%0a%20-%20username%20=%20${sf.username}%0a%20-%20testlevel%20=%20*${sf.testlevel}*%0a%20-%20checkonly%20=%20*${sf.checkonly}*"/>

    <target name="deploy" depends="deleteunmigrateablefiles">
      <sf:deploy
          username="${sf.username}" 
          password="${sf.password}" 
          serverurl="${sf.serverurl}"
          testlevel="${sf.testlevel}"
          checkonly="${sf.checkonly}"
          logtype="debugonly"
          deployroot="${basedir}/localcopy"
          pollwaitmillis="${sfdc.pollwaitmillis}"
          maxpoll="${sfdc.maxpoll}" 
          allowmissingfiles="false"
          autoupdatepackage="false"
          rollbackonerror="true"
          ignorewarnings="true"/>
        <antcall target="posttoslack"/>
    </target>

    <target name="posttoslack">
      <exec executable="curl">
        <arg line="-d 'token=${slack.token}&amp;channel=${slack.channel}&amp;text=${slackmessage}&amp;pretty=1' https://slack.com/api/chat.postmessage"/>
      </exec>
    </target>

    <target name="deleteunmigrateablefiles" depends="copysource">
        <echo level="info">removing files that cannot be migrated:</echo>
        <delete dir="${basedir}/localcopy/flows" />
        <delete dir="${basedir}/localcopy/layouts" />
        <delete dir="${basedir}/localcopy/permissionsets" />
        <delete dir="${basedir}/localcopy/profiles" />
        <delete dir="${basedir}/localcopy/quickactions" />
        <delete dir="${basedir}/localcopy/settings" />
        <delete dir="${basedir}/localcopy/workflows" />

        <echo level="info">cleaning up build.xml for references that cannot be migrated:</echo>
        <echo level="info">  - unfiled$public</echo>
        <replaceregexp
            match="^        &lt;members&gt;unfiled\$public&lt;/members&gt;$"
            replace=""
            flags="gm"
            byline="false">
            <fileset
            dir="${basedir}/localcopy"
            includes="**/package.xml"
            />
        </replaceregexp>
    </target>

    <target name="copysource" depends="deployinformation">
      <echo level="info">initializing localcopy folder</echo>
      <delete dir="${basedir}/localcopy" />
      <mkdir dir="${basedir}/localcopy" />
      <echo level="info">copying src to localcopy folder</echo>
      <copy todir="${basedir}/localcopy" >  
        <fileset dir="${basedir}/src" includes="**"/>  
      </copy> 
      <echo level="info">deleting standard package.xml</echo>
      <delete file="${basedir}/localcopy/package.xml" />
      <echo level="info">renaming ${sf.deployfile} to package.xml</echo>
      <move file="${basedir}/localcopy/${sf.deployfile}" tofile="${basedir}/localcopy/package.xml"/>
    </target>

    <target name="deployinformation" depends="banner">
      <echo level="info"> information for this deployment:</echo>
      <echo level="info"> - target host name = ${sf.serverurl}</echo>
      <echo level="info"> - login id = ${sf.username}</echo>
      <echo level="info"> - deployment file = ${sf.deployfile}</echo>
      <echo level="info"> - test only mode = ${sf.checkonly}</echo>
      <echo level="info"> - apex test level = ${sf.testlevel}</echo>
    </target>

    <target name="banner">
      <echo level="info">╔═════════════════════════════════════════════════════╗</echo>
      <echo level="info">║    ____ _                  ____  _       _          ║</echo>
      <echo level="info">║   / ___| | ___  __ _ _ __ / ___|| | __ _| |_ ___    ║</echo>
      <echo level="info">║  | |   | |/ _ \/ _` | '_ \\___ \| |/ _` | __/ _ \   ║</echo>
      <echo level="info">║  | |___| |  __/ (_| | | | |___) | | (_| | ||  __/   ║</echo>
      <echo level="info">║   \____|_|\___|\__,_|_| |_|____/|_|\__,_|\__\___|   ║</echo>
      <echo level="info">║                                                     ║</echo>
      <echo level="info">║    salesforce continuous intergration deployment    ║</echo>
      <echo level="info">║       created by cleanslate technology group        ║</echo>
      <echo level="info">╚═════════════════════════════════════════════════════╝</echo>
    </target>
</project>

the copy source step is optional, but makes things a lot easier when running the force migration tool locally. it does make a copy of the entire source directory so that the files can be manipulated to work while deploying to salesforce. this should not be an issue, since the localcopy folder is in the . gitignore file and pipeline deployments will be pushed via a docker image, which will be discarded after use.

with everything in place, it is possible to execute the deployment by running the following command:

ant -buildfile build/build.xml deploy 
  -dsf.username=enter.username@here.com 
  -dsf.password=passwordhereplussecuritytoken 
  -dsf.serverurl=https://test.salesforce.com 
  -dsf.checkonly=true 
  -dsf.testlevel=notestrun 
  -dsf.deployfile=deploypackagecscore.xml

setting up pipelines

with the force migration tool process working, the pipeline process can automate the deployment. from the settings | pipelines | settings screen within bitbucket, make sure enable pipelines is set to "enabled."

the next step is to configure environment variables used by the pipeline processing. from the settings | pipelines | environment variables screen within bitbucket, i configured the following items:

my personal sandbox:

  • sfdc_jv_check_only - true/false to specify if code will be deployed (true = check only is performed and code is not deployed).

  • sfdc_jv_host_name - host address (https://test.salesforce.com).

  • sfdc_jv_user_id - username to login to salesforce.

  • sfdc_jv_password_token - password + security token (configured as a secured variable within bitbucket).

  • sfdc_jv_test_level - notestrun/runlocaltests/runalltestsinorg.

the process was repeated for sfdc_qa (qa sandbox) and sfdc_prod (production) environment variables.

finally, configure and deploy the bitbucket-pipelines.yml file. a very simple example is displayed below:

# ----- 
image: johnjvester/docker-salesforce

pipelines:
  default:
    - step:
        script:
          - echo "running default script against qa environment (mock deploy, no tests executed)"
          - ant -buildfile build/build.xml deploy -dsf.deployfile=deploypackagecscore.xml -dsf.checkonly=true -dsf.testlevel=notestrun -dsf.username=$sfdc_qa_user_id -dsf.password=$sfdc_qa_password_token -dsf.serverurl=$sfdc_qa_host_name -dcommit.id=$bitbucket_commit -dbranch.id=$bitbucket_branch

  custom: # pipelines that are triggered manually
    sandbox-jv: # john vester's sandbox
      - step: 
          script: 
            - echo "running jv sandbox"
            - ant -buildfile build/build.xml deploy -dsf.deployfile=deploypackagecscore.xml -dsf.checkonly=$sfdc_jv_check_only -dsf.testlevel=$sfdc_jv_test_level -dsf.username=$sfdc_jv_user_id -dsf.password=$sfdc_jv_password_token -dsf.serverurl=$sfdc_jv_host_name -dcommit.id=$bitbucket_commit -dbranch.id=$bitbucket_branch

    qa: # qa full copy sandbox
      - step: 
          script: 
            - echo "running qa (showing banner only and posting to slack)"
            - ant -buildfile build/build.xml banner
            - ant -buildfile build/build.xml posttoslack -dsf.checkonly=true -dsf.testlevel=notestrun -dsf.username=$sfdc_qa_user_id -dsf.serverurl=$sfdc_qa_host_name -dcommit.id=$bitbucket_commit -dbranch.id=$bitbucket_branch

    prod: # production
      - step: 
          script: 
            - echo "running production (showing banner only)"
            - ant -buildfile build/build.xml banner 

this file leverages the johnjvester\docker-salesforce docker image that i have uploaded on dockerhub (feel free to utilize) and completes the following tasks when commits are made to bitbucket:

  1. use/download the johnjvester\docker-salesforce image.

  2. execute the default pipeline:

    1. echo text to the console.

    2. run the deploy use environment variables for the following parameters:

      1. username.

      2. password.

      3. server url.

the bitbucket commit number and branch will be included in the processing as well, to use in the slack message.

the file also includes three custom pipelines, which can be run on demand. in my example, there is an update for my personal sandbox, our qa environment, and production. these could be automatically updated, but we like to control when these sandboxes are updated.

again, this is just a simple example pipeline file.

running the pipeline

with bitbucket pipelines in place, a merge into the master branch yields the following flow:

banner is displayed, with migration information:

image title

deployment information is displayed:

image title

the copy source processing is executed, which also renames the deploypackagecscore.xml to be package.xml:

image title

files that cannot be migrated are removed from localcopy folder:

image title

perform the deployment:

image title

post to slack:

image title

build successful:

image title

additionally, on the bitbucket commit itself, a run pipeline action exists:

image title

which allows the custom deployments to be executed on demand:

image title

conclusion

using a simple docker image that i uploaded to dockerhub and a functional force migration tool implementation, implementing bitbucket pipelines becomes a task which can be implemented without much effort. as a result, it is possible to introduce ci/cd into your salesforce environment, while keeping all the deployment information with the source code - where it truly belongs.

have a really great day!

Pipeline (software)

Opinions expressed by DZone contributors are their own.

Popular on DZone

  • Introduction to Container Orchestration
  • Low-Code Development: The Future of Software Development
  • How To Set Up and Run Cypress Test Cases in CI/CD TeamCity
  • Simulating and Troubleshooting BLOCKED Threads in Kotlin [Video]

Comments

Partner Resources

X

ABOUT US

  • About DZone
  • Send feedback
  • Careers
  • Sitemap

ADVERTISE

  • Advertise with DZone

CONTRIBUTE ON DZONE

  • Article Submission Guidelines
  • Become a Contributor
  • Visit the Writers' Zone

LEGAL

  • Terms of Service
  • Privacy Policy

CONTACT US

  • 600 Park Offices Drive
  • Suite 300
  • Durham, NC 27709
  • support@dzone.com
  • +1 (919) 678-0300

Let's be friends: