init flake parts docs modules

This commit is contained in:
DavHau 2023-10-10 16:18:09 +02:00 committed by mergify[bot]
parent 5e938ebb20
commit 87d65e704e
9 changed files with 596 additions and 0 deletions

View File

@ -0,0 +1,399 @@
{
config,
inputs,
lib,
flake-parts-lib,
...
}: let
inherit
(lib)
mkOption
types
concatMap
concatLists
mapAttrsToList
attrValues
hasPrefix
removePrefix
;
failPkgAttr = name: _v:
throw ''
Most nixpkgs attributes are not supported when generating documentation.
Please check with `--show-trace` to see which option leads to this `pkgs.${lib.strings.escapeNixIdentifier name}` reference. Often it can be cut short with a `defaultText` argument to `lib.mkOption`, or by escaping an option `example` using `lib.literalExpression`.
'';
in {
options.perSystem = flake-parts-lib.mkPerSystemOption ({
config,
pkgs,
lib,
...
}: let
cfg = config.render;
pkgsStub = lib.mapAttrs failPkgAttr pkgs;
fixups = {
lib,
flake-parts-lib,
...
}: {
options.perSystem = flake-parts-lib.mkPerSystemOption {
config = {
_module.args.pkgs =
pkgsStub
// {
_type = "pkgs";
inherit lib;
formats =
lib.mapAttrs
(
formatName: formatFn: formatArgs: let
result = formatFn formatArgs;
stubs =
lib.mapAttrs
(
name: _:
throw "The attribute `(pkgs.formats.${lib.strings.escapeNixIdentifier formatName} x).${lib.strings.escapeNixIdentifier name}` is not supported during documentation generation. Please check with `--show-trace` to see which option leads to this `${lib.strings.escapeNixIdentifier name}` reference. Often it can be cut short with a `defaultText` argument to `lib.mkOption`, or by escaping an option `example` using `lib.literalExpression`."
)
result;
in
stubs
// {
inherit (result) type;
}
)
pkgs.formats;
};
};
};
};
eval = evalWith {
modules = concatLists (mapAttrsToList (name: inputCfg: inputCfg.getModules inputCfg.flake) cfg.inputs);
};
evalWith = {modules}:
inputs.flake-parts.lib.evalFlakeModule
{
inputs = {
inherit (inputs) nixpkgs;
self =
eval.config.flake
// {
outPath =
throw "The `self.outPath` attribute is not available when generating documentation, because the documentation should not depend on the specifics of the flake files where it is loaded. This error is generally caused by a missing `defaultText` on one or more options in the trace. Please run this evaluation with `--show-trace`, and look for `while evaluating the default value of option` and add a `defaultText` to one or more of the options involved.";
};
};
}
{
imports =
modules
++ [
fixups
];
systems = [(throw "The `systems` option value is not available when generating documentation. This is generally caused by a missing `defaultText` on one or more options in the trace. Please run this evaluation with `--show-trace`, look for `while evaluating the default value of option` and add a `defaultText` to the one or more of the options involved.")];
};
opts = eval.options;
coreOptDecls = config.render.inputs.flake-parts._nixosOptionsDoc.optionsNix;
filterTransformOptions = {
sourceName,
sourcePath,
baseUrl,
coreOptDecls,
}: let
sourcePathStr = toString sourcePath;
in
opt: let
declarations =
concatMap
(
decl:
if hasPrefix sourcePathStr (toString decl)
then let
subpath = removePrefix sourcePathStr (toString decl);
in [
{
url = baseUrl + subpath;
name = sourceName + subpath;
}
]
else []
)
opt.declarations;
in
if
declarations
== []
|| (
sourceName != "flake-parts" && coreOptDecls ? ${lib.showOption opt.loc}
)
then opt // {visible = false;}
else opt // {inherit declarations;};
inputModule = {
config,
name,
...
}: {
options = {
flake = mkOption {
type = types.raw;
description = ''
A flake.
'';
default = inputs.${name};
};
sourcePath = mkOption {
type = types.path;
description = ''
Source path in which the modules are contained.
'';
default = config.flake.outPath;
};
title = mkOption {
type = types.str;
description = ''
Title of the markdown page.
'';
default = name;
};
flakeRef = mkOption {
type = types.str;
default =
# This only works for github for now, but we can set a non-default
# value in the list just fine.
let
match = builtins.match "https://github.com/([^/]*)/([^/]*)/blob/([^/]*)" config.baseUrl;
owner = lib.elemAt match 0;
repo = lib.elemAt match 1;
branch = lib.elemAt match 2; # ignored for now because they're all default branches
in
if match != null
then "github:${owner}/${repo}"
else throw "Couldn't figure out flakeref for ${name}: ${config.baseUrl}";
};
preface = mkOption {
type = types.str;
description = ''
Stuff between the title and the options.
'';
default = ''
${config.intro}
${config.installation}
'';
};
intro = mkOption {
type = types.str;
description = ''
Introductory paragraph between title and installation.
'';
};
installationDeclareInput = mkOption {
type = types.bool;
description = ''
Whether to show how to declare the input.
'';
default = true;
};
installation = mkOption {
type = types.str;
description = ''
Installation paragraph between installation and options.
'';
default = ''
## Installation
${
if config.installationDeclareInput
then ''
To use these options, add to your flake inputs:
```nix
${config.sourceName}.url = "${config.flakeRef}";
```
and inside the `mkFlake`:
''
else ''
To use these options, add inside the `mkFlake`:
''
}
```nix
imports = [
inputs.${config.sourceName}.${lib.concatMapStringsSep "." lib.strings.escapeNixIdentifier config.attributePath}
];
```
Run `nix flake lock` and you're set.
'';
};
sourceName = mkOption {
type = types.str;
description = ''
Name by which the source is shown in the list of declarations.
'';
default = name;
};
baseUrl = mkOption {
type = types.str;
description = ''
URL prefix for source location links.
'';
};
getModules = mkOption {
type = types.functionTo (types.listOf types.raw);
description = ''
Get the modules to render.
'';
default = flake: [
(
builtins.addErrorContext "while getting modules for input '${name}'"
(lib.getAttrFromPath config.attributePath flake)
)
];
};
attributePath = mkOption {
type = types.listOf types.str;
description = ''
Flake output attribute path to import.
'';
default = ["flakeModule"];
};
rendered = mkOption {
type = types.package;
description = ''
Built Markdown docs.
'';
readOnly = true;
};
_nixosOptionsDoc = mkOption {};
separateEval = mkOption {
type = types.bool;
default = false;
description = ''
Whether to include this in the main evaluation.
'';
};
filterTransformOptions = mkOption {
default = filterTransformOptions;
description = ''
Function to customize the set of options to render for this input.
'';
};
killLinks = mkOption {
type = types.bool;
default = false;
description = ''
Remove local anchor links, a workaround for proper {option}`` support in the doc tooling.
'';
};
};
config = {
_nixosOptionsDoc = pkgs.nixosOptionsDoc {
options =
if config.separateEval
then
(evalWith {
modules = config.getModules config.flake;
})
.options
else opts;
documentType = "none";
transformOptions = config.filterTransformOptions {
inherit (config) sourceName baseUrl sourcePath;
inherit coreOptDecls;
};
warningsAreErrors = true; # not sure if feasible long term
markdownByDefault = true;
};
rendered =
pkgs.runCommand "option-doc-${config.sourceName}"
{
nativeBuildInputs = [pkgs.libxslt.bin pkgs.pandoc];
inputDoc = config._nixosOptionsDoc.optionsDocBook;
inherit (config) title preface;
} ''
xsltproc --stringparam title "$title" \
--stringparam killLinks '${lib.boolToString config.killLinks}' \
-o options.db.xml ${./options.xsl} \
"$inputDoc"
mkdir $out
pandoc --verbose --from docbook --to html options.db.xml >options.html
substitute options.html $out/options.html --replace '<p>@intro@</p>' "$preface"
grep -v '@intro@' <$out/options.html >/dev/null || {
grep '@intro@' <$out/options.html
echo intro replacement failed; exit 1;
}
'';
};
};
in {
options = {
render = {
inputs = mkOption {
description = "Which modules to render.";
type = types.attrsOf (types.submodule inputModule);
};
};
};
config = {
packages =
lib.mapAttrs' (name: inputCfg: {
name = "generated-docs-${name}";
value = inputCfg.rendered;
})
cfg.inputs
// {
generated-docs =
pkgs.runCommand "generated-docs"
{
passthru = {
inherit config;
inherit eval;
# This won't be in sync with the actual nixosOptionsDoc
# invocations, but it's useful for troubleshooting.
allOptionsPerhaps =
(pkgs.nixosOptionsDoc {
options = opts;
})
.optionsNix;
};
}
''
mkdir $out
${
lib.concatStringsSep "\n"
(lib.mapAttrsToList
(name: inputCfg: ''
cp ${inputCfg.rendered}/options.html $out/${name}.html
'')
cfg.inputs)
}
'';
};
};
});
}

View File

@ -0,0 +1,66 @@
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet
version="1.0"
xmlns="http://docbook.org/ns/docbook"
xmlns:db="http://docbook.org/ns/docbook"
xmlns:xlink="http://www.w3.org/1999/xlink"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
>
<xsl:template match="/">
<xsl:apply-templates/>
</xsl:template>
<xsl:template match="db:link">
<xsl:choose>
<xsl:when test="$killLinks = 'true'">
<xsl:copy-of select="./node()"/>
</xsl:when>
<xsl:otherwise>
<xsl:copy-of select="."/>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
<xsl:template match="db:variablelist">
<chapter>
<title>
<xsl:value-of select="$title"/>
</title>
<para>@intro@</para>
<section><title>Options</title>
<xsl:for-each select="db:varlistentry">
<para><link xlink:href="#{db:term/@xml:id}"><xsl:copy-of select="db:term/db:option"/></link></para>
</xsl:for-each>
</section>
<xsl:apply-templates />
</chapter>
</xsl:template>
<xsl:template match="db:varlistentry">
<section>
<title>
<link xlink:href="#{db:term/@xml:id}" xml:id="{db:term/@xml:id}"><xsl:copy-of select="db:term/db:option"/></link>
</title>
<xsl:apply-templates select="db:listitem/*"/>
</section>
</xsl:template>
<!-- Pandoc doesn't like block-level simplelist -->
<!-- https://github.com/jgm/pandoc/issues/8086 -->
<xsl:template match="db:simplelist">
<para>
<xsl:copy>
<xsl:apply-templates select="@*|node()"/>
</xsl:copy>
</para>
</xsl:template>
<!-- Turn filename tags with href attrs into explicit links -->
<xsl:template match="db:filename">
<link xlink:href="{@xlink:href}">
<xsl:copy>
<xsl:apply-templates select="@*|node()"/>
</xsl:copy>
</link>
</xsl:template>
<xsl:template match="@*|node()">
<xsl:copy>
<xsl:apply-templates select="@*|node()"/>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>

1
modules/flake-parts/site/.gitignore vendored Normal file
View File

@ -0,0 +1 @@
book

View File

@ -0,0 +1 @@
/getting-things.html /cheat-sheet.html

View File

@ -0,0 +1,19 @@
[book]
authors = ["Robert Hensing", "Various module authors"]
language = "en"
multilingual = false
src = "src"
title = "flake-parts"
[output.html]
git-repository-url = "https://github.com/hercules-ci/flake-parts"
edit-url-template = "https://github.com/hercules-ci/flake.parts-website/edit/main/site/{path}"
additional-js = [ "no-edit-options.js" ]
additional-css = [ "flake-parts.css" ]
no-section-label = true
[output.html.print]
enable = false # big single page; don't need that
[output.linkcheck]
follow-web-links = false # no Internet during the build

View File

@ -0,0 +1,81 @@
{inputs, ...}: {
perSystem = {
config,
self',
inputs',
pkgs,
lib,
...
}: {
/*
Check the links, including anchors (not currently supported by mdbook)
Putting this in a separate check has the benefits that
- output can always be inspect with browser
- this slow check (1 minute) is not part of the iteration cycle
Ideally https://github.com/linkchecker/linkchecker/pull/661 is merged
upstream, so that it's quick and can run often without blocking the
iteration cycle unnecessarily.
*/
checks.linkcheck =
pkgs.runCommand "linkcheck"
{
nativeBuildInputs = [pkgs.linkchecker pkgs.python3];
site = config.packages.default;
} ''
# https://linkchecker.github.io/linkchecker/man/linkcheckerrc.html
cat >>$TMPDIR/linkcheckrc <<EOF
[checking]
threads=''${NIX_BUILD_CORES:-4}
[AnchorCheck]
EOF
echo Checking $site
linkchecker -f $TMPDIR/linkcheckrc $site/
touch $out
'';
packages = {
default = pkgs.stdenvNoCC.mkDerivation {
name = "site";
nativeBuildInputs = [pkgs.mdbook pkgs.mdbook-linkcheck];
src = ./.;
buildPhase = ''
runHook preBuild
{
while read ln; do
case "$ln" in
*end_of_intro*)
break
;;
*)
echo "$ln"
;;
esac
done
cat src/intro-continued.md
} <${inputs.flake-parts + "/README.md"} >src/README.md
mkdir -p src/options
for f in ${config.packages.generated-docs}/*.html; do
cp "$f" "src/options/$(basename "$f" .html).md"
done
mdbook build --dest-dir $TMPDIR/out
cp -r $TMPDIR/out/html $out
cp _redirects $out
echo '<html><head><script>window.location.pathname = window.location.pathname.replace(/options.html$/, "") + "options/flake-parts.html"</script></head><body><a href="options/flake-parts.html">to the options</a></body></html>' \
>$out/options.html
runHook postBuild
'';
dontInstall = true;
};
};
};
}

View File

@ -0,0 +1,19 @@
h1.menu-title::before {
content: "";
display: inline-block;
background-image: url(./favicon.svg);
background-repeat: no-repeat;
background-size: contain;
width: 1.8ex;
height: 1.8ex;
margin-right: 0.9ex;
vertical-align: middle;
}
.light {
--links: #058;
--sidebar-active: #0af;
}
.sidebar .sidebar-scrollbox {
/* We don't use numbers, so we can take a consistent amount of space */
padding: var(--page-padding) !important;
}

View File

@ -0,0 +1,7 @@
if (window.location.pathname.match(/options/)) {
var buttons = document.querySelector("#menu-bar > div.right-buttons")
if (buttons != null) {
buttons.style.display = "none"
}
}

View File

@ -0,0 +1,3 @@
# Summary
- [Reference Documentation]()