diff --git a/.gitignore b/.gitignore index 54813d3..c7ac740 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1 @@ -_gen -static/syntax.css \ No newline at end of file +_gen \ No newline at end of file diff --git a/Makefile b/Makefile deleted file mode 100644 index 8ec4397..0000000 --- a/Makefile +++ /dev/null @@ -1,7 +0,0 @@ -all: static/syntax.css - -static/syntax.css: static/light-syntax.css static/dark-syntax.css - cat static/light-syntax.css > $@ - echo '@media (prefers-color-scheme: dark) { ' >> $@ - cat static/dark-syntax.css >> $@ - echo '}' >> $@ diff --git a/README.md b/README.md deleted file mode 100644 index ea46267..0000000 --- a/README.md +++ /dev/null @@ -1,29 +0,0 @@ -# Codigo para llevar's generator - -This is the static site generator used to build [Codigo Para Llevar](https://codigoparallevar.com/) (my personal site). It contains: - -- A markdown blog (with content ported from acrylamid). Saved as `/blog/`. -- A set of org-mode based notes. Saved as `/notes/`. - -It also copies over some static assets (css, js, fonts). - -The scripts are hardcoded with the hostnames and paths for my own site, so you might want to update them. - -General documentation is in progress and might be replaced little by little by the more interactive [org-web-editor](https://code.codigoparallevar.com/kenkeiras/org-web-editor) once that one (1) supports all the features here and (2) has support for building static sites. - -## Instructions - -Generally, what you want to do is to run `make` once to prepare the static files, then run this to generate the notes. - -```bash -mkdir -p _gen -WATCH_AND_REBUILD=0 python3 scripts/generate.py _gen/notes [] -``` - -Use `WATCH_AND_REBUILD=1` (or empty) for automatic rebuilds. - -## Filtering - -This won't render **all** notes, but try to select the PUBLIC ones and skip the PRIVATE ones. - -PUBLIC files are contained on the DEFAULT_SUBPATH, PRIVATE headlines have the `:private:` tag. diff --git a/scripts/autoserve.py b/scripts/autoserve.py index ab6f149..6e69025 100644 --- a/scripts/autoserve.py +++ b/scripts/autoserve.py @@ -8,11 +8,9 @@ import os import time import select -import urllib.parse - import inotify.adapters -PORT = int(os.getenv('PORT', 8000)) +PORT = 8000 THIS_DIR = os.path.dirname(os.path.abspath(__file__)) @@ -57,34 +55,16 @@ class Server(http.server.SimpleHTTPRequestHandler): time.sleep(SLEEP_TIME) return - path = urllib.parse.unquote(self.path) - if path.strip('/') == '': - path = '/index.html' - if os.path.isdir(path.strip('/')): - if path.endswith('/'): - path = path.strip('/') + '/index.html' - else: - # Redirect to + / - self.send_response(301) - self.send_header('Location', path + '/') - self.end_headers() - return - - if not os.path.exists(path.strip('/')): - self.send_response(404) - self.end_headers() - return - # send 200 response self.send_response(200) # send response headers self.end_headers() - with open(path.strip('/'), 'rb') as f: + with open(self.path.strip('/'), 'rb') as f: # send the body of the response self.wfile.write(f.read()) - if not path.endswith('.html'): + if not self.path.endswith('.html'): return else: # Append update waiter diff --git a/scripts/blog.py b/scripts/blog.py index d177201..74a5800 100644 --- a/scripts/blog.py +++ b/scripts/blog.py @@ -12,7 +12,6 @@ MARKDOWN_EXTRA_FEATURES = [ 'markdown.extensions.extra', ] -import copy import json import logging import sys @@ -25,16 +24,12 @@ import re from typing import List from bs4 import BeautifulSoup as bs4 -import bs4 as BeautifulSoup import jinja2 import inotify.adapters import yaml import markdown from unidecode import unidecode -SUMMARIZE_MAX_TOKENS = 1000 -ITEMS_IN_RSS = 50 - NIKOLA_DATE_RE = re.compile(r'^([0-2]\d|30|31)\.(0\d|1[012])\.(\d{4}), (\d{1,2}):(\d{2})$') COMPLETE_DATE_RE = re.compile(r'^(\d{4})-(0\d|1[012])-([0-2]\d|30|31) ' @@ -47,9 +42,6 @@ ROOT_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) STATIC_PATH = os.path.join(ROOT_DIR, 'static') ARTICLE_TEMPLATE_NAME = 'article.tmpl.html' BLOG_INDEX_TEMPLATE_NAME = 'blog_index.tmpl.html' -CATEGORY_LIST_TEMPLATE_NAME = 'category_list.tmpl.html' -ARTICLE_LIST_TEMPLATE_NAME = 'article_list.tmpl.html' -RSS_TEMPLATE_NAME = 'rss.tmpl.xml' BLOG_INDEX_PAGE_SIZE = 10 STATIC_RESOURCES = ( @@ -63,21 +55,11 @@ JINJA_ENV = jinja2.Environment( autoescape=jinja2.select_autoescape() ) -WATCH = True -if os.getenv('WATCH_AND_REBUILD', '1') == '0': - WATCH = False - def update_statics(): global ARTICLE_TEMPLATE ARTICLE_TEMPLATE = JINJA_ENV.get_template(ARTICLE_TEMPLATE_NAME) global BLOG_INDEX_TEMPLATE BLOG_INDEX_TEMPLATE = JINJA_ENV.get_template(BLOG_INDEX_TEMPLATE_NAME) - global CATEGORY_LIST_TEMPLATE - CATEGORY_LIST_TEMPLATE = JINJA_ENV.get_template(CATEGORY_LIST_TEMPLATE_NAME) - global ARTICLE_LIST_TEMPLATE - ARTICLE_LIST_TEMPLATE = JINJA_ENV.get_template(ARTICLE_LIST_TEMPLATE_NAME) - global RSS_TEMPLATE - RSS_TEMPLATE = JINJA_ENV.get_template(RSS_TEMPLATE_NAME) update_statics() @@ -91,7 +73,6 @@ MONITORED_EVENT_TYPES = ( 'IN_DELETE_SELF', 'IN_MOVE_SELF', ) -LANG_PRIORITY = ('en', 'es', 'gl') def parse_nikola_date(match): @@ -126,7 +107,6 @@ def slugify(title): slug = unidecode(title).lower() slug = SLUG_REMOVE_RE.sub('', slug) slug = SLUG_HYPHENATE_RE.sub('-', slug) - slug = slug.strip('-') return slug.strip() @@ -171,8 +151,6 @@ def get_out_path(front_matter): front_matter['slug'] = slugify(front_matter['title']) out_path = os.path.join(str(front_matter['date'].year), front_matter['slug']) - if front_matter.get('lang', LANG_PRIORITY[0]) != LANG_PRIORITY[0]: - out_path = os.path.join(front_matter['lang'], str(front_matter['date'].year), front_matter['slug']) return out_path @@ -181,7 +159,6 @@ def load_all(top_dir_relative): docs = {} - count = 0 for root, dirs, files in os.walk(top): for name in files: if all([not name.endswith(ext) for ext in EXTENSIONS]): @@ -193,12 +170,9 @@ def load_all(top_dir_relative): doc, front_matter = read_markdown(path) out_path = get_out_path(front_matter) docs[path] = (doc, front_matter, out_path) - print('\rLoading posts... {}'.format(count), end='', flush=True) - count += 1 else: raise NotImplementedError('Unknown filetype: {}'.format(name)) - print(" [DONE]") return docs @@ -208,121 +182,22 @@ def load_doc(filepath): return (doc, front_matter, out_path) -def render_article(doc, front_matter, f, out_path): - extsep = '/' if '/' in out_path else '\\' - subdirs = len(out_path.split(extsep)) - base_path = os.path.join(*(['..'] * subdirs)) +def render_article(doc, front_matter, f): result = ARTICLE_TEMPLATE.render( content=doc, title=front_matter['title'], post_publication_date=front_matter['date'], post_tags=split_tags(front_matter['tags']), - base_path=base_path, ) f.write(result) def summarize(doc): - tree = bs4(doc, features='lxml') - - html = list(tree.children)[0] - body = list(html.children)[0] - - comments = tree.find_all(string=lambda text: isinstance(text, BeautifulSoup.Comment)) - - teaser_end = None - for comment in comments: - if 'TEASER_END' in comment: - teaser_end = comment - break - - if 'gnucash' in doc: - assert teaser_end is not None - - def recur_select_to_summarize(source, dest, num_tokens): - for item in source.children: - if num_tokens + len(item.text) < SUMMARIZE_MAX_TOKENS: - # All source fits - num_tokens += len(item.text) - dest.append(item) - - else: - if not isinstance(item, BeautifulSoup.NavigableString): - # Let's take as much source as we can and then stop - subsect = bs4() - recur_select_to_summarize(item, subsect, num_tokens) - - if len(list(subsect.children)) > 0: - dest.append(subsect) - break - - def cut_after_element(reference): - while reference.next_sibling is None: - if reference.parent is None: - logging.warning("Reached root when looking for cutting point for teaser. Doc: {}".format(doc[:100])) - return - reference = reference.parent - - nxt = reference.next_sibling - while nxt is not None: - was = nxt - if reference.next_sibling is not None: - # Move to the "right" - nxt = reference.next_sibling - - else: - # Move "up and right" - nxt = reference.parent - if nxt is not None: - nxt = nxt.next_sibling - was.extract() - - if teaser_end is None: - result = bs4() - - recur_select_to_summarize(body, result, 0) - else: - summary = copy.copy(body) - comments = summary.find_all(string=lambda text: isinstance(text, BeautifulSoup.Comment)) - - teaser_end = None - for comment in comments: - if 'TEASER_END' in comment: - teaser_end = comment - break - assert teaser_end is not None, 'Error finding teaser end on copy' - - cut_after_element(teaser_end) - result = bs4() - for child in summary.children: - result.append(child) - - return result + return bs4(doc, features='lxml').text[:1000] def render_index(docs, dest_top): - # Collect all languages accepted for all docs - docs_by_slug = {} - for (doc, front_matter, out_path) in docs.values(): - if front_matter['slug'] not in docs_by_slug: - docs_by_slug[front_matter['slug']] = {} - docs_by_slug[front_matter['slug']][front_matter.get('lang', LANG_PRIORITY[0])] = (doc, front_matter, out_path) + docs = sorted(docs.values(), key=lambda x: x[1]['date'], reverse=True) - # Remove duplicated for langs with less priority - selected_docs = [] - for (doc, front_matter, out_path) in docs.values(): - langs = docs_by_slug[front_matter['slug']] - lang_priority = LANG_PRIORITY.index(front_matter.get('lang', LANG_PRIORITY[0])) - min_lang_priority = min([ - LANG_PRIORITY.index(lang) - for lang in langs.keys() - ]) - if lang_priority == min_lang_priority: - selected_docs.append((doc, front_matter, out_path, langs)) - - docs = sorted(selected_docs, key=lambda x: x[1]['date'], reverse=True) - - index_ranges = range(0, len(docs), BLOG_INDEX_PAGE_SIZE) - - for off in index_ranges: + for off in range(0, len(docs), BLOG_INDEX_PAGE_SIZE): page = docs[off: off + BLOG_INDEX_PAGE_SIZE] posts = [ @@ -332,22 +207,12 @@ def render_index(docs, dest_top): "post_publication_date": front_matter['date'], "post_tags": split_tags(front_matter['tags']), "summary": summarize(doc), - "link": out_path.rstrip('/') + '/', } - for (doc, front_matter, out_path, _alternatives) in page + for (doc, front_matter, out_path) in page ] - prev_index_num = None - next_index_num = off // BLOG_INDEX_PAGE_SIZE + 1 - if off > 0: - prev_index_num = off // BLOG_INDEX_PAGE_SIZE - 1 - if next_index_num >= len(index_ranges): - next_index_num = None - result = BLOG_INDEX_TEMPLATE.render( posts=posts, - prev_index_num=prev_index_num, - next_index_num=next_index_num, ) if off == 0: @@ -357,104 +222,6 @@ def render_index(docs, dest_top): with open(os.path.join(dest_top, fname), 'wt') as f: f.write(result) -def render_categories(docs, dest_top): - categories = {} - for (doc, front_matter, out_path) in docs.values(): - for tag in split_tags(front_matter['tags']): - if tag not in categories: - categories[tag] = [] - categories[tag].append((doc, front_matter, out_path)) - - print("Found {} tags".format(len(categories), categories)) - for tag, docs in categories.items(): - docs = sorted(docs, key=lambda x: x[1]['date'], reverse=True) - - posts = [ - { - # "doc": doc, - "title": front_matter['title'], - "post_publication_date": front_matter['date'], - "post_tags": split_tags(front_matter['tags']), - # "summary": summarize(doc), - "link": out_path.rstrip('/') + '/', - } - for (doc, front_matter, out_path) in docs - ] - - result = CATEGORY_LIST_TEMPLATE.render( - posts=posts, - ) - path = os.path.join(dest_top, "tags", tag.replace('/', '_'), "index.html") - os.makedirs(os.path.dirname(path), exist_ok=True) - with open(path, 'wt') as f: - f.write(result) - -def render_archive(docs, dest_top): - docs = sorted(docs.values(), key=lambda x: x[1]['date'], reverse=True) - - posts = [ - { - # "doc": doc, - "title": front_matter['title'], - "post_publication_date": front_matter['date'], - "post_tags": split_tags(front_matter['tags']), - # "summary": summarize(doc), - "link": out_path.rstrip('/') + '/', - } - for (doc, front_matter, out_path) in docs - ] - - result = ARTICLE_LIST_TEMPLATE.render( - posts=posts, - ) - path = os.path.join(dest_top, "articles", "index.html") - os.makedirs(os.path.dirname(path), exist_ok=True) - with open(path, 'wt') as f: - f.write(result) - -def render_rss(docs, dest_top): - # Collect all languages accepted for all docs - docs_by_slug = {} - for (doc, front_matter, out_path) in docs.values(): - if front_matter['slug'] not in docs_by_slug: - docs_by_slug[front_matter['slug']] = {} - docs_by_slug[front_matter['slug']][front_matter.get('lang', LANG_PRIORITY[0])] = (doc, front_matter, out_path) - - # Remove duplicated for langs with less priority - selected_docs = [] - for (doc, front_matter, out_path) in docs.values(): - langs = docs_by_slug[front_matter['slug']] - lang_priority = LANG_PRIORITY.index(front_matter.get('lang', LANG_PRIORITY[0])) - min_lang_priority = min([ - LANG_PRIORITY.index(lang) - for lang in langs.keys() - ]) - if lang_priority == min_lang_priority: - selected_docs.append((doc, front_matter, out_path, langs)) - - docs = sorted(selected_docs, key=lambda x: x[1]['date'], reverse=True) - - posts = [ - { - # "doc": doc, - "title": front_matter['title'], - "post_publication_date": front_matter['date'], - "post_tags": split_tags(front_matter['tags']), - "summary": summarize(doc), - "link": out_path.rstrip('/') + '/', - } - for (doc, front_matter, out_path, langs) in docs[:ITEMS_IN_RSS] - ] - - result = RSS_TEMPLATE.render( - posts=posts, - last_build_date=datetime.datetime.utcnow(), - ) - path = os.path.join(dest_top, "rss.xml") - os.makedirs(os.path.dirname(path), exist_ok=True) - with open(path, 'wt') as f: - f.write(result) - def regen_all(source_top, dest_top, docs=None): if docs is None: @@ -465,11 +232,9 @@ def regen_all(source_top, dest_top, docs=None): doc_full_path = os.path.join(dest_top, out_path) os.makedirs(os.path.dirname(doc_full_path), exist_ok=True) # print("==", doc_full_path) - full_out_path = doc_full_path + '/index.html' - os.makedirs(os.path.dirname(full_out_path), exist_ok=True) - with open(full_out_path, 'wt') as f: + with open(doc_full_path + '.html', 'wt') as f: try: - render_article(doc, front_matter, f, out_path) + render_article(doc, front_matter, f) except: logging.error(traceback.format_exc()) logging.error("Rendering failed 😿") @@ -495,15 +260,6 @@ def regen_all(source_top, dest_top, docs=None): # Render index render_index(docs, dest_top) - # Render categories - render_categories(docs, dest_top) - - # Render archive - render_archive(docs, dest_top) - - # Render RSS - render_rss(docs, dest_top) - return docs @@ -516,10 +272,6 @@ def main(source_top, dest_top): docs = regen_all(source_top, dest_top) logging.info("Initial load completed in {:.2f}s".format(time.time() - t0)) - if not WATCH: - logging.info("Build completed in {:.2f}s".format(time.time() - t0)) - return 0 - ## Updating for event in notifier.event_gen(yield_nones=False): (ev, types, directory, file) = event @@ -562,7 +314,6 @@ def main(source_top, dest_top): else: try: - print("Reloading: {}".format(filepath)) (doc, front_matter, out_path) = load_doc(filepath) except: logging.error(traceback.format_exc()) @@ -572,13 +323,11 @@ def main(source_top, dest_top): t0 = time.time() docs[filepath] = (doc, front_matter, out_path) doc_full_path = os.path.join(dest_top, out_path) - print("Updated: {}.html".format(doc_full_path)) - os.makedirs(os.path.dirname(doc_full_path + '/index.html'), exist_ok=True) + os.makedirs(os.path.dirname(doc_full_path), exist_ok=True) # print("==", doc_full_path) - with open(doc_full_path + '/index.html', 'wt') as f: + with open(doc_full_path + '.html', 'wt') as f: try: - render_article(doc, front_matter, f, out_path) - render_archive(docs, dest_top) + render_article(doc, front_matter, f) except: logging.error(traceback.format_exc()) logging.error("Rendering failed 😿") diff --git a/scripts/gen_centered_graph.py b/scripts/gen_centered_graph.py deleted file mode 100644 index 05692b1..0000000 --- a/scripts/gen_centered_graph.py +++ /dev/null @@ -1,165 +0,0 @@ -import subprocess -import ops_cache -import copy -import tempfile -import os - - -@ops_cache.cache -def gen(headline_id, graph, doc_to_headline_remapping): - reference_node = headline_id - font_name = 'monospace' - - linked_from_internal = set() - g = copy.deepcopy(graph) - - if 'id:' + reference_node in doc_to_headline_remapping: - reference_node = doc_to_headline_remapping['id:' + reference_node].split(':', 1)[1] - - centered_graph = { reference_node: g[reference_node] } - for l in g[reference_node]['links']: - lt = l['target'] - if lt.startswith("id:"): - lt = lt[3:] - linked_from_internal.add(lt) - del g[reference_node] - new_nodes = True - - in_emacs_tree = { - reference_node: set(), - } - - while new_nodes: - new_nodes = False - removed = set() - for k, v in g.items(): - if 'id:' + k in doc_to_headline_remapping: - k = doc_to_headline_remapping['id:' + k].split(':', 1)[1] - - for link in v["links"]: - if link["target"].startswith("id:"): - link["target"] = link["target"][3:] - if link['target'] in centered_graph and link.get('relation') == 'in': - centered_graph[k] = v - - for l in v["links"]: - if l.get('relation') == 'in': - t = l['target'] - if t.startswith("id:"): - t = t[3:] - - if '[' in t: - # Special case, to be handled on org_rw - continue - - if t not in in_emacs_tree: - in_emacs_tree[t] = set() - in_emacs_tree[t].add(k) - - v['links'] = [ - l for l in v["links"] - if l.get('relation') != 'in' - ] - for l in v['links']: - lt = l['target'] - if lt.startswith("id:"): - lt = lt[3:] - linked_from_internal.add(lt) - - removed.add(k) - new_nodes = True - break - for k in removed: - del g[k] - - in_emacs = set(centered_graph.keys()) - - # One more round for the rest, not requiring "in" - for k, v in g.items(): - if 'id:' + k in doc_to_headline_remapping: - k = doc_to_headline_remapping['id:' + k].split(':', 1)[1] - - backlinked = False - for link in v["links"]: - if link["target"].startswith("id:"): - link["target"] = link["target"][3:] - if link['target'] in in_emacs: - centered_graph[k] = v - backlinked = True - removed.add(k) - if not backlinked and (k in linked_from_internal): - centered_graph[k] = v - removed.add(k) - - g = centered_graph - - with tempfile.NamedTemporaryFile(suffix='.dot', mode='wt') as f: - f.write('strict digraph {\n') - f.write('maxiter=10000\n') - f.write('splines=curved\n') - # f.write('splines=spline\n') # Not supported with edges to cluster - f.write('node[shape=rect, width=0.5, height=0.5]\n') - f.write('K=0.3\n') - f.write('edge[len = 1]\n') - def draw_subgraph(node_id, depth): - f.write("subgraph cluster_{} {{\n".format(node_id.replace("-", "_"))) - f.write(' URL="./{}.node.html"\n'.format(node_id)) - f.write(' class="{}"\n'.format('cluster-depth-' + str(depth - 1))) - f.write(" fontname=\"{}\"\n".format(font_name)) - f.write(" label=\"{}\"\n".format(g[node_id]['title'].replace("\"", "'"))) - f.write("\n") - - # print("T: {}".format(in_emacs_tree), file=sys.stderr) - for k in in_emacs_tree[node_id]: - v = g[k] - - if k in in_emacs_tree: - draw_subgraph(k, depth=depth + 1) - else: - print(" _" + k.replace("-", "_") - + "[label=\"" + v["title"].replace("\"", "'") + "\", " - + "URL=\"" + k + ".node.html\", " - + "fontname=\"" + font_name + "\", " - + "class=\"cluster-depth-" + str(depth) + "\"" - + "];", file=f) - - - f.write("\n}\n") - - draw_subgraph(reference_node, 1) - - for k, v in g.items(): - if k not in in_emacs: - print("_" + k.replace("-", "_") - + "[label=\"" + v["title"].replace("\"", "'") + "\", " - + "fontname=\"" + font_name + "\", " - + "URL=\"" + k + ".node.html\"];", file=f) - - for k, v in g.items(): - link_src = '_' + k.replace("-", "_") - if k in in_emacs_tree: - link_src = 'cluster_{}'.format(k.replace("-", "_")) - - for link in v["links"]: - if link["target"].startswith("id:"): - link["target"] = link["target"][3:] - - if '[' in link['target']: - # Special case, to be handled on org_rw - continue - if link['target'] not in g: - # Irrelevant - continue - if link['target'] in in_emacs_tree: - t = 'cluster_{}'.format(link['target'].replace("-", "_")) - else: - t = "_" + link["target"].replace("-", "_") - print(link_src + "->" + t, file=f) - - f.write('}\n') - f.flush() - - with tempfile.NamedTemporaryFile(suffix='.svg') as fsvg: - subprocess.call(['fdp', f.name, '-Tsvg', '-o', fsvg.name]) - fsvg.seek(0) - return fsvg.read().decode() diff --git a/scripts/generate.py b/scripts/generate.py index 12bac97..3a6d2f0 100644 --- a/scripts/generate.py +++ b/scripts/generate.py @@ -1,7 +1,5 @@ #!/usr/bin/env python3 -import sqlite3 -import time import json import html import logging @@ -9,12 +7,6 @@ import os import sys import uuid from datetime import datetime -import traceback -import re -from itertools import chain -import shutil - -import inotify.adapters import org_rw from org_rw import OrgTime, dom, Link @@ -22,79 +14,12 @@ from org_rw import dump as dump_org from org_rw import load as load_org from org_rw import token_list_to_raw -import pygments -import pygments.lexers -import pygments.formatters -import gen_centered_graph - -# Set custom states -for state in ("NEXT", "MEETING", "Q", "PAUSED", "SOMETIME", "TRACK", "WAITING"): - org_rw.DEFAULT_TODO_KEYWORDS.append(state) - -for state in ("DISCARDED", "VALIDATING"): - org_rw.DEFAULT_DONE_KEYWORDS.append(state) - EXTENSIONS = [ ".org", ".org.txt", ] -IMG_EXTENSIONS = set([ - "svg", - "png", - "jpg", - "jpeg", - "gif", -]) -SKIPPED_TAGS = set(['attach']) -DEFAULT_SUBPATH = "public" - -WATCH = True -if os.getenv('WATCH_AND_REBUILD', '1') == '0': - WATCH = False MIN_HIDDEN_HEADLINE_LEVEL = 2 -INDEX_ID = os.getenv("INDEX_ID", "ea48ec1d-f9d4-4fb7-b39a-faa7b6e2ba95") -SITE_NAME = "Código para llevar" - -MONITORED_EVENT_TYPES = ( - 'IN_CREATE', - # 'IN_MODIFY', - 'IN_CLOSE_WRITE', - 'IN_DELETE', - 'IN_MOVED_FROM', - 'IN_MOVED_TO', - 'IN_DELETE_SELF', - 'IN_MOVE_SELF', -) - -TEXT_OR_LINK_RE = re.compile(r'([^\s\[\]]+|.)') - -ROOT_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) - -STATIC_PATH = os.path.join(ROOT_DIR, 'static') - -class NonExistingLocalNoteError(AssertionError): - def __init__(self, note_id, src_headline): - AssertionError.__init__(self) - self.note_id = note_id - self.src_headline = src_headline - - def get_message(self): - return ("Cannot follow link to '{}' on headline '{}' ({})" - .format(self.note_id, - self.src_headline.id, - self.src_headline.title.get_text().strip())) - -def is_git_path(path): - return any([chunk == ".git" for chunk in path.split(os.sep)]) - -def create_db(path): - if os.path.exists(path): - os.unlink(path) - - db = sqlite3.connect(path) - db.execute('CREATE VIRTUAL TABLE note_search USING fts5(note_id, title, body, top_level_title, is_done, is_todo, tokenize="trigram");') - return db def load_all(top_dir_relative): top = os.path.abspath(top_dir_relative) @@ -109,9 +34,7 @@ def load_all(top_dir_relative): path = os.path.join(root, name) try: - doc = load_org(open(path), - environment={"org-todo-keywords": "TODO(t) NEXT(n) MEETING(m/!) Q(q) PAUSED(p!/!) EVENT(e/!) SOMETIME(s) WAITING(w@/!) TRACK(r/!) | DISCARDED(x@/!) VALIDATING(v!/!) DONE(d!/!)"}, - extra_cautious=True) + doc = load_org(open(path), extra_cautious=True) docs.append(doc) except Exception as err: import traceback @@ -123,63 +46,26 @@ def load_all(top_dir_relative): logging.info("Collected {} files".format(len(docs))) return docs -def remove_non_public_headlines(doc: org_rw.OrgDoc | org_rw.Headline): - if isinstance(doc, org_rw.OrgDoc): - doc.headlines = list(filter_private_headlines(doc.headlines)) - for hl in doc.headlines: - remove_non_public_headlines(hl) - else: - doc.children = list(filter_private_headlines(doc.children)) - for hl in doc.children: - remove_non_public_headlines(hl) - -def filter_private_headlines(headlines): - for hl in headlines: - if 'private' not in hl.tags: - yield hl - -def regen_all(src_top, dest_top, subpath, *, docs=None, db=None): - files_generated = 0 - cur = db.cursor() - cleaned_db = False - - try: - cur.execute('DELETE FROM note_search;') - cleaned_db = True - except sqlite3.OperationalError as err: - if WATCH: - logging.warning("Error pre-cleaning DB, search won't be updated") - else: - raise +def main(src_top, dest_top): docs = load_all(src_top) - base_dirs = set() + files_generated = 0 + doc_to_headline_remapping = {} os.makedirs(dest_top, exist_ok=True) - - ## Build headline list - # This includes a virtual headline for ID-referenced documents. - all_headlines = [] - main_headlines_by_path = {} - main_headline_to_docid = {} + graph = {} for doc in docs: relpath = os.path.relpath(doc.path, src_top) - - remove_non_public_headlines(doc) changed = False headlines = list(doc.getAllHeadlines()) related = None - if not relpath.startswith(subpath + "/"): - # print("Skip:", relpath) - continue - base_dirs.add(os.path.dirname(relpath)) i = len(headlines) while i > 0: i -= 1 headline = headlines[i] - if headline.title.get_text().strip().lower() == "related" and headline.depth == 1: + if headline.title.strip().lower() == "related" and headline.depth == 1: if related is not None: print( "Found duplicated related: {} vs {}".format( @@ -199,7 +85,10 @@ def regen_all(src_top, dest_top, subpath, *, docs=None, db=None): print("Updated", relpath) save_changes(doc) - all_headlines.extend(headlines) + if not relpath.startswith("public/"): + # print("Skip:", relpath) + continue + main_headline = None topHeadlines = doc.getTopHeadlines() @@ -207,11 +96,13 @@ def regen_all(src_top, dest_top, subpath, *, docs=None, db=None): or (len(topHeadlines) == 2 and related is not None)): main_headline = [h for h in topHeadlines if h != related][0] - main_headlines_by_path[doc.path] = main_headline if doc.id is not None: - doc_to_headline_remapping['id:' + doc.id] = 'id:' + main_headline.id - main_headline_to_docid[main_headline.id] = doc.id - files_generated += 1 + endpath = os.path.join(dest_top, doc.id + ".node.html") + with open(endpath, "wt") as f: + doc_to_headline_remapping['id:' + doc.id] = 'id:' + main_headline.id + + f.write(as_document(render(main_headline, doc, headlineLevel=0))) + files_generated += 1 elif doc.id is not None: logging.error("Cannot render document from id: {}. {} headlines {} related".format( relpath, @@ -219,78 +110,51 @@ def regen_all(src_top, dest_top, subpath, *, docs=None, db=None): 'with' if related is not None else 'without' )) - # Build graph - graph = {} - backlink_graph = {} - for headline in all_headlines: - links = [] - headline_links = list(headline.get_links()) - if headline == main_headline and related is not None: - headline_links.extend(list(related.get_links())) + for headline in headlines: + endpath = os.path.join(dest_top, headline.id + ".node.html") - for l in headline_links: - if l.value.startswith('http://') or l.value.startswith('https://'): - pass # Ignore for now, external URL - elif l.value.startswith('id:'): - links.append({'target': l.value}) - elif l.value.startswith('attachment:'): - pass # Ignore, attachment - elif l.value.startswith('file:'): - pass # Ignore, attachment - elif l.value.startswith('notmuch:'): - pass # Ignore, mail - elif l.value.startswith('orgit-rev:'): - pass # Ignore, mail - elif l.value.startswith('*'): - pass # Ignore, internal - elif not ':' in l.value.split()[0]: - pass # Ignore, internal - elif l.value.startswith('./'): - pass # TODO: Properly handle - else: - logging.warning('On document {}, unknown link to {}'.format(doc.path, l.value)) + links = [] + headline_links = list(headline.get_links()) + if headline == main_headline and related is not None: + headline_links.extend(list(related.get_links())) - if headline.parent: - if isinstance(headline.parent, org_rw.Headline): - links.append({ - "target": headline.parent.id, - "relation": "in" - }) - for backlink in links: - if 'relation' in backlink and backlink['relation'] == 'in': - continue + for l in headline_links: + if l.value.startswith('http://') or l.value.startswith('https://'): + pass # Ignore for now, external URL + elif l.value.startswith('id:'): + links.append({'target': l.value}) + elif l.value.startswith('attachment:'): + pass # Ignore, attachment + elif l.value.startswith('file:'): + pass # Ignore, attachment + elif l.value.startswith('notmuch:'): + pass # Ignore, mail + elif l.value.startswith('orgit-rev:'): + pass # Ignore, mail + elif l.value.startswith('*'): + pass # Ignore, internal + elif not ':' in l.value.split()[0]: + pass # Ignore, internal + elif l.value.startswith('./'): + pass # TODO: Properly handle + else: + logging.warning('On document {}, unknown link to {}'.format(doc.path, l.value)) - target = backlink['target'] - if target.startswith('id:'): - target = target[len('id:'):] + if headline.parent: + if isinstance(headline.parent, org_rw.Headline): + links.append({ + "target": headline.parent.id, + "relation": "in" + }) + graph[headline.id] = { + "title": headline.title.strip(), + "links": links, + "depth": headline.depth, + } - if target not in backlink_graph: - backlink_graph[target] = set() - - backlink_graph[target].add(headline.id) - - graph[headline.id] = { - "title": org_rw.org_rw.token_list_to_plaintext(headline.title.contents).strip(), - "links": links, - "depth": headline.depth, - } - if headline.id in main_headline_to_docid: - graph[main_headline_to_docid[headline.id]] = graph[headline.id] - - topLevelHeadline = headline - while isinstance(topLevelHeadline.parent, org_rw.Headline): - topLevelHeadline = topLevelHeadline.parent - - # Save for full-text-search - cur.execute('''INSERT INTO note_search(note_id, title, body, top_level_title, is_done, is_todo) VALUES (?, ?, ?, ?, ?, ?);''', - ( - headline.id, - headline.title.get_text(), - '\n'.join(headline.doc.dump_headline(headline, recursive=False)), - topLevelHeadline.title.get_text(), - headline.is_done, - headline.is_todo, - )) + with open(endpath, "wt") as f: + f.write(as_document(render(headline, doc, headlineLevel=0))) + files_generated += 1 # Update graph, replace document ids with headline ids for headline_data in graph.values(): @@ -298,17 +162,6 @@ def regen_all(src_top, dest_top, subpath, *, docs=None, db=None): if link['target'] in doc_to_headline_remapping: link['target'] = doc_to_headline_remapping[link['target']] - # Remap document ids backlinks to main headlines - for doc_id, main_headline_id in doc_to_headline_remapping.items(): - if doc_id.startswith('id:'): - doc_id = doc_id[len('id:'):] - if main_headline_id.startswith('id:'): - main_headline_id = main_headline_id[len('id:'):] - for backlink in backlink_graph.get(doc_id, []): - if main_headline_id not in backlink_graph: - backlink_graph[main_headline_id] = set() - backlink_graph[main_headline_id].add(backlink) - # Output graph files graphpath = os.path.join(dest_top, "graph.json") graph_explorer_path = os.path.join(dest_top, "graph.html") @@ -322,485 +175,137 @@ def regen_all(src_top, dest_top, subpath, *, docs=None, db=None): json.dumps(graph))) logging.info("Generated {} files".format(files_generated)) - # Render docs after we've built the graph - # Render main headlines - full_graph_info = { "nodes": graph, "backlinks": backlink_graph, "main_headlines": main_headlines_by_path } - for _docpath, main_headline in main_headlines_by_path.items(): - if main_headline.doc.id: - endpath = os.path.join(dest_top, main_headline.doc.id + ".node.html") - with open(endpath, "wt") as f: - f.write(render_as_document(main_headline, main_headline.doc, headlineLevel=0, graph=full_graph_info, - doc_to_headline_remapping=doc_to_headline_remapping, - title=org_rw.token_list_to_plaintext(main_headline.title.contents))) - # Render all headlines - for headline in all_headlines: - endpath = os.path.join(dest_top, headline.id + ".node.html") - - # Render HTML - with open(endpath, "wt") as f: - f.write(render_as_document(headline, headline.doc, headlineLevel=0, graph=full_graph_info, - doc_to_headline_remapping=doc_to_headline_remapping, - title=org_rw.token_list_to_plaintext(headline.title.contents))) - files_generated += 1 - - if headline.id == INDEX_ID: - index_endpath = os.path.join(dest_top, "index.html") - with open(index_endpath, "wt") as f: - f.write(render_as_document(headline, headline.doc, headlineLevel=0, graph=full_graph_info, - doc_to_headline_remapping=doc_to_headline_remapping, - title=org_rw.token_list_to_plaintext(headline.title.contents))) - files_generated += 1 - - cur.close() - db.commit() - - logging.info("Copying attachments") - attachments_dir = os.path.join(dest_top, 'attachments') - os.makedirs(attachments_dir, exist_ok=True) - for base in base_dirs: - data_dir = os.path.join(src_top, base, 'data') - logging.info("Copying attachments from: {}".format(data_dir)) - if not os.path.exists(data_dir): - continue - for subdir in os.listdir(data_dir): - shutil.copytree(os.path.join(data_dir, subdir), - os.path.join(attachments_dir, subdir), - dirs_exist_ok=True) - - -def main(src_top, dest_top, subpath): - notifier = inotify.adapters.InotifyTrees([src_top, STATIC_PATH]) - - ## Initial load - t0 = time.time() - - os.makedirs(dest_top, exist_ok=True) - db = create_db(os.path.join(dest_top, 'db.sqlite3')) - docs = regen_all(src_top, dest_top, subpath=subpath, db=db) - - if not WATCH: - logging.info("Build completed in {:.2f}s".format(time.time() - t0)) - return 0 - - logging.info("Initial load completed in {:.2f}s".format(time.time() - t0)) - ## Updating - for event in notifier.event_gen(yield_nones=False): - (ev, types, directory, file) = event - if not any([type in MONITORED_EVENT_TYPES for type in types]): - continue - if is_git_path(directory): - continue - filepath = os.path.join(directory, file) - print("CHANGED: {}".format(filepath)) - t0 = time.time() - try: - docs = regen_all(src_top, dest_top, subpath=subpath, docs=docs, db=db) - except: - logging.error(traceback.format_exc()) - logging.error("Loading new templates failed 😿") - continue - logging.info("Updated all in {:.2f}s".format(time.time() - t0)) - -def get_headline_with_name(target_name, doc): - target_name = target_name.strip() - for headline in doc.getAllHeadlines(): - if headline.title.get_text().strip() == target_name: - return headline - - return None - -def assert_id_exists(id, src_headline, graph): - if id not in graph["nodes"]: - raise NonExistingLocalNoteError(id, src_headline) - -def print_tree(tree, indentation=0, headline=None): - # if headline and headline.id != INDEX_ID: - # return +def print_tree(tree, indentation=0): return for element in tree: + print(" " * indentation + "- " + str(type(element))) if "children" in dir(element): if len(element.children) > 0: - print_element(element.children, indentation + 1, headline) + print_tree(element.children, indentation + 1) print() - elif "content" in dir(element): - for content in element.content: - print_element(content, indentation + 1, headline) -def print_element(element, indentation, headline): - if isinstance(element, org_rw.Link): - print(" " * indentation, "Link:", element.get_raw()) - elif isinstance(element, str): - print(" " * indentation, "{" + element + "}", type(element)) - else: - print_tree(element, indentation, headline) - - -def render_property_drawer(element, acc, headline, graph): +def render_property_drawer(element, acc): pass -def render_logbook_drawer(element, acc, headline, graph): +def render_logbook_drawer(element, acc): pass -def render_property_node(element, acc, headline, graph): +def render_property_node(element, acc): pass -def render_list_group(element, acc, headline, graph): +def render_list_group(element, acc): acc.append("") -def render_table(element, acc, headline, graph): - acc.append("") - render_tree(element.children, acc, headline, graph) - acc.append("
") -def render_table_row(element, acc, headline, graph): - acc.append("") - for cell in element.cells: - acc.append("") - acc.append(html.escape(cell)) - acc.append("") - acc.append("") - -def render_table_separator_row(element, acc, headline, graph): - acc.append("") - -def render_list_item(element, acc, headline, graph): +def render_list_item(element, acc): acc.append("
  • ") if element.tag is not None: acc.append("") - render_text_tokens(element.tag, acc, headline, graph) + acc.append(html.escape(element.tag)) acc.append("") acc.append("") - render_text_tokens(element.content, acc, headline, graph) + render_text_tokens(element.content, acc) acc.append("
  • ") -def render_block(content, acc, _class, is_code): - acc.append('
    '.format(_class))
    -    if is_code:
    -        acc.append('')
     
    -    # Remove indentation common to all lines
    -    acc.append(unindent(content))
    -    if is_code:
    -        acc.append('')
    -    acc.append('
    ') +def render_code_block(element, acc): + acc.append('
    ')
    +    acc.append(html.escape(element.lines))
    +    acc.append('
    ') -def unindent(content): - base_indentation = min([ - len(l) - len(l.lstrip(' ')) - for l in content.split('\n') - if len(l.strip()) > 0 - ] or [0]) - content_lines = [ - l[base_indentation:] - for l in content.split('\n') - ] - return '\n'.join(content_lines) - -def render_code_block(element, acc, headline, graph): - code = element.lines - - if element.arguments is not None and len(element.arguments) > 0 : - try: - lexer = pygments.lexers.get_lexer_by_name(element.arguments.split()[0], stripall=True) - content = pygments.highlight(unindent(code), - lexer, - pygments.formatters.HtmlFormatter() - ) - acc.append(content) - return - - except pygments.util.ClassNotFound: - pass - logging.error("Cannot find lexer for {}".format(element.subtype.lower())) - content = html.escape(code) - render_block(content, acc, _class='code ' + element.subtype.lower(), is_code=True) +def render_results_block(element, acc): + # TODO: + # acc.append('
    ')
    +    # render_tree(element.children, acc)
    +    # acc.append('
    ') + pass -def render_results_block(element, acc, headline, graph): - items = [e.get_raw() for e in element.children] - content = '\n'.join(items) - if len(content.strip()) > 0: - render_block(content, acc, _class='results lang-text', is_code=False) +def render_text(element, acc): + acc.append('') + render_text_tokens(element.content, acc) + acc.append('') -def render_generic_drawer_block(element, acc, headline, graph): - items = [e.get_raw() for e in element.children] - content = '\n'.join(items) - if len(content.strip()) > 0: - render_block(content, acc, _class='generic-drawer {}-drawer lang-text'.format(element.drawer_name), is_code=False) - -def render_org_text(element, acc, headline, graph): - as_dom = org_rw.text_to_dom(element.contents, element) - render_text_tokens(as_dom, acc, headline, graph) - -def render_text(element, acc, headline, graph): - acc.append('
    ') - render_text_tokens(element.content, acc, headline, graph) - acc.append('
    ') - -def render_text_tokens(tokens, acc, headline, graph): - acc.append('

    ') - if isinstance(tokens, org_rw.Text): - tokens = tokens.contents +def render_text_tokens(tokens, acc): for chunk in tokens: if isinstance(chunk, str): - lines = chunk.split('\n\n') - contents = [] - for line in lines: - line_chunks = [] - for word in TEXT_OR_LINK_RE.findall(line): - if '://' in word and not (word.startswith('org-protocol://')): - if not (word.startswith('http://') - or word.startswith('https://') - or word.startswith('ftp://') - or word.startswith('ftps://') - ): - logging.warning('Is this a link? {} (on {})\nLine: {}\nChunks: {}'.format(word, headline.doc.path, line, line_chunks)) - line_chunks.append(html.escape(word)) - else: - line_chunks.append('{description}' - .format(url=word, - description=html.escape(word))) - else: - line_chunks.append(html.escape(word)) - contents.append(' '.join(line_chunks)) - - acc.append('{}'.format('

    '.join(contents))) - + acc.append('{} '.format(chunk)) elif isinstance(chunk, Link): link_target = chunk.value - is_internal_link = True + if link_target.startswith('id:'): + link_target = './' + link_target[3:] + '.node.html' description = chunk.description if description is None: description = chunk.value - try: - if link_target.startswith('id:'): - assert_id_exists(link_target[3:], headline, graph) - link_target = './' + link_target[3:] + '.node.html' - elif link_target.startswith('./') or link_target.startswith('../'): - if '::' in link_target: - logging.warning('Not implemented headline links to other files. Used on {}'.format(link_target)) - - else: - target_path = os.path.abspath(os.path.join(os.path.dirname(headline.doc.path), link_target)) - if target_path not in graph['main_headlines']: - logging.warning('Link to doc not in graph: {}'.format(target_path)) - else: - assert_id_exists(graph['main_headlines'][target_path].id, headline, graph) - link_target = './' + graph['main_headlines'][target_path].id + '.node.html' - elif link_target.startswith('attachment:'): - inner_target = link_target.split(':', 1)[1] - link_target = 'attachments/{}/{}/{}'.format(headline.id[:2], headline.id[2:], inner_target) - logging.warning('Not implemented `attachment:` links. Used on {}'.format(link_target)) - elif link_target.startswith('* '): - target_headline = get_headline_with_name(link_target.lstrip('* '), headline.doc) - if target_headline is None: - logging.warning('No headline found corresponding to {}. On file {}'.format(link_target, headline.doc.path)) - else: - assert_id_exists(target_headline.id, headline, graph) - link_target = './' + target_headline.id + '.node.html' - else: - is_internal_link = False - if link_target.startswith('orgit-rev'): - raise NonExistingLocalNoteError(link_target, headline) - elif link_target.startswith('file:'): - raise NonExistingLocalNoteError(link_target, headline) - elif not ( - link_target.startswith('https://') - or link_target.startswith('http://') - or link_target.startswith('/') - ): - raise NotImplementedError('Unknown link type: {}' - .format(link_target)) - - if link_target.rsplit('.', 1)[-1].lower() in IMG_EXTENSIONS: - acc.append(''.format( - html.escape(link_target), - 'internal' if is_internal_link else 'external', - html.escape(link_target), - )) - else: - acc.append('{}'.format( - html.escape(link_target), - 'internal' if is_internal_link else 'external', - html.escape(description), - )) - except NonExistingLocalNoteError as err: - logging.warning(err.get_message()) - acc.append(html.escape(description)) - elif isinstance(chunk, org_rw.MarkerToken): - tag = '<' - if chunk.closing: - tag += '/' - tag += { - org_rw.MarkerType.BOLD_MODE: 'strong', - org_rw.MarkerType.CODE_MODE: 'code', - org_rw.MarkerType.ITALIC_MODE: 'em', - org_rw.MarkerType.STRIKE_MODE: 's', - org_rw.MarkerType.UNDERLINED_MODE: 'span class="underlined"' if not chunk.closing else 'span', - org_rw.MarkerType.VERBATIM_MODE: 'span class="verbatim"' if not chunk.closing else 'span', - }[chunk.tok_type] - tag += '>' - acc.append(tag) - else: - raise NotImplementedError('TextToken: {}'.format(chunk)) - acc.append('

    ') + acc.append('{}'.format( + html.escape(link_target), + html.escape(description), + )) + # else: + # raise NotImplementedError('TextToken: {}'.format(chunk)) -def render_tag(element, acc, headline, graph): +def render_tag(element, acc): return { dom.PropertyDrawerNode: render_property_drawer, dom.LogbookDrawerNode: render_logbook_drawer, dom.PropertyNode: render_property_node, dom.ListGroupNode: render_list_group, dom.ListItem: render_list_item, - dom.TableNode: render_table, - dom.TableSeparatorRow: render_table_separator_row, - dom.TableRow: render_table_row, dom.CodeBlock: render_code_block, dom.Text: render_text, dom.ResultsDrawerNode: render_results_block, - dom.GenericDrawerNode: render_generic_drawer_block, - org_rw.Text: render_org_text, - }[type(element)](element, acc, headline, graph) + }[type(element)](element, acc) -def render_tree(tree, acc, headline, graph): +def render_tree(tree, acc): for element in tree: - render_tag(element, acc, headline, graph) - -def render_inline(tree, f, headline, graph): - acc = [] - f(tree, acc, headline, graph) - return ''.join(acc) + render_tag(element, acc) -def render_as_document(headline, doc, headlineLevel, graph, title, doc_to_headline_remapping): - if isinstance(headline.parent, org_rw.Headline): - topLevelHeadline = headline.parent - while isinstance(topLevelHeadline.parent, org_rw.Headline): - topLevelHeadline = topLevelHeadline.parent - return f""" - - - - {title} @ {SITE_NAME} - - - - - - - Sending you to the main note... [{org_rw.token_list_to_plaintext(topLevelHeadline.title.contents)}] - - - """ - else: - return as_document(render(headline, doc, graph=graph, headlineLevel=headlineLevel, - doc_to_headline_remapping=doc_to_headline_remapping), - title, render_toc(doc)) - -def render_toc(doc): - acc = ['') - - if sum([chunk == '
  • ' for chunk in acc]) < 2: - # If < 2 headlines, ignore it - return None - - return ''.join(acc) - -def render_toc_headline(headline, acc): - acc.append('
  • ') - acc.append(f'{html.escape(headline.title.get_text())}') - children = list(headline.children) - if children: - acc.append('
      ') - for child in children: - render_toc_headline(child, acc) - acc.append('
    ') - acc.append('
  • ') - - - -def render_connections(headline_id, content, graph, doc_to_headline_remapping): - # if headline_id != 'aa29be89-70e7-4465-91ed-361cf0ce62f2': - # return - - logging.info("Generating centered graph for {}".format(headline_id)) - try: - svg = gen_centered_graph.gen(headline_id, graph['nodes'], doc_to_headline_remapping) - content.append("
    {}
    ".format(svg)) - except: - logging.warning("Broken reference on headline ID={}".format(headline_id)) - -def render(headline, doc, graph, headlineLevel, doc_to_headline_remapping): +def render(headline, doc, headlineLevel): try: dom = headline.as_dom() except: logging.error("Error generating DOM for {}".format(doc.path)) raise - print_tree(dom, indentation=2, headline=headline) + print_tree(dom) content = [] - if headline.id and headlineLevel == 0: - render_connections(headline.id, content, graph, doc_to_headline_remapping=doc_to_headline_remapping) - - render_tree(dom, content, headline, graph) - + render_tree(dom, content) for child in headline.children: - content.append(render(child, doc, headlineLevel=headlineLevel+1, graph=graph, - doc_to_headline_remapping=doc_to_headline_remapping)) + content.append(render(child, doc, headlineLevel=headlineLevel+1)) - if headline.state is None or headline.state.get('name') is None: + if headline.state is None: state = "" else: - state = f'{headline.state["name"]}' + state = f'{headline.state}' if headline.is_todo: todo_state = "todo" else: todo_state = "done" - tag_list = [] - for tag in headline.shallow_tags: - if tag.lower() not in SKIPPED_TAGS: - tag_list.append(f'{html.escape(tag)}') - tags = f'{"".join(tag_list)}' - - display_state = 'expanded' - # # Update display based on document STARTUP config - # visual_level = doc.get_keywords('STARTUP', 'showall') - # if visual_level.startswith('show') and visual_level.endswith('levels'): - # visual_level_num = int(visual_level[len('show'):-len('levels')]) - 1 - # # Note that level is 0 indexed inside this loop - # if headlineLevel >= visual_level_num: - # display_state = 'collapsed' - - title = render_inline(headline.title, render_tag, headline, graph) - - if headlineLevel > 0: - title = f"{title}" + display_state = 'collapsed' + if headlineLevel < MIN_HIDDEN_HEADLINE_LEVEL: + display_state = 'expanded' return f"""

    {state} - {title} - {tags} + + {html.escape(headline.title)} +

    {''.join(content)} @@ -809,40 +314,27 @@ def render(headline, doc, graph, headlineLevel, doc_to_headline_remapping): """ -def as_document(html, title, global_toc): - body_classes = [] - if global_toc is None: - toc_section = "" - body_classes.append('no-toc') - else: - toc_section = f""" -
    -

    Table of contents

    - {global_toc} -
    - """ +def as_document(html): return f""" - - {title} @ {SITE_NAME} - - - - - + - - - {toc_section} + {html} - - - """ @@ -855,13 +347,9 @@ def save_changes(doc): if __name__ == "__main__": - if len(sys.argv) not in (3, 4): - print("Usage: {} SOURCE_TOP DEST_TOP ".format(sys.argv[0])) + if len(sys.argv) != 3: + print("Usage: {} SOURCE_TOP DEST_TOP".format(sys.argv[0])) exit(0) logging.basicConfig(level=logging.INFO, format="%(levelname)-8s %(message)s") - subpath = DEFAULT_SUBPATH - - if len(sys.argv) == 4: - subpath = sys.argv[3] - exit(main(sys.argv[1], sys.argv[2], subpath=subpath)) + main(sys.argv[1], sys.argv[2]) diff --git a/scripts/ops_cache.py b/scripts/ops_cache.py deleted file mode 100644 index 45c1431..0000000 --- a/scripts/ops_cache.py +++ /dev/null @@ -1,75 +0,0 @@ -import sqlite3 -import json -import logging -from typing import Optional -import xdg -import os -import datetime - -CACHE_DB: Optional[sqlite3.Connection] = None -CACHE_PATH = os.path.join(xdg.xdg_cache_home(), 'codigoparallevar', 'ops.sqlite3') - -def init_db(): - global CACHE_DB - - os.makedirs(os.path.dirname(CACHE_PATH), exist_ok=True) - CACHE_DB = sqlite3.connect(CACHE_PATH) - - cur = CACHE_DB.cursor() - cur.execute('''CREATE TABLE IF NOT EXISTS ops( - in_val TEXT PRIMARY KEY, - code TEXT, - out_val TEXT, - added_at DateTime - ); - ''') - CACHE_DB.commit() - cur.close() - -def query_cache(in_val, code): - if CACHE_DB is None: - init_db() - assert CACHE_DB is not None - cur = CACHE_DB.cursor() - cur.execute('''SELECT out_val FROM ops WHERE in_val = ? AND code = ?''', (in_val, code)) - - # Should return only one result, right? 🤷 - results = cur.fetchall() - assert len(results) < 2 - if len(results) == 0: - return None - else: - return results[0][0] - -def save_cache(in_val, code, out_val): - if CACHE_DB is None: - init_db() - assert CACHE_DB is not None - cur = CACHE_DB.cursor() - cur.execute(''' - INSERT INTO ops(in_val, code, out_val, added_at) - VALUES (?, ?, ?, ?);''', - (in_val, code, out_val, datetime.datetime.now())) - CACHE_DB.commit() - cur.close() - -def cache(fun): - fun_code = fun.__code__.co_code.decode('latin-1') - def wrapped(*kargs, **kwargs): - in_val = json.dumps({ - 'kargs': kargs, - 'kwargs': kwargs, - 'fun_code': fun_code, - }) - - cache_result = query_cache(in_val, fun_code) - found_in_cache = cache_result is not None - if not found_in_cache: - out_val = fun(*kargs, **kwargs) - save_cache(in_val, fun_code, out_val) - else: - out_val = cache_result - - logging.info("{} bytes in, {} bytes out (in_cache: {})".format(len(in_val), len(out_val), found_in_cache)) - return out_val - return wrapped diff --git a/scripts/requirements.txt b/scripts/requirements.txt index 4de0fcc..9878f29 100644 --- a/scripts/requirements.txt +++ b/scripts/requirements.txt @@ -1,3 +1,2 @@ Markdown Jinja2 -pygments diff --git a/scripts/search-server.sh b/scripts/search-server.sh deleted file mode 100644 index 62abda0..0000000 --- a/scripts/search-server.sh +++ /dev/null @@ -1,15 +0,0 @@ -#!/usr/bin/env bash - -set -eu - -PORT=${PORT:-3001} - -cd "$(dirname "$0")/search-server" - -docker build -t search-server . - -cd ../../_gen/notes/ - -set -x - -exec docker run -it --rm -p $PORT:80 -e SNIPPET_SIZE=256 -e PORT=80 -e DB_PATH=/db.sqlite3 -v `pwd`/db.sqlite3:/db.sqlite3:ro search-server diff --git a/scripts/search-server/.gitignore b/scripts/search-server/.gitignore deleted file mode 100644 index 59e27c6..0000000 --- a/scripts/search-server/.gitignore +++ /dev/null @@ -1 +0,0 @@ -search-server diff --git a/scripts/search-server/Dockerfile b/scripts/search-server/Dockerfile deleted file mode 100644 index 727f1d9..0000000 --- a/scripts/search-server/Dockerfile +++ /dev/null @@ -1,47 +0,0 @@ -FROM golang:1.19-alpine as builder - -# Install build dependencies -RUN apk add alpine-sdk - -# Create appuser. -ENV USER=appuser -ENV UID=10001 - -# See https://stackoverflow.com/a/55757473/12429735 -RUN adduser \ - --disabled-password \ - --gecos "" \ - --home "/nonexistent" \ - --shell "/sbin/nologin" \ - --no-create-home \ - --uid "${UID}" \ - "${USER}" - -# Prepare dependencies -RUN mkdir /build -ADD go.mod go.sum /build/ -WORKDIR /build -RUN go mod download -RUN go mod verify - -# Prepare app -ADD server.go /build/ -# Build as static binary -RUN CGO_ENABLED=1 go build --tags "fts5" -ldflags='-w -s -extldflags "-static"' -o /build/search-server - -# Copy binary to empty image -FROM scratch -# Import the user and group files from the builder. -COPY --from=builder /etc/passwd /etc/passwd -COPY --from=builder /etc/group /etc/group - -# Prepare environment -ENV GIN_MODE=release - -# Copy executable -COPY --from=builder /build/search-server /server - -# Use an unprivileged user. -USER appuser:appuser - -ENTRYPOINT ["/server"] diff --git a/scripts/search-server/go.mod b/scripts/search-server/go.mod deleted file mode 100644 index 09793db..0000000 --- a/scripts/search-server/go.mod +++ /dev/null @@ -1,66 +0,0 @@ -module codigoparallevar/search-server - -go 1.19 - -require github.com/gin-gonic/gin v1.8.1 - -require ( - github.com/Microsoft/go-winio v0.4.14 // indirect - github.com/awesome-gocui/gocui v0.6.0 // indirect - github.com/awesome-gocui/keybinding v1.0.0 // indirect - github.com/awesome-gocui/termbox-go v0.0.0-20190427202837-c0aef3d18bcc // indirect - github.com/cespare/xxhash v1.1.0 // indirect - github.com/docker/cli v0.0.0-20190906153656-016a3232168d // indirect - github.com/docker/distribution v2.7.1+incompatible // indirect - github.com/docker/docker v0.7.3-0.20190309235953-33c3200e0d16 // indirect - github.com/docker/go-connections v0.4.0 // indirect - github.com/docker/go-units v0.4.0 // indirect - github.com/dustin/go-humanize v1.0.0 // indirect - github.com/fatih/color v1.7.0 // indirect - github.com/fsnotify/fsnotify v1.4.7 // indirect - github.com/gin-contrib/sse v0.1.0 // indirect - github.com/go-errors/errors v1.0.1 // indirect - github.com/go-playground/locales v0.14.0 // indirect - github.com/go-playground/universal-translator v0.18.0 // indirect - github.com/go-playground/validator/v10 v10.10.0 // indirect - github.com/goccy/go-json v0.9.7 // indirect - github.com/gogo/protobuf v1.3.0 // indirect - github.com/google/uuid v1.1.1 // indirect - github.com/hashicorp/hcl v1.0.0 // indirect - github.com/inconshreveable/mousetrap v1.0.0 // indirect - github.com/json-iterator/go v1.1.12 // indirect - github.com/konsorten/go-windows-terminal-sequences v1.0.2 // indirect - github.com/leodido/go-urn v1.2.1 // indirect - github.com/logrusorgru/aurora v0.0.0-20190803045625-94edacc10f9b // indirect - github.com/lunixbochs/vtclean v1.0.0 // indirect - github.com/magiconair/properties v1.8.1 // indirect - github.com/mattn/go-colorable v0.1.2 // indirect - github.com/mattn/go-isatty v0.0.14 // indirect - github.com/mattn/go-runewidth v0.0.4 // indirect - github.com/mattn/go-sqlite3 v1.14.15 // indirect - github.com/mitchellh/go-homedir v1.1.0 // indirect - github.com/mitchellh/mapstructure v1.1.2 // indirect - github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421 // indirect - github.com/modern-go/reflect2 v1.0.2 // indirect - github.com/opencontainers/go-digest v1.0.0-rc1 // indirect - github.com/opencontainers/image-spec v1.0.1 // indirect - github.com/pelletier/go-toml v1.4.0 // indirect - github.com/pelletier/go-toml/v2 v2.0.1 // indirect - github.com/phayes/permbits v0.0.0-20190612203442-39d7c581d2ee // indirect - github.com/pkg/errors v0.8.1 // indirect - github.com/sirupsen/logrus v1.4.2 // indirect - github.com/spf13/afero v1.2.2 // indirect - github.com/spf13/cast v1.3.0 // indirect - github.com/spf13/cobra v0.0.5 // indirect - github.com/spf13/jwalterweatherman v1.1.0 // indirect - github.com/spf13/pflag v1.0.5 // indirect - github.com/spf13/viper v1.4.0 // indirect - github.com/ugorji/go/codec v1.2.7 // indirect - github.com/wagoodman/dive v0.10.0 // indirect - golang.org/x/crypto v0.0.0-20210711020723-a769d52b0f97 // indirect - golang.org/x/net v0.0.0-20210226172049-e18ecbb05110 // indirect - golang.org/x/sys v0.0.0-20210806184541-e5e7981a1069 // indirect - golang.org/x/text v0.3.6 // indirect - google.golang.org/protobuf v1.28.0 // indirect - gopkg.in/yaml.v2 v2.4.0 // indirect -) diff --git a/scripts/search-server/go.sum b/scripts/search-server/go.sum deleted file mode 100644 index a078a11..0000000 --- a/scripts/search-server/go.sum +++ /dev/null @@ -1,295 +0,0 @@ -cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= -github.com/Azure/go-ansiterm v0.0.0-20170929234023-d6e3b3328b78/go.mod h1:LmzpDX56iTiv29bbRTIsUNlaFfuhWRQBWjQdVyAevI8= -github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= -github.com/Microsoft/go-winio v0.4.14 h1:+hMXMk01us9KgxGb7ftKQt2Xpf5hH/yky+TDA+qxleU= -github.com/Microsoft/go-winio v0.4.14/go.mod h1:qXqCSQ3Xa7+6tgxaGTIe4Kpcdsi+P8jBhyzoq1bpyYA= -github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU= -github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= -github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= -github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8= -github.com/awesome-gocui/gocui v0.5.0/go.mod h1:1QikxFaPhe2frKeKvEwZEIGia3haiOxOUXKinrv17mA= -github.com/awesome-gocui/gocui v0.6.0 h1:hhDJiQC12tEsJNJ+iZBBVaSSLFYo9llFuYpQlL5JZVI= -github.com/awesome-gocui/gocui v0.6.0/go.mod h1:1QikxFaPhe2frKeKvEwZEIGia3haiOxOUXKinrv17mA= -github.com/awesome-gocui/keybinding v1.0.0 h1:CrnjCfEhWpjcqIQUan9IllaXeRGELdwfjeUmY7ljbng= -github.com/awesome-gocui/keybinding v1.0.0/go.mod h1:z0TyCwIhaT97yU+becTse8Dqh2CvYT0FLw0R0uTk0ag= -github.com/awesome-gocui/termbox-go v0.0.0-20190427202837-c0aef3d18bcc h1:wGNpKcHU8Aadr9yOzsT3GEsFLS7HQu8HxQIomnekqf0= -github.com/awesome-gocui/termbox-go v0.0.0-20190427202837-c0aef3d18bcc/go.mod h1:tOy3o5Nf1bA17mnK4W41gD7PS3u4Cv0P0pqFcoWMy8s= -github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= -github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8= -github.com/cespare/xxhash v1.1.0 h1:a6HrQnmkObjyL+Gs60czilIUGqrzKutQD6XZog3p+ko= -github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc= -github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= -github.com/coreos/bbolt v1.3.2/go.mod h1:iRUV2dpdMOn7Bo10OQBFzIJO9kkE559Wcmn+qkEiiKk= -github.com/coreos/etcd v3.3.10+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE= -github.com/coreos/go-etcd v2.0.0+incompatible/go.mod h1:Jez6KQU2B/sWsbdaef3ED8NzMklzPG4d5KIOhIy30Tk= -github.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= -github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= -github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA= -github.com/cpuguy83/go-md2man v1.0.10/go.mod h1:SmD6nW6nTyfqj6ABTjUi3V3JVMnlJmwcJI5acqYI6dE= -github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= -github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ= -github.com/dgryski/go-sip13 v0.0.0-20181026042036-e10d5fee7954/go.mod h1:vAd38F8PWV+bWy6jNmig1y/TA+kYO4g3RSRF0IAv0no= -github.com/docker/cli v0.0.0-20190906153656-016a3232168d h1:gwX/88xJZfxZV1yjhhuQpWTmEgJis7/XGCVu3iDIZYU= -github.com/docker/cli v0.0.0-20190906153656-016a3232168d/go.mod h1:JLrzqnKDaYBop7H2jaqPtU4hHvMKP+vjCwu2uszcLI8= -github.com/docker/distribution v2.7.1+incompatible h1:a5mlkVzth6W5A4fOsS3D2EO5BUmsJpcB+cRlLU7cSug= -github.com/docker/distribution v2.7.1+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w= -github.com/docker/docker v0.7.3-0.20190309235953-33c3200e0d16 h1:dmUn0SuGx7unKFwxyeQ/oLUHhEfZosEDrpmYM+6MTuc= -github.com/docker/docker v0.7.3-0.20190309235953-33c3200e0d16/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= -github.com/docker/go-connections v0.4.0 h1:El9xVISelRB7BuFusrZozjnkIM5YnzCViNKohAFqRJQ= -github.com/docker/go-connections v0.4.0/go.mod h1:Gbd7IOopHjR8Iph03tsViu4nIes5XhDvyHbTtUxmeec= -github.com/docker/go-units v0.4.0 h1:3uh0PgVws3nIA0Q+MwDC8yjEPf9zjRfZZWXZYDct3Tw= -github.com/docker/go-units v0.4.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk= -github.com/dustin/go-humanize v1.0.0 h1:VSnTsYCnlFHaM2/igO1h6X3HA71jcobQuxemgkq4zYo= -github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk= -github.com/fatih/color v1.7.0 h1:DkWD4oS2D8LGGgTQ6IvwJJXSL5Vp2ffcQg58nFV38Ys= -github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4= -github.com/fsnotify/fsnotify v1.4.7 h1:IXs+QLmnXW2CcXuY+8Mzv/fWEsPGWxqefPtCP5CnV9I= -github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= -github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= -github.com/gin-contrib/sse v0.1.0 h1:Y/yl/+YNO8GZSjAhjMsSuLt29uWRFHdHYUb5lYOV9qE= -github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI= -github.com/gin-gonic/gin v1.8.1 h1:4+fr/el88TOO3ewCmQr8cx/CtZ/umlIRIs5M4NTNjf8= -github.com/gin-gonic/gin v1.8.1/go.mod h1:ji8BvRH1azfM+SYow9zQ6SZMvR8qOMZHmsCuWR9tTTk= -github.com/go-errors/errors v1.0.1 h1:LUHzmkK3GUKUrL/1gfBUxAHzcev3apQlezX/+O7ma6w= -github.com/go-errors/errors v1.0.1/go.mod h1:f4zRHt4oKfwPJE5k8C9vpYG+aDHdBFUsgrm6/TyX73Q= -github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= -github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE= -github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk= -github.com/go-playground/assert/v2 v2.0.1/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4= -github.com/go-playground/locales v0.14.0 h1:u50s323jtVGugKlcYeyzC0etD1HifMjqmJqb8WugfUU= -github.com/go-playground/locales v0.14.0/go.mod h1:sawfccIbzZTqEDETgFXqTho0QybSa7l++s0DH+LDiLs= -github.com/go-playground/universal-translator v0.18.0 h1:82dyy6p4OuJq4/CByFNOn/jYrnRPArHwAcmLoJZxyho= -github.com/go-playground/universal-translator v0.18.0/go.mod h1:UvRDBj+xPUEGrFYl+lu/H90nyDXpg0fqeB/AQUGNTVA= -github.com/go-playground/validator/v10 v10.10.0 h1:I7mrTYv78z8k8VXa/qJlOlEXn/nBh+BF8dHX5nt/dr0= -github.com/go-playground/validator/v10 v10.10.0/go.mod h1:74x4gJWsvQexRdW8Pn3dXSGrTK4nAUsbPlLADvpJkos= -github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= -github.com/goccy/go-json v0.9.7 h1:IcB+Aqpx/iMHu5Yooh7jEzJk1JZ7Pjtmys2ukPr7EeM= -github.com/goccy/go-json v0.9.7/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I= -github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= -github.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zVXpSg4= -github.com/gogo/protobuf v1.3.0 h1:G8O7TerXerS4F6sx9OV7/nRfJdnXgHZu/S/7F2SN+UE= -github.com/gogo/protobuf v1.3.0/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXPKa29o= -github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= -github.com/golang/groupcache v0.0.0-20190129154638-5b532d6fd5ef/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= -github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= -github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= -github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= -github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= -github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= -github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= -github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= -github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= -github.com/google/uuid v1.1.1 h1:Gkbcsh/GbpXz7lPftLA3P6TYMwjCLYm83jiFQZF/3gY= -github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= -github.com/gorilla/mux v1.7.2/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs= -github.com/gorilla/websocket v1.4.0/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ= -github.com/grpc-ecosystem/go-grpc-middleware v1.0.0/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs= -github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk= -github.com/grpc-ecosystem/grpc-gateway v1.9.0/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY= -github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4= -github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= -github.com/inconshreveable/mousetrap v1.0.0 h1:Z8tu5sraLXCXIcARxBp/8cbvlwVa7Z1NHg9XEKhtSvM= -github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= -github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo= -github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM= -github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= -github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w= -github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q= -github.com/kisielk/errcheck v1.2.0/go.mod h1:/BMXB+zMLi60iA8Vv6Ksmxu/1UDYcXs4uQLJ+jE2L00= -github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= -github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= -github.com/konsorten/go-windows-terminal-sequences v1.0.2 h1:DB17ag19krx9CFsz4o3enTrPXyIXCl+2iCXH/aMAp9s= -github.com/konsorten/go-windows-terminal-sequences v1.0.2/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= -github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc= -github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= -github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= -github.com/kr/pretty v0.3.0/go.mod h1:640gp4NfQd8pI5XOwp5fnNeVWj67G7CFk/SaSQn7NBk= -github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= -github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= -github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= -github.com/leodido/go-urn v1.2.1 h1:BqpAaACuzVSgi/VLzGZIobT2z4v53pjosyNd9Yv6n/w= -github.com/leodido/go-urn v1.2.1/go.mod h1:zt4jvISO2HfUBqxjfIshjdMTYS56ZS/qv49ictyFfxY= -github.com/logrusorgru/aurora v0.0.0-20190803045625-94edacc10f9b h1:PMbSa9CgaiQR9NLlUTwKi+7aeLl3GG5JX5ERJxfQ3IE= -github.com/logrusorgru/aurora v0.0.0-20190803045625-94edacc10f9b/go.mod h1:7rIyQOR62GCctdiQpZ/zOJlFyk6y+94wXzv6RNZgaR4= -github.com/lunixbochs/vtclean v1.0.0 h1:xu2sLAri4lGiovBDQKxl5mrXyESr3gUr5m5SM5+LVb8= -github.com/lunixbochs/vtclean v1.0.0/go.mod h1:pHhQNgMf3btfWnGBVipUOjRYhoOsdGqdm/+2c2E2WMI= -github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= -github.com/magiconair/properties v1.8.1 h1:ZC2Vc7/ZFkGmsVC9KvOjumD+G5lXy2RtTKyzRKO2BQ4= -github.com/magiconair/properties v1.8.1/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= -github.com/mattn/go-colorable v0.1.2 h1:/bC9yWikZXAL9uJdulbSfyVNIR3n3trXl+v8+1sx8mU= -github.com/mattn/go-colorable v0.1.2/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE= -github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= -github.com/mattn/go-isatty v0.0.9/go.mod h1:YNRxwqDuOph6SZLI9vUUz6OYw3QyUt7WiY2yME+cCiQ= -github.com/mattn/go-isatty v0.0.14 h1:yVuAays6BHfxijgZPzw+3Zlu5yQgKGP2/hcQbHb7S9Y= -github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94= -github.com/mattn/go-runewidth v0.0.4 h1:2BvfKmzob6Bmd4YsL0zygOqfdFnK7GR4QL06Do4/p7Y= -github.com/mattn/go-runewidth v0.0.4/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU= -github.com/mattn/go-sqlite3 v1.14.15 h1:vfoHhTN1af61xCRSWzFIWzx2YskyMTwHLrExkBOjvxI= -github.com/mattn/go-sqlite3 v1.14.15/go.mod h1:2eHXhiwb8IkHr+BDWZGa96P6+rkvnG63S2DGjv9HUNg= -github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= -github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y= -github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= -github.com/mitchellh/mapstructure v1.1.2 h1:fmNYVwqnSfB9mZU6OS2O6GsXM+wcskZDuKQzvN1EDeE= -github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= -github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421 h1:ZqeYNhU3OHLH3mGKHDcjJRFFRrJa6eAM5H+CtDdOsPc= -github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= -github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M= -github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= -github.com/morikuni/aec v1.0.0/go.mod h1:BbKIizmSmc5MMPqRYbxO4ZU0S0+P200+tUnFx7PXmsc= -github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= -github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U= -github.com/opencontainers/go-digest v1.0.0-rc1 h1:WzifXhOVOEOuFYOJAW6aQqW0TooG2iki3E3Ii+WN7gQ= -github.com/opencontainers/go-digest v1.0.0-rc1/go.mod h1:cMLVZDEM3+U2I4VmLI6N8jQYUd2OVphdqWwCJHrFt2s= -github.com/opencontainers/image-spec v1.0.1 h1:JMemWkRwHx4Zj+fVxWoMCFm/8sYGGrUVojFA6h/TRcI= -github.com/opencontainers/image-spec v1.0.1/go.mod h1:BtxoFyWECRxE4U/7sNtV5W15zMzWCbyJoFRP3s7yZA0= -github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic= -github.com/pelletier/go-toml v1.4.0 h1:u3Z1r+oOXJIkxqw34zVhyPgjBsm6X2wn21NWs/HfSeg= -github.com/pelletier/go-toml v1.4.0/go.mod h1:PN7xzY2wHTK0K9p34ErDQMlFxa51Fk0OUruD3k1mMwo= -github.com/pelletier/go-toml/v2 v2.0.1 h1:8e3L2cCQzLFi2CR4g7vGFuFxX7Jl1kKX8gW+iV0GUKU= -github.com/pelletier/go-toml/v2 v2.0.1/go.mod h1:r9LEWfGN8R5k0VXJ+0BkIe7MYkRdwZOjgMj2KwnJFUo= -github.com/phayes/permbits v0.0.0-20190612203442-39d7c581d2ee h1:P6U24L02WMfj9ymZTxl7CxS73JC99x3ukk+DBkgQGQs= -github.com/phayes/permbits v0.0.0-20190612203442-39d7c581d2ee/go.mod h1:3uODdxMgOaPYeWU7RzZLxVtJHZ/x1f/iHkBZuKJDzuY= -github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA= -github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= -github.com/pkg/errors v0.8.1 h1:iURUrRGxPUNPdy5/HRSm+Yj6okJ6UtLINN0Q9M4+h3I= -github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= -github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw= -github.com/prometheus/client_golang v0.9.3/go.mod h1:/TN21ttK/J9q6uSwhBd54HahCDft0ttaMvbicHlPoso= -github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= -github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= -github.com/prometheus/common v0.0.0-20181113130724-41aa239b4cce/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro= -github.com/prometheus/common v0.4.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= -github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= -github.com/prometheus/procfs v0.0.0-20190507164030-5867b95ac084/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= -github.com/prometheus/tsdb v0.7.1/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40TwIPHuXU= -github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg= -github.com/rogpeppe/go-internal v1.6.1/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc= -github.com/rogpeppe/go-internal v1.8.0/go.mod h1:WmiCO8CzOY8rg0OYDC4/i/2WRWAB6poM+XZ2dLUbcbE= -github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g= -github.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo= -github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= -github.com/sirupsen/logrus v1.4.1/go.mod h1:ni0Sbl8bgC9z8RoU9G6nDWqqs/fq4eDPysMBDgk/93Q= -github.com/sirupsen/logrus v1.4.2 h1:SPIRibHv4MatM3XXNO2BJeFLZwZ2LvZgfQ5+UNI2im4= -github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE= -github.com/soheilhy/cmux v0.1.4/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4kGIyLM= -github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA= -github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ= -github.com/spf13/afero v1.2.2 h1:5jhuqJyZCZf2JRofRvN/nIFgIWNzPa3/Vz8mYylgbWc= -github.com/spf13/afero v1.2.2/go.mod h1:9ZxEEn6pIJ8Rxe320qSDBk6AsU0r9pR7Q4OcevTdifk= -github.com/spf13/cast v1.3.0 h1:oget//CVOEoFewqQxwr0Ej5yjygnqGkvggSE/gB35Q8= -github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= -github.com/spf13/cobra v0.0.5 h1:f0B+LkLX6DtmRH1isoNA9VTtNUK9K8xYd28JNNfOv/s= -github.com/spf13/cobra v0.0.5/go.mod h1:3K3wKZymM7VvHMDS9+Akkh4K60UwM26emMESw8tLCHU= -github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo= -github.com/spf13/jwalterweatherman v1.1.0 h1:ue6voC5bR5F8YxI5S67j9i582FU4Qvo2bmqnqMYADFk= -github.com/spf13/jwalterweatherman v1.1.0/go.mod h1:aNWZUN0dPAAO/Ljvb5BEdw96iTZ0EXowPYD95IqWIGo= -github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= -github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= -github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= -github.com/spf13/viper v1.3.2/go.mod h1:ZiWeW+zYFKm7srdB9IoDzzZXaJaI5eL9QjNiN/DMA2s= -github.com/spf13/viper v1.4.0 h1:yXHLWeravcrgGyFSyCgdYpXQ9dR9c/WED3pg1RhxqEU= -github.com/spf13/viper v1.4.0/go.mod h1:PTJ7Z/lr49W6bUbkmS1V3by4uWynFiR9p7+dSq/yZzE= -github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= -github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= -github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= -github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= -github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= -github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= -github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= -github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= -github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U= -github.com/ugorji/go v1.1.4/go.mod h1:uQMGLiO92mf5W77hV/PUCpI3pbzQx3CRekS0kk+RGrc= -github.com/ugorji/go v1.2.7/go.mod h1:nF9osbDWLy6bDVv/Rtoh6QgnvNDpmCalQV5urGCCS6M= -github.com/ugorji/go/codec v0.0.0-20181204163529-d75b2dcb6bc8/go.mod h1:VFNgLljTbGfSG7qAOspJ7OScBnGdDN/yBr0sguwnwf0= -github.com/ugorji/go/codec v1.2.7 h1:YPXUKf7fYbp/y8xloBqZOw2qaVggbfwMlI8WM3wZUJ0= -github.com/ugorji/go/codec v1.2.7/go.mod h1:WGN1fab3R1fzQlVQTkfxVtIBhWDRqOviHU95kRgeqEY= -github.com/wagoodman/dive v0.10.0 h1:JaitQBVwmfZD5mvLkBHk1LUq6jwsjvnNS6mgIl7YNZQ= -github.com/wagoodman/dive v0.10.0/go.mod h1:8IDxfzmg3+5DQwK6/sGyMpJr95ejuv511+rF9CTNYdQ= -github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU= -github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q= -go.etcd.io/bbolt v1.3.2/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU= -go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= -go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0= -go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q= -golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= -golang.org/x/crypto v0.0.0-20181203042331-505ab145d0a9/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= -golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= -golang.org/x/crypto v0.0.0-20210711020723-a769d52b0f97 h1:/UOmuWzQfxxo9UtlXMwuQU8CMgg1eZXqTRwkSQJWKOI= -golang.org/x/crypto v0.0.0-20210711020723-a769d52b0f97/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= -golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= -golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= -golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= -golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= -golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20181220203305-927f97764cc3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= -golang.org/x/net v0.0.0-20190522155817-f3200d17e092/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= -golang.org/x/net v0.0.0-20190827160401-ba9fcec4b297/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20210226172049-e18ecbb05110 h1:qWPm9rbaAMKs8Bq/9LRpbMqxWRVUAQwMI9fVrssnTfw= -golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= -golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= -golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20181107165924-66b7b1311ac8/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20181205085412-a5c9d58dba9a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190813064441-fde4db37ae7a/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200212091648-12a6c2dcc1e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20210806184541-e5e7981a1069 h1:siQdpVirKtzPhKl3lZWozZraCFObP8S1v6PRp0bLrtU= -golang.org/x/sys v0.0.0-20210806184541-e5e7981a1069/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= -golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= -golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= -golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/text v0.3.6 h1:aRYxNxv6iGQlyVaZmk6ZgYEDa+Jg18DxebPSrd6bg1M= -golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= -golang.org/x/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= -golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= -golang.org/x/tools v0.0.0-20181030221726-6c7e314b6563/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= -golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= -golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= -golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= -golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= -google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= -google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= -google.golang.org/genproto v0.0.0-20190620144150-6af8c5fc6601/go.mod h1:z3L6/3dTEVtUr6QSP8miRzeRqwQOioJ9I66odjN4I7s= -google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= -google.golang.org/grpc v1.21.0/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= -google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= -google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= -google.golang.org/protobuf v1.28.0 h1:w43yiav+6bVFTBQFZX0r7ipe9JQ1QsbMgHwbBziscLw= -google.golang.org/protobuf v1.28.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= -gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= -gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= -gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= -gopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo= -gopkg.in/yaml.v2 v2.0.0-20170812160011-eb3733d160e7/go.mod h1:JAlM8MvJe8wmxCU4Bli9HhUf9+ttbYbLASfIpnQbh74= -gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= -gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= -gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= -gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= -gotest.tools v2.2.0+incompatible/go.mod h1:DsYFclhRJ6vuDpmuTbkuFWG+y2sxOXAzmJt81HFBacw= -honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= diff --git a/scripts/search-server/server.go b/scripts/search-server/server.go deleted file mode 100644 index be6ab0d..0000000 --- a/scripts/search-server/server.go +++ /dev/null @@ -1,183 +0,0 @@ -package main - -import ( - "github.com/gin-gonic/gin" - "database/sql" - "log" - "os" - "fmt" - "strconv" - _ "github.com/mattn/go-sqlite3" -) - -func main() { - database_path, ok := os.LookupEnv("DB_PATH") - if !ok { - log.Fatal("Environment variable $DB_PATH must point to sqlite3 database with text indices.") - os.Exit(1) - } - - port := 3000 - port_str, ok := os.LookupEnv("PORT") - if ok { - port_num, err := strconv.Atoi(port_str) - - if err != nil { - log.Fatal(err) - os.Exit(1) - } - if (port_num < 1) || (port_num > 65535) { - log.Fatal("Environment variale $PORT must be a number between 1 and 65535.") - os.Exit(1) - } - port = port_num - } - - snippet_size := 128 - snippet_size_str, ok := os.LookupEnv("SNIPPET_SIZE") - if ok { - snippet_size_num, err := strconv.Atoi(snippet_size_str) - - if err != nil { - log.Fatal(err) - os.Exit(1) - } - if (snippet_size_num < 64) { - log.Fatal("Environment variale $SNIPPET_SIZE must be >= 64.") - os.Exit(1) - } - snippet_size = snippet_size_num - } - - db, err := sql.Open("sqlite3", database_path) - if err != nil { - log.Fatal(err) - os.Exit(1) - } - - r := gin.Default() - - api := r.Group("/api") - - api.GET("/ping", func(c *gin.Context) { - c.JSON(200, gin.H{ - "message": "pong", - }) - }) - - api.OPTIONS("/search", func(c *gin.Context) { - c.Writer.Header().Set("Access-Control-Allow-Origin", "*") - c.Writer.Header().Set("Access-Control-Allow-Headers", "Accept-Encoding, Authorization, accept, origin, Cache-Control, X-Requested-With") - c.Writer.Header().Set("Access-Control-Allow-Methods", "GET, OPTIONS") - - c.AbortWithStatus(204) - }) - - api.GET("/search", func(c *gin.Context) { - c.Writer.Header().Set("Access-Control-Allow-Origin", "*") - c.Writer.Header().Set("Access-Control-Allow-Headers", "Accept-Encoding, Authorization, accept, origin, Cache-Control, X-Requested-With") - c.Writer.Header().Set("Access-Control-Allow-Methods", "GET, OPTIONS") - - query := c.Query("q") - body_type := c.Query("body") - - if ((body_type != "all") && (body_type != "none") && (body_type != "snippet")) { - body_type = "none" - } - - var stm *sql.Stmt - var err error - - if (body_type == "snippet") { - stm, err = db.Prepare("SELECT note_id, highlight(note_search, 1, '', ''), top_level_title, is_done, is_todo, snippet(note_search, 2, '', '', '', ?) FROM note_search(?)") - } else if (body_type == "all") { - stm, err = db.Prepare("SELECT note_id, highlight(note_search, 1, '', ''), top_level_title, is_done, is_todo, highlight(note_search, 2, '', '') FROM note_search(?)") - } else if (body_type == "none") { - stm, err = db.Prepare("SELECT note_id, highlight(note_search, 1, '', ''), top_level_title, is_done, is_todo FROM note_search(?)") - } - - - if err != nil { - log.Fatal(err) - c.JSON(500, gin.H{ - "success": false, - "message": "Error preparing note-search query", - }) - return - } - - results := make([]map[string]string, 0) - var rows *sql.Rows - - if (body_type == "snippet") { - rows, err = stm.Query(snippet_size, query) - } else { - rows, err = stm.Query(query) - } - if err != nil { - log.Fatal(err) - c.JSON(500, gin.H{ - "success": false, - "message": "Error querying note DB", - }) - return - } - - for rows.Next() { - var note_id string - var note_title string - var note_top_level_title string - var note_is_done string - var note_is_todo string - - item := make(map[string]string) - - if (body_type != "none") { - var note_highlight string - - err = rows.Scan( - ¬e_id, - ¬e_title, - ¬e_top_level_title, - ¬e_is_done, - ¬e_is_todo, - ¬e_highlight, - ) - if (body_type != "none") { - item["highlight"] = note_highlight - } - } else { - err = rows.Scan( - ¬e_id, - ¬e_title, - ¬e_top_level_title, - ¬e_is_done, - ¬e_is_todo, - ) - } - if err != nil { - log.Fatal(err) - c.JSON(500, gin.H{ - "success": false, - "message": "Error reading note DB results", - }) - return - } - - item["id"] = note_id - item["title"] = note_title - item["top_level_title"] = note_top_level_title - item["is_done"] = note_is_done - item["is_todo"] = note_is_todo - results = append(results, item) - } - - c.JSON(200, gin.H{ - "results": gin.H{ - "notes": results, - }, - }) - }) - - r.Run(fmt.Sprintf(":%v", port)) -} diff --git a/scripts/test-links.py b/scripts/test-links.py deleted file mode 100644 index 1fbd25e..0000000 --- a/scripts/test-links.py +++ /dev/null @@ -1,59 +0,0 @@ -#!/usr/bin/env python3 - -import logging -import os -import sys -import urllib.parse - -from bs4 import BeautifulSoup as bs4 - -from tqdm import tqdm - -def main(files_top): - - print("Listing files...") - found_files = [] - for root, dirs, files in os.walk(files_top): - for name in files: - if name.endswith('.html'): - found_files.append(os.path.join(root, name)) - print("\r{} files".format(len(found_files)), end='', flush=True) - - print() - found_broken = 0 - for fpath in tqdm(found_files): - with open(fpath) as f: - tree = bs4(f.read(), features='lxml', parser='html5') - - for tag, attr in [('a', 'href'), ('img', 'src'), ('audio', 'src'), ('video', 'src')]: - for link in tree.find_all(tag): - if attr not in link.attrs: - continue - link.attrs[attr] = link.attrs[attr].split('#')[0] - if not link.attrs[attr]: - continue - if ':' in link[attr]: - continue - if link[attr].startswith('/'): - target = os.path.join(os.path.abspath(files_top), urllib.parse.unquote(link[attr].lstrip('/'))) - else: - target = os.path.join(os.path.dirname(fpath), urllib.parse.unquote(link[attr])) - if os.path.isdir(target): - pass - elif not os.path.exists(target): - print("[{}] -[ error ]-> {} | {}".format(fpath, target, link[attr])) - found_broken += 1 - - if found_broken: - print(f"Found {found_broken} broken links") - exit(1) - else: - exit(0) - -if __name__ == "__main__": - if len(sys.argv) != 2: - print("Usage: {} FILES_TOP".format(sys.argv[0])) - exit(0) - - logging.basicConfig(level=logging.INFO, format="%(levelname)-8s %(message)s") - exit(main(sys.argv[1])) diff --git a/scripts/upload-homepage.sh b/scripts/upload-homepage.sh new file mode 100644 index 0000000..8d475ef --- /dev/null +++ b/scripts/upload-homepage.sh @@ -0,0 +1,8 @@ +#!/usr/bin/env bash + +set -eu + +cd "$(dirname "$0")/.." + +cd static +scp homepage.html root@codigoparallevar.com:/mnt/vols/misc/codigoparallevar/index.html diff --git a/scripts/upload.sh b/scripts/upload.sh deleted file mode 100644 index 2a213b9..0000000 --- a/scripts/upload.sh +++ /dev/null @@ -1,30 +0,0 @@ -#!/usr/bin/env bash - -set -eu - -cd "$(dirname "$0")/.." - -# Upload homepage -cd static -scp homepage.html root@codigoparallevar.com:/mnt/vols/misc/codigoparallevar/index.html - -# Build notes -cd ../scripts -rm -Rf ../_gen/notes -WATCH_AND_REBUILD=0 python3 generate.py ~/.logs/brain ../_gen/notes - -rm -Rf ../_gen/blog -WATCH_AND_REBUILD=0 python3 blog.py ~/cloud/nextcloud/blog/posts/ ../_gen/blog - -rm -Rf ../_gen/static -cp -Rv ../static ../_gen/static - -# Upload notes -cd ../_gen -rsync -HPaz static/ --delete-after --exclude='*.html' root@codigoparallevar.com:/mnt/vols/misc/codigoparallevar/static/ -rsync -HPaz notes/ --delete-after --exclude='xapian' --exclude='*.sqlite3' root@codigoparallevar.com:/mnt/vols/misc/codigoparallevar/notes/ -rsync -HPaz notes/db.sqlite3 root@codigoparallevar.com:/mnt/vols/misc/codigoparallevar-api/ -rsync -HPaz blog/ --delete-after --exclude='xapian' --exclude='*.sqlite3' root@codigoparallevar.com:/mnt/vols/misc/codigoparallevar/blog/ - -# Restart API server -ssh root@codigoparallevar.com docker restart notes-api-server diff --git a/static/article.tmpl.html b/static/article.tmpl.html index 6484b84..d2750ea 100644 --- a/static/article.tmpl.html +++ b/static/article.tmpl.html @@ -2,21 +2,18 @@ - {{ title }} @ Código para llevar [blog] + {{ title }} @ Código para llevar - - - + + + - +