| #!/usr/bin/env python3 |
| # Copyright 2019 The ChromiumOS Authors |
| # Use of this source code is governed by a BSD-style license that can be |
| # found in the LICENSE file. |
| |
| """Run node with the right settings.""" |
| |
| import os |
| import platform |
| import shutil |
| import sys |
| |
| import libdot |
| |
| |
| # The hash of the node_modules that we maintain. |
| # Allow a long line for easy automated updating. |
| # pylint: disable=line-too-long |
| NODE_MODULES_HASH = ( |
| "a8883766ac35c101613cfb8a3b8a4e2813e409b7faa0703b3114c90e2ba4bac1" |
| ) |
| # pylint: enable=line-too-long |
| |
| # In sync with Chromium's DEPS file because it's easier to use something that |
| # already exists than maintain our own. |
| # Run `./node_sync_with_chromium` to update these settings. |
| NODE_VER = "20.11.0" |
| NODE_LINUX_HASH = "46795170ff5df9831955f163f6966abde581c8af" |
| NODE_MAC_HASH = "85ccc2202fd4f1615a443248c01a866ae227ba78" |
| NODE_WIN_HASH = "2cb36010af52bc5e2a2d1e3675c10361c80d8f8d" |
| |
| # Bucket maintained by Chromium. |
| # gsutil ls gs://chromium-nodejs/ |
| NODE_BASE_URI = "https://storage.googleapis.com/chromium-nodejs" |
| |
| # Bucket maintained by us. |
| NODE_MODULES_GS_FRAGMENT = "chromeos-localmirror/secureshell/distfiles" |
| NODE_MODULES_GS_URI = f"gs://{NODE_MODULES_GS_FRAGMENT}" |
| NODE_MODULES_BASE_URI = ( |
| f"https://storage.googleapis.com/{NODE_MODULES_GS_FRAGMENT}" |
| ) |
| |
| # The node_modules & node/npm paths. |
| NODE_MODULES_DIR = libdot.LIBAPPS_DIR / "node_modules" |
| NODE_BIN_DIR = NODE_MODULES_DIR / ".bin" |
| NODE = NODE_BIN_DIR / "node" |
| NPM = NODE_BIN_DIR / "npm" |
| # Use a dotdir as npm expects to manage everything under node_modules/. |
| NODE_DIR = NODE_MODULES_DIR / ".node" |
| |
| |
| def run(cmd, **kwargs): |
| """Run the node |cmd|. |
| |
| This assumes |cmd| is a node program. Different OS's might need to invoke |
| node differently, so wrap the calls here. |
| """ |
| # We need the node bin dir to be at the start of the $PATH as some packages |
| # will try to run other npm packages directly. |
| extra_env = kwargs.setdefault("extra_env", {}) |
| assert "PATH" not in extra_env |
| extra_env["PATH"] = os.pathsep.join((str(NODE_BIN_DIR), os.getenv("PATH"))) |
| return libdot.run( |
| cmd[1:], |
| cmd_prefix=[NODE, NODE_BIN_DIR / cmd[0]], |
| log_prefix=[cmd[0]], |
| **kwargs, |
| ) |
| |
| |
| def update(): |
| """Download & update our copy of node.""" |
| osname = platform.system() |
| if osname == "Linux": |
| node_hash = NODE_LINUX_HASH |
| elif osname == "Darwin": |
| node_hash = NODE_MAC_HASH |
| elif osname == "Windows": |
| node_hash = NODE_WIN_HASH |
| else: |
| raise RuntimeError(f"Unknown OS {osname}") |
| |
| # In case of an upgrade, nuke existing dir. |
| hash_file = NODE_DIR / node_hash |
| if not hash_file.exists(): |
| shutil.rmtree(NODE_DIR, ignore_errors=True) |
| |
| node = NODE |
| if osname == "Windows": |
| node = node.with_suffix(".exe") |
| if not node.exists(): |
| os.makedirs(NODE_BIN_DIR, exist_ok=True) |
| os.makedirs(NODE_DIR, exist_ok=True) |
| |
| # Download & unpack the archive. |
| uri = "/".join((NODE_BASE_URI, NODE_VER, node_hash)) |
| output = NODE_DIR / node_hash |
| libdot.fetch(uri, output) |
| if osname == "Windows": |
| libdot.unlink(node) |
| output.rename(node) |
| else: |
| libdot.unpack(output, cwd=NODE_DIR) |
| libdot.unlink(output) |
| |
| # Create canonical symlinks for node & npm. |
| path = next(NODE_DIR.glob("*/bin/node")) |
| libdot.symlink(path, NODE) |
| path = next(NODE_DIR.glob("*/*/node_modules/npm/bin/npm-cli.js")) |
| libdot.symlink(path, NPM) |
| |
| # Mark the hash of this checkout. |
| libdot.touch(hash_file) |
| |
| |
| def modules_update(): |
| """Download & update our copy of node_modules.""" |
| libdot.download_tarball( |
| f"node_modules-{NODE_MODULES_HASH}.tar.xz", |
| NODE_MODULES_BASE_URI, |
| NODE_MODULES_DIR, |
| NODE_MODULES_HASH, |
| ) |
| |
| |
| def main(argv): |
| """The main func!""" |
| libdot.setup_logging() |
| libdot.node_and_npm_setup() |
| os.execv(NODE, ["node"] + argv) |
| |
| |
| if __name__ == "__main__": |
| sys.exit(main(sys.argv[1:])) |