| # -*- coding:utf-8 -*- |
| |
| |
| import io |
| import logging |
| import re |
| import sys |
| import textwrap |
| |
| # import our initialized portage instance |
| from repoman._portage import portage |
| |
| from portage import os |
| from portage import _encodings |
| from portage import _unicode_encode |
| |
| from repoman.errors import err |
| from repoman.profile import ProfileDesc, valid_profile_types |
| |
| GPG_KEY_ID_REGEX = r'(0x)?([0-9a-fA-F]{8}){1,5}!?' |
| bad = portage.output.create_color_func("BAD") |
| |
| |
| class RepoSettings(object): |
| '''Holds out repo specific settings''' |
| |
| def __init__( |
| self, config_root, portdir, portdir_overlay, |
| repoman_settings=None, vcs_settings=None, options=None, |
| qawarnings=None): |
| self.repoman_settings = repoman_settings |
| self.vcs_settings = vcs_settings |
| |
| self.repositories = self.repoman_settings.repositories |
| |
| # Ensure that current repository is in the list of enabled repositories. |
| self.repodir = os.path.realpath(portdir_overlay) |
| try: |
| self.repositories.get_repo_for_location(self.repodir) |
| except KeyError: |
| self._add_repo(config_root, portdir_overlay) |
| |
| self.root = self.repoman_settings['EROOT'] |
| self.trees = { |
| self.root: {'porttree': portage.portagetree(settings=self.repoman_settings)} |
| } |
| self.portdb = self.trees[self.root]['porttree'].dbapi |
| |
| # Constrain dependency resolution to the master(s) |
| # that are specified in layout.conf. |
| self.repo_config = self.repositories.get_repo_for_location(self.repodir) |
| self.portdb.porttrees = list(self.repo_config.eclass_db.porttrees) |
| self.portdir = self.portdb.porttrees[0] |
| self.commit_env = os.environ.copy() |
| # list() is for iteration on a copy. |
| for repo in list(self.repositories): |
| # all paths are canonical |
| if repo.location not in self.repo_config.eclass_db.porttrees: |
| del self.repositories[repo.name] |
| |
| if self.repo_config.allow_provide_virtual: |
| qawarnings.add("virtual.oldstyle") |
| |
| if self.repo_config.sign_commit and options.mode in ("commit", "fix", "manifest"): |
| if vcs_settings.vcs: |
| func = getattr(self, '_vcs_gpg_%s' % vcs_settings.vcs) |
| func() |
| else: |
| logging.warning("No VCS type detected, unable to sign the commit") |
| |
| # In order to disable manifest signatures, repos may set |
| # "sign-manifests = false" in metadata/layout.conf. This |
| # can be used to prevent merge conflicts like those that |
| # thin-manifests is designed to prevent. |
| self.sign_manifests = "sign" in self.repoman_settings.features and \ |
| self.repo_config.sign_manifest |
| |
| if self.repo_config.sign_manifest and self.repo_config.name == "gentoo" and \ |
| options.mode in ("commit",) and not self.sign_manifests: |
| msg = ( |
| "The '%s' repository has manifest signatures enabled, " |
| "but FEATURES=sign is currently disabled. In order to avoid this " |
| "warning, enable FEATURES=sign in make.conf. Alternatively, " |
| "repositories can disable manifest signatures by setting " |
| "'sign-manifests = false' in metadata/layout.conf.") % ( |
| self.repo_config.name,) |
| for line in textwrap.wrap(msg, 60): |
| logging.warn(line) |
| |
| is_commit = options.mode in ("commit",) |
| valid_gpg_key = self.repoman_settings.get("PORTAGE_GPG_KEY") and re.match( |
| r'^%s$' % GPG_KEY_ID_REGEX, self.repoman_settings["PORTAGE_GPG_KEY"]) |
| |
| if self.sign_manifests and is_commit and not valid_gpg_key: |
| logging.error( |
| "PORTAGE_GPG_KEY value is invalid: %s" % |
| self.repoman_settings["PORTAGE_GPG_KEY"]) |
| sys.exit(1) |
| |
| manifest_hashes = self.repo_config.manifest_hashes |
| if manifest_hashes is None: |
| manifest_hashes = portage.const.MANIFEST2_HASH_DEFAULTS |
| |
| if options.mode in ("commit", "fix", "manifest"): |
| if portage.const.MANIFEST2_REQUIRED_HASH not in manifest_hashes: |
| msg = ( |
| "The 'manifest-hashes' setting in the '%s' repository's " |
| "metadata/layout.conf does not contain the '%s' hash which " |
| "is required by this portage version. You will have to " |
| "upgrade portage if you want to generate valid manifests for " |
| "this repository.") % ( |
| self.repo_config.name, portage.const.MANIFEST2_REQUIRED_HASH) |
| for line in textwrap.wrap(msg, 70): |
| logging.error(line) |
| sys.exit(1) |
| |
| unsupported_hashes = manifest_hashes.difference( |
| portage.const.MANIFEST2_HASH_FUNCTIONS) |
| if unsupported_hashes: |
| msg = ( |
| "The 'manifest-hashes' setting in the '%s' repository's " |
| "metadata/layout.conf contains one or more hash types '%s' " |
| "which are not supported by this portage version. You will " |
| "have to upgrade portage if you want to generate valid " |
| "manifests for this repository.") % ( |
| self.repo_config.name, " ".join(sorted(unsupported_hashes))) |
| for line in textwrap.wrap(msg, 70): |
| logging.error(line) |
| sys.exit(1) |
| |
| def _add_repo(self, config_root, portdir_overlay): |
| self.repo_conf = portage.repository.config |
| self.repo_name = self.repo_conf.RepoConfig._read_valid_repo_name( |
| portdir_overlay)[0] |
| self.layout_conf_data = self.repo_conf.parse_layout_conf(portdir_overlay)[0] |
| if self.layout_conf_data['repo-name']: |
| self.repo_name = self.layout_conf_data['repo-name'] |
| tmp_conf_file = io.StringIO(textwrap.dedent(""" |
| [%s] |
| location = %s |
| """) % (self.repo_name, portdir_overlay)) |
| # Ensure that the repository corresponding to $PWD overrides a |
| # repository of the same name referenced by the existing PORTDIR |
| # or PORTDIR_OVERLAY settings. |
| self.repoman_settings['PORTDIR_OVERLAY'] = "%s %s" % ( |
| self.repoman_settings.get('PORTDIR_OVERLAY', ''), |
| portage._shell_quote(portdir_overlay)) |
| self.repositories = self.repo_conf.load_repository_config( |
| self.repoman_settings, extra_files=[tmp_conf_file]) |
| # We have to call the config constructor again so that attributes |
| # dependent on config.repositories are initialized correctly. |
| self.repoman_settings = portage.config( |
| config_root=config_root, local_config=False, |
| repositories=self.repositories) |
| |
| ########## |
| # future vcs plugin functions |
| ########## |
| |
| def _vcs_gpg_bzr(self): |
| pass |
| |
| def _vcs_gpg_cvs(self): |
| pass |
| |
| def _vcs_gpg_git(self): |
| # NOTE: It's possible to use --gpg-sign=key_id to specify the key in |
| # the commit arguments. If key_id is unspecified, then it must be |
| # configured by `git config user.signingkey key_id`. |
| self.vcs_settings.vcs_local_opts.append("--gpg-sign") |
| if self.repoman_settings.get("PORTAGE_GPG_DIR"): |
| # Pass GNUPGHOME to git for bug #462362. |
| self.commit_env["GNUPGHOME"] = self.repoman_settings["PORTAGE_GPG_DIR"] |
| |
| # Pass GPG_TTY to git for bug #477728. |
| try: |
| self.commit_env["GPG_TTY"] = os.ttyname(sys.stdin.fileno()) |
| except OSError: |
| pass |
| |
| def _vcs_gpg_hg(self): |
| pass |
| |
| def _vcs_gpg_svn(self): |
| pass |
| |
| |
| def list_checks(kwlist, liclist, uselist, repoman_settings): |
| liclist_deprecated = set() |
| if "DEPRECATED" in repoman_settings._license_manager._license_groups: |
| liclist_deprecated.update( |
| repoman_settings._license_manager.expandLicenseTokens(["@DEPRECATED"])) |
| |
| if not liclist: |
| logging.fatal("Couldn't find licenses?") |
| sys.exit(1) |
| |
| if not kwlist: |
| logging.fatal("Couldn't read KEYWORDS from arch.list") |
| sys.exit(1) |
| |
| if not uselist: |
| logging.fatal("Couldn't find use.desc?") |
| sys.exit(1) |
| return liclist_deprecated |
| |
| |
| def repo_metadata(portdb, repoman_settings): |
| # get lists of valid keywords, licenses, and use |
| kwlist = set() |
| liclist = set() |
| uselist = set() |
| profile_list = [] |
| global_pmasklines = [] |
| |
| for path in portdb.porttrees: |
| try: |
| liclist.update(os.listdir(os.path.join(path, "licenses"))) |
| except OSError: |
| pass |
| kwlist.update( |
| portage.grabfile(os.path.join(path, "profiles", "arch.list"))) |
| |
| use_desc = portage.grabfile(os.path.join(path, 'profiles', 'use.desc')) |
| for x in use_desc: |
| x = x.split() |
| if x: |
| uselist.add(x[0]) |
| |
| expand_desc_dir = os.path.join(path, 'profiles', 'desc') |
| try: |
| expand_list = os.listdir(expand_desc_dir) |
| except OSError: |
| pass |
| else: |
| for fn in expand_list: |
| if not fn[-5:] == '.desc': |
| continue |
| use_prefix = fn[:-5].lower() + '_' |
| for x in portage.grabfile(os.path.join(expand_desc_dir, fn)): |
| x = x.split() |
| if x: |
| uselist.add(use_prefix + x[0]) |
| |
| global_pmasklines.append( |
| portage.util.grabfile_package( |
| os.path.join(path, 'profiles', 'package.mask'), |
| recursive=1, verify_eapi=True)) |
| |
| desc_path = os.path.join(path, 'profiles', 'profiles.desc') |
| try: |
| desc_file = io.open( |
| _unicode_encode( |
| desc_path, encoding=_encodings['fs'], errors='strict'), |
| mode='r', encoding=_encodings['repo.content'], errors='replace') |
| except EnvironmentError: |
| pass |
| else: |
| for i, x in enumerate(desc_file): |
| if x[0] == "#": |
| continue |
| arch = x.split() |
| if len(arch) == 0: |
| continue |
| if len(arch) != 3: |
| err( |
| "wrong format: \"%s\" in %s line %d" % |
| (bad(x.strip()), desc_path, i + 1, )) |
| elif arch[0] not in kwlist: |
| err( |
| "invalid arch: \"%s\" in %s line %d" % |
| (bad(arch[0]), desc_path, i + 1, )) |
| elif arch[2] not in valid_profile_types: |
| err( |
| "invalid profile type: \"%s\" in %s line %d" % |
| (bad(arch[2]), desc_path, i + 1, )) |
| profile_desc = ProfileDesc(arch[0], arch[2], arch[1], path) |
| if not os.path.isdir(profile_desc.abs_path): |
| logging.error( |
| "Invalid %s profile (%s) for arch %s in %s line %d", |
| arch[2], arch[1], arch[0], desc_path, i + 1) |
| continue |
| if os.path.exists( |
| os.path.join(profile_desc.abs_path, 'deprecated')): |
| continue |
| profile_list.append(profile_desc) |
| desc_file.close() |
| |
| global_pmasklines = portage.util.stack_lists(global_pmasklines, incremental=1) |
| global_pmaskdict = {} |
| for x in global_pmasklines: |
| global_pmaskdict.setdefault(x.cp, []).append(x) |
| del global_pmasklines |
| |
| return ( |
| kwlist, liclist, uselist, profile_list, global_pmaskdict, |
| list_checks(kwlist, liclist, uselist, repoman_settings)) |
| |
| |
| def has_global_mask(pkg, global_pmaskdict): |
| mask_atoms = global_pmaskdict.get(pkg.cp) |
| if mask_atoms: |
| pkg_list = [pkg] |
| for x in mask_atoms: |
| if portage.dep.match_from_list(x, pkg_list): |
| return x |
| return None |