tricu

An interpreted language for exploring Tree Calculus
Log | Files | Refs | README | LICENSE

asset-release.py (2090B)


      1 #!/usr/bin/env python3
      2 """Place a complete static dependency tree under a reproducible release URL."""
      3 import argparse
      4 import hashlib
      5 import json
      6 from pathlib import Path
      7 
      8 
      9 def freeze(root):
     10     root = Path(root)
     11     names = ['dist', 'runtime', 'images', 'reference', 'style.css', 'catalogue.json', 'compiler.json',
     12              'packages.json', 'environments.json']
     13     files = sorted(p for name in names for p in
     14                    ((root / name).rglob('*') if (root / name).is_dir() else [root / name])
     15                    if p.is_file())
     16     rows = [{'path': p.relative_to(root).as_posix(), 'size': p.stat().st_size,
     17              'sha256': hashlib.sha256(p.read_bytes()).hexdigest()} for p in files]
     18     rows.sort(key=lambda row: row['path'])
     19     encoded = json.dumps(rows, sort_keys=True, separators=(',', ':')).encode()
     20     version = hashlib.sha256(b'arboricx.site-assets.v1\0' + encoded).hexdigest()
     21     prefix = 'assets/' + version
     22     html = (root / 'index.html').read_text()
     23     for name in ['dist/app.js', 'style.css', 'images/favicon.png', 'images/icon.svg', 'images/apple-touch-icon.png']:
     24         old = './' + name
     25         if html.count('"' + old + '"') != 1:
     26             raise ValueError('Expected one HTML entrypoint: ' + old)
     27         html = html.replace('"' + old + '"', '"./' + prefix + '/' + name + '"')
     28     html = html.replace('https://arbori.cx/images/preview.png', 'https://arbori.cx/' + prefix + '/images/preview.png')
     29     llms = root / 'llms.txt'
     30     if llms.exists():
     31         llms.write_text(llms.read_text().replace('./reference/', './' + prefix + '/reference/'))
     32     destination = root / prefix
     33     destination.mkdir(parents=True, exist_ok=False)
     34     for name in names:
     35         (root / name).rename(destination / name)
     36     (root / 'index.html').write_text(html)
     37     (root / 'assets.json').write_text(json.dumps(
     38         {'version': version, 'base': prefix + '/', 'files': rows}, indent=2) + '\n')
     39 
     40 
     41 if __name__ == '__main__':
     42     parser = argparse.ArgumentParser(description=__doc__)
     43     parser.add_argument('root', type=Path)
     44     freeze(parser.parse_args().root)