mirror of
				https://github.com/zulip/zulip.git
				synced 2025-11-03 21:43:21 +00:00 
			
		
		
		
	
		
			
				
	
	
		
			135 lines
		
	
	
		
			5.2 KiB
		
	
	
	
		
			Python
		
	
	
		
			Executable File
		
	
	
	
	
			
		
		
	
	
			135 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 optparse
 | 
						|
import sys
 | 
						|
import six
 | 
						|
 | 
						|
from typing import Optional, Set
 | 
						|
 | 
						|
parser = optparse.OptionParser()
 | 
						|
parser.add_option('--prev-deploy', nargs=1, metavar='DIR',
 | 
						|
                  help='A previous deploy from which to reuse files if possible')
 | 
						|
(options, args) = parser.parse_args()
 | 
						|
prev_deploy = options.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__), '..'))
 | 
						|
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)
 |