Skip to content

Commit

Permalink
Merge branch 'main' into feature/improve-find-replace-ns-handling
Browse files Browse the repository at this point in the history
  • Loading branch information
leboff authored Oct 17, 2024
2 parents 2ec9f22 + 42093d7 commit 4acbd07
Show file tree
Hide file tree
Showing 18 changed files with 766 additions and 83 deletions.
12 changes: 6 additions & 6 deletions .github/workflows/feature_test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ jobs:
runs-on: SFDO-Tooling-Ubuntu
steps:
- name: "Checkout"
uses: actions/checkout@v2
uses: actions/checkout@v4
with:
fetch-depth: 1
- name: Set up Python 3.8
Expand Down Expand Up @@ -49,7 +49,7 @@ jobs:
- os: macos-13
python-version: 3.8
steps:
- uses: actions/checkout@v2
- uses: actions/checkout@v4
- name: Set up Python
uses: actions/setup-python@v4
with:
Expand All @@ -65,7 +65,7 @@ jobs:
name: "Robot: No browser"
runs-on: SFDO-Tooling-Ubuntu
steps:
- uses: actions/checkout@v2
- uses: actions/checkout@v4
- name: Set up Python 3.8
uses: actions/setup-python@v4
with:
Expand All @@ -81,9 +81,9 @@ jobs:
echo $(realpath sfdx/bin) >> $GITHUB_PATH
- name: Authenticate Dev Hub
run: |
sfdx plugins --core
sf plugins --core
echo $SFDX_HUB_KEY_BASE64 | base64 --decode > sfdx.key
sfdx auth:jwt:grant --clientid $SFDX_CLIENT_ID --jwtkeyfile sfdx.key --username $SFDX_HUB_USERNAME --setdefaultdevhubusername -a hub
sf org login jwt --client-id $SFDX_CLIENT_ID --jwt-key-file sfdx.key --username $SFDX_HUB_USERNAME --setdefaultdevhubusername -a hub
env:
SFDX_HUB_KEY_BASE64: ${{ secrets.SFDX_HUB_KEY_BASE64 }}
SFDX_CLIENT_ID: ${{ secrets.SFDX_CLIENT_ID }}
Expand All @@ -101,7 +101,7 @@ jobs:
cci org scratch_delete dev
- name: Store robot results
if: failure()
uses: actions/upload-artifact@v1
uses: actions/upload-artifact@v4
with:
name: robot
path: robot/CumulusCI/results
6 changes: 3 additions & 3 deletions .github/workflows/slow_integration_tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -84,15 +84,15 @@ jobs:
- name: Install sfdx
run: |
mkdir sfdx
wget -qO- https://developer.salesforce.com/media/salesforce-cli/sfdx/channels/stable/sfdx-linux-x64.tar.xz | tar xJ -C sfdx --strip-components 1
wget -qO- https://developer.salesforce.com/media/salesforce-cli/sfdx/channels/stable/sf-linux-x64.tar.xz | tar xJ -C sfdx --strip-components 1
echo $(realpath sfdx/bin) >> $GITHUB_PATH
- name: Initialize Browser/Playwright
run: cci robot install_playwright
- name: Authenticate Dev Hub
run: |
sfdx plugins --core
echo $SFDX_HUB_KEY_BASE64 | base64 --decode > sfdx.key
sfdx auth:jwt:grant --clientid $SFDX_CLIENT_ID --jwtkeyfile sfdx.key --username $SFDX_HUB_USERNAME --setdefaultdevhubusername -a hub
sf org login jwt --client-id $SFDX_CLIENT_ID --jwt-key-file sfdx.key --username $SFDX_HUB_USERNAME --setdefaultdevhubusername -a hub
env:
SFDX_HUB_KEY_BASE64: ${{ secrets.SFDX_HUB_KEY_BASE64 }}
SFDX_CLIENT_ID: ${{ secrets.SFDX_CLIENT_ID }}
Expand All @@ -110,7 +110,7 @@ jobs:
cci org scratch_delete ${{ matrix.org-shape }}
- name: Store robot results
if: failure()
uses: actions/upload-artifact@v1
uses: actions/upload-artifact@v4
with:
name: robot
path: robot/CumulusCI/results
2 changes: 1 addition & 1 deletion cumulusci/__about__.py
Original file line number Diff line number Diff line change
@@ -1 +1 @@
__version__ = "3.91.0"
__version__ = "3.93.0"
6 changes: 5 additions & 1 deletion cumulusci/cumulusci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,10 @@ tasks:
description: Waits on a batch apex or queueable apex job to finish.
class_path: cumulusci.tasks.apex.batch.BatchApexWait
group: Salesforce
check_components:
description: "Check if common components exist in the target org based on provided deploy paths or those from a plan/flow."
class_path: cumulusci.tasks.salesforce.check_components.CheckComponents
group: Salesforce Preflight Checks
check_dataset_load:
description: Runs as a preflight check to determine whether dataset can be loaded successfully.
class_path: cumulusci.tasks.preflight.dataset_load.LoadDataSetCheck
Expand Down Expand Up @@ -1496,7 +1500,7 @@ project:
namespace:
install_class:
uninstall_class:
api_version: "61.0"
api_version: "62.0"
git:
default_branch: master
prefix_feature: feature/
Expand Down
7 changes: 6 additions & 1 deletion cumulusci/robotframework/form_handlers.py
Original file line number Diff line number Diff line change
Expand Up @@ -160,7 +160,12 @@ def clear(self):
class LightningInputHandler(BaseFormHandler):
"""An input handler for components that can be treated as an input or textarea"""

tags = ["lightning-input", "lightning-textarea", "lightning-datepicker"]
tags = [
"lightning-primitive-input-checkbox",
"lightning-primitive-input-simple",
"lightning-textarea",
"lightning-datepicker",
]

def set(self, value):
self.focus()
Expand Down
23 changes: 23 additions & 0 deletions cumulusci/tasks/metadata/package.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
import urllib.parse
from logging import Logger, getLogger
from pathlib import Path
from typing import Dict, List

import yaml

Expand Down Expand Up @@ -40,6 +41,28 @@ def metadata_sort_key_section(name):
return key


def process_common_components(response_messages: List, components: Dict):
"""Compare compoents in the api responce object with list of components and return common common components"""
if not response_messages or not components:
return components

for message in response_messages:
message_list = message.firstChild.nextSibling.firstChild.nodeValue.split("'")
if len(message_list) > 1:
component_type = message_list[1]
message_txt = message_list[2]

if "is not available in this organization" in message_txt:
del components[component_type]
else:
component_name = message_list[3]
if component_name in components[component_type]:
components[component_type].remove(component_name)
if len(components[component_type]) == 0:
del components[component_type]
return components


class MetadataParserMissingError(Exception):
pass

Expand Down
76 changes: 76 additions & 0 deletions cumulusci/tasks/metadata/tests/test_package.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
from unittest import mock

import pytest
from defusedxml.minidom import parseString

from cumulusci.core.config import (
BaseProjectConfig,
Expand All @@ -27,6 +28,7 @@
RecordTypeParser,
UpdatePackageXml,
metadata_sort_key,
process_common_components,
)
from cumulusci.utils import temporary_dir, touch

Expand Down Expand Up @@ -398,3 +400,77 @@ def test_run_task(self):
with open(output_path, "r") as f:
result = f.read()
assert expected == result


class TestProcessComponents:
response = """<?xml version="1.0" encoding="UTF-8"?>
<soapenv:Envelope
xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/"
xmlns="http://soap.sforce.com/2006/04/metadata">
<soapenv:Body> <checkRetrieveStatusResponse>
<result>
<done>true</done>
<fileProperties>
<createdById>0058N000006PycGQAS</createdById>
<createdByName>User User</createdByName>
<createdDate>2024-10-08T22:54:34.372Z</createdDate>
<fileName>unpackaged/labels/CustomLabels.labels</fileName>
<fullName>CustomLabels</fullName>
<id>000000000000000AAA</id>
<lastModifiedById>0058N000006PycGQAS</lastModifiedById>
<lastModifiedByName>User User</lastModifiedByName>
<lastModifiedDate>2024-10-08T22:54:34.372Z</lastModifiedDate>
<type>CustomLabels</type>
</fileProperties>
<id>09S8N000002vlujUAA</id>
<messages>
<problem>Entity of type 'ApexClass' 'TestClass' cannot be found</problem>
<fileName>unpackaged/package.xml</fileName>
</messages>
<messages>
<problem>Entity of type 'CustomObject' 'TestObject' cannot be found</problem>
<fileName>unpackaged/package.xml</fileName>
</messages>
<messages>
<problem>Entity of type 'CustomObject' 'AnotherObject' cannot be found</problem>
<fileName>unpackaged/package.xml</fileName>
</messages>
</result></checkRetrieveStatusResponse></soapenv:Body></soapenv:Envelope>
"""

def test_process_common_components(self):

response_messages = parseString(self.response).getElementsByTagName("messages")

components = {
"ApexClass": {"TestClass", "AnotherClass"},
"CustomObject": {"TestObject", "AnotherObject"},
}

result = process_common_components(response_messages, components)

expected_components = {
"ApexClass": {"AnotherClass"},
}

assert result == expected_components
assert "ApexClass" in result
assert "AnotherClass" in result["ApexClass"]
assert "TestClass" not in result["ApexClass"]
assert "CustomObject" not in result

def test_process_common_components_no_response_messages(self):
components = {
"ApexClass": {"TestClass", "AnotherClass"},
"CustomObject": {"TestObject", "AnotherObject"},
}

result = process_common_components([], components)

# If there are no response messages, the components list should remain unchanged
assert result == components

def test_process_common_components_no_components(self):
response_messages = parseString(self.response).getElementsByTagName("messages")
result = process_common_components(response_messages, {})
assert result == {}
7 changes: 5 additions & 2 deletions cumulusci/tasks/preflight/permsets.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,11 +9,14 @@ def _run_task(self):
for result in self.sf.query_all(query)["records"]:
if result["PermissionSet"]["Name"] not in self.return_values:
self.return_values.append(result["PermissionSet"]["Name"])

if result["PermissionSetGroupId"] is not None:
psg_query = f"SELECT PermissionSet.Name from PermissionSetGroupComponent where PermissionSetGroupId = '{result['PermissionSetGroupId']}'"
for psg_result in self.sf.query_all(psg_query)["records"]:
if psg_result["PermissionSet"]["Name"] not in self.return_values:
if (
psg_result["PermissionSet"]
and psg_result["PermissionSet"]["Name"]
not in self.return_values
):
self.return_values.append(psg_result["PermissionSet"]["Name"])

permsets_str = "\n".join(self.return_values)
Expand Down
24 changes: 4 additions & 20 deletions cumulusci/tasks/salesforce/Deploy.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
from cumulusci.salesforce_api.metadata import ApiDeploy, ApiRetrieveUnpackaged
from cumulusci.salesforce_api.package_zip import MetadataPackageZipBuilder
from cumulusci.salesforce_api.rest_deploy import RestDeploy
from cumulusci.tasks.metadata.package import process_common_components
from cumulusci.tasks.salesforce.BaseSalesforceMetadataApiTask import (
BaseSalesforceMetadataApiTask,
)
Expand Down Expand Up @@ -169,38 +170,21 @@ def _create_api_object(self, package_xml, api_version):
return api_retrieve_unpackaged_object

def _collision_check(self, src_path):
xml_map = {}
is_collision = False
package_xml = open(f"{src_path}/package.xml", "r")
source_xml_tree = metadata_tree.parse(f"{src_path}/package.xml")

for type in source_xml_tree.types:
members = []
try:
for member in type.members:
members.append(member.text)
except AttributeError: # Exception if there are no members for a type
pass
xml_map[type["name"].text] = members

api_retrieve_unpackaged_response = self._create_api_object(
package_xml.read(), source_xml_tree.version.text
)

xml_map = metadata_tree.parse_package_xml_types("name", source_xml_tree)

messages = parseString(
api_retrieve_unpackaged_response._get_response().content
).getElementsByTagName("messages")

for i in range(len(messages)):
# print(messages[i])
message_list = messages[
i
].firstChild.nextSibling.firstChild.nodeValue.split("'")

if message_list[3] in xml_map[message_list[1]]:
xml_map[message_list[1]].remove(message_list[3])
if len(xml_map[message_list[1]]) == 0:
del xml_map[message_list[1]]
process_common_components(messages, xml_map)

for type, api_names in xml_map.items():
if len(api_names) != 0:
Expand Down
3 changes: 2 additions & 1 deletion cumulusci/tasks/salesforce/EnsureRecordTypes.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import os
import re
from xml.sax.saxutils import escape

from cumulusci.core.exceptions import TaskOptionsError
from cumulusci.core.utils import process_bool_arg
Expand Down Expand Up @@ -152,7 +153,7 @@ def _build_package(self):
record_type_developer_name=self.options[
"record_type_developer_name"
],
stage_name=self.options["stage_name"],
stage_name=escape(self.options["stage_name"]),
default=default,
)
business_process_link = BUSINESS_PROCESS_LINK.format(
Expand Down
Loading

0 comments on commit 4acbd07

Please sign in to comment.