mirror of
https://github.com/zulip/zulip.git
synced 2025-11-06 23:13:25 +00:00
Usually, update-prod-static takes care of this, but it's useful for debugging purposes to be able to rerun this manually.
136 lines
5.2 KiB
Python
Executable File
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.py 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)
|