This document provides a high level introduction to the Cumulus Continuous Integration infrastructure (CumulusCI). CumulusCI is the process used by the Salesforce.com Foundation for the Cumulus project, the next generation of the Non-Profit Starter Pack (NPSP).
The process integrates GitHub, Jenkins, the Salesforce.com Ant Migration Tool, and a custom web application called mrbelvedere running on Heroku.
While this process was specifically built for the Cumulus project, it should be useable by other Force.com projects.
Project Cumulus is an update and overhaul of the existing Salesforce.com Foundation Nonprofit Starterpack. More detail about the project is available through the Cumulus GitHub repository, however there are some details useful to explaining the CumulusCI process:
The CumulusCI process is a hybrid pulling concepts from GitHub Flow and Git Flow to adapt to the unique build requirements of doing CI on the Force.com platform.
There is only one main, persistent branch in the repository and it is named
dev to avoid confusion of whether the ""master"" branch means the current development version or the current production version. All other branches are created off
dev and are designed to be deleted once merged back into dev.
All development work is done in feature branches. Completed feature branches are merged into the
dev branch via a GitHub Pull Request. No commits should ever be made to the
dev branch directly.
Since the process is based heavily around GitHub Pull Requests which use
git merge behind the scenes to perform the merge operation on approved Pull Requests, we have opted for a merge instead of rebase approach. To ensure that history stays consistent,
git rebase should never be used in the Cumulus repository.
The workflow for the internal team uses a single repository owned by the SalesforceFoundation organization in GitHub. The process for external contributors is slightly different. Since external contributors do not have rights to the repository, they cannot create their own feature branches directly in the repository. They need to first fork the repository in GitHub and then use the same process used by internal developers to build a feature branch. Once the branch is ready, the external contribution is submitted as a GitHub Pull Request against the
dev branch of the SalesforceFoundation/Cumulus repository.
Cumulus uses a build.xml file in the root of the repository to provide a number of useful Ant build targets for working on, deploying, and testing Cumulus. Even without a deployed Jenkins server, these targets allow anyone to check out the repository and quickly deploy either an unmanaged or managed version of Cumulus to a DE or Partner DE org.
The following build targets are defined in the build.xml file:
Runs a simple deployment of the code including execution of all Apex tests as part of the deployment. If any test failures occur, the deployment is rolled back.
Runs a simple deployment of the code but does not execute all Apex tests. This is useful if you want to quickly deploy already tested code into an org.
Runs a complete clean, update, build, test cycle. Starts with the uninstallCumulus target to clean unpackaged metadata from the org. Then runs updateDependentPackages to ensure all managed packages match the required versions for the checked out code. Finally, runs deploy to deploy the code and run all tests.
NOTE: This is a very destructive operation which is designed to be run against organizations dedicated to CI purposes. Do not run this against an org with any metadata you care about keeping.
Deploys the 5 NPSP managed packages plus the latest beta managed package for Cumulus.
Calls out to the mrbelvedere application to get the latest beta managed package version and its corresponding repository tag. Sets the required versions per the repository tag's version.properties file and then runs updateDependentPackages to do the install/uninstall of managed packages so they are the requested version. Finally, calls runAllTests to kick off all the Apex tests deployed in the org.
Uninstalls all unpackaged metadata of types used by Cumulus from a target org. First, reads all metadata from the org. Next, builds and deploys a package to reset all ActionOverrides to default on Standard Objects (Account, Contact, Lead, and Opportunity). Next, builds and deploys a package with a destructiveChanges.xml file to delete all unpackaged metadata which can be deleted.
Ensures that all 5 original NPSP packages (npe01, npo02, npe03, npe4, and npe5) and the Cumulus managed package (npsp) are installed and at the correct version. If a package needs to be downgraded, it is first uninstalled. If a package needs to be upgraded, the upgraded version is installed without first uninstalling the package.
Retrieves all unpackaged metadata from the org. This target is more a utility for developers than a part of the CI process.
Uses a blank package to deploy and then run all tests in the target org. This is useful if the code you want to test is already deployed (i.e. from a managed package) and you just want to kick off the deployed tests.
devbranch to all feature branches. If a merge conflict is encountered, it creates a Pull Request in GitHub to merge dev into the feature branch. Once the developer manually resolves the merge conflict, the Pull Request is closed automatically.
scripts/set_dependent_versions.pyscript was run after changing the
All development work for Cumulus is done in feature branches with a naming convention of `feature/123-description-of-feature' where 123 is the GitHub issue number associated with the branch and description-of-feature is a short description of what the branch contains.
Whenever a new feature branch is pushed to the repository in GitHub or when a push is made against an existing feature branch, the mrbelvedere Heroku app triggers the Cumulus_feature job to build the branch and report any build failures to the developer who last committed to the branch. It also marks build status of the commit via the GitHub Commit Status API so any Pull Requests created from the feature branch are marked with their build status as shown in the two examples below:
Once a feature branch's Pull Request is approved and merged into the
dev branch, the Dev Branch Process starts to test the merged code.
One challenge of a feature branch approach is how to keep feature branches in sync with the dev branch. For example, say I start on a feature branch, feature/1-some-feature. At the same time, another developer starts the feature/2-another-feature branch. The other developer completes their branch before I do and submits a Pull Request which is approved and merged into dev. At this point, my feature/1-some-feature branch is out of sync with dev.
The typical process to handle this example would be for the developer to merge the changes from dev back into their branch. However, there is not an easy way for them to know this needs to be done. If dev is not merged into their branch, then a build run on their feature branch does not accurately reflect how their code would function when merged with
To handle this use case, we use the Cumulus_dev_to_feature job at the end of the Dev Branch Process to push all changes to dev which have passed build back into all open feature branches. This way, the next time the developer goes to push from their local repository to their feature branch on GitHub, they will be notified that there are new changes they need to pull to their local branch. This is usually as simple as running
dev branch is the only persistent branch in the process and should always be passing builds. We test all feature branches before merge and all feature branches are kept up to date with any changes to the
There is a 3 step chain of builds involved in the Dev Branch Process:
devbranch and uses the deployCI ant target to clean the cumulus.dev org, upgrade any managed package versions which need upgraded, and deploys the
devbranch code to the org running all apex tests. If all tests pass, it then runs the Cumulus_dev_to_feature job. If the build fails, all developers are notified.
devbranch code to the dedicated org (cumulus.dev.cin) for running Cinnamon browser based tests (Selenium + SauceLabs). This job only deploys the Cumulus code to the org and does not kick off the Cinnamon tests. If the build passes, the Cumulus_dev_cinnamon_test job is run.
The Cumulus team works on a Scrum process with 2 week development iterations. At the end of each iteration, managed beta package is built for User Acceptance Testing (UAT). The purpose of the UAT process is to identify issues which prevent the release from going into production. Changes in functionality which are merely enhancements on the release's functionality should be handled through the normal development process.
The following Jenkins job handle the automation of the UAT Process to the extent currently possible on the Force.com platform.
Creating a UAT release is a 3 step process requiring manual interaction with GitHub and the package org.
GitHub has support for managing Releases with a repository. A release in GitHub is simply a git tag with addional metadata including a markdown enabled body field.
dev branch is ready to be rolled over into a UAT release, a Release is created in GitHub by going to the repository, clicking the Releases tab, and clicking the Draft a new release button.
UAT releases should have the This is a pre-release checkbox checked and should use the naming conventions
uat/1.0-beta2 for the tag name and
1.0 (Beta 2) for the release name. The release name syntax should exactly model the Force.com version number for the managed beta version which will contain it.
Once the Cumulus_uat build passes, the code should be deployed to the packaging org. To create the beta managed package:
Once the beta managed package is ready, we need to update the body of the GitHub release created in Step 1 to include the install URL for the package.
The mrbelvedere app looks for an installation URL in the body of the Release when searching for the latest beta release. This is necessary since the Release's tag must be created in the repository, tested, and finally manually packaged before the package can be installed in an org. Adding the URL to the body of the Release is essentially a way to flag the release as ready. GitHub Releases have a draft mode but the draft mode does not create a tag in the repository until it is published and thus does not solve this issue.
The Cumulus_uat_managed job in Jenkins monitors the mrbelvedere app for a change in the latest managed beta package version. When a change is detected, a build is triggered to deploy the managed package to the cumulus.uat org and then kick off all tests in the org.
More on the Release Process coming soon...
The Installation and Setup documentation provides details on building out a server with Jenkins and the necessary plugins and configuration needed to run the jobs for CumulusCI.