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()
 |