diff --git a/doc/common.nix b/doc/common.nix new file mode 100644 index 000000000000..56f723eb6bd7 --- /dev/null +++ b/doc/common.nix @@ -0,0 +1,4 @@ +{ + outputPath = "share/doc/nixpkgs"; + indexPath = "manual.html"; +} diff --git a/doc/contributing/contributing-to-documentation.chapter.md b/doc/contributing/contributing-to-documentation.chapter.md index a732eee4b962..0b7b49bf7dda 100644 --- a/doc/contributing/contributing-to-documentation.chapter.md +++ b/doc/contributing/contributing-to-documentation.chapter.md @@ -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. diff --git a/doc/default.nix b/doc/default.nix index 8efa406ec1ea..f4270ae856d5 100644 --- a/doc/default.nix +++ b/doc/default.nix @@ -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 ''; } diff --git a/doc/shell.nix b/doc/shell.nix new file mode 100644 index 000000000000..d71e3f3a709a --- /dev/null +++ b/doc/shell.nix @@ -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 + ]; + } diff --git a/nixos/doc/manual/common.nix b/nixos/doc/manual/common.nix new file mode 100644 index 000000000000..48d1d909492d --- /dev/null +++ b/nixos/doc/manual/common.nix @@ -0,0 +1,4 @@ +{ + outputPath = "share/doc/nixos"; + indexPath = "index.html"; +} diff --git a/nixos/doc/manual/contributing-to-this-manual.chapter.md b/nixos/doc/manual/contributing-to-this-manual.chapter.md index c306cc084cdb..4633c7e1b058 100644 --- a/nixos/doc/manual/contributing-to-this-manual.chapter.md +++ b/nixos/doc/manual/contributing-to-this-manual.chapter.md @@ -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. diff --git a/nixos/doc/manual/default.nix b/nixos/doc/manual/default.nix index f2fd6a682934..902dee701801 100644 --- a/nixos/doc/manual/default.nix +++ b/nixos/doc/manual/default.nix @@ -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 ''; diff --git a/nixos/doc/manual/shell.nix b/nixos/doc/manual/shell.nix new file mode 100644 index 000000000000..70500a12b037 --- /dev/null +++ b/nixos/doc/manual/shell.nix @@ -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 + ]; + } diff --git a/pkgs/tools/nix/web-devmode.nix b/pkgs/tools/nix/web-devmode.nix new file mode 100644 index 000000000000..6ebdc31118f0 --- /dev/null +++ b/pkgs/tools/nix/web-devmode.nix @@ -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 " + + + + +
$1
+ " + ''; + + # 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