feat(users/sterni/htmlman): static site generator for manual pages

htmlman is a very simple nix based static site generator which is
intended for rendering HTML representations for man pages plus an index
page listing all available pages. For the sake of simplicity (and unlike
previous iterations of this piece of code) other documentation artifacts
and formats are not supported.

Usually web services like GitHub and depot's web interface are pretty
good at displaying "normal" documentation artifacts like markdown files,
but man pages are usually not rendered — with the additional problem
that it's source is virtually unreadable. htmlman should provide a
simple static site generator which can be plugged into GitHub actions or
the like to automatically generate rendered version of man pages tracked
in version control.

Change-Id: Ib53292964b3ff84c32d70c5fde257a2edb8c2122
Reviewed-on: https://cl.tvl.fyi/c/depot/+/2596
Tested-by: BuildkiteCI
Reviewed-by: tazjin <mail@tazj.in>
Reviewed-by: Profpatsch <mail@profpatsch.de>
Reviewed-by: sterni <sternenseemann@systemli.org>
This commit is contained in:
sterni 2021-03-10 15:37:16 +01:00
parent e41bd6a82d
commit 2cd2b58a04
5 changed files with 291 additions and 0 deletions

View file

@ -4,3 +4,4 @@
inherited: true
owners:
- Profpatsch
- sterni

View file

@ -84,6 +84,7 @@
lutris
makeFontsConf
makeWrapper
mandoc
mdbook
meson
mime-types

View file

@ -0,0 +1,36 @@
# htmlman
static site generator for man pages intended for
rendering man page documentation viewable using
a web browser.
## usage
If you have a nix expression, `doc.nix`, like this:
```nix
{ depot, ... }:
depot.users.sterni.htmlman {
title = "foo project";
pages = [
{
name = "foo";
section = 1;
}
{
name = "foo";
section = 3;
path = ../devman/foo.3;
}
];
manDir = ../man;
}
```
You can run the following to directly deploy the resulting
documentation output to a specific target directory:
```sh
nix-build -A deploy doc.nix && ./result target_directory
```

View file

@ -0,0 +1,204 @@
{ depot, lib, pkgs, ... }:
let
inherit (depot.nix)
getBins
runExecline
;
inherit (depot.tools)
cheddar
;
inherit (pkgs)
mandoc
coreutils
fetchurl
writers
;
bins = getBins cheddar [ "cheddar" ]
// getBins mandoc [ "mandoc" ]
// getBins coreutils [ "cat" "mv" "mkdir" ]
;
normalizeDrv = fetchurl {
url = "https://necolas.github.io/normalize.css/8.0.1/normalize.css";
sha256 = "04jmvybwh2ks4dlnfa70sb3a3z3ig4cv0ya9rizjvm140xq1h22q";
};
execlineStdoutInto = target: line: [
"redirfd" "-w" "1" target
] ++ line;
# I will not write a pure nix markdown renderer
# I will not write a pure nix markdown renderer
# I will not write a pure nix markdown renderer
# I will not write a pure nix markdown renderer
# I will not write a pure nix markdown renderer
markdown = md:
let
html = runExecline.local "rendered-markdown" {
stdin = md;
} ([
"importas" "-iu" "out" "out"
] ++ execlineStdoutInto "$out" [
bins.cheddar "--about-filter" "description.md"
]);
in builtins.readFile html;
indexTemplate = { title, description, pages ? [] }: ''
<!doctype html>
<html>
<head>
<meta charset="utf-8">
<title>${title}</title>
<link rel="stylesheet" type="text/css" href="style.css"/>
</head>
<body>
<div class="index-text">
<h1>${title}</h1>
${markdown description}
<h2>man pages</h2>
<ul>
${lib.concatMapStrings ({ name, section, ... }: ''
<li><a href="${name}.${toString section}.html">${name}(${toString section})</a></li>
'') pages}
</ul>
</div>
</body>
</html>
'';
defaultStyle = import ./defaultStyle.nix { };
# This deploy script automatically copies the build result into
# a TARGET directory and marks it as writeable optionally.
# It is exposed as the deploy attribute of the result of
# htmlman, so an htmlman expression can be used like this:
# nix-build -A deploy htmlman.nix && ./result target_dir
deployScript = title: drv: writers.writeDash "deploy-${title}" ''
usage() {
printf 'Usage: %s [-w] TARGET\n\n' "$0"
printf 'Deploy htmlman documentation to TARGET directory.\n\n'
printf ' -h Display this help message\n'
printf ' -w Make TARGET directory writeable\n'
}
if test "$#" -lt 1; then
usage
exit 100
fi
writeable=false
while test "$#" -gt 0; do
case "$1" in
-h)
usage
exit 0
;;
-w)
writeable=true
;;
-*)
usage
exit 100
;;
*)
if test -z "$target"; then
target="$1"
else
echo "Too many arguments"
exit 100
fi
;;
esac
shift
done
if test -z "$target"; then
echo "Missing TARGET"
usage
exit 100
fi
set -ex
mkdir -p "$target"
cp -RTL --reflink=auto "${drv}" "$target"
if $writeable; then
chmod -R +w "$target"
fi
'';
htmlman =
{ title
# title of the index page
, description ? ""
# description which is displayed after
# the main heading on the index page
, pages ? []
# man pages of the following structure:
# {
# name : string;
# section : int;
# path : either path string;
# }
# path is optional, if it is not given,
# the man page source must be located at
# "${manDir}/${name}.${toString section}"
, manDir ? null
# directory in which man page sources are located
, style ? defaultStyle
# CSS to use as a string
, normalizeCss ? true
# whether to include normalize.css before the custom CSS
}:
let
index = indexTemplate {
inherit title description pages;
};
resolvePath = { path ? null, name, section }:
if path != null
then path
else "${manDir}/${name}.${toString section}";
html =
runExecline.local "htmlman-${title}" {
derivationArgs = {
inherit index style;
passAsFile = [ "index" "style" ];
};
} ([
"multisubstitute" [
"importas" "-iu" "out" "out"
"importas" "-iu" "index" "indexPath"
"importas" "-iu" "style" "stylePath"
]
"if" [ bins.mkdir "-p" "$out" ]
"if" [ bins.mv "$index" "\${out}/index.html" ]
"if" (execlineStdoutInto "\${out}/style.css" [
"if" ([
bins.cat
] ++ lib.optional normalizeCss normalizeDrv
++ [
"$style"
])
])
] ++ lib.concatMap ({ name, section, ... }@p:
execlineStdoutInto "\${out}/${name}.${toString section}.html" [
"if" [
bins.mandoc
"-T" "html" "-mdoc"
"-O" "style=style.css"
(resolvePath p)
]
]) pages);
in html // {
deploy = deployScript title html;
};
in
htmlman

View file

@ -0,0 +1,49 @@
{ ... }:
''
body {
font-size: 1em;
line-height: 1.5;
font-family: serif;
background-color: #efefef;
}
h1, h2, h3, h4, h5, h6 {
font-family: sans-serif;
font-size: 1em;
margin: 5px 0;
}
h1 {
margin-top: 0;
}
a:link, a:visited {
color: #3e7eff;
}
h1 a, h2 a, h3 a, h4 a, h5 a, h6 a {
text-decoration: none;
}
.manual-text, .index-text {
padding: 20px;
max-width: 800px;
background-color: white;
margin: 0 auto;
}
table.head, table.foot {
display: none;
}
.Nd {
display: inline;
}
/* use same as cheddar for man pages */
pre {
padding: 16px;
background-color: #f6f8fa;
}
''