Files
zulip/tools/minify-js
Robert Hönig 789ae8648a Add wrapper and log file output for provisioning.
Before this commit, provisioning was done by executing provision.py,
which printed the log directly to stdout, making debugging harder.
This commit creates a wrapper bash script 'provision' in tools, which
calls 'zulip/scripts/tools/provision_vm.py' (the new location of
provision.py) and prints all the output to
'zulip/var/log/zulip/zulip_provision.log' via 'tee'.
Travis tests and docs have been modified accordingly.
2017-01-17 14:23:28 -08:00

136 lines
5.2 KiB
Python
Executable File

#!/usr/bin/env python
# Minifies JavaScripts, creating source maps
from __future__ import absolute_import
from __future__ import print_function
import os
import subprocess
import argparse
import sys
import six
parser = argparse.ArgumentParser()
parser.add_argument('--prev-deploy', metavar='DIR',
help='a previous deploy from which to reuse files if possible')
args = parser.parse_args()
prev_deploy = args.prev_deploy
# We have to pull out JS_SPECS, defined in our settings file, so we know what
# JavaScript source files to minify (and what output files to create).
sys.path.insert(0, os.path.join(os.path.dirname(__file__), '..'))
import scripts.lib.setup_path_on_import
from typing import Optional, Set
os.environ['DJANGO_SETTINGS_MODULE'] = 'zproject.settings'
from django.conf import settings
os.chdir(settings.DEPLOY_ROOT)
STATIC_PATH = 'static/'
# Compile Handlebars templates
subprocess.check_call(['tools/compile-handlebars-templates'])
# Create webpack bundle
subprocess.check_call(['tools/webpack'])
def get_changed_source_files(other_checkout):
# type: (Optional[str]) -> Optional[Set[str]]
""" Get list of changed static files since other_checkout.
If git fails to return a reasonable looking list, this returns None,
in which case it should be assumed no files can be reused from
other_checkout. """
try:
git_dir = os.path.join(other_checkout, '.git')
old_commit_sha1 = subprocess.check_output(['git', 'rev-parse', 'HEAD'],
env={'GIT_DIR': git_dir}, universal_newlines=True)
old_commit_sha1 = old_commit_sha1.rstrip()
git_diff = subprocess.check_output(['git', 'diff', '--name-only',
old_commit_sha1], universal_newlines=True)
except subprocess.CalledProcessError:
# If git returned an error, assume we can't reuse any files, and
# regenerate everything.
print("Warning: git returned an error when comparing to the previous")
print("deploy in %s. Will re-minify JavaScript instead of reusing"
% (other_checkout,))
return None
changed = set() # type: Set[str]
for filename in git_diff.split('\n'):
if not filename.startswith(STATIC_PATH):
continue # Ignore non-static files.
if filename.endswith('.handlebars'):
continue
changed.add(filename)
return changed
changed_files = set() # type: Set[str]
if prev_deploy:
changed_files = get_changed_source_files(prev_deploy)
if changed_files is None:
prev_deploy = None
# Always use the newly compiled handlebars templates and webpack bundle.
if prev_deploy:
changed_files.add(os.path.join(STATIC_PATH, 'templates/compiled.js'))
changed_files.add(os.path.join(STATIC_PATH, 'js/bundle.js'))
JS_SPECS = settings.JS_SPECS
CLOSURE_BINARY = '/usr/bin/closure-compiler'
if not os.path.exists(CLOSURE_BINARY):
CLOSURE_BINARY = 'tools/closure-compiler/run'
if not os.path.exists(CLOSURE_BINARY):
print("closure-compiler not installed; the Vagrant tools/provision installs it via apt "
"or you can manually unpack http://dl.google.com/closure-compiler/compiler-latest.zip to "
"tools/closure-compiler")
sys.exit(1)
# Where to put minified JS and source maps
MIN_DIR = os.path.join(STATIC_PATH, 'min/')
MAP_DIR = os.path.join(STATIC_PATH, 'source-map/')
subprocess.check_call(['mkdir', '-p', MIN_DIR, MAP_DIR])
for js_group, filespec in six.iteritems(JS_SPECS):
# JS_SPECS look like 'js/foobar.js'.
# changed_files look like 'static/js/foobar.js'.
# So we prepend 'static/' to the JS_SPECS so these match up.
in_files = [os.path.join(STATIC_PATH, filename)
for filename in filespec['source_filenames']]
out_file = os.path.join(MIN_DIR, os.path.basename(filespec['output_filename']))
map_file = os.path.join(MAP_DIR, os.path.basename(filespec['output_filename'])
+ '.map')
if (not 'force_minify' in filespec) and (prev_deploy and len(set(in_files) & changed_files) == 0):
# Try to reuse the output file from previous deploy
try:
for dest in [out_file, map_file]:
src = os.path.join(prev_deploy, dest)
os.path.getsize(src) # Just to throw error if it doesn't exist.
if os.path.abspath(src) != os.path.abspath(dest):
subprocess.check_call(['cp', src, dest])
continue # Copy succeeded, so go on to next file.
except (subprocess.CalledProcessError, OSError):
pass # Copy failed, so fall through to minification instead.
# No previous deploy, or a source file has changed, or copying was
# supposed to work but failed. Thus, minify the JS anew.
# (N.B. we include STATIC_HEADER_FILE before the JavaScripts.
# This way it doesn't throw off the source map.)
cmd = '%s --language_in ECMASCRIPT5 --create_source_map %s %s %s' % (
CLOSURE_BINARY, map_file,
settings.STATIC_HEADER_FILE, ' '.join(in_files))
js = subprocess.check_output(cmd, shell=True)
# Write out the JS
with open(out_file, 'wb') as fp:
fp.write(js)