cumulusci-plus 5.0.18__py3-none-any.whl → 5.0.19__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- cumulusci/__about__.py +1 -1
- cumulusci/core/dependencies/base.py +77 -33
- cumulusci/core/dependencies/dependencies.py +27 -17
- cumulusci/core/dependencies/tests/test_dependencies.py +12 -6
- cumulusci/core/dependencies/tests/test_resolvers.py +61 -27
- cumulusci/cumulusci.yml +0 -4
- cumulusci/tasks/salesforce/SfDataCommands.py +234 -0
- cumulusci/tasks/salesforce/profiles.py +36 -7
- cumulusci/tasks/salesforce/tests/test_profiles.py +125 -1
- cumulusci/tasks/salesforce/tests/test_update_dependencies.py +1 -1
- cumulusci/tasks/salesforce/update_dependencies.py +9 -3
- {cumulusci_plus-5.0.18.dist-info → cumulusci_plus-5.0.19.dist-info}/METADATA +4 -4
- {cumulusci_plus-5.0.18.dist-info → cumulusci_plus-5.0.19.dist-info}/RECORD +17 -19
- cumulusci/tasks/utility/data_management.py +0 -16
- cumulusci/tasks/utility/tests/test_data_management.py +0 -43
- {cumulusci_plus-5.0.18.dist-info → cumulusci_plus-5.0.19.dist-info}/WHEEL +0 -0
- {cumulusci_plus-5.0.18.dist-info → cumulusci_plus-5.0.19.dist-info}/entry_points.txt +0 -0
- {cumulusci_plus-5.0.18.dist-info → cumulusci_plus-5.0.19.dist-info}/licenses/AUTHORS.rst +0 -0
- {cumulusci_plus-5.0.18.dist-info → cumulusci_plus-5.0.19.dist-info}/licenses/LICENSE +0 -0
cumulusci/__about__.py
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
__version__ = "5.0.
|
|
1
|
+
__version__ = "5.0.19"
|
|
@@ -255,7 +255,9 @@ class UnmanagedDependency(UnmanagedStaticDependency, ABC):
|
|
|
255
255
|
|
|
256
256
|
return package_zip
|
|
257
257
|
|
|
258
|
-
def install(
|
|
258
|
+
def install(
|
|
259
|
+
self, context: BaseProjectConfig, org: OrgConfig, options: Optional[dict] = {}
|
|
260
|
+
):
|
|
259
261
|
|
|
260
262
|
context.logger.info(f"Deploying unmanaged metadata from {self.description}")
|
|
261
263
|
|
|
@@ -411,6 +413,7 @@ class VcsDynamicDependency(BaseVcsDynamicDependency, ABC):
|
|
|
411
413
|
unmanaged=not managed,
|
|
412
414
|
namespace_inject=namespace if namespace and managed else None,
|
|
413
415
|
namespace_strip=namespace if namespace and not managed else None,
|
|
416
|
+
package_dependency=self.package_dependency,
|
|
414
417
|
)
|
|
415
418
|
]
|
|
416
419
|
return []
|
|
@@ -446,6 +449,7 @@ class VcsDynamicDependency(BaseVcsDynamicDependency, ABC):
|
|
|
446
449
|
namespace_strip=namespace
|
|
447
450
|
if namespace and not managed
|
|
448
451
|
else None,
|
|
452
|
+
package_dependency=self.package_dependency,
|
|
449
453
|
)
|
|
450
454
|
)
|
|
451
455
|
|
|
@@ -493,22 +497,22 @@ class VcsDynamicDependency(BaseVcsDynamicDependency, ABC):
|
|
|
493
497
|
|
|
494
498
|
# Look for any flow to executed in project config
|
|
495
499
|
# Pre flows will run to dynamically generate metadata and deploy.
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
namespace=namespace,
|
|
502
|
-
)
|
|
500
|
+
flow = self._flatten_dependency_flow(
|
|
501
|
+
package_config,
|
|
502
|
+
"dependency_flow_pre",
|
|
503
|
+
managed=False,
|
|
504
|
+
namespace=namespace,
|
|
503
505
|
)
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
506
|
+
if flow:
|
|
507
|
+
deps.extend(flow)
|
|
508
|
+
else:
|
|
509
|
+
# Look for subfolders under unpackaged/pre
|
|
510
|
+
# unpackaged/pre is always deployed unmanaged, no namespace manipulation.
|
|
511
|
+
deps.extend(
|
|
512
|
+
self._flatten_unpackaged(
|
|
513
|
+
repo, "unpackaged/pre", self.skip, managed=False, namespace=None
|
|
514
|
+
)
|
|
510
515
|
)
|
|
511
|
-
)
|
|
512
516
|
|
|
513
517
|
if not self.package_dependency:
|
|
514
518
|
if managed:
|
|
@@ -532,25 +536,26 @@ class VcsDynamicDependency(BaseVcsDynamicDependency, ABC):
|
|
|
532
536
|
|
|
533
537
|
# Look for any flow to executed in project config
|
|
534
538
|
# Pre flows will run to dynamically generate metadata and deploy.
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
namespace=namespace,
|
|
541
|
-
)
|
|
539
|
+
flow = self._flatten_dependency_flow(
|
|
540
|
+
package_config,
|
|
541
|
+
"dependency_flow_post",
|
|
542
|
+
managed=managed,
|
|
543
|
+
namespace=namespace,
|
|
542
544
|
)
|
|
543
545
|
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
self.
|
|
550
|
-
|
|
551
|
-
|
|
546
|
+
if flow:
|
|
547
|
+
deps.extend(flow)
|
|
548
|
+
else:
|
|
549
|
+
# We always inject the project's namespace into unpackaged/post metadata if managed
|
|
550
|
+
deps.extend(
|
|
551
|
+
self._flatten_unpackaged(
|
|
552
|
+
repo,
|
|
553
|
+
"unpackaged/post",
|
|
554
|
+
self.skip,
|
|
555
|
+
managed=managed,
|
|
556
|
+
namespace=namespace,
|
|
557
|
+
)
|
|
552
558
|
)
|
|
553
|
-
)
|
|
554
559
|
|
|
555
560
|
return deps
|
|
556
561
|
|
|
@@ -574,6 +579,9 @@ class UnmanagedVcsDependency(UnmanagedDependency, ABC):
|
|
|
574
579
|
namespace_inject: Optional[str] = None
|
|
575
580
|
namespace_strip: Optional[str] = None
|
|
576
581
|
|
|
582
|
+
# Field to reference corresponding package dependency
|
|
583
|
+
package_dependency: Optional["BasePackageVersionDependency"] = None
|
|
584
|
+
|
|
577
585
|
@root_validator(pre=True)
|
|
578
586
|
@abstractmethod
|
|
579
587
|
def sync_vcs_and_url(cls, values):
|
|
@@ -619,6 +627,19 @@ class UnmanagedVcsDependency(UnmanagedDependency, ABC):
|
|
|
619
627
|
|
|
620
628
|
return f"{self.url}{subfolder} @{self.ref}"
|
|
621
629
|
|
|
630
|
+
def install(
|
|
631
|
+
self, context: BaseProjectConfig, org: OrgConfig, options: Optional[dict] = {}
|
|
632
|
+
):
|
|
633
|
+
if (
|
|
634
|
+
self.package_dependency is not None
|
|
635
|
+
and not self.package_dependency.is_installable(org, options)
|
|
636
|
+
):
|
|
637
|
+
context.logger.info(
|
|
638
|
+
f"{self.description} or a newer version is already deployed; skipping."
|
|
639
|
+
)
|
|
640
|
+
return
|
|
641
|
+
super().install(context, org)
|
|
642
|
+
|
|
622
643
|
|
|
623
644
|
class UnmanagedVcsDependencyFlow(UnmanagedStaticDependency, ABC):
|
|
624
645
|
vcs: str
|
|
@@ -632,6 +653,9 @@ class UnmanagedVcsDependencyFlow(UnmanagedStaticDependency, ABC):
|
|
|
632
653
|
namespace_strip: Optional[str] = None
|
|
633
654
|
password_env_name: Optional[str] = None
|
|
634
655
|
|
|
656
|
+
# Field to reference corresponding package dependency
|
|
657
|
+
package_dependency: Optional["BasePackageVersionDependency"] = None
|
|
658
|
+
|
|
635
659
|
@property
|
|
636
660
|
def name(self):
|
|
637
661
|
return f"Deploy {self.url} Flow: {self.flow_name}"
|
|
@@ -640,7 +664,18 @@ class UnmanagedVcsDependencyFlow(UnmanagedStaticDependency, ABC):
|
|
|
640
664
|
def description(self):
|
|
641
665
|
return f"{self.url} Flow: {self.flow_name} @{self.commit}"
|
|
642
666
|
|
|
643
|
-
def install(
|
|
667
|
+
def install(
|
|
668
|
+
self, context: BaseProjectConfig, org: OrgConfig, options: Optional[dict] = {}
|
|
669
|
+
):
|
|
670
|
+
if (
|
|
671
|
+
self.package_dependency is not None
|
|
672
|
+
and not self.package_dependency.is_installable(org, options)
|
|
673
|
+
):
|
|
674
|
+
context.logger.info(
|
|
675
|
+
f"{self.description} or a newer version is already deployed; skipping."
|
|
676
|
+
)
|
|
677
|
+
return
|
|
678
|
+
|
|
644
679
|
context.logger.info(f"Deploying dependency Flow from {self.description}")
|
|
645
680
|
|
|
646
681
|
from cumulusci.utils.yaml.cumulusci_yml import VCSSourceModel
|
|
@@ -656,7 +691,6 @@ class UnmanagedVcsDependencyFlow(UnmanagedStaticDependency, ABC):
|
|
|
656
691
|
vcs_source = VCSSource.create(context, source_model)
|
|
657
692
|
|
|
658
693
|
# Fetch the data and get remote project config.
|
|
659
|
-
context.logger.info(f"Fetching from {vcs_source}")
|
|
660
694
|
project_config = vcs_source.fetch()
|
|
661
695
|
|
|
662
696
|
project_config.set_keychain(context.keychain)
|
|
@@ -708,3 +742,13 @@ class UnmanagedVcsDependencyFlow(UnmanagedStaticDependency, ABC):
|
|
|
708
742
|
coordinator.run(org)
|
|
709
743
|
duration = datetime.now() - start_time
|
|
710
744
|
context.logger.info(f"Ran {self.flow_name} in {format_duration(duration)}")
|
|
745
|
+
|
|
746
|
+
|
|
747
|
+
class BasePackageVersionDependency(StaticDependency, ABC):
|
|
748
|
+
@abstractmethod
|
|
749
|
+
def is_installable(self, org: OrgConfig, options: Optional[dict] = {}) -> bool:
|
|
750
|
+
pass
|
|
751
|
+
|
|
752
|
+
|
|
753
|
+
UnmanagedVcsDependency.update_forward_refs()
|
|
754
|
+
UnmanagedVcsDependencyFlow.update_forward_refs()
|
|
@@ -20,7 +20,7 @@ from cumulusci.utils import download_extract_zip
|
|
|
20
20
|
logger = logging.getLogger(__name__)
|
|
21
21
|
|
|
22
22
|
|
|
23
|
-
class PackageNamespaceVersionDependency(base_dependency.
|
|
23
|
+
class PackageNamespaceVersionDependency(base_dependency.BasePackageVersionDependency):
|
|
24
24
|
"""Static dependency on a package identified by namespace and version number."""
|
|
25
25
|
|
|
26
26
|
namespace: str
|
|
@@ -34,6 +34,21 @@ class PackageNamespaceVersionDependency(base_dependency.StaticDependency):
|
|
|
34
34
|
def package(self):
|
|
35
35
|
return self.package_name or self.namespace or "Unknown Package"
|
|
36
36
|
|
|
37
|
+
def is_installable(self, org: OrgConfig, options: Optional[dict] = {}) -> bool:
|
|
38
|
+
if "Beta" in self.version:
|
|
39
|
+
version_string = self.version.split(" ")[0]
|
|
40
|
+
beta = self.version.split(" ")[-1].strip(")")
|
|
41
|
+
version = f"{version_string}b{beta}"
|
|
42
|
+
else:
|
|
43
|
+
version = self.version
|
|
44
|
+
|
|
45
|
+
if org.has_minimum_package_version(
|
|
46
|
+
self.namespace,
|
|
47
|
+
version,
|
|
48
|
+
):
|
|
49
|
+
return False
|
|
50
|
+
return True
|
|
51
|
+
|
|
37
52
|
def install(
|
|
38
53
|
self,
|
|
39
54
|
context: BaseProjectConfig,
|
|
@@ -48,17 +63,7 @@ class PackageNamespaceVersionDependency(base_dependency.StaticDependency):
|
|
|
48
63
|
if not retry_options:
|
|
49
64
|
retry_options = DEFAULT_PACKAGE_RETRY_OPTIONS
|
|
50
65
|
|
|
51
|
-
if
|
|
52
|
-
version_string = self.version.split(" ")[0]
|
|
53
|
-
beta = self.version.split(" ")[-1].strip(")")
|
|
54
|
-
version = f"{version_string}b{beta}"
|
|
55
|
-
else:
|
|
56
|
-
version = self.version
|
|
57
|
-
|
|
58
|
-
if org.has_minimum_package_version(
|
|
59
|
-
self.namespace,
|
|
60
|
-
version,
|
|
61
|
-
):
|
|
66
|
+
if not self.is_installable(org):
|
|
62
67
|
context.logger.info(
|
|
63
68
|
f"{self} or a newer version is already installed; skipping."
|
|
64
69
|
)
|
|
@@ -83,7 +88,7 @@ class PackageNamespaceVersionDependency(base_dependency.StaticDependency):
|
|
|
83
88
|
return f"{self.package} {self.version}"
|
|
84
89
|
|
|
85
90
|
|
|
86
|
-
class PackageVersionIdDependency(base_dependency.
|
|
91
|
+
class PackageVersionIdDependency(base_dependency.BasePackageVersionDependency):
|
|
87
92
|
"""Static dependency on a package identified by 04t version id."""
|
|
88
93
|
|
|
89
94
|
version_id: str
|
|
@@ -96,6 +101,14 @@ class PackageVersionIdDependency(base_dependency.StaticDependency):
|
|
|
96
101
|
def package(self):
|
|
97
102
|
return self.package_name or "Unknown Package"
|
|
98
103
|
|
|
104
|
+
def is_installable(self, org: OrgConfig, options: Optional[dict] = {}) -> bool:
|
|
105
|
+
if any(
|
|
106
|
+
self.version_id == v.id
|
|
107
|
+
for v in itertools.chain(*org.installed_packages.values())
|
|
108
|
+
) and not options.get("force_pre_post_install"):
|
|
109
|
+
return False
|
|
110
|
+
return True
|
|
111
|
+
|
|
99
112
|
def install(
|
|
100
113
|
self,
|
|
101
114
|
context: BaseProjectConfig,
|
|
@@ -110,10 +123,7 @@ class PackageVersionIdDependency(base_dependency.StaticDependency):
|
|
|
110
123
|
if not retry_options:
|
|
111
124
|
retry_options = DEFAULT_PACKAGE_RETRY_OPTIONS
|
|
112
125
|
|
|
113
|
-
if
|
|
114
|
-
self.version_id == v.id
|
|
115
|
-
for v in itertools.chain(*org.installed_packages.values())
|
|
116
|
-
):
|
|
126
|
+
if not self.is_installable(org):
|
|
117
127
|
context.logger.info(
|
|
118
128
|
f"{self} or a newer version is already installed; skipping."
|
|
119
129
|
)
|
|
@@ -256,6 +256,8 @@ class TestGitHubDynamicDependency:
|
|
|
256
256
|
namespace="bar", version="2.0"
|
|
257
257
|
)
|
|
258
258
|
|
|
259
|
+
root_repo = PackageNamespaceVersionDependency(namespace="bar", version="2.0")
|
|
260
|
+
|
|
259
261
|
assert gh.flatten(project_config) == [
|
|
260
262
|
GitHubDynamicDependency(
|
|
261
263
|
github="https://github.com/SFDO-Tooling/DependencyRepo",
|
|
@@ -266,20 +268,23 @@ class TestGitHubDynamicDependency:
|
|
|
266
268
|
subfolder="unpackaged/pre/first",
|
|
267
269
|
unmanaged=True,
|
|
268
270
|
ref="aaaaa",
|
|
271
|
+
package_dependency=root_repo,
|
|
269
272
|
),
|
|
270
273
|
UnmanagedGitHubRefDependency(
|
|
271
274
|
github="https://github.com/SFDO-Tooling/RootRepo",
|
|
272
275
|
subfolder="unpackaged/pre/second",
|
|
273
276
|
unmanaged=True,
|
|
274
277
|
ref="aaaaa",
|
|
278
|
+
package_dependency=root_repo,
|
|
275
279
|
),
|
|
276
|
-
|
|
280
|
+
root_repo,
|
|
277
281
|
UnmanagedGitHubRefDependency(
|
|
278
282
|
github="https://github.com/SFDO-Tooling/RootRepo",
|
|
279
283
|
subfolder="unpackaged/post/first",
|
|
280
284
|
unmanaged=False,
|
|
281
285
|
ref="aaaaa",
|
|
282
286
|
namespace_inject="bar",
|
|
287
|
+
package_dependency=root_repo,
|
|
283
288
|
),
|
|
284
289
|
]
|
|
285
290
|
|
|
@@ -295,6 +300,8 @@ class TestGitHubDynamicDependency:
|
|
|
295
300
|
namespace="bar", version="2.0"
|
|
296
301
|
)
|
|
297
302
|
|
|
303
|
+
root_repo = PackageNamespaceVersionDependency(namespace="bar", version="2.0")
|
|
304
|
+
|
|
298
305
|
assert gh.flatten(project_config) == [
|
|
299
306
|
GitHubDynamicDependency(
|
|
300
307
|
github="https://github.com/SFDO-Tooling/DependencyRepo",
|
|
@@ -305,14 +312,16 @@ class TestGitHubDynamicDependency:
|
|
|
305
312
|
subfolder="unpackaged/pre/second",
|
|
306
313
|
unmanaged=True,
|
|
307
314
|
ref="aaaaa",
|
|
315
|
+
package_dependency=root_repo,
|
|
308
316
|
),
|
|
309
|
-
|
|
317
|
+
root_repo,
|
|
310
318
|
UnmanagedGitHubRefDependency(
|
|
311
319
|
github="https://github.com/SFDO-Tooling/RootRepo",
|
|
312
320
|
subfolder="unpackaged/post/first",
|
|
313
321
|
unmanaged=False,
|
|
314
322
|
ref="aaaaa",
|
|
315
323
|
namespace_inject="bar",
|
|
324
|
+
package_dependency=root_repo,
|
|
316
325
|
),
|
|
317
326
|
]
|
|
318
327
|
|
|
@@ -1086,14 +1095,11 @@ class TestUnmanagedVcsDependencyFlow:
|
|
|
1086
1095
|
|
|
1087
1096
|
# Verify logging
|
|
1088
1097
|
assert (
|
|
1089
|
-
mock_context.logger.info.call_count ==
|
|
1098
|
+
mock_context.logger.info.call_count == 2
|
|
1090
1099
|
) # Initial log, fetching log, final log
|
|
1091
1100
|
mock_context.logger.info.assert_any_call(
|
|
1092
1101
|
"Deploying dependency Flow from https://github.com/test/repo Flow: install_deps @abc123"
|
|
1093
1102
|
)
|
|
1094
|
-
mock_context.logger.info.assert_any_call(
|
|
1095
|
-
f"Fetching from {mock_vcs_source_instance}"
|
|
1096
|
-
)
|
|
1097
1103
|
mock_context.logger.info.assert_any_call("Ran install_deps in 5.2s")
|
|
1098
1104
|
|
|
1099
1105
|
# Verify datetime and format_duration calls
|
|
@@ -767,6 +767,16 @@ class TestStaticDependencyResolution:
|
|
|
767
767
|
|
|
768
768
|
gh = GitHubDynamicDependency(github="https://github.com/SFDO-Tooling/RootRepo")
|
|
769
769
|
|
|
770
|
+
dep_repo = PackageNamespaceVersionDependency(
|
|
771
|
+
namespace="foo",
|
|
772
|
+
version="1.1",
|
|
773
|
+
package_name="DependencyRepo",
|
|
774
|
+
password_env_name="DEP_PW",
|
|
775
|
+
)
|
|
776
|
+
root_repo = PackageNamespaceVersionDependency(
|
|
777
|
+
namespace="bar", version="2.0", package_name="RootRepo"
|
|
778
|
+
)
|
|
779
|
+
|
|
770
780
|
assert get_static_dependencies(
|
|
771
781
|
project_config,
|
|
772
782
|
dependencies=[gh],
|
|
@@ -777,41 +787,39 @@ class TestStaticDependencyResolution:
|
|
|
777
787
|
subfolder="unpackaged/pre/top",
|
|
778
788
|
unmanaged=True,
|
|
779
789
|
ref="tag_sha",
|
|
790
|
+
package_dependency=dep_repo,
|
|
780
791
|
),
|
|
781
|
-
|
|
782
|
-
namespace="foo",
|
|
783
|
-
version="1.1",
|
|
784
|
-
package_name="DependencyRepo",
|
|
785
|
-
password_env_name="DEP_PW",
|
|
786
|
-
),
|
|
792
|
+
dep_repo,
|
|
787
793
|
UnmanagedGitHubRefDependency(
|
|
788
794
|
github="https://github.com/SFDO-Tooling/DependencyRepo",
|
|
789
795
|
subfolder="unpackaged/post/top",
|
|
790
796
|
unmanaged=False,
|
|
791
797
|
ref="tag_sha",
|
|
792
798
|
namespace_inject="foo",
|
|
799
|
+
package_dependency=dep_repo,
|
|
793
800
|
),
|
|
794
801
|
UnmanagedGitHubRefDependency(
|
|
795
802
|
github="https://github.com/SFDO-Tooling/RootRepo",
|
|
796
803
|
subfolder="unpackaged/pre/first",
|
|
797
804
|
unmanaged=True,
|
|
798
805
|
ref="tag_sha",
|
|
806
|
+
package_dependency=root_repo,
|
|
799
807
|
),
|
|
800
808
|
UnmanagedGitHubRefDependency(
|
|
801
809
|
github="https://github.com/SFDO-Tooling/RootRepo",
|
|
802
810
|
subfolder="unpackaged/pre/second",
|
|
803
811
|
unmanaged=True,
|
|
804
812
|
ref="tag_sha",
|
|
813
|
+
package_dependency=root_repo,
|
|
805
814
|
),
|
|
806
|
-
|
|
807
|
-
namespace="bar", version="2.0", package_name="RootRepo"
|
|
808
|
-
),
|
|
815
|
+
root_repo,
|
|
809
816
|
UnmanagedGitHubRefDependency(
|
|
810
817
|
github="https://github.com/SFDO-Tooling/RootRepo",
|
|
811
818
|
subfolder="unpackaged/post/first",
|
|
812
819
|
unmanaged=False,
|
|
813
820
|
ref="tag_sha",
|
|
814
821
|
namespace_inject="bar",
|
|
822
|
+
package_dependency=root_repo,
|
|
815
823
|
),
|
|
816
824
|
]
|
|
817
825
|
|
|
@@ -855,50 +863,58 @@ version_id: 04t000000000000""",
|
|
|
855
863
|
pins=pins,
|
|
856
864
|
)
|
|
857
865
|
|
|
866
|
+
dep_repo = PackageNamespaceVersionDependency(
|
|
867
|
+
namespace="foo",
|
|
868
|
+
version="1.0", # from the pinned tag
|
|
869
|
+
package_name="DependencyRepo",
|
|
870
|
+
version_id="04t000000000000",
|
|
871
|
+
)
|
|
872
|
+
root_repo = PackageNamespaceVersionDependency(
|
|
873
|
+
namespace="bar",
|
|
874
|
+
version="1.5", # From pinned tag
|
|
875
|
+
package_name="RootRepo",
|
|
876
|
+
version_id="04t000000000000",
|
|
877
|
+
)
|
|
878
|
+
|
|
858
879
|
assert deps == [
|
|
859
880
|
UnmanagedGitHubRefDependency(
|
|
860
881
|
github="https://github.com/SFDO-Tooling/DependencyRepo",
|
|
861
882
|
subfolder="unpackaged/pre/top",
|
|
862
883
|
unmanaged=True,
|
|
863
884
|
ref="tag_sha",
|
|
885
|
+
package_dependency=dep_repo,
|
|
864
886
|
),
|
|
865
|
-
|
|
866
|
-
namespace="foo",
|
|
867
|
-
version="1.0", # from the pinned tag
|
|
868
|
-
package_name="DependencyRepo",
|
|
869
|
-
version_id="04t000000000000",
|
|
870
|
-
),
|
|
887
|
+
dep_repo,
|
|
871
888
|
UnmanagedGitHubRefDependency(
|
|
872
889
|
github="https://github.com/SFDO-Tooling/DependencyRepo",
|
|
873
890
|
subfolder="unpackaged/post/top",
|
|
874
891
|
unmanaged=False,
|
|
875
892
|
ref="tag_sha",
|
|
876
893
|
namespace_inject="foo",
|
|
894
|
+
package_dependency=dep_repo,
|
|
877
895
|
),
|
|
878
896
|
UnmanagedGitHubRefDependency(
|
|
879
897
|
github="https://github.com/SFDO-Tooling/RootRepo",
|
|
880
898
|
subfolder="unpackaged/pre/first",
|
|
881
899
|
unmanaged=True,
|
|
882
900
|
ref="tag_sha",
|
|
901
|
+
package_dependency=root_repo,
|
|
883
902
|
),
|
|
884
903
|
UnmanagedGitHubRefDependency(
|
|
885
904
|
github="https://github.com/SFDO-Tooling/RootRepo",
|
|
886
905
|
subfolder="unpackaged/pre/second",
|
|
887
906
|
unmanaged=True,
|
|
888
907
|
ref="tag_sha",
|
|
908
|
+
package_dependency=root_repo,
|
|
889
909
|
),
|
|
890
|
-
|
|
891
|
-
namespace="bar",
|
|
892
|
-
version="1.5", # From pinned tag
|
|
893
|
-
package_name="RootRepo",
|
|
894
|
-
version_id="04t000000000000",
|
|
895
|
-
),
|
|
910
|
+
root_repo,
|
|
896
911
|
UnmanagedGitHubRefDependency(
|
|
897
912
|
github="https://github.com/SFDO-Tooling/RootRepo",
|
|
898
913
|
subfolder="unpackaged/post/first",
|
|
899
914
|
unmanaged=False,
|
|
900
915
|
ref="tag_sha",
|
|
901
916
|
namespace_inject="bar",
|
|
917
|
+
package_dependency=root_repo,
|
|
902
918
|
),
|
|
903
919
|
]
|
|
904
920
|
|
|
@@ -938,6 +954,16 @@ version_id: 04t000000000000""",
|
|
|
938
954
|
|
|
939
955
|
gh = GitHubDynamicDependency(github="https://github.com/SFDO-Tooling/RootRepo")
|
|
940
956
|
|
|
957
|
+
root_repo = PackageNamespaceVersionDependency(
|
|
958
|
+
namespace="bar", version="2.0", package_name="RootRepo"
|
|
959
|
+
)
|
|
960
|
+
dep_repo = PackageNamespaceVersionDependency(
|
|
961
|
+
namespace="foo",
|
|
962
|
+
version="1.1", # from the pinned tag
|
|
963
|
+
package_name="DependencyRepo",
|
|
964
|
+
password_env_name="DEP_PW",
|
|
965
|
+
)
|
|
966
|
+
|
|
941
967
|
assert get_static_dependencies(
|
|
942
968
|
project_config,
|
|
943
969
|
dependencies=[gh],
|
|
@@ -949,6 +975,7 @@ version_id: 04t000000000000""",
|
|
|
949
975
|
subfolder="unpackaged/pre/top",
|
|
950
976
|
unmanaged=True,
|
|
951
977
|
ref="tag_sha",
|
|
978
|
+
package_dependency=dep_repo,
|
|
952
979
|
),
|
|
953
980
|
UnmanagedGitHubRefDependency(
|
|
954
981
|
github="https://github.com/SFDO-Tooling/DependencyRepo",
|
|
@@ -956,28 +983,30 @@ version_id: 04t000000000000""",
|
|
|
956
983
|
unmanaged=False,
|
|
957
984
|
ref="tag_sha",
|
|
958
985
|
namespace_inject="foo",
|
|
986
|
+
package_dependency=dep_repo,
|
|
959
987
|
),
|
|
960
988
|
UnmanagedGitHubRefDependency(
|
|
961
989
|
github="https://github.com/SFDO-Tooling/RootRepo",
|
|
962
990
|
subfolder="unpackaged/pre/first",
|
|
963
991
|
unmanaged=True,
|
|
964
992
|
ref="tag_sha",
|
|
993
|
+
package_dependency=root_repo,
|
|
965
994
|
),
|
|
966
995
|
UnmanagedGitHubRefDependency(
|
|
967
996
|
github="https://github.com/SFDO-Tooling/RootRepo",
|
|
968
997
|
subfolder="unpackaged/pre/second",
|
|
969
998
|
unmanaged=True,
|
|
970
999
|
ref="tag_sha",
|
|
1000
|
+
package_dependency=root_repo,
|
|
971
1001
|
),
|
|
972
|
-
|
|
973
|
-
namespace="bar", version="2.0", package_name="RootRepo"
|
|
974
|
-
),
|
|
1002
|
+
root_repo,
|
|
975
1003
|
UnmanagedGitHubRefDependency(
|
|
976
1004
|
github="https://github.com/SFDO-Tooling/RootRepo",
|
|
977
1005
|
subfolder="unpackaged/post/first",
|
|
978
1006
|
unmanaged=False,
|
|
979
1007
|
ref="tag_sha",
|
|
980
1008
|
namespace_inject="bar",
|
|
1009
|
+
package_dependency=root_repo,
|
|
981
1010
|
),
|
|
982
1011
|
]
|
|
983
1012
|
|
|
@@ -994,6 +1023,10 @@ version_id: 04t000000000000""",
|
|
|
994
1023
|
|
|
995
1024
|
gh = GitHubDynamicDependency(github="https://github.com/SFDO-Tooling/RootRepo")
|
|
996
1025
|
|
|
1026
|
+
root_repo = PackageNamespaceVersionDependency(
|
|
1027
|
+
namespace="bar", version="2.0", package_name="RootRepo"
|
|
1028
|
+
)
|
|
1029
|
+
|
|
997
1030
|
assert get_static_dependencies(
|
|
998
1031
|
project_config,
|
|
999
1032
|
dependencies=[gh],
|
|
@@ -1007,21 +1040,22 @@ version_id: 04t000000000000""",
|
|
|
1007
1040
|
subfolder="unpackaged/pre/first",
|
|
1008
1041
|
unmanaged=True,
|
|
1009
1042
|
ref="tag_sha",
|
|
1043
|
+
package_dependency=root_repo,
|
|
1010
1044
|
),
|
|
1011
1045
|
UnmanagedGitHubRefDependency(
|
|
1012
1046
|
github="https://github.com/SFDO-Tooling/RootRepo",
|
|
1013
1047
|
subfolder="unpackaged/pre/second",
|
|
1014
1048
|
unmanaged=True,
|
|
1015
1049
|
ref="tag_sha",
|
|
1050
|
+
package_dependency=root_repo,
|
|
1016
1051
|
),
|
|
1017
|
-
|
|
1018
|
-
namespace="bar", version="2.0", package_name="RootRepo"
|
|
1019
|
-
),
|
|
1052
|
+
root_repo,
|
|
1020
1053
|
UnmanagedGitHubRefDependency(
|
|
1021
1054
|
github="https://github.com/SFDO-Tooling/RootRepo",
|
|
1022
1055
|
subfolder="unpackaged/post/first",
|
|
1023
1056
|
unmanaged=False,
|
|
1024
1057
|
ref="tag_sha",
|
|
1025
1058
|
namespace_inject="bar",
|
|
1059
|
+
package_dependency=root_repo,
|
|
1026
1060
|
),
|
|
1027
1061
|
]
|
cumulusci/cumulusci.yml
CHANGED
|
@@ -823,10 +823,6 @@ tasks:
|
|
|
823
823
|
class_path: cumulusci.tasks.salesforce.SfDataCommands.DataCreateRecordTask
|
|
824
824
|
description: "Executes the `sf data create` command against an org"
|
|
825
825
|
group: SalesforceDX Data Commands
|
|
826
|
-
get_first_item_from_record_list:
|
|
827
|
-
class_path: cumulusci.tasks.utility.data_management.GetFirstItemFromRecordListTask
|
|
828
|
-
description: "Get the first item from a record list"
|
|
829
|
-
group: Utilities
|
|
830
826
|
flows:
|
|
831
827
|
ci_beta:
|
|
832
828
|
group: Continuous Integration
|
|
@@ -164,3 +164,237 @@ class DataDeleteRecordTask(SfDataToolingAPISupportedCommands):
|
|
|
164
164
|
|
|
165
165
|
def _run_task(self):
|
|
166
166
|
return super()._run_task()
|
|
167
|
+
|
|
168
|
+
|
|
169
|
+
class DataUpdateRecordTask(SfDataToolingAPISupportedCommands):
|
|
170
|
+
class Options(SfDataToolingAPISupportedCommands.Options):
|
|
171
|
+
sobject: str = Field(
|
|
172
|
+
...,
|
|
173
|
+
description="API name of the Salesforce or Tooling API object that you're updating a record from.",
|
|
174
|
+
)
|
|
175
|
+
record_id: str = Field(None, description="ID of the record you’re updating.")
|
|
176
|
+
where: str = Field(
|
|
177
|
+
None,
|
|
178
|
+
description="List of <fieldName>=<value> pairs that identify the record you want to update.",
|
|
179
|
+
)
|
|
180
|
+
values: str = Field(
|
|
181
|
+
...,
|
|
182
|
+
description="Values for the flags in the form <fieldName>=<value>, separate multiple pairs with spaces.",
|
|
183
|
+
)
|
|
184
|
+
|
|
185
|
+
def _init_task(self):
|
|
186
|
+
super()._init_task()
|
|
187
|
+
self.data_command += "update record"
|
|
188
|
+
|
|
189
|
+
def _init_options(self, kwargs):
|
|
190
|
+
super()._init_options(kwargs)
|
|
191
|
+
if self.parsed_options.sobject:
|
|
192
|
+
self.args.extend(["--sobject", self.parsed_options.sobject])
|
|
193
|
+
if self.parsed_options.record_id:
|
|
194
|
+
self.args.extend(["--record-id", self.parsed_options.record_id])
|
|
195
|
+
if self.parsed_options.where:
|
|
196
|
+
self.args.extend(["--where", self.parsed_options.where])
|
|
197
|
+
if self.parsed_options.values:
|
|
198
|
+
self.args.extend(["--values", self.parsed_options.values])
|
|
199
|
+
|
|
200
|
+
def _run_task(self):
|
|
201
|
+
return super()._run_task()
|
|
202
|
+
|
|
203
|
+
|
|
204
|
+
class DataGetRecordTask(SfDataToolingAPISupportedCommands):
|
|
205
|
+
class Options(SfDataToolingAPISupportedCommands.Options):
|
|
206
|
+
sobject: str = Field(
|
|
207
|
+
...,
|
|
208
|
+
description="API name of the Salesforce or Tooling API object that you're fetching a record from.",
|
|
209
|
+
)
|
|
210
|
+
record_id: str = Field(None, description="ID of the record you’re fetching.")
|
|
211
|
+
where: str = Field(
|
|
212
|
+
None,
|
|
213
|
+
description="List of <fieldName>=<value> pairs that identify the record you want to fetch.",
|
|
214
|
+
)
|
|
215
|
+
|
|
216
|
+
def _init_task(self):
|
|
217
|
+
super()._init_task()
|
|
218
|
+
self.data_command += "get record"
|
|
219
|
+
|
|
220
|
+
def _init_options(self, kwargs):
|
|
221
|
+
super()._init_options(kwargs)
|
|
222
|
+
if self.parsed_options.sobject:
|
|
223
|
+
self.args.extend(["--sobject", self.parsed_options.sobject])
|
|
224
|
+
if self.parsed_options.record_id:
|
|
225
|
+
self.args.extend(["--record-id", self.parsed_options.record_id])
|
|
226
|
+
if self.parsed_options.where:
|
|
227
|
+
self.args.extend(["--where", self.parsed_options.where])
|
|
228
|
+
|
|
229
|
+
def _run_task(self):
|
|
230
|
+
return super()._run_task()
|
|
231
|
+
|
|
232
|
+
|
|
233
|
+
class DataQueryResumeTask(SfDataToolingAPISupportedCommands):
|
|
234
|
+
class Options(SfDataToolingAPISupportedCommands.Options):
|
|
235
|
+
bulk_query_id: str = Field(
|
|
236
|
+
...,
|
|
237
|
+
description="The 18-character ID of the bulk query to resume.",
|
|
238
|
+
)
|
|
239
|
+
result_format: str = Field(
|
|
240
|
+
None,
|
|
241
|
+
description="Format to display the results; the --json_output flag overrides this flag. Permissible values are: human, csv, json.",
|
|
242
|
+
)
|
|
243
|
+
|
|
244
|
+
def _init_task(self):
|
|
245
|
+
super()._init_task()
|
|
246
|
+
self.data_command += "query resume"
|
|
247
|
+
|
|
248
|
+
def _init_options(self, kwargs):
|
|
249
|
+
super()._init_options(kwargs)
|
|
250
|
+
if self.parsed_options.bulk_query_id:
|
|
251
|
+
self.args.extend(["--bulk-query-id", self.parsed_options.bulk_query_id])
|
|
252
|
+
if self.parsed_options.result_format:
|
|
253
|
+
self.args.extend(["--result-format", self.parsed_options.result_format])
|
|
254
|
+
|
|
255
|
+
def _run_task(self):
|
|
256
|
+
return super()._run_task()
|
|
257
|
+
|
|
258
|
+
|
|
259
|
+
class DataDeleteBulkTask(SfDataCommands):
|
|
260
|
+
class Options(SfDataCommands.Options):
|
|
261
|
+
sobject: str = Field(
|
|
262
|
+
...,
|
|
263
|
+
description="The API name of the object for the bulk job.",
|
|
264
|
+
)
|
|
265
|
+
file: str = Field(
|
|
266
|
+
...,
|
|
267
|
+
description="The path to the CSV file that contains the IDs of the records to delete.",
|
|
268
|
+
)
|
|
269
|
+
wait: int = Field(
|
|
270
|
+
None,
|
|
271
|
+
description="The number of minutes to wait for the command to complete.",
|
|
272
|
+
)
|
|
273
|
+
|
|
274
|
+
def _init_task(self):
|
|
275
|
+
super()._init_task()
|
|
276
|
+
self.data_command += "delete bulk"
|
|
277
|
+
|
|
278
|
+
def _init_options(self, kwargs):
|
|
279
|
+
super()._init_options(kwargs)
|
|
280
|
+
if self.parsed_options.sobject:
|
|
281
|
+
self.args.extend(["--sobject", self.parsed_options.sobject])
|
|
282
|
+
if self.parsed_options.file:
|
|
283
|
+
self.args.extend(["--file", self.parsed_options.file])
|
|
284
|
+
if self.parsed_options.wait:
|
|
285
|
+
self.args.extend(["--wait", str(self.parsed_options.wait)])
|
|
286
|
+
|
|
287
|
+
def _run_task(self):
|
|
288
|
+
return super()._run_task()
|
|
289
|
+
|
|
290
|
+
|
|
291
|
+
class DataUpsertBulkTask(SfDataCommands):
|
|
292
|
+
class Options(SfDataCommands.Options):
|
|
293
|
+
sobject: str = Field(
|
|
294
|
+
...,
|
|
295
|
+
description="The API name of the object for the bulk job.",
|
|
296
|
+
)
|
|
297
|
+
file: str = Field(
|
|
298
|
+
...,
|
|
299
|
+
description="The path to the CSV file that contains the records to upsert.",
|
|
300
|
+
)
|
|
301
|
+
external_id_field: str = Field(
|
|
302
|
+
...,
|
|
303
|
+
description="The API name of the external ID field for the upsert.",
|
|
304
|
+
)
|
|
305
|
+
wait: int = Field(
|
|
306
|
+
None,
|
|
307
|
+
description="The number of minutes to wait for the command to complete.",
|
|
308
|
+
)
|
|
309
|
+
|
|
310
|
+
def _init_task(self):
|
|
311
|
+
super()._init_task()
|
|
312
|
+
self.data_command += "upsert bulk"
|
|
313
|
+
|
|
314
|
+
def _init_options(self, kwargs):
|
|
315
|
+
super()._init_options(kwargs)
|
|
316
|
+
if self.parsed_options.sobject:
|
|
317
|
+
self.args.extend(["--sobject", self.parsed_options.sobject])
|
|
318
|
+
if self.parsed_options.file:
|
|
319
|
+
self.args.extend(["--file", self.parsed_options.file])
|
|
320
|
+
if self.parsed_options.external_id_field:
|
|
321
|
+
self.args.extend(
|
|
322
|
+
["--external-id-field", self.parsed_options.external_id_field]
|
|
323
|
+
)
|
|
324
|
+
if self.parsed_options.wait:
|
|
325
|
+
self.args.extend(["--wait", str(self.parsed_options.wait)])
|
|
326
|
+
|
|
327
|
+
def _run_task(self):
|
|
328
|
+
return super()._run_task()
|
|
329
|
+
|
|
330
|
+
|
|
331
|
+
class DataImportTreeTask(SfDataCommands):
|
|
332
|
+
class Options(SfDataCommands.Options):
|
|
333
|
+
files: list = Field(
|
|
334
|
+
None,
|
|
335
|
+
description="A list of paths to sObject Tree API plan definition files.",
|
|
336
|
+
)
|
|
337
|
+
plan: str = Field(
|
|
338
|
+
None,
|
|
339
|
+
description="The path to a plan definition file.",
|
|
340
|
+
)
|
|
341
|
+
content_type_map: str = Field(
|
|
342
|
+
None,
|
|
343
|
+
description="A mapping of file extensions to content types.",
|
|
344
|
+
)
|
|
345
|
+
|
|
346
|
+
def _init_task(self):
|
|
347
|
+
super()._init_task()
|
|
348
|
+
self.data_command += "import tree"
|
|
349
|
+
|
|
350
|
+
def _init_options(self, kwargs):
|
|
351
|
+
super()._init_options(kwargs)
|
|
352
|
+
if self.parsed_options.files:
|
|
353
|
+
self.args.extend(["--files", ",".join(self.parsed_options.files)])
|
|
354
|
+
if self.parsed_options.plan:
|
|
355
|
+
self.args.extend(["--plan", self.parsed_options.plan])
|
|
356
|
+
if self.parsed_options.content_type_map:
|
|
357
|
+
self.args.extend(
|
|
358
|
+
["--content-type-map", self.parsed_options.content_type_map]
|
|
359
|
+
)
|
|
360
|
+
|
|
361
|
+
def _run_task(self):
|
|
362
|
+
return super()._run_task()
|
|
363
|
+
|
|
364
|
+
|
|
365
|
+
class DataExportTreeTask(SfDataCommands):
|
|
366
|
+
class Options(SfDataCommands.Options):
|
|
367
|
+
query: str = Field(
|
|
368
|
+
...,
|
|
369
|
+
description="A SOQL query that retrieves the records you want to export.",
|
|
370
|
+
)
|
|
371
|
+
plan: bool = Field(
|
|
372
|
+
False,
|
|
373
|
+
description="Generate a plan definition file.",
|
|
374
|
+
)
|
|
375
|
+
prefix: str = Field(
|
|
376
|
+
None,
|
|
377
|
+
description="The prefix for the exported data files.",
|
|
378
|
+
)
|
|
379
|
+
output_dir: str = Field(
|
|
380
|
+
None,
|
|
381
|
+
description="The directory to store the exported files.",
|
|
382
|
+
)
|
|
383
|
+
|
|
384
|
+
def _init_task(self):
|
|
385
|
+
super()._init_task()
|
|
386
|
+
self.data_command += "export tree"
|
|
387
|
+
|
|
388
|
+
def _init_options(self, kwargs):
|
|
389
|
+
super()._init_options(kwargs)
|
|
390
|
+
if self.parsed_options.query:
|
|
391
|
+
self.args.extend(["--query", self.parsed_options.query])
|
|
392
|
+
if self.parsed_options.plan:
|
|
393
|
+
self.args.extend(["--plan"])
|
|
394
|
+
if self.parsed_options.prefix:
|
|
395
|
+
self.args.extend(["--prefix", self.parsed_options.prefix])
|
|
396
|
+
if self.parsed_options.output_dir:
|
|
397
|
+
self.args.extend(["--output-dir", self.parsed_options.output_dir])
|
|
398
|
+
|
|
399
|
+
def _run_task(self):
|
|
400
|
+
return super()._run_task()
|
|
@@ -23,6 +23,9 @@ class CreateBlankProfile(BaseSalesforceMetadataApiTask):
|
|
|
23
23
|
"description": "The description of the the new profile",
|
|
24
24
|
"required": False,
|
|
25
25
|
},
|
|
26
|
+
"collision_check": {
|
|
27
|
+
"description": "Performs a collision check with metadata already present in the target org. Defaults to True"
|
|
28
|
+
},
|
|
26
29
|
}
|
|
27
30
|
|
|
28
31
|
def _init_options(self, kwargs):
|
|
@@ -32,6 +35,7 @@ class CreateBlankProfile(BaseSalesforceMetadataApiTask):
|
|
|
32
35
|
"Either the name or the ID of the user license must be set."
|
|
33
36
|
)
|
|
34
37
|
self.license = self.options.get("license", "Salesforce")
|
|
38
|
+
self.sf = None
|
|
35
39
|
|
|
36
40
|
def _run_task(self):
|
|
37
41
|
|
|
@@ -39,6 +43,15 @@ class CreateBlankProfile(BaseSalesforceMetadataApiTask):
|
|
|
39
43
|
self.description = self.options.get("description") or ""
|
|
40
44
|
self.license_id = self.options.get("license_id")
|
|
41
45
|
|
|
46
|
+
if self.options.get("collision_check", True):
|
|
47
|
+
profile_id = self._get_profile_id(self.name)
|
|
48
|
+
if profile_id:
|
|
49
|
+
self.logger.info(
|
|
50
|
+
f"Profile '{self.name}' already exists with id: {profile_id}"
|
|
51
|
+
)
|
|
52
|
+
self.return_values = {"profile_id": profile_id}
|
|
53
|
+
return profile_id
|
|
54
|
+
|
|
42
55
|
if not self.license_id:
|
|
43
56
|
self.license_id = self._get_user_license_id(self.license)
|
|
44
57
|
|
|
@@ -48,22 +61,38 @@ class CreateBlankProfile(BaseSalesforceMetadataApiTask):
|
|
|
48
61
|
self.logger.info(f"Profile '{self.name}' created with id: {result}")
|
|
49
62
|
return result
|
|
50
63
|
|
|
64
|
+
def _get_profile_id(self, profile_name):
|
|
65
|
+
"""Returns the Id of a Profile from a given Name"""
|
|
66
|
+
res = self._query_sf(
|
|
67
|
+
f"SELECT Id, Name FROM Profile WHERE FullName = '{profile_name}' LIMIT 1"
|
|
68
|
+
)
|
|
69
|
+
if res["records"]:
|
|
70
|
+
return res["records"][0]["Id"]
|
|
71
|
+
|
|
72
|
+
self.logger.info(f"Profile name '{profile_name}' was not found.")
|
|
73
|
+
return None
|
|
74
|
+
|
|
51
75
|
def _get_user_license_id(self, license_name):
|
|
52
76
|
"""Returns the Id of a UserLicense from a given Name"""
|
|
53
|
-
|
|
54
|
-
self.project_config,
|
|
55
|
-
self.org_config,
|
|
56
|
-
api_version=self.org_config.latest_api_version,
|
|
57
|
-
base_url=None,
|
|
58
|
-
)
|
|
59
|
-
res = self.sf.query(
|
|
77
|
+
res = self._query_sf(
|
|
60
78
|
f"SELECT Id, Name FROM UserLicense WHERE Name = '{license_name}' LIMIT 1"
|
|
61
79
|
)
|
|
80
|
+
|
|
62
81
|
if res["records"]:
|
|
63
82
|
return res["records"][0]["Id"]
|
|
64
83
|
else:
|
|
65
84
|
raise TaskOptionsError(f"License name '{license_name}' was not found.")
|
|
66
85
|
|
|
86
|
+
def _query_sf(self, query):
|
|
87
|
+
self.sf = self.sf or get_simple_salesforce_connection(
|
|
88
|
+
self.project_config,
|
|
89
|
+
self.org_config,
|
|
90
|
+
api_version=self.org_config.latest_api_version,
|
|
91
|
+
base_url=None,
|
|
92
|
+
)
|
|
93
|
+
res = self.sf.query(query)
|
|
94
|
+
return res
|
|
95
|
+
|
|
67
96
|
def _get_api(self):
|
|
68
97
|
return self.api_class(
|
|
69
98
|
self,
|
|
@@ -95,6 +95,15 @@ def test_run_task_success():
|
|
|
95
95
|
json={
|
|
96
96
|
"done": True,
|
|
97
97
|
"totalSize": 1,
|
|
98
|
+
"records": [],
|
|
99
|
+
},
|
|
100
|
+
)
|
|
101
|
+
responses.add(
|
|
102
|
+
responses.GET,
|
|
103
|
+
query_url,
|
|
104
|
+
json={
|
|
105
|
+
"done": True,
|
|
106
|
+
"totalSize": 0,
|
|
98
107
|
"records": [
|
|
99
108
|
{
|
|
100
109
|
"attributes": {
|
|
@@ -115,9 +124,12 @@ def test_run_task_success():
|
|
|
115
124
|
result = task._run_task()
|
|
116
125
|
assert result == "001R0000029IyDPIA0"
|
|
117
126
|
assert responses.calls[0].request.params == {
|
|
127
|
+
"q": "SELECT Id, Name FROM Profile WHERE FullName = 'Test Profile Name' LIMIT 1"
|
|
128
|
+
}
|
|
129
|
+
assert responses.calls[1].request.params == {
|
|
118
130
|
"q": "SELECT Id, Name FROM UserLicense WHERE Name = 'Foo' LIMIT 1"
|
|
119
131
|
}
|
|
120
|
-
soap_body = responses.calls[
|
|
132
|
+
soap_body = responses.calls[2].request.body
|
|
121
133
|
assert "<Name>Test Profile Name</Name>" in str(soap_body)
|
|
122
134
|
assert "<UserLicenseId>10056000000VGjUAAW</UserLicenseId>" in str(soap_body)
|
|
123
135
|
assert "<Description>Have fun stormin da castle</Description>" in str(soap_body)
|
|
@@ -135,6 +147,15 @@ def test_run_task_fault():
|
|
|
135
147
|
)
|
|
136
148
|
task.org_config._latest_api_version = "53.0"
|
|
137
149
|
|
|
150
|
+
responses.add(
|
|
151
|
+
responses.GET,
|
|
152
|
+
"https://test.salesforce.com/services/data/v53.0/query/",
|
|
153
|
+
json={
|
|
154
|
+
"done": True,
|
|
155
|
+
"totalSize": 0,
|
|
156
|
+
"records": [],
|
|
157
|
+
},
|
|
158
|
+
)
|
|
138
159
|
responses.add(
|
|
139
160
|
responses.POST,
|
|
140
161
|
"https://test.salesforce.com/services/Soap/u/53.0/ORG_ID",
|
|
@@ -157,6 +178,17 @@ def test_run_task_field_error():
|
|
|
157
178
|
},
|
|
158
179
|
)
|
|
159
180
|
task.org_config._latest_api_version = "53.0"
|
|
181
|
+
|
|
182
|
+
responses.add(
|
|
183
|
+
responses.GET,
|
|
184
|
+
"https://test.salesforce.com/services/data/v53.0/query/",
|
|
185
|
+
json={
|
|
186
|
+
"done": True,
|
|
187
|
+
"totalSize": 0,
|
|
188
|
+
"records": [],
|
|
189
|
+
},
|
|
190
|
+
)
|
|
191
|
+
|
|
160
192
|
responses.add(
|
|
161
193
|
responses.POST,
|
|
162
194
|
"https://test.salesforce.com/services/Soap/u/53.0/ORG_ID",
|
|
@@ -180,6 +212,16 @@ def test_run_task_error():
|
|
|
180
212
|
)
|
|
181
213
|
task.org_config._latest_api_version = "53.0"
|
|
182
214
|
|
|
215
|
+
responses.add(
|
|
216
|
+
responses.GET,
|
|
217
|
+
"https://test.salesforce.com/services/data/v53.0/query/",
|
|
218
|
+
json={
|
|
219
|
+
"done": True,
|
|
220
|
+
"totalSize": 0,
|
|
221
|
+
"records": [],
|
|
222
|
+
},
|
|
223
|
+
)
|
|
224
|
+
|
|
183
225
|
responses.add(
|
|
184
226
|
responses.POST,
|
|
185
227
|
"https://test.salesforce.com/services/Soap/u/53.0/ORG_ID",
|
|
@@ -200,3 +242,85 @@ def test_task_options_error():
|
|
|
200
242
|
"description": "Foo",
|
|
201
243
|
},
|
|
202
244
|
)
|
|
245
|
+
|
|
246
|
+
|
|
247
|
+
@responses.activate
|
|
248
|
+
def test_run_task_success_with_collision_check():
|
|
249
|
+
query_url = "https://test.salesforce.com/services/data/v53.0/query/"
|
|
250
|
+
|
|
251
|
+
task = create_task(
|
|
252
|
+
CreateBlankProfile,
|
|
253
|
+
{
|
|
254
|
+
"license": "Foo",
|
|
255
|
+
"name": "Test Profile Name",
|
|
256
|
+
"description": "Have fun stormin da castle",
|
|
257
|
+
},
|
|
258
|
+
)
|
|
259
|
+
task.org_config._latest_api_version = "53.0"
|
|
260
|
+
|
|
261
|
+
responses.add(
|
|
262
|
+
responses.GET,
|
|
263
|
+
query_url,
|
|
264
|
+
json={
|
|
265
|
+
"done": True,
|
|
266
|
+
"totalSize": 1,
|
|
267
|
+
"records": [
|
|
268
|
+
{
|
|
269
|
+
"attributes": {
|
|
270
|
+
"type": "Profile",
|
|
271
|
+
"url": "/services/data/v53.0/sobjects/Profile/10056000000VGjUAAW",
|
|
272
|
+
},
|
|
273
|
+
"Id": "10056000000VGjUAAW",
|
|
274
|
+
"Name": "Test Profile Name",
|
|
275
|
+
}
|
|
276
|
+
],
|
|
277
|
+
},
|
|
278
|
+
)
|
|
279
|
+
|
|
280
|
+
result = task._run_task()
|
|
281
|
+
assert result == "10056000000VGjUAAW"
|
|
282
|
+
assert responses.calls[0].request.params == {
|
|
283
|
+
"q": "SELECT Id, Name FROM Profile WHERE FullName = 'Test Profile Name' LIMIT 1"
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
|
|
287
|
+
@responses.activate
|
|
288
|
+
def test_run_task_with_invalid_license():
|
|
289
|
+
query_url = "https://test.salesforce.com/services/data/v53.0/query/"
|
|
290
|
+
|
|
291
|
+
task = create_task(
|
|
292
|
+
CreateBlankProfile,
|
|
293
|
+
{
|
|
294
|
+
"license": "Foo",
|
|
295
|
+
"name": "Test Profile Name",
|
|
296
|
+
"description": "Have fun stormin da castle",
|
|
297
|
+
},
|
|
298
|
+
)
|
|
299
|
+
task.org_config._latest_api_version = "53.0"
|
|
300
|
+
|
|
301
|
+
responses.add(
|
|
302
|
+
responses.GET,
|
|
303
|
+
query_url,
|
|
304
|
+
json={
|
|
305
|
+
"done": True,
|
|
306
|
+
"totalSize": 1,
|
|
307
|
+
"records": [],
|
|
308
|
+
},
|
|
309
|
+
)
|
|
310
|
+
responses.add(
|
|
311
|
+
responses.GET,
|
|
312
|
+
query_url,
|
|
313
|
+
json={
|
|
314
|
+
"done": True,
|
|
315
|
+
"totalSize": 0,
|
|
316
|
+
"records": [],
|
|
317
|
+
},
|
|
318
|
+
)
|
|
319
|
+
responses.add(
|
|
320
|
+
responses.POST,
|
|
321
|
+
"https://test.salesforce.com/services/Soap/u/53.0/ORG_ID",
|
|
322
|
+
RESPONSE_SUCCESS,
|
|
323
|
+
)
|
|
324
|
+
with pytest.raises(TaskOptionsError) as e:
|
|
325
|
+
task._run_task()
|
|
326
|
+
assert "License name 'Foo' was not found." in str(e)
|
|
@@ -362,7 +362,7 @@ def test_install_dependency_installs_unmanaged():
|
|
|
362
362
|
|
|
363
363
|
task._install_dependency(task.dependencies[0])
|
|
364
364
|
task.dependencies[0].install.assert_called_once_with(
|
|
365
|
-
task.project_config, task.org_config
|
|
365
|
+
task.project_config, task.org_config, task.options
|
|
366
366
|
)
|
|
367
367
|
|
|
368
368
|
|
|
@@ -64,6 +64,9 @@ class UpdateDependencies(BaseSalesforceTask):
|
|
|
64
64
|
"base_package_url_format": {
|
|
65
65
|
"description": "If `interactive` is set to True, display package Ids using a format string ({} will be replaced with the package Id)."
|
|
66
66
|
},
|
|
67
|
+
"force_pre_post_install": {
|
|
68
|
+
"description": "Forces the pre-install and post-install steps to be run. Defaults to False."
|
|
69
|
+
},
|
|
67
70
|
**{k: v for k, v in PACKAGE_INSTALL_TASK_OPTIONS.items() if k != "password"},
|
|
68
71
|
}
|
|
69
72
|
|
|
@@ -220,8 +223,11 @@ class UpdateDependencies(BaseSalesforceTask):
|
|
|
220
223
|
if not click.confirm("Continue to install dependencies?", default=True):
|
|
221
224
|
raise CumulusCIException("Dependency installation was canceled.")
|
|
222
225
|
|
|
223
|
-
|
|
224
|
-
self.
|
|
226
|
+
if dependencies:
|
|
227
|
+
self.logger.info("Installing dependencies:")
|
|
228
|
+
|
|
229
|
+
for d in dependencies:
|
|
230
|
+
self._install_dependency(d)
|
|
225
231
|
|
|
226
232
|
self.org_config.reset_installed_packages()
|
|
227
233
|
|
|
@@ -233,7 +239,7 @@ class UpdateDependencies(BaseSalesforceTask):
|
|
|
233
239
|
self.project_config, self.org_config, self.install_options
|
|
234
240
|
)
|
|
235
241
|
else:
|
|
236
|
-
dependency.install(self.project_config, self.org_config)
|
|
242
|
+
dependency.install(self.project_config, self.org_config, self.options)
|
|
237
243
|
|
|
238
244
|
def freeze(self, step):
|
|
239
245
|
if self.options["interactive"]:
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: cumulusci-plus
|
|
3
|
-
Version: 5.0.
|
|
3
|
+
Version: 5.0.19
|
|
4
4
|
Summary: Build and release tools for Salesforce developers
|
|
5
5
|
Project-URL: Homepage, https://github.com/jorgesolebur/CumulusCI
|
|
6
6
|
Project-URL: Changelog, https://cumulusci.readthedocs.io/en/stable/history.html
|
|
@@ -127,7 +127,7 @@ license](https://github.com/SFDO-Tooling/CumulusCI/blob/main/LICENSE)
|
|
|
127
127
|
and is not covered by the Salesforce Master Subscription Agreement.
|
|
128
128
|
|
|
129
129
|
<!-- Changelog -->
|
|
130
|
-
## v5.0.
|
|
130
|
+
## v5.0.19 (2025-09-04)
|
|
131
131
|
|
|
132
132
|
<!-- Release notes generated using configuration in .github/release.yml at main -->
|
|
133
133
|
|
|
@@ -135,6 +135,6 @@ and is not covered by the Salesforce Master Subscription Agreement.
|
|
|
135
135
|
|
|
136
136
|
### Changes
|
|
137
137
|
|
|
138
|
-
-
|
|
138
|
+
- Feature/pm 2055 pre post flow by [@rupeshjSFDC](https://github.com/rupeshjSFDC) in [#63](https://github.com/jorgesolebur/CumulusCI/pull/63)
|
|
139
139
|
|
|
140
|
-
**Full Changelog**: https://github.com/jorgesolebur/CumulusCI/compare/v5.0.
|
|
140
|
+
**Full Changelog**: https://github.com/jorgesolebur/CumulusCI/compare/v5.0.18...v5.0.19
|
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
cumulusci/__about__.py,sha256=
|
|
1
|
+
cumulusci/__about__.py,sha256=iG1RqsHMM9Y4rg_H9ZqoQLdpz9vroikOtO-jPCwzbaY,23
|
|
2
2
|
cumulusci/__init__.py,sha256=jdanFQ_i8vbdO7Eltsf4pOfvV4mwa_Osyc4gxWKJ8ng,764
|
|
3
3
|
cumulusci/__main__.py,sha256=kgRH-n5AJrH_daCK_EJwH7azAUxdXEmpi-r-dPGMR6Y,43
|
|
4
4
|
cumulusci/conftest.py,sha256=AIL98BDwNAQtdo8YFmLKwav0tmrQ5dpbw1cX2FyGouQ,5108
|
|
5
|
-
cumulusci/cumulusci.yml,sha256=
|
|
5
|
+
cumulusci/cumulusci.yml,sha256=5w0fVZqfgWu-RkNN_uHt1HnZqpDM0f4WySCNxpcLjE4,73262
|
|
6
6
|
cumulusci/cli/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
7
7
|
cumulusci/cli/cci.py,sha256=yAq8jFoGde6g_1TeAAjzZYsk77itiONCQGBFe3g3nOs,11836
|
|
8
8
|
cumulusci/cli/error.py,sha256=znj0YN8D2Grozm1u7mZAsJlmmdGebbuy0c1ofQluL4Q,4410
|
|
@@ -65,17 +65,17 @@ cumulusci/core/config/tests/test_config.py,sha256=ZtIQSIzQWebw7mYXgehnp3CvoatC_t
|
|
|
65
65
|
cumulusci/core/config/tests/test_config_expensive.py,sha256=__3JEuoAQ8s5njTcbyZlpXHr0jR0Qtne96xyF7fzqjQ,30137
|
|
66
66
|
cumulusci/core/config/tests/test_config_util.py,sha256=X1SY9PIhLoQuC8duBKgs804aghN3n12DhqiC_f6jSmM,3177
|
|
67
67
|
cumulusci/core/dependencies/__init__.py,sha256=Txf4VCrRW-aREKHqzK3ZyauQMsgtCXjiLkQzpMQT0kI,1533
|
|
68
|
-
cumulusci/core/dependencies/base.py,sha256=
|
|
69
|
-
cumulusci/core/dependencies/dependencies.py,sha256=
|
|
68
|
+
cumulusci/core/dependencies/base.py,sha256=xeZXWxkBZHQc9GjLhouv5ogkZaQJ7r8-yrl8fi6gMx0,24972
|
|
69
|
+
cumulusci/core/dependencies/dependencies.py,sha256=ldibl-bm_zTgpecE625VW1zIYJAlcNdwx02FSwi6xkk,9101
|
|
70
70
|
cumulusci/core/dependencies/github.py,sha256=ozpRc5ADJsRDD5C_T-TLFygnBDE5Y9_03ZLCtZ-qr98,5897
|
|
71
71
|
cumulusci/core/dependencies/github_resolvers.py,sha256=Em8p41Q-npoKv1ZAYNxXVrluQmYitzVfLLXlmln-MGw,9196
|
|
72
72
|
cumulusci/core/dependencies/resolvers.py,sha256=xWVijK6Eu-WFyGnQPFANLkZFTjq4NQYhXsnLos5CElc,20742
|
|
73
73
|
cumulusci/core/dependencies/utils.py,sha256=y54fWqLZ8IUIV9i1b6WcDXIJsK0ygny5DTsZCXbgeoM,369
|
|
74
74
|
cumulusci/core/dependencies/tests/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
75
75
|
cumulusci/core/dependencies/tests/conftest.py,sha256=rd0ZqpV6GTk7IRtPYJ7jyLWHM1g_xtZ4PABuvkN-TZY,11004
|
|
76
|
-
cumulusci/core/dependencies/tests/test_dependencies.py,sha256=
|
|
76
|
+
cumulusci/core/dependencies/tests/test_dependencies.py,sha256=sdRcNmiBi0VsQwfbxgQSxTYwndocAD246KIAjFdn1N8,40748
|
|
77
77
|
cumulusci/core/dependencies/tests/test_github.py,sha256=_JiUh-xgGmnsA2X_57i3gOwcDv_xBj66mdhitcZ-APU,2765
|
|
78
|
-
cumulusci/core/dependencies/tests/test_resolvers.py,sha256=
|
|
78
|
+
cumulusci/core/dependencies/tests/test_resolvers.py,sha256=dhclH8_qQN4MXtd0gZ6TvLvehUVQJSJr7oGJdbht_hc,37415
|
|
79
79
|
cumulusci/core/keychain/__init__.py,sha256=UbuaIrKZSczVVqbG_7BHFTkQukbqinGJlJlQGIpIsOI,595
|
|
80
80
|
cumulusci/core/keychain/base_project_keychain.py,sha256=uIQ6Qc5xMIQIGWL947Ou9XdXpHvsGrrZdGKMypk6BFc,16927
|
|
81
81
|
cumulusci/core/keychain/encrypted_file_project_keychain.py,sha256=yUrh4h2lynEjDvBDERFRXbJ4ZGTKKEjwXuo4bjBg7Xg,37734
|
|
@@ -516,7 +516,7 @@ cumulusci/tasks/salesforce/RetrievePackaged.py,sha256=XMdA3qBIuGJaZnPNeZBKupz9N6
|
|
|
516
516
|
cumulusci/tasks/salesforce/RetrieveReportsAndDashboards.py,sha256=-pVD0RJ4vsB1TM9b7VcPQAV-pO4LTAm-YoESq4bM2dA,3289
|
|
517
517
|
cumulusci/tasks/salesforce/RetrieveUnpackaged.py,sha256=_9jglbNWmjg9kq5EkvCusprrt14czurBtNM2hbU9RFI,1174
|
|
518
518
|
cumulusci/tasks/salesforce/SOQLQuery.py,sha256=RFV-bLzvAx_m4SWWLMyCKuA3ie08iLwLyrOVyhpNCPE,1730
|
|
519
|
-
cumulusci/tasks/salesforce/SfDataCommands.py,sha256=
|
|
519
|
+
cumulusci/tasks/salesforce/SfDataCommands.py,sha256=5t3441TOC7_Qv4rCerXsBpWRlQCIIqd40o8vGnnFV1g,14415
|
|
520
520
|
cumulusci/tasks/salesforce/UninstallLocal.py,sha256=6nnRDyFrwIcXd9WygDHIMhMQYBZMFEQ75Sh6nNxivfg,492
|
|
521
521
|
cumulusci/tasks/salesforce/UninstallLocalBundles.py,sha256=eG4yoDJdjruo9kwfcQU-i5_AVt473uWF7SvLD7IQoSw,759
|
|
522
522
|
cumulusci/tasks/salesforce/UninstallLocalNamespacedBundles.py,sha256=KgM6TPSkczAdeM10fOAtijs4uszJ_f219lGgVNwJ1eM,2210
|
|
@@ -538,14 +538,14 @@ cumulusci/tasks/salesforce/network_member_group.py,sha256=sWkRr4A6Zm_Iv_7uzPmiNj
|
|
|
538
538
|
cumulusci/tasks/salesforce/nonsourcetracking.py,sha256=kKaQGEOErCIaE53A_TO4Hc5Afhv2aIi97UjPAN-4cQM,8720
|
|
539
539
|
cumulusci/tasks/salesforce/org_settings.py,sha256=0Xg_voNeHV94husGpo7HTpA0QpqQ1NBJKqBsvLM24NA,7105
|
|
540
540
|
cumulusci/tasks/salesforce/package_upload.py,sha256=AKrguElY4cTprmReq4s5BtIuG-QoxeaP9-YT8385Hzo,13024
|
|
541
|
-
cumulusci/tasks/salesforce/profiles.py,sha256
|
|
541
|
+
cumulusci/tasks/salesforce/profiles.py,sha256=J3fO72UdnSct7HpPUTwVWVwbSWnAyfHxCGYqMcVF05M,3724
|
|
542
542
|
cumulusci/tasks/salesforce/promote_package_version.py,sha256=2mD_iOzkbelaglH_NJKfR_MJoJOVpnqPuaGmnRTL5Go,15186
|
|
543
543
|
cumulusci/tasks/salesforce/retrieve_profile.py,sha256=VTO--xWQJ7sidMMkvrfvgd2fSUaumQWJK25Aqa0oSNU,7798
|
|
544
544
|
cumulusci/tasks/salesforce/salesforce_files.py,sha256=91VHtOkZzi9Tabfy0IDFWBW5bZPi8xOPb_4RPCqmFaY,9193
|
|
545
545
|
cumulusci/tasks/salesforce/sourcetracking.py,sha256=-JaZt1NNlA_dEzYsIHvFKZ9va-MMA2JOPIplFN1g2EM,18797
|
|
546
546
|
cumulusci/tasks/salesforce/trigger_handlers.py,sha256=cs6pDHhvi_eu0Vr8sLtfH2nrhlsF8TPrkKezjciy79o,4932
|
|
547
547
|
cumulusci/tasks/salesforce/uninstall_packaged_incremental.py,sha256=9-_3S0PaVm-K6t44McBHSfRTB7KVzkHUMii4-p5PkS0,5673
|
|
548
|
-
cumulusci/tasks/salesforce/update_dependencies.py,sha256=
|
|
548
|
+
cumulusci/tasks/salesforce/update_dependencies.py,sha256=BE-ey6eH_x75jC4D6kSa-aR0JaCuPbzvN5MlhxlKAkU,12153
|
|
549
549
|
cumulusci/tasks/salesforce/update_profile.py,sha256=P8TQeWEjzXFI4hN5cUk9zMCweBerqNP08seIuYEo-RI,15163
|
|
550
550
|
cumulusci/tasks/salesforce/tests/__init__.py,sha256=zEUlLU8eRXUU1HAcYdHtdAgHbdlAPPj39rcWRPEu2H4,57
|
|
551
551
|
cumulusci/tasks/salesforce/tests/test_CreateCommunity.py,sha256=aepyVVrM6zfmT2UJ_pKKdgwv7DsU66F_eiwmE4EfVUo,8720
|
|
@@ -583,12 +583,12 @@ cumulusci/tasks/salesforce/tests/test_install_package_version.py,sha256=f_hHO83v
|
|
|
583
583
|
cumulusci/tasks/salesforce/tests/test_network_member_group.py,sha256=N0ZXcy74nNQdpmGrBnkVzY6mALW4KxoYhQtARrZJ578,13968
|
|
584
584
|
cumulusci/tasks/salesforce/tests/test_nonsourcetracking.py,sha256=14yjidvGa0Q85n8uoWXwMeFsOWYJGqvFShevAbidfbU,9154
|
|
585
585
|
cumulusci/tasks/salesforce/tests/test_org_settings.py,sha256=SRpqihPzRy29WEinOlU-l-DU8viyFP1guIzjfROTK7A,13532
|
|
586
|
-
cumulusci/tasks/salesforce/tests/test_profiles.py,sha256=
|
|
586
|
+
cumulusci/tasks/salesforce/tests/test_profiles.py,sha256=qe1A6s_0ckuRJQsTDPbVr0QMuCaYvzH7dAG5ufkOmGc,9462
|
|
587
587
|
cumulusci/tasks/salesforce/tests/test_retrieve_profile.py,sha256=Ujbt1k34CmN6-CvcKhTY_vgVp-OtpvGb0NDXS-vGYSQ,9858
|
|
588
588
|
cumulusci/tasks/salesforce/tests/test_salesforce_files.py,sha256=eBeyanF7ygldukf9TQrPSXmeTU5sLQi_vNHVplx8dd0,8005
|
|
589
589
|
cumulusci/tasks/salesforce/tests/test_sourcetracking.py,sha256=n9dyJ21OZs8P574Ds3uzVESCVl6fK0_RYv2bKI1dm3E,12540
|
|
590
590
|
cumulusci/tasks/salesforce/tests/test_trigger_handlers.py,sha256=IpqnCKgGVWU0XUP4LDB10HG1LLj5VvsSfxMBa26EScs,10567
|
|
591
|
-
cumulusci/tasks/salesforce/tests/test_update_dependencies.py,sha256=
|
|
591
|
+
cumulusci/tasks/salesforce/tests/test_update_dependencies.py,sha256=C7Zu115-Bi585Fu7tWy3TF9ZKDcKiu1zXzqJiOhA0L0,14679
|
|
592
592
|
cumulusci/tasks/salesforce/tests/util.py,sha256=YbeHOKyKNhKNYtd1g467a4sQ07W3SvyMAuT0E3bL6Js,2543
|
|
593
593
|
cumulusci/tasks/salesforce/users/permsets.py,sha256=EMFX7QhSbVd1LM0ognYlaXUhYcpSY-0CbWOARRVORM0,9326
|
|
594
594
|
cumulusci/tasks/salesforce/users/photos.py,sha256=67-fQk0m9w4bf1N2WSWfsn-kwtmIuLtSpyJCqa8Cr1w,5745
|
|
@@ -613,9 +613,7 @@ cumulusci/tasks/tests/test_pushfails.py,sha256=9JG9D0iD4dR-1fKheaRN7BEy3lzzuOKeR
|
|
|
613
613
|
cumulusci/tasks/tests/test_salesforce.py,sha256=yCGtuHapxyAEmXQhuF2g2fh2naknTu7Md4OfEJQvGAA,2594
|
|
614
614
|
cumulusci/tasks/tests/test_sfdx.py,sha256=oUbHo28d796m5RuskXMLitJw2rCLjjXIfxggzr4gsso,3545
|
|
615
615
|
cumulusci/tasks/tests/test_util.py,sha256=D1T0QnvPTS0PHeZWo2xiVgE1jVTYcLzGTGHwEIoVmxk,7296
|
|
616
|
-
cumulusci/tasks/utility/data_management.py,sha256=7DbK0uxMxj_e4WuH08gWFVxr_ZBMrvHvumHnM_oi2YM,443
|
|
617
616
|
cumulusci/tasks/utility/env_management.py,sha256=hJX6ySEiXD2oFW38JqbGQKMj89ucxdSBsPwytSdkgO8,6591
|
|
618
|
-
cumulusci/tasks/utility/tests/test_data_management.py,sha256=f3F1yFikaNEkzllMXZa67gNX0T_PCC-4zEoL8kUII_Q,1363
|
|
619
617
|
cumulusci/tasks/utility/tests/test_env_management.py,sha256=fw34meWGOe1YYZO449MMCi2O7BgSaOA_I_wScrIr1Uk,8702
|
|
620
618
|
cumulusci/tasks/vcs/__init__.py,sha256=ZzpMZnhooXZ6r_ywBVTS3UNw9uMcXW6h33LylRqTDK0,700
|
|
621
619
|
cumulusci/tasks/vcs/commit_status.py,sha256=hgPUVHeQyIfMsCuwrw2RI-ufnbjdRARc6HI3BEgdcxI,2332
|
|
@@ -742,9 +740,9 @@ cumulusci/vcs/tests/dummy_service.py,sha256=RltOUpMIhSDNrfxk0LhLqlH4ppC0sK6NC2cO
|
|
|
742
740
|
cumulusci/vcs/tests/test_vcs_base.py,sha256=9mp6uZ3lTxY4onjUNCucp9N9aB3UylKS7_2Zu_hdAZw,24331
|
|
743
741
|
cumulusci/vcs/tests/test_vcs_bootstrap.py,sha256=N0NA48-rGNIIjY3Z7PtVnNwHObSlEGDk2K55TQGI8g4,27954
|
|
744
742
|
cumulusci/vcs/utils/__init__.py,sha256=py4fEcHM7Vd0M0XWznOlywxaeCtG3nEVGmELmEKVGU8,869
|
|
745
|
-
cumulusci_plus-5.0.
|
|
746
|
-
cumulusci_plus-5.0.
|
|
747
|
-
cumulusci_plus-5.0.
|
|
748
|
-
cumulusci_plus-5.0.
|
|
749
|
-
cumulusci_plus-5.0.
|
|
750
|
-
cumulusci_plus-5.0.
|
|
743
|
+
cumulusci_plus-5.0.19.dist-info/METADATA,sha256=H6WNavlWKMBgslTFJcl_b64AJF_owi7XhFRlYg6G5RI,5750
|
|
744
|
+
cumulusci_plus-5.0.19.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
|
|
745
|
+
cumulusci_plus-5.0.19.dist-info/entry_points.txt,sha256=nTtu04b9iLXhzADcTrb5PwmdXE6e2MTUAMh9OK6Z2pg,80
|
|
746
|
+
cumulusci_plus-5.0.19.dist-info/licenses/AUTHORS.rst,sha256=PvewjKImdKPhhJ6xR2EEZ4T7GbpY2ZeAeyWm2aLtiMQ,676
|
|
747
|
+
cumulusci_plus-5.0.19.dist-info/licenses/LICENSE,sha256=NFsF_s7RVXk2dU6tmRAN8wF45pnD98VZ5IwqOsyBcaU,1499
|
|
748
|
+
cumulusci_plus-5.0.19.dist-info/RECORD,,
|
|
@@ -1,16 +0,0 @@
|
|
|
1
|
-
from typing import Any
|
|
2
|
-
|
|
3
|
-
from cumulusci.core.tasks import BaseTask
|
|
4
|
-
from cumulusci.utils.options import CCIOptions, Field
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
class GetFirstItemFromRecordListTask(BaseTask):
|
|
8
|
-
class Options(CCIOptions):
|
|
9
|
-
result: dict[str, Any] = Field(
|
|
10
|
-
None, description="The result from SF CLI operations."
|
|
11
|
-
)
|
|
12
|
-
|
|
13
|
-
parsed_options: Options
|
|
14
|
-
|
|
15
|
-
def _run_task(self):
|
|
16
|
-
self.return_values = self.parsed_options.result["records"][0]
|
|
@@ -1,43 +0,0 @@
|
|
|
1
|
-
import pytest
|
|
2
|
-
|
|
3
|
-
from cumulusci.core.config import (
|
|
4
|
-
BaseProjectConfig,
|
|
5
|
-
OrgConfig,
|
|
6
|
-
TaskConfig,
|
|
7
|
-
UniversalConfig,
|
|
8
|
-
)
|
|
9
|
-
from cumulusci.tasks.utility.data_management import GetFirstItemFromRecordListTask
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
@pytest.fixture
|
|
13
|
-
def a_task():
|
|
14
|
-
universal_config = UniversalConfig()
|
|
15
|
-
project_config = BaseProjectConfig(universal_config, config={"noyaml": True})
|
|
16
|
-
org_config = OrgConfig({}, "test")
|
|
17
|
-
|
|
18
|
-
def _a_task(options):
|
|
19
|
-
task_config = TaskConfig({"options": options})
|
|
20
|
-
return GetFirstItemFromRecordListTask(project_config, task_config, org_config)
|
|
21
|
-
|
|
22
|
-
return _a_task
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
class TestGetFirstItemFromRecordListTask:
|
|
26
|
-
def test_returns_first_item(self, a_task):
|
|
27
|
-
"""Tests that the task returns the first item from the 'records' list."""
|
|
28
|
-
result_input = {
|
|
29
|
-
"records": [
|
|
30
|
-
{"Id": "001", "Name": "Test Account"},
|
|
31
|
-
{"Id": "002", "Name": "Another Account"},
|
|
32
|
-
]
|
|
33
|
-
}
|
|
34
|
-
task = a_task({"result": result_input})
|
|
35
|
-
task()
|
|
36
|
-
assert task.return_values == {"Id": "001", "Name": "Test Account"}
|
|
37
|
-
|
|
38
|
-
def test_empty_record_list(self, a_task):
|
|
39
|
-
"""Tests that an IndexError is raised for an empty 'records' list."""
|
|
40
|
-
result_input = {"records": []}
|
|
41
|
-
task = a_task({"result": result_input})
|
|
42
|
-
with pytest.raises(IndexError):
|
|
43
|
-
task()
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|