If your deployment process involves manual modification of the metadata files between retrieve and deploy steps, it’s time to consider extending your knowledge of Ant. This is critical for Continuous Integration where manual processes are an anathema. With a small amount of Ant knowledge you can delete metadata files, edit and replace/remove content via regex, copy files into the directory structure, invoke Selenium scripts to perform configuration tasks at the UI level (addressing gaps in the metadata API perhaps) and so on and so forth. In short, understanding the potential of Ant is key to delivering build automation.
One exemplar use case for post-retrieve modification is deploying metadata from orgs with Social Contacts enabled – errors can arise as below due inconsistencies in the retrieval of the SocialPost object and related metadata.
SocialPost-Social Post Layout.layout(SocialPost-Social Post Layout):Parent entity failed to deploy
No Layout named SocialPost-Social Post Layout found
In this use case, to get the metadata to deploy we need to remove profile references to the SocialPost layout and then remove the layout file itself. The example build file below shows how this can be achieved. In addition, sandbox email address suffixes are also updated to match the target sandbox – a fairly common deployment issue with sandboxes and workflow alerts, dashboard running users etc.
Build File – Retrieve Org Metadata, Modify & Deploy to Org
[sourcecode language=”xml”]
<project xmlns:sf="antlib:com.salesforce" basedir="." default="deploy_ci" name="org to org">
<property file="build.properties" />
<property environment="env" />
<target name="retrieve_dev" depends="">
<echo message="retrieving metadata to ${metadata.root}" />
<sf:retrieve unpackaged="${metadata.root}/package.xml" retrieveTarget="${metadata.root}" singlePackage="true" serverurl="${dev.sf.org.serverurl}" password="${dev.sf.org.password}" username="${dev.sf.org.username}" />
</target>
<target name="update_email_address_suffixes" depends="retrieve_dev">
<echo message="updating email addresses in ${metadata.root}…" />
<replaceregexp match="${dev.sf.org.suffix}" replace="${ci.sf.org.suffix}" flags="gs" byline="false">
<fileset dir="${metadata.root}" />
</replaceregexp>
</target>
<target name="remove_social_post_from_profiles" depends="update_email_address_suffixes">
<echo message="updating profiles to remove Social-Post references in ${metadata.root}…" />
<replaceregexp match="^ <layoutAssignments>\n <layout>SocialPost-Social Post Layout</layout>\n </layoutAssignments>$" replace="" flags="gm" byline="false">
<fileset dir="${metadata.root}\profiles" includes="**/*.profile" />
</replaceregexp>
</target>
<target name="delete_social_post_files" depends="remove_social_post_from_profiles">
<echo message="deleting Social-Post related files from ${metadata.root}…" />
<delete file="${metadata.root}\workflows\SocialPost.workflow"/>
<delete file="${metadata.root}\layouts\SocialPost-Social Post Layout.layout"/>
</target>
<target name="deploy_ci" depends="delete_social_post_files">
<echo message="deploying modified metadata from ${metadata.root}…" />
<sf:deploy singlePackage="true" serverurl="${ci.sf.org.serverurl}" password="${ci.sf.org.password}" username="${ci.sf.org.username}" maxPoll="360" pollWaitMillis="20000" logType="Debugonly" rollbackOnError="true" runAllTests="${ci.sf.org.forcetests}" checkOnly="${ci.sf.org.checkonly}" deployroot="${metadata.root}">
</sf:deploy>
</target>
</project>
[/sourcecode]