180 lines
		
	
	
		
			5.2 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
		
		
			
		
	
	
			180 lines
		
	
	
		
			5.2 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
| 
								 | 
							
								import sys
							 | 
						||
| 
								 | 
							
								import subprocess
							 | 
						||
| 
								 | 
							
								import shlex
							 | 
						||
| 
								 | 
							
								import os
							 | 
						||
| 
								 | 
							
								import argparse
							 | 
						||
| 
								 | 
							
								import shutil
							 | 
						||
| 
								 | 
							
								import logging
							 | 
						||
| 
								 | 
							
								import coloredlogs
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								class _CombinedFormatter(
							 | 
						||
| 
								 | 
							
								    argparse.ArgumentDefaultsHelpFormatter, argparse.RawDescriptionHelpFormatter
							 | 
						||
| 
								 | 
							
								):
							 | 
						||
| 
								 | 
							
								    pass
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								logger = logging.getLogger(__name__)
							 | 
						||
| 
								 | 
							
								coloredlogs.install(
							 | 
						||
| 
								 | 
							
								    fmt="%(asctime)s,%(msecs)03d %(levelname)s - %(message)s", datefmt="%H:%M:%S"
							 | 
						||
| 
								 | 
							
								)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								dest_dir_map = {
							 | 
						||
| 
								 | 
							
								    "dash-core-components": "dcc",
							 | 
						||
| 
								 | 
							
								    "dash-html-components": "html",
							 | 
						||
| 
								 | 
							
								    "dash-table": "dash_table",
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								def status_print(msg, **kwargs):
							 | 
						||
| 
								 | 
							
								    try:
							 | 
						||
| 
								 | 
							
								        print(msg, **kwargs)
							 | 
						||
| 
								 | 
							
								    except UnicodeEncodeError:
							 | 
						||
| 
								 | 
							
								        print(msg.encode("ascii", "ignore"), **kwargs)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								def bootstrap_components(components_source, concurrency, install_type):
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    is_windows = sys.platform == "win32"
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    source_glob = (
							 | 
						||
| 
								 | 
							
								        components_source
							 | 
						||
| 
								 | 
							
								        if components_source != "all"
							 | 
						||
| 
								 | 
							
								        else "{dash-core-components,dash-html-components,dash-table}"
							 | 
						||
| 
								 | 
							
								    )
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    cmdstr = f"npx lerna exec --concurrency {concurrency} --scope='{source_glob}' -- npm {install_type}"
							 | 
						||
| 
								 | 
							
								    cmd = shlex.split(cmdstr, posix=not is_windows)
							 | 
						||
| 
								 | 
							
								    status_print(cmdstr)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    with subprocess.Popen(
							 | 
						||
| 
								 | 
							
								        cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE, shell=is_windows
							 | 
						||
| 
								 | 
							
								    ) as proc:
							 | 
						||
| 
								 | 
							
								        out, err = proc.communicate()
							 | 
						||
| 
								 | 
							
								        status = proc.poll()
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    if err:
							 | 
						||
| 
								 | 
							
								        status_print(("🛑 " if status else "") + err.decode(), file=sys.stderr)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    if status or not out:
							 | 
						||
| 
								 | 
							
								        status_print(
							 | 
						||
| 
								 | 
							
								            f"🚨 Failed installing npm dependencies for component packages: {source_glob} (status={status}) 🚨",
							 | 
						||
| 
								 | 
							
								            file=sys.stderr,
							 | 
						||
| 
								 | 
							
								        )
							 | 
						||
| 
								 | 
							
								        sys.exit(1)
							 | 
						||
| 
								 | 
							
								    else:
							 | 
						||
| 
								 | 
							
								        status_print(
							 | 
						||
| 
								 | 
							
								            f"🟢 Finished installing npm dependencies for component packages: {source_glob} 🟢",
							 | 
						||
| 
								 | 
							
								            file=sys.stderr,
							 | 
						||
| 
								 | 
							
								        )
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								def build_components(components_source, concurrency):
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    is_windows = sys.platform == "win32"
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    source_glob = (
							 | 
						||
| 
								 | 
							
								        components_source
							 | 
						||
| 
								 | 
							
								        if components_source != "all"
							 | 
						||
| 
								 | 
							
								        else "{dash-core-components,dash-html-components,dash-table}"
							 | 
						||
| 
								 | 
							
								    )
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    cmdstr = f"npx lerna exec --concurrency {concurrency} --scope='{source_glob}' -- npm run build"
							 | 
						||
| 
								 | 
							
								    cmd = shlex.split(cmdstr, posix=not is_windows)
							 | 
						||
| 
								 | 
							
								    status_print(cmdstr)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    with subprocess.Popen(
							 | 
						||
| 
								 | 
							
								        cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE, shell=is_windows
							 | 
						||
| 
								 | 
							
								    ) as proc:
							 | 
						||
| 
								 | 
							
								        out, err = proc.communicate()
							 | 
						||
| 
								 | 
							
								        status = proc.poll()
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    if err:
							 | 
						||
| 
								 | 
							
								        status_print(("🛑 " if status else "") + err.decode(), file=sys.stderr)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    if status or not out:
							 | 
						||
| 
								 | 
							
								        status_print(
							 | 
						||
| 
								 | 
							
								            f"🚨 Finished updating component packages: {source_glob} (status={status}) 🚨",
							 | 
						||
| 
								 | 
							
								            file=sys.stderr,
							 | 
						||
| 
								 | 
							
								        )
							 | 
						||
| 
								 | 
							
								        sys.exit(1)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    if "{" in source_glob:
							 | 
						||
| 
								 | 
							
								        source_glob = source_glob.split("{")[1].split("}")[0]
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    for package in source_glob.split(","):
							 | 
						||
| 
								 | 
							
								        build_directory = os.path.join(
							 | 
						||
| 
								 | 
							
								            "components", package, package.replace("-", "_").rstrip("/\\")
							 | 
						||
| 
								 | 
							
								        )
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        dest_dir = dest_dir_map.get(package) or package
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        dest_path = os.path.join("dash", dest_dir)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        if not os.path.exists(dest_path):
							 | 
						||
| 
								 | 
							
								            try:
							 | 
						||
| 
								 | 
							
								                os.makedirs(dest_path)
							 | 
						||
| 
								 | 
							
								            except OSError:
							 | 
						||
| 
								 | 
							
								                logger.exception("🚨 Having issues manipulating %s", dest_path)
							 | 
						||
| 
								 | 
							
								                sys.exit(1)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        if not os.path.exists(build_directory):
							 | 
						||
| 
								 | 
							
								            status_print(
							 | 
						||
| 
								 | 
							
								                "🚨 Could not locate build artifacts."
							 | 
						||
| 
								 | 
							
								                + " Check that the npm build process completed"
							 | 
						||
| 
								 | 
							
								                + f" successfully for package: {package} 🚨"
							 | 
						||
| 
								 | 
							
								            )
							 | 
						||
| 
								 | 
							
								            sys.exit(1)
							 | 
						||
| 
								 | 
							
								        else:
							 | 
						||
| 
								 | 
							
								            status_print(f"🚚 Moving build artifacts from {build_directory} to Dash 🚚")
							 | 
						||
| 
								 | 
							
								            shutil.rmtree(dest_path)
							 | 
						||
| 
								 | 
							
								            shutil.copytree(build_directory, dest_path)
							 | 
						||
| 
								 | 
							
								            with open(os.path.join(dest_path, ".gitkeep"), "w", encoding="utf-8"):
							 | 
						||
| 
								 | 
							
								                pass
							 | 
						||
| 
								 | 
							
								            status_print(
							 | 
						||
| 
								 | 
							
								                f"🟢 Finished moving build artifacts from {build_directory} to Dash 🟢"
							 | 
						||
| 
								 | 
							
								            )
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								def cli():
							 | 
						||
| 
								 | 
							
								    parser = argparse.ArgumentParser(
							 | 
						||
| 
								 | 
							
								        prog="dash-update-components",
							 | 
						||
| 
								 | 
							
								        formatter_class=_CombinedFormatter,
							 | 
						||
| 
								 | 
							
								        description="Update the specified subcomponent libraries within Dash"
							 | 
						||
| 
								 | 
							
								        " by copying over build artifacts, dependencies, and dependency metadata.",
							 | 
						||
| 
								 | 
							
								    )
							 | 
						||
| 
								 | 
							
								    parser.add_argument(
							 | 
						||
| 
								 | 
							
								        "components_source",
							 | 
						||
| 
								 | 
							
								        help="A glob string that matches the Dash component libraries to be updated"
							 | 
						||
| 
								 | 
							
								        " (eg.'dash-table' // 'dash-core-components|dash-html-components' // 'all')."
							 | 
						||
| 
								 | 
							
								        " The default argument is 'all'.",
							 | 
						||
| 
								 | 
							
								        default="all",
							 | 
						||
| 
								 | 
							
								    )
							 | 
						||
| 
								 | 
							
								    parser.add_argument(
							 | 
						||
| 
								 | 
							
								        "--concurrency",
							 | 
						||
| 
								 | 
							
								        type=int,
							 | 
						||
| 
								 | 
							
								        default=3,
							 | 
						||
| 
								 | 
							
								        help="Maximum concurrent steps, up to 3 (ie all components in parallel)",
							 | 
						||
| 
								 | 
							
								    )
							 | 
						||
| 
								 | 
							
								    parser.add_argument(
							 | 
						||
| 
								 | 
							
								        "--ci",
							 | 
						||
| 
								 | 
							
								        help="For clean-install use '--ci True'",
							 | 
						||
| 
								 | 
							
								        default="False",
							 | 
						||
| 
								 | 
							
								    )
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    args = parser.parse_args()
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    if sys.platform == "win32":
							 | 
						||
| 
								 | 
							
								        args.components_source = args.components_source.replace('"', "").replace(
							 | 
						||
| 
								 | 
							
								            "'", ""
							 | 
						||
| 
								 | 
							
								        )
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    bootstrap_components(
							 | 
						||
| 
								 | 
							
								        args.components_source, args.concurrency, "ci" if args.ci == "True" else "i"
							 | 
						||
| 
								 | 
							
								    )
							 | 
						||
| 
								 | 
							
								    build_components(args.components_source, args.concurrency)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								if __name__ == "__main__":
							 | 
						||
| 
								 | 
							
								    cli()
							 |