mirror of
				https://github.com/zulip/zulip.git
				synced 2025-10-31 12:03:46 +00:00 
			
		
		
		
	
		
			
				
	
	
		
			180 lines
		
	
	
		
			6.5 KiB
		
	
	
	
		
			Python
		
	
	
		
			Executable File
		
	
	
	
	
			
		
		
	
	
			180 lines
		
	
	
		
			6.5 KiB
		
	
	
	
		
			Python
		
	
	
		
			Executable File
		
	
	
	
	
| #!/usr/bin/env python3
 | |
| import argparse
 | |
| import json
 | |
| import os
 | |
| import subprocess
 | |
| import sys
 | |
| from typing import NoReturn
 | |
| 
 | |
| sys.path.insert(0, os.path.abspath(os.path.join(os.path.dirname(__file__), "..")))
 | |
| 
 | |
| os.chdir(os.path.join(os.path.dirname(__file__), "../web"))
 | |
| 
 | |
| 
 | |
| from scripts.lib.zulip_tools import get_config, get_config_file
 | |
| from version import ZULIP_VERSION
 | |
| 
 | |
| webpack_command = ["node", "../node_modules/webpack-cli/bin/cli.js"]
 | |
| 
 | |
| 
 | |
| def build_for_prod_or_puppeteer(quiet: bool, config_name: str | None = None) -> NoReturn:
 | |
|     """Builds for production, writing the output to disk"""
 | |
| 
 | |
|     with open("/proc/meminfo") as meminfo:
 | |
|         if int(next(meminfo).split()[1]) < 3 * 1024 * 1024:
 | |
|             os.environ["NODE_OPTIONS"] = "--max-old-space-size=1536"
 | |
|     webpack_args = [
 | |
|         *webpack_command,
 | |
|         "build",
 | |
|         "--disable-interpret",
 | |
|         "--mode=production",
 | |
|         f"--env=ZULIP_VERSION={ZULIP_VERSION}",
 | |
|     ]
 | |
|     if quiet:
 | |
|         webpack_args += ["--stats=errors-only"]
 | |
|     if config_name is not None:
 | |
|         webpack_args += [f"--config-name={config_name}"]
 | |
|     if "PUPPETEER_TESTS" in os.environ:
 | |
|         webpack_args.append("--env=puppeteer_tests")
 | |
|     else:
 | |
|         custom_5xx_file = get_config(get_config_file(), "application_server", "5xx_file")
 | |
|         if custom_5xx_file is not None:
 | |
|             webpack_args.append(f"--env=custom_5xx_file={custom_5xx_file}")
 | |
| 
 | |
|     # Silence warnings from "browserslist" about using old data; those
 | |
|     # warnings are only useful for development
 | |
|     os.environ["BROWSERSLIST_IGNORE_OLD_DATA"] = "1"
 | |
| 
 | |
|     os.execvp(webpack_args[0], webpack_args)
 | |
| 
 | |
| 
 | |
| def build_for_dev_server(host: str, port: str, minify: bool, disable_host_check: bool) -> None:
 | |
|     """watches and rebuilds on changes, serving files from memory via webpack-dev-server"""
 | |
| 
 | |
|     # This is our most dynamic configuration, which we use for our
 | |
|     # dev server.  The key piece here is that we identify changes to
 | |
|     # files as devs make edits and serve new assets on the fly.
 | |
|     webpack_args = [
 | |
|         *webpack_command,
 | |
|         "serve",
 | |
|         "--disable-interpret",
 | |
|         # webpack-cli has a bug where it ignores --watch-poll with
 | |
|         # multi-config, and we don't need the katex part anyway.
 | |
|         "--config-name=frontend",
 | |
|         f"--host={host}",
 | |
|         f"--port={port}",
 | |
|         # We add the hot flag using the cli because it takes care
 | |
|         # of addition to entry points and adding the plugin
 | |
|         # automatically
 | |
|         "--hot",
 | |
|     ]
 | |
|     if minify:
 | |
|         webpack_args.append("--env=minimize")
 | |
|     if disable_host_check:
 | |
|         webpack_args.append("--allowed-hosts=all")
 | |
|     else:
 | |
|         webpack_args.append(f"--allowed-hosts={host},.zulipdev.com,.zulipdev.org")
 | |
| 
 | |
|     # Tell webpack-dev-server to fall back to periodic polling on
 | |
|     # filesystems where inotify is known to be broken.
 | |
|     cwd = os.getcwd()
 | |
|     inotify_broken = False
 | |
|     with open("/proc/self/mounts") as mounts:
 | |
|         for line in mounts:
 | |
|             fields = line.split()
 | |
|             if fields[1] == cwd:
 | |
|                 inotify_broken = fields[2] in ["nfs", "vboxsf", "prl_fs"]
 | |
|     if inotify_broken:
 | |
|         os.environ["CHOKIDAR_USEPOLLING"] = "1"
 | |
|         os.environ["CHOKIDAR_INTERVAL"] = "1000"
 | |
| 
 | |
|     # TODO: This process should also fall back to periodic polling if
 | |
|     # inotify_broken.
 | |
|     import pyinotify
 | |
| 
 | |
|     try:
 | |
|         webpack_process = subprocess.Popen(webpack_args)
 | |
| 
 | |
|         class WebpackConfigFileChangeHandler(pyinotify.ProcessEvent):
 | |
|             def process_default(self, event: pyinotify.Event) -> None:
 | |
|                 nonlocal webpack_process
 | |
|                 print("Restarting webpack-dev-server due to config changes...")
 | |
|                 webpack_process.terminate()
 | |
|                 webpack_process.wait()
 | |
|                 webpack_process = subprocess.Popen(webpack_args)
 | |
| 
 | |
|         watch_manager = pyinotify.WatchManager()
 | |
|         event_notifier = pyinotify.Notifier(watch_manager, WebpackConfigFileChangeHandler())
 | |
|         # We did a chdir to the root of the Zulip checkout above.
 | |
|         for filepath in [
 | |
|             "webpack.config.ts",
 | |
|             "webpack.assets.json",
 | |
|             "webpack.dev-assets.json",
 | |
|         ]:
 | |
|             watch_manager.add_watch(filepath, pyinotify.IN_MODIFY)
 | |
|         event_notifier.loop()
 | |
|     finally:
 | |
|         webpack_process.terminate()
 | |
|         webpack_process.wait()
 | |
| 
 | |
| 
 | |
| def build_for_most_tests() -> None:
 | |
|     """Generates a stub asset stat file for django so backend test can render a page"""
 | |
| 
 | |
|     # Tests like test-backend, test-api, and test-home-documentation use
 | |
|     # our "test" configuration.  The one exception is Puppeteer, which uses
 | |
|     # our production configuration.
 | |
|     #
 | |
|     # It's not completely clear why we don't also use the same
 | |
|     # configuration for ALL tests, but figuring out the full history here
 | |
|     # was out of the scope of the effort here to add some comments and
 | |
|     # clean up names.
 | |
|     with open("webpack.assets.json") as json_data:
 | |
|         entries = json.load(json_data)
 | |
| 
 | |
|     with open("webpack.dev-assets.json") as json_data:
 | |
|         entries.update(json.load(json_data))
 | |
| 
 | |
|     stat_data = {
 | |
|         "status": "done",
 | |
|         "chunks": {entry: [f"{entry}-stubentry.js"] for entry in entries},
 | |
|     }
 | |
|     directory = "../var"
 | |
|     if not os.path.exists(directory):
 | |
|         os.makedirs(directory)
 | |
|     with open(os.path.join(directory, "webpack-stats-test.json"), "w") as outfile:
 | |
|         json.dump(stat_data, outfile)
 | |
| 
 | |
| 
 | |
| parser = argparse.ArgumentParser()
 | |
| parser.add_argument(
 | |
|     "--test",
 | |
|     action="store_true",
 | |
|     help="generate a stub webpack-stats.json file (for backend testing)",
 | |
| )
 | |
| parser.add_argument("--quiet", action="store_true", help="Minimizes webpack output while running")
 | |
| parser.add_argument(
 | |
|     "--watch", action="store_true", help="watch for changes to source files (for development)"
 | |
| )
 | |
| parser.add_argument(
 | |
|     "--host", default="127.0.0.1", help="set the host for the webpack server to run on"
 | |
| )
 | |
| parser.add_argument("--port", default="9994", help="set the port for the webpack server to run on")
 | |
| parser.add_argument(
 | |
|     "--minify", action="store_true", help="Minify and optimize the assets (for development)"
 | |
| )
 | |
| parser.add_argument(
 | |
|     "--disable-host-check", action="store_true", help="Disable host check for webpack-dev-server"
 | |
| )
 | |
| parser.add_argument("--config-name", help="Limit production building to only one config-name")
 | |
| 
 | |
| args = parser.parse_args()
 | |
| if "PUPPETEER_TESTS" in os.environ:
 | |
|     build_for_prod_or_puppeteer(args.quiet)
 | |
| elif args.test:
 | |
|     build_for_most_tests()
 | |
| elif args.watch:
 | |
|     build_for_dev_server(args.host, args.port, args.minify, args.disable_host_check)
 | |
| else:
 | |
|     build_for_prod_or_puppeteer(args.quiet, args.config_name)
 |