Migrating APEX via ANT


Recently I was tasked with migrating approximately 550,000 lines of APEX code from a Salesforce Sandbox to a Production Org.  Not a small task.  Development on the code base had started about 18 months ago and was full of fun business logic instituted via APEX and regular Validation Rules.  Not necessarily a “fun” package to deploy via the common UI tools.

This push consisted of hundreds of classes, pages, triggers, static resources, page layouts, profiles, tabs, and custom links.  Absolutely doable for Eclipse and Change Sets, but because we didn’t have two weeks to build the Change Set and didn’t want to continually re-select the components via Eclipse, I set out to see how native ANT would fit the need.

Typically, the code pushes I do consist of deploying via Eclipse or more recently taking the time to build a Change Set, but for reasons I won’t get into deep detail here, I also needed to push things in separate distinct “batches”.

Situation:  There were seven distinct “batches” of changes to migrate to Prod, each consisting of 10-500 “things”.  I wanted to be able to just specify what “things” I wanted in each step and execute one big batch file to shove it all in.  Here is what I did:

1.  Install ANT on your machine (Salesforce link)

2.  Create a fresh directory in your ANT install directory (I install things to C:\Projects…so this was C:\Projects\ANTSalesforce\<Project Name> Deploy)

3.  Expand your “build.properties” file to include both your SBX and PROD credentials:

 # build.properties
 #
 # SBX
 sf.usernameSBX = <your SBX username>
 sf.passwordSBX = <your SBX password>
 sf.serverurlSBX = https://test.salesforce.com
 #
 # PROD
 sf.usernamePROD = <your PROD username>
 sf.passwordPROD = <your PROD password>
 sf.serverurlPROD = https://www.salesforce.com
 #
 # (you could expand this file for other environments as well...)

4.  Once you have that done, I modified my build.xml to specify the packages that will be processed in the overall batch:

<project name="ANT Deployment Batcher" default="test" basedir="." xmlns:sf="antlib:com.salesforce">
 <property file="build.properties" />
 <property environment="env" />
<target name="retrieveStep1">
 <mkdir dir="Step1">
 <sf:retrieve
 username="${sf.usernameSBX}"
 password="${sf.passwordSBX}"
 serverurl="${sf.serverurlSBX}"
 retrieveTarget="Step1"
 unpackaged="unpackaged/1fromSandboxBaseObjects.xml"
 pollWaitMillis="10000" maxPoll="100" />
</target>
<target name="retrieveStep2">
 <mkdir dir="Step2">
 <sf:retrieve
 username="${sf.usernameSBX}"
 password="${sf.passwordSBX}"
 serverurl="${sf.serverurlSBX}"
 retrieveTarget="Step2"
 unpackaged="unpackaged/2fromSandboxClassesAndPages.xml"
 pollWaitMillis="10000" maxPoll="100" />
</target>
(others here, but truncated...you get the point)
<target name="deployStep1">
 <sf:deploy
 username="${sf.usernamePROD}"
 password="${sf.passwordPROD}"
 serverurl="${sf.serverurlPROD}"
 deployroot="Step1"
 checkonly="false"
 pollWaitMillis="10000" maxPoll="100" />
</target>
<target name="deployStep2">
 <sf:deploy
 username="${sf.usernamePROD}"
 password="${sf.passwordPROD}"
 serverurl="${sf.serverurlPROD}"
 deployroot="Step2"
 checkonly="false"
 pollWaitMillis="10000" maxPoll="100" />
</target>
(others here, but truncated...you get the point) 
</project>

5.  Define your package files (again, samples…rinse and repeat for additional files / steps)
(1fromSandboxBaseObjects.xml)

<?xml version="1.0" encoding="UTF-8"?>
<Package xmlns="http://soap.sforce.com/2006/04/metadata">
    <types>
	<members>Custom_Object_1__c</members>
	<members>Custom_Object_2__c</members>
	<members>Activity</members>
	<members>Task</members>
	<members>Event</members>
	<name>CustomObject</name>
    </types>
    <version>22.0</version>
</Package>

(2fromSandboxClassesAndPages.xml)

<?xml version="1.0" encoding="UTF-8"?>
<Package xmlns="http://soap.sforce.com/2006/04/metadata">
    <types>
	<members>*</members>
	<name>ApexClass</name>
    </types>
    <types>
	<members>*</members>
	<name>ApexPage</name>
    </types>
    <types>
        <members>*</members>
        <name>ApexPage</name>
    </types>
    <types>
	<members>*</members>
	<name>CustomPageWebLink</name>
    </types>
   <version>22.0</version>
</Package>

6.  Build your batch file

@ECHO OFF
ECHO THIS IS THE BATCH DEPLOYMENT SCRIPT.  PRESS ANY BUTTON TO START!
PAUSE
ECHO DELETING ANY PREEXISTING MIGRATION DIRECTORIES
RMDIR /S /Q Step1
RMDIR /S /Q Step2
DEL *.txt /Y
ECHO RETRIEVING CODE FROM SANDBOX
CALL ANT retrieveStep1 >Step1_Retrieve_Log.txt
CALL ANT retrieveStep2 >Step2_Retrieve_Log.txt

ECHO DEPLOYING CODE TO PRODUCTION
CALL ANT deployStep1 >Step1_Deploy_Log.txt
ECHO SUCCESSFUL BUILD?  PRESS ANY KEY TO CONTINUE TO NEXT STEP
PAUSE
CALL ANT deployStep2 >Step2_Deploy_Log.txt
ECHO SUCCESSFUL BUILD?  PRESS ANY KEY TO CONTINUE TO NEXT STEP
PAUSE

That’s really all there is to it.  Took me a little to work out the kinks, but it works slick!  The biggest hurdle I had to deal with was in the batch file…to Windows, ANT works like another batch file – and natively, batch files can call other batch files, but they don’t wait for them to finish.  The DOS “CALL” command calls the ANT “batch” script but WAITS until it’s complete before continuing.  Finally, I directed the output to some local TXT files so I could go back and deal with any errors that came out of an unsuccessful deployment.

Advertisements

5 thoughts on “Migrating APEX via ANT

  1. Hi Andrew

    As you have already gone through this :-). What is the recommended sequence. In my app I have

    1. Custom objects
    2. Custom settings
    3. Components
    4. Triggers
    5. Classes
    6. VF Pages
    7. Standard objects

    Do you suggest I make it in 7 step and in sequence as listed above ?

  2. Hi Andrew,

    I followed the steps above. But while running the batch file, Build gets failed and I get a Error Text created as – Buildfile: build.xml does not exist!

    I have specified the Build.xml file and Build.properties file. Please advise !

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 )

Twitter picture

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

Facebook photo

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

Google+ photo

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

Connecting to %s