def check_package_version(self): """Check the package version in meta.yaml for proper formatting.""" package_version = self.meta.get("package", {}).get("version", "") if package_version == "": return Error(self.recipe_dir, "C2104", "Missing package version in meta.yaml") if isinstance(package_version, str): if (not self.version_pat.match(package_version) or package_version.startswith( ("_", ".")) or package_version.endswith(("_", "."))): return Error( self.recipe_dir, "C2105", u'Found invalid package version "{}" in meta.yaml'.format( package_version), ) seq = get_bad_seq(package_version) if seq: return Error( self.recipe_dir, "C2106", u'Found invalid sequence "{}" in package version'.format( seq), )
def check_source(self): """Check the source field in meta.yaml for proper formatting.""" sources = ensure_list(self.meta.get("source", {})) for source in sources: url = source.get("url") if url is not None: if self.url_pat.match(url): for hash_algorithm in ["md5", "sha1", "sha256"]: hexdigest = source.get(hash_algorithm) if hexdigest is not None and not self.hash_pat[ hash_algorithm].match(hexdigest): return Error( self.recipe_dir, "C2119", u'Found invalid hash "{}" in meta.yaml'.format( hexdigest), ) else: return Error( self.recipe_dir, "C2120", u'Found invalid URL "{}" in meta.yaml'.format(url), ) git_url = source.get("git_url") if git_url and (source.get("git_tag") and source.get("git_branch")): return Error( self.recipe_dir, "C2121", "Found both git_branch and git_tag in meta.yaml source field", )
def check_windows_arch(self): """Check that Windows package .exes and .dlls contain the correct headers.""" if self.win_pkg: arch = self.info["arch"] if arch not in ("x86", "x86_64"): return Error( self.path, "C1144", u'Found unrecognized Windows architecture "{}"'.format( arch), ) for member in self.archive_members: if member.endswith((".exe", ".dll")): with open(os.path.join(self.tmpdir, member), "rb") as file_object: file_header = file_object.read(4096) file_object_type = get_object_type(file_header) if (arch == "x86" and file_object_type != "DLL I386" ) or (arch == "x86_64" and file_object_type != "DLL AMD64"): return Error( self.path, "C1145", u'Found file "{}" with object type "{}" but with arch "{}"' .format(member, file_object_type, arch), )
def check_prefix_file_binary_mode(self): """Check that the has_prefix file binary mode is correct.""" if self.prefix_file_contents is not None: placeholder, mode, _ = self.prefix_file_contents if mode == "binary": if self.name == "python": return Error( self.path, "C1131", "Binary placeholder found in info/has_prefix not allowed when building Python", ) elif self.win_pkg: return Error( self.path, "C1132", "Binary placeholder found in info/has_prefix not allowed in Windows package", ) elif len(placeholder) != 255: return Error( self.path, "C1133", u'Binary placeholder "{}" found in info/has_prefix does not have a length of 255 bytes' .format(placeholder), )
def check_files_file_for_validity(self): """Check that the files listed in info/files exist in the tar archive and vice versa.""" members = set([ member for member in self.archive_members if not os.path.isdir(os.path.join(self.tmpdir, member)) and not member.startswith("info") ]) filenames = set([ os.path.normpath(path.strip()) for path in self.files_file.decode("utf-8").splitlines() if not path.strip().startswith("info") ]) sorted_union_filenames = sorted(members.union(filenames)) for filename in sorted_union_filenames: if filename not in members: return Error( self.path, "C1122", (u"Found filename in info/files missing from tar " "archive: {}").format(filename), ) elif filename not in filenames: return Error( self.path, "C1123", u"Found filename in tar archive missing from info/files: {}" .format(filename), )
def check_for_valid_files(self): """Check that the files listed in meta.yaml exist.""" test_files = self.meta.get("test", {}).get("files", []) test_source_files = self.meta.get("test", {}).get("source_files", []) sources = ensure_list(self.meta.get("source", {})) source_patches = [] for source in sources: source_patches.extend(source.get("patches", [])) for filename in test_files + test_source_files + source_patches: filepath = os.path.join(self.recipe_dir, filename) if filename.startswith(".."): return Error( self.recipe_dir, "C2123", u'Found file "{}" listed outside recipe directory'.format( filename), ) if not os.path.exists(filepath): return Error( self.recipe_dir, "C2124", u'Found file "{}" in meta.yaml that doesn\'t exist'.format( filename), )
def check_build_string(self): """Check the build string in info/index.json.""" build_string = self.info.get('build') if not self.version_pat.match(build_string): return Error(self.path, 'C1110', 'Found invalid build string "{}" in info/index.json' .format(build_string)) if build_string != self.build: return Error(self.path, 'C1111', 'Found build number in info/index.json "{}" does not match build number "{}" in filename' .format(build_string, self.build))
def check_package_version(self): """Check the package version located in info/index.json.""" package_version = str(self.info.get("version")) if package_version == "None": return Error( self.path, "C1104", "Missing package version in info/index.json" ) if package_version.startswith(("_", ".")) or package_version.endswith( ("_", ".") ): return Error( self.path, "C1107", "Package version in info/index.json cannot start or end with '_' or '.'", ) if not self.version_pat.match(package_version) or get_bad_seq(package_version): return Error( self.path, "C1105", "Found invalid version number in info/index.json" ) if package_version != self.version: return Error( self.path, "C1106", u'Found package version in info/index.json "{}" does not match filename version "{}"'.format( package_version, self.version ), )
def check_source(self): """Check the source field in meta.yaml for proper formatting.""" sources = ensure_list(self.meta.get('source', {})) for source in sources: url = source.get('url') if url is not None: if self.url_pat.match(url): for hash_algorithm in ['md5', 'sha1', 'sha256']: hexdigest = source.get(hash_algorithm) if hexdigest is not None and not self.hash_pat[ hash_algorithm].match(hexdigest): return Error( self.recipe_dir, 'C2119', u'Found invalid hash "{}" in meta.yaml'.format( hexdigest)) else: return Error( self.recipe_dir, 'C2120', u'Found invalid URL "{}" in meta.yaml'.format(url)) git_url = source.get('git_url') if git_url and (source.get('git_tag') and source.get('git_branch')): return Error( self.recipe_dir, 'C2121', 'Found both git_branch and git_tag in meta.yaml source field' )
def check_fields(self): """Check that the fields listed in meta.yaml are valid.""" for section in self.meta: if section not in FIELDS and section != 'extra': return Error(self.recipe_dir, 'C2109', u'Found invalid section "{}"'.format(section)) if section != 'extra': subfield = self.meta.get(section) if hasattr(subfield, 'keys'): for key in subfield: if key not in FIELDS[section]: return Error( self.recipe_dir, 'C2110', u'Found invalid field "{}" in section "{}"'. format(key, section)) else: # list of dicts. Used in source and outputs. for entry in subfield: for key in entry: if key not in FIELDS[section]: return Error( self.recipe_dir, 'C2110', u'Found invalid field "{}" in section "{}"' .format(key, section))
def check_package_hashes_and_size(self): """Check the sha256 checksum and filesize of each file in the package.""" for member in self.archive_members: file_path = os.path.join(self.tmpdir, member) if member in self.paths_json_path: if os.path.isfile(file_path): path = self.paths_json_path[member] size = os.stat(file_path).st_size if size != path["size_in_bytes"]: return Error( self.path, "C1147", 'Found file "{}" with filesize different than listed in paths.json'.format( member ), ) with open(file_path, "rb") as file_object: sha256_digest = sha256_checksum(file_object) if sha256_digest != path["sha256"]: return Error( self.path, "C1146", 'Found file "{}" with sha256 hash different than listed in paths.json'.format( member ), )
def check_index_dependencies_specs(self): """Check that the dependencies in info/index.json are properly formatted.""" dependencies = ensure_list(self.info.get('depends')) if dependencies != [None]: for dependency in dependencies: dependency_parts = dependency.split() if len(dependency_parts) == 0: return Error(self.path, 'C1113', 'Found empty dependencies in info/index.json') elif len(dependency_parts) == 2 and not self.ver_spec_pat.match(dependency_parts[1]) or len(dependency_parts) > 3: return Error(self.path, 'C1114', 'Found invalid dependency "{}" in info/index.json' .format(dependency))
def check_menu_json_name(self): """Check that the Menu/package.json filename is identical to the package name.""" menu_json_files = [filepath for filepath in self.paths if filepath.startswith('Menu/') and filepath.endswith('.json')] if len(menu_json_files) == 1: filename = menu_json_files[0] if filename != '{}.json' .format(self.name): return Error(self.path, 'C1142', u'Found invalid Menu json file "{}"' .format(filename)) elif len(menu_json_files) > 1: return Error(self.path, 'C1143', 'Found more than one Menu json file')
def check_package_version(self): """Check the package version located in info/index.json.""" package_version = str(self.info.get('version')) if not package_version: return Error(self.path, 'C1104', 'Missing package version in info/index.json') if not self.version_pat.match(package_version) or get_bad_seq(package_version): return Error(self.path, 'C1105', 'Found invalid version number in info/index.json') if package_version != self.version: return Error(self.path, 'C1106', u'Found package version in info/index.json "{}" does not match filename version "{}"' .format(package_version, self.version)) if package_version.startswith(('_', '.')) or package_version.endswith(('_', '.')): return Error(self.path, 'C1107', "Package version in info/index.json cannot start or end with '_' or '.'")
def check_build_number(self): """Check the build number located in info/index.json.""" build_number = self.info.get('build_number') if build_number is not None: try: build_number = int(build_number) if build_number < 0: return Error(self.path, 'C1109', 'Build number in info/index.json cannot be a negative integer') except ValueError: return Error(self.path, 'C1108', 'Build number in info/index.json must be an integer')
def check_package_name(self): """Check the package name located in info/index.json.""" package_name = self.info.get('name') if package_name is None: return Error(self.path, 'C1101', 'Missing package name in info/index.json') if package_name != self.name: return Error(self.path, 'C1102', u'Found package name in info/index.json "{}" does not match filename "{}"' .format(package_name, self.name)) if not self.name_pat.match(package_name) or package_name.endswith(('.', '-', '_')): return Error(self.path, 'C1103', 'Found invalid package name in info/index.json')
def check_build_number(self): """Check the build number in meta.yaml for proper formatting.""" build_number = self.meta.get('build', {}).get('number') if build_number is not None: try: build_number = int(build_number) if build_number < 0: return Error(self.recipe_dir, 'C2108', 'Build number in info/index.json cannot be a negative integer') except ValueError: return Error(self.recipe_dir, 'C2107', 'Build number in info/index.json must be an integer')
def check_package_name(self): """Check the package name in meta.yaml for proper formatting.""" package_name = self.meta.get('package', {}).get('name', '') if package_name == '': return Error(self.recipe_dir, 'C2101', 'Missing package name in meta.yaml') if not self.name_pat.match(package_name) or package_name.endswith(('.', '-', '_')): return Error(self.recipe_dir, 'C2102', u'Found invalid package name "{}" in meta.yaml' .format(package_name)) seq = get_bad_seq(package_name) if seq: return Error(self.recipe_dir, 'C2103', u'Found invalid sequence "{}" in package name' .format(seq))
def check_files_file_for_validity(self): """Check that the files listed in info/files exist in the tar archive and vice versa.""" members = [member.path for member in self.archive.getmembers() if not member.isdir() and not member.path.startswith('info')] filenames = [path.strip() for path in self.files_file.decode('utf-8').splitlines() if not path.strip().startswith('info')] for filename in sorted(set(members).union(set(filenames))): if filename not in members: return Error(self.path, 'C1122', (u'Found filename in info/files missing from tar ' 'archive: {}').format(filename)) elif filename not in filenames: return Error(self.path, 'C1123', u'Found filename in tar archive missing from info/files: {}' .format(filename))
def check_package_version(self): """Check the package version in meta.yaml for proper formatting.""" package_version = self.meta.get('package', {}).get('version', '') if package_version == '': return Error(self.recipe_dir, 'C2104', 'Missing package version in meta.yaml') if isinstance(package_version, str): if not self.version_pat.match(package_version) or package_version.startswith(('_', '.')) or package_version.endswith(('_', '.')): return Error(self.recipe_dir, 'C2105', u'Found invalid package version "{}" in meta.yaml' .format(package_version)) seq = get_bad_seq(package_version) if seq: return Error(self.recipe_dir, 'C2106', u'Found invalid sequence "{}" in package version' .format(seq))
def check_package_hashes_and_size(self): """Check the sha256 checksum and filesize of each file in the package.""" paths_json = json.loads(self.paths_file.decode('utf-8')) for member in self.archive.getmembers(): if member.isfile(): file_object = self.archive.extractfile(member.name).read() sha256_digest = hashlib.sha256(file_object).hexdigest() for path in paths_json['paths']: if member.name == path['_path']: if sha256_digest != path['sha256']: return Error(self.path, 'C1146', 'Found file "{}" with sha256 hash different than listed in paths.json' .format(member.name)) elif member.size != path['size_in_bytes']: return Error(self.path, 'C1147', 'Found file "{}" with filesize different than listed in paths.json' .format(member.name))
def check_windows_arch(self): """Check that Windows package .exes and .dlls contain the correct headers.""" if self.win_pkg: arch = self.info['arch'] if arch not in ('x86', 'x86_64'): return Error(self.path, 'C1144', u'Found unrecognized Windows architecture "{}"' .format(arch)) for member in self.archive.getmembers(): if member.path.endswith(('.exe', '.dll')): file_header = self.archive.extractfile(member.path).read(4096) file_object_type = get_object_type(file_header) if ((arch == 'x86' and file_object_type != 'DLL I386') or (arch == 'x86_64' and file_object_type != 'DLL AMD64')): return Error(self.path, 'C1145', u'Found file "{}" with object type "{}" but with arch "{}"' .format(member.name, file_object_type, arch))
def check_about(self): """Check the about field in meta.yaml for proper formatting.""" summary = self.meta.get('about', {}).get('summary') if summary is not None and len(summary) > 80: return Error(self.recipe_dir, 'C2117', 'Found summary with length greater than 80 characters') home = self.meta.get('about', {}).get('home') dev_url = self.meta.get('about', {}).get('dev_url') doc_url = self.meta.get('about', {}).get('doc_url') license_url = self.meta.get('about', {}).get('license_url') for url in [home, dev_url, doc_url, license_url]: if url is not None and not self.url_pat.match(url): return Error(self.recipe_dir, 'C2118', u'Found invalid URL "{}" in meta.yaml' .format(url))
def check_license_family(self): """Check that the license family listed in meta.yaml is valid.""" license_family = (self.meta.get('about', {}).get('license_family', self.meta.get('about', {}).get('license'))) if license_family is not None and license_family not in LICENSE_FAMILIES: return Error(self.recipe_dir, 'C2122', u'Found invalid license family "{}"' .format(license_family))
def check_for_pyo_file(self): """Check the tar archive for .pyo files""" for filepath in self.paths: if filepath.endswith('.pyo') and self.name != 'python': return Error( self.path, 'C1138', u'Found pyo file "{}" in archive'.format(filepath))
def check_pyc_files(self): """Check that a .pyc file exists for every .py file in a Python 2 package.""" if 'py3' not in self.build: for filepath in self.paths: if '/site-packages/' in filepath: if filepath.endswith('.py') and (filepath + 'c') not in self.paths: return Error(self.path, 'C1141', u'Found python file "{}" without a corresponding pyc file' .format(filepath))
def check_for_bat_and_exe(self): """Check that both .bat and .exe files don't exist in the same package.""" bat_files = [filepath for filepath in self.paths if filepath.endswith('.bat')] exe_files = [filepath for filepath in self.paths if filepath.endswith('.exe')] if len(bat_files) > 0 and len(exe_files) > 0: return Error(self.path, 'C1127', 'Found both .bat and .exe files in executable directory')
def check_for_2to3_pickle(self): """Check the tar archive for .pickle files.""" for filepath in self.paths: if 'lib2to3' in filepath and filepath.endswith('.pickle'): return Error( self.path, 'C1140', u'Found lib2to3 .pickle file "{}"'.format(filepath))
def check_for_unallowed_files(self): """Check the tar archive for unallowed directories.""" unallowed_directories = {'conda-meta', 'conda-bld', 'pkgs', 'pkgs32', 'envs'} for filepath in self.paths: if filepath in unallowed_directories or filepath.endswith(('.DS_Store', '~')): return Error(self.path, 'C1125', u'Found unallowed file in tar archive: {}' .format(filepath))
def check_for_pth_file(self): """Check the tar archive for .pth files.""" for filepath in self.paths: if filepath.endswith('.pth'): return Error( self.path, 'C1137', u'Found namespace file "{}" in archive'.format(filepath))