Gentoo Archives: gentoo-portage-dev

From: Pavel Kazakov <nullishzero@g.o>
To: gentoo-portage-dev@l.g.o
Cc: Pavel Kazakov <nullishzero@g.o>
Subject: [gentoo-portage-dev] [PATCH v2] Add an emaint module that can scan for failed merges and that can fix failed merges.
Date: Sat, 22 Feb 2014 00:35:22
Message-Id: 1393029287-6842-1-git-send-email-nullishzero@gentoo.org
In Reply to: [gentoo-portage-dev] [PATCH] Add an emaint module that can scan for failed merges and that can fix failed merges. by Pavel Kazakov
1 ---
2 pym/portage/emaint/main.py | 6 +-
3 pym/portage/emaint/modules/merges/__init__.py | 19 ++++
4 pym/portage/emaint/modules/merges/merges.py | 121 ++++++++++++++++++++++++++
5 3 files changed, 144 insertions(+), 2 deletions(-)
6 create mode 100644 pym/portage/emaint/modules/merges/__init__.py
7 create mode 100644 pym/portage/emaint/modules/merges/merges.py
8
9 diff --git a/pym/portage/emaint/main.py b/pym/portage/emaint/main.py
10 index a03ffd7..c1e0c0b 100644
11 --- a/pym/portage/emaint/main.py
12 +++ b/pym/portage/emaint/main.py
13 @@ -95,10 +95,11 @@ def module_opts(module_controller, module):
14 class TaskHandler(object):
15 """Handles the running of the tasks it is given"""
16
17 - def __init__(self, show_progress_bar=True, verbose=True, callback=None):
18 + def __init__(self, show_progress_bar=True, verbose=True, callback=None, module_output=None):
19 self.show_progress_bar = show_progress_bar
20 self.verbose = verbose
21 self.callback = callback
22 + self.module_output = module_output
23 self.isatty = os.environ.get('TERM') != 'dumb' and sys.stdout.isatty()
24 self.progress_bar = ProgressBar(self.isatty, title="Emaint", max_desc_length=27)
25
26 @@ -121,6 +122,7 @@ class TaskHandler(object):
27 onProgress = None
28 kwargs = {
29 'onProgress': onProgress,
30 + 'module_output': self.module_output,
31 # pass in a copy of the options so a module can not pollute or change
32 # them for other tasks if there is more to do.
33 'options': options.copy()
34 @@ -215,5 +217,5 @@ def emaint_main(myargv):
35 # need to pass the parser options dict to the modules
36 # so they are available if needed.
37 task_opts = options.__dict__
38 - taskmaster = TaskHandler(callback=print_results)
39 + taskmaster = TaskHandler(callback=print_results, module_output=sys.stdout)
40 taskmaster.run_tasks(tasks, func, status, options=task_opts)
41 diff --git a/pym/portage/emaint/modules/merges/__init__.py b/pym/portage/emaint/modules/merges/__init__.py
42 new file mode 100644
43 index 0000000..638d3da
44 --- /dev/null
45 +++ b/pym/portage/emaint/modules/merges/__init__.py
46 @@ -0,0 +1,19 @@
47 +# Copyright 2005-2014 Gentoo Foundation
48 +# Distributed under the terms of the GNU General Public License v2
49 +
50 +"""Scan for failed merges and fix them."""
51 +
52 +
53 +module_spec = {
54 + 'name': 'merges',
55 + 'description': __doc__,
56 + 'provides':{
57 + 'merges': {
58 + 'name': "merges",
59 + 'class': "MergesHandler",
60 + 'description': __doc__,
61 + 'functions': ['check', 'fix',],
62 + 'func_desc': {}
63 + }
64 + }
65 + }
66 diff --git a/pym/portage/emaint/modules/merges/merges.py b/pym/portage/emaint/modules/merges/merges.py
67 new file mode 100644
68 index 0000000..f03d9c4
69 --- /dev/null
70 +++ b/pym/portage/emaint/modules/merges/merges.py
71 @@ -0,0 +1,121 @@
72 +# Copyright 2005-2014 Gentoo Foundation
73 +# Distributed under the terms of the GNU General Public License v2
74 +
75 +import portage
76 +from portage import os, _unicode_encode
77 +from portage.const import PORTAGE_BIN_PATH, PRIVATE_PATH, VDB_PATH
78 +from portage.dep import isvalidatom
79 +
80 +import shutil
81 +import sys
82 +import subprocess
83 +
84 +class MergesHandler(object):
85 +
86 + short_desc = "Remove failed merges"
87 +
88 + @staticmethod
89 + def name():
90 + return "merges"
91 +
92 +
93 + def __init__(self):
94 + self._eroot = portage.settings['EROOT']
95 + self._vardb_path = os.path.join(self._eroot, VDB_PATH)
96 +
97 +
98 + def can_progressbar(self, func):
99 + if func == 'fix':
100 + return False
101 + return True
102 +
103 +
104 + def _failed_packages(self, onProgress=None):
105 + for cat in os.listdir(self._vardb_path):
106 + pkgs = os.listdir(os.path.join(self._vardb_path, cat))
107 + maxval = len(pkgs)
108 + for i, pkg in enumerate(pkgs):
109 + if onProgress:
110 + onProgress(maxval, i+1)
111 +
112 + if '-MERGING-' in pkg:
113 + yield os.path.join(cat, pkg)
114 +
115 +
116 + def check(self, **kwargs):
117 + onProgress = kwargs.get('onProgress', None)
118 + failed_pkgs = set(self._failed_packages(onProgress))
119 + errors = ["'%s' failed to merge." % x for x in failed_pkgs]
120 + return errors
121 +
122 +
123 + def fix(self, **kwargs):
124 + module_output = kwargs.get('module_output', None)
125 + failed_pkgs = set(self._failed_packages())
126 + if not failed_pkgs:
127 + return [ 'No failed merges found. ' ]
128 +
129 + tracking_path = os.path.join(self._eroot, PRIVATE_PATH, 'failed-merges');
130 + try:
131 + with open(_unicode_encode(tracking_path), 'w') as tracking_file:
132 + tracking_file.write('\n'.join(failed_pkgs))
133 + except IOError as ex:
134 + errors = [ 'Unable to save failed merges to tracking file: %s\n'
135 + % str(ex) ]
136 + errors.append(', '.join(failed_pkgs))
137 +
138 + return errors
139 +
140 + pkg_dne = []
141 + pkg_atoms = set()
142 + for failed_pkg in failed_pkgs:
143 + # validate pkg name
144 + pkg_name = '%s' % failed_pkg.replace('-MERGING-', '')
145 + pkg_atom = '=%s' % pkg_name
146 + if not isvalidatom(pkg_atom):
147 + pkg_dne.append( "'%s' is an invalid package atom." % pkg_atom)
148 + if not portage.portdb.cpv_exists(pkg_name):
149 + pkg_dne.append( "'%s' does not exist in the portage tree."
150 + % pkg_name)
151 + pkg_atoms.add(pkg_atom)
152 + if pkg_dne:
153 + return pkg_dne
154 +
155 + for failed_pkg in failed_pkgs:
156 + pkg_path = os.path.join(self._vardb_path, failed_pkg)
157 + # delete failed merge directory
158 + shutil.rmtree(pkg_path)
159 + # TODO: try removing package contents to prevent orphaned
160 + # files
161 +
162 + # TODO: rewrite code to use portage's APIs instead of a subprocess
163 + env = {
164 + "FEATURES" : "-collision-detect -protect-owned",
165 + "PATH" : os.environ["PATH"]
166 + }
167 + emerge_cmd = (
168 + portage._python_interpreter,
169 + '-b',
170 + os.path.join(PORTAGE_BIN_PATH, 'emerge'),
171 + '--quiet',
172 + '--oneshot',
173 + '--complete-graph=y'
174 + )
175 + results = []
176 + msg = 'Re-Emerging packages that failed to merge...\n'
177 + if module_output:
178 + module_output.write(msg)
179 + else:
180 + module_output = subprocess.PIPE
181 + results.append(msg)
182 + proc = subprocess.Popen(emerge_cmd + tuple(pkg_atoms), env=env,
183 + stdout=module_output, stderr=sys.stderr)
184 + output = proc.communicate()[0]
185 + if output:
186 + results.append(output)
187 + if proc.returncode != os.EX_OK:
188 + finish = "Failed to emerge '%s'" % (' '.join(pkg_atoms))
189 + else:
190 + finish = "Successfully emerged '%s'" % (' '.join(pkg_atoms))
191 + results.append(finish)
192 + return results
193 --
194 1.8.3.2

Replies