mirror of
https://github.com/zulip/zulip.git
synced 2025-11-03 05:23:35 +00:00
This causes `upgrade-zulip-from-git`, as well as a no-option run of `tools/build-release-tarball`, to produce a Zulip install running Python 3, rather than Python 2. In particular this means that the virtualenv we create, in which all application code runs, is Python 3. One shebang line, on `zulip-ec2-configure-interfaces`, explicitly keeps Python 2, and at least one external ops script, `wal-e`, also still runs on Python 2. See discussion on the respective previous commits that made those explicit. There may also be some other third-party scripts we use, outside of this source tree and running outside our virtualenv, that still run on Python 2.
153 lines
6.0 KiB
Python
Executable File
153 lines
6.0 KiB
Python
Executable File
#!/usr/bin/env python3
|
|
|
|
# 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 Any, Dict, 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: (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 filename in ["package.json", "zproject/settings.py", "tools/minify-js"]:
|
|
print("Changed a core JS pipeline file; not reusing cached minification results")
|
|
return None
|
|
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_tmp = get_changed_source_files(prev_deploy)
|
|
if changed_files_tmp is None:
|
|
prev_deploy = None
|
|
else:
|
|
changed_files = changed_files_tmp
|
|
|
|
# Always use the newly compiled handlebars templates and webpack bundle.
|
|
if prev_deploy:
|
|
changed_files.add(os.path.join(STATIC_PATH, 'templates/compiled.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_pair in six.iteritems(JS_SPECS):
|
|
# JS_SPECS is not typed, so forcefully type keys and values being read from JS_SPECS
|
|
js_group = js_group_filespec_pair[0] # type: str
|
|
filespec = js_group_filespec_pair[1] # type: Dict[str, Any]
|
|
# 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']]
|
|
|
|
min_files = [os.path.join(STATIC_PATH, filename)
|
|
for filename in filespec.get('minifed_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 ('force_minify' not 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:
|
|
# Minified source files (most likely libraries) should be loaded
|
|
# first to prevent any dependency errors.
|
|
for file in min_files:
|
|
with open(file, 'rb') as f:
|
|
fp.write(f.read())
|
|
|
|
fp.write(js)
|