doit overhaul

most notably now we preserve the mtime of the source files so
cachebusted static urls won't change every time everything is
regenerated for whatever reason
This commit is contained in:
codl 2017-08-24 18:43:28 +02:00
parent 092cc1919d
commit 6d8991ee65
No known key found for this signature in database
GPG Key ID: 6CD7C8891ED1233A
1 changed files with 57 additions and 36 deletions

93
dodo.py
View File

@ -1,3 +1,10 @@
from doit import create_after
def reltouch(source_filename, dest_filename):
from os import stat, utime
stat_res = stat(source_filename)
utime(dest_filename, ns=(stat_res.st_atime_ns, stat_res.st_mtime_ns))
def resize_image(basename, width, format): def resize_image(basename, width, format):
from PIL import Image from PIL import Image
with Image.open('assets/{}.png'.format(basename)) as im: with Image.open('assets/{}.png'.format(basename)) as im:
@ -22,9 +29,10 @@ def resize_image(basename, width, format):
optimize = True, optimize = True,
) )
new.save('static/{}-{}.{}'.format(basename, width, format), **kwargs) new.save('static/{}-{}.{}'.format(basename, width, format), **kwargs)
reltouch('assets/{}.png'.format(basename), 'static/{}-{}.{}'.format(basename, width, format))
def task_gen_logo(): def task_logotype():
"""generate versions of the logo in various sizes""" """resize and convert logotype"""
widths = (200, 400, 600, 800) widths = (200, 400, 600, 800)
formats = ('jpeg', 'webp') formats = ('jpeg', 'webp')
for width in widths: for width in widths:
@ -37,7 +45,8 @@ def task_gen_logo():
clean=True, clean=True,
) )
def task_button_icons(): def task_service_icon():
"""resize and convert service icons"""
widths = (20,40,80) widths = (20,40,80)
formats = ('webp', 'png') formats = ('webp', 'png')
for width in widths: for width in widths:
@ -51,20 +60,29 @@ def task_button_icons():
clean=True, clean=True,
) )
def task_copy_asset(): def task_copy():
import shutil "copy assets verbatim"
assets = ('icon.png', 'logotype.png', 'settings.js') assets = ('icon.png', 'logotype.png', 'settings.js')
def do_the_thing(src, dst):
from shutil import copy
copy(src, dst)
reltouch(src, dst)
for asset in assets: for asset in assets:
src = 'assets/{}'.format(asset)
dst = 'static/{}'.format(asset)
yield dict( yield dict(
name=asset, name=asset,
actions=[(lambda asset: shutil.copy(f'assets/{asset}', f'static/{asset}'), (asset,))], actions=[(do_the_thing, (src, dst))],
targets=[f'static/{asset}'], targets=[src],
file_dep=[f'assets/{asset}'], file_dep=[dst],
clean=True, clean=True,
) )
def task_minify_css(): def task_minify_css():
"""minify css""" """minify css file with csscompressor"""
from csscompressor import compress from csscompressor import compress
@ -72,6 +90,7 @@ def task_minify_css():
with open('assets/styles.css') as in_: with open('assets/styles.css') as in_:
with open('static/styles.css', 'w') as out: with open('static/styles.css', 'w') as out:
out.write(compress(in_.read())) out.write(compress(in_.read()))
reltouch('assets/styles.css', 'static/styles.css')
return dict( return dict(
actions=[minify], actions=[minify],
@ -80,48 +99,50 @@ def task_minify_css():
clean=True, clean=True,
) )
@create_after('logotype')
@create_after('service_icon')
@create_after('copy')
@create_after('minify_css')
def task_compress():
"make gzip and brotli compressed versions of each static file for the server to lazily serve"
from glob import glob
from itertools import chain
def cross(a, b): files = chain(
for A in a: glob('static/*.css'),
for B in b: glob('static/*.js'),
yield (A, B) glob('static/*.jpeg'),
glob('static/*.png'),
glob('static/*.webp'),
)
def task_compress_static(): def compress_brotli(filename):
import brotli import brotli
import gzip with open(filename, 'rb') as in_:
with open(filename + '.br', 'wb') as out:
out.write(brotli.compress(in_.read()))
reltouch(filename, filename+'.br')
files = ( def compress_gzip(filename):
'static/styles.css', import gzip
'static/icon.png', with open(filename, 'rb') as in_:
'static/logotype.png', with gzip.open(filename + '.gz', 'wb') as out:
'static/settings.js', out.write(in_.read())
) + tuple((f'static/logotype-{width}.{format}' for width, format in cross((200, 400, 600, 800), ('jpeg','webp')))) reltouch(filename, filename+'.gz')
def compress_brotli(dependencies):
for filename in dependencies:
with open(filename, 'rb') as in_:
with open(filename + '.br', 'wb') as out:
out.write(brotli.compress(in_.read()))
def compress_gzip(dependencies):
for filename in dependencies:
with open(filename, 'rb') as in_:
with gzip.open(filename + '.gz', 'wb') as out:
out.write(in_.read())
for filename in files: for filename in files:
yield dict( yield dict(
file_dep=(filename,), file_dep=(filename,),
targets=(filename+'.br',), targets=(filename+'.br',),
name=filename+'.br', name=filename+'.br',
actions=[compress_brotli], actions=[(compress_brotli, (filename,))],
clean=True, clean=True,
) )
yield dict( yield dict(
file_dep=(filename,), file_dep=(filename,),
targets=(filename+'.gz',), targets=(filename+'.gz',),
name=filename+'.gz', name=filename+'.gz',
actions=[compress_gzip], actions=[(compress_gzip, (filename,))],
clean=True, clean=True,
) )