2017-11-16 19:51:44 +01:00
|
|
|
# Useful reading is https://zulip.readthedocs.io/en/latest/subsystems/front-end-build-process.html
|
2013-10-10 21:37:26 +02:00
|
|
|
|
2016-07-04 13:33:16 +02:00
|
|
|
import os
|
|
|
|
import shutil
|
2018-06-02 01:59:50 +02:00
|
|
|
from typing import Any, Dict, List, Optional, Tuple
|
2016-07-04 13:33:16 +02:00
|
|
|
|
2013-06-12 00:01:13 +02:00
|
|
|
from django.conf import settings
|
2017-02-06 22:12:03 +01:00
|
|
|
from django.contrib.staticfiles.storage import ManifestStaticFilesStorage
|
2013-06-12 00:01:13 +02:00
|
|
|
from pipeline.storage import PipelineMixin
|
|
|
|
|
2017-02-15 17:28:40 +01:00
|
|
|
from zerver.lib.str_utils import force_str
|
|
|
|
|
2017-11-05 11:53:59 +01:00
|
|
|
class AddHeaderMixin:
|
2017-11-27 07:33:05 +01:00
|
|
|
def post_process(self, paths: Dict[str, Tuple['ZulipStorage', str]], dry_run: bool=False,
|
|
|
|
**kwargs: Any) -> List[Tuple[str, str, bool]]:
|
2013-06-12 00:01:13 +02:00
|
|
|
if dry_run:
|
2016-01-27 02:13:23 +01:00
|
|
|
return []
|
2013-06-12 00:01:13 +02:00
|
|
|
|
2016-10-10 16:13:51 +02:00
|
|
|
with open(settings.STATIC_HEADER_FILE, 'rb') as header_file:
|
2013-06-12 00:01:13 +02:00
|
|
|
header = header_file.read().decode(settings.FILE_CHARSET)
|
|
|
|
|
|
|
|
# A dictionary of path to tuples of (old_path, new_path,
|
|
|
|
# processed). The return value of this method is the values
|
|
|
|
# of this dictionary
|
|
|
|
ret_dict = {}
|
|
|
|
|
|
|
|
for name in paths:
|
|
|
|
storage, path = paths[name]
|
|
|
|
|
Reuse minified JS from previous deploys
This is a big change affecting lots of areas:
* Pipeline no longer deals with JS (though it still minifies CSS)
* A new script, tools/minify-js (called from update-prod-static),
minifies JavaScripts
* A command-line argument --prev-deploy, if passed to minify-js or
update-prod-static, is used to copy minified JS from a previous
deploy (i.e., a previous git checkout), if the source files have
not changed
* update-deployment passes --prev-deploy
* Scripts are now included with the minified_js template tag, rather
than Pipeline's compressed_js
Also, as a side benefit of this commit, our Handlebars templates will
no longer be copied into prod-static/ and accessible in production.
Unminification is probably broken, but, per Zev and Trac ticket #1377,
it wasn't working perfectly before this change either.
(Based on code review, this commit has been revised to:
* Warn if git returns an error in minify-js
* Add missing output redirects in update-prod-static
* Use DEPLOY_ROOT instead of manually constructing that directory
* Use old style formatting)
(imported from commit e67722ea252756db8519d5c0bd6a421d59374185)
2013-07-03 22:42:25 +02:00
|
|
|
if not path.startswith('min/') or not path.endswith('.css'):
|
2013-06-12 00:01:13 +02:00
|
|
|
ret_dict[path] = (path, path, False)
|
|
|
|
continue
|
|
|
|
|
|
|
|
# Prepend the header
|
2016-10-10 16:13:51 +02:00
|
|
|
with storage.open(path, 'rb') as orig_file:
|
2013-06-12 00:01:13 +02:00
|
|
|
orig_contents = orig_file.read().decode(settings.FILE_CHARSET)
|
|
|
|
|
|
|
|
storage.delete(path)
|
|
|
|
|
|
|
|
with storage.open(path, 'w') as new_file:
|
2017-02-15 17:28:40 +01:00
|
|
|
new_file.write(force_str(header + orig_contents, encoding=settings.FILE_CHARSET))
|
2013-06-12 00:01:13 +02:00
|
|
|
|
|
|
|
ret_dict[path] = (path, path, True)
|
|
|
|
|
2017-10-27 08:28:23 +02:00
|
|
|
super_class = super()
|
2013-06-12 00:01:13 +02:00
|
|
|
if hasattr(super_class, 'post_process'):
|
2017-03-05 00:18:18 +01:00
|
|
|
super_ret = super_class.post_process(paths, dry_run, **kwargs) # type: ignore # https://github.com/python/mypy/issues/2956
|
2013-06-12 00:01:13 +02:00
|
|
|
else:
|
|
|
|
super_ret = []
|
|
|
|
|
|
|
|
# Merge super class's return value with ours
|
|
|
|
for val in super_ret:
|
|
|
|
old_path, new_path, processed = val
|
|
|
|
if processed:
|
|
|
|
ret_dict[old_path] = val
|
|
|
|
|
2016-01-25 01:27:18 +01:00
|
|
|
return list(ret_dict.values())
|
2013-06-12 00:01:13 +02:00
|
|
|
|
2013-06-24 21:19:18 +02:00
|
|
|
|
2017-11-05 11:53:59 +01:00
|
|
|
class RemoveUnminifiedFilesMixin:
|
2017-11-27 07:33:05 +01:00
|
|
|
def post_process(self, paths: Dict[str, Tuple['ZulipStorage', str]], dry_run: bool=False,
|
|
|
|
**kwargs: Any) -> List[Tuple[str, str, bool]]:
|
2016-07-04 13:33:16 +02:00
|
|
|
if dry_run:
|
|
|
|
return []
|
|
|
|
|
|
|
|
root = settings.STATIC_ROOT
|
2018-06-01 01:56:16 +02:00
|
|
|
to_remove = ['js']
|
2016-07-04 13:33:16 +02:00
|
|
|
|
|
|
|
for tree in to_remove:
|
|
|
|
shutil.rmtree(os.path.join(root, tree))
|
|
|
|
|
|
|
|
is_valid = lambda p: all([not p.startswith(k) for k in to_remove])
|
|
|
|
|
|
|
|
paths = {k: v for k, v in paths.items() if is_valid(k)}
|
2017-10-27 08:28:23 +02:00
|
|
|
super_class = super()
|
2016-07-04 13:33:16 +02:00
|
|
|
if hasattr(super_class, 'post_process'):
|
2017-03-05 00:18:18 +01:00
|
|
|
return super_class.post_process(paths, dry_run, **kwargs) # type: ignore # https://github.com/python/mypy/issues/2956
|
2016-07-04 13:33:16 +02:00
|
|
|
|
|
|
|
return []
|
|
|
|
|
2018-06-02 01:59:50 +02:00
|
|
|
class IgnoreBundlesManifestStaticFilesStorage(ManifestStaticFilesStorage):
|
|
|
|
def hashed_name(self, name: str, content: Optional[str]=None, filename: Optional[str]=None) -> str:
|
|
|
|
if (name.startswith("webpack-bundles") and
|
|
|
|
os.path.splitext(name)[1] in ['.js', '.css', '.map']):
|
|
|
|
# Hack to avoid renaming already-hashnamed webpack bundles
|
|
|
|
# when minifying; this was causing every bundle to have
|
|
|
|
# two hashes appended to its name, one by webpack and one
|
|
|
|
# here. We can't just skip processing of these bundles,
|
|
|
|
# since we do need the Django storage to add these to the
|
|
|
|
# manifest for django_webpack_loader to work. So, we just
|
|
|
|
# use a no-op hash function for these already-hashed
|
|
|
|
# assets.
|
|
|
|
return name
|
|
|
|
return super().hashed_name(name, content, filename)
|
|
|
|
|
2017-02-06 22:12:03 +01:00
|
|
|
if settings.PRODUCTION:
|
|
|
|
# This is a hack to use staticfiles.json from within the
|
|
|
|
# deployment, rather than a directory under STATIC_ROOT. By doing
|
|
|
|
# so, we can use a different copy of staticfiles.json for each
|
|
|
|
# deployment, which ensures that we always use the correct static
|
|
|
|
# assets for each deployment.
|
|
|
|
ManifestStaticFilesStorage.manifest_name = os.path.join(settings.DEPLOY_ROOT,
|
|
|
|
"staticfiles.json")
|
|
|
|
orig_path = ManifestStaticFilesStorage.path
|
|
|
|
|
2017-12-08 17:03:08 +01:00
|
|
|
def path(self: ManifestStaticFilesStorage, name: str) -> str:
|
2017-02-06 22:12:03 +01:00
|
|
|
if name == ManifestStaticFilesStorage.manifest_name:
|
|
|
|
return name
|
|
|
|
return orig_path(self, name)
|
|
|
|
ManifestStaticFilesStorage.path = path
|
2016-07-04 13:33:16 +02:00
|
|
|
|
|
|
|
class ZulipStorage(PipelineMixin,
|
2016-12-03 00:04:17 +01:00
|
|
|
AddHeaderMixin, RemoveUnminifiedFilesMixin,
|
2018-06-02 01:59:50 +02:00
|
|
|
IgnoreBundlesManifestStaticFilesStorage):
|
2013-06-12 00:01:13 +02:00
|
|
|
pass
|