summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorpablu <me@pablu.de>2026-09-19 14:57:17 +0200
committerpablu <me@pablu.de>2026-09-19 15:02:55 +0200
commita0fc8e577e7414312ef66307497b36c393a1a582 (patch)
treecb41b4cd7b209ba48f7d8f2f757b6debbd09bcc1
parent1ff95d6dd3cbb9e75b0eb71a7341c278fda9f645 (diff)
feat: markdown about renderHEADmain
-rw-r--r--Containerfile6
-rw-r--r--about-filter.py72
2 files changed, 76 insertions, 2 deletions
diff --git a/Containerfile b/Containerfile
index e15c99c..b517a28 100644
--- a/Containerfile
+++ b/Containerfile
@@ -1,6 +1,6 @@
ARG DEBIAN_VERSION=trixie
ARG CGIT_VERSION=1.3.1
-ARG IMAGE_REVISION=1
+ARG IMAGE_REVISION=2
FROM debian:${DEBIAN_VERSION}-slim AS builder
@@ -46,7 +46,7 @@ RUN make install \
FROM debian:${DEBIAN_VERSION}-slim
ARG CGIT_VERSION=1.3.1
-ARG IMAGE_REVISION=1
+ARG IMAGE_REVISION=2
LABEL org.opencontainers.image.title="cgit" \
org.opencontainers.image.version="${CGIT_VERSION}-r${IMAGE_REVISION}"
@@ -54,6 +54,7 @@ LABEL org.opencontainers.image.title="cgit" \
RUN apt-get update \
&& apt-get install --no-install-recommends -y \
busybox-static \
+ cmark-gfm \
python3-pygments \
tini \
zlib1g \
@@ -61,6 +62,7 @@ RUN apt-get update \
COPY --from=builder /out/www /www
+COPY --chmod=0555 about-filter.py /usr/libexec/cgit/about-filter.py
COPY --chmod=0555 syntax-highlighting.py /usr/libexec/cgit/syntax-highlighting.py
RUN mkdir -p \
diff --git a/about-filter.py b/about-filter.py
new file mode 100644
index 0000000..5e97bbe
--- /dev/null
+++ b/about-filter.py
@@ -0,0 +1,72 @@
+#!/usr/bin/env python3
+
+"""Render cgit repository READMEs as safe HTML."""
+
+import html
+import os
+import subprocess
+import sys
+
+
+MARKDOWN_EXTENSIONS = (".md", ".markdown")
+CMARK_COMMAND = (
+ "/usr/bin/cmark-gfm",
+ "--validate-utf8",
+ "--extension",
+ "table",
+ "--extension",
+ "autolink",
+ "--extension",
+ "strikethrough",
+ "--extension",
+ "tasklist",
+)
+
+STYLE = """<style>
+.markdown-body { line-height: 1.6; overflow-wrap: anywhere; }
+.markdown-body img { max-width: 100%; }
+.markdown-body pre { overflow-x: auto; padding: 1em; background: rgb(255 255 255 / 6%); }
+.markdown-body code { padding: .15em .3em; background: rgb(255 255 255 / 8%); }
+.markdown-body pre code { padding: 0; background: none; }
+.markdown-body blockquote { margin-left: 0; padding-left: 1em; color: #aaa; border-left: .25em solid #555; }
+.markdown-body table { display: block; max-width: 100%; overflow-x: auto; border-collapse: collapse; }
+.markdown-body th, .markdown-body td { padding: .4em .8em; border: 1px solid #555; }
+.markdown-body input[type="checkbox"] { margin-right: .4em; }
+</style>
+"""
+
+
+def render_markdown(source: bytes) -> bytes:
+ """Render Markdown with cmark-gfm's safe defaults."""
+ result = subprocess.run(
+ CMARK_COMMAND,
+ input=source,
+ stdout=subprocess.PIPE,
+ check=True,
+ )
+ return result.stdout
+
+
+def render_plain_text(source: bytes) -> bytes:
+ """Return arbitrary input as escaped, readable text."""
+ text = source.decode("utf-8", errors="replace")
+ return f"<pre>{html.escape(text)}</pre>\n".encode()
+
+
+def main() -> None:
+ filename = sys.argv[1] if len(sys.argv) > 1 else ""
+ source = sys.stdin.buffer.read()
+
+ if os.path.splitext(filename)[1].lower() in MARKDOWN_EXTENSIONS:
+ rendered = render_markdown(source)
+ else:
+ rendered = render_plain_text(source)
+
+ sys.stdout.buffer.write(STYLE.encode())
+ sys.stdout.buffer.write(b'<div class="markdown-body">\n')
+ sys.stdout.buffer.write(rendered)
+ sys.stdout.buffer.write(b"</div>\n")
+
+
+if __name__ == "__main__":
+ main()