cumulusci-plus 5.0.43__py3-none-any.whl → 5.0.45__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 CHANGED
@@ -1 +1 @@
1
- __version__ = "5.0.43"
1
+ __version__ = "5.0.45"
cumulusci/cli/cci.py CHANGED
@@ -173,11 +173,11 @@ def main(args=None):
173
173
  with set_debug_mode(debug):
174
174
  try:
175
175
  runtime = CliRuntime(load_keychain=False)
176
+ runtime.check_cumulusci_version()
176
177
  except Exception as e:
177
178
  handle_exception(e, is_error_command, tempfile_path, debug)
178
179
  sys.exit(1)
179
180
 
180
- runtime.check_cumulusci_version()
181
181
  should_show_stacktraces = runtime.universal_config.cli__show_stacktraces
182
182
 
183
183
  init_logger(debug=debug)
@@ -233,7 +233,8 @@ def handle_exception(
233
233
  with open(logfile_path, "a") as log_file:
234
234
  traceback.print_exc(file=log_file) # log stacktrace silently
235
235
 
236
- if should_show_stacktraces and not isinstance(error, USAGE_ERRORS):
236
+ # Show stacktraces when explicitly requested (e.g., --debug flag)
237
+ if should_show_stacktraces:
237
238
  error_console.print_exception()
238
239
 
239
240
 
@@ -53,7 +53,10 @@ class SfdxOrgConfig(OrgConfig):
53
53
  "Failed to parse json from output.\n "
54
54
  f"Exception: {e.__class__.__name__}\n Output: {''.join(stdout_list)}"
55
55
  )
56
- org_id = org_info["result"]["accessToken"].split("!")[0]
56
+ org_id = (
57
+ org_info["result"].get("id")
58
+ or org_info["result"]["accessToken"].split("!")[0]
59
+ )
57
60
 
58
61
  sfdx_info = {
59
62
  "instance_url": org_info["result"]["instanceUrl"],
@@ -64,18 +64,21 @@ class SecretsToEnv(BaseTask):
64
64
 
65
65
  self._init_secrets()
66
66
 
67
- output_file = self.parsed_options.env_path
68
-
69
67
  for secret_key, secret_name in self.secrets.items():
70
68
  if secret_key == "*":
71
69
  self.env_values.update(
72
70
  self._get_all_credentials(secret_key, secret_name=secret_name)
73
71
  )
74
72
  else:
75
- safe_value, _ = self._get_credential(
73
+ self.env_values[secret_key] = self._get_credential(
76
74
  secret_key, secret_key, secret_name=secret_name
77
75
  )
78
- self.env_values[secret_key] = safe_value
76
+ self.return_values = {"env_values": self.env_values}
77
+ self._write_env_file()
78
+
79
+ def _write_env_file(self):
80
+ safe_env_values = {}
81
+ output_file = self.parsed_options.env_path
79
82
 
80
83
  # Ensure output directory exists
81
84
  os.makedirs(os.path.dirname(output_file) or ".", exist_ok=True)
@@ -83,7 +86,37 @@ class SecretsToEnv(BaseTask):
83
86
  # Write env file with UTF-8 encoding to support Unicode characters
84
87
  with open(output_file, "w", encoding="utf-8") as env_file:
85
88
  for key, value in self.env_values.items():
86
- env_file.write(f'{key}="{value}"\n')
89
+ safe_env_values[key] = self._escape_env_value(value)
90
+ env_file.write(f'{key}="{safe_env_values[key]}"\n')
91
+
92
+ self.return_values["safe_env_values"] = safe_env_values
93
+
94
+ # https://pypi.org/project/python-dotenv/ -> README -> Escaping Values
95
+ def _escape_env_value(self, value):
96
+ """
97
+ Escape special characters for .env file values in double quotes.
98
+ Escapes: \\, \', \", \a, \b, \f, \n, \r, \t, \v
99
+ """
100
+ if not isinstance(value, str):
101
+ return value
102
+
103
+ escape_map = {
104
+ "\\": "\\\\", # Backslash must be first
105
+ "'": "\\'", # Single quote
106
+ '"': '\\"', # Double quote
107
+ "\a": "\\a", # Bell/Alert
108
+ "\b": "\\b", # Backspace
109
+ "\f": "\\f", # Form feed
110
+ "\n": "\\n", # Newline
111
+ "\r": "\\r", # Carriage return
112
+ "\t": "\\t", # Tab
113
+ "\v": "\\v", # Vertical tab
114
+ }
115
+
116
+ for char, escaped in escape_map.items():
117
+ value = value.replace(char, escaped)
118
+
119
+ return value
87
120
 
88
121
  def _get_credential(
89
122
  self,
@@ -108,8 +141,7 @@ class SecretsToEnv(BaseTask):
108
141
  self.logger.info(
109
142
  f"Set secrets env var from {self.provider.provider_type}: {env_key}={display_value}"
110
143
  )
111
- safe_value = cred_secret_value.replace('"', '\\"').replace("\n", "\\n")
112
- return safe_value, cred_secret_value
144
+ return cred_secret_value
113
145
 
114
146
  def _get_all_credentials(
115
147
  self, credential_key, display_value="*****", secret_name=None
@@ -129,7 +161,6 @@ class SecretsToEnv(BaseTask):
129
161
  self.logger.info(
130
162
  f"Set secrets env var from {self.provider.provider_type}: {key}={display_value}"
131
163
  )
132
- safe_value = value.replace('"', '\\"').replace("\n", "\\n")
133
- dict_values[key] = safe_value
164
+ dict_values[key] = value
134
165
 
135
166
  return dict_values