| OLD | NEW |
| 1 #!/usr/bin/env python | 1 #!/usr/bin/env python |
| 2 # Copyright 2016 The LUCI Authors. All rights reserved. | 2 # Copyright 2016 The LUCI Authors. All rights reserved. |
| 3 # Use of this source code is governed under the Apache License, Version 2.0 | 3 # Use of this source code is governed under the Apache License, Version 2.0 |
| 4 # that can be found in the LICENSE file. | 4 # that can be found in the LICENSE file. |
| 5 | 5 |
| 6 """Manages web/ resource checkout and building. | 6 """Manages web/ resource checkout and building. |
| 7 | 7 |
| 8 This script can be run in one of three modes: | 8 This script can be run in one of three modes: |
| 9 - As "initialize.py", it will perform resource dependency checkout for | 9 - As "initialize.py", it will perform resource dependency checkout for |
| 10 "luci_deploy" and quit. | 10 "luci_deploy" and quit. |
| 11 - As "build.py", it will build web apps to a "luci_deploy" directory. | 11 - As "build.py", it will build web apps to a "luci_deploy" directory. |
| 12 - As "web.py", it is a user-facing tool to manually build web components. | 12 - As "web.py", it is a user-facing tool to manually build web components. |
| 13 """ | 13 """ |
| 14 | 14 |
| 15 import argparse | 15 import argparse |
| 16 import itertools |
| 16 import logging | 17 import logging |
| 17 import os | 18 import os |
| 18 import pipes | 19 import pipes |
| 20 import shutil |
| 19 import subprocess | 21 import subprocess |
| 20 import sys | 22 import sys |
| 21 | 23 |
| 22 from distutils.spawn import find_executable | 24 from distutils.spawn import find_executable |
| 23 | 25 |
| 24 LOGGER = logging.getLogger('web.py') | 26 LOGGER = logging.getLogger('web.py') |
| 25 | 27 |
| 26 # The root of the "luci-go" checkout, relative to the current "build.py" file. | 28 # The root of the "luci-go" checkout, relative to the current "build.py" file. |
| 27 _LUCI_GO_ROOT = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) | 29 _LUCI_GO_ROOT = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) |
| 28 | 30 |
| (...skipping 27 matching lines...) Expand all Loading... |
| 56 raise Exception('Unable to locate Node.js installation.') | 58 raise Exception('Unable to locate Node.js installation.') |
| 57 tc = cls(web_dir, *node_js) | 59 tc = cls(web_dir, *node_js) |
| 58 | 60 |
| 59 # Install NPM deps from "package.json". | 61 # Install NPM deps from "package.json". |
| 60 def install_npm_deps(): | 62 def install_npm_deps(): |
| 61 tc.npm('install', cwd=web_dir) | 63 tc.npm('install', cwd=web_dir) |
| 62 cls._call_if_outdated( | 64 cls._call_if_outdated( |
| 63 install_npm_deps, | 65 install_npm_deps, |
| 64 os.path.join(web_dir, '.npm.installed'), | 66 os.path.join(web_dir, '.npm.installed'), |
| 65 os.path.join(web_dir, 'package.json'), | 67 os.path.join(web_dir, 'package.json'), |
| 68 [os.path.join(web_dir, 'node_modules')], |
| 66 force) | 69 force) |
| 67 | 70 |
| 68 # Install Bower deps from "bower.json". | 71 # Install Bower deps from "bower.json". |
| 69 def install_bower_deps(): | 72 def install_bower_deps(): |
| 70 tc.bower('install', cwd=web_dir) | 73 tc.bower('install', cwd=web_dir) |
| 71 cls._call_if_outdated( | 74 cls._call_if_outdated( |
| 72 install_bower_deps, | 75 install_bower_deps, |
| 73 os.path.join(web_dir, '.bower.installed'), | 76 os.path.join(web_dir, '.bower.installed'), |
| 74 os.path.join(web_dir, 'bower.json'), | 77 os.path.join(web_dir, 'bower.json'), |
| 78 [os.path.join(web_dir, 'inc', 'bower_components')], |
| 75 force) | 79 force) |
| 76 | 80 |
| 77 return tc | 81 return tc |
| 78 | 82 |
| 79 @staticmethod | 83 @staticmethod |
| 80 def _call_if_outdated(fn, manifest_path, defs_path, force): | 84 def _call_if_outdated(fn, manifest_path, defs_path, clean_paths, force): |
| 81 """Will call "fn" if the file at "install_path" doesn't match "spec". | 85 """Will call "fn" if the file at "install_path" doesn't match "spec". |
| 82 | 86 |
| 83 If "fn" completes without raising an exception, the "spec" file will be | 87 If "fn" completes without raising an exception, the "spec" file will be |
| 84 copied to the "installed" path, making subsequent runs of this function a | 88 copied to the "installed" path, making subsequent runs of this function a |
| 85 no-op until the spec file changes.. | 89 no-op until the spec file changes.. |
| 86 | 90 |
| 87 Args: | 91 Args: |
| 88 fn (callable): The function to call if they don't match. | 92 fn (callable): The function to call if they don't match. |
| 89 manifest_path (str): The path to the installed state file. | 93 manifest_path (str): The path to the installed state file. |
| 90 defs_path (str): The path to the source spec file. | 94 defs_path (str): The path to the source spec file. |
| 95 clean_paths (list): Path of destination files and directories to clean on |
| 96 reprovision. |
| 91 force (bool): If true, call the function regardless. | 97 force (bool): If true, call the function regardless. |
| 92 """ | 98 """ |
| 93 with open(defs_path, 'r') as fd: | 99 with open(defs_path, 'r') as fd: |
| 94 spec_data = fd.read() | 100 spec_data = fd.read() |
| 95 | 101 |
| 96 if not force and os.path.isfile(manifest_path): | 102 if not force and os.path.isfile(manifest_path): |
| 97 with open(manifest_path, 'r') as fd: | 103 with open(manifest_path, 'r') as fd: |
| 98 current = fd.read() | 104 current = fd.read() |
| 99 if spec_data == current: | 105 if spec_data == current: |
| 100 return | 106 return |
| 101 | 107 |
| 108 # Clean all paths. |
| 109 for path in itertools.chain(clean_paths, (manifest_path,)): |
| 110 if os.path.isdir(path): |
| 111 LOGGER.info('Purging directory on reprovision: %r', path) |
| 112 shutil.rmtree(path) |
| 113 elif os.path.isfile(path): |
| 114 LOGGER.info('Purging file on reprovision: %r', path) |
| 115 os.remove(path) |
| 116 |
| 102 # Either forcing or out of date. | 117 # Either forcing or out of date. |
| 103 fn() | 118 fn() |
| 104 | 119 |
| 105 # Update our installed file to match our spec data. | 120 # Update our installed file to match our spec data. |
| 106 with open(manifest_path, 'w') as fd: | 121 with open(manifest_path, 'w') as fd: |
| 107 fd.write(spec_data) | 122 fd.write(spec_data) |
| 108 | 123 |
| 109 @property | 124 @property |
| 110 def web_dir(self): | 125 def web_dir(self): |
| 111 return self._web_dir | 126 return self._web_dir |
| (...skipping 161 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 273 return _main_initialize(args) | 288 return _main_initialize(args) |
| 274 elif script_name == 'build.py': | 289 elif script_name == 'build.py': |
| 275 return _main_deploy(args) | 290 return _main_deploy(args) |
| 276 else: | 291 else: |
| 277 return _main(args) | 292 return _main(args) |
| 278 | 293 |
| 279 | 294 |
| 280 if __name__ == '__main__': | 295 if __name__ == '__main__': |
| 281 logging.basicConfig(level=logging.INFO) | 296 logging.basicConfig(level=logging.INFO) |
| 282 sys.exit(main(sys.argv)) | 297 sys.exit(main(sys.argv)) |
| OLD | NEW |