salesforce-data-customcode 0.1.26__tar.gz → 0.1.28__tar.gz

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.
Files changed (64) hide show
  1. {salesforce_data_customcode-0.1.26 → salesforce_data_customcode-0.1.28}/PKG-INFO +1 -1
  2. {salesforce_data_customcode-0.1.26 → salesforce_data_customcode-0.1.28}/pyproject.toml +1 -1
  3. {salesforce_data_customcode-0.1.26 → salesforce_data_customcode-0.1.28}/src/datacustomcode/cli.py +4 -1
  4. {salesforce_data_customcode-0.1.26 → salesforce_data_customcode-0.1.28}/src/datacustomcode/cmd.py +6 -2
  5. {salesforce_data_customcode-0.1.26 → salesforce_data_customcode-0.1.28}/src/datacustomcode/deploy.py +45 -22
  6. salesforce_data_customcode-0.1.28/src/datacustomcode/templates/function/build_native_dependencies.sh +8 -0
  7. salesforce_data_customcode-0.1.26/src/datacustomcode/templates/script/build_native_dependencies.sh +0 -9
  8. {salesforce_data_customcode-0.1.26 → salesforce_data_customcode-0.1.28}/LICENSE.txt +0 -0
  9. {salesforce_data_customcode-0.1.26 → salesforce_data_customcode-0.1.28}/README.md +0 -0
  10. {salesforce_data_customcode-0.1.26 → salesforce_data_customcode-0.1.28}/src/datacustomcode/__init__.py +0 -0
  11. {salesforce_data_customcode-0.1.26 → salesforce_data_customcode-0.1.28}/src/datacustomcode/auth.py +0 -0
  12. {salesforce_data_customcode-0.1.26 → salesforce_data_customcode-0.1.28}/src/datacustomcode/client.py +0 -0
  13. {salesforce_data_customcode-0.1.26 → salesforce_data_customcode-0.1.28}/src/datacustomcode/config.py +0 -0
  14. {salesforce_data_customcode-0.1.26 → salesforce_data_customcode-0.1.28}/src/datacustomcode/config.yaml +0 -0
  15. {salesforce_data_customcode-0.1.26 → salesforce_data_customcode-0.1.28}/src/datacustomcode/credentials.py +0 -0
  16. {salesforce_data_customcode-0.1.26 → salesforce_data_customcode-0.1.28}/src/datacustomcode/file/__init__.py +0 -0
  17. {salesforce_data_customcode-0.1.26 → salesforce_data_customcode-0.1.28}/src/datacustomcode/file/base.py +0 -0
  18. {salesforce_data_customcode-0.1.26 → salesforce_data_customcode-0.1.28}/src/datacustomcode/file/path/__init__.py +0 -0
  19. {salesforce_data_customcode-0.1.26 → salesforce_data_customcode-0.1.28}/src/datacustomcode/file/path/default.py +0 -0
  20. {salesforce_data_customcode-0.1.26 → salesforce_data_customcode-0.1.28}/src/datacustomcode/io/__init__.py +0 -0
  21. {salesforce_data_customcode-0.1.26 → salesforce_data_customcode-0.1.28}/src/datacustomcode/io/base.py +0 -0
  22. {salesforce_data_customcode-0.1.26 → salesforce_data_customcode-0.1.28}/src/datacustomcode/io/reader/__init__.py +0 -0
  23. {salesforce_data_customcode-0.1.26 → salesforce_data_customcode-0.1.28}/src/datacustomcode/io/reader/base.py +0 -0
  24. {salesforce_data_customcode-0.1.26 → salesforce_data_customcode-0.1.28}/src/datacustomcode/io/reader/query_api.py +0 -0
  25. {salesforce_data_customcode-0.1.26 → salesforce_data_customcode-0.1.28}/src/datacustomcode/io/reader/sf_cli.py +0 -0
  26. {salesforce_data_customcode-0.1.26 → salesforce_data_customcode-0.1.28}/src/datacustomcode/io/reader/utils.py +0 -0
  27. {salesforce_data_customcode-0.1.26 → salesforce_data_customcode-0.1.28}/src/datacustomcode/io/writer/__init__.py +0 -0
  28. {salesforce_data_customcode-0.1.26 → salesforce_data_customcode-0.1.28}/src/datacustomcode/io/writer/base.py +0 -0
  29. {salesforce_data_customcode-0.1.26 → salesforce_data_customcode-0.1.28}/src/datacustomcode/io/writer/csv.py +0 -0
  30. {salesforce_data_customcode-0.1.26 → salesforce_data_customcode-0.1.28}/src/datacustomcode/io/writer/print.py +0 -0
  31. {salesforce_data_customcode-0.1.26 → salesforce_data_customcode-0.1.28}/src/datacustomcode/mixin.py +0 -0
  32. {salesforce_data_customcode-0.1.26 → salesforce_data_customcode-0.1.28}/src/datacustomcode/proxy/__init__.py +0 -0
  33. {salesforce_data_customcode-0.1.26 → salesforce_data_customcode-0.1.28}/src/datacustomcode/proxy/base.py +0 -0
  34. {salesforce_data_customcode-0.1.26 → salesforce_data_customcode-0.1.28}/src/datacustomcode/proxy/client/LocalProxyClientProvider.py +0 -0
  35. {salesforce_data_customcode-0.1.26 → salesforce_data_customcode-0.1.28}/src/datacustomcode/proxy/client/__init__.py +0 -0
  36. {salesforce_data_customcode-0.1.26 → salesforce_data_customcode-0.1.28}/src/datacustomcode/proxy/client/base.py +0 -0
  37. {salesforce_data_customcode-0.1.26 → salesforce_data_customcode-0.1.28}/src/datacustomcode/py.typed +0 -0
  38. {salesforce_data_customcode-0.1.26 → salesforce_data_customcode-0.1.28}/src/datacustomcode/run.py +0 -0
  39. {salesforce_data_customcode-0.1.26 → salesforce_data_customcode-0.1.28}/src/datacustomcode/scan.py +0 -0
  40. {salesforce_data_customcode-0.1.26 → salesforce_data_customcode-0.1.28}/src/datacustomcode/spark/__init__.py +0 -0
  41. {salesforce_data_customcode-0.1.26 → salesforce_data_customcode-0.1.28}/src/datacustomcode/spark/base.py +0 -0
  42. {salesforce_data_customcode-0.1.26 → salesforce_data_customcode-0.1.28}/src/datacustomcode/spark/default.py +0 -0
  43. {salesforce_data_customcode-0.1.26 → salesforce_data_customcode-0.1.28}/src/datacustomcode/template.py +0 -0
  44. {salesforce_data_customcode-0.1.26 → salesforce_data_customcode-0.1.28}/src/datacustomcode/templates/function/.devcontainer/devcontainer.json +0 -0
  45. {salesforce_data_customcode-0.1.26 → salesforce_data_customcode-0.1.28}/src/datacustomcode/templates/function/Dockerfile.dependencies +0 -0
  46. {salesforce_data_customcode-0.1.26 → salesforce_data_customcode-0.1.28}/src/datacustomcode/templates/function/README.md +0 -0
  47. {salesforce_data_customcode-0.1.26 → salesforce_data_customcode-0.1.28}/src/datacustomcode/templates/function/payload/config.json +0 -0
  48. {salesforce_data_customcode-0.1.26 → salesforce_data_customcode-0.1.28}/src/datacustomcode/templates/function/payload/entrypoint.py +0 -0
  49. {salesforce_data_customcode-0.1.26 → salesforce_data_customcode-0.1.28}/src/datacustomcode/templates/function/requirements-dev.txt +0 -0
  50. {salesforce_data_customcode-0.1.26 → salesforce_data_customcode-0.1.28}/src/datacustomcode/templates/function/requirements.txt +0 -0
  51. {salesforce_data_customcode-0.1.26 → salesforce_data_customcode-0.1.28}/src/datacustomcode/templates/script/.devcontainer/devcontainer.json +0 -0
  52. {salesforce_data_customcode-0.1.26 → salesforce_data_customcode-0.1.28}/src/datacustomcode/templates/script/Dockerfile +0 -0
  53. {salesforce_data_customcode-0.1.26 → salesforce_data_customcode-0.1.28}/src/datacustomcode/templates/script/Dockerfile.dependencies +0 -0
  54. {salesforce_data_customcode-0.1.26 → salesforce_data_customcode-0.1.28}/src/datacustomcode/templates/script/README.md +0 -0
  55. {salesforce_data_customcode-0.1.26 → salesforce_data_customcode-0.1.28}/src/datacustomcode/templates/script/account.ipynb +0 -0
  56. {salesforce_data_customcode-0.1.26/src/datacustomcode/templates/function → salesforce_data_customcode-0.1.28/src/datacustomcode/templates/script}/build_native_dependencies.sh +0 -0
  57. {salesforce_data_customcode-0.1.26 → salesforce_data_customcode-0.1.28}/src/datacustomcode/templates/script/examples/employee_hierarchy/employee_data.csv +0 -0
  58. {salesforce_data_customcode-0.1.26 → salesforce_data_customcode-0.1.28}/src/datacustomcode/templates/script/examples/employee_hierarchy/entrypoint.py +0 -0
  59. {salesforce_data_customcode-0.1.26 → salesforce_data_customcode-0.1.28}/src/datacustomcode/templates/script/jupyterlab.sh +0 -0
  60. {salesforce_data_customcode-0.1.26 → salesforce_data_customcode-0.1.28}/src/datacustomcode/templates/script/payload/config.json +0 -0
  61. {salesforce_data_customcode-0.1.26 → salesforce_data_customcode-0.1.28}/src/datacustomcode/templates/script/payload/entrypoint.py +0 -0
  62. {salesforce_data_customcode-0.1.26 → salesforce_data_customcode-0.1.28}/src/datacustomcode/templates/script/requirements-dev.txt +0 -0
  63. {salesforce_data_customcode-0.1.26 → salesforce_data_customcode-0.1.28}/src/datacustomcode/templates/script/requirements.txt +0 -0
  64. {salesforce_data_customcode-0.1.26 → salesforce_data_customcode-0.1.28}/src/datacustomcode/version.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: salesforce-data-customcode
3
- Version: 0.1.26
3
+ Version: 0.1.28
4
4
  Summary: Data Cloud Custom Code SDK
5
5
  License-Expression: Apache-2.0
6
6
  License-File: LICENSE.txt
@@ -18,7 +18,7 @@ license = "Apache-2.0"
18
18
  name = "salesforce-data-customcode"
19
19
  readme = "README.md"
20
20
  requires-python = ">=3.10,<3.12"
21
- version = "0.1.26"
21
+ version = "0.1.28"
22
22
 
23
23
  [tool.black]
24
24
  exclude = '''
@@ -134,9 +134,12 @@ def auth(profile: str):
134
134
  @click.option("--network", default="default")
135
135
  def zip(path: str, network: str):
136
136
  from datacustomcode.deploy import zip
137
+ from datacustomcode.scan import find_base_directory, get_package_type
137
138
 
138
139
  logger.debug("Zipping project")
139
- zip(path, network)
140
+ base_directory = find_base_directory(path)
141
+ package_type = get_package_type(base_directory)
142
+ zip(path, network, package_type)
140
143
 
141
144
 
142
145
  @cli.command()
@@ -85,9 +85,13 @@ def _cmd_output(
85
85
  **kwargs: Any,
86
86
  ) -> tuple[int, bytes, Union[bytes, None]]:
87
87
  _setdefault_kwargs(kwargs)
88
+ kwargs.setdefault("shell", True)
89
+ # On Windows, Popen with shell=True and a sequence uses list2cmdline() which
90
+ # quotes the entire string, causing cmd.exe to fail. Joining to a plain string
91
+ # works correctly on both Unix (/bin/sh -c "...") and Windows (cmd.exe /c ...).
92
+ cmd_arg: Union[tuple[str, ...], str] = " ".join(cmd) if kwargs.get("shell") else cmd
88
93
  try:
89
- kwargs.setdefault("shell", True)
90
- proc = subprocess.Popen(cmd, **kwargs)
94
+ proc = subprocess.Popen(cmd_arg, **kwargs)
91
95
  except OSError as e:
92
96
  returncode, stdout_b, stderr_b = _oserror_to_output(e)
93
97
  else:
@@ -272,45 +272,68 @@ def create_deployment(
272
272
  raise
273
273
 
274
274
 
275
- PLATFORM_ENV_VAR = "DOCKER_DEFAULT_PLATFORM=linux/amd64"
275
+ PLATFORM_ENV = {"DOCKER_DEFAULT_PLATFORM": "linux/amd64"}
276
276
  DOCKER_IMAGE_NAME = "datacloud-custom-code-dependency-builder"
277
277
  DEPENDENCIES_ARCHIVE_NAME = "native_dependencies"
278
278
  DEPENDENCIES_ARCHIVE_FULL_NAME = f"{DEPENDENCIES_ARCHIVE_NAME}.tar.gz"
279
279
  DEPENDENCIES_ARCHIVE_PATH = os.path.join(
280
280
  "payload", "archives", DEPENDENCIES_ARCHIVE_FULL_NAME
281
281
  )
282
+ PY_FILES_PATH = os.path.join("payload", "py-files")
282
283
  ZIP_FILE_NAME = "deployment.zip"
283
284
 
284
285
 
285
- def prepare_dependency_archive(directory: str, docker_network: str) -> None:
286
+ def prepare_dependency_archive(
287
+ directory: str, docker_network: str, package_type: str
288
+ ) -> None:
286
289
  cmd = f"docker images -q {DOCKER_IMAGE_NAME}"
287
290
  image_exists = cmd_output(cmd)
288
291
 
292
+ docker_env = {**os.environ, **PLATFORM_ENV}
293
+
289
294
  if not image_exists:
290
295
  logger.info(f"Building docker image with docker network: {docker_network}...")
291
296
  cmd = docker_build_cmd(docker_network)
292
- cmd_output(cmd)
297
+ cmd_output(cmd, env=docker_env)
293
298
 
294
- with tempfile.TemporaryDirectory() as temp_dir:
299
+ # ignore_cleanup_errors=True: on Windows, Docker creates files inside the
300
+ # mounted volume whose permissions prevent the host from deleting them.
301
+ # The archive has already been copied out, so silently skipping leftover
302
+ # files is safe and avoids a fatal error on context-manager exit.
303
+ with tempfile.TemporaryDirectory(ignore_cleanup_errors=True) as temp_dir:
295
304
  logger.info(
296
305
  f"Building dependencies archive with docker network: {docker_network}"
297
306
  )
298
307
  shutil.copy("requirements.txt", temp_dir)
299
308
  shutil.copy("build_native_dependencies.sh", temp_dir)
300
309
  cmd = docker_run_cmd(docker_network, temp_dir)
301
- cmd_output(cmd)
302
- archives_temp_path = os.path.join(temp_dir, DEPENDENCIES_ARCHIVE_FULL_NAME)
303
- os.makedirs(os.path.dirname(DEPENDENCIES_ARCHIVE_PATH), exist_ok=True)
304
- shutil.copy(archives_temp_path, DEPENDENCIES_ARCHIVE_PATH)
305
-
306
- logger.info(f"Dependencies archived to {DEPENDENCIES_ARCHIVE_PATH}")
310
+ cmd_output(cmd, env=docker_env)
311
+ if package_type == "function":
312
+ source_py_files = os.path.join(temp_dir, "py-files")
313
+ if os.path.exists(source_py_files):
314
+ logger.info(
315
+ f"py-files directory found at {source_py_files}. "
316
+ "Copying to payload directory..."
317
+ )
318
+ os.makedirs(os.path.dirname(PY_FILES_PATH), exist_ok=True)
319
+ if os.path.exists(PY_FILES_PATH):
320
+ shutil.rmtree(PY_FILES_PATH)
321
+ shutil.copytree(source_py_files, PY_FILES_PATH)
322
+ logger.info(f"py-files copied to {PY_FILES_PATH}")
323
+ else:
324
+ logger.info(
325
+ f"No py-files directory found at {source_py_files}. "
326
+ "Skipping py-files copy."
327
+ )
328
+ else:
329
+ archives_temp_path = os.path.join(temp_dir, DEPENDENCIES_ARCHIVE_FULL_NAME)
330
+ os.makedirs(os.path.dirname(DEPENDENCIES_ARCHIVE_PATH), exist_ok=True)
331
+ shutil.copy(archives_temp_path, DEPENDENCIES_ARCHIVE_PATH)
332
+ logger.info(f"Dependencies archived to {DEPENDENCIES_ARCHIVE_PATH}")
307
333
 
308
334
 
309
335
  def docker_build_cmd(network: str) -> str:
310
- cmd = (
311
- f"{PLATFORM_ENV_VAR} docker build -t {DOCKER_IMAGE_NAME} "
312
- f"--file Dockerfile.dependencies . "
313
- )
336
+ cmd = f"docker build -t {DOCKER_IMAGE_NAME} --file Dockerfile.dependencies . "
314
337
 
315
338
  if network != "default":
316
339
  cmd = cmd + f"--network {network}"
@@ -318,12 +341,11 @@ def docker_build_cmd(network: str) -> str:
318
341
  return cmd
319
342
 
320
343
 
321
- def docker_run_cmd(network: str, temp_dir) -> str:
322
- cmd = (
323
- f"{PLATFORM_ENV_VAR} docker run --rm "
324
- f"-v {temp_dir}:/workspace "
325
- f"{DOCKER_IMAGE_NAME} "
326
- )
344
+ def docker_run_cmd(network: str, temp_dir: str) -> str:
345
+ # Normalise path separators: Docker expects forward slashes even on Windows,
346
+ # and quoting handles paths that contain spaces.
347
+ docker_path = temp_dir.replace("\\", "/")
348
+ cmd = f'docker run --rm -v "{docker_path}:/workspace" {DOCKER_IMAGE_NAME} '
327
349
 
328
350
  if network != "default":
329
351
  cmd = cmd + f"--network {network} "
@@ -514,13 +536,14 @@ def upload_zip(file_upload_url: str) -> None:
514
536
  def zip(
515
537
  directory: str,
516
538
  docker_network: str,
539
+ package_type: str,
517
540
  ):
518
541
  # Create a zip file excluding .DS_Store files
519
542
  import zipfile
520
543
 
521
544
  # prepare payload only if requirements.txt is non-empty
522
545
  if has_nonempty_requirements_file(directory):
523
- prepare_dependency_archive(directory, docker_network)
546
+ prepare_dependency_archive(directory, docker_network, package_type)
524
547
  else:
525
548
  logger.info(
526
549
  f"Skipping dependency archive: requirements.txt is missing or empty "
@@ -559,7 +582,7 @@ def deploy_full(
559
582
 
560
583
  # create deployment and upload payload
561
584
  deployment = create_deployment(access_token, metadata)
562
- zip(directory, docker_network)
585
+ zip(directory, docker_network, metadata.codeType)
563
586
  upload_zip(deployment.fileUploadUrl)
564
587
  wait_for_deployment(access_token, metadata, callback)
565
588
 
@@ -0,0 +1,8 @@
1
+ #!/bin/bash
2
+ set -e
3
+
4
+ # Description: build native dependencies for function (unpacked pip install to py-files)
5
+
6
+ python3.11 -m venv --copies .venv
7
+ source .venv/bin/activate
8
+ pip install --target ./py-files -r requirements.txt
@@ -1,9 +0,0 @@
1
- #!/bin/bash
2
- set -e
3
-
4
- # Description: build native dependencies
5
-
6
- python3.11 -m venv --copies .venv
7
- source .venv/bin/activate
8
- pip install -r requirements.txt
9
- venv-pack -o native_dependencies.tar.gz -f