lol

Merge pull request #212684 from pennae/nixos-render-docs

nixos-render-docs: init, use for some manual rendering to docbook

authored by

pennae and committed by
GitHub
5b6dcece 8de8ee54

+1834 -7611
+5 -9
doc/contributing/contributing-to-documentation.chapter.md
··· 27 27 28 28 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. 29 29 30 - Additional syntax extensions are available, though not all extensions can be used in NixOS option documentation. The following extensions are currently used: 30 + Additional syntax extensions are available, all of which can be used in NixOS option documentation. The following extensions are currently used: 31 31 32 32 - []{#ssec-contributing-markup-anchors} 33 33 Explicitly defined **anchors** on headings, to allow linking to sections. These should be always used, to ensure the anchors can be linked even when the heading text changes, and to prevent conflicts between [automatically assigned identifiers](https://github.com/jgm/commonmark-hs/blob/master/commonmark-extensions/test/auto_identifiers.md). ··· 37 37 ```markdown 38 38 ## Syntax {#sec-contributing-markup} 39 39 ``` 40 + 41 + ::: {.note} 42 + NixOS option documentation does not support headings in general. 43 + ::: 40 44 41 45 - []{#ssec-contributing-markup-anchors-inline} 42 46 **Inline anchors**, which allow linking arbitrary place in the text (e.g. individual list items, sentences…). ··· 67 71 68 72 This syntax is taken from [MyST](https://myst-parser.readthedocs.io/en/latest/syntax/syntax.html#roles-an-in-line-extension-point). Though, the feature originates from [reStructuredText](https://www.sphinx-doc.org/en/master/usage/restructuredtext/roles.html#role-manpage) with slightly different syntax. 69 73 70 - ::: {.note} 71 - Inline roles are available for option documentation. 72 - ::: 73 - 74 74 - []{#ssec-contributing-markup-admonitions} 75 75 **Admonitions**, set off from the text to bring attention to something. 76 76 ··· 95 95 - [`note`](https://tdg.docbook.org/tdg/5.0/note.html) 96 96 - [`tip`](https://tdg.docbook.org/tdg/5.0/tip.html) 97 97 - [`warning`](https://tdg.docbook.org/tdg/5.0/warning.html) 98 - 99 - ::: {.note} 100 - Admonitions are available for option documentation. 101 - ::: 102 98 103 99 - []{#ssec-contributing-markup-definition-lists} 104 100 [**Definition lists**](https://github.com/jgm/commonmark-hs/blob/master/commonmark-extensions/test/definition_lists.md), for defining a group of terms:
+9 -6
nixos/doc/manual/default.nix
··· 68 68 69 69 sources = lib.sourceFilesBySuffices ./. [".xml"]; 70 70 71 - modulesDoc = builtins.toFile "modules.xml" '' 72 - <section xmlns:xi="http://www.w3.org/2001/XInclude" id="modules"> 73 - ${(lib.concatMapStrings (path: '' 74 - <xi:include href="${path}" /> 75 - '') (lib.catAttrs "value" config.meta.doc))} 76 - </section> 71 + modulesDoc = runCommand "modules.xml" { 72 + nativeBuildInputs = [ pkgs.nixos-render-docs ]; 73 + } '' 74 + nixos-render-docs manual docbook \ 75 + --manpage-urls ${pkgs.path + "/doc/manpage-urls.json"} \ 76 + "$out" \ 77 + --section \ 78 + --section-id modules \ 79 + --chapters ${lib.concatMapStrings (p: "${p.value} ") config.meta.doc} 77 80 ''; 78 81 79 82 generatedSources = runCommand "generated-docbook" {} ''
+4 -22
nixos/doc/manual/development/meta-attributes.section.md
··· 23 23 24 24 meta = { 25 25 maintainers = with lib.maintainers; [ ericsagnes ]; 26 - doc = ./default.xml; 26 + doc = ./default.md; 27 27 buildDocsInSandbox = true; 28 28 }; 29 29 } ··· 31 31 32 32 - `maintainers` contains a list of the module maintainers. 33 33 34 - - `doc` points to a valid DocBook file containing the module 34 + - `doc` points to a valid [Nixpkgs-flavored CommonMark]( 35 + https://nixos.org/manual/nixpkgs/unstable/#sec-contributing-markup 36 + ) file containing the module 35 37 documentation. Its contents is automatically added to 36 38 [](#ch-configuration). Changes to a module documentation have to 37 39 be checked to not break building the NixOS manual: 38 40 39 41 ```ShellSession 40 42 $ nix-build nixos/release.nix -A manual.x86_64-linux 41 - ``` 42 - 43 - This file should *not* usually be written by hand. Instead it is preferred 44 - to write documentation using CommonMark and converting it to CommonMark 45 - using pandoc. The simplest documentation can be converted using just 46 - 47 - ```ShellSession 48 - $ pandoc doc.md -t docbook --top-level-division=chapter -f markdown+smart > doc.xml 49 - ``` 50 - 51 - More elaborate documentation may wish to add one or more of the pandoc 52 - filters used to build the remainder of the manual, for example the GNOME 53 - desktop uses 54 - 55 - ```ShellSession 56 - $ pandoc gnome.md -t docbook --top-level-division=chapter \ 57 - --extract-media=media -f markdown+smart \ 58 - --lua-filter ../../../../../doc/build-aux/pandoc-filters/myst-reader/roles.lua \ 59 - --lua-filter ../../../../../doc/build-aux/pandoc-filters/docbook-writer/rst-roles.lua \ 60 - > gnome.xml 61 43 ``` 62 44 63 45 - `buildDocsInSandbox` indicates whether the option documentation for the
+5 -24
nixos/doc/manual/from_md/development/meta-attributes.section.xml
··· 28 28 29 29 meta = { 30 30 maintainers = with lib.maintainers; [ ericsagnes ]; 31 - doc = ./default.xml; 31 + doc = ./default.md; 32 32 buildDocsInSandbox = true; 33 33 }; 34 34 } ··· 42 42 </listitem> 43 43 <listitem> 44 44 <para> 45 - <literal>doc</literal> points to a valid DocBook file containing 46 - the module documentation. Its contents is automatically added to 45 + <literal>doc</literal> points to a valid 46 + <link xlink:href="https://nixos.org/manual/nixpkgs/unstable/#sec-contributing-markup">Nixpkgs-flavored 47 + CommonMark</link> file containing the module documentation. Its 48 + contents is automatically added to 47 49 <xref linkend="ch-configuration" />. Changes to a module 48 50 documentation have to be checked to not break building the NixOS 49 51 manual: 50 52 </para> 51 53 <programlisting> 52 54 $ nix-build nixos/release.nix -A manual.x86_64-linux 53 - </programlisting> 54 - <para> 55 - This file should <emphasis>not</emphasis> usually be written by 56 - hand. Instead it is preferred to write documentation using 57 - CommonMark and converting it to CommonMark using pandoc. The 58 - simplest documentation can be converted using just 59 - </para> 60 - <programlisting> 61 - $ pandoc doc.md -t docbook --top-level-division=chapter -f markdown+smart &gt; doc.xml 62 - </programlisting> 63 - <para> 64 - More elaborate documentation may wish to add one or more of the 65 - pandoc filters used to build the remainder of the manual, for 66 - example the GNOME desktop uses 67 - </para> 68 - <programlisting> 69 - $ pandoc gnome.md -t docbook --top-level-division=chapter \ 70 - --extract-media=media -f markdown+smart \ 71 - --lua-filter ../../../../../doc/build-aux/pandoc-filters/myst-reader/roles.lua \ 72 - --lua-filter ../../../../../doc/build-aux/pandoc-filters/docbook-writer/rst-roles.lua \ 73 - &gt; gnome.xml 74 55 </programlisting> 75 56 </listitem> 76 57 <listitem>
-18
nixos/doc/manual/md-to-db.sh
··· 50 50 done 51 51 52 52 popd 53 - 54 - # now handle module chapters. we'll need extra checks to ensure that we don't process 55 - # markdown files we're not interested in, so we'll require an x.nix file for ever x.md 56 - # that we'll convert to xml. 57 - pushd "$DIR/../../modules" 58 - 59 - mapfile -t MD_FILES < <(find . -type f -regex '.*\.md$') 60 - 61 - for mf in ${MD_FILES[*]}; do 62 - [ -f "${mf%.md}.nix" ] || continue 63 - 64 - pandoc --top-level-division=chapter "$mf" "${pandoc_flags[@]}" -o "${mf%.md}.xml" 65 - sed -i -e '1 i <!-- Do not edit this file directly, edit its companion .md instead\ 66 - and regenerate this file using nixos/doc/manual/md-to-db.sh -->' \ 67 - "${mf%.md}.xml" 68 - done 69 - 70 - popd
+8 -31
nixos/lib/make-options-doc/default.nix
··· 148 148 ''; 149 149 150 150 optionsDocBook = pkgs.runCommand "options-docbook.xml" { 151 - MANPAGE_URLS = pkgs.path + "/doc/manpage-urls.json"; 152 - OTD_DOCUMENT_TYPE = documentType; 153 - OTD_VARIABLE_LIST_ID = variablelistId; 154 - OTD_OPTION_ID_PREFIX = optionIdPrefix; 155 - OTD_REVISION = revision; 156 - 157 151 nativeBuildInputs = [ 158 - (let 159 - # python3Minimal can't be overridden with packages on Darwin, due to a missing framework. 160 - # Instead of modifying stdenv, we take the easy way out, since most people on Darwin will 161 - # just be hacking on the Nixpkgs manual (which also uses make-options-doc). 162 - python = if pkgs.stdenv.isDarwin then pkgs.python3 else pkgs.python3Minimal; 163 - self = (python.override { 164 - inherit self; 165 - includeSiteCustomize = true; 166 - }); 167 - in self.withPackages (p: 168 - let 169 - # TODO add our own small test suite when rendering is split out into a new tool 170 - markdown-it-py = p.markdown-it-py.override { 171 - disableTests = true; 172 - }; 173 - mdit-py-plugins = p.mdit-py-plugins.override { 174 - inherit markdown-it-py; 175 - disableTests = true; 176 - }; 177 - in [ 178 - markdown-it-py 179 - mdit-py-plugins 180 - ])) 152 + pkgs.nixos-render-docs 181 153 ]; 182 154 } '' 183 - python ${./optionsToDocbook.py} \ 155 + nixos-render-docs options docbook \ 156 + --manpage-urls ${pkgs.path + "/doc/manpage-urls.json"} \ 157 + --revision ${lib.escapeShellArg revision} \ 158 + --document-type ${lib.escapeShellArg documentType} \ 159 + --varlist-id ${lib.escapeShellArg variablelistId} \ 160 + --id-prefix ${lib.escapeShellArg optionIdPrefix} \ 184 161 ${lib.optionalString markdownByDefault "--markdown-by-default"} \ 185 162 ${optionsJSON}/share/doc/nixos/options.json \ 186 - > options.xml 163 + options.xml 187 164 188 165 if grep /nixpkgs/nixos/modules options.xml; then 189 166 echo "The manual appears to depend on the location of Nixpkgs, which is bad"
-343
nixos/lib/make-options-doc/optionsToDocbook.py
··· 1 - import collections 2 - import json 3 - import os 4 - import sys 5 - from typing import Any, Dict, List 6 - from collections.abc import MutableMapping, Sequence 7 - import inspect 8 - 9 - # for MD conversion 10 - import markdown_it 11 - import markdown_it.renderer 12 - from markdown_it.token import Token 13 - from markdown_it.utils import OptionsDict 14 - from mdit_py_plugins.container import container_plugin 15 - from mdit_py_plugins.deflist import deflist_plugin 16 - from mdit_py_plugins.myst_role import myst_role_plugin 17 - from xml.sax.saxutils import escape, quoteattr 18 - 19 - manpage_urls = json.load(open(os.getenv('MANPAGE_URLS'))) 20 - 21 - class Renderer(markdown_it.renderer.RendererProtocol): 22 - __output__ = "docbook" 23 - def __init__(self, parser=None): 24 - self.rules = { 25 - k: v 26 - for k, v in inspect.getmembers(self, predicate=inspect.ismethod) 27 - if not (k.startswith("render") or k.startswith("_")) 28 - } | { 29 - "container_{.note}_open": self._note_open, 30 - "container_{.note}_close": self._note_close, 31 - "container_{.important}_open": self._important_open, 32 - "container_{.important}_close": self._important_close, 33 - "container_{.warning}_open": self._warning_open, 34 - "container_{.warning}_close": self._warning_close, 35 - } 36 - def render(self, tokens: Sequence[Token], options: OptionsDict, env: MutableMapping) -> str: 37 - assert '-link-tag-stack' not in env 38 - env['-link-tag-stack'] = [] 39 - assert '-deflist-stack' not in env 40 - env['-deflist-stack'] = [] 41 - def do_one(i, token): 42 - if token.type == "inline": 43 - assert token.children is not None 44 - return self.renderInline(token.children, options, env) 45 - elif token.type in self.rules: 46 - return self.rules[token.type](tokens[i], tokens, i, options, env) 47 - else: 48 - raise NotImplementedError("md token not supported yet", token) 49 - return "".join(map(lambda arg: do_one(*arg), enumerate(tokens))) 50 - def renderInline(self, tokens: Sequence[Token], options: OptionsDict, env: MutableMapping) -> str: 51 - # HACK to support docbook links and xrefs. link handling is only necessary because the docbook 52 - # manpage stylesheet converts - in urls to a mathematical minus, which may be somewhat incorrect. 53 - for i, token in enumerate(tokens): 54 - if token.type != 'link_open': 55 - continue 56 - token.tag = 'link' 57 - # turn [](#foo) into xrefs 58 - if token.attrs['href'][0:1] == '#' and tokens[i + 1].type == 'link_close': 59 - token.tag = "xref" 60 - # turn <x> into links without contents 61 - if tokens[i + 1].type == 'text' and tokens[i + 1].content == token.attrs['href']: 62 - tokens[i + 1].content = '' 63 - 64 - def do_one(i, token): 65 - if token.type in self.rules: 66 - return self.rules[token.type](tokens[i], tokens, i, options, env) 67 - else: 68 - raise NotImplementedError("md node not supported yet", token) 69 - return "".join(map(lambda arg: do_one(*arg), enumerate(tokens))) 70 - 71 - def text(self, token, tokens, i, options, env): 72 - return escape(token.content) 73 - def paragraph_open(self, token, tokens, i, options, env): 74 - return "<para>" 75 - def paragraph_close(self, token, tokens, i, options, env): 76 - return "</para>" 77 - def hardbreak(self, token, tokens, i, options, env): 78 - return "<literallayout>\n</literallayout>" 79 - def softbreak(self, token, tokens, i, options, env): 80 - # should check options.breaks() and emit hard break if so 81 - return "\n" 82 - def code_inline(self, token, tokens, i, options, env): 83 - return f"<literal>{escape(token.content)}</literal>" 84 - def code_block(self, token, tokens, i, options, env): 85 - return f"<programlisting>{escape(token.content)}</programlisting>" 86 - def link_open(self, token, tokens, i, options, env): 87 - env['-link-tag-stack'].append(token.tag) 88 - (attr, start) = ('linkend', 1) if token.attrs['href'][0] == '#' else ('xlink:href', 0) 89 - return f"<{token.tag} {attr}={quoteattr(token.attrs['href'][start:])}>" 90 - def link_close(self, token, tokens, i, options, env): 91 - return f"</{env['-link-tag-stack'].pop()}>" 92 - def list_item_open(self, token, tokens, i, options, env): 93 - return "<listitem>" 94 - def list_item_close(self, token, tokens, i, options, env): 95 - return "</listitem>\n" 96 - # HACK open and close para for docbook change size. remove soon. 97 - def bullet_list_open(self, token, tokens, i, options, env): 98 - return "<para><itemizedlist>\n" 99 - def bullet_list_close(self, token, tokens, i, options, env): 100 - return "\n</itemizedlist></para>" 101 - def em_open(self, token, tokens, i, options, env): 102 - return "<emphasis>" 103 - def em_close(self, token, tokens, i, options, env): 104 - return "</emphasis>" 105 - def strong_open(self, token, tokens, i, options, env): 106 - return "<emphasis role=\"strong\">" 107 - def strong_close(self, token, tokens, i, options, env): 108 - return "</emphasis>" 109 - def fence(self, token, tokens, i, options, env): 110 - info = f" language={quoteattr(token.info)}" if token.info != "" else "" 111 - return f"<programlisting{info}>{escape(token.content)}</programlisting>" 112 - def blockquote_open(self, token, tokens, i, options, env): 113 - return "<para><blockquote>" 114 - def blockquote_close(self, token, tokens, i, options, env): 115 - return "</blockquote></para>" 116 - def _note_open(self, token, tokens, i, options, env): 117 - return "<para><note>" 118 - def _note_close(self, token, tokens, i, options, env): 119 - return "</note></para>" 120 - def _important_open(self, token, tokens, i, options, env): 121 - return "<para><important>" 122 - def _important_close(self, token, tokens, i, options, env): 123 - return "</important></para>" 124 - def _warning_open(self, token, tokens, i, options, env): 125 - return "<para><warning>" 126 - def _warning_close(self, token, tokens, i, options, env): 127 - return "</warning></para>" 128 - # markdown-it emits tokens based on the html syntax tree, but docbook is 129 - # slightly different. html has <dl>{<dt/>{<dd/>}}</dl>, 130 - # docbook has <variablelist>{<varlistentry><term/><listitem/></varlistentry>}<variablelist> 131 - # we have to reject multiple definitions for the same term for time being. 132 - def dl_open(self, token, tokens, i, options, env): 133 - env['-deflist-stack'].append({}) 134 - return "<para><variablelist>" 135 - def dl_close(self, token, tokens, i, options, env): 136 - env['-deflist-stack'].pop() 137 - return "</variablelist></para>" 138 - def dt_open(self, token, tokens, i, options, env): 139 - env['-deflist-stack'][-1]['has-dd'] = False 140 - return "<varlistentry><term>" 141 - def dt_close(self, token, tokens, i, options, env): 142 - return "</term>" 143 - def dd_open(self, token, tokens, i, options, env): 144 - if env['-deflist-stack'][-1]['has-dd']: 145 - raise Exception("multiple definitions per term not supported") 146 - env['-deflist-stack'][-1]['has-dd'] = True 147 - return "<listitem>" 148 - def dd_close(self, token, tokens, i, options, env): 149 - return "</listitem></varlistentry>" 150 - def myst_role(self, token, tokens, i, options, env): 151 - if token.meta['name'] == 'command': 152 - return f"<command>{escape(token.content)}</command>" 153 - if token.meta['name'] == 'file': 154 - return f"<filename>{escape(token.content)}</filename>" 155 - if token.meta['name'] == 'var': 156 - return f"<varname>{escape(token.content)}</varname>" 157 - if token.meta['name'] == 'env': 158 - return f"<envar>{escape(token.content)}</envar>" 159 - if token.meta['name'] == 'option': 160 - return f"<option>{escape(token.content)}</option>" 161 - if token.meta['name'] == 'manpage': 162 - [page, section] = [ s.strip() for s in token.content.rsplit('(', 1) ] 163 - section = section[:-1] 164 - man = f"{page}({section})" 165 - title = f"<refentrytitle>{escape(page)}</refentrytitle>" 166 - vol = f"<manvolnum>{escape(section)}</manvolnum>" 167 - ref = f"<citerefentry>{title}{vol}</citerefentry>" 168 - if man in manpage_urls: 169 - return f"<link xlink:href={quoteattr(manpage_urls[man])}>{ref}</link>" 170 - else: 171 - return ref 172 - raise NotImplementedError("md node not supported yet", token) 173 - 174 - md = ( 175 - markdown_it.MarkdownIt(renderer_cls=Renderer) 176 - # TODO maybe fork the plugin and have only a single rule for all? 177 - .use(container_plugin, name="{.note}") 178 - .use(container_plugin, name="{.important}") 179 - .use(container_plugin, name="{.warning}") 180 - .use(deflist_plugin) 181 - .use(myst_role_plugin) 182 - ) 183 - 184 - # converts in-place! 185 - def convertMD(options: Dict[str, Any]) -> str: 186 - def optionIs(option: Dict[str, Any], key: str, typ: str) -> bool: 187 - if key not in option: return False 188 - if type(option[key]) != dict: return False 189 - if '_type' not in option[key]: return False 190 - return option[key]['_type'] == typ 191 - 192 - def convertCode(name: str, option: Dict[str, Any], key: str): 193 - if optionIs(option, key, 'literalMD'): 194 - option[key] = md.render(f"*{key.capitalize()}:*\n{option[key]['text']}") 195 - elif optionIs(option, key, 'literalExpression'): 196 - code = option[key]['text'] 197 - # for multi-line code blocks we only have to count ` runs at the beginning 198 - # of a line, but this is much easier. 199 - multiline = '\n' in code 200 - longest, current = (0, 0) 201 - for c in code: 202 - current = current + 1 if c == '`' else 0 203 - longest = max(current, longest) 204 - # inline literals need a space to separate ticks from content, code blocks 205 - # need newlines. inline literals need one extra tick, code blocks need three. 206 - ticks, sep = ('`' * (longest + (3 if multiline else 1)), '\n' if multiline else ' ') 207 - code = f"{ticks}{sep}{code}{sep}{ticks}" 208 - option[key] = md.render(f"*{key.capitalize()}:*\n{code}") 209 - elif optionIs(option, key, 'literalDocBook'): 210 - option[key] = f"<para><emphasis>{key.capitalize()}:</emphasis> {option[key]['text']}</para>" 211 - elif key in option: 212 - raise Exception(f"{name} {key} has unrecognized type", option[key]) 213 - 214 - for (name, option) in options.items(): 215 - try: 216 - if optionIs(option, 'description', 'mdDoc'): 217 - option['description'] = md.render(option['description']['text']) 218 - elif markdownByDefault: 219 - option['description'] = md.render(option['description']) 220 - else: 221 - option['description'] = ("<nixos:option-description><para>" + 222 - option['description'] + 223 - "</para></nixos:option-description>") 224 - 225 - convertCode(name, option, 'example') 226 - convertCode(name, option, 'default') 227 - 228 - if 'relatedPackages' in option: 229 - option['relatedPackages'] = md.render(option['relatedPackages']) 230 - except Exception as e: 231 - raise Exception(f"Failed to render option {name}") from e 232 - 233 - return options 234 - 235 - id_translate_table = { 236 - ord('*'): ord('_'), 237 - ord('<'): ord('_'), 238 - ord(' '): ord('_'), 239 - ord('>'): ord('_'), 240 - ord('['): ord('_'), 241 - ord(']'): ord('_'), 242 - ord(':'): ord('_'), 243 - ord('"'): ord('_'), 244 - } 245 - 246 - def need_env(n): 247 - if n not in os.environ: 248 - raise RuntimeError("required environment variable not set", n) 249 - return os.environ[n] 250 - 251 - OTD_REVISION = need_env('OTD_REVISION') 252 - OTD_DOCUMENT_TYPE = need_env('OTD_DOCUMENT_TYPE') 253 - OTD_VARIABLE_LIST_ID = need_env('OTD_VARIABLE_LIST_ID') 254 - OTD_OPTION_ID_PREFIX = need_env('OTD_OPTION_ID_PREFIX') 255 - 256 - def print_decl_def(header, locs): 257 - print(f"""<para><emphasis>{header}:</emphasis></para>""") 258 - print(f"""<simplelist>""") 259 - for loc in locs: 260 - # locations can be either plain strings (specific to nixpkgs), or attrsets 261 - # { name = "foo/bar.nix"; url = "https://github.com/....."; } 262 - if isinstance(loc, str): 263 - # Hyperlink the filename either to the NixOS github 264 - # repository (if it’s a module and we have a revision number), 265 - # or to the local filesystem. 266 - if not loc.startswith('/'): 267 - if OTD_REVISION == 'local': 268 - href = f"https://github.com/NixOS/nixpkgs/blob/master/{loc}" 269 - else: 270 - href = f"https://github.com/NixOS/nixpkgs/blob/{OTD_REVISION}/{loc}" 271 - else: 272 - href = f"file://{loc}" 273 - # Print the filename and make it user-friendly by replacing the 274 - # /nix/store/<hash> prefix by the default location of nixos 275 - # sources. 276 - if not loc.startswith('/'): 277 - name = f"<nixpkgs/{loc}>" 278 - elif loc.contains('nixops') and loc.contains('/nix/'): 279 - name = f"<nixops/{loc[loc.find('/nix/') + 5:]}>" 280 - else: 281 - name = loc 282 - print(f"""<member><filename xlink:href={quoteattr(href)}>""") 283 - print(escape(name)) 284 - print(f"""</filename></member>""") 285 - else: 286 - href = f" xlink:href={quoteattr(loc['url'])}" if 'url' in loc else "" 287 - print(f"""<member><filename{href}>{escape(loc['name'])}</filename></member>""") 288 - print(f"""</simplelist>""") 289 - 290 - markdownByDefault = False 291 - optOffset = 0 292 - for arg in sys.argv[1:]: 293 - if arg == "--markdown-by-default": 294 - optOffset += 1 295 - markdownByDefault = True 296 - 297 - options = convertMD(json.load(open(sys.argv[1 + optOffset], 'r'))) 298 - 299 - keys = list(options.keys()) 300 - keys.sort(key=lambda opt: [ (0 if p.startswith("enable") else 1 if p.startswith("package") else 2, p) 301 - for p in options[opt]['loc'] ]) 302 - 303 - print(f"""<?xml version="1.0" encoding="UTF-8"?>""") 304 - if OTD_DOCUMENT_TYPE == 'appendix': 305 - print("""<appendix xmlns="http://docbook.org/ns/docbook" xml:id="appendix-configuration-options">""") 306 - print(""" <title>Configuration Options</title>""") 307 - print(f"""<variablelist xmlns:xlink="http://www.w3.org/1999/xlink" 308 - xmlns:nixos="tag:nixos.org" 309 - xmlns="http://docbook.org/ns/docbook" 310 - xml:id="{OTD_VARIABLE_LIST_ID}">""") 311 - 312 - for name in keys: 313 - opt = options[name] 314 - id = OTD_OPTION_ID_PREFIX + name.translate(id_translate_table) 315 - print(f"""<varlistentry>""") 316 - # NOTE adding extra spaces here introduces spaces into xref link expansions 317 - print(f"""<term xlink:href={quoteattr("#" + id)} xml:id={quoteattr(id)}>""", end='') 318 - print(f"""<option>{escape(name)}</option>""", end='') 319 - print(f"""</term>""") 320 - print(f"""<listitem>""") 321 - print(opt['description']) 322 - if typ := opt.get('type'): 323 - ro = " <emphasis>(read only)</emphasis>" if opt.get('readOnly', False) else "" 324 - print(f"""<para><emphasis>Type:</emphasis> {escape(typ)}{ro}</para>""") 325 - if default := opt.get('default'): 326 - print(default) 327 - if example := opt.get('example'): 328 - print(example) 329 - if related := opt.get('relatedPackages'): 330 - print(f"""<para>""") 331 - print(f""" <emphasis>Related packages:</emphasis>""") 332 - print(f"""</para>""") 333 - print(related) 334 - if decl := opt.get('declarations'): 335 - print_decl_def("Declared by", decl) 336 - if defs := opt.get('definitions'): 337 - print_decl_def("Defined by", defs) 338 - print(f"""</listitem>""") 339 - print(f"""</varlistentry>""") 340 - 341 - print("""</variablelist>""") 342 - if OTD_DOCUMENT_TYPE == 'appendix': 343 - print("""</appendix>""")
+1 -1
nixos/modules/i18n/input-method/default.nix
··· 66 66 67 67 meta = { 68 68 maintainers = with lib.maintainers; [ ericsagnes ]; 69 - doc = ./default.xml; 69 + doc = ./default.md; 70 70 }; 71 71 72 72 }
-275
nixos/modules/i18n/input-method/default.xml
··· 1 - <!-- Do not edit this file directly, edit its companion .md instead 2 - and regenerate this file using nixos/doc/manual/md-to-db.sh --> 3 - <chapter xmlns="http://docbook.org/ns/docbook" xmlns:xlink="http://www.w3.org/1999/xlink" xml:id="module-services-input-methods"> 4 - <title>Input Methods</title> 5 - <para> 6 - Input methods are an operating system component that allows any 7 - data, such as keyboard strokes or mouse movements, to be received as 8 - input. In this way users can enter characters and symbols not found 9 - on their input devices. Using an input method is obligatory for any 10 - language that has more graphemes than there are keys on the 11 - keyboard. 12 - </para> 13 - <para> 14 - The following input methods are available in NixOS: 15 - </para> 16 - <itemizedlist spacing="compact"> 17 - <listitem> 18 - <para> 19 - IBus: The intelligent input bus. 20 - </para> 21 - </listitem> 22 - <listitem> 23 - <para> 24 - Fcitx: A customizable lightweight input method. 25 - </para> 26 - </listitem> 27 - <listitem> 28 - <para> 29 - Nabi: A Korean input method based on XIM. 30 - </para> 31 - </listitem> 32 - <listitem> 33 - <para> 34 - Uim: The universal input method, is a library with a XIM bridge. 35 - </para> 36 - </listitem> 37 - <listitem> 38 - <para> 39 - Hime: An extremely easy-to-use input method framework. 40 - </para> 41 - </listitem> 42 - <listitem> 43 - <para> 44 - Kime: Korean IME 45 - </para> 46 - </listitem> 47 - </itemizedlist> 48 - <section xml:id="module-services-input-methods-ibus"> 49 - <title>IBus</title> 50 - <para> 51 - IBus is an Intelligent Input Bus. It provides full featured and 52 - user friendly input method user interface. 53 - </para> 54 - <para> 55 - The following snippet can be used to configure IBus: 56 - </para> 57 - <programlisting> 58 - i18n.inputMethod = { 59 - enabled = &quot;ibus&quot;; 60 - ibus.engines = with pkgs.ibus-engines; [ anthy hangul mozc ]; 61 - }; 62 - </programlisting> 63 - <para> 64 - <literal>i18n.inputMethod.ibus.engines</literal> is optional and 65 - can be used to add extra IBus engines. 66 - </para> 67 - <para> 68 - Available extra IBus engines are: 69 - </para> 70 - <itemizedlist> 71 - <listitem> 72 - <para> 73 - Anthy (<literal>ibus-engines.anthy</literal>): Anthy is a 74 - system for Japanese input method. It converts Hiragana text to 75 - Kana Kanji mixed text. 76 - </para> 77 - </listitem> 78 - <listitem> 79 - <para> 80 - Hangul (<literal>ibus-engines.hangul</literal>): Korean input 81 - method. 82 - </para> 83 - </listitem> 84 - <listitem> 85 - <para> 86 - m17n (<literal>ibus-engines.m17n</literal>): m17n is an input 87 - method that uses input methods and corresponding icons in the 88 - m17n database. 89 - </para> 90 - </listitem> 91 - <listitem> 92 - <para> 93 - mozc (<literal>ibus-engines.mozc</literal>): A Japanese input 94 - method from Google. 95 - </para> 96 - </listitem> 97 - <listitem> 98 - <para> 99 - Table (<literal>ibus-engines.table</literal>): An input method 100 - that load tables of input methods. 101 - </para> 102 - </listitem> 103 - <listitem> 104 - <para> 105 - table-others (<literal>ibus-engines.table-others</literal>): 106 - Various table-based input methods. To use this, and any other 107 - table-based input methods, it must appear in the list of 108 - engines along with <literal>table</literal>. For example: 109 - </para> 110 - <programlisting> 111 - ibus.engines = with pkgs.ibus-engines; [ table table-others ]; 112 - </programlisting> 113 - </listitem> 114 - </itemizedlist> 115 - <para> 116 - To use any input method, the package must be added in the 117 - configuration, as shown above, and also (after running 118 - <literal>nixos-rebuild</literal>) the input method must be added 119 - from IBus’ preference dialog. 120 - </para> 121 - <section xml:id="module-services-input-methods-troubleshooting"> 122 - <title>Troubleshooting</title> 123 - <para> 124 - If IBus works in some applications but not others, a likely 125 - cause of this is that IBus is depending on a different version 126 - of <literal>glib</literal> to what the applications are 127 - depending on. This can be checked by running 128 - <literal>nix-store -q --requisites &lt;path&gt; | grep glib</literal>, 129 - where <literal>&lt;path&gt;</literal> is the path of either IBus 130 - or an application in the Nix store. The <literal>glib</literal> 131 - packages must match exactly. If they do not, uninstalling and 132 - reinstalling the application is a likely fix. 133 - </para> 134 - </section> 135 - </section> 136 - <section xml:id="module-services-input-methods-fcitx"> 137 - <title>Fcitx</title> 138 - <para> 139 - Fcitx is an input method framework with extension support. It has 140 - three built-in Input Method Engine, Pinyin, QuWei and Table-based 141 - input methods. 142 - </para> 143 - <para> 144 - The following snippet can be used to configure Fcitx: 145 - </para> 146 - <programlisting> 147 - i18n.inputMethod = { 148 - enabled = &quot;fcitx&quot;; 149 - fcitx.engines = with pkgs.fcitx-engines; [ mozc hangul m17n ]; 150 - }; 151 - </programlisting> 152 - <para> 153 - <literal>i18n.inputMethod.fcitx.engines</literal> is optional and 154 - can be used to add extra Fcitx engines. 155 - </para> 156 - <para> 157 - Available extra Fcitx engines are: 158 - </para> 159 - <itemizedlist spacing="compact"> 160 - <listitem> 161 - <para> 162 - Anthy (<literal>fcitx-engines.anthy</literal>): Anthy is a 163 - system for Japanese input method. It converts Hiragana text to 164 - Kana Kanji mixed text. 165 - </para> 166 - </listitem> 167 - <listitem> 168 - <para> 169 - Chewing (<literal>fcitx-engines.chewing</literal>): Chewing is 170 - an intelligent Zhuyin input method. It is one of the most 171 - popular input methods among Traditional Chinese Unix users. 172 - </para> 173 - </listitem> 174 - <listitem> 175 - <para> 176 - Hangul (<literal>fcitx-engines.hangul</literal>): Korean input 177 - method. 178 - </para> 179 - </listitem> 180 - <listitem> 181 - <para> 182 - Unikey (<literal>fcitx-engines.unikey</literal>): Vietnamese 183 - input method. 184 - </para> 185 - </listitem> 186 - <listitem> 187 - <para> 188 - m17n (<literal>fcitx-engines.m17n</literal>): m17n is an input 189 - method that uses input methods and corresponding icons in the 190 - m17n database. 191 - </para> 192 - </listitem> 193 - <listitem> 194 - <para> 195 - mozc (<literal>fcitx-engines.mozc</literal>): A Japanese input 196 - method from Google. 197 - </para> 198 - </listitem> 199 - <listitem> 200 - <para> 201 - table-others (<literal>fcitx-engines.table-others</literal>): 202 - Various table-based input methods. 203 - </para> 204 - </listitem> 205 - </itemizedlist> 206 - </section> 207 - <section xml:id="module-services-input-methods-nabi"> 208 - <title>Nabi</title> 209 - <para> 210 - Nabi is an easy to use Korean X input method. It allows you to 211 - enter phonetic Korean characters (hangul) and pictographic Korean 212 - characters (hanja). 213 - </para> 214 - <para> 215 - The following snippet can be used to configure Nabi: 216 - </para> 217 - <programlisting> 218 - i18n.inputMethod = { 219 - enabled = &quot;nabi&quot;; 220 - }; 221 - </programlisting> 222 - </section> 223 - <section xml:id="module-services-input-methods-uim"> 224 - <title>Uim</title> 225 - <para> 226 - Uim (short for <quote>universal input method</quote>) is a 227 - multilingual input method framework. Applications can use it 228 - through so-called bridges. 229 - </para> 230 - <para> 231 - The following snippet can be used to configure uim: 232 - </para> 233 - <programlisting> 234 - i18n.inputMethod = { 235 - enabled = &quot;uim&quot;; 236 - }; 237 - </programlisting> 238 - <para> 239 - Note: The <xref linkend="opt-i18n.inputMethod.uim.toolbar" /> 240 - option can be used to choose uim toolbar. 241 - </para> 242 - </section> 243 - <section xml:id="module-services-input-methods-hime"> 244 - <title>Hime</title> 245 - <para> 246 - Hime is an extremely easy-to-use input method framework. It is 247 - lightweight, stable, powerful and supports many commonly used 248 - input methods, including Cangjie, Zhuyin, Dayi, Rank, Shrimp, 249 - Greek, Korean Pinyin, Latin Alphabet, etc… 250 - </para> 251 - <para> 252 - The following snippet can be used to configure Hime: 253 - </para> 254 - <programlisting> 255 - i18n.inputMethod = { 256 - enabled = &quot;hime&quot;; 257 - }; 258 - </programlisting> 259 - </section> 260 - <section xml:id="module-services-input-methods-kime"> 261 - <title>Kime</title> 262 - <para> 263 - Kime is Korean IME. it’s built with Rust language and let you get 264 - simple, safe, fast Korean typing 265 - </para> 266 - <para> 267 - The following snippet can be used to configure Kime: 268 - </para> 269 - <programlisting> 270 - i18n.inputMethod = { 271 - enabled = &quot;kime&quot;; 272 - }; 273 - </programlisting> 274 - </section> 275 - </chapter>
+1 -1
nixos/modules/misc/meta.nix
··· 47 47 doc = mkOption { 48 48 type = docFile; 49 49 internal = true; 50 - example = "./meta.chapter.xml"; 50 + example = "./meta.chapter.md"; 51 51 description = lib.mdDoc '' 52 52 Documentation prologue for the set of options of each module. This 53 53 option should be defined at most once per module.
+1 -1
nixos/modules/programs/digitalbitbox/default.nix
··· 33 33 }; 34 34 35 35 meta = { 36 - doc = ./default.xml; 36 + doc = ./default.md; 37 37 maintainers = with lib.maintainers; [ vidbina ]; 38 38 }; 39 39 }
-70
nixos/modules/programs/digitalbitbox/default.xml
··· 1 - <!-- Do not edit this file directly, edit its companion .md instead 2 - and regenerate this file using nixos/doc/manual/md-to-db.sh --> 3 - <chapter xmlns="http://docbook.org/ns/docbook" xmlns:xlink="http://www.w3.org/1999/xlink" xml:id="module-programs-digitalbitbox"> 4 - <title>Digital Bitbox</title> 5 - <para> 6 - Digital Bitbox is a hardware wallet and second-factor authenticator. 7 - </para> 8 - <para> 9 - The <literal>digitalbitbox</literal> programs module may be 10 - installed by setting <literal>programs.digitalbitbox</literal> to 11 - <literal>true</literal> in a manner similar to 12 - </para> 13 - <programlisting> 14 - programs.digitalbitbox.enable = true; 15 - </programlisting> 16 - <para> 17 - and bundles the <literal>digitalbitbox</literal> package (see 18 - <xref linkend="sec-digitalbitbox-package" />), which contains the 19 - <literal>dbb-app</literal> and <literal>dbb-cli</literal> binaries, 20 - along with the hardware module (see 21 - <xref linkend="sec-digitalbitbox-hardware-module" />) which sets up 22 - the necessary udev rules to access the device. 23 - </para> 24 - <para> 25 - Enabling the digitalbitbox module is pretty much the easiest way to 26 - get a Digital Bitbox device working on your system. 27 - </para> 28 - <para> 29 - For more information, see 30 - <link xlink:href="https://digitalbitbox.com/start_linux">https://digitalbitbox.com/start_linux</link>. 31 - </para> 32 - <section xml:id="sec-digitalbitbox-package"> 33 - <title>Package</title> 34 - <para> 35 - The binaries, <literal>dbb-app</literal> (a GUI tool) and 36 - <literal>dbb-cli</literal> (a CLI tool), are available through the 37 - <literal>digitalbitbox</literal> package which could be installed 38 - as follows: 39 - </para> 40 - <programlisting> 41 - environment.systemPackages = [ 42 - pkgs.digitalbitbox 43 - ]; 44 - </programlisting> 45 - </section> 46 - <section xml:id="sec-digitalbitbox-hardware-module"> 47 - <title>Hardware</title> 48 - <para> 49 - The digitalbitbox hardware package enables the udev rules for 50 - Digital Bitbox devices and may be installed as follows: 51 - </para> 52 - <programlisting> 53 - hardware.digitalbitbox.enable = true; 54 - </programlisting> 55 - <para> 56 - In order to alter the udev rules, one may provide different values 57 - for the <literal>udevRule51</literal> and 58 - <literal>udevRule52</literal> attributes by means of overriding as 59 - follows: 60 - </para> 61 - <programlisting> 62 - programs.digitalbitbox = { 63 - enable = true; 64 - package = pkgs.digitalbitbox.override { 65 - udevRule51 = &quot;something else&quot;; 66 - }; 67 - }; 68 - </programlisting> 69 - </section> 70 - </chapter>
+1 -1
nixos/modules/programs/plotinus.nix
··· 8 8 { 9 9 meta = { 10 10 maintainers = pkgs.plotinus.meta.maintainers; 11 - doc = ./plotinus.xml; 11 + doc = ./plotinus.md; 12 12 }; 13 13 14 14 ###### interface
-30
nixos/modules/programs/plotinus.xml
··· 1 - <!-- Do not edit this file directly, edit its companion .md instead 2 - and regenerate this file using nixos/doc/manual/md-to-db.sh --> 3 - <chapter xmlns="http://docbook.org/ns/docbook" xmlns:xlink="http://www.w3.org/1999/xlink" xml:id="module-program-plotinus"> 4 - <title>Plotinus</title> 5 - <para> 6 - <emphasis>Source:</emphasis> 7 - <filename>modules/programs/plotinus.nix</filename> 8 - </para> 9 - <para> 10 - <emphasis>Upstream documentation:</emphasis> 11 - <link xlink:href="https://github.com/p-e-w/plotinus">https://github.com/p-e-w/plotinus</link> 12 - </para> 13 - <para> 14 - Plotinus is a searchable command palette in every modern GTK 15 - application. 16 - </para> 17 - <para> 18 - When in a GTK 3 application and Plotinus is enabled, you can press 19 - <literal>Ctrl+Shift+P</literal> to open the command palette. The 20 - command palette provides a searchable list of of all menu items in 21 - the application. 22 - </para> 23 - <para> 24 - To enable Plotinus, add the following to your 25 - <filename>configuration.nix</filename>: 26 - </para> 27 - <programlisting> 28 - programs.plotinus.enable = true; 29 - </programlisting> 30 - </chapter>
+1 -1
nixos/modules/programs/zsh/oh-my-zsh.nix
··· 142 142 143 143 }; 144 144 145 - meta.doc = ./oh-my-zsh.xml; 145 + meta.doc = ./oh-my-zsh.md; 146 146 }
-154
nixos/modules/programs/zsh/oh-my-zsh.xml
··· 1 - <!-- Do not edit this file directly, edit its companion .md instead 2 - and regenerate this file using nixos/doc/manual/md-to-db.sh --> 3 - <chapter xmlns="http://docbook.org/ns/docbook" xmlns:xlink="http://www.w3.org/1999/xlink" xml:id="module-programs-zsh-ohmyzsh"> 4 - <title>Oh my ZSH</title> 5 - <para> 6 - <link xlink:href="https://ohmyz.sh/"><literal>oh-my-zsh</literal></link> 7 - is a framework to manage your 8 - <link xlink:href="https://www.zsh.org/">ZSH</link> configuration 9 - including completion scripts for several CLI tools or custom prompt 10 - themes. 11 - </para> 12 - <section xml:id="module-programs-oh-my-zsh-usage"> 13 - <title>Basic usage</title> 14 - <para> 15 - The module uses the <literal>oh-my-zsh</literal> package with all 16 - available features. The initial setup using Nix expressions is 17 - fairly similar to the configuration format of 18 - <literal>oh-my-zsh</literal>. 19 - </para> 20 - <programlisting> 21 - { 22 - programs.zsh.ohMyZsh = { 23 - enable = true; 24 - plugins = [ &quot;git&quot; &quot;python&quot; &quot;man&quot; ]; 25 - theme = &quot;agnoster&quot;; 26 - }; 27 - } 28 - </programlisting> 29 - <para> 30 - For a detailed explanation of these arguments please refer to the 31 - <link xlink:href="https://github.com/robbyrussell/oh-my-zsh/wiki"><literal>oh-my-zsh</literal> 32 - docs</link>. 33 - </para> 34 - <para> 35 - The expression generates the needed configuration and writes it 36 - into your <literal>/etc/zshrc</literal>. 37 - </para> 38 - </section> 39 - <section xml:id="module-programs-oh-my-zsh-additions"> 40 - <title>Custom additions</title> 41 - <para> 42 - Sometimes third-party or custom scripts such as a modified theme 43 - may be needed. <literal>oh-my-zsh</literal> provides the 44 - <link xlink:href="https://github.com/robbyrussell/oh-my-zsh/wiki/Customization#overriding-internals"><literal>ZSH_CUSTOM</literal></link> 45 - environment variable for this which points to a directory with 46 - additional scripts. 47 - </para> 48 - <para> 49 - The module can do this as well: 50 - </para> 51 - <programlisting> 52 - { 53 - programs.zsh.ohMyZsh.custom = &quot;~/path/to/custom/scripts&quot;; 54 - } 55 - </programlisting> 56 - </section> 57 - <section xml:id="module-programs-oh-my-zsh-environments"> 58 - <title>Custom environments</title> 59 - <para> 60 - There are several extensions for <literal>oh-my-zsh</literal> 61 - packaged in <literal>nixpkgs</literal>. One of them is 62 - <link xlink:href="https://github.com/spwhitt/nix-zsh-completions">nix-zsh-completions</link> 63 - which bundles completion scripts and a plugin for 64 - <literal>oh-my-zsh</literal>. 65 - </para> 66 - <para> 67 - Rather than using a single mutable path for 68 - <literal>ZSH_CUSTOM</literal>, it’s also possible to generate this 69 - path from a list of Nix packages: 70 - </para> 71 - <programlisting> 72 - { pkgs, ... }: 73 - { 74 - programs.zsh.ohMyZsh.customPkgs = [ 75 - pkgs.nix-zsh-completions 76 - # and even more... 77 - ]; 78 - } 79 - </programlisting> 80 - <para> 81 - Internally a single store path will be created using 82 - <literal>buildEnv</literal>. Please refer to the docs of 83 - <link xlink:href="https://nixos.org/nixpkgs/manual/#sec-building-environment"><literal>buildEnv</literal></link> 84 - for further reference. 85 - </para> 86 - <para> 87 - <emphasis>Please keep in mind that this is not compatible with 88 - <literal>programs.zsh.ohMyZsh.custom</literal> as it requires an 89 - immutable store path while <literal>custom</literal> shall remain 90 - mutable! An evaluation failure will be thrown if both 91 - <literal>custom</literal> and <literal>customPkgs</literal> are 92 - set.</emphasis> 93 - </para> 94 - </section> 95 - <section xml:id="module-programs-oh-my-zsh-packaging-customizations"> 96 - <title>Package your own customizations</title> 97 - <para> 98 - If third-party customizations (e.g. new themes) are supposed to be 99 - added to <literal>oh-my-zsh</literal> there are several pitfalls 100 - to keep in mind: 101 - </para> 102 - <itemizedlist> 103 - <listitem> 104 - <para> 105 - To comply with the default structure of <literal>ZSH</literal> 106 - the entire output needs to be written to 107 - <literal>$out/share/zsh.</literal> 108 - </para> 109 - </listitem> 110 - <listitem> 111 - <para> 112 - Completion scripts are supposed to be stored at 113 - <literal>$out/share/zsh/site-functions</literal>. This 114 - directory is part of the 115 - <link xlink:href="http://zsh.sourceforge.net/Doc/Release/Functions.html"><literal>fpath</literal></link> 116 - and the package should be compatible with pure 117 - <literal>ZSH</literal> setups. The module will automatically 118 - link the contents of <literal>site-functions</literal> to 119 - completions directory in the proper store path. 120 - </para> 121 - </listitem> 122 - <listitem> 123 - <para> 124 - The <literal>plugins</literal> directory needs the structure 125 - <literal>pluginname/pluginname.plugin.zsh</literal> as 126 - structured in the 127 - <link xlink:href="https://github.com/robbyrussell/oh-my-zsh/tree/91b771914bc7c43dd7c7a43b586c5de2c225ceb7/plugins">upstream 128 - repo.</link> 129 - </para> 130 - </listitem> 131 - </itemizedlist> 132 - <para> 133 - A derivation for <literal>oh-my-zsh</literal> may look like this: 134 - </para> 135 - <programlisting> 136 - { stdenv, fetchFromGitHub }: 137 - 138 - stdenv.mkDerivation rec { 139 - name = &quot;exemplary-zsh-customization-${version}&quot;; 140 - version = &quot;1.0.0&quot;; 141 - src = fetchFromGitHub { 142 - # path to the upstream repository 143 - }; 144 - 145 - dontBuild = true; 146 - installPhase = '' 147 - mkdir -p $out/share/zsh/site-functions 148 - cp {themes,plugins} $out/share/zsh 149 - cp completions $out/share/zsh/site-functions 150 - ''; 151 - } 152 - </programlisting> 153 - </section> 154 - </chapter>
+1 -1
nixos/modules/security/acme/default.nix
··· 916 916 917 917 meta = { 918 918 maintainers = lib.teams.acme.members; 919 - doc = ./default.xml; 919 + doc = ./default.md; 920 920 }; 921 921 }
-395
nixos/modules/security/acme/default.xml
··· 1 - <!-- Do not edit this file directly, edit its companion .md instead 2 - and regenerate this file using nixos/doc/manual/md-to-db.sh --> 3 - <chapter xmlns="http://docbook.org/ns/docbook" xmlns:xlink="http://www.w3.org/1999/xlink" xml:id="module-security-acme"> 4 - <title>SSL/TLS Certificates with ACME</title> 5 - <para> 6 - NixOS supports automatic domain validation &amp; certificate 7 - retrieval and renewal using the ACME protocol. Any provider can be 8 - used, but by default NixOS uses Let’s Encrypt. The alternative ACME 9 - client 10 - <link xlink:href="https://go-acme.github.io/lego/">lego</link> is 11 - used under the hood. 12 - </para> 13 - <para> 14 - Automatic cert validation and configuration for Apache and Nginx 15 - virtual hosts is included in NixOS, however if you would like to 16 - generate a wildcard cert or you are not using a web server you will 17 - have to configure DNS based validation. 18 - </para> 19 - <section xml:id="module-security-acme-prerequisites"> 20 - <title>Prerequisites</title> 21 - <para> 22 - To use the ACME module, you must accept the provider’s terms of 23 - service by setting 24 - <xref linkend="opt-security.acme.acceptTerms" /> to 25 - <literal>true</literal>. The Let’s Encrypt ToS can be found 26 - <link xlink:href="https://letsencrypt.org/repository/">here</link>. 27 - </para> 28 - <para> 29 - You must also set an email address to be used when creating 30 - accounts with Let’s Encrypt. You can set this for all certs with 31 - <xref linkend="opt-security.acme.defaults.email" /> and/or on a 32 - per-cert basis with 33 - <xref linkend="opt-security.acme.certs._name_.email" />. This 34 - address is only used for registration and renewal reminders, and 35 - cannot be used to administer the certificates in any way. 36 - </para> 37 - <para> 38 - Alternatively, you can use a different ACME server by changing the 39 - <xref linkend="opt-security.acme.defaults.server" /> option to a 40 - provider of your choosing, or just change the server for one cert 41 - with <xref linkend="opt-security.acme.certs._name_.server" />. 42 - </para> 43 - <para> 44 - You will need an HTTP server or DNS server for verification. For 45 - HTTP, the server must have a webroot defined that can serve 46 - <filename>.well-known/acme-challenge</filename>. This directory 47 - must be writeable by the user that will run the ACME client. For 48 - DNS, you must set up credentials with your provider/server for use 49 - with lego. 50 - </para> 51 - </section> 52 - <section xml:id="module-security-acme-nginx"> 53 - <title>Using ACME certificates in Nginx</title> 54 - <para> 55 - NixOS supports fetching ACME certificates for you by setting 56 - <literal>enableACME = true;</literal> in a virtualHost config. We 57 - first create self-signed placeholder certificates in place of the 58 - real ACME certs. The placeholder certs are overwritten when the 59 - ACME certs arrive. For <literal>foo.example.com</literal> the 60 - config would look like this: 61 - </para> 62 - <programlisting> 63 - security.acme.acceptTerms = true; 64 - security.acme.defaults.email = &quot;admin+acme@example.com&quot;; 65 - services.nginx = { 66 - enable = true; 67 - virtualHosts = { 68 - &quot;foo.example.com&quot; = { 69 - forceSSL = true; 70 - enableACME = true; 71 - # All serverAliases will be added as extra domain names on the certificate. 72 - serverAliases = [ &quot;bar.example.com&quot; ]; 73 - locations.&quot;/&quot; = { 74 - root = &quot;/var/www&quot;; 75 - }; 76 - }; 77 - 78 - # We can also add a different vhost and reuse the same certificate 79 - # but we have to append extraDomainNames manually beforehand: 80 - # security.acme.certs.&quot;foo.example.com&quot;.extraDomainNames = [ &quot;baz.example.com&quot; ]; 81 - &quot;baz.example.com&quot; = { 82 - forceSSL = true; 83 - useACMEHost = &quot;foo.example.com&quot;; 84 - locations.&quot;/&quot; = { 85 - root = &quot;/var/www&quot;; 86 - }; 87 - }; 88 - }; 89 - } 90 - </programlisting> 91 - </section> 92 - <section xml:id="module-security-acme-httpd"> 93 - <title>Using ACME certificates in Apache/httpd</title> 94 - <para> 95 - Using ACME certificates with Apache virtual hosts is identical to 96 - using them with Nginx. The attribute names are all the same, just 97 - replace <quote>nginx</quote> with <quote>httpd</quote> where 98 - appropriate. 99 - </para> 100 - </section> 101 - <section xml:id="module-security-acme-configuring"> 102 - <title>Manual configuration of HTTP-01 validation</title> 103 - <para> 104 - First off you will need to set up a virtual host to serve the 105 - challenges. This example uses a vhost called 106 - <literal>certs.example.com</literal>, with the intent that you 107 - will generate certs for all your vhosts and redirect everyone to 108 - HTTPS. 109 - </para> 110 - <programlisting> 111 - security.acme.acceptTerms = true; 112 - security.acme.defaults.email = &quot;admin+acme@example.com&quot;; 113 - 114 - # /var/lib/acme/.challenges must be writable by the ACME user 115 - # and readable by the Nginx user. The easiest way to achieve 116 - # this is to add the Nginx user to the ACME group. 117 - users.users.nginx.extraGroups = [ &quot;acme&quot; ]; 118 - 119 - services.nginx = { 120 - enable = true; 121 - virtualHosts = { 122 - &quot;acmechallenge.example.com&quot; = { 123 - # Catchall vhost, will redirect users to HTTPS for all vhosts 124 - serverAliases = [ &quot;*.example.com&quot; ]; 125 - locations.&quot;/.well-known/acme-challenge&quot; = { 126 - root = &quot;/var/lib/acme/.challenges&quot;; 127 - }; 128 - locations.&quot;/&quot; = { 129 - return = &quot;301 https://$host$request_uri&quot;; 130 - }; 131 - }; 132 - }; 133 - } 134 - # Alternative config for Apache 135 - users.users.wwwrun.extraGroups = [ &quot;acme&quot; ]; 136 - services.httpd = { 137 - enable = true; 138 - virtualHosts = { 139 - &quot;acmechallenge.example.com&quot; = { 140 - # Catchall vhost, will redirect users to HTTPS for all vhosts 141 - serverAliases = [ &quot;*.example.com&quot; ]; 142 - # /var/lib/acme/.challenges must be writable by the ACME user and readable by the Apache user. 143 - # By default, this is the case. 144 - documentRoot = &quot;/var/lib/acme/.challenges&quot;; 145 - extraConfig = '' 146 - RewriteEngine On 147 - RewriteCond %{HTTPS} off 148 - RewriteCond %{REQUEST_URI} !^/\.well-known/acme-challenge [NC] 149 - RewriteRule (.*) https://%{HTTP_HOST}%{REQUEST_URI} [R=301] 150 - ''; 151 - }; 152 - }; 153 - } 154 - </programlisting> 155 - <para> 156 - Now you need to configure ACME to generate a certificate. 157 - </para> 158 - <programlisting> 159 - security.acme.certs.&quot;foo.example.com&quot; = { 160 - webroot = &quot;/var/lib/acme/.challenges&quot;; 161 - email = &quot;foo@example.com&quot;; 162 - # Ensure that the web server you use can read the generated certs 163 - # Take a look at the group option for the web server you choose. 164 - group = &quot;nginx&quot;; 165 - # Since we have a wildcard vhost to handle port 80, 166 - # we can generate certs for anything! 167 - # Just make sure your DNS resolves them. 168 - extraDomainNames = [ &quot;mail.example.com&quot; ]; 169 - }; 170 - </programlisting> 171 - <para> 172 - The private key <filename>key.pem</filename> and certificate 173 - <filename>fullchain.pem</filename> will be put into 174 - <filename>/var/lib/acme/foo.example.com</filename>. 175 - </para> 176 - <para> 177 - Refer to <xref linkend="ch-options" /> for all available 178 - configuration options for the 179 - <link linkend="opt-security.acme.certs">security.acme</link> 180 - module. 181 - </para> 182 - </section> 183 - <section xml:id="module-security-acme-config-dns"> 184 - <title>Configuring ACME for DNS validation</title> 185 - <para> 186 - This is useful if you want to generate a wildcard certificate, 187 - since ACME servers will only hand out wildcard certs over DNS 188 - validation. There are a number of supported DNS providers and 189 - servers you can utilise, see the 190 - <link xlink:href="https://go-acme.github.io/lego/dns/">lego 191 - docs</link> for provider/server specific configuration values. For 192 - the sake of these docs, we will provide a fully self-hosted 193 - example using bind. 194 - </para> 195 - <programlisting> 196 - services.bind = { 197 - enable = true; 198 - extraConfig = '' 199 - include &quot;/var/lib/secrets/dnskeys.conf&quot;; 200 - ''; 201 - zones = [ 202 - rec { 203 - name = &quot;example.com&quot;; 204 - file = &quot;/var/db/bind/${name}&quot;; 205 - master = true; 206 - extraConfig = &quot;allow-update { key rfc2136key.example.com.; };&quot;; 207 - } 208 - ]; 209 - } 210 - 211 - # Now we can configure ACME 212 - security.acme.acceptTerms = true; 213 - security.acme.defaults.email = &quot;admin+acme@example.com&quot;; 214 - security.acme.certs.&quot;example.com&quot; = { 215 - domain = &quot;*.example.com&quot;; 216 - dnsProvider = &quot;rfc2136&quot;; 217 - credentialsFile = &quot;/var/lib/secrets/certs.secret&quot;; 218 - # We don't need to wait for propagation since this is a local DNS server 219 - dnsPropagationCheck = false; 220 - }; 221 - </programlisting> 222 - <para> 223 - The <filename>dnskeys.conf</filename> and 224 - <filename>certs.secret</filename> must be kept secure and thus you 225 - should not keep their contents in your Nix config. Instead, 226 - generate them one time with a systemd service: 227 - </para> 228 - <programlisting> 229 - systemd.services.dns-rfc2136-conf = { 230 - requiredBy = [&quot;acme-example.com.service&quot; &quot;bind.service&quot;]; 231 - before = [&quot;acme-example.com.service&quot; &quot;bind.service&quot;]; 232 - unitConfig = { 233 - ConditionPathExists = &quot;!/var/lib/secrets/dnskeys.conf&quot;; 234 - }; 235 - serviceConfig = { 236 - Type = &quot;oneshot&quot;; 237 - UMask = 0077; 238 - }; 239 - path = [ pkgs.bind ]; 240 - script = '' 241 - mkdir -p /var/lib/secrets 242 - chmod 755 /var/lib/secrets 243 - tsig-keygen rfc2136key.example.com &gt; /var/lib/secrets/dnskeys.conf 244 - chown named:root /var/lib/secrets/dnskeys.conf 245 - chmod 400 /var/lib/secrets/dnskeys.conf 246 - 247 - # extract secret value from the dnskeys.conf 248 - while read x y; do if [ &quot;$x&quot; = &quot;secret&quot; ]; then secret=&quot;''${y:1:''${#y}-3}&quot;; fi; done &lt; /var/lib/secrets/dnskeys.conf 249 - 250 - cat &gt; /var/lib/secrets/certs.secret &lt;&lt; EOF 251 - RFC2136_NAMESERVER='127.0.0.1:53' 252 - RFC2136_TSIG_ALGORITHM='hmac-sha256.' 253 - RFC2136_TSIG_KEY='rfc2136key.example.com' 254 - RFC2136_TSIG_SECRET='$secret' 255 - EOF 256 - chmod 400 /var/lib/secrets/certs.secret 257 - ''; 258 - }; 259 - </programlisting> 260 - <para> 261 - Now you’re all set to generate certs! You should monitor the first 262 - invocation by running 263 - <literal>systemctl start acme-example.com.service &amp; journalctl -fu acme-example.com.service</literal> 264 - and watching its log output. 265 - </para> 266 - </section> 267 - <section xml:id="module-security-acme-config-dns-with-vhosts"> 268 - <title>Using DNS validation with web server virtual hosts</title> 269 - <para> 270 - It is possible to use DNS-01 validation with all certificates, 271 - including those automatically configured via the Nginx/Apache 272 - <link linkend="opt-services.nginx.virtualHosts._name_.enableACME"><literal>enableACME</literal></link> 273 - option. This configuration pattern is fully supported and part of 274 - the module’s test suite for Nginx + Apache. 275 - </para> 276 - <para> 277 - You must follow the guide above on configuring DNS-01 validation 278 - first, however instead of setting the options for one certificate 279 - (e.g. 280 - <xref linkend="opt-security.acme.certs._name_.dnsProvider" />) you 281 - will set them as defaults (e.g. 282 - <xref linkend="opt-security.acme.defaults.dnsProvider" />). 283 - </para> 284 - <programlisting> 285 - # Configure ACME appropriately 286 - security.acme.acceptTerms = true; 287 - security.acme.defaults.email = &quot;admin+acme@example.com&quot;; 288 - security.acme.defaults = { 289 - dnsProvider = &quot;rfc2136&quot;; 290 - credentialsFile = &quot;/var/lib/secrets/certs.secret&quot;; 291 - # We don't need to wait for propagation since this is a local DNS server 292 - dnsPropagationCheck = false; 293 - }; 294 - 295 - # For each virtual host you would like to use DNS-01 validation with, 296 - # set acmeRoot = null 297 - services.nginx = { 298 - enable = true; 299 - virtualHosts = { 300 - &quot;foo.example.com&quot; = { 301 - enableACME = true; 302 - acmeRoot = null; 303 - }; 304 - }; 305 - } 306 - </programlisting> 307 - <para> 308 - And that’s it! Next time your configuration is rebuilt, or when 309 - you add a new virtualHost, it will be DNS-01 validated. 310 - </para> 311 - </section> 312 - <section xml:id="module-security-acme-root-owned"> 313 - <title>Using ACME with services demanding root owned 314 - certificates</title> 315 - <para> 316 - Some services refuse to start if the configured certificate files 317 - are not owned by root. PostgreSQL and OpenSMTPD are examples of 318 - these. There is no way to change the user the ACME module uses (it 319 - will always be <literal>acme</literal>), however you can use 320 - systemd’s <literal>LoadCredential</literal> feature to resolve 321 - this elegantly. Below is an example configuration for OpenSMTPD, 322 - but this pattern can be applied to any service. 323 - </para> 324 - <programlisting> 325 - # Configure ACME however you like (DNS or HTTP validation), adding 326 - # the following configuration for the relevant certificate. 327 - # Note: You cannot use `systemctl reload` here as that would mean 328 - # the LoadCredential configuration below would be skipped and 329 - # the service would continue to use old certificates. 330 - security.acme.certs.&quot;mail.example.com&quot;.postRun = '' 331 - systemctl restart opensmtpd 332 - ''; 333 - 334 - # Now you must augment OpenSMTPD's systemd service to load 335 - # the certificate files. 336 - systemd.services.opensmtpd.requires = [&quot;acme-finished-mail.example.com.target&quot;]; 337 - systemd.services.opensmtpd.serviceConfig.LoadCredential = let 338 - certDir = config.security.acme.certs.&quot;mail.example.com&quot;.directory; 339 - in [ 340 - &quot;cert.pem:${certDir}/cert.pem&quot; 341 - &quot;key.pem:${certDir}/key.pem&quot; 342 - ]; 343 - 344 - # Finally, configure OpenSMTPD to use these certs. 345 - services.opensmtpd = let 346 - credsDir = &quot;/run/credentials/opensmtpd.service&quot;; 347 - in { 348 - enable = true; 349 - setSendmail = false; 350 - serverConfiguration = '' 351 - pki mail.example.com cert &quot;${credsDir}/cert.pem&quot; 352 - pki mail.example.com key &quot;${credsDir}/key.pem&quot; 353 - listen on localhost tls pki mail.example.com 354 - action act1 relay host smtp://127.0.0.1:10027 355 - match for local action act1 356 - ''; 357 - }; 358 - </programlisting> 359 - </section> 360 - <section xml:id="module-security-acme-regenerate"> 361 - <title>Regenerating certificates</title> 362 - <para> 363 - Should you need to regenerate a particular certificate in a hurry, 364 - such as when a vulnerability is found in Let’s Encrypt, there is 365 - now a convenient mechanism for doing so. Running 366 - <literal>systemctl clean --what=state acme-example.com.service</literal> 367 - will remove all certificate files and the account data for the 368 - given domain, allowing you to then 369 - <literal>systemctl start acme-example.com.service</literal> to 370 - generate fresh ones. 371 - </para> 372 - </section> 373 - <section xml:id="module-security-acme-fix-jws"> 374 - <title>Fixing JWS Verification error</title> 375 - <para> 376 - It is possible that your account credentials file may become 377 - corrupt and need to be regenerated. In this scenario lego will 378 - produce the error <literal>JWS verification error</literal>. The 379 - solution is to simply delete the associated accounts file and 380 - re-run the affected service(s). 381 - </para> 382 - <programlisting> 383 - # Find the accounts folder for the certificate 384 - systemctl cat acme-example.com.service | grep -Po 'accounts/[^:]*' 385 - export accountdir=&quot;$(!!)&quot; 386 - # Move this folder to some place else 387 - mv /var/lib/acme/.lego/$accountdir{,.bak} 388 - # Recreate the folder using systemd-tmpfiles 389 - systemd-tmpfiles --create 390 - # Get a new account and reissue certificates 391 - # Note: Do this for all certs that share the same account email address 392 - systemctl start acme-example.com.service 393 - </programlisting> 394 - </section> 395 - </chapter>
+1 -1
nixos/modules/services/backup/borgbackup.nix
··· 226 226 227 227 in { 228 228 meta.maintainers = with maintainers; [ dotlambda ]; 229 - meta.doc = ./borgbackup.xml; 229 + meta.doc = ./borgbackup.md; 230 230 231 231 ###### interface 232 232
-215
nixos/modules/services/backup/borgbackup.xml
··· 1 - <!-- Do not edit this file directly, edit its companion .md instead 2 - and regenerate this file using nixos/doc/manual/md-to-db.sh --> 3 - <chapter xmlns="http://docbook.org/ns/docbook" xmlns:xlink="http://www.w3.org/1999/xlink" xml:id="module-borgbase"> 4 - <title>BorgBackup</title> 5 - <para> 6 - <emphasis>Source:</emphasis> 7 - <filename>modules/services/backup/borgbackup.nix</filename> 8 - </para> 9 - <para> 10 - <emphasis>Upstream documentation:</emphasis> 11 - <link xlink:href="https://borgbackup.readthedocs.io/">https://borgbackup.readthedocs.io/</link> 12 - </para> 13 - <para> 14 - <link xlink:href="https://www.borgbackup.org/">BorgBackup</link> 15 - (short: Borg) is a deduplicating backup program. Optionally, it 16 - supports compression and authenticated encryption. 17 - </para> 18 - <para> 19 - The main goal of Borg is to provide an efficient and secure way to 20 - backup data. The data deduplication technique used makes Borg 21 - suitable for daily backups since only changes are stored. The 22 - authenticated encryption technique makes it suitable for backups to 23 - not fully trusted targets. 24 - </para> 25 - <section xml:id="module-services-backup-borgbackup-configuring"> 26 - <title>Configuring</title> 27 - <para> 28 - A complete list of options for the Borgbase module may be found 29 - <link linkend="opt-services.borgbackup.jobs">here</link>. 30 - </para> 31 - </section> 32 - <section xml:id="opt-services-backup-borgbackup-local-directory"> 33 - <title>Basic usage for a local backup</title> 34 - <para> 35 - A very basic configuration for backing up to a locally accessible 36 - directory is: 37 - </para> 38 - <programlisting> 39 - { 40 - opt.services.borgbackup.jobs = { 41 - { rootBackup = { 42 - paths = &quot;/&quot;; 43 - exclude = [ &quot;/nix&quot; &quot;/path/to/local/repo&quot; ]; 44 - repo = &quot;/path/to/local/repo&quot;; 45 - doInit = true; 46 - encryption = { 47 - mode = &quot;repokey&quot;; 48 - passphrase = &quot;secret&quot;; 49 - }; 50 - compression = &quot;auto,lzma&quot;; 51 - startAt = &quot;weekly&quot;; 52 - }; 53 - } 54 - }; 55 - } 56 - </programlisting> 57 - <warning> 58 - <para> 59 - If you do not want the passphrase to be stored in the 60 - world-readable Nix store, use passCommand. You find an example 61 - below. 62 - </para> 63 - </warning> 64 - </section> 65 - <section xml:id="opt-services-backup-create-server"> 66 - <title>Create a borg backup server</title> 67 - <para> 68 - You should use a different SSH key for each repository you write 69 - to, because the specified keys are restricted to running borg 70 - serve and can only access this single repository. You need the 71 - output of the generate pub file. 72 - </para> 73 - <programlisting> 74 - # sudo ssh-keygen -N '' -t ed25519 -f /run/keys/id_ed25519_my_borg_repo 75 - # cat /run/keys/id_ed25519_my_borg_repo 76 - ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAID78zmOyA+5uPG4Ot0hfAy+sLDPU1L4AiIoRYEIVbbQ/ root@nixos 77 - </programlisting> 78 - <para> 79 - Add the following snippet to your NixOS configuration: 80 - </para> 81 - <programlisting> 82 - { 83 - services.borgbackup.repos = { 84 - my_borg_repo = { 85 - authorizedKeys = [ 86 - &quot;ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAID78zmOyA+5uPG4Ot0hfAy+sLDPU1L4AiIoRYEIVbbQ/ root@nixos&quot; 87 - ] ; 88 - path = &quot;/var/lib/my_borg_repo&quot; ; 89 - }; 90 - }; 91 - } 92 - </programlisting> 93 - </section> 94 - <section xml:id="opt-services-backup-borgbackup-remote-server"> 95 - <title>Backup to the borg repository server</title> 96 - <para> 97 - The following NixOS snippet creates an hourly backup to the 98 - service (on the host nixos) as created in the section above. We 99 - assume that you have stored a secret passphrasse in the file 100 - <filename>/run/keys/borgbackup_passphrase</filename>, which should 101 - be only accessible by root 102 - </para> 103 - <programlisting> 104 - { 105 - services.borgbackup.jobs = { 106 - backupToLocalServer = { 107 - paths = [ &quot;/etc/nixos&quot; ]; 108 - doInit = true; 109 - repo = &quot;borg@nixos:.&quot; ; 110 - encryption = { 111 - mode = &quot;repokey-blake2&quot;; 112 - passCommand = &quot;cat /run/keys/borgbackup_passphrase&quot;; 113 - }; 114 - environment = { BORG_RSH = &quot;ssh -i /run/keys/id_ed25519_my_borg_repo&quot;; }; 115 - compression = &quot;auto,lzma&quot;; 116 - startAt = &quot;hourly&quot;; 117 - }; 118 - }; 119 - }; 120 - </programlisting> 121 - <para> 122 - The following few commands (run as root) let you test your backup. 123 - </para> 124 - <programlisting> 125 - &gt; nixos-rebuild switch 126 - ...restarting the following units: polkit.service 127 - &gt; systemctl restart borgbackup-job-backupToLocalServer 128 - &gt; sleep 10 129 - &gt; systemctl restart borgbackup-job-backupToLocalServer 130 - &gt; export BORG_PASSPHRASE=topSecrect 131 - &gt; borg list --rsh='ssh -i /run/keys/id_ed25519_my_borg_repo' borg@nixos:. 132 - nixos-backupToLocalServer-2020-03-30T21:46:17 Mon, 2020-03-30 21:46:19 [84feb97710954931ca384182f5f3cb90665f35cef214760abd7350fb064786ac] 133 - nixos-backupToLocalServer-2020-03-30T21:46:30 Mon, 2020-03-30 21:46:32 [e77321694ecd160ca2228611747c6ad1be177d6e0d894538898de7a2621b6e68] 134 - </programlisting> 135 - </section> 136 - <section xml:id="opt-services-backup-borgbackup-borgbase"> 137 - <title>Backup to a hosting service</title> 138 - <para> 139 - Several companies offer 140 - <link xlink:href="https://www.borgbackup.org/support/commercial.html">(paid) 141 - hosting services</link> for Borg repositories. 142 - </para> 143 - <para> 144 - To backup your home directory to borgbase you have to: 145 - </para> 146 - <itemizedlist> 147 - <listitem> 148 - <para> 149 - Generate a SSH key without a password, to access the remote 150 - server. E.g. 151 - </para> 152 - <programlisting> 153 - sudo ssh-keygen -N '' -t ed25519 -f /run/keys/id_ed25519_borgbase 154 - </programlisting> 155 - </listitem> 156 - <listitem> 157 - <para> 158 - Create the repository on the server by following the 159 - instructions for your hosting server. 160 - </para> 161 - </listitem> 162 - <listitem> 163 - <para> 164 - Initialize the repository on the server. Eg. 165 - </para> 166 - <programlisting> 167 - sudo borg init --encryption=repokey-blake2 \ 168 - -rsh &quot;ssh -i /run/keys/id_ed25519_borgbase&quot; \ 169 - zzz2aaaaa@zzz2aaaaa.repo.borgbase.com:repo 170 - </programlisting> 171 - </listitem> 172 - <listitem> 173 - <para> 174 - Add it to your NixOS configuration, e.g. 175 - </para> 176 - <programlisting> 177 - { 178 - services.borgbackup.jobs = { 179 - my_Remote_Backup = { 180 - paths = [ &quot;/&quot; ]; 181 - exclude = [ &quot;/nix&quot; &quot;'**/.cache'&quot; ]; 182 - repo = &quot;zzz2aaaaa@zzz2aaaaa.repo.borgbase.com:repo&quot;; 183 - encryption = { 184 - mode = &quot;repokey-blake2&quot;; 185 - passCommand = &quot;cat /run/keys/borgbackup_passphrase&quot;; 186 - }; 187 - environment = { BORG_RSH = &quot;ssh -i /run/keys/id_ed25519_borgbase&quot;; }; 188 - compression = &quot;auto,lzma&quot;; 189 - startAt = &quot;daily&quot;; 190 - }; 191 - }; 192 - }} 193 - </programlisting> 194 - </listitem> 195 - </itemizedlist> 196 - </section> 197 - <section xml:id="opt-services-backup-borgbackup-vorta"> 198 - <title>Vorta backup client for the desktop</title> 199 - <para> 200 - Vorta is a backup client for macOS and Linux desktops. It 201 - integrates the mighty BorgBackup with your desktop environment to 202 - protect your data from disk failure, ransomware and theft. 203 - </para> 204 - <para> 205 - It can be installed in NixOS e.g. by adding 206 - <literal>pkgs.vorta</literal> to 207 - <xref linkend="opt-environment.systemPackages" />. 208 - </para> 209 - <para> 210 - Details about using Vorta can be found under 211 - <link xlink:href="https://vorta.borgbase.com/usage">https://vorta.borgbase.com</link> 212 - . 213 - </para> 214 - </section> 215 - </chapter>
+1 -1
nixos/modules/services/databases/foundationdb.nix
··· 424 424 }; 425 425 }; 426 426 427 - meta.doc = ./foundationdb.xml; 427 + meta.doc = ./foundationdb.md; 428 428 meta.maintainers = with lib.maintainers; [ thoughtpolice ]; 429 429 }
-425
nixos/modules/services/databases/foundationdb.xml
··· 1 - <!-- Do not edit this file directly, edit its companion .md instead 2 - and regenerate this file using nixos/doc/manual/md-to-db.sh --> 3 - <chapter xmlns="http://docbook.org/ns/docbook" xmlns:xlink="http://www.w3.org/1999/xlink" xml:id="module-services-foundationdb"> 4 - <title>FoundationDB</title> 5 - <para> 6 - <emphasis>Source:</emphasis> 7 - <filename>modules/services/databases/foundationdb.nix</filename> 8 - </para> 9 - <para> 10 - <emphasis>Upstream documentation:</emphasis> 11 - <link xlink:href="https://apple.github.io/foundationdb/">https://apple.github.io/foundationdb/</link> 12 - </para> 13 - <para> 14 - <emphasis>Maintainer:</emphasis> Austin Seipp 15 - </para> 16 - <para> 17 - <emphasis>Available version(s):</emphasis> 5.1.x, 5.2.x, 6.0.x 18 - </para> 19 - <para> 20 - FoundationDB (or <quote>FDB</quote>) is an open source, distributed, 21 - transactional key-value store. 22 - </para> 23 - <section xml:id="module-services-foundationdb-configuring"> 24 - <title>Configuring and basic setup</title> 25 - <para> 26 - To enable FoundationDB, add the following to your 27 - <filename>configuration.nix</filename>: 28 - </para> 29 - <programlisting> 30 - services.foundationdb.enable = true; 31 - services.foundationdb.package = pkgs.foundationdb52; # FoundationDB 5.2.x 32 - </programlisting> 33 - <para> 34 - The <option>services.foundationdb.package</option> option is 35 - required, and must always be specified. Due to the fact 36 - FoundationDB network protocols and on-disk storage formats may 37 - change between (major) versions, and upgrades must be explicitly 38 - handled by the user, you must always manually specify this 39 - yourself so that the NixOS module will use the proper version. 40 - Note that minor, bugfix releases are always compatible. 41 - </para> 42 - <para> 43 - After running <command>nixos-rebuild</command>, you can verify 44 - whether FoundationDB is running by executing 45 - <command>fdbcli</command> (which is added to 46 - <option>environment.systemPackages</option>): 47 - </para> 48 - <programlisting> 49 - $ sudo -u foundationdb fdbcli 50 - Using cluster file `/etc/foundationdb/fdb.cluster'. 51 - 52 - The database is available. 53 - 54 - Welcome to the fdbcli. For help, type `help'. 55 - fdb&gt; status 56 - 57 - Using cluster file `/etc/foundationdb/fdb.cluster'. 58 - 59 - Configuration: 60 - Redundancy mode - single 61 - Storage engine - memory 62 - Coordinators - 1 63 - 64 - Cluster: 65 - FoundationDB processes - 1 66 - Machines - 1 67 - Memory availability - 5.4 GB per process on machine with least available 68 - Fault Tolerance - 0 machines 69 - Server time - 04/20/18 15:21:14 70 - 71 - ... 72 - 73 - fdb&gt; 74 - </programlisting> 75 - <para> 76 - You can also write programs using the available client libraries. 77 - For example, the following Python program can be run in order to 78 - grab the cluster status, as a quick example. (This example uses 79 - <command>nix-shell</command> shebang support to automatically 80 - supply the necessary Python modules). 81 - </para> 82 - <programlisting> 83 - a@link&gt; cat fdb-status.py 84 - #! /usr/bin/env nix-shell 85 - #! nix-shell -i python -p python pythonPackages.foundationdb52 86 - 87 - import fdb 88 - import json 89 - 90 - def main(): 91 - fdb.api_version(520) 92 - db = fdb.open() 93 - 94 - @fdb.transactional 95 - def get_status(tr): 96 - return str(tr['\xff\xff/status/json']) 97 - 98 - obj = json.loads(get_status(db)) 99 - print('FoundationDB available: %s' % obj['client']['database_status']['available']) 100 - 101 - if __name__ == &quot;__main__&quot;: 102 - main() 103 - a@link&gt; chmod +x fdb-status.py 104 - a@link&gt; ./fdb-status.py 105 - FoundationDB available: True 106 - a@link&gt; 107 - </programlisting> 108 - <para> 109 - FoundationDB is run under the <command>foundationdb</command> user 110 - and group by default, but this may be changed in the NixOS 111 - configuration. The systemd unit 112 - <command>foundationdb.service</command> controls the 113 - <command>fdbmonitor</command> process. 114 - </para> 115 - <para> 116 - By default, the NixOS module for FoundationDB creates a single 117 - SSD-storage based database for development and basic usage. This 118 - storage engine is designed for SSDs and will perform poorly on 119 - HDDs; however it can handle far more data than the alternative 120 - <quote>memory</quote> engine and is a better default choice for 121 - most deployments. (Note that you can change the storage backend 122 - on-the-fly for a given FoundationDB cluster using 123 - <command>fdbcli</command>.) 124 - </para> 125 - <para> 126 - Furthermore, only 1 server process and 1 backup agent are started 127 - in the default configuration. See below for more on scaling to 128 - increase this. 129 - </para> 130 - <para> 131 - FoundationDB stores all data for all server processes under 132 - <filename>/var/lib/foundationdb</filename>. You can override this 133 - using <option>services.foundationdb.dataDir</option>, e.g. 134 - </para> 135 - <programlisting> 136 - services.foundationdb.dataDir = &quot;/data/fdb&quot;; 137 - </programlisting> 138 - <para> 139 - Similarly, logs are stored under 140 - <filename>/var/log/foundationdb</filename> by default, and there 141 - is a corresponding <option>services.foundationdb.logDir</option> 142 - as well. 143 - </para> 144 - </section> 145 - <section xml:id="module-services-foundationdb-scaling"> 146 - <title>Scaling processes and backup agents</title> 147 - <para> 148 - Scaling the number of server processes is quite easy; simply 149 - specify <option>services.foundationdb.serverProcesses</option> to 150 - be the number of FoundationDB worker processes that should be 151 - started on the machine. 152 - </para> 153 - <para> 154 - FoundationDB worker processes typically require 4GB of RAM 155 - per-process at minimum for good performance, so this option is set 156 - to 1 by default since the maximum amount of RAM is unknown. You’re 157 - advised to abide by this restriction, so pick a number of 158 - processes so that each has 4GB or more. 159 - </para> 160 - <para> 161 - A similar option exists in order to scale backup agent processes, 162 - <option>services.foundationdb.backupProcesses</option>. Backup 163 - agents are not as performance/RAM sensitive, so feel free to 164 - experiment with the number of available backup processes. 165 - </para> 166 - </section> 167 - <section xml:id="module-services-foundationdb-clustering"> 168 - <title>Clustering</title> 169 - <para> 170 - FoundationDB on NixOS works similarly to other Linux systems, so 171 - this section will be brief. Please refer to the full FoundationDB 172 - documentation for more on clustering. 173 - </para> 174 - <para> 175 - FoundationDB organizes clusters using a set of 176 - <emphasis>coordinators</emphasis>, which are just 177 - specially-designated worker processes. By default, every 178 - installation of FoundationDB on NixOS will start as its own 179 - individual cluster, with a single coordinator: the first worker 180 - process on <command>localhost</command>. 181 - </para> 182 - <para> 183 - Coordinators are specified globally using the 184 - <command>/etc/foundationdb/fdb.cluster</command> file, which all 185 - servers and client applications will use to find and join 186 - coordinators. Note that this file <emphasis>can not</emphasis> be 187 - managed by NixOS so easily: FoundationDB is designed so that it 188 - will rewrite the file at runtime for all clients and nodes when 189 - cluster coordinators change, with clients transparently handling 190 - this without intervention. It is fundamentally a mutable file, and 191 - you should not try to manage it in any way in NixOS. 192 - </para> 193 - <para> 194 - When dealing with a cluster, there are two main things you want to 195 - do: 196 - </para> 197 - <itemizedlist spacing="compact"> 198 - <listitem> 199 - <para> 200 - Add a node to the cluster for storage/compute. 201 - </para> 202 - </listitem> 203 - <listitem> 204 - <para> 205 - Promote an ordinary worker to a coordinator. 206 - </para> 207 - </listitem> 208 - </itemizedlist> 209 - <para> 210 - A node must already be a member of the cluster in order to 211 - properly be promoted to a coordinator, so you must always add it 212 - first if you wish to promote it. 213 - </para> 214 - <para> 215 - To add a machine to a FoundationDB cluster: 216 - </para> 217 - <itemizedlist spacing="compact"> 218 - <listitem> 219 - <para> 220 - Choose one of the servers to start as the initial coordinator. 221 - </para> 222 - </listitem> 223 - <listitem> 224 - <para> 225 - Copy the <command>/etc/foundationdb/fdb.cluster</command> file 226 - from this server to all the other servers. Restart 227 - FoundationDB on all of these other servers, so they join the 228 - cluster. 229 - </para> 230 - </listitem> 231 - <listitem> 232 - <para> 233 - All of these servers are now connected and working together in 234 - the cluster, under the chosen coordinator. 235 - </para> 236 - </listitem> 237 - </itemizedlist> 238 - <para> 239 - At this point, you can add as many nodes as you want by just 240 - repeating the above steps. By default there will still be a single 241 - coordinator: you can use <command>fdbcli</command> to change this 242 - and add new coordinators. 243 - </para> 244 - <para> 245 - As a convenience, FoundationDB can automatically assign 246 - coordinators based on the redundancy mode you wish to achieve for 247 - the cluster. Once all the nodes have been joined, simply set the 248 - replication policy, and then issue the 249 - <command>coordinators auto</command> command 250 - </para> 251 - <para> 252 - For example, assuming we have 3 nodes available, we can enable 253 - double redundancy mode, then auto-select coordinators. For double 254 - redundancy, 3 coordinators is ideal: therefore FoundationDB will 255 - make <emphasis>every</emphasis> node a coordinator automatically: 256 - </para> 257 - <programlisting> 258 - fdbcli&gt; configure double ssd 259 - fdbcli&gt; coordinators auto 260 - </programlisting> 261 - <para> 262 - This will transparently update all the servers within seconds, and 263 - appropriately rewrite the <command>fdb.cluster</command> file, as 264 - well as informing all client processes to do the same. 265 - </para> 266 - </section> 267 - <section xml:id="module-services-foundationdb-connectivity"> 268 - <title>Client connectivity</title> 269 - <para> 270 - By default, all clients must use the current 271 - <command>fdb.cluster</command> file to access a given FoundationDB 272 - cluster. This file is located by default in 273 - <command>/etc/foundationdb/fdb.cluster</command> on all machines 274 - with the FoundationDB service enabled, so you may copy the active 275 - one from your cluster to a new node in order to connect, if it is 276 - not part of the cluster. 277 - </para> 278 - </section> 279 - <section xml:id="module-services-foundationdb-authorization"> 280 - <title>Client authorization and TLS</title> 281 - <para> 282 - By default, any user who can connect to a FoundationDB process 283 - with the correct cluster configuration can access anything. 284 - FoundationDB uses a pluggable design to transport security, and 285 - out of the box it supports a LibreSSL-based plugin for TLS 286 - support. This plugin not only does in-flight encryption, but also 287 - performs client authorization based on the given endpoint’s 288 - certificate chain. For example, a FoundationDB server may be 289 - configured to only accept client connections over TLS, where the 290 - client TLS certificate is from organization <emphasis>Acme 291 - Co</emphasis> in the <emphasis>Research and Development</emphasis> 292 - unit. 293 - </para> 294 - <para> 295 - Configuring TLS with FoundationDB is done using the 296 - <option>services.foundationdb.tls</option> options in order to 297 - control the peer verification string, as well as the certificate 298 - and its private key. 299 - </para> 300 - <para> 301 - Note that the certificate and its private key must be accessible 302 - to the FoundationDB user account that the server runs under. These 303 - files are also NOT managed by NixOS, as putting them into the 304 - store may reveal private information. 305 - </para> 306 - <para> 307 - After you have a key and certificate file in place, it is not 308 - enough to simply set the NixOS module options – you must also 309 - configure the <command>fdb.cluster</command> file to specify that 310 - a given set of coordinators use TLS. This is as simple as adding 311 - the suffix <command>:tls</command> to your cluster coordinator 312 - configuration, after the port number. For example, assuming you 313 - have a coordinator on localhost with the default configuration, 314 - simply specifying: 315 - </para> 316 - <programlisting> 317 - XXXXXX:XXXXXX@127.0.0.1:4500:tls 318 - </programlisting> 319 - <para> 320 - will configure all clients and server processes to use TLS from 321 - now on. 322 - </para> 323 - </section> 324 - <section xml:id="module-services-foundationdb-disaster-recovery"> 325 - <title>Backups and Disaster Recovery</title> 326 - <para> 327 - The usual rules for doing FoundationDB backups apply on NixOS as 328 - written in the FoundationDB manual. However, one important 329 - difference is the security profile for NixOS: by default, the 330 - <command>foundationdb</command> systemd unit uses <emphasis>Linux 331 - namespaces</emphasis> to restrict write access to the system, 332 - except for the log directory, data directory, and the 333 - <command>/etc/foundationdb/</command> directory. This is enforced 334 - by default and cannot be disabled. 335 - </para> 336 - <para> 337 - However, a side effect of this is that the 338 - <command>fdbbackup</command> command doesn’t work properly for 339 - local filesystem backups: FoundationDB uses a server process 340 - alongside the database processes to perform backups and copy the 341 - backups to the filesystem. As a result, this process is put under 342 - the restricted namespaces above: the backup process can only write 343 - to a limited number of paths. 344 - </para> 345 - <para> 346 - In order to allow flexible backup locations on local disks, the 347 - FoundationDB NixOS module supports a 348 - <option>services.foundationdb.extraReadWritePaths</option> option. 349 - This option takes a list of paths, and adds them to the systemd 350 - unit, allowing the processes inside the service to write (and 351 - read) the specified directories. 352 - </para> 353 - <para> 354 - For example, to create backups in 355 - <command>/opt/fdb-backups</command>, first set up the paths in the 356 - module options: 357 - </para> 358 - <programlisting> 359 - services.foundationdb.extraReadWritePaths = [ &quot;/opt/fdb-backups&quot; ]; 360 - </programlisting> 361 - <para> 362 - Restart the FoundationDB service, and it will now be able to write 363 - to this directory (even if it does not yet exist.) Note: this path 364 - <emphasis>must</emphasis> exist before restarting the unit. 365 - Otherwise, systemd will not include it in the private FoundationDB 366 - namespace (and it will not add it dynamically at runtime). 367 - </para> 368 - <para> 369 - You can now perform a backup: 370 - </para> 371 - <programlisting> 372 - $ sudo -u foundationdb fdbbackup start -t default -d file:///opt/fdb-backups 373 - $ sudo -u foundationdb fdbbackup status -t default 374 - </programlisting> 375 - </section> 376 - <section xml:id="module-services-foundationdb-limitations"> 377 - <title>Known limitations</title> 378 - <para> 379 - The FoundationDB setup for NixOS should currently be considered 380 - beta. FoundationDB is not new software, but the NixOS compilation 381 - and integration has only undergone fairly basic testing of all the 382 - available functionality. 383 - </para> 384 - <itemizedlist spacing="compact"> 385 - <listitem> 386 - <para> 387 - There is no way to specify individual parameters for 388 - individual <command>fdbserver</command> processes. Currently, 389 - all server processes inherit all the global 390 - <command>fdbmonitor</command> settings. 391 - </para> 392 - </listitem> 393 - <listitem> 394 - <para> 395 - Ruby bindings are not currently installed. 396 - </para> 397 - </listitem> 398 - <listitem> 399 - <para> 400 - Go bindings are not currently installed. 401 - </para> 402 - </listitem> 403 - </itemizedlist> 404 - </section> 405 - <section xml:id="module-services-foundationdb-options"> 406 - <title>Options</title> 407 - <para> 408 - NixOS’s FoundationDB module allows you to configure all of the 409 - most relevant configuration options for 410 - <command>fdbmonitor</command>, matching it quite closely. A 411 - complete list of options for the FoundationDB module may be found 412 - <link linkend="opt-services.foundationdb.enable">here</link>. You 413 - should also read the FoundationDB documentation as well. 414 - </para> 415 - </section> 416 - <section xml:id="module-services-foundationdb-full-docs"> 417 - <title>Full documentation</title> 418 - <para> 419 - FoundationDB is a complex piece of software, and requires careful 420 - administration to properly use. Full documentation for 421 - administration can be found here: 422 - <link xlink:href="https://apple.github.io/foundationdb/">https://apple.github.io/foundationdb/</link>. 423 - </para> 424 - </section> 425 - </chapter>
+1 -1
nixos/modules/services/databases/postgresql.nix
··· 585 585 586 586 }; 587 587 588 - meta.doc = ./postgresql.xml; 588 + meta.doc = ./postgresql.md; 589 589 meta.maintainers = with lib.maintainers; [ thoughtpolice danbst ]; 590 590 }
-250
nixos/modules/services/databases/postgresql.xml
··· 1 - <!-- Do not edit this file directly, edit its companion .md instead 2 - and regenerate this file using nixos/doc/manual/md-to-db.sh --> 3 - <chapter xmlns="http://docbook.org/ns/docbook" xmlns:xlink="http://www.w3.org/1999/xlink" xml:id="module-postgresql"> 4 - <title>PostgreSQL</title> 5 - <para> 6 - <emphasis>Source:</emphasis> 7 - <filename>modules/services/databases/postgresql.nix</filename> 8 - </para> 9 - <para> 10 - <emphasis>Upstream documentation:</emphasis> 11 - <link xlink:href="http://www.postgresql.org/docs/">http://www.postgresql.org/docs/</link> 12 - </para> 13 - <para> 14 - PostgreSQL is an advanced, free relational database. 15 - </para> 16 - <section xml:id="module-services-postgres-configuring"> 17 - <title>Configuring</title> 18 - <para> 19 - To enable PostgreSQL, add the following to your 20 - <filename>configuration.nix</filename>: 21 - </para> 22 - <programlisting> 23 - services.postgresql.enable = true; 24 - services.postgresql.package = pkgs.postgresql_11; 25 - </programlisting> 26 - <para> 27 - Note that you are required to specify the desired version of 28 - PostgreSQL (e.g. <literal>pkgs.postgresql_11</literal>). Since 29 - upgrading your PostgreSQL version requires a database dump and 30 - reload (see below), NixOS cannot provide a default value for 31 - <xref linkend="opt-services.postgresql.package" /> such as the 32 - most recent release of PostgreSQL. 33 - </para> 34 - <para> 35 - By default, PostgreSQL stores its databases in 36 - <filename>/var/lib/postgresql/$psqlSchema</filename>. You can 37 - override this using 38 - <xref linkend="opt-services.postgresql.dataDir" />, e.g. 39 - </para> 40 - <programlisting> 41 - services.postgresql.dataDir = &quot;/data/postgresql&quot;; 42 - </programlisting> 43 - </section> 44 - <section xml:id="module-services-postgres-upgrading"> 45 - <title>Upgrading</title> 46 - <note> 47 - <para> 48 - The steps below demonstrate how to upgrade from an older version 49 - to <literal>pkgs.postgresql_13</literal>. These instructions are 50 - also applicable to other versions. 51 - </para> 52 - </note> 53 - <para> 54 - Major PostgreSQL upgrades require a downtime and a few imperative 55 - steps to be called. This is the case because each major version 56 - has some internal changes in the databases’ state during major 57 - releases. Because of that, NixOS places the state into 58 - <filename>/var/lib/postgresql/&lt;version&gt;</filename> where 59 - each <literal>version</literal> can be obtained like this: 60 - </para> 61 - <programlisting> 62 - $ nix-instantiate --eval -A postgresql_13.psqlSchema 63 - &quot;13&quot; 64 - </programlisting> 65 - <para> 66 - For an upgrade, a script like this can be used to simplify the 67 - process: 68 - </para> 69 - <programlisting> 70 - { config, pkgs, ... }: 71 - { 72 - environment.systemPackages = [ 73 - (let 74 - # XXX specify the postgresql package you'd like to upgrade to. 75 - # Do not forget to list the extensions you need. 76 - newPostgres = pkgs.postgresql_13.withPackages (pp: [ 77 - # pp.plv8 78 - ]); 79 - in pkgs.writeScriptBin &quot;upgrade-pg-cluster&quot; '' 80 - set -eux 81 - # XXX it's perhaps advisable to stop all services that depend on postgresql 82 - systemctl stop postgresql 83 - 84 - export NEWDATA=&quot;/var/lib/postgresql/${newPostgres.psqlSchema}&quot; 85 - 86 - export NEWBIN=&quot;${newPostgres}/bin&quot; 87 - 88 - export OLDDATA=&quot;${config.services.postgresql.dataDir}&quot; 89 - export OLDBIN=&quot;${config.services.postgresql.package}/bin&quot; 90 - 91 - install -d -m 0700 -o postgres -g postgres &quot;$NEWDATA&quot; 92 - cd &quot;$NEWDATA&quot; 93 - sudo -u postgres $NEWBIN/initdb -D &quot;$NEWDATA&quot; 94 - 95 - sudo -u postgres $NEWBIN/pg_upgrade \ 96 - --old-datadir &quot;$OLDDATA&quot; --new-datadir &quot;$NEWDATA&quot; \ 97 - --old-bindir $OLDBIN --new-bindir $NEWBIN \ 98 - &quot;$@&quot; 99 - '') 100 - ]; 101 - } 102 - </programlisting> 103 - <para> 104 - The upgrade process is: 105 - </para> 106 - <orderedlist numeration="arabic"> 107 - <listitem> 108 - <para> 109 - Rebuild nixos configuration with the configuration above added 110 - to your <filename>configuration.nix</filename>. Alternatively, 111 - add that into separate file and reference it in 112 - <literal>imports</literal> list. 113 - </para> 114 - </listitem> 115 - <listitem> 116 - <para> 117 - Login as root (<literal>sudo su -</literal>) 118 - </para> 119 - </listitem> 120 - <listitem> 121 - <para> 122 - Run <literal>upgrade-pg-cluster</literal>. It will stop old 123 - postgresql, initialize a new one and migrate the old one to 124 - the new one. You may supply arguments like 125 - <literal>--jobs 4</literal> and <literal>--link</literal> to 126 - speedup migration process. See 127 - <link xlink:href="https://www.postgresql.org/docs/current/pgupgrade.html">https://www.postgresql.org/docs/current/pgupgrade.html</link> 128 - for details. 129 - </para> 130 - </listitem> 131 - <listitem> 132 - <para> 133 - Change postgresql package in NixOS configuration to the one 134 - you were upgrading to via 135 - <xref linkend="opt-services.postgresql.package" />. Rebuild 136 - NixOS. This should start new postgres using upgraded data 137 - directory and all services you stopped during the upgrade. 138 - </para> 139 - </listitem> 140 - <listitem> 141 - <para> 142 - After the upgrade it’s advisable to analyze the new cluster. 143 - </para> 144 - <itemizedlist> 145 - <listitem> 146 - <para> 147 - For PostgreSQL ≥ 14, use the <literal>vacuumdb</literal> 148 - command printed by the upgrades script. 149 - </para> 150 - </listitem> 151 - <listitem> 152 - <para> 153 - For PostgreSQL &lt; 14, run (as 154 - <literal>su -l postgres</literal> in the 155 - <xref linkend="opt-services.postgresql.dataDir" />, in 156 - this example <filename>/var/lib/postgresql/13</filename>): 157 - </para> 158 - <programlisting> 159 - $ ./analyze_new_cluster.sh 160 - </programlisting> 161 - </listitem> 162 - </itemizedlist> 163 - <warning> 164 - <para> 165 - The next step removes the old state-directory! 166 - </para> 167 - </warning> 168 - <programlisting> 169 - $ ./delete_old_cluster.sh 170 - </programlisting> 171 - </listitem> 172 - </orderedlist> 173 - </section> 174 - <section xml:id="module-services-postgres-options"> 175 - <title>Options</title> 176 - <para> 177 - A complete list of options for the PostgreSQL module may be found 178 - <link linkend="opt-services.postgresql.enable">here</link>. 179 - </para> 180 - </section> 181 - <section xml:id="module-services-postgres-plugins"> 182 - <title>Plugins</title> 183 - <para> 184 - Plugins collection for each PostgreSQL version can be accessed 185 - with <literal>.pkgs</literal>. For example, for 186 - <literal>pkgs.postgresql_11</literal> package, its plugin 187 - collection is accessed by 188 - <literal>pkgs.postgresql_11.pkgs</literal>: 189 - </para> 190 - <programlisting> 191 - $ nix repl '&lt;nixpkgs&gt;' 192 - 193 - Loading '&lt;nixpkgs&gt;'... 194 - Added 10574 variables. 195 - 196 - nix-repl&gt; postgresql_11.pkgs.&lt;TAB&gt;&lt;TAB&gt; 197 - postgresql_11.pkgs.cstore_fdw postgresql_11.pkgs.pg_repack 198 - postgresql_11.pkgs.pg_auto_failover postgresql_11.pkgs.pg_safeupdate 199 - postgresql_11.pkgs.pg_bigm postgresql_11.pkgs.pg_similarity 200 - postgresql_11.pkgs.pg_cron postgresql_11.pkgs.pg_topn 201 - postgresql_11.pkgs.pg_hll postgresql_11.pkgs.pgjwt 202 - postgresql_11.pkgs.pg_partman postgresql_11.pkgs.pgroonga 203 - ... 204 - </programlisting> 205 - <para> 206 - To add plugins via NixOS configuration, set 207 - <literal>services.postgresql.extraPlugins</literal>: 208 - </para> 209 - <programlisting> 210 - services.postgresql.package = pkgs.postgresql_11; 211 - services.postgresql.extraPlugins = with pkgs.postgresql_11.pkgs; [ 212 - pg_repack 213 - postgis 214 - ]; 215 - </programlisting> 216 - <para> 217 - You can build custom PostgreSQL-with-plugins (to be used outside 218 - of NixOS) using function <literal>.withPackages</literal>. For 219 - example, creating a custom PostgreSQL package in an overlay can 220 - look like: 221 - </para> 222 - <programlisting> 223 - self: super: { 224 - postgresql_custom = self.postgresql_11.withPackages (ps: [ 225 - ps.pg_repack 226 - ps.postgis 227 - ]); 228 - } 229 - </programlisting> 230 - <para> 231 - Here’s a recipe on how to override a particular plugin through an 232 - overlay: 233 - </para> 234 - <programlisting> 235 - self: super: { 236 - postgresql_11 = super.postgresql_11.override { this = self.postgresql_11; } // { 237 - pkgs = super.postgresql_11.pkgs // { 238 - pg_repack = super.postgresql_11.pkgs.pg_repack.overrideAttrs (_: { 239 - name = &quot;pg_repack-v20181024&quot;; 240 - src = self.fetchzip { 241 - url = &quot;https://github.com/reorg/pg_repack/archive/923fa2f3c709a506e111cc963034bf2fd127aa00.tar.gz&quot;; 242 - sha256 = &quot;17k6hq9xaax87yz79j773qyigm4fwk8z4zh5cyp6z0sxnwfqxxw5&quot;; 243 - }; 244 - }); 245 - }; 246 - }; 247 - } 248 - </programlisting> 249 - </section> 250 - </chapter>
+1 -1
nixos/modules/services/desktops/flatpak.nix
··· 7 7 cfg = config.services.flatpak; 8 8 in { 9 9 meta = { 10 - doc = ./flatpak.xml; 10 + doc = ./flatpak.md; 11 11 maintainers = pkgs.flatpak.meta.maintainers; 12 12 }; 13 13
-59
nixos/modules/services/desktops/flatpak.xml
··· 1 - <!-- Do not edit this file directly, edit its companion .md instead 2 - and regenerate this file using nixos/doc/manual/md-to-db.sh --> 3 - <chapter xmlns="http://docbook.org/ns/docbook" xmlns:xlink="http://www.w3.org/1999/xlink" xml:id="module-services-flatpak"> 4 - <title>Flatpak</title> 5 - <para> 6 - <emphasis>Source:</emphasis> 7 - <filename>modules/services/desktop/flatpak.nix</filename> 8 - </para> 9 - <para> 10 - <emphasis>Upstream documentation:</emphasis> 11 - <link xlink:href="https://github.com/flatpak/flatpak/wiki">https://github.com/flatpak/flatpak/wiki</link> 12 - </para> 13 - <para> 14 - Flatpak is a system for building, distributing, and running 15 - sandboxed desktop applications on Linux. 16 - </para> 17 - <para> 18 - To enable Flatpak, add the following to your 19 - <filename>configuration.nix</filename>: 20 - </para> 21 - <programlisting> 22 - services.flatpak.enable = true; 23 - </programlisting> 24 - <para> 25 - For the sandboxed apps to work correctly, desktop integration 26 - portals need to be installed. If you run GNOME, this will be handled 27 - automatically for you; in other cases, you will need to add 28 - something like the following to your 29 - <filename>configuration.nix</filename>: 30 - </para> 31 - <programlisting> 32 - xdg.portal.extraPortals = [ pkgs.xdg-desktop-portal-gtk ]; 33 - </programlisting> 34 - <para> 35 - Then, you will need to add a repository, for example, 36 - <link xlink:href="https://github.com/flatpak/flatpak/wiki">Flathub</link>, 37 - either using the following commands: 38 - </para> 39 - <programlisting> 40 - $ flatpak remote-add --if-not-exists flathub https://flathub.org/repo/flathub.flatpakrepo 41 - $ flatpak update 42 - </programlisting> 43 - <para> 44 - or by opening the 45 - <link xlink:href="https://flathub.org/repo/flathub.flatpakrepo">repository 46 - file</link> in GNOME Software. 47 - </para> 48 - <para> 49 - Finally, you can search and install programs: 50 - </para> 51 - <programlisting> 52 - $ flatpak search bustle 53 - $ flatpak install flathub org.freedesktop.Bustle 54 - $ flatpak run org.freedesktop.Bustle 55 - </programlisting> 56 - <para> 57 - Again, GNOME Software offers graphical interface for these tasks. 58 - </para> 59 - </chapter>
+1 -1
nixos/modules/services/development/blackfire.nix
··· 11 11 in { 12 12 meta = { 13 13 maintainers = pkgs.blackfire.meta.maintainers; 14 - doc = ./blackfire.xml; 14 + doc = ./blackfire.md; 15 15 }; 16 16 17 17 options = {
-61
nixos/modules/services/development/blackfire.xml
··· 1 - <!-- Do not edit this file directly, edit its companion .md instead 2 - and regenerate this file using nixos/doc/manual/md-to-db.sh --> 3 - <chapter xmlns="http://docbook.org/ns/docbook" xmlns:xlink="http://www.w3.org/1999/xlink" xml:id="module-services-blackfire"> 4 - <title>Blackfire profiler</title> 5 - <para> 6 - <emphasis>Source:</emphasis> 7 - <filename>modules/services/development/blackfire.nix</filename> 8 - </para> 9 - <para> 10 - <emphasis>Upstream documentation:</emphasis> 11 - <link xlink:href="https://blackfire.io/docs/introduction">https://blackfire.io/docs/introduction</link> 12 - </para> 13 - <para> 14 - <link xlink:href="https://blackfire.io">Blackfire</link> is a 15 - proprietary tool for profiling applications. There are several 16 - languages supported by the product but currently only PHP support is 17 - packaged in Nixpkgs. The back-end consists of a module that is 18 - loaded into the language runtime (called <emphasis>probe</emphasis>) 19 - and a service (<emphasis>agent</emphasis>) that the probe connects 20 - to and that sends the profiles to the server. 21 - </para> 22 - <para> 23 - To use it, you will need to enable the agent and the probe on your 24 - server. The exact method will depend on the way you use PHP but here 25 - is an example of NixOS configuration for PHP-FPM: 26 - </para> 27 - <programlisting> 28 - let 29 - php = pkgs.php.withExtensions ({ enabled, all }: enabled ++ (with all; [ 30 - blackfire 31 - ])); 32 - in { 33 - # Enable the probe extension for PHP-FPM. 34 - services.phpfpm = { 35 - phpPackage = php; 36 - }; 37 - 38 - # Enable and configure the agent. 39 - services.blackfire-agent = { 40 - enable = true; 41 - settings = { 42 - # You will need to get credentials at https://blackfire.io/my/settings/credentials 43 - # You can also use other options described in https://blackfire.io/docs/up-and-running/configuration/agent 44 - server-id = &quot;XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX&quot;; 45 - server-token = &quot;XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX&quot;; 46 - }; 47 - }; 48 - 49 - # Make the agent run on start-up. 50 - # (WantedBy= from the upstream unit not respected: https://github.com/NixOS/nixpkgs/issues/81138) 51 - # Alternately, you can start it manually with `systemctl start blackfire-agent`. 52 - systemd.services.blackfire-agent.wantedBy = [ &quot;phpfpm-foo.service&quot; ]; 53 - } 54 - </programlisting> 55 - <para> 56 - On your developer machine, you will also want to install 57 - <link xlink:href="https://blackfire.io/docs/up-and-running/installation#install-a-profiling-client">the 58 - client</link> (see <literal>blackfire</literal> package) or the 59 - browser extension to actually trigger the profiling. 60 - </para> 61 - </chapter>
+1 -1
nixos/modules/services/editors/emacs.nix
··· 99 99 environment.variables.EDITOR = mkIf cfg.defaultEditor (mkOverride 900 "${editorScript}/bin/emacseditor"); 100 100 }; 101 101 102 - meta.doc = ./emacs.xml; 102 + meta.doc = ./emacs.md; 103 103 }
-490
nixos/modules/services/editors/emacs.xml
··· 1 - <!-- Do not edit this file directly, edit its companion .md instead 2 - and regenerate this file using nixos/doc/manual/md-to-db.sh --> 3 - <chapter xmlns="http://docbook.org/ns/docbook" xmlns:xlink="http://www.w3.org/1999/xlink" xml:id="module-services-emacs"> 4 - <title>Emacs</title> 5 - <para> 6 - <link xlink:href="https://www.gnu.org/software/emacs/">Emacs</link> 7 - is an extensible, customizable, self-documenting real-time display 8 - editor — and more. At its core is an interpreter for Emacs Lisp, a 9 - dialect of the Lisp programming language with extensions to support 10 - text editing. 11 - </para> 12 - <para> 13 - Emacs runs within a graphical desktop environment using the X Window 14 - System, but works equally well on a text terminal. Under macOS, a 15 - <quote>Mac port</quote> edition is available, which uses Apple’s 16 - native GUI frameworks. 17 - </para> 18 - <para> 19 - Nixpkgs provides a superior environment for running Emacs. It’s 20 - simple to create custom builds by overriding the default packages. 21 - Chaotic collections of Emacs Lisp code and extensions can be brought 22 - under control using declarative package management. NixOS even 23 - provides a <command>systemd</command> user service for automatically 24 - starting the Emacs daemon. 25 - </para> 26 - <section xml:id="module-services-emacs-installing"> 27 - <title>Installing Emacs</title> 28 - <para> 29 - Emacs can be installed in the normal way for Nix (see 30 - <xref linkend="sec-package-management" />). In addition, a NixOS 31 - <emphasis>service</emphasis> can be enabled. 32 - </para> 33 - <section xml:id="module-services-emacs-releases"> 34 - <title>The Different Releases of Emacs</title> 35 - <para> 36 - Nixpkgs defines several basic Emacs packages. The following are 37 - attributes belonging to the <varname>pkgs</varname> set: 38 - </para> 39 - <variablelist spacing="compact"> 40 - <varlistentry> 41 - <term> 42 - <varname>emacs</varname> 43 - </term> 44 - <listitem> 45 - <para> 46 - The latest stable version of Emacs using the 47 - <link xlink:href="http://www.gtk.org">GTK 2</link> widget 48 - toolkit. 49 - </para> 50 - </listitem> 51 - </varlistentry> 52 - <varlistentry> 53 - <term> 54 - <varname>emacs-nox</varname> 55 - </term> 56 - <listitem> 57 - <para> 58 - Emacs built without any dependency on X11 libraries. 59 - </para> 60 - </listitem> 61 - </varlistentry> 62 - <varlistentry> 63 - <term> 64 - <varname>emacsMacport</varname> 65 - </term> 66 - <listitem> 67 - <para> 68 - Emacs with the <quote>Mac port</quote> patches, providing 69 - a more native look and feel under macOS. 70 - </para> 71 - </listitem> 72 - </varlistentry> 73 - </variablelist> 74 - <para> 75 - If those aren’t suitable, then the following imitation Emacs 76 - editors are also available in Nixpkgs: 77 - <link xlink:href="https://www.gnu.org/software/zile/">Zile</link>, 78 - <link xlink:href="http://homepage.boetes.org/software/mg/">mg</link>, 79 - <link xlink:href="http://yi-editor.github.io/">Yi</link>, 80 - <link xlink:href="https://joe-editor.sourceforge.io/">jmacs</link>. 81 - </para> 82 - </section> 83 - <section xml:id="module-services-emacs-adding-packages"> 84 - <title>Adding Packages to Emacs</title> 85 - <para> 86 - Emacs includes an entire ecosystem of functionality beyond text 87 - editing, including a project planner, mail and news reader, 88 - debugger interface, calendar, and more. 89 - </para> 90 - <para> 91 - Most extensions are gotten with the Emacs packaging system 92 - (<filename>package.el</filename>) from 93 - <link xlink:href="https://elpa.gnu.org/">Emacs Lisp Package 94 - Archive (ELPA)</link>, 95 - <link xlink:href="https://melpa.org/">MELPA</link>, 96 - <link xlink:href="https://stable.melpa.org/">MELPA 97 - Stable</link>, and 98 - <link xlink:href="http://orgmode.org/elpa.html">Org ELPA</link>. 99 - Nixpkgs is regularly updated to mirror all these archives. 100 - </para> 101 - <para> 102 - Under NixOS, you can continue to use 103 - <literal>package-list-packages</literal> and 104 - <literal>package-install</literal> to install packages. You can 105 - also declare the set of Emacs packages you need using the 106 - derivations from Nixpkgs. The rest of this section discusses 107 - declarative installation of Emacs packages through nixpkgs. 108 - </para> 109 - <para> 110 - The first step to declare the list of packages you want in your 111 - Emacs installation is to create a dedicated derivation. This can 112 - be done in a dedicated <filename>emacs.nix</filename> file such 113 - as: 114 - </para> 115 - <para> 116 - <anchor xml:id="ex-emacsNix" /> 117 - </para> 118 - <programlisting language="nix"> 119 - /* 120 - This is a nix expression to build Emacs and some Emacs packages I like 121 - from source on any distribution where Nix is installed. This will install 122 - all the dependencies from the nixpkgs repository and build the binary files 123 - without interfering with the host distribution. 124 - 125 - To build the project, type the following from the current directory: 126 - 127 - $ nix-build emacs.nix 128 - 129 - To run the newly compiled executable: 130 - 131 - $ ./result/bin/emacs 132 - */ 133 - 134 - # The first non-comment line in this file indicates that 135 - # the whole file represents a function. 136 - { pkgs ? import &lt;nixpkgs&gt; {} }: 137 - 138 - let 139 - # The let expression below defines a myEmacs binding pointing to the 140 - # current stable version of Emacs. This binding is here to separate 141 - # the choice of the Emacs binary from the specification of the 142 - # required packages. 143 - myEmacs = pkgs.emacs; 144 - # This generates an emacsWithPackages function. It takes a single 145 - # argument: a function from a package set to a list of packages 146 - # (the packages that will be available in Emacs). 147 - emacsWithPackages = (pkgs.emacsPackagesFor myEmacs).emacsWithPackages; 148 - in 149 - # The rest of the file specifies the list of packages to install. In the 150 - # example, two packages (magit and zerodark-theme) are taken from 151 - # MELPA stable. 152 - emacsWithPackages (epkgs: (with epkgs.melpaStablePackages; [ 153 - magit # ; Integrate git &lt;C-x g&gt; 154 - zerodark-theme # ; Nicolas' theme 155 - ]) 156 - # Two packages (undo-tree and zoom-frm) are taken from MELPA. 157 - ++ (with epkgs.melpaPackages; [ 158 - undo-tree # ; &lt;C-x u&gt; to show the undo tree 159 - zoom-frm # ; increase/decrease font size for all buffers %lt;C-x C-+&gt; 160 - ]) 161 - # Three packages are taken from GNU ELPA. 162 - ++ (with epkgs.elpaPackages; [ 163 - auctex # ; LaTeX mode 164 - beacon # ; highlight my cursor when scrolling 165 - nameless # ; hide current package name everywhere in elisp code 166 - ]) 167 - # notmuch is taken from a nixpkgs derivation which contains an Emacs mode. 168 - ++ [ 169 - pkgs.notmuch # From main packages set 170 - ]) 171 - </programlisting> 172 - <para> 173 - The result of this configuration will be an 174 - <command>emacs</command> command which launches Emacs with all 175 - of your chosen packages in the <varname>load-path</varname>. 176 - </para> 177 - <para> 178 - You can check that it works by executing this in a terminal: 179 - </para> 180 - <programlisting> 181 - $ nix-build emacs.nix 182 - $ ./result/bin/emacs -q 183 - </programlisting> 184 - <para> 185 - and then typing <literal>M-x package-initialize</literal>. Check 186 - that you can use all the packages you want in this Emacs 187 - instance. For example, try switching to the zerodark theme 188 - through 189 - <literal>M-x load-theme &lt;RET&gt; zerodark &lt;RET&gt; y</literal>. 190 - </para> 191 - <tip> 192 - <para> 193 - A few popular extensions worth checking out are: auctex, 194 - company, edit-server, flycheck, helm, iedit, magit, 195 - multiple-cursors, projectile, and yasnippet. 196 - </para> 197 - </tip> 198 - <para> 199 - The list of available packages in the various ELPA repositories 200 - can be seen with the following commands: 201 - <anchor xml:id="module-services-emacs-querying-packages" /> 202 - </para> 203 - <programlisting> 204 - nix-env -f &quot;&lt;nixpkgs&gt;&quot; -qaP -A emacs.pkgs.elpaPackages 205 - nix-env -f &quot;&lt;nixpkgs&gt;&quot; -qaP -A emacs.pkgs.melpaPackages 206 - nix-env -f &quot;&lt;nixpkgs&gt;&quot; -qaP -A emacs.pkgs.melpaStablePackages 207 - nix-env -f &quot;&lt;nixpkgs&gt;&quot; -qaP -A emacs.pkgs.orgPackages 208 - </programlisting> 209 - <para> 210 - If you are on NixOS, you can install this particular Emacs for 211 - all users by adding it to the list of system packages (see 212 - <xref linkend="sec-declarative-package-mgmt" />). Simply modify 213 - your file <filename>configuration.nix</filename> to make it 214 - contain: 215 - <anchor xml:id="module-services-emacs-configuration-nix" /> 216 - </para> 217 - <programlisting> 218 - { 219 - environment.systemPackages = [ 220 - # [...] 221 - (import /path/to/emacs.nix { inherit pkgs; }) 222 - ]; 223 - } 224 - </programlisting> 225 - <para> 226 - In this case, the next <command>nixos-rebuild switch</command> 227 - will take care of adding your <command>emacs</command> to the 228 - <varname>PATH</varname> environment variable (see 229 - <xref linkend="sec-changing-config" />). 230 - </para> 231 - <para> 232 - If you are not on NixOS or want to install this particular Emacs 233 - only for yourself, you can do so by adding it to your 234 - <filename>~/.config/nixpkgs/config.nix</filename> (see 235 - <link xlink:href="https://nixos.org/nixpkgs/manual/#sec-modify-via-packageOverrides">Nixpkgs 236 - manual</link>): 237 - <anchor xml:id="module-services-emacs-config-nix" /> 238 - </para> 239 - <programlisting> 240 - { 241 - packageOverrides = super: let self = super.pkgs; in { 242 - myemacs = import /path/to/emacs.nix { pkgs = self; }; 243 - }; 244 - } 245 - </programlisting> 246 - <para> 247 - In this case, the next 248 - <literal>nix-env -f '&lt;nixpkgs&gt;' -iA myemacs</literal> will 249 - take care of adding your emacs to the <varname>PATH</varname> 250 - environment variable. 251 - </para> 252 - </section> 253 - <section xml:id="module-services-emacs-advanced"> 254 - <title>Advanced Emacs Configuration</title> 255 - <para> 256 - If you want, you can tweak the Emacs package itself from your 257 - <filename>emacs.nix</filename>. For example, if you want to have 258 - a GTK 3-based Emacs instead of the default GTK 2-based binary 259 - and remove the automatically generated 260 - <filename>emacs.desktop</filename> (useful if you only use 261 - <command>emacsclient</command>), you can change your file 262 - <filename>emacs.nix</filename> in this way: 263 - </para> 264 - <para> 265 - <anchor xml:id="ex-emacsGtk3Nix" /> 266 - </para> 267 - <programlisting> 268 - { pkgs ? import &lt;nixpkgs&gt; {} }: 269 - let 270 - myEmacs = (pkgs.emacs.override { 271 - # Use gtk3 instead of the default gtk2 272 - withGTK3 = true; 273 - withGTK2 = false; 274 - }).overrideAttrs (attrs: { 275 - # I don't want emacs.desktop file because I only use 276 - # emacsclient. 277 - postInstall = (attrs.postInstall or &quot;&quot;) + '' 278 - rm $out/share/applications/emacs.desktop 279 - ''; 280 - }); 281 - in [...] 282 - </programlisting> 283 - <para> 284 - After building this file as shown in 285 - <link linkend="ex-emacsNix">the example above</link>, you will 286 - get an GTK 3-based Emacs binary pre-loaded with your favorite 287 - packages. 288 - </para> 289 - </section> 290 - </section> 291 - <section xml:id="module-services-emacs-running"> 292 - <title>Running Emacs as a Service</title> 293 - <para> 294 - NixOS provides an optional <command>systemd</command> service 295 - which launches 296 - <link xlink:href="https://www.gnu.org/software/emacs/manual/html_node/emacs/Emacs-Server.html">Emacs 297 - daemon</link> with the user’s login session. 298 - </para> 299 - <para> 300 - <emphasis>Source:</emphasis> 301 - <filename>modules/services/editors/emacs.nix</filename> 302 - </para> 303 - <section xml:id="module-services-emacs-enabling"> 304 - <title>Enabling the Service</title> 305 - <para> 306 - To install and enable the <command>systemd</command> user 307 - service for Emacs daemon, add the following to your 308 - <filename>configuration.nix</filename>: 309 - </para> 310 - <programlisting> 311 - services.emacs.enable = true; 312 - services.emacs.package = import /home/cassou/.emacs.d { pkgs = pkgs; }; 313 - </programlisting> 314 - <para> 315 - The <varname>services.emacs.package</varname> option allows a 316 - custom derivation to be used, for example, one created by 317 - <literal>emacsWithPackages</literal>. 318 - </para> 319 - <para> 320 - Ensure that the Emacs server is enabled for your user’s Emacs 321 - configuration, either by customizing the 322 - <varname>server-mode</varname> variable, or by adding 323 - <literal>(server-start)</literal> to 324 - <filename>~/.emacs.d/init.el</filename>. 325 - </para> 326 - <para> 327 - To start the daemon, execute the following: 328 - </para> 329 - <programlisting> 330 - $ nixos-rebuild switch # to activate the new configuration.nix 331 - $ systemctl --user daemon-reload # to force systemd reload 332 - $ systemctl --user start emacs.service # to start the Emacs daemon 333 - </programlisting> 334 - <para> 335 - The server should now be ready to serve Emacs clients. 336 - </para> 337 - </section> 338 - <section xml:id="module-services-emacs-starting-client"> 339 - <title>Starting the client</title> 340 - <para> 341 - Ensure that the emacs server is enabled, either by customizing 342 - the <varname>server-mode</varname> variable, or by adding 343 - <literal>(server-start)</literal> to 344 - <filename>~/.emacs</filename>. 345 - </para> 346 - <para> 347 - To connect to the emacs daemon, run one of the following: 348 - </para> 349 - <programlisting> 350 - emacsclient FILENAME 351 - emacsclient --create-frame # opens a new frame (window) 352 - emacsclient --create-frame --tty # opens a new frame on the current terminal 353 - </programlisting> 354 - </section> 355 - <section xml:id="module-services-emacs-editor-variable"> 356 - <title>Configuring the <varname>EDITOR</varname> variable</title> 357 - <para> 358 - If <xref linkend="opt-services.emacs.defaultEditor" /> is 359 - <literal>true</literal>, the <varname>EDITOR</varname> variable 360 - will be set to a wrapper script which launches 361 - <command>emacsclient</command>. 362 - </para> 363 - <para> 364 - Any setting of <varname>EDITOR</varname> in the shell config 365 - files will override 366 - <varname>services.emacs.defaultEditor</varname>. To make sure 367 - <varname>EDITOR</varname> refers to the Emacs wrapper script, 368 - remove any existing <varname>EDITOR</varname> assignment from 369 - <filename>.profile</filename>, <filename>.bashrc</filename>, 370 - <filename>.zshenv</filename> or any other shell config file. 371 - </para> 372 - <para> 373 - If you have formed certain bad habits when editing files, these 374 - can be corrected with a shell alias to the wrapper script: 375 - </para> 376 - <programlisting> 377 - alias vi=$EDITOR 378 - </programlisting> 379 - </section> 380 - <section xml:id="module-services-emacs-per-user"> 381 - <title>Per-User Enabling of the Service</title> 382 - <para> 383 - In general, <command>systemd</command> user services are 384 - globally enabled by symlinks in 385 - <filename>/etc/systemd/user</filename>. In the case where Emacs 386 - daemon is not wanted for all users, it is possible to install 387 - the service but not globally enable it: 388 - </para> 389 - <programlisting> 390 - services.emacs.enable = false; 391 - services.emacs.install = true; 392 - </programlisting> 393 - <para> 394 - To enable the <command>systemd</command> user service for just 395 - the currently logged in user, run: 396 - </para> 397 - <programlisting> 398 - systemctl --user enable emacs 399 - </programlisting> 400 - <para> 401 - This will add the symlink 402 - <filename>~/.config/systemd/user/emacs.service</filename>. 403 - </para> 404 - </section> 405 - </section> 406 - <section xml:id="module-services-emacs-configuring"> 407 - <title>Configuring Emacs</title> 408 - <para> 409 - The Emacs init file should be changed to load the extension 410 - packages at startup: 411 - <anchor xml:id="module-services-emacs-package-initialisation" /> 412 - </para> 413 - <programlisting> 414 - (require 'package) 415 - 416 - ;; optional. makes unpure packages archives unavailable 417 - (setq package-archives nil) 418 - 419 - (setq package-enable-at-startup nil) 420 - (package-initialize) 421 - </programlisting> 422 - <para> 423 - After the declarative emacs package configuration has been tested, 424 - previously downloaded packages can be cleaned up by removing 425 - <filename>~/.emacs.d/elpa</filename> (do make a backup first, in 426 - case you forgot a package). 427 - </para> 428 - <section xml:id="module-services-emacs-major-mode"> 429 - <title>A Major Mode for Nix Expressions</title> 430 - <para> 431 - Of interest may be <varname>melpaPackages.nix-mode</varname>, 432 - which provides syntax highlighting for the Nix language. This is 433 - particularly convenient if you regularly edit Nix files. 434 - </para> 435 - </section> 436 - <section xml:id="module-services-emacs-man-pages"> 437 - <title>Accessing man pages</title> 438 - <para> 439 - You can use <literal>woman</literal> to get completion of all 440 - available man pages. For example, type 441 - <literal>M-x woman &lt;RET&gt; nixos-rebuild &lt;RET&gt;.</literal> 442 - </para> 443 - </section> 444 - <section xml:id="sec-emacs-docbook-xml"> 445 - <title>Editing DocBook 5 XML Documents</title> 446 - <para> 447 - Emacs includes 448 - <link xlink:href="https://www.gnu.org/software/emacs/manual/html_node/nxml-mode/Introduction.html">nXML</link>, 449 - a major-mode for validating and editing XML documents. When 450 - editing DocBook 5.0 documents, such as 451 - <link linkend="book-nixos-manual">this one</link>, nXML needs to 452 - be configured with the relevant schema, which is not included. 453 - </para> 454 - <para> 455 - To install the DocBook 5.0 schemas, either add 456 - <varname>pkgs.docbook5</varname> to 457 - <xref linkend="opt-environment.systemPackages" /> 458 - (<link linkend="sec-declarative-package-mgmt">NixOS</link>), or 459 - run <literal>nix-env -f '&lt;nixpkgs&gt;' -iA docbook5</literal> 460 - (<link linkend="sec-ad-hoc-packages">Nix</link>). 461 - </para> 462 - <para> 463 - Then customize the variable 464 - <varname>rng-schema-locating-files</varname> to include 465 - <filename>~/.emacs.d/schemas.xml</filename> and put the 466 - following text into that file: 467 - <anchor xml:id="ex-emacs-docbook-xml" /> 468 - </para> 469 - <programlisting language="xml"> 470 - &lt;?xml version=&quot;1.0&quot;?&gt; 471 - &lt;!-- 472 - To let emacs find this file, evaluate: 473 - (add-to-list 'rng-schema-locating-files &quot;~/.emacs.d/schemas.xml&quot;) 474 - --&gt; 475 - &lt;locatingRules xmlns=&quot;http://thaiopensource.com/ns/locating-rules/1.0&quot;&gt; 476 - &lt;!-- 477 - Use this variation if pkgs.docbook5 is added to environment.systemPackages 478 - --&gt; 479 - &lt;namespace ns=&quot;http://docbook.org/ns/docbook&quot; 480 - uri=&quot;/run/current-system/sw/share/xml/docbook-5.0/rng/docbookxi.rnc&quot;/&gt; 481 - &lt;!-- 482 - Use this variation if installing schema with &quot;nix-env -iA pkgs.docbook5&quot;. 483 - &lt;namespace ns=&quot;http://docbook.org/ns/docbook&quot; 484 - uri=&quot;../.nix-profile/share/xml/docbook-5.0/rng/docbookxi.rnc&quot;/&gt; 485 - --&gt; 486 - &lt;/locatingRules&gt; 487 - </programlisting> 488 - </section> 489 - </section> 490 - </chapter>
+1 -1
nixos/modules/services/hardware/trezord.nix
··· 8 8 ### docs 9 9 10 10 meta = { 11 - doc = ./trezord.xml; 11 + doc = ./trezord.md; 12 12 }; 13 13 14 14 ### interface
-29
nixos/modules/services/hardware/trezord.xml
··· 1 - <!-- Do not edit this file directly, edit its companion .md instead 2 - and regenerate this file using nixos/doc/manual/md-to-db.sh --> 3 - <chapter xmlns="http://docbook.org/ns/docbook" xmlns:xlink="http://www.w3.org/1999/xlink" xml:id="trezor"> 4 - <title>Trezor</title> 5 - <para> 6 - Trezor is an open-source cryptocurrency hardware wallet and security 7 - token allowing secure storage of private keys. 8 - </para> 9 - <para> 10 - It offers advanced features such U2F two-factor authorization, SSH 11 - login through 12 - <link xlink:href="https://wiki.trezor.io/Apps:SSH_agent">Trezor SSH 13 - agent</link>, 14 - <link xlink:href="https://wiki.trezor.io/GPG">GPG</link> and a 15 - <link xlink:href="https://wiki.trezor.io/Trezor_Password_Manager">password 16 - manager</link>. For more information, guides and documentation, see 17 - <link xlink:href="https://wiki.trezor.io">https://wiki.trezor.io</link>. 18 - </para> 19 - <para> 20 - To enable Trezor support, add the following to your 21 - <filename>configuration.nix</filename>: 22 - </para> 23 - <programlisting> 24 - services.trezord.enable = true; 25 - </programlisting> 26 - <para> 27 - This will add all necessary udev rules and start Trezor Bridge. 28 - </para> 29 - </chapter>
+1 -1
nixos/modules/services/mail/mailman.nix
··· 642 642 643 643 meta = { 644 644 maintainers = with lib.maintainers; [ lheckemann qyliss ma27 ]; 645 - doc = ./mailman.xml; 645 + doc = ./mailman.md; 646 646 }; 647 647 648 648 }
-112
nixos/modules/services/mail/mailman.xml
··· 1 - <!-- Do not edit this file directly, edit its companion .md instead 2 - and regenerate this file using nixos/doc/manual/md-to-db.sh --> 3 - <chapter xmlns="http://docbook.org/ns/docbook" xmlns:xlink="http://www.w3.org/1999/xlink" xml:id="module-services-mailman"> 4 - <title>Mailman</title> 5 - <para> 6 - <link xlink:href="https://www.list.org">Mailman</link> is free 7 - software for managing electronic mail discussion and e-newsletter 8 - lists. Mailman and its web interface can be configured using the 9 - corresponding NixOS module. Note that this service is best used with 10 - an existing, securely configured Postfix setup, as it does not 11 - automatically configure this. 12 - </para> 13 - <section xml:id="module-services-mailman-basic-usage"> 14 - <title>Basic usage with Postfix</title> 15 - <para> 16 - For a basic configuration with Postfix as the MTA, the following 17 - settings are suggested: 18 - </para> 19 - <programlisting> 20 - { config, ... }: { 21 - services.postfix = { 22 - enable = true; 23 - relayDomains = [&quot;hash:/var/lib/mailman/data/postfix_domains&quot;]; 24 - sslCert = config.security.acme.certs.&quot;lists.example.org&quot;.directory + &quot;/full.pem&quot;; 25 - sslKey = config.security.acme.certs.&quot;lists.example.org&quot;.directory + &quot;/key.pem&quot;; 26 - config = { 27 - transport_maps = [&quot;hash:/var/lib/mailman/data/postfix_lmtp&quot;]; 28 - local_recipient_maps = [&quot;hash:/var/lib/mailman/data/postfix_lmtp&quot;]; 29 - }; 30 - }; 31 - services.mailman = { 32 - enable = true; 33 - serve.enable = true; 34 - hyperkitty.enable = true; 35 - webHosts = [&quot;lists.example.org&quot;]; 36 - siteOwner = &quot;mailman@example.org&quot;; 37 - }; 38 - services.nginx.virtualHosts.&quot;lists.example.org&quot;.enableACME = true; 39 - networking.firewall.allowedTCPPorts = [ 25 80 443 ]; 40 - } 41 - </programlisting> 42 - <para> 43 - DNS records will also be required: 44 - </para> 45 - <itemizedlist spacing="compact"> 46 - <listitem> 47 - <para> 48 - <literal>AAAA</literal> and <literal>A</literal> records 49 - pointing to the host in question, in order for browsers to be 50 - able to discover the address of the web server; 51 - </para> 52 - </listitem> 53 - <listitem> 54 - <para> 55 - An <literal>MX</literal> record pointing to a domain name at 56 - which the host is reachable, in order for other mail servers 57 - to be able to deliver emails to the mailing lists it hosts. 58 - </para> 59 - </listitem> 60 - </itemizedlist> 61 - <para> 62 - After this has been done and appropriate DNS records have been set 63 - up, the Postorius mailing list manager and the Hyperkitty archive 64 - browser will be available at https://lists.example.org/. Note that 65 - this setup is not sufficient to deliver emails to most email 66 - providers nor to avoid spam – a number of additional measures for 67 - authenticating incoming and outgoing mails, such as SPF, DMARC and 68 - DKIM are necessary, but outside the scope of the Mailman module. 69 - </para> 70 - </section> 71 - <section xml:id="module-services-mailman-other-mtas"> 72 - <title>Using with other MTAs</title> 73 - <para> 74 - Mailman also supports other MTA, though with a little bit more 75 - configuration. For example, to use Mailman with Exim, you can use 76 - the following settings: 77 - </para> 78 - <programlisting> 79 - { config, ... }: { 80 - services = { 81 - mailman = { 82 - enable = true; 83 - siteOwner = &quot;mailman@example.org&quot;; 84 - enablePostfix = false; 85 - settings.mta = { 86 - incoming = &quot;mailman.mta.exim4.LMTP&quot;; 87 - outgoing = &quot;mailman.mta.deliver.deliver&quot;; 88 - lmtp_host = &quot;localhost&quot;; 89 - lmtp_port = &quot;8024&quot;; 90 - smtp_host = &quot;localhost&quot;; 91 - smtp_port = &quot;25&quot;; 92 - configuration = &quot;python:mailman.config.exim4&quot;; 93 - }; 94 - }; 95 - exim = { 96 - enable = true; 97 - # You can configure Exim in a separate file to reduce configuration.nix clutter 98 - config = builtins.readFile ./exim.conf; 99 - }; 100 - }; 101 - } 102 - </programlisting> 103 - <para> 104 - The exim config needs some special additions to work with Mailman. 105 - Currently NixOS can’t manage Exim config with such granularity. 106 - Please refer to 107 - <link xlink:href="https://mailman.readthedocs.io/en/latest/src/mailman/docs/mta.html">Mailman 108 - documentation</link> for more info on configuring Mailman for 109 - working with Exim. 110 - </para> 111 - </section> 112 - </chapter>
+1 -1
nixos/modules/services/matrix/mjolnir.nix
··· 236 236 }; 237 237 238 238 meta = { 239 - doc = ./mjolnir.xml; 239 + doc = ./mjolnir.md; 240 240 maintainers = with maintainers; [ jojosch ]; 241 241 }; 242 242 }
-148
nixos/modules/services/matrix/mjolnir.xml
··· 1 - <!-- Do not edit this file directly, edit its companion .md instead 2 - and regenerate this file using nixos/doc/manual/md-to-db.sh --> 3 - <chapter xmlns="http://docbook.org/ns/docbook" xmlns:xlink="http://www.w3.org/1999/xlink" xml:id="module-services-mjolnir"> 4 - <title>Mjolnir (Matrix Moderation Tool)</title> 5 - <para> 6 - This chapter will show you how to set up your own, self-hosted 7 - <link xlink:href="https://github.com/matrix-org/mjolnir">Mjolnir</link> 8 - instance. 9 - </para> 10 - <para> 11 - As an all-in-one moderation tool, it can protect your server from 12 - malicious invites, spam messages, and whatever else you don’t want. 13 - In addition to server-level protection, Mjolnir is great for 14 - communities wanting to protect their rooms without having to use 15 - their personal accounts for moderation. 16 - </para> 17 - <para> 18 - The bot by default includes support for bans, redactions, anti-spam, 19 - server ACLs, room directory changes, room alias transfers, account 20 - deactivation, room shutdown, and more. 21 - </para> 22 - <para> 23 - See the 24 - <link xlink:href="https://github.com/matrix-org/mjolnir#readme">README</link> 25 - page and the 26 - <link xlink:href="https://github.com/matrix-org/mjolnir/blob/main/docs/moderators.md">Moderator’s 27 - guide</link> for additional instructions on how to setup and use 28 - Mjolnir. 29 - </para> 30 - <para> 31 - For <link linkend="opt-services.mjolnir.settings">additional 32 - settings</link> see 33 - <link xlink:href="https://github.com/matrix-org/mjolnir/blob/main/config/default.yaml">the 34 - default configuration</link>. 35 - </para> 36 - <section xml:id="module-services-mjolnir-setup"> 37 - <title>Mjolnir Setup</title> 38 - <para> 39 - First create a new Room which will be used as a management room 40 - for Mjolnir. In this room, Mjolnir will log possible errors and 41 - debugging information. You’ll need to set this Room-ID in 42 - <link linkend="opt-services.mjolnir.managementRoom">services.mjolnir.managementRoom</link>. 43 - </para> 44 - <para> 45 - Next, create a new user for Mjolnir on your homeserver, if not 46 - present already. 47 - </para> 48 - <para> 49 - The Mjolnir Matrix user expects to be free of any rate limiting. 50 - See 51 - <link xlink:href="https://github.com/matrix-org/synapse/issues/6286">Synapse 52 - #6286</link> for an example on how to achieve this. 53 - </para> 54 - <para> 55 - If you want Mjolnir to be able to deactivate users, move room 56 - aliases, shutdown rooms, etc. you’ll need to make the Mjolnir user 57 - a Matrix server admin. 58 - </para> 59 - <para> 60 - Now invite the Mjolnir user to the management room. 61 - </para> 62 - <para> 63 - It is recommended to use 64 - <link xlink:href="https://github.com/matrix-org/pantalaimon">Pantalaimon</link>, 65 - so your management room can be encrypted. This also applies if you 66 - are looking to moderate an encrypted room. 67 - </para> 68 - <para> 69 - To enable the Pantalaimon E2E Proxy for mjolnir, enable 70 - <link linkend="opt-services.mjolnir.pantalaimon.enable">services.mjolnir.pantalaimon</link>. 71 - This will autoconfigure a new Pantalaimon instance, which will 72 - connect to the homeserver set in 73 - <link linkend="opt-services.mjolnir.homeserverUrl">services.mjolnir.homeserverUrl</link> 74 - and Mjolnir itself will be configured to connect to the new 75 - Pantalaimon instance. 76 - </para> 77 - <programlisting> 78 - { 79 - services.mjolnir = { 80 - enable = true; 81 - homeserverUrl = &quot;https://matrix.domain.tld&quot;; 82 - pantalaimon = { 83 - enable = true; 84 - username = &quot;mjolnir&quot;; 85 - passwordFile = &quot;/run/secrets/mjolnir-password&quot;; 86 - }; 87 - protectedRooms = [ 88 - &quot;https://matrix.to/#/!xxx:domain.tld&quot; 89 - ]; 90 - managementRoom = &quot;!yyy:domain.tld&quot;; 91 - }; 92 - } 93 - </programlisting> 94 - <section xml:id="module-services-mjolnir-setup-ems"> 95 - <title>Element Matrix Services (EMS)</title> 96 - <para> 97 - If you are using a managed 98 - <link xlink:href="https://ems.element.io/"><quote>Element Matrix 99 - Services (EMS)</quote></link> server, you will need to consent 100 - to the terms and conditions. Upon startup, an error log entry 101 - with a URL to the consent page will be generated. 102 - </para> 103 - </section> 104 - </section> 105 - <section xml:id="module-services-mjolnir-matrix-synapse-antispam"> 106 - <title>Synapse Antispam Module</title> 107 - <para> 108 - A Synapse module is also available to apply the same rulesets the 109 - bot uses across an entire homeserver. 110 - </para> 111 - <para> 112 - To use the Antispam Module, add 113 - <literal>matrix-synapse-plugins.matrix-synapse-mjolnir-antispam</literal> 114 - to the Synapse plugin list and enable the 115 - <literal>mjolnir.Module</literal> module. 116 - </para> 117 - <programlisting> 118 - { 119 - services.matrix-synapse = { 120 - plugins = with pkgs; [ 121 - matrix-synapse-plugins.matrix-synapse-mjolnir-antispam 122 - ]; 123 - extraConfig = '' 124 - modules: 125 - - module: mjolnir.Module 126 - config: 127 - # Prevent servers/users in the ban lists from inviting users on this 128 - # server to rooms. Default true. 129 - block_invites: true 130 - # Flag messages sent by servers/users in the ban lists as spam. Currently 131 - # this means that spammy messages will appear as empty to users. Default 132 - # false. 133 - block_messages: false 134 - # Remove users from the user directory search by filtering matrix IDs and 135 - # display names by the entries in the user ban list. Default false. 136 - block_usernames: false 137 - # The room IDs of the ban lists to honour. Unlike other parts of Mjolnir, 138 - # this list cannot be room aliases or permalinks. This server is expected 139 - # to already be joined to the room - Mjolnir will not automatically join 140 - # these rooms. 141 - ban_lists: 142 - - &quot;!roomid:example.org&quot; 143 - ''; 144 - }; 145 - } 146 - </programlisting> 147 - </section> 148 - </chapter>
+1 -1
nixos/modules/services/matrix/synapse.nix
··· 801 801 802 802 meta = { 803 803 buildDocsInSandbox = false; 804 - doc = ./synapse.xml; 804 + doc = ./synapse.md; 805 805 maintainers = teams.matrix.members; 806 806 }; 807 807
-263
nixos/modules/services/matrix/synapse.xml
··· 1 - <!-- Do not edit this file directly, edit its companion .md instead 2 - and regenerate this file using nixos/doc/manual/md-to-db.sh --> 3 - <chapter xmlns="http://docbook.org/ns/docbook" xmlns:xlink="http://www.w3.org/1999/xlink" xml:id="module-services-matrix"> 4 - <title>Matrix</title> 5 - <para> 6 - <link xlink:href="https://matrix.org/">Matrix</link> is an open 7 - standard for interoperable, decentralised, real-time communication 8 - over IP. It can be used to power Instant Messaging, VoIP/WebRTC 9 - signalling, Internet of Things communication - or anywhere you need 10 - a standard HTTP API for publishing and subscribing to data whilst 11 - tracking the conversation history. 12 - </para> 13 - <para> 14 - This chapter will show you how to set up your own, self-hosted 15 - Matrix homeserver using the Synapse reference homeserver, and how to 16 - serve your own copy of the Element web client. See the 17 - <link xlink:href="https://matrix.org/docs/projects/try-matrix-now.html">Try 18 - Matrix Now!</link> overview page for links to Element Apps for 19 - Android and iOS, desktop clients, as well as bridges to other 20 - networks and other projects around Matrix. 21 - </para> 22 - <section xml:id="module-services-matrix-synapse"> 23 - <title>Synapse Homeserver</title> 24 - <para> 25 - <link xlink:href="https://github.com/matrix-org/synapse">Synapse</link> 26 - is the reference homeserver implementation of Matrix from the core 27 - development team at matrix.org. The following configuration 28 - example will set up a synapse server for the 29 - <literal>example.org</literal> domain, served from the host 30 - <literal>myhostname.example.org</literal>. For more information, 31 - please refer to the 32 - <link xlink:href="https://matrix-org.github.io/synapse/latest/setup/installation.html">installation 33 - instructions of Synapse</link> . 34 - </para> 35 - <programlisting> 36 - { pkgs, lib, config, ... }: 37 - let 38 - fqdn = &quot;${config.networking.hostName}.${config.networking.domain}&quot;; 39 - clientConfig = { 40 - &quot;m.homeserver&quot;.base_url = &quot;https://${fqdn}&quot;; 41 - &quot;m.identity_server&quot; = {}; 42 - }; 43 - serverConfig.&quot;m.server&quot; = &quot;${config.services.matrix-synapse.settings.server_name}:443&quot;; 44 - mkWellKnown = data: '' 45 - add_header Content-Type application/json; 46 - add_header Access-Control-Allow-Origin *; 47 - return 200 '${builtins.toJSON data}'; 48 - ''; 49 - in { 50 - networking.hostName = &quot;myhostname&quot;; 51 - networking.domain = &quot;example.org&quot;; 52 - networking.firewall.allowedTCPPorts = [ 80 443 ]; 53 - 54 - services.postgresql.enable = true; 55 - services.postgresql.initialScript = pkgs.writeText &quot;synapse-init.sql&quot; '' 56 - CREATE ROLE &quot;matrix-synapse&quot; WITH LOGIN PASSWORD 'synapse'; 57 - CREATE DATABASE &quot;matrix-synapse&quot; WITH OWNER &quot;matrix-synapse&quot; 58 - TEMPLATE template0 59 - LC_COLLATE = &quot;C&quot; 60 - LC_CTYPE = &quot;C&quot;; 61 - ''; 62 - 63 - services.nginx = { 64 - enable = true; 65 - recommendedTlsSettings = true; 66 - recommendedOptimisation = true; 67 - recommendedGzipSettings = true; 68 - recommendedProxySettings = true; 69 - virtualHosts = { 70 - # If the A and AAAA DNS records on example.org do not point on the same host as the 71 - # records for myhostname.example.org, you can easily move the /.well-known 72 - # virtualHost section of the code to the host that is serving example.org, while 73 - # the rest stays on myhostname.example.org with no other changes required. 74 - # This pattern also allows to seamlessly move the homeserver from 75 - # myhostname.example.org to myotherhost.example.org by only changing the 76 - # /.well-known redirection target. 77 - &quot;${config.networking.domain}&quot; = { 78 - enableACME = true; 79 - forceSSL = true; 80 - # This section is not needed if the server_name of matrix-synapse is equal to 81 - # the domain (i.e. example.org from @foo:example.org) and the federation port 82 - # is 8448. 83 - # Further reference can be found in the docs about delegation under 84 - # https://matrix-org.github.io/synapse/latest/delegate.html 85 - locations.&quot;= /.well-known/matrix/server&quot;.extraConfig = mkWellKnown serverConfig; 86 - # This is usually needed for homeserver discovery (from e.g. other Matrix clients). 87 - # Further reference can be found in the upstream docs at 88 - # https://spec.matrix.org/latest/client-server-api/#getwell-knownmatrixclient 89 - locations.&quot;= /.well-known/matrix/client&quot;.extraConfig = mkWellKnown clientConfig; 90 - }; 91 - &quot;${fqdn}&quot; = { 92 - enableACME = true; 93 - forceSSL = true; 94 - # It's also possible to do a redirect here or something else, this vhost is not 95 - # needed for Matrix. It's recommended though to *not put* element 96 - # here, see also the section about Element. 97 - locations.&quot;/&quot;.extraConfig = '' 98 - return 404; 99 - ''; 100 - # Forward all Matrix API calls to the synapse Matrix homeserver. A trailing slash 101 - # *must not* be used here. 102 - locations.&quot;/_matrix&quot;.proxyPass = &quot;http://[::1]:8008&quot;; 103 - # Forward requests for e.g. SSO and password-resets. 104 - locations.&quot;/_synapse/client&quot;.proxyPass = &quot;http://[::1]:8008&quot;; 105 - }; 106 - }; 107 - }; 108 - 109 - services.matrix-synapse = { 110 - enable = true; 111 - settings.server_name = config.networking.domain; 112 - settings.listeners = [ 113 - { port = 8008; 114 - bind_addresses = [ &quot;::1&quot; ]; 115 - type = &quot;http&quot;; 116 - tls = false; 117 - x_forwarded = true; 118 - resources = [ { 119 - names = [ &quot;client&quot; &quot;federation&quot; ]; 120 - compress = true; 121 - } ]; 122 - } 123 - ]; 124 - }; 125 - } 126 - </programlisting> 127 - </section> 128 - <section xml:id="module-services-matrix-register-users"> 129 - <title>Registering Matrix users</title> 130 - <para> 131 - If you want to run a server with public registration by anybody, 132 - you can then enable 133 - <literal>services.matrix-synapse.settings.enable_registration = true;</literal>. 134 - Otherwise, or you can generate a registration secret with 135 - <command>pwgen -s 64 1</command> and set it with 136 - <xref linkend="opt-services.matrix-synapse.settings.registration_shared_secret" />. 137 - To create a new user or admin, run the following after you have 138 - set the secret and have rebuilt NixOS: 139 - </para> 140 - <programlisting> 141 - $ nix-shell -p matrix-synapse 142 - $ register_new_matrix_user -k your-registration-shared-secret http://localhost:8008 143 - New user localpart: your-username 144 - Password: 145 - Confirm password: 146 - Make admin [no]: 147 - Success! 148 - </programlisting> 149 - <para> 150 - In the example, this would create a user with the Matrix 151 - Identifier <literal>@your-username:example.org</literal>. 152 - </para> 153 - <warning> 154 - <para> 155 - When using 156 - <xref linkend="opt-services.matrix-synapse.settings.registration_shared_secret" />, 157 - the secret will end up in the world-readable store. Instead it’s 158 - recommended to deploy the secret in an additional file like 159 - this: 160 - </para> 161 - <itemizedlist> 162 - <listitem> 163 - <para> 164 - Create a file with the following contents: 165 - </para> 166 - <programlisting> 167 - registration_shared_secret: your-very-secret-secret 168 - </programlisting> 169 - </listitem> 170 - <listitem> 171 - <para> 172 - Deploy the file with a secret-manager such as 173 - <link xlink:href="https://nixops.readthedocs.io/en/latest/overview.html#managing-keys"><option>deployment.keys</option></link> 174 - from 175 - <citerefentry><refentrytitle>nixops</refentrytitle><manvolnum>1</manvolnum></citerefentry> 176 - or 177 - <link xlink:href="https://github.com/Mic92/sops-nix/">sops-nix</link> 178 - to e.g. 179 - <filename>/run/secrets/matrix-shared-secret</filename> and 180 - ensure that it’s readable by 181 - <literal>matrix-synapse</literal>. 182 - </para> 183 - </listitem> 184 - <listitem> 185 - <para> 186 - Include the file like this in your configuration: 187 - </para> 188 - <programlisting> 189 - { 190 - services.matrix-synapse.extraConfigFiles = [ 191 - &quot;/run/secrets/matrix-shared-secret&quot; 192 - ]; 193 - } 194 - </programlisting> 195 - </listitem> 196 - </itemizedlist> 197 - </warning> 198 - <note> 199 - <para> 200 - It’s also possible to user alternative authentication mechanism 201 - such as 202 - <link xlink:href="https://github.com/matrix-org/matrix-synapse-ldap3">LDAP 203 - (via <literal>matrix-synapse-ldap3</literal>)</link> or 204 - <link xlink:href="https://matrix-org.github.io/synapse/latest/openid.html">OpenID</link>. 205 - </para> 206 - </note> 207 - </section> 208 - <section xml:id="module-services-matrix-element-web"> 209 - <title>Element (formerly known as Riot) Web Client</title> 210 - <para> 211 - <link xlink:href="https://github.com/vector-im/riot-web/">Element 212 - Web</link> is the reference web client for Matrix and developed by 213 - the core team at matrix.org. Element was formerly known as 214 - Riot.im, see the 215 - <link xlink:href="https://element.io/blog/welcome-to-element/">Element 216 - introductory blog post</link> for more information. The following 217 - snippet can be optionally added to the code before to complete the 218 - synapse installation with a web client served at 219 - <literal>https://element.myhostname.example.org</literal> and 220 - <literal>https://element.example.org</literal>. Alternatively, you 221 - can use the hosted copy at 222 - <link xlink:href="https://app.element.io/">https://app.element.io/</link>, 223 - or use other web clients or native client applications. Due to the 224 - <literal>/.well-known</literal> urls set up done above, many 225 - clients should fill in the required connection details 226 - automatically when you enter your Matrix Identifier. See 227 - <link xlink:href="https://matrix.org/docs/projects/try-matrix-now.html">Try 228 - Matrix Now!</link> for a list of existing clients and their 229 - supported featureset. 230 - </para> 231 - <programlisting> 232 - { 233 - services.nginx.virtualHosts.&quot;element.${fqdn}&quot; = { 234 - enableACME = true; 235 - forceSSL = true; 236 - serverAliases = [ 237 - &quot;element.${config.networking.domain}&quot; 238 - ]; 239 - 240 - root = pkgs.element-web.override { 241 - conf = { 242 - default_server_config = clientConfig; # see `clientConfig` from the snippet above. 243 - }; 244 - }; 245 - }; 246 - } 247 - </programlisting> 248 - <note> 249 - <para> 250 - The Element developers do not recommend running Element and your 251 - Matrix homeserver on the same fully-qualified domain name for 252 - security reasons. In the example, this means that you should not 253 - reuse the <literal>myhostname.example.org</literal> virtualHost 254 - to also serve Element, but instead serve it on a different 255 - subdomain, like <literal>element.example.org</literal> in the 256 - example. See the 257 - <link xlink:href="https://github.com/vector-im/element-web/tree/v1.10.0#important-security-notes">Element 258 - Important Security Notes</link> for more information on this 259 - subject. 260 - </para> 261 - </note> 262 - </section> 263 - </chapter>
+1 -1
nixos/modules/services/misc/gitlab.nix
··· 1504 1504 1505 1505 }; 1506 1506 1507 - meta.doc = ./gitlab.xml; 1507 + meta.doc = ./gitlab.md; 1508 1508 1509 1509 }
-143
nixos/modules/services/misc/gitlab.xml
··· 1 - <!-- Do not edit this file directly, edit its companion .md instead 2 - and regenerate this file using nixos/doc/manual/md-to-db.sh --> 3 - <chapter xmlns="http://docbook.org/ns/docbook" xmlns:xlink="http://www.w3.org/1999/xlink" xml:id="module-services-gitlab"> 4 - <title>GitLab</title> 5 - <para> 6 - GitLab is a feature-rich git hosting service. 7 - </para> 8 - <section xml:id="module-services-gitlab-prerequisites"> 9 - <title>Prerequisites</title> 10 - <para> 11 - The <literal>gitlab</literal> service exposes only an Unix socket 12 - at <literal>/run/gitlab/gitlab-workhorse.socket</literal>. You 13 - need to configure a webserver to proxy HTTP requests to the 14 - socket. 15 - </para> 16 - <para> 17 - For instance, the following configuration could be used to use 18 - nginx as frontend proxy: 19 - </para> 20 - <programlisting> 21 - services.nginx = { 22 - enable = true; 23 - recommendedGzipSettings = true; 24 - recommendedOptimisation = true; 25 - recommendedProxySettings = true; 26 - recommendedTlsSettings = true; 27 - virtualHosts.&quot;git.example.com&quot; = { 28 - enableACME = true; 29 - forceSSL = true; 30 - locations.&quot;/&quot;.proxyPass = &quot;http://unix:/run/gitlab/gitlab-workhorse.socket&quot;; 31 - }; 32 - }; 33 - </programlisting> 34 - </section> 35 - <section xml:id="module-services-gitlab-configuring"> 36 - <title>Configuring</title> 37 - <para> 38 - GitLab depends on both PostgreSQL and Redis and will automatically 39 - enable both services. In the case of PostgreSQL, a database and a 40 - role will be created. 41 - </para> 42 - <para> 43 - The default state dir is <literal>/var/gitlab/state</literal>. 44 - This is where all data like the repositories and uploads will be 45 - stored. 46 - </para> 47 - <para> 48 - A basic configuration with some custom settings could look like 49 - this: 50 - </para> 51 - <programlisting> 52 - services.gitlab = { 53 - enable = true; 54 - databasePasswordFile = &quot;/var/keys/gitlab/db_password&quot;; 55 - initialRootPasswordFile = &quot;/var/keys/gitlab/root_password&quot;; 56 - https = true; 57 - host = &quot;git.example.com&quot;; 58 - port = 443; 59 - user = &quot;git&quot;; 60 - group = &quot;git&quot;; 61 - smtp = { 62 - enable = true; 63 - address = &quot;localhost&quot;; 64 - port = 25; 65 - }; 66 - secrets = { 67 - dbFile = &quot;/var/keys/gitlab/db&quot;; 68 - secretFile = &quot;/var/keys/gitlab/secret&quot;; 69 - otpFile = &quot;/var/keys/gitlab/otp&quot;; 70 - jwsFile = &quot;/var/keys/gitlab/jws&quot;; 71 - }; 72 - extraConfig = { 73 - gitlab = { 74 - email_from = &quot;gitlab-no-reply@example.com&quot;; 75 - email_display_name = &quot;Example GitLab&quot;; 76 - email_reply_to = &quot;gitlab-no-reply@example.com&quot;; 77 - default_projects_features = { builds = false; }; 78 - }; 79 - }; 80 - }; 81 - </programlisting> 82 - <para> 83 - If you’re setting up a new GitLab instance, generate new secrets. 84 - You for instance use 85 - <literal>tr -dc A-Za-z0-9 &lt; /dev/urandom | head -c 128 &gt; /var/keys/gitlab/db</literal> 86 - to generate a new db secret. Make sure the files can be read by, 87 - and only by, the user specified by 88 - <link linkend="opt-services.gitlab.user">services.gitlab.user</link>. 89 - GitLab encrypts sensitive data stored in the database. If you’re 90 - restoring an existing GitLab instance, you must specify the 91 - secrets secret from <literal>config/secrets.yml</literal> located 92 - in your GitLab state folder. 93 - </para> 94 - <para> 95 - When <literal>incoming_mail.enabled</literal> is set to 96 - <literal>true</literal> in 97 - <link linkend="opt-services.gitlab.extraConfig">extraConfig</link> 98 - an additional service called <literal>gitlab-mailroom</literal> is 99 - enabled for fetching incoming mail. 100 - </para> 101 - <para> 102 - Refer to <xref linkend="ch-options" /> for all available 103 - configuration options for the 104 - <link linkend="opt-services.gitlab.enable">services.gitlab</link> 105 - module. 106 - </para> 107 - </section> 108 - <section xml:id="module-services-gitlab-maintenance"> 109 - <title>Maintenance</title> 110 - <section xml:id="module-services-gitlab-maintenance-backups"> 111 - <title>Backups</title> 112 - <para> 113 - Backups can be configured with the options in 114 - <link linkend="opt-services.gitlab.backup.keepTime">services.gitlab.backup</link>. 115 - Use the 116 - <link linkend="opt-services.gitlab.backup.startAt">services.gitlab.backup.startAt</link> 117 - option to configure regular backups. 118 - </para> 119 - <para> 120 - To run a manual backup, start the 121 - <literal>gitlab-backup</literal> service: 122 - </para> 123 - <programlisting> 124 - $ systemctl start gitlab-backup.service 125 - </programlisting> 126 - </section> 127 - <section xml:id="module-services-gitlab-maintenance-rake"> 128 - <title>Rake tasks</title> 129 - <para> 130 - You can run GitLab’s rake tasks with 131 - <literal>gitlab-rake</literal> which will be available on the 132 - system when GitLab is enabled. You will have to run the command 133 - as the user that you configured to run GitLab with. 134 - </para> 135 - <para> 136 - A list of all available rake tasks can be obtained by running: 137 - </para> 138 - <programlisting> 139 - $ sudo -u git -H gitlab-rake -T 140 - </programlisting> 141 - </section> 142 - </section> 143 - </chapter>
+1 -1
nixos/modules/services/misc/sourcehut/default.nix
··· 1390 1390 '') 1391 1391 ]; 1392 1392 1393 - meta.doc = ./default.xml; 1393 + meta.doc = ./default.md; 1394 1394 meta.maintainers = with maintainers; [ tomberek ]; 1395 1395 }
-113
nixos/modules/services/misc/sourcehut/default.xml
··· 1 - <!-- Do not edit this file directly, edit its companion .md instead 2 - and regenerate this file using nixos/doc/manual/md-to-db.sh --> 3 - <chapter xmlns="http://docbook.org/ns/docbook" xmlns:xlink="http://www.w3.org/1999/xlink" xml:id="module-services-sourcehut"> 4 - <title>Sourcehut</title> 5 - <para> 6 - <link xlink:href="https://sr.ht.com/">Sourcehut</link> is an 7 - open-source, self-hostable software development platform. The server 8 - setup can be automated using 9 - <link linkend="opt-services.sourcehut.enable">services.sourcehut</link>. 10 - </para> 11 - <section xml:id="module-services-sourcehut-basic-usage"> 12 - <title>Basic usage</title> 13 - <para> 14 - Sourcehut is a Python and Go based set of applications. This NixOS 15 - module also provides basic configuration integrating Sourcehut 16 - into locally running <literal>services.nginx</literal>, 17 - <literal>services.redis.servers.sourcehut</literal>, 18 - <literal>services.postfix</literal> and 19 - <literal>services.postgresql</literal> services. 20 - </para> 21 - <para> 22 - A very basic configuration may look like this: 23 - </para> 24 - <programlisting> 25 - { pkgs, ... }: 26 - let 27 - fqdn = 28 - let 29 - join = hostName: domain: hostName + optionalString (domain != null) &quot;.${domain}&quot;; 30 - in join config.networking.hostName config.networking.domain; 31 - in { 32 - 33 - networking = { 34 - hostName = &quot;srht&quot;; 35 - domain = &quot;tld&quot;; 36 - firewall.allowedTCPPorts = [ 22 80 443 ]; 37 - }; 38 - 39 - services.sourcehut = { 40 - enable = true; 41 - git.enable = true; 42 - man.enable = true; 43 - meta.enable = true; 44 - nginx.enable = true; 45 - postfix.enable = true; 46 - postgresql.enable = true; 47 - redis.enable = true; 48 - settings = { 49 - &quot;sr.ht&quot; = { 50 - environment = &quot;production&quot;; 51 - global-domain = fqdn; 52 - origin = &quot;https://${fqdn}&quot;; 53 - # Produce keys with srht-keygen from sourcehut.coresrht. 54 - network-key = &quot;/run/keys/path/to/network-key&quot;; 55 - service-key = &quot;/run/keys/path/to/service-key&quot;; 56 - }; 57 - webhooks.private-key= &quot;/run/keys/path/to/webhook-key&quot;; 58 - }; 59 - }; 60 - 61 - security.acme.certs.&quot;${fqdn}&quot;.extraDomainNames = [ 62 - &quot;meta.${fqdn}&quot; 63 - &quot;man.${fqdn}&quot; 64 - &quot;git.${fqdn}&quot; 65 - ]; 66 - 67 - services.nginx = { 68 - enable = true; 69 - # only recommendedProxySettings are strictly required, but the rest make sense as well. 70 - recommendedTlsSettings = true; 71 - recommendedOptimisation = true; 72 - recommendedGzipSettings = true; 73 - recommendedProxySettings = true; 74 - 75 - # Settings to setup what certificates are used for which endpoint. 76 - virtualHosts = { 77 - &quot;${fqdn}&quot;.enableACME = true; 78 - &quot;meta.${fqdn}&quot;.useACMEHost = fqdn: 79 - &quot;man.${fqdn}&quot;.useACMEHost = fqdn: 80 - &quot;git.${fqdn}&quot;.useACMEHost = fqdn: 81 - }; 82 - }; 83 - } 84 - </programlisting> 85 - <para> 86 - The <literal>hostName</literal> option is used internally to 87 - configure the nginx reverse-proxy. The <literal>settings</literal> 88 - attribute set is used by the configuration generator and the 89 - result is placed in <literal>/etc/sr.ht/config.ini</literal>. 90 - </para> 91 - </section> 92 - <section xml:id="module-services-sourcehut-configuration"> 93 - <title>Configuration</title> 94 - <para> 95 - All configuration parameters are also stored in 96 - <literal>/etc/sr.ht/config.ini</literal> which is generated by the 97 - module and linked from the store to ensure that all values from 98 - <literal>config.ini</literal> can be modified by the module. 99 - </para> 100 - </section> 101 - <section xml:id="module-services-sourcehut-httpd"> 102 - <title>Using an alternative webserver as reverse-proxy (e.g. 103 - <literal>httpd</literal>)</title> 104 - <para> 105 - By default, <literal>nginx</literal> is used as reverse-proxy for 106 - <literal>sourcehut</literal>. However, it’s possible to use e.g. 107 - <literal>httpd</literal> by explicitly disabling 108 - <literal>nginx</literal> using 109 - <xref linkend="opt-services.nginx.enable" /> and fixing the 110 - <literal>settings</literal>. 111 - </para> 112 - </section> 113 - </chapter>
+1 -1
nixos/modules/services/misc/taskserver/default.nix
··· 566 566 }) 567 567 ]; 568 568 569 - meta.doc = ./default.xml; 569 + meta.doc = ./default.md; 570 570 }
-130
nixos/modules/services/misc/taskserver/default.xml
··· 1 - <!-- Do not edit this file directly, edit its companion .md instead 2 - and regenerate this file using nixos/doc/manual/md-to-db.sh --> 3 - <chapter xmlns="http://docbook.org/ns/docbook" xmlns:xlink="http://www.w3.org/1999/xlink" xml:id="module-services-taskserver"> 4 - <title>Taskserver</title> 5 - <para> 6 - Taskserver is the server component of 7 - <link xlink:href="https://taskwarrior.org/">Taskwarrior</link>, a 8 - free and open source todo list application. 9 - </para> 10 - <para> 11 - <emphasis>Upstream documentation:</emphasis> 12 - <link xlink:href="https://taskwarrior.org/docs/#taskd">https://taskwarrior.org/docs/#taskd</link> 13 - </para> 14 - <section xml:id="module-services-taskserver-configuration"> 15 - <title>Configuration</title> 16 - <para> 17 - Taskserver does all of its authentication via TLS using client 18 - certificates, so you either need to roll your own CA or purchase a 19 - certificate from a known CA, which allows creation of client 20 - certificates. These certificates are usually advertised as 21 - <quote>server certificates</quote>. 22 - </para> 23 - <para> 24 - So in order to make it easier to handle your own CA, there is a 25 - helper tool called <command>nixos-taskserver</command> which 26 - manages the custom CA along with Taskserver organisations, users 27 - and groups. 28 - </para> 29 - <para> 30 - While the client certificates in Taskserver only authenticate 31 - whether a user is allowed to connect, every user has its own UUID 32 - which identifies it as an entity. 33 - </para> 34 - <para> 35 - With <command>nixos-taskserver</command> the client certificate is 36 - created along with the UUID of the user, so it handles all of the 37 - credentials needed in order to setup the Taskwarrior client to 38 - work with a Taskserver. 39 - </para> 40 - </section> 41 - <section xml:id="module-services-taskserver-nixos-taskserver-tool"> 42 - <title>The nixos-taskserver tool</title> 43 - <para> 44 - Because Taskserver by default only provides scripts to setup users 45 - imperatively, the <command>nixos-taskserver</command> tool is used 46 - for addition and deletion of organisations along with users and 47 - groups defined by 48 - <xref linkend="opt-services.taskserver.organisations" /> and as 49 - well for imperative set up. 50 - </para> 51 - <para> 52 - The tool is designed to not interfere if the command is used to 53 - manually set up some organisations, users or groups. 54 - </para> 55 - <para> 56 - For example if you add a new organisation using 57 - <command>nixos-taskserver org add foo</command>, the organisation 58 - is not modified and deleted no matter what you define in 59 - <option>services.taskserver.organisations</option>, even if you’re 60 - adding the same organisation in that option. 61 - </para> 62 - <para> 63 - The tool is modelled to imitate the official 64 - <command>taskd</command> command, documentation for each 65 - subcommand can be shown by using the <option>--help</option> 66 - switch. 67 - </para> 68 - </section> 69 - <section xml:id="module-services-taskserver-declarative-ca-management"> 70 - <title>Declarative/automatic CA management</title> 71 - <para> 72 - Everything is done according to what you specify in the module 73 - options, however in order to set up a Taskwarrior client for 74 - synchronisation with a Taskserver instance, you have to transfer 75 - the keys and certificates to the client machine. 76 - </para> 77 - <para> 78 - This is done using 79 - <command>nixos-taskserver user export $orgname $username</command> 80 - which is printing a shell script fragment to stdout which can 81 - either be used verbatim or adjusted to import the user on the 82 - client machine. 83 - </para> 84 - <para> 85 - For example, let’s say you have the following configuration: 86 - </para> 87 - <programlisting> 88 - { 89 - services.taskserver.enable = true; 90 - services.taskserver.fqdn = &quot;server&quot;; 91 - services.taskserver.listenHost = &quot;::&quot;; 92 - services.taskserver.organisations.my-company.users = [ &quot;alice&quot; ]; 93 - } 94 - </programlisting> 95 - <para> 96 - This creates an organisation called <literal>my-company</literal> 97 - with the user <literal>alice</literal>. 98 - </para> 99 - <para> 100 - Now in order to import the <literal>alice</literal> user to 101 - another machine <literal>alicebox</literal>, all we need to do is 102 - something like this: 103 - </para> 104 - <programlisting> 105 - $ ssh server nixos-taskserver user export my-company alice | sh 106 - </programlisting> 107 - <para> 108 - Of course, if no SSH daemon is available on the server you can 109 - also copy &amp; paste it directly into a shell. 110 - </para> 111 - <para> 112 - After this step the user should be set up and you can start 113 - synchronising your tasks for the first time with 114 - <command>task sync init</command> on <literal>alicebox</literal>. 115 - </para> 116 - <para> 117 - Subsequent synchronisation requests merely require the command 118 - <command>task sync</command> after that stage. 119 - </para> 120 - </section> 121 - <section xml:id="module-services-taskserver-manual-ca-management"> 122 - <title>Manual CA management</title> 123 - <para> 124 - If you set any options within 125 - <link linkend="opt-services.taskserver.pki.manual.ca.cert">service.taskserver.pki.manual</link>.*, 126 - <command>nixos-taskserver</command> won’t issue certificates, but 127 - you can still use it for adding or removing user accounts. 128 - </para> 129 - </section> 130 - </chapter>
+1 -1
nixos/modules/services/misc/weechat.nix
··· 59 59 }; 60 60 }; 61 61 62 - meta.doc = ./weechat.xml; 62 + meta.doc = ./weechat.md; 63 63 }
-63
nixos/modules/services/misc/weechat.xml
··· 1 - <!-- Do not edit this file directly, edit its companion .md instead 2 - and regenerate this file using nixos/doc/manual/md-to-db.sh --> 3 - <chapter xmlns="http://docbook.org/ns/docbook" xmlns:xlink="http://www.w3.org/1999/xlink" xml:id="module-services-weechat"> 4 - <title>WeeChat</title> 5 - <para> 6 - <link xlink:href="https://weechat.org/">WeeChat</link> is a fast and 7 - extensible IRC client. 8 - </para> 9 - <section xml:id="module-services-weechat-basic-usage"> 10 - <title>Basic Usage</title> 11 - <para> 12 - By default, the module creates a 13 - <link xlink:href="https://www.freedesktop.org/wiki/Software/systemd/"><literal>systemd</literal></link> 14 - unit which runs the chat client in a detached 15 - <link xlink:href="https://www.gnu.org/software/screen/"><literal>screen</literal></link> 16 - session. 17 - </para> 18 - <para> 19 - This can be done by enabling the <literal>weechat</literal> 20 - service: 21 - </para> 22 - <programlisting> 23 - { ... }: 24 - 25 - { 26 - services.weechat.enable = true; 27 - } 28 - </programlisting> 29 - <para> 30 - The service is managed by a dedicated user named 31 - <literal>weechat</literal> in the state directory 32 - <literal>/var/lib/weechat</literal>. 33 - </para> 34 - </section> 35 - <section xml:id="module-services-weechat-reattach"> 36 - <title>Re-attaching to WeeChat</title> 37 - <para> 38 - WeeChat runs in a screen session owned by a dedicated user. To 39 - explicitly allow your another user to attach to this session, the 40 - <literal>screenrc</literal> needs to be tweaked by adding 41 - <link xlink:href="https://www.gnu.org/software/screen/manual/html_node/Multiuser.html#Multiuser">multiuser</link> 42 - support: 43 - </para> 44 - <programlisting> 45 - { 46 - programs.screen.screenrc = '' 47 - multiuser on 48 - acladd normal_user 49 - ''; 50 - } 51 - </programlisting> 52 - <para> 53 - Now, the session can be re-attached like this: 54 - </para> 55 - <programlisting> 56 - screen -x weechat/weechat-screen 57 - </programlisting> 58 - <para> 59 - <emphasis>The session name can be changed using 60 - <link xlink:href="options.html#opt-services.weechat.sessionName">services.weechat.sessionName.</link></emphasis> 61 - </para> 62 - </section> 63 - </chapter>
+1 -1
nixos/modules/services/monitoring/parsedmarc.nix
··· 539 539 }; 540 540 }; 541 541 542 - meta.doc = ./parsedmarc.xml; 542 + meta.doc = ./parsedmarc.md; 543 543 meta.maintainers = [ lib.maintainers.talyz ]; 544 544 }
-126
nixos/modules/services/monitoring/parsedmarc.xml
··· 1 - <!-- Do not edit this file directly, edit its companion .md instead 2 - and regenerate this file using nixos/doc/manual/md-to-db.sh --> 3 - <chapter xmlns="http://docbook.org/ns/docbook" xmlns:xlink="http://www.w3.org/1999/xlink" xml:id="module-services-parsedmarc"> 4 - <title>parsedmarc</title> 5 - <para> 6 - <link xlink:href="https://domainaware.github.io/parsedmarc/">parsedmarc</link> 7 - is a service which parses incoming 8 - <link xlink:href="https://dmarc.org/">DMARC</link> reports and 9 - stores or sends them to a downstream service for further analysis. 10 - In combination with Elasticsearch, Grafana and the included Grafana 11 - dashboard, it provides a handy overview of DMARC reports over time. 12 - </para> 13 - <section xml:id="module-services-parsedmarc-basic-usage"> 14 - <title>Basic usage</title> 15 - <para> 16 - A very minimal setup which reads incoming reports from an external 17 - email address and saves them to a local Elasticsearch instance 18 - looks like this: 19 - </para> 20 - <programlisting language="nix"> 21 - services.parsedmarc = { 22 - enable = true; 23 - settings.imap = { 24 - host = &quot;imap.example.com&quot;; 25 - user = &quot;alice@example.com&quot;; 26 - password = &quot;/path/to/imap_password_file&quot;; 27 - }; 28 - provision.geoIp = false; # Not recommended! 29 - }; 30 - </programlisting> 31 - <para> 32 - Note that GeoIP provisioning is disabled in the example for 33 - simplicity, but should be turned on for fully functional reports. 34 - </para> 35 - </section> 36 - <section xml:id="module-services-parsedmarc-local-mail"> 37 - <title>Local mail</title> 38 - <para> 39 - Instead of watching an external inbox, a local inbox can be 40 - automatically provisioned. The recipient’s name is by default set 41 - to <literal>dmarc</literal>, but can be configured in 42 - <link xlink:href="options.html#opt-services.parsedmarc.provision.localMail.recipientName">services.parsedmarc.provision.localMail.recipientName</link>. 43 - You need to add an MX record pointing to the host. More 44 - concretely: for the example to work, an MX record needs to be set 45 - up for <literal>monitoring.example.com</literal> and the complete 46 - email address that should be configured in the domain’s dmarc 47 - policy is <literal>dmarc@monitoring.example.com</literal>. 48 - </para> 49 - <programlisting language="nix"> 50 - services.parsedmarc = { 51 - enable = true; 52 - provision = { 53 - localMail = { 54 - enable = true; 55 - hostname = monitoring.example.com; 56 - }; 57 - geoIp = false; # Not recommended! 58 - }; 59 - }; 60 - </programlisting> 61 - </section> 62 - <section xml:id="module-services-parsedmarc-grafana-geoip"> 63 - <title>Grafana and GeoIP</title> 64 - <para> 65 - The reports can be visualized and summarized with parsedmarc’s 66 - official Grafana dashboard. For all views to work, and for the 67 - data to be complete, GeoIP databases are also required. The 68 - following example shows a basic deployment where the provisioned 69 - Elasticsearch instance is automatically added as a Grafana 70 - datasource, and the dashboard is added to Grafana as well. 71 - </para> 72 - <programlisting language="nix"> 73 - services.parsedmarc = { 74 - enable = true; 75 - provision = { 76 - localMail = { 77 - enable = true; 78 - hostname = url; 79 - }; 80 - grafana = { 81 - datasource = true; 82 - dashboard = true; 83 - }; 84 - }; 85 - }; 86 - 87 - # Not required, but recommended for full functionality 88 - services.geoipupdate = { 89 - settings = { 90 - AccountID = 000000; 91 - LicenseKey = &quot;/path/to/license_key_file&quot;; 92 - }; 93 - }; 94 - 95 - services.grafana = { 96 - enable = true; 97 - addr = &quot;0.0.0.0&quot;; 98 - domain = url; 99 - rootUrl = &quot;https://&quot; + url; 100 - protocol = &quot;socket&quot;; 101 - security = { 102 - adminUser = &quot;admin&quot;; 103 - adminPasswordFile = &quot;/path/to/admin_password_file&quot;; 104 - secretKeyFile = &quot;/path/to/secret_key_file&quot;; 105 - }; 106 - }; 107 - 108 - services.nginx = { 109 - enable = true; 110 - recommendedTlsSettings = true; 111 - recommendedOptimisation = true; 112 - recommendedGzipSettings = true; 113 - recommendedProxySettings = true; 114 - upstreams.grafana.servers.&quot;unix:/${config.services.grafana.socket}&quot; = {}; 115 - virtualHosts.${url} = { 116 - root = config.services.grafana.staticRootPath; 117 - enableACME = true; 118 - forceSSL = true; 119 - locations.&quot;/&quot;.tryFiles = &quot;$uri @grafana&quot;; 120 - locations.&quot;@grafana&quot;.proxyPass = &quot;http://grafana&quot;; 121 - }; 122 - }; 123 - users.users.nginx.extraGroups = [ &quot;grafana&quot; ]; 124 - </programlisting> 125 - </section> 126 - </chapter>
+1 -1
nixos/modules/services/monitoring/prometheus/exporters.nix
··· 323 323 ); 324 324 325 325 meta = { 326 - doc = ./exporters.xml; 326 + doc = ./exporters.md; 327 327 maintainers = [ maintainers.willibutz ]; 328 328 }; 329 329 }
-245
nixos/modules/services/monitoring/prometheus/exporters.xml
··· 1 - <!-- Do not edit this file directly, edit its companion .md instead 2 - and regenerate this file using nixos/doc/manual/md-to-db.sh --> 3 - <chapter xmlns="http://docbook.org/ns/docbook" xmlns:xlink="http://www.w3.org/1999/xlink" xml:id="module-services-prometheus-exporters"> 4 - <title>Prometheus exporters</title> 5 - <para> 6 - Prometheus exporters provide metrics for the 7 - <link xlink:href="https://prometheus.io">prometheus monitoring 8 - system</link>. 9 - </para> 10 - <section xml:id="module-services-prometheus-exporters-configuration"> 11 - <title>Configuration</title> 12 - <para> 13 - One of the most common exporters is the 14 - <link xlink:href="https://github.com/prometheus/node_exporter">node 15 - exporter</link>, it provides hardware and OS metrics from the host 16 - it’s running on. The exporter could be configured as follows: 17 - </para> 18 - <programlisting> 19 - services.prometheus.exporters.node = { 20 - enable = true; 21 - port = 9100; 22 - enabledCollectors = [ 23 - &quot;logind&quot; 24 - &quot;systemd&quot; 25 - ]; 26 - disabledCollectors = [ 27 - &quot;textfile&quot; 28 - ]; 29 - openFirewall = true; 30 - firewallFilter = &quot;-i br0 -p tcp -m tcp --dport 9100&quot;; 31 - }; 32 - </programlisting> 33 - <para> 34 - It should now serve all metrics from the collectors that are 35 - explicitly enabled and the ones that are 36 - <link xlink:href="https://github.com/prometheus/node_exporter#enabled-by-default">enabled 37 - by default</link>, via http under <literal>/metrics</literal>. In 38 - this example the firewall should just allow incoming connections 39 - to the exporter’s port on the bridge interface 40 - <literal>br0</literal> (this would have to be configured 41 - separately of course). For more information about configuration 42 - see <literal>man configuration.nix</literal> or search through the 43 - <link xlink:href="https://nixos.org/nixos/options.html#prometheus.exporters">available 44 - options</link>. 45 - </para> 46 - <para> 47 - Prometheus can now be configured to consume the metrics produced 48 - by the exporter: 49 - </para> 50 - <programlisting> 51 - services.prometheus = { 52 - # ... 53 - 54 - scrapeConfigs = [ 55 - { 56 - job_name = &quot;node&quot;; 57 - static_configs = [{ 58 - targets = [ &quot;localhost:${toString config.services.prometheus.exporters.node.port}&quot; ]; 59 - }]; 60 - } 61 - ]; 62 - 63 - # ... 64 - } 65 - </programlisting> 66 - </section> 67 - <section xml:id="module-services-prometheus-exporters-new-exporter"> 68 - <title>Adding a new exporter</title> 69 - <para> 70 - To add a new exporter, it has to be packaged first (see 71 - <literal>nixpkgs/pkgs/servers/monitoring/prometheus/</literal> for 72 - examples), then a module can be added. The postfix exporter is 73 - used in this example: 74 - </para> 75 - <itemizedlist> 76 - <listitem> 77 - <para> 78 - Some default options for all exporters are provided by 79 - <literal>nixpkgs/nixos/modules/services/monitoring/prometheus/exporters.nix</literal>: 80 - </para> 81 - <itemizedlist spacing="compact"> 82 - <listitem> 83 - <para> 84 - <literal>enable</literal> 85 - </para> 86 - </listitem> 87 - <listitem> 88 - <para> 89 - <literal>port</literal> 90 - </para> 91 - </listitem> 92 - <listitem> 93 - <para> 94 - <literal>listenAddress</literal> 95 - </para> 96 - </listitem> 97 - <listitem> 98 - <para> 99 - <literal>extraFlags</literal> 100 - </para> 101 - </listitem> 102 - <listitem> 103 - <para> 104 - <literal>openFirewall</literal> 105 - </para> 106 - </listitem> 107 - <listitem> 108 - <para> 109 - <literal>firewallFilter</literal> 110 - </para> 111 - </listitem> 112 - <listitem> 113 - <para> 114 - <literal>user</literal> 115 - </para> 116 - </listitem> 117 - <listitem> 118 - <para> 119 - <literal>group</literal> 120 - </para> 121 - </listitem> 122 - </itemizedlist> 123 - </listitem> 124 - <listitem> 125 - <para> 126 - As there is already a package available, the module can now be 127 - added. This is accomplished by adding a new file to the 128 - <literal>nixos/modules/services/monitoring/prometheus/exporters/</literal> 129 - directory, which will be called postfix.nix and contains all 130 - exporter specific options and configuration: 131 - </para> 132 - <programlisting> 133 - # nixpgs/nixos/modules/services/prometheus/exporters/postfix.nix 134 - { config, lib, pkgs, options }: 135 - 136 - with lib; 137 - 138 - let 139 - # for convenience we define cfg here 140 - cfg = config.services.prometheus.exporters.postfix; 141 - in 142 - { 143 - port = 9154; # The postfix exporter listens on this port by default 144 - 145 - # `extraOpts` is an attribute set which contains additional options 146 - # (and optional overrides for default options). 147 - # Note that this attribute is optional. 148 - extraOpts = { 149 - telemetryPath = mkOption { 150 - type = types.str; 151 - default = &quot;/metrics&quot;; 152 - description = '' 153 - Path under which to expose metrics. 154 - ''; 155 - }; 156 - logfilePath = mkOption { 157 - type = types.path; 158 - default = /var/log/postfix_exporter_input.log; 159 - example = /var/log/mail.log; 160 - description = '' 161 - Path where Postfix writes log entries. 162 - This file will be truncated by this exporter! 163 - ''; 164 - }; 165 - showqPath = mkOption { 166 - type = types.path; 167 - default = /var/spool/postfix/public/showq; 168 - example = /var/lib/postfix/queue/public/showq; 169 - description = '' 170 - Path at which Postfix places its showq socket. 171 - ''; 172 - }; 173 - }; 174 - 175 - # `serviceOpts` is an attribute set which contains configuration 176 - # for the exporter's systemd service. One of 177 - # `serviceOpts.script` and `serviceOpts.serviceConfig.ExecStart` 178 - # has to be specified here. This will be merged with the default 179 - # service configuration. 180 - # Note that by default 'DynamicUser' is 'true'. 181 - serviceOpts = { 182 - serviceConfig = { 183 - DynamicUser = false; 184 - ExecStart = '' 185 - ${pkgs.prometheus-postfix-exporter}/bin/postfix_exporter \ 186 - --web.listen-address ${cfg.listenAddress}:${toString cfg.port} \ 187 - --web.telemetry-path ${cfg.telemetryPath} \ 188 - ${concatStringsSep &quot; \\\n &quot; cfg.extraFlags} 189 - ''; 190 - }; 191 - }; 192 - } 193 - </programlisting> 194 - </listitem> 195 - <listitem> 196 - <para> 197 - This should already be enough for the postfix exporter. 198 - Additionally one could now add assertions and conditional 199 - default values. This can be done in the 200 - <quote>meta-module</quote> that combines all exporter 201 - definitions and generates the submodules: 202 - <literal>nixpkgs/nixos/modules/services/prometheus/exporters.nix</literal> 203 - </para> 204 - </listitem> 205 - </itemizedlist> 206 - </section> 207 - <section xml:id="module-services-prometheus-exporters-update-exporter-module"> 208 - <title>Updating an exporter module</title> 209 - <para> 210 - Should an exporter option change at some point, it is possible to 211 - add information about the change to the exporter definition 212 - similar to <literal>nixpkgs/nixos/modules/rename.nix</literal>: 213 - </para> 214 - <programlisting> 215 - { config, lib, pkgs, options }: 216 - 217 - with lib; 218 - 219 - let 220 - cfg = config.services.prometheus.exporters.nginx; 221 - in 222 - { 223 - port = 9113; 224 - extraOpts = { 225 - # additional module options 226 - # ... 227 - }; 228 - serviceOpts = { 229 - # service configuration 230 - # ... 231 - }; 232 - imports = [ 233 - # 'services.prometheus.exporters.nginx.telemetryEndpoint' -&gt; 'services.prometheus.exporters.nginx.telemetryPath' 234 - (mkRenamedOptionModule [ &quot;telemetryEndpoint&quot; ] [ &quot;telemetryPath&quot; ]) 235 - 236 - # removed option 'services.prometheus.exporters.nginx.insecure' 237 - (mkRemovedOptionModule [ &quot;insecure&quot; ] '' 238 - This option was replaced by 'prometheus.exporters.nginx.sslVerify' which defaults to true. 239 - '') 240 - ({ options.warnings = options.warnings; }) 241 - ]; 242 - } 243 - </programlisting> 244 - </section> 245 - </chapter>
+1 -1
nixos/modules/services/network-filesystems/litestream/default.nix
··· 95 95 users.groups.litestream = {}; 96 96 }; 97 97 98 - meta.doc = ./default.xml; 98 + meta.doc = ./default.md; 99 99 }
-62
nixos/modules/services/network-filesystems/litestream/default.xml
··· 1 - <!-- Do not edit this file directly, edit its companion .md instead 2 - and regenerate this file using nixos/doc/manual/md-to-db.sh --> 3 - <chapter xmlns="http://docbook.org/ns/docbook" xmlns:xlink="http://www.w3.org/1999/xlink" xml:id="module-services-litestream"> 4 - <title>Litestream</title> 5 - <para> 6 - <link xlink:href="https://litestream.io/">Litestream</link> is a 7 - standalone streaming replication tool for SQLite. 8 - </para> 9 - <section xml:id="module-services-litestream-configuration"> 10 - <title>Configuration</title> 11 - <para> 12 - Litestream service is managed by a dedicated user named 13 - <literal>litestream</literal> which needs permission to the 14 - database file. Here’s an example config which gives required 15 - permissions to access 16 - <link linkend="opt-services.grafana.settings.database.path">grafana 17 - database</link>: 18 - </para> 19 - <programlisting> 20 - { pkgs, ... }: 21 - { 22 - users.users.litestream.extraGroups = [ &quot;grafana&quot; ]; 23 - 24 - systemd.services.grafana.serviceConfig.ExecStartPost = &quot;+&quot; + pkgs.writeShellScript &quot;grant-grafana-permissions&quot; '' 25 - timeout=10 26 - 27 - while [ ! -f /var/lib/grafana/data/grafana.db ]; 28 - do 29 - if [ &quot;$timeout&quot; == 0 ]; then 30 - echo &quot;ERROR: Timeout while waiting for /var/lib/grafana/data/grafana.db.&quot; 31 - exit 1 32 - fi 33 - 34 - sleep 1 35 - 36 - ((timeout--)) 37 - done 38 - 39 - find /var/lib/grafana -type d -exec chmod -v 775 {} \; 40 - find /var/lib/grafana -type f -exec chmod -v 660 {} \; 41 - ''; 42 - 43 - services.litestream = { 44 - enable = true; 45 - 46 - environmentFile = &quot;/run/secrets/litestream&quot;; 47 - 48 - settings = { 49 - dbs = [ 50 - { 51 - path = &quot;/var/lib/grafana/data/grafana.db&quot;; 52 - replicas = [{ 53 - url = &quot;s3://mybkt.litestream.io/grafana&quot;; 54 - }]; 55 - } 56 - ]; 57 - }; 58 - }; 59 - } 60 - </programlisting> 61 - </section> 62 - </chapter>
+1 -1
nixos/modules/services/networking/firefox-syncserver.nix
··· 311 311 312 312 meta = { 313 313 maintainers = with lib.maintainers; [ pennae ]; 314 - doc = ./firefox-syncserver.xml; 314 + doc = ./firefox-syncserver.md; 315 315 }; 316 316 }
-79
nixos/modules/services/networking/firefox-syncserver.xml
··· 1 - <!-- Do not edit this file directly, edit its companion .md instead 2 - and regenerate this file using nixos/doc/manual/md-to-db.sh --> 3 - <chapter xmlns="http://docbook.org/ns/docbook" xmlns:xlink="http://www.w3.org/1999/xlink" xml:id="module-services-firefox-syncserver"> 4 - <title>Firefox Sync server</title> 5 - <para> 6 - A storage server for Firefox Sync that you can easily host yourself. 7 - </para> 8 - <section xml:id="module-services-firefox-syncserver-quickstart"> 9 - <title>Quickstart</title> 10 - <para> 11 - The absolute minimal configuration for the sync server looks like 12 - this: 13 - </para> 14 - <programlisting language="nix"> 15 - services.mysql.package = pkgs.mariadb; 16 - 17 - services.firefox-syncserver = { 18 - enable = true; 19 - secrets = builtins.toFile &quot;sync-secrets&quot; '' 20 - SYNC_MASTER_SECRET=this-secret-is-actually-leaked-to-/nix/store 21 - ''; 22 - singleNode = { 23 - enable = true; 24 - hostname = &quot;localhost&quot;; 25 - url = &quot;http://localhost:5000&quot;; 26 - }; 27 - }; 28 - </programlisting> 29 - <para> 30 - This will start a sync server that is only accessible locally. 31 - Once the services is running you can navigate to 32 - <literal>about:config</literal> in your Firefox profile and set 33 - <literal>identity.sync.tokenserver.uri</literal> to 34 - <literal>http://localhost:5000/1.0/sync/1.5</literal>. Your 35 - browser will now use your local sync server for data storage. 36 - </para> 37 - <warning> 38 - <para> 39 - This configuration should never be used in production. It is not 40 - encrypted and stores its secrets in a world-readable location. 41 - </para> 42 - </warning> 43 - </section> 44 - <section xml:id="module-services-firefox-syncserver-configuration"> 45 - <title>More detailed setup</title> 46 - <para> 47 - The <literal>firefox-syncserver</literal> service provides a 48 - number of options to make setting up small deployment easier. 49 - These are grouped under the <literal>singleNode</literal> element 50 - of the option tree and allow simple configuration of the most 51 - important parameters. 52 - </para> 53 - <para> 54 - Single node setup is split into two kinds of options: those that 55 - affect the sync server itself, and those that affect its 56 - surroundings. Options that affect the sync server are 57 - <literal>capacity</literal>, which configures how many accounts 58 - may be active on this instance, and <literal>url</literal>, which 59 - holds the URL under which the sync server can be accessed. The 60 - <literal>url</literal> can be configured automatically when using 61 - nginx. 62 - </para> 63 - <para> 64 - Options that affect the surroundings of the sync server are 65 - <literal>enableNginx</literal>, <literal>enableTLS</literal> and 66 - <literal>hostnam</literal>. If <literal>enableNginx</literal> is 67 - set the sync server module will automatically add an nginx virtual 68 - host to the system using <literal>hostname</literal> as the domain 69 - and set <literal>url</literal> accordingly. If 70 - <literal>enableTLS</literal> is set the module will also enable 71 - ACME certificates on the new virtual host and force all 72 - connections to be made via TLS. 73 - </para> 74 - <para> 75 - For actual deployment it is also recommended to store the 76 - <literal>secrets</literal> file in a secure location. 77 - </para> 78 - </section> 79 - </chapter>
+1 -1
nixos/modules/services/networking/mosquitto.nix
··· 671 671 672 672 meta = { 673 673 maintainers = with lib.maintainers; [ pennae ]; 674 - doc = ./mosquitto.xml; 674 + doc = ./mosquitto.md; 675 675 }; 676 676 }
-149
nixos/modules/services/networking/mosquitto.xml
··· 1 - <!-- Do not edit this file directly, edit its companion .md instead 2 - and regenerate this file using nixos/doc/manual/md-to-db.sh --> 3 - <chapter xmlns="http://docbook.org/ns/docbook" xmlns:xlink="http://www.w3.org/1999/xlink" xml:id="module-services-mosquitto"> 4 - <title>Mosquitto</title> 5 - <para> 6 - Mosquitto is a MQTT broker often used for IoT or home automation 7 - data transport. 8 - </para> 9 - <section xml:id="module-services-mosquitto-quickstart"> 10 - <title>Quickstart</title> 11 - <para> 12 - A minimal configuration for Mosquitto is 13 - </para> 14 - <programlisting language="nix"> 15 - services.mosquitto = { 16 - enable = true; 17 - listeners = [ { 18 - acl = [ &quot;pattern readwrite #&quot; ]; 19 - omitPasswordAuth = true; 20 - settings.allow_anonymous = true; 21 - } ]; 22 - }; 23 - </programlisting> 24 - <para> 25 - This will start a broker on port 1883, listening on all interfaces 26 - of the machine, allowing read/write access to all topics to any 27 - user without password requirements. 28 - </para> 29 - <para> 30 - User authentication can be configured with the 31 - <literal>users</literal> key of listeners. A config that gives 32 - full read access to a user <literal>monitor</literal> and 33 - restricted write access to a user <literal>service</literal> could 34 - look like 35 - </para> 36 - <programlisting language="nix"> 37 - services.mosquitto = { 38 - enable = true; 39 - listeners = [ { 40 - users = { 41 - monitor = { 42 - acl = [ &quot;read #&quot; ]; 43 - password = &quot;monitor&quot;; 44 - }; 45 - service = { 46 - acl = [ &quot;write service/#&quot; ]; 47 - password = &quot;service&quot;; 48 - }; 49 - }; 50 - } ]; 51 - }; 52 - </programlisting> 53 - <para> 54 - TLS authentication is configured by setting TLS-related options of 55 - the listener: 56 - </para> 57 - <programlisting language="nix"> 58 - services.mosquitto = { 59 - enable = true; 60 - listeners = [ { 61 - port = 8883; # port change is not required, but helpful to avoid mistakes 62 - # ... 63 - settings = { 64 - cafile = &quot;/path/to/mqtt.ca.pem&quot;; 65 - certfile = &quot;/path/to/mqtt.pem&quot;; 66 - keyfile = &quot;/path/to/mqtt.key&quot;; 67 - }; 68 - } ]; 69 - </programlisting> 70 - </section> 71 - <section xml:id="module-services-mosquitto-config"> 72 - <title>Configuration</title> 73 - <para> 74 - The Mosquitto configuration has four distinct types of settings: 75 - the global settings of the daemon, listeners, plugins, and 76 - bridges. Bridges and listeners are part of the global 77 - configuration, plugins are part of listeners. Users of the broker 78 - are configured as parts of listeners rather than globally, 79 - allowing configurations in which a given user is only allowed to 80 - log in to the broker using specific listeners (eg to configure an 81 - admin user with full access to all topics, but restricted to 82 - localhost). 83 - </para> 84 - <para> 85 - Almost all options of Mosquitto are available for configuration at 86 - their appropriate levels, some as NixOS options written in camel 87 - case, the remainders under <literal>settings</literal> with their 88 - exact names in the Mosquitto config file. The exceptions are 89 - <literal>acl_file</literal> (which is always set according to the 90 - <literal>acl</literal> attributes of a listener and its users) and 91 - <literal>per_listener_settings</literal> (which is always set to 92 - <literal>true</literal>). 93 - </para> 94 - <section xml:id="module-services-mosquitto-config-passwords"> 95 - <title>Password authentication</title> 96 - <para> 97 - Mosquitto can be run in two modes, with a password file or 98 - without. Each listener has its own password file, and different 99 - listeners may use different password files. Password file 100 - generation can be disabled by setting 101 - <literal>omitPasswordAuth = true</literal> for a listener; in 102 - this case it is necessary to either set 103 - <literal>settings.allow_anonymous = true</literal> to allow all 104 - logins, or to configure other authentication methods like TLS 105 - client certificates with 106 - <literal>settings.use_identity_as_username = true</literal>. 107 - </para> 108 - <para> 109 - The default is to generate a password file for each listener 110 - from the users configured to that listener. Users with no 111 - configured password will not be added to the password file and 112 - thus will not be able to use the broker. 113 - </para> 114 - </section> 115 - <section xml:id="module-services-mosquitto-config-acl"> 116 - <title>ACL format</title> 117 - <para> 118 - Every listener has a Mosquitto <literal>acl_file</literal> 119 - attached to it. This ACL is configured via two attributes of the 120 - config: 121 - </para> 122 - <itemizedlist spacing="compact"> 123 - <listitem> 124 - <para> 125 - the <literal>acl</literal> attribute of the listener 126 - configures pattern ACL entries and topic ACL entries for 127 - anonymous users. Each entry must be prefixed with 128 - <literal>pattern</literal> or <literal>topic</literal> to 129 - distinguish between these two cases. 130 - </para> 131 - </listitem> 132 - <listitem> 133 - <para> 134 - the <literal>acl</literal> attribute of every user 135 - configures in the listener configured the ACL for that given 136 - user. Only topic ACLs are supported by Mosquitto in this 137 - setting, so no prefix is required or allowed. 138 - </para> 139 - </listitem> 140 - </itemizedlist> 141 - <para> 142 - The default ACL for a listener is empty, disallowing all 143 - accesses from all clients. To configure a completely open ACL, 144 - set <literal>acl = [ &quot;pattern readwrite #&quot; ]</literal> 145 - in the listener. 146 - </para> 147 - </section> 148 - </section> 149 - </chapter>
+1 -1
nixos/modules/services/networking/pleroma.nix
··· 147 147 148 148 }; 149 149 meta.maintainers = with lib.maintainers; [ ninjatrappeur ]; 150 - meta.doc = ./pleroma.xml; 150 + meta.doc = ./pleroma.md; 151 151 }
-244
nixos/modules/services/networking/pleroma.xml
··· 1 - <!-- Do not edit this file directly, edit its companion .md instead 2 - and regenerate this file using nixos/doc/manual/md-to-db.sh --> 3 - <chapter xmlns="http://docbook.org/ns/docbook" xmlns:xlink="http://www.w3.org/1999/xlink" xml:id="module-services-pleroma"> 4 - <title>Pleroma</title> 5 - <para> 6 - <link xlink:href="https://pleroma.social/">Pleroma</link> is a 7 - lightweight activity pub server. 8 - </para> 9 - <section xml:id="module-services-pleroma-generate-config"> 10 - <title>Generating the Pleroma config</title> 11 - <para> 12 - The <literal>pleroma_ctl</literal> CLI utility will prompt you 13 - some questions and it will generate an initial config file. This 14 - is an example of usage 15 - </para> 16 - <programlisting> 17 - $ mkdir tmp-pleroma 18 - $ cd tmp-pleroma 19 - $ nix-shell -p pleroma-otp 20 - $ pleroma_ctl instance gen --output config.exs --output-psql setup.psql 21 - </programlisting> 22 - <para> 23 - The <literal>config.exs</literal> file can be further customized 24 - following the instructions on the 25 - <link xlink:href="https://docs-develop.pleroma.social/backend/configuration/cheatsheet/">upstream 26 - documentation</link>. Many refinements can be applied also after 27 - the service is running. 28 - </para> 29 - </section> 30 - <section xml:id="module-services-pleroma-initialize-db"> 31 - <title>Initializing the database</title> 32 - <para> 33 - First, the Postgresql service must be enabled in the NixOS 34 - configuration 35 - </para> 36 - <programlisting> 37 - services.postgresql = { 38 - enable = true; 39 - package = pkgs.postgresql_13; 40 - }; 41 - </programlisting> 42 - <para> 43 - and activated with the usual 44 - </para> 45 - <programlisting> 46 - $ nixos-rebuild switch 47 - </programlisting> 48 - <para> 49 - Then you can create and seed the database, using the 50 - <literal>setup.psql</literal> file that you generated in the 51 - previous section, by running 52 - </para> 53 - <programlisting> 54 - $ sudo -u postgres psql -f setup.psql 55 - </programlisting> 56 - </section> 57 - <section xml:id="module-services-pleroma-enable"> 58 - <title>Enabling the Pleroma service locally</title> 59 - <para> 60 - In this section we will enable the Pleroma service only locally, 61 - so its configurations can be improved incrementally. 62 - </para> 63 - <para> 64 - This is an example of configuration, where 65 - <xref linkend="opt-services.pleroma.configs" /> option contains 66 - the content of the file <literal>config.exs</literal>, generated 67 - <link linkend="module-services-pleroma-generate-config">in the 68 - first section</link>, but with the secrets (database password, 69 - endpoint secret key, salts, etc.) removed. Removing secrets is 70 - important, because otherwise they will be stored publicly in the 71 - Nix store. 72 - </para> 73 - <programlisting> 74 - services.pleroma = { 75 - enable = true; 76 - secretConfigFile = &quot;/var/lib/pleroma/secrets.exs&quot;; 77 - configs = [ 78 - '' 79 - import Config 80 - 81 - config :pleroma, Pleroma.Web.Endpoint, 82 - url: [host: &quot;pleroma.example.net&quot;, scheme: &quot;https&quot;, port: 443], 83 - http: [ip: {127, 0, 0, 1}, port: 4000] 84 - 85 - config :pleroma, :instance, 86 - name: &quot;Test&quot;, 87 - email: &quot;admin@example.net&quot;, 88 - notify_email: &quot;admin@example.net&quot;, 89 - limit: 5000, 90 - registrations_open: true 91 - 92 - config :pleroma, :media_proxy, 93 - enabled: false, 94 - redirect_on_failure: true 95 - 96 - config :pleroma, Pleroma.Repo, 97 - adapter: Ecto.Adapters.Postgres, 98 - username: &quot;pleroma&quot;, 99 - database: &quot;pleroma&quot;, 100 - hostname: &quot;localhost&quot; 101 - 102 - # Configure web push notifications 103 - config :web_push_encryption, :vapid_details, 104 - subject: &quot;mailto:admin@example.net&quot; 105 - 106 - # ... TO CONTINUE ... 107 - '' 108 - ]; 109 - }; 110 - </programlisting> 111 - <para> 112 - Secrets must be moved into a file pointed by 113 - <xref linkend="opt-services.pleroma.secretConfigFile" />, in our 114 - case <literal>/var/lib/pleroma/secrets.exs</literal>. This file 115 - can be created copying the previously generated 116 - <literal>config.exs</literal> file and then removing all the 117 - settings, except the secrets. This is an example 118 - </para> 119 - <programlisting> 120 - # Pleroma instance passwords 121 - 122 - import Config 123 - 124 - config :pleroma, Pleroma.Web.Endpoint, 125 - secret_key_base: &quot;&lt;the secret generated by pleroma_ctl&gt;&quot;, 126 - signing_salt: &quot;&lt;the secret generated by pleroma_ctl&gt;&quot; 127 - 128 - config :pleroma, Pleroma.Repo, 129 - password: &quot;&lt;the secret generated by pleroma_ctl&gt;&quot; 130 - 131 - # Configure web push notifications 132 - config :web_push_encryption, :vapid_details, 133 - public_key: &quot;&lt;the secret generated by pleroma_ctl&gt;&quot;, 134 - private_key: &quot;&lt;the secret generated by pleroma_ctl&gt;&quot; 135 - 136 - # ... TO CONTINUE ... 137 - </programlisting> 138 - <para> 139 - Note that the lines of the same configuration group are comma 140 - separated (i.e. all the lines end with a comma, except the last 141 - one), so when the lines with passwords are added or removed, 142 - commas must be adjusted accordingly. 143 - </para> 144 - <para> 145 - The service can be enabled with the usual 146 - </para> 147 - <programlisting> 148 - $ nixos-rebuild switch 149 - </programlisting> 150 - <para> 151 - The service is accessible only from the local 152 - <literal>127.0.0.1:4000</literal> port. It can be tested using a 153 - port forwarding like this 154 - </para> 155 - <programlisting> 156 - $ ssh -L 4000:localhost:4000 myuser@example.net 157 - </programlisting> 158 - <para> 159 - and then accessing 160 - <link xlink:href="http://localhost:4000">http://localhost:4000</link> 161 - from a web browser. 162 - </para> 163 - </section> 164 - <section xml:id="module-services-pleroma-admin-user"> 165 - <title>Creating the admin user</title> 166 - <para> 167 - After Pleroma service is running, all 168 - <link xlink:href="https://docs-develop.pleroma.social/">Pleroma 169 - administration utilities</link> can be used. In particular an 170 - admin user can be created with 171 - </para> 172 - <programlisting> 173 - $ pleroma_ctl user new &lt;nickname&gt; &lt;email&gt; --admin --moderator --password &lt;password&gt; 174 - </programlisting> 175 - </section> 176 - <section xml:id="module-services-pleroma-nginx"> 177 - <title>Configuring Nginx</title> 178 - <para> 179 - In this configuration, Pleroma is listening only on the local port 180 - 4000. Nginx can be configured as a Reverse Proxy, for forwarding 181 - requests from public ports to the Pleroma service. This is an 182 - example of configuration, using 183 - <link xlink:href="https://letsencrypt.org/">Let’s Encrypt</link> 184 - for the TLS certificates 185 - </para> 186 - <programlisting> 187 - security.acme = { 188 - email = &quot;root@example.net&quot;; 189 - acceptTerms = true; 190 - }; 191 - 192 - services.nginx = { 193 - enable = true; 194 - addSSL = true; 195 - 196 - recommendedTlsSettings = true; 197 - recommendedOptimisation = true; 198 - recommendedGzipSettings = true; 199 - 200 - recommendedProxySettings = false; 201 - # NOTE: if enabled, the NixOS proxy optimizations will override the Pleroma 202 - # specific settings, and they will enter in conflict. 203 - 204 - virtualHosts = { 205 - &quot;pleroma.example.net&quot; = { 206 - http2 = true; 207 - enableACME = true; 208 - forceSSL = true; 209 - 210 - locations.&quot;/&quot; = { 211 - proxyPass = &quot;http://127.0.0.1:4000&quot;; 212 - 213 - extraConfig = '' 214 - etag on; 215 - gzip on; 216 - 217 - add_header 'Access-Control-Allow-Origin' '*' always; 218 - add_header 'Access-Control-Allow-Methods' 'POST, PUT, DELETE, GET, PATCH, OPTIONS' always; 219 - add_header 'Access-Control-Allow-Headers' 'Authorization, Content-Type, Idempotency-Key' always; 220 - add_header 'Access-Control-Expose-Headers' 'Link, X-RateLimit-Reset, X-RateLimit-Limit, X-RateLimit-Remaining, X-Request-Id' always; 221 - if ($request_method = OPTIONS) { 222 - return 204; 223 - } 224 - add_header X-XSS-Protection &quot;1; mode=block&quot;; 225 - add_header X-Permitted-Cross-Domain-Policies none; 226 - add_header X-Frame-Options DENY; 227 - add_header X-Content-Type-Options nosniff; 228 - add_header Referrer-Policy same-origin; 229 - add_header X-Download-Options noopen; 230 - proxy_http_version 1.1; 231 - proxy_set_header Upgrade $http_upgrade; 232 - proxy_set_header Connection &quot;upgrade&quot;; 233 - proxy_set_header Host $host; 234 - 235 - client_max_body_size 16m; 236 - # NOTE: increase if users need to upload very big files 237 - ''; 238 - }; 239 - }; 240 - }; 241 - }; 242 - </programlisting> 243 - </section> 244 - </chapter>
+1 -1
nixos/modules/services/networking/prosody.nix
··· 905 905 906 906 }; 907 907 908 - meta.doc = ./prosody.xml; 908 + meta.doc = ./prosody.md; 909 909 }
-92
nixos/modules/services/networking/prosody.xml
··· 1 - <!-- Do not edit this file directly, edit its companion .md instead 2 - and regenerate this file using nixos/doc/manual/md-to-db.sh --> 3 - <chapter xmlns="http://docbook.org/ns/docbook" xmlns:xlink="http://www.w3.org/1999/xlink" xml:id="module-services-prosody"> 4 - <title>Prosody</title> 5 - <para> 6 - <link xlink:href="https://prosody.im/">Prosody</link> is an 7 - open-source, modern XMPP server. 8 - </para> 9 - <section xml:id="module-services-prosody-basic-usage"> 10 - <title>Basic usage</title> 11 - <para> 12 - A common struggle for most XMPP newcomers is to find the right set 13 - of XMPP Extensions (XEPs) to setup. Forget to activate a few of 14 - those and your XMPP experience might turn into a nightmare! 15 - </para> 16 - <para> 17 - The XMPP community tackles this problem by creating a meta-XEP 18 - listing a decent set of XEPs you should implement. This meta-XEP 19 - is issued every year, the 2020 edition being 20 - <link xlink:href="https://xmpp.org/extensions/xep-0423.html">XEP-0423</link>. 21 - </para> 22 - <para> 23 - The NixOS Prosody module will implement most of these recommendend 24 - XEPs out of the box. That being said, two components still require 25 - some manual configuration: the 26 - <link xlink:href="https://xmpp.org/extensions/xep-0045.html">Multi 27 - User Chat (MUC)</link> and the 28 - <link xlink:href="https://xmpp.org/extensions/xep-0363.html">HTTP 29 - File Upload</link> ones. You’ll need to create a DNS subdomain for 30 - each of those. The current convention is to name your MUC endpoint 31 - <literal>conference.example.org</literal> and your HTTP upload 32 - domain <literal>upload.example.org</literal>. 33 - </para> 34 - <para> 35 - A good configuration to start with, including a 36 - <link xlink:href="https://xmpp.org/extensions/xep-0045.html">Multi 37 - User Chat (MUC)</link> endpoint as well as a 38 - <link xlink:href="https://xmpp.org/extensions/xep-0363.html">HTTP 39 - File Upload</link> endpoint will look like this: 40 - </para> 41 - <programlisting> 42 - services.prosody = { 43 - enable = true; 44 - admins = [ &quot;root@example.org&quot; ]; 45 - ssl.cert = &quot;/var/lib/acme/example.org/fullchain.pem&quot;; 46 - ssl.key = &quot;/var/lib/acme/example.org/key.pem&quot;; 47 - virtualHosts.&quot;example.org&quot; = { 48 - enabled = true; 49 - domain = &quot;example.org&quot;; 50 - ssl.cert = &quot;/var/lib/acme/example.org/fullchain.pem&quot;; 51 - ssl.key = &quot;/var/lib/acme/example.org/key.pem&quot;; 52 - }; 53 - muc = [ { 54 - domain = &quot;conference.example.org&quot;; 55 - } ]; 56 - uploadHttp = { 57 - domain = &quot;upload.example.org&quot;; 58 - }; 59 - }; 60 - </programlisting> 61 - </section> 62 - <section xml:id="module-services-prosody-letsencrypt"> 63 - <title>Let’s Encrypt Configuration</title> 64 - <para> 65 - As you can see in the code snippet from the 66 - <link linkend="module-services-prosody-basic-usage">previous 67 - section</link>, you’ll need a single TLS certificate covering your 68 - main endpoint, the MUC one as well as the HTTP Upload one. We can 69 - generate such a certificate by leveraging the ACME 70 - <link linkend="opt-security.acme.certs._name_.extraDomainNames">extraDomainNames</link> 71 - module option. 72 - </para> 73 - <para> 74 - Provided the setup detailed in the previous section, you’ll need 75 - the following acme configuration to generate a TLS certificate for 76 - the three endponits: 77 - </para> 78 - <programlisting> 79 - security.acme = { 80 - email = &quot;root@example.org&quot;; 81 - acceptTerms = true; 82 - certs = { 83 - &quot;example.org&quot; = { 84 - webroot = &quot;/var/www/example.org&quot;; 85 - email = &quot;root@example.org&quot;; 86 - extraDomainNames = [ &quot;conference.example.org&quot; &quot;upload.example.org&quot; ]; 87 - }; 88 - }; 89 - }; 90 - </programlisting> 91 - </section> 92 - </chapter>
+1 -1
nixos/modules/services/networking/yggdrasil.nix
··· 193 193 environment.systemPackages = [ cfg.package ]; 194 194 }); 195 195 meta = { 196 - doc = ./yggdrasil.xml; 196 + doc = ./yggdrasil.md; 197 197 maintainers = with lib.maintainers; [ gazally ehmry ]; 198 198 }; 199 199 }
-157
nixos/modules/services/networking/yggdrasil.xml
··· 1 - <!-- Do not edit this file directly, edit its companion .md instead 2 - and regenerate this file using nixos/doc/manual/md-to-db.sh --> 3 - <chapter xmlns="http://docbook.org/ns/docbook" xmlns:xlink="http://www.w3.org/1999/xlink" xml:id="module-services-networking-yggdrasil"> 4 - <title>Yggdrasil</title> 5 - <para> 6 - <emphasis>Source:</emphasis> 7 - <filename>modules/services/networking/yggdrasil/default.nix</filename> 8 - </para> 9 - <para> 10 - <emphasis>Upstream documentation:</emphasis> 11 - <link xlink:href="https://yggdrasil-network.github.io/">https://yggdrasil-network.github.io/</link> 12 - </para> 13 - <para> 14 - Yggdrasil is an early-stage implementation of a fully end-to-end 15 - encrypted, self-arranging IPv6 network. 16 - </para> 17 - <section xml:id="module-services-networking-yggdrasil-configuration"> 18 - <title>Configuration</title> 19 - <section xml:id="module-services-networking-yggdrasil-configuration-simple"> 20 - <title>Simple ephemeral node</title> 21 - <para> 22 - An annotated example of a simple configuration: 23 - </para> 24 - <programlisting> 25 - { 26 - services.yggdrasil = { 27 - enable = true; 28 - persistentKeys = false; 29 - # The NixOS module will generate new keys and a new IPv6 address each time 30 - # it is started if persistentKeys is not enabled. 31 - 32 - settings = { 33 - Peers = [ 34 - # Yggdrasil will automatically connect and &quot;peer&quot; with other nodes it 35 - # discovers via link-local multicast announcements. Unless this is the 36 - # case (it probably isn't) a node needs peers within the existing 37 - # network that it can tunnel to. 38 - &quot;tcp://1.2.3.4:1024&quot; 39 - &quot;tcp://1.2.3.5:1024&quot; 40 - # Public peers can be found at 41 - # https://github.com/yggdrasil-network/public-peers 42 - ]; 43 - }; 44 - }; 45 - } 46 - </programlisting> 47 - </section> 48 - <section xml:id="module-services-networking-yggdrasil-configuration-prefix"> 49 - <title>Persistent node with prefix</title> 50 - <para> 51 - A node with a fixed address that announces a prefix: 52 - </para> 53 - <programlisting> 54 - let 55 - address = &quot;210:5217:69c0:9afc:1b95:b9f:8718:c3d2&quot;; 56 - prefix = &quot;310:5217:69c0:9afc&quot;; 57 - # taken from the output of &quot;yggdrasilctl getself&quot;. 58 - in { 59 - 60 - services.yggdrasil = { 61 - enable = true; 62 - persistentKeys = true; # Maintain a fixed public key and IPv6 address. 63 - settings = { 64 - Peers = [ &quot;tcp://1.2.3.4:1024&quot; &quot;tcp://1.2.3.5:1024&quot; ]; 65 - NodeInfo = { 66 - # This information is visible to the network. 67 - name = config.networking.hostName; 68 - location = &quot;The North Pole&quot;; 69 - }; 70 - }; 71 - }; 72 - 73 - boot.kernel.sysctl.&quot;net.ipv6.conf.all.forwarding&quot; = 1; 74 - # Forward traffic under the prefix. 75 - 76 - networking.interfaces.${eth0}.ipv6.addresses = [{ 77 - # Set a 300::/8 address on the local physical device. 78 - address = prefix + &quot;::1&quot;; 79 - prefixLength = 64; 80 - }]; 81 - 82 - services.radvd = { 83 - # Announce the 300::/8 prefix to eth0. 84 - enable = true; 85 - config = '' 86 - interface eth0 87 - { 88 - AdvSendAdvert on; 89 - prefix ${prefix}::/64 { 90 - AdvOnLink on; 91 - AdvAutonomous on; 92 - }; 93 - route 200::/8 {}; 94 - }; 95 - ''; 96 - }; 97 - } 98 - </programlisting> 99 - </section> 100 - <section xml:id="module-services-networking-yggdrasil-configuration-container"> 101 - <title>Yggdrasil attached Container</title> 102 - <para> 103 - A NixOS container attached to the Yggdrasil network via a node 104 - running on the host: 105 - </para> 106 - <programlisting> 107 - let 108 - yggPrefix64 = &quot;310:5217:69c0:9afc&quot;; 109 - # Again, taken from the output of &quot;yggdrasilctl getself&quot;. 110 - in 111 - { 112 - boot.kernel.sysctl.&quot;net.ipv6.conf.all.forwarding&quot; = 1; 113 - # Enable IPv6 forwarding. 114 - 115 - networking = { 116 - bridges.br0.interfaces = [ ]; 117 - # A bridge only to containers… 118 - 119 - interfaces.br0 = { 120 - # … configured with a prefix address. 121 - ipv6.addresses = [{ 122 - address = &quot;${yggPrefix64}::1&quot;; 123 - prefixLength = 64; 124 - }]; 125 - }; 126 - }; 127 - 128 - containers.foo = { 129 - autoStart = true; 130 - privateNetwork = true; 131 - hostBridge = &quot;br0&quot;; 132 - # Attach the container to the bridge only. 133 - config = { config, pkgs, ... }: { 134 - networking.interfaces.eth0.ipv6 = { 135 - addresses = [{ 136 - # Configure a prefix address. 137 - address = &quot;${yggPrefix64}::2&quot;; 138 - prefixLength = 64; 139 - }]; 140 - routes = [{ 141 - # Configure the prefix route. 142 - address = &quot;200::&quot;; 143 - prefixLength = 7; 144 - via = &quot;${yggPrefix64}::1&quot;; 145 - }]; 146 - }; 147 - 148 - services.httpd.enable = true; 149 - networking.firewall.allowedTCPPorts = [ 80 ]; 150 - }; 151 - }; 152 - 153 - } 154 - </programlisting> 155 - </section> 156 - </section> 157 - </chapter>
+1 -1
nixos/modules/services/search/meilisearch.nix
··· 9 9 { 10 10 11 11 meta.maintainers = with maintainers; [ Br1ght0ne happysalada ]; 12 - meta.doc = ./meilisearch.xml; 12 + meta.doc = ./meilisearch.md; 13 13 14 14 ###### interface 15 15
-87
nixos/modules/services/search/meilisearch.xml
··· 1 - <!-- Do not edit this file directly, edit its companion .md instead 2 - and regenerate this file using nixos/doc/manual/md-to-db.sh --> 3 - <chapter xmlns="http://docbook.org/ns/docbook" xmlns:xlink="http://www.w3.org/1999/xlink" xml:id="module-services-meilisearch"> 4 - <title>Meilisearch</title> 5 - <para> 6 - Meilisearch is a lightweight, fast and powerful search engine. Think 7 - elastic search with a much smaller footprint. 8 - </para> 9 - <section xml:id="module-services-meilisearch-quickstart"> 10 - <title>Quickstart</title> 11 - <para> 12 - the minimum to start meilisearch is 13 - </para> 14 - <programlisting language="nix"> 15 - services.meilisearch.enable = true; 16 - </programlisting> 17 - <para> 18 - this will start the http server included with meilisearch on port 19 - 7700. 20 - </para> 21 - <para> 22 - test with 23 - <literal>curl -X GET 'http://localhost:7700/health'</literal> 24 - </para> 25 - </section> 26 - <section xml:id="module-services-meilisearch-usage"> 27 - <title>Usage</title> 28 - <para> 29 - you first need to add documents to an index before you can search 30 - for documents. 31 - </para> 32 - <section xml:id="module-services-meilisearch-quickstart-add"> 33 - <title>Add a documents to the <literal>movies</literal> 34 - index</title> 35 - <para> 36 - <literal>curl -X POST 'http://127.0.0.1:7700/indexes/movies/documents' --data '[{&quot;id&quot;: &quot;123&quot;, &quot;title&quot;: &quot;Superman&quot;}, {&quot;id&quot;: 234, &quot;title&quot;: &quot;Batman&quot;}]'</literal> 37 - </para> 38 - </section> 39 - <section xml:id="module-services-meilisearch-quickstart-search"> 40 - <title>Search documents in the <literal>movies</literal> 41 - index</title> 42 - <para> 43 - <literal>curl 'http://127.0.0.1:7700/indexes/movies/search' --data '{ &quot;q&quot;: &quot;botman&quot; }'</literal> 44 - (note the typo is intentional and there to demonstrate the typo 45 - tolerant capabilities) 46 - </para> 47 - </section> 48 - </section> 49 - <section xml:id="module-services-meilisearch-defaults"> 50 - <title>Defaults</title> 51 - <itemizedlist> 52 - <listitem> 53 - <para> 54 - The default nixos package doesn’t come with the 55 - <link xlink:href="https://docs.meilisearch.com/learn/getting_started/quick_start.html#search">dashboard</link>, 56 - since the dashboard features makes some assets downloads at 57 - compile time. 58 - </para> 59 - </listitem> 60 - <listitem> 61 - <para> 62 - Anonimized Analytics sent to meilisearch are disabled by 63 - default. 64 - </para> 65 - </listitem> 66 - <listitem> 67 - <para> 68 - Default deployment is development mode. It doesn’t require a 69 - secret master key. All routes are not protected and 70 - accessible. 71 - </para> 72 - </listitem> 73 - </itemizedlist> 74 - </section> 75 - <section xml:id="module-services-meilisearch-missing"> 76 - <title>Missing</title> 77 - <itemizedlist spacing="compact"> 78 - <listitem> 79 - <para> 80 - the snapshot feature is not yet configurable from the module, 81 - it’s just a matter of adding the relevant environment 82 - variables. 83 - </para> 84 - </listitem> 85 - </itemizedlist> 86 - </section> 87 - </chapter>
+1 -1
nixos/modules/services/web-apps/akkoma.nix
··· 1082 1082 }; 1083 1083 1084 1084 meta.maintainers = with maintainers; [ mvs ]; 1085 - meta.doc = ./akkoma.xml; 1085 + meta.doc = ./akkoma.md; 1086 1086 }
-398
nixos/modules/services/web-apps/akkoma.xml
··· 1 - <!-- Do not edit this file directly, edit its companion .md instead 2 - and regenerate this file using nixos/doc/manual/md-to-db.sh --> 3 - <chapter xmlns="http://docbook.org/ns/docbook" xmlns:xlink="http://www.w3.org/1999/xlink" xml:id="module-services-akkoma"> 4 - <title>Akkoma</title> 5 - <para> 6 - <link xlink:href="https://akkoma.dev/">Akkoma</link> is a 7 - lightweight ActivityPub microblogging server forked from Pleroma. 8 - </para> 9 - <section xml:id="modules-services-akkoma-service-configuration"> 10 - <title>Service configuration</title> 11 - <para> 12 - The Elixir configuration file required by Akkoma is generated 13 - automatically from 14 - <link xlink:href="options.html#opt-services.akkoma.config"><option>services.akkoma.config</option></link>. 15 - Secrets must be included from external files outside of the Nix 16 - store by setting the configuration option to an attribute set 17 - containing the attribute <option>_secret</option> – a string 18 - pointing to the file containing the actual value of the option. 19 - </para> 20 - <para> 21 - For the mandatory configuration settings these secrets will be 22 - generated automatically if the referenced file does not exist 23 - during startup, unless disabled through 24 - <link xlink:href="options.html#opt-services.akkoma.initSecrets"><option>services.akkoma.initSecrets</option></link>. 25 - </para> 26 - <para> 27 - The following configuration binds Akkoma to the Unix socket 28 - <literal>/run/akkoma/socket</literal>, expecting to be run behind 29 - a HTTP proxy on <literal>fediverse.example.com</literal>. 30 - </para> 31 - <programlisting language="nix"> 32 - services.akkoma.enable = true; 33 - services.akkoma.config = { 34 - &quot;:pleroma&quot; = { 35 - &quot;:instance&quot; = { 36 - name = &quot;My Akkoma instance&quot;; 37 - description = &quot;More detailed description&quot;; 38 - email = &quot;admin@example.com&quot;; 39 - registration_open = false; 40 - }; 41 - 42 - &quot;Pleroma.Web.Endpoint&quot; = { 43 - url.host = &quot;fediverse.example.com&quot;; 44 - }; 45 - }; 46 - }; 47 - </programlisting> 48 - <para> 49 - Please refer to the 50 - <link xlink:href="https://docs.akkoma.dev/stable/configuration/cheatsheet/">configuration 51 - cheat sheet</link> for additional configuration options. 52 - </para> 53 - </section> 54 - <section xml:id="modules-services-akkoma-user-management"> 55 - <title>User management</title> 56 - <para> 57 - After the Akkoma service is running, the administration utility 58 - can be used to 59 - <link xlink:href="https://docs.akkoma.dev/stable/administration/CLI_tasks/user/">manage 60 - users</link>. In particular an administrative user can be created 61 - with 62 - </para> 63 - <programlisting> 64 - $ pleroma_ctl user new &lt;nickname&gt; &lt;email&gt; --admin --moderator --password &lt;password&gt; 65 - </programlisting> 66 - </section> 67 - <section xml:id="modules-services-akkoma-proxy-configuration"> 68 - <title>Proxy configuration</title> 69 - <para> 70 - Although it is possible to expose Akkoma directly, it is common 71 - practice to operate it behind an HTTP reverse proxy such as nginx. 72 - </para> 73 - <programlisting language="nix"> 74 - services.akkoma.nginx = { 75 - enableACME = true; 76 - forceSSL = true; 77 - }; 78 - 79 - services.nginx = { 80 - enable = true; 81 - 82 - clientMaxBodySize = &quot;16m&quot;; 83 - recommendedTlsSettings = true; 84 - recommendedOptimisation = true; 85 - recommendedGzipSettings = true; 86 - }; 87 - </programlisting> 88 - <para> 89 - Please refer to <xref linkend="module-security-acme" /> for 90 - details on how to provision an SSL/TLS certificate. 91 - </para> 92 - <section xml:id="modules-services-akkoma-media-proxy"> 93 - <title>Media proxy</title> 94 - <para> 95 - Without the media proxy function, Akkoma does not store any 96 - remote media like pictures or video locally, and clients have to 97 - fetch them directly from the source server. 98 - </para> 99 - <programlisting language="nix"> 100 - # Enable nginx slice module distributed with Tengine 101 - services.nginx.package = pkgs.tengine; 102 - 103 - # Enable media proxy 104 - services.akkoma.config.&quot;:pleroma&quot;.&quot;:media_proxy&quot; = { 105 - enabled = true; 106 - proxy_opts.redirect_on_failure = true; 107 - }; 108 - 109 - # Adjust the persistent cache size as needed: 110 - # Assuming an average object size of 128 KiB, around 1 MiB 111 - # of memory is required for the key zone per GiB of cache. 112 - # Ensure that the cache directory exists and is writable by nginx. 113 - services.nginx.commonHttpConfig = '' 114 - proxy_cache_path /var/cache/nginx/cache/akkoma-media-cache 115 - levels= keys_zone=akkoma_media_cache:16m max_size=16g 116 - inactive=1y use_temp_path=off; 117 - ''; 118 - 119 - services.akkoma.nginx = { 120 - locations.&quot;/proxy&quot; = { 121 - proxyPass = &quot;http://unix:/run/akkoma/socket&quot;; 122 - 123 - extraConfig = '' 124 - proxy_cache akkoma_media_cache; 125 - 126 - # Cache objects in slices of 1 MiB 127 - slice 1m; 128 - proxy_cache_key $host$uri$is_args$args$slice_range; 129 - proxy_set_header Range $slice_range; 130 - 131 - # Decouple proxy and upstream responses 132 - proxy_buffering on; 133 - proxy_cache_lock on; 134 - proxy_ignore_client_abort on; 135 - 136 - # Default cache times for various responses 137 - proxy_cache_valid 200 1y; 138 - proxy_cache_valid 206 301 304 1h; 139 - 140 - # Allow serving of stale items 141 - proxy_cache_use_stale error timeout invalid_header updating; 142 - ''; 143 - }; 144 - }; 145 - </programlisting> 146 - <section xml:id="modules-services-akkoma-prefetch-remote-media"> 147 - <title>Prefetch remote media</title> 148 - <para> 149 - The following example enables the 150 - <literal>MediaProxyWarmingPolicy</literal> MRF policy which 151 - automatically fetches all media associated with a post through 152 - the media proxy, as soon as the post is received by the 153 - instance. 154 - </para> 155 - <programlisting language="nix"> 156 - services.akkoma.config.&quot;:pleroma&quot;.&quot;:mrf&quot;.policies = 157 - map (pkgs.formats.elixirConf { }).lib.mkRaw [ 158 - &quot;Pleroma.Web.ActivityPub.MRF.MediaProxyWarmingPolicy&quot; 159 - ]; 160 - </programlisting> 161 - </section> 162 - <section xml:id="modules-services-akkoma-media-previews"> 163 - <title>Media previews</title> 164 - <para> 165 - Akkoma can generate previews for media. 166 - </para> 167 - <programlisting language="nix"> 168 - services.akkoma.config.&quot;:pleroma&quot;.&quot;:media_preview_proxy&quot; = { 169 - enabled = true; 170 - thumbnail_max_width = 1920; 171 - thumbnail_max_height = 1080; 172 - }; 173 - </programlisting> 174 - </section> 175 - </section> 176 - </section> 177 - <section xml:id="modules-services-akkoma-frontend-management"> 178 - <title>Frontend management</title> 179 - <para> 180 - Akkoma will be deployed with the <literal>pleroma-fe</literal> and 181 - <literal>admin-fe</literal> frontends by default. These can be 182 - modified by setting 183 - <link xlink:href="options.html#opt-services.akkoma.frontends"><option>services.akkoma.frontends</option></link>. 184 - </para> 185 - <para> 186 - The following example overrides the primary frontend’s default 187 - configuration using a custom derivation. 188 - </para> 189 - <programlisting language="nix"> 190 - services.akkoma.frontends.primary.package = pkgs.runCommand &quot;pleroma-fe&quot; { 191 - config = builtins.toJSON { 192 - expertLevel = 1; 193 - collapseMessageWithSubject = false; 194 - stopGifs = false; 195 - replyVisibility = &quot;following&quot;; 196 - webPushHideIfCW = true; 197 - hideScopeNotice = true; 198 - renderMisskeyMarkdown = false; 199 - hideSiteFavicon = true; 200 - postContentType = &quot;text/markdown&quot;; 201 - showNavShortcuts = false; 202 - }; 203 - nativeBuildInputs = with pkgs; [ jq xorg.lndir ]; 204 - passAsFile = [ &quot;config&quot; ]; 205 - } '' 206 - mkdir $out 207 - lndir ${pkgs.akkoma-frontends.pleroma-fe} $out 208 - 209 - rm $out/static/config.json 210 - jq -s add ${pkgs.akkoma-frontends.pleroma-fe}/static/config.json ${config} \ 211 - &gt;$out/static/config.json 212 - ''; 213 - </programlisting> 214 - </section> 215 - <section xml:id="modules-services-akkoma-federation-policies"> 216 - <title>Federation policies</title> 217 - <para> 218 - Akkoma comes with a number of modules to police federation with 219 - other ActivityPub instances. The most valuable for typical users 220 - is the 221 - <link xlink:href="https://docs.akkoma.dev/stable/configuration/cheatsheet/#mrf_simple"><literal>:mrf_simple</literal></link> 222 - module which allows limiting federation based on instance 223 - hostnames. 224 - </para> 225 - <para> 226 - This configuration snippet provides an example on how these can be 227 - used. Choosing an adequate federation policy is not trivial and 228 - entails finding a balance between connectivity to the rest of the 229 - fediverse and providing a pleasant experience to the users of an 230 - instance. 231 - </para> 232 - <programlisting language="nix"> 233 - services.akkoma.config.&quot;:pleroma&quot; = with (pkgs.formats.elixirConf { }).lib; { 234 - &quot;:mrf&quot;.policies = map mkRaw [ 235 - &quot;Pleroma.Web.ActivityPub.MRF.SimplePolicy&quot; 236 - ]; 237 - 238 - &quot;:mrf_simple&quot; = { 239 - # Tag all media as sensitive 240 - media_nsfw = mkMap { 241 - &quot;nsfw.weird.kinky&quot; = &quot;Untagged NSFW content&quot;; 242 - }; 243 - 244 - # Reject all activities except deletes 245 - reject = mkMap { 246 - &quot;kiwifarms.cc&quot; = &quot;Persistent harassment of users, no moderation&quot;; 247 - }; 248 - 249 - # Force posts to be visible by followers only 250 - followers_only = mkMap { 251 - &quot;beta.birdsite.live&quot; = &quot;Avoid polluting timelines with Twitter posts&quot;; 252 - }; 253 - }; 254 - }; 255 - </programlisting> 256 - </section> 257 - <section xml:id="modules-services-akkoma-upload-filters"> 258 - <title>Upload filters</title> 259 - <para> 260 - This example strips GPS and location metadata from uploads, 261 - deduplicates them and anonymises the the file name. 262 - </para> 263 - <programlisting language="nix"> 264 - services.akkoma.config.&quot;:pleroma&quot;.&quot;Pleroma.Upload&quot;.filters = 265 - map (pkgs.formats.elixirConf { }).lib.mkRaw [ 266 - &quot;Pleroma.Upload.Filter.Exiftool&quot; 267 - &quot;Pleroma.Upload.Filter.Dedupe&quot; 268 - &quot;Pleroma.Upload.Filter.AnonymizeFilename&quot; 269 - ]; 270 - </programlisting> 271 - </section> 272 - <section xml:id="modules-services-akkoma-migration-pleroma"> 273 - <title>Migration from Pleroma</title> 274 - <para> 275 - Pleroma instances can be migrated to Akkoma either by copying the 276 - database and upload data or by pointing Akkoma to the existing 277 - data. The necessary database migrations are run automatically 278 - during startup of the service. 279 - </para> 280 - <para> 281 - The configuration has to be copy‐edited manually. 282 - </para> 283 - <para> 284 - Depending on the size of the database, the initial migration may 285 - take a long time and exceed the startup timeout of the system 286 - manager. To work around this issue one may adjust the startup 287 - timeout 288 - <option>systemd.services.akkoma.serviceConfig.TimeoutStartSec</option> 289 - or simply run the migrations manually: 290 - </para> 291 - <programlisting> 292 - pleroma_ctl migrate 293 - </programlisting> 294 - <section xml:id="modules-services-akkoma-migration-pleroma-copy"> 295 - <title>Copying data</title> 296 - <para> 297 - Copying the Pleroma data instead of re‐using it in place may 298 - permit easier reversion to Pleroma, but allows the two data sets 299 - to diverge. 300 - </para> 301 - <para> 302 - First disable Pleroma and then copy its database and upload 303 - data: 304 - </para> 305 - <programlisting> 306 - # Create a copy of the database 307 - nix-shell -p postgresql --run 'createdb -T pleroma akkoma' 308 - 309 - # Copy upload data 310 - mkdir /var/lib/akkoma 311 - cp -R --reflink=auto /var/lib/pleroma/uploads /var/lib/akkoma/ 312 - </programlisting> 313 - <para> 314 - After the data has been copied, enable the Akkoma service and 315 - verify that the migration has been successful. If no longer 316 - required, the original data may then be deleted: 317 - </para> 318 - <programlisting> 319 - # Delete original database 320 - nix-shell -p postgresql --run 'dropdb pleroma' 321 - 322 - # Delete original Pleroma state 323 - rm -r /var/lib/pleroma 324 - </programlisting> 325 - </section> 326 - <section xml:id="modules-services-akkoma-migration-pleroma-reuse"> 327 - <title>Re‐using data</title> 328 - <para> 329 - To re‐use the Pleroma data in place, disable Pleroma and enable 330 - Akkoma, pointing it to the Pleroma database and upload 331 - directory. 332 - </para> 333 - <programlisting language="nix"> 334 - # Adjust these settings according to the database name and upload directory path used by Pleroma 335 - services.akkoma.config.&quot;:pleroma&quot;.&quot;Pleroma.Repo&quot;.database = &quot;pleroma&quot;; 336 - services.akkoma.config.&quot;:pleroma&quot;.&quot;:instance&quot;.upload_dir = &quot;/var/lib/pleroma/uploads&quot;; 337 - </programlisting> 338 - <para> 339 - Please keep in mind that after the Akkoma service has been 340 - started, any migrations applied by Akkoma have to be rolled back 341 - before the database can be used again with Pleroma. This can be 342 - achieved through <literal>pleroma_ctl ecto.rollback</literal>. 343 - Refer to the 344 - <link xlink:href="https://hexdocs.pm/ecto_sql/Mix.Tasks.Ecto.Rollback.html">Ecto 345 - SQL documentation</link> for details. 346 - </para> 347 - </section> 348 - </section> 349 - <section xml:id="modules-services-akkoma-advanced-deployment"> 350 - <title>Advanced deployment options</title> 351 - <section xml:id="modules-services-akkoma-confinement"> 352 - <title>Confinement</title> 353 - <para> 354 - The Akkoma systemd service may be confined to a chroot with 355 - </para> 356 - <programlisting language="nix"> 357 - services.systemd.akkoma.confinement.enable = true; 358 - </programlisting> 359 - <para> 360 - Confinement of services is not generally supported in NixOS and 361 - therefore disabled by default. Depending on the Akkoma 362 - configuration, the default confinement settings may be 363 - insufficient and lead to subtle errors at run time, requiring 364 - adjustment: 365 - </para> 366 - <para> 367 - Use 368 - <link xlink:href="options.html#opt-systemd.services._name_.confinement.packages"><option>services.systemd.akkoma.confinement.packages</option></link> 369 - to make packages available in the chroot. 370 - </para> 371 - <para> 372 - <option>services.systemd.akkoma.serviceConfig.BindPaths</option> 373 - and 374 - <option>services.systemd.akkoma.serviceConfig.BindReadOnlyPaths</option> 375 - permit access to outside paths through bind mounts. Refer to 376 - <link xlink:href="https://www.freedesktop.org/software/systemd/man/systemd.exec.html#BindPaths="><citerefentry><refentrytitle>systemd.exec</refentrytitle><manvolnum>5</manvolnum></citerefentry></link> 377 - for details. 378 - </para> 379 - </section> 380 - <section xml:id="modules-services-akkoma-distributed-deployment"> 381 - <title>Distributed deployment</title> 382 - <para> 383 - Being an Elixir application, Akkoma can be deployed in a 384 - distributed fashion. 385 - </para> 386 - <para> 387 - This requires setting 388 - <link xlink:href="options.html#opt-services.akkoma.dist.address"><option>services.akkoma.dist.address</option></link> 389 - and 390 - <link xlink:href="options.html#opt-services.akkoma.dist.cookie"><option>services.akkoma.dist.cookie</option></link>. 391 - The specifics depend strongly on the deployment environment. For 392 - more information please check the relevant 393 - <link xlink:href="https://www.erlang.org/doc/reference_manual/distributed.html">Erlang 394 - documentation</link>. 395 - </para> 396 - </section> 397 - </section> 398 - </chapter>
+1 -1
nixos/modules/services/web-apps/discourse.nix
··· 1080 1080 ]; 1081 1081 }; 1082 1082 1083 - meta.doc = ./discourse.xml; 1083 + meta.doc = ./discourse.md; 1084 1084 meta.maintainers = [ lib.maintainers.talyz ]; 1085 1085 }
-331
nixos/modules/services/web-apps/discourse.xml
··· 1 - <!-- Do not edit this file directly, edit its companion .md instead 2 - and regenerate this file using nixos/doc/manual/md-to-db.sh --> 3 - <chapter xmlns="http://docbook.org/ns/docbook" xmlns:xlink="http://www.w3.org/1999/xlink" xml:id="module-services-discourse"> 4 - <title>Discourse</title> 5 - <para> 6 - <link xlink:href="https://www.discourse.org/">Discourse</link> is a 7 - modern and open source discussion platform. 8 - </para> 9 - <section xml:id="module-services-discourse-basic-usage"> 10 - <title>Basic usage</title> 11 - <para> 12 - A minimal configuration using Let’s Encrypt for TLS certificates 13 - looks like this: 14 - </para> 15 - <programlisting> 16 - services.discourse = { 17 - enable = true; 18 - hostname = &quot;discourse.example.com&quot;; 19 - admin = { 20 - email = &quot;admin@example.com&quot;; 21 - username = &quot;admin&quot;; 22 - fullName = &quot;Administrator&quot;; 23 - passwordFile = &quot;/path/to/password_file&quot;; 24 - }; 25 - secretKeyBaseFile = &quot;/path/to/secret_key_base_file&quot;; 26 - }; 27 - security.acme.email = &quot;me@example.com&quot;; 28 - security.acme.acceptTerms = true; 29 - </programlisting> 30 - <para> 31 - Provided a proper DNS setup, you’ll be able to connect to the 32 - instance at <literal>discourse.example.com</literal> and log in 33 - using the credentials provided in 34 - <literal>services.discourse.admin</literal>. 35 - </para> 36 - </section> 37 - <section xml:id="module-services-discourse-tls"> 38 - <title>Using a regular TLS certificate</title> 39 - <para> 40 - To set up TLS using a regular certificate and key on file, use the 41 - <xref linkend="opt-services.discourse.sslCertificate" /> and 42 - <xref linkend="opt-services.discourse.sslCertificateKey" /> 43 - options: 44 - </para> 45 - <programlisting> 46 - services.discourse = { 47 - enable = true; 48 - hostname = &quot;discourse.example.com&quot;; 49 - sslCertificate = &quot;/path/to/ssl_certificate&quot;; 50 - sslCertificateKey = &quot;/path/to/ssl_certificate_key&quot;; 51 - admin = { 52 - email = &quot;admin@example.com&quot;; 53 - username = &quot;admin&quot;; 54 - fullName = &quot;Administrator&quot;; 55 - passwordFile = &quot;/path/to/password_file&quot;; 56 - }; 57 - secretKeyBaseFile = &quot;/path/to/secret_key_base_file&quot;; 58 - }; 59 - </programlisting> 60 - </section> 61 - <section xml:id="module-services-discourse-database"> 62 - <title>Database access</title> 63 - <para> 64 - Discourse uses PostgreSQL to store most of its data. A database 65 - will automatically be enabled and a database and role created 66 - unless <xref linkend="opt-services.discourse.database.host" /> is 67 - changed from its default of <literal>null</literal> or 68 - <xref linkend="opt-services.discourse.database.createLocally" /> 69 - is set to <literal>false</literal>. 70 - </para> 71 - <para> 72 - External database access can also be configured by setting 73 - <xref linkend="opt-services.discourse.database.host" />, 74 - <xref linkend="opt-services.discourse.database.username" /> and 75 - <xref linkend="opt-services.discourse.database.passwordFile" /> as 76 - appropriate. Note that you need to manually create a database 77 - called <literal>discourse</literal> (or the name you chose in 78 - <xref linkend="opt-services.discourse.database.name" />) and allow 79 - the configured database user full access to it. 80 - </para> 81 - </section> 82 - <section xml:id="module-services-discourse-mail"> 83 - <title>Email</title> 84 - <para> 85 - In addition to the basic setup, you’ll want to configure an SMTP 86 - server Discourse can use to send user registration and password 87 - reset emails, among others. You can also optionally let Discourse 88 - receive email, which enables people to reply to threads and 89 - conversations via email. 90 - </para> 91 - <para> 92 - A basic setup which assumes you want to use your configured 93 - <link linkend="opt-services.discourse.hostname">hostname</link> as 94 - email domain can be done like this: 95 - </para> 96 - <programlisting> 97 - services.discourse = { 98 - enable = true; 99 - hostname = &quot;discourse.example.com&quot;; 100 - sslCertificate = &quot;/path/to/ssl_certificate&quot;; 101 - sslCertificateKey = &quot;/path/to/ssl_certificate_key&quot;; 102 - admin = { 103 - email = &quot;admin@example.com&quot;; 104 - username = &quot;admin&quot;; 105 - fullName = &quot;Administrator&quot;; 106 - passwordFile = &quot;/path/to/password_file&quot;; 107 - }; 108 - mail.outgoing = { 109 - serverAddress = &quot;smtp.emailprovider.com&quot;; 110 - port = 587; 111 - username = &quot;user@emailprovider.com&quot;; 112 - passwordFile = &quot;/path/to/smtp_password_file&quot;; 113 - }; 114 - mail.incoming.enable = true; 115 - secretKeyBaseFile = &quot;/path/to/secret_key_base_file&quot;; 116 - }; 117 - </programlisting> 118 - <para> 119 - This assumes you have set up an MX record for the address you’ve 120 - set in 121 - <link linkend="opt-services.discourse.hostname">hostname</link> 122 - and requires proper SPF, DKIM and DMARC configuration to be done 123 - for the domain you’re sending from, in order for email to be 124 - reliably delivered. 125 - </para> 126 - <para> 127 - If you want to use a different domain for your outgoing email (for 128 - example <literal>example.com</literal> instead of 129 - <literal>discourse.example.com</literal>) you should set 130 - <xref linkend="opt-services.discourse.mail.notificationEmailAddress" /> 131 - and 132 - <xref linkend="opt-services.discourse.mail.contactEmailAddress" /> 133 - manually. 134 - </para> 135 - <note> 136 - <para> 137 - Setup of TLS for incoming email is currently only configured 138 - automatically when a regular TLS certificate is used, i.e. when 139 - <xref linkend="opt-services.discourse.sslCertificate" /> and 140 - <xref linkend="opt-services.discourse.sslCertificateKey" /> are 141 - set. 142 - </para> 143 - </note> 144 - </section> 145 - <section xml:id="module-services-discourse-settings"> 146 - <title>Additional settings</title> 147 - <para> 148 - Additional site settings and backend settings, for which no 149 - explicit NixOS options are provided, can be set in 150 - <xref linkend="opt-services.discourse.siteSettings" /> and 151 - <xref linkend="opt-services.discourse.backendSettings" /> 152 - respectively. 153 - </para> 154 - <section xml:id="module-services-discourse-site-settings"> 155 - <title>Site settings</title> 156 - <para> 157 - <quote>Site settings</quote> are the settings that can be 158 - changed through the Discourse UI. Their 159 - <emphasis>default</emphasis> values can be set using 160 - <xref linkend="opt-services.discourse.siteSettings" />. 161 - </para> 162 - <para> 163 - Settings are expressed as a Nix attribute set which matches the 164 - structure of the configuration in 165 - <link xlink:href="https://github.com/discourse/discourse/blob/master/config/site_settings.yml">config/site_settings.yml</link>. 166 - To find a setting’s path, you only need to care about the first 167 - two levels; i.e. its category (e.g. <literal>login</literal>) 168 - and name (e.g. <literal>invite_only</literal>). 169 - </para> 170 - <para> 171 - Settings containing secret data should be set to an attribute 172 - set containing the attribute <literal>_secret</literal> - a 173 - string pointing to a file containing the value the option should 174 - be set to. See the example. 175 - </para> 176 - </section> 177 - <section xml:id="module-services-discourse-backend-settings"> 178 - <title>Backend settings</title> 179 - <para> 180 - Settings are expressed as a Nix attribute set which matches the 181 - structure of the configuration in 182 - <link xlink:href="https://github.com/discourse/discourse/blob/stable/config/discourse_defaults.conf">config/discourse.conf</link>. 183 - Empty parameters can be defined by setting them to 184 - <literal>null</literal>. 185 - </para> 186 - </section> 187 - <section xml:id="module-services-discourse-settings-example"> 188 - <title>Example</title> 189 - <para> 190 - The following example sets the title and description of the 191 - Discourse instance and enables GitHub login in the site 192 - settings, and changes a few request limits in the backend 193 - settings: 194 - </para> 195 - <programlisting> 196 - services.discourse = { 197 - enable = true; 198 - hostname = &quot;discourse.example.com&quot;; 199 - sslCertificate = &quot;/path/to/ssl_certificate&quot;; 200 - sslCertificateKey = &quot;/path/to/ssl_certificate_key&quot;; 201 - admin = { 202 - email = &quot;admin@example.com&quot;; 203 - username = &quot;admin&quot;; 204 - fullName = &quot;Administrator&quot;; 205 - passwordFile = &quot;/path/to/password_file&quot;; 206 - }; 207 - mail.outgoing = { 208 - serverAddress = &quot;smtp.emailprovider.com&quot;; 209 - port = 587; 210 - username = &quot;user@emailprovider.com&quot;; 211 - passwordFile = &quot;/path/to/smtp_password_file&quot;; 212 - }; 213 - mail.incoming.enable = true; 214 - siteSettings = { 215 - required = { 216 - title = &quot;My Cats&quot;; 217 - site_description = &quot;Discuss My Cats (and be nice plz)&quot;; 218 - }; 219 - login = { 220 - enable_github_logins = true; 221 - github_client_id = &quot;a2f6dfe838cb3206ce20&quot;; 222 - github_client_secret._secret = /run/keys/discourse_github_client_secret; 223 - }; 224 - }; 225 - backendSettings = { 226 - max_reqs_per_ip_per_minute = 300; 227 - max_reqs_per_ip_per_10_seconds = 60; 228 - max_asset_reqs_per_ip_per_10_seconds = 250; 229 - max_reqs_per_ip_mode = &quot;warn+block&quot;; 230 - }; 231 - secretKeyBaseFile = &quot;/path/to/secret_key_base_file&quot;; 232 - }; 233 - </programlisting> 234 - <para> 235 - In the resulting site settings file, the 236 - <literal>login.github_client_secret</literal> key will be set to 237 - the contents of the 238 - <filename>/run/keys/discourse_github_client_secret</filename> 239 - file. 240 - </para> 241 - </section> 242 - </section> 243 - <section xml:id="module-services-discourse-plugins"> 244 - <title>Plugins</title> 245 - <para> 246 - You can install Discourse plugins using the 247 - <xref linkend="opt-services.discourse.plugins" /> option. 248 - Pre-packaged plugins are provided in 249 - <literal>&lt;your_discourse_package_here&gt;.plugins</literal>. If 250 - you want the full suite of plugins provided through 251 - <literal>nixpkgs</literal>, you can also set the 252 - <xref linkend="opt-services.discourse.package" /> option to 253 - <literal>pkgs.discourseAllPlugins</literal>. 254 - </para> 255 - <para> 256 - Plugins can be built with the 257 - <literal>&lt;your_discourse_package_here&gt;.mkDiscoursePlugin</literal> 258 - function. Normally, it should suffice to provide a 259 - <literal>name</literal> and <literal>src</literal> attribute. If 260 - the plugin has Ruby dependencies, however, they need to be 261 - packaged in accordance with the 262 - <link xlink:href="https://nixos.org/manual/nixpkgs/stable/#developing-with-ruby">Developing 263 - with Ruby</link> section of the Nixpkgs manual and the appropriate 264 - gem options set in <literal>bundlerEnvArgs</literal> (normally 265 - <literal>gemdir</literal> is sufficient). A plugin’s Ruby 266 - dependencies are listed in its <filename>plugin.rb</filename> file 267 - as function calls to <literal>gem</literal>. To construct the 268 - corresponding <filename>Gemfile</filename> manually, run 269 - <command>bundle init</command>, then add the 270 - <literal>gem</literal> lines to it verbatim. 271 - </para> 272 - <para> 273 - Much of the packaging can be done automatically by the 274 - <filename>nixpkgs/pkgs/servers/web-apps/discourse/update.py</filename> 275 - script - just add the plugin to the <literal>plugins</literal> 276 - list in the <literal>update_plugins</literal> function and run the 277 - script: 278 - </para> 279 - <programlisting language="bash"> 280 - ./update.py update-plugins 281 - </programlisting> 282 - <para> 283 - Some plugins provide 284 - <link linkend="module-services-discourse-site-settings">site 285 - settings</link>. Their defaults can be configured using 286 - <xref linkend="opt-services.discourse.siteSettings" />, just like 287 - regular site settings. To find the names of these settings, look 288 - in the <literal>config/settings.yml</literal> file of the plugin 289 - repo. 290 - </para> 291 - <para> 292 - For example, to add the 293 - <link xlink:href="https://github.com/discourse/discourse-spoiler-alert">discourse-spoiler-alert</link> 294 - and 295 - <link xlink:href="https://github.com/discourse/discourse-solved">discourse-solved</link> 296 - plugins, and disable <literal>discourse-spoiler-alert</literal> by 297 - default: 298 - </para> 299 - <programlisting> 300 - services.discourse = { 301 - enable = true; 302 - hostname = &quot;discourse.example.com&quot;; 303 - sslCertificate = &quot;/path/to/ssl_certificate&quot;; 304 - sslCertificateKey = &quot;/path/to/ssl_certificate_key&quot;; 305 - admin = { 306 - email = &quot;admin@example.com&quot;; 307 - username = &quot;admin&quot;; 308 - fullName = &quot;Administrator&quot;; 309 - passwordFile = &quot;/path/to/password_file&quot;; 310 - }; 311 - mail.outgoing = { 312 - serverAddress = &quot;smtp.emailprovider.com&quot;; 313 - port = 587; 314 - username = &quot;user@emailprovider.com&quot;; 315 - passwordFile = &quot;/path/to/smtp_password_file&quot;; 316 - }; 317 - mail.incoming.enable = true; 318 - plugins = with config.services.discourse.package.plugins; [ 319 - discourse-spoiler-alert 320 - discourse-solved 321 - ]; 322 - siteSettings = { 323 - plugins = { 324 - spoiler_enabled = false; 325 - }; 326 - }; 327 - secretKeyBaseFile = &quot;/path/to/secret_key_base_file&quot;; 328 - }; 329 - </programlisting> 330 - </section> 331 - </chapter>
+1 -1
nixos/modules/services/web-apps/grocy.nix
··· 167 167 168 168 meta = { 169 169 maintainers = with maintainers; [ ma27 ]; 170 - doc = ./grocy.xml; 170 + doc = ./grocy.md; 171 171 }; 172 172 }
-84
nixos/modules/services/web-apps/grocy.xml
··· 1 - <!-- Do not edit this file directly, edit its companion .md instead 2 - and regenerate this file using nixos/doc/manual/md-to-db.sh --> 3 - <chapter xmlns="http://docbook.org/ns/docbook" xmlns:xlink="http://www.w3.org/1999/xlink" xml:id="module-services-grocy"> 4 - <title>Grocy</title> 5 - <para> 6 - <link xlink:href="https://grocy.info/">Grocy</link> is a web-based 7 - self-hosted groceries &amp; household management solution for your 8 - home. 9 - </para> 10 - <section xml:id="module-services-grocy-basic-usage"> 11 - <title>Basic usage</title> 12 - <para> 13 - A very basic configuration may look like this: 14 - </para> 15 - <programlisting> 16 - { pkgs, ... }: 17 - { 18 - services.grocy = { 19 - enable = true; 20 - hostName = &quot;grocy.tld&quot;; 21 - }; 22 - } 23 - </programlisting> 24 - <para> 25 - This configures a simple vhost using 26 - <link linkend="opt-services.nginx.enable">nginx</link> which 27 - listens to <literal>grocy.tld</literal> with fully configured 28 - ACME/LE (this can be disabled by setting 29 - <link linkend="opt-services.grocy.nginx.enableSSL">services.grocy.nginx.enableSSL</link> 30 - to <literal>false</literal>). After the initial setup the 31 - credentials <literal>admin:admin</literal> can be used to login. 32 - </para> 33 - <para> 34 - The application’s state is persisted at 35 - <literal>/var/lib/grocy/grocy.db</literal> in a 36 - <literal>sqlite3</literal> database. The migration is applied when 37 - requesting the <literal>/</literal>-route of the application. 38 - </para> 39 - </section> 40 - <section xml:id="module-services-grocy-settings"> 41 - <title>Settings</title> 42 - <para> 43 - The configuration for <literal>grocy</literal> is located at 44 - <literal>/etc/grocy/config.php</literal>. By default, the 45 - following settings can be defined in the NixOS-configuration: 46 - </para> 47 - <programlisting> 48 - { pkgs, ... }: 49 - { 50 - services.grocy.settings = { 51 - # The default currency in the system for invoices etc. 52 - # Please note that exchange rates aren't taken into account, this 53 - # is just the setting for what's shown in the frontend. 54 - currency = &quot;EUR&quot;; 55 - 56 - # The display language (and locale configuration) for grocy. 57 - culture = &quot;de&quot;; 58 - 59 - calendar = { 60 - # Whether or not to show the week-numbers 61 - # in the calendar. 62 - showWeekNumber = true; 63 - 64 - # Index of the first day to be shown in the calendar (0=Sunday, 1=Monday, 65 - # 2=Tuesday and so on). 66 - firstDayOfWeek = 2; 67 - }; 68 - }; 69 - } 70 - </programlisting> 71 - <para> 72 - If you want to alter the configuration file on your own, you can 73 - do this manually with an expression like this: 74 - </para> 75 - <programlisting> 76 - { lib, ... }: 77 - { 78 - environment.etc.&quot;grocy/config.php&quot;.text = lib.mkAfter '' 79 - // Arbitrary PHP code in grocy's configuration file 80 - ''; 81 - } 82 - </programlisting> 83 - </section> 84 - </chapter>
+1 -1
nixos/modules/services/web-apps/jitsi-meet.nix
··· 451 451 }; 452 452 }; 453 453 454 - meta.doc = ./jitsi-meet.xml; 454 + meta.doc = ./jitsi-meet.md; 455 455 meta.maintainers = lib.teams.jitsi.members; 456 456 }
-55
nixos/modules/services/web-apps/jitsi-meet.xml
··· 1 - <!-- Do not edit this file directly, edit its companion .md instead 2 - and regenerate this file using nixos/doc/manual/md-to-db.sh --> 3 - <chapter xmlns="http://docbook.org/ns/docbook" xmlns:xlink="http://www.w3.org/1999/xlink" xml:id="module-services-jitsi-meet"> 4 - <title>Jitsi Meet</title> 5 - <para> 6 - With Jitsi Meet on NixOS you can quickly configure a complete, 7 - private, self-hosted video conferencing solution. 8 - </para> 9 - <section xml:id="module-services-jitsi-basic-usage"> 10 - <title>Basic usage</title> 11 - <para> 12 - A minimal configuration using Let’s Encrypt for TLS certificates 13 - looks like this: 14 - </para> 15 - <programlisting> 16 - { 17 - services.jitsi-meet = { 18 - enable = true; 19 - hostName = &quot;jitsi.example.com&quot;; 20 - }; 21 - services.jitsi-videobridge.openFirewall = true; 22 - networking.firewall.allowedTCPPorts = [ 80 443 ]; 23 - security.acme.email = &quot;me@example.com&quot;; 24 - security.acme.acceptTerms = true; 25 - } 26 - </programlisting> 27 - </section> 28 - <section xml:id="module-services-jitsi-configuration"> 29 - <title>Configuration</title> 30 - <para> 31 - Here is the minimal configuration with additional configurations: 32 - </para> 33 - <programlisting> 34 - { 35 - services.jitsi-meet = { 36 - enable = true; 37 - hostName = &quot;jitsi.example.com&quot;; 38 - config = { 39 - enableWelcomePage = false; 40 - prejoinPageEnabled = true; 41 - defaultLang = &quot;fi&quot;; 42 - }; 43 - interfaceConfig = { 44 - SHOW_JITSI_WATERMARK = false; 45 - SHOW_WATERMARK_FOR_GUESTS = false; 46 - }; 47 - }; 48 - services.jitsi-videobridge.openFirewall = true; 49 - networking.firewall.allowedTCPPorts = [ 80 443 ]; 50 - security.acme.email = &quot;me@example.com&quot;; 51 - security.acme.acceptTerms = true; 52 - } 53 - </programlisting> 54 - </section> 55 - </chapter>
+1 -1
nixos/modules/services/web-apps/keycloak.nix
··· 674 674 mkIf createLocalMySQL (mkDefault dbPkg); 675 675 }; 676 676 677 - meta.doc = ./keycloak.xml; 677 + meta.doc = ./keycloak.md; 678 678 meta.maintainers = [ maintainers.talyz ]; 679 679 }
-177
nixos/modules/services/web-apps/keycloak.xml
··· 1 - <!-- Do not edit this file directly, edit its companion .md instead 2 - and regenerate this file using nixos/doc/manual/md-to-db.sh --> 3 - <chapter xmlns="http://docbook.org/ns/docbook" xmlns:xlink="http://www.w3.org/1999/xlink" xml:id="module-services-keycloak"> 4 - <title>Keycloak</title> 5 - <para> 6 - <link xlink:href="https://www.keycloak.org/">Keycloak</link> is an 7 - open source identity and access management server with support for 8 - <link xlink:href="https://openid.net/connect/">OpenID 9 - Connect</link>, <link xlink:href="https://oauth.net/2/">OAUTH 10 - 2.0</link> and 11 - <link xlink:href="https://en.wikipedia.org/wiki/SAML_2.0">SAML 12 - 2.0</link>. 13 - </para> 14 - <section xml:id="module-services-keycloak-admin"> 15 - <title>Administration</title> 16 - <para> 17 - An administrative user with the username <literal>admin</literal> 18 - is automatically created in the <literal>master</literal> realm. 19 - Its initial password can be configured by setting 20 - <xref linkend="opt-services.keycloak.initialAdminPassword" /> and 21 - defaults to <literal>changeme</literal>. The password is not 22 - stored safely and should be changed immediately in the admin 23 - panel. 24 - </para> 25 - <para> 26 - Refer to the 27 - <link xlink:href="https://www.keycloak.org/docs/latest/server_admin/index.html">Keycloak 28 - Server Administration Guide</link> for information on how to 29 - administer your Keycloak instance. 30 - </para> 31 - </section> 32 - <section xml:id="module-services-keycloak-database"> 33 - <title>Database access</title> 34 - <para> 35 - Keycloak can be used with either PostgreSQL, MariaDB or MySQL. 36 - Which one is used can be configured in 37 - <xref linkend="opt-services.keycloak.database.type" />. The 38 - selected database will automatically be enabled and a database and 39 - role created unless 40 - <xref linkend="opt-services.keycloak.database.host" /> is changed 41 - from its default of <literal>localhost</literal> or 42 - <xref linkend="opt-services.keycloak.database.createLocally" /> is 43 - set to <literal>false</literal>. 44 - </para> 45 - <para> 46 - External database access can also be configured by setting 47 - <xref linkend="opt-services.keycloak.database.host" />, 48 - <xref linkend="opt-services.keycloak.database.name" />, 49 - <xref linkend="opt-services.keycloak.database.username" />, 50 - <xref linkend="opt-services.keycloak.database.useSSL" /> and 51 - <xref linkend="opt-services.keycloak.database.caCert" /> as 52 - appropriate. Note that you need to manually create the database 53 - and allow the configured database user full access to it. 54 - </para> 55 - <para> 56 - <xref linkend="opt-services.keycloak.database.passwordFile" /> 57 - must be set to the path to a file containing the password used to 58 - log in to the database. If 59 - <xref linkend="opt-services.keycloak.database.host" /> and 60 - <xref linkend="opt-services.keycloak.database.createLocally" /> 61 - are kept at their defaults, the database role 62 - <literal>keycloak</literal> with that password is provisioned on 63 - the local database instance. 64 - </para> 65 - <warning> 66 - <para> 67 - The path should be provided as a string, not a Nix path, since 68 - Nix paths are copied into the world readable Nix store. 69 - </para> 70 - </warning> 71 - </section> 72 - <section xml:id="module-services-keycloak-hostname"> 73 - <title>Hostname</title> 74 - <para> 75 - The hostname is used to build the public URL used as base for all 76 - frontend requests and must be configured through 77 - <xref linkend="opt-services.keycloak.settings.hostname" />. 78 - </para> 79 - <note> 80 - <para> 81 - If you’re migrating an old Wildfly based Keycloak instance and 82 - want to keep compatibility with your current clients, you’ll 83 - likely want to set 84 - <xref linkend="opt-services.keycloak.settings.http-relative-path" /> 85 - to <literal>/auth</literal>. See the option description for more 86 - details. 87 - </para> 88 - </note> 89 - <para> 90 - <xref linkend="opt-services.keycloak.settings.hostname-strict-backchannel" /> 91 - determines whether Keycloak should force all requests to go 92 - through the frontend URL. By default, Keycloak allows backend 93 - requests to instead use its local hostname or IP address and may 94 - also advertise it to clients through its OpenID Connect Discovery 95 - endpoint. 96 - </para> 97 - <para> 98 - For more information on hostname configuration, see the 99 - <link xlink:href="https://www.keycloak.org/server/hostname">Hostname 100 - section of the Keycloak Server Installation and Configuration 101 - Guide</link>. 102 - </para> 103 - </section> 104 - <section xml:id="module-services-keycloak-tls"> 105 - <title>Setting up TLS/SSL</title> 106 - <para> 107 - By default, Keycloak won’t accept unsecured HTTP connections 108 - originating from outside its local network. 109 - </para> 110 - <para> 111 - HTTPS support requires a TLS/SSL certificate and a private key, 112 - both 113 - <link xlink:href="https://en.wikipedia.org/wiki/Privacy-Enhanced_Mail">PEM 114 - formatted</link>. Their paths should be set through 115 - <xref linkend="opt-services.keycloak.sslCertificate" /> and 116 - <xref linkend="opt-services.keycloak.sslCertificateKey" />. 117 - </para> 118 - <warning> 119 - <para> 120 - The paths should be provided as a strings, not a Nix paths, 121 - since Nix paths are copied into the world readable Nix store. 122 - </para> 123 - </warning> 124 - </section> 125 - <section xml:id="module-services-keycloak-themes"> 126 - <title>Themes</title> 127 - <para> 128 - You can package custom themes and make them visible to Keycloak 129 - through <xref linkend="opt-services.keycloak.themes" />. See the 130 - <link xlink:href="https://www.keycloak.org/docs/latest/server_development/#_themes">Themes 131 - section of the Keycloak Server Development Guide</link> and the 132 - description of the aforementioned NixOS option for more 133 - information. 134 - </para> 135 - </section> 136 - <section xml:id="module-services-keycloak-settings"> 137 - <title>Configuration file settings</title> 138 - <para> 139 - Keycloak server configuration parameters can be set in 140 - <xref linkend="opt-services.keycloak.settings" />. These 141 - correspond directly to options in 142 - <filename>conf/keycloak.conf</filename>. Some of the most 143 - important parameters are documented as suboptions, the rest can be 144 - found in the 145 - <link xlink:href="https://www.keycloak.org/server/all-config">All 146 - configuration section of the Keycloak Server Installation and 147 - Configuration Guide</link>. 148 - </para> 149 - <para> 150 - Options containing secret data should be set to an attribute set 151 - containing the attribute <literal>_secret</literal> - a string 152 - pointing to a file containing the value the option should be set 153 - to. See the description of 154 - <xref linkend="opt-services.keycloak.settings" /> for an example. 155 - </para> 156 - </section> 157 - <section xml:id="module-services-keycloak-example-config"> 158 - <title>Example configuration</title> 159 - <para> 160 - A basic configuration with some custom settings could look like 161 - this: 162 - </para> 163 - <programlisting> 164 - services.keycloak = { 165 - enable = true; 166 - settings = { 167 - hostname = &quot;keycloak.example.com&quot;; 168 - hostname-strict-backchannel = true; 169 - }; 170 - initialAdminPassword = &quot;e6Wcm0RrtegMEHl&quot;; # change on first login 171 - sslCertificate = &quot;/run/keys/ssl_cert&quot;; 172 - sslCertificateKey = &quot;/run/keys/ssl_key&quot;; 173 - database.passwordFile = &quot;/run/keys/db_password&quot;; 174 - }; 175 - </programlisting> 176 - </section> 177 - </chapter>
+1 -1
nixos/modules/services/web-apps/lemmy.nix
··· 6 6 in 7 7 { 8 8 meta.maintainers = with maintainers; [ happysalada ]; 9 - meta.doc = ./lemmy.xml; 9 + meta.doc = ./lemmy.md; 10 10 11 11 imports = [ 12 12 (mkRemovedOptionModule [ "services" "lemmy" "jwtSecretPath" ] "As of v0.13.0, Lemmy auto-generates the JWT secret.")
-53
nixos/modules/services/web-apps/lemmy.xml
··· 1 - <!-- Do not edit this file directly, edit its companion .md instead 2 - and regenerate this file using nixos/doc/manual/md-to-db.sh --> 3 - <chapter xmlns="http://docbook.org/ns/docbook" xmlns:xlink="http://www.w3.org/1999/xlink" xml:id="module-services-lemmy"> 4 - <title>Lemmy</title> 5 - <para> 6 - Lemmy is a federated alternative to reddit in rust. 7 - </para> 8 - <section xml:id="module-services-lemmy-quickstart"> 9 - <title>Quickstart</title> 10 - <para> 11 - the minimum to start lemmy is 12 - </para> 13 - <programlisting language="nix"> 14 - services.lemmy = { 15 - enable = true; 16 - settings = { 17 - hostname = &quot;lemmy.union.rocks&quot;; 18 - database.createLocally = true; 19 - }; 20 - caddy.enable = true; 21 - } 22 - </programlisting> 23 - <para> 24 - this will start the backend on port 8536 and the frontend on port 25 - 1234. It will expose your instance with a caddy reverse proxy to 26 - the hostname you’ve provided. Postgres will be initialized on that 27 - same instance automatically. 28 - </para> 29 - </section> 30 - <section xml:id="module-services-lemmy-usage"> 31 - <title>Usage</title> 32 - <para> 33 - On first connection you will be asked to define an admin user. 34 - </para> 35 - </section> 36 - <section xml:id="module-services-lemmy-missing"> 37 - <title>Missing</title> 38 - <itemizedlist spacing="compact"> 39 - <listitem> 40 - <para> 41 - Exposing with nginx is not implemented yet. 42 - </para> 43 - </listitem> 44 - <listitem> 45 - <para> 46 - This has been tested using a local database with a unix socket 47 - connection. Using different database settings will likely 48 - require modifications 49 - </para> 50 - </listitem> 51 - </itemizedlist> 52 - </section> 53 - </chapter>
+1 -1
nixos/modules/services/web-apps/matomo.nix
··· 325 325 }; 326 326 327 327 meta = { 328 - doc = ./matomo.xml; 328 + doc = ./matomo.md; 329 329 maintainers = with lib.maintainers; [ florianjacob ]; 330 330 }; 331 331 }
-107
nixos/modules/services/web-apps/matomo.xml
··· 1 - <!-- Do not edit this file directly, edit its companion .md instead 2 - and regenerate this file using nixos/doc/manual/md-to-db.sh --> 3 - <chapter xmlns="http://docbook.org/ns/docbook" xmlns:xlink="http://www.w3.org/1999/xlink" xml:id="module-services-matomo"> 4 - <title>Matomo</title> 5 - <para> 6 - Matomo is a real-time web analytics application. This module 7 - configures php-fpm as backend for Matomo, optionally configuring an 8 - nginx vhost as well. 9 - </para> 10 - <para> 11 - An automatic setup is not suported by Matomo, so you need to 12 - configure Matomo itself in the browser-based Matomo setup. 13 - </para> 14 - <section xml:id="module-services-matomo-database-setup"> 15 - <title>Database Setup</title> 16 - <para> 17 - You also need to configure a MariaDB or MySQL database and -user 18 - for Matomo yourself, and enter those credentials in your browser. 19 - You can use passwordless database authentication via the 20 - UNIX_SOCKET authentication plugin with the following SQL commands: 21 - </para> 22 - <programlisting> 23 - # For MariaDB 24 - INSTALL PLUGIN unix_socket SONAME 'auth_socket'; 25 - CREATE DATABASE matomo; 26 - CREATE USER 'matomo'@'localhost' IDENTIFIED WITH unix_socket; 27 - GRANT ALL PRIVILEGES ON matomo.* TO 'matomo'@'localhost'; 28 - 29 - # For MySQL 30 - INSTALL PLUGIN auth_socket SONAME 'auth_socket.so'; 31 - CREATE DATABASE matomo; 32 - CREATE USER 'matomo'@'localhost' IDENTIFIED WITH auth_socket; 33 - GRANT ALL PRIVILEGES ON matomo.* TO 'matomo'@'localhost'; 34 - </programlisting> 35 - <para> 36 - Then fill in <literal>matomo</literal> as database user and 37 - database name, and leave the password field blank. This 38 - authentication works by allowing only the 39 - <literal>matomo</literal> unix user to authenticate as the 40 - <literal>matomo</literal> database user (without needing a 41 - password), but no other users. For more information on 42 - passwordless login, see 43 - <link xlink:href="https://mariadb.com/kb/en/mariadb/unix_socket-authentication-plugin/">https://mariadb.com/kb/en/mariadb/unix_socket-authentication-plugin/</link>. 44 - </para> 45 - <para> 46 - Of course, you can use password based authentication as well, e.g. 47 - when the database is not on the same host. 48 - </para> 49 - </section> 50 - <section xml:id="module-services-matomo-archive-processing"> 51 - <title>Archive Processing</title> 52 - <para> 53 - This module comes with the systemd service 54 - <literal>matomo-archive-processing.service</literal> and a timer 55 - that automatically triggers archive processing every hour. This 56 - means that you can safely 57 - <link xlink:href="https://matomo.org/docs/setup-auto-archiving/#disable-browser-triggers-for-matomo-archiving-and-limit-matomo-reports-to-updating-every-hour">disable 58 - browser triggers for Matomo archiving</link> at 59 - <literal>Administration &gt; System &gt; General Settings</literal>. 60 - </para> 61 - <para> 62 - With automatic archive processing, you can now also enable to 63 - <link xlink:href="https://matomo.org/docs/privacy/#step-2-delete-old-visitors-logs">delete 64 - old visitor logs</link> at 65 - <literal>Administration &gt; System &gt; Privacy</literal>, but 66 - make sure that you run 67 - <literal>systemctl start matomo-archive-processing.service</literal> 68 - at least once without errors if you have already collected data 69 - before, so that the reports get archived before the source data 70 - gets deleted. 71 - </para> 72 - </section> 73 - <section xml:id="module-services-matomo-backups"> 74 - <title>Backup</title> 75 - <para> 76 - You only need to take backups of your MySQL database and the 77 - <filename>/var/lib/matomo/config/config.ini.php</filename> file. 78 - Use a user in the <literal>matomo</literal> group or root to 79 - access the file. For more information, see 80 - <link xlink:href="https://matomo.org/faq/how-to-install/faq_138/">https://matomo.org/faq/how-to-install/faq_138/</link>. 81 - </para> 82 - </section> 83 - <section xml:id="module-services-matomo-issues"> 84 - <title>Issues</title> 85 - <itemizedlist spacing="compact"> 86 - <listitem> 87 - <para> 88 - Matomo will warn you that the JavaScript tracker is not 89 - writable. This is because it’s located in the read-only nix 90 - store. You can safely ignore this, unless you need a plugin 91 - that needs JavaScript tracker access. 92 - </para> 93 - </listitem> 94 - </itemizedlist> 95 - </section> 96 - <section xml:id="module-services-matomo-other-web-servers"> 97 - <title>Using other Web Servers than nginx</title> 98 - <para> 99 - You can use other web servers by forwarding calls for 100 - <filename>index.php</filename> and <filename>piwik.php</filename> 101 - to the 102 - <link linkend="opt-services.phpfpm.pools._name_.socket"><literal>services.phpfpm.pools.&lt;name&gt;.socket</literal></link> 103 - fastcgi unix socket. You can use the nginx configuration in the 104 - module code as a reference to what else should be configured. 105 - </para> 106 - </section> 107 - </chapter>
+1 -1
nixos/modules/services/web-apps/nextcloud.nix
··· 1146 1146 } 1147 1147 ]); 1148 1148 1149 - meta.doc = ./nextcloud.xml; 1149 + meta.doc = ./nextcloud.md; 1150 1150 }
-333
nixos/modules/services/web-apps/nextcloud.xml
··· 1 - <!-- Do not edit this file directly, edit its companion .md instead 2 - and regenerate this file using nixos/doc/manual/md-to-db.sh --> 3 - <chapter xmlns="http://docbook.org/ns/docbook" xmlns:xlink="http://www.w3.org/1999/xlink" xml:id="module-services-nextcloud"> 4 - <title>Nextcloud</title> 5 - <para> 6 - <link xlink:href="https://nextcloud.com/">Nextcloud</link> is an 7 - open-source, self-hostable cloud platform. The server setup can be 8 - automated using 9 - <link linkend="opt-services.nextcloud.enable">services.nextcloud</link>. 10 - A desktop client is packaged at 11 - <literal>pkgs.nextcloud-client</literal>. 12 - </para> 13 - <para> 14 - The current default by NixOS is <literal>nextcloud25</literal> which 15 - is also the latest major version available. 16 - </para> 17 - <section xml:id="module-services-nextcloud-basic-usage"> 18 - <title>Basic usage</title> 19 - <para> 20 - Nextcloud is a PHP-based application which requires an HTTP server 21 - (<link linkend="opt-services.nextcloud.enable"><literal>services.nextcloud</literal></link> 22 - optionally supports 23 - <link linkend="opt-services.nginx.enable"><literal>services.nginx</literal></link>) 24 - and a database (it’s recommended to use 25 - <link linkend="opt-services.postgresql.enable"><literal>services.postgresql</literal></link>). 26 - </para> 27 - <para> 28 - A very basic configuration may look like this: 29 - </para> 30 - <programlisting> 31 - { pkgs, ... }: 32 - { 33 - services.nextcloud = { 34 - enable = true; 35 - hostName = &quot;nextcloud.tld&quot;; 36 - config = { 37 - dbtype = &quot;pgsql&quot;; 38 - dbuser = &quot;nextcloud&quot;; 39 - dbhost = &quot;/run/postgresql&quot;; # nextcloud will add /.s.PGSQL.5432 by itself 40 - dbname = &quot;nextcloud&quot;; 41 - adminpassFile = &quot;/path/to/admin-pass-file&quot;; 42 - adminuser = &quot;root&quot;; 43 - }; 44 - }; 45 - 46 - services.postgresql = { 47 - enable = true; 48 - ensureDatabases = [ &quot;nextcloud&quot; ]; 49 - ensureUsers = [ 50 - { name = &quot;nextcloud&quot;; 51 - ensurePermissions.&quot;DATABASE nextcloud&quot; = &quot;ALL PRIVILEGES&quot;; 52 - } 53 - ]; 54 - }; 55 - 56 - # ensure that postgres is running *before* running the setup 57 - systemd.services.&quot;nextcloud-setup&quot; = { 58 - requires = [&quot;postgresql.service&quot;]; 59 - after = [&quot;postgresql.service&quot;]; 60 - }; 61 - 62 - networking.firewall.allowedTCPPorts = [ 80 443 ]; 63 - } 64 - </programlisting> 65 - <para> 66 - The <literal>hostName</literal> option is used internally to 67 - configure an HTTP server using 68 - <link xlink:href="https://php-fpm.org/"><literal>PHP-FPM</literal></link> 69 - and <literal>nginx</literal>. The <literal>config</literal> 70 - attribute set is used by the imperative installer and all values 71 - are written to an additional file to ensure that changes can be 72 - applied by changing the module’s options. 73 - </para> 74 - <para> 75 - In case the application serves multiple domains (those are checked 76 - with 77 - <link xlink:href="http://php.net/manual/en/reserved.variables.server.php"><literal>$_SERVER['HTTP_HOST']</literal></link>) 78 - it’s needed to add them to 79 - <link linkend="opt-services.nextcloud.config.extraTrustedDomains"><literal>services.nextcloud.config.extraTrustedDomains</literal></link>. 80 - </para> 81 - <para> 82 - Auto updates for Nextcloud apps can be enabled using 83 - <link linkend="opt-services.nextcloud.autoUpdateApps.enable"><literal>services.nextcloud.autoUpdateApps</literal></link>. 84 - </para> 85 - </section> 86 - <section xml:id="module-services-nextcloud-pitfalls-during-upgrade"> 87 - <title>Common problems</title> 88 - <itemizedlist> 89 - <listitem> 90 - <para> 91 - <emphasis role="strong">General notes.</emphasis> 92 - Unfortunately Nextcloud appears to be very stateful when it 93 - comes to managing its own configuration. The config file lives 94 - in the home directory of the <literal>nextcloud</literal> user 95 - (by default 96 - <literal>/var/lib/nextcloud/config/config.php</literal>) and 97 - is also used to track several states of the application (e.g., 98 - whether installed or not). 99 - </para> 100 - <para> 101 - All configuration parameters are also stored in 102 - <filename>/var/lib/nextcloud/config/override.config.php</filename> 103 - which is generated by the module and linked from the store to 104 - ensure that all values from <filename>config.php</filename> 105 - can be modified by the module. However 106 - <filename>config.php</filename> manages the application’s 107 - state and shouldn’t be touched manually because of that. 108 - </para> 109 - <warning> 110 - <para> 111 - Don’t delete <filename>config.php</filename>! This file 112 - tracks the application’s state and a deletion can cause 113 - unwanted side-effects! 114 - </para> 115 - </warning> 116 - <warning> 117 - <para> 118 - Don’t rerun 119 - <literal>nextcloud-occ maintenance:install</literal>! This 120 - command tries to install the application and can cause 121 - unwanted side-effects! 122 - </para> 123 - </warning> 124 - </listitem> 125 - <listitem> 126 - <para> 127 - <emphasis role="strong">Multiple version upgrades.</emphasis> 128 - Nextcloud doesn’t allow to move more than one major-version 129 - forward. E.g., if you’re on <literal>v16</literal>, you cannot 130 - upgrade to <literal>v18</literal>, you need to upgrade to 131 - <literal>v17</literal> first. This is ensured automatically as 132 - long as the 133 - <link linkend="opt-system.stateVersion">stateVersion</link> is 134 - declared properly. In that case the oldest version available 135 - (one major behind the one from the previous NixOS release) 136 - will be selected by default and the module will generate a 137 - warning that reminds the user to upgrade to latest Nextcloud 138 - <emphasis>after</emphasis> that deploy. 139 - </para> 140 - </listitem> 141 - <listitem> 142 - <para> 143 - <emphasis role="strong"><literal>Error: Command &quot;upgrade&quot; is not defined.</literal></emphasis> 144 - This error usually occurs if the initial installation 145 - (<command>nextcloud-occ maintenance:install</command>) has 146 - failed. After that, the application is not installed, but the 147 - upgrade is attempted to be executed. Further context can be 148 - found in 149 - <link xlink:href="https://github.com/NixOS/nixpkgs/issues/111175">NixOS/nixpkgs#111175</link>. 150 - </para> 151 - <para> 152 - First of all, it makes sense to find out what went wrong by 153 - looking at the logs of the installation via 154 - <command>journalctl -u nextcloud-setup</command> and try to 155 - fix the underlying issue. 156 - </para> 157 - <itemizedlist> 158 - <listitem> 159 - <para> 160 - If this occurs on an <emphasis>existing</emphasis> setup, 161 - this is most likely because the maintenance mode is 162 - active. It can be deactivated by running 163 - <command>nextcloud-occ maintenance:mode --off</command>. 164 - It’s advisable though to check the logs first on why the 165 - maintenance mode was activated. 166 - </para> 167 - </listitem> 168 - <listitem> 169 - <warning> 170 - <para> 171 - Only perform the following measures on <emphasis>freshly 172 - installed instances!</emphasis> 173 - </para> 174 - </warning> 175 - <para> 176 - A re-run of the installer can be forced by 177 - <emphasis>deleting</emphasis> 178 - <filename>/var/lib/nextcloud/config/config.php</filename>. 179 - This is the only time advisable because the fresh install 180 - doesn’t have any state that can be lost. In case that 181 - doesn’t help, an entire re-creation can be forced via 182 - <command>rm -rf ~nextcloud/</command>. 183 - </para> 184 - </listitem> 185 - </itemizedlist> 186 - </listitem> 187 - <listitem> 188 - <para> 189 - <emphasis role="strong">Server-side encryption.</emphasis> 190 - Nextcloud supports 191 - <link xlink:href="https://docs.nextcloud.com/server/latest/admin_manual/configuration_files/encryption_configuration.html">server-side 192 - encryption (SSE)</link>. This is not an end-to-end encryption, 193 - but can be used to encrypt files that will be persisted to 194 - external storage such as S3. Please note that this won’t work 195 - anymore when using OpenSSL 3 for PHP’s openssl extension 196 - because this is implemented using the legacy cipher RC4. If 197 - <xref linkend="opt-system.stateVersion" /> is 198 - <emphasis>above</emphasis> <literal>22.05</literal>, this is 199 - disabled by default. To turn it on again and for further 200 - information please refer to 201 - <xref linkend="opt-services.nextcloud.enableBrokenCiphersForSSE" />. 202 - </para> 203 - </listitem> 204 - </itemizedlist> 205 - </section> 206 - <section xml:id="module-services-nextcloud-httpd"> 207 - <title>Using an alternative webserver as reverse-proxy (e.g. 208 - <literal>httpd</literal>)</title> 209 - <para> 210 - By default, <literal>nginx</literal> is used as reverse-proxy for 211 - <literal>nextcloud</literal>. However, it’s possible to use e.g. 212 - <literal>httpd</literal> by explicitly disabling 213 - <literal>nginx</literal> using 214 - <xref linkend="opt-services.nginx.enable" /> and fixing the 215 - settings <literal>listen.owner</literal> &amp; 216 - <literal>listen.group</literal> in the 217 - <link linkend="opt-services.phpfpm.pools">corresponding 218 - <literal>phpfpm</literal> pool</link>. 219 - </para> 220 - <para> 221 - An exemplary configuration may look like this: 222 - </para> 223 - <programlisting> 224 - { config, lib, pkgs, ... }: { 225 - services.nginx.enable = false; 226 - services.nextcloud = { 227 - enable = true; 228 - hostName = &quot;localhost&quot;; 229 - 230 - /* further, required options */ 231 - }; 232 - services.phpfpm.pools.nextcloud.settings = { 233 - &quot;listen.owner&quot; = config.services.httpd.user; 234 - &quot;listen.group&quot; = config.services.httpd.group; 235 - }; 236 - services.httpd = { 237 - enable = true; 238 - adminAddr = &quot;webmaster@localhost&quot;; 239 - extraModules = [ &quot;proxy_fcgi&quot; ]; 240 - virtualHosts.&quot;localhost&quot; = { 241 - documentRoot = config.services.nextcloud.package; 242 - extraConfig = '' 243 - &lt;Directory &quot;${config.services.nextcloud.package}&quot;&gt; 244 - &lt;FilesMatch &quot;\.php$&quot;&gt; 245 - &lt;If &quot;-f %{REQUEST_FILENAME}&quot;&gt; 246 - SetHandler &quot;proxy:unix:${config.services.phpfpm.pools.nextcloud.socket}|fcgi://localhost/&quot; 247 - &lt;/If&gt; 248 - &lt;/FilesMatch&gt; 249 - &lt;IfModule mod_rewrite.c&gt; 250 - RewriteEngine On 251 - RewriteBase / 252 - RewriteRule ^index\.php$ - [L] 253 - RewriteCond %{REQUEST_FILENAME} !-f 254 - RewriteCond %{REQUEST_FILENAME} !-d 255 - RewriteRule . /index.php [L] 256 - &lt;/IfModule&gt; 257 - DirectoryIndex index.php 258 - Require all granted 259 - Options +FollowSymLinks 260 - &lt;/Directory&gt; 261 - ''; 262 - }; 263 - }; 264 - } 265 - </programlisting> 266 - </section> 267 - <section xml:id="installing-apps-php-extensions-nextcloud"> 268 - <title>Installing Apps and PHP extensions</title> 269 - <para> 270 - Nextcloud apps are installed statefully through the web interface. 271 - Some apps may require extra PHP extensions to be installed. This 272 - can be configured with the 273 - <xref linkend="opt-services.nextcloud.phpExtraExtensions" /> 274 - setting. 275 - </para> 276 - <para> 277 - Alternatively, extra apps can also be declared with the 278 - <xref linkend="opt-services.nextcloud.extraApps" /> setting. When 279 - using this setting, apps can no longer be managed statefully 280 - because this can lead to Nextcloud updating apps that are managed 281 - by Nix. If you want automatic updates it is recommended that you 282 - use web interface to install apps. 283 - </para> 284 - </section> 285 - <section xml:id="module-services-nextcloud-maintainer-info"> 286 - <title>Maintainer information</title> 287 - <para> 288 - As stated in the previous paragraph, we must provide a clean 289 - upgrade-path for Nextcloud since it cannot move more than one 290 - major version forward on a single upgrade. This chapter adds some 291 - notes how Nextcloud updates should be rolled out in the future. 292 - </para> 293 - <para> 294 - While minor and patch-level updates are no problem and can be done 295 - directly in the package-expression (and should be backported to 296 - supported stable branches after that), major-releases should be 297 - added in a new attribute (e.g. Nextcloud 298 - <literal>v19.0.0</literal> should be available in 299 - <literal>nixpkgs</literal> as 300 - <literal>pkgs.nextcloud19</literal>). To provide simple upgrade 301 - paths it’s generally useful to backport those as well to stable 302 - branches. As long as the package-default isn’t altered, this won’t 303 - break existing setups. After that, the versioning-warning in the 304 - <literal>nextcloud</literal>-module should be updated to make sure 305 - that the 306 - <link linkend="opt-services.nextcloud.package">package</link>-option 307 - selects the latest version on fresh setups. 308 - </para> 309 - <para> 310 - If major-releases will be abandoned by upstream, we should check 311 - first if those are needed in NixOS for a safe upgrade-path before 312 - removing those. In that case we should keep those packages, but 313 - mark them as insecure in an expression like this (in 314 - <literal>&lt;nixpkgs/pkgs/servers/nextcloud/default.nix&gt;</literal>): 315 - </para> 316 - <programlisting> 317 - /* ... */ 318 - { 319 - nextcloud17 = generic { 320 - version = &quot;17.0.x&quot;; 321 - sha256 = &quot;0000000000000000000000000000000000000000000000000000&quot;; 322 - eol = true; 323 - }; 324 - } 325 - </programlisting> 326 - <para> 327 - Ideally we should make sure that it’s possible to jump two NixOS 328 - versions forward: i.e. the warnings and the logic in the module 329 - should guard a user to upgrade from a Nextcloud on e.g. 19.09 to a 330 - Nextcloud on 20.09. 331 - </para> 332 - </section> 333 - </chapter>
+1 -1
nixos/modules/services/web-apps/pict-rs.nix
··· 5 5 in 6 6 { 7 7 meta.maintainers = with maintainers; [ happysalada ]; 8 - meta.doc = ./pict-rs.xml; 8 + meta.doc = ./pict-rs.md; 9 9 10 10 options.services.pict-rs = { 11 11 enable = mkEnableOption (lib.mdDoc "pict-rs server");
-185
nixos/modules/services/web-apps/pict-rs.xml
··· 1 - <!-- Do not edit this file directly, edit its companion .md instead 2 - and regenerate this file using nixos/doc/manual/md-to-db.sh --> 3 - <chapter xmlns="http://docbook.org/ns/docbook" xmlns:xlink="http://www.w3.org/1999/xlink" xml:id="module-services-pict-rs"> 4 - <title>Pict-rs</title> 5 - <para> 6 - pict-rs is a a simple image hosting service. 7 - </para> 8 - <section xml:id="module-services-pict-rs-quickstart"> 9 - <title>Quickstart</title> 10 - <para> 11 - the minimum to start pict-rs is 12 - </para> 13 - <programlisting language="nix"> 14 - services.pict-rs.enable = true; 15 - </programlisting> 16 - <para> 17 - this will start the http server on port 8080 by default. 18 - </para> 19 - </section> 20 - <section xml:id="module-services-pict-rs-usage"> 21 - <title>Usage</title> 22 - <para> 23 - pict-rs offers the following endpoints: 24 - </para> 25 - <itemizedlist> 26 - <listitem> 27 - <para> 28 - <literal>POST /image</literal> for uploading an image. 29 - Uploaded content must be valid multipart/form-data with an 30 - image array located within the <literal>images[]</literal> key 31 - </para> 32 - <para> 33 - This endpoint returns the following JSON structure on success 34 - with a 201 Created status 35 - </para> 36 - <programlisting language="json"> 37 - { 38 - &quot;files&quot;: [ 39 - { 40 - &quot;delete_token&quot;: &quot;JFvFhqJA98&quot;, 41 - &quot;file&quot;: &quot;lkWZDRvugm.jpg&quot; 42 - }, 43 - { 44 - &quot;delete_token&quot;: &quot;kAYy9nk2WK&quot;, 45 - &quot;file&quot;: &quot;8qFS0QooAn.jpg&quot; 46 - }, 47 - { 48 - &quot;delete_token&quot;: &quot;OxRpM3sf0Y&quot;, 49 - &quot;file&quot;: &quot;1hJaYfGE01.jpg&quot; 50 - } 51 - ], 52 - &quot;msg&quot;: &quot;ok&quot; 53 - } 54 - </programlisting> 55 - </listitem> 56 - <listitem> 57 - <para> 58 - <literal>GET /image/download?url=...</literal> Download an 59 - image from a remote server, returning the same JSON payload as 60 - the <literal>POST</literal> endpoint 61 - </para> 62 - </listitem> 63 - <listitem> 64 - <para> 65 - <literal>GET /image/original/{file}</literal> for getting a 66 - full-resolution image. <literal>file</literal> here is the 67 - <literal>file</literal> key from the <literal>/image</literal> 68 - endpoint’s JSON 69 - </para> 70 - </listitem> 71 - <listitem> 72 - <para> 73 - <literal>GET /image/details/original/{file}</literal> for 74 - getting the details of a full-resolution image. The returned 75 - JSON is structured like so: 76 - </para> 77 - <programlisting language="json"> 78 - { 79 - &quot;width&quot;: 800, 80 - &quot;height&quot;: 537, 81 - &quot;content_type&quot;: &quot;image/webp&quot;, 82 - &quot;created_at&quot;: [ 83 - 2020, 84 - 345, 85 - 67376, 86 - 394363487 87 - ] 88 - } 89 - </programlisting> 90 - </listitem> 91 - <listitem> 92 - <para> 93 - <literal>GET /image/process.{ext}?src={file}&amp;...</literal> 94 - get a file with transformations applied. existing 95 - transformations include 96 - </para> 97 - <itemizedlist spacing="compact"> 98 - <listitem> 99 - <para> 100 - <literal>identity=true</literal>: apply no changes 101 - </para> 102 - </listitem> 103 - <listitem> 104 - <para> 105 - <literal>blur={float}</literal>: apply a gaussian blur to 106 - the file 107 - </para> 108 - </listitem> 109 - <listitem> 110 - <para> 111 - <literal>thumbnail={int}</literal>: produce a thumbnail of 112 - the image fitting inside an <literal>{int}</literal> by 113 - <literal>{int}</literal> square using raw pixel sampling 114 - </para> 115 - </listitem> 116 - <listitem> 117 - <para> 118 - <literal>resize={int}</literal>: produce a thumbnail of 119 - the image fitting inside an <literal>{int}</literal> by 120 - <literal>{int}</literal> square using a Lanczos2 filter. 121 - This is slower than sampling but looks a bit better in 122 - some cases 123 - </para> 124 - </listitem> 125 - <listitem> 126 - <para> 127 - <literal>crop={int-w}x{int-h}</literal>: produce a cropped 128 - version of the image with an <literal>{int-w}</literal> by 129 - <literal>{int-h}</literal> aspect ratio. The resulting 130 - crop will be centered on the image. Either the width or 131 - height of the image will remain full-size, depending on 132 - the image’s aspect ratio and the requested aspect ratio. 133 - For example, a 1600x900 image cropped with a 1x1 aspect 134 - ratio will become 900x900. A 1600x1100 image cropped with 135 - a 16x9 aspect ratio will become 1600x900. 136 - </para> 137 - </listitem> 138 - </itemizedlist> 139 - <para> 140 - Supported <literal>ext</literal> file extensions include 141 - <literal>png</literal>, <literal>jpg</literal>, and 142 - <literal>webp</literal> 143 - </para> 144 - <para> 145 - An example of usage could be 146 - </para> 147 - <programlisting> 148 - GET /image/process.jpg?src=asdf.png&amp;thumbnail=256&amp;blur=3.0 149 - </programlisting> 150 - <para> 151 - which would create a 256x256px JPEG thumbnail and blur it 152 - </para> 153 - </listitem> 154 - <listitem> 155 - <para> 156 - <literal>GET /image/details/process.{ext}?src={file}&amp;...</literal> 157 - for getting the details of a processed image. The returned 158 - JSON is the same format as listed for the full-resolution 159 - details endpoint. 160 - </para> 161 - </listitem> 162 - <listitem> 163 - <para> 164 - <literal>DELETE /image/delete/{delete_token}/{file}</literal> 165 - or <literal>GET /image/delete/{delete_token}/{file}</literal> 166 - to delete a file, where <literal>delete_token</literal> and 167 - <literal>file</literal> are from the <literal>/image</literal> 168 - endpoint’s JSON 169 - </para> 170 - </listitem> 171 - </itemizedlist> 172 - </section> 173 - <section xml:id="module-services-pict-rs-missing"> 174 - <title>Missing</title> 175 - <itemizedlist spacing="compact"> 176 - <listitem> 177 - <para> 178 - Configuring the secure-api-key is not included yet. The 179 - envisioned basic use case is consumption on localhost by other 180 - services without exposing the service to the internet. 181 - </para> 182 - </listitem> 183 - </itemizedlist> 184 - </section> 185 - </chapter>
+1 -1
nixos/modules/services/web-apps/plausible.nix
··· 292 292 }; 293 293 294 294 meta.maintainers = with maintainers; [ ma27 ]; 295 - meta.doc = ./plausible.xml; 295 + meta.doc = ./plausible.md; 296 296 }
-45
nixos/modules/services/web-apps/plausible.xml
··· 1 - <!-- Do not edit this file directly, edit its companion .md instead 2 - and regenerate this file using nixos/doc/manual/md-to-db.sh --> 3 - <chapter xmlns="http://docbook.org/ns/docbook" xmlns:xlink="http://www.w3.org/1999/xlink" xml:id="module-services-plausible"> 4 - <title>Plausible</title> 5 - <para> 6 - <link xlink:href="https://plausible.io/">Plausible</link> is a 7 - privacy-friendly alternative to Google analytics. 8 - </para> 9 - <section xml:id="module-services-plausible-basic-usage"> 10 - <title>Basic Usage</title> 11 - <para> 12 - At first, a secret key is needed to be generated. This can be done 13 - with e.g. 14 - </para> 15 - <programlisting> 16 - $ openssl rand -base64 64 17 - </programlisting> 18 - <para> 19 - After that, <literal>plausible</literal> can be deployed like 20 - this: 21 - </para> 22 - <programlisting> 23 - { 24 - services.plausible = { 25 - enable = true; 26 - adminUser = { 27 - # activate is used to skip the email verification of the admin-user that's 28 - # automatically created by plausible. This is only supported if 29 - # postgresql is configured by the module. This is done by default, but 30 - # can be turned off with services.plausible.database.postgres.setup. 31 - activate = true; 32 - email = &quot;admin@localhost&quot;; 33 - passwordFile = &quot;/run/secrets/plausible-admin-pwd&quot;; 34 - }; 35 - server = { 36 - baseUrl = &quot;http://analytics.example.org&quot;; 37 - # secretKeybaseFile is a path to the file which contains the secret generated 38 - # with openssl as described above. 39 - secretKeybaseFile = &quot;/run/secrets/plausible-secret-key-base&quot;; 40 - }; 41 - }; 42 - } 43 - </programlisting> 44 - </section> 45 - </chapter>
+1 -1
nixos/modules/services/web-servers/garage.nix
··· 9 9 in 10 10 { 11 11 meta = { 12 - doc = ./garage.xml; 12 + doc = ./garage.md; 13 13 maintainers = with pkgs.lib.maintainers; [ raitobezarius ]; 14 14 }; 15 15
-206
nixos/modules/services/web-servers/garage.xml
··· 1 - <!-- Do not edit this file directly, edit its companion .md instead 2 - and regenerate this file using nixos/doc/manual/md-to-db.sh --> 3 - <chapter xmlns="http://docbook.org/ns/docbook" xmlns:xlink="http://www.w3.org/1999/xlink" xml:id="module-services-garage"> 4 - <title>Garage</title> 5 - <para> 6 - <link xlink:href="https://garagehq.deuxfleurs.fr/">Garage</link> is 7 - an open-source, self-hostable S3 store, simpler than MinIO, for 8 - geodistributed stores. The server setup can be automated using 9 - <link linkend="opt-services.garage.enable">services.garage</link>. A 10 - client configured to your local Garage instance is available in the 11 - global environment as <literal>garage-manage</literal>. 12 - </para> 13 - <para> 14 - The current default by NixOS is <literal>garage_0_8</literal> which 15 - is also the latest major version available. 16 - </para> 17 - <section xml:id="module-services-garage-upgrade-scenarios"> 18 - <title>General considerations on upgrades</title> 19 - <para> 20 - Garage provides a cookbook documentation on how to upgrade: 21 - <link xlink:href="https://garagehq.deuxfleurs.fr/documentation/cookbook/upgrading/">https://garagehq.deuxfleurs.fr/documentation/cookbook/upgrading/</link> 22 - </para> 23 - <warning> 24 - <para> 25 - Garage has two types of upgrades: patch-level upgrades and 26 - minor/major version upgrades. 27 - </para> 28 - <para> 29 - In all cases, you should read the changelog and ideally test the 30 - upgrade on a staging cluster. 31 - </para> 32 - <para> 33 - Checking the health of your cluster can be achieved using 34 - <literal>garage-manage repair</literal>. 35 - </para> 36 - </warning> 37 - <warning> 38 - <para> 39 - Until 1.0 is released, patch-level upgrades are considered as 40 - minor version upgrades. Minor version upgrades are considered as 41 - major version upgrades. i.e. 0.6 to 0.7 is a major version 42 - upgrade. 43 - </para> 44 - </warning> 45 - <itemizedlist spacing="compact"> 46 - <listitem> 47 - <para> 48 - <emphasis role="strong">Straightforward upgrades (patch-level 49 - upgrades).</emphasis> Upgrades must be performed one by one, 50 - i.e. for each node, stop it, upgrade it : change 51 - <link linkend="opt-system.stateVersion">stateVersion</link> or 52 - <link linkend="opt-services.garage.package">services.garage.package</link>, 53 - restart it if it was not already by switching. 54 - </para> 55 - </listitem> 56 - <listitem> 57 - <para> 58 - <emphasis role="strong">Multiple version upgrades.</emphasis> 59 - Garage do not provide any guarantee on moving more than one 60 - major-version forward. E.g., if you’re on 61 - <literal>0.7</literal>, you cannot upgrade to 62 - <literal>0.9</literal>. You need to upgrade to 63 - <literal>0.8</literal> first. As long as 64 - <link linkend="opt-system.stateVersion">stateVersion</link> is 65 - declared properly, this is enforced automatically. The module 66 - will issue a warning to remind the user to upgrade to latest 67 - Garage <emphasis>after</emphasis> that deploy. 68 - </para> 69 - </listitem> 70 - </itemizedlist> 71 - </section> 72 - <section xml:id="module-services-garage-advanced-upgrades"> 73 - <title>Advanced upgrades (minor/major version upgrades)</title> 74 - <para> 75 - Here are some baseline instructions to handle advanced upgrades in 76 - Garage, when in doubt, please refer to upstream instructions. 77 - </para> 78 - <itemizedlist spacing="compact"> 79 - <listitem> 80 - <para> 81 - Disable API and web access to Garage. 82 - </para> 83 - </listitem> 84 - <listitem> 85 - <para> 86 - Perform 87 - <literal>garage-manage repair --all-nodes --yes tables</literal> 88 - and 89 - <literal>garage-manage repair --all-nodes --yes blocks</literal>. 90 - </para> 91 - </listitem> 92 - <listitem> 93 - <para> 94 - Verify the resulting logs and check that data is synced 95 - properly between all nodes. If you have time, do additional 96 - checks (<literal>scrub</literal>, 97 - <literal>block_refs</literal>, etc.). 98 - </para> 99 - </listitem> 100 - <listitem> 101 - <para> 102 - Check if queues are empty by 103 - <literal>garage-manage stats</literal> or through monitoring 104 - tools. 105 - </para> 106 - </listitem> 107 - <listitem> 108 - <para> 109 - Run <literal>systemctl stop garage</literal> to stop the 110 - actual Garage version. 111 - </para> 112 - </listitem> 113 - <listitem> 114 - <para> 115 - Backup the metadata folder of ALL your nodes, e.g. for a 116 - metadata directory (the default one) in 117 - <literal>/var/lib/garage/meta</literal>, you can run 118 - <literal>pushd /var/lib/garage; tar -acf meta-v0.7.tar.zst meta/; popd</literal>. 119 - </para> 120 - </listitem> 121 - <listitem> 122 - <para> 123 - Run the offline migration: 124 - <literal>nix-shell -p garage_0_8 --run &quot;garage offline-repair --yes&quot;</literal>, 125 - this can take some time depending on how many objects are 126 - stored in your cluster. 127 - </para> 128 - </listitem> 129 - <listitem> 130 - <para> 131 - Bump Garage version in your NixOS configuration, either by 132 - changing 133 - <link linkend="opt-system.stateVersion">stateVersion</link> or 134 - bumping 135 - <link linkend="opt-services.garage.package">services.garage.package</link>, 136 - this should restart Garage automatically. 137 - </para> 138 - </listitem> 139 - <listitem> 140 - <para> 141 - Perform 142 - <literal>garage-manage repair --all-nodes --yes tables</literal> 143 - and 144 - <literal>garage-manage repair --all-nodes --yes blocks</literal>. 145 - </para> 146 - </listitem> 147 - <listitem> 148 - <para> 149 - Wait for a full table sync to run. 150 - </para> 151 - </listitem> 152 - </itemizedlist> 153 - <para> 154 - Your upgraded cluster should be in a working state, re-enable API 155 - and web access. 156 - </para> 157 - </section> 158 - <section xml:id="module-services-garage-maintainer-info"> 159 - <title>Maintainer information</title> 160 - <para> 161 - As stated in the previous paragraph, we must provide a clean 162 - upgrade-path for Garage since it cannot move more than one major 163 - version forward on a single upgrade. This chapter adds some notes 164 - how Garage updates should be rolled out in the future. This is 165 - inspired from how Nextcloud does it. 166 - </para> 167 - <para> 168 - While patch-level updates are no problem and can be done directly 169 - in the package-expression (and should be backported to supported 170 - stable branches after that), major-releases should be added in a 171 - new attribute (e.g. Garage <literal>v0.8.0</literal> should be 172 - available in <literal>nixpkgs</literal> as 173 - <literal>pkgs.garage_0_8_0</literal>). To provide simple upgrade 174 - paths it’s generally useful to backport those as well to stable 175 - branches. As long as the package-default isn’t altered, this won’t 176 - break existing setups. After that, the versioning-warning in the 177 - <literal>garage</literal>-module should be updated to make sure 178 - that the 179 - <link linkend="opt-services.garage.package">package</link>-option 180 - selects the latest version on fresh setups. 181 - </para> 182 - <para> 183 - If major-releases will be abandoned by upstream, we should check 184 - first if those are needed in NixOS for a safe upgrade-path before 185 - removing those. In that case we shold keep those packages, but 186 - mark them as insecure in an expression like this (in 187 - <literal>&lt;nixpkgs/pkgs/tools/filesystem/garage/default.nix&gt;</literal>): 188 - </para> 189 - <programlisting> 190 - /* ... */ 191 - { 192 - garage_0_7_3 = generic { 193 - version = &quot;0.7.3&quot;; 194 - sha256 = &quot;0000000000000000000000000000000000000000000000000000&quot;; 195 - eol = true; 196 - }; 197 - } 198 - </programlisting> 199 - <para> 200 - Ideally we should make sure that it’s possible to jump two NixOS 201 - versions forward: i.e. the warnings and the logic in the module 202 - should guard a user to upgrade from a Garage on e.g. 22.11 to a 203 - Garage on 23.11. 204 - </para> 205 - </section> 206 - </chapter>
+1 -1
nixos/modules/services/x11/desktop-managers/gnome.nix
··· 66 66 { 67 67 68 68 meta = { 69 - doc = ./gnome.xml; 69 + doc = ./gnome.md; 70 70 maintainers = teams.gnome.members; 71 71 }; 72 72
-261
nixos/modules/services/x11/desktop-managers/gnome.xml
··· 1 - <!-- Do not edit this file directly, edit its companion .md instead 2 - and regenerate this file using nixos/doc/manual/md-to-db.sh --> 3 - <chapter xmlns="http://docbook.org/ns/docbook" xmlns:xlink="http://www.w3.org/1999/xlink" xml:id="chap-gnome"> 4 - <title>GNOME Desktop</title> 5 - <para> 6 - GNOME provides a simple, yet full-featured desktop environment with 7 - a focus on productivity. Its Mutter compositor supports both Wayland 8 - and X server, and the GNOME Shell user interface is fully 9 - customizable by extensions. 10 - </para> 11 - <section xml:id="sec-gnome-enable"> 12 - <title>Enabling GNOME</title> 13 - <para> 14 - All of the core apps, optional apps, games, and core developer 15 - tools from GNOME are available. 16 - </para> 17 - <para> 18 - To enable the GNOME desktop use: 19 - </para> 20 - <programlisting> 21 - services.xserver.desktopManager.gnome.enable = true; 22 - services.xserver.displayManager.gdm.enable = true; 23 - </programlisting> 24 - <note> 25 - <para> 26 - While it is not strictly necessary to use GDM as the display 27 - manager with GNOME, it is recommended, as some features such as 28 - screen lock 29 - <link linkend="sec-gnome-faq-can-i-use-lightdm-with-gnome">might 30 - not work</link> without it. 31 - </para> 32 - </note> 33 - <para> 34 - The default applications used in NixOS are very minimal, inspired 35 - by the defaults used in 36 - <link xlink:href="https://gitlab.gnome.org/GNOME/gnome-build-meta/blob/40.0/elements/core/meta-gnome-core-utilities.bst">gnome-build-meta</link>. 37 - </para> 38 - <section xml:id="sec-gnome-without-the-apps"> 39 - <title>GNOME without the apps</title> 40 - <para> 41 - If you’d like to only use the GNOME desktop and not the apps, 42 - you can disable them with: 43 - </para> 44 - <programlisting> 45 - services.gnome.core-utilities.enable = false; 46 - </programlisting> 47 - <para> 48 - and none of them will be installed. 49 - </para> 50 - <para> 51 - If you’d only like to omit a subset of the core utilities, you 52 - can use 53 - <xref linkend="opt-environment.gnome.excludePackages" />. Note 54 - that this mechanism can only exclude core utilities, games and 55 - core developer tools. 56 - </para> 57 - </section> 58 - <section xml:id="sec-gnome-disabling-services"> 59 - <title>Disabling GNOME services</title> 60 - <para> 61 - It is also possible to disable many of the 62 - <link xlink:href="https://github.com/NixOS/nixpkgs/blob/b8ec4fd2a4edc4e30d02ba7b1a2cc1358f3db1d5/nixos/modules/services/x11/desktop-managers/gnome.nix#L329-L348">core 63 - services</link>. For example, if you do not need indexing files, 64 - you can disable Tracker with: 65 - </para> 66 - <programlisting> 67 - services.gnome.tracker-miners.enable = false; 68 - services.gnome.tracker.enable = false; 69 - </programlisting> 70 - <para> 71 - Note, however, that doing so is not supported and might break 72 - some applications. Notably, GNOME Music cannot work without 73 - Tracker. 74 - </para> 75 - </section> 76 - <section xml:id="sec-gnome-games"> 77 - <title>GNOME games</title> 78 - <para> 79 - You can install all of the GNOME games with: 80 - </para> 81 - <programlisting> 82 - services.gnome.games.enable = true; 83 - </programlisting> 84 - </section> 85 - <section xml:id="sec-gnome-core-developer-tools"> 86 - <title>GNOME core developer tools</title> 87 - <para> 88 - You can install GNOME core developer tools with: 89 - </para> 90 - <programlisting> 91 - services.gnome.core-developer-tools.enable = true; 92 - </programlisting> 93 - </section> 94 - </section> 95 - <section xml:id="sec-gnome-enable-flashback"> 96 - <title>Enabling GNOME Flashback</title> 97 - <para> 98 - GNOME Flashback provides a desktop environment based on the 99 - classic GNOME 2 architecture. You can enable the default GNOME 100 - Flashback session, which uses the Metacity window manager, with: 101 - </para> 102 - <programlisting> 103 - services.xserver.desktopManager.gnome.flashback.enableMetacity = true; 104 - </programlisting> 105 - <para> 106 - It is also possible to create custom sessions that replace 107 - Metacity with a different window manager using 108 - <xref linkend="opt-services.xserver.desktopManager.gnome.flashback.customSessions" />. 109 - </para> 110 - <para> 111 - The following example uses <literal>xmonad</literal> window 112 - manager: 113 - </para> 114 - <programlisting> 115 - services.xserver.desktopManager.gnome.flashback.customSessions = [ 116 - { 117 - wmName = &quot;xmonad&quot;; 118 - wmLabel = &quot;XMonad&quot;; 119 - wmCommand = &quot;${pkgs.haskellPackages.xmonad}/bin/xmonad&quot;; 120 - enableGnomePanel = false; 121 - } 122 - ]; 123 - </programlisting> 124 - </section> 125 - <section xml:id="sec-gnome-icons-and-gtk-themes"> 126 - <title>Icons and GTK Themes</title> 127 - <para> 128 - Icon themes and GTK themes don’t require any special option to 129 - install in NixOS. 130 - </para> 131 - <para> 132 - You can add them to 133 - <xref linkend="opt-environment.systemPackages" /> and switch to 134 - them with GNOME Tweaks. If you’d like to do this manually in 135 - dconf, change the values of the following keys: 136 - </para> 137 - <programlisting> 138 - /org/gnome/desktop/interface/gtk-theme 139 - /org/gnome/desktop/interface/icon-theme 140 - </programlisting> 141 - <para> 142 - in <literal>dconf-editor</literal> 143 - </para> 144 - </section> 145 - <section xml:id="sec-gnome-shell-extensions"> 146 - <title>Shell Extensions</title> 147 - <para> 148 - Most Shell extensions are packaged under the 149 - <literal>gnomeExtensions</literal> attribute. Some packages that 150 - include Shell extensions, like <literal>gnome.gpaste</literal>, 151 - don’t have their extension decoupled under this attribute. 152 - </para> 153 - <para> 154 - You can install them like any other package: 155 - </para> 156 - <programlisting> 157 - environment.systemPackages = [ 158 - gnomeExtensions.dash-to-dock 159 - gnomeExtensions.gsconnect 160 - gnomeExtensions.mpris-indicator-button 161 - ]; 162 - </programlisting> 163 - <para> 164 - Unfortunately, we lack a way for these to be managed in a 165 - completely declarative way. So you have to enable them manually 166 - with an Extensions application. It is possible to use a 167 - <link linkend="sec-gnome-gsettings-overrides">GSettings 168 - override</link> for this on 169 - <literal>org.gnome.shell.enabled-extensions</literal>, but that 170 - will only influence the default value. 171 - </para> 172 - </section> 173 - <section xml:id="sec-gnome-gsettings-overrides"> 174 - <title>GSettings Overrides</title> 175 - <para> 176 - Majority of software building on the GNOME platform use GLib’s 177 - <link xlink:href="https://developer.gnome.org/gio/unstable/GSettings.html">GSettings</link> 178 - system to manage runtime configuration. For our purposes, the 179 - system consists of XML schemas describing the individual 180 - configuration options, stored in the package, and a settings 181 - backend, where the values of the settings are stored. On NixOS, 182 - like on most Linux distributions, dconf database is used as the 183 - backend. 184 - </para> 185 - <para> 186 - <link xlink:href="https://developer.gnome.org/gio/unstable/GSettings.html#id-1.4.19.2.9.25">GSettings 187 - vendor overrides</link> can be used to adjust the default values 188 - for settings of the GNOME desktop and apps by replacing the 189 - default values specified in the XML schemas. Using overrides will 190 - allow you to pre-seed user settings before you even start the 191 - session. 192 - </para> 193 - <warning> 194 - <para> 195 - Overrides really only change the default values for GSettings 196 - keys so if you or an application changes the setting value, the 197 - value set by the override will be ignored. Until 198 - <link xlink:href="https://github.com/NixOS/nixpkgs/issues/54150">NixOS’s 199 - dconf module implements changing values</link>, you will either 200 - need to keep that in mind and clear the setting from the backend 201 - using <literal>dconf reset</literal> command when that happens, 202 - or use the 203 - <link xlink:href="https://nix-community.github.io/home-manager/options.html#opt-dconf.settings">module 204 - from home-manager</link>. 205 - </para> 206 - </warning> 207 - <para> 208 - You can override the default GSettings values using the 209 - <xref linkend="opt-services.xserver.desktopManager.gnome.extraGSettingsOverrides" /> 210 - option. 211 - </para> 212 - <para> 213 - Take note that whatever packages you want to override GSettings 214 - for, you need to add them to 215 - <xref linkend="opt-services.xserver.desktopManager.gnome.extraGSettingsOverridePackages" />. 216 - </para> 217 - <para> 218 - You can use <literal>dconf-editor</literal> tool to explore which 219 - GSettings you can set. 220 - </para> 221 - <section xml:id="sec-gnome-gsettings-overrides-example"> 222 - <title>Example</title> 223 - <programlisting> 224 - services.xserver.desktopManager.gnome = { 225 - extraGSettingsOverrides = '' 226 - # Change default background 227 - [org.gnome.desktop.background] 228 - picture-uri='file://${pkgs.nixos-artwork.wallpapers.mosaic-blue.gnomeFilePath}' 229 - 230 - # Favorite apps in gnome-shell 231 - [org.gnome.shell] 232 - favorite-apps=['org.gnome.Photos.desktop', 'org.gnome.Nautilus.desktop'] 233 - ''; 234 - 235 - extraGSettingsOverridePackages = [ 236 - pkgs.gsettings-desktop-schemas # for org.gnome.desktop 237 - pkgs.gnome.gnome-shell # for org.gnome.shell 238 - ]; 239 - }; 240 - </programlisting> 241 - </section> 242 - </section> 243 - <section xml:id="sec-gnome-faq"> 244 - <title>Frequently Asked Questions</title> 245 - <section xml:id="sec-gnome-faq-can-i-use-lightdm-with-gnome"> 246 - <title>Can I use LightDM with GNOME?</title> 247 - <para> 248 - Yes you can, and any other display-manager in NixOS. 249 - </para> 250 - <para> 251 - However, it doesn’t work correctly for the Wayland session of 252 - GNOME Shell yet, and won’t be able to lock your screen. 253 - </para> 254 - <para> 255 - See 256 - <link xlink:href="https://github.com/NixOS/nixpkgs/issues/56342">this 257 - issue.</link> 258 - </para> 259 - </section> 260 - </section> 261 - </chapter>
+1 -1
nixos/modules/services/x11/desktop-managers/pantheon.nix
··· 17 17 { 18 18 19 19 meta = { 20 - doc = ./pantheon.xml; 20 + doc = ./pantheon.md; 21 21 maintainers = teams.pantheon.members; 22 22 }; 23 23
-171
nixos/modules/services/x11/desktop-managers/pantheon.xml
··· 1 - <!-- Do not edit this file directly, edit its companion .md instead 2 - and regenerate this file using nixos/doc/manual/md-to-db.sh --> 3 - <chapter xmlns="http://docbook.org/ns/docbook" xmlns:xlink="http://www.w3.org/1999/xlink" xml:id="chap-pantheon"> 4 - <title>Pantheon Desktop</title> 5 - <para> 6 - Pantheon is the desktop environment created for the elementary OS 7 - distribution. It is written from scratch in Vala, utilizing GNOME 8 - technologies with GTK and Granite. 9 - </para> 10 - <section xml:id="sec-pantheon-enable"> 11 - <title>Enabling Pantheon</title> 12 - <para> 13 - All of Pantheon is working in NixOS and the applications should be 14 - available, aside from a few 15 - <link xlink:href="https://github.com/NixOS/nixpkgs/issues/58161">exceptions</link>. 16 - To enable Pantheon, set 17 - </para> 18 - <programlisting> 19 - services.xserver.desktopManager.pantheon.enable = true; 20 - </programlisting> 21 - <para> 22 - This automatically enables LightDM and Pantheon’s LightDM greeter. 23 - If you’d like to disable this, set 24 - </para> 25 - <programlisting> 26 - services.xserver.displayManager.lightdm.greeters.pantheon.enable = false; 27 - services.xserver.displayManager.lightdm.enable = false; 28 - </programlisting> 29 - <para> 30 - but please be aware using Pantheon without LightDM as a display 31 - manager will break screenlocking from the UI. The NixOS module for 32 - Pantheon installs all of Pantheon’s default applications. If you’d 33 - like to not install Pantheon’s apps, set 34 - </para> 35 - <programlisting> 36 - services.pantheon.apps.enable = false; 37 - </programlisting> 38 - <para> 39 - You can also use 40 - <xref linkend="opt-environment.pantheon.excludePackages" /> to 41 - remove any other app (like <literal>elementary-mail</literal>). 42 - </para> 43 - </section> 44 - <section xml:id="sec-pantheon-wingpanel-switchboard"> 45 - <title>Wingpanel and Switchboard plugins</title> 46 - <para> 47 - Wingpanel and Switchboard work differently than they do in other 48 - distributions, as far as using plugins. You cannot install a 49 - plugin globally (like with 50 - <option>environment.systemPackages</option>) to start using it. 51 - You should instead be using the following options: 52 - </para> 53 - <itemizedlist spacing="compact"> 54 - <listitem> 55 - <para> 56 - <xref linkend="opt-services.xserver.desktopManager.pantheon.extraWingpanelIndicators" /> 57 - </para> 58 - </listitem> 59 - <listitem> 60 - <para> 61 - <xref linkend="opt-services.xserver.desktopManager.pantheon.extraSwitchboardPlugs" /> 62 - </para> 63 - </listitem> 64 - </itemizedlist> 65 - <para> 66 - to configure the programs with plugs or indicators. 67 - </para> 68 - <para> 69 - The difference in NixOS is both these programs are patched to load 70 - plugins from a directory that is the value of an environment 71 - variable. All of which is controlled in Nix. If you need to 72 - configure the particular packages manually you can override the 73 - packages like: 74 - </para> 75 - <programlisting> 76 - wingpanel-with-indicators.override { 77 - indicators = [ 78 - pkgs.some-special-indicator 79 - ]; 80 - }; 81 - 82 - switchboard-with-plugs.override { 83 - plugs = [ 84 - pkgs.some-special-plug 85 - ]; 86 - }; 87 - </programlisting> 88 - <para> 89 - please note that, like how the NixOS options describe these as 90 - extra plugins, this would only add to the default plugins included 91 - with the programs. If for some reason you’d like to configure 92 - which plugins to use exactly, both packages have an argument for 93 - this: 94 - </para> 95 - <programlisting> 96 - wingpanel-with-indicators.override { 97 - useDefaultIndicators = false; 98 - indicators = specialListOfIndicators; 99 - }; 100 - 101 - switchboard-with-plugs.override { 102 - useDefaultPlugs = false; 103 - plugs = specialListOfPlugs; 104 - }; 105 - </programlisting> 106 - <para> 107 - this could be most useful for testing a particular plug-in in 108 - isolation. 109 - </para> 110 - </section> 111 - <section xml:id="sec-pantheon-faq"> 112 - <title>FAQ</title> 113 - <variablelist spacing="compact"> 114 - <varlistentry> 115 - <term> 116 - <anchor xml:id="sec-pantheon-faq-messed-up-theme" />I have 117 - switched from a different desktop and Pantheon’s theming looks 118 - messed up. 119 - </term> 120 - <listitem> 121 - <para> 122 - Open Switchboard and go to: Administration → About → Restore 123 - Default Settings → Restore Settings. This will reset any 124 - dconf settings to their Pantheon defaults. Note this could 125 - reset certain GNOME specific preferences if that desktop was 126 - used prior. 127 - </para> 128 - </listitem> 129 - </varlistentry> 130 - <varlistentry> 131 - <term> 132 - <anchor xml:id="sec-pantheon-faq-gnome-and-pantheon" />I 133 - cannot enable both GNOME and Pantheon. 134 - </term> 135 - <listitem> 136 - <para> 137 - This is a known 138 - <link xlink:href="https://github.com/NixOS/nixpkgs/issues/64611">issue</link> 139 - and there is no known workaround. 140 - </para> 141 - </listitem> 142 - </varlistentry> 143 - <varlistentry> 144 - <term> 145 - <anchor xml:id="sec-pantheon-faq-appcenter" />Does AppCenter 146 - work, or is it available? 147 - </term> 148 - <listitem> 149 - <para> 150 - AppCenter has been available since 20.03. Starting from 151 - 21.11, the Flatpak backend should work so you can install 152 - some Flatpak applications using it. However, due to missing 153 - appstream metadata, the Packagekit backend does not function 154 - currently. See this 155 - <link xlink:href="https://github.com/NixOS/nixpkgs/issues/15932">issue</link>. 156 - </para> 157 - <para> 158 - If you are using Pantheon, AppCenter should be installed by 159 - default if you have 160 - <link linkend="module-services-flatpak">Flatpak 161 - support</link> enabled. If you also wish to add the 162 - <literal>appcenter</literal> Flatpak remote: 163 - </para> 164 - <programlisting> 165 - $ flatpak remote-add --if-not-exists appcenter https://flatpak.elementary.io/repo.flatpakrepo 166 - </programlisting> 167 - </listitem> 168 - </varlistentry> 169 - </variablelist> 170 - </section> 171 - </chapter>
+1 -1
nixos/modules/system/boot/loader/external/external.nix
··· 8 8 { 9 9 meta = { 10 10 maintainers = with maintainers; [ cole-h grahamc raitobezarius ]; 11 - doc = ./external.xml; 11 + doc = ./external.md; 12 12 }; 13 13 14 14 options.boot.loader.external = {
-43
nixos/modules/system/boot/loader/external/external.xml
··· 1 - <!-- Do not edit this file directly, edit its companion .md instead 2 - and regenerate this file using nixos/doc/manual/md-to-db.sh --> 3 - <chapter xmlns="http://docbook.org/ns/docbook" xmlns:xlink="http://www.w3.org/1999/xlink" xml:id="sec-bootloader-external"> 4 - <title>External Bootloader Backends</title> 5 - <para> 6 - NixOS has support for several bootloader backends by default: 7 - systemd-boot, grub, uboot, etc. The built-in bootloader backend 8 - support is generic and supports most use cases. Some users may 9 - prefer to create advanced workflows around managing the bootloader 10 - and bootable entries. 11 - </para> 12 - <para> 13 - You can replace the built-in bootloader support with your own 14 - tooling using the <quote>external</quote> bootloader option. 15 - </para> 16 - <para> 17 - Imagine you have created a new package called FooBoot. FooBoot 18 - provides a program at 19 - <literal>${pkgs.fooboot}/bin/fooboot-install</literal> which takes 20 - the system closure’s path as its only argument and configures the 21 - system’s bootloader. 22 - </para> 23 - <para> 24 - You can enable FooBoot like this: 25 - </para> 26 - <programlisting language="nix"> 27 - { pkgs, ... }: { 28 - boot.loader.external = { 29 - enable = true; 30 - installHook = &quot;${pkgs.fooboot}/bin/fooboot-install&quot;; 31 - }; 32 - } 33 - </programlisting> 34 - <section xml:id="sec-bootloader-external-developing"> 35 - <title>Developing Custom Bootloader Backends</title> 36 - <para> 37 - Bootloaders should use 38 - <link xlink:href="https://github.com/NixOS/rfcs/pull/125">RFC-0125</link>’s 39 - Bootspec format and synthesis tools to identify the key properties 40 - for bootable system generations. 41 - </para> 42 - </section> 43 - </chapter>
+63
pkgs/tools/nix/nixos-render-docs/default.nix
··· 1 + { lib 2 + , stdenv 3 + , python3 4 + , python3Minimal 5 + }: 6 + 7 + let 8 + # python3Minimal can't be overridden with packages on Darwin, due to a missing framework. 9 + # Instead of modifying stdenv, we take the easy way out, since most people on Darwin will 10 + # just be hacking on the Nixpkgs manual (which also uses make-options-doc). 11 + python = ((if stdenv.isDarwin then python3 else python3Minimal).override { 12 + self = python; 13 + includeSiteCustomize = true; 14 + }); 15 + 16 + # TODO add our own small test suite, maybe add tests for these deps to channels? 17 + markdown-it-py-no-tests = python.pkgs.markdown-it-py.override { 18 + disableTests = true; 19 + }; 20 + mdit-py-plugins-no-tests = python.pkgs.mdit-py-plugins.override { 21 + markdown-it-py = markdown-it-py-no-tests; 22 + disableTests = true; 23 + }; 24 + in 25 + 26 + python.pkgs.buildPythonApplication { 27 + pname = "nixos-render-docs"; 28 + version = "0.0"; 29 + format = "pyproject"; 30 + 31 + src = lib.cleanSourceWith { 32 + filter = name: type: 33 + lib.cleanSourceFilter name type 34 + && ! (type == "directory" 35 + && builtins.elem 36 + (baseNameOf name) 37 + [ 38 + ".pytest_cache" 39 + ".mypy_cache" 40 + "__pycache__" 41 + ]); 42 + src = ./src; 43 + }; 44 + 45 + nativeBuildInputs = [ 46 + python.pkgs.setuptools 47 + python.pkgs.pytestCheckHook 48 + ]; 49 + 50 + propagatedBuildInputs = [ 51 + markdown-it-py-no-tests 52 + mdit-py-plugins-no-tests 53 + python.pkgs.frozendict 54 + ]; 55 + 56 + pytestFlagsArray = [ "-vvrP" "tests/" ]; 57 + 58 + meta = with lib; { 59 + description = "Renderer for NixOS manual and option docs"; 60 + license = licenses.mit; 61 + maintainers = [ ]; 62 + }; 63 + }
+24
pkgs/tools/nix/nixos-render-docs/src/nixos_render_docs/__init__.py
··· 1 + import argparse 2 + import os 3 + import sys 4 + from typing import Any, Dict 5 + 6 + from .md import Converter 7 + from . import manual 8 + from . import options 9 + 10 + def main() -> None: 11 + parser = argparse.ArgumentParser(description='render nixos manual bits') 12 + 13 + commands = parser.add_subparsers(dest='command', required=True) 14 + 15 + options.build_cli(commands.add_parser('options')) 16 + manual.build_cli(commands.add_parser('manual')) 17 + 18 + args = parser.parse_args() 19 + if args.command == 'options': 20 + options.run_cli(args) 21 + elif args.command == 'manual': 22 + manual.run_cli(args) 23 + else: 24 + raise RuntimeError('command not hooked up', args)
+254
pkgs/tools/nix/nixos-render-docs/src/nixos_render_docs/docbook.py
··· 1 + from collections.abc import Mapping, MutableMapping, Sequence 2 + from frozendict import frozendict # type: ignore[attr-defined] 3 + from typing import Any, cast, Optional, NamedTuple 4 + 5 + import markdown_it 6 + from markdown_it.token import Token 7 + from markdown_it.utils import OptionsDict 8 + from xml.sax.saxutils import escape, quoteattr 9 + 10 + from .md import Renderer 11 + 12 + _xml_id_translate_table = { 13 + ord('*'): ord('_'), 14 + ord('<'): ord('_'), 15 + ord(' '): ord('_'), 16 + ord('>'): ord('_'), 17 + ord('['): ord('_'), 18 + ord(']'): ord('_'), 19 + ord(':'): ord('_'), 20 + ord('"'): ord('_'), 21 + } 22 + def make_xml_id(s: str) -> str: 23 + return s.translate(_xml_id_translate_table) 24 + 25 + class Deflist: 26 + has_dd = False 27 + 28 + class Heading(NamedTuple): 29 + container_tag: str 30 + level: int 31 + 32 + class DocBookRenderer(Renderer): 33 + __output__ = "docbook" 34 + _link_tags: list[str] 35 + _deflists: list[Deflist] 36 + _headings: list[Heading] 37 + 38 + def __init__(self, manpage_urls: Mapping[str, str], parser: Optional[markdown_it.MarkdownIt] = None): 39 + super().__init__(manpage_urls, parser) 40 + self._link_tags = [] 41 + self._deflists = [] 42 + self._headings = [] 43 + 44 + def render(self, tokens: Sequence[Token], options: OptionsDict, 45 + env: MutableMapping[str, Any]) -> str: 46 + result = super().render(tokens, options, env) 47 + result += self._close_headings(None, env) 48 + return result 49 + def renderInline(self, tokens: Sequence[Token], options: OptionsDict, 50 + env: MutableMapping[str, Any]) -> str: 51 + # HACK to support docbook links and xrefs. link handling is only necessary because the docbook 52 + # manpage stylesheet converts - in urls to a mathematical minus, which may be somewhat incorrect. 53 + for i, token in enumerate(tokens): 54 + if token.type != 'link_open': 55 + continue 56 + token.tag = 'link' 57 + # turn [](#foo) into xrefs 58 + if token.attrs['href'][0:1] == '#' and tokens[i + 1].type == 'link_close': # type: ignore[index] 59 + token.tag = "xref" 60 + # turn <x> into links without contents 61 + if tokens[i + 1].type == 'text' and tokens[i + 1].content == token.attrs['href']: 62 + tokens[i + 1].content = '' 63 + 64 + return super().renderInline(tokens, options, env) 65 + 66 + def text(self, token: Token, tokens: Sequence[Token], i: int, options: OptionsDict, 67 + env: MutableMapping[str, Any]) -> str: 68 + return escape(token.content) 69 + def paragraph_open(self, token: Token, tokens: Sequence[Token], i: int, options: OptionsDict, 70 + env: MutableMapping[str, Any]) -> str: 71 + return "<para>" 72 + def paragraph_close(self, token: Token, tokens: Sequence[Token], i: int, options: OptionsDict, 73 + env: MutableMapping[str, Any]) -> str: 74 + return "</para>" 75 + def hardbreak(self, token: Token, tokens: Sequence[Token], i: int, options: OptionsDict, 76 + env: MutableMapping[str, Any]) -> str: 77 + return "<literallayout>\n</literallayout>" 78 + def softbreak(self, token: Token, tokens: Sequence[Token], i: int, options: OptionsDict, 79 + env: MutableMapping[str, Any]) -> str: 80 + # should check options.breaks() and emit hard break if so 81 + return "\n" 82 + def code_inline(self, token: Token, tokens: Sequence[Token], i: int, options: OptionsDict, 83 + env: MutableMapping[str, Any]) -> str: 84 + return f"<literal>{escape(token.content)}</literal>" 85 + def code_block(self, token: Token, tokens: Sequence[Token], i: int, options: OptionsDict, 86 + env: MutableMapping[str, Any]) -> str: 87 + return f"<programlisting>{escape(token.content)}</programlisting>" 88 + def link_open(self, token: Token, tokens: Sequence[Token], i: int, options: OptionsDict, 89 + env: MutableMapping[str, Any]) -> str: 90 + self._link_tags.append(token.tag) 91 + href = cast(str, token.attrs['href']) 92 + (attr, start) = ('linkend', 1) if href[0] == '#' else ('xlink:href', 0) 93 + return f"<{token.tag} {attr}={quoteattr(href[start:])}>" 94 + def link_close(self, token: Token, tokens: Sequence[Token], i: int, options: OptionsDict, 95 + env: MutableMapping[str, Any]) -> str: 96 + return f"</{self._link_tags.pop()}>" 97 + def list_item_open(self, token: Token, tokens: Sequence[Token], i: int, options: OptionsDict, 98 + env: MutableMapping[str, Any]) -> str: 99 + return "<listitem>" 100 + def list_item_close(self, token: Token, tokens: Sequence[Token], i: int, options: OptionsDict, 101 + env: MutableMapping[str, Any]) -> str: 102 + return "</listitem>\n" 103 + # HACK open and close para for docbook change size. remove soon. 104 + def bullet_list_open(self, token: Token, tokens: Sequence[Token], i: int, options: OptionsDict, 105 + env: MutableMapping[str, Any]) -> str: 106 + spacing = ' spacing="compact"' if token.attrs.get('compact', False) else '' 107 + return f"<para><itemizedlist{spacing}>\n" 108 + def bullet_list_close(self, token: Token, tokens: Sequence[Token], i: int, options: OptionsDict, 109 + env: MutableMapping[str, Any]) -> str: 110 + return "\n</itemizedlist></para>" 111 + def em_open(self, token: Token, tokens: Sequence[Token], i: int, options: OptionsDict, 112 + env: MutableMapping[str, Any]) -> str: 113 + return "<emphasis>" 114 + def em_close(self, token: Token, tokens: Sequence[Token], i: int, options: OptionsDict, 115 + env: MutableMapping[str, Any]) -> str: 116 + return "</emphasis>" 117 + def strong_open(self, token: Token, tokens: Sequence[Token], i: int, options: OptionsDict, 118 + env: MutableMapping[str, Any]) -> str: 119 + return "<emphasis role=\"strong\">" 120 + def strong_close(self, token: Token, tokens: Sequence[Token], i: int, options: OptionsDict, 121 + env: MutableMapping[str, Any]) -> str: 122 + return "</emphasis>" 123 + def fence(self, token: Token, tokens: Sequence[Token], i: int, options: OptionsDict, 124 + env: MutableMapping[str, Any]) -> str: 125 + info = f" language={quoteattr(token.info)}" if token.info != "" else "" 126 + return f"<programlisting{info}>{escape(token.content)}</programlisting>" 127 + def blockquote_open(self, token: Token, tokens: Sequence[Token], i: int, options: OptionsDict, 128 + env: MutableMapping[str, Any]) -> str: 129 + return "<para><blockquote>" 130 + def blockquote_close(self, token: Token, tokens: Sequence[Token], i: int, options: OptionsDict, 131 + env: MutableMapping[str, Any]) -> str: 132 + return "</blockquote></para>" 133 + def note_open(self, token: Token, tokens: Sequence[Token], i: int, options: OptionsDict, 134 + env: MutableMapping[str, Any]) -> str: 135 + return "<para><note>" 136 + def note_close(self, token: Token, tokens: Sequence[Token], i: int, options: OptionsDict, 137 + env: MutableMapping[str, Any]) -> str: 138 + return "</note></para>" 139 + def caution_open(self, token: Token, tokens: Sequence[Token], i: int, options: OptionsDict, 140 + env: MutableMapping[str, Any]) -> str: 141 + return "<para><caution>" 142 + def caution_close(self, token: Token, tokens: Sequence[Token], i: int, options: OptionsDict, 143 + env: MutableMapping[str, Any]) -> str: 144 + return "</caution></para>" 145 + def important_open(self, token: Token, tokens: Sequence[Token], i: int, options: OptionsDict, 146 + env: MutableMapping[str, Any]) -> str: 147 + return "<para><important>" 148 + def important_close(self, token: Token, tokens: Sequence[Token], i: int, options: OptionsDict, 149 + env: MutableMapping[str, Any]) -> str: 150 + return "</important></para>" 151 + def tip_open(self, token: Token, tokens: Sequence[Token], i: int, options: OptionsDict, 152 + env: MutableMapping[str, Any]) -> str: 153 + return "<para><tip>" 154 + def tip_close(self, token: Token, tokens: Sequence[Token], i: int, options: OptionsDict, 155 + env: MutableMapping[str, Any]) -> str: 156 + return "</tip></para>" 157 + def warning_open(self, token: Token, tokens: Sequence[Token], i: int, options: OptionsDict, 158 + env: MutableMapping[str, Any]) -> str: 159 + return "<para><warning>" 160 + def warning_close(self, token: Token, tokens: Sequence[Token], i: int, options: OptionsDict, 161 + env: MutableMapping[str, Any]) -> str: 162 + return "</warning></para>" 163 + # markdown-it emits tokens based on the html syntax tree, but docbook is 164 + # slightly different. html has <dl>{<dt/>{<dd/>}}</dl>, 165 + # docbook has <variablelist>{<varlistentry><term/><listitem/></varlistentry>}<variablelist> 166 + # we have to reject multiple definitions for the same term for time being. 167 + def dl_open(self, token: Token, tokens: Sequence[Token], i: int, options: OptionsDict, 168 + env: MutableMapping[str, Any]) -> str: 169 + self._deflists.append(Deflist()) 170 + return "<para><variablelist>" 171 + def dl_close(self, token: Token, tokens: Sequence[Token], i: int, options: OptionsDict, 172 + env: MutableMapping[str, Any]) -> str: 173 + self._deflists.pop() 174 + return "</variablelist></para>" 175 + def dt_open(self, token: Token, tokens: Sequence[Token], i: int, options: OptionsDict, 176 + env: MutableMapping[str, Any]) -> str: 177 + self._deflists[-1].has_dd = False 178 + return "<varlistentry><term>" 179 + def dt_close(self, token: Token, tokens: Sequence[Token], i: int, options: OptionsDict, 180 + env: MutableMapping[str, Any]) -> str: 181 + return "</term>" 182 + def dd_open(self, token: Token, tokens: Sequence[Token], i: int, options: OptionsDict, 183 + env: MutableMapping[str, Any]) -> str: 184 + if self._deflists[-1].has_dd: 185 + raise Exception("multiple definitions per term not supported") 186 + self._deflists[-1].has_dd = True 187 + return "<listitem>" 188 + def dd_close(self, token: Token, tokens: Sequence[Token], i: int, options: OptionsDict, 189 + env: MutableMapping[str, Any]) -> str: 190 + return "</listitem></varlistentry>" 191 + def myst_role(self, token: Token, tokens: Sequence[Token], i: int, options: OptionsDict, 192 + env: MutableMapping[str, Any]) -> str: 193 + if token.meta['name'] == 'command': 194 + return f"<command>{escape(token.content)}</command>" 195 + if token.meta['name'] == 'file': 196 + return f"<filename>{escape(token.content)}</filename>" 197 + if token.meta['name'] == 'var': 198 + return f"<varname>{escape(token.content)}</varname>" 199 + if token.meta['name'] == 'env': 200 + return f"<envar>{escape(token.content)}</envar>" 201 + if token.meta['name'] == 'option': 202 + return f"<option>{escape(token.content)}</option>" 203 + if token.meta['name'] == 'manpage': 204 + [page, section] = [ s.strip() for s in token.content.rsplit('(', 1) ] 205 + section = section[:-1] 206 + man = f"{page}({section})" 207 + title = f"<refentrytitle>{escape(page)}</refentrytitle>" 208 + vol = f"<manvolnum>{escape(section)}</manvolnum>" 209 + ref = f"<citerefentry>{title}{vol}</citerefentry>" 210 + if man in self._manpage_urls: 211 + return f"<link xlink:href={quoteattr(self._manpage_urls[man])}>{ref}</link>" 212 + else: 213 + return ref 214 + raise NotImplementedError("md node not supported yet", token) 215 + def inline_anchor(self, token: Token, tokens: Sequence[Token], i: int, options: OptionsDict, 216 + env: MutableMapping[str, Any]) -> str: 217 + return f'<anchor xml:id={quoteattr(cast(str, token.attrs["id"]))} />' 218 + def ordered_list_open(self, token: Token, tokens: Sequence[Token], i: int, options: OptionsDict, 219 + env: MutableMapping[str, Any]) -> str: 220 + start = f' startingnumber="{token.attrs["start"]}"' if 'start' in token.attrs else "" 221 + spacing = ' spacing="compact"' if token.attrs.get('compact', False) else '' 222 + return f"<orderedlist{start}{spacing}>" 223 + def ordered_list_close(self, token: Token, tokens: Sequence[Token], i: int, options: OptionsDict, 224 + env: MutableMapping[str, Any]) -> str: 225 + return f"</orderedlist>" 226 + def heading_open(self, token: Token, tokens: Sequence[Token], i: int, options: OptionsDict, 227 + env: MutableMapping[str, Any]) -> str: 228 + hlevel = int(token.tag[1:]) 229 + result = self._close_headings(hlevel, env) 230 + (tag, attrs) = self._heading_tag(token, tokens, i, options, env) 231 + self._headings.append(Heading(tag, hlevel)) 232 + attrs_str = "".join([ f" {k}={quoteattr(v)}" for k, v in attrs.items() ]) 233 + return result + f'<{tag}{attrs_str}>\n<title>' 234 + def heading_close(self, token: Token, tokens: Sequence[Token], i: int, options: OptionsDict, 235 + env: MutableMapping[str, Any]) -> str: 236 + return '</title>' 237 + 238 + def _close_headings(self, level: Optional[int], env: MutableMapping[str, Any]) -> str: 239 + # we rely on markdown-it producing h{1..6} tags in token.tag for this to work 240 + result = [] 241 + while len(self._headings): 242 + if level is None or self._headings[-1].level >= level: 243 + result.append(f"</{self._headings[-1].container_tag}>") 244 + self._headings.pop() 245 + else: 246 + break 247 + return "\n".join(result) 248 + 249 + def _heading_tag(self, token: Token, tokens: Sequence[Token], i: int, options: OptionsDict, 250 + env: MutableMapping[str, Any]) -> tuple[str, dict[str, str]]: 251 + attrs = {} 252 + if id := token.attrs.get('id'): 253 + attrs['xml:id'] = cast(str, id) 254 + return ("section", attrs)
+143
pkgs/tools/nix/nixos-render-docs/src/nixos_render_docs/manual.py
··· 1 + import argparse 2 + import json 3 + 4 + from abc import abstractmethod 5 + from collections.abc import MutableMapping, Sequence 6 + from typing import Any, cast, NamedTuple, Optional, Union 7 + from xml.sax.saxutils import escape, quoteattr 8 + from markdown_it.token import Token 9 + from markdown_it.utils import OptionsDict 10 + 11 + from .docbook import DocBookRenderer 12 + from .md import Converter 13 + 14 + class RenderedSection: 15 + id: Optional[str] 16 + chapters: list[str] 17 + 18 + def __init__(self, id: Optional[str]) -> None: 19 + self.id = id 20 + self.chapters = [] 21 + 22 + class BaseConverter(Converter): 23 + _sections: list[RenderedSection] 24 + 25 + def __init__(self, manpage_urls: dict[str, str]): 26 + super().__init__(manpage_urls) 27 + self._sections = [] 28 + 29 + def add_section(self, id: Optional[str], chapters: list[str]) -> None: 30 + self._sections.append(RenderedSection(id)) 31 + for content in chapters: 32 + self._md.renderer._title_seen = False # type: ignore[attr-defined] 33 + self._sections[-1].chapters.append(self._render(content)) 34 + 35 + @abstractmethod 36 + def finalize(self) -> str: raise NotImplementedError() 37 + 38 + class ManualDocBookRenderer(DocBookRenderer): 39 + # needed to check correctness of chapters. 40 + # we may want to use front matter instead of this kind of heuristic. 41 + _title_seen = False 42 + 43 + def _heading_tag(self, token: Token, tokens: Sequence[Token], i: int, options: OptionsDict, 44 + env: MutableMapping[str, Any]) -> tuple[str, dict[str, str]]: 45 + (tag, attrs) = super()._heading_tag(token, tokens, i, options, env) 46 + if self._title_seen: 47 + if token.tag == 'h1': 48 + raise RuntimeError("only one title heading allowed", token) 49 + return (tag, attrs) 50 + self._title_seen = True 51 + return ("chapter", attrs | { 52 + 'xmlns': "http://docbook.org/ns/docbook", 53 + 'xmlns:xlink': "http://www.w3.org/1999/xlink", 54 + }) 55 + 56 + # TODO minimize docbook diffs with existing conversions. remove soon. 57 + def paragraph_open(self, token: Token, tokens: Sequence[Token], i: int, options: OptionsDict, 58 + env: MutableMapping[str, Any]) -> str: 59 + return super().paragraph_open(token, tokens, i, options, env) + "\n " 60 + def paragraph_close(self, token: Token, tokens: Sequence[Token], i: int, options: OptionsDict, 61 + env: MutableMapping[str, Any]) -> str: 62 + return "\n" + super().paragraph_close(token, tokens, i, options, env) 63 + def code_block(self, token: Token, tokens: Sequence[Token], i: int, options: OptionsDict, 64 + env: MutableMapping[str, Any]) -> str: 65 + return f"<programlisting>\n{escape(token.content)}</programlisting>" 66 + def fence(self, token: Token, tokens: Sequence[Token], i: int, options: OptionsDict, 67 + env: MutableMapping[str, Any]) -> str: 68 + info = f" language={quoteattr(token.info)}" if token.info != "" else "" 69 + return f"<programlisting{info}>\n{escape(token.content)}</programlisting>" 70 + 71 + class DocBookConverter(BaseConverter): 72 + __renderer__ = ManualDocBookRenderer 73 + 74 + def finalize(self) -> str: 75 + result = [] 76 + 77 + for section in self._sections: 78 + id = "id=" + quoteattr(section.id) if section.id is not None else "" 79 + result.append(f'<section {id}>') 80 + result += section.chapters 81 + result.append(f'</section>') 82 + 83 + return "\n".join(result) 84 + 85 + 86 + 87 + class Section: 88 + id: Optional[str] = None 89 + chapters: list[str] 90 + 91 + def __init__(self) -> None: 92 + self.chapters = [] 93 + 94 + class SectionAction(argparse.Action): 95 + def __call__(self, parser: argparse.ArgumentParser, ns: argparse.Namespace, 96 + values: Union[str, Sequence[Any], None], opt_str: Optional[str] = None) -> None: 97 + sections = getattr(ns, self.dest) 98 + if sections is None: sections = [] 99 + sections.append(Section()) 100 + setattr(ns, self.dest, sections) 101 + 102 + class SectionIDAction(argparse.Action): 103 + def __call__(self, parser: argparse.ArgumentParser, ns: argparse.Namespace, 104 + values: Union[str, Sequence[Any], None], opt_str: Optional[str] = None) -> None: 105 + sections = getattr(ns, self.dest) 106 + if sections is None: raise argparse.ArgumentError(self, "no active section") 107 + sections[-1].id = cast(str, values) 108 + 109 + class ChaptersAction(argparse.Action): 110 + def __call__(self, parser: argparse.ArgumentParser, ns: argparse.Namespace, 111 + values: Union[str, Sequence[Any], None], opt_str: Optional[str] = None) -> None: 112 + sections = getattr(ns, self.dest) 113 + if sections is None: raise argparse.ArgumentError(self, "no active section") 114 + sections[-1].chapters.extend(cast(Sequence[str], values)) 115 + 116 + def _build_cli_db(p: argparse.ArgumentParser) -> None: 117 + p.add_argument('--manpage-urls', required=True) 118 + p.add_argument("outfile") 119 + p.add_argument("--section", dest="contents", action=SectionAction, nargs=0) 120 + p.add_argument("--section-id", dest="contents", action=SectionIDAction) 121 + p.add_argument("--chapters", dest="contents", action=ChaptersAction, nargs='+') 122 + 123 + def _run_cli_db(args: argparse.Namespace) -> None: 124 + with open(args.manpage_urls, 'r') as manpage_urls: 125 + md = DocBookConverter(json.load(manpage_urls)) 126 + for section in args.contents: 127 + chapters = [] 128 + for p in section.chapters: 129 + with open(p, 'r') as f: 130 + chapters.append(f.read()) 131 + md.add_section(section.id, chapters) 132 + with open(args.outfile, 'w') as f: 133 + f.write(md.finalize()) 134 + 135 + def build_cli(p: argparse.ArgumentParser) -> None: 136 + formats = p.add_subparsers(dest='format', required=True) 137 + _build_cli_db(formats.add_parser('docbook')) 138 + 139 + def run_cli(args: argparse.Namespace) -> None: 140 + if args.format == 'docbook': 141 + _run_cli_db(args) 142 + else: 143 + raise RuntimeError('format not hooked up', args)
+385
pkgs/tools/nix/nixos-render-docs/src/nixos_render_docs/md.py
··· 1 + from abc import ABC 2 + from collections.abc import Mapping, MutableMapping, Sequence 3 + from frozendict import frozendict # type: ignore[attr-defined] 4 + from typing import Any, Callable, Optional 5 + 6 + import re 7 + 8 + from .types import RenderFn 9 + 10 + import markdown_it 11 + from markdown_it.token import Token 12 + from markdown_it.utils import OptionsDict 13 + from mdit_py_plugins.container import container_plugin # type: ignore[attr-defined] 14 + from mdit_py_plugins.deflist import deflist_plugin # type: ignore[attr-defined] 15 + from mdit_py_plugins.myst_role import myst_role_plugin # type: ignore[attr-defined] 16 + 17 + _md_escape_table = { 18 + ord('*'): '\\*', 19 + ord('<'): '\\<', 20 + ord('['): '\\[', 21 + ord('`'): '\\`', 22 + ord('.'): '\\.', 23 + ord('#'): '\\#', 24 + ord('&'): '\\&', 25 + ord('\\'): '\\\\', 26 + } 27 + def md_escape(s: str) -> str: 28 + return s.translate(_md_escape_table) 29 + 30 + class Renderer(markdown_it.renderer.RendererProtocol): 31 + _admonitions: dict[str, tuple[RenderFn, RenderFn]] 32 + _admonition_stack: list[str] 33 + 34 + def __init__(self, manpage_urls: Mapping[str, str], parser: Optional[markdown_it.MarkdownIt] = None): 35 + self._manpage_urls = manpage_urls 36 + self.rules = { 37 + 'text': self.text, 38 + 'paragraph_open': self.paragraph_open, 39 + 'paragraph_close': self.paragraph_close, 40 + 'hardbreak': self.hardbreak, 41 + 'softbreak': self.softbreak, 42 + 'code_inline': self.code_inline, 43 + 'code_block': self.code_block, 44 + 'link_open': self.link_open, 45 + 'link_close': self.link_close, 46 + 'list_item_open': self.list_item_open, 47 + 'list_item_close': self.list_item_close, 48 + 'bullet_list_open': self.bullet_list_open, 49 + 'bullet_list_close': self.bullet_list_close, 50 + 'em_open': self.em_open, 51 + 'em_close': self.em_close, 52 + 'strong_open': self.strong_open, 53 + 'strong_close': self.strong_close, 54 + 'fence': self.fence, 55 + 'blockquote_open': self.blockquote_open, 56 + 'blockquote_close': self.blockquote_close, 57 + 'dl_open': self.dl_open, 58 + 'dl_close': self.dl_close, 59 + 'dt_open': self.dt_open, 60 + 'dt_close': self.dt_close, 61 + 'dd_open': self.dd_open, 62 + 'dd_close': self.dd_close, 63 + 'myst_role': self.myst_role, 64 + "container_admonition_open": self.admonition_open, 65 + "container_admonition_close": self.admonition_close, 66 + "inline_anchor": self.inline_anchor, 67 + "heading_open": self.heading_open, 68 + "heading_close": self.heading_close, 69 + "ordered_list_open": self.ordered_list_open, 70 + "ordered_list_close": self.ordered_list_close, 71 + } 72 + 73 + self._admonitions = { 74 + "{.note}": (self.note_open, self.note_close), 75 + "{.caution}": (self.caution_open,self.caution_close), 76 + "{.tip}": (self.tip_open, self.tip_close), 77 + "{.important}": (self.important_open, self.important_close), 78 + "{.warning}": (self.warning_open, self.warning_close), 79 + } 80 + self._admonition_stack = [] 81 + 82 + def admonition_open(self, token: Token, tokens: Sequence[Token], i: int, options: OptionsDict, 83 + env: MutableMapping[str, Any]) -> str: 84 + tag = token.info.strip() 85 + self._admonition_stack.append(tag) 86 + return self._admonitions[tag][0](token, tokens, i, options, env) 87 + def admonition_close(self, token: Token, tokens: Sequence[Token], i: int, options: OptionsDict, 88 + env: MutableMapping[str, Any]) -> str: 89 + return self._admonitions[self._admonition_stack.pop()][1](token, tokens, i, options, env) 90 + 91 + def render(self, tokens: Sequence[Token], options: OptionsDict, 92 + env: MutableMapping[str, Any]) -> str: 93 + def do_one(i: int, token: Token) -> str: 94 + if token.type == "inline": 95 + assert token.children is not None 96 + return self.renderInline(token.children, options, env) 97 + elif token.type in self.rules: 98 + return self.rules[token.type](tokens[i], tokens, i, options, env) 99 + else: 100 + raise NotImplementedError("md token not supported yet", token) 101 + return "".join(map(lambda arg: do_one(*arg), enumerate(tokens))) 102 + def renderInline(self, tokens: Sequence[Token], options: OptionsDict, 103 + env: MutableMapping[str, Any]) -> str: 104 + def do_one(i: int, token: Token) -> str: 105 + if token.type in self.rules: 106 + return self.rules[token.type](tokens[i], tokens, i, options, env) 107 + else: 108 + raise NotImplementedError("md token not supported yet", token) 109 + return "".join(map(lambda arg: do_one(*arg), enumerate(tokens))) 110 + 111 + def text(self, token: Token, tokens: Sequence[Token], i: int, options: OptionsDict, 112 + env: MutableMapping[str, Any]) -> str: 113 + raise RuntimeError("md token not supported", token) 114 + def paragraph_open(self, token: Token, tokens: Sequence[Token], i: int, options: OptionsDict, 115 + env: MutableMapping[str, Any]) -> str: 116 + raise RuntimeError("md token not supported", token) 117 + def paragraph_close(self, token: Token, tokens: Sequence[Token], i: int, options: OptionsDict, 118 + env: MutableMapping[str, Any]) -> str: 119 + raise RuntimeError("md token not supported", token) 120 + def hardbreak(self, token: Token, tokens: Sequence[Token], i: int, options: OptionsDict, 121 + env: MutableMapping[str, Any]) -> str: 122 + raise RuntimeError("md token not supported", token) 123 + def softbreak(self, token: Token, tokens: Sequence[Token], i: int, options: OptionsDict, 124 + env: MutableMapping[str, Any]) -> str: 125 + raise RuntimeError("md token not supported", token) 126 + def code_inline(self, token: Token, tokens: Sequence[Token], i: int, options: OptionsDict, 127 + env: MutableMapping[str, Any]) -> str: 128 + raise RuntimeError("md token not supported", token) 129 + def code_block(self, token: Token, tokens: Sequence[Token], i: int, options: OptionsDict, 130 + env: MutableMapping[str, Any]) -> str: 131 + raise RuntimeError("md token not supported", token) 132 + def link_open(self, token: Token, tokens: Sequence[Token], i: int, options: OptionsDict, 133 + env: MutableMapping[str, Any]) -> str: 134 + raise RuntimeError("md token not supported", token) 135 + def link_close(self, token: Token, tokens: Sequence[Token], i: int, options: OptionsDict, 136 + env: MutableMapping[str, Any]) -> str: 137 + raise RuntimeError("md token not supported", token) 138 + def list_item_open(self, token: Token, tokens: Sequence[Token], i: int, options: OptionsDict, 139 + env: MutableMapping[str, Any]) -> str: 140 + raise RuntimeError("md token not supported", token) 141 + def list_item_close(self, token: Token, tokens: Sequence[Token], i: int, options: OptionsDict, 142 + env: MutableMapping[str, Any]) -> str: 143 + raise RuntimeError("md token not supported", token) 144 + def bullet_list_open(self, token: Token, tokens: Sequence[Token], i: int, options: OptionsDict, 145 + env: MutableMapping[str, Any]) -> str: 146 + raise RuntimeError("md token not supported", token) 147 + def bullet_list_close(self, token: Token, tokens: Sequence[Token], i: int, options: OptionsDict, 148 + env: MutableMapping[str, Any]) -> str: 149 + raise RuntimeError("md token not supported", token) 150 + def em_open(self, token: Token, tokens: Sequence[Token], i: int, options: OptionsDict, 151 + env: MutableMapping[str, Any]) -> str: 152 + raise RuntimeError("md token not supported", token) 153 + def em_close(self, token: Token, tokens: Sequence[Token], i: int, options: OptionsDict, 154 + env: MutableMapping[str, Any]) -> str: 155 + raise RuntimeError("md token not supported", token) 156 + def strong_open(self, token: Token, tokens: Sequence[Token], i: int, options: OptionsDict, 157 + env: MutableMapping[str, Any]) -> str: 158 + raise RuntimeError("md token not supported", token) 159 + def strong_close(self, token: Token, tokens: Sequence[Token], i: int, options: OptionsDict, 160 + env: MutableMapping[str, Any]) -> str: 161 + raise RuntimeError("md token not supported", token) 162 + def fence(self, token: Token, tokens: Sequence[Token], i: int, options: OptionsDict, 163 + env: MutableMapping[str, Any]) -> str: 164 + raise RuntimeError("md token not supported", token) 165 + def blockquote_open(self, token: Token, tokens: Sequence[Token], i: int, options: OptionsDict, 166 + env: MutableMapping[str, Any]) -> str: 167 + raise RuntimeError("md token not supported", token) 168 + def blockquote_close(self, token: Token, tokens: Sequence[Token], i: int, options: OptionsDict, 169 + env: MutableMapping[str, Any]) -> str: 170 + raise RuntimeError("md token not supported", token) 171 + def note_open(self, token: Token, tokens: Sequence[Token], i: int, options: OptionsDict, 172 + env: MutableMapping[str, Any]) -> str: 173 + raise RuntimeError("md token not supported", token) 174 + def note_close(self, token: Token, tokens: Sequence[Token], i: int, options: OptionsDict, 175 + env: MutableMapping[str, Any]) -> str: 176 + raise RuntimeError("md token not supported", token) 177 + def caution_open(self, token: Token, tokens: Sequence[Token], i: int, options: OptionsDict, 178 + env: MutableMapping[str, Any]) -> str: 179 + raise RuntimeError("md token not supported", token) 180 + def caution_close(self, token: Token, tokens: Sequence[Token], i: int, options: OptionsDict, 181 + env: MutableMapping[str, Any]) -> str: 182 + raise RuntimeError("md token not supported", token) 183 + def important_open(self, token: Token, tokens: Sequence[Token], i: int, options: OptionsDict, 184 + env: MutableMapping[str, Any]) -> str: 185 + raise RuntimeError("md token not supported", token) 186 + def important_close(self, token: Token, tokens: Sequence[Token], i: int, options: OptionsDict, 187 + env: MutableMapping[str, Any]) -> str: 188 + raise RuntimeError("md token not supported", token) 189 + def tip_open(self, token: Token, tokens: Sequence[Token], i: int, options: OptionsDict, 190 + env: MutableMapping[str, Any]) -> str: 191 + raise RuntimeError("md token not supported", token) 192 + def tip_close(self, token: Token, tokens: Sequence[Token], i: int, options: OptionsDict, 193 + env: MutableMapping[str, Any]) -> str: 194 + raise RuntimeError("md token not supported", token) 195 + def warning_open(self, token: Token, tokens: Sequence[Token], i: int, options: OptionsDict, 196 + env: MutableMapping[str, Any]) -> str: 197 + raise RuntimeError("md token not supported", token) 198 + def warning_close(self, token: Token, tokens: Sequence[Token], i: int, options: OptionsDict, 199 + env: MutableMapping[str, Any]) -> str: 200 + raise RuntimeError("md token not supported", token) 201 + def dl_open(self, token: Token, tokens: Sequence[Token], i: int, options: OptionsDict, 202 + env: MutableMapping[str, Any]) -> str: 203 + raise RuntimeError("md token not supported", token) 204 + def dl_close(self, token: Token, tokens: Sequence[Token], i: int, options: OptionsDict, 205 + env: MutableMapping[str, Any]) -> str: 206 + raise RuntimeError("md token not supported", token) 207 + def dt_open(self, token: Token, tokens: Sequence[Token], i: int, options: OptionsDict, 208 + env: MutableMapping[str, Any]) -> str: 209 + raise RuntimeError("md token not supported", token) 210 + def dt_close(self, token: Token, tokens: Sequence[Token], i: int, options: OptionsDict, 211 + env: MutableMapping[str, Any]) -> str: 212 + raise RuntimeError("md token not supported", token) 213 + def dd_open(self, token: Token, tokens: Sequence[Token], i: int, options: OptionsDict, 214 + env: MutableMapping[str, Any]) -> str: 215 + raise RuntimeError("md token not supported", token) 216 + def dd_close(self, token: Token, tokens: Sequence[Token], i: int, options: OptionsDict, 217 + env: MutableMapping[str, Any]) -> str: 218 + raise RuntimeError("md token not supported", token) 219 + def myst_role(self, token: Token, tokens: Sequence[Token], i: int, options: OptionsDict, 220 + env: MutableMapping[str, Any]) -> str: 221 + raise RuntimeError("md token not supported", token) 222 + def inline_anchor(self, token: Token, tokens: Sequence[Token], i: int, options: OptionsDict, 223 + env: MutableMapping[str, Any]) -> str: 224 + raise RuntimeError("md token not supported", token) 225 + def heading_open(self, token: Token, tokens: Sequence[Token], i: int, options: OptionsDict, 226 + env: MutableMapping[str, Any]) -> str: 227 + raise RuntimeError("md token not supported", token) 228 + def heading_close(self, token: Token, tokens: Sequence[Token], i: int, options: OptionsDict, 229 + env: MutableMapping[str, Any]) -> str: 230 + raise RuntimeError("md token not supported", token) 231 + def ordered_list_open(self, token: Token, tokens: Sequence[Token], i: int, options: OptionsDict, 232 + env: MutableMapping[str, Any]) -> str: 233 + raise RuntimeError("md token not supported", token) 234 + def ordered_list_close(self, token: Token, tokens: Sequence[Token], i: int, options: OptionsDict, 235 + env: MutableMapping[str, Any]) -> str: 236 + raise RuntimeError("md token not supported", token) 237 + 238 + def _is_escaped(src: str, pos: int) -> bool: 239 + found = 0 240 + while pos >= 0 and src[pos] == '\\': 241 + found += 1 242 + pos -= 1 243 + return found % 2 == 1 244 + 245 + _INLINE_ANCHOR_PATTERN = re.compile(r"\{\s*#([\w-]+)\s*\}") 246 + 247 + def _inline_anchor_plugin(md: markdown_it.MarkdownIt) -> None: 248 + def inline_anchor(state: markdown_it.rules_inline.StateInline, silent: bool) -> bool: 249 + if state.src[state.pos] != '[': 250 + return False 251 + if _is_escaped(state.src, state.pos - 1): 252 + return False 253 + 254 + # treat the inline span like a link label for simplicity. 255 + label_begin = state.pos + 1 256 + label_end = markdown_it.helpers.parseLinkLabel(state, state.pos) 257 + input_end = state.posMax 258 + if label_end < 0: 259 + return False 260 + 261 + # match id 262 + match = _INLINE_ANCHOR_PATTERN.match(state.src[label_end + 1 : ]) 263 + if not match: 264 + return False 265 + 266 + if not silent: 267 + token = state.push("inline_anchor", "", 0) # type: ignore[no-untyped-call] 268 + token.attrs['id'] = match[1] 269 + 270 + state.pos = label_begin 271 + state.posMax = label_end 272 + state.md.inline.tokenize(state) 273 + 274 + state.pos = label_end + match.end() + 1 275 + state.posMax = input_end 276 + return True 277 + 278 + md.inline.ruler.before("link", "inline_anchor", inline_anchor) 279 + 280 + def _inline_comment_plugin(md: markdown_it.MarkdownIt) -> None: 281 + def inline_comment(state: markdown_it.rules_inline.StateInline, silent: bool) -> bool: 282 + if state.src[state.pos : state.pos + 4] != '<!--': 283 + return False 284 + if _is_escaped(state.src, state.pos - 1): 285 + return False 286 + for i in range(state.pos + 4, state.posMax - 2): 287 + if state.src[i : i + 3] == '-->': # --> 288 + state.pos = i + 3 289 + return True 290 + 291 + return False 292 + 293 + md.inline.ruler.after("autolink", "inline_comment", inline_comment) 294 + 295 + def _block_comment_plugin(md: markdown_it.MarkdownIt) -> None: 296 + def block_comment(state: markdown_it.rules_block.StateBlock, startLine: int, endLine: int, 297 + silent: bool) -> bool: 298 + pos = state.bMarks[startLine] + state.tShift[startLine] 299 + posMax = state.eMarks[startLine] 300 + 301 + if state.src[pos : pos + 4] != '<!--': 302 + return False 303 + 304 + nextLine = startLine 305 + while nextLine < endLine: 306 + pos = state.bMarks[nextLine] + state.tShift[nextLine] 307 + posMax = state.eMarks[nextLine] 308 + 309 + if state.src[posMax - 3 : posMax] == '-->': 310 + state.line = nextLine + 1 311 + return True 312 + 313 + nextLine += 1 314 + 315 + return False 316 + 317 + md.block.ruler.after("code", "block_comment", block_comment) 318 + 319 + _HEADER_ID_RE = re.compile(r"\s*\{\s*\#([\w-]+)\s*\}\s*$") 320 + 321 + class Converter(ABC): 322 + __renderer__: Callable[[Mapping[str, str], markdown_it.MarkdownIt], Renderer] 323 + 324 + def __init__(self, manpage_urls: Mapping[str, str]): 325 + self._manpage_urls = frozendict(manpage_urls) 326 + 327 + self._md = markdown_it.MarkdownIt( 328 + "commonmark", 329 + { 330 + 'maxNesting': 100, # default is 20 331 + 'html': False, # not useful since we target many formats 332 + 'typographer': True, # required for smartquotes 333 + }, 334 + renderer_cls=lambda parser: self.__renderer__(self._manpage_urls, parser) 335 + ) 336 + self._md.use( 337 + container_plugin, 338 + name="admonition", 339 + validate=lambda name, *args: ( 340 + name.strip() in self._md.renderer._admonitions # type: ignore[attr-defined] 341 + ) 342 + ) 343 + self._md.use(deflist_plugin) 344 + self._md.use(myst_role_plugin) 345 + self._md.use(_inline_anchor_plugin) 346 + self._md.use(_inline_comment_plugin) 347 + self._md.use(_block_comment_plugin) 348 + self._md.enable(["smartquotes", "replacements"]) 349 + 350 + def _post_parse(self, tokens: list[Token]) -> list[Token]: 351 + for i in range(0, len(tokens)): 352 + # parse header IDs. this is purposely simple and doesn't support 353 + # classes or other inds of attributes. 354 + if tokens[i].type == 'heading_open': 355 + children = tokens[i + 1].children 356 + assert children is not None 357 + if len(children) == 0 or children[-1].type != 'text': 358 + continue 359 + if m := _HEADER_ID_RE.search(children[-1].content): 360 + tokens[i].attrs['id'] = m[1] 361 + children[-1].content = children[-1].content[:-len(m[0])].rstrip() 362 + 363 + # markdown-it signifies wide lists by setting the wrapper paragraphs 364 + # of each item to hidden. this is not useful for our stylesheets, which 365 + # signify this with a special css class on list elements instead. 366 + wide_stack = [] 367 + for i in range(0, len(tokens)): 368 + if tokens[i].type in [ 'bullet_list_open', 'ordered_list_open' ]: 369 + wide_stack.append([i, True]) 370 + elif tokens[i].type in [ 'bullet_list_close', 'ordered_list_close' ]: 371 + (idx, compact) = wide_stack.pop() 372 + tokens[idx].attrs['compact'] = compact 373 + elif len(wide_stack) > 0 and tokens[i].type == 'paragraph_open' and not tokens[i].hidden: 374 + wide_stack[-1][1] = False 375 + 376 + return tokens 377 + 378 + def _parse(self, src: str, env: Optional[MutableMapping[str, Any]] = None) -> list[Token]: 379 + tokens = self._md.parse(src, env if env is not None else {}) 380 + return self._post_parse(tokens) 381 + 382 + def _render(self, src: str) -> str: 383 + env: dict[str, Any] = {} 384 + tokens = self._parse(src, env) 385 + return self._md.renderer.render(tokens, self._md.options, env) # type: ignore[no-any-return]
+284
pkgs/tools/nix/nixos-render-docs/src/nixos_render_docs/options.py
··· 1 + import argparse 2 + import json 3 + 4 + from abc import abstractmethod 5 + from collections.abc import MutableMapping, Sequence 6 + from markdown_it.utils import OptionsDict 7 + from markdown_it.token import Token 8 + from typing import Any, Optional 9 + from xml.sax.saxutils import escape, quoteattr 10 + 11 + from .docbook import DocBookRenderer, make_xml_id 12 + from .md import Converter, md_escape 13 + from .types import OptionLoc, Option, RenderedOption 14 + 15 + def option_is(option: Option, key: str, typ: str) -> Optional[dict[str, str]]: 16 + if key not in option: 17 + return None 18 + if type(option[key]) != dict: 19 + return None 20 + if option[key].get('_type') != typ: # type: ignore[union-attr] 21 + return None 22 + return option[key] # type: ignore[return-value] 23 + 24 + class BaseConverter(Converter): 25 + _options: dict[str, RenderedOption] 26 + 27 + def __init__(self, manpage_urls: dict[str, str], 28 + revision: str, 29 + document_type: str, 30 + varlist_id: str, 31 + id_prefix: str, 32 + markdown_by_default: bool): 33 + super().__init__(manpage_urls) 34 + self._options = {} 35 + self._revision = revision 36 + self._document_type = document_type 37 + self._varlist_id = varlist_id 38 + self._id_prefix = id_prefix 39 + self._markdown_by_default = markdown_by_default 40 + 41 + def _format_decl_def_loc(self, loc: OptionLoc) -> tuple[Optional[str], str]: 42 + # locations can be either plain strings (specific to nixpkgs), or attrsets 43 + # { name = "foo/bar.nix"; url = "https://github.com/....."; } 44 + if isinstance(loc, str): 45 + # Hyperlink the filename either to the NixOS github 46 + # repository (if it’s a module and we have a revision number), 47 + # or to the local filesystem. 48 + if not loc.startswith('/'): 49 + if self._revision == 'local': 50 + href = f"https://github.com/NixOS/nixpkgs/blob/master/{loc}" 51 + else: 52 + href = f"https://github.com/NixOS/nixpkgs/blob/{self._revision}/{loc}" 53 + else: 54 + href = f"file://{loc}" 55 + # Print the filename and make it user-friendly by replacing the 56 + # /nix/store/<hash> prefix by the default location of nixos 57 + # sources. 58 + if not loc.startswith('/'): 59 + name = f"<nixpkgs/{loc}>" 60 + elif 'nixops' in loc and '/nix/' in loc: 61 + name = f"<nixops/{loc[loc.find('/nix/') + 5:]}>" 62 + else: 63 + name = loc 64 + return (href, name) 65 + else: 66 + return (loc['url'] if 'url' in loc else None, loc['name']) 67 + 68 + @abstractmethod 69 + def _decl_def_header(self, header: str) -> list[str]: raise NotImplementedError() 70 + 71 + @abstractmethod 72 + def _decl_def_entry(self, href: Optional[str], name: str) -> list[str]: raise NotImplementedError() 73 + 74 + @abstractmethod 75 + def _decl_def_footer(self) -> list[str]: raise NotImplementedError() 76 + 77 + def _render_decl_def(self, header: str, locs: list[OptionLoc]) -> list[str]: 78 + result = [] 79 + result += self._decl_def_header(header) 80 + for loc in locs: 81 + href, name = self._format_decl_def_loc(loc) 82 + result += self._decl_def_entry(href, name) 83 + result += self._decl_def_footer() 84 + return result 85 + 86 + def _render_code(self, option: Option, key: str) -> list[str]: 87 + if lit := option_is(option, key, 'literalMD'): 88 + return [ self._render(f"*{key.capitalize()}:*\n{lit['text']}") ] 89 + elif lit := option_is(option, key, 'literalExpression'): 90 + code = lit['text'] 91 + # for multi-line code blocks we only have to count ` runs at the beginning 92 + # of a line, but this is much easier. 93 + multiline = '\n' in code 94 + longest, current = (0, 0) 95 + for c in code: 96 + current = current + 1 if c == '`' else 0 97 + longest = max(current, longest) 98 + # inline literals need a space to separate ticks from content, code blocks 99 + # need newlines. inline literals need one extra tick, code blocks need three. 100 + ticks, sep = ('`' * (longest + (3 if multiline else 1)), '\n' if multiline else ' ') 101 + code = f"{ticks}{sep}{code}{sep}{ticks}" 102 + return [ self._render(f"*{key.capitalize()}:*\n{code}") ] 103 + elif key in option: 104 + raise Exception(f"{key} has unrecognized type", option[key]) 105 + else: 106 + return [] 107 + 108 + def _render_description(self, desc: str | dict[str, str]) -> list[str]: 109 + if isinstance(desc, str) and self._markdown_by_default: 110 + return [ self._render(desc) ] 111 + elif isinstance(desc, dict) and desc.get('_type') == 'mdDoc': 112 + return [ self._render(desc['text']) ] 113 + else: 114 + raise Exception("description has unrecognized type", desc) 115 + 116 + @abstractmethod 117 + def _related_packages_header(self) -> list[str]: raise NotImplementedError() 118 + 119 + def _convert_one(self, option: dict[str, Any]) -> list[str]: 120 + result = [] 121 + 122 + if desc := option.get('description'): 123 + result += self._render_description(desc) 124 + if typ := option.get('type'): 125 + ro = " *(read only)*" if option.get('readOnly', False) else "" 126 + result.append(self._render(f"*Type:* {md_escape(typ)}{ro}")) 127 + 128 + result += self._render_code(option, 'default') 129 + result += self._render_code(option, 'example') 130 + 131 + if related := option.get('relatedPackages'): 132 + result += self._related_packages_header() 133 + result.append(self._render(related)) 134 + if decl := option.get('declarations'): 135 + result += self._render_decl_def("Declared by", decl) 136 + if defs := option.get('definitions'): 137 + result += self._render_decl_def("Defined by", defs) 138 + 139 + return result 140 + 141 + def add_options(self, options: dict[str, Any]) -> None: 142 + for (name, option) in options.items(): 143 + try: 144 + self._options[name] = RenderedOption(option['loc'], self._convert_one(option)) 145 + except Exception as e: 146 + raise Exception(f"Failed to render option {name}") from e 147 + 148 + @abstractmethod 149 + def finalize(self) -> str: raise NotImplementedError() 150 + 151 + class OptionsDocBookRenderer(DocBookRenderer): 152 + def heading_open(self, token: Token, tokens: Sequence[Token], i: int, options: OptionsDict, 153 + env: MutableMapping[str, Any]) -> str: 154 + raise RuntimeError("md token not supported in options doc", token) 155 + def heading_close(self, token: Token, tokens: Sequence[Token], i: int, options: OptionsDict, 156 + env: MutableMapping[str, Any]) -> str: 157 + raise RuntimeError("md token not supported in options doc", token) 158 + 159 + # TODO keep optionsDocBook diff small. remove soon if rendering is still good. 160 + def ordered_list_open(self, token: Token, tokens: Sequence[Token], i: int, options: OptionsDict, 161 + env: MutableMapping[str, Any]) -> str: 162 + token.attrs['compact'] = False 163 + return super().ordered_list_open(token, tokens, i, options, env) 164 + def bullet_list_open(self, token: Token, tokens: Sequence[Token], i: int, options: OptionsDict, 165 + env: MutableMapping[str, Any]) -> str: 166 + token.attrs['compact'] = False 167 + return super().bullet_list_open(token, tokens, i, options, env) 168 + 169 + class DocBookConverter(BaseConverter): 170 + __renderer__ = OptionsDocBookRenderer 171 + 172 + def _render_code(self, option: dict[str, Any], key: str) -> list[str]: 173 + if lit := option_is(option, key, 'literalDocBook'): 174 + return [ f"<para><emphasis>{key.capitalize()}:</emphasis> {lit['text']}</para>" ] 175 + else: 176 + return super()._render_code(option, key) 177 + 178 + def _render_description(self, desc: str | dict[str, Any]) -> list[str]: 179 + if isinstance(desc, str) and not self._markdown_by_default: 180 + return [ f"<nixos:option-description><para>{desc}</para></nixos:option-description>" ] 181 + else: 182 + return super()._render_description(desc) 183 + 184 + def _related_packages_header(self) -> list[str]: 185 + return [ 186 + "<para>", 187 + " <emphasis>Related packages:</emphasis>", 188 + "</para>", 189 + ] 190 + 191 + def _decl_def_header(self, header: str) -> list[str]: 192 + return [ 193 + f"<para><emphasis>{header}:</emphasis></para>", 194 + "<simplelist>" 195 + ] 196 + 197 + def _decl_def_entry(self, href: Optional[str], name: str) -> list[str]: 198 + if href is not None: 199 + href = " xlink:href=" + quoteattr(href) 200 + return [ 201 + f"<member><filename{href}>", 202 + escape(name), 203 + "</filename></member>" 204 + ] 205 + 206 + def _decl_def_footer(self) -> list[str]: 207 + return [ "</simplelist>" ] 208 + 209 + def finalize(self) -> str: 210 + keys = list(self._options.keys()) 211 + keys.sort(key=lambda opt: [ (0 if p.startswith("enable") else 1 if p.startswith("package") else 2, p) 212 + for p in self._options[opt].loc ]) 213 + 214 + result = [] 215 + 216 + result.append('<?xml version="1.0" encoding="UTF-8"?>') 217 + if self._document_type == 'appendix': 218 + result += [ 219 + '<appendix xmlns="http://docbook.org/ns/docbook"', 220 + ' xml:id="appendix-configuration-options">', 221 + ' <title>Configuration Options</title>', 222 + ] 223 + result += [ 224 + f'<variablelist xmlns:xlink="http://www.w3.org/1999/xlink"', 225 + ' xmlns:nixos="tag:nixos.org"', 226 + ' xmlns="http://docbook.org/ns/docbook"', 227 + f' xml:id="{self._varlist_id}">', 228 + ] 229 + 230 + for name in keys: 231 + id = make_xml_id(self._id_prefix + name) 232 + result += [ 233 + "<varlistentry>", 234 + # NOTE adding extra spaces here introduces spaces into xref link expansions 235 + (f"<term xlink:href={quoteattr('#' + id)} xml:id={quoteattr(id)}>" + 236 + f"<option>{escape(name)}</option></term>"), 237 + "<listitem>" 238 + ] 239 + result += self._options[name].lines 240 + result += [ 241 + "</listitem>", 242 + "</varlistentry>" 243 + ] 244 + 245 + result.append("</variablelist>") 246 + if self._document_type == 'appendix': 247 + result.append("</appendix>") 248 + 249 + return "\n".join(result) 250 + 251 + def _build_cli_db(p: argparse.ArgumentParser) -> None: 252 + p.add_argument('--manpage-urls', required=True) 253 + p.add_argument('--revision', required=True) 254 + p.add_argument('--document-type', required=True) 255 + p.add_argument('--varlist-id', required=True) 256 + p.add_argument('--id-prefix', required=True) 257 + p.add_argument('--markdown-by-default', default=False, action='store_true') 258 + p.add_argument("infile") 259 + p.add_argument("outfile") 260 + 261 + def _run_cli_db(args: argparse.Namespace) -> None: 262 + with open(args.manpage_urls, 'r') as manpage_urls: 263 + md = DocBookConverter( 264 + json.load(manpage_urls), 265 + revision = args.revision, 266 + document_type = args.document_type, 267 + varlist_id = args.varlist_id, 268 + id_prefix = args.id_prefix, 269 + markdown_by_default = args.markdown_by_default) 270 + 271 + with open(args.infile, 'r') as f: 272 + md.add_options(json.load(f)) 273 + with open(args.outfile, 'w') as f: 274 + f.write(md.finalize()) 275 + 276 + def build_cli(p: argparse.ArgumentParser) -> None: 277 + formats = p.add_subparsers(dest='format', required=True) 278 + _build_cli_db(formats.add_parser('docbook')) 279 + 280 + def run_cli(args: argparse.Namespace) -> None: 281 + if args.format == 'docbook': 282 + _run_cli_db(args) 283 + else: 284 + raise RuntimeError('format not hooked up', args)
+13
pkgs/tools/nix/nixos-render-docs/src/nixos_render_docs/types.py
··· 1 + from collections.abc import Sequence, MutableMapping 2 + from typing import Any, Callable, Optional, Tuple, NamedTuple 3 + 4 + from markdown_it.token import Token 5 + from markdown_it.utils import OptionsDict 6 + 7 + OptionLoc = str | dict[str, str] 8 + Option = dict[str, str | dict[str, str] | list[OptionLoc]] 9 + 10 + RenderedOption = NamedTuple('RenderedOption', [('loc', list[str]), 11 + ('lines', list[str])]) 12 + 13 + RenderFn = Callable[[Token, Sequence[Token], int, OptionsDict, MutableMapping[str, Any]], str]
+15
pkgs/tools/nix/nixos-render-docs/src/pyproject.toml
··· 1 + [project] 2 + name = "nixos-render-docs" 3 + version = "0.0" 4 + description = "Renderer for NixOS manual and option docs" 5 + classifiers = [ 6 + "Programming Language :: Python :: 3", 7 + "License :: OSI Approved :: MIT License", 8 + "Operating System :: OS Independent", 9 + ] 10 + 11 + [project.scripts] 12 + nixos-render-docs = "nixos_render_docs:main" 13 + 14 + [build-system] 15 + requires = ["setuptools"]
+102
pkgs/tools/nix/nixos-render-docs/src/tests/test_headings.py
··· 1 + import nixos_render_docs 2 + 3 + from markdown_it.token import Token 4 + 5 + class Converter(nixos_render_docs.md.Converter): 6 + # actual renderer doesn't matter, we're just parsing. 7 + __renderer__ = nixos_render_docs.docbook.DocBookRenderer 8 + 9 + def test_heading_id_absent() -> None: 10 + c = Converter({}) 11 + assert c._parse("# foo") == [ 12 + Token(type='heading_open', tag='h1', nesting=1, attrs={}, map=[0, 1], level=0, children=None, 13 + content='', markup='#', info='', meta={}, block=True, hidden=False), 14 + Token(type='inline', tag='', nesting=0, attrs={}, map=[0, 1], level=1, 15 + children=[ 16 + Token(type='text', tag='', nesting=0, attrs={}, map=None, level=0, children=None, 17 + content='foo', markup='', info='', meta={}, block=False, hidden=False) 18 + ], 19 + content='foo', markup='', info='', meta={}, block=True, hidden=False), 20 + Token(type='heading_close', tag='h1', nesting=-1, attrs={}, map=None, level=0, children=None, 21 + content='', markup='#', info='', meta={}, block=True, hidden=False) 22 + ] 23 + 24 + def test_heading_id_present() -> None: 25 + c = Converter({}) 26 + assert c._parse("# foo {#foo}\n## bar { #bar}\n### bal { #bal} ") == [ 27 + Token(type='heading_open', tag='h1', nesting=1, attrs={'id': 'foo'}, map=[0, 1], level=0, 28 + children=None, content='', markup='#', info='', meta={}, block=True, hidden=False), 29 + Token(type='inline', tag='', nesting=0, attrs={}, map=[0, 1], level=1, 30 + content='foo {#foo}', markup='', info='', meta={}, block=True, hidden=False, 31 + children=[ 32 + Token(type='text', tag='', nesting=0, attrs={}, map=None, level=0, children=None, 33 + content='foo', markup='', info='', meta={}, block=False, hidden=False) 34 + ]), 35 + Token(type='heading_close', tag='h1', nesting=-1, attrs={}, map=None, level=0, children=None, 36 + content='', markup='#', info='', meta={}, block=True, hidden=False), 37 + Token(type='heading_open', tag='h2', nesting=1, attrs={'id': 'bar'}, map=[1, 2], level=0, 38 + children=None, content='', markup='##', info='', meta={}, block=True, hidden=False), 39 + Token(type='inline', tag='', nesting=0, attrs={}, map=[1, 2], level=1, 40 + content='bar { #bar}', markup='', info='', meta={}, block=True, hidden=False, 41 + children=[ 42 + Token(type='text', tag='', nesting=0, attrs={}, map=None, level=0, children=None, 43 + content='bar', markup='', info='', meta={}, block=False, hidden=False) 44 + ]), 45 + Token(type='heading_close', tag='h2', nesting=-1, attrs={}, map=None, level=0, children=None, 46 + content='', markup='##', info='', meta={}, block=True, hidden=False), 47 + Token(type='heading_open', tag='h3', nesting=1, attrs={'id': 'bal'}, map=[2, 3], level=0, 48 + children=None, content='', markup='###', info='', meta={}, block=True, hidden=False), 49 + Token(type='inline', tag='', nesting=0, attrs={}, map=[2, 3], level=1, 50 + content='bal { #bal}', markup='', info='', meta={}, block=True, hidden=False, 51 + children=[ 52 + Token(type='text', tag='', nesting=0, attrs={}, map=None, level=0, children=None, 53 + content='bal', markup='', info='', meta={}, block=False, hidden=False) 54 + ]), 55 + Token(type='heading_close', tag='h3', nesting=-1, attrs={}, map=None, level=0, children=None, 56 + content='', markup='###', info='', meta={}, block=True, hidden=False) 57 + ] 58 + 59 + def test_heading_id_incomplete() -> None: 60 + c = Converter({}) 61 + assert c._parse("# foo {#}") == [ 62 + Token(type='heading_open', tag='h1', nesting=1, attrs={}, map=[0, 1], level=0, children=None, 63 + content='', markup='#', info='', meta={}, block=True, hidden=False), 64 + Token(type='inline', tag='', nesting=0, attrs={}, map=[0, 1], level=1, 65 + content='foo {#}', markup='', info='', meta={}, block=True, hidden=False, 66 + children=[ 67 + Token(type='text', tag='', nesting=0, attrs={}, map=None, level=0, children=None, 68 + content='foo {#}', markup='', info='', meta={}, block=False, hidden=False) 69 + ]), 70 + Token(type='heading_close', tag='h1', nesting=-1, attrs={}, map=None, level=0, children=None, 71 + content='', markup='#', info='', meta={}, block=True, hidden=False) 72 + ] 73 + 74 + def test_heading_id_double() -> None: 75 + c = Converter({}) 76 + assert c._parse("# foo {#a} {#b}") == [ 77 + Token(type='heading_open', tag='h1', nesting=1, attrs={'id': 'b'}, map=[0, 1], level=0, 78 + children=None, content='', markup='#', info='', meta={}, block=True, hidden=False), 79 + Token(type='inline', tag='', nesting=0, attrs={}, map=[0, 1], level=1, 80 + content='foo {#a} {#b}', markup='', info='', meta={}, block=True, hidden=False, 81 + children=[ 82 + Token(type='text', tag='', nesting=0, attrs={}, map=None, level=0, children=None, 83 + content='foo {#a}', markup='', info='', meta={}, block=False, hidden=False) 84 + ]), 85 + Token(type='heading_close', tag='h1', nesting=-1, attrs={}, map=None, level=0, children=None, 86 + content='', markup='#', info='', meta={}, block=True, hidden=False) 87 + ] 88 + 89 + def test_heading_id_suffixed() -> None: 90 + c = Converter({}) 91 + assert c._parse("# foo {#a} s") == [ 92 + Token(type='heading_open', tag='h1', nesting=1, attrs={}, map=[0, 1], level=0, 93 + children=None, content='', markup='#', info='', meta={}, block=True, hidden=False), 94 + Token(type='inline', tag='', nesting=0, attrs={}, map=[0, 1], level=1, 95 + content='foo {#a} s', markup='', info='', meta={}, block=True, hidden=False, 96 + children=[ 97 + Token(type='text', tag='', nesting=0, attrs={}, map=None, level=0, children=None, 98 + content='foo {#a} s', markup='', info='', meta={}, block=False, hidden=False) 99 + ]), 100 + Token(type='heading_close', tag='h1', nesting=-1, attrs={}, map=None, level=0, children=None, 101 + content='', markup='#', info='', meta={}, block=True, hidden=False) 102 + ]
+182
pkgs/tools/nix/nixos-render-docs/src/tests/test_lists.py
··· 1 + import nixos_render_docs 2 + import pytest 3 + 4 + from markdown_it.token import Token 5 + 6 + class Converter(nixos_render_docs.md.Converter): 7 + # actual renderer doesn't matter, we're just parsing. 8 + __renderer__ = nixos_render_docs.docbook.DocBookRenderer 9 + 10 + @pytest.mark.parametrize("ordered", [True, False]) 11 + def test_list_wide(ordered: bool) -> None: 12 + t, tag, m, e1, e2, i1, i2 = ( 13 + ("ordered", "ol", ".", "1.", "2.", "1", "2") if ordered else ("bullet", "ul", "-", "-", "-", "", "") 14 + ) 15 + c = Converter({}) 16 + assert c._parse(f"{e1} a\n\n{e2} b") == [ 17 + Token(type=f'{t}_list_open', tag=tag, nesting=1, attrs={'compact': False}, map=[0, 3], level=0, 18 + children=None, content='', markup=m, info='', meta={}, block=True, hidden=False), 19 + Token(type='list_item_open', tag='li', nesting=1, attrs={}, map=[0, 2], level=1, children=None, 20 + content='', markup=m, info=i1, meta={}, block=True, hidden=False), 21 + Token(type='paragraph_open', tag='p', nesting=1, attrs={}, map=[0, 1], level=2, children=None, 22 + content='', markup='', info='', meta={}, block=True, hidden=False), 23 + Token(type='inline', tag='', nesting=0, attrs={}, map=[0, 1], level=3, 24 + content='a', markup='', info='', meta={}, block=True, hidden=False, 25 + children=[ 26 + Token(type='text', tag='', nesting=0, attrs={}, map=None, level=0, children=None, 27 + content='a', markup='', info='', meta={}, block=False, hidden=False) 28 + ]), 29 + Token(type='paragraph_close', tag='p', nesting=-1, attrs={}, map=None, level=2, children=None, 30 + content='', markup='', info='', meta={}, block=True, hidden=False), 31 + Token(type='list_item_close', tag='li', nesting=-1, attrs={}, map=None, level=1, children=None, 32 + content='', markup=m, info='', meta={}, block=True, hidden=False), 33 + Token(type='list_item_open', tag='li', nesting=1, attrs={}, map=[2, 3], level=1, children=None, 34 + content='', markup=m, info=i2, meta={}, block=True, hidden=False), 35 + Token(type='paragraph_open', tag='p', nesting=1, attrs={}, map=[2, 3], level=2, children=None, 36 + content='', markup='', info='', meta={}, block=True, hidden=False), 37 + Token(type='inline', tag='', nesting=0, attrs={}, map=[2, 3], level=3, 38 + content='b', markup='', info='', meta={}, block=True, hidden=False, 39 + children=[ 40 + Token(type='text', tag='', nesting=0, attrs={}, map=None, level=0, children=None, 41 + content='b', markup='', info='', meta={}, block=False, hidden=False) 42 + ]), 43 + Token(type='paragraph_close', tag='p', nesting=-1, attrs={}, map=None, level=2, children=None, 44 + content='', markup='', info='', meta={}, block=True, hidden=False), 45 + Token(type='list_item_close', tag='li', nesting=-1, attrs={}, map=None, level=1, children=None, 46 + content='', markup=m, info='', meta={}, block=True, hidden=False), 47 + Token(type=f'{t}_list_close', tag=tag, nesting=-1, attrs={}, map=None, level=0, children=None, 48 + content='', markup=m, info='', meta={}, block=True, hidden=False) 49 + ] 50 + 51 + @pytest.mark.parametrize("ordered", [True, False]) 52 + def test_list_narrow(ordered: bool) -> None: 53 + t, tag, m, e1, e2, i1, i2 = ( 54 + ("ordered", "ol", ".", "1.", "2.", "1", "2") if ordered else ("bullet", "ul", "-", "-", "-", "", "") 55 + ) 56 + c = Converter({}) 57 + assert c._parse(f"{e1} a\n{e2} b") == [ 58 + Token(type=f'{t}_list_open', tag=tag, nesting=1, attrs={'compact': True}, map=[0, 2], level=0, 59 + children=None, content='', markup=m, info='', meta={}, block=True, hidden=False), 60 + Token(type='list_item_open', tag='li', nesting=1, attrs={}, map=[0, 1], level=1, children=None, 61 + content='', markup=m, info=i1, meta={}, block=True, hidden=False), 62 + Token(type='paragraph_open', tag='p', nesting=1, attrs={}, map=[0, 1], level=2, children=None, 63 + content='', markup='', info='', meta={}, block=True, hidden=True), 64 + Token(type='inline', tag='', nesting=0, attrs={}, map=[0, 1], level=3, 65 + content='a', markup='', info='', meta={}, block=True, hidden=False, 66 + children=[ 67 + Token(type='text', tag='', nesting=0, attrs={}, map=None, level=0, children=None, 68 + content='a', markup='', info='', meta={}, block=False, hidden=False) 69 + ]), 70 + Token(type='paragraph_close', tag='p', nesting=-1, attrs={}, map=None, level=2, children=None, 71 + content='', markup='', info='', meta={}, block=True, hidden=True), 72 + Token(type='list_item_close', tag='li', nesting=-1, attrs={}, map=None, level=1, children=None, 73 + content='', markup=m, info='', meta={}, block=True, hidden=False), 74 + Token(type='list_item_open', tag='li', nesting=1, attrs={}, map=[1, 2], level=1, children=None, 75 + content='', markup=m, info=i2, meta={}, block=True, hidden=False), 76 + Token(type='paragraph_open', tag='p', nesting=1, attrs={}, map=[1, 2], level=2, children=None, 77 + content='', markup='', info='', meta={}, block=True, hidden=True), 78 + Token(type='inline', tag='', nesting=0, attrs={}, map=[1, 2], level=3, 79 + content='b', markup='', info='', meta={}, block=True, hidden=False, 80 + children=[ 81 + Token(type='text', tag='', nesting=0, attrs={}, map=None, level=0, children=None, 82 + content='b', markup='', info='', meta={}, block=False, hidden=False) 83 + ]), 84 + Token(type='paragraph_close', tag='p', nesting=-1, attrs={}, map=None, level=2, children=None, 85 + content='', markup='', info='', meta={}, block=True, hidden=True), 86 + Token(type='list_item_close', tag='li', nesting=-1, attrs={}, map=None, level=1, children=None, 87 + content='', markup=m, info='', meta={}, block=True, hidden=False), 88 + Token(type=f'{t}_list_close', tag=tag, nesting=-1, attrs={}, map=None, level=0, children=None, 89 + content='', markup=m, info='', meta={}, block=True, hidden=False) 90 + ] 91 + assert c._parse(f"{e1} - a\n{e2} b") == [ 92 + Token(type=f'{t}_list_open', tag=tag, nesting=1, attrs={'compact': True}, map=[0, 2], level=0, 93 + children=None, content='', markup=m, info='', meta={}, block=True, hidden=False), 94 + Token(type='list_item_open', tag='li', nesting=1, attrs={}, map=[0, 1], level=1, children=None, 95 + content='', markup=m, info=i1, meta={}, block=True, hidden=False), 96 + Token(type='bullet_list_open', tag='ul', nesting=1, attrs={'compact': True}, map=[0, 1], level=2, 97 + children=None, content='', markup='-', info='', meta={}, block=True, hidden=False), 98 + Token(type='list_item_open', tag='li', nesting=1, attrs={}, map=[0, 1], level=3, children=None, 99 + content='', markup='-', info='', meta={}, block=True, hidden=False), 100 + Token(type='paragraph_open', tag='p', nesting=1, attrs={}, map=[0, 1], level=4, children=None, 101 + content='', markup='', info='', meta={}, block=True, hidden=True), 102 + Token(type='inline', tag='', nesting=0, attrs={}, map=[0, 1], level=5, 103 + content='a', markup='', info='', meta={}, block=True, hidden=False, 104 + children=[ 105 + Token(type='text', tag='', nesting=0, attrs={}, map=None, level=0, children=None, 106 + content='a', markup='', info='', meta={}, block=False, hidden=False) 107 + ]), 108 + Token(type='paragraph_close', tag='p', nesting=-1, attrs={}, map=None, level=4, children=None, 109 + content='', markup='', info='', meta={}, block=True, hidden=True), 110 + Token(type='list_item_close', tag='li', nesting=-1, attrs={}, map=None, level=3, children=None, 111 + content='', markup='-', info='', meta={}, block=True, hidden=False), 112 + Token(type='bullet_list_close', tag='ul', nesting=-1, attrs={}, map=None, level=2, children=None, 113 + content='', markup='-', info='', meta={}, block=True, hidden=False), 114 + Token(type='list_item_close', tag='li', nesting=-1, attrs={}, map=None, level=1, children=None, 115 + content='', markup=m, info='', meta={}, block=True, hidden=False), 116 + Token(type='list_item_open', tag='li', nesting=1, attrs={}, map=[1, 2], level=1, children=None, 117 + content='', markup=m, info=i2, meta={}, block=True, hidden=False), 118 + Token(type='paragraph_open', tag='p', nesting=1, attrs={}, map=[1, 2], level=2, children=None, 119 + content='', markup='', info='', meta={}, block=True, hidden=True), 120 + Token(type='inline', tag='', nesting=0, attrs={}, map=[1, 2], level=3, 121 + content='b', markup='', info='', meta={}, block=True, hidden=False, 122 + children=[ 123 + Token(type='text', tag='', nesting=0, attrs={}, map=None, level=0, children=None, 124 + content='b', markup='', info='', meta={}, block=False, hidden=False) 125 + ]), 126 + Token(type='paragraph_close', tag='p', nesting=-1, attrs={}, map=None, level=2, children=None, 127 + content='', markup='', info='', meta={}, block=True, hidden=True), 128 + Token(type='list_item_close', tag='li', nesting=-1, attrs={}, map=None, level=1, children=None, 129 + content='', markup=m, info='', meta={}, block=True, hidden=False), 130 + Token(type=f'{t}_list_close', tag=tag, nesting=-1, attrs={}, map=None, level=0, children=None, 131 + content='', markup=m, info='', meta={}, block=True, hidden=False) 132 + ] 133 + assert c._parse(f"{e1} - a\n{e2} - b") == [ 134 + Token(type=f'{t}_list_open', tag=tag, nesting=1, attrs={'compact': True}, map=[0, 2], level=0, 135 + children=None, content='', markup=m, info='', meta={}, block=True, hidden=False), 136 + Token(type='list_item_open', tag='li', nesting=1, attrs={}, map=[0, 1], level=1, children=None, 137 + content='', markup=m, info=i1, meta={}, block=True, hidden=False), 138 + Token(type='bullet_list_open', tag='ul', nesting=1, attrs={'compact': True}, map=[0, 1], level=2, 139 + children=None, content='', markup='-', info='', meta={}, block=True, hidden=False), 140 + Token(type='list_item_open', tag='li', nesting=1, attrs={}, map=[0, 1], level=3, children=None, 141 + content='', markup='-', info='', meta={}, block=True, hidden=False), 142 + Token(type='paragraph_open', tag='p', nesting=1, attrs={}, map=[0, 1], level=4, children=None, 143 + content='', markup='', info='', meta={}, block=True, hidden=True), 144 + Token(type='inline', tag='', nesting=0, attrs={}, map=[0, 1], level=5, 145 + content='a', markup='', info='', meta={}, block=True, hidden=False, 146 + children=[ 147 + Token(type='text', tag='', nesting=0, attrs={}, map=None, level=0, children=None, 148 + content='a', markup='', info='', meta={}, block=False, hidden=False) 149 + ]), 150 + Token(type='paragraph_close', tag='p', nesting=-1, attrs={}, map=None, level=4, children=None, 151 + content='', markup='', info='', meta={}, block=True, hidden=True), 152 + Token(type='list_item_close', tag='li', nesting=-1, attrs={}, map=None, level=3, children=None, 153 + content='', markup='-', info='', meta={}, block=True, hidden=False), 154 + Token(type='bullet_list_close', tag='ul', nesting=-1, attrs={}, map=None, level=2, children=None, 155 + content='', markup='-', info='', meta={}, block=True, hidden=False), 156 + Token(type='list_item_close', tag='li', nesting=-1, attrs={}, map=None, level=1, children=None, 157 + content='', markup=m, info='', meta={}, block=True, hidden=False), 158 + Token(type='list_item_open', tag='li', nesting=1, attrs={}, map=[1, 2], level=1, children=None, 159 + content='', markup=m, info=i2, meta={}, block=True, hidden=False), 160 + Token(type='bullet_list_open', tag='ul', nesting=1, attrs={'compact': True}, map=[1, 2], level=2, 161 + children=None, content='', markup='-', info='', meta={}, block=True, hidden=False), 162 + Token(type='list_item_open', tag='li', nesting=1, attrs={}, map=[1, 2], level=3, children=None, 163 + content='', markup='-', info='', meta={}, block=True, hidden=False), 164 + Token(type='paragraph_open', tag='p', nesting=1, attrs={}, map=[1, 2], level=4, children=None, 165 + content='', markup='', info='', meta={}, block=True, hidden=True), 166 + Token(type='inline', tag='', nesting=0, attrs={}, map=[1, 2], level=5, 167 + content='b', markup='', info='', meta={}, block=True, hidden=False, 168 + children=[ 169 + Token(type='text', tag='', nesting=0, attrs={}, map=None, level=0, children=None, 170 + content='b', markup='', info='', meta={}, block=False, hidden=False) 171 + ]), 172 + Token(type='paragraph_close', tag='p', nesting=-1, attrs={}, map=None, level=4, children=None, 173 + content='', markup='', info='', meta={}, block=True, hidden=True), 174 + Token(type='list_item_close', tag='li', nesting=-1, attrs={}, map=None, level=3, children=None, 175 + content='', markup='-', info='', meta={}, block=True, hidden=False), 176 + Token(type='bullet_list_close', tag='ul', nesting=-1, attrs={}, map=None, level=2, children=None, 177 + content='', markup='-', info='', meta={}, block=True, hidden=False), 178 + Token(type='list_item_close', tag='li', nesting=-1, attrs={}, map=None, level=1, children=None, 179 + content='', markup=m, info='', meta={}, block=True, hidden=False), 180 + Token(type=f'{t}_list_close', tag=tag, nesting=-1, attrs={}, map=None, level=0, children=None, 181 + content='', markup=m, info='', meta={}, block=True, hidden=False) 182 + ]
+14
pkgs/tools/nix/nixos-render-docs/src/tests/test_options.py
··· 1 + import nixos_render_docs 2 + 3 + from markdown_it.token import Token 4 + import pytest 5 + 6 + def test_option_headings() -> None: 7 + c = nixos_render_docs.options.DocBookConverter({}, 'local', 'none', 'vars', 'opt-', False) 8 + with pytest.raises(RuntimeError) as exc: 9 + c._render("# foo") 10 + assert exc.value.args[0] == 'md token not supported in options doc' 11 + assert exc.value.args[1] == Token( 12 + type='heading_open', tag='h1', nesting=1, attrs={}, map=[0, 1], level=0, children=None, 13 + content='', markup='#', info='', meta={}, block=True, hidden=False 14 + )
+279
pkgs/tools/nix/nixos-render-docs/src/tests/test_plugins.py
··· 1 + import nixos_render_docs 2 + 3 + from markdown_it.token import Token 4 + 5 + class Converter(nixos_render_docs.md.Converter): 6 + # actual renderer doesn't matter, we're just parsing. 7 + __renderer__ = nixos_render_docs.docbook.DocBookRenderer 8 + 9 + def test_inline_anchor_simple() -> None: 10 + c = Converter({}) 11 + assert c._parse("[]{#test}") == [ 12 + Token(type='paragraph_open', tag='p', nesting=1, attrs={}, map=[0, 1], level=0, children=None, 13 + content='', markup='', info='', meta={}, block=True, hidden=False), 14 + Token(type='inline', tag='', nesting=0, attrs={}, map=[0, 1], level=1, content='[]{#test}', 15 + markup='', info='', meta={}, block=True, hidden=False, 16 + children=[ 17 + Token(type='inline_anchor', tag='', nesting=0, attrs={'id': 'test'}, map=None, level=0, 18 + children=None, content='', markup='', info='', meta={}, block=False, hidden=False) 19 + ]), 20 + Token(type='paragraph_close', tag='p', nesting=-1, attrs={}, map=None, level=0, 21 + children=None, content='', markup='', info='', meta={}, block=True, hidden=False) 22 + ] 23 + 24 + def test_inline_anchor_formatted() -> None: 25 + c = Converter({}) 26 + assert c._parse("a[b c `d` ***e***]{#test}f") == [ 27 + Token(type='paragraph_open', tag='p', nesting=1, attrs={}, map=[0, 1], level=0, 28 + children=None, content='', markup='', info='', meta={}, block=True, hidden=False), 29 + Token(type='inline', tag='', nesting=0, attrs={}, map=[0, 1], level=1, 30 + content='a[b c `d` ***e***]{#test}f', markup='', info='', meta={}, block=True, hidden=False, 31 + children=[ 32 + Token(type='text', tag='', nesting=0, attrs={}, map=None, level=0, 33 + children=None, content='a', markup='', info='', meta={}, block=False, hidden=False), 34 + Token(type='inline_anchor', tag='', nesting=0, attrs={'id': 'test'}, map=None, level=0, 35 + children=None, content='', markup='', info='', meta={}, block=False, hidden=False), 36 + Token(type='text', tag='', nesting=0, attrs={}, map=None, level=0, children=None, 37 + content='b c ', markup='', info='', meta={}, block=False, hidden=False), 38 + Token(type='code_inline', tag='code', nesting=0, attrs={}, map=None, level=0, 39 + children=None, content='d', markup='`', info='', meta={}, block=False, hidden=False), 40 + Token(type='text', tag='', nesting=0, attrs={}, map=None, level=0, children=None, 41 + content=' ', markup='', info='', meta={}, block=False, hidden=False), 42 + Token(type='em_open', tag='em', nesting=1, attrs={}, map=None, level=0, children=None, 43 + content='', markup='*', info='', meta={}, block=False, hidden=False), 44 + Token(type='text', tag='', nesting=0, attrs={}, map=None, level=1, children=None, 45 + content='', markup='', info='', meta={}, block=False, hidden=False), 46 + Token(type='strong_open', tag='strong', nesting=1, attrs={}, map=None, level=1, 47 + children=None, content='', markup='**', info='', meta={}, block=False, hidden=False), 48 + Token(type='text', tag='', nesting=0, attrs={}, map=None, level=2, children=None, 49 + content='e', markup='', info='', meta={}, block=False, hidden=False), 50 + Token(type='strong_close', tag='strong', nesting=-1, attrs={}, map=None, level=1, 51 + children=None, content='', markup='**', info='', meta={}, block=False, hidden=False), 52 + Token(type='text', tag='', nesting=0, attrs={}, map=None, level=1, children=None, 53 + content='', markup='', info='', meta={}, block=False, hidden=False), 54 + Token(type='em_close', tag='em', nesting=-1, attrs={}, map=None, level=0, children=None, 55 + content='', markup='*', info='', meta={}, block=False, hidden=False), 56 + Token(type='text', tag='', nesting=0, attrs={}, map=None, level=0, children=None, 57 + content='f', markup='', info='', meta={}, block=False, hidden=False) 58 + ]), 59 + Token(type='paragraph_close', tag='p', nesting=-1, attrs={}, map=None, level=0, children=None, 60 + content='', markup='', info='', meta={}, block=True, hidden=False) 61 + ] 62 + 63 + def test_inline_anchor_in_heading() -> None: 64 + c = Converter({}) 65 + # inline anchors in headers are allowed, but header attributes should be preferred 66 + assert c._parse("# foo []{#bar} baz") == [ 67 + Token(type='heading_open', tag='h1', nesting=1, attrs={}, map=[0, 1], level=0, children=None, 68 + content='', markup='#', info='', meta={}, block=True, hidden=False), 69 + Token(type='inline', tag='', nesting=0, attrs={}, map=[0, 1], level=1, 70 + content='foo []{#bar} baz', markup='', info='', meta={}, block=True, hidden=False, 71 + children=[ 72 + Token(type='text', tag='', nesting=0, attrs={}, map=None, level=0, children=None, 73 + content='foo ', markup='', info='', meta={}, block=False, hidden=False), 74 + Token(type='inline_anchor', tag='', nesting=0, attrs={'id': 'bar'}, map=None, level=0, 75 + children=None, content='', markup='', info='', meta={}, block=False, hidden=False), 76 + Token(type='text', tag='', nesting=0, attrs={}, map=None, level=0, children=None, 77 + content=' baz', markup='', info='', meta={}, block=False, hidden=False) 78 + ]), 79 + Token(type='heading_close', tag='h1', nesting=-1, attrs={}, map=None, level=0, children=None, 80 + content='', markup='#', info='', meta={}, block=True, hidden=False) 81 + ] 82 + 83 + def test_inline_anchor_on_links() -> None: 84 + c = Converter({}) 85 + assert c._parse("[ [a](#bar) ]{#foo}") == [ 86 + Token(type='paragraph_open', tag='p', nesting=1, attrs={}, map=[0, 1], level=0, children=None, 87 + content='', markup='', info='', meta={}, block=True, hidden=False), 88 + Token(type='inline', tag='', nesting=0, attrs={}, map=[0, 1], level=1, content='[ [a](#bar) ]{#foo}', 89 + markup='', info='', meta={}, block=True, hidden=False, 90 + children=[ 91 + Token(type='inline_anchor', tag='', nesting=0, attrs={'id': 'foo'}, map=None, level=0, 92 + children=None, content='', markup='', info='', meta={}, block=False, hidden=False), 93 + Token(type='text', tag='', nesting=0, attrs={}, map=None, level=0, children=None, 94 + content=' ', markup='', info='', meta={}, block=False, hidden=False), 95 + Token(type='link_open', tag='a', nesting=1, attrs={'href': '#bar'}, map=None, level=0, 96 + children=None, content='', markup='', info='', meta={}, block=False, hidden=False), 97 + Token(type='text', tag='', nesting=0, attrs={}, map=None, level=1, children=None, 98 + content='a', markup='', info='', meta={}, block=False, hidden=False), 99 + Token(type='link_close', tag='a', nesting=-1, attrs={}, map=None, level=0, children=None, 100 + content='', markup='', info='', meta={}, block=False, hidden=False), 101 + Token(type='text', tag='', nesting=0, attrs={}, map=None, level=0, children=None, 102 + content=' ', markup='', info='', meta={}, block=False, hidden=False) 103 + ]), 104 + Token(type='paragraph_close', tag='p', nesting=-1, attrs={}, map=None, level=0, children=None, 105 + content='', markup='', info='', meta={}, block=True, hidden=False) 106 + ] 107 + 108 + def test_inline_anchor_nested() -> None: 109 + # inline anchors may contain more anchors (even though this is a bit pointless) 110 + c = Converter({}) 111 + assert c._parse("[ [a]{#bar} ]{#foo}") == [ 112 + Token(type='paragraph_open', tag='p', nesting=1, attrs={}, map=[0, 1], level=0, children=None, 113 + content='', markup='', info='', meta={}, block=True, hidden=False), 114 + Token(type='inline', tag='', nesting=0, attrs={}, map=[0, 1], level=1, 115 + content='[ [a]{#bar} ]{#foo}', markup='', info='', meta={}, block=True, hidden=False, 116 + children=[ 117 + Token(type='inline_anchor', tag='', nesting=0, attrs={'id': 'foo'}, map=None, level=0, 118 + children=None, content='', markup='', info='', meta={}, block=False, hidden=False), 119 + Token(type='text', tag='', nesting=0, attrs={}, map=None, level=0, children=None, 120 + content=' ', markup='', info='', meta={}, block=False, hidden=False), 121 + Token(type='inline_anchor', tag='', nesting=0, attrs={'id': 'bar'}, map=None, level=0, 122 + children=None, content='', markup='', info='', meta={}, block=False, hidden=False), 123 + Token(type='text', tag='', nesting=0, attrs={}, map=None, level=0, children=None, 124 + content='a ', markup='', info='', meta={}, block=False, hidden=False) 125 + ]), 126 + Token(type='paragraph_close', tag='p', nesting=-1, attrs={}, map=None, level=0, children=None, 127 + content='', markup='', info='', meta={}, block=True, hidden=False) 128 + ] 129 + 130 + def test_inline_anchor_escaping() -> None: 131 + c = Converter({}) 132 + assert c._parse("\\[a]{#bar}") == [ 133 + Token(type='paragraph_open', tag='p', nesting=1, attrs={}, map=[0, 1], level=0, children=None, 134 + content='', markup='', info='', meta={}, block=True, hidden=False), 135 + Token(type='inline', tag='', nesting=0, attrs={}, map=[0, 1], level=1, 136 + content='\\[a]{#bar}', markup='', info='', meta={}, block=True, hidden=False, 137 + children=[ 138 + Token(type='text', tag='', nesting=0, attrs={}, map=None, level=0, children=None, 139 + content='[a]{#bar}', markup='', info='', meta={}, block=False, hidden=False) 140 + ]), 141 + Token(type='paragraph_close', tag='p', nesting=-1, attrs={}, map=None, level=0, children=None, 142 + content='', markup='', info='', meta={}, block=True, hidden=False) 143 + ] 144 + assert c._parse("\\\\[a]{#bar}") == [ 145 + Token(type='paragraph_open', tag='p', nesting=1, attrs={}, map=[0, 1], level=0, children=None, 146 + content='', markup='', info='', meta={}, block=True, hidden=False), 147 + Token(type='inline', tag='', nesting=0, attrs={}, map=[0, 1], level=1, 148 + content='\\\\[a]{#bar}', markup='', info='', meta={}, block=True, hidden=False, 149 + children=[ 150 + Token(type='text', tag='', nesting=0, attrs={}, map=None, level=0, children=None, 151 + content='\\', markup='', info='', meta={}, block=False, hidden=False), 152 + Token(type='inline_anchor', tag='', nesting=0, attrs={'id': 'bar'}, map=None, level=0, 153 + children=None, content='', markup='', info='', meta={}, block=False, hidden=False), 154 + Token(type='text', tag='', nesting=0, attrs={}, map=None, level=0, children=None, 155 + content='a', markup='', info='', meta={}, block=False, hidden=False) 156 + ]), 157 + Token(type='paragraph_close', tag='p', nesting=-1, attrs={}, map=None, level=0, children=None, 158 + content='', markup='', info='', meta={}, block=True, hidden=False) 159 + ] 160 + assert c._parse("\\\\\\[a]{#bar}") == [ 161 + Token(type='paragraph_open', tag='p', nesting=1, attrs={}, map=[0, 1], level=0, children=None, 162 + content='', markup='', info='', meta={}, block=True, hidden=False), 163 + Token(type='inline', tag='', nesting=0, attrs={}, map=[0, 1], level=1, 164 + children=[ 165 + Token(type='text', tag='', nesting=0, attrs={}, map=None, level=0, children=None, 166 + content='\\[a]{#bar}', markup='', info='', meta={}, block=False, hidden=False) 167 + ], 168 + content='\\\\\\[a]{#bar}', markup='', info='', meta={}, block=True, hidden=False), 169 + Token(type='paragraph_close', tag='p', nesting=-1, attrs={}, map=None, level=0, children=None, 170 + content='', markup='', info='', meta={}, block=True, hidden=False) 171 + ] 172 + 173 + def test_inline_comment_basic() -> None: 174 + c = Converter({}) 175 + assert c._parse("a <!-- foo --><!----> b") == [ 176 + Token(type='paragraph_open', tag='p', nesting=1, attrs={}, map=[0, 1], level=0, children=None, 177 + content='', markup='', info='', meta={}, block=True, hidden=False), 178 + Token(type='inline', tag='', nesting=0, attrs={}, map=[0, 1], level=1, 179 + content='a <!-- foo --><!----> b', markup='', info='', meta={}, block=True, hidden=False, 180 + children=[ 181 + Token(type='text', tag='', nesting=0, attrs={}, map=None, level=0, children=None, 182 + content='a b', markup='', info='', meta={}, block=False, hidden=False) 183 + ]), 184 + Token(type='paragraph_close', tag='p', nesting=-1, attrs={}, map=None, level=0, children=None, 185 + content='', markup='', info='', meta={}, block=True, hidden=False) 186 + ] 187 + assert c._parse("a<!-- b -->") == [ 188 + Token(type='paragraph_open', tag='p', nesting=1, attrs={}, map=[0, 1], level=0, children=None, 189 + content='', markup='', info='', meta={}, block=True, hidden=False), 190 + Token(type='inline', tag='', nesting=0, attrs={}, map=[0, 1], level=1, 191 + content='a<!-- b -->', markup='', info='', meta={}, block=True, hidden=False, 192 + children=[ 193 + Token(type='text', tag='', nesting=0, attrs={}, map=None, level=0, children=None, 194 + content='a', markup='', info='', meta={}, block=False, hidden=False) 195 + ]), 196 + Token(type='paragraph_close', tag='p', nesting=-1, attrs={}, map=None, level=0, children=None, 197 + content='', markup='', info='', meta={}, block=True, hidden=False) 198 + ] 199 + 200 + def test_inline_comment_does_not_nest_in_code() -> None: 201 + c = Converter({}) 202 + assert c._parse("`a<!-- b -->c`") == [ 203 + Token(type='paragraph_open', tag='p', nesting=1, attrs={}, map=[0, 1], level=0, children=None, 204 + content='', markup='', info='', meta={}, block=True, hidden=False), 205 + Token(type='inline', tag='', nesting=0, attrs={}, map=[0, 1], level=1, 206 + content='`a<!-- b -->c`', markup='', info='', meta={}, block=True, hidden=False, 207 + children=[ 208 + Token(type='code_inline', tag='code', nesting=0, attrs={}, map=None, level=0, children=None, 209 + content='a<!-- b -->c', markup='`', info='', meta={}, block=False, hidden=False) 210 + ]), 211 + Token(type='paragraph_close', tag='p', nesting=-1, attrs={}, map=None, level=0, children=None, 212 + content='', markup='', info='', meta={}, block=True, hidden=False) 213 + ] 214 + 215 + def test_inline_comment_does_not_nest_elsewhere() -> None: 216 + c = Converter({}) 217 + assert c._parse("*a<!-- b -->c*") == [ 218 + Token(type='paragraph_open', tag='p', nesting=1, attrs={}, map=[0, 1], level=0, children=None, 219 + content='', markup='', info='', meta={}, block=True, hidden=False), 220 + Token(type='inline', tag='', nesting=0, attrs={}, map=[0, 1], level=1, 221 + content='*a<!-- b -->c*', markup='', info='', meta={}, block=True, hidden=False, 222 + children=[ 223 + Token(type='em_open', tag='em', nesting=1, attrs={}, map=None, level=0, children=None, 224 + content='', markup='*', info='', meta={}, block=False, hidden=False), 225 + Token(type='text', tag='', nesting=0, attrs={}, map=None, level=1, children=None, 226 + content='ac', markup='', info='', meta={}, block=False, hidden=False), 227 + Token(type='em_close', tag='em', nesting=-1, attrs={}, map=None, level=0, children=None, 228 + content='', markup='*', info='', meta={}, block=False, hidden=False) 229 + ]), 230 + Token(type='paragraph_close', tag='p', nesting=-1, attrs={}, map=None, level=0, children=None, 231 + content='', markup='', info='', meta={}, block=True, hidden=False) 232 + ] 233 + 234 + def test_inline_comment_can_be_escaped() -> None: 235 + c = Converter({}) 236 + assert c._parse("a\\<!-- b -->c") == [ 237 + Token(type='paragraph_open', tag='p', nesting=1, attrs={}, map=[0, 1], level=0, children=None, 238 + content='', markup='', info='', meta={}, block=True, hidden=False), 239 + Token(type='inline', tag='', nesting=0, attrs={}, map=[0, 1], level=1, 240 + content='a\\<!-- b -->c', markup='', info='', meta={}, block=True, hidden=False, 241 + children=[ 242 + Token(type='text', tag='', nesting=0, attrs={}, map=None, level=0, children=None, 243 + content='a<!-- b -->c', markup='', info='', meta={}, block=False, hidden=False) 244 + ]), 245 + Token(type='paragraph_close', tag='p', nesting=-1, attrs={}, map=None, level=0, children=None, 246 + content='', markup='', info='', meta={}, block=True, hidden=False) 247 + ] 248 + assert c._parse("a\\\\<!-- b -->c") == [ 249 + Token(type='paragraph_open', tag='p', nesting=1, attrs={}, map=[0, 1], level=0, children=None, 250 + content='', markup='', info='', meta={}, block=True, hidden=False), 251 + Token(type='inline', tag='', nesting=0, attrs={}, map=[0, 1], level=1, 252 + children=[ 253 + Token(type='text', tag='', nesting=0, attrs={}, map=None, level=0, children=None, 254 + content='a\\c', markup='', info='', meta={}, block=False, hidden=False) 255 + ], 256 + content='a\\\\<!-- b -->c', markup='', info='', meta={}, block=True, hidden=False), 257 + Token(type='paragraph_close', tag='p', nesting=-1, attrs={}, map=None, level=0, children=None, 258 + content='', markup='', info='', meta={}, block=True, hidden=False) 259 + ] 260 + assert c._parse("a\\\\\\<!-- b -->c") == [ 261 + Token(type='paragraph_open', tag='p', nesting=1, attrs={}, map=[0, 1], level=0, children=None, 262 + content='', markup='', info='', meta={}, block=True, hidden=False), 263 + Token(type='inline', tag='', nesting=0, attrs={}, map=[0, 1], level=1, 264 + children=[ 265 + Token(type='text', tag='', nesting=0, attrs={}, map=None, level=0, children=None, 266 + content='a\\<!-- b -->c', markup='', info='', meta={}, block=False, hidden=False) 267 + ], 268 + content='a\\\\\\<!-- b -->c', markup='', info='', meta={}, block=True, hidden=False), 269 + Token(type='paragraph_close', tag='p', nesting=-1, attrs={}, map=None, level=0, children=None, 270 + content='', markup='', info='', meta={}, block=True, hidden=False) 271 + ] 272 + 273 + def test_block_comment() -> None: 274 + c = Converter({}) 275 + assert c._parse("<!-- a -->") == [] 276 + assert c._parse("<!-- a\n-->") == [] 277 + assert c._parse("<!--\na\n-->") == [] 278 + assert c._parse("<!--\n\na\n\n-->") == [] 279 + assert c._parse("<!--\n\n```\n\n\n```\n\n-->") == []
+2
pkgs/top-level/all-packages.nix
··· 37661 37661 37662 37662 nixos-install-tools = callPackage ../tools/nix/nixos-install-tools { }; 37663 37663 37664 + nixos-render-docs = callPackage ../tools/nix/nixos-render-docs { }; 37665 + 37664 37666 nixdoc = callPackage ../tools/nix/nixdoc {}; 37665 37667 37666 37668 dnadd = callPackage ../tools/nix/dnadd { };