cumulusci-plus 5.0.21__py3-none-any.whl → 5.0.43__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/cli/logger.py +2 -2
- cumulusci/cli/service.py +20 -0
- cumulusci/cli/task.py +19 -3
- cumulusci/cli/tests/test_error.py +3 -1
- cumulusci/cli/tests/test_flow.py +279 -2
- cumulusci/cli/tests/test_org.py +5 -0
- cumulusci/cli/tests/test_service.py +15 -12
- cumulusci/cli/tests/test_task.py +122 -2
- cumulusci/cli/tests/utils.py +1 -4
- cumulusci/core/config/__init__.py +1 -0
- cumulusci/core/config/base_task_flow_config.py +26 -1
- cumulusci/core/config/org_config.py +2 -1
- cumulusci/core/config/project_config.py +14 -20
- cumulusci/core/config/scratch_org_config.py +12 -0
- cumulusci/core/config/tests/test_config.py +1 -0
- cumulusci/core/config/tests/test_config_expensive.py +9 -3
- cumulusci/core/config/universal_config.py +3 -4
- cumulusci/core/dependencies/base.py +5 -1
- cumulusci/core/dependencies/dependencies.py +1 -1
- cumulusci/core/dependencies/github.py +1 -2
- cumulusci/core/dependencies/resolvers.py +1 -1
- cumulusci/core/dependencies/tests/test_dependencies.py +1 -1
- cumulusci/core/dependencies/tests/test_resolvers.py +1 -1
- cumulusci/core/flowrunner.py +90 -6
- cumulusci/core/github.py +1 -1
- cumulusci/core/sfdx.py +3 -1
- cumulusci/core/source_transforms/tests/test_transforms.py +1 -1
- cumulusci/core/source_transforms/transforms.py +1 -1
- cumulusci/core/tasks.py +13 -2
- cumulusci/core/tests/test_flowrunner.py +100 -0
- cumulusci/core/tests/test_tasks.py +65 -0
- cumulusci/core/utils.py +3 -1
- cumulusci/core/versions.py +1 -1
- cumulusci/cumulusci.yml +73 -1
- cumulusci/oauth/client.py +1 -1
- cumulusci/plugins/plugin_base.py +5 -3
- cumulusci/robotframework/pageobjects/ObjectManagerPageObject.py +1 -1
- cumulusci/salesforce_api/rest_deploy.py +1 -1
- cumulusci/schema/cumulusci.jsonschema.json +69 -0
- cumulusci/tasks/apex/anon.py +1 -1
- cumulusci/tasks/apex/testrunner.py +421 -144
- cumulusci/tasks/apex/tests/test_apex_tasks.py +917 -1
- cumulusci/tasks/bulkdata/extract.py +0 -1
- cumulusci/tasks/bulkdata/extract_dataset_utils/extract_yml.py +1 -1
- cumulusci/tasks/bulkdata/extract_dataset_utils/synthesize_extract_declarations.py +1 -1
- cumulusci/tasks/bulkdata/extract_dataset_utils/tests/test_extract_yml.py +1 -1
- cumulusci/tasks/bulkdata/generate_and_load_data.py +136 -12
- cumulusci/tasks/bulkdata/mapping_parser.py +139 -44
- cumulusci/tasks/bulkdata/select_utils.py +1 -1
- cumulusci/tasks/bulkdata/snowfakery.py +100 -25
- cumulusci/tasks/bulkdata/tests/test_generate_and_load.py +159 -0
- cumulusci/tasks/bulkdata/tests/test_load.py +0 -2
- cumulusci/tasks/bulkdata/tests/test_mapping_parser.py +763 -1
- cumulusci/tasks/bulkdata/tests/test_select_utils.py +46 -0
- cumulusci/tasks/bulkdata/tests/test_snowfakery.py +133 -0
- cumulusci/tasks/create_package_version.py +190 -16
- cumulusci/tasks/datadictionary.py +1 -1
- cumulusci/tasks/metadata_etl/__init__.py +2 -0
- cumulusci/tasks/metadata_etl/applications.py +256 -0
- cumulusci/tasks/metadata_etl/base.py +7 -3
- cumulusci/tasks/metadata_etl/layouts.py +1 -1
- cumulusci/tasks/metadata_etl/permissions.py +1 -1
- cumulusci/tasks/metadata_etl/remote_site_settings.py +2 -2
- cumulusci/tasks/metadata_etl/tests/test_applications.py +710 -0
- cumulusci/tasks/push/README.md +15 -17
- cumulusci/tasks/release_notes/README.md +13 -13
- cumulusci/tasks/release_notes/generator.py +13 -8
- cumulusci/tasks/robotframework/tests/test_robotframework.py +6 -1
- cumulusci/tasks/salesforce/Deploy.py +53 -2
- cumulusci/tasks/salesforce/SfPackageCommands.py +363 -0
- cumulusci/tasks/salesforce/__init__.py +1 -0
- cumulusci/tasks/salesforce/assign_ps_psg.py +448 -0
- 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/getPackageVersion.py +89 -0
- cumulusci/tasks/salesforce/insert_record.py +18 -19
- cumulusci/tasks/salesforce/sourcetracking.py +1 -1
- cumulusci/tasks/salesforce/tests/test_Deploy.py +316 -1
- cumulusci/tasks/salesforce/tests/test_SfPackageCommands.py +554 -0
- cumulusci/tasks/salesforce/tests/test_assign_ps_psg.py +1055 -0
- cumulusci/tasks/salesforce/tests/test_enable_prediction.py +4 -2
- cumulusci/tasks/salesforce/tests/test_getPackageVersion.py +651 -0
- cumulusci/tasks/salesforce/tests/test_update_dependencies.py +1 -1
- cumulusci/tasks/salesforce/tests/test_update_external_auth_identity_provider.py +927 -0
- cumulusci/tasks/salesforce/tests/test_update_external_credential.py +1427 -0
- cumulusci/tasks/salesforce/tests/test_update_named_credential.py +1042 -0
- cumulusci/tasks/salesforce/tests/test_update_record.py +512 -0
- cumulusci/tasks/salesforce/update_dependencies.py +2 -2
- cumulusci/tasks/salesforce/update_external_auth_identity_provider.py +551 -0
- cumulusci/tasks/salesforce/update_external_credential.py +647 -0
- cumulusci/tasks/salesforce/update_named_credential.py +441 -0
- cumulusci/tasks/salesforce/update_profile.py +17 -13
- cumulusci/tasks/salesforce/update_record.py +217 -0
- cumulusci/tasks/salesforce/users/permsets.py +62 -5
- cumulusci/tasks/salesforce/users/tests/test_permsets.py +237 -11
- cumulusci/tasks/sfdmu/__init__.py +0 -0
- cumulusci/tasks/sfdmu/sfdmu.py +376 -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 +1012 -0
- cumulusci/tasks/tests/test_create_package_version.py +716 -1
- cumulusci/tasks/tests/test_util.py +42 -0
- cumulusci/tasks/util.py +37 -1
- cumulusci/tasks/utility/copyContents.py +402 -0
- cumulusci/tasks/utility/credentialManager.py +302 -0
- cumulusci/tasks/utility/directoryRecreator.py +30 -0
- cumulusci/tasks/utility/env_management.py +1 -1
- cumulusci/tasks/utility/secretsToEnv.py +135 -0
- cumulusci/tasks/utility/tests/test_copyContents.py +1719 -0
- cumulusci/tasks/utility/tests/test_credentialManager.py +1150 -0
- cumulusci/tasks/utility/tests/test_directoryRecreator.py +439 -0
- cumulusci/tasks/utility/tests/test_secretsToEnv.py +1118 -0
- cumulusci/tests/test_integration_infrastructure.py +3 -1
- cumulusci/tests/test_utils.py +70 -6
- cumulusci/utils/__init__.py +54 -9
- cumulusci/utils/classutils.py +5 -2
- cumulusci/utils/http/tests/cassettes/ManualEditTestCompositeParallelSalesforce.test_http_headers.yaml +31 -30
- cumulusci/utils/options.py +23 -1
- cumulusci/utils/parallel/task_worker_queues/parallel_worker.py +1 -1
- cumulusci/utils/yaml/cumulusci_yml.py +8 -3
- cumulusci/utils/yaml/model_parser.py +2 -2
- cumulusci/utils/yaml/tests/test_cumulusci_yml.py +1 -1
- cumulusci/utils/yaml/tests/test_model_parser.py +3 -3
- cumulusci/vcs/base.py +23 -15
- cumulusci/vcs/bootstrap.py +5 -4
- cumulusci/vcs/utils/list_modified_files.py +189 -0
- cumulusci/vcs/utils/tests/test_list_modified_files.py +588 -0
- {cumulusci_plus-5.0.21.dist-info → cumulusci_plus-5.0.43.dist-info}/METADATA +11 -10
- {cumulusci_plus-5.0.21.dist-info → cumulusci_plus-5.0.43.dist-info}/RECORD +135 -104
- {cumulusci_plus-5.0.21.dist-info → cumulusci_plus-5.0.43.dist-info}/WHEEL +1 -1
- {cumulusci_plus-5.0.21.dist-info → cumulusci_plus-5.0.43.dist-info}/entry_points.txt +0 -0
- {cumulusci_plus-5.0.21.dist-info → cumulusci_plus-5.0.43.dist-info}/licenses/AUTHORS.rst +0 -0
- {cumulusci_plus-5.0.21.dist-info → cumulusci_plus-5.0.43.dist-info}/licenses/LICENSE +0 -0
|
@@ -0,0 +1,189 @@
|
|
|
1
|
+
"""Utility functions and task for working with git modified files."""
|
|
2
|
+
|
|
3
|
+
import os
|
|
4
|
+
import subprocess
|
|
5
|
+
from typing import Optional, Set
|
|
6
|
+
|
|
7
|
+
from cumulusci.core.tasks import BaseTask
|
|
8
|
+
from cumulusci.utils.options import CCIOptions, Field, ListOfStringsOption
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
class ListModifiedFiles(BaseTask):
|
|
12
|
+
"""Task to list modified files in a git repository.
|
|
13
|
+
|
|
14
|
+
This task compares the current working directory against a base reference
|
|
15
|
+
(branch, tag, or commit) and lists all modified files, optionally filtered
|
|
16
|
+
to package directories.
|
|
17
|
+
"""
|
|
18
|
+
|
|
19
|
+
class Options(CCIOptions):
|
|
20
|
+
base_ref: Optional[str] = Field(
|
|
21
|
+
None,
|
|
22
|
+
description="Git reference (branch, tag, or commit) to compare against. "
|
|
23
|
+
"If not set, uses the default branch of the repository.",
|
|
24
|
+
)
|
|
25
|
+
file_extensions: Optional[ListOfStringsOption] = Field(
|
|
26
|
+
None,
|
|
27
|
+
description="List of file extensions to extract. If not set, all file extensions are extracted. Example: ['cls', 'flow', 'trigger']",
|
|
28
|
+
)
|
|
29
|
+
directories: ListOfStringsOption = Field(
|
|
30
|
+
["force-app", "src"],
|
|
31
|
+
description="List of directories to extract. If not set, only the default package directory is extracted. Example: ['force-app', 'src']",
|
|
32
|
+
)
|
|
33
|
+
|
|
34
|
+
parsed_options: Options
|
|
35
|
+
|
|
36
|
+
def _init_options(self, kwargs):
|
|
37
|
+
super(ListModifiedFiles, self)._init_options(kwargs)
|
|
38
|
+
|
|
39
|
+
if self.parsed_options.base_ref is None:
|
|
40
|
+
self.parsed_options.base_ref = (
|
|
41
|
+
self.project_config.project__git__default_branch or "main"
|
|
42
|
+
)
|
|
43
|
+
|
|
44
|
+
def _run_task(self):
|
|
45
|
+
"""Run the task to list modified files."""
|
|
46
|
+
self.return_values = {
|
|
47
|
+
"files": set(),
|
|
48
|
+
"file_names": set(),
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
# Check if the current base folder has git
|
|
52
|
+
if self.project_config.get_repo() is None:
|
|
53
|
+
self.logger.info("No git repository found.")
|
|
54
|
+
return
|
|
55
|
+
|
|
56
|
+
# Get changed files
|
|
57
|
+
changed_files = self._get_git_changed_files()
|
|
58
|
+
|
|
59
|
+
if changed_files is None:
|
|
60
|
+
self.logger.warning(
|
|
61
|
+
f"Could not determine git changes against {self.parsed_options.base_ref}."
|
|
62
|
+
)
|
|
63
|
+
return
|
|
64
|
+
|
|
65
|
+
if not changed_files:
|
|
66
|
+
self.logger.info("No files changed.")
|
|
67
|
+
return
|
|
68
|
+
|
|
69
|
+
# Filter to directories if requested
|
|
70
|
+
changed_files = self._filter_package_changed_files(changed_files)
|
|
71
|
+
|
|
72
|
+
if not changed_files:
|
|
73
|
+
self.logger.info(
|
|
74
|
+
f"No changed files found in directories: {', '.join(self.parsed_options.directories)}."
|
|
75
|
+
)
|
|
76
|
+
return
|
|
77
|
+
|
|
78
|
+
self.return_values["files"] = sorted(changed_files)
|
|
79
|
+
|
|
80
|
+
# Extract file names if requested
|
|
81
|
+
file_names = set()
|
|
82
|
+
if self.parsed_options.file_extensions is not None:
|
|
83
|
+
file_names = self._extract_file_names_from_files(changed_files)
|
|
84
|
+
if file_names:
|
|
85
|
+
self.logger.info(
|
|
86
|
+
f"Found {len(file_names)} affected file(s): {', '.join(sorted(file_names))}"
|
|
87
|
+
)
|
|
88
|
+
self.return_values["file_names"] = file_names
|
|
89
|
+
else:
|
|
90
|
+
self.logger.info("No file names found in changed files.")
|
|
91
|
+
|
|
92
|
+
# Log file list if not too many
|
|
93
|
+
if len(changed_files) > 0 and len(changed_files) <= 20:
|
|
94
|
+
self.logger.info("Changed files:")
|
|
95
|
+
for file_path in changed_files:
|
|
96
|
+
self.logger.info(f" {file_path}")
|
|
97
|
+
else:
|
|
98
|
+
self.logger.info(f" ... and {len(changed_files) - 20} more files")
|
|
99
|
+
|
|
100
|
+
if len(file_names) > 0 and len(file_names) <= 20:
|
|
101
|
+
self.logger.info("Selected file names:")
|
|
102
|
+
for file_name in file_names:
|
|
103
|
+
self.logger.info(f" {file_name}")
|
|
104
|
+
else:
|
|
105
|
+
self.logger.info(f" ... and {len(file_names) - 20} more file names")
|
|
106
|
+
|
|
107
|
+
def _get_git_changed_files(self) -> Optional[Set[str]]:
|
|
108
|
+
"""Get list of changed files using git diff.
|
|
109
|
+
Returns:
|
|
110
|
+
Set of changed file paths, or None if git command failed
|
|
111
|
+
"""
|
|
112
|
+
try:
|
|
113
|
+
result = subprocess.run(
|
|
114
|
+
["git", "diff", "--name-only", self.parsed_options.base_ref],
|
|
115
|
+
capture_output=True,
|
|
116
|
+
text=True,
|
|
117
|
+
check=False,
|
|
118
|
+
)
|
|
119
|
+
|
|
120
|
+
if result.returncode != 0:
|
|
121
|
+
self.logger.warning(
|
|
122
|
+
f"Git diff failed with return code {result.returncode}: {result.stderr}"
|
|
123
|
+
)
|
|
124
|
+
return None
|
|
125
|
+
|
|
126
|
+
return set([f.strip() for f in result.stdout.splitlines() if f.strip()])
|
|
127
|
+
|
|
128
|
+
except FileNotFoundError:
|
|
129
|
+
self.logger.warning(
|
|
130
|
+
"Git command not found. Cannot determine changed files."
|
|
131
|
+
)
|
|
132
|
+
return set()
|
|
133
|
+
except Exception as e:
|
|
134
|
+
self.logger.warning(f"Error running git diff: {str(e)}")
|
|
135
|
+
return set()
|
|
136
|
+
|
|
137
|
+
def _filter_package_changed_files(self, changed_files: Set[str]) -> Set[str]:
|
|
138
|
+
"""Filter changed files to only include those in the package directories."""
|
|
139
|
+
package_dir = os.path.basename(
|
|
140
|
+
os.path.normpath(self.project_config.default_package_path)
|
|
141
|
+
)
|
|
142
|
+
|
|
143
|
+
if package_dir not in self.parsed_options.directories:
|
|
144
|
+
self.parsed_options.directories.append(package_dir)
|
|
145
|
+
|
|
146
|
+
filtered_files = set()
|
|
147
|
+
for file_path in changed_files:
|
|
148
|
+
# Check if file is in any of the package directories
|
|
149
|
+
for pkg_dir in self.parsed_options.directories:
|
|
150
|
+
if file_path.startswith(pkg_dir + "/") or file_path.startswith(
|
|
151
|
+
pkg_dir + "\\"
|
|
152
|
+
):
|
|
153
|
+
filtered_files.add(file_path)
|
|
154
|
+
break
|
|
155
|
+
|
|
156
|
+
return set(filtered_files)
|
|
157
|
+
|
|
158
|
+
def _extract_file_names_from_files(self, changed_files: Set[str]) -> Set[str]:
|
|
159
|
+
"""Extract file names from changed file paths based on specified extensions.
|
|
160
|
+
|
|
161
|
+
Args:
|
|
162
|
+
changed_files: List of changed file paths
|
|
163
|
+
|
|
164
|
+
Returns:
|
|
165
|
+
Set of file names found in changed files matching the specified extensions
|
|
166
|
+
"""
|
|
167
|
+
file_names = set()
|
|
168
|
+
|
|
169
|
+
for file_path in changed_files:
|
|
170
|
+
# Check if file path ends with any of the specified extensions
|
|
171
|
+
# Handle both "cls" and ".cls" formats
|
|
172
|
+
matched_extension = None
|
|
173
|
+
for ext in self.parsed_options.file_extensions:
|
|
174
|
+
# Normalize extension to have a dot prefix
|
|
175
|
+
normalized_ext = ext if ext.startswith(".") else f".{ext}"
|
|
176
|
+
if file_path.endswith(normalized_ext):
|
|
177
|
+
matched_extension = normalized_ext
|
|
178
|
+
break
|
|
179
|
+
|
|
180
|
+
if matched_extension:
|
|
181
|
+
# Extract file name from path
|
|
182
|
+
# Examples: force-app/main/default/classes/MyClass.cls -> MyClass
|
|
183
|
+
# src/classes/MyClass.cls -> MyClass
|
|
184
|
+
file_name = os.path.basename(file_path)
|
|
185
|
+
file_name = file_name.replace(matched_extension, "")
|
|
186
|
+
if file_name:
|
|
187
|
+
file_names.add(file_name)
|
|
188
|
+
|
|
189
|
+
return file_names
|