23

Deploying Process Builder Flows from one org to a target org only works via the ant migration tool if the existing process builder flows are deleted first using destructive changes. However, if the destructive changes take place and the subsequently deployment breaks for any reason then the target org remains in a state where the flows no longer exist.

In general, the deployment of the flows presents some challenges.

  1. The flows will only deploy in deactivated mode in the target org by default. They have to be manually activated in the target org after deployment.

  2. Once they are activated in the target org, any subsequent builds to the target org fail with the message that the flow(s) in the target org are active and cannot be overwritten. Deactivating them in the target org, does not seem to work. They have to be deleted before the next build in Version 33.0.

To summarize, the challenge is that if we do not delete the flows, any subsequent build to the org fails. If we do delete them, any build that fails ends up leaving the org with no flows.

Attempted solution:

Create entries in the destructiveChanges.xml to delete these flows. The subsequent build that occurs will recreate them. This solution is currently in place, but there are two issues with this: 1) If the build fails, then the destructive changes are not rolled back. The target org is therefore without the flows that it needs. 2) If the build is successful, the flows still have to be manually activated.

I investigated using version 34.0 and that still has the same issue even though it supports a FlowDefinition and activeVersionNumber metadata, it still runs into the same issue. The existing flows even if they are not active prevent subsequent deployments.

4 Answers 4

14

There are some goodies in version 34.0 that should help you out. As you already noticed 196 added the concept of FlowDefinition that should allow you to activate/deactivate processes (flows) via the MD API. 196 also solve the problems around continuous integration by adding a permission you can turn on: FlowMdOverwriteEnabled. Flip this perm on and you will be able to overwrite active versions as well.

For anyone reading this post: Use this perm carefully because it will bypass every guard we have put in place to accidentally avoid overwriting an active process/flow.

Update:

The permission to request on the Case you raise is Enable MD Deploy to overwrite flows.

6
  • 3
    I'm not able to find any reference to FlowMDOverwriteEnabled in any Salesforce documentation. Is this something that we need to submit a feature activation ticket for? Oct 15, 2015 at 18:20
  • For reference, the FlowDefinition is a new type of metadata that can be deployed alongside the Flow type. Updating it enables one to activate or deactivate a flow in a particular org which can be useful in a continuous integration context.
    – abd3721
    Oct 28, 2015 at 1:53
  • 1
    Has anyone worked out where you can 'flip this perm on'? I can't find any reference to it anywhere. Is it a profile setting? A deployment setting?
    – Nick C
    Jul 14, 2016 at 23:58
  • 1
    You have to file a Case and request Salesforce to activate this for you in your production org and sandbox(es). We recently did this and it helped our continuous integration process immensely. Unfortunately, although they told me that the permission would persist upon sandbox refreshes, that does not appear to be the case. I'm filing another Case now... Jul 28, 2016 at 20:13
  • 1
    @JoD, You don't have to flip it on, when SFDC activates the feature (after you open the case with them), it is automatically enabled for all users (I had the same question). I still am seeing issues with test methods failing which is directly related to this permission being enabled and working with SFDC now to fix those issues.
    – Cody Cusic
    May 15, 2017 at 19:46
9

I wrote an ant target to help with the deployment of flows.

Before deploying flows, I fetch the flow definitions, then only deploy flows that are newer than the currently active version.

This assumes that you keep your metadata in a directory named src, so your flows are in src/flows and flow definitions in src/flowDefinitions. Deployment is done from a build directory that is populated by ant.

A script task is used. Refer to the ant documentation regarding dependencies for javascript tasks.

<target name="deploy" depends="prepBuildDir,prepFlows">
   <sf:deploy username="${sf.username}" password="${sf.password}" serverurl="${sf.serverurl}" deployRoot="build" singlePackage="true" maxPoll="${sf.maxPoll}" pollWaitMillis="${sf.maxWaitMillis}"  testLevel="${test.level}" checkOnly="${check.only}" logType="${log.type}" ignoreWarnings="true" />
</target>

<target name="prepBuildDir">
   <delete dir="build"/>
   <mkdir dir="build"/>
   <sync todir="build">
      <fileset dir="src">
         <exclude name="flows/*"/>
         <exclude name="flowDefinitions/*"/>
      </fileset>
   </sync>
</target>

<target name="prepFlows" depends="prepBuildDir,retrieveCurrentFlows,findFlowsToDeploy"/>

<target name="retrieveCurrentFlows" depends="orgType,setCredentials">
   <sf:retrieve username="${sf.username}" password="${sf.password}" serverurl="${sf.serverurl}" retrieveTarget="build" unpackaged="flowDefinitions.xml" />
</target>

<!--
     Compare retrieved flowDefinitions to ones in src
     If different and activeVersionNumber in src is greater, copy flow with activeVersionNumber to build/flows
-->
<target name="findFlowsToDeploy">
   <script language="javascript"><![CDATA[
      var File = java.io.File;

      function version(xml) {
         var matches = (''+ xml).match(/activeVersionNumber>(\d+)</);
         if (matches) {
            return parseInt(matches[1], 10);
         }
      }

      function getFileContents(javaFile) {
         var contents = '';
         var lines = java.nio.file.Files.readAllLines(java.nio.file.Paths.get(javaFile.getPath()), java.nio.charset.Charset.forName('UTF-8'));
         for (var i = 0; i < lines.size(); i++) {
            contents += lines.get(i);
         }
         return contents;
      }

      function getFlowVersion(flowFile) {
         var content = getFileContents(flowFile);
         return version(content);
      }

      function addFileToFileset(fileset, path) {
         java.lang.System.out.println('Flow needs to be deployed: ' + path);
         fileset.appendIncludes([path]);
      }

      function copyFiles(files, dest) {
         var copyTask = project.createTask('copy');
         if (typeof files === 'string') {
            copyTask.setFile(new File(files));
         } else {
            copyTask.add(files);
         }
         copyTask.setTodir(new File(dest));
         copyTask.setOverwrite(true);
         copyTask.perform();
      }

      function createFileset(dir) {
         var fileset = project.createDataType('fileset');
         fileset.setDir(new File(dir));
         return fileset;
      };

      function flowName(flowDefinition, version) {
         return flowDefinition.replace('.flowDefinition', '') + '-' + version + '.flow';
      }

      var flowsToDeploy = createFileset('src/flows');
      var flowDefinitions = createFileset('src/flowDefinitions');

      var srcFiles = flowDefinitions.getDirectoryScanner(project).getIncludedFiles();
      var flowFileNamesToDeploy = Array.prototype.map.call(srcFiles, function(filename) {
         var orgFlow = new File('./build/flowDefinitions/', filename);
         var gitFlow = new File('./src/flowDefinitions/', filename);
         var gitVersion = getFlowVersion(gitFlow);
         if (!orgFlow.exists()) {
            return flowName(filename, gitVersion);
         }
         var orgVersion = getFlowVersion(orgFlow);
         if (gitVersion > orgVersion) {
            return flowName(filename, gitVersion);
         }
         return null;
      })
         .filter(function(filename) {
            return !!filename;
         });
      flowFileNamesToDeploy.forEach(addFileToFileset.bind(null, flowsToDeploy));

      var flowDefinitionFileNamesToDeploy = Array.prototype.filter.call(srcFiles, function(filename) {
        var orgFlow = new File('./build/flowDefinitions/', filename);
        // Flow Definitions cannot be created by a deploy
        if (! orgFlow.exists()) {
          java.lang.System.out.println('Not going to deploy new flow definition: ' + filename);
        }
        return orgFlow.exists();
      });
      flowDefinitionFileNamesToDeploy.forEach(addFileToFileset.bind(null, flowDefinitions));

      // Overwrite fetched package.xml
      copyFiles('src/package.xml', 'build');
      if (!flowDefinitions.hasPatterns()) {
        java.lang.System.out.println('No Flow Definitions to deploy');
      } else {
        copyFiles(flowDefinitions, 'build/flowDefinitions');
      }

      if (!flowsToDeploy.hasPatterns()) {
        java.lang.System.out.println('No Flows to deploy');
      } else {
        copyFiles(flowsToDeploy, 'build/flows');
      }
   ]]></script>
</target>

Here's the flowDefinitions.xml file used by the retrieveCurrentFlows target to retrieve the current flow definitions:

<?xml version="1.0" encoding="UTF-8"?>
<Package xmlns="http://soap.sforce.com/2006/04/metadata">
   <types>
      <members>*</members>
      <name>FlowDefinition</name>
   </types>
   <version>36.0</version>
</Package>
1

You should be able to deploy a flow with a higher version number to a target org. If you include a FlowDefinition in your deploy, it will activate the proper version of the flow.

I think that's the approach I'm using.

Does this not work for you?

3
  • Does this work in a CI environment, or only when you're doing a one off manual deployment and you know that your new flow has a higher version number?
    – Nick C
    Jul 25, 2016 at 6:56
  • @NickCook I'm not using CI, so I can't speak to that. Jul 25, 2016 at 13:46
  • You can deploy a higher number version of a Flow into another org. I do this all time when deploying Flows / Process Builder from my github projects to other orgs. If the Flow / PB is newly created in destination org it would then have one active version, and the version number would match what the file name had in it. If the Flow / PB existed then new version is added. Use FlowDefinition metadata type to indicate which version should be active.
    – Doug Ayers
    Dec 18, 2016 at 5:30
1

After struggling for a long time with this as well as other limitations, I have built some ad-hoc Ant tasks to work around this and other issues. The result is a collection of ready to use ant tasks collected in a library (ForceFlow), that can be used in conjunction with the Force.com migration tool.

ForceFlow can be found here: https://github.com/eroispaziali/ForceFlow/

The Flow/FlowDefinition addition is quite new, but we use it successfully to seamlessly deploy metadata that include Flows via CI. Under the hood deploys an increased version of the latest flows in order to deactivate them all, and then all the flows are deleted before a normal deployment is made. I hope it helps!

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .