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

Side by Side Diff: ensure_dependencies.py

Issue 29526588: Issue 5559 - include Node.js in ensure_dependencies.py (Closed)
Patch Set: Created Aug. 24, 2017, 11:04 a.m.
Left:
Right:
Use n/p to move between diff chunks; N/P to move between comments.
Jump to:
View unified diff | Download patch
« no previous file with comments | « no previous file | no next file » | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 #!/usr/bin/env python 1 #!/usr/bin/env python
2 2
3 # This Source Code Form is subject to the terms of the Mozilla Public 3 # This Source Code Form is subject to the terms of the Mozilla Public
4 # License, v. 2.0. If a copy of the MPL was not distributed with this 4 # License, v. 2.0. If a copy of the MPL was not distributed with this
5 # file, You can obtain one at http://mozilla.org/MPL/2.0/. 5 # file, You can obtain one at http://mozilla.org/MPL/2.0/.
6 6
7 import sys 7 import sys
8 import os 8 import os
9 import posixpath 9 import posixpath
10 import re 10 import re
11 import io 11 import io
12 import errno 12 import errno
13 import logging 13 import logging
14 import subprocess 14 import subprocess
15 import urlparse 15 import urlparse
16 import argparse 16 import argparse
17 import json
17 18
18 from collections import OrderedDict 19 from collections import OrderedDict
19 from ConfigParser import RawConfigParser 20 from ConfigParser import RawConfigParser
20 21
21 USAGE = ''' 22 USAGE = '''
22 A dependencies file should look like this: 23 A dependencies file should look like this:
23 24
24 # VCS-specific root URLs for the repositories 25 # VCS-specific root URLs for the repositories
25 _root = hg:https://hg.adblockplus.org/ git:https://github.com/adblockplus/ 26 _root = hg:https://hg.adblockplus.org/ git:https://github.com/adblockplus/
26 # File to update this script from (optional) 27 # File to update this script from (optional)
(...skipping 211 matching lines...) Expand 10 before | Expand all | Expand 10 after
238 return os.path.join(path, *normpath.split(posixpath.sep)) 239 return os.path.join(path, *normpath.split(posixpath.sep))
239 240
240 241
241 def get_repo_type(repo): 242 def get_repo_type(repo):
242 for name, repotype in repo_types.iteritems(): 243 for name, repotype in repo_types.iteritems():
243 if repotype.istype(repo): 244 if repotype.istype(repo):
244 return name 245 return name
245 return 'hg' 246 return 'hg'
246 247
247 248
249 def resolve_npm_dependencies(target, vcs):
250 """Install Node.js production-only dependencies if necessary and desired.
251
252 When the target dependency has additional Node.js dependencies declared
253 and when those are outdated (or not installed), run
254 "npm install --only=production" to resolve the declared dependencies.
255
256 Additionally, make sure that any VCS will ignore the installed files
257 from any dependency.
258 Do nothing when the SKIP_DEPENDENCY_UPDATES environment variable is set.
Wladimir Palant 2017/08/24 11:38:12 SKIP_DEPENDENCY_UPDATES should be handled implicit
tlucas 2017/08/24 13:27:54 Acknowledged.
tlucas 2017/08/25 09:55:03 Done.
259
260 Requires Node.js to be installed locally.
261 """
262 try:
263 with open(os.path.join(target, 'package.json'), 'r') as fp:
264 package_data = json.load(fp)
265
266 # In case a package.json does not exist at all or if there are no
267 # production-dependencies declared, we don't need to run npm and can
Wladimir Palant 2017/08/24 11:38:12 Nit: "production dependencies"
tlucas 2017/08/24 13:27:54 Acknowledged.
tlucas 2017/08/25 09:55:03 Done.
268 # bail out early
269 if not package_data.get('dependencies', False):
270 return
271 except IOError:
272 return
273
274 try:
275 subprocess_kwargs = {
276 'stdout': subprocess.PIPE,
277 'stderr': subprocess.PIPE,
278 'cwd': target,
279 }
Wladimir Palant 2017/08/24 11:38:12 I'd rather have the arguments specified in the act
tlucas 2017/08/24 13:27:54 Acknowledged.
tlucas 2017/08/25 09:55:02 Done.
280
281 if subprocess.call(['npm', 'outdated'], **subprocess_kwargs) == 0:
Wladimir Palant 2017/08/24 11:38:12 Given that `npm outdated` hits the network, I'd ra
tlucas 2017/08/24 13:27:54 What do you think about looking for the desired de
Wladimir Palant 2017/08/25 08:10:42 I don't have a packages-lock.json file. There is p
Wladimir Palant 2017/08/25 08:16:58 And that reason is an outdated npm version (was ob
tlucas 2017/08/25 09:55:02 Fair enough - Done
282 return
283
284 if SKIP_DEPENDENCY_UPDATES:
285 logging.warning('SKIP_DEPENDENCY_UPDATES environment variable '
286 'set, not ensuring Node.js dependencies in %s',
287 target)
288 return
289
290 cmd = ['npm', 'install', '--only=production']
291 npm_process = subprocess.Popen(cmd, **subprocess_kwargs)
292 output, error = npm_process.communicate()
Wladimir Palant 2017/08/24 11:38:12 Better use subprocess.check_output() - this will s
tlucas 2017/08/24 13:27:54 Acknowledged, it should be check_output. Regardi
Wladimir Palant 2017/08/25 08:10:42 npm documentation could definitely be better, but
tlucas 2017/08/25 09:55:03 Yes, this is sufficient (as discussed in irc, stat
293 logging.info('Node.js: ' + output)
Wladimir Palant 2017/08/24 11:38:12 It's npm output, not Node.js. Besides, I doubt tha
tlucas 2017/08/24 13:27:54 Acknowledged.
tlucas 2017/08/25 09:55:02 Done.
294
295 if npm_process.returncode != 0:
296 raise Exception(error)
297
298 # Make sure Node.js related files / folders are ignored by the VCS in
299 # use
300 repo_types[vcs].ignore(
301 os.path.join(target, 'package-lock.json'), target
302 )
Wladimir Palant 2017/08/25 08:16:58 It seems that this file should indeed be committed
tlucas 2017/08/25 09:55:03 Done.
303 repo_types[vcs].ignore(
304 os.path.join(target, 'node_modules'), target
305 )
306 except OSError as e:
307 import errno
308 if e.errno == errno.ENOENT:
309 logging.error('Failed to install Node.js dependencies for %s,'
310 ' please ensure Node.js is installed.', target)
311 else:
312 raise
313
314
248 def ensure_repo(parentrepo, parenttype, target, type, root, sourcename): 315 def ensure_repo(parentrepo, parenttype, target, type, root, sourcename):
249 if os.path.exists(target): 316 if os.path.exists(target):
250 return 317 return
251 318
252 if SKIP_DEPENDENCY_UPDATES: 319 if SKIP_DEPENDENCY_UPDATES:
253 logging.warning('SKIP_DEPENDENCY_UPDATES environment variable set, ' 320 logging.warning('SKIP_DEPENDENCY_UPDATES environment variable set, '
254 '%s not cloned', target) 321 '%s not cloned', target)
255 return 322 return
256 323
257 postprocess_url = repo_types[type].postprocess_url 324 postprocess_url = repo_types[type].postprocess_url
(...skipping 57 matching lines...) Expand 10 before | Expand all | Expand 10 after
315 if key == parenttype or key is None and vcs != '*': 382 if key == parenttype or key is None and vcs != '*':
316 vcs = key 383 vcs = key
317 source, rev = merge_seqs(sources.get('*'), sources.get(vcs)) 384 source, rev = merge_seqs(sources.get('*'), sources.get(vcs))
318 385
319 if not (vcs and source and rev): 386 if not (vcs and source and rev):
320 logging.warning('No valid source / revision found to create %s' % ta rget) 387 logging.warning('No valid source / revision found to create %s' % ta rget)
321 continue 388 continue
322 389
323 ensure_repo(repodir, parenttype, target, vcs, _root.get(vcs, ''), source ) 390 ensure_repo(repodir, parenttype, target, vcs, _root.get(vcs, ''), source )
324 update_repo(target, vcs, rev) 391 update_repo(target, vcs, rev)
392 resolve_npm_dependencies(target, vcs)
Wladimir Palant 2017/08/24 11:38:12 This will be called every time, not merely when th
tlucas 2017/08/24 13:27:54 I thought about this too - but this would require
Wladimir Palant 2017/08/25 08:10:42 The way I see it, this wouldn't normally be an iss
tlucas 2017/08/25 09:55:03 Yes, that sounds reasonable. Done.
325 resolve_deps(target, level + 1, self_update=False, 393 resolve_deps(target, level + 1, self_update=False,
326 overrideroots=overrideroots, skipdependencies=skipdependenc ies) 394 overrideroots=overrideroots, skipdependencies=skipdependenc ies)
327 395
328 if self_update and '_self' in config and '*' in config['_self']: 396 if self_update and '_self' in config and '*' in config['_self']:
329 source = safe_join(repodir, config['_self']['*']) 397 source = safe_join(repodir, config['_self']['*'])
330 try: 398 try:
331 with io.open(source, 'rb') as handle: 399 with io.open(source, 'rb') as handle:
332 sourcedata = handle.read() 400 sourcedata = handle.read()
333 except IOError as e: 401 except IOError as e:
334 if e.errno != errno.ENOENT: 402 if e.errno != errno.ENOENT:
(...skipping 36 matching lines...) Expand 10 before | Expand all | Expand 10 after
371 args = parser.parse_args() 439 args = parser.parse_args()
372 440
373 if args.quiet: 441 if args.quiet:
374 logging.disable(logging.INFO) 442 logging.disable(logging.INFO)
375 443
376 repos = args.repos 444 repos = args.repos
377 if not len(repos): 445 if not len(repos):
378 repos = [os.path.dirname(__file__)] 446 repos = [os.path.dirname(__file__)]
379 for repo in repos: 447 for repo in repos:
380 resolve_deps(repo) 448 resolve_deps(repo)
OLDNEW
« no previous file with comments | « no previous file | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld