If you're reading this you're probably familiar with how, in django-pipeline, you define bundles of static files to be combined and served. If you're not familiar with
django-pipeline it's unlike this'll be of much help.
The Challenge (aka. the pitfall)
So you specify bundles by creating things in your
settings.py something like this:
You do a bit more configuration and now, when you run
./manage.py collectstatic --noinput Django and
django-pipeline will gather up all static files from all Django apps installed, then start post processing then and doing things like concatenating them into one file and doing stuff like minification etc.
The problem is, if you look at the example snippet above, there's a typo. Instead of
js/application.js it's accidentally
js/aplication.js. Oh noes!!
What's sad is it that nobody will notice (running
./manage.py collectstatic will exit with a
0). At least not unless you do some careful manual reviewing. Perhaps you will notice later, when you've pushed the site to prod, that the output file
js/stats.js actually doesn't contain the code from
Or, you can automate it!
A Solution (aka. the hack)
I started this work this morning because the error actually happened to us. Thankfully not in production but our staging server produced a rendered HTML page with
<link href="/static/css/report.min.cd784b4a5e2d.css" rel="stylesheet" type="text/css" /> which was an actual file but it was 0 bytes.
It wasn't that hard to figure out what the problem was because of the context of recent changes but it would have been nice to catch this during continuous integration.
So what we did was add an extra class to
myproject.base.finders.LeftoverPipelineFinder. So now it looks like this:
# in settings.py STATICFILES_FINDERS = ( 'django.contrib.staticfiles.finders.FileSystemFinder', 'django.contrib.staticfiles.finders.AppDirectoriesFinder', 'pipeline.finders.PipelineFinder', 'myproject.finders.LeftoverPipelineFinder', # the new hotness! )
And here's the class implementation:
Now, if you have a typo or something in your bundles, you'll get a nice error about it as soon as you try to run
collectstatic. For example:
This was a morning hack. I'm still not entirely sure if this the best approach, but there was none better and the result is pretty good.
./manage.py collectstatic --noinput in our continous integration just before it runs
./manage.py test. So if you make a Pull Request that has a typo in
bundles.py it will get caught.
Unfortunately, it won't find missing files if you use
foo*.js or something like that.
glob.glob to convert expressions like that into a list of actual files and that depends on the filesystem and all of that happens before the
django.contrib.staticfiles.finders.find function is called.
If you have any better suggestions to solve this, please let me know.