def _remove_feature_parameters(self): # Remove feature parameters from unlocked packages only if self.options.get("package_type") != "Unlocked": return # Copy existing files to new zipfile package_xml = None zip_dest = zipfile.ZipFile(io.BytesIO(), "w", zipfile.ZIP_DEFLATED) for name in self.zf.namelist(): if name == "package.xml": package_xml = self.zf.open(name) elif name.startswith("featureParameters/"): # skip feature parameters self.logger.info(f"Skipping {name} in unlocked package") continue else: content = self.zf.read(name) zip_dest.writestr(name, content) # Remove from package.xml if package_xml is not None: Package = metadata_tree.parse(package_xml) for mdtype in ( "FeatureParameterInteger", "FeatureParameterString", "FeatureParameterBoolean", ): section = Package.find("types", name=mdtype) if section is not None: Package.remove(section) package_xml = Package.tostring(xml_declaration=True) zip_dest.writestr("package.xml", package_xml) self.zf.close() self.zf = zip_dest
def test_pathlib_support(self): from cumulusci.tasks.metadata import tests path = (Path(tests.__file__).parent / "package_metadata/namespaced_report_folder/package.xml") tree = parse(path) assert tree
def _process_static_resources(self, zip_src): relpath = self.options.get("static_resource_path") if not relpath or not os.path.exists(relpath): return zip_src path = os.path.realpath(relpath) # We need to build a new zip file so that we can replace package.xml zip_dest = zipfile.ZipFile(io.BytesIO(), "w", zipfile.ZIP_DEFLATED) for name in zip_src.namelist(): if name == "package.xml": package_xml = zip_src.open(name) else: content = zip_src.read(name) zip_dest.writestr(name, content) # Build static resource bundles and add to package zip with temporary_dir(): os.mkdir("staticresources") bundles = [] for name in os.listdir(path): bundle_relpath = os.path.join(relpath, name) bundle_path = os.path.join(path, name) if not os.path.isdir(bundle_path): continue self.logger.info( "Zipping {} to add to staticresources".format(bundle_relpath) ) # Add resource-meta.xml file meta_name = "{}.resource-meta.xml".format(name) meta_path = os.path.join(path, meta_name) with open(meta_path, "rb") as f: zip_dest.writestr("staticresources/{}".format(meta_name), f.read()) # Add bundle zip_path = os.path.join("staticresources", "{}.resource".format(name)) with open(zip_path, "wb") as bundle_fp: bundle_zip = zipfile.ZipFile(bundle_fp, "w", zipfile.ZIP_DEFLATED) with cd(bundle_path): for resource_file in self._get_static_resource_files(): bundle_zip.write(resource_file) bundle_zip.close() zip_dest.write(zip_path) bundles.append(name) # Update package.xml Package = metadata_tree.parse(package_xml) sections = Package.findall("types", name="StaticResource") section = sections[0] if sections else None if not section: section = Package.append("types") section.append("name", text="StaticResource") for name in bundles: section.insert_before(section.find("name"), tag="members", text=name) package_xml = Package.tostring(xml_declaration=True) zip_dest.writestr("package.xml", package_xml) return zip_dest
def test_whitespace(self): from cumulusci.tasks.metadata import tests path = (Path(tests.__file__).parent / "package_metadata/namespaced_report_folder/package.xml") with open(path) as f: raw = f.read() # get rid of whitespace to see if we can replace it faithfully raw_flattened = raw.replace(" ", "").replace("\n", "") Package = parse(BytesIO(raw_flattened.encode("utf-8"))) x = Package.tostring(xml_declaration=True).strip() assert x == raw
def _transform(self): # call _transform_entity once per retrieved entity # if the entity is an XML file, provide a parsed version # and write the returned metadata into the deploy directory parser = PackageXmlGenerator( None, self.api_version) # We'll use it for its metadata_map entity_configurations = [ entry for entry in parser.metadata_map if any([ subentry["type"] == self.entity for subentry in parser.metadata_map[entry] ]) ] if not entity_configurations: raise CumulusCIException( f"Unable to locate configuration for entity {self.entity}") configuration = parser.metadata_map[entity_configurations[0]][0] if configuration["class"] not in [ "MetadataFilenameParser", "CustomObjectParser", ]: raise CumulusCIException( f"MetadataSingleEntityTransformTask only supports manipulating complete, file-based XML entities (not {self.entity})" ) extension = configuration["extension"] directory = entity_configurations[0] source_metadata_dir = self.retrieve_dir / directory if "*" in self.api_names: # Walk the retrieved directory to get the actual suite # of API names retrieved and rebuild our api_names list. self.api_names.remove("*") self.api_names = self.api_names.union( metadata_file.stem for metadata_file in source_metadata_dir.iterdir() if metadata_file.suffix == f".{extension}") removed_api_names = set() for api_name in self.api_names: # Page Layout names can contain spaces, but parentheses and other # characters like ' and < are quoted. # We quote user-specified API names so we can locate the corresponding # metadata files, but present them un-quoted in messages to the user. unquoted_api_name = unquote(api_name) path = source_metadata_dir / f"{api_name}.{extension}" if not path.exists(): raise CumulusCIException(f"Cannot find metadata file {path}") try: tree = metadata_tree.parse(str(path)) except SyntaxError as err: err.filename = path raise err transformed_xml = self._transform_entity(tree, unquoted_api_name) if transformed_xml: parent_dir = self.deploy_dir / directory if not parent_dir.exists(): parent_dir.mkdir() destination_path = parent_dir / f"{api_name}.{extension}" with destination_path.open(mode="w", encoding="utf-8") as f: f.write(transformed_xml.tostring(xml_declaration=True)) else: # Make sure to remove from our package.xml removed_api_names.add(api_name) self.api_names = self.api_names - removed_api_names