Salesforce DX

This post provides a practioner’s introduction to Salesforce DX (Developer Experience) a new software development lifecycle paradigm for Salesforce. Please note at the time of writing (Spring ’17 – v40.0) Salesforce DX is at public beta status.

Salesforce DX (or SFDX) – What is it?

A new set of tools and practices to accelerate the software development lifecycle on the Salesforce platform. The tools in question enable established (technology agnostic) software development models to be applied to the Salesforce context efficiently; the practices provide guidance in how to do this effectively and at scale.
Many aspects of Salesforce DX have become standard practice over the last few years; source-driven development using tools such as Jenkins and git being a good example. Other aspects such as artifact-based development, the CLI and Scratch Orgs introduce a fundamentally new paradigm.

The Salesforce development model has historically been org-centric in concept; with the entire set of metadata types representing a single monolithic code-base. This conceptual model has benefits in respect to ensuring complex interdependencies are not overlooked and code structure and conventions are considered fully in a standardised approach. In the org-centric model – and typically not exclusively – a source-code control repository is mapped at the org-level and deployments are org-based not feature or app based. Salesforce DX provides the tools and practices to change the development model from org-centric to artifact-based; where an artifact represents a collection of components (org subset) – developed, tested and deployed together in safe isolation.

SFDX Tools

CLI (Command-line Interface – sfdx)

The new SFDX CLI provides the foundation for Salesforce DX with capabilities that span authorisation, org management, metadata creation, data population, packaging and deployment. The CLI commands are extensive and clearly structured within the top-level force namespace.

command examples:

//CLI create a new project (specific DX folder structure plus config file)
sfdx force:project:create 

//CLI needs to authorise against the DevHub (OAuth web-based flow or for CI JWT Bearer flow)
sfdx force:auth:web:login -d -a "Hub Org"

//CLI create scratch org (or set existing as default)
sfdx force:org:create -s -f config/project-scratch-def.json

//CLI push source code from local (respects the .forceignore file placed in root folder)
//Conflicts can be overwritten --forceoverwrite
//Note - Local project is the source of truth.
sfdx force:source:push

//CLI run unit tests
sfdx force:apex:test:run

A key concept is the local project which is the primary source of truth. The project structure is different from the Metadata API folder structure with higher-level metadata types such as Object broken down into sub-folders and individual files for sub-types such as listViews and fields. This new sfdx structure (or source shape) is designed for development and source-code control system interactions whereas the longstanding Metadata API structure related to deployment primarily. Conversion between the 2 formats is necessary when creating a new sfdx project from an existing org (or simply from existing metadata) and prior to deploying a sfdx project to a Salesforce org (not Scratch Orgs). The CLI provides commands to perform the format changes as illustrated below.

//Create DX project > Create folder for metadata files then ..

// Retrieve unpackaged metadata
sfdx force:mdapi:retrieve -r ./mdapipkg -u <username> -k ./package.xml

// Retrieve the metadata for a package
sfdx force:mdapi:retrieve -s -r ./mdapipkg -u <username> -p <package name>

// Convert to SFDX format.
sfdx force:mdapi:convert --rootdir <retrieve folder name>

// Convert to Metadata API format.
sfdx force:source:convert -d <metadata folder name>/ --packagename package_name
Scratch Orgs

A Scratch Org is a temporary org that can be created quickly and configured with predefined settings and preferences (Edition, languages, platform features etc.) via a JSON definition file. Scratch Orgs are easily created and deleted via the CLI; up to 50 can be created per-day (per Dev Hub) with up to 25 active concurrently, deletion occurs automatically after 7 days.

A Scratch Org is typically used for development and testing purposes and plays an enabling role in build automation. Note, unlike sandboxes, Scratch Orgs are not constrained by a refresh cycle, but also are not a clone of a source org configuration. A typical development workflow will be to create a short-lived feature branch in the Source Code Control system (SCC), create a local sfdx project (from the branch metadata) and Scratch Org, complete development and testing in the Scratch Org, commit change back to SCC and convert and deploy the change to Sandbox. In a Continuous Integration (CI) environment the CI server (Travis CI, Jenkins etc.) may initiate an automated build from the commit event. In such a scenario, headless Scratch Orgs may be used for automated testing (across editions, languages etc.).

The SFDX CLI provides commands for automating the population of data in a Scratch Org. This can be via CSV file loading or more interestingly via org-to-org copy via SOQL query export and subsequent import.

Finally, a key feature of the CLI is the automated tracking of change (the diff) between the current local project and the default Scratch Org. The CLI push and pull commands that facilitate the movement of change between the local project and the Scratch Org automatically process changed components only.

Dev Hub

The SFDX CLI must authorise to a Dev Hub before Scratch Orgs can be created; each Scratch Org is related to a parent Dev Hub. The Dev Hub is a production Salesforce org (Enterprise Edition or Unlimited Edition) – the feature can be activated from the Setup menu. A Dev Hub trial org can be created to enable safe experimentation with SFDX. Dev Hub trial orgs expire after 30 days and are limited (40 Scratch Orgs created per day, 20 active concurrently) why this is subtly different from the standard limits is unclear.

Authorisation of the Dev Hub org (and indeed any org) by the SFDX CLI is possible via OAuth Web Flow or JWT-based Flow. The latter being preferred for system authentications such as those conducted by a CI server.

For ISV (and others) working with namespaces, the Dev Hub provides a Namespace Registry capability which (as the name suggests) allows Developer Editions containing namespaces to be registered. Once the org link is complete, Scratch Orgs can be created as normal. This capability is great for ISV developing in a namespace registered org (as opposed to utilising a separate packaging org to apply the namespace).

IDE2

The new Force.com IDE2 is an Eclipse plug-in built on the SFDX CLI. Additional features of the IDE2 include the Lightning Code Analyser and Apex Code Analysis tools. The former wraps the capabilities of ESLint and the Salesforce Lightning CLI to evaluate JavaScript code against standard or custom rule sets. The latter covers syntax error detection and highlighting plus code auto-completion. The IDE2 also provides an embedded browser for editing Lightning Pages.

On initial inspection there didn’t appear to be any means to directly login/import metadata from an existing org. Presumably this must be done externally via the SFDX CLI.

Finally, the new IDE2 provides integrated tools for working with Git repositories, a key facet of the envisaged developer workflows centred around Salesforce DX.

Summary

At the time of writing, the available SFDX documentation relates primarily to the technical aspects of the tools, there is minimal information relating to process guidance (or practices). Whilst it’s relatively easy to envisage how the new tools and techniques could be integrated into development, test and deployment workflows, best-practice guidance in such areas would certainly provide confidence and accelerate adoption.

Historically, a key sticking point for source-based development (and deployment in general) in the Salesforce context has been the Metadata API coverage. In short, not all metadata types are supported and therefore automated deployments (whether Change Set or API based) are typically accompanied by pre- and post- deployment steps that manually address the gaps. As there’s no significant change in this regard, it’s likely that Scratch Orgs will require the same approach – however considering Scratch Org use cases will typically be code related this may have limited actual impact.

In general however Salesforce DX is a considerable step forward in terms of applying established software development lifecycle practices to the Salesforce context. The CLI alone is incredibly impressive and provides significant flexibility in respect to development, testing and release process. It will be interesting to see how tools vendors react to the availability of the CLI as a foundation for future, evolved IDE capabilities. Hopefully Salesforce DX represents the first step of many in this direction.

Resources

Salesforce DX Developer Guide (Beta)
Salesforce DX on Trailhead
Salesforce DX Chatter Group

Salesforce Source Control and Release Process

This post outlines my preferred approach to managing parallel developments on the Salesforce platform in what I refer to as the Converged Programme Model. I readily acknowledge that there’s a multitude of ways to accomplish this each with it’s own subjective merits. Before adopting a parallel work-stream model take the time to understand the technical complexity, process overhead and time investment required. Of particular concern should be the team’s readiness for such a disruptive change. In my experience it’s better to plug any skills gaps upfront, be very prescriptive with process guidance, start-small and build out incrementally – the risk otherwise is considerable. Typically resistance will come from individuals unaccustomed to a disciplined approach to software development/release process.

SCC

Objectives

  1. Concurrent Development. Support parallel programme workstreams converging into a shared production Salesforce environment.
  2. Automation. Deliver build automation – reducing the manual overhead required to deploy between environments.
  3. Gold Standard. Deliver a best practice approach – the initial design should scale up and down in response to changing programme conditions.
  4. Non-disruptive. Facilitate a staggered approach to adoption – enabling key benefits to be realised quickly without disrupting productivity.
  5. Minimise Release Overhead. Project branches should be regularly and incrementally updated from the master branch – reducing the inherent risk of divergence over time.

Tools

  1. GitHub
  2. – Get started with public repositories, upgrade to a paid plan and use private repositories for any source code you don’t want to share with the world at large.

    – Create an Organisation account to enable Team functionality.

    – Key benefit versus Subversion (CVS etc.) is fast and efficient branch management; parallel workstreams are managed on branches with frequent merging.

    – It is possible, albeit time expensive to implement a Git server within the enterprise. In my view the GitHub administration interface alone is worth the price.

  3. Jenkins
  4. – Deployed on a Windows EC2 instance with an elastic IP. A free usage tier, micro instance provides an ideal server host. Using a Linux host can be beneficial in regard to SSH authentication from Jenkins to GitHub. This is just one advantage of many. Pick the Operating System/Platform the team you’re working with are most familiar, a Linux host that only one team member can administrate makes no sense.

    – On Windows the Jenkins service should be configured to run a specific user account (with least privileges assigned). This is required to generate the key files for SSH authentication.

    – Enable Jenkins security. Particularly relevant if the host is open to the public web. Lock the inbound IP ranges via the EC2 security group if possible.

    – Either store the Ant build files (build.xml, build.properties) in the Git repository or use an XCOPY post-build step to copy the files into the workspace from a file system location – as below. I prefer to keep the build files external to Git – there shouldn’t be any need to version manage such files – plus the build.properties file may contain passwords in plaintext.

    Jenkins Job Build Config

    – Install GitHub and Git Plugins
    Required to build from a GitHub repository and enables build automation via Post-Receive Hooks. Under Jenkins System Configuration; configure “Manually manage hook URLs”, this requires your GitHub repository to have the hook set manually via Service Hooks under repository settings. Add a [Jenkins (GitHub plugin)] service hook like http://yourservername:8080/github-webhook/. The message sent on git-push to the remote repository will trigger any Jenkins job that builds from the branch that has been updated and has the [Build when a change is pushed to GitHub] option set to true.

    – SSH Keys
    In order to use SSH from Jenkins to a private GitHub repository, SSH authentication is required, which uses a generated key pair. The public key is added as a Deploy Key in GitHub under repository settings. This works well but if you want the same Jenkins user to access multiple repositories over SSH you have a problem as each Deploy Key must be globally unique across all GitHub repositories. The answer to this is to use aliasing and a SSH config file (refer: http://www.onemogin.com/blog/2011/9/1/jenkins-and-github-multiple-private-projects.html) however this won’t work with Post-Receive Hooks as the repository URL in the sent message won’t match to the aliased repository URL in the Jenkins job – typically errant behaviour below from the Jenkins log. I can’t see a way around this at the time of writing this post.

    FINE: Skipped GitHub Test - buildautomationtest repository because it doesn't have a matching repository.
    May 7, 2013 6:21:35 PM com.cloudbees.jenkins.GitHubWebHook
    FINE: Considering to poke GitHub Test - buildautomationtest repository
    

    – Chatter Plugin
    I’m a big fan of this plugin by Simon Fell. I tend to use a dedicated release manager user, e.g. release.manager@force365.com, standard user license capacity permitting, and perform all deployment tasks in this user context. This approach provides clarity on changes made by a deployment versus actual user and provides an easy way to be notified of failures etc.

    Key Principles

    1. Fit-for-purpose Org-set
    2. – Org-set is the terminology I use to describe the collection of orgs, and their roles, required to deliver a project safely to production.

      – One size does not fit all. Pick the minimum set of orgs roles required to deliver the project. Each org is a time expensive overhead.

      – Sandbox types. In defining the org-set, factor in the availability of config-only and full-copy sandboxes. The latter must be retained for cases where infrequent refresh is required. Project-level orgs don’t need to be part of the sandbox estate, Developer Edition orgs, or perhaps Partner Developer Edition orgs can be employed. Full-copy sandboxes are incredibly expensive, valuable resources, use only when absolutely necessary for as wide a set of roles as possible.

      – Connected orgs. For projects involving complex integrations, the complexity involved in creating a connected-org may influence the org-set design – there may be an argument to consolidate roles onto a single test org used for QA and UAT perhaps.

    3. Continuous Integration
    4. A best practice org-set design for non-trivial technical projects with multiple technical contributors should require isolation of developer activities into a separate developer orgs with a code-level integration org and Continuous Integration (CI) process in place.

    5. Project-level sandboxes are not refreshed
    6. Project-level orgs are all built from the Git repository. The Pre-production programme-level org must be refreshed from Production pre-deployment to ensure the deployment is verified against the current state.

    7. Commit to the remote project branch is a commitment that metadata is ready for system testing
    8. Build automation will deploy a project branch commit to the project QA org. In my experience it pays to be prescriptive in terms of development process.

    9. Commit to the remote master branch is a commitment that metadata is ready for integration testing
    10. Build automation will deploy a master branch commit to the programme INT (integration) org – this org exists to enable rigorous regression testing to be applied by all project workstream. Post-deployment suites of automated tests should be invoked and reports analysed by the test lead on each project.

    11. Test Automation
    12. It’s a significant resource overhead to execute manual test scripts for each regression test cycle, not to mention error prone. For non-trivial projects, the investment must be made at an early stage in automated-testing. Selenium is a good choice, but the tool utilised doesn’t really matter, what matters is that from the outset of the project the test team start to build-up a comprehensive suite of automated test cases with coverage of the key acceptance criteria defined for each user story. The suites then enable automation of regression testing during deployment phases – the same scripts underpin system testing and provide an often overlooked second stage to CI (unit tests + acceptance tests).

    13. GitHub branch design
    14. – A simple, clean branch design is desirable in the remote repository.

      – Long-lived branches for active project workstreams. Project branches may have sub-branches for each sprint or phase.

      – Long-lived branch for patches. Bug fixes are developed on local branches and committed to the remote support branch when ready for system testing.

      – It can be advisable to consider how important a clean Network Graph is, this is impacted by Git merge versus rebase decisions.

    15. Build automation challenges
    16. In a perfect world, all metadata component types would be covered by the Metadata API. This isn’t the case so the nirvana of simple cloning of an org configuration is yet to exist. Instead a prescriptive process is required which spans manual configuration tasks, metadata deletion and build automation.

      – Proactive management of change
      A nominated release manager should proactively manage change at a programme-level, advise the project teams on release process and strive to minimise deployment conflicts through early involvement in all project developments. A change log should be maintained which lists all changes being made. This could include technical component types (ApexClass, ApexTrigger etc.) being added, modified or deleted, but as a minimum must track configuration changes requiring manual action – enablement of features, field data type changes etc. and required standing data (custom settings etc.). All changes should be mapped to a Change Type of manual or automated and a list of orgs to which the change has been deployed tracked. This is clearly an overhead to the project but without control it can be very easy to lose track of the current state of the orgs in use and face significant time expense in attempting to rationalise the situation through failing deployments. The release manager, or technical lead should apply manual tasks to target orgs pre-emptively to minimise automated build failures.

      – Be prepared for build failures
      Automated builds will fail; this is a fact of life where build-dependencies on manual actions exist. Proactive management will only get you so far. Attempting to minimise this is more realistic than elimination.

      – Data
      Automation of data setup in a target org is possible via Ant and the Data Loader CLI, or other similar means. Alternatively a data file could be deployed as a document or static resource and then loaded from an Apex script (as per the ISV approach).

      – Unsupported metadata component types
      Automation is possible using Selenium scripts, which execute at the UI level and can simulate, for example, a user activating a setting. Such scripts can then be integrated into an automated build. This is highly possible, but takes time and expertise with both Ant and Selenium to accomplish.

    17. Programme-level Integration
    18. The Converged Programme Model involves project workstreams building in isolated org-sets with frequent merge-from-master actions bringing across any changes to the production state. This approach should surface conflicts early, i.e. during development itself, but to be sure that shared component changes have not introduced any functional inconsistencies, regression testing must be applied by each and every project workstream on each occasion any project does a release. This is a strong argument for test automation.

    19. UAT
    20. – Project-level or programme-level?
      In principle UAT should always be applied at the local project-level as the commit to the programme-level integration org is an absolute commitment that the code is production ready. In practice UAT may be two tiered; initial user acceptance of new functionality, followed by some form of secondary acceptance testing in Pre-production, in parallel to deployment verification testing.

    21. Path-to-production Change Management
    22. As with any programme of work, fit-for-purpose Change Management processes should be in place. In context this means a Change Advisory Board (CAB) should be in place to approve deployment, this must include informed and empowered representation across business and technical functions.

      – A Deployment Request Form (DRF), or similar, should be produced to document the change being released, the impact, pre and post deployment tasks, GitHub commit # etc., approval date or rejection reason. The DRF could be approved by a convened board or via email response.

      – The DRF process is absolutely required for the final deployment to Production, but may also be applied to the Pre-Production deployment, i.e. the commencement of the final step of the path-to-production release flow.

External Id Deployment Error

On occasion when deploying components between orgs you may encounter a database-level category of deployment error, bubbling up from the underlying Oracle RDBMS with limited information to support any diagnostic process. The example below is one such case I’ve seen in practice recently. Most concerning about this type of error is that the deployment appears to commit (with the exception of the affected fields) but reports a failure result state. It’s therefore one to take seriously in automated build environments where undesirable action may be taken on the basis of the build result etc.

Example Case – Too many external Ids on a single object
Scenario – object (standard or custom) has 3 custom fields defined as external Ids (this is the soft limit), and an attempt is made to deploy a new object definition with 2 different fields marked as external Ids, making the sum 5. Remember, the deployment won’t remove this field attribute. In all such cases, where the updated object definition has more than the 3 limit, the result is..

C:\Program Files (x86)\Jenkins\jobs\..\workspace\build.xml:41: Failed to process the request successfully. Cause(LIMIT_EXCEEDED): LIMIT_EXCEEDED: java.sql.SQLException: ORA-20526: 
ORA-06512: at "HAPPY.CCUSTOMINDEX", line 275
ORA-06512: at line 1
: {call cCustomIndex.insert_detail(?,?,?,?,?,?,?,?,?,?,?)}

..or..

C:\Program Files (x86)\Jenkins\jobs\..\build.xml:41: Failed to process the request successfully. Cause(LIMIT_EXCEEDED): LIMIT_EXCEEDED: java.sql.SQLException: ORA-20526: 
ORA-06512: at "SLEEPY.CCUSTOMINDEX", line 275
ORA-06512: at line 1
: {call cCustomIndex.insert_detail(?,?,?,?,?,?,?,?,?,?,?)}

I’ve also seen this behaviour with deployments that exceed the tracked fields limit in a similar way, however I’ve been unable to recreate this with the 27.0 Metadata API.

Salesforce Ant Scripts – Selenium

The Salesforce metadata API is an extremely powerful tool, when combined with Ant, Jenkins etc. for build automation. There is however a number of configuration items that simply can’t be retrieved and deployed using this API (Account Teams, Support Settings, Lead Settings, Case Assignment and Escalation Rules etc.). The unsupported list can be found here, unfortunately the platform expands at a rate more or less equal to the rate at which coverage of the API has increased over time. Anyway, my point here is that typically deployments have three steps; a manual step to cover the gaps in the metadata API (pre-requisites), an automated deployment step (retrieve-and-deploy with Ant) and finally a data population step (Data Loader CLI with Ant perhaps..). Leaving data to one side (for this post), an ability to merge steps 1 and 2 would enable full automation of the deployment of configuration – which in most cases would be a good thing. One approach to automate step 1 is to write Selenium web browser automation scripts which drive the Salesforce application at the UI level. The scripts can be exported as JUnit test cases and then be incorporated into an Ant based build process and automated. My approach to doing this is outlined below, as with most things there are many ways to achieve the same result and I’m sure this can be improved on, however it keeps the process simple and gets the job done which tends to work for me. Additionally, the approach plays well with Ant, Jenkins/Hudson etc.. so it should be straightforward to extend an existing build process.

1. Install the Selenium IDE Firefox Extension.
2. Using Selenium IDE record the act of logging-in to Salesforce and making the required configuration changes.
3. Export the test case as a Java / JUnit 4 / WebDriver file. This creates a .java file as below. The example simply creates a Chatter post for the logged-in user, hopefully this is simple and illustrative enough to make the point.

package com.example.tests;

import java.util.regex.Pattern;
import java.util.concurrent.TimeUnit;
import org.junit.*;
import static org.junit.Assert.*;
import static org.hamcrest.CoreMatchers.*;
import org.openqa.selenium.*;
import org.openqa.selenium.firefox.FirefoxDriver;
import org.openqa.selenium.support.ui.Select;

public class SeleniumTest {
  private WebDriver driver;
  private String baseUrl;
  private boolean acceptNextAlert = true;
  private StringBuffer verificationErrors = new StringBuffer();

  @Before
  public void setUp() throws Exception {
    driver = new FirefoxDriver();
    baseUrl = "https://test.salesforce.com/";
    driver.manage().timeouts().implicitlyWait(30, TimeUnit.SECONDS);
  }

  @Test
  public void testSelenium() throws Exception {
    driver.get(baseUrl + "/");
    driver.findElement(By.id("username")).clear();
    driver.findElement(By.id("username")).sendKeys("release.manager@force365.com");
    driver.findElement(By.id("password")).clear();
    driver.findElement(By.id("password")).sendKeys("mypassword");
    driver.findElement(By.id("Login")).click();
    driver.findElement(By.id("publishereditablearea")).clear();
    driver.findElement(By.id("publishereditablearea")).sendKeys("new Chatter post - Selenium");
    driver.findElement(By.id("publishersharebutton")).click();
  }

  @After
  public void tearDown() throws Exception {
    driver.quit();
    String verificationErrorString = verificationErrors.toString();
    if (!"".equals(verificationErrorString)) {
      fail(verificationErrorString);
    }
  }

  private boolean isElementPresent(By by) {
    try {
      driver.findElement(by);
      return true;
    } catch (NoSuchElementException e) {
      return false;
    }
  }

  private String closeAlertAndGetItsText() {
    try {
      Alert alert = driver.switchTo().alert();
      if (acceptNextAlert) {
        alert.accept();
      } else {
        alert.dismiss();
      }
      return alert.getText();
    } finally {
      acceptNextAlert = true;
    }
  }
}

4. Modify the test case java code as required.
5. Download the Java Selenium Client Driver from http://seleniumhq.org/download/
6. Extend or create a new Ant build file to compile and execute the test case. My example below requires a [selenium\src] sub directory structure in the build root, with the .java test case files placed in the src directory.

<project basedir="." default="usage" name="invoke Selenium script to configure Salesforce org">
	<property name="bin" value=".\selenium\bin" />
	<property name="lib" value="c:\Release Management\selenium-2.28.0\libs" />
	<property name="src" value=".\selenium\src" />
	<property name="report" value=".\selenium\reports" />	
	
 	<target name="usage" depends="">
		<echo message="Compiles and executes Selenium IDE exported test cases (source format JUnit4 WebDriver .java files)" />
	</target>

	<target name="init">
		<delete dir="${bin}" />
		<mkdir dir="${bin}" />
	</target>

	<target name="compile" depends="init">
		<javac includeantruntime="false" source="1.7" srcdir="${src}" fork="true" destdir="${bin}" >
			<!-- requires Selenium test cases exported as JUnit4 WebDriver .java files in the src sub-directory -->
			<classpath>
		    	<pathelement path="${bin}">
		        </pathelement>
		        <fileset dir="${lib}">
		        	<include name="**/*.jar" />
		        </fileset>
		  	</classpath>
		</javac>
	</target>

	<target name="exec" depends="compile">
		<delete dir="${report}" />
		<mkdir dir="${report}" />
		<mkdir dir="${report}/xml" />
		
		<junit printsummary="yes" haltonfailure="yes">
			<classpath>
		    	<pathelement path="${bin}">
		    	</pathelement>
		        <fileset dir="${lib}">
		        	<include name="**/*.jar" />
		        </fileset>
		  	</classpath>
			<test name="com.example.tests.SeleniumTest" haltonfailure="yes" todir="${report}/xml" outfile="SeleniumTest-result">
				<formatter type="xml" />
			</test>
		</junit>
		
		<junitreport todir="${report}">
	        <fileset dir="${report}/xml">
	            <include name="TEST*.xml" />
	        </fileset>
	        <report format="frames" todir="${report}/html" />
	    </junitreport>		
	</target>
</project>

Note. There is no need to start or stop a Selenium server as the script runs locally on the build server – Firefox will be required however if you stick with the default browser in recorded scripts.

I’ll follow this initial post with further detail on the following;
1. Conditional script logic – i.e. I want the script to check for a condition before making a change such that it selectively configures and therefore won’t be reliant on a clean, predictable state.
2. Execution of test suites rather than individual cases.
3. Most likely I’ll refine the build.xml example as I understand more about this.

Salesforce Continuous Integration

What is it?
In essence CI is an aggressive build strategy requiring the isolated work of project developers to be integrated immediately following code commits to a shared source-code control system. Regression tests are run automatically, surfacing build errors or code inconsistency at an early stage.

CI is viewed as an Agile practice and is typically characteristic of a mature development process, and experienced developers. There is a definite learning curve and mindset adjustment for developers to be considered.

The manual alternative, which I term staged integration (SI), involves periodic integration testing of the HEAD revision from the source code control (SCC) system. The difference being the immediacy of performing the integration tests, and therefore verifying the integrity of the current build status. With the manual approach it can be difficult to instil team discipline, minor changes can often be viewed as not warranting a build and test.

Basic tenets
1. Developers work on an isolated copy of the code (i.e. branch) to avoid contention on shared resources, utility classes etc.
2. Developers commit unit-tested code to the shared SCC repository – often many times per-day.
3. An automated build process is triggered by the commit which takes the HEAD revision, deploys to a dedicated org running the full suite of unit tests. Test failures are reported proactively, naming and shaming the individual responsible for the failing commit. It’s key to note that pre-commit the developer should merge the current HEAD revision into their local branch and resolve conflicts (GIT for example will enforce this).
4. The HEAD revision represents a consistent “code complete” status. Development will typically take place in a isolated branch, with the master branch holding the production ready code.

Typical steps
1. Code is committed, this triggers a deployment to the INT org with unit test execution during the deployment.
2. Once deployment completes successfully, functional acceptance tests are executed, possibly via a tool like Selenium where functional tests at the UI level can be scripted (perhaps to verify a particular user story).

Why do it?
1.Daily builds have long been an industry best-practice, continuous integration is an evolutionary improvement.
2.The more frequently code is integrated the less painful it is.
3.Build errors are surfaced early, while the developer is still “in the zone” and can resolve the problem expediently.
4.Builds trust within the development team and a sense of collective ownership.
5.Driver for technical excellence, a key agile principle.
6.Encourages quality unit tests (code coverage and test case).

Obstacles
1.Big unit test suites can often take hours to run. To mitigate this obstacle, a smoke test could be executed on commit (current sprint related unit tests only), followed by a full test scheduled every 1/2 day, or overnight. The Force.com Migration tool enables executing test classes to be defined by name – so this is a feasible option.
2.Unit tests are an afterthought. Switch the team to TDD – perhaps with some education first.
3.Unsupported Metadata Types. Certain salesforce configuration elements (metadata types) can’t be deployed via the Force.com Migration Tool. Such elements must be recorded in a audit log and manually applied to the target org, or for automation a Selenium script could be utilised.
4.Standing Data. New features may require standing data (custom settings etc.). Use the Apex Data Loader in command-line mode (CLI), and invoke data manipulation operations within the build file.

Tools and process
A CI implementation requires fit-for-purpose tooling, for Force.com development the following stack is typical:

    SCC = Subversion or Git
    Build Automation Server = Jenkins or Hudson
    Scripting = ANT plus the Force.com Migration Tool (scriptable ANT task)

In simple terms CI works as follows. Within the build server (Jenkins for example) a job is defined that on each commit connects to the SCC repository and copies the HEAD revision to a working folder, then runs an ANT script. The script invokes a build.xml file which is held in SCC and therefore copied into the working folder. The build file runs whatever tasks are required including folder manipulation, static resource zipping, but ultimately (in context) the intent is to run the deploy target in the Force.com Migration Tool task, to deploy to a specific salesforce.com org. Connection details can be passed in via the job configuration or read from a build.properties file. A Jenkins plug-in can also be used to post build results to a Chatter post in a another org – very useful for notifications.

Exemplar Scenario – Single Project Org Strategy

Exemplar Scenario – Multiple Project Org Strategy

Related Concepts (for future posts)
TDD – Test Driven Development
Pair Programming
SCC Branching Strategy