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

Delta Between Two Patch Sets: ensure_dependencies.py

Issue 29526588: Issue 5559 - include Node.js in ensure_dependencies.py (Closed)
Left Patch Set: Created Aug. 24, 2017, 11:04 a.m.
Right Patch Set: Created Aug. 28, 2017, 6:50 a.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 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
(...skipping 232 matching lines...) Expand 10 before | Expand all | Expand 10 after
243 for name, repotype in repo_types.iteritems(): 243 for name, repotype in repo_types.iteritems():
244 if repotype.istype(repo): 244 if repotype.istype(repo):
245 return name 245 return name
246 return 'hg' 246 return 'hg'
247 247
248 248
249 def resolve_npm_dependencies(target, vcs): 249 def resolve_npm_dependencies(target, vcs):
250 """Install Node.js production-only dependencies if necessary and desired. 250 """Install Node.js production-only dependencies if necessary and desired.
251 251
252 When the target dependency has additional Node.js dependencies declared 252 When the target dependency has additional Node.js dependencies declared
253 and when those are outdated (or not installed), run 253 run "npm install --only=production --loglevel=warn" to resolve the declared
254 "npm install --only=production" to resolve the declared dependencies. 254 dependencies.
255 255
256 Additionally, make sure that any VCS will ignore the installed files 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 257
260 Requires Node.js to be installed locally. 258 Requires Node.js to be installed locally.
261 """ 259 """
262 try: 260 try:
263 with open(os.path.join(target, 'package.json'), 'r') as fp: 261 with open(os.path.join(target, 'package.json'), 'r') as fp:
264 package_data = json.load(fp) 262 package_data = json.load(fp)
265 263
266 # In case a package.json does not exist at all or if there are no 264 # 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 265 # 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 266 # bail out early.
269 if not package_data.get('dependencies', False): 267 if not package_data.get('dependencies', False):
270 return 268 return
271 except IOError: 269 except IOError:
272 return 270 return
273 271
274 try: 272 try:
275 subprocess_kwargs = { 273 cmd = ['npm', 'install', '--only=production', '--loglevel=warn']
276 'stdout': subprocess.PIPE, 274 subprocess.check_output(cmd, cwd=target)
277 'stderr': subprocess.PIPE, 275
278 'cwd': target, 276 repo_types[vcs].ignore(os.path.join(target, 'node_modules'), 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: 277 except OSError as e:
307 import errno 278 import errno
308 if e.errno == errno.ENOENT: 279 if e.errno == errno.ENOENT:
309 logging.error('Failed to install Node.js dependencies for %s,' 280 logging.error('Failed to install Node.js dependencies for %s,'
310 ' please ensure Node.js is installed.', target) 281 ' please ensure Node.js is installed.', target)
311 else: 282 else:
312 raise 283 raise
313 284
314 285
315 def ensure_repo(parentrepo, parenttype, target, type, root, sourcename): 286 def ensure_repo(parentrepo, parenttype, target, type, root, sourcename):
316 if os.path.exists(target): 287 if os.path.exists(target):
317 return 288 return False
318 289
319 if SKIP_DEPENDENCY_UPDATES: 290 if SKIP_DEPENDENCY_UPDATES:
320 logging.warning('SKIP_DEPENDENCY_UPDATES environment variable set, ' 291 logging.warning('SKIP_DEPENDENCY_UPDATES environment variable set, '
321 '%s not cloned', target) 292 '%s not cloned', target)
322 return 293 return False
323 294
324 postprocess_url = repo_types[type].postprocess_url 295 postprocess_url = repo_types[type].postprocess_url
325 root = postprocess_url(root) 296 root = postprocess_url(root)
326 sourcename = postprocess_url(sourcename) 297 sourcename = postprocess_url(sourcename)
327 298
328 if os.path.exists(root): 299 if os.path.exists(root):
329 url = os.path.join(root, sourcename) 300 url = os.path.join(root, sourcename)
330 else: 301 else:
331 url = urlparse.urljoin(root, sourcename) 302 url = urlparse.urljoin(root, sourcename)
332 303
333 logging.info('Cloning repository %s into %s' % (url, target)) 304 logging.info('Cloning repository %s into %s' % (url, target))
334 repo_types[type].clone(url, target) 305 repo_types[type].clone(url, target)
335 repo_types[parenttype].ignore(target, parentrepo) 306 repo_types[parenttype].ignore(target, parentrepo)
307 return True
336 308
337 309
338 def update_repo(target, type, revision): 310 def update_repo(target, type, revision):
339 resolved_revision = repo_types[type].get_revision_id(target, revision) 311 resolved_revision = repo_types[type].get_revision_id(target, revision)
340 current_revision = repo_types[type].get_revision_id(target) 312 current_revision = repo_types[type].get_revision_id(target)
341 313
342 if resolved_revision != current_revision: 314 if resolved_revision != current_revision:
343 if SKIP_DEPENDENCY_UPDATES: 315 if SKIP_DEPENDENCY_UPDATES:
344 logging.warning('SKIP_DEPENDENCY_UPDATES environment variable set, ' 316 logging.warning('SKIP_DEPENDENCY_UPDATES environment variable set, '
345 '%s not checked out to %s', target, revision) 317 '%s not checked out to %s', target, revision)
346 return 318 return False
347 319
348 if not resolved_revision: 320 if not resolved_revision:
349 logging.info('Revision %s is unknown, downloading remote changes' % revision) 321 logging.info('Revision %s is unknown, downloading remote changes' % revision)
350 repo_types[type].pull(target) 322 repo_types[type].pull(target)
351 resolved_revision = repo_types[type].get_revision_id(target, revisio n) 323 resolved_revision = repo_types[type].get_revision_id(target, revisio n)
352 if not resolved_revision: 324 if not resolved_revision:
353 raise Exception('Failed to resolve revision %s' % revision) 325 raise Exception('Failed to resolve revision %s' % revision)
354 326
355 logging.info('Updating repository %s to revision %s' % (target, resolved _revision)) 327 logging.info('Updating repository %s to revision %s' % (target, resolved _revision))
356 repo_types[type].update(target, resolved_revision, revision) 328 repo_types[type].update(target, resolved_revision, revision)
329 return True
330 return False
357 331
358 332
359 def resolve_deps(repodir, level=0, self_update=True, overrideroots=None, skipdep endencies=set()): 333 def resolve_deps(repodir, level=0, self_update=True, overrideroots=None, skipdep endencies=set()):
360 config = read_deps(repodir) 334 config = read_deps(repodir)
361 if config is None: 335 if config is None:
362 if level == 0: 336 if level == 0:
363 logging.warning('No dependencies file in directory %s, nothing to do ...\n%s' % (repodir, USAGE)) 337 logging.warning('No dependencies file in directory %s, nothing to do ...\n%s' % (repodir, USAGE))
364 return 338 return
365 if level >= 10: 339 if level >= 10:
366 logging.warning('Too much subrepository nesting, ignoring %s' % repo) 340 logging.warning('Too much subrepository nesting, ignoring %s' % repo)
(...skipping 13 matching lines...) Expand all
380 354
381 for key in sources.keys() + _root.keys(): 355 for key in sources.keys() + _root.keys():
382 if key == parenttype or key is None and vcs != '*': 356 if key == parenttype or key is None and vcs != '*':
383 vcs = key 357 vcs = key
384 source, rev = merge_seqs(sources.get('*'), sources.get(vcs)) 358 source, rev = merge_seqs(sources.get('*'), sources.get(vcs))
385 359
386 if not (vcs and source and rev): 360 if not (vcs and source and rev):
387 logging.warning('No valid source / revision found to create %s' % ta rget) 361 logging.warning('No valid source / revision found to create %s' % ta rget)
388 continue 362 continue
389 363
390 ensure_repo(repodir, parenttype, target, vcs, _root.get(vcs, ''), source ) 364 repo_cloned = ensure_repo(repodir, parenttype, target, vcs,
391 update_repo(target, vcs, rev) 365 _root.get(vcs, ''), source)
392 resolve_npm_dependencies(target, vcs) 366 repo_updated = update_repo(target, vcs, rev)
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.
367 if repo_cloned or repo_updated:
368 resolve_npm_dependencies(target, vcs)
393 resolve_deps(target, level + 1, self_update=False, 369 resolve_deps(target, level + 1, self_update=False,
394 overrideroots=overrideroots, skipdependencies=skipdependenc ies) 370 overrideroots=overrideroots, skipdependencies=skipdependenc ies)
395 371
396 if self_update and '_self' in config and '*' in config['_self']: 372 if self_update and '_self' in config and '*' in config['_self']:
397 source = safe_join(repodir, config['_self']['*']) 373 source = safe_join(repodir, config['_self']['*'])
398 try: 374 try:
399 with io.open(source, 'rb') as handle: 375 with io.open(source, 'rb') as handle:
400 sourcedata = handle.read() 376 sourcedata = handle.read()
401 except IOError as e: 377 except IOError as e:
402 if e.errno != errno.ENOENT: 378 if e.errno != errno.ENOENT:
(...skipping 36 matching lines...) Expand 10 before | Expand all | Expand 10 after
439 args = parser.parse_args() 415 args = parser.parse_args()
440 416
441 if args.quiet: 417 if args.quiet:
442 logging.disable(logging.INFO) 418 logging.disable(logging.INFO)
443 419
444 repos = args.repos 420 repos = args.repos
445 if not len(repos): 421 if not len(repos):
446 repos = [os.path.dirname(__file__)] 422 repos = [os.path.dirname(__file__)]
447 for repo in repos: 423 for repo in repos:
448 resolve_deps(repo) 424 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