This commit is contained in:
ed committed 2026-08-24 00:30:36 -04:00
1 parent 8f368b0dc3
commit 2a895e42d6
3 files changed
+106 -4

No files matched your search

+2
View File
@@ -60,6 +60,8 @@ jobs:
- name: Build Quartz
working-directory: site
run: npx quartz build -d ..
- name: Patch sidebar title link
run: python ../publish/quartz-patches/patch-title-link.py
- name: Upload artifact
uses: actions/upload-pages-artifact@v3
with:
@@ -0,0 +1,65 @@
"""Patch all built HTML files so the sidebar title link goes to /Threadwell/.
The `<h2 class="page-title">` link is generated by the
`@quartz-community/page-title` plugin from a relative href that breaks
when the URL bar lacks a trailing slash. We rewrite the href to the
absolute site base path so it never lands on the bare domain.
Idempotent. Safe to re-run.
"""
from __future__ import annotations
import re
import sys
from pathlib import Path
SCRIPT = Path(__file__).resolve()
VAULT = SCRIPT.parents[2]
# Detect base path from the publish overlay if present, else default to /Threadwell/
overlay = VAULT / "publish" / "quartz.config.yaml"
site_base = "/Threadwell/"
if overlay.is_file():
for line in overlay.read_text(encoding="utf-8").splitlines():
if line.startswith("baseUrl:"):
host = line.split(":", 1)[1].strip()
if "/" in host:
site_base = "/" + host.split("/", 1)[1].rstrip("/") + "/"
else:
site_base = "/"
break
PUBLIC = VAULT / "site" / "public"
PATTERN = re.compile(
r'(<h2\s+class="page-title"><a\s+href=")[^"]*(">)'
)
REPLACEMENT = rf"\1{site_base}\2"
def patch_html(path: Path) -> bool:
text = path.read_text(encoding="utf-8", errors="ignore")
new = PATTERN.sub(REPLACEMENT, text)
if new != text:
path.write_text(new, encoding="utf-8")
return True
return False
def main() -> int:
if not PUBLIC.is_dir():
print(f"missing {PUBLIC}")
return 1
changed = 0
total = 0
for path in PUBLIC.rglob("*.html"):
total += 1
if patch_html(path):
changed += 1
print(f"site base: {site_base}")
print(f"total {total}, changed {changed}")
return 0
if __name__ == "__main__":
raise SystemExit(main())
+39 -4
View File
@@ -9,6 +9,9 @@ Default without --orig: only rows already publish=true.
After upload, any note token that is the local filename (backticked or
bare) or a previous url for that row becomes ![](https://files.catbox.moe/…).
Per-upload curl: --max-time 120, --retry 3, --retry-delay 2, --retry-all-errors.
Errors are sanitized to redact the userhash before printing.
"""
from __future__ import annotations
@@ -17,12 +20,28 @@ import json
import re
import subprocess
import sys
import time
import tomllib
from pathlib import Path
from paths import VAULT as VAULT_ROOT
API = "https://catbox.moe/user/api.php"
MAX_TIME = 120
RETRIES = 3
RETRY_DELAY = 5
THROTTLE_SECS = 5.0
LIMIT_RATE = "500k"
def _redact_cmd(cmd: list[str]) -> str:
out: list[str] = []
for tok in cmd:
if tok.startswith("userhash="):
out.append("userhash=<redacted>")
else:
out.append(tok)
return " ".join(out)
def load_userhash(vault: Path) -> str:
@@ -54,6 +73,15 @@ def upload(path: Path, userhash: str) -> str:
cmd = [
"curl.exe",
"-sS",
"--max-time",
str(MAX_TIME),
"--retry",
str(RETRIES),
"--retry-delay",
str(RETRY_DELAY),
"--retry-all-errors",
"--limit-rate",
LIMIT_RATE,
"-F",
"reqtype=fileupload",
"-F",
@@ -62,7 +90,13 @@ def upload(path: Path, userhash: str) -> str:
f"fileToUpload=@{path}",
API,
]
raw = subprocess.check_output(cmd, text=True, stderr=subprocess.STDOUT)
try:
raw = subprocess.check_output(cmd, text=True, stderr=subprocess.STDOUT)
except subprocess.CalledProcessError as exc:
raise SystemExit(
f"catbox upload failed for {path.name} "
f"(curl exit {exc.returncode}): {_redact_cmd(cmd)}"
) from None
url = raw.strip()
if not url.startswith("https://"):
raise SystemExit(f"catbox refused {path.name}: {url[:200]}")
@@ -122,9 +156,10 @@ def main(argv: list[str] | None = None) -> int:
userhash = None if args.dry_run else load_userhash(args.vault)
uploaded = 0
for item in items:
if not item.get("publish"):
continue
publishable = [it for it in items if it.get("publish")]
for i, item in enumerate(publishable):
if i > 0 and not args.dry_run:
time.sleep(THROTTLE_SECS)
name = str(item.get("filename") or "")
src = args.thread / name
if not src.is_file():