diff --git a/.circleci/config.yml b/.circleci/config.yml index 9c830220f1..1460364639 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -34,6 +34,7 @@ jobs: - run: sudo pip install -r requirements_dev.txt - run: sudo pip install -r requirements.txt - run: sudo npm install + - run: sudo npm run bundle - run: sudo npm run build - run: command: pytest --junitxml=/tmp/test-reports/pytest/junit.xml tests/ diff --git a/Dockerfile b/Dockerfile index bbfaa77f40..cbb333eba2 100644 --- a/Dockerfile +++ b/Dockerfile @@ -15,7 +15,7 @@ RUN sudo tar --strip-components 1 -xzvf node-v* -C /usr/local # Upgrade npm RUN npm upgrade npm -RUN npm install && npm run build && rm -rf node_modules +RUN npm install && npm run bundle && npm run build && rm -rf node_modules RUN chown -R redash /app USER redash diff --git a/Makefile b/Makefile new file mode 100644 index 0000000000..c776130b5a --- /dev/null +++ b/Makefile @@ -0,0 +1,22 @@ +.PHONY: build bundle compose_build create_database tests test_db clean + +compose_build: + docker-compose build + +test_db: + docker-compose run --rm postgres psql -h postgres -U postgres -c "create database tests" + +create_database: + docker-compose run server create_db + +clean: + docker ps -a -q | xargs docker kill;docker ps -a -q | xargs docker rm + +bundle: + docker-compose run server bin/bundle-extensions + +tests: + docker-compose run server tests + +build: bundle + npm run build diff --git a/bin/bundle-extensions b/bin/bundle-extensions new file mode 100755 index 0000000000..fe4def797d --- /dev/null +++ b/bin/bundle-extensions @@ -0,0 +1,37 @@ +#!/usr/bin/env python + +import os +import redash_stmo +from subprocess import call +from distutils.dir_util import copy_tree + +from pkg_resources import iter_entry_points, resource_filename + + +# Make a directory for extensions and set it as an environment variable +# to be picked up by webpack. +EXTENSIONS_RELATIVE_PATH = os.path.join('client', 'app', 'extensions') +EXTENSIONS_DIRECTORY = os.path.join( + os.path.dirname(os.path.dirname(__file__)), + EXTENSIONS_RELATIVE_PATH) + +if not os.path.exists(EXTENSIONS_DIRECTORY): + os.makedirs(EXTENSIONS_DIRECTORY) +os.environ["EXTENSIONS_DIRECTORY"] = EXTENSIONS_RELATIVE_PATH + +for entry_point in iter_entry_points('webpack.bundles'): + extension_data = entry_point.load() + + # This is where the frontend code for an extension lives + # inside of its package. + content_folder_relative = os.path.join( + extension_data['extension_directory'], + extension_data['frontend_content']) + content_folder = resource_filename(redash_stmo.__name__, content_folder_relative) + + # This is where we place our extensions folder. + destination = os.path.join( + EXTENSIONS_DIRECTORY, + extension_data['extension_directory']) + + copy_tree(content_folder, destination) diff --git a/client/app/config/index.js b/client/app/config/index.js index aecdaf14b0..d51affad70 100644 --- a/client/app/config/index.js +++ b/client/app/config/index.js @@ -82,6 +82,11 @@ function registerComponents() { registerAll(context); } +function registerExtensions() { + const context = require.context('%', true, /^((?![\\/]test[\\/]).)*\.js$/); + registerAll(context); +} + function registerServices() { const context = require.context('@/services', true, /^((?![\\/]test[\\/]).)*\.js$/); registerAll(context); @@ -142,6 +147,7 @@ markdownFilter(ngModule); dateTimeFilter(ngModule); registerComponents(); registerPages(); +registerExtensions(); registerVisualizations(ngModule); export default ngModule; diff --git a/package.json b/package.json index d036f12353..5f53bc5fbd 100644 --- a/package.json +++ b/package.json @@ -6,6 +6,7 @@ "scripts": { "start": "webpack-dev-server", "dev": "REDASH_BACKEND=https://dev.redashapp.com npm start", + "bundle": "bin/bundle-extensions", "build": "rm -rf ./client/dist/ && NODE_ENV=production webpack", "watch": "webpack --watch --progress --colors -d", "analyze": "rm -rf ./client/dist/ && BUNDLE_ANALYZER=on webpack", diff --git a/webpack.config.js b/webpack.config.js index 3d9200a513..50eea339c0 100644 --- a/webpack.config.js +++ b/webpack.config.js @@ -17,6 +17,10 @@ const redashBackend = process.env.REDASH_BACKEND || "http://localhost:5000"; const basePath = fs.realpathSync(path.join(__dirname, "client")); const appPath = fs.realpathSync(path.join(__dirname, "client", "app")); +const extensionsRelativePath = process.env.EXTENSIONS_DIRECTORY || + path.join("client", "app", "extensions"); +const extensionPath = fs.realpathSync(path.join(__dirname, extensionsRelativePath)); + const config = { entry: { app: ["./client/app/index.js", "./client/app/assets/less/main.less"], @@ -29,7 +33,8 @@ const config = { }, resolve: { alias: { - "@": appPath + "@": appPath, + "%": extensionPath } }, plugins: [