Thursday, February 28, 2013

Using the Migration Tool - Part 2

This post is a continuation of Working Sandbox and Production Using the Force Migration Tool
This post assumes we have made the build.xml file described in that posting.

Using the Ant Force.com Migration Tool:

  • I'll show you how to perform a typical retrieve from Production; deploy to Sandbox; fix; deploy to Sandbox and test; then deploy to Producton. 
  • Then I'll show you how to create a new APEX  Class.
  • Then I'll show you how to create a new APEX Trigger.

Typical workflow to fix something on Production.  

  1. Edit the workArea/package.xml file to specify what classes, triggers or object I need to fix. See this post for more how to do this.
  2. Run the ant tool to retrieve from production.  
    1. ant -v -lib . -f build.xml -Dtarget=prod retrieve
  3. Run the ant tool to deploy to the sandbox.   
    1. ant -v -lib . -f build.xml -Dtarget=test deploy
  4. Assuming the deploy worked it is now time to fix what you need to fix.  Include test cases verify your fix and provide the 75+% test coverage required by Salesforce.  Strive for 90% coverage.
  5. Run the ant tool to deploy to the sandbox.  Test cases will run to validate your fix.
    1. ant -v -lib . -f build.xml -Dtarget=test deploy
  6. Run the ant tool to deploy to production.
    1. ant -v -lib . -f build.xml -Dtarget=prod deploy
Nice and clean.


Here is the complete build file:

<project name="Salesforce Project"  default="work" basedir="." xmlns:sf="antlib:com.salesforce">
<taskdef resource="net/sf/antcontrib/antcontrib.properties"/>

<target name="setup" >
  <!-- on command line include "-DtargetDir=src" to change the target -->
  <property name="targetDir"  value="workarea" />
  <if>
   <equals arg1="${target}" arg2="test" />
   <then>
     <property file="buildTest.properties"/>
     <property name="system"  value="TEST" />
   </then>
   <elseif>
    <equals arg1="${target}" arg2="prod" />
    <then>
     <property file="buildProd.properties"/>
     <property name="system"  value="PROD" />
    </then>
   </elseif>
   <else>
     <echo message="The value of property target is not 'test' or 'prod'" level="error" />
     <fail/>
   </else>
  </if>
</target>

<target name="work"  depends="setup">
   <echo>The ${system} user name is ${sf.username}</echo>
</target>


<!-- Retrieve an unpackaged set of metadata from your org -->
<!-- The file ${targetDir}/package.xml lists what is to be retrieved -->
<target name="retrieve" depends="setup">
   <!-- Retrieve the contents into another directory -->
   <sf:retrieve username="${sf.username}" password="${sf.password}" serverurl="${sf.serverurl}" retrieveTarget="${targetDir}" unpackaged="${targetDir}/package.xml"/>
</target>

<!-- Deploy the unpackaged set of metadata retrieved with retrieveUnpackaged -->
<!-- The file ${targetDir}/package.xml lists what is to be depployed -->
<target name="deploy" depends="setup">
   <sf:deploy username="${sf.username}" password="${sf.password}" serverurl="${sf.serverurl}" deployRoot="${targetDir}"/>
</target>

</project>



Create APEX Class Using Ant

So here is a snippet to include in your build.xml file to generate a blank APEX class.

<!--
Sample: Create an after upsert trigger on object Opportunity
    ant -v -lib . -f build.xml -Dtarget=test -DclassName=Foo -Dtype=after createClass
Results:
Two files:
Foo.cls
Foo.cls-meta.xml

-->
<target name="createClass" depends="setup" >
    <fail unless="className" message="Must provide className (-DclassName=Foo) "/>
    <mkdir dir="${targetDir}/classes"/>
    <echo file="${targetDir}/classes/${className}.cls" append="false">public with sharing class ${className} {
}</echo>
    <copy file="templates/classes.meta.xml"  tofile="${targetDir}/triggers/${className}.cls-meta.xml" />
</target>
 


Before running this create a directory for the templates

~/Documents/salesforce/dev$ mkdir templates/

Then create the file classes.meta.xml  and insert the following:

<?xml version="1.0" encoding="UTF-8"?>
<ApexClasses xmlns="http://soap.sforce.com/2006/04/metadata">
    <apiVersion>26.0</apiVersion>
    <status>Active</status>
</
ApexClasses>


Now when you run ant like this
ant -v -lib . -f build.xml -Dtarget=test -Dobject=Opportunity -Dtype=after createTrigger

you get triggers/OpportunityAfterUpsertTrigger.trigger  with this content

trigger OpportunityAfterUpsertTrigger on Opportunity (after insert, after update) {
}


and you get   triggers/OpportunityAfterUpsertTrigger.trigger-meta.xml  with the necessary meta definition.  When you deploy this new class will be pushed to the server.




Create APEX Trigger Using Ant

I have a simple naming convention for my trigger files.   Object + Before or After + "UpsertTrigger".  For example  OpportunityAfterUpsertTrigger works on Opportunity objects after insert or update.    I generally only build before or after upsert triggers; meaning I generally have triggers like this
trigger OpportunityAfterUpsertTrigger on Opportunity (after insert, after update) {
}

or this
trigger OpportunityBeforeUpsertTrigger on Opportunity (before insert, before update) {
}


Inside these triggers I generally collect the objects that need to be worked on and ship them over to another class to do the work and testing.

So here is a snippet to include in your build.xml file to generate a blank trigger. 

<!--
Sample: Create an after upsert trigger on object Opportunity
    ant -v -lib . -f build.xml -Dtarget=test -Dobject=Opportunity -Dtype=after createTrigger
Results:
Two files:
OpportunityAfterUpsertTrigger.trigger
OpportunityAfterUpsertTrigger.trigger-meta.xml

-->
<target name="createTrigger" depends="setup" >
    <fail unless="object" message="Must provide Object for the trigger"/>
    <fail unless="type" message="Must provide type 'before' or 'after' "/>
    <if>
    <equals arg1="${type}" arg2="before" />
    <then>
            <property name="triggerName" value="${object}BeforeUpsertTrigger" />
    </then>
    <elseif>
    <equals arg1="${type}" arg2="after" />
    <then>
            <property name="triggerName" value="${object}AfterUpsertTrigger" />
    </then>
    </elseif>
    <else>
     <echo message="The value of property type is not 'before' or 'after'" level="error" />
     <fail/>
    </else>
    </if>
    <mkdir dir="${targetDir}/triggers"/>
    <echo file="${targetDir}/triggers/${triggerName}.trigger" append="false">trigger ${triggerName} on ${object}</echo>
    <if> <equals arg1="${type}" arg2="before" />
    <then>
        <echo file="${targetDir}/triggers/${triggerName}.trigger" append="true"> (before insert, before update) {
}</echo>
    </then>
    <elseif> <equals arg1="${type}" arg2="after" />
    <then>
        <echo file="${targetDir}/triggers/${triggerName}.trigger" append="true"> (after insert, after update) {
}</echo>
    </then>
    </elseif>
    </if>
  
    <copy file="templates/trigger.meta.xml"  tofile="${targetDir}/triggers/${triggerName}.trigger-meta.xml" />
 
</target>


Before running this create a directory for the templates

~/Documents/salesforce/dev$ mkdir templates/

Then create the file trigger.meta.xml  and insert the following:

<?xml version="1.0" encoding="UTF-8"?>
<ApexTrigger xmlns="http://soap.sforce.com/2006/04/metadata">
    <apiVersion>26.0</apiVersion>
    <status>Active</status>
</ApexTrigger>



Now when you run ant like this
ant -v -lib . -f build.xml -Dtarget=test -Dobject=Opportunity -Dtype=after createTrigger

you get triggers/OpportunityAfterUpsertTrigger.trigger  with this content

trigger OpportunityAfterUpsertTrigger on Opportunity (after insert, after update) {
}


and you get   triggers/OpportunityAfterUpsertTrigger.trigger-meta.xml  with the necessary meta definition.  When you deploy this new class will be pushed to the server.










Working Sandbox and Production Using the Force Migration Tool


 Easily Switch Between Sandbox and Production  Using the Force Migration Tool

This is a continuation of my previous post Getting Started with Salesforce Ant Migration with Force.com IDE

I'll show you how to create ant build scripts to easily switch between a Sandbox and Production.   I'm using a Mac so Window's users will need to make a few adjustments.

The Ant Migration Tool is significantly faster that using the Force.com IDE.   So, I am beginning to use the command line tool more and the Force.com IDE less.  What I need is an easy way to work with a project and have it quickly switch between a Sandbox and the main Production instance of Salesforce.


At first this is easy enough:  I just need to have two build.properties files. One for the sandbox and the other for production.  Then, keeping it simple, I create two build.xml files that are identical except one uses the sandbox properties and the other uses the production.

Simple, but clearly not a scalable solution.  So, how can I do this better?  By using some form of if-then-else condition processing.   Note, once a property is set in Ant it can not be modified.

Google search locates this answer:
http://stackoverflow.com/questions/5116619/apache-ant-if-then-else-task

Contributors provide two answers.  One uses built in Ant tasks that can do one thing or another.  But, I find that approach difficult to understand and would prefer a more classic "if-then-else" solution.  Further down in the post is another answer that uses a module that needs to be downloaded into your system.   This module provides more than just if-then-else so I feel it is worth the extra effort.

Download, Install and Use the Contrib Module


You can read about what the contrib module provides here
http://ant-contrib.sourceforge.net/tasks/tasks/index.html
Download from here:
http://sourceforge.net/projects/ant-contrib/files/ant-contrib/
Installation instructions from here:
http://ant-contrib.sourceforge.net/


But I'll describe my installation steps here.

OK.  Here is the reward for reading this far!  The download page provides a zip with everything but the jar file.  It took some time to dig our the real download URL that you want.
http://sourceforge.net/projects/ant-contrib/files/ant-contrib/ant-contrib-0.3/
On this page get the bin file ant-contrib-0.3-bin.zip

Unpack the file:

~/Downloads$ ls -l ant-contrib-0.3-bin.zip
-rw-r--r--@ 1 yourRoot  staff    97K 28 Feb 11:00 ant-contrib-0.3-bin.zip
~/Downloads$ mkdir AntContrib
~/Downloads$ mv ant-contrib-0.3-bin.zip AntContrib/
~/Downloads$ cd AntContrib/
~/Downloads/AntContrib$ unzip ant-contrib-0.3-bin.zip


To see the local copy of the API documentation run this:
~/Downloads/AntContrib$ open docs/api/index.html  



The jar file we want is in the lib directory.  Just need to know where to copy it.  With linux systems  you can run 'which' to locate the ant install directory.
~/Downloads/AntContrib$ which ant
/usr/bin/ant







But do a long directory listing and note the directory is actually a link.  After some more exploration I found the actual location to put the jar.  The target directory is owned by the root user so you may need to run the copy command with super user privileges; sudo



~/Downloads/AntContrib$ sudo cp lib/ant-contrib-0.3.jar /usr/share/ant/lib/
Password:
~/Downloads/AntContrib$

Using the If-Then-Else

Now we are ready to use the module.   From the installation notes there is an important first step. In each build file you need to include this:
<taskdef resource="net/sf/antcontrib/antcontrib.properties"/>


Here is a sample file that illustrates the if-then-else

<project name="Try If">
<taskdef resource="net/sf/antcontrib/antcontrib.properties"/>
<if>
 <equals arg1="${foo}" arg2="bar" />
 <then>
   <echo message="The value of property foo is 'bar'" />
 </then>
 <elseif>
  <equals arg1="${foo}" arg2="foo" />
  <then>
   <echo message="The value of property foo is 'foo'" />
  </then>
 </elseif>
 <else>
   <echo message="The value of property foo is not 'foo' or 'bar'" />
 </else>
</if>
</project>


Save the above into a file and call it testif.xml. Invoke ant without a define and with the foo property defined. See the results

~/Documents/salesforce/dev$ ant -f testif.xml
Buildfile: /Users/yourRoot/Documents/salesforce/dev/testif.xml
     [echo] The value of property foo is not bar
     [echo] The value of property foo is not 'foo' or 'bar'

BUILD SUCCESSFUL
Total time: 0 seconds
~/Documents/salesforce/dev$ ant -f testif.xml -Dfoo=bar
Buildfile: /Users/yourRoot/Documents/salesforce/dev/testif.xml
     [echo] The value of property foo is bar
     [echo] The value of property foo is 'bar'

BUILD SUCCESSFUL
Total time: 0 seconds


Using If-Then-Else to Select Test or Prod Properties

Create two build properties files. One for Test and another for Prod.  We will use the if-then-else to select the right one based on the command line.  Put the following into a file called build.xml in your root project directory.


<project name="Salesforce Project"  default="work" basedir="." xmlns:sf="antlib:com.salesforce">
<taskdef resource="net/sf/antcontrib/antcontrib.properties"/>

<target name="setup" >
<if>
 <equals arg1="${target}" arg2="test" />
 <then>
   <property file="buildTest.properties"/>
   <property name="system"  value="TEST" />
 </then>
 <elseif>
  <equals arg1="${target}" arg2="prod" />
  <then>
   <property file="buildProd.properties"/>
   <property name="system"  value="PROD" />
  </then>
 </elseif>
 <else>
   <echo message="The value of property target is not 'test' or 'prod'" level="error" />
   <fail/>
 </else>
</if>
</target>

<target name="work"  depends="setup">
   <echo>The ${system} user name is ${sf.username}</echo>
</target>

</project>



Run it with and without the 'target' property

~/Documents/salesforce/dev$ ant -f build.xml
Buildfile: /Users/yourRoot/Documents/salesforce/dev/
build.xml

setup:
     [echo] The value of property target is not 'test' or 'prod'

BUILD FAILED
/Users/
yourRoot/Documents/salesforce/dev/build.xml:20: No message

Total time: 0 seconds
~/Documents/salesforce/dev$ ant -f
build.xml -Dtarget=test
Buildfile: /Users/
yourRoot/Documents/salesforce/dev/build.xml

setup:

work:
     [echo] The TEST user name is myName@myDomain.com.test

BUILD SUCCESSFUL
Total time: 0 seconds



We can now put the real work into the 'work' target and easily switch between Test and Producton




Wednesday, February 20, 2013

Getting Started with Salesforce Ant Migration with Force.com IDE

I've been using the Force.com IDE for years.  As mentioned in previous posts I found that the time to deploy got really long. Then at Dreamforce 2011 I learned to make smaller projects and this really helped.   But recently, I have found deployment time to be excessive (30 to 60 minutes).  Thanks to the folks who post comments on salesforce.stackoverflow.com!  I love the stackoverflow communities.  This post described my experience and one suggestion is to use the Force.com Ant Migration Tool.


As some folks did remark in the post using a command line tool can be a learning curve that we might not have time for.   The following are my steps to use this tool on my Force.com IDE projects.  These steps are mainly about how to adapt the tool to work within the Force.com IDE file structure and to even use and edit it inside the IDE.

Get the Migration Tool

Get the tool via the Salesforce UI under Settings ... Develop ... Tools.  Download the zip file and unpack it.  Open and read the Readme.html.

My system is a Mac 10.6 so I have Java and Ant installed.  Getting these is not hard and the documentation provides the instructions.

Let's assume our IDE workspace is located
~/Documents/salesforce/workspace
. In here are a number of projects. For example, TestMini, TestFull, ProdMini, ProdFull. The Test project are for developing on a sandbox and the Prod project are for Production. The Mini projects hold just the classes, triggers, etc that I need for the current work. The Full projects contain everything. I do not use these for any development. Instead they are a full copy of all the classes, etc that I can use to check into a source control program.


For this discussion I will do everything in
~/Documents/salesforce/workspace/ProdMini  
and call it ProdMini   for short.  All Force.com IDE projects have a subdirectory called "src". In here you will find a package.xml file and subdirectories like classes.  All you really need at this point is the package.xml file. More on this file later.

Setup Ant in the workspace

Copy  ant-salesforce.jar, build.xml, build.properties  to ProdMini. Edit the build.properties and adjust the user name and password (append your token). Next edit the sample build.xml and make these small adjustments. Locate the retrieveUnpackaged section

<target name="retrieveUnpackaged">
<mkdir dir="retrieveUnpackaged"/>
<!-- Retrieve the contents into another directory -->
<sf:retrieve username="${sf.username}" password="${sf.password}" serverurl="${sf.serverurl}" retrieveTarget="retrieveUnpackaged" unpackaged="unpackaged/package.xml"/>
</target>
change
 retrieveTarget="retrieveUnpackaged" unpackaged="unpackaged/package.xml"
to
retrieveTarget="src" unpackaged="src/package.xml"
This sets up the download from Production to your project of anything contained in the package.xml file. Here is what this file looks like:

<?xml version="1.0" encoding="UTF-8"?>
<Package xmlns="http://soap.sforce.com/2006/04/metadata">
    <types>
        <members>OpportunityUtilities</members>
        <name>ApexClass</name>
    </types>
    <types>
        <members>OpportunityUpsertTrigger</members>
        <name>ApexTrigger</name>
    </types>
    <version>26.0</version>
</Package>

The version number depends on the version of ant tool and Force.com IDE.  I don't know what happens if these are not in sync.  So update the Force.com IDE and get the latest Migration Tool.

My project has one Apex class and one trigger.  See my early post about opportunities

Download from the server

Ready to download the files from the server.    From the command prompt and after changing directory to get to ProdMini run this:
$ ant -v -lib . retrieveUnpackaged

The -v shows everything that happens. It is optional but helps you understand what is going on.   The "-lib ." says to include the jar file located in this directory.  Then retrieveUnpackaged
tells Ant to run the contents of that section of the ant file.  The result is a download of the contents in the package.  

Deploy a code change

Next make some small change to the class. For example, add a comment.  The new normal with any change like this, for me, is a 30 to 40 minute hurry up and wait when I deploy it to production.  I am going to see what happens now.

Also make one more edit in the build.xml file.  Locate the deployUnpackaged section
<target name="deployUnpackaged">
      <sf:deploy username="${sf.username}" password="${sf.password}" serverurl="${sf.serverurl}" deployRoot="src"/>
    </target>


Notice that I have changed deployRoot to src
 
Now run the ant tool but this time with the deployUnpackaged target  ant -v -lib . deployUnpackaged

It runs a lot quicker that using the Force.com IDE.   This strongly suggests there is a problem with the IDE.

By using the -v flag I get to see all the tests that run. Interesting information.  The deploy might be faster without this flag.

Next: how to use the Ant tool from within Force.com IDE

Return to the Force.com IDE and selection the Production project. Right click and refresh.  Notice the build.xml etc files are listed (if you have no filters).  Click on the external tools button in the top.
  1. First select the build.xml file.  
  2. Then select the External Tools Configurations...  


  1. Select Ant Build and 
  2. Then press the New button







 
  1. Rename the configuration to Retrieve.  
  2. Select the Targets tab and 
    1. unselect the default and 
    2. select retrieveUnpacked (same as we did above on the command line).  
  3. Press Run to save. (this will fail ...)



Yes, the above fails because the ant-salesforce.jar is not on the classpath.
The Force.com IDE console will suggest where you can place the ant-salesforce.jar file. Or fiddle with the Classpath tab in the external tool configuration.








Repeat the above and create a second external tool configuration. This time name it deploy and select the deployUnpackaged target.