Liviu Rau | 3f14624 | 2022-02-23 11:48:28 | [diff] [blame] | 1 | #!/usr/bin/env vpython3 |
Mathias Bynens | 8555df0 | 2020-04-27 14:19:06 | [diff] [blame] | 2 | # |
Yang Guo | 4fd355c | 2019-09-19 08:59:03 | [diff] [blame] | 3 | # Copyright 2019 The Chromium Authors. All rights reserved. |
| 4 | # Use of this source code is governed by a BSD-style license that can be |
| 5 | # found in the LICENSE file. |
| 6 | """ |
Alex Rudenko | 2e1b8ab | 2024-04-16 19:23:32 | [diff] [blame] | 7 | Use this script to update node_modules instead of |
Peter Marshall | 9c4cbce | 2020-07-06 07:36:54 | [diff] [blame] | 8 | running npm install manually. To upgrade a dependency, change the version |
Alex Rudenko | 2e1b8ab | 2024-04-16 19:23:32 | [diff] [blame] | 9 | number in package.json below and run this script, which can be done with `npm run |
Jack Franklin | cc58dfa | 2022-11-29 10:08:14 | [diff] [blame] | 10 | install-deps` locally. |
Yang Guo | 4fd355c | 2019-09-19 08:59:03 | [diff] [blame] | 11 | """ |
| 12 | |
| 13 | import os |
| 14 | import os.path as path |
Yang Guo | 4fd355c | 2019-09-19 08:59:03 | [diff] [blame] | 15 | import json |
Yang Guo | 9117835 | 2019-10-31 07:50:19 | [diff] [blame] | 16 | import subprocess |
| 17 | import sys |
Tim van der Lippe | 6d109a9 | 2021-02-16 16:00:32 | [diff] [blame] | 18 | from collections import OrderedDict |
Yang Guo | 9117835 | 2019-10-31 07:50:19 | [diff] [blame] | 19 | |
| 20 | scripts_path = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) |
| 21 | sys.path.append(scripts_path) |
| 22 | |
| 23 | import devtools_paths |
Yang Guo | 4fd355c | 2019-09-19 08:59:03 | [diff] [blame] | 24 | |
Paul Lewis | e184c4c | 2019-12-02 12:30:15 | [diff] [blame] | 25 | LICENSES = [ |
| 26 | "MIT", |
| 27 | "Apache-2.0", |
| 28 | "BSD", |
| 29 | "BSD-2-Clause", |
| 30 | "BSD-3-Clause", |
| 31 | "CC0-1.0", |
| 32 | "CC-BY-3.0", |
Mathias Bynens | 79e2cf0 | 2020-05-29 14:46:17 | [diff] [blame] | 33 | "CC-BY-4.0", |
Paul Lewis | e184c4c | 2019-12-02 12:30:15 | [diff] [blame] | 34 | "ISC", |
Tim van der Lippe | 1fe7f95 | 2021-01-05 12:41:06 | [diff] [blame] | 35 | "MPL-2.0", |
Tim van der Lippe | 6d109a9 | 2021-02-16 16:00:32 | [diff] [blame] | 36 | "Python-2.0", |
Johan Bay | 0c881d5 | 2022-02-01 19:42:44 | [diff] [blame] | 37 | "W3C", |
Paul Lewis | e184c4c | 2019-12-02 12:30:15 | [diff] [blame] | 38 | ] |
| 39 | |
Alex Rudenko | d04dd45 | 2024-02-27 08:25:35 | [diff] [blame] | 40 | |
Tim van der Lippe | 6d109a9 | 2021-02-16 16:00:32 | [diff] [blame] | 41 | def load_json_file(location): |
| 42 | # By default, json load uses a standard Python dictionary, which is not ordered. |
| 43 | # To prevent subsequent invocations of this script to erroneously alter the order |
| 44 | # of keys defined in package.json files, we should use an `OrderedDict`. This |
| 45 | # ensures not only that we use a strict ordering, it will also make sure we maintain |
| 46 | # the order defined by the NPM packages themselves. That in turn is important, since |
| 47 | # NPM packages can define `exports`, where the order of entrypoints is crucial for |
| 48 | # how an NPM package is loaded. If you would change the order, it could break loading |
| 49 | # that package. |
| 50 | return json.load(location, object_pairs_hook=OrderedDict) |
| 51 | |
Alex Rudenko | d04dd45 | 2024-02-27 08:25:35 | [diff] [blame] | 52 | |
Alex Rudenko | 2e05f8f | 2024-04-04 10:36:59 | [diff] [blame] | 53 | DEPS = {} |
| 54 | |
Philip Pfaffe | 3b20821 | 2024-07-31 07:59:16 | [diff] [blame] | 55 | pkg_file = path.join(devtools_paths.devtools_root_path(), 'package.json') |
Alex Rudenko | 2e05f8f | 2024-04-04 10:36:59 | [diff] [blame] | 56 | with open(pkg_file, 'r+') as pkg_file: |
| 57 | DEPS = load_json_file(pkg_file)["devDependencies"] |
| 58 | |
Alex Rudenko | 2e05f8f | 2024-04-04 10:36:59 | [diff] [blame] | 59 | |
Paul Lewis | 66e1206 | 2019-12-02 12:04:54 | [diff] [blame] | 60 | def exec_command(cmd): |
Yang Guo | 4fd355c | 2019-09-19 08:59:03 | [diff] [blame] | 61 | try: |
Peter Marshall | 9c4cbce | 2020-07-06 07:36:54 | [diff] [blame] | 62 | new_env = os.environ.copy() |
Philip Pfaffe | 3b20821 | 2024-07-31 07:59:16 | [diff] [blame] | 63 | cmd_proc_result = subprocess.check_call( |
| 64 | cmd, cwd=devtools_paths.devtools_root_path(), env=new_env) |
Tim van der Lippe | 35cca41 | 2020-04-06 12:03:38 | [diff] [blame] | 65 | except Exception as error: |
| 66 | print(error) |
Paul Lewis | 66e1206 | 2019-12-02 12:04:54 | [diff] [blame] | 67 | return True |
| 68 | |
| 69 | return False |
Yang Guo | 4fd355c | 2019-09-19 08:59:03 | [diff] [blame] | 70 | |
| 71 | |
Paul Lewis | e184c4c | 2019-12-02 12:30:15 | [diff] [blame] | 72 | def ensure_licenses(): |
| 73 | cmd = [ |
| 74 | devtools_paths.node_path(), |
Alex Rudenko | d04dd45 | 2024-02-27 08:25:35 | [diff] [blame] | 75 | devtools_paths.license_checker_path(), '--onlyAllow', |
Paul Lewis | e184c4c | 2019-12-02 12:30:15 | [diff] [blame] | 76 | ('%s' % (';'.join(LICENSES))) |
| 77 | ] |
| 78 | |
| 79 | return exec_command(cmd) |
| 80 | |
| 81 | |
Yang Guo | 4fd355c | 2019-09-19 08:59:03 | [diff] [blame] | 82 | def strip_private_fields(): |
| 83 | # npm adds private fields which need to be stripped. |
Yang Guo | 4fd355c | 2019-09-19 08:59:03 | [diff] [blame] | 84 | packages = [] |
Liviu Rau | 3f14624 | 2022-02-23 11:48:28 | [diff] [blame] | 85 | for root, _, filenames in os.walk(devtools_paths.node_modules_path()): |
| 86 | if 'package.json' in filenames: |
| 87 | packages.append(path.join(root, 'package.json')) |
Yang Guo | 4fd355c | 2019-09-19 08:59:03 | [diff] [blame] | 88 | |
| 89 | for pkg in packages: |
| 90 | with open(pkg, 'r+') as pkg_file: |
Yang Guo | 4fd355c | 2019-09-19 08:59:03 | [diff] [blame] | 91 | try: |
Tim van der Lippe | 6d109a9 | 2021-02-16 16:00:32 | [diff] [blame] | 92 | pkg_data = load_json_file(pkg_file) |
Tim van der Lippe | ece3c51 | 2022-03-08 15:07:28 | [diff] [blame] | 93 | updated_pkg_data = pkg_data.copy() |
Yang Guo | 4fd355c | 2019-09-19 08:59:03 | [diff] [blame] | 94 | |
| 95 | # Remove anything that begins with an underscore, as these are |
| 96 | # the private fields in a package.json |
| 97 | for key in pkg_data.keys(): |
Liviu Rau | 3f14624 | 2022-02-23 11:48:28 | [diff] [blame] | 98 | if key.find('_') == 0: |
Tim van der Lippe | ece3c51 | 2022-03-08 15:07:28 | [diff] [blame] | 99 | updated_pkg_data.pop(key) |
Yang Guo | 4fd355c | 2019-09-19 08:59:03 | [diff] [blame] | 100 | |
| 101 | pkg_file.truncate(0) |
| 102 | pkg_file.seek(0) |
Alex Rudenko | d04dd45 | 2024-02-27 08:25:35 | [diff] [blame] | 103 | json.dump(updated_pkg_data, |
| 104 | pkg_file, |
| 105 | indent=2, |
| 106 | separators=(',', ': ')) |
Mathias Bynens | 8604a98 | 2020-06-23 06:41:44 | [diff] [blame] | 107 | pkg_file.write('\n') |
Yang Guo | 4fd355c | 2019-09-19 08:59:03 | [diff] [blame] | 108 | except: |
Tim van der Lippe | ece3c51 | 2022-03-08 15:07:28 | [diff] [blame] | 109 | print('Unable to fix: %s, %s' % (pkg, sys.exc_info())) |
Yang Guo | 4fd355c | 2019-09-19 08:59:03 | [diff] [blame] | 110 | return True |
| 111 | |
| 112 | return False |
| 113 | |
| 114 | |
Paul Lewis | 75090cf | 2019-10-25 13:13:11 | [diff] [blame] | 115 | def remove_package_json_entries(): |
| 116 | with open(devtools_paths.package_json_path(), 'r+') as pkg_file: |
| 117 | try: |
Tim van der Lippe | 6d109a9 | 2021-02-16 16:00:32 | [diff] [blame] | 118 | pkg_data = load_json_file(pkg_file) |
Paul Lewis | 75090cf | 2019-10-25 13:13:11 | [diff] [blame] | 119 | |
| 120 | # Remove the dependencies and devDependencies from the root package.json |
| 121 | # so that they can't be used to overwrite the node_modules managed by this file. |
| 122 | for key in pkg_data.keys(): |
Alex Rudenko | 2e05f8f | 2024-04-04 10:36:59 | [diff] [blame] | 123 | if key.find('dependencies') == 0: |
Paul Lewis | 75090cf | 2019-10-25 13:13:11 | [diff] [blame] | 124 | pkg_data.pop(key) |
| 125 | |
| 126 | pkg_file.truncate(0) |
| 127 | pkg_file.seek(0) |
Tim van der Lippe | 6d109a9 | 2021-02-16 16:00:32 | [diff] [blame] | 128 | json.dump(pkg_data, pkg_file, indent=2, separators=(',', ': ')) |
Mathias Bynens | 8604a98 | 2020-06-23 06:41:44 | [diff] [blame] | 129 | pkg_file.write('\n') |
Paul Lewis | 75090cf | 2019-10-25 13:13:11 | [diff] [blame] | 130 | except: |
| 131 | print('Unable to fix: %s' % pkg) |
| 132 | return True |
| 133 | return False |
| 134 | |
| 135 | |
Tim van der Lippe | 1511efc | 2020-03-20 11:57:11 | [diff] [blame] | 136 | def addClangFormat(): |
Alex Rudenko | d04dd45 | 2024-02-27 08:25:35 | [diff] [blame] | 137 | with open(path.join(devtools_paths.node_modules_path(), '.clang-format'), |
| 138 | 'w+') as clang_format_file: |
Tim van der Lippe | 1511efc | 2020-03-20 11:57:11 | [diff] [blame] | 139 | try: |
Tim van der Lippe | 6541ce6 | 2020-10-28 10:24:26 | [diff] [blame] | 140 | clang_format_file.write('DisableFormat: true\n') |
Tim van der Lippe | 1511efc | 2020-03-20 11:57:11 | [diff] [blame] | 141 | except: |
| 142 | print('Unable to write .clang-format file') |
| 143 | return True |
| 144 | return False |
| 145 | |
| 146 | |
Tim van der Lippe | d79d79b | 2020-05-27 14:55:46 | [diff] [blame] | 147 | def addOwnersFile(): |
| 148 | with open(path.join(devtools_paths.node_modules_path(), 'OWNERS'), |
| 149 | 'w+') as owners_file: |
| 150 | try: |
Tim van der Lippe | 2e14387 | 2021-04-08 11:56:40 | [diff] [blame] | 151 | owners_file.write('file://config/owner/INFRA_OWNERS\n') |
Tim van der Lippe | d79d79b | 2020-05-27 14:55:46 | [diff] [blame] | 152 | except: |
| 153 | print('Unable to write OWNERS file') |
| 154 | return True |
| 155 | return False |
| 156 | |
Alex Rudenko | d04dd45 | 2024-02-27 08:25:35 | [diff] [blame] | 157 | |
Tim van der Lippe | 6541ce6 | 2020-10-28 10:24:26 | [diff] [blame] | 158 | def addChromiumReadme(): |
| 159 | with open(path.join(devtools_paths.node_modules_path(), 'README.chromium'), |
| 160 | 'w+') as readme_file: |
| 161 | try: |
Alex Rudenko | d04dd45 | 2024-02-27 08:25:35 | [diff] [blame] | 162 | readme_file.write( |
| 163 | 'This directory hosts all packages downloaded from NPM that are used in either the build system or infrastructure scripts.\n' |
| 164 | ) |
| 165 | readme_file.write( |
| 166 | 'If you want to make any changes to this directory, please see "scripts/deps/manage_node_deps.py".\n' |
| 167 | ) |
Tim van der Lippe | 6541ce6 | 2020-10-28 10:24:26 | [diff] [blame] | 168 | except: |
| 169 | print('Unable to write README.chromium file') |
| 170 | return True |
| 171 | return False |
| 172 | |
Tim van der Lippe | d79d79b | 2020-05-27 14:55:46 | [diff] [blame] | 173 | |
Alex Rudenko | 2e1b8ab | 2024-04-16 19:23:32 | [diff] [blame] | 174 | def run_npm_command(): |
Paul Lewis | 66e1206 | 2019-12-02 12:04:54 | [diff] [blame] | 175 | for (name, version) in DEPS.items(): |
Liviu Rau | 3f14624 | 2022-02-23 11:48:28 | [diff] [blame] | 176 | if (version.find('^') == 0): |
Alex Rudenko | d04dd45 | 2024-02-27 08:25:35 | [diff] [blame] | 177 | print( |
| 178 | 'Versions must be locked to a specific version; remove ^ from the start of the version.' |
| 179 | ) |
Paul Lewis | 66e1206 | 2019-12-02 12:04:54 | [diff] [blame] | 180 | return True |
Yang Guo | 4fd355c | 2019-09-19 08:59:03 | [diff] [blame] | 181 | |
Alex Rudenko | 2e1b8ab | 2024-04-16 19:23:32 | [diff] [blame] | 182 | # Modern npm versions do not cause extra updates so it is not necessary to run clean install. |
| 183 | if exec_command([ |
| 184 | 'npm', |
| 185 | 'install', |
| 186 | ]): |
Paul Lewis | d903909 | 2019-11-27 17:06:23 | [diff] [blame] | 187 | return True |
| 188 | |
Alex Rudenko | 2e1b8ab | 2024-04-16 19:23:32 | [diff] [blame] | 189 | # To minimize disk usage for Chrome DevTools node_modules, always try to dedupe dependencies. |
| 190 | # We need to perform this every time, as the order of dependencies added could lead to a |
| 191 | # non-optimal dependency tree, resulting in unnecessary disk usage. |
| 192 | if exec_command([ |
| 193 | 'npm', |
| 194 | 'dedupe', |
| 195 | ]): |
| 196 | return True |
Tim van der Lippe | 35cca41 | 2020-04-06 12:03:38 | [diff] [blame] | 197 | |
| 198 | if remove_package_json_entries(): |
| 199 | return True |
| 200 | |
Paul Lewis | 66e1206 | 2019-12-02 12:04:54 | [diff] [blame] | 201 | if strip_private_fields(): |
Paul Lewis | 75090cf | 2019-10-25 13:13:11 | [diff] [blame] | 202 | return True |
| 203 | |
Tim van der Lippe | 1511efc | 2020-03-20 11:57:11 | [diff] [blame] | 204 | if addClangFormat(): |
| 205 | return True |
| 206 | |
Tim van der Lippe | d79d79b | 2020-05-27 14:55:46 | [diff] [blame] | 207 | if addOwnersFile(): |
| 208 | return True |
| 209 | |
Tim van der Lippe | 6541ce6 | 2020-10-28 10:24:26 | [diff] [blame] | 210 | if addChromiumReadme(): |
| 211 | return True |
| 212 | |
Paul Lewis | e184c4c | 2019-12-02 12:30:15 | [diff] [blame] | 213 | return ensure_licenses() |
Yang Guo | 4fd355c | 2019-09-19 08:59:03 | [diff] [blame] | 214 | |
| 215 | |
Alex Rudenko | 2e1b8ab | 2024-04-16 19:23:32 | [diff] [blame] | 216 | npm_errors_found = run_npm_command() |
Yang Guo | 4fd355c | 2019-09-19 08:59:03 | [diff] [blame] | 217 | |
| 218 | if npm_errors_found: |
Tim van der Lippe | 35cca41 | 2020-04-06 12:03:38 | [diff] [blame] | 219 | print('npm command failed') |
| 220 | exit(1) |