Merge pull request #243062 from mobusoperandi/doc_watchexec

nixpkgs/NixOS manuals: devmode feature
This commit is contained in:
Silvan Mosberger 2023-07-27 16:11:18 +02:00 committed by GitHub
commit a7e5800273
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
9 changed files with 189 additions and 10 deletions

4
doc/common.nix Normal file
View file

@ -0,0 +1,4 @@
{
outputPath = "share/doc/nixpkgs";
indexPath = "manual.html";
}

View file

@ -23,6 +23,14 @@ $ nix-shell
If the build succeeds, the manual will be in `./result/share/doc/nixpkgs/manual.html`.
## devmode {#sec-contributing-devmode}
The shell in the manual source directory makes available a command, `devmode`.
It is a daemon, that:
1. watches the manual's source for changes and when they occur — rebuilds
2. HTTP serves the manual, injecting a script that triggers reload on changes
3. opens the manual in the default browser
## Syntax {#sec-contributing-markup}
As per [RFC 0072](https://github.com/NixOS/rfcs/pull/72), all new documentation content should be written in [CommonMark](https://commonmark.org/) Markdown dialect.

View file

@ -3,6 +3,8 @@ let
inherit (pkgs) lib;
inherit (lib) hasPrefix removePrefix;
common = import ./common.nix;
lib-docs = import ./doc-support/lib-function-docs.nix {
inherit pkgs nixpkgs;
libsets = [
@ -132,15 +134,15 @@ in pkgs.stdenv.mkDerivation {
'';
installPhase = ''
dest="$out/share/doc/nixpkgs"
dest="$out/${common.outputPath}"
mkdir -p "$(dirname "$dest")"
mv out "$dest"
mv "$dest/index.html" "$dest/manual.html"
mv "$dest/index.html" "$dest/${common.indexPath}"
cp ${epub} "$dest/nixpkgs-manual.epub"
mkdir -p $out/nix-support/
echo "doc manual $dest manual.html" >> $out/nix-support/hydra-build-products
echo "doc manual $dest ${common.indexPath}" >> $out/nix-support/hydra-build-products
echo "doc manual $dest nixpkgs-manual.epub" >> $out/nix-support/hydra-build-products
'';
}

20
doc/shell.nix Normal file
View file

@ -0,0 +1,20 @@
let
pkgs = import ../. {
config = {};
overlays = [];
};
common = import ./common.nix;
inherit (common) outputPath indexPath;
web-devmode = import ../pkgs/tools/nix/web-devmode.nix {
inherit pkgs;
buildArgs = "./.";
open = "/${outputPath}/${indexPath}";
};
in
pkgs.mkShell {
packages = [
web-devmode
];
}

View file

@ -0,0 +1,4 @@
{
outputPath = "share/doc/nixos";
indexPath = "index.html";
}

View file

@ -11,6 +11,8 @@ $ nix-build nixos/release.nix -A manual.x86_64-linux
If the build succeeds, the manual will be in `./result/share/doc/nixos/index.html`.
There's also [a convenient development daemon](https://nixos.org/manual/nixpkgs/unstable/#sec-contributing-devmode).
**Contributing to the man pages**
The man pages are written in [DocBook] which is XML.

View file

@ -16,6 +16,8 @@ let
lib = pkgs.lib;
common = import ./common.nix;
manpageUrls = pkgs.path + "/doc/manpage-urls.json";
# We need to strip references to /nix/store/* from options,
@ -78,11 +80,11 @@ let
substituteInPlace ./nixos-options.md \
--replace \
'@NIXOS_OPTIONS_JSON@' \
${optionsDoc.optionsJSON}/share/doc/nixos/options.json
${optionsDoc.optionsJSON}/${common.outputPath}/options.json
substituteInPlace ./development/writing-nixos-tests.section.md \
--replace \
'@NIXOS_TEST_OPTIONS_JSON@' \
${testOptionsDoc.optionsJSON}/share/doc/nixos/options.json
${testOptionsDoc.optionsJSON}/${common.outputPath}/options.json
sed -e '/@PYTHON_MACHINE_METHODS@/ {' -e 'r ${testDriverMachineDocstrings}/machine-methods.md' -e 'd' -e '}' \
-i ./development/writing-nixos-tests.section.md
'';
@ -99,7 +101,7 @@ in rec {
}
''
# Generate the HTML manual.
dst=$out/share/doc/nixos
dst=$out/${common.outputPath}
mkdir -p $dst
cp ${../../../doc/style.css} $dst/style.css
@ -120,7 +122,7 @@ in rec {
--toc-depth 1 \
--chunk-toc-depth 1 \
./manual.md \
$dst/index.html
$dst/${common.indexPath}
mkdir -p $out/nix-support
echo "nix-build out $out" >> $out/nix-support/hydra-build-products
@ -131,7 +133,7 @@ in rec {
manual = manualHTML;
# Index page of the NixOS manual.
manualHTMLIndex = "${manualHTML}/share/doc/nixos/index.html";
manualHTMLIndex = "${manualHTML}/${common.outputPath}/${common.indexPath}";
manualEpub = runCommand "nixos-manual-epub"
{ nativeBuildInputs = [ buildPackages.libxml2.bin buildPackages.libxslt.bin buildPackages.zip ];
@ -162,7 +164,7 @@ in rec {
}
''
# Generate the epub manual.
dst=$out/share/doc/nixos
dst=$out/${common.outputPath}
xsltproc \
--param chapter.autolabel 0 \
@ -197,7 +199,7 @@ in rec {
mkdir -p $out/share/man/man5
nixos-render-docs -j $NIX_BUILD_CORES options manpage \
--revision ${lib.escapeShellArg revision} \
${optionsJSON}/share/doc/nixos/options.json \
${optionsJSON}/${common.outputPath}/options.json \
$out/share/man/man5/configuration.nix.5
'';

View file

@ -0,0 +1,20 @@
let
pkgs = import ../../.. {
config = {};
overlays = [];
};
common = import ./common.nix;
inherit (common) outputPath indexPath;
web-devmode = import ../../../pkgs/tools/nix/web-devmode.nix {
inherit pkgs;
buildArgs = "../../release.nix -A manualHTML.${builtins.currentSystem}";
open = "/${outputPath}/${indexPath}";
};
in
pkgs.mkShell {
packages = [
web-devmode
];
}

View file

@ -0,0 +1,117 @@
{
pkgs,
# arguments to `nix-build`, e.g. `"foo.nix -A bar"`
buildArgs,
# what path to open a browser at
open,
}: let
inherit (pkgs) lib;
error_page = pkgs.writeShellScriptBin "error_page" ''
echo "<!DOCTYPE html>
<html>
<head>
<style>
@media (prefers-color-scheme: dark) {
:root { filter: invert(100%); }
}
</style>
</head>
<body><pre>$1</pre></body>
</html>"
'';
# The following would have been simpler:
# 1. serve from `$serve`
# 2. pass each build a `--out-link $serve/result`
# But that way live-server does not seem to detect changes and therefore no
# auto-reloads occur.
# Instead, we copy the contents of each build to the `$serve` directory.
# Using rsync here, instead of `cp`, to get as close to an atomic
# directory copy operation as possible. `--delay-updates` should
# also go towards that.
build_and_copy = pkgs.writeShellScriptBin "build_and_copy" ''
set -euxo pipefail
set +e
stderr=$(2>&1 nix-build --out-link $out_link ${buildArgs})
exit_status=$?
set -e
if [ $exit_status -eq 0 ];
then
# setting permissions to be able to clean up
${lib.getBin pkgs.rsync}/bin/rsync \
--recursive \
--chmod=u=rwX \
--delete-before \
--delay-updates \
$out_link/ \
$serve/
else
set +x
${lib.getBin error_page}/bin/error_page "$stderr" > $error_page_absolute
set -x
${lib.getBin pkgs.findutils}/bin/find $serve \
-type f \
! -name $error_page_relative \
-delete
fi
'';
# https://watchexec.github.io/
watcher = pkgs.writeShellScriptBin "watcher" ''
set -euxo pipefail
${lib.getBin pkgs.watchexec}/bin/watchexec \
--shell=none \
--restart \
--print-events \
${lib.getBin build_and_copy}/bin/build_and_copy
'';
# A Rust alternative to live-server exists, but it was not in nixpkgs.
# `--no-css-inject`: without this it seems that only CSS is auto-reloaded.
# https://www.npmjs.com/package/live-server
server = pkgs.writeShellScriptBin "server" ''
set -euxo pipefail
${lib.getBin pkgs.nodePackages_latest.live-server}/bin/live-server \
--host=127.0.0.1 \
--verbose \
--no-css-inject \
--entry-file=$error_page_relative \
--open=${open} \
$serve
'';
devmode =
pkgs.writeShellScriptBin "devmode"
''
set -euxo pipefail
function handle_exit {
rm -rf "$tmpdir"
}
tmpdir=$(mktemp -d)
trap handle_exit EXIT
export out_link="$tmpdir/result"
export serve="$tmpdir/serve"
mkdir $serve
export error_page_relative=error.html
export error_page_absolute=$serve/$error_page_relative
${lib.getBin error_page}/bin/error_page "building " > $error_page_absolute
${lib.getBin pkgs.parallel}/bin/parallel \
--will-cite \
--line-buffer \
--tagstr '{/}' \
::: \
"${lib.getBin watcher}/bin/watcher" \
"${lib.getBin server}/bin/server"
'';
in
devmode