Example #1
0
def UpdateChangeLog(
	pkgdir, user, msg, skel_path, category, package,
	new=(), removed=(), changed=(), pretend=False, quiet=False):
	"""
	Write an entry to an existing ChangeLog, or create a new one.
	Updates copyright year on changed files, and updates the header of
	ChangeLog with the contents of skel.ChangeLog.
	"""

	if '<root@' in user:
		if not quiet:
			logging.critical('Please set ECHANGELOG_USER or run as non-root')
		return None

	# ChangeLog times are in UTC
	gmtime = time.gmtime()
	year = time.strftime('%Y', gmtime)
	date = time.strftime('%d %b %Y', gmtime)

	# check modified files and the ChangeLog for copyright updates
	# patches and diffs (identified by .patch and .diff) are excluded
	for fn in chain(new, changed):
		if fn.endswith('.diff') or fn.endswith('.patch'):
			continue
		update_copyright(os.path.join(pkgdir, fn), year, pretend=pretend)

	cl_path = os.path.join(pkgdir, 'ChangeLog')
	clold_lines = []
	clnew_lines = []
	old_header_lines = []
	header_lines = []

	clold_file = None
	try:
		clold_file = io.open(_unicode_encode(
			cl_path, encoding=_encodings['fs'], errors='strict'),
			mode='r', encoding=_encodings['repo.content'], errors='replace')
	except EnvironmentError:
		pass

	f, clnew_path = mkstemp()

	# construct correct header first
	try:
		if clold_file is not None:
			# retain header from old ChangeLog
			first_line = True
			for line in clold_file:
				line_strip = line.strip()
				if line_strip and line[:1] != "#":
					clold_lines.append(line)
					break
				# always make sure cat/pkg is up-to-date in case we are
				# moving packages around, or copied from another pkg, or ...
				if first_line:
					if line.startswith('# ChangeLog for'):
						line = '# ChangeLog for %s/%s\n' % (category, package)
					first_line = False
				old_header_lines.append(line)
				header_lines.append(update_copyright_year(year, line))
				if not line_strip:
					break

		clskel_file = None
		if not header_lines:
			# delay opening this until we find we need a header
			try:
				clskel_file = io.open(_unicode_encode(
					skel_path, encoding=_encodings['fs'], errors='strict'),
					mode='r', encoding=_encodings['repo.content'],
					errors='replace')
			except EnvironmentError:
				pass

		if clskel_file is not None:
			# read skel.ChangeLog up to first empty line
			for line in clskel_file:
				line_strip = line.strip()
				if not line_strip:
					break
				line = line.replace('<CATEGORY>', category)
				line = line.replace('<PACKAGE_NAME>', package)
				line = update_copyright_year(year, line)
				header_lines.append(line)
			header_lines.append('\n')
			clskel_file.close()

		# write new ChangeLog entry
		clnew_lines.extend(header_lines)
		newebuild = False
		for fn in new:
			if not fn.endswith('.ebuild'):
				continue
			ebuild = fn.split(os.sep)[-1][0:-7]
			clnew_lines.append('*%s (%s)\n' % (ebuild, date))
			newebuild = True
		if newebuild:
			clnew_lines.append('\n')
		trivial_files = ('ChangeLog', 'Manifest')
		display_new = [
			'+' + elem
			for elem in new
			if elem not in trivial_files]
		display_removed = [
			'-' + elem
			for elem in removed]
		display_changed = [
			elem for elem in changed
			if elem not in trivial_files]
		if not (display_new or display_removed or display_changed):
			# If there's nothing else to display, show one of the
			# trivial files.
			for fn in trivial_files:
				if fn in new:
					display_new = ['+' + fn]
					break
				elif fn in changed:
					display_changed = [fn]
					break

		display_new.sort()
		display_removed.sort()
		display_changed.sort()

		mesg = '%s; %s %s:' % (date, user, ', '.join(chain(
			display_new, display_removed, display_changed)))
		for line in textwrap.wrap(
			mesg, 80, initial_indent='  ', subsequent_indent='  ',
			break_on_hyphens=False):
			clnew_lines.append('%s\n' % line)
		for line in textwrap.wrap(
			msg, 80, initial_indent='  ', subsequent_indent='  '):
			clnew_lines.append('%s\n' % line)
		# Don't append a trailing newline if the file is new.
		if clold_file is not None:
			clnew_lines.append('\n')

		f = io.open(
			f, mode='w', encoding=_encodings['repo.content'],
			errors='backslashreplace')

		for line in clnew_lines:
			f.write(line)

		# append stuff from old ChangeLog
		if clold_file is not None:

			if clold_lines:
				# clold_lines may contain a saved non-header line
				# that we want to write first.
				# Also, append this line to clnew_lines so that the
				# unified_diff call doesn't show it as removed.
				for line in clold_lines:
					f.write(line)
					clnew_lines.append(line)

			else:
				# ensure that there is no more than one blank
				# line after our new entry
				for line in clold_file:
					if line.strip():
						f.write(line)
						break

			# Now prepend old_header_lines to clold_lines, for use
			# in the unified_diff call below.
			clold_lines = old_header_lines + clold_lines

			# Trim any trailing newlines.
			lines = clold_file.readlines()
			clold_file.close()
			while lines and lines[-1] == '\n':
				del lines[-1]
			f.writelines(lines)
		f.close()

		# show diff
		if not quiet:
			for line in difflib.unified_diff(
				clold_lines, clnew_lines,
				fromfile=cl_path, tofile=cl_path, n=0):
				util.writemsg_stdout(line, noiselevel=-1)
			util.writemsg_stdout("\n", noiselevel=-1)

		if pretend:
			# remove what we've done
			os.remove(clnew_path)
		else:
			# rename to ChangeLog, and set permissions
			try:
				clold_stat = os.stat(cl_path)
			except OSError:
				clold_stat = None

			shutil.move(clnew_path, cl_path)

			if clold_stat is None:
				util.apply_permissions(cl_path, mode=0o644)
			else:
				util.apply_stat_permissions(cl_path, clold_stat)

		if clold_file is None:
			return True
		else:
			return False
	except IOError as e:
		err = 'Repoman is unable to create/write to Changelog.new file: %s' % (e,)
		logging.critical(err)
		# try to remove if possible
		try:
			os.remove(clnew_path)
		except OSError:
			pass
		return None
Example #2
0
    def perform(self, qa_output):
        myautoadd = self._vcs_autoadd()

        self._vcs_deleted()

        changes = self.get_vcs_changed()

        mynew, mychanged, myremoved, no_expansion, expansion = changes

        # Manifests need to be regenerated after all other commits, so don't commit
        # them now even if they have changed.
        mymanifests = set()
        myupdates = set()
        for f in mychanged + mynew:
            if "Manifest" == os.path.basename(f):
                mymanifests.add(f)
            else:
                myupdates.add(f)
        myupdates.difference_update(myremoved)
        myupdates = list(myupdates)
        mymanifests = list(mymanifests)
        myheaders = []

        commitmessage = self.options.commitmsg
        if self.options.commitmsgfile:
            try:
                f = io.open(_unicode_encode(self.options.commitmsgfile,
                                            encoding=_encodings['fs'],
                                            errors='strict'),
                            mode='r',
                            encoding=_encodings['content'],
                            errors='replace')
                commitmessage = f.read()
                f.close()
                del f
            except (IOError, OSError) as e:
                if e.errno == errno.ENOENT:
                    portage.writemsg("!!! File Not Found:"
                                     " --commitmsgfile='%s'\n" %
                                     self.options.commitmsgfile)
                else:
                    raise
            if commitmessage[:9].lower() in ("cat/pkg: ", ):
                commitmessage = self.msg_prefix() + commitmessage[9:]

        if not commitmessage or not commitmessage.strip():
            commitmessage = self.get_new_commit_message(qa_output)

        commitmessage = commitmessage.rstrip()

        # Update copyright for new and changed files
        year = time.strftime('%Y', time.gmtime())
        for fn in chain(mynew, mychanged):
            if fn.endswith('.diff') or fn.endswith('.patch'):
                continue
            update_copyright(fn, year, pretend=self.options.pretend)

        myupdates, broken_changelog_manifests = self.changelogs(
            myupdates, mymanifests, myremoved, mychanged, myautoadd, mynew,
            commitmessage)

        lines = commitmessage.splitlines()
        lastline = lines[-1]
        if len(lines) == 1 or re.match(r'^\S+:\s', lastline) is None:
            commitmessage += '\n'

        commit_footer = self.get_commit_footer()
        commitmessage += commit_footer

        print("* %s files being committed..." % green(str(len(myupdates))),
              end=' ')

        if not self.vcs_settings.needs_keyword_expansion:
            # With some VCS types there's never any keyword expansion, so
            # there's no need to regenerate manifests and all files will be
            # committed in one big commit at the end.
            logging.debug("VCS type doesn't need keyword expansion")
            print()
        elif not self.repo_settings.repo_config.thin_manifest:
            logging.debug("perform: Calling thick_manifest()")
            self.vcs_settings.changes.thick_manifest(myupdates, myheaders,
                                                     no_expansion, expansion)

        logging.info("myupdates: %s", myupdates)
        logging.info("myheaders: %s", myheaders)

        uq = UserQuery(self.options)
        if self.options.ask and uq.query('Commit changes?', True) != 'Yes':
            print("* aborting commit.")
            sys.exit(128 + signal.SIGINT)

        # Handle the case where committed files have keywords which
        # will change and need a priming commit before the Manifest
        # can be committed.
        if (myupdates or myremoved) and myheaders:
            self.priming_commit(myupdates, myremoved, commitmessage)

        # When files are removed and re-added, the cvs server will put /Attic/
        # inside the $Header path. This code detects the problem and corrects it
        # so that the Manifest will generate correctly. See bug #169500.
        # Use binary mode in order to avoid potential character encoding issues.
        self.vcs_settings.changes.clear_attic(myheaders)

        if self.scanner.repolevel == 1:
            utilities.repoman_sez("\"You're rather crazy... "
                                  "doing the entire repository.\"\n")

        self.vcs_settings.changes.digest_regen(myupdates, myremoved,
                                               mymanifests, self.scanner,
                                               broken_changelog_manifests)

        if self.repo_settings.sign_manifests:
            self.sign_manifest(myupdates, myremoved, mymanifests)

        self.vcs_settings.changes.update_index(mymanifests, myupdates)

        self.add_manifest(mymanifests, myheaders, myupdates, myremoved,
                          commitmessage)

        if self.options.quiet:
            return
        print()
        if self.vcs_settings.vcs:
            print("Commit complete.")
        else:
            print("repoman was too scared"
                  " by not seeing any familiar version control file"
                  " that he forgot to commit anything")
        utilities.repoman_sez(
            "\"If everyone were like you, I'd be out of business!\"\n")
        return
Example #3
0
	def perform(self, qa_output):
		myautoadd = self._vcs_autoadd()

		self._vcs_deleted()

		changes = self.get_vcs_changed()

		mynew, mychanged, myremoved, no_expansion, expansion = changes

		# Manifests need to be regenerated after all other commits, so don't commit
		# them now even if they have changed.
		mymanifests = set()
		myupdates = set()
		for f in mychanged + mynew:
			if "Manifest" == os.path.basename(f):
				mymanifests.add(f)
			else:
				myupdates.add(f)
		myupdates.difference_update(myremoved)
		myupdates = list(myupdates)
		mymanifests = list(mymanifests)
		myheaders = []

		commitmessage = self.options.commitmsg
		if self.options.commitmsgfile:
			try:
				f = io.open(
					_unicode_encode(
						self.options.commitmsgfile,
						encoding=_encodings['fs'], errors='strict'),
					mode='r', encoding=_encodings['content'], errors='replace')
				commitmessage = f.read()
				f.close()
				del f
			except (IOError, OSError) as e:
				if e.errno == errno.ENOENT:
					portage.writemsg(
						"!!! File Not Found:"
						" --commitmsgfile='%s'\n" % self.options.commitmsgfile)
				else:
					raise
			if commitmessage[:9].lower() in ("cat/pkg: ",):
				commitmessage = self.msg_prefix() + commitmessage[9:]

		if not commitmessage or not commitmessage.strip():
			commitmessage = self.get_new_commit_message(qa_output)

		commitmessage = commitmessage.rstrip()

		# Update copyright for new and changed files
		year = time.strftime('%Y', time.gmtime())
		for fn in chain(mynew, mychanged):
			if fn.endswith('.diff') or fn.endswith('.patch'):
				continue
			update_copyright(fn, year, pretend=self.options.pretend)

		myupdates, broken_changelog_manifests = self.changelogs(
					myupdates, mymanifests, myremoved, mychanged, myautoadd,
					mynew, commitmessage)

		lines = commitmessage.splitlines()
		lastline = lines[-1]
		if len(lines) == 1 or re.match(r'^\S+:\s', lastline) is None:
			commitmessage += '\n'

		commit_footer = self.get_commit_footer()
		commitmessage += commit_footer

		print("* %s files being committed..." % green(str(len(myupdates))), end=' ')

		if not self.vcs_settings.needs_keyword_expansion:
			# With some VCS types there's never any keyword expansion, so
			# there's no need to regenerate manifests and all files will be
			# committed in one big commit at the end.
			logging.debug("VCS type doesn't need keyword expansion")
			print()
		elif not self.repo_settings.repo_config.thin_manifest:
			logging.debug("perform: Calling thick_manifest()")
			self.vcs_settings.changes.thick_manifest(myupdates, myheaders,
				no_expansion, expansion)

		logging.info("myupdates: %s", myupdates)
		logging.info("myheaders: %s", myheaders)

		uq = UserQuery(self.options)
		if self.options.ask and uq.query('Commit changes?', True) != 'Yes':
			print("* aborting commit.")
			sys.exit(128 + signal.SIGINT)

		# Handle the case where committed files have keywords which
		# will change and need a priming commit before the Manifest
		# can be committed.
		if (myupdates or myremoved) and myheaders:
			self.priming_commit(myupdates, myremoved, commitmessage)

		# When files are removed and re-added, the cvs server will put /Attic/
		# inside the $Header path. This code detects the problem and corrects it
		# so that the Manifest will generate correctly. See bug #169500.
		# Use binary mode in order to avoid potential character encoding issues.
		self.vcs_settings.changes.clear_attic(myheaders)

		if self.scanner.repolevel == 1:
			utilities.repoman_sez(
				"\"You're rather crazy... "
				"doing the entire repository.\"\n")

		self.vcs_settings.changes.digest_regen(myupdates, myremoved, mymanifests,
			self.scanner, broken_changelog_manifests)

		if self.repo_settings.sign_manifests:
			self.sign_manifest(myupdates, myremoved, mymanifests)

		self.vcs_settings.changes.update_index(mymanifests, myupdates)

		self.add_manifest(mymanifests, myheaders, myupdates, myremoved, commitmessage)

		if self.options.quiet:
			return
		print()
		if self.vcs_settings.vcs:
			print("Commit complete.")
		else:
			print(
				"repoman was too scared"
				" by not seeing any familiar version control file"
				" that he forgot to commit anything")
		utilities.repoman_sez(
			"\"If everyone were like you, I'd be out of business!\"\n")
		return
Example #4
0
    def perform(self, qa_output):
        myautoadd = self._vcs_autoadd()

        self._vcs_deleted()

        changes = self.get_vcs_changed()

        mynew, mychanged, myremoved, no_expansion, expansion = changes

        # Manifests need to be regenerated after all other commits, so don't commit
        # them now even if they have changed.
        mymanifests = set()
        myupdates = set()
        for f in mychanged + mynew:
            if "Manifest" == os.path.basename(f):
                mymanifests.add(f)
            else:
                myupdates.add(f)
        myupdates.difference_update(myremoved)
        myupdates = list(myupdates)
        mymanifests = list(mymanifests)
        myheaders = []

        commitmessage = self.options.commitmsg
        if self.options.commitmsgfile:
            try:
                f = io.open(
                    _unicode_encode(
                        self.options.commitmsgfile,
                        encoding=_encodings["fs"],
                        errors="strict",
                    ),
                    mode="r",
                    encoding=_encodings["content"],
                    errors="replace",
                )
                commitmessage = f.read()
                f.close()
                del f
            except (IOError, OSError) as e:
                if e.errno == errno.ENOENT:
                    portage.writemsg("!!! File Not Found:"
                                     " --commitmsgfile='%s'\n" %
                                     self.options.commitmsgfile)
                else:
                    raise
            if commitmessage[:9].lower() in ("cat/pkg: ", ):
                commitmessage = self.msg_prefix() + commitmessage[9:]

        if commitmessage is not None and commitmessage.strip():
            res, expl = self.verify_commit_message(commitmessage)
            if not res:
                print(bad("RepoMan does not like your commit message:"))
                print(expl)
                if self.options.force:
                    print("(but proceeding due to --force)")
                else:
                    sys.exit(1)
        else:
            commitmessage = None
            msg_qa_output = qa_output
            initial_message = None
            while True:
                commitmessage = self.get_new_commit_message(
                    msg_qa_output, commitmessage)
                res, expl = self.verify_commit_message(commitmessage)
                if res:
                    break
                else:
                    full_expl = (
                        """Issues with the commit message were found. Please fix it or remove
the whole commit message to abort.

""" + expl)
                    msg_qa_output = [
                        " %s\n" % x for x in full_expl.splitlines()
                    ] + qa_output

        commitmessage = commitmessage.rstrip()

        # Update copyright for new and changed files
        year = time.strftime("%Y", time.gmtime())
        updated_copyright = []
        for fn in chain(mynew, mychanged):
            if fn.endswith(".diff") or fn.endswith(".patch"):
                continue
            if update_copyright(fn, year, pretend=self.options.pretend):
                updated_copyright.append(fn)

        if updated_copyright and not (
                self.options.pretend
                or self.repo_settings.repo_config.thin_manifest):
            for cp in sorted(self._vcs_files_to_cps(iter(updated_copyright))):
                self._manifest_gen(cp)

        myupdates, broken_changelog_manifests = self.changelogs(
            myupdates,
            mymanifests,
            myremoved,
            mychanged,
            myautoadd,
            mynew,
            commitmessage,
        )

        lines = commitmessage.splitlines()
        lastline = lines[-1]
        if len(lines) == 1 or re.match(r"^\S+:\s", lastline) is None:
            commitmessage += "\n"

        commit_footer = self.get_commit_footer()
        commitmessage += commit_footer

        print("* %s files being committed..." % green(str(len(myupdates))),
              end=" ")

        if not self.vcs_settings.needs_keyword_expansion:
            # With some VCS types there's never any keyword expansion, so
            # there's no need to regenerate manifests and all files will be
            # committed in one big commit at the end.
            logging.debug("VCS type doesn't need keyword expansion")
            print()
        elif not self.repo_settings.repo_config.thin_manifest:
            logging.debug("perform: Calling thick_manifest()")
            self.vcs_settings.changes.thick_manifest(myupdates, myheaders,
                                                     no_expansion, expansion)

        logging.info("myupdates: %s", myupdates)
        logging.info("myheaders: %s", myheaders)

        uq = UserQuery(self.options)
        if self.options.ask and uq.query("Commit changes?", True) != "Yes":
            print("* aborting commit.")
            sys.exit(128 + signal.SIGINT)

        # Handle the case where committed files have keywords which
        # will change and need a priming commit before the Manifest
        # can be committed.
        if (myupdates or myremoved) and myheaders:
            self.priming_commit(myupdates, myremoved, commitmessage)

        # When files are removed and re-added, the cvs server will put /Attic/
        # inside the $Header path. This code detects the problem and corrects it
        # so that the Manifest will generate correctly. See bug #169500.
        # Use binary mode in order to avoid potential character encoding issues.
        self.vcs_settings.changes.clear_attic(myheaders)

        if self.scanner.repolevel == 1:
            utilities.repoman_sez("\"You're rather crazy... "
                                  'doing the entire repository."\n')

        self.vcs_settings.changes.digest_regen(myupdates, myremoved,
                                               mymanifests, self.scanner,
                                               broken_changelog_manifests)

        if self.repo_settings.sign_manifests:
            self.sign_manifest(myupdates, myremoved, mymanifests)

        self.vcs_settings.changes.update_index(mymanifests, myupdates)

        self.add_manifest(mymanifests, myheaders, myupdates, myremoved,
                          commitmessage)

        if self.options.quiet:
            return
        print()
        if self.vcs_settings.vcs:
            print("Commit complete.")
        else:
            print("repoman was too scared"
                  " by not seeing any familiar version control file"
                  " that he forgot to commit anything")
        utilities.repoman_sez(
            '"If everyone were like you, I\'d be out of business!"\n')
        return