stagit

Static Git page generator
git clone https://git.pngu.org/stagit
Log | Files | Refs | README | LICENSE

commit 5161c1dbfbfb3df8d5c4c9cc3e63083f7c64ed5b
parent d27987f0a36fd3809a579907dffe019b2d7d9852
Author: Jack Sangdahl <jack@pngu.org>
Date:   Fri, 10 Jul 2026 17:24:11 -0600

Rework syntax highlighting to not use Python

Diffstat:
Mhighlight | 63++++++++++++++++++---------------------------------------------
Mreadme.md | 166++++++++++++++++++++++++++++++++++++-------------------------------------------
Drequirements.txt | 3---
Mstyle.css | 224++++++++++++++++++++++++++++++++++++++++++++++++-------------------------------
4 files changed, 231 insertions(+), 225 deletions(-)

diff --git a/highlight b/highlight @@ -1,50 +1,23 @@ -#!/usr/bin/env python3 +#!/usr/bin/env bash -from sys import stdin +read -r FILENAME -import pygments -from markdown import markdown -from pygments import highlight -from pygments.formatters import HtmlFormatter -from pygments.lexers import TextLexer, guess_lexer, guess_lexer_for_filename +EXT="${FILENAME##*.}" +EXT=$(echo "$EXT" | tr '[:upper:]' '[:lower:]') -filename = stdin.readline().strip() -contents = stdin.read() -lexer = None -rendered = None +if [ "$EXT" = "md" ] || [ "$EXT" = "markdown" ]; then + echo '<article class="markup markdown">' + pandoc --from gfm --to html + echo '</article>' + exit 0 +fi -try: - lexer = guess_lexer_for_filename(filename, contents) -except pygments.util.ClassNotFound: - try: - lexer = guess_lexer(contents) - except pygments.util.ClassNotFound: - pass +if [ "$EXT" = "$FILENAME" ]; then + SYNTAX_ARG="$FILENAME" +else + SYNTAX_ARG="$EXT" +fi -if lexer is None: - lexer = TextLexer() - -if lexer.__class__ is pygments.lexers.MarkdownLexer: - rendered = markdown( - contents, - extensions=["codehilite", "extra", "sane_lists", "smarty", "pymdownx.tasklist"], - ) - -format = HtmlFormatter( - style="murphy", - cssclass="highlight", - linenos="table", - lineanchors="loc", - anchorlinenos=True, -) - -if rendered: - print('<article class="markup markdown">') - print(rendered) - print("</article>") -print('<div id="blob">') -print(highlight(contents, lexer, format)) -print("</div>") -print("<style>") -print(format.get_style_defs("")) -print("</style>") +echo '<pre id="blob">' +highlight --syntax="$SYNTAX_ARG" --fragment --style=github --quiet +echo '</pre>' diff --git a/readme.md b/readme.md @@ -1,54 +1,49 @@ -stagit ------- +## stagit static git page generator. It generates static HTML pages for a git repository. -(personal fork) +(personal fork with markdown rendering and syntax highlighting) -Usage ------ +syntax highlighting is provided by [highlight](https://gitlab.com/saalen/highlight), and markdown rendering is provided by pandoc. + +see the `highlight` shell script for more, and the `stagit-generate.sh` script for generation as a whole. + +## Usage Make files per repository: - $ mkdir -p htmlroot/htmlrepo1 && cd htmlroot/htmlrepo1 - $ stagit path/to/gitrepo1 - repeat for other repositories - $ ... + $ mkdir -p htmlroot/htmlrepo1 && cd htmlroot/htmlrepo1 + $ stagit path/to/gitrepo1 + repeat for other repositories + $ ... Make index file for repositories: - $ cd htmlroot - $ stagit-index path/to/gitrepo1 \ - path/to/gitrepo2 \ - path/to/gitrepo3 > index.html - + $ cd htmlroot + $ stagit-index path/to/gitrepo1 \ + path/to/gitrepo2 \ + path/to/gitrepo3 > index.html -Build and install ------------------ +## Build and install $ make -# make install +# make install -Dependencies ------------- +## Dependencies - C compiler (C99). - libc (tested with OpenBSD, FreeBSD, NetBSD, Linux: glibc and musl). - libgit2 (v0.22+). - POSIX make (optional). - -Documentation -------------- +## Documentation See man pages: stagit(1) and stagit-index(1). - -Building a static binary ------------------------- +## Building a static binary It may be useful to build static binaries, for example to run in a chroot. @@ -57,46 +52,42 @@ It can be done like this at the time of writing (v0.24): cd libgit2-src # change the options in the CMake file: CMakeLists.txt + BUILD_SHARED_LIBS to OFF (static) -CURL to OFF (not needed) -USE_SSH OFF (not needed) -THREADSAFE OFF (not needed) -USE_OPENSSL OFF (not needed, use builtin) +CURL to OFF (not needed) +USE_SSH OFF (not needed) +THREADSAFE OFF (not needed) +USE_OPENSSL OFF (not needed, use builtin) mkdir -p build && cd build cmake ../ make make install - -Extract owner field from git config ------------------------------------ +## Extract owner field from git config A way to extract the gitweb owner for example in the format: - [gitweb] - owner = Name here + [gitweb] + owner = Name here Script: - #!/bin/sh - awk '/^[ ]*owner[ ]=/ { - sub(/^[^=]*=[ ]*/, ""); - print $0; - }' + #!/bin/sh + awk '/^[ ]*owner[ ]=/ { + sub(/^[^=]*=[ ]*/, ""); + print $0; + }' +## Set clone URL for a directory of repos -Set clone URL for a directory of repos --------------------------------------- - #!/bin/sh - cd "$dir" - for i in *; do - test -d "$i" && echo "git://git.codemadness.org/$i" > "$i/url" - done + #!/bin/sh + cd "$dir" + for i in *; do + test -d "$i" && echo "git://git.codemadness.org/$i" > "$i/url" + done - -Update files on git push ------------------------- +## Update files on git push Using a post-receive hook the static files can be automatically updated. Keep in mind git push -f can change the history and the commits may need @@ -106,44 +97,41 @@ history. See stagit(1). git post-receive hook (repo/.git/hooks/post-receive): - #!/bin/sh - # detect git push -f - force=0 - while read -r old new ref; do - hasrevs=$(git rev-list "$old" "^$new" | sed 1q) - if test -n "$hasrevs"; then - force=1 - break - fi - done - - # remove commits and .cache on git push -f - #if test "$force" = "1"; then - # ... - #fi - - # see example_create.sh for normal creation of the files. - - -Create .tar.gz archives by tag ------------------------------- - #!/bin/sh - name="stagit" - mkdir -p archives - git tag -l | while read -r t; do - f="archives/${name}-$(echo "${t}" | tr '/' '_').tar.gz" - test -f "${f}" && continue - git archive \ - --format tar.gz \ - --prefix "${t}/" \ - -o "${f}" \ - -- \ - "${t}" - done - - -Features --------- + #!/bin/sh + # detect git push -f + force=0 + while read -r old new ref; do + hasrevs=$(git rev-list "$old" "^$new" | sed 1q) + if test -n "$hasrevs"; then + force=1 + break + fi + done + + # remove commits and .cache on git push -f + #if test "$force" = "1"; then + # ... + #fi + + # see example_create.sh for normal creation of the files. + +## Create .tar.gz archives by tag + + #!/bin/sh + name="stagit" + mkdir -p archives + git tag -l | while read -r t; do + f="archives/${name}-$(echo "${t}" | tr '/' '_').tar.gz" + test -f "${f}" && continue + git archive \ + --format tar.gz \ + --prefix "${t}/" \ + -o "${f}" \ + -- \ + "${t}" + done + +## Features - Log of all commits from HEAD. - Log and diffstat per commit. @@ -159,9 +147,7 @@ Features a HTTP file server is required. - Usable with text-browsers such as dillo, links, lynx and w3m. - -Cons ----- +## Cons - Not suitable for large repositories (2000+ commits), because diffstats are an expensive operation, the cache (-c flag) is a workaround for this in diff --git a/requirements.txt b/requirements.txt @@ -1,3 +0,0 @@ -markdown -pygments -pymdown-extensions diff --git a/style.css b/style.css @@ -1,144 +1,153 @@ html { - font-size: 12px; - height: 100%; + font-size: 12px; + height: 100%; } body { - margin: 5rem auto; - color: #222; - background-color: #fff; - font-family: monospace; - width: 66rem; + margin: 5rem auto; + color: #222; + background-color: #fff; + font-family: monospace; + width: 66rem; } -pre { -moz-tab-size: 4; } -pre { tab-size: 4; } +pre { + -moz-tab-size: 4; +} +pre { + tab-size: 4; +} -h1, h2, h3, h4, h5, h6 { - font-size: 1em; - margin: 0; +h1, +h2, +h3, +h4, +h5, +h6 { + font-size: 1em; + margin: 0; } -img, h1, h2 { - vertical-align: middle; +img, +h1, +h2 { + vertical-align: middle; } img { - border: 0; + border: 0; } a:target { - background-color: #ccc; + background-color: #ccc; } a.d, a.h, a.i, a.line { - text-decoration: none; + text-decoration: none; } #blob { - display: block; - max-width: 100%; - overflow-x: scroll; + display: block; + max-width: 100%; + overflow-x: scroll; } +/* Keeps Python Markdown files looking excellent */ article.markup { - font-size: 15px; - border: 2px solid #00000017; - border-radius: 10px; - font-family: sans-serif; - padding: 2.5em; - margin: 2em 0; + font-size: 15px; + border: 2px solid #00000017; + border-radius: 10px; + font-family: sans-serif; + padding: 2.5em; + margin: 2em 0; } article.markup code { - font-size: 0.9em; - border: 1px solid #dbdbdb; - background-color: #f7f7f7; - padding: 0 0.3em; - border-radius: 0.3em + font-size: 0.9em; + border: 1px solid #dbdbdb; + background-color: #f7f7f7; + padding: 0 0.3em; + border-radius: 0.3em; } article.markup pre code { - border: none; - background: none; - padding: 0; - border-radius: 0; + border: none; + background: none; + padding: 0; + border-radius: 0; } article.markup pre { - background-color: #f7f7f7; - padding: 1em; - border: 1px solid #dbdbdb; - border-radius: 0.3em; + background-color: #f7f7f7; + padding: 1em; + border: 1px solid #dbdbdb; + border-radius: 0.3em; } article.markup h1 { - font-size: 2.4em; - padding-bottom: 6px; - border-bottom: 5px solid #0000000a; + font-size: 2.4em; + padding-bottom: 6px; + border-bottom: 5px solid #0000000a; } article.markup h2 { - font-size: 1.9em; - padding-bottom: 5px; - border-bottom: 2px solid #00000014; + font-size: 1.9em; + padding-bottom: 5px; + border-bottom: 2px solid #00000014; } article.markup h3 { - font-size: 1.5em; + font-size: 1.5em; } - article.markup h4 { - font-size: 1.3em; + font-size: 1.3em; } - article.markup h5 { - font-size: 1.1em; + font-size: 1.1em; } - article.markup h6 { - font-size: 1em; + font-size: 1em; } article img { - max-width: 100%; + max-width: 100%; } .linenos { - margin-right: 0; - border-right: 1px solid rgb(0 0 0 / 8%); - user-select: none; + margin-right: 0; + border-right: 1px solid rgb(0 0 0 / 8%); + user-select: none; } .linenos a { - margin-right: 0.9em; - user-select: none; - text-decoration: none; + margin-right: 0.9em; + user-select: none; + text-decoration: none; } #blob a { - color: #777; + color: #777; } #blob a:hover { - color: blue; - text-decoration: none; + color: blue; + text-decoration: none; } table thead td { - font-weight: bold; + font-weight: bold; } table td { - padding: 0 0.4em; + padding: 0 0.4em; } #content table td { - vertical-align: top; - white-space: nowrap; + vertical-align: top; + white-space: nowrap; } #branches tr:hover td, @@ -146,68 +155,109 @@ table td { #index tr:hover td, #log tr:hover td, #files tr:hover td { - background-color: #eee; + background-color: #eee; } #index tr td:nth-child(2), #tags tr td:nth-child(3), #branches tr td:nth-child(3), #log tr td:nth-child(2) { - white-space: normal; + white-space: normal; } td.num { - text-align: right; + text-align: right; } .desc { - color: #777; + color: #777; } hr { - border: 0; - border-top: 1px solid #777; - height: 1px; + border: 0; + border-top: 1px solid #777; + height: 1px; } pre { - font-family: monospace; + font-family: monospace; } pre a.h { - color: #00a; + color: #00a; } - .A, span.i, pre a.i { - color: #070; + color: #070; } - .D, span.d, pre a.d { - color: #e00; + color: #e00; } pre a.h:hover, pre a.i:hover, pre a.d:hover { - text-decoration: none; + text-decoration: none; } .url td:nth-child(2) { - padding-top: 0.2em; - padding-bottom: 0.9em; + padding-top: 0.2em; + padding-bottom: 0.9em; } .url td:nth-child(2) span { - padding: 1px 5px; - background-color: #eee; - border: 1px solid #ddd; - border-radius: 5px; + padding: 1px 5px; + background-color: #eee; + border: 1px solid #ddd; + border-radius: 5px; } .url td:nth-child(2) span a { - color: #444; + color: #444; +} + +.hl.num { + color: #0086b3; +} +.hl.esc { + color: #183691; +} +.hl.str { + color: #183691; +} +.hl.pps { + color: #183691; +} +.hl.slc { + color: #969896; +} +.hl.com { + color: #969896; +} +.hl.ppc { + color: #a71d5d; +} +.hl.opt { + color: #000000; +} +.hl.ipl { + color: #183691; +} +.hl.lin { + color: #b2b2b2; +} +.hl.kwa { + color: #a71d5d; +} +.hl.kwb { + color: #0086b3; +} +.hl.kwc { + color: #0086b3; +} +.hl.kwd { + color: #0086b3; }