Repository: SFDO-Tooling/CumulusCI
Pull request #3596 (2023-05-22 15:31:16)

Add deploy transform to inject org URLs


Patch:
diff --git a/cumulusci/core/source_transforms/transforms.py b/cumulusci/core/source_transforms/transforms.py
index 15e4d04b6..146802bb5 100644
--- a/cumulusci/core/source_transforms/transforms.py
+++ b/cumulusci/core/source_transforms/transforms.py
@@ -347,7 +347,7 @@ class FindReplaceIdSpec(FindReplaceBaseSpec):
 
 
 class FindReplaceCurrentUserSpec(FindReplaceBaseSpec):
-    inject_username: bool = True
+    inject_username: bool
 
     def get_replace_string(self, context: TaskContext) -> str:
         if not self.inject_username:  # pragma: no cover
@@ -358,6 +358,18 @@ class FindReplaceCurrentUserSpec(FindReplaceBaseSpec):
         return context.org_config.username
 
 
+class FindReplaceOrgUrlSpec(FindReplaceBaseSpec):
+    inject_org_url: bool
+
+    def get_replace_string(self, context: TaskContext) -> str:
+        if not self.inject_org_url:  # pragma: no cover
+            self.logger.warning(
+                "The inject_org_url value for the find_replace transform is set to False. Skipping transform."
+            )
+            return self.find
+        return context.org_config.instance_url
+
+
 class FindReplaceTransformOptions(BaseModel):
     patterns: T.List[
         T.Union[
@@ -365,6 +377,7 @@ class FindReplaceTransformOptions(BaseModel):
             FindReplaceEnvSpec,
             FindReplaceIdSpec,
             FindReplaceCurrentUserSpec,
+            FindReplaceOrgUrlSpec,
         ]
     ]
 
diff --git a/docs/deploy.md b/docs/deploy.md
index f06e0ac39..df3b50aef 100644
--- a/docs/deploy.md
+++ b/docs/deploy.md
@@ -153,6 +153,22 @@ options:
                     inject_username: True
 ```
 
+#### Find-and-Replace Org URL Injection
+
+CumulusCI can replace a given token with the org URL of the target Salesforce org.
+All that is needed is to specify a value for `find` and set `inject_org_url: True`:
+
+```yaml
+task: deploy
+options:
+    transforms:
+        - transform: find_replace
+          options:
+              patterns:
+                  - find: special_string
+                    inject_org_url: True
+```
+
 ### Stripping Components with a `package.xml` Manifest
 
 This transformation allows you to deploy a subset of a metadata directory based on a `package.xml` manifest by removing unwanted components. It will compare components available in the source folder with a provided `package.xml` file and delete/modify component files which are not found.


Test patch:
diff --git a/cumulusci/core/source_transforms/tests/test_transforms.py b/cumulusci/core/source_transforms/tests/test_transforms.py
index cf255eb97..4fb9015d5 100644
--- a/cumulusci/core/source_transforms/tests/test_transforms.py
+++ b/cumulusci/core/source_transforms/tests/test_transforms.py
@@ -522,9 +522,7 @@ def test_find_replace_current_user(task_context):
     options = FindReplaceTransformOptions.parse_obj(
         {
             "patterns": [
-                {
-                    "find": "%%%CURRENT_USER%%%",
-                },
+                {"find": "%%%CURRENT_USER%%%", "inject_username": True},
             ]
         }
     )
@@ -551,6 +549,40 @@ def test_find_replace_current_user(task_context):
     )
 
 
+def test_find_replace_org_url(task_context):
+    options = FindReplaceTransformOptions.parse_obj(
+        {
+            "patterns": [
+                {
+                    "find": "{url}",
+                    "inject_org_url": True,
+                },
+            ]
+        }
+    )
+    builder = MetadataPackageZipBuilder.from_zipfile(
+        ZipFileSpec(
+            {
+                Path("classes") / "Foo.cls": "System.debug('{url}');",
+                Path("Bar.cls"): "System.debug('blah');",
+            }
+        ).as_zipfile(),
+        context=task_context,
+        transforms=[FindReplaceTransform(options)],
+    )
+
+    instance_url = task_context.org_config.instance_url
+    assert (
+        ZipFileSpec(
+            {
+                Path("classes") / "Foo.cls": f"System.debug('{instance_url}');",
+                Path("Bar.cls"): "System.debug('blah');",
+            }
+        )
+        == builder.zf
+    )
+
+
 @pytest.mark.parametrize("api", [FindReplaceIdAPI.REST, FindReplaceIdAPI.TOOLING])
 def test_find_replace_id(api):
     context = mock.Mock()