import os import sys import time import platform import json import shutil from subprocess import check_call from distutils import log project_root = os.path.dirname(os.path.abspath(__file__)) node_root = os.path.join(project_root, "js") is_repo = os.path.exists(os.path.join(project_root, ".git")) node_modules = os.path.join(node_root, "node_modules") targets = [ os.path.join(project_root, "plotly", "package_data", "widgetbundle.js"), ] npm_path = os.pathsep.join( [ os.path.join(node_root, "node_modules", ".bin"), os.environ.get("PATH", os.defpath), ] ) # Load plotly.js version from js/package.json def plotly_js_version(): path = os.path.join(project_root, "js", "package.json") with open(path, "rt") as f: package_json = json.load(f) version = package_json["dependencies"]["plotly.js"] version = version.replace("^", "") return version # install package.json dependencies using npm def install_js_deps(local): npmName = "npm" if platform.system() == "Windows": npmName = "npm.cmd" try: check_call([npmName, "--version"]) has_npm = True except: has_npm = False skip_npm = os.environ.get("SKIP_NPM", False) if skip_npm: log.info("Skipping npm-installation") return if not has_npm: log.error( "`npm` unavailable. If you're running this command using sudo, make sure `npm` is available to sudo" ) env = os.environ.copy() env["PATH"] = npm_path if has_npm: log.info("Installing build dependencies with npm. This may take a while...") check_call( [npmName, "install"], cwd=node_root, stdout=sys.stdout, stderr=sys.stderr, ) if local is not None: plotly_archive = os.path.join(local, "plotly.js.tgz") check_call( [npmName, "install", plotly_archive], cwd=node_root, stdout=sys.stdout, stderr=sys.stderr, ) check_call( [npmName, "run", "build"], cwd=node_root, stdout=sys.stdout, stderr=sys.stderr, ) os.utime(node_modules, None) for t in targets: if not os.path.exists(t): msg = "Missing file: %s" % t raise ValueError(msg) # Generate class hierarchy from Plotly JSON schema def run_codegen(): if sys.version_info < (3, 8): raise ImportError("Code generation must be executed with Python >= 3.8") from codegen import perform_codegen perform_codegen() def overwrite_schema_local(uri): path = os.path.join(project_root, "codegen", "resources", "plot-schema.json") shutil.copyfile(uri, path) def overwrite_schema(url): import requests req = requests.get(url) assert req.status_code == 200 path = os.path.join(project_root, "codegen", "resources", "plot-schema.json") with open(path, "wb") as f: f.write(req.content) def overwrite_bundle_local(uri): path = os.path.join(project_root, "plotly", "package_data", "plotly.min.js") shutil.copyfile(uri, path) def overwrite_bundle(url): import requests req = requests.get(url) print("url:", url) assert req.status_code == 200 path = os.path.join(project_root, "plotly", "package_data", "plotly.min.js") with open(path, "wb") as f: f.write(req.content) def overwrite_plotlyjs_version_file(plotlyjs_version): path = os.path.join(project_root, "plotly", "offline", "_plotlyjs_version.py") with open(path, "w") as f: f.write( """\ # DO NOT EDIT # This file is generated by the updatebundle commands.py command __plotlyjs_version__ = "{plotlyjs_version}" """.format( plotlyjs_version=plotlyjs_version ) ) def request_json(url): import requests req = requests.get(url) return json.loads(req.content.decode("utf-8")) def get_latest_publish_build_info(repo, branch): url = ( r"https://circleci.com/api/v1.1/project/github/" r"{repo}/tree/{branch}?limit=100&filter=completed" ).format(repo=repo, branch=branch) branch_jobs = request_json(url) # Get most recent successful publish build for branch builds = [ j for j in branch_jobs if j.get("workflows", {}).get("job_name", None) == "publish-dist" and j.get("status", None) == "success" ] build = builds[0] # Extract build info return {p: build[p] for p in ["vcs_revision", "build_num", "committer_date"]} def get_bundle_schema_local(local): plotly_archive = os.path.join(local, "plotly.js.tgz") plotly_bundle = os.path.join(local, "dist/plotly.min.js") plotly_schemas = os.path.join(local, "dist/plot-schema.json") return plotly_archive, plotly_bundle, plotly_schemas def get_bundle_schema_urls(build_num): url = ( "https://circleci.com/api/v1.1/project/github/" "plotly/plotly.js/{build_num}/artifacts" ).format(build_num=build_num) artifacts = request_json(url) # Find archive archives = [a for a in artifacts if a.get("path", None) == "plotly.js.tgz"] archive = archives[0] # Find bundle bundles = [a for a in artifacts if a.get("path", None) == "dist/plotly.min.js"] bundle = bundles[0] # Find schema schemas = [a for a in artifacts if a.get("path", None) == "dist/plot-schema.json"] schema = schemas[0] return archive["url"], bundle["url"], schema["url"] # Download latest version of the plot-schema JSON file def update_schema(plotly_js_version): url = ( "https://raw.githubusercontent.com/plotly/plotly.js/" "v%s/dist/plot-schema.json" % plotly_js_version ) overwrite_schema(url) # Download latest version of the plotly.js bundle def update_bundle(plotly_js_version): url = ( "https://raw.githubusercontent.com/plotly/plotly.js/" "v%s/dist/plotly.min.js" % plotly_js_version ) overwrite_bundle(url) # Write plotly.js version file plotlyjs_version = plotly_js_version overwrite_plotlyjs_version_file(plotlyjs_version) # Update project to a new version of plotly.js def update_plotlyjs(plotly_js_version): update_bundle(plotly_js_version) update_schema(plotly_js_version) run_codegen() # Update the plotly.js schema and bundle from master def update_schema_bundle_from_master(): if "--devrepo" in sys.argv: devrepo = sys.argv[sys.argv.index("--devrepo") + 1] else: devrepo = "plotly/plotly.js" if "--devbranch" in sys.argv: devbranch = sys.argv[sys.argv.index("--devbranch") + 1] else: devbranch = "master" if "--local" in sys.argv: local = sys.argv[sys.argv.index("--local") + 1] else: local = None if local is None: build_info = get_latest_publish_build_info(devrepo, devbranch) archive_url, bundle_url, schema_url = get_bundle_schema_urls( build_info["build_num"] ) # Update bundle in package data overwrite_bundle(bundle_url) # Update schema in package data overwrite_schema(schema_url) else: # this info could be more informative but # it doesn't seem as useful in a local context # and requires dependencies and programming. build_info = {"vcs_revision": "local", "committer_date": str(time.time())} devrepo = local devbranch = "" archive_uri, bundle_uri, schema_uri = get_bundle_schema_local(local) overwrite_bundle_local(bundle_uri) overwrite_schema_local(schema_uri) # Update plotly.js url in package.json package_json_path = os.path.join(node_root, "package.json") with open(package_json_path, "r") as f: package_json = json.load(f) # Replace version with bundle url package_json["dependencies"]["plotly.js"] = ( archive_url if local is None else archive_uri ) with open(package_json_path, "w") as f: json.dump(package_json, f, indent=2) # update plotly.js version in _plotlyjs_version rev = build_info["vcs_revision"] date = str(build_info["committer_date"]) version = "_".join([devrepo, devbranch, date[:10], rev[:8]]) overwrite_plotlyjs_version_file(version) install_js_deps(local) # Update project to a new development version of plotly.js def update_plotlyjs_dev(): update_schema_bundle_from_master() run_codegen() if __name__ == "__main__": if "updateplotlyjsdev" in sys.argv: update_plotlyjs_dev() elif "updateplotlyjs" in sys.argv: print(plotly_js_version()) update_plotlyjs(plotly_js_version())