cumulusci-plus 5.0.24__py3-none-any.whl → 5.0.26__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.
Potentially problematic release.
This version of cumulusci-plus might be problematic. Click here for more details.
- cumulusci/__about__.py +1 -1
- cumulusci/cli/task.py +17 -0
- cumulusci/cli/tests/test_error.py +3 -1
- cumulusci/cli/tests/test_task.py +88 -2
- cumulusci/core/github.py +1 -1
- cumulusci/core/sfdx.py +3 -1
- cumulusci/cumulusci.yml +20 -0
- cumulusci/robotframework/pageobjects/ObjectManagerPageObject.py +1 -1
- cumulusci/salesforce_api/rest_deploy.py +1 -1
- cumulusci/tasks/apex/anon.py +1 -1
- cumulusci/tasks/apex/testrunner.py +6 -1
- cumulusci/tasks/bulkdata/extract.py +0 -1
- cumulusci/tasks/bulkdata/tests/test_load.py +0 -2
- cumulusci/tasks/bulkdata/tests/test_select_utils.py +6 -0
- cumulusci/tasks/metadata_etl/base.py +7 -3
- cumulusci/tasks/push/README.md +15 -17
- cumulusci/tasks/release_notes/README.md +13 -13
- cumulusci/tasks/robotframework/tests/test_robotframework.py +1 -1
- cumulusci/tasks/salesforce/Deploy.py +5 -1
- cumulusci/tasks/salesforce/composite.py +1 -1
- cumulusci/tasks/salesforce/custom_settings_wait.py +1 -1
- cumulusci/tasks/salesforce/enable_prediction.py +5 -1
- cumulusci/tasks/salesforce/sourcetracking.py +1 -1
- cumulusci/tasks/salesforce/tests/test_update_external_credential.py +912 -0
- cumulusci/tasks/salesforce/tests/test_update_named_credential.py +1042 -0
- cumulusci/tasks/salesforce/update_external_credential.py +562 -0
- cumulusci/tasks/salesforce/update_named_credential.py +441 -0
- cumulusci/tasks/salesforce/update_profile.py +17 -13
- cumulusci/tasks/salesforce/users/permsets.py +70 -2
- cumulusci/tasks/salesforce/users/tests/test_permsets.py +184 -0
- cumulusci/tasks/sfdmu/__init__.py +0 -0
- cumulusci/tasks/sfdmu/sfdmu.py +256 -0
- cumulusci/tasks/sfdmu/tests/__init__.py +1 -0
- cumulusci/tasks/sfdmu/tests/test_runner.py +212 -0
- cumulusci/tasks/sfdmu/tests/test_sfdmu.py +443 -0
- cumulusci/tasks/utility/credentialManager.py +256 -0
- cumulusci/tasks/utility/directoryRecreator.py +30 -0
- cumulusci/tasks/utility/secretsToEnv.py +130 -0
- cumulusci/tasks/utility/tests/test_credentialManager.py +564 -0
- cumulusci/tasks/utility/tests/test_directoryRecreator.py +439 -0
- cumulusci/tasks/utility/tests/test_secretsToEnv.py +1091 -0
- cumulusci/utils/__init__.py +23 -1
- cumulusci/utils/http/tests/cassettes/ManualEditTestCompositeParallelSalesforce.test_http_headers.yaml +31 -30
- cumulusci/utils/yaml/tests/test_model_parser.py +2 -2
- {cumulusci_plus-5.0.24.dist-info → cumulusci_plus-5.0.26.dist-info}/METADATA +7 -9
- {cumulusci_plus-5.0.24.dist-info → cumulusci_plus-5.0.26.dist-info}/RECORD +50 -35
- {cumulusci_plus-5.0.24.dist-info → cumulusci_plus-5.0.26.dist-info}/WHEEL +0 -0
- {cumulusci_plus-5.0.24.dist-info → cumulusci_plus-5.0.26.dist-info}/entry_points.txt +0 -0
- {cumulusci_plus-5.0.24.dist-info → cumulusci_plus-5.0.26.dist-info}/licenses/AUTHORS.rst +0 -0
- {cumulusci_plus-5.0.24.dist-info → cumulusci_plus-5.0.26.dist-info}/licenses/LICENSE +0 -0
cumulusci/__about__.py
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
__version__ = "5.0.
|
|
1
|
+
__version__ = "5.0.26"
|
cumulusci/cli/task.py
CHANGED
|
@@ -2,6 +2,7 @@ import json
|
|
|
2
2
|
from pathlib import Path
|
|
3
3
|
|
|
4
4
|
import click
|
|
5
|
+
from dotenv import load_dotenv
|
|
5
6
|
from rich.console import Console
|
|
6
7
|
from rst2ansi import rst2ansi
|
|
7
8
|
|
|
@@ -126,6 +127,10 @@ class RunTaskCommand(click.MultiCommand):
|
|
|
126
127
|
"help": "Drops into the Python debugger at task completion.",
|
|
127
128
|
"is_flag": True,
|
|
128
129
|
},
|
|
130
|
+
"loadenv": {
|
|
131
|
+
"help": "Loads environment variables from the .env file.",
|
|
132
|
+
"is_flag": True,
|
|
133
|
+
},
|
|
129
134
|
}
|
|
130
135
|
|
|
131
136
|
def list_commands(self, ctx):
|
|
@@ -151,6 +156,17 @@ class RunTaskCommand(click.MultiCommand):
|
|
|
151
156
|
|
|
152
157
|
def run_task(*args, **kwargs):
|
|
153
158
|
"""Callback function that executes when the command fires."""
|
|
159
|
+
# Load environment variables FIRST, before any task processing
|
|
160
|
+
if kwargs.get("loadenv", None):
|
|
161
|
+
# Load .env file from the project root directory
|
|
162
|
+
env_path = (
|
|
163
|
+
Path(runtime.project_config.repo_root) / ".env"
|
|
164
|
+
if runtime.project_config
|
|
165
|
+
else None
|
|
166
|
+
)
|
|
167
|
+
if env_path:
|
|
168
|
+
load_dotenv(env_path)
|
|
169
|
+
|
|
154
170
|
org, org_config = runtime.get_org(
|
|
155
171
|
kwargs.pop("org", None), fail_if_missing=False
|
|
156
172
|
)
|
|
@@ -168,6 +184,7 @@ class RunTaskCommand(click.MultiCommand):
|
|
|
168
184
|
task_config.config["options"].update(options)
|
|
169
185
|
|
|
170
186
|
try:
|
|
187
|
+
|
|
171
188
|
task = task_class(
|
|
172
189
|
task_config.project_config, task_config, org_config=org_config
|
|
173
190
|
)
|
|
@@ -98,7 +98,9 @@ Environment Info: Rossian / x68_46
|
|
|
98
98
|
)
|
|
99
99
|
webbrowser_open.assert_called_once_with(expected_gist_url)
|
|
100
100
|
|
|
101
|
-
@pytest.mark.skipif(
|
|
101
|
+
@pytest.mark.skipif(
|
|
102
|
+
sys.version_info > (3, 11), reason="requires python3.10 or higher"
|
|
103
|
+
)
|
|
102
104
|
@mock.patch("cumulusci.cli.error.platform")
|
|
103
105
|
@mock.patch("cumulusci.cli.error.sys")
|
|
104
106
|
@mock.patch("cumulusci.cli.error.datetime")
|
cumulusci/cli/tests/test_task.py
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import contextlib
|
|
2
2
|
import io
|
|
3
3
|
import json
|
|
4
|
+
import tempfile
|
|
5
|
+
from pathlib import Path
|
|
4
6
|
from unittest.mock import Mock, patch
|
|
5
7
|
|
|
6
8
|
import click
|
|
@@ -126,10 +128,10 @@ def test_format_help(runtime):
|
|
|
126
128
|
|
|
127
129
|
def test_get_default_command_options():
|
|
128
130
|
opts = task.RunTaskCommand()._get_default_command_options(is_salesforce_task=False)
|
|
129
|
-
assert len(opts) ==
|
|
131
|
+
assert len(opts) == 5
|
|
130
132
|
|
|
131
133
|
opts = task.RunTaskCommand()._get_default_command_options(is_salesforce_task=True)
|
|
132
|
-
assert len(opts) ==
|
|
134
|
+
assert len(opts) == 6
|
|
133
135
|
assert any([o.name == "org" for o in opts])
|
|
134
136
|
|
|
135
137
|
|
|
@@ -264,3 +266,87 @@ class SetTrace(Exception):
|
|
|
264
266
|
class DummyDerivedTask(DummyTask):
|
|
265
267
|
def _run_task(self):
|
|
266
268
|
click.echo(f"<{self.__class__}>\n\tcolor: {self.options['color']}")
|
|
269
|
+
|
|
270
|
+
|
|
271
|
+
@patch("cumulusci.cli.task.load_dotenv")
|
|
272
|
+
def test_task_run__loadenv_with_project_root(load_dotenv, runtime):
|
|
273
|
+
"""Test that loadenv loads .env file from project root when project exists."""
|
|
274
|
+
DummyTask._run_task = Mock()
|
|
275
|
+
|
|
276
|
+
# Create a temporary directory for the test
|
|
277
|
+
with tempfile.TemporaryDirectory() as temp_dir:
|
|
278
|
+
runtime.project_config._repo_info = {"root": temp_dir}
|
|
279
|
+
|
|
280
|
+
multi_cmd = task.RunTaskCommand()
|
|
281
|
+
with click.Context(multi_cmd, obj=runtime) as ctx:
|
|
282
|
+
cmd = multi_cmd.get_command(ctx, "dummy-task")
|
|
283
|
+
cmd.callback(runtime, "dummy-task", color="blue", loadenv=True)
|
|
284
|
+
|
|
285
|
+
# Verify load_dotenv was called with the correct path
|
|
286
|
+
expected_path = Path(temp_dir) / ".env"
|
|
287
|
+
load_dotenv.assert_called_once_with(expected_path)
|
|
288
|
+
DummyTask._run_task.assert_called_once()
|
|
289
|
+
|
|
290
|
+
|
|
291
|
+
@patch("cumulusci.cli.task.load_dotenv")
|
|
292
|
+
def test_task_run__loadenv_false(load_dotenv, runtime):
|
|
293
|
+
"""Test that loadenv does not call load_dotenv when loadenv=False."""
|
|
294
|
+
DummyTask._run_task = Mock()
|
|
295
|
+
|
|
296
|
+
multi_cmd = task.RunTaskCommand()
|
|
297
|
+
with click.Context(multi_cmd, obj=runtime) as ctx:
|
|
298
|
+
cmd = multi_cmd.get_command(ctx, "dummy-task")
|
|
299
|
+
cmd.callback(runtime, "dummy-task", color="blue", loadenv=False)
|
|
300
|
+
|
|
301
|
+
# Verify load_dotenv was not called
|
|
302
|
+
load_dotenv.assert_not_called()
|
|
303
|
+
DummyTask._run_task.assert_called_once()
|
|
304
|
+
|
|
305
|
+
|
|
306
|
+
@patch("cumulusci.cli.task.load_dotenv")
|
|
307
|
+
def test_task_run__loadenv_not_provided(load_dotenv, runtime):
|
|
308
|
+
"""Test that loadenv does not call load_dotenv when loadenv is not provided."""
|
|
309
|
+
DummyTask._run_task = Mock()
|
|
310
|
+
|
|
311
|
+
multi_cmd = task.RunTaskCommand()
|
|
312
|
+
with click.Context(multi_cmd, obj=runtime) as ctx:
|
|
313
|
+
cmd = multi_cmd.get_command(ctx, "dummy-task")
|
|
314
|
+
cmd.callback(runtime, "dummy-task", color="blue")
|
|
315
|
+
|
|
316
|
+
# Verify load_dotenv was not called
|
|
317
|
+
load_dotenv.assert_not_called()
|
|
318
|
+
DummyTask._run_task.assert_called_once()
|
|
319
|
+
|
|
320
|
+
|
|
321
|
+
@patch("cumulusci.cli.task.load_dotenv")
|
|
322
|
+
def test_task_run__loadenv_none_value(load_dotenv, runtime):
|
|
323
|
+
"""Test that loadenv does not call load_dotenv when loadenv=None."""
|
|
324
|
+
DummyTask._run_task = Mock()
|
|
325
|
+
|
|
326
|
+
multi_cmd = task.RunTaskCommand()
|
|
327
|
+
with click.Context(multi_cmd, obj=runtime) as ctx:
|
|
328
|
+
cmd = multi_cmd.get_command(ctx, "dummy-task")
|
|
329
|
+
cmd.callback(runtime, "dummy-task", color="blue", loadenv=None)
|
|
330
|
+
|
|
331
|
+
# Verify load_dotenv was not called
|
|
332
|
+
load_dotenv.assert_not_called()
|
|
333
|
+
DummyTask._run_task.assert_called_once()
|
|
334
|
+
|
|
335
|
+
|
|
336
|
+
def test_get_default_command_options_includes_loadenv():
|
|
337
|
+
"""Test that the loadenv option is included in default command options."""
|
|
338
|
+
opts = task.RunTaskCommand()._get_default_command_options(is_salesforce_task=False)
|
|
339
|
+
|
|
340
|
+
# Should have 5 global options including loadenv
|
|
341
|
+
assert len(opts) == 5
|
|
342
|
+
|
|
343
|
+
# Find the loadenv option
|
|
344
|
+
loadenv_opt = None
|
|
345
|
+
for opt in opts:
|
|
346
|
+
if hasattr(opt, "name") and opt.name == "loadenv":
|
|
347
|
+
loadenv_opt = opt
|
|
348
|
+
break
|
|
349
|
+
|
|
350
|
+
assert loadenv_opt is not None
|
|
351
|
+
assert loadenv_opt.is_flag is True
|
|
352
|
+
assert "Loads environment variables from the .env file" in loadenv_opt.help
|
cumulusci/core/github.py
CHANGED
|
@@ -189,7 +189,7 @@ def validate_service(options: dict, keychain) -> dict:
|
|
|
189
189
|
server_domain = options.get("server_domain", None)
|
|
190
190
|
|
|
191
191
|
gh = _determine_github_client(server_domain, {"token": token})
|
|
192
|
-
if
|
|
192
|
+
if isinstance(gh, GitHubEnterprise):
|
|
193
193
|
validate_gh_enterprise(server_domain, keychain)
|
|
194
194
|
try:
|
|
195
195
|
authed_user = gh.me()
|
cumulusci/core/sfdx.py
CHANGED
|
@@ -14,10 +14,12 @@ import sarge
|
|
|
14
14
|
|
|
15
15
|
# Fix for TextIOWrapper flush issue with sarge.Capture objects
|
|
16
16
|
# Add flush method to sarge.Capture to prevent AttributeError during garbage collection
|
|
17
|
-
if not hasattr(sarge.Capture,
|
|
17
|
+
if not hasattr(sarge.Capture, "flush"):
|
|
18
|
+
|
|
18
19
|
def _capture_flush(self):
|
|
19
20
|
"""No-op flush method for sarge.Capture compatibility with TextIOWrapper"""
|
|
20
21
|
pass
|
|
22
|
+
|
|
21
23
|
sarge.Capture.flush = _capture_flush
|
|
22
24
|
|
|
23
25
|
from cumulusci.core.enums import StrEnum
|
cumulusci/cumulusci.yml
CHANGED
|
@@ -761,6 +761,14 @@ tasks:
|
|
|
761
761
|
description: Get or set environment variables.
|
|
762
762
|
class_path: cumulusci.tasks.utility.env_management.EnvManagement
|
|
763
763
|
group: Utilities
|
|
764
|
+
secrets_to_dotenv:
|
|
765
|
+
description: Get all environment variables from a target JSON file and generates a .env file that can be used to set up the environment variables for the deployment
|
|
766
|
+
class_path: cumulusci.tasks.utility.secretsToEnv.SecretsToEnv
|
|
767
|
+
group: Utilities
|
|
768
|
+
directory_recreator:
|
|
769
|
+
description: Remove and recreate a directory
|
|
770
|
+
class_path: cumulusci.tasks.utility.directoryRecreator.DirectoryRecreator
|
|
771
|
+
group: Utilities
|
|
764
772
|
download_extract:
|
|
765
773
|
description: Downloads files and folders from a VCS repository.
|
|
766
774
|
class_path: cumulusci.tasks.vcs.download_extract.DownloadExtract
|
|
@@ -795,6 +803,10 @@ tasks:
|
|
|
795
803
|
description: Load Custom Settings specified in a YAML file to the target org
|
|
796
804
|
class_path: cumulusci.tasks.salesforce.LoadCustomSettings
|
|
797
805
|
group: "Data Operations"
|
|
806
|
+
sfdmu:
|
|
807
|
+
description: Execute SFDmu data migration with namespace injection support
|
|
808
|
+
class_path: cumulusci.tasks.sfdmu.sfdmu.SfdmuTask
|
|
809
|
+
group: "Data Operations"
|
|
798
810
|
remove_metadata_xml_elements:
|
|
799
811
|
description: Remove specified XML elements from one or more metadata files
|
|
800
812
|
class_path: cumulusci.tasks.metadata.modify.RemoveElementsXPath
|
|
@@ -842,6 +854,14 @@ tasks:
|
|
|
842
854
|
options:
|
|
843
855
|
package_name: $project_config.project__package__name
|
|
844
856
|
fail_on_error: False
|
|
857
|
+
update_named_credential:
|
|
858
|
+
class_path: cumulusci.tasks.salesforce.update_named_credential.UpdateNamedCredential
|
|
859
|
+
description: Update named credential parameters
|
|
860
|
+
group: Metadata Transformations
|
|
861
|
+
update_external_credential:
|
|
862
|
+
class_path: cumulusci.tasks.salesforce.update_external_credential.UpdateExternalCredential
|
|
863
|
+
description: Update external credential parameters
|
|
864
|
+
group: Metadata Transformations
|
|
845
865
|
|
|
846
866
|
flows:
|
|
847
867
|
ci_beta:
|
|
@@ -203,7 +203,7 @@ class ObjectManagerPage(BasePage):
|
|
|
203
203
|
|
|
204
204
|
except Exception as e:
|
|
205
205
|
self.builtin.log(
|
|
206
|
-
f"on try #{tries+1} we caught this error: {e}", "DEBUG"
|
|
206
|
+
f"on try #{tries + 1} we caught this error: {e}", "DEBUG"
|
|
207
207
|
)
|
|
208
208
|
self.builtin.sleep("1 second")
|
|
209
209
|
last_error = e
|
|
@@ -138,7 +138,7 @@ class RestDeploy:
|
|
|
138
138
|
|
|
139
139
|
# Construct an error message from deployment failure details
|
|
140
140
|
def _construct_error_message(self, failure):
|
|
141
|
-
error_message = f"{str.upper(failure['problemType'])} in file {failure['fileName'][len(PARENT_DIR_NAME)+len('/'):]}: {failure['problem']}"
|
|
141
|
+
error_message = f"{str.upper(failure['problemType'])} in file {failure['fileName'][len(PARENT_DIR_NAME) + len('/'):]}: {failure['problem']}"
|
|
142
142
|
|
|
143
143
|
if failure["lineNumber"] and failure["columnNumber"]:
|
|
144
144
|
error_message += (
|
cumulusci/tasks/apex/anon.py
CHANGED
|
@@ -4,7 +4,7 @@ from cumulusci.core.exceptions import (
|
|
|
4
4
|
SalesforceException,
|
|
5
5
|
TaskOptionsError,
|
|
6
6
|
)
|
|
7
|
-
from cumulusci.core.utils import
|
|
7
|
+
from cumulusci.core.utils import determine_managed_mode, process_bool_arg
|
|
8
8
|
from cumulusci.tasks.salesforce import BaseSalesforceApiTask
|
|
9
9
|
from cumulusci.utils import in_directory, inject_namespace
|
|
10
10
|
from cumulusci.utils.http.requests_utils import safe_json_from_response
|
|
@@ -10,7 +10,12 @@ from cumulusci.core.exceptions import (
|
|
|
10
10
|
CumulusCIException,
|
|
11
11
|
TaskOptionsError,
|
|
12
12
|
)
|
|
13
|
-
from cumulusci.core.utils import
|
|
13
|
+
from cumulusci.core.utils import (
|
|
14
|
+
decode_to_unicode,
|
|
15
|
+
determine_managed_mode,
|
|
16
|
+
process_bool_arg,
|
|
17
|
+
process_list_arg,
|
|
18
|
+
)
|
|
14
19
|
from cumulusci.tasks.salesforce import BaseSalesforceApiTask
|
|
15
20
|
from cumulusci.utils.http.requests_utils import safe_json_from_response
|
|
16
21
|
|
|
@@ -213,7 +213,6 @@ class ExtractData(SqlAlchemyMixin, BaseSalesforceApiTask):
|
|
|
213
213
|
IsPersonAccount_index = columns.index(mapping.fields["IsPersonAccount"])
|
|
214
214
|
|
|
215
215
|
def strip_name_field(record):
|
|
216
|
-
nonlocal Name_index, IsPersonAccount_index
|
|
217
216
|
if record[IsPersonAccount_index].lower() == "true":
|
|
218
217
|
record[Name_index] = ""
|
|
219
218
|
return record
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import sys
|
|
2
|
+
|
|
1
3
|
import pytest
|
|
2
4
|
|
|
3
5
|
from cumulusci.tasks.bulkdata.select_utils import (
|
|
@@ -618,6 +620,10 @@ def test_vectorize_records_mixed_numerical_boolean_categorical():
|
|
|
618
620
|
not PANDAS_AVAILABLE or not OPTIONAL_DEPENDENCIES_AVAILABLE,
|
|
619
621
|
reason="requires optional dependencies for annoy",
|
|
620
622
|
)
|
|
623
|
+
@pytest.mark.skipif(
|
|
624
|
+
sys.platform == "darwin" and sys.version_info[:2] in [(3, 11), (3, 13)],
|
|
625
|
+
reason="Annoy library has known compatibility issues on macOS with Python 3.11 and 3.13",
|
|
626
|
+
)
|
|
621
627
|
def test_annoy_post_process():
|
|
622
628
|
# Test data
|
|
623
629
|
load_records = [["Alice", "Engineer"], ["Bob", "Doctor"]]
|
|
@@ -7,7 +7,11 @@ from cumulusci.core.config import TaskConfig
|
|
|
7
7
|
from cumulusci.core.enums import StrEnum
|
|
8
8
|
from cumulusci.core.exceptions import CumulusCIException, TaskOptionsError
|
|
9
9
|
from cumulusci.core.tasks import BaseSalesforceTask
|
|
10
|
-
from cumulusci.core.utils import
|
|
10
|
+
from cumulusci.core.utils import (
|
|
11
|
+
determine_managed_mode,
|
|
12
|
+
process_bool_arg,
|
|
13
|
+
process_list_arg,
|
|
14
|
+
)
|
|
11
15
|
from cumulusci.salesforce_api.metadata import ApiRetrieveUnpackaged
|
|
12
16
|
from cumulusci.tasks.metadata.package import PackageXmlGenerator
|
|
13
17
|
from cumulusci.utils import inject_namespace
|
|
@@ -74,8 +78,8 @@ class BaseMetadataETLTask(BaseSalesforceTask, metaclass=ABCMeta):
|
|
|
74
78
|
self.options["namespaced_org"] or False
|
|
75
79
|
)
|
|
76
80
|
else:
|
|
77
|
-
self.options["namespaced_org"] = (
|
|
78
|
-
|
|
81
|
+
self.options["namespaced_org"] = bool(namespace) and namespace == getattr(
|
|
82
|
+
self.org_config, "namespace", None
|
|
79
83
|
)
|
|
80
84
|
|
|
81
85
|
def _inject_namespace(self, text):
|
cumulusci/tasks/push/README.md
CHANGED
|
@@ -1,21 +1,20 @@
|
|
|
1
1
|
# Push Upgrade API Scripts
|
|
2
2
|
|
|
3
|
-
These scripts are designed to work with the Salesforce Push Upgrade API (in Pilot in Winter 16) which exposes new objects via the Tooling API that allow interacting with push upgrades in a packaging org.
|
|
3
|
+
These scripts are designed to work with the Salesforce Push Upgrade API (in Pilot in Winter 16) which exposes new objects via the Tooling API that allow interacting with push upgrades in a packaging org. The main purpose of these scripts is to use the Push Upgrade API to automate push upgrades through Jenkins.
|
|
4
4
|
|
|
5
5
|
# push_api.py - Python Wrapper for Push Upgrade API
|
|
6
6
|
|
|
7
|
-
This python file provides wrapper classes around the Tooling API objects and abstracts interaction with them and their related data to make writing scripts easier.
|
|
7
|
+
This python file provides wrapper classes around the Tooling API objects and abstracts interaction with them and their related data to make writing scripts easier. All the other scripts in this directory use the SalesforcePushApi wrapper to interact with the Tooling API.
|
|
8
8
|
|
|
9
9
|
Initializing the SalesforcePushApi wrapper can be done with the following python code:
|
|
10
10
|
|
|
11
11
|
push_api = SalesforcePushApi(sf_user, sf_pass, sf_serverurl)
|
|
12
12
|
|
|
13
13
|
You can also pass two optional keyword args to the initialization to control the wrapper's behavior
|
|
14
|
-
|
|
15
|
-
* **lazy**: A list of objects that should be lazily looked up. Currently, the only implementations for this are 'jobs' and 'subscribers'. If either are included in the list, they will be looked up on demand when needed by a referenced object. For example, if you are querying all jobs and subscribers is not set to lazy, all subscribers will first be retrieved. If lazy is enabled, subscriber orgs will only be retrieved when trying to resolve references for a particular job. Generally, if you have a lot of subscribers and only expect your script to need to lookup a small number of them, enabling lazy for subscribers will reduce api calls and cause the script to run faster.
|
|
16
14
|
|
|
17
|
-
|
|
18
|
-
|
|
15
|
+
- **lazy**: A list of objects that should be lazily looked up. Currently, the only implementations for this are 'jobs' and 'subscribers'. If either are included in the list, they will be looked up on demand when needed by a referenced object. For example, if you are querying all jobs and subscribers is not set to lazy, all subscribers will first be retrieved. If lazy is enabled, subscriber orgs will only be retrieved when trying to resolve references for a particular job. Generally, if you have a lot of subscribers and only expect your script to need to lookup a small number of them, enabling lazy for subscribers will reduce api calls and cause the script to run faster.
|
|
16
|
+
|
|
17
|
+
- **default_where**: A dictionary with Push Upgrade API objects as key and a value containing a SOQL WHERE clause statement which is applied to all queries against the object to effectively set the universe for a given object. For example:
|
|
19
18
|
default_where = {'PackageSubscriber': "OrgType = 'Sandbox'"}
|
|
20
19
|
|
|
21
20
|
In the example above, the wrapper would never return a PackageSubscriber which is not a Sandbox org.
|
|
@@ -24,22 +23,22 @@ In the example above, the wrapper would never return a PackageSubscriber which i
|
|
|
24
23
|
|
|
25
24
|
## Common Environment Variables
|
|
26
25
|
|
|
27
|
-
The push scripts are all designed to receive their arguments via environment variables.
|
|
26
|
+
The push scripts are all designed to receive their arguments via environment variables. The following are common amongst all of the Push Scripts
|
|
28
27
|
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
28
|
+
- **SF_USERNAME**: The Salesforce username for the packaging org
|
|
29
|
+
- **SF_PASSWORD**: The Salesforce password and security token for the packaging org
|
|
30
|
+
- **SF_SERVERURL**: The login url for the Salesforce packaging org.
|
|
32
31
|
|
|
33
32
|
## get_version_id.py
|
|
34
33
|
|
|
35
|
-
Takes a namespace and version string and looks up the given version.
|
|
34
|
+
Takes a namespace and version string and looks up the given version. Returns the version's Salesforce Id.
|
|
36
35
|
|
|
37
36
|
The script handles parsing the version number string into a SOQL query against the MetadataPackageVersion object with the correct MajorVersion, MinorVersion, PatchVersion, ReleaseState, and BuildNumber (i.e. Beta number).
|
|
38
37
|
|
|
39
38
|
### Required Environment Variables
|
|
40
39
|
|
|
41
|
-
|
|
42
|
-
|
|
40
|
+
- **NAMESPACE**: The Package's namespace prefix
|
|
41
|
+
- **VERSION_NUMBER**: The version number string.
|
|
43
42
|
|
|
44
43
|
## orgs_for_push.py
|
|
45
44
|
|
|
@@ -47,13 +46,12 @@ Takes a MetadataPackageVersion Id and optionally a where clause to filter Subscr
|
|
|
47
46
|
|
|
48
47
|
### Required Environment Variables
|
|
49
48
|
|
|
50
|
-
|
|
49
|
+
- **VERSION**: The MetadataPackageVersion Id of the version you want to push upgrade. This is used to look for all users not on the version or a newer version
|
|
51
50
|
|
|
52
51
|
### Optional Environment Variables
|
|
53
52
|
|
|
54
|
-
|
|
53
|
+
- **SUBSCRIBER_WHERE**: An extra filter to be applied to all Subscriber queries. For example, setting this to OrgType = 'Sandbox' would find all Sandbox orgs eligible for push upgrade to the specified version
|
|
55
54
|
|
|
56
55
|
## failed_orgs_for_push.py
|
|
57
56
|
|
|
58
|
-
Takes a PackagePushRequest Id and optionally a where clause to filter Subscribers and returns a list of OrgId's one per line for all orgs which failed the
|
|
59
|
-
|
|
57
|
+
Takes a PackagePushRequest Id and optionally a where clause to filter Subscribers and returns a list of OrgId's one per line for all orgs which failed the
|
|
@@ -13,25 +13,25 @@ Start the section with `# Critical Changes` followed by your content
|
|
|
13
13
|
For example:
|
|
14
14
|
|
|
15
15
|
This won't be included
|
|
16
|
-
|
|
16
|
+
|
|
17
17
|
# Critical Changes
|
|
18
|
-
|
|
18
|
+
|
|
19
19
|
This will be included in Critical Changes
|
|
20
|
-
|
|
20
|
+
|
|
21
21
|
## Changes
|
|
22
22
|
|
|
23
|
-
The Changes section is where you should list off any changes worth highlight to users in the release notes.
|
|
23
|
+
The Changes section is where you should list off any changes worth highlight to users in the release notes. This section should always include instructions for users for any post-upgrade tasks they need to perform to enable new functionality. For example, users should be told to grant permissions and add new CustomFields to layouts.
|
|
24
24
|
|
|
25
25
|
Start the section with `# Changes` followed by your content
|
|
26
26
|
|
|
27
27
|
For example:
|
|
28
28
|
|
|
29
29
|
This won't be included
|
|
30
|
-
|
|
30
|
+
|
|
31
31
|
# Changes
|
|
32
|
-
|
|
32
|
+
|
|
33
33
|
This will be included in Changes
|
|
34
|
-
|
|
34
|
+
|
|
35
35
|
## Issues Closed
|
|
36
36
|
|
|
37
37
|
The Issues Closed section is where you should link to any closed issues that should be listed in the release notes.
|
|
@@ -41,9 +41,9 @@ Start the section with `# Changes` followed by your content
|
|
|
41
41
|
For example:
|
|
42
42
|
|
|
43
43
|
This won't be included
|
|
44
|
-
|
|
44
|
+
|
|
45
45
|
# Issues Closed
|
|
46
|
-
|
|
46
|
+
|
|
47
47
|
Fixes #102
|
|
48
48
|
resolves #100
|
|
49
49
|
This release closes #101
|
|
@@ -55,9 +55,9 @@ Would output:
|
|
|
55
55
|
#100: Title of Issue 100
|
|
56
56
|
#101: Title of Issue 101
|
|
57
57
|
#102: Title of Issue 102
|
|
58
|
-
|
|
58
|
+
|
|
59
59
|
A few notes about how issues are parsed:
|
|
60
60
|
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
61
|
+
- The parser uses the same format as Github: https://help.github.com/articles/closing-issues-via-commit-messages/
|
|
62
|
+
- The parser searches for all issue numbers and sorts them by their integer value, looks up their title, and outputs a formatted line with the issue number and title for each issue.
|
|
63
|
+
- The parser ignores everything else in the line that is not an issue number. Anything that is not an issue number will not appear in the rendered release notes
|
|
@@ -826,7 +826,7 @@ class TestRobotPerformanceKeywords:
|
|
|
826
826
|
elapsed_times.sort()
|
|
827
827
|
|
|
828
828
|
assert elapsed_times[1:] == [53, 11655.9, 18000.0]
|
|
829
|
-
assert float(elapsed_times[0]) < 3
|
|
829
|
+
assert float(elapsed_times[0]) < 3 or float(elapsed_times[0]) <= 3.0
|
|
830
830
|
|
|
831
831
|
def test_metrics(self):
|
|
832
832
|
pattern = "Max_CPU_Percent: "
|
|
@@ -12,7 +12,11 @@ from cumulusci.core.source_transforms.transforms import (
|
|
|
12
12
|
SourceTransform,
|
|
13
13
|
SourceTransformList,
|
|
14
14
|
)
|
|
15
|
-
from cumulusci.core.utils import
|
|
15
|
+
from cumulusci.core.utils import (
|
|
16
|
+
determine_managed_mode,
|
|
17
|
+
process_bool_arg,
|
|
18
|
+
process_list_arg,
|
|
19
|
+
)
|
|
16
20
|
from cumulusci.salesforce_api.metadata import ApiDeploy, ApiRetrieveUnpackaged
|
|
17
21
|
from cumulusci.salesforce_api.package_zip import MetadataPackageZipBuilder
|
|
18
22
|
from cumulusci.salesforce_api.rest_deploy import RestDeploy
|
|
@@ -6,7 +6,7 @@ from pathlib import Path
|
|
|
6
6
|
|
|
7
7
|
from cumulusci.cli.ui import CliTable
|
|
8
8
|
from cumulusci.core.exceptions import SalesforceException
|
|
9
|
-
from cumulusci.core.utils import
|
|
9
|
+
from cumulusci.core.utils import determine_managed_mode, process_list_arg
|
|
10
10
|
from cumulusci.tasks.salesforce import BaseSalesforceApiTask
|
|
11
11
|
from cumulusci.utils import inject_namespace
|
|
12
12
|
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
from simple_salesforce.exceptions import SalesforceError
|
|
4
4
|
|
|
5
5
|
from cumulusci.core.exceptions import TaskOptionsError
|
|
6
|
-
from cumulusci.core.utils import
|
|
6
|
+
from cumulusci.core.utils import determine_managed_mode, process_bool_arg
|
|
7
7
|
from cumulusci.tasks.salesforce import BaseSalesforceApiTask
|
|
8
8
|
|
|
9
9
|
|
|
@@ -1,7 +1,11 @@
|
|
|
1
1
|
from simple_salesforce.exceptions import SalesforceError
|
|
2
2
|
|
|
3
3
|
from cumulusci.core.exceptions import CumulusCIException
|
|
4
|
-
from cumulusci.core.utils import
|
|
4
|
+
from cumulusci.core.utils import (
|
|
5
|
+
determine_managed_mode,
|
|
6
|
+
process_bool_arg,
|
|
7
|
+
process_list_arg,
|
|
8
|
+
)
|
|
5
9
|
from cumulusci.tasks.salesforce import BaseSalesforceApiTask
|
|
6
10
|
from cumulusci.utils import inject_namespace
|
|
7
11
|
from cumulusci.utils.http.requests_utils import safe_json_from_response
|