Rietveld Code Review Tool
Help | Bug tracker | Discussion group | Source code

Delta Between Two Patch Sets: ensure_dependencies.py

Issue 5934936779390976: 1377 - Blacklist dependencies from SCM tracking (Closed)
Left Patch Set: Created Sept. 23, 2014, 9:13 a.m.
Right Patch Set: 1377 - Blacklist dependencies from SCM tracking Created Oct. 9, 2014, 2:37 p.m.
Left:
Right:
Use n/p to move between diff chunks; N/P to move between comments.
Jump to:
Left: Side by side diff | Download
Right: Side by side diff | Download
« no previous file with change/comment | « no previous file | no next file » | no next file with change/comment »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
LEFTRIGHT
1 #!/usr/bin/env python 1 #!/usr/bin/env python
2 # coding: utf-8 2 # coding: utf-8
3 3
4 # This file is part of the Adblock Plus build tools, 4 # This file is part of the Adblock Plus build tools,
5 # Copyright (C) 2006-2014 Eyeo GmbH 5 # Copyright (C) 2006-2014 Eyeo GmbH
6 # 6 #
7 # Adblock Plus is free software: you can redistribute it and/or modify 7 # Adblock Plus is free software: you can redistribute it and/or modify
8 # it under the terms of the GNU General Public License version 3 as 8 # it under the terms of the GNU General Public License version 3 as
9 # published by the Free Software Foundation. 9 # published by the Free Software Foundation.
10 # 10 #
11 # Adblock Plus is distributed in the hope that it will be useful, 11 # Adblock Plus is distributed in the hope that it will be useful,
12 # but WITHOUT ANY WARRANTY; without even the implied warranty of 12 # but WITHOUT ANY WARRANTY; without even the implied warranty of
13 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 # GNU General Public License for more details. 14 # GNU General Public License for more details.
15 # 15 #
16 # You should have received a copy of the GNU General Public License 16 # You should have received a copy of the GNU General Public License
17 # along with Adblock Plus. If not, see <http://www.gnu.org/licenses/>. 17 # along with Adblock Plus. If not, see <http://www.gnu.org/licenses/>.
18 18
19 import sys 19 import sys
20 import os 20 import os
21 import posixpath 21 import posixpath
22 import re 22 import re
23 import io 23 import io
24 import errno 24 import errno
25 import logging 25 import logging
26 import subprocess 26 import subprocess
27 import urlparse 27 import urlparse
28
28 from collections import OrderedDict 29 from collections import OrderedDict
30 from ConfigParser import RawConfigParser
29 31
30 USAGE = """ 32 USAGE = """
31 A dependencies file should look like this: 33 A dependencies file should look like this:
32 34
33 # VCS-specific root URLs for the repositories 35 # VCS-specific root URLs for the repositories
34 _root = hg:https://hg.adblockplus.org/ git:https://github.com/adblockplus/ 36 _root = hg:https://hg.adblockplus.org/ git:https://github.com/adblockplus/
35 # File to update this script from (optional) 37 # File to update this script from (optional)
36 _self = buildtools/ensure_dependencies.py 38 _self = buildtools/ensure_dependencies.py
37 # Check out elemhidehelper repository into extensions/elemhidehelper directory 39 # Check out elemhidehelper repository into extensions/elemhidehelper directory
38 # at tag "1.2". 40 # at tag "1.2".
(...skipping 21 matching lines...) Expand all
60 # should simply return an empty string. 62 # should simply return an empty string.
61 result = subprocess.Popen(command, stdout=subprocess.PIPE, stderr=subprocess .PIPE).communicate()[0] 63 result = subprocess.Popen(command, stdout=subprocess.PIPE, stderr=subprocess .PIPE).communicate()[0]
62 return result.strip() 64 return result.strip()
63 65
64 def pull(self, repo): 66 def pull(self, repo):
65 subprocess.check_call(["hg", "pull", "--repository", repo, "--quiet"]) 67 subprocess.check_call(["hg", "pull", "--repository", repo, "--quiet"])
66 68
67 def update(self, repo, rev): 69 def update(self, repo, rev):
68 subprocess.check_call(["hg", "update", "--repository", repo, "--quiet", "--c heck", "--rev", rev]) 70 subprocess.check_call(["hg", "update", "--repository", repo, "--quiet", "--c heck", "--rev", rev])
69 71
70 def ignore(self, target): 72 def ignore(self, target, repo):
73
71 if not self.istype(target): 74 if not self.istype(target):
72 logging.warning("Mercurial won't ignore dependency %s by default. You shou ld do it manually." % (target)) 75
Wladimir Palant 2014/09/23 15:04:13 Quite the opposite actually - Mercurial will autom
mathias 2014/09/23 16:20:05 Mercurial does not ignore Git submodules (just ver
Wladimir Palant 2014/09/25 19:52:20 You are right, so a local hgignore file should be
mathias 2014/09/26 05:06:39 Thank you for the "local" keyword! I've been searc
mathias 2014/09/28 00:24:25 Done.
76 config_path = os.path.join(repo, ".hg", "hgrc")
77 ignore_path = os.path.abspath(os.path.join(repo, ".hg", "dependencies"))
78
79 config = RawConfigParser()
80 config.read(config_path)
81
82 if not config.has_section("ui"):
83 config.add_section("ui")
84
85 config.set("ui", "ignore.dependencies", ignore_path)
86 with open(config_path, "w") as stream:
87 config.write(stream)
88
89 module = os.path.relpath(target, repo)
90 _ensure_line_exists(ignore_path, module)
73 91
74 class Git(): 92 class Git():
75 def istype(self, repodir): 93 def istype(self, repodir):
76 return os.path.exists(os.path.join(repodir, ".git")) 94 return os.path.exists(os.path.join(repodir, ".git"))
77 95
78 def clone(self, source, target): 96 def clone(self, source, target):
79 source = source.rstrip("/") 97 source = source.rstrip("/")
80 if not source.endswith(".git"): 98 if not source.endswith(".git"):
81 source += ".git" 99 source += ".git"
82 subprocess.check_call(["git", "clone", "--quiet", source, target]) 100 subprocess.check_call(["git", "clone", "--quiet", source, target])
83 101
84 def get_revision_id(self, repo, rev="HEAD"): 102 def get_revision_id(self, repo, rev="HEAD"):
85 command = ["git", "rev-parse", "--revs-only", rev] 103 command = ["git", "rev-parse", "--revs-only", rev]
86 return subprocess.check_output(command, cwd=repo).strip() 104 return subprocess.check_output(command, cwd=repo).strip()
87 105
88 def pull(self, repo): 106 def pull(self, repo):
89 subprocess.check_call(["git", "fetch", "--quiet", "--all", "--tags"], cwd=re po) 107 subprocess.check_call(["git", "fetch", "--quiet", "--all", "--tags"], cwd=re po)
90 108
91 def update(self, repo, rev): 109 def update(self, repo, rev):
92 subprocess.check_call(["git", "checkout", "--quiet", rev], cwd=repo) 110 subprocess.check_call(["git", "checkout", "--quiet", rev], cwd=repo)
93 111
94 def ignore(self, target): 112 def ignore(self, target, repo):
95 module = str(target).replace(os.getcwd(),'')[1:] 113 module = os.path.relpath(target, repo)
Wladimir Palant 2014/09/23 15:04:13 There is a reason why this script never relies on
mathias 2014/09/23 16:20:05 Agreed, will be addressed in the next patch-set.
mathias 2014/09/28 00:24:25 Done.
96 exclude_file = '.git/info/exclude' 114 exclude_file = os.path.join(repo, ".git", "info", "exclude")
97 if os.path.exists(exclude_file): 115 _ensure_line_exists(exclude_file, module)
Wladimir Palant 2014/09/23 15:04:13 1) Please don't use relative file paths (see above
mathias 2014/09/23 16:20:05 Agreed, will be addressed in the next patch-set.
mathias 2014/09/28 00:24:25 Done.
98 with open(exclude_file, 'r') as f: 116
99 exclude_file_content = f.read().splitlines() 117 repo_types = OrderedDict((
Wladimir Palant 2014/09/23 15:04:13 I'd rather do [l.strip() for l in f.readlines()] h
mathias 2014/09/23 16:20:05 Agreed, will be addressed in the next patch-set.
mathias 2014/09/28 00:24:25 Done.
100 if not module in exclude_file_content: 118 ("hg", Mercurial()),
101 logging.warning("Adding dependency %s to %s" % (module, exclude_file) ) 119 ("git", Git()),
Wladimir Palant 2014/09/23 15:04:13 1) Why is this a warning? Nothing is wrong, a comp
mathias 2014/09/23 16:20:05 Agreed, we just remove the line entirely.
mathias 2014/09/28 00:24:25 Done.
102 exclude_file_content = open(exclude_file, 'a') 120 ))
Wladimir Palant 2014/09/23 15:04:13 This will fail on Windows - you are opening a file
mathias 2014/09/23 16:20:05 Agreed, will be addressed in the next patch-set.
mathias 2014/09/28 00:24:25 Done.
103 exclude_file_content.write("\n"+module)
Wladimir Palant 2014/09/23 15:04:13 What if there is already a line break at the end o
mathias 2014/09/23 16:20:05 Agreed, will be addressed in the next patch-set.
mathias 2014/09/28 00:24:25 Done.
104 exclude_file_content.close()
Wladimir Palant 2014/09/23 15:04:13 If your code throws an exception that file will ne
mathias 2014/09/23 16:20:05 Agreed, will be addressed as well.
mathias 2014/09/28 00:24:25 Done.
105
106 repo_types = {
107 "hg": Mercurial(),
108 "git": Git(),
109 }
110 121
111 def parse_spec(path, line): 122 def parse_spec(path, line):
112 if "=" not in line: 123 if "=" not in line:
113 logging.warning("Invalid line in file %s: %s" % (path, line)) 124 logging.warning("Invalid line in file %s: %s" % (path, line))
114 return None, None 125 return None, None
115 126
116 key, value = line.split("=", 1) 127 key, value = line.split("=", 1)
117 key = key.strip() 128 key = key.strip()
118 items = value.split() 129 items = value.split()
119 if not len(items): 130 if not len(items):
(...skipping 63 matching lines...) Expand 10 before | Expand all | Expand 10 after
183 for key in roots: 194 for key in roots:
184 if key == parenttype or (key in repo_types and type is None): 195 if key == parenttype or (key in repo_types and type is None):
185 type = key 196 type = key
186 if type is None: 197 if type is None:
187 raise Exception("No valid source found to create %s" % target) 198 raise Exception("No valid source found to create %s" % target)
188 199
189 url = urlparse.urljoin(roots[type], sourcename) 200 url = urlparse.urljoin(roots[type], sourcename)
190 logging.info("Cloning repository %s into %s" % (url, target)) 201 logging.info("Cloning repository %s into %s" % (url, target))
191 repo_types[type].clone(url, target) 202 repo_types[type].clone(url, target)
192 203
204 for repo in repo_types.itervalues():
205 if repo.istype(parentrepo):
206 repo.ignore(target, parentrepo)
207
193 def update_repo(target, revisions): 208 def update_repo(target, revisions):
194 type = get_repo_type(target) 209 type = get_repo_type(target)
195 if type is None: 210 if type is None:
196 logging.warning("Type of repository %s unknown, skipping update" % target) 211 logging.warning("Type of repository %s unknown, skipping update" % target)
197 return 212 return
198 213
199 if type in revisions: 214 if type in revisions:
200 revision = revisions[type] 215 revision = revisions[type]
201 elif "*" in revisions: 216 elif "*" in revisions:
202 revision = revisions["*"] 217 revision = revisions["*"]
(...skipping 26 matching lines...) Expand all
229 if overrideroots is not None: 244 if overrideroots is not None:
230 config["_root"] = overrideroots 245 config["_root"] = overrideroots
231 246
232 for dir, revisions in config.iteritems(): 247 for dir, revisions in config.iteritems():
233 if dir.startswith("_") or revisions["_source"] in skipdependencies: 248 if dir.startswith("_") or revisions["_source"] in skipdependencies:
234 continue 249 continue
235 target = safe_join(repodir, dir) 250 target = safe_join(repodir, dir)
236 ensure_repo(repodir, target, config.get("_root", {}), revisions["_source"]) 251 ensure_repo(repodir, target, config.get("_root", {}), revisions["_source"])
237 update_repo(target, revisions) 252 update_repo(target, revisions)
238 resolve_deps(target, level + 1, self_update=False, overrideroots=overrideroo ts, skipdependencies=skipdependencies) 253 resolve_deps(target, level + 1, self_update=False, overrideroots=overrideroo ts, skipdependencies=skipdependencies)
239 for repo in repo_types.itervalues():
240 if repo.istype(repodir):
241 repo.ignore(target)
Wladimir Palant 2014/09/23 15:04:13 IMHO, this should be part of ensure_repo(): 1) Th
mathias 2014/09/23 16:20:05 Valid point, we move it to `ensure_repo()`. Note,
mathias 2014/09/28 00:24:25 Done.
242 254
243 if self_update and "_self" in config and "*" in config["_self"]: 255 if self_update and "_self" in config and "*" in config["_self"]:
244 source = safe_join(repodir, config["_self"]["*"]) 256 source = safe_join(repodir, config["_self"]["*"])
245 try: 257 try:
246 with io.open(source, "rb") as handle: 258 with io.open(source, "rb") as handle:
247 sourcedata = handle.read() 259 sourcedata = handle.read()
248 except IOError, e: 260 except IOError, e:
249 if e.errno != errno.ENOENT: 261 if e.errno != errno.ENOENT:
250 raise 262 raise
251 logging.warning("File %s doesn't exist, skipping self-update" % source) 263 logging.warning("File %s doesn't exist, skipping self-update" % source)
252 return 264 return
253 265
254 target = __file__ 266 target = __file__
255 with io.open(target, "rb") as handle: 267 with io.open(target, "rb") as handle:
256 targetdata = handle.read() 268 targetdata = handle.read()
257 269
258 if sourcedata != targetdata: 270 if sourcedata != targetdata:
259 logging.info("Updating %s from %s, don't forget to commit" % (source, targ et)) 271 logging.info("Updating %s from %s, don't forget to commit" % (source, targ et))
260 with io.open(target, "wb") as handle: 272 with io.open(target, "wb") as handle:
261 handle.write(sourcedata) 273 handle.write(sourcedata)
262 if __name__ == "__main__": 274 if __name__ == "__main__":
263 logging.info("Restarting %s" % target) 275 logging.info("Restarting %s" % target)
264 os.execv(sys.executable, [sys.executable, target] + sys.argv[1:]) 276 os.execv(sys.executable, [sys.executable, target] + sys.argv[1:])
265 else: 277 else:
266 logging.warning("Cannot restart %s automatically, please rerun" % target ) 278 logging.warning("Cannot restart %s automatically, please rerun" % target )
267 279
280 def _ensure_line_exists(path, pattern):
281 with open(path, 'a+') as f:
282 file_content = [l.strip() for l in f.readlines()]
283 if not pattern in file_content:
284 file_content.append(pattern)
285 f.seek(0, os.SEEK_SET)
286 f.truncate()
287 for l in file_content:
288 print >>f, l
289
268 if __name__ == "__main__": 290 if __name__ == "__main__":
269 logging.basicConfig(format='%(levelname)s: %(message)s', level=logging.INFO) 291 logging.basicConfig(format='%(levelname)s: %(message)s', level=logging.INFO)
270 repos = sys.argv[1:] 292 repos = sys.argv[1:]
271 if not len(repos): 293 if not len(repos):
272 repos = [os.getcwd()] 294 repos = [os.getcwd()]
273 for repo in repos: 295 for repo in repos:
274 resolve_deps(repo) 296 resolve_deps(repo)
LEFTRIGHT
« no previous file | no next file » | Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Toggle Comments ('s')

Powered by Google App Engine
This is Rietveld