1 |
If sync-hooks-lazy is set to true, do not trigger postsync hooks unless |
2 |
hooks would have executed for a master repository or the repository |
3 |
has changed since the previous sync operation. |
4 |
|
5 |
If the user has not explicitly enabled sync-hooks-lazy in repos.conf, |
6 |
then execute all hooks regardless of whether or not anything has |
7 |
changed (for backward compatibility). |
8 |
|
9 |
X-Gentoo-Bug: 565172 |
10 |
X-Gentoo-Bug-URL: https://bugs.gentoo.org/show_bug.cgi?id=565172 |
11 |
--- |
12 |
man/portage.5 | 7 +++- |
13 |
pym/portage/emaint/modules/sync/sync.py | 57 +++++++++++++++++++++++++++------ |
14 |
pym/portage/repository/config.py | 6 ++-- |
15 |
pym/portage/sync/controller.py | 17 ++++++---- |
16 |
4 files changed, 69 insertions(+), 18 deletions(-) |
17 |
|
18 |
diff --git a/man/portage.5 b/man/portage.5 |
19 |
index 8e2be4f..9ddbee8 100644 |
20 |
--- a/man/portage.5 |
21 |
+++ b/man/portage.5 |
22 |
@@ -1,4 +1,4 @@ |
23 |
-.TH "PORTAGE" "5" "Feb 2015" "Portage VERSION" "Portage" |
24 |
+.TH "PORTAGE" "5" "Nov 2015" "Portage VERSION" "Portage" |
25 |
.SH NAME |
26 |
portage \- the heart of Gentoo |
27 |
.SH "DESCRIPTION" |
28 |
@@ -968,6 +968,11 @@ Specifies CVS repository. |
29 |
Specifies clone depth to use for DVCS repositories. Defaults to 1 (only |
30 |
the newest commit). If set to 0, the depth is unlimited. |
31 |
.TP |
32 |
+.B sync\-hooks\-lazy |
33 |
+If set to true, then sync of a given repository will not trigger postsync |
34 |
+hooks unless hooks would have executed for a master repository or the |
35 |
+repository has changed since the previous sync operation. |
36 |
+.TP |
37 |
.B sync\-type |
38 |
Specifies type of synchronization performed by `emerge \-\-sync`. |
39 |
.br |
40 |
diff --git a/pym/portage/emaint/modules/sync/sync.py b/pym/portage/emaint/modules/sync/sync.py |
41 |
index 57c779d..15d63e2 100644 |
42 |
--- a/pym/portage/emaint/modules/sync/sync.py |
43 |
+++ b/pym/portage/emaint/modules/sync/sync.py |
44 |
@@ -233,15 +233,17 @@ class SyncRepos(object): |
45 |
retvals = sync_scheduler.retvals |
46 |
msgs.extend(sync_scheduler.msgs) |
47 |
|
48 |
- # run the post_sync_hook one last time for |
49 |
- # run only at sync completion hooks |
50 |
- rcode = sync_manager.perform_post_sync_hook('') |
51 |
if retvals: |
52 |
msgs.extend(self.rmessage(retvals, 'sync')) |
53 |
else: |
54 |
msgs.extend(self.rmessage([('None', os.EX_OK)], 'sync')) |
55 |
- if rcode: |
56 |
- msgs.extend(self.rmessage([('None', rcode)], 'post-sync')) |
57 |
+ |
58 |
+ # run the post_sync_hook one last time for |
59 |
+ # run only at sync completion hooks |
60 |
+ if sync_scheduler.global_hooks_enabled: |
61 |
+ rcode = sync_manager.perform_post_sync_hook('') |
62 |
+ if rcode: |
63 |
+ msgs.extend(self.rmessage([('None', rcode)], 'post-sync')) |
64 |
|
65 |
# Reload the whole config. |
66 |
portage._sync_mode = False |
67 |
@@ -339,6 +341,8 @@ class SyncScheduler(AsyncScheduler): |
68 |
if master.name in selected_repo_names: |
69 |
self._repo_map[master.name] = master |
70 |
self._sync_graph.add(master.name, repo.name) |
71 |
+ self._complete_graph = self._sync_graph.copy() |
72 |
+ self._hooks_repos = set() |
73 |
self._update_leaf_nodes() |
74 |
|
75 |
def _task_exit(self, task): |
76 |
@@ -347,9 +351,13 @@ class SyncScheduler(AsyncScheduler): |
77 |
more leaf nodes. |
78 |
''' |
79 |
self._running_tasks.discard(task) |
80 |
+ # Set hooks_enabled = True by default, in order to ensure |
81 |
+ # that hooks will be called in a backward-compatible manner |
82 |
+ # even if all sync tasks have failed. |
83 |
+ hooks_enabled = True |
84 |
returncode = task.returncode |
85 |
if task.returncode == os.EX_OK: |
86 |
- returncode, message, updatecache_flg = task.result |
87 |
+ returncode, message, updatecache_flg, hooks_enabled = task.result |
88 |
if message: |
89 |
self.msgs.append(message) |
90 |
repo = task.kwargs['repo'].name |
91 |
@@ -357,8 +365,38 @@ class SyncScheduler(AsyncScheduler): |
92 |
self.retvals.append((repo, returncode)) |
93 |
self._sync_graph.remove(repo) |
94 |
self._update_leaf_nodes() |
95 |
+ if hooks_enabled: |
96 |
+ self._hooks_repos.add(repo) |
97 |
super(SyncScheduler, self)._task_exit(self) |
98 |
|
99 |
+ def _master_hooks(self, repo_name): |
100 |
+ """ |
101 |
+ @param repo_name: a repo name |
102 |
+ @type repo_name: str |
103 |
+ @return: True if hooks would have been executed for any master |
104 |
+ repositories of the given repo, False otherwise |
105 |
+ @rtype: bool |
106 |
+ """ |
107 |
+ traversed_nodes = set() |
108 |
+ node_stack = [repo_name] |
109 |
+ while node_stack: |
110 |
+ node = node_stack.pop() |
111 |
+ if node in self._hooks_repos: |
112 |
+ return True |
113 |
+ if node not in traversed_nodes: |
114 |
+ traversed_nodes.add(node) |
115 |
+ node_stack.extend(self._complete_graph.child_nodes(node)) |
116 |
+ return False |
117 |
+ |
118 |
+ @property |
119 |
+ def global_hooks_enabled(self): |
120 |
+ """ |
121 |
+ @return: True if repo.postsync.d hooks would have been executed |
122 |
+ for any repositories. |
123 |
+ @rtype: bool |
124 |
+ """ |
125 |
+ return bool(self._hooks_repos) |
126 |
+ |
127 |
def _update_leaf_nodes(self): |
128 |
''' |
129 |
Populate self._leaf_nodes with current leaves from |
130 |
@@ -389,9 +427,10 @@ class SyncScheduler(AsyncScheduler): |
131 |
self._running_repos.add(node) |
132 |
self._update_leaf_nodes() |
133 |
|
134 |
- task = self._sync_manager.async( |
135 |
- self._emerge_config, self._repo_map[node]) |
136 |
- return task |
137 |
+ return self._sync_manager.async( |
138 |
+ emerge_config=self._emerge_config, |
139 |
+ repo=self._repo_map[node], |
140 |
+ master_hooks=self._master_hooks(node)) |
141 |
|
142 |
def _can_add_job(self): |
143 |
''' |
144 |
diff --git a/pym/portage/repository/config.py b/pym/portage/repository/config.py |
145 |
index 1060bc7..c7a1a1a 100644 |
146 |
--- a/pym/portage/repository/config.py |
147 |
+++ b/pym/portage/repository/config.py |
148 |
@@ -87,7 +87,7 @@ class RepoConfig(object): |
149 |
'main_repo', 'manifest_hashes', 'masters', 'missing_repo_name', |
150 |
'name', 'portage1_profiles', 'portage1_profiles_compat', 'priority', |
151 |
'profile_formats', 'sign_commit', 'sign_manifest', |
152 |
- 'sync_depth', |
153 |
+ 'sync_depth', 'sync_hooks_lazy', |
154 |
'sync_type', 'sync_umask', 'sync_uri', 'sync_user', 'thin_manifest', |
155 |
'update_changelog', 'user_location', '_eapis_banned', |
156 |
'_eapis_deprecated', '_masters_orig', 'module_specific_options', |
157 |
@@ -175,6 +175,8 @@ class RepoConfig(object): |
158 |
self.auto_sync = auto_sync |
159 |
|
160 |
self.sync_depth = repo_opts.get('sync-depth') |
161 |
+ self.sync_hooks_lazy = repo_opts.get('sync-hooks-lazy', |
162 |
+ 'false').lower() == 'true' |
163 |
|
164 |
self.module_specific_options = {} |
165 |
|
166 |
@@ -506,7 +508,7 @@ class RepoConfigLoader(object): |
167 |
# repos.conf is allowed to override. |
168 |
for k in ('aliases', 'auto_sync', 'eclass_overrides', |
169 |
'force', 'masters', 'priority', |
170 |
- 'sync_depth', |
171 |
+ 'sync_depth', 'sync_hooks_lazy', |
172 |
'sync_type', 'sync_umask', 'sync_uri', 'sync_user', |
173 |
'module_specific_options'): |
174 |
v = getattr(repos_conf_opts, k, None) |
175 |
diff --git a/pym/portage/sync/controller.py b/pym/portage/sync/controller.py |
176 |
index e8132c2..57add91 100644 |
177 |
--- a/pym/portage/sync/controller.py |
178 |
+++ b/pym/portage/sync/controller.py |
179 |
@@ -114,16 +114,17 @@ class SyncManager(object): |
180 |
return desc |
181 |
return [] |
182 |
|
183 |
- def async(self, emerge_config=None, repo=None): |
184 |
+ def async(self, emerge_config=None, repo=None, master_hooks=True): |
185 |
self.emerge_config = emerge_config |
186 |
self.settings, self.trees, self.mtimedb = emerge_config |
187 |
self.xterm_titles = "notitles" not in self.settings.features |
188 |
self.portdb = self.trees[self.settings['EROOT']]['porttree'].dbapi |
189 |
return SyncRepo(sync_task=AsyncFunction(target=self.sync, |
190 |
- kwargs=dict(emerge_config=emerge_config, repo=repo)), |
191 |
+ kwargs=dict(emerge_config=emerge_config, repo=repo, |
192 |
+ master_hooks=master_hooks)), |
193 |
sync_callback=self._sync_callback) |
194 |
|
195 |
- def sync(self, emerge_config=None, repo=None): |
196 |
+ def sync(self, emerge_config=None, repo=None, master_hooks=True): |
197 |
self.callback = None |
198 |
self.repo = repo |
199 |
self.exitcode = 1 |
200 |
@@ -156,9 +157,13 @@ class SyncManager(object): |
201 |
taskmaster = TaskHandler(callback=self.do_callback) |
202 |
taskmaster.run_tasks(tasks, func, status, options=task_opts) |
203 |
|
204 |
- self.perform_post_sync_hook(repo.name, repo.sync_uri, repo.location) |
205 |
+ hooks_enabled = False |
206 |
+ if master_hooks or not repo.sync_hooks_lazy or self.updatecache_flg: |
207 |
+ hooks_enabled = True |
208 |
+ self.perform_post_sync_hook( |
209 |
+ repo.name, repo.sync_uri, repo.location) |
210 |
|
211 |
- return self.exitcode, None, self.updatecache_flg |
212 |
+ return self.exitcode, None, self.updatecache_flg, hooks_enabled |
213 |
|
214 |
|
215 |
def do_callback(self, result): |
216 |
@@ -328,7 +333,7 @@ class SyncManager(object): |
217 |
exitcode = proc.returncode |
218 |
updatecache_flg = False |
219 |
if proc.returncode == os.EX_OK: |
220 |
- exitcode, message, updatecache_flg = proc.result |
221 |
+ exitcode, message, updatecache_flg, hooks_enabled = proc.result |
222 |
|
223 |
if updatecache_flg and "metadata-transfer" not in self.settings.features: |
224 |
updatecache_flg = False |
225 |
-- |
226 |
2.4.9 |