I am familiar with how to use the destructiveChanges.xml file to remove components from my org. But it seems like I have to deploy then delete or vice versa. Is there a way to set up a package so that it will deploy and remove in a single ant command?
[SalesForce] How to deploy and remove components in one step with the ant migration tool
Related Solutions
Edit: Summer '15 made this "all better". If you upgrade your ant-salesforce.jar to one that supports API version 34, you can now pass a "testLevel=RunLocalTests" attribute to sf:deploy like so:
<sf:deploy
testLevel="RunLocalTests"
username="${sf.username}"
password="${sf.password}"
serverUrl="${sf.server}"
deployRoot="src"
/>
From the docs:
As part of this change, the runAllTests deployment option is now replaced with testLevel. You can choose which tests to run in a deployment by setting the desired test level. For a description of all test levels, see test levels for the deploy() call. In particular, to run a subset of tests in a deployment, set testLevel to the RunSpecifiedTests value and specify the tests to run in the runTests option.
And
RunLocalTests—All tests in your organization are run, except the ones that originate from installed managed packages. This test level is the default for production deployments that include Apex classes or triggers.
This was the workaround for pre-Summer '15 for posterity:
Because I killed off the better part of a day hacking around in ant to accomplish the workaround @pepefloyd suggested, I wanted to publish a working example; In my case, the test classes are prefixed with "Test_" but you can fool around with the fileset as necessary
This is tested against v30 of the Force.com ant migration tool
<target name="test">
<sfCompileAndTestUnmanaged checkOnly="true" username="${sf.username}" password="${sf.password}" server="${sf.server}">
<fileset dir="src/classes">
<include name="**/Test_*.cls"/>
</fileset>
</sfCompileAndTestUnmanaged>
</target>
<scriptdef name="sfCompileAndTestUnmanaged" language="javascript">
<attribute name="checkonly"/>
<attribute name="username"/>
<attribute name="password"/>
<attribute name="server"/>
<attribute name="trace"/>
<element name="fileset" type="fileset"/>
<![CDATA[
var filesets = elements.get("fileset");
var filesetsIterator = filesets.iterator();
var projectClasses = [];
while(filesetsIterator.hasNext()){
var fs = filesetsIterator.next();
var iter = fs.iterator();
while(iter.hasNext()){
var resource = iter.next();
var clazz = resource.getName().replace(".cls","");
self.log("CLASS: " + clazz);
projectClasses.push(clazz);
}
}
var task = project.createTask("antlib:com.salesforce:compileAndTest");
task.setCheckonly(attributes.get("checkonly") == 'true');
task.setUsername(attributes.get("username"));
task.setPassword(attributes.get("password"));
task.setServer(attributes.get("server"));
task.setTrace(attributes.get("trace") == 'true');
//I tried 'importPackage' and conventional instantiation but couldn't get the inner class to instantiate; this works though
var testsElement = task.getClass().getClassLoader().loadClass("com.salesforce.ant.CompileAndTest$RunTestsElement").newInstance();
task.addRunTests(testsElement);
var classClazz = task.getClass().getClassLoader().loadClass("com.salesforce.ant.CompileAndTest$CodeNameElement");
for(i in projectClasses){
var clazz = classClazz.newInstance();
clazz.addText(projectClasses[i]);
testsElement.addClass(clazz);
}
task.execute();
]]>
</scriptdef>
Alternatively, we've actually used a version of 'deploy' that does a no-op deploy (the package.xml in the 'ant' directory is empty except for the <version>
element. This gives us incremental updates as the tests are running and allows the deployment of the source files to succeed in a separate ant target (not described below) while the tests may fail (insufficient code coverage, failing assertions, etc.)
<target name="test">
<sfDeployUnmanaged purgeOnDelete="true" ignoreWarnings="true" username="${sf.username}" password="${sf.password}" serverUrl="${sf.server}" deployRoot="ant" maxPoll="75">
<fileset dir="src/classes">
<include name="**/*.cls"/>
</fileset>
</sfDeployUnmanaged>
</target>
<scriptdef name="sfDeployUnmanaged" language="javascript">
<attribute name="purgeondelete"/>
<attribute name="ignorewarnings"/>
<attribute name="username"/>
<attribute name="password"/>
<attribute name="serverurl"/>
<attribute name="deployroot"/>
<attribute name="maxpoll"/>
<attribute name="trace"/>
<element name="fileset" type="fileset"/>
<![CDATA[
var filesets = elements.get("fileset");
var filesetsIterator = filesets.iterator();
var projectClasses = [];
while(filesetsIterator.hasNext()){
var fs = filesetsIterator.next();
var iter = fs.iterator();
while(iter.hasNext()){
var resource = iter.next();
var clazz = resource.getName().replace(".cls","");
self.log("CLASS: " + clazz);
projectClasses.push(clazz);
}
}
var task = project.createTask("antlib:com.salesforce:deploy");
task.setPurgeOnDelete(attributes.get("purgeondelete") == 'true');
task.setIgnoreWarnings(attributes.get("ignorewarnings") == 'true');
task.setUsername(attributes.get("username"));
task.setPassword(attributes.get("password"));
task.setServerURL(attributes.get("serverurl"));
task.setDeployRoot(attributes.get("deployroot"));
task.setMaxPoll(attributes.get("maxpoll"));
task.setTrace(attributes.get("trace") == 'true');
//Blows up when build timeout is reached if we don't set this (it uses this value when formatting the exception it throws)
task.setOwningTarget(self.owningTarget);
var classClazz = task.getClass().getClassLoader().loadClass("com.salesforce.ant.DeployTask$CodeNameElement");
for(i in projectClasses){
var clazz = classClazz.newInstance();
clazz.addText(projectClasses[i]);
task.addRunTest(clazz);
}
task.execute();
]]>
</scriptdef>
One possible solution is to use Packages as a container for your code. This allows you to retrieve only the code in your package from an org which ignores all other metadata in the org such as managed package fields, standard Salesforce fields, and any customer created fields.
To test it out, go to Setup->Create->Packages in your source org. Create an unmanaged package and use the Add Components button to add all the metadata you want to bundle in your package. Then, retrieve all metadata from the package into a folder. You'll wind up with a folder containing all the metadata and a package.xml file formatted to allow deployment into a Package in your target orgs.
The key difference in the package.xml is the inclusion of the element which flips on the deploy to package functionality.
The one catch is the target org will need to have a package with the same name already created before the deployment but this is a one time manual step per target org.
I just wrote up the build.xml targets in a different answer you can reference here: https://salesforce.stackexchange.com/a/31437/4832
There are, however, some drawbacks. Some metadata types won't deploy this way. For example, ActionOverrides don't work this way as they can't be bundled in a package. As a workaround, you can create a normal bundle of metadata for use only in deploying the types you can't deploy as part of a package.
Best Answer
It actually is possible to do both together. You just need to put metadata component additions and updates into the package.xml, and component removals should be included in destructiveChanges.xml.
Then you can do a single deploy and it will work as expected.
I've put together a working example here: https://github.com/alan-morey/example-deploy-remove-force-components