CumulusCI is a powerful open source framework for automating delivery of Salesforce products and solutions. While CumulusCI comes with a rich library of tasks, you may find a need to implement custom logic.
CumulusCI’s task framework provides much of the scaffolding needed so writing a custom task is mostly focused on implementing the business logic in Python. Understanding CumulusCI’s task framework will help you avoid reinventing the wheel when creating custom tasks.
Every task in CumulusCI is a subclass of the BaseTask class. BaseTask is where much of the scaffolding is set up for you. At a high level, BaseTask includes:
task_options
and processing them via the _init_options
method.self.project_config
from within the task and to the org config as self.org_config
for tasks that require oneself.logger
_run_task
method where classes implement their business logicIn addition to BaseTask, CumulusCI includes a number of base task classes for more specific types of tasks:
self.sf
and self.tooling
self.github
self.mc_config
While the logic of tasks is implemented in Python, the cumulusci.yml
file is used to wire those Python task classes into your project’s configuration. In each task definition, the class_path
provides the Python path to the class that implements the task’s logic. CumulusCI automatically adds the project’s repository root to the PYTHONPATH allowing for custom tasks to live in the project’s repository.
Now that we have an overview of the task framework, let’s dive into some examples to see it in action.
This real world use case was mentioned in the CumulusCI group on the Trailblazer Community by a user who needed to deploy an authProvider that has the target org’s username.
In this example, we’ll be extending CumulusCI’s built in FindReplace task which already contains much of the logic we need. The task accepts task options for find
, replace
, path
, and file_pattern
and will replace all instances of find
with the value of replace
in all files matching the path
and file_pattern
.
Since the FindReplace task provides all the replacement logic we need, all we need to do is create a custom task that dynamically sets the replace
value with the target org’s username. However, FindReplace is set up as a local task that doesn’t need a target org to run so we need the custom task to require a target org.
Step 1: Create the custom task in Python
Create a file called tasks/util.py
in the root of your repository containing
Let’s review the Python code:
_init_options
method to add custom logic to setting up the task’s optionsreplace
option’s value to the target org’s username, found via self.org_config.username
Step 2: Configure the task in cumulusci.yml
Now that we have the task implemented in Python, we need to add it as a task to the project’s config. Add the following to the tasks:
section of your CumulusCI.yml file
Step 3: Run It
You can verify that the task was successfully configured using cci
in a command prompt:
In this fictional use case, we want to query the Tooling API to get the info on all record types for a given object so that the values can be used in a later task in a flow. We’ll be creating a new task named get_record_types
with a task class named GetRecordTypes
that extends BaseSalesforceApiTask
to interact with the Tooling API.
Step 1: Create the custom task in Python
Create the file tasks/tooling.py
with the following code:
Let’s briefly review the Python code:
GetRecordTypes
which subclasses BaseSalesforceApiTask
object
to receive the object name to list record types for_init_options
task to add SOQL injection protection to the object
optionsf.tooling
instance of the simple-salesforce Python library to query all record types for the objectself.return_values
dictionary used to pass values from one task to another in a flowStep 2: Configure the task in cumulusci.yml
Add the following to the tasks:
section of your CumulusCI.yml file
Step 3: Run It
In a command prompt:
Since CumulusCI is open source, you should consider if the custom task you’ve written is specific to your project or if it is generically useful to other Salesforce developers. If it’s the latter, you can enhance CumulusCI by contributing your new task. While contributing may require a bit more work up front like writing test cases for your custom task, by contributing you are improving life for other Salesforce developers and get your task and its test cases running as part of CumulusCI’s builds.
Hopefully these two examples help illustrate the power of custom Python tasks in CumulusCI and how much of the heavy lifting CumulusCI’s task framework does for you. Now that you know how to write custom tasks for CumulusCI, what will you automate next?
Interested in exploring what CumulusCI can do to improve your development to delivery lifecycle? Book a free one-hour consultation at https://calendly.com/muselab