changes
All checks were successful
Build Org Website / build (push) Successful in 52s

This commit is contained in:
2026-05-07 16:21:41 +01:00
parent 3d87e3d1e8
commit c65df7d95a
17 changed files with 140 additions and 81 deletions

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.2 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 22 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 22 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 22 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 22 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 22 KiB

View File

@@ -24,13 +24,58 @@ from typing import Any
from urllib.parse import parse_qs, urlparse
ROOT = Path(__file__).resolve().parent
def looks_like_content_root(path: Path) -> bool:
return (path / "authoring_server.py").exists() and (
(path / "blogs").exists()
or (path / "posts").exists()
or (path / "lima").exists()
)
def resolve_root() -> Path:
env_root = os.environ.get("AUTHOR_ROOT")
if env_root:
return Path(env_root).expanduser().resolve()
candidates = [
Path.cwd(),
Path(__file__).resolve().parent,
]
workspace = os.environ.get("GITHUB_WORKSPACE")
if workspace:
candidates.insert(0, Path(workspace))
for base in list(candidates):
candidates.extend(base.parents)
seen = set()
for candidate in candidates:
resolved = candidate.expanduser().resolve()
if resolved in seen:
continue
seen.add(resolved)
if looks_like_content_root(resolved):
return resolved
return Path(__file__).resolve().parent
ROOT = resolve_root()
BLOGS_DIR = ROOT / "blogs"
POSTS_DIR = ROOT / "posts"
LIMA_DIR = ROOT / "lima"
HZONE_ASSETS_DIR = ROOT / "assets" / "images" / "hzone"
EXCLUDED_CONTENT_DIR_NAMES = {
".agents",
".codex",
".git",
".packages",
".venv",
"__pycache__",
"assets",
"backups",
"output",
"tags",
}
GENERATED_ORG_NAMES = {
"blogs-list.org",
"books-list.org",
"posts-list.org",
"career-list.org",
"sitemap.org",
@@ -249,12 +294,16 @@ def safe_relative_path(path: str) -> Path:
full = (ROOT / rel).resolve()
if not full.is_relative_to(ROOT):
raise ValueError("Path must stay inside this repository.")
if full.suffix == ".org" and (full.is_relative_to(BLOGS_DIR) or full.is_relative_to(POSTS_DIR)):
if full.name in GENERATED_CONTENT_NAMES or "sync-conflict" in full.name:
raise ValueError("Generated and sync-conflict files are not editable here.")
rel_parts = full.relative_to(ROOT).parts
if any(part in EXCLUDED_CONTENT_DIR_NAMES for part in rel_parts):
raise ValueError("This path is outside the editable content folders.")
if full.suffix == ".org":
return full
if full.suffix == ".md" and full.is_relative_to(LIMA_DIR):
return full
raise ValueError("Only .org files under blogs/posts and .md files under lima can be edited here.")
return full
raise ValueError("Only .org content files and .md files under lima can be edited here.")
def safe_target_path(path: str, slug: str, page_type: str) -> Path:
@@ -310,7 +359,12 @@ def read_page(path: Path) -> ContentPage:
in_header = False
body_lines.append(line)
rel = path.relative_to(ROOT).as_posix()
page_type = "blog" if path.is_relative_to(BLOGS_DIR) else "post"
if path.is_relative_to(BLOGS_DIR):
page_type = "blog"
elif path.is_relative_to(POSTS_DIR):
page_type = "post"
else:
page_type = "page"
slug = meta.get("SLUG") or path.stem
tags = normalise_tags(meta.get("FILETAGS", ""))
return ContentPage(
@@ -346,14 +400,15 @@ def page_to_dict(page: ContentPage) -> dict[str, Any]:
def list_pages() -> list[dict[str, Any]]:
pages = []
for base, page_type, extension in (
(BLOGS_DIR, "blog", "*.org"),
(POSTS_DIR, "post", "*.org"),
(LIMA_DIR, "lima", "*.md"),
):
if not base.exists():
org_paths = []
if ROOT.exists():
for path in ROOT.rglob("*.org"):
rel_parts = path.relative_to(ROOT).parts
if any(part in EXCLUDED_CONTENT_DIR_NAMES for part in rel_parts):
continue
for path in sorted(base.rglob(extension)):
org_paths.append(path)
md_paths = list(LIMA_DIR.rglob("*.md")) if LIMA_DIR.exists() else []
for path in sorted(org_paths + md_paths):
if path.name in GENERATED_CONTENT_NAMES or "sync-conflict" in path.name:
continue
try:
@@ -364,7 +419,7 @@ def list_pages() -> list[dict[str, Any]]:
pages.append(
{
"path": page.path,
"pageType": page_type,
"pageType": page.page_type,
"title": page.title,
"slug": page.slug,
"tags": page.tags,
@@ -396,7 +451,9 @@ def target_path(data: dict[str, Any], existing_path: str | None) -> Path:
return folder / f"{slug}.org"
if page_type == "lima":
return LIMA_DIR / f"{slug}.md"
raise ValueError("pageType must be blog, post, or lima.")
if page_type == "page":
return ROOT / f"{slug}.org"
raise ValueError("pageType must be blog, post, page, or lima.")
def render_markdown(data: dict[str, Any]) -> str:
@@ -669,9 +726,10 @@ APP_HTML = r"""<!doctype html>
<div class="filters">
<input id="search" type="search" placeholder="Filter pages" />
<select id="typeFilter">
<option value="">Blogs and posts</option>
<option value="">All files</option>
<option value="blog">Blogs</option>
<option value="post">Posts</option>
<option value="page">Pages</option>
<option value="lima">Lima</option>
</select>
</div>
@@ -688,6 +746,7 @@ APP_HTML = r"""<!doctype html>
<select name="pageType">
<option value="blog">Blog</option>
<option value="post">Post</option>
<option value="page">Page</option>
<option value="lima">Lima</option>
</select>
</label>
@@ -819,11 +878,14 @@ APP_HTML = r"""<!doctype html>
const query = $("#search").value.toLowerCase();
const type = $("#typeFilter").value;
pagesBox.innerHTML = "";
state.pages
const matches = state.pages
.filter((page) => (!type || page.pageType === type))
.filter((page) => `${page.title} ${page.path} ${page.tags.join(" ")}`.toLowerCase().includes(query))
.slice(0, 120)
.forEach((page) => {
.slice(0, 120);
if (!matches.length) {
pagesBox.innerHTML = `<div class="queue-item"><span>No files matched.</span></div>`;
}
matches.forEach((page) => {
const btn = document.createElement("button");
btn.type = "button";
btn.className = "page-item";
@@ -938,8 +1000,12 @@ APP_HTML = r"""<!doctype html>
}
async function refreshPages() {
try {
state.pages = await api("/api/pages");
renderPages();
} catch (err) {
pagesBox.innerHTML = `<div class="queue-item"><span>${html(err.message)}</span></div>`;
}
}
async function refreshBuild() {
@@ -1002,6 +1068,10 @@ APP_HTML = r"""<!doctype html>
editor.targetPath.value = `lima/${slug}.md`;
return;
}
if (editor.pageType.value === "page") {
editor.targetPath.value = `${slug}.org`;
return;
}
if (editor.pageType.value === "post") {
const section = slugify(editor.section.value || "");
editor.targetPath.value = section === "untitled" ? `posts/${slug}.org` : `posts/${section}/${slug}.org`;
@@ -1299,6 +1369,7 @@ def main() -> None:
port = int(os.environ.get("AUTHOR_PORT", "8765"))
server = ThreadingHTTPServer(("127.0.0.1", port), Handler)
print(f"Authoring UI running at http://127.0.0.1:{port}")
print(f"Content root: {ROOT}")
print("Press Ctrl-C to stop.")
try:
server.serve_forever()

View File

@@ -0,0 +1,10 @@
#+TITLE: Using Codex
#+OPTIONS: num:nil
#+DATE: <2026-05-07 Thu 16:12>
#+filetags: :life:
#+COMMENTS: t
#+SLUG: using-codex-07-05-26
Recently started to play around with codex, installing the CLI, and having it do a few things I've been wanting to work on. So far it's exceeded my expectations. This page [[https://author.zainezq.com][Author]] has been created by Codex using a single python script.
I also had it create a powershell script to do an analysis of the build pipelines hosted in gitea.

View File

@@ -6,6 +6,7 @@ See the categories: @@html:<a href="../home/categories.html">Categories</a>@@
* 2026
** May 2026
- [[file:2026/05-may/using-codex-07-05-26.org][Using Codex]] @@html:<span class="post-date">07-05-2026 16:12</span>@@ @@html:<a href="/tags/life.html"><span class="post-tag">life</span></a>@@
- [[file:2026/05-may/03-05-week-review.org][[03-05-2026] - Weekly Review]] @@html:<span class="post-date">03-05-2026 12:00</span>@@ @@html:<a href="/tags/review.html"><span class="post-tag">review</span></a>@@
- [[file:2026/05-may/sun-soak-01-05.org][Soaking in the sun]] @@html:<span class="post-date">01-05-2026 12:27</span>@@ @@html:<a href="/tags/life.html"><span class="post-tag">life</span></a>@@

View File

@@ -7,7 +7,7 @@
- [[file:../tags/insights.org][@@html:<span class="post-tag">insights</span>@@]] (4)
- [[file:../tags/introduction.org][@@html:<span class="post-tag">introduction</span>@@]] (3)
- [[file:../tags/learning.org][@@html:<span class="post-tag">learning</span>@@]] (16)
- [[file:../tags/life.org][@@html:<span class="post-tag">life</span>@@]] (28)
- [[file:../tags/life.org][@@html:<span class="post-tag">life</span>@@]] (29)
- [[file:../tags/maths.org][@@html:<span class="post-tag">maths</span>@@]] (1)
- [[file:../tags/notes.org][@@html:<span class="post-tag">notes</span>@@]] (17)
- [[file:../tags/reading.org][@@html:<span class="post-tag">reading</span>@@]] (1)

View File

@@ -9,6 +9,3 @@ I made quite a few changes. It all started when I realised that not everyone kno
- I probably didnt explain it as well, but you can create folders and files under the `lima-website` directory. The files must have a `.md` extension at the end (markdown).
- One other thing, a more personal one: what do you think of all this? am i doing too much? am i doing too little? am i overengineering things? am i forcing you to do something you dont wanna do? these little trinkets work for me, but im not sure if it would work for someone else, so at any point if you have reservations, let me know okay?
![20260507-140053-example-png-image.png](../assets/images/hzone/2026/05/20260507-140053-example-png-image.png)

View File

@@ -4,7 +4,7 @@
See the categories: @@html:<a href="../home/categories.html">Categories</a>@@
* Posts:
- [[file:career/career-list.org][Career List]] @@html:<span class="post-date">07-05-2026 15:52</span>@@
- [[file:career/career-list.org][Career List]] @@html:<span class="post-date">07-05-2026 16:21</span>@@
- [[file:posts-list.sync-conflict-20260417-233432-VT6366A.org][Posts List]] @@html:<span class="post-date">14-04-2026 16:36</span>@@
- [[file:career/ha-dr.org][High Availability, Disaster Recovery and Business Continuity]] @@html:<span class="post-date">11-03-2026 17:18</span>@@ @@html:<a href="/tags/learning.html"><span class="post-tag">learning</span></a>@@ @@html:<a href="/tags/notes.html"><span class="post-tag">notes</span></a>@@
- [[file:career/javascript.org][Understands the Javascript language]] @@html:<span class="post-date">11-03-2026 16:52</span>@@ @@html:<a href="/tags/learning.html"><span class="post-tag">learning</span></a>@@ @@html:<a href="/tags/notes.html"><span class="post-tag">notes</span></a>@@

View File

@@ -2,6 +2,7 @@
#+OPTIONS: toc:nil num:nil
* Recently Updated (top 26 files)
- [[file:blogs/2026/05-may/using-codex-07-05-26.org][Using Codex]] @@html:<span class="post-date">2026-05-07 16:12</span>@@
- [[file:blogs/2026/05-may/03-05-week-review.org][[03-05-2026] - Weekly Review]] @@html:<span class="post-date">2026-05-03 12:00</span>@@
- [[file:blogs/2026/05-may/sun-soak-01-05.org][Soaking in the sun]] @@html:<span class="post-date">2026-05-01 12:27</span>@@
- [[file:blogs/2026/04-april/wise-words-29-04.org][Wise Words I need to engrain]] @@html:<span class="post-date">2026-04-29 11:31</span>@@
@@ -15,7 +16,6 @@
- [[file:blogs/2026/04-april/16-04-26.org][Rambles]] @@html:<span class="post-date">2026-04-16 16:02</span>@@
- [[file:posts/posts-list.sync-conflict-20260417-233432-VT6366A.org][Posts List]] @@html:<span class="post-date">2026-04-14 16:36</span>@@
- [[file:home/categories.sync-conflict-20260417-233429-VT6366A.org][Categories]] @@html:<span class="post-date">2026-04-14 16:36</span>@@
- [[file:recently-updated.sync-conflict-20260417-233430-VT6366A.org][Recently Updated]] @@html:<span class="post-date">2026-04-14 16:36</span>@@
- [[file:blogs/2026/04-april/comparison-14-04.org][Comparison is the thief of joy]] @@html:<span class="post-date">2026-04-14 09:55</span>@@
- [[file:blogs/2026/04-april/12-04-week-review.org][[12-04-2026] - Weekly Review]] @@html:<span class="post-date">2026-04-12 12:00</span>@@
- [[file:blogs/2026/04-april/starting-new-rotation-08-04-26.org][Starting new rotation]] @@html:<span class="post-date">2026-04-08 16:15</span>@@

View File

@@ -1,30 +0,0 @@
#+TITLE: Recently Updated
#+OPTIONS: toc:nil num:nil
* Recently Updated (top 26 files)
- [[file:blogs/2026/04-april/comparison-14-04.org][Comparison is the thief of joy]] @@html:<span class="post-date">2026-04-14 09:55</span>@@
- [[file:blogs/2026/04-april/12-04-week-review.org][[12-04-2026] - Weekly Review]] @@html:<span class="post-date">2026-04-12 12:00</span>@@
- [[file:blogs/2026/04-april/starting-new-rotation-08-04-26.org][Starting new rotation]] @@html:<span class="post-date">2026-04-08 16:15</span>@@
- [[file:blogs/2026/04-april/action-plan-07-04-26.org][Action plan to change teams]] @@html:<span class="post-date">2026-04-07 10:04</span>@@
- [[file:blogs/2026/04-april/weekly-target-06-04-26.org][[06-04-2026] - Weekly target]] @@html:<span class="post-date">2026-04-06 22:53</span>@@
- [[file:blogs/2026/04-april/making-notes-through-org-noter-06-05.org][Making Notes through Org Noter]] @@html:<span class="post-date">2026-04-06 14:30</span>@@
- [[file:blogs/2026/04-april/05-04-week-review.org][[05-04-2026] - Weekly Review]] @@html:<span class="post-date">2026-04-05 12:00</span>@@
- [[file:blogs/2026/04-april/joining-new-team-meeting-02-04-2026.org][New team meeting]] @@html:<span class="post-date">2026-04-02 16:03</span>@@
- [[file:home/wird-tracker.org][Wird Tracker]] @@html:<span class="post-date">2026-04-02 11:17</span>@@
- [[file:blogs/2026/03-march/intellectually-challenging-myself-30-03-26.org][Intellectually challenging oneself]] @@html:<span class="post-date">2026-03-31 11:14</span>@@
- [[file:blogs/2026/03-march/cooking-dinner-29-03-26.org][Cooking Dinner (num)]] @@html:<span class="post-date">2026-03-30 11:49</span>@@
- [[file:blogs/2026/03-march/29-03-week-review.org][[29-03-2026] - Weekly Review]] @@html:<span class="post-date">2026-03-29 12:00</span>@@
- [[file:blogs/2026/03-march/training-on-friday-afternoon-27-03-26.org][Training time on Friday afternoon]] @@html:<span class="post-date">2026-03-27 15:09</span>@@
- [[file:blogs/2026/03-march/more-thoughts-regarding-changes-26-03-26.org][More thoughts on schedule changes]] @@html:<span class="post-date">2026-03-26 12:33</span>@@
- [[file:blogs/2026/03-march/thoughts-on-life-25-03-26.org][Thoughts on life...]] @@html:<span class="post-date">2026-03-25 11:37</span>@@
- [[file:blogs/2026/03-march/quick-lunch-24-03.org][Making Lunch QUiCk!!]] @@html:<span class="post-date">2026-03-24 15:30</span>@@
- [[file:blogs/2026/03-march/installing-emacs-23-03.org][Installing Emacs on Windows]] @@html:<span class="post-date">2026-03-23 16:47</span>@@
- [[file:blogs/2026/03-march/22-03-week-review.org][[22-03-2026] - Weekly Review]] @@html:<span class="post-date">2026-03-22 12:00</span>@@
- [[file:blogs/2026/03-march/setting-a-wird-tracker-19-03.org][Setting a Wird Tracker]] @@html:<span class="post-date">2026-03-19 13:15</span>@@
- [[file:home/guide/wird-tracker-guide.org][Wird Tracker — Technical Guide]] @@html:<span class="post-date">2026-03-19 12:43</span>@@
- [[file:blogs/2026/03-march/fixing-the-dag-18-03.org][DAG fixes]] @@html:<span class="post-date">2026-03-18 15:01</span>@@
- [[file:blogs/2026/03-march/feeling-sleepy.org][Feeling extremely sleepy]] @@html:<span class="post-date">2026-03-16 16:18</span>@@
- [[file:blogs/2026/03-march/oversleeping-16-03.org][Oversleeping and missing a meeting...]] @@html:<span class="post-date">2026-03-16 11:21</span>@@
- [[file:blogs/2026/03-march/15-03-week-review.org][[15-03-2026] - Weekly Review]] @@html:<span class="post-date">2026-03-15 12:00</span>@@
- [[file:posts/career/ha-dr.org][High Availability, Disaster Recovery and Business Continuity]] @@html:<span class="post-date">2026-03-11 17:18</span>@@
- [[file:posts/career/javascript.org][Understands the Javascript language]] @@html:<span class="post-date">2026-03-11 16:52</span>@@

View File

@@ -1,7 +1,6 @@
#+TITLE: Sitemap
- [[file:index.org][Home]]
- [[file:recently-updated.sync-conflict-20260417-233430-VT6366A.org][Recently Updated]]
- [[file:wip.org][Work in progress]]
- [[file:recently-updated.org][Recently Updated]]
- tags
@@ -11,14 +10,14 @@
- [[file:tags/learning.org][Tag: learning]]
- [[file:tags/notes.org][Tag: notes]]
- [[file:tags/review.org][Tag: review]]
- [[file:tags/life.org][Tag: life]]
- [[file:tags/website.org][Tag: website]]
- [[file:tags/life.org][Tag: life]]
- [[file:tags/education.org][Tag: education]]
- [[file:tags/update.org][Tag: update]]
- [[file:tags/insights.org][Tag: insights]]
- [[file:tags/emacs.org][Tag: emacs]]
- [[file:tags/education.org][Tag: education]]
- [[file:tags/reading.org][Tag: reading]]
- [[file:tags/maths.org][Tag: maths]]
- [[file:tags/reading.org][Tag: reading]]
- home
- [[file:home/countdown.org][Countdown]]
- [[file:home/contact.org][Contact]]

View File

@@ -2,6 +2,7 @@
#+OPTIONS: toc:nil num:nil title:nil
* Posts tagged life
- [[file:../blogs/2026/05-may/using-codex-07-05-26.org][Using Codex]]
- [[file:../blogs/2026/05-may/sun-soak-01-05.org][Soaking in the sun]]
- [[file:../blogs/2026/04-april/wise-words-29-04.org][Wise Words I need to engrain]]
- [[file:../blogs/2026/04-april/april-almost-over-28-04.org][April is almost over...]]

View File

@@ -14,10 +14,14 @@ class AuthoringServerTestCase(unittest.TestCase):
self.blogs = self.root / "blogs"
self.posts = self.root / "posts"
self.lima = self.root / "lima"
self.home = self.root / "home"
self.tags = self.root / "tags"
self.hzone = self.root / "assets" / "images" / "hzone"
self.blogs.mkdir()
self.posts.mkdir()
self.lima.mkdir()
self.home.mkdir()
self.tags.mkdir()
patches = {
"ROOT": self.root,
@@ -71,9 +75,13 @@ class UtilityTests(AuthoringServerTestCase):
server.safe_relative_path("lima/index.md"),
self.lima / "index.md",
)
self.assertEqual(
server.safe_relative_path("home/notes.org"),
self.home / "notes.org",
)
def test_safe_relative_path_rejects_escapes_and_wrong_locations(self):
for path in ("../secret.org", "/tmp/secret.org", "sitemap.org", "lima/index.org"):
for path in ("../secret.org", "/tmp/secret.org", "sitemap.org", "assets/style.org", "tags/life.org"):
with self.subTest(path=path):
with self.assertRaises(ValueError):
server.safe_relative_path(path)
@@ -156,10 +164,12 @@ class PageRenderingTests(AuthoringServerTestCase):
(self.posts / "posts-list.org").write_text("#+TITLE: Generated\n", encoding="utf-8")
(self.blogs / "note.sync-conflict-1.org").write_text("#+TITLE: Conflict\n", encoding="utf-8")
(self.lima / "index.md").write_text("# Lima Home\n", encoding="utf-8")
(self.home / "notes.org").write_text("#+TITLE: Notes\n", encoding="utf-8")
(self.tags / "life.org").write_text("#+TITLE: Tag\n", encoding="utf-8")
paths = [page["path"] for page in server.list_pages()]
self.assertEqual(set(paths), {"blogs/keep.org", "lima/index.md"})
self.assertEqual(set(paths), {"blogs/keep.org", "home/notes.org", "lima/index.md"})
def test_relative_asset_path_is_calculated_from_lima_page_directory(self):
self.assertEqual(