diff --git a/endpoints/common.py b/endpoints/common.py index 090708e39..3534ee072 100644 --- a/endpoints/common.py +++ b/endpoints/common.py @@ -3,6 +3,7 @@ import urlparse import json import string import datetime +import os # Register the various exceptions via decorators. import endpoints.decorated @@ -32,6 +33,23 @@ profile = logging.getLogger('application.profiler') route_data = None +CACHE_BUSTERS_JSON = 'static/dist/cachebusters.json' +CACHE_BUSTERS = None + +def get_cache_busters(): + """ Retrieves the cache busters hashes. """ + global CACHE_BUSTERS + if CACHE_BUSTERS is not None: + return CACHE_BUSTERS + + if not os.path.exists(CACHE_BUSTERS_JSON): + return {} + + with open(CACHE_BUSTERS_JSON, 'r') as f: + CACHE_BUSTERS = json.loads(f.read()) + return CACHE_BUSTERS + + class RepoPathConverter(BaseConverter): regex = '[\.a-zA-Z0-9_\-]+/[\.a-zA-Z0-9_\-]+' weight = 200 @@ -113,17 +131,15 @@ def list_files(path, extension): filepath = 'static/' + path return [join_path(dp, f) for dp, dn, files in os.walk(filepath) for f in files if matches(f)] -SAVED_CACHE_STRING = random_string() - def render_page_template(name, **kwargs): - if app.config.get('DEBUGGING', False): + debugging = app.config.get('DEBUGGING', False) + if debugging: # If DEBUGGING is enabled, then we load the full set of individual JS and CSS files # from the file system. library_styles = list_files('lib', 'css') main_styles = list_files('css', 'css') library_scripts = list_files('lib', 'js') main_scripts = list_files('js', 'js') - cache_buster = 'debugging' file_lists = [library_styles, main_styles, library_scripts, main_scripts] for file_list in file_lists: @@ -133,7 +149,6 @@ def render_page_template(name, **kwargs): main_styles = ['dist/quay-frontend.css'] library_scripts = [] main_scripts = ['dist/quay-frontend.min.js'] - cache_buster = SAVED_CACHE_STRING use_cdn = app.config.get('USE_CDN', True) if request.args.get('use_cdn') is not None: @@ -142,6 +157,12 @@ def render_page_template(name, **kwargs): external_styles = get_external_css(local=not use_cdn) external_scripts = get_external_javascript(local=not use_cdn) + def add_cachebusters(filenames): + cachebusters = get_cache_busters() + for filename in filenames: + cache_buster = cachebusters.get(filename, random_string()) if not debugging else 'debugging' + yield (filename, cache_buster) + def get_oauth_config(): oauth_config = {} for oauth_app in oauth_apps: @@ -153,13 +174,14 @@ def render_page_template(name, **kwargs): if len(app.config.get('CONTACT_INFO', [])) == 1: contact_href = app.config['CONTACT_INFO'][0] - resp = make_response(render_template(name, route_data=json.dumps(get_route_data()), + 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, - library_scripts=library_scripts, + main_styles=add_cachebusters(main_styles), + library_styles=add_cachebusters(library_styles), + main_scripts=add_cachebusters(main_scripts), + library_scripts=add_cachebusters(library_scripts), feature_set=json.dumps(features.get_features()), config_set=json.dumps(getFrontendVisibleConfig(app.config)), oauth_set=json.dumps(get_oauth_config()), @@ -169,7 +191,6 @@ def render_page_template(name, **kwargs): sentry_public_dsn=app.config.get('SENTRY_PUBLIC_DSN', ''), is_debug=str(app.config.get('DEBUGGING', False)).lower(), show_chat=features.OLARK_CHAT, - cache_buster=cache_buster, has_billing=features.BILLING, contact_href=contact_href, hostname=app.config['SERVER_HOSTNAME'], diff --git a/grunt/Gruntfile.js b/grunt/Gruntfile.js index 5dd381e8d..e9cb14818 100644 --- a/grunt/Gruntfile.js +++ b/grunt/Gruntfile.js @@ -68,6 +68,18 @@ module.exports = function(grunt) { src: ['../static/partials/*.html', '../static/directives/*.html'], dest: '../static/dist/template-cache.js' } + }, + + cachebuster: { + build: { + options: { + format: 'json', + basedir: '../static/' + }, + src: [ '../static/dist/template-cache.js', '../static/dist/<%= pkg.name %>.min.js', + '../static/dist/<%= pkg.name %>.css' ], + dest: '../static/dist/cachebusters.json' + } } }); @@ -75,7 +87,8 @@ module.exports = function(grunt) { grunt.loadNpmTasks('grunt-contrib-concat'); grunt.loadNpmTasks('grunt-contrib-cssmin'); grunt.loadNpmTasks('grunt-angular-templates'); + grunt.loadNpmTasks('grunt-cachebuster'); // Default task(s). - grunt.registerTask('default', ['ngtemplates', 'concat', 'cssmin', 'uglify']); + grunt.registerTask('default', ['ngtemplates', 'concat', 'cssmin', 'uglify', 'cachebuster']); }; diff --git a/grunt/package.json b/grunt/package.json index e4d9836a3..0ea53569b 100644 --- a/grunt/package.json +++ b/grunt/package.json @@ -6,6 +6,7 @@ "grunt-contrib-concat": "~0.4.0", "grunt-contrib-cssmin": "~0.9.0", "grunt-angular-templates": "~0.5.4", - "grunt-contrib-uglify": "~0.4.0" + "grunt-contrib-uglify": "~0.4.0", + "grunt-cachebuster": "~0.1.5" } } diff --git a/templates/base.html b/templates/base.html index 9c4b0fed0..122a69614 100644 --- a/templates/base.html +++ b/templates/base.html @@ -28,11 +28,11 @@ - {% for style_path in main_styles %} + {% for style_path, cache_buster in main_styles %} {% endfor %} - {% for style_path in library_styles %} + {% for style_path, cache_buster in library_styles %} {% endfor %} @@ -53,7 +53,7 @@ {% endfor %} - {% for script_path in library_scripts %} + {% for script_path, cache_buster in library_scripts %} {% endfor %} @@ -61,7 +61,7 @@ {% endblock %} - {% for script_path in main_scripts %} + {% for script_path, cache_buster in main_scripts %} {% endfor %}