Hard-Delete Objects Using the Force.com Migration tool

A hidden gem in the Metadata API deploy operation takes a DeployOptions structure, in it is a purgeOnDelete setting that will do what you need!

purgeOnDelete. If true, the deleted components in the destructiveChanges.xml manifest file aren't stored in the Recycle Bin. Instead, they become immediately eligible for deletion. This field is available in API version 22.0 and later. This option only works in Developer Edition or sandbox organizations; it doesn't work in production organizations.

Extending Salesforce Migration Tool to Support PurgeOnDelete.. For some reason this excellent feature has still not been exposed by the Salesforce Migration Tools via the sf:deploy Ant Task. However with a bit of Java skills you can create a new deploy Ant Task by extending the current one, to expose the attribute to your Ant build scripts, for example...

<taskdef name="deploypurge" classname="com.salesforce.ant.SFDCDeployPurge" classpath="../lib/ant-salesforce.jar"/>
<deploypurge purgeOnDelete="true" username="${sf.username}" password="${sf.password}" serverurl="${sf.server}" deployRoot="stage/deploy" singlePackage="true" runAllTests="false" rollbackOnError="false" allowMissingFiles="true" maxPoll="2000" pollWaitMillis="5000"/>

Prebuilt ant-salesforce.jar with deploypurge in it. You can download a modified ant-salesforce.jar here from the FinancialForce.com Developers Github repo (be warned this is API v22.0, though you can use it to deploy code at any version).

Building your own extended ant-salesforce.jar. If you want rebuild a new version, then you need to download this Java class, compile it, unzip the ant-salesforce.jar (rename .jar to .zip), put it in and zip it back up again and your all set!


This is now exposed in the Force.com migration tool by default. You may now use purgeOnDelete="true" in your build.xml to permanently delete your destructive changes. This only works on developer/sandbox orgs, and does not work on production orgs.

Example:

  <sf:deploy purgeOnDelete="true" username="${sf.username}" password="${sf.password}" serverurl="${sf.serverurl}" maxPoll="${sf.maxPoll}" deployRoot="mypkg" rollbackOnError="true"/>