diff --git a/cumulusci/salesforce_api/package_zip.py b/cumulusci/salesforce_api/package_zip.py
index bf12bf1f84..c84aff4b4e 100644
--- a/cumulusci/salesforce_api/package_zip.py
+++ b/cumulusci/salesforce_api/package_zip.py
@@ -117,7 +117,8 @@ def from_zipfile(cls, zf, *, options=None, logger=None):
def _convert_sfdx_format(self, path, name):
orig_path = path
with contextlib.ExitStack() as stack:
- if not pathlib.Path(path, "package.xml").exists():
+ # convert sfdx -> mdapi format if path exists but does not have package.xml
+ if len(os.listdir(path)) and not pathlib.Path(path, "package.xml").exists():
self.logger.info("Converting from sfdx to mdapi format")
path = stack.enter_context(temporary_dir(chdir=False))
args = ["-r", str(orig_path), "-d", path]
diff --git a/cumulusci/salesforce_api/tests/test_package_zip.py b/cumulusci/salesforce_api/tests/test_package_zip.py
index ec799774fa..91c39cb9bf 100644
--- a/cumulusci/salesforce_api/tests/test_package_zip.py
+++ b/cumulusci/salesforce_api/tests/test_package_zip.py
@@ -317,12 +317,21 @@ def test_include_file(self):
def test_convert_sfdx(self):
with temporary_dir() as path:
+ touch("README.md") # make sure there's something in the directory
with mock.patch("cumulusci.salesforce_api.package_zip.sfdx") as sfdx:
builder = MetadataPackageZipBuilder()
with builder._convert_sfdx_format(path, "Test Package"):
pass
sfdx.assert_called_once()
+ def test_convert_sfdx__skipped_if_directory_empty(self):
+ with temporary_dir() as path:
+ with mock.patch("cumulusci.salesforce_api.package_zip.sfdx") as sfdx:
+ builder = MetadataPackageZipBuilder()
+ with builder._convert_sfdx_format(path, "Test Package"):
+ pass
+ sfdx.assert_not_called()
+
def test_removes_feature_parameters_from_unlocked_package(self):
with temporary_dir() as path:
pathlib.Path(path, "package.xml").write_text(
diff --git a/cumulusci/tasks/salesforce/Deploy.py b/cumulusci/tasks/salesforce/Deploy.py
index 7af67eed5e..7f9e852705 100644
--- a/cumulusci/tasks/salesforce/Deploy.py
+++ b/cumulusci/tasks/salesforce/Deploy.py
@@ -79,7 +79,11 @@ def _get_api(self, path=None):
path = self.options.get("path")
package_zip = self._get_package_zip(path)
- self.logger.info("Payload size: {} bytes".format(len(package_zip)))
+ if package_zip is not None:
+ self.logger.info("Payload size: {} bytes".format(len(package_zip)))
+ else:
+ self.logger.warning("Deployment package is empty; skipping deployment.")
+ return
return self.api_class(
self,
@@ -113,9 +117,12 @@ def _get_package_zip(self, path):
"namespaced_org": self._is_namespaced_org(namespace),
}
- return MetadataPackageZipBuilder(
+ package_zip = MetadataPackageZipBuilder(
path=path, options=options, logger=self.logger
- ).as_base64()
+ )
+ if not package_zip.zf.namelist():
+ return
+ return package_zip.as_base64()
def freeze(self, step):
steps = super(Deploy, self).freeze(step)
diff --git a/cumulusci/tasks/salesforce/tests/test_Deploy.py b/cumulusci/tasks/salesforce/tests/test_Deploy.py
index 72059b8c92..6a8b7e2043 100644
--- a/cumulusci/tasks/salesforce/tests/test_Deploy.py
+++ b/cumulusci/tasks/salesforce/tests/test_Deploy.py
@@ -114,6 +114,19 @@ def test_get_api__static_resources(self):
self.assertIn("StaticResource", package_xml)
self.assertIn("TestBundle", package_xml)
+ def test_get_api__empty_package_zip(self):
+ with temporary_dir() as path:
+ task = create_task(
+ Deploy,
+ {
+ "path": path,
+ "unmanaged": True,
+ },
+ )
+
+ api = task._get_api()
+ assert api is None
+
def test_init_options(self):
with self.assertRaises(TaskOptionsError):
create_task(