mirror of
https://github.com/quantum5/purge-static.git
synced 2025-04-24 11:22:00 -04:00
127 lines
4.7 KiB
Python
127 lines
4.7 KiB
Python
|
from __future__ import print_function
|
||
|
|
||
|
import argparse
|
||
|
import os
|
||
|
import re
|
||
|
import shelve
|
||
|
import sys
|
||
|
from contextlib import closing
|
||
|
from hashlib import sha256
|
||
|
from textwrap import dedent
|
||
|
|
||
|
from purge_static.cdn import *
|
||
|
|
||
|
|
||
|
def main():
|
||
|
parser = argparse.ArgumentParser(
|
||
|
formatter_class=argparse.RawDescriptionHelpFormatter,
|
||
|
description=dedent('''\
|
||
|
Find changed static files, show their URLs, and optionally purge them
|
||
|
for you on your CDN.
|
||
|
|
||
|
This tool can be used to enable aggressive caching for your static site.
|
||
|
You can cache your entire static site on CDN edge nodes, and then use this
|
||
|
tool to purge all static files that changed on disk, by file hash (SHA256).
|
||
|
|
||
|
Currently, only CloudFlare is supported.
|
||
|
'''))
|
||
|
|
||
|
parser.add_argument('url', help='URL prefix corresponding to --dir')
|
||
|
|
||
|
parser.add_argument('-d', '--dir', default='.',
|
||
|
help='local filesystem path corresponding to --url')
|
||
|
parser.add_argument('-S', '--select',
|
||
|
help='regex to run on file names to select files to be purged, '
|
||
|
'matched from the start of the string')
|
||
|
parser.add_argument('-i', '--ignore',
|
||
|
help='regex to run on file names to ignore files, '
|
||
|
'matched from the start of the string')
|
||
|
parser.add_argument('-I', '--index', action='append',
|
||
|
help='file to consider as directory index (repeatable, default: index.html)')
|
||
|
parser.add_argument('-s', '--store',
|
||
|
help='file to store hashes in (default: $dir/.purge-static)')
|
||
|
parser.add_argument('-D', '--dry-run', action='store_true', help="dry run, don't update hashes")
|
||
|
parser.add_argument('-q', '--quiet', help='reduce output')
|
||
|
|
||
|
cdn_group = parser.add_argument_group(title='CDN options')
|
||
|
cdn_group.add_argument('--cloudflare', dest='cdn', action='store_const', const=CloudFlareCDN,
|
||
|
help='purge files on CloudFlare CDN. need --credentials, which must be '
|
||
|
'a JSON files with two keys, email and api_key, containing your '
|
||
|
'CloudFlare account email and API key. need --zone, which must '
|
||
|
'be your CloudFlare zone ID (the hex code)')
|
||
|
cdn_group.add_argument('-c', '--credentials', help='credentials file path')
|
||
|
cdn_group.add_argument('-z', '--zone', help='zone ID (for CloudFlare)')
|
||
|
|
||
|
args = parser.parse_args()
|
||
|
|
||
|
if args.cdn:
|
||
|
cdn = args.cdn(args)
|
||
|
else:
|
||
|
cdn = None
|
||
|
|
||
|
def select_regex(name):
|
||
|
regex = getattr(args, name)
|
||
|
if regex:
|
||
|
try:
|
||
|
return re.compile(regex)
|
||
|
except re.error:
|
||
|
sys.exit('Invalid regex for %s: %s' % (name, regex))
|
||
|
|
||
|
select = select_regex('select')
|
||
|
ignore = select_regex('ignore')
|
||
|
indexes = args.index or ['index.html']
|
||
|
store = args.store or os.path.join(args.dir, '.purge-static')
|
||
|
|
||
|
url_prefix = args.url
|
||
|
if not url_prefix.endswith('/'):
|
||
|
url_prefix += '/'
|
||
|
|
||
|
urls = []
|
||
|
with closing(shelve.open(store, protocol=2)) as store:
|
||
|
for dirpath, _, filenames in os.walk(args.dir):
|
||
|
relpath = os.path.relpath(dirpath, args.dir)
|
||
|
|
||
|
urlpath = relpath.replace(os.sep, '/')
|
||
|
if os.altsep:
|
||
|
urlpath = urlpath.replace(os.altsep, '/')
|
||
|
if urlpath == os.curdir:
|
||
|
urlpath = ''
|
||
|
else:
|
||
|
urlpath += '/'
|
||
|
|
||
|
changed = set()
|
||
|
for filename in filenames:
|
||
|
if select and not select.match(filename):
|
||
|
continue
|
||
|
if ignore and ignore.match(filename):
|
||
|
continue
|
||
|
|
||
|
path = os.path.join(dirpath, filename)
|
||
|
hasher = sha256()
|
||
|
with open(os.path.join(args.dir, path), 'rb') as f:
|
||
|
for block in iter(lambda: f.read(65536), b''):
|
||
|
hasher.update(block)
|
||
|
|
||
|
if hasher.digest() != store.get(path):
|
||
|
if not args.dry_run:
|
||
|
store[path] = hasher.digest()
|
||
|
urls.append(url_prefix + urlpath + filename)
|
||
|
changed.add(filename)
|
||
|
|
||
|
for index in indexes:
|
||
|
if index in filenames:
|
||
|
if index in changed:
|
||
|
urls.append(url_prefix + urlpath)
|
||
|
break
|
||
|
|
||
|
if cdn:
|
||
|
if urls:
|
||
|
cdn.purge(urls)
|
||
|
if not args.quiet:
|
||
|
print('Success: %d URLs purged' % len(urls))
|
||
|
elif not args.quiet:
|
||
|
print('Nothing to change')
|
||
|
else:
|
||
|
for path in urls:
|
||
|
print(path)
|