summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--maintainers/maintainer-list.nix6
-rw-r--r--nixos/doc/manual/from_md/release-notes/rl-2305.section.xml17
-rw-r--r--nixos/doc/manual/release-notes/rl-2305.section.md4
-rw-r--r--nixos/lib/make-options-doc/default.nix30
-rw-r--r--nixos/lib/make-options-doc/mergeJSON.py354
-rw-r--r--nixos/lib/make-options-doc/options-to-docbook.xsl43
-rw-r--r--nixos/modules/module-list.nix1
-rw-r--r--nixos/modules/services/misc/autosuspend.nix230
-rw-r--r--nixos/modules/services/monitoring/uptime-kuma.nix2
-rw-r--r--nixos/modules/services/networking/redsocks.nix3
-rw-r--r--nixos/modules/services/web-servers/nginx/default.nix49
-rw-r--r--nixos/modules/tasks/filesystems/envfs.nix2
-rw-r--r--nixos/tests/nginx.nix2
-rw-r--r--pkgs/applications/networking/cluster/argocd/default.nix4
-rw-r--r--pkgs/development/libraries/wlroots/default.nix4
-rw-r--r--pkgs/development/python-modules/markdown-it-py/default.nix4
-rw-r--r--pkgs/development/python-modules/mdit-py-plugins/default.nix5
-rw-r--r--pkgs/development/tools/misc/igprof/default.nix4
-rw-r--r--pkgs/development/tools/yarn2nix-moretea/yarn2nix/default.nix12
-rw-r--r--pkgs/development/tools/zsv/default.nix4
-rw-r--r--pkgs/os-specific/darwin/cctools/apple.nix118
-rw-r--r--pkgs/os-specific/darwin/cctools/cctools-add-missing-vtool-libstuff-dep.patch11
-rw-r--r--pkgs/tools/graphics/gifski/default.nix6
-rw-r--r--pkgs/tools/networking/socat/default.nix5
-rw-r--r--pkgs/top-level/darwin-packages.nix4
25 files changed, 650 insertions, 274 deletions
diff --git a/maintainers/maintainer-list.nix b/maintainers/maintainer-list.nix
index 6900d420a14..61096b5981c 100644
--- a/maintainers/maintainer-list.nix
+++ b/maintainers/maintainer-list.nix
@@ -15420,6 +15420,12 @@
     githubId = 17534323;
     name = "Quentin Vaucher";
   };
+  xlambein = {
+    email = "xlambein@gmail.com";
+    github = "xlambein";
+    githubId = 5629059;
+    name = "Xavier Lambein";
+  };
   xnaveira = {
     email = "xnaveira@gmail.com";
     github = "xnaveira";
diff --git a/nixos/doc/manual/from_md/release-notes/rl-2305.section.xml b/nixos/doc/manual/from_md/release-notes/rl-2305.section.xml
index 17769ff0b45..a672deb20b6 100644
--- a/nixos/doc/manual/from_md/release-notes/rl-2305.section.xml
+++ b/nixos/doc/manual/from_md/release-notes/rl-2305.section.xml
@@ -130,6 +130,13 @@
           <link xlink:href="options.html#opt-services.photoprism.enable">services.photoprism</link>.
         </para>
       </listitem>
+      <listitem>
+        <para>
+          <link xlink:href="https://github.com/languitar/autosuspend">autosuspend</link>,
+          a python daemon that suspends a system if certain conditions
+          are met, or not met.
+        </para>
+      </listitem>
     </itemizedlist>
   </section>
   <section xml:id="sec-release-23.05-incompatibilities">
@@ -282,16 +289,6 @@
       </listitem>
       <listitem>
         <para>
-          The Nginx module now validates the syntax of config files at
-          build time. For more complex configurations (using
-          <literal>include</literal> with out-of-store files notably)
-          you may need to disable this check by setting
-          <link linkend="opt-services.nginx.validateConfig">services.nginx.validateConfig</link>
-          to <literal>false</literal>.
-        </para>
-      </listitem>
-      <listitem>
-        <para>
           The EC2 image module previously detected and automatically
           mounted ext3-formatted instance store devices and partitions
           in stage-1 (initramfs), storing <literal>/tmp</literal> on the
diff --git a/nixos/doc/manual/release-notes/rl-2305.section.md b/nixos/doc/manual/release-notes/rl-2305.section.md
index d4b31a9df1b..c0518d4cd6f 100644
--- a/nixos/doc/manual/release-notes/rl-2305.section.md
+++ b/nixos/doc/manual/release-notes/rl-2305.section.md
@@ -42,6 +42,8 @@ In addition to numerous new and upgraded packages, this release has the followin
 
 - [photoprism](https://photoprism.app/), a AI-Powered Photos App for the Decentralized Web. Available as [services.photoprism](options.html#opt-services.photoprism.enable).
 
+- [autosuspend](https://github.com/languitar/autosuspend), a python daemon that suspends a system if certain conditions are met, or not met.
+
 ## Backward Incompatibilities {#sec-release-23.05-incompatibilities}
 
 <!-- To avoid merge conflicts, consider adding your item at an arbitrary place in the list instead. -->
@@ -71,8 +73,6 @@ In addition to numerous new and upgraded packages, this release has the followin
 
 - `llvmPackages_rocm.llvm` will not contain `clang` or `compiler-rt`. `llvmPackages_rocm.clang` will not contain `llvm`. `llvmPackages_rocm.clangNoCompilerRt` has been removed in favor of using `llvmPackages_rocm.clang-unwrapped`.
 
-- The Nginx module now validates the syntax of config files at build time. For more complex configurations (using `include` with out-of-store files notably) you may need to disable this check by setting [services.nginx.validateConfig](#opt-services.nginx.validateConfig) to `false`.
-
 - The EC2 image module previously detected and automatically mounted ext3-formatted instance store devices and partitions in stage-1 (initramfs), storing `/tmp` on the first discovered device. This behaviour, which only catered to very specific use cases and could not be disabled, has been removed. Users relying on this should provide their own implementation, and probably use ext4 and perform the mount in stage-2.
 
 - `teleport` has been upgraded to major version 11. Please see upstream [upgrade instructions](https://goteleport.com/docs/setup/operations/upgrading/) and [release notes](https://goteleport.com/docs/changelog/#1100).
diff --git a/nixos/lib/make-options-doc/default.nix b/nixos/lib/make-options-doc/default.nix
index 7595b66771a..335217703c8 100644
--- a/nixos/lib/make-options-doc/default.nix
+++ b/nixos/lib/make-options-doc/default.nix
@@ -78,16 +78,13 @@ let
           title = args.title or null;
           name = args.name or (lib.concatStringsSep "." args.path);
         in ''
-          <listitem>
-            <para>
-              <link xlink:href="https://search.nixos.org/packages?show=${name}&amp;sort=relevance&amp;query=${name}">
-                <literal>${lib.optionalString (title != null) "${title} aka "}pkgs.${name}</literal>
-              </link>
-            </para>
-            ${lib.optionalString (args ? comment) "<para>${args.comment}</para>"}
-          </listitem>
+          - [`${lib.optionalString (title != null) "${title} aka "}pkgs.${name}`](
+              https://search.nixos.org/packages?show=${name}&sort=relevance&query=${name}
+            )${
+              lib.optionalString (args ? comment) "\n\n  ${args.comment}"
+            }
         '';
-    in "<itemizedlist>${lib.concatStringsSep "\n" (map (p: describe (unpack p)) packages)}</itemizedlist>";
+    in lib.concatMapStrings (p: describe (unpack p)) packages;
 
   optionsNix = builtins.listToAttrs (map (o: { name = o.name; value = removeAttrs o ["name" "visible" "internal"]; }) optionsList);
 
@@ -121,7 +118,20 @@ in rec {
             inherit self;
             includeSiteCustomize = true;
            });
-         in self.withPackages (p: [ p.mistune ]))
+         in self.withPackages (p:
+           let
+            # TODO add our own small test suite when rendering is split out into a new tool
+            markdown-it-py = p.markdown-it-py.override {
+              disableTests = true;
+            };
+            mdit-py-plugins = p.mdit-py-plugins.override {
+              inherit markdown-it-py;
+              disableTests = true;
+            };
+          in [
+            markdown-it-py
+            mdit-py-plugins
+          ]))
       ];
       options = builtins.toFile "options.json"
         (builtins.unsafeDiscardStringContext (builtins.toJSON optionsNix));
diff --git a/nixos/lib/make-options-doc/mergeJSON.py b/nixos/lib/make-options-doc/mergeJSON.py
index 3108b9e2197..2de0bbae1d9 100644
--- a/nixos/lib/make-options-doc/mergeJSON.py
+++ b/nixos/lib/make-options-doc/mergeJSON.py
@@ -3,9 +3,17 @@ import json
 import os
 import sys
 from typing import Any, Dict, List
+from collections.abc import MutableMapping, Sequence
+import inspect
 
 # for MD conversion
-import mistune
+import markdown_it
+import markdown_it.renderer
+from markdown_it.token import Token
+from markdown_it.utils import OptionsDict
+from mdit_py_plugins.container import container_plugin
+from mdit_py_plugins.deflist import deflist_plugin
+from mdit_py_plugins.myst_role import myst_role_plugin
 import re
 from xml.sax.saxutils import escape, quoteattr
 
@@ -49,155 +57,175 @@ def unpivot(options: Dict[Key, Option]) -> Dict[str, JSON]:
 
 manpage_urls = json.load(open(os.getenv('MANPAGE_URLS')))
 
-admonitions = {
-    '.warning': 'warning',
-    '.important': 'important',
-    '.note': 'note'
-}
-class Renderer(mistune.renderers.BaseRenderer):
-    def _get_method(self, name):
-        try:
-            return super(Renderer, self)._get_method(name)
-        except AttributeError:
-            def not_supported(*args, **kwargs):
-                raise NotImplementedError("md node not supported yet", name, args, **kwargs)
-            return not_supported
-
-    def text(self, text):
-        return escape(text)
-    def paragraph(self, text):
-        return text + "\n\n"
-    def newline(self):
-        return "<literallayout>\n</literallayout>"
-    def codespan(self, text):
-        return f"<literal>{escape(text)}</literal>"
-    def block_code(self, text, info=None):
-        info = f" language={quoteattr(info)}" if info is not None else ""
-        return f"<programlisting{info}>\n{escape(text)}</programlisting>"
-    def link(self, link, text=None, title=None):
-        tag = "link"
-        if link[0:1] == '#':
-            if text == "":
-                tag = "xref"
-            attr = "linkend"
-            link = quoteattr(link[1:])
-        else:
-            # try to faithfully reproduce links that were of the form <link href="..."/>
-            # in docbook format
-            if text == link:
-                text = ""
-            attr = "xlink:href"
-            link = quoteattr(link)
-        return f"<{tag} {attr}={link}>{text}</{tag}>"
-    def list(self, text, ordered, level, start=None):
-        if ordered:
-            raise NotImplementedError("ordered lists not supported yet")
-        return f"<itemizedlist>\n{text}\n</itemizedlist>"
-    def list_item(self, text, level):
-        return f"<listitem><para>{text}</para></listitem>\n"
-    def block_text(self, text):
-        return text
-    def emphasis(self, text):
-        return f"<emphasis>{text}</emphasis>"
-    def strong(self, text):
-        return f"<emphasis role=\"strong\">{text}</emphasis>"
-    def admonition(self, text, kind):
-        if kind not in admonitions:
-            raise NotImplementedError(f"admonition {kind} not supported yet")
-        tag = admonitions[kind]
-        # we don't keep whitespace here because usually we'll contain only
-        # a single paragraph and the original docbook string is no longer
-        # available to restore the trailer.
-        return f"<{tag}><para>{text.rstrip()}</para></{tag}>"
-    def block_quote(self, text):
-        return f"<blockquote><para>{text}</para></blockquote>"
-    def command(self, text):
-        return f"<command>{escape(text)}</command>"
-    def option(self, text):
-        return f"<option>{escape(text)}</option>"
-    def file(self, text):
-        return f"<filename>{escape(text)}</filename>"
-    def var(self, text):
-        return f"<varname>{escape(text)}</varname>"
-    def env(self, text):
-        return f"<envar>{escape(text)}</envar>"
-    def manpage(self, page, section):
-        man = f"{page}({section})"
-        title = f"<refentrytitle>{escape(page)}</refentrytitle>"
-        vol = f"<manvolnum>{escape(section)}</manvolnum>"
-        ref = f"<citerefentry>{title}{vol}</citerefentry>"
-        if man in manpage_urls:
-            return self.link(manpage_urls[man], text=ref)
-        else:
-            return ref
-
-    def finalize(self, data):
-        return "".join(data)
-
-def p_command(md):
-    COMMAND_PATTERN = r'\{command\}`(.*?)`'
-    def parse(self, m, state):
-        return ('command', m.group(1))
-    md.inline.register_rule('command', COMMAND_PATTERN, parse)
-    md.inline.rules.append('command')
-
-def p_file(md):
-    FILE_PATTERN = r'\{file\}`(.*?)`'
-    def parse(self, m, state):
-        return ('file', m.group(1))
-    md.inline.register_rule('file', FILE_PATTERN, parse)
-    md.inline.rules.append('file')
-
-def p_var(md):
-    VAR_PATTERN = r'\{var\}`(.*?)`'
-    def parse(self, m, state):
-        return ('var', m.group(1))
-    md.inline.register_rule('var', VAR_PATTERN, parse)
-    md.inline.rules.append('var')
-
-def p_env(md):
-    ENV_PATTERN = r'\{env\}`(.*?)`'
-    def parse(self, m, state):
-        return ('env', m.group(1))
-    md.inline.register_rule('env', ENV_PATTERN, parse)
-    md.inline.rules.append('env')
-
-def p_option(md):
-    OPTION_PATTERN = r'\{option\}`(.*?)`'
-    def parse(self, m, state):
-        return ('option', m.group(1))
-    md.inline.register_rule('option', OPTION_PATTERN, parse)
-    md.inline.rules.append('option')
-
-def p_manpage(md):
-    MANPAGE_PATTERN = r'\{manpage\}`(.*?)\((.+?)\)`'
-    def parse(self, m, state):
-        return ('manpage', m.group(1), m.group(2))
-    md.inline.register_rule('manpage', MANPAGE_PATTERN, parse)
-    md.inline.rules.append('manpage')
-
-def p_admonition(md):
-    ADMONITION_PATTERN = re.compile(r'^::: \{([^\n]*?)\}\n(.*?)^:::$\n*', flags=re.MULTILINE|re.DOTALL)
-    def parse(self, m, state):
-        return {
-            'type': 'admonition',
-            'children': self.parse(m.group(2), state),
-            'params': [ m.group(1) ],
+class Renderer(markdown_it.renderer.RendererProtocol):
+    __output__ = "docbook"
+    def __init__(self, parser=None):
+        self.rules = {
+            k: v
+            for k, v in inspect.getmembers(self, predicate=inspect.ismethod)
+            if not (k.startswith("render") or k.startswith("_"))
+        } | {
+            "container_{.note}_open": self._note_open,
+            "container_{.note}_close": self._note_close,
+            "container_{.important}_open": self._important_open,
+            "container_{.important}_close": self._important_close,
+            "container_{.warning}_open": self._warning_open,
+            "container_{.warning}_close": self._warning_close,
         }
-    md.block.register_rule('admonition', ADMONITION_PATTERN, parse)
-    md.block.rules.append('admonition')
-
-md = mistune.create_markdown(renderer=Renderer(), plugins=[
-    p_command, p_file, p_var, p_env, p_option, p_manpage, p_admonition
-])
+    def render(self, tokens: Sequence[Token], options: OptionsDict, env: MutableMapping) -> str:
+        assert '-link-tag-stack' not in env
+        env['-link-tag-stack'] = []
+        assert '-deflist-stack' not in env
+        env['-deflist-stack'] = []
+        def do_one(i, token):
+            if token.type == "inline":
+                assert token.children is not None
+                return self.renderInline(token.children, options, env)
+            elif token.type in self.rules:
+                return self.rules[token.type](tokens[i], tokens, i, options, env)
+            else:
+                raise NotImplementedError("md token not supported yet", token)
+        return "".join(map(lambda arg: do_one(*arg), enumerate(tokens)))
+    def renderInline(self, tokens: Sequence[Token], options: OptionsDict, env: MutableMapping) -> str:
+        # HACK to support docbook links and xrefs. link handling is only necessary because the docbook
+        # manpage stylesheet converts - in urls to a mathematical minus, which may be somewhat incorrect.
+        for i, token in enumerate(tokens):
+            if token.type != 'link_open':
+                continue
+            token.tag = 'link'
+            # turn [](#foo) into xrefs
+            if token.attrs['href'][0:1] == '#' and tokens[i + 1].type == 'link_close':
+                token.tag = "xref"
+            # turn <x> into links without contents
+            if tokens[i + 1].type == 'text' and tokens[i + 1].content == token.attrs['href']:
+                tokens[i + 1].content = ''
+
+        def do_one(i, token):
+            if token.type in self.rules:
+                return self.rules[token.type](tokens[i], tokens, i, options, env)
+            else:
+                raise NotImplementedError("md node not supported yet", token)
+        return "".join(map(lambda arg: do_one(*arg), enumerate(tokens)))
+
+    def text(self, token, tokens, i, options, env):
+        return escape(token.content)
+    def paragraph_open(self, token, tokens, i, options, env):
+        return "<para>"
+    def paragraph_close(self, token, tokens, i, options, env):
+        return "</para>"
+    def hardbreak(self, token, tokens, i, options, env):
+        return "<literallayout>\n</literallayout>"
+    def softbreak(self, token, tokens, i, options, env):
+        # should check options.breaks() and emit hard break if so
+        return "\n"
+    def code_inline(self, token, tokens, i, options, env):
+        return f"<literal>{escape(token.content)}</literal>"
+    def code_block(self, token, tokens, i, options, env):
+        return f"<programlisting>{escape(token.content)}</programlisting>"
+    def link_open(self, token, tokens, i, options, env):
+        env['-link-tag-stack'].append(token.tag)
+        (attr, start) = ('linkend', 1) if token.attrs['href'][0] == '#' else ('xlink:href', 0)
+        return f"<{token.tag} {attr}={quoteattr(token.attrs['href'][start:])}>"
+    def link_close(self, token, tokens, i, options, env):
+        return f"</{env['-link-tag-stack'].pop()}>"
+    def list_item_open(self, token, tokens, i, options, env):
+        return "<listitem>"
+    def list_item_close(self, token, tokens, i, options, env):
+        return "</listitem>\n"
+    # HACK open and close para for docbook change size. remove soon.
+    def bullet_list_open(self, token, tokens, i, options, env):
+        return "<para><itemizedlist>\n"
+    def bullet_list_close(self, token, tokens, i, options, env):
+        return "\n</itemizedlist></para>"
+    def em_open(self, token, tokens, i, options, env):
+        return "<emphasis>"
+    def em_close(self, token, tokens, i, options, env):
+        return "</emphasis>"
+    def strong_open(self, token, tokens, i, options, env):
+        return "<emphasis role=\"strong\">"
+    def strong_close(self, token, tokens, i, options, env):
+        return "</emphasis>"
+    def fence(self, token, tokens, i, options, env):
+        info = f" language={quoteattr(token.info)}" if token.info != "" else ""
+        return f"<programlisting{info}>{escape(token.content)}</programlisting>"
+    def blockquote_open(self, token, tokens, i, options, env):
+        return "<para><blockquote>"
+    def blockquote_close(self, token, tokens, i, options, env):
+        return "</blockquote></para>"
+    def _note_open(self, token, tokens, i, options, env):
+        return "<para><note>"
+    def _note_close(self, token, tokens, i, options, env):
+        return "</note></para>"
+    def _important_open(self, token, tokens, i, options, env):
+        return "<para><important>"
+    def _important_close(self, token, tokens, i, options, env):
+        return "</important></para>"
+    def _warning_open(self, token, tokens, i, options, env):
+        return "<para><warning>"
+    def _warning_close(self, token, tokens, i, options, env):
+        return "</warning></para>"
+    # markdown-it emits tokens based on the html syntax tree, but docbook is
+    # slightly different. html has <dl>{<dt/>{<dd/>}}</dl>,
+    # docbook has <variablelist>{<varlistentry><term/><listitem/></varlistentry>}<variablelist>
+    # we have to reject multiple definitions for the same term for time being.
+    def dl_open(self, token, tokens, i, options, env):
+        env['-deflist-stack'].append({})
+        return "<para><variablelist>"
+    def dl_close(self, token, tokens, i, options, env):
+        env['-deflist-stack'].pop()
+        return "</variablelist></para>"
+    def dt_open(self, token, tokens, i, options, env):
+        env['-deflist-stack'][-1]['has-dd'] = False
+        return "<varlistentry><term>"
+    def dt_close(self, token, tokens, i, options, env):
+        return "</term>"
+    def dd_open(self, token, tokens, i, options, env):
+        if env['-deflist-stack'][-1]['has-dd']:
+            raise Exception("multiple definitions per term not supported")
+        env['-deflist-stack'][-1]['has-dd'] = True
+        return "<listitem>"
+    def dd_close(self, token, tokens, i, options, env):
+        return "</listitem></varlistentry>"
+    def myst_role(self, token, tokens, i, options, env):
+        if token.meta['name'] == 'command':
+            return f"<command>{escape(token.content)}</command>"
+        if token.meta['name'] == 'file':
+            return f"<filename>{escape(token.content)}</filename>"
+        if token.meta['name'] == 'var':
+            return f"<varname>{escape(token.content)}</varname>"
+        if token.meta['name'] == 'env':
+            return f"<envar>{escape(token.content)}</envar>"
+        if token.meta['name'] == 'option':
+            return f"<option>{escape(token.content)}</option>"
+        if token.meta['name'] == 'manpage':
+            [page, section] = [ s.strip() for s in token.content.rsplit('(', 1) ]
+            section = section[:-1]
+            man = f"{page}({section})"
+            title = f"<refentrytitle>{escape(page)}</refentrytitle>"
+            vol = f"<manvolnum>{escape(section)}</manvolnum>"
+            ref = f"<citerefentry>{title}{vol}</citerefentry>"
+            if man in manpage_urls:
+                return f"<link xlink:href={quoteattr(manpage_urls[man])}>{ref}</link>"
+            else:
+                return ref
+        raise NotImplementedError("md node not supported yet", token)
+
+md = (
+    markdown_it.MarkdownIt(renderer_cls=Renderer)
+    # TODO maybe fork the plugin and have only a single rule for all?
+    .use(container_plugin, name="{.note}")
+    .use(container_plugin, name="{.important}")
+    .use(container_plugin, name="{.warning}")
+    .use(deflist_plugin)
+    .use(myst_role_plugin)
+)
 
 # converts in-place!
 def convertMD(options: Dict[str, Any]) -> str:
     def convertString(path: str, text: str) -> str:
         try:
-            rendered = md(text)
-            # keep trailing spaces so we can diff the generated XML to check for conversion bugs.
-            return rendered.rstrip() + text[len(text.rstrip()):]
+            rendered = md.render(text)
+            return rendered
         except:
             print(f"error in {path}")
             raise
@@ -208,19 +236,45 @@ def convertMD(options: Dict[str, Any]) -> str:
         if '_type' not in option[key]: return False
         return option[key]['_type'] == typ
 
+    def convertCode(name: str, option: Dict[str, Any], key: str):
+        rendered = f"{key}-db"
+        if optionIs(option, key, 'literalMD'):
+            option[rendered] = convertString(name, f"*{key.capitalize()}:*\n{option[key]['text']}")
+        elif optionIs(option, key, 'literalExpression'):
+            code = option[key]['text']
+            # for multi-line code blocks we only have to count ` runs at the beginning
+            # of a line, but this is much easier.
+            multiline = '\n' in code
+            longest, current = (0, 0)
+            for c in code:
+                current = current + 1 if c == '`' else 0
+                longest = max(current, longest)
+            # inline literals need a space to separate ticks from content, code blocks
+            # need newlines. inline literals need one extra tick, code blocks need three.
+            ticks, sep = ('`' * (longest + (3 if multiline else 1)), '\n' if multiline else ' ')
+            code = f"{ticks}{sep}{code}{sep}{ticks}"
+            option[rendered] = convertString(name, f"*{key.capitalize()}:*\n{code}")
+        elif optionIs(option, key, 'literalDocBook'):
+            option[rendered] = f"<para><emphasis>{key.capitalize()}:</emphasis> {option[key]['text']}</para>"
+        elif key in option:
+            raise Exception(f"{name} {key} has unrecognized type", option[key])
+
     for (name, option) in options.items():
         try:
             if optionIs(option, 'description', 'mdDoc'):
                 option['description'] = convertString(name, option['description']['text'])
             elif markdownByDefault:
                 option['description'] = convertString(name, option['description'])
+            else:
+                option['description'] = ("<nixos:option-description><para>" +
+                                         option['description'] +
+                                         "</para></nixos:option-description>")
+
+            convertCode(name, option, 'example')
+            convertCode(name, option, 'default')
 
-            if optionIs(option, 'example', 'literalMD'):
-                docbook = convertString(name, option['example']['text'])
-                option['example'] = { '_type': 'literalDocBook', 'text': docbook }
-            if optionIs(option, 'default', 'literalMD'):
-                docbook = convertString(name, option['default']['text'])
-                option['default'] = { '_type': 'literalDocBook', 'text': docbook }
+            if 'relatedPackages' in option:
+                option['relatedPackages'] = convertString(name, option['relatedPackages'])
         except Exception as e:
             raise Exception(f"Failed to render option {name}: {str(e)}")
 
diff --git a/nixos/lib/make-options-doc/options-to-docbook.xsl b/nixos/lib/make-options-doc/options-to-docbook.xsl
index ac49659c681..a2e88febdaf 100644
--- a/nixos/lib/make-options-doc/options-to-docbook.xsl
+++ b/nixos/lib/make-options-doc/options-to-docbook.xsl
@@ -53,12 +53,8 @@
 
             <listitem>
 
-              <nixos:option-description>
-                <para>
-                  <xsl:value-of disable-output-escaping="yes"
-                                select="attr[@name = 'description']/string/@value" />
-                </para>
-              </nixos:option-description>
+              <xsl:value-of disable-output-escaping="yes"
+                            select="attr[@name = 'description']/string/@value" />
 
               <xsl:if test="attr[@name = 'type']">
                 <para>
@@ -72,29 +68,22 @@
                 </para>
               </xsl:if>
 
-              <xsl:if test="attr[@name = 'default']">
-                <para>
-                  <emphasis>Default:</emphasis>
-                  <xsl:text> </xsl:text>
-                  <xsl:apply-templates select="attr[@name = 'default']/*" mode="top" />
-                </para>
+              <xsl:if test="attr[@name = 'default-db']">
+                <xsl:value-of disable-output-escaping="yes"
+                              select="attr[@name = 'default-db']/string/@value" />
               </xsl:if>
 
-              <xsl:if test="attr[@name = 'example']">
-                <para>
-                  <emphasis>Example:</emphasis>
-                  <xsl:text> </xsl:text>
-                  <xsl:apply-templates select="attr[@name = 'example']/*" mode="top" />
-                </para>
+              <xsl:if test="attr[@name = 'example-db']">
+                <xsl:value-of disable-output-escaping="yes"
+                              select="attr[@name = 'example-db']/string/@value" />
               </xsl:if>
 
               <xsl:if test="attr[@name = 'relatedPackages']">
                 <para>
                   <emphasis>Related packages:</emphasis>
-                  <xsl:text> </xsl:text>
-                  <xsl:value-of disable-output-escaping="yes"
-                                select="attr[@name = 'relatedPackages']/string/@value" />
                 </para>
+                <xsl:value-of disable-output-escaping="yes"
+                              select="attr[@name = 'relatedPackages']/string/@value" />
               </xsl:if>
 
               <xsl:if test="count(attr[@name = 'declarations']/list/*) != 0">
@@ -121,18 +110,6 @@
   </xsl:template>
 
 
-  <xsl:template match="attrs[attr[@name = '_type' and string[@value = 'literalExpression']]]" mode = "top">
-    <xsl:choose>
-      <xsl:when test="contains(attr[@name = 'text']/string/@value, '&#010;')">
-        <programlisting><xsl:value-of select="attr[@name = 'text']/string/@value" /></programlisting>
-      </xsl:when>
-      <xsl:otherwise>
-        <literal><xsl:value-of select="attr[@name = 'text']/string/@value" /></literal>
-      </xsl:otherwise>
-    </xsl:choose>
-  </xsl:template>
-
-
   <xsl:template match="attrs[attr[@name = '_type' and string[@value = 'literalDocBook']]]" mode = "top">
     <xsl:value-of disable-output-escaping="yes" select="attr[@name = 'text']/string/@value" />
   </xsl:template>
diff --git a/nixos/modules/module-list.nix b/nixos/modules/module-list.nix
index 45a7acdedc4..e1c174a00f9 100644
--- a/nixos/modules/module-list.nix
+++ b/nixos/modules/module-list.nix
@@ -571,6 +571,7 @@
   ./services/misc/atuin.nix
   ./services/misc/autofs.nix
   ./services/misc/autorandr.nix
+  ./services/misc/autosuspend.nix
   ./services/misc/bazarr.nix
   ./services/misc/beanstalkd.nix
   ./services/misc/bees.nix
diff --git a/nixos/modules/services/misc/autosuspend.nix b/nixos/modules/services/misc/autosuspend.nix
new file mode 100644
index 00000000000..b3e362533a0
--- /dev/null
+++ b/nixos/modules/services/misc/autosuspend.nix
@@ -0,0 +1,230 @@
+{ config, pkgs, lib, ... }:
+let
+  inherit (lib) mapAttrs' nameValuePair filterAttrs types mkEnableOption
+    mdDoc mkPackageOptionMD mkOption literalExpression mkIf flatten
+    maintainers attrValues;
+
+  cfg = config.services.autosuspend;
+
+  settingsFormat = pkgs.formats.ini { };
+
+  checks =
+    mapAttrs'
+      (n: v: nameValuePair "check.${n}" (filterAttrs (_: v: v != null) v))
+      cfg.checks;
+  wakeups =
+    mapAttrs'
+      (n: v: nameValuePair "wakeup.${n}" (filterAttrs (_: v: v != null) v))
+      cfg.wakeups;
+
+  # Whether the given check is enabled
+  hasCheck = class:
+    (filterAttrs
+      (n: v: v.enabled && (if v.class == null then n else v.class) == class)
+      cfg.checks)
+    != { };
+
+  # Dependencies needed by specific checks
+  dependenciesForChecks = {
+    "Smb" = pkgs.samba;
+    "XIdleTime" = [ pkgs.xprintidle pkgs.sudo ];
+  };
+
+  autosuspend-conf =
+    settingsFormat.generate "autosuspend.conf" ({ general = cfg.settings; } // checks // wakeups);
+
+  autosuspend = cfg.package;
+
+  checkType = types.submodule {
+    freeformType = settingsFormat.type.nestedTypes.elemType;
+
+    options.enabled = mkEnableOption (mdDoc "this activity check") // { default = true; };
+
+    options.class = mkOption {
+      default = null;
+      type = with types; nullOr (enum [
+        "ActiveCalendarEvent"
+        "ActiveConnection"
+        "ExternalCommand"
+        "JsonPath"
+        "Kodi"
+        "KodiIdleTime"
+        "LastLogActivity"
+        "Load"
+        "LogindSessionsIdle"
+        "Mpd"
+        "NetworkBandwidth"
+        "Ping"
+        "Processes"
+        "Smb"
+        "Users"
+        "XIdleTime"
+        "XPath"
+      ]);
+      description = mdDoc ''
+        Name of the class implementing the check.  If this option is not specified, the check's
+        name must represent a valid internal check class.
+      '';
+    };
+  };
+
+  wakeupType = types.submodule {
+    freeformType = settingsFormat.type.nestedTypes.elemType;
+
+    options.enabled = mkEnableOption (mdDoc "this wake-up check") // { default = true; };
+
+    options.class = mkOption {
+      default = null;
+      type = with types; nullOr (enum [
+        "Calendar"
+        "Command"
+        "File"
+        "Periodic"
+        "SystemdTimer"
+        "XPath"
+        "XPathDelta"
+      ]);
+      description = mdDoc ''
+        Name of the class implementing the check.  If this option is not specified, the check's
+        name must represent a valid internal check class.
+      '';
+    };
+  };
+in
+{
+  options = {
+    services.autosuspend = {
+      enable = mkEnableOption (mdDoc "the autosuspend daemon");
+
+      package = mkPackageOptionMD pkgs "autosuspend" { };
+
+      settings = mkOption {
+        type = types.submodule {
+          freeformType = settingsFormat.type.nestedTypes.elemType;
+
+          options = {
+            # Provide reasonable defaults for these two (required) options
+            suspend_cmd = mkOption {
+              default = "systemctl suspend";
+              type = with types; str;
+              description = mdDoc ''
+                The command to execute in case the host shall be suspended. This line can contain
+                additional command line arguments to the command to execute.
+              '';
+            };
+            wakeup_cmd = mkOption {
+              default = ''sh -c 'echo 0 > /sys/class/rtc/rtc0/wakealarm && echo {timestamp:.0f} > /sys/class/rtc/rtc0/wakealarm' '';
+              type = with types; str;
+              description = mdDoc ''
+                The command to execute for scheduling a wake up of the system. The given string is
+                processed using Python’s `str.format()` and a format argument called `timestamp`
+                encodes the UTC timestamp of the planned wake up time (float). Additionally `iso`
+                can be used to acquire the timestamp in ISO 8601 format.
+              '';
+            };
+          };
+        };
+        default = { };
+        example = literalExpression ''
+          {
+            enable = true;
+            interval = 30;
+            idle_time = 120;
+          }
+        '';
+        description = mdDoc ''
+          Configuration for autosuspend, see
+          <https://autosuspend.readthedocs.io/en/latest/configuration_file.html#general-configuration>
+          for supported values.
+        '';
+      };
+
+      checks = mkOption {
+        default = { };
+        type = with types; attrsOf checkType;
+        description = mdDoc ''
+          Checks for activity.  For more information, see:
+           - <https://autosuspend.readthedocs.io/en/latest/configuration_file.html#activity-check-configuration>
+           - <https://autosuspend.readthedocs.io/en/latest/available_checks.html>
+        '';
+        example = literalExpression ''
+          {
+            # Basic activity check configuration.
+            # The check class name is derived from the section header (Ping in this case).
+            # Remember to enable desired checks. They are disabled by default.
+            Ping = {
+              hosts = "192.168.0.7";
+            };
+
+            # This check is disabled.
+            Smb.enabled = false;
+
+            # Example for a custom check name.
+            # This will use the Users check with the custom name RemoteUsers.
+            # Custom names are necessary in case a check class is used multiple times.
+            # Custom names can also be used for clarification.
+            RemoteUsers = {
+              class = "Users";
+              name = ".*";
+              terminal = ".*";
+              host = "[0-9].*";
+            };
+
+            # Here the Users activity check is used again with different settings and a different name
+            LocalUsers = {
+              class = "Users";
+              name = ".*";
+              terminal = ".*";
+              host = "localhost";
+            };
+          }
+        '';
+      };
+
+      wakeups = mkOption {
+        default = { };
+        type = with types; attrsOf wakeupType;
+        description = mdDoc ''
+          Checks for wake up.  For more information, see:
+           - <https://autosuspend.readthedocs.io/en/latest/configuration_file.html#wake-up-check-configuration>
+           - <https://autosuspend.readthedocs.io/en/latest/available_wakeups.html>
+        '';
+        example = literalExpression ''
+          {
+            # Wake up checks reuse the same configuration mechanism as activity checks.
+            Calendar = {
+              url = "http://example.org/test.ics";
+            };
+          }
+        '';
+      };
+    };
+  };
+
+  config = mkIf cfg.enable {
+    systemd.services.autosuspend = {
+      description = "A daemon to suspend your server in case of inactivity";
+      documentation = [ "https://autosuspend.readthedocs.io/en/latest/systemd_integration.html" ];
+      wantedBy = [ "multi-user.target" ];
+      after = [ "network.target" ];
+      path = flatten (attrValues (filterAttrs (n: _: hasCheck n) dependenciesForChecks));
+      serviceConfig = {
+        ExecStart = ''${autosuspend}/bin/autosuspend -l ${autosuspend}/etc/autosuspend-logging.conf -c ${autosuspend-conf} daemon'';
+      };
+    };
+
+    systemd.services.autosuspend-detect-suspend = {
+      description = "Notifies autosuspend about suspension";
+      documentation = [ "https://autosuspend.readthedocs.io/en/latest/systemd_integration.html" ];
+      wantedBy = [ "sleep.target" ];
+      after = [ "sleep.target" ];
+      serviceConfig = {
+        ExecStart = ''${autosuspend}/bin/autosuspend -l ${autosuspend}/etc/autosuspend-logging.conf -c ${autosuspend-conf} presuspend'';
+      };
+    };
+  };
+
+  meta = {
+    maintainers = with maintainers; [ xlambein ];
+  };
+}
diff --git a/nixos/modules/services/monitoring/uptime-kuma.nix b/nixos/modules/services/monitoring/uptime-kuma.nix
index 3dfbfe3652c..455721b3a0e 100644
--- a/nixos/modules/services/monitoring/uptime-kuma.nix
+++ b/nixos/modules/services/monitoring/uptime-kuma.nix
@@ -7,6 +7,8 @@ let
 in
 {
 
+  meta.maintainers = [ lib.maintainers.julienmalka ];
+
   options = {
     services.uptime-kuma = {
       enable = mkEnableOption (mdDoc "Uptime Kuma, this assumes a reverse proxy to be set.");
diff --git a/nixos/modules/services/networking/redsocks.nix b/nixos/modules/services/networking/redsocks.nix
index 45feb1313c9..30d6a0a6336 100644
--- a/nixos/modules/services/networking/redsocks.nix
+++ b/nixos/modules/services/networking/redsocks.nix
@@ -37,7 +37,7 @@ in
               - stderr
               - file:/path/to/file
               - syslog:FACILITY where FACILITY is any of "daemon", "local0",
-              etc.
+                etc.
           '';
       };
 
@@ -125,6 +125,7 @@ in
               lib.mdDoc ''
                 Way to disclose client IP to the proxy.
                   - "false": do not disclose
+
                 http-connect supports the following ways:
                   - "X-Forwarded-For": add header "X-Forwarded-For: IP"
                   - "Forwarded_ip": add header "Forwarded: for=IP" (see RFC7239)
diff --git a/nixos/modules/services/web-servers/nginx/default.nix b/nixos/modules/services/web-servers/nginx/default.nix
index 6fafae8928a..c723b962c84 100644
--- a/nixos/modules/services/web-servers/nginx/default.nix
+++ b/nixos/modules/services/web-servers/nginx/default.nix
@@ -288,7 +288,7 @@ let
 
   configPath = if cfg.enableReload
     then "/etc/nginx/nginx.conf"
-    else finalConfigFile;
+    else configFile;
 
   execCommand = "${cfg.package}/bin/nginx -c '${configPath}'";
 
@@ -440,38 +440,6 @@ let
   );
 
   mkCertOwnershipAssertion = import ../../../security/acme/mk-cert-ownership-assertion.nix;
-
-  snakeOilCert = pkgs.runCommand "nginx-config-validate-cert" { nativeBuildInputs = [ pkgs.openssl.bin ]; } ''
-    mkdir $out
-    openssl genrsa -des3 -passout pass:xxxxx -out server.pass.key 2048
-    openssl rsa -passin pass:xxxxx -in server.pass.key -out $out/server.key
-    openssl req -new -key $out/server.key -out server.csr \
-    -subj "/C=UK/ST=Warwickshire/L=Leamington/O=OrgName/OU=IT Department/CN=example.com"
-    openssl x509 -req -days 1 -in server.csr -signkey $out/server.key -out $out/server.crt
-  '';
-  validatedConfigFile = pkgs.runCommand "validated-nginx.conf" { nativeBuildInputs = [ cfg.package ]; } ''
-    # nginx absolutely wants to read the certificates even when told to only validate config, so let's provide fake certs
-    sed ${configFile} \
-    -e "s|ssl_certificate .*;|ssl_certificate ${snakeOilCert}/server.crt;|g" \
-    -e "s|ssl_trusted_certificate .*;|ssl_trusted_certificate ${snakeOilCert}/server.crt;|g" \
-    -e "s|ssl_certificate_key .*;|ssl_certificate_key ${snakeOilCert}/server.key;|g" \
-    > conf
-
-    LD_PRELOAD=${pkgs.libredirect}/lib/libredirect.so \
-      NIX_REDIRECTS="/etc/resolv.conf=/dev/null" \
-      nginx -t -c $(readlink -f ./conf) > out 2>&1 || true
-    if ! grep -q "syntax is ok" out; then
-      echo nginx config validation failed.
-      echo config was ${configFile}.
-      echo 'in case of false positive, set `services.nginx.validateConfig` to false.'
-      echo nginx output:
-      cat out
-      exit 1
-    fi
-    cp ${configFile} $out
-  '';
-
-  finalConfigFile = if cfg.validateConfig then validatedConfigFile else configFile;
 in
 
 {
@@ -580,17 +548,6 @@ in
         '';
       };
 
-      validateConfig = mkOption {
-        # FIXME: re-enable if we can make of the configurations work.
-        #default = pkgs.stdenv.hostPlatform == pkgs.stdenv.buildPlatform;
-        default = false;
-        defaultText = literalExpression "pkgs.stdenv.hostPlatform == pkgs.stdenv.buildPlatform";
-        type = types.bool;
-        description = lib.mdDoc ''
-          Validate the generated nginx config at build time. The check is not very robust and can be disabled in case of false positives. This is notably the case when cross-compiling or when using `include` with files outside of the store.
-        '';
-      };
-
       additionalModules = mkOption {
         default = [];
         type = types.listOf (types.attrsOf types.anything);
@@ -1128,7 +1085,7 @@ in
     };
 
     environment.etc."nginx/nginx.conf" = mkIf cfg.enableReload {
-      source = finalConfigFile;
+      source = configFile;
     };
 
     # This service waits for all certificates to be available
@@ -1147,7 +1104,7 @@ in
       # certs are updated _after_ config has been reloaded.
       before = sslTargets;
       after = sslServices;
-      restartTriggers = optionals cfg.enableReload [ finalConfigFile ];
+      restartTriggers = optionals cfg.enableReload [ configFile ];
       # Block reloading if not all certs exist yet.
       # Happens when config changes add new vhosts/certs.
       unitConfig.ConditionPathExists = optionals (sslServices != []) (map (certName: certs.${certName}.directory + "/fullchain.pem") dependentCertNames);
diff --git a/nixos/modules/tasks/filesystems/envfs.nix b/nixos/modules/tasks/filesystems/envfs.nix
index ef8f655c532..450b805f0f5 100644
--- a/nixos/modules/tasks/filesystems/envfs.nix
+++ b/nixos/modules/tasks/filesystems/envfs.nix
@@ -35,7 +35,7 @@ in {
         type = lib.types.package;
         description = lib.mdDoc "Which package to use for the envfs.";
         default = pkgs.envfs;
-        defaultText = lib.mdDoc "pkgs.envfs";
+        defaultText = lib.literalExpression "pkgs.envfs";
       };
     };
   };
diff --git a/nixos/tests/nginx.nix b/nixos/tests/nginx.nix
index 73f1133bd6c..d9d073822a1 100644
--- a/nixos/tests/nginx.nix
+++ b/nixos/tests/nginx.nix
@@ -61,7 +61,7 @@ import ./make-test-python.nix ({ pkgs, ... }: {
 
       specialisation.reloadWithErrorsSystem.configuration = {
         services.nginx.package = pkgs.nginxMainline;
-        services.nginx.virtualHosts."hello".extraConfig = "access_log /does/not/exist.log;";
+        services.nginx.virtualHosts."!@$$(#*%".locations."~@#*$*!)".proxyPass = ";;;";
       };
     };
   };
diff --git a/pkgs/applications/networking/cluster/argocd/default.nix b/pkgs/applications/networking/cluster/argocd/default.nix
index aaa07c0af80..034af9a07e5 100644
--- a/pkgs/applications/networking/cluster/argocd/default.nix
+++ b/pkgs/applications/networking/cluster/argocd/default.nix
@@ -2,13 +2,13 @@
 
 buildGoModule rec {
   pname = "argocd";
-  version = "2.5.6";
+  version = "2.5.7";
 
   src = fetchFromGitHub {
     owner = "argoproj";
     repo = "argo-cd";
     rev = "v${version}";
-    sha256 = "sha256-R00HW4jh6zohMoli9aomPCK/svzWSUi9fcRFvevMhyU=";
+    sha256 = "sha256-hEfPiDbEdmuD/IHM9Tfy0kUkKDpbTZ0HzMjIt/ifcPk=";
   };
 
   proxyVendor = true; # darwin/linux hash mismatch
diff --git a/pkgs/development/libraries/wlroots/default.nix b/pkgs/development/libraries/wlroots/default.nix
index 761e9778401..9d03d193d81 100644
--- a/pkgs/development/libraries/wlroots/default.nix
+++ b/pkgs/development/libraries/wlroots/default.nix
@@ -121,8 +121,8 @@ rec {
   };
 
   wlroots_0_16 = generic {
-    version = "0.16.0";
-    hash = "sha256-k7BFx1xvvsdCXNWX0XeZYwv8H/myk4p42i2Y6vjILqM=";
+    version = "0.16.1";
+    hash = "sha256-UyPN7zmytre4emwx/ztZ4JefXHwixPV6UEEqnhSLbIY=";
     extraBuildInputs = [ vulkan-loader ];
     extraNativeBuildInputs = [ glslang ];
     extraPatch = ''
diff --git a/pkgs/development/python-modules/markdown-it-py/default.nix b/pkgs/development/python-modules/markdown-it-py/default.nix
index 1f2bc34b000..c1c5da228f0 100644
--- a/pkgs/development/python-modules/markdown-it-py/default.nix
+++ b/pkgs/development/python-modules/markdown-it-py/default.nix
@@ -12,6 +12,9 @@
 , pytestCheckHook
 , pythonOlder
 , typing-extensions
+# allow disabling tests for the nixos manual build.
+# the test suite closure is just too large.
+, disableTests ? false
 }:
 
 buildPythonPackage rec {
@@ -43,6 +46,7 @@ buildPythonPackage rec {
   nativeCheckInputs = [
     psutil
     py
+  ] ++ lib.optionals (! disableTests) [
     pytest-benchmark
     pytest-regressions
     pytestCheckHook
diff --git a/pkgs/development/python-modules/mdit-py-plugins/default.nix b/pkgs/development/python-modules/mdit-py-plugins/default.nix
index 291ea54c56b..f93751ca71d 100644
--- a/pkgs/development/python-modules/mdit-py-plugins/default.nix
+++ b/pkgs/development/python-modules/mdit-py-plugins/default.nix
@@ -6,6 +6,9 @@
 , markdown-it-py
 , pytest-regressions
 , pytestCheckHook
+# allow disabling tests for the nixos manual build.
+# the test suite closure is just too large.
+, disableTests ? false
 }:
 
 buildPythonPackage rec {
@@ -30,7 +33,7 @@ buildPythonPackage rec {
     markdown-it-py
   ];
 
-  nativeCheckInputs = [
+  nativeCheckInputs = lib.optionals (!disableTests) [
     pytestCheckHook
     pytest-regressions
   ];
diff --git a/pkgs/development/tools/misc/igprof/default.nix b/pkgs/development/tools/misc/igprof/default.nix
index 91d78d0f5fd..a4e170f31b2 100644
--- a/pkgs/development/tools/misc/igprof/default.nix
+++ b/pkgs/development/tools/misc/igprof/default.nix
@@ -1,14 +1,14 @@
 {lib, stdenv, fetchFromGitHub, libunwind, cmake, pcre, gdb}:
 
 stdenv.mkDerivation rec {
-  version = "5.9.16";
+  version = "5.9.18";
   pname = "igprof";
 
   src = fetchFromGitHub {
     owner = "igprof";
     repo = "igprof";
     rev = "v${version}";
-    sha256 = "0rx3mv8zdh9bmcpfbzkib3d52skzfr8600gh5gv21wcsh50jnifx";
+    sha256 = "sha256-UTrAaH8C79km78Z/7NxvQ6dnl4u4Ki80nORf4bsoSNw=";
   };
 
   postPatch = ''
diff --git a/pkgs/development/tools/yarn2nix-moretea/yarn2nix/default.nix b/pkgs/development/tools/yarn2nix-moretea/yarn2nix/default.nix
index 459de392820..3af3e43fe21 100644
--- a/pkgs/development/tools/yarn2nix-moretea/yarn2nix/default.nix
+++ b/pkgs/development/tools/yarn2nix-moretea/yarn2nix/default.nix
@@ -2,7 +2,7 @@
 , nodejs ? pkgs.nodejs
 , yarn ? pkgs.yarn
 , allowAliases ? pkgs.config.allowAliases
-}:
+}@inputs:
 
 let
   inherit (pkgs) stdenv lib fetchurl linkFarm callPackage git rsync makeWrapper runCommandLocal;
@@ -70,6 +70,8 @@ in rec {
     offlineCache ? importOfflineCache yarnNix,
     yarnFlags ? [ ],
     ignoreScripts ? true,
+    nodejs ? inputs.nodejs,
+    yarn ? inputs.yarn.override { nodejs = nodejs; },
     pkgConfig ? {},
     preBuild ? "",
     postBuild ? "",
@@ -169,6 +171,8 @@ in rec {
     src,
     packageJSON ? src + "/package.json",
     yarnLock ? src + "/yarn.lock",
+    nodejs ? inputs.nodejs,
+    yarn ? inputs.yarn.override { nodejs = nodejs; },
     packageOverrides ? {},
     ...
   }@attrs:
@@ -226,7 +230,7 @@ in rec {
         inherit name;
         value = mkYarnPackage (
           builtins.removeAttrs attrs ["packageOverrides"]
-          // { inherit src packageJSON yarnLock packageResolutions workspaceDependencies; }
+          // { inherit src packageJSON yarnLock nodejs yarn packageResolutions workspaceDependencies; }
           // lib.attrByPath [name] {} packageOverrides
         );
       })
@@ -241,6 +245,8 @@ in rec {
     yarnLock ? src + "/yarn.lock",
     yarnNix ? mkYarnNix { inherit yarnLock; },
     offlineCache ? importOfflineCache yarnNix,
+    nodejs ? inputs.nodejs,
+    yarn ? inputs.yarn.override { nodejs = nodejs; },
     yarnFlags ? [ ],
     yarnPreBuild ? "",
     yarnPostBuild ? "",
@@ -268,7 +274,7 @@ in rec {
         preBuild = yarnPreBuild;
         postBuild = yarnPostBuild;
         workspaceDependencies = workspaceDependenciesTransitive;
-        inherit packageJSON pname version yarnLock offlineCache yarnFlags pkgConfig packageResolutions;
+        inherit packageJSON pname version yarnLock offlineCache nodejs yarn yarnFlags pkgConfig packageResolutions;
       };
 
       publishBinsFor_ = unlessNull publishBinsFor [pname];
diff --git a/pkgs/development/tools/zsv/default.nix b/pkgs/development/tools/zsv/default.nix
index d5f188f80b6..639b9bcb4b2 100644
--- a/pkgs/development/tools/zsv/default.nix
+++ b/pkgs/development/tools/zsv/default.nix
@@ -2,13 +2,13 @@
 
 stdenv.mkDerivation rec {
   pname = "zsv";
-  version = "0.3.4-alpha";
+  version = "0.3.5-alpha";
 
   src = fetchFromGitHub {
     owner = "liquidaty";
     repo = "zsv";
     rev = "v${version}";
-    sha256 = "sha256-3drVqKRs5bjkvQiHyEANI5geeF5g7ba2+RxmAhxbu84=";
+    hash = "sha256-HW/w2bJVnTELh36rUfGIzAsc6e+PTBGsAdHDz7gFAdI=";
   };
 
   nativeBuildInputs = [ perl ];
diff --git a/pkgs/os-specific/darwin/cctools/apple.nix b/pkgs/os-specific/darwin/cctools/apple.nix
new file mode 100644
index 00000000000..f8ff90dbb13
--- /dev/null
+++ b/pkgs/os-specific/darwin/cctools/apple.nix
@@ -0,0 +1,118 @@
+{ lib, stdenv, fetchurl, symlinkJoin, xcbuildHook, tcsh, libobjc, libtapi, libunwind, llvm, memstreamHook, xar }:
+
+let
+
+cctools = stdenv.mkDerivation rec {
+  pname = "cctools";
+  version = "973.0.1";
+
+  src = fetchurl {
+    url = "https://opensource.apple.com/tarballs/cctools/cctools-${version}.tar.gz";
+    hash = "sha256-r/6tsyyfi3R/0cLl+lN/B9ZaOaVF+Z7vJ6xj4LzSgiQ=";
+  };
+
+  patches = [
+    ./cctools-add-missing-vtool-libstuff-dep.patch
+  ];
+
+  postPatch = ''
+    for file in libstuff/writeout.c misc/libtool.c misc/lipo.c; do
+      substituteInPlace "$file" \
+        --replace '__builtin_available(macOS 10.12, *)' '0'
+    done
+    substituteInPlace libmacho/swap.c \
+      --replace '#ifndef RLD' '#if 1'
+  '';
+
+  nativeBuildInputs = [ xcbuildHook memstreamHook ];
+  buildInputs = [ libobjc llvm ];
+
+  xcbuildFlags = [
+    "MACOSX_DEPLOYMENT_TARGET=10.12"
+  ];
+
+  doCheck = true;
+  checkPhase = ''
+    runHook preCheck
+
+    Products/Release/libstuff_test
+    rm Products/Release/libstuff_test
+
+    runHook postCheck
+  '';
+
+  installPhase = ''
+    runHook preInstall
+
+    rm -rf "$out/usr"
+    mkdir -p "$out/bin"
+    find Products/Release -maxdepth 1 -type f -perm 755 -exec cp {} "$out/bin/" \;
+    cp -r include "$out/"
+
+    ln -s ./nm-classic "$out"/bin/nm
+    ln -s ./otool-classic "$out"/bin/otool
+
+    runHook postInstall
+  '';
+};
+
+ld64 = stdenv.mkDerivation rec {
+  pname = "ld64";
+  version = "609";
+
+  src = fetchurl {
+    url = "https://opensource.apple.com/tarballs/ld64/ld64-${version}.tar.gz";
+    hash = "sha256-SqQ7SqmK+uOPijzxOTqtkEu3qYmcth6H7rrQ03R1Q+4=";
+  };
+
+  postPatch = ''
+    substituteInPlace ld64.xcodeproj/project.pbxproj \
+      --replace "/bin/csh" "${tcsh}/bin/tcsh" \
+      --replace 'F9E8D4BE07FCAF2A00FD5801 /* PBXBuildRule */,' "" \
+      --replace 'F9E8D4BD07FCAF2000FD5801 /* PBXBuildRule */,' ""
+
+    sed -i src/ld/Options.cpp -e '1iconst char ldVersionString[] = "${version}";'
+  '';
+
+  nativeBuildInputs = [ xcbuildHook ];
+  buildInputs = [
+    libtapi
+    libunwind
+    llvm
+    xar
+  ];
+
+  installPhase = ''
+    runHook preInstall
+
+    mkdir -p "$out/bin"
+    find Products/Release-assert -maxdepth 1 -type f -perm 755 -exec cp {} "$out/bin/" \;
+
+    runHook postInstall
+  '';
+};
+
+in
+
+symlinkJoin rec {
+  name = "cctools-${version}";
+  version = "${cctools.version}-${ld64.version}";
+
+  paths = [
+    cctools
+    ld64
+  ];
+
+  # workaround for the fetch-tarballs script
+  passthru = {
+    inherit (cctools) src;
+    ld64_src = ld64.src;
+  };
+
+  meta = with lib; {
+    description = "MacOS Compiler Tools";
+    homepage = "http://www.opensource.apple.com/source/cctools/";
+    license = licenses.apsl20;
+    platforms = platforms.darwin;
+  };
+}
diff --git a/pkgs/os-specific/darwin/cctools/cctools-add-missing-vtool-libstuff-dep.patch b/pkgs/os-specific/darwin/cctools/cctools-add-missing-vtool-libstuff-dep.patch
new file mode 100644
index 00000000000..1cd65ec6bcf
--- /dev/null
+++ b/pkgs/os-specific/darwin/cctools/cctools-add-missing-vtool-libstuff-dep.patch
@@ -0,0 +1,11 @@
+diff -ru a/cctools.xcodeproj/project.pbxproj b/cctools.xcodeproj/project.pbxproj
+--- a/cctools.xcodeproj/project.pbxproj	2021-02-24 20:30:55.000000000 -0500
++++ b/cctools.xcodeproj/project.pbxproj	2022-01-31 20:01:09.000000000 -0500
+@@ -2558,6 +2558,7 @@
+ 			isa = PBXFrameworksBuildPhase;
+ 			buildActionMask = 2147483647;
+ 			files = (
++				DE97E92421F3B86100C7947D /* libstuff.a in Frameworks */,
+ 			);
+ 			runOnlyForDeploymentPostprocessing = 0;
+ 		};
diff --git a/pkgs/tools/graphics/gifski/default.nix b/pkgs/tools/graphics/gifski/default.nix
index 7751ecdf25e..532201c4818 100644
--- a/pkgs/tools/graphics/gifski/default.nix
+++ b/pkgs/tools/graphics/gifski/default.nix
@@ -2,16 +2,16 @@
 
 rustPlatform.buildRustPackage rec {
   pname = "gifski";
-  version = "1.8.0";
+  version = "1.9.0";
 
   src = fetchFromGitHub {
     owner = "ImageOptim";
     repo = "gifski";
     rev = version;
-    sha256 = "sha256-KAm4ng+FIMmhHAxoFNNVo48GVbW3c+raX6Hcab+KCf8=";
+    sha256 = "sha256-iG7XaPBNTmt/yNMeSY8UKwesFJFUECAsOYQ0idegk1w=";
   };
 
-  cargoSha256 = "sha256-xbE1Olf0lh6o4kF9ubZhdnTbZsJcd5TvLf7P1nWLf9Q=";
+  cargoHash = "sha256-7bjsxbUsHjsVER0HaQ+x0dkaAI2sb7if8mS6JwQIbzc=";
 
   nativeBuildInputs = lib.optionals stdenv.isLinux [ pkg-config ];
 
diff --git a/pkgs/tools/networking/socat/default.nix b/pkgs/tools/networking/socat/default.nix
index aee97d1de58..edd5b0603e6 100644
--- a/pkgs/tools/networking/socat/default.nix
+++ b/pkgs/tools/networking/socat/default.nix
@@ -23,11 +23,6 @@ stdenv.mkDerivation rec {
       --replace /sbin/ifconfig ifconfig
   '';
 
-  configureFlags = lib.optionals stdenv.hostPlatform.isMusl [
-    # musl doesn't have getprotobynumber_r
-    "sc_cv_getprotobynumber_r=2"
-  ];
-
   buildInputs = [ openssl readline ];
 
   hardeningEnable = [ "pie" ];
diff --git a/pkgs/top-level/darwin-packages.nix b/pkgs/top-level/darwin-packages.nix
index ddf0be41a3e..cb32a46ca25 100644
--- a/pkgs/top-level/darwin-packages.nix
+++ b/pkgs/top-level/darwin-packages.nix
@@ -104,6 +104,10 @@ impure-cmds // appleSourcePackages // chooseLibs // {
     stdenv = if stdenv.isDarwin then stdenv else pkgs.libcxxStdenv;
   };
 
+  cctools-apple = callPackage ../os-specific/darwin/cctools/apple.nix {
+    stdenv = if stdenv.isDarwin then stdenv else pkgs.libcxxStdenv;
+  };
+
   # TODO: remove alias.
   cf-private = self.apple_sdk.frameworks.CoreFoundation;