diff --git a/Dockerfile b/Dockerfile index ec0e1f4e8..365adfec7 100644 --- a/Dockerfile +++ b/Dockerfile @@ -53,6 +53,7 @@ ADD app.py app.py ADD application.py application.py ADD config.py config.py ADD initdb.py initdb.py +ADD external_libraries.py external_libraries.py ADD conf/init/mklogsdir.sh /etc/my_init.d/ ADD conf/init/gunicorn.sh /etc/service/gunicorn/run @@ -60,9 +61,16 @@ ADD conf/init/nginx.sh /etc/service/nginx/run ADD conf/init/diffsworker.sh /etc/service/diffsworker/run ADD conf/init/webhookworker.sh /etc/service/webhookworker/run +# Build the compiled binaries of JS and CSS RUN cd grunt && npm install RUN cd grunt && grunt +# Download any external libs. +RUN mkdir static/fonts +RUN mkdir static/ldn + +RUN venv/bin/python -m external_libraries + # Add the tests last because they're prone to accidental changes, then run them ADD test test RUN TEST=true venv/bin/python -m unittest discover diff --git a/config.py b/config.py index 9202b26d7..8d7cf43af 100644 --- a/config.py +++ b/config.py @@ -68,6 +68,10 @@ class DefaultConfig(object): DB_TRANSACTION_FACTORY = create_transaction + # If true, CDN URLs will be used for our external dependencies, rather than the local + # copies. + USE_CDN = True + # Data storage STORAGE_TYPE = 'LocalStorage' STORAGE_PATH = 'test/data/registry' diff --git a/endpoints/common.py b/endpoints/common.py index e9bd7b7c6..6a85ef5e6 100644 --- a/endpoints/common.py +++ b/endpoints/common.py @@ -17,6 +17,7 @@ from endpoints.api.discovery import swagger_route_data from werkzeug.routing import BaseConverter from functools import wraps from config import getFrontendVisibleConfig +from external_libraries import get_external_javascript, get_external_css import features @@ -147,7 +148,12 @@ def render_page_template(name, **kwargs): main_scripts = ['dist/quay-frontend.min.js'] cache_buster = random_string() + external_styles = get_external_css(local=not app.config.get('USE_CDN', True)) + external_scripts = get_external_javascript(local=not app.config.get('USE_CDN', True)) + resp = make_response(render_template(name, route_data=json.dumps(get_route_data()), + external_styles=external_styles, + external_scripts=external_scripts, main_styles=main_styles, library_styles=library_styles, main_scripts=main_scripts, diff --git a/external_libraries.py b/external_libraries.py new file mode 100644 index 000000000..d731d5ce1 --- /dev/null +++ b/external_libraries.py @@ -0,0 +1,77 @@ +import urllib2 +import re +import os + +LOCAL_DIRECTORY = 'static/ldn/' + +EXTERNAL_JS = [ + 'code.jquery.com/jquery.js', + 'netdna.bootstrapcdn.com/bootstrap/3.0.0/js/bootstrap.min.js', + 'ajax.googleapis.com/ajax/libs/angularjs/1.2.9/angular.min.js', + 'ajax.googleapis.com/ajax/libs/angularjs/1.2.9/angular-route.min.js', + 'ajax.googleapis.com/ajax/libs/angularjs/1.2.9/angular-sanitize.min.js', + 'ajax.googleapis.com/ajax/libs/angularjs/1.2.9/angular-animate.min.js', + 'cdnjs.cloudflare.com/ajax/libs/bootstrap-datepicker/1.2.0/js/bootstrap-datepicker.min.js', + 'cdn.jsdelivr.net/g/bootbox@4.1.0,underscorejs@1.5.2,restangular@1.2.0,d3js@3.3.3,momentjs', + 'cdn.ravenjs.com/1.1.14/jquery,native/raven.min.js', + 'checkout.stripe.com/checkout.js', +] + +EXTERNAL_CSS = [ + 'netdna.bootstrapcdn.com/font-awesome/4.0.3/css/font-awesome.css', + 'netdna.bootstrapcdn.com/bootstrap/3.0.0/css/bootstrap.no-icons.min.css', + 'fonts.googleapis.com/css?family=Droid+Sans:400,700', +] + +EXTERNAL_FONTS = [ + 'netdna.bootstrapcdn.com/font-awesome/4.0.3/fonts/fontawesome-webfont.woff?v=4.0.3', + 'netdna.bootstrapcdn.com/font-awesome/4.0.3/fonts/fontawesome-webfont.ttf?v=4.0.3', + 'netdna.bootstrapcdn.com/font-awesome/4.0.3/fonts/fontawesome-webfont.svg?v=4.0.3', +] + + +def get_external_javascript(local=False): + if local: + return [LOCAL_DIRECTORY + format_local_name(src) for src in EXTERNAL_JS] + + return ['//' + src for src in EXTERNAL_JS] + + +def get_external_css(local=False): + if local: + return [LOCAL_DIRECTORY + format_local_name(src) for src in EXTERNAL_CSS] + + return ['//' + src for src in EXTERNAL_CSS] + + +def format_local_name(url): + filename = url.split('/')[-1] + filename = re.sub(r'[+,?@=:]', '', filename) + if not filename.endswith('.css') and not filename.endswith('.js'): + if filename.find('css') >= 0: + filename = filename + '.css' + else: + filename = filename + '.js' + + return filename + + +if __name__ == '__main__': + for url in EXTERNAL_JS + EXTERNAL_CSS: + print 'Downloading %s' % url + response = urllib2.urlopen('https://' + url) + contents = response.read() + + filename = format_local_name(url) + print 'Writing %s' % filename + with open(LOCAL_DIRECTORY + filename, 'w') as f: + f.write(contents) + + + for url in EXTERNAL_FONTS: + print 'Downloading %s' % url + response = urllib2.urlopen('https://' + url) + + filename = os.path.basename(url).split('?')[0] + with open('static/fonts/' + filename, "wb") as local_file: + local_file.write(response.read()) diff --git a/templates/base.html b/templates/base.html index 01f0e6f6c..228cb2742 100644 --- a/templates/base.html +++ b/templates/base.html @@ -11,9 +11,9 @@ - - - + {% for style_url in external_styles %} + + {% endfor %} @@ -47,20 +47,9 @@ window.__token = '{{ csrf_token() }}'; - - - - - - - - - - - - - - + {% for script_url in external_scripts %} + + {% endfor %} {% for script_path in library_scripts %}