]> git.ipfire.org Git - pakfire.git/commitdiff
Add reinstall command to reinstall package easily.
authorMichael Tremer <michael.tremer@ipfire.org>
Sun, 16 Oct 2011 22:09:35 +0000 (22:09 +0000)
committerMichael Tremer <michael.tremer@ipfire.org>
Sun, 16 Oct 2011 22:09:35 +0000 (22:09 +0000)
Maybe this is not very mature code, yet. Be careful
when you use it.

po/pakfire.pot
python/pakfire/api.py
python/pakfire/base.py
python/pakfire/cli.py
python/pakfire/transaction.py

index 7be59cb81b54d157c38d55d3512f52a36c8477b8..ce08fc10042c54f6fe45f091ed2176db355bf4fb 100644 (file)
@@ -8,7 +8,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: PACKAGE VERSION\n"
 "Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2011-10-16 18:42+0000\n"
+"POT-Creation-Date: 2011-10-16 21:36+0000\n"
 "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
 "Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
 "Language-Team: LANGUAGE <LL@li.org>\n"
@@ -67,31 +67,47 @@ msgstr ""
 msgid "Downgrading"
 msgstr ""
 
-#: ../python/pakfire/base.py:203 ../python/pakfire/base.py:233
-#: ../python/pakfire/base.py:279 ../python/pakfire/base.py:330
-#: ../python/pakfire/base.py:383 ../python/pakfire/base.py:403
+#: ../python/pakfire/base.py:205 ../python/pakfire/base.py:235
+#: ../python/pakfire/base.py:281 ../python/pakfire/base.py:332
+#: ../python/pakfire/base.py:401 ../python/pakfire/base.py:440
+#: ../python/pakfire/base.py:493 ../python/pakfire/base.py:513
 msgid "Nothing to do"
 msgstr ""
 
-#: ../python/pakfire/base.py:265
+#: ../python/pakfire/base.py:267
 msgid "There are no packages to install."
 msgstr ""
 
-#: ../python/pakfire/base.py:319
+#: ../python/pakfire/base.py:322
+#, python-format
+msgid "Could not find any installed package providing \"%s\"."
+msgstr ""
+
+#: ../python/pakfire/base.py:328
+#, python-format
+msgid "Multiple reinstall candidates for \"%s\": %s"
+msgstr ""
+
+#: ../python/pakfire/base.py:357
+#, python-format
+msgid "Could not find package %s in a remote repository."
+msgstr ""
+
+#: ../python/pakfire/base.py:429
 #, python-format
 msgid "Excluding %s."
 msgstr ""
 
-#: ../python/pakfire/base.py:371
+#: ../python/pakfire/base.py:481
 #, python-format
 msgid "\"%s\" package does not seem to be installed."
 msgstr ""
 
-#: ../python/pakfire/base.py:515
+#: ../python/pakfire/base.py:625
 msgid "Build command has failed."
 msgstr ""
 
-#: ../python/pakfire/base.py:595
+#: ../python/pakfire/base.py:705
 msgid "Everything is fine."
 msgstr ""
 
@@ -128,257 +144,265 @@ msgstr ""
 msgid "The path where pakfire should operate in."
 msgstr ""
 
-#: ../python/pakfire/cli.py:115
+#: ../python/pakfire/cli.py:117
 msgid "Enable verbose output."
 msgstr ""
 
-#: ../python/pakfire/cli.py:118
+#: ../python/pakfire/cli.py:120
 msgid "Path to a configuration file to load."
 msgstr ""
 
-#: ../python/pakfire/cli.py:121
+#: ../python/pakfire/cli.py:123
 msgid "Disable a repository temporarily."
 msgstr ""
 
-#: ../python/pakfire/cli.py:124
+#: ../python/pakfire/cli.py:126
 msgid "Enable a repository temporarily."
 msgstr ""
 
-#: ../python/pakfire/cli.py:127
+#: ../python/pakfire/cli.py:129
 msgid "Run pakfire in offline mode."
 msgstr ""
 
-#: ../python/pakfire/cli.py:132
+#: ../python/pakfire/cli.py:134
 msgid "Install one or more packages to the system."
 msgstr ""
 
-#: ../python/pakfire/cli.py:134
+#: ../python/pakfire/cli.py:136
 msgid "Give name of at least one package to install."
 msgstr ""
 
-#: ../python/pakfire/cli.py:140
+#: ../python/pakfire/cli.py:142
 msgid "Install one or more packages from the filesystem."
 msgstr ""
 
-#: ../python/pakfire/cli.py:142
+#: ../python/pakfire/cli.py:144
 msgid "Give filename of at least one package."
 msgstr ""
 
-#: ../python/pakfire/cli.py:148
+#: ../python/pakfire/cli.py:150
+msgid "Reinstall one or more packages."
+msgstr ""
+
+#: ../python/pakfire/cli.py:152
+msgid "Give name of at least one package to reinstall."
+msgstr ""
+
+#: ../python/pakfire/cli.py:158
 msgid "Remove one or more packages from the system."
 msgstr ""
 
-#: ../python/pakfire/cli.py:150
+#: ../python/pakfire/cli.py:160
 msgid "Give name of at least one package to remove."
 msgstr ""
 
-#: ../python/pakfire/cli.py:156
+#: ../python/pakfire/cli.py:166
 msgid "Give a name of a package to update or leave emtpy for all."
 msgstr ""
 
-#: ../python/pakfire/cli.py:158
+#: ../python/pakfire/cli.py:168
 msgid "Exclude package from update."
 msgstr ""
 
-#: ../python/pakfire/cli.py:160 ../python/pakfire/cli.py:185
+#: ../python/pakfire/cli.py:170 ../python/pakfire/cli.py:195
 msgid "Allow changing the vendor of packages."
 msgstr ""
 
-#: ../python/pakfire/cli.py:162 ../python/pakfire/cli.py:187
+#: ../python/pakfire/cli.py:172 ../python/pakfire/cli.py:197
 msgid "Allow changing the architecture of packages."
 msgstr ""
 
-#: ../python/pakfire/cli.py:167
+#: ../python/pakfire/cli.py:177
 msgid "Update the whole system or one specific package."
 msgstr ""
 
-#: ../python/pakfire/cli.py:174
+#: ../python/pakfire/cli.py:184
 msgid "Check, if there are any updates available."
 msgstr ""
 
-#: ../python/pakfire/cli.py:181
+#: ../python/pakfire/cli.py:191
 msgid "Downgrade one or more packages."
 msgstr ""
 
-#: ../python/pakfire/cli.py:183
+#: ../python/pakfire/cli.py:193
 msgid "Give a name of a package to downgrade."
 msgstr ""
 
-#: ../python/pakfire/cli.py:193
+#: ../python/pakfire/cli.py:203
 msgid "Print some information about the given package(s)."
 msgstr ""
 
-#: ../python/pakfire/cli.py:195
+#: ../python/pakfire/cli.py:205
 msgid "Give at least the name of one package."
 msgstr ""
 
-#: ../python/pakfire/cli.py:201
+#: ../python/pakfire/cli.py:211
 msgid "Search for a given pattern."
 msgstr ""
 
-#: ../python/pakfire/cli.py:203
+#: ../python/pakfire/cli.py:213
 msgid "A pattern to search for."
 msgstr ""
 
-#: ../python/pakfire/cli.py:209
+#: ../python/pakfire/cli.py:219
 msgid "Get a list of packages that provide a given file or feature."
 msgstr ""
 
-#: ../python/pakfire/cli.py:211
+#: ../python/pakfire/cli.py:221
 msgid "File or feature to search for."
 msgstr ""
 
-#: ../python/pakfire/cli.py:217
+#: ../python/pakfire/cli.py:227
 msgid "Get list of packages that belong to the given group."
 msgstr ""
 
-#: ../python/pakfire/cli.py:219
+#: ../python/pakfire/cli.py:229
 msgid "Group name to search for."
 msgstr ""
 
-#: ../python/pakfire/cli.py:225
+#: ../python/pakfire/cli.py:235
 msgid "Install all packages that belong to the given group."
 msgstr ""
 
-#: ../python/pakfire/cli.py:227
+#: ../python/pakfire/cli.py:237
 msgid "Group name."
 msgstr ""
 
-#: ../python/pakfire/cli.py:233
+#: ../python/pakfire/cli.py:243
 msgid "List all currently enabled repositories."
 msgstr ""
 
-#: ../python/pakfire/cli.py:237
+#: ../python/pakfire/cli.py:247
 msgid "Cleanup commands."
 msgstr ""
 
-#: ../python/pakfire/cli.py:245
+#: ../python/pakfire/cli.py:255
 msgid "Cleanup all temporary files."
 msgstr ""
 
-#: ../python/pakfire/cli.py:251
+#: ../python/pakfire/cli.py:261
 msgid "Check the system for any errors."
 msgstr ""
 
-#: ../python/pakfire/cli.py:257
+#: ../python/pakfire/cli.py:267
 msgid "Check the dependencies for a particular package."
 msgstr ""
 
-#: ../python/pakfire/cli.py:259
+#: ../python/pakfire/cli.py:269
 msgid "Give name of at least one package to check."
 msgstr ""
 
-#: ../python/pakfire/cli.py:335 ../python/pakfire/transaction.py:316
+#: ../python/pakfire/cli.py:348 ../python/pakfire/transaction.py:345
 msgid "Repository"
 msgstr ""
 
-#: ../python/pakfire/cli.py:335
+#: ../python/pakfire/cli.py:348
 msgid "Enabled"
 msgstr ""
 
-#: ../python/pakfire/cli.py:335
+#: ../python/pakfire/cli.py:348
 msgid "Priority"
 msgstr ""
 
-#: ../python/pakfire/cli.py:335
+#: ../python/pakfire/cli.py:348
 msgid "Packages"
 msgstr ""
 
-#: ../python/pakfire/cli.py:347
+#: ../python/pakfire/cli.py:360
 msgid "Cleaning up everything..."
 msgstr ""
 
-#: ../python/pakfire/cli.py:363
+#: ../python/pakfire/cli.py:376
 msgid "You cannot run pakfire-builder in a pakfire chroot."
 msgstr ""
 
-#: ../python/pakfire/cli.py:366 ../python/pakfire/cli.py:623
+#: ../python/pakfire/cli.py:379 ../python/pakfire/cli.py:636
 msgid "Pakfire builder command line interface."
 msgstr ""
 
-#: ../python/pakfire/cli.py:421
+#: ../python/pakfire/cli.py:434
 msgid "Update the package indexes."
 msgstr ""
 
-#: ../python/pakfire/cli.py:427 ../python/pakfire/cli.py:643
+#: ../python/pakfire/cli.py:440 ../python/pakfire/cli.py:656
 msgid "Build one or more packages."
 msgstr ""
 
-#: ../python/pakfire/cli.py:429 ../python/pakfire/cli.py:645
+#: ../python/pakfire/cli.py:442 ../python/pakfire/cli.py:658
 msgid "Give name of at least one package to build."
 msgstr ""
 
-#: ../python/pakfire/cli.py:433 ../python/pakfire/cli.py:649
+#: ../python/pakfire/cli.py:446 ../python/pakfire/cli.py:662
 msgid "Build the package for the given architecture."
 msgstr ""
 
-#: ../python/pakfire/cli.py:435 ../python/pakfire/cli.py:463
-#: ../python/pakfire/cli.py:651
+#: ../python/pakfire/cli.py:448 ../python/pakfire/cli.py:476
+#: ../python/pakfire/cli.py:664
 msgid "Path were the output files should be copied to."
 msgstr ""
 
-#: ../python/pakfire/cli.py:437 ../python/pakfire/cli.py:452
-#: ../python/pakfire/cli.py:653
+#: ../python/pakfire/cli.py:450 ../python/pakfire/cli.py:465
+#: ../python/pakfire/cli.py:666
 msgid "Mode to run in. Is either 'release' or 'development' (default)."
 msgstr ""
 
-#: ../python/pakfire/cli.py:439
+#: ../python/pakfire/cli.py:452
 msgid "Run a shell after a successful build."
 msgstr ""
 
-#: ../python/pakfire/cli.py:444
+#: ../python/pakfire/cli.py:457
 msgid "Go into a shell."
 msgstr ""
 
-#: ../python/pakfire/cli.py:446
+#: ../python/pakfire/cli.py:459
 msgid "Give name of a package."
 msgstr ""
 
-#: ../python/pakfire/cli.py:450
+#: ../python/pakfire/cli.py:463
 msgid "Emulated architecture in the shell."
 msgstr ""
 
-#: ../python/pakfire/cli.py:457
+#: ../python/pakfire/cli.py:470
 msgid "Generate a source package."
 msgstr ""
 
-#: ../python/pakfire/cli.py:459
+#: ../python/pakfire/cli.py:472
 msgid "Give name(s) of a package(s)."
 msgstr ""
 
-#: ../python/pakfire/cli.py:536
+#: ../python/pakfire/cli.py:549
 msgid "Pakfire server command line interface."
 msgstr ""
 
-#: ../python/pakfire/cli.py:573
+#: ../python/pakfire/cli.py:586
 msgid "Request a build job from the server."
 msgstr ""
 
-#: ../python/pakfire/cli.py:579
+#: ../python/pakfire/cli.py:592
 msgid "Send a keepalive to the server."
 msgstr ""
 
-#: ../python/pakfire/cli.py:586
+#: ../python/pakfire/cli.py:599
 msgid "Update all repositories."
 msgstr ""
 
-#: ../python/pakfire/cli.py:592
+#: ../python/pakfire/cli.py:605
 msgid "Repository management commands."
 msgstr ""
 
-#: ../python/pakfire/cli.py:600
+#: ../python/pakfire/cli.py:613
 msgid "Create a new repository index."
 msgstr ""
 
-#: ../python/pakfire/cli.py:601
+#: ../python/pakfire/cli.py:614
 msgid "Path to the packages."
 msgstr ""
 
-#: ../python/pakfire/cli.py:602
+#: ../python/pakfire/cli.py:615
 msgid "Path to input packages."
 msgstr ""
 
-#: ../python/pakfire/cli.py:655
+#: ../python/pakfire/cli.py:668
 msgid "Do not verify build dependencies."
 msgstr ""
 
@@ -411,7 +435,7 @@ msgstr ""
 msgid "Running pakfire-build in a pakfire container?"
 msgstr ""
 
-#: ../python/pakfire/errors.py:85 ../python/pakfire/transaction.py:381
+#: ../python/pakfire/errors.py:85 ../python/pakfire/transaction.py:410
 msgid "Transaction test was not successful"
 msgstr ""
 
@@ -424,11 +448,11 @@ msgstr ""
 msgid "Name"
 msgstr ""
 
-#: ../python/pakfire/packages/base.py:105 ../python/pakfire/transaction.py:315
+#: ../python/pakfire/packages/base.py:105 ../python/pakfire/transaction.py:344
 msgid "Arch"
 msgstr ""
 
-#: ../python/pakfire/packages/base.py:108 ../python/pakfire/transaction.py:315
+#: ../python/pakfire/packages/base.py:108 ../python/pakfire/transaction.py:344
 msgid "Version"
 msgstr ""
 
@@ -436,7 +460,7 @@ msgstr ""
 msgid "Release"
 msgstr ""
 
-#: ../python/pakfire/packages/base.py:113 ../python/pakfire/transaction.py:316
+#: ../python/pakfire/packages/base.py:113 ../python/pakfire/transaction.py:345
 msgid "Size"
 msgstr ""
 
@@ -653,75 +677,75 @@ msgid ""
 "perform transaction."
 msgstr ""
 
-#: ../python/pakfire/transaction.py:252
+#: ../python/pakfire/transaction.py:281
 #, python-format
 msgid "Not enough space to download %s of packages."
 msgstr ""
 
-#: ../python/pakfire/transaction.py:255
+#: ../python/pakfire/transaction.py:284
 msgid "Downloading packages:"
 msgstr ""
 
-#: ../python/pakfire/transaction.py:315
+#: ../python/pakfire/transaction.py:344
 msgid "Package"
 msgstr ""
 
-#: ../python/pakfire/transaction.py:320
+#: ../python/pakfire/transaction.py:349
 msgid "Installing:"
 msgstr ""
 
-#: ../python/pakfire/transaction.py:321
+#: ../python/pakfire/transaction.py:350
 msgid "Reinstalling:"
 msgstr ""
 
-#: ../python/pakfire/transaction.py:322
+#: ../python/pakfire/transaction.py:351
 msgid "Updating:"
 msgstr ""
 
-#: ../python/pakfire/transaction.py:323
+#: ../python/pakfire/transaction.py:352
 msgid "Downgrading:"
 msgstr ""
 
-#: ../python/pakfire/transaction.py:324
+#: ../python/pakfire/transaction.py:353
 msgid "Removing:"
 msgstr ""
 
-#: ../python/pakfire/transaction.py:330
+#: ../python/pakfire/transaction.py:359
 msgid "Transaction Summary"
 msgstr ""
 
-#: ../python/pakfire/transaction.py:337
+#: ../python/pakfire/transaction.py:366
 msgid "package"
 msgstr ""
 
-#: ../python/pakfire/transaction.py:343
+#: ../python/pakfire/transaction.py:372
 #, python-format
 msgid "Total download size: %s"
 msgstr ""
 
-#: ../python/pakfire/transaction.py:347
+#: ../python/pakfire/transaction.py:376
 #, python-format
 msgid "Installed size: %s"
 msgstr ""
 
-#: ../python/pakfire/transaction.py:350
+#: ../python/pakfire/transaction.py:379
 #, python-format
 msgid "Freed size: %s"
 msgstr ""
 
-#: ../python/pakfire/transaction.py:359
+#: ../python/pakfire/transaction.py:388
 msgid "Is this okay?"
 msgstr ""
 
-#: ../python/pakfire/transaction.py:362
+#: ../python/pakfire/transaction.py:391
 msgid "Running Transaction Test"
 msgstr ""
 
-#: ../python/pakfire/transaction.py:374
+#: ../python/pakfire/transaction.py:403
 msgid "Transaction Test Succeeded"
 msgstr ""
 
-#: ../python/pakfire/transaction.py:390
+#: ../python/pakfire/transaction.py:421
 msgid "Running transaction"
 msgstr ""
 
index 4092586a5e33e5631daeadda818d3c628e23acf8..033801530a78634314ee7b57acc6c1b5dcf6e638 100644 (file)
@@ -40,6 +40,11 @@ def localinstall(files, **pakfire_args):
 
        return pakfire.localinstall(files)
 
+def reinstall(pkgs, **pakfire_args):
+       pakfire = Pakfire(**pakfire_args)
+
+       return pakfire.reinstall(pkgs)
+
 def remove(what, **pakfire_args):
        pakfire = Pakfire(**pakfire_args)
 
index 1707e759f0a1e39604419c9008d0c0e5ccaef05e..aec3d7372d34ab9f131b290df0004a85d1c29355 100644 (file)
@@ -24,14 +24,16 @@ import os
 import random
 import string
 
+import actions
 import builder
 import config
 import distro
 import filelist
 import logger
-import repository
 import packages
+import repository
 import satsolver
+import transaction
 import util
 
 from constants import *
@@ -296,6 +298,111 @@ class Pakfire(object):
                        repo.remove()
                        self.repos.rem_repo(repo)
 
+       def reinstall(self, pkgs, strict=False):
+               """
+                       Reinstall one or more packages.
+
+                       If strict is True, only a package with excatly the same UUID
+                       will replace the currently installed one.
+               """
+               # XXX it is possible to install packages without fulfulling
+               # all dependencies.
+
+               reinstall_pkgs = []
+               for pattern in pkgs:
+                       _pkgs = []
+                       for pkg in self.repos.whatprovides(pattern):
+                               # Do not reinstall non-installed packages.
+                               if not pkg.is_installed():
+                                       continue
+
+                               _pkgs.append(pkg)
+
+                       if not _pkgs:
+                               logging.warning(_("Could not find any installed package providing \"%s\".") \
+                                       % pattern)
+                       elif len(_pkgs) == 1:
+                               reinstall_pkgs.append(_pkgs[0])
+                               #t.add("reinstall", _pkgs[0])
+                       else:
+                               logging.warning(_("Multiple reinstall candidates for \"%s\": %s") \
+                                       % (pattern, ", ".join(p.friendly_name for p in sorted(_pkgs))))
+
+               if not reinstall_pkgs:
+                       logging.info(_("Nothing to do"))
+                       return
+
+               # Packages we want to replace.
+               # Contains a tuple with the old and the new package.
+               pkgs = []
+
+               # Find the package that is installed in a remote repository to
+               # download it again and re-install it. We need that.
+               for pkg in reinstall_pkgs:
+                       # Collect all candidates in here.
+                       _pkgs = []
+
+                       provides = "%s=%s" % (pkg.name, pkg.friendly_version)
+                       for _pkg in self.repos.whatprovides(provides):
+                               if _pkg.is_installed():
+                                       continue
+
+                               if strict:
+                                       if pkg.uuid == _pkg.uuid:
+                                               _pkgs.append(_pkg)
+                               else:
+                                       _pkgs.append(_pkg)
+
+                       if not _pkgs:
+                               logging.warning(_("Could not find package %s in a remote repository.") % \
+                                       pkg.friendly_name)
+                       else:
+                               # Sort packages to reflect repository priorities, etc...
+                               # and take the best (first) one.
+                               _pkgs.sort()
+
+                               # Re-install best package and cleanup the old one.
+                               pkgs.append((pkg, _pkgs[0]))
+
+               # Eventually, create a request.
+               request = None
+
+               _pkgs = []
+               for old, new in pkgs:
+                       if old.uuid == new.uuid:
+                               _pkgs.append((old, new))
+                       else:
+                               if request is None:
+                                       # Create a new request.
+                                       request = self.create_request()
+
+                               # Install the new package, the old will
+                               # be cleaned up automatically.
+                               request.install(new.solvable)
+
+               if request:
+                       solver = self.create_solver()
+                       t = solver.solve(request)
+               else:
+                       # Create new transaction.
+                       t = transaction.Transaction(self)
+
+               for old, new in _pkgs:
+                       # Install the new package and remove the old one.
+                       t.add(actions.ActionReinstall.type, new)
+                       t.add(actions.ActionCleanup.type, old)
+
+               t.sort()
+
+               if not t:
+                       logging.info(_("Nothing to do"))
+                       return
+
+               if not t.cli_yesno():
+                       return
+
+               t.run()
+
        def update(self, pkgs, check=False, excludes=None, allow_vendorchange=False, allow_archchange=False):
                """
                        check indicates, if the method should return after calculation
index a9baab3dc0b0928abc929e79a5b0d71125ed17ae..6956b94ae0c74ed51decbd5265e40341c418a7cc 100644 (file)
@@ -54,6 +54,7 @@ class Cli(object):
 
                self.parse_command_install()
                self.parse_command_localinstall()
+               self.parse_command_reinstall()
                self.parse_command_remove()
                self.parse_command_info()
                self.parse_command_search()
@@ -74,6 +75,7 @@ class Cli(object):
                self.action2func = {
                        "install"      : self.handle_install,
                        "localinstall" : self.handle_localinstall,
+                       "reinstall"    : self.handle_reinstall,
                        "remove"       : self.handle_remove,
                        "check_update" : self.handle_check_update,
                        "update"       : self.handle_update,
@@ -142,6 +144,14 @@ class Cli(object):
                        help=_("Give filename of at least one package."))
                sub_install.add_argument("action", action="store_const", const="localinstall")
 
+       def parse_command_reinstall(self):
+               # Implement the "reinstall" command.
+               sub_install = self.sub_commands.add_parser("reinstall",
+                       help=_("Reinstall one or more packages."))
+               sub_install.add_argument("package", nargs="+",
+                       help=_("Give name of at least one package to reinstall."))
+               sub_install.add_argument("action", action="store_const", const="reinstall")
+
        def parse_command_remove(self):
                # Implement the "remove" command.
                sub_remove = self.sub_commands.add_parser("remove",
@@ -309,6 +319,9 @@ class Cli(object):
        def handle_localinstall(self):
                pakfire.localinstall(self.args.package, **self.pakfire_args)
 
+       def handle_reinstall(self):
+               pakfire.reinstall(self.args.package, **self.pakfire_args)
+
        def handle_remove(self):
                pakfire.remove(self.args.package, **self.pakfire_args)
 
index 627acffaa03f217280c4ced02b5f4bc397efd160..048154454104b645450a0c7ca2cce4f076a2c91f 100644 (file)
@@ -166,6 +166,14 @@ class Transaction(object):
 
                self.installsizechange = 0
 
+               self.__need_sort = False
+
+       def __nonzero__(self):
+               if self.actions:
+                       return True
+
+               return False
+
        @classmethod
        def from_solver(cls, pakfire, solver, _transaction):
                # Create a new instance of our own transaction class.
@@ -186,23 +194,44 @@ class Transaction(object):
                        action_name = step.get_type()
                        pkg = packages.SolvPackage(pakfire, step.get_solvable())
 
-                       try:
-                               classes = transaction.action_classes[action_name]
-                       except KeyError:
-                               raise Exception, "Unknown action required: %s" % action_name
+                       transaction.add(action_name, pkg)
 
-                       for action_cls in classes:
-                               action = action_cls(pakfire, pkg)
-                               assert isinstance(action, Action), action
+               # Sort all previously added actions.
+               transaction.sort()
 
-                               if isinstance(action, ActionScriptPostTrans):
-                                       actions_post.append(action)
-                               else:
-                                       actions.append(action)
+               return transaction
 
-               transaction.actions += actions + actions_post
+       def add(self, action_name, pkg):
+               assert isinstance(pkg, packages.SolvPackage), pkg
 
-               return transaction
+               try:
+                       classes = self.action_classes[action_name]
+               except KeyError:
+                       raise Exception, "Unknown action requires: %s" % action_name
+
+               for cls in classes:
+                       action = cls(self.pakfire, pkg)
+                       assert isinstance(action, Action), action
+
+                       self.actions.append(action)
+
+               self.__need_sort = True
+
+       def sort(self):
+               """
+                       Sort all actions.
+               """
+               actions = []
+               actions_post = []
+
+               for action in self.actions:
+                       if isinstance(action, ActionScriptPostTrans):
+                               actions_post.append(action)
+                       else:
+                               actions.append(action)
+
+               self.actions = actions + actions_post
+               self.__need_sort = False
 
        @property
        def installs(self):
@@ -381,6 +410,8 @@ class Transaction(object):
                raise TransactionCheckError, _("Transaction test was not successful")
 
        def run(self):
+               assert not self.__need_sort, "Did you forget to sort the transaction?"
+
                # Download all packages.
                self.download()