From 862bea4dda7da7ac7619274e54f8f34abb15b302 Mon Sep 17 00:00:00 2001 From: Michael Tremer Date: Fri, 30 Sep 2011 11:58:02 +0200 Subject: [PATCH] Add transaction test for duplicate files. --- po/pakfire.pot | 219 +++++++++++++++----------- python/pakfire/actions.py | 41 +++++ python/pakfire/base.py | 6 +- python/pakfire/errors.py | 4 + python/pakfire/filelist.py | 92 +++++++++++ python/pakfire/i18n.py | 14 ++ python/pakfire/packages/file.py | 36 ++++- python/pakfire/packages/installed.py | 14 +- python/pakfire/repository/database.py | 16 +- python/pakfire/repository/index.py | 6 + python/pakfire/repository/local.py | 4 + python/pakfire/transaction.py | 103 ++++++++++++ 12 files changed, 444 insertions(+), 111 deletions(-) create mode 100644 python/pakfire/filelist.py diff --git a/po/pakfire.pot b/po/pakfire.pot index 38cfa0469..e3ea95e1d 100644 --- a/po/pakfire.pot +++ b/po/pakfire.pot @@ -8,7 +8,7 @@ msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2011-09-27 16:07+0200\n" +"POT-Creation-Date: 2011-09-28 12:35+0200\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" @@ -17,46 +17,53 @@ msgstr "" "Content-Type: text/plain; charset=CHARSET\n" "Content-Transfer-Encoding: 8bit\n" -#: ../python/pakfire/actions.py:108 +#: ../python/pakfire/actions.py:112 #, python-format msgid "Cannot run scriptlet because no interpreter is available: %s" msgstr "" -#: ../python/pakfire/actions.py:112 +#: ../python/pakfire/actions.py:116 #, python-format msgid "Cannot run scriptlet because the interpreter is not executable: %s" msgstr "" -#: ../python/pakfire/actions.py:161 +#: ../python/pakfire/actions.py:165 #, python-format msgid "" "The scriptlet returned an error:\n" "%s" msgstr "" -#: ../python/pakfire/actions.py:164 +#: ../python/pakfire/actions.py:168 #, python-format msgid "The scriptlet ran more than %s seconds and was killed." msgstr "" -#: ../python/pakfire/actions.py:222 ../python/pakfire/actions.py:279 +#: ../python/pakfire/actions.py:223 ../python/pakfire/actions.py:239 +#: ../python/pakfire/actions.py:262 ../python/pakfire/actions.py:285 +#: ../python/pakfire/actions.py:302 ../python/pakfire/actions.py:321 +#, python-format +msgid "Running transaction test for %s" +msgstr "" + +#: ../python/pakfire/actions.py:232 ../python/pakfire/actions.py:314 msgid "Installing" msgstr "" -#: ../python/pakfire/actions.py:232 +#: ../python/pakfire/actions.py:248 msgid "Updating" msgstr "" -#: ../python/pakfire/actions.py:246 +#: ../python/pakfire/actions.py:268 msgid "Removing" msgstr "" #. Cleaning up leftover files and stuff. -#: ../python/pakfire/actions.py:264 +#: ../python/pakfire/actions.py:292 msgid "Cleanup" msgstr "" -#: ../python/pakfire/actions.py:289 +#: ../python/pakfire/actions.py:330 msgid "Downgrading" msgstr "" @@ -70,270 +77,274 @@ msgstr "" msgid "There are no packages to install." msgstr "" -#: ../python/pakfire/base.py:451 +#: ../python/pakfire/base.py:454 msgid "Build command has failed." msgstr "" -#: ../python/pakfire/base.py:535 +#: ../python/pakfire/base.py:538 msgid "Everything is fine." msgstr "" -#: ../python/pakfire/builder.py:123 +#: ../python/pakfire/builder.py:122 msgid "Package information:" msgstr "" #. Copy the makefile and load source tarballs. -#: ../python/pakfire/builder.py:261 +#: ../python/pakfire/builder.py:284 msgid "Extracting" msgstr "" -#: ../python/pakfire/builder.py:551 +#: ../python/pakfire/builder.py:540 msgid "The build command failed. See logfile for details." msgstr "" #. Package the result. #. Make all these little package from the build environment. -#: ../python/pakfire/builder.py:700 +#: ../python/pakfire/builder.py:684 msgid "Creating packages:" msgstr "" #. Execute the buildscript of this stage. -#: ../python/pakfire/builder.py:720 +#: ../python/pakfire/builder.py:704 #, python-format msgid "Running stage %s:" msgstr "" -#: ../python/pakfire/cli.py:42 +#: ../python/pakfire/cli.py:43 msgid "Pakfire command line interface." msgstr "" -#: ../python/pakfire/cli.py:49 +#: ../python/pakfire/cli.py:50 msgid "The path where pakfire should operate in." msgstr "" -#: ../python/pakfire/cli.py:112 +#: ../python/pakfire/cli.py:113 msgid "Enable verbose output." msgstr "" -#: ../python/pakfire/cli.py:115 +#: ../python/pakfire/cli.py:116 msgid "Path to a configuration file to load." msgstr "" -#: ../python/pakfire/cli.py:118 +#: ../python/pakfire/cli.py:119 msgid "Disable a repository temporarily." msgstr "" -#: ../python/pakfire/cli.py:121 +#: ../python/pakfire/cli.py:122 msgid "Enable a repository temporarily." msgstr "" -#: ../python/pakfire/cli.py:124 +#: ../python/pakfire/cli.py:125 msgid "Run pakfire in offline mode." msgstr "" -#: ../python/pakfire/cli.py:129 +#: ../python/pakfire/cli.py:130 msgid "Install one or more packages to the system." msgstr "" -#: ../python/pakfire/cli.py:131 +#: ../python/pakfire/cli.py:132 msgid "Give name of at least one package to install." msgstr "" -#: ../python/pakfire/cli.py:137 +#: ../python/pakfire/cli.py:138 msgid "Install one or more packages from the filesystem." msgstr "" -#: ../python/pakfire/cli.py:139 +#: ../python/pakfire/cli.py:140 msgid "Give filename of at least one package." msgstr "" -#: ../python/pakfire/cli.py:145 +#: ../python/pakfire/cli.py:146 msgid "Remove one or more packages from the system." msgstr "" -#: ../python/pakfire/cli.py:147 +#: ../python/pakfire/cli.py:148 msgid "Give name of at least one package to remove." msgstr "" -#: ../python/pakfire/cli.py:153 +#: ../python/pakfire/cli.py:154 msgid "Update the whole system or one specific package." msgstr "" -#: ../python/pakfire/cli.py:155 ../python/pakfire/cli.py:163 +#: ../python/pakfire/cli.py:156 ../python/pakfire/cli.py:164 msgid "Give a name of a package to update or leave emtpy for all." msgstr "" -#: ../python/pakfire/cli.py:161 +#: ../python/pakfire/cli.py:162 msgid "Check, if there are any updates available." msgstr "" -#: ../python/pakfire/cli.py:169 +#: ../python/pakfire/cli.py:170 msgid "Print some information about the given package(s)." msgstr "" -#: ../python/pakfire/cli.py:171 +#: ../python/pakfire/cli.py:172 msgid "Give at least the name of one package." msgstr "" -#: ../python/pakfire/cli.py:177 +#: ../python/pakfire/cli.py:178 msgid "Search for a given pattern." msgstr "" -#: ../python/pakfire/cli.py:179 +#: ../python/pakfire/cli.py:180 msgid "A pattern to search for." msgstr "" -#: ../python/pakfire/cli.py:185 +#: ../python/pakfire/cli.py:186 msgid "Get a list of packages that provide a given file or feature." msgstr "" -#: ../python/pakfire/cli.py:187 +#: ../python/pakfire/cli.py:188 msgid "File or feature to search for." msgstr "" -#: ../python/pakfire/cli.py:193 +#: ../python/pakfire/cli.py:194 msgid "Get list of packages that belong to the given group." msgstr "" -#: ../python/pakfire/cli.py:195 +#: ../python/pakfire/cli.py:196 msgid "Group name to search for." msgstr "" -#: ../python/pakfire/cli.py:201 +#: ../python/pakfire/cli.py:202 msgid "Install all packages that belong to the given group." msgstr "" -#: ../python/pakfire/cli.py:203 +#: ../python/pakfire/cli.py:204 msgid "Group name." msgstr "" -#: ../python/pakfire/cli.py:209 +#: ../python/pakfire/cli.py:210 msgid "List all currently enabled repositories." msgstr "" -#: ../python/pakfire/cli.py:213 +#: ../python/pakfire/cli.py:214 msgid "Cleanup commands." msgstr "" -#: ../python/pakfire/cli.py:221 +#: ../python/pakfire/cli.py:222 msgid "Cleanup all temporary files." msgstr "" -#: ../python/pakfire/cli.py:227 +#: ../python/pakfire/cli.py:228 msgid "Check the system for any errors." msgstr "" -#: ../python/pakfire/cli.py:233 +#: ../python/pakfire/cli.py:234 msgid "Check the dependencies for a particular package." msgstr "" -#: ../python/pakfire/cli.py:235 +#: ../python/pakfire/cli.py:236 msgid "Give name of at least one package to check." msgstr "" -#: ../python/pakfire/cli.py:298 ../python/pakfire/transaction.py:194 +#: ../python/pakfire/cli.py:299 ../python/pakfire/transaction.py:246 msgid "Repository" msgstr "" -#: ../python/pakfire/cli.py:298 +#: ../python/pakfire/cli.py:299 msgid "Enabled" msgstr "" -#: ../python/pakfire/cli.py:298 +#: ../python/pakfire/cli.py:299 msgid "Priority" msgstr "" -#: ../python/pakfire/cli.py:298 +#: ../python/pakfire/cli.py:299 msgid "Packages" msgstr "" -#: ../python/pakfire/cli.py:310 +#: ../python/pakfire/cli.py:311 msgid "Cleaning up everything..." msgstr "" -#: ../python/pakfire/cli.py:324 ../python/pakfire/cli.py:579 +#: ../python/pakfire/cli.py:327 +msgid "You cannot run pakfire-builder in a pakfire chroot." +msgstr "" + +#: ../python/pakfire/cli.py:330 ../python/pakfire/cli.py:585 msgid "Pakfire builder command line interface." msgstr "" -#: ../python/pakfire/cli.py:379 +#: ../python/pakfire/cli.py:385 msgid "Update the package indexes." msgstr "" -#: ../python/pakfire/cli.py:385 ../python/pakfire/cli.py:599 +#: ../python/pakfire/cli.py:391 ../python/pakfire/cli.py:605 msgid "Build one or more packages." msgstr "" -#: ../python/pakfire/cli.py:387 ../python/pakfire/cli.py:601 +#: ../python/pakfire/cli.py:393 ../python/pakfire/cli.py:607 msgid "Give name of at least one package to build." msgstr "" -#: ../python/pakfire/cli.py:391 ../python/pakfire/cli.py:605 +#: ../python/pakfire/cli.py:397 ../python/pakfire/cli.py:611 msgid "Build the package for the given architecture." msgstr "" -#: ../python/pakfire/cli.py:393 ../python/pakfire/cli.py:419 -#: ../python/pakfire/cli.py:607 +#: ../python/pakfire/cli.py:399 ../python/pakfire/cli.py:425 +#: ../python/pakfire/cli.py:613 msgid "Path were the output files should be copied to." msgstr "" -#: ../python/pakfire/cli.py:395 ../python/pakfire/cli.py:408 -#: ../python/pakfire/cli.py:609 +#: ../python/pakfire/cli.py:401 ../python/pakfire/cli.py:414 +#: ../python/pakfire/cli.py:615 msgid "Mode to run in. Is either 'release' or 'development' (default)." msgstr "" -#: ../python/pakfire/cli.py:400 +#: ../python/pakfire/cli.py:406 msgid "Go into a shell." msgstr "" -#: ../python/pakfire/cli.py:402 +#: ../python/pakfire/cli.py:408 msgid "Give name of a package." msgstr "" -#: ../python/pakfire/cli.py:406 +#: ../python/pakfire/cli.py:412 msgid "Emulated architecture in the shell." msgstr "" -#: ../python/pakfire/cli.py:413 +#: ../python/pakfire/cli.py:419 msgid "Generate a source package." msgstr "" -#: ../python/pakfire/cli.py:415 +#: ../python/pakfire/cli.py:421 msgid "Give name(s) of a package(s)." msgstr "" -#: ../python/pakfire/cli.py:492 +#: ../python/pakfire/cli.py:498 msgid "Pakfire server command line interface." msgstr "" -#: ../python/pakfire/cli.py:529 +#: ../python/pakfire/cli.py:535 msgid "Request a build job from the server." msgstr "" -#: ../python/pakfire/cli.py:535 +#: ../python/pakfire/cli.py:541 msgid "Send a keepalive to the server." msgstr "" -#: ../python/pakfire/cli.py:542 +#: ../python/pakfire/cli.py:548 msgid "Update all repositories." msgstr "" -#: ../python/pakfire/cli.py:548 +#: ../python/pakfire/cli.py:554 msgid "Repository management commands." msgstr "" -#: ../python/pakfire/cli.py:556 +#: ../python/pakfire/cli.py:562 msgid "Create a new repository index." msgstr "" -#: ../python/pakfire/cli.py:557 +#: ../python/pakfire/cli.py:563 msgid "Path to the packages." msgstr "" -#: ../python/pakfire/cli.py:558 +#: ../python/pakfire/cli.py:564 msgid "Path to input packages." msgstr "" -#: ../python/pakfire/cli.py:611 +#: ../python/pakfire/cli.py:617 msgid "Do not verify build dependencies." msgstr "" @@ -358,15 +369,23 @@ msgid "" "line and try again." msgstr "" +#: ../python/pakfire/errors.py:78 +msgid "Running pakfire-build in a pakfire container?" +msgstr "" + +#: ../python/pakfire/errors.py:82 ../python/pakfire/transaction.py:308 +msgid "Transaction test was not successful" +msgstr "" + #: ../python/pakfire/packages/base.py:89 msgid "Name" msgstr "" -#: ../python/pakfire/packages/base.py:90 ../python/pakfire/transaction.py:193 +#: ../python/pakfire/packages/base.py:90 ../python/pakfire/transaction.py:245 msgid "Arch" msgstr "" -#: ../python/pakfire/packages/base.py:91 ../python/pakfire/transaction.py:193 +#: ../python/pakfire/packages/base.py:91 ../python/pakfire/transaction.py:245 msgid "Version" msgstr "" @@ -374,7 +393,7 @@ msgstr "" msgid "Release" msgstr "" -#: ../python/pakfire/packages/base.py:96 ../python/pakfire/transaction.py:194 +#: ../python/pakfire/packages/base.py:96 ../python/pakfire/transaction.py:246 msgid "Size" msgstr "" @@ -460,11 +479,11 @@ msgstr "" msgid "Filename: %s" msgstr "" -#: ../python/pakfire/packages/make.py:101 +#: ../python/pakfire/packages/make.py:100 msgid "Package name is undefined." msgstr "" -#: ../python/pakfire/packages/make.py:104 +#: ../python/pakfire/packages/make.py:103 msgid "Package version is undefined." msgstr "" @@ -536,62 +555,70 @@ msgstr "" msgid " Solutions:" msgstr "" -#: ../python/pakfire/transaction.py:128 +#: ../python/pakfire/transaction.py:180 msgid "Downloading packages:" msgstr "" -#: ../python/pakfire/transaction.py:193 +#: ../python/pakfire/transaction.py:245 msgid "Package" msgstr "" -#: ../python/pakfire/transaction.py:198 +#: ../python/pakfire/transaction.py:250 msgid "Installing:" msgstr "" -#: ../python/pakfire/transaction.py:199 +#: ../python/pakfire/transaction.py:251 msgid "Reinstalling:" msgstr "" -#: ../python/pakfire/transaction.py:200 +#: ../python/pakfire/transaction.py:252 msgid "Updating:" msgstr "" -#: ../python/pakfire/transaction.py:201 +#: ../python/pakfire/transaction.py:253 msgid "Downgrading:" msgstr "" -#: ../python/pakfire/transaction.py:202 +#: ../python/pakfire/transaction.py:254 msgid "Removing:" msgstr "" -#: ../python/pakfire/transaction.py:208 +#: ../python/pakfire/transaction.py:260 msgid "Transaction Summary" msgstr "" -#: ../python/pakfire/transaction.py:215 +#: ../python/pakfire/transaction.py:267 msgid "package" msgstr "" -#: ../python/pakfire/transaction.py:221 +#: ../python/pakfire/transaction.py:273 #, python-format msgid "Total download size: %s" msgstr "" -#: ../python/pakfire/transaction.py:225 +#: ../python/pakfire/transaction.py:277 #, python-format msgid "Installed size: %s" msgstr "" -#: ../python/pakfire/transaction.py:227 +#: ../python/pakfire/transaction.py:279 #, python-format msgid "Freed size: %s" msgstr "" -#: ../python/pakfire/transaction.py:236 +#: ../python/pakfire/transaction.py:288 msgid "Is this okay?" msgstr "" -#: ../python/pakfire/transaction.py:242 +#: ../python/pakfire/transaction.py:291 +msgid "Running Transaction Test" +msgstr "" + +#: ../python/pakfire/transaction.py:303 +msgid "Transaction Test Succeeded" +msgstr "" + +#: ../python/pakfire/transaction.py:317 msgid "Running transaction" msgstr "" diff --git a/python/pakfire/actions.py b/python/pakfire/actions.py index 2efa60954..ee1b5275b 100644 --- a/python/pakfire/actions.py +++ b/python/pakfire/actions.py @@ -52,6 +52,10 @@ class Action(object): # A function to run additional initialization. pass + def check(self, filelist): + # This is just a dummy test that does nothing at all. + return filelist + @property def needs_download(self): return self.type in ("install", "reinstall", "upgrade", "downgrade",) \ @@ -215,6 +219,12 @@ class ActionScriptPostTransUp(ActionScriptPostTrans): class ActionInstall(Action): type = "install" + def check(self, check): + logging.debug(_("Running transaction test for %s") % self.pkg.friendly_name) + + # Check if this package can be installed. + check.install(self.pkg) + def run(self): # Add package to the database. self.local.add_package(self.pkg) @@ -225,6 +235,12 @@ class ActionInstall(Action): class ActionUpdate(Action): type = "upgrade" + def check(self, check): + logging.debug(_("Running transaction test for %s") % self.pkg.friendly_name) + + # Check if this package can be updated. + check.update(self.pkg) + def run(self): # Add new package to the database. self.local.add_package(self.pkg) @@ -242,6 +258,12 @@ class ActionRemove(Action): self.pkg = self.local.index.db.get_package_from_solv(self.pkg_solv) assert self.pkg + def check(self, check): + logging.debug(_("Running transaction test for %s") % self.pkg.friendly_name) + + # Check if this package can be removed. + check.remove(self.pkg) + def run(self): self.pkg.cleanup(_("Removing"), prefix=self.pakfire.path) @@ -259,6 +281,12 @@ class ActionCleanup(Action): self.pkg = self.local.index.db.get_package_from_solv(self.pkg_solv) assert self.pkg + def check(self, check): + logging.debug(_("Running transaction test for %s") % self.pkg.friendly_name) + + # Check if this package can be removed. + check.cleanup(self.pkg) + def run(self): # Cleaning up leftover files and stuff. self.pkg.cleanup(_("Cleanup"), prefix=self.pakfire.path) @@ -270,6 +298,13 @@ class ActionCleanup(Action): class ActionReinstall(Action): type = "reinstall" + def check(self, check): + logging.debug(_("Running transaction test for %s") % self.pkg.friendly_name) + + # Check if this package can be reinstalled. + check.remove(self.pkg) + check.install(self.pkg) + def run(self): # Remove package from the database and add it afterwards. # Sounds weird, but fixes broken entries in the database. @@ -282,6 +317,12 @@ class ActionReinstall(Action): class ActionDowngrade(Action): type = "downgrade" + def check(self, check): + logging.debug(_("Running transaction test for %s") % self.pkg.friendly_name) + + # Check if this package can be downgraded. + check.install(self.pkg) + def run(self): # Add new package to database. self.local.add_package(self.pkg) diff --git a/python/pakfire/base.py b/python/pakfire/base.py index b2c2fed39..2b2fd54f2 100644 --- a/python/pakfire/base.py +++ b/python/pakfire/base.py @@ -27,6 +27,7 @@ import string import builder import config import distro +import filelist import logger import repository import packages @@ -106,7 +107,10 @@ class Pakfire(object): def create_relation(self, s): assert s - if s.startswith("/"): + if isinstance(s, filelist._File): + return satsolver.Relation(self.pool, s.name) + + elif s.startswith("/"): return satsolver.Relation(self.pool, s) for pattern, type in self.RELATIONS: diff --git a/python/pakfire/errors.py b/python/pakfire/errors.py index 87cb84014..9e8f0cc0e 100644 --- a/python/pakfire/errors.py +++ b/python/pakfire/errors.py @@ -76,3 +76,7 @@ class PakfireError(Error): class PakfireContainerError(Error): message = _("Running pakfire-build in a pakfire container?") + + +class TransactionCheckError(Error): + message = _("Transaction test was not successful") diff --git a/python/pakfire/filelist.py b/python/pakfire/filelist.py new file mode 100644 index 000000000..4f280f5ec --- /dev/null +++ b/python/pakfire/filelist.py @@ -0,0 +1,92 @@ +#!/usr/bin/python +############################################################################### +# # +# Pakfire - The IPFire package management system # +# Copyright (C) 2011 Pakfire development team # +# # +# This program is free software: you can redistribute it and/or modify # +# it under the terms of the GNU General Public License as published by # +# the Free Software Foundation, either version 3 of the License, or # +# (at your option) any later version. # +# # +# This program is distributed in the hope that it will be useful, # +# but WITHOUT ANY WARRANTY; without even the implied warranty of # +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # +# GNU General Public License for more details. # +# # +# You should have received a copy of the GNU General Public License # +# along with this program. If not, see . # +# # +############################################################################### + +class _File(object): + def __init__(self, pakfire): + self.pakfire = pakfire + + +class File(_File): + def __init__(self, pakfire): + _File.__init__(self, pakfire) + + self.name = "" + self.pkg = None + self.size = -1 + self.hash1 = "" + + def __cmp__(self, other): + return cmp(self.pkg, other.pkg) or cmp(self.name, other.name) + + def is_dir(self): + # XXX TODO + # VERY POOR CHECK + return self.name.endswith("/") + + def is_config(self): + # XXX TODO + return False + + +class FileDatabase(_File): + def __init__(self, pakfire, db, row_id): + _File.__init__(self, pakfire) + + self.db = db + self.row_id = row_id + + self.__row = None + + @property + def row(self): + """ + Lazy fetching of the database row. + """ + if self.__row is None: + c = self.db.cursor() + c.execute("SELECT * FROM files WHERE id = ? LIMIT 1", (self.row_id,)) + + # Check if we got the same row. + #assert c.lastrowid == self.row_id + + for row in c: + self.__row = row + break + + c.close() + + return self.__row + + @property + def pkg(self): + return self.db.get_package_by_id(self.row["pkg"]) + + @property + def name(self): + return self.row["name"] + + @property + def size(self): + return self.row["size"] + + @property + def hash1(self): + return self.row["hash1"] diff --git a/python/pakfire/i18n.py b/python/pakfire/i18n.py index 2980dce5e..cae14ebe0 100644 --- a/python/pakfire/i18n.py +++ b/python/pakfire/i18n.py @@ -41,3 +41,17 @@ def _(singular, plural=None, n=None): return gettext.dngettext("pakfire", singular, plural, n) return gettext.dgettext("pakfire", singular) + +def list(self, parts): + """ + Returns a comma-separated list for the given list of parts. + + The format is, e.g., "A, B and C", "A and B" or just "A" for lists + of size 1. + """ + if len(parts) == 0: return "" + if len(parts) == 1: return parts[0] + return _("%(commas)s and %(last)s") % { + "commas": u", ".join(parts[:-1]), + "last": parts[len(parts) - 1], + } diff --git a/python/pakfire/packages/file.py b/python/pakfire/packages/file.py index f6e370902..ed37fee52 100644 --- a/python/pakfire/packages/file.py +++ b/python/pakfire/packages/file.py @@ -26,6 +26,7 @@ import tarfile import tempfile import xattr +import pakfire.filelist import pakfire.util as util import pakfire.compress as compress from pakfire.constants import * @@ -133,6 +134,9 @@ class FilePackage(Package): # Place to cache the metadata self._metadata = {} + # Place to cache the filelist + self._filelist = None + # Store the format of this package file. self.format = self.get_format() @@ -332,33 +336,51 @@ class FilePackage(Package): return inst_size - @property - def filelist(self): + def get_filelist(self): """ Return a list of the files that are contained in the package payload. """ + ret = [] + a = self.open_archive() f = a.extractfile("filelist") - ret = [] for line in f.readlines(): line = line.strip() + file = pakfire.filelist.File(self.pakfire) + if self.format >= 1: line = line.split() - line = line[0] + name = line[0] - if not line.startswith("/"): - line = "/%s" % line + # XXX need to parse the rest of the information from the + # file - ret.append(line) + else: + name = line + + if not name.startswith("/"): + name = "/%s" % name + + file.name = name + file.pkg = self + + ret.append(file) f.close() a.close() return ret + @property + def filelist(self): + if self._filelist is None: + self._filelist = self.get_filelist() + + return self._filelist + @property def configfiles(self): a = self.open_archive() diff --git a/python/pakfire/packages/installed.py b/python/pakfire/packages/installed.py index 4f01fec37..f0d8364d5 100644 --- a/python/pakfire/packages/installed.py +++ b/python/pakfire/packages/installed.py @@ -22,6 +22,7 @@ import os import pakfire.downloader +import pakfire.filelist from base import Package from file import BinaryPackage @@ -168,13 +169,16 @@ class DatabasePackage(Package): @property def filelist(self): + filelist = [] + c = self.db.cursor() - try: - c.execute("SELECT name FROM files WHERE pkg = ?", (self.id,)) + c.execute("SELECT id FROM files WHERE pkg = ?", (self.id,)) + + for id in c: + file = pakfire.filelist.FileDatabase(self.pakfire, self.db, id[0]) + filelist.append(file) - return [f["name"] for f in c] - finally: - c.close() + return filelist @property def configfiles(self): diff --git a/python/pakfire/repository/database.py b/python/pakfire/repository/database.py index f8dc8ca8d..1b3872f02 100644 --- a/python/pakfire/repository/database.py +++ b/python/pakfire/repository/database.py @@ -117,6 +117,7 @@ class DatabaseLocal(Database): INSERT INTO settings(key, val) VALUES('version', '0'); CREATE TABLE files( + id INTEGER PRIMARY KEY, name TEXT, pkg INTEGER, size INTEGER, @@ -233,8 +234,8 @@ class DatabaseLocal(Database): pkg_id = c.lastrowid - c.executemany("INSERT INTO files(name, pkg) VALUES(?, ?)", - ((file, pkg_id) for file in pkg.filelist)) + c.executemany("INSERT INTO files(name, pkg, size, hash1) VALUES(?, ?, ?, ?)", + ((f.name, pkg_id, f.size, f.hash1) for f in pkg.filelist)) except: raise @@ -266,6 +267,17 @@ class DatabaseLocal(Database): c.close() self.commit() + def get_package_by_id(self, id): + c = self.cursor() + c.execute("SELECT * FROM packages WHERE id = ?", (id,)) + + try: + for row in c: + return packages.DatabasePackage(self.pakfire, self.repo, self, row) + + finally: + c.close() + @property def packages(self): c = self.cursor() diff --git a/python/pakfire/repository/index.py b/python/pakfire/repository/index.py index 9a398d240..9b4b309fa 100644 --- a/python/pakfire/repository/index.py +++ b/python/pakfire/repository/index.py @@ -433,3 +433,9 @@ class IndexLocal(Index): if pb: pb.finish() + + @property + def filelist(self): + for pkg in self.db.packages: + for file in pkg.filelist: + yield file diff --git a/python/pakfire/repository/local.py b/python/pakfire/repository/local.py index 5ca12fc32..8e41f5e07 100644 --- a/python/pakfire/repository/local.py +++ b/python/pakfire/repository/local.py @@ -205,3 +205,7 @@ class RepositoryLocal(base.RepositoryFactory): def rem_package(self, pkg): # Remove package from the database. self.index.rem_package(pkg) + + @property + def filelist(self): + return self.index.filelist diff --git a/python/pakfire/transaction.py b/python/pakfire/transaction.py index 5dd9d5f0e..e7734a850 100644 --- a/python/pakfire/transaction.py +++ b/python/pakfire/transaction.py @@ -25,6 +25,7 @@ import progressbar import sys import time +import i18n import packages import satsolver import util @@ -37,6 +38,86 @@ PKG_DUMP_FORMAT = " %-21s %-8s %-21s %-18s %6s " # Import all actions directly. from actions import * +class TransactionCheck(object): + def __init__(self, pakfire, transaction): + self.pakfire = pakfire + self.transaction = transaction + + # A place to store errors. + self.errors = [] + + # Get a list of all installed files from the database. + self.filelist = self.load_filelist() + + @property + def error_files(self): + ret = {} + + for name, files in self.filelist.items(): + if len(files) <= 1: + continue + + ret[name] = files + + return ret + + @property + def successful(self): + return not self.error_files + + def print_errors(self): + for name, files in sorted(self.error_files.items()): + assert len(files) >= 2 + + pkgs = [f.pkg.friendly_name for f in files] + + if len(files) == 2: + logging.critical( + _("file %s from %s conflicts with file from package %s") % \ + (name, pkgs[0], pkgs[1]) + ) + + elif len(files) >= 3: + logging.critical( + _("file %s from %s conflicts with files from %s") % \ + (name, pkgs[0], i18n.list(pkgs[1:])) + ) + + def load_filelist(self): + filelist = {} + + for file in self.pakfire.repos.local.filelist: + filelist[file.name] = [file,] + + return filelist + + def install(self, pkg): + for file in pkg.filelist: + if file.is_dir(): + continue + + if self.filelist.has_key(file.name): + self.filelist[file.name].append(file) + + else: + self.filelist[file.name] = [file,] + + #self.errors.append((self.ERROR_TYPE_CONFLICT, file)) + + def remove(self, pkg): + for file in pkg.filelist: + try: + self.filelist[file.name].remove(file) + except KeyError: + pass + + def update(self, pkg): + self.install(pkg) + + def cleanup(self, pkg): + self.remove(pkg) + + class Transaction(object): action_classes = { ActionInstall.type : [ActionScriptPreIn, ActionInstall, ActionScriptPostIn, ActionScriptPostTransIn], @@ -235,10 +316,32 @@ class Transaction(object): return util.ask_user(_("Is this okay?")) + def check(self): + logging.info(_("Running Transaction Test")) + + # Initialize the check object. + check = TransactionCheck(self.pakfire, self) + + for action in self.actions: + try: + action.check(check) + except ActionError, e: + raise + + if check.successful: + logging.info(_("Transaction Test Succeeded")) + return + + check.print_errors() + raise TransactionCheckError, _("Transaction test was not successful") + def run(self): # Download all packages. self.download() + # Run the transaction test + self.check() + logging.info(_("Running transaction")) # Run all actions in order and catch all kinds of ActionError. for action in self.actions: -- 2.39.5