浏览代码

Merge pull request #2265 from blink1073/auto-build

Add a build prompt to the UI
Brian E. Granger 8 年之前
父节点
当前提交
5c7acdea35

+ 57 - 4
jupyterlab/commands.py

@@ -16,6 +16,8 @@ import sys
 import tarfile
 from jupyter_core.paths import ENV_JUPYTER_PATH
 
+from ._version import __version__
+
 
 if sys.platform == 'win32':
     from subprocess import list2cmdline
@@ -143,6 +145,54 @@ def unlink_package(package, app_dir=None):
     return True
 
 
+def should_build(app_dir=None):
+    """Determine whether JupyterLab should be built.
+
+    Note: Linked packages should be updated by manually building.
+
+    Returns a tuple of whether a build is necessary, and an associated message.
+    """
+    app_dir = get_app_dir(app_dir)
+
+    # Check for installed extensions
+    extensions = _get_extensions(app_dir)
+
+    # No linked and no extensions and no built version.
+    if not extensions and not os.path.exists(pjoin(app_dir, 'static')):
+        return False, ''
+
+    pkg_path = pjoin(app_dir, 'static', 'package.json')
+    if not os.path.exists(pkg_path):
+        return True, 'Installed extensions with no built application'
+
+    with open(pkg_path) as fid:
+        data = json.load(fid)
+
+    # Look for mismatched version.
+    if not data['jupyterlab'].get('version', '') == __version__:
+        msg = 'Version mismatch: %s (built), %s (current)'
+        return True, msg % (data['jupyterlab'].get('version', ''), __version__)
+
+    # Look for mismatched extensions.
+    _ensure_package(app_dir)
+
+    staging_path = pjoin(app_dir, 'staging', 'package.json')
+    with open(staging_path) as fid:
+        staging_data = json.load(fid)
+
+    staging_exts = staging_data['jupyterlab']['extensions']
+
+    if set(staging_exts) != set(data['jupyterlab']['extensions']):
+        return True, 'Installed extensions changed'
+
+    # Look for mismatched extension paths.
+    for name in extensions:
+        if data['dependencies'][name] != staging_data['dependencies'][name]:
+            return True, 'Installed extensions changed'
+
+    return False, ''
+
+
 def _get_build_config(app_dir):
     """Get the build config data for the given app dir
     """
@@ -182,7 +232,8 @@ def uninstall_extension(name, app_dir=None):
                 _write_build_config(config, app_dir)
             return True
 
-    for (extname, path) in _get_extensions(app_dir).items():
+    for (extname, data) in _get_extensions(app_dir).items():
+        path = data['path']
         if extname == name:
             print('Uninstalling %s from %s' % (name, os.path.dirname(path)))
             os.remove(path)
@@ -271,7 +322,7 @@ def _ensure_package(app_dir, name='JupyterLab', version=None):
     extensions = _get_extensions(app_dir)
 
     for (key, value) in extensions.items():
-        data['dependencies'][key] = value
+        data['dependencies'][key] = value['path']
         data['jupyterlab']['extensions'].append(key)
 
     config = _get_build_config(app_dir)
@@ -316,7 +367,8 @@ def _get_extensions(app_dir):
     sys_path = pjoin(ENV_JUPYTER_PATH[0], 'lab', 'extensions')
     for target in glob.glob(pjoin(sys_path, '*.tgz')):
         data = _read_package(target)
-        extensions[data['name']] = os.path.realpath(target)
+        extensions[data['name']] = dict(path=os.path.realpath(target),
+                                        version=data['version'])
 
     app_path = pjoin(app_dir, 'extensions')
     if app_path == sys_path or not os.path.exists(app_path):
@@ -324,7 +376,8 @@ def _get_extensions(app_dir):
 
     for target in glob.glob(pjoin(app_path, '*.tgz')):
         data = _read_package(target)
-        extensions[data['name']] = os.path.realpath(target)
+        extensions[data['name']] = dict(path=os.path.realpath(target),
+                                        version=data['version'])
 
     return extensions
 

+ 9 - 1
jupyterlab/extension.py

@@ -7,7 +7,7 @@ import os
 
 from jupyterlab_launcher import add_handlers, LabConfig
 
-from .commands import get_app_dir, list_extensions
+from .commands import get_app_dir, list_extensions, should_build
 from ._version import __version__
 
 #-----------------------------------------------------------------------------
@@ -65,6 +65,14 @@ def load_jupyter_server_extension(nbapp):
     installed = list_extensions(app_dir)
     fallback = not installed and not os.path.exists(config.assets_dir)
 
+    web_app.settings.setdefault('page_config_data', dict())
+
+    if not core_mode:
+        build_needed, msg = should_build(app_dir)
+        if build_needed:
+            nbapp.log.warn('Build required: %s' % msg)
+            web_app.settings['page_config_data']['buildRequired'] = msg
+
     if core_mode or fallback:
         config.assets_dir = os.path.join(here, 'build')
         if not os.path.exists(config.assets_dir):

+ 10 - 1
jupyterlab/tests/test_jupyterlab.py

@@ -27,7 +27,7 @@ from jupyterlab.extension import (
 )
 from jupyterlab.commands import (
     install_extension, uninstall_extension, list_extensions,
-    build, link_package, unlink_package, get_app_dir,
+    build, link_package, unlink_package, get_app_dir, should_build,
     _get_linked_packages, _ensure_package
 )
 
@@ -248,3 +248,12 @@ class TestExtension(TestCase):
         app.initialize()
         sys.stderr = stderr
         load_jupyter_server_extension(app)
+
+    def test_should_build(self):
+        assert not should_build()[0]
+        install_extension(self.source_dir)
+        assert should_build()[0]
+        build()
+        assert not should_build()[0]
+        uninstall_extension('@jupyterlab/python-tests')
+        assert should_build()[0]

+ 2 - 1
packages/application-extension/package.json

@@ -13,7 +13,8 @@
   },
   "dependencies": {
     "@jupyterlab/application": "^0.5.0",
-    "@jupyterlab/apputils": "^0.5.0"
+    "@jupyterlab/apputils": "^0.5.0",
+    "@jupyterlab/coreutils": "^0.5.0"
   },
   "devDependencies": {
     "rimraf": "^2.5.2",

+ 21 - 1
packages/application-extension/src/index.ts

@@ -6,9 +6,13 @@ import {
 } from '@jupyterlab/application';
 
 import {
-  ICommandPalette
+  Dialog, ICommandPalette, showDialog
 } from '@jupyterlab/apputils';
 
+import {
+  PageConfig
+} from '@jupyterlab/coreutils';
+
 
 /**
  * The command IDs used by the application plugin.
@@ -87,6 +91,22 @@ const plugin: JupyterLabPlugin<void> = {
     });
     palette.addItem({ command, category });
 
+    // Temporary build message for manual rebuild.
+    let buildMessage = PageConfig.getOption('buildRequired');
+    if (buildMessage) {
+      let body = document.createElement('div');
+      body.innerHTML = (
+        '<p>JupyterLab build is out of date.<br>' +
+        'Please run <code>jupyter lab build</code> from<br>' +
+        'the command line and relaunch.</p>'
+      );
+      showDialog({
+        title: 'Build Recommended',
+        body,
+        buttons: [Dialog.okButton()]
+      });
+    }
+
     const message = 'Are you sure you want to exit JupyterLab?\n' +
                     'Any unsaved changes will be lost.';
 

+ 0 - 1
packages/apputils/style/dialog.css

@@ -58,7 +58,6 @@
   font-size: var(--jp-ui-font-size2);
   background: #FAFAFA;
   overflow: auto;
-  max-width: 252px;
 }