Commit

Author:

Hash:

Timestamp:

+184 -0 +/-9 browse

Kevin Schoon [me@kevinschoon.com]

30d4b6432fbc0e349787bcc3398b1295055241de

Tue, 13 May 2025 17:35:54 +0000 (3 weeks ago)

automated www update
1diff --git a/gen/blog/announcing-ayllu-0.4/index.html b/gen/blog/announcing-ayllu-0.4/index.html
2new file mode 100644
3index 0000000..e134003
4--- /dev/null
5+++ b/gen/blog/announcing-ayllu-0.4/index.html
6 @@ -0,0 +1,29 @@
7+ <!doctypehtml><html lang=en><title>kevinschoon-dot-com</title><meta charset=UTF-8><meta content=width=device-width,initial-scale=1 name=viewport><link href=/main.css rel=stylesheet><link href=https://kevinschoon.com/index.xml rel=alternate title=RSS type=application/rss+xml><body><section><div class=wrapper><header class=page-header><a href=/>Home</a></header><main class=page-body><h1>Ayllu 0.4 Has Been Released!</h1><p><code>05/13/2025</code><p><br><p>Ayllu release <a href=https://ayllu-forge.org/ayllu/ayllu/refs/tag/0.4.0>0.4.0</a> has been tagged after a year of inconsistent development. This release has seen a major <em>reduction</em> in the number of features available in Ayllu and a simplification of it's operation. Most of the features that have been removed will eventually make their way back into the software as time permits and can be done in a way that is simple to maintain. Although the core functionality of Ayllu has been minimized, two new major (unfinished) libraries were developed adjacently: <a href=https://ayllu-forge.org/ayllu/maitred>maitred</a>, an embedable SMTP server, and <a href=https://ayllu-forge.org/ayllu/papyri>papyri</a>, an OCI compliant container registry. See more details below.<h2>Notable Reductions</h2><h3>The Ayllu Binary is Now Stateless</h3><p>The <code>ayllu-jobs</code> binary has been temporarily removed after several iterations of implementing sqlite based state. The binary is responsible for doing CLOC anaylsis with <a href=https://github.com/XAMPPRocky/tokei>Tokei</a>, calculating authorship statistics, and anything else that needs to be run at a periodic interval. The jobs themselves have worked well but I've not been satisfied with any approach I've used to communicate stateful data into the UI in a way that is extensible. Ultimately my goal is to maintain an ultra fast static web interface that can be easily extended with new kinds of repository information. A future update will re-enable the state as opt-in likely via an RPC interface or shared database crate.<h3>Git Blame is Removed</h3><p>Although the server has the ability to generate everything that is needed to display <code>git blame</code> data I haven't been able to design UX that looks good enough to present it so I've opted to remove the code for now. If anyone has suggestions for how to do it in pure html/css please reach out.<h3>Simplified Themeing</h3><p>The themeing engine was reverted back from Tera to Askama see commit: <a href=https://ayllu-forge.org/ayllu/ayllu/commit/5c5e94b2eff4d4181bd59ed359dabb509b0a34fc>5c5e94</a>.<h2>New Features</h2><h3>Web Finger Protocol Support</h3><p>A concept of static "membership" has been added to the server which is browsable over the <a href=https://en.wikipedia.org/wiki/WebFinger>webfinger</a> protocol. You can use the <code>quipu</code> binary to query resources on an any Ayllu server:<pre><code class=language-sh>quipu finger "acct:demo@example.org"
8+ </code></pre><pre><code class=language-json>{
9+ "subject": "demo@example.org",
10+ "links": [
11+ {
12+ "rel": "http://webfinger.net/rel/profile-page",
13+ "type": "text/html",
14+ "href": "https://example.org/demo"
15+ },
16+ {
17+ "rel": "http://webfinger.net/rel/profile-page",
18+ "type": "text/html",
19+ "href": "https://example.org/@demo"
20+ },
21+ {
22+ "rel": "http://webfinger.net/rel/avatar",
23+ "href": "https://example.org/avatar.png"
24+ },
25+ {
26+ "rel": "http://ayllu-forge.org/rel/description",
27+ "properties": {
28+ "text": "Programmer interested free software"
29+ }
30+ }
31+ ]
32+ }
33+ </code></pre><pre><code class=language-sh># Or via curl
34+ curl 'http://localhost:10000/.well-known/webfinger?resource=acct:demo@example.org' |jq .
35+ </code></pre><p>Currently the server only can share static account information from the global configuration file but future updates will expose repository and other information. Eventually there are plans to implement global searchable index of Ayllu instances which will crawl this protocol data.<h3>Mirror Aware Repositories</h3><p>Ayllu will detect repositories that contain origins that have the <code>mirror</code> flag enabled and display them as "non-canonical" sources in the UI.<h3>Multiuser Container Image</h3><p>The <a href=https://ayllu-forge.org/ayllu/ayllu/tree/main/containers/multiuser>multiuser</a> container image has been further developed and offers a simple way for managing trusted multi-user forge environments.<h3>New Crates</h3><p>Two new crates <a href=https://ayllu-forge.org/ayllu/ayllu/tree/main/ayllu-shell>ayllu-shell</a> and <a href=https://ayllu-forge.org/ayllu/ayllu/tree/main/ayllu-keys>ayllu-keys</a> were added which when completed will allow a simple static configuration of user authorization into an Ayllu managed server.<h2>Supplemental Libraries</h2><h3>Maitred SMTP Server</h3><p>A new crate offering an embeddable SMTP server has been developed called <a href=https://ayllu-forge.org/ayllu/maitred>maitred</a> for use in Ayllu so that it can easily receive patches over SMTP. This project is made possible by the low-level protocol implementation of the <a href=https://github.com/stalwartlabs/mail-server>stalwart</a> project. Most of this crate is functional however ARC and DMARC verification need to be finished before we can safely handle messages. Once completed this will be implemented as part of the <code>ayllu-mail</code> binary.<h3>Papari OCI Container Registry</h3><p>The <a href=https://ayllu-forge.org/ayllu/papyri>Papyri</a> OCI container registry has started development which will provide the ability to read and write container images directly into an Ayllu instance. This crate currently only supports direct file system storage but a SQLite blob backend or possibly S3 compatible store may be added. This crate is shipped with an export of Axum routes and once integrated will allow pushing and pulling OCI manifests directly from the web interface.<h1></h1><p>That's all for now, thanks for reading!</main><footer class=page-footer></footer></div></section>
36\ No newline at end of file
37 diff --git a/gen/blog/building-this-website/index.html b/gen/blog/building-this-website/index.html
38new file mode 100644
39index 0000000..a8bffa3
40--- /dev/null
41+++ b/gen/blog/building-this-website/index.html
42 @@ -0,0 +1,97 @@
43+ <!doctypehtml><html lang=en><title>kevinschoon-dot-com</title><meta charset=UTF-8><meta content=width=device-width,initial-scale=1 name=viewport><link href=/main.css rel=stylesheet><link href=https://kevinschoon.com/index.xml rel=alternate title=RSS type=application/rss+xml><body><section><div class=wrapper><header class=page-header><a href=/>Home</a></header><main class=page-body><h1>Building This Website</h1><p>There is no deficiency of static website generators these days and so naturally I had to write my own.<h2>Overview</h2><p>The main functionality of the tool will be encapsulated in a Python script called <code>render.py</code>. Even though <a href=https://www.devever.net/~hl/stringtemplates#narrow>string based templates are the wrong solution</a> we're going to use a combination of <a href=https://jinja.palletsprojects.com/>Jinja</a> and markdown to generate HTML output. Each page will be generated by a single call to the script which will allow the whole thing to be ran in parallel across multiple CPUs by wiring it together using <a href=https://ninja-build.org/>ninja</a>.<p>Here you can see an example invocation of the <code>render.py</code> script with some annotated flags.<pre><code>./render.py \
44+ # the HTML template file
45+ --template=index.jinja \
46+ # this contains a map of each webpage which is available in the renderer
47+ # as well as page level configuration options.
48+ --sitemap sitemap.yaml \
49+ # markdown file, this one contains the text I'm currently typing!
50+ --content=blog/building-this-website/README.md \
51+ # output of the rendered HTML page
52+ gen/blog/building-this-website/index.html
53+ </code></pre><h3>Hierarchical Sitemap</h3><p>I want to generate a "tree" sitemap similar to a file browser like NERDTree. We'll pass in a YAML document to the <code>render.py</code> script and then transform it into an XML tree.<pre><code>def _load_sitemap(path, current_path):
54+ by_name = dict()
55+ flattened = []
56+ with open(path, "r") as fp:
57+ links = yaml.safe_load(fp.read())
58+
59+ def _link(links, parent=None):
60+ for link in links:
61+ flattened.append(link)
62+ link["parent"] = parent
63+ if "others" in link:
64+ _link(link["others"], parent=link)
65+
66+ def _make_url(link):
67+ path = [link["name"]]
68+ parent = link["parent"]
69+ while parent is not None:
70+ path.append(parent["name"])
71+ parent = parent["parent"]
72+ path.reverse()
73+ return "/".join(path)
74+
75+ _link(links, parent=None)
76+
77+ for link in flattened:
78+ target = "/" + _make_url(link)
79+ if target == current_path:
80+ link["active"] = True
81+ else:
82+ link["active"] = False
83+ link["url"] = target
84+ by_name[target] = link
85+
86+ return dict(by_name=by_name, links=links)
87+
88+
89+ def _make_tree(links):
90+ def _populate(root, links):
91+ for link in links:
92+ elm = ET.Element("li")
93+ if "directory" in link and link["directory"]:
94+ elm.text = link["name"]
95+ else:
96+ lref = ET.Element("a", href=link["url"])
97+ lref.text = link["name"]
98+ if link["active"]:
99+ lref.text = lref.text + " <"
100+ elm.append(lref)
101+ if "others" in link:
102+ others = link["others"]
103+ ul = ET.Element("ul")
104+ elm.append(ul)
105+ _populate(root=ul, links=others)
106+ root.append(elm)
107+
108+ params = {"class": "tree"}
109+
110+ root = ET.Element("ul", **params)
111+ _populate(root=root, links=links)
112+ return ET.tostring(root).decode()
113+ </code></pre><h3>Dithering Engine</h3><p>Images can be reduced in size with <a href=https://en.wikipedia.org/wiki/Dither>dithering algorithms</a> and they also look very cool. The idea for this was inspired by <a href=https://solar.lowtechmagazine.com/>LOW←TECH MAGAZINE</a>.<h3>Spell & Grammar Checking</h3><p>I can implement some basic spell checking against the website with <a href=http://aspell.net/>aspell</a>.<pre><code class=language-sh>find content -name '*.md' -exec aspell --mode=markdown check {} \;
114+ </code></pre><h3>Filesystem "Watch" Support</h3><p>We'll use the native <a href=https://en.wikipedia.org/wiki/Inotify>inotify</a> via the <a href=https://man7.org/linux/man-pages/man1/inotifywatch.1.html>inotifywatch</a> tool which is available on all Linux distributions.<pre><code class=language-sh>#!/bin/sh
115+
116+ # the actual command we want to run each time any file changes in the project
117+ BUILD_CMD="$@"
118+
119+ # some pretty colorized output
120+ _do_build() {
121+ $BUILD_CMD && {
122+ echo -e "\033[32;1;4mSuccess\033[0m"
123+ } || {
124+ echo -e "\033[31;1;4mFailure\033[0m"
125+ }
126+ }
127+
128+ # build the project the first time you start up the watch
129+ _do_build
130+
131+ # watch all the files in the project except for the output directory
132+ find . -type d -not -path './gen*' -printf '%p ' | xargs inotifywait \
133+ -m -e close_write \
134+ --format %e/%f |
135+ while IFS=/ read -r events file; do
136+ echo "file $file modified, rebuilding"
137+ _do_build
138+ done
139+ </code></pre><p>Now we can test the script by launching it <code>scripts/watch.sh build.py</code>.<p>You can checkout the source for the whole site <a href=https://ayllu-forge.org/web/kevinschoon-dot-com>here</a>.</main><footer class=page-footer></footer></div></section>
140\ No newline at end of file
141 diff --git a/gen/contact.png b/gen/contact.png
142new file mode 100644
143index 0000000..e74e5b7
144 Binary files /dev/null and b/gen/contact.png differ
145 diff --git a/gen/favicon.ico b/gen/favicon.ico
146new file mode 100644
147index 0000000..59a4e28
148 Binary files /dev/null and b/gen/favicon.ico differ
149 diff --git a/gen/index.html b/gen/index.html
150new file mode 100644
151index 0000000..5aa14db
152--- /dev/null
153+++ b/gen/index.html
154 @@ -0,0 +1 @@
155+ <!doctypehtml><html lang=en><title>kevinschoon-dot-com</title><meta charset=UTF-8><meta content=width=device-width,initial-scale=1 name=viewport><link href=/main.css rel=stylesheet><link href=https://kevinschoon.com/index.xml rel=alternate title=RSS type=application/rss+xml><body><section><div class=wrapper><header class=page-header></header><main class=page-body><p><a href=/><img alt="dithered photo of me"class=me src=/ks_stylized.png></a><h1>Hi, thanks for stopping by!</h1><p>I'm a programmer interested in free software.<h3>Sitemap</h3><p><ul class=tree><li><a href=/blog>blog</a><ul><li><a href=/blog/announcing-ayllu-0.4>announcing-ayllu-0.4 [2025-05-13]</a><li><a href=/blog/building-this-website>building-this-website [2023-04-04]</a></ul></ul><h3>Email</h3><p>You can contact me at <a href=mailto:me@kevinschoon.com>me@kevinschoon.com</a>.<h3>Software</h3><p>Check out some of my past and present projects <a href=https://ayllu-forge.org/browse>here</a>.<h3>Contact (QR)</h3><p>Contact info (same as above, but a QR code):<p><img alt="qr code"class=contact src=/contact.png><h3>Subscribe</h3><p><a href=/index.xml>RSS</a></main><footer class=page-footer></footer></div></section>
156\ No newline at end of file
157 diff --git a/gen/index.xml b/gen/index.xml
158new file mode 100644
159index 0000000..df4d72c
160--- /dev/null
161+++ b/gen/index.xml
162 @@ -0,0 +1,2 @@
163+ <?xml version='1.0' encoding='utf-8'?><?xml-stylesheet href='/style.xsl' type='text/xsl'?>
164+ <rss version="2.0" encoding="utf-8"><channel><title>Blog of Kevin Schoon</title><link href="https://kevinschoon.com" /><description>Blog of Kevin Schoon</description><item><title>announcing-ayllu-0.4</title><link href="https://kevinschoon.com/blog/announcing-ayllu-0.4" /><guid>https://kevinschoon.com/blog/announcing-ayllu-0.4</guid><description>Ayllu version 0.4 has been released.</description></item><item><title>building-this-website</title><link href="https://kevinschoon.com/blog/building-this-website" /><guid>https://kevinschoon.com/blog/building-this-website</guid><description>There is no deficiency of static website generators these days and so I had to write my own.</description></item></channel></rss>
165\ No newline at end of file
166 diff --git a/gen/ks_stylized.png b/gen/ks_stylized.png
167new file mode 100644
168index 0000000..0123974
169 Binary files /dev/null and b/gen/ks_stylized.png differ
170 diff --git a/gen/main.css b/gen/main.css
171new file mode 100644
172index 0000000..fc6eb20
173--- /dev/null
174+++ b/gen/main.css
175 @@ -0,0 +1 @@
176+ :root{--accent: #b1675d;--light-gray: #eee;--dark-gray: #c2c2c2;--white: #f8f9fa;--black: #2d2d2d;--border-width: 5px}table{text-align:justify;width:100%;border-collapse:collapse;margin-bottom:2rem}td,th{padding:0.5em;border-bottom:1px solid #4a4a4a}html,body{box-sizing:border-box;font-size:16px;height:100%;margin:0;padding:0;max-width:800px}section{height:100%}.wrapper{min-height:100%;display:grid;grid-template-rows:auto 1fr auto}.page-body{padding:20px;max-inline-size:70ch}*,*:before,*:after{box-sizing:inherit}ol,ul{list-style:none}p{padding:1px;margin:10px}span{background:white;border:1px solid black}pre{background:#e2e2e2;font-family:monospace;overflow:scroll}img.me{border-radius:2em;max-width:350px}main.rss-page{margin:20px}@media (prefers-color-scheme: dark){:root{background-color:#2c2c2c;color:white}pre{color:white;background:#3f3f3f}a:link{color:white;text-decoration:underline}a:visited{color:white;text-decoration:none}.notice{border-color:white}}@media (prefers-color-scheme: light){:root{background-color:#f5f5f5;color:black}pre{color:black;background:#e2e2e2}a:link{color:black;text-decoration:underline}a:visited{color:black;text-decoration:none}.notice{border-color:black}}.tree,.tree ul{font-size:18px;font:normal normal 14px/20px Helvetica, Arial, sans-serif;list-style-type:none;margin-left:0 0 0 10px;padding:0;position:relative;overflow:hidden}.tree li{margin:0;padding:0 12px;position:relative}.tree li::before,.tree li::after{content:"";position:absolute;left:0}.tree li::before{border-top:2px solid #999;top:10px;width:10px;height:0}.tree li:after{border-left:2px solid #999;height:100%;width:0px;top:-10px}.tree>li::after{top:10px}.tree>li:last-child::after{display:none}
177 diff --git a/gen/style.xsl b/gen/style.xsl
178new file mode 100644
179index 0000000..10b5101
180--- /dev/null
181+++ b/gen/style.xsl
182 @@ -0,0 +1,54 @@
183+ <?xml version="1.0" encoding="utf-8"?>
184+ <xsl:stylesheet version="3.0"
185+ xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
186+ xmlns:atom="http://www.w3.org/2005/Atom"
187+ xmlns:dc="http://purl.org/dc/elements/1.1/"
188+ xmlns:itunes="http://www.itunes.com/dtds/podcast-1.0.dtd">
189+ <xsl:output method="html" version="1.0" encoding="UTF-8" indent="yes"/>
190+ <xsl:template match="/">
191+ <html lang="en">
192+ <head>
193+ <title>
194+ <xsl:value-of select="/rss/channel/title"/>
195+ </title>
196+ <link rel="stylesheet" href="/main.css" />
197+ </head>
198+ <body>
199+ <main class="rss-page">
200+ <article>
201+ <header>
202+ <h1>
203+ <img class="me" src="/ks_stylized.png"/>
204+ <a>
205+ <xsl:attribute name="href"> <xsl:value-of select="/rss/channel/link"/> </xsl:attribute>
206+ </a>
207+ </h1>
208+ <h2><xsl:value-of select="/rss/channel/description"/></h2>
209+ </header>
210+ <p><strong>This is an <a href="https://en.wikipedia.org/wiki/RSS">RSS feed</a>.</strong></p>
211+ <p>Copy the link from the address bar into your feed reader to receive regular updates.</p>
212+ </article>
213+ <xsl:for-each select="/rss/channel/item">
214+ <article class="card">
215+ <header>
216+ <h3>
217+ <a>
218+ <xsl:attribute name="href">
219+ <xsl:value-of select="link"/>
220+ </xsl:attribute>
221+ <xsl:value-of select="title"/>
222+ </a>
223+ </h3>
224+ </header>
225+ <xsl:value-of select="description" disable-output-escaping="yes"/>
226+ <footer class="rss">
227+ <xsl:value-of select="author" /><br/>
228+ <b><xsl:value-of select="pubDate"/></b>
229+ </footer>
230+ </article>
231+ </xsl:for-each>
232+ </main>
233+ </body>
234+ </html>
235+ </xsl:template>
236+ </xsl:stylesheet>