2 # Copyright 1999-2006 Gentoo Foundation
3 # Distributed under the terms of the GNU General Public License v2
6 # Next to do: dep syntax checking in mask files
7 # Then, check to make sure deps are satisfiable (to avoid "can't find match for" problems)
8 # that last one is tricky because multiple profiles need to be checked.
25 from itertools import chain, izip
26 from stat import S_ISDIR, ST_CTIME
29 import cStringIO as StringIO
33 if not hasattr(__builtins__, "set"):
34 from sets import Set as set
39 from os import path as osp
40 sys.path.insert(0, osp.join(osp.dirname(osp.dirname(osp.realpath(__file__))), "pym"))
42 portage._disable_legacy_globals()
45 from repoman.checks import run_checks
46 from repoman import utilities
48 from os import path as osp
49 sys.path.insert(0, osp.join(osp.dirname(osp.dirname(osp.realpath(__file__))), 'pym'))
50 from repoman.checks import run_checks
51 from repoman import utilities
53 from _emerge import Package, RootConfig
54 from portage._sets import load_default_config
56 import portage.checksum
59 portage.dep._dep_check_strict = True
60 import portage.exception
61 from portage import cvstree, normalize_path
62 from portage import util
63 from portage.exception import ParseError
64 from portage.manifest import Manifest
65 from portage.process import find_binary, spawn
66 from portage.output import bold, create_color_func, darkgreen, \
67 green, nocolor, red, turquoise, yellow
68 from portage.output import ConsoleStyleFile, StyleWriter
69 from portage.util import writemsg_level
71 util.initialize_logger()
73 # 14 is the length of DESCRIPTION=""
75 allowed_filename_chars="a-zA-Z0-9._-+:"
76 allowed_filename_chars_set = {}
77 map(allowed_filename_chars_set.setdefault, map(chr, range(ord('a'), ord('z')+1)))
78 map(allowed_filename_chars_set.setdefault, map(chr, range(ord('A'), ord('Z')+1)))
79 map(allowed_filename_chars_set.setdefault, map(chr, range(ord('0'), ord('9')+1)))
80 map(allowed_filename_chars_set.setdefault, map(chr, map(ord, [".", "-", "_", "+", ":"])))
81 bad = create_color_func("BAD")
83 # A sane umask is needed for files that portage creates.
85 repoman_settings = portage.config(local_config=False,
86 config_incrementals=portage.const.INCREMENTALS)
87 repoman_settings.lock()
89 if repoman_settings.get("NOCOLOR", "").lower() in ("yes", "true") or \
90 not sys.stdout.isatty():
94 print "repoman: " + txt
100 def exithandler(signum=None, frame=None):
101 logging.fatal("Interrupted; exiting...")
103 os.kill(0, signal.SIGKILL)
105 signal.signal(signal.SIGINT,exithandler)
107 class RepomanHelpFormatter(optparse.IndentedHelpFormatter):
108 """Repoman needs it's own HelpFormatter for now, because the default ones
109 murder the help text."""
111 def __init__(self, indent_increment=1, max_help_position=24, width=150, short_first=1):
112 optparse.HelpFormatter.__init__(self, indent_increment, max_help_position, width, short_first)
114 def format_description(self, description):
117 class RepomanOptionParser(optparse.OptionParser):
118 """Add the on_tail function, ruby has it, optionParser should too
121 def __init__(self, *args, **kwargs):
122 optparse.OptionParser.__init__(self, *args, **kwargs)
125 def on_tail(self, description):
126 self.tail += description
128 def format_help(self, formatter=None):
129 result = optparse.OptionParser.format_help(self, formatter)
134 def ParseArgs(args, qahelp):
135 """This function uses a customized optionParser to parse command line arguments for repoman
137 args - a sequence of command line arguments
138 qahelp - a dict of qa warning to help message
140 (opts, args), just like a call to parser.parse_args()
144 'commit' : 'Run a scan then commit changes',
145 'ci' : 'Run a scan then commit changes',
146 'fix' : 'Fix simple QA issues (stray digests, missing digests)',
147 'full' : 'Scan directory tree and print all issues (not a summary)',
148 'help' : 'Show this screen',
149 'manifest' : 'Generate a Manifest (fetches files if necessary)',
150 'scan' : 'Scan directory tree for QA issues'
153 mode_keys = modes.keys()
156 parser = RepomanOptionParser(formatter=RepomanHelpFormatter(), usage="%prog [options] [mode]")
157 parser.description = green(" ".join((os.path.basename(args[0]), "1.2")))
158 parser.description += "\nCopyright 1999-2007 Gentoo Foundation"
159 parser.description += "\nDistributed under the terms of the GNU General Public License v2"
160 parser.description += "\nmodes: " + " | ".join(map(green,mode_keys))
162 parser.add_option('-m', '--commitmsg', dest='commitmsg',
163 help='specify a commit message on the command line')
165 parser.add_option('-M', '--commitmsgfile', dest='commitmsgfile',
166 help='specify a path to a file that contains a commit message')
168 parser.add_option('-p', '--pretend', dest='pretend', default=False,
169 action='store_true', help='don\'t commit or fix anything; just show what would be done')
171 parser.add_option('-q', '--quiet', dest="quiet", action="count", default=0,
172 help='do not print unnecessary messages')
174 parser.add_option('-f', '--force', dest='force', default=False, action='store_true',
175 help='Commit with QA violations')
177 parser.add_option('-v', '--verbose', dest="verbosity", action='count',
178 help='be very verbose in output', default=0)
180 parser.add_option('-x', '--xmlparse', dest='xml_parse', action='store_true',
181 default=False, help='forces the metadata.xml parse check to be carried out')
183 parser.add_option('-i', '--ignore-arches', dest='ignore_arches', action='store_true',
184 default=False, help='ignore arch-specific failures (where arch != host)')
186 parser.add_option('-I', '--ignore-masked', dest='ignore_masked', action='store_true',
187 default=False, help='ignore masked packages (not allowed with commit mode)')
189 parser.add_option('-d', '--include-dev', dest='include_dev', action='store_true',
190 default=False, help='include dev profiles in dependency checks')
192 parser.add_option('--without-mask', dest='without_mask', action='store_true',
193 default=False, help='behave as if no package.mask entries exist (not allowed with commit mode)')
195 parser.add_option('--mode', type='choice', dest='mode', choices=modes.keys(),
196 help='specify which mode repoman will run in (default=full)')
198 parser.on_tail("\n " + green("Modes".ljust(20) + " Description\n"))
201 parser.on_tail(" %s %s\n" % (k.ljust(20), modes[k]))
203 parser.on_tail("\n " + green("QA keyword".ljust(20) + " Description\n"))
205 sorted_qa = qahelp.keys()
208 parser.on_tail(" %s %s\n" % (k.ljust(20), qahelp[k]))
212 opts, args = parser.parse_args(args)
214 if opts.mode == 'help':
215 parser.print_help(short=False)
226 if opts.mode == 'ci':
227 opts.mode = 'commit' # backwards compat shortcut
229 if opts.mode == 'commit' and not (opts.force or opts.pretend):
230 if opts.ignore_masked:
231 parser.error('Commit mode and --ignore-masked are not compatible')
232 if opts.without_mask:
233 parser.error('Commit mode and --without-mask are not compatible')
235 # Use the verbosity and quiet options to fiddle with the loglevel appropriately
236 for val in range(opts.verbosity):
237 logger = logging.getLogger()
238 logger.setLevel(logger.getEffectiveLevel() - 10)
240 for val in range(opts.quiet):
241 logger = logging.getLogger()
242 logger.setLevel(logger.getEffectiveLevel() + 10)
247 "CVS/Entries.IO_error":"Attempting to commit, and an IO error was encountered access the Entries file",
248 "desktop.invalid":"desktop-file-validate reports errors in a *.desktop file",
249 "ebuild.invalidname":"Ebuild files with a non-parseable or syntactically incorrect name (or using 2.1 versioning extensions)",
250 "ebuild.namenomatch":"Ebuild files that do not have the same name as their parent directory",
251 "changelog.ebuildadded":"An ebuild was added but the ChangeLog was not modified",
252 "changelog.missing":"Missing ChangeLog files",
253 "ebuild.notadded":"Ebuilds that exist but have not been added to cvs",
254 "ebuild.patches":"PATCHES variable should be a bash array to ensure white space safety",
255 "changelog.notadded":"ChangeLogs that exist but have not been added to cvs",
256 "filedir.missing":"Package lacks a files directory",
257 "file.executable":"Ebuilds, digests, metadata.xml, Manifest, and ChangeLog do note need the executable bit",
258 "file.size":"Files in the files directory must be under 20k",
259 "file.name":"File/dir name must be composed of only the following chars: %s " % allowed_filename_chars,
260 "file.UTF8":"File is not UTF8 compliant",
261 "inherit.autotools":"Ebuild inherits autotools but does not call eautomake, eautoconf or eautoreconf",
262 "java.eclassesnotused":"With virtual/jdk in DEPEND you must inherit a java eclass",
263 "KEYWORDS.dropped":"Ebuilds that appear to have dropped KEYWORDS for some arch",
264 "KEYWORDS.missing":"Ebuilds that have a missing or empty KEYWORDS variable",
265 "KEYWORDS.stable":"Ebuilds that have been added directly with stable KEYWORDS",
266 "KEYWORDS.stupid":"Ebuilds that use KEYWORDS=-* instead of package.mask",
267 "LICENSE.missing":"Ebuilds that have a missing or empty LICENSE variable",
268 "DESCRIPTION.missing":"Ebuilds that have a missing or empty DESCRIPTION variable",
269 "DESCRIPTION.toolong":"DESCRIPTION is over %d characters" % max_desc_len,
270 "EAPI.incompatible":"Ebuilds that use features that are only available with a different EAPI",
271 "EAPI.unsupported":"Ebuilds that have an unsupported EAPI version (you must upgrade portage)",
272 "SLOT.missing":"Ebuilds that have a missing or empty SLOT variable",
273 "HOMEPAGE.missing":"Ebuilds that have a missing or empty HOMEPAGE variable",
274 "DEPEND.bad":"User-visible ebuilds with bad DEPEND settings (matched against *visible* ebuilds)",
275 "RDEPEND.bad":"User-visible ebuilds with bad RDEPEND settings (matched against *visible* ebuilds)",
276 "PDEPEND.bad":"User-visible ebuilds with bad PDEPEND settings (matched against *visible* ebuilds)",
277 "DEPEND.badmasked":"Masked ebuilds with bad DEPEND settings (matched against *all* ebuilds)",
278 "RDEPEND.badmasked":"Masked ebuilds with RDEPEND settings (matched against *all* ebuilds)",
279 "PDEPEND.badmasked":"Masked ebuilds with PDEPEND settings (matched against *all* ebuilds)",
280 "DEPEND.badindev":"User-visible ebuilds with bad DEPEND settings (matched against *visible* ebuilds) in developing arch",
281 "RDEPEND.badindev":"User-visible ebuilds with bad RDEPEND settings (matched against *visible* ebuilds) in developing arch",
282 "PDEPEND.badindev":"User-visible ebuilds with bad PDEPEND settings (matched against *visible* ebuilds) in developing arch",
283 "DEPEND.badmaskedindev":"Masked ebuilds with bad DEPEND settings (matched against *all* ebuilds) in developing arch",
284 "RDEPEND.badmaskedindev":"Masked ebuilds with RDEPEND settings (matched against *all* ebuilds) in developing arch",
285 "PDEPEND.badmaskedindev":"Masked ebuilds with PDEPEND settings (matched against *all* ebuilds) in developing arch",
286 "DEPEND.syntax":"Syntax error in DEPEND (usually an extra/missing space/parenthesis)",
287 "RDEPEND.syntax":"Syntax error in RDEPEND (usually an extra/missing space/parenthesis)",
288 "PDEPEND.syntax":"Syntax error in PDEPEND (usually an extra/missing space/parenthesis)",
289 "LICENSE.syntax":"Syntax error in LICENSE (usually an extra/missing space/parenthesis)",
290 "PROVIDE.syntax":"Syntax error in PROVIDE (usually an extra/missing space/parenthesis)",
291 "PROPERTIES.syntax":"Syntax error in PROPERTIES (usually an extra/missing space/parenthesis)",
292 "RESTRICT.syntax":"Syntax error in RESTRICT (usually an extra/missing space/parenthesis)",
293 "SRC_URI.syntax":"Syntax error in SRC_URI (usually an extra/missing space/parenthesis)",
294 "SRC_URI.mirror":"A uri listed in profiles/thirdpartymirrors is found in SRC_URI",
295 "ebuild.syntax":"Error generating cache entry for ebuild; typically caused by ebuild syntax error or digest verification failure",
296 "ebuild.output":"A simple sourcing of the ebuild produces output; this breaks ebuild policy.",
297 "ebuild.nesteddie":"Placing 'die' inside ( ) prints an error, but doesn't stop the ebuild.",
298 "variable.readonly":"Assigning a readonly variable",
299 "LIVEVCS.stable":"This ebuild is a live checkout from a VCS but has stable keywords.",
300 "IUSE.invalid":"This ebuild has a variable in IUSE that is not in the use.desc or use.local.desc file",
301 "IUSE.undefined":"This ebuild does not define IUSE (style guideline says to define IUSE even when empty)",
302 "LICENSE.invalid":"This ebuild is listing a license that doesnt exist in portages license/ dir.",
303 "KEYWORDS.invalid":"This ebuild contains KEYWORDS that are not listed in profiles/arch.list or for which no valid profile was found",
304 "RDEPEND.suspect":"RDEPEND contains a package that usually only belongs in DEPEND.",
305 "RESTRICT.invalid":"This ebuild contains invalid RESTRICT values.",
306 "digestentry.unused":"Some files listed in the Manifest aren't referenced in SRC_URI",
307 "ebuild.nostable":"There are no ebuilds that are marked as stable for your ARCH",
308 "ebuild.allmasked":"All ebuilds are masked for this package (Package level only)",
309 "ebuild.majorsyn":"This ebuild has a major syntax error that may cause the ebuild to fail partially or fully",
310 "ebuild.minorsyn":"This ebuild has a minor syntax error that contravenes gentoo coding style",
311 "ebuild.badheader":"This ebuild has a malformed header",
312 "metadata.missing":"Missing metadata.xml files",
313 "metadata.bad":"Bad metadata.xml files",
314 "virtual.versioned":"PROVIDE contains virtuals with versions",
315 "virtual.exists":"PROVIDE contains existing package names",
316 "virtual.unavailable":"PROVIDE contains a virtual which contains no profile default",
317 "usage.obsolete":"The ebuild makes use of an obsolete construct",
318 "upstream.workaround":"The ebuild works around an upstream bug, an upstream bug should be filed and tracked in bugs.gentoo.org"
321 qacats = qahelp.keys()
326 "changelog.notadded",
327 "digestentry.unused",
333 "DEPEND.badmasked","RDEPEND.badmasked","PDEPEND.badmasked",
334 "DEPEND.badindev","RDEPEND.badindev","PDEPEND.badindev",
335 "DEPEND.badmaskedindev","RDEPEND.badmaskedindev","PDEPEND.badmaskedindev",
336 "DESCRIPTION.toolong",
349 "java.eclassesnotused",
352 "virtual.unavailable",
354 "upstream.workaround",
358 missingvars=["KEYWORDS","LICENSE","DESCRIPTION","HOMEPAGE","SLOT"]
359 allvars = set(x for x in portage.auxdbkeys if not x.startswith("UNUSED_"))
360 allvars.discard("CDEPEND")
361 allvars.update(Package.metadata_keys)
362 allvars = sorted(allvars)
364 for x in missingvars:
367 logging.warn('* missingvars values need to be added to qahelp ("%s")' % x)
371 valid_restrict = frozenset(["binchecks", "bindist",
372 "fetch", "installsources", "mirror",
373 "primaryuri", "strip", "test", "userpriv"])
375 live_eclasses = frozenset([
384 suspect_rdepend = frozenset([
385 "app-arch/cabextract",
386 "app-arch/rpm2targz",
391 "dev-perl/extutils-pkgconfig",
392 "dev-python/setuptools",
397 "dev-util/gtk-doc-am",
400 "dev-util/pkgconfig",
404 "media-gfx/ebdftopcf",
406 "sys-devel/autoconf",
407 "sys-devel/automake",
420 no_exec = frozenset(["Manifest","ChangeLog","metadata.xml"])
422 options, arguments = ParseArgs(sys.argv, qahelp)
424 # Set this to False when an extraordinary issue (generally
425 # something other than a QA issue) makes it impossible to
426 # commit (like if Manifest generation fails).
429 portdir, portdir_overlay, mydir = utilities.FindPortdir(repoman_settings)
434 if os.path.isdir("CVS"):
436 if os.path.isdir(".svn"):
438 elif os.path.isdir(os.path.join(portdir_overlay, ".git")):
441 vcs_local_opts = repoman_settings.get("REPOMAN_VCS_LOCAL_OPTS", "").split()
442 vcs_global_opts = repoman_settings.get("REPOMAN_VCS_GLOBAL_OPTS")
443 if vcs_global_opts is None:
445 vcs_global_opts = "-q"
448 vcs_global_opts = vcs_global_opts.split()
450 if vcs == "cvs" and \
451 "commit" == options.mode and \
452 "RMD160" not in portage.checksum.hashorigin_map:
453 from portage.util import grablines
454 repo_lines = grablines("./CVS/Repository")
456 "gentoo-x86" == repo_lines[0].strip().split(os.path.sep)[0]:
457 msg = "Please install " \
458 "pycrypto or enable python's ssl USE flag in order " \
459 "to enable RMD160 hash support. See bug #198398 for " \
462 from textwrap import wrap
463 for line in wrap(msg, 70):
468 if options.mode == 'commit' and not options.pretend and not vcs:
469 logging.info("Not in a version controlled repository; enabling pretend mode.")
470 options.pretend = True
472 os.environ["PORTDIR"] = portdir
473 if portdir_overlay != portdir:
474 os.environ["PORTDIR_OVERLAY"] = portdir_overlay
476 os.environ["PORTDIR_OVERLAY"] = ""
478 logging.info('Setting paths:')
479 logging.info('PORTDIR = "' + os.environ['PORTDIR'] + '"')
480 logging.info('PORTDIR_OVERLAY = "' + os.environ['PORTDIR_OVERLAY']+'"')
482 # Now that PORTDIR_OVERLAY is properly overridden, create the portdb.
483 repoman_settings = portage.config(local_config=False,
484 config_incrementals=portage.const.INCREMENTALS)
485 trees = portage.create_trees()
486 trees["/"]["porttree"].settings = repoman_settings
487 portdb = trees["/"]["porttree"].dbapi
488 portdb.mysettings = repoman_settings
489 setconfig = load_default_config(repoman_settings, trees["/"])
490 root_config = RootConfig(repoman_settings, trees["/"], setconfig)
491 # We really only need to cache the metadata that's necessary for visibility
492 # filtering. Anything else can be discarded to reduce memory consumption.
493 portdb._aux_cache_keys.clear()
494 portdb._aux_cache_keys.update(["EAPI", "KEYWORDS", "SLOT"])
495 # dep_zapdeps looks at the vardbapi, but it shouldn't for repoman.
496 del trees["/"]["vartree"]
498 myreporoot = os.path.basename(portdir_overlay)
499 myreporoot += mydir[len(portdir_overlay):]
501 reposplit = myreporoot.split(os.path.sep)
502 repolevel = len(reposplit)
504 # check if it's in $PORTDIR/$CATEGORY/$PN , otherwise bail if commiting.
505 # Reason for this is if they're trying to commit in just $FILESDIR/*, the Manifest needs updating.
506 # this check ensures that repoman knows where it is, and the manifest recommit is at least possible.
507 if options.mode == 'commit' and repolevel not in [1,2,3]:
508 print red("***")+" Commit attempts *must* be from within a vcs co, category, or package directory."
509 print red("***")+" Attempting to commit from a packages files directory will be blocked for instance."
510 print red("***")+" This is intended behaviour, to ensure the manifest is recommited for a package."
512 err("Unable to identify level we're commiting from for %s" % '/'.join(reposplit))
514 startdir = normalize_path(mydir)
516 for x in range(0, repolevel - 1):
517 repodir = os.path.dirname(repodir)
520 err(mycat+" is not an official category. Skipping QA checks in this directory.\nPlease ensure that you add "+catdir+" to "+repodir+"/profiles/categories\nif it is a new category.")
522 # setup a uselist from portage
525 uselist=portage.grabfile(portdir+"/profiles/use.desc")
526 for l in range(0,len(uselist)):
527 uselist[l]=uselist[l].split()[0]
528 for var in repoman_settings["USE_EXPAND"].split():
529 vardescs = portage.grabfile(portdir+"/profiles/desc/"+var.lower()+".desc")
530 for l in range(0, len(vardescs)):
531 uselist.append(var.lower() + "_" + vardescs[l].split()[0])
532 except (IOError, OSError, ParseError), e:
533 logging.exception("Couldn't read USE flags from use.desc")
536 # retrieve a list of current licenses in portage
537 liclist = set(portage.listdir(os.path.join(portdir, "licenses")))
539 logging.fatal("Couldn't find licenses?")
541 if portdir_overlay != portdir:
542 liclist.update(portage.listdir(os.path.join(portdir_overlay, "licenses")))
544 # retrieve list of offical keywords
545 kwlist = set(portage.grabfile(os.path.join(portdir, "profiles", "arch.list")))
547 logging.fatal("Couldn't read KEYWORDS from arch.list")
550 if portdir_overlay != portdir:
551 kwlist.update(portage.grabfile(
552 os.path.join(portdir_overlay, "profiles", "arch.list")))
556 #we are inside a category directory
558 if catdir not in repoman_settings.categories:
560 mydirlist=os.listdir(startdir)
562 if x == "CVS" or x.startswith("."):
564 if os.path.isdir(startdir+"/"+x):
565 scanlist.append(catdir+"/"+x)
566 repo_subdir = catdir + os.sep
568 for x in repoman_settings.categories:
569 if not os.path.isdir(startdir+"/"+x):
571 for y in os.listdir(startdir+"/"+x):
572 if y == "CVS" or y.startswith("."):
574 if os.path.isdir(startdir+"/"+x+"/"+y):
575 scanlist.append(x+"/"+y)
578 catdir = reposplit[-2]
579 if catdir not in repoman_settings.categories:
581 scanlist.append(catdir+"/"+reposplit[-1])
582 repo_subdir = scanlist[-1] + os.sep
583 repo_subdir_len = len(repo_subdir)
586 logging.debug("Found the following packages to scan:\n%s" % '\n'.join(scanlist))
589 valid_profile_types = frozenset(["dev", "exp", "stable"])
590 descfile=portdir+"/profiles/profiles.desc"
591 if os.path.exists(descfile):
592 for i, x in enumerate(open(descfile, 'rb')):
599 err("wrong format: \"" + bad(x.strip()) + "\" in " + \
600 descfile + " line %d" % (i+1, ))
601 elif arch[0] not in kwlist:
602 err("invalid arch: \"" + bad(arch[0]) + "\" in " + \
603 descfile + " line %d" % (i+1, ))
604 elif arch[2] not in valid_profile_types:
605 err("invalid profile type: \"" + bad(arch[2]) + "\" in " + \
606 descfile + " line %d" % (i+1, ))
607 if not os.path.isdir(portdir+"/profiles/"+arch[1]):
608 print "Invalid "+arch[2]+" profile ("+arch[1]+") for arch "+arch[0]
610 if arch[0] in profiles:
611 profiles[arch[0]]+= [[arch[1], arch[2]]]
613 profiles[arch[0]] = [[arch[1], arch[2]]]
615 for x in repoman_settings.archlist():
618 if x not in profiles:
619 print red("\""+x+"\" doesn't have a valid profile listed in profiles.desc.")
620 print red("You need to either \"cvs update\" your profiles dir or follow this")
621 print red("up with the "+x+" team.")
624 print red("profiles.desc does not exist: "+descfile)
625 print red("You need to do \"cvs update\" in profiles dir.")
629 def dev_keywords(profiles):
631 Create a set of KEYWORDS values that exist in 'dev'
632 profiles. These are used
633 to trigger a message notifying the user when they might
634 want to add the --include-dev option.
637 for arch, arch_profiles in profiles.iteritems():
638 for profile_path, profile_type in arch_profiles:
639 arch_set = type_arch_map.get(profile_type)
642 type_arch_map[profile_type] = arch_set
645 dev_keywords = type_arch_map.get('dev', set())
646 dev_keywords.update(['~' + arch for arch in dev_keywords])
647 return frozenset(dev_keywords)
649 dev_keywords = dev_keywords(profiles)
654 # provided by the desktop-file-utils package
655 desktop_file_validate = find_binary("desktop-file-validate")
656 desktop_pattern = re.compile(r'.*\.desktop$')
661 xmllint_capable = False
662 metadata_dtd = os.path.join(repoman_settings["DISTDIR"], 'metadata.dtd')
663 if options.mode == "manifest":
665 elif not find_binary('xmllint'):
666 print red("!!! xmllint not found. Can't check metadata.xml.\n")
667 if options.xml_parse or repolevel==3:
668 print red("!!!")+" sorry, xmllint is needed. failing\n"
671 #hardcoded paths/urls suck. :-/
675 # if it's been over a week since fetching (or the system clock is fscked), grab an updated copy of metadata.dtd
676 # clock is fscked or it's been a week. time to grab a new one.
677 ct = os.stat(metadata_dtd)[ST_CTIME]
678 if abs(time.time() - ct) > (60*60*24*7):
679 # don't trap the exception, we're watching for errno 2 (file not found), anything else is a bug.
684 except (OSError,IOError), e:
686 print red("!!!")+" caught exception '%s' for %s/metadata.dtd, bailing" % (str(e), portage.CACHE_PATH)
691 print green("***")+" the local copy of metadata.dtd needs to be refetched, doing that now"
696 os.unlink(metadata_dtd)
698 if e.errno != errno.ENOENT:
701 val=portage.fetch(['http://www.gentoo.org/dtd/metadata.dtd'],repoman_settings,fetchonly=0, \
704 except SystemExit, e:
705 raise # Need to propogate this
708 print red("!!!")+" attempting to fetch 'http://www.gentoo.org/dtd/metadata.dtd', caught"
709 print red("!!!")+" exception '%s' though." % str(e)
712 print red("!!!")+" fetching new metadata.dtd failed, aborting"
714 #this can be problematic if xmllint changes their output
717 if options.mode == 'commit' and vcs:
718 utilities.detect_vcs_conflicts(options, vcs)
720 if options.mode == "manifest":
722 elif options.pretend:
723 print green("\nRepoMan does a once-over of the neighborhood...")
725 print green("\nRepoMan scours the neighborhood...")
728 modified_changelogs = set()
734 mycvstree = cvstree.getentries("./", recursive=1)
735 mychanged = cvstree.findchanged(mycvstree, recursive=1, basedir="./")
736 mynew = cvstree.findnew(mycvstree, recursive=1, basedir="./")
738 svnstatus = os.popen("svn status").readlines()
739 mychanged = [ "./" + elem.split()[-1:][0] for elem in svnstatus if elem and elem[:1] in "MR" ]
740 mynew = [ "./" + elem.split()[-1:][0] for elem in svnstatus if elem.startswith("A") ]
742 mychanged = os.popen("git ls-files -m --with-tree=HEAD").readlines()
743 mychanged = [ "./" + elem[:-1] for elem in mychanged ]
744 mynew = os.popen("git diff --cached --name-only --diff-filter=A").readlines()
745 strip_levels = repolevel - 1
747 mynew = [elem[repo_subdir_len:] for elem in mynew \
748 if elem[:repo_subdir_len] == repo_subdir]
749 mynew = ["./" + elem[:-1] for elem in mynew]
751 new_ebuilds.update(x for x in mynew if x.endswith(".ebuild"))
752 modified_changelogs.update(x for x in chain(mychanged, mynew) \
753 if os.path.basename(x) == "ChangeLog")
756 have_dev_keywords = False
759 arch_xmatch_caches = {}
760 shared_xmatch_caches = {"cp-list":{}}
762 # Disable the "ebuild.notadded" check when not in commit mode and
763 # running `svn status` in every package dir will be too expensive.
765 check_ebuild_notadded = not \
766 (vcs == "svn" and repolevel < 3 and options.mode != "commit")
768 # Build a regex from thirdpartymirrors for the SRC_URI.mirror check.
769 thirdpartymirrors = portage.flatten(repoman_settings.thirdpartymirrors().values())
772 #ebuilds and digests added to cvs respectively.
773 logging.info("checking package %s" % x)
775 catdir,pkgdir=x.split("/")
776 checkdir=repodir+"/"+x
777 checkdir_relative = ""
779 checkdir_relative = os.path.join(pkgdir, checkdir_relative)
781 checkdir_relative = os.path.join(catdir, checkdir_relative)
782 checkdir_relative = os.path.join(".", checkdir_relative)
784 if options.mode == "manifest" or \
785 options.mode in ('commit', 'fix') and not options.pretend:
786 repoman_settings["O"] = checkdir
787 if not portage.digestgen([], repoman_settings, myportdb=portdb):
788 print "Unable to generate manifest."
790 if options.mode == "manifest":
795 checkdirlist=os.listdir(checkdir)
798 for y in checkdirlist:
799 if y in no_exec and \
800 stat.S_IMODE(os.stat(os.path.join(checkdir, y)).st_mode) & 0111:
801 stats["file.executable"] += 1
802 fails["file.executable"].append(os.path.join(checkdir, y))
803 if y.endswith(".ebuild"):
805 ebuildlist.append(pf)
806 cpv = "%s/%s" % (catdir, pf)
808 myaux = dict(izip(allvars, portdb.aux_get(cpv, allvars)))
810 stats["ebuild.syntax"] += 1
811 fails["ebuild.syntax"].append(os.path.join(x, y))
814 stats["ebuild.output"] += 1
815 fails["ebuild.output"].append(os.path.join(x, y))
817 if not portage.eapi_is_supported(myaux["EAPI"]):
818 stats["EAPI.unsupported"] += 1
819 fails["EAPI.unsupported"].append(os.path.join(x, y))
821 pkgs[pf] = Package(cpv=cpv, metadata=myaux,
822 root_config=root_config)
824 # Sort ebuilds in ascending order for the KEYWORDS.dropped check.
826 for i in xrange(len(ebuildlist)):
827 ebuild_split = portage.pkgsplit(ebuildlist[i])
828 pkgsplits[ebuild_split] = ebuildlist[i]
829 ebuildlist[i] = ebuild_split
830 ebuildlist.sort(portage.pkgcmp)
831 for i in xrange(len(ebuildlist)):
832 ebuildlist[i] = pkgsplits[ebuildlist[i]]
837 if len(pkgs) != len(ebuildlist):
838 # If we can't access all the metadata then it's totally unsafe to
839 # commit since there's no way to generate a correct Manifest.
840 # Do not try to do any more QA checks on this package since missing
841 # metadata leads to false positives for several checks, and false
842 # positives confuse users.
846 for y in checkdirlist:
847 for c in y.strip(os.path.sep):
848 if c not in allowed_filename_chars_set:
849 stats["file.name"] += 1
850 fails["file.name"].append("%s/%s: char '%s'" % (checkdir, y, c))
853 if not (y in ("ChangeLog", "metadata.xml") or y.endswith(".ebuild")):
857 for l in codecs.open(checkdir+"/"+y, "r", "utf8"):
859 except UnicodeDecodeError, ue:
860 stats["file.UTF8"] += 1
861 s = ue.object[:ue.start]
865 s = s[s.rfind("\n") + 1:]
866 fails["file.UTF8"].append("%s/%s: line %i, just after: '%s'" % (checkdir, y, line, s))
868 if vcs == "git" and check_ebuild_notadded:
869 myf = os.popen("git ls-files --others %s" % \
870 (portage._shell_quote(checkdir_relative),))
872 if l[:-1][-7:] == ".ebuild":
873 stats["ebuild.notadded"] += 1
874 fails["ebuild.notadded"].append(
875 os.path.join(x, os.path.basename(l[:-1])))
878 if vcs in ("cvs", "svn") and check_ebuild_notadded:
881 myf=open(checkdir+"/CVS/Entries","r")
883 myf = os.popen("svn status --depth=files --verbose " + checkdir)
884 myl = myf.readlines()
890 splitl=l[1:].split("/")
893 if splitl[0][-7:]==".ebuild":
894 eadded.append(splitl[0][:-7])
899 if l[-7:] == ".ebuild":
900 eadded.append(os.path.basename(l[:-7]))
902 myf = os.popen("svn status " + checkdir)
907 l = l.rstrip().split(' ')[-1]
908 if l[-7:] == ".ebuild":
909 eadded.append(os.path.basename(l[:-7]))
911 if options.mode == 'commit' and vcs == "cvs":
912 stats["CVS/Entries.IO_error"] += 1
913 fails["CVS/Entries.IO_error"].append(checkdir+"/CVS/Entries")
914 if options.mode == 'commit' and vcs == "svn":
915 stats["svn.IO_error"] += 1
916 fails["svn.IO_error"].append(checkdir+"svn info")
919 mf = Manifest(checkdir, repoman_settings["DISTDIR"])
920 mydigests=mf.getTypeDigests("DIST")
922 fetchlist_dict = portage.FetchlistDict(checkdir, repoman_settings, portdb)
924 src_uri_error = False
925 for mykey in fetchlist_dict:
927 myfiles_all.extend(fetchlist_dict[mykey])
928 except portage.exception.InvalidDependString, e:
931 portdb.aux_get(mykey, ["SRC_URI"])
933 # This will be reported as an "ebuild.syntax" error.
936 stats["SRC_URI.syntax"] = stats["SRC_URI.syntax"] + 1
937 fails["SRC_URI.syntax"].append(
938 "%s.ebuild SRC_URI: %s" % (mykey, e))
940 if not src_uri_error:
941 # This test can produce false positives if SRC_URI could not
942 # be parsed for one or more ebuilds. There's no point in
943 # producing a false error here since the root cause will
944 # produce a valid error elsewhere, such as "SRC_URI.syntax"
946 myfiles_all = set(myfiles_all)
947 for entry in mydigests:
948 if entry not in myfiles_all:
949 stats["digestentry.unused"] += 1
950 fails["digestentry.unused"].append(checkdir+"::"+entry)
953 if os.path.exists(checkdir+"/files"):
954 filesdirlist=os.listdir(checkdir+"/files")
956 # recurse through files directory
957 # use filesdirlist as a stack, appending directories as needed so people can't hide > 20k files in a subdirectory.
959 y = filesdirlist.pop(0)
960 relative_path = os.path.join(x, "files", y)
961 full_path = os.path.join(repodir, relative_path)
963 mystat = os.stat(full_path)
966 # don't worry about it. it likely was removed via fix above.
970 if S_ISDIR(mystat.st_mode):
971 # !!! VCS "portability" alert! Need some function isVcsDir() or alike !!!
972 if y == "CVS" or y == ".svn":
974 for z in os.listdir(checkdir+"/files/"+y):
975 if z == "CVS" or z == ".svn":
977 filesdirlist.append(y+"/"+z)
978 # current policy is no files over 20k, this is the check.
979 elif mystat.st_size > 20480:
980 stats["file.size"] += 1
981 fails["file.size"].append("("+ str(mystat.st_size/1024) + "K) "+x+"/files/"+y)
983 for c in os.path.basename(y.rstrip(os.path.sep)):
984 if c not in allowed_filename_chars_set:
985 stats["file.name"] += 1
986 fails["file.name"].append("%s/files/%s: char '%s'" % (checkdir, y, c))
989 if desktop_file_validate and desktop_pattern.match(y):
990 status, cmd_output = commands.getstatusoutput(
991 "'%s' '%s'" % (desktop_file_validate, full_path))
992 if os.WIFEXITED(status) and os.WEXITSTATUS(status) != os.EX_OK:
993 # Note: in the future we may want to grab the
994 # warnings in addition to the errors. We're
995 # just doing errors now since we don't want
996 # to generate too much noise at first.
997 error_re = re.compile(r'.*\s*error:\s*(.*)')
998 for line in cmd_output.splitlines():
999 error_match = error_re.match(line)
1000 if error_match is None:
1002 stats["desktop.invalid"] += 1
1003 fails["desktop.invalid"].append(
1004 relative_path + ': %s' % error_match.group(1))
1008 if "ChangeLog" not in checkdirlist:
1009 stats["changelog.missing"]+=1
1010 fails["changelog.missing"].append(x+"/ChangeLog")
1012 #metadata.xml file check
1015 if "metadata.xml" not in checkdirlist:
1016 stats["metadata.missing"]+=1
1017 fails["metadata.missing"].append(x+"/metadata.xml")
1018 #metadata.xml parse check
1020 metadata_bad = False
1022 # load USE flags from metadata.xml
1024 f = open(os.path.join(checkdir, "metadata.xml"))
1025 utilities.parse_metadata_use(f, muselist)
1027 except (EnvironmentError, ParseError), e:
1029 stats["metadata.bad"] += 1
1030 fails["metadata.bad"].append("%s/metadata.xml: %s" % (x, e))
1033 #Only carry out if in package directory or check forced
1034 if xmllint_capable and not metadata_bad:
1035 # xmlint can produce garbage output even on success, so only dump
1036 # the ouput when it fails.
1037 st, out = commands.getstatusoutput(
1038 "xmllint --nonet --noout --dtdvalid '%s' '%s'" % \
1039 (metadata_dtd, os.path.join(checkdir, "metadata.xml")))
1041 print red("!!!") + " metadata.xml is invalid:"
1042 for z in out.splitlines():
1044 stats["metadata.bad"]+=1
1045 fails["metadata.bad"].append(x+"/metadata.xml")
1049 changelog_path = os.path.join(checkdir_relative, "ChangeLog")
1050 changelog_modified = changelog_path in modified_changelogs
1054 for y in ebuildlist:
1055 relative_path = os.path.join(x, y + ".ebuild")
1056 full_path = os.path.join(repodir, relative_path)
1057 ebuild_path = y + ".ebuild"
1059 ebuild_path = os.path.join(pkgdir, ebuild_path)
1061 ebuild_path = os.path.join(catdir, ebuild_path)
1062 ebuild_path = os.path.join(".", ebuild_path)
1063 if not changelog_modified and ebuild_path in new_ebuilds:
1064 stats['changelog.ebuildadded'] += 1
1065 fails['changelog.ebuildadded'].append(relative_path)
1067 if stat.S_IMODE(os.stat(full_path).st_mode) & 0111:
1068 stats["file.executable"] += 1
1069 fails["file.executable"].append(x+"/"+y+".ebuild")
1070 if vcs in ("cvs", "svn") and check_ebuild_notadded and y not in eadded:
1071 #ebuild not added to vcs
1072 stats["ebuild.notadded"]=stats["ebuild.notadded"]+1
1073 fails["ebuild.notadded"].append(x+"/"+y+".ebuild")
1074 myesplit=portage.pkgsplit(y)
1075 if myesplit is None or myesplit[0] != x.split("/")[-1]:
1076 stats["ebuild.invalidname"]=stats["ebuild.invalidname"]+1
1077 fails["ebuild.invalidname"].append(x+"/"+y+".ebuild")
1079 elif myesplit[0]!=pkgdir:
1080 print pkgdir,myesplit[0]
1081 stats["ebuild.namenomatch"]=stats["ebuild.namenomatch"]+1
1082 fails["ebuild.namenomatch"].append(x+"/"+y+".ebuild")
1086 myaux = pkg.metadata
1087 eapi = myaux["EAPI"]
1088 inherited = pkg.inherited
1089 live_ebuild = live_eclasses.intersection(inherited)
1091 if not src_uri_error:
1092 # Check that URIs don't reference a server from thirdpartymirrors.
1093 for uri in portage.flatten(portage.dep.use_reduce(
1094 portage.dep.paren_reduce(myaux["SRC_URI"]), matchall=True)):
1095 contains_mirror = False
1096 for mirror in thirdpartymirrors:
1097 if uri.startswith(mirror):
1098 contains_mirror = True
1100 if not contains_mirror:
1103 stats["SRC_URI.mirror"] += 1
1104 fails["SRC_URI.mirror"].append(
1105 "%s: '%s' found in thirdpartymirrors" % \
1106 (relative_path, mirror))
1108 # Test for negative logic and bad words in the RESTRICT var.
1109 #for x in myaux[allvars.index("RESTRICT")].split():
1110 # if x.startswith("no"):
1111 # print "Bad RESTRICT value: %s" % x
1113 myaux["PROVIDE"] = portage.dep.use_reduce(
1114 portage.dep.paren_reduce(myaux["PROVIDE"]), matchall=1)
1115 except portage.exception.InvalidDependString, e:
1116 stats["PROVIDE.syntax"] = stats["PROVIDE.syntax"] + 1
1117 fails["PROVIDE.syntax"].append(mykey+".ebuild PROVIDE: "+str(e))
1120 myaux["PROVIDE"] = " ".join(portage.flatten(myaux["PROVIDE"]))
1121 for myprovide in myaux["PROVIDE"].split():
1122 prov_cp = portage.dep_getkey(myprovide)
1123 if prov_cp != myprovide:
1124 stats["virtual.versioned"]+=1
1125 fails["virtual.versioned"].append(x+"/"+y+".ebuild: "+myprovide)
1126 prov_pkg = portage.dep_getkey(
1127 portage.best(portdb.xmatch("match-all", prov_cp)))
1128 if prov_cp == prov_pkg:
1129 stats["virtual.exists"]+=1
1130 fails["virtual.exists"].append(x+"/"+y+".ebuild: "+prov_cp)
1132 for pos, missing_var in enumerate(missingvars):
1133 if not myaux.get(missing_var):
1134 if catdir == "virtual" and \
1135 missing_var in ("HOMEPAGE", "LICENSE"):
1137 if live_ebuild and missing_var == "KEYWORDS":
1139 myqakey=missingvars[pos]+".missing"
1140 stats[myqakey]=stats[myqakey]+1
1141 fails[myqakey].append(x+"/"+y+".ebuild")
1143 # 14 is the length of DESCRIPTION=""
1144 if len(myaux['DESCRIPTION']) > max_desc_len:
1145 stats['DESCRIPTION.toolong'] += 1
1146 fails['DESCRIPTION.toolong'].append(
1147 "%s: DESCRIPTION is %d characters (max %d)" % \
1148 (relative_path, len(myaux['DESCRIPTION']), max_desc_len))
1150 keywords = myaux["KEYWORDS"].split()
1151 stable_keywords = []
1152 for keyword in keywords:
1153 if not keyword.startswith("~") and \
1154 not keyword.startswith("-"):
1155 stable_keywords.append(keyword)
1157 if ebuild_path in new_ebuilds:
1158 stable_keywords.sort()
1159 stats["KEYWORDS.stable"] += 1
1160 fails["KEYWORDS.stable"].append(
1161 x + "/" + y + ".ebuild added with stable keywords: %s" % \
1162 " ".join(stable_keywords))
1164 ebuild_archs = set(kw.lstrip("~") for kw in keywords \
1165 if not kw.startswith("-"))
1167 previous_keywords = slot_keywords.get(myaux["SLOT"])
1168 if previous_keywords is None:
1169 slot_keywords[myaux["SLOT"]] = set()
1170 elif not live_ebuild:
1171 dropped_keywords = previous_keywords.difference(ebuild_archs)
1172 if dropped_keywords:
1173 stats["KEYWORDS.dropped"] += 1
1174 fails["KEYWORDS.dropped"].append(
1175 relative_path + ": %s" % \
1176 " ".join(sorted(dropped_keywords)))
1178 slot_keywords[myaux["SLOT"]].update(ebuild_archs)
1180 # KEYWORDS="-*" is a stupid replacement for package.mask and screws general KEYWORDS semantics
1181 if "-*" in keywords:
1189 stats["KEYWORDS.stupid"] += 1
1190 fails["KEYWORDS.stupid"].append(x+"/"+y+".ebuild")
1193 Ebuilds that inherit a "Live" eclass (darcs,subversion,git,cvs,etc..) should
1194 not be allowed to be marked stable
1197 bad_stable_keywords = []
1198 for keyword in keywords:
1199 if not keyword.startswith("~") and \
1200 not keyword.startswith("-"):
1201 bad_stable_keywords.append(keyword)
1203 if bad_stable_keywords:
1204 stats["LIVEVCS.stable"] += 1
1205 fails["LIVEVCS.stable"].append(
1206 x + "/" + y + ".ebuild with stable keywords:%s " % \
1207 bad_stable_keywords)
1208 del bad_stable_keywords
1210 if options.ignore_arches:
1211 arches = [[repoman_settings["ARCH"], repoman_settings["ARCH"],
1212 repoman_settings["ACCEPT_KEYWORDS"].split()]]
1215 for keyword in myaux["KEYWORDS"].split():
1216 if (keyword[0]=="-"):
1218 elif (keyword[0]=="~"):
1219 arches.append([keyword, keyword[1:], [keyword[1:], keyword]])
1221 arches.append([keyword, keyword, [keyword]])
1224 baddepsyntax = False
1225 badlicsyntax = False
1226 badprovsyntax = False
1227 catpkg = catdir+"/"+y
1228 myiuse = set(repoman_settings.archlist())
1229 for myflag in myaux["IUSE"].split():
1230 if myflag.startswith("+"):
1234 inherited_java_eclass = "java-pkg-2" in inherited or \
1235 "java-pkg-opt-2" in inherited
1236 operator_tokens = set(["||", "(", ")"])
1237 type_list, badsyntax = [], []
1238 for mytype in ("DEPEND", "RDEPEND", "PDEPEND",
1239 "LICENSE", "PROPERTIES", "PROVIDE"):
1240 mydepstr = myaux[mytype]
1242 if mydepstr.find(" ?") != -1:
1243 badsyntax.append("'?' preceded by space")
1246 # Missing closing parenthesis will result in a ValueError
1247 mydeplist = portage.dep.paren_reduce(mydepstr)
1248 # Missing opening parenthesis will result in a final "" element
1249 if "" in mydeplist or "(" in mydeplist:
1252 badsyntax.append("parenthesis mismatch")
1254 except portage.exception.InvalidDependString, e:
1255 badsyntax.append(str(e))
1260 portage.dep.use_reduce(mydeplist, matchall=1)
1261 except portage.exception.InvalidDependString, e:
1262 badsyntax.append(str(e))
1264 for token in operator_tokens:
1265 if mydepstr.startswith(token+" "):
1266 myteststr = mydepstr[len(token):]
1268 myteststr = mydepstr
1269 if myteststr.endswith(" "+token):
1270 myteststr = myteststr[:-len(token)]
1271 while myteststr.find(" "+token+" ") != -1:
1272 myteststr = " ".join(myteststr.split(" "+token+" ", 1))
1273 if myteststr.find(token) != -1:
1274 badsyntax.append("'%s' not separated by space" % (token))
1276 if mytype in ("DEPEND", "RDEPEND", "PDEPEND"):
1277 for token in mydepstr.split():
1278 if token in operator_tokens or \
1279 token.endswith("?"):
1282 atom = portage.dep.Atom(token)
1283 except portage.exception.InvalidAtom:
1284 badsyntax.append("'%s' not a valid atom" % token)
1286 is_blocker = atom.blocker
1288 if mytype == "DEPEND" and \
1289 not is_blocker and \
1290 not inherited_java_eclass and \
1291 portage.dep_getkey(atom) == "virtual/jdk":
1292 stats['java.eclassesnotused'] += 1
1293 fails['java.eclassesnotused'].append(relative_path)
1294 elif mytype == "RDEPEND":
1295 if not is_blocker and \
1296 portage.dep_getkey(atom) in suspect_rdepend:
1297 stats['RDEPEND.suspect'] += 1
1298 fails['RDEPEND.suspect'].append(
1299 relative_path + ": '%s'" % atom)
1301 if portage.dep.dep_getslot(atom):
1302 stats['EAPI.incompatible'] += 1
1303 fails['EAPI.incompatible'].append(
1304 (relative_path + ": %s slot dependency" + \
1305 " not supported with EAPI='%s':" + \
1306 " '%s'") % (mytype, eapi, atom))
1307 if atom.use and eapi in ("0", "1"):
1308 stats['EAPI.incompatible'] += 1
1309 fails['EAPI.incompatible'].append(
1310 (relative_path + ": %s use dependency" + \
1311 " not supported with EAPI='%s':" + \
1312 " '%s'") % (mytype, eapi, atom))
1313 if atom.blocker and atom.blocker.overlap.forbid \
1314 and eapi in ("0", "1"):
1315 stats['EAPI.incompatible'] += 1
1316 fails['EAPI.incompatible'].append(
1317 (relative_path + ": %s new blocker syntax" + \
1318 " not supported with EAPI='%s':" + \
1319 " '%s'") % (mytype, eapi, atom))
1321 type_list.extend([mytype] * (len(badsyntax) - len(type_list)))
1323 for m,b in zip(type_list, badsyntax):
1324 stats[m+".syntax"] += 1
1325 fails[m+".syntax"].append(catpkg+".ebuild "+m+": "+b)
1327 badlicsyntax = len(filter(lambda x:x=="LICENSE", type_list))
1328 badprovsyntax = len(filter(lambda x:x=="PROVIDE", type_list))
1329 baddepsyntax = len(type_list) != badlicsyntax + badprovsyntax
1330 badlicsyntax = badlicsyntax > 0
1331 badprovsyntax = badprovsyntax > 0
1333 # uselist checks - global
1336 for myflag in myaux["IUSE"].split():
1337 flag_name = myflag.lstrip("+-")
1338 if myflag != flag_name:
1339 default_use.append(myflag)
1340 if flag_name not in uselist:
1341 myuse.append(flag_name)
1343 # uselist checks - metadata
1344 for mypos in range(len(myuse)-1,-1,-1):
1345 if myuse[mypos] and (myuse[mypos] in muselist):
1348 if default_use and eapi == "0":
1349 for myflag in default_use:
1350 stats['EAPI.incompatible'] += 1
1351 fails['EAPI.incompatible'].append(
1352 (relative_path + ": IUSE defaults" + \
1353 " not supported with EAPI='%s':" + \
1354 " '%s'") % (eapi, myflag))
1356 for mypos in range(len(myuse)):
1357 stats["IUSE.invalid"]=stats["IUSE.invalid"]+1
1358 fails["IUSE.invalid"].append(x+"/"+y+".ebuild: %s" % myuse[mypos])
1361 if not badlicsyntax:
1362 myuse = myaux["LICENSE"]
1363 # Parse the LICENSE variable, remove USE conditions and
1365 myuse=portage.dep.use_reduce(portage.dep.paren_reduce(myuse), matchall=1)
1366 myuse=portage.flatten(myuse)
1367 # Check each entry to ensure that it exists in PORTDIR's
1368 # license directory.
1369 for mypos in range(0,len(myuse)):
1370 # Need to check for "||" manually as no portage
1371 # function will remove it without removing values.
1372 if myuse[mypos] not in liclist and myuse[mypos] != "||":
1373 stats["LICENSE.invalid"]=stats["LICENSE.invalid"]+1
1374 fails["LICENSE.invalid"].append(x+"/"+y+".ebuild: %s" % myuse[mypos])
1377 myuse = myaux["KEYWORDS"].split()
1385 if myskey not in kwlist:
1386 stats["KEYWORDS.invalid"] += 1
1387 fails["KEYWORDS.invalid"].append(x+"/"+y+".ebuild: %s" % mykey)
1388 elif myskey not in profiles:
1389 stats["KEYWORDS.invalid"] += 1
1390 fails["KEYWORDS.invalid"].append(x+"/"+y+".ebuild: %s (profile invalid)" % mykey)
1395 myrestrict = portage.dep.use_reduce(
1396 portage.dep.paren_reduce(myaux["RESTRICT"]), matchall=1)
1397 except portage.exception.InvalidDependString, e:
1398 stats["RESTRICT.syntax"] = stats["RESTRICT.syntax"] + 1
1399 fails["RESTRICT.syntax"].append(
1400 "%s: RESTRICT: %s" % (relative_path, e))
1403 myrestrict = set(portage.flatten(myrestrict))
1404 mybadrestrict = myrestrict.difference(valid_restrict)
1406 stats["RESTRICT.invalid"] += len(mybadrestrict)
1407 for mybad in mybadrestrict:
1408 fails["RESTRICT.invalid"].append(x+"/"+y+".ebuild: %s" % mybad)
1410 relative_path = os.path.join(x, y + ".ebuild")
1411 full_path = os.path.join(repodir, relative_path)
1412 f = open(full_path, 'rb')
1414 for check_name, e in run_checks(f, pkg):
1415 stats[check_name] += 1
1416 fails[check_name].append(relative_path + ': %s' % e)
1422 # The dep_check() calls are the most expensive QA test. If --force
1423 # is enabled, there's no point in wasting time on these since the
1424 # user is intent on forcing the commit anyway.
1427 for keyword,arch,groups in arches:
1429 if arch not in profiles:
1430 # A missing profile will create an error further down
1431 # during the KEYWORDS verification.
1434 for prof in profiles[arch]:
1436 if prof[1] not in ("stable", "dev") or \
1437 prof[1] == "dev" and not options.include_dev:
1440 profdir = portdir+"/profiles/"+prof[0]
1442 if prof[0] in arch_caches:
1443 dep_settings = arch_caches[prof[0]]
1445 dep_settings = portage.config(
1446 config_profile_path=profdir,
1447 config_incrementals=portage.const.INCREMENTALS,
1449 if options.without_mask:
1450 dep_settings.pmaskdict.clear()
1451 arch_caches[prof[0]] = dep_settings
1454 # Protect ACCEPT_KEYWORDS from config.regenerate()
1456 dep_settings.incrementals.remove("ACCEPT_KEYWORDS")
1460 xmatch_cache_key = (prof[0], tuple(groups))
1461 xcache = arch_xmatch_caches.get(xmatch_cache_key)
1465 xcache = portdb.xcache
1466 xcache.update(shared_xmatch_caches)
1467 arch_xmatch_caches[xmatch_cache_key] = xcache
1469 trees["/"]["porttree"].settings = dep_settings
1470 portdb.mysettings = dep_settings
1471 portdb.xcache = xcache
1472 # for package.use.mask support inside dep_check
1473 dep_settings.setcpv(pkg)
1474 dep_settings["ACCEPT_KEYWORDS"] = " ".join(groups)
1475 # just in case, prevent config.reset() from nuking these.
1476 dep_settings.backup_changes("ACCEPT_KEYWORDS")
1478 for myprovide in myaux["PROVIDE"].split():
1479 prov_cp = portage.dep_getkey(myprovide)
1480 if prov_cp not in dep_settings.getvirtuals():
1481 stats["virtual.unavailable"]+=1
1482 fails["virtual.unavailable"].append(x+"/"+y+".ebuild: "+keyword+"("+prof[0]+") "+prov_cp)
1484 if not baddepsyntax:
1485 ismasked = os.path.join(catdir, y) not in \
1486 portdb.xmatch("list-visible", x)
1488 if not have_pmasked:
1489 have_pmasked = bool(dep_settings._getMaskAtom(
1490 pkg.cpv, pkg.metadata))
1491 if options.ignore_masked:
1493 #we are testing deps for a masked package; give it some lee-way
1495 matchmode = "minimum-all"
1498 matchmode = "minimum-visible"
1500 if not have_dev_keywords:
1501 have_dev_keywords = \
1502 bool(dev_keywords.intersection(keywords))
1504 if prof[1] == "dev":
1505 suffix=suffix+"indev"
1507 for mytype,mypos in [["DEPEND",len(missingvars)],["RDEPEND",len(missingvars)+1],["PDEPEND",len(missingvars)+2]]:
1509 mykey=mytype+".bad"+suffix
1510 myvalue = myaux[mytype]
1514 mydep = portage.dep_check(myvalue, portdb,
1515 dep_settings, use="all", mode=matchmode,
1518 stats[mykey]=stats[mykey]+1
1519 fails[mykey].append(x+"/"+y+".ebuild: "+keyword+"("+prof[0]+") "+repr(e[0]))
1524 #we have some unsolvable deps
1525 #remove ! deps, which always show up as unsatisfiable
1527 while d<len(mydep[1]):
1528 if mydep[1][d][0]=="!":
1532 #if we emptied out our list, continue:
1535 stats[mykey]=stats[mykey]+1
1536 fails[mykey].append(x+"/"+y+".ebuild: "+keyword+"("+prof[0]+") "+repr(mydep[1]))
1538 stats[mykey]=stats[mykey]+1
1539 fails[mykey].append(x+"/"+y+".ebuild: "+keyword+"("+prof[0]+") "+repr(mydep[1]))
1541 # Check for 'all unstable' or 'all masked' -- ACCEPT_KEYWORDS is stripped
1542 # XXX -- Needs to be implemented in dep code. Can't determine ~arch nicely.
1543 #if not portage.portdb.xmatch("bestmatch-visible",x):
1544 # stats["ebuild.nostable"]+=1
1545 # fails["ebuild.nostable"].append(x)
1546 if allmasked and repolevel == 3:
1547 stats["ebuild.allmasked"]+=1
1548 fails["ebuild.allmasked"].append(x)
1550 if options.mode == "manifest":
1553 #dofail will be set to 1 if we have failed in at least one non-warning category
1555 #dowarn will be set to 1 if we tripped any warnings
1557 #dofull will be set if we should print a "repoman full" informational message
1558 dofull = options.mode != 'full'
1564 if x not in qawarnings:
1568 (dowarn and not (options.quiet or options.mode == "scan")):
1571 # Save QA output so that it can be conveniently displayed
1572 # in $EDITOR while the user creates a commit message.
1573 # Otherwise, the user would not be able to see this output
1574 # once the editor has taken over the screen.
1575 qa_output = StringIO.StringIO()
1576 style_file = ConsoleStyleFile(sys.stdout)
1577 if options.mode == 'commit' and \
1578 (not commitmessage or not commitmessage.strip()):
1579 style_file.write_listener = qa_output
1580 console_writer = StyleWriter(file=style_file, maxcol=9999)
1581 console_writer.style_listener = style_file.new_styles
1583 f = formatter.AbstractFormatter(console_writer)
1585 utilities.format_qa_output(f, stats, fails, dofull, dofail, options, qawarnings)
1588 del console_writer, f, style_file
1589 qa_output = qa_output.getvalue()
1590 qa_output = qa_output.splitlines(True)
1592 def grouplist(mylist,seperator="/"):
1593 """(list,seperator="/") -- Takes a list of elements; groups them into
1594 same initial element categories. Returns a dict of {base:[sublist]}
1595 From: ["blah/foo","spork/spatula","blah/weee/splat"]
1596 To: {"blah":["foo","weee/splat"], "spork":["spatula"]}"""
1599 xs=x.split(seperator)
1602 if xs[0] not in mygroups:
1603 mygroups[xs[0]]=[seperator.join(xs[1:])]
1605 mygroups[xs[0]]+=[seperator.join(xs[1:])]
1608 if have_pmasked and not (options.without_mask or options.ignore_masked):
1609 print bold("Note: use --without-mask to check " + \
1610 "KEYWORDS on dependencies of masked packages")
1612 if have_dev_keywords and not options.include_dev:
1613 print bold("Note: use --include-dev (-d) to check " + \
1614 "dependencies for 'dev' profiles")
1616 if options.mode != 'commit':
1618 print bold("Note: type \"repoman full\" for a complete listing.")
1619 if dowarn and not dofail:
1620 print green("RepoMan sez:"),"\"You're only giving me a partial QA payment?\n I'll take it this time, but I'm not happy.\""
1622 print green("RepoMan sez:"),"\"If everyone were like you, I'd be out of business!\""
1624 print turquoise("Please fix these important QA issues first.")
1625 print green("RepoMan sez:"),"\"Make your QA payment on time and you'll never see the likes of me.\"\n"
1628 if dofail and can_force and options.force and not options.pretend:
1629 print green("RepoMan sez:") + \
1630 " \"You want to commit even with these QA issues?\n" + \
1631 " I'll take it this time, but I'm not happy.\"\n"
1633 if options.force and not can_force:
1634 print bad("The --force option has been disabled due to extraordinary issues.")
1635 print turquoise("Please fix these important QA issues first.")
1636 print green("RepoMan sez:"),"\"Make your QA payment on time and you'll never see the likes of me.\"\n"
1640 print green("RepoMan sez:"), "\"So, you want to play it safe. Good call.\"\n"
1645 myvcstree=portage.cvstree.getentries("./",recursive=1)
1646 myunadded=portage.cvstree.findunadded(myvcstree,recursive=1,basedir="./")
1647 except SystemExit, e:
1648 raise # TODO propogate this
1650 err("Error retrieving CVS tree; exiting.")
1653 svnstatus=os.popen("svn status --no-ignore").readlines()
1654 myunadded = [ "./"+elem.rstrip().split()[1] for elem in svnstatus if elem.startswith("?") or elem.startswith("I") ]
1655 except SystemExit, e:
1656 raise # TODO propogate this
1658 err("Error retrieving SVN info; exiting.")
1660 # get list of files not under version control or missing
1661 myf = os.popen("git ls-files --others")
1662 myunadded = [ "./" + elem[:-1] for elem in myf ]
1667 for x in range(len(myunadded)-1,-1,-1):
1668 xs=myunadded[x].split("/")
1670 print "!!! files dir is not added! Please correct this."
1672 elif xs[-1]=="Manifest":
1673 # It's a manifest... auto add
1674 myautoadd+=[myunadded[x]]
1678 print ">>> Auto-Adding missing Manifest(s)..."
1681 print "(cvs add "+" ".join(myautoadd)+")"
1683 print "(svn add "+" ".join(myautoadd)+")"
1685 print "(git add "+" ".join(myautoadd)+")"
1689 retval=os.system("cvs add "+" ".join(myautoadd))
1691 retval=os.system("svn add "+" ".join(myautoadd))
1693 retval=os.system("git add "+" ".join(myautoadd))
1695 writemsg_level("!!! Exiting on %s (shell) error code: %s\n" % \
1696 (vcs, retval), level=logging.ERROR, noiselevel=-1)
1700 print red("!!! The following files are in your local tree but are not added to the master")
1701 print red("!!! tree. Please remove them from the local tree or add them to the master tree.")
1709 mycvstree = cvstree.getentries("./", recursive=1)
1710 mychanged = cvstree.findchanged(mycvstree, recursive=1, basedir="./")
1711 mynew = cvstree.findnew(mycvstree, recursive=1, basedir="./")
1712 myremoved=portage.cvstree.findremoved(mycvstree,recursive=1,basedir="./")
1713 bin_blob_pattern = re.compile("^-kb$")
1714 no_expansion = set(portage.cvstree.findoption(mycvstree, bin_blob_pattern,
1715 recursive=1, basedir="./"))
1719 svnstatus = os.popen("svn status").readlines()
1720 mychanged = [ "./" + elem.split()[-1:][0] for elem in svnstatus if elem and elem[:1] in "MR" ]
1721 mynew = [ "./" + elem.split()[-1:][0] for elem in svnstatus if elem.startswith("A") ]
1722 myremoved = [ "./" + elem.split()[-1:][0] for elem in svnstatus if elem.startswith("D") ]
1723 # in contrast to CVS, SVN expands nothing by default.
1724 # bin_blobs historically
1725 # were just there to see what files need to be checked for
1726 # keyword expansion, which is exactly what we do here, so
1727 # slightly change the semantic meaning of "bin_blob"... In the
1728 # future we could store which keyword is expanded.
1729 props = os.popen("svn propget -R svn:keywords").readlines()
1731 # For files with multiple props set, props are delimited by newlines,
1732 # so exclude lines that don't contain " - " since each of those lines
1733 # only a contain props for a file listed on a previous line.
1734 expansion = set("./" + prop.split(" - ")[0] \
1735 for prop in props if " - " in prop)
1738 mychanged = os.popen("git ls-files -m --with-tree=HEAD").readlines()
1739 mychanged = [ "./" + elem[:-1] for elem in mychanged ]
1740 mynew = os.popen("git diff --cached --name-only --diff-filter=A").readlines()
1741 strip_levels = repolevel - 1
1743 mynew = [elem[repo_subdir_len:] for elem in mynew \
1744 if elem[:repo_subdir_len] == repo_subdir]
1745 mynew = ["./" + elem[:-1] for elem in mynew]
1746 myremoved = os.popen("git diff --cached --name-only --diff-filter=D").readlines()
1748 myremoved = [elem[repo_subdir_len:] for elem in myremoved \
1749 if elem[:repo_subdir_len] == repo_subdir]
1750 myremoved = ["./" + elem[:-1] for elem in myremoved]
1753 if not (mychanged or mynew or myremoved):
1754 print green("RepoMan sez:"), "\"Doing nothing is not always good for QA.\""
1756 print "(Didn't find any changed files...)"
1760 # Manifests need to be regenerated after all other commits, so don't commit
1761 # them now even if they have changed.
1764 for f in mychanged + mynew:
1765 if "Manifest" == os.path.basename(f):
1770 myupdates.difference_update(myremoved)
1771 myupdates = list(myupdates)
1772 mymanifests = list(mymanifests)
1775 headerstring = "'\$(Header|Id)"
1776 headerstring += ".*\$'"
1777 for myfile in myupdates:
1779 # for CVS, no_expansion contains files that are excluded from expansion
1781 if myfile in no_expansion:
1784 # for SVN, expansion contains files that are included in expansion
1786 if myfile not in expansion:
1789 myout = commands.getstatusoutput("egrep -q "+headerstring+" "+myfile)
1791 myheaders.append(myfile)
1793 print "* %s files being committed..." % green(str(len(myupdates))),
1795 # With git, there's never any keyword expansion, so there's
1796 # no need to regenerate manifests and all files will be
1797 # committed in one big commit at the end.
1800 print "%s have headers that will change." % green(str(len(myheaders)))
1801 print "* Files with headers will cause the " + \
1802 "manifests to be made and recommited."
1803 logging.info("myupdates:", str(myupdates))
1804 logging.info("myheaders:", str(myheaders))
1806 commitmessage = options.commitmsg
1807 if options.commitmsgfile:
1809 f = open(options.commitmsgfile)
1810 commitmessage = f.read()
1813 except (IOError, OSError), e:
1814 if e.errno == errno.ENOENT:
1815 portage.writemsg("!!! File Not Found: --commitmsgfile='%s'\n" % options.commitmsgfile)
1818 # We've read the content so the file is no longer needed.
1819 commitmessagefile = None
1820 if not commitmessage or not commitmessage.strip():
1822 editor = os.environ.get("EDITOR")
1823 if editor and utilities.editor_is_executable(editor):
1824 commitmessage = utilities.get_commit_message_with_editor(
1825 editor, message=qa_output)
1827 commitmessage = utilities.get_commit_message_with_stdin()
1828 except KeyboardInterrupt:
1830 if not commitmessage or not commitmessage.strip():
1831 print "* no commit message? aborting commit."
1833 commitmessage = commitmessage.rstrip()
1834 portage_version = getattr(portage, "VERSION", None)
1835 if portage_version is None:
1836 sys.stderr.write("Failed to insert portage version in message!\n")
1838 portage_version = "Unknown"
1839 unameout = platform.system() + " " + platform.release() + " "
1840 if platform.system() in ["Darwin", "SunOS"]:
1841 unameout += platform.processor()
1843 unameout += platform.machine()
1844 commitmessage += "\n(Portage version: %s/%s/%s" % \
1845 (portage_version, vcs, unameout)
1847 commitmessage += ", RepoMan options: --force"
1848 commitmessage += ")"
1850 if vcs != 'git' and (myupdates or myremoved):
1851 myfiles = myupdates + myremoved
1852 if not myheaders and "sign" not in repoman_settings.features:
1853 myfiles += mymanifests
1854 fd, commitmessagefile = tempfile.mkstemp(".repoman.msg")
1855 mymsg = os.fdopen(fd, "w")
1856 mymsg.write(commitmessage)
1860 print green("Using commit message:")
1861 print green("------------------------------------------------------------------------------")
1863 print green("------------------------------------------------------------------------------")
1866 # Having a leading ./ prefix on file paths can trigger a bug in
1867 # the cvs server when committing files to multiple directories,
1868 # so strip the prefix.
1869 myfiles = [f.lstrip("./") for f in myfiles]
1872 commit_cmd.extend(vcs_global_opts)
1873 commit_cmd.append("commit")
1874 commit_cmd.extend(vcs_local_opts)
1875 commit_cmd.extend(["-F", commitmessagefile])
1876 commit_cmd.extend(myfiles)
1880 print "(%s)" % (" ".join(commit_cmd),)
1882 retval = spawn(commit_cmd, env=os.environ)
1883 if retval != os.EX_OK:
1884 writemsg_level(("!!! Exiting on %s (shell) " + \
1885 "error code: %s\n") % (vcs, retval),
1886 level=logging.ERROR, noiselevel=-1)
1890 os.unlink(commitmessagefile)
1894 # Setup the GPG commands
1895 def gpgsign(filename):
1896 if "PORTAGE_GPG_KEY" not in repoman_settings:
1897 raise portage.exception.MissingParameter("PORTAGE_GPG_KEY is unset!")
1898 if "PORTAGE_GPG_DIR" not in repoman_settings:
1899 if "HOME" in os.environ:
1900 repoman_settings["PORTAGE_GPG_DIR"] = os.path.join(os.environ["HOME"], ".gnupg")
1901 logging.info("Automatically setting PORTAGE_GPG_DIR to %s" % repoman_settings["PORTAGE_GPG_DIR"])
1903 raise portage.exception.MissingParameter("PORTAGE_GPG_DIR is unset!")
1904 gpg_dir = repoman_settings["PORTAGE_GPG_DIR"]
1905 if gpg_dir.startswith("~") and "HOME" in os.environ:
1906 repoman_settings["PORTAGE_GPG_DIR"] = os.path.join(
1907 os.environ["HOME"], gpg_dir[1:].lstrip(os.path.sep))
1908 if not os.access(repoman_settings["PORTAGE_GPG_DIR"], os.X_OK):
1909 raise portage.exception.InvalidLocation(
1910 "Unable to access directory: PORTAGE_GPG_DIR='%s'" % \
1911 repoman_settings["PORTAGE_GPG_DIR"])
1912 gpgcmd = "gpg --sign --clearsign --yes "
1913 gpgcmd+= "--default-key "+repoman_settings["PORTAGE_GPG_KEY"]
1914 if "PORTAGE_GPG_DIR" in repoman_settings:
1915 gpgcmd += " --homedir "+repoman_settings["PORTAGE_GPG_DIR"]
1917 print "("+gpgcmd+" "+filename+")"
1919 rValue = os.system(gpgcmd+" "+filename)
1920 if rValue == os.EX_OK:
1921 os.rename(filename+".asc", filename)
1923 raise portage.exception.PortageException("!!! gpg exited with '" + str(rValue) + "' status")
1925 # When files are removed and re-added, the cvs server will put /Attic/
1926 # inside the $Header path. This code detects the problem and corrects it
1927 # so that the Manifest will generate correctly. See bug #169500.
1928 from portage.util import write_atomic
1929 cvs_header = re.compile(r'^#\s*\$Header.*\$$')
1932 mylines = f.readlines()
1935 for i, line in enumerate(mylines):
1936 if cvs_header.match(line) and "/Attic/" in line:
1937 mylines[i] = line.replace("/Attic/", "/")
1940 write_atomic(x, "".join(mylines))
1942 manifest_commit_required = True
1943 if vcs != 'git' and (myupdates or myremoved):
1944 myfiles = myupdates + myremoved
1945 for x in range(len(myfiles)-1, -1, -1):
1946 if myfiles[x].count("/") < 4-repolevel:
1949 if repolevel==3: # In a package dir
1950 repoman_settings["O"] = startdir
1951 portage.digestgen([], repoman_settings, manifestonly=1,
1953 elif repolevel==2: # In a category dir
1956 if len(xs) < 4-repolevel:
1962 mydone.append(xs[0])
1963 repoman_settings["O"] = os.path.join(startdir, xs[0])
1964 if not os.path.isdir(repoman_settings["O"]):
1966 portage.digestgen([], repoman_settings, manifestonly=1,
1968 elif repolevel==1: # repo-cvsroot
1969 print green("RepoMan sez:"), "\"You're rather crazy... doing the entire repository.\"\n"
1972 if len(xs) < 4-repolevel:
1976 if "/".join(xs[:2]) in mydone:
1978 mydone.append("/".join(xs[:2]))
1979 repoman_settings["O"] = os.path.join(startdir, xs[0], xs[1])
1980 if not os.path.isdir(repoman_settings["O"]):
1982 portage.digestgen([], repoman_settings, manifestonly=1,
1985 print red("I'm confused... I don't know where I am!")
1988 # Force an unsigned commit when more than one Manifest needs to be signed.
1989 if repolevel < 3 and "sign" in repoman_settings.features:
1991 fd, commitmessagefile = tempfile.mkstemp(".repoman.msg")
1992 mymsg = os.fdopen(fd, "w")
1993 mymsg.write(commitmessage)
1994 mymsg.write("\n (Unsigned Manifest commit)")
1998 commit_cmd.extend(vcs_global_opts)
1999 commit_cmd.append("commit")
2000 commit_cmd.extend(vcs_local_opts)
2001 commit_cmd.extend(["-F", commitmessagefile])
2002 commit_cmd.extend(f.lstrip("./") for f in mymanifests)
2006 print "(%s)" % (" ".join(commit_cmd),)
2008 retval = spawn(commit_cmd, env=os.environ)
2010 writemsg_level(("!!! Exiting on %s (shell) " + \
2011 "error code: %s\n") % (vcs, retval),
2012 level=logging.ERROR, noiselevel=-1)
2016 os.unlink(commitmessagefile)
2019 manifest_commit_required = False
2022 if "sign" in repoman_settings.features:
2024 myfiles = myupdates + myremoved + mymanifests
2026 if repolevel==3: # In a package dir
2027 repoman_settings["O"] = "."
2028 gpgsign(os.path.join(repoman_settings["O"], "Manifest"))
2029 elif repolevel==2: # In a category dir
2033 if len(xs) < 4-repolevel:
2039 mydone.append(xs[0])
2040 repoman_settings["O"] = os.path.join(".", xs[0])
2041 if not os.path.isdir(repoman_settings["O"]):
2043 gpgsign(os.path.join(repoman_settings["O"], "Manifest"))
2044 elif repolevel==1: # repo-cvsroot
2045 print green("RepoMan sez:"), "\"You're rather crazy... doing the entire repository.\"\n"
2049 if len(xs) < 4-repolevel:
2053 if "/".join(xs[:2]) in mydone:
2055 mydone.append("/".join(xs[:2]))
2056 repoman_settings["O"] = os.path.join(".", xs[0], xs[1])
2057 if not os.path.isdir(repoman_settings["O"]):
2059 gpgsign(os.path.join(repoman_settings["O"], "Manifest"))
2060 except portage.exception.PortageException, e:
2061 portage.writemsg("!!! %s\n" % str(e))
2062 portage.writemsg("!!! Disabled FEATURES='sign'\n")
2066 # It's not safe to use the git commit -a option since there might
2067 # be some modified files elsewhere in the working tree that the
2068 # user doesn't want to commit. Therefore, call git update-index
2069 # in order to ensure that the index is updated with the latest
2070 # versions of all new and modified files in the relevant portion
2071 # of the working tree.
2072 myfiles = mymanifests + myupdates
2074 update_index_cmd = ["git", "update-index"]
2075 update_index_cmd.extend(f.lstrip("./") for f in myfiles)
2077 print "(%s)" % (" ".join(update_index_cmd),)
2079 retval = spawn(update_index_cmd, env=os.environ)
2080 if retval != os.EX_OK:
2081 writemsg_level(("!!! Exiting on %s (shell) " + \
2082 "error code: %s\n") % (vcs, retval),
2083 level=logging.ERROR, noiselevel=-1)
2086 if vcs == 'git' or manifest_commit_required or signed:
2088 myfiles = mymanifests[:]
2090 myfiles += myupdates
2091 myfiles += myremoved
2094 fd, commitmessagefile = tempfile.mkstemp(".repoman.msg")
2095 mymsg = os.fdopen(fd, "w")
2096 mymsg.write(commitmessage)
2098 mymsg.write("\n (Signed Manifest commit)")
2100 mymsg.write("\n (Unsigned Manifest commit)")
2104 commit_cmd.extend(vcs_global_opts)
2105 commit_cmd.append("commit")
2106 commit_cmd.extend(vcs_local_opts)
2107 commit_cmd.extend(["-F", commitmessagefile])
2108 commit_cmd.extend(f.lstrip("./") for f in myfiles)
2112 print "(%s)" % (" ".join(commit_cmd),)
2114 retval = spawn(commit_cmd, env=os.environ)
2115 if retval != os.EX_OK:
2116 writemsg_level(("!!! Exiting on %s (shell) " + \
2117 "error code: %s\n") % (vcs, retval),
2118 level=logging.ERROR, noiselevel=-1)
2122 os.unlink(commitmessagefile)
2128 print "Commit complete."
2130 print "repoman was too scared by not seeing any familiar version control file that he forgot to commit anything"
2131 print green("RepoMan sez:"), "\"If everyone were like you, I'd be out of business!\"\n"