diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json
index 02b2f9f0273d..dfcfce92d1bd 100644
--- a/.devcontainer/devcontainer.json
+++ b/.devcontainer/devcontainer.json
@@ -5,17 +5,24 @@
"ghcr.io/devcontainers/features/nix:1": {
// fails in the devcontainer sandbox, enable sandbox via config instead
"multiUser": false,
- "packages": "nixd,nixfmt-unstable",
+ "packages": "nixpkgs.nixd,nixpkgs.nixfmt-rfc-style",
+ "useAttributePath": true,
"extraNixConfig": "experimental-features = nix-command flakes,sandbox = true"
}
},
+ // Fixup permissions inside container.
+ // https://github.com/NixOS/nix/issues/6680#issuecomment-1230902525
+ "postCreateCommand": "sudo apt-get install -y acl",
+ "postStartCommand": "sudo setfacl -k /tmp; if [ -e /dev/kvm ]; then sudo chgrp $(id -g) /dev/kvm; fi",
"customizations": {
"vscode": {
"extensions": [
"jnoortheen.nix-ide"
],
"settings": {
- "nix.formatterPath": "nixfmt-rfc-style",
+ "[nix]": {
+ "editor.formatOnSave": true
+ },
"nix.enableLanguageServer": true,
"nix.serverPath": "nixd"
}
diff --git a/.editorconfig b/.editorconfig
index 3e6d1711e421..020db105c04a 100644
--- a/.editorconfig
+++ b/.editorconfig
@@ -35,8 +35,12 @@ indent_size = 1
[*.{json,lock,md,nix,rb}]
indent_size = 2
-# Match perl/python/shell scripts, set indent width of four
-[*.{bash,pl,pm,py,sh}]
+# Match all the Bash code in Nix files, set indent width of two
+[*.{bash,sh}]
+indent_size = 2
+
+# Match Perl and Python scripts, set indent width of four
+[*.{pl,pm,py}]
indent_size = 4
# Match gemfiles, set indent to spaces with width of two
@@ -44,9 +48,10 @@ indent_size = 4
indent_size = 2
indent_style = space
-# Match package.json, which are generally pulled from upstream and accept them as they are
-[package.json]
+# Match package.json and package-lock.json, which are generally pulled from upstream and accept them as they are
+[package{,-lock}.json]
indent_style = unset
+insert_final_newline = unset
# Disable file types or individual files
# some of these files may be auto-generated and/or require significant changes
@@ -81,47 +86,10 @@ charset = unset
[eggs.nix]
trim_trailing_whitespace = unset
-[nixos/modules/services/networking/ircd-hybrid/*.{conf,in}]
-trim_trailing_whitespace = unset
-
-[pkgs/build-support/dotnetenv/Wrapper/**]
-end_of_line = unset
-indent_style = unset
-insert_final_newline = unset
-trim_trailing_whitespace = unset
-
[registry.dat]
end_of_line = unset
insert_final_newline = unset
-[pkgs/development/haskell-modules/hackage-packages.nix]
-indent_style = unset
-trim_trailing_whitespace = unset
-
-[pkgs/misc/documentation-highlighter/**]
-insert_final_newline = unset
-
-[pkgs/servers/dict/wordnet_structures.py]
-trim_trailing_whitespace = unset
-
-[pkgs/tools/misc/timidity/timidity.cfg]
-trim_trailing_whitespace = unset
-
-[pkgs/tools/security/qdigidoc/vendor/*]
-end_of_line = unset
-insert_final_newline = unset
-trim_trailing_whitespace = unset
-
-[pkgs/tools/virtualization/ovftool/*.ova]
-end_of_line = unset
-insert_final_newline = unset
-trim_trailing_whitespace = unset
-charset = unset
-
-[lib/tests/*.plist]
-indent_style = tab
-insert_final_newline = unset
-
-[pkgs/kde/generated/**]
-insert_final_newline = unset
-end_of_line = unset
+# Keep this hint at the bottom:
+# Please don't add entries for subfolders here.
+# Create /.editorconfig instead.
diff --git a/.git-blame-ignore-revs b/.git-blame-ignore-revs
index bfd399101269..1e37e6f68ffe 100644
--- a/.git-blame-ignore-revs
+++ b/.git-blame-ignore-revs
@@ -251,3 +251,18 @@ fd14c067813572afc03ddbf7cdedc3eab5a59954
# treewide format of all Nix files
374e6bcc403e02a35e07b650463c01a52b13a7c8 # !autorebase nix-shell --run treefmt
+
+# nix: nixfmt-rfc-style
+a4f7e161b380b35b2f7bc432659a95fd71254ad8
+0812c9a321003c924868051d2b2e1934e8880f3f
+34f269c14ac18d89ddee9a8f54b1ca92a85bbcc6
+062c34cdace499aa44f0fa6ca6f2ca71769f6c43
+
+# haskellPackages.hercules-ci-agent (cabal2nix -> nixfmt-rfc-style)
+9314da7ee8d2aedfb15193b8c489da51efe52bb5
+
+# nix-builder-vm: nixfmt-rfc-style
+a034fb50f79816c6738fb48b48503b09ea3b0132
+
+# treewide: switch instances of lib.teams.*.members to the new meta.teams attribute
+05580f4b4433fda48fff30f60dfd303d6ee05d21
diff --git a/.github/ISSUE_TEMPLATE/04_build_failure.yml b/.github/ISSUE_TEMPLATE/04_build_failure.yml
index c672361dd56a..2dfa1b779a6d 100644
--- a/.github/ISSUE_TEMPLATE/04_build_failure.yml
+++ b/.github/ISSUE_TEMPLATE/04_build_failure.yml
@@ -61,12 +61,14 @@ body:
You can still open a build failure report, but please say '**No, Hydra cannot reproduce this build failure.**' below.
- If there's a icon near the package entry, say '**Hydra is currently rebuilding this package.**'
- If there's a icon near the package entry, then the build job was stopped manually. If this occurs, please coordinate with the [Infrastructure Team](https://matrix.to/#/#infra:nixos.org), and say '**The last build job was manually cancelled.**'
+ - If Hydra isn't supposed to build the package at all, say '**Hydra doesn’t try to build the package.**'
options:
- "Please select the Hydra Status."
- "Yes, Hydra can reproduce this build failure."
- "No, Hydra cannot reproduce this build failure."
- "Hydra is currently rebuilding this package."
- "The last build job was manually cancelled."
+ - "Hydra doesn’t try to build the package."
default: 0
validations:
required: true
diff --git a/.github/ISSUE_TEMPLATE/05_package_request.yml b/.github/ISSUE_TEMPLATE/05_package_request.yml
deleted file mode 100644
index b73360a91cb4..000000000000
--- a/.github/ISSUE_TEMPLATE/05_package_request.yml
+++ /dev/null
@@ -1,108 +0,0 @@
-name: "Request: new package"
-description: "Create a package request for software that is not yet included in Nixpkgs."
-title: "Package request: PACKAGENAME"
-labels: ["0.kind: enhancement", "0.kind: packaging request", "9.needs: package (new)"]
-body:
- - type: "markdown"
- attributes:
- value: |
-
-
-
-
-
-
-
-
-
-
- Welcome to Nixpkgs. Please replace the **`Package request: PACKAGENAME`** template above with the correct package name (As seen in the [NixOS Package Search](https://search.nixos.org/packages)).
-
- > [!TIP]
- > For instance, if you were filing a request against the missing `hello` package, your title would be as follows:
- > `Package request: hello`
-
- ---
- - type: "textarea"
- id: "description"
- attributes:
- label: "Describe the package"
- description: "Please include a clear and concise description of what the package is."
- validations:
- required: true
- - type: "input"
- id: "homepage"
- attributes:
- label: "Upstream homepage"
- description: "Please copy and paste a link to the package's homepage."
- validations:
- required: true
- - type: "input"
- id: "source"
- attributes:
- label: "Source URL"
- description: "Please copy and paste a link to the package's source code or binary download page."
- validations:
- required: true
- - type: "input"
- id: "license"
- attributes:
- label: "License"
- description: "Please indicate the package's license. If the package has no license, or the source code is not public, please indicate the package is `unfree`."
- validations:
- required: true
- - type: "dropdown"
- id: "platforms"
- attributes:
- label: "Platforms"
- description: "Please indicate the platforms this package compiles for. `darwin` refers to macOS. `Exotic` refers to uncommon platforms like RISC-V or 32-bit ARM; please mention in the 'Additional Context' section below if this package is supposed to compile for such exotic platforms."
- multiple: true
- options:
- - "x86_64-linux"
- - "aarch64-linux"
- - "x86_64-darwin"
- - "aarch64-darwin"
- - "Exotic"
- - type: "textarea"
- id: "additional-context"
- attributes:
- label: "Additional context"
- description: "Add any other context about the proposed module here."
- validations:
- required: false
- - type: "checkboxes"
- id: "sanity-check"
- attributes:
- label: "I assert that this issue is relevant for Nixpkgs"
- options:
- - label: "I assert that this package does not yet exist in an [open pull request](https://github.com/NixOS/nixpkgs/pulls?q=is%3Aopen+is%3Apr+label%3A%228.has%3A+package+%28new%29%22) or in [Nixpkgs Unstable](https://search.nixos.org/packages?channel=unstable)."
- required: true
- - label: "I assert that this is not a [duplicate of any known issue](https://github.com/NixOS/nixpkgs/issues?q=is%3Aissue+is%3Aopen+label%3A%220.kind%3A+packaging+request%22)."
- required: true
- - label: "I assert that I have read the [NixOS Code of Conduct](https://github.com/NixOS/.github/blob/master/CODE_OF_CONDUCT.md) and agree to abide by it."
- required: true
- - type: "markdown"
- attributes:
- value: |
- # Thank you for helping improve Nixpkgs!
-
- ---
- - type: "textarea"
- id: "prioritisation"
- attributes:
- label: "For this package's maintainers:"
- description: |
- **Please do not modify this text area!**
-
- This template helps Nixpkgs developers know which issues should be prioritised by allowing users to vote with a :+1: reaction and also reminds them to tag this issue in their pull requests.
- This is not a guarantee that highly-requested issues will be fixed first, but it helps us to figure out what's important to users. Please react on other users' issues if you find them important.
- value: |
-
- Please tag this issue in your pull request description. (i.e. `Resolves #ISSUE`.)
-
- ---
-
- Add a :+1: [reaction] to [issues you find important].
-
- [reaction]: https://github.blog/2016-03-10-add-reactions-to-pull-requests-issues-and-comments/
- [issues you find important]: https://github.com/NixOS/nixpkgs/issues?q=is%3Aissue+is%3Aopen+sort%3Areactions-%2B1-desc
diff --git a/.github/ISSUE_TEMPLATE/05_update_request.yml b/.github/ISSUE_TEMPLATE/05_update_request.yml
new file mode 100644
index 000000000000..91944dbd9cf5
--- /dev/null
+++ b/.github/ISSUE_TEMPLATE/05_update_request.yml
@@ -0,0 +1,125 @@
+name: "Request: package update"
+description: "Create an update request for an existing, but outdated package."
+title: "Update Request: PACKAGENAME OLDVERSION → NEWVERSION"
+labels: ["0.kind: enhancement", "9.needs: package (update)"]
+body:
+ - type: "markdown"
+ attributes:
+ value: |
+
+
+
+
+
+
+
+
+
+
+ Welcome to Nixpkgs. Please replace the **`Update Request: PACKAGENAME OLDVERSION → NEWVERSION`** template above with the correct package name (As seen in the [NixOS Package Search](https://search.nixos.org/packages)), the current version of the package, and the latest version of the package.
+
+ > [!TIP]
+ > For instance, if you were filing a request against the out of date `hello` package, where the current version in Nixpkgs is 1.0.0, but the latest version upstream is 1.0.1, your title would be as follows:
+ > `Update Request: hello 1.0.0 → 1.0.1`
+
+ ---
+ - type: "dropdown"
+ id: "version"
+ attributes:
+ label: "Nixpkgs version"
+ description: |
+ What version of Nixpkgs are you using?
+
+ > [!IMPORTANT]
+ > If you are using an older or stable version, please update to the latest **unstable** version and check if the package is still out of date.
+ > If the package has been updated in unstable, but you believe the update should be backported to the stable release of Nixpkgs, please file the '**Request: backport to stable**' form instead.
+ options:
+ - "Please select a version."
+ - "- Unstable (25.05)"
+ - "- Stable (24.11)"
+ - "- Previous Stable (24.05)"
+ default: 0
+ validations:
+ required: true
+ - type: "input"
+ id: "name"
+ attributes:
+ label: "Package name"
+ description: "Please indicate the name of the package."
+ validations:
+ required: true
+ - type: "input"
+ id: "upstream-version"
+ attributes:
+ label: "Upstream version"
+ description: "Please indicate the latest version of the package."
+ validations:
+ required: true
+ - type: "input"
+ id: "nixpkgs-version"
+ attributes:
+ label: "Nixpkgs version"
+ description: |
+ Please indicate the current version number in Nixpkgs' **unstable** channel. You can check this by setting the [NixOS Package Search](https://search.nixos.org/packages?channel=unstable) channel to 'unstable' and searching for the package.
+ If you meant to request an upgrade in the stable channel, please file the '**Request: backport to stable**' form instead.
+ validations:
+ required: true
+ - type: "input"
+ id: "changelog"
+ attributes:
+ label: "Changelog"
+ description: "If applicable, please link the upstream changelog for the latest version."
+ validations:
+ required: false
+ - type: "textarea"
+ id: "additional-context"
+ attributes:
+ label: "Additional context"
+ description: "Add any other context about the update here."
+ validations:
+ required: false
+ - type: "textarea"
+ id: "maintainers"
+ attributes:
+ label: "Notify maintainers"
+ description: |
+ Please mention the people who are in the **Maintainers** list of the offending package. This is done by by searching for the package on the [NixOS Package Search](https://search.nixos.org/packages) and mentioning the people listed under **Maintainers** by prefixing their GitHub usernames with an '@' character. Please add the mentions above the `---` characters in the template below.
+ value: |
+
+
+ ---
+
+ **Note for maintainers:** Please tag this issue in your pull request description. (i.e. `Resolves #ISSUE`.)
+ validations:
+ required: false
+ - type: "checkboxes"
+ id: "sanity-check"
+ attributes:
+ label: "I assert that this issue is relevant for Nixpkgs"
+ options:
+ - label: "I assert that this package update does not yet exist in an [open pull request](https://github.com/NixOS/nixpkgs/pulls?q=is%3Aopen+is%3Apr+label%3A%228.has%3A+package+%28update%29%22) or in [Nixpkgs Unstable](https://search.nixos.org/packages?channel=unstable)."
+ required: true
+ - label: "I assert that this is not a [duplicate of any known issue](https://github.com/NixOS/nixpkgs/issues?q=is%3Aopen+is%3Aissue+label%3A%229.needs%3A+package+%28update%29%22)."
+ required: true
+ - label: "I assert that I have read the [NixOS Code of Conduct](https://github.com/NixOS/.github/blob/master/CODE_OF_CONDUCT.md) and agree to abide by it."
+ required: true
+ - type: "markdown"
+ attributes:
+ value: |
+ # Thank you for helping improve Nixpkgs!
+
+ ---
+ - type: "textarea"
+ id: "prioritisation"
+ attributes:
+ label: "Is this issue important to you?"
+ description: |
+ **Please do not modify this text area!**
+
+ This template helps Nixpkgs developers know which issues should be prioritised by allowing users to vote with a :+1: reaction.
+ This is not a guarantee that highly-requested issues will be fixed first, but it helps us to figure out what's important to users. Please react on other users' issues if you find them important.
+ value: |
+ Add a :+1: [reaction] to [issues you find important].
+
+ [reaction]: https://github.blog/2016-03-10-add-reactions-to-pull-requests-issues-and-comments/
+ [issues you find important]: https://github.com/NixOS/nixpkgs/issues?q=is%3Aissue+is%3Aopen+sort%3Areactions-%2B1-desc
diff --git a/.github/ISSUE_TEMPLATE/07_module_request.yml b/.github/ISSUE_TEMPLATE/06_module_request.yml
similarity index 100%
rename from .github/ISSUE_TEMPLATE/07_module_request.yml
rename to .github/ISSUE_TEMPLATE/06_module_request.yml
diff --git a/.github/ISSUE_TEMPLATE/06_update_request.yml b/.github/ISSUE_TEMPLATE/06_update_request.yml
deleted file mode 100644
index 4b29a2af0126..000000000000
--- a/.github/ISSUE_TEMPLATE/06_update_request.yml
+++ /dev/null
@@ -1,121 +0,0 @@
-name: "Request: package update"
-description: "Create an update request for an existing, but outdated package."
-title: "Update Request: PACKAGENAME OLDVERSION → NEWVERSION"
-labels: ["0.kind: enhancement", "9.needs: package (update)"]
-body:
- - type: "markdown"
- attributes:
- value: |
-
-
-
-
-
-
-
-
-
-
- Welcome to Nixpkgs. Please replace the **`Update Request: PACKAGENAME OLDVERSION → NEWVERSION`** template above with the correct package name (As seen in the [NixOS Package Search](https://search.nixos.org/packages)), the current version of the package, and the latest version of the package.
-
- > [!TIP]
- > For instance, if you were filing a request against the out of date `hello` package, where the current version in Nixpkgs is 1.0.0, but the latest version upstream is 1.0.1, your title would be as follows:
- > `Update Request: hello 1.0.0 → 1.0.1`
-
- > [!NOTE]
- > If you are filing an update request to change a package's source to a fork, please file a new package request instead. Even if the original upstream is outdated, the fork should be considered a new package.
-
- ---
- - type: "dropdown"
- id: "version"
- attributes:
- label: "Nixpkgs version"
- description: |
- What version of Nixpkgs are you using?
-
- > [!IMPORTANT]
- > If you are using an older or stable version, please update to the latest **unstable** version and check if the package is still out of date.
- > If the package has been updated in unstable, but you believe the update should be backported to the stable release of Nixpkgs, please file the '**Request: backport to stable**' form instead.
- options:
- - "Please select a version."
- - "- Unstable (25.05)"
- - "- Stable (24.11)"
- - "- Previous Stable (24.05)"
- default: 0
- validations:
- required: true
- - type: "input"
- id: "name"
- attributes:
- label: "Package name"
- description: "Please indicate the name of the package."
- validations:
- required: true
- - type: "input"
- id: "upstream-version"
- attributes:
- label: "Upstream version"
- description: "Please indicate the latest version of the package."
- validations:
- required: true
- - type: "input"
- id: "nixpkgs-version"
- attributes:
- label: "Nixpkgs version"
- description: |
- Please indicate the current version number in Nixpkgs' **unstable** channel. You can check this by setting the [NixOS Package Search](https://search.nixos.org/packages?channel=unstable) channel to 'unstable' and searching for the package.
- If you meant to request an upgrade in the stable channel, please file the '**Request: backport to stable**' form instead.
- validations:
- required: true
- - type: "input"
- id: "changelog"
- attributes:
- label: "Changelog"
- description: "If applicable, please link the upstream changelog for the latest version."
- validations:
- required: false
- - type: "textarea"
- id: "maintainers"
- attributes:
- label: "Notify maintainers"
- description: |
- Please mention the people who are in the **Maintainers** list of the offending package. This is done by by searching for the package on the [NixOS Package Search](https://search.nixos.org/packages) and mentioning the people listed under **Maintainers** by prefixing their GitHub usernames with an '@' character. Please add the mentions above the `---` characters in the template below.
- value: |
-
-
- ---
-
- **Note for maintainers:** Please tag this issue in your pull request description. (i.e. `Resolves #ISSUE`.)
- validations:
- required: false
- - type: "checkboxes"
- id: "sanity-check"
- attributes:
- label: "I assert that this issue is relevant for Nixpkgs"
- options:
- - label: "I assert that this package update does not yet exist in an [open pull request](https://github.com/NixOS/nixpkgs/pulls?q=is%3Aopen+is%3Apr+label%3A%228.has%3A+package+%28update%29%22) or in [Nixpkgs Unstable](https://search.nixos.org/packages?channel=unstable)."
- required: true
- - label: "I assert that this is not a [duplicate of any known issue](https://github.com/NixOS/nixpkgs/issues?q=is%3Aopen+is%3Aissue+label%3A%229.needs%3A+package+%28update%29%22)."
- required: true
- - label: "I assert that I have read the [NixOS Code of Conduct](https://github.com/NixOS/.github/blob/master/CODE_OF_CONDUCT.md) and agree to abide by it."
- required: true
- - type: "markdown"
- attributes:
- value: |
- # Thank you for helping improve Nixpkgs!
-
- ---
- - type: "textarea"
- id: "prioritisation"
- attributes:
- label: "Is this issue important to you?"
- description: |
- **Please do not modify this text area!**
-
- This template helps Nixpkgs developers know which issues should be prioritised by allowing users to vote with a :+1: reaction.
- This is not a guarantee that highly-requested issues will be fixed first, but it helps us to figure out what's important to users. Please react on other users' issues if you find them important.
- value: |
- Add a :+1: [reaction] to [issues you find important].
-
- [reaction]: https://github.blog/2016-03-10-add-reactions-to-pull-requests-issues-and-comments/
- [issues you find important]: https://github.com/NixOS/nixpkgs/issues?q=is%3Aissue+is%3Aopen+sort%3Areactions-%2B1-desc
diff --git a/.github/ISSUE_TEMPLATE/08_backport_request.yml b/.github/ISSUE_TEMPLATE/07_backport_request.yml
similarity index 100%
rename from .github/ISSUE_TEMPLATE/08_backport_request.yml
rename to .github/ISSUE_TEMPLATE/07_backport_request.yml
diff --git a/.github/ISSUE_TEMPLATE/09_documentation_request.yml b/.github/ISSUE_TEMPLATE/08_documentation_request.yml
similarity index 100%
rename from .github/ISSUE_TEMPLATE/09_documentation_request.yml
rename to .github/ISSUE_TEMPLATE/08_documentation_request.yml
diff --git a/.github/ISSUE_TEMPLATE/10_unreproducible_package.yml b/.github/ISSUE_TEMPLATE/09_unreproducible_package.yml
similarity index 100%
rename from .github/ISSUE_TEMPLATE/10_unreproducible_package.yml
rename to .github/ISSUE_TEMPLATE/09_unreproducible_package.yml
diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md
index 16481ee4c513..27635498f54d 100644
--- a/.github/PULL_REQUEST_TEMPLATE.md
+++ b/.github/PULL_REQUEST_TEMPLATE.md
@@ -25,8 +25,9 @@ For new packages please briefly describe the package or provide a link to its ho
- made sure NixOS tests are [linked](https://github.com/NixOS/nixpkgs/blob/master/pkgs/README.md#linking-nixos-module-tests-to-a-package) to the relevant packages
- [ ] Tested compilation of all packages that depend on this change using `nix-shell -p nixpkgs-review --run "nixpkgs-review rev HEAD"`. Note: all changes have to be committed, also see [nixpkgs-review usage](https://github.com/Mic92/nixpkgs-review#usage)
- [ ] Tested basic functionality of all binary files (usually in `./result/bin/`)
-- [25.05 Release Notes](https://github.com/NixOS/nixpkgs/blob/master/nixos/doc/manual/release-notes/rl-2505.section.md) (or backporting [24.11](https://github.com/NixOS/nixpkgs/blob/master/nixos/doc/manual/release-notes/rl-2411.section.md) and [25.05](https://github.com/NixOS/nixpkgs/blob/master/nixos/doc/manual/release-notes/rl-2505.section.md) Release notes)
+- [Nixpkgs 25.11 Release Notes](https://github.com/NixOS/nixpkgs/blob/master/doc/release-notes/rl-2511.section.md) (or backporting [24.11](https://github.com/NixOS/nixpkgs/blob/master/nixos/doc/release-notes/rl-2411.section.md) and [25.05](https://github.com/NixOS/nixpkgs/blob/master/doc/manual/release-notes/rl-2505.section.md) Nixpkgs Release notes)
- [ ] (Package updates) Added a release notes entry if the change is major or breaking
+- [NixOS 25.11 Release Notes](https://github.com/NixOS/nixpkgs/blob/master/nixos/doc/manual/release-notes/rl-2511.section.md) (or backporting [24.11](https://github.com/NixOS/nixpkgs/blob/master/nixos/doc/manual/release-notes/rl-2411.section.md) and [25.05](https://github.com/NixOS/nixpkgs/blob/master/nixos/doc/manual/release-notes/rl-2505.section.md) NixOS Release notes)
- [ ] (Module updates) Added a release notes entry if the change is significant
- [ ] (Module addition) Added a release notes entry if adding a new NixOS module
- [ ] Fits [CONTRIBUTING.md](https://github.com/NixOS/nixpkgs/blob/master/CONTRIBUTING.md).
diff --git a/.github/dependabot.yml b/.github/dependabot.yml
index 5ace4600a1f2..a909ff0fdde0 100644
--- a/.github/dependabot.yml
+++ b/.github/dependabot.yml
@@ -4,3 +4,4 @@ updates:
directory: "/"
schedule:
interval: "weekly"
+ labels: [ ]
diff --git a/.github/labeler-development-branches.yml b/.github/labeler-development-branches.yml
new file mode 100644
index 000000000000..e0b855a07cde
--- /dev/null
+++ b/.github/labeler-development-branches.yml
@@ -0,0 +1,23 @@
+# This file is used by .github/workflows/labels.yml
+# This version is only run for Pull Requests from development branches like staging-next, haskell-updates or python-updates.
+
+"4.workflow: package set update":
+ - any:
+ - head-branch:
+ - '-updates$'
+
+"4.workflow: staging":
+ - any:
+ - head-branch:
+ - '^staging-next$'
+ - '^staging-next-'
+
+"6.topic: haskell":
+ - any:
+ - head-branch:
+ - '^haskell-updates$'
+
+"6.topic: python":
+ - any:
+ - head-branch:
+ - '^python-updates$'
diff --git a/.github/labeler-no-sync.yml b/.github/labeler-no-sync.yml
index a64a5d3cf730..2d4836c58b7b 100644
--- a/.github/labeler-no-sync.yml
+++ b/.github/labeler-no-sync.yml
@@ -1,12 +1,7 @@
# This file is used by .github/workflows/labels.yml
# This version uses `sync-labels: false`, meaning that a non-match will NOT remove the label
-"backport release-24.11":
- - any:
- - changed-files:
- - any-glob-to-any-file:
- - .github/workflows/*
- - ci/**/*.*
+# keep-sorted start case=no numeric=yes newline_separated=yes skip_lines=1
"6.topic: policy discussion":
- any:
@@ -26,3 +21,12 @@
- any-glob-to-any-file:
- doc/**/*
- nixos/doc/**/*
+
+"backport release-25.05":
+ - any:
+ - changed-files:
+ - any-glob-to-any-file:
+ - .github/workflows/*
+ - ci/**/*.*
+
+# keep-sorted end
diff --git a/.github/labeler.yml b/.github/labeler.yml
index 0d1a59489084..7f1ac6d2ea32 100644
--- a/.github/labeler.yml
+++ b/.github/labeler.yml
@@ -1,6 +1,15 @@
# This file is used by .github/workflows/labels.yml
# This version uses `sync-labels: true`, meaning that a non-match will remove the label
+# keep-sorted start case=no numeric=yes newline_separated=yes skip_lines=1
+
+"4.workflow: backport":
+ - any:
+ - base-branch:
+ - '^release-'
+ - '^staging-\d'
+ - '^staging-next-\d'
+
# NOTE: bsd, darwin and cross-compilation labels are handled by ofborg
"6.topic: agda":
- any:
@@ -30,7 +39,7 @@
- changed-files:
- any-glob-to-any-file:
- .github/**/*
- - ci/**/*
+ - ci/**/*.*
"6.topic: coq":
- any:
@@ -40,6 +49,16 @@
- pkgs/development/coq-modules/**/*
- pkgs/top-level/coq-packages.nix
+"6.topic: COSMIC":
+ - any:
+ - changed-files:
+ - any-glob-to-any-file:
+ - nixos/modules/services/desktop-managers/cosmic.nix
+ - nixos/modules/services/display-managers/cosmic-greeter.nix
+ - nixos/tests/cosmic.nix
+ - pkgs/by-name/co/cosmic-*/**/*
+ - pkgs/by-name/xd/xdg-desktop-portal-cosmic/*
+
"6.topic: crystal":
- any:
- changed-files:
@@ -310,6 +329,16 @@
- any-glob-to-any-file:
- pkgs/os-specific/linux/musl/**/*
+"6.topic: nim":
+ - any:
+ - changed-files:
+ - any-glob-to-any-file:
+ - doc/languages-frameworks/nim.section.md
+ - pkgs/build-support/build-nim-package.nix
+ - pkgs/build-support/build-nim-sbom.nix
+ - pkgs/by-name/ni/nim*
+ - pkgs/top-level/nim-overrides.nix
+
"6.topic: nixos":
- any:
- changed-files:
@@ -326,16 +355,6 @@
- nixos/modules/virtualisation/nixos-containers.nix
- pkgs/tools/virtualization/nixos-container/**/*
-"6.topic: nim":
- - any:
- - changed-files:
- - any-glob-to-any-file:
- - doc/languages-frameworks/nim.section.md
- - pkgs/build-support/build-nim-package.nix
- - pkgs/build-support/build-nim-sbom.nix
- - pkgs/by-name/ni/nim*
- - pkgs/top-level/nim-overrides.nix
-
"6.topic: nodejs":
- any:
- changed-files:
@@ -482,14 +501,6 @@
- any-glob-to-any-file:
- maintainers/team-list.nix
-"6.topic: TeX":
- - any:
- - changed-files:
- - any-glob-to-any-file:
- - doc/languages-frameworks/texlive.section.md
- - pkgs/test/texlive/**
- - pkgs/tools/typesetting/tex/**/*
-
"6.topic: testing":
- any:
- changed-files:
@@ -506,6 +517,14 @@
- nixos/tests/make-test-python.nix # legacy
# lib/debug.nix has a test framework (runTests) but it's not the main focus
+"6.topic: TeX":
+ - any:
+ - changed-files:
+ - any-glob-to-any-file:
+ - doc/languages-frameworks/texlive.section.md
+ - pkgs/test/texlive/**
+ - pkgs/tools/typesetting/tex/**/*
+
"6.topic: updaters":
- any:
- changed-files:
@@ -569,13 +588,16 @@
- any-glob-to-any-file:
- nixos/doc/manual/release-notes/**/*
-"8.has: module (update)":
- - any:
- - changed-files:
- - any-glob-to-any-file:
- - nixos/modules/**/*
"8.has: maintainer-list (update)":
- any:
- changed-files:
- any-glob-to-any-file:
- maintainers/maintainer-list.nix
+
+"8.has: module (update)":
+ - any:
+ - changed-files:
+ - any-glob-to-any-file:
+ - nixos/modules/**/*
+
+# keep-sorted end
diff --git a/.github/workflows/backport.yml b/.github/workflows/backport.yml
index d27bec04f26f..aee4258f7bb7 100644
--- a/.github/workflows/backport.yml
+++ b/.github/workflows/backport.yml
@@ -14,16 +14,18 @@ permissions: {}
jobs:
backport:
name: Backport Pull Request
- if: github.repository_owner == 'NixOS' && github.event.pull_request.merged == true && (github.event_name != 'labeled' || startsWith('backport', github.event.label.name))
- runs-on: ubuntu-24.04
+ if: vars.NIXPKGS_CI_APP_ID && github.event.pull_request.merged == true && (github.event.action != 'labeled' || startsWith(github.event.label.name, 'backport'))
+ runs-on: ubuntu-24.04-arm
steps:
# Use a GitHub App to create the PR so that CI gets triggered
# The App is scoped to Repository > Contents and Pull Requests: write for Nixpkgs
- - uses: actions/create-github-app-token@af35edadc00be37caa72ed9f3e6d5f7801bfdf09 # v1.11.7
+ - uses: actions/create-github-app-token@df432ceedc7162793a195dd1713ff69aefc7379e # v2.0.6
id: app-token
with:
app-id: ${{ vars.NIXPKGS_CI_APP_ID }}
private-key: ${{ secrets.NIXPKGS_CI_APP_PRIVATE_KEY }}
+ permission-contents: write
+ permission-pull-requests: write
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
with:
@@ -31,6 +33,7 @@ jobs:
token: ${{ steps.app-token.outputs.token }}
- name: Create backport PRs
+ id: backport
uses: korthout/backport-action@436145e922f9561fc5ea157ff406f21af2d6b363 # v3.2.0
with:
# Config README: https://github.com/korthout/backport-action#backport-action
@@ -40,4 +43,16 @@ jobs:
Bot-based backport to `${target_branch}`, triggered by a label in #${pull_number}.
* [ ] Before merging, ensure that this backport is [acceptable for the release](https://github.com/NixOS/nixpkgs/blob/master/CONTRIBUTING.md#changes-acceptable-for-releases).
- * Even as a non-commiter, if you find that it is not acceptable, leave a comment.
+ * Even as a non-committer, if you find that it is not acceptable, leave a comment.
+
+ - name: "Add 'has: port to stable' label"
+ if: steps.backport.outputs.created_pull_numbers != ''
+ env:
+ GH_TOKEN: ${{ steps.app-token.outputs.token }}
+ REPOSITORY: ${{ github.repository }}
+ NUMBER: ${{ github.event.number }}
+ run: |
+ gh api \
+ --method POST \
+ /repos/"$REPOSITORY"/issues/"$NUMBER"/labels \
+ -f "labels[]=8.has: port to stable"
diff --git a/.github/workflows/check-cherry-picks.yml b/.github/workflows/check-cherry-picks.yml
index 1759aa5833b2..70dfdfba1e5f 100644
--- a/.github/workflows/check-cherry-picks.yml
+++ b/.github/workflows/check-cherry-picks.yml
@@ -1,6 +1,9 @@
name: "Check cherry-picks"
on:
+ pull_request:
+ paths:
+ - .github/workflows/check-cherry-picks.yml
pull_request_target:
branches:
- 'release-**'
@@ -12,8 +15,7 @@ permissions: {}
jobs:
check:
name: cherry-pick-check
- runs-on: ubuntu-24.04
- if: github.repository_owner == 'NixOS'
+ runs-on: ubuntu-24.04-arm
steps:
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
with:
diff --git a/.github/workflows/check-format.yml b/.github/workflows/check-format.yml
new file mode 100644
index 000000000000..01312cd8f170
--- /dev/null
+++ b/.github/workflows/check-format.yml
@@ -0,0 +1,44 @@
+name: Check that files are formatted
+
+on:
+ pull_request:
+ paths:
+ - .github/workflows/check-format.yml
+ pull_request_target:
+ types: [opened, synchronize, reopened, edited]
+
+permissions: {}
+
+jobs:
+ get-merge-commit:
+ uses: ./.github/workflows/get-merge-commit.yml
+
+ nixos:
+ name: fmt-check
+ runs-on: ubuntu-24.04-arm
+ needs: get-merge-commit
+ if: needs.get-merge-commit.outputs.mergedSha
+ steps:
+ - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
+ with:
+ ref: ${{ needs.get-merge-commit.outputs.mergedSha }}
+
+ - uses: cachix/install-nix-action@526118121621777ccd86f79b04685a9319637641 # v31
+ with:
+ extra_nix_config: sandbox = true
+
+ - name: Check that files are formatted
+ run: |
+ # Note that it's fine to run this on untrusted code because:
+ # - There's no secrets accessible here
+ # - The build is sandboxed
+ if ! nix-build ci -A fmt.check; then
+ echo "Some files are not properly formatted"
+ echo "Please format them by going to the Nixpkgs root directory and running one of:"
+ echo " nix-shell --run treefmt"
+ echo " nix develop --command treefmt"
+ echo " nix fmt"
+ echo "Make sure your branch is up to date with master; rebase if not."
+ echo "If you're having trouble, please ping @NixOS/nix-formatting"
+ exit 1
+ fi
diff --git a/.github/workflows/check-maintainers-sorted.yml b/.github/workflows/check-maintainers-sorted.yml
deleted file mode 100644
index d69813c62c97..000000000000
--- a/.github/workflows/check-maintainers-sorted.yml
+++ /dev/null
@@ -1,28 +0,0 @@
-name: "Check that maintainer list is sorted"
-
-on:
- pull_request_target:
- paths:
- - 'maintainers/maintainer-list.nix'
-
-permissions: {}
-
-jobs:
- nixos:
- name: maintainer-list-check
- runs-on: ubuntu-24.04
- steps:
- - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
- with:
- ref: refs/pull/${{ github.event.pull_request.number }}/merge
- # Only these directories to perform the check
- sparse-checkout: |
- lib
- maintainers
-
- - uses: cachix/install-nix-action@d1ca217b388ee87b2507a9a93bf01368bde7cec2 # v31
- with:
- extra_nix_config: sandbox = true
-
- - name: Check that maintainer-list.nix is sorted
- run: nix-instantiate --eval maintainers/scripts/check-maintainers-sorted.nix
diff --git a/.github/workflows/check-nix-format.yml b/.github/workflows/check-nix-format.yml
deleted file mode 100644
index 37c32dfcc224..000000000000
--- a/.github/workflows/check-nix-format.yml
+++ /dev/null
@@ -1,44 +0,0 @@
-# NOTE: Formatting with the RFC-style nixfmt command is not yet stable.
-# See https://github.com/NixOS/rfcs/pull/166.
-
-name: Check that Nix files are formatted
-
-on:
- pull_request_target:
- types: [opened, synchronize, reopened, edited]
-
-permissions: {}
-
-jobs:
- get-merge-commit:
- uses: ./.github/workflows/get-merge-commit.yml
-
- nixos:
- name: nixfmt-check
- runs-on: ubuntu-24.04
- needs: get-merge-commit
- if: needs.get-merge-commit.outputs.mergedSha
- steps:
- - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
- with:
- ref: ${{ needs.get-merge-commit.outputs.mergedSha }}
-
- - uses: cachix/install-nix-action@d1ca217b388ee87b2507a9a93bf01368bde7cec2 # v31
- with:
- extra_nix_config: sandbox = true
-
- - name: Check that Nix files are formatted
- run: |
- # Note that it's fine to run this on untrusted code because:
- # - There's no secrets accessible here
- # - The build is sandboxed
- if ! nix-build ci -A fmt.check; then
- echo "Some Nix files are not properly formatted"
- echo "Please format them by going to the Nixpkgs root directory and running one of:"
- echo " nix-shell --run treefmt"
- echo " nix develop --command treefmt"
- echo " nix fmt"
- echo "Make sure your branch is up to date with master; rebase if not."
- echo "If you're having trouble, please ping @NixOS/nix-formatting"
- exit 1
- fi
diff --git a/.github/workflows/check-nixf-tidy.yml b/.github/workflows/check-nixf-tidy.yml
deleted file mode 100644
index 546ae1351fbc..000000000000
--- a/.github/workflows/check-nixf-tidy.yml
+++ /dev/null
@@ -1,132 +0,0 @@
-name: Check changed Nix files with nixf-tidy (experimental)
-
-on:
- pull_request_target:
- types: [opened, synchronize, reopened, edited]
-
-permissions: {}
-
-jobs:
- nixos:
- name: exp-nixf-tidy-check
- runs-on: ubuntu-24.04
- if: "!contains(github.event.pull_request.title, '[skip treewide]')"
- steps:
- - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
- with:
- ref: refs/pull/${{ github.event.pull_request.number }}/merge
- # Fetches the merge commit and its parents
- fetch-depth: 2
-
- - name: Checking out target branch
- run: |
- target=$(mktemp -d)
- targetRev=$(git rev-parse HEAD^1)
- git worktree add "$target" "$targetRev"
- echo "targetRev=$targetRev" >> "$GITHUB_ENV"
- echo "target=$target" >> "$GITHUB_ENV"
-
- - name: Get Nixpkgs revision for nixf
- run: |
- # pin to a commit from nixpkgs-unstable to avoid e.g. building nixf
- # from staging
- # This should not be a URL, because it would allow PRs to run arbitrary code in CI!
- rev=$(jq -r .rev ci/pinned-nixpkgs.json)
- echo "url=https://github.com/NixOS/nixpkgs/archive/$rev.tar.gz" >> "$GITHUB_ENV"
-
- - uses: cachix/install-nix-action@d1ca217b388ee87b2507a9a93bf01368bde7cec2 # v31
- with:
- extra_nix_config: sandbox = true
- nix_path: nixpkgs=${{ env.url }}
-
- - name: Install nixf and jq
- # provided jq is incompatible with our expression
- run: "nix-env -f '' -iAP nixf jq"
-
- - name: Check that Nix files pass nixf-tidy
- run: |
- # Filtering error messages we don't like
- nixf_wrapper(){
- nixf-tidy --variable-lookup < "$1" | jq -r '
- [
- "sema-escaping-with"
- ]
- as $ignored_errors|[.[]|select(.sname as $s|$ignored_errors|index($s)|not)]
- '
- }
-
- failedFiles=()
-
- # Don't report errors to file overview
- # to avoid duplicates when editing title and description
- if [[ "${{ github.event.action }}" == 'edited' ]] && [[ -z "${{ github.event.edited.changes.base }}" ]]; then
- DONT_REPORT_ERROR=1
- else
- DONT_REPORT_ERROR=
- fi
- # TODO: Make this more parallel
-
- # Loop through all Nix files touched by the PR
- while readarray -d '' -n 2 entry && (( ${#entry[@]} != 0 )); do
- type=${entry[0]}
- file=${entry[1]}
- case $type in
- A*)
- source=""
- dest=$file
- ;;
- M*)
- source=$file
- dest=$file
- ;;
- C*|R*)
- source=$file
- read -r -d '' dest
- ;;
- *)
- echo "Ignoring file $file with type $type"
- continue
- esac
-
- if [[ -n "$source" ]] && [[ "$(nixf_wrapper ${{ env.target }}/"$source")" != '[]' ]] 2>/dev/null; then
- echo "Ignoring file $file because it doesn't pass nixf-tidy in the target commit"
- echo # insert blank line
- else
- nixf_report="$(nixf_wrapper "$dest")"
- if [[ "$nixf_report" != '[]' ]]; then
- echo "$dest doesn't pass nixf-tidy. Reported by nixf-tidy:"
- errors=$(echo "$nixf_report" | jq -r --arg dest "$dest" '
- def getLCur: "line=" + (.line+1|tostring) + ",col=" + (.column|tostring);
- def getRCur: "endLine=" + (.line+1|tostring) + ",endColumn=" + (.column|tostring);
- def getRange: "file=\($dest)," + (.lCur|getLCur) + "," + (.rCur|getRCur);
- def getBody: . as $top|(.range|getRange) + ",title="+ .sname + "::" +
- (.message|sub("{}" ; ($top.args.[]|tostring)));
- def getNote: "\n::notice " + (.|getBody);
- def getMessage: "::error " + (.|getBody) + (if (.notes|length)>0 then
- ([.notes.[]|getNote]|add) else "" end);
- .[]|getMessage
- ')
- if [[ -z "$DONT_REPORT_ERROR" ]]; then
- echo "$errors"
- else
- # just print in plain text
- echo "${errors/::/}"
- echo # add one empty line
- fi
- failedFiles+=("$dest")
- fi
- fi
- done < <(git diff -z --name-status ${{ env.targetRev }} -- '*.nix')
-
- if [[ -n "$DONT_REPORT_ERROR" ]]; then
- echo "Edited the PR but didn't change the base branch, only the description/title."
- echo "Not reporting errors again to avoid duplication."
- echo # add one empty line
- fi
-
- if (( "${#failedFiles[@]}" > 0 )); then
- echo "Some new/changed Nix files don't pass nixf-tidy."
- echo "See ${{ github.event.pull_request.html_url }}/files for reported errors."
- echo "If you believe this is a false positive, ping @Aleksanaa and @inclyc in this PR."
- exit 1
- fi
diff --git a/.github/workflows/check-shell.yml b/.github/workflows/check-shell.yml
index 2ba1ec405d71..c26bea7761bc 100644
--- a/.github/workflows/check-shell.yml
+++ b/.github/workflows/check-shell.yml
@@ -1,6 +1,9 @@
name: "Check shell"
on:
+ pull_request:
+ paths:
+ - .github/workflows/check-shell.yml
pull_request_target:
paths:
- 'shell.nix'
@@ -16,6 +19,10 @@ jobs:
include:
- runner: ubuntu-24.04
system: x86_64-linux
+ - runner: ubuntu-24.04-arm
+ system: aarch64-linux
+ - runner: macos-13
+ system: x86_64-darwin
- runner: macos-14
system: aarch64-darwin
@@ -27,7 +34,7 @@ jobs:
with:
ref: refs/pull/${{ github.event.pull_request.number }}/merge
- - uses: cachix/install-nix-action@d1ca217b388ee87b2507a9a93bf01368bde7cec2 # v31
+ - uses: cachix/install-nix-action@526118121621777ccd86f79b04685a9319637641 # v31
- name: Build shell
- run: nix-build shell.nix
+ run: nix-build ci -A shell
diff --git a/.github/workflows/codeowners-v2.yml b/.github/workflows/codeowners-v2.yml
index cb9a090bf73a..28b0ef43d5ac 100644
--- a/.github/workflows/codeowners-v2.yml
+++ b/.github/workflows/codeowners-v2.yml
@@ -23,6 +23,9 @@
name: Codeowners v2
on:
+ pull_request:
+ paths:
+ - .github/workflows/codeowners-v2.yml
pull_request_target:
types: [opened, ready_for_review, synchronize, reopened, edited]
@@ -41,11 +44,11 @@ jobs:
# Check that code owners is valid
check:
name: Check
- runs-on: ubuntu-24.04
+ runs-on: ubuntu-24.04-arm
needs: get-merge-commit
if: github.repository_owner == 'NixOS' && needs.get-merge-commit.outputs.mergedSha
steps:
- - uses: cachix/install-nix-action@d1ca217b388ee87b2507a9a93bf01368bde7cec2 # v31
+ - uses: cachix/install-nix-action@526118121621777ccd86f79b04685a9319637641 # v31
- uses: cachix/cachix-action@0fc020193b5a1fa3ac4575aa3a7d3aa6a35435ad # v16
with:
@@ -63,11 +66,14 @@ jobs:
- name: Build codeowners validator
run: nix-build base/ci -A codeownersValidator
- - uses: actions/create-github-app-token@af35edadc00be37caa72ed9f3e6d5f7801bfdf09 # v1.11.7
+ - uses: actions/create-github-app-token@df432ceedc7162793a195dd1713ff69aefc7379e # v2.0.6
+ if: vars.OWNER_RO_APP_ID
id: app-token
with:
app-id: ${{ vars.OWNER_RO_APP_ID }}
private-key: ${{ secrets.OWNER_RO_APP_PRIVATE_KEY }}
+ permission-administration: read
+ permission-members: read
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
with:
@@ -75,6 +81,7 @@ jobs:
path: pr
- name: Validate codeowners
+ if: steps.app-token.outputs.token
run: result/bin/codeowners-validator
env:
OWNERS_FILE: pr/${{ env.OWNERS_FILE }}
@@ -87,25 +94,30 @@ jobs:
# Request reviews from code owners
request:
name: Request
- runs-on: ubuntu-24.04
+ runs-on: ubuntu-24.04-arm
if: github.repository_owner == 'NixOS'
steps:
- - uses: cachix/install-nix-action@d1ca217b388ee87b2507a9a93bf01368bde7cec2 # v31
+ - uses: cachix/install-nix-action@526118121621777ccd86f79b04685a9319637641 # v31
# Important: Because we use pull_request_target, this checks out the base branch of the PR, not the PR head.
# This is intentional, because we need to request the review of owners as declared in the base branch.
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
- - uses: actions/create-github-app-token@af35edadc00be37caa72ed9f3e6d5f7801bfdf09 # v1.11.7
+ - uses: actions/create-github-app-token@df432ceedc7162793a195dd1713ff69aefc7379e # v2.0.6
+ if: vars.OWNER_APP_ID
id: app-token
with:
app-id: ${{ vars.OWNER_APP_ID }}
private-key: ${{ secrets.OWNER_APP_PRIVATE_KEY }}
+ permission-administration: read
+ permission-members: read
+ permission-pull-requests: write
- name: Build review request package
run: nix-build ci -A requestReviews
- name: Request reviews
+ if: steps.app-token.outputs.token
run: result/bin/request-code-owner-reviews.sh ${{ github.repository }} ${{ github.event.number }} "$OWNERS_FILE"
env:
GH_TOKEN: ${{ steps.app-token.outputs.token }}
diff --git a/.github/workflows/editorconfig-v2.yml b/.github/workflows/editorconfig-v2.yml
deleted file mode 100644
index 9046b25032cd..000000000000
--- a/.github/workflows/editorconfig-v2.yml
+++ /dev/null
@@ -1,47 +0,0 @@
-name: "Checking EditorConfig v2"
-
-on:
- pull_request_target:
-
-permissions: {}
-
-jobs:
- get-merge-commit:
- uses: ./.github/workflows/get-merge-commit.yml
-
- tests:
- name: editorconfig-check
- runs-on: ubuntu-24.04
- needs: get-merge-commit
- if: "needs.get-merge-commit.outputs.mergedSha && !contains(github.event.pull_request.title, '[skip treewide]')"
- steps:
- - name: Get list of changed files from PR
- env:
- GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- run: |
- gh api \
- repos/${{ github.repository }}/pulls/${{ github.event.number }}/files --paginate \
- | jq '.[] | select(.status != "removed") | .filename' \
- > "$HOME/changed_files"
-
- - name: print list of changed files
- run: |
- cat "$HOME/changed_files"
-
- - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
- with:
- ref: ${{ needs.get-merge-commit.outputs.mergedSha }}
-
- - uses: cachix/install-nix-action@d1ca217b388ee87b2507a9a93bf01368bde7cec2 # v31
- with:
- # nixpkgs commit is pinned so that it doesn't break
- # editorconfig-checker 2.4.0
- nix_path: nixpkgs=https://github.com/NixOS/nixpkgs/archive/c473cc8714710179df205b153f4e9fa007107ff9.tar.gz
-
- - name: Checking EditorConfig
- run: |
- < "$HOME/changed_files" nix-shell -p editorconfig-checker --run 'xargs -r editorconfig-checker -disable-indent-size'
-
- - if: ${{ failure() }}
- run: |
- echo "::error :: Hey! It looks like your changes don't follow our editorconfig settings. Read https://editorconfig.org/#download to configure your editor so you never see this error again."
diff --git a/.github/workflows/eval-aliases.yml b/.github/workflows/eval-aliases.yml
new file mode 100644
index 000000000000..7f9b658081f1
--- /dev/null
+++ b/.github/workflows/eval-aliases.yml
@@ -0,0 +1,36 @@
+name: Eval aliases
+
+on:
+ pull_request:
+ paths:
+ - .github/workflows/eval-aliases.yml
+ pull_request_target:
+
+permissions: {}
+
+jobs:
+ get-merge-commit:
+ uses: ./.github/workflows/get-merge-commit.yml
+
+ eval-aliases:
+ name: Eval nixpkgs with aliases enabled
+ runs-on: ubuntu-24.04-arm
+ needs: [ get-merge-commit ]
+ steps:
+ - name: Check out the PR at the test merge commit
+ uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
+ with:
+ ref: ${{ needs.get-merge-commit.outputs.mergedSha }}
+ path: nixpkgs
+
+ - name: Install Nix
+ uses: cachix/install-nix-action@526118121621777ccd86f79b04685a9319637641 # v31
+ with:
+ extra_nix_config: sandbox = true
+
+ - name: Ensure flake outputs on all systems still evaluate
+ run: nix flake check --all-systems --no-build ./nixpkgs
+
+ - name: Query nixpkgs with aliases enabled to check for basic syntax errors
+ run: |
+ time nix-env -I ./nixpkgs -f ./nixpkgs -qa '*' --option restrict-eval true --option allow-import-from-derivation false >/dev/null
diff --git a/.github/workflows/eval-lib-tests.yml b/.github/workflows/eval-lib-tests.yml
deleted file mode 100644
index 47bfa61d4c9c..000000000000
--- a/.github/workflows/eval-lib-tests.yml
+++ /dev/null
@@ -1,31 +0,0 @@
-name: "Building Nixpkgs lib-tests"
-
-on:
- pull_request_target:
- paths:
- - 'lib/**'
- - 'maintainers/**'
-
-permissions: {}
-
-jobs:
- get-merge-commit:
- uses: ./.github/workflows/get-merge-commit.yml
-
- nixpkgs-lib-tests:
- name: nixpkgs-lib-tests
- runs-on: ubuntu-24.04
- needs: get-merge-commit
- if: needs.get-merge-commit.outputs.mergedSha
- steps:
- - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
- with:
- ref: ${{ needs.get-merge-commit.outputs.mergedSha }}
-
- - uses: cachix/install-nix-action@d1ca217b388ee87b2507a9a93bf01368bde7cec2 # v31
- with:
- extra_nix_config: sandbox = true
-
- - name: Building Nixpkgs lib-tests
- run: |
- nix-build --arg pkgs "(import ./ci/. {}).pkgs" ./lib/tests/release.nix
diff --git a/.github/workflows/eval.yml b/.github/workflows/eval.yml
index 82781f96c736..ecb763043425 100644
--- a/.github/workflows/eval.yml
+++ b/.github/workflows/eval.yml
@@ -1,6 +1,9 @@
name: Eval
on:
+ pull_request:
+ paths:
+ - .github/workflows/eval.yml
pull_request_target:
types: [opened, ready_for_review, synchronize, reopened]
push:
@@ -19,87 +22,21 @@ jobs:
get-merge-commit:
uses: ./.github/workflows/get-merge-commit.yml
- attrs:
- name: Attributes
- runs-on: ubuntu-24.04
- needs: get-merge-commit
- if: needs.get-merge-commit.outputs.mergedSha
- outputs:
- targetSha: ${{ steps.targetSha.outputs.targetSha }}
- systems: ${{ steps.systems.outputs.systems }}
- steps:
- - name: Check out the PR at the test merge commit
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
- with:
- ref: ${{ needs.get-merge-commit.outputs.mergedSha }}
- fetch-depth: 2
- path: nixpkgs
-
- - name: Determine target commit
- if: github.event_name == 'pull_request_target'
- id: targetSha
- run: |
- targetSha=$(git -C nixpkgs rev-parse HEAD^1)
- echo "targetSha=$targetSha" >> "$GITHUB_OUTPUT"
-
- - name: Install Nix
- uses: cachix/install-nix-action@d1ca217b388ee87b2507a9a93bf01368bde7cec2 # v31
- with:
- extra_nix_config: sandbox = true
-
- - name: Evaluate the list of all attributes and get the systems matrix
- id: systems
- run: |
- nix-build nixpkgs/ci -A eval.attrpathsSuperset
- echo "systems=$(> "$GITHUB_OUTPUT"
-
- - name: Upload the list of all attributes
- uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2
- with:
- name: paths
- path: result/*
-
- eval-aliases:
- name: Eval nixpkgs with aliases enabled
- runs-on: ubuntu-24.04
- needs: [ get-merge-commit ]
- steps:
- - name: Check out the PR at the test merge commit
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
- with:
- ref: ${{ needs.get-merge-commit.outputs.mergedSha }}
- path: nixpkgs
-
- - name: Install Nix
- uses: cachix/install-nix-action@d1ca217b388ee87b2507a9a93bf01368bde7cec2 # v31
- with:
- extra_nix_config: sandbox = true
-
- - name: Query nixpkgs with aliases enabled to check for basic syntax errors
- run: |
- time nix-env -I ./nixpkgs -f ./nixpkgs -qa '*' --option restrict-eval true --option allow-import-from-derivation false >/dev/null
-
outpaths:
name: Outpaths
- runs-on: ubuntu-24.04
- needs: [ attrs, get-merge-commit ]
+ runs-on: ubuntu-24.04-arm
+ needs: [ get-merge-commit ]
strategy:
fail-fast: false
matrix:
- system: ${{ fromJSON(needs.attrs.outputs.systems) }}
+ system: ${{ fromJSON(needs.get-merge-commit.outputs.systems) }}
steps:
- name: Enable swap
run: |
- sudo fallocate -l 10G /swapfile
- sudo chmod 600 /swapfile
- sudo mkswap /swapfile
- sudo swapon /swapfile
-
- - name: Download the list of all attributes
- uses: actions/download-artifact@fa0a91b85d4f404e444e00e005971372dc801d16 # v4.1.8
- with:
- name: paths
- path: paths
+ sudo fallocate -l 10G /swap
+ sudo chmod 600 /swap
+ sudo mkswap /swap
+ sudo swapon /swap
- name: Check out the PR at the test merge commit
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
@@ -108,7 +45,7 @@ jobs:
path: nixpkgs
- name: Install Nix
- uses: cachix/install-nix-action@d1ca217b388ee87b2507a9a93bf01368bde7cec2 # v31
+ uses: cachix/install-nix-action@526118121621777ccd86f79b04685a9319637641 # v31
with:
extra_nix_config: sandbox = true
@@ -118,7 +55,6 @@ jobs:
run: |
nix-build nixpkgs/ci -A eval.singleSystem \
--argstr evalSystem "$MATRIX_SYSTEM" \
- --arg attrpathFile ./paths/paths.json \
--arg chunkSize 10000
# If it uses too much memory, slightly decrease chunkSize
@@ -130,8 +66,8 @@ jobs:
process:
name: Process
- runs-on: ubuntu-24.04
- needs: [ outpaths, attrs, get-merge-commit ]
+ runs-on: ubuntu-24.04-arm
+ needs: [ outpaths, get-merge-commit ]
outputs:
targetRunId: ${{ steps.targetRunId.outputs.targetRunId }}
steps:
@@ -149,7 +85,7 @@ jobs:
path: nixpkgs
- name: Install Nix
- uses: cachix/install-nix-action@d1ca217b388ee87b2507a9a93bf01368bde7cec2 # v31
+ uses: cachix/install-nix-action@526118121621777ccd86f79b04685a9319637641 # v31
with:
extra_nix_config: sandbox = true
@@ -166,7 +102,7 @@ jobs:
path: prResult/*
- name: Get target run id
- if: needs.attrs.outputs.targetSha
+ if: needs.get-merge-commit.outputs.targetSha
id: targetRunId
run: |
# Get the latest eval.yml workflow run for the PR's target commit
@@ -195,7 +131,7 @@ jobs:
echo "targetRunId=$runId" >> "$GITHUB_OUTPUT"
env:
REPOSITORY: ${{ github.repository }}
- TARGET_SHA: ${{ needs.attrs.outputs.targetSha }}
+ TARGET_SHA: ${{ needs.get-merge-commit.outputs.targetSha }}
GH_TOKEN: ${{ github.token }}
- uses: actions/download-artifact@v4
@@ -209,18 +145,21 @@ jobs:
- name: Compare against the target branch
if: steps.targetRunId.outputs.targetRunId
run: |
- git -C nixpkgs worktree add ../target ${{ needs.attrs.outputs.targetSha }}
- git -C nixpkgs diff --name-only ${{ needs.attrs.outputs.targetSha }} \
+ git -C nixpkgs worktree add ../target ${{ needs.get-merge-commit.outputs.targetSha }}
+ git -C nixpkgs diff --name-only ${{ needs.get-merge-commit.outputs.targetSha }} \
| jq --raw-input --slurp 'split("\n")[:-1]' > touched-files.json
# Use the target branch to get accurate maintainer info
nix-build target/ci -A eval.compare \
--arg beforeResultDir ./targetResult \
- --arg afterResultDir ./prResult \
+ --arg afterResultDir "$(realpath prResult)" \
--arg touchedFilesJson ./touched-files.json \
+ --argstr githubAuthorId "$AUTHOR_ID" \
-o comparison
cat comparison/step-summary.md >> "$GITHUB_STEP_SUMMARY"
+ env:
+ AUTHOR_ID: ${{ github.event.pull_request.user.id }}
- name: Upload the combined results
if: steps.targetRunId.outputs.targetRunId
@@ -232,8 +171,8 @@ jobs:
# Separate job to have a very tightly scoped PR write token
tag:
name: Tag
- runs-on: ubuntu-24.04
- needs: [ attrs, process ]
+ runs-on: ubuntu-24.04-arm
+ needs: [ get-merge-commit, process ]
if: needs.process.outputs.targetRunId
permissions:
pull-requests: write
@@ -241,11 +180,15 @@ jobs:
steps:
# See ./codeowners-v2.yml, reuse the same App because we need the same permissions
# Can't use the token received from permissions above, because it can't get enough permissions
- - uses: actions/create-github-app-token@af35edadc00be37caa72ed9f3e6d5f7801bfdf09 # v1.11.7
+ - uses: actions/create-github-app-token@df432ceedc7162793a195dd1713ff69aefc7379e # v2.0.6
+ if: vars.OWNER_APP_ID
id: app-token
with:
app-id: ${{ vars.OWNER_APP_ID }}
private-key: ${{ secrets.OWNER_APP_PRIVATE_KEY }}
+ permission-administration: read
+ permission-members: read
+ permission-pull-requests: write
- name: Download process result
uses: actions/download-artifact@fa0a91b85d4f404e444e00e005971372dc801d16 # v4.1.8
@@ -254,14 +197,14 @@ jobs:
path: comparison
- name: Install Nix
- uses: cachix/install-nix-action@d1ca217b388ee87b2507a9a93bf01368bde7cec2 # v31
+ uses: cachix/install-nix-action@526118121621777ccd86f79b04685a9319637641 # v31
# Important: This workflow job runs with extra permissions,
# so we need to make sure to not run untrusted code from PRs
- name: Check out Nixpkgs at the base commit
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
with:
- ref: ${{ needs.attrs.outputs.targetSha }}
+ ref: ${{ needs.get-merge-commit.outputs.targetSha }}
path: base
sparse-checkout: ci
@@ -269,11 +212,12 @@ jobs:
run: nix-build base/ci -A requestReviews
- name: Labelling pull request
+ if: ${{ github.event_name == 'pull_request_target' && github.repository_owner == 'NixOS' }}
run: |
- # Get all currently set rebuild labels
+ # Get all currently set labels that we manage
gh api \
/repos/"$REPOSITORY"/issues/"$NUMBER"/labels \
- --jq '.[].name | select(startswith("10.rebuild"))' \
+ --jq '.[].name | select(startswith("10.rebuild") or . == "11.by: package-maintainer")' \
| sort > before
# And the labels that should be there
@@ -303,7 +247,7 @@ jobs:
NUMBER: ${{ github.event.number }}
- name: Add eval summary to commit statuses
- if: ${{ github.event_name == 'pull_request_target' }}
+ if: ${{ github.event_name == 'pull_request_target' && github.repository_owner == 'NixOS' }}
run: |
description=$(jq -r '
"Package: added " + (.attrdiff.added | length | tostring) +
@@ -323,6 +267,7 @@ jobs:
NUMBER: ${{ github.event.number }}
- name: Requesting maintainer reviews
+ if: ${{ steps.app-token.outputs.token && github.repository_owner == 'NixOS' }}
run: |
# maintainers.json contains GitHub IDs. Look up handles to request reviews from.
# There appears to be no API to request reviews based on GitHub IDs
diff --git a/.github/workflows/get-merge-commit.yml b/.github/workflows/get-merge-commit.yml
index a32595ae1ad4..edbda3e040eb 100644
--- a/.github/workflows/get-merge-commit.yml
+++ b/.github/workflows/get-merge-commit.yml
@@ -1,19 +1,30 @@
name: Get merge commit
on:
+ pull_request:
+ paths:
+ - .github/workflows/get-merge-commit.yml
workflow_call:
outputs:
mergedSha:
description: "The merge commit SHA"
value: ${{ jobs.resolve-merge-commit.outputs.mergedSha }}
+ targetSha:
+ description: "The target commit SHA"
+ value: ${{ jobs.resolve-merge-commit.outputs.targetSha }}
+ systems:
+ description: "The supported systems"
+ value: ${{ jobs.resolve-merge-commit.outputs.systems }}
permissions: {}
jobs:
resolve-merge-commit:
- runs-on: ubuntu-24.04
+ runs-on: ubuntu-24.04-arm
outputs:
mergedSha: ${{ steps.merged.outputs.mergedSha }}
+ targetSha: ${{ steps.merged.outputs.targetSha }}
+ systems: ${{ steps.systems.outputs.systems }}
steps:
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
with:
@@ -30,14 +41,18 @@ jobs:
push)
echo "mergedSha=${{ github.sha }}" >> "$GITHUB_OUTPUT"
;;
- pull_request_target)
- if mergedSha=$(base/ci/get-merge-commit.sh ${{ github.repository }} ${{ github.event.number }}); then
- echo "Checking the merge commit $mergedSha"
- echo "mergedSha=$mergedSha" >> "$GITHUB_OUTPUT"
+ pull_request*)
+ if commits=$(base/ci/get-merge-commit.sh ${{ github.repository }} ${{ github.event.number }}); then
+ echo -e "Checking the commits:\n$commits"
+ echo "$commits" >> "$GITHUB_OUTPUT"
else
# Skipping so that no notifications are sent
echo "Skipping the rest..."
fi
;;
esac
- rm -rf base
+
+ - name: Load supported systems
+ id: systems
+ run: |
+ echo "systems=$(jq -c > "$GITHUB_OUTPUT"
diff --git a/.github/workflows/keep-sorted.yml b/.github/workflows/keep-sorted.yml
deleted file mode 100644
index 2433c7b57264..000000000000
--- a/.github/workflows/keep-sorted.yml
+++ /dev/null
@@ -1,40 +0,0 @@
-name: Check that files are sorted
-
-on:
- pull_request_target:
- types: [opened, synchronize, reopened]
-
-permissions: {}
-
-jobs:
- get-merge-commit:
- uses: ./.github/workflows/get-merge-commit.yml
-
- nixos:
- name: keep-sorted
- runs-on: ubuntu-24.04
- needs: get-merge-commit
- if: "needs.get-merge-commit.outputs.mergedSha && !contains(github.event.pull_request.title, '[skip treewide]')"
- steps:
- - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
- with:
- ref: ${{ needs.get-merge-commit.outputs.mergedSha }}
-
- - name: Get Nixpkgs revision for keep-sorted
- run: |
- # Pin to a commit from nixpkgs-unstable to avoid e.g. building nixfmt from staging.
- # This should not be a URL, because it would allow PRs to run arbitrary code in CI!
- rev=$(jq -r .rev ci/pinned-nixpkgs.json)
- echo "url=https://github.com/NixOS/nixpkgs/archive/$rev.tar.gz" >> "$GITHUB_ENV"
-
- - uses: cachix/install-nix-action@d1ca217b388ee87b2507a9a93bf01368bde7cec2 # v31
- with:
- extra_nix_config: sandbox = true
- nix_path: nixpkgs=${{ env.url }}
-
- - name: Install keep-sorted
- run: "nix-env -f '' -iAP keep-sorted jq"
-
- - name: Check that Nix files are sorted
- run: |
- git ls-files | xargs keep-sorted --mode lint | jq --raw-output '.[] | "Please make sure any new entries in \(.path) are sorted alphabetically."'
diff --git a/.github/workflows/labels.yml b/.github/workflows/labels.yml
index 0ae4ee09d5ca..d183985f0708 100644
--- a/.github/workflows/labels.yml
+++ b/.github/workflows/labels.yml
@@ -16,16 +16,45 @@ permissions:
jobs:
labels:
name: label-pr
- runs-on: ubuntu-24.04
+ runs-on: ubuntu-24.04-arm
if: "github.repository_owner == 'NixOS' && !contains(github.event.pull_request.title, '[skip treewide]')"
steps:
- uses: actions/labeler@8558fd74291d67161a8a78ce36a881fa63b766a9 # v5.0.0
+ if: |
+ github.event.pull_request.head.repo.owner.login != 'NixOS' || !(
+ github.head_ref == 'haskell-updates' ||
+ github.head_ref == 'python-updates' ||
+ github.head_ref == 'staging-next' ||
+ startsWith(github.head_ref, 'staging-next-')
+ )
with:
repo-token: ${{ secrets.GITHUB_TOKEN }}
configuration-path: .github/labeler.yml # default
sync-labels: true
- uses: actions/labeler@8558fd74291d67161a8a78ce36a881fa63b766a9 # v5.0.0
+ if: |
+ github.event.pull_request.head.repo.owner.login != 'NixOS' || !(
+ github.head_ref == 'haskell-updates' ||
+ github.head_ref == 'python-updates' ||
+ github.head_ref == 'staging-next' ||
+ startsWith(github.head_ref, 'staging-next-')
+ )
with:
repo-token: ${{ secrets.GITHUB_TOKEN }}
configuration-path: .github/labeler-no-sync.yml
sync-labels: false
+ - uses: actions/labeler@8558fd74291d67161a8a78ce36a881fa63b766a9 # v5.0.0
+ # Development branches like staging-next, haskell-updates and python-updates get special labels.
+ # This is to avoid the mass of labels there, which is mostly useless - and really annoying for
+ # the backport labels.
+ if: |
+ github.event.pull_request.head.repo.owner.login == 'NixOS' && (
+ github.head_ref == 'haskell-updates' ||
+ github.head_ref == 'python-updates' ||
+ github.head_ref == 'staging-next' ||
+ startsWith(github.head_ref, 'staging-next-')
+ )
+ with:
+ repo-token: ${{ secrets.GITHUB_TOKEN }}
+ configuration-path: .github/labeler-development-branches.yml
+ sync-labels: true
diff --git a/.github/workflows/lib-tests.yml b/.github/workflows/lib-tests.yml
new file mode 100644
index 000000000000..d55a109aa9f5
--- /dev/null
+++ b/.github/workflows/lib-tests.yml
@@ -0,0 +1,34 @@
+name: "Building Nixpkgs lib-tests"
+
+on:
+ pull_request:
+ paths:
+ - .github/workflows/lib-tests.yml
+ pull_request_target:
+ paths:
+ - 'lib/**'
+ - 'maintainers/**'
+
+permissions: {}
+
+jobs:
+ get-merge-commit:
+ uses: ./.github/workflows/get-merge-commit.yml
+
+ nixpkgs-lib-tests:
+ name: nixpkgs-lib-tests
+ runs-on: ubuntu-24.04
+ needs: get-merge-commit
+ if: needs.get-merge-commit.outputs.mergedSha
+ steps:
+ - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
+ with:
+ ref: ${{ needs.get-merge-commit.outputs.mergedSha }}
+
+ - uses: cachix/install-nix-action@526118121621777ccd86f79b04685a9319637641 # v31
+ with:
+ extra_nix_config: sandbox = true
+
+ - name: Building Nixpkgs lib-tests
+ run: |
+ nix-build ci -A lib-tests
diff --git a/.github/workflows/lint-actions.sh b/.github/workflows/lint-actions.sh
deleted file mode 100755
index 43d6e801caf6..000000000000
--- a/.github/workflows/lint-actions.sh
+++ /dev/null
@@ -1,7 +0,0 @@
-#!/usr/bin/env nix-shell
-#!nix-shell -i bash -p bash actionlint shellcheck -I nixpkgs=../..
-set -euo pipefail
-
-SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" &> /dev/null && pwd )"
-cd "$SCRIPT_DIR/../.."
-actionlint
diff --git a/.github/workflows/manual-nixos-v2.yml b/.github/workflows/manual-nixos-v2.yml
index 2369f3b251f3..a8eadf63057e 100644
--- a/.github/workflows/manual-nixos-v2.yml
+++ b/.github/workflows/manual-nixos-v2.yml
@@ -1,6 +1,9 @@
name: "Build NixOS manual v2"
on:
+ pull_request:
+ paths:
+ - .github/workflows/manual-nixos-v2.yml
pull_request_target:
branches:
- master
@@ -22,23 +25,22 @@ jobs:
strategy:
fail-fast: false
matrix:
- system:
- - x86_64-linux
- - aarch64-linux
- runs-on: >-
- ${{ (matrix.system == 'x86_64-linux' && 'ubuntu-24.04')
- || (matrix.system == 'aarch64-linux' && 'ubuntu-24.04-arm') }}
+ include:
+ - runner: ubuntu-24.04
+ system: x86_64-linux
+ - runner: ubuntu-24.04-arm
+ system: aarch64-linux
+ runs-on: ${{ matrix.runner }}
steps:
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
with:
ref: refs/pull/${{ github.event.pull_request.number }}/merge
- - uses: cachix/install-nix-action@d1ca217b388ee87b2507a9a93bf01368bde7cec2 # v31
+ - uses: cachix/install-nix-action@526118121621777ccd86f79b04685a9319637641 # v31
with:
extra_nix_config: sandbox = true
- uses: cachix/cachix-action@0fc020193b5a1fa3ac4575aa3a7d3aa6a35435ad # v16
- if: github.repository_owner == 'NixOS'
with:
# This cache is for the nixpkgs repo checks and should not be trusted or used elsewhere.
name: nixpkgs-ci
@@ -46,7 +48,7 @@ jobs:
- name: Build NixOS manual
id: build-manual
- run: NIX_PATH=nixpkgs=$(pwd) nix-build --option restrict-eval true nixos/release.nix -A manual.${{ matrix.system }}
+ run: NIX_PATH=nixpkgs=$(pwd) nix-build --option restrict-eval true ci -A manual-nixos --argstr system ${{ matrix.system }}
- name: Upload NixOS manual
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2
diff --git a/.github/workflows/manual-nixpkgs-v2.yml b/.github/workflows/manual-nixpkgs-v2.yml
index 874f6c3ef58e..f9a5f8aa30b1 100644
--- a/.github/workflows/manual-nixpkgs-v2.yml
+++ b/.github/workflows/manual-nixpkgs-v2.yml
@@ -1,35 +1,37 @@
name: "Build Nixpkgs manual v2"
on:
+ pull_request:
+ paths:
+ - .github/workflows/manual-nixpkgs-v2.yml
pull_request_target:
branches:
- master
paths:
- 'doc/**'
- 'lib/**'
- - 'pkgs/tools/nix/nixdoc/**'
+ - 'pkgs/by-name/ni/nixdoc/**'
permissions: {}
jobs:
nixpkgs:
name: nixpkgs-manual-build
- runs-on: ubuntu-24.04
+ runs-on: ubuntu-24.04-arm
steps:
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
with:
ref: refs/pull/${{ github.event.pull_request.number }}/merge
- - uses: cachix/install-nix-action@d1ca217b388ee87b2507a9a93bf01368bde7cec2 # v31
+ - uses: cachix/install-nix-action@526118121621777ccd86f79b04685a9319637641 # v31
with:
extra_nix_config: sandbox = true
- uses: cachix/cachix-action@0fc020193b5a1fa3ac4575aa3a7d3aa6a35435ad # v16
- if: github.repository_owner == 'NixOS'
with:
# This cache is for the nixpkgs repo checks and should not be trusted or used elsewhere.
name: nixpkgs-ci
authToken: '${{ secrets.CACHIX_AUTH_TOKEN }}'
- name: Building Nixpkgs manual
- run: NIX_PATH=nixpkgs=$(pwd) nix-build --option restrict-eval true pkgs/top-level/release.nix -A manual -A manual.tests
+ run: NIX_PATH=nixpkgs=$(pwd) nix-build --option restrict-eval true ci -A manual-nixpkgs -A manual-nixpkgs-tests
diff --git a/.github/workflows/nix-parse-v2.yml b/.github/workflows/nix-parse-v2.yml
index 497bba08ae97..4b670331308e 100644
--- a/.github/workflows/nix-parse-v2.yml
+++ b/.github/workflows/nix-parse-v2.yml
@@ -1,6 +1,9 @@
name: "Check whether nix files are parseable v2"
on:
+ pull_request:
+ paths:
+ - .github/workflows/nix-parse-v2.yml
pull_request_target:
permissions: {}
@@ -11,37 +14,20 @@ jobs:
tests:
name: nix-files-parseable-check
- runs-on: ubuntu-24.04
+ runs-on: ubuntu-24.04-arm
needs: get-merge-commit
if: "needs.get-merge-commit.outputs.mergedSha && !contains(github.event.pull_request.title, '[skip treewide]')"
steps:
- - name: Get list of changed files from PR
- env:
- GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- run: |
- gh api \
- repos/${{ github.repository }}/pulls/${{github.event.number}}/files --paginate \
- | jq --raw-output '.[] | select(.status != "removed" and (.filename | endswith(".nix"))) | .filename' \
- > "$HOME/changed_files"
- if [[ -s "$HOME/changed_files" ]]; then
- echo "CHANGED_FILES=$HOME/changed_files" > "$GITHUB_ENV"
- fi
-
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
with:
ref: ${{ needs.get-merge-commit.outputs.mergedSha }}
- if: ${{ env.CHANGED_FILES && env.CHANGED_FILES != '' }}
- - uses: cachix/install-nix-action@d1ca217b388ee87b2507a9a93bf01368bde7cec2 # v31
+ - uses: cachix/install-nix-action@526118121621777ccd86f79b04685a9319637641 # v31
with:
extra_nix_config: sandbox = true
nix_path: nixpkgs=channel:nixpkgs-unstable
- - name: Parse all changed or added nix files
+ - name: Parse all nix files
run: |
- ret=0
- while IFS= read -r file; do
- out="$(nix-instantiate --parse "$file")" || { echo "$out" && ret=1; }
- done < "$HOME/changed_files"
- exit "$ret"
- if: ${{ env.CHANGED_FILES && env.CHANGED_FILES != '' }}
+ # Tests multiple versions at once, let's make sure all of them run, so keep-going.
+ nix-build ci -A parse --keep-going
diff --git a/.github/workflows/nixpkgs-vet.yml b/.github/workflows/nixpkgs-vet.yml
index dd4a8c2b7ab8..b9f9fd2c56f6 100644
--- a/.github/workflows/nixpkgs-vet.yml
+++ b/.github/workflows/nixpkgs-vet.yml
@@ -6,6 +6,9 @@
name: Vet nixpkgs
on:
+ pull_request:
+ paths:
+ - .github/workflows/nixpkgs-vet.yml
pull_request_target:
# This workflow depends on the base branch of the PR, but changing the base branch is not included in the default trigger events, which would be `opened`, `synchronize` or `reopened`.
# Instead it causes an `edited` event, so we need to add it explicitly here.
@@ -43,7 +46,7 @@ jobs:
git worktree add "$target" "$(git rev-parse HEAD^1)"
echo "target=$target" >> "$GITHUB_ENV"
- - uses: cachix/install-nix-action@d1ca217b388ee87b2507a9a93bf01368bde7cec2 # v31
+ - uses: cachix/install-nix-action@526118121621777ccd86f79b04685a9319637641 # v31
- name: Fetching the pinned tool
# Update the pinned version using ci/nixpkgs-vet/update-pinned-tool.sh
diff --git a/.github/workflows/no-channel.yml b/.github/workflows/no-channel.yml
index acaa937ad936..ee305a12d61b 100644
--- a/.github/workflows/no-channel.yml
+++ b/.github/workflows/no-channel.yml
@@ -1,19 +1,22 @@
name: "No channel PR"
on:
+ pull_request:
+ paths:
+ - .github/workflows/no-channel.yml
pull_request_target:
# Re-run should be triggered when the base branch is updated, instead of silently failing
types: [opened, synchronize, reopened, edited]
- branches:
- - 'nixos-**'
- - 'nixpkgs-**'
permissions: {}
jobs:
fail:
- name: "This PR is is targeting a channel branch"
- runs-on: ubuntu-24.04
+ if: |
+ startsWith(github.event.pull_request.base.ref, 'nixos-') ||
+ startsWith(github.event.pull_request.base.ref, 'nixpkgs-')
+ name: "This PR is targeting a channel branch"
+ runs-on: ubuntu-24.04-arm
steps:
- run: |
cat < Contents and Pull Requests: write for Nixpkgs
- - uses: actions/create-github-app-token@af35edadc00be37caa72ed9f3e6d5f7801bfdf09 # v1.11.7
+ - uses: actions/create-github-app-token@df432ceedc7162793a195dd1713ff69aefc7379e # v2.0.6
id: app-token
with:
app-id: ${{ vars.NIXPKGS_CI_APP_ID }}
private-key: ${{ secrets.NIXPKGS_CI_APP_PRIVATE_KEY }}
+ permission-contents: write
+ permission-pull-requests: write
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
index d4a573f5bb3c..8636219cc9ad 100644
--- a/CONTRIBUTING.md
+++ b/CONTRIBUTING.md
@@ -345,7 +345,7 @@ See [Nix Channel Status](https://status.nixos.org/) for the current channels and
Here's a brief overview of the main Git branches and what channels they're used for:
- `master`: The main branch, used for the unstable channels such as `nixpkgs-unstable`, `nixos-unstable` and `nixos-unstable-small`.
-- `release-YY.MM` (e.g. `release-25.05`): The NixOS release branches, used for the stable channels such as `nixos-25.05`, `nixos-25.05-small` and `nixpkgs-25.05-darwin`.
+- `release-YY.MM` (e.g. `release-25.11`): The NixOS release branches, used for the stable channels such as `nixos-25.11`, `nixos-25.11-small` and `nixpkgs-25.11-darwin`.
When a channel is updated, a corresponding Git branch is also updated to point to the corresponding commit.
So e.g. the [`nixpkgs-unstable` branch](https://github.com/nixos/nixpkgs/tree/nixpkgs-unstable) corresponds to the Git commit from the [`nixpkgs-unstable` channel](https://channels.nixos.org/nixpkgs-unstable).
@@ -533,7 +533,7 @@ Names of files and directories should be in lowercase, with dashes between words
### Formatting
-CI [enforces](./.github/workflows/check-nix-format.yml) all Nix files to be
+CI [enforces](./.github/workflows/check-format.yml) all Nix files to be
formatted using the [official Nix formatter](https://github.com/NixOS/nixfmt).
You can ensure this locally using either of these commands:
diff --git a/README.md b/README.md
index 92184fbf1145..fc275f69e4f4 100644
--- a/README.md
+++ b/README.md
@@ -1,9 +1,9 @@
-
+
-
+
@@ -14,7 +14,7 @@
[Nixpkgs](https://github.com/nixos/nixpkgs) is a collection of over
-100,000 software packages that can be installed with the
+120,000 software packages that can be installed with the
[Nix](https://nixos.org/nix/) package manager. It also implements
[NixOS](https://nixos.org/nixos/), a purely-functional Linux distribution.
diff --git a/ci/OWNERS b/ci/OWNERS
index 30b5c90ba2b4..cad846ee537e 100644
--- a/ci/OWNERS
+++ b/ci/OWNERS
@@ -16,7 +16,7 @@
# CI
/.github/*_TEMPLATE* @SigmaSquadron
/.github/workflows @NixOS/Security @Mic92 @zowoq @infinisil @azuwis @wolfgangwalther
-/.github/workflows/check-nix-format.yml @infinisil @wolfgangwalther
+/.github/workflows/check-format.yml @infinisil @wolfgangwalther
/.github/workflows/codeowners-v2.yml @infinisil @wolfgangwalther
/.github/workflows/nixpkgs-vet.yml @infinisil @philiptaron @wolfgangwalther
/ci @infinisil @philiptaron @NixOS/Security @wolfgangwalther
@@ -171,14 +171,19 @@ nixos/modules/installer/tools/nix-fallback-paths.nix @NixOS/nix-team @raitobeza
/pkgs/top-level/python-packages.nix @natsukium
/pkgs/top-level/release-python.nix @natsukium
+# CUDA
+/pkgs/top-level/cuda-packages.nix @NixOS/cuda-maintainers
+/pkgs/top-level/release-cuda.nix @NixOS/cuda-maintainers
+/pkgs/development/cuda-modules @NixOS/cuda-maintainers
+
# Haskell
-/doc/languages-frameworks/haskell.section.md @sternenseemann @maralorn
-/maintainers/scripts/haskell @sternenseemann @maralorn
-/pkgs/development/compilers/ghc @sternenseemann @maralorn
-/pkgs/development/haskell-modules @sternenseemann @maralorn
-/pkgs/test/haskell @sternenseemann @maralorn
-/pkgs/top-level/release-haskell.nix @sternenseemann @maralorn
-/pkgs/top-level/haskell-packages.nix @sternenseemann @maralorn
+/doc/languages-frameworks/haskell.section.md @sternenseemann @maralorn @wolfgangwalther
+/maintainers/scripts/haskell @sternenseemann @maralorn @wolfgangwalther
+/pkgs/development/compilers/ghc @sternenseemann @maralorn @wolfgangwalther
+/pkgs/development/haskell-modules @sternenseemann @maralorn @wolfgangwalther
+/pkgs/test/haskell @sternenseemann @maralorn @wolfgangwalther
+/pkgs/top-level/release-haskell.nix @sternenseemann @maralorn @wolfgangwalther
+/pkgs/top-level/haskell-packages.nix @sternenseemann @maralorn @wolfgangwalther
# Perl
/pkgs/development/interpreters/perl @stigtsp @zakame @marcusramberg
@@ -206,6 +211,7 @@ nixos/modules/installer/tools/nix-fallback-paths.nix @NixOS/nix-team @raitobeza
/pkgs/development/compilers/gcc
/pkgs/development/compilers/llvm @alyssais @RossComputerGuy @NixOS/llvm
/pkgs/development/compilers/emscripten @raitobezarius
+/doc/toolchains/llvm.chapter.md @alyssais @RossComputerGuy @NixOS/llvm
/doc/languages-frameworks/emscripten.section.md @raitobezarius
# Audio
@@ -251,6 +257,7 @@ pkgs/development/python-modules/buildcatrust/ @ajs124 @lukegb @mweinelt
/maintainers/scripts/kde @K900 @NickCao @SuperSandro2000 @ttuegel
# PostgreSQL and related stuff
+/pkgs/by-name/po/postgresqlTestHook @NixOS/postgres
/pkgs/by-name/ps/psqlodbc @NixOS/postgres
/pkgs/servers/sql/postgresql @NixOS/postgres
/pkgs/development/tools/rust/cargo-pgrx @NixOS/postgres
@@ -299,9 +306,12 @@ pkgs/development/python-modules/buildcatrust/ @ajs124 @lukegb @mweinelt
/pkgs/servers/http/nginx/ @raitobezarius
/nixos/modules/services/web-servers/nginx/ @raitobezarius
+# D
+/pkgs/build-support/dlang @jtbx @TomaSajt
+
# Dhall
-/pkgs/development/dhall-modules @Gabriella439 @Profpatsch @ehmry
-/pkgs/development/interpreters/dhall @Gabriella439 @Profpatsch @ehmry
+/pkgs/development/dhall-modules @Gabriella439 @Profpatsch
+/pkgs/development/interpreters/dhall @Gabriella439 @Profpatsch
# Idris
/pkgs/development/idris-modules @Infinisil
@@ -326,6 +336,9 @@ pkgs/development/python-modules/buildcatrust/ @ajs124 @lukegb @mweinelt
# Kakoune
/pkgs/applications/editors/kakoune @philiptaron
+# LuaPackages
+/pkgs/development/lua-modules @NixOS/lua
+
# Neovim
/pkgs/applications/editors/neovim @NixOS/neovim
@@ -372,12 +385,6 @@ pkgs/development/python-modules/buildcatrust/ @ajs124 @lukegb @mweinelt
# Xfce
/doc/hooks/xfce4-dev-tools.section.md @NixOS/xfce
-# nim
-/doc/languages-frameworks/nim.section.md @ehmry
-/pkgs/build-support/build-nim-package.nix @ehmry
-/pkgs/build-support/build-nim-sbom.nix @ehmry
-/pkgs/top-level/nim-overrides.nix @ehmry
-
# terraform providers
/pkgs/applications/networking/cluster/terraform-providers @zowoq
@@ -460,3 +467,12 @@ pkgs/development/beam-modules/ @NixOS/beam
pkgs/development/interpreters/erlang/ @NixOS/beam
pkgs/development/interpreters/elixir/ @NixOS/beam
pkgs/development/interpreters/lfe/ @NixOS/beam
+
+# OctoDNS
+pkgs/by-name/oc/octodns/ @anthonyroussel
+
+# Teleport
+pkgs/by-name/te/teleport* @arianvp @justinas @sigma @tomberek @freezeboy @techknowlogick @JuliusFreudenberger
+
+# Warp-terminal
+pkgs/by-name/wa/warp-terminal/ @emilytrau @imadnyc @donteatoreo @johnrtitor
diff --git a/ci/README.md b/ci/README.md
index a1b327de4e5d..6ef665e8b099 100644
--- a/ci/README.md
+++ b/ci/README.md
@@ -44,14 +44,14 @@ Why not just build the tooling right from the PRs Nixpkgs version?
## `get-merge-commit.sh GITHUB_REPO PR_NUMBER`
Check whether a PR is mergeable and return the test merge commit as
-[computed by GitHub](https://docs.github.com/en/rest/guides/using-the-rest-api-to-interact-with-your-git-database?apiVersion=2022-11-28#checking-mergeability-of-pull-requests).
+[computed by GitHub](https://docs.github.com/en/rest/guides/using-the-rest-api-to-interact-with-your-git-database?apiVersion=2022-11-28#checking-mergeability-of-pull-requests) and its parent.
Arguments:
- `GITHUB_REPO`: The repository of the PR, e.g. `NixOS/nixpkgs`
- `PR_NUMBER`: The PR number, e.g. `1234`
Exit codes:
-- 0: The PR can be merged, the test merge commit hash is returned on stdout
+- 0: The PR can be merged, the hashes of the test merge commit and the target commit are returned on stdout
- 1: The PR cannot be merged because it's not open anymore
- 2: The PR cannot be merged because it has a merge conflict
- 3: The merge commit isn't being computed, GitHub is likely having internal issues, unknown if the PR is mergeable
diff --git a/ci/default.nix b/ci/default.nix
index 67f59d61bfd4..dd39e082a6fb 100644
--- a/ci/default.nix
+++ b/ci/default.nix
@@ -44,18 +44,26 @@ let
# By default it's info, which is too noisy since we have many unmatched files
settings.on-unmatched = "debug";
+ programs.actionlint.enable = true;
+
+ programs.keep-sorted.enable = true;
+
# This uses nixfmt-rfc-style underneath,
# the default formatter for Nix code.
# See https://github.com/NixOS/nixfmt
programs.nixfmt.enable = true;
+
+ settings.formatter.editorconfig-checker = {
+ command = "${pkgs.lib.getExe pkgs.editorconfig-checker}";
+ options = [ "-disable-indent-size" ];
+ includes = [ "*" ];
+ priority = 1;
+ };
};
fs = pkgs.lib.fileset;
nixFilesSrc = fs.toSource {
root = ../.;
- fileset = fs.difference (fs.unions [
- (fs.fileFilter (file: file.hasExt "nix") ../.)
- ../.git-blame-ignore-revs
- ]) (fs.maybeMissing ../.git);
+ fileset = fs.difference ../. (fs.maybeMissing ../.git);
};
in
{
@@ -70,4 +78,16 @@ in
requestReviews = pkgs.callPackage ./request-reviews { };
codeownersValidator = pkgs.callPackage ./codeowners-validator { };
eval = pkgs.callPackage ./eval { };
+
+ # CI jobs
+ lib-tests = import ../lib/tests/release.nix { inherit pkgs; };
+ manual-nixos = (import ../nixos/release.nix { }).manual.${system} or null;
+ manual-nixpkgs = (import ../pkgs/top-level/release.nix { }).manual;
+ manual-nixpkgs-tests = (import ../pkgs/top-level/release.nix { }).manual.tests;
+ parse = pkgs.lib.recurseIntoAttrs {
+ latest = pkgs.callPackage ./parse.nix { nix = pkgs.nixVersions.latest; };
+ lix = pkgs.callPackage ./parse.nix { nix = pkgs.lix; };
+ minimum = pkgs.callPackage ./parse.nix { nix = pkgs.nixVersions.minimum; };
+ };
+ shell = import ../shell.nix { inherit nixpkgs system; };
}
diff --git a/ci/eval/README.md b/ci/eval/README.md
index 0436a028ed69..011f3dd74ed0 100644
--- a/ci/eval/README.md
+++ b/ci/eval/README.md
@@ -11,7 +11,7 @@ nix-build ci -A eval.full \
--arg evalSystems '["x86_64-linux" "aarch64-darwin"]'
```
-- `--max-jobs`: The maximum number of derivations to run at the same time. Only each [supported system](../supportedSystems.nix) gets a separate derivation, so it doesn't make sense to set this higher than that number.
+- `--max-jobs`: The maximum number of derivations to run at the same time. Only each [supported system](../supportedSystems.json) gets a separate derivation, so it doesn't make sense to set this higher than that number.
- `--cores`: The number of cores to use for each job. Recommended to set this to the amount of cores on your system divided by `--max-jobs`.
- `chunkSize`: The number of attributes that are evaluated simultaneously on a single core. Lowering this decreases memory usage at the cost of increased evaluation time. If this is too high, there won't be enough chunks to process them in parallel, and will also increase evaluation time.
- `evalSystems`: The set of systems for which `nixpkgs` should be evaluated. Defaults to the four official platforms (`x86_64-linux`, `aarch64-linux`, `x86_64-darwin` and `aarch64-darwin`).
diff --git a/ci/eval/compare/cmp-stats.py b/ci/eval/compare/cmp-stats.py
new file mode 100644
index 000000000000..0ef9c773163a
--- /dev/null
+++ b/ci/eval/compare/cmp-stats.py
@@ -0,0 +1,154 @@
+import json
+import os
+from scipy.stats import ttest_rel
+import pandas as pd
+import numpy as np
+from pathlib import Path
+
+# Define metrics of interest (can be expanded as needed)
+METRIC_PREFIXES = ("nr", "gc")
+
+def flatten_data(json_data: dict) -> dict:
+ """
+ Extracts and flattens metrics from JSON data.
+ This is needed because the JSON data can be nested.
+ For example, the JSON data entry might look like this:
+
+ "gc":{"cycles":13,"heapSize":5404549120,"totalBytes":9545876464}
+
+ Flattened:
+
+ "gc.cycles": 13
+ "gc.heapSize": 5404549120
+ ...
+
+ Args:
+ json_data (dict): JSON data containing metrics.
+ Returns:
+ dict: Flattened metrics with keys as metric names.
+ """
+ flat_metrics = {}
+ for k, v in json_data.items():
+ if isinstance(v, (int, float)):
+ flat_metrics[k] = v
+ elif isinstance(v, dict):
+ for sub_k, sub_v in v.items():
+ flat_metrics[f"{k}.{sub_k}"] = sub_v
+ return flat_metrics
+
+
+
+
+def load_all_metrics(directory: Path) -> dict:
+ """
+ Loads all stats JSON files in the specified directory and extracts metrics.
+
+ Args:
+ directory (Path): Directory containing JSON files.
+ Returns:
+ dict: Dictionary with filenames as keys and extracted metrics as values.
+ """
+ metrics = {}
+ for system_dir in directory.iterdir():
+ assert system_dir.is_dir()
+
+ for chunk_output in system_dir.iterdir():
+ with chunk_output.open() as f:
+ data = json.load(f)
+ metrics[f"{system_dir.name}/${chunk_output.name}"] = flatten_data(data)
+
+ return metrics
+
+def dataframe_to_markdown(df: pd.DataFrame) -> str:
+ df = df.sort_values(by=df.columns[0], ascending=True)
+ markdown_lines = []
+
+ # Header (get column names and format them)
+ header = '\n| ' + ' | '.join(df.columns) + ' |'
+ markdown_lines.append(header)
+ markdown_lines.append("| - " * (len(df.columns)) + "|") # Separator line
+
+ # Iterate over rows to build Markdown rows
+ for _, row in df.iterrows():
+ # TODO: define threshold for highlighting
+ highlight = False
+
+ fmt = lambda x: f"**{x}**" if highlight else f"{x}"
+
+ # Check for no change and NaN in p_value/t_stat
+ row_values = []
+ for val in row:
+ if isinstance(val, float) and np.isnan(val): # For NaN values in p-value or t-stat
+ row_values.append("-") # Custom symbol for NaN
+ elif isinstance(val, float) and val == 0: # For no change (mean_diff == 0)
+ row_values.append("-") # Custom symbol for no change
+ else:
+ row_values.append(fmt(f"{val:.4f}" if isinstance(val, float) else str(val)))
+
+ markdown_lines.append('| ' + ' | '.join(row_values) + ' |')
+
+ return '\n'.join(markdown_lines)
+
+
+def perform_pairwise_tests(before_metrics: dict, after_metrics: dict) -> pd.DataFrame:
+ common_files = sorted(set(before_metrics) & set(after_metrics))
+ all_keys = sorted({ metric_keys for file_metrics in before_metrics.values() for metric_keys in file_metrics.keys() })
+
+ results = []
+
+ for key in all_keys:
+ before_vals, after_vals = [], []
+
+ for fname in common_files:
+ if key in before_metrics[fname] and key in after_metrics[fname]:
+ before_vals.append(before_metrics[fname][key])
+ after_vals.append(after_metrics[fname][key])
+
+ if len(before_vals) >= 2:
+ before_arr = np.array(before_vals)
+ after_arr = np.array(after_vals)
+
+ diff = after_arr - before_arr
+ pct_change = 100 * diff / before_arr
+ t_stat, p_val = ttest_rel(after_arr, before_arr)
+
+ results.append({
+ "metric": key,
+ "mean_before": np.mean(before_arr),
+ "mean_after": np.mean(after_arr),
+ "mean_diff": np.mean(diff),
+ "mean_%_change": np.mean(pct_change),
+ "p_value": p_val,
+ "t_stat": t_stat
+ })
+
+ df = pd.DataFrame(results).sort_values("p_value")
+ return df
+
+
+if __name__ == "__main__":
+ before_dir = os.environ.get("BEFORE_DIR")
+ after_dir = os.environ.get("AFTER_DIR")
+
+ if not before_dir or not after_dir:
+ print("Error: Environment variables 'BEFORE_DIR' and 'AFTER_DIR' must be set.")
+ exit(1)
+
+ before_stats = Path(before_dir) / "stats"
+ after_stats = Path(after_dir) / "stats"
+
+ # This may happen if the pull request target does not include PR#399720 yet.
+ if not before_stats.exists():
+ print("⚠️ Skipping comparison: stats directory is missing in the target commit.")
+ exit(0)
+
+ # This should never happen, but we're exiting gracefully anyways
+ if not after_stats.exists():
+ print("⚠️ Skipping comparison: stats directory missing in current PR evaluation.")
+ exit(0)
+
+ before_metrics = load_all_metrics(before_stats)
+ after_metrics = load_all_metrics(after_stats)
+ df1 = perform_pairwise_tests(before_metrics, after_metrics)
+ markdown_table = dataframe_to_markdown(df1)
+ print(markdown_table)
diff --git a/ci/eval/compare/default.nix b/ci/eval/compare/default.nix
index 9b71c6656914..267cab8c0986 100644
--- a/ci/eval/compare/default.nix
+++ b/ci/eval/compare/default.nix
@@ -3,12 +3,15 @@
jq,
runCommand,
writeText,
+ python3,
...
}:
{
beforeResultDir,
afterResultDir,
touchedFilesJson,
+ githubAuthorId,
+ byName ? false,
}:
let
/*
@@ -112,29 +115,79 @@ let
# Adds "10.rebuild-*-stdenv" label if the "stdenv" attribute was changed
++ lib.mapAttrsToList (kernel: _: "10.rebuild-${kernel}-stdenv") (
lib.filterAttrs (_: kernelRebuilds: kernelRebuilds ? "stdenv") rebuildsByKernel
- );
+ )
+ # Adds the "11.by: package-maintainer" label if all of the packages directly
+ # changed are maintained by the PR's author. (https://github.com/NixOS/ofborg/blob/df400f44502d4a4a80fa283d33f2e55a4e43ee90/ofborg/src/tagger.rs#L83-L88)
+ ++ lib.optional (
+ maintainers ? ${githubAuthorId}
+ && lib.all (lib.flip lib.elem maintainers.${githubAuthorId}) (
+ lib.flatten (lib.attrValues maintainers)
+ )
+ ) "11.by: package-maintainer";
}
);
maintainers = import ./maintainers.nix {
changedattrs = lib.attrNames (lib.groupBy (a: a.name) rebuildsPackagePlatformAttrs);
changedpathsjson = touchedFilesJson;
+ inherit byName;
};
in
runCommand "compare"
{
- nativeBuildInputs = [ jq ];
+ nativeBuildInputs = [
+ jq
+ (python3.withPackages (
+ ps: with ps; [
+ numpy
+ pandas
+ scipy
+ ]
+ ))
+
+ ];
maintainers = builtins.toJSON maintainers;
passAsFile = [ "maintainers" ];
+ env = {
+ BEFORE_DIR = "${beforeResultDir}";
+ AFTER_DIR = "${afterResultDir}";
+ };
}
''
mkdir $out
cp ${changed-paths} $out/changed-paths.json
- jq -r -f ${./generate-step-summary.jq} < ${changed-paths} > $out/step-summary.md
+
+ if jq -e '(.attrdiff.added | length == 0) and (.attrdiff.removed | length == 0)' "${changed-paths}" > /dev/null; then
+ # Chunks have changed between revisions
+ # We cannot generate a performance comparison
+ {
+ echo
+ echo "# Performance comparison"
+ echo
+ echo "This compares the performance of this branch against its pull request base branch (e.g., 'master')"
+ echo
+ echo "For further help please refer to: [ci/README.md](https://github.com/NixOS/nixpkgs/blob/master/ci/README.md)"
+ echo
+ } >> $out/step-summary.md
+
+ python3 ${./cmp-stats.py} >> $out/step-summary.md
+
+ else
+ # Package chunks are the same in both revisions
+ # We can use the to generate a performance comparison
+ {
+ echo
+ echo "# Performance Comparison"
+ echo
+ echo "Performance stats were skipped because the package sets differ between the two revisions."
+ echo
+ echo "For further help please refer to: [ci/README.md](https://github.com/NixOS/nixpkgs/blob/master/ci/README.md)"
+ } >> $out/step-summary.md
+ fi
+
+ jq -r -f ${./generate-step-summary.jq} < ${changed-paths} >> $out/step-summary.md
cp "$maintainersPath" "$out/maintainers.json"
-
- # TODO: Compare eval stats
''
diff --git a/ci/eval/compare/maintainers.nix b/ci/eval/compare/maintainers.nix
index 69748a629cff..9b641d6ec60f 100644
--- a/ci/eval/compare/maintainers.nix
+++ b/ci/eval/compare/maintainers.nix
@@ -1,5 +1,9 @@
# Almost directly vendored from https://github.com/NixOS/ofborg/blob/5a4e743f192fb151915fcbe8789922fa401ecf48/ofborg/src/maintainers.nix
-{ changedattrs, changedpathsjson }:
+{
+ changedattrs,
+ changedpathsjson,
+ byName ? false,
+}:
let
pkgs = import ../../.. {
system = "x86_64-linux";
@@ -41,7 +45,16 @@ let
) validPackageAttributes;
attrsWithMaintainers = builtins.map (
- pkg: pkg // { maintainers = (pkg.package.meta or { }).maintainers or [ ]; }
+ pkg:
+ let
+ meta = pkg.package.meta or { };
+ in
+ pkg
+ // {
+ # TODO: Refactor this so we can ping entire teams instead of the individual members.
+ # Note that this will require keeping track of GH team IDs in "maintainers/teams.nix".
+ maintainers = meta.maintainers or [ ];
+ }
) attrsWithPackages;
relevantFilenames =
@@ -49,7 +62,8 @@ let
(lib.lists.unique (
builtins.map (pos: lib.strings.removePrefix (toString ../..) pos.file) (
builtins.filter (x: x != null) [
- (builtins.unsafeGetAttrPos "maintainers" (drv.meta or { }))
+ ((drv.meta or { }).maintainersPosition or null)
+ ((drv.meta or { }).teamsPosition or null)
(builtins.unsafeGetAttrPos "src" drv)
# broken because name is always set by stdenv:
# # A hack to make `nix-env -qa` and `nix search` ignore broken packages.
@@ -83,12 +97,13 @@ let
pkg:
builtins.map (maintainer: {
id = maintainer.githubId;
+ inherit (maintainer) github;
packageName = pkg.name;
dueToFiles = pkg.filenames;
}) pkg.maintainers
) attrsWithModifiedFiles;
- byMaintainer = lib.groupBy (ping: toString ping.id) listToPing;
+ byMaintainer = lib.groupBy (ping: toString ping.${if byName then "github" else "id"}) listToPing;
packagesPerMaintainer = lib.attrsets.mapAttrs (
maintainer: packages: builtins.map (pkg: pkg.packageName) packages
diff --git a/ci/eval/default.nix b/ci/eval/default.nix
index 6115feddfba8..d46d0f9a759d 100644
--- a/ci/eval/default.nix
+++ b/ci/eval/default.nix
@@ -9,6 +9,7 @@
nixVersions,
jq,
sta,
+ python3,
}:
let
@@ -25,16 +26,19 @@ let
"nixos"
"pkgs"
".version"
- "ci/supportedSystems.nix"
+ "ci/supportedSystems.json"
]
);
};
- nix = nixVersions.nix_2_24;
+ nix = nixVersions.latest;
- supportedSystems = import ../supportedSystems.nix;
+ supportedSystems = builtins.fromJSON (builtins.readFile ../supportedSystems.json);
attrpathsSuperset =
+ {
+ evalSystem,
+ }:
runCommand "attrpaths-superset.json"
{
src = nixpkgs;
@@ -42,8 +46,6 @@ let
nix
time
];
- env.supportedSystems = builtins.toJSON supportedSystems;
- passAsFile = [ "supportedSystems" ];
}
''
export NIX_STATE_DIR=$(mktemp -d)
@@ -56,8 +58,8 @@ let
-I "$src" \
--option restrict-eval true \
--option allow-import-from-derivation false \
+ --option eval-system "${evalSystem}" \
--arg enableWarnings false > $out/paths.json
- mv "$supportedSystemsPath" $out/systems.json
'';
singleSystem =
@@ -67,7 +69,7 @@ let
# because `--argstr system` would only be passed to the ci/default.nix file!
evalSystem,
# The path to the `paths.json` file from `attrpathsSuperset`
- attrpathFile,
+ attrpathFile ? "${attrpathsSuperset { inherit evalSystem; }}/paths.json",
# The number of attributes per chunk, see ./README.md for more info.
chunkSize,
checkMeta ? true,
@@ -179,6 +181,8 @@ let
xargs -I{} -P"$cores" \
${singleChunk} "$chunkSize" {} "$evalSystem" "$chunkOutputDir"
+ cp -r "$chunkOutputDir"/stats $out/stats-by-chunk
+
if (( chunkSize * chunkCount != attrCount )); then
# A final incomplete chunk would mess up the stats, don't include it
rm "$chunkOutputDir"/stats/"$seq_end"
@@ -253,6 +257,12 @@ let
done
} |
jq -s from_entries > $out/stats.json
+
+ mkdir -p $out/stats
+
+ for d in ${resultsDir}/*; do
+ cp -r "$d"/stats-by-chunk $out/stats/$(basename "$d")
+ done
'';
compare = import ./compare {
@@ -262,6 +272,7 @@ let
runCommand
writeText
supportedSystems
+ python3
;
};
@@ -279,7 +290,6 @@ let
name = evalSystem;
path = singleSystem {
inherit quickTest evalSystem chunkSize;
- attrpathFile = attrpathsSuperset + "/paths.json";
};
}) evalSystems
);
diff --git a/ci/get-merge-commit.sh b/ci/get-merge-commit.sh
index c62bb56dd993..c233f7f91691 100755
--- a/ci/get-merge-commit.sh
+++ b/ci/get-merge-commit.sh
@@ -55,7 +55,10 @@ done
if [[ "$mergeable" == "true" ]]; then
log "The PR can be merged"
- jq -r .merge_commit_sha <<< "$prInfo"
+ mergedSha="$(jq -r .merge_commit_sha <<< "$prInfo")"
+ echo "mergedSha=$mergedSha"
+ targetSha="$(gh api "/repos/$repo/commits/$mergedSha" --jq '.parents[0].sha')"
+ echo "targetSha=$targetSha"
else
log "The PR has a merge conflict"
exit 2
diff --git a/ci/parse.nix b/ci/parse.nix
new file mode 100644
index 000000000000..26ac0f785fd4
--- /dev/null
+++ b/ci/parse.nix
@@ -0,0 +1,43 @@
+{
+ lib,
+ nix,
+ runCommand,
+}:
+let
+ nixpkgs =
+ with lib.fileset;
+ toSource {
+ root = ../.;
+ fileset = (fileFilter (file: file.hasExt "nix") ../.);
+ };
+in
+runCommand "nix-parse-${nix.name}"
+ {
+ nativeBuildInputs = [
+ nix
+ ];
+ }
+ ''
+ export NIX_STORE_DIR=$TMPDIR/store
+ export NIX_STATE_DIR=$TMPDIR/state
+
+ cd "${nixpkgs}"
+
+ # Passes all files to nix-instantiate at once.
+ # Much faster, but will only show first error.
+ parse-all() {
+ find . -type f -iname '*.nix' | xargs -P $(nproc) nix-instantiate --parse >/dev/null 2>/dev/null
+ }
+
+ # Passes each file separately to nix-instantiate with -n1.
+ # Much slower, but will show all errors.
+ parse-each() {
+ find . -type f -iname '*.nix' | xargs -n1 -P $(nproc) nix-instantiate --parse >/dev/null
+ }
+
+ if ! parse-all; then
+ parse-each
+ fi
+
+ touch $out
+ ''
diff --git a/ci/pinned-nixpkgs.json b/ci/pinned-nixpkgs.json
index c5c558dc61bc..f5efff109e8c 100644
--- a/ci/pinned-nixpkgs.json
+++ b/ci/pinned-nixpkgs.json
@@ -1,4 +1,4 @@
{
- "rev": "573c650e8a14b2faa0041645ab18aed7e60f0c9a",
- "sha256": "0qg99zj0gb0pc6sjlkmwhk1c1xz14qxmk6gamgfmcxpsfdp5vn72"
+ "rev": "eaeed9530c76ce5f1d2d8232e08bec5e26f18ec1",
+ "sha256": "132nimgi1g88fbhddk4b8b1qk68jly494x2mnphyk3xa1d2wy9q7"
}
diff --git a/ci/supportedSystems.json b/ci/supportedSystems.json
new file mode 100644
index 000000000000..44c18f1abf0e
--- /dev/null
+++ b/ci/supportedSystems.json
@@ -0,0 +1,6 @@
+[
+ "aarch64-linux",
+ "aarch64-darwin",
+ "x86_64-linux",
+ "x86_64-darwin"
+]
diff --git a/ci/supportedSystems.nix b/ci/supportedSystems.nix
deleted file mode 100644
index 471f84b92fc2..000000000000
--- a/ci/supportedSystems.nix
+++ /dev/null
@@ -1,6 +0,0 @@
-[
- "aarch64-linux"
- "aarch64-darwin"
- "x86_64-linux"
- "x86_64-darwin"
-]
diff --git a/doc/README.md b/doc/README.md
index 531c38acdfa5..029db7eabc7f 100644
--- a/doc/README.md
+++ b/doc/README.md
@@ -34,7 +34,27 @@ $ nix-build doc
If the build succeeds, the manual will be in `./result/share/doc/nixpkgs/manual.html`.
-### devmode
+### Development environment
+
+In order to reduce repetition, consider using tools from the provided development environment:
+
+Load it from the Nixpkgs documentation directory with
+
+```ShellSession
+$ cd /path/to/nixpkgs/doc
+$ nix-shell
+```
+
+To load the development utilities automatically when entering that directory, [set up `nix-direnv`](https://nix.dev/guides/recipes/direnv).
+
+Make sure that your local files aren't added to Git history by adding the following lines to `.git/info/exclude` at the root of the Nixpkgs repository:
+
+```
+/**/.envrc
+/**/.direnv
+```
+
+#### `devmode`
The shell in the manual source directory makes available a command, `devmode`.
It is a daemon, that:
diff --git a/doc/build-helpers/dev-shell-tools.chapter.md b/doc/build-helpers/dev-shell-tools.chapter.md
index 0168ea39f7aa..12018cd76cee 100644
--- a/doc/build-helpers/dev-shell-tools.chapter.md
+++ b/doc/build-helpers/dev-shell-tools.chapter.md
@@ -20,12 +20,12 @@ Converts Nix values to strings in the way the [`derivation` built-in function](h
```nix
devShellTools.valueToString (builtins.toFile "foo" "bar")
-=> "/nix/store/...-foo"
+# => "/nix/store/...-foo"
```
```nix
devShellTools.valueToString false
-=> ""
+# => ""
```
:::
@@ -42,16 +42,22 @@ This function does not support `__structuredAttrs`, but does support `passAsFile
devShellTools.unstructuredDerivationInputEnv {
drvAttrs = {
name = "foo";
- buildInputs = [ hello figlet ];
+ buildInputs = [
+ hello
+ figlet
+ ];
builder = bash;
- args = [ "-c" "${./builder.sh}" ];
+ args = [
+ "-c"
+ "${./builder.sh}"
+ ];
};
}
-=> {
- name = "foo";
- buildInputs = "/nix/store/...-hello /nix/store/...-figlet";
- builder = "/nix/store/...-bash";
-}
+# => {
+# name = "foo";
+# buildInputs = "/nix/store/...-hello /nix/store/...-figlet";
+# builder = "/nix/store/...-bash";
+#}
```
Note that `args` is not included, because Nix does not added it to the builder process environment.
@@ -69,7 +75,10 @@ Takes the relevant parts of a derivation and returns a set of environment variab
let
pkg = hello;
in
-devShellTools.derivationOutputEnv { outputList = pkg.outputs; outputMap = pkg; }
+devShellTools.derivationOutputEnv {
+ outputList = pkg.outputs;
+ outputMap = pkg;
+}
```
:::
diff --git a/doc/build-helpers/fetchers.chapter.md b/doc/build-helpers/fetchers.chapter.md
index fd7b41f64c70..997f97f81bdb 100644
--- a/doc/build-helpers/fetchers.chapter.md
+++ b/doc/build-helpers/fetchers.chapter.md
@@ -491,7 +491,11 @@ It might be useful to manipulate the content downloaded by `fetchurl` directly i
In this example, we'll adapt [](#ex-fetchers-fetchurl-nixpkgs-version) to append the result of running the `hello` package to the contents we download, purely to illustrate how to manipulate the content.
```nix
-{ fetchurl, hello, lib }:
+{
+ fetchurl,
+ hello,
+ lib,
+}:
fetchurl {
url = "https://raw.githubusercontent.com/NixOS/nixpkgs/23.11/.version";
@@ -714,9 +718,10 @@ A wrapper around `fetchpatch`, which takes:
Here is an example of `fetchDebianPatch` in action:
```nix
-{ lib
-, fetchDebianPatch
-, buildPythonPackage
+{
+ lib,
+ fetchDebianPatch,
+ buildPythonPackage,
}:
buildPythonPackage rec {
@@ -768,9 +773,14 @@ Additionally, the following optional arguments can be given:
: Whether to fetch LFS objects.
+*`preFetch`* (String)
+
+: Shell code to be executed before the repository has been fetched, to allow
+ changing the environment the fetcher runs in.
+
*`postFetch`* (String)
-: Shell code executed after the file has been fetched successfully.
+: Shell code executed after the repository has been fetched successfully.
This can do things like check or transform the file.
*`leaveDotGit`* (Boolean)
@@ -914,7 +924,9 @@ It produces packages that cannot be built automatically.
{ fetchtorrent }:
fetchtorrent {
- config = { peer-limit-global = 100; };
+ config = {
+ peer-limit-global = 100;
+ };
url = "magnet:?xt=urn:btih:dd8255ecdc7ca55fb0bbf81323d87062db1f6d1c";
hash = "";
}
diff --git a/doc/build-helpers/images/appimagetools.section.md b/doc/build-helpers/images/appimagetools.section.md
index cbd40ab6c35f..7bfc45287d1f 100644
--- a/doc/build-helpers/images/appimagetools.section.md
+++ b/doc/build-helpers/images/appimagetools.section.md
@@ -33,7 +33,7 @@ let
version = "0.6.30";
src = fetchurl {
- url = "https://github.com/nukeop/nuclear/releases/download/v${version}/${pname}-v${version}.AppImage";
+ url = "https://github.com/nukeop/nuclear/releases/download/v${version}/nuclear-v${version}.AppImage";
hash = "sha256-he1uGC1M/nFcKpMM9JKY4oeexJcnzV0ZRxhTjtJz6xw=";
};
in
@@ -66,7 +66,8 @@ let
url = "https://github.com/irccloud/irccloud-desktop/releases/download/v${version}/IRCCloud-${version}-linux-x86_64.AppImage";
hash = "sha256-/hMPvYdnVB1XjKgU2v47HnVvW4+uC3rhRjbucqin4iI=";
};
-in appimageTools.wrapType2 {
+in
+appimageTools.wrapType2 {
inherit pname version src;
extraPkgs = pkgs: [ pkgs.at-spi2-core ];
}
@@ -106,7 +107,8 @@ let
appimageContents = appimageTools.extract {
inherit pname version src;
};
-in appimageTools.wrapType2 {
+in
+appimageTools.wrapType2 {
inherit pname version src;
extraPkgs = pkgs: [ pkgs.at-spi2-core ];
@@ -150,7 +152,8 @@ let
substituteInPlace $out/irccloud.desktop --replace-fail 'Exec=AppRun' 'Exec=${pname}'
'';
};
-in appimageTools.wrapType2 {
+in
+appimageTools.wrapType2 {
inherit pname version src;
extraPkgs = pkgs: [ pkgs.at-spi2-core ];
diff --git a/doc/build-helpers/images/binarycache.section.md b/doc/build-helpers/images/binarycache.section.md
index 954f07e93213..46b43bd4f65a 100644
--- a/doc/build-helpers/images/binarycache.section.md
+++ b/doc/build-helpers/images/binarycache.section.md
@@ -35,7 +35,7 @@ The following derivation will construct a flat-file binary cache containing the
```nix
{ mkBinaryCache, hello }:
mkBinaryCache {
- rootPaths = [hello];
+ rootPaths = [ hello ];
}
```
diff --git a/doc/build-helpers/images/dockertools.section.md b/doc/build-helpers/images/dockertools.section.md
index 04d477bdc506..fbb0df85d647 100644
--- a/doc/build-helpers/images/dockertools.section.md
+++ b/doc/build-helpers/images/dockertools.section.md
@@ -235,7 +235,11 @@ The following package builds a Docker image that runs the `redis-server` executa
The Docker image will have name `redis` and tag `latest`.
```nix
-{ dockerTools, buildEnv, redis }:
+{
+ dockerTools,
+ buildEnv,
+ redis,
+}:
dockerTools.buildImage {
name = "redis";
tag = "latest";
@@ -253,7 +257,9 @@ dockerTools.buildImage {
config = {
Cmd = [ "/bin/redis-server" ];
WorkingDir = "/data";
- Volumes = { "/data" = { }; };
+ Volumes = {
+ "/data" = { };
+ };
};
}
```
@@ -286,7 +292,11 @@ It uses `runAsRoot` to create a directory and a file inside the image.
This works the same as [](#ex-dockerTools-buildImage-extraCommands), but uses `runAsRoot` instead of `extraCommands`.
```nix
-{ dockerTools, buildEnv, hello }:
+{
+ dockerTools,
+ buildEnv,
+ hello,
+}:
dockerTools.buildImage {
name = "hello";
tag = "latest";
@@ -320,7 +330,11 @@ This works the same as [](#ex-dockerTools-buildImage-runAsRoot), but uses `extra
Note that with `extraCommands`, we can't directly reference `/` and must create files and directories as if we were already on `/`.
```nix
-{ dockerTools, buildEnv, hello }:
+{
+ dockerTools,
+ buildEnv,
+ hello,
+}:
dockerTools.buildImage {
name = "hello";
tag = "latest";
@@ -350,7 +364,11 @@ dockerTools.buildImage {
Note that using a value of `"now"` in the `created` attribute will break reproducibility.
```nix
-{ dockerTools, buildEnv, hello }:
+{
+ dockerTools,
+ buildEnv,
+ hello,
+}:
dockerTools.buildImage {
name = "hello";
tag = "latest";
@@ -766,7 +784,11 @@ The closure of `config` is automatically included in the generated image.
The following package shows a more compact way to create the same output generated in [](#ex-dockerTools-streamLayeredImage-hello).
```nix
-{ dockerTools, hello, lib }:
+{
+ dockerTools,
+ hello,
+ lib,
+}:
dockerTools.streamLayeredImage {
name = "hello";
tag = "latest";
@@ -1547,11 +1569,15 @@ The Docker image generated will have a name like `hello--env` and tag `
This example uses [](#ex-dockerTools-streamNixShellImage-hello) as a starting point.
```nix
-{ dockerTools, cowsay, hello }:
+{
+ dockerTools,
+ cowsay,
+ hello,
+}:
dockerTools.streamNixShellImage {
tag = "latest";
drv = hello.overrideAttrs (old: {
- nativeBuildInputs = old.nativeBuildInputs or [] ++ [
+ nativeBuildInputs = old.nativeBuildInputs or [ ] ++ [
cowsay
];
});
diff --git a/doc/build-helpers/images/makediskimage.section.md b/doc/build-helpers/images/makediskimage.section.md
index 3edfa906aa6a..6d9afccdbae5 100644
--- a/doc/build-helpers/images/makediskimage.section.md
+++ b/doc/build-helpers/images/makediskimage.section.md
@@ -52,23 +52,23 @@ A `deterministic` flag is available for best efforts determinism.
To produce a Nix-store only image:
```nix
let
- pkgs = import {};
+ pkgs = import { };
lib = pkgs.lib;
make-disk-image = import ;
in
- make-disk-image {
- inherit pkgs lib;
- config = {};
- additionalPaths = [ ];
- format = "qcow2";
- onlyNixStore = true;
- partitionTableType = "none";
- installBootLoader = false;
- touchEFIVars = false;
- diskSize = "auto";
- additionalSpace = "0M"; # Defaults to 512M.
- copyChannel = false;
- }
+make-disk-image {
+ inherit pkgs lib;
+ config = { };
+ additionalPaths = [ ];
+ format = "qcow2";
+ onlyNixStore = true;
+ partitionTableType = "none";
+ installBootLoader = false;
+ touchEFIVars = false;
+ diskSize = "auto";
+ additionalSpace = "0M"; # Defaults to 512M.
+ copyChannel = false;
+}
```
Some arguments can be left out, they are shown explicitly for the sake of the example.
@@ -78,29 +78,36 @@ Building this derivation will provide a QCOW2 disk image containing only the Nix
To produce a NixOS installation image disk with UEFI and bootloader installed:
```nix
let
- pkgs = import {};
+ pkgs = import { };
lib = pkgs.lib;
make-disk-image = import ;
evalConfig = import ;
in
- make-disk-image {
- inherit pkgs lib;
- inherit (evalConfig {
+make-disk-image {
+ inherit pkgs lib;
+ inherit
+ (evalConfig {
modules = [
{
- fileSystems."/" = { device = "/dev/vda"; fsType = "ext4"; autoFormat = true; };
+ fileSystems."/" = {
+ device = "/dev/vda";
+ fsType = "ext4";
+ autoFormat = true;
+ };
boot.grub.device = "/dev/vda";
}
];
- }) config;
- format = "qcow2";
- onlyNixStore = false;
- partitionTableType = "legacy+gpt";
- installBootLoader = true;
- touchEFIVars = true;
- diskSize = "auto";
- additionalSpace = "0M"; # Defaults to 512M.
- copyChannel = false;
- memSize = 2048; # Qemu VM memory size in megabytes. Defaults to 1024M.
- }
+ })
+ config
+ ;
+ format = "qcow2";
+ onlyNixStore = false;
+ partitionTableType = "legacy+gpt";
+ installBootLoader = true;
+ touchEFIVars = true;
+ diskSize = "auto";
+ additionalSpace = "0M"; # Defaults to 512M.
+ copyChannel = false;
+ memSize = 2048; # Qemu VM memory size in megabytes. Defaults to 1024M.
+}
```
diff --git a/doc/build-helpers/images/ocitools.section.md b/doc/build-helpers/images/ocitools.section.md
index 96627615ffb5..5101dd81715b 100644
--- a/doc/build-helpers/images/ocitools.section.md
+++ b/doc/build-helpers/images/ocitools.section.md
@@ -76,7 +76,11 @@ Note that no user namespace is created, which means that you won't be able to ru
This example uses `ociTools.buildContainer` to create a simple container that runs `bash`.
```nix
-{ ociTools, lib, bash }:
+{
+ ociTools,
+ lib,
+ bash,
+}:
ociTools.buildContainer {
args = [
(lib.getExe bash)
diff --git a/doc/build-helpers/images/portableservice.section.md b/doc/build-helpers/images/portableservice.section.md
index c271bc775dba..43e44fe33aec 100644
--- a/doc/build-helpers/images/portableservice.section.md
+++ b/doc/build-helpers/images/portableservice.section.md
@@ -91,7 +91,12 @@ See [](#ex-portableService-hello) to understand how to use the output of `portab
The following example builds a Portable Service image with the `hello` package, along with a service unit that runs it.
```nix
-{ lib, writeText, portableService, hello }:
+{
+ lib,
+ writeText,
+ portableService,
+ hello,
+}:
let
hello-service = writeText "hello.service" ''
[Unit]
@@ -151,7 +156,13 @@ To make things available globally, you must specify the `symlinks` attribute whe
The following package builds on the package from [](#ex-portableService-hello) to make `/etc/ssl` available globally (this is only for illustrative purposes, because `hello` doesn't use `/etc/ssl`).
```nix
-{ lib, writeText, portableService, hello, cacert }:
+{
+ lib,
+ writeText,
+ portableService,
+ hello,
+ cacert,
+}:
let
hello-service = writeText "hello.service" ''
[Unit]
@@ -167,7 +178,10 @@ portableService {
inherit (hello) version;
units = [ hello-service ];
symlinks = [
- { object = "${cacert}/etc/ssl"; symlink = "/etc/ssl"; }
+ {
+ object = "${cacert}/etc/ssl";
+ symlink = "/etc/ssl";
+ }
];
}
```
diff --git a/doc/build-helpers/special/checkpoint-build.section.md b/doc/build-helpers/special/checkpoint-build.section.md
index a1ce5608f246..036fee286a99 100644
--- a/doc/build-helpers/special/checkpoint-build.section.md
+++ b/doc/build-helpers/special/checkpoint-build.section.md
@@ -26,7 +26,9 @@ To change a normal derivation to a checkpoint based build, these steps must be t
## Example {#sec-checkpoint-build-example}
```nix
-{ pkgs ? import {} }:
+{
+ pkgs ? import { },
+}:
let
inherit (pkgs.checkpointBuildTools)
prepareCheckpointBuild
@@ -39,5 +41,6 @@ let
sed -i 's/Hello, world!/Hello, Nix!/g' src/hello.c
'';
});
-in mkCheckpointBuild changedHello helloCheckpoint
+in
+mkCheckpointBuild changedHello helloCheckpoint
```
diff --git a/doc/build-helpers/special/fakenss.section.md b/doc/build-helpers/special/fakenss.section.md
index c890752c0653..7b1b6f2576f2 100644
--- a/doc/build-helpers/special/fakenss.section.md
+++ b/doc/build-helpers/special/fakenss.section.md
@@ -48,12 +48,19 @@ It is useful with functions in `dockerTools` to allow building Docker images tha
This example includes the `hello` binary in the image so it can do something besides just have the extra files.
```nix
-{ dockerTools, fakeNss, hello }:
+{
+ dockerTools,
+ fakeNss,
+ hello,
+}:
dockerTools.buildImage {
name = "image-with-passwd";
tag = "latest";
- copyToRoot = [ fakeNss hello ];
+ copyToRoot = [
+ fakeNss
+ hello
+ ];
config = {
Cmd = [ "/bin/hello" ];
@@ -70,8 +77,8 @@ The following code uses `override` to add extra lines to `/etc/passwd` and `/etc
```nix
{ fakeNss }:
fakeNss.override {
- extraPasswdLines = ["newuser:x:9001:9001:new user:/var/empty:/bin/sh"];
- extraGroupLines = ["newuser:x:9001:"];
+ extraPasswdLines = [ "newuser:x:9001:9001:new user:/var/empty:/bin/sh" ];
+ extraGroupLines = [ "newuser:x:9001:" ];
}
```
:::
diff --git a/doc/build-helpers/special/fhs-environments.section.md b/doc/build-helpers/special/fhs-environments.section.md
index 815f6cac1126..e81dbdfdc641 100644
--- a/doc/build-helpers/special/fhs-environments.section.md
+++ b/doc/build-helpers/special/fhs-environments.section.md
@@ -36,22 +36,29 @@ Accepted arguments are:
You can create a simple environment using a `shell.nix` like this:
```nix
-{ pkgs ? import {} }:
+{
+ pkgs ? import { },
+}:
(pkgs.buildFHSEnv {
name = "simple-x11-env";
- targetPkgs = pkgs: (with pkgs; [
- udev
- alsa-lib
- ]) ++ (with pkgs.xorg; [
- libX11
- libXcursor
- libXrandr
- ]);
- multiPkgs = pkgs: (with pkgs; [
- udev
- alsa-lib
- ]);
+ targetPkgs =
+ pkgs:
+ (with pkgs; [
+ udev
+ alsa-lib
+ ])
+ ++ (with pkgs.xorg; [
+ libX11
+ libXcursor
+ libXrandr
+ ]);
+ multiPkgs =
+ pkgs:
+ (with pkgs; [
+ udev
+ alsa-lib
+ ]);
runScript = "bash";
}).env
```
diff --git a/doc/build-helpers/special/makesetuphook.section.md b/doc/build-helpers/special/makesetuphook.section.md
index 179d8d456372..7b83653296eb 100644
--- a/doc/build-helpers/special/makesetuphook.section.md
+++ b/doc/build-helpers/special/makesetuphook.section.md
@@ -9,7 +9,7 @@ pkgs.makeSetupHook {
name = "something-hook";
propagatedBuildInputs = [ pkgs.commandsomething ];
depsTargetTargetPropagated = [ pkgs.libsomething ];
-} ./script.sh;
+} ./script.sh
```
### setup hook that depends on the hello package and runs hello and @shell@ is substituted with path to bash {#sec-pkgs.makeSetupHook-usage-example}
@@ -42,7 +42,7 @@ pkgs.makeSetupHook
}
preConfigureHooks+=(_printHelloHook)
''
- );
+ )
```
## Attributes {#sec-pkgs.makeSetupHook-attributes}
diff --git a/doc/build-helpers/special/mkshell.section.md b/doc/build-helpers/special/mkshell.section.md
index e39bef7468e3..15443660c9ff 100644
--- a/doc/build-helpers/special/mkshell.section.md
+++ b/doc/build-helpers/special/mkshell.section.md
@@ -8,11 +8,16 @@ repetition when using it with `nix-shell` (or `nix develop`).
Here is a common usage example:
```nix
-{ pkgs ? import {} }:
+{
+ pkgs ? import { },
+}:
pkgs.mkShell {
packages = [ pkgs.gnumake ];
- inputsFrom = [ pkgs.hello pkgs.gnutar ];
+ inputsFrom = [
+ pkgs.hello
+ pkgs.gnutar
+ ];
shellHook = ''
export DEBUG=1
diff --git a/doc/build-helpers/special/vm-tools.section.md b/doc/build-helpers/special/vm-tools.section.md
index 46ced7cd9990..7591f62eb462 100644
--- a/doc/build-helpers/special/vm-tools.section.md
+++ b/doc/build-helpers/special/vm-tools.section.md
@@ -31,25 +31,34 @@ If the build fails and Nix is run with the `-K/--keep-failed` option, a script `
Build the derivation hello inside a VM:
```nix
-{ pkgs }: with pkgs; with vmTools;
-runInLinuxVM hello
+{ pkgs }: with pkgs; with vmTools; runInLinuxVM hello
```
Build inside a VM with extra memory:
```nix
-{ pkgs }: with pkgs; with vmTools;
-runInLinuxVM (hello.overrideAttrs (_: { memSize = 1024; }))
+{ pkgs }:
+with pkgs;
+with vmTools;
+runInLinuxVM (
+ hello.overrideAttrs (_: {
+ memSize = 1024;
+ })
+)
```
Use VM with a disk image (implicitly sets `diskImage`, see [`vmTools.createEmptyImage`](#vm-tools-createEmptyImage)):
```nix
-{ pkgs }: with pkgs; with vmTools;
-runInLinuxVM (hello.overrideAttrs (_: {
- preVM = createEmptyImage {
- size = 1024;
- fullName = "vm-image";
- };
-}))
+{ pkgs }:
+with pkgs;
+with vmTools;
+runInLinuxVM (
+ hello.overrideAttrs (_: {
+ preVM = createEmptyImage {
+ size = 1024;
+ fullName = "vm-image";
+ };
+ })
+)
```
## `vmTools.extractFs` {#vm-tools-extractFs}
@@ -66,8 +75,7 @@ Takes a file, such as an ISO, and extracts its contents into the store.
Extract the contents of an ISO file:
```nix
-{ pkgs }: with pkgs; with vmTools;
-extractFs { file = ./image.iso; }
+{ pkgs }: with pkgs; with vmTools; extractFs { file = ./image.iso; }
```
## `vmTools.extractMTDfs` {#vm-tools-extractMTDfs}
@@ -86,14 +94,12 @@ Generate a script that can be used to run an interactive session in the given im
Create a script for running a Fedora 27 VM:
```nix
-{ pkgs }: with pkgs; with vmTools;
-makeImageTestScript diskImages.fedora27x86_64
+{ pkgs }: with pkgs; with vmTools; makeImageTestScript diskImages.fedora27x86_64
```
Create a script for running an Ubuntu 20.04 VM:
```nix
-{ pkgs }: with pkgs; with vmTools;
-makeImageTestScript diskImages.ubuntu2004x86_64
+{ pkgs }: with pkgs; with vmTools; makeImageTestScript diskImages.ubuntu2004x86_64
```
## `vmTools.diskImageFuns` {#vm-tools-diskImageFuns}
@@ -137,8 +143,13 @@ A set of functions that build a predefined set of minimal Linux distributions im
8GiB image containing Firefox in addition to the default packages:
```nix
-{ pkgs }: with pkgs; with vmTools;
-diskImageFuns.ubuntu2004x86_64 { extraPackages = [ "firefox" ]; size = 8192; }
+{ pkgs }:
+with pkgs;
+with vmTools;
+diskImageFuns.ubuntu2004x86_64 {
+ extraPackages = [ "firefox" ];
+ size = 8192;
+}
```
## `vmTools.diskImageExtraFuns` {#vm-tools-diskImageExtraFuns}
diff --git a/doc/build-helpers/testers.chapter.md b/doc/build-helpers/testers.chapter.md
index fe1a0954348d..9e46635c600c 100644
--- a/doc/build-helpers/testers.chapter.md
+++ b/doc/build-helpers/testers.chapter.md
@@ -98,7 +98,8 @@ It has two modes:
```nix
{
"https://nix\\.dev/manual/nix/[a-z0-9.-]*" = "${nix.doc}/share/doc/nix/manual";
- "https://nixos\\.org/manual/nix/(un)?stable" = "${emptyDirectory}/placeholder-to-disallow-old-nix-docs-urls";
+ "https://nixos\\.org/manual/nix/(un)?stable" =
+ "${emptyDirectory}/placeholder-to-disallow-old-nix-docs-urls";
}
```
@@ -302,18 +303,22 @@ While `testBuildFailure` is designed to keep changes to the original builder's e
# Check that a build fails, and verify the changes made during build
```nix
-runCommand "example" {
- failed = testers.testBuildFailure (runCommand "fail" {} ''
- echo ok-ish >$out
- echo failing though
- exit 3
- '');
-} ''
- grep -F 'ok-ish' $failed/result
- grep -F 'failing though' $failed/testBuildFailure.log
- [[ 3 = $(cat $failed/testBuildFailure.exit) ]]
- touch $out
-''
+runCommand "example"
+ {
+ failed = testers.testBuildFailure (
+ runCommand "fail" { } ''
+ echo ok-ish >$out
+ echo failing though
+ exit 3
+ ''
+ );
+ }
+ ''
+ grep -F 'ok-ish' $failed/result
+ grep -F 'failing though' $failed/testBuildFailure.log
+ [[ 3 = $(cat $failed/testBuildFailure.exit) ]]
+ touch $out
+ ''
```
:::
@@ -396,15 +401,18 @@ testers.testEqualContents {
expected = writeText "expected" ''
foo baz baz
'';
- actual = runCommand "actual" {
- # not really necessary for a package that's in stdenv
- nativeBuildInputs = [ gnused ];
- base = writeText "base" ''
- foo bar baz
- '';
- } ''
- sed -e 's/bar/baz/g' $base >$out
- '';
+ actual =
+ runCommand "actual"
+ {
+ # not really necessary for a package that's in stdenv
+ nativeBuildInputs = [ gnused ];
+ base = writeText "base" ''
+ foo bar baz
+ '';
+ }
+ ''
+ sed -e 's/bar/baz/g' $base >$out
+ '';
}
```
@@ -515,10 +523,11 @@ Otherwise, the build log explains the difference via `nix-diff`.
# Check that two packages produce the same derivation
```nix
-testers.testEqualDerivation
- "The hello package must stay the same when enabling checks."
- hello
- (hello.overrideAttrs(o: { doCheck = true; }))
+testers.testEqualDerivation "The hello package must stay the same when enabling checks." hello (
+ hello.overrideAttrs (o: {
+ doCheck = true;
+ })
+)
```
:::
@@ -586,7 +595,10 @@ testers.runCommand {
curl -o /dev/null https://example.com
touch $out
'';
- nativeBuildInputs = with pkgs; [ cacert curl ];
+ nativeBuildInputs = with pkgs; [
+ cacert
+ curl
+ ];
}
```
@@ -603,15 +615,20 @@ If your test is part of the Nixpkgs repository, or if you need a more general en
# Run a NixOS test using `runNixOSTest`
```nix
-pkgs.testers.runNixOSTest ({ lib, ... }: {
- name = "hello";
- nodes.machine = { pkgs, ... }: {
- environment.systemPackages = [ pkgs.hello ];
- };
- testScript = ''
- machine.succeed("hello")
- '';
-})
+pkgs.testers.runNixOSTest (
+ { lib, ... }:
+ {
+ name = "hello";
+ nodes.machine =
+ { pkgs, ... }:
+ {
+ environment.systemPackages = [ pkgs.hello ];
+ };
+ testScript = ''
+ machine.succeed("hello")
+ '';
+ }
+)
```
:::
@@ -634,10 +651,17 @@ A [NixOS VM test network](https://nixos.org/nixos/manual/index.html#sec-nixos-te
{
name = "my-test";
nodes = {
- machine1 = { lib, pkgs, nodes, ... }: {
- environment.systemPackages = [ pkgs.hello ];
- services.foo.enable = true;
- };
+ machine1 =
+ {
+ lib,
+ pkgs,
+ nodes,
+ ...
+ }:
+ {
+ environment.systemPackages = [ pkgs.hello ];
+ services.foo.enable = true;
+ };
# machine2 = ...;
};
testScript = ''
diff --git a/doc/build-helpers/trivial-build-helpers.chapter.md b/doc/build-helpers/trivial-build-helpers.chapter.md
index 9e818be674b4..48ed99b2fa36 100644
--- a/doc/build-helpers/trivial-build-helpers.chapter.md
+++ b/doc/build-helpers/trivial-build-helpers.chapter.md
@@ -66,15 +66,17 @@ runCommandWith :: {
# Invocation of `runCommandWith`
```nix
-runCommandWith {
- name = "example";
- derivationArgs.nativeBuildInputs = [ cowsay ];
-} ''
- cowsay > $out < $out </my-program.desktop` to the Nix store.
```nix
-{makeDesktopItem}:
+{ makeDesktopItem }:
makeDesktopItem {
name = "my-program";
desktopName = "My Program";
@@ -260,7 +262,10 @@ makeDesktopItem {
mimeTypes = [ "video/mp4" ];
categories = [ "Utility" ];
implements = [ "org.my-program" ];
- keywords = [ "Video" "Player" ];
+ keywords = [
+ "Video"
+ "Player"
+ ];
startupNotify = false;
startupWMClass = "MyProgram";
prefersNonDefaultGPU = false;
@@ -276,18 +281,22 @@ makeDesktopItem {
Override the `hello` package to add a desktop item.
```nix
-{ copyDesktopItems
-, hello
-, makeDesktopItem }:
+{
+ copyDesktopItems,
+ hello,
+ makeDesktopItem,
+}:
hello.overrideAttrs {
nativeBuildInputs = [ copyDesktopItems ];
- desktopItems = [(makeDesktopItem {
- name = "hello";
- desktopName = "Hello";
- exec = "hello";
- })];
+ desktopItems = [
+ (makeDesktopItem {
+ name = "hello";
+ desktopName = "Hello";
+ exec = "hello";
+ })
+ ];
}
```
@@ -446,10 +455,9 @@ The store path will include the name, and it will be a file.
Write the string `Contents of File` to `/nix/store/`:
```nix
-writeText "my-file"
- ''
+writeText "my-file" ''
Contents of File
- ''
+''
```
:::
@@ -486,10 +494,9 @@ The store path will be a directory.
Write the string `Contents of File` to `/nix/store//share/my-file`:
```nix
-writeTextDir "share/my-file"
- ''
+writeTextDir "share/my-file" ''
Contents of File
- ''
+''
```
:::
@@ -528,10 +535,9 @@ The store path will include the name, and it will be a file.
Write the string `Contents of File` to `/nix/store/` and make the file executable.
```nix
-writeScript "my-file"
- ''
+writeScript "my-file" ''
Contents of File
- ''
+''
```
This is equivalent to:
@@ -570,10 +576,9 @@ The store path will include the name, and it will be a directory.
# Usage of `writeScriptBin`
```nix
-writeScriptBin "my-script"
- ''
+writeScriptBin "my-script" ''
echo "hi"
- ''
+''
```
:::
@@ -614,10 +619,9 @@ This function is almost exactly like [](#trivial-builder-writeScript), except th
# Usage of `writeShellScript`
```nix
-writeShellScript "my-script"
- ''
+writeShellScript "my-script" ''
echo "hi"
- ''
+''
```
:::
@@ -657,10 +661,9 @@ This function is a combination of [](#trivial-builder-writeShellScript) and [](#
# Usage of `writeShellScriptBin`
```nix
-writeShellScriptBin "my-script"
- ''
+writeShellScriptBin "my-script" ''
echo "hi"
- ''
+''
```
:::
@@ -685,26 +688,40 @@ These functions concatenate `files` to the Nix store in a single file. This is u
Here are a few examples:
```nix
-
# Writes my-file to /nix/store/
-concatTextFile {
- name = "my-file";
- files = [ drv1 "${drv2}/path/to/file" ];
-}
-# See also the `concatText` helper function below.
+concatTextFile
+ {
+ name = "my-file";
+ files = [
+ drv1
+ "${drv2}/path/to/file"
+ ];
+ }
+ # See also the `concatText` helper function below.
-# Writes executable my-file to /nix/store//bin/my-file
-concatTextFile {
- name = "my-file";
- files = [ drv1 "${drv2}/path/to/file" ];
- executable = true;
- destination = "/bin/my-file";
-}
-# Writes contents of files to /nix/store/
-concatText "my-file" [ file1 file2 ]
+ # Writes executable my-file to /nix/store//bin/my-file
+ concatTextFile
+ {
+ name = "my-file";
+ files = [
+ drv1
+ "${drv2}/path/to/file"
+ ];
+ executable = true;
+ destination = "/bin/my-file";
+ }
+ # Writes contents of files to /nix/store/
+ concatText
+ "my-file"
+ [ file1 file2 ]
-# Writes contents of files to /nix/store/
-concatScript "my-file" [ file1 file2 ]
+ # Writes contents of files to /nix/store/
+ concatScript
+ "my-file"
+ [
+ file1
+ file2
+ ]
```
## `writeShellApplication` {#trivial-builder-writeShellApplication}
@@ -722,7 +739,10 @@ For example, the following shell application can refer to `curl` directly, rathe
writeShellApplication {
name = "show-nixos-org";
- runtimeInputs = [ curl w3m ];
+ runtimeInputs = [
+ curl
+ w3m
+ ];
text = ''
curl -s 'https://nixos.org' | w3m -dump -T text/html
@@ -736,7 +756,14 @@ This can be used to put many derivations into the same directory structure. It w
Here is an example:
```nix
# adds symlinks of hello and stack to current build and prints "links added"
-symlinkJoin { name = "myexample"; paths = [ pkgs.hello pkgs.stack ]; postBuild = "echo links added"; }
+symlinkJoin {
+ name = "myexample";
+ paths = [
+ pkgs.hello
+ pkgs.stack
+ ];
+ postBuild = "echo links added";
+}
```
This creates a derivation with a directory structure like the following:
```
diff --git a/doc/doc-support/package.nix b/doc/doc-support/package.nix
index f316656a585c..023be3a3a116 100644
--- a/doc/doc-support/package.nix
+++ b/doc/doc-support/package.nix
@@ -12,8 +12,10 @@
nixos-render-docs-redirects,
writeShellScriptBin,
nixpkgs ? { },
+ markdown-code-runner,
+ roboto,
+ treefmt,
}:
-
stdenvNoCC.mkDerivation (
finalAttrs:
let
@@ -45,9 +47,13 @@ stdenvNoCC.mkDerivation (
postPatch = ''
ln -s ${optionsJSON}/share/doc/nixos/options.json ./config-options.json
+ ln -s ${treefmt.functionsDoc.markdown} ./packages/treefmt-functions.section.md
+ ln -s ${treefmt.optionsDoc.optionsJSON}/share/doc/nixos/options.json ./treefmt-options.json
'';
buildPhase = ''
+ runHook preBuild
+
substituteInPlace ./languages-frameworks/python.section.md \
--subst-var-by python-interpreter-table "$(<"${pythonInterpreterTable}")"
@@ -83,19 +89,27 @@ stdenvNoCC.mkDerivation (
--section-toc-depth 1 \
manual.md \
out/index.html
+
+ runHook postBuild
'';
installPhase = ''
+ runHook preInstall
+
dest="$out/share/doc/nixpkgs"
mkdir -p "$(dirname "$dest")"
mv out "$dest"
- mv "$dest/index.html" "$dest/manual.html"
+ cp "$dest/index.html" "$dest/manual.html"
+
+ cp ${roboto.src}/web/Roboto\[ital\,wdth\,wght\].ttf "$dest/Roboto.ttf"
cp ${epub} "$dest/nixpkgs-manual.epub"
mkdir -p $out/nix-support/
- echo "doc manual $dest manual.html" >> $out/nix-support/hydra-build-products
+ echo "doc manual $dest index.html" >> $out/nix-support/hydra-build-products
echo "doc manual $dest nixpkgs-manual.epub" >> $out/nix-support/hydra-build-products
+
+ runHook postInstall
'';
passthru = {
@@ -111,7 +125,7 @@ stdenvNoCC.mkDerivation (
let
devmode' = devmode.override {
buildArgs = toString ../.;
- open = "/share/doc/nixpkgs/manual.html";
+ open = "/share/doc/nixpkgs/index.html";
};
nixos-render-docs-redirects' = writeShellScriptBin "redirects" "${lib.getExe nixos-render-docs-redirects} --file ${toString ../redirects.json} $@";
in
@@ -119,10 +133,14 @@ stdenvNoCC.mkDerivation (
packages = [
devmode'
nixos-render-docs-redirects'
+ markdown-code-runner
];
};
- tests.manpage-urls = callPackage ../tests/manpage-urls.nix { };
+ tests = {
+ manpage-urls = callPackage ../tests/manpage-urls.nix { };
+ check-nix-code-blocks = callPackage ../tests/check-nix-code-blocks.nix { };
+ };
};
}
)
diff --git a/doc/functions/generators.section.md b/doc/functions/generators.section.md
index 9d71a0240108..0b073c641e53 100644
--- a/doc/functions/generators.section.md
+++ b/doc/functions/generators.section.md
@@ -13,17 +13,23 @@ let
# specifies how to format a key/value pair
mkKeyValue = generators.mkKeyValueDefault {
# specifies the generated string for a subset of nix values
- mkValueString = v:
- if v == true then ''"yes"''
- else if v == false then ''"no"''
- else if isString v then ''"${v}"''
+ mkValueString =
+ v:
+ if v == true then
+ ''"yes"''
+ else if v == false then
+ ''"no"''
+ else if isString v then
+ ''"${v}"''
# and delegates all other values to the default generator
- else generators.mkValueStringDefault {} v;
+ else
+ generators.mkValueStringDefault { } v;
} ":";
};
+in
# the INI file can now be given as plain old nix values
-in customToINI {
+customToINI {
main = {
pushinfo = true;
autopush = false;
diff --git a/doc/functions/nix-gitignore.section.md b/doc/functions/nix-gitignore.section.md
index 8532ab68ac04..416b5435fa58 100644
--- a/doc/functions/nix-gitignore.section.md
+++ b/doc/functions/nix-gitignore.section.md
@@ -7,20 +7,23 @@
`pkgs.nix-gitignore` exports a number of functions, but you'll most likely need either `gitignoreSource` or `gitignoreSourcePure`. As their first argument, they both accept either 1. a file with gitignore lines or 2. a string with gitignore lines, or 3. a list of either of the two. They will be concatenated into a single big string.
```nix
-{ pkgs ? import {} }: {
+{
+ pkgs ? import { },
+}:
+{
- src = nix-gitignore.gitignoreSource [] ./source;
- # Simplest version
+ src = nix-gitignore.gitignoreSource [ ] ./source;
+ # Simplest version
- src = nix-gitignore.gitignoreSource "supplemental-ignores\n" ./source;
- # This one reads the ./source/.gitignore and concats the auxiliary ignores
+ src = nix-gitignore.gitignoreSource "supplemental-ignores\n" ./source;
+ # This one reads the ./source/.gitignore and concats the auxiliary ignores
- src = nix-gitignore.gitignoreSourcePure "ignore-this\nignore-that\n" ./source;
- # Use this string as gitignore, don't read ./source/.gitignore.
+ src = nix-gitignore.gitignoreSourcePure "ignore-this\nignore-that\n" ./source;
+ # Use this string as gitignore, don't read ./source/.gitignore.
- src = nix-gitignore.gitignoreSourcePure ["ignore-this\nignore-that\n" ~/.gitignore] ./source;
- # It also accepts a list (of strings and paths) that will be concatenated
- # once the paths are turned to strings via readFile.
+ src = nix-gitignore.gitignoreSourcePure [ "ignore-this\nignore-that\n" ~/.gitignore ] ./source;
+ # It also accepts a list (of strings and paths) that will be concatenated
+ # once the paths are turned to strings via readFile.
}
```
diff --git a/doc/functions/prefer-remote-fetch.section.md b/doc/functions/prefer-remote-fetch.section.md
index 8760c100224a..b3d4a84eb4fd 100644
--- a/doc/functions/prefer-remote-fetch.section.md
+++ b/doc/functions/prefer-remote-fetch.section.md
@@ -3,8 +3,7 @@
`prefer-remote-fetch` is an overlay that download sources on remote builder. This is useful when the evaluating machine has a slow upload while the builder can fetch faster directly from the source. To use it, put the following snippet as a new overlay:
```nix
-self: super:
- (super.prefer-remote-fetch self super)
+self: super: (super.prefer-remote-fetch self super)
```
A full configuration example for that sets the overlay up for your own account, could look like this
diff --git a/doc/hooks/autopatchcil.section.md b/doc/hooks/autopatchcil.section.md
new file mode 100644
index 000000000000..f7ed8cb6835f
--- /dev/null
+++ b/doc/hooks/autopatchcil.section.md
@@ -0,0 +1,17 @@
+# autoPatchcilHook {#setup-hook-autopatchcilhook}
+
+This is a special setup hook which helps in packaging .NET assemblies/programs in that it automatically tries to find missing shared library dependencies of .NET assemblies based on the given `buildInputs` and `nativeBuildInputs`.
+
+As the hook needs information for the host where the package will be run on, there's a required environment variable called `autoPatchcilRuntimeId` which should be filled in with the RID (Runtime Identifier) of the machine where the output will be run on. If you're using `buildDotnetModule`, it will fall back to `dotnetRuntimeIds` (which is set to `lib.singleton (if runtimeId != null then runtimeId else systemToDotnetRid stdenvNoCC.hostPlatform.system)`) for you if not provided.
+
+In certain situations you may want to run the main command (`autoPatchcil`) of the setup hook on a file or a set of directories instead of unconditionally patching all outputs. This can be done by setting the `dontAutoPatchcil` environment variable to a non-empty value.
+
+By default, `autoPatchcil` will fail as soon as any .NET assembly requires a dependency which cannot be resolved via the given build inputs. In some situations you might prefer to just leave missing dependencies unpatched and continue to patch the rest. This can be achieved by setting the `autoPatchcilIgnoreMissingDeps` environment variable to a non-empty value. `autoPatchcilIgnoreMissingDeps` can be set to a list like `autoPatchcilIgnoreMissingDeps = [ "libcuda.so.1" "libcudart.so.1" ];` or to `[ "*" ]` to ignore all missing dependencies.
+
+The `autoPatchcil` command requires the `--rid` command line flag, informing the RID (Runtime Identifier) it should assume the assemblies will be executed on, and also recognizes a `--no-recurse` command line flag, which prevents it from recursing into subdirectories.
+
+::: {.note}
+Since, unlike most native binaries, .NET assemblies are compiled once to run on any platform, many assemblies may have PInvoke stubs for libraries that might not be available on the platform that the package will effectively run on. A few examples are assemblies that call native Windows APIs through PInvoke targeting `kernel32`, `gdi32`, `user32`, `shell32` or `ntdll`.
+
+`autoPatchcil` does its best to ignore dependencies from other platforms by checking the requested file extensions, however not all PInvoke stubs provide an extension so in those cases it will be necessary to list those in `autoPatchcilIgnoreMissingDeps` manually.
+:::
diff --git a/doc/hooks/cmake.section.md b/doc/hooks/cmake.section.md
index 5bcc3c980fc5..4aecc8440c64 100644
--- a/doc/hooks/cmake.section.md
+++ b/doc/hooks/cmake.section.md
@@ -33,3 +33,21 @@ The default value is `build`.
#### `dontUseCmakeConfigure` {#dont-use-cmake-configure}
When set to true, don't use the predefined `cmakeConfigurePhase`.
+
+## Controlling CTest invocation {#cmake-ctest}
+
+By default tests are run by make in [`checkPhase`](#ssec-check-phase) or by [ninja](#ninja) if `ninja` is
+available in `nativeBuildInputs`. Makefile and Ninja generators produce the `test` target, which invokes `ctest` under the hood.
+This makes passing additional arguments to `ctest` difficult, so it's possible to invoke it directly in `checkPhase`
+by adding `ctestCheckHook` to `nativeCheckInputs`.
+
+### CTest Variables {#cmake-ctest-variables}
+
+#### `disabledTests` {#cmake-ctest-disabled-tests}
+
+Allows to disable running a list of tests. Note that regular expressions are not supported by `disabledTests`, but
+it can be combined with `--exclude-regex` option.
+
+#### `ctestFlags` {#cmake-ctest-flags}
+
+Additional options passed to `ctest` together with `checkFlags`.
diff --git a/doc/hooks/index.md b/doc/hooks/index.md
index e4b744056c5e..574b7eea8de3 100644
--- a/doc/hooks/index.md
+++ b/doc/hooks/index.md
@@ -7,6 +7,7 @@ The stdenv built-in hooks are documented in [](#ssec-setup-hooks).
```{=include=} sections
autoconf.section.md
automake.section.md
+autopatchcil.section.md
autopatchelf.section.md
aws-c-common.section.md
bmake.section.md
diff --git a/doc/hooks/installShellFiles.section.md b/doc/hooks/installShellFiles.section.md
index f33545477c6d..edaea5895a3b 100644
--- a/doc/hooks/installShellFiles.section.md
+++ b/doc/hooks/installShellFiles.section.md
@@ -99,17 +99,12 @@ failure. To prevent this, guard the completion generation commands.
```nix
{
nativeBuildInputs = [ installShellFiles ];
- postInstall = lib.optionalString (stdenv.hostPlatform.emulatorAvailable buildPackages) (
- let
- emulator = stdenv.hostPlatform.emulator buildPackages;
- in
- ''
- # using named fd
- installShellCompletion --cmd foobar \
- --bash <(${emulator} $out/bin/foobar --bash-completion) \
- --fish <(${emulator} $out/bin/foobar --fish-completion) \
- --zsh <(${emulator} $out/bin/foobar --zsh-completion)
- ''
- );
+ postInstall = lib.optionalString (stdenv.buildPlatform.canExecute stdenv.hostPlatform) ''
+ # using named fd
+ installShellCompletion --cmd foobar \
+ --bash <($out/bin/foobar --bash-completion) \
+ --fish <($out/bin/foobar --fish-completion) \
+ --zsh <($out/bin/foobar --zsh-completion)
+ '';
}
```
diff --git a/doc/hooks/memcached-test-hook.section.md b/doc/hooks/memcached-test-hook.section.md
new file mode 100644
index 000000000000..03fc91ab4bf0
--- /dev/null
+++ b/doc/hooks/memcached-test-hook.section.md
@@ -0,0 +1,55 @@
+
+# `memcachedTestHook` {#sec-memcachedTestHook}
+
+This hook starts a Memcached server during `checkPhase`. Example:
+
+```nix
+{
+ stdenv,
+ memcachedTestHook,
+}:
+stdenv.mkDerivation {
+
+ # ...
+
+ nativeCheckInputs = [
+ memcachedTestHook
+ ];
+}
+```
+
+If you use a custom `checkPhase`, remember to add the `runHook` calls:
+```nix
+{
+ checkPhase = ''
+ runHook preCheck
+
+ # ... your tests
+
+ runHook postCheck
+ '';
+}
+```
+
+## Variables {#sec-memcachedTestHook-variables}
+
+Bash-only variables:
+
+ - `memcachedTestPort`: Port to use by Memcached. Defaults to `11211`
+
+Example usage:
+
+```nix
+{ stdenv, memcachedTestHook }:
+stdenv.mkDerivation {
+
+ # ...
+
+ nativeCheckInputs = [
+ memcachedTestHook
+ ];
+
+ preCheck = ''
+ memcachedTestPort=1234;
+ '';
+}
diff --git a/doc/hooks/mpi-check-hook.section.md b/doc/hooks/mpi-check-hook.section.md
index c182c4cc6195..299069fc89ba 100644
--- a/doc/hooks/mpi-check-hook.section.md
+++ b/doc/hooks/mpi-check-hook.section.md
@@ -11,15 +11,15 @@ the neceesary environment variables to use
Example:
```nix
- { mpiCheckPhaseHook, mpi, ... }:
- {
- # ...
+{ mpiCheckPhaseHook, mpi, ... }:
+{
+ # ...
- nativeCheckInputs = [
- openssh
- mpiCheckPhaseHook
- ];
- }
+ nativeCheckInputs = [
+ openssh
+ mpiCheckPhaseHook
+ ];
+}
```
diff --git a/doc/hooks/patch-rc-path-hooks.section.md b/doc/hooks/patch-rc-path-hooks.section.md
index 5c870dc782c2..080a03da72d6 100644
--- a/doc/hooks/patch-rc-path-hooks.section.md
+++ b/doc/hooks/patch-rc-path-hooks.section.md
@@ -29,7 +29,11 @@ Given a package `foo` containing an init script `this-foo.fish` that depends on
patch the init script for users to source without having the above dependencies in their `PATH`:
```nix
-{ lib, stdenv, patchRcPathFish}:
+{
+ lib,
+ stdenv,
+ patchRcPathFish,
+}:
stdenv.mkDerivation {
# ...
@@ -39,7 +43,13 @@ stdenv.mkDerivation {
];
postFixup = ''
- patchRcPathFish $out/bin/this-foo.fish ${lib.makeBinPath [ coreutils man which ]}
+ patchRcPathFish $out/bin/this-foo.fish ${
+ lib.makeBinPath [
+ coreutils
+ man
+ which
+ ]
+ }
'';
}
```
diff --git a/doc/hooks/postgresql-test-hook.section.md b/doc/hooks/postgresql-test-hook.section.md
index 59d7f7a644c9..8b3cc5f03f3e 100644
--- a/doc/hooks/postgresql-test-hook.section.md
+++ b/doc/hooks/postgresql-test-hook.section.md
@@ -4,7 +4,11 @@
This hook starts a PostgreSQL server during the `checkPhase`. Example:
```nix
-{ stdenv, postgresql, postgresqlTestHook }:
+{
+ stdenv,
+ postgresql,
+ postgresqlTestHook,
+}:
stdenv.mkDerivation {
# ...
@@ -18,13 +22,13 @@ stdenv.mkDerivation {
If you use a custom `checkPhase`, remember to add the `runHook` calls:
```nix
- checkPhase ''
- runHook preCheck
+checkPhase ''
+ runHook preCheck
- # ... your tests
+ # ... your tests
- runHook postCheck
- ''
+ runHook postCheck
+''
```
## Variables {#sec-postgresqlTestHook-variables}
diff --git a/doc/hooks/redis-test-hook.section.md b/doc/hooks/redis-test-hook.section.md
new file mode 100644
index 000000000000..7971b29fa10a
--- /dev/null
+++ b/doc/hooks/redis-test-hook.section.md
@@ -0,0 +1,67 @@
+
+# `redisTestHook` {#sec-redisTestHook}
+
+This hook starts a Redis server during `checkPhase`. Example:
+
+```nix
+{
+ stdenv,
+ redis,
+ redisTestHook,
+}:
+stdenv.mkDerivation {
+
+ # ...
+
+ nativeCheckInputs = [
+ redisTestHook
+ ];
+}
+```
+
+If you use a custom `checkPhase`, remember to add the `runHook` calls:
+```nix
+{
+ checkPhase = ''
+ runHook preCheck
+
+ # ... your tests
+
+ runHook postCheck
+ '';
+}
+```
+
+## Variables {#sec-redisTestHook-variables}
+
+The hook logic will read the following variables and set them to a default value if unset or empty.
+
+Exported variables:
+
+- `REDIS_SOCKET`: UNIX domain socket path
+
+Bash-only variables:
+
+ - `redisTestPort`: Port to use by Redis. Defaults to `6379`
+
+Example usage:
+
+```nix
+{
+ stdenv,
+ redis,
+ redisTestHook,
+}:
+stdenv.mkDerivation {
+
+ # ...
+
+ nativeCheckInputs = [
+ redisTestHook
+ ];
+
+ preCheck = ''
+ redisTestPort=6390;
+ '';
+}
+```
diff --git a/doc/hooks/tauri.section.md b/doc/hooks/tauri.section.md
index 400e493d7fee..290bc3a1d781 100644
--- a/doc/hooks/tauri.section.md
+++ b/doc/hooks/tauri.section.md
@@ -23,46 +23,47 @@ In Nixpkgs, `cargo-tauri.hook` overrides the default build and install phases.
wrapGAppsHook4,
}:
-rustPlatform.buildRustPackage rec {
- # . . .
+rustPlatform.buildRustPackage (finalAttrs: {
+ # ...
- useFetchCargoVendor = true;
cargoHash = "...";
# Assuming our app's frontend uses `npm` as a package manager
npmDeps = fetchNpmDeps {
- name = "${pname}-npm-deps-${version}";
- inherit src;
+ name = "${finalAttrs.pname}-${finalAttrs.version}-npm-deps";
+ inherit (finalAttrs) src;
hash = "...";
};
- nativeBuildInputs = [
- # Pull in our main hook
- cargo-tauri.hook
+ nativeBuildInputs =
+ [
+ # Pull in our main hook
+ cargo-tauri.hook
- # Setup npm
- nodejs
- npmHooks.npmConfigHook
+ # Setup npm
+ nodejs
+ npmHooks.npmConfigHook
- # Make sure we can find our libraries
- pkg-config
- wrapGAppsHook4
- ];
-
- buildInputs =
- [ openssl ]
+ # Make sure we can find our libraries
+ pkg-config
+ ]
++ lib.optionals stdenv.hostPlatform.isLinux [
- glib-networking # Most Tauri apps need networking
- webkitgtk_4_1
+ wrapGAppsHook4
];
+ buildInputs = lib.optionals stdenv.hostPlatform.isLinux [
+ glib-networking # Most Tauri apps need networking
+ openssl
+ webkitgtk_4_1
+ ];
+
# Set our Tauri source directory
cargoRoot = "src-tauri";
# And make sure we build there too
- buildAndTestSubdir = cargoRoot;
+ buildAndTestSubdir = finalAttrs.cargoRoot;
- # . . .
-}
+ # ...
+})
```
## Variables controlling cargo-tauri {#tauri-hook-variables-controlling}
diff --git a/doc/hooks/versionCheckHook.section.md b/doc/hooks/versionCheckHook.section.md
index 6c45b37cd17b..16b1ee97e890 100644
--- a/doc/hooks/versionCheckHook.section.md
+++ b/doc/hooks/versionCheckHook.section.md
@@ -9,7 +9,7 @@ You use it like this:
lib,
stdenv,
versionCheckHook,
- # ...
+# ...
}:
stdenv.mkDerivation (finalAttrs: {
diff --git a/doc/hooks/waf.section.md b/doc/hooks/waf.section.md
index b58887b6b647..437a98920790 100644
--- a/doc/hooks/waf.section.md
+++ b/doc/hooks/waf.section.md
@@ -14,7 +14,7 @@ The variables below are exclusive of `wafHook`.
Location of the `waf` tool. It defaults to `./waf`, to honor software projects that include it directly inside their source trees.
-If `wafPath` doesn't exist, then `wafHook` will copy the `waf` provided from Nixpkgs to it.
+If the file pointed by `wafPath` doesn't exist, then `waf` provided by Nixpkgs will be used.
#### `wafFlags` {#waf-flags}
diff --git a/doc/hooks/zig.section.md b/doc/hooks/zig.section.md
index 8bef293769e5..1c52e9827b41 100644
--- a/doc/hooks/zig.section.md
+++ b/doc/hooks/zig.section.md
@@ -7,9 +7,10 @@ In Nixpkgs, `zig.hook` overrides the default build, check and install phases.
## Example code snippet {#zig-hook-example-code-snippet}
```nix
-{ lib
-, stdenv
-, zig
+{
+ lib,
+ stdenv,
+ zig,
}:
stdenv.mkDerivation {
diff --git a/doc/interoperability/cyclonedx.md b/doc/interoperability/cyclonedx.md
index 7a3dea3dbc2f..3c141b86d9f9 100644
--- a/doc/interoperability/cyclonedx.md
+++ b/doc/interoperability/cyclonedx.md
@@ -63,17 +63,27 @@ For example, the `fetchFromGitHub` is commonly used within Nixpkgs but should be
`nix:fod` properties may be extracted and evaluated to a derivation using code similar to the following, assuming a fictitious function `filterPropertiesToAttrs`:
```nix
-{ pkgs, filterPropertiesToAttrs, properties }:
+{
+ pkgs,
+ filterPropertiesToAttrs,
+ properties,
+}:
let
fodProps = filterPropertiesToAttrs "nix:fod:" properties;
methods = {
fetchzip =
- { name, url, sha256, ... }:
+ {
+ name,
+ url,
+ sha256,
+ ...
+ }:
pkgs.fetchzip {
inherit name url sha256;
};
};
-in methods.${fodProps.method} fodProps
+in
+methods.${fodProps.method} fodProps
```
diff --git a/doc/languages-frameworks/agda.section.md b/doc/languages-frameworks/agda.section.md
index 33fffc60c8db..6b9e577f8119 100644
--- a/doc/languages-frameworks/agda.section.md
+++ b/doc/languages-frameworks/agda.section.md
@@ -48,7 +48,7 @@ You can also reference a GitHub repository
agda.withPackages (p: [
(p.standard-library.overrideAttrs (oldAttrs: {
version = "1.5";
- src = fetchFromGitHub {
+ src = fetchFromGitHub {
repo = "agda-stdlib";
owner = "agda";
rev = "v1.5";
@@ -114,7 +114,9 @@ This can be overridden by a different version of `ghc` as follows:
```nix
agda.withPackages {
- pkgs = [ /* ... */ ];
+ pkgs = [
+ # ...
+ ];
ghc = haskell.compiler.ghcHEAD;
}
```
@@ -132,8 +134,10 @@ A derivation can then be written using `agdaPackages.mkDerivation`. This has sim
Here is an example `default.nix`
```nix
-{ nixpkgs ? }:
-with (import nixpkgs {});
+{
+ nixpkgs ? ,
+}:
+with (import nixpkgs { });
agdaPackages.mkDerivation {
version = "1.0";
pname = "my-agda-lib";
@@ -179,8 +183,12 @@ the Agda package set is small and can (still) be maintained by hand.
To add an Agda package to `nixpkgs`, the derivation should be written to `pkgs/development/libraries/agda/${library-name}/` and an entry should be added to `pkgs/top-level/agda-packages.nix`. Here it is called in a scope with access to all other Agda libraries, so the top line of the `default.nix` can look like:
```nix
-{ mkDerivation, standard-library, fetchFromGitHub }:
-{}
+{
+ mkDerivation,
+ standard-library,
+ fetchFromGitHub,
+}:
+{ }
```
Note that the derivation function is called with `mkDerivation` set to `agdaPackages.mkDerivation`, therefore you
@@ -200,8 +208,12 @@ mkDerivation {
libraryName = "IAL-1.3";
buildPhase = ''
+ runHook preBuild
+
patchShebangs find-deps.sh
make
+
+ runHook postBuild
'';
}
```
diff --git a/doc/languages-frameworks/android.section.md b/doc/languages-frameworks/android.section.md
index b640ba8d5011..9e93d4eda36d 100644
--- a/doc/languages-frameworks/android.section.md
+++ b/doc/languages-frameworks/android.section.md
@@ -8,23 +8,31 @@ supporting features.
Use the `android-studio-full` attribute for a very complete Android SDK, including system images:
```nix
-buildInputs = [ android-studio-full ];
+{
+ buildInputs = [ android-studio-full ];
+}
```
This is identical to:
```nix
-buildInputs = [ androidStudioPackages.stable.full ];
+{
+ buildInputs = [ androidStudioPackages.stable.full ];
+}
```
Alternatively, you can pass composeAndroidPackages to the `withSdk` passthru:
```nix
-buildInputs = [
- (android-studio.withSdk (androidenv.composeAndroidPackages {
- includeNDK = true;
- }).androidsdk)
-];
+{
+ buildInputs = [
+ (android-studio.withSdk
+ (androidenv.composeAndroidPackages {
+ includeNDK = true;
+ }).androidsdk
+ )
+ ];
+}
```
These will export `ANDROID_SDK_ROOT` and `ANDROID_NDK_ROOT` to the SDK and NDK directories
@@ -35,13 +43,20 @@ in the specified Android build environment.
Alternatively, you can deploy the SDK separately with a desired set of plugins, or subsets of an SDK.
```nix
-with import {};
+with import { };
let
androidComposition = androidenv.composeAndroidPackages {
- platformVersions = [ "34" "35" ];
+ platformVersions = [
+ "34"
+ "35"
+ "latest"
+ ];
systemImageTypes = [ "google_apis_playstore" ];
- abiVersions = [ "armeabi-v7a" "arm64-v8a" ];
+ abiVersions = [
+ "armeabi-v7a"
+ "arm64-v8a"
+ ];
includeNDK = true;
includeExtras = [
"extras;google;auto"
@@ -116,7 +131,8 @@ For each requested system image we can specify the following options:
be included. Defaults to `armeabi-v7a` and `arm64-v8a`.
Most of the function arguments have reasonable default settings, preferring the latest
-versions of tools when possible.
+versions of tools when possible. You can additionally specify "latest" for any plugin version
+that you do not care about, and just want the latest of.
You can specify license names:
@@ -165,7 +181,7 @@ We can also deploy subsets of the Android SDK. For example, to only the
`platform-tools` package, you can evaluate the following expression:
```nix
-with import {};
+with import { };
let
androidComposition = androidenv.composeAndroidPackages {
@@ -183,7 +199,7 @@ to use a predefined composition that contains a fairly complete set of Android p
The following Nix expression can be used to deploy the entire SDK:
```nix
-with import {};
+with import { };
androidenv.androidPkgs.androidsdk
```
@@ -191,7 +207,7 @@ androidenv.androidPkgs.androidsdk
It is also possible to use one plugin only:
```nix
-with import {};
+with import { };
androidenv.androidPkgs.platform-tools
```
@@ -205,7 +221,7 @@ An emulator spawn script can be configured by invoking the `emulateApp {}`
function:
```nix
-with import {};
+with import { };
androidenv.emulateApp {
name = "emulate-MyAndroidApp";
@@ -221,7 +237,7 @@ It is also possible to specify an APK to deploy inside the emulator
and the package and activity names to launch it:
```nix
-with import {};
+with import { };
androidenv.emulateApp {
name = "emulate-MyAndroidApp";
@@ -344,7 +360,7 @@ requires. Most newer Android projects use Gradle, and this is included for histo
purposes.
```nix
-with import {};
+with import { };
androidenv.buildApp {
name = "MyAndroidApp";
diff --git a/doc/languages-frameworks/astal.section.md b/doc/languages-frameworks/astal.section.md
index 34022b9c5850..9b2d59a1c009 100644
--- a/doc/languages-frameworks/astal.section.md
+++ b/doc/languages-frameworks/astal.section.md
@@ -11,7 +11,9 @@ ags.bundle {
pname = "hyprpanel";
version = "1.0.0";
- src = fetchFromGitHub { ... };
+ src = fetchFromGitHub {
+ #...
+ };
# change your entry file (default is `app.ts`)
entry = "app.ts";
@@ -32,7 +34,9 @@ ags.bundle {
# GTK 4 support is opt-in
enableGtk4 = true;
- meta = { ... };
+ meta = {
+ #...
+ };
}
```
diff --git a/doc/languages-frameworks/beam.section.md b/doc/languages-frameworks/beam.section.md
index 6c88278def19..ada05b0ddc22 100644
--- a/doc/languages-frameworks/beam.section.md
+++ b/doc/languages-frameworks/beam.section.md
@@ -60,7 +60,10 @@ $ nix-shell -p beamPackages.rebar3
```nix
let
- pkgs = import { config = {}; overlays = []; };
+ pkgs = import {
+ config = { };
+ overlays = [ ];
+ };
in
pkgs.mkShell {
packages = [ pkgs.beamPackages.rebar3 ];
@@ -120,26 +123,28 @@ If there are git dependencies.
{
mixNixDeps = import ./mix.nix {
inherit beamPackages lib;
- overrides = (final: prev: {
- # mix2nix does not support git dependencies yet,
- # so we need to add them manually
- prometheus_ex = beamPackages.buildMix rec {
- name = "prometheus_ex";
- version = "3.0.5";
+ overrides = (
+ final: prev: {
+ # mix2nix does not support git dependencies yet,
+ # so we need to add them manually
+ prometheus_ex = beamPackages.buildMix rec {
+ name = "prometheus_ex";
+ version = "3.0.5";
- # Change the argument src with the git src that you actually need
- src = fetchFromGitLab {
- domain = "git.pleroma.social";
- group = "pleroma";
- owner = "elixir-libraries";
- repo = "prometheus.ex";
- rev = "a4e9beb3c1c479d14b352fd9d6dd7b1f6d7deee5";
- hash = "sha256-U17LlN6aGUKUFnT4XyYXppRN+TvUBIBRHEUsfeIiGOw=";
+ # Change the argument src with the git src that you actually need
+ src = fetchFromGitLab {
+ domain = "git.pleroma.social";
+ group = "pleroma";
+ owner = "elixir-libraries";
+ repo = "prometheus.ex";
+ rev = "a4e9beb3c1c479d14b352fd9d6dd7b1f6d7deee5";
+ hash = "sha256-U17LlN6aGUKUFnT4XyYXppRN+TvUBIBRHEUsfeIiGOw=";
+ };
+ # you can re-use the same beamDeps argument as generated
+ beamDeps = with final; [ prometheus ];
};
- # you can re-use the same beamDeps argument as generated
- beamDeps = with final; [ prometheus ];
- };
- });
+ }
+ );
};
}
```
@@ -195,15 +200,21 @@ let
hash = lib.fakeHash;
mixEnv = ""; # default is "prod", when empty includes all dependencies, such as "dev", "test".
# if you have build time environment variables add them here
- MY_ENV_VAR="my_value";
+ MY_ENV_VAR = "my_value";
};
nodeDependencies = (pkgs.callPackage ./assets/default.nix { }).shell.nodeDependencies;
-in packages.mixRelease {
- inherit src pname version mixFodDeps;
+in
+packages.mixRelease {
+ inherit
+ src
+ pname
+ version
+ mixFodDeps
+ ;
# if you have build time environment variables add them here
- MY_ENV_VAR="my_value";
+ MY_ENV_VAR = "my_value";
postBuild = ''
ln -sf ${nodeDependencies}/lib/node_modules assets/node_modules
@@ -231,7 +242,12 @@ In order to create a service with your release, you could add a `service.nix`
in your project with the following
```nix
-{config, pkgs, lib, ...}:
+{
+ config,
+ pkgs,
+ lib,
+ ...
+}:
let
release = pkgs.callPackage ./default.nix;
@@ -241,10 +257,16 @@ in
{
systemd.services.${release_name} = {
wantedBy = [ "multi-user.target" ];
- after = [ "network.target" "postgresql.service" ];
+ after = [
+ "network.target"
+ "postgresql.service"
+ ];
# note that if you are connecting to a postgres instance on a different host
# postgresql.service should not be included in the requires.
- requires = [ "network-online.target" "postgresql.service" ];
+ requires = [
+ "network-online.target"
+ "postgresql.service"
+ ];
description = "my app";
environment = {
# RELEASE_TMP is used to write the state of the
@@ -292,7 +314,9 @@ in
Usually, we need to create a `shell.nix` file and do our development inside of the environment specified therein. Just install your version of Erlang and any other interpreters, and then use your normal build tools. As an example with Elixir:
```nix
-{ pkgs ? import {} }:
+{
+ pkgs ? import { },
+}:
with pkgs;
let
@@ -311,12 +335,14 @@ If you need to use an overlay to change some attributes of a derivation, e.g. if
```nix
let
- elixir_1_18_1_overlay = (self: super: {
+ elixir_1_18_1_overlay = (
+ self: super: {
elixir_1_18 = super.elixir_1_18.override {
version = "1.18.1";
sha256 = "sha256-AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=";
};
- });
+ }
+ );
pkgs = import { overlays = [ elixir_1_18_1_overlay ]; };
in
with pkgs;
@@ -349,9 +375,7 @@ let
nodePackages.prettier
];
- inputs = basePackages ++ lib.optionals stdenv.hostPlatform.isLinux [ inotify-tools ]
- ++ lib.optionals stdenv.hostPlatform.isDarwin
- (with darwin.apple_sdk.frameworks; [ CoreFoundation CoreServices ]);
+ inputs = basePackages ++ lib.optionals stdenv.hostPlatform.isLinux [ inotify-tools ];
# define shell startup command
hooks = ''
@@ -380,7 +404,8 @@ let
export ENV_VAR="your_env_var"
'';
-in mkShell {
+in
+mkShell {
buildInputs = inputs;
shellHook = hooks;
}
diff --git a/doc/languages-frameworks/bower.section.md b/doc/languages-frameworks/bower.section.md
index 346852c49426..3783773e2bf2 100644
--- a/doc/languages-frameworks/bower.section.md
+++ b/doc/languages-frameworks/bower.section.md
@@ -24,11 +24,15 @@ Running `bower2nix` will produce something like the following output:
```nix
{ fetchbower, buildEnv }:
-buildEnv { name = "bower-env"; ignoreCollisions = true; paths = [
- (fetchbower "angular" "1.5.3" "~1.5.0" "1749xb0firxdra4rzadm4q9x90v6pzkbd7xmcyjk6qfza09ykk9y")
- (fetchbower "bootstrap" "3.3.6" "~3.3.6" "1vvqlpbfcy0k5pncfjaiskj3y6scwifxygfqnw393sjfxiviwmbv")
- (fetchbower "jquery" "2.2.2" "1.9.1 - 2" "10sp5h98sqwk90y4k6hbdviwqzvzwqf47r3r51pakch5ii2y7js1")
-]; }
+buildEnv {
+ name = "bower-env";
+ ignoreCollisions = true;
+ paths = [
+ (fetchbower "angular" "1.5.3" "~1.5.0" "1749xb0firxdra4rzadm4q9x90v6pzkbd7xmcyjk6qfza09ykk9y")
+ (fetchbower "bootstrap" "3.3.6" "~3.3.6" "1vvqlpbfcy0k5pncfjaiskj3y6scwifxygfqnw393sjfxiviwmbv")
+ (fetchbower "jquery" "2.2.2" "1.9.1 - 2" "10sp5h98sqwk90y4k6hbdviwqzvzwqf47r3r51pakch5ii2y7js1")
+ ];
+}
```
Using the `bower2nix` command line arguments, the output can be redirected to a file. A name like `bower-packages.nix` would be fine.
@@ -80,8 +84,12 @@ gulp.task('build', [], function () {
### Example Full example — default.nix {#ex-buildBowerComponentsDefaultNix}
```nix
-{ myWebApp ? { outPath = ./.; name = "myWebApp"; }
-, pkgs ? import {}
+{
+ myWebApp ? {
+ outPath = ./.;
+ name = "myWebApp";
+ },
+ pkgs ? import { },
}:
pkgs.stdenv.mkDerivation {
@@ -90,16 +98,24 @@ pkgs.stdenv.mkDerivation {
buildInputs = [ pkgs.nodePackages.gulp ];
- bowerComponents = pkgs.buildBowerComponents { # note 1
+ bowerComponents = pkgs.buildBowerComponents {
+ # note 1
name = "my-web-app";
generated = ./bower-packages.nix;
src = myWebApp;
};
+ nativeBuildInputs = [
+ writableTmpDirAsHomeHook # note 3
+ ];
+
buildPhase = ''
+ runHook preBuild
+
cp --reflink=auto --no-preserve=mode -R $bowerComponents/bower_components . # note 2
- export HOME=$PWD # note 3
${pkgs.nodePackages.gulp}/bin/gulp build # note 4
+
+ runHook postBuild
'';
installPhase = "mv gulpdist $out";
diff --git a/doc/languages-frameworks/chicken.section.md b/doc/languages-frameworks/chicken.section.md
index d1e12e2cccbc..78a4469a8e1d 100644
--- a/doc/languages-frameworks/chicken.section.md
+++ b/doc/languages-frameworks/chicken.section.md
@@ -60,19 +60,23 @@ all the other eggs:
```nix
let
- myChickenPackages = pkgs.chickenPackages.overrideScope (self: super: {
+ myChickenPackages = pkgs.chickenPackages.overrideScope (
+ self: super: {
# The chicken package itself can be overridden to effect the whole ecosystem.
# chicken = super.chicken.overrideAttrs {
# src = ...
# };
- chickenEggs = super.chickenEggs.overrideScope (eggself: eggsuper: {
- srfi-180 = eggsuper.srfi-180.overrideAttrs {
- # path to a local copy of srfi-180
- src = <...>;
- };
- });
- });
+ chickenEggs = super.chickenEggs.overrideScope (
+ eggself: eggsuper: {
+ srfi-180 = eggsuper.srfi-180.overrideAttrs {
+ # path to a local copy of srfi-180
+ src = <...>;
+ };
+ }
+ );
+ }
+ );
in
# Here, `myChickenPackages.chickenEggs.json-rpc`, which depends on `srfi-180` will use
# the local copy of `srfi-180`.
diff --git a/doc/languages-frameworks/coq.section.md b/doc/languages-frameworks/coq.section.md
index ca983eec4f0d..c08ed2bc6f43 100644
--- a/doc/languages-frameworks/coq.section.md
+++ b/doc/languages-frameworks/coq.section.md
@@ -54,35 +54,78 @@ It also takes other standard `mkDerivation` attributes, they are added as such,
Here is a simple package example. It is a pure Coq library, thus it depends on Coq. It builds on the Mathematical Components library, thus it also takes some `mathcomp` derivations as `extraBuildInputs`.
```nix
-{ lib, mkCoqDerivation, version ? null
-, coq, mathcomp, mathcomp-finmap, mathcomp-bigenough }:
+{
+ lib,
+ mkCoqDerivation,
+ version ? null,
+ coq,
+ mathcomp,
+ mathcomp-finmap,
+ mathcomp-bigenough,
+}:
mkCoqDerivation {
- /* namePrefix leads to e.g. `name = coq8.11-mathcomp1.11-multinomials-1.5.2` */
- namePrefix = [ "coq" "mathcomp" ];
+ # namePrefix leads to e.g. `name = coq8.11-mathcomp1.11-multinomials-1.5.2`
+ namePrefix = [
+ "coq"
+ "mathcomp"
+ ];
pname = "multinomials";
owner = "math-comp";
inherit version;
- defaultVersion = with lib.versions; lib.switch [ coq.version mathcomp.version ] [
- { cases = [ (range "8.7" "8.12") (isEq "1.11") ]; out = "1.5.2"; }
- { cases = [ (range "8.7" "8.11") (range "1.8" "1.10") ]; out = "1.5.0"; }
- { cases = [ (range "8.7" "8.10") (range "1.8" "1.10") ]; out = "1.4"; }
- { cases = [ (isEq "8.6") (range "1.6" "1.7") ]; out = "1.1"; }
- ] null;
+ defaultVersion =
+ with lib.versions;
+ lib.switch
+ [ coq.version mathcomp.version ]
+ [
+ {
+ cases = [
+ (range "8.7" "8.12")
+ (isEq "1.11")
+ ];
+ out = "1.5.2";
+ }
+ {
+ cases = [
+ (range "8.7" "8.11")
+ (range "1.8" "1.10")
+ ];
+ out = "1.5.0";
+ }
+ {
+ cases = [
+ (range "8.7" "8.10")
+ (range "1.8" "1.10")
+ ];
+ out = "1.4";
+ }
+ {
+ cases = [
+ (isEq "8.6")
+ (range "1.6" "1.7")
+ ];
+ out = "1.1";
+ }
+ ]
+ null;
release = {
"1.5.2".hash = "sha256-mjCx9XKa38Nz9E6wNK7YSqHdJ7YTua5fD3d6J4e7WpU=";
"1.5.1".hash = "sha256-Q8tm0y2FQAt2V1kZYkDlHWRia/lTvXAMVjdmzEV11I4=";
"1.5.0".hash = "sha256-HIK0f21G69oEW8JG46gSBde/Q2LR3GiBCv680gHbmRg=";
- "1.5.0".rev = "1.5";
- "1.4".hash = "sha256-F9g3MSIr3B6UZ3p8QWjz3/Jpw9sudJ+KRlvjiHSO024=";
- "1.3".hash = "sha256-BPJTlAL0ETHvLMBslE0KFVt3DNoaGuMrHt2SBGyJe1A=";
- "1.2".hash = "sha256-mHXBXSLYO4BN+jfN50y/+XCx0Qq5g4Ac2Y/qlsbgAdY=";
- "1.1".hash = "sha256-ejAsMQbB/LtU9j+g160VdGXULrCe9s0gBWzyhKqmCuE=";
- "1.0".hash = "sha256-tZTOltEBBKWciDxDMs/Ye4Jnq/33CANrHJ4FBMPtq+I=";
+ "1.5.0".rev = "1.5";
+ "1.4".hash = "sha256-F9g3MSIr3B6UZ3p8QWjz3/Jpw9sudJ+KRlvjiHSO024=";
+ "1.3".hash = "sha256-BPJTlAL0ETHvLMBslE0KFVt3DNoaGuMrHt2SBGyJe1A=";
+ "1.2".hash = "sha256-mHXBXSLYO4BN+jfN50y/+XCx0Qq5g4Ac2Y/qlsbgAdY=";
+ "1.1".hash = "sha256-ejAsMQbB/LtU9j+g160VdGXULrCe9s0gBWzyhKqmCuE=";
+ "1.0".hash = "sha256-tZTOltEBBKWciDxDMs/Ye4Jnq/33CANrHJ4FBMPtq+I=";
};
- propagatedBuildInputs =
- [ mathcomp.ssreflect mathcomp.algebra mathcomp-finmap mathcomp-bigenough ];
+ propagatedBuildInputs = [
+ mathcomp.ssreflect
+ mathcomp.algebra
+ mathcomp-finmap
+ mathcomp-bigenough
+ ];
meta = {
description = "Coq/SSReflect Library for Monoidal Rings and Multinomials";
@@ -124,12 +167,10 @@ The `overrideCoqDerivation` function lets you easily change arguments to `mkCoqD
For example, here is how you could locally add a new release of the `multinomials` library, and set the `defaultVersion` to use this release:
```nix
-coqPackages.lib.overrideCoqDerivation
- {
- defaultVersion = "2.0";
- release."2.0".hash = "sha256-czoP11rtrIM7+OLdMisv2EF7n/IbGuwFxHiPtg3qCNM=";
- }
- coqPackages.multinomials
+coqPackages.lib.overrideCoqDerivation {
+ defaultVersion = "2.0";
+ release."2.0".hash = "sha256-czoP11rtrIM7+OLdMisv2EF7n/IbGuwFxHiPtg3qCNM=";
+} coqPackages.multinomials
```
### `.overrideAttrs` {#coq-overrideAttrs}
@@ -140,8 +181,10 @@ For instance, here is how you could add some code to be performed in the derivat
```nix
coqPackages.multinomials.overrideAttrs (oldAttrs: {
- postInstall = oldAttrs.postInstall or "" + ''
- echo "you can do anything you want here"
- '';
+ postInstall =
+ oldAttrs.postInstall or ""
+ + ''
+ echo "you can do anything you want here"
+ '';
})
```
diff --git a/doc/languages-frameworks/crystal.section.md b/doc/languages-frameworks/crystal.section.md
index 9953f357048a..5ea70319dd49 100644
--- a/doc/languages-frameworks/crystal.section.md
+++ b/doc/languages-frameworks/crystal.section.md
@@ -18,7 +18,7 @@ This should have generated a `shards.nix` file.
Next create a Nix file for your derivation and use `pkgs.crystal.buildCrystalPackage` as follows:
```nix
-with import {};
+with import { };
crystal.buildCrystalPackage rec {
pname = "mint";
version = "0.5.0";
@@ -51,14 +51,17 @@ Additionally you can override the default `crystal build` options (which are cur
```nix
{
- crystalBinaries.mint.options = [ "--release" "--verbose" ];
+ crystalBinaries.mint.options = [
+ "--release"
+ "--verbose"
+ ];
}
```
Depending on the project, you might need additional steps to get it to compile successfully. In Mint's case, we need to link against openssl, so in the end the Nix file looks as follows:
```nix
-with import {};
+with import { };
crystal.buildCrystalPackage rec {
version = "0.5.0";
pname = "mint";
diff --git a/doc/languages-frameworks/cuda.section.md b/doc/languages-frameworks/cuda.section.md
index bb394a243b79..091fc57a53a0 100644
--- a/doc/languages-frameworks/cuda.section.md
+++ b/doc/languages-frameworks/cuda.section.md
@@ -12,11 +12,13 @@ compatible are available as well. For example, there can be a
To use one or more CUDA packages in an expression, give the expression a `cudaPackages` parameter, and in case CUDA is optional
```nix
-{ config
-, cudaSupport ? config.cudaSupport
-, cudaPackages ? { }
-, ...
-}: {}
+{
+ config,
+ cudaSupport ? config.cudaSupport,
+ cudaPackages ? { },
+ ...
+}:
+{ }
```
When using `callPackage`, you can choose to pass in a different variant, e.g.
@@ -32,11 +34,15 @@ package set to make it the default. This guarantees you get a consistent package
set.
```nix
{
- mypkg = let
- cudaPackages = cudaPackages_11_5.overrideScope (final: prev: {
- cudnn = prev.cudnn_8_3;
- });
- in callPackage { inherit cudaPackages; };
+ mypkg =
+ let
+ cudaPackages = cudaPackages_11_5.overrideScope (
+ final: prev: {
+ cudnn = prev.cudnn_8_3;
+ }
+ );
+ in
+ callPackage { inherit cudaPackages; };
}
```
diff --git a/doc/languages-frameworks/cuelang.section.md b/doc/languages-frameworks/cuelang.section.md
index 70329b15fd7d..bbef29ee78a8 100644
--- a/doc/languages-frameworks/cuelang.section.md
+++ b/doc/languages-frameworks/cuelang.section.md
@@ -27,13 +27,11 @@ Nixpkgs provides a `pkgs.writeCueValidator` helper, which will write a validatio
Here is an example:
```nix
-pkgs.writeCueValidator
- (pkgs.writeText "schema.cue" ''
- #Def1: {
- field1: string
- }
- '')
- { document = "#Def1"; }
+pkgs.writeCueValidator (pkgs.writeText "schema.cue" ''
+ #Def1: {
+ field1: string
+ }
+'') { document = "#Def1"; }
```
- The first parameter is the Cue schema file.
@@ -43,19 +41,19 @@ pkgs.writeCueValidator
Another example, given the following `validator.nix` :
```nix
-{ pkgs ? import {} }:
+{
+ pkgs ? import { },
+}:
let
- genericValidator = version:
- pkgs.writeCueValidator
- (pkgs.writeText "schema.cue" ''
+ genericValidator =
+ version:
+ pkgs.writeCueValidator (pkgs.writeText "schema.cue" ''
#Version1: {
field1: string
}
#Version2: #Version1 & {
field1: "unused"
- }''
- )
- { document = "#Version${toString version}"; };
+ }'') { document = "#Version${toString version}"; };
in
{
validateV1 = genericValidator 1;
diff --git a/doc/languages-frameworks/dart.section.md b/doc/languages-frameworks/dart.section.md
index dfb456ba1941..32a9a0b6f653 100644
--- a/doc/languages-frameworks/dart.section.md
+++ b/doc/languages-frameworks/dart.section.md
@@ -30,7 +30,11 @@ The `dart` commands run can be overridden through `pubGetScript` and `dartCompil
Dart supports multiple [outputs types](https://dart.dev/tools/dart-compile#types-of-output), you can choose between them using `dartOutputType` (defaults to `exe`). If you want to override the binaries path or the source path they come from, you can use `dartEntryPoints`. Outputs that require a runtime will automatically be wrapped with the relevant runtime (`dartaotruntime` for `aot-snapshot`, `dart run` for `jit-snapshot` and `kernel`, `node` for `js`), this can be overridden through `dartRuntimeCommand`.
```nix
-{ lib, buildDartApplication, fetchFromGitHub }:
+{
+ lib,
+ buildDartApplication,
+ fetchFromGitHub,
+}:
buildDartApplication rec {
pname = "dart-sass";
@@ -38,8 +42,8 @@ buildDartApplication rec {
src = fetchFromGitHub {
owner = "sass";
- repo = pname;
- rev = version;
+ repo = "dart-sass";
+ tag = version;
hash = "sha256-U6enz8yJcc4Wf8m54eYIAnVg/jsGi247Wy8lp1r1wg4=";
};
@@ -101,7 +105,7 @@ See the [Dart documentation](#ssec-dart-applications) for more details on requir
`flutter` in Nixpkgs always points to `flutterPackages.stable`, which is the latest packaged version. To avoid unforeseen breakage during upgrade, packages in Nixpkgs should use a specific flutter version, such as `flutter319` and `flutter322`, instead of using `flutter` directly.
```nix
-{ flutter322, fetchFromGitHub }:
+{ flutter322, fetchFromGitHub }:
flutter322.buildFlutterApplication {
pname = "firmware-updater";
diff --git a/doc/languages-frameworks/dhall.section.md b/doc/languages-frameworks/dhall.section.md
index 8d85c9f1daf7..469f1a4fe3d1 100644
--- a/doc/languages-frameworks/dhall.section.md
+++ b/doc/languages-frameworks/dhall.section.md
@@ -90,7 +90,7 @@ buildDhallPackage {
let
nixpkgs = builtins.fetchTarball {
- url = "https://github.com/NixOS/nixpkgs/archive/94b2848559b12a8ed1fe433084686b2a81123c99.tar.gz";
+ url = "https://github.com/NixOS/nixpkgs/archive/94b2848559b12a8ed1fe433084686b2a81123c99.tar.gz";
hash = "sha256-B4Q3c6IvTLg3Q92qYa8y+i4uTaphtFdjp+Ir3QQjdN0=";
};
@@ -100,15 +100,17 @@ let
overlay = self: super: {
dhallPackages = super.dhallPackages.override (old: {
- overrides =
- self.lib.composeExtensions (old.overrides or (_: _: {})) dhallOverlay;
+ overrides = self.lib.composeExtensions (old.overrides or (_: _: { })) dhallOverlay;
});
};
- pkgs = import nixpkgs { config = {}; overlays = [ overlay ]; };
+ pkgs = import nixpkgs {
+ config = { };
+ overlays = [ overlay ];
+ };
in
- pkgs
+pkgs
```
… which we can then build using this command:
@@ -190,8 +192,7 @@ Dhall overlay like this:
{
dhallOverrides = self: super: {
# Enable source for all Dhall packages
- buildDhallPackage =
- args: super.buildDhallPackage (args // { source = true; });
+ buildDhallPackage = args: super.buildDhallPackage (args // { source = true; });
true = self.callPackage ./true.nix { };
};
diff --git a/doc/languages-frameworks/dlang.section.md b/doc/languages-frameworks/dlang.section.md
index 6e9edefc5e0f..fa211dc6a43d 100644
--- a/doc/languages-frameworks/dlang.section.md
+++ b/doc/languages-frameworks/dlang.section.md
@@ -22,7 +22,7 @@ buildDubPackage rec {
src = fetchFromGitHub {
owner = "CyberShadow";
repo = "btdu";
- rev = "v${version}";
+ tag = "v${version}";
hash = "sha256-3sSZq+5UJH02IO0Y1yL3BLHDb4lk8k6awb5ZysBQciE=";
};
diff --git a/doc/languages-frameworks/dotnet.section.md b/doc/languages-frameworks/dotnet.section.md
index 23e3be414573..ea3448542f5d 100644
--- a/doc/languages-frameworks/dotnet.section.md
+++ b/doc/languages-frameworks/dotnet.section.md
@@ -6,7 +6,7 @@ For local development, it's recommended to use nix-shell to create a dotnet envi
```nix
# shell.nix
-with import {};
+with import { };
mkShell {
name = "dotnet-env";
@@ -21,15 +21,18 @@ mkShell {
It's very likely that more than one sdk will be needed on a given project. Dotnet provides several different frameworks (E.g dotnetcore, aspnetcore, etc.) as well as many versions for a given framework. Normally, dotnet is able to fetch a framework and install it relative to the executable. However, this would mean writing to the nix store in nixpkgs, which is read-only. To support the many-sdk use case, one can compose an environment using `dotnetCorePackages.combinePackages`:
```nix
-with import {};
+with import { };
mkShell {
name = "dotnet-env";
packages = [
- (with dotnetCorePackages; combinePackages [
- sdk_8_0
- sdk_9_0
- ])
+ (
+ with dotnetCorePackages;
+ combinePackages [
+ sdk_8_0
+ sdk_9_0
+ ]
+ )
];
}
```
@@ -137,11 +140,19 @@ When packaging a new application, you need to fetch its dependencies. Create an
Here is an example `default.nix`, using some of the previously discussed arguments:
```nix
-{ lib, buildDotnetModule, dotnetCorePackages, ffmpeg }:
+{
+ lib,
+ buildDotnetModule,
+ dotnetCorePackages,
+ ffmpeg,
+}:
let
- referencedProject = import ../../bar { /* ... */ };
-in buildDotnetModule rec {
+ referencedProject = import ../../bar {
+ # ...
+ };
+in
+buildDotnetModule rec {
pname = "someDotnetApplication";
version = "0.1";
@@ -156,7 +167,7 @@ in buildDotnetModule rec {
dotnet-runtime = dotnetCorePackages.runtime_8_0;
executables = [ "foo" ]; # This wraps "$out/lib/$pname/foo" to `$out/bin/foo`.
- executables = []; # Don't install any executables.
+ executables = [ ]; # Don't install any executables.
packNupkg = true; # This packs the project as "foo-0.1.nupkg" at `$out/share`.
diff --git a/doc/languages-frameworks/emscripten.section.md b/doc/languages-frameworks/emscripten.section.md
index d1ed62d0503f..0fca82f70aed 100644
--- a/doc/languages-frameworks/emscripten.section.md
+++ b/doc/languages-frameworks/emscripten.section.md
@@ -41,56 +41,75 @@ One advantage is that when `pkgs.zlib` is updated, it will automatically update
(pkgs.zlib.override {
stdenv = pkgs.emscriptenStdenv;
}).overrideAttrs
-(old: rec {
- buildInputs = old.buildInputs ++ [ pkg-config ];
- # we need to reset this setting!
- env = (old.env or { }) // { NIX_CFLAGS_COMPILE = ""; };
- configurePhase = ''
- # FIXME: Some tests require writing at $HOME
- HOME=$TMPDIR
- runHook preConfigure
+ (old: {
+ buildInputs = old.buildInputs ++ [ pkg-config ];
+ # we need to reset this setting!
+ env = (old.env or { }) // {
+ NIX_CFLAGS_COMPILE = "";
+ };
- #export EMCC_DEBUG=2
- emconfigure ./configure --prefix=$out --shared
+ configurePhase = ''
+ # FIXME: Some tests require writing at $HOME
+ HOME=$TMPDIR
+ runHook preConfigure
- runHook postConfigure
- '';
- dontStrip = true;
- outputs = [ "out" ];
- buildPhase = ''
- emmake make
- '';
- installPhase = ''
- emmake make install
- '';
- checkPhase = ''
- echo "================= testing zlib using node ================="
+ #export EMCC_DEBUG=2
+ emconfigure ./configure --prefix=$out --shared
- echo "Compiling a custom test"
- set -x
- emcc -O2 -s EMULATE_FUNCTION_POINTER_CASTS=1 test/example.c -DZ_SOLO \
- libz.so.${old.version} -I . -o example.js
+ runHook postConfigure
+ '';
- echo "Using node to execute the test"
- ${pkgs.nodejs}/bin/node ./example.js
+ dontStrip = true;
+ outputs = [ "out" ];
- set +x
- if [ $? -ne 0 ]; then
- echo "test failed for some reason"
- exit 1;
- else
- echo "it seems to work! very good."
- fi
- echo "================= /testing zlib using node ================="
- '';
+ buildPhase = ''
+ runHook preBuild
- postPatch = pkgs.lib.optionalString pkgs.stdenv.hostPlatform.isDarwin ''
- substituteInPlace configure \
- --replace-fail '/usr/bin/libtool' 'ar' \
- --replace-fail 'AR="libtool"' 'AR="ar"' \
- --replace-fail 'ARFLAGS="-o"' 'ARFLAGS="-r"'
- '';
-})
+ emmake make
+
+ runHook postBuild
+ '';
+
+ installPhase = ''
+ runHook preInstall
+
+ emmake make install
+
+ runHook postInstall
+ '';
+
+ checkPhase = ''
+ runHook preCheck
+
+ echo "================= testing zlib using node ================="
+
+ echo "Compiling a custom test"
+ set -x
+ emcc -O2 -s EMULATE_FUNCTION_POINTER_CASTS=1 test/example.c -DZ_SOLO \
+ libz.so.${old.version} -I . -o example.js
+
+ echo "Using node to execute the test"
+ ${pkgs.nodejs}/bin/node ./example.js
+
+ set +x
+ if [ $? -ne 0 ]; then
+ echo "test failed for some reason"
+ exit 1;
+ else
+ echo "it seems to work! very good."
+ fi
+ echo "================= /testing zlib using node ================="
+
+ runHook postCheck
+ '';
+
+ postPatch = pkgs.lib.optionalString pkgs.stdenv.hostPlatform.isDarwin ''
+ substituteInPlace configure \
+ --replace-fail '/usr/bin/libtool' 'ar' \
+ --replace-fail 'AR="libtool"' 'AR="ar"' \
+ --replace-fail 'ARFLAGS="-o"' 'ARFLAGS="-r"'
+ '';
+ })
```
:::{.example #usage-2-pkgs.buildemscriptenpackage}
@@ -100,11 +119,27 @@ One advantage is that when `pkgs.zlib` is updated, it will automatically update
This `xmlmirror` example features an Emscripten package that is defined completely from this context and no `pkgs.zlib.override` is used.
```nix
-pkgs.buildEmscriptenPackage rec {
- name = "xmlmirror";
+pkgs.buildEmscriptenPackage {
+ pname = "xmlmirror";
+ version = "1.2.3";
- buildInputs = [ pkg-config autoconf automake libtool gnumake libxml2 nodejs openjdk json_c ];
- nativeBuildInputs = [ pkg-config zlib ];
+ buildInputs = [
+ pkg-config
+ autoconf
+ automake
+ libtool
+ gnumake
+ libxml2
+ nodejs
+ openjdk
+ json_c
+ ];
+
+ nativeBuildInputs = [
+ pkg-config
+ writableTmpDirAsHomeHook
+ zlib
+ ];
src = pkgs.fetchgit {
url = "https://gitlab.com/odfplugfest/xmlmirror.git";
@@ -113,6 +148,8 @@ pkgs.buildEmscriptenPackage rec {
};
configurePhase = ''
+ runHook preConfigure
+
rm -f fastXmlLint.js*
# a fix for ERROR:root:For asm.js, TOTAL_MEMORY must be a multiple of 16MB, was 234217728
# https://gitlab.com/odfplugfest/xmlmirror/issues/8
@@ -122,16 +159,26 @@ pkgs.buildEmscriptenPackage rec {
sed -e "s/\$(JSONC_LDFLAGS) \$(ZLIB_LDFLAGS) \$(LIBXML20_LDFLAGS)/\$(JSONC_LDFLAGS) \$(LIBXML20_LDFLAGS) \$(ZLIB_LDFLAGS) /g" -i Makefile.emEnv
# https://gitlab.com/odfplugfest/xmlmirror/issues/11
sed -e "s/-o fastXmlLint.js/-s EXTRA_EXPORTED_RUNTIME_METHODS='[\"ccall\", \"cwrap\"]' -o fastXmlLint.js/g" -i Makefile.emEnv
+
+ runHook postConfigure
'';
buildPhase = ''
- HOME=$TMPDIR
+ runHook preBuild
+
make -f Makefile.emEnv
+
+ runHook postBuild
'';
- outputs = [ "out" "doc" ];
+ outputs = [
+ "out"
+ "doc"
+ ];
installPhase = ''
+ runHook preInstall
+
mkdir -p $out/share
mkdir -p $doc/share/${name}
@@ -145,9 +192,13 @@ pkgs.buildEmscriptenPackage rec {
cp *.json $out/share
cp *.rng $out/share
cp README.md $doc/share/${name}
+ runHook postInstall
'';
- checkPhase = ''
+ checkPhase = ''
+ runHook preCheck
+
+ runHook postCheck
'';
}
```
diff --git a/doc/languages-frameworks/factor.section.md b/doc/languages-frameworks/factor.section.md
index 62db50e1ff6a..0d67eede4d34 100644
--- a/doc/languages-frameworks/factor.section.md
+++ b/doc/languages-frameworks/factor.section.md
@@ -125,7 +125,7 @@ factorPackages.buildFactorApplication (finalAttrs: {
version = "1.0";
src = fetchurl {
- url = "https://some-forge.org/foo-${finalAttrs.version}.tar.gz"
+ url = "https://some-forge.org/foo-${finalAttrs.version}.tar.gz";
};
})
```
diff --git a/doc/languages-frameworks/gnome.section.md b/doc/languages-frameworks/gnome.section.md
index 8374d55ac2f7..718c296bce80 100644
--- a/doc/languages-frameworks/gnome.section.md
+++ b/doc/languages-frameworks/gnome.section.md
@@ -96,7 +96,12 @@ Given the requirements above, the package expression would become messy quickly:
--prefix XDG_DATA_DIRS : "$out/share/gsettings-schemas/${name}" \
--prefix XDG_DATA_DIRS : "${gsettings-desktop-schemas}/share/gsettings-schemas/${gsettings-desktop-schemas.name}" \
--prefix XDG_DATA_DIRS : "${hicolor-icon-theme}/share" \
- --prefix GI_TYPELIB_PATH : "${lib.makeSearchPath "lib/girepository-1.0" [ pango json-glib ]}"
+ --prefix GI_TYPELIB_PATH : "${
+ lib.makeSearchPath "lib/girepository-1.0" [
+ pango
+ json-glib
+ ]
+ }"
done
'';
}
@@ -209,7 +214,7 @@ stdenv.mkDerivation {
You can rely on applications depending on the library setting the necessary environment variables but that is often easy to miss. Instead we recommend to patch the paths in the source code whenever possible. Here are some examples:
-- []{#ssec-gnome-common-issues-unwrappable-package-gnome-shell-ext} [Replacing a `GI_TYPELIB_PATH` in GNOME Shell extension](https://github.com/NixOS/nixpkgs/blob/7bb8f05f12ca3cff9da72b56caa2f7472d5732bc/pkgs/desktops/gnome-3/core/gnome-shell-extensions/default.nix#L21-L24) – we are using `substituteAll` to include the path to a typelib into a patch.
+- []{#ssec-gnome-common-issues-unwrappable-package-gnome-shell-ext} [Replacing a `GI_TYPELIB_PATH` in GNOME Shell extension](https://github.com/NixOS/nixpkgs/blob/e981466fbb08e6231a1377539ff17fbba3270fda/pkgs/by-name/gn/gnome-shell-extensions/package.nix#L25-L32) – we are using `replaceVars` to include the path to a typelib into a patch.
- []{#ssec-gnome-common-issues-unwrappable-package-gsettings} The following examples are hardcoding GSettings schema paths. To get the schema paths we use the functions
@@ -217,7 +222,7 @@ You can rely on applications depending on the library setting the necessary envi
* `glib.makeSchemaPath` Takes a package output like `$out` and a derivation name. You should use this if the schemas you need to hardcode are in the same derivation.
- []{#ssec-gnome-common-issues-unwrappable-package-gsettings-vala} [Hard-coding GSettings schema path in Vala plug-in (dynamically loaded library)](https://github.com/NixOS/nixpkgs/blob/7bb8f05f12ca3cff9da72b56caa2f7472d5732bc/pkgs/desktops/pantheon/apps/elementary-files/default.nix#L78-L86) – here, `substituteAll` cannot be used since the schema comes from the same package preventing us from pass its path to the function, probably due to a [Nix bug](https://github.com/NixOS/nix/issues/1846).
+ []{#ssec-gnome-common-issues-unwrappable-package-gsettings-vala} [Hard-coding GSettings schema path in Vala plug-in (dynamically loaded library)](https://github.com/NixOS/nixpkgs/blob/7bb8f05f12ca3cff9da72b56caa2f7472d5732bc/pkgs/desktops/pantheon/apps/elementary-files/default.nix#L78-L86) – here, `replaceVars` cannot be used since the schema comes from the same package preventing us from pass its path to the function, probably due to a [Nix bug](https://github.com/NixOS/nix/issues/1846).
[]{#ssec-gnome-common-issues-unwrappable-package-gsettings-c} [Hard-coding GSettings schema path in C library](https://github.com/NixOS/nixpkgs/blob/29c120c065d03b000224872251bed93932d42412/pkgs/development/libraries/glib-networking/default.nix#L31-L34) – nothing special other than using [Coccinelle patch](https://github.com/NixOS/nixpkgs/pull/67957#issuecomment-527717467) to generate the patch itself.
diff --git a/doc/languages-frameworks/go.section.md b/doc/languages-frameworks/go.section.md
index 34bf913ef1e8..172bf2ca8f37 100644
--- a/doc/languages-frameworks/go.section.md
+++ b/doc/languages-frameworks/go.section.md
@@ -13,14 +13,14 @@ The following is an example expression using `buildGoModule`:
```nix
{
- pet = buildGoModule rec {
+ pet = buildGoModule (finalAttrs: {
pname = "pet";
version = "0.3.4";
src = fetchFromGitHub {
owner = "knqyf263";
repo = "pet";
- rev = "v${version}";
+ tag = "v${finalAttrs.version}";
hash = "sha256-Gjw1dRrgM8D3G7v6WIM2+50r4HmTXvx0Xxme2fH9TlQ=";
};
@@ -32,7 +32,7 @@ The following is an example expression using `buildGoModule`:
license = lib.licenses.mit;
maintainers = with lib.maintainers; [ kalbasit ];
};
- };
+ });
}
```
@@ -188,6 +188,13 @@ Whether the build result should be allowed to contain references to the Go tool
Defaults to `false`
+### `goSum` {#var-go-goSum}
+
+Specifies the contents of the `go.sum` file and triggers rebuilds when it changes. This helps combat inconsistent dependency errors on `go.sum` changes.
+
+Defaults to `null`
+
+
## Overriding `goModules` {#buildGoModule-goModules-override}
Overriding `.goModules` by calling `goModules.overrideAttrs` is unsupported. Still, it is possible to override the `vendorHash` (`goModules`'s `outputHash`) and the `pre`/`post` hooks for both the build and patch phases of the primary and `goModules` derivation.
diff --git a/doc/languages-frameworks/gradle.section.md b/doc/languages-frameworks/gradle.section.md
index 762c8003a7a7..3d648ec0ae0b 100644
--- a/doc/languages-frameworks/gradle.section.md
+++ b/doc/languages-frameworks/gradle.section.md
@@ -21,7 +21,10 @@ stdenv.mkDerivation (finalAttrs: {
hash = "sha256-ciKotTHSEcITfQYKFZ6sY2LZnXGChBJy0+eno8B3YHY=";
};
- nativeBuildInputs = [ gradle makeWrapper ];
+ nativeBuildInputs = [
+ gradle
+ makeWrapper
+ ];
# if the package has dependencies, mitmCache must be set
mitmCache = gradle.fetchDeps {
@@ -72,11 +75,12 @@ The first is to add the derivation arguments required for getting the
package. Using the pdftk example above:
```nix
-{ lib
-, stdenv
-, gradle
-# ...
-, pdftk
+{
+ lib,
+ stdenv,
+ gradle,
+ # ...
+ pdftk,
}:
stdenv.mkDerivation (finalAttrs: {
diff --git a/doc/languages-frameworks/hare.section.md b/doc/languages-frameworks/hare.section.md
index 0ae8abeba45c..7dfa42311480 100644
--- a/doc/languages-frameworks/hare.section.md
+++ b/doc/languages-frameworks/hare.section.md
@@ -25,7 +25,8 @@ The following attributes are accepted by `hareHook`:
hareHook,
lib,
stdenv,
-}: stdenv.mkDerivation {
+}:
+stdenv.mkDerivation {
pname = "";
version = "";
src = "";
diff --git a/doc/languages-frameworks/haskell.section.md b/doc/languages-frameworks/haskell.section.md
index 9ad32a3d20fb..0e0d44114971 100644
--- a/doc/languages-frameworks/haskell.section.md
+++ b/doc/languages-frameworks/haskell.section.md
@@ -58,7 +58,7 @@ Each of those compiler versions has a corresponding attribute set `packages` bui
it. However, the non-standard package sets are not tested regularly and, as a
result, contain fewer working packages. The corresponding package set for GHC
9.4.8 is `haskell.packages.ghc948`. In fact `haskellPackages` (at the time of writing) is just an alias
-for `haskell.packages.ghc966`:
+for `haskell.packages.ghc984`:
Every package set also re-exposes the GHC used to build its packages as `haskell.packages.*.ghc`.
@@ -297,8 +297,8 @@ Defaults to `false`.
: Whether to build (HTML) documentation using [haddock][haddock].
Defaults to `true` if supported.
-`testTarget`
-: Name of the test suite to build and run. If unset, all test suites will be executed.
+`testTargets`
+: Names of the test suites to build and run. If unset, all test suites will be executed.
`preCompileBuildDriver`
: Shell code to run before compiling `Setup.hs`.
@@ -487,7 +487,7 @@ so:
```nix
let
- pkgs = import {};
+ pkgs = import { };
inherit (pkgs) haskell;
inherit (haskell.lib.compose) overrideCabal;
@@ -511,7 +511,7 @@ let
previousIntermediates = turtle-full-build-with-incremental-output.intermediates;
}) turtle;
in
- turtle-incremental-build
+turtle-incremental-build
```
## Development environments {#haskell-development-environments}
@@ -590,7 +590,9 @@ that:
```nix
# Retrieve nixpkgs impurely from NIX_PATH for now, you can pin it instead, of course.
-{ pkgs ? import {} }:
+{
+ pkgs ? import { },
+}:
# use the nixpkgs default haskell package set
pkgs.haskellPackages.callPackage ./my-project.nix { }
@@ -619,6 +621,12 @@ environment. This means you can reuse Nix expressions of packages included in
nixpkgs, but also use local Nix expressions like this: `hpkgs: [
(hpkgs.callPackage ./my-project.nix { }) ]`.
+`extraDependencies`
+: Extra dependencies, in the form of cabal2nix build attributes. An example use
+case is when you have Haskell scripts that use libraries that don't occur in
+your packages' dependencies. Example: `hpkgs: {libraryHaskellDepends =
+[ hpkgs.releaser ]}`. Defaults to `hpkgs: { }`.
+
`nativeBuildInputs`
: Expects a list of derivations to add as build tools to the build environment.
This is the place to add packages like `cabal-install`, `doctest` or `hlint`.
@@ -654,7 +662,9 @@ Say our example above depends on `distribution-nixpkgs` and we have a project
file set up for both, we can add the following `shell.nix` expression:
```nix
-{ pkgs ? import {} }:
+{
+ pkgs ? import { },
+}:
pkgs.haskellPackages.shellFor {
packages = hpkgs: [
@@ -703,7 +713,12 @@ linked to work reliably. You can override the list of supported GHC versions
with e.g.
```nix
-pkgs.haskell-language-server.override { supportedGhcVersions = [ "90" "94" ]; }
+pkgs.haskell-language-server.override {
+ supportedGhcVersions = [
+ "90"
+ "94"
+ ];
+}
```
Where all strings `version` are allowed such that
`haskell.packages.ghc${version}` is an existing package set.
@@ -764,7 +779,7 @@ that depend on that library, you may want to use:
```nix
haskellPackages.haskell-ci.overrideScope (self: super: {
- Cabal = self.Cabal_3_14_1_0;
+ Cabal = self.Cabal_3_14_2_0;
})
```
@@ -886,11 +901,9 @@ for this to work.
derivation:
```nix
- pkgs.haskell.lib.overrideCabal
- (pkgs.haskell.lib.justStaticExecutables my-haskell-package)
- (drv: {
- disallowGhcReference = false;
- })
+ pkgs.haskell.lib.overrideCabal (pkgs.haskell.lib.justStaticExecutables my-haskell-package) (drv: {
+ disallowGhcReference = false;
+ })
```
Then use `strings` to determine which libraries are responsible:
@@ -906,14 +919,12 @@ for this to work.
Finally, use `remove-references-to` to delete those store paths from the produced output:
```nix
- pkgs.haskell.lib.overrideCabal
- (pkgs.haskell.lib.justStaticExecutables my-haskell-package)
- (drv: {
- postInstall = ''
- ${drv.postInstall or ""}
- remove-references-to -t ${pkgs.haskellPackages.hs-opentelemetry-sdk}
- '';
- })
+ pkgs.haskell.lib.overrideCabal (pkgs.haskell.lib.justStaticExecutables my-haskell-package) (drv: {
+ postInstall = ''
+ ${drv.postInstall or ""}
+ remove-references-to -t ${pkgs.haskellPackages.hs-opentelemetry-sdk}
+ '';
+ })
```
[164630]: https://github.com/NixOS/nixpkgs/issues/164630
@@ -1122,12 +1133,20 @@ Haskell packages using [import from derivation][import-from-derivation].
```nix
# cabal get mtl-2.2.1 && cd mtl-2.2.1 && cabal2nix .
-{ mkDerivation, base, lib, transformers }:
+{
+ mkDerivation,
+ base,
+ lib,
+ transformers,
+}:
mkDerivation {
pname = "mtl";
version = "2.2.1";
src = ./.;
- libraryHaskellDepends = [ base transformers ];
+ libraryHaskellDepends = [
+ base
+ transformers
+ ];
homepage = "http://github.com/ekmett/mtl";
description = "Monad classes, using functional dependencies";
license = lib.licenses.bsd3;
@@ -1274,60 +1293,69 @@ in
# recommended to only use such an overlay if you are enabling profiling on a
# platform that doesn't by default, because compiling GHC from scratch is
# quite expensive.
- (final: prev:
- let
- inherit (final) lib;
- in
+ (
+ final: prev:
+ let
+ inherit (final) lib;
+ in
- {
- haskell = prev.haskell // {
- compiler = prev.haskell.compiler // {
- ${ghcName} = prev.haskell.compiler.${ghcName}.override {
- # Unfortunately, the GHC setting is named differently for historical reasons
- enableProfiledLibs = enableProfiling;
- };
- };
- };
- })
-
- (final: prev:
- let
- inherit (final) lib;
- haskellLib = final.haskell.lib.compose;
- in
-
- {
- haskell = prev.haskell // {
- packages = prev.haskell.packages // {
- ${ghcName} = prev.haskell.packages.${ghcName}.override {
- overrides = hfinal: hprev: {
- mkDerivation = args: hprev.mkDerivation (args // {
- # Since we are forcing our ideas upon mkDerivation, this change will
- # affect every package in the package set.
- enableLibraryProfiling = enableProfiling;
-
- # To actually use profiling on an executable, executable profiling
- # needs to be enabled for the executable you want to profile. You
- # can either do this globally or…
- enableExecutableProfiling = enableProfiling;
- });
-
- # …only for the package that contains an executable you want to profile.
- # That saves on unnecessary rebuilds for packages that you only depend
- # on for their library, but also contain executables (e.g. pandoc).
- my-executable = haskellLib.enableExecutableProfiling hprev.my-executable;
-
- # If you are disabling profiling to save on build time, but want to
- # retain the ability to substitute from the binary cache. Drop the
- # override for mkDerivation above and instead have an override like
- # this for the specific packages you are building locally and want
- # to make cheaper to build.
- my-library = haskellLib.disableLibraryProfiling hprev.my-library;
+ {
+ haskell = prev.haskell // {
+ compiler = prev.haskell.compiler // {
+ ${ghcName} = prev.haskell.compiler.${ghcName}.override {
+ # Unfortunately, the GHC setting is named differently for historical reasons
+ enableProfiledLibs = enableProfiling;
};
};
};
- };
- })
+ }
+ )
+
+ (
+ final: prev:
+ let
+ inherit (final) lib;
+ haskellLib = final.haskell.lib.compose;
+ in
+
+ {
+ haskell = prev.haskell // {
+ packages = prev.haskell.packages // {
+ ${ghcName} = prev.haskell.packages.${ghcName}.override {
+ overrides = hfinal: hprev: {
+ mkDerivation =
+ args:
+ hprev.mkDerivation (
+ args
+ // {
+ # Since we are forcing our ideas upon mkDerivation, this change will
+ # affect every package in the package set.
+ enableLibraryProfiling = enableProfiling;
+
+ # To actually use profiling on an executable, executable profiling
+ # needs to be enabled for the executable you want to profile. You
+ # can either do this globally or…
+ enableExecutableProfiling = enableProfiling;
+ }
+ );
+
+ # …only for the package that contains an executable you want to profile.
+ # That saves on unnecessary rebuilds for packages that you only depend
+ # on for their library, but also contain executables (e.g. pandoc).
+ my-executable = haskellLib.enableExecutableProfiling hprev.my-executable;
+
+ # If you are disabling profiling to save on build time, but want to
+ # retain the ability to substitute from the binary cache. Drop the
+ # override for mkDerivation above and instead have an override like
+ # this for the specific packages you are building locally and want
+ # to make cheaper to build.
+ my-library = haskellLib.disableLibraryProfiling hprev.my-library;
+ };
+ };
+ };
+ };
+ }
+ )
]
```
diff --git a/doc/languages-frameworks/hy.section.md b/doc/languages-frameworks/hy.section.md
index 49309e4819f5..9eaa7a39fa1e 100644
--- a/doc/languages-frameworks/hy.section.md
+++ b/doc/languages-frameworks/hy.section.md
@@ -22,10 +22,16 @@ $ nix-shell -p "hy.withPackages (ps: with ps; [ numpy matplotlib ])"
Or if you want to extend your `configuration.nix`:
```nix
-{ # ...
+{
+ # ...
environment.systemPackages = with pkgs; [
- (hy.withPackages (py-packages: with py-packages; [ numpy matplotlib ]))
+ (hy.withPackages (
+ py-packages: with py-packages; [
+ numpy
+ matplotlib
+ ]
+ ))
];
}
```
diff --git a/doc/languages-frameworks/idris.section.md b/doc/languages-frameworks/idris.section.md
index 0fa828825749..692a6b64d69d 100644
--- a/doc/languages-frameworks/idris.section.md
+++ b/doc/languages-frameworks/idris.section.md
@@ -12,7 +12,12 @@ This however only provides the `prelude` and `base` libraries. To install idris
```nix
self: super: {
- myIdris = with self.idrisPackages; with-packages [ contrib pruviloj ];
+ myIdris =
+ with self.idrisPackages;
+ with-packages [
+ contrib
+ pruviloj
+ ];
}
```
@@ -68,13 +73,14 @@ prelude
As an example of how a Nix expression for an Idris package can be created, here is the one for `idrisPackages.yaml`:
```nix
-{ lib
-, build-idris-package
-, fetchFromGitHub
-, contrib
-, lightyear
+{
+ lib,
+ build-idris-package,
+ fetchFromGitHub,
+ contrib,
+ lightyear,
}:
-build-idris-package {
+build-idris-package {
name = "yaml";
version = "2018-01-25";
@@ -84,7 +90,10 @@ build-idris-package {
# different from its package name here.
ipkgName = "Yaml";
# Idris dependencies to provide for the build
- idrisDeps = [ contrib lightyear ];
+ idrisDeps = [
+ contrib
+ lightyear
+ ];
src = fetchFromGitHub {
owner = "Heather";
@@ -111,10 +120,10 @@ $ nix-build -E '(import {}).idrisPackages.callPackage ./yaml.nix {}'
Or it's possible to use
```nix
-with import {};
+with import { };
{
- yaml = idrisPackages.callPackage ./yaml.nix {};
+ yaml = idrisPackages.callPackage ./yaml.nix { };
}
```
@@ -134,7 +143,11 @@ For example you could set
```nix
build-idris-package {
- idrisBuildOptions = [ "--log" "1" "--verbose" ];
+ idrisBuildOptions = [
+ "--log"
+ "1"
+ "--verbose"
+ ];
# ...
}
diff --git a/doc/languages-frameworks/idris2.section.md b/doc/languages-frameworks/idris2.section.md
index 3ea9b4f988f7..6350f44c8c74 100644
--- a/doc/languages-frameworks/idris2.section.md
+++ b/doc/languages-frameworks/idris2.section.md
@@ -9,39 +9,50 @@ Importantly, `buildIdris` does not create a single derivation but rather an attr
A simple example of a fully packaged library would be the [`LSP-lib`](https://github.com/idris-community/LSP-lib) found in the `idris-community` GitHub organization.
```nix
{ fetchFromGitHub, idris2Packages }:
-let lspLibPkg = idris2Packages.buildIdris {
- ipkgName = "lsp-lib";
- src = fetchFromGitHub {
- owner = "idris-community";
- repo = "LSP-lib";
- rev = "main";
- hash = "sha256-EvSyMCVyiy9jDZMkXQmtwwMoLaem1GsKVFqSGNNHHmY=";
+let
+ lspLibPkg = idris2Packages.buildIdris {
+ ipkgName = "lsp-lib";
+ src = fetchFromGitHub {
+ owner = "idris-community";
+ repo = "LSP-lib";
+ rev = "main";
+ hash = "sha256-EvSyMCVyiy9jDZMkXQmtwwMoLaem1GsKVFqSGNNHHmY=";
+ };
+ idrisLibraries = [ ];
};
- idrisLibraries = [ ];
-};
-in lspLibPkg.library { withSource = true; }
+in
+lspLibPkg.library { withSource = true; }
```
The above results in a derivation with the installed library results (with sourcecode).
A slightly more involved example of a fully packaged executable would be the [`idris2-lsp`](https://github.com/idris-community/idris2-lsp) which is an Idris2 language server that uses the `LSP-lib` found above.
```nix
-{ callPackage, fetchFromGitHub, idris2Packages }:
+{
+ callPackage,
+ fetchFromGitHub,
+ idris2Packages,
+}:
# Assuming the previous example lives in `lsp-lib.nix`:
-let lspLib = callPackage ./lsp-lib.nix { };
- inherit (idris2Packages) idris2Api;
- lspPkg = idris2Packages.buildIdris {
- ipkgName = "idris2-lsp";
- src = fetchFromGitHub {
- owner = "idris-community";
- repo = "idris2-lsp";
- rev = "main";
- hash = "sha256-vQTzEltkx7uelDtXOHc6QRWZ4cSlhhm5ziOqWA+aujk=";
- };
- idrisLibraries = [idris2Api lspLib];
+let
+ lspLib = callPackage ./lsp-lib.nix { };
+ inherit (idris2Packages) idris2Api;
+ lspPkg = idris2Packages.buildIdris {
+ ipkgName = "idris2-lsp";
+ src = fetchFromGitHub {
+ owner = "idris-community";
+ repo = "idris2-lsp";
+ rev = "main";
+ hash = "sha256-vQTzEltkx7uelDtXOHc6QRWZ4cSlhhm5ziOqWA+aujk=";
};
-in lspPkg.executable
+ idrisLibraries = [
+ idris2Api
+ lspLib
+ ];
+ };
+in
+lspPkg.executable
```
The above uses the default value of `withSource = false` for the `idris2Api` but could be modified to include that library's source by passing `(idris2Api { withSource = true; })` to `idrisLibraries` instead. `idris2Api` in the above derivation comes built in with `idris2Packages`. This library exposes many of the otherwise internal APIs of the Idris2 compiler.
diff --git a/doc/languages-frameworks/index.md b/doc/languages-frameworks/index.md
index 12e54e2a76ab..2624b9afc5e4 100644
--- a/doc/languages-frameworks/index.md
+++ b/doc/languages-frameworks/index.md
@@ -98,6 +98,7 @@ scheme.section.md
swift.section.md
tcl.section.md
texlive.section.md
+typst.section.md
vim.section.md
neovim.section.md
```
diff --git a/doc/languages-frameworks/ios.section.md b/doc/languages-frameworks/ios.section.md
index eb8e2ca55326..3823b097d7a2 100644
--- a/doc/languages-frameworks/ios.section.md
+++ b/doc/languages-frameworks/ios.section.md
@@ -29,7 +29,7 @@ Xcode.
```nix
let
- pkgs = import {};
+ pkgs = import { };
xcodeenv = import ./xcodeenv {
inherit (pkgs) stdenv;
@@ -63,7 +63,7 @@ executing the `xcodeenv.buildApp {}` function:
```nix
let
- pkgs = import {};
+ pkgs = import { };
xcodeenv = import ./xcodeenv {
inherit (pkgs) stdenv;
@@ -159,7 +159,7 @@ instances:
```nix
let
- pkgs = import {};
+ pkgs = import { };
xcodeenv = import ./xcodeenv {
inherit (pkgs) stdenv;
@@ -193,7 +193,7 @@ app in the requested simulator instance:
```nix
let
- pkgs = import {};
+ pkgs = import { };
xcodeenv = import ./xcodeenv {
inherit (pkgs) stdenv;
diff --git a/doc/languages-frameworks/java.section.md b/doc/languages-frameworks/java.section.md
index 5208a6388a32..05203c378ae9 100644
--- a/doc/languages-frameworks/java.section.md
+++ b/doc/languages-frameworks/java.section.md
@@ -7,7 +7,9 @@ stdenv.mkDerivation {
pname = "...";
version = "...";
- src = fetchurl { /* ... */ };
+ src = fetchurl {
+ # ...
+ };
nativeBuildInputs = [
ant
@@ -95,7 +97,7 @@ let
something = (pkgs.something.override { jre = my_jre; });
other = (pkgs.other.override { jre = my_jre; });
in
- <...>
+<...>
```
You can also specify what JDK your JRE should be based on, for example
@@ -122,7 +124,10 @@ OpenJDK. For instance, to use the GNU Java Compiler:
```nix
{
- nativeBuildInputs = [ gcj ant ];
+ nativeBuildInputs = [
+ gcj
+ ant
+ ];
}
```
diff --git a/doc/languages-frameworks/javascript.section.md b/doc/languages-frameworks/javascript.section.md
index 9acfd4181108..e076984db000 100644
--- a/doc/languages-frameworks/javascript.section.md
+++ b/doc/languages-frameworks/javascript.section.md
@@ -117,12 +117,19 @@ After you have identified the correct system, you need to override your package
For example, `dat` requires `node-gyp-build`, so we override its expression in [pkgs/development/node-packages/overrides.nix](https://github.com/NixOS/nixpkgs/blob/master/pkgs/development/node-packages/overrides.nix):
```nix
- {
- dat = prev.dat.override (oldAttrs: {
- buildInputs = [ final.node-gyp-build pkgs.libtool pkgs.autoconf pkgs.automake ];
- meta = oldAttrs.meta // { broken = since "12"; };
- });
- }
+{
+ dat = prev.dat.override (oldAttrs: {
+ buildInputs = [
+ final.node-gyp-build
+ pkgs.libtool
+ pkgs.autoconf
+ pkgs.automake
+ ];
+ meta = oldAttrs.meta // {
+ broken = since "12";
+ };
+ });
+}
```
### Adding and Updating Javascript packages in nixpkgs {#javascript-adding-or-updating-packages}
@@ -185,16 +192,20 @@ It works by utilizing npm's cache functionality -- creating a reproducible cache
Here's an example:
```nix
-{ lib, buildNpmPackage, fetchFromGitHub }:
+{
+ lib,
+ buildNpmPackage,
+ fetchFromGitHub,
+}:
-buildNpmPackage rec {
+buildNpmPackage (finalAttrs: {
pname = "flood";
version = "4.7.0";
src = fetchFromGitHub {
owner = "jesec";
- repo = pname;
- rev = "v${version}";
+ repo = "flood";
+ tag = "v${finalAttrs.version}";
hash = "sha256-BR+ZGkBBfd0dSQqAvujsbgsEPFYw/ThrylxUbOksYxM=";
};
@@ -211,7 +222,7 @@ buildNpmPackage rec {
license = lib.licenses.gpl3Only;
maintainers = with lib.maintainers; [ winter ];
};
-}
+})
```
In the default `installPhase` set by `buildNpmPackage`, it uses `npm pack --json --dry-run` to decide what files to install in `$out/lib/node_modules/$name/`, where `$name` is the `name` string defined in the package's `package.json`.
@@ -323,7 +334,9 @@ buildNpmPackage {
npmRoot = ./.;
fetcherOpts = {
# Pass 'curlOptsList' to 'pkgs.fetchurl' while fetching 'axios'
- { "node_modules/axios" = { curlOptsList = [ "--verbose" ]; }; }
+ "node_modules/axios" = {
+ curlOptsList = [ "--verbose" ];
+ };
};
};
@@ -369,6 +382,15 @@ pkgs.mkShell {
```
will create a development shell where a `node_modules` directory is created & packages symlinked to the Nix store when activated.
+:::{.note}
+Commands like `npm install` & `npm add` that writes packages & executables needs to be used with `--package-lock-only`.
+
+This means `npm` installs dependencies by writing into `package-lock.json` without modifying the `node_modules` folder. Installation happens through reloading the devShell.
+This might be best practice since it gives the `nix shell` virtually exclusive ownership over your `node_modules` folder.
+
+It's recommended to set `package-lock-only = true` in your project-local [`.npmrc`](https://docs.npmjs.com/cli/v11/configuring-npm/npmrc).
+:::
+
### corepack {#javascript-corepack}
This package puts the corepack wrappers for pnpm and yarn in your PATH, and they will honor the `packageManager` setting in the `package.json`.
@@ -403,14 +425,16 @@ When packaging an application that includes a `pnpm-lock.yaml`, you need to fetc
stdenv,
nodejs,
# This is pinned as { pnpm = pnpm_9; }
- pnpm
+ pnpm,
}:
stdenv.mkDerivation (finalAttrs: {
pname = "foo";
version = "0-unstable-1980-01-01";
- src = ...;
+ src = {
+ #...
+ };
nativeBuildInputs = [
nodejs
@@ -439,7 +463,9 @@ stdenv.mkDerivation (finalAttrs: {
pname = "foo";
version = "0-unstable-1980-01-01";
- src = ...;
+ src = {
+ # ...
+ };
pnpmInstallFlags = [ "--shamefully-hoist" ];
@@ -466,14 +492,16 @@ Assuming the following directory structure, we can define `sourceRoot` and `pnpm
```
```nix
- ...
+{
+ # ...
pnpmDeps = pnpm.fetchDeps {
- ...
+ # ...
sourceRoot = "${finalAttrs.src.name}/frontend";
};
# by default the working directory is the extracted source
pnpmRoot = "frontend";
+}
```
#### PNPM Workspaces {#javascript-pnpm-workspaces}
@@ -484,11 +512,13 @@ which will make PNPM only install dependencies for those workspace packages.
For example:
```nix
-...
-pnpmWorkspaces = [ "@astrojs/language-server" ];
-pnpmDeps = pnpm.fetchDeps {
- inherit (finalAttrs) pnpmWorkspaces;
- ...
+{
+ # ...
+ pnpmWorkspaces = [ "@astrojs/language-server" ];
+ pnpmDeps = pnpm.fetchDeps {
+ inherit (finalAttrs) pnpmWorkspaces;
+ #...
+ };
}
```
@@ -498,13 +528,15 @@ Note that you do not need to set `sourceRoot` to make this work.
Usually in such cases, you'd want to use `pnpm --filter= build` to build your project, as `npmHooks.npmBuildHook` probably won't work. A `buildPhase` based on the following example will probably fit most workspace projects:
```nix
-buildPhase = ''
- runHook preBuild
+{
+ buildPhase = ''
+ runHook preBuild
- pnpm --filter=@astrojs/language-server build
+ pnpm --filter=@astrojs/language-server build
- runHook postBuild
-'';
+ runHook postBuild
+ '';
+}
```
#### Additional PNPM Commands and settings {#javascript-pnpm-extraCommands}
@@ -513,13 +545,15 @@ If you require setting an additional PNPM configuration setting (such as `dedupe
set `prePnpmInstall` to the right commands to run. For example:
```nix
-prePnpmInstall = ''
- pnpm config set dedupe-peer-dependants false
-'';
-pnpmDeps = pnpm.fetchDeps {
- inherit (finalAttrs) prePnpmInstall;
- ...
-};
+{
+ prePnpmInstall = ''
+ pnpm config set dedupe-peer-dependants false
+ '';
+ pnpmDeps = pnpm.fetchDeps {
+ inherit (finalAttrs) prePnpmInstall;
+ # ...
+ };
+}
```
In this example, `prePnpmInstall` will be run by both `pnpm.configHook` and by the `pnpm.fetchDeps` builder.
@@ -527,7 +561,15 @@ In this example, `prePnpmInstall` will be run by both `pnpm.configHook` and by t
### Yarn {#javascript-yarn}
-Yarn based projects use a `yarn.lock` file instead of a `package-lock.json` to pin dependencies. Nixpkgs provides the Nix function `fetchYarnDeps` which fetches an offline cache suitable for running `yarn install` before building the project. In addition, Nixpkgs provides the hooks:
+Yarn based projects use a `yarn.lock` file instead of a `package-lock.json` to pin dependencies.
+
+To package yarn-based applications, you need to distinguish by the version pointers in the `yarn.lock` file. See the following sections.
+
+#### Yarn v1 {#javascript-yarn-v1}
+
+Yarn v1 lockfiles contain a comment `# yarn lockfile v1` at the beginning of the file.
+
+Nixpkgs provides the Nix function `fetchYarnDeps` which fetches an offline cache suitable for running `yarn install` before building the project. In addition, Nixpkgs provides the hooks:
- `yarnConfigHook`: Fetches the dependencies from the offline cache and installs them into `node_modules`.
- `yarnBuildHook`: Runs `yarn build` or a specified `yarn` command that builds the project.
@@ -577,28 +619,28 @@ stdenv.mkDerivation (finalAttrs: {
})
```
-#### `yarnConfigHook` arguments {#javascript-yarnconfighook}
+##### `yarnConfigHook` arguments {#javascript-yarnconfighook}
By default, `yarnConfigHook` relies upon the attribute `${yarnOfflineCache}` (or `${offlineCache}` if the former is not set) to find the location of the offline cache produced by `fetchYarnDeps`. To disable this phase, you can set `dontYarnInstallDeps = true` or override the `configurePhase`.
-#### `yarnBuildHook` arguments {#javascript-yarnbuildhook}
+##### `yarnBuildHook` arguments {#javascript-yarnbuildhook}
This script by default runs `yarn --offline build`, and it relies upon the project's dependencies installed at `node_modules`. Below is a list of additional `mkDerivation` arguments read by this hook:
- `yarnBuildScript`: Sets a different `yarn --offline` subcommand (defaults to `build`).
- `yarnBuildFlags`: Single string list of additional flags to pass the above command, or a Nix list of such additional flags.
-#### `yarnInstallHook` arguments {#javascript-yarninstallhook}
+##### `yarnInstallHook` arguments {#javascript-yarninstallhook}
To install the package `yarnInstallHook` uses both `npm` and `yarn` to cleanup project files and dependencies. To disable this phase, you can set `dontYarnInstall = true` or override the `installPhase`. Below is a list of additional `mkDerivation` arguments read by this hook:
- `yarnKeepDevDeps`: Disables the removal of devDependencies from `node_modules` before installation.
-### yarn2nix {#javascript-yarn2nix}
+#### yarn2nix {#javascript-yarn2nix}
-WARNING: The `yarn2nix` functions have been deprecated in favor of the new `yarnConfigHook`, `yarnBuildHook` and `yarnInstallHook`. Documentation for them still appears here for the sake of the packages that still use them. See also a tracking issue [#324246](https://github.com/NixOS/nixpkgs/issues/324246).
+WARNING: The `yarn2nix` functions have been deprecated in favor of `yarnConfigHook`, `yarnBuildHook` and `yarnInstallHook` (for Yarn v1) and `yarn-berry_*.*` tooling (Yarn v3 and v4). Documentation for `yarn2nix` functions still appears here for the sake of the packages that still use them. See also a tracking issue [#324246](https://github.com/NixOS/nixpkgs/issues/324246).
-#### Preparation {#javascript-yarn2nix-preparation}
+##### Preparation {#javascript-yarn2nix-preparation}
You will need at least a `yarn.lock` file. If upstream does not have one you need to generate it and reference it in your package definition.
@@ -613,7 +655,7 @@ If the downloaded files contain the `package.json` and `yarn.lock` files they ca
}
```
-#### mkYarnPackage {#javascript-yarn2nix-mkYarnPackage}
+##### mkYarnPackage {#javascript-yarn2nix-mkYarnPackage}
`mkYarnPackage` will by default try to generate a binary. For package only generating static assets (Svelte, Vue, React, WebPack, ...), you will need to explicitly override the build step with your instructions.
@@ -621,9 +663,16 @@ It's important to use the `--offline` flag. For example if you script is `"build
```nix
{
+ nativeBuildInputs = [
+ writableTmpDirAsHomeHook
+ ];
+
buildPhase = ''
- export HOME=$(mktemp -d)
+ runHook preBuild
+
yarn --offline build
+
+ runHook postBuild
'';
}
```
@@ -657,7 +706,7 @@ or if you need a writeable node_modules directory:
}
```
-#### mkYarnModules {#javascript-yarn2nix-mkYarnModules}
+##### mkYarnModules {#javascript-yarn2nix-mkYarnModules}
This will generate a derivation including the `node_modules` directory.
If you have to build a derivation for an integrated web framework (rails, phoenix..), this is probably the easiest way.
@@ -678,7 +727,11 @@ To fix this we will specify different versions of build inputs to use, as well a
mkYarnPackage rec {
pkgConfig = {
node-sass = {
- buildInputs = with final;[ python libsass pkg-config ];
+ buildInputs = with final; [
+ python
+ libsass
+ pkg-config
+ ];
postInstall = ''
LIBSASS_EXT=auto yarn --offline run build
rm build/config.gypi
@@ -688,7 +741,7 @@ mkYarnPackage rec {
}
```
-#### Pitfalls {#javascript-yarn2nix-pitfalls}
+##### Pitfalls {#javascript-yarn2nix-pitfalls}
- If version is missing from upstream package.json, yarn will silently install nothing. In that case, you will need to override package.json as shown in the [package.json section](#javascript-upstream-package-json)
- Having trouble with `node-gyp`? Try adding these lines to the `yarnPreBuild` steps:
@@ -708,6 +761,116 @@ mkYarnPackage rec {
- Exporting the headers in `npm_config_nodedir` comes from this issue:
- `offlineCache` (described [above](#javascript-yarn2nix-preparation)) must be specified to avoid [Import From Derivation](#ssec-import-from-derivation) (IFD) when used inside Nixpkgs.
+#### Yarn Berry v3/v4 {#javascript-yarn-v3-v4}
+Yarn Berry (v3 / v4) have similar formats, they start with blocks like these:
+
+```yaml
+__metadata:
+ version: 6
+ cacheKey: 8[cX]
+```
+
+```yaml
+__metadata:
+ version: 8
+ cacheKey: 10[cX]
+```
+
+For these packages, we have some helpers exposed under the respective `yarn-berry_3` and `yarn-berry_4` packages:
+
+- `yarn-berry-fetcher`
+- `fetchYarnBerryDeps`
+- `yarnBerryConfigHook`
+
+It's recommended to ensure you're explicitly pinning the major version used, for example by capturing the `yarn-berry_Xn` argument and then re-defining it as a `yarn-berry` `let` binding.
+
+```nix
+{
+ stdenv,
+ nodejs,
+ yarn-berry_4,
+}:
+
+let
+ yarn-berry = yarn-berry_4;
+in
+
+stdenv.mkDerivation (finalAttrs: {
+ pname = "foo";
+ version = "0-unstable-1980-01-01";
+
+ src = {
+ #...
+ };
+
+ nativeBuildInputs = [
+ nodejs
+ yarn-berry.yarnBerryConfigHook
+ ];
+
+ offlineCache = yarn-berry.fetchYarnBerryDeps {
+ inherit (finalAttrs) src;
+ hash = "...";
+ };
+})
+```
+
+##### `yarn-berry_X.fetchYarnBerryDeps` {#javascript-fetchYarnBerryDeps}
+`fetchYarnBerryDeps` runs `yarn-berry-fetcher fetch` in a fixed-output-derivation. It is a custom fetcher designed to reproducibly download all files in the `yarn.lock` file, validating their hashes in the process. For git dependencies, it creates a checkout at `${offlineCache}/checkouts/<40-character-commit-hash>` (relying on the git commit hash to describe the contents of the checkout).
+
+To produce the `hash` argument for `fetchYarnBerryDeps` function call, the `yarn-berry-fetcher prefetch` command can be used:
+
+```console
+$ yarn-berry-fetcher prefetch [/path/to/missing-hashes.json]
+```
+
+This prints the hash to stdout and can be used in update scripts to recalculate the hash for a new version of `yarn.lock`.
+
+##### `yarn-berry_X.yarnBerryConfigHook` {#javascript-yarnBerryConfigHook}
+`yarnBerryConfigHook` uses the store path `offlineCache` points to, to run a `yarn install` during the build, producing a usable `node_modules` directory from the downloaded dependencies.
+
+Internally, this uses a patched version of Yarn to ensure git dependencies are re-packed and any attempted downloads fail immediately.
+
+##### Patching upstream `package.json` or `yarn.lock` files {#javascript-yarnBerry-patching}
+In case patching the upstream `package.json` or `yarn.lock` is needed, it's important to pass `finalAttrs.patches` to `fetchYarnBerryDeps` as well, so the patched variants are picked up (i.e. `inherit (finalAttrs) patches`.
+
+##### Missing hashes in the `yarn.lock` file {#javascript-yarnBerry-missing-hashes}
+Unfortunately, `yarn.lock` files do not include hashes for optional/platform-specific dependencies. This is [by design](https://github.com/yarnpkg/berry/issues/6759).
+
+To compensate for this, the `yarn-berry-fetcher missing-hashes` subcommand can be used to produce all missing hashes. These are usually stored in a `missing-hashes.json` file, which needs to be passed to both the build itself, as well as the `fetchYarnBerryDeps` helper:
+
+```nix
+{
+ stdenv,
+ nodejs,
+ yarn-berry_4,
+}:
+
+let
+ yarn-berry = yarn-berry_4;
+in
+
+stdenv.mkDerivation (finalAttrs: {
+ pname = "foo";
+ version = "0-unstable-1980-01-01";
+
+ src = {
+ #...
+ };
+
+ nativeBuildInputs = [
+ nodejs
+ yarn-berry.yarnBerryConfigHook
+ ];
+
+ missingHashes = ./missing-hashes.json;
+ offlineCache = yarn-berry.fetchYarnBerryDeps {
+ inherit (finalAttrs) src missingHashes;
+ hash = "...";
+ };
+})
+```
+
## Outside Nixpkgs {#javascript-outside-nixpkgs}
There are some other tools available, which are written in the Nix language.
diff --git a/doc/languages-frameworks/julia.section.md b/doc/languages-frameworks/julia.section.md
index 4e7d9d365a39..c0b7fd8d6618 100644
--- a/doc/languages-frameworks/julia.section.md
+++ b/doc/languages-frameworks/julia.section.md
@@ -19,7 +19,7 @@ This function accepts a list of strings representing Julia package names.
For example, you can build a Julia environment with the `Plots` package as follows.
```nix
-julia.withPackages ["Plots"]
+julia.withPackages [ "Plots" ]
```
Arguments can be passed using `.override`.
@@ -28,7 +28,8 @@ For example:
```nix
(julia.withPackages.override {
precompile = false; # Turn off precompilation
-}) ["Plots"]
+})
+ [ "Plots" ]
```
Here's a nice way to run a Julia environment with a shell one-liner:
diff --git a/doc/languages-frameworks/lisp.section.md b/doc/languages-frameworks/lisp.section.md
index 73f20436c76f..7f6fe99419bd 100644
--- a/doc/languages-frameworks/lisp.section.md
+++ b/doc/languages-frameworks/lisp.section.md
@@ -48,7 +48,8 @@ Also one can create a `pkgs.mkShell` environment in `shell.nix`/`flake.nix`:
```nix
let
sbcl' = sbcl.withPackages (ps: [ ps.alexandria ]);
-in mkShell {
+in
+mkShell {
packages = [ sbcl' ];
}
```
@@ -134,7 +135,6 @@ During Quicklisp import:
- names starting with a number have a `_` prepended (`3d-vectors`->`_3d-vectors`)
- `_` in names is converted to `__` for reversibility
-
## Defining packages manually inside Nixpkgs {#lisp-defining-packages-inside}
Packages that for some reason are not in Quicklisp, and so cannot be
@@ -184,14 +184,17 @@ let
domain = "gitlab.common-lisp.net";
owner = "alexandria";
repo = "alexandria";
- rev = "v${version}";
+ tag = "v${version}";
hash = "sha256-1Hzxt65dZvgOFIljjjlSGgKYkj+YBLwJCACi5DZsKmQ=";
};
};
- sbcl' = sbcl.withOverrides (self: super: {
- inherit alexandria;
- });
-in sbcl'.pkgs.alexandria
+ sbcl' = sbcl.withOverrides (
+ self: super: {
+ inherit alexandria;
+ }
+ );
+in
+sbcl'.pkgs.alexandria
```
## Overriding package attributes {#lisp-overriding-package-attributes}
@@ -208,7 +211,7 @@ sbcl.pkgs.alexandria.overrideLispAttrs (oldAttrs: rec {
domain = "gitlab.common-lisp.net";
owner = "alexandria";
repo = "alexandria";
- rev = "v${version}";
+ tag = "v${version}";
hash = "sha256-1Hzxt65dZvgOFIljjjlSGgKYkj+YBLwJCACi5DZsKmQ=";
};
})
@@ -296,6 +299,9 @@ This example wraps CLISP:
wrapLisp {
pkg = clisp;
faslExt = "fas";
- flags = ["-E" "UTF8"];
+ flags = [
+ "-E"
+ "UTF8"
+ ];
}
```
diff --git a/doc/languages-frameworks/lua.section.md b/doc/languages-frameworks/lua.section.md
index 87bf7ce885bc..84af997df860 100644
--- a/doc/languages-frameworks/lua.section.md
+++ b/doc/languages-frameworks/lua.section.md
@@ -27,9 +27,14 @@ Note that nixpkgs patches the non-luajit interpreters to avoid referring to
Create a file, e.g. `build.nix`, with the following expression
```nix
-with import {};
+with import { };
-lua5_2.withPackages (ps: with ps; [ busted luafilesystem ])
+lua5_2.withPackages (
+ ps: with ps; [
+ busted
+ luafilesystem
+ ]
+)
```
and install it in your profile with
@@ -46,11 +51,18 @@ If you prefer to, you could also add the environment as a package override to th
using `config.nix`,
```nix
-{ # ...
+{
+ # ...
- packageOverrides = pkgs: with pkgs; {
- myLuaEnv = lua5_2.withPackages (ps: with ps; [ busted luafilesystem ]);
- };
+ packageOverrides =
+ pkgs: with pkgs; {
+ myLuaEnv = lua5_2.withPackages (
+ ps: with ps; [
+ busted
+ luafilesystem
+ ]
+ );
+ };
}
```
@@ -67,10 +79,16 @@ the `nixpkgs` channel was used.
For the sake of completeness, here's another example how to install the environment system-wide.
```nix
-{ # ...
+{
+ # ...
environment.systemPackages = with pkgs; [
- (lua.withPackages(ps: with ps; [ busted luafilesystem ]))
+ (lua.withPackages (
+ ps: with ps; [
+ busted
+ luafilesystem
+ ]
+ ))
];
}
```
@@ -80,13 +98,12 @@ For the sake of completeness, here's another example how to install the environm
Use the following overlay template:
```nix
-final: prev:
-{
+final: prev: {
lua = prev.lua.override {
packageOverrides = luaself: luaprev: {
- luarocks-nix = luaprev.luarocks-nix.overrideAttrs(oa: {
+ luarocks-nix = luaprev.luarocks-nix.overrideAttrs (oa: {
pname = "luarocks-nix";
src = /home/my_luarocks/repository;
});
@@ -159,7 +176,11 @@ within a `toLuaModule` call, for instance
```nix
{
- mynewlib = toLuaModule ( stdenv.mkDerivation { /* ... */ });
+ mynewlib = toLuaModule (
+ stdenv.mkDerivation {
+ # ...
+ }
+ );
}
```
@@ -194,16 +215,23 @@ The following is an example:
version = "34.0.4-1";
src = fetchurl {
- url = "https://raw.githubusercontent.com/rocks-moonscript-org/moonrocks-mirror/master/luaposix-34.0.4-1.src.rock";
+ url = "https://raw.githubusercontent.com/rocks-moonscript-org/moonrocks-mirror/master/luaposix-34.0.4-1.src.rock";
hash = "sha256-4mLJG8n4m6y4Fqd0meUDfsOb9RHSR0qa/KD5KCwrNXs=";
};
disabled = (luaOlder "5.1") || (luaAtLeast "5.4");
- propagatedBuildInputs = [ bit32 lua std_normalize ];
+ propagatedBuildInputs = [
+ bit32
+ lua
+ std_normalize
+ ];
meta = {
homepage = "https://github.com/luaposix/luaposix/";
description = "Lua bindings for POSIX";
- maintainers = with lib.maintainers; [ vyp lblasc ];
+ maintainers = with lib.maintainers; [
+ vyp
+ lblasc
+ ];
license.fullName = "MIT/X11";
};
};
@@ -242,14 +270,14 @@ The `lua.withPackages` takes a function as an argument that is passed the set of
Using the `withPackages` function, the previous example for the luafilesystem environment can be written like this:
```nix
-lua.withPackages (ps: [ps.luafilesystem])
+lua.withPackages (ps: [ ps.luafilesystem ])
```
`withPackages` passes the correct package set for the specific interpreter version as an argument to the function. In the above example, `ps` equals `luaPackages`.
But you can also easily switch to using `lua5_1`:
```nix
-lua5_1.withPackages (ps: [ps.lua])
+lua5_1.withPackages (ps: [ ps.lua ])
```
Now, `ps` is set to `lua5_1.pkgs`, matching the version of the interpreter.
diff --git a/doc/languages-frameworks/maven.section.md b/doc/languages-frameworks/maven.section.md
index 88fe4d0c9224..d70501651f26 100644
--- a/doc/languages-frameworks/maven.section.md
+++ b/doc/languages-frameworks/maven.section.md
@@ -9,7 +9,13 @@ The following provides a list of common patterns with how to package a Maven pro
Consider the following package:
```nix
-{ lib, fetchFromGitHub, jre, makeWrapper, maven }:
+{
+ lib,
+ fetchFromGitHub,
+ jre,
+ makeWrapper,
+ maven,
+}:
maven.buildMavenPackage rec {
pname = "jd-cli";
@@ -17,8 +23,8 @@ maven.buildMavenPackage rec {
src = fetchFromGitHub {
owner = "intoolswetrust";
- repo = pname;
- rev = "${pname}-${version}";
+ repo = "jd-cli";
+ tag = "jd-cli-${version}";
hash = "sha256-rRttA5H0A0c44loBzbKH7Waoted3IsOgxGCD2VM0U/Q=";
};
@@ -27,11 +33,15 @@ maven.buildMavenPackage rec {
nativeBuildInputs = [ makeWrapper ];
installPhase = ''
+ runHook preInstall
+
mkdir -p $out/bin $out/share/jd-cli
install -Dm644 jd-cli/target/jd-cli.jar $out/share/jd-cli
makeWrapper ${jre}/bin/java $out/bin/jd-cli \
--add-flags "-jar $out/share/jd-cli/jd-cli.jar"
+
+ runHook postInstall
'';
meta = {
@@ -91,7 +101,7 @@ jd-cli.overrideMavenAttrs (old: rec {
# old mvnHash of 1.2.0 maven dependencies
mvnHash = "sha256-N9XC1pg6Y4sUiBWIQUf16QSXCuiAPpXEHGlgApviF4I=";
-});
+})
```
:::
@@ -129,7 +139,7 @@ maven.buildMavenPackage rec {
"org.apache.maven.surefire:surefire-junit-platform:3.1.2"
"org.junit.platform:junit-platform-launcher:1.10.0"
];
-};
+}
```
:::
@@ -246,7 +256,9 @@ This file is then given to the `buildMaven` function, and it returns 2 attribute
Here is an [example](https://github.com/fzakaria/nixos-maven-example/blob/main/build-maven-repository.nix) of building the Maven repository
```nix
-{ pkgs ? import { } }:
+{
+ pkgs ? import { },
+}:
with pkgs;
(buildMaven ./project-info.json).repo
```
@@ -283,22 +295,34 @@ Traditionally the Maven repository is at `~/.m2/repository`. We will override th
:::
```nix
-{ lib, stdenv, maven }:
+{
+ lib,
+ stdenv,
+ maven,
+}:
stdenv.mkDerivation {
name = "maven-repository";
buildInputs = [ maven ];
src = ./.; # or fetchFromGitHub, cleanSourceWith, etc
buildPhase = ''
+ runHook preBuild
+
mvn package -Dmaven.repo.local=$out
+
+ runHook postBuild
'';
# keep only *.{pom,jar,sha1,nbm} and delete all ephemeral files with lastModified timestamps inside
installPhase = ''
+ runHook preInstall
+
find $out -type f \
-name \*.lastUpdated -or \
-name resolver-status.properties -or \
-name _remote.repositories \
-delete
+
+ runHook postInstall
'';
# don't do any fixup
@@ -337,10 +361,16 @@ If your package uses _SNAPSHOT_ dependencies or _version ranges_; there is a str
Regardless of which strategy is chosen above, the step to build the derivation is the same.
```nix
-{ stdenv, maven, callPackage }:
-# pick a repository derivation, here we will use buildMaven
-let repository = callPackage ./build-maven-repository.nix { };
-in stdenv.mkDerivation rec {
+{
+ stdenv,
+ maven,
+ callPackage,
+}:
+let
+ # pick a repository derivation, here we will use buildMaven
+ repository = callPackage ./build-maven-repository.nix { };
+in
+stdenv.mkDerivation (finalAttrs: {
pname = "maven-demo";
version = "1.0";
@@ -348,14 +378,22 @@ in stdenv.mkDerivation rec {
buildInputs = [ maven ];
buildPhase = ''
+ runHook preBuild
+
echo "Using repository ${repository}"
mvn --offline -Dmaven.repo.local=${repository} package;
+
+ runHook postBuild
'';
installPhase = ''
- install -Dm644 target/${pname}-${version}.jar $out/share/java
+ runHook preInstall
+
+ install -Dm644 target/${finalAttrs.pname}-${finalAttrs.version}.jar $out/share/java
+
+ runHook postInstall
'';
-}
+})
```
::: {.tip}
@@ -393,35 +431,49 @@ We will read the Maven repository and flatten it to a single list. This list wil
We make sure to provide this classpath to the `makeWrapper`.
```nix
-{ stdenv, maven, callPackage, makeWrapper, jre }:
+{
+ stdenv,
+ maven,
+ callPackage,
+ makeWrapper,
+ jre,
+}:
let
repository = callPackage ./build-maven-repository.nix { };
-in stdenv.mkDerivation rec {
+in
+stdenv.mkDerivation (finalAttrs: {
pname = "maven-demo";
version = "1.0";
- src = builtins.fetchTarball
- "https://github.com/fzakaria/nixos-maven-example/archive/main.tar.gz";
+ src = builtins.fetchTarball "https://github.com/fzakaria/nixos-maven-example/archive/main.tar.gz";
nativeBuildInputs = [ makeWrapper ];
buildInputs = [ maven ];
buildPhase = ''
+ runHook preBuild
+
echo "Using repository ${repository}"
mvn --offline -Dmaven.repo.local=${repository} package;
+
+ runHook postBuild
'';
installPhase = ''
+ runHook preInstall
+
mkdir -p $out/bin
classpath=$(find ${repository} -name "*.jar" -printf ':%h/%f');
- install -Dm644 target/${pname}-${version}.jar $out/share/java
+ install -Dm644 target/maven-demo-${finalAttrs.version}.jar $out/share/java
# create a wrapper that will automatically set the classpath
# this should be the paths from the dependency derivation
- makeWrapper ${jre}/bin/java $out/bin/${pname} \
- --add-flags "-classpath $out/share/java/${pname}-${version}.jar:''${classpath#:}" \
+ makeWrapper ${jre}/bin/java $out/bin/maven-demo \
+ --add-flags "-classpath $out/share/java/maven-demo-${finalAttrs.version}.jar:''${classpath#:}" \
--add-flags "Main"
+
+ runHook postInstall
'';
-}
+})
```
#### MANIFEST file via Maven Plugin {#manifest-file-via-maven-plugin}
@@ -471,36 +523,51 @@ Main-Class: Main
We will modify the derivation above to add a symlink to our repository so that it's accessible to our JAR during the `installPhase`.
```nix
-{ stdenv, maven, callPackage, makeWrapper, jre }:
-# pick a repository derivation, here we will use buildMaven
-let repository = callPackage ./build-maven-repository.nix { };
-in stdenv.mkDerivation rec {
+{
+ stdenv,
+ maven,
+ callPackage,
+ makeWrapper,
+ jre,
+}:
+let
+ # pick a repository derivation, here we will use buildMaven
+ repository = callPackage ./build-maven-repository.nix { };
+in
+stdenv.mkDerivation (finalAttrs: {
pname = "maven-demo";
version = "1.0";
- src = builtins.fetchTarball
- "https://github.com/fzakaria/nixos-maven-example/archive/main.tar.gz";
+ src = builtins.fetchTarball "https://github.com/fzakaria/nixos-maven-example/archive/main.tar.gz";
nativeBuildInputs = [ makeWrapper ];
buildInputs = [ maven ];
buildPhase = ''
+ runHook preBuild
+
echo "Using repository ${repository}"
mvn --offline -Dmaven.repo.local=${repository} package;
+
+ runHook postBuild
'';
installPhase = ''
+ runHook preInstall
+
mkdir -p $out/bin
# create a symbolic link for the repository directory
ln -s ${repository} $out/repository
- install -Dm644 target/${pname}-${version}.jar $out/share/java
+ install -Dm644 target/maven-demo-${finalAttrs.version}.jar $out/share/java
# create a wrapper that will automatically set the classpath
# this should be the paths from the dependency derivation
- makeWrapper ${jre}/bin/java $out/bin/${pname} \
- --add-flags "-jar $out/share/java/${pname}-${version}.jar"
+ makeWrapper ${jre}/bin/java $out/bin/maven-demo \
+ --add-flags "-jar $out/share/java/maven-demo-${finalAttrs.version}.jar"
+
+ runHook postInstall
'';
-}
+})
```
::: {.note}
Our script produces a dependency on `jre` rather than `jdk` to restrict the runtime closure necessary to run the application.
diff --git a/doc/languages-frameworks/neovim.section.md b/doc/languages-frameworks/neovim.section.md
index 111fb77c8bd2..b7c77b91734c 100644
--- a/doc/languages-frameworks/neovim.section.md
+++ b/doc/languages-frameworks/neovim.section.md
@@ -63,7 +63,7 @@ For instance, `sqlite-lua` needs `g:sqlite_clib_path` to be set to work. Nixpkgs
- `plugins`: A list of plugins to add to the wrapper.
```
-wrapNeovimUnstable {
+wrapNeovimUnstable neovim-unwrapped {
autoconfigure = true;
autowrapRuntimeDeps = true;
luaRcContent = ''
@@ -91,8 +91,8 @@ wrapNeovimUnstable {
You can explore the configuration with`nix repl` to discover these options and
override them. For instance:
```nix
-neovim.overrideAttrs(oldAttrs: {
- autowrapRuntimeDeps = false;
+neovim.overrideAttrs (oldAttrs: {
+ autowrapRuntimeDeps = false;
})
```
@@ -105,10 +105,10 @@ patch those plugins but expose the necessary configuration under
`PLUGIN.passthru.initLua` for neovim plugins. For instance, the `unicode-vim` plugin
needs the path towards a unicode database so we expose the following snippet `vim.g.Unicode_data_directory="${self.unicode-vim}/autoload/unicode"` under `vimPlugins.unicode-vim.passthru.initLua`.
-#### {#neovim-luarocks-based-plugins}
+#### LuaRocks based plugins {#neovim-luarocks-based-plugins}
In order to automatically handle plugin dependencies, several neovim plugins
-upload their package to [](www.luarocks.org). This means less work for nixpkgs maintainers in the long term as dependencies get updated automatically.
+upload their package to [LuaRocks](https://www.luarocks.org). This means less work for nixpkgs maintainers in the long term as dependencies get updated automatically.
This means several neovim plugins are first packaged as nixpkgs [lua
packages](#packaging-a-library-on-luarocks), and converted via `buildNeovimPlugin` in
a vim plugin. This conversion is necessary because neovim expects lua folders to be
@@ -116,9 +116,11 @@ top-level while luarocks installs them in various subfolders by default.
For instance:
```nix
-rtp-nvim = neovimUtils.buildNeovimPlugin {
+{
+ rtp-nvim = neovimUtils.buildNeovimPlugin {
luaAttr = luaPackages.rtp-nvim;
-};
+ };
+}
```
To update these packages, you should use the lua updater rather than vim's.
@@ -164,16 +166,19 @@ The check hook will fail the build if any modules cannot be loaded. This encoura
To only check a specific module, add it manually to the plugin definition [overrides](https://github.com/NixOS/nixpkgs/blob/master/pkgs/applications/editors/vim/plugins/overrides.nix).
```nix
+{
gitsigns-nvim = super.gitsigns-nvim.overrideAttrs {
dependencies = [ self.plenary-nvim ];
nvimRequireCheck = "gitsigns";
};
+}
```
Some plugins will have lua modules that require a user configuration to function properly or can contain optional lua modules that we dont want to test requiring.
We can skip specific modules using `nvimSkipModules`. Similar to `nvimRequireCheck`, it accepts a list of strings.
- `nvimSkipModules = [ MODULE1 MODULE2 ];`
```nix
+{
asyncrun-vim = super.asyncrun-vim.overrideAttrs {
nvimSkipModules = [
# vim plugin with optional toggleterm integration
@@ -181,14 +186,17 @@ We can skip specific modules using `nvimSkipModules`. Similar to `nvimRequireChe
"asyncrun.toggleterm2"
];
};
+}
```
In rare cases, we might not want to actually test loading lua modules for a plugin. In those cases, we can disable `neovimRequireCheck` with `doCheck = false;`.
This can be manually added through plugin definition overrides in the [overrides.nix](https://github.com/NixOS/nixpkgs/blob/master/pkgs/applications/editors/vim/plugins/overrides.nix).
```nix
+{
vim-test = super.vim-test.overrideAttrs {
# Vim plugin with a test lua file
doCheck = false;
};
+}
```
diff --git a/doc/languages-frameworks/nim.section.md b/doc/languages-frameworks/nim.section.md
index f0196c9d116f..ef3ecbba7d74 100644
--- a/doc/languages-frameworks/nim.section.md
+++ b/doc/languages-frameworks/nim.section.md
@@ -7,7 +7,11 @@ Nim programs are built using a lockfile and either `buildNimPackage` or `buildNi
The following example shows a Nim program that depends only on Nim libraries:
```nix
-{ lib, buildNimPackage, fetchFromGitHub }:
+{
+ lib,
+ buildNimPackage,
+ fetchFromGitHub,
+}:
buildNimPackage (finalAttrs: {
pname = "ttop";
@@ -91,7 +95,9 @@ The `buildNimPackage` and `buildNimSbom` functions generate flags and additional
```nix
pkgs.nitter.overrideNimAttrs {
# using a different source which has different dependencies from the standard package
- src = pkgs.fetchFromGithub { /* … */ };
+ src = pkgs.fetchFromGithub {
+ # …
+ };
# new lock file generated from the source
lockFile = ./custom-lock.json;
}
@@ -104,21 +110,25 @@ The default overrides are maintained as the top-level `nimOverrides` attrset at
For example, to propagate a dependency on SDL2 for lockfiles that select the Nim `sdl2` library, an overlay is added to the set in the `nim-overrides.nix` file:
```nix
-{ lib
-/* … */
-, SDL2
-/* … */
+{
+ lib,
+ # …
+ SDL2,
+# …
}:
{
- /* … */
+ # …
sdl2 =
lockAttrs:
- { buildInputs ? [ ], ... }:
+ {
+ buildInputs ? [ ],
+ ...
+ }:
{
buildInputs = buildInputs ++ [ SDL2 ];
};
- /* … */
+ # …
}
```
@@ -132,22 +142,28 @@ The `nimOverrides` attrset makes it possible to modify overrides in a few differ
Override a package internal to its definition:
```nix
-{ lib, buildNimPackage, nimOverrides, libressl }:
+{
+ lib,
+ buildNimPackage,
+ nimOverrides,
+ libressl,
+}:
let
buildNimPackage' = buildNimPackage.override {
nimOverrides = nimOverrides.override { openssl = libressl; };
};
-in buildNimPackage' (finalAttrs: {
+in
+buildNimPackage' (finalAttrs: {
pname = "foo";
# …
})
-
```
Override a package externally:
```nix
-{ pkgs }: {
+{ pkgs }:
+{
foo = pkgs.foo.override {
buildNimPackage = pkgs.buildNimPackage.override {
nimOverrides = pkgs.nimOverrides.override { openssl = libressl; };
diff --git a/doc/languages-frameworks/ocaml.section.md b/doc/languages-frameworks/ocaml.section.md
index 7f2c2a63a00b..62a54640b2fb 100644
--- a/doc/languages-frameworks/ocaml.section.md
+++ b/doc/languages-frameworks/ocaml.section.md
@@ -12,13 +12,18 @@ To open a shell able to build a typical OCaml project, put the dependencies in `
For example:
```nix
let
- pkgs = import {};
- # choose the ocaml version you want to use
- ocamlPackages = pkgs.ocaml-ng.ocamlPackages_4_12;
+ pkgs = import { };
+ # choose the ocaml version you want to use
+ ocamlPackages = pkgs.ocaml-ng.ocamlPackages_4_12;
in
pkgs.mkShell {
# build tools
- nativeBuildInputs = with ocamlPackages; [ ocaml findlib dune_2 ocaml-lsp ];
+ nativeBuildInputs = with ocamlPackages; [
+ ocaml
+ findlib
+ dune_2
+ ocaml-lsp
+ ];
# dependencies
buildInputs = with ocamlPackages; [ ocamlgraph ];
}
@@ -58,7 +63,8 @@ Here is a simple package example.
generates.
```nix
-{ lib,
+{
+ lib,
fetchFromGitHub,
buildDunePackage,
ocaml,
@@ -66,7 +72,8 @@ Here is a simple package example.
alcotest,
result,
bigstringaf,
- ppx_let }:
+ ppx_let,
+}:
buildDunePackage rec {
pname = "angstrom";
@@ -75,15 +82,21 @@ buildDunePackage rec {
minimalOCamlVersion = "4.04";
src = fetchFromGitHub {
- owner = "inhabitedtype";
- repo = pname;
- rev = version;
- hash = "sha256-MK8o+iPGANEhrrTc1Kz9LBilx2bDPQt7Pp5P2libucI=";
+ owner = "inhabitedtype";
+ repo = "angstrom";
+ tag = version;
+ hash = "sha256-MK8o+iPGANEhrrTc1Kz9LBilx2bDPQt7Pp5P2libucI=";
};
- checkInputs = [ alcotest ppx_let ];
+ checkInputs = [
+ alcotest
+ ppx_let
+ ];
buildInputs = [ ocaml-syntax-shims ];
- propagatedBuildInputs = [ bigstringaf result ];
+ propagatedBuildInputs = [
+ bigstringaf
+ result
+ ];
doCheck = lib.versionAtLeast ocaml.version "4.05";
meta = {
@@ -98,7 +111,11 @@ buildDunePackage rec {
Here is a second example, this time using a source archive generated with `dune-release`. It is a good idea to use this archive when it is available as it will usually contain substituted variables such as a `%%VERSION%%` field. This library does not depend on any other OCaml library and no tests are run after building it.
```nix
-{ lib, fetchurl, buildDunePackage }:
+{
+ lib,
+ fetchurl,
+ buildDunePackage,
+}:
buildDunePackage rec {
pname = "wtf8";
@@ -107,7 +124,7 @@ buildDunePackage rec {
minimalOCamlVersion = "4.02";
src = fetchurl {
- url = "https://github.com/flowtype/ocaml-${pname}/releases/download/v${version}/${pname}-v${version}.tbz";
+ url = "https://github.com/flowtype/ocaml-wtf8/releases/download/v${version}/wtf8-v${version}.tbz";
hash = "sha256-d5/3KUBAWRj8tntr4RkJ74KWW7wvn/B/m1nx0npnzyc=";
};
diff --git a/doc/languages-frameworks/octave.section.md b/doc/languages-frameworks/octave.section.md
index 4ad2cb0d5fbf..858dd611198f 100644
--- a/doc/languages-frameworks/octave.section.md
+++ b/doc/languages-frameworks/octave.section.md
@@ -3,7 +3,7 @@
## Introduction {#ssec-octave-introduction}
Octave is a modular scientific programming language and environment.
-A majority of the packages supported by Octave from their [website](https://octave.sourceforge.io/packages.php) are packaged in nixpkgs.
+A majority of the packages supported by Octave from their [website](https://gnu-octave.github.io/packages/) are packaged in nixpkgs.
## Structure {#ssec-octave-structure}
@@ -39,7 +39,9 @@ $ nix-shell -p 'octave.withPackages (ps: with ps; [ symbolic ])'
This will also work in a `shell.nix` file.
```nix
-{ pkgs ? import { }}:
+{
+ pkgs ? import { },
+}:
pkgs.mkShell {
nativeBuildInputs = with pkgs; [
diff --git a/doc/languages-frameworks/perl.section.md b/doc/languages-frameworks/perl.section.md
index 843d46584cdd..50fc4945ff76 100644
--- a/doc/languages-frameworks/perl.section.md
+++ b/doc/languages-frameworks/perl.section.md
@@ -39,7 +39,7 @@ Perl packages from CPAN are defined in [pkgs/top-level/perl-packages.nix](https:
pname = "Class-C3";
version = "0.21";
src = fetchurl {
- url = "mirror://cpan/authors/id/F/FL/FLORA/${pname}-${version}.tar.gz";
+ url = "mirror://cpan/authors/id/F/FL/FLORA/Class-C3-${version}.tar.gz";
hash = "sha256-/5GE5xHT0uYGOQxroqj6LMU7CtKn2s6vMVoSXxL4iK4=";
};
};
@@ -51,7 +51,10 @@ Note the use of `mirror://cpan/`, and the `pname` and `version` in the URL defin
```nix
{
foo = import ../path/to/foo.nix {
- inherit stdenv fetchurl /* ... */;
+ inherit
+ stdenv
+ fetchurl # ...
+ ;
inherit (perlPackages) ClassC3;
};
}
@@ -74,14 +77,18 @@ So what does `buildPerlPackage` do? It does the following:
`buildPerlPackage` is built on top of `stdenv`, so everything can be customised in the usual way. For instance, the `BerkeleyDB` module has a `preConfigure` hook to generate a configuration file used by `Makefile.PL`:
```nix
-{ buildPerlPackage, fetchurl, db }:
+{
+ buildPerlPackage,
+ fetchurl,
+ db,
+}:
buildPerlPackage rec {
pname = "BerkeleyDB";
version = "0.36";
src = fetchurl {
- url = "mirror://cpan/authors/id/P/PM/PMQS/${pname}-${version}.tar.gz";
+ url = "mirror://cpan/authors/id/P/PM/PMQS/BerkeleyDB-${version}.tar.gz";
hash = "sha256-4Y+HGgGQqcOfdiKcFIyMrWBEccVNVAMDBWZlFTMorh8=";
};
@@ -100,11 +107,14 @@ Dependencies on other Perl packages can be specified in the `buildInputs` and `p
pname = "Class-C3-Componentised";
version = "1.0004";
src = fetchurl {
- url = "mirror://cpan/authors/id/A/AS/ASH/${pname}-${version}.tar.gz";
+ url = "mirror://cpan/authors/id/A/AS/ASH/Class-C3-Componentised-${version}.tar.gz";
hash = "sha256-ASO9rV/FzJYZ0BH572Fxm2ZrFLMZLFATJng1NuU4FHc=";
};
propagatedBuildInputs = [
- ClassC3 ClassInspector TestException MROCompat
+ ClassC3
+ ClassInspector
+ TestException
+ MROCompat
];
};
}
@@ -113,7 +123,13 @@ Dependencies on other Perl packages can be specified in the `buildInputs` and `p
On Darwin, if a script has too many `-Idir` flags in its first line (its “shebang line”), it will not run. This can be worked around by calling the `shortenPerlShebang` function from the `postInstall` phase:
```nix
-{ lib, stdenv, buildPerlPackage, fetchurl, shortenPerlShebang }:
+{
+ lib,
+ stdenv,
+ buildPerlPackage,
+ fetchurl,
+ shortenPerlShebang,
+}:
{
ImageExifTool = buildPerlPackage {
@@ -121,7 +137,7 @@ On Darwin, if a script has too many `-Idir` flags in its first line (its “sheb
version = "12.50";
src = fetchurl {
- url = "https://exiftool.org/${pname}-${version}.tar.gz";
+ url = "https://exiftool.org/Image-ExifTool-${version}.tar.gz";
hash = "sha256-vOhB/FwQMC8PPvdnjDvxRpU6jAZcC6GMQfc0AH4uwKg=";
};
diff --git a/doc/languages-frameworks/php.section.md b/doc/languages-frameworks/php.section.md
index 1bcb4ee727a5..ce97da872677 100644
--- a/doc/languages-frameworks/php.section.md
+++ b/doc/languages-frameworks/php.section.md
@@ -45,24 +45,30 @@ extensions. For example, a PHP package with all default extensions and
ImageMagick enabled:
```nix
-php.withExtensions ({ enabled, all }:
- enabled ++ [ all.imagick ])
+php.withExtensions ({ enabled, all }: enabled ++ [ all.imagick ])
```
To exclude some, but not all, of the default extensions, you can
filter the `enabled` list like this:
```nix
-php.withExtensions ({ enabled, all }:
- (lib.filter (e: e != php.extensions.opcache) enabled)
- ++ [ all.imagick ])
+php.withExtensions (
+ { enabled, all }: (lib.filter (e: e != php.extensions.opcache) enabled) ++ [ all.imagick ]
+)
```
To build your list of extensions from the ground up, you can
ignore `enabled`:
```nix
-php.withExtensions ({ all, ... }: with all; [ imagick opcache ])
+php.withExtensions (
+ { all, ... }:
+ with all;
+ [
+ imagick
+ opcache
+ ]
+)
```
`php.withExtensions` provides extensions by wrapping a minimal php
@@ -82,7 +88,13 @@ and ImageMagick extensions enabled, and `memory_limit` set to `256M`:
```nix
php.buildEnv {
- extensions = { all, ... }: with all; [ imagick opcache ];
+ extensions =
+ { all, ... }:
+ with all;
+ [
+ imagick
+ opcache
+ ];
extraConfig = "memory_limit=256M";
}
```
@@ -94,8 +106,16 @@ follows:
```nix
let
- myPhp = php.withExtensions ({ all, ... }: with all; [ imagick opcache ]);
-in {
+ myPhp = php.withExtensions (
+ { all, ... }:
+ with all;
+ [
+ imagick
+ opcache
+ ]
+ );
+in
+{
services.phpfpm.pools."foo".phpPackage = myPhp;
}
```
@@ -103,10 +123,17 @@ in {
```nix
let
myPhp = php.buildEnv {
- extensions = { all, ... }: with all; [ imagick opcache ];
+ extensions =
+ { all, ... }:
+ with all;
+ [
+ imagick
+ opcache
+ ];
extraConfig = "memory_limit=256M";
};
-in {
+in
+{
services.phpfpm.pools."foo".phpPackage = myPhp;
}
```
@@ -132,9 +159,14 @@ won't work with that project unless those extensions are loaded.
Example of building `composer` with additional extensions:
```nix
-(php.withExtensions ({ all, enabled }:
- enabled ++ (with all; [ imagick redis ]))
-).packages.composer
+(php.withExtensions (
+ { all, enabled }:
+ enabled
+ ++ (with all; [
+ imagick
+ redis
+ ])
+)).packages.composer
```
### Overriding PHP packages {#ssec-php-user-guide-overriding-packages}
@@ -148,7 +180,7 @@ php.override {
packageOverrides = final: prev: {
extensions = prev.extensions // {
mysqlnd = prev.extensions.mysqlnd.overrideAttrs (attrs: {
- patches = attrs.patches or [] ++ [
+ patches = attrs.patches or [ ] ++ [
# ...
];
});
@@ -182,7 +214,7 @@ code, while others choose not to.
In Nix, there are multiple approaches to building a Composer-based project.
-One such method is the `php.buildComposerProject` helper function, which serves
+One such method is the `php.buildComposerProject2` helper function, which serves
as a wrapper around `mkDerivation`.
Using this function, you can build a PHP project that includes both a
@@ -217,27 +249,31 @@ To customize the PHP version, you can specify the `php` attribute. Similarly, if
you wish to modify the Composer version, use the `composer` attribute. It is
important to note that both attributes should be of the `derivation` type.
-Here's an example of working code example using `php.buildComposerProject`:
+Here's an example of working code example using `php.buildComposerProject2`:
```nix
{ php, fetchFromGitHub }:
-php.buildComposerProject (finalAttrs: {
+php.buildComposerProject2 (finalAttrs: {
pname = "php-app";
version = "1.0.0";
src = fetchFromGitHub {
owner = "git-owner";
repo = "git-repo";
- rev = finalAttrs.version;
+ tag = finalAttrs.version;
hash = "sha256-VcQRSss2dssfkJ+iUb5qT+FJ10GHiFDzySigcmuVI+8=";
};
# PHP version containing the `ast` extension enabled
php = php.buildEnv {
- extensions = ({ enabled, all }: enabled ++ (with all; [
- ast
- ]));
+ extensions = (
+ { enabled, all }:
+ enabled
+ ++ (with all; [
+ ast
+ ])
+ );
};
# The composer vendor hash
@@ -259,38 +295,45 @@ Here's a working code example to build a PHP library using `mkDerivation` and
separate functions and hooks:
```nix
-{ stdenvNoCC, fetchFromGitHub, php }:
+{
+ stdenvNoCC,
+ fetchFromGitHub,
+ php,
+}:
-stdenvNoCC.mkDerivation (finalAttrs:
-let
- src = fetchFromGitHub {
- owner = "git-owner";
- repo = "git-repo";
- rev = finalAttrs.version;
- hash = "sha256-VcQRSss2dssfkJ+iUb5qT+FJ10GHiFDzySigcmuVI+8=";
- };
-in {
- inherit src;
- pname = "php-app";
- version = "1.0.0";
+stdenvNoCC.mkDerivation (
+ finalAttrs:
+ let
+ src = fetchFromGitHub {
+ owner = "git-owner";
+ repo = "git-repo";
+ rev = finalAttrs.version;
+ hash = "sha256-VcQRSss2dssfkJ+iUb5qT+FJ10GHiFDzySigcmuVI+8=";
+ };
+ in
+ {
+ inherit src;
+ pname = "php-app";
+ version = "1.0.0";
- buildInputs = [ php ];
+ buildInputs = [ php ];
- nativeBuildInputs = [
- php.packages.composer
- # This hook will use the attribute `composerRepository`
- php.composerHooks.composerInstallHook
- ];
+ nativeBuildInputs = [
+ php.packages.composer
+ # This hook will use the attribute `composerRepository`
+ php.composerHooks.composerInstallHook
+ ];
- composerRepository = php.mkComposerRepository {
- inherit (finalAttrs) pname version src;
- composerNoDev = true;
- composerNoPlugins = true;
- composerNoScripts = true;
- # Specifying a custom composer.lock since it is not present in the sources.
- composerLock = ./composer.lock;
- # The composer vendor hash
- vendorHash = "sha256-86s/F+/5cBAwBqZ2yaGRM5rTGLmou5//aLRK5SA0WiQ=";
- };
-})
+ composerRepository = php.mkComposerRepository {
+ inherit (finalAttrs) pname version src;
+ composerNoDev = true;
+ composerNoPlugins = true;
+ composerNoScripts = true;
+ # Specifying a custom composer.lock since it is not present in the sources.
+ composerLock = ./composer.lock;
+ # The composer vendor hash
+ vendorHash = "sha256-86s/F+/5cBAwBqZ2yaGRM5rTGLmou5//aLRK5SA0WiQ=";
+ };
+ }
+)
```
diff --git a/doc/languages-frameworks/pkg-config.section.md b/doc/languages-frameworks/pkg-config.section.md
index 0b25396314cb..f788d5500c0d 100644
--- a/doc/languages-frameworks/pkg-config.section.md
+++ b/doc/languages-frameworks/pkg-config.section.md
@@ -17,9 +17,12 @@ A good example of all these things is miniz:
{ pkg-config, testers, ... }:
stdenv.mkDerivation (finalAttrs: {
- /* ... */
+ # ...
- nativeBuildInputs = [ pkg-config validatePkgConfig ];
+ nativeBuildInputs = [
+ pkg-config
+ validatePkgConfig
+ ];
passthru.tests.pkg-config = testers.hasPkgConfigModules {
package = finalAttrs.finalPackage;
@@ -27,7 +30,7 @@ stdenv.mkDerivation (finalAttrs: {
};
meta = {
- /* ... */
+ # ...
pkgConfigModules = [ "miniz" ];
};
})
diff --git a/doc/languages-frameworks/python.section.md b/doc/languages-frameworks/python.section.md
index bbc5da116a6a..af951cc3e805 100644
--- a/doc/languages-frameworks/python.section.md
+++ b/doc/languages-frameworks/python.section.md
@@ -50,7 +50,6 @@ sets are
* `pkgs.python27Packages`
* `pkgs.python3Packages`
-* `pkgs.python39Packages`
* `pkgs.python310Packages`
* `pkgs.python311Packages`
* `pkgs.python312Packages`
@@ -79,24 +78,25 @@ using setup hooks.
The following is an example:
```nix
-{ lib
-, buildPythonPackage
-, fetchPypi
+{
+ lib,
+ buildPythonPackage,
+ fetchPypi,
-# build-system
-, setuptools
-, setuptools-scm
+ # build-system
+ setuptools,
+ setuptools-scm,
-# dependencies
-, attrs
-, pluggy
-, py
-, setuptools
-, six
+ # dependencies
+ attrs,
+ pluggy,
+ py,
+ setuptools,
+ six,
-# tests
-, hypothesis
- }:
+ # tests
+ hypothesis,
+}:
buildPythonPackage rec {
pname = "pytest";
@@ -135,7 +135,12 @@ buildPythonPackage rec {
description = "Framework for writing tests";
homepage = "https://github.com/pytest-dev/pytest";
license = lib.licenses.mit;
- maintainers = with lib.maintainers; [ domenkozar lovek323 madjar lsix ];
+ maintainers = with lib.maintainers; [
+ domenkozar
+ lovek323
+ madjar
+ lsix
+ ];
};
}
```
@@ -232,23 +237,31 @@ override first the Python interpreter and pass `packageOverrides` which contains
the overrides for packages in the package set.
```nix
-with import {};
+with import { };
-(let
- python = let
- packageOverrides = self: super: {
- pandas = super.pandas.overridePythonAttrs(old: rec {
- version = "0.19.1";
- src = fetchPypi {
- pname = "pandas";
- inherit version;
- hash = "sha256-JQn+rtpy/OA2deLszSKEuxyttqBzcAil50H+JDHUdCE=";
+(
+ let
+ python =
+ let
+ packageOverrides = self: super: {
+ pandas = super.pandas.overridePythonAttrs (old: rec {
+ version = "0.19.1";
+ src = fetchPypi {
+ pname = "pandas";
+ inherit version;
+ hash = "sha256-JQn+rtpy/OA2deLszSKEuxyttqBzcAil50H+JDHUdCE=";
+ };
+ });
};
- });
- };
- in pkgs.python3.override {inherit packageOverrides; self = python;};
+ in
+ pkgs.python3.override {
+ inherit packageOverrides;
+ self = python;
+ };
-in python.withPackages(ps: [ ps.blaze ])).env
+ in
+ python.withPackages (ps: [ ps.blaze ])
+).env
```
The next example shows a non trivial overriding of the `blas` implementation to
@@ -259,12 +272,16 @@ be used through out all of the Python package set:
python3MyBlas = pkgs.python3.override {
packageOverrides = self: super: {
# We need toPythonModule for the package set to evaluate this
- blas = super.toPythonModule(super.pkgs.blas.override {
- blasProvider = super.pkgs.mkl;
- });
- lapack = super.toPythonModule(super.pkgs.lapack.override {
- lapackProvider = super.pkgs.mkl;
- });
+ blas = super.toPythonModule (
+ super.pkgs.blas.override {
+ blasProvider = super.pkgs.mkl;
+ }
+ );
+ lapack = super.toPythonModule (
+ super.pkgs.lapack.override {
+ lapackProvider = super.pkgs.mkl;
+ }
+ );
};
};
}
@@ -291,9 +308,10 @@ called with `callPackage` and passed `python3` or `python3Packages` (possibly
specifying an interpreter version), like this:
```nix
-{ lib
-, python3Packages
-, fetchPypi
+{
+ lib,
+ python3Packages,
+ fetchPypi,
}:
python3Packages.buildPythonApplication rec {
@@ -303,7 +321,7 @@ python3Packages.buildPythonApplication rec {
src = fetchPypi {
inherit pname version;
- hash = "sha256-Pe229rT0aHwA98s+nTHQMEFKZPo/yw6sot8MivFDvAw=";
+ hash = "sha256-Pe229rT0aHwA98s+nTHQMEFKZPo/yw6sot8MivFDvAw=";
};
build-system = with python3Packages; [
@@ -357,10 +375,12 @@ modifications.
```nix
{
- opencv = toPythonModule (pkgs.opencv.override {
- enablePython = true;
- pythonPackages = self;
- });
+ opencv = toPythonModule (
+ pkgs.opencv.override {
+ enablePython = true;
+ pythonPackages = self;
+ }
+ );
}
```
@@ -395,8 +415,10 @@ The `build-system`'s provided will instead become runtime dependencies of the ed
Note that overriding packages deeper in the dependency graph _can_ work, but it's not the primary use case and overriding existing packages can make others break in unexpected ways.
-``` nix
-{ pkgs ? import { } }:
+```nix
+{
+ pkgs ? import { },
+}:
let
pyproject = pkgs.lib.importTOML ./pyproject.toml;
@@ -419,9 +441,10 @@ let
};
};
- pythonEnv = myPython.withPackages (ps: [ ps.my-editable ]);
+ pythonEnv = myPython.withPackages (ps: [ ps.my-editable ]);
-in pkgs.mkShell {
+in
+pkgs.mkShell {
packages = [ pythonEnv ];
}
```
@@ -433,7 +456,7 @@ This example shows how to create an environment that has the Pyramid Web Framewo
Saving the following as `default.nix`
```nix
-with import {};
+with import { };
python3.buildEnv.override {
extraLibs = [ python3Packages.pyramid ];
@@ -454,7 +477,7 @@ packages installed. This is somewhat comparable to `virtualenv`. For example,
running `nix-shell` with the following `shell.nix`
```nix
-with import {};
+with import { };
(python3.buildEnv.override {
extraLibs = with python3Packages; [
@@ -484,7 +507,7 @@ of the packages to be included in the environment. Using the [`withPackages`](#p
example for the Pyramid Web Framework environment can be written like this:
```nix
-with import {};
+with import { };
python.withPackages (ps: [ ps.pyramid ])
```
@@ -494,7 +517,7 @@ version as an argument to the function. In the above example, `ps` equals
`pythonPackages`. But you can also easily switch to using python3:
```nix
-with import {};
+with import { };
python3.withPackages (ps: [ ps.pyramid ])
```
@@ -506,12 +529,14 @@ supports the `env` attribute. The `shell.nix` file from the previous section can
thus be also written like this:
```nix
-with import {};
+with import { };
-(python3.withPackages (ps: with ps; [
- numpy
- requests
-])).env
+(python3.withPackages (
+ ps: with ps; [
+ numpy
+ requests
+ ]
+)).env
```
In contrast to [`python.buildEnv`](#python.buildenv-function), [`python.withPackages`](#python.withpackages-function) does not support the
@@ -759,11 +784,13 @@ Say we want to have Python 3.12, `numpy` and `toolz`, like before,
in an environment. We can add a `shell.nix` file describing our dependencies:
```nix
-with import {};
-(python312.withPackages (ps: with ps; [
- numpy
- toolz
-])).env
+with import { };
+(python312.withPackages (
+ ps: with ps; [
+ numpy
+ toolz
+ ]
+)).env
```
And then at the command line, just typing `nix-shell` produces the same
@@ -786,13 +813,14 @@ What's happening here?
To combine this with `mkShell` you can:
```nix
-with import {};
+with import { };
let
pythonEnv = python312.withPackages (ps: [
ps.numpy
ps.toolz
]);
-in mkShell {
+in
+mkShell {
packages = [
pythonEnv
@@ -869,10 +897,16 @@ For the sake of completeness, here's how to install the environment system-wide
on NixOS.
```nix
-{ # ...
+{
+ # ...
environment.systemPackages = with pkgs; [
- (python310.withPackages(ps: with ps; [ numpy toolz ]))
+ (python310.withPackages (
+ ps: with ps; [
+ numpy
+ toolz
+ ]
+ ))
];
}
```
@@ -892,10 +926,11 @@ building Python libraries is [`buildPythonPackage`](#buildpythonpackage-function
`toolz` package.
```nix
-{ lib
-, buildPythonPackage
-, fetchPypi
-, setuptools
+{
+ lib,
+ buildPythonPackage,
+ fetchPypi,
+ setuptools,
}:
buildPythonPackage rec {
@@ -953,9 +988,10 @@ The following expression creates a derivation for the `toolz` package,
and adds it along with a `numpy` package to a Python environment.
```nix
-with import {};
+with import { };
-( let
+(
+ let
my_toolz = python312.pkgs.buildPythonPackage rec {
pname = "toolz";
version = "0.10.0";
@@ -980,10 +1016,13 @@ with import {};
};
};
- in python312.withPackages (ps: with ps; [
- numpy
- my_toolz
- ])
+ in
+ python312.withPackages (
+ ps: with ps; [
+ numpy
+ my_toolz
+ ]
+ )
).env
```
@@ -1015,18 +1054,21 @@ The following example shows which arguments are given to [`buildPythonPackage`](
order to build [`datashape`](https://github.com/blaze/datashape).
```nix
-{ lib
-, buildPythonPackage
-, fetchPypi
+{
+ lib,
+ buildPythonPackage,
+ fetchPypi,
-# build dependencies
-, setuptools
+ # build dependencies
+ setuptools,
-# dependencies
-, numpy, multipledispatch, python-dateutil
+ # dependencies
+ numpy,
+ multipledispatch,
+ python-dateutil,
-# tests
-, pytestCheckHook
+ # tests
+ pytestCheckHook,
}:
buildPythonPackage rec {
@@ -1073,12 +1115,13 @@ Python bindings to `libxml2` and `libxslt`. These libraries are only required
when building the bindings and are therefore added as [`buildInputs`](#var-stdenv-buildInputs).
```nix
-{ lib
-, buildPythonPackage
-, fetchPypi
-, setuptools
-, libxml2
-, libxslt
+{
+ lib,
+ buildPythonPackage,
+ fetchPypi,
+ setuptools,
+ libxml2,
+ libxslt,
}:
buildPythonPackage rec {
@@ -1129,19 +1172,20 @@ The bindings don't expect to find each of them in a different folder, and
therefore we have to set `LDFLAGS` and `CFLAGS`.
```nix
-{ lib
-, buildPythonPackage
-, fetchPypi
+{
+ lib,
+ buildPythonPackage,
+ fetchPypi,
-# build dependencies
-, setuptools
+ # build dependencies
+ setuptools,
-# dependencies
-, fftw
-, fftwFloat
-, fftwLongDouble
-, numpy
-, scipy
+ # dependencies
+ fftw,
+ fftwFloat,
+ fftwLongDouble,
+ numpy,
+ scipy,
}:
buildPythonPackage rec {
@@ -1183,7 +1227,10 @@ buildPythonPackage rec {
changelog = "https://github.com/pyFFTW/pyFFTW/releases/tag/v${version}";
description = "Pythonic wrapper around FFTW, the FFT library, presenting a unified interface for all the supported transforms";
homepage = "http://hgomersall.github.com/pyFFTW";
- license = with lib.licenses; [ bsd2 bsd3 ];
+ license = with lib.licenses; [
+ bsd2
+ bsd3
+ ];
};
}
```
@@ -1233,36 +1280,14 @@ test run would be:
However, many repositories' test suites do not translate well to nix's build
sandbox, and will generally need many tests to be disabled.
-To filter tests using pytest, one can do the following:
+This is achievable by
+- Including paths or test items (`path/to/file.py::MyClass` or `path/to/file.py::MyClass::test_method`) with positional arguments.
+- Excluding paths with `--ignore` or globbed paths with `--ignore-glob`.
+- Excluding test items using the `--deselect` flag.
+- Including or excluding classes or test methods by their name using the `-k` flag.
+- Including or excluding test by their marks using the `-m` flag.
-```nix
-{
- nativeCheckInputs = [ pytest ];
- # avoid tests which need additional data or touch network
- checkPhase = ''
- runHook preCheck
-
- pytest tests/ --ignore=tests/integration -k 'not download and not update' --ignore=tests/test_failing.py
-
- runHook postCheck
- '';
-}
-```
-
-`--ignore` will tell pytest to ignore that file or directory from being
-collected as part of a test run. This is useful is a file uses a package
-which is not available in nixpkgs, thus skipping that test file is much
-easier than having to create a new package.
-
-`-k` is used to define a predicate for test names. In this example, we are
-filtering out tests which contain `download` or `update` in their test case name.
-Only one `-k` argument is allowed, and thus a long predicate should be concatenated
-with “\\” and wrapped to the next line.
-
-::: {.note}
-In pytest==6.0.1, the use of “\\” to continue a line (e.g. `-k 'not download \'`) has
-been removed, in this case, it's recommended to use `pytestCheckHook`.
-:::
+We highly recommend `pytestCheckHook` for an easier and more structural setup.
#### Using pytestCheckHook {#using-pytestcheckhook}
@@ -1272,7 +1297,40 @@ when a package may need many items disabled to run the test suite.
Most packages use `pytest` or `unittest`, which is compatible with `pytest`,
so you will most likely use `pytestCheckHook`.
-Using the example above, the analogous `pytestCheckHook` usage would be:
+To use `pytestCheckHook`, add it to `nativeCheckInputs`.
+Adding `pytest` is not required, since it is included with `pytestCheckHook`.
+
+```nix
+{
+ nativeCheckInputs = [
+ pytestCheckHook
+ ];
+}
+```
+
+`pytestCheckHook` recognizes the following attributes:
+
+`enabledTestPaths` and `disabledTestPaths`
+
+: To specify path globs (files or directories) or test items.
+
+`enabledTests` and `disabledTests`
+
+: To specify keywords for class names or test method names.
+
+`enabledTestMarks` and `disabledTestMarks`
+
+: To specify test marks.
+
+`pytestFlags`
+
+: To append additional command-line arguments to `pytest`.
+
+By default, `pytest` automatically discovers which tests to run.
+If tests are explicitly enabled, only those tests will run.
+A test, that is both enabled and disabled, will not run.
+
+The following example demonstrates usage of various `pytestCheckHook` attributes:
```nix
{
@@ -1280,46 +1338,92 @@ Using the example above, the analogous `pytestCheckHook` usage would be:
pytestCheckHook
];
- # requires additional data
- pytestFlags = [
+ # Allow running the following test paths and test objects.
+ enabledTestPaths = [
+ # Find tests under the tests directory.
+ # The trailing slash is not necessary.
"tests/"
- "--ignore=tests/integration"
- ];
-
- disabledTests = [
- # touches network
- "download"
- "update"
+
+ # Additionally run test_foo
+ "other-tests/test_foo.py::Foo::test_foo"
];
+ # Override the above-enabled test paths and test objects.
disabledTestPaths = [
- "tests/test_failing.py"
+ # Tests under tests/integration requires additional data.
+ "tests/integration"
+ ];
+
+ # Allow tests by keywords matching their class names or method names.
+ enabledTests = [
+ # pytest by default only runs test methods begin with "test_" or end with "_test".
+ # This includes all functions whose name contains "test".
+ "test"
+ ];
+
+ # Override the above-enabled tests by keywords matching their class names or method names.
+ disabledTests = [
+ # Tests touching networks.
+ "upload"
+ "download"
+ ];
+
+ # Additional pytest flags
+ pytestFlags = [
+ # Disable benchmarks and run benchmarking tests only once.
+ "--benchmark-disable"
];
}
```
-This is especially useful when tests need to be conditionally disabled,
-for example:
+These attributes are all passed into the derivation directly
+and added to the `pytest` command without additional Bash expansion.
+It requires `__structuredAttrs = true` to pass list elements containing spaces.
+
+The `TestsPaths` attributes expand Unix-style globs.
+If a test path contains characters like `*`, `?`, `[`, or `]`, you can
+quote them with square brackets (`[*]`, `[?]`, `[[]`, and `[]]`) to match literally.
+
+The `Tests` and `TestMarks` attribute pairs
+form a logical expression `((included_element1) or (included_element2)) and not (excluded_element1) and not (excluded_element2)`
+which will be passed to pytest's `-k` and `-m` flags respectively.
+With `__structuredAttrs = true` enabled, they additionally support sub-expressions.
+
+For example, you could disable test items like `TestFoo::test_bar_functionality`
+by disabling tests that match both `"Foo"` **and** `"bar"`:
```nix
{
+ __structuredAttrs = true;
+
disabledTests = [
- # touches network
- "download"
- "update"
- ] ++ lib.optionals (pythonAtLeast "3.8") [
- # broken due to python3.8 async changes
- "async"
- ] ++ lib.optionals stdenv.buildPlatform.isDarwin [
- # can fail when building with other packages
- "socket"
+ "Foo and bar"
];
}
```
-Trying to concatenate the related strings to disable tests in a regular
-[`checkPhase`](#ssec-check-phase) would be much harder to read. This also enables us to comment on
-why specific tests are disabled.
+The main benefits of using `pytestCheckHook` to construct `pytest` commands
+is structuralization and eval-time accessibility.
+This is especially helpful to select tests or specify flags conditionally:
+
+```nix
+{
+ disabledTests =
+ [
+ # touches network
+ "download"
+ "update"
+ ]
+ ++ lib.optionals (pythonAtLeast "3.8") [
+ # broken due to python3.8 async changes
+ "async"
+ ]
+ ++ lib.optionals stdenv.buildPlatform.isDarwin [
+ # can fail when building with other packages
+ "socket"
+ ];
+}
+```
#### Using pythonImportsCheck {#using-pythonimportscheck}
@@ -1442,7 +1546,9 @@ automatically add `pythonRelaxDepsHook` if either `pythonRelaxDeps` or
];
unittestFlags = [
- "-s" "tests" "-v"
+ "-s"
+ "tests"
+ "-v"
];
}
```
@@ -1523,10 +1629,11 @@ Let's split the package definition from the environment definition.
We first create a function that builds `toolz` in `~/path/to/toolz/release.nix`
```nix
-{ lib
-, buildPythonPackage
-, fetchPypi
-, setuptools
+{
+ lib,
+ buildPythonPackage,
+ fetchPypi,
+ setuptools,
}:
buildPythonPackage rec {
@@ -1556,13 +1663,15 @@ It takes an argument [`buildPythonPackage`](#buildpythonpackage-function). We no
`callPackage` in the definition of our environment
```nix
-with import {};
+with import { };
-( let
+(
+ let
toolz = callPackage /path/to/toolz/release.nix {
buildPythonPackage = python3Packages.buildPythonPackage;
};
- in python3.withPackages (ps: [
+ in
+ python3.withPackages (ps: [
ps.numpy
toolz
])
@@ -1590,20 +1699,27 @@ We can override the interpreter and pass `packageOverrides`. In the following
example we rename the `pandas` package and build it.
```nix
-with import {};
+with import { };
-(let
- python = let
- packageOverrides = self: super: {
- pandas = super.pandas.overridePythonAttrs(old: {name="foo";});
- };
- in pkgs.python310.override {
- inherit packageOverrides;
- };
+(
+ let
+ python =
+ let
+ packageOverrides = self: super: {
+ pandas = super.pandas.overridePythonAttrs (old: {
+ name = "foo";
+ });
+ };
+ in
+ pkgs.python310.override {
+ inherit packageOverrides;
+ };
-in python.withPackages (ps: [
- ps.pandas
-])).env
+ in
+ python.withPackages (ps: [
+ ps.pandas
+ ])
+).env
```
Using `nix-build` on this expression will build an environment that contains the
@@ -1617,17 +1733,20 @@ environment that uses it. All packages in the Python package set will now use
the updated `scipy` version.
```nix
-with import {};
+with import { };
-( let
+(
+ let
packageOverrides = self: super: {
scipy = super.scipy_0_17;
};
- in (pkgs.python310.override {
+ in
+ (pkgs.python310.override {
inherit packageOverrides;
- }).withPackages (ps: [
- ps.blaze
- ])
+ }).withPackages
+ (ps: [
+ ps.blaze
+ ])
).env
```
@@ -1639,15 +1758,22 @@ If you want the whole of Nixpkgs to use your modifications, then you can use
```nix
let
- pkgs = import {};
- newpkgs = import pkgs.path { overlays = [ (self: super: {
- python310 = let
- packageOverrides = python-self: python-super: {
- numpy = python-super.numpy_1_18;
- };
- in super.python310.override {inherit packageOverrides;};
- } ) ]; };
-in newpkgs.inkscape
+ pkgs = import { };
+ newpkgs = import pkgs.path {
+ overlays = [
+ (self: super: {
+ python310 =
+ let
+ packageOverrides = python-self: python-super: {
+ numpy = python-super.numpy_1_18;
+ };
+ in
+ super.python310.override { inherit packageOverrides; };
+ })
+ ];
+ };
+in
+newpkgs.inkscape
```
### `python setup.py bdist_wheel` cannot create .whl {#python-setup.py-bdist_wheel-cannot-create-.whl}
@@ -1737,7 +1863,8 @@ with import { };
let
pythonPackages = python3Packages;
-in pkgs.mkShell rec {
+in
+pkgs.mkShell rec {
name = "impurePythonEnv";
venvDir = "./.venv";
buildInputs = [
@@ -1792,7 +1919,8 @@ with import { };
let
venvDir = "./.venv";
pythonPackages = python3Packages;
-in pkgs.mkShell rec {
+in
+pkgs.mkShell rec {
name = "impurePythonEnv";
buildInputs = [
pythonPackages.python
@@ -1904,13 +2032,11 @@ The following overlay overrides the call to [`buildPythonPackage`](#buildpythonp
```nix
final: prev: {
pythonPackagesExtensions = prev.pythonPackagesExtensions ++ [
- (
- python-final: python-prev: {
- foo = python-prev.foo.overridePythonAttrs (oldAttrs: {
- # ...
- });
- }
- )
+ (python-final: python-prev: {
+ foo = python-prev.foo.overridePythonAttrs (oldAttrs: {
+ # ...
+ });
+ })
];
}
```
@@ -1936,13 +2062,14 @@ interpreter of interest, e.g using
```nix
let
- pkgs = import ./. {};
+ pkgs = import ./. { };
mypython = pkgs.python3.override {
enableOptimizations = true;
reproducibleBuild = false;
self = mypython;
};
-in mypython
+in
+mypython
```
### How to add optional dependencies? {#python-optional-dependencies}
@@ -1975,6 +2102,14 @@ Note this method is preferred over adding parameters to builders, as that can
result in packages depending on different variants and thereby causing
collisions.
+::: {.note}
+The `optional-dependencies` attribute should only be used for dependency groups
+as defined in package metadata. If a package gracefully handles missing
+dependencies in runtime but doesn't advertise it through package metadata, then
+these dependencies should not be listed at all. (One may still have to list
+them in `nativeCheckInputs` to pass test suite.)
+:::
+
### How to contribute a Python package to nixpkgs? {#tools}
Packages inside nixpkgs must use the [`buildPythonPackage`](#buildpythonpackage-function) or [`buildPythonApplication`](#buildpythonapplication-function) function directly,
@@ -1982,6 +2117,7 @@ because we can only provide security support for non-vendored dependencies.
We recommend [nix-init](https://github.com/nix-community/nix-init) for creating new python packages within nixpkgs,
as it already prefetches the source, parses dependencies for common formats and prefills most things in `meta`.
+When using the tool, pull from the original source repository instead of PyPI, if possible.
See also [contributing section](#contributing).
@@ -2008,38 +2144,9 @@ Occasionally packages don't make use of a common test framework, which may then
#### Common issues {#common-issues}
-* Non-working tests can often be deselected. Most Python modules
- do follow the standard test protocol where the pytest runner can be used.
- `pytest` supports the `-k` and `--ignore-glob` parameters to ignore test
- methods or classes as well as whole files. For `pytestCheckHook` these are
- conveniently exposed as `disabledTests` and `disabledTestPaths` respectively.
-
- ```nix
- buildPythonPackage {
- # ...
- nativeCheckInputs = [
- pytestCheckHook
- ];
-
- disabledTests = [
- "function_name"
- "other_function"
- ];
-
- disabledTestPaths = [
- "path/to/performance.py"
- "path/to/connect-*.py"
- ];
- }
- ```
-
- ::: {.note}
- If the test path to disable contains characters like `*`, `?`, `[`, and `]`,
- quote them with square brackets (`[*]`, `[?]`, `[[]`, and `[]]`) to match literally.
- :::
-
-* Tests that attempt to access `$HOME` can be fixed by using the following
- work-around before running tests (e.g. `preCheck`): `export HOME=$(mktemp -d)`
+* Tests that attempt to access `$HOME` can be fixed by using `writableTmpDirAsHomeHook` in
+ `nativeCheckInputs`, which sets up a writable temporary directory as the home directory. Alternatively,
+ you can achieve the same effect manually (e.g. in `preCheck`) with: `export HOME=$(mktemp -d)`.
* Compiling with Cython causes tests to fail with a `ModuleNotLoadedError`.
This can be fixed with two changes in the derivation: 1) replacing `pytest` with
`pytestCheckHook` and 2) adding a `preCheck` containing `cd $out` to run
@@ -2084,12 +2191,17 @@ The following rules are desired to be respected:
that characters should be converted to lowercase and `.` and `_` should be
replaced by a single `-` (foo-bar-baz instead of Foo__Bar.baz).
If necessary, `pname` has to be given a different value within `fetchPypi`.
+* It's generally preferable to fetch `src` directly from the repo and not from
+ PyPI. Use `fetchPypi` when there's a clear technical reason to do so.
* Packages from sources such as GitHub and GitLab that do not exist on PyPI
should not use a name that is already used on PyPI. When possible, they should
use the package repository name prefixed with the owner (e.g. organization) name
and using a `-` as delimiter.
* Attribute names in `python-packages.nix` should be sorted alphanumerically to
avoid merge conflicts and ease locating attributes.
+* Non-python runtime dependencies should be added via explicit wrapping or
+ patching (using e.g. `substituteInPlace`), rather than through propagation via
+ `dependencies`/`propagatedBuildInputs`, to reduce clutter in `$PATH`.
This list is useful for reviewers as well as for self-checking when submitting packages.
diff --git a/doc/languages-frameworks/qt.section.md b/doc/languages-frameworks/qt.section.md
index 9b3d1e054a62..cdbcb3aefb7c 100644
--- a/doc/languages-frameworks/qt.section.md
+++ b/doc/languages-frameworks/qt.section.md
@@ -64,14 +64,18 @@ and then create wrappers manually in `fixupPhase`, using `wrapQtApp`, which itse
The `makeWrapper` arguments required for Qt are also exposed in the environment as `$qtWrapperArgs`.
```nix
-{ stdenv, lib, wrapQtAppsHook }:
+{
+ stdenv,
+ lib,
+ wrapQtAppsHook,
+}:
stdenv.mkDerivation {
# ...
nativeBuildInputs = [ wrapQtAppsHook ];
dontWrapQtApps = true;
preFixup = ''
- wrapQtApp "$out/bin/myapp" --prefix PATH : /path/to/bin
+ wrapQtApp "$out/bin/myapp" --prefix PATH : /path/to/bin
'';
}
```
diff --git a/doc/languages-frameworks/r.section.md b/doc/languages-frameworks/r.section.md
index d25c5e848232..efbd170cd034 100644
--- a/doc/languages-frameworks/r.section.md
+++ b/doc/languages-frameworks/r.section.md
@@ -7,18 +7,22 @@ use by adding the following snippet to your $HOME/.config/nixpkgs/config.nix fil
```nix
{
- packageOverrides = super: let self = super.pkgs; in
+ packageOverrides =
+ super:
+ let
+ self = super.pkgs;
+ in
{
- rEnv = super.rWrapper.override {
- packages = with self.rPackages; [
- devtools
- ggplot2
- reshape2
- yaml
- optparse
- ];
- };
+ rEnv = super.rWrapper.override {
+ packages = with self.rPackages; [
+ devtools
+ ggplot2
+ reshape2
+ yaml
+ optparse
+ ];
+ };
};
}
```
@@ -33,7 +37,7 @@ environment available for other contributors, you can create a `default.nix`
file like so:
```nix
-with import {};
+with import { };
{
myProject = stdenv.mkDerivation {
name = "myProject";
@@ -60,16 +64,20 @@ environment, see `rstudioWrapper`, which functions similarly to
```nix
{
- packageOverrides = super: let self = super.pkgs; in
+ packageOverrides =
+ super:
+ let
+ self = super.pkgs;
+ in
{
- rstudioEnv = super.rstudioWrapper.override {
- packages = with self.rPackages; [
- dplyr
- ggplot2
- reshape2
- ];
- };
+ rstudioEnv = super.rstudioWrapper.override {
+ packages = with self.rPackages; [
+ dplyr
+ ggplot2
+ reshape2
+ ];
+ };
};
}
```
@@ -81,13 +89,17 @@ Alternatively, you can create a self-contained `shell.nix` without the need to
modify any configuration files:
```nix
-{ pkgs ? import {}
+{
+ pkgs ? import { },
}:
pkgs.rstudioWrapper.override {
- packages = with pkgs.rPackages; [ dplyr ggplot2 reshape2 ];
+ packages = with pkgs.rPackages; [
+ dplyr
+ ggplot2
+ reshape2
+ ];
}
-
```
Executing `nix-shell` will then drop you into an environment equivalent to the
diff --git a/doc/languages-frameworks/ruby.section.md b/doc/languages-frameworks/ruby.section.md
index 31f696bd6427..d11078aacaf2 100644
--- a/doc/languages-frameworks/ruby.section.md
+++ b/doc/languages-frameworks/ruby.section.md
@@ -36,8 +36,13 @@ As explained [in the `nix-shell` section](https://nixos.org/manual/nix/stable/co
Say we want to have Ruby, `nokogori`, and `pry`. Consider a `shell.nix` file with:
```nix
-with import {};
-ruby.withPackages (ps: with ps; [ nokogiri pry ])
+with import { };
+ruby.withPackages (
+ ps: with ps; [
+ nokogiri
+ pry
+ ]
+)
```
What's happening here?
@@ -107,7 +112,13 @@ let
name = "gems-for-some-project";
gemdir = ./.;
};
-in mkShell { packages = [ gems gems.wrappedRuby ]; }
+in
+mkShell {
+ packages = [
+ gems
+ gems.wrappedRuby
+ ];
+}
```
With this file in your directory, you can run `nix-shell` to build and use the gems. The important parts here are `bundlerEnv` and `wrappedRuby`.
@@ -118,7 +129,12 @@ One common issue that you might have is that you have Ruby, but also `bundler` i
```nix
# ...
-mkShell { buildInputs = [ gems (lowPrio gems.wrappedRuby) ]; }
+mkShell {
+ buildInputs = [
+ gems
+ (lowPrio gems.wrappedRuby)
+ ];
+}
```
Sometimes a Gemfile references other files. Such as `.ruby-version` or vendored gems. When copying the Gemfile to the nix store we need to copy those files alongside. This can be done using `extraConfigPaths`. For example:
@@ -148,41 +164,54 @@ Two places that allow this modification are the `ruby` derivation, or `bundlerEn
Here's the `ruby` one:
```nix
-{ pg_version ? "10", pkgs ? import { } }:
+{
+ pg_version ? "10",
+ pkgs ? import { },
+}:
let
myRuby = pkgs.ruby.override {
defaultGemConfig = pkgs.defaultGemConfig // {
pg = attrs: {
- buildFlags =
- [ "--with-pg-config=${lib.getDev pkgs."postgresql_${pg_version}"}/bin/pg_config" ];
+ buildFlags = [ "--with-pg-config=${pkgs."postgresql_${pg_version}".pg_config}/bin/pg_config" ];
};
};
};
-in myRuby.withPackages (ps: with ps; [ pg ])
+in
+myRuby.withPackages (ps: with ps; [ pg ])
```
And an example with `bundlerEnv`:
```nix
-{ pg_version ? "10", pkgs ? import { } }:
+{
+ pg_version ? "10",
+ pkgs ? import { },
+}:
let
gems = pkgs.bundlerEnv {
name = "gems-for-some-project";
gemdir = ./.;
gemConfig = pkgs.defaultGemConfig // {
pg = attrs: {
- buildFlags =
- [ "--with-pg-config=${lib.getDev pkgs."postgresql_${pg_version}"}/bin/pg_config" ];
+ buildFlags = [ "--with-pg-config=${pkgs."postgresql_${pg_version}".pg_config}/bin/pg_config" ];
};
};
};
-in mkShell { buildInputs = [ gems gems.wrappedRuby ]; }
+in
+mkShell {
+ buildInputs = [
+ gems
+ gems.wrappedRuby
+ ];
+}
```
And finally via overlays:
```nix
-{ pg_version ? "10" }:
+{
+ pg_version ? "10",
+}:
let
pkgs = import {
overlays = [
@@ -190,14 +219,15 @@ let
defaultGemConfig = super.defaultGemConfig // {
pg = attrs: {
buildFlags = [
- "--with-pg-config=${lib.getDev pkgs."postgresql_${pg_version}"}/bin/pg_config"
+ "--with-pg-config=${pkgs."postgresql_${pg_version}".pg_config}/bin/pg_config"
];
};
};
})
];
};
-in pkgs.ruby.withPackages (ps: with ps; [ pg ])
+in
+pkgs.ruby.withPackages (ps: with ps; [ pg ])
```
Then we can get whichever postgresql version we desire and the `pg` gem will always reference it correctly:
@@ -278,7 +308,14 @@ Of course you could also make a custom `gemConfig` if you know exactly how to pa
Here's another example:
```nix
-{ lib, bundlerApp, makeWrapper, git, gnutar, gzip }:
+{
+ lib,
+ bundlerApp,
+ makeWrapper,
+ git,
+ gnutar,
+ gzip,
+}:
bundlerApp {
pname = "r10k";
@@ -288,7 +325,13 @@ bundlerApp {
nativeBuildInputs = [ makeWrapper ];
postBuild = ''
- wrapProgram $out/bin/r10k --prefix PATH : ${lib.makeBinPath [ git gnutar gzip ]}
+ wrapProgram $out/bin/r10k --prefix PATH : ${
+ lib.makeBinPath [
+ git
+ gnutar
+ gzip
+ ]
+ }
'';
}
```
diff --git a/doc/languages-frameworks/rust.section.md b/doc/languages-frameworks/rust.section.md
index 34ac80cac998..5078de14d80f 100644
--- a/doc/languages-frameworks/rust.section.md
+++ b/doc/languages-frameworks/rust.section.md
@@ -22,20 +22,23 @@ or use [community maintained Rust toolchains](#using-community-maintained-rust-t
Rust applications are packaged by using the `buildRustPackage` helper from `rustPlatform`:
```nix
-{ lib, fetchFromGitHub, rustPlatform }:
+{
+ lib,
+ fetchFromGitHub,
+ rustPlatform,
+}:
-rustPlatform.buildRustPackage rec {
+rustPlatform.buildRustPackage (finalAttrs: {
pname = "ripgrep";
version = "14.1.1";
src = fetchFromGitHub {
owner = "BurntSushi";
- repo = pname;
- rev = version;
+ repo = "ripgrep";
+ tag = finalAttrs.version;
hash = "sha256-gyWnahj1A+iXUQlQ1O1H1u7K5euYQOld9qWm99Vjaeg=";
};
- useFetchCargoVendor = true;
cargoHash = "sha256-9atn5qyBDy4P6iUoHFhg+TV6Ur71fiah4oTJbBMeEy4=";
meta = {
@@ -44,7 +47,7 @@ rustPlatform.buildRustPackage rec {
license = lib.licenses.unlicense;
maintainers = [ ];
};
-}
+})
```
`buildRustPackage` requires a `cargoHash` attribute, computed over all crate sources of this package.
@@ -100,21 +103,20 @@ be made invariant to the version by setting `cargoDepsName` to
`pname`:
```nix
-rustPlatform.buildRustPackage rec {
+rustPlatform.buildRustPackage (finalAttrs: {
pname = "broot";
version = "1.2.0";
src = fetchCrate {
- inherit pname version;
+ inherit (finalAttrs) pname version;
hash = "sha256-aDQA4A5mScX9or3Lyiv/5GyAehidnpKKE0grhbP1Ctc=";
};
- useFetchCargoVendor = true;
cargoHash = "sha256-iDYh52rj1M5Uupvbx2WeDd/jvQZ+2A50V5rp5e2t7q4=";
- cargoDepsName = pname;
+ cargoDepsName = finalAttrs.pname;
# ...
-}
+})
```
### Importing a `Cargo.lock` file {#importing-a-cargo.lock-file}
@@ -151,11 +153,13 @@ rustPlatform.buildRustPackage {
pname = "myproject";
version = "1.0.0";
- cargoLock = let
- fixupLockFile = path: f (builtins.readFile path);
- in {
- lockFileContents = fixupLockFile ./Cargo.lock;
- };
+ cargoLock =
+ let
+ fixupLockFile = path: f (builtins.readFile path);
+ in
+ {
+ lockFileContents = fixupLockFile ./Cargo.lock;
+ };
# ...
}
@@ -178,7 +182,7 @@ The output hash of each dependency that uses a git source must be
specified in the `outputHashes` attribute. For example:
```nix
-rustPlatform.buildRustPackage rec {
+rustPlatform.buildRustPackage {
pname = "myproject";
version = "1.0.0";
@@ -203,7 +207,7 @@ For usage outside nixpkgs, `allowBuiltinFetchGit` could be used to
avoid having to specify `outputHashes`. For example:
```nix
-rustPlatform.buildRustPackage rec {
+rustPlatform.buildRustPackage {
pname = "myproject";
version = "1.0.0";
@@ -229,12 +233,15 @@ If you want to use different features for check phase, you can use
For example:
```nix
-rustPlatform.buildRustPackage rec {
+rustPlatform.buildRustPackage {
pname = "myproject";
version = "1.0.0";
buildNoDefaultFeatures = true;
- buildFeatures = [ "color" "net" ];
+ buildFeatures = [
+ "color"
+ "net"
+ ];
# disable network features in tests
checkFeatures = [ "color" ];
@@ -283,7 +290,10 @@ where they are known to differ. But there are ways to customize the argument:
import {
crossSystem = (import ).systems.examples.armhf-embedded // {
rust.rustcTarget = "thumb-crazy";
- rust.platform = { foo = ""; bar = ""; };
+ rust.platform = {
+ foo = "";
+ bar = "";
+ };
};
}
```
@@ -310,7 +320,7 @@ so:
```nix
rustPlatform.buildRustPackage {
- /* ... */
+ # ...
checkType = "debug";
}
```
@@ -353,7 +363,7 @@ This can be achieved with `--skip` in `checkFlags`:
```nix
rustPlatform.buildRustPackage {
- /* ... */
+ # ...
checkFlags = [
# reason for disabling test
"--skip=example::tests:example_test"
@@ -370,7 +380,7 @@ adapted to be compatible with cargo-nextest.
```nix
rustPlatform.buildRustPackage {
- /* ... */
+ # ...
useNextest = true;
}
```
@@ -382,7 +392,7 @@ sometimes it may be necessary to disable this so the tests run consecutively.
```nix
rustPlatform.buildRustPackage {
- /* ... */
+ # ...
dontUseCargoParallelTests = true;
}
```
@@ -394,7 +404,7 @@ should be built in `debug` mode, it can be configured like so:
```nix
rustPlatform.buildRustPackage {
- /* ... */
+ # ...
buildType = "debug";
}
```
@@ -415,7 +425,7 @@ source code in a reproducible way. If it is missing or out-of-date one can use
the `cargoPatches` attribute to update or add it.
```nix
-rustPlatform.buildRustPackage rec {
+rustPlatform.buildRustPackage {
# ...
cargoPatches = [
# a patch file to add/update Cargo.lock in the source code
@@ -548,12 +558,13 @@ directory of the `tokenizers` project's source archive, we use
`sourceRoot` to point the tooling to this directory:
```nix
-{ fetchFromGitHub
-, buildPythonPackage
-, cargo
-, rustPlatform
-, rustc
-, setuptools-rust
+{
+ fetchFromGitHub,
+ buildPythonPackage,
+ cargo,
+ rustPlatform,
+ rustc,
+ setuptools-rust,
}:
buildPythonPackage rec {
@@ -562,13 +573,18 @@ buildPythonPackage rec {
src = fetchFromGitHub {
owner = "huggingface";
- repo = pname;
- rev = "python-v${version}";
+ repo = "tokenizers";
+ tag = "python-v${version}";
hash = "sha256-rQ2hRV52naEf6PvRsWVCTN7B1oXAQGmnpJw4iIdhamw=";
};
cargoDeps = rustPlatform.fetchCargoVendor {
- inherit pname version src sourceRoot;
+ inherit
+ pname
+ version
+ src
+ sourceRoot
+ ;
hash = "sha256-RO1m8wEd5Ic2M9q+zFHeCJWhCr4Sv3CEWd08mkxsBec=";
};
@@ -593,12 +609,12 @@ following example, the crate is in `src/rust`, as specified in the
path for `fetchCargoVendor`.
```nix
-
-{ buildPythonPackage
-, fetchPypi
-, rustPlatform
-, setuptools-rust
-, openssl
+{
+ buildPythonPackage,
+ fetchPypi,
+ rustPlatform,
+ setuptools-rust,
+ openssl,
}:
buildPythonPackage rec {
@@ -632,10 +648,11 @@ builds the `retworkx` Python package. `fetchCargoVendor` and
`maturinBuildHook` is used to perform the build.
```nix
-{ lib
-, buildPythonPackage
-, rustPlatform
-, fetchFromGitHub
+{
+ lib,
+ buildPythonPackage,
+ rustPlatform,
+ fetchFromGitHub,
}:
buildPythonPackage rec {
@@ -646,7 +663,7 @@ buildPythonPackage rec {
src = fetchFromGitHub {
owner = "Qiskit";
repo = "retworkx";
- rev = version;
+ tag = version;
hash = "sha256-11n30ldg3y3y6qxg3hbj837pnbwjkqw3nxq6frds647mmmprrd20=";
};
@@ -655,7 +672,10 @@ buildPythonPackage rec {
hash = "sha256-QsPCQhNZKYCAogQriQX6pBYQUDAIUsEdRX/63dAqTzg=";
};
- nativeBuildInputs = with rustPlatform; [ cargoSetupHook maturinBuildHook ];
+ nativeBuildInputs = with rustPlatform; [
+ cargoSetupHook
+ maturinBuildHook
+ ];
# ...
}
@@ -666,23 +686,24 @@ buildPythonPackage rec {
Some projects, especially GNOME applications, are built with the Meson Build System instead of calling Cargo directly. Using `rustPlatform.buildRustPackage` may successfully build the main program, but related files will be missing. Instead, you need to set up Cargo dependencies with `fetchCargoVendor` and `cargoSetupHook` and leave the rest to Meson. `rust` and `cargo` are still needed in `nativeBuildInputs` for Meson to use.
```nix
-{ lib
-, stdenv
-, fetchFromGitLab
-, meson
-, ninja
-, pkg-config
-, rustPlatform
-, rustc
-, cargo
-, wrapGAppsHook4
-, blueprint-compiler
-, libadwaita
-, libsecret
-, tinysparql
+{
+ lib,
+ stdenv,
+ fetchFromGitLab,
+ meson,
+ ninja,
+ pkg-config,
+ rustPlatform,
+ rustc,
+ cargo,
+ wrapGAppsHook4,
+ blueprint-compiler,
+ libadwaita,
+ libsecret,
+ tinysparql,
}:
-stdenv.mkDerivation rec {
+stdenv.mkDerivation (finalAttrs: {
pname = "health";
version = "0.95.0";
@@ -690,12 +711,12 @@ stdenv.mkDerivation rec {
domain = "gitlab.gnome.org";
owner = "World";
repo = "health";
- rev = version;
+ tag = finalAttrs.version;
hash = "sha256-PrNPprSS98yN8b8yw2G6hzTSaoE65VbsM3q7FVB4mds=";
};
cargoDeps = rustPlatform.fetchCargoVendor {
- inherit pname version src;
+ inherit (finalAttrs) pname version src;
hash = "sha256-eR1ZGtTZQNhofFUEjI7IX16sMKPJmAl7aIFfPJukecg=";
};
@@ -717,7 +738,7 @@ stdenv.mkDerivation rec {
];
# ...
-}
+})
```
## `buildRustCrate`: Compiling Rust crates using Nix instead of Cargo {#compiling-rust-crates-using-nix-instead-of-cargo}
@@ -744,8 +765,8 @@ Starting from that file, one can add more overrides, to add features
or build inputs by overriding the hello crate in a separate file.
```nix
-with import {};
-((import ./hello.nix).hello {}).override {
+with import { };
+((import ./hello.nix).hello { }).override {
crateOverrides = defaultCrateOverrides // {
hello = attrs: { buildInputs = [ openssl ]; };
};
@@ -764,15 +785,17 @@ the override above can be read, as in the following example, which
patches the derivation:
```nix
-with import {};
-((import ./hello.nix).hello {}).override {
+with import { };
+((import ./hello.nix).hello { }).override {
crateOverrides = defaultCrateOverrides // {
- hello = attrs: lib.optionalAttrs (lib.versionAtLeast attrs.version "1.0") {
- postPatch = ''
- substituteInPlace lib/zoneinfo.rs \
- --replace-fail "/usr/share/zoneinfo" "${tzdata}/share/zoneinfo"
- '';
- };
+ hello =
+ attrs:
+ lib.optionalAttrs (lib.versionAtLeast attrs.version "1.0") {
+ postPatch = ''
+ substituteInPlace lib/zoneinfo.rs \
+ --replace-fail "/usr/share/zoneinfo" "${tzdata}/share/zoneinfo"
+ '';
+ };
};
}
```
@@ -785,10 +808,10 @@ dependencies. For instance, to override the build inputs for crate
crate, we could do:
```nix
-with import {};
-((import hello.nix).hello {}).override {
+with import { };
+((import hello.nix).hello { }).override {
crateOverrides = defaultCrateOverrides // {
- libc = attrs: { buildInputs = []; };
+ libc = attrs: { buildInputs = [ ]; };
};
}
```
@@ -801,27 +824,27 @@ general. A number of other parameters can be overridden:
- The version of `rustc` used to compile the crate:
```nix
- (hello {}).override { rust = pkgs.rust; }
+ (hello { }).override { rust = pkgs.rust; }
```
- Whether to build in release mode or debug mode (release mode by
default):
```nix
- (hello {}).override { release = false; }
+ (hello { }).override { release = false; }
```
- Whether to print the commands sent to `rustc` when building
(equivalent to `--verbose` in cargo:
```nix
- (hello {}).override { verbose = false; }
+ (hello { }).override { verbose = false; }
```
- Extra arguments to be passed to `rustc`:
```nix
- (hello {}).override { extraRustcOpts = "-Z debuginfo=2"; }
+ (hello { }).override { extraRustcOpts = "-Z debuginfo=2"; }
```
- Phases, just like in any other derivation, can be specified using
@@ -833,9 +856,9 @@ general. A number of other parameters can be overridden:
before running the build script:
```nix
- (hello {}).override {
+ (hello { }).override {
preConfigure = ''
- echo "pub const PATH=\"${hi.out}\";" >> src/path.rs"
+ echo "pub const PATH=\"${hi.out}\";" >> src/path.rs"
'';
}
```
@@ -856,12 +879,13 @@ Using the example `hello` project above, we want to do the following:
A typical `shell.nix` might look like:
```nix
-with import {};
+with import { };
stdenv.mkDerivation {
name = "rust-env";
nativeBuildInputs = [
- rustc cargo
+ rustc
+ cargo
# Example Build-time Additional Dependencies
pkg-config
@@ -917,15 +941,13 @@ Here is a simple `shell.nix` that provides Rust nightly (default profile) using
```nix
with import { };
let
- fenix = callPackage
- (fetchFromGitHub {
- owner = "nix-community";
- repo = "fenix";
- # commit from: 2023-03-03
- rev = "e2ea04982b892263c4d939f1cc3bf60a9c4deaa1";
- hash = "sha256-AsOim1A8KKtMWIxG+lXh5Q4P2bhOZjoUhFWJ1EuZNNk=";
- })
- { };
+ fenix = callPackage (fetchFromGitHub {
+ owner = "nix-community";
+ repo = "fenix";
+ # commit from: 2023-03-03
+ rev = "e2ea04982b892263c4d939f1cc3bf60a9c4deaa1";
+ hash = "sha256-AsOim1A8KKtMWIxG+lXh5Q4P2bhOZjoUhFWJ1EuZNNk=";
+ }) { };
in
mkShell {
name = "rust-env";
@@ -964,8 +986,7 @@ You can also use Rust nightly to build rust packages using `makeRustPlatform`.
The below snippet demonstrates invoking `buildRustPackage` with a Rust toolchain from oxalica's overlay:
```nix
-with import
-{
+with import {
overlays = [
(import (fetchTarball "https://github.com/oxalica/rust-overlay/archive/master.tar.gz"))
];
@@ -977,29 +998,32 @@ let
};
in
-rustPlatform.buildRustPackage rec {
+rustPlatform.buildRustPackage (finalAttrs: {
pname = "ripgrep";
version = "14.1.1";
src = fetchFromGitHub {
owner = "BurntSushi";
repo = "ripgrep";
- rev = version;
+ tag = finalAttrs.version;
hash = "sha256-gyWnahj1A+iXUQlQ1O1H1u7K5euYQOld9qWm99Vjaeg=";
};
- useFetchCargoVendor = true;
cargoHash = "sha256-9atn5qyBDy4P6iUoHFhg+TV6Ur71fiah4oTJbBMeEy4=";
+ # Tests require network access. Skipping.
doCheck = false;
meta = {
description = "Fast line-oriented regex search tool, similar to ag and ack";
homepage = "https://github.com/BurntSushi/ripgrep";
- license = with lib.licenses; [ mit unlicense ];
- maintainers = with lib.maintainers; [];
+ license = with lib.licenses; [
+ mit
+ unlicense
+ ];
+ maintainers = with lib.maintainers; [ ];
};
-}
+})
```
Follow the below steps to try that snippet.
@@ -1029,19 +1053,28 @@ with the path into which you have `git clone`d the `rustc` git
repository:
```nix
- (final: prev: /*lib.optionalAttrs prev.stdenv.targetPlatform.isAarch64*/ {
- rust_1_72 =
- lib.updateManyAttrsByPath [{
- path = [ "packages" "stable" ];
- update = old: old.overrideScope(final: prev: {
- rustc-unwrapped = prev.rustc-unwrapped.overrideAttrs (_: {
- src = lib.cleanSource /git/scratch/rust;
- # do *not* put passthru.isReleaseTarball=true here
- });
- });
- }]
- prev.rust_1_72;
- })
+(
+ final: prev: # lib.optionalAttrs prev.stdenv.targetPlatform.isAarch64
+ {
+ rust_1_72 = lib.updateManyAttrsByPath [
+ {
+ path = [
+ "packages"
+ "stable"
+ ];
+ update =
+ old:
+ old.overrideScope (
+ final: prev: {
+ rustc-unwrapped = prev.rustc-unwrapped.overrideAttrs (_: {
+ src = lib.cleanSource /git/scratch/rust;
+ # do *not* put passthru.isReleaseTarball=true here
+ });
+ }
+ );
+ }
+ ] prev.rust_1_72;
+ })
```
If the problem you're troubleshooting only manifests when
diff --git a/doc/languages-frameworks/swift.section.md b/doc/languages-frameworks/swift.section.md
index 88d98deeb2dd..b9d4b5a7cba8 100644
--- a/doc/languages-frameworks/swift.section.md
+++ b/doc/languages-frameworks/swift.section.md
@@ -69,42 +69,55 @@ This produces some files in a directory `nix`, which will be part of your Nix
expression. The next step is to write that expression:
```nix
-{ stdenv, swift, swiftpm, swiftpm2nix, fetchFromGitHub }:
+{
+ stdenv,
+ swift,
+ swiftpm,
+ swiftpm2nix,
+ fetchFromGitHub,
+}:
let
# Pass the generated files to the helper.
generated = swiftpm2nix.helpers ./nix;
in
-stdenv.mkDerivation rec {
+stdenv.mkDerivation (finalAttrs: {
pname = "myproject";
version = "0.0.0";
src = fetchFromGitHub {
owner = "nixos";
- repo = pname;
- rev = version;
- hash = "sha256-AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=";
+ repo = "myproject";
+ tag = finalAttrs.version;
+ hash = "";
};
# Including SwiftPM as a nativeBuildInput provides a buildPhase for you.
# This by default performs a release build using SwiftPM, essentially:
# swift build -c release
- nativeBuildInputs = [ swift swiftpm ];
+ nativeBuildInputs = [
+ swift
+ swiftpm
+ ];
# The helper provides a configure snippet that will prepare all dependencies
# in the correct place, where SwiftPM expects them.
configurePhase = generated.configure;
installPhase = ''
+ runHook preInstall
+
# This is a special function that invokes swiftpm to find the location
# of the binaries it produced.
binPath="$(swiftpmBinPath)"
# Now perform any installation steps.
mkdir -p $out/bin
cp $binPath/myproject $out/bin/
+
+ runHook postInstall
'';
-}
+})
```
### Custom build flags {#ssec-swiftpm-custom-build-flags}
diff --git a/doc/languages-frameworks/texlive.section.md b/doc/languages-frameworks/texlive.section.md
index a31a4357a22f..b3a13dc8b8d2 100644
--- a/doc/languages-frameworks/texlive.section.md
+++ b/doc/languages-frameworks/texlive.section.md
@@ -10,7 +10,13 @@ Release 23.11 ships with a new interface that will eventually replace `texlive.c
- Packages cannot be used directly but must be assembled in an environment. To create or add packages to an environment, use
```nix
- texliveSmall.withPackages (ps: with ps; [ collection-langkorean algorithms cm-super ])
+ texliveSmall.withPackages (
+ ps: with ps; [
+ collection-langkorean
+ algorithms
+ cm-super
+ ]
+ )
```
The function `withPackages` can be called multiple times to add more packages.
@@ -18,12 +24,14 @@ Release 23.11 ships with a new interface that will eventually replace `texlive.c
- `texlive.withPackages` uses the same logic as `buildEnv`. Only parts of a package are installed in an environment: its 'runtime' files (`tex` output), binaries (`out` output), and support files (`tlpkg` output). Moreover, man and info pages are assembled into separate `man` and `info` outputs. To add only the TeX files of a package, or its documentation (`texdoc` output), just specify the outputs:
```nix
- texlive.withPackages (ps: with ps; [
- texdoc # recommended package to navigate the documentation
- perlPackages.LaTeXML.tex # tex files of LaTeXML, omit binaries
- cm-super
- cm-super.texdoc # documentation of cm-super
- ])
+ texlive.withPackages (
+ ps: with ps; [
+ texdoc # recommended package to navigate the documentation
+ perlPackages.LaTeXML.tex # tex files of LaTeXML, omit binaries
+ cm-super
+ cm-super.texdoc # documentation of cm-super
+ ]
+ )
```
- All packages distributed by TeX Live, which contains most of CTAN, are available and can be found under `texlive.pkgs`:
@@ -50,7 +58,12 @@ Release 23.11 ships with a new interface that will eventually replace `texlive.c
```nix
texlive.combine {
- inherit (texlive) scheme-small collection-langkorean algorithms cm-super;
+ inherit (texlive)
+ scheme-small
+ collection-langkorean
+ algorithms
+ cm-super
+ ;
}
```
@@ -61,8 +74,8 @@ Release 23.11 ships with a new interface that will eventually replace `texlive.c
```nix
texlive.combine {
# inherit (texlive) whatever-you-want;
- pkgFilter = pkg:
- pkg.tlType == "run" || pkg.tlType == "bin" || pkg.hasManpages || pkg.pname == "cm-super";
+ pkgFilter =
+ pkg: pkg.tlType == "run" || pkg.tlType == "bin" || pkg.hasManpages || pkg.pname == "cm-super";
# elem tlType [ "run" "bin" "doc" "source" ]
# there are also other attributes: version, name
}
@@ -81,18 +94,18 @@ Release 23.11 ships with a new interface that will eventually replace `texlive.c
- TeX Live packages are also available under `texlive.pkgs` as derivations with outputs `out`, `tex`, `texdoc`, `texsource`, `tlpkg`, `man`, `info`. They cannot be installed outside of `texlive.combine` but are available for other uses. To repackage a font, for instance, use
```nix
- stdenvNoCC.mkDerivation rec {
+ stdenvNoCC.mkDerivation (finalAttrs: {
src = texlive.pkgs.iwona;
dontUnpack = true;
- inherit (src) pname version;
+ inherit (finalAttrs.src) pname version;
installPhase = ''
runHook preInstall
install -Dm644 $src/fonts/opentype/nowacki/iwona/*.otf -t $out/share/fonts/opentype
runHook postInstall
'';
- }
+ })
```
See `biber`, `iwona` for complete examples.
@@ -114,14 +127,17 @@ When using `pkgFilter`, `texlive.combine` will assign `tlType` respectively `"bi
Here is a (very verbose) example. See also the packages `auctex`, `eukleides`, `mftrace` for more examples.
```nix
-with import {};
+with import { };
let
foiltex = stdenvNoCC.mkDerivation {
pname = "latex-foiltex";
version = "2.1.4b";
- outputs = [ "tex" "texdoc" ];
+ outputs = [
+ "tex"
+ "texdoc"
+ ];
passthru.tlDeps = with texlive; [ latex ];
srcs = [
@@ -146,11 +162,18 @@ let
'';
nativeBuildInputs = [
- (texliveSmall.withPackages (ps: with ps; [ cm-super hypdoc latexmk ]))
+ (texliveSmall.withPackages (
+ ps: with ps; [
+ cm-super
+ hypdoc
+ latexmk
+ ]
+ ))
# multiple-outputs.sh fails if $out is not defined
(writeShellScript "force-tex-output.sh" ''
out="''${tex-}"
'')
+ writableTmpDirAsHomeHook # Need a writable $HOME for latexmk
];
dontConfigure = true;
@@ -162,7 +185,6 @@ let
latex foiltex.ins
# Generate the documentation
- export HOME=.
latexmk -pdf foiltex.dtx
runHook postBuild
@@ -192,22 +214,24 @@ let
latex_with_foiltex = texliveSmall.withPackages (_: [ foiltex ]);
in
- runCommand "test.pdf" {
+runCommand "test.pdf"
+ {
nativeBuildInputs = [ latex_with_foiltex ];
- } ''
-cat >test.tex <test.tex < main.tex
- env HOME=$(mktemp -d) lualatex -interaction=nonstopmode -output-format=pdf -output-directory=$out ./main.tex
-''
+runCommandNoCC "lualatex-hello-world"
+ {
+ buildInputs = [ texliveFull ];
+ }
+ ''
+ mkdir $out
+ echo '\documentclass{article} \begin{document} Hello world \end{document}' > main.tex
+ env HOME=$(mktemp -d) lualatex -interaction=nonstopmode -output-format=pdf -output-directory=$out ./main.tex
+ ''
```
Additionally, [the cache of a user can diverge from the nix store](https://github.com/NixOS/nixpkgs/issues/278718).
diff --git a/doc/languages-frameworks/typst.section.md b/doc/languages-frameworks/typst.section.md
new file mode 100644
index 000000000000..3a4910ad8489
--- /dev/null
+++ b/doc/languages-frameworks/typst.section.md
@@ -0,0 +1,73 @@
+# Typst {#typst}
+
+Typst can be configured to include packages from [Typst Universe](https://typst.app/universe/) or custom packages.
+
+## Custom Environment {#typst-custom-environment}
+
+You can create a custom Typst environment with a selected set of packages from **Typst Universe** using the following code. It is also possible to specify a Typst package with a specific version (e.g., `cetz_0_3_0`). A package without a version number will always refer to its latest version.
+
+```nix
+typst.withPackages (
+ p: with p; [
+ polylux_0_4_0
+ cetz_0_3_0
+ ]
+)
+```
+
+### Handling Outdated Package Hashes {#typst-handling-outdated-package-hashes}
+
+Since **Typst Universe** does not provide a way to fetch a package with a specific hash, the package hashes in `nixpkgs` can sometimes be outdated. To resolve this issue, you can manually override the package source using the following approach:
+
+```nix
+typst.withPackages.override
+ (old: {
+ typstPackages = old.typstPackages.extend (
+ _: previous: {
+ polylux_0_4_0 = previous.polylux_0_4_0.overrideAttrs (oldPolylux: {
+ src = oldPolylux.src.overrideAttrs {
+ outputHash = YourUpToDatePolyluxHash;
+ };
+ });
+ }
+ );
+ })
+ (
+ p: with p; [
+ polylux_0_4_0
+ cetz_0_3_0
+ ]
+ )
+```
+
+## Custom Packages {#typst-custom-packages}
+
+`Nixpkgs` provides a helper function, `buildTypstPackage`, to build custom Typst packages that can be used within the Typst environment. However, all dependencies of the custom package must be explicitly specified in `typstDeps`.
+
+Here's how to define a custom Typst package:
+
+```nix
+{
+ buildTypstPackage,
+ typstPackages,
+}:
+
+buildTypstPackage (finalAttrs: {
+ pname = "my-typst-package";
+ version = "0.0.1";
+ src = ./.;
+ typstDeps = with typstPackages; [ cetz_0_3_0 ];
+})
+```
+
+### Package Scope and Usage {#typst-package-scope-and-usage}
+
+By default, every custom package is scoped under `@preview`, as shown below:
+
+```typst
+#import "@preview/my-typst-package:0.0.1": *
+```
+
+Since `@preview` is intended for packages from **Typst Universe**, it is recommended to use this approach **only for temporary or experimental modifications over existing packages** from **Typst Universe**.
+
+On the other hand, **local packages**, packages scoped under `@local`, are **not** considered part of the Typst environment. This means that local packages must be manually linked to the Typst compiler if needed.
diff --git a/doc/languages-frameworks/vim.section.md b/doc/languages-frameworks/vim.section.md
index da139623a25c..9c8c3faaecf0 100644
--- a/doc/languages-frameworks/vim.section.md
+++ b/doc/languages-frameworks/vim.section.md
@@ -47,11 +47,17 @@ To store your plugins in Vim packages (the native Vim plugin manager, see `:help
vim-full.customize {
vimrcConfig.packages.myVimPackage = with pkgs.vimPlugins; {
# loaded on launch
- start = [ youcompleteme fugitive ];
+ start = [
+ youcompleteme
+ fugitive
+ ];
# manually loadable by calling `:packadd $plugin-name`
# however, if a Vim plugin has a dependency that is not explicitly listed in
# opt that dependency will always be added to start to avoid confusion.
- opt = [ phpCompletion elm-vim ];
+ opt = [
+ phpCompletion
+ elm-vim
+ ];
# To automatically load a plugin when opening a filetype, add vimrc lines like:
# autocmd FileType php :packadd phpCompletion
};
@@ -63,18 +69,19 @@ The resulting package can be added to `packageOverrides` in `~/.nixpkgs/config.n
```nix
{
- packageOverrides = pkgs: with pkgs; {
- myVim = vim-full.customize {
- # `name` specifies the name of the executable and package
- name = "vim-with-plugins";
- # add here code from the example section
- };
- myNeovim = neovim.override {
- configure = {
- # add code from the example section here
+ packageOverrides =
+ pkgs: with pkgs; {
+ myVim = vim-full.customize {
+ # `name` specifies the name of the executable and package
+ name = "vim-with-plugins";
+ # add here code from the example section
+ };
+ myNeovim = neovim.override {
+ configure = {
+ # add code from the example section here
+ };
};
};
- };
}
```
@@ -100,20 +107,18 @@ let
in
{
environment.systemPackages = [
- (
- pkgs.neovim.override {
- configure = {
- packages.myPlugins = with pkgs.vimPlugins; {
+ (pkgs.neovim.override {
+ configure = {
+ packages.myPlugins = with pkgs.vimPlugins; {
start = [
vim-go # already packaged plugin
easygrep # custom package
];
- opt = [];
+ opt = [ ];
};
# ...
};
- }
- )
+ })
];
}
```
@@ -129,7 +134,12 @@ plugins the following example can be used:
vim-full.customize {
vimrcConfig.packages.myVimPackage = with pkgs.vimPlugins; {
# loaded on launch
- plug.plugins = [ youcompleteme fugitive phpCompletion elm-vim ];
+ plug.plugins = [
+ youcompleteme
+ fugitive
+ phpCompletion
+ elm-vim
+ ];
};
}
```
@@ -147,8 +157,11 @@ Some plugins require overrides in order to function properly. Overrides are plac
```nix
{
- deoplete-fish = super.deoplete-fish.overrideAttrs(old: {
- dependencies = with super; [ deoplete-nvim vim-fish ];
+ deoplete-fish = super.deoplete-fish.overrideAttrs (old: {
+ dependencies = with super; [
+ deoplete-nvim
+ vim-fish
+ ];
});
}
```
@@ -164,7 +177,7 @@ Finally, there are some plugins that are also packaged in nodePackages because t
Run the update script with a GitHub API token that has at least `public_repo` access. Running the script without the token is likely to result in rate-limiting (429 errors). For steps on creating an API token, please refer to [GitHub's token documentation](https://docs.github.com/en/free-pro-team@latest/github/authenticating-to-github/creating-a-personal-access-token).
```sh
-nix-shell -p vimPluginsUpdater --run 'vim-plugins-updater --github-token=mytoken' # or set GITHUB_API_TOKEN environment variable
+nix-shell -p vimPluginsUpdater --run 'vim-plugins-updater --github-token=mytoken' # or set GITHUB_TOKEN environment variable
```
Alternatively, set the number of processes to a lower count to avoid rate-limiting.
@@ -199,9 +212,7 @@ You can then reference the generated vim plugins via:
```nix
{
- myVimPlugins = pkgs.vimPlugins.extend (
- (pkgs.callPackage ./generated.nix {})
- );
+ myVimPlugins = pkgs.vimPlugins.extend ((pkgs.callPackage ./generated.nix { }));
}
```
diff --git a/doc/manpage-urls.json b/doc/manpage-urls.json
index 63f877dcb660..8b3b58c15125 100644
--- a/doc/manpage-urls.json
+++ b/doc/manpage-urls.json
@@ -228,6 +228,8 @@
"systemd-socket-activate(1)": "https://www.freedesktop.org/software/systemd/man/systemd-socket-activate.html",
"systemd-socket-proxyd(8)": "https://www.freedesktop.org/software/systemd/man/systemd-socket-proxyd.html",
"systemd-soft-reboot.service(8)": "https://www.freedesktop.org/software/systemd/man/systemd-soft-reboot.service.html",
+ "systemd-ssh-generator(8)": "https://www.freedesktop.org/software/systemd/man/systemd-ssh-generator.html",
+ "systemd-ssh-proxy(1)": "https://www.freedesktop.org/software/systemd/man/systemd-ssh-proxy.html",
"systemd-stdio-bridge(1)": "https://www.freedesktop.org/software/systemd/man/systemd-stdio-bridge.html",
"systemd-stub(7)": "https://www.freedesktop.org/software/systemd/man/systemd-stub.html",
"systemd-suspend-then-hibernate.service(8)": "https://www.freedesktop.org/software/systemd/man/systemd-suspend-then-hibernate.service.html",
diff --git a/doc/manual.md.in b/doc/manual.md.in
index 07e587190d84..160c6eaead3c 100644
--- a/doc/manual.md.in
+++ b/doc/manual.md.in
@@ -9,6 +9,7 @@ preface.chapter.md
using-nixpkgs.md
lib.md
stdenv.md
+toolchains.md
build-helpers.md
development.md
contributing.md
diff --git a/doc/packages/build-support.md b/doc/packages/build-support.md
index 80392f4d121e..1e4756d7f258 100644
--- a/doc/packages/build-support.md
+++ b/doc/packages/build-support.md
@@ -30,13 +30,12 @@ substitute {
```
:::
-## `pkgs.substituteAll` {#pkgs-substituteall}
+## `pkgs.replaceVars` {#pkgs-replacevars}
-`pkgs.substituteAll` substitutes all instances of `@varName@` (`@`s included) in file `src` with the value of the corresponding environment variable.
-As this uses the [`substituteAll`] (#fun-substitute) function, its limitations regarding variable names that will or will not be replaced also apply here.
+`pkgs.replaceVars ` replaces all instances of `@varName@` (`@`s included) in file `src` with the respective value in the attribute set `replacements`.
-:::{.example #ex-pkgs-substituteAll}
-# Usage of `pkgs.substituteAll`
+:::{.example #ex-pkgs-replace-vars}
+# Usage of `pkgs.replaceVars`
If `say-goodbye.sh` contains the following:
@@ -51,16 +50,14 @@ the following derivation will make substitutions to `@bash@`, `@hello@`, and `@g
```nix
{
- substituteAll,
+ replaceVars,
bash,
hello,
}:
-substituteAll {
- src = ./say-goodbye.sh;
- env = {
- inherit bash hello;
- greeting = "goodbye";
- };
+replaceVars ./say-goodbye.sh {
+ inherit bash hello;
+ greeting = "goodbye";
+ unchanged = null;
}
```
@@ -72,31 +69,37 @@ such that `$out` will result in something like the following:
echo @unchanged@
/nix/store/566f5isbvw014h7knmzmxa5l6hshx43k-hello-2.12.1/bin/hello --greeting goodbye
```
+
+Note that, in contrast to the old `substituteAll`, `unchanged = null` must explicitly be set.
+Any unreferenced `@...@` pattern in the source file will throw an error.
:::
-## `pkgs.substituteAllFiles` {#pkgs-substituteallfiles}
+## `pkgs.replaceVarsWith` {#pkgs-replacevarswith}
-`pkgs.substituteAllFiles` replaces `@varName@` with the value of the environment variable `varName`.
-It expects `src` to be a directory and requires a `files` argument that specifies which files will be subject to replacements; only these files will be placed in `$out`.
+`pkgs.replaceVarsWith` works the same way as [pkgs.replaceVars](#pkgs-replacevars), but additionally allows more options.
-As it also uses the `substituteAll` function, it is subject to the same limitations on environment variables as discussed in [pkgs.substituteAll](#pkgs-substituteall).
+:::{.example #ex-pkgs-replace-vars-with}
+# Usage of `pkgs.replaceVarsWith`
-:::{.example #ex-pkgs-substitute-all-files}
-# Usage of `pkgs.substituteAllFiles`
-
-If the current directory contains `{foo,bar,baz}.txt` and the following `default.nix`
+With the example file `say-goodbye.sh`, consider:
```nix
-{ substituteAllFiles }:
-substituteAllFiles {
- src = ./.;
- files = [
- "foo.txt"
- "bar.txt"
- ];
- hello = "there";
+{ replaceVarsWith }:
+replaceVarsWith {
+ src = ./say-goodbye.sh;
+
+ replacements = {
+ inherit bash hello;
+ greeting = "goodbye";
+ unchanged = null;
+ };
+
+ name = "say-goodbye";
+ dir = "bin";
+ isExecutable = true;
+ meta.mainProgram = "say-goodbye";
}
```
-in the resulting derivation, every instance of `@hello@` will be replaced with `there` in `$out/foo.txt` and `$out/bar.txt`; `baz.txt` will not be processed nor will it appear in `$out`.
+This will make the resulting file executable, put it in `bin/say-goodbye` and set `meta` attributes respectively.
:::
diff --git a/doc/packages/cataclysm-dda.section.md b/doc/packages/cataclysm-dda.section.md
index f401e9b9efa5..2e7d86f951b8 100644
--- a/doc/packages/cataclysm-dda.section.md
+++ b/doc/packages/cataclysm-dda.section.md
@@ -48,7 +48,7 @@ let
# Unfortunately, this refers to the package before overriding and
# parallel building is still disabled.
- badExample = myCDDA.withMods (_: []);
+ badExample = myCDDA.withMods (_: [ ]);
inherit (cataclysmDDA) attachPkgs pkgs wrapCDDA;
@@ -66,7 +66,7 @@ in
# badExample # parallel building disabled
# goodExample1.withMods (_: []) # parallel building enabled
-goodExample2.withMods (_: []) # parallel building enabled
+goodExample2.withMods (_: [ ]) # parallel building enabled
```
## Customizing with mods {#customizing-with-mods}
@@ -75,9 +75,11 @@ To install Cataclysm DDA with mods of your choice, you can use `withMods`
attribute:
```nix
-cataclysm-dda.withMods (mods: with mods; [
- tileset.UndeadPeople
-])
+cataclysm-dda.withMods (
+ mods: with mods; [
+ tileset.UndeadPeople
+ ]
+)
```
All mods, soundpacks, and tilesets available in nixpkgs are found in
@@ -88,42 +90,46 @@ in nixpkgs:
```nix
let
- customMods = self: super: lib.recursiveUpdate super {
- # Modify existing mod
- tileset.UndeadPeople = super.tileset.UndeadPeople.overrideAttrs (old: {
- # If you like to apply a patch to the tileset for example
- patches = [ ./path/to/your.patch ];
- });
+ customMods =
+ self: super:
+ lib.recursiveUpdate super {
+ # Modify existing mod
+ tileset.UndeadPeople = super.tileset.UndeadPeople.overrideAttrs (old: {
+ # If you like to apply a patch to the tileset for example
+ patches = [ ./path/to/your.patch ];
+ });
- # Add another mod
- mod.Awesome = cataclysmDDA.buildMod {
- modName = "Awesome";
- version = "0.x";
- src = fetchFromGitHub {
- owner = "Someone";
- repo = "AwesomeMod";
- rev = "...";
- hash = "...";
+ # Add another mod
+ mod.Awesome = cataclysmDDA.buildMod {
+ modName = "Awesome";
+ version = "0.x";
+ src = fetchFromGitHub {
+ owner = "Someone";
+ repo = "AwesomeMod";
+ rev = "...";
+ hash = "...";
+ };
+ # Path to be installed in the unpacked source (default: ".")
+ modRoot = "contents/under/this/path/will/be/installed";
};
- # Path to be installed in the unpacked source (default: ".")
- modRoot = "contents/under/this/path/will/be/installed";
- };
- # Add another soundpack
- soundpack.Fantastic = cataclysmDDA.buildSoundPack {
- # ditto
- };
+ # Add another soundpack
+ soundpack.Fantastic = cataclysmDDA.buildSoundPack {
+ # ditto
+ };
- # Add another tileset
- tileset.SuperDuper = cataclysmDDA.buildTileSet {
- # ditto
+ # Add another tileset
+ tileset.SuperDuper = cataclysmDDA.buildTileSet {
+ # ditto
+ };
};
- };
in
-cataclysm-dda.withMods (mods: with mods.extend customMods; [
- tileset.UndeadPeople
- mod.Awesome
- soundpack.Fantastic
- tileset.SuperDuper
-])
+cataclysm-dda.withMods (
+ mods: with mods.extend customMods; [
+ tileset.UndeadPeople
+ mod.Awesome
+ soundpack.Fantastic
+ tileset.SuperDuper
+ ]
+)
```
diff --git a/doc/packages/citrix.section.md b/doc/packages/citrix.section.md
index bcf0924249bc..9b680759f461 100644
--- a/doc/packages/citrix.section.md
+++ b/doc/packages/citrix.section.md
@@ -28,5 +28,6 @@ let
./custom-cert-1.pem
./custom-cert-2.pem # ...
];
-in citrix_workspace.override { inherit extraCerts; }
+in
+citrix_workspace.override { inherit extraCerts; }
```
diff --git a/doc/packages/darwin-builder.section.md b/doc/packages/darwin-builder.section.md
index 06358c790165..fb71a78d6eb3 100644
--- a/doc/packages/darwin-builder.section.md
+++ b/doc/packages/darwin-builder.section.md
@@ -89,58 +89,72 @@ $ sudo launchctl kickstart -k system/org.nixos.nix-daemon
darwin.inputs.nixpkgs.follows = "nixpkgs";
};
- outputs = { self, darwin, nixpkgs, ... }@inputs:
- let
+ outputs =
+ {
+ self,
+ darwin,
+ nixpkgs,
+ ...
+ }@inputs:
+ let
- inherit (darwin.lib) darwinSystem;
- system = "aarch64-darwin";
- pkgs = nixpkgs.legacyPackages."${system}";
- linuxSystem = builtins.replaceStrings [ "darwin" ] [ "linux" ] system;
+ inherit (darwin.lib) darwinSystem;
+ system = "aarch64-darwin";
+ pkgs = nixpkgs.legacyPackages."${system}";
+ linuxSystem = builtins.replaceStrings [ "darwin" ] [ "linux" ] system;
- darwin-builder = nixpkgs.lib.nixosSystem {
- system = linuxSystem;
- modules = [
- "${nixpkgs}/nixos/modules/profiles/nix-builder-vm.nix"
- { virtualisation = {
- host.pkgs = pkgs;
- darwin-builder.workingDirectory = "/var/lib/darwin-builder";
- darwin-builder.hostPort = 22;
- };
- }
- ];
- };
- in {
-
- darwinConfigurations = {
- machine1 = darwinSystem {
- inherit system;
+ darwin-builder = nixpkgs.lib.nixosSystem {
+ system = linuxSystem;
modules = [
+ "${nixpkgs}/nixos/modules/profiles/nix-builder-vm.nix"
{
- nix.distributedBuilds = true;
- nix.buildMachines = [{
- hostName = "localhost";
- sshUser = "builder";
- sshKey = "/etc/nix/builder_ed25519";
- system = linuxSystem;
- maxJobs = 4;
- supportedFeatures = [ "kvm" "benchmark" "big-parallel" ];
- }];
-
- launchd.daemons.darwin-builder = {
- command = "${darwin-builder.config.system.build.macos-builder-installer}/bin/create-builder";
- serviceConfig = {
- KeepAlive = true;
- RunAtLoad = true;
- StandardOutPath = "/var/log/darwin-builder.log";
- StandardErrorPath = "/var/log/darwin-builder.log";
- };
+ virtualisation = {
+ host.pkgs = pkgs;
+ darwin-builder.workingDirectory = "/var/lib/darwin-builder";
+ darwin-builder.hostPort = 22;
};
}
];
};
- };
+ in
+ {
- };
+ darwinConfigurations = {
+ machine1 = darwinSystem {
+ inherit system;
+ modules = [
+ {
+ nix.distributedBuilds = true;
+ nix.buildMachines = [
+ {
+ hostName = "localhost";
+ sshUser = "builder";
+ sshKey = "/etc/nix/builder_ed25519";
+ system = linuxSystem;
+ maxJobs = 4;
+ supportedFeatures = [
+ "kvm"
+ "benchmark"
+ "big-parallel"
+ ];
+ }
+ ];
+
+ launchd.daemons.darwin-builder = {
+ command = "${darwin-builder.config.system.build.macos-builder-installer}/bin/create-builder";
+ serviceConfig = {
+ KeepAlive = true;
+ RunAtLoad = true;
+ StandardOutPath = "/var/log/darwin-builder.log";
+ StandardErrorPath = "/var/log/darwin-builder.log";
+ };
+ };
+ }
+ ];
+ };
+ };
+
+ };
}
```
@@ -154,21 +168,21 @@ To do this, you just need to set the `virtualisation.darwin-builder.*` parameter
in the example below and rebuild.
```nix
- {
- darwin-builder = nixpkgs.lib.nixosSystem {
- system = linuxSystem;
- modules = [
- "${nixpkgs}/nixos/modules/profiles/nix-builder-vm.nix"
- {
- virtualisation.host.pkgs = pkgs;
- virtualisation.darwin-builder.diskSize = 5120;
- virtualisation.darwin-builder.memorySize = 1024;
- virtualisation.darwin-builder.hostPort = 33022;
- virtualisation.darwin-builder.workingDirectory = "/var/lib/darwin-builder";
- }
- ];
- };
- }
+{
+ darwin-builder = nixpkgs.lib.nixosSystem {
+ system = linuxSystem;
+ modules = [
+ "${nixpkgs}/nixos/modules/profiles/nix-builder-vm.nix"
+ {
+ virtualisation.host.pkgs = pkgs;
+ virtualisation.darwin-builder.diskSize = 5120;
+ virtualisation.darwin-builder.memorySize = 1024;
+ virtualisation.darwin-builder.hostPort = 33022;
+ virtualisation.darwin-builder.workingDirectory = "/var/lib/darwin-builder";
+ }
+ ];
+ };
+}
```
You may make any other changes to your VM in this attribute set. For example,
diff --git a/doc/packages/eclipse.section.md b/doc/packages/eclipse.section.md
index acf34b57571a..be554de6520b 100644
--- a/doc/packages/eclipse.section.md
+++ b/doc/packages/eclipse.section.md
@@ -15,11 +15,13 @@ If you prefer to install plugins in a more declarative manner, then Nixpkgs also
```nix
{
packageOverrides = pkgs: {
- myEclipse = with pkgs.eclipses; eclipseWithPlugins {
- eclipse = eclipse-platform;
- jvmArgs = [ "-Xmx2048m" ];
- plugins = [ plugins.color-theme ];
- };
+ myEclipse =
+ with pkgs.eclipses;
+ eclipseWithPlugins {
+ eclipse = eclipse-platform;
+ jvmArgs = [ "-Xmx2048m" ];
+ plugins = [ plugins.color-theme ];
+ };
};
}
```
@@ -37,32 +39,34 @@ Expanding the previous example with two plugins using the above functions, we ha
```nix
{
packageOverrides = pkgs: {
- myEclipse = with pkgs.eclipses; eclipseWithPlugins {
- eclipse = eclipse-platform;
- jvmArgs = [ "-Xmx2048m" ];
- plugins = [
- plugins.color-theme
- (plugins.buildEclipsePlugin {
- name = "myplugin1-1.0";
- srcFeature = fetchurl {
- url = "http://…/features/myplugin1.jar";
- hash = "sha256-123…";
- };
- srcPlugin = fetchurl {
- url = "http://…/plugins/myplugin1.jar";
- hash = "sha256-123…";
- };
- })
- (plugins.buildEclipseUpdateSite {
- name = "myplugin2-1.0";
- src = fetchurl {
- stripRoot = false;
- url = "http://…/myplugin2.zip";
- hash = "sha256-123…";
- };
- })
- ];
- };
+ myEclipse =
+ with pkgs.eclipses;
+ eclipseWithPlugins {
+ eclipse = eclipse-platform;
+ jvmArgs = [ "-Xmx2048m" ];
+ plugins = [
+ plugins.color-theme
+ (plugins.buildEclipsePlugin {
+ name = "myplugin1-1.0";
+ srcFeature = fetchurl {
+ url = "http://…/features/myplugin1.jar";
+ hash = "sha256-123…";
+ };
+ srcPlugin = fetchurl {
+ url = "http://…/plugins/myplugin1.jar";
+ hash = "sha256-123…";
+ };
+ })
+ (plugins.buildEclipseUpdateSite {
+ name = "myplugin2-1.0";
+ src = fetchurl {
+ stripRoot = false;
+ url = "http://…/myplugin2.zip";
+ hash = "sha256-123…";
+ };
+ })
+ ];
+ };
};
}
```
diff --git a/doc/packages/emacs.section.md b/doc/packages/emacs.section.md
index 2ced251f3e46..322ece4a348f 100644
--- a/doc/packages/emacs.section.md
+++ b/doc/packages/emacs.section.md
@@ -6,17 +6,21 @@ The Emacs package comes with some extra helpers to make it easier to configure.
```nix
{
- packageOverrides = pkgs: with pkgs; {
- myEmacs = emacs.pkgs.withPackages (epkgs: (with epkgs.melpaStablePackages; [
- company
- counsel
- flycheck
- ivy
- magit
- projectile
- use-package
- ]));
- };
+ packageOverrides =
+ pkgs: with pkgs; {
+ myEmacs = emacs.pkgs.withPackages (
+ epkgs:
+ (with epkgs.melpaStablePackages; [
+ company
+ counsel
+ flycheck
+ ivy
+ magit
+ projectile
+ use-package
+ ])
+ );
+ };
}
```
@@ -24,8 +28,8 @@ You can install it like any other packages via `nix-env -iA myEmacs`. However, t
```nix
{
- packageOverrides = pkgs: with pkgs; rec {
- myEmacsConfig = writeText "default.el" ''
+ packageOverrides = pkgs: {
+ myEmacsConfig = pkgs.writeText "default.el" ''
(eval-when-compile
(require 'use-package))
@@ -80,19 +84,22 @@ You can install it like any other packages via `nix-env -iA myEmacs`. However, t
(projectile-global-mode))
'';
- myEmacs = emacs.pkgs.withPackages (epkgs: (with epkgs.melpaStablePackages; [
- (runCommand "default.el" {} ''
- mkdir -p $out/share/emacs/site-lisp
- cp ${myEmacsConfig} $out/share/emacs/site-lisp/default.el
- '')
- company
- counsel
- flycheck
- ivy
- magit
- projectile
- use-package
- ]));
+ myEmacs = emacs.pkgs.withPackages (
+ epkgs:
+ (with epkgs.melpaStablePackages; [
+ (runCommand "default.el" { } ''
+ mkdir -p $out/share/emacs/site-lisp
+ cp ${myEmacsConfig} $out/share/emacs/site-lisp/default.el
+ '')
+ company
+ counsel
+ flycheck
+ ivy
+ magit
+ projectile
+ use-package
+ ])
+ );
};
}
```
@@ -108,11 +115,12 @@ let
# ...
};
in
-((emacsPackagesFor emacs).overrideScope overrides).withPackages
- (p: with p; [
+((emacsPackagesFor emacs).overrideScope overrides).withPackages (
+ p: with p; [
# here both these package will use haskell-mode of our own choice
ghc-mod
dante
- ])
+ ]
+)
```
}
diff --git a/doc/packages/fish.section.md b/doc/packages/fish.section.md
index 85b57acd1090..510d406b1ba1 100644
--- a/doc/packages/fish.section.md
+++ b/doc/packages/fish.section.md
@@ -42,9 +42,12 @@ way to test Fish plugins and scripts without having to alter the environment.
```nix
wrapFish {
- pluginPkgs = with fishPlugins; [ pure foreign-env ];
- completionDirs = [];
- functionDirs = [];
+ pluginPkgs = with fishPlugins; [
+ pure
+ foreign-env
+ ];
+ completionDirs = [ ];
+ functionDirs = [ ];
confDirs = [ "/path/to/some/fish/init/dir/" ];
}
```
diff --git a/doc/packages/ibus.section.md b/doc/packages/ibus.section.md
index 0e379723da12..4f298db35943 100644
--- a/doc/packages/ibus.section.md
+++ b/doc/packages/ibus.section.md
@@ -9,7 +9,8 @@ IBus needs to be configured accordingly to activate `typing-booster`. The config
On NixOS, you need to explicitly enable `ibus` with given engines before customizing your desktop to use `typing-booster`. This can be achieved using the `ibus` module:
```nix
-{ pkgs, ... }: {
+{ pkgs, ... }:
+{
i18n.inputMethod = {
enable = true;
type = "ibus";
@@ -23,7 +24,12 @@ On NixOS, you need to explicitly enable `ibus` with given engines before customi
The IBus engine is based on `hunspell` to support completion in many languages. By default, the dictionaries `de-de`, `en-us`, `fr-moderne` `es-es`, `it-it`, `sv-se` and `sv-fi` are in use. To add another dictionary, the package can be overridden like this:
```nix
-ibus-engines.typing-booster.override { langs = [ "de-at" "en-gb" ]; }
+ibus-engines.typing-booster.override {
+ langs = [
+ "de-at"
+ "en-gb"
+ ];
+}
```
_Note: each language passed to `langs` must be an attribute name in `pkgs.hunspellDicts`._
@@ -35,7 +41,8 @@ The `ibus-engines.typing-booster` package contains a program named `emoji-picker
On NixOS, it can be installed using the following expression:
```nix
-{ pkgs, ... }: {
+{ pkgs, ... }:
+{
fonts.packages = with pkgs; [ noto-fonts-color-emoji ];
}
```
diff --git a/doc/packages/index.md b/doc/packages/index.md
index 35bb6c1fe4ff..35e2656ab670 100644
--- a/doc/packages/index.md
+++ b/doc/packages/index.md
@@ -24,6 +24,8 @@ etc-files.section.md
nginx.section.md
opengl.section.md
shell-helpers.section.md
+python-tree-sitter.section.md
+treefmt.section.md
steam.section.md
cataclysm-dda.section.md
urxvt.section.md
diff --git a/doc/packages/krita.section.md b/doc/packages/krita.section.md
index ba427bd62ba1..f81068d73211 100644
--- a/doc/packages/krita.section.md
+++ b/doc/packages/krita.section.md
@@ -23,7 +23,7 @@ list of previous plugins via `pkgs.krita.binaryPlugins`:
```nix
(pkgs.krita.override (old: {
- binaryPlugins = old.binaryPlugins ++ [ your-plugin ];
+ binaryPlugins = old.binaryPlugins ++ [ your-plugin ];
}))
```
diff --git a/doc/packages/python-tree-sitter.section.md b/doc/packages/python-tree-sitter.section.md
new file mode 100644
index 000000000000..6ccaa31a6283
--- /dev/null
+++ b/doc/packages/python-tree-sitter.section.md
@@ -0,0 +1,56 @@
+# Python Tree Sitter {#python-tree-sitter}
+
+[Tree Sitter](https://tree-sitter.github.io/tree-sitter/) is a framework for building grammars for programming languages. It generates and uses syntax trees from source files, which are useful for code analysis, tooling, and syntax highlighting.
+
+Python bindings for Tree Sitter grammars are provided through the [py-tree-sitter](https://github.com/tree-sitter/py-tree-sitter) module. The Nix package `python3Packages.tree-sitter-grammars` provides pre-built grammars for various languages.
+
+For example, to experiment with the Rust grammar, you can create a shell environment with the following configuration:
+
+```nix
+{
+ pkgs ? { },
+}:
+
+pkgs.mkShell {
+ name = "py-tree-sitter-dev-shell";
+
+ buildInputs = with pkgs; [
+ (python3.withPackages (
+ ps: with ps; [
+ tree-sitter
+ tree-sitter-grammars.tree-sitter-rust
+ ]
+ ))
+ ];
+}
+```
+
+Once inside the shell, the following Python code demonstrates how to parse a Rust code snippet:
+
+```python
+# Import the Tree Sitter library and Rust grammar
+import tree_sitter
+import tree_sitter_rust
+
+# Load the Rust grammar and initialize the parser
+rust = tree_sitter.Language(tree_sitter_rust.language())
+parser = tree_sitter.Parser(rust)
+
+# Parse a Rust snippet
+tree = parser.parse(
+ bytes(
+ """
+ fn main() {
+ println!("Hello, world!");
+ }
+ """,
+ "utf8"
+ )
+)
+
+# Display the resulting syntax tree
+print(tree.root_node)
+```
+
+The `tree_sitter_rust.language()` function references the Rust grammar loaded in the Nix shell. The resulting tree allows you to inspect the structure of the code programmatically.
+
diff --git a/doc/packages/treefmt.section.md b/doc/packages/treefmt.section.md
new file mode 100644
index 000000000000..fbf38f5eb821
--- /dev/null
+++ b/doc/packages/treefmt.section.md
@@ -0,0 +1,23 @@
+# treefmt {#treefmt}
+
+[treefmt](https://github.com/numtide/treefmt) streamlines the process of applying formatters to your project, making it a breeze with just one command line.
+
+The [`treefmt` package](https://search.nixos.org/packages?channel=unstable&show=treefmt)
+provides functions for configuring treefmt using the module system, which are [documented below](#sec-functions-library-treefmt), along with [their options](#sec-treefmt-options-reference).
+
+Alternatively, treefmt can be configured using [treefmt-nix](https://github.com/numtide/treefmt-nix).
+
+```{=include=} sections auto-id-prefix=auto-generated-treefmt-functions
+treefmt-functions.section.md
+```
+
+## Options Reference {#sec-treefmt-options-reference}
+
+The following attributes can be passed to [`withConfig`](#pkgs.treefmt.withConfig) or [`evalConfig`](#pkgs.treefmt.evalConfig):
+
+```{=include=} options
+id-prefix: opt-treefmt-
+list-id: configuration-variable-list
+source: ../treefmt-options.json
+```
+
diff --git a/doc/packages/urxvt.section.md b/doc/packages/urxvt.section.md
index 1d40c92ed73f..e31f0756eb19 100644
--- a/doc/packages/urxvt.section.md
+++ b/doc/packages/urxvt.section.md
@@ -8,9 +8,15 @@ In `nixpkgs`, urxvt is provided by the package `rxvt-unicode`. It can be configu
```nix
rxvt-unicode.override {
- configure = { availablePlugins, ... }: {
- plugins = with availablePlugins; [ perls resize-font vtwheel ];
- };
+ configure =
+ { availablePlugins, ... }:
+ {
+ plugins = with availablePlugins; [
+ perls
+ resize-font
+ vtwheel
+ ];
+ };
}
```
@@ -20,9 +26,11 @@ In order to add plugins but also keep all default plugins installed, it is possi
```nix
rxvt-unicode.override {
- configure = { availablePlugins, ... }: {
- plugins = (builtins.attrValues availablePlugins) ++ [ custom-plugin ];
- };
+ configure =
+ { availablePlugins, ... }:
+ {
+ plugins = (builtins.attrValues availablePlugins) ++ [ custom-plugin ];
+ };
}
```
@@ -40,9 +48,11 @@ In addition to `plugins` the options `extraDeps` and `perlDeps` can be used to i
```nix
rxvt-unicode.override {
- configure = { availablePlugins, ... }: {
- pluginsDeps = [ xsel ];
- };
+ configure =
+ { availablePlugins, ... }:
+ {
+ pluginsDeps = [ xsel ];
+ };
}
```
@@ -50,9 +60,11 @@ rxvt-unicode.override {
```nix
rxvt-unicode.override {
- configure = { availablePlugins, ... }: {
- perlDeps = with perlPackages; [ AnyEvent ];
- };
+ configure =
+ { availablePlugins, ... }:
+ {
+ perlDeps = with perlPackages; [ AnyEvent ];
+ };
}
```
diff --git a/doc/packages/weechat.section.md b/doc/packages/weechat.section.md
index 295397f476b0..f175547de825 100644
--- a/doc/packages/weechat.section.md
+++ b/doc/packages/weechat.section.md
@@ -3,9 +3,16 @@
WeeChat can be configured to include your choice of plugins, reducing its closure size from the default configuration which includes all available plugins. To make use of this functionality, install an expression that overrides its configuration, such as:
```nix
-weechat.override {configure = ({availablePlugins, ...}: {
- plugins = with availablePlugins; [ python perl ];
- });
+weechat.override {
+ configure = (
+ { availablePlugins, ... }:
+ {
+ plugins = with availablePlugins; [
+ python
+ perl
+ ];
+ }
+ );
}
```
@@ -16,10 +23,18 @@ The plugins currently available are `python`, `perl`, `ruby`, `guile`, `tcl` and
The Python and Perl plugins allows the addition of extra libraries. For instance, the `inotify.py` script in `weechat-scripts` requires D-Bus or libnotify, and the `fish.py` script requires `pycrypto`. To use these scripts, use the plugin's `withPackages` attribute:
```nix
-weechat.override { configure = {availablePlugins, ...}: {
- plugins = with availablePlugins; [
- (python.withPackages (ps: with ps; [ pycrypto python-dbus ]))
- ];
+weechat.override {
+ configure =
+ { availablePlugins, ... }:
+ {
+ plugins = with availablePlugins; [
+ (python.withPackages (
+ ps: with ps; [
+ pycrypto
+ python-dbus
+ ]
+ ))
+ ];
};
}
```
@@ -27,23 +42,37 @@ weechat.override { configure = {availablePlugins, ...}: {
In order to also keep all default plugins installed, it is possible to use the following method:
```nix
-weechat.override { configure = { availablePlugins, ... }: {
- plugins = builtins.attrValues (availablePlugins // {
- python = availablePlugins.python.withPackages (ps: with ps; [ pycrypto python-dbus ]);
- });
-}; }
+weechat.override {
+ configure =
+ { availablePlugins, ... }:
+ {
+ plugins = builtins.attrValues (
+ availablePlugins
+ // {
+ python = availablePlugins.python.withPackages (
+ ps: with ps; [
+ pycrypto
+ python-dbus
+ ]
+ );
+ }
+ );
+ };
+}
```
WeeChat allows to set defaults on startup using the `--run-command`. The `configure` method can be used to pass commands to the program:
```nix
weechat.override {
- configure = { availablePlugins, ... }: {
- init = ''
- /set foo bar
- /server add libera irc.libera.chat
- '';
- };
+ configure =
+ { availablePlugins, ... }:
+ {
+ init = ''
+ /set foo bar
+ /server add libera irc.libera.chat
+ '';
+ };
}
```
@@ -53,14 +82,18 @@ Additionally, it's possible to specify scripts to be loaded when starting `weech
```nix
weechat.override {
- configure = { availablePlugins, ... }: {
- scripts = with pkgs.weechatScripts; [
- weechat-xmpp weechat-matrix-bridge wee-slack
- ];
- init = ''
- /set plugins.var.python.jabber.key "val"
- '';
- };
+ configure =
+ { availablePlugins, ... }:
+ {
+ scripts = with pkgs.weechatScripts; [
+ weechat-xmpp
+ weechat-matrix-bridge
+ wee-slack
+ ];
+ init = ''
+ /set plugins.var.python.jabber.key "val"
+ '';
+ };
}
```
@@ -75,7 +108,10 @@ stdenv.mkDerivation {
url = "https://scripts.tld/your-scripts.tar.gz";
hash = "...";
};
- passthru.scripts = [ "foo.py" "bar.lua" ];
+ passthru.scripts = [
+ "foo.py"
+ "bar.lua"
+ ];
installPhase = ''
mkdir $out/share
cp foo.py $out/share
diff --git a/doc/preface.chapter.md b/doc/preface.chapter.md
index e6a0905c5a95..56f089703021 100644
--- a/doc/preface.chapter.md
+++ b/doc/preface.chapter.md
@@ -42,7 +42,7 @@ shows the status of tests for the `nixpkgs-unstable` channel.
The tests are conducted by a cluster called [Hydra](https://nixos.org/hydra/),
which also builds binary packages from the Nix expressions in Nixpkgs for
-`x86_64-linux`, `i686-linux` and `x86_64-darwin`.
+`x86_64-linux`, `aarch64-linux`, `x86_64-darwin` and `aarch64-darwin`.
The binaries are made available via a [binary cache](https://cache.nixos.org).
The current Nix expressions of the channels are available in the
diff --git a/doc/redirects.json b/doc/redirects.json
index 2a8069a490bb..1bcf2a8bf52c 100644
--- a/doc/redirects.json
+++ b/doc/redirects.json
@@ -5,9 +5,32 @@
"chap-release-notes": [
"release-notes.html#chap-release-notes"
],
+ "chap-toolchains": [
+ "index.html#chap-toolchains"
+ ],
+ "cmake-ctest": [
+ "index.html#cmake-ctest"
+ ],
+ "cmake-ctest-disabled-tests": [
+ "index.html#cmake-ctest-disabled-tests"
+ ],
+ "cmake-ctest-flags": [
+ "index.html#cmake-ctest-flags"
+ ],
+ "cmake-ctest-variables": [
+ "index.html#cmake-ctest-variables"
+ ],
"ex-build-helpers-extendMkDerivation": [
"index.html#ex-build-helpers-extendMkDerivation"
],
+ "ex-pkgs-replace-vars": [
+ "index.html#ex-pkgs-replace-vars",
+ "index.html#ex-pkgs-substituteAll",
+ "index.html#ex-pkgs-substitute-all-files"
+ ],
+ "ex-pkgs-replace-vars-with": [
+ "index.html#ex-pkgs-replace-vars-with"
+ ],
"ex-shfmt": [
"index.html#ex-shfmt"
],
@@ -35,6 +58,29 @@
"no-broken-symlinks.sh": [
"index.html#no-broken-symlinks.sh"
],
+ "nostrictaliasing": [
+ "index.html#nostrictaliasing"
+ ],
+ "pkgs-replacevars": [
+ "index.html#pkgs-replacevars",
+ "index.html#pkgs-substituteall",
+ "index.html#pkgs-substituteallfiles"
+ ],
+ "pkgs-replacevarswith": [
+ "index.html#pkgs-replacevarswith"
+ ],
+ "part-toolchains": [
+ "index.html#part-toolchains"
+ ],
+ "pkgs.treefmt.buildConfig": [
+ "index.html#pkgs.treefmt.buildConfig"
+ ],
+ "pkgs.treefmt.evalConfig": [
+ "index.html#pkgs.treefmt.evalConfig"
+ ],
+ "pkgs.treefmt.withConfig": [
+ "index.html#pkgs.treefmt.withConfig"
+ ],
"preface": [
"index.html#preface"
],
@@ -50,6 +96,9 @@
"chap-packageconfig": [
"index.html#chap-packageconfig"
],
+ "python-tree-sitter": [
+ "index.html#python-tree-sitter"
+ ],
"sec-allow-broken": [
"index.html#sec-allow-broken"
],
@@ -65,6 +114,15 @@
"sec-build-helper-extendMkDerivation": [
"index.html#sec-build-helper-extendMkDerivation"
],
+ "sec-building-packages-with-llvm": [
+ "index.html#sec-building-packages-with-llvm"
+ ],
+ "sec-building-packages-with-llvm-using-clang-stdenv": [
+ "index.html#sec-building-packages-with-llvm-using-clang-stdenv"
+ ],
+ "sec-functions-library-treefmt": [
+ "index.html#sec-functions-library-treefmt"
+ ],
"sec-inkscape": [
"index.html#sec-inkscape"
],
@@ -92,6 +150,30 @@
"chap-overlays": [
"index.html#chap-overlays"
],
+ "sec-nixpkgs-release-25.11": [
+ "release-notes.html#sec-nixpkgs-release-25.11"
+ ],
+ "sec-nixpkgs-release-25.11-highlights": [
+ "release-notes.html#sec-nixpkgs-release-25.11-highlights"
+ ],
+ "sec-nixpkgs-release-25.11-incompatibilities": [
+ "release-notes.html#sec-nixpkgs-release-25.11-incompatibilities"
+ ],
+ "sec-nixpkgs-release-25.11-lib": [
+ "release-notes.html#sec-nixpkgs-release-25.11-lib"
+ ],
+ "sec-nixpkgs-release-25.11-lib-breaking": [
+ "release-notes.html#sec-nixpkgs-release-25.11-lib-breaking"
+ ],
+ "sec-nixpkgs-release-25.11-lib-deprecations": [
+ "release-notes.html#sec-nixpkgs-release-25.11-lib-deprecations"
+ ],
+ "sec-nixpkgs-release-25.11-lib-additions-improvements": [
+ "release-notes.html#sec-nixpkgs-release-25.11-lib-additions-improvements"
+ ],
+ "sec-nixpkgs-release-25.11-notable-changes": [
+ "release-notes.html#sec-nixpkgs-release-25.11-notable-changes"
+ ],
"sec-nixpkgs-release-25.05": [
"release-notes.html#sec-nixpkgs-release-25.05"
],
@@ -326,6 +408,9 @@
"chap-stdenv": [
"index.html#chap-stdenv"
],
+ "sec-using-llvm": [
+ "index.html#sec-using-llvm"
+ ],
"sec-using-stdenv": [
"index.html#sec-using-stdenv"
],
@@ -335,6 +420,9 @@
"sec-tools-of-stdenv": [
"index.html#sec-tools-of-stdenv"
],
+ "sec-treefmt-options-reference": [
+ "index.html#sec-treefmt-options-reference"
+ ],
"ssec-cosmic-common-issues": [
"index.html#ssec-cosmic-common-issues"
],
@@ -410,6 +498,33 @@
"tester-testEqualArrayOrMap-return": [
"index.html#tester-testEqualArrayOrMap-return"
],
+ "treefmt": [
+ "index.html#treefmt"
+ ],
+ "typst": [
+ "index.html#typst",
+ "doc/languages-frameworks/typst.section.md#typst"
+ ],
+ "typst-custom-environment": [
+ "index.html#typst-custom-environment",
+ "doc/languages-frameworks/typst.section.md#typst-custom-environment"
+ ],
+ "typst-custom-packages": [
+ "index.html#typst-custom-packages",
+ "doc/languages-frameworks/typst.section.md#typst-custom-packages"
+ ],
+ "typst-handling-outdated-package-hashes": [
+ "index.html#typst-handling-outdated-package-hashes"
+ ],
+ "typst-package-scope-and-usage": [
+ "index.html#typst-package-scope-and-usage"
+ ],
+ "var-meta-teams": [
+ "index.html#var-meta-teams"
+ ],
+ "var-go-goSum": [
+ "index.html#var-go-goSum"
+ ],
"variables-specifying-dependencies": [
"index.html#variables-specifying-dependencies"
],
@@ -1058,6 +1173,9 @@
"var-meta-broken": [
"index.html#var-meta-broken"
],
+ "var-meta-knownVulnerabilities": [
+ "index.html#var-meta-knownVulnerabilities"
+ ],
"sec-meta-license": [
"index.html#sec-meta-license"
],
@@ -2064,6 +2182,9 @@
"setup-hook-automake": [
"index.html#setup-hook-automake"
],
+ "setup-hook-autopatchcilhook": [
+ "index.html#setup-hook-autopatchcilhook"
+ ],
"setup-hook-autopatchelfhook": [
"index.html#setup-hook-autopatchelfhook"
],
@@ -3184,6 +3305,12 @@
"javascript-yarn": [
"index.html#javascript-yarn"
],
+ "javascript-yarn-v1": [
+ "index.html#javascript-yarn-v1"
+ ],
+ "javascript-yarn-v3-v4": [
+ "index.html#javascript-yarn-v3-v4"
+ ],
"javascript-yarnconfighook": [
"index.html#javascript-yarnconfighook"
],
@@ -3211,6 +3338,18 @@
"javascript-yarn2nix-pitfalls": [
"index.html#javascript-yarn2nix-pitfalls"
],
+ "javascript-yarnBerry-missing-hashes": [
+ "index.html#javascript-yarnBerry-missing-hashes"
+ ],
+ "javascript-yarnBerryConfigHook": [
+ "index.html#javascript-yarnBerryConfigHook"
+ ],
+ "javascript-yarnBerry-patching": [
+ "index.html#javascript-yarnBerry-patching"
+ ],
+ "javascript-fetchYarnBerryDeps": [
+ "index.html#javascript-fetchYarnBerryDeps"
+ ],
"javascript-outside-nixpkgs": [
"index.html#javascript-outside-nixpkgs"
],
@@ -4166,18 +4305,6 @@
"ex-pkgs-substitute": [
"index.html#ex-pkgs-substitute"
],
- "pkgs-substituteall": [
- "index.html#pkgs-substituteall"
- ],
- "ex-pkgs-substituteAll": [
- "index.html#ex-pkgs-substituteAll"
- ],
- "pkgs-substituteallfiles": [
- "index.html#pkgs-substituteallfiles"
- ],
- "ex-pkgs-substitute-all-files": [
- "index.html#ex-pkgs-substitute-all-files"
- ],
"part-development": [
"index.html#part-development"
],
diff --git a/doc/release-notes/release-notes.md b/doc/release-notes/release-notes.md
index 454ea7c50a9e..0f4df3086f43 100644
--- a/doc/release-notes/release-notes.md
+++ b/doc/release-notes/release-notes.md
@@ -3,5 +3,6 @@
This section lists the release notes for each stable version of Nixpkgs and current unstable revision.
```{=include=} sections
+rl-2511.section.md
rl-2505.section.md
```
diff --git a/doc/release-notes/rl-2505.section.md b/doc/release-notes/rl-2505.section.md
index 6a6a797269c6..8ae1797aaf1a 100644
--- a/doc/release-notes/rl-2505.section.md
+++ b/doc/release-notes/rl-2505.section.md
@@ -15,6 +15,9 @@
- GCC has been updated from GCC 13 to GCC 14.
This introduces some backwards‐incompatible changes; see the [upstream porting guide](https://gcc.gnu.org/gcc-14/porting_to.html) for details.
+- The default GHC version has been updated from 9.6 to 9.8.
+ `haskellPackages` also uses Stackage LTS 23 (instead of LTS 22) as a baseline.
+
- LLVM has been updated from LLVM 16 (on Darwin) and LLVM 18 (on other platforms) to LLVM 19.
This introduces some backwards‐incompatible changes; see the [upstream release notes](https://releases.llvm.org/) for details.
@@ -25,14 +28,14 @@
NEWS can been viewed from Emacs by typing `C-h n`, or by clicking `Help->Emacs News` from the menu bar.
It can also be browsed [online](https://git.savannah.gnu.org/cgit/emacs.git/tree/etc/NEWS?h=emacs-30).
+- The default openexr version has been updated to 3.2.4.
+
- The default PHP version has been updated to 8.4.
- The default Erlang OTP version has been updated to 27.
- The default Elixir version has been updated to 1.18.
-- `buildPythonPackage`, `buildPythonApplication` and the Python building setup hooks now support both `__structuredAttrs = true` and `__structuredAttrs = false`.
-
## Backward Incompatibilities {#sec-nixpkgs-release-25.05-incompatibilities}
@@ -46,16 +49,36 @@
- `squid` has been updated to version 7, this release includes multiple breaking changes, like ESI removal.
For more information, [check the release notes](https://github.com/squid-cache/squid/releases/tag/SQUID_7_0_1).
+- `postgresql` and `libpq` don't provide `pg_config` by default anymore. Instead, `pg_config` is available via `postgresql.pg_config` or `libpq.pg_config`. This allowed implementing it as a shell script, which can be built for both the build and host systems when cross-compiling. If your build fails to find `pg_config`, add `postgresql.pg_config` or `libpq.pg_config` to `nativeBuildInputs`.
+
- The [`no-broken-symlinks` hook](https://nixos.org/manual/nixpkgs/unstable/#no-broken-symlinks.sh) was added to catch builds containing dangling or reflexive symlinks, as these are indicative of problems with packaging.
The hook can be disabled by providing `dontCheckForBrokenSymlinks = true;` as an argument to `mkDerivation`.
For more information, [check the docs](https://nixos.org/manual/nixpkgs/unstable/#no-broken-symlinks.sh) or [see this PR](https://github.com/NixOS/nixpkgs/pull/370750).
+- `opensmtpd-extras` has been deprecated by upstream and is not compatible with
+ OpenSMTPD 7.6.0 or later. The package has been removed in favor of a set of new
+ `opensmtpd-table-*` packages.
+
+- `postsrsd` upgraded to `>= 2.0.0`, with some different behaviors and
+ configuration settings. Notably, it now defaults to listening on a socket
+ rather than a port. See [Migrating from version 1.x](https://github.com/roehling/postsrsd/blob/2.0.10/README.rst#migrating-from-version-1x) and [Postfix Setup](https://github.com/roehling/postsrsd?tab=readme-ov-file#postfix-setup) for details.
+
- The hand written `perlPackages.SearchXapian` bindings have been dropped in favor of the (mostly compatible)
`perlPackages.Xapian`.
+- The `config` triple for `aarch64-darwin` has been changed from `aarch64-apple-darwin` to `arm64-apple-darwin` to match the Apple toolchain and LLVM’s expectations.
+
+- The `electron` packages will now provide their headers (available via `electron.headers`) in extracted form instead of in a tarball.
+
+- The `ephemeral` package was removed due to upstream archival in early 2022.
+
+- The `vocal` package was removed due to upstream archival. The upstream developer suggests using `gnome-podcasts` or `kasts` instead.
+
- [testers.shellcheck](https://nixos.org/manual/nixpkgs/unstable/#tester-shellcheck) now warns when `name` is not provided.
The `name` argument will become mandatory in a future release.
+- [GIMP 3.0](https://www.gimp.org/news/2025/03/16/gimp-3-0-released/) available as `gimp3`.
+
- `grafana-agent` and `services.grafana-agent` have been removed in favor of
Grafana Alloy (`grafana-alloy` and `services.alloy`), as they depend on an EOL compiler version
and will become EOL during the 25.05 lifecycle.
@@ -70,6 +93,8 @@
The `nixLog` function, which logs unconditionally, was also re-introduced and modified to prefix messages with the function name of the caller.
For more information, [see this PR](https://github.com/NixOS/nixpkgs/pull/370742).
+- `postgresql`'s `pythonSupport` argument has been changed. It is now enabled by default, but to use PL/Python the extension needs to be added explicitly with `postgresql.withPackages`. If you were using `postgresql.override { pythonSupport = true; }` before, change it to `postgresql.withPackages (ps: [ ps.plpython3 ])`. The same applies to `perlSupport`/`plperl` and `tclSupport`/`pltcl` respectively.
+
- Rust packages will need to regenerate their `cargoHash`.
Cargo 1.84.0 changed the format of `cargo vendor` output, which invalidated all existing `rustPlatform.fetchCargoTarball` hashes.
To preserve Nix’s invariants, it has been replaced with `rustPlatform.fetchCargoVendor`, an independent implementation prioritizing format stability.
@@ -77,6 +102,10 @@
Packages wishing to maintain compatibility with Nixpkgs 24.11 must set `useFetchCargoVendor` to `true` explicitly.
`rustPlatform.importCargoLock` may also be appropriate in some circumstances.
+- `cassandra_3_0` and `cassandra_3_11` have been removed as they have reached end-of-life. Please update to `cassandra_4`. See the [changelog](https://github.com/apache/cassandra/blob/cassandra-4.0.17/NEWS.txt) for more information about the upgrade process.
+
+- `mariadb_105` has been removed as it has reached end-of-life in 2025-06. Please update to `mariadb_106`.
+
- NetBox was updated to `>= 4.2.0`. Have a look at the breaking changes
of the [4.1 release](https://github.com/netbox-community/netbox/releases/tag/v4.1.0)
and the [4.2 release](https://github.com/netbox-community/netbox/releases/tag/v4.2.0),
@@ -90,8 +119,14 @@
- `ocis-bin` has been renamed to `ocis_5-bin`. Future versions will have the major version suffix.
+- All support for 32‐bit Darwin systems has been dropped.
+
+- `substituteAll` and `substituteAllFiles` have been deprecated in favor of `replaceVars` and will be removed in the next release.
+
- Default ICU version updated from 74 to 76
+- The packages `signald`, `signaldctl` and `purple-signald` have been dropped as they are unmaintained upstream and have been incompatible with the official Signal servers for a long while.
+
- Apache Kafka was updated to `>= 4.0.0`. Please note that this is the first release which operates
entirely without Apache ZooKeeper support, and all clusters need to be migrated to KRaft mode. See
the [release announcement](https://kafka.apache.org/blog#apache_kafka_400_release_announcement)
@@ -108,7 +143,9 @@
- `binwalk` was updated to 3.1.0, which has been rewritten in rust. The python module is no longer available.
See the release notes of [3.1.0](https://github.com/ReFirmLabs/binwalk/releases/tag/v3.1.0) for more information.
-- `pkgs.nextcloud28` has been removed since it's out of support upstream.
+- `pkgs.nextcloud28` and `pkgs.nextcloud29` have been removed since they are out of support upstream.
+
+- `centrifugo` was updated to v6, which uses a new config format. See [upstream documentation](https://centrifugal.dev/docs/getting-started/migration_v6) for migration.
- `teleport` has been upgraded from major version 16 to major version 17.
Refer to [upstream upgrade instructions](https://goteleport.com/docs/upgrading/overview/)
@@ -125,6 +162,8 @@
- `buildGoPackage` has been removed. Use `buildGoModule` instead. See the [Go section in the nixpkgs manual](https://nixos.org/manual/nixpkgs/unstable/#sec-language-go) for details.
+- `buildGoModule` now supports a `goSum` attribute (`null` by default) to optionally provide a path to `go.sum` and correctly enabling rebuilds when the file changes.
+
- top-level `playwright` now refers to the github Microsoft/playwright package
instead of the python tester launcher. You can still refer to the python
launcher via `python3Packages.toPythonApplication python3Packages.playwright`
@@ -141,6 +180,8 @@
- The `haka` package and module has been removed because the package was broken and unmaintained for 9 years.
+- The `gsignond` package, plugins and module have been removed because they were unmaintained for 6 years.
+
- `strawberry` has been updated to 1.2, which drops support for the VLC backend and Qt 5. The `strawberry-qt5` package
and `withGstreamer`/`withVlc` override options have been removed due to this.
@@ -165,6 +206,10 @@
- `mkBinaryCache` now defaults to using `zstd` compression for the binary caches it creates. The previous `xz` compression method can be used by passing `compression = "xz";`.
+- `nodejs_latest` was updated from 23.x to 24.x. `nodejs_23` has been removed in favor of `nodejs_24`.
+
+- `nodejs_18` package was removed due to upstream End-of-Life in April 2025.
+
- `nodePackages."@commitlint/config-conventional"` has been removed, as it is a library, and projects should depend on it instead.
- zigbee2mqtt is now available in version 2.x as `zigbee2mqtt_2`. In NixOS 25.11 we'll remove `zigbee2mqtt_1` and default to `zigbee2mqtt_2`. See the [breaking changes](https://github.com/Koenkk/zigbee2mqtt/discussions/24198) announcement for 2.0.0.
@@ -185,6 +230,10 @@
- `pnpm` was updated to version 10. If your project is incompatible, you can install the previous version from the package attribute `pnpm_9`.
+- `dwarf-fortress-packages` now only contains one minor version for each major version since version 0.44. Saves should still be compatible, but you may have to change which minor version you were using if it was one other than the newest.
+
+- `tpm2-pkcs11` now is compiled without abrmd (Access Broker and Resource Manager Daemon) support by default, preferring the kernel resource manager. Use `tpm2-pkcs11.abrmd` if you would like a version with abrmd support. Note that the NixOS module picks the correct one automatically based on `security.tpm2.abrmd`.
+
- `zig_0_9` and `zig_0_10` have been removed, you should upgrade to `zig_0_13` (also available as just `zig`), `zig_0_12` or `zig_0_11` instead.
- `webpack-cli` was updated to major version 6, which has breaking changes from the previous version 5.1.4. See the [upstream release notes](https://github.com/webpack/webpack-cli/releases/tag/webpack-cli%406.0.0) for details on these changes.
@@ -205,6 +254,9 @@
- `fluxus` has been removed, as it depends on `racket_7_9` and had no updates in 9 years.
+- `meilisearch` has been upgraded from 1.11.3 to 1.14.0, which requires manually dumping and importing the data.
+ See Meilisearch's [upgrade guide](https://www.meilisearch.com/docs/learn/update_and_migration/updating) for more information.
+
- `sm64ex-coop` has been removed as it was archived upstream. Consider migrating to `sm64coopdx`.
- `tldr` now uses [`tldr-python-client`](https://github.com/tldr-pages/tldr-python-client) instead of [`tldr-c-client`](https://github.com/tldr-pages/tldr-c-client) which is unmaintained.
@@ -220,7 +272,7 @@
- `nodePackages.meshcommander` has been removed, as the package was deprecated by Intel.
-- The default version of `z3` has been updated from 4.8 to 4.13. There are still a few packages that need specific older versions; those will continue to be maintained as long as other packages depend on them but may be removed in the future.
+- The default version of `z3` has been updated from 4.8 to 4.14, and all old versions have been dropped. Note that `fstar` still depends on specific versions, and maintains them as overrides.
- `prometheus` has been updated from 2.55.0 to 3.1.0.
Read the [release blog post](https://prometheus.io/blog/2024/11/14/prometheus-3-0/) and
@@ -252,6 +304,11 @@
For those unable to upgrade yet, there is a [v0 compatibility mode](https://www.openpolicyagent.org/docs/v1.0.1/v0-compatibility/)
available too.
+- `helmfile` was updated to v1.0.0, which introduces several breaking changes.
+ See the release notes of
+ [v1.0.0](https://github.com/helmfile/helmfile/releases/v1.0.0) for more
+ information.
+
- `vscode-utils.buildVscodeExtension` now requires pname as an argument
- `nerdfonts` has been separated into individual font packages under the namespace `nerd-fonts`. The directories for font
@@ -273,6 +330,7 @@
- `docker_24` has been removed, as it was EOL with vulnerabilities since June 08, 2024.
- Emacs 28 and 29 have been removed.
+- Emacs 28 Macport has been removed, while CVEs of Emacs 29 Macport are patched.
- `containerd` has been updated to v2, which contains breaking changes. See the [containerd
2.0](https://github.com/containerd/containerd/blob/main/docs/containerd-2.0.md) documentation for more
@@ -287,12 +345,12 @@
add `vimPlugins.notmuch-vim` to your (Neo)vim configuration if you want the
vim plugin.
-- `prisma` and `prisma-engines` have been updated to version 6.3.0, which
+- `prisma` and `prisma-engines` have been updated to version 6.7.0, which
introduces several breaking changes. See the
[Prisma ORM upgrade guide](https://www.prisma.io/docs/orm/more/upgrade-guides/upgrading-versions/upgrading-to-prisma-6)
for more information.
-- `depdendency-track` no longer bundes the UI inside the jar. This bundling
+- `dependency-track` no longer bundes the UI inside the jar. This bundling
functionality is deprecated by upstream and causes UI assets not being served
after weeks of runtime.
@@ -314,21 +372,38 @@
## Other Notable Changes {#sec-nixpkgs-release-25.05-notable-changes}
+- `i18n` module improvements:
+ - `i18n.extraLocales` should now be the preferred way to install additional locales.
+ - `i18n.supportedLocales` is now considered an implementation detail and will be hidden from the documentation. But the option will still continue to work.
+ - `i18n.supportedLocales` will now trigger a warning when it omits any locale set in `i18n.defaultLocale`, `i18n.extraLocales` or `i18n.extraLocaleSettings`.
+ - The options `i18n.defaultCharset` & `i18n.localeCharsets` were added, and they complement `i18n.defaultLocale` & `i18n.extraLocaleSettings` respectively - allowing to control the character set used per locale setting.
+
- `titaniumenv`, `titanium`, and `titanium-alloy` have been removed due to lack of maintenance in Nixpkgs []{#sec-nixpkgs-release-25.05-incompatibilities-titanium-removed}.
+- androidenv has been improved:
+ - All versions specified in composeAndroidPackages now track the latest. Android packages are automatically updated on unstable, and run the androidenv test suite on every update.
+ - Many androidenv packages are now searchable on [search.nixos.org](https://search.nixos.org).
+ - We now use the latest Google repositories, which should improve aarch64-darwin compatibility. The SDK now additionally evaluates on aarch64-linux, though not all packages are functional.
+
+- `dwarf-fortress` audio now works again. Additionally, the `dfhack` and `dwarf-fortress-full` packages are now exposed at toplevel, making it easier to install and play Dwarf Fortress. Note that `dwarf-fortress-full` is the Nixpkgs equivalent of the Dwarf Fortress Lazy Pack.
+
- `gerbera` now has wavpack support.
- GOverlay has been updated to 1.2, please check the [upstream changelog](https://github.com/benjamimgois/goverlay/releases) for more details.
+- `tpm2-pkcs11` now has the variant `tpm2-pkcs11-fapi`, which has been patched to default to the Feature API backend. It has also been split into `tpm2-pkcs11-esapi`, which _only_ supports the older Enhanced System API backend. Note the [differences](https://github.com/tpm2-software/tpm2-pkcs11/blob/1.9.1/docs/FAPI.md), and that `tpm2-pkcs11` itself still needs `TPM2_PKCS11_BACKEND=fapi` exported in order to use the Feature API, whereas `tpm2-pkcs11-fapi` does not, and `tpm2-pkcs11-esapi` just does not support fapi entirely.
+
- For matrix homeserver Synapse we are now following the upstream recommendation to enable jemalloc as the memory allocator by default.
- In `dovecot` package removed hard coding path to module directory.
+- `signal-desktop` has been migrated to a from source build. No state migration is necessary. In case there's no working source build available (like on Darwin), the the binary build is still available at `signal-desktop-bin`.
+
- `ddclient` was updated from 3.11.2 to 4.0.0 [Release notes](https://github.com/ddclient/ddclient/releases/tag/v4.0.0)
### NexusMods.App upgraded {#sec-nixpkgs-release-25.05-incompatibilities-nexusmods-app-upgraded}
-- `nexusmods-app` has been upgraded from version 0.6.3 to 0.8.3.
+- `nexusmods-app` has been upgraded from version 0.6.3 to 0.10.2.
- Before upgrading, you **must reset all app state** (mods, games, settings, etc). NexusMods.App will crash if any state from a version older than 0.7.0 is still present.
@@ -352,6 +427,8 @@
- [`lib.packagesFromDirectoryRecursive`] now rejects unknown arguments.
[`lib.packagesFromDirectoryRecursive`]: https://nixos.org/manual/nixpkgs/stable/#function-library-lib.filesystem.packagesFromDirectoryRecursive
+- The `godot-export-templates` package now has its content at `share/godot/export_templates/$version` instead of the output root. This makes it more convenient for for symlinking into `~/.local`, but scripts expecting the old layout will need to be changed.
+
### Deprecations {#sec-nixpkgs-release-25.05-lib-deprecations}
- `functor` is an implementation detail and should not be relied upon, but since its status wasn't clear and it has had some use cases without alternatives, changes are being handled as gracefully as possible. Deprecations within functor:
@@ -364,6 +441,10 @@
- `lib.types.coercedTo`
- `lib.types.either`
+- The `testTarget` argument of `haskellPackages.mkDerivation` has been deprecated in favour of `testTargets`.
+ `testTarget` took a space separated string of targets, whereas the new `testTargets` argument takes a list of targets.
+ For instance, `testTarget = "foo bar baz"` should become `testTargets = [ "foo" "bar" "baz" ]`.
+
- Plasma 5 and Qt 5 based versions of associated software are deprecated in NixOS 25.05, and will be removed in NixOS 25.11. Users are encouraged to upgrade to Plasma 6.
- `rustPlatform.buildRustPackage` stops handling the deprecated argument `cargoSha256`. Out-of-tree packages that haven't migrated from `cargoSha256` to `cargoHash` now receive errors.
diff --git a/doc/release-notes/rl-2511.section.md b/doc/release-notes/rl-2511.section.md
new file mode 100644
index 000000000000..da42ae4326de
--- /dev/null
+++ b/doc/release-notes/rl-2511.section.md
@@ -0,0 +1,36 @@
+# Nixpkgs 25.11 ("Xantusia", 2025.11/??) {#sec-nixpkgs-release-25.11}
+
+## Highlights {#sec-nixpkgs-release-25.11-highlights}
+
+
+- Added `allowVariants` to gate availability of package sets like `pkgsLLVM`, `pkgsMusl`, `pkgsZig`, etc.
+
+## Backward Incompatibilities {#sec-nixpkgs-release-25.11-incompatibilities}
+
+
+
+- Create the first release note entry in this section!
+
+## Other Notable Changes {#sec-nixpkgs-release-25.11-notable-changes}
+
+
+
+- Create the first release note entry in this section!
+
+## Nixpkgs Library {#sec-nixpkgs-release-25.11-lib}
+
+
+
+### Breaking changes {#sec-nixpkgs-release-25.11-lib-breaking}
+
+- Create the first release note entry in this section!
+
+
+### Deprecations {#sec-nixpkgs-release-25.11-lib-deprecations}
+
+- Create the first release note entry in this section!
+
+
+### Additions and Improvements {#sec-nixpkgs-release-25.11-lib-additions-improvements}
+
+- Create the first release note entry in this section!
diff --git a/doc/stdenv/cross-compilation.chapter.md b/doc/stdenv/cross-compilation.chapter.md
index b06a8ab93a70..e115d9d103ae 100644
--- a/doc/stdenv/cross-compilation.chapter.md
+++ b/doc/stdenv/cross-compilation.chapter.md
@@ -15,7 +15,13 @@ Nixpkgs follows the [conventions of GNU autoconf](https://gcc.gnu.org/onlinedocs
In Nixpkgs, these three platforms are defined as attribute sets under the names `buildPlatform`, `hostPlatform`, and `targetPlatform`. They are always defined as attributes in the standard environment. That means one can access them like:
```nix
-{ stdenv, fooDep, barDep, ... }: {
+{
+ stdenv,
+ fooDep,
+ barDep,
+ ...
+}:
+{
# ...stdenv.buildPlatform...
}
```
@@ -169,11 +175,13 @@ e.g.
```nix
{
- nativeBuildInputs = [
- meson
- ] ++ lib.optionals (!stdenv.buildPlatform.canExecute stdenv.hostPlatform) [
- mesonEmulatorHook
- ];
+ nativeBuildInputs =
+ [
+ meson
+ ]
+ ++ lib.optionals (!stdenv.buildPlatform.canExecute stdenv.hostPlatform) [
+ mesonEmulatorHook
+ ];
}
```
diff --git a/doc/stdenv/meta.chapter.md b/doc/stdenv/meta.chapter.md
index ddd5eb45441a..18cf5be10d45 100644
--- a/doc/stdenv/meta.chapter.md
+++ b/doc/stdenv/meta.chapter.md
@@ -91,6 +91,10 @@ For details, see [Source provenance](#sec-meta-sourceProvenance).
A list of the maintainers of this Nix expression. Maintainers are defined in [`nixpkgs/maintainers/maintainer-list.nix`](https://github.com/NixOS/nixpkgs/blob/master/maintainers/maintainer-list.nix). There is no restriction to becoming a maintainer, just add yourself to that list in a separate commit titled “maintainers: add alice” in the same pull request, and reference maintainers with `maintainers = with lib.maintainers; [ alice bob ]`.
+### `teams` {#var-meta-teams}
+
+A list of the teams of this Nix expression. Teams are defined in [`nixpkgs/maintainers/team-list.nix`](https://github.com/NixOS/nixpkgs/blob/master/maintainers/team-list.nix), and can be defined in a package with `meta.teams = with lib.teams; [ team1 team2 ]`.
+
### `mainProgram` {#var-meta-mainProgram}
The name of the main binary for the package. This affects the binary `nix run` executes. Example: `"rg"`
@@ -145,7 +149,7 @@ The list of Nix platform types for which the [Hydra](https://github.com/nixos/hy
```nix
{
meta.platforms = lib.platforms.linux;
- meta.hydraPlatforms = [];
+ meta.hydraPlatforms = [ ];
}
```
@@ -169,13 +173,34 @@ This means that `broken` can be used to express constraints, for example:
```nix
{
- meta.broken = lib.all (map (p: p.meta.broken) [ glibc musl ]);
+ meta.broken = lib.all (
+ map (p: p.meta.broken) [
+ glibc
+ musl
+ ]
+ );
}
```
This makes `broken` strictly more powerful than `meta.badPlatforms`.
However `meta.availableOn` currently examines only `meta.platforms` and `meta.badPlatforms`, so `meta.broken` does not influence the default values for optional dependencies.
+## `knownVulnerabilities` {#var-meta-knownVulnerabilities}
+
+A list of known vulnerabilities affecting the package, usually identified by CVE identifiers.
+
+This metadata allows users and tools to be aware of unresolved security issues before using the package, for example:
+
+```nix
+{
+ meta.knownVulnerabilities = [
+ "CVE-2024-3094: Malicious backdoor allowing unauthorized remote code execution"
+ ];
+}
+```
+
+If this list is not empty, the package is marked as "insecure", meaning that it cannot be built or installed unless the environment variable [`NIXPKGS_ALLOW_INSECURE`](#sec-allow-insecure) is set.
+
## Licenses {#sec-meta-license}
The `meta.license` attribute should preferably contain a value from `lib.licenses` defined in [`nixpkgs/lib/licenses.nix`](https://github.com/NixOS/nixpkgs/blob/master/lib/licenses.nix), or in-place license description of the same format if the license is unlikely to be useful in another expression.
diff --git a/doc/stdenv/multiple-output.chapter.md b/doc/stdenv/multiple-output.chapter.md
index 09fdba01c44a..9140f5a952b7 100644
--- a/doc/stdenv/multiple-output.chapter.md
+++ b/doc/stdenv/multiple-output.chapter.md
@@ -31,7 +31,12 @@ In nixpkgs there is a framework supporting multiple-output derivations. It tries
```nix
{
- outputs = [ "bin" "dev" "out" "doc" ];
+ outputs = [
+ "bin"
+ "dev"
+ "out"
+ "doc"
+ ];
}
```
diff --git a/doc/stdenv/passthru.chapter.md b/doc/stdenv/passthru.chapter.md
index 0c0b03fd0dc2..db40d4bbe054 100644
--- a/doc/stdenv/passthru.chapter.md
+++ b/doc/stdenv/passthru.chapter.md
@@ -18,7 +18,9 @@ Its value can be accessed as if it was set inside a derivation.
let
hello = stdenv.mkDerivation {
pname = "hello";
- src = fetchGit { /* ... */ };
+ src = fetchGit {
+ # ...
+ };
passthru = {
foo = "bar";
diff --git a/doc/stdenv/stdenv.chapter.md b/doc/stdenv/stdenv.chapter.md
index 80059a78733c..6851294ddf96 100644
--- a/doc/stdenv/stdenv.chapter.md
+++ b/doc/stdenv/stdenv.chapter.md
@@ -20,14 +20,14 @@ stdenv.mkDerivation {
**Since [RFC 0035](https://github.com/NixOS/rfcs/pull/35), this is preferred for packages in Nixpkgs**, as it allows us to reuse the version easily:
```nix
-stdenv.mkDerivation rec {
+stdenv.mkDerivation (finalAttrs: {
pname = "libfoo";
version = "1.2.3";
src = fetchurl {
- url = "http://example.org/libfoo-source-${version}.tar.bz2";
+ url = "http://example.org/libfoo-source-${finalAttrs.version}.tar.bz2";
hash = "sha256-tWxU/LANbQE32my+9AXyt3nCT7NBVfJ45CX757EMT3Q=";
};
-}
+})
```
Many packages have dependencies that are not provided in the standard environment. It’s usually sufficient to specify those dependencies in the `buildInputs` attribute:
@@ -37,7 +37,11 @@ stdenv.mkDerivation {
pname = "libfoo";
version = "1.2.3";
# ...
- buildInputs = [libbar perl ncurses];
+ buildInputs = [
+ libbar
+ perl
+ ncurses
+ ];
}
```
@@ -49,13 +53,24 @@ Often it is necessary to override or modify some aspect of the build. To make th
stdenv.mkDerivation {
pname = "fnord";
version = "4.5";
+
# ...
+
buildPhase = ''
+ runHook preBuild
+
gcc foo.c -o foo
+
+ runHook postBuild
'';
+
installPhase = ''
+ runHook preInstall
+
mkdir -p $out/bin
cp foo $out/bin
+
+ runHook postInstall
'';
}
```
@@ -208,16 +223,20 @@ These dependencies are only injected when [`doCheck`](#var-stdenv-doCheck) is se
Consider for example this simplified derivation for `solo5`, a sandboxing tool:
```nix
-stdenv.mkDerivation rec {
+stdenv.mkDerivation (finalAttrs: {
pname = "solo5";
version = "0.7.5";
src = fetchurl {
- url = "https://github.com/Solo5/solo5/releases/download/v${version}/solo5-v${version}.tar.gz";
+ url = "https://github.com/Solo5/solo5/releases/download/v${finalAttrs.version}/solo5-v${finalAttrs.version}.tar.gz";
hash = "sha256-viwrS9lnaU8sTGuzK/+L/PlMM/xRRtgVuK5pixVeDEw=";
};
- nativeBuildInputs = [ makeWrapper pkg-config ];
+ nativeBuildInputs = [
+ makeWrapper
+ pkg-config
+ ];
+
buildInputs = [ libseccomp ];
postInstall = ''
@@ -227,13 +246,23 @@ stdenv.mkDerivation rec {
--replace-fail "cp " "cp --no-preserve=mode "
wrapProgram $out/bin/solo5-virtio-mkimage \
- --prefix PATH : ${lib.makeBinPath [ dosfstools mtools parted syslinux ]}
+ --prefix PATH : ${
+ lib.makeBinPath [
+ dosfstools
+ mtools
+ parted
+ syslinux
+ ]
+ }
'';
doCheck = true;
- nativeCheckInputs = [ util-linux qemu ];
- checkPhase = '' [elided] '';
-}
+ nativeCheckInputs = [
+ util-linux
+ qemu
+ ];
+ checkPhase = ''[elided]'';
+})
```
- `makeWrapper` is a setup hook, i.e., a shell script sourced by the generic builder of `stdenv`.
@@ -272,7 +301,7 @@ This can lead to conflicting dependencies that cannot easily be resolved.
# A propagated dependency
```nix
-with import {};
+with import { };
let
bar = stdenv.mkDerivation {
name = "bar";
@@ -442,8 +471,7 @@ If you pass a function to `mkDerivation`, it will receive as its argument the fi
mkDerivation (finalAttrs: {
pname = "hello";
withFeature = true;
- configureFlags =
- lib.optionals finalAttrs.withFeature ["--with-feature"];
+ configureFlags = lib.optionals finalAttrs.withFeature [ "--with-feature" ];
})
```
@@ -460,28 +488,32 @@ various bindings:
```nix
# `pkg` is the _original_ definition (for illustration purposes)
-let pkg =
- mkDerivation (finalAttrs: {
+let
+ pkg = mkDerivation (finalAttrs: {
# ...
# An example attribute
- packages = [];
+ packages = [ ];
# `passthru.tests` is a commonly defined attribute.
passthru.tests.simple = f finalAttrs.finalPackage;
# An example of an attribute containing a function
- passthru.appendPackages = packages':
- finalAttrs.finalPackage.overrideAttrs (newSelf: super: {
- packages = super.packages ++ packages';
- });
+ passthru.appendPackages =
+ packages':
+ finalAttrs.finalPackage.overrideAttrs (
+ newSelf: super: {
+ packages = super.packages ++ packages';
+ }
+ );
# For illustration purposes; referenced as
# `(pkg.overrideAttrs(x)).finalAttrs` etc in the text below.
passthru.finalAttrs = finalAttrs;
passthru.original = pkg;
});
-in pkg
+in
+pkg
```
Unlike the `pkg` binding in the above example, the `finalAttrs` parameter always references the final attributes. For instance `(pkg.overrideAttrs(x)).finalAttrs.finalPackage` is identical to `pkg.overrideAttrs(x)`, whereas `(pkg.overrideAttrs(x)).original` is the same as the original `pkg`.
@@ -955,7 +987,7 @@ To make GDB find debug information for the `socat` package and its dependencies,
```nix
let
pkgs = import ./. {
- config = {};
+ config = { };
overlays = [
(final: prev: {
ncurses = prev.ncurses.overrideAttrs { separateDebugInfo = true; };
@@ -974,19 +1006,19 @@ let
];
};
in
- pkgs.mkShell {
+pkgs.mkShell {
- NIX_DEBUG_INFO_DIRS = "${pkgs.lib.getLib myDebugInfoDirs}/lib/debug";
+ NIX_DEBUG_INFO_DIRS = "${pkgs.lib.getLib myDebugInfoDirs}/lib/debug";
- packages = [
- pkgs.gdb
- pkgs.socat
- ];
+ packages = [
+ pkgs.gdb
+ pkgs.socat
+ ];
- shellHook = ''
- ${pkgs.lib.getBin pkgs.gdb}/bin/gdb ${pkgs.lib.getBin pkgs.socat}/bin/socat
- '';
- }
+ shellHook = ''
+ ${pkgs.lib.getBin pkgs.gdb}/bin/gdb ${pkgs.lib.getBin pkgs.socat}/bin/socat
+ '';
+}
```
This setup works as follows:
@@ -1109,12 +1141,15 @@ They cannot be overridden without rebuilding the package.
If dependencies should be resolved at runtime, use `--suffix` to append fallback values to `PATH`.
-There’s many more kinds of arguments, they are documented in `nixpkgs/pkgs/build-support/setup-hooks/make-wrapper.sh` for the `makeWrapper` implementation and in `nixpkgs/pkgs/build-support/setup-hooks/make-binary-wrapper/make-binary-wrapper.sh` for the `makeBinaryWrapper` implementation.
+There’s many more kinds of arguments, they are documented in `nixpkgs/pkgs/build-support/setup-hooks/make-wrapper.sh` for the `makeWrapper` implementation and in `nixpkgs/pkgs/by-name/ma/makeBinaryWrapper/make-binary-wrapper.sh` for the `makeBinaryWrapper` implementation.
`wrapProgram` is a convenience function you probably want to use most of the time, implemented by both `makeWrapper` and `makeBinaryWrapper`.
Using the `makeBinaryWrapper` implementation is usually preferred, as it creates a tiny _compiled_ wrapper executable, that can be used as a shebang interpreter. This is needed mostly on Darwin, where shebangs cannot point to scripts, [due to a limitation with the `execve`-syscall](https://stackoverflow.com/questions/67100831/macos-shebang-with-absolute-path-not-working). Compiled wrappers generated by `makeBinaryWrapper` can be inspected with `less ` - by scrolling past the binary data you should be able to see the shell command that generated the executable and there see the environment variables that were injected into the wrapper.
+However, `makeWrapper` is more flexible and implements more arguments.
+Use `makeWrapper` if you need the wrapper to use shell features (e.g. look up environment variables) at runtime.
+
### `remove-references-to -t` \ [ `-t` \ ... ] \ ... {#fun-remove-references-to}
Removes the references of the specified files to the specified store files. This is done without changing the size of the file by replacing the hash by `eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee`, and should work on compiled executables. This is meant to be used to remove the dependency of the output on inputs that are known to be unnecessary at runtime. Of course, reckless usage will break the patched programs.
@@ -1568,6 +1603,10 @@ This flag adds the `-fstack-clash-protection` compiler option, which causes grow
The following flags are disabled by default and should be enabled with `hardeningEnable` for packages that take untrusted input like network services.
+#### `nostrictaliasing` {#nostrictaliasing}
+
+This flag adds the `-fno-strict-aliasing` compiler option, which prevents the compiler from assuming code has been written strictly following the standard in regards to pointer aliasing and therefore performing optimizations that may be unsafe for code that has not followed these rules.
+
#### `pie` {#pie}
This flag is disabled by default for normal `glibc` based NixOS package builds, but enabled by default for
@@ -1593,7 +1632,7 @@ This breaks some code that does advanced stack management or exception handling.
#### `trivialautovarinit` {#trivialautovarinit}
-Adds the `-ftrivial-auto-var-init=pattern` compiler option. This causes "trivially-initializable" uninitialized stack variables to be forcibly initialized with a nonzero value that is likely to cause a crash (and therefore be noticed). Uninitialized variables generally take on their values based on fragments of previous program state, and attackers can carefully manipulate that state to craft malicious initial values for these variables.
+Adds the `-ftrivial-auto-var-init=pattern` compiler option. Uninitialized variables generally take on their values based on fragments of previous program state, and attackers can carefully manipulate that state to craft malicious initial values for these variables. This flag causes "trivially-initializable" uninitialized stack variables to be forcibly initialized with a nonzero value that is likely to cause a crash (and therefore be noticed).
Use of this flag is controversial as it can prevent tools that detect uninitialized variable use (such as valgrind) from operating correctly.
diff --git a/doc/style.css b/doc/style.css
index f517733be972..4ba76cc39114 100644
--- a/doc/style.css
+++ b/doc/style.css
@@ -7,25 +7,29 @@ body {
margin: 0;
}
-.book {
+.book,
+.appendix {
margin: auto;
width: 100%;
}
@media screen and (min-width: 768px) {
- .book {
+ .book,
+ .appendix {
max-width: 46rem;
}
}
@media screen and (min-width: 992px) {
- .book {
+ .book,
+ .appendix {
max-width: 60rem;
}
}
@media screen and (min-width: 1200px) {
- .book {
+ .book,
+ .appendix {
max-width: 73rem;
}
}
@@ -113,10 +117,10 @@ html {
body {
font-size: 1rem;
- font-family: 'Roboto', sans-serif;
+ font-family: "Roboto", sans-serif;
font-weight: 300;
- color: #000000;
- background-color: #ffffff;
+ color: var(--main-text-color);
+ background-color: var(--background);
min-height: 100vh;
display: flex;
flex-direction: column;
@@ -132,7 +136,7 @@ body {
a {
text-decoration: none;
border-bottom: 1px solid;
- color: #405d99;
+ color: var(--link-color);
}
ul {
@@ -163,7 +167,7 @@ h1 {
line-height: 110%;
font-size: 200%;
margin-bottom: 1rem;
- color: #6586c8;
+ color: var(--heading-color);
}
h2 {
@@ -171,7 +175,7 @@ h2 {
line-height: 110%;
font-size: 170%;
margin-bottom: 0.625rem;
- color: #6586c8;
+ color: var(--heading-color);
}
h2:not(:first-child) {
@@ -183,7 +187,7 @@ h3 {
line-height: 110%;
margin-bottom: 1rem;
font-size: 150%;
- color: #6586c8;
+ color: var(--heading-color);
}
.note h3,
@@ -199,7 +203,7 @@ h4 {
line-height: 110%;
margin-bottom: 1rem;
font-size: 140%;
- color: #6586c8;
+ color: var(--heading-color);
}
h5 {
@@ -207,14 +211,14 @@ h5 {
line-height: 110%;
margin-bottom: 1rem;
font-size: 130%;
- color: #6a6a6a;
+ color: var(--small-heading-color);
}
h6 {
font-weight: 800;
line-height: 110%;
margin-bottom: 1rem;
- font-size: 120%
+ font-size: 120%;
}
strong {
@@ -226,13 +230,13 @@ p {
margin-bottom: 1rem;
}
-dt>*:first-child,
-dd>*:first-child {
+dt > *:first-child,
+dd > *:first-child {
margin-top: 0;
}
-dt>*:last-child,
-dd>*:last-child {
+dt > *:last-child,
+dd > *:last-child {
margin-bottom: 0;
}
@@ -256,8 +260,8 @@ div.appendix .programlisting {
border-radius: 0.5rem;
padding: 1rem;
overflow: auto;
- background: #f2f8fd;
- color: #000000;
+ background: var(--codeblock-background);
+ color: var(--codeblock-text-color);
}
div.book .note,
@@ -277,47 +281,46 @@ div.appendix .important {
background: #f4f4f4;
}
-div.book .note>.title,
-div.book .tip>.title,
-div.book .warning>.title,
-div.book .caution>.title,
-div.book .important>.title,
-div.appendix .note>.title,
-div.appendix .tip>.title,
-div.appendix .warning>.title,
-div.appendix .caution>.title,
-div.appendix .important>.title {
+div.book .note > .title,
+div.book .tip > .title,
+div.book .warning > .title,
+div.book .caution > .title,
+div.book .important > .title,
+div.appendix .note > .title,
+div.appendix .tip > .title,
+div.appendix .warning > .title,
+div.appendix .caution > .title,
+div.appendix .important > .title {
font-weight: 800;
- /* font-family: 'Overpass', serif; */
line-height: 110%;
margin-bottom: 1rem;
color: inherit;
margin-bottom: 0;
}
-div.book .note> :first-child,
-div.book .tip> :first-child,
-div.book .warning> :first-child,
-div.book .caution> :first-child,
-div.book .important> :first-child,
-div.appendix .note> :first-child,
-div.appendix .tip> :first-child,
-div.appendix .warning> :first-child,
-div.appendix .caution> :first-child,
-div.appendix .important> :first-child {
+div.book .note > :first-child,
+div.book .tip > :first-child,
+div.book .warning > :first-child,
+div.book .caution > :first-child,
+div.book .important > :first-child,
+div.appendix .note > :first-child,
+div.appendix .tip > :first-child,
+div.appendix .warning > :first-child,
+div.appendix .caution > :first-child,
+div.appendix .important > :first-child {
margin-top: 0;
}
-div.book .note> :last-child,
-div.book .tip> :last-child,
-div.book .warning> :last-child,
-div.book .caution> :last-child,
-div.book .important> :last-child,
-div.appendix .note> :last-child,
-div.appendix .tip> :last-child,
-div.appendix .warning> :last-child,
-div.appendix .caution> :last-child,
-div.appendix .important> :last-child {
+div.book .note > :last-child,
+div.book .tip > :last-child,
+div.book .warning > :last-child,
+div.book .caution > :last-child,
+div.book .important > :last-child,
+div.appendix .note > :last-child,
+div.appendix .tip > :last-child,
+div.appendix .warning > :last-child,
+div.appendix .caution > :last-child,
+div.appendix .important > :last-child {
margin-bottom: 0;
}
@@ -325,16 +328,16 @@ div.book .note,
div.book .tip,
div.appendix .note,
div.appendix .tip {
- color: #5277c3;
- background: #f2f8fd;
+ color: var(--note-text-color);
+ background: var(--note-background);
}
div.book .warning,
div.book .caution,
div.appendix .warning,
div.appendix .caution {
- color: #cc3900;
- background-color: #fff5e1;
+ color: var(--warning-text-color);
+ background-color: var(--warning-background);
}
div.book .section,
@@ -358,8 +361,8 @@ div.appendix div.example details[open] {
border-radius: 4px;
}
-div.book div.example details>summary,
-div.appendix div.example details>summary {
+div.book div.example details > summary,
+div.appendix div.example details > summary {
cursor: pointer;
}
@@ -368,13 +371,13 @@ div.appendix br.example-break {
display: none;
}
-div.book div.footnotes>hr,
-div.appendix div.footnotes>hr {
+div.book div.footnotes > hr,
+div.appendix div.footnotes > hr {
border-color: #d8d8d8;
}
-div.book div.footnotes>br,
-div.appendix div.footnotes>br {
+div.book div.footnotes > br,
+div.appendix div.footnotes > br {
display: none;
}
@@ -444,3 +447,47 @@ div.appendix .variablelist .term {
user-select: none;
-webkit-user-select: none;
}
+
+:root {
+ --background: #fff;
+ --main-text-color: #000;
+ --link-color: #405d99;
+ --heading-color: #6586c8;
+ --small-heading-color: #6a6a6a;
+ --note-text-color: #5277c3;
+ --note-background: #f2f8fd;
+ --warning-text-color: #cc3900;
+ --warning-background: #fff5e1;
+ --codeblock-background: #f2f8fd;
+ --codeblock-text-color: #000;
+}
+
+@media (prefers-color-scheme: dark) {
+ :root {
+ --background: #242424;
+ --main-text-color: #fff;
+ --link-color: #6586c8;
+ --small-heading-color: #fff;
+ --note-background: none;
+ --warning-background: none;
+ --codeblock-background: #393939;
+ --codeblock-text-color: #fff;
+ }
+
+ div.book .note,
+ div.book .tip,
+ div.appendix .note,
+ div.appendix .tip,
+ div.book .warning,
+ div.book .caution,
+ div.appendix .warning,
+ div.appendix .caution {
+ border: 2px solid;
+ font-weight: 400;
+ }
+}
+
+@font-face {
+ font-family: Roboto;
+ src: url(Roboto.ttf);
+}
diff --git a/doc/tests/check-nix-code-blocks.nix b/doc/tests/check-nix-code-blocks.nix
new file mode 100644
index 000000000000..5ec934f6bba8
--- /dev/null
+++ b/doc/tests/check-nix-code-blocks.nix
@@ -0,0 +1,32 @@
+{
+ runCommand,
+ markdown-code-runner,
+ nixfmt-rfc-style,
+}:
+
+runCommand "manual_check-nix-code-blocks"
+ {
+ nativeBuildInputs = [
+ markdown-code-runner
+ nixfmt-rfc-style
+ ];
+ }
+ ''
+ set +e
+
+ mdcr --check --config ${./mdcr-config.toml} ${./..}
+
+ if [ $? -ne 0 ]; then
+ cat < {
+ localSystem = {
+ system = "x86_64-linux";
+ };
+ crossSystem = {
+ useLLVM = true;
+ linker = "lld";
+ };
+}
+```
+
+Note that we set `linker` to `lld`. This is because LLVM has its own linker called "lld". By setting it, we utilize Clang and lld within this new instance of Nixpkgs. There is a shorthand method for building everything with LLVM: `pkgsLLVM`. This is easier to use with `nix-build` (or `nix build`):
+
+```bash
+nix-build -A pkgsLLVM.hello
+```
+
+This will compile the GNU hello package with LLVM and the lld linker like previously mentioned.
+
+#### Using `clangStdenv` {#sec-building-packages-with-llvm-using-clang-stdenv}
+
+Another simple way is to override the stdenv with `clangStdenv`. This causes a single package to be built with Clang. However, this `stdenv` does not override platform defaults to use compiler-rt, libc++, and libunwind. This is the preferred way to make a single package in Nixpkgs build with Clang. There are cases where just Clang isn't enough. For these situations, there is `libcxxStdenv`, which uses Clang with libc++ and compiler-rt.
diff --git a/doc/using/configuration.chapter.md b/doc/using/configuration.chapter.md
index cea9532d6a7b..3467021715e6 100644
--- a/doc/using/configuration.chapter.md
+++ b/doc/using/configuration.chapter.md
@@ -99,10 +99,12 @@ There are several ways to tweak how Nix handles a package which has been marked
```nix
{
- allowUnfreePredicate = pkg: builtins.elem (lib.getName pkg) [
- "roon-server"
- "vscode"
- ];
+ allowUnfreePredicate =
+ pkg:
+ builtins.elem (lib.getName pkg) [
+ "roon-server"
+ "vscode"
+ ];
}
```
@@ -112,7 +114,10 @@ There are several ways to tweak how Nix handles a package which has been marked
```nix
{
- allowlistedLicenses = with lib.licenses; [ amd wtfpl ];
+ allowlistedLicenses = with lib.licenses; [
+ amd
+ wtfpl
+ ];
}
```
@@ -120,7 +125,10 @@ There are several ways to tweak how Nix handles a package which has been marked
```nix
{
- blocklistedLicenses = with lib.licenses; [ agpl3Only gpl3Only ];
+ blocklistedLicenses = with lib.licenses; [
+ agpl3Only
+ gpl3Only
+ ];
}
```
@@ -158,9 +166,11 @@ There are several ways to tweak how Nix handles a package which has been marked
```nix
{
- allowInsecurePredicate = pkg: builtins.elem (lib.getName pkg) [
- "ovftool"
- ];
+ allowInsecurePredicate =
+ pkg:
+ builtins.elem (lib.getName pkg) [
+ "ovftool"
+ ];
}
```
@@ -173,7 +183,9 @@ You can define a function called `packageOverrides` in your local `~/.config/nix
```nix
{
packageOverrides = pkgs: rec {
- foo = pkgs.foo.override { /* ... */ };
+ foo = pkgs.foo.override {
+ # ...
+ };
};
}
```
@@ -197,23 +209,24 @@ Using `packageOverrides`, it is possible to manage packages declaratively. This
```nix
{
- packageOverrides = pkgs: with pkgs; {
- myPackages = pkgs.buildEnv {
- name = "my-packages";
- paths = [
- aspell
- bc
- coreutils
- gdb
- ffmpeg
- nix
- emscripten
- jq
- nox
- silver-searcher
- ];
+ packageOverrides =
+ pkgs: with pkgs; {
+ myPackages = pkgs.buildEnv {
+ name = "my-packages";
+ paths = [
+ aspell
+ bc
+ coreutils
+ gdb
+ ffmpeg
+ nix
+ emscripten
+ jq
+ nox
+ silver-searcher
+ ];
+ };
};
- };
}
```
@@ -221,24 +234,28 @@ To install it into our environment, you can just run `nix-env -iA nixpkgs.myPack
```nix
{
- packageOverrides = pkgs: with pkgs; {
- myPackages = pkgs.buildEnv {
- name = "my-packages";
- paths = [
- aspell
- bc
- coreutils
- gdb
- ffmpeg
- nix
- emscripten
- jq
- nox
- silver-searcher
- ];
- pathsToLink = [ "/share" "/bin" ];
+ packageOverrides =
+ pkgs: with pkgs; {
+ myPackages = pkgs.buildEnv {
+ name = "my-packages";
+ paths = [
+ aspell
+ bc
+ coreutils
+ gdb
+ ffmpeg
+ nix
+ emscripten
+ jq
+ nox
+ silver-searcher
+ ];
+ pathsToLink = [
+ "/share"
+ "/bin"
+ ];
+ };
};
- };
}
```
@@ -250,24 +267,32 @@ After building that new environment, look through `~/.nix-profile` to make sure
```nix
{
- packageOverrides = pkgs: with pkgs; {
- myPackages = pkgs.buildEnv {
- name = "my-packages";
- paths = [
- aspell
- bc
- coreutils
- ffmpeg
- nix
- emscripten
- jq
- nox
- silver-searcher
- ];
- pathsToLink = [ "/share/man" "/share/doc" "/bin" ];
- extraOutputsToInstall = [ "man" "doc" ];
+ packageOverrides =
+ pkgs: with pkgs; {
+ myPackages = pkgs.buildEnv {
+ name = "my-packages";
+ paths = [
+ aspell
+ bc
+ coreutils
+ ffmpeg
+ nix
+ emscripten
+ jq
+ nox
+ silver-searcher
+ ];
+ pathsToLink = [
+ "/share/man"
+ "/share/doc"
+ "/bin"
+ ];
+ extraOutputsToInstall = [
+ "man"
+ "doc"
+ ];
+ };
};
- };
}
```
@@ -275,15 +300,15 @@ This provides us with some useful documentation for using our packages. However
```nix
{
- packageOverrides = pkgs: with pkgs; rec {
- myProfile = writeText "my-profile" ''
+ packageOverrides = pkgs: {
+ myProfile = pkgs.writeText "my-profile" ''
export PATH=$HOME/.nix-profile/bin:/nix/var/nix/profiles/default/bin:/sbin:/bin:/usr/sbin:/usr/bin
export MANPATH=$HOME/.nix-profile/share/man:/nix/var/nix/profiles/default/share/man:/usr/share/man
'';
myPackages = pkgs.buildEnv {
name = "my-packages";
- paths = [
- (runCommand "profile" {} ''
+ paths = with pkgs; [
+ (runCommand "profile" { } ''
mkdir -p $out/etc/profile.d
cp ${myProfile} $out/etc/profile.d/my-profile.sh
'')
@@ -298,8 +323,16 @@ This provides us with some useful documentation for using our packages. However
nox
silver-searcher
];
- pathsToLink = [ "/share/man" "/share/doc" "/bin" "/etc" ];
- extraOutputsToInstall = [ "man" "doc" ];
+ pathsToLink = [
+ "/share/man"
+ "/share/doc"
+ "/bin"
+ "/etc"
+ ];
+ extraOutputsToInstall = [
+ "man"
+ "doc"
+ ];
};
};
}
@@ -326,16 +359,16 @@ Configuring GNU info is a little bit trickier than man pages. To work correctly,
```nix
{
- packageOverrides = pkgs: with pkgs; rec {
- myProfile = writeText "my-profile" ''
+ packageOverrides = pkgs: {
+ myProfile = pkgs.writeText "my-profile" ''
export PATH=$HOME/.nix-profile/bin:/nix/var/nix/profiles/default/bin:/sbin:/bin:/usr/sbin:/usr/bin
export MANPATH=$HOME/.nix-profile/share/man:/nix/var/nix/profiles/default/share/man:/usr/share/man
export INFOPATH=$HOME/.nix-profile/share/info:/nix/var/nix/profiles/default/share/info:/usr/share/info
'';
myPackages = pkgs.buildEnv {
name = "my-packages";
- paths = [
- (runCommand "profile" {} ''
+ paths = with pkgs; [
+ (runCommand "profile" { } ''
mkdir -p $out/etc/profile.d
cp ${myProfile} $out/etc/profile.d/my-profile.sh
'')
@@ -351,8 +384,18 @@ Configuring GNU info is a little bit trickier than man pages. To work correctly,
silver-searcher
texinfoInteractive
];
- pathsToLink = [ "/share/man" "/share/doc" "/share/info" "/bin" "/etc" ];
- extraOutputsToInstall = [ "man" "doc" "info" ];
+ pathsToLink = [
+ "/share/man"
+ "/share/doc"
+ "/share/info"
+ "/bin"
+ "/etc"
+ ];
+ extraOutputsToInstall = [
+ "man"
+ "doc"
+ "info"
+ ];
postBuild = ''
if [ -x $out/bin/install-info -a -w $out/share/info ]; then
shopt -s nullglob
diff --git a/doc/using/overlays.chapter.md b/doc/using/overlays.chapter.md
index 46200730f0b2..e1803786a9a7 100644
--- a/doc/using/overlays.chapter.md
+++ b/doc/using/overlays.chapter.md
@@ -136,7 +136,12 @@ self: super:
For BLAS/LAPACK switching to work correctly, all packages must depend on `blas` or `lapack`. This ensures that only one BLAS/LAPACK library is used at one time. There are two versions of BLAS/LAPACK currently in the wild, `LP64` (integer size = 32 bits) and `ILP64` (integer size = 64 bits). The attributes `blas` and `lapack` are `LP64` by default. Their `ILP64` version are provided through the attributes `blas-ilp64` and `lapack-ilp64`. Some software needs special flags or patches to work with `ILP64`. You can check if `ILP64` is used in Nixpkgs with `blas.isILP64` and `lapack.isILP64`. Some software does NOT work with `ILP64`, and derivations need to specify an assertion to prevent this. You can prevent `ILP64` from being used with the following:
```nix
-{ stdenv, blas, lapack, ... }:
+{
+ stdenv,
+ blas,
+ lapack,
+ ...
+}:
assert (!blas.isILP64) && (!lapack.isILP64);
diff --git a/doc/using/overrides.chapter.md b/doc/using/overrides.chapter.md
index 27a042963dd6..95190b44a500 100644
--- a/doc/using/overrides.chapter.md
+++ b/doc/using/overrides.chapter.md
@@ -13,27 +13,38 @@ It is used to override the arguments passed to a function.
Example usages:
```nix
-pkgs.foo.override { arg1 = val1; arg2 = val2; /* ... */ }
+pkgs.foo.override {
+ arg1 = val1;
+ arg2 = val2; # ...
+}
```
It's also possible to access the previous arguments.
```nix
-pkgs.foo.override (previous: { arg1 = previous.arg1; /* ... */ })
+pkgs.foo.override (previous: {
+ arg1 = previous.arg1; # ...
+})
```
```nix
-import pkgs.path { overlays = [ (self: super: {
- foo = super.foo.override { barSupport = true ; };
- })];}
+import pkgs.path {
+ overlays = [
+ (self: super: {
+ foo = super.foo.override { barSupport = true; };
+ })
+ ];
+}
```
```nix
{
mypkg = pkgs.callPackage ./mypkg.nix {
- mydep = pkgs.mydep.override { /* ... */ };
+ mydep = pkgs.mydep.override {
+ # ...
+ };
};
}
```
@@ -55,9 +66,11 @@ Example usages:
```nix
{
- helloBar = pkgs.hello.overrideAttrs (finalAttrs: previousAttrs: {
- pname = previousAttrs.pname + "-bar";
- });
+ helloBar = pkgs.hello.overrideAttrs (
+ finalAttrs: previousAttrs: {
+ pname = previousAttrs.pname + "-bar";
+ }
+ );
}
```
@@ -107,7 +120,7 @@ Example usage:
url = "ftp://alpha.gnu.org/gnu/sed/sed-4.2.2-pre.tar.bz2";
hash = "sha256-MxBJRcM2rYzQYwJ5XKxhXTQByvSg5jZc5cSHEZoB2IY=";
};
- patches = [];
+ patches = [ ];
});
}
```
@@ -128,8 +141,15 @@ Example usage:
```nix
{
- f = { a, b }: { result = a+b; };
- c = lib.makeOverridable f { a = 1; b = 2; };
+ f =
+ { a, b }:
+ {
+ result = a + b;
+ };
+ c = lib.makeOverridable f {
+ a = 1;
+ b = 2;
+ };
}
```
diff --git a/flake.nix b/flake.nix
index a82bb383c062..3bd0a48a5cce 100644
--- a/flake.nix
+++ b/flake.nix
@@ -98,15 +98,26 @@
checks = forAllSystems (
system:
- {
- tarball = jobs.${system}.tarball;
- }
+ { }
+ //
+ lib.optionalAttrs
+ (
+ # Exclude x86_64-freebsd because "Failed to evaluate rustc-wrapper-1.85.0: «broken»: is marked as broken"
+ system != "x86_64-freebsd"
+ )
+ {
+ tarball = jobs.${system}.tarball;
+ }
//
lib.optionalAttrs
(
self.legacyPackages.${system}.stdenv.hostPlatform.isLinux
# Exclude power64 due to "libressl is not available on the requested hostPlatform" with hostPlatform being power64
&& !self.legacyPackages.${system}.targetPlatform.isPower64
+ # Exclude armv6l-linux because "cannot bootstrap GHC on this platform ('armv6l-linux' with libc 'defaultLibc')"
+ && system != "armv6l-linux"
+ # Exclude riscv64-linux because "cannot bootstrap GHC on this platform ('riscv64-linux' with libc 'defaultLibc')"
+ && system != "riscv64-linux"
)
{
# Test that ensures that the nixosSystem function can accept a lib argument
@@ -156,8 +167,8 @@
system != "armv6l-linux"
# Exclude riscv64-linux because "Package ‘ghc-9.6.6’ in .../pkgs/development/compilers/ghc/common-hadrian.nix:579 is not available on the requested hostPlatform"
&& system != "riscv64-linux"
- # Exclude FreeBSD because "Package ‘ghc-9.6.6’ in .../pkgs/development/compilers/ghc/common-hadrian.nix:579 is not available on the requested hostPlatform"
- && !self.legacyPackages.${system}.stdenv.hostPlatform.isFreeBSD
+ # Exclude x86_64-freebsd because "Package ‘ghc-9.6.6’ in .../pkgs/development/compilers/ghc/common-hadrian.nix:579 is not available on the requested hostPlatform"
+ && system != "x86_64-freebsd"
)
{
/**
@@ -167,7 +178,15 @@
}
);
- formatter = forAllSystems (system: (import ./ci { inherit system; }).fmt.pkg);
+ formatter = lib.filterAttrs (
+ system: _:
+ # Exclude armv6l-linux because "cannot bootstrap GHC on this platform ('armv6l-linux' with libc 'defaultLibc')"
+ system != "armv6l-linux"
+ # Exclude riscv64-linux because "cannot bootstrap GHC on this platform ('riscv64-linux' with libc 'defaultLibc')"
+ && system != "riscv64-linux"
+ # Exclude x86_64-freebsd because "Package ‘go-1.22.12-freebsd-amd64-bootstrap’ in /nix/store/0yw40qnrar3lvc5hax5n49abl57apjbn-source/pkgs/development/compilers/go/binary.nix:50 is not available on the requested hostPlatform"
+ && system != "x86_64-freebsd"
+ ) (forAllSystems (system: (import ./ci { inherit system; }).fmt.pkg));
/**
A nested structure of [packages](https://nix.dev/manual/nix/latest/glossary#package-attribute-set) and other values.
diff --git a/lib/.version b/lib/.version
index 5d540763cfb4..115ab7a6a9ae 100644
--- a/lib/.version
+++ b/lib/.version
@@ -1 +1 @@
-25.05
\ No newline at end of file
+25.11
\ No newline at end of file
diff --git a/lib/asserts.nix b/lib/asserts.nix
index b22252a7654b..41908d00a812 100644
--- a/lib/asserts.nix
+++ b/lib/asserts.nix
@@ -1,5 +1,16 @@
{ lib }:
+let
+ inherit (lib.strings)
+ concatStringsSep
+ ;
+ inherit (lib.lists)
+ filter
+ ;
+ inherit (lib.trivial)
+ showWarnings
+ ;
+in
rec {
/**
@@ -131,4 +142,61 @@ rec {
"each element in ${name} must be one of ${lib.generators.toPretty { } xs}, but is: ${
lib.generators.toPretty { } vals
}";
+
+ /**
+ Wrap a value with logic that throws an error when assertions
+ fail and emits any warnings.
+
+ # Inputs
+
+ `assertions`
+
+ : A list of assertions. If any of their `assertion` attrs is `false`, their `message` attrs will be emitted in a `throw`.
+
+ `warnings`
+
+ : A list of strings to emit as warnings. This function does no filtering on this list.
+
+ `val`
+
+ : A value to return, wrapped in `warn`, if a `throw` is not necessary.
+
+ # Type
+
+ ```
+ checkAssertWarn :: [ { assertion :: Bool; message :: String } ] -> [ String ] -> Any -> Any
+ ```
+
+ # Examples
+ :::{.example}
+ ## `lib.asserts.checkAssertWarn` usage example
+ ```nix
+ checkAssertWarn
+ [ { assertion = false; message = "Will fail"; } ]
+ [ ]
+ null
+ stderr> error:
+ stderr> Failed assertions:
+ stderr> - Will fail
+
+ checkAssertWarn
+ [ { assertion = true; message = "Will not fail"; } ]
+ [ "Will warn" ]
+ null
+ stderr> evaluation warning: Will warn
+ null
+ ```
+
+ :::
+ */
+ checkAssertWarn =
+ assertions: warnings: val:
+ let
+ failedAssertions = map (x: x.message) (filter (x: !x.assertion) assertions);
+ in
+ if failedAssertions != [ ] then
+ throw "\nFailed assertions:\n${concatStringsSep "\n" (map (x: "- ${x}") failedAssertions)}"
+ else
+ showWarnings warnings val;
+
}
diff --git a/lib/attrsets.nix b/lib/attrsets.nix
index 8482887023f7..fc6bf84415b8 100644
--- a/lib/attrsets.nix
+++ b/lib/attrsets.nix
@@ -301,9 +301,9 @@ rec {
Nix has an [attribute selection operator](https://nixos.org/manual/nix/stable/language/operators#attribute-selection) which is sufficient for such queries, as long as the number of attributes is static. For example:
```nix
- x.a.b == getAttrByPath ["a" "b"] x
+ x.a.b == getAttrFromPath ["a" "b"] x
# and
- x.${f p}."example.com" == getAttrByPath [ (f p) "example.com" ] x
+ x.${f p}."example.com" == getAttrFromPath [ (f p) "example.com" ] x
```
# Inputs
@@ -1042,7 +1042,7 @@ rec {
:::
*/
- mapAttrs' = f: set: listToAttrs (map (attr: f attr set.${attr}) (attrNames set));
+ mapAttrs' = f: set: listToAttrs (mapAttrsToList f set);
/**
Call a function for each attribute in the given set and return
@@ -1076,7 +1076,7 @@ rec {
:::
*/
- mapAttrsToList = f: attrs: map (name: f name attrs.${name}) (attrNames attrs);
+ mapAttrsToList = f: attrs: attrValues (mapAttrs f attrs);
/**
Deconstruct an attrset to a list of name-value pairs as expected by [`builtins.listToAttrs`](https://nixos.org/manual/nix/stable/language/builtins.html#builtins-listToAttrs).
diff --git a/lib/default.nix b/lib/default.nix
index e671fcf9546d..63e02882e2c3 100644
--- a/lib/default.nix
+++ b/lib/default.nix
@@ -279,6 +279,7 @@ let
naturalSort
compareLists
take
+ takeEnd
drop
dropEnd
sublist
@@ -344,9 +345,11 @@ let
upperChars
toLower
toUpper
+ toCamelCase
toSentenceCase
addContextFrom
splitString
+ splitStringBy
removePrefix
removeSuffix
versionOlder
@@ -446,6 +449,7 @@ let
fixupOptionType
mkIf
mkAssert
+ mkDefinition
mkMerge
mkOverride
mkOptionDefault
diff --git a/lib/licenses.nix b/lib/licenses.nix
index c202d8d07180..38fb12d2a887 100644
--- a/lib/licenses.nix
+++ b/lib/licenses.nix
@@ -257,6 +257,11 @@ lib.mapAttrs mkLicense (
fullName = "BSD Protection License";
};
+ bsdSourceCode = {
+ spdxId = "BSD-Source-Code";
+ fullName = "BSD Source Code Attribution";
+ };
+
bsl11 = {
spdxId = "BUSL-1.1";
fullName = "Business Source License 1.1";
@@ -452,6 +457,11 @@ lib.mapAttrs mkLicense (
fullName = "Common Public License 1.0";
};
+ cronyx = {
+ spdxId = "Cronyx";
+ fullName = "Cronyx License";
+ };
+
curl = {
spdxId = "curl";
fullName = "curl License";
@@ -641,6 +651,11 @@ lib.mapAttrs mkLicense (
url = "https://fedoraproject.org/wiki/Licensing/GPL_Classpath_Exception";
};
+ gpl2UBDLPlus = {
+ fullName = "GNU General Public License v3.0 or later (with UBDL exception)";
+ url = "https://spdx.org/licenses/UBDL-exception.html";
+ };
+
gpl2Oss = {
fullName = "GNU General Public License version 2 only (with OSI approved licenses linking exception)";
url = "https://www.mysql.com/about/legal/licensing/foss-exception";
@@ -937,6 +952,11 @@ lib.mapAttrs mkLicense (
fullName = "MIT No Attribution";
};
+ mitOpenGroup = {
+ spdxId = "MIT-open-group";
+ fullName = "MIT Open Group variant";
+ };
+
mpl10 = {
spdxId = "MPL-1.0";
fullName = "Mozilla Public License 1.0";
@@ -1164,6 +1184,13 @@ lib.mapAttrs mkLicense (
fullName = "Sendmail License";
};
+ sfl = {
+ fullName = "Source First License 1.1";
+ url = "https://gitlab.futo.org/videostreaming/grayjay/-/blob/master/LICENSE.md";
+ free = false;
+ redistributable = true;
+ };
+
sgi-b-20 = {
spdxId = "SGI-B-2.0";
fullName = "SGI Free Software License B v2.0";
@@ -1321,6 +1348,11 @@ lib.mapAttrs mkLicense (
fullName = "Unicode License Agreement - Data Files and Software (2016)";
};
+ unicodeTOU = {
+ spdxId = "Unicode-TOU";
+ fullName = "Unicode Terms of Use";
+ };
+
unlicense = {
spdxId = "Unlicense";
fullName = "The Unlicense";
diff --git a/lib/lists.nix b/lib/lists.nix
index e119606dd5e7..ec0fe22d2afa 100644
--- a/lib/lists.nix
+++ b/lib/lists.nix
@@ -1462,6 +1462,40 @@ rec {
*/
take = count: sublist 0 count;
+ /**
+ Return the last (at most) N elements of a list.
+
+ # Inputs
+
+ `count`
+
+ : Maximum number of elements to pick
+
+ `list`
+
+ : Input list
+
+ # Type
+
+ ```
+ takeEnd :: int -> [a] -> [a]
+ ```
+
+ # Examples
+ :::{.example}
+ ## `lib.lists.takeEnd` usage example
+
+ ```nix
+ takeEnd 2 [ "a" "b" "c" "d" ]
+ => [ "c" "d" ]
+ takeEnd 2 [ ]
+ => [ ]
+ ```
+
+ :::
+ */
+ takeEnd = n: xs: drop (max 0 (length xs - n)) xs;
+
/**
Remove the first (at most) N elements of a list.
diff --git a/lib/meta.nix b/lib/meta.nix
index ee234d94489b..ad4f770806dd 100644
--- a/lib/meta.nix
+++ b/lib/meta.nix
@@ -289,7 +289,8 @@ rec {
*/
availableOn =
platform: pkg:
- ((!pkg ? meta.platforms) || any (platformMatch platform) pkg.meta.platforms)
+ pkg != null
+ && ((!pkg ? meta.platforms) || any (platformMatch platform) pkg.meta.platforms)
&& all (elem: !platformMatch platform elem) (pkg.meta.badPlatforms or [ ]);
/**
diff --git a/lib/modules.nix b/lib/modules.nix
index a9ddaf7bda02..d6061ec6d039 100644
--- a/lib/modules.nix
+++ b/lib/modules.nix
@@ -257,6 +257,7 @@ let
config
specialArgs
;
+ _class = class;
}
// specialArgs
);
@@ -1097,10 +1098,16 @@ let
# Process mkMerge and mkIf properties.
defs' = concatMap (
m:
- map (value: {
- inherit (m) file;
- inherit value;
- }) (addErrorContext "while evaluating definitions from `${m.file}':" (dischargeProperties m.value))
+ map (
+ value:
+ if value._type or null == "definition" then
+ value
+ else
+ {
+ inherit (m) file;
+ inherit value;
+ }
+ ) (addErrorContext "while evaluating definitions from `${m.file}':" (dischargeProperties m.value))
) defs;
# Process mkOverride properties.
@@ -1365,6 +1372,11 @@ let
inherit contents;
};
+ /**
+ Return a definition with file location information.
+ */
+ mkDefinition = args@{ file, value, ... }: args // { _type = "definition"; };
+
mkOverride = priority: content: {
_type = "override";
inherit priority content;
@@ -1869,7 +1881,7 @@ let
This function does not add support for deduplication and `disabledModules`,
although that could be achieved by wrapping the returned module and setting
- the `_key` module attribute.
+ the `key` module attribute.
The reason for this omission is that the file path is not guaranteed to be
a unique identifier for the module, as two instances of the module may
reference different `arg`s in their closures.
@@ -2095,6 +2107,7 @@ private
mkBefore
mkChangedOptionModule
mkDefault
+ mkDefinition
mkDerivedConfig
mkFixStrictness
mkForce
diff --git a/lib/options.nix b/lib/options.nix
index 85ce6ca77e92..007a14f15b67 100644
--- a/lib/options.nix
+++ b/lib/options.nix
@@ -30,6 +30,7 @@ let
inherit (lib.attrsets)
attrByPath
optionalAttrs
+ showAttrPath
;
inherit (lib.strings)
concatMapStrings
@@ -40,6 +41,7 @@ let
;
inherit (lib.lists)
last
+ toList
;
prioritySuggestion = ''
Use `lib.mkForce value` or `lib.mkDefault value` to change the priority on any of these definitions.
@@ -310,14 +312,14 @@ rec {
}:
let
name' = if isList name then last name else name;
- default' = if isList default then default else [ default ];
- defaultText = concatStringsSep "." default';
+ default' = toList default;
+ defaultText = showAttrPath default';
defaultValue = attrByPath default' (throw "${defaultText} cannot be found in ${pkgsText}") pkgs;
defaults =
if default != null then
{
default = defaultValue;
- defaultText = literalExpression ("${pkgsText}." + defaultText);
+ defaultText = literalExpression "${pkgsText}.${defaultText}";
}
else
optionalAttrs nullable {
@@ -333,7 +335,7 @@ rec {
}
// optionalAttrs (example != null) {
example = literalExpression (
- if isList example then "${pkgsText}." + concatStringsSep "." example else example
+ if isList example then "${pkgsText}.${showAttrPath example}" else example
);
}
);
diff --git a/lib/path/default.nix b/lib/path/default.nix
index be559eadf182..a03f6a04cafd 100644
--- a/lib/path/default.nix
+++ b/lib/path/default.nix
@@ -165,7 +165,7 @@ let
# This is a workaround for https://github.com/NixOS/nix/issues/12361 which
# was needed during the experimental phase of ca-derivations and should be
# removed once the issue has been resolved.
- || match "[0-9a-z]{52}" (head components) != null;
+ || components != [ ] && match "[0-9a-z]{52}" (head components) != null;
in
# No rec! Add dependencies on this file at the top.
@@ -382,7 +382,7 @@ in
(splitRoot p).root
(splitRoot p).subpath
- - Trying to get the parent directory of `root` using [`readDir`](https://nixos.org/manual/nix/stable/language/builtins.html#builtins-readDir) returns `root` itself:
+ - Trying to get the parent directory of `root` using [`dirOf`](https://nixos.org/manual/nix/stable/language/builtins.html#builtins-dirOf) returns `root` itself:
dirOf (splitRoot p).root == (splitRoot p).root
diff --git a/lib/path/tests/unit.nix b/lib/path/tests/unit.nix
index a52b4f44e51d..fa2e004e9c3a 100644
--- a/lib/path/tests/unit.nix
+++ b/lib/path/tests/unit.nix
@@ -110,6 +110,12 @@ let
expected = false;
};
+ # Root path (empty path components list)
+ testHasStorePathPrefixRoot = {
+ expr = hasStorePathPrefix /.;
+ expected = false;
+ };
+
testHasStorePathPrefixExample1 = {
expr = hasStorePathPrefix (storeDirPath + "/nvl9ic0pj1fpyln3zaqrf4cclbqdfn1j-foo/bar/baz");
expected = true;
diff --git a/lib/strings.nix b/lib/strings.nix
index d281120cad7f..ba8c47b69037 100644
--- a/lib/strings.nix
+++ b/lib/strings.nix
@@ -998,7 +998,11 @@ rec {
:::
*/
- escapeC = list: replaceStrings list (map (c: "\\x${toLower (lib.toHexString (charToInt c))}") list);
+ escapeC =
+ list:
+ replaceStrings list (
+ map (c: "\\x${fixedWidthString 2 "0" (toLower (lib.toHexString (charToInt c)))}") list
+ );
/**
Escape the `string` so it can be safely placed inside a URL
@@ -1496,6 +1500,63 @@ rec {
addContextFrom str (toUpper firstChar + toLower rest)
);
+ /**
+ Converts a string to camelCase. Handles snake_case, PascalCase,
+ kebab-case strings as well as strings delimited by spaces.
+
+ # Inputs
+
+ `string`
+ : The string to convert to camelCase
+
+ # Type
+
+ ```
+ toCamelCase :: string -> string
+ ```
+
+ # Examples
+ :::{.example}
+ ## `lib.strings.toCamelCase` usage example
+
+ ```nix
+ toCamelCase "hello-world"
+ => "helloWorld"
+ toCamelCase "hello_world"
+ => "helloWorld"
+ toCamelCase "hello world"
+ => "helloWorld"
+ toCamelCase "HelloWorld"
+ => "helloWorld"
+ ```
+
+ :::
+ */
+ toCamelCase =
+ str:
+ lib.throwIfNot (isString str) "toCamelCase does only accepts string values, but got ${typeOf str}" (
+ let
+ separators = splitStringBy (
+ prev: curr:
+ elem curr [
+ "-"
+ "_"
+ " "
+ ]
+ ) false str;
+
+ parts = lib.flatten (
+ map (splitStringBy (
+ prev: curr: match "[a-z]" prev != null && match "[A-Z]" curr != null
+ ) true) separators
+ );
+
+ first = if length parts > 0 then toLower (head parts) else "";
+ rest = if length parts > 1 then map toSentenceCase (tail parts) else [ ];
+ in
+ concatStrings (map (addContextFrom str) ([ first ] ++ rest))
+ );
+
/**
Appends string context from string like object `src` to `target`.
@@ -1588,6 +1649,97 @@ rec {
in
map (addContextFrom s) splits;
+ /**
+ Splits a string into substrings based on a predicate that examines adjacent characters.
+
+ This function provides a flexible way to split strings by checking pairs of characters
+ against a custom predicate function. Unlike simpler splitting functions, this allows
+ for context-aware splitting based on character transitions and patterns.
+
+ # Inputs
+
+ `predicate`
+ : Function that takes two arguments (previous character and current character)
+ and returns true when the string should be split at the current position.
+ For the first character, previous will be "" (empty string).
+
+ `keepSplit`
+ : Boolean that determines whether the splitting character should be kept as
+ part of the result. If true, the character will be included at the beginning
+ of the next substring; if false, it will be discarded.
+
+ `str`
+ : The input string to split.
+
+ # Return
+
+ A list of substrings from the original string, split according to the predicate.
+
+ # Type
+
+ ```
+ splitStringBy :: (string -> string -> bool) -> bool -> string -> [string]
+ ```
+
+ # Examples
+ :::{.example}
+ ## `lib.strings.splitStringBy` usage example
+
+ Split on periods and hyphens, discarding the separators:
+ ```nix
+ splitStringBy (prev: curr: builtins.elem curr [ "." "-" ]) false "foo.bar-baz"
+ => [ "foo" "bar" "baz" ]
+ ```
+
+ Split on transitions from lowercase to uppercase, keeping the uppercase characters:
+ ```nix
+ splitStringBy (prev: curr: builtins.match "[a-z]" prev != null && builtins.match "[A-Z]" curr != null) true "fooBarBaz"
+ => [ "foo" "Bar" "Baz" ]
+ ```
+
+ Handle leading separators correctly:
+ ```nix
+ splitStringBy (prev: curr: builtins.elem curr [ "." ]) false ".foo.bar.baz"
+ => [ "" "foo" "bar" "baz" ]
+ ```
+
+ Handle trailing separators correctly:
+ ```nix
+ splitStringBy (prev: curr: builtins.elem curr [ "." ]) false "foo.bar.baz."
+ => [ "foo" "bar" "baz" "" ]
+ ```
+ :::
+ */
+ splitStringBy =
+ predicate: keepSplit: str:
+ let
+ len = stringLength str;
+
+ # Helper function that processes the string character by character
+ go =
+ pos: currentPart: result:
+ # Base case: reached end of string
+ if pos == len then
+ result ++ [ currentPart ]
+ else
+ let
+ currChar = substring pos 1 str;
+ prevChar = if pos > 0 then substring (pos - 1) 1 str else "";
+ isSplit = predicate prevChar currChar;
+ in
+ if isSplit then
+ # Split here - add current part to results and start a new one
+ let
+ newResult = result ++ [ currentPart ];
+ newCurrentPart = if keepSplit then currChar else "";
+ in
+ go (pos + 1) newCurrentPart newResult
+ else
+ # Keep building current part
+ go (pos + 1) (currentPart + currChar) result;
+ in
+ if len == 0 then [ (addContextFrom str "") ] else map (addContextFrom str) (go 0 "" [ ]);
+
/**
Return a string without the specified prefix, if the prefix matches.
@@ -1881,7 +2033,7 @@ rec {
: The type of the feature to be set, as described in
https://cmake.org/cmake/help/latest/command/set.html
the possible values (case insensitive) are:
- BOOL FILEPATH PATH STRING INTERNAL
+ BOOL FILEPATH PATH STRING INTERNAL LIST
`value`
: The desired value
@@ -1911,6 +2063,7 @@ rec {
"PATH"
"STRING"
"INTERNAL"
+ "LIST"
];
in
type: feature: value:
diff --git a/lib/systems/default.nix b/lib/systems/default.nix
index 67bd9826465c..cf3f335eb7d4 100644
--- a/lib/systems/default.nix
+++ b/lib/systems/default.nix
@@ -121,8 +121,6 @@ let
"uclibc"
else if final.isAndroid then
"bionic"
- else if final.isLLVMLibc then
- "llvm"
else if
final.isLinux # default
then
@@ -209,6 +207,8 @@ let
"ppc${optionalString final.isLittleEndian "le"}"
else if final.isMips64 then
"mips64" # endianness is *not* included on mips64
+ else if final.isDarwin then
+ final.darwinArch
else
final.parsed.cpu.name;
@@ -303,6 +303,8 @@ let
qemuArch =
if final.isAarch32 then
"arm"
+ else if final.isAarch64 then
+ "aarch64"
else if final.isS390 && !final.isS390x then
null
else if final.isx86_64 then
@@ -329,12 +331,7 @@ let
else
final.parsed.cpu.name;
- darwinArch =
- {
- armv7a = "armv7";
- aarch64 = "arm64";
- }
- .${final.parsed.cpu.name} or final.parsed.cpu.name;
+ darwinArch = parse.darwinArch final.parsed.cpu;
darwinPlatform =
if final.isMacOS then
@@ -355,21 +352,9 @@ let
else
null;
- # Remove before 25.05
- androidSdkVersion =
- if (args ? sdkVer && !args ? androidSdkVersion) then
- throw "For android `sdkVer` has been renamed to `androidSdkVersion`"
- else if (args ? androidSdkVersion) then
- args.androidSdkVersion
- else
- null;
- androidNdkVersion =
- if (args ? ndkVer && !args ? androidNdkVersion) then
- throw "For android `ndkVer` has been renamed to `androidNdkVersion`"
- else if (args ? androidSdkVersion) then
- args.androidNdkVersion
- else
- null;
+ # Handle Android SDK and NDK versions.
+ androidSdkVersion = args.androidSdkVersion or null;
+ androidNdkVersion = args.androidNdkVersion or null;
}
// (
let
@@ -488,8 +473,8 @@ let
}
.${cpu.name} or cpu.name;
vendor_ = final.rust.platform.vendor;
- # TODO: deprecate args.rustc in favour of args.rust after 23.05 is EOL.
in
+ # TODO: deprecate args.rustc in favour of args.rust after 23.05 is EOL.
args.rust.rustcTarget or args.rustc.config or (
# Rust uses `wasm32-wasip?` rather than `wasm32-unknown-wasi`.
# We cannot know which subversion does the user want, and
@@ -534,6 +519,35 @@ let
"-uefi"
];
};
+ }
+ // {
+ go = {
+ # See https://pkg.go.dev/internal/platform for a list of known platforms
+ GOARCH =
+ {
+ "aarch64" = "arm64";
+ "arm" = "arm";
+ "armv5tel" = "arm";
+ "armv6l" = "arm";
+ "armv7l" = "arm";
+ "i686" = "386";
+ "loongarch64" = "loong64";
+ "mips" = "mips";
+ "mips64el" = "mips64le";
+ "mipsel" = "mipsle";
+ "powerpc64" = "ppc64";
+ "powerpc64le" = "ppc64le";
+ "riscv64" = "riscv64";
+ "s390x" = "s390x";
+ "x86_64" = "amd64";
+ "wasm32" = "wasm";
+ }
+ .${final.parsed.cpu.name} or (throw "Unknown CPU variant ${final.parsed.cpu.name} by Go");
+ GOOS = if final.isWasi then "wasip1" else final.parsed.kernel.name;
+
+ # See https://go.dev/wiki/GoArm
+ GOARM = toString (lib.intersectLists [ (final.parsed.cpu.version or "") ] [ "5" "6" "7" ]);
+ };
};
in
assert final.useAndroidPrebuilt -> final.isAndroid;
diff --git a/lib/systems/doubles.nix b/lib/systems/doubles.nix
index b2a66021dfc9..90f67b3916fb 100644
--- a/lib/systems/doubles.nix
+++ b/lib/systems/doubles.nix
@@ -12,9 +12,7 @@ let
# Darwin
"x86_64-darwin"
- "i686-darwin"
"aarch64-darwin"
- "armv7a-darwin"
# FreeBSD
"i686-freebsd"
diff --git a/lib/systems/examples.nix b/lib/systems/examples.nix
index cb54d8fd631a..7378b3bc5ecb 100644
--- a/lib/systems/examples.nix
+++ b/lib/systems/examples.nix
@@ -146,6 +146,10 @@ rec {
riscv64 = riscv "64";
riscv32 = riscv "32";
+ riscv64-musl = {
+ config = "riscv64-unknown-linux-musl";
+ };
+
riscv64-embedded = {
config = "riscv64-none-elf";
libc = "newlib";
@@ -215,6 +219,10 @@ rec {
config = "arm-none-eabi";
libc = "newlib";
};
+ arm-embedded-nano = {
+ config = "arm-none-eabi";
+ libc = "newlib-nano";
+ };
armhf-embedded = {
config = "arm-none-eabihf";
libc = "newlib";
@@ -276,7 +284,7 @@ rec {
#
iphone64 = {
- config = "aarch64-apple-ios";
+ config = "arm64-apple-ios";
# config = "aarch64-apple-darwin14";
darwinSdkVersion = "14.3";
xcodeVer = "12.3";
@@ -284,15 +292,6 @@ rec {
useiOSPrebuilt = true;
};
- iphone32 = {
- config = "armv7a-apple-ios";
- # config = "arm-apple-darwin10";
- darwinSdkVersion = "14.3";
- xcodeVer = "12.3";
- xcodePlatform = "iPhoneOS";
- useiOSPrebuilt = true;
- };
-
iphone64-simulator = {
config = "x86_64-apple-ios";
# config = "x86_64-apple-darwin14";
@@ -303,18 +302,8 @@ rec {
useiOSPrebuilt = true;
};
- iphone32-simulator = {
- config = "i686-apple-ios";
- # config = "i386-apple-darwin11";
- darwinSdkVersion = "14.3";
- xcodeVer = "12.3";
- xcodePlatform = "iPhoneSimulator";
- darwinPlatform = "ios-simulator";
- useiOSPrebuilt = true;
- };
-
aarch64-darwin = {
- config = "aarch64-apple-darwin";
+ config = "arm64-apple-darwin";
xcodePlatform = "MacOSX";
platform = { };
};
diff --git a/lib/systems/inspect.nix b/lib/systems/inspect.nix
index c330d5473bc5..82fd289e075c 100644
--- a/lib/systems/inspect.nix
+++ b/lib/systems/inspect.nix
@@ -386,7 +386,6 @@ rec {
uclibceabi
uclibceabihf
];
- isLLVMLibc = [ { abi = abis.llvm; } ];
isEfi = [
{
@@ -417,6 +416,11 @@ rec {
family = "x86";
};
}
+ {
+ cpu = {
+ family = "loongarch";
+ };
+ }
];
isElf = {
diff --git a/lib/systems/parse.nix b/lib/systems/parse.nix
index a7f65e69bf4c..0dfd91b7bb76 100644
--- a/lib/systems/parse.nix
+++ b/lib/systems/parse.nix
@@ -396,6 +396,12 @@ rec {
significantByte = littleEndian;
family = "javascript";
};
+ }
+ // {
+ # aliases
+ # Apple architecture name, as used by `darwinArch`; required by
+ # LLVM ≥ 20.
+ arm64 = cpuTypes.aarch64;
};
# GNU build systems assume that older NetBSD architectures are using a.out.
@@ -734,9 +740,6 @@ rec {
};
uclibc = { };
- # LLVM libc
- llvm = { };
-
unknown = { };
};
@@ -921,6 +924,8 @@ rec {
kernelName = kernel: kernel.name + toString (kernel.version or "");
+ darwinArch = cpu: if cpu.name == "aarch64" then "arm64" else cpu.name;
+
doubleFromSystem =
{
cpu,
@@ -949,8 +954,9 @@ rec {
kernel.name == "netbsd" && gnuNetBSDDefaultExecFormat cpu != kernel.execFormat
) kernel.execFormat.name;
optAbi = optionalString (abi != abis.unknown) "-${abi.name}";
+ cpuName = if kernel.families ? darwin then darwinArch cpu else cpu.name;
in
- "${cpu.name}-${vendor.name}-${kernelName kernel}${optExecFormat}${optAbi}";
+ "${cpuName}-${vendor.name}-${kernelName kernel}${optExecFormat}${optAbi}";
################################################################################
diff --git a/lib/tests/.editorconfig b/lib/tests/.editorconfig
new file mode 100644
index 000000000000..7853c13cecf3
--- /dev/null
+++ b/lib/tests/.editorconfig
@@ -0,0 +1,3 @@
+[*.plist]
+indent_style = tab
+insert_final_newline = unset
diff --git a/lib/tests/maintainers.nix b/lib/tests/maintainers.nix
index d04bb07ea2c8..1d3fd5630c26 100644
--- a/lib/tests/maintainers.nix
+++ b/lib/tests/maintainers.nix
@@ -1,6 +1,6 @@
# to run these tests (and the others)
# nix-build nixpkgs/lib/tests/release.nix
-# These tests should stay in sync with the comment in maintainers/maintainers-list.nix
+# These tests should stay in sync with the comment in maintainers/maintainer-list.nix
{
# The pkgs used for dependencies for the testing itself
pkgs ? import ../.. { },
diff --git a/lib/tests/misc.nix b/lib/tests/misc.nix
index f5f1fb5e7c2d..0d8377ec6006 100644
--- a/lib/tests/misc.nix
+++ b/lib/tests/misc.nix
@@ -631,6 +631,101 @@ runTests {
];
};
+ testSplitStringBySimpleDelimiter = {
+ expr = strings.splitStringBy (
+ prev: curr:
+ builtins.elem curr [
+ "."
+ "-"
+ ]
+ ) false "foo.bar-baz";
+ expected = [
+ "foo"
+ "bar"
+ "baz"
+ ];
+ };
+
+ testSplitStringByLeadingDelimiter = {
+ expr = strings.splitStringBy (prev: curr: builtins.elem curr [ "." ]) false ".foo.bar.baz";
+ expected = [
+ ""
+ "foo"
+ "bar"
+ "baz"
+ ];
+ };
+
+ testSplitStringByTrailingDelimiter = {
+ expr = strings.splitStringBy (prev: curr: builtins.elem curr [ "." ]) false "foo.bar.baz.";
+ expected = [
+ "foo"
+ "bar"
+ "baz"
+ ""
+ ];
+ };
+
+ testSplitStringByMultipleConsecutiveDelimiters = {
+ expr = strings.splitStringBy (prev: curr: builtins.elem curr [ "." ]) false "foo...bar";
+ expected = [
+ "foo"
+ ""
+ ""
+ "bar"
+ ];
+ };
+
+ testSplitStringByKeepingSplitChar = {
+ expr = strings.splitStringBy (prev: curr: builtins.elem curr [ "." ]) true "foo.bar.baz";
+ expected = [
+ "foo"
+ ".bar"
+ ".baz"
+ ];
+ };
+
+ testSplitStringByCaseTransition = {
+ expr = strings.splitStringBy (
+ prev: curr: builtins.match "[a-z]" prev != null && builtins.match "[A-Z]" curr != null
+ ) true "fooBarBaz";
+ expected = [
+ "foo"
+ "Bar"
+ "Baz"
+ ];
+ };
+
+ testSplitStringByEmptyString = {
+ expr = strings.splitStringBy (prev: curr: builtins.elem curr [ "." ]) false "";
+ expected = [ "" ];
+ };
+
+ testSplitStringByComplexPredicate = {
+ expr = strings.splitStringBy (
+ prev: curr:
+ prev != ""
+ && curr != ""
+ && builtins.match "[0-9]" prev != null
+ && builtins.match "[a-z]" curr != null
+ ) true "123abc456def";
+ expected = [
+ "123"
+ "abc456"
+ "def"
+ ];
+ };
+
+ testSplitStringByUpperCaseStart = {
+ expr = strings.splitStringBy (prev: curr: builtins.match "[A-Z]" curr != null) true "FooBarBaz";
+ expected = [
+ ""
+ "Foo"
+ "Bar"
+ "Baz"
+ ];
+ };
+
testEscapeShellArg = {
expr = strings.escapeShellArg "esc'ape\nme";
expected = "'esc'\\''ape\nme'";
@@ -851,8 +946,8 @@ runTests {
};
testEscapeC = {
- expr = strings.escapeC [ " " ] "Hello World";
- expected = "Hello\\x20World";
+ expr = strings.escapeC [ "\n" " " ] "Hello World\n";
+ expected = "Hello\\x20World\\x0a";
};
testEscapeURL = testAllTrue [
@@ -874,6 +969,28 @@ runTests {
testToSentenceCasePath = testingThrow (strings.toSentenceCase ./.);
+ testToCamelCase = {
+ expr = strings.toCamelCase "hello world";
+ expected = "helloWorld";
+ };
+
+ testToCamelCaseFromKebab = {
+ expr = strings.toCamelCase "hello-world";
+ expected = "helloWorld";
+ };
+
+ testToCamelCaseFromSnake = {
+ expr = strings.toCamelCase "hello_world";
+ expected = "helloWorld";
+ };
+
+ testToCamelCaseFromPascal = {
+ expr = strings.toCamelCase "HelloWorld";
+ expected = "helloWorld";
+ };
+
+ testToCamelCasePath = testingThrow (strings.toCamelCase ./.);
+
testToInt = testAllTrue [
# Naive
(123 == toInt "123")
@@ -1262,6 +1379,69 @@ runTests {
)
];
+ testTakeEnd =
+ let
+ inherit (lib) takeEnd;
+ in
+ testAllTrue [
+ (
+ takeEnd 0 [
+ 1
+ 2
+ 3
+ ] == [ ]
+ )
+ (
+ takeEnd 1 [
+ 1
+ 2
+ 3
+ ] == [ 3 ]
+ )
+ (
+ takeEnd 2 [
+ 1
+ 2
+ 3
+ ] == [
+ 2
+ 3
+ ]
+ )
+ (
+ takeEnd 3 [
+ 1
+ 2
+ 3
+ ] == [
+ 1
+ 2
+ 3
+ ]
+ )
+ (
+ takeEnd 4 [
+ 1
+ 2
+ 3
+ ] == [
+ 1
+ 2
+ 3
+ ]
+ )
+ (takeEnd 0 [ ] == [ ])
+ (takeEnd 1 [ ] == [ ])
+ (
+ takeEnd (-1) [
+ 1
+ 2
+ 3
+ ] == [ ]
+ )
+ (takeEnd (-1) [ ] == [ ])
+ ];
+
testDrop =
let
inherit (lib) drop;
@@ -2830,6 +3010,42 @@ runTests {
expected = "unknown";
};
+ # https://github.com/NixOS/nixpkgs/issues/396849
+ "test: submodule definitions aren't unchecked when evaluating submodule documentation" = {
+ expr =
+ let
+ module =
+ { lib, ... }:
+ {
+ options.foo = lib.mkOption { type = lib.types.submodule submodule; };
+ };
+
+ submodule = {
+ options.bar = lib.mkOption { type = lib.types.int; };
+ config.submoduleWrong = throw "yikes";
+ };
+
+ options = (evalModules { modules = [ module ]; }).options;
+
+ renderableOpts = filter (o: !o.internal) (optionAttrSetToDocList options);
+ # Evaluate the whole docs
+ in
+ builtins.deepSeq renderableOpts
+ # Return the locations
+ (map (o: o.loc) renderableOpts);
+ expected = [
+ [
+ "_module"
+ "args"
+ ]
+ [ "foo" ]
+ [
+ "foo"
+ "bar"
+ ]
+ ];
+ };
+
testFreeformOptions = {
expr =
let
diff --git a/lib/tests/modules.sh b/lib/tests/modules.sh
index e623c0fb55b8..3b19c8c63f26 100755
--- a/lib/tests/modules.sh
+++ b/lib/tests/modules.sh
@@ -315,14 +315,26 @@ checkConfigOutput '^false$' config.enableAlias ./alias-with-priority-can-overrid
checkConfigOutput '^"hello"$' config.package.pname ./declare-mkPackageOption.nix
checkConfigOutput '^"hello"$' config.namedPackage.pname ./declare-mkPackageOption.nix
checkConfigOutput '^".*Hello.*"$' options.namedPackage.description ./declare-mkPackageOption.nix
+checkConfigOutput '^"literalExpression"$' options.namedPackage.defaultText._type ./declare-mkPackageOption.nix
+checkConfigOutput '^"pkgs\.hello"$' options.namedPackage.defaultText.text ./declare-mkPackageOption.nix
+checkConfigOutput '^"hello"$' config.namedPackageSingletonDefault.pname ./declare-mkPackageOption.nix
+checkConfigOutput '^".*Hello.*"$' options.namedPackageSingletonDefault.description ./declare-mkPackageOption.nix
+checkConfigOutput '^"pkgs\.hello"$' options.namedPackageSingletonDefault.defaultText.text ./declare-mkPackageOption.nix
checkConfigOutput '^"hello"$' config.pathPackage.pname ./declare-mkPackageOption.nix
+checkConfigOutput '^"literalExpression"$' options.packageWithExample.example._type ./declare-mkPackageOption.nix
checkConfigOutput '^"pkgs\.hello\.override \{ stdenv = pkgs\.clangStdenv; \}"$' options.packageWithExample.example.text ./declare-mkPackageOption.nix
+checkConfigOutput '^"literalExpression"$' options.packageWithPathExample.example._type ./declare-mkPackageOption.nix
+checkConfigOutput '^"pkgs\.hello"$' options.packageWithPathExample.example.text ./declare-mkPackageOption.nix
checkConfigOutput '^".*Example extra description\..*"$' options.packageWithExtraDescription.description ./declare-mkPackageOption.nix
checkConfigError 'The option .undefinedPackage. was accessed but has no value defined. Try setting the option.' config.undefinedPackage ./declare-mkPackageOption.nix
checkConfigOutput '^null$' config.nullablePackage ./declare-mkPackageOption.nix
-checkConfigOutput '^"null or package"$' options.nullablePackageWithDefault.type.description ./declare-mkPackageOption.nix
+checkConfigOutput '^"null or package"$' options.nullablePackage.type.description ./declare-mkPackageOption.nix
+checkConfigOutput '^"hello"$' config.nullablePackageWithDefault.pname ./declare-mkPackageOption.nix
checkConfigOutput '^"myPkgs\.hello"$' options.packageWithPkgsText.defaultText.text ./declare-mkPackageOption.nix
checkConfigOutput '^"hello-other"$' options.packageFromOtherSet.default.pname ./declare-mkPackageOption.nix
+checkConfigOutput '^"hello"$' config.packageInvalidIdentifier.pname ./declare-mkPackageOption.nix
+checkConfigOutput '^"pkgs\.\\"123\\"\.\\"with\\\\\\"quote\\"\.hello"$' options.packageInvalidIdentifier.defaultText.text ./declare-mkPackageOption.nix
+checkConfigOutput '^"pkgs\.\\"123\\"\.\\"with\\\\\\"quote\\"\.hello"$' options.packageInvalidIdentifierExample.example.text ./declare-mkPackageOption.nix
# submoduleWith
@@ -673,6 +685,26 @@ checkConfigError 'The option .conflictingPathOptionType. in .*/pathWith.nix. is
# types.pathWith { inStore = true; absolute = false; }
checkConfigError 'In pathWith, inStore means the path must be absolute' config.impossiblePathOptionType ./pathWith.nix
+# mkDefinition
+# check that mkDefinition 'file' is printed in the error message
+checkConfigError 'Cannot merge definitions.*\n\s*- In .file.*\n\s*- In .other.*' config.conflict ./mkDefinition.nix
+checkConfigError 'A definition for option .viaOptionDefault. is not of type .boolean.*' config.viaOptionDefault ./mkDefinition.nix
+checkConfigOutput '^true$' config.viaConfig ./mkDefinition.nix
+checkConfigOutput '^true$' config.mkMerge ./mkDefinition.nix
+checkConfigOutput '^true$' config.mkForce ./mkDefinition.nix
+
+# specialArgs._class
+checkConfigOutput '"nixos"' config.nixos.config.foo ./specialArgs-class.nix
+checkConfigOutput '"bar"' config.conditionalImportAsNixos.config.foo ./specialArgs-class.nix
+checkConfigError 'attribute .*bar.* not found' config.conditionalImportAsNixos.config.bar ./specialArgs-class.nix
+checkConfigError 'attribute .*foo.* not found' config.conditionalImportAsDarwin.config.foo ./specialArgs-class.nix
+checkConfigOutput '"foo"' config.conditionalImportAsDarwin.config.bar ./specialArgs-class.nix
+checkConfigOutput '"nixos"' config.sub.nixos.foo ./specialArgs-class.nix
+checkConfigOutput '"bar"' config.sub.conditionalImportAsNixos.foo ./specialArgs-class.nix
+checkConfigError 'attribute .*bar.* not found' config.sub.conditionalImportAsNixos.bar ./specialArgs-class.nix
+checkConfigError 'attribute .*foo.* not found' config.sub.conditionalImportAsDarwin.foo ./specialArgs-class.nix
+checkConfigOutput '"foo"' config.sub.conditionalImportAsDarwin.bar ./specialArgs-class.nix
+
cat < {
+ $attr = import {
url = "http://tarballs.nixos.org/${s3_prefix}/${nixpkgs_revision}/$fname";
hash = "${sri}";$(
[[ -n ${executable_nix} ]] && printf "\n %s" "${executable_nix}"
[[ -n ${name_nix} ]] && printf "\n %s" "${name_nix}"
[[ -n ${unpack_nix} ]] && printf "\n %s" "${unpack_nix}"
)
-};
+ };
EOF
done
# footer
diff --git a/maintainers/scripts/check-maintainers-sorted.nix b/maintainers/scripts/check-maintainers-sorted.nix
deleted file mode 100644
index 606a72c0aa9b..000000000000
--- a/maintainers/scripts/check-maintainers-sorted.nix
+++ /dev/null
@@ -1,87 +0,0 @@
-let
- lib = import ../../lib;
- inherit (lib)
- add
- attrNames
- elemAt
- foldl'
- genList
- length
- replaceStrings
- sort
- toLower
- trace
- ;
-
- maintainers = import ../maintainer-list.nix;
- simplify = replaceStrings [ "-" "_" ] [ "" "" ];
- compare = a: b: simplify (toLower a) < simplify (toLower b);
- namesSorted = sort (a: b: a.key < b.key) (
- map (
- n:
- let
- pos = builtins.unsafeGetAttrPos n maintainers;
- in
- assert pos == null -> throw "maintainers entry ${n} is malformed";
- {
- name = n;
- line = pos.line;
- key = toLower (simplify n);
- }
- ) (attrNames maintainers)
- );
- before =
- {
- name,
- line,
- key,
- }:
- foldl' (
- acc: n: if n.key < key && (acc == null || n.key > acc.key) then n else acc
- ) null namesSorted;
- errors = foldl' add 0 (
- map (
- i:
- let
- a = elemAt namesSorted i;
- b = elemAt namesSorted (i + 1);
- lim =
- let
- t = before a;
- in
- if t == null then "the initial {" else t.name;
- in
- if a.line >= b.line then
- trace (
- "maintainer ${a.name} (line ${toString a.line}) should be listed "
- + "after ${lim}, not after ${b.name} (line ${toString b.line})"
- ) 1
- else
- 0
- ) (genList (i: i) (length namesSorted - 1))
- );
-in
-assert errors == 0;
-"all good!"
-
-# generate edit commands to sort the list.
-# may everything following the last current entry (closing } ff) in the wrong place
-# with lib;
-# concatStringsSep
-# "\n"
-# (let first = foldl' (acc: n: if n.line < acc then n.line else acc) 999999999 namesSorted;
-# commands = map
-# (i: let e = elemAt namesSorted i;
-# begin = foldl'
-# (acc: n: if n.line < e.line && n.line > acc then n.line else acc)
-# 1
-# namesSorted;
-# end =
-# foldl' (acc: n: if n.line > e.line && n.line < acc then n.line else acc)
-# 999999999
-# namesSorted;
-# in "${toString e.line},${toString (end - 1)} p")
-# (genList (i: i) (length namesSorted));
-# in map
-# (c: "sed -ne '${c}' maintainers/maintainer-list.nix")
-# ([ "1,${toString (first - 1)} p" ] ++ commands))
diff --git a/maintainers/scripts/get-maintainer-pings-between.sh b/maintainers/scripts/get-maintainer-pings-between.sh
new file mode 100755
index 000000000000..4b6d7ff78052
--- /dev/null
+++ b/maintainers/scripts/get-maintainer-pings-between.sh
@@ -0,0 +1,78 @@
+#!/usr/bin/env nix-shell
+#!nix-shell -i bash -p git jq
+
+# Outputs a list of maintainers that would be pinged across two nixpkgs revisions.
+# Authors:
+# Morgan Jones (@numinit)
+# Tristan Ross (@RossComputerGuy)
+
+set -euo pipefail
+
+if [ $# -lt 2 ]; then
+ echo "Usage: $0 " >&2
+ exit 1
+fi
+
+repo="$(git rev-parse --show-toplevel)"
+system="$(nix-instantiate --eval --expr builtins.currentSystem)"
+rev1="$(git -C "$repo" rev-parse "$1")"
+rev2="$(git -C "$repo" rev-parse "$2")"
+
+echo "Touched files:" >&2
+git -C "$repo" diff --name-only "$rev1" "$rev2" \
+ | jq --raw-input --slurp 'split("\n")[:-1]' | tee "$TMPDIR/touched-files.json" >&2
+
+# Runs an eval in the given worktree, outputting the path to $TMPDIR/$1.path.
+# $1: The revision SHA.
+eval_in_worktree() (
+ mkdir -p .worktree
+ local rev="$1"
+ local tree=".worktree/$rev"
+ if [ ! -d "$tree" ]; then
+ git -C "$repo" worktree add -f -d "$tree" "$rev" >&2
+ fi
+ cd "$tree"
+
+ local workdir="$TMPDIR/$rev"
+ rm -rf "$workdir"
+ mkdir -p "$workdir"
+
+ nix-build ci -A eval.attrpathsSuperset -o "$workdir/paths" >&2
+ mkdir -p "$workdir/intermediates"
+ nix-build ci -A eval.singleSystem \
+ --arg evalSystem "$system" \
+ --arg attrpathFile "$workdir/paths/paths.json" \
+ --arg chunkSize ${CHUNK_SIZE:-10000} \
+ -o "$workdir/intermediates/.intermediate-1" >&2
+
+ # eval.combine nix-build needs a directory, not a symlink
+ cp -RL "$workdir/intermediates/.intermediate-1" "$workdir/intermediates/intermediate-1"
+ chmod -R +w "$workdir/intermediates/intermediate-1"
+ rm -rf "$workdir/intermediates/.intermediate-1"
+
+ nix-build ci -A eval.combine \
+ --arg resultsDir "$workdir/intermediates" \
+ -o "$workdir/result" >&2
+)
+
+eval_in_worktree "$rev1" &
+pid1=$!
+eval_in_worktree "$rev2" &
+pid2=$!
+
+wait $pid1
+wait $pid2
+
+path1="$TMPDIR/$rev1"
+path2="$TMPDIR/$rev2"
+
+# Use the repo this script was executed in to get accurate maintainer info
+nix-build "$repo/ci" -A eval.compare \
+ --arg beforeResultDir "$path1/result" \
+ --arg afterResultDir "$path2/result" \
+ --arg touchedFilesJson "$TMPDIR/touched-files.json" \
+ --arg byName true \
+ -o comparison
+
+echo "Pinged maintainers (check $repo/comparison for more details)" >&2
+jq < comparison/maintainers.json
diff --git a/maintainers/scripts/haskell/merge-and-open-pr.sh b/maintainers/scripts/haskell/merge-and-open-pr.sh
index 62565d24d623..ea985acfc9a0 100755
--- a/maintainers/scripts/haskell/merge-and-open-pr.sh
+++ b/maintainers/scripts/haskell/merge-and-open-pr.sh
@@ -1,8 +1,8 @@
#! /usr/bin/env nix-shell
#! nix-shell -i bash -p git gh -I nixpkgs=.
#
-# Script to merge the currently open haskell-updates PR into master, bump the
-# Stackage version and Hackage versions, and open the next haskell-updates PR.
+# Script to merge the currently open haskell-updates PR , bump the Stackage
+# version and Hackage versions, and open the next haskell-updates PR.
set -eu -o pipefail
@@ -79,10 +79,6 @@ fi
echo "Merging https://github.com/NixOS/nixpkgs/pull/${curr_haskell_updates_pr_num}..."
gh pr merge --repo NixOS/nixpkgs --merge "$curr_haskell_updates_pr_num"
-# Update the list of Haskell package versions in NixOS on Hackage.
-echo "Updating list of Haskell package versions in NixOS on Hackage..."
-./maintainers/scripts/haskell/upload-nixos-package-list-to-hackage.sh
-
# Update stackage, Hackage hashes, and regenerate Haskell package set
echo "Updating Stackage..."
./maintainers/scripts/haskell/update-stackage.sh --do-commit
@@ -100,7 +96,7 @@ git push "$push_remote" haskell-updates
new_pr_body=$(cat < bool {
+ let res = with-env { NIXPKGS_ALLOW_BROKEN: "1" } {
+ # rather high timeout of half an hour, just to prevent never-ending builds
+ ^nix-build --no-out-link -j 1 --cores 1 --timeout 1800 -A $"haskellPackages.($package)" | complete
+ }
+ if $res.exit_code == 0 {
+ log warning $"($package) is not broken anymore!"
+ return false
+ } else {
+ log info $"($package) is still broken."
+ log debug $"($package) build log:\n($res.stderr)"
+ return true
+ }
+}
+
+def main [] {
+ $broken_config | open | get broken-packages
+ | par-each {|package| if not (is-broken $package) { ^flock -x $broken_config -c $"sed -i -e '/^ - ($package) /d' ($broken_config)" }}
+}
diff --git a/maintainers/scripts/haskell/update-stackage.sh b/maintainers/scripts/haskell/update-stackage.sh
index e72b77ccd349..2430edbdd420 100755
--- a/maintainers/scripts/haskell/update-stackage.sh
+++ b/maintainers/scripts/haskell/update-stackage.sh
@@ -8,7 +8,7 @@ set -eu -o pipefail
# (should be capitalized like the display name)
SOLVER=LTS
# Stackage solver verson, if any. Use latest if empty
-VERSION=22
+VERSION=
TMP_TEMPLATE=update-stackage.XXXXXXX
readonly SOLVER
readonly VERSION
diff --git a/maintainers/scripts/kde/generate-sources.py b/maintainers/scripts/kde/generate-sources.py
index e4241cced120..f251a737efd1 100755
--- a/maintainers/scripts/kde/generate-sources.py
+++ b/maintainers/scripts/kde/generate-sources.py
@@ -1,10 +1,10 @@
#!/usr/bin/env nix-shell
-#!nix-shell -i python3 -p "python3.withPackages(ps: [ ps.beautifulsoup4 ps.click ps.httpx ps.jinja2 ps.packaging ps.pyyaml ])"
+#!nix-shell -i python3 -p "python3.withPackages(ps: [ ps.beautifulsoup4 ps.click ps.httpx ps.jinja2 ps.packaging ps.pyyaml ])" nix-update
import base64
import binascii
import json
import pathlib
-from typing import Optional
+import subprocess
from urllib.parse import urljoin, urlparse
import bs4
@@ -30,7 +30,13 @@ ROOT_TEMPLATE = jinja2.Template('''
{{ p }} = callPackage ./{{ p }} { };
{%- endfor %}
}
-'''.strip());
+'''.strip())
+
+PROJECTS_WITH_RUST = {
+ "akonadi-search",
+ "angelfish",
+ "kdepim-addons",
+}
def to_sri(hash):
raw = binascii.unhexlify(hash)
@@ -40,7 +46,7 @@ def to_sri(hash):
@click.command
@click.argument(
- "set",
+ "pkgset",
type=click.Choice(["frameworks", "gear", "plasma"]),
required=True
)
@@ -65,9 +71,9 @@ def to_sri(hash):
type=str,
default=None,
)
-def main(set: str, version: str, nixpkgs: pathlib.Path, sources_url: Optional[str]):
+def main(pkgset: str, version: str, nixpkgs: pathlib.Path, sources_url: str | None):
root_dir = nixpkgs / "pkgs/kde"
- set_dir = root_dir / set
+ set_dir = root_dir / pkgset
generated_dir = root_dir / "generated"
metadata = utils.KDERepoMetadata.from_json(generated_dir)
@@ -76,7 +82,7 @@ def main(set: str, version: str, nixpkgs: pathlib.Path, sources_url: Optional[st
"frameworks": f"frameworks/{version}/",
"gear": f"release-service/{version}/src/",
"plasma": f"plasma/{version}/",
- }[set]
+ }[pkgset]
sources_url = f"https://download.kde.org/stable/{set_url}"
client = httpx.Client()
@@ -85,6 +91,7 @@ def main(set: str, version: str, nixpkgs: pathlib.Path, sources_url: Optional[st
bs = bs4.BeautifulSoup(sources.text, features="html.parser")
results = {}
+ projects_to_update_rust = set()
for item in bs.select("tr")[3:]:
link = item.select_one("td:nth-child(2) a")
if not link:
@@ -95,6 +102,9 @@ def main(set: str, version: str, nixpkgs: pathlib.Path, sources_url: Optional[st
if project_name not in metadata.projects_by_name:
print(f"Warning: unknown tarball: {project_name}")
+ if project_name in PROJECTS_WITH_RUST:
+ projects_to_update_rust.add(project_name)
+
if version_and_ext.endswith(".sig"):
continue
@@ -119,8 +129,9 @@ def main(set: str, version: str, nixpkgs: pathlib.Path, sources_url: Optional[st
pkg_dir = set_dir / project_name
pkg_file = pkg_dir / "default.nix"
+
if not pkg_file.exists():
- print(f"Generated new package: {set}/{project_name}")
+ print(f"Generated new package: {pkgset}/{project_name}")
pkg_dir.mkdir(parents=True, exist_ok=True)
with pkg_file.open("w") as fd:
fd.write(LEAF_TEMPLATE.render(pname=project_name) + "\n")
@@ -131,9 +142,20 @@ def main(set: str, version: str, nixpkgs: pathlib.Path, sources_url: Optional[st
sources_dir = generated_dir / "sources"
sources_dir.mkdir(parents=True, exist_ok=True)
- with (sources_dir / f"{set}.json").open("w") as fd:
+ with (sources_dir / f"{pkgset}.json").open("w") as fd:
json.dump(results, fd, indent=2)
+ for project_name in projects_to_update_rust:
+ print(f"Updating cargoDeps hash for {pkgset}/{project_name}...")
+ subprocess.run([
+ "nix-update",
+ f"kdePackages.{project_name}",
+ "--version",
+ "skip",
+ "--override-filename",
+ pkg_file
+ ])
+
if __name__ == "__main__":
main() # type: ignore
diff --git a/maintainers/scripts/luarocks-packages.csv b/maintainers/scripts/luarocks-packages.csv
index 5beef16bf14b..2b9c13e5071d 100644
--- a/maintainers/scripts/luarocks-packages.csv
+++ b/maintainers/scripts/luarocks-packages.csv
@@ -4,7 +4,7 @@ ansicolors,,,,,,Freed-Wu
argparse,,,,,,
basexx,,,,,,
binaryheap,,,,,,vcunat
-bit32,,,,5.3.0-1,5.1,lblasc
+bit32,,,,,5.1,lblasc
busted,,,,,,
busted-htest,,,,,,mrcjkb
cassowary,,,,,,alerque
@@ -26,8 +26,9 @@ funnyfiles.nvim,,,,,,mrcjkb
fzf-lua,,,,,,mrcjkb
fzy,,,,,,mrcjkb
gitsigns.nvim,https://raw.githubusercontent.com/lewis6991/gitsigns.nvim/main/gitsigns.nvim-scm-1.rockspec,,,,5.1,
+grug-far.nvim,,,,,,teto
haskell-tools.nvim,,,,,,mrcjkb
-http,,,,0.3-0,,vcunat
+http,,,,0.4-0,,vcunat
image.nvim,,,,,,teto
inspect,,,,,,
jsregexp,,,,,,
@@ -105,7 +106,6 @@ luazip,,,,,,
lusc_luv,,,,,,
lush.nvim,,,https://luarocks.org/dev,,,teto
luuid,,,,20120509-2,,
-luv,,,,1.48.0-2,,
lyaml,,,,,,lblasc
lz.n,,,,,,mrcjkb
lze,,,,,,birdee
diff --git a/maintainers/scripts/pluginupdate-py/pluginupdate.py b/maintainers/scripts/pluginupdate-py/pluginupdate.py
index 2f55c1359de7..e3a4c44ede7e 100644
--- a/maintainers/scripts/pluginupdate-py/pluginupdate.py
+++ b/maintainers/scripts/pluginupdate-py/pluginupdate.py
@@ -558,7 +558,16 @@ class Editor:
}
for plugin_desc, plugin, redirect in fetched:
- result[plugin.normalized_name] = (plugin_desc, plugin, redirect)
+ # Check if plugin is a Plugin object and has normalized_name attribute
+ if isinstance(plugin, Plugin) and hasattr(plugin, 'normalized_name'):
+ result[plugin.normalized_name] = (plugin_desc, plugin, redirect)
+ elif isinstance(plugin, Exception):
+ # For exceptions, we can't determine the normalized_name
+ # Just log the error and continue
+ log.error(f"Error fetching plugin {plugin_desc.name}: {plugin!r}")
+ else:
+ # For unexpected types, log the issue
+ log.error(f"Unexpected plugin type for {plugin_desc.name}: {type(plugin)}")
return list(result.values())
@@ -615,9 +624,9 @@ class Editor:
"--github-token",
"-t",
type=str,
- default=os.getenv("GITHUB_API_TOKEN"),
+ default=os.getenv("GITHUB_TOKEN"),
help="""Allows to set --proc to higher values.
- Uses GITHUB_API_TOKEN environment variables as the default value.""",
+ Uses GITHUB_TOKEN environment variables as the default value.""",
)
common.add_argument(
"--no-commit",
diff --git a/maintainers/scripts/update-typst-packages.py b/maintainers/scripts/update-typst-packages.py
new file mode 100755
index 000000000000..2264f97d7706
--- /dev/null
+++ b/maintainers/scripts/update-typst-packages.py
@@ -0,0 +1,226 @@
+#!/usr/bin/env nix-shell
+#!nix-shell -p "python3.withPackages (p: with p; [ tomli tomli-w packaging license-expression])" -i python3
+
+# This file is formatted with `ruff format`.
+
+import os
+import re
+import tomli
+import tomli_w
+import subprocess
+import concurrent.futures
+import argparse
+import tempfile
+import tarfile
+from string import punctuation
+from packaging.version import Version
+from urllib import request
+from collections import OrderedDict
+
+
+class TypstPackage:
+ def __init__(self, **kwargs):
+ self.pname = kwargs["pname"]
+ self.version = kwargs["version"]
+ self.meta = kwargs["meta"]
+ self.path = kwargs["path"]
+ self.repo = (
+ None
+ if "repository" not in self.meta["package"]
+ else self.meta["package"]["repository"]
+ )
+ self.description = self.meta["package"]["description"].rstrip(punctuation)
+ self.license = self.meta["package"]["license"]
+ self.params = "" if "params" not in kwargs else kwargs["params"]
+ self.deps = [] if "deps" not in kwargs else kwargs["deps"]
+
+ @classmethod
+ def package_name_full(cls, package_name, version):
+ version_number = map(lambda x: int(x), version.split("."))
+ version_nix = "_".join(map(lambda x: str(x), version_number))
+ return "_".join((package_name, version_nix))
+
+ def license_tokens(self):
+ import license_expression as le
+
+ try:
+ # FIXME: ad hoc conversion
+ exception_list = [("EUPL-1.2+", "EUPL-1.2")]
+
+ def sanitize_license_string(license_string, lookups):
+ if not lookups:
+ return license_string
+ return sanitize_license_string(
+ license_string.replace(lookups[0][0], lookups[0][1]), lookups[1:]
+ )
+
+ sanitized = sanitize_license_string(self.license, exception_list)
+ licensing = le.get_spdx_licensing()
+ parsed = licensing.parse(sanitized, validate=True)
+ return [s.key for s in licensing.license_symbols(parsed)]
+ except le.ExpressionError as e:
+ print(
+ f'Failed to parse license string "{self.license}" because of {str(e)}'
+ )
+ exit(1)
+
+ def source(self):
+ url = f"https://packages.typst.org/preview/{self.pname}-{self.version}.tar.gz"
+ cmd = [
+ "nix",
+ "store",
+ "prefetch-file",
+ "--unpack",
+ "--hash-type",
+ "sha256",
+ "--refresh",
+ "--extra-experimental-features",
+ "nix-command",
+ ]
+ result = subprocess.run(cmd + [url], capture_output=True, text=True)
+ hash = re.search(r"hash\s+\'(sha256-.{44})\'", result.stderr).groups()[0]
+ return url, hash
+
+ def to_name_full(self):
+ return self.package_name_full(self.pname, self.version)
+
+ def to_attrs(self):
+ deps = set()
+ excludes = list(map(
+ lambda e: os.path.join(self.path, e),
+ self.meta["package"]["exclude"] if "exclude" in self.meta["package"] else [],
+ ))
+ for root, _, files in os.walk(self.path):
+ for file in filter(lambda f: f.split(".")[-1] == "typ", files):
+ file_path = os.path.join(root, file)
+ if file_path in excludes:
+ continue
+ with open(file_path, "r") as f:
+ deps.update(
+ set(
+ re.findall(
+ r"^\s*#import\s+\"@preview/([\w|-]+):(\d+.\d+.\d+)\"",
+ f.read(),
+ re.MULTILINE,
+ )
+ )
+ )
+ self.deps = list(
+ filter(lambda p: p[0] != self.pname or p[1] != self.version, deps)
+ )
+ source_url, source_hash = self.source()
+
+ return dict(
+ url=source_url,
+ hash=source_hash,
+ typstDeps=[
+ self.package_name_full(p, v)
+ for p, v in sorted(self.deps, key=lambda x: (x[0], Version(x[1])))
+ ],
+ description=self.description,
+ license=self.license_tokens(),
+ ) | (dict(homepage=self.repo) if self.repo else dict())
+
+
+def generate_typst_packages(preview_dir, output_file):
+ package_tree = dict()
+
+ print("Parsing metadata... from", preview_dir)
+ for p in os.listdir(preview_dir):
+ package_dir = os.path.join(preview_dir, p)
+ for v in os.listdir(package_dir):
+ package_version_dir = os.path.join(package_dir, v)
+ with open(
+ os.path.join(package_version_dir, "typst.toml"), "rb"
+ ) as meta_file:
+ try:
+ package = TypstPackage(
+ pname=p,
+ version=v,
+ meta=tomli.load(meta_file),
+ path=package_version_dir,
+ )
+ if package.pname in package_tree:
+ package_tree[package.pname][v] = package
+ else:
+ package_tree[package.pname] = dict({v: package})
+ except tomli.TOMLDecodeError:
+ print("Invalid typst.toml:", package_version_dir)
+
+ with open(output_file, "wb") as typst_packages:
+
+ def generate_package(pname, package_subtree):
+ sorted_keys = sorted(package_subtree.keys(), key=Version, reverse=True)
+ print(f"Generating metadata for {pname}")
+ return {
+ pname: OrderedDict(
+ (k, package_subtree[k].to_attrs()) for k in sorted_keys
+ )
+ }
+
+ with concurrent.futures.ThreadPoolExecutor(max_workers=100) as executor:
+ sorted_packages = sorted(package_tree.items(), key=lambda x: x[0])
+ futures = list()
+ for pname, psubtree in sorted_packages:
+ futures.append(executor.submit(generate_package, pname, psubtree))
+ packages = OrderedDict(
+ (package, subtree)
+ for future in futures
+ for package, subtree in future.result().items()
+ )
+ print(f"Writing metadata... to {output_file}")
+ tomli_w.dump(packages, typst_packages)
+
+
+def main(args):
+ PREVIEW_DIR = "packages/preview"
+ TYPST_PACKAGE_TARBALL_URL = (
+ "https://github.com/typst/packages/archive/refs/heads/main.tar.gz"
+ )
+
+ directory = args.directory
+ if not directory:
+ tempdir = tempfile.mkdtemp()
+ print(tempdir)
+ typst_tarball = os.path.join(tempdir, "main.tar.gz")
+
+ print(
+ "Downloading Typst packages source from {} to {}".format(
+ TYPST_PACKAGE_TARBALL_URL, typst_tarball
+ )
+ )
+ with request.urlopen(
+ request.Request(TYPST_PACKAGE_TARBALL_URL), timeout=15.0
+ ) as response:
+ if response.status == 200:
+ with open(typst_tarball, "wb+") as f:
+ f.write(response.read())
+ else:
+ print("Download failed")
+ exit(1)
+ with tarfile.open(typst_tarball) as tar:
+ tar.extractall(path=tempdir, filter="data")
+ directory = os.path.join(tempdir, "packages-main")
+ directory = os.path.abspath(directory)
+
+ generate_typst_packages(
+ os.path.join(directory, PREVIEW_DIR),
+ args.output,
+ )
+
+ exit(0)
+
+
+if __name__ == "__main__":
+ parser = argparse.ArgumentParser()
+ parser.add_argument(
+ "-d", "--directory", help="Local Typst Universe repository", default=None
+ )
+ parser.add_argument(
+ "-o",
+ "--output",
+ help="Output file",
+ default=os.path.join(os.path.abspath("."), "typst-packages-from-universe.toml"),
+ )
+ args = parser.parse_args()
+ main(args)
diff --git a/maintainers/scripts/update.py b/maintainers/scripts/update.py
index cfa051087ae5..576332b76607 100644
--- a/maintainers/scripts/update.py
+++ b/maintainers/scripts/update.py
@@ -1,6 +1,6 @@
from graphlib import TopologicalSorter
from pathlib import Path
-from typing import Any, Generator, Literal
+from typing import Any, Final, Generator, Literal
import argparse
import asyncio
import contextlib
@@ -15,6 +15,11 @@ import tempfile
Order = Literal["arbitrary", "reverse-topological", "topological"]
+FAKE_DEPENDENCY_FOR_INDEPENDENT_PACKAGES: Final[str] = (
+ "::fake_dependency_for_independent_packages"
+)
+
+
class CalledProcessError(Exception):
process: asyncio.subprocess.Process
stderr: bytes | None
@@ -116,10 +121,14 @@ def requisites_to_attrs(
def reverse_edges(graph: dict[str, set[str]]) -> dict[str, set[str]]:
"""
Flips the edges of a directed graph.
+
+ Packages without any dependency relation in the updated set
+ will be added to `FAKE_DEPENDENCY_FOR_INDEPENDENT_PACKAGES` node.
"""
reversed_graph: dict[str, set[str]] = {}
for dependent, dependencies in graph.items():
+ dependencies = dependencies or {FAKE_DEPENDENCY_FOR_INDEPENDENT_PACKAGES}
for dependency in dependencies:
reversed_graph.setdefault(dependency, set()).add(dependent)
@@ -413,6 +422,8 @@ async def populate_queue(
ready_packages = list(sorter.get_ready())
eprint(f"Enqueuing group of {len(ready_packages)} packages")
for package in ready_packages:
+ if package == FAKE_DEPENDENCY_FOR_INDEPENDENT_PACKAGES:
+ continue
await packages_to_update.put(attr_packages[package])
await packages_to_update.join()
sorter.done(*ready_packages)
diff --git a/maintainers/team-list.nix b/maintainers/team-list.nix
index e1df0e2714d7..fbd079df3460 100644
--- a/maintainers/team-list.nix
+++ b/maintainers/team-list.nix
@@ -68,6 +68,16 @@ with lib.maintainers;
];
};
+ apparmor = {
+ scope = "AppArmor-related modules, userspace tool packages and profiles";
+ shortName = "apparmor";
+ members = [
+ julm
+ thoughtpolice
+ grimmauld
+ ];
+ };
+
bazel = {
members = [
mboes
@@ -216,6 +226,25 @@ with lib.maintainers;
enableFeatureFreezePing = true;
};
+ cosmic = {
+ members = [
+ a-kenji
+ ahoneybun
+ drakon64
+ griffi-gh
+ HeitorAugustoLN
+ nyabinary
+ pandapip1
+ qyliss
+ thefossguy
+ michaelBelsanti
+ ];
+ githubTeams = [ "cosmic" ];
+ shortName = "cosmic";
+ scope = "Maintain the COSMIC DE and related packages.";
+ enableFeatureFreezePing = true;
+ };
+
cuda = {
members = [
connorbaker
@@ -380,7 +409,6 @@ with lib.maintainers;
leona
osnyx
ma27
- laalsaas
];
scope = "Team for Flying Circus employees who collectively maintain packages.";
shortName = "Flying Circus employees";
@@ -509,6 +537,7 @@ with lib.maintainers;
cdepillabout
maralorn
sternenseemann
+ wolfgangwalther
];
githubTeams = [ "haskell" ];
scope = "Maintain Haskell packages and infrastructure.";
@@ -814,6 +843,7 @@ with lib.maintainers;
Gabriella439
curran
lf-
+ jkachmar
];
scope = "Group registry for packages maintained by Mercury";
shortName = "Mercury Employees";
@@ -839,6 +869,7 @@ with lib.maintainers;
qyriad
_9999years
lf-
+ alois31
];
scope = "Maintain the Lix package manager inside of Nixpkgs.";
shortName = "Lix ecosystem";
@@ -905,6 +936,12 @@ with lib.maintainers;
enableFeatureFreezePing = true;
};
+ octodns = {
+ members = [ anthonyroussel ];
+ scope = "Maintain the ecosystem around OctoDNS";
+ shortName = "OctoDNS";
+ };
+
openstack = {
members = [
SuperSandro2000
@@ -1083,9 +1120,17 @@ with lib.maintainers;
};
sdl = {
- members = [ ];
- scope = "Maintain SDL libraries.";
+ members = [
+ evythedemon
+ grimmauld
+ jansol
+ marcin-serwin
+ pbsds
+ ];
+ githubTeams = [ "SDL" ];
+ scope = "Maintain core SDL libraries.";
shortName = "SDL";
+ enableFeatureFreezePing = true;
};
sphinx = {
@@ -1147,7 +1192,12 @@ with lib.maintainers;
};
systemd = {
- members = [ ];
+ members = [
+ flokli
+ arianvp
+ elvishjerricco
+ aanderse
+ ];
githubTeams = [ "systemd" ];
scope = "Maintain systemd for NixOS.";
shortName = "systemd";
@@ -1183,6 +1233,7 @@ with lib.maintainers;
hehongbo
lach
sigmasquadron
+ rane
];
scope = "Maintain the Xen Project Hypervisor and the related tooling ecosystem.";
shortName = "Xen Project Hypervisor";
diff --git a/nixos/doc/manual/configuration/luks-file-systems.section.md b/nixos/doc/manual/configuration/luks-file-systems.section.md
index b20957b40b89..a1d22f34e920 100644
--- a/nixos/doc/manual/configuration/luks-file-systems.section.md
+++ b/nixos/doc/manual/configuration/luks-file-systems.section.md
@@ -117,7 +117,7 @@ added to the LUKS volume.
```
Existing key slots are left intact, unless `--wipe-slot=` is specified. It is
-recommened to add a recovery key that should be stored in a secure physical
+recommended to add a recovery key that should be stored in a secure physical
location and can be entered wherever a password would be entered.
```ShellSession
diff --git a/nixos/doc/manual/configuration/profiles/minimal.section.md b/nixos/doc/manual/configuration/profiles/minimal.section.md
index 5b72112477f7..cfbd7ae6067b 100644
--- a/nixos/doc/manual/configuration/profiles/minimal.section.md
+++ b/nixos/doc/manual/configuration/profiles/minimal.section.md
@@ -1,6 +1,6 @@
# Minimal {#sec-profile-minimal}
This profile defines a small NixOS configuration. It does not contain any
-graphical stuff. It's a very short file that sets [](#opt-i18n.supportedLocales)
+graphical stuff. It's a very short file that sets the supported locales
to only support the user-selected locale, and
[disables packages' documentation](#opt-documentation.enable).
diff --git a/nixos/doc/manual/contributing-to-this-manual.chapter.md b/nixos/doc/manual/contributing-to-this-manual.chapter.md
index 7515bef44b10..a78a136becca 100644
--- a/nixos/doc/manual/contributing-to-this-manual.chapter.md
+++ b/nixos/doc/manual/contributing-to-this-manual.chapter.md
@@ -17,6 +17,28 @@ There's also [a convenient development daemon](https://nixos.org/manual/nixpkgs/
The above instructions don't deal with the appendix of available `configuration.nix` options, and the manual pages related to NixOS. These are built, and written in a different location and in a different format, as explained in the next sections.
+## Development environment {#sec-contributing-development-env}
+
+In order to reduce repetition, consider using tools from the provided development environment:
+
+Load it from the NixOS documentation directory with
+
+```ShellSession
+$ cd /path/to/nixpkgs/nixos/doc/manual
+$ nix-shell
+```
+
+To load the development utilities automatically when entering that directory, [set up `nix-direnv`](https://nix.dev/guides/recipes/direnv).
+
+Make sure that your local files aren't added to Git history by adding the following lines to `.git/info/exclude` at the root of the Nixpkgs repository:
+
+```
+/**/.envrc
+/**/.direnv
+```
+
+You might want to also use [`devmode`](https://github.com/NixOS/nixpkgs/blob/master/doc/README.md#devmode) while editing the manual.
+
## Testing redirects {#sec-contributing-redirects}
Once you have a successful build, you can open the relevant HTML (path mentioned above) in a browser along with the anchor, and observe the redirection.
diff --git a/nixos/doc/manual/default.nix b/nixos/doc/manual/default.nix
index 824b5eabb965..fd9f6761ac17 100644
--- a/nixos/doc/manual/default.nix
+++ b/nixos/doc/manual/default.nix
@@ -160,6 +160,8 @@ rec {
./manual.md \
$dst/${common.indexPath}
+ cp ${pkgs.roboto.src}/web/Roboto\[ital\,wdth\,wght\].ttf "$dst/Roboto.ttf"
+
mkdir -p $out/nix-support
echo "nix-build out $out" >> $out/nix-support/hydra-build-products
echo "doc manual $dst" >> $out/nix-support/hydra-build-products
diff --git a/nixos/doc/manual/development/meta-attributes.section.md b/nixos/doc/manual/development/meta-attributes.section.md
index fc0f7ae268de..c1795ba89696 100644
--- a/nixos/doc/manual/development/meta-attributes.section.md
+++ b/nixos/doc/manual/development/meta-attributes.section.md
@@ -22,7 +22,7 @@ file.
};
meta = {
- maintainers = with lib.maintainers; [ ericsagnes ];
+ maintainers = with lib.maintainers; [ ];
doc = ./default.md;
buildDocsInSandbox = true;
};
diff --git a/nixos/doc/manual/development/nixos-tests.chapter.md b/nixos/doc/manual/development/nixos-tests.chapter.md
index ec0e4b9f076a..889a90bae68b 100644
--- a/nixos/doc/manual/development/nixos-tests.chapter.md
+++ b/nixos/doc/manual/development/nixos-tests.chapter.md
@@ -10,4 +10,5 @@ writing-nixos-tests.section.md
running-nixos-tests.section.md
running-nixos-tests-interactively.section.md
linking-nixos-tests-to-packages.section.md
+testing-hardware-features.section.md
```
diff --git a/nixos/doc/manual/development/option-declarations.section.md b/nixos/doc/manual/development/option-declarations.section.md
index 112c4f054f2a..efccba3da37c 100644
--- a/nixos/doc/manual/development/option-declarations.section.md
+++ b/nixos/doc/manual/development/option-declarations.section.md
@@ -158,14 +158,14 @@ lib.mkOption {
::: {#ex-options-declarations-util-mkPackageOption-extraDescription .example}
### `mkPackageOption` with additional description text
```nix
-mkPackageOption pkgs [ "python39Packages" "pytorch" ] {
+mkPackageOption pkgs [ "python312Packages" "torch" ] {
extraDescription = "This is an example and doesn't actually do anything.";
}
# is like
lib.mkOption {
type = lib.types.package;
- default = pkgs.python39Packages.pytorch;
- defaultText = lib.literalExpression "pkgs.python39Packages.pytorch";
+ default = pkgs.python312Packages.torch;
+ defaultText = lib.literalExpression "pkgs.python312Packages.torch";
description = "The pytorch package to use. This is an example and doesn't actually do anything.";
}
```
diff --git a/nixos/doc/manual/development/option-def.section.md b/nixos/doc/manual/development/option-def.section.md
index 227f41d812ff..fddcfef393ae 100644
--- a/nixos/doc/manual/development/option-def.section.md
+++ b/nixos/doc/manual/development/option-def.section.md
@@ -123,3 +123,65 @@ they were declared in separate modules. This can be done using
];
}
```
+
+## Free-floating definitions {#sec-option-definitions-definitions}
+
+:::{.note}
+The module system internally transforms module syntax into definitions. This always happens internally.
+:::
+
+It is possible to create first class definitions which are not transformed _again_ into definitions by the module system.
+
+Usually the file location of a definition is implicit and equal to the file it came from.
+However, when manipulating definitions, it may be useful for them to be completely self-contained (or "free-floating").
+
+A free-floating definition is created with `mkDefinition { file = ...; value = ...; }`.
+
+Preserving the file location creates better error messages, for example when copying definitions from one option to another.
+
+Other properties like `mkOverride` `mkMerge` `mkAfter` can be used in the `value` attribute but not on the entire definition.
+
+This is what would work
+
+```nix
+mkDefinition {
+ value = mkForce 42;
+ file = "somefile.nix";
+}
+```
+
+While this would NOT work.
+
+```nix
+mkForce (mkDefinition {
+ value = 42;
+ file = "somefile.nix";
+})
+```
+
+The following shows an example configuration that yields an error with the custom position information:
+
+```nix
+{
+ _file = "file.nix";
+ options.foo = mkOption {
+ default = 13;
+ };
+ config.foo = lib.mkDefinition {
+ file = "custom place";
+ # mkOptionDefault creates a conflict with the option foo's `default = 1` on purpose
+ # So we see the error message below contains the conflicting values and different positions
+ value = lib.mkOptionDefault 42;
+ };
+}
+```
+
+evaluating the module yields the following error:
+
+```
+error: Cannot merge definitions of `foo'. Definition values:
+- In `file.nix': 13
+- In `custom place': 42
+```
+
+To set the file location for all definitions in a module, you may add the `_file` module syntax attribute, which has a similar effect to using `mkDefinition` on all definitions in the module, without the hassle.
diff --git a/nixos/doc/manual/development/running-nixos-tests-interactively.section.md b/nixos/doc/manual/development/running-nixos-tests-interactively.section.md
index b65cab992253..38d1e5916072 100644
--- a/nixos/doc/manual/development/running-nixos-tests-interactively.section.md
+++ b/nixos/doc/manual/development/running-nixos-tests-interactively.section.md
@@ -63,6 +63,66 @@ using:
Once the connection is established, you can enter commands in the socat terminal
where socat is running.
+## SSH Access for test machines {#sec-nixos-test-ssh-access}
+
+An SSH-based backdoor to log into machines can be enabled with
+
+```nix
+{
+ name = "…";
+ nodes.machines = { /* … */ };
+ interactive.sshBackdoor.enable = true;
+}
+```
+
+::: {.warning}
+Make sure to only enable the backdoor for interactive tests
+(i.e. by using `interactive.sshBackdoor.enable`)! This is the only
+supported configuration.
+
+Running a test in a sandbox with this will fail because `/dev/vhost-vsock` isn't available
+in the sandbox.
+:::
+
+This creates a [vsock socket](https://man7.org/linux/man-pages/man7/vsock.7.html)
+for each VM to log in with SSH. This configures root login with an empty password.
+
+When the VMs get started interactively with the test-driver, it's possible to
+connect to `machine` with
+
+```
+$ ssh vsock/3 -o User=root
+```
+
+The socket numbers correspond to the node number of the test VM, but start
+at three instead of one because that's the lowest possible
+vsock number. The exact SSH commands are also printed out when starting
+`nixos-test-driver`.
+
+On non-NixOS systems you'll probably need to enable
+the SSH config from {manpage}`systemd-ssh-proxy(1)` yourself.
+
+If starting VM fails with an error like
+
+```
+qemu-system-x86_64: -device vhost-vsock-pci,guest-cid=3: vhost-vsock: unable to set guest cid: Address already in use
+```
+
+it means that the vsock numbers for the VMs are already in use. This can happen
+if another interactive test with SSH backdoor enabled is running on the machine.
+
+In that case, you need to assign another range of vsock numbers. You can pick another
+offset with
+
+```nix
+{
+ sshBackdoor = {
+ enable = true;
+ vsockOffset = 23542;
+ };
+}
+```
+
## Port forwarding to NixOS test VMs {#sec-nixos-test-port-forwarding}
If your test has only a single VM, you may use e.g.
diff --git a/nixos/doc/manual/development/settings-options.section.md b/nixos/doc/manual/development/settings-options.section.md
index 4b49a1e82090..736662ad7ed9 100644
--- a/nixos/doc/manual/development/settings-options.section.md
+++ b/nixos/doc/manual/development/settings-options.section.md
@@ -412,7 +412,7 @@ have a predefined type and string generator already declared under
`multiline` (default `true`)
- : Whether to procude a multiline output. The output may still wrap across
+ : Whether to produce a multiline output. The output may still wrap across
multiple lines if it would otherwise exceed `columnWidth`.
`columnWidth` (default `100`)
diff --git a/nixos/doc/manual/development/testing-hardware-features.section.md b/nixos/doc/manual/development/testing-hardware-features.section.md
new file mode 100644
index 000000000000..aaf652d731f7
--- /dev/null
+++ b/nixos/doc/manual/development/testing-hardware-features.section.md
@@ -0,0 +1,152 @@
+# Testing Hardware Features {#sec-nixos-test-testing-hardware-features}
+
+This section covers how to test various features using NixOS tests that would
+normally only be possible with hardware. It is designed to showcase the NixOS test
+framework's flexibility when combined with various hardware simulation libraries
+or kernel modules.
+
+## Wi-Fi {#sec-nixos-test-wifi}
+
+Use `services.vwifi` to set up a virtual Wi-Fi physical layer. Create at least two nodes
+for this kind of test: one with vwifi active, and either a station or an access point.
+Give each a static IP address on the test network so they will never collide.
+This module likely supports other topologies too; document them if you make one.
+
+This NixOS module leverages [vwifi](https://github.com/Raizo62/vwifi). Read the
+upstream repository's documentation for more information.
+
+### vwifi server {#sec-nixos-test-wifi-vwifi-server}
+
+This node runs the vwifi server, and otherwise does not interact with the network.
+You can run `vwifi-ctrl` on this node to control characteristics of the simulated
+physical layer.
+
+```nix
+airgap =
+ { config, ... }:
+ {
+ networking.interfaces.eth1.ipv4.addresses = lib.mkForce [
+ {
+ address = "192.168.1.2";
+ prefixLength = 24;
+ }
+ ];
+ services.vwifi = {
+ server = {
+ enable = true;
+ ports.tcp = 8212;
+ # uncomment if you want to enable monitor mode on another node
+ # ports.spy = 8213;
+ openFirewall = true;
+ };
+ };
+ };
+```
+
+### AP {#sec-nixos-test-wifi-ap}
+
+A node like this will act as a wireless access point in infrastructure mode.
+
+```nix
+ap =
+ { config, ... }:
+ {
+ networking.interfaces.eth1.ipv4.addresses = lib.mkForce [
+ {
+ address = "192.168.1.3";
+ prefixLength = 24;
+ }
+ ];
+ services.hostapd = {
+ enable = true;
+ radios.wlan0 = {
+ channel = 1;
+ networks.wlan0 = {
+ ssid = "NixOS Test Wi-Fi Network";
+ authentication = {
+ mode = "wpa3-sae";
+ saePasswords = [ { password = "supersecret"; } ];
+ enableRecommendedPairwiseCiphers = true;
+ };
+ };
+ };
+ };
+ services.vwifi = {
+ module = {
+ enable = true;
+ macPrefix = "74:F8:F6:00:01";
+ };
+ client = {
+ enable = true;
+ serverAddress = "192.168.1.2";
+ };
+ };
+ };
+```
+
+### Station {#sec-nixos-test-wifi-station}
+
+A node like this acts as a wireless client.
+
+```nix
+station =
+ { config, ... }:
+ {
+ networking.interfaces.eth1.ipv4.addresses = lib.mkForce [
+ {
+ address = "192.168.1.3";
+ prefixLength = 24;
+ }
+ ];
+ networking.wireless = {
+ # No, really, we want it enabled!
+ enable = lib.mkOverride 0 true;
+ interfaces = [ "wlan0" ];
+ networks = {
+ "NixOS Test Wi-Fi Network" = {
+ psk = "supersecret";
+ authProtocols = [ "SAE" ];
+ };
+ };
+ };
+ services.vwifi = {
+ module = {
+ enable = true;
+ macPrefix = "74:F8:F6:00:02";
+ };
+ client = {
+ enable = true;
+ serverAddress = "192.168.1.2";
+ };
+ };
+ };
+```
+
+### Monitor {#sec-nixos-test-wifi-monitor}
+
+When the monitor mode interface is enabled, this node will receive
+all packets broadcast by all other nodes through the spy interface.
+
+```nix
+monitor =
+ { config, ... }:
+ {
+ networking.interfaces.eth1.ipv4.addresses = lib.mkForce [
+ {
+ address = "192.168.1.4";
+ prefixLength = 24;
+ }
+ ];
+
+ services.vwifi = {
+ module = {
+ enable = true;
+ macPrefix = "74:F8:F6:00:03";
+ };
+ client = {
+ enable = true;
+ spy = true;
+ serverAddress = "192.168.1.2";
+ };
+ };
+```
diff --git a/nixos/doc/manual/development/writing-nixos-tests.section.md b/nixos/doc/manual/development/writing-nixos-tests.section.md
index bd588e2ba80b..5b08975e5ea4 100644
--- a/nixos/doc/manual/development/writing-nixos-tests.section.md
+++ b/nixos/doc/manual/development/writing-nixos-tests.section.md
@@ -121,8 +121,7 @@ and checks that the output is more-or-less correct:
```py
machine.start()
machine.wait_for_unit("default.target")
-if not "Linux" in machine.succeed("uname"):
- raise Exception("Wrong OS")
+t.assertIn("Linux", machine.succeed("uname"), "Wrong OS")
```
The first line is technically unnecessary; machines are implicitly started
@@ -134,6 +133,8 @@ starting them in parallel:
start_all()
```
+Under the variable `t`, all assertions from [`unittest.TestCase`](https://docs.python.org/3/library/unittest.html) are available.
+
If the hostname of a node contains characters that can't be used in a
Python variable name, those characters will be replaced with
underscores in the variable name, so `nodes.machine-a` will be exposed
diff --git a/nixos/doc/manual/installation/building-images-via-nixos-rebuild-build-image.chapter.md b/nixos/doc/manual/installation/building-images-via-nixos-rebuild-build-image.chapter.md
index 378b1163a6e3..075a8fca3bbb 100644
--- a/nixos/doc/manual/installation/building-images-via-nixos-rebuild-build-image.chapter.md
+++ b/nixos/doc/manual/installation/building-images-via-nixos-rebuild-build-image.chapter.md
@@ -2,15 +2,30 @@
Nixpkgs contains a variety of modules to build custom images for different virtualization platforms and cloud providers, such as e.g. `amazon-image.nix` and `proxmox-lxc.nix`.
-While those can be imported individually, `system.build.images` provides an attribute set mapping variant names to image derivations. Available variants are defined - end extendable - in `image.modules`, an attribute set mapping variant names to a list of NixOS modules.
+While those can be imported directly, `system.build.images` provides an attribute set mapping variant names to image derivations. Available variants are defined - end extendable - in `image.modules`, an attribute set mapping variant names to NixOS modules.
-All of those images can be built via both, their `system.build.image` attribute, and the CLI `nixos-rebuild build-image`. To build i.e. an Amazon image from your existing NixOS configuration:
+All of those images can be built via both, their `system.build.image` attribute and the `nixos-rebuild build-image` command.
+
+For example, to build an Amazon image from your existing NixOS configuration, run:
```ShellSession
$ nixos-rebuild build-image --image-variant amazon
-$ ls result
-nixos-image-amazon-25.05pre-git-x86_64-linux.vhd nix-support
+[...]
+Done. The disk image can be found in /nix/store/[hash]-nixos-image-amazon-25.05pre-git-x86_64-linux/nixos-image-amazon-25.05pre-git-x86_64-linux.vpc
```
To get a list of all variants available, run `nixos-rebuild build-image` without arguments.
+::: {.example #ex-nixos-rebuild-build-image-customize}
+
+## Customize specific image variants {#sec-image-nixos-rebuild-build-image-customize}
+
+The `image.modules` option can be used to set specific options per image variant, in a similar fashion as [specialisations](options.html#opt-specialisation) for generic NixOS configurations.
+
+E.g. images for the cloud provider Linode use `grub2` as a bootloader by default. If you are using `systemd-boot` on other platforms and want to disable it for Linode only, you could use the following options:
+
+``` nix
+ image.modules.linode = {
+ boot.loader.systemd-boot.enable = lib.mkForce false;
+ };
+```
diff --git a/nixos/doc/manual/installation/installing-virtualbox-guest.section.md b/nixos/doc/manual/installation/installing-virtualbox-guest.section.md
index a887a923e57f..19713761e9a0 100644
--- a/nixos/doc/manual/installation/installing-virtualbox-guest.section.md
+++ b/nixos/doc/manual/installation/installing-virtualbox-guest.section.md
@@ -8,7 +8,7 @@ up a VirtualBox guest, follow these instructions:
1. Base Memory Size: 768 MB or higher.
-1. New Hard Disk of 8 GB or higher.
+1. New Hard Disk of 10 GB or higher.
1. Mount the CD-ROM with the NixOS ISO (by clicking on CD/DVD-ROM)
diff --git a/nixos/doc/manual/redirects.json b/nixos/doc/manual/redirects.json
index 65520e2afb7d..6f5bbe8b01b5 100644
--- a/nixos/doc/manual/redirects.json
+++ b/nixos/doc/manual/redirects.json
@@ -2,6 +2,15 @@
"book-nixos-manual": [
"index.html#book-nixos-manual"
],
+ "module-services-anubis": [
+ "index.html#module-services-anubis"
+ ],
+ "module-services-anubis-configuration": [
+ "index.html#module-services-anubis-configuration"
+ ],
+ "module-services-anubis-quickstart": [
+ "index.html#module-services-anubis-quickstart"
+ ],
"module-services-crab-hole": [
"index.html#module-services-crab-hole"
],
@@ -41,6 +50,12 @@
"module-services-crab-hole-upstream-options": [
"index.html#module-services-crab-hole-upstream-options"
],
+ "module-services-opencloud": [
+ "index.html#module-services-opencloud"
+ ],
+ "module-services-opencloud-basic-usage": [
+ "index.html#module-services-opencloud-basic-usage"
+ ],
"module-services-strfry": [
"index.html#module-services-strfry"
],
@@ -50,12 +65,21 @@
"module-services-strfry-reverse-proxy": [
"index.html#module-services-strfry-reverse-proxy"
],
+ "module-services-dump1090-fa": [
+ "index.html#module-services-dump1090-fa"
+ ],
+ "module-services-dump1090-fa-configuration": [
+ "index.html#module-services-dump1090-fa-configuration"
+ ],
"preface": [
"index.html#preface"
],
"ch-installation": [
"index.html#ch-installation"
],
+ "sec-contributing-development-env": [
+ "index.html#sec-contributing-development-env"
+ ],
"sec-mattermost": [
"index.html#sec-mattermost"
],
@@ -68,6 +92,21 @@
"sec-mattermost-plugins-build": [
"index.html#sec-mattermost-plugins-build"
],
+ "sec-nixos-test-wifi": [
+ "index.html#sec-nixos-test-wifi"
+ ],
+ "sec-nixos-test-wifi-ap": [
+ "index.html#sec-nixos-test-wifi-ap"
+ ],
+ "sec-nixos-test-wifi-monitor": [
+ "index.html#sec-nixos-test-wifi-monitor"
+ ],
+ "sec-nixos-test-wifi-station": [
+ "index.html#sec-nixos-test-wifi-station"
+ ],
+ "sec-nixos-test-wifi-vwifi-server": [
+ "index.html#sec-nixos-test-wifi-vwifi-server"
+ ],
"sec-obtaining": [
"index.html#sec-obtaining"
],
@@ -137,6 +176,9 @@
"ex-config": [
"index.html#ex-config"
],
+ "ex-nixos-rebuild-build-image-customize": [
+ "index.html#ex-nixos-rebuild-build-image-customize"
+ ],
"sec-installation-additional-notes": [
"index.html#sec-installation-additional-notes"
],
@@ -191,6 +233,9 @@
"sec-image-nixos-rebuild-build-image": [
"index.html#sec-image-nixos-rebuild-build-image"
],
+ "sec-image-nixos-rebuild-build-image-customize": [
+ "index.html#sec-image-nixos-rebuild-build-image-customize"
+ ],
"sec-image-repart": [
"index.html#sec-image-repart"
],
@@ -1250,6 +1295,12 @@
"module-services-postgres-initializing-extra-permissions-service-user-oneshot": [
"index.html#module-services-postgres-initializing-extra-permissions-service-user-oneshot"
],
+ "module-services-postgres-authentication": [
+ "index.html#module-services-postgres-authentication"
+ ],
+ "module-services-postgres-authentication-user-mapping": [
+ "index.html#module-services-postgres-authentication-user-mapping"
+ ],
"module-services-postgres-upgrading": [
"index.html#module-services-postgres-upgrading"
],
@@ -1262,6 +1313,9 @@
"module-services-postgres-plugins": [
"index.html#module-services-postgres-plugins"
],
+ "module-services-postgres-pls": [
+ "index.html#module-services-postgres-pls"
+ ],
"module-services-postgres-jit": [
"index.html#module-services-postgres-jit"
],
@@ -1664,6 +1718,9 @@
"sec-option-definitions-merging": [
"index.html#sec-option-definitions-merging"
],
+ "sec-option-definitions-definitions": [
+ "index.html#sec-option-definitions-definitions"
+ ],
"sec-assertions": [
"index.html#sec-assertions"
],
@@ -1781,6 +1838,12 @@
"sec-test-options-reference": [
"index.html#sec-test-options-reference"
],
+ "test-opt-sshBackdoor.enable": [
+ "index.html#test-opt-sshBackdoor.enable"
+ ],
+ "test-opt-sshBackdoor.vsockOffset": [
+ "index.html#test-opt-sshBackdoor.vsockOffset"
+ ],
"test-opt-defaults": [
"index.html#test-opt-defaults"
],
@@ -1820,6 +1883,9 @@
"test-opt-meta.platforms": [
"index.html#test-opt-meta.platforms"
],
+ "test-opt-meta.hydraPlatforms": [
+ "index.html#test-opt-meta.hydraPlatforms"
+ ],
"test-opt-meta.timeout": [
"index.html#test-opt-meta.timeout"
],
@@ -1868,6 +1934,9 @@
"sec-nixos-test-shell-access": [
"index.html#sec-nixos-test-shell-access"
],
+ "sec-nixos-test-ssh-access": [
+ "index.html#sec-nixos-test-ssh-access"
+ ],
"sec-nixos-test-port-forwarding": [
"index.html#sec-nixos-test-port-forwarding"
],
@@ -1880,6 +1949,9 @@
"sec-linking-nixos-tests-to-packages": [
"index.html#sec-linking-nixos-tests-to-packages"
],
+ "sec-nixos-test-testing-hardware-features": [
+ "index.html#sec-nixos-test-testing-hardware-features"
+ ],
"chap-developing-the-test-driver": [
"index.html#chap-developing-the-test-driver"
],
@@ -1916,6 +1988,21 @@
"ch-release-notes": [
"release-notes.html#ch-release-notes"
],
+ "sec-release-25.11": [
+ "release-notes.html#sec-release-25.11"
+ ],
+ "sec-release-25.11-highlights": [
+ "release-notes.html#sec-release-25.11-highlights"
+ ],
+ "sec-release-25.11-new-modules": [
+ "release-notes.html#sec-release-25.11-new-modules"
+ ],
+ "sec-release-25.11-incompatibilities": [
+ "release-notes.html#sec-release-25.11-incompatibilities"
+ ],
+ "sec-release-25.11-notable-changes": [
+ "release-notes.html#sec-release-25.11-notable-changes"
+ ],
"sec-release-25.05": [
"release-notes.html#sec-release-25.05"
],
@@ -1931,6 +2018,9 @@
"sec-release-25.05-notable-changes": [
"release-notes.html#sec-release-25.05-notable-changes"
],
+ "sec-release-25.05-wiki": [
+ "release-notes.html#sec-release-25.05-wiki"
+ ],
"sec-nixpkgs-release-25.05": [
"release-notes.html#sec-nixpkgs-release-25.05"
],
diff --git a/nixos/doc/manual/release-notes/release-notes.md b/nixos/doc/manual/release-notes/release-notes.md
index 7110603c44bc..3889f4890f66 100644
--- a/nixos/doc/manual/release-notes/release-notes.md
+++ b/nixos/doc/manual/release-notes/release-notes.md
@@ -3,6 +3,7 @@
This section lists the release notes for each stable version of NixOS and current unstable revision.
```{=include=} sections
+rl-2511.section.md
rl-2505.section.md
rl-2411.section.md
rl-2405.section.md
diff --git a/nixos/doc/manual/release-notes/rl-2205.section.md b/nixos/doc/manual/release-notes/rl-2205.section.md
index 486b5f542ff4..c21e36403161 100644
--- a/nixos/doc/manual/release-notes/rl-2205.section.md
+++ b/nixos/doc/manual/release-notes/rl-2205.section.md
@@ -101,7 +101,7 @@ In addition to numerous new and upgraded packages, this release has the followin
- [InvoicePlane](https://invoiceplane.com), web application for managing and creating invoices. Available at [services.invoiceplane](#opt-services.invoiceplane.sites._name_.enable).
-- [k3b](https://userbase.kde.org/K3b), the KDE disk burning application. Available as programs.k3b.
+- [k3b](https://userbase.kde.org/K3b), the KDE disk burning application. Available as [programs.k3b](#opt-programs.k3b.enable).
- [K40-Whisperer](https://www.scorchworks.com/K40whisperer/k40whisperer.html), a program to control cheap Chinese laser cutters. Available as [programs.k40-whisperer.enable](#opt-programs.k40-whisperer.enable). Users must add themselves to the `k40` group to be able to access the device.
diff --git a/nixos/doc/manual/release-notes/rl-2405.section.md b/nixos/doc/manual/release-notes/rl-2405.section.md
index 0a4aedba87fc..b11f36719035 100644
--- a/nixos/doc/manual/release-notes/rl-2405.section.md
+++ b/nixos/doc/manual/release-notes/rl-2405.section.md
@@ -139,7 +139,7 @@ The pre-existing `services.ankisyncd` has been marked deprecated and will be dro
- [Netbird](https://netbird.io), an open-source VPN management platform, now has a self-hosted management server. Available as [services.netbird.server](#opt-services.netbird.server.enable).
-- [nh](https://github.com/viperML/nh), yet another Nix CLI helper. Available as [programs.nh](#opt-programs.nh.enable).
+- [nh](https://github.com/nix-community/nh), yet another Nix CLI helper. Available as [programs.nh](#opt-programs.nh.enable).
- [oink](https://github.com/rlado/oink), a dynamic DNS client for Porkbun. Available as [services.oink](#opt-services.oink.enable).
diff --git a/nixos/doc/manual/release-notes/rl-2505.section.md b/nixos/doc/manual/release-notes/rl-2505.section.md
index f531ff61af86..b70490d0de93 100644
--- a/nixos/doc/manual/release-notes/rl-2505.section.md
+++ b/nixos/doc/manual/release-notes/rl-2505.section.md
@@ -1,42 +1,41 @@
-# Nixos 25.05 (“Warbler”, 2025.05/??) {#sec-release-25.05}
+# NixOS 25.05 (“Warbler”, 2025.05/??) {#sec-release-25.05}
## Highlights {#sec-release-25.05-highlights}
-- Initial support for the [COSMIC DE](https://system76.com/cosmic), a Rust-based desktop environment by System76, makers of Pop!_OS. Toggle the greeter (login manager) using `services.displayManager.cosmic-greeter.enable` and the DE itself with `services.desktopManager.cosmic.enable`. Mostly stable but still experimental. Please report any issues to the [COSMIC DE tracker in Nixpkgs](https://github.com/NixOS/nixpkgs/issues/259641) instead of upstream.
+Alongside many enhancements to NixOS modules and general system improvements, this release features the following highlights:
-- `services.dex` now restarts upon changes to the `.environmentFile` or entries in `.settings.staticClients[].secretFile` when the entry is a `path` type.
+- NixOS now has initial support for the [**COSMIC DE**](https://system76.com/cosmic) which is currently at **Alpha 7**. COSMIC is a Rust-based Desktop Environment by System76, makers of Pop!_OS. You can use COSMIC by enabling the greeter (login manager) with [](#opt-services.displayManager.cosmic-greeter.enable), and the DE itself by enabling [](#opt-services.desktopManager.cosmic.enable). The support in NixOS/Nixpkgs is stable but still considered experimental because of the recent the addition. The COSMIC maintainers will be waiting for one more release of NixOS to determine if the experimental tag should be removed or not. Until then, please report any issues to the [COSMIC DE tracker in Nixpkgs](https://github.com/NixOS/nixpkgs/issues/259641) instead of upstream.
-- `nixos-rebuild-ng`, a full rewrite of `nixos-rebuild` in Python, is available for testing. You can enable it by setting [system.rebuild.enableNg](options.html#opt-system.rebuild.enableNg) in your configuration (this will replace the old `nixos-rebuild`), or by adding `nixos-rebuild-ng` to your `environment.systemPackages` (in this case, it will live side-by-side with `nixos-rebuild` as `nixos-rebuild-ng`). It is expected that the next major version of NixOS (25.11) will enable `system.rebuild.enableNg` by default.
-
-- The `nixos-generate-config` command now supports a optional `--flake` option, which will generate a flake.nix file alongside the `configuration.nix` and `hardware-configuration.nix`, providing an easy instroduction into flake-based system configurations.
+- `nixos-rebuild-ng`, a full rewrite of `nixos-rebuild` in Python, is available for testing. You can enable it by setting [](#opt-system.rebuild.enableNg) in your configuration (this will replace the old `nixos-rebuild`), or by adding `nixos-rebuild-ng` to your `environment.systemPackages` (in this case, it will live side-by-side with `nixos-rebuild` as `nixos-rebuild-ng`). It is expected that the next major version of NixOS (25.11) will enable `system.rebuild.enableNg` by default.
- A `nixos-rebuild build-image` sub-command has been added.
- It allows users to build platform-specific (disk) images from their NixOS configurations. `nixos-rebuild build-image` works similar to the popular [nix-community/nixos-generators](https://github.com/nix-community/nixos-generators) project. See new [section on image building in the NixOS manual](https://nixos.org/manual/nixos/unstable/#sec-image-nixos-rebuild-build-image). It is also available for `nixos-rebuild-ng`.
+ It allows users to build platform-specific (disk) images from their NixOS configurations. `nixos-rebuild build-image` works similar to the popular [nix-community/nixos-generators](https://github.com/nix-community/nixos-generators) project. See new [section on image building in the NixOS manual](#sec-image-nixos-rebuild-build-image). It is also available for `nixos-rebuild-ng`.
- `nixos-option` has been rewritten to a Nix expression called by a simple bash script. This lowers our maintenance threshold, makes eval errors less verbose, adds support for flake-based configurations, descending into `attrsOf` and `listOf` submodule options, and `--show-trace`.
-- The `intel` video driver for X.org (from the xf86-video-intel package) which was previously removed because it was non-functional has been fixed and the driver has been re-introduced.
+- The packaging of Mesa graphics drivers has been significantly reworked, in particular:
+ - Applications linked against different Mesa versions than installed on the system should now work correctly going forward (however, applications against older Mesa, e.g. from Nixpkgs releases before 25.05, remain broken)
+ - The global Mesa version can now be managed without a mass rebuild by setting [](#opt-hardware.graphics.package)
+ - Packages that used to depend on Mesa for libgbm or libdri should use `libgbm` or `dri-pkgconfig-stub` as inputs, respectively
-- The Mattermost module ({option}`services.mattermost`) and packages (`mattermost` and `mmctl`) have been substantially updated:
- - {option}`services.mattermost.preferNixConfig` now defaults to true if you advance {option}`system.stateVersion` to 25.05. This means that if you have {option}`services.mattermost.mutableConfig` set, NixOS will override your settings to those that you define in the module. It is recommended to leave this at the default, even if you used a mutable config before, because it will ensure that your Mattermost data directories are correct. If you moved your data directories, you may want to review the module changes before upgrading.
- - Mattermost telemetry reporting is now disabled by default, though security update notifications are enabled. Look at {option}`services.mattermost.telemetry` for options to control this behavior.
- - `pkgs.mattermostLatest` is now an option to track the latest (non-prerelease) Mattermost release. We test upgrade migrations from ESR releases (`pkgs.mattermost`) to `pkgs.mattermostLatest`.
- - The Mattermost frontend is now built from source and can be overridden.
- - Note that the Mattermost derivation containing both the webapp and server is now wrapped to allow them to be built independently, so overrides to both webapp and server look like `mattermost.overrideAttrs (prev: { webapp = prev.webapp.override { ... }; server = prev.server.override { ... }; })` now.
- - `services.mattermost.listenAddress` has been split into {option}`services.mattermost.host` and {option}`services.mattermost.port`. If your `listenAddress` contained a port, you will need to edit your configuration.
- - Mattermost now supports peer authentication on both MySQL and Postgres database backends. Updating {option}`system.stateVersion` to 25.05 or later will result in peer authentication being used by default if the Mattermost server would otherwise be connecting to localhost. This is the recommended configuration.
- - The Mattermost module will produce eval warnings if a database password would end up in the Nix store, and recommend alternatives such as peer authentication or using the environment file.
- - Mattermost's entire test suite is now enabled by default, which will extend build time from sources by up to an hour. A `withoutTests` passthru has been added in case you want to skip it.
- - We now support `mmctl` for Mattermost administration if both {option}`services.mattermost.socket.enable` and {option}`services.mattermost.socket.export` are set, which export the Mattermost control socket path into the system environment.
- - A new `pkgs.mattermost.buildPlugin` function has been added, which allows plugins to be built from source, including webapp frontends with a supported package-lock.json. See the Mattermost NixOS test and [manual](https://nixos.org/manual/nixpkgs/unstable/#sec-mattermost-plugins-build) for an example.
- - Note that the Mattermost module will create an account _without_ a well-known UID if the username differs from the default (`mattermost`). If you used Mattermost with a nonstandard username, you may want to review the module changes before upgrading.
+- OpenSSH has been updated from 9.9p2 to 10.0p2, dropping support for DSA keys and adding a new `ssh-auth` binary to handle user authentication in a different address space from unauthenticated sessions. Additionally, we now enable a configure option by default that attempts to lock sshd into RAM to prevent it from being swapped out, which may improve performance if the system is under memory pressure. See the [full changelog](https://www.openwall.com/lists/oss-security/2025/04/09/1) for more details.
-- androidenv has been updated:
- - All versions specified in composeAndroidPackages now track latest. Android packages are automatically updated on unstable, and run the androidenv test suite on every update.
- - Some androidenv packages are now searchable on [search.nixos.org](https://search.nixos.org).
- - We now use the latest Google repositories, which should improve aarch64-darwin compatibility. The SDK now additionally evaluates on aarch64-linux, though not all packages are functional.
+- GNOME has been updated to version 48.
+
+ - `decibels` music player is now installed by default. You can disable it using [](#opt-environment.gnome.excludePackages).
+ - `gnome-shell-extensions` extension collection (which included GNOME Classic extensions, Apps Menu, and User Themes, among others) are no longer installed by default. You can install them again with [](#opt-services.xserver.desktopManager.gnome.sessionPath).
+ - Option [](#opt-services.gnome.core-developer-tools.enable) now also installs `sysprof` and `d-spy`.
+ - Option `services.gnome.core-utilities.enable` has been renamed to [](#opt-services.gnome.core-apps.enable).
+ - `cantarell-fonts`, `source-code-pro` and `source-sans` fonts are no longer installed by default. They have been replaced by `adwaita-fonts`.
+
+ Refer to the [GNOME release notes](https://release.gnome.org/48/) for more details.
+
+- [channels.nixos.org](https://channels.nixos.org) now supports the Lockable HTTP Tarball Protocol. This allows using the channel `nixexprs.tar` as Nix Flake input, e.g.:
+ ```
+ inputs.nixpkgs.url = "https://channels.nixos.org/nixos-25.05/nixexprs.tar.xz";
+ ```
@@ -44,18 +43,25 @@
-- [AmneziaVPN](https://amnezia.org/en), an open-source VPN client, with a key feature that enables you to deploy your own VPN server on your server. Available as [programs.amnezia-vpn](#opt-programs.amnezia-vpn.enable).
+- [AmneziaVPN](https://amnezia.org/en), a self-hostable open-source VPN client, is available in two variants:
+ - [programs.amnezia-vpn](#opt-programs.amnezia-vpn.enable): a GUI client which can also deploy a VPN endpoint to a remote server
+ - {option}`networking.wireguard` adds support for the [AmneziaWG](https://docs.amnezia.org/documentation/amnezia-wg/) variant of the protocol, featuring better masking against Deep Packet Inspection. The variant to be used is set per interface as `networking.wireguard.interfaces..type`, defaulting to wireguard.
+
- [Bazecor](https://github.com/Dygmalab/Bazecor), the graphical configurator for Dygma Products.
- [Bonsai](https://git.sr.ht/~stacyharper/bonsai), a general-purpose event mapper/state machine primarily used to create complex key shortcuts, and as part of the [SXMO](https://sxmo.org/) desktop environment. Available as [services.bonsaid](#opt-services.bonsaid.enable).
-- [archtika](https://github.com/archtika/archtika), a FLOSS, modern, performant, lightweight and self‑hosted CMS. Available as [services.archtika](#opt-services.archtika.enable).
-
- [scanservjs](https://github.com/sbs20/scanservjs/), a web UI for SANE scanners. Available at [services.scanservjs](#opt-services.scanservjs.enable).
- [Kimai](https://www.kimai.org/), a web-based multi-user time-tracking application. Available as [services.kimai](options.html#opt-services.kimai).
+- [Kismet](https://www.kismetwireless.net/), a Wi-Fi, Bluetooth, and RF monitoring application supporting a wide range of hardware. Available as {option}`services.kismet`.
+
+- [vwifi](https://github.com/Raizo62/vwifi), a Wi-Fi simulator daemon leveraging the `mac80211_hwsim` and `vhost_vsock` kernel modules for efficient simulation of multi-node Wi-Fi networks. Available as {option}`services.vwifi`.
+
+- [Oncall](https://oncall.tools), a web-based calendar tool designed for scheduling and managing on-call shifts. Available as [services.oncall](options.html#opt-services.oncall).
+
- [Homer](https://homer-demo.netlify.app/), a very simple static homepage for your server. Available as [services.homer](options.html#opt-services.homer).
- [Ghidra](https://ghidra-sre.org/), a software reverse engineering (SRE) suite of tools. Available as [programs.ghidra](options.html#opt-programs.ghidra).
@@ -74,6 +80,10 @@
- [MaryTTS](https://github.com/marytts/marytts), an open-source, multilingual text-to-speech synthesis system written in pure Java. Available as [services.marytts](options.html#opt-services.marytts).
+- [Continuwuity](https://continuwuity.org/), a federated chat server implementing the Matrix protocol, forked from Conduwuit. Available as [services.matrix-continuwuity](#opt-services.matrix-continuwuity.enable).
+
+- [Reposilite](https://reposilite.com), a lightweight and easy-to-use repository manager for Maven-based artifacts in the JVM ecosystem. Available as [services.reposilite](options.html#opt-services.reposilite).
+
- [networking.modemmanager](options.html#opt-networking.modemmanager) has been split out of [networking.networkmanager](options.html#opt-networking.networkmanager). NetworkManager still enables ModemManager by default, but options exist now to run NetworkManager without ModemManager.
- [Routinator 3000](https://nlnetlabs.nl/projects/routing/routinator/), a full-featured RPKI Relying Party software package that runs as a service which periodically downloads and verifies RPKI data.
@@ -82,8 +92,6 @@
- [ncps](https://github.com/kalbasit/ncps), a Nix binary cache proxy service implemented in Go using [go-nix](https://github.com/nix-community/go-nix). Available as [services.ncps](options.html#opt-services.ncps.enable).
-- [Conduwuit](https://conduwuit.puppyirl.gay/), a federated chat server implementing the Matrix protocol, forked from Conduit. Available as [services.conduwuit](#opt-services.conduwuit.enable).
-
- [Readeck](https://readeck.org/), a read-it later web-application. Available as [services.readeck](#opt-services.readeck.enable).
- [Traccar](https://www.traccar.org/), a modern GPS Tracking Platform. Available as [services.traccar](#opt-services.traccar.enable).
@@ -108,6 +116,8 @@
- [PostgREST](https://postgrest.org), a standalone web server that turns your PostgreSQL database directly into a RESTful API. Available as [services.postgrest](options.html#opt-services.postgrest.enable).
+- [postgres-websockets](https://github.com/diogob/postgres-websockets), a middleware that adds websockets capabilities on top of PostgreSQL's asynchronous notifications using LISTEN and NOTIFY commands. Available as [services.postgres-websockets](options.html#opt-services.postgres-websockets.enable).
+
- [µStreamer](https://github.com/pikvm/ustreamer), a lightweight MJPEG-HTTP streamer. Available as [services.ustreamer](options.html#opt-services.ustreamer).
- [Whoogle Search](https://github.com/benbusby/whoogle-search), a self-hosted, ad-free, privacy-respecting metasearch engine. Available as [services.whoogle-search](options.html#opt-services.whoogle-search.enable).
@@ -134,7 +144,7 @@
- [Zoxide](https://github.com/ajeetdsouza/zoxide), a smarter cd command, inspired by z and autojump. Available as [programs.zoxide](options.html#opt-programs.zoxide.enable)
-- [victorialogs][https://docs.victoriametrics.com/victorialogs/], log database from VictoriaMetrics. Available as [services.victorialogs](#opt-services.victorialogs.enable)
+- [victorialogs](https://docs.victoriametrics.com/victorialogs/), log database from VictoriaMetrics. Available as [services.victorialogs](#opt-services.victorialogs.enable)
- [gokapi](https://github.com/Forceu/Gokapi), Lightweight selfhosted Firefox Send alternative without public upload. AWS S3 supported. Available with [services.gokapi](options.html#opt-services.gokapi.enable)
@@ -154,6 +164,12 @@
- [GlitchTip](https://glitchtip.com/), an open source Sentry API compatible error tracking platform. Available as [services.glitchtip](#opt-services.glitchtip.enable).
+- [`yarr`](https://github.com/nkanaev/yarr), a small, web-based feed aggregator and RSS reader. Available as [services.yarr](#opt-services.yarr.enable).
+
+- [OliveTin](https://www.olivetin.app/), gives safe and simple access to predefined shell commands from a web interface. Available as [services.olivetin](#opt-services.olivetin.enable).
+
+- [alertmanager-ntfy](https://github.com/alexbakker/alertmanager-ntfy), forwards Prometheus Alertmanager notifications to ntfy.sh. Available as [services.prometheus.alertmanager-ntfy](#opt-services.prometheus.alertmanager-ntfy.enable).
+
- [Stash](https://github.com/stashapp/stash), An organizer for your adult videos/images, written in Go. Available as [services.stash](#opt-services.stash.enable).
- [vsmartcard-vpcd](https://frankmorgner.github.io/vsmartcard/virtualsmartcard/README.html), a virtual smart card driver. Available as [services.vsmartcard-vpcd](#opt-services.vsmartcard-vpcd.enable).
@@ -162,22 +178,34 @@
- [PDS](https://github.com/bluesky-social/pds), Personal Data Server for [bsky](https://bsky.social/). Available as [services.pds](option.html#opt-services.pds).
+- [Anubis](https://github.com/TecharoHQ/anubis), a scraper defense software. Available as [services.anubis](options.html#opt-services.anubis).
+
- [synapse-auto-compressor](https://github.com/matrix-org/rust-synapse-compress-state?tab=readme-ov-file#automated-tool-synapse_auto_compressor), a rust-based matrix-synapse state compressor for postgresql. Available as [services.synapse-auto-compressor](#opt-services.synapse-auto-compressor.enable).
- [mqtt-exporter](https://github.com/kpetremann/mqtt-exporter/), a Prometheus exporter for exposing messages from MQTT. Available as [services.prometheus.exporters.mqtt](#opt-services.prometheus.exporters.mqtt.enable).
+- [pocket-id](https://pocket-id.org/), an OIDC provider with passkeys support. Available as [services.pocket-id](#opt-services.pocket-id.enable).
+
- [nvidia-gpu](https://github.com/utkuozdemir/nvidia_gpu_exporter), a Prometheus exporter that scrapes `nvidia-smi` for GPU metrics. Available as [services.prometheus.exporters.nvidia-gpu](#opt-services.prometheus.exporters.nvidia-gpu.enable).
+- [Lavalink](https://github.com/lavalink-devs/Lavalink), a standalone audio sending node based on Lavaplayer and Koe. Available as [services.lavalink](#opt-services.lavalink.enable).
+
- [OpenGamepadUI](https://github.com/ShadowBlip/OpenGamepadUI/), an open source gamepad-native game launcher and overlay for Linux. Available as [programs.opengamepadui](#opt-programs.opengamepadui.enable).
- [InputPlumber](https://github.com/ShadowBlip/InputPlumber/), an open source input router and remapper daemon for Linux. Available as [services.inputplumber](#opt-services.inputplumber.enable).
+- [`dump1090-fa`](https://github.com/flightaware/dump1090), a simple Mode S decoder for RTLSDR devices with a web interface. Available as [services.dump1090-fa](#opt-services.dump1090-fa.enable).
+
- [PowerStation](https://github.com/ShadowBlip/PowerStation/), an open source TDP control and performance daemon with DBus interface for Linux. Available as [services.powerstation](#opt-services.powerstation.enable).
- [`g3proxy`](https://github.com/bytedance/g3), an open source enterprise forward proxy from ByteDance, similar to Squid or tinyproxy. Available as [services.g3proxy](#opt-services.g3proxy.enable).
+- [OpenCloud](https://opencloud.eu/), an open-source, modern file-sync and sharing platform. It is a fork of oCIS, a ground-up rewrite of the well-known PHP-based NextCloud server. Available as [services.opencloud](#opt-services.opencloud.enable).
+
- [echoip](https://github.com/mpolden/echoip), a simple service for looking up your IP address. Available as [services.echoip](#opt-services.echoip.enable).
+- [whoami](https://github.com/traefik/whoami), a tiny Go server that prints OS information and HTTP request to output. Available as [services.whoami](#opt-services.whoami.enable).
+
- [LiteLLM](https://github.com/BerriAI/litellm), a LLM Gateway to provide model access, fallbacks and spend tracking across 100+ LLMs. All in the OpenAI format. Available as [services.litellm](#opt-services.litellm.enable).
- [Buffyboard](https://gitlab.postmarketos.org/postmarketOS/buffybox/-/tree/master/buffyboard), a framebuffer on-screen keyboard. Available as [services.buffyboard](option.html#opt-services.buffyboard).
@@ -188,6 +216,8 @@
- [GLPI-Agent](https://github.com/glpi-project/glpi-agent), GLPI Agent. Available as [services.glpiAgent](options.html#opt-services.glpiAgent.enable).
+- [pgBackRest](https://pgbackrest.org), a reliable backup and restore solution for PostgreSQL. Available as [services.pgbackrest](options.html#opt-services.pgbackrest.enable).
+
- [Recyclarr](https://github.com/recyclarr/recyclarr) a TRaSH Guides synchronizer for Sonarr and Radarr. Available as [services.recyclarr](#opt-services.recyclarr.enable).
- [Rebuilderd](https://github.com/kpcyrd/rebuilderd) an independent verification of binary packages - Reproducible Builds. Available as [services.rebuilderd](#opt-services.rebuilderd.enable).
@@ -200,9 +230,22 @@
- [Pareto Security](https://paretosecurity.com/) is an alternative to corporate compliance solutions for companies that care about security but know it doesn't have to be invasive. Available as [services.paretosecurity](#opt-services.paretosecurity.enable)
+- [Cursor](https://cursor.com/) is a vscode-based editor that uses AI to help you write code faster.
+
+- [GNU Rush](https://gnu.org/software/rush/) is a Restricted User Shell, designed for systems providing limited remote access to their resources. Available as [programs.rush](#opt-programs.rush.enable).
+
- [ipfs-cluster](https://ipfscluster.io/), Pinset orchestration for IPFS. Available as [services.ipfs-cluster](#opt-services.ipfs-cluster.enable)
- [bitbox-bridge](https://github.com/BitBoxSwiss/bitbox-bridge), a bridge software that connects BitBox hardware wallets to computers & web wallets like [Rabby](https://rabby.io/). Allows one to interact & transact with smart contracts, Web3 websites & financial services without storing private keys anywhere other than the hardware wallet. Available as [services.bitbox-bridge](#opt-services.bitbox-bridge.enable).
+
+- [GoDNS](https://github.com/TimothyYe/godns), a dynamic DNS client written in Go, which supports multiple DNS providers. Available as [services.godns](option.html#opt-services.godns.enable).
+
+- [CookCLI](https://cooklang.org/cli/) Server, a web UI for cooklang recipes.
+
+- [Prometheus eBPF Exporter](https://github.com/cloudflare/ebpf_exporter),
+ Prometheus exporter for custom eBPF metrics. Available as
+ [services.prometheus.exporters.ebpf](#opt-services.prometheus.exporters.ebpf.enable).
+
## Backward Incompatibilities {#sec-release-25.05-incompatibilities}
@@ -232,16 +275,31 @@
[not recommended by upstream](https://docs.nextcloud.com/server/30/admin_manual/installation/system_requirements.html)
and thus doesn't qualify as default.
+- PowerDNS Recursor has been updated to version 5.1.2, which comes with a new YAML configuration format (`recursor.yml`)
+ and deprecates the previous format (`recursor.conf`). Accordingly, the NixOS option `services.pdns-recursor.settings`
+ has been renamed to [old-settings](#opt-services.pdns-recursor.old-settings) and will be provided for backward compatibility
+ until the next NixOS release. Users are asked to migrate their settings to the new [yaml-settings](#opt-services.pdns-recursor.old-settings)
+ option following this [guide](https://doc.powerdns.com/recursor/appendices/yamlconversion.html).
+ Note that options other than `services.pdns-recursor.settings` are unaffacted by this change.
+
- Nextcloud's default FPM pool settings have been increased according to upstream recommentations. It's advised
to review the new defaults and description of
[](#opt-services.nextcloud.poolSettings).
+- In `users.users` subuid allocation on systems with multiple users it could happen that some users' allocated subuid ranges collided with others. Now these users get new subuid ranges assigned. When this happens, a warning is issued on the first activation. If the subuids were used (e.g. with rootless container managers like podman), please change the ownership of affected files accordingly.
+
- The `services.locate` module does no longer support findutil's `locate` due to its inferior performance compared to `mlocate` and `plocate`. The new default is `plocate`.
As the `service.locate.localuser` option only applied when using findutil's `locate`, it has also been removed.
- `services.paperless` now installs `paperless-manage` as a normal system package instead of creating a symlink in `/var/lib/paperless`.
`paperless-manage` now also changes to the appropriate user when being executed.
+- The `gotenberg` package has been updated to 8.16.0, which brings breaking changes to the configuration from version 8.13.0. See the [upstream release notes](https://github.com/gotenberg/gotenberg/releases/tag/v8.13.0)
+ for that release to get all the details. The `services.gotenberg` module has been updated appropriately to ensure your configuration is valid with this new release.
+
+- `varnish` was updated from 7.5.0 to 7.7.0, see [Varnish 7.6.0 upgrade guide](https://varnish-cache.org/docs/7.6/whats-new/upgrading-7.6.html) and
+[Varnish 7.7.0 upgrade guide](https://varnish-cache.org/docs/7.7/whats-new/upgrading-7.7.html#whatsnew-upgrading-7-7).
+
- `asusd` has been upgraded to version 6 which supports multiple aura devices. To account for this, the single `auraConfig` configuration option has been replaced with `auraConfigs` which is an attribute set of config options per each device. The config files may also be now specified as either source files or text strings; to account for this you will need to specify that `text` is used for your existing configs, e.g.:
```diff
-services.asusd.asusdConfig = '''file contents'''
@@ -250,16 +308,24 @@
- `linuxPackages.nvidiaPackages.stable` now defaults to the `production` variant instead of `latest`.
+- `paperless-ngx` has been updated to minor version 2.15 which switched the web server from Gunicorn to Granian. If you set Gunicorn specific envs (usually contain GUNICORN) they must be updated. Also `services.paperless.address` no longer accepts a domain name and Granian also does not support listening on unix domain sockets.
+
- `timescaledb` requires manual upgrade steps.
After you run ALTER EXTENSION, you must run [this SQL script](https://github.com/timescale/timescaledb-extras/blob/master/utils/2.15.X-fix_hypertable_foreign_keys.sql). For more details, see the following pull requests [#6797](https://github.com/timescale/timescaledb/pull/6797).
PostgreSQL 13 is no longer supported in TimescaleDB v2.16.
+- `networking.wireguard.enable = true` does not always add `wireguard-tools` to system packages anymore. Only when wireguard interfaces are configured, the backing implementation packages are added to system PATH.
+
- `virtualisation/azure-common.nix`'s filesystem and grub configurations have been moved to `virtualisation/azure-image.nix`. This makes `azure-common.nix` more generic so it could be used for users who generate Azure image using other methods (e.g. nixos-generators and disko). For existing users depending on these configurations, please also import `azure-image.nix`.
- `zammad` has had its support for MySQL removed, since it was never working correctly and is now deprecated upstream. Check the [migration guide](https://docs.zammad.org/en/latest/appendix/migrate-to-postgresql.html) for how to convert your database to PostgreSQL.
+- `services.signald` has been removed as `signald` is unmaintained upstream and has been incompatible to official Signal servers for a long while.
+
- `tauon` 7.9.0+ when launched for the first time, migrates its database to a new schema that is not backwards compatible. Older versions will refuse to start at all with that database afterwards. If you need to still use older tauon versions, make sure to back up `~/.local/share/TauonMusicBox`.
+- `aws-workspaces` has dropped support for PCoiP networking.
+
- The `earlyoom` service is now using upstream systemd service, which enables
hardening and filesystem isolation by default. If you need filesystem write
access or want to access home directory via `killHook`, hardening setting can
@@ -279,6 +345,60 @@
- `services.bird2` has been renamed to `services.bird` and the default bird package has been switched to `bird3`. `bird2` can still be chosen via the `services.bird.package` option.
+- `renovate` was updated to v39. See the [upstream release notes](https://docs.renovatebot.com/release-notes-for-major-versions/#version-39) for breaking changes.
+ Like upstream's docker images, renovate now runs on NodeJS 22.
+
+- The behavior of the `networking.nat.externalIP` and `networking.nat.externalIPv6` options has been changed. `networking.nat.forwardPorts` now only forwards packets destined for the specified IP addresses.
+
+- `gitlab` has been updated from 17.x to 18.x and requires `postgresql` >= 16, as stated in the [documentation](https://docs.gitlab.com/18.0/install/requirements/#postgresql). Check the [upgrade guide](#module-services-postgres-upgrading) in the NixOS manual on how to upgrade your PostgreSQL installation.
+
+- `services.gitlab` now requires the setting of `activeRecordPrimaryKeyFile`, `activeRecordDeterministicKeyFile`, `activeRecordSaltFile` as GitLab introduced Rails ActiveRecord encryption.
+
+- `python3Packages.bpycv` has been removed due to being incompatible with Blender 4 and unmaintained.
+
+- `python3Packages.jaeger-client` was removed because it was deprecated upstream. [OpenTelemetry](https://opentelemetry.io) is the recommended replacement.
+
+- `rocmPackages_6` has been updated to ROCm 6.3.
+
+- `rocmPackages_5` has been removed.
+
+- `rocmPackages.rocm-thunk` has been removed and its functionality has been integrated with the ROCm CLR. Use `rocmPackages.clr` instead.
+
+- `rocmPackages.clang-ocl` has been removed. [It was deprecated by AMD in 2023.](https://github.com/ROCm/clang-ocl)
+
+- `nodePackages.meshcommander` has been removed, as the package was deprecated by Intel.
+
+- The default version of `z3` has been updated from 4.8 to 4.13. There are still a few packages that need specific older versions; those will continue to be maintained as long as other packages depend on them but may be removed in the future.
+
+- `prometheus` has been updated from 2.55.0 to 3.1.0.
+ Read the [release blog post](https://prometheus.io/blog/2024/11/14/prometheus-3-0/) and
+ [migration guide](https://prometheus.io/docs/prometheus/3.1/migration/).
+
+- The Mattermost module ([`services.mattermost`](#opt-services.mattermost.enable)) and packages (`mattermost` and `mmctl`) have been substantially updated:
+ - `services.mattermost.listenAddress` has been split into [](#opt-services.mattermost.host) and [](#opt-services.mattermost.port). If your `listenAddress` contained a port, you will need to edit your configuration. This will be the only truly breaking change in this release for most configurations.
+ - [](#opt-services.mattermost.preferNixConfig) now defaults to true if you advance [](#opt-system.stateVersion) to 25.05. This means that if you have [](#opt-services.mattermost.mutableConfig) set, NixOS will override settings set in the Admin Console to those that you define in the module configuration. It is recommended to leave this at the default, even if you used a fully mutable configuration before, because it will ensure that your Mattermost data directories are correct. If you moved your data directories, you may want to review the module changes before upgrading.
+ - Mattermost now supports peer authentication on both MySQL and Postgres database backends. Updating [](#opt-system.stateVersion) to 25.05 or later will result in peer authentication being used by default if the Mattermost server would otherwise be connecting to localhost. This is the recommended configuration.
+ - Note that the Mattermost module will create an account _without_ a well-known UID if the username differs from the default (`mattermost`). If you used Mattermost with a nonstandard username, you may want to review the module changes before upgrading.
+
+- `kanata` was updated to v1.8.0, which introduces several breaking changes.
+ See the release notes of
+ [v1.7.0](https://github.com/jtroo/kanata/releases/tag/v1.7.0) and
+ [v1.8.0](https://github.com/jtroo/kanata/releases/tag/v1.8.0)
+ for more information.
+
+- `authelia` version 4.39.0 has made changes on the default claims for ID Tokens, to mirror the standard claims from the specification.
+ This change may affect some clients in unexpected ways, so manual intervention may be required.
+ Read the [release notes](https://www.authelia.com/blog/4.39-release-notes/), along with [the guide](https://www.authelia.com/integration/openid-connect/openid-connect-1.0-claims/#restore-functionality-prior-to-claims-parameter) to work around issues that may be encountered.
+
+- `ags` was updated to v2, which is just a CLI for Astal now. Components are available as a different package set `astal.*`.
+ If you want to use v1, it is available as `ags_1` package.
+
+ See the release notes of
+ [v2.0.0](https://github.com/Aylur/ags/releases/tag/v2.0.0)
+ for more information.
+
+- `nodePackages.expo-cli` has been removed, as it was deprecated by upstream. The suggested replacement is the `npx expo` command.
+
- DokuWiki with the Caddy webserver (`services.dokuwiki.webserver = "caddy"`) now sets up sites with Caddy's automatic HTTPS instead of HTTP-only.
To keep the old behavior for a site `example.com`, set `services.caddy.virtualHosts."example.com".hostName = "http://example.com"`.
If you set custom Caddy options for a DokuWiki site, migrate these options by removing `http://` from `services.caddy.virtualHosts."http://example.com"`.
@@ -287,9 +407,20 @@
Given a site example.com, http://example.com now 301 redirects to https://example.com.
To keep the old behavior for a site `example.com`, set `services.caddy.virtualHosts."example.com".hostName = "http://example.com"`.
+- `slskd` has been updated to v0.22.3, which includes breaking changes to `script` integrations. Please review the [changelog](https://github.com/slskd/slskd/releases/tag/0.22.3)
+ and the accompanying [pull request](https://github.com/slskd/slskd/pull/1292).
+
+- `forgejo` and `forgejo-lts` have been updated to v11.
+ See upstreams [release blog post](https://forgejo.org/2025-04-release-v11-0/) for more information.
+
+- `unifi` has been updated to v9.1.
+ This version should be backward compatible with v8.x, however as a result, `unifi8` package has been removed.
+
- The behavior of `services.hostapd.radios..networks..authentication.enableRecommendedPairwiseCiphers` was changed to not include `CCMP-256` anymore.
Since all configured pairwise ciphers have to be supported by the radio, this caused startup failures on many devices which is hard to debug in hostapd.
+- The `conduwuit` matrix server implementation has officially been discontinued by upstream and the package has thus been marked as vulnerable, as it is a security-sensitive package that has reached EOL.
+
- `gkraken` software and `hardware.gkraken.enable` option have been removed, use `coolercontrol` via `programs.coolercontrol.enable` option instead.
- To avoid delaying user logins unnecessarily the `multi-user.target` is no longer ordered after `network-online.target`.
@@ -308,7 +439,7 @@
+extraCreateArgs+=("--exclude" "/some/path")
```
-- `programs.xonsh.package` now gets overrided internally with `extraPackages` to support `programs.xonsh.extraPackages`. See `programs.xonsh.extraPackages` for more details.
+- `programs.xonsh.package` now gets overridden internally with `extraPackages` to support `programs.xonsh.extraPackages`. See `programs.xonsh.extraPackages` for more details.
- `services.nitter.guestAccounts` has been renamed to `services.nitter.sessionsFile`, for consistency with upstream. The file format is unchanged.
@@ -349,10 +480,19 @@
- `programs.clash-verge.tunMode` was deprecated and removed because now service mode is necessary to start program. Without `programs.clash-verge.enable`, clash-verge-rev will refuse to start.
+- `services.discourse` now requires PostgreSQL 15 per default. Please update before upgrading.
+
+- `services.homepage-dashboard` now requires the `allowedHosts` option to be set in accordance with the [documentation](https://gethomepage.dev/installation/#homepage_allowed_hosts).
+
+- `luakit` has been updated to 2.4.0. If you use any website which uses IndexedDB or local storage and wish to retain the saved information, [some manual intervention may be required](https://luakit.github.io/news/luakit-2.4.0.html)
- `services.netbird.tunnels` was renamed to [`services.netbird.clients`](#opt-services.netbird.clients),
hardened (using dedicated less-privileged users) and significantly extended.
+- `services.rsyncd.settings` now supports only two attributes `sections` and `globalSection`.
+ As a result, all sections previously defined under `services.rsyncd.settings` must now be put in `services.rsyncd.settings.sections`.
+ Global settings must now be placed in `services.rsyncd.settings.globalSection` instead of `services.rsyncd.settings.global`.
+
## Other Notable Changes {#sec-release-25.05-notable-changes}
@@ -375,8 +515,46 @@
- Overriding Wayland compositor is possible using `waylandSessionCompositor` option, but you might need to take care [`xfce4-session`](https://gitlab.xfce.org/xfce/xfce4-session/-/merge_requests/49), [`dbus-update-activation-environment`](https://github.com/labwc/labwc/blob/eaf11face68ee1f1bcc7ce1498304ca8c108c8ba/src/config/session.c#L234) and [`systemctl --user import-environment`](https://github.com/labwc/labwc/blob/eaf11face68ee1f1bcc7ce1498304ca8c108c8ba/src/config/session.c#L239) on startup.
- For new Xfce installations, default panel layout has [changed](https://gitlab.xfce.org/xfce/xfce4-panel/-/merge_requests/158/diffs) to not include external panel plugins by default. You can still add them yourself using the "Panel Preferences" dialog.
+- PAM services for `i3lock`/`i3lock-color`, `vlock`, `xlock`, and `xscreensaver` now default to disabled unless other corresponding NixOS options are set (`programs.i3lock.enable`, `console.enable`, `services.xserver.enable`, and `services.xscreensaver.enable`, respectively). If for some reason you want one of them back without setting the corresponding option, set, e.g., `security.pam.services.xlock.enable = true`.
+
+- The `nixos-generate-config` command now supports a optional `--flake` option, which will generate a flake.nix file alongside the `configuration.nix` and `hardware-configuration.nix`, providing an easy introduction into flake-based system configurations.
+
- [`system.stateVersion`](#opt-system.stateVersion) is now validated and must be in the `"YY.MM"` format, ideally corresponding to a prior NixOS release.
+- [`hardware.xone`](options.html#opt-hardware.xone.enable) will also enable [`hardware.xpad-noone`](options.html#opt-hardware.xpad-noone.enable) to provide Xbox 360 driver by default.
+
+- `services.mysql` now supports easy cluster setup via [`services.mysql.galeraCluster`](#opt-services.mysql.galeraCluster.enable) option.
+
+ Example:
+
+ ```nix
+ services.mysql = {
+ enable = true;
+ galeraCluster = {
+ enable = true;
+ localName = "Node 1";
+ localAddress = "galera_01";
+ nodeAddresses = [ "galera_01" "galera_02" "galera_03"];
+ };
+ };
+ ```
+
+- The `intel` video driver for X.org (from the xf86-video-intel package, which was previously removed because it was non-functional) has been fixed and the driver has been re-introduced.
+
+- systemd's {manpage}`systemd-ssh-generator(8)` now works out of the box on NixOS.
+ - You can ssh into VMs without any networking configuration if your hypervisor configures the vm to support AF_VSOCK.
+ It still requires the usual ssh authentication methods.
+ - An SSH key for the root user can be provisioned using the `ssh.authorized_keys.root` systemd credential.
+ This can be useful for booting an installation image and providing the SSH key with an smbios string.
+ - SSH can be used for suid-less privilege escalation on the local system without having to rely on networking:
+ ```shell
+ ssh root@.host
+ ```
+ - systemd's {manpage}`systemd-ssh-proxy(1)` is enabled by default. It can be disabled using [`programs.ssh.systemd-ssh-proxy.enable`](#opt-programs.ssh.systemd-ssh-proxy.enable).
+
+- SSH host key generation has been separated into the dedicated systemd service sshd-keygen.service.
+
+- [`services.dex`](#opt-services.dex.enable) now restarts upon changes to the [`.environmentFile`](#opt-services.dex.environmentFile) option or `path` type entries in `.settings.staticClients[].secretFile`.
- [`services.geoclue2`](#opt-services.geoclue2.enable) now has an `enableStatic` option, which allows the NixOS configuration to specify a fixed location for GeoClue to use.
@@ -392,11 +570,13 @@
- `services.avahi.ipv6` now defaults to true.
+- A new hardening flag, `nostrictaliasing` was made available, corresponding to the gcc/clang option `-fno-strict-aliasing`.
+
- In the `services.xserver.displayManager.startx` module, two new options [generateScript](#opt-services.xserver.displayManager.startx.generateScript) and [extraCommands](#opt-services.xserver.displayManager.startx.extraCommands) have been added to to declaratively configure the .xinitrc script.
- All services that require a root certificate bundle now use the value of a new read-only option, `security.pki.caBundle`.
-- hddfancontrol has been updated to major release 2. See the [migration guide](https://github.com/desbma/hddfancontrol/tree/master?tab=readme-ov-file#migrating-from-v1x), as there are breaking changes.
+- hddfancontrol has been updated to major release 2. See the [migration guide](https://github.com/desbma/hddfancontrol/tree/master?tab=readme-ov-file#migrating-from-v1x), as there are breaking changes. The settings options have been modified to use an attrset, enabling configurations with multiple instances of the daemon running at once, eg, for two separate drive bays.
- `nextcloud-news-updater` is unmaintained and was removed from nixpkgs.
@@ -420,8 +600,11 @@
- [`services.mongodb.enableAuth`](#opt-services.mongodb.enableAuth) now uses the newer [mongosh](https://github.com/mongodb-js/mongosh) shell instead of the legacy shell to configure the initial superuser. You can configure the mongosh package to use through the [`services.mongodb.mongoshPackage`](#opt-services.mongodb.mongoshPackage) option.
-- The paperless module now has an option for regular automatic export of
- documents data using the integrated document exporter.
+- There is a new set of NixOS test tools for testing virtual Wi-Fi networks in many different topologies. See the {option}`services.vwifi` module, {option}`services.kismet` NixOS test, and [manual](https://nixos.org/manual/nixpkgs/unstable/#sec-nixos-test-wifi) for documentation and examples.
+
+- The paperless module now has an option for regular automatic export of documents data using the integrated document exporter.
+
+- Exposed the `paperless-manage` script package via the `services.paperless.manage` read-only option.
- New options for the declarative configuration of the user space part of ALSA have been introduced under [hardware.alsa](options.html#opt-hardware.alsa.enable), including setting the default capture and playback device, defining sound card aliases and volume controls.
Note: these are intended for users not running a sound server like PulseAudio or PipeWire, but having ALSA as their only sound system.
@@ -451,21 +634,52 @@
Note that all provided plugins must have versions/tags (string after `@`), even if upstream repo does not tag each release. For untagged plugins, you can either create an empty Go project and run `go get ` and see changes in `go.mod` to get the pseudo-version number, or provide a commit hash in place of version/tag for the first run, and update the plugin string based on the error output.
+- `buildGoModule` now supports a self-referencing `finalAttrs:` parameter
+ containing the final arguments including overrides.
+ This allows packaging configuration to be overridden in a consistent manner by
+ providing an alternative to `rec {}` syntax.
+
+- [Mattermost](#opt-services.mattermost.enable), a self-hosted chat collaboration platform supporting calls, playbooks, and boards, has been updated. It now has multiple versions, disabled telemetry, and a native frontend build in nixpkgs, removing all upstream prebuilt blobs.
+ - A new `pkgs.mattermost.buildPlugin` function has been added, which allows plugins to be built from source, including webapp frontends with a supported package-lock.json. See the Mattermost NixOS test and [manual](https://nixos.org/manual/nixpkgs/unstable/#sec-mattermost-plugins-build) for an example.
+ - Mattermost telemetry reporting is now disabled by default, though security update notifications are enabled. Look at [`services.mattermost.telemetry`](#opt-services.mattermost.telemetry.enableDiagnostics) for options to control this behavior.
+ - The Mattermost frontend is now built from source and can be overridden. Note that the Mattermost derivation containing both the webapp and server is now wrapped to allow them to be built independently, so overrides to both webapp and server look like `mattermost.overrideAttrs (prev: { webapp = prev.webapp.override { ... }; server = prev.server.override { ... }; })` now.
+ - `pkgs.mattermost` has been updated from 9.11 to 10.5 to track the latest extended support release, since 9.11 will become end-of-life during the lifetime of NixOS 25.05.
+ - `pkgs.mattermostLatest` is now an option to track the latest (non-prerelease) Mattermost release. We test upgrade migrations from ESR releases (`pkgs.mattermost`) to `pkgs.mattermostLatest`.
+ - The Mattermost module will produce eval warnings if a database password would end up in the Nix store, and recommend alternatives such as peer authentication or using the environment file.
+ - We now support `mmctl` for Mattermost administration if both [](#opt-services.mattermost.socket.enable) and [](#opt-services.mattermost.socket.export) are set, which export the Mattermost control socket path into the system environment.
+
- KDE Partition Manager `partitionmanager`'s support for ReiserFS is removed.
ReiserFS has not been actively maintained for many years. It has been marked as obsolete since Linux 6.6, and
[is removed](https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=c01f664e4ca210823b7594b50669bbd9b0a3c3b0)
in Linux 6.13.
+- `services.geoclue2` now uses [beaconDB](https://beacondb.net/) as a default geolocation service, replacing Mozilla Location Services which was [retired in June 2024](https://github.com/mozilla/ichnaea/issues/2065).
+
- `authelia` version 4.39.0 has made some changes which deprecate older configurations.
They are still expected to be working until future version 5.0.0, but will generate warnings in logs.
Read the [release notes](https://www.authelia.com/blog/4.39-release-notes/) for human readable summaries of the changes.
+- `security.acme` now supports renewal using CSRs (Certificate Signing Request) through the options `security.acme.*.csr` and `security.acme.*.csrKey`.
+
- `programs.fzf.keybindings` now supports the fish shell.
+- `gerbera` now has wavpack support.
+
+- `octave` (and `octaveFull`) was updated to version `10.x`. The update broke a few `octavePackages`, and `librsb`. See [the PR's commits](https://github.com/NixOS/nixpkgs/pull/394495/commits) for more details.
+
- A toggle has been added under `users.users..enable` to allow toggling individual users conditionally. If set to false, the user account will not be created.
+- New hooks were added:
+ - `writableTmpDirAsHomeHook`: This setup hook ensures that the directory specified by the `HOME` environment variable is writable.
+ - `addBinToPathHook`: This setup hook checks if the `bin/` directory exists in the `$out` output path and, if so, adds it to the `PATH` environment variable.
+ - `gitSetupHook`: This setup hook sets up a valid Git configuration, including the `user.name` and `user.email` fields.
+
+## NixOS Wiki {#sec-release-25.05-wiki}
+
+The official NixOS Wiki at [wiki.nixos.org](https://wiki.nixos.org) has new and improved articles, new contributors and some improvements in its dark theme and mobile readability.
+
```{=include=} sections
../release-notes-nixpkgs/rl-2505.section.md
```
diff --git a/nixos/doc/manual/release-notes/rl-2511.section.md b/nixos/doc/manual/release-notes/rl-2511.section.md
new file mode 100644
index 000000000000..65bac7327ed9
--- /dev/null
+++ b/nixos/doc/manual/release-notes/rl-2511.section.md
@@ -0,0 +1,27 @@
+# Release 25.11 ("Xantusia", 2025.11/??) {#sec-release-25.11}
+
+## Highlights {#sec-release-25.11-highlights}
+
+
+
+- Secure boot support can now be enabled for the Limine bootloader through {option}`boot.loader.limine.secureBoot.enable`. Bootloader install script signs the bootloader, then kernels are hashed during system rebuild and written to a config. This allows Limine to boot only the kernels installed through NixOS system.
+
+## New Modules {#sec-release-25.11-new-modules}
+
+
+
+- [gtklock](https://github.com/jovanlanik/gtklock), a GTK-based lockscreen for Wayland. Available as [programs.gtklock](#opt-programs.gtklock.enable).
+
+## Backward Incompatibilities {#sec-release-25.11-incompatibilities}
+
+
+
+- The `services.polipo` module has been removed as `polipo` is unmaintained and archived upstream.
+
+- `renovate` was updated to v40. See the [upstream release notes](https://github.com/renovatebot/renovate/releases/tag/40.0.0) for breaking changes.
+
+## Other Notable Changes {#sec-release-25.11-notable-changes}
+
+
+
+- `services.clamsmtp` is unmaintained and was removed from Nixpkgs.
diff --git a/nixos/lib/make-disk-image.nix b/nixos/lib/make-disk-image.nix
index 1866d8095a2b..015d7e4dbe53 100644
--- a/nixos/lib/make-disk-image.nix
+++ b/nixos/lib/make-disk-image.nix
@@ -1,7 +1,7 @@
/*
Technical details
- `make-disk-image` has a bit of magic to minimize the amount of work to do in a virtual machine.
+ `make-disk-image` has a bit of magic to minimize the amount of work to do in a virtual machine. It also might arguably have too much, or at least too specific magic, so please consider to work towards the effort of unifying our image builders, as outlined in https://github.com/NixOS/nixpkgs/issues/324817 before adding more.
It relies on the [LKL (Linux Kernel Library) project](https://github.com/lkl/linux) which provides Linux kernel as userspace library.
@@ -447,8 +447,7 @@ let
mkdir -p $root
# Copy arbitrary other files into the image
- # Semi-shamelessly copied from make-etc.sh. I (@copumpkin) shall factor this stuff out as part of
- # https://github.com/NixOS/nixpkgs/issues/23052.
+ # Semi-shamelessly copied from make-etc.sh.
set -f
sources_=(${lib.concatStringsSep " " sources})
targets_=(${lib.concatStringsSep " " targets})
diff --git a/nixos/lib/systemd-unit-options.nix b/nixos/lib/systemd-unit-options.nix
index 6c93bfddfa52..0ddae4e20198 100644
--- a/nixos/lib/systemd-unit-options.nix
+++ b/nixos/lib/systemd-unit-options.nix
@@ -99,7 +99,7 @@ rec {
Defines how unit configuration is provided for systemd:
`asDropinIfExists` creates a unit file when no unit file is provided by the package
- otherwise a drop-in file name `overrides.conf`.
+ otherwise it creates a drop-in file named `overrides.conf`.
`asDropin` creates a drop-in file named `overrides.conf`.
Mainly needed to define instances for systemd template units (e.g. `systemd-nspawn@mycontainer.service`).
@@ -398,9 +398,21 @@ rec {
enableStrictShellChecks = mkOption {
type = types.bool;
- description = "Enable running shellcheck on the generated scripts for this unit.";
- # The default gets set in systemd-lib.nix because we don't have access to
- # the full NixOS config here.
+ description = ''
+ Enable running `shellcheck` on the generated scripts for this unit.
+
+ When enabled, scripts generated by the unit will be checked with
+ `shellcheck` and any errors or warnings will cause the build to
+ fail.
+
+ This affects all scripts that have been created through the
+ `script`, `reload`, `preStart`, `postStart`, `preStop` and
+ `postStop` options for systemd services. This does not affect
+ command lines passed directly to `ExecStart`, `ExecReload`,
+ `ExecStartPre`, `ExecStartPost`, `ExecStop` or `ExecStopPost`.
+ '';
+ # The default gets set in systemd-lib.nix because we don't have
+ # access to the full NixOS config here.
defaultText = literalExpression "config.systemd.enableStrictShellChecks";
};
diff --git a/nixos/lib/test-driver/default.nix b/nixos/lib/test-driver/default.nix
index f22744806d48..91db5d8be3c2 100644
--- a/nixos/lib/test-driver/default.nix
+++ b/nixos/lib/test-driver/default.nix
@@ -31,6 +31,7 @@ python3Packages.buildPythonApplication {
colorama
junit-xml
ptpython
+ ipython
]
++ extraPythonPackages python3Packages;
diff --git a/nixos/lib/test-driver/src/pyproject.toml b/nixos/lib/test-driver/src/pyproject.toml
index ac83eed268d9..fa4e6a2de127 100644
--- a/nixos/lib/test-driver/src/pyproject.toml
+++ b/nixos/lib/test-driver/src/pyproject.toml
@@ -21,7 +21,7 @@ target-version = "py312"
line-length = 88
lint.select = ["E", "F", "I", "U", "N"]
-lint.ignore = ["E501"]
+lint.ignore = ["E501", "N818"]
# xxx: we can import https://pypi.org/project/types-colorama/ here
[[tool.mypy.overrides]]
diff --git a/nixos/lib/test-driver/src/test_driver/__init__.py b/nixos/lib/test-driver/src/test_driver/__init__.py
index 1c0793aa75a5..86e663da9b7d 100755
--- a/nixos/lib/test-driver/src/test_driver/__init__.py
+++ b/nixos/lib/test-driver/src/test_driver/__init__.py
@@ -3,7 +3,7 @@ import os
import time
from pathlib import Path
-import ptpython.repl
+import ptpython.ipython
from test_driver.driver import Driver
from test_driver.logger import (
@@ -109,6 +109,11 @@ def main() -> None:
help="the test script to run",
type=Path,
)
+ arg_parser.add_argument(
+ "--dump-vsocks",
+ help="indicates that the interactive SSH backdoor is active and dumps information about it on start",
+ type=int,
+ )
args = arg_parser.parse_args()
@@ -136,11 +141,12 @@ def main() -> None:
if args.interactive:
history_dir = os.getcwd()
history_path = os.path.join(history_dir, ".nixos-test-history")
- ptpython.repl.embed(
- driver.test_symbols(),
- {},
+ if offset := args.dump_vsocks:
+ driver.dump_machine_ssh(offset)
+ ptpython.ipython.embed(
+ user_ns=driver.test_symbols(),
history_filename=history_path,
- )
+ ) # type:ignore
else:
tic = time.time()
driver.run_tests()
diff --git a/nixos/lib/test-driver/src/test_driver/driver.py b/nixos/lib/test-driver/src/test_driver/driver.py
index 6061c1bc09b8..bf3dda06a617 100644
--- a/nixos/lib/test-driver/src/test_driver/driver.py
+++ b/nixos/lib/test-driver/src/test_driver/driver.py
@@ -1,13 +1,19 @@
import os
import re
import signal
+import sys
import tempfile
import threading
+import traceback
from collections.abc import Callable, Iterator
from contextlib import AbstractContextManager, contextmanager
from pathlib import Path
from typing import Any
+from unittest import TestCase
+from colorama import Style
+
+from test_driver.errors import MachineError, RequestedAssertionFailed
from test_driver.logger import AbstractLogger
from test_driver.machine import Machine, NixStartScript, retry
from test_driver.polling_condition import PollingCondition
@@ -16,6 +22,18 @@ from test_driver.vlan import VLan
SENTINEL = object()
+class AssertionTester(TestCase):
+ """
+ Subclass of `unittest.TestCase` which is used in the
+ `testScript` to perform assertions.
+
+ It throws a custom exception whose parent class
+ gets special treatment in the logs.
+ """
+
+ failureException = RequestedAssertionFailed
+
+
def get_tmp_dir() -> Path:
"""Returns a temporary directory that is defined by TMPDIR, TEMP, TMP or CWD
Raises an exception in case the retrieved temporary directory is not writeable
@@ -115,7 +133,7 @@ class Driver:
try:
yield
except Exception as e:
- self.logger.error(f'Test "{name}" failed with error: "{e}"')
+ self.logger.log_test_error(f'Test "{name}" failed with error: "{e}"')
raise e
def test_symbols(self) -> dict[str, Any]:
@@ -140,6 +158,7 @@ class Driver:
serial_stdout_on=self.serial_stdout_on,
polling_condition=self.polling_condition,
Machine=Machine, # for typing
+ t=AssertionTester(),
)
machine_symbols = {pythonize_name(m.name): m for m in self.machines}
# If there's exactly one machine, make it available under the name
@@ -159,11 +178,53 @@ class Driver:
)
return {**general_symbols, **machine_symbols, **vlan_symbols}
+ def dump_machine_ssh(self, offset: int) -> None:
+ print("SSH backdoor enabled, the machines can be accessed like this:")
+ print(
+ f"{Style.BRIGHT}Note:{Style.RESET_ALL} this requires {Style.BRIGHT}systemd-ssh-proxy(1){Style.RESET_ALL} to be enabled (default on NixOS 25.05 and newer)."
+ )
+ names = [machine.name for machine in self.machines]
+ longest_name = len(max(names, key=len))
+ for num, name in enumerate(names, start=offset + 1):
+ spaces = " " * (longest_name - len(name) + 2)
+ print(
+ f" {name}:{spaces}{Style.BRIGHT}ssh -o User=root vsock/{num}{Style.RESET_ALL}"
+ )
+
def test_script(self) -> None:
"""Run the test script"""
with self.logger.nested("run the VM test script"):
symbols = self.test_symbols() # call eagerly
- exec(self.tests, symbols, None)
+ try:
+ exec(self.tests, symbols, None)
+ except MachineError:
+ for line in traceback.format_exc().splitlines():
+ self.logger.log_test_error(line)
+ sys.exit(1)
+ except RequestedAssertionFailed:
+ exc_type, exc, tb = sys.exc_info()
+ # We manually print the stack frames, keeping only the ones from the test script
+ # (note: because the script is not a real file, the frame filename is ``)
+ filtered = [
+ frame
+ for frame in traceback.extract_tb(tb)
+ if frame.filename == ""
+ ]
+
+ self.logger.log_test_error("Traceback (most recent call last):")
+
+ code = self.tests.splitlines()
+ for frame, line in zip(filtered, traceback.format_list(filtered)):
+ self.logger.log_test_error(line.rstrip())
+ if lineno := frame.lineno:
+ self.logger.log_test_error(f" {code[lineno - 1].strip()}")
+
+ self.logger.log_test_error("") # blank line for readability
+ exc_prefix = exc_type.__name__ if exc_type is not None else "Error"
+ for line in f"{exc_prefix}: {exc}".splitlines():
+ self.logger.log_test_error(line)
+
+ sys.exit(1)
def run_tests(self) -> None:
"""Run the test script (for non-interactive test runs)"""
diff --git a/nixos/lib/test-driver/src/test_driver/errors.py b/nixos/lib/test-driver/src/test_driver/errors.py
new file mode 100644
index 000000000000..fe072b5185c9
--- /dev/null
+++ b/nixos/lib/test-driver/src/test_driver/errors.py
@@ -0,0 +1,20 @@
+class MachineError(Exception):
+ """
+ Exception that indicates an error that is NOT the user's fault,
+ i.e. something went wrong without the test being necessarily invalid,
+ such as failing OCR.
+
+ To make it easier to spot, this exception (and its subclasses)
+ get a `!!!` prefix in the log output.
+ """
+
+
+class RequestedAssertionFailed(AssertionError):
+ """
+ Special assertion that gets thrown on an assertion error,
+ e.g. a failing `t.assertEqual(...)` or `machine.succeed(...)`.
+
+ This gets special treatment in error reporting: i.e. it gets
+ `!!!` as prefix just as `MachineError`, but only stack frames coming
+ from `testScript` will show up in logs.
+ """
diff --git a/nixos/lib/test-driver/src/test_driver/logger.py b/nixos/lib/test-driver/src/test_driver/logger.py
index 564d39f4f055..a218d234fe3f 100644
--- a/nixos/lib/test-driver/src/test_driver/logger.py
+++ b/nixos/lib/test-driver/src/test_driver/logger.py
@@ -44,6 +44,10 @@ class AbstractLogger(ABC):
def error(self, *args, **kwargs) -> None: # type: ignore
pass
+ @abstractmethod
+ def log_test_error(self, *args, **kwargs) -> None: # type:ignore
+ pass
+
@abstractmethod
def log_serial(self, message: str, machine: str) -> None:
pass
@@ -97,6 +101,9 @@ class JunitXMLLogger(AbstractLogger):
self.tests[self.currentSubtest].stderr += args[0] + os.linesep
self.tests[self.currentSubtest].failure = True
+ def log_test_error(self, *args, **kwargs) -> None: # type: ignore
+ self.error(*args, **kwargs)
+
def log_serial(self, message: str, machine: str) -> None:
if not self._print_serial_logs:
return
@@ -156,6 +163,10 @@ class CompositeLogger(AbstractLogger):
for logger in self.logger_list:
logger.warning(*args, **kwargs)
+ def log_test_error(self, *args, **kwargs) -> None: # type: ignore
+ for logger in self.logger_list:
+ logger.log_test_error(*args, **kwargs)
+
def error(self, *args, **kwargs) -> None: # type: ignore
for logger in self.logger_list:
logger.error(*args, **kwargs)
@@ -202,7 +213,7 @@ class TerminalLogger(AbstractLogger):
tic = time.time()
yield
toc = time.time()
- self.log(f"(finished: {message}, in {toc - tic:.2f} seconds)")
+ self.log(f"(finished: {message}, in {toc - tic:.2f} seconds)", attributes)
def info(self, *args, **kwargs) -> None: # type: ignore
self.log(*args, **kwargs)
@@ -222,6 +233,11 @@ class TerminalLogger(AbstractLogger):
self._eprint(Style.DIM + f"{machine} # {message}" + Style.RESET_ALL)
+ def log_test_error(self, *args, **kwargs) -> None: # type: ignore
+ prefix = Fore.RED + "!!! " + Style.RESET_ALL
+ # NOTE: using `warning` instead of `error` to ensure it does not exit after printing the first log
+ self.warning(f"{prefix}{args[0]}", *args[1:], **kwargs)
+
class XMLLogger(AbstractLogger):
def __init__(self, outfile: str) -> None:
@@ -261,6 +277,9 @@ class XMLLogger(AbstractLogger):
def error(self, *args, **kwargs) -> None: # type: ignore
self.log(*args, **kwargs)
+ def log_test_error(self, *args, **kwargs) -> None: # type: ignore
+ self.log(*args, **kwargs)
+
def log(self, message: str, attributes: dict[str, str] = {}) -> None:
self.drain_log_queue()
self.log_line(message, attributes)
diff --git a/nixos/lib/test-driver/src/test_driver/machine.py b/nixos/lib/test-driver/src/test_driver/machine.py
index cba386ae86b4..1b9dd1262ce6 100644
--- a/nixos/lib/test-driver/src/test_driver/machine.py
+++ b/nixos/lib/test-driver/src/test_driver/machine.py
@@ -19,6 +19,7 @@ from pathlib import Path
from queue import Queue
from typing import Any
+from test_driver.errors import MachineError, RequestedAssertionFailed
from test_driver.logger import AbstractLogger
from .qmp import QMPSession
@@ -129,7 +130,7 @@ def _preprocess_screenshot(screenshot_path: str, negate: bool = False) -> str:
)
if ret.returncode != 0:
- raise Exception(
+ raise MachineError(
f"Image processing failed with exit code {ret.returncode}, stdout: {ret.stdout.decode()}, stderr: {ret.stderr.decode()}"
)
@@ -140,7 +141,7 @@ def _perform_ocr_on_screenshot(
screenshot_path: str, model_ids: Iterable[int]
) -> list[str]:
if shutil.which("tesseract") is None:
- raise Exception("OCR requested but enableOCR is false")
+ raise MachineError("OCR requested but enableOCR is false")
processed_image = _preprocess_screenshot(screenshot_path, negate=False)
processed_negative = _preprocess_screenshot(screenshot_path, negate=True)
@@ -163,7 +164,7 @@ def _perform_ocr_on_screenshot(
capture_output=True,
)
if ret.returncode != 0:
- raise Exception(f"OCR failed with exit code {ret.returncode}")
+ raise MachineError(f"OCR failed with exit code {ret.returncode}")
model_results.append(ret.stdout.decode("utf-8"))
return model_results
@@ -180,7 +181,9 @@ def retry(fn: Callable, timeout: int = 900) -> None:
time.sleep(1)
if not fn(True):
- raise Exception(f"action timed out after {timeout} seconds")
+ raise RequestedAssertionFailed(
+ f"action timed out after {timeout} tries with one-second pause in-between"
+ )
class StartCommand:
@@ -409,14 +412,14 @@ class Machine:
def check_active(_last_try: bool) -> bool:
state = self.get_unit_property(unit, "ActiveState", user)
if state == "failed":
- raise Exception(f'unit "{unit}" reached state "{state}"')
+ raise RequestedAssertionFailed(f'unit "{unit}" reached state "{state}"')
if state == "inactive":
status, jobs = self.systemctl("list-jobs --full 2>&1", user)
if "No jobs" in jobs:
info = self.get_unit_info(unit, user)
if info["ActiveState"] == state:
- raise Exception(
+ raise RequestedAssertionFailed(
f'unit "{unit}" is inactive and there are no pending jobs'
)
@@ -431,7 +434,7 @@ class Machine:
def get_unit_info(self, unit: str, user: str | None = None) -> dict[str, str]:
status, lines = self.systemctl(f'--no-pager show "{unit}"', user)
if status != 0:
- raise Exception(
+ raise RequestedAssertionFailed(
f'retrieving systemctl info for unit "{unit}"'
+ ("" if user is None else f' under user "{user}"')
+ f" failed with exit code {status}"
@@ -461,7 +464,7 @@ class Machine:
user,
)
if status != 0:
- raise Exception(
+ raise RequestedAssertionFailed(
f'retrieving systemctl property "{property}" for unit "{unit}"'
+ ("" if user is None else f' under user "{user}"')
+ f" failed with exit code {status}"
@@ -509,7 +512,7 @@ class Machine:
info = self.get_unit_info(unit)
state = info["ActiveState"]
if state != require_state:
- raise Exception(
+ raise RequestedAssertionFailed(
f"Expected unit '{unit}' to to be in state "
f"'{require_state}' but it is in state '{state}'"
)
@@ -663,7 +666,9 @@ class Machine:
(status, out) = self.execute(command, timeout=timeout)
if status != 0:
self.log(f"output: {out}")
- raise Exception(f"command `{command}` failed (exit code {status})")
+ raise RequestedAssertionFailed(
+ f"command `{command}` failed (exit code {status})"
+ )
output += out
return output
@@ -677,7 +682,9 @@ class Machine:
with self.nested(f"must fail: {command}"):
(status, out) = self.execute(command, timeout=timeout)
if status == 0:
- raise Exception(f"command `{command}` unexpectedly succeeded")
+ raise RequestedAssertionFailed(
+ f"command `{command}` unexpectedly succeeded"
+ )
output += out
return output
@@ -922,7 +929,7 @@ class Machine:
ret = subprocess.run(f"pnmtopng '{tmp}' > '{filename}'", shell=True)
os.unlink(tmp)
if ret.returncode != 0:
- raise Exception("Cannot convert screenshot")
+ raise MachineError("Cannot convert screenshot")
def copy_from_host_via_shell(self, source: str, target: str) -> None:
"""Copy a file from the host into the guest by piping it over the
diff --git a/nixos/lib/test-script-prepend.py b/nixos/lib/test-script-prepend.py
index 9d2efdf97303..31dad14ef8dd 100644
--- a/nixos/lib/test-script-prepend.py
+++ b/nixos/lib/test-script-prepend.py
@@ -8,6 +8,7 @@ from test_driver.logger import AbstractLogger
from typing import Callable, Iterator, ContextManager, Optional, List, Dict, Any, Union
from typing_extensions import Protocol
from pathlib import Path
+from unittest import TestCase
class RetryProtocol(Protocol):
@@ -51,3 +52,4 @@ join_all: Callable[[], None]
serial_stdout_off: Callable[[], None]
serial_stdout_on: Callable[[], None]
polling_condition: PollingConditionProtocol
+t: TestCase
diff --git a/nixos/lib/testing-python.nix b/nixos/lib/testing-python.nix
index d01c1d9b5e09..878f9669321a 100644
--- a/nixos/lib/testing-python.nix
+++ b/nixos/lib/testing-python.nix
@@ -75,6 +75,7 @@ pkgs.lib.throwIf (args ? specialArgs)
),
extraPythonPackages ? (_: [ ]),
interactive ? { },
+ sshBackdoor ? { },
}@t:
let
testConfig =
diff --git a/nixos/lib/testing/meta.nix b/nixos/lib/testing/meta.nix
index 0a4c89ed4b06..1e8f37cf9b51 100644
--- a/nixos/lib/testing/meta.nix
+++ b/nixos/lib/testing/meta.nix
@@ -1,48 +1,61 @@
{ lib, ... }:
let
- inherit (lib) types mkOption;
+ inherit (lib) types mkOption literalMD;
in
{
options = {
- meta = lib.mkOption {
+ meta = mkOption {
description = ''
The [`meta`](https://nixos.org/manual/nixpkgs/stable/#chap-meta) attributes that will be set on the returned derivations.
Not all [`meta`](https://nixos.org/manual/nixpkgs/stable/#chap-meta) attributes are supported, but more can be added as desired.
'';
apply = lib.filterAttrs (k: v: v != null);
- type = types.submodule {
- options = {
- maintainers = lib.mkOption {
- type = types.listOf types.raw;
- default = [ ];
- description = ''
- The [list of maintainers](https://nixos.org/manual/nixpkgs/stable/#var-meta-maintainers) for this test.
- '';
+ type = types.submodule (
+ { config, ... }:
+ {
+ options = {
+ maintainers = mkOption {
+ type = types.listOf types.raw;
+ default = [ ];
+ description = ''
+ The [list of maintainers](https://nixos.org/manual/nixpkgs/stable/#var-meta-maintainers) for this test.
+ '';
+ };
+ timeout = mkOption {
+ type = types.nullOr types.int;
+ default = 3600; # 1 hour
+ description = ''
+ The [{option}`test`](#test-opt-test)'s [`meta.timeout`](https://nixos.org/manual/nixpkgs/stable/#var-meta-timeout) in seconds.
+ '';
+ };
+ broken = mkOption {
+ type = types.bool;
+ default = false;
+ description = ''
+ Sets the [`meta.broken`](https://nixos.org/manual/nixpkgs/stable/#var-meta-broken) attribute on the [{option}`test`](#test-opt-test) derivation.
+ '';
+ };
+ platforms = mkOption {
+ type = types.listOf types.raw;
+ default = lib.platforms.linux ++ lib.platforms.darwin;
+ description = ''
+ Sets the [`meta.platforms`](https://nixos.org/manual/nixpkgs/stable/#var-meta-platforms) attribute on the [{option}`test`](#test-opt-test) derivation.
+ '';
+ };
+ hydraPlatforms = mkOption {
+ type = types.listOf types.raw;
+ # Ideally this would default to `platforms` again:
+ # default = config.platforms;
+ default = lib.platforms.linux;
+ defaultText = literalMD "`lib.platforms.linux` only, as the `hydra.nixos.org` build farm does not currently support virtualisation on Darwin.";
+ description = ''
+ Sets the [`meta.hydraPlatforms`](https://nixos.org/manual/nixpkgs/stable/#var-meta-hydraPlatforms) attribute on the [{option}`test`](#test-opt-test) derivation.
+ '';
+ };
};
- timeout = lib.mkOption {
- type = types.nullOr types.int;
- default = 3600; # 1 hour
- description = ''
- The [{option}`test`](#test-opt-test)'s [`meta.timeout`](https://nixos.org/manual/nixpkgs/stable/#var-meta-timeout) in seconds.
- '';
- };
- broken = lib.mkOption {
- type = types.bool;
- default = false;
- description = ''
- Sets the [`meta.broken`](https://nixos.org/manual/nixpkgs/stable/#var-meta-broken) attribute on the [{option}`test`](#test-opt-test) derivation.
- '';
- };
- platforms = lib.mkOption {
- type = types.listOf types.raw;
- default = lib.platforms.linux ++ lib.platforms.darwin;
- description = ''
- Sets the [`meta.platforms`](https://nixos.org/manual/nixpkgs/stable/#var-meta-platforms) attribute on the [{option}`test`](#test-opt-test) derivation.
- '';
- };
- };
- };
+ }
+ );
default = { };
};
};
diff --git a/nixos/lib/testing/nodes.nix b/nixos/lib/testing/nodes.nix
index caefac6c748c..b2352c478110 100644
--- a/nixos/lib/testing/nodes.nix
+++ b/nixos/lib/testing/nodes.nix
@@ -13,6 +13,7 @@ let
mapAttrs
mkDefault
mkIf
+ mkMerge
mkOption
mkForce
optional
@@ -77,6 +78,30 @@ in
{
options = {
+ sshBackdoor = {
+ enable = mkOption {
+ default = false;
+ type = types.bool;
+ description = "Whether to turn on the VSOCK-based access to all VMs. This provides an unauthenticated access intended for debugging.";
+ };
+ vsockOffset = mkOption {
+ default = 2;
+ type = types.ints.between 2 4294967296;
+ description = ''
+ This field is only relevant when multiple users run the (interactive)
+ driver outside the sandbox and with the SSH backdoor activated.
+ The typical symptom for this being a problem are error messages like this:
+ `vhost-vsock: unable to set guest cid: Address already in use`
+
+ This option allows to assign an offset to each vsock number to
+ resolve this.
+
+ This is a 32bit number. The lowest possible vsock number is `3`
+ (i.e. with the lowest node number being `1`, this is 2+1).
+ '';
+ };
+ };
+
node.type = mkOption {
type = types.raw;
default = baseOS.type;
@@ -172,10 +197,41 @@ in
passthru.nodes = config.nodesCompat;
- defaults = mkIf config.node.pkgsReadOnly {
- nixpkgs.pkgs = config.node.pkgs;
- imports = [ ../../modules/misc/nixpkgs/read-only.nix ];
- };
+ extraDriverArgs = mkIf config.sshBackdoor.enable [
+ "--dump-vsocks=${toString config.sshBackdoor.vsockOffset}"
+ ];
+
+ defaults = mkMerge [
+ (mkIf config.node.pkgsReadOnly {
+ nixpkgs.pkgs = config.node.pkgs;
+ imports = [ ../../modules/misc/nixpkgs/read-only.nix ];
+ })
+ (mkIf config.sshBackdoor.enable (
+ let
+ inherit (config.sshBackdoor) vsockOffset;
+ in
+ { config, ... }:
+ {
+ services.openssh = {
+ enable = true;
+ settings = {
+ PermitRootLogin = "yes";
+ PermitEmptyPasswords = "yes";
+ };
+ };
+
+ security.pam.services.sshd = {
+ allowNullPassword = true;
+ };
+
+ virtualisation.qemu.options = [
+ "-device vhost-vsock-pci,guest-cid=${
+ toString (config.virtualisation.test.nodeNumber + vsockOffset)
+ }"
+ ];
+ }
+ ))
+ ];
};
}
diff --git a/nixos/lib/testing/run.nix b/nixos/lib/testing/run.nix
index 4ea0b1e9a034..f37aa1bcd0e5 100644
--- a/nixos/lib/testing/run.nix
+++ b/nixos/lib/testing/run.nix
@@ -43,27 +43,30 @@ in
};
config = {
- rawTestDerivation = hostPkgs.stdenv.mkDerivation {
- name = "vm-test-run-${config.name}";
+ rawTestDerivation =
+ assert lib.assertMsg (!config.sshBackdoor.enable)
+ "The SSH backdoor is currently not supported for non-interactive testing! Please make sure to only set `interactive.sshBackdoor.enable = true;`!";
+ hostPkgs.stdenv.mkDerivation {
+ name = "vm-test-run-${config.name}";
- requiredSystemFeatures =
- [ "nixos-test" ]
- ++ lib.optionals hostPkgs.stdenv.hostPlatform.isLinux [ "kvm" ]
- ++ lib.optionals hostPkgs.stdenv.hostPlatform.isDarwin [ "apple-virt" ];
+ requiredSystemFeatures =
+ [ "nixos-test" ]
+ ++ lib.optionals hostPkgs.stdenv.hostPlatform.isLinux [ "kvm" ]
+ ++ lib.optionals hostPkgs.stdenv.hostPlatform.isDarwin [ "apple-virt" ];
- buildCommand = ''
- mkdir -p $out
+ buildCommand = ''
+ mkdir -p $out
- # effectively mute the XMLLogger
- export LOGFILE=/dev/null
+ # effectively mute the XMLLogger
+ export LOGFILE=/dev/null
- ${config.driver}/bin/nixos-test-driver -o $out
- '';
+ ${config.driver}/bin/nixos-test-driver -o $out
+ '';
- passthru = config.passthru;
+ passthru = config.passthru;
- meta = config.meta;
- };
+ meta = config.meta;
+ };
test = lib.lazyDerivation {
# lazyDerivation improves performance when only passthru items and/or meta are used.
derivation = config.rawTestDerivation;
diff --git a/nixos/maintainers/scripts/cloudstack/cloudstack-image.nix b/nixos/maintainers/scripts/cloudstack/cloudstack-image.nix
index 724c220abaf8..4b966bd431ef 100644
--- a/nixos/maintainers/scripts/cloudstack/cloudstack-image.nix
+++ b/nixos/maintainers/scripts/cloudstack/cloudstack-image.nix
@@ -8,10 +8,19 @@
}:
{
- imports = [ ../../../modules/virtualisation/cloudstack-config.nix ];
+ imports = [
+ ../../../modules/virtualisation/cloudstack-config.nix
+ ../../../modules/image/file-options.nix
+ ];
+
+ system.nixos.tags = [ "cloudstack" ];
+ image.extension = "qcow2";
+ system.build.image = config.system.build.cloudstackImage;
system.build.cloudstackImage = import ../../../lib/make-disk-image.nix {
inherit lib config pkgs;
+ inherit (config.virtualisation) diskSize;
+ baseName = config.image.baseName;
format = "qcow2";
configFile = pkgs.writeText "configuration.nix" ''
{
diff --git a/nixos/maintainers/scripts/ec2/amazon-image.nix b/nixos/maintainers/scripts/ec2/amazon-image.nix
index d5b23d8a65f6..a192b91196de 100644
--- a/nixos/maintainers/scripts/ec2/amazon-image.nix
+++ b/nixos/maintainers/scripts/ec2/amazon-image.nix
@@ -83,7 +83,7 @@ in
# Use a priority just below mkOptionDefault (1500) instead of lib.mkDefault
# to avoid breaking existing configs using that.
- config.virtualisation.diskSize = lib.mkOverride 1490 (3 * 1024);
+ config.virtualisation.diskSize = lib.mkOverride 1490 (4 * 1024);
config.virtualisation.diskSizeAutoSupported = !config.ec2.zfs.enable;
config.system.nixos.tags = [ "amazon" ];
@@ -140,7 +140,7 @@ in
echo "file ${cfg.format} $rootDisk" >> $out/nix-support/hydra-build-products
${pkgs.jq}/bin/jq -n \
- --arg system_label ${lib.escapeShellArg config.system.nixos.label} \
+ --arg system_version ${lib.escapeShellArg config.system.nixos.version} \
--arg system ${lib.escapeShellArg pkgs.stdenv.hostPlatform.system} \
--arg root_logical_bytes "$(${pkgs.qemu_kvm}/bin/qemu-img info --output json "$rootDisk" | ${pkgs.jq}/bin/jq '."virtual-size"')" \
--arg boot_logical_bytes "$(${pkgs.qemu_kvm}/bin/qemu-img info --output json "$bootDisk" | ${pkgs.jq}/bin/jq '."virtual-size"')" \
@@ -148,7 +148,7 @@ in
--arg root "$rootDisk" \
--arg boot "$bootDisk" \
'{}
- | .label = $system_label
+ | .label = $system_version
| .boot_mode = $boot_mode
| .system = $system
| .disks.boot.logical_bytes = $boot_logical_bytes
@@ -181,13 +181,13 @@ in
echo "file ${cfg.format} $diskImage" >> $out/nix-support/hydra-build-products
${pkgs.jq}/bin/jq -n \
- --arg system_label ${lib.escapeShellArg config.system.nixos.label} \
+ --arg system_version ${lib.escapeShellArg config.system.nixos.version} \
--arg system ${lib.escapeShellArg pkgs.stdenv.hostPlatform.system} \
--arg logical_bytes "$(${pkgs.qemu_kvm}/bin/qemu-img info --output json "$diskImage" | ${pkgs.jq}/bin/jq '."virtual-size"')" \
--arg boot_mode "${amiBootMode}" \
--arg file "$diskImage" \
'{}
- | .label = $system_label
+ | .label = $system_version
| .boot_mode = $boot_mode
| .system = $system
| .logical_bytes = $logical_bytes
diff --git a/nixos/modules/config/i18n.nix b/nixos/modules/config/i18n.nix
index d36d4c3ecdfd..22a47d5bdffa 100644
--- a/nixos/modules/config/i18n.nix
+++ b/nixos/modules/config/i18n.nix
@@ -4,6 +4,22 @@
pkgs,
...
}:
+let
+ sanitizeUTF8Capitalization =
+ lang: (lib.replaceStrings [ "utf8" "utf-8" "UTF8" ] [ "UTF-8" "UTF-8" "UTF-8" ] lang);
+ aggregatedLocales =
+ [
+ "${config.i18n.defaultLocale}/${config.i18n.defaultCharset}"
+ ]
+ ++ lib.pipe config.i18n.extraLocaleSettings [
+ (lib.mapAttrs (n: v: (sanitizeUTF8Capitalization v)))
+ (lib.mapAttrsToList (LCRole: lang: lang + "/" + (config.i18n.localeCharsets.${LCRole} or "UTF-8")))
+ ]
+ ++ (builtins.map sanitizeUTF8Capitalization (
+ lib.optionals (builtins.isList config.i18n.extraLocales) config.i18n.extraLocales
+ ))
+ ++ (lib.optional (builtins.isString config.i18n.extraLocales) config.i18n.extraLocales);
+in
{
###### interface
@@ -36,9 +52,29 @@
default = "en_US.UTF-8";
example = "nl_NL.UTF-8";
description = ''
- The default locale. It determines the language for program
- messages, the format for dates and times, sort order, and so on.
- It also determines the character set, such as UTF-8.
+ The default locale. It determines the language for program messages,
+ the format for dates and times, sort order, and so on. Setting the
+ default character set is done via {option}`i18n.defaultCharset`.
+ '';
+ };
+ defaultCharset = lib.mkOption {
+ type = lib.types.str;
+ default = "UTF-8";
+ example = "ISO-8859-8";
+ description = ''
+ The default locale character set.
+ '';
+ };
+
+ extraLocales = lib.mkOption {
+ type = lib.types.either (lib.types.listOf lib.types.str) (lib.types.enum [ "all" ]);
+ default = [ ];
+ example = [ "nl_NL.UTF-8/UTF-8" ];
+ description = ''
+ Additional locales that the system should support, besides the ones
+ configured with {option}`i18n.defaultLocale` and
+ {option}`i18n.extraLocaleSettings`.
+ Set this to `"all"` to install all available locales.
'';
};
@@ -50,36 +86,37 @@
LC_TIME = "de_DE.UTF-8";
};
description = ''
- A set of additional system-wide locale settings other than
- `LANG` which can be configured with
- {option}`i18n.defaultLocale`.
+ A set of additional system-wide locale settings other than `LANG`
+ which can be configured with {option}`i18n.defaultLocale`. Note that
+ the `/UTF-8` suffix used in {option}`i18n.extraLocales` indicates a
+ character set, and it must not be added manually here. To use a
+ non-`UTF-8` character set such as ISO-XXXX-8, the
+ {option}`i18n.localeCharsets` can be used.
+ '';
+ };
+ localeCharsets = lib.mkOption {
+ type = lib.types.attrsOf lib.types.str;
+ default = { };
+ example = {
+ LC_MESSAGES = "ISO-8859-15";
+ LC_TIME = "ISO-8859-1";
+ };
+ description = ''
+ Per each {option}`i18n.extraLocaleSettings`, choose the character set
+ to use for it. Essentially defaults to UTF-8 for all of them.
'';
};
supportedLocales = lib.mkOption {
type = lib.types.listOf lib.types.str;
+ visible = false;
default = lib.unique (
- builtins.map
- (l: (lib.replaceStrings [ "utf8" "utf-8" "UTF8" ] [ "UTF-8" "UTF-8" "UTF-8" ] l) + "/UTF-8")
- (
- [
- "C.UTF-8"
- "en_US.UTF-8"
- config.i18n.defaultLocale
- ]
- ++ (lib.attrValues (lib.filterAttrs (n: v: n != "LANGUAGE") config.i18n.extraLocaleSettings))
- )
+ [
+ "C.UTF-8/UTF-8"
+ "en_US.UTF-8/UTF-8"
+ ]
+ ++ aggregatedLocales
);
- defaultText = lib.literalExpression ''
- lib.unique
- (builtins.map (l: (lib.replaceStrings [ "utf8" "utf-8" "UTF8" ] [ "UTF-8" "UTF-8" "UTF-8" ] l) + "/UTF-8") (
- [
- "C.UTF-8"
- "en_US.UTF-8"
- config.i18n.defaultLocale
- ] ++ (lib.attrValues (lib.filterAttrs (n: v: n != "LANGUAGE") config.i18n.extraLocaleSettings))
- ))
- '';
example = [
"en_US.UTF-8/UTF-8"
"nl_NL.UTF-8/UTF-8"
@@ -100,6 +137,24 @@
###### implementation
config = {
+ warnings =
+ lib.optional
+ (
+ !(
+ (lib.subtractLists config.i18n.supportedLocales aggregatedLocales) == [ ]
+ || lib.any (x: x == "all") config.i18n.supportedLocales
+ )
+ )
+ ''
+ `i18n.supportedLocales` is deprecated in favor of `i18n.extraLocales`,
+ and it seems you are using `i18n.supportedLocales` and forgot to
+ include some locales specified in `i18n.defaultLocale`,
+ `i18n.extraLocales` or `i18n.extraLocaleSettings`.
+
+ If you're trying to install additional locales not specified in
+ `i18n.defaultLocale` or `i18n.extraLocaleSettings`, consider adding
+ only those locales to `i18n.extraLocales`.
+ '';
environment.systemPackages =
# We increase the priority a little, so that plain glibc in systemPackages can't win.
diff --git a/nixos/modules/config/nix-flakes.nix b/nixos/modules/config/nix-flakes.nix
index 69388e791154..1a7b2724b486 100644
--- a/nixos/modules/config/nix-flakes.nix
+++ b/nixos/modules/config/nix-flakes.nix
@@ -19,6 +19,10 @@ let
cfg = config.nix;
+ flakeRefFormat = ''
+ The format of flake references is described in {manpage}`nix3-flake(1)`.
+ '';
+
in
{
options = {
@@ -46,7 +50,11 @@ in
type = "indirect";
id = "nixpkgs";
};
- description = "The flake reference to be rewritten.";
+ description = ''
+ The flake reference to be rewritten.
+
+ ${flakeRefFormat}
+ '';
};
to = mkOption {
type = referenceAttrs;
@@ -55,7 +63,11 @@ in
owner = "my-org";
repo = "my-nixpkgs";
};
- description = "The flake reference {option}`from` is rewritten to.";
+ description = ''
+ The flake reference {option}`from` is rewritten to.
+
+ ${flakeRefFormat}
+ '';
};
flake = mkOption {
type = types.nullOr types.attrs;
@@ -96,6 +108,8 @@ in
default = { };
description = ''
A system-wide flake registry.
+
+ See {manpage}`nix3-registry(1)` for more information.
'';
};
};
diff --git a/nixos/modules/config/stub-ld.nix b/nixos/modules/config/stub-ld.nix
index 836cd129e22f..277ed7594566 100644
--- a/nixos/modules/config/stub-ld.nix
+++ b/nixos/modules/config/stub-ld.nix
@@ -42,10 +42,7 @@ let
$CC -Os main.c -o $out
'';
- pkgs32 = pkgs.pkgsi686Linux;
-
stub-ld = stub-ld-for pkgs message;
- stub-ld32 = stub-ld-for pkgs32 message;
in
{
options = {
@@ -65,7 +62,6 @@ in
config = mkIf cfg.enable {
environment.ldso = mkDefault stub-ld;
- environment.ldso32 = mkIf pkgs.stdenv.hostPlatform.isx86_64 (mkDefault stub-ld32);
};
meta.maintainers = with lib.maintainers; [ tejing ];
diff --git a/nixos/modules/config/update-users-groups.pl b/nixos/modules/config/update-users-groups.pl
index a2be448d625b..0d192ae04073 100644
--- a/nixos/modules/config/update-users-groups.pl
+++ b/nixos/modules/config/update-users-groups.pl
@@ -54,15 +54,14 @@ sub dry_print {
# Functions for allocating free GIDs/UIDs. FIXME: respect ID ranges in
# /etc/login.defs.
sub allocId {
- my ($used, $prevUsed, $idMin, $idMax, $up, $getid) = @_;
- my $id = $up ? $idMin : $idMax;
+ my ($used, $prevUsed, $idMin, $idMax, $delta, $getid) = @_;
+ my $id = $delta > 0 ? $idMin : $idMax;
while ($id >= $idMin && $id <= $idMax) {
if (!$used->{$id} && !$prevUsed->{$id} && !defined &$getid($id)) {
$used->{$id} = 1;
return $id;
}
- $used->{$id} = 1;
- if ($up) { $id++; } else { $id--; }
+ $id += $delta;
}
die "$0: out of free UIDs or GIDs\n";
}
@@ -77,19 +76,19 @@ sub allocGid {
$gidsUsed{$prevGid} = 1;
return $prevGid;
}
- return allocId(\%gidsUsed, \%gidsPrevUsed, 400, 999, 0, sub { my ($gid) = @_; getgrgid($gid) });
+ return allocId(\%gidsUsed, \%gidsPrevUsed, 400, 999, -1, sub { my ($gid) = @_; getgrgid($gid) });
}
sub allocUid {
my ($name, $isSystemUser) = @_;
- my ($min, $max, $up) = $isSystemUser ? (400, 999, 0) : (1000, 29999, 1);
+ my ($min, $max, $delta) = $isSystemUser ? (400, 999, -1) : (1000, 29999, 1);
my $prevUid = $uidMap->{$name};
if (defined $prevUid && $prevUid >= $min && $prevUid <= $max && !defined $uidsUsed{$prevUid}) {
dry_print("reviving", "would revive", "user '$name' with UID $prevUid");
$uidsUsed{$prevUid} = 1;
return $prevUid;
}
- return allocId(\%uidsUsed, \%uidsPrevUsed, $min, $max, $up, sub { my ($uid) = @_; getpwuid($uid) });
+ return allocId(\%uidsUsed, \%uidsPrevUsed, $min, $max, $delta, sub { my ($uid) = @_; getpwuid($uid) });
}
# Read the declared users/groups
@@ -336,18 +335,14 @@ sub allocSubUid {
my ($name, @rest) = @_;
# TODO: No upper bounds?
- my ($min, $max, $up) = (100000, 100000 * 100, 1);
+ my ($min, $max, $delta) = (100000, 100000 + 100 * 65536, 65536);
my $prevId = $subUidMap->{$name};
if (defined $prevId && !defined $subUidsUsed{$prevId}) {
$subUidsUsed{$prevId} = 1;
return $prevId;
}
- my $id = allocId(\%subUidsUsed, \%subUidsPrevUsed, $min, $max, $up, sub { my ($uid) = @_; getpwuid($uid) });
- my $offset = $id - 100000;
- my $count = $offset * 65536;
- my $subordinate = 100000 + $count;
- return $subordinate;
+ return allocId(\%subUidsUsed, \%subUidsPrevUsed, $min, $max, $delta, sub { undef });
}
my @subGids;
@@ -367,6 +362,14 @@ foreach my $u (values %usersOut) {
if($u->{autoSubUidGidRange}) {
my $subordinate = allocSubUid($name);
+ if (defined $subUidMap->{$name} && $subUidMap->{$name} != $subordinate) {
+ print STDERR "warning: The subuids for '$name' changed, as they coincided with the subuids of a different user (see /etc/subuid). "
+ . "The range now starts with $subordinate instead of $subUidMap->{$name}. "
+ . "If the subuids were used (e.g. with rootless container managers like podman), please change the ownership of affected files accordingly. "
+ . "Alternatively, to keep the old overlapping ranges, add this to the system configuration: "
+ . "users.users.$name.subUidRanges = [{startUid = $subUidMap->{$name}; count = 65536;}]; "
+ . "users.users.$name.subGidRanges = [{startGid = $subUidMap->{$name}; count = 65536;}];\n";
+ }
$subUidMap->{$name} = $subordinate;
my $value = join(":", ($name, $subordinate, 65536));
push @subUids, $value;
diff --git a/nixos/modules/hardware/graphics.nix b/nixos/modules/hardware/graphics.nix
index 60e73f7eef65..627cf19333bf 100644
--- a/nixos/modules/hardware/graphics.nix
+++ b/nixos/modules/hardware/graphics.nix
@@ -80,16 +80,13 @@ in
The package that provides the default driver set.
'';
type = lib.types.package;
- internal = true;
};
package32 = lib.mkOption {
description = ''
The package that provides the 32-bit driver set. Used when {option}`enable32Bit` is enabled.
- set.
'';
type = lib.types.package;
- internal = true;
};
extraPackages = lib.mkOption {
diff --git a/nixos/modules/hardware/video/nvidia.nix b/nixos/modules/hardware/video/nvidia.nix
index 068e23aa3347..48f8ded73c64 100644
--- a/nixos/modules/hardware/video/nvidia.nix
+++ b/nixos/modules/hardware/video/nvidia.nix
@@ -343,14 +343,23 @@ in
];
# Don't add `nvidia-uvm` to `kernelModules`, because we want
- # `nvidia-uvm` be loaded only after `udev` rules for `nvidia` kernel
- # module are applied.
+ # `nvidia-uvm` be loaded only after the GPU device is available, i.e. after `udev` rules
+ # for `nvidia` kernel module are applied.
+ # This matters on Azure GPU instances: https://github.com/NixOS/nixpkgs/pull/267335
#
# Instead, we use `softdep` to lazily load `nvidia-uvm` kernel module
# after `nvidia` kernel module is loaded and `udev` rules are applied.
extraModprobeConfig = ''
softdep nvidia post: nvidia-uvm
'';
+
+ # Exception is the open-source kernel module failing to load nvidia-uvm using softdep
+ # for unknown reasons.
+ # It affects CUDA: https://github.com/NixOS/nixpkgs/issues/334180
+ # Previously nvidia-uvm was explicitly loaded only when xserver was enabled:
+ # https://github.com/NixOS/nixpkgs/pull/334340/commits/4548c392862115359e50860bcf658cfa8715bde9
+ # We are now loading the module eagerly for all users of the open driver (including headless).
+ kernelModules = lib.optionals useOpenModules [ "nvidia_uvm" ];
};
systemd.tmpfiles.rules = lib.mkIf config.virtualisation.docker.enableNvidia [
"L+ /run/nvidia-docker/bin - - - - ${nvidia_x11.bin}/origBin"
@@ -639,16 +648,11 @@ in
boot = {
extraModulePackages = if useOpenModules then [ nvidia_x11.open ] else [ nvidia_x11.bin ];
# nvidia-uvm is required by CUDA applications.
- kernelModules =
- lib.optionals config.services.xserver.enable [
- "nvidia"
- "nvidia_modeset"
- "nvidia_drm"
- ]
- # With the open driver, nvidia-uvm does not automatically load as
- # a softdep of the nvidia module, so we explicitly load it for now.
- # See https://github.com/NixOS/nixpkgs/issues/334180
- ++ lib.optionals (config.services.xserver.enable && useOpenModules) [ "nvidia_uvm" ];
+ kernelModules = lib.optionals config.services.xserver.enable [
+ "nvidia"
+ "nvidia_modeset"
+ "nvidia_drm"
+ ];
# If requested enable modesetting via kernel parameters.
kernelParams =
diff --git a/nixos/modules/hardware/xone.nix b/nixos/modules/hardware/xone.nix
index 04216e8260a7..f2b2bf668d1d 100644
--- a/nixos/modules/hardware/xone.nix
+++ b/nixos/modules/hardware/xone.nix
@@ -21,6 +21,7 @@ in
extraModulePackages = with config.boot.kernelPackages; [ xone ];
};
hardware.firmware = [ pkgs.xow_dongle-firmware ];
+ hardware.xpad-noone.enable = lib.mkDefault true;
};
meta = {
diff --git a/nixos/modules/i18n/input-method/default.nix b/nixos/modules/i18n/input-method/default.nix
index d84be512b39f..810abe127af4 100644
--- a/nixos/modules/i18n/input-method/default.nix
+++ b/nixos/modules/i18n/input-method/default.nix
@@ -91,6 +91,12 @@ in
The input method method package.
'';
};
+
+ enableGtk2 = lib.mkEnableOption "Gtk2 support";
+
+ enableGtk3 = lib.mkEnableOption "Gtk3 support" // {
+ default = true;
+ };
};
};
@@ -98,15 +104,16 @@ in
warnings =
lib.optional (cfg.enabled != null)
"i18n.inputMethod.enabled will be removed in a future release. Please use .type, and .enable = true instead";
- environment.systemPackages = [
- cfg.package
- gtk2_cache
- gtk3_cache
- ];
+ environment.systemPackages =
+ [
+ cfg.package
+ ]
+ ++ lib.optional cfg.enableGtk2 gtk2_cache
+ ++ lib.optional cfg.enableGtk3 gtk3_cache;
};
meta = {
- maintainers = with lib.maintainers; [ ericsagnes ];
+ maintainers = with lib.maintainers; [ ];
doc = ./default.md;
};
diff --git a/nixos/modules/image/file-options.nix b/nixos/modules/image/file-options.nix
index dff30e7408af..4a6b377111a0 100644
--- a/nixos/modules/image/file-options.nix
+++ b/nixos/modules/image/file-options.nix
@@ -9,6 +9,7 @@
baseName = lib.mkOption {
type = lib.types.str;
default = "nixos-image-${config.system.nixos.label}-${pkgs.stdenv.hostPlatform.system}";
+ defaultText = lib.literalExpression "nixos-image-\${config.system.nixos.label}-\${pkgs.stdenv.hostPlatform.system}";
description = ''
Basename of the image filename without any extension (e.g. `image_1`).
'';
@@ -29,6 +30,7 @@
fileName = lib.mkOption {
type = lib.types.str;
default = "${config.image.baseName}.${config.image.extension}";
+ defaultText = lib.literalExpression "\${config.image.baseName}.\${config.image.extension}";
description = ''
Filename of the image including all extensions (e.g `image_1.raw` or
`image_1.raw.zst`).
@@ -38,6 +40,7 @@
filePath = lib.mkOption {
type = lib.types.str;
default = config.image.fileName;
+ defaultText = lib.literalExpression "config.image.fileName";
description = ''
Path of the image, relative to `$out` in `system.build.image`.
While it defaults to `config.image.fileName`, it can be different for builders where
diff --git a/nixos/modules/image/images.nix b/nixos/modules/image/images.nix
index f3cdde327d36..b0f86ed5c455 100644
--- a/nixos/modules/image/images.nix
+++ b/nixos/modules/image/images.nix
@@ -11,6 +11,7 @@ let
imageModules = {
amazon = ../../maintainers/scripts/ec2/amazon-image.nix;
azure = ../virtualisation/azure-image.nix;
+ cloudstack = ../../maintainers/scripts/cloudstack/cloudstack-image.nix;
digital-ocean = ../virtualisation/digital-ocean-image.nix;
google-compute = ../virtualisation/google-compute-image.nix;
hyperv = ../virtualisation/hyperv-image.nix;
diff --git a/nixos/modules/image/repart.nix b/nixos/modules/image/repart.nix
index 3f8188ff44e8..13c90aa9ab7a 100644
--- a/nixos/modules/image/repart.nix
+++ b/nixos/modules/image/repart.nix
@@ -3,6 +3,7 @@
{
config,
+ options,
pkgs,
lib,
utils,
@@ -88,6 +89,31 @@ in
{
imports = [
./repart-verity-store.nix
+ ./file-options.nix
+ (lib.mkRenamedOptionModuleWith {
+ sinceRelease = 2411;
+ from = [
+ "image"
+ "repart"
+ "imageFileBasename"
+ ];
+ to = [
+ "image"
+ "baseName"
+ ];
+ })
+ (lib.mkRenamedOptionModuleWith {
+ sinceRelease = 2411;
+ from = [
+ "image"
+ "repart"
+ "imageFile"
+ ];
+ to = [
+ "image"
+ "fileName"
+ ];
+ })
];
options.image.repart = {
@@ -95,7 +121,7 @@ in
name = lib.mkOption {
type = lib.types.str;
description = ''
- Name of the image.
+ Name of the image.
If this option is unset but config.system.image.id is set,
config.system.image.id is used as the default value.
@@ -109,23 +135,6 @@ in
description = "Version of the image";
};
- imageFileBasename = lib.mkOption {
- type = lib.types.str;
- readOnly = true;
- description = ''
- Basename of the image filename without any extension (e.g. `image_1`).
- '';
- };
-
- imageFile = lib.mkOption {
- type = lib.types.str;
- readOnly = true;
- description = ''
- Filename of the image including all extensions (e.g `image_1.raw` or
- `image_1.raw.zst`).
- '';
- };
-
compression = {
enable = lib.mkEnableOption "Image compression";
@@ -250,55 +259,37 @@ in
'';
};
+ assertions = lib.mkOption {
+ type = options.assertions.type;
+ default = [ ];
+ internal = true;
+ visible = false;
+ description = ''
+ Assertions only evaluated by the repart image, not by the system toplevel.
+ '';
+ };
+
+ warnings = lib.mkOption {
+ type = options.warnings.type;
+ default = [ ];
+ internal = true;
+ visible = false;
+ description = ''
+ Warnings only evaluated by the repart image, not by the system toplevel.
+ '';
+ };
+
};
config = {
-
- assertions = lib.mapAttrsToList (
- fileName: partitionConfig:
- let
- inherit (partitionConfig) repartConfig;
- labelLength = builtins.stringLength repartConfig.Label;
- in
- {
- assertion = repartConfig ? Label -> GPTMaxLabelLength >= labelLength;
- message = ''
- The partition label '${repartConfig.Label}'
- defined for '${fileName}' is ${toString labelLength} characters long,
- but the maximum label length supported by UEFI is ${toString GPTMaxLabelLength}.
- '';
- }
- ) cfg.partitions;
-
- warnings = lib.filter (v: v != null) (
- lib.mapAttrsToList (
- fileName: partitionConfig:
- let
- inherit (partitionConfig) repartConfig;
- suggestedMaxLabelLength = GPTMaxLabelLength - 2;
- labelLength = builtins.stringLength repartConfig.Label;
- in
- if (repartConfig ? Label && labelLength >= suggestedMaxLabelLength) then
- ''
- The partition label '${repartConfig.Label}'
- defined for '${fileName}' is ${toString labelLength} characters long.
- The suggested maximum label length is ${toString suggestedMaxLabelLength}.
-
- If you use sytemd-sysupdate style A/B updates, this might
- not leave enough space to increment the version number included in
- the label in a future release. For example, if your label is
- ${toString GPTMaxLabelLength} characters long (the maximum enforced by UEFI) and
- you're at version 9, you cannot increment this to 10.
- ''
- else
- null
- ) cfg.partitions
- );
-
- image.repart =
+ image.baseName =
let
version = config.image.repart.version;
versionInfix = if version != null then "_${version}" else "";
+ in
+ cfg.name + versionInfix;
+ image.extension =
+ let
compressionSuffix =
lib.optionalString cfg.compression.enable
{
@@ -308,6 +299,11 @@ in
}
."${cfg.compression.algorithm}";
+ in
+ "raw" + compressionSuffix;
+
+ image.repart =
+ let
makeClosure = paths: pkgs.closureInfo { rootPaths = paths; };
# Add the closure of the provided Nix store paths to cfg.partitions so
@@ -321,9 +317,6 @@ in
in
{
name = lib.mkIf (config.system.image.id != null) (lib.mkOptionDefault config.system.image.id);
- imageFileBasename = cfg.name + versionInfix;
- imageFile = cfg.imageFileBasename + ".raw" + compressionSuffix;
-
compression = {
# Generally default to slightly faster than default compression
# levels under the assumption that most of the building will be done
@@ -339,6 +332,47 @@ in
};
finalPartitions = lib.mapAttrs addClosure cfg.partitions;
+
+ assertions = lib.mapAttrsToList (
+ fileName: partitionConfig:
+ let
+ inherit (partitionConfig) repartConfig;
+ labelLength = builtins.stringLength repartConfig.Label;
+ in
+ {
+ assertion = repartConfig ? Label -> GPTMaxLabelLength >= labelLength;
+ message = ''
+ The partition label '${repartConfig.Label}'
+ defined for '${fileName}' is ${toString labelLength} characters long,
+ but the maximum label length supported by UEFI is ${toString GPTMaxLabelLength}.
+ '';
+ }
+ ) cfg.partitions;
+
+ warnings = lib.filter (v: v != null) (
+ lib.mapAttrsToList (
+ fileName: partitionConfig:
+ let
+ inherit (partitionConfig) repartConfig;
+ suggestedMaxLabelLength = GPTMaxLabelLength - 2;
+ labelLength = builtins.stringLength repartConfig.Label;
+ in
+ if (repartConfig ? Label && labelLength >= suggestedMaxLabelLength) then
+ ''
+ The partition label '${repartConfig.Label}'
+ defined for '${fileName}' is ${toString labelLength} characters long.
+ The suggested maximum label length is ${toString suggestedMaxLabelLength}.
+
+ If you use sytemd-sysupdate style A/B updates, this might
+ not leave enough space to increment the version number included in
+ the label in a future release. For example, if your label is
+ ${toString GPTMaxLabelLength} characters long (the maximum enforced by UEFI) and
+ you're at version 9, you cannot increment this to 10.
+ ''
+ else
+ null
+ ) cfg.partitions
+ );
};
system.build.image =
@@ -354,26 +388,26 @@ in
);
mkfsEnv = mkfsOptionsToEnv cfg.mkfsOptions;
+ val = pkgs.callPackage ./repart-image.nix {
+ systemd = cfg.package;
+ imageFileBasename = config.image.baseName;
+ inherit (cfg)
+ name
+ version
+ compression
+ split
+ seed
+ sectorSize
+ finalPartitions
+ ;
+ inherit fileSystems definitionsDirectory mkfsEnv;
+ };
in
- pkgs.callPackage ./repart-image.nix {
- systemd = cfg.package;
- inherit (cfg)
- name
- version
- imageFileBasename
- compression
- split
- seed
- sectorSize
- finalPartitions
- ;
- inherit fileSystems definitionsDirectory mkfsEnv;
- };
-
- meta.maintainers = with lib.maintainers; [
- nikstur
- willibutz
- ];
-
+ lib.asserts.checkAssertWarn cfg.assertions cfg.warnings val;
};
+
+ meta.maintainers = with lib.maintainers; [
+ nikstur
+ willibutz
+ ];
}
diff --git a/nixos/modules/installer/tools/nix-fallback-paths.nix b/nixos/modules/installer/tools/nix-fallback-paths.nix
index 93c52da399ff..55a0c46237ed 100644
--- a/nixos/modules/installer/tools/nix-fallback-paths.nix
+++ b/nixos/modules/installer/tools/nix-fallback-paths.nix
@@ -1,8 +1,8 @@
{
- x86_64-linux = "/nix/store/00a7rdfwhm6avqkgj68grddbzyz3h6ql-nix-2.24.13";
- i686-linux = "/nix/store/s6c620v60hfishzi1lbfpryk65lbvg8g-nix-2.24.13";
- aarch64-linux = "/nix/store/7yg9is1shh3383iwi6qynz3vh91l1f9d-nix-2.24.13";
- riscv64-linux = "/nix/store/fagjkrx5r6p52xp8qb5581bmnlgp01sn-nix-riscv64-unknown-linux-gnu-2.24.13";
- x86_64-darwin = "/nix/store/ifby7rrgkkly5pzjnyac90lzvrak3i9y-nix-2.24.13";
- aarch64-darwin = "/nix/store/b0rbdp6ba2fprprpgsw1a8pplzg0j324-nix-2.24.13";
+ x86_64-linux = "/nix/store/pfh6bq2wxbpp3xz5sinymmp44n505zh8-nix-2.28.3";
+ i686-linux = "/nix/store/nfxdfb9zcrm9sqkw8xhdqs7vcvrwp1k2-nix-2.28.3";
+ aarch64-linux = "/nix/store/7w6fj8s7h4pcmx38m1f51xd93ywizm4i-nix-2.28.3";
+ riscv64-linux = "/nix/store/nnynd5vfd6pf9jkp13bmj44rlrd61l3h-nix-riscv64-unknown-linux-gnu-2.28.3";
+ x86_64-darwin = "/nix/store/rdxbh5m09c9i2s7zkh7b8g6mnrpmaa19-nix-2.28.3";
+ aarch64-darwin = "/nix/store/wjrdsqbaial7pl9vfhqc7cpzd9lqcr6a-nix-2.28.3";
}
diff --git a/nixos/modules/installer/tools/tools.nix b/nixos/modules/installer/tools/tools.nix
index 74a22b1b6b36..bbbe0a7ed4a9 100644
--- a/nixos/modules/installer/tools/tools.nix
+++ b/nixos/modules/installer/tools/tools.nix
@@ -159,8 +159,8 @@ let
# programs.firefox.enable = true;
- # List packages installed in system profile. To search, run:
- # \$ nix search wget
+ # List packages installed in system profile.
+ # You can use https://search.nixos.org/ to find more packages (and options).
# environment.systemPackages = with pkgs; [
# vim # Do not forget to add an editor to edit configuration.nix! The Nano editor is also installed by default.
# wget
@@ -274,7 +274,7 @@ in
description = ''
Disable nixos-rebuild, nixos-generate-config, nixos-installer
and other NixOS tools. This is useful to shrink embedded,
- read-only systems which are not expected to be rebuild or
+ read-only systems which are not expected to rebuild or
reconfigure themselves. Use at your own risk!
'';
};
diff --git a/nixos/modules/misc/ids.nix b/nixos/modules/misc/ids.nix
index fcdffc520967..ebd53cb17ea5 100644
--- a/nixos/modules/misc/ids.nix
+++ b/nixos/modules/misc/ids.nix
@@ -167,7 +167,7 @@ in
nsd = 126;
gitolite = 127;
znc = 128;
- polipo = 129;
+ # polipo = 129; removed 2025-05-18
mopidy = 130;
#docker = 131; # unused
gdm = 132;
@@ -507,7 +507,7 @@ in
nsd = 126;
gitolite = 127;
znc = 128;
- polipo = 129;
+ # polipo = 129; removed 2025-05-18
mopidy = 130;
docker = 131;
gdm = 132;
diff --git a/nixos/modules/module-list.nix b/nixos/modules/module-list.nix
index 0a23a0d0d653..ff65ce2125f1 100644
--- a/nixos/modules/module-list.nix
+++ b/nixos/modules/module-list.nix
@@ -1,4 +1,5 @@
[
+ # keep-sorted start case=no numeric=yes
./config/appstream.nix
./config/console.nix
./config/debug-info.nix
@@ -16,10 +17,10 @@
./config/malloc.nix
./config/mysql.nix
./config/networking.nix
- ./config/nix.nix
./config/nix-channel.nix
./config/nix-flakes.nix
./config/nix-remote-build.nix
+ ./config/nix.nix
./config/nsswitch.nix
./config/power-management.nix
./config/qt.nix
@@ -55,8 +56,8 @@
./hardware/coral.nix
./hardware/corectrl.nix
./hardware/cpu/amd-microcode.nix
- ./hardware/cpu/amd-sev.nix
./hardware/cpu/amd-ryzen-smu.nix
+ ./hardware/cpu/amd-sev.nix
./hardware/cpu/intel-microcode.nix
./hardware/cpu/intel-sgx.nix
./hardware/cpu/x86-msr.nix
@@ -121,8 +122,8 @@
./hardware/video/webcam/ipu6.nix
./hardware/wooting.nix
./hardware/xone.nix
- ./hardware/xpadneo.nix
./hardware/xpad-noone.nix
+ ./hardware/xpadneo.nix
./i18n/input-method/default.nix
./i18n/input-method/fcitx5.nix
./i18n/input-method/hime.nix
@@ -144,8 +145,8 @@
./misc/mandoc.nix
./misc/meta.nix
./misc/nixops-autoluks.nix
- ./misc/nixpkgs.nix
./misc/nixpkgs-flake.nix
+ ./misc/nixpkgs.nix
./misc/passthru.nix
./misc/version.nix
./misc/wordlist.nix
@@ -159,6 +160,7 @@
./programs/arp-scan.nix
./programs/atop.nix
./programs/ausweisapp.nix
+ ./programs/autoenv.nix
./programs/autojump.nix
./programs/bandwhich.nix
./programs/bash-my-aws.nix
@@ -167,8 +169,8 @@
./programs/bash/blesh.nix
./programs/bash/ls-colors.nix
./programs/bash/undistract-me.nix
- ./programs/bazecor.nix
./programs/bat.nix
+ ./programs/bazecor.nix
./programs/bcc.nix
./programs/benchexec.nix
./programs/browserpass.nix
@@ -180,12 +182,11 @@
./programs/chromium.nix
./programs/clash-verge.nix
./programs/cnping.nix
- ./programs/cpu-energy-meter.nix
./programs/command-not-found/command-not-found.nix
./programs/coolercontrol.nix
./programs/corefreq.nix
+ ./programs/cpu-energy-meter.nix
./programs/criu.nix
- ./programs/darling.nix
./programs/dconf.nix
./programs/digitalbitbox/default.nix
./programs/direnv.nix
@@ -215,8 +216,8 @@
./programs/gdk-pixbuf.nix
./programs/geary.nix
./programs/ghidra.nix
- ./programs/git.nix
./programs/git-worktree-switcher.nix
+ ./programs/git.nix
./programs/gnome-disks.nix
./programs/gnome-terminal.nix
./programs/gnupg.nix
@@ -227,23 +228,23 @@
./programs/haguichi.nix
./programs/hamster.nix
./programs/htop.nix
+ ./programs/i3lock.nix
./programs/iay.nix
./programs/iftop.nix
- ./programs/i3lock.nix
./programs/iio-hyprland.nix
./programs/immersed.nix
./programs/iotop.nix
./programs/java.nix
./programs/joycond-cemuhook.nix
./programs/k3b.nix
- ./programs/kde-pim.nix
./programs/k40-whisperer.nix
./programs/kbdlight.nix
./programs/kclock.nix
+ ./programs/kde-pim.nix
./programs/kdeconnect.nix
+ ./programs/kubeswitch.nix
./programs/ladybird.nix
./programs/lazygit.nix
- ./programs/kubeswitch.nix
./programs/less.nix
./programs/liboping.nix
./programs/light.nix
@@ -273,10 +274,10 @@
./programs/npm.nix
./programs/ns-usbloader.nix
./programs/oblogout.nix
+ ./programs/obs-studio.nix
./programs/oddjobd.nix
./programs/opengamepadui.nix
./programs/openvpn3.nix
- ./programs/obs-studio.nix
./programs/partition-manager.nix
./programs/pay-respects.nix
./programs/plotinus.nix
@@ -290,10 +291,11 @@
./programs/quark-goldleaf.nix
./programs/regreet.nix
./programs/rog-control-center.nix
+ ./programs/rush.nix
./programs/rust-motd.nix
./programs/ryzen-monitor-ng.nix
- ./programs/screen.nix
./programs/schroot.nix
+ ./programs/screen.nix
./programs/seahorse.nix
./programs/sedutil.nix
./programs/shadow.nix
@@ -313,6 +315,7 @@
./programs/system-config-printer.nix
./programs/systemtap.nix
./programs/tcpdump.nix
+ ./programs/television.nix
./programs/thefuck.nix
./programs/thunar.nix
./programs/thunderbird.nix
@@ -328,8 +331,9 @@
./programs/vivid.nix
./programs/wavemon.nix
./programs/wayland/cardboard.nix
- ./programs/wayland/hyprlock.nix
+ ./programs/wayland/gtklock.nix
./programs/wayland/hyprland.nix
+ ./programs/wayland/hyprlock.nix
./programs/wayland/labwc.nix
./programs/wayland/miracle-wm.nix
./programs/wayland/niri.nix
@@ -341,8 +345,8 @@
./programs/weylus.nix
./programs/winbox.nix
./programs/wireshark.nix
- ./programs/xastir.nix
./programs/wshowkeys.nix
+ ./programs/xastir.nix
./programs/xfconf.nix
./programs/xfs_quota.nix
./programs/xonsh.nix
@@ -361,11 +365,13 @@
./programs/zsh/zsh.nix
./rename.nix
./security/acme
+ ./security/agnos.nix
./security/apparmor.nix
./security/audit.nix
./security/auditd.nix
./security/ca.nix
./security/chromium-suid-sandbox.nix
+ ./security/default.nix
./security/dhparams.nix
./security/doas.nix
./security/duosec.nix
@@ -383,8 +389,8 @@
./security/rngd.nix
./security/rtkit.nix
./security/soteria.nix
- ./security/sudo.nix
./security/sudo-rs.nix
+ ./security/sudo.nix
./security/systemd-confinement.nix
./security/tpm2.nix
./security/wrappers/default.nix
@@ -407,6 +413,7 @@
./services/audio/icecast.nix
./services/audio/jack.nix
./services/audio/jmusicbot.nix
+ ./services/audio/lavalink.nix
./services/audio/liquidsoap.nix
./services/audio/marytts.nix
./services/audio/mopidy.nix
@@ -434,13 +441,14 @@
./services/backup/duplicati.nix
./services/backup/duplicity.nix
./services/backup/mysql-backup.nix
+ ./services/backup/pgbackrest.nix
./services/backup/postgresql-backup.nix
./services/backup/postgresql-wal-receiver.nix
- ./services/backup/snapraid.nix
./services/backup/restic-rest-server.nix
./services/backup/restic.nix
./services/backup/rsnapshot.nix
./services/backup/sanoid.nix
+ ./services/backup/snapraid.nix
./services/backup/syncoid.nix
./services/backup/tarsnap.nix
./services/backup/tsm.nix
@@ -501,8 +509,8 @@
./services/databases/firebird.nix
./services/databases/foundationdb.nix
./services/databases/hbase-standalone.nix
- ./services/databases/influxdb.nix
./services/databases/influxdb2.nix
+ ./services/databases/influxdb.nix
./services/databases/lldap.nix
./services/databases/memcached.nix
./services/databases/monetdb.nix
@@ -513,6 +521,7 @@
./services/databases/opentsdb.nix
./services/databases/pgbouncer.nix
./services/databases/pgmanage.nix
+ ./services/databases/postgres-websockets.nix
./services/databases/postgresql.nix
./services/databases/postgrest.nix
./services/databases/redis.nix
@@ -526,16 +535,14 @@
./services/desktops/blueman.nix
./services/desktops/bonsaid.nix
./services/desktops/cpupower-gui.nix
- ./services/desktops/deepin/deepin-anything.nix
- ./services/desktops/deepin/dde-api.nix
./services/desktops/deepin/app-services.nix
+ ./services/desktops/deepin/dde-api.nix
./services/desktops/deepin/dde-daemon.nix
- ./services/desktops/dleyna-renderer.nix
- ./services/desktops/dleyna-server.nix
+ ./services/desktops/deepin/deepin-anything.nix
+ ./services/desktops/dleyna.nix
./services/desktops/espanso.nix
./services/desktops/flatpak.nix
./services/desktops/geoclue2.nix
- ./services/desktops/playerctld.nix
./services/desktops/gnome/at-spi2-core.nix
./services/desktops/gnome/evolution-data-server.nix
./services/desktops/gnome/glib-networking.nix
@@ -551,16 +558,16 @@
./services/desktops/gnome/rygel.nix
./services/desktops/gnome/sushi.nix
./services/desktops/gnome/tinysparql.nix
- ./services/desktops/gsignond.nix
./services/desktops/gvfs.nix
./services/desktops/malcontent.nix
./services/desktops/neard.nix
./services/desktops/pipewire/pipewire.nix
./services/desktops/pipewire/wireplumber.nix
+ ./services/desktops/playerctld.nix
./services/desktops/profile-sync-daemon.nix
./services/desktops/seatd.nix
- ./services/desktops/system-config-printer.nix
./services/desktops/system76-scheduler.nix
+ ./services/desktops/system-config-printer.nix
./services/desktops/telepathy.nix
./services/desktops/tumbler.nix
./services/desktops/wlock.nix
@@ -579,11 +586,11 @@
./services/development/rstudio-server/default.nix
./services/development/vsmartcard-vpcd.nix
./services/development/zammad.nix
- ./services/display-managers/default.nix
./services/display-managers/cosmic-greeter.nix
+ ./services/display-managers/default.nix
./services/display-managers/greetd.nix
- ./services/display-managers/sddm.nix
./services/display-managers/ly.nix
+ ./services/display-managers/sddm.nix
./services/editors/emacs.nix
./services/editors/haste.nix
./services/editors/infinoted.nix
@@ -597,7 +604,6 @@
./services/games/archisteamfarm.nix
./services/games/armagetronad.nix
./services/games/crossfire-server.nix
- ./services/games/deliantra-server.nix
./services/games/factorio.nix
./services/games/freeciv.nix
./services/games/mchprs.nix
@@ -636,15 +642,17 @@
./services/hardware/irqbalance.nix
./services/hardware/joycond.nix
./services/hardware/kanata.nix
+ ./services/hardware/keyd.nix
./services/hardware/kmonad.nix
./services/hardware/lcd.nix
./services/hardware/libinput.nix
./services/hardware/lirc.nix
- ./services/hardware/nvidia-container-toolkit
./services/hardware/monado.nix
+ ./services/hardware/nvidia-container-toolkit
./services/hardware/nvidia-optimus.nix
./services/hardware/openrgb.nix
./services/hardware/pcscd.nix
+ ./services/hardware/pid-fan-controller.nix
./services/hardware/pommed.nix
./services/hardware/power-profiles-daemon.nix
./services/hardware/powerstation.nix
@@ -672,7 +680,6 @@
./services/hardware/usbmuxd.nix
./services/hardware/usbrelayd.nix
./services/hardware/vdr.nix
- ./services/hardware/keyd.nix
./services/home-automation/ebusd.nix
./services/home-automation/esphome.nix
./services/home-automation/evcc.nix
@@ -684,9 +691,8 @@
./services/home-automation/wyoming/piper.nix
./services/home-automation/wyoming/satellite.nix
./services/home-automation/zigbee2mqtt.nix
- ./services/home-automation/zwave-js.nix
./services/home-automation/zwave-js-ui.nix
- ./services/logging/SystemdJournal2Gelf.nix
+ ./services/home-automation/zwave-js.nix
./services/logging/awstats.nix
./services/logging/filebeat.nix
./services/logging/fluentd.nix
@@ -703,10 +709,10 @@
./services/logging/rsyslogd.nix
./services/logging/syslog-ng.nix
./services/logging/syslogd.nix
- ./services/logging/vector.nix
+ ./services/logging/SystemdJournal2Gelf.nix
./services/logging/ulogd.nix
+ ./services/logging/vector.nix
./services/mail/automx2.nix
- ./services/mail/clamsmtp.nix
./services/mail/cyrus-imap.nix
./services/mail/davmail.nix
./services/mail/dkimproxy-out.nix
@@ -734,8 +740,8 @@
./services/mail/protonmail-bridge.nix
./services/mail/public-inbox.nix
./services/mail/roundcube.nix
- ./services/mail/rspamd.nix
./services/mail/rspamd-trainer.nix
+ ./services/mail/rspamd.nix
./services/mail/rss2email.nix
./services/mail/schleuder.nix
./services/mail/spamassassin.nix
@@ -745,10 +751,11 @@
./services/matrix/appservice-discord.nix
./services/matrix/appservice-irc.nix
./services/matrix/conduit.nix
- ./services/matrix/conduwuit.nix
+ ./services/matrix/continuwuity.nix
./services/matrix/dendrite.nix
./services/matrix/hebbot.nix
./services/matrix/hookshot.nix
+ ./services/matrix/lk-jwt-service.nix
./services/matrix/matrix-alertmanager.nix
./services/matrix/maubot.nix
./services/matrix/mautrix-meta.nix
@@ -758,8 +765,8 @@
./services/matrix/mjolnir.nix
./services/matrix/mx-puppet-discord.nix
./services/matrix/pantalaimon.nix
- ./services/matrix/synapse.nix
./services/matrix/synapse-auto-compressor.nix
+ ./services/matrix/synapse.nix
./services/misc/airsonic.nix
./services/misc/amazon-ssm-agent.nix
./services/misc/ananicy.nix
@@ -794,6 +801,7 @@
./services/misc/domoticz.nix
./services/misc/duckdns.nix
./services/misc/duckling.nix
+ ./services/misc/dump1090-fa.nix
./services/misc/dwm-status.nix
./services/misc/dysnomia.nix
./services/misc/errbot.nix
@@ -882,8 +890,8 @@
./services/misc/redlib.nix
./services/misc/redmine.nix
./services/misc/renovate.nix
- ./services/misc/rmfakecloud.nix
./services/misc/rkvm.nix
+ ./services/misc/rmfakecloud.nix
./services/misc/rshim.nix
./services/misc/safeeyes.nix
./services/misc/sdrplay.nix
@@ -895,7 +903,6 @@
./services/misc/servarr/whisparr.nix
./services/misc/serviio.nix
./services/misc/sickbeard.nix
- ./services/misc/signald.nix
./services/misc/siproxd.nix
./services/misc/snapper.nix
./services/misc/soft-serve.nix
@@ -911,20 +918,21 @@
./services/misc/sysprof.nix
./services/misc/tabby.nix
./services/misc/tandoor-recipes.nix
- ./services/misc/taskserver
./services/misc/taskchampion-sync-server.nix
+ ./services/misc/taskserver
./services/misc/tautulli.nix
./services/misc/tiddlywiki.nix
./services/misc/tp-auto-kbbl.nix
+ ./services/misc/transfer-sh.nix
./services/misc/turn-rs.nix
./services/misc/tuxclocker.nix
- ./services/misc/transfer-sh.nix
./services/misc/tzupdate.nix
./services/misc/uhub.nix
./services/misc/wastebin.nix
./services/misc/weechat.nix
./services/misc/workout-tracker.nix
./services/misc/xmrig.nix
+ ./services/misc/yarr.nix
./services/misc/ytdl-sub.nix
./services/misc/zoneminder.nix
./services/misc/zookeeper.nix
@@ -978,6 +986,7 @@
./services/monitoring/pgscv.nix
./services/monitoring/prometheus/alertmanager-gotify-bridge.nix
./services/monitoring/prometheus/alertmanager-irc-relay.nix
+ ./services/monitoring/prometheus/alertmanager-ntfy.nix
./services/monitoring/prometheus/alertmanager-webhook-logger.nix
./services/monitoring/prometheus/alertmanager.nix
./services/monitoring/prometheus/default.nix
@@ -1020,11 +1029,11 @@
./services/network-filesystems/drbd.nix
./services/network-filesystems/eris-server.nix
./services/network-filesystems/glusterfs.nix
+ ./services/network-filesystems/ipfs-cluster.nix
./services/network-filesystems/kbfs.nix
./services/network-filesystems/kubo.nix
./services/network-filesystems/litestream/default.nix
./services/network-filesystems/moosefs.nix
- ./services/network-filesystems/ipfs-cluster.nix
./services/network-filesystems/netatalk.nix
./services/network-filesystems/nfsd.nix
./services/network-filesystems/openafs/client.nix
@@ -1046,12 +1055,15 @@
./services/networking/adguardhome.nix
./services/networking/alice-lg.nix
./services/networking/amuled.nix
+ ./services/networking/anubis.nix
./services/networking/aria2.nix
./services/networking/asterisk.nix
./services/networking/atftpd.nix
./services/networking/atticd.nix
./services/networking/autossh.nix
./services/networking/avahi-daemon.nix
+ ./services/networking/ax25/axlisten.nix
+ ./services/networking/ax25/axports.nix
./services/networking/babeld.nix
./services/networking/bee.nix
./services/networking/biboumi.nix
@@ -1063,6 +1075,7 @@
./services/networking/bitlbee.nix
./services/networking/blockbook-frontend.nix
./services/networking/blocky.nix
+ ./services/networking/cato-client.nix
./services/networking/centrifugo.nix
./services/networking/cgit.nix
./services/networking/charybdis.nix
@@ -1070,8 +1083,8 @@
./services/networking/cjdns.nix
./services/networking/clatd.nix
./services/networking/cloudflare-dyndns.nix
- ./services/networking/cloudflared.nix
./services/networking/cloudflare-warp.nix
+ ./services/networking/cloudflared.nix
./services/networking/cntlm.nix
./services/networking/connman.nix
./services/networking/consul.nix
@@ -1083,9 +1096,9 @@
./services/networking/croc.nix
./services/networking/dae.nix
./services/networking/dante.nix
- ./services/networking/deconz.nix
./services/networking/ddclient.nix
./services/networking/ddns-updater.nix
+ ./services/networking/deconz.nix
./services/networking/dhcpcd.nix
./services/networking/dnscache.nix
./services/networking/dnscrypt-proxy2.nix
@@ -1107,9 +1120,9 @@
./services/networking/ferm.nix
./services/networking/firefox-syncserver.nix
./services/networking/fireqos.nix
- ./services/networking/firewall.nix
./services/networking/firewall-iptables.nix
./services/networking/firewall-nftables.nix
+ ./services/networking/firewall.nix
./services/networking/firezone/gateway.nix
./services/networking/firezone/gui-client.nix
./services/networking/firezone/headless-client.nix
@@ -1120,6 +1133,7 @@
./services/networking/freeradius.nix
./services/networking/frp.nix
./services/networking/frr.nix
+ ./services/networking/g3proxy.nix
./services/networking/gateone.nix
./services/networking/gdomap.nix
./services/networking/ghostunnel.nix
@@ -1132,11 +1146,12 @@
./services/networking/go-neb.nix
./services/networking/go-shadowsocks2.nix
./services/networking/gobgpd.nix
+ ./services/networking/godns.nix
./services/networking/gokapi.nix
./services/networking/gvpe.nix
./services/networking/hans.nix
- ./services/networking/harmonia.nix
./services/networking/haproxy.nix
+ ./services/networking/harmonia.nix
./services/networking/headscale.nix
./services/networking/hickory-dns.nix
./services/networking/hostapd.nix
@@ -1167,18 +1182,19 @@
./services/networking/kea.nix
./services/networking/keepalived/default.nix
./services/networking/keybase.nix
+ ./services/networking/kismet.nix
./services/networking/knot.nix
./services/networking/kresd.nix
./services/networking/lambdabot.nix
./services/networking/legit.nix
./services/networking/libreswan.nix
+ ./services/networking/livekit.nix
./services/networking/lldpd.nix
./services/networking/logmein-hamachi.nix
./services/networking/lokinet.nix
./services/networking/lxd-image-server.nix
./services/networking/magic-wormhole-mailbox-server.nix
./services/networking/matterbridge.nix
- ./services/networking/mptcpd.nix
./services/networking/microsocks.nix
./services/networking/mihomo.nix
./services/networking/minidlna.nix
@@ -1191,6 +1207,7 @@
./services/networking/morty.nix
./services/networking/mosquitto.nix
./services/networking/mozillavpn.nix
+ ./services/networking/mptcpd.nix
./services/networking/mstpd.nix
./services/networking/mtprotoproxy.nix
./services/networking/mtr-exporter.nix
@@ -1200,9 +1217,9 @@
./services/networking/mycelium.nix
./services/networking/namecoind.nix
./services/networking/nar-serve.nix
- ./services/networking/nat.nix
./services/networking/nat-iptables.nix
./services/networking/nat-nftables.nix
+ ./services/networking/nat.nix
./services/networking/nats.nix
./services/networking/nbd.nix
./services/networking/ncdns.nix
@@ -1221,15 +1238,15 @@
./services/networking/nix-serve.nix
./services/networking/nix-store-gcs-proxy.nix
./services/networking/nixops-dns.nix
+ ./services/networking/nm-file-secret-agent.nix
./services/networking/nncp.nix
./services/networking/nntp-proxy.nix
- ./services/networking/nm-file-secret-agent.nix
./services/networking/nomad.nix
./services/networking/nsd.nix
./services/networking/ntopng.nix
./services/networking/ntp/chrony.nix
- ./services/networking/ntp/ntpd.nix
./services/networking/ntp/ntpd-rs.nix
+ ./services/networking/ntp/ntpd.nix
./services/networking/ntp/openntpd.nix
./services/networking/nullidentdmod.nix
./services/networking/nylon.nix
@@ -1243,19 +1260,18 @@
./services/networking/openvpn.nix
./services/networking/ostinato.nix
./services/networking/owamp.nix
- ./services/networking/pyload.nix
./services/networking/pdns-recursor.nix
./services/networking/pdnsd.nix
./services/networking/peroxide.nix
./services/networking/picosnitch.nix
./services/networking/pixiecore.nix
./services/networking/pleroma.nix
- ./services/networking/polipo.nix
./services/networking/powerdns.nix
./services/networking/pppd.nix
./services/networking/pptpd.nix
./services/networking/privoxy.nix
./services/networking/prosody.nix
+ ./services/networking/pyload.nix
./services/networking/quassel.nix
./services/networking/quicktun.nix
./services/networking/quorum.nix
@@ -1274,19 +1290,19 @@
./services/networking/rpcbind.nix
./services/networking/rxe.nix
./services/networking/sabnzbd.nix
- ./services/networking/scion/scion.nix
./services/networking/scion/scion-control.nix
./services/networking/scion/scion-daemon.nix
./services/networking/scion/scion-dispatcher.nix
- ./services/networking/scion/scion-router.nix
./services/networking/scion/scion-ip-gateway.nix
+ ./services/networking/scion/scion-router.nix
+ ./services/networking/scion/scion.nix
./services/networking/seafile.nix
./services/networking/searx.nix
./services/networking/shadowsocks.nix
./services/networking/shairport-sync.nix
./services/networking/shellhub-agent.nix
- ./services/networking/shorewall.nix
./services/networking/shorewall6.nix
+ ./services/networking/shorewall.nix
./services/networking/sing-box.nix
./services/networking/sitespeed-io.nix
./services/networking/skydns.nix
@@ -1300,7 +1316,6 @@
./services/networking/spacecookie.nix
./services/networking/spiped.nix
./services/networking/squid.nix
- ./services/networking/g3proxy.nix
./services/networking/ssh/sshd.nix
./services/networking/sslh.nix
./services/networking/strongswan-swanctl/module.nix
@@ -1314,9 +1329,9 @@
./services/networking/syncplay.nix
./services/networking/syncthing-relay.nix
./services/networking/syncthing.nix
- ./services/networking/tailscale.nix
./services/networking/tailscale-auth.nix
./services/networking/tailscale-derper.nix
+ ./services/networking/tailscale.nix
./services/networking/tayga.nix
./services/networking/tcpcrypt.nix
./services/networking/teamspeak3.nix
@@ -1341,40 +1356,41 @@
./services/networking/uptermd.nix
./services/networking/v2ray.nix
./services/networking/v2raya.nix
- ./services/networking/veilid.nix
./services/networking/vdirsyncer.nix
+ ./services/networking/veilid.nix
./services/networking/vsftpd.nix
+ ./services/networking/vwifi.nix
./services/networking/wasabibackend.nix
+ ./services/networking/webhook.nix
./services/networking/websockify.nix
./services/networking/wg-access-server.nix
./services/networking/wg-netmanager.nix
- ./services/networking/whoogle-search.nix
- ./services/networking/wvdial.nix
- ./services/networking/webhook.nix
./services/networking/wg-quick.nix
./services/networking/wgautomesh.nix
- ./services/networking/wireguard.nix
+ ./services/networking/whoogle-search.nix
./services/networking/wireguard-networkd.nix
+ ./services/networking/wireguard.nix
./services/networking/wpa_supplicant.nix
./services/networking/wstunnel.nix
+ ./services/networking/wvdial.nix
./services/networking/x2goserver.nix
./services/networking/xandikos.nix
./services/networking/xinetd.nix
./services/networking/xl2tpd.nix
./services/networking/xray.nix
./services/networking/xrdp.nix
+ ./services/networking/yggdrasil-jumper.nix
./services/networking/yggdrasil.nix
./services/networking/zapret.nix
- ./services/networking/yggdrasil-jumper.nix
- ./services/networking/zerobin.nix
./services/networking/zenohd.nix
+ ./services/networking/zerobin.nix
./services/networking/zeronet.nix
- ./services/networking/zerotierone.nix
./services/networking/zeronsd.nix
+ ./services/networking/zerotierone.nix
./services/networking/znc/default.nix
+ ./services/printing/cups-pdf.nix
./services/printing/cupsd.nix
./services/printing/ipp-usb.nix
- ./services/printing/cups-pdf.nix
./services/scheduling/atd.nix
./services/scheduling/cron.nix
./services/scheduling/fcron.nix
@@ -1398,6 +1414,7 @@
./services/security/certmgr.nix
./services/security/cfssl.nix
./services/security/clamav.nix
+ ./services/security/e-imzo.nix
./services/security/endlessh-go.nix
./services/security/endlessh.nix
./services/security/esdm.nix
@@ -1413,12 +1430,14 @@
./services/security/kanidm.nix
./services/security/munge.nix
./services/security/nginx-sso.nix
- ./services/security/oauth2-proxy.nix
./services/security/oauth2-proxy-nginx.nix
+ ./services/security/oauth2-proxy.nix
+ ./services/security/openbao.nix
./services/security/opensnitch.nix
./services/security/paretosecurity.nix
./services/security/pass-secret-service.nix
./services/security/physlock.nix
+ ./services/security/pocket-id.nix
./services/security/shibboleth-sp.nix
./services/security/sks.nix
./services/security/sshguard.nix
@@ -1429,8 +1448,8 @@
./services/security/torify.nix
./services/security/torsocks.nix
./services/security/usbguard.nix
- ./services/security/vault.nix
./services/security/vault-agent.nix
+ ./services/security/vault.nix
./services/security/vaultwarden/default.nix
./services/security/yubikey-agent.nix
./services/system/automatic-timezoned.nix
@@ -1461,48 +1480,49 @@
./services/torrent/opentracker.nix
./services/torrent/peerflix.nix
./services/torrent/rtorrent.nix
- ./services/torrent/transmission.nix
./services/torrent/torrentstream.nix
+ ./services/torrent/transmission.nix
./services/tracing/tempo.nix
./services/ttys/getty.nix
./services/ttys/gpm.nix
./services/ttys/kmscon.nix
./services/video/epgstation/default.nix
- ./services/video/go2rtc/default.nix
./services/video/frigate.nix
+ ./services/video/go2rtc/default.nix
+ ./services/video/mediamtx.nix
./services/video/mirakurun.nix
./services/video/photonvision.nix
- ./services/video/mediamtx.nix
./services/video/ustreamer.nix
./services/video/v4l2-relayd.nix
./services/video/wivrn.nix
./services/wayland/cage.nix
./services/wayland/hypridle.nix
./services/web-apps/actual.nix
- ./services/web-apps/akkoma.nix
./services/web-apps/agorakit.nix
+ ./services/web-apps/akkoma.nix
./services/web-apps/alps.nix
./services/web-apps/anuko-time-tracker.nix
- ./services/web-apps/archtika.nix
./services/web-apps/artalk.nix
./services/web-apps/audiobookshelf.nix
+ ./services/web-apps/baikal.nix
./services/web-apps/bluemap.nix
./services/web-apps/bookstack.nix
./services/web-apps/c2fmzq-server.nix
./services/web-apps/calibre-web.nix
./services/web-apps/castopod.nix
- ./services/web-apps/coder.nix
./services/web-apps/changedetection-io.nix
./services/web-apps/chatgpt-retrieval-plugin.nix
./services/web-apps/cloudlog.nix
./services/web-apps/code-server.nix
+ ./services/web-apps/coder.nix
./services/web-apps/collabora-online.nix
./services/web-apps/commafeed.nix
./services/web-apps/convos.nix
+ ./services/web-apps/cook-cli.nix
./services/web-apps/crabfit.nix
- ./services/web-apps/davis.nix
./services/web-apps/cryptpad.nix
./services/web-apps/dashy.nix
+ ./services/web-apps/davis.nix
./services/web-apps/dependency-track.nix
./services/web-apps/dex.nix
./services/web-apps/discourse.nix
@@ -1515,8 +1535,8 @@
./services/web-apps/ethercalc.nix
./services/web-apps/fider.nix
./services/web-apps/filesender.nix
- ./services/web-apps/firefly-iii.nix
./services/web-apps/firefly-iii-data-importer.nix
+ ./services/web-apps/firefly-iii.nix
./services/web-apps/flarum.nix
./services/web-apps/fluidd.nix
./services/web-apps/freshrss.nix
@@ -1526,13 +1546,11 @@
./services/web-apps/gerrit.nix
./services/web-apps/glance.nix
./services/web-apps/glitchtip.nix
+ ./services/web-apps/goatcounter.nix
./services/web-apps/gotify-server.nix
./services/web-apps/gotosocial.nix
./services/web-apps/grav.nix
./services/web-apps/grocy.nix
- ./services/web-apps/part-db.nix
- ./services/web-apps/pixelfed.nix
- ./services/web-apps/goatcounter.nix
./services/web-apps/guacamole-client.nix
./services/web-apps/guacamole-server.nix
./services/web-apps/hatsu.nix
@@ -1546,15 +1564,16 @@
./services/web-apps/icingaweb2/icingaweb2.nix
./services/web-apps/icingaweb2/module-monitoring.nix
./services/web-apps/ifm.nix
- ./services/web-apps/immich.nix
./services/web-apps/immich-public-proxy.nix
+ ./services/web-apps/immich.nix
./services/web-apps/invidious.nix
./services/web-apps/invoiceplane.nix
./services/web-apps/isso.nix
./services/web-apps/jirafeau.nix
./services/web-apps/jitsi-meet.nix
- ./services/web-apps/kasmweb/default.nix
./services/web-apps/kanboard.nix
+ ./services/web-apps/karakeep.nix
+ ./services/web-apps/kasmweb/default.nix
./services/web-apps/kavita.nix
./services/web-apps/keycloak.nix
./services/web-apps/kimai.nix
@@ -1567,39 +1586,44 @@
./services/web-apps/matomo.nix
./services/web-apps/mattermost.nix
./services/web-apps/mealie.nix
+ ./services/web-apps/mediagoblin.nix
./services/web-apps/mediawiki.nix
./services/web-apps/meme-bingo-web.nix
./services/web-apps/microbin.nix
./services/web-apps/miniflux.nix
./services/web-apps/misskey.nix
+ ./services/web-apps/mobilizon.nix
./services/web-apps/monica.nix
./services/web-apps/moodle.nix
./services/web-apps/movim.nix
./services/web-apps/netbox.nix
- ./services/web-apps/nextcloud.nix
./services/web-apps/nextcloud-notify_push.nix
./services/web-apps/nextcloud-whiteboard-server.nix
+ ./services/web-apps/nextcloud.nix
./services/web-apps/nextjs-ollama-llm-ui.nix
./services/web-apps/nexus.nix
./services/web-apps/nifi.nix
./services/web-apps/node-red.nix
./services/web-apps/nostr-rs-relay.nix
./services/web-apps/ocis.nix
+ ./services/web-apps/olivetin.nix
+ ./services/web-apps/oncall.nix
./services/web-apps/onlyoffice.nix
- ./services/web-apps/openvscode-server.nix
- ./services/web-apps/mediagoblin.nix
./services/web-apps/open-web-calendar.nix
- ./services/web-apps/mobilizon.nix
+ ./services/web-apps/opencloud.nix
+ ./services/web-apps/openvscode-server.nix
./services/web-apps/openwebrx.nix
./services/web-apps/outline.nix
+ ./services/web-apps/part-db.nix
./services/web-apps/pds.nix
./services/web-apps/peering-manager.nix
./services/web-apps/peertube.nix
./services/web-apps/pgpkeyserver-lite.nix
- ./services/web-apps/phylactery.nix
./services/web-apps/photoprism.nix
+ ./services/web-apps/phylactery.nix
./services/web-apps/pict-rs.nix
./services/web-apps/pingvin-share.nix
+ ./services/web-apps/pixelfed.nix
./services/web-apps/plantuml-server.nix
./services/web-apps/plausible.nix
./services/web-apps/porn-vault/default.nix
@@ -1608,29 +1632,31 @@
./services/web-apps/pretix.nix
./services/web-apps/privatebin.nix
./services/web-apps/prosody-filer.nix
+ ./services/web-apps/readeck.nix
+ ./services/web-apps/reposilite.nix
./services/web-apps/rimgo.nix
+ ./services/web-apps/rss-bridge.nix
./services/web-apps/rutorrent.nix
./services/web-apps/screego.nix
- ./services/web-apps/sftpgo.nix
- ./services/web-apps/strfry.nix
- ./services/web-apps/suwayomi-server.nix
- ./services/web-apps/readeck.nix
- ./services/web-apps/rss-bridge.nix
./services/web-apps/selfoss.nix
+ ./services/web-apps/sftpgo.nix
./services/web-apps/shiori.nix
./services/web-apps/silverbullet.nix
./services/web-apps/simplesamlphp.nix
./services/web-apps/slskd.nix
./services/web-apps/snipe-it.nix
./services/web-apps/sogo.nix
- ./services/web-apps/stirling-pdf.nix
./services/web-apps/stash.nix
+ ./services/web-apps/stirling-pdf.nix
+ ./services/web-apps/strfry.nix
+ ./services/web-apps/suwayomi-server.nix
./services/web-apps/trilium.nix
./services/web-apps/tt-rss.nix
./services/web-apps/vikunja.nix
./services/web-apps/wakapi.nix
./services/web-apps/weblate.nix
./services/web-apps/whitebophir.nix
+ ./services/web-apps/whoami.nix
./services/web-apps/wiki-js.nix
./services/web-apps/windmill.nix
./services/web-apps/wordpress.nix
@@ -1719,10 +1745,10 @@
./services/x11/xserver.nix
./system/activation/activatable-system.nix
./system/activation/activation-script.nix
+ ./system/activation/bootspec.nix
./system/activation/pre-switch-check.nix
./system/activation/specialisation.nix
./system/activation/switchable-system.nix
- ./system/activation/bootspec.nix
./system/activation/top-level.nix
./system/boot/binfmt.nix
./system/boot/clevis.nix
@@ -1734,37 +1760,36 @@
./system/boot/kernel.nix
./system/boot/kexec.nix
./system/boot/loader/efi.nix
+ ./system/boot/loader/external/external.nix
./system/boot/loader/generations-dir/generations-dir.nix
./system/boot/loader/generic-extlinux-compatible
./system/boot/loader/grub/grub.nix
./system/boot/loader/grub/ipxe.nix
./system/boot/loader/grub/memtest.nix
- ./system/boot/loader/external/external.nix
./system/boot/loader/init-script/init-script.nix
./system/boot/loader/limine/limine.nix
./system/boot/loader/loader.nix
./system/boot/loader/systemd-boot/systemd-boot.nix
./system/boot/luksroot.nix
- ./system/boot/stratisroot.nix
./system/boot/modprobe.nix
./system/boot/networkd.nix
- ./system/boot/uki.nix
- ./system/boot/unl0kr.nix
./system/boot/plymouth.nix
./system/boot/resolved.nix
./system/boot/shutdown.nix
./system/boot/stage-1.nix
./system/boot/stage-2.nix
+ ./system/boot/stratisroot.nix
./system/boot/systemd.nix
./system/boot/systemd/coredump.nix
./system/boot/systemd/dm-verity.nix
./system/boot/systemd/fido2.nix
+ ./system/boot/systemd/homed.nix
./system/boot/systemd/initrd-secrets.nix
./system/boot/systemd/initrd.nix
- ./system/boot/systemd/journald.nix
./system/boot/systemd/journald-gateway.nix
./system/boot/systemd/journald-remote.nix
./system/boot/systemd/journald-upload.nix
+ ./system/boot/systemd/journald.nix
./system/boot/systemd/logind.nix
./system/boot/systemd/nspawn.nix
./system/boot/systemd/oomd.nix
@@ -1776,9 +1801,10 @@
./system/boot/systemd/tpm2.nix
./system/boot/systemd/user.nix
./system/boot/systemd/userdbd.nix
- ./system/boot/systemd/homed.nix
./system/boot/timesyncd.nix
./system/boot/tmp.nix
+ ./system/boot/uki.nix
+ ./system/boot/unl0kr.nix
./system/boot/uvesafb.nix
./system/etc/etc-activation.nix
./tasks/auto-upgrade.nix
@@ -1802,8 +1828,8 @@
./tasks/filesystems/ntfs.nix
./tasks/filesystems/overlayfs.nix
./tasks/filesystems/reiserfs.nix
- ./tasks/filesystems/sshfs.nix
./tasks/filesystems/squashfs.nix
+ ./tasks/filesystems/sshfs.nix
./tasks/filesystems/unionfs-fuse.nix
./tasks/filesystems/vboxsf.nix
./tasks/filesystems/vfat.nix
@@ -1830,19 +1856,19 @@
./virtualisation/docker.nix
./virtualisation/ecs-agent.nix
./virtualisation/hyperv-guest.nix
- ./virtualisation/incus.nix
./virtualisation/incus-agent.nix
+ ./virtualisation/incus.nix
./virtualisation/kvmgt.nix
./virtualisation/libvirtd.nix
./virtualisation/lxc.nix
./virtualisation/lxcfs.nix
- ./virtualisation/lxd.nix
./virtualisation/lxd-agent.nix
+ ./virtualisation/lxd.nix
./virtualisation/multipass.nix
./virtualisation/nixos-containers.nix
./virtualisation/oci-containers.nix
- ./virtualisation/openstack-options.nix
./virtualisation/oci-options.nix
+ ./virtualisation/openstack-options.nix
./virtualisation/openvswitch.nix
./virtualisation/parallels-guest.nix
./virtualisation/podman/default.nix
@@ -1857,6 +1883,7 @@
./virtualisation/waydroid.nix
./virtualisation/xe-guest-utilities.nix
./virtualisation/xen-dom0.nix
+ # keep-sorted end
{
documentation.nixos.extraModules = [
./virtualisation/qemu-vm.nix
diff --git a/nixos/modules/profiles/nix-builder-vm.nix b/nixos/modules/profiles/nix-builder-vm.nix
index 1f5eff8f133f..50dde02eef64 100644
--- a/nixos/modules/profiles/nix-builder-vm.nix
+++ b/nixos/modules/profiles/nix-builder-vm.nix
@@ -126,6 +126,20 @@ in
# TODO system.switch.enable = false;?
system.disableInstallerTools = true;
+ # Allow the system derivation to be substituted, so that
+ # users are less likely to run into a state where they need
+ # the builder running to build the builder if they just want
+ # to make a tweak that only affects the macOS side of things,
+ # like changing the QEMU args.
+ #
+ # TODO(winter): Move to qemu-vm? Trying it here for now as a
+ # low impact change that'll probably improve people's experience.
+ #
+ # (I have no clue what is going on in https://github.com/nix-darwin/nix-darwin/issues/1081
+ # though, as this fix would only apply to one person in that thread... hopefully someone
+ # comes across with a reproducer if this doesn't do it.)
+ system.systemBuilderArgs.allowSubstitutes = true;
+
nix.settings = {
min-free = cfg.min-free;
@@ -163,7 +177,7 @@ in
hostPkgs = config.virtualisation.host.pkgs;
- script = hostPkgs.writeShellScriptBin "create-builder" (
+ add-keys = hostPkgs.writeShellScriptBin "add-keys" (
''
set -euo pipefail
''
@@ -191,10 +205,22 @@ in
if ! ${hostPkgs.diffutils}/bin/cmp "''${PUBLIC_KEY}" ${publicKey}; then
(set -x; sudo --reset-timestamp ${installCredentials} "''${KEYS}")
fi
- KEYS="$(${hostPkgs.nix}/bin/nix-store --add "$KEYS")" ${lib.getExe config.system.build.vm}
''
);
+ run-builder = hostPkgs.writeShellScriptBin "run-builder" (''
+ set -euo pipefail
+ KEYS="''${KEYS:-./keys}"
+ KEYS="$(${hostPkgs.nix}/bin/nix-store --add "$KEYS")" ${lib.getExe config.system.build.vm}
+ '');
+
+ script = hostPkgs.writeShellScriptBin "create-builder" (''
+ set -euo pipefail
+ export KEYS="''${KEYS:-./keys}"
+ ${lib.getExe add-keys}
+ ${lib.getExe run-builder}
+ '');
+
in
script.overrideAttrs (old: {
pos = __curPos; # sets meta.position to point here; see script binding above for package definition
@@ -205,6 +231,8 @@ in
# Let users in the repl inspect the config
nixosConfig = config;
nixosOptions = options;
+
+ inherit add-keys run-builder;
};
});
diff --git a/nixos/modules/programs/amnezia-vpn.nix b/nixos/modules/programs/amnezia-vpn.nix
index e2b49ebb6e7b..22db93bde51c 100644
--- a/nixos/modules/programs/amnezia-vpn.nix
+++ b/nixos/modules/programs/amnezia-vpn.nix
@@ -10,16 +10,24 @@ in
{
options.programs.amnezia-vpn = {
enable = lib.mkEnableOption "The AmneziaVPN client";
+ package = lib.mkPackageOption pkgs "amnezia-vpn" { };
};
config = lib.mkIf cfg.enable {
- environment.systemPackages = [ pkgs.amnezia-vpn ];
- services.dbus.packages = [ pkgs.amnezia-vpn ];
+ environment.systemPackages = [ cfg.package ];
+ services.dbus.packages = [ cfg.package ];
services.resolved.enable = true;
systemd = {
- packages = [ pkgs.amnezia-vpn ];
- services."AmneziaVPN".wantedBy = [ "multi-user.target" ];
+ packages = [ cfg.package ];
+ services."AmneziaVPN" = {
+ wantedBy = [ "multi-user.target" ];
+ path = with pkgs; [
+ procps
+ iproute2
+ sudo
+ ];
+ };
};
};
diff --git a/nixos/modules/programs/bash/bash.nix b/nixos/modules/programs/bash/bash.nix
index 213f0d2698ae..05b41ae619fc 100644
--- a/nixos/modules/programs/bash/bash.nix
+++ b/nixos/modules/programs/bash/bash.nix
@@ -111,6 +111,20 @@ in
internal = true;
};
+ logout = lib.mkOption {
+ # Reset the title bar when logging out. This protects against a remote
+ # NixOS system clobbering your local terminal's title bar when you SSH
+ # into the remote NixOS system and then log out.
+ #
+ # For more details, see: https://superuser.com/a/339946
+ default = ''
+ printf '\e]0;\a'
+ '';
+ description = ''
+ Shell script code called during login bash shell logout.
+ '';
+ type = lib.types.lines;
+ };
};
};
@@ -197,6 +211,21 @@ in
fi
'';
+ environment.etc.bash_logout.text = ''
+ # /etc/bash_logout: DO NOT EDIT -- this file has been generated automatically.
+
+ # Only execute this file once per shell.
+ if [ -n "$__ETC_BASHLOGOUT_SOURCED" ] || [ -n "$NOSYSBASHLOGOUT" ]; then return; fi
+ __ETC_BASHLOGOUT_SOURCED=1
+
+ ${cfg.logout}
+
+ # Read system-wide modifications.
+ if test -f /etc/bash_logout.local; then
+ . /etc/bash_logout.local
+ fi
+ '';
+
# Configuration for readline in bash. We use "option default"
# priority to allow user override using both .text and .source.
environment.etc.inputrc.source = lib.mkOptionDefault ./inputrc;
diff --git a/nixos/modules/programs/darling.nix b/nixos/modules/programs/darling.nix
deleted file mode 100644
index fc9a56e1165b..000000000000
--- a/nixos/modules/programs/darling.nix
+++ /dev/null
@@ -1,27 +0,0 @@
-{
- config,
- lib,
- pkgs,
- ...
-}:
-
-let
- cfg = config.programs.darling;
-in
-{
- options = {
- programs.darling = {
- enable = lib.mkEnableOption "Darling, a Darwin/macOS compatibility layer for Linux";
- package = lib.mkPackageOption pkgs "darling" { };
- };
- };
-
- config = lib.mkIf cfg.enable {
- security.wrappers.darling = {
- source = lib.getExe cfg.package;
- owner = "root";
- group = "root";
- setuid = true;
- };
- };
-}
diff --git a/nixos/modules/programs/direnv.nix b/nixos/modules/programs/direnv.nix
index 751e279b47bc..2be2059bcca3 100644
--- a/nixos/modules/programs/direnv.nix
+++ b/nixos/modules/programs/direnv.nix
@@ -13,6 +13,7 @@ let
default = true;
example = false;
};
+ format = pkgs.formats.toml { };
in
{
options.programs.direnv = {
@@ -25,6 +26,12 @@ in
package = lib.mkPackageOption pkgs "direnv" { };
+ finalPackage = lib.mkOption {
+ type = lib.types.package;
+ readOnly = true;
+ description = "The wrapped direnv package.";
+ };
+
enableBashIntegration = enabledOption ''
Bash integration
'';
@@ -72,14 +79,47 @@ in
'';
};
};
+
+ settings = lib.mkOption {
+ inherit (format) type;
+ default = { };
+ example = lib.literalExpression ''
+ {
+ global = {
+ log_format = "-";
+ log_filter = "^$";
+ };
+ }
+ '';
+ description = ''
+ Direnv configuration. Refer to {manpage}`direnv.toml(1)`.
+ '';
+ };
};
config = lib.mkIf cfg.enable {
-
programs = {
+ direnv = {
+ finalPackage = pkgs.symlinkJoin {
+ inherit (cfg.package) name;
+ paths = [ cfg.package ];
+ # direnv has a fish library which automatically sources direnv for some reason
+ postBuild = ''
+ rm -rf "$out/share/fish"
+ '';
+ meta.mainProgram = "direnv";
+ };
+ settings = lib.mkIf cfg.silent {
+ global = {
+ log_format = lib.mkDefault "-";
+ log_filter = lib.mkDefault "^$";
+ };
+ };
+ };
+
zsh.interactiveShellInit = lib.mkIf cfg.enableZshIntegration ''
if ${lib.boolToString cfg.loadInNixShell} || printenv PATH | grep -vqc '/nix/store'; then
- eval "$(${lib.getExe cfg.package} hook zsh)"
+ eval "$(${lib.getExe cfg.finalPackage} hook zsh)"
fi
'';
@@ -87,14 +127,13 @@ in
#$IN_NIX_SHELL for "nix-shell"
bash.interactiveShellInit = lib.mkIf cfg.enableBashIntegration ''
if ${lib.boolToString cfg.loadInNixShell} || [ -z "$IN_NIX_SHELL$NIX_GCROOT$(printenv PATH | grep '/nix/store')" ] ; then
- eval "$(${lib.getExe cfg.package} hook bash)"
+ eval "$(${lib.getExe cfg.finalPackage} hook bash)"
fi
'';
fish.interactiveShellInit = lib.mkIf cfg.enableFishIntegration ''
- if ${lib.boolToString cfg.loadInNixShell};
- or printenv PATH | grep -vqc '/nix/store';
- ${lib.getExe cfg.package} hook fish | source
+ if ${lib.boolToString cfg.loadInNixShell}; or printenv PATH | grep -vqc '/nix/store';
+ ${lib.getExe cfg.finalPackage} hook fish | source
end
'';
@@ -114,41 +153,33 @@ in
environment = {
systemPackages = [
- # direnv has a fish library which automatically sources direnv for some reason
- # I don't see any harm in doing this if we're sourcing it with fish.interactiveShellInit
- (pkgs.symlinkJoin {
- inherit (cfg.package) name;
- paths = [ cfg.package ];
- postBuild = ''
- rm -rf $out/share/fish
- '';
- })
+ cfg.finalPackage
];
- variables = {
- DIRENV_CONFIG = "/etc/direnv";
- DIRENV_LOG_FORMAT = lib.mkIf cfg.silent "";
- };
+ variables.DIRENV_CONFIG = "/etc/direnv";
etc = {
+ "direnv/direnv.toml" = lib.mkIf (cfg.settings != { }) {
+ source = format.generate "direnv.toml" cfg.settings;
+ };
"direnv/direnvrc".text = ''
${lib.optionalString cfg.nix-direnv.enable ''
#Load nix-direnv
source ${cfg.nix-direnv.package}/share/nix-direnv/direnvrc
''}
- #Load direnvrcExtra
- ${cfg.direnvrcExtra}
+ #Load direnvrcExtra
+ ${cfg.direnvrcExtra}
- #Load user-configuration if present (~/.direnvrc or ~/.config/direnv/direnvrc)
- direnv_config_dir_home="''${DIRENV_CONFIG_HOME:-''${XDG_CONFIG_HOME:-$HOME/.config}/direnv}"
- if [[ -f $direnv_config_dir_home/direnvrc ]]; then
- source "$direnv_config_dir_home/direnvrc" >&2
- elif [[ -f $HOME/.direnvrc ]]; then
- source "$HOME/.direnvrc" >&2
- fi
+ #Load user-configuration if present (~/.direnvrc or ~/.config/direnv/direnvrc)
+ direnv_config_dir_home="''${DIRENV_CONFIG_HOME:-''${XDG_CONFIG_HOME:-$HOME/.config}/direnv}"
+ if [[ -f $direnv_config_dir_home/direnvrc ]]; then
+ source "$direnv_config_dir_home/direnvrc" >&2
+ elif [[ -f $HOME/.direnvrc ]]; then
+ source "$HOME/.direnvrc" >&2
+ fi
- unset direnv_config_dir_home
+ unset direnv_config_dir_home
'';
"direnv/lib/zz-user.sh".text = ''
diff --git a/nixos/modules/programs/flashprog.nix b/nixos/modules/programs/flashprog.nix
index d7529575a3d3..89671fba08f3 100644
--- a/nixos/modules/programs/flashprog.nix
+++ b/nixos/modules/programs/flashprog.nix
@@ -20,6 +20,8 @@ in
config = lib.mkIf cfg.enable {
services.udev.packages = [ cfg.package ];
environment.systemPackages = [ cfg.package ];
+ hardware.libjaylink.enable = true;
+ hardware.libftdi.enable = true;
};
meta.maintainers = with lib.maintainers; [ felixsinger ];
diff --git a/nixos/modules/programs/iotop.nix b/nixos/modules/programs/iotop.nix
index 39284e960923..1d18edf3d9f8 100644
--- a/nixos/modules/programs/iotop.nix
+++ b/nixos/modules/programs/iotop.nix
@@ -10,14 +10,17 @@ let
in
{
options = {
- programs.iotop.enable = lib.mkEnableOption "iotop + setcap wrapper";
+ programs.iotop = {
+ enable = lib.mkEnableOption "iotop + setcap wrapper";
+ package = lib.mkPackageOption pkgs "iotop" { example = "iotop-c"; };
+ };
};
config = lib.mkIf cfg.enable {
security.wrappers.iotop = {
owner = "root";
group = "root";
capabilities = "cap_net_admin+p";
- source = "${pkgs.iotop}/bin/iotop";
+ source = lib.getExe cfg.package;
};
};
}
diff --git a/nixos/modules/programs/k3b.nix b/nixos/modules/programs/k3b.nix
index 6e39f18b6794..1352c88111ec 100644
--- a/nixos/modules/programs/k3b.nix
+++ b/nixos/modules/programs/k3b.nix
@@ -1,11 +1,53 @@
-{ lib, ... }:
+{
+ config,
+ pkgs,
+ lib,
+ ...
+}:
{
- imports = [
- (lib.mkRemovedOptionModule [
- "programs"
- "k3b"
- "enable"
- ] "Please add kdePackages.k3b to environment.systemPackages instead")
- ];
+ options.programs.k3b = {
+ enable = lib.mkOption {
+ type = lib.types.bool;
+ default = false;
+ description = ''
+ Whether to enable k3b, the KDE disk burning application.
+
+ Additionally to installing `k3b` enabling this will
+ add `setuid` wrappers in `/run/wrappers/bin`
+ for both `cdrdao` and `cdrecord`. On first
+ run you must manually configure the path of `cdrdae` and
+ `cdrecord` to correspond to the appropriate paths under
+ `/run/wrappers/bin` in the "Setup External Programs" menu.
+ '';
+ };
+ };
+
+ config = lib.mkIf config.programs.k3b.enable {
+
+ environment.systemPackages = with pkgs; [
+ kdePackages.k3b
+ dvdplusrwtools
+ cdrdao
+ cdrtools
+ ];
+
+ security.wrappers = {
+ cdrdao = {
+ setuid = true;
+ owner = "root";
+ group = "cdrom";
+ permissions = "u+wrx,g+x";
+ source = "${pkgs.cdrdao}/bin/cdrdao";
+ };
+ cdrecord = {
+ setuid = true;
+ owner = "root";
+ group = "cdrom";
+ permissions = "u+wrx,g+x";
+ source = "${pkgs.cdrtools}/bin/cdrecord";
+ };
+ };
+
+ };
}
diff --git a/nixos/modules/programs/kde-pim.nix b/nixos/modules/programs/kde-pim.nix
index d0acaddd4b87..f00a316f20ff 100644
--- a/nixos/modules/programs/kde-pim.nix
+++ b/nixos/modules/programs/kde-pim.nix
@@ -33,8 +33,14 @@ in
++ lib.optionals cfg.kontact [
kontact
]
- ++ lib.optionals cfg.merkuro [
- merkuro
- ];
+ ++ lib.optionals cfg.merkuro (
+ [
+ merkuro
+ ]
+ # Only needed when using the Merkuro Contacts widget in Plasma.
+ ++ lib.optionals config.services.desktopManager.plasma6.enable [
+ kcontacts
+ ]
+ );
};
}
diff --git a/nixos/modules/programs/nh.nix b/nixos/modules/programs/nh.nix
index 21ffa5cc17d2..321893f4bd23 100644
--- a/nixos/modules/programs/nh.nix
+++ b/nixos/modules/programs/nh.nix
@@ -8,7 +8,10 @@ let
cfg = config.programs.nh;
in
{
- meta.maintainers = [ lib.maintainers.viperML ];
+ meta.maintainers = with lib.maintainers; [
+ NotAShelf
+ viperML
+ ];
options.programs.nh = {
enable = lib.mkEnableOption "nh, yet another Nix CLI helper";
@@ -19,9 +22,18 @@ in
type = lib.types.nullOr lib.types.path;
default = null;
description = ''
- The path that will be used for the `FLAKE` environment variable.
+ The path that will be used for the `NH_FLAKE` environment variable.
- `FLAKE` is used by nh as the default flake for performing actions, like `nh os switch`.
+ `NH_FLAKE` is used by nh as the default flake for performing actions, such as
+ `nh os switch`. This behaviour can be overriden per-command with environment
+ variables that will take priority.
+
+ - `NH_OS_FLAKE`: will take priority for `nh os` commands.
+ - `NH_HOME_FLAKE`: will take priority for `nh home` commands.
+ - `NH_DARWIN_FLAKE`: will take priority for `nh darwin` commands.
+
+ The formerly valid `FLAKE` is now deprecated by nh, and will cause hard errors
+ in future releases if `NH_FLAKE` is not set.
'';
};
@@ -77,7 +89,7 @@ in
environment = lib.mkIf cfg.enable {
systemPackages = [ cfg.package ];
variables = lib.mkIf (cfg.flake != null) {
- FLAKE = cfg.flake;
+ NH_FLAKE = cfg.flake;
};
};
diff --git a/nixos/modules/programs/nix-ld.nix b/nixos/modules/programs/nix-ld.nix
index 84e17c65db82..e41742ac7e0d 100644
--- a/nixos/modules/programs/nix-ld.nix
+++ b/nixos/modules/programs/nix-ld.nix
@@ -22,7 +22,7 @@ in
{
meta.maintainers = [ lib.maintainers.mic92 ];
options.programs.nix-ld = {
- enable = lib.mkEnableOption ''nix-ld, Documentation: '';
+ enable = lib.mkEnableOption ''nix-ld, Documentation: '';
package = lib.mkPackageOption pkgs "nix-ld" { };
libraries = lib.mkOption {
type = lib.types.listOf lib.types.package;
diff --git a/nixos/modules/programs/nm-applet.nix b/nixos/modules/programs/nm-applet.nix
index 27a95dea57e2..285e0a896280 100644
--- a/nixos/modules/programs/nm-applet.nix
+++ b/nixos/modules/programs/nm-applet.nix
@@ -28,6 +28,7 @@
description = "Network manager applet";
wantedBy = [ "graphical-session.target" ];
partOf = [ "graphical-session.target" ];
+ after = [ "graphical-session.target" ];
serviceConfig.ExecStart = "${pkgs.networkmanagerapplet}/bin/nm-applet ${lib.optionalString config.programs.nm-applet.indicator "--indicator"}";
};
diff --git a/nixos/modules/programs/regreet.nix b/nixos/modules/programs/regreet.nix
index d865b1008b45..d3990a9fbf49 100644
--- a/nixos/modules/programs/regreet.nix
+++ b/nixos/modules/programs/regreet.nix
@@ -7,6 +7,7 @@
let
cfg = config.programs.regreet;
settingsFormat = pkgs.formats.toml { };
+ user = config.services.greetd.settings.default_session.user;
in
{
options.programs.regreet = {
@@ -25,7 +26,10 @@ in
'';
};
- package = lib.mkPackageOption pkgs [ "greetd" "regreet" ] { };
+ package = lib.mkPackageOption pkgs [
+ "greetd"
+ "regreet"
+ ] { };
settings = lib.mkOption {
type = settingsFormat.type;
@@ -157,14 +161,19 @@ in
"greetd/regreet.css" =
if lib.isPath cfg.extraCss then { source = cfg.extraCss; } else { text = cfg.extraCss; };
- "greetd/regreet.toml".source = settingsFormat.generate "regreet.toml" cfg.settings;
+ "greetd/regreet.toml".source =
+ if lib.isPath cfg.settings then
+ cfg.settings
+ else
+ settingsFormat.generate "regreet.toml" cfg.settings;
};
systemd.tmpfiles.settings."10-regreet" =
let
defaultConfig = {
- user = "greeter";
- group = config.users.users.${config.services.greetd.settings.default_session.user}.group;
+ inherit user;
+ group =
+ if config.users.users.${user}.group != "" then config.users.users.${user}.group else "greeter";
mode = "0755";
};
dataDir =
@@ -177,5 +186,12 @@ in
"/var/log/regreet".d = defaultConfig;
}
// dataDir;
+
+ assertions = [
+ {
+ assertion = (config.users.users.${user} or { }) != { };
+ message = "regreet: user ${user} does not exist. Please create it before referencing it.";
+ }
+ ];
};
}
diff --git a/nixos/modules/programs/rush.nix b/nixos/modules/programs/rush.nix
new file mode 100644
index 000000000000..782e6da2c2a3
--- /dev/null
+++ b/nixos/modules/programs/rush.nix
@@ -0,0 +1,109 @@
+{
+ config,
+ lib,
+ pkgs,
+ ...
+}:
+let
+ cfg = config.programs.rush;
+
+ indent =
+ lines:
+ lib.pipe lines [
+ (lib.splitString "\n")
+ (builtins.filter (line: line != ""))
+ (map (line: " " + line))
+ (builtins.concatStringsSep "\n")
+ ];
+in
+{
+ meta.maintainers = pkgs.rush.meta.maintainers;
+
+ options.programs.rush = with lib.types; {
+ enable = lib.mkEnableOption "Restricted User Shell.";
+
+ package = lib.mkPackageOption pkgs "rush" { } // {
+ type = shellPackage;
+ };
+
+ global = lib.mkOption {
+ type = lines;
+ description = "The `global` statement defines global settings.";
+ default = "";
+ };
+
+ rules = lib.mkOption {
+ type = attrsOf lines;
+ default = { };
+
+ description = ''
+ The rule statement configures a GNU Rush rule. This is a block statement, which means that all
+ statements located between it and the next rule statement (or end of file, whichever occurs first)
+ modify the definition of that rule.
+ '';
+ };
+
+ shell = lib.mkOption {
+ readOnly = true;
+ type = either shellPackage path;
+
+ description = ''
+ The resolved shell path that users can inherit to set `rush` as their login shell.
+ This is a convenience option for use in user definitions. Example:
+ `users.users.alice = { inherit (config.programs.rush) shell; ... };`
+ '';
+ };
+
+ wrap = lib.mkOption {
+ type = bool;
+ default = config.security.enableWrappers;
+ defaultText = lib.literalExpression "config.security.enableWrappers";
+
+ description = ''
+ Whether to wrap the `rush` binary with a SUID-enabled wrapper.
+ This is required if {option}`security.enableWrappers` is enabled in your configuration.
+ '';
+ };
+ };
+
+ config = lib.mkIf cfg.enable (
+ lib.mkMerge [
+ (lib.mkIf cfg.wrap {
+ security.wrappers.rush = lib.mkDefault {
+ group = "root";
+ owner = "root";
+ permissions = "u+rx,g+x,o+x";
+ setgid = false;
+ setuid = true;
+ source = lib.getExe cfg.package;
+ };
+ })
+
+ {
+ programs.rush.shell = if cfg.wrap then config.security.wrapperDir + "/rush" else cfg.package;
+
+ environment = {
+ shells = [ cfg.shell ];
+ systemPackages = [ cfg.package ];
+
+ etc."rush.rc".text =
+ lib.pipe
+ [
+ "# This file was created by the module `programs.rush`;"
+ "rush 2.0"
+ (lib.optionalString (cfg.global != "") "global\n${indent cfg.global}")
+ (lib.optionals (cfg.rules != { }) (
+ lib.mapAttrsToList (name: content: "rule ${name}\n${indent content}") cfg.rules
+ ))
+ ]
+ [
+ (lib.flatten)
+ (builtins.filter (line: line != ""))
+ (builtins.concatStringsSep "\n\n")
+ (lib.mkDefault)
+ ];
+ };
+ }
+ ]
+ );
+}
diff --git a/nixos/modules/programs/ssh.nix b/nixos/modules/programs/ssh.nix
index fbc59c09a68f..27a56034d86c 100644
--- a/nixos/modules/programs/ssh.nix
+++ b/nixos/modules/programs/ssh.nix
@@ -49,6 +49,15 @@ in
description = "Whether to configure SSH_ASKPASS in the environment.";
};
+ systemd-ssh-proxy.enable = lib.mkOption {
+ type = lib.types.bool;
+ default = true;
+ description = ''
+ Whether to enable systemd's ssh proxy plugin.
+ See {manpage}`systemd-ssh-proxy(1)`.
+ '';
+ };
+
askPassword = lib.mkOption {
type = lib.types.str;
default = "${pkgs.x11_ssh_askpass}/libexec/x11-ssh-askpass";
@@ -334,6 +343,11 @@ in
# Generated options from other settings
Host *
+ ${lib.optionalString cfg.systemd-ssh-proxy.enable ''
+ # See systemd-ssh-proxy(1)
+ Include ${config.systemd.package}/lib/systemd/ssh_config.d/20-systemd-ssh-proxy.conf
+ ''}
+
GlobalKnownHostsFile ${builtins.concatStringsSep " " knownHostsFiles}
${lib.optionalString (!config.networking.enableIPv6) "AddressFamily inet"}
diff --git a/nixos/modules/programs/starship.nix b/nixos/modules/programs/starship.nix
index 17a2cf9b8666..044054c5452f 100644
--- a/nixos/modules/programs/starship.nix
+++ b/nixos/modules/programs/starship.nix
@@ -65,6 +65,47 @@ in
See https://starship.rs/config/#prompt for documentation.
'';
};
+
+ transientPrompt =
+ let
+ mkTransientPromptOption =
+ side:
+ lib.mkOption {
+ type =
+ with lib.types;
+ nullOr (str // { description = "Fish shell code concatenated with \"\\n\""; });
+ description =
+ let
+ function = "`starship_transient_${lib.optionalString (side == "right") "r"}prompt_func` function";
+ in
+ ''
+ Fish code composing the body of the ${function}. The output of
+ this code will become the ${side} side of the transient prompt.
+
+ Not setting this option (or setting it to `null`) will prevent
+ the ${function} from being generated. By default, the ${side}
+ prompt is ${if (side == "right") then "empty" else "a bold-green '❯' character"}.
+ '';
+ example = "starship module ${if (side == "right") then "time" else "character"}";
+ default = null;
+ };
+ in
+ {
+ enable = lib.mkEnableOption ''
+ Starship's [transient prompt](https://starship.rs/advanced-config/#transientprompt-and-transientrightprompt-in-fish)
+ feature in `fish` shells. After a command has been entered, Starship
+ replaces the usual prompt with the terminal output of the commands
+ defined in the `programs.starship.transientPrompt.left`
+ and `programs.starship.transientPrompt.right` options.
+
+ This option only works with `fish`, as `bash` requires a
+ [custom configuration](https://starship.rs/advanced-config/#transientprompt-and-transientrightprompt-in-bash)
+ involving [Ble.sh](https://github.com/akinomyoga/ble.sh), which can be
+ enabled with `programs.bash.blesh.enable`, but not configured using NixOS
+ '';
+ left = mkTransientPromptOption "left";
+ right = mkTransientPromptOption "right";
+ };
};
config = lib.mkIf cfg.enable {
@@ -90,7 +131,18 @@ in
if not test -f "$HOME/.config/starship.toml";
set -x STARSHIP_CONFIG ${settingsFile}
end
+ ${lib.optionalString (!isNull cfg.transientPrompt.left) ''
+ function starship_transient_prompt_func
+ ${cfg.transientPrompt.left}
+ end
+ ''}
+ ${lib.optionalString (!isNull cfg.transientPrompt.right) ''
+ function starship_transient_rprompt_func
+ ${cfg.transientPrompt.right}
+ end
+ ''}
eval (${cfg.package}/bin/starship init fish)
+ ${lib.optionalString cfg.transientPrompt.enable "enable_transience"}
end
'';
diff --git a/nixos/modules/programs/television.nix b/nixos/modules/programs/television.nix
new file mode 100644
index 000000000000..1989df2b925b
--- /dev/null
+++ b/nixos/modules/programs/television.nix
@@ -0,0 +1,42 @@
+{
+ config,
+ lib,
+ pkgs,
+ ...
+}:
+let
+ inherit (lib.options) mkEnableOption mkPackageOption;
+ inherit (lib.modules) mkIf;
+ inherit (lib.meta) getExe;
+
+ cfg = config.programs.television;
+in
+{
+ options.programs.television = {
+ enable = mkEnableOption "Blazingly fast general purpose fuzzy finder TUI";
+ package = mkPackageOption pkgs "television" { };
+
+ enableBashIntegration = mkEnableOption "Bash integration";
+ enableZshIntegration = mkEnableOption "Zsh integration";
+ enableFishIntegration = mkEnableOption "Fish integration";
+ };
+
+ config = mkIf cfg.enable {
+ environment.systemPackages = [ cfg.package ];
+
+ programs = {
+ zsh.interactiveShellInit = mkIf cfg.enableZshIntegration ''
+ eval "$(${getExe cfg.package} init zsh)"
+ '';
+ bash.interactiveShellInit = mkIf cfg.enableBashIntegration ''
+ eval "$(${getExe cfg.package} init bash)"
+ '';
+ fish.interactiveShellInit = mkIf cfg.enableFishIntegration ''
+ ${getExe cfg.package} init fish | source
+ '';
+ };
+
+ };
+
+ meta.maintainers = with lib.maintainers; [ pbek ];
+}
diff --git a/nixos/modules/programs/wayland/gtklock.nix b/nixos/modules/programs/wayland/gtklock.nix
new file mode 100644
index 000000000000..a3cef937ca83
--- /dev/null
+++ b/nixos/modules/programs/wayland/gtklock.nix
@@ -0,0 +1,78 @@
+{
+ config,
+ lib,
+ pkgs,
+ ...
+}:
+let
+ cfg = config.programs.gtklock;
+ configFormat = pkgs.formats.ini {
+ listToValue = builtins.concatStringsSep ";";
+ };
+
+ inherit (lib)
+ types
+ mkOption
+ mkEnableOption
+ mkPackageOption
+ ;
+in
+{
+ options.programs.gtklock = {
+ enable = mkEnableOption "gtklock, a GTK-based lockscreen for Wayland";
+
+ package = mkPackageOption pkgs "gtklock" { };
+
+ config = mkOption {
+ type = configFormat.type;
+ example = lib.literalExpression ''
+ {
+ main = {
+ idle-hide = true;
+ idle-timeout = 10;
+ };
+ }'';
+ description = ''
+ Configuration for gtklock.
+ See [`gtklock(1)`](https://github.com/jovanlanik/gtklock/blob/master/man/gtklock.1.scd) man page for details.
+ '';
+ };
+
+ style = mkOption {
+ type = with types; nullOr str;
+ default = null;
+ description = ''
+ CSS Stylesheet for gtklock.
+ See [gtklock's wiki](https://github.com/jovanlanik/gtklock/wiki#Styling) for details.
+ '';
+ };
+
+ modules = mkOption {
+ type = with types; listOf package;
+ default = [ ];
+ example = lib.literalExpression ''
+ with pkgs; [
+ gtklock-playerctl-module
+ gtklock-powerbar-module
+ gtklock-userinfo-module
+ ]'';
+ description = "gtklock modules to load.";
+ };
+ };
+
+ config = lib.mkIf cfg.enable {
+ programs.gtklock.config.main = {
+ style = lib.mkIf (cfg.style != null) "${pkgs.writeText "style.css" cfg.style}";
+
+ modules = lib.mkIf (cfg.modules != [ ]) (
+ map (pkg: "${pkg}/lib/gtklock/${lib.removePrefix "gtklock-" pkg.pname}.so") cfg.modules
+ );
+ };
+
+ environment.etc."xdg/gtklock/config.ini".source = configFormat.generate "config.ini" cfg.config;
+
+ environment.systemPackages = [ cfg.package ];
+
+ security.pam.services.gtklock = { };
+ };
+}
diff --git a/nixos/modules/programs/wayland/waybar.nix b/nixos/modules/programs/wayland/waybar.nix
index 074537ea9ed3..6e7199dbf425 100644
--- a/nixos/modules/programs/wayland/waybar.nix
+++ b/nixos/modules/programs/wayland/waybar.nix
@@ -16,13 +16,20 @@ in
// lib.mkOption {
apply = pkg: pkg.override { systemdSupport = true; };
};
+ systemd.target = lib.mkOption {
+ type = lib.types.str;
+ description = ''
+ The systemd target that will automatically start the Waybar service.
+ '';
+ default = "graphical-session.target";
+ };
};
config = lib.mkIf cfg.enable {
environment.systemPackages = [ cfg.package ];
systemd = {
packages = [ cfg.package ];
- user.services.waybar.wantedBy = [ "graphical-session.target" ];
+ user.services.waybar.wantedBy = [ cfg.systemd.target ];
};
};
diff --git a/nixos/modules/rename.nix b/nixos/modules/rename.nix
index 49872bc7a96b..94e868dad32c 100644
--- a/nixos/modules/rename.nix
+++ b/nixos/modules/rename.nix
@@ -102,6 +102,10 @@ in
"services"
"chronos"
] "The corresponding package was removed from nixpkgs.")
+ (mkRemovedOptionModule [
+ "services"
+ "clamsmtp"
+ ] "The corresponding package was removed from nixpkgs.")
(mkRemovedOptionModule [ "services" "confluence" ]
"Atlassian software has been removed, as support for the Atlassian Server products ended in February 2024 and there was insufficient interest in maintaining the Atlassian Data Center replacements"
)
@@ -193,6 +197,9 @@ in
(mkRemovedOptionModule [ "services" "pantheon" "files" ] ''
This module was removed, please add pkgs.pantheon.elementary-files to environment.systemPackages directly.
'')
+ (mkRemovedOptionModule [ "services" "polipo" ] ''
+ The polipo project is unmaintained and archived upstream.
+ '')
(mkRemovedOptionModule [ "services" "prey" ] ''
prey-bash-client is deprecated upstream
'')
@@ -299,6 +306,9 @@ in
See https://www.isc.org/blogs/isc-dhcp-eol/ for details.
Please switch to a different implementation like kea or dnsmasq.
'')
+ (mkRemovedOptionModule [ "services" "gsignond" ] ''
+ The corresponding package was unmaintained, abandoned upstream, used outdated library and thus removed from nixpkgs.
+ '')
(mkRemovedOptionModule [ "services" "haka" ] ''
The corresponding package was broken and removed from nixpkgs.
'')
@@ -311,6 +321,14 @@ in
(mkRemovedOptionModule [ "services" "rippleDataApi" ] ''
The corresponding package was broken, abandoned upstream and thus removed from nixpkgs.
'')
+ (mkRemovedOptionModule [ "services" "conduwuit" ] ''
+ The conduwuit project has been discontinued by upstream.
+ See https://github.com/NixOS/nixpkgs/pull/397902 for more information.
+ '')
+ (mkRemovedOptionModule [ "services" "signald" ] ''
+ The signald project is unmaintained and has long been incompatible with the
+ official Signal servers.
+ '')
# Do NOT add any option renames here, see top of the file
];
diff --git a/nixos/modules/security/acme/default.nix b/nixos/modules/security/acme/default.nix
index 286814eaba8e..58ab3623223d 100644
--- a/nixos/modules/security/acme/default.nix
+++ b/nixos/modules/security/acme/default.nix
@@ -236,13 +236,16 @@ let
# Create hashes for cert data directories based on configuration
# Flags are separated to avoid collisions
- hashData = with builtins; ''
- ${lib.concatStringsSep " " data.extraLegoFlags} -
- ${lib.concatStringsSep " " data.extraLegoRunFlags} -
- ${lib.concatStringsSep " " data.extraLegoRenewFlags} -
- ${toString acmeServer} ${toString data.dnsProvider}
- ${toString data.ocspMustStaple} ${data.keyType}
- '';
+ hashData =
+ with builtins;
+ ''
+ ${lib.concatStringsSep " " data.extraLegoFlags} -
+ ${lib.concatStringsSep " " data.extraLegoRunFlags} -
+ ${lib.concatStringsSep " " data.extraLegoRenewFlags} -
+ ${toString acmeServer} ${toString data.dnsProvider}
+ ${toString data.ocspMustStaple} ${data.keyType}
+ ''
+ + (lib.optionalString (data.csr != null) (" - " + data.csr));
certDir = mkHash hashData;
# TODO remove domainHash usage entirely. Waiting on go-acme/lego#1532
domainHash = mkHash "${lib.concatStringsSep " " extraDomains} ${data.domain}";
@@ -286,18 +289,24 @@ let
"--accept-tos" # Checking the option is covered by the assertions
"--path"
"."
- "-d"
- data.domain
"--email"
data.email
- "--key-type"
- data.keyType
]
++ protocolOpts
++ lib.optionals (acmeServer != null) [
"--server"
acmeServer
]
+ ++ lib.optionals (data.csr != null) [
+ "--csr"
+ data.csr
+ ]
+ ++ lib.optionals (data.csr == null) [
+ "--key-type"
+ data.keyType
+ "-d"
+ data.domain
+ ]
++ lib.concatMap (name: [
"-d"
name
@@ -327,6 +336,8 @@ let
webroots = lib.remove null (
lib.unique (builtins.map (certAttrs: certAttrs.webroot) (lib.attrValues config.security.acme.certs))
);
+
+ certificateKey = if data.csrKey != null then "${data.csrKey}" else "certificates/${keyName}.key";
in
{
inherit accountHash cert selfsignedDeps;
@@ -529,7 +540,7 @@ let
# Check if we can renew.
# We can only renew if the list of domains has not changed.
# We also need an account key. Avoids #190493
- if cmp -s domainhash.txt certificates/domainhash.txt && [ -e 'certificates/${keyName}.key' ] && [ -e 'certificates/${keyName}.crt' ] && [ -n "$(find accounts -name '${data.email}.key')" ]; then
+ if cmp -s domainhash.txt certificates/domainhash.txt && [ -e '${certificateKey}' ] && [ -e 'certificates/${keyName}.crt' ] && [ -n "$(find accounts -name '${data.email}.key')" ]; then
# Even if a cert is not expired, it may be revoked by the CA.
# Try to renew, and silently fail if the cert is not expired.
@@ -564,7 +575,7 @@ let
touch out/renewed
echo Installing new certificate
cp -vp 'certificates/${keyName}.crt' out/fullchain.pem
- cp -vp 'certificates/${keyName}.key' out/key.pem
+ cp -vp '${certificateKey}' out/key.pem
cp -vp 'certificates/${keyName}.issuer.crt' out/chain.pem
ln -sf fullchain.pem out/cert.pem
cat out/key.pem out/fullchain.pem > out/full.pem
@@ -845,6 +856,18 @@ let
description = "Domain to fetch certificate for (defaults to the entry name).";
};
+ csr = lib.mkOption {
+ type = lib.types.nullOr lib.types.str;
+ default = null;
+ description = "Path to a certificate signing request to apply when fetching the certificate.";
+ };
+
+ csrKey = lib.mkOption {
+ type = lib.types.nullOr lib.types.str;
+ default = null;
+ description = "Path to the private key to the matching certificate signing request.";
+ };
+
extraDomainNames = lib.mkOption {
type = lib.types.listOf lib.types.str;
default = [ ];
@@ -1113,6 +1136,17 @@ in
used for variables suffixed by "_FILE".
'';
}
+
+ {
+ assertion = lib.all (
+ certOpts:
+ (certOpts.csr == null && certOpts.csrKey == null)
+ || (certOpts.csr != null && certOpts.csrKey != null)
+ ) certs;
+ message = ''
+ When passing a certificate signing request both `security.acme.certs.${cert}.csr` and `security.acme.certs.${cert}.csrKey` need to be set.
+ '';
+ }
]) cfg.certs
));
diff --git a/nixos/modules/security/agnos.nix b/nixos/modules/security/agnos.nix
new file mode 100644
index 000000000000..dbd93afdb263
--- /dev/null
+++ b/nixos/modules/security/agnos.nix
@@ -0,0 +1,314 @@
+{
+ config,
+ lib,
+ pkgs,
+ ...
+}:
+let
+ cfg = config.security.agnos;
+ format = pkgs.formats.toml { };
+ name = "agnos";
+ stateDir = "/var/lib/${name}";
+
+ accountType =
+ let
+ inherit (lib) types mkOption;
+ in
+ types.submodule {
+ freeformType = format.type;
+
+ options = {
+ email = mkOption {
+ type = types.str;
+ description = ''
+ Email associated with this account.
+ '';
+ };
+ private_key_path = mkOption {
+ type = types.str;
+ description = ''
+ Path of the PEM-encoded private key for this account.
+ Currently, only RSA keys are supported.
+
+ If this path does not exist, then the behavior depends on `generateKeys.enable`.
+ When this option is `true`,
+ the key will be automatically generated and saved to this path.
+ When it is `false`, agnos will fail.
+
+ If a relative path is specified,
+ the key will be looked up (or generated and saved to) under `${stateDir}`.
+ '';
+ };
+ certificates = mkOption {
+ type = types.listOf certificateType;
+ description = ''
+ Certificates for agnos to issue or renew.
+ '';
+ };
+ };
+ };
+
+ certificateType =
+ let
+ inherit (lib) types literalExpression mkOption;
+ in
+ types.submodule {
+ freeformType = format.type;
+
+ options = {
+ domains = mkOption {
+ type = types.listOf types.str;
+ description = ''
+ Domains the certificate represents
+ '';
+ example = literalExpression ''["a.example.com", "b.example.com", "*b.example.com"]'';
+ };
+ fullchain_output_file = mkOption {
+ type = types.str;
+ description = ''
+ Output path for the full chain including the acquired certificate.
+ If a relative path is specified, the file will be created in `${stateDir}`.
+ '';
+ };
+ key_output_file = mkOption {
+ type = types.str;
+ description = ''
+ Output path for the certificate private key.
+ If a relative path is specified, the file will be created in `${stateDir}`.
+ '';
+ };
+ };
+ };
+in
+{
+ options.security.agnos =
+ let
+ inherit (lib) types mkEnableOption mkOption;
+ in
+ {
+ enable = mkEnableOption name;
+
+ settings = mkOption {
+ description = "Settings";
+ type = types.submodule {
+ freeformType = format.type;
+
+ options = {
+ dns_listen_addr = mkOption {
+ type = types.str;
+ default = "0.0.0.0:53";
+ description = ''
+ Address for agnos to listen on.
+ Note that this needs to be reachable by the outside world,
+ and 53 is required in most situations
+ since `NS` records do not allow specifying the port.
+ '';
+ };
+
+ accounts = mkOption {
+ type = types.listOf accountType;
+ description = ''
+ A list of ACME accounts.
+ Each account is associated with an email address
+ and can be used to obtain an arbitrary amount of certificate
+ (subject to provider's rate limits,
+ see e.g. [Let's Encrypt Rate Limits](https://letsencrypt.org/docs/rate-limits/)).
+ '';
+ };
+ };
+ };
+ };
+
+ generateKeys = {
+ enable = mkOption {
+ type = types.bool;
+ default = false;
+ description = ''
+ Enable automatic generation of account keys.
+
+ When this is `true`, a key will be generated for each account where
+ the file referred to by the `private_key` path does not exist yet.
+
+ Currently, only RSA keys can be generated.
+ '';
+ };
+
+ keySize = mkOption {
+ type = types.int;
+ default = 4096;
+ description = ''
+ Key size in bits to use when generating new keys.
+ '';
+ };
+ };
+
+ server = mkOption {
+ type = types.nullOr types.str;
+ default = null;
+ description = ''
+ ACME Directory Resource URI. Defaults to Let's Encrypt's production endpoint,
+ `https://acme-v02.api.letsencrypt.org/directory`, if unset.
+ '';
+ };
+
+ serverCa = mkOption {
+ type = types.nullOr types.path;
+ default = null;
+ description = ''
+ The root certificate (in PEM format) of the ACME server's HTTPS interface.
+ '';
+ };
+
+ persistent = mkOption {
+ type = types.bool;
+ default = true;
+ description = ''
+ When `true`, use a persistent systemd timer.
+ '';
+ };
+
+ startAt = mkOption {
+ type = types.either types.str (types.listOf types.str);
+ default = "daily";
+ example = "02:00";
+ description = ''
+ How often or when to run agnos.
+
+ The format is described in
+ {manpage}`systemd.time(7)`.
+ '';
+ };
+
+ temporarilyOpenFirewall = mkOption {
+ type = types.bool;
+ default = false;
+ description = ''
+ When `true`, will open the port specified in `settings.dns_listen_addr`
+ before running the agnos service, and close it when agnos finishes running.
+ '';
+ };
+
+ group = mkOption {
+ type = types.str;
+ default = name;
+ description = ''
+ Group to run Agnos as. The acquired certificates will be owned by this group.
+ '';
+ };
+
+ user = mkOption {
+ type = types.str;
+ default = name;
+ description = ''
+ User to run Agnos as. The acquired certificates will be owned by this user.
+ '';
+ };
+ };
+
+ config =
+ let
+ configFile = format.generate "agnos.toml" cfg.settings;
+ port = lib.toInt (lib.last (builtins.split ":" cfg.settings.dns_listen_addr));
+
+ useNftables = config.networking.nftables.enable;
+
+ # nftables implementation for temporarilyOpenFirewall
+ nftablesSetup = pkgs.writeShellScript "agnos-fw-setup" ''
+ ${lib.getExe pkgs.nftables} add element inet nixos-fw temp-ports "{ tcp . ${toString port} }"
+ ${lib.getExe pkgs.nftables} add element inet nixos-fw temp-ports "{ udp . ${toString port} }"
+ '';
+ nftablesTeardown = pkgs.writeShellScript "agnos-fw-teardown" ''
+ ${lib.getExe pkgs.nftables} delete element inet nixos-fw temp-ports "{ tcp . ${toString port} }"
+ ${lib.getExe pkgs.nftables} delete element inet nixos-fw temp-ports "{ udp . ${toString port} }"
+ '';
+
+ # iptables implementation for temporarilyOpenFirewall
+ helpers = ''
+ function ip46tables() {
+ ${lib.getExe' pkgs.iptables "iptables"} -w "$@"
+ ${lib.getExe' pkgs.iptables "ip6tables"} -w "$@"
+ }
+ '';
+ fwFilter = ''--dport ${toString port} -j ACCEPT -m comment --comment "agnos"'';
+ iptablesSetup = pkgs.writeShellScript "agnos-fw-setup" ''
+ ${helpers}
+ ip46tables -I INPUT 1 -p tcp ${fwFilter}
+ ip46tables -I INPUT 1 -p udp ${fwFilter}
+ '';
+ iptablesTeardown = pkgs.writeShellScript "agnos-fw-setup" ''
+ ${helpers}
+ ip46tables -D INPUT -p tcp ${fwFilter}
+ ip46tables -D INPUT -p udp ${fwFilter}
+ '';
+ in
+ lib.mkIf cfg.enable {
+ assertions = [
+ {
+ assertion = !cfg.temporarilyOpenFirewall || config.networking.firewall.enable;
+ message = "temporarilyOpenFirewall is only useful when firewall is enabled";
+ }
+ ];
+
+ systemd.services.agnos = {
+ serviceConfig = {
+ ExecStartPre =
+ lib.optional cfg.generateKeys.enable ''
+ ${pkgs.agnos}/bin/agnos-generate-accounts-keys \
+ --no-confirm \
+ --key-size ${toString cfg.generateKeys.keySize} \
+ ${configFile}
+ ''
+ ++ lib.optional cfg.temporarilyOpenFirewall (
+ "+" + (if useNftables then nftablesSetup else iptablesSetup)
+ );
+ ExecStopPost = lib.optional cfg.temporarilyOpenFirewall (
+ "+" + (if useNftables then nftablesTeardown else iptablesTeardown)
+ );
+ ExecStart = ''
+ ${pkgs.agnos}/bin/agnos \
+ ${if cfg.server != null then "--acme-url=${cfg.server}" else "--no-staging"} \
+ ${lib.optionalString (cfg.serverCa != null) "--acme-serv-ca=${cfg.serverCa}"} \
+ ${configFile}
+ '';
+ Type = "oneshot";
+ User = cfg.user;
+ Group = cfg.group;
+ StateDirectory = name;
+ StateDirectoryMode = "0750";
+ WorkingDirectory = "${stateDir}";
+
+ # Allow binding privileged ports if necessary
+ CapabilityBoundingSet = lib.mkIf (port < 1024) [ "CAP_NET_BIND_SERVICE" ];
+ AmbientCapabilities = lib.mkIf (port < 1024) [ "CAP_NET_BIND_SERVICE" ];
+ };
+
+ after = [
+ "firewall.target"
+ "network-online.target"
+ "nftables.service"
+ ];
+ wants = [ "network-online.target" ];
+ };
+
+ systemd.timers.agnos = {
+ timerConfig = {
+ OnCalendar = cfg.startAt;
+ Persistent = cfg.persistent;
+ Unit = "agnos.service";
+ };
+ wantedBy = [ "timers.target" ];
+ };
+
+ users.groups = lib.mkIf (cfg.group == name) {
+ ${cfg.group} = { };
+ };
+
+ users.users = lib.mkIf (cfg.user == name) {
+ ${cfg.user} = {
+ isSystemUser = true;
+ description = "Agnos service user";
+ group = cfg.group;
+ };
+ };
+ };
+}
diff --git a/nixos/modules/security/apparmor.nix b/nixos/modules/security/apparmor.nix
index a4c2f9e29fc3..2a20a673d5af 100644
--- a/nixos/modules/security/apparmor.nix
+++ b/nixos/modules/security/apparmor.nix
@@ -172,7 +172,7 @@ in
logfiles = /dev/stdin
parser = ${pkgs.apparmor-parser}/bin/apparmor_parser
- ldd = ${pkgs.glibc.bin}/bin/ldd
+ ldd = ${lib.getExe' pkgs.stdenv.cc.libc "ldd"}
logger = ${pkgs.util-linux}/bin/logger
# customize how file ownership permissions are presented
@@ -200,10 +200,8 @@ in
sed '1,/\[qualifiers\]/d' $footer >> $out
'';
- boot.kernelParams = [
- "apparmor=1"
- "security=apparmor"
- ];
+ boot.kernelParams = [ "apparmor=1" ];
+ security.lsm = [ "apparmor" ];
systemd.services.apparmor = {
after = [
@@ -277,8 +275,5 @@ in
};
};
- meta.maintainers = with lib.maintainers; [
- julm
- grimmauld
- ];
+ meta.maintainers = lib.teams.apparmor.members;
}
diff --git a/nixos/modules/security/default.nix b/nixos/modules/security/default.nix
new file mode 100644
index 000000000000..c8baad1a3dd9
--- /dev/null
+++ b/nixos/modules/security/default.nix
@@ -0,0 +1,28 @@
+{ config, lib, ... }:
+let
+ cfg = config.security;
+in
+{
+ options = {
+ security.lsm = lib.mkOption {
+ type = lib.types.uniq (lib.types.listOf lib.types.str);
+ default = [ ];
+ description = ''
+ A list of the LSMs to initialize in order.
+ '';
+ };
+ };
+
+ config = lib.mkIf (lib.lists.length cfg.lsm > 0) {
+ assertions = [
+ {
+ assertion = builtins.length (lib.filter (lib.hasPrefix "security=") config.boot.kernelParams) == 0;
+ message = "security parameter in boot.kernelParams cannot be used when security.lsm is used";
+ }
+ ];
+
+ boot.kernelParams = [
+ "lsm=${lib.concatStringsSep "," cfg.lsm}"
+ ];
+ };
+}
diff --git a/nixos/modules/security/isolate.nix b/nixos/modules/security/isolate.nix
index c2df90e426a6..9f0060e305ac 100644
--- a/nixos/modules/security/isolate.nix
+++ b/nixos/modules/security/isolate.nix
@@ -140,7 +140,7 @@ in
systemd.slices.isolate = {
description = "Isolate Sandbox Slice";
};
-
- meta.maintainers = with maintainers; [ virchau13 ];
};
+
+ meta.maintainers = with maintainers; [ virchau13 ];
}
diff --git a/nixos/modules/security/pam.nix b/nixos/modules/security/pam.nix
index de74e5cda3c5..ad4e32e82561 100644
--- a/nixos/modules/security/pam.nix
+++ b/nixos/modules/security/pam.nix
@@ -145,6 +145,11 @@ let
description = "Name of the PAM service.";
};
+ enable = lib.mkEnableOption "this PAM service" // {
+ default = true;
+ example = false;
+ };
+
rules = lib.mkOption {
# This option is experimental and subject to breaking changes without notice.
visible = false;
@@ -1566,6 +1571,8 @@ let
Defaults env_keep+=SSH_AUTH_SOCK
'';
+ enabledServices = lib.filterAttrs (name: svc: svc.enable) config.security.pam.services;
+
in
{
@@ -2282,7 +2289,7 @@ in
};
};
- environment.etc = lib.mapAttrs' makePAMService config.security.pam.services;
+ environment.etc = lib.mapAttrs' makePAMService enabledServices;
security.pam.services =
{
@@ -2298,11 +2305,11 @@ in
'';
# Most of these should be moved to specific modules.
- i3lock = { };
- i3lock-color = { };
- vlock = { };
- xlock = { };
- xscreensaver = { };
+ i3lock.enable = lib.mkDefault config.programs.i3lock.enable;
+ i3lock-color.enable = lib.mkDefault config.programs.i3lock.enable;
+ vlock.enable = lib.mkDefault config.console.enable;
+ xlock.enable = lib.mkDefault config.services.xserver.enable;
+ xscreensaver.enable = lib.mkDefault config.services.xscreensaver.enable;
runuser = {
rootOK = true;
@@ -2327,11 +2334,11 @@ in
security.apparmor.includes."abstractions/pam" =
lib.concatMapStrings (name: "r ${config.environment.etc."pam.d/${name}".source},\n") (
- lib.attrNames config.security.pam.services
+ lib.attrNames enabledServices
)
+ (
with lib;
- pipe config.security.pam.services [
+ pipe enabledServices [
lib.attrValues
(catAttrs "rules")
(lib.concatMap lib.attrValues)
diff --git a/nixos/modules/security/pam_mount.nix b/nixos/modules/security/pam_mount.nix
index d47f2ec05521..fbc5fe1ed2b3 100644
--- a/nixos/modules/security/pam_mount.nix
+++ b/nixos/modules/security/pam_mount.nix
@@ -15,7 +15,7 @@ let
${pkgs.lsof}/bin/lsof | ${pkgs.gnugrep}/bin/grep $MNTPT | ${pkgs.gawk}/bin/awk '{print $2}' | ${pkgs.findutils}/bin/xargs ${pkgs.util-linux}/bin/kill -$SIGNAL
'';
- anyPamMount = lib.any (lib.attrByPath [ "pamMount" ] false) (
+ anyPamMount = lib.any (svc: svc.enable && svc.pamMount) (
lib.attrValues config.security.pam.services
);
in
diff --git a/nixos/modules/security/please.nix b/nixos/modules/security/please.nix
index 9c3b6c4bca6a..b67f0945521a 100644
--- a/nixos/modules/security/please.nix
+++ b/nixos/modules/security/please.nix
@@ -121,7 +121,5 @@ in
sshAgentAuth = true;
usshAuth = true;
};
-
- meta.maintainers = with lib.maintainers; [ azahi ];
};
}
diff --git a/nixos/modules/security/sudo-rs.nix b/nixos/modules/security/sudo-rs.nix
index 14c8929f281a..a157bfebfab7 100644
--- a/nixos/modules/security/sudo-rs.nix
+++ b/nixos/modules/security/sudo-rs.nix
@@ -286,7 +286,7 @@ in
in
{
sudo = {
- source = "${cfg.package.out}/bin/sudo";
+ source = "${lib.getExe cfg.package}";
inherit
owner
group
diff --git a/nixos/modules/security/tpm2.nix b/nixos/modules/security/tpm2.nix
index f60adb24cdbe..bb947cb32a56 100644
--- a/nixos/modules/security/tpm2.nix
+++ b/nixos/modules/security/tpm2.nix
@@ -75,8 +75,8 @@ in
package = lib.mkOption {
description = "tpm2-pkcs11 package to use";
type = lib.types.package;
- default = pkgs.tpm2-pkcs11;
- defaultText = lib.literalExpression "pkgs.tpm2-pkcs11";
+ default = if cfg.abrmd.enable then pkgs.tpm2-pkcs11.abrmd else pkgs.tpm2-pkcs11;
+ defaultText = lib.literalExpression "if config.security.tpm2.abrmd.enable then pkgs.tpm2-pkcs11.abrmd else pkgs.tpm2-pkcs11";
};
};
diff --git a/nixos/modules/services/accessibility/orca.nix b/nixos/modules/services/accessibility/orca.nix
index 4487afba103c..e624ffa23a33 100644
--- a/nixos/modules/services/accessibility/orca.nix
+++ b/nixos/modules/services/accessibility/orca.nix
@@ -20,6 +20,7 @@ in
config = mkIf cfg.enable {
environment.systemPackages = [ cfg.package ];
+ systemd.services.display-manager.path = [ cfg.package ];
services.speechd.enable = true;
};
}
diff --git a/nixos/modules/services/accessibility/speechd.nix b/nixos/modules/services/accessibility/speechd.nix
index 165be86346cc..ff98c363fc29 100644
--- a/nixos/modules/services/accessibility/speechd.nix
+++ b/nixos/modules/services/accessibility/speechd.nix
@@ -7,7 +7,6 @@
let
cfg = config.services.speechd;
inherit (lib)
- getExe
mkEnableOption
mkIf
mkPackageOption
@@ -21,12 +20,12 @@ in
package = mkPackageOption pkgs "speechd" { };
};
- # FIXME: speechd 0.12 (or whatever the next version is)
- # will support socket activation, so switch to that once it's out.
config = mkIf cfg.enable {
environment = {
systemPackages = [ cfg.package ];
- sessionVariables.SPEECHD_CMD = getExe cfg.package;
};
+ systemd.packages = [ cfg.package ];
+ # have to set `wantedBy` since `systemd.packages` ignores `[Install]`
+ systemd.user.sockets.speech-dispatcher.wantedBy = [ "sockets.target" ];
};
}
diff --git a/nixos/modules/services/admin/oxidized.nix b/nixos/modules/services/admin/oxidized.nix
index ad151c57666c..44b28991feab 100644
--- a/nixos/modules/services/admin/oxidized.nix
+++ b/nixos/modules/services/admin/oxidized.nix
@@ -11,6 +11,8 @@ in
options.services.oxidized = {
enable = lib.mkEnableOption "the oxidized configuration backup service";
+ package = lib.mkPackageOption pkgs "oxidized" { };
+
user = lib.mkOption {
type = lib.types.str;
default = "oxidized";
@@ -70,7 +72,8 @@ in
};
routerDB = lib.mkOption {
- type = lib.types.path;
+ type = lib.types.nullOr lib.types.path;
+ default = null;
example = lib.literalExpression ''
pkgs.writeText "oxidized-router.db" '''
hostname-sw1:powerconnect:username1:password2
@@ -94,18 +97,57 @@ in
isSystemUser = true;
};
+ systemd.tmpfiles.settings."10-oxidized" =
+ {
+ "${cfg.dataDir}" = {
+ d = {
+ mode = "0750";
+ user = cfg.user;
+ group = cfg.group;
+ };
+ };
+
+ "${cfg.dataDir}/.config" = {
+ d = {
+ mode = "0750";
+ user = cfg.user;
+ group = cfg.group;
+ };
+ };
+
+ "${cfg.dataDir}/.config/oxidized" = {
+ d = {
+ mode = "0750";
+ user = cfg.user;
+ group = cfg.group;
+ };
+ };
+
+ "${cfg.dataDir}/.config/oxidized/config" = {
+ L = {
+ argument = "${cfg.configFile}";
+ user = cfg.user;
+ group = cfg.group;
+ };
+ };
+
+ }
+ // lib.optionalAttrs (cfg.routerDB != null) {
+ "${cfg.dataDir}/.config/oxidized/router.db" = {
+ L = {
+ argument = "${cfg.routerDB}";
+ user = cfg.user;
+ group = cfg.group;
+ };
+ };
+ };
+
systemd.services.oxidized = {
wantedBy = [ "multi-user.target" ];
after = [ "network.target" ];
- preStart = ''
- mkdir -p ${cfg.dataDir}/.config/oxidized
- ln -f -s ${cfg.routerDB} ${cfg.dataDir}/.config/oxidized/router.db
- ln -f -s ${cfg.configFile} ${cfg.dataDir}/.config/oxidized/config
- '';
-
serviceConfig = {
- ExecStart = "${pkgs.oxidized}/bin/oxidized";
+ ExecStart = lib.getExe cfg.package;
User = cfg.user;
Group = cfg.group;
UMask = "0077";
diff --git a/nixos/modules/services/audio/lavalink.nix b/nixos/modules/services/audio/lavalink.nix
new file mode 100644
index 000000000000..8cb37017bf96
--- /dev/null
+++ b/nixos/modules/services/audio/lavalink.nix
@@ -0,0 +1,335 @@
+{
+ config,
+ pkgs,
+ lib,
+ ...
+}:
+
+let
+ inherit (lib)
+ mkOption
+ mkEnableOption
+ mkIf
+ types
+ ;
+
+ cfg = config.services.lavalink;
+
+ format = pkgs.formats.yaml { };
+in
+
+{
+ options.services.lavalink = {
+ enable = mkEnableOption "Lavalink";
+
+ package = lib.mkPackageOption pkgs "lavalink" { };
+
+ password = mkOption {
+ type = types.nullOr types.str;
+ default = null;
+ example = "s3cRe!p4SsW0rD";
+ description = ''
+ The password for Lavalink's authentication in plain text.
+ '';
+ };
+
+ port = mkOption {
+ type = types.port;
+ default = 2333;
+ example = 4567;
+ description = ''
+ The port that Lavalink will use.
+ '';
+ };
+
+ address = mkOption {
+ type = types.str;
+ default = "0.0.0.0";
+ example = "127.0.0.1";
+ description = ''
+ The network address to bind to.
+ '';
+ };
+
+ openFirewall = mkOption {
+ type = types.bool;
+ default = false;
+ example = true;
+ description = ''
+ Whether to expose the port to the network.
+ '';
+ };
+
+ user = mkOption {
+ type = types.str;
+ default = "lavalink";
+ example = "root";
+ description = ''
+ The user of the service.
+ '';
+ };
+
+ group = mkOption {
+ type = types.str;
+ default = "lavalink";
+ example = "medias";
+ description = ''
+ The group of the service.
+ '';
+ };
+
+ home = mkOption {
+ type = types.str;
+ default = "/var/lib/lavalink";
+ example = "/home/lavalink";
+ description = ''
+ The home directory for lavalink.
+ '';
+ };
+
+ enableHttp2 = mkEnableOption "HTTP/2 support";
+
+ jvmArgs = mkOption {
+ type = types.str;
+ default = "-Xmx4G";
+ example = "-Djava.io.tmpdir=/var/lib/lavalink/tmp -Xmx6G";
+ description = ''
+ Set custom JVM arguments.
+ '';
+ };
+
+ environmentFile = mkOption {
+ type = types.nullOr types.str;
+ default = null;
+ example = "/run/secrets/lavalink/passwordEnvFile";
+ description = ''
+ Add custom environment variables from a file.
+ See for the full documentation.
+ '';
+ };
+
+ plugins = mkOption {
+ type = types.listOf (
+ types.submodule {
+ options = {
+ dependency = mkOption {
+ type = types.str;
+ example = "dev.lavalink.youtube:youtube-plugin:1.8.0";
+ description = ''
+ The coordinates of the plugin.
+ '';
+ };
+
+ repository = mkOption {
+ type = types.str;
+ example = "https://maven.example.com/releases";
+ default = "https://maven.lavalink.dev/releases";
+ description = ''
+ The plugin repository. Defaults to the lavalink releases repository.
+
+ To use the snapshots repository, use instead
+ '';
+ };
+
+ hash = mkOption {
+ type = types.str;
+ example = lib.fakeHash;
+ description = ''
+ The hash of the plugin.
+ '';
+ };
+
+ configName = mkOption {
+ type = types.nullOr types.str;
+ example = "youtube";
+ default = null;
+ description = ''
+ The name of the plugin to use as the key for the plugin configuration.
+ '';
+ };
+
+ extraConfig = mkOption {
+ type = types.submodule { freeformType = format.type; };
+ default = { };
+ description = ''
+ The configuration for the plugin.
+
+ The {option}`services.lavalink.plugins.*.configName` option must be set.
+ '';
+ };
+ };
+ }
+ );
+ default = [ ];
+
+ example = lib.literalExpression ''
+ [
+ {
+ dependency = "dev.lavalink.youtube:youtube-plugin:1.8.0";
+ repository = "https://maven.lavalink.dev/snapshots";
+ hash = lib.fakeHash;
+ configName = "youtube";
+ extraConfig = {
+ enabled = true;
+ allowSearch = true;
+ allowDirectVideoIds = true;
+ allowDirectPlaylistIds = true;
+ };
+ }
+ ]
+ '';
+
+ description = ''
+ A list of plugins for lavalink.
+ '';
+ };
+
+ extraConfig = mkOption {
+ type = types.submodule { freeformType = format.type; };
+
+ description = ''
+ Configuration to write to {file}`application.yml`.
+ See for the full documentation.
+
+ Individual configuration parameters can be overwritten using environment variables.
+ See for more information.
+ '';
+
+ default = { };
+
+ example = lib.literalExpression ''
+ {
+ lavalink.server = {
+ sources.twitch = true;
+
+ filters.volume = true;
+ };
+
+ logging.file.path = "./logs/";
+ }
+ '';
+ };
+ };
+
+ config =
+ let
+ pluginSymlinks = lib.concatStringsSep "\n" (
+ map (
+ pluginCfg:
+ let
+ pluginParts = lib.match ''^(.*?:(.*?):)([0-9]+\.[0-9]+\.[0-9]+)$'' pluginCfg.dependency;
+
+ pluginWebPath = lib.replaceStrings [ "." ":" ] [ "/" "/" ] (lib.elemAt pluginParts 0);
+
+ pluginFileName = lib.elemAt pluginParts 1;
+ pluginVersion = lib.elemAt pluginParts 2;
+
+ pluginFile = "${pluginFileName}-${pluginVersion}.jar";
+ pluginUrl = "${pluginCfg.repository}/${pluginWebPath}${pluginVersion}/${pluginFile}";
+
+ plugin = pkgs.fetchurl {
+ url = pluginUrl;
+ inherit (pluginCfg) hash;
+ };
+ in
+ "ln -sf ${plugin} ${cfg.home}/plugins/${pluginFile}"
+ ) cfg.plugins
+ );
+
+ pluginExtraConfigs = builtins.listToAttrs (
+ builtins.map (
+ pluginConfig: lib.attrsets.nameValuePair pluginConfig.configName pluginConfig.extraConfig
+ ) (lib.lists.filter (pluginCfg: pluginCfg.configName != null) cfg.plugins)
+ );
+
+ config = lib.attrsets.recursiveUpdate cfg.extraConfig {
+ server = {
+ inherit (cfg) port address;
+ http2.enabled = cfg.enableHttp2;
+ };
+
+ plugins = pluginExtraConfigs;
+ lavalink.plugins = (
+ builtins.map (
+ pluginConfig:
+ builtins.removeAttrs pluginConfig [
+ "name"
+ "extraConfig"
+ "hash"
+ ]
+ ) cfg.plugins
+ );
+ };
+
+ configWithPassword = lib.attrsets.recursiveUpdate config (
+ lib.attrsets.optionalAttrs (cfg.password != null) { lavalink.server.password = cfg.password; }
+ );
+
+ configFile = format.generate "application.yml" configWithPassword;
+ in
+ mkIf cfg.enable {
+ assertions = [
+ {
+ assertion =
+ !(lib.lists.any (
+ pluginCfg: pluginCfg.extraConfig != { } && pluginCfg.configName == null
+ ) cfg.plugins);
+ message = "Plugins with extra configuration need to have the `configName` attribute defined";
+ }
+ ];
+
+ networking.firewall.allowedTCPPorts = mkIf cfg.openFirewall [ cfg.port ];
+
+ users.groups = mkIf (cfg.group == "lavalink") { lavalink = { }; };
+ users.users = mkIf (cfg.user == "lavalink") {
+ lavalink = {
+ inherit (cfg) home;
+ group = "lavalink";
+ description = "The user for the Lavalink server";
+ isSystemUser = true;
+ };
+ };
+
+ systemd.tmpfiles.settings."10-lavalink" =
+ let
+ dirConfig = {
+ inherit (cfg) user group;
+ mode = "0700";
+ };
+ in
+ {
+ "${cfg.home}/plugins".d = mkIf (cfg.plugins != [ ]) dirConfig;
+ ${cfg.home}.d = dirConfig;
+ };
+
+ systemd.services.lavalink = {
+ description = "Lavalink Service";
+
+ wantedBy = [ "multi-user.target" ];
+ after = [
+ "syslog.target"
+ "network.target"
+ ];
+
+ script = ''
+ ${pluginSymlinks}
+
+ ln -sf ${configFile} ${cfg.home}/application.yml
+ export _JAVA_OPTIONS="${cfg.jvmArgs}"
+
+ ${lib.getExe cfg.package}
+ '';
+
+ serviceConfig = {
+ User = cfg.user;
+ Group = cfg.group;
+
+ Type = "simple";
+ Restart = "on-failure";
+
+ EnvironmentFile = cfg.environmentFile;
+ WorkingDirectory = cfg.home;
+ };
+ };
+ };
+}
diff --git a/nixos/modules/services/audio/music-assistant.nix b/nixos/modules/services/audio/music-assistant.nix
index aaabe5c3ac4c..d48bb7f99f84 100644
--- a/nixos/modules/services/audio/music-assistant.nix
+++ b/nixos/modules/services/audio/music-assistant.nix
@@ -69,6 +69,9 @@ in
description = "Music Assistant";
documentation = [ "https://music-assistant.io" ];
+ after = [ "network-online.target" ];
+ wants = [ "network-online.target" ];
+
wantedBy = [ "multi-user.target" ];
environment = {
diff --git a/nixos/modules/services/audio/navidrome.nix b/nixos/modules/services/audio/navidrome.nix
index c3540196c7a3..ab6fc7b6bce4 100644
--- a/nixos/modules/services/audio/navidrome.nix
+++ b/nixos/modules/services/audio/navidrome.nix
@@ -102,6 +102,11 @@ in
mode = "700";
inherit (cfg) user group;
};
+ "${cfg.settings.MusicFolder or (WorkingDirectory + "/music")}"."d" = {
+ mode = ":700";
+ user = ":${cfg.user}";
+ group = ":${cfg.group}";
+ };
};
services.navidrome = {
description = "Navidrome Media Server";
diff --git a/nixos/modules/services/audio/spotifyd.nix b/nixos/modules/services/audio/spotifyd.nix
index 1f6ab0b5f75b..2264d7c39c6b 100644
--- a/nixos/modules/services/audio/spotifyd.nix
+++ b/nixos/modules/services/audio/spotifyd.nix
@@ -28,7 +28,7 @@ in
type = lib.types.lines;
description = ''
(Deprecated) Configuration for Spotifyd. For syntax and directives, see
- .
+ .
'';
};
@@ -40,7 +40,7 @@ in
};
description = ''
Configuration for Spotifyd. For syntax and directives, see
- .
+ .
'';
};
};
diff --git a/nixos/modules/services/backup/borgbackup.md b/nixos/modules/services/backup/borgbackup.md
index 8ae869308bb8..23f0e5c934ed 100644
--- a/nixos/modules/services/backup/borgbackup.md
+++ b/nixos/modules/services/backup/borgbackup.md
@@ -105,7 +105,7 @@ The following few commands (run as root) let you test your backup.
> systemctl restart borgbackup-job-backupToLocalServer
> sleep 10
> systemctl restart borgbackup-job-backupToLocalServer
-> export BORG_PASSPHRASE=topSecrect
+> export BORG_PASSPHRASE=topSecret
> borg list --rsh='ssh -i /run/keys/id_ed25519_my_borg_repo' borg@nixos:.
nixos-backupToLocalServer-2020-03-30T21:46:17 Mon, 2020-03-30 21:46:19 [84feb97710954931ca384182f5f3cb90665f35cef214760abd7350fb064786ac]
nixos-backupToLocalServer-2020-03-30T21:46:30 Mon, 2020-03-30 21:46:32 [e77321694ecd160ca2228611747c6ad1be177d6e0d894538898de7a2621b6e68]
diff --git a/nixos/modules/services/backup/pgbackrest.nix b/nixos/modules/services/backup/pgbackrest.nix
new file mode 100644
index 000000000000..1e1377818097
--- /dev/null
+++ b/nixos/modules/services/backup/pgbackrest.nix
@@ -0,0 +1,426 @@
+{
+ config,
+ lib,
+ pkgs,
+ ...
+}:
+
+let
+ cfg = config.services.pgbackrest;
+
+ settingsFormat = pkgs.formats.ini {
+ listsAsDuplicateKeys = true;
+ };
+
+ # pgBackRest "options"
+ settingsType =
+ with lib.types;
+ attrsOf (oneOf [
+ bool
+ ints.unsigned
+ str
+ (attrsOf str)
+ (listOf str)
+ ]);
+
+ # Applied to both repoNNN-* and pgNNN-* options in global and stanza sections.
+ flattenWithIndex =
+ attrs: prefix:
+ lib.concatMapAttrs (
+ name:
+ let
+ index = lib.lists.findFirstIndex (n: n == name) null (lib.attrNames attrs);
+ index1 = index + 1;
+ in
+ lib.mapAttrs' (option: lib.nameValuePair "${prefix}${toString index1}-${option}")
+ ) attrs;
+
+ # Remove nulls, turn attrsets into lists and bools into y/n
+ normalize =
+ x:
+ lib.pipe x [
+ (lib.filterAttrs (_: v: v != null))
+ (lib.mapAttrs (_: v: if lib.isAttrs v then lib.mapAttrsToList (n': v': "${n'}=${v'}") v else v))
+ (lib.mapAttrs (
+ _: v:
+ if v == true then
+ "y"
+ else if v == false then
+ "n"
+ else
+ v
+ ))
+ ];
+
+ fullConfig =
+ {
+ global = normalize (cfg.settings // flattenWithIndex cfg.repos "repo");
+ }
+ // lib.mapAttrs (
+ _: cfg': normalize (cfg'.settings // flattenWithIndex cfg'.instances "pg")
+ ) cfg.stanzas;
+
+ namedJobs = lib.listToAttrs (
+ lib.flatten (
+ lib.mapAttrsToList (
+ stanza:
+ { jobs, ... }:
+ lib.mapAttrsToList (
+ job: attrs: lib.nameValuePair "pgbackrest-${stanza}-${job}" (attrs // { inherit stanza job; })
+ ) jobs
+ ) cfg.stanzas
+ )
+ );
+
+ disabledOption = lib.mkOption {
+ default = null;
+ readOnly = true;
+ internal = true;
+ };
+
+ secretPathOption =
+ with lib.types;
+ lib.mkOption {
+ type = nullOr (pathWith {
+ inStore = false;
+ absolute = true;
+ });
+ default = null;
+ internal = true;
+ };
+in
+
+{
+ meta = {
+ maintainers = with lib.maintainers; [ wolfgangwalther ];
+ };
+
+ # TODO: Add enableServer option and corresponding pgBackRest TLS server service.
+ # TODO: Allow command-specific options
+ # TODO: Write wrapper around pgbackrest to turn --repo= into --repo=
+ # The following two are dependent on improvements upstream:
+ # https://github.com/pgbackrest/pgbackrest/issues/2621
+ # TODO: Add support for more repository types
+ # TODO: Support passing encryption key safely
+ options.services.pgbackrest = {
+ enable = lib.mkEnableOption "pgBackRest";
+
+ repos = lib.mkOption {
+ type =
+ with lib.types;
+ attrsOf (
+ submodule (
+ { config, name, ... }:
+ let
+ setHostForType =
+ type:
+ if name == "localhost" then
+ null
+ # "posix" is the default repo type, which uses the -host option.
+ # Other types use prefixed options, for example -sftp-host.
+ else if config.type or "posix" != type then
+ null
+ else
+ name;
+ in
+ {
+ freeformType = settingsType;
+
+ options.host = lib.mkOption {
+ type = nullOr str;
+ default = setHostForType "posix";
+ defaultText = lib.literalExpression "name";
+ description = "Repository host when operating remotely";
+ };
+
+ options.sftp-host = lib.mkOption {
+ type = nullOr str;
+ default = setHostForType "sftp";
+ defaultText = lib.literalExpression "name";
+ description = "SFTP repository host";
+ };
+
+ options.sftp-private-key-file = lib.mkOption {
+ type = nullOr (pathWith {
+ inStore = false;
+ absolute = true;
+ });
+ default = null;
+ description = ''
+ SFTP private key file.
+
+ The file must be accessible by both the pgbackrest and the postgres users.
+ '';
+ };
+
+ # The following options should not be used; they would store secrets in the store.
+ options.azure-key = disabledOption;
+ options.cipher-pass = disabledOption;
+ options.s3-key = disabledOption;
+ options.s3-key-secret = disabledOption;
+ options.s3-kms-key-id = disabledOption; # unsure whether that's a secret or not
+ options.s3-sse-customer-key = disabledOption; # unsure whether that's a secret or not
+ options.s3-token = disabledOption;
+ options.sftp-private-key-passphrase = disabledOption;
+
+ # The following options are not fully supported / tested, yet, but point to files with secrets.
+ # Users can already set those options, but we'll force non-store paths.
+ options.gcs-key = secretPathOption;
+ options.host-cert-file = secretPathOption;
+ options.host-key-file = secretPathOption;
+ }
+ )
+ );
+ default = { };
+ description = ''
+ An attribute set of repositories as described in:
+
+
+ Each repository defaults to set `repo-host` to the attribute's name.
+ The special value "localhost" will unset `repo-host`.
+
+ ::: {.note}
+ The prefix `repoNNN-` is added automatically.
+ Example: Use `path` instead of `repo1-path`.
+ :::
+ '';
+ example = lib.literalExpression ''
+ {
+ localhost.path = "/var/lib/backup";
+ "backup.example.com".host-type = "tls";
+ }
+ '';
+ };
+
+ stanzas = lib.mkOption {
+ type =
+ with lib.types;
+ attrsOf (submodule {
+ options = {
+ jobs = lib.mkOption {
+ type = lib.types.attrsOf (
+ lib.types.submodule {
+ options.schedule = lib.mkOption {
+ type = lib.types.str;
+ description = ''
+ When or how often the backup should run.
+ Must be in the format described in {manpage}`systemd.time(7)`.
+ '';
+ };
+
+ options.type = lib.mkOption {
+ type = lib.types.str;
+ description = ''
+ Backup type as described in:
+
+ '';
+ };
+ }
+ );
+ default = { };
+ description = ''
+ Backups jobs to schedule for this stanza as described in:
+
+ '';
+ example = lib.literalExpression ''
+ {
+ weekly = { schedule = "Sun, 6:30"; type = "full"; };
+ daily = { schedule = "Mon..Sat, 6:30"; type = "diff"; };
+ }
+ '';
+ };
+
+ instances = lib.mkOption {
+ type =
+ with lib.types;
+ attrsOf (
+ submodule (
+ { name, ... }:
+ {
+ freeformType = settingsType;
+ options.host = lib.mkOption {
+ type = nullOr str;
+ default = if name == "localhost" then null else name;
+ defaultText = lib.literalExpression ''if name == "localhost" then null else name'';
+ description = "PostgreSQL host for operating remotely.";
+ };
+
+ # The following options are not fully supported / tested, yet, but point to files with secrets.
+ # Users can already set those options, but we'll force non-store paths.
+ options.host-cert-file = secretPathOption;
+ options.host-key-file = secretPathOption;
+ }
+ )
+ );
+ default = { };
+ description = ''
+ An attribute set of database instances as described in:
+
+
+ Each instance defaults to set `pg-host` to the attribute's name.
+ The special value "localhost" will unset `pg-host`.
+
+ ::: {.note}
+ The prefix `pgNNN-` is added automatically.
+ Example: Use `user` instead of `pg1-user`.
+ :::
+ '';
+ example = lib.literalExpression ''
+ {
+ localhost.database = "app";
+ "postgres.example.com".port = "5433";
+ }
+ '';
+ };
+
+ settings = lib.mkOption {
+ type = lib.types.submodule {
+ freeformType = settingsType;
+
+ # The following options are not fully supported / tested, yet, but point to files with secrets.
+ # Users can already set those options, but we'll force non-store paths.
+ options.tls-server-cert-file = secretPathOption;
+ options.tls-server-key-file = secretPathOption;
+ };
+ default = { };
+ description = ''
+ An attribute set of options as described in:
+
+
+ All options can be used.
+ Repository options should be set via [`repos`](#opt-services.pgbackrest.repos) instead.
+ Stanza options should be set via [`instances`](#opt-services.pgbackrest.stanzas._name_.instances) instead.
+ '';
+ example = lib.literalExpression ''
+ {
+ process-max = 2;
+ }
+ '';
+ };
+ };
+ });
+ default = { };
+ description = ''
+ An attribute set of stanzas as described in:
+
+ '';
+ };
+
+ settings = lib.mkOption {
+ type = lib.types.submodule {
+ freeformType = settingsType;
+
+ # The following options are not fully supported / tested, yet, but point to files with secrets.
+ # Users can already set those options, but we'll force non-store paths.
+ options.tls-server-cert-file = secretPathOption;
+ options.tls-server-key-file = secretPathOption;
+ };
+ default = { };
+ description = ''
+ An attribute set of options as described in:
+
+
+ All globally available options, i.e. all except stanza options, can be used.
+ Repository options should be set via [`repos`](#opt-services.pgbackrest.repos) instead.
+ '';
+ example = lib.literalExpression ''
+ {
+ process-max = 2;
+ }
+ '';
+ };
+ };
+
+ config = lib.mkIf cfg.enable (
+ lib.mkMerge [
+ {
+ services.pgbackrest.settings = {
+ log-level-console = lib.mkDefault "info";
+ log-level-file = lib.mkDefault "off";
+ cmd-ssh = lib.getExe pkgs.openssh;
+ };
+
+ environment.systemPackages = [ pkgs.pgbackrest ];
+ environment.etc."pgbackrest/pgbackrest.conf".source =
+ settingsFormat.generate "pgbackrest.conf" fullConfig;
+
+ users.users.pgbackrest = {
+ name = "pgbackrest";
+ group = "pgbackrest";
+ description = "pgBackRest service user";
+ isSystemUser = true;
+ useDefaultShell = true;
+ createHome = true;
+ home = cfg.repos.localhost.path or "/var/lib/pgbackrest";
+ };
+ users.groups.pgbackrest = { };
+
+ systemd.services = lib.mapAttrs (
+ _:
+ {
+ stanza,
+ job,
+ type,
+ ...
+ }:
+ {
+ description = "pgBackRest job ${job} for stanza ${stanza}";
+
+ serviceConfig = {
+ User = "pgbackrest";
+ Group = "pgbackrest";
+ Type = "oneshot";
+ # stanza-create is idempotent, so safe to always run
+ ExecStartPre = "${lib.getExe pkgs.pgbackrest} --stanza='${stanza}' stanza-create";
+ ExecStart = "${lib.getExe pkgs.pgbackrest} --stanza='${stanza}' backup --type='${type}'";
+ };
+ }
+ ) namedJobs;
+
+ systemd.timers = lib.mapAttrs (
+ name:
+ {
+ stanza,
+ job,
+ schedule,
+ ...
+ }:
+ {
+ description = "pgBackRest job ${job} for stanza ${stanza}";
+ wantedBy = [ "timers.target" ];
+ after = [ "network-online.target" ];
+ wants = [ "network-online.target" ];
+ timerConfig = {
+ OnCalendar = schedule;
+ Persistent = true;
+ Unit = "${name}.service";
+ };
+ }
+ ) namedJobs;
+ }
+
+ # The default stanza is set up for the local postgresql instance.
+ # It does not backup automatically, the systemd timer still needs to be set.
+ (lib.mkIf config.services.postgresql.enable {
+ services.pgbackrest.stanzas.default = {
+ settings.cmd = lib.getExe pkgs.pgbackrest;
+ instances.localhost = {
+ path = config.services.postgresql.dataDir;
+ user = "postgres";
+ };
+ };
+ services.postgresql.identMap = ''
+ postgres pgbackrest postgres
+ '';
+ services.postgresql.initdbArgs = [ "--allow-group-access" ];
+ users.users.pgbackrest.extraGroups = [ "postgres" ];
+
+ services.postgresql.settings = {
+ archive_command = ''${lib.getExe pkgs.pgbackrest} --stanza=default archive-push "%p"'';
+ archive_mode = lib.mkDefault "on";
+ };
+ users.groups.pgbackrest.members = [ "postgres" ];
+ })
+ ]
+ );
+}
diff --git a/nixos/modules/services/backup/restic-rest-server.nix b/nixos/modules/services/backup/restic-rest-server.nix
index 5850b17e9cb7..e8d34e879dc5 100644
--- a/nixos/modules/services/backup/restic-rest-server.nix
+++ b/nixos/modules/services/backup/restic-rest-server.nix
@@ -36,6 +36,12 @@ in
'';
};
+ htpasswd-file = lib.mkOption {
+ default = null;
+ type = lib.types.nullOr lib.types.path;
+ description = "The path to the servers .htpasswd file. Defaults to `\${dataDir}/.htpasswd`.";
+ };
+
privateRepos = lib.mkOption {
default = false;
type = lib.types.bool;
@@ -84,6 +90,7 @@ in
ExecStart = ''
${cfg.package}/bin/rest-server \
--path ${cfg.dataDir} \
+ ${lib.optionalString (cfg.htpasswd-file != null) "--htpasswd-file ${cfg.htpasswd-file}"} \
${lib.optionalString cfg.appendOnly "--append-only"} \
${lib.optionalString cfg.privateRepos "--private-repos"} \
${lib.optionalString cfg.prometheus "--prometheus"} \
@@ -112,6 +119,7 @@ in
ProtectControlGroups = true;
PrivateDevices = true;
ReadWritePaths = [ cfg.dataDir ];
+ ReadOnlyPaths = lib.optional (cfg.htpasswd-file != null) cfg.htpasswd-file;
RemoveIPC = true;
RestrictAddressFamilies = "none";
RestrictNamespaces = true;
diff --git a/nixos/modules/services/backup/restic.nix b/nixos/modules/services/backup/restic.nix
index ce8bcc3994b8..69df4f60d8de 100644
--- a/nixos/modules/services/backup/restic.nix
+++ b/nixos/modules/services/backup/restic.nix
@@ -465,8 +465,8 @@ in
# set same environment variables as the systemd service
${lib.pipe config.systemd.services."restic-backups-${name}".environment [
(lib.filterAttrs (n: v: v != null && n != "PATH"))
- (lib.mapAttrsToList (n: v: "${n}=${v}"))
- (lib.concatStringsSep "\n")
+ (lib.mapAttrs (_: v: "${v}"))
+ (lib.toShellVars)
]}
PATH=${config.systemd.services."restic-backups-${name}".environment.PATH}:$PATH
diff --git a/nixos/modules/services/backup/syncoid.nix b/nixos/modules/services/backup/syncoid.nix
index 0d379f6c915d..8b4c59155f4d 100644
--- a/nixos/modules/services/backup/syncoid.nix
+++ b/nixos/modules/services/backup/syncoid.nix
@@ -106,14 +106,17 @@ in
package = lib.mkPackageOption pkgs "sanoid" { };
interval = lib.mkOption {
- type = lib.types.str;
+ type = with lib.types; either str (listOf str);
default = "hourly";
example = "*-*-* *:15:00";
description = ''
Run syncoid at this interval. The default is to run hourly.
- The format is described in
- {manpage}`systemd.time(7)`.
+ Must be in the format described in {manpage}`systemd.time(7)`. This is
+ equivalent to adding a corresponding timer unit with
+ {option}`OnCalendar` set to the value given here.
+
+ Set to an empty list to avoid starting syncoid automatically.
'';
};
diff --git a/nixos/modules/services/cluster/k3s/default.nix b/nixos/modules/services/cluster/k3s/default.nix
index 4911d7871412..22e3534ea591 100644
--- a/nixos/modules/services/cluster/k3s/default.nix
+++ b/nixos/modules/services/cluster/k3s/default.nix
@@ -46,8 +46,12 @@ let
)
);
- # Replace characters that are problematic in file names
+ # Replace prefixes and characters that are problematic in file names
cleanHelmChartName =
+ name:
+ let
+ woPrefix = lib.removePrefix "https://" (lib.removePrefix "oci://" name);
+ in
lib.replaceStrings
[
"/"
@@ -56,7 +60,8 @@ let
[
"-"
"-"
- ];
+ ]
+ woPrefix;
# Fetch a Helm chart from a public registry. This only supports a basic Helm pull.
fetchHelm =
@@ -66,7 +71,12 @@ let
version,
hash ? lib.fakeHash,
}:
- pkgs.runCommand (cleanHelmChartName "${lib.removePrefix "https://" repo}-${name}-${version}.tgz")
+ let
+ isOci = lib.hasPrefix "oci://" repo;
+ pullCmd = if isOci then repo else "--repo ${repo} ${name}";
+ name' = if isOci then "${repo}-${version}" else "${repo}-${name}-${version}";
+ in
+ pkgs.runCommand (cleanHelmChartName "${name'}.tgz")
{
inherit (lib.fetchers.normalizeHash { } { inherit hash; }) outputHash outputHashAlgo;
impureEnvVars = lib.fetchers.proxyImpureEnvVars;
@@ -76,9 +86,7 @@ let
];
}
''
- export HOME="$PWD"
- helm repo add repository ${repo}
- helm pull repository/${name} --version ${version}
+ helm pull ${pullCmd} --version ${version}
mv ./*.tgz $out
'';
@@ -190,6 +198,7 @@ let
hash = lib.mkOption {
type = lib.types.str;
example = "sha256-ej+vpPNdiOoXsaj1jyRpWLisJgWo8EqX+Z5VbpSjsPA=";
+ default = "";
description = ''
The hash of the packaged Helm chart. Only has an effect if `package` is not set.
The Helm chart is fetched during build time and placed as a `.tgz` archive on the
@@ -723,7 +732,11 @@ in
};
};
};
-
+ nginx = {
+ repo = "oci://registry-1.docker.io/bitnamicharts/nginx";
+ version = "20.0.0";
+ hash = "sha256-sy+tzB+i9jIl/tqOMzzuhVhTU4EZVsoSBtPznxF/36c=";
+ };
custom-chart = {
package = ../charts/my-chart.tgz;
values = ../values/my-values.yaml;
diff --git a/nixos/modules/services/cluster/rke2/default.nix b/nixos/modules/services/cluster/rke2/default.nix
index 1c7ef3af1c8a..6e0cfd9df3fe 100644
--- a/nixos/modules/services/cluster/rke2/default.nix
+++ b/nixos/modules/services/cluster/rke2/default.nix
@@ -228,7 +228,7 @@ in
}
{
assertion = cfg.role == "agent" -> !(cfg.agentTokenFile != null || cfg.agentToken != "");
- message = "agentToken or agentTokenFile should be set if role is 'agent'";
+ message = "agentToken or agentTokenFile should NOT be set if role is 'agent'";
}
{
assertion = cfg.role == "agent" -> !(cfg.disable != [ ]);
diff --git a/nixos/modules/services/continuous-integration/gitlab-runner.nix b/nixos/modules/services/continuous-integration/gitlab-runner.nix
index 895ba66e7ea0..f68460c86c91 100644
--- a/nixos/modules/services/continuous-integration/gitlab-runner.nix
+++ b/nixos/modules/services/continuous-integration/gitlab-runner.nix
@@ -188,6 +188,7 @@ let
[ "--docker-image ${service.dockerImage}" ]
++ optional service.dockerDisableCache "--docker-disable-cache"
++ optional service.dockerPrivileged "--docker-privileged"
+ ++ optional (service.dockerPullPolicy != null) "--docker-pull-policy ${service.dockerPullPolicy}"
++ map (v: "--docker-volumes ${escapeShellArg v}") service.dockerVolumes
++ map (v: "--docker-extra-hosts ${escapeShellArg v}") service.dockerExtraHosts
++ map (v: "--docker-allowed-images ${escapeShellArg v}") service.dockerAllowedImages
@@ -482,6 +483,19 @@ in
Docker image to be used.
'';
};
+ dockerPullPolicy = mkOption {
+ type = types.nullOr (
+ types.enum [
+ "always"
+ "never"
+ "if-not-present"
+ ]
+ );
+ default = null;
+ description = ''
+ Default pull-policy for Docker images
+ '';
+ };
dockerVolumes = mkOption {
type = types.listOf types.str;
default = [ ];
diff --git a/nixos/modules/services/continuous-integration/hydra/default.nix b/nixos/modules/services/continuous-integration/hydra/default.nix
index d83db34b9aa3..df586356306a 100644
--- a/nixos/modules/services/continuous-integration/hydra/default.nix
+++ b/nixos/modules/services/continuous-integration/hydra/default.nix
@@ -564,16 +564,14 @@ in
services.postgresql.enable = lib.mkIf haveLocalDB true;
services.postgresql.identMap = lib.optionalString haveLocalDB ''
- hydra-users hydra hydra
- hydra-users hydra-queue-runner hydra
- hydra-users hydra-www hydra
- hydra-users root hydra
- # The postgres user is used to create the pg_trgm extension for the hydra database
- hydra-users postgres postgres
+ hydra hydra hydra
+ hydra hydra-queue-runner hydra
+ hydra hydra-www hydra
+ hydra root hydra
'';
services.postgresql.authentication = lib.optionalString haveLocalDB ''
- local hydra all ident map=hydra-users
+ local all hydra peer map=hydra
'';
};
diff --git a/nixos/modules/services/databases/cassandra.nix b/nixos/modules/services/databases/cassandra.nix
index 0130564778e6..814f0e99a8a7 100644
--- a/nixos/modules/services/databases/cassandra.nix
+++ b/nixos/modules/services/databases/cassandra.nix
@@ -24,10 +24,6 @@ let
cfg = config.services.cassandra;
- atLeast3 = versionAtLeast cfg.package.version "3";
- atLeast3_11 = versionAtLeast cfg.package.version "3.11";
- atLeast4 = versionAtLeast cfg.package.version "4";
-
defaultUser = "cassandra";
cassandraConfig = flip recursiveUpdate cfg.extraConfig (
@@ -41,6 +37,7 @@ let
data_file_directories = [ "${cfg.homeDir}/data" ];
commitlog_directory = "${cfg.homeDir}/commitlog";
saved_caches_directory = "${cfg.homeDir}/saved_caches";
+ hints_directory = "${cfg.homeDir}/hints";
}
// optionalAttrs (cfg.seedAddresses != [ ]) {
seed_provider = [
@@ -50,9 +47,6 @@ let
}
];
}
- // optionalAttrs atLeast3 {
- hints_directory = "${cfg.homeDir}/hints";
- }
);
cassandraConfigWithAddresses =
@@ -97,9 +91,7 @@ let
# Delete default password file
sed -i '/-Dcom.sun.management.jmxremote.password.file=\/etc\/cassandra\/jmxremote.password/d' "$out/cassandra-env.sh"
- ${lib.optionalString atLeast4 ''
- cp $package/conf/jvm*.options $out/
- ''}
+ cp $package/conf/jvm*.options $out/
'';
};
@@ -109,17 +101,17 @@ let
fullJvmOptions =
cfg.jvmOpts
+ ++ [
+ # Historically, we don't use a log dir, whereas the upstream scripts do
+ # expect this. We override those by providing our own -Xlog:gc flag.
+ "-Xlog:gc=warning,heap*=warning,age*=warning,safepoint=warning,promotion*=warning"
+ ]
++ optionals (cfg.jmxRoles != [ ]) [
"-Dcom.sun.management.jmxremote.authenticate=true"
"-Dcom.sun.management.jmxremote.password.file=${cfg.jmxRolesFile}"
]
++ optionals cfg.remoteJmx [
"-Djava.rmi.server.hostname=${cfg.rpcAddress}"
- ]
- ++ optionals atLeast4 [
- # Historically, we don't use a log dir, whereas the upstream scripts do
- # expect this. We override those by providing our own -Xlog:gc flag.
- "-Xlog:gc=warning,heap*=warning,age*=warning,safepoint=warning,promotion*=warning"
];
commonEnv = {
@@ -169,7 +161,7 @@ in
};
package = mkPackageOption pkgs "cassandra" {
- example = "cassandra_3_11";
+ example = "cassandra_4";
};
jvmOpts = mkOption {
@@ -462,14 +454,11 @@ in
jmxRolesFile = mkOption {
type = types.nullOr types.path;
- default = if atLeast3_11 then pkgs.writeText "jmx-roles-file" defaultJmxRolesFile else null;
- defaultText = literalMD ''generated configuration file if version is at least 3.11, otherwise `null`'';
+ default = pkgs.writeText "jmx-roles-file" defaultJmxRolesFile;
+ defaultText = "generated configuration file";
example = "/var/lib/cassandra/jmx.password";
description = ''
Specify your own jmx roles file.
-
- Make sure the permissions forbid "others" from reading the file if
- you're using Cassandra below version 3.11.
'';
};
};
@@ -492,8 +481,7 @@ in
assertion = cfg.remoteJmx -> cfg.jmxRolesFile != null;
message = ''
If you want JMX available remotely you need to set a password using
- jmxRoles or jmxRolesFile if
- using Cassandra older than v3.11.
+ jmxRoles .
'';
}
];
diff --git a/nixos/modules/services/databases/mysql.nix b/nixos/modules/services/databases/mysql.nix
index a43482393641..59c868d6628e 100644
--- a/nixos/modules/services/databases/mysql.nix
+++ b/nixos/modules/services/databases/mysql.nix
@@ -18,6 +18,22 @@ let
format = pkgs.formats.ini { listsAsDuplicateKeys = true; };
configFile = format.generate "my.cnf" cfg.settings;
+ generateClusterAddressExpr = ''
+ if (config.services.mysql.galeraCluster.nodeAddresses == [ ]) then
+ ""
+ else
+ "gcomm://''${builtins.concatStringsSep \",\" config.services.mysql.galeraCluster.nodeAddresses}"
+ + lib.optionalString (config.services.mysql.galeraCluster.clusterPassword != "")
+ "?gmcast.seg=1:''${config.services.mysql.galeraCluster.clusterPassword}"
+ '';
+ generateClusterAddress =
+ if (cfg.galeraCluster.nodeAddresses == [ ]) then
+ ""
+ else
+ "gcomm://${builtins.concatStringsSep "," cfg.galeraCluster.nodeAddresses}"
+ + lib.optionalString (
+ cfg.galeraCluster.clusterPassword != ""
+ ) "?gmcast.seg=1:${cfg.galeraCluster.clusterPassword}";
in
{
@@ -320,6 +336,69 @@ in
description = "Port number on which the MySQL master server runs.";
};
};
+
+ galeraCluster = {
+ enable = lib.mkEnableOption "MariaDB Galera Cluster";
+
+ package = lib.mkOption {
+ type = lib.types.package;
+ description = "The MariaDB Galera package that provides the shared library 'libgalera_smm.so' required for cluster functionality.";
+ default = lib.literalExpression "pkgs.mariadb-galera";
+ };
+
+ name = lib.mkOption {
+ type = lib.types.str;
+ description = "The logical name of the Galera cluster. All nodes in the same cluster must use the same name.";
+ default = "galera";
+ };
+
+ sstMethod = lib.mkOption {
+ type = lib.types.enum [
+ "rsync"
+ "mariabackup"
+ ];
+ description = "Method for the initial state transfer (wsrep_sst_method) when a node joins the cluster. Be aware that rsync needs SSH keys to be generated and authorized on all nodes!";
+ default = "rsync";
+ example = "mariabackup";
+ };
+
+ localName = lib.mkOption {
+ type = lib.types.str;
+ description = "The unique name that identifies this particular node within the cluster. Each node must have a different name.";
+ example = "node1";
+ };
+
+ localAddress = lib.mkOption {
+ type = lib.types.str;
+ description = "IP address or hostname of this node that will be used for cluster communication. Must be reachable by all other nodes.";
+ example = "1.2.3.4";
+ default = cfg.galeraCluster.localName;
+ defaultText = lib.literalExpression "config.services.mysql.galeraCluster.localName";
+ };
+
+ nodeAddresses = lib.mkOption {
+ type = lib.types.listOf lib.types.str;
+ description = "IP addresses or hostnames of all nodes in the cluster, including this node. This is used to construct the default clusterAddress connection string.";
+ example = lib.literalExpression ''["10.0.0.10" "10.0.0.20" "10.0.0.30"]'';
+ default = [ ];
+ };
+
+ clusterPassword = lib.mkOption {
+ type = lib.types.str;
+ description = "Optional password for securing cluster communications. If provided, it will be used in the clusterAddress for authentication between nodes.";
+ example = "SomePassword";
+ default = "";
+ };
+
+ clusterAddress = lib.mkOption {
+ type = lib.types.str;
+ description = "Full Galera cluster connection string. If nodeAddresses is set, this will be auto-generated, but you can override it with a custom value. Format is typically 'gcomm://node1,node2,node3' with optional parameters.";
+ example = "gcomm://10.0.0.10,10.0.0.20,10.0.0.30?gmcast.seg=1:SomePassword";
+ default = ""; # will be evaluate by generateClusterAddress
+ defaultText = lib.literalExpression generateClusterAddressExpr;
+ };
+
+ };
};
};
@@ -327,6 +406,30 @@ in
###### implementation
config = lib.mkIf cfg.enable {
+ assertions =
+ [
+ {
+ assertion = !cfg.galeraCluster.enable || isMariaDB;
+ message = "'services.mysql.galeraCluster.enable' expect services.mysql.package to be an mariadb variant";
+ }
+ ]
+ # galeraCluster options checks
+ ++ lib.optionals cfg.galeraCluster.enable [
+ {
+ assertion =
+ cfg.galeraCluster.localAddress != ""
+ && (cfg.galeraCluster.nodeAddresses != [ ] || cfg.galeraCluster.clusterAddress != "");
+ message = "mariadb galera cluster is enabled but the localAddress and (nodeAddresses or clusterAddress) are not set";
+ }
+ {
+ assertion = cfg.galeraCluster.clusterPassword == "" || cfg.galeraCluster.clusterAddress == "";
+ message = "mariadb galera clusterPassword is set but overwritten by clusterAddress";
+ }
+ {
+ assertion = cfg.galeraCluster.nodeAddresses != [ ] || cfg.galeraCluster.clusterAddress != "";
+ message = "When services.mysql.galeraCluster.clusterAddress is set, setting services.mysql.galeraCluster.nodeAddresses is redundant and will be overwritten by clusterAddress. Choose one approach.";
+ }
+ ];
services.mysql.dataDir = lib.mkDefault (
if lib.versionAtLeast config.system.stateVersion "17.09" then "/var/lib/mysql" else "/var/mysql"
@@ -351,8 +454,42 @@ in
(lib.mkIf (!isMariaDB) {
plugin-load-add = [ "auth_socket.so" ];
})
+ (lib.mkIf cfg.galeraCluster.enable {
+ # Ensure Only InnoDB is used as galera clusters can only work with them
+ enforce_storage_engine = "InnoDB";
+ default_storage_engine = "InnoDB";
+
+ # galera only support this binlog format
+ binlog-format = "ROW";
+
+ bind_address = lib.mkDefault "0.0.0.0";
+ })
];
+ services.mysql.settings.galera = lib.optionalAttrs cfg.galeraCluster.enable {
+ wsrep_on = "ON";
+ wsrep_debug = lib.mkDefault "NONE";
+ wsrep_retry_autocommit = lib.mkDefault "3";
+ wsrep_provider = "${cfg.galeraCluster.package}/lib/galera/libgalera_smm.so";
+
+ wsrep_cluster_name = cfg.galeraCluster.name;
+ wsrep_cluster_address =
+ if (cfg.galeraCluster.clusterAddress != "") then
+ cfg.galeraCluster.clusterAddress
+ else
+ generateClusterAddress;
+
+ wsrep_node_address = cfg.galeraCluster.localAddress;
+ wsrep_node_name = "${cfg.galeraCluster.localName}";
+
+ # SST method using rsync
+ wsrep_sst_method = lib.mkDefault cfg.galeraCluster.sstMethod;
+ wsrep_sst_auth = lib.mkDefault "check_repl:check_pass";
+
+ binlog_format = "ROW";
+ innodb_autoinc_lock_mode = 2;
+ };
+
users.users = lib.optionalAttrs (cfg.user == "mysql") {
mysql = {
description = "MySQL server user";
@@ -384,11 +521,29 @@ in
unitConfig.RequiresMountsFor = cfg.dataDir;
- path = [
- # Needed for the mysql_install_db command in the preStart script
- # which calls the hostname command.
- pkgs.nettools
- ];
+ path =
+ [
+ # Needed for the mysql_install_db command in the preStart script
+ # which calls the hostname command.
+ pkgs.nettools
+ ]
+ # tools 'wsrep_sst_rsync' needs
+ ++ lib.optionals cfg.galeraCluster.enable [
+ cfg.package
+ pkgs.bash
+ pkgs.gawk
+ pkgs.gnutar
+ pkgs.gzip
+ pkgs.inetutils
+ pkgs.iproute2
+ pkgs.netcat
+ pkgs.procps
+ pkgs.pv
+ pkgs.rsync
+ pkgs.socat
+ pkgs.stunnel
+ pkgs.which
+ ];
preStart =
if isMariaDB then
@@ -581,6 +736,17 @@ in
})
];
};
+
+ # Open firewall ports for MySQL (and Galera)
+ networking.firewall.allowedTCPPorts = lib.optionals cfg.galeraCluster.enable [
+ 3306 # MySQL
+ 4567 # Galera Cluster
+ 4568 # Galera IST
+ 4444 # SST
+ ];
+ networking.firewall.allowedUDPPorts = lib.optionals cfg.galeraCluster.enable [
+ 4567 # Galera Cluster
+ ];
};
meta.maintainers = [ lib.maintainers._6543 ];
diff --git a/nixos/modules/services/databases/postgres-websockets.nix b/nixos/modules/services/databases/postgres-websockets.nix
new file mode 100644
index 000000000000..a83054a507cb
--- /dev/null
+++ b/nixos/modules/services/databases/postgres-websockets.nix
@@ -0,0 +1,221 @@
+{
+ config,
+ lib,
+ pkgs,
+ ...
+}:
+
+let
+ cfg = config.services.postgres-websockets;
+
+ # Turns an attrset of libpq connection params:
+ # {
+ # dbname = "postgres";
+ # user = "authenticator";
+ # }
+ # into a libpq connection string:
+ # dbname=postgres user=authenticator
+ PGWS_DB_URI = lib.pipe cfg.environment.PGWS_DB_URI [
+ (lib.filterAttrs (_: v: v != null))
+ (lib.mapAttrsToList (k: v: "${k}='${lib.escape [ "'" "\\" ] v}'"))
+ (lib.concatStringsSep " ")
+ ];
+in
+
+{
+ meta = {
+ maintainers = with lib.maintainers; [ wolfgangwalther ];
+ };
+
+ options.services.postgres-websockets = {
+ enable = lib.mkEnableOption "postgres-websockets";
+
+ pgpassFile = lib.mkOption {
+ type =
+ with lib.types;
+ nullOr (pathWith {
+ inStore = false;
+ absolute = true;
+ });
+ default = null;
+ example = "/run/keys/db_password";
+ description = ''
+ The password to authenticate to PostgreSQL with.
+ Not needed for peer or trust based authentication.
+
+ The file must be a valid `.pgpass` file as described in:
+
+
+ In most cases, the following will be enough:
+ ```
+ *:*:*:*:
+ ```
+ '';
+ };
+
+ jwtSecretFile = lib.mkOption {
+ type =
+ with lib.types;
+ nullOr (pathWith {
+ inStore = false;
+ absolute = true;
+ });
+ example = "/run/keys/jwt_secret";
+ description = ''
+ Secret used to sign JWT tokens used to open communications channels.
+ '';
+ };
+
+ environment = lib.mkOption {
+ type = lib.types.submodule {
+ freeformType = with lib.types; attrsOf str;
+
+ options = {
+ PGWS_DB_URI = lib.mkOption {
+ type = lib.types.submodule {
+ freeformType = with lib.types; attrsOf str;
+
+ # This should not be used; use pgpassFile instead.
+ options.password = lib.mkOption {
+ default = null;
+ readOnly = true;
+ internal = true;
+ };
+ # This should not be used; use pgpassFile instead.
+ options.passfile = lib.mkOption {
+ default = null;
+ readOnly = true;
+ internal = true;
+ };
+ };
+ default = { };
+ description = ''
+ libpq connection parameters as documented in:
+
+
+
+ ::: {.note}
+ The `environment.PGWS_DB_URI.password` and `environment.PGWS_DB_URI.passfile` options are blocked.
+ Use [`pgpassFile`](#opt-services.postgres-websockets.pgpassFile) instead.
+ :::
+ '';
+ example = lib.literalExpression ''
+ {
+ host = "localhost";
+ dbname = "postgres";
+ }
+ '';
+ };
+
+ # This should not be used; use jwtSecretFile instead.
+ PGWS_JWT_SECRET = lib.mkOption {
+ default = null;
+ readOnly = true;
+ internal = true;
+ };
+
+ PGWS_HOST = lib.mkOption {
+ type = with lib.types; nullOr str;
+ default = "127.0.0.1";
+ description = ''
+ Address the server will listen for websocket connections.
+ '';
+ };
+ };
+ };
+ default = { };
+ description = ''
+ postgres-websockets configuration as defined in:
+
+
+ `PGWS_DB_URI` is represented as an attribute set, see [`environment.PGWS_DB_URI`](#opt-services.postgres-websockets.environment.PGWS_DB_URI)
+
+ ::: {.note}
+ The `environment.PGWS_JWT_SECRET` option is blocked.
+ Use [`jwtSecretFile`](#opt-services.postgres-websockets.jwtSecretFile) instead.
+ :::
+ '';
+ example = lib.literalExpression ''
+ {
+ PGWS_LISTEN_CHANNEL = "my_channel";
+ PGWS_DB_URI.dbname = "postgres";
+ }
+ '';
+ };
+ };
+
+ config = lib.mkIf cfg.enable {
+ services.postgres-websockets.environment.PGWS_DB_URI.application_name =
+ with pkgs.postgres-websockets;
+ "${pname} ${version}";
+
+ systemd.services.postgres-websockets = {
+ description = "postgres-websockets";
+
+ wantedBy = [ "multi-user.target" ];
+ wants = [ "network-online.target" ];
+ after = [
+ "network-online.target"
+ "postgresql.service"
+ ];
+
+ environment =
+ cfg.environment
+ // {
+ inherit PGWS_DB_URI;
+ PGWS_JWT_SECRET = "@%d/jwt_secret";
+ }
+ // lib.optionalAttrs (cfg.pgpassFile != null) {
+ PGPASSFILE = "%C/postgres-websockets/pgpass";
+ };
+
+ serviceConfig = {
+ CacheDirectory = "postgres-websockets";
+ CacheDirectoryMode = "0700";
+ LoadCredential = [
+ "jwt_secret:${cfg.jwtSecretFile}"
+ ] ++ lib.optional (cfg.pgpassFile != null) "pgpass:${cfg.pgpassFile}";
+ Restart = "always";
+ User = "postgres-websockets";
+
+ # Hardening
+ CapabilityBoundingSet = [ "" ];
+ DevicePolicy = "closed";
+ DynamicUser = true;
+ LockPersonality = true;
+ MemoryDenyWriteExecute = true;
+ NoNewPrivileges = true;
+ PrivateDevices = true;
+ PrivateIPC = true;
+ PrivateMounts = true;
+ ProcSubset = "pid";
+ ProtectClock = true;
+ ProtectControlGroups = true;
+ ProtectHostname = true;
+ ProtectKernelLogs = true;
+ ProtectKernelModules = true;
+ ProtectKernelTunables = true;
+ ProtectProc = "invisible";
+ RestrictAddressFamilies = [
+ "AF_INET"
+ "AF_INET6"
+ "AF_UNIX"
+ ];
+ RestrictNamespaces = true;
+ RestrictRealtime = true;
+ SystemCallArchitectures = "native";
+ SystemCallFilter = [ "" ];
+ UMask = "0077";
+ };
+
+ # Copy the pgpass file to different location, to have it report mode 0400.
+ # Fixes: https://github.com/systemd/systemd/issues/29435
+ script = ''
+ if [ -f "$CREDENTIALS_DIRECTORY/pgpass" ]; then
+ cp -f "$CREDENTIALS_DIRECTORY/pgpass" "$CACHE_DIRECTORY/pgpass"
+ fi
+ exec ${lib.getExe pkgs.postgres-websockets}
+ '';
+ };
+ };
+}
diff --git a/nixos/modules/services/databases/postgresql.md b/nixos/modules/services/databases/postgresql.md
index 34eabb1db6a7..e1256c9672f2 100644
--- a/nixos/modules/services/databases/postgresql.md
+++ b/nixos/modules/services/databases/postgresql.md
@@ -170,6 +170,38 @@ are already created.
}
```
+## Authentication {#module-services-postgres-authentication}
+
+Local connections are made through unix sockets by default and support [peer authentication](https://www.postgresql.org/docs/current/auth-peer.html).
+This allows system users to login with database roles of the same name.
+For example, the `postgres` system user is allowed to login with the database role `postgres`.
+
+System users and database roles might not always match.
+In this case, to allow access for a service, you can create a [user name map](https://www.postgresql.org/docs/current/auth-username-maps.html) between system roles and an existing database role.
+
+### User Mapping {#module-services-postgres-authentication-user-mapping}
+
+Assume that your app creates a role `admin` and you want the `root` user to be able to login with it.
+You can then use [](#opt-services.postgresql.identMap) to define the map and [](#opt-services.postgresql.authentication) to enable it:
+
+```nix
+services.postgresql = {
+ identMap = ''
+ admin root admin
+ '';
+ authentication = ''
+ local all admin peer map=admin
+ '';
+}
+```
+
+::: {.warning}
+To avoid conflicts with other modules, you should never apply a map to `all` roles.
+Because PostgreSQL will stop on the first matching line in `pg_hba.conf`, a line matching all roles would lock out other services.
+Each module should only manage user maps for the database roles that belong to this module.
+Best practice is to name the map after the database role it manages to avoid name conflicts.
+:::
+
## Upgrading {#module-services-postgres-upgrading}
::: {.note}
@@ -327,6 +359,37 @@ self: super: {
}
```
+## Procedural Languages {#module-services-postgres-pls}
+
+PostgreSQL ships the additional procedural languages PL/Perl, PL/Python and PL/Tcl as extensions.
+They are packaged as plugins and can be made available in the same way as external extensions:
+```nix
+{
+ services.postgresql.extensions = ps: with ps; [
+ plperl
+ plpython3
+ pltcl
+ ];
+}
+```
+
+Each procedural language plugin provides a `.withPackages` helper to make language specific packages available at run-time.
+
+For example, to make `python3Packages.base58` available:
+```nix
+{
+ services.postgresql.extensions = pgps: with pgps; [
+ (plpython3.withPackages (pyps: with pyps; [ base58 ]))
+ ];
+}
+```
+
+This currently works for:
+- `plperl` by re-using `perl.withPackages`
+- `plpython3` by re-using `python3.withPackages`
+- `plr` by exposing `rPackages`
+- `pltcl` by exposing `tclPackages`
+
## JIT (Just-In-Time compilation) {#module-services-postgres-jit}
[JIT](https://www.postgresql.org/docs/current/jit-reason.html)-support in the PostgreSQL package
diff --git a/nixos/modules/services/databases/postgresql.nix b/nixos/modules/services/databases/postgresql.nix
index 99f36d85a943..48ff4c2a48ef 100644
--- a/nixos/modules/services/databases/postgresql.nix
+++ b/nixos/modules/services/databases/postgresql.nix
@@ -90,6 +90,12 @@ in
"extraConfig"
] "Use services.postgresql.settings instead.")
+ (mkRemovedOptionModule [
+ "services"
+ "postgresql"
+ "recoveryConfig"
+ ] "PostgreSQL v12+ doesn't support recovery.conf.")
+
(mkRenamedOptionModule
[ "services" "postgresql" "logLinePrefix" ]
[ "services" "postgresql" "settings" "log_line_prefix" ]
@@ -268,6 +274,14 @@ in
Defines the mapping from system users to database users.
See the [auth doc](https://postgresql.org/docs/current/auth-username-maps.html).
+
+ There is a default map "postgres" which is used for local peer authentication
+ as the postgres superuser role.
+ For example, to allow the root user to login as the postgres superuser, add:
+
+ ```
+ postgres root postgres
+ ```
'';
};
@@ -588,14 +602,6 @@ in
'';
};
- recoveryConfig = mkOption {
- type = types.nullOr types.lines;
- default = null;
- description = ''
- Contents of the {file}`recovery.conf` file.
- '';
- };
-
superUser = mkOption {
type = types.str;
default = "postgres";
@@ -676,12 +682,20 @@ in
(mkBefore "# Generated file; do not edit!")
(mkAfter ''
# default value of services.postgresql.authentication
+ local all postgres peer map=postgres
local all all peer
host all all 127.0.0.1/32 md5
host all all ::1/128 md5
'')
];
+ # The default allows to login with the same database username as the current system user.
+ # This is the default for peer authentication without a map, but needs to be made explicit
+ # once a map is used.
+ services.postgresql.identMap = mkAfter ''
+ postgres postgres postgres
+ '';
+
services.postgresql.systemCallFilter = mkMerge [
(mapAttrs (const mkDefault) {
"@system-service" = true;
@@ -741,10 +755,6 @@ in
fi
ln -sfn "${configFile}/postgresql.conf" "${cfg.dataDir}/postgresql.conf"
- ${optionalString (cfg.recoveryConfig != null) ''
- ln -sfn "${pkgs.writeText "recovery.conf" cfg.recoveryConfig}" \
- "${cfg.dataDir}/recovery.conf"
- ''}
'';
# Wait for PostgreSQL to be ready to accept connections.
diff --git a/nixos/modules/services/databases/postgrest.nix b/nixos/modules/services/databases/postgrest.nix
index 34d36bae0bee..e9e03089a8b2 100644
--- a/nixos/modules/services/databases/postgrest.nix
+++ b/nixos/modules/services/databases/postgrest.nix
@@ -245,6 +245,10 @@ in
lib.optional (cfg.settings.admin-server-port != null && cfg.settings.server-host != "127.0.0.1")
"The PostgREST admin server is potentially listening on a public host. This may expose sensitive information via the `/config` endpoint.";
+ # Since we're using DynamicUser, we can't add the e.g. nginx user to
+ # a postgrest group, so the unix socket must be world-readable to make it useful.
+ services.postgrest.settings.service-unix-socket-mode = "666";
+
systemd.services.postgrest = {
description = "PostgREST";
diff --git a/nixos/modules/services/desktop-managers/cosmic.nix b/nixos/modules/services/desktop-managers/cosmic.nix
index 576cbe1438ec..18ac3f4836f3 100644
--- a/nixos/modules/services/desktop-managers/cosmic.nix
+++ b/nixos/modules/services/desktop-managers/cosmic.nix
@@ -14,12 +14,7 @@ let
cfg = config.services.desktopManager.cosmic;
in
{
- meta.maintainers = with lib.maintainers; [
- thefossguy
- HeitorAugustoLN
- nyabinary
- ahoneybun
- ];
+ meta.maintainers = lib.teams.cosmic.members;
options = {
services.desktopManager.cosmic = {
@@ -45,7 +40,7 @@ in
cosmic-applets
cosmic-applibrary
cosmic-bg
- (cosmic-comp.override { useXWayland = false; })
+ cosmic-comp
cosmic-edit
cosmic-files
config.services.displayManager.cosmic-greeter.package
diff --git a/nixos/modules/services/desktops/dleyna-renderer.nix b/nixos/modules/services/desktops/dleyna-renderer.nix
deleted file mode 100644
index 78375735e6e1..000000000000
--- a/nixos/modules/services/desktops/dleyna-renderer.nix
+++ /dev/null
@@ -1,29 +0,0 @@
-# dleyna-renderer service.
-{
- config,
- lib,
- pkgs,
- ...
-}:
-{
- ###### interface
- options = {
- services.dleyna-renderer = {
- enable = lib.mkOption {
- type = lib.types.bool;
- default = false;
- description = ''
- Whether to enable dleyna-renderer service, a DBus service
- for handling DLNA renderers.
- '';
- };
- };
- };
-
- ###### implementation
- config = lib.mkIf config.services.dleyna-renderer.enable {
- environment.systemPackages = [ pkgs.dleyna-renderer ];
-
- services.dbus.packages = [ pkgs.dleyna-renderer ];
- };
-}
diff --git a/nixos/modules/services/desktops/dleyna-server.nix b/nixos/modules/services/desktops/dleyna-server.nix
deleted file mode 100644
index e36340405790..000000000000
--- a/nixos/modules/services/desktops/dleyna-server.nix
+++ /dev/null
@@ -1,29 +0,0 @@
-# dleyna-server service.
-{
- config,
- lib,
- pkgs,
- ...
-}:
-{
- ###### interface
- options = {
- services.dleyna-server = {
- enable = lib.mkOption {
- type = lib.types.bool;
- default = false;
- description = ''
- Whether to enable dleyna-server service, a DBus service
- for handling DLNA servers.
- '';
- };
- };
- };
-
- ###### implementation
- config = lib.mkIf config.services.dleyna-server.enable {
- environment.systemPackages = [ pkgs.dleyna-server ];
-
- services.dbus.packages = [ pkgs.dleyna-server ];
- };
-}
diff --git a/nixos/modules/services/desktops/dleyna.nix b/nixos/modules/services/desktops/dleyna.nix
new file mode 100644
index 000000000000..f34e3250067d
--- /dev/null
+++ b/nixos/modules/services/desktops/dleyna.nix
@@ -0,0 +1,33 @@
+{
+ config,
+ lib,
+ pkgs,
+ ...
+}:
+{
+ imports = [
+ (lib.mkRenamedOptionModule [ "services" "dleyna-server" ] [ "services" "dleyna" ])
+ (lib.mkRenamedOptionModule [ "services" "dleyna-renderer" ] [ "services" "dleyna" ])
+ ];
+
+ ###### interface
+ options = {
+ services.dleyna = {
+ enable = lib.mkOption {
+ type = lib.types.bool;
+ default = false;
+ description = ''
+ Whether to enable dleyna-renderer and dleyna-server service,
+ a DBus service for handling DLNA servers and renderers.
+ '';
+ };
+ };
+ };
+
+ ###### implementation
+ config = lib.mkIf config.services.dleyna.enable {
+ environment.systemPackages = [ pkgs.dleyna ];
+
+ services.dbus.packages = [ pkgs.dleyna ];
+ };
+}
diff --git a/nixos/modules/services/desktops/geoclue2.nix b/nixos/modules/services/desktops/geoclue2.nix
index bc5d52f9e374..b9d423acf9f3 100644
--- a/nixos/modules/services/desktops/geoclue2.nix
+++ b/nixos/modules/services/desktops/geoclue2.nix
@@ -180,7 +180,7 @@ in
geoProviderUrl = lib.mkOption {
type = lib.types.str;
- default = "https://location.services.mozilla.com/v1/geolocate?key=geoclue";
+ default = "https://api.beacondb.net/v1/geolocate";
example = "https://www.googleapis.com/geolocation/v1/geolocate?key=YOUR_KEY";
description = ''
The url to the wifi GeoLocation Service.
@@ -210,7 +210,7 @@ in
submissionUrl = lib.mkOption {
type = lib.types.str;
- default = "https://location.services.mozilla.com/v1/submit?key=geoclue";
+ default = "https://api.beacondb.net/v2/geosubmit";
description = ''
The url to submit data to a GeoLocation Service.
'';
diff --git a/nixos/modules/services/desktops/gsignond.nix b/nixos/modules/services/desktops/gsignond.nix
deleted file mode 100644
index 96a54889babf..000000000000
--- a/nixos/modules/services/desktops/gsignond.nix
+++ /dev/null
@@ -1,46 +0,0 @@
-# Accounts-SSO gSignOn daemon
-{
- config,
- lib,
- pkgs,
- ...
-}:
-let
- package = pkgs.gsignond.override { plugins = config.services.gsignond.plugins; };
-in
-{
-
- meta.maintainers = [ ];
-
- ###### interface
-
- options = {
-
- services.gsignond = {
-
- enable = lib.mkOption {
- type = lib.types.bool;
- default = false;
- description = ''
- Whether to enable gSignOn daemon, a DBus service
- which performs user authentication on behalf of its clients.
- '';
- };
-
- plugins = lib.mkOption {
- type = lib.types.listOf lib.types.package;
- default = [ ];
- description = ''
- What plugins to use with the gSignOn daemon.
- '';
- };
- };
- };
-
- ###### implementation
- config = lib.mkIf config.services.gsignond.enable {
- environment.etc."gsignond.conf".source = "${package}/etc/gsignond.conf";
- services.dbus.packages = [ package ];
- };
-
-}
diff --git a/nixos/modules/services/desktops/telepathy.nix b/nixos/modules/services/desktops/telepathy.nix
index d4aac1aa0c26..cfab2c3a413f 100644
--- a/nixos/modules/services/desktops/telepathy.nix
+++ b/nixos/modules/services/desktops/telepathy.nix
@@ -41,7 +41,6 @@
# Enable runtime optional telepathy in gnome-shell
services.xserver.desktopManager.gnome.sessionPath = with pkgs; [
telepathy-glib
- telepathy-logger
];
};
diff --git a/nixos/modules/services/display-managers/cosmic-greeter.nix b/nixos/modules/services/display-managers/cosmic-greeter.nix
index aa4243cfa700..80ca40e23d99 100644
--- a/nixos/modules/services/display-managers/cosmic-greeter.nix
+++ b/nixos/modules/services/display-managers/cosmic-greeter.nix
@@ -12,15 +12,11 @@
let
cfg = config.services.displayManager.cosmic-greeter;
+ cfgAutoLogin = config.services.displayManager.autoLogin;
in
{
- meta.maintainers = with lib.maintainers; [
- thefossguy
- HeitorAugustoLN
- nyabinary
- ahoneybun
- ];
+ meta.maintainers = lib.teams.cosmic.members;
options.services.displayManager.cosmic-greeter = {
enable = lib.mkEnableOption "COSMIC greeter";
@@ -35,6 +31,10 @@ in
user = "cosmic-greeter";
command = ''${lib.getExe' pkgs.coreutils "env"} XCURSOR_THEME="''${XCURSOR_THEME:-Pop}" systemd-cat -t cosmic-greeter ${lib.getExe pkgs.cosmic-comp} ${lib.getExe cfg.package}'';
};
+ initial_session = lib.mkIf (cfgAutoLogin.enable && (cfgAutoLogin.user != null)) {
+ user = cfgAutoLogin.user;
+ command = ''${lib.getExe' pkgs.coreutils "env"} XCURSOR_THEME="''${XCURSOR_THEME:-Pop}" systemd-cat -t cosmic-session ${lib.getExe pkgs.cosmic-session}'';
+ };
};
};
diff --git a/nixos/modules/services/editors/emacs.nix b/nixos/modules/services/editors/emacs.nix
index 50bca3596032..65211763e5a2 100644
--- a/nixos/modules/services/editors/emacs.nix
+++ b/nixos/modules/services/editors/emacs.nix
@@ -73,7 +73,8 @@ in
serviceConfig = {
Type = "notify";
ExecStart = "${pkgs.runtimeShell} -c 'source ${config.system.build.setEnvironment}; exec ${cfg.package}/bin/emacs --fg-daemon'";
- ExecStop = "${cfg.package}/bin/emacsclient --eval (kill-emacs)";
+ # Emacs exits with exit code 15 (SIGTERM), when stopped by systemd.
+ SuccessExitStatus = 15;
Restart = "always";
};
diff --git a/nixos/modules/services/games/deliantra-server.nix b/nixos/modules/services/games/deliantra-server.nix
deleted file mode 100644
index 4854731714f7..000000000000
--- a/nixos/modules/services/games/deliantra-server.nix
+++ /dev/null
@@ -1,182 +0,0 @@
-{
- config,
- lib,
- pkgs,
- ...
-}:
-let
- cfg = config.services.deliantra-server;
- serverPort = 13327;
-in
-{
- options.services.deliantra-server = {
- enable = lib.mkOption {
- type = lib.types.bool;
- default = false;
- description = ''
- If enabled, the Deliantra game server will be started at boot.
- '';
- };
-
- package = lib.mkPackageOption pkgs "deliantra-server" {
- extraDescription = ''
- ::: {.note}
- This will also be used for map/arch data, if you don't change {option}`dataDir`
- :::
- '';
- };
-
- dataDir = lib.mkOption {
- type = lib.types.str;
- default = "${pkgs.deliantra-data}";
- defaultText = lib.literalExpression ''"''${pkgs.deliantra-data}"'';
- description = ''
- Where to store readonly data (maps, archetypes, sprites, etc).
- Note that if you plan to use the live map editor (rather than editing
- the maps offline and then nixos-rebuilding), THIS MUST BE WRITEABLE --
- copy the deliantra-data someplace writeable (say,
- /var/lib/deliantra/data) and update this option accordingly.
- '';
- };
-
- stateDir = lib.mkOption {
- type = lib.types.str;
- default = "/var/lib/deliantra";
- description = ''
- Where to store runtime data (save files, persistent items, etc).
-
- If left at the default, this will be automatically created on server
- startup if it does not already exist. If changed, it is the admin's
- responsibility to make sure that the directory exists and is writeable
- by the `crossfire` user.
- '';
- };
-
- openFirewall = lib.mkOption {
- type = lib.types.bool;
- default = false;
- description = ''
- Whether to open ports in the firewall for the server.
- '';
- };
-
- configFiles = lib.mkOption {
- type = lib.types.attrsOf lib.types.str;
- description = ''
- Contents of the server configuration files. These will be appended to
- the example configurations the server comes with and overwrite any
- default settings defined therein.
-
- The example here is not comprehensive. See the files in
- /etc/deliantra-server after enabling this module for full documentation.
- '';
- example = lib.literalExpression ''
- {
- dm_file = '''
- admin:secret_password:localhost
- alice:xyzzy:*
- ''';
- motd = "Welcome to Deliantra!";
- settings = '''
- # Settings for game mechanics.
- stat_loss_on_death true
- armor_max_enchant 7
- ''';
- config = '''
- # Settings for the server daemon.
- hiscore_url https://deliantra.example.net/scores/
- max_map_reset 86400
- ''';
- }
- '';
- default = {
- motd = "";
- };
- };
- };
-
- config = lib.mkIf cfg.enable {
- users.users.deliantra = {
- description = "Deliantra server daemon user";
- home = cfg.stateDir;
- createHome = false;
- isSystemUser = true;
- group = "deliantra";
- };
- users.groups.deliantra = { };
-
- # Merge the cfg.configFiles setting with the default files shipped with
- # Deliantra.
- # For most files this consists of reading
- # ${deliantra}/etc/deliantra-server/${name} and appending the user setting
- # to it.
- environment.etc =
- lib.attrsets.mapAttrs'
- (
- name: value:
- lib.attrsets.nameValuePair "deliantra-server/${name}" {
- mode = "0644";
- text =
- # Deliantra doesn't come with a motd file, but respects it if present
- # in /etc.
- (lib.optionalString (name != "motd") (
- lib.fileContents "${cfg.package}/etc/deliantra-server/${name}"
- ))
- + "\n${value}";
- }
- )
- (
- {
- motd = "";
- settings = "";
- config = "";
- dm_file = "";
- }
- // cfg.configFiles
- );
-
- systemd.services.deliantra-server = {
- description = "Deliantra Server Daemon";
- wantedBy = [ "multi-user.target" ];
- after = [ "network.target" ];
-
- environment = {
- DELIANTRA_DATADIR = "${cfg.dataDir}";
- DELIANTRA_LOCALDIR = "${cfg.stateDir}";
- DELIANTRA_CONFDIR = "/etc/deliantra-server";
- };
-
- serviceConfig = lib.mkMerge [
- {
- ExecStart = "${cfg.package}/bin/deliantra-server";
- Restart = "always";
- User = "deliantra";
- Group = "deliantra";
- WorkingDirectory = cfg.stateDir;
- }
- (lib.mkIf (cfg.stateDir == "/var/lib/deliantra") {
- StateDirectory = "deliantra";
- })
- ];
-
- # The deliantra server needs access to a bunch of files at runtime that
- # are not created automatically at server startup; they're meant to be
- # installed in $PREFIX/var/deliantra-server by `make install`. And those
- # files need to be writeable, so we can't just point at the ones in the
- # nix store. Instead we take the approach of copying them out of the store
- # on first run. If `bookarch` already exists, we assume the rest of the
- # files do as well, and copy nothing -- otherwise we risk ovewriting
- # server state information every time the server is upgraded.
- preStart = ''
- if [ ! -e "${cfg.stateDir}"/bookarch ]; then
- ${pkgs.rsync}/bin/rsync -a --chmod=u=rwX,go=rX \
- "${cfg.package}/var/deliantra-server/" "${cfg.stateDir}/"
- fi
- '';
- };
-
- networking.firewall = lib.mkIf cfg.openFirewall {
- allowedTCPPorts = [ serverPort ];
- };
- };
-}
diff --git a/nixos/modules/services/games/minecraft-server.nix b/nixos/modules/services/games/minecraft-server.nix
index 55061eeccded..a4d82a6779bf 100644
--- a/nixos/modules/services/games/minecraft-server.nix
+++ b/nixos/modules/services/games/minecraft-server.nix
@@ -45,7 +45,7 @@ let
# To be able to open the firewall, we need to read out port values in the
# server properties, but fall back to the defaults when those don't exist.
- # These defaults are from https://minecraft.gamepedia.com/Server.properties#Java_Edition_3
+ # These defaults are from https://minecraft.wiki/w/Server.properties#Java_Edition
defaultServerPort = 25565;
serverPort = cfg.serverProperties.server-port or defaultServerPort;
@@ -93,10 +93,8 @@ in
type = lib.types.bool;
default = false;
description = ''
- Whether you agree to
- [
- Mojangs EULA](https://account.mojang.com/documents/minecraft_eula). This option must be set to
- `true` to run Minecraft server.
+ Whether you agree to [Mojangs EULA](https://www.minecraft.net/eula).
+ This option must be set to `true` to run Minecraft server.
'';
};
@@ -167,10 +165,10 @@ in
}
'';
description = ''
- Minecraft server properties for the server.properties file. Only has
+ Minecraft server properties forthe server.properties file. Only has
an effect when {option}`services.minecraft-server.declarative`
is set to `true`. See
-
+
for documentation on these values.
'';
};
@@ -182,7 +180,7 @@ in
jvmOpts = lib.mkOption {
type = lib.types.separatedString " ";
default = "-Xmx2048M -Xms2048M";
- # Example options from https://minecraft.gamepedia.com/Tutorials/Server_startup_script
+ # Example options from https://minecraft.wiki/w/Tutorial:Server_startup_script
example =
"-Xms4092M -Xmx4092M -XX:+UseG1GC -XX:+CMSIncrementalPacing "
+ "-XX:+CMSClassUnloadingEnabled -XX:ParallelGCThreads=2 "
diff --git a/nixos/modules/services/hardware/bluetooth.nix b/nixos/modules/services/hardware/bluetooth.nix
index d8907d554d33..47fb322e56d2 100644
--- a/nixos/modules/services/hardware/bluetooth.nix
+++ b/nixos/modules/services/hardware/bluetooth.nix
@@ -143,6 +143,8 @@ in
{
wantedBy = [ "bluetooth.target" ];
aliases = [ "dbus-org.bluez.service" ];
+ # restarting can leave people without a mouse/keyboard
+ restartIfChanged = false;
serviceConfig = {
ExecStart = [
""
@@ -151,6 +153,7 @@ in
CapabilityBoundingSet = [
"CAP_NET_BIND_SERVICE" # sockets and tethering
];
+ ConfigurationDirectoryMode = "0755";
NoNewPrivileges = true;
RestrictNamespaces = true;
ProtectControlGroups = true;
@@ -171,8 +174,6 @@ in
PrivateNetwork = false; # tethering
};
- # restarting can leave people without a mouse/keyboard
- unitConfig.X-RestartIfChanged = false;
};
}
// (optionalAttrs cfg.hsphfpd.enable {
diff --git a/nixos/modules/services/hardware/hddfancontrol.nix b/nixos/modules/services/hardware/hddfancontrol.nix
index 942425d63f7e..a1e36012893d 100644
--- a/nixos/modules/services/hardware/hddfancontrol.nix
+++ b/nixos/modules/services/hardware/hddfancontrol.nix
@@ -18,64 +18,162 @@ in
"hddfancontrol"
"smartctl"
] "Smartctl is now automatically used when necessary, which makes this option redundant")
+ (lib.mkRemovedOptionModule [
+ "services"
+ "hddfancontrol"
+ "disks"
+ ] "Disks should now be specified per hddfancontrol instance in its attrset")
+ (lib.mkRemovedOptionModule [
+ "services"
+ "hddfancontrol"
+ "pwmPaths"
+ ] "Pwm Paths should now be specified per hddfancontrol instance in its attrset")
+ (lib.mkRemovedOptionModule [
+ "services"
+ "hddfancontrol"
+ "logVerbosity"
+ ] "Log Verbosity should now be specified per hddfancontrol instance in its attrset")
+ (lib.mkRemovedOptionModule [
+ "services"
+ "hddfancontrol"
+ "extraArgs"
+ ] "Extra Args should now be specified per hddfancontrol instance in its attrset")
];
options = {
services.hddfancontrol.enable = lib.mkEnableOption "hddfancontrol daemon";
- services.hddfancontrol.disks = lib.mkOption {
- type = lib.types.listOf lib.types.path;
- default = [ ];
- description = ''
- Drive(s) to get temperature from
- '';
- example = [ "/dev/sda" ];
- };
+ services.hddfancontrol.settings = lib.mkOption {
+ type = lib.types.attrsWith {
+ placeholder = "drive-bay-name";
+ elemType = (
+ lib.types.submodule (
+ { ... }:
+ {
+ options = {
+ disks = lib.mkOption {
+ type = lib.types.listOf lib.types.path;
+ default = [ ];
+ description = ''
+ Drive(s) to get temperature from
+ '';
+ example = [ "/dev/sda" ];
+ };
- services.hddfancontrol.pwmPaths = lib.mkOption {
- type = lib.types.listOf lib.types.path;
- default = [ ];
- description = ''
- PWM filepath(s) to control fan speed (under /sys), followed by initial and fan-stop PWM values
- '';
- example = [ "/sys/class/hwmon/hwmon2/pwm1:30:10" ];
- };
+ pwmPaths = lib.mkOption {
+ type = lib.types.listOf lib.types.path;
+ default = [ ];
+ description = ''
+ PWM filepath(s) to control fan speed (under /sys), followed by initial and fan-stop PWM values
+ '';
+ example = [ "/sys/class/hwmon/hwmon2/pwm1:30:10" ];
+ };
- services.hddfancontrol.logVerbosity = lib.mkOption {
- type = lib.types.enum [
- "TRACE"
- "DEBUG"
- "INFO"
- "WARN"
- "ERROR"
- ];
- default = "INFO";
- description = ''
- Verbosity of the log level
- '';
- };
+ logVerbosity = lib.mkOption {
+ type = lib.types.enum [
+ "TRACE"
+ "DEBUG"
+ "INFO"
+ "WARN"
+ "ERROR"
+ ];
+ default = "INFO";
+ description = ''
+ Verbosity of the log level
+ '';
+ };
- services.hddfancontrol.extraArgs = lib.mkOption {
- type = lib.types.listOf lib.types.str;
- default = [ ];
+ extraArgs = lib.mkOption {
+ type = lib.types.listOf lib.types.str;
+ default = [ ];
+ description = ''
+ Extra commandline arguments for hddfancontrol
+ '';
+ example = [
+ "--min-fan-speed-prct=10"
+ "--interval=1min"
+ ];
+ };
+ };
+ }
+ )
+ );
+ };
+ default = { };
description = ''
- Extra commandline arguments for hddfancontrol
+ Parameter-sets for each instance of hddfancontrol.
+ '';
+ example = lib.literalExpression ''
+ {
+ harddrives = {
+ disks = [
+ "/dev/sda"
+ "/dev/sdb"
+ "/dev/sdc"
+ ];
+ pwmPaths = [
+ "/sys/class/hwmon/hwmon1/pwm1:25:10"
+ ];
+ logVerbosity = "DEBUG";
+ };
+ ssddrives = {
+ disks = [
+ "/dev/sdd"
+ "/dev/sde"
+ "/dev/sdf"
+ ];
+ pwmPaths = [
+ "/sys/class/hwmon/hwmon1/pwm2:25:10"
+ ];
+ extraArgs = [
+ "--interval=30s"
+ ];
+ };
+ }
'';
- example = [
- "--min-fan-speed-prct=10"
- "--interval=1min"
- ];
};
};
config = lib.mkIf cfg.enable (
let
- args = lib.concatLists [
- [ "-d" ]
- cfg.disks
- [ "-p" ]
- cfg.pwmPaths
- cfg.extraArgs
+ args =
+ cnf:
+ lib.concatLists [
+ [ "-d" ]
+ cnf.disks
+ [ "-p" ]
+ cnf.pwmPaths
+ cnf.extraArgs
+ ];
+
+ createService = cnf: {
+ description = "HDD fan control";
+ documentation = [ "man:hddfancontrol(1)" ];
+ after = [ "hddtemp.service" ];
+ wants = [ "hddtemp.service" ];
+ serviceConfig = {
+ ExecStart = "${lib.getExe pkgs.hddfancontrol} -v ${cnf.logVerbosity} daemon ${lib.escapeShellArgs (args cnf)}";
+
+ CPUSchedulingPolicy = "rr";
+ CPUSchedulingPriority = 49;
+
+ ProtectSystem = "strict";
+ PrivateTmp = true;
+ ProtectHome = true;
+ SystemCallArchitectures = "native";
+ MemoryDenyWriteExecute = true;
+ NoNewPrivileges = true;
+ };
+ wantedBy = [ "multi-user.target" ];
+ };
+
+ services = lib.attrsets.mergeAttrsList [
+ (lib.attrsets.mapAttrs' (
+ name: cnf: lib.nameValuePair "hddfancontrol-${name}" (createService cnf)
+ ) cfg.settings)
+ {
+ "hddfancontrol".enable = false;
+ }
];
in
{
@@ -83,16 +181,10 @@ in
hardware.sensor.hddtemp = {
enable = true;
- drives = cfg.disks;
+ drives = lib.lists.flatten (lib.attrsets.catAttrs "disks" (lib.attrsets.attrValues cfg.settings));
};
- systemd.services.hddfancontrol = {
- wantedBy = [ "multi-user.target" ];
- environment = {
- HDDFANCONTROL_LOG_LEVEL = cfg.logVerbosity;
- HDDFANCONTROL_DAEMON_ARGS = lib.escapeShellArgs args;
- };
- };
+ systemd.services = services;
}
);
}
diff --git a/nixos/modules/services/hardware/nvidia-container-toolkit/cdi-generate.nix b/nixos/modules/services/hardware/nvidia-container-toolkit/cdi-generate.nix
index 78b7ec080289..bf4e5632271a 100644
--- a/nixos/modules/services/hardware/nvidia-container-toolkit/cdi-generate.nix
+++ b/nixos/modules/services/hardware/nvidia-container-toolkit/cdi-generate.nix
@@ -39,7 +39,7 @@ writeScriptBin "nvidia-cdi-generator" ''
--device-name-strategy ${deviceNameStrategy} \
--ldconfig-path ${lib.getExe' glibc "ldconfig"} \
--library-search-path ${lib.getLib nvidia-driver}/lib \
- --nvidia-ctk-path ${lib.getExe' nvidia-container-toolkit "nvidia-ctk"}
+ --nvidia-cdi-hook-path ${lib.getExe' nvidia-container-toolkit.tools "nvidia-cdi-hook"}
}
function additionalMount {
diff --git a/nixos/modules/services/hardware/nvidia-container-toolkit/default.nix b/nixos/modules/services/hardware/nvidia-container-toolkit/default.nix
index 8b5c5c95b12e..af94fbe6a45b 100644
--- a/nixos/modules/services/hardware/nvidia-container-toolkit/default.nix
+++ b/nixos/modules/services/hardware/nvidia-container-toolkit/default.nix
@@ -50,6 +50,15 @@
'';
};
+ suppressNvidiaDriverAssertion = lib.mkOption {
+ default = false;
+ type = lib.types.bool;
+ description = ''
+ Suppress the assertion for installing Nvidia driver.
+ Useful in WSL where drivers are mounted from Windows, not provided by NixOS.
+ '';
+ };
+
mounts = lib.mkOption {
type = lib.types.listOf (lib.types.submodule mountType);
default = [ ];
@@ -98,8 +107,10 @@
assertions = [
{
assertion =
- config.hardware.nvidia.datacenter.enable || lib.elem "nvidia" config.services.xserver.videoDrivers;
- message = ''`nvidia-container-toolkit` requires nvidia datacenter or desktop drivers: set `hardware.nvidia.datacenter.enable` or add "nvidia" to `services.xserver.videoDrivers`'';
+ config.hardware.nvidia.datacenter.enable
+ || lib.elem "nvidia" config.services.xserver.videoDrivers
+ || config.hardware.nvidia-container-toolkit.suppressNvidiaDriverAssertion;
+ message = ''`nvidia-container-toolkit` requires nvidia drivers: set `hardware.nvidia.datacenter.enable`, add "nvidia" to `services.xserver.videoDrivers`, or set `hardware.nvidia-container-toolkit.suppressNvidiaDriverAssertion` if the driver is provided by another NixOS module (e.g. from NixOS-WSL)'';
}
];
diff --git a/nixos/modules/services/hardware/pid-fan-controller.nix b/nixos/modules/services/hardware/pid-fan-controller.nix
new file mode 100644
index 000000000000..a90e515868d4
--- /dev/null
+++ b/nixos/modules/services/hardware/pid-fan-controller.nix
@@ -0,0 +1,188 @@
+{
+ lib,
+ config,
+ pkgs,
+ ...
+}:
+let
+ cfg = config.services.pid-fan-controller;
+ heatSource = {
+ options = {
+ name = lib.mkOption {
+ type = lib.types.uniq lib.types.nonEmptyStr;
+ description = "Name of the heat source.";
+ };
+ wildcardPath = lib.mkOption {
+ type = lib.types.nonEmptyStr;
+ description = ''
+ Path of the heat source's `hwmon` `temp_input` file.
+ This path can contain multiple wildcards, but has to resolve to
+ exactly one result.
+ '';
+ };
+ pidParams = {
+ setPoint = lib.mkOption {
+ type = lib.types.ints.unsigned;
+ description = "Set point of the controller in °C.";
+ };
+ P = lib.mkOption {
+ description = "K_p of PID controller.";
+ type = lib.types.float;
+ };
+ I = lib.mkOption {
+ description = "K_i of PID controller.";
+ type = lib.types.float;
+ };
+ D = lib.mkOption {
+ description = "K_d of PID controller.";
+ type = lib.types.float;
+ };
+ };
+ };
+ };
+
+ fan = {
+ options = {
+ wildcardPath = lib.mkOption {
+ type = lib.types.str;
+ description = ''
+ Wildcard path of the `hwmon` `pwm` file.
+ If the fans are not to be found in `/sys/class/hwmon/hwmon*` the corresponding
+ kernel module (like `nct6775`) needs to be added to `boot.kernelModules`.
+ See the [`hwmon` Documentation](https://www.kernel.org/doc/html/latest/hwmon/index.html).
+ '';
+ };
+ minPwm = lib.mkOption {
+ default = 0;
+ type = lib.types.ints.u8;
+ description = "Minimum PWM value.";
+ };
+ maxPwm = lib.mkOption {
+ default = 255;
+ type = lib.types.ints.u8;
+ description = "Maximum PWM value.";
+ };
+ cutoff = lib.mkOption {
+ default = false;
+ type = lib.types.bool;
+ description = "Whether to stop the fan when `minPwm` is reached.";
+ };
+ heatPressureSrcs = lib.mkOption {
+ type = lib.types.nonEmptyListOf lib.types.str;
+ description = "Heat pressure sources affected by the fan.";
+ };
+ };
+ };
+in
+{
+ options.services.pid-fan-controller = {
+ enable = lib.mkEnableOption "the PID fan controller, which controls the configured fans by running a closed-loop PID control loop";
+ package = lib.mkPackageOption pkgs "pid-fan-controller" { };
+ settings = {
+ interval = lib.mkOption {
+ default = 500;
+ type = lib.types.int;
+ description = "Interval between controller cycles in milliseconds.";
+ };
+ heatSources = lib.mkOption {
+ type = lib.types.listOf (lib.types.submodule heatSource);
+ description = "List of heat sources to be monitored.";
+ example = ''
+ [
+ {
+ name = "cpu";
+ wildcardPath = "/sys/devices/pci0000:00/0000:00:18.3/hwmon/hwmon*/temp1_input";
+ pidParams = {
+ setPoint = 60;
+ P = -5.0e-3;
+ I = -2.0e-3;
+ D = -6.0e-3;
+ };
+ }
+ ];
+ '';
+ };
+ fans = lib.mkOption {
+ type = lib.types.listOf (lib.types.submodule fan);
+ description = "List of fans to be controlled.";
+ example = ''
+ [
+ {
+ wildcardPath = "/sys/devices/platform/nct6775.2592/hwmon/hwmon*/pwm1";
+ minPwm = 60;
+ maxPwm = 255;
+ heatPressureSrcs = [
+ "cpu"
+ "gpu"
+ ];
+ }
+ ];
+ '';
+ };
+ };
+ };
+ config = lib.mkIf cfg.enable {
+ #map camel cased attrs into snake case for config
+ environment.etc."pid-fan-settings.json".text = builtins.toJSON {
+ interval = cfg.settings.interval;
+ heat_srcs = map (heatSrc: {
+ name = heatSrc.name;
+ wildcard_path = heatSrc.wildcardPath;
+ PID_params = {
+ set_point = heatSrc.pidParams.setPoint;
+ P = heatSrc.pidParams.P;
+ I = heatSrc.pidParams.I;
+ D = heatSrc.pidParams.D;
+ };
+ }) cfg.settings.heatSources;
+ fans = map (fan: {
+ wildcard_path = fan.wildcardPath;
+ min_pwm = fan.minPwm;
+ max_pwm = fan.maxPwm;
+ cutoff = fan.cutoff;
+ heat_pressure_srcs = fan.heatPressureSrcs;
+ }) cfg.settings.fans;
+ };
+
+ systemd.services.pid-fan-controller = {
+ wantedBy = [ "multi-user.target" ];
+ serviceConfig = {
+ Type = "simple";
+ ExecStart = [ (lib.getExe cfg.package) ];
+ ExecStopPost = [ "${lib.getExe cfg.package} disable" ];
+ Restart = "always";
+ #This service needs to run as root to write to /sys.
+ #therefore it should operate with the least amount of privileges needed
+ ProtectHome = "yes";
+ #strict is not possible as it needs /sys
+ ProtectSystem = "full";
+ ProtectProc = "invisible";
+ PrivateNetwork = "yes";
+ NoNewPrivileges = "yes";
+ MemoryDenyWriteExecute = "yes";
+ RestrictNamespaces = "~user pid net uts mnt";
+ ProtectKernelModules = "yes";
+ RestrictRealtime = "yes";
+ SystemCallFilter = "@system-service";
+ CapabilityBoundingSet = "~CAP_KILL CAP_WAKE_ALARM CAP_IPC_LOC CAP_BPF CAP_LINUX_IMMUTABLE CAP_BLOCK_SUSPEND CAP_MKNOD";
+ };
+ # restart unit if config changed
+ restartTriggers = [ config.environment.etc."pid-fan-settings.json".source ];
+ };
+ #sleep hook to restart the service as it breaks otherwise
+ systemd.services.pid-fan-controller-sleep = {
+ before = [ "sleep.target" ];
+ wantedBy = [ "sleep.target" ];
+ unitConfig = {
+ StopWhenUnneeded = "yes";
+ };
+ serviceConfig = {
+ Type = "oneshot";
+ RemainAfterExit = true;
+ ExecStart = [ "systemctl stop pid-fan-controller.service" ];
+ ExecStop = [ "systemctl restart pid-fan-controller.service" ];
+ };
+ };
+ };
+ meta.maintainers = with lib.maintainers; [ zimward ];
+}
diff --git a/nixos/modules/services/hardware/spacenavd.nix b/nixos/modules/services/hardware/spacenavd.nix
index db3ab37dc267..ddc3f1dc1b59 100644
--- a/nixos/modules/services/hardware/spacenavd.nix
+++ b/nixos/modules/services/hardware/spacenavd.nix
@@ -17,7 +17,10 @@ in
config = lib.mkIf cfg.enable {
systemd = {
packages = [ pkgs.spacenavd ];
- services.spacenavd.enable = true;
+ services.spacenavd = {
+ enable = true;
+ wantedBy = [ "graphical.target" ];
+ };
};
};
}
diff --git a/nixos/modules/services/hardware/udev.nix b/nixos/modules/services/hardware/udev.nix
index 1284dc86f0a7..0d5a001c0e59 100644
--- a/nixos/modules/services/hardware/udev.nix
+++ b/nixos/modules/services/hardware/udev.nix
@@ -55,6 +55,13 @@ let
preferLocalBuild = true;
allowSubstitutes = false;
packages = lib.unique (map toString udevPackages);
+
+ nativeBuildInputs = [
+ # We only include the out output here to avoid needing to include all
+ # other outputs in the installer tests as well
+ # We only need the udevadm command anyway
+ pkgs.systemdMinimal.out
+ ];
}
''
mkdir -p $out
@@ -147,6 +154,11 @@ let
exit 1
fi
+ # Verify all the udev rules
+ echo "Verifying udev rules using udevadm verify..."
+ udevadm verify --resolve-names=never --no-style $out
+ echo "OK"
+
# If auto-configuration is disabled, then remove
# udev's 80-drivers.rules file, which contains rules for
# automatically calling modprobe.
diff --git a/nixos/modules/services/home-automation/evcc.nix b/nixos/modules/services/home-automation/evcc.nix
index 32d327da9a84..9708a3ce16f1 100644
--- a/nixos/modules/services/home-automation/evcc.nix
+++ b/nixos/modules/services/home-automation/evcc.nix
@@ -113,6 +113,7 @@ in
"AF_INET"
"AF_INET6"
"AF_UNIX"
+ "AF_NETLINK"
];
RestrictNamespaces = true;
RestrictRealtime = true;
diff --git a/nixos/modules/services/home-automation/home-assistant.nix b/nixos/modules/services/home-automation/home-assistant.nix
index eda79e890193..2887e5754173 100644
--- a/nixos/modules/services/home-automation/home-assistant.nix
+++ b/nixos/modules/services/home-automation/home-assistant.nix
@@ -20,6 +20,7 @@ let
filter
filterAttrsRecursive
flatten
+ getAttr
hasAttrByPath
isAttrs
isDerivation
@@ -111,7 +112,9 @@ let
hasAttrByPath (splitString "." component) cfg.config
|| useComponentPlatform component
|| useExplicitComponent component
- || builtins.elem component (cfg.extraComponents ++ cfg.defaultIntegrations);
+ || builtins.elem component (
+ cfg.extraComponents ++ cfg.defaultIntegrations ++ map (getAttr "domain") cfg.customComponents
+ );
# Final list of components passed into the package to include required dependencies
extraComponents = filter useComponent availableComponents;
@@ -842,6 +845,7 @@ in
# Custom components, maintained manually.
"amshan"
+ "benqprojector"
];
in
{
diff --git a/nixos/modules/services/home-automation/wyoming/piper.nix b/nixos/modules/services/home-automation/wyoming/piper.nix
index b4029eb631f2..bf14d4ce85c6 100644
--- a/nixos/modules/services/home-automation/wyoming/piper.nix
+++ b/nixos/modules/services/home-automation/wyoming/piper.nix
@@ -164,7 +164,7 @@ in
DeviceAllow = "";
DevicePolicy = "closed";
LockPersonality = true;
- MemoryDenyWriteExecute = true;
+ MemoryDenyWriteExecute = false; # required for onnxruntime
PrivateDevices = true;
PrivateUsers = true;
ProtectHome = true;
diff --git a/nixos/modules/services/mail/clamsmtp.nix b/nixos/modules/services/mail/clamsmtp.nix
deleted file mode 100644
index 6def179e5e10..000000000000
--- a/nixos/modules/services/mail/clamsmtp.nix
+++ /dev/null
@@ -1,189 +0,0 @@
-{
- config,
- lib,
- pkgs,
- ...
-}:
-let
- cfg = config.services.clamsmtp;
- clamdSocket = "/run/clamav/clamd.ctl"; # See services/security/clamav.nix
-in
-{
- ##### interface
- options = {
- services.clamsmtp = {
- enable = lib.mkOption {
- type = lib.types.bool;
- default = false;
- description = "Whether to enable clamsmtp.";
- };
-
- instances = lib.mkOption {
- description = "Instances of clamsmtp to run.";
- type = lib.types.listOf (
- lib.types.submodule {
- options = {
- action = lib.mkOption {
- type = lib.types.enum [
- "bounce"
- "drop"
- "pass"
- ];
- default = "drop";
- description = ''
- Action to take when a virus is detected.
-
- Note that viruses often spoof sender addresses, so bouncing is
- in most cases not a good idea.
- '';
- };
-
- header = lib.mkOption {
- type = lib.types.str;
- default = "";
- example = "X-Virus-Scanned: ClamAV using ClamSMTP";
- description = ''
- A header to add to scanned messages. See {manpage}`clamsmtpd.conf(5)` for
- more details. Empty means no header.
- '';
- };
-
- keepAlives = lib.mkOption {
- type = lib.types.int;
- default = 0;
- description = ''
- Number of seconds to wait between each NOOP sent to the sending
- server. 0 to disable.
-
- This is meant for slow servers where the sending MTA times out
- waiting for clamd to scan the file.
- '';
- };
-
- listen = lib.mkOption {
- type = lib.types.str;
- example = "127.0.0.1:10025";
- description = ''
- Address to wait for incoming SMTP connections on. See
- {manpage}`clamsmtpd.conf(5)` for more details.
- '';
- };
-
- quarantine = lib.mkOption {
- type = lib.types.bool;
- default = false;
- description = ''
- Whether to quarantine files that contain viruses by leaving them
- in the temporary directory.
- '';
- };
-
- maxConnections = lib.mkOption {
- type = lib.types.int;
- default = 64;
- description = "Maximum number of connections to accept at once.";
- };
-
- outAddress = lib.mkOption {
- type = lib.types.str;
- description = ''
- Address of the SMTP server to send email to once it has been
- scanned.
- '';
- };
-
- tempDirectory = lib.mkOption {
- type = lib.types.str;
- default = "/tmp";
- description = ''
- Temporary directory that needs to be accessible to both clamd
- and clamsmtpd.
- '';
- };
-
- timeout = lib.mkOption {
- type = lib.types.int;
- default = 180;
- description = "Time-out for network connections.";
- };
-
- transparentProxy = lib.mkOption {
- type = lib.types.bool;
- default = false;
- description = "Enable clamsmtp's transparent proxy support.";
- };
-
- virusAction = lib.mkOption {
- type = with lib.types; nullOr path;
- default = null;
- description = ''
- Command to run when a virus is found. Please see VIRUS ACTION in
- {manpage}`clamsmtpd(8)` for a discussion of this option and its safe use.
- '';
- };
-
- xClient = lib.mkOption {
- type = lib.types.bool;
- default = false;
- description = ''
- Send the XCLIENT command to the receiving server, for forwarding
- client addresses and connection information if the receiving
- server supports this feature.
- '';
- };
- };
- }
- );
- };
- };
- };
-
- ##### implementation
- config =
- let
- configfile =
- conf:
- pkgs.writeText "clamsmtpd.conf" ''
- Action: ${conf.action}
- ClamAddress: ${clamdSocket}
- Header: ${conf.header}
- KeepAlives: ${toString conf.keepAlives}
- Listen: ${conf.listen}
- Quarantine: ${if conf.quarantine then "on" else "off"}
- MaxConnections: ${toString conf.maxConnections}
- OutAddress: ${conf.outAddress}
- TempDirectory: ${conf.tempDirectory}
- TimeOut: ${toString conf.timeout}
- TransparentProxy: ${if conf.transparentProxy then "on" else "off"}
- User: clamav
- ${lib.optionalString (conf.virusAction != null) "VirusAction: ${conf.virusAction}"}
- XClient: ${if conf.xClient then "on" else "off"}
- '';
- in
- lib.mkIf cfg.enable {
- assertions = [
- {
- assertion = config.services.clamav.daemon.enable;
- message = "clamsmtp requires clamav to be enabled";
- }
- ];
-
- systemd.services = lib.listToAttrs (
- lib.imap1 (
- i: conf:
- lib.nameValuePair "clamsmtp-${toString i}" {
- description = "ClamSMTP instance ${toString i}";
- wantedBy = [ "multi-user.target" ];
- script = "exec ${pkgs.clamsmtp}/bin/clamsmtpd -f ${configfile conf}";
- after = [ "clamav-daemon.service" ];
- requires = [ "clamav-daemon.service" ];
- serviceConfig.Type = "forking";
- serviceConfig.PrivateTmp = "yes";
- unitConfig.JoinsNamespaceOf = "clamav-daemon.service";
- }
- ) cfg.instances
- );
- };
-
- meta.maintainers = with lib.maintainers; [ ekleog ];
-}
diff --git a/nixos/modules/services/mail/cyrus-imap.nix b/nixos/modules/services/mail/cyrus-imap.nix
index 12d63431cd73..3ffd74451b6c 100644
--- a/nixos/modules/services/mail/cyrus-imap.nix
+++ b/nixos/modules/services/mail/cyrus-imap.nix
@@ -72,6 +72,20 @@ let
} cfg.imapdSettings;
in
{
+ imports = [
+ (lib.mkRenamedOptionModule
+ [ "services" "cyrus-imap" "sslServerCert" ]
+ [ "services" "cyrus-imap" "imapdSettings" "tls_server_cert" ]
+ )
+ (lib.mkRenamedOptionModule
+ [ "services" "cyrus-imap" "sslServerKey" ]
+ [ "services" "cyrus-imap" "imapdSettings" "tls_server_key" ]
+ )
+ (lib.mkRenamedOptionModule
+ [ "services" "cyrus-imap" "sslCACert" ]
+ [ "services" "cyrus-imap" "imapdSettings" "tls_client_ca_file" ]
+ )
+ ];
options.services.cyrus-imap = {
enable = mkEnableOption "Cyrus IMAP, an email, contacts and calendar server";
debug = mkEnableOption "debugging messages for the Cyrus master process";
@@ -294,24 +308,6 @@ in
description = "Path to the configuration file used for Cyrus.";
apply = v: if v != null then v else pkgs.writeText "cyrus.conf" cyrusConfig;
};
-
- sslCACert = mkOption {
- type = nullOr str;
- default = null;
- description = "File path which containing one or more CA certificates to use.";
- };
-
- sslServerCert = mkOption {
- type = nullOr str;
- default = null;
- description = "File containing the global certificate used for all services (IMAP, POP3, LMTP, Sieve)";
- };
-
- sslServerKey = mkOption {
- type = nullOr str;
- default = null;
- description = "File containing the private key belonging to the global server certificate.";
- };
};
config = mkIf cfg.enable {
diff --git a/nixos/modules/services/mail/maddy.nix b/nixos/modules/services/mail/maddy.nix
index 41d25eaf7885..b086f319c136 100644
--- a/nixos/modules/services/mail/maddy.nix
+++ b/nixos/modules/services/mail/maddy.nix
@@ -143,6 +143,8 @@ in
enable = lib.mkEnableOption "Maddy, a free an open source mail server";
+ package = lib.mkPackageOption pkgs "maddy" { };
+
user = lib.mkOption {
default = "maddy";
type = with lib.types; uniq str;
@@ -386,7 +388,7 @@ in
systemd = {
- packages = [ pkgs.maddy ];
+ packages = [ cfg.package ];
services = {
maddy = {
serviceConfig = {
@@ -402,16 +404,16 @@ in
script = ''
${lib.optionalString (cfg.ensureAccounts != [ ]) ''
${lib.concatMapStrings (account: ''
- if ! ${pkgs.maddy}/bin/maddyctl imap-acct list | grep "${account}"; then
- ${pkgs.maddy}/bin/maddyctl imap-acct create ${account}
+ if ! ${cfg.package}/bin/maddyctl imap-acct list | grep "${account}"; then
+ ${cfg.package}/bin/maddyctl imap-acct create ${account}
fi
'') cfg.ensureAccounts}
''}
${lib.optionalString (cfg.ensureCredentials != { }) ''
${lib.concatStringsSep "\n" (
- lib.mapAttrsToList (name: cfg: ''
- if ! ${pkgs.maddy}/bin/maddyctl creds list | grep "${name}"; then
- ${pkgs.maddy}/bin/maddyctl creds create --password $(cat ${lib.escapeShellArg cfg.passwordFile}) ${name}
+ lib.mapAttrsToList (name: credentials: ''
+ if ! ${cfg.package}/bin/maddyctl creds list | grep "${name}"; then
+ ${cfg.package}/bin/maddyctl creds create --password $(cat ${lib.escapeShellArg credentials.passwordFile}) ${name}
fi
'') cfg.ensureCredentials
)}
@@ -486,7 +488,7 @@ in
};
environment.systemPackages = [
- pkgs.maddy
+ cfg.package
];
};
}
diff --git a/nixos/modules/services/mail/mailman.nix b/nixos/modules/services/mail/mailman.nix
index b19637e5784f..d9940cb4d98f 100644
--- a/nixos/modules/services/mail/mailman.nix
+++ b/nixos/modules/services/mail/mailman.nix
@@ -661,8 +661,14 @@ in
mailman-web-setup = {
description = "Prepare mailman-web files and database";
- before = [ "mailman-uwsgi.service" ];
- requiredBy = [ "mailman-uwsgi.service" ];
+ before = [
+ "hyperkitty.service"
+ "mailman-uwsgi.service"
+ ];
+ requiredBy = [
+ "hyperkitty.service"
+ "mailman-uwsgi.service"
+ ];
restartTriggers = [ config.environment.etc."mailman3/settings.py".source ];
script = ''
[[ -e "${webSettings.STATIC_ROOT}" ]] && find "${webSettings.STATIC_ROOT}/" -mindepth 1 -delete
diff --git a/nixos/modules/services/mail/opensmtpd.nix b/nixos/modules/services/mail/opensmtpd.nix
index 2fc628a64aeb..ba5226351a57 100644
--- a/nixos/modules/services/mail/opensmtpd.nix
+++ b/nixos/modules/services/mail/opensmtpd.nix
@@ -76,8 +76,8 @@ in
description = ''
Packages to search for filters, tables, queues, and schedulers.
- Add OpenSMTPD-extras here if you want to use the filters, etc. from
- that package.
+ Add packages here if you want to use them as as such, for example
+ from the opensmtpd-table-* packages.
'';
};
};
@@ -121,25 +121,41 @@ in
}
);
- systemd.tmpfiles.rules = [
- "d /var/spool/smtpd 711 root - - -"
- "d /var/spool/smtpd/offline 770 root smtpq - -"
- "d /var/spool/smtpd/purge 700 smtpq root - -"
- ];
+ systemd.tmpfiles.settings.opensmtpd = {
+ "/var/spool/smtpd".d = {
+ mode = "0711";
+ user = "root";
+ };
+ "/var/spool/smtpd/offline".d = {
+ mode = "0770";
+ user = "root";
+ group = "smtpq";
+ };
+ "/var/spool/smtpd/purge".d = {
+ mode = "0700";
+ user = "smtpq";
+ group = "root";
+ };
+ "/var/spool/smtpd/queue".d = {
+ mode = "0700";
+ user = "smtpq";
+ group = "root";
+ };
+ };
systemd.services.opensmtpd =
let
procEnv = pkgs.buildEnv {
name = "opensmtpd-procs";
paths = [ cfg.package ] ++ cfg.procPackages;
- pathsToLink = [ "/libexec/opensmtpd" ];
+ pathsToLink = [ "/libexec/smtpd" ];
};
in
{
wantedBy = [ "multi-user.target" ];
after = [ "network.target" ];
serviceConfig.ExecStart = "${cfg.package}/sbin/smtpd -d -f ${conf} ${args}";
- environment.OPENSMTPD_PROC_PATH = "${procEnv}/libexec/opensmtpd";
+ environment.OPENSMTPD_PROC_PATH = "${procEnv}/libexec/smtpd";
};
};
}
diff --git a/nixos/modules/services/mail/postsrsd.nix b/nixos/modules/services/mail/postsrsd.nix
index 3f0db2d91c00..bc91edbbe000 100644
--- a/nixos/modules/services/mail/postsrsd.nix
+++ b/nixos/modules/services/mail/postsrsd.nix
@@ -7,16 +7,52 @@
let
cfg = config.services.postsrsd;
+ runtimeDirectoryName = "postsrsd";
+ runtimeDirectory = "/run/${runtimeDirectoryName}";
+ # TODO: follow RFC 42, but we need a libconfuse format first:
+ # https://github.com/NixOS/nixpkgs/issues/401565
+ # Arrays in `libconfuse` look like this: {"Life", "Universe", "Everything"}
+ # See https://www.nongnu.org/confuse/tutorial-html/ar01s03.html.
+ #
+ # Note: We're using `builtins.toJSON` to escape strings, but JSON strings
+ # don't have exactly the same semantics as libconfuse strings. For example,
+ # "${F}" gets treated as an env var reference, see above issue for details.
+ libconfuseDomains = "{ " + lib.concatMapStringsSep ", " builtins.toJSON cfg.domains + " }";
+ configFile = pkgs.writeText "postsrsd.conf" ''
+ secrets-file = "''${CREDENTIALS_DIRECTORY}/secrets-file"
+ domains = ${libconfuseDomains}
+ separator = "${cfg.separator}"
+ socketmap = "unix:${cfg.socketPath}"
+
+ # Disable postsrsd's jailing in favor of confinement with systemd.
+ unprivileged-user = ""
+ chroot-dir = ""
+ '';
in
{
-
- ###### interface
+ imports =
+ map
+ (
+ name:
+ lib.mkRemovedOptionModule [ "services" "postsrsd" name ] ''
+ `postsrsd` was upgraded to `>= 2.0.0`, with some different behaviors and configuration settings:
+ - NixOS Release Notes: https://nixos.org/manual/nixos/unstable/release-notes#sec-nixpkgs-release-25.05-incompatibilities
+ - NixOS Options Reference: https://nixos.org/manual/nixos/unstable/options#opt-services.postsrsd.enable
+ - Migration instructions: https://github.com/roehling/postsrsd/blob/2.0.10/README.rst#migrating-from-version-1x
+ - Postfix Setup: https://github.com/roehling/postsrsd/blob/2.0.10/README.rst#postfix-setup
+ ''
+ )
+ [
+ "domain"
+ "forwardPort"
+ "reversePort"
+ "timeout"
+ "excludeDomains"
+ ];
options = {
-
services.postsrsd = {
-
enable = lib.mkOption {
type = lib.types.bool;
default = false;
@@ -29,9 +65,11 @@ in
description = "Secret keys used for signing and verification";
};
- domain = lib.mkOption {
- type = lib.types.str;
- description = "Domain name for rewrite";
+ domains = lib.mkOption {
+ type = lib.types.listOf lib.types.str;
+ description = "Domain names for rewrite";
+ default = [ config.networking.hostName ];
+ defaultText = lib.literalExpression "[ config.networking.hostName ]";
};
separator = lib.mkOption {
@@ -44,36 +82,6 @@ in
description = "First separator character in generated addresses";
};
- # bindAddress = lib.mkOption { # uncomment once 1.5 is released
- # type = lib.types.str;
- # default = "127.0.0.1";
- # description = "Socket listen address";
- # };
-
- forwardPort = lib.mkOption {
- type = lib.types.int;
- default = 10001;
- description = "Port for the forward SRS lookup";
- };
-
- reversePort = lib.mkOption {
- type = lib.types.int;
- default = 10002;
- description = "Port for the reverse SRS lookup";
- };
-
- timeout = lib.mkOption {
- type = lib.types.int;
- default = 1800;
- description = "Timeout for idle client connections in seconds";
- };
-
- excludeDomains = lib.mkOption {
- type = lib.types.listOf lib.types.str;
- default = [ ];
- description = "Origin domains to exclude from rewriting in addition to primary domain";
- };
-
user = lib.mkOption {
type = lib.types.str;
default = "postsrsd";
@@ -86,16 +94,18 @@ in
description = "Group for the daemon";
};
+ socketPath = lib.mkOption {
+ type = lib.types.path;
+ default = "${runtimeDirectory}/socket";
+ readOnly = true;
+ description = ''
+ Path to the Unix socket for connecting to postsrsd.
+ Read-only, intended for usage when integrating postsrsd into other NixOS config.'';
+ };
};
-
};
- ###### implementation
-
config = lib.mkIf cfg.enable {
-
- services.postsrsd.domain = lib.mkDefault config.networking.hostName;
-
users.users = lib.optionalAttrs (cfg.user == "postsrsd") {
postsrsd = {
group = cfg.group;
@@ -107,30 +117,42 @@ in
postsrsd.gid = config.ids.gids.postsrsd;
};
- systemd.services.postsrsd = {
- description = "PostSRSd SRS rewriting server";
- after = [ "network.target" ];
- before = [ "postfix.service" ];
- wantedBy = [ "multi-user.target" ];
-
+ systemd.services.postsrsd-generate-secrets = {
path = [ pkgs.coreutils ];
-
- serviceConfig = {
- ExecStart = ''${pkgs.postsrsd}/sbin/postsrsd "-s${cfg.secretsFile}" "-d${cfg.domain}" -a${cfg.separator} -f${toString cfg.forwardPort} -r${toString cfg.reversePort} -t${toString cfg.timeout} "-X${lib.concatStringsSep "," cfg.excludeDomains}"'';
- User = cfg.user;
- Group = cfg.group;
- PermissionsStartOnly = true;
- };
-
- preStart = ''
- if [ ! -e "${cfg.secretsFile}" ]; then
+ script = ''
+ if [ -e "${cfg.secretsFile}" ]; then
+ echo "Secrets file exists. Nothing to do!"
+ else
echo "WARNING: secrets file not found, autogenerating!"
DIR="$(dirname "${cfg.secretsFile}")"
install -m 750 -o ${cfg.user} -g ${cfg.group} -d "$DIR"
install -m 600 -o ${cfg.user} -g ${cfg.group} <(dd if=/dev/random bs=18 count=1 | base64) "${cfg.secretsFile}"
fi
'';
+ serviceConfig = {
+ Type = "oneshot";
+ };
};
+ systemd.services.postsrsd = {
+ description = "PostSRSd SRS rewriting server";
+ after = [
+ "network.target"
+ "postsrsd-generate-secrets.service"
+ ];
+ before = [ "postfix.service" ];
+ wantedBy = [ "multi-user.target" ];
+ requires = [ "postsrsd-generate-secrets.service" ];
+ confinement.enable = true;
+
+ serviceConfig = {
+ ExecStart = "${lib.getExe pkgs.postsrsd} -C ${configFile}";
+ User = cfg.user;
+ Group = cfg.group;
+ PermissionsStartOnly = true;
+ RuntimeDirectory = runtimeDirectoryName;
+ LoadCredential = "secrets-file:${cfg.secretsFile}";
+ };
+ };
};
}
diff --git a/nixos/modules/services/matrix/conduwuit.nix b/nixos/modules/services/matrix/conduwuit.nix
deleted file mode 100644
index b4d9fdd1c95e..000000000000
--- a/nixos/modules/services/matrix/conduwuit.nix
+++ /dev/null
@@ -1,265 +0,0 @@
-{
- config,
- lib,
- pkgs,
- ...
-}:
-let
- cfg = config.services.conduwuit;
- defaultUser = "conduwuit";
- defaultGroup = "conduwuit";
-
- format = pkgs.formats.toml { };
- configFile = format.generate "conduwuit.toml" cfg.settings;
-in
-{
- meta.maintainers = with lib.maintainers; [ niklaskorz ];
- options.services.conduwuit = {
- enable = lib.mkEnableOption "conduwuit";
-
- user = lib.mkOption {
- type = lib.types.nonEmptyStr;
- description = ''
- The user {command}`conduwuit` is run as.
- '';
- default = defaultUser;
- };
-
- group = lib.mkOption {
- type = lib.types.nonEmptyStr;
- description = ''
- The group {command}`conduwuit` is run as.
- '';
- default = defaultGroup;
- };
-
- extraEnvironment = lib.mkOption {
- type = lib.types.attrsOf lib.types.str;
- description = "Extra Environment variables to pass to the conduwuit server.";
- default = { };
- example = {
- RUST_BACKTRACE = "yes";
- };
- };
-
- package = lib.mkPackageOption pkgs "conduwuit" { };
-
- settings = lib.mkOption {
- type = lib.types.submodule {
- freeformType = format.type;
- options = {
- global.server_name = lib.mkOption {
- type = lib.types.nonEmptyStr;
- example = "example.com";
- description = "The server_name is the name of this server. It is used as a suffix for user and room ids.";
- };
- global.address = lib.mkOption {
- type = lib.types.nullOr (lib.types.listOf lib.types.nonEmptyStr);
- default = null;
- example = [
- "127.0.0.1"
- "::1"
- ];
- description = ''
- Addresses (IPv4 or IPv6) to listen on for connections by the reverse proxy/tls terminator.
- If set to `null`, conduwuit will listen on IPv4 and IPv6 localhost.
- Must be `null` if `unix_socket_path` is set.
- '';
- };
- global.port = lib.mkOption {
- type = lib.types.listOf lib.types.port;
- default = [ 6167 ];
- description = ''
- The port(s) conduwuit will be running on.
- You need to set up a reverse proxy in your web server (e.g. apache or nginx),
- so all requests to /_matrix on port 443 and 8448 will be forwarded to the conduwuit
- instance running on this port.
- '';
- };
- global.unix_socket_path = lib.mkOption {
- type = lib.types.nullOr lib.types.path;
- default = null;
- description = ''
- Listen on a UNIX socket at the specified path. If listening on a UNIX socket,
- listening on an address will be disabled. The `address` option must be set to
- `null` (the default value). The option {option}`services.conduwuit.group` must
- be set to a group your reverse proxy is part of.
-
- This will automatically add a system user "conduwuit" to your system if
- {option}`services.conduwuit.user` is left at the default, and a "conduwuit"
- group if {option}`services.conduwuit.group` is left at the default.
- '';
- };
- global.unix_socket_perms = lib.mkOption {
- type = lib.types.ints.positive;
- default = 660;
- description = "The default permissions (in octal) to create the UNIX socket with.";
- };
- global.max_request_size = lib.mkOption {
- type = lib.types.ints.positive;
- default = 20000000;
- description = "Max request size in bytes. Don't forget to also change it in the proxy.";
- };
- global.allow_registration = lib.mkOption {
- type = lib.types.bool;
- default = false;
- description = ''
- Whether new users can register on this server.
-
- Registration with token requires `registration_token` or `registration_token_file` to be set.
-
- If set to true without a token configured, and
- `yes_i_am_very_very_sure_i_want_an_open_registration_server_prone_to_abuse`
- is set to true, users can freely register.
- '';
- };
- global.allow_encryption = lib.mkOption {
- type = lib.types.bool;
- default = true;
- description = "Whether new encrypted rooms can be created. Note: existing rooms will continue to work.";
- };
- global.allow_federation = lib.mkOption {
- type = lib.types.bool;
- default = true;
- description = ''
- Whether this server federates with other servers.
- '';
- };
- global.trusted_servers = lib.mkOption {
- type = lib.types.listOf lib.types.nonEmptyStr;
- default = [ "matrix.org" ];
- description = ''
- Servers listed here will be used to gather public keys of other servers
- (notary trusted key servers).
-
- Currently, conduwuit doesn't support inbound batched key requests, so
- this list should only contain other Synapse servers.
-
- Example: `[ "matrix.org" "constellatory.net" "tchncs.de" ]`
- '';
- };
- global.database_path = lib.mkOption {
- readOnly = true;
- type = lib.types.path;
- default = "/var/lib/conduwuit/";
- description = ''
- Path to the conduwuit database, the directory where conduwuit will save its data.
- Note that database_path cannot be edited because of the service's reliance on systemd StateDir.
- '';
- };
- global.allow_check_for_updates = lib.mkOption {
- type = lib.types.bool;
- default = false;
- description = ''
- If enabled, conduwuit will send a simple GET request periodically to
- for any new announcements made.
- Despite the name, this is not an update check endpoint, it is simply an announcement check endpoint.
-
- Disabled by default.
- '';
- };
- };
- };
- default = { };
- # TOML does not allow null values, so we use null to omit those fields
- apply = lib.filterAttrsRecursive (_: v: v != null);
- description = ''
- Generates the conduwuit.toml configuration file. Refer to
-
- for details on supported values.
- '';
- };
- };
-
- config = lib.mkIf cfg.enable {
- assertions = [
- {
- assertion = !(cfg.settings ? global.unix_socket_path) || !(cfg.settings ? global.address);
- message = ''
- In `services.conduwuit.settings.global`, `unix_socket_path` and `address` cannot be set at the
- same time.
- Leave one of the two options unset or explicitly set them to `null`.
- '';
- }
- {
- assertion = cfg.user != defaultUser -> config ? users.users.${cfg.user};
- message = "If `services.conduwuit.user` is changed, the configured user must already exist.";
- }
- {
- assertion = cfg.group != defaultGroup -> config ? users.groups.${cfg.group};
- message = "If `services.conduwuit.group` is changed, the configured group must already exist.";
- }
- ];
-
- users.users = lib.mkIf (cfg.user == defaultUser) {
- ${defaultUser} = {
- group = cfg.group;
- home = cfg.settings.global.database_path;
- isSystemUser = true;
- };
- };
-
- users.groups = lib.mkIf (cfg.group == defaultGroup) {
- ${defaultGroup} = { };
- };
-
- systemd.services.conduwuit = {
- description = "Conduwuit Matrix Server";
- documentation = [ "https://conduwuit.puppyirl.gay/" ];
- wantedBy = [ "multi-user.target" ];
- wants = [ "network-online.target" ];
- after = [ "network-online.target" ];
- environment = lib.mkMerge ([
- { CONDUWUIT_CONFIG = configFile; }
- cfg.extraEnvironment
- ]);
- startLimitBurst = 5;
- startLimitIntervalSec = 60;
- serviceConfig = {
- DynamicUser = true;
- User = cfg.user;
- Group = cfg.group;
-
- DevicePolicy = "closed";
- LockPersonality = true;
- MemoryDenyWriteExecute = true;
- NoNewPrivileges = true;
- ProtectClock = true;
- ProtectControlGroups = true;
- ProtectHome = true;
- ProtectHostname = true;
- ProtectKernelLogs = true;
- ProtectKernelModules = true;
- ProtectKernelTunables = true;
- PrivateDevices = true;
- PrivateMounts = true;
- PrivateTmp = true;
- PrivateUsers = true;
- PrivateIPC = true;
- RemoveIPC = true;
- RestrictAddressFamilies = [
- "AF_INET"
- "AF_INET6"
- "AF_UNIX"
- ];
- RestrictNamespaces = true;
- RestrictRealtime = true;
- SystemCallArchitectures = "native";
- SystemCallFilter = [
- "@system-service @resources"
- "~@clock @debug @module @mount @reboot @swap @cpu-emulation @obsolete @timer @chown @setuid @privileged @keyring @ipc"
- ];
- SystemCallErrorNumber = "EPERM";
-
- StateDirectory = "conduwuit";
- StateDirectoryMode = "0700";
- RuntimeDirectory = "conduwuit";
- RuntimeDirectoryMode = "0750";
-
- ExecStart = lib.getExe cfg.package;
- Restart = "on-failure";
- RestartSec = 10;
- };
- };
- };
-}
diff --git a/nixos/modules/services/matrix/continuwuity.nix b/nixos/modules/services/matrix/continuwuity.nix
new file mode 100644
index 000000000000..5170011d3037
--- /dev/null
+++ b/nixos/modules/services/matrix/continuwuity.nix
@@ -0,0 +1,268 @@
+{
+ config,
+ lib,
+ pkgs,
+ ...
+}:
+let
+ cfg = config.services.matrix-continuwuity;
+ defaultUser = "continuwuity";
+ defaultGroup = "continuwuity";
+
+ format = pkgs.formats.toml { };
+ configFile = format.generate "continuwuity.toml" cfg.settings;
+in
+{
+ meta.maintainers = with lib.maintainers; [
+ nyabinary
+ snaki
+ ];
+ options.services.matrix-continuwuity = {
+ enable = lib.mkEnableOption "continuwuity";
+
+ user = lib.mkOption {
+ type = lib.types.nonEmptyStr;
+ description = ''
+ The user {command}`continuwuity` is run as.
+ '';
+ default = defaultUser;
+ };
+
+ group = lib.mkOption {
+ type = lib.types.nonEmptyStr;
+ description = ''
+ The group {command}`continuwuity` is run as.
+ '';
+ default = defaultGroup;
+ };
+
+ extraEnvironment = lib.mkOption {
+ type = lib.types.attrsOf lib.types.str;
+ description = "Extra Environment variables to pass to the continuwuity server.";
+ default = { };
+ example = {
+ RUST_BACKTRACE = "yes";
+ };
+ };
+
+ package = lib.mkPackageOption pkgs "matrix-continuwuity" { };
+
+ settings = lib.mkOption {
+ type = lib.types.submodule {
+ freeformType = format.type;
+ options = {
+ global.server_name = lib.mkOption {
+ type = lib.types.nonEmptyStr;
+ example = "example.com";
+ description = "The server_name is the name of this server. It is used as a suffix for user and room ids.";
+ };
+ global.address = lib.mkOption {
+ type = lib.types.nullOr (lib.types.listOf lib.types.nonEmptyStr);
+ default = null;
+ example = [
+ "127.0.0.1"
+ "::1"
+ ];
+ description = ''
+ Addresses (IPv4 or IPv6) to listen on for connections by the reverse proxy/tls terminator.
+ If set to `null`, continuwuity will listen on IPv4 and IPv6 localhost.
+ Must be `null` if `unix_socket_path` is set.
+ '';
+ };
+ global.port = lib.mkOption {
+ type = lib.types.listOf lib.types.port;
+ default = [ 6167 ];
+ description = ''
+ The port(s) continuwuity will be running on.
+ You need to set up a reverse proxy in your web server (e.g. apache or nginx),
+ so all requests to /_matrix on port 443 and 8448 will be forwarded to the continuwuity
+ instance running on this port.
+ '';
+ };
+ global.unix_socket_path = lib.mkOption {
+ type = lib.types.nullOr lib.types.path;
+ default = null;
+ description = ''
+ Listen on a UNIX socket at the specified path. If listening on a UNIX socket,
+ listening on an address will be disabled. The `address` option must be set to
+ `null` (the default value). The option {option}`services.continuwuity.group` must
+ be set to a group your reverse proxy is part of.
+
+ This will automatically add a system user "continuwuity" to your system if
+ {option}`services.continuwuity.user` is left at the default, and a "continuwuity"
+ group if {option}`services.continuwuity.group` is left at the default.
+ '';
+ };
+ global.unix_socket_perms = lib.mkOption {
+ type = lib.types.ints.positive;
+ default = 660;
+ description = "The default permissions (in octal) to create the UNIX socket with.";
+ };
+ global.max_request_size = lib.mkOption {
+ type = lib.types.ints.positive;
+ default = 20000000;
+ description = "Max request size in bytes. Don't forget to also change it in the proxy.";
+ };
+ global.allow_registration = lib.mkOption {
+ type = lib.types.bool;
+ default = false;
+ description = ''
+ Whether new users can register on this server.
+
+ Registration with token requires `registration_token` or `registration_token_file` to be set.
+
+ If set to true without a token configured, and
+ `yes_i_am_very_very_sure_i_want_an_open_registration_server_prone_to_abuse`
+ is set to true, users can freely register.
+ '';
+ };
+ global.allow_encryption = lib.mkOption {
+ type = lib.types.bool;
+ default = true;
+ description = "Whether new encrypted rooms can be created. Note: existing rooms will continue to work.";
+ };
+ global.allow_federation = lib.mkOption {
+ type = lib.types.bool;
+ default = true;
+ description = ''
+ Whether this server federates with other servers.
+ '';
+ };
+ global.trusted_servers = lib.mkOption {
+ type = lib.types.listOf lib.types.nonEmptyStr;
+ default = [ "matrix.org" ];
+ description = ''
+ Servers listed here will be used to gather public keys of other servers
+ (notary trusted key servers).
+
+ Currently, continuwuity doesn't support inbound batched key requests, so
+ this list should only contain other Synapse servers.
+
+ Example: `[ "matrix.org" "constellatory.net" "tchncs.de" ]`
+ '';
+ };
+ global.database_path = lib.mkOption {
+ readOnly = true;
+ type = lib.types.path;
+ default = "/var/lib/continuwuity/";
+ description = ''
+ Path to the continuwuity database, the directory where continuwuity will save its data.
+ Note that database_path cannot be edited because of the service's reliance on systemd StateDir.
+ '';
+ };
+ global.allow_announcements_check = lib.mkOption {
+ type = lib.types.bool;
+ default = true;
+ description = ''
+ If enabled, continuwuity will send a simple GET request periodically to
+ for any new announcements made.
+ '';
+ };
+ };
+ };
+ default = { };
+ # TOML does not allow null values, so we use null to omit those fields
+ apply = lib.filterAttrsRecursive (_: v: v != null);
+ description = ''
+ Generates the continuwuity.toml configuration file. Refer to
+
+ for details on supported values.
+ '';
+ };
+ };
+
+ config = lib.mkIf cfg.enable {
+ assertions = [
+ {
+ assertion = !(cfg.settings ? global.unix_socket_path) || !(cfg.settings ? global.address);
+ message = ''
+ In `services.continuwuity.settings.global`, `unix_socket_path` and `address` cannot be set at the
+ same time.
+ Leave one of the two options unset or explicitly set them to `null`.
+ '';
+ }
+ {
+ assertion = cfg.user != defaultUser -> config ? users.users.${cfg.user};
+ message = "If `services.continuwuity.user` is changed, the configured user must already exist.";
+ }
+ {
+ assertion = cfg.group != defaultGroup -> config ? users.groups.${cfg.group};
+ message = "If `services.continuwuity.group` is changed, the configured group must already exist.";
+ }
+ ];
+
+ users.users = lib.mkIf (cfg.user == defaultUser) {
+ ${defaultUser} = {
+ group = cfg.group;
+ home = cfg.settings.global.database_path;
+ isSystemUser = true;
+ };
+ };
+
+ users.groups = lib.mkIf (cfg.group == defaultGroup) {
+ ${defaultGroup} = { };
+ };
+
+ systemd.services.continuwuity = {
+ description = "Continuwuity Matrix Server";
+ documentation = [ "https://continuwuity.org/" ];
+ wantedBy = [ "multi-user.target" ];
+ wants = [ "network-online.target" ];
+ after = [ "network-online.target" ];
+ environment = lib.mkMerge [
+ { CONDUWUIT_CONFIG = configFile; }
+ cfg.extraEnvironment
+ ];
+ startLimitBurst = 5;
+ startLimitIntervalSec = 60;
+ serviceConfig = {
+ DynamicUser = true;
+ User = cfg.user;
+ Group = cfg.group;
+
+ DevicePolicy = "closed";
+ LockPersonality = true;
+ MemoryDenyWriteExecute = true;
+ NoNewPrivileges = true;
+ ProtectClock = true;
+ ProtectControlGroups = true;
+ ProtectHome = true;
+ ProtectHostname = true;
+ ProtectKernelLogs = true;
+ ProtectKernelModules = true;
+ ProtectKernelTunables = true;
+ ProtectProc = "invisible";
+ ProtectSystem = "strict";
+ PrivateDevices = true;
+ PrivateMounts = true;
+ PrivateTmp = true;
+ PrivateUsers = true;
+ PrivateIPC = true;
+ RemoveIPC = true;
+ RestrictAddressFamilies = [
+ "AF_INET"
+ "AF_INET6"
+ "AF_UNIX"
+ ];
+ RestrictNamespaces = true;
+ RestrictRealtime = true;
+ RestrictSUIDSGID = true;
+ SystemCallArchitectures = "native";
+ SystemCallFilter = [
+ "@system-service @resources"
+ "~@clock @debug @module @mount @reboot @swap @cpu-emulation @obsolete @timer @chown @setuid @privileged @keyring @ipc"
+ ];
+ SystemCallErrorNumber = "EPERM";
+
+ StateDirectory = "continuwuity";
+ StateDirectoryMode = "0700";
+ RuntimeDirectory = "continuwuity";
+ RuntimeDirectoryMode = "0750";
+
+ ExecStart = lib.getExe cfg.package;
+ Restart = "on-failure";
+ RestartSec = 10;
+ };
+ };
+ };
+}
diff --git a/nixos/modules/services/matrix/lk-jwt-service.nix b/nixos/modules/services/matrix/lk-jwt-service.nix
new file mode 100644
index 000000000000..2be1fe806d5c
--- /dev/null
+++ b/nixos/modules/services/matrix/lk-jwt-service.nix
@@ -0,0 +1,91 @@
+{
+ config,
+ lib,
+ pkgs,
+ ...
+}:
+let
+ cfg = config.services.lk-jwt-service;
+in
+{
+ meta.maintainers = [ lib.maintainers.quadradical ];
+ options.services.lk-jwt-service = {
+ enable = lib.mkEnableOption "lk-jwt-service";
+ package = lib.mkPackageOption pkgs "lk-jwt-service" { };
+
+ livekitUrl = lib.mkOption {
+ type = lib.types.strMatching "^wss?://.*";
+ example = "wss://example.com/livekit/sfu";
+ description = ''
+ The public websocket URL for livekit.
+ The proto needs to be either `wss://` (recommended) or `ws://` (insecure).
+ '';
+ };
+
+ keyFile = lib.mkOption {
+ type = lib.types.path;
+ description = ''
+ Path to a file containing the credential mapping (`: `) to access LiveKit.
+
+ Example:
+ `lk-jwt-service: f6lQGaHtM5HfgZjIcec3cOCRfiDqIine4CpZZnqdT5cE`
+
+ For more information, see .
+ '';
+ };
+
+ port = lib.mkOption {
+ type = lib.types.port;
+ default = 8080;
+ description = "Port that lk-jwt-service should listen on.";
+ };
+ };
+
+ config = lib.mkIf cfg.enable {
+ systemd.services.lk-jwt-service = {
+ description = "Minimal service to issue LiveKit JWTs for MatrixRTC";
+ documentation = [ "https://github.com/element-hq/lk-jwt-service" ];
+ wantedBy = [ "multi-user.target" ];
+ wants = [ "network-online.target" ];
+ after = [ "network-online.target" ];
+ environment = {
+ LIVEKIT_URL = cfg.livekitUrl;
+ LIVEKIT_JWT_PORT = toString cfg.port;
+ LIVEKIT_KEY_FILE = "/run/credentials/lk-jwt-service.service/livekit-secrets";
+ };
+
+ serviceConfig = {
+ LoadCredential = [ "livekit-secrets:${cfg.keyFile}" ];
+ ExecStart = lib.getExe cfg.package;
+ DynamicUser = true;
+ LockPersonality = true;
+ MemoryDenyWriteExecute = true;
+ ProtectClock = true;
+ ProtectControlGroups = true;
+ ProtectHostname = true;
+ ProtectKernelLogs = true;
+ ProtectKernelModules = true;
+ ProtectKernelTunables = true;
+ PrivateDevices = true;
+ PrivateMounts = true;
+ PrivateUsers = true;
+ RestrictAddressFamilies = [
+ "AF_INET"
+ "AF_INET6"
+ ];
+ RestrictNamespaces = true;
+ RestrictRealtime = true;
+ ProtectHome = true;
+ SystemCallArchitectures = "native";
+ SystemCallFilter = [
+ "@system-service"
+ "~@privileged"
+ "~@resources"
+ ];
+ Restart = "on-failure";
+ RestartSec = 5;
+ UMask = "077";
+ };
+ };
+ };
+}
diff --git a/nixos/modules/services/matrix/mautrix-signal.nix b/nixos/modules/services/matrix/mautrix-signal.nix
index 9977011e0035..c7ef540b4f59 100644
--- a/nixos/modules/services/matrix/mautrix-signal.nix
+++ b/nixos/modules/services/matrix/mautrix-signal.nix
@@ -68,6 +68,8 @@ in
options.services.mautrix-signal = {
enable = lib.mkEnableOption "mautrix-signal, a Matrix-Signal puppeting bridge";
+ package = lib.mkPackageOption pkgs "mautrix-signal" { };
+
settings = lib.mkOption {
apply = lib.recursiveUpdate defaultConfig;
type = settingsFormat.type;
@@ -206,7 +208,7 @@ in
# generate the appservice's registration file if absent
if [ ! -f '${registrationFile}' ]; then
- ${pkgs.mautrix-signal}/bin/mautrix-signal \
+ ${cfg.package}/bin/mautrix-signal \
--generate-registration \
--config='${settingsFile}' \
--registration='${registrationFile}'
@@ -234,7 +236,7 @@ in
StateDirectory = baseNameOf dataDir;
WorkingDirectory = dataDir;
ExecStart = ''
- ${pkgs.mautrix-signal}/bin/mautrix-signal \
+ ${cfg.package}/bin/mautrix-signal \
--config='${settingsFile}' \
--registration='${registrationFile}'
'';
@@ -268,7 +270,7 @@ in
buildDocsInSandbox = false;
doc = ./mautrix-signal.md;
maintainers = with lib.maintainers; [
- niklaskorz
+ alyaeanyx
frederictobiasc
];
};
diff --git a/nixos/modules/services/matrix/mautrix-telegram.nix b/nixos/modules/services/matrix/mautrix-telegram.nix
index f3adbc26ab58..74f18923d790 100644
--- a/nixos/modules/services/matrix/mautrix-telegram.nix
+++ b/nixos/modules/services/matrix/mautrix-telegram.nix
@@ -18,6 +18,8 @@ in
services.mautrix-telegram = {
enable = lib.mkEnableOption "Mautrix-Telegram, a Matrix-Telegram hybrid puppeting/relaybot bridge";
+ package = lib.mkPackageOption pkgs "mautrix-telegram" { };
+
settings = lib.mkOption rec {
apply = lib.recursiveUpdate default;
inherit (settingsFormat) type;
@@ -201,7 +203,7 @@ in
# generate the appservice's registration file if absent
if [ ! -f '${registrationFile}' ]; then
- ${pkgs.mautrix-telegram}/bin/mautrix-telegram \
+ ${cfg.package}/bin/mautrix-telegram \
--generate-registration \
--config='${settingsFile}' \
--registration='${registrationFile}'
@@ -220,9 +222,9 @@ in
umask $old_umask
''
- + lib.optionalString (pkgs.mautrix-telegram ? alembic) ''
+ + lib.optionalString (cfg.package ? alembic) ''
# run automatic database init and migration scripts
- ${pkgs.mautrix-telegram.alembic}/bin/alembic -x config='${settingsFile}' upgrade head
+ ${cfg.package.alembic}/bin/alembic -x config='${settingsFile}' upgrade head
'';
serviceConfig = {
@@ -238,13 +240,13 @@ in
ProtectControlGroups = true;
PrivateTmp = true;
- WorkingDirectory = pkgs.mautrix-telegram; # necessary for the database migration scripts to be found
+ WorkingDirectory = cfg.package; # necessary for the database migration scripts to be found
StateDirectory = baseNameOf dataDir;
UMask = "0027";
EnvironmentFile = cfg.environmentFile;
ExecStart = ''
- ${pkgs.mautrix-telegram}/bin/mautrix-telegram \
+ ${cfg.package}/bin/mautrix-telegram \
--config='${settingsFile}'
'';
};
diff --git a/nixos/modules/services/matrix/mautrix-whatsapp.nix b/nixos/modules/services/matrix/mautrix-whatsapp.nix
index 165e614fec48..d43032f9a31d 100644
--- a/nixos/modules/services/matrix/mautrix-whatsapp.nix
+++ b/nixos/modules/services/matrix/mautrix-whatsapp.nix
@@ -51,6 +51,8 @@ in
options.services.mautrix-whatsapp = {
enable = lib.mkEnableOption "mautrix-whatsapp, a puppeting/relaybot bridge between Matrix and WhatsApp";
+ package = lib.mkPackageOption pkgs "mautrix-whatsapp" { };
+
settings = lib.mkOption {
type = settingsFormat.type;
default = defaultConfig;
@@ -168,7 +170,7 @@ in
# generate the appservice's registration file if absent
if [ ! -f '${registrationFile}' ]; then
- ${pkgs.mautrix-whatsapp}/bin/mautrix-whatsapp \
+ ${cfg.package}/bin/mautrix-whatsapp \
--generate-registration \
--config='${settingsFile}' \
--registration='${registrationFile}'
@@ -196,7 +198,7 @@ in
StateDirectory = baseNameOf dataDir;
WorkingDirectory = dataDir;
ExecStart = ''
- ${pkgs.mautrix-whatsapp}/bin/mautrix-whatsapp \
+ ${cfg.package}/bin/mautrix-whatsapp \
--config='${settingsFile}' \
--registration='${registrationFile}'
'';
diff --git a/nixos/modules/services/matrix/mjolnir.nix b/nixos/modules/services/matrix/mjolnir.nix
index d0160765a357..46007d4e1118 100644
--- a/nixos/modules/services/matrix/mjolnir.nix
+++ b/nixos/modules/services/matrix/mjolnir.nix
@@ -40,7 +40,7 @@ let
# these config files will be merged one after the other to build the final config
configFiles = [
- "${pkgs.mjolnir}/libexec/mjolnir/deps/mjolnir/config/default.yaml"
+ "${pkgs.mjolnir}/lib/node_modules/mjolnir/config/default.yaml"
moduleConfigFile
];
diff --git a/nixos/modules/services/misc/bazarr.nix b/nixos/modules/services/misc/bazarr.nix
index 493e617a808e..bfd708c8bbd4 100644
--- a/nixos/modules/services/misc/bazarr.nix
+++ b/nixos/modules/services/misc/bazarr.nix
@@ -14,6 +14,12 @@ in
package = lib.mkPackageOption pkgs "bazarr" { };
+ dataDir = lib.mkOption {
+ type = lib.types.str;
+ default = "/var/lib/bazarr";
+ description = "The directory where Bazarr stores its data files.";
+ };
+
openFirewall = lib.mkOption {
type = lib.types.bool;
default = false;
@@ -41,20 +47,24 @@ in
};
config = lib.mkIf cfg.enable {
+ systemd.tmpfiles.settings."10-bazarr".${cfg.dataDir}.d = {
+ inherit (cfg) user group;
+ mode = "0700";
+ };
+
systemd.services.bazarr = {
description = "Bazarr";
after = [ "network.target" ];
wantedBy = [ "multi-user.target" ];
- serviceConfig = rec {
+ serviceConfig = {
Type = "simple";
User = cfg.user;
Group = cfg.group;
- StateDirectory = "bazarr";
SyslogIdentifier = "bazarr";
ExecStart = pkgs.writeShellScript "start-bazarr" ''
${cfg.package}/bin/bazarr \
- --config '/var/lib/${StateDirectory}' \
+ --config '${cfg.dataDir}' \
--port ${toString cfg.listenPort} \
--no-update True
'';
@@ -72,7 +82,7 @@ in
bazarr = {
isSystemUser = true;
group = cfg.group;
- home = "/var/lib/${config.systemd.services.bazarr.serviceConfig.StateDirectory}";
+ home = cfg.dataDir;
};
};
diff --git a/nixos/modules/services/misc/devpi-server.nix b/nixos/modules/services/misc/devpi-server.nix
index 309ef8325f9f..16c5fd34ff17 100644
--- a/nixos/modules/services/misc/devpi-server.nix
+++ b/nixos/modules/services/misc/devpi-server.nix
@@ -125,7 +125,7 @@ in
networking.firewall = lib.mkIf cfg.openFirewall {
allowedTCPPorts = [ cfg.port ];
};
-
- meta.maintainers = [ lib.maintainers.cafkafk ];
};
+
+ meta.maintainers = [ lib.maintainers.cafkafk ];
}
diff --git a/nixos/modules/services/misc/dump1090-fa.md b/nixos/modules/services/misc/dump1090-fa.md
new file mode 100644
index 000000000000..835d91e61828
--- /dev/null
+++ b/nixos/modules/services/misc/dump1090-fa.md
@@ -0,0 +1,26 @@
+# Dump1090-fa {#module-services-dump1090-fa}
+
+[dump1090-fa](https://github.com/flightaware/dump1090) is a demodulator and decoder for ADS-B, Mode S, and Mode 3A/3C aircraft transponder messages. It can receive and decode these messages from an attached software-defined radio or from data received over a network connection.
+
+## Configuration {#module-services-dump1090-fa-configuration}
+
+When enabled, this module automatically creates a systemd service to start the `dump1090-fa` application. The application will then write its JSON output files to `/run/dump1090-fa`.
+
+Exposing the integrated web interface is left to the user's configuration. Below is a minimal example demonstrating how to serve it using Nginx:
+
+```nix
+{ pkgs, ... }: {
+ services.dump1090-fa.enable = true;
+
+ services.nginx = {
+ enable = true;
+ virtualHosts."dump1090-fa" = {
+ locations = {
+ "/".alias = "${pkgs.dump1090-fa}/share/dump1090/";
+ "/data/".alias = "/run/dump1090-fa/";
+ };
+ };
+ };
+}
+
+```
diff --git a/nixos/modules/services/misc/dump1090-fa.nix b/nixos/modules/services/misc/dump1090-fa.nix
new file mode 100644
index 000000000000..91f60f2d1d1f
--- /dev/null
+++ b/nixos/modules/services/misc/dump1090-fa.nix
@@ -0,0 +1,135 @@
+{
+ pkgs,
+ config,
+ lib,
+ ...
+}:
+let
+ cfg = config.services.dump1090-fa;
+ inherit (lib) mkOption types;
+in
+{
+ options.services.dump1090-fa = {
+ enable = lib.mkEnableOption "dump1090-fa";
+
+ package = lib.mkPackageOption pkgs "dump1090-fa" { };
+
+ extraArgs = mkOption {
+ type = types.listOf types.str;
+ default = [ ];
+ description = "Additional passed arguments";
+ };
+ };
+
+ config = lib.mkIf cfg.enable {
+ systemd.services.dump1090-fa = {
+ description = "dump1090 ADS-B receiver (FlightAware customization)";
+ after = [ "network.target" ];
+ wantedBy = [ "multi-user.target" ];
+
+ serviceConfig = {
+ ExecStart = lib.escapeShellArgs (
+ [
+ (lib.getExe cfg.package)
+ "--net"
+ "--write-json"
+ "%t/dump1090-fa"
+ ]
+ ++ cfg.extraArgs
+ );
+ DynamicUser = true;
+ SupplementaryGroups = "plugdev";
+ RuntimeDirectory = "dump1090-fa";
+ WorkingDirectory = "%t/dump1090-fa";
+ RuntimeDirectoryMode = 755;
+ PrivateNetwork = true;
+ LockPersonality = true;
+ MemoryDenyWriteExecute = true;
+ NoNewPrivileges = true;
+ PrivateMounts = true;
+ PrivateTmp = true;
+ PrivateUsers = true;
+ ProtectClock = true;
+ ProtectHome = true;
+ ProtectKernelLogs = true;
+ ProtectKernelModules = true;
+ ProtectKernelTunables = true;
+ ProtectProc = "invisible";
+ ProcSubset = "pid";
+ ProtectSystem = "strict";
+ ProtectHostname = true;
+ RestrictSUIDSGID = true;
+ RestrictNamespaces =
+ "~"
+ + (lib.concatStringsSep " " [
+ "cgroup"
+ "ipc"
+ "net"
+ "mnt"
+ "pid"
+ "user"
+ "uts"
+ ]);
+ CapabilityBoundingSet = [
+ "~CAP_AUDIT_CONTROL"
+ "~CAP_AUDIT_READ"
+ "~CAP_AUDIT_WRITE"
+ "~CAP_KILL"
+ "~CAP_MKNOD"
+ "~CAP_NET_BIND_SERVICE"
+ "~CAP_NET_BROADCAST"
+ "~CAP_NET_ADMIN"
+ "~CAP_NET_RAW"
+ "~CAP_SYS_RAWIO"
+ "~CAP_SYS_MODULE"
+ "~CAP_SYS_PTRACE"
+ "~CAP_SYS_TIME"
+ "~CAP_SYS_NICE"
+ "~CAP_SYS_RESOURCE"
+ "~CAP_CHOWN"
+ "~CAP_FSETID"
+ "~CAP_SETUID"
+ "~CAP_SETGID"
+ "~CAP_SETPCAP"
+ "~CAP_SETFCAP"
+ "~CAP_DAC_OVERRIDE"
+ "~CAP_DAC_READ_SEARCH"
+ "~CAP_FOWNER"
+ "~CAP_IPC_OWNER"
+ "~CAP_IPC_LOCK"
+ "~CAP_SYS_BOOT"
+ "~CAP_SYS_ADMIN"
+ "~CAP_MAC_ADMIN"
+ "~CAP_MAC_OVERRIDE"
+ "~CAP_SYS_CHROOT"
+ "~CAP_BLOCK_SUSPEND"
+ "~CAP_WAKE_ALARM"
+ "~CAP_LEASE"
+ "~CAP_SYS_PACCT"
+ ];
+ SystemCallFilter = [
+ "~@clock"
+ "~@debug"
+ "~@module"
+ "~@mount"
+ "~@raw-io"
+ "~@reboot"
+ "~@swap"
+ "~@privileged"
+ "~@resources"
+ "~@cpu-emulation"
+ "~@obsolete"
+ ];
+ RestrictAddressFamilies = [ "~AF_PACKET" ];
+ ProtectControlGroups = true;
+ UMask = "0022";
+ SystemCallArchitectures = "native";
+ };
+ };
+ };
+
+ meta = {
+ maintainers = with lib.maintainers; [ aciceri ];
+ doc = ./dump1090-fa.md;
+ };
+}
diff --git a/nixos/modules/services/misc/evremap.nix b/nixos/modules/services/misc/evremap.nix
index ee433aecbf5f..d02d9726f43a 100644
--- a/nixos/modules/services/misc/evremap.nix
+++ b/nixos/modules/services/misc/evremap.nix
@@ -131,9 +131,9 @@ in
description = "evremap - keyboard input remapper";
wantedBy = [ "multi-user.target" ];
- script = "${lib.getExe pkgs.evremap} remap ${configFile}";
-
serviceConfig = {
+ ExecStart = "${lib.getExe pkgs.evremap} remap ${configFile}";
+
DynamicUser = true;
User = "evremap";
SupplementaryGroups = [
diff --git a/nixos/modules/services/misc/forgejo.md b/nixos/modules/services/misc/forgejo.md
index f234ebf44aef..cca557408639 100644
--- a/nixos/modules/services/misc/forgejo.md
+++ b/nixos/modules/services/misc/forgejo.md
@@ -24,6 +24,42 @@ Both modules and projects are likely to diverge further with each release.
Which might lead to an even more involved migration.
:::
+::: {.warning}
+The last supported version of Forgejo which supports migration from Gitea is
+*10.0.x*. You should *NOT* try to migrate from Gitea to Forgejo `11.x` or
+higher without first migrating to `10.0.x`.
+
+See [upstream migration guide](https://forgejo.org/docs/latest/admin/gitea-migration/)
+
+The last supported version of *Gitea* for this migration process is *1.22*. Do
+*NOT* try to directly migrate from Gitea *1.23* or higher, as it will likely
+result in data loss.
+
+See [upstream news article](https://forgejo.org/2024-12-gitea-compatibility/)
+:::
+
+In order to migrate, the version of Forgejo needs to be pinned to `10.0.x`
+*before* using the latest version. This means that nixpkgs commit
+[`3bb45b041e7147e2fd2daf689e26a1f970a55d65`](https://github.com/NixOS/nixpkgs/commit/3bb45b041e7147e2fd2daf689e26a1f970a55d65)
+or earlier should be used.
+
+To do this, temporarily add the following to your `configuration.nix`:
+
+```nix
+{ pkgs, ... }:
+let
+ nixpkgs-forgejo-10 = import (pkgs.fetchFromGitHub {
+ owner = "NixOS";
+ repo = "nixpkgs";
+ rev = "3bb45b041e7147e2fd2daf689e26a1f970a55d65";
+ hash = "sha256-8JL5NI9eUcGzzbR/ARkrG81WLwndoxqI650mA/4rUGI=";
+ }) {};
+in
+{
+ services.forgejo.package = nixpkgs-forgejo-10.forgejo;
+}
+```
+
### Full-Migration {#module-forgejo-migration-gitea-default}
This will migrate the state directory (data), rename and chown the database and
@@ -49,6 +85,8 @@ chown -R forgejo:forgejo /var/lib/forgejo
systemctl restart forgejo
```
+Afterwards, the Forgejo version can be set back to a newer desired version.
+
### Alternatively, keeping the gitea user {#module-forgejo-migration-gitea-impersonate}
Alternatively, instead of renaming the database, copying the state folder and
diff --git a/nixos/modules/services/misc/forgejo.nix b/nixos/modules/services/misc/forgejo.nix
index 08e1b85a7544..7da42084550d 100644
--- a/nixos/modules/services/misc/forgejo.nix
+++ b/nixos/modules/services/misc/forgejo.nix
@@ -722,7 +722,7 @@ in
'';
serviceConfig = {
- Type = "simple";
+ Type = "notify";
User = cfg.user;
Group = cfg.group;
WorkingDirectory = cfg.stateDir;
diff --git a/nixos/modules/services/misc/gitlab.nix b/nixos/modules/services/misc/gitlab.nix
index e22473058d4b..09dafdda600a 100644
--- a/nixos/modules/services/misc/gitlab.nix
+++ b/nixos/modules/services/misc/gitlab.nix
@@ -907,6 +907,50 @@ in
'';
};
+ secrets.activeRecordPrimaryKeyFile = mkOption {
+ type = with types; nullOr path;
+ default = null;
+ description = ''
+ A file containing the secret used to encrypt some rails data
+ in the DB. This should not be the same as `services.gitlab.secrets.activeRecordDeterministicKeyFile`!
+
+ Make sure the secret is at ideally 32 characters and all random,
+ no regular words or you'll be exposed to dictionary attacks.
+
+ This should be a string, not a nix path, since nix paths are
+ copied into the world-readable nix store.
+ '';
+ };
+
+ secrets.activeRecordDeterministicKeyFile = mkOption {
+ type = with types; nullOr path;
+ default = null;
+ description = ''
+ A file containing the secret used to encrypt some rails data in a deterministic way
+ in the DB. This should not be the same as `services.gitlab.secrets.activeRecordPrimaryKeyFile`!
+
+ Make sure the secret is at ideally 32 characters and all random,
+ no regular words or you'll be exposed to dictionary attacks.
+
+ This should be a string, not a nix path, since nix paths are
+ copied into the world-readable nix store.
+ '';
+ };
+
+ secrets.activeRecordSaltFile = mkOption {
+ type = with types; nullOr path;
+ default = null;
+ description = ''
+ A file containing the salt for active record encryption in the DB.
+
+ Make sure the secret is at ideally 32 characters and all random,
+ no regular words or you'll be exposed to dictionary attacks.
+
+ This should be a string, not a nix path, since nix paths are
+ copied into the world-readable nix store.
+ '';
+ };
+
extraShellConfig = mkOption {
type = types.attrs;
default = { };
@@ -1181,8 +1225,20 @@ in
message = "services.gitlab.secrets.jwsFile must be set!";
}
{
- assertion = versionAtLeast postgresqlPackage.version "14.9";
- message = "PostgreSQL >= 14.9 is required to run GitLab 17. Follow the instructions in the manual section for upgrading PostgreSQL here: https://nixos.org/manual/nixos/stable/index.html#module-services-postgres-upgrading";
+ assertion = cfg.secrets.activeRecordPrimaryKeyFile != null;
+ message = "services.gitlab.secrets.activeRecordPrimaryKeyFile must be set!";
+ }
+ {
+ assertion = cfg.secrets.activeRecordDeterministicKeyFile != null;
+ message = "services.gitlab.secrets.activeRecordDeterministicKeyFile must be set!";
+ }
+ {
+ assertion = cfg.secrets.activeRecordSaltFile != null;
+ message = "services.gitlab.secrets.activeRecordSaltFile must be set!";
+ }
+ {
+ assertion = versionAtLeast postgresqlPackage.version "16";
+ message = "PostgreSQL >= 16 is required to run GitLab 18. Follow the instructions in the manual section for upgrading PostgreSQL here: https://nixos.org/manual/nixos/stable/index.html#module-services-postgres-upgrading";
}
];
@@ -1296,6 +1352,7 @@ in
ConditionPathExists = "!${cfg.registry.certFile}";
};
serviceConfig = {
+ Type = "oneshot";
Slice = "system-gitlab.slice";
};
};
@@ -1479,11 +1536,17 @@ in
db="$(<'${cfg.secrets.dbFile}')"
otp="$(<'${cfg.secrets.otpFile}')"
jws="$(<'${cfg.secrets.jwsFile}')"
- export secret db otp jws
+ arprimary="$(<'${cfg.secrets.activeRecordPrimaryKeyFile}')"
+ ardeterministic="$(<'${cfg.secrets.activeRecordDeterministicKeyFile}')"
+ arsalt="$(<'${cfg.secrets.activeRecordSaltFile}')"
+ export secret db otp jws arprimary ardeterministic arsalt
jq -n '{production: {secret_key_base: $ENV.secret,
otp_key_base: $ENV.otp,
db_key_base: $ENV.db,
- openid_connect_signing_key: $ENV.jws}}' \
+ openid_connect_signing_key: $ENV.jws,
+ active_record_encryption_primary_key: $ENV.arprimary,
+ active_record_encryption_deterministic_key: $ENV.ardeterministic,
+ active_record_encryption_key_derivation_salt: $ENV.arsalt}}' \
> '${cfg.statePath}/config/secrets.yml'
)
diff --git a/nixos/modules/services/misc/gotenberg.nix b/nixos/modules/services/misc/gotenberg.nix
index e92e11b50c71..6d59b905dbea 100644
--- a/nixos/modules/services/misc/gotenberg.nix
+++ b/nixos/modules/services/misc/gotenberg.nix
@@ -16,14 +16,26 @@ let
"--chromium-max-queue-size=${toString cfg.chromium.maxQueueSize}"
"--libreoffice-restart-after=${toString cfg.libreoffice.restartAfter}"
"--libreoffice-max-queue-size=${toString cfg.libreoffice.maxQueueSize}"
- "--pdfengines-engines=${lib.concatStringsSep "," cfg.pdfEngines}"
+ "--pdfengines-merge-engines=${lib.concatStringsSep "," cfg.pdfEngines.merge}"
+ "--pdfengines-convert-engines=${lib.concatStringsSep "," cfg.pdfEngines.convert}"
+ "--pdfengines-read-metadata-engines=${lib.concatStringsSep "," cfg.pdfEngines.readMetadata}"
+ "--pdfengines-write-metadata-engines=${lib.concatStringsSep "," cfg.pdfEngines.writeMetadata}"
+ "--api-download-from-allow-list=${cfg.downloadFrom.allowList}"
+ "--api-download-from-max-retry=${toString cfg.downloadFrom.maxRetries}"
]
++ optional cfg.enableBasicAuth "--api-enable-basic-auth"
++ optional cfg.chromium.autoStart "--chromium-auto-start"
++ optional cfg.chromium.disableJavascript "--chromium-disable-javascript"
++ optional cfg.chromium.disableRoutes "--chromium-disable-routes"
++ optional cfg.libreoffice.autoStart "--libreoffice-auto-start"
- ++ optional cfg.libreoffice.disableRoutes "--libreoffice-disable-routes";
+ ++ optional cfg.libreoffice.disableRoutes "--libreoffice-disable-routes"
+ ++ optional cfg.pdfEngines.disableRoutes "--pdfengines-disable-routes"
+ ++ optional (
+ cfg.downloadFrom.denyList != null
+ ) "--api-download-from-deny-list=${cfg.downloadFrom.denyList}"
+ ++ optional cfg.downloadFrom.disable "--api-disable-download-from"
+ ++ optional (cfg.bodyLimit != null) "--api-body-limit=${cfg.bodyLimit}"
+ ++ lib.optionals (cfg.extraArgs != [ ]) cfg.extraArgs;
inherit (lib)
mkEnableOption
@@ -51,6 +63,12 @@ in
description = "Port on which the API should listen.";
};
+ bindIP = mkOption {
+ type = types.nullOr types.str;
+ default = "127.0.0.1";
+ description = "Port the API listener should bind to. Set to 0.0.0.0 to listen on all available IPs.";
+ };
+
timeout = mkOption {
type = types.nullOr types.str;
default = "30s";
@@ -74,6 +92,12 @@ in
'';
};
+ bodyLimit = mkOption {
+ type = types.nullOr types.str;
+ default = null;
+ description = "Sets the max limit for `multipart/form-data` requests. Accepts values like '5M', '20G', etc.";
+ };
+
extraFontPackages = mkOption {
type = types.listOf types.package;
default = [ ];
@@ -108,6 +132,29 @@ in
};
};
+ downloadFrom = {
+ allowList = mkOption {
+ type = types.nullOr types.str;
+ default = ".*";
+ description = "Allow these URLs to be used in the `downloadFrom` API field. Accepts a regular expression.";
+ };
+ denyList = mkOption {
+ type = types.nullOr types.str;
+ default = null;
+ description = "Deny accepting URLs from these domains in the `downloadFrom` API field. Accepts a regular expression.";
+ };
+ maxRetries = mkOption {
+ type = types.int;
+ default = 4;
+ description = "The maximum amount of times to retry downloading a file specified with `downloadFrom`.";
+ };
+ disable = mkOption {
+ type = types.bool;
+ default = false;
+ description = "Whether to disable the ability to download files for conversion from outside sources.";
+ };
+ };
+
libreoffice = {
package = mkPackageOption pkgs "libreoffice" { };
@@ -136,28 +183,61 @@ in
};
};
- pdfEngines = mkOption {
- type = types.listOf (
- types.enum [
- "pdftk"
+ pdfEngines = {
+ merge = mkOption {
+ type = types.listOf (
+ types.enum [
+ "qpdf"
+ "pdfcpu"
+ "pdftk"
+ ]
+ );
+ default = [
"qpdf"
- "libreoffice-pdfengine"
- "exiftool"
"pdfcpu"
- ]
- );
- default = [
- "pdftk"
- "qpdf"
- "libreoffice-pdfengine"
- "exiftool"
- "pdfcpu"
- ];
- description = ''
- PDF engines to enable. Each one can be used to perform a specific task.
- See [the documentation](https://gotenberg.dev/docs/configuration#pdf-engines) for more details.
- Defaults to all possible PDF engines.
- '';
+ "pdftk"
+ ];
+ description = "PDF Engines to use for merging files.";
+ };
+ convert = mkOption {
+ type = types.listOf (
+ types.enum [
+ "libreoffice-pdfengine"
+ ]
+ );
+ default = [
+ "libreoffice-pdfengine"
+ ];
+ description = "PDF Engines to use for converting files.";
+ };
+ readMetadata = mkOption {
+ type = types.listOf (
+ types.enum [
+ "exiftool"
+ ]
+ );
+ default = [
+ "exiftool"
+ ];
+ description = "PDF Engines to use for reading metadata from files.";
+ };
+ writeMetadata = mkOption {
+ type = types.listOf (
+ types.enum [
+ "exiftool"
+ ]
+ );
+ default = [
+ "exiftool"
+ ];
+ description = "PDF Engines to use for writing metadata to files.";
+ };
+
+ disableRoutes = mkOption {
+ type = types.bool;
+ default = false;
+ description = "Disable routes related to PDF engines.";
+ };
};
logLevel = mkOption {
@@ -196,6 +276,15 @@ in
See `services.gotenberg.enableBasicAuth` for the names of those variables.
'';
}
+ {
+ assertion = !(lib.isList cfg.pdfEngines);
+ message = ''
+ Setting `services.gotenberg.pdfEngines` to a list is now deprecated.
+ Use the new `pdfEngines.mergeEngines`, `pdfEngines.convertEngines`, `pdfEngines.readMetadataEngines`, and `pdfEngines.writeMetadataEngines` settings instead.
+
+ The previous option was using a method that is now deprecated by upstream.
+ '';
+ }
];
systemd.services.gotenberg = {
@@ -209,12 +298,20 @@ in
FONTCONFIG_FILE = pkgs.makeFontsConf {
fontDirectories = [ pkgs.liberation_ttf_v2 ] ++ cfg.extraFontPackages;
};
+ # Needed for LibreOffice to work correctly.
+ # https://github.com/NixOS/nixpkgs/issues/349123#issuecomment-2418330936
+ HOME = "/run/gotenberg";
};
serviceConfig = {
Type = "simple";
DynamicUser = true;
ExecStart = "${lib.getExe cfg.package} ${lib.escapeShellArgs args}";
+ # Needed for LibreOffice to work correctly.
+ # See above issue comment.
+ WorkingDirectory = "/run/gotenberg";
+ RuntimeDirectory = "gotenberg";
+
# Hardening options
PrivateDevices = true;
PrivateIPC = true;
@@ -243,6 +340,7 @@ in
SystemCallFilter = [
"@sandbox"
"@system-service"
+ "@chown"
];
SystemCallArchitectures = "native";
diff --git a/nixos/modules/services/misc/homepage-dashboard.nix b/nixos/modules/services/misc/homepage-dashboard.nix
index 72861fdbbe87..07ccf7112d05 100644
--- a/nixos/modules/services/misc/homepage-dashboard.nix
+++ b/nixos/modules/services/misc/homepage-dashboard.nix
@@ -4,7 +4,6 @@
lib,
...
}:
-
let
cfg = config.services.homepage-dashboard;
# Define the settings format used for this program
@@ -29,6 +28,19 @@ in
description = "Port for Homepage to bind to.";
};
+ allowedHosts = lib.mkOption {
+ type = lib.types.str;
+ default = "localhost:8082,127.0.0.1:8082";
+ example = "example.com";
+ description = ''
+ Hosts that homepage-dashboard will be running under.
+ You will want to change this in order to acess homepage from anything other than localhost.
+ see the upsream documentation:
+
+
+ '';
+ };
+
environmentFile = lib.mkOption {
type = lib.types.str;
description = ''
@@ -215,16 +227,55 @@ in
NIXPKGS_HOMEPAGE_CACHE_DIR = "/var/cache/homepage-dashboard";
PORT = toString cfg.listenPort;
LOG_TARGETS = "stdout";
+ HOMEPAGE_ALLOWED_HOSTS = cfg.allowedHosts;
};
serviceConfig = {
Type = "simple";
- DynamicUser = true;
EnvironmentFile = lib.mkIf (cfg.environmentFile != null) cfg.environmentFile;
StateDirectory = "homepage-dashboard";
CacheDirectory = "homepage-dashboard";
ExecStart = lib.getExe cfg.package;
Restart = "on-failure";
+
+ # hardening
+ DynamicUser = true;
+ DevicePolicy = "closed";
+ CapabilityBoundingSet = "";
+ RestrictAddressFamilies = [
+ "AF_INET"
+ "AF_INET6"
+ "AF_UNIX"
+ "AF_NETLINK"
+ ];
+ DeviceAllow = "";
+ NoNewPrivileges = true;
+ PrivateDevices = true;
+ PrivateMounts = true;
+ PrivateTmp = true;
+ PrivateUsers = true;
+ ProtectClock = true;
+ ProtectControlGroups = true;
+ ProtectHome = true;
+ ProtectKernelLogs = true;
+ ProtectKernelModules = true;
+ ProtectKernelTunables = true;
+ ProtectSystem = "strict";
+ LockPersonality = true;
+ RemoveIPC = true;
+ RestrictNamespaces = true;
+ RestrictRealtime = true;
+ RestrictSUIDSGID = true;
+ SystemCallArchitectures = "native";
+ SystemCallFilter = [
+ "@system-service"
+ "~@resources"
+ ];
+ ProtectProc = "invisible";
+ ProtectHostname = true;
+ UMask = "0077";
+ # cpu widget requires access to /proc
+ ProcSubset = if lib.any (widget: widget.resources.cpu or false) cfg.widgets then "all" else "pid";
};
enableStrictShellChecks = true;
diff --git a/nixos/modules/services/misc/moonraker.nix b/nixos/modules/services/misc/moonraker.nix
index 43d46a690915..9c6067634209 100644
--- a/nixos/modules/services/misc/moonraker.nix
+++ b/nixos/modules/services/misc/moonraker.nix
@@ -224,6 +224,8 @@ in
platform = "linux";
enable_estimator_updates = false;
};
+ # suppress PolicyKit warnings if system control is disabled
+ machine.provider = lib.mkIf (!cfg.allowSystemControl) (lib.mkDefault "none");
};
security.polkit.extraConfig = lib.optionalString cfg.allowSystemControl ''
diff --git a/nixos/modules/services/misc/nix-gc.nix b/nixos/modules/services/misc/nix-gc.nix
index 56d176d81d36..034b044fbafe 100644
--- a/nixos/modules/services/misc/nix-gc.nix
+++ b/nixos/modules/services/misc/nix-gc.nix
@@ -19,8 +19,9 @@ in
};
dates = lib.mkOption {
- type = lib.types.singleLineStr;
- default = "03:15";
+ type = with lib.types; either singleLineStr (listOf str);
+ apply = lib.toList;
+ default = [ "03:15" ];
example = "weekly";
description = ''
How often or when garbage collection is performed. For most desktop and server systems
@@ -86,7 +87,7 @@ in
description = "Nix Garbage Collector";
script = "exec ${config.nix.package.out}/bin/nix-collect-garbage ${cfg.options}";
serviceConfig.Type = "oneshot";
- startAt = lib.optional cfg.automatic cfg.dates;
+ startAt = lib.optionals cfg.automatic cfg.dates;
};
systemd.timers.nix-gc = lib.mkIf cfg.automatic {
diff --git a/nixos/modules/services/misc/nix-optimise.nix b/nixos/modules/services/misc/nix-optimise.nix
index 42f60b77a5b4..3581b95ce18a 100644
--- a/nixos/modules/services/misc/nix-optimise.nix
+++ b/nixos/modules/services/misc/nix-optimise.nix
@@ -15,7 +15,8 @@ in
dates = lib.mkOption {
default = [ "03:45" ];
- type = with lib.types; listOf str;
+ apply = lib.toList;
+ type = with lib.types; either singleLineStr (listOf str);
description = ''
Specification (in the format described by
{manpage}`systemd.time(7)`) of the time at
diff --git a/nixos/modules/services/misc/nix-ssh-serve.nix b/nixos/modules/services/misc/nix-ssh-serve.nix
index b27ef03dae6f..3b09ca0f1be0 100644
--- a/nixos/modules/services/misc/nix-ssh-serve.nix
+++ b/nixos/modules/services/misc/nix-ssh-serve.nix
@@ -26,7 +26,13 @@ in
write = lib.mkOption {
type = lib.types.bool;
default = false;
- description = "Whether to enable writing to the Nix store as a remote store via SSH. Note: the sshServe user is named nix-ssh and is not a trusted-user. nix-ssh should be added to the {option}`nix.settings.trusted-users` option in most use cases, such as allowing remote building of derivations.";
+ description = "Whether to enable writing to the Nix store as a remote store via SSH. Note: by default, the sshServe user is named nix-ssh and is not a trusted-user. nix-ssh should be added to the {option}`nix.sshServe.trusted` option in most use cases, such as allowing remote building of derivations to anonymous people based on ssh key";
+ };
+
+ trusted = lib.mkOption {
+ type = lib.types.bool;
+ default = false;
+ description = "Whether to add nix-ssh to the nix.settings.trusted-users";
};
keys = lib.mkOption {
@@ -59,6 +65,8 @@ in
};
users.groups.nix-ssh = { };
+ nix.settings.trusted-users = lib.mkIf cfg.trusted [ "nix-ssh" ];
+
services.openssh.enable = true;
services.openssh.extraConfig = ''
diff --git a/nixos/modules/services/misc/octoprint.nix b/nixos/modules/services/misc/octoprint.nix
index 193e4222a37e..45c631a36dfd 100644
--- a/nixos/modules/services/misc/octoprint.nix
+++ b/nixos/modules/services/misc/octoprint.nix
@@ -18,9 +18,7 @@ let
cfgUpdate = pkgs.writeText "octoprint-config.yaml" (builtins.toJSON fullConfig);
- pluginsEnv = package.python.withPackages (ps: [ ps.octoprint ] ++ (cfg.plugins ps));
-
- package = pkgs.octoprint;
+ pluginsEnv = cfg.package.python.withPackages (ps: [ ps.octoprint ] ++ (cfg.plugins ps));
in
{
@@ -30,6 +28,8 @@ in
services.octoprint = {
+ package = lib.mkPackageOption pkgs "octoprint" { };
+
enable = lib.mkEnableOption "OctoPrint, web interface for 3D printers";
host = lib.mkOption {
diff --git a/nixos/modules/services/misc/ollama.nix b/nixos/modules/services/misc/ollama.nix
index 9fd876ec5869..d6de23fe4c40 100644
--- a/nixos/modules/services/misc/ollama.nix
+++ b/nixos/modules/services/misc/ollama.nix
@@ -223,6 +223,7 @@ in
"char-nvidia-uvm"
# ROCm
"char-drm"
+ "char-fb"
"char-kfd"
# WSL (Windows Subsystem for Linux)
"/dev/dxg"
diff --git a/nixos/modules/services/misc/paperless.nix b/nixos/modules/services/misc/paperless.nix
index db703ce77883..c66a6df50721 100644
--- a/nixos/modules/services/misc/paperless.nix
+++ b/nixos/modules/services/misc/paperless.nix
@@ -21,7 +21,9 @@ let
PAPERLESS_MEDIA_ROOT = cfg.mediaDir;
PAPERLESS_CONSUMPTION_DIR = cfg.consumptionDir;
PAPERLESS_THUMBNAIL_FONT_NAME = defaultFont;
- GUNICORN_CMD_ARGS = "--bind=${cfg.address}:${toString cfg.port}";
+ GRANIAN_HOST = cfg.address;
+ GRANIAN_PORT = toString cfg.port;
+ GRANIAN_WORKERS_KILL_TIMEOUT = "60";
}
// lib.optionalAttrs (config.time.timeZone != null) {
PAPERLESS_TIME_ZONE = config.time.timeZone;
@@ -196,7 +198,7 @@ in
address = lib.mkOption {
type = lib.types.str;
- default = "localhost";
+ default = "127.0.0.1";
description = "Web interface address.";
};
@@ -357,11 +359,29 @@ in
description = "Settings to pass to the document exporter as CLI arguments.";
};
};
+
+ configureTika = lib.mkOption {
+ type = lib.types.bool;
+ default = false;
+ description = ''
+ Whether to configure Tika and Gotenberg to process Office and e-mail files with OCR.
+ '';
+ };
+
+ manage = lib.mkOption {
+ type = lib.types.package;
+ readOnly = true;
+ description = ''
+ The package derivation for the `paperless-manage` wrapper script.
+ Useful for other modules that need to add this specific script to a service's PATH.
+ '';
+ };
};
config = lib.mkIf cfg.enable (
lib.mkMerge [
{
+ services.paperless.manage = manage;
environment.systemPackages = [ manage ];
services.redis.servers.paperless.enable = lib.mkIf enableRedis true;
@@ -377,12 +397,18 @@ in
];
};
- services.paperless.settings = lib.mkIf cfg.database.createLocally {
- PAPERLESS_DBENGINE = "postgresql";
- PAPERLESS_DBHOST = "/run/postgresql";
- PAPERLESS_DBNAME = "paperless";
- PAPERLESS_DBUSER = "paperless";
- };
+ services.paperless.settings = lib.mkMerge [
+ (lib.mkIf cfg.database.createLocally {
+ PAPERLESS_DBENGINE = "postgresql";
+ PAPERLESS_DBHOST = "/run/postgresql";
+ PAPERLESS_DBNAME = "paperless";
+ PAPERLESS_DBUSER = "paperless";
+ })
+ (lib.mkIf cfg.configureTika {
+ PAPERLESS_GOTENBERG_ENABLED = true;
+ PAPERLESS_TIKA_ENABLED = true;
+ })
+ ];
systemd.slices.system-paperless = {
description = "Paperless Document Management System Slice";
@@ -421,8 +447,7 @@ in
};
environment = env;
- preStart =
- ''
+ preStart = ''
# remove old papaerless-manage symlink
# TODO: drop with NixOS 25.11
[[ -L '${cfg.dataDir}/paperless-manage' ]] && rm '${cfg.dataDir}/paperless-manage'
@@ -448,13 +473,15 @@ in
${cfg.package}/bin/paperless-ngx document_index reindex
fi
- echo ${cfg.package.version} > "$versionFile"
- fi
- ''
- + lib.optionalString (cfg.passwordFile != null) ''
+ echo ${cfg.package.version} > "$versionFile"
+ fi
+
+ if ${lib.boolToString (cfg.passwordFile != null)} || [[ -n $PAPERLESS_ADMIN_PASSWORD ]]; then
export PAPERLESS_ADMIN_USER="''${PAPERLESS_ADMIN_USER:-admin}"
- PAPERLESS_ADMIN_PASSWORD=$(cat "$CREDENTIALS_DIRECTORY/PAPERLESS_ADMIN_PASSWORD")
- export PAPERLESS_ADMIN_PASSWORD
+ if [[ -e $CREDENTIALS_DIRECTORY/PAPERLESS_ADMIN_PASSWORD ]]; then
+ PAPERLESS_ADMIN_PASSWORD=$(cat "$CREDENTIALS_DIRECTORY/PAPERLESS_ADMIN_PASSWORD")
+ export PAPERLESS_ADMIN_PASSWORD
+ fi
superuserState="$PAPERLESS_ADMIN_USER:$PAPERLESS_ADMIN_PASSWORD"
superuserStateFile="${cfg.dataDir}/superuser-state"
@@ -462,7 +489,8 @@ in
${cfg.package}/bin/paperless-ngx manage_superuser
echo "$superuserState" > "$superuserStateFile"
fi
- '';
+ fi
+ '';
requires = lib.optional cfg.database.createLocally "postgresql.service";
after =
lib.optional enableRedis "redis-paperless.service"
@@ -537,16 +565,15 @@ in
echo "PAPERLESS_SECRET_KEY is empty, refusing to start."
exit 1
fi
- exec ${cfg.package.python.pkgs.gunicorn}/bin/gunicorn \
- -c ${cfg.package}/lib/paperless-ngx/gunicorn.conf.py paperless.asgi:application
+ exec ${lib.getExe cfg.package.python.pkgs.granian} --interface asginl --ws "paperless.asgi:application"
'';
serviceConfig = defaultServiceConfig // {
User = cfg.user;
Restart = "on-failure";
LimitNOFILE = 65536;
- # gunicorn needs setuid, liblapack needs mbind
- SystemCallFilter = defaultServiceConfig.SystemCallFilter ++ [ "@setuid mbind" ];
+ # liblapack needs mbind
+ SystemCallFilter = defaultServiceConfig.SystemCallFilter ++ [ "mbind" ];
# Needs to serve web page
PrivateNetwork = false;
};
@@ -569,6 +596,18 @@ in
gid = config.ids.gids.paperless;
};
};
+
+ services.gotenberg = lib.mkIf cfg.configureTika {
+ enable = true;
+ # https://github.com/paperless-ngx/paperless-ngx/blob/v2.15.3/docker/compose/docker-compose.sqlite-tika.yml#L64-L69
+ chromium.disableJavascript = true;
+ extraArgs = [ "--chromium-allow-list=file:///tmp/.*" ];
+ };
+
+ services.tika = lib.mkIf cfg.configureTika {
+ enable = true;
+ enableOcr = true;
+ };
}
(lib.mkIf cfg.exporter.enable {
diff --git a/nixos/modules/services/misc/servarr/prowlarr.nix b/nixos/modules/services/misc/servarr/prowlarr.nix
index dcf5b18005b4..b8d93bd6e8a4 100644
--- a/nixos/modules/services/misc/servarr/prowlarr.nix
+++ b/nixos/modules/services/misc/servarr/prowlarr.nix
@@ -13,6 +13,12 @@ in
services.prowlarr = {
enable = lib.mkEnableOption "Prowlarr, an indexer manager/proxy for Torrent trackers and Usenet indexers";
+ dataDir = lib.mkOption {
+ type = lib.types.str;
+ default = "/var/lib/prowlarr";
+ description = "The directory where Prowlarr stores its data files.";
+ };
+
package = lib.mkPackageOption pkgs "prowlarr" { };
openFirewall = lib.mkOption {
@@ -24,30 +30,63 @@ in
settings = servarr.mkServarrSettingsOptions "prowlarr" 9696;
environmentFiles = servarr.mkServarrEnvironmentFiles "prowlarr";
+
+ user = lib.mkOption {
+ type = lib.types.str;
+ default = "prowlarr";
+ description = ''
+ User account under which Prowlarr runs.
+ '';
+ };
+
+ group = lib.mkOption {
+ type = lib.types.str;
+ default = "prowlarr";
+ description = ''
+ Group under which Prowlarr runs.
+ '';
+ };
};
};
config = lib.mkIf cfg.enable {
- systemd.services.prowlarr = {
- description = "Prowlarr";
- after = [ "network.target" ];
- wantedBy = [ "multi-user.target" ];
- environment = servarr.mkServarrSettingsEnvVars "PROWLARR" cfg.settings // {
- HOME = "/var/empty";
+ systemd = {
+ services.prowlarr = {
+ description = "Prowlarr";
+ after = [ "network.target" ];
+ wantedBy = [ "multi-user.target" ];
+ environment = servarr.mkServarrSettingsEnvVars "PROWLARR" cfg.settings;
+
+ serviceConfig = {
+ Type = "simple";
+ User = cfg.user;
+ Group = cfg.group;
+ EnvironmentFile = cfg.environmentFiles;
+ ExecStart = "${lib.getExe cfg.package} -nobrowser -data='${cfg.dataDir}'";
+ Restart = "on-failure";
+ };
};
- serviceConfig = {
- Type = "simple";
- DynamicUser = true;
- StateDirectory = "prowlarr";
- EnvironmentFile = cfg.environmentFiles;
- ExecStart = "${lib.getExe cfg.package} -nobrowser -data=/var/lib/prowlarr";
- Restart = "on-failure";
+ tmpfiles.settings."10-prowlarr".${cfg.dataDir}.d = {
+ inherit (cfg) user group;
+ mode = "0700";
};
};
networking.firewall = lib.mkIf cfg.openFirewall {
allowedTCPPorts = [ cfg.settings.server.port ];
};
+
+ users.users = lib.mkIf (cfg.user == "prowlarr") {
+ prowlarr = {
+ isSystemUser = true;
+ group = cfg.group;
+ home = cfg.dataDir;
+ };
+ };
+
+ users.groups = lib.mkIf (cfg.group == "prowlarr") {
+ prowlarr = { };
+ };
};
}
diff --git a/nixos/modules/services/misc/signald.nix b/nixos/modules/services/misc/signald.nix
deleted file mode 100644
index 90b184401917..000000000000
--- a/nixos/modules/services/misc/signald.nix
+++ /dev/null
@@ -1,116 +0,0 @@
-{
- config,
- lib,
- pkgs,
- ...
-}:
-let
- cfg = config.services.signald;
- dataDir = "/var/lib/signald";
- defaultUser = "signald";
-in
-{
- options.services.signald = {
- enable = lib.mkEnableOption "signald, the unofficial daemon for interacting with Signal";
-
- user = lib.mkOption {
- type = lib.types.str;
- default = defaultUser;
- description = "User under which signald runs.";
- };
-
- group = lib.mkOption {
- type = lib.types.str;
- default = defaultUser;
- description = "Group under which signald runs.";
- };
-
- socketPath = lib.mkOption {
- type = lib.types.str;
- default = "/run/signald/signald.sock";
- description = "Path to the signald socket";
- };
- };
-
- config = lib.mkIf cfg.enable {
- users.users = lib.optionalAttrs (cfg.user == defaultUser) {
- ${defaultUser} = {
- group = cfg.group;
- isSystemUser = true;
- };
- };
-
- users.groups = lib.optionalAttrs (cfg.group == defaultUser) {
- ${defaultUser} = { };
- };
-
- systemd.services.signald = {
- description = "A daemon for interacting with the Signal Private Messenger";
- wants = [ "network.target" ];
- wantedBy = [ "multi-user.target" ];
- after = [ "network.target" ];
-
- serviceConfig = {
- User = cfg.user;
- Group = cfg.group;
- ExecStart = "${pkgs.signald}/bin/signald -d ${dataDir} -s ${cfg.socketPath}";
- ExecStartPre = "${pkgs.signald}/bin/signald -d ${dataDir} -s ${cfg.socketPath} --migrate-data";
- Restart = "on-failure";
- StateDirectory = "signald";
- RuntimeDirectory = "signald";
- StateDirectoryMode = "0750";
- RuntimeDirectoryMode = "0750";
-
- BindReadOnlyPaths = [
- "/nix/store"
- "-/etc/resolv.conf"
- "-/etc/nsswitch.conf"
- "-/etc/hosts"
- "-/etc/localtime"
- ];
- CapabilityBoundingSet = "";
- # ProtectClock= adds DeviceAllow=char-rtc r
- DeviceAllow = "";
- # Use a static user so other applications can access the files
- #DynamicUser = true;
- LockPersonality = true;
- # Needed for java
- #MemoryDenyWriteExecute = true;
- NoNewPrivileges = true;
- PrivateDevices = true;
- PrivateMounts = true;
- # Needs network access
- #PrivateNetwork = true;
- PrivateTmp = true;
- PrivateUsers = true;
- ProcSubset = "pid";
- ProtectClock = true;
- ProtectHome = true;
- ProtectHostname = true;
- # Would re-mount paths ignored by temporary root
- #ProtectSystem = "strict";
- ProtectControlGroups = true;
- ProtectKernelLogs = true;
- ProtectKernelModules = true;
- ProtectKernelTunables = true;
- ProtectProc = "invisible";
- RestrictAddressFamilies = [
- "AF_INET"
- "AF_INET6"
- "AF_UNIX"
- ];
- RestrictNamespaces = true;
- RestrictRealtime = true;
- RestrictSUIDSGID = true;
- SystemCallArchitectures = "native";
- SystemCallFilter = [
- "@system-service"
- "~@privileged @resources @setuid @keyring"
- ];
- TemporaryFileSystem = "/:ro";
- # Does not work well with the temporary root
- #UMask = "0066";
- };
- };
- };
-}
diff --git a/nixos/modules/services/misc/sourcehut/default.nix b/nixos/modules/services/misc/sourcehut/default.nix
index e9178d153b9e..b6e785802206 100644
--- a/nixos/modules/services/misc/sourcehut/default.nix
+++ b/nixos/modules/services/misc/sourcehut/default.nix
@@ -97,13 +97,14 @@ let
# Those paths are mounted using BindPaths= or BindReadOnlyPaths=
# for services needing access to them.
"builds.sr.ht::worker".buildlogs = "/var/log/sourcehut/buildsrht-worker";
- "git.sr.ht".post-update-script = "/usr/bin/gitsrht-update-hook";
+ "git.sr.ht".post-update-script = "/usr/bin/git.sr.ht-update-hook";
"git.sr.ht".repos = cfg.settings."git.sr.ht".repos;
- "hg.sr.ht".changegroup-script = "/usr/bin/hgsrht-hook-changegroup";
+ "hg.sr.ht".changegroup-script = "/usr/bin/hg.sr.ht-hook-changegroup";
"hg.sr.ht".repos = cfg.settings."hg.sr.ht".repos;
# Making this a per service option despite being in a global section,
# so that it uses the redis-server used by the service.
"sr.ht".redis-host = cfg.${srv}.redis.host;
+ "sr.ht".assets = "${cfg.${srv}.package}/share/sourcehut";
}
)
)
@@ -376,7 +377,7 @@ in
redis = mkOption {
description = "The Redis connection used for the Celery worker.";
type = types.str;
- default = "redis+socket:///run/redis-sourcehut-buildsrht/redis.sock?virtual_host=2";
+ default = "redis+socket:///run/redis-sourcehut-builds.sr.ht/redis.sock?virtual_host=2";
};
shell = mkOption {
description = ''
@@ -436,8 +437,8 @@ in
This setting is propagated to newer and existing repositories.
'';
type = types.path;
- default = "${pkgs.sourcehut.gitsrht}/bin/gitsrht-update-hook";
- defaultText = "\${pkgs.sourcehut.gitsrht}/bin/gitsrht-update-hook";
+ default = "${cfg.git.package}/bin/git.sr.ht-update-hook";
+ defaultText = "\${pkgs.sourcehut.gitsrht}/bin/git.sr.ht-update-hook";
};
repos = mkOption {
description = ''
@@ -446,12 +447,12 @@ in
the gitsrht's user as read and write access to it.
'';
type = types.str;
- default = "/var/lib/sourcehut/gitsrht/repos";
+ default = "/var/lib/sourcehut/git.sr.ht/repos";
};
webhooks = mkOption {
description = "The Redis connection used for the webhooks worker.";
type = types.str;
- default = "redis+socket:///run/redis-sourcehut-gitsrht/redis.sock?virtual_host=1";
+ default = "redis+socket:///run/redis-sourcehut-git.sr.ht/redis.sock?virtual_host=1";
};
};
options."git.sr.ht::api" = {
@@ -477,8 +478,8 @@ in
This setting is propagated to newer and existing repositories.
'';
type = types.str;
- default = "${pkgs.sourcehut.hgsrht}/bin/hgsrht-hook-changegroup";
- defaultText = "\${pkgs.sourcehut.hgsrht}/bin/hgsrht-hook-changegroup";
+ default = "${cfg.hg.package}/bin/hg.sr.ht-hook-changegroup";
+ defaultText = "\${pkgs.sourcehut.hgsrht}/bin/hg.sr.ht-hook-changegroup";
};
repos = mkOption {
description = ''
@@ -487,7 +488,7 @@ in
the hgsrht's user as read and write access to it.
'';
type = types.str;
- default = "/var/lib/sourcehut/hgsrht/repos";
+ default = "/var/lib/sourcehut/hg.sr.ht/repos";
};
srhtext = mkOptionNullOrStr ''
Path to the srht mercurial extension
@@ -507,7 +508,7 @@ in
webhooks = mkOption {
description = "The Redis connection used for the webhooks worker.";
type = types.str;
- default = "redis+socket:///run/redis-sourcehut-hgsrht/redis.sock?virtual_host=1";
+ default = "redis+socket:///run/redis-sourcehut-hg.sr.ht/redis.sock?virtual_host=1";
};
};
@@ -529,12 +530,12 @@ in
redis = mkOption {
description = "The Redis connection used for the Celery worker.";
type = types.str;
- default = "redis+socket:///run/redis-sourcehut-listssrht/redis.sock?virtual_host=2";
+ default = "redis+socket:///run/redis-sourcehut-lists.sr.ht/redis.sock?virtual_host=2";
};
webhooks = mkOption {
description = "The Redis connection used for the webhooks worker.";
type = types.str;
- default = "redis+socket:///run/redis-sourcehut-listssrht/redis.sock?virtual_host=1";
+ default = "redis+socket:///run/redis-sourcehut-lists.sr.ht/redis.sock?virtual_host=1";
};
};
options."lists.sr.ht::worker" = {
@@ -584,7 +585,7 @@ in
webhooks = mkOption {
description = "The Redis connection used for the webhooks worker.";
type = types.str;
- default = "redis+socket:///run/redis-sourcehut-metasrht/redis.sock?virtual_host=1";
+ default = "redis+socket:///run/redis-sourcehut-meta.sr.ht/redis.sock?virtual_host=1";
};
welcome-emails = mkEnableOption "sending stock sourcehut welcome emails after signup";
};
@@ -691,7 +692,7 @@ in
webhooks = mkOption {
description = "The Redis connection used for the webhooks worker.";
type = types.str;
- default = "redis+socket:///run/redis-sourcehut-todosrht/redis.sock?virtual_host=1";
+ default = "redis+socket:///run/redis-sourcehut-todo.sr.ht/redis.sock?virtual_host=1";
};
};
options."todo.sr.ht::mail" = {
@@ -763,7 +764,7 @@ in
};
git = {
- package = mkPackageOption pkgs "git" {
+ gitPackage = mkPackageOption pkgs "git" {
example = "gitFull";
};
fcgiwrap.preforkProcess = mkOption {
@@ -774,7 +775,7 @@ in
};
hg = {
- package = mkPackageOption pkgs "mercurial" { };
+ mercurialPackage = mkPackageOption pkgs "mercurial" { };
cloneBundles = mkOption {
type = types.bool;
default = false;
@@ -806,6 +807,7 @@ in
config = mkIf cfg.enable (mkMerge [
{
+ # TODO: make configurable
environment.systemPackages = [ pkgs.sourcehut.coresrht ];
services.sourcehut.settings = {
@@ -875,14 +877,14 @@ in
set -e
set -x
cd /etc/ssh/sourcehut/subdir
- ${pkgs.sourcehut.gitsrht}/bin/gitsrht-dispatch "$@"
+ ${cfg.git.package}/bin/git.sr.ht-dispatch "$@"
'';
};
systemd.tmpfiles.settings."10-sourcehut-gitsrht" = mkIf cfg.git.enable (mkMerge [
(builtins.listToAttrs (
map
(name: {
- name = "/var/log/sourcehut/gitsrht-${name}";
+ name = "/var/log/sourcehut/git.sr.ht-${name}";
value.f = {
inherit (cfg.git) user group;
mode = "0644";
@@ -903,7 +905,7 @@ in
]);
systemd.services.sshd = {
preStart = mkIf cfg.hg.enable ''
- chown ${cfg.hg.user}:${cfg.hg.group} /var/log/sourcehut/hgsrht-keys
+ chown ${cfg.hg.user}:${cfg.hg.group} /var/log/sourcehut/hg.sr.ht-keys
'';
serviceConfig = {
LogsDirectory = "sourcehut";
@@ -919,62 +921,62 @@ in
"${pkgs.writeShellScript "buildsrht-keys-wrapper" ''
set -e
cd /run/sourcehut/buildsrht/subdir
- exec -a "$0" ${pkgs.sourcehut.buildsrht}/bin/buildsrht-keys "$@"
+ exec -a "$0" ${cfg.builds.package}/bin/builds.sr.ht-keys "$@"
''}:/usr/bin/buildsrht-keys"
- "${pkgs.sourcehut.buildsrht}/bin/master-shell:/usr/bin/master-shell"
- "${pkgs.sourcehut.buildsrht}/bin/runner-shell:/usr/bin/runner-shell"
+ "${cfg.builds.package}/bin/master-shell:/usr/bin/master-shell"
+ "${cfg.builds.package}/bin/runner-shell:/usr/bin/runner-shell"
]
++ optionals cfg.git.enable [
# /path/to/gitsrht-keys calls /path/to/gitsrht-shell,
# or [git.sr.ht] shell= if set.
"${pkgs.writeShellScript "gitsrht-keys-wrapper" ''
set -e
- cd /run/sourcehut/gitsrht/subdir
- exec -a "$0" ${pkgs.sourcehut.gitsrht}/bin/gitsrht-keys "$@"
- ''}:/usr/bin/gitsrht-keys"
+ cd /run/sourcehut/git.sr.ht/subdir
+ exec -a "$0" ${cfg.git.package}/bin/git.sr.ht-keys "$@"
+ ''}:/usr/bin/git.sr.ht-keys"
"${pkgs.writeShellScript "gitsrht-shell-wrapper" ''
set -e
- cd /run/sourcehut/gitsrht/subdir
- export PATH="${cfg.git.package}/bin:$PATH"
- export SRHT_CONFIG=/run/sourcehut/gitsrht/config.ini
- exec -a "$0" ${pkgs.sourcehut.gitsrht}/bin/gitsrht-shell "$@"
- ''}:/usr/bin/gitsrht-shell"
+ cd /run/sourcehut/git.sr.ht/subdir
+ export PATH="${cfg.git.gitPackage}/bin:$PATH"
+ export SRHT_CONFIG=/run/sourcehut/git.sr.ht/config.ini
+ exec -a "$0" ${cfg.git.package}/bin/git.sr.ht-shell "$@"
+ ''}:/usr/bin/git.sr.ht-shell"
"${pkgs.writeShellScript "gitsrht-update-hook" ''
set -e
- export SRHT_CONFIG=/run/sourcehut/gitsrht/config.ini
+ export SRHT_CONFIG=/run/sourcehut/git.sr.ht/config.ini
# hooks/post-update calls /usr/bin/gitsrht-update-hook as hooks/stage-3
# but this wrapper being a bash script, it overrides $0 with /usr/bin/gitsrht-update-hook
# hence this hack to put hooks/stage-3 back into gitsrht-update-hook's $0
if test "''${STAGE3:+set}"
then
- exec -a hooks/stage-3 ${pkgs.sourcehut.gitsrht}/bin/gitsrht-update-hook "$@"
+ exec -a hooks/stage-3 ${cfg.git.package}/bin/git.sr.ht-update-hook "$@"
else
export STAGE3=set
- exec -a "$0" ${pkgs.sourcehut.gitsrht}/bin/gitsrht-update-hook "$@"
+ exec -a "$0" ${cfg.git.package}/bin/git.sr.ht-update-hook "$@"
fi
- ''}:/usr/bin/gitsrht-update-hook"
+ ''}:/usr/bin/git.sr.ht-update-hook"
]
++ optionals cfg.hg.enable [
# /path/to/hgsrht-keys calls /path/to/hgsrht-shell,
# or [hg.sr.ht] shell= if set.
"${pkgs.writeShellScript "hgsrht-keys-wrapper" ''
set -e
- cd /run/sourcehut/hgsrht/subdir
- exec -a "$0" ${pkgs.sourcehut.hgsrht}/bin/hgsrht-keys "$@"
- ''}:/usr/bin/hgsrht-keys"
- "${pkgs.writeShellScript "hgsrht-shell-wrapper" ''
+ cd /run/sourcehut/hg.sr.ht/subdir
+ exec -a "$0" ${cfg.hg.package}/bin/hg.sr.ht-keys "$@"
+ ''}:/usr/bin/hg.sr.ht-keys"
+ "${pkgs.writeShellScript "hg.sr.ht-shell-wrapper" ''
set -e
- cd /run/sourcehut/hgsrht/subdir
- exec -a "$0" ${pkgs.sourcehut.hgsrht}/bin/hgsrht-shell "$@"
- ''}:/usr/bin/hgsrht-shell"
+ cd /run/sourcehut/hg.sr.ht/subdir
+ exec -a "$0" ${cfg.hg.package}/bin/hg.sr.ht-shell "$@"
+ ''}:/usr/bin/hg.sr.ht-shell"
# Mercurial's changegroup hooks are run relative to their repository's directory,
# but hgsrht-hook-changegroup looks up ./config.ini
"${pkgs.writeShellScript "hgsrht-hook-changegroup" ''
set -e
test -e "''$PWD"/config.ini ||
- ln -s /run/sourcehut/hgsrht/config.ini "''$PWD"/config.ini
- exec -a "$0" ${pkgs.sourcehut.hgsrht}/bin/hgsrht-hook-changegroup "$@"
- ''}:/usr/bin/hgsrht-hook-changegroup"
+ ln -s /run/sourcehut/hg.sr.ht/config.ini "''$PWD"/config.ini
+ exec -a "$0" ${cfg.hg.package}/bin/hg.sr.ht-hook-changegroup "$@"
+ ''}:/usr/bin/hg.sr.ht-hook-changegroup"
];
};
};
@@ -985,17 +987,17 @@ in
(import ./service.nix "builds" {
inherit configIniOfService;
- srvsrht = "buildsrht";
+ pkgname = "buildsrht";
port = 5002;
- extraServices.buildsrht-api = {
+ extraServices."build.sr.ht-api" = {
serviceConfig.Restart = "always";
serviceConfig.RestartSec = "5s";
- serviceConfig.ExecStart = "${pkgs.sourcehut.buildsrht}/bin/buildsrht-api -b ${cfg.listenAddress}:${
+ serviceConfig.ExecStart = "${cfg.builds.package}/bin/builds.sr.ht-api -b ${cfg.listenAddress}:${
toString (cfg.builds.port + 100)
}";
};
# TODO: a celery worker on the master and worker are apparently needed
- extraServices.buildsrht-worker =
+ extraServices."build.sr.ht-worker" =
let
qemuPackage = pkgs.qemu_kvm;
serviceName = "buildsrht-worker";
@@ -1024,7 +1026,7 @@ in
fi
'';
serviceConfig = {
- ExecStart = "${pkgs.sourcehut.buildsrht}/bin/buildsrht-worker";
+ ExecStart = "${cfg.builds.package}/bin/builds.sr.ht-worker";
BindPaths = [ cfg.settings."builds.sr.ht::worker".buildlogs ];
LogsDirectory = [ "sourcehut/${serviceName}" ];
RuntimeDirectory = [ "sourcehut/${serviceName}/subdir" ];
@@ -1055,7 +1057,7 @@ in
name = "buildsrht-worker-images-pre";
paths = image_dirs;
# FIXME: not working, apparently because ubuntu/latest is a broken link
- # ++ [ "${pkgs.sourcehut.buildsrht}/lib/images" ];
+ # ++ [ "${cfg.builds.package}/lib/images" ];
};
image_dir = pkgs.runCommand "buildsrht-worker-images" { } ''
mkdir -p $out/images
@@ -1072,7 +1074,7 @@ in
{
# Note that git.sr.ht::dispatch is not a typo,
# gitsrht-dispatch always use this section
- "git.sr.ht::dispatch"."/usr/bin/buildsrht-keys" =
+ "git.sr.ht::dispatch"."/usr/bin/builds.sr.ht-keys" =
mkDefault "${cfg.builds.user}:${cfg.builds.group}";
}
(mkIf cfg.builds.enableWorker {
@@ -1113,8 +1115,10 @@ in
(import ./service.nix "git" (
let
baseService = {
- path = [ cfg.git.package ];
- serviceConfig.BindPaths = [ "${cfg.settings."git.sr.ht".repos}:/var/lib/sourcehut/gitsrht/repos" ];
+ path = [ cfg.git.gitPackage ];
+ serviceConfig.BindPaths = [
+ "${cfg.settings."git.sr.ht".repos}:/var/lib/sourcehut/git.sr.ht/repos"
+ ];
};
in
{
@@ -1123,23 +1127,23 @@ in
baseService
{
serviceConfig.StateDirectory = [
- "sourcehut/gitsrht"
- "sourcehut/gitsrht/repos"
+ "sourcehut/git.sr.ht"
+ "sourcehut/git.sr.ht/repos"
];
preStart = mkIf (versionOlder config.system.stateVersion "22.05") (mkBefore ''
# Fix Git hooks of repositories pre-dating https://github.com/NixOS/nixpkgs/pull/133984
(
set +f
shopt -s nullglob
- for h in /var/lib/sourcehut/gitsrht/repos/~*/*/hooks/{pre-receive,update,post-update}
- do ln -fnsv /usr/bin/gitsrht-update-hook "$h"; done
+ for h in /var/lib/sourcehut/git.sr.ht/repos/~*/*/hooks/{pre-receive,update,post-update}
+ do ln -fnsv /usr/bin/git.sr.ht-update-hook "$h"; done
)
'');
}
];
port = 5001;
webhooks = true;
- extraTimers.gitsrht-periodic = {
+ extraTimers."git.sr.ht-periodic" = {
service = baseService;
timerConfig.OnCalendar = [ "*:0/20" ];
};
@@ -1149,7 +1153,7 @@ in
# Probably could use gitsrht-shell if output is restricted to just parameters...
users.users.${cfg.git.user}.shell = pkgs.bash;
services.sourcehut.settings = {
- "git.sr.ht::dispatch"."/usr/bin/gitsrht-keys" = mkDefault "${cfg.git.user}:${cfg.git.group}";
+ "git.sr.ht::dispatch"."/usr/bin/git.sr.ht-keys" = mkDefault "${cfg.git.user}:${cfg.git.group}";
};
systemd.services.sshd = baseService;
}
@@ -1164,49 +1168,50 @@ in
'';
};
locations."~ ^/([^/]+)/([^/]+)/(HEAD|info/refs|objects/info/.*|git-upload-pack).*$" = {
- root = "/var/lib/sourcehut/gitsrht/repos";
+ root = "/var/lib/sourcehut/git.sr.ht/repos";
fastcgiParams = {
GIT_HTTP_EXPORT_ALL = "";
GIT_PROJECT_ROOT = "$document_root";
PATH_INFO = "$uri";
- SCRIPT_FILENAME = "${cfg.git.package}/bin/git-http-backend";
+ SCRIPT_FILENAME = "${cfg.git.gitPackage}/bin/git-http-backend";
};
extraConfig = ''
auth_request /authorize;
fastcgi_read_timeout 500s;
- fastcgi_pass unix:/run/gitsrht-fcgiwrap.sock;
+ fastcgi_pass unix:/run/git.sr.ht-fcgiwrap.sock;
gzip off;
'';
};
};
- systemd.sockets.gitsrht-fcgiwrap = {
+ systemd.sockets."git.sr.ht-fcgiwrap" = {
before = [ "nginx.service" ];
wantedBy = [
"sockets.target"
- "gitsrht.service"
+ "git.sr.ht.service"
];
# This path remains accessible to nginx.service, which has no RootDirectory=
- socketConfig.ListenStream = "/run/gitsrht-fcgiwrap.sock";
+ socketConfig.ListenStream = "/run/git.sr.ht-fcgiwrap.sock";
socketConfig.SocketUser = nginx.user;
socketConfig.SocketMode = "600";
};
})
];
- extraServices.gitsrht-api.serviceConfig = {
+ extraServices."git.sr.ht-api".serviceConfig = {
Restart = "always";
RestartSec = "5s";
- ExecStart = "${pkgs.sourcehut.gitsrht}/bin/gitsrht-api -b ${cfg.listenAddress}:${toString (cfg.git.port + 100)}";
- BindPaths = [ "${cfg.settings."git.sr.ht".repos}:/var/lib/sourcehut/gitsrht/repos" ];
+ ExecStart = "${cfg.git.package}/bin/git.sr.ht-api -b ${cfg.listenAddress}:${toString (cfg.git.port + 100)}";
+ BindPaths = [ "${cfg.settings."git.sr.ht".repos}:/var/lib/sourcehut/git.sr.ht/repos" ];
};
- extraServices.gitsrht-fcgiwrap = mkIf cfg.nginx.enable {
+ extraServices."git.sr.ht-fcgiwrap" = mkIf cfg.nginx.enable {
serviceConfig = {
# Socket is passed by gitsrht-fcgiwrap.socket
- ExecStart = "${pkgs.fcgiwrap}/sbin/fcgiwrap -c ${toString cfg.git.fcgiwrap.preforkProcess}";
+ ExecStart = "${pkgs.fcgiwrap}/bin/fcgiwrap -c ${toString cfg.git.fcgiwrap.preforkProcess}";
# No need for config.ini
ExecStartPre = mkForce [ ];
- User = null;
- DynamicUser = true;
- BindReadOnlyPaths = [ "${cfg.settings."git.sr.ht".repos}:/var/lib/sourcehut/gitsrht/repos" ];
+ # FIXME: Fails to start with dynamic user
+ # User = null;
+ # DynamicUser = true;
+ BindReadOnlyPaths = [ "${cfg.settings."git.sr.ht".repos}:/var/lib/sourcehut/git.sr.ht/repos" ];
IPAddressDeny = "any";
InaccessiblePaths = [
"-+/run/postgresql"
@@ -1232,8 +1237,8 @@ in
(import ./service.nix "hg" (
let
baseService = {
- path = [ cfg.hg.package ];
- serviceConfig.BindPaths = [ "${cfg.settings."hg.sr.ht".repos}:/var/lib/sourcehut/hgsrht/repos" ];
+ path = [ cfg.hg.mercurialPackage ];
+ serviceConfig.BindPaths = [ "${cfg.settings."hg.sr.ht".repos}:/var/lib/sourcehut/hg.sr.ht/repos" ];
};
in
{
@@ -1242,26 +1247,26 @@ in
baseService
{
serviceConfig.StateDirectory = [
- "sourcehut/hgsrht"
- "sourcehut/hgsrht/repos"
+ "sourcehut/hg.sr.ht"
+ "sourcehut/hg.sr.ht/repos"
];
}
];
port = 5010;
webhooks = true;
- extraTimers.hgsrht-periodic = {
+ extraTimers."hg.sr.ht-periodic" = {
service = baseService;
timerConfig.OnCalendar = [ "*:0/20" ];
};
- extraTimers.hgsrht-clonebundles = mkIf cfg.hg.cloneBundles {
+ extraTimers."hg.sr.ht-clonebundles" = mkIf cfg.hg.cloneBundles {
service = baseService;
timerConfig.OnCalendar = [ "daily" ];
timerConfig.AccuracySec = "1h";
};
- extraServices.hgsrht-api = {
+ extraServices."hg.sr.ht-api" = {
serviceConfig.Restart = "always";
serviceConfig.RestartSec = "5s";
- serviceConfig.ExecStart = "${pkgs.sourcehut.hgsrht}/bin/hgsrht-api -b ${cfg.listenAddress}:${toString (cfg.hg.port + 100)}";
+ serviceConfig.ExecStart = "${cfg.hgsrht.package}/bin/hg.sr.ht-api -b ${cfg.listenAddress}:${toString (cfg.hg.port + 100)}";
};
extraConfig = mkMerge [
{
@@ -1269,7 +1274,7 @@ in
services.sourcehut.settings = {
# Note that git.sr.ht::dispatch is not a typo,
# gitsrht-dispatch always uses this section.
- "git.sr.ht::dispatch"."/usr/bin/hgsrht-keys" = mkDefault "${cfg.hg.user}:${cfg.hg.group}";
+ "git.sr.ht::dispatch"."/usr/bin/hg.sr.ht-keys" = mkDefault "${cfg.hg.user}:${cfg.hg.group}";
};
systemd.services.sshd = baseService;
}
@@ -1290,7 +1295,7 @@ in
# so someone would need to know or guess a SHA value to download anything.
# TODO: proxyPass to an hg serve service?
locations."~ ^/[~^][a-z0-9_]+/[a-zA-Z0-9_.-]+/\\.hg/bundles/.*$" = {
- root = "/var/lib/nginx/hgsrht/repos";
+ root = "/var/lib/nginx/hg.sr.ht/repos";
extraConfig = ''
auth_request /authorize;
gzip off;
@@ -1299,7 +1304,7 @@ in
};
systemd.services.nginx = {
serviceConfig.BindReadOnlyPaths = [
- "${cfg.settings."hg.sr.ht".repos}:/var/lib/nginx/hgsrht/repos"
+ "${cfg.settings."hg.sr.ht".repos}:/var/lib/nginx/hg.sr.ht/repos"
];
};
})
@@ -1330,23 +1335,23 @@ in
inherit configIniOfService;
port = 5006;
webhooks = true;
- extraServices.listssrht-api = {
+ extraServices."lists.sr.ht-api" = {
serviceConfig.Restart = "always";
serviceConfig.RestartSec = "5s";
- serviceConfig.ExecStart = "${pkgs.sourcehut.listssrht}/bin/listssrht-api -b ${cfg.listenAddress}:${
+ serviceConfig.ExecStart = "${cfg.lists.package}/bin/lists.sr.ht-api -b ${cfg.listenAddress}:${
toString (cfg.lists.port + 100)
}";
};
# Receive the mail from Postfix and enqueue them into Redis and PostgreSQL
- extraServices.listssrht-lmtp = {
+ extraServices."lists.sr.ht-lmtp" = {
wants = [ "postfix.service" ];
unitConfig.JoinsNamespaceOf = optional cfg.postfix.enable "postfix.service";
- serviceConfig.ExecStart = "${pkgs.sourcehut.listssrht}/bin/listssrht-lmtp";
+ serviceConfig.ExecStart = "${cfg.lists.package}/bin/lists.sr.ht-lmtp";
# Avoid crashing: os.chown(sock, os.getuid(), sock_gid)
serviceConfig.PrivateUsers = mkForce false;
};
# Dequeue the mails from Redis and dispatch them
- extraServices.listssrht-process = {
+ extraServices."lists.sr.ht-process" = {
serviceConfig = {
preStart = ''
cp ${pkgs.writeText "${srvsrht}-webhooks-celeryconfig.py" cfg.lists.process.celeryConfig} \
@@ -1392,7 +1397,7 @@ in
OnCalendar = [ "daily" ];
AccuracySec = "1h";
};
- extraServices.metasrht-api = {
+ extraServices."meta.sr.ht-api" = {
serviceConfig.Restart = "always";
serviceConfig.RestartSec = "5s";
preStart =
@@ -1414,7 +1419,7 @@ in
) cfg.settings
)
);
- serviceConfig.ExecStart = "${pkgs.sourcehut.metasrht}/bin/metasrht-api -b ${cfg.listenAddress}:${toString (cfg.meta.port + 100)}";
+ serviceConfig.ExecStart = "${cfg.meta.package}/bin/meta.sr.ht-api -b ${cfg.listenAddress}:${toString (cfg.meta.port + 100)}";
};
extraConfig = {
assertions = [
@@ -1428,14 +1433,14 @@ in
}
];
environment.systemPackages = optional cfg.meta.enable (
- pkgs.writeShellScriptBin "metasrht-manageuser" ''
+ pkgs.writeShellScriptBin "meta.sr.ht-manageuser" ''
set -eux
if test "$(${pkgs.coreutils}/bin/id -n -u)" != '${cfg.meta.user}'
then exec sudo -u '${cfg.meta.user}' "$0" "$@"
else
# In order to load config.ini
- if cd /run/sourcehut/metasrht
- then exec ${pkgs.sourcehut.metasrht}/bin/metasrht-manageuser "$@"
+ if cd /run/sourcehut/meta.sr.ht
+ then exec ${cfg.meta.package}/bin/meta.sr.ht-manageuser "$@"
else cat <${stateDir}/db
fi
${optionalString cfg.settings.${iniKey}.migrate-on-upgrade ''
# Just try all the migrations because they're not linked to the version
- for sql in ${pkgs.sourcehut.pagessrht}/share/sql/migrations/*.sql; do
+ for sql in ${package}/share/sql/migrations/*.sql; do
${postgresql.package}/bin/psql '${cfg.settings.${iniKey}.connection-string}' -f "$sql" || true
done
''}
@@ -1482,7 +1488,7 @@ in
touch ${stateDir}/webhook
'';
serviceConfig = {
- ExecStart = mkForce "${pkgs.sourcehut.pagessrht}/bin/pages.sr.ht -b ${cfg.listenAddress}:${toString cfg.pages.port}";
+ ExecStart = mkForce "${cfg.pages.package}/bin/pages.sr.ht -b ${cfg.listenAddress}:${toString cfg.pages.port}";
};
};
})
@@ -1490,10 +1496,10 @@ in
(import ./service.nix "paste" {
inherit configIniOfService;
port = 5011;
- extraServices.pastesrht-api = {
+ extraServices."paste.sr.ht-api" = {
serviceConfig.Restart = "always";
serviceConfig.RestartSec = "5s";
- serviceConfig.ExecStart = "${pkgs.sourcehut.pastesrht}/bin/pastesrht-api -b ${cfg.listenAddress}:${
+ serviceConfig.ExecStart = "${cfg.paste.package}/bin/paste.sr.ht-api -b ${cfg.listenAddress}:${
toString (cfg.paste.port + 100)
}";
};
@@ -1503,15 +1509,15 @@ in
inherit configIniOfService;
port = 5003;
webhooks = true;
- extraServices.todosrht-api = {
+ extraServices."todo.sr.ht-api" = {
serviceConfig.Restart = "always";
serviceConfig.RestartSec = "5s";
- serviceConfig.ExecStart = "${pkgs.sourcehut.todosrht}/bin/todosrht-api -b ${cfg.listenAddress}:${toString (cfg.todo.port + 100)}";
+ serviceConfig.ExecStart = "${cfg.todo.package}/bin/todo.sr.ht-api -b ${cfg.listenAddress}:${toString (cfg.todo.port + 100)}";
};
- extraServices.todosrht-lmtp = {
+ extraServices."todo.sr.ht-lmtp" = {
wants = [ "postfix.service" ];
unitConfig.JoinsNamespaceOf = optional cfg.postfix.enable "postfix.service";
- serviceConfig.ExecStart = "${pkgs.sourcehut.todosrht}/bin/todosrht-lmtp";
+ serviceConfig.ExecStart = "${cfg.todo.package}/bin/todo.sr.ht-lmtp";
# Avoid crashing: os.chown(sock, os.getuid(), sock_gid)
serviceConfig.PrivateUsers = mkForce false;
};
diff --git a/nixos/modules/services/misc/sourcehut/service.nix b/nixos/modules/services/misc/sourcehut/service.nix
index 37040c5e8661..dce07c6d1182 100644
--- a/nixos/modules/services/misc/sourcehut/service.nix
+++ b/nixos/modules/services/misc/sourcehut/service.nix
@@ -1,7 +1,8 @@
srv:
{
configIniOfService,
- srvsrht ? "${srv}srht", # Because "buildsrht" does not follow that pattern (missing an "s").
+ pkgname ? "${srv}srht", # Because "buildsrht" does not follow that pattern (missing an "s").
+ srvsrht ? "${srv}.sr.ht",
iniKey ? "${srv}.sr.ht",
webhooks ? false,
extraTimers ? { },
@@ -28,7 +29,7 @@ let
mkIf
mkMerge
;
- inherit (lib.options) mkEnableOption mkOption;
+ inherit (lib.options) mkEnableOption mkOption mkPackageOption;
inherit (lib.strings) concatStringsSep hasSuffix optionalString;
inherit (config.services) postgresql;
redis = config.services.redis.servers."sourcehut-${srvsrht}";
@@ -162,6 +163,8 @@ in
{
enable = mkEnableOption "${srv} service";
+ package = mkPackageOption pkgs [ "sourcehut" pkgname ] { };
+
user = mkOption {
type = types.str;
default = srvsrht;
@@ -276,7 +279,7 @@ in
forceSSL = mkDefault true;
locations."/".proxyPass = "http://${cfg.listenAddress}:${toString srvCfg.port}";
locations."/static" = {
- root = "${pkgs.sourcehut.${srvsrht}}/${pkgs.sourcehut.python.sitePackages}/${srvsrht}";
+ root = "${srvCfg.package}/${pkgs.sourcehut.python.sitePackages}/${srvsrht}";
extraConfig = mkDefault ''
expires 30d;
'';
@@ -367,12 +370,12 @@ in
StateDirectory = [ "sourcehut/${srvsrht}" ];
StateDirectoryMode = "2750";
ExecStart =
- "${cfg.python}/bin/gunicorn ${srvsrht}.app:app --name ${srvsrht} --bind ${cfg.listenAddress}:${toString srvCfg.port} "
+ "${cfg.python}/bin/gunicorn ${pkgname}.app:app --name ${srvsrht} --bind ${cfg.listenAddress}:${toString srvCfg.port} "
+ concatStringsSep " " srvCfg.gunicorn.extraArgs;
};
preStart =
let
- package = pkgs.sourcehut.${srvsrht};
+ package = srvCfg.package;
version = package.version;
stateDir = "/var/lib/sourcehut/${srvsrht}";
in
@@ -385,7 +388,7 @@ in
if test ! -e ${stateDir}/db; then
# Setup the initial database.
# Note that it stamps the alembic head afterward
- ${package}/bin/${srvsrht}-initdb
+ ${postgresql.package}/bin/psql -d ${srvsrht} -f ${package}/share/sourcehut/${srvsrht}-schema.sql
echo ${version} >${stateDir}/db
fi
@@ -401,7 +404,7 @@ in
# See https://lists.sr.ht/~sircmpwn/sr.ht-admins/<20190302181207.GA13778%40cirno.my.domain>
if test ! -e ${stateDir}/webhook; then
# Update ${iniKey}'s users' profile copy to the latest
- ${cfg.python}/bin/srht-update-profiles ${iniKey}
+ ${cfg.python}/bin/sr.ht-update-profiles ${iniKey}
touch ${stateDir}/webhook
fi
'';
@@ -424,7 +427,7 @@ in
Type = "simple";
Restart = "always";
ExecStart =
- "${cfg.python}/bin/celery --app ${srvsrht}.webhooks worker --hostname ${srvsrht}-webhooks@%%h "
+ "${cfg.python}/bin/celery --app ${pkgname}.webhooks worker --hostname ${srvsrht}-webhooks@%%h "
+ concatStringsSep " " srvCfg.webhooks.extraArgs;
# Avoid crashing: os.getloadavg()
ProcSubset = mkForce "all";
@@ -443,7 +446,7 @@ in
];
serviceConfig = {
Type = "oneshot";
- ExecStart = "${pkgs.sourcehut.${srvsrht}}/bin/${timerName}";
+ ExecStart = "${srvCfg.package}/bin/${timerName}";
};
}
(timer.service or { })
diff --git a/nixos/modules/services/misc/taskchampion-sync-server.nix b/nixos/modules/services/misc/taskchampion-sync-server.nix
index bab826829063..272cea170a4f 100644
--- a/nixos/modules/services/misc/taskchampion-sync-server.nix
+++ b/nixos/modules/services/misc/taskchampion-sync-server.nix
@@ -22,6 +22,12 @@ in
type = types.str;
default = "taskchampion";
};
+ host = lib.mkOption {
+ description = "Host address on which to serve";
+ type = types.str;
+ default = "127.0.0.1";
+ example = "0.0.0.0";
+ };
port = lib.mkOption {
description = "Port on which to serve";
type = types.port;
@@ -79,7 +85,7 @@ in
DynamicUser = false;
ExecStart = ''
${lib.getExe cfg.package} \
- --port ${builtins.toString cfg.port} \
+ --listen "${cfg.host}:${builtins.toString cfg.port}" \
--data-dir ${cfg.dataDir} \
--snapshot-versions ${builtins.toString cfg.snapshot.versions} \
--snapshot-days ${builtins.toString cfg.snapshot.days} \
diff --git a/nixos/modules/services/misc/tp-auto-kbbl.nix b/nixos/modules/services/misc/tp-auto-kbbl.nix
index 6db22e5aa840..2af0521b848a 100644
--- a/nixos/modules/services/misc/tp-auto-kbbl.nix
+++ b/nixos/modules/services/misc/tp-auto-kbbl.nix
@@ -37,6 +37,8 @@ in
config = lib.mkIf cfg.enable {
environment.systemPackages = [ cfg.package ];
+ services.upower.enable = true;
+
systemd.services.tp-auto-kbbl = {
serviceConfig = {
ExecStart = lib.concatStringsSep " " (
diff --git a/nixos/modules/services/misc/tzupdate.nix b/nixos/modules/services/misc/tzupdate.nix
index 18fee37ddd1b..044b1dc44777 100644
--- a/nixos/modules/services/misc/tzupdate.nix
+++ b/nixos/modules/services/misc/tzupdate.nix
@@ -18,6 +18,25 @@ in
update the timezone.
'';
};
+
+ package = lib.mkPackageOption pkgs "tzupdate" { };
+
+ timer.enable = lib.mkOption {
+ type = lib.types.bool;
+ default = true;
+ description = ''
+ Enable the tzupdate timer to update the timezone automatically.
+ '';
+ };
+
+ timer.interval = lib.mkOption {
+ type = lib.types.str;
+ default = "hourly";
+ description = ''
+ The interval at which the tzupdate timer should run. See
+ {manpage}`systemd.time(7)` to understand the format.
+ '';
+ };
};
config = lib.mkIf cfg.enable {
@@ -26,15 +45,16 @@ in
# zone, which is better than silently overriding it.
time.timeZone = null;
- # We provide a one-shot service which can be manually run. We could
- # provide a service that runs on startup, but it's tricky to get
- # a service to run after you have *internet* access.
+ # We provide a one-shot service that runs at startup once network
+ # interfaces are up, but we can’t ensure we actually have Internet access
+ # at that point. It can also be run manually with `systemctl start tzupdate`.
systemd.services.tzupdate = {
description = "tzupdate timezone update service";
+ wantedBy = [ "multi-user.target" ];
wants = [ "network-online.target" ];
after = [ "network-online.target" ];
script = ''
- timezone="$(${lib.getExe pkgs.tzupdate} --print-only)"
+ timezone="$(${lib.getExe cfg.package} --print-only)"
if [[ -n "$timezone" ]]; then
echo "Setting timezone to '$timezone'"
timedatectl set-timezone "$timezone"
@@ -45,6 +65,16 @@ in
Type = "oneshot";
};
};
+
+ systemd.timers.tzupdate = {
+ enable = cfg.timer.enable;
+ timerConfig = {
+ OnStartupSec = "30s";
+ OnCalendar = cfg.timer.interval;
+ Persistent = true;
+ };
+ wantedBy = [ "timers.target" ];
+ };
};
meta.maintainers = with lib.maintainers; [ doronbehar ];
diff --git a/nixos/modules/services/misc/yarr.nix b/nixos/modules/services/misc/yarr.nix
new file mode 100644
index 000000000000..62cc54b9de19
--- /dev/null
+++ b/nixos/modules/services/misc/yarr.nix
@@ -0,0 +1,118 @@
+{
+ config,
+ lib,
+ pkgs,
+ ...
+}:
+
+let
+ inherit (lib)
+ types
+ mkIf
+ mkOption
+ mkEnableOption
+ mkPackageOption
+ optionalString
+ ;
+
+ cfg = config.services.yarr;
+in
+{
+ meta.maintainers = with lib.maintainers; [ christoph-heiss ];
+
+ options.services.yarr = {
+ enable = mkEnableOption "Yet another rss reader";
+
+ package = mkPackageOption pkgs "yarr" { };
+
+ environmentFile = mkOption {
+ type = types.nullOr types.path;
+ default = null;
+ description = ''
+ Environment file for specifying additional settings such as secrets.
+
+ See `yarr -help` for all available options.
+ '';
+ };
+
+ address = mkOption {
+ type = types.str;
+ default = "localhost";
+ description = "Address to run server on.";
+ };
+
+ port = mkOption {
+ type = types.port;
+ default = 7070;
+ description = "Port to run server on.";
+ };
+
+ baseUrl = mkOption {
+ type = types.nullOr types.str;
+ default = null;
+ description = "Base path of the service url.";
+ };
+
+ authFilePath = mkOption {
+ type = types.nullOr types.path;
+ default = null;
+ description = "Path to a file containing username:password. `null` means no authentication required to use the service.";
+ };
+ };
+
+ config = mkIf cfg.enable {
+ systemd.services.yarr = {
+ description = "Yet another rss reader";
+ after = [ "network-online.target" ];
+ wants = [ "network-online.target" ];
+ wantedBy = [ "multi-user.target" ];
+
+ environment.XDG_CONFIG_HOME = "/var/lib/yarr/.config";
+
+ serviceConfig = {
+ Type = "simple";
+ Restart = "on-failure";
+
+ StateDirectory = "yarr";
+ StateDirectoryMode = "0700";
+ WorkingDirectory = "/var/lib/yarr";
+ EnvironmentFile = cfg.environmentFile;
+
+ LoadCredential = mkIf (cfg.authFilePath != null) "authfile:${cfg.authFilePath}";
+
+ DynamicUser = true;
+ DevicePolicy = "closed";
+ LockPersonality = "yes";
+ MemoryDenyWriteExecute = true;
+ NoNewPrivileges = true;
+ PrivateDevices = true;
+ PrivateMounts = true;
+ PrivateTmp = true;
+ ProcSubset = "pid";
+ ProtectClock = true;
+ ProtectControlGroups = true;
+ ProtectHome = true;
+ ProtectHostname = true;
+ ProtectKernelLogs = true;
+ ProtectKernelModules = true;
+ ProtectKernelTunables = true;
+ ProtectProc = "invisible";
+ ProtectSystem = "strict";
+ RemoveIPC = true;
+ RestrictAddressFamilies = "AF_INET AF_INET6";
+ RestrictNamespaces = true;
+ RestrictRealtime = true;
+ RestrictSUIDSGID = true;
+ UMask = "0077";
+
+ ExecStart = ''
+ ${lib.getExe cfg.package} \
+ -db storage.db \
+ -addr "${cfg.address}:${toString cfg.port}" \
+ ${optionalString (cfg.baseUrl != null) "-base ${cfg.baseUrl}"} \
+ ${optionalString (cfg.authFilePath != null) "-auth-file /run/credentials/yarr.service/authfile"}
+ '';
+ };
+ };
+ };
+}
diff --git a/nixos/modules/services/monitoring/alloy.nix b/nixos/modules/services/monitoring/alloy.nix
index 73f967addc91..fe0ed2cab8b3 100644
--- a/nixos/modules/services/monitoring/alloy.nix
+++ b/nixos/modules/services/monitoring/alloy.nix
@@ -65,6 +65,7 @@ in
config = lib.mkIf cfg.enable {
systemd.services.alloy = {
+ after = [ "network.target" ];
wantedBy = [ "multi-user.target" ];
reloadTriggers = lib.mapAttrsToList (_: v: v.source or null) (
lib.filterAttrs (n: _: lib.hasPrefix "alloy/" n && lib.hasSuffix ".alloy" n) config.environment.etc
diff --git a/nixos/modules/services/monitoring/cockpit.nix b/nixos/modules/services/monitoring/cockpit.nix
index 7b5e05bfb55c..7f2f5a6ced1b 100644
--- a/nixos/modules/services/monitoring/cockpit.nix
+++ b/nixos/modules/services/monitoring/cockpit.nix
@@ -12,7 +12,6 @@ let
mkEnableOption
mkOption
mkIf
- literalMD
mkPackageOption
;
settingsFormat = pkgs.formats.ini { };
@@ -26,6 +25,18 @@ in
default = [ "cockpit" ];
};
+ allowed-origins = lib.mkOption {
+ type = types.listOf types.str;
+
+ default = [ ];
+
+ description = ''
+ List of allowed origins.
+
+ Maps to the WebService.Origins setting and allows merging from multiple modules.
+ '';
+ };
+
settings = lib.mkOption {
type = settingsFormat.type;
@@ -62,14 +73,16 @@ in
# generate cockpit settings
environment.etc."cockpit/cockpit.conf".source = settingsFormat.generate "cockpit.conf" cfg.settings;
- security.pam.services.cockpit = { };
+ security.pam.services.cockpit = {
+ startSession = true;
+ };
networking.firewall.allowedTCPPorts = mkIf cfg.openFirewall [ cfg.port ];
systemd.packages = [ cfg.package ];
systemd.sockets.cockpit.wantedBy = [ "multi-user.target" ];
systemd.sockets.cockpit.listenStreams = [
- ""
+ "" # workaround so it doesn't listen on both ports caused by the runtime merging
(toString cfg.port)
];
@@ -80,6 +93,13 @@ in
"L+ /run/cockpit/motd - - - - inactive.motd"
"d /etc/cockpit/ws-certs.d 0600 root root 0"
];
+
+ services.cockpit.allowed-origins = [
+ "https://localhost:${toString config.services.cockpit.port}"
+ ];
+
+ services.cockpit.settings.WebService.Origins =
+ builtins.concatStringsSep " " config.services.cockpit.allowed-origins;
};
meta.maintainers = pkgs.cockpit.meta.maintainers;
diff --git a/nixos/modules/services/monitoring/grafana.nix b/nixos/modules/services/monitoring/grafana.nix
index d03fd3b72c09..a4156137e3f9 100644
--- a/nixos/modules/services/monitoring/grafana.nix
+++ b/nixos/modules/services/monitoring/grafana.nix
@@ -1220,12 +1220,6 @@ in
type = types.bool;
};
- editors_can_admin = mkOption {
- description = "Editors can administrate dashboards, folders and teams they create.";
- default = false;
- type = types.bool;
- };
-
user_invite_max_lifetime_duration = mkOption {
description = ''
The duration in time a user invitation remains valid before expiring.
@@ -1968,6 +1962,12 @@ in
environment.systemPackages = [ cfg.package ];
assertions = [
+ {
+ assertion = !(cfg.settings.users ? editors_can_admin);
+ message = ''
+ Option `services.grafana.settings.users.editors_can_admin` has been removed in Grafana 12.
+ '';
+ }
{
assertion = cfg.provision.datasources.settings == null || cfg.provision.datasources.path == null;
message = "Cannot set both datasources settings and datasources path";
diff --git a/nixos/modules/services/monitoring/graphite.nix b/nixos/modules/services/monitoring/graphite.nix
index 162d9ce679be..fc65c07e5dfa 100644
--- a/nixos/modules/services/monitoring/graphite.nix
+++ b/nixos/modules/services/monitoring/graphite.nix
@@ -292,7 +292,7 @@ in
serviceConfig = {
Slice = "system-graphite.slice";
RuntimeDirectory = name;
- ExecStart = "${pkgs.python3Packages.twisted}/bin/twistd ${carbonOpts name}";
+ ExecStart = "${lib.getExe' pkgs.python3Packages.twisted "twistd"} ${carbonOpts name}";
User = "graphite";
Group = "graphite";
PermissionsStartOnly = true;
@@ -319,7 +319,7 @@ in
serviceConfig = {
Slice = "system-graphite.slice";
RuntimeDirectory = name;
- ExecStart = "${pkgs.python3Packages.twisted}/bin/twistd ${carbonOpts name}";
+ ExecStart = "${lib.getExe' pkgs.python3Packages.twisted "twistd"} ${carbonOpts name}";
User = "graphite";
Group = "graphite";
PIDFile = "/run/${name}/${name}.pid";
@@ -340,7 +340,7 @@ in
serviceConfig = {
Slice = "system-graphite.slice";
RuntimeDirectory = name;
- ExecStart = "${pkgs.python3Packages.twisted}/bin/twistd ${carbonOpts name}";
+ ExecStart = "${lib.getExe' pkgs.python3Packages.twisted "twistd"} ${carbonOpts name}";
User = "graphite";
Group = "graphite";
PIDFile = "/run/${name}/${name}.pid";
@@ -384,7 +384,7 @@ in
};
serviceConfig = {
ExecStart = ''
- ${pkgs.python3Packages.waitress-django}/bin/waitress-serve-django \
+ ${lib.getExe pkgs.python3Packages.waitress-django} \
--host=${cfg.web.listenAddress} --port=${toString cfg.web.port}
'';
User = "graphite";
@@ -397,7 +397,7 @@ in
mkdir -p ${dataDir}/{whisper/,log/webapp/}
chmod 0700 ${dataDir}/{whisper/,log/webapp/}
- ${pkgs.python3Packages.django}/bin/django-admin.py migrate --noinput
+ ${lib.getExe' pkgs.python3Packages.django "django-admin"} migrate --noinput
chown -R graphite:graphite ${dataDir}
@@ -407,7 +407,7 @@ in
# Only collect static files when graphite_web changes.
if ! [ "${dataDir}/current_graphite_web" -ef "${pkgs.python3Packages.graphite-web}" ]; then
mkdir -p ${staticDir}
- ${pkgs.python3Packages.django}/bin/django-admin.py collectstatic --noinput --clear
+ ${lib.getExe' pkgs.python3Packages.django "django-admin"} collectstatic --noinput --clear
chown -R graphite:graphite ${staticDir}
ln -sfT "${pkgs.python3Packages.graphite-web}" "${dataDir}/current_graphite_web"
fi
@@ -427,7 +427,7 @@ in
];
environment = seyrenConfig;
serviceConfig = {
- ExecStart = "${pkgs.seyren}/bin/seyren -httpPort ${toString cfg.seyren.port}";
+ ExecStart = "${lib.getExe pkgs.seyren} -httpPort ${toString cfg.seyren.port}";
WorkingDirectory = dataDir;
User = "graphite";
Group = "graphite";
diff --git a/nixos/modules/services/monitoring/librenms.nix b/nixos/modules/services/monitoring/librenms.nix
index 49d76a82a921..06ee43f2a26d 100644
--- a/nixos/modules/services/monitoring/librenms.nix
+++ b/nixos/modules/services/monitoring/librenms.nix
@@ -596,7 +596,9 @@ in
${pkgs.envsubst}/bin/envsubst -i ${configJson} -o ${cfg.dataDir}/config.json
export PHPRC=${phpIni}
+ INIT=false
if [[ ! -s ${cfg.dataDir}/.env ]]; then
+ INIT=true
# init .env file
echo "APP_KEY=" > ${cfg.dataDir}/.env
${artisanWrapper}/bin/librenms-artisan key:generate --ansi
@@ -655,6 +657,10 @@ in
echo "${package.version}" > ${cfg.dataDir}/version
fi
+ if [[ $INIT == "true" ]]; then
+ ${artisanWrapper}/bin/librenms-artisan db:seed --force --no-interaction
+ fi
+
# regenerate cache if package has changed
if [[ $OLD_PACKAGE != "${package}" ]]; then
${artisanWrapper}/bin/librenms-artisan view:clear
diff --git a/nixos/modules/services/monitoring/prometheus/alertmanager-ntfy.nix b/nixos/modules/services/monitoring/prometheus/alertmanager-ntfy.nix
new file mode 100644
index 000000000000..5237a24406b1
--- /dev/null
+++ b/nixos/modules/services/monitoring/prometheus/alertmanager-ntfy.nix
@@ -0,0 +1,201 @@
+{
+ config,
+ lib,
+ pkgs,
+ ...
+}:
+
+let
+ cfg = config.services.prometheus.alertmanager-ntfy;
+
+ settingsFormat = pkgs.formats.yaml { };
+ settingsFile = settingsFormat.generate "settings.yml" cfg.settings;
+
+ configsArg = lib.concatStringsSep "," (
+ [ settingsFile ] ++ lib.imap0 (i: _: "%d/config-${toString i}.yml") cfg.extraConfigFiles
+ );
+in
+
+{
+ meta.maintainers = with lib.maintainers; [ defelo ];
+
+ options.services.prometheus.alertmanager-ntfy = {
+ enable = lib.mkEnableOption "alertmanager-ntfy";
+
+ package = lib.mkPackageOption pkgs "alertmanager-ntfy" { };
+
+ settings = lib.mkOption {
+ description = ''
+ Configuration of alertmanager-ntfy.
+ See for more information.
+ '';
+ default = { };
+
+ type = lib.types.submodule {
+ freeformType = settingsFormat.type;
+
+ options = {
+ http.addr = lib.mkOption {
+ type = lib.types.str;
+ description = "The address to listen on.";
+ default = "127.0.0.1:8000";
+ example = ":8000";
+ };
+
+ ntfy = {
+ baseurl = lib.mkOption {
+ type = lib.types.str;
+ description = "The base URL of the ntfy.sh instance.";
+ example = "https://ntfy.sh";
+ };
+
+ notification = {
+ topic = lib.mkOption {
+ type = lib.types.str;
+ description = ''
+ The topic to which alerts should be published.
+ Can either be a hardcoded string or a gval expression that evaluates to a string.
+ '';
+ example = "alertmanager";
+ };
+
+ priority = lib.mkOption {
+ type = lib.types.str;
+ description = ''
+ The ntfy.sh message priority (see for more information).
+ Can either be a hardcoded string or a gval expression that evaluates to a string.
+ '';
+ default = ''status == "firing" ? "high" : "default"'';
+ };
+
+ tags = lib.mkOption {
+ type = lib.types.listOf (
+ lib.types.submodule {
+ options = {
+ tag = lib.mkOption {
+ type = lib.types.str;
+ description = ''
+ The tag to add.
+ See for a list of all supported emojis.
+ '';
+ example = "rotating_light";
+ };
+
+ condition = lib.mkOption {
+ type = lib.types.nullOr lib.types.str;
+ description = ''
+ The condition under which this tag should be added.
+ Tags with no condition are always included.
+ '';
+ default = null;
+ example = ''status == "firing"'';
+ };
+ };
+ }
+ );
+ description = ''
+ Tags to add to ntfy.sh messages.
+ See for more information.
+ '';
+ default = [
+ {
+ tag = "green_circle";
+ condition = ''status == "resolved"'';
+ }
+ {
+ tag = "red_circle";
+ condition = ''status == "firing"'';
+ }
+ ];
+ };
+
+ templates = {
+ title = lib.mkOption {
+ type = lib.types.str;
+ description = "The ntfy.sh message title template.";
+ default = ''
+ {{ if eq .Status "resolved" }}Resolved: {{ end }}{{ index .Annotations "summary" }}
+ '';
+ };
+
+ description = lib.mkOption {
+ type = lib.types.str;
+ description = "The ntfy.sh message description template.";
+ default = ''
+ {{ index .Annotations "description" }}
+ '';
+ };
+ };
+ };
+ };
+ };
+ };
+ };
+
+ extraConfigFiles = lib.mkOption {
+ type = lib.types.listOf lib.types.path;
+ default = [ ];
+ example = [ "/run/secrets/alertmanager-ntfy.yml" ];
+ description = ''
+ Config files to merge into the settings defined in [](#opt-services.prometheus.alertmanager-ntfy.settings).
+ This is useful to avoid putting secrets into the Nix store.
+ See for more information.
+ '';
+ };
+ };
+
+ config = lib.mkIf cfg.enable {
+ systemd.services.alertmanager-ntfy = {
+ wantedBy = [ "multi-user.target" ];
+
+ wants = [ "network-online.target" ];
+ after = [ "network-online.target" ];
+
+ serviceConfig = {
+ User = "alertmanager-ntfy";
+ Group = "alertmanager-ntfy";
+ DynamicUser = true;
+
+ LoadCredential = lib.imap0 (i: path: "config-${toString i}.yml:${path}") cfg.extraConfigFiles;
+
+ ExecStart = "${lib.getExe cfg.package} --configs ${configsArg}";
+
+ Restart = "always";
+ RestartSec = 5;
+
+ # Hardening
+ AmbientCapabilities = "";
+ CapabilityBoundingSet = [ "" ];
+ DevicePolicy = "closed";
+ LockPersonality = true;
+ MemoryDenyWriteExecute = true;
+ NoNewPrivileges = true;
+ PrivateDevices = true;
+ PrivateTmp = true;
+ PrivateUsers = true;
+ ProcSubset = "pid";
+ ProtectClock = true;
+ ProtectControlGroups = true;
+ ProtectHome = true;
+ ProtectHostname = true;
+ ProtectKernelLogs = true;
+ ProtectKernelModules = true;
+ ProtectKernelTunables = true;
+ ProtectProc = "invisible";
+ ProtectSystem = "strict";
+ RemoveIPC = true;
+ RestrictAddressFamilies = [ "AF_INET AF_INET6" ];
+ RestrictNamespaces = true;
+ RestrictRealtime = true;
+ RestrictSUIDSGID = true;
+ SystemCallArchitectures = "native";
+ SystemCallFilter = [
+ "@system-service"
+ "~@privileged"
+ "~@resources"
+ ];
+ UMask = "0077";
+ };
+ };
+ };
+}
diff --git a/nixos/modules/services/monitoring/prometheus/exporters.nix b/nixos/modules/services/monitoring/prometheus/exporters.nix
index 258d13e3c7c7..2adc59eefbef 100644
--- a/nixos/modules/services/monitoring/prometheus/exporters.nix
+++ b/nixos/modules/services/monitoring/prometheus/exporters.nix
@@ -65,6 +65,7 @@ let
"dnssec"
"domain"
"dovecot"
+ "ebpf"
"fastly"
"flow"
"fritz"
diff --git a/nixos/modules/services/monitoring/prometheus/exporters/ebpf.nix b/nixos/modules/services/monitoring/prometheus/exporters/ebpf.nix
new file mode 100644
index 000000000000..8ccb6d21623b
--- /dev/null
+++ b/nixos/modules/services/monitoring/prometheus/exporters/ebpf.nix
@@ -0,0 +1,49 @@
+{
+ config,
+ lib,
+ pkgs,
+ options,
+ ...
+}:
+
+let
+ cfg = config.services.prometheus.exporters.ebpf;
+ inherit (lib)
+ mkOption
+ types
+ concatStringsSep
+ ;
+in
+{
+ port = 9435;
+ extraOpts = {
+ names = mkOption {
+ type = types.listOf types.str;
+ default = [ ];
+ example = [ "timers" ];
+ description = ''
+ List of eBPF programs to load
+ '';
+ };
+ };
+ serviceOpts = {
+ serviceConfig = {
+ AmbientCapabilities = [
+ "CAP_BPF"
+ "CAP_DAC_READ_SEARCH"
+ "CAP_PERFMON"
+ ];
+ CapabilityBoundingSet = [
+ "CAP_BPF"
+ "CAP_DAC_READ_SEARCH"
+ "CAP_PERFMON"
+ ];
+ ExecStart = ''
+ ${pkgs.prometheus-ebpf-exporter}/bin/ebpf_exporter \
+ --config.dir=${pkgs.prometheus-ebpf-exporter}/examples \
+ --config.names=${concatStringsSep "," cfg.names} \
+ --web.listen-address ${cfg.listenAddress}:${toString cfg.port}
+ '';
+ };
+ };
+}
diff --git a/nixos/modules/services/monitoring/ups.nix b/nixos/modules/services/monitoring/ups.nix
index b3b3632385db..a652d41587ee 100644
--- a/nixos/modules/services/monitoring/ups.nix
+++ b/nixos/modules/services/monitoring/ups.nix
@@ -74,9 +74,9 @@ let
};
installSecrets =
- source: target: secrets:
+ source: target: owner: secrets:
pkgs.writeShellScript "installSecrets.sh" ''
- install -m0600 -D ${source} "${target}"
+ install -m0600 -o${owner} -D ${source} "${target}"
${lib.concatLines (
lib.forEach secrets (name: ''
${pkgs.replace-secret}/bin/replace-secret \
@@ -327,6 +327,23 @@ let
description = "Whether to enable `upsmon`.";
};
+ user = lib.mkOption {
+ type = lib.types.str;
+ default = "nutmon";
+ description = ''
+ User to run `upsmon` as. `upsmon.conf` will have its owner set to this
+ user. If not specified, a default user will be created.
+ '';
+ };
+ group = lib.mkOption {
+ type = lib.types.str;
+ default = "nutmon";
+ description = ''
+ Group for the default `nutmon` user. If the default user is created
+ and this is not specified, a default group will be created.
+ '';
+ };
+
monitor = lib.mkOption {
type = with lib.types; attrsOf (submodule monitorOptions);
default = { };
@@ -344,7 +361,6 @@ let
MONITOR =
NOTIFYCMD = "''${pkgs.nut}/bin/upssched";
POWERDOWNFLAG = "/run/killpower";
- RUN_AS_USER = "root";
SHUTDOWNCMD = "''${pkgs.systemd}/bin/shutdown now";
}
'';
@@ -382,7 +398,6 @@ let
);
NOTIFYCMD = lib.mkDefault "${pkgs.nut}/bin/upssched";
POWERDOWNFLAG = lib.mkDefault "/run/killpower";
- RUN_AS_USER = "root"; # TODO: replace 'root' by another username.
SHUTDOWNCMD = lib.mkDefault "${pkgs.systemd}/bin/shutdown now";
};
};
@@ -581,7 +596,7 @@ in
systemd.services.upsmon =
let
secrets = lib.mapAttrsToList (name: monitor: "upsmon_password_${name}") cfg.upsmon.monitor;
- createUpsmonConf = installSecrets upsmonConf "/run/nut/upsmon.conf" secrets;
+ createUpsmonConf = installSecrets upsmonConf "/run/nut/upsmon.conf" cfg.upsmon.user secrets;
in
{
enable = cfg.upsmon.enable;
@@ -591,7 +606,7 @@ in
serviceConfig = {
Type = "forking";
ExecStartPre = "${createUpsmonConf}";
- ExecStart = "${pkgs.nut}/sbin/upsmon";
+ ExecStart = "${pkgs.nut}/sbin/upsmon -u ${cfg.upsmon.user}";
ExecReload = "${pkgs.nut}/sbin/upsmon -c reload";
LoadCredential = lib.mapAttrsToList (
name: monitor: "upsmon_password_${name}:${monitor.passwordFile}"
@@ -604,7 +619,7 @@ in
systemd.services.upsd =
let
secrets = lib.mapAttrsToList (name: user: "upsdusers_password_${name}") cfg.users;
- createUpsdUsers = installSecrets upsdUsers "/run/nut/upsd.users" secrets;
+ createUpsdUsers = installSecrets upsdUsers "/run/nut/upsd.users" "root" secrets;
in
{
enable = cfg.upsd.enable;
@@ -696,18 +711,11 @@ in
services.udev.packages = [ pkgs.nut ];
- /*
- users.users.nut =
- { uid = 84;
- home = "/var/lib/nut";
- createHome = true;
- group = "nut";
- description = "UPnP A/V Media Server user";
- };
-
- users.groups."nut" =
- { gid = 84; };
- */
+ users.users.nutmon = lib.mkIf (cfg.upsmon.user == "nutmon") {
+ isSystemUser = true;
+ group = cfg.upsmon.group;
+ };
+ users.groups.nutmon = lib.mkIf (cfg.upsmon.user == "nutmon" && cfg.upsmon.group == "nutmon") { };
};
}
diff --git a/nixos/modules/services/network-filesystems/rsyncd.nix b/nixos/modules/services/network-filesystems/rsyncd.nix
index 810ed10b695d..209e826c37c9 100644
--- a/nixos/modules/services/network-filesystems/rsyncd.nix
+++ b/nixos/modules/services/network-filesystems/rsyncd.nix
@@ -6,7 +6,7 @@
}:
let
cfg = config.services.rsyncd;
- settingsFormat = pkgs.formats.ini { };
+ settingsFormat = pkgs.formats.iniWithGlobalSection { };
configFile = settingsFormat.generate "rsyncd.conf" cfg.settings;
in
{
@@ -25,24 +25,27 @@ in
inherit (settingsFormat) type;
default = { };
example = {
- global = {
+ globalSection = {
uid = "nobody";
gid = "nobody";
"use chroot" = true;
"max connections" = 4;
+ address = "0.0.0.0";
};
- ftp = {
- path = "/var/ftp/./pub";
- comment = "whole ftp area";
- };
- cvs = {
- path = "/data/cvs";
- comment = "CVS repository (requires authentication)";
- "auth users" = [
- "tridge"
- "susan"
- ];
- "secrets file" = "/etc/rsyncd.secrets";
+ sections = {
+ ftp = {
+ path = "/var/ftp/./pub";
+ comment = "whole ftp area";
+ };
+ cvs = {
+ path = "/data/cvs";
+ comment = "CVS repository (requires authentication)";
+ "auth users" = [
+ "tridge"
+ "susan"
+ ];
+ "secrets file" = "/etc/rsyncd.secrets";
+ };
};
};
description = ''
@@ -81,7 +84,7 @@ in
config = lib.mkIf cfg.enable {
- services.rsyncd.settings.global.port = toString cfg.port;
+ services.rsyncd.settings.globalSection.port = toString cfg.port;
systemd =
let
diff --git a/nixos/modules/services/networking/anubis.md b/nixos/modules/services/networking/anubis.md
new file mode 100644
index 000000000000..8a9a2ea76aa6
--- /dev/null
+++ b/nixos/modules/services/networking/anubis.md
@@ -0,0 +1,61 @@
+# Anubis {#module-services-anubis}
+
+[Anubis](https://anubis.techaro.lol) is a scraper defense software that blocks AI scrapers. It is designed to sit
+between a reverse proxy and the service to be protected.
+
+## Quickstart {#module-services-anubis-quickstart}
+
+This module is designed to use Unix domain sockets as the socket paths can be automatically configured for multiple
+instances, but TCP sockets are also supported.
+
+A minimal configuration with [nginx](#opt-services.nginx.enable) may look like the following:
+
+```nix
+{ config, ... }: {
+ services.anubis.instances.default.settings.TARGET = "http://localhost:8000";
+
+ # required due to unix socket permissions
+ users.users.nginx.extraGroups = [ config.users.groups.anubis.name ];
+ services.nginx.virtualHosts."example.com" = {
+ locations = {
+ "/".proxyPass = "http://unix:${config.services.anubis.instances.default.settings.BIND}";
+ };
+ };
+}
+```
+
+If Unix domain sockets are not needed or desired, this module supports operating with only TCP sockets.
+
+```nix
+{
+ services.anubis = {
+ instances.default = {
+ settings = {
+ TARGET = "http://localhost:8080";
+ BIND = ":9000";
+ BIND_NETWORK = "tcp";
+ METRICS_BIND = "127.0.0.1:9001";
+ METRICS_BIND_NETWORK = "tcp";
+ };
+ };
+ };
+}
+```
+
+## Configuration {#module-services-anubis-configuration}
+
+It is possible to configure default settings for all instances of Anubis, via {option}`services.anubis.defaultOptions`.
+
+```nix
+{
+ services.anubis.defaultOptions = {
+ botPolicy = { dnsbl = false; };
+ settings.DIFFICULTY = 3;
+ };
+}
+```
+
+Note that at the moment, a custom bot policy is not merged with the baked-in one. That means to only override a setting
+like `dnsbl`, copying the entire bot policy is required. Check
+[the upstream repository](https://github.com/TecharoHQ/anubis/blob/1509b06cb921aff842e71fbb6636646be6ed5b46/cmd/anubis/botPolicies.json)
+for the policy.
diff --git a/nixos/modules/services/networking/anubis.nix b/nixos/modules/services/networking/anubis.nix
new file mode 100644
index 000000000000..2d63fa4ecc59
--- /dev/null
+++ b/nixos/modules/services/networking/anubis.nix
@@ -0,0 +1,334 @@
+{
+ config,
+ lib,
+ pkgs,
+ ...
+}:
+let
+ inherit (lib) types;
+ jsonFormat = pkgs.formats.json { };
+
+ cfg = config.services.anubis;
+ enabledInstances = lib.filterAttrs (_: conf: conf.enable) cfg.instances;
+ instanceName = name: if name == "" then "anubis" else "anubis-${name}";
+
+ commonSubmodule =
+ isDefault:
+ let
+ mkDefaultOption =
+ path: opts:
+ lib.mkOption (
+ opts
+ // lib.optionalAttrs (!isDefault && opts ? default) {
+ default =
+ lib.attrByPath (lib.splitString "." path)
+ (throw "This is a bug in the Anubis module. Please report this as an issue.")
+ cfg.defaultOptions;
+ defaultText = lib.literalExpression "config.services.anubis.defaultOptions.${path}";
+ }
+ );
+ in
+ { name, ... }:
+ {
+ options = {
+ enable = lib.mkEnableOption "this instance of Anubis" // {
+ default = true;
+ };
+ user = mkDefaultOption "user" {
+ default = "anubis";
+ description = ''
+ The user under which Anubis is run.
+
+ This module utilizes systemd's DynamicUser feature. See the corresponding section in
+ {manpage}`systemd.exec(5)` for more details.
+ '';
+ type = types.str;
+ };
+ group = mkDefaultOption "group" {
+ default = "anubis";
+ description = ''
+ The group under which Anubis is run.
+
+ This module utilizes systemd's DynamicUser feature. See the corresponding section in
+ {manpage}`systemd.exec(5)` for more details.
+ '';
+ type = types.str;
+ };
+
+ botPolicy = lib.mkOption {
+ default = null;
+ description = ''
+ Anubis policy configuration in Nix syntax. Set to `null` to use the baked-in policy which should be
+ sufficient for most use-cases.
+
+ This option has no effect if `settings.POLICY_FNAME` is set to a different value, which is useful for
+ importing an existing configuration.
+
+ See [the documentation](https://anubis.techaro.lol/docs/admin/policies) for details.
+ '';
+ type = types.nullOr jsonFormat.type;
+ };
+
+ extraFlags = mkDefaultOption "extraFlags" {
+ default = [ ];
+ description = "A list of extra flags to be passed to Anubis.";
+ example = [ "-metrics-bind \"\"" ];
+ type = types.listOf types.str;
+ };
+
+ settings = lib.mkOption {
+ default = { };
+ description = ''
+ Freeform configuration via environment variables for Anubis.
+
+ See [the documentation](https://anubis.techaro.lol/docs/admin/installation) for a complete list of
+ available environment variables.
+ '';
+ type = types.submodule [
+ {
+ freeformType =
+ with types;
+ attrsOf (
+ nullOr (oneOf [
+ str
+ int
+ bool
+ ])
+ );
+
+ options = {
+ # BIND and METRICS_BIND are defined in instance specific options, since global defaults don't make sense
+ BIND_NETWORK = mkDefaultOption "settings.BIND_NETWORK" {
+ default = "unix";
+ description = ''
+ The network family that Anubis should bind to.
+
+ Accepts anything supported by Go's [`net.Listen`](https://pkg.go.dev/net#Listen).
+
+ Common values are `tcp` and `unix`.
+ '';
+ example = "tcp";
+ type = types.str;
+ };
+ METRICS_BIND_NETWORK = mkDefaultOption "settings.METRICS_BIND_NETWORK" {
+ default = "unix";
+ description = ''
+ The network family that the metrics server should bind to.
+
+ Accepts anything supported by Go's [`net.Listen`](https://pkg.go.dev/net#Listen).
+
+ Common values are `tcp` and `unix`.
+ '';
+ example = "tcp";
+ type = types.str;
+ };
+ DIFFICULTY = mkDefaultOption "settings.DIFFICULTY" {
+ default = 4;
+ description = ''
+ The difficulty required for clients to solve the challenge.
+
+ Currently, this means the amount of leading zeros in a successful response.
+ '';
+ type = types.int;
+ example = 5;
+ };
+ SERVE_ROBOTS_TXT = mkDefaultOption "settings.SERVE_ROBOTS_TXT" {
+ default = false;
+ description = ''
+ Whether to serve a default robots.txt that denies access to common AI bots by name and all other
+ bots by wildcard.
+ '';
+ type = types.bool;
+ };
+ OG_PASSTHROUGH = mkDefaultOption "settings.OG_PASSTHROUGH" {
+ default = false;
+ description = ''
+ Whether to enable Open Graph tag passthrough.
+
+ This enables social previews of resources protected by
+ Anubis without having to exempt each scraper individually.
+ '';
+ type = types.bool;
+ };
+ WEBMASTER_EMAIL = mkDefaultOption "settings.WEBMASTER_EMAIL" {
+ default = null;
+ description = ''
+ If set, shows a contact email address when rendering error pages.
+
+ This email address will be how users can get in contact with administrators.
+ '';
+ example = "alice@example.com";
+ type = types.nullOr types.str;
+ };
+
+ # generated by default
+ POLICY_FNAME = mkDefaultOption "settings.POLICY_FNAME" {
+ default = null;
+ description = ''
+ The bot policy file to use. Leave this as `null` to respect the value set in
+ {option}`services.anubis.instances..botPolicy`.
+ '';
+ type = types.nullOr types.path;
+ };
+ };
+ }
+ (lib.optionalAttrs (!isDefault) (instanceSpecificOptions name))
+ ];
+ };
+ };
+ };
+
+ instanceSpecificOptions = name: {
+ options = {
+ # see other options above
+ BIND = lib.mkOption {
+ default = "/run/anubis/${instanceName name}.sock";
+ description = ''
+ The address that Anubis listens to. See Go's [`net.Listen`](https://pkg.go.dev/net#Listen) for syntax.
+
+ Defaults to Unix domain sockets. To use TCP sockets, set this to a TCP address and `BIND_NETWORK` to `"tcp"`.
+ '';
+ example = ":8080";
+ type = types.str;
+ };
+ METRICS_BIND = lib.mkOption {
+ default = "/run/anubis/${instanceName name}-metrics.sock";
+ description = ''
+ The address Anubis' metrics server listens to. See Go's [`net.Listen`](https://pkg.go.dev/net#Listen) for
+ syntax.
+
+ The metrics server is enabled by default and may be disabled. However, due to implementation details, this is
+ only possible by setting a command line flag. See {option}`services.anubis.defaultOptions.extraFlags` for an
+ example.
+
+ Defaults to Unix domain sockets. To use TCP sockets, set this to a TCP address and `METRICS_BIND_NETWORK` to
+ `"tcp"`.
+ '';
+ example = "127.0.0.1:8081";
+ type = types.str;
+ };
+ TARGET = lib.mkOption {
+ description = ''
+ The reverse proxy target that Anubis is protecting. This is a required option.
+
+ The usage of Unix domain sockets is supported by the following syntax: `unix:///path/to/socket.sock`.
+ '';
+ example = "http://127.0.0.1:8000";
+ type = types.str;
+ };
+ };
+ };
+in
+{
+ options.services.anubis = {
+ package = lib.mkPackageOption pkgs "anubis" { };
+
+ defaultOptions = lib.mkOption {
+ default = { };
+ description = "Default options for all instances of Anubis.";
+ type = types.submodule (commonSubmodule true);
+ };
+
+ instances = lib.mkOption {
+ default = { };
+ description = ''
+ An attribute set of Anubis instances.
+
+ The attribute name may be an empty string, in which case the `-` suffix is not added to the service name
+ and socket paths.
+ '';
+ type = types.attrsOf (types.submodule (commonSubmodule false));
+
+ # Merge defaultOptions into each instance
+ apply = lib.mapAttrs (_: lib.recursiveUpdate cfg.defaultOptions);
+ };
+ };
+
+ config = lib.mkIf (enabledInstances != { }) {
+ users.users = lib.mkIf (cfg.defaultOptions.user == "anubis") {
+ anubis = {
+ isSystemUser = true;
+ group = cfg.defaultOptions.group;
+ };
+ };
+
+ users.groups = lib.mkIf (cfg.defaultOptions.group == "anubis") {
+ anubis = { };
+ };
+
+ systemd.services = lib.mapAttrs' (
+ name: instance:
+ lib.nameValuePair "${instanceName name}" {
+ description = "Anubis (${if name == "" then "default" else name} instance)";
+ wantedBy = [ "multi-user.target" ];
+ after = [ "network-online.target" ];
+ wants = [ "network-online.target" ];
+
+ environment = lib.mapAttrs (lib.const (lib.generators.mkValueStringDefault { })) (
+ lib.filterAttrs (_: v: v != null) instance.settings
+ );
+
+ serviceConfig = {
+ User = instance.user;
+ Group = instance.group;
+ DynamicUser = true;
+
+ ExecStart = lib.concatStringsSep " " (
+ (lib.singleton (lib.getExe cfg.package)) ++ instance.extraFlags
+ );
+ RuntimeDirectory =
+ if
+ lib.any (lib.hasPrefix "/run/anubis") (
+ with instance.settings;
+ [
+ BIND
+ METRICS_BIND
+ ]
+ )
+ then
+ "anubis"
+ else
+ null;
+
+ # hardening
+ NoNewPrivileges = true;
+ CapabilityBoundingSet = null;
+ SystemCallFilter = [
+ "@system-service"
+ "~@privileged"
+ ];
+ SystemCallArchitectures = "native";
+ MemoryDenyWriteExecute = true;
+
+ PrivateUsers = true;
+ PrivateTmp = true;
+ PrivateDevices = true;
+ ProtectHome = true;
+ ProtectClock = true;
+ ProtectHostname = true;
+ ProtectKernelLogs = true;
+ ProtectKernelModules = true;
+ ProtectKernelTunables = true;
+ ProtectProc = "invisible";
+ ProtectSystem = "strict";
+ ProtectControlGroups = "strict";
+ LockPersonality = true;
+ RestrictRealtime = true;
+ RestrictSUIDSGID = true;
+ RestrictNamespaces = true;
+ RestrictAddressFamilies = [
+ "AF_UNIX"
+ "AF_INET"
+ "AF_INET6"
+ ];
+ };
+ }
+ ) enabledInstances;
+ };
+
+ meta.maintainers = with lib.maintainers; [
+ soopyc
+ nullcube
+ ];
+ meta.doc = ./anubis.md;
+}
diff --git a/nixos/modules/services/networking/ax25/axlisten.nix b/nixos/modules/services/networking/ax25/axlisten.nix
new file mode 100644
index 000000000000..ad887885c142
--- /dev/null
+++ b/nixos/modules/services/networking/ax25/axlisten.nix
@@ -0,0 +1,62 @@
+{
+ config,
+ lib,
+ pkgs,
+ ...
+}:
+
+let
+ inherit (lib)
+ types
+ ;
+
+ inherit (lib.modules)
+ mkIf
+ ;
+
+ inherit (lib.options)
+ mkEnableOption
+ mkOption
+ literalExpression
+ ;
+
+ cfg = config.services.ax25.axlisten;
+in
+{
+ options = {
+
+ services.ax25.axlisten = {
+
+ enable = mkEnableOption "AX.25 axlisten daemon";
+
+ package = mkOption {
+ type = types.package;
+ default = pkgs.ax25-apps;
+ defaultText = literalExpression "pkgs.ax25-apps";
+ description = "The ax25-apps package to use.";
+ };
+
+ config = mkOption {
+ type = types.str;
+ default = "-art";
+ description = ''
+ Options that will be passed to the axlisten daemon.
+ '';
+ };
+ };
+ };
+
+ config = mkIf cfg.enable {
+
+ systemd.services.axlisten = {
+ description = "AX.25 traffic monitor";
+ wantedBy = [ "multi-user.target" ];
+ after = [ "ax25-axports.target" ];
+ requires = [ "ax25-axports.target" ];
+ serviceConfig = {
+ Type = "exec";
+ ExecStart = "${cfg.package}/bin/axlisten ${cfg.config}";
+ };
+ };
+ };
+}
diff --git a/nixos/modules/services/networking/ax25/axports.nix b/nixos/modules/services/networking/ax25/axports.nix
new file mode 100644
index 000000000000..257d7ee8c1c5
--- /dev/null
+++ b/nixos/modules/services/networking/ax25/axports.nix
@@ -0,0 +1,149 @@
+{
+ config,
+ lib,
+ pkgs,
+ ...
+}:
+
+let
+ inherit (lib)
+ types
+ ;
+
+ inherit (lib.strings)
+ concatStringsSep
+ optionalString
+ ;
+
+ inherit (lib.attrsets)
+ filterAttrs
+ mapAttrsToList
+ mapAttrs'
+ ;
+
+ inherit (lib.modules)
+ mkIf
+ ;
+
+ inherit (lib.options)
+ mkEnableOption
+ mkOption
+ mkPackageOption
+ ;
+
+ cfg = config.services.ax25.axports;
+
+ enabledAxports = filterAttrs (ax25Name: cfg: cfg.enable) cfg;
+
+ axportsOpts = {
+
+ options = {
+ enable = mkEnableOption "Enables the axport interface";
+
+ package = mkPackageOption pkgs "ax25-tools" { };
+
+ tty = mkOption {
+ type = types.str;
+ example = "/dev/ttyACM0";
+ description = ''
+ Location of hardware kiss tnc for this interface.
+ '';
+ };
+
+ callsign = mkOption {
+ type = types.str;
+ example = "WB6WLV-7";
+ description = ''
+ The callsign of the physical interface to bind to.
+ '';
+ };
+
+ description = mkOption {
+ type = types.str;
+ # This cannot be empty since some ax25 tools cant parse /etc/ax25/axports without it
+ default = "NixOS managed tnc";
+ description = ''
+ Free format description of this interface.
+ '';
+ };
+
+ baud = mkOption {
+ type = types.int;
+ example = 57600;
+ description = ''
+ The serial port speed of this interface.
+ '';
+ };
+
+ paclen = mkOption {
+ type = types.int;
+ default = 255;
+ description = ''
+ Default maximum packet size for this interface.
+ '';
+ };
+
+ window = mkOption {
+ type = types.int;
+ default = 7;
+ description = ''
+ Default window size for this interface.
+ '';
+ };
+
+ kissParams = mkOption {
+ type = types.nullOr types.str;
+ default = null;
+ example = "-t 300 -l 10 -s 12 -r 80 -f n";
+ description = ''
+ Kissattach parameters for this interface.
+ '';
+ };
+ };
+ };
+in
+{
+
+ options = {
+
+ services.ax25.axports = mkOption {
+ type = types.attrsOf (types.submodule axportsOpts);
+ default = { };
+ description = "Specification of one or more AX.25 ports.";
+ };
+ };
+
+ config = mkIf (enabledAxports != { }) {
+
+ environment.etc."ax25/axports" = {
+ text = concatStringsSep "\n" (
+ mapAttrsToList (
+ portName: portCfg:
+ "${portName} ${portCfg.callsign} ${toString portCfg.baud} ${toString portCfg.paclen} ${toString portCfg.window} ${portCfg.description}"
+ ) enabledAxports
+ );
+ mode = "0644";
+ };
+
+ systemd.targets.ax25-axports = {
+ description = "AX.25 axports group target";
+ };
+
+ systemd.services = mapAttrs' (portName: portCfg: {
+ name = "ax25-kissattach-${portName}";
+ value = {
+ description = "AX.25 KISS attached interface for ${portName}";
+ wantedBy = [ "multi-user.target" ];
+ before = [ "ax25-axports.target" ];
+ partOf = [ "ax25-axports.target" ];
+ serviceConfig = {
+ Type = "exec";
+ ExecStart = "${portCfg.package}/bin/kissattach ${portCfg.tty} ${portName}";
+ };
+ postStart = optionalString (portCfg.kissParams != null) ''
+ ${portCfg.package}/bin/kissparms -p ${portName} ${portCfg.kissParams}
+ '';
+ };
+ }) enabledAxports;
+ };
+}
diff --git a/nixos/modules/services/networking/bird-lg.nix b/nixos/modules/services/networking/bird-lg.nix
index f565c7f505fb..5d7b7c30f04c 100644
--- a/nixos/modules/services/networking/bird-lg.nix
+++ b/nixos/modules/services/networking/bird-lg.nix
@@ -187,7 +187,7 @@ in
};
extraArgs = lib.mkOption {
- type = with lib.types; either lines (listOf str);
+ type = with lib.types; listOf str;
default = [ ];
description = ''
Extra parameters documented [here](https://github.com/xddxdd/bird-lg-go#frontend).
@@ -247,14 +247,10 @@ in
};
extraArgs = lib.mkOption {
- type = with lib.types; either lines (listOf str);
+ type = with lib.types; listOf str;
default = [ ];
description = ''
Extra parameters documented [here](https://github.com/xddxdd/bird-lg-go#proxy).
-
- :::{.note}
- Passing lines (plain strings) is deprecated in favour of passing lists of strings.
- :::
'';
};
};
@@ -264,15 +260,6 @@ in
###### implementation
config = {
-
- warnings =
- lib.optional (cfg.frontend.enable && builtins.isString cfg.frontend.extraArgs) ''
- Passing strings to `services.bird-lg.frontend.extraOptions' is deprecated. Please pass a list of strings instead.
- ''
- ++ lib.optional (cfg.proxy.enable && builtins.isString cfg.proxy.extraArgs) ''
- Passing strings to `services.bird-lg.proxy.extraOptions' is deprecated. Please pass a list of strings instead.
- '';
-
systemd.services = {
bird-lg-frontend = lib.mkIf cfg.frontend.enable {
enable = true;
diff --git a/nixos/modules/services/networking/cato-client.nix b/nixos/modules/services/networking/cato-client.nix
new file mode 100644
index 000000000000..b9b31e0c5794
--- /dev/null
+++ b/nixos/modules/services/networking/cato-client.nix
@@ -0,0 +1,75 @@
+{
+ config,
+ pkgs,
+ lib,
+ ...
+}:
+let
+ inherit (lib) mkIf mkEnableOption mkPackageOption;
+
+ cfg = config.services.cato-client;
+in
+{
+ options.services.cato-client = {
+ enable = mkEnableOption "cato-client service";
+ package = mkPackageOption pkgs "cato-client" { };
+ };
+
+ config = mkIf cfg.enable {
+ users = {
+ groups.cato-client = { };
+ };
+
+ environment.systemPackages = [
+ cfg.package
+ ];
+
+ systemd.services.cato-client = {
+ enable = true;
+ description = "Cato Networks Linux client - connects tunnel to Cato cloud";
+ after = [ "network.target" ];
+
+ serviceConfig = {
+ Type = "simple";
+ User = "root"; # Note: daemon runs as root, tools sticky to group
+ Group = "cato-client";
+ ExecStart = "${cfg.package}/bin/cato-clientd systemd";
+ WorkingDirectory = "${cfg.package}";
+ Restart = "always";
+
+ # Cato client seems to do the following:
+ # - Look in each user's ~/.cato/ for configuration and keys
+ # - Write to /var/log/cato-client.log
+ # - Create and use sockets /var/run/cato-sdp.i, /var/run/cato-sdp.o
+ # - Read and Write to /opt/cato/ for runtime settings
+ # - Read /etc/systemd/resolved.conf (but fine if fails)
+ # - Restart systemd-resolved (also fine if doesn't exist)
+
+ NoNewPrivileges = true;
+ PrivateTmp = true;
+ ProtectKernelTunables = true;
+ ProtectControlGroups = true;
+ ProtectSystem = true;
+ };
+
+ wantedBy = [ "multi-user.target" ];
+ };
+
+ # set up Security wrapper Same as inteded in deb post install
+ security.wrappers.cato-clientd = {
+ source = "${cfg.package}/bin/cato-clientd";
+ owner = "root";
+ group = "cato-client";
+ permissions = "u+rwx,g+rwx"; # 770
+ setgid = true;
+ };
+
+ security.wrappers.cato-sdp = {
+ source = "${cfg.package}/bin/cato-sdp";
+ owner = "root";
+ group = "cato-client";
+ permissions = "u+rwx,g+rx,a+rx"; # 755
+ setgid = true;
+ };
+ };
+}
diff --git a/nixos/modules/services/networking/centrifugo.nix b/nixos/modules/services/networking/centrifugo.nix
index 7b9a05069498..0f2a9f3e5f6a 100644
--- a/nixos/modules/services/networking/centrifugo.nix
+++ b/nixos/modules/services/networking/centrifugo.nix
@@ -65,6 +65,14 @@ in
};
config = lib.mkIf cfg.enable {
+ assertions = [
+ {
+ assertion =
+ (lib.versionAtLeast cfg.package.version "6") -> (!(cfg.settings ? name) && !(cfg.settings ? port));
+ message = "`services.centrifugo.settings` is v5 config, must be compatible with centrifugo v6 config format";
+ }
+ ];
+
systemd.services.centrifugo = {
description = "Centrifugo messaging server";
wantedBy = [ "multi-user.target" ];
diff --git a/nixos/modules/services/networking/cloudflare-dyndns.nix b/nixos/modules/services/networking/cloudflare-dyndns.nix
index f99935210b06..c8597d28c209 100644
--- a/nixos/modules/services/networking/cloudflare-dyndns.nix
+++ b/nixos/modules/services/networking/cloudflare-dyndns.nix
@@ -108,13 +108,14 @@ in
++ lib.optional cfg.proxied "--proxied";
in
''
- export CLOUDFLARE_API_TOKEN=$(< "''${CREDENTIALS_DIRECTORY}/apiToken")
+ export CLOUDFLARE_API_TOKEN_FILE=''${CREDENTIALS_DIRECTORY}/apiToken
# Added 2025-03-10: `cfg.apiTokenFile` used to be passed as an
# `EnvironmentFile` to the service, which required it to be of
# the form "CLOUDFLARE_API_TOKEN=" rather than just the secret.
# If we detect this legacy usage, error out.
- if [[ $CLOUDFLARE_API_TOKEN == CLOUDFLARE_API_TOKEN* ]]; then
+ token=$(< "''${CLOUDFLARE_API_TOKEN_FILE}")
+ if [[ $token == CLOUDFLARE_API_TOKEN* ]]; then
echo "Error: your api token starts with 'CLOUDFLARE_API_TOKEN='. Remove that, and instead specify just the token." >&2
exit 1
fi
diff --git a/nixos/modules/services/networking/cloudflared.nix b/nixos/modules/services/networking/cloudflared.nix
index dd03daefe710..0a6a09dabf2b 100644
--- a/nixos/modules/services/networking/cloudflared.nix
+++ b/nixos/modules/services/networking/cloudflared.nix
@@ -369,7 +369,7 @@ in
RuntimeDirectoryMode = "0400";
LoadCredential = [
"credentials.json:${tunnel.credentialsFile}"
- ] ++ (lib.optional (certFile != null) "cert.pem:certFile");
+ ] ++ (lib.optional (certFile != null) "cert.pem:${certFile}");
ExecStart = "${cfg.package}/bin/cloudflared tunnel --config=${mkConfigFile} --no-autoupdate run";
Restart = "on-failure";
diff --git a/nixos/modules/services/networking/coturn.nix b/nixos/modules/services/networking/coturn.nix
index 2c89c3a12fb5..044d3ba0b791 100644
--- a/nixos/modules/services/networking/coturn.nix
+++ b/nixos/modules/services/networking/coturn.nix
@@ -363,7 +363,7 @@ in
chmod 640 ${runConfig}
'';
serviceConfig = rec {
- Type = "simple";
+ Type = "notify";
ExecStart = utils.escapeSystemdExecArgs [
(lib.getExe' pkgs.coturn "turnserver")
"-c"
@@ -413,6 +413,7 @@ in
[
"AF_INET"
"AF_INET6"
+ "AF_UNIX"
]
++ lib.optionals (cfg.listening-ips == [ ]) [
# only used for interface discovery when no listening ips are configured
diff --git a/nixos/modules/services/networking/dhcpcd.nix b/nixos/modules/services/networking/dhcpcd.nix
index 308ae1278a1b..6aecbec7bd4a 100644
--- a/nixos/modules/services/networking/dhcpcd.nix
+++ b/nixos/modules/services/networking/dhcpcd.nix
@@ -69,7 +69,7 @@ let
hostname
# A list of options to request from the DHCP server.
- option domain_name_servers, domain_name, domain_search, host_name
+ option domain_name_servers, domain_name, domain_search
option classless_static_routes, ntp_servers, interface_mtu
# A ServerID is required by RFC2131.
@@ -112,6 +112,7 @@ let
${lib.optionalString (config.networking.enableIPv6 && cfg.IPv6rs == false) ''
noipv6rs
''}
+ ${lib.optionalString cfg.setHostname "option host_name"}
${cfg.extraConfig}
'';
@@ -137,7 +138,7 @@ in
type = lib.types.bool;
default = false;
description = ''
- Whenever to leave interfaces configured on dhcpcd daemon
+ Whether to leave interfaces configured on dhcpcd daemon
shutdown. Set to true if you have your root or store mounted
over the network or this machine accepts SSH connections
through DHCP interfaces and clients should be notified when
@@ -145,6 +146,22 @@ in
'';
};
+ networking.dhcpcd.setHostname = lib.mkOption {
+ type = lib.types.bool;
+ default = true;
+ description = ''
+ Whether to set the machine hostname based on the information
+ received from the DHCP server.
+
+ ::: {.note}
+ The hostname will be changed only if the current one is
+ the empty string, `localhost` or `nixos`.
+
+ Polkit ([](#opt-security.polkit.enable)) is also required.
+ :::
+ '';
+ };
+
networking.dhcpcd.denyInterfaces = lib.mkOption {
type = lib.types.listOf lib.types.str;
default = [ ];
@@ -185,6 +202,15 @@ in
'';
};
+ networking.dhcpcd.allowSetuid = lib.mkOption {
+ type = lib.types.bool;
+ default = false;
+ description = ''
+ Whether to relax the security sandbox to allow running setuid
+ binaries (e.g. `sudo`) in the dhcpcd hooks.
+ '';
+ };
+
networking.dhcpcd.runHook = lib.mkOption {
type = lib.types.lines;
default = "";
@@ -196,7 +222,7 @@ in
::: {.note}
To use sudo or similar tools in your script you may have to set:
- systemd.services.dhcpcd.serviceConfig.NoNewPrivileges = false;
+ networking.dhcpcd.allowSetuid = true;
In addition, as most of the filesystem is inaccessible to dhcpcd
by default, you may want to define some exceptions, e.g.
@@ -263,11 +289,16 @@ in
# dhcpcd. So do a "systemctl restart" instead.
stopIfChanged = false;
- path = [
- dhcpcd
- pkgs.nettools
- config.networking.resolvconf.package
- ];
+ path =
+ [
+ dhcpcd
+ config.networking.resolvconf.package
+ ]
+ ++ lib.optional cfg.setHostname (
+ pkgs.writeShellScriptBin "hostname" ''
+ ${lib.getExe' pkgs.systemd "hostnamectl"} set-hostname --transient $1
+ ''
+ );
unitConfig.ConditionCapability = "CAP_NET_ADMIN";
@@ -299,7 +330,7 @@ in
"CAP_NET_RAW"
"CAP_NET_BIND_SERVICE"
];
- CapabilityBoundingSet = [
+ CapabilityBoundingSet = lib.optionals (!cfg.allowSetuid) [
"CAP_NET_ADMIN"
"CAP_NET_RAW"
"CAP_NET_BIND_SERVICE"
@@ -313,7 +344,7 @@ in
DeviceAllow = "";
LockPersonality = true;
MemoryDenyWriteExecute = true;
- NoNewPrivileges = lib.mkDefault true; # may be disabled for sudo in runHook
+ NoNewPrivileges = lib.mkDefault (!cfg.allowSetuid); # may be disabled for sudo in runHook
PrivateDevices = true;
PrivateMounts = true;
PrivateTmp = true;
@@ -338,15 +369,18 @@ in
RestrictNamespaces = true;
RestrictRealtime = true;
RestrictSUIDSGID = true;
- SystemCallFilter = [
- "@system-service"
- "~@aio"
- "~@keyring"
- "~@memlock"
- "~@mount"
- "~@privileged"
- "~@resources"
- ];
+ SystemCallFilter =
+ [
+ "@system-service"
+ "~@aio"
+ "~@keyring"
+ "~@memlock"
+ "~@mount"
+ ]
+ ++ lib.optionals (!cfg.allowSetuid) [
+ "~@privileged"
+ "~@resources"
+ ];
SystemCallArchitectures = "native";
UMask = "0027";
};
@@ -371,17 +405,27 @@ in
/run/current-system/systemd/bin/systemctl reload dhcpcd.service
'';
- security.polkit.extraConfig = lib.mkIf config.services.resolved.enable ''
- polkit.addRule(function(action, subject) {
- if (action.id == 'org.freedesktop.resolve1.revert' ||
- action.id == 'org.freedesktop.resolve1.set-dns-servers' ||
- action.id == 'org.freedesktop.resolve1.set-domains') {
- if (subject.user == '${config.systemd.services.dhcpcd.serviceConfig.User}') {
- return polkit.Result.YES;
- }
- }
- });
- '';
+ security.polkit.extraConfig = lib.mkMerge [
+ (lib.mkIf config.services.resolved.enable ''
+ polkit.addRule(function(action, subject) {
+ if (action.id == 'org.freedesktop.resolve1.revert' ||
+ action.id == 'org.freedesktop.resolve1.set-dns-servers' ||
+ action.id == 'org.freedesktop.resolve1.set-domains') {
+ if (subject.user == 'dhcpcd') {
+ return polkit.Result.YES;
+ }
+ }
+ });
+ '')
+ (lib.mkIf cfg.setHostname ''
+ polkit.addRule(function(action, subject) {
+ if (action.id == 'org.freedesktop.hostname1.set-hostname' &&
+ subject.user == 'dhcpcd') {
+ return polkit.Result.YES;
+ }
+ });
+ '')
+ ];
};
diff --git a/nixos/modules/services/networking/godns.nix b/nixos/modules/services/networking/godns.nix
new file mode 100644
index 000000000000..e69460ba14c8
--- /dev/null
+++ b/nixos/modules/services/networking/godns.nix
@@ -0,0 +1,84 @@
+{
+ config,
+ lib,
+ pkgs,
+ ...
+}:
+let
+ inherit (lib)
+ mkEnableOption
+ mkIf
+ mkOption
+ mkPackageOption
+ types
+ ;
+
+ cfg = config.services.godns;
+
+ settingsFormat = pkgs.formats.yaml { };
+in
+{
+ options.services.godns = {
+ enable = mkEnableOption "GoDNS service";
+
+ package = mkPackageOption pkgs "godns" { };
+
+ settings = mkOption {
+ type = types.submodule {
+ freeformType = settingsFormat.type;
+ };
+
+ description = ''
+ Configuration for GoDNS. Refer to the [configuration section](1) in the
+ GoDNS GitHub repository for details.
+
+ [1]: https://github.com/TimothyYe/godns?tab=readme-ov-file#configuration
+ '';
+
+ example = {
+ provider = "Cloudflare";
+ login_token_file = "$CREDENTIALS_DIRECTORY/login_token";
+ domains = [
+ {
+ domain_name = "example.com";
+ sub_domains = [ "foo" ];
+ }
+ ];
+ ipv6_urls = [
+ "https://api6.ipify.org"
+ "https://ip2location.io/ip"
+ "https://v6.ipinfo.io/ip"
+ ];
+ ip_type = "IPv6";
+ interval = 300;
+ };
+ };
+
+ loadCredential = lib.mkOption {
+ type = types.listOf types.str;
+ default = [ ];
+ example = [ "login_token:/path/to/login_token" ];
+ description = ''
+ This can be used to pass secrets to the systemd service without adding
+ them to the nix store.
+ '';
+ };
+ };
+
+ config = mkIf cfg.enable {
+ systemd.services.godns = {
+ description = "GoDNS service";
+ wantedBy = [ "multi-user.target" ];
+ after = [ "network.target" ];
+ serviceConfig = {
+ DynamicUser = true;
+ ExecStart = "${lib.getExe cfg.package} -c ${settingsFormat.generate "config.yaml" cfg.settings}";
+ LoadCredential = cfg.loadCredential;
+ Restart = "always";
+ RestartSec = "2s";
+ };
+ };
+ };
+
+ meta.maintainers = [ lib.maintainers.michaelvanstraten ];
+}
diff --git a/nixos/modules/services/networking/headscale.nix b/nixos/modules/services/networking/headscale.nix
index bd17f005f69b..07924f54e369 100644
--- a/nixos/modules/services/networking/headscale.nix
+++ b/nixos/modules/services/networking/headscale.nix
@@ -164,7 +164,7 @@ in
'';
};
- auto_update_enable = lib.mkOption {
+ auto_update_enabled = lib.mkOption {
type = lib.types.bool;
default = true;
description = ''
@@ -493,7 +493,11 @@ in
imports = with lib; [
(mkRenamedOptionModule
[ "services" "headscale" "derp" "autoUpdate" ]
- [ "services" "headscale" "settings" "derp" "auto_update_enable" ]
+ [ "services" "headscale" "settings" "derp" "auto_update_enabled" ]
+ )
+ (mkRenamedOptionModule
+ [ "services" "headscale" "derp" "auto_update_enable" ]
+ [ "services" "headscale" "settings" "derp" "auto_update_enabled" ]
)
(mkRenamedOptionModule
[ "services" "headscale" "derp" "paths" ]
@@ -629,6 +633,7 @@ in
in
{
Restart = "always";
+ RestartSec = "5s";
Type = "simple";
User = cfg.user;
Group = cfg.group;
diff --git a/nixos/modules/services/networking/ircd-hybrid/.editorconfig b/nixos/modules/services/networking/ircd-hybrid/.editorconfig
new file mode 100644
index 000000000000..de54d884adbd
--- /dev/null
+++ b/nixos/modules/services/networking/ircd-hybrid/.editorconfig
@@ -0,0 +1,2 @@
+[*.{conf,in}]
+trim_trailing_whitespace = unset
diff --git a/nixos/modules/services/networking/iwd.nix b/nixos/modules/services/networking/iwd.nix
index 2209841678b3..e275e7906ad5 100644
--- a/nixos/modules/services/networking/iwd.nix
+++ b/nixos/modules/services/networking/iwd.nix
@@ -20,7 +20,7 @@ let
defaults = {
# without UseDefaultInterface, sometimes wlan0 simply goes AWOL with NetworkManager
# https://iwd.wiki.kernel.org/interface_lifecycle#interface_management_in_iwd
- General.UseDefaultInterface =
+ DriverQuirks.UseDefaultInterface =
with config.networking.networkmanager;
(enable && (wifi.backend == "iwd"));
};
@@ -61,6 +61,12 @@ in
Only one wireless daemon is allowed at the time: networking.wireless.enable and networking.wireless.iwd.enable are mutually exclusive.
'';
}
+ {
+ assertion = !(cfg.settings ? General && cfg.settings.General ? UseDefaultInterface);
+ message = ''
+ `networking.wireless.iwd.settings.General.UseDefaultInterface` has been deprecated. Use `networking.wireless.iwd.settings.DriverQuirks.UseDefaultInterface` instead.
+ '';
+ }
];
environment.etc."iwd/${configFile.name}".source = configFile;
diff --git a/nixos/modules/services/networking/kismet.nix b/nixos/modules/services/networking/kismet.nix
new file mode 100644
index 000000000000..4e14e9fd51d4
--- /dev/null
+++ b/nixos/modules/services/networking/kismet.nix
@@ -0,0 +1,459 @@
+{
+ config,
+ lib,
+ pkgs,
+ ...
+}:
+
+let
+ inherit (lib.trivial) isFloat isInt isBool;
+ inherit (lib.modules) mkIf;
+ inherit (lib.options)
+ literalExpression
+ mkOption
+ mkPackageOption
+ mkEnableOption
+ ;
+ inherit (lib.strings)
+ isString
+ escapeShellArg
+ escapeShellArgs
+ concatMapStringsSep
+ concatMapAttrsStringSep
+ replaceStrings
+ substring
+ stringLength
+ hasInfix
+ hasSuffix
+ typeOf
+ match
+ ;
+ inherit (lib.lists) all isList flatten;
+ inherit (lib.attrsets)
+ attrsToList
+ filterAttrs
+ optionalAttrs
+ mapAttrs'
+ mapAttrsToList
+ nameValuePair
+ ;
+ inherit (lib.generators) toKeyValue;
+ inherit (lib) types;
+
+ # Deeply checks types for a given type function. Calls `override` with type and value.
+ deep =
+ func: override: type:
+ let
+ prev = func type;
+ in
+ prev
+ // {
+ check = value: prev.check value && (override type value);
+ };
+
+ # Deep listOf.
+ listOf' = deep types.listOf (type: value: all type.check value);
+
+ # Deep attrsOf.
+ attrsOf' = deep types.attrsOf (type: value: all (item: type.check item.value) (attrsToList value));
+
+ # Kismet config atoms.
+ atom =
+ with types;
+ oneOf [
+ number
+ bool
+ str
+ ];
+
+ # Composite types.
+ listOfAtom = listOf' atom;
+ atomOrList = with types; either atom listOfAtom;
+ lists = listOf' atomOrList;
+ kvPair = attrsOf' atomOrList;
+ kvPairs = listOf' kvPair;
+
+ # Options that eval to a string with a header (foo:key=value)
+ headerKvPair = attrsOf' (attrsOf' atomOrList);
+ headerKvPairs = attrsOf' (listOf' (attrsOf' atomOrList));
+
+ # Toplevel config type.
+ topLevel =
+ let
+ topLevel' =
+ with types;
+ oneOf [
+ headerKvPairs
+ headerKvPair
+ kvPairs
+ kvPair
+ listOfAtom
+ lists
+ atom
+ ];
+ in
+ topLevel'
+ // {
+ description = "Kismet config stanza";
+ };
+
+ # Throws invalid.
+ invalid = atom: throw "invalid value '${toString atom}' of type '${typeOf atom}'";
+
+ # Converts an atom.
+ mkAtom =
+ atom:
+ if isString atom then
+ if hasInfix "\"" atom || hasInfix "," atom then
+ ''"${replaceStrings [ ''"'' ] [ ''\"'' ] atom}"''
+ else
+ atom
+ else if isFloat atom || isInt atom || isBool atom then
+ toString atom
+ else
+ invalid atom;
+
+ # Converts an inline atom or list to a string.
+ mkAtomOrListInline =
+ atomOrList:
+ if isList atomOrList then
+ mkAtom "${concatMapStringsSep "," mkAtom atomOrList}"
+ else
+ mkAtom atomOrList;
+
+ # Converts an out of line atom or list to a string.
+ mkAtomOrList =
+ atomOrList:
+ if isList atomOrList then
+ "${concatMapStringsSep "," mkAtomOrListInline atomOrList}"
+ else
+ mkAtom atomOrList;
+
+ # Throws if the string matches the given regex.
+ deny =
+ regex: str:
+ assert (match regex str) == null;
+ str;
+
+ # Converts a set of k/v pairs.
+ convertKv = concatMapAttrsStringSep "," (
+ name: value: "${mkAtom (deny "=" name)}=${mkAtomOrListInline value}"
+ );
+
+ # Converts k/v pairs with a header.
+ convertKvWithHeader = header: attrs: "${mkAtom (deny ":" header)}:${convertKv attrs}";
+
+ # Converts the entire config.
+ convertConfig = mapAttrs' (
+ name: value:
+ let
+ # Convert foo' into 'foo+' for support for '+=' syntax.
+ newName = if hasSuffix "'" name then substring 0 (stringLength name - 1) name + "+" else name;
+
+ # Get the stringified value.
+ newValue =
+ if headerKvPairs.check value then
+ flatten (
+ mapAttrsToList (header: values: (map (value: convertKvWithHeader header value) values)) value
+ )
+ else if headerKvPair.check value then
+ mapAttrsToList convertKvWithHeader value
+ else if kvPairs.check value then
+ map convertKv value
+ else if kvPair.check value then
+ convertKv value
+ else if listOfAtom.check value then
+ mkAtomOrList value
+ else if lists.check value then
+ map mkAtomOrList value
+ else if atom.check value then
+ mkAtom value
+ else
+ invalid value;
+ in
+ nameValuePair newName newValue
+ );
+
+ mkKismetConf =
+ options:
+ (toKeyValue { listsAsDuplicateKeys = true; }) (
+ filterAttrs (_: value: value != null) (convertConfig options)
+ );
+
+ cfg = config.services.kismet;
+in
+{
+ options.services.kismet = {
+ enable = mkEnableOption "kismet";
+ package = mkPackageOption pkgs "kismet" { };
+ user = mkOption {
+ description = "The user to run Kismet as.";
+ type = types.str;
+ default = "kismet";
+ };
+ group = mkOption {
+ description = "The group to run Kismet as.";
+ type = types.str;
+ default = "kismet";
+ };
+ serverName = mkOption {
+ description = "The name of the server.";
+ type = types.str;
+ default = "Kismet";
+ };
+ serverDescription = mkOption {
+ description = "The description of the server.";
+ type = types.str;
+ default = "NixOS Kismet server";
+ };
+ logTypes = mkOption {
+ description = "The log types.";
+ type = with types; listOf str;
+ default = [ "kismet" ];
+ };
+ dataDir = mkOption {
+ description = "The Kismet data directory.";
+ type = types.path;
+ default = "/var/lib/kismet";
+ };
+ httpd = {
+ enable = mkOption {
+ description = "True to enable the HTTP server.";
+ type = types.bool;
+ default = false;
+ };
+ address = mkOption {
+ description = "The address to listen on. Note that this cannot be a hostname or Kismet will not start.";
+ type = types.str;
+ default = "127.0.0.1";
+ };
+ port = mkOption {
+ description = "The port to listen on.";
+ type = types.port;
+ default = 2501;
+ };
+ };
+ settings = mkOption {
+ description = ''
+ Options for Kismet. See:
+ https://www.kismetwireless.net/docs/readme/configuring/configfiles/
+ '';
+ default = { };
+ type = with types; attrsOf topLevel;
+ example = literalExpression ''
+ {
+ /* Examples for atoms */
+ # dot11_link_bssts=false
+ dot11_link_bssts = false; # Boolean
+
+ # dot11_related_bss_window=10000000
+ dot11_related_bss_window = 10000000; # Integer
+
+ # devicefound=00:11:22:33:44:55
+ devicefound = "00:11:22:33:44:55"; # String
+
+ # log_types+=wiglecsv
+ log_types' = "wiglecsv";
+
+ /* Examples for lists of atoms */
+ # wepkey=00:DE:AD:C0:DE:00,FEEDFACE42
+ wepkey = [ "00:DE:AD:C0:DE:00" "FEEDFACE42" ];
+
+ # alert=ADHOCCONFLICT,5/min,1/sec
+ # alert=ADVCRYPTCHANGE,5/min,1/sec
+ alert = [
+ [ "ADHOCCONFLICT" "5/min" "1/sec" ]
+ [ "ADVCRYPTCHANGE" "5/min" "1/sec" ]
+ ];
+
+ /* Examples for sets of atoms */
+ # source=wlan0:name=ath11k
+ source.wlan0 = { name = "ath11k"; };
+
+ /* Examples with colon-suffixed headers */
+ # gps=gpsd:host=localhost,port=2947
+ gps.gpsd = {
+ host = "localhost";
+ port = 2947;
+ };
+
+ # apspoof=Foo1:ssid=Bar1,validmacs="00:11:22:33:44:55,aa:bb:cc:dd:ee:ff"
+ # apspoof=Foo1:ssid=Bar2,validmacs="01:12:23:34:45:56,ab:bc:cd:de:ef:f0"
+ # apspoof=Foo2:ssid=Baz1,validmacs="11:22:33:44:55:66,bb:cc:dd:ee:ff:00"
+ apspoof.Foo1 = [
+ { ssid = "Bar1"; validmacs = [ "00:11:22:33:44:55" "aa:bb:cc:dd:ee:ff" ]; }
+ { ssid = "Bar2"; validmacs = [ "01:12:23:34:45:56" "ab:bc:cd:de:ef:f0" ]; }
+ ];
+
+ # because Foo1 is a list, Foo2 needs to be as well
+ apspoof.Foo2 = [
+ {
+ ssid = "Bar2";
+ validmacs = [ "00:11:22:33:44:55" "aa:bb:cc:dd:ee:ff" ];
+ };
+ ];
+ }
+ '';
+ };
+ extraConfig = mkOption {
+ description = ''
+ Literal Kismet config lines appended to the site config.
+ Note that `services.kismet.settings` allows you to define
+ all options here using Nix attribute sets.
+ '';
+ default = "";
+ type = types.str;
+ example = ''
+ # Looks like the following in `services.kismet.settings`:
+ # wepkey = [ "00:DE:AD:C0:DE:00" "FEEDFACE42" ];
+ wepkey=00:DE:AD:C0:DE:00,FEEDFACE42
+ '';
+ };
+ };
+
+ config =
+ let
+ configDir = "${cfg.dataDir}/.kismet";
+ settings =
+ cfg.settings
+ // {
+ server_name = cfg.serverName;
+ server_description = cfg.serverDescription;
+ logging_enabled = cfg.logTypes != [ ];
+ log_types = cfg.logTypes;
+ }
+ // optionalAttrs cfg.httpd.enable {
+ httpd_bind_address = cfg.httpd.address;
+ httpd_port = cfg.httpd.port;
+ httpd_auth_file = "${configDir}/kismet_httpd.conf";
+ httpd_home = "${cfg.package}/share/kismet/httpd";
+ };
+ in
+ mkIf cfg.enable {
+ systemd.tmpfiles.settings = {
+ "10-kismet" = {
+ ${cfg.dataDir} = {
+ d = {
+ inherit (cfg) user group;
+ mode = "0750";
+ };
+ };
+ ${configDir} = {
+ d = {
+ inherit (cfg) user group;
+ mode = "0750";
+ };
+ };
+ };
+ };
+ systemd.services.kismet =
+ let
+ kismetConf = pkgs.writeText "kismet.conf" ''
+ ${mkKismetConf settings}
+ ${cfg.extraConfig}
+ '';
+ in
+ {
+ description = "Kismet monitoring service";
+ wants = [ "basic.target" ];
+ after = [
+ "basic.target"
+ "network.target"
+ ];
+ wantedBy = [ "multi-user.target" ];
+ serviceConfig =
+ let
+ capabilities = [
+ "CAP_NET_ADMIN"
+ "CAP_NET_RAW"
+ ];
+ kismetPreStart = pkgs.writeShellScript "kismet-pre-start" ''
+ owner=${escapeShellArg "${cfg.user}:${cfg.group}"}
+ mkdir -p ~/.kismet
+
+ # Ensure permissions on directories Kismet uses.
+ chown "$owner" ~/ ~/.kismet
+ cd ~/.kismet
+
+ package=${cfg.package}
+ if [ -d "$package/etc" ]; then
+ for file in "$package/etc"/*.conf; do
+ # Symlink the config files if they exist or are already a link.
+ base="''${file##*/}"
+ if [ ! -f "$base" ] || [ -L "$base" ]; then
+ ln -sf "$file" "$base"
+ fi
+ done
+ fi
+
+ for file in kismet_httpd.conf; do
+ # Un-symlink these files.
+ if [ -L "$file" ]; then
+ cp "$file" ".$file"
+ rm -f "$file"
+ mv ".$file" "$file"
+ chmod 0640 "$file"
+ chown "$owner" "$file"
+ fi
+ done
+
+ # Link the site config.
+ ln -sf ${kismetConf} kismet_site.conf
+ '';
+ in
+ {
+ Type = "simple";
+ ExecStart = escapeShellArgs [
+ "${cfg.package}/bin/kismet"
+ "--homedir"
+ cfg.dataDir
+ "--confdir"
+ configDir
+ "--datadir"
+ "${cfg.package}/share"
+ "--no-ncurses"
+ "-f"
+ "${configDir}/kismet.conf"
+ ];
+ WorkingDirectory = cfg.dataDir;
+ ExecStartPre = "+${kismetPreStart}";
+ Restart = "always";
+ KillMode = "control-group";
+ CapabilityBoundingSet = capabilities;
+ AmbientCapabilities = capabilities;
+ LockPersonality = true;
+ NoNewPrivileges = true;
+ PrivateDevices = false;
+ PrivateTmp = true;
+ PrivateUsers = false;
+ ProtectClock = true;
+ ProtectControlGroups = true;
+ ProtectHome = true;
+ ProtectHostname = true;
+ ProtectKernelLogs = true;
+ ProtectKernelModules = true;
+ ProtectKernelTunables = true;
+ ProtectProc = "invisible";
+ ProtectSystem = "full";
+ RestrictNamespaces = true;
+ RestrictSUIDSGID = true;
+ User = cfg.user;
+ Group = cfg.group;
+ UMask = "0007";
+ TimeoutStopSec = 30;
+ };
+
+ # Allow it to restart if the wifi interface is not up
+ unitConfig.StartLimitIntervalSec = 5;
+ };
+ users.groups.${cfg.group} = { };
+ users.users.${cfg.user} = {
+ inherit (cfg) group;
+ description = "User for running Kismet";
+ isSystemUser = true;
+ home = cfg.dataDir;
+ };
+ };
+
+ meta.maintainers = with lib.maintainers; [ numinit ];
+}
diff --git a/nixos/modules/services/networking/kresd.nix b/nixos/modules/services/networking/kresd.nix
index c93b591701a7..40440fbd16cc 100644
--- a/nixos/modules/services/networking/kresd.nix
+++ b/nixos/modules/services/networking/kresd.nix
@@ -68,8 +68,7 @@ in
description = ''
Whether to enable knot-resolver domain name server.
DNSSEC validation is turned on by default.
- You can run `sudo nc -U /run/knot-resolver/control/1`
- and give commands interactively to kresd@1.service.
+ You can run `kresd-cli 1` and give commands interactively to kresd@1.service.
'';
};
package = lib.mkPackageOption pkgs "knot-resolver" {
@@ -135,7 +134,25 @@ in
###### implementation
config = lib.mkIf cfg.enable {
- environment.etc."knot-resolver/kresd.conf".source = configFile; # not required
+ environment = {
+ etc."knot-resolver/kresd.conf".source = configFile; # not required
+ systemPackages = [
+ (pkgs.writeShellScriptBin "kresd-cli" ''
+ if [[ ''${1:-} == -h || ''${1:-} == --help ]]; then
+ echo "Usage: $0 [X]"
+ echo
+ echo " X is number of the control socket and corresponds to the number of the template unit."
+ exit
+ fi
+
+ exec=exec
+ if [[ "$USER" != knot-resolver ]]; then
+ exec='exec /run/wrappers/bin/sudo -u knot-resolver'
+ fi
+ $exec ${lib.getExe pkgs.socat} - /run/knot-resolver/control/''${1:-1}
+ '')
+ ];
+ };
networking.resolvconf.useLocalResolver = lib.mkDefault true;
diff --git a/nixos/modules/services/networking/livekit.nix b/nixos/modules/services/networking/livekit.nix
new file mode 100644
index 000000000000..523e84094ebd
--- /dev/null
+++ b/nixos/modules/services/networking/livekit.nix
@@ -0,0 +1,140 @@
+{
+ config,
+ lib,
+ pkgs,
+ utils,
+ ...
+}:
+let
+ cfg = config.services.livekit;
+ format = pkgs.formats.json { };
+in
+{
+ meta.maintainers = with lib.maintainers; [ quadradical ];
+ options.services.livekit = {
+ enable = lib.mkEnableOption "the livekit server";
+ package = lib.mkPackageOption pkgs "livekit" { };
+
+ keyFile = lib.mkOption {
+ type = lib.types.path;
+ description = ''
+ LiveKit key file holding one or multiple application secrets. Use `livekit-server generate-keys` to generate a random key name and secret.
+
+ The file should have the format `: `.
+ Example:
+ `lk-jwt-service: f6lQGaHtM5HfgZjIcec3cOCRfiDqIine4CpZZnqdT5cE`
+
+ Individual key/secret pairs need to be passed to clients to connect to this instance.
+ '';
+ };
+
+ openFirewall = lib.mkOption {
+ type = lib.types.bool;
+ default = false;
+ description = "Opens port range for LiveKit on the firewall.";
+ };
+
+ settings = lib.mkOption {
+ type = lib.types.submodule {
+ freeformType = format.type;
+ options = {
+ port = lib.mkOption {
+ type = lib.types.port;
+ default = 7880;
+ description = "Main TCP port for RoomService and RTC endpoint.";
+ };
+
+ rtc = {
+ port_range_start = lib.mkOption {
+ type = lib.types.int;
+ default = 50000;
+ description = "Start of UDP port range for WebRTC";
+ };
+
+ port_range_end = lib.mkOption {
+ type = lib.types.int;
+ default = 51000;
+ description = "End of UDP port range for WebRTC";
+ };
+
+ use_external_ip = lib.mkOption {
+ type = lib.types.bool;
+ default = false;
+ description = ''
+ When set to true, attempts to discover the host's public IP via STUN.
+ This is useful for cloud environments such as AWS & Google where hosts have an internal IP that maps to an external one.
+ '';
+ };
+ };
+ };
+ };
+ default = { };
+ description = ''
+ LiveKit configuration file expressed in nix.
+
+ For an example configuration, see .
+ For all possible values, see .
+ '';
+ };
+ };
+
+ config = lib.mkIf cfg.enable {
+ networking.firewall = lib.mkIf cfg.openFirewall {
+ allowedTCPPorts = [
+ cfg.settings.port
+ ];
+ allowedUDPPortRanges = [
+ {
+ from = cfg.settings.rtc.port_range_start;
+ to = cfg.settings.rtc.port_range_end;
+ }
+ ];
+ };
+
+ systemd.services.livekit = {
+ description = "LiveKit SFU server";
+ documentation = [ "https://docs.livekit.io" ];
+ wantedBy = [ "multi-user.target" ];
+ wants = [ "network-online.target" ];
+ after = [ "network-online.target" ];
+
+ serviceConfig = {
+ LoadCredential = [ "livekit-secrets:${cfg.keyFile}" ];
+ ExecStart = utils.escapeSystemdExecArgs [
+ (lib.getExe cfg.package)
+ "--config=${format.generate "livekit.json" cfg.settings}"
+ "--key-file=/run/credentials/livekit.service/livekit-secrets"
+ ];
+ DynamicUser = true;
+ LockPersonality = true;
+ MemoryDenyWriteExecute = true;
+ ProtectClock = true;
+ ProtectControlGroups = true;
+ ProtectHostname = true;
+ ProtectKernelLogs = true;
+ ProtectKernelModules = true;
+ ProtectKernelTunables = true;
+ PrivateDevices = true;
+ PrivateMounts = true;
+ PrivateUsers = true;
+ RestrictAddressFamilies = [
+ "AF_INET"
+ "AF_INET6"
+ "AF_NETLINK"
+ ];
+ RestrictNamespaces = true;
+ RestrictRealtime = true;
+ ProtectHome = true;
+ SystemCallArchitectures = "native";
+ SystemCallFilter = [
+ "@system-service"
+ "~@privileged"
+ "~@resources"
+ ];
+ Restart = "on-failure";
+ RestartSec = 5;
+ UMask = "077";
+ };
+ };
+ };
+}
diff --git a/nixos/modules/services/networking/mycelium.nix b/nixos/modules/services/networking/mycelium.nix
index 26c9be290fed..7ba506ef25eb 100644
--- a/nixos/modules/services/networking/mycelium.nix
+++ b/nixos/modules/services/networking/mycelium.nix
@@ -73,7 +73,13 @@ in
systemd.services.mycelium = {
description = "Mycelium network";
- after = [ "network.target" ];
+ after = [
+ "network.target"
+ "network-online.target"
+ ];
+ wants = [
+ "network-online.target"
+ ];
wantedBy = [ "multi-user.target" ];
restartTriggers = [
cfg.keyFile
diff --git a/nixos/modules/services/networking/ncps.nix b/nixos/modules/services/networking/ncps.nix
index 12b51bf05217..d29a24445ae7 100644
--- a/nixos/modules/services/networking/ncps.nix
+++ b/nixos/modules/services/networking/ncps.nix
@@ -206,11 +206,6 @@ in
assertion = cfg.cache.lru.schedule == null || cfg.cache.maxSize != null;
message = "You must specify config.ncps.cache.lru.schedule when config.ncps.cache.maxSize is set";
}
-
- {
- assertion = cfg.cache.secretKeyPath == null || (builtins.pathExists cfg.cache.secretKeyPath);
- message = "config.ncps.cache.secresecretKeyPath=${cfg.cache.secretKeyPath} must exist but does not";
- }
];
users.users.ncps = {
@@ -245,7 +240,8 @@ in
systemd.services.ncps = {
description = "ncps binary cache proxy service";
- after = [ "network.target" ];
+ after = [ "network-online.target" ];
+ wants = [ "network-online.target" ];
wantedBy = [ "multi-user.target" ];
preStart = ''
diff --git a/nixos/modules/services/networking/nebula.nix b/nixos/modules/services/networking/nebula.nix
index 35d7fafb43b5..36122d45c128 100644
--- a/nixos/modules/services/networking/nebula.nix
+++ b/nixos/modules/services/networking/nebula.nix
@@ -84,6 +84,28 @@ in
description = "Whether this node is a relay.";
};
+ lighthouse.dns.enable = lib.mkOption {
+ type = lib.types.bool;
+ default = false;
+ description = "Whether this lighthouse node should serve DNS.";
+ };
+
+ lighthouse.dns.host = lib.mkOption {
+ type = lib.types.str;
+ default = "localhost";
+ description = ''
+ IP address on which nebula lighthouse should serve DNS.
+ 'localhost' is a good default to ensure the service does not listen on public interfaces;
+ use a Nebula address like 10.0.0.5 to make DNS resolution available to nebula hosts only.
+ '';
+ };
+
+ lighthouse.dns.port = lib.mkOption {
+ type = lib.types.nullOr lib.types.port;
+ default = 5353;
+ description = "UDP port number for lighthouse DNS server.";
+ };
+
lighthouses = lib.mkOption {
type = lib.types.listOf lib.types.str;
default = [ ];
@@ -172,10 +194,7 @@ in
'';
example = lib.literalExpression ''
{
- lighthouse.dns = {
- host = "0.0.0.0";
- port = 53;
- };
+ lighthouse.interval = 15;
}
'';
};
@@ -203,6 +222,9 @@ in
lighthouse = {
am_lighthouse = netCfg.isLighthouse;
hosts = netCfg.lighthouses;
+ serve_dns = netCfg.lighthouse.dns.enable;
+ dns.host = netCfg.lighthouse.dns.host;
+ dns.port = netCfg.lighthouse.dns.port;
};
relay = {
am_relay = netCfg.isRelay;
@@ -231,6 +253,19 @@ in
''
settings
);
+ capabilities =
+ let
+ nebulaPort = if !settings.tun.disabled then settings.listen.port else 0;
+ dnsPort = if settings.lighthouse.serve_dns then settings.lighthouse.dns.port else 0;
+ in
+ lib.concatStringsSep " " (
+ # creation of tunnel interfaces
+ lib.optional (!settings.tun.disabled) "CAP_NET_ADMIN"
+ # binding to privileged ports
+ ++ lib.optional (
+ nebulaPort > 0 && nebulaPort < 1024 || dnsPort > 0 && dnsPort < 1024
+ ) "CAP_NET_BIND_SERVICE"
+ );
in
{
# Create the systemd service for Nebula.
@@ -248,8 +283,8 @@ in
Restart = "always";
ExecStart = "${netCfg.package}/bin/nebula -config ${configFile}";
UMask = "0027";
- CapabilityBoundingSet = "CAP_NET_ADMIN";
- AmbientCapabilities = "CAP_NET_ADMIN";
+ CapabilityBoundingSet = capabilities;
+ AmbientCapabilities = capabilities;
LockPersonality = true;
NoNewPrivileges = true;
PrivateDevices = false; # needs access to /dev/net/tun (below)
@@ -302,5 +337,8 @@ in
);
};
- meta.maintainers = with lib.maintainers; [ numinit ];
+ meta.maintainers = with lib.maintainers; [
+ numinit
+ siriobalmelli
+ ];
}
diff --git a/nixos/modules/services/networking/networkd-dispatcher.nix b/nixos/modules/services/networking/networkd-dispatcher.nix
index 99b56609a70f..eb874447bb3a 100644
--- a/nixos/modules/services/networking/networkd-dispatcher.nix
+++ b/nixos/modules/services/networking/networkd-dispatcher.nix
@@ -103,21 +103,29 @@ in
services.networkd-dispatcher.extraArgs =
let
- scriptDir = pkgs.symlinkJoin {
- name = "networkd-dispatcher-script-dir";
- paths = lib.mapAttrsToList (
- name: cfg:
- (map (
- state:
- pkgs.writeTextFile {
- inherit name;
- text = cfg.script;
- destination = "/${state}.d/${name}";
- executable = true;
- }
- ) cfg.onState)
- ) cfg.rules;
- };
+ scriptDir = pkgs.runCommand "networkd-dispatcher-script-dir" { } (
+ ''
+ mkdir $out
+ ''
+ + (lib.concatStrings (
+ lib.mapAttrsToList (
+ name: cfg:
+ (lib.concatStrings (
+ map (state: ''
+ mkdir -p $out/${state}.d
+ ln -s ${
+ lib.getExe (
+ pkgs.writeShellApplication {
+ inherit name;
+ text = cfg.script;
+ }
+ )
+ } $out/${state}.d/${name}
+ '') cfg.onState
+ ))
+ ) cfg.rules
+ ))
+ );
in
[
"--verbose"
diff --git a/nixos/modules/services/networking/ntp/chrony.nix b/nixos/modules/services/networking/ntp/chrony.nix
index a8c4c3885a71..3a9d911b8048 100644
--- a/nixos/modules/services/networking/ntp/chrony.nix
+++ b/nixos/modules/services/networking/ntp/chrony.nix
@@ -180,12 +180,12 @@ in
};
};
- config = mkIf cfg.enable {
- meta.maintainers = with lib.maintainers; [
- thoughtpolice
- vifino
- ];
+ meta.maintainers = with lib.maintainers; [
+ thoughtpolice
+ vifino
+ ];
+ config = mkIf cfg.enable {
environment.systemPackages = [ chronyPkg ];
users.groups.chrony.gid = config.ids.gids.chrony;
diff --git a/nixos/modules/services/networking/ntp/ntpd-rs.nix b/nixos/modules/services/networking/ntp/ntpd-rs.nix
index 557bf05fbc42..14287ded9abf 100644
--- a/nixos/modules/services/networking/ntp/ntpd-rs.nix
+++ b/nixos/modules/services/networking/ntp/ntpd-rs.nix
@@ -63,7 +63,7 @@ in
};
source = lib.mkIf cfg.useNetworkingTimeServers (
map (ts: {
- mode = "server";
+ mode = if lib.strings.hasInfix "pool" ts then "pool" else "server";
address = ts;
}) config.networking.timeServers
);
diff --git a/nixos/modules/services/networking/ntp/ntpd.nix b/nixos/modules/services/networking/ntp/ntpd.nix
index 84f79df52b0e..6debe11753f9 100644
--- a/nixos/modules/services/networking/ntp/ntpd.nix
+++ b/nixos/modules/services/networking/ntp/ntpd.nix
@@ -126,9 +126,9 @@ in
###### implementation
- config = mkIf config.services.ntp.enable {
- meta.maintainers = with lib.maintainers; [ thoughtpolice ];
+ meta.maintainers = with lib.maintainers; [ thoughtpolice ];
+ config = mkIf config.services.ntp.enable {
# Make tools such as ntpq available in the system path.
environment.systemPackages = [ pkgs.ntp ];
services.timesyncd.enable = mkForce false;
diff --git a/nixos/modules/services/networking/ntp/openntpd.nix b/nixos/modules/services/networking/ntp/openntpd.nix
index c4ad630826b5..8d7eebde8cdc 100644
--- a/nixos/modules/services/networking/ntp/openntpd.nix
+++ b/nixos/modules/services/networking/ntp/openntpd.nix
@@ -58,8 +58,9 @@ in
###### implementation
+ meta.maintainers = with lib.maintainers; [ thoughtpolice ];
+
config = mkIf cfg.enable {
- meta.maintainers = with lib.maintainers; [ thoughtpolice ];
services.timesyncd.enable = mkForce false;
# Add ntpctl to the environment for status checking
diff --git a/nixos/modules/services/networking/pdns-recursor.nix b/nixos/modules/services/networking/pdns-recursor.nix
index 3b554c7e31a1..42f609128b95 100644
--- a/nixos/modules/services/networking/pdns-recursor.nix
+++ b/nixos/modules/services/networking/pdns-recursor.nix
@@ -38,12 +38,34 @@ let
else
"";
- configDir = pkgs.writeTextDir "recursor.conf" (
- concatStringsSep "\n" (flip mapAttrsToList cfg.settings (name: val: "${name}=${serialize val}"))
- );
+ settingsFormat = pkgs.formats.yaml { };
mkDefaultAttrs = mapAttrs (n: v: mkDefault v);
+ mkForwardZone = mapAttrsToList (
+ zone: uri: {
+ inherit zone;
+ forwarders = [ uri ];
+ }
+ );
+
+ configFile =
+ if cfg.old-settings != { } then
+ # Convert recursor.conf to recursor.yml and merge it
+ let
+ conf = pkgs.writeText "recursor.conf" (
+ concatStringsSep "\n" (mapAttrsToList (name: val: "${name}=${serialize val}") cfg.old-settings)
+ );
+
+ yaml = settingsFormat.generate "recursor.yml" cfg.yaml-settings;
+ in
+ pkgs.runCommand "recursor-merged.yml" { } ''
+ ${pkgs.pdns-recursor}/bin/rec_control show-yaml --config ${conf} > override.yml
+ ${pkgs.yq-go}/bin/yq '. *= load("override.yml")' ${yaml} > $out
+ ''
+ else
+ settingsFormat.generate "recursor.yml" cfg.yaml-settings;
+
in
{
options.services.pdns-recursor = {
@@ -175,7 +197,7 @@ in
'';
};
- settings = mkOption {
+ old-settings = mkOption {
type = configType;
default = { };
example = literalExpression ''
@@ -184,11 +206,34 @@ in
log-common-errors = true;
}
'';
+ description = ''
+ Older PowerDNS Recursor settings. Use this option to configure
+ Recursor settings not exposed in a NixOS option or to bypass one.
+ See the full documentation at
+
+ for the available options.
+
+ ::: {.warning}
+ This option is provided for backward compatibility only
+ and will be removed in the next release of NixOS.
+ :::
+ '';
+ };
+
+ yaml-settings = mkOption {
+ type = settingsFormat.type;
+ default = { };
+ example = literalExpression ''
+ {
+ loglevel = 8;
+ log-common-errors = true;
+ }
+ '';
description = ''
PowerDNS Recursor settings. Use this option to configure Recursor
settings not exposed in a NixOS option or to bypass one.
See the full documentation at
-
+
for the available options.
'';
};
@@ -205,42 +250,44 @@ in
config = mkIf cfg.enable {
- environment.etc."pdns-recursor".source = configDir;
+ environment.etc."/pdns-recursor/recursor.yml".source = configFile;
- services.pdns-recursor.settings = mkDefaultAttrs {
- local-address = cfg.dns.address;
- local-port = cfg.dns.port;
- allow-from = cfg.dns.allowFrom;
+ services.pdns-recursor.yaml-settings = {
+ incoming = mkDefaultAttrs {
+ listen = cfg.dns.address;
+ port = cfg.dns.port;
+ allow_from = cfg.dns.allowFrom;
+ };
- webserver-address = cfg.api.address;
- webserver-port = cfg.api.port;
- webserver-allow-from = cfg.api.allowFrom;
+ webservice = mkDefaultAttrs {
+ address = cfg.api.address;
+ port = cfg.api.port;
+ allow_from = cfg.api.allowFrom;
+ };
- forward-zones = mapAttrsToList (zone: uri: "${zone}.=${uri}") cfg.forwardZones;
- forward-zones-recurse = mapAttrsToList (zone: uri: "${zone}.=${uri}") cfg.forwardZonesRecurse;
- export-etc-hosts = cfg.exportHosts;
- dnssec = cfg.dnssecValidation;
- serve-rfc1918 = cfg.serveRFC1918;
- lua-config-file = pkgs.writeText "recursor.lua" cfg.luaConfig;
+ recursor = mkDefaultAttrs {
+ forward_zones = mkForwardZone cfg.forwardZones;
+ forward_zones_recurse = mkForwardZone cfg.forwardZonesRecurse;
+ export_etc_hosts = cfg.exportHosts;
+ serve_rfc1918 = cfg.serveRFC1918;
+ lua_config_file = pkgs.writeText "recursor.lua" cfg.luaConfig;
+ daemon = false;
+ write_pid = false;
+ };
- daemon = false;
- write-pid = false;
- log-timestamp = false;
- disable-syslog = true;
+ dnssec = mkDefaultAttrs {
+ validation = cfg.dnssecValidation;
+ };
+
+ logging = mkDefaultAttrs {
+ timestamp = false;
+ disable_syslog = true;
+ };
};
systemd.packages = [ pkgs.pdns-recursor ];
- systemd.services.pdns-recursor = {
- wantedBy = [ "multi-user.target" ];
-
- serviceConfig = {
- ExecStart = [
- ""
- "${pkgs.pdns-recursor}/bin/pdns_recursor --config-dir=${configDir}"
- ];
- };
- };
+ systemd.services.pdns-recursor.wantedBy = [ "multi-user.target" ];
users.users.pdns-recursor = {
isSystemUser = true;
@@ -250,6 +297,15 @@ in
users.groups.pdns-recursor = { };
+ warnings = lib.optional (cfg.old-settings != { }) ''
+ pdns-recursor has changed its configuration file format from pdns-recursor.conf
+ (mapped to `services.pdns-recursor.old-settings`) to the newer pdns-recursor.yml
+ (mapped to `services.pdns-recursor.yaml-settings`).
+
+ Support for the older format will be removed in a future version, so please migrate
+ your settings over. See .
+ '';
+
};
imports = [
@@ -258,6 +314,19 @@ in
"pdns-recursor"
"extraConfig"
] "To change extra Recursor settings use services.pdns-recursor.settings instead.")
+
+ (mkRenamedOptionModule
+ [
+ "services"
+ "pdns-recursor"
+ "settings"
+ ]
+ [
+ "services"
+ "pdns-recursor"
+ "old-settings"
+ ]
+ )
];
meta.maintainers = with lib.maintainers; [ rnhmjoj ];
diff --git a/nixos/modules/services/networking/polipo.nix b/nixos/modules/services/networking/polipo.nix
deleted file mode 100644
index 67f83358312e..000000000000
--- a/nixos/modules/services/networking/polipo.nix
+++ /dev/null
@@ -1,124 +0,0 @@
-{
- config,
- lib,
- pkgs,
- ...
-}:
-
-with lib;
-
-let
-
- cfg = config.services.polipo;
-
- polipoConfig = pkgs.writeText "polipo.conf" ''
- proxyAddress = ${cfg.proxyAddress}
- proxyPort = ${toString cfg.proxyPort}
- allowedClients = ${concatStringsSep ", " cfg.allowedClients}
- ${optionalString (cfg.parentProxy != "") "parentProxy = ${cfg.parentProxy}"}
- ${optionalString (cfg.socksParentProxy != "") "socksParentProxy = ${cfg.socksParentProxy}"}
- ${config.services.polipo.extraConfig}
- '';
-
-in
-
-{
-
- options = {
-
- services.polipo = {
-
- enable = mkEnableOption "polipo caching web proxy";
-
- proxyAddress = mkOption {
- type = types.str;
- default = "127.0.0.1";
- description = "IP address on which Polipo will listen.";
- };
-
- proxyPort = mkOption {
- type = types.port;
- default = 8123;
- description = "TCP port on which Polipo will listen.";
- };
-
- allowedClients = mkOption {
- type = types.listOf types.str;
- default = [
- "127.0.0.1"
- "::1"
- ];
- example = [
- "127.0.0.1"
- "::1"
- "134.157.168.0/24"
- "2001:660:116::/48"
- ];
- description = ''
- List of IP addresses or network addresses that may connect to Polipo.
- '';
- };
-
- parentProxy = mkOption {
- type = types.str;
- default = "";
- example = "localhost:8124";
- description = ''
- Hostname and port number of an HTTP parent proxy;
- it should have the form ‘host:port’.
- '';
- };
-
- socksParentProxy = mkOption {
- type = types.str;
- default = "";
- example = "localhost:9050";
- description = ''
- Hostname and port number of an SOCKS parent proxy;
- it should have the form ‘host:port’.
- '';
- };
-
- extraConfig = mkOption {
- type = types.lines;
- default = "";
- description = ''
- Polio configuration. Contents will be added
- verbatim to the configuration file.
- '';
- };
-
- };
-
- };
-
- config = mkIf cfg.enable {
-
- users.users.polipo = {
- uid = config.ids.uids.polipo;
- description = "Polipo caching proxy user";
- home = "/var/cache/polipo";
- createHome = true;
- };
-
- users.groups.polipo = {
- gid = config.ids.gids.polipo;
- members = [ "polipo" ];
- };
-
- systemd.services.polipo = {
- description = "caching web proxy";
- after = [
- "network.target"
- "nss-lookup.target"
- ];
- wantedBy = [ "multi-user.target" ];
- serviceConfig = {
- ExecStart = "${pkgs.polipo}/bin/polipo -c ${polipoConfig}";
- User = "polipo";
- };
- };
-
- };
-
-}
diff --git a/nixos/modules/services/networking/ssh/sshd.nix b/nixos/modules/services/networking/ssh/sshd.nix
index 6ff4f9b5a683..b53af1dbdf71 100644
--- a/nixos/modules/services/networking/ssh/sshd.nix
+++ b/nixos/modules/services/networking/ssh/sshd.nix
@@ -693,102 +693,120 @@ in
"ssh/sshd_config".source = sshconf;
};
- systemd =
- let
- service = {
- description = "SSH Daemon";
- wantedBy = lib.optional (!cfg.startWhenNeeded) "multi-user.target";
- after = [ "network.target" ];
- stopIfChanged = false;
- path = [
- cfg.package
- pkgs.gawk
+ systemd.tmpfiles.settings."ssh-root-provision" = {
+ "/root"."d-" = {
+ user = "root";
+ group = ":root";
+ mode = ":700";
+ };
+ "/root/.ssh"."d-" = {
+ user = "root";
+ group = ":root";
+ mode = ":700";
+ };
+ "/root/.ssh/authorized_keys"."f^" = {
+ user = "root";
+ group = ":root";
+ mode = ":600";
+ argument = "ssh.authorized_keys.root";
+ };
+ };
+
+ systemd = {
+ sockets.sshd = lib.mkIf cfg.startWhenNeeded {
+ description = "SSH Socket";
+ wantedBy = [ "sockets.target" ];
+ socketConfig.ListenStream =
+ if cfg.listenAddresses != [ ] then
+ lib.concatMap (
+ { addr, port }:
+ if port != null then [ "${addr}:${toString port}" ] else map (p: "${addr}:${toString p}") cfg.ports
+ ) cfg.listenAddresses
+ else
+ cfg.ports;
+ socketConfig.Accept = true;
+ # Prevent brute-force attacks from shutting down socket
+ socketConfig.TriggerLimitIntervalSec = 0;
+ };
+
+ services."sshd@" = {
+ description = "SSH per-connection Daemon";
+ after = [
+ "network.target"
+ "sshd-keygen.service"
+ ];
+ wants = [ "sshd-keygen.service" ];
+ stopIfChanged = false;
+ path = [ cfg.package ];
+ environment.LD_LIBRARY_PATH = nssModulesPath;
+
+ serviceConfig = {
+ ExecStart = lib.concatStringsSep " " [
+ "-${lib.getExe' cfg.package "sshd"}"
+ "-i"
+ "-D"
+ "-f /etc/ssh/sshd_config"
];
- environment.LD_LIBRARY_PATH = nssModulesPath;
+ KillMode = "process";
+ StandardInput = "socket";
+ StandardError = "journal";
+ };
+ };
- restartTriggers = lib.optionals (!cfg.startWhenNeeded) [
- config.environment.etc."ssh/sshd_config".source
+ services.sshd = lib.mkIf (!cfg.startWhenNeeded) {
+ description = "SSH Daemon";
+ wantedBy = [ "multi-user.target" ];
+ after = [
+ "network.target"
+ "sshd-keygen.service"
+ ];
+ wants = [ "sshd-keygen.service" ];
+ stopIfChanged = false;
+ path = [ cfg.package ];
+ environment.LD_LIBRARY_PATH = nssModulesPath;
+
+ restartTriggers = [ config.environment.etc."ssh/sshd_config".source ];
+
+ serviceConfig = {
+ Restart = "always";
+ ExecStart = lib.concatStringsSep " " [
+ (lib.getExe' cfg.package "sshd")
+ "-D"
+ "-f"
+ "/etc/ssh/sshd_config"
];
+ KillMode = "process";
+ };
+ };
- preStart = ''
- # Make sure we don't write to stdout, since in case of
- # socket activation, it goes to the remote side (#19589).
- exec >&2
-
- ${lib.flip lib.concatMapStrings cfg.hostKeys (k: ''
- if ! [ -s "${k.path}" ]; then
- if ! [ -h "${k.path}" ]; then
- rm -f "${k.path}"
- fi
- mkdir -p "$(dirname '${k.path}')"
- chmod 0755 "$(dirname '${k.path}')"
- ssh-keygen \
- -t "${k.type}" \
- ${lib.optionalString (k ? bits) "-b ${toString k.bits}"} \
- ${lib.optionalString (k ? rounds) "-a ${toString k.rounds}"} \
- ${lib.optionalString (k ? comment) "-C '${k.comment}'"} \
- ${lib.optionalString (k ? openSSHFormat && k.openSSHFormat) "-o"} \
- -f "${k.path}" \
- -N ""
+ services.sshd-keygen = {
+ description = "SSH Host Keys Generation";
+ unitConfig = {
+ ConditionFileNotEmpty = map (k: "|!${k.path}") cfg.hostKeys;
+ };
+ serviceConfig = {
+ Type = "oneshot";
+ };
+ path = [ cfg.package ];
+ script = lib.flip lib.concatMapStrings cfg.hostKeys (k: ''
+ if ! [ -s "${k.path}" ]; then
+ if ! [ -h "${k.path}" ]; then
+ rm -f "${k.path}"
fi
- '')}
- '';
-
- serviceConfig =
- {
- ExecStart =
- (lib.optionalString cfg.startWhenNeeded "-")
- + "${cfg.package}/bin/sshd "
- + (lib.optionalString cfg.startWhenNeeded "-i ")
- + "-D "
- # don't detach into a daemon process
- + "-f /etc/ssh/sshd_config";
- KillMode = "process";
- }
- // (
- if cfg.startWhenNeeded then
- {
- StandardInput = "socket";
- StandardError = "journal";
- }
- else
- {
- Restart = "always";
- Type = "simple";
- }
- );
-
- };
- in
-
- if cfg.startWhenNeeded then
- {
-
- sockets.sshd = {
- description = "SSH Socket";
- wantedBy = [ "sockets.target" ];
- socketConfig.ListenStream =
- if cfg.listenAddresses != [ ] then
- lib.concatMap (
- { addr, port }:
- if port != null then [ "${addr}:${toString port}" ] else map (p: "${addr}:${toString p}") cfg.ports
- ) cfg.listenAddresses
- else
- cfg.ports;
- socketConfig.Accept = true;
- # Prevent brute-force attacks from shutting down socket
- socketConfig.TriggerLimitIntervalSec = 0;
- };
-
- services."sshd@" = service;
-
- }
- else
- {
-
- services.sshd = service;
-
- };
+ mkdir -p "$(dirname '${k.path}')"
+ chmod 0755 "$(dirname '${k.path}')"
+ ssh-keygen \
+ -t "${k.type}" \
+ ${lib.optionalString (k ? bits) "-b ${toString k.bits}"} \
+ ${lib.optionalString (k ? rounds) "-a ${toString k.rounds}"} \
+ ${lib.optionalString (k ? comment) "-C '${k.comment}'"} \
+ ${lib.optionalString (k ? openSSHFormat && k.openSSHFormat) "-o"} \
+ -f "${k.path}" \
+ -N ""
+ fi
+ '');
+ };
+ };
networking.firewall.allowedTCPPorts = lib.optionals cfg.openFirewall cfg.ports;
diff --git a/nixos/modules/services/networking/strongswan-swanctl/param-lib.nix b/nixos/modules/services/networking/strongswan-swanctl/param-lib.nix
index 1619cb6cf089..b8a46d5150bc 100644
--- a/nixos/modules/services/networking/strongswan-swanctl/param-lib.nix
+++ b/nixos/modules/services/networking/strongswan-swanctl/param-lib.nix
@@ -89,7 +89,7 @@ rec {
in
recurse [ ] set;
- mapAttrs'' = f: set: foldl' (a: b: a // b) { } (map (attr: f attr set.${attr}) (attrNames set));
+ mapAttrs'' = f: set: foldl' (a: b: a // b) { } (mapAttrsToList f set);
# Extract the options from the given set of parameters.
paramsToOptions = ps: mapParamsRecursive (_path: name: param: { ${name} = param.option; }) ps;
diff --git a/nixos/modules/services/networking/stunnel.nix b/nixos/modules/services/networking/stunnel.nix
index 0e02cc74184c..c986fc604a64 100644
--- a/nixos/modules/services/networking/stunnel.nix
+++ b/nixos/modules/services/networking/stunnel.nix
@@ -222,13 +222,13 @@ in
Type = "forking";
};
};
-
- meta.maintainers = with lib.maintainers; [
- # Server side
- lschuermann
- # Client side
- das_j
- ];
};
+ meta.maintainers = with lib.maintainers; [
+ # Server side
+ lschuermann
+ # Client side
+ das_j
+ ];
+
}
diff --git a/nixos/modules/services/networking/syncthing.nix b/nixos/modules/services/networking/syncthing.nix
index f76509d78b69..0a901148dc9c 100644
--- a/nixos/modules/services/networking/syncthing.nix
+++ b/nixos/modules/services/networking/syncthing.nix
@@ -55,10 +55,19 @@ let
were removed. Please use, respectively, {rescanIntervalS,fsWatcherEnabled,fsWatcherDelayS} instead.
''
{
- devices = map (
- device:
- if builtins.isString device then { deviceId = cfg.settings.devices.${device}.id; } else device
- ) folder.devices;
+ devices =
+ let
+ folderDevices = folder.devices;
+ in
+ map (
+ device:
+ if builtins.isString device then
+ { deviceId = cfg.settings.devices.${device}.id; }
+ else if builtins.isAttrs device then
+ { deviceId = cfg.settings.devices.${device.name}.id; } // device
+ else
+ throw "Invalid type for devices in folder '${folderName}'; expected list or attrset."
+ ) folderDevices;
}
) (filterAttrs (_: folder: folder.enable) cfg.settings.folders);
@@ -128,9 +137,79 @@ let
# don't exist in the array given. That's why we use here `POST`, and
# only if s.override == true then we DELETE the relevant folders
# afterwards.
- (map (new_cfg: ''
- curl -d ${lib.escapeShellArg (builtins.toJSON new_cfg)} -X POST ${s.baseAddress}
- ''))
+ (map (
+ new_cfg:
+ let
+ jsonPreSecretsFile = pkgs.writeTextFile {
+ name = "${conf_type}-${new_cfg.id}-conf-pre-secrets.json";
+ text = builtins.toJSON new_cfg;
+ };
+ injectSecretsJqCmd =
+ {
+ # There are no secrets in `devs`, so no massaging needed.
+ "devs" = "${jq} .";
+ "dirs" =
+ let
+ folder = new_cfg;
+ devicesWithSecrets = lib.pipe folder.devices [
+ (lib.filter (device: (builtins.isAttrs device) && device ? encryptionPasswordFile))
+ (map (device: {
+ deviceId = device.deviceId;
+ variableName = "secret_${builtins.hashString "sha256" device.encryptionPasswordFile}";
+ secretPath = device.encryptionPasswordFile;
+ }))
+ ];
+ # At this point, `jsonPreSecretsFile` looks something like this:
+ #
+ # {
+ # ...,
+ # "devices": [
+ # {
+ # "deviceId": "id1",
+ # "encryptionPasswordFile": "/etc/bar-encryption-password",
+ # "name": "..."
+ # }
+ # ],
+ # }
+ #
+ # We now generate a `jq` command that can replace those
+ # `encryptionPasswordFile`s with `encryptionPassword`.
+ # The `jq` command ends up looking like this:
+ #
+ # jq --rawfile secret_DEADBEEF /etc/bar-encryption-password '
+ # .devices[] |= (
+ # if .deviceId == "id1" then
+ # del(.encryptionPasswordFile) |
+ # .encryptionPassword = $secret_DEADBEEF
+ # else
+ # .
+ # end
+ # )
+ # '
+ jqUpdates = map (device: ''
+ .devices[] |= (
+ if .deviceId == "${device.deviceId}" then
+ del(.encryptionPasswordFile) |
+ .encryptionPassword = ''$${device.variableName}
+ else
+ .
+ end
+ )
+ '') devicesWithSecrets;
+ jqRawFiles = map (
+ device: "--rawfile ${device.variableName} ${lib.escapeShellArg device.secretPath}"
+ ) devicesWithSecrets;
+ in
+ "${jq} ${lib.concatStringsSep " " jqRawFiles} ${
+ lib.escapeShellArg (lib.concatStringsSep "|" ([ "." ] ++ jqUpdates))
+ }";
+ }
+ .${conf_type};
+ in
+ ''
+ ${injectSecretsJqCmd} ${jsonPreSecretsFile} | curl --json @- -X POST ${s.baseAddress}
+ ''
+ ))
(lib.concatStringsSep "\n")
]
/*
@@ -438,11 +517,48 @@ in
};
devices = mkOption {
- type = types.listOf types.str;
+ type = types.listOf (
+ types.oneOf [
+ types.str
+ (types.submodule (
+ { ... }:
+ {
+ freeformType = settingsFormat.type;
+ options = {
+ name = mkOption {
+ type = types.str;
+ default = null;
+ description = ''
+ The name of a device defined in the
+ [devices](#opt-services.syncthing.settings.devices)
+ option.
+ '';
+ };
+ encryptionPasswordFile = mkOption {
+ type = types.nullOr (
+ types.pathWith {
+ inStore = false;
+ absolute = true;
+ }
+ );
+ default = null;
+ description = ''
+ Path to encryption password. If set, the file will be read during
+ service activation, without being embedded in derivation.
+ '';
+ };
+ };
+ }
+ ))
+ ]
+ );
default = [ ];
description = ''
The devices this folder should be shared with. Each device must
be defined in the [devices](#opt-services.syncthing.settings.devices) option.
+
+ A list of either strings or attribute sets, where values
+ are device names or device configurations.
'';
};
diff --git a/nixos/modules/services/networking/unifi.nix b/nixos/modules/services/networking/unifi.nix
index 8f05634f566b..50f8f844188a 100644
--- a/nixos/modules/services/networking/unifi.nix
+++ b/nixos/modules/services/networking/unifi.nix
@@ -47,9 +47,7 @@ in
'';
};
- services.unifi.unifiPackage = lib.mkPackageOption pkgs "unifi" {
- default = "unifi8";
- };
+ services.unifi.unifiPackage = lib.mkPackageOption pkgs "unifi" { };
services.unifi.mongodbPackage = lib.mkPackageOption pkgs "mongodb" {
default = "mongodb-7_0";
@@ -116,7 +114,7 @@ in
only supports migrating one major version at a time; therefore, you
may wish to set `services.unifi.mongodbPackage = pkgs.mongodb-6_0;`
and activate your configuration before upgrading again to the default
- `mongodb-7_0` supported by `unifi8`.
+ `mongodb-7_0` supported by `unifi`.
For more information, see the MongoDB upgrade notes:
diff --git a/nixos/modules/services/networking/vwifi.nix b/nixos/modules/services/networking/vwifi.nix
new file mode 100644
index 000000000000..64b7fbdf45a4
--- /dev/null
+++ b/nixos/modules/services/networking/vwifi.nix
@@ -0,0 +1,200 @@
+{
+ config,
+ lib,
+ pkgs,
+ ...
+}:
+
+let
+ inherit (lib.modules) mkIf mkMerge;
+ inherit (lib.options) mkOption mkPackageOption mkEnableOption;
+ inherit (lib.lists) optional optionals;
+ inherit (lib.strings)
+ hasSuffix
+ escapeShellArgs
+ ;
+ inherit (lib) types;
+ cfg = config.services.vwifi;
+in
+{
+ options = {
+ services.vwifi =
+ let
+ mkOptionalPort =
+ name:
+ mkOption {
+ description = ''
+ The ${name} port. Set to null if we should leave it unset.
+ '';
+ type = with types; nullOr port;
+ default = null;
+ };
+ in
+ {
+ package = mkPackageOption pkgs "vwifi" { };
+ module = {
+ enable = mkEnableOption "mac80211_hwsim module";
+ numRadios = mkOption {
+ description = "The number of virtual radio interfaces to create.";
+ type = types.int;
+ default = 1;
+ };
+ macPrefix = mkOption {
+ description = ''
+ The prefix for MAC addresses to use, without the trailing ':'.
+ If one radio is created, you can specify the whole MAC address here.
+ The default is defined in vwifi/src/config.h.
+ '';
+ type = types.strMatching "^(([0-9A-Fa-f]{2}:){0,5}[0-9A-Fa-f]{2})$";
+ default = "74:F8:F6";
+ };
+ };
+ client = {
+ enable = mkEnableOption "vwifi client";
+ spy = mkEnableOption "spy mode, useful for wireless monitors";
+ serverAddress = mkOption {
+ description = ''
+ The address of the server. If set to null, will try to use the vsock protocol.
+ Note that this assumes that the server is spawned on the host and passed through to
+ QEMU, with something like:
+
+ -device vhost-vsock-pci,id=vwifi0,guest-cid=42
+ '';
+ type = with types; nullOr str;
+ default = null;
+ };
+ serverPort = mkOptionalPort "server port";
+ extraArgs = mkOption {
+ description = ''
+ Extra arguments to pass to vwifi-client. You can use this if you want to bring
+ the radios up using vwifi-client instead of at boot.
+ '';
+ type = with types; listOf str;
+ default = [ ];
+ example = [
+ "--number"
+ "3"
+ ];
+ };
+ };
+ server = {
+ enable = mkEnableOption "vwifi server";
+ vsock.enable = mkEnableOption "vsock kernel module";
+ ports = {
+ vhost = mkOptionalPort "vhost";
+ tcp = mkOptionalPort "TCP server";
+ spy = mkOptionalPort "spy interface";
+ control = mkOptionalPort "control interface";
+ };
+ openFirewall = mkEnableOption "opening the firewall for the TCP and spy ports";
+ extraArgs = mkOption {
+ description = ''
+ Extra arguments to pass to vwifi-server. You can use this for things including
+ changing the ports or inducing packet loss.
+ '';
+ type = with types; listOf str;
+ default = [ ];
+ example = [ "--lost-packets" ];
+ };
+ };
+ };
+ };
+
+ config = mkMerge [
+ (mkIf cfg.module.enable {
+ boot.kernelModules = [
+ "mac80211_hwsim"
+ ];
+ boot.extraModprobeConfig = ''
+ # We'll add more radios using vwifi-add-interfaces in the systemd unit.
+ options mac80211_hwsim radios=0
+ '';
+ systemd.services.vwifi-add-interfaces = mkIf (cfg.module.numRadios > 0) {
+ description = "vwifi interface bringup";
+ wantedBy = [ "network-pre.target" ];
+ serviceConfig = {
+ Type = "oneshot";
+ ExecStart =
+ let
+ args = [
+ (toString cfg.module.numRadios)
+ cfg.module.macPrefix
+ ];
+ in
+ "${cfg.package}/bin/vwifi-add-interfaces ${escapeShellArgs args}";
+ };
+ };
+ assertions = [
+ {
+ assertion = !(hasSuffix ":" cfg.module.macPrefix);
+ message = ''
+ services.vwifi.module.macPrefix should not have a trailing ":".
+ '';
+ }
+ ];
+ })
+ (mkIf cfg.client.enable {
+ systemd.services.vwifi-client =
+ let
+ clientArgs =
+ optional cfg.client.spy "--spy"
+ ++ optional (cfg.client.serverAddress != null) cfg.client.serverAddress
+ ++ optionals (cfg.client.serverPort != null) [
+ "--port"
+ cfg.client.serverPort
+ ]
+ ++ cfg.client.extraArgs;
+ in
+ rec {
+ description = "vwifi client";
+ wantedBy = [ "multi-user.target" ];
+ after = [ "network.target" ];
+ requires = after;
+ serviceConfig = {
+ ExecStart = "${cfg.package}/bin/vwifi-client ${escapeShellArgs clientArgs}";
+ };
+ };
+ })
+ (mkIf cfg.server.enable {
+ boot.kernelModules = mkIf cfg.server.vsock.enable [
+ "vhost_vsock"
+ ];
+ networking.firewall.allowedTCPPorts = mkIf cfg.server.openFirewall (
+ optional (cfg.server.ports.tcp != null) cfg.server.ports.tcp
+ ++ optional (cfg.server.ports.spy != null) cfg.server.ports.spy
+ );
+ systemd.services.vwifi-server =
+ let
+ serverArgs =
+ optionals (cfg.server.ports.vhost != null) [
+ "--port-vhost"
+ (toString cfg.server.ports.vhost)
+ ]
+ ++ optionals (cfg.server.ports.tcp != null) [
+ "--port-tcp"
+ (toString cfg.server.ports.tcp)
+ ]
+ ++ optionals (cfg.server.ports.spy != null) [
+ "--port-spy"
+ (toString cfg.server.ports.spy)
+ ]
+ ++ optionals (cfg.server.ports.control != null) [
+ "--port-ctrl"
+ (toString cfg.server.ports.control)
+ ]
+ ++ cfg.server.extraArgs;
+ in
+ rec {
+ description = "vwifi server";
+ wantedBy = [ "multi-user.target" ];
+ after = [ "network.target" ];
+ requires = after;
+ serviceConfig = {
+ ExecStart = "${cfg.package}/bin/vwifi-server ${escapeShellArgs serverArgs}";
+ };
+ };
+ })
+ ];
+
+ meta.maintainers = with lib.maintainers; [ numinit ];
+}
diff --git a/nixos/modules/services/networking/whoogle-search.nix b/nixos/modules/services/networking/whoogle-search.nix
index 4665cfc5793d..c0067edce7f0 100644
--- a/nixos/modules/services/networking/whoogle-search.nix
+++ b/nixos/modules/services/networking/whoogle-search.nix
@@ -64,7 +64,7 @@ in
RestartSec = "5s";
};
};
-
- meta.maintainers = with lib.maintainers; [ malte-v ];
};
+
+ meta.maintainers = with lib.maintainers; [ malte-v ];
}
diff --git a/nixos/modules/services/networking/xandikos.nix b/nixos/modules/services/networking/xandikos.nix
index 1b72cd03ba9c..908107a259a9 100644
--- a/nixos/modules/services/networking/xandikos.nix
+++ b/nixos/modules/services/networking/xandikos.nix
@@ -87,9 +87,10 @@ in
};
+ meta.maintainers = with lib.maintainers; [ _0x4A6F ];
+
config = mkIf cfg.enable (mkMerge [
{
- meta.maintainers = with lib.maintainers; [ _0x4A6F ];
systemd.services.xandikos = {
description = "A Simple Calendar and Contact Server";
diff --git a/nixos/modules/services/search/meilisearch.nix b/nixos/modules/services/search/meilisearch.nix
index 1a1465b8e222..03b581c4d8cd 100644
--- a/nixos/modules/services/search/meilisearch.nix
+++ b/nixos/modules/services/search/meilisearch.nix
@@ -108,6 +108,21 @@ in
type = lib.types.str;
};
+ # TODO: turn on by default when it stops being experimental
+ dumplessUpgrade = lib.mkOption {
+ default = false;
+ example = true;
+ description = ''
+ Whether to enable (experimental) dumpless upgrade.
+
+ Allows upgrading from Meilisearch >=v1.12 to Meilisearch >=v1.13 without manually
+ dumping and importing the database.
+
+ More information at https://www.meilisearch.com/docs/learn/update_and_migration/updating#dumpless-upgrade
+ '';
+ type = lib.types.bool;
+ };
+
};
###### implementation
@@ -129,6 +144,7 @@ in
MEILI_DUMP_DIR = "/var/lib/meilisearch/dumps";
MEILI_LOG_LEVEL = cfg.logLevel;
MEILI_MAX_INDEX_SIZE = cfg.maxIndexSize;
+ MEILI_EXPERIMENTAL_DUMPLESS_UPGRADE = lib.boolToString cfg.dumplessUpgrade;
};
serviceConfig = {
ExecStart = "${cfg.package}/bin/meilisearch";
diff --git a/nixos/modules/services/search/tika.nix b/nixos/modules/services/search/tika.nix
index 94096b6db29f..5ddd1a551e49 100644
--- a/nixos/modules/services/search/tika.nix
+++ b/nixos/modules/services/search/tika.nix
@@ -79,7 +79,10 @@ in
serviceConfig =
let
- package = cfg.package.override { inherit (cfg) enableOcr; };
+ package = cfg.package.override {
+ inherit (cfg) enableOcr;
+ enableGui = false;
+ };
in
{
Type = "simple";
diff --git a/nixos/modules/services/security/cfssl.nix b/nixos/modules/services/security/cfssl.nix
index 2d0465d6a0b8..514442dbe437 100644
--- a/nixos/modules/services/security/cfssl.nix
+++ b/nixos/modules/services/security/cfssl.nix
@@ -164,6 +164,12 @@ in
];
description = "Log level (0 = DEBUG, 5 = FATAL).";
};
+
+ disable = lib.mkOption {
+ default = null;
+ type = lib.types.nullOr lib.types.commas;
+ description = "Endpoints to disable (comma-separated list)";
+ };
};
config = lib.mkIf cfg.enable {
@@ -218,6 +224,7 @@ in
(opt "tls-remote-ca" tlsRemoteCa)
(opt "db-config" dbConfig)
(opt "loglevel" (toString logLevel))
+ (opt "disable" disable)
];
}
(lib.mkIf (cfg.dataDir == options.services.cfssl.dataDir.default) {
diff --git a/nixos/modules/services/security/e-imzo.nix b/nixos/modules/services/security/e-imzo.nix
new file mode 100644
index 000000000000..1423f3ec9596
--- /dev/null
+++ b/nixos/modules/services/security/e-imzo.nix
@@ -0,0 +1,50 @@
+{
+ config,
+ lib,
+ pkgs,
+ ...
+}:
+let
+ cfg = config.services.e-imzo;
+in
+{
+ options = {
+ services.e-imzo = {
+ enable = lib.mkEnableOption "E-IMZO";
+
+ package = lib.mkPackageOption pkgs "e-imzo" {
+ extraDescription = "Official mirror deletes old versions as soon as they release new one. Feel free to use either unstable or your own custom e-imzo package and ping maintainer.";
+ };
+ };
+ };
+
+ config = lib.mkIf cfg.enable {
+ systemd.user.services.e-imzo = {
+ enable = true;
+ description = "E-IMZO, uzbek state web signing service";
+ documentation = [ "https://github.com/xinux-org/e-imzo" ];
+
+ after = [
+ "network-online.target"
+ "graphical.target"
+ ];
+ wants = [
+ "network-online.target"
+ "graphical.target"
+ ];
+ wantedBy = [ "default.target" ];
+
+ serviceConfig = {
+ Type = "simple";
+ Restart = "always";
+ RestartSec = 1;
+ ExecStart = lib.getExe cfg.package;
+
+ NoNewPrivileges = true;
+ SystemCallArchitectures = "native";
+ };
+ };
+ };
+
+ meta.maintainers = with lib.maintainers; [ orzklv ];
+}
diff --git a/nixos/modules/services/security/kanidm.nix b/nixos/modules/services/security/kanidm.nix
index b2dc4a57fa44..67b219595458 100644
--- a/nixos/modules/services/security/kanidm.nix
+++ b/nixos/modules/services/security/kanidm.nix
@@ -563,6 +563,16 @@ in
default = null;
};
+ imageFile = mkOption {
+ description = ''
+ Application image to display in the WebUI.
+ Kanidm supports "image/jpeg", "image/png", "image/gif", "image/svg+xml", and "image/webp".
+ The image will be uploaded each time kanidm-provision is run.
+ '';
+ type = types.nullOr types.path;
+ default = null;
+ };
+
enableLocalhostRedirects = mkOption {
description = "Allow localhost redirects. Only for public clients.";
type = types.bool;
diff --git a/nixos/modules/services/security/openbao.nix b/nixos/modules/services/security/openbao.nix
new file mode 100644
index 000000000000..9d75ffbebcb7
--- /dev/null
+++ b/nixos/modules/services/security/openbao.nix
@@ -0,0 +1,160 @@
+{
+ config,
+ lib,
+ pkgs,
+ ...
+}:
+let
+ cfg = config.services.openbao;
+
+ settingsFormat = pkgs.formats.json { };
+in
+{
+ options = {
+ services.openbao = {
+ enable = lib.mkEnableOption "OpenBao daemon";
+
+ package = lib.mkPackageOption pkgs "openbao" {
+ example = "pkgs.openbao.override { withHsm = false; withUi = false; }";
+ };
+
+ settings = lib.mkOption {
+ description = ''
+ Settings of OpenBao.
+
+ See [documentation](https://openbao.org/docs/configuration) for more details.
+ '';
+ example = lib.literalExpression ''
+ {
+ ui = true;
+
+ listener.default = {
+ type = "tcp";
+ tls_acme_email = config.security.acme.defaults.email;
+ tls_acme_domains = [ "example.com" ];
+ tls_acme_disable_http_challenge = true;
+ };
+
+ cluster_addr = "http://127.0.0.1:8201";
+ api_addr = "https://example.com";
+
+ storage.raft.path = "/var/lib/openbao";
+ }
+ '';
+
+ type = lib.types.submodule {
+ freeformType = settingsFormat.type;
+ options = {
+ ui = lib.mkEnableOption "the OpenBao web UI";
+
+ listener = lib.mkOption {
+ type = lib.types.attrsOf (
+ lib.types.submodule (
+ { config, ... }:
+ {
+ freeformType = settingsFormat.type;
+ options = {
+ type = lib.mkOption {
+ type = lib.types.enum [
+ "tcp"
+ "unix"
+ ];
+ description = ''
+ The listener type to enable.
+ '';
+ };
+ address = lib.mkOption {
+ type = lib.types.str;
+ default = if config.type == "unix" then "/run/openbao/openbao.sock" else "127.0.0.1:8200";
+ defaultText = lib.literalExpression ''if config.services.openbao.settings.listener..type == "unix" then "/run/openbao/openbao.sock" else "127.0.0.1:8200"'';
+ description = ''
+ The TCP address or UNIX socket path to listen on.
+ '';
+ };
+ };
+ }
+ )
+ );
+ description = ''
+ Configure a listener for responding to requests.
+ '';
+ };
+ };
+ };
+ };
+
+ extraArgs = lib.mkOption {
+ type = lib.types.listOf lib.types.str;
+ default = [ ];
+ description = ''
+ Additional arguments given to OpenBao.
+ '';
+ };
+ };
+ };
+
+ config = lib.mkIf cfg.enable {
+ environment.systemPackages = [ cfg.package ];
+
+ systemd.services.openbao = {
+ description = "OpenBao - A tool for managing secrets";
+
+ wantedBy = [ "multi-user.target" ];
+ after = [ "network.target" ];
+
+ restartIfChanged = false; # do not restart on "nixos-rebuild switch". It would seal the storage and disrupt the clients.
+
+ serviceConfig = {
+ Type = "notify";
+
+ ExecStart = lib.escapeShellArgs (
+ [
+ (lib.getExe cfg.package)
+ "server"
+ "-config"
+ (settingsFormat.generate "openbao.hcl.json" cfg.settings)
+ ]
+ ++ cfg.extraArgs
+ );
+ ExecReload = "${lib.getExe' pkgs.coreutils "kill"} -SIGHUP $MAINPID";
+
+ StateDirectory = "openbao";
+ StateDirectoryMode = "0700";
+ RuntimeDirectory = "openbao";
+ RuntimeDirectoryMode = "0700";
+
+ CapabilityBoundingSet = "";
+ DynamicUser = true;
+ LimitCORE = 0;
+ LockPersonality = true;
+ MemorySwapMax = 0;
+ MemoryZSwapMax = 0;
+ PrivateUsers = true;
+ ProcSubset = "pid";
+ ProtectClock = true;
+ ProtectControlGroups = true;
+ ProtectHome = true;
+ ProtectHostname = true;
+ ProtectKernelLogs = true;
+ ProtectKernelModules = true;
+ ProtectKernelTunables = true;
+ ProtectProc = "invisible";
+ Restart = "on-failure";
+ RestrictAddressFamilies = [
+ "AF_INET"
+ "AF_INET6"
+ "AF_UNIX"
+ ];
+ RestrictNamespaces = true;
+ RestrictRealtime = true;
+ SystemCallArchitectures = "native";
+ SystemCallFilter = [
+ "@system-service"
+ "@resources"
+ "~@privileged"
+ ];
+ UMask = "0077";
+ };
+ };
+ };
+}
diff --git a/nixos/modules/services/security/paretosecurity.nix b/nixos/modules/services/security/paretosecurity.nix
index 9ec196eee6a0..d942fe67c62a 100644
--- a/nixos/modules/services/security/paretosecurity.nix
+++ b/nixos/modules/services/security/paretosecurity.nix
@@ -4,17 +4,24 @@
pkgs,
...
}:
+let
+ cfg = config.services.paretosecurity;
+in
{
options.services.paretosecurity = {
enable = lib.mkEnableOption "[ParetoSecurity](https://paretosecurity.com) [agent](https://github.com/ParetoSecurity/agent) and its root helper";
package = lib.mkPackageOption pkgs "paretosecurity" { };
- trayIcon = lib.mkEnableOption "tray icon for ParetoSecurity";
+ trayIcon = lib.mkOption {
+ type = lib.types.bool;
+ default = true;
+ description = "Set to false to disable the tray icon and run as a CLI tool only.";
+ };
};
- config = lib.mkIf config.services.paretosecurity.enable {
- environment.systemPackages = [ config.services.paretosecurity.package ];
- systemd.packages = [ config.services.paretosecurity.package ];
+ config = lib.mkIf cfg.enable {
+ environment.systemPackages = [ cfg.package ];
+ systemd.packages = [ cfg.package ];
# In traditional Linux distributions, systemd would read the [Install] section from
# unit files and automatically create the appropriate symlinks to enable services.
@@ -24,17 +31,29 @@
# dependencies here. This creates the necessary symlinks in the proper locations.
systemd.sockets.paretosecurity.wantedBy = [ "sockets.target" ];
+ # In NixOS, systemd services are configured with minimal PATH. However,
+ # paretosecurity helper looks for installed software to do its job, so
+ # it needs the full system PATH. For example, it runs `iptables` to see if
+ # firewall is configured. And it looks for various password managers to see
+ # if one is installed.
+ # The `paretosecurity-user` timer service that is configured lower has
+ # the same need.
+ systemd.services.paretosecurity.serviceConfig.Environment = [
+ "PATH=${config.system.path}/bin:${config.system.path}/sbin"
+ ];
+
# Enable the tray icon and timer services if the trayIcon option is enabled
- systemd.user = lib.mkIf config.services.paretosecurity.trayIcon {
- services.paretosecurity-trayicon = {
- wantedBy = [ "graphical-session.target" ];
- };
- services.paretosecurity-user = {
- wantedBy = [ "graphical-session.target" ];
- };
- timers.paretosecurity-user = {
- wantedBy = [ "timers.target" ];
+ systemd.user = lib.mkIf cfg.trayIcon {
+ services = {
+ paretosecurity-trayicon.wantedBy = [ "graphical-session.target" ];
+ paretosecurity-user = {
+ wantedBy = [ "graphical-session.target" ];
+ serviceConfig.Environment = [
+ "PATH=${config.system.path}/bin:${config.system.path}/sbin"
+ ];
+ };
};
+ timers.paretosecurity-user.wantedBy = [ "timers.target" ];
};
};
}
diff --git a/nixos/modules/services/security/pocket-id.nix b/nixos/modules/services/security/pocket-id.nix
new file mode 100644
index 000000000000..87c13b638dcb
--- /dev/null
+++ b/nixos/modules/services/security/pocket-id.nix
@@ -0,0 +1,278 @@
+{
+ lib,
+ pkgs,
+ config,
+ ...
+}:
+
+let
+ inherit (lib)
+ mkEnableOption
+ mkIf
+ mkOption
+ optionalAttrs
+ optional
+ mkPackageOption
+ ;
+ inherit (lib.types)
+ bool
+ path
+ str
+ submodule
+ ;
+
+ cfg = config.services.pocket-id;
+
+ format = pkgs.formats.keyValue { };
+ settingsFile = format.generate "pocket-id-env-vars" cfg.settings;
+in
+{
+ meta.maintainers = with lib.maintainers; [
+ gepbird
+ ymstnt
+ ];
+
+ options.services.pocket-id = {
+ enable = mkEnableOption "Pocket ID server";
+
+ package = mkPackageOption pkgs "pocket-id" { };
+
+ environmentFile = mkOption {
+ type = path;
+ description = ''
+ Path to an environment file loaded for the Pocket ID service.
+
+ This can be used to securely store tokens and secrets outside of the world-readable Nix store.
+
+ Example contents of the file:
+ MAXMIND_LICENSE_KEY=your-license-key
+ '';
+ default = "/dev/null";
+ example = "/var/lib/secrets/pocket-id";
+ };
+
+ settings = mkOption {
+ type = submodule {
+ freeformType = format.type;
+
+ options = {
+ PUBLIC_APP_URL = mkOption {
+ type = str;
+ description = ''
+ The URL where you will access the app.
+ '';
+ default = "http://localhost";
+ };
+
+ TRUST_PROXY = mkOption {
+ type = bool;
+ description = ''
+ Whether the app is behind a reverse proxy.
+ '';
+ default = false;
+ };
+ };
+ };
+
+ default = { };
+
+ description = ''
+ Environment variables that will be passed to Pocket ID, see
+ [configuration options](https://pocket-id.org/docs/configuration/environment-variables)
+ for supported values.
+ '';
+ };
+
+ dataDir = mkOption {
+ type = path;
+ default = "/var/lib/pocket-id";
+ description = ''
+ The directory where Pocket ID will store its data, such as the database.
+ '';
+ };
+
+ user = mkOption {
+ type = str;
+ default = "pocket-id";
+ description = "User account under which Pocket ID runs.";
+ };
+
+ group = mkOption {
+ type = str;
+ default = "pocket-id";
+ description = "Group account under which Pocket ID runs.";
+ };
+ };
+
+ config = mkIf cfg.enable {
+ warnings = (
+ optional (cfg.settings ? MAXMIND_LICENSE_KEY)
+ "config.services.pocket-id.settings.MAXMIND_LICENSE_KEY will be stored as plaintext in the Nix store. Use config.services.pocket-id.environmentFile instead."
+ );
+
+ systemd.tmpfiles.rules = [
+ "d ${cfg.dataDir} 0755 ${cfg.user} ${cfg.group}"
+ ];
+
+ systemd.services = {
+ pocket-id-backend = {
+ description = "Pocket ID backend";
+ after = [ "network.target" ];
+ wantedBy = [ "multi-user.target" ];
+ restartTriggers = [
+ cfg.package
+ cfg.environmentFile
+ settingsFile
+ ];
+
+ serviceConfig = {
+ Type = "simple";
+ User = cfg.user;
+ Group = cfg.group;
+ WorkingDirectory = cfg.dataDir;
+ ExecStart = "${cfg.package}/bin/pocket-id-backend";
+ Restart = "always";
+ EnvironmentFile = [
+ cfg.environmentFile
+ settingsFile
+ ];
+
+ # Hardening
+ AmbientCapabilities = "";
+ CapabilityBoundingSet = "";
+ DeviceAllow = "";
+ DevicePolicy = "closed";
+ #IPAddressDeny = "any"; # communicates with the frontend
+ LockPersonality = true;
+ MemoryDenyWriteExecute = true;
+ NoNewPrivileges = true;
+ PrivateDevices = true;
+ PrivateNetwork = false; # communicates with the frontend
+ PrivateTmp = true;
+ PrivateUsers = true;
+ ProcSubset = "pid";
+ ProtectClock = true;
+ ProtectControlGroups = true;
+ ProtectHome = true;
+ ProtectHostname = true;
+ ProtectKernelLogs = true;
+ ProtectKernelModules = true;
+ ProtectKernelTunables = true;
+ ProtectProc = "invisible";
+ ProtectSystem = "full"; # needs to write in cfg.dataDir
+ RemoveIPC = true;
+ RestrictAddressFamilies = [
+ "AF_INET"
+ "AF_INET6"
+ ];
+ RestrictNamespaces = true;
+ RestrictRealtime = true;
+ RestrictSUIDSGID = true;
+ SystemCallArchitectures = "native";
+ SystemCallFilter = lib.concatStringsSep " " [
+ "~"
+ "@clock"
+ "@cpu-emulation"
+ "@debug"
+ "@module"
+ "@mount"
+ "@obsolete"
+ "@privileged"
+ "@raw-io"
+ "@reboot"
+ #"@resources" # vm test segfaults
+ "@swap"
+ ];
+ UMask = "0077";
+ };
+ };
+
+ pocket-id-frontend = {
+ description = "Pocket ID frontend";
+ after = [
+ "network.target"
+ "pocket-id-backend.service"
+ ];
+ wantedBy = [ "multi-user.target" ];
+ restartTriggers = [
+ cfg.package
+ cfg.environmentFile
+ settingsFile
+ ];
+
+ serviceConfig = {
+ Type = "simple";
+ User = cfg.user;
+ Group = cfg.group;
+ ExecStart = "${cfg.package}/bin/pocket-id-frontend";
+ Restart = "always";
+ EnvironmentFile = [
+ cfg.environmentFile
+ settingsFile
+ ];
+
+ # Hardening
+ AmbientCapabilities = "";
+ CapabilityBoundingSet = "";
+ DeviceAllow = "";
+ DevicePolicy = "closed";
+ #IPAddressDeny = "any"; # communicates with the backend and client
+ LockPersonality = true;
+ MemoryDenyWriteExecute = false; # V8_Fatal segfault
+ NoNewPrivileges = true;
+ PrivateDevices = true;
+ PrivateNetwork = false; # communicates with the backend and client
+ PrivateTmp = true;
+ PrivateUsers = true;
+ ProcSubset = "pid";
+ ProtectClock = true;
+ ProtectControlGroups = true;
+ ProtectHome = true;
+ ProtectHostname = true;
+ ProtectKernelLogs = true;
+ ProtectKernelModules = true;
+ ProtectKernelTunables = true;
+ ProtectProc = "invisible";
+ ProtectSystem = "strict";
+ RemoveIPC = true;
+ RestrictAddressFamilies = [
+ "AF_INET"
+ "AF_INET6"
+ ];
+ RestrictNamespaces = true;
+ RestrictRealtime = true;
+ RestrictSUIDSGID = true;
+ SystemCallArchitectures = "native";
+ SystemCallFilter = lib.concatStringsSep " " [
+ "~"
+ "@clock"
+ "@cpu-emulation"
+ "@debug"
+ "@module"
+ "@mount"
+ "@obsolete"
+ "@privileged"
+ "@raw-io"
+ "@reboot"
+ "@resources"
+ "@swap"
+ ];
+ UMask = "0077";
+ };
+ };
+ };
+
+ users.users = optionalAttrs (cfg.user == "pocket-id") {
+ pocket-id = {
+ isSystemUser = true;
+ group = cfg.group;
+ description = "Pocket ID backend user";
+ home = cfg.dataDir;
+ };
+ };
+
+ users.groups = optionalAttrs (cfg.group == "pocket-id") {
+ pocket-id = { };
+ };
+ };
+}
diff --git a/nixos/modules/services/security/vaultwarden/default.nix b/nixos/modules/services/security/vaultwarden/default.nix
index 285dbc5d7046..613b81b297d2 100644
--- a/nixos/modules/services/security/vaultwarden/default.nix
+++ b/nixos/modules/services/security/vaultwarden/default.nix
@@ -65,6 +65,7 @@ let
vaultwarden = cfg.package.override { inherit (cfg) dbBackend; };
+ useSendmail = configEnv.USE_SENDMAIL or null == "true";
in
{
imports = [
@@ -236,10 +237,10 @@ in
DevicePolicy = "closed";
LockPersonality = true;
MemoryDenyWriteExecute = true;
- NoNewPrivileges = true;
- PrivateDevices = true;
+ NoNewPrivileges = !useSendmail;
+ PrivateDevices = !useSendmail;
PrivateTmp = true;
- PrivateUsers = true;
+ PrivateUsers = !useSendmail;
ProcSubset = "pid";
ProtectClock = true;
ProtectControlGroups = true;
@@ -262,10 +263,13 @@ in
inherit StateDirectory;
StateDirectoryMode = "0700";
SystemCallArchitectures = "native";
- SystemCallFilter = [
- "@system-service"
- "~@privileged"
- ];
+ SystemCallFilter =
+ [
+ "@system-service"
+ ]
+ ++ lib.optionals (!useSendmail) [
+ "~@privileged"
+ ];
Restart = "always";
UMask = "0077";
};
diff --git a/nixos/modules/services/torrent/cross-seed.nix b/nixos/modules/services/torrent/cross-seed.nix
index b860a0fff550..3db8b84071c3 100644
--- a/nixos/modules/services/torrent/cross-seed.nix
+++ b/nixos/modules/services/torrent/cross-seed.nix
@@ -14,6 +14,15 @@ let
types
;
settingsFormat = pkgs.formats.json { };
+
+ generatedConfig =
+ pkgs.runCommand "cross-seed-gen-config" { nativeBuildInputs = [ pkgs.cross-seed ]; }
+ ''
+ export HOME=$(mktemp -d)
+ cross-seed gen-config
+ mkdir $out
+ cp -r $HOME/.cross-seed/config.js $out/
+ '';
in
{
options.services.cross-seed = {
@@ -40,6 +49,22 @@ in
description = "Cross-seed config directory";
};
+ useGenConfigDefaults = mkOption {
+ type = types.bool;
+ default = false;
+ description = ''
+ Whether to use the option defaults from the configuration generated by
+ {command}`cross-seed gen-config`.
+
+ Those are the settings recommended by the project, and can be inspected
+ from their [template file](https://github.com/cross-seed/cross-seed/blob/master/src/config.template.cjs).
+
+ Settings set in {option}`services.cross-seed.settings` and
+ {option}`services.cross-seed.settingsFile` will override the ones from
+ this option.
+ '';
+ };
+
settings = mkOption {
default = { };
type = types.submodule {
@@ -120,6 +145,13 @@ in
let
jsonSettingsFile = settingsFormat.generate "settings.json" cfg.settings;
+ genConfigSegment =
+ lib.optionalString cfg.useGenConfigDefaults # js
+ ''
+ const gen_config_js = "${generatedConfig}/config.js";
+ Object.assign(loaded_settings, require(gen_config_js));
+ '';
+
# Since cross-seed uses a javascript config file, we can use node's
# ability to parse JSON directly to avoid having to do any conversion.
# This also means we don't need to use any external programs to merge the
@@ -138,7 +170,9 @@ in
"use strict";
const fs = require("fs");
const settings_json = "${jsonSettingsFile}";
- let loaded_settings = JSON.parse(fs.readFileSync(settings_json, "utf8"));
+ let loaded_settings = {};
+ ${genConfigSegment}
+ Object.assign(loaded_settings, JSON.parse(fs.readFileSync(settings_json, "utf8")));
${secretSettingsSegment}
module.exports = loaded_settings;
'';
diff --git a/nixos/modules/services/torrent/transmission.nix b/nixos/modules/services/torrent/transmission.nix
index 65501ecf7f4c..adf4a5a24680 100644
--- a/nixos/modules/services/torrent/transmission.nix
+++ b/nixos/modules/services/torrent/transmission.nix
@@ -366,6 +366,7 @@ in
};
serviceConfig = {
+ Type = "notify";
# Use "+" because credentialsFile may not be accessible to User= or Group=.
ExecStartPre = [
(
diff --git a/nixos/modules/services/video/frigate.nix b/nixos/modules/services/video/frigate.nix
index 3a0112d04d4b..7bc277f89b20 100644
--- a/nixos/modules/services/video/frigate.nix
+++ b/nixos/modules/services/video/frigate.nix
@@ -669,7 +669,11 @@ in
# Caches
PrivateTmp = true;
- CacheDirectory = "frigate";
+ CacheDirectory = [
+ "frigate"
+ # https://github.com/blakeblackshear/frigate/discussions/18129
+ "frigate/model_cache"
+ ];
CacheDirectoryMode = "0750";
# Sockets/IPC
diff --git a/nixos/modules/services/web-apps/archtika.nix b/nixos/modules/services/web-apps/archtika.nix
deleted file mode 100644
index cfd80dfb064f..000000000000
--- a/nixos/modules/services/web-apps/archtika.nix
+++ /dev/null
@@ -1,307 +0,0 @@
-{
- config,
- lib,
- pkgs,
- ...
-}:
-
-let
- inherit (lib)
- mkEnableOption
- mkOption
- mkIf
- mkPackageOption
- types
- ;
- cfg = config.services.archtika;
-in
-{
- options.services.archtika = {
- enable = mkEnableOption "Whether to enable the archtika service";
-
- package = mkPackageOption pkgs "archtika" { };
-
- user = mkOption {
- type = types.str;
- default = "archtika";
- description = "User account under which archtika runs.";
- };
-
- group = mkOption {
- type = types.str;
- default = "archtika";
- description = "Group under which archtika runs.";
- };
-
- databaseName = mkOption {
- type = types.str;
- default = "archtika";
- description = "Name of the PostgreSQL database for archtika.";
- };
-
- apiPort = mkOption {
- type = types.port;
- default = 5000;
- description = "Port on which the API runs.";
- };
-
- apiAdminPort = mkOption {
- type = types.port;
- default = 7500;
- description = "Port on which the API admin server runs.";
- };
-
- webAppPort = mkOption {
- type = types.port;
- default = 10000;
- description = "Port on which the web application runs.";
- };
-
- domain = mkOption {
- type = types.str;
- description = "Domain to use for the application.";
- };
-
- settings = mkOption {
- description = "Settings for the running archtika application.";
- type = types.submodule {
- options = {
- disableRegistration = mkOption {
- type = types.bool;
- default = false;
- description = "By default any user can create an account. That behavior can be disabled with this option.";
- };
- maxUserWebsites = mkOption {
- type = types.ints.positive;
- default = 2;
- description = "Maximum number of websites allowed per user by default.";
- };
- maxWebsiteStorageSize = mkOption {
- type = types.ints.positive;
- default = 50;
- description = "Maximum amount of disk space in MB allowed per user website by default.";
- };
- };
- };
- };
- };
-
- config = mkIf cfg.enable (
- let
- baseHardenedSystemdOptions = {
- CapabilityBoundingSet = "";
- LockPersonality = true;
- NoNewPrivileges = true;
- PrivateDevices = true;
- PrivateTmp = true;
- ProtectClock = true;
- ProtectControlGroups = true;
- ProtectHome = true;
- ProtectHostname = true;
- ProtectKernelLogs = true;
- ProtectKernelModules = true;
- ProtectKernelTunables = true;
- ProtectSystem = "strict";
- RemoveIPC = true;
- RestrictNamespaces = true;
- RestrictRealtime = true;
- RestrictSUIDSGID = true;
- SystemCallArchitectures = "native";
- SystemCallFilter = [
- "@system-service"
- "~@privileged"
- "~@resources"
- ];
- ReadWritePaths = [ "/var/www/archtika-websites" ];
- };
- in
- {
- users.users.${cfg.user} = {
- isSystemUser = true;
- group = cfg.group;
- };
-
- users.groups.${cfg.group} = {
- members = [
- "nginx"
- "postgres"
- ];
- };
-
- systemd.tmpfiles.settings."10-archtika" = {
- "/var/www" = {
- d = {
- mode = "0755";
- user = "root";
- group = "root";
- };
- };
- "/var/www/archtika-websites" = {
- d = {
- mode = "0770";
- user = cfg.user;
- group = cfg.group;
- };
- };
- };
-
- systemd.services.archtika-api = {
- description = "archtika API service";
- wantedBy = [ "multi-user.target" ];
- after = [
- "network.target"
- "postgresql.service"
- ];
-
- path = [ config.services.postgresql.package ];
-
- serviceConfig = baseHardenedSystemdOptions // {
- User = cfg.user;
- Group = cfg.group;
- Restart = "always";
- WorkingDirectory = "${cfg.package}/rest-api";
- RestrictAddressFamilies = [
- "AF_INET"
- "AF_INET6"
- "AF_UNIX"
- ];
- };
-
- script =
- let
- dbUrl = user: "postgres://${user}@/${cfg.databaseName}?host=/var/run/postgresql";
- in
- ''
- JWT_SECRET=$(tr -dc 'A-Za-z0-9' < /dev/urandom | head -c64)
-
- psql ${dbUrl "postgres"} \
- -c "ALTER DATABASE ${cfg.databaseName} SET \"app.jwt_secret\" TO '$JWT_SECRET'" \
- -c "ALTER DATABASE ${cfg.databaseName} SET \"app.website_max_storage_size\" TO ${toString cfg.settings.maxWebsiteStorageSize}" \
- -c "ALTER DATABASE ${cfg.databaseName} SET \"app.website_max_number_user\" TO ${toString cfg.settings.maxUserWebsites}"
-
- ${lib.getExe pkgs.dbmate} --url "${dbUrl "postgres"}&sslmode=disable" --migrations-dir ${cfg.package}/rest-api/db/migrations up
-
- PGRST_SERVER_CORS_ALLOWED_ORIGINS="https://${cfg.domain}" \
- PGRST_ADMIN_SERVER_PORT=${toString cfg.apiAdminPort} \
- PGRST_SERVER_PORT=${toString cfg.apiPort} \
- PGRST_DB_SCHEMAS="api" \
- PGRST_DB_ANON_ROLE="anon" \
- PGRST_OPENAPI_MODE="ignore-privileges" \
- PGRST_DB_URI=${dbUrl "authenticator"} \
- PGRST_JWT_SECRET="$JWT_SECRET" \
- ${lib.getExe pkgs.postgrest}
- '';
- };
-
- systemd.services.archtika-web = {
- description = "archtika Web App service";
- wantedBy = [ "multi-user.target" ];
- after = [ "network.target" ];
-
- serviceConfig = baseHardenedSystemdOptions // {
- User = cfg.user;
- Group = cfg.group;
- Restart = "always";
- WorkingDirectory = "${cfg.package}/web-app";
- RestrictAddressFamilies = [
- "AF_INET"
- "AF_INET6"
- ];
- };
-
- environment = {
- REGISTRATION_IS_DISABLED = toString cfg.settings.disableRegistration;
- BODY_SIZE_LIMIT = "10M";
- ORIGIN = "https://${cfg.domain}";
- PORT = toString cfg.webAppPort;
- };
-
- script = "${lib.getExe pkgs.nodejs} ${cfg.package}/web-app";
- };
-
- services.postgresql = {
- enable = true;
- ensureDatabases = [ cfg.databaseName ];
- extensions = ps: with ps; [ pgjwt ];
- authentication = lib.mkOverride 11 ''
- local postgres postgres trust
- local ${cfg.databaseName} all trust
- '';
- };
-
- systemd.services.postgresql = {
- path = with pkgs; [
- gnutar
- gzip
- ];
- serviceConfig = {
- ReadWritePaths = [ "/var/www/archtika-websites" ];
- SystemCallFilter = [ "@system-service" ];
- };
- };
-
- services.nginx = {
- enable = true;
- recommendedProxySettings = true;
- recommendedTlsSettings = true;
- recommendedZstdSettings = true;
- recommendedOptimisation = true;
-
- appendHttpConfig = ''
- map $http_cookie $archtika_auth_header {
- default "";
- "~*session_token=([^;]+)" "Bearer $1";
- }
- '';
-
- virtualHosts = {
- "${cfg.domain}" = {
- useACMEHost = cfg.domain;
- forceSSL = true;
- locations = {
- "/" = {
- proxyPass = "http://127.0.0.1:${toString cfg.webAppPort}";
- };
- "/previews/" = {
- alias = "/var/www/archtika-websites/previews/";
- index = "index.html";
- tryFiles = "$uri $uri/ $uri.html =404";
- };
- "/api/rpc/export_articles_zip" = {
- proxyPass = "http://127.0.0.1:${toString cfg.apiPort}/rpc/export_articles_zip";
- extraConfig = ''
- default_type application/json;
- proxy_set_header Authorization $archtika_auth_header;
- '';
- };
- "/api/" = {
- proxyPass = "http://127.0.0.1:${toString cfg.apiPort}/";
- extraConfig = ''
- default_type application/json;
- '';
- };
- "/api/rpc/register" = mkIf cfg.settings.disableRegistration {
- extraConfig = ''
- deny all;
- '';
- };
- };
- };
- "~^(?.+)\\.${cfg.domain}$" = {
- useACMEHost = cfg.domain;
- forceSSL = true;
- locations = {
- "/" = {
- root = "/var/www/archtika-websites/$subdomain";
- index = "index.html";
- tryFiles = "$uri $uri/ $uri.html =404";
- };
- };
- };
- };
- };
- }
- );
-
- meta.maintainers = [ lib.maintainers.thiloho ];
-}
diff --git a/nixos/modules/services/web-apps/baikal.nix b/nixos/modules/services/web-apps/baikal.nix
new file mode 100644
index 000000000000..91a007e6e541
--- /dev/null
+++ b/nixos/modules/services/web-apps/baikal.nix
@@ -0,0 +1,141 @@
+{
+ config,
+ lib,
+ pkgs,
+ ...
+}:
+
+let
+ common-name = "baikal";
+ cfg = config.services.baikal;
+in
+{
+ meta.maintainers = [ lib.maintainers.wrvsrx ];
+ options = {
+ services.baikal = {
+ enable = lib.mkEnableOption "baikal";
+ user = lib.mkOption {
+ type = lib.types.str;
+ default = common-name;
+ description = ''
+ User account under which the web-application run.
+ '';
+ };
+ group = lib.mkOption {
+ type = lib.types.str;
+ default = common-name;
+ description = ''
+ Group account under which the web-application run.
+ '';
+ };
+ pool = lib.mkOption {
+ type = lib.types.str;
+ default = common-name;
+ description = ''
+ Name of existing phpfpm pool that is used to run web-application.
+ If not specified a pool will be created automatically with
+ default values.
+ '';
+ };
+ virtualHost = lib.mkOption {
+ type = lib.types.nullOr lib.types.str;
+ default = common-name;
+ description = ''
+ Name of the nginx virtualhost to use and setup. If null, do not setup any virtualhost.
+ '';
+ };
+ phpPackage = lib.mkOption {
+ type = lib.types.package;
+ default = pkgs.php;
+ defaultText = "pkgs.php";
+ description = ''
+ php package to use for php fpm daemon.
+ '';
+ };
+ package = lib.mkOption {
+ type = lib.types.package;
+ default = pkgs.baikal;
+ defaultText = "pkgs.baikal";
+ description = ''
+ Baikal package to use.
+ '';
+ };
+
+ };
+ };
+ config = lib.mkIf cfg.enable {
+ services.phpfpm.pools = lib.mkIf (cfg.pool == "${common-name}") {
+ ${common-name} = {
+ inherit (cfg) user phpPackage;
+ phpEnv = {
+ "BAIKAL_PATH_CONFIG" = "/var/lib/baikal/config/";
+ "BAIKAL_PATH_SPECIFIC" = "/var/lib/baikal/specific/";
+ };
+ settings = lib.mapAttrs (name: lib.mkDefault) {
+ "listen.owner" = "nginx";
+ "listen.group" = "nginx";
+ "listen.mode" = "0600";
+ "pm" = "dynamic";
+ "pm.max_children" = 75;
+ "pm.start_servers" = 1;
+ "pm.min_spare_servers" = 1;
+ "pm.max_spare_servers" = 4;
+ "pm.max_requests" = 500;
+ "pm.process_idle_timeout" = 30;
+ "catch_workers_output" = 1;
+ };
+ };
+ };
+ services.nginx = lib.mkIf (cfg.virtualHost != null) {
+ enable = true;
+ virtualHosts."${cfg.virtualHost}" = {
+ root = "${cfg.package}/share/php/baikal/html";
+ locations = {
+ "/" = {
+ index = "index.php";
+ };
+ "/.well-known/".extraConfig = ''
+ rewrite ^/.well-known/caldav /dav.php redirect;
+ rewrite ^/.well-known/carddav /dav.php redirect;
+ '';
+ "~ /(\.ht|Core|Specific|config)".extraConfig = ''
+ deny all;
+ return 404;
+ '';
+ "~ ^(.+\.php)(.*)$".extraConfig = ''
+ try_files $fastcgi_script_name =404;
+ include ${config.services.nginx.package}/conf/fastcgi.conf;
+ fastcgi_split_path_info ^(.+\.php)(.*)$;
+ fastcgi_pass unix:${config.services.phpfpm.pools.${cfg.pool}.socket};
+ fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
+ fastcgi_param PATH_INFO $fastcgi_path_info;
+ '';
+ };
+ };
+ };
+
+ users.users.${cfg.user} = lib.mkIf (cfg.user == common-name) {
+ description = "baikal service user";
+ isSystemUser = true;
+ inherit (cfg) group;
+ };
+
+ users.groups.${cfg.group} = lib.mkIf (cfg.group == common-name) { };
+
+ systemd.tmpfiles.settings."baikal" = builtins.listToAttrs (
+ map
+ (x: {
+ name = "/var/lib/baikal/${x}";
+ value.d = {
+ mode = "0700";
+ inherit (cfg) user group;
+ };
+ })
+ [
+ "config"
+ "specific"
+ "specific/db"
+ ]
+ );
+ };
+}
diff --git a/nixos/modules/services/web-apps/cook-cli.nix b/nixos/modules/services/web-apps/cook-cli.nix
new file mode 100644
index 000000000000..e1addb21318e
--- /dev/null
+++ b/nixos/modules/services/web-apps/cook-cli.nix
@@ -0,0 +1,113 @@
+{
+ config,
+ pkgs,
+ lib,
+ ...
+}:
+
+let
+ cfg = config.services.cook-cli;
+ inherit (lib)
+ mkIf
+ mkEnableOption
+ mkPackageOption
+ mkOption
+ getExe
+ types
+ ;
+in
+{
+ options = {
+ services.cook-cli = {
+ enable = lib.mkEnableOption "cook-cli";
+
+ package = lib.mkPackageOption pkgs "cook-cli" { };
+
+ autoStart = lib.mkOption {
+ type = lib.types.bool;
+ default = true;
+ description = ''
+ Whether to start cook-cli server automatically.
+ '';
+ };
+
+ port = lib.mkOption {
+ type = lib.types.port;
+ default = 9080;
+ description = ''
+ Which port cook-cli server will use.
+ '';
+ };
+
+ basePath = lib.mkOption {
+ type = lib.types.str;
+ default = "/var/lib/cook-cli";
+ description = ''
+ Path to the directory cook-cli will look for recipes.
+ '';
+ };
+
+ openFirewall = lib.mkOption {
+ type = lib.types.bool;
+ default = false;
+ description = ''
+ Whether to open the cook-cli server port in the firewall.
+ '';
+ };
+ };
+ };
+
+ config = lib.mkIf cfg.enable {
+ environment.systemPackages = [ cfg.package ];
+
+ systemd.tmpfiles.rules = [
+ "d ${cfg.basePath} 0770 cook-cli users"
+ ];
+
+ users.users.cook-cli = {
+ home = "${cfg.basePath}";
+ group = "cook-cli";
+ isSystemUser = true;
+ };
+ users.groups.cook-cli.members = [
+ "cook-cli"
+ ];
+
+ systemd.services.cook-cli = {
+ description = "cook-cli server";
+ serviceConfig = {
+ ExecStart = "${getExe cfg.package} server --host --port ${toString cfg.port} ${cfg.basePath}";
+ WorkingDirectory = cfg.basePath;
+ User = "cook-cli";
+ Group = "cook-cli";
+ # Hardening options
+ CapabilityBoundingSet = [ "CAP_SYS_NICE" ];
+ AmbientCapabilities = [ "CAP_SYS_NICE" ];
+ LockPersonality = true;
+ NoNewPrivileges = true;
+ PrivateTmp = true;
+ ProtectControlGroups = true;
+ ProtectKernelLogs = true;
+ ProtectKernelModules = true;
+ ProtectKernelTunables = true;
+ ProtectSystem = "strict";
+ ReadWritePaths = cfg.basePath;
+ RestrictNamespaces = true;
+ RestrictSUIDSGID = true;
+ Restart = "on-failure";
+ RestartSec = 5;
+ };
+ wantedBy = mkIf cfg.autoStart [ "multi-user.target" ];
+ wants = [ "network.target" ];
+ };
+
+ networking.firewall = lib.mkIf cfg.openFirewall {
+ allowedTCPPorts = [ cfg.port ];
+ };
+ };
+
+ meta.maintainers = [
+ lib.maintainers.luNeder
+ lib.maintainers.emilioziniades
+ ];
+}
diff --git a/nixos/modules/services/web-apps/davis.md b/nixos/modules/services/web-apps/davis.md
index 9775d8221b5b..d654f2fd7f43 100644
--- a/nixos/modules/services/web-apps/davis.md
+++ b/nixos/modules/services/web-apps/davis.md
@@ -24,9 +24,10 @@ After that, `davis` can be deployed like this:
adminLogin = "admin";
adminPasswordFile = "/run/secrets/davis-admin-password";
appSecretFile = "/run/secrets/davis-app-secret";
- nginx = {};
};
}
```
This deploys Davis using a sqlite database running out of `/var/lib/davis`.
+
+Logs can be found in `/var/lib/davis/var/log/`.
diff --git a/nixos/modules/services/web-apps/davis.nix b/nixos/modules/services/web-apps/davis.nix
index 45748c346e2e..23a28ed84fd3 100644
--- a/nixos/modules/services/web-apps/davis.nix
+++ b/nixos/modules/services/web-apps/davis.nix
@@ -220,10 +220,13 @@ in
};
nginx = lib.mkOption {
- type = lib.types.submodule (
- lib.recursiveUpdate (import ../web-servers/nginx/vhost-options.nix { inherit config lib; }) { }
+ type = lib.types.nullOr (
+ lib.types.submodule (
+ lib.recursiveUpdate (import ../web-servers/nginx/vhost-options.nix { inherit config lib; }) {
+ }
+ )
);
- default = null;
+ default = { };
example = ''
{
serverAliases = [
@@ -235,7 +238,7 @@ in
}
'';
description = ''
- With this option, you can customize the nginx virtualHost settings.
+ Use this option to customize an nginx virtual host. To disable the nginx set this to null.
'';
};
@@ -308,18 +311,16 @@ in
message = "One of services.davis.database.urlFile or services.davis.database.createLocally must be set.";
}
{
- assertion = (mail.dsn != null) != (mail.dsnFile != null);
- message = "One of (and only one of) services.davis.mail.dsn or services.davis.mail.dsnFile must be set.";
+ assertion = !(mail.dsn != null && mail.dsnFile != null);
+ message = "services.davis.mail.dsn and services.davis.mail.dsnFile cannot both be set.";
}
];
services.davis.config =
{
APP_ENV = "prod";
APP_CACHE_DIR = "${cfg.dataDir}/var/cache";
- # note: we do not need the log dir (we log to stdout/journald), by davis/symfony will try to create it, and the default value is one in the nix-store
- # so we set it to a path under dataDir to avoid something like: Unable to create the "logs" directory (/nix/store/5cfskz0ybbx37s1161gjn5klwb5si1zg-davis-4.4.1/var/log).
APP_LOG_DIR = "${cfg.dataDir}/var/log";
- LOG_FILE_PATH = "/dev/stdout";
+ LOG_FILE_PATH = "%kernel.logs_dir%/%kernel.environment%.log";
DATABASE_DRIVER = db.driver;
INVITE_FROM_ADDRESS = mail.inviteFromAddress;
APP_SECRET._secret = cfg.appSecretFile;
@@ -330,7 +331,14 @@ in
CALDAV_ENABLED = true;
CARDDAV_ENABLED = true;
}
- // (if mail.dsn != null then { MAILER_DSN = mail.dsn; } else { MAILER_DSN._secret = mail.dsnFile; })
+ // (
+ if mail.dsn != null then
+ { MAILER_DSN = mail.dsn; }
+ else if mail.dsnFile != null then
+ { MAILER_DSN._secret = mail.dsnFile; }
+ else
+ { }
+ )
// (
if db.createLocally then
{
@@ -381,6 +389,7 @@ in
APP_CACHE_DIR = "${cfg.dataDir}/var/cache";
APP_LOG_DIR = "${cfg.dataDir}/var/log";
};
+ phpPackage = lib.mkDefault cfg.package.passthru.php;
settings =
{
"listen.mode" = "0660";
diff --git a/nixos/modules/services/web-apps/dependency-track.nix b/nixos/modules/services/web-apps/dependency-track.nix
index 129770926222..32f25e81f242 100644
--- a/nixos/modules/services/web-apps/dependency-track.nix
+++ b/nixos/modules/services/web-apps/dependency-track.nix
@@ -509,9 +509,27 @@ in
upstreams.dependency-track.servers."localhost:${toString cfg.port}" = { };
virtualHosts.${cfg.nginx.domain} = {
locations = {
- "/".alias = "${cfg.package.frontend}/dist/";
+ "/" = {
+ alias = "${cfg.package.frontend}/dist/";
+ index = "index.html";
+ tryFiles = "$uri $uri/ /index.html";
+ extraConfig = ''
+ location ~ (index\.html)$ {
+ add_header Cache-Control "max-age=0, no-cache, no-store, must-revalidate";
+ add_header Pragma "no-cache";
+ add_header Expires 0;
+ }
+ '';
+ };
"/api".proxyPass = "http://dependency-track";
- "= /static/config.json".alias = frontendConfigFile;
+ "= /static/config.json" = {
+ alias = frontendConfigFile;
+ extraConfig = ''
+ add_header Cache-Control "max-age=0, no-cache, no-store, must-revalidate";
+ add_header Pragma "no-cache";
+ add_header Expires 0;
+ '';
+ };
};
};
};
diff --git a/nixos/modules/services/web-apps/discourse.nix b/nixos/modules/services/web-apps/discourse.nix
index 1d7b3c2ff7be..b454ed97744f 100644
--- a/nixos/modules/services/web-apps/discourse.nix
+++ b/nixos/modules/services/web-apps/discourse.nix
@@ -13,8 +13,8 @@ let
cfg = config.services.discourse;
opt = options.services.discourse;
- # Keep in sync with https://github.com/discourse/discourse_docker/blob/main/image/base/slim.Dockerfile#L5
- upstreamPostgresqlVersion = lib.getVersion pkgs.postgresql_13;
+ # Keep in sync with https://github.com/discourse/discourse_docker/blob/main/image/base/Dockerfile PG_MAJOR
+ upstreamPostgresqlVersion = lib.getVersion pkgs.postgresql_15;
postgresqlPackage =
if config.services.postgresql.enable then config.services.postgresql.package else pkgs.postgresql;
@@ -676,6 +676,8 @@ in
dns_query_timeout_secs = null;
regex_timeout_seconds = 2;
allow_impersonation = true;
+ log_line_max_chars = 160000;
+ yjit_enabled = false;
};
services.redis.servers.discourse =
@@ -901,6 +903,9 @@ in
extraConfig
+ ''
proxy_set_header X-Request-Start "t=''${msec}";
+ proxy_set_header X-Sendfile-Type "";
+ proxy_set_header X-Accel-Mapping "";
+ proxy_set_header Client-Ip "";
'';
};
cache = time: ''
diff --git a/nixos/modules/services/web-apps/eintopf.nix b/nixos/modules/services/web-apps/eintopf.nix
index c814e58f6aca..d9bf612042a0 100644
--- a/nixos/modules/services/web-apps/eintopf.nix
+++ b/nixos/modules/services/web-apps/eintopf.nix
@@ -15,14 +15,14 @@ in
{
options.services.eintopf = {
- enable = mkEnableOption "Eintopf community event calendar web app";
+ enable = mkEnableOption "Lauti (Eintopf) community event calendar web app";
settings = mkOption {
type = types.attrsOf types.str;
default = { };
description = ''
Settings to configure web service. See
-
+
for available options.
'';
example = literalExpression ''
@@ -54,7 +54,7 @@ in
wants = [ "network-online.target" ];
environment = cfg.settings;
serviceConfig = {
- ExecStart = "${pkgs.eintopf}/bin/eintopf";
+ ExecStart = lib.getExe pkgs.lauti;
WorkingDirectory = "/var/lib/eintopf";
StateDirectory = "eintopf";
EnvironmentFile = [ cfg.secrets ];
diff --git a/nixos/modules/services/web-apps/gancio.nix b/nixos/modules/services/web-apps/gancio.nix
index 230e93737fdb..aec066112132 100644
--- a/nixos/modules/services/web-apps/gancio.nix
+++ b/nixos/modules/services/web-apps/gancio.nix
@@ -57,7 +57,7 @@ in
default = "http${
lib.optionalString config.services.nginx.virtualHosts."${cfg.settings.hostname}".enableACME "s"
}://${cfg.settings.hostname}";
- defaultText = lib.literalExpression ''"https://''${cfg.settings.hostname}"'';
+ defaultText = lib.literalExpression ''"https://''${config.services.gancio.settings.hostname}"'';
example = "https://demo.gancio.org/gancio";
description = "The full URL under which the server is reachable.";
};
@@ -89,9 +89,7 @@ in
readOnly = true;
type = types.nullOr types.str;
default = if cfg.settings.db.dialect == "sqlite" then "/var/lib/gancio/db.sqlite" else null;
- defaultText = ''
- if cfg.settings.db.dialect == "sqlite" then "/var/lib/gancio/db.sqlite" else null
- '';
+ defaultText = ''if config.services.gancio.settings.db.dialect == "sqlite" then "/var/lib/gancio/db.sqlite" else null'';
};
host = mkOption {
description = ''
@@ -100,9 +98,7 @@ in
readOnly = true;
type = types.nullOr types.str;
default = if cfg.settings.db.dialect == "postgres" then "/run/postgresql" else null;
- defaultText = ''
- if cfg.settings.db.dialect == "postgres" then "/run/postgresql" else null
- '';
+ defaultText = ''if config.services.gancio.settings.db.dialect == "postgres" then "/run/postgresql" else null'';
};
database = mkOption {
description = ''
@@ -111,9 +107,7 @@ in
readOnly = true;
type = types.nullOr types.str;
default = if cfg.settings.db.dialect == "postgres" then cfg.user else null;
- defaultText = ''
- if cfg.settings.db.dialect == "postgres" then cfg.user else null
- '';
+ defaultText = ''if config.services.gancio.settings.db.dialect == "postgres" then cfg.user else null'';
};
};
log_level = mkOption {
@@ -174,10 +168,14 @@ in
environment.systemPackages = [
(pkgs.runCommand "gancio" { } ''
mkdir -p $out/bin
- echo "#!${pkgs.runtimeShell}
- cd /var/lib/gancio/
- exec ${lib.getExe cfg.package} ''${1:---help}
- " > $out/bin/gancio
+ echo '#!${pkgs.runtimeShell}
+ cd /var/lib/gancio/
+ sudo=exec
+ if [[ "$USER" != ${cfg.user} ]]; then
+ sudo="exec /run/wrappers/bin/sudo -u ${cfg.user}"
+ fi
+ $sudo ${lib.getExe cfg.package} "''${@:--help}"
+ ' > $out/bin/gancio
chmod +x $out/bin/gancio
'')
];
diff --git a/nixos/modules/services/web-apps/gerrit.nix b/nixos/modules/services/web-apps/gerrit.nix
index d7d19ad84c05..2fc4c349b58c 100644
--- a/nixos/modules/services/web-apps/gerrit.nix
+++ b/nixos/modules/services/web-apps/gerrit.nix
@@ -261,6 +261,7 @@ in
meta.maintainers = with lib.maintainers; [
edef
zimbatm
+ felixsinger
];
# uses attributes of the linked package
meta.buildDocsInSandbox = false;
diff --git a/nixos/modules/services/web-apps/glance.nix b/nixos/modules/services/web-apps/glance.nix
index 69170858f7c8..ff5fd1195639 100644
--- a/nixos/modules/services/web-apps/glance.nix
+++ b/nixos/modules/services/web-apps/glance.nix
@@ -8,15 +8,27 @@ let
cfg = config.services.glance;
inherit (lib)
- mkEnableOption
- mkPackageOption
- mkOption
- mkIf
+ catAttrs
+ concatMapStrings
getExe
+ mkEnableOption
+ mkIf
+ mkOption
+ mkPackageOption
types
;
+ inherit (builtins)
+ concatLists
+ isAttrs
+ isList
+ attrNames
+ getAttr
+ ;
+
settingsFormat = pkgs.formats.yaml { };
+ settingsFile = settingsFormat.generate "glance.yaml" cfg.settings;
+ mergedSettingsFile = "/run/glance/glance.yaml";
in
{
options.services.glance = {
@@ -69,7 +81,9 @@ in
{ type = "calendar"; }
{
type = "weather";
- location = "Nivelles, Belgium";
+ location = {
+ _secret = "/var/lib/secrets/glance/location";
+ };
}
];
}
@@ -84,6 +98,13 @@ in
Configuration written to a yaml file that is read by glance. See
for more.
+
+ Settings containing secret data should be set to an
+ attribute set containing the attribute
+ _secret - a string pointing to a file
+ containing the value the option should be set to. See the
+ example in `services.glance.settings.pages` at the weather widget
+ with a location secret to get a better picture of this.
'';
};
@@ -102,13 +123,41 @@ in
description = "Glance feed dashboard server";
wantedBy = [ "multi-user.target" ];
after = [ "network.target" ];
+ path = [ pkgs.replace-secret ];
serviceConfig = {
- ExecStart =
+ ExecStartPre =
let
- glance-yaml = settingsFormat.generate "glance.yaml" cfg.settings;
+ findSecrets =
+ data:
+ if isAttrs data then
+ if data ? _secret then
+ [ data ]
+ else
+ concatLists (map (attr: findSecrets (getAttr attr data)) (attrNames data))
+ else if isList data then
+ concatLists (map findSecrets data)
+ else
+ [ ];
+ secretPaths = catAttrs "_secret" (findSecrets cfg.settings);
+ mkSecretReplacement = secretPath: ''
+ replace-secret ${
+ lib.escapeShellArgs [
+ "_secret: ${secretPath}"
+ secretPath
+ mergedSettingsFile
+ ]
+ }
+ '';
+ secretReplacements = concatMapStrings mkSecretReplacement secretPaths;
in
- "${getExe cfg.package} --config ${glance-yaml}";
+ # Use "+" to run as root because the secrets may not be accessible to glance
+ "+"
+ + pkgs.writeShellScript "glance-start-pre" ''
+ install -m 600 -o $USER ${settingsFile} ${mergedSettingsFile}
+ ${secretReplacements}
+ '';
+ ExecStart = "${getExe cfg.package} --config ${mergedSettingsFile}";
WorkingDirectory = "/var/lib/glance";
StateDirectory = "glance";
RuntimeDirectory = "glance";
diff --git a/nixos/modules/services/web-apps/homebox.nix b/nixos/modules/services/web-apps/homebox.nix
index f2e17a1b85f0..132cc874d5c1 100644
--- a/nixos/modules/services/web-apps/homebox.nix
+++ b/nixos/modules/services/web-apps/homebox.nix
@@ -23,7 +23,8 @@ in
defaultText = lib.literalExpression ''
{
HBOX_STORAGE_DATA = "/var/lib/homebox/data";
- HBOX_STORAGE_SQLITE_URL = "/var/lib/homebox/data/homebox.db?_pragma=busy_timeout=999&_pragma=journal_mode=WAL&_fk=1";
+ HBOX_DATABASE_DRIVER = "sqlite3";
+ HBOX_DATABASE_SQLITE_PATH = "/var/lib/homebox/data/homebox.db?_pragma=busy_timeout=999&_pragma=journal_mode=WAL&_fk=1";
HBOX_OPTIONS_ALLOW_REGISTRATION = "false";
HBOX_OPTIONS_CHECK_GITHUB_RELEASE = "false";
HBOX_MODE = "production";
@@ -34,6 +35,15 @@ in
[documentation](https://homebox.software/en/configure-homebox.html).
'';
};
+ database = {
+ createLocally = lib.mkOption {
+ type = lib.types.bool;
+ default = false;
+ description = ''
+ Configure local PostgreSQL database server for Homebox.
+ '';
+ };
+ };
};
config = mkIf cfg.enable {
@@ -42,15 +52,37 @@ in
group = "homebox";
};
users.groups.homebox = { };
- services.homebox.settings = {
- HBOX_STORAGE_DATA = mkDefault "/var/lib/homebox/data";
- HBOX_STORAGE_SQLITE_URL = mkDefault "/var/lib/homebox/data/homebox.db?_pragma=busy_timeout=999&_pragma=journal_mode=WAL&_fk=1";
- HBOX_OPTIONS_ALLOW_REGISTRATION = mkDefault "false";
- HBOX_OPTIONS_CHECK_GITHUB_RELEASE = mkDefault "false";
- HBOX_MODE = mkDefault "production";
+ services.homebox.settings = lib.mkMerge [
+ (lib.mapAttrs (_: mkDefault) {
+ HBOX_STORAGE_DATA = "/var/lib/homebox/data";
+ HBOX_DATABASE_DRIVER = "sqlite3";
+ HBOX_DATABASE_SQLITE_PATH = "/var/lib/homebox/data/homebox.db?_pragma=busy_timeout=999&_pragma=journal_mode=WAL&_fk=1";
+ HBOX_OPTIONS_ALLOW_REGISTRATION = "false";
+ HBOX_OPTIONS_CHECK_GITHUB_RELEASE = "false";
+ HBOX_MODE = "production";
+ })
+
+ (lib.mkIf cfg.database.createLocally {
+ HBOX_DATABASE_DRIVER = "postgres";
+ HBOX_DATABASE_HOST = "/run/postgresql";
+ HBOX_DATABASE_USERNAME = "homebox";
+ HBOX_DATABASE_DATABASE = "homebox";
+ HBOX_DATABASE_PORT = toString config.services.postgresql.settings.port;
+ })
+ ];
+ services.postgresql = lib.mkIf cfg.database.createLocally {
+ enable = true;
+ ensureDatabases = [ "homebox" ];
+ ensureUsers = [
+ {
+ name = "homebox";
+ ensureDBOwnership = true;
+ }
+ ];
};
systemd.services.homebox = {
- after = [ "network.target" ];
+ requires = lib.optional cfg.database.createLocally "postgresql.service";
+ after = lib.optional cfg.database.createLocally "postgresql.service";
environment = cfg.settings;
serviceConfig = {
User = "homebox";
@@ -80,6 +112,7 @@ in
ProcSubset = "pid";
ProtectSystem = "strict";
RestrictAddressFamilies = [
+ "AF_UNIX"
"AF_INET"
"AF_INET6"
"AF_NETLINK"
diff --git a/nixos/modules/services/web-apps/immich-public-proxy.nix b/nixos/modules/services/web-apps/immich-public-proxy.nix
index 85238e1cbacf..817c79bd534c 100644
--- a/nixos/modules/services/web-apps/immich-public-proxy.nix
+++ b/nixos/modules/services/web-apps/immich-public-proxy.nix
@@ -92,7 +92,6 @@ in
};
networking.firewall.allowedTCPPorts = mkIf cfg.openFirewall [ cfg.port ];
-
- meta.maintainers = with lib.maintainers; [ jaculabilis ];
};
+ meta.maintainers = with lib.maintainers; [ jaculabilis ];
}
diff --git a/nixos/modules/services/web-apps/immich.nix b/nixos/modules/services/web-apps/immich.nix
index cc7d5ff6f39e..a647b552678f 100644
--- a/nixos/modules/services/web-apps/immich.nix
+++ b/nixos/modules/services/web-apps/immich.nix
@@ -389,7 +389,6 @@ in
};
};
users.groups = mkIf (cfg.group == "immich") { immich = { }; };
-
- meta.maintainers = with lib.maintainers; [ jvanbruegge ];
};
+ meta.maintainers = with lib.maintainers; [ jvanbruegge ];
}
diff --git a/nixos/modules/services/web-apps/karakeep.nix b/nixos/modules/services/web-apps/karakeep.nix
new file mode 100644
index 000000000000..14ec552a41d6
--- /dev/null
+++ b/nixos/modules/services/web-apps/karakeep.nix
@@ -0,0 +1,225 @@
+{
+ config,
+ lib,
+ pkgs,
+ ...
+}:
+let
+ cfg = config.services.karakeep;
+
+ karakeepEnv = lib.mkMerge [
+ { DATA_DIR = "/var/lib/karakeep"; }
+ (lib.mkIf cfg.meilisearch.enable {
+ MEILI_ADDR = "http://127.0.0.1:${toString config.services.meilisearch.listenPort}";
+ })
+ (lib.mkIf cfg.browser.enable {
+ BROWSER_WEB_URL = "http://127.0.0.1:${toString cfg.browser.port}";
+ })
+ cfg.extraEnvironment
+ ];
+
+ environmentFiles = [
+ "/var/lib/karakeep/settings.env"
+ ] ++ (lib.optional (cfg.environmentFile != null) cfg.environmentFile);
+in
+{
+ options = {
+ services.karakeep = {
+ enable = lib.mkEnableOption "Enable the Karakeep service";
+ package = lib.mkPackageOption pkgs "karakeep" { };
+
+ extraEnvironment = lib.mkOption {
+ description = ''
+ Environment variables to pass to Karakaeep. This is how most settings
+ can be configured. Changing DATA_DIR is possible but not supported.
+
+ See https://docs.karakeep.app/configuration/
+ '';
+ type = lib.types.attrsOf lib.types.str;
+ default = { };
+ example = lib.literalExpression ''
+ {
+ PORT = "1234";
+ DISABLE_SIGNUPS = "true";
+ DISABLE_NEW_RELEASE_CHECK = "true";
+ }
+ '';
+ };
+
+ environmentFile = lib.mkOption {
+ type = lib.types.nullOr lib.types.path;
+ default = null;
+ description = ''
+ An optional path to an environment file that will be used in the web and workers
+ services. This is useful for loading private keys.
+ '';
+ example = "/var/lib/karakeep/secrets.env";
+ };
+
+ browser = {
+ enable = lib.mkOption {
+ description = ''
+ Enable the karakeep-browser service that runs a chromium instance in
+ the background with debugging ports exposed. This is necessary for
+ certain features like screenshots.
+ '';
+ type = lib.types.bool;
+ default = true;
+ };
+ port = lib.mkOption {
+ description = "The port the browser should run on.";
+ type = lib.types.port;
+ default = 9222;
+ };
+ exe = lib.mkOption {
+ description = "The browser executable (must be Chrome-like).";
+ type = lib.types.str;
+ default = "${pkgs.chromium}/bin/chromium";
+ defaultText = lib.literalExpression "\${pkgs.chromium}/bin/chromium";
+ example = lib.literalExpression "\${pkgs.google-chrome}/bin/google-chrome-stable";
+ };
+ };
+
+ meilisearch = {
+ enable = lib.mkOption {
+ type = lib.types.bool;
+ default = true;
+ description = ''
+ Enable Meilisearch and configure Karakeep to use it. Meilisearch is
+ required for text search.
+ '';
+ };
+ };
+ };
+ };
+
+ config = lib.mkIf cfg.enable {
+ environment.systemPackages = [ cfg.package ];
+
+ users.groups.karakeep = { };
+ users.users.karakeep = {
+ isSystemUser = true;
+ group = "karakeep";
+ };
+
+ services.meilisearch = lib.mkIf cfg.meilisearch.enable {
+ enable = true;
+ };
+
+ systemd.services.karakeep-init = {
+ description = "Initialize Karakeep Data";
+ wantedBy = [ "multi-user.target" ];
+ after = [ "network.target" ];
+ partOf = [ "karakeep.service" ];
+ path = [ pkgs.openssl ];
+ script = ''
+ umask 0077
+
+ if [ ! -f "$STATE_DIRECTORY/settings.env" ]; then
+ cat <"$STATE_DIRECTORY/settings.env"
+ # Generated by NixOS Karakeep module
+ MEILI_MASTER_KEY=$(openssl rand -base64 36)
+ NEXTAUTH_SECRET=$(openssl rand -base64 36)
+ EOF
+ fi
+
+ export DATA_DIR="$STATE_DIRECTORY"
+ exec "${cfg.package}/lib/karakeep/migrate"
+ '';
+ serviceConfig = {
+ Type = "oneshot";
+ RemainAfterExit = true;
+ User = "karakeep";
+ Group = "karakeep";
+ StateDirectory = "karakeep";
+ PrivateTmp = "yes";
+ };
+ };
+
+ systemd.services.karakeep-workers = {
+ description = "Karakeep Workers";
+ wantedBy = [ "multi-user.target" ];
+ after = [
+ "network.target"
+ "karakeep-init.service"
+ ];
+ partOf = [ "karakeep.service" ];
+ path = [
+ pkgs.monolith
+ pkgs.yt-dlp
+ ];
+ environment = karakeepEnv;
+ serviceConfig = {
+ User = "karakeep";
+ Group = "karakeep";
+ ExecStart = "${cfg.package}/lib/karakeep/start-workers";
+ StateDirectory = "karakeep";
+ EnvironmentFile = environmentFiles;
+ PrivateTmp = "yes";
+ };
+ };
+
+ systemd.services.karakeep-web = {
+ description = "Karakeep Web";
+ wantedBy = [ "multi-user.target" ];
+ after = [
+ "network.target"
+ "karakeep-init.service"
+ "karakeep-workers.service"
+ ];
+ partOf = [ "karakeep.service" ];
+ environment = karakeepEnv;
+ serviceConfig = {
+ ExecStart = "${cfg.package}/lib/karakeep/start-web";
+ User = "karakeep";
+ Group = "karakeep";
+ StateDirectory = "karakeep";
+ EnvironmentFile = environmentFiles;
+ PrivateTmp = "yes";
+ };
+ };
+
+ systemd.services.karakeep-browser = lib.mkIf cfg.browser.enable {
+ wantedBy = [ "multi-user.target" ];
+ after = [ "network.target" ];
+ partOf = [ "karakeep.service" ];
+ script = ''
+ export HOME="$CACHE_DIRECTORY"
+ exec ${cfg.browser.exe} \
+ --headless --no-sandbox --disable-gpu --disable-dev-shm-usage \
+ --remote-debugging-address=127.0.0.1 \
+ --remote-debugging-port=${toString cfg.browser.port} \
+ --hide-scrollbars \
+ --user-data-dir="$STATE_DIRECTORY"
+ '';
+ serviceConfig = {
+ Type = "simple";
+ Restart = "on-failure";
+
+ CacheDirectory = "karakeep-browser";
+ StateDirectory = "karakeep-browser";
+
+ DevicePolicy = "closed";
+ DynamicUser = true;
+ LockPersonality = true;
+ NoNewPrivileges = true;
+ PrivateDevices = true;
+ PrivateTmp = true;
+ PrivateUsers = true;
+ ProtectClock = true;
+ ProtectControlGroups = true;
+ ProtectHostname = true;
+ ProtectKernelLogs = true;
+ ProtectKernelModules = true;
+ ProtectKernelTunables = true;
+ ProtectSystem = "strict";
+ RestrictNamespaces = true;
+ RestrictRealtime = true;
+ };
+ };
+ };
+
+ meta = {
+ maintainers = [ lib.maintainers.three ];
+ };
+}
diff --git a/nixos/modules/services/web-apps/kimai.nix b/nixos/modules/services/web-apps/kimai.nix
index 47e1dfc20981..4ca464489b47 100644
--- a/nixos/modules/services/web-apps/kimai.nix
+++ b/nixos/modules/services/web-apps/kimai.nix
@@ -324,12 +324,14 @@ in
${pkg hostName cfg}/bin/console lint:yaml --parse-tags \
${pkg hostName cfg}/share/php/kimai/config
- # Run kimai:install to ensure database is created or updated.
+ # Before running any further console commands, clear cache. This
+ # avoids errors due to old cache getting used with new version
+ # of Kimai.
+ ${pkg hostName cfg}/bin/console cache:clear --env=prod
+ # Then, run kimai:install to ensure database is created or updated.
# Note that kimai:update is an alias to kimai:install.
${pkg hostName cfg}/bin/console kimai:install --no-cache
- # Clear cache and warmup cache separately, to avoid "Cannot declare
- # class App\Entity\Timesheet" error on first init after upgrade.
- ${pkg hostName cfg}/bin/console cache:clear --env=prod
+ # Finally, warm up cache.
${pkg hostName cfg}/bin/console cache:warmup --env=prod
'';
diff --git a/nixos/modules/services/web-apps/mastodon.nix b/nixos/modules/services/web-apps/mastodon.nix
index 80352fc4c920..da6a09a00101 100644
--- a/nixos/modules/services/web-apps/mastodon.nix
+++ b/nixos/modules/services/web-apps/mastodon.nix
@@ -55,6 +55,9 @@ let
// lib.optionalAttrs cfg.smtp.authenticate { SMTP_LOGIN = cfg.smtp.user; }
// lib.optionalAttrs (cfg.elasticsearch.host != null) { ES_HOST = cfg.elasticsearch.host; }
// lib.optionalAttrs (cfg.elasticsearch.host != null) { ES_PORT = toString cfg.elasticsearch.port; }
+ // lib.optionalAttrs (cfg.elasticsearch.host != null && cfg.elasticsearch.prefix != null) {
+ ES_PREFIX = cfg.elasticsearch.prefix;
+ }
// lib.optionalAttrs (cfg.elasticsearch.host != null) { ES_PRESET = cfg.elasticsearch.preset; }
// lib.optionalAttrs (cfg.elasticsearch.user != null) { ES_USER = cfg.elasticsearch.user; }
// cfg.extraConfig;
@@ -670,6 +673,16 @@ in
default = 9200;
};
+ prefix = lib.mkOption {
+ description = ''
+ If provided, adds a prefix to indexes in Elasticsearch. This allows to use the same
+ Elasticsearch cluster between different projects or Mastodon servers.
+ '';
+ type = lib.types.nullOr lib.types.str;
+ default = null;
+ example = "mastodon";
+ };
+
preset = lib.mkOption {
description = ''
It controls the ElasticSearch indices configuration (number of shards and replica).
diff --git a/nixos/modules/services/web-apps/mattermost.nix b/nixos/modules/services/web-apps/mattermost.nix
index b1352778fee0..dce0774b07ee 100644
--- a/nixos/modules/services/web-apps/mattermost.nix
+++ b/nixos/modules/services/web-apps/mattermost.nix
@@ -41,9 +41,15 @@ let
# The directory to store mutable data within dataDir.
mutableDataDir = "${cfg.dataDir}/data";
- # The plugin directory. Note that this is the *post-unpack* plugin directory,
- # since Mattermost unpacks plugins to put them there. (Hence, mutable data.)
- pluginDir = "${mutableDataDir}/plugins";
+ # The plugin directory. Note that this is the *pre-unpack* plugin directory,
+ # since Mattermost looks in mutableDataDir for a directory called "plugins".
+ # If Mattermost is installed with plugins defined in a Nix configuration, the plugins
+ # are symlinked here. Otherwise, this is a real directory and the tarballs are uploaded here.
+ pluginTarballDir = "${mutableDataDir}/plugins";
+
+ # We need a different unpack directory for Mattermost to sync things to at launch,
+ # since the above may be a symlink to the store.
+ pluginUnpackDir = "${mutableDataDir}/.plugins";
# Mattermost uses this as a staging directory to unpack plugins, among possibly other things.
# Ensure that it's inside mutableDataDir since it can get rather large.
@@ -147,34 +153,34 @@ let
else
throw "Invalid database driver: ${cfg.database.driver}";
- mattermostPluginDerivations =
- with pkgs;
- map (
- plugin:
- stdenv.mkDerivation {
- name = "mattermost-plugin";
- installPhase = ''
- mkdir -p $out/share
- cp ${plugin} $out/share/plugin.tar.gz
- '';
- dontUnpack = true;
- dontPatch = true;
- dontConfigure = true;
- dontBuild = true;
- preferLocalBuild = true;
- }
- ) cfg.plugins;
+ mattermostPluginDerivations = map (
+ plugin:
+ pkgs.stdenvNoCC.mkDerivation {
+ name = "${cfg.package.name}-plugin";
+ installPhase = ''
+ runHook preInstall
+ mkdir -p $out/share
+ ln -sf ${plugin} $out/share/plugin.tar.gz
+ runHook postInstall
+ '';
+ dontUnpack = true;
+ dontPatch = true;
+ dontConfigure = true;
+ dontBuild = true;
+ preferLocalBuild = true;
+ }
+ ) cfg.plugins;
mattermostPlugins =
- with pkgs;
if mattermostPluginDerivations == [ ] then
null
else
- stdenv.mkDerivation {
+ pkgs.stdenvNoCC.mkDerivation {
name = "${cfg.package.name}-plugins";
- nativeBuildInputs = [ autoPatchelfHook ] ++ mattermostPluginDerivations;
+ nativeBuildInputs = [ pkgs.autoPatchelfHook ] ++ mattermostPluginDerivations;
buildInputs = [ cfg.package ];
installPhase = ''
+ runHook preInstall
mkdir -p $out
plugins=(${
escapeShellArgs (map (plugin: "${plugin}/share/plugin.tar.gz") mattermostPluginDerivations)
@@ -187,6 +193,7 @@ let
GZIP_OPT=-9 tar -C "$hash" -cvzf "$out/$hash.tar.gz" .
rm -rf "$hash"
done
+ runHook postInstall
'';
dontUnpack = true;
@@ -231,9 +238,12 @@ let
services.mattermost.environmentFile = "";
services.mattermost.database.fromEnvironment = true;
'' database;
- FileSettings.Directory = cfg.dataDir;
- PluginSettings.Directory = "${pluginDir}/server";
- PluginSettings.ClientDirectory = "${pluginDir}/client";
+
+ # Note that the plugin tarball directory is not configurable, and is expected to be in FileSettings.Directory/plugins.
+ FileSettings.Directory = mutableDataDir;
+ PluginSettings.Directory = "${pluginUnpackDir}/server";
+ PluginSettings.ClientDirectory = "${pluginUnpackDir}/client";
+
LogSettings = {
FileLocation = cfg.logDir;
@@ -254,8 +264,8 @@ let
}
);
- mattermostConfJSON = pkgs.writeText "mattermost-config.json" (builtins.toJSON mattermostConf);
-
+ format = pkgs.formats.json { };
+ finalConfig = format.generate "mattermost-config.json" mattermostConf;
in
{
imports = [
@@ -454,9 +464,9 @@ in
the options specified in services.mattermost will be generated
but won't be overwritten on changes or rebuilds.
- If this option is disabled, changes in the system console won't
- be possible (default). If an config.json is present, it will be
- overwritten!
+ If this option is disabled, persistent changes in the system
+ console won't be possible (the default). If a config.json is
+ present, it will be overwritten at service start!
'';
};
@@ -480,7 +490,20 @@ in
description = ''
Plugins to add to the configuration. Overrides any installed if non-null.
This is a list of paths to .tar.gz files or derivations evaluating to
- .tar.gz files.
+ .tar.gz files. You can use `mattermost.buildPlugin` to build plugins;
+ see the NixOS documentation for more details.
+ '';
+ };
+
+ pluginsBundle = mkOption {
+ type = with types; nullOr package;
+ default = mattermostPlugins;
+ defaultText = ''
+ All entries in {config}`services.mattermost.plugins`, repacked
+ '';
+ description = ''
+ Derivation building to a directory of plugin tarballs.
+ This overrides {option}`services.mattermost.plugins` if provided.
'';
};
@@ -508,7 +531,8 @@ in
type = with types; attrsOf (either int str);
default = { };
description = ''
- Extra environment variables to export to the Mattermost process, in the systemd unit.
+ Extra environment variables to export to the Mattermost process
+ from the systemd unit configuration.
'';
example = {
MM_SERVICESETTINGS_SITEURL = "http://example.com";
@@ -524,11 +548,11 @@ in
for mattermost (see [the Mattermost documentation](https://docs.mattermost.com/configure/configuration-settings.html#environment-variables)).
Settings defined in the environment file will overwrite settings
- set via nix or via the {option}`services.mattermost.extraConfig`
+ set via Nix or via the {option}`services.mattermost.extraConfig`
option.
Useful for setting config options without their value ending up in the
- (world-readable) nix store, e.g. for a database password.
+ (world-readable) Nix store, e.g. for a database password.
'';
};
@@ -639,13 +663,13 @@ in
if cfg.database.driver == "postgres" then
{
sslmode = "disable";
- connect_timeout = 30;
+ connect_timeout = 60;
}
else if cfg.database.driver == "mysql" then
{
charset = "utf8mb4,utf8";
- writeTimeout = "30s";
- readTimeout = "30s";
+ writeTimeout = "60s";
+ readTimeout = "60s";
}
else
throw "Invalid database driver ${cfg.database.driver}";
@@ -653,13 +677,13 @@ in
if config.mattermost.database.driver == "postgres" then
{
sslmode = "disable";
- connect_timeout = 30;
+ connect_timeout = 60;
}
else if config.mattermost.database.driver == "mysql" then
{
charset = "utf8mb4,utf8";
- writeTimeout = "30s";
- readTimeout = "30s";
+ writeTimeout = "60s";
+ readTimeout = "60s";
}
else
throw "Invalid database driver";
@@ -687,7 +711,7 @@ in
};
settings = mkOption {
- type = types.attrs;
+ inherit (format) type;
default = { };
description = ''
Additional configuration options as Nix attribute set in config.json schema.
@@ -785,9 +809,9 @@ in
"R- ${tempDir} - - - - -"
"d= ${tempDir} 0750 ${cfg.user} ${cfg.group} - -"
- # Ensure that pluginDir is a directory, as it could be a symlink on prior versions.
- "r- ${pluginDir} - - - - -"
- "d= ${pluginDir} 0750 ${cfg.user} ${cfg.group} - -"
+ # Ensure that pluginUnpackDir is a directory.
+ # Don't remove or clean it out since it should be persistent, as this is where plugins are unpacked.
+ "d= ${pluginUnpackDir} 0750 ${cfg.user} ${cfg.group} - -"
# Ensure that the plugin directories exist.
"d= ${mattermostConf.PluginSettings.Directory} 0750 ${cfg.user} ${cfg.group} - -"
@@ -801,15 +825,14 @@ in
"L+ ${cfg.dataDir}/client - - - - ${cfg.package}/client"
]
++ (
- if mattermostPlugins == null then
- # Create the plugin tarball directory if it's a symlink.
+ if cfg.pluginsBundle == null then
+ # Create the plugin tarball directory to allow plugin uploads.
[
- "r- ${cfg.dataDir}/plugins - - - - -"
- "d= ${cfg.dataDir}/plugins 0750 ${cfg.user} ${cfg.group} - -"
+ "d= ${pluginTarballDir} 0750 ${cfg.user} ${cfg.group} - -"
]
else
- # Symlink the plugin tarball directory, removing anything existing.
- [ "L+ ${cfg.dataDir}/plugins - - - - ${mattermostPlugins}" ]
+ # Symlink the plugin tarball directory, removing anything existing, since it's managed by Nix.
+ [ "L+ ${pluginTarballDir} - - - - ${cfg.pluginsBundle}" ]
);
systemd.services.mattermost = rec {
@@ -836,7 +859,7 @@ in
configDir=${escapeShellArg cfg.configDir}
logDir=${escapeShellArg cfg.logDir}
package=${escapeShellArg cfg.package}
- nixConfig=${escapeShellArg mattermostConfJSON}
+ nixConfig=${escapeShellArg finalConfig}
''
+ optionalString (versionAtLeast config.system.stateVersion "25.05") ''
# Migrate configs in the pre-25.05 directory structure.
@@ -853,12 +876,13 @@ in
# Logs too.
oldLogs="$dataDir/logs"
newLogs="$logDir"
- if [ "$oldLogs" != "$newLogs" ] && [ -d "$oldLogs" ]; then
+ if [ "$oldLogs" != "$newLogs" ] && [ -d "$oldLogs" ] && [ ! -f "$newLogs/.initial-created" ]; then
# Migrate the legacy log location to the new log location.
# Allow this to fail if there aren't any logs to move.
echo "Moving legacy logs at $oldLogs to $newLogs" >&2
mkdir -p "$newLogs"
mv "$oldLogs"/* "$newLogs" || true
+ touch "$newLogs/.initial-created"
fi
''
+ optionalString (!cfg.mutableConfig) ''
diff --git a/nixos/modules/services/web-apps/mealie.nix b/nixos/modules/services/web-apps/mealie.nix
index 48b0450eac2d..b8f65b1fb98e 100644
--- a/nixos/modules/services/web-apps/mealie.nix
+++ b/nixos/modules/services/web-apps/mealie.nix
@@ -50,13 +50,24 @@ in
Expects the format of an `EnvironmentFile=`, as described by {manpage}`systemd.exec(5)`.
'';
};
+
+ database = {
+ createLocally = lib.mkOption {
+ type = lib.types.bool;
+ default = false;
+ description = ''
+ Configure local PostgreSQL database server for Mealie.
+ '';
+ };
+ };
};
config = lib.mkIf cfg.enable {
systemd.services.mealie = {
description = "Mealie, a self hosted recipe manager and meal planner";
- after = [ "network-online.target" ];
+ after = [ "network-online.target" ] ++ lib.optional cfg.database.createLocally "postgresql.service";
+ requires = lib.optional cfg.database.createLocally "postgresql.service";
wants = [ "network-online.target" ];
wantedBy = [ "multi-user.target" ];
@@ -65,7 +76,7 @@ in
API_PORT = toString cfg.port;
BASE_URL = "http://localhost:${toString cfg.port}";
DATA_DIR = "/var/lib/mealie";
- CRF_MODEL_PATH = "/var/lib/mealie/model.crfmodel";
+ NLTK_DATA = pkgs.nltk-data.averaged-perceptron-tagger-eng;
} // (builtins.mapAttrs (_: val: toString val) cfg.settings);
serviceConfig = {
@@ -78,5 +89,21 @@ in
StandardOutput = "journal";
};
};
+
+ services.mealie.settings = lib.mkIf cfg.database.createLocally {
+ DB_ENGINE = "postgres";
+ POSTGRES_URL_OVERRIDE = "postgresql://mealie:@/mealie?host=/run/postgresql";
+ };
+
+ services.postgresql = lib.mkIf cfg.database.createLocally {
+ enable = true;
+ ensureDatabases = [ "mealie" ];
+ ensureUsers = [
+ {
+ name = "mealie";
+ ensureDBOwnership = true;
+ }
+ ];
+ };
};
}
diff --git a/nixos/modules/services/web-apps/movim.nix b/nixos/modules/services/web-apps/movim.nix
index 7890e005f506..e9a00878606a 100644
--- a/nixos/modules/services/web-apps/movim.nix
+++ b/nixos/modules/services/web-apps/movim.nix
@@ -58,7 +58,7 @@ let
// lib.optionalAttrs (cfg.database.type == "postgresql") {
withPostgreSQL = true;
}
- // lib.optionalAttrs (cfg.database.type == "mysql") {
+ // lib.optionalAttrs (cfg.database.type == "mariadb") {
withMySQL = true;
}
);
@@ -168,7 +168,7 @@ let
dbService =
{
"postgresql" = "postgresql.service";
- "mysql" = "mysql.service";
+ "mariadb" = "mysql.service";
}
.${cfg.database.type};
@@ -475,10 +475,10 @@ in
database = {
type = mkOption {
type = types.enum [
- "mysql"
+ "mariadb"
"postgresql"
];
- example = "mysql";
+ example = "mariadb";
default = "postgresql";
description = "Database engine to use.";
};
@@ -621,7 +621,7 @@ in
DB_DRIVER =
{
"postgresql" = "pgsql";
- "mysql" = "mysql";
+ "mariadb" = "mysql";
}
.${cfg.database.type};
DB_HOST = "localhost";
@@ -791,7 +791,7 @@ in
}
);
- mysql = mkIf (cfg.database.createLocally && cfg.database.type == "mysql") {
+ mysql = mkIf (cfg.database.createLocally && cfg.database.type == "mariadb") {
enable = mkDefault true;
package = mkDefault pkgs.mariadb;
ensureDatabases = [ cfg.database.name ];
diff --git a/nixos/modules/services/web-apps/nextcloud.nix b/nixos/modules/services/web-apps/nextcloud.nix
index 4b3be5e9fda3..6f92c0ca5e58 100644
--- a/nixos/modules/services/web-apps/nextcloud.nix
+++ b/nixos/modules/services/web-apps/nextcloud.nix
@@ -9,6 +9,11 @@ with lib;
let
cfg = config.services.nextcloud;
+
+ overridePackage = cfg.package.override {
+ inherit (config.security.pki) caBundle;
+ };
+
fpm = config.services.phpfpm.pools.nextcloud;
jsonFormat = pkgs.formats.json { };
@@ -51,13 +56,13 @@ let
};
webroot =
- pkgs.runCommand "${cfg.package.name or "nextcloud"}-with-apps"
+ pkgs.runCommand "${overridePackage.name or "nextcloud"}-with-apps"
{
preferLocalBuild = true;
}
''
mkdir $out
- ln -sfv "${cfg.package}"/* "$out"
+ ln -sfv "${overridePackage}"/* "$out"
${concatStrings (
mapAttrsToList (
name: store:
@@ -116,7 +121,8 @@ let
++ (lib.optional (cfg.config.objectstore.s3.enable) "s3_secret:${cfg.config.objectstore.s3.secretFile}")
++ (lib.optional (
cfg.config.objectstore.s3.sseCKeyFile != null
- ) "s3_sse_c_key:${cfg.config.objectstore.s3.sseCKeyFile}");
+ ) "s3_sse_c_key:${cfg.config.objectstore.s3.sseCKeyFile}")
+ ++ (lib.optional (cfg.secretFile != null) "secret_file:${cfg.secretFile}");
requiresRuntimeSystemdCredentials = (lib.length runtimeSystemdCredentials) != 0;
@@ -184,8 +190,8 @@ let
mysqlLocal = cfg.database.createLocally && cfg.config.dbtype == "mysql";
pgsqlLocal = cfg.database.createLocally && cfg.config.dbtype == "pgsql";
- nextcloudGreaterOrEqualThan = versionAtLeast cfg.package.version;
- nextcloudOlderThan = versionOlder cfg.package.version;
+ nextcloudGreaterOrEqualThan = versionAtLeast overridePackage.version;
+ nextcloudOlderThan = versionOlder overridePackage.version;
# https://github.com/nextcloud/documentation/pull/11179
ocmProviderIsNotAStaticDirAnymore =
@@ -195,7 +201,6 @@ let
overrideConfig =
let
c = cfg.config;
- requiresReadSecretFunction = c.dbpassFile != null || c.objectstore.s3.enable;
objectstoreConfig =
let
s3 = c.objectstore.s3;
@@ -205,7 +210,7 @@ let
'class' => '\\OC\\Files\\ObjectStore\\S3',
'arguments' => [
'bucket' => '${s3.bucket}',
- 'autocreate' => ${boolToString s3.autocreate},
+ 'verify_bucket_exists' => ${boolToString s3.verify_bucket_exists},
'key' => '${s3.key}',
'secret' => nix_read_secret('s3_secret'),
${optionalString (s3.hostname != null) "'hostname' => '${s3.hostname}',"}
@@ -232,7 +237,7 @@ let
in
pkgs.writeText "nextcloud-config.php" ''
for more information.
+ '';
+ default = { };
+
+ type = lib.types.submodule {
+ freeformType = settingsFormat.type;
+
+ options = {
+ ListenAddressSingleHTTPFrontend = lib.mkOption {
+ type = lib.types.str;
+ description = ''
+ The address to listen on for the internal "microproxy" frontend.
+ '';
+ default = "127.0.0.1:8000";
+ example = "0.0.0.0:8000";
+ };
+ };
+ };
+ };
+
+ extraConfigFiles = lib.mkOption {
+ type = lib.types.listOf lib.types.path;
+ default = [ ];
+ example = [ "/run/secrets/olivetin.yaml" ];
+ description = ''
+ Config files to merge into the settings defined in [](#opt-services.olivetin.settings).
+ This is useful to avoid putting secrets into the nix store.
+ See for more information.
+ '';
+ };
+ };
+
+ config = lib.mkIf cfg.enable {
+ services.olivetin = {
+ path = with pkgs; [ bash ];
+ };
+
+ systemd.services.olivetin = {
+ description = "OliveTin";
+
+ wantedBy = [ "multi-user.target" ];
+
+ wants = [
+ "network-online.target"
+ "local-fs.target"
+ ];
+ after = [
+ "network-online.target"
+ "local-fs.target"
+ ];
+
+ inherit (cfg) path;
+
+ preStart = ''
+ tmp="$(mktemp -d)"
+ trap 'rm -rf "$tmp"' EXIT
+ cd "$tmp"
+
+ cp ${settingsFormat.generate "olivetin-config.yaml" cfg.settings} config.yaml
+ chmod +w config.yaml
+ for ((i=0; i < ${toString (lib.length cfg.extraConfigFiles)}; i++)); do
+ ${lib.getExe pkgs.yq} -yi '
+ def merge($y):
+ . as $x |
+ if ($x | type == "object") and ($y | type == "object") then
+ $x + $y + with_entries(select(.key | in($y)) | .key as $key | .value |= merge($y[$key]))
+ elif ($x | type == "array") and ($y | type == "array") then
+ $x + $y
+ else
+ $y
+ end;
+ merge($f | fromjson)
+ ' config.yaml --rawfile f <(${lib.getExe pkgs.yq} -c . "$CREDENTIALS_DIRECTORY/config-$i.yaml")
+ done
+ chmod -w config.yaml
+
+ mkdir -p /run/olivetin/config
+ mv config.yaml /run/olivetin/config/config.yaml
+ '';
+
+ serviceConfig = {
+ User = cfg.user;
+ Group = cfg.group;
+ RuntimeDirectory = "olivetin";
+ Restart = "always";
+
+ LoadCredential = lib.imap0 (i: path: "config-${toString i}.yaml:${path}") cfg.extraConfigFiles;
+
+ ExecStart = "${lib.getExe cfg.package} -configdir /run/olivetin/config";
+ };
+ };
+
+ users.users = lib.mkIf (cfg.user == "olivetin") {
+ olivetin = {
+ group = cfg.group;
+ isSystemUser = true;
+ };
+ };
+
+ users.groups = lib.mkIf (cfg.group == "olivetin") { olivetin = { }; };
+ };
+}
diff --git a/nixos/modules/services/web-apps/oncall.nix b/nixos/modules/services/web-apps/oncall.nix
new file mode 100644
index 000000000000..cd842606f278
--- /dev/null
+++ b/nixos/modules/services/web-apps/oncall.nix
@@ -0,0 +1,203 @@
+{
+ config,
+ lib,
+ pkgs,
+ ...
+}:
+let
+
+ cfg = config.services.oncall;
+ settingsFormat = pkgs.formats.yaml { };
+ configFile = settingsFormat.generate "oncall_extra_settings.yaml" cfg.settings;
+
+in
+{
+ options.services.oncall = {
+
+ enable = lib.mkEnableOption "Oncall web app";
+
+ package = lib.mkPackageOption pkgs "oncall" { };
+
+ database.createLocally = lib.mkEnableOption "Create the database and database user locally." // {
+ default = true;
+ };
+
+ settings = lib.mkOption {
+ type = lib.types.submodule {
+ freeformType = settingsFormat.type;
+ options = {
+ oncall_host = lib.mkOption {
+ type = lib.types.str;
+ default = "localhost";
+ description = "FQDN for the Oncall instance.";
+ };
+ db.conn = {
+ kwargs = {
+ user = lib.mkOption {
+ type = lib.types.str;
+ default = "oncall";
+ description = "Database user.";
+ };
+ host = lib.mkOption {
+ type = lib.types.str;
+ default = "localhost";
+ description = "Database host.";
+ };
+ database = lib.mkOption {
+ type = lib.types.str;
+ default = "oncall";
+ description = "Database name.";
+ };
+ };
+ str = lib.mkOption {
+ type = lib.types.str;
+ default = "%(scheme)s://%(user)s@%(host)s:%(port)s/%(database)s?charset=%(charset)s&unix_socket=/run/mysqld/mysqld.sock";
+ description = ''
+ Database connection scheme. The default specifies the
+ connection through a local socket.
+ '';
+ };
+ require_auth = lib.mkOption {
+ type = lib.types.bool;
+ default = true;
+ description = ''
+ Whether authentication is required to access the web app.
+ '';
+ };
+ };
+ };
+ };
+ default = { };
+ description = ''
+ Extra configuration options to append or override.
+ For available and default option values see
+ [upstream configuration file](https://github.com/linkedin/oncall/blob/master/configs/config.yaml)
+ and the administration part in the
+ [offical documentation](https://oncall.tools/docs/admin_guide.html).
+ '';
+ };
+
+ secretFile = lib.mkOption {
+ type = lib.types.pathWith {
+ inStore = false;
+ absolute = true;
+ };
+ example = "/run/keys/oncall-dbpassword";
+ description = ''
+ A YAML file containing secrets such as database or user passwords.
+ Some variables that can be considered secrets are:
+
+ - db.conn.kwargs.password:
+ Password used to authenticate to the database.
+
+ - session.encrypt_key:
+ Key for encrypting/signing session cookies.
+ Change to random long values in production.
+
+ - session.sign_key:
+ Key for encrypting/signing session cookies.
+ Change to random long values in production.
+ '';
+ };
+
+ };
+
+ config = lib.mkIf cfg.enable {
+
+ # Disable debug, only needed for development
+ services.oncall.settings = lib.mkMerge [
+ ({
+ debug = lib.mkDefault false;
+ auth.debug = lib.mkDefault false;
+ })
+ ];
+
+ services.uwsgi = {
+ enable = true;
+ plugins = [ "python3" ];
+ user = "oncall";
+ instance = {
+ type = "emperor";
+ vassals = {
+ oncall = {
+ type = "normal";
+ env = [
+ "PYTHONPATH=${pkgs.oncall.pythonPath}"
+ (
+ "ONCALL_EXTRA_CONFIG="
+ + (lib.concatStringsSep "," (
+ [ configFile ] ++ lib.optional (cfg.secretFile != null) cfg.secretFile
+ ))
+ )
+ "STATIC_ROOT=/var/lib/oncall"
+ ];
+ module = "oncall.app:get_wsgi_app()";
+ socket = "${config.services.uwsgi.runDir}/oncall.sock";
+ socketGroup = "nginx";
+ immediate-gid = "nginx";
+ chmod-socket = "770";
+ pyargv = "${pkgs.oncall}/share/configs/config.yaml";
+ buffer-size = 32768;
+ };
+ };
+ };
+ };
+
+ services.nginx = {
+ enable = lib.mkDefault true;
+ virtualHosts."${cfg.settings.oncall_host}".locations = {
+ "/".extraConfig = "uwsgi_pass unix://${config.services.uwsgi.runDir}/oncall.sock;";
+ };
+ };
+
+ services.mysql = lib.mkIf cfg.database.createLocally {
+ enable = true;
+ package = lib.mkDefault pkgs.mariadb;
+ ensureDatabases = [ cfg.settings.db.conn.kwargs.database ];
+ ensureUsers = [
+ {
+ name = cfg.settings.db.conn.kwargs.user;
+ ensurePermissions = {
+ "${cfg.settings.db.conn.kwargs.database}.*" = "ALL PRIVILEGES";
+ };
+ }
+ ];
+ };
+
+ users.users.oncall = {
+ group = "nginx";
+ isSystemUser = true;
+ };
+
+ systemd = {
+ services = {
+ uwsgi.serviceConfig.StateDirectory = "oncall";
+ oncall-setup-database = lib.mkIf cfg.database.createLocally {
+ description = "Set up Oncall database";
+ serviceConfig = {
+ Type = "oneshot";
+ RemainAfterExit = true;
+ };
+ requiredBy = [ "uwsgi.service" ];
+ after = [ "mysql.service" ];
+ script =
+ let
+ mysql = "${lib.getExe' config.services.mysql.package "mysql"}";
+ in
+ ''
+ if [ ! -f /var/lib/oncall/.dbexists ]; then
+ # Load database schema provided with package
+ ${mysql} ${cfg.settings.db.conn.kwargs.database} < ${cfg.package}/share/db/schema.v0.sql
+ ${mysql} ${cfg.settings.db.conn.kwargs.database} < ${cfg.package}/share/db/schema-update.v0-1602184489.sql
+ touch /var/lib/oncall/.dbexists
+ fi
+ '';
+ };
+ };
+ };
+
+ };
+
+ meta.maintainers = with lib.maintainers; [ onny ];
+
+}
diff --git a/nixos/modules/services/web-apps/onlyoffice.nix b/nixos/modules/services/web-apps/onlyoffice.nix
index 5d2e03147a68..48e51b13bb39 100644
--- a/nixos/modules/services/web-apps/onlyoffice.nix
+++ b/nixos/modules/services/web-apps/onlyoffice.nix
@@ -114,7 +114,7 @@ in
proxy_pass http://onlyoffice-docservice/$2$3;
'';
# /etc/nginx/includes/ds-docservice.conf
- #disable caching for api.js
+ # disable caching for api.js
"~ ^(\\/[\\d]+\\.[\\d]+\\.[\\d]+[\\.|-][\\w]+)?\\/(web-apps\\/apps\\/api\\/documents\\/api\\.js)$".extraConfig =
''
expires -1;
@@ -124,26 +124,23 @@ in
"~ ^(\\/[\\d]+\\.[\\d]+\\.[\\d]+[\\.|-][\\w]+)?\\/(document_editor_service_worker\\.js)$".extraConfig =
''
expires 365d;
- # gzip_static on;
- alias ${cfg.package}/var/www/onlyoffice/documentserver/sdkjs/common/serviceworker/$2;
+ alias ${cfg.package}/var/www/onlyoffice/documentserver/sdkjs/common/serviceworker/$2;
'';
- #suppress logging the unsupported locale error in web-apps
+ # suppress logging the unsupported locale error in web-apps
"~ ^(\\/[\\d]+\\.[\\d]+\\.[\\d]+[\\.|-][\\w]+)?\\/(web-apps)(\\/.*\\.json)$".extraConfig = ''
expires 365d;
error_log /dev/null crit;
alias ${cfg.package}/var/www/onlyoffice/documentserver/$2$3;
'';
- #suppress logging the unsupported locale error in plugins
+ # suppress logging the unsupported locale error in plugins
"~ ^(\\/[\\d]+\\.[\\d]+\\.[\\d]+[\\.|-][\\w]+)?\\/(sdkjs-plugins)(\\/.*\\.json)$".extraConfig = ''
expires 365d;
error_log /dev/null crit;
- # gzip_static on;
alias ${cfg.package}/var/www/onlyoffice/documentserver/$2$3;
'';
"~ ^(\\/[\\d]+\\.[\\d]+\\.[\\d]+[\\.|-][\\w]+)?\\/(web-apps|sdkjs|sdkjs-plugins|fonts|dictionaries)(\\/.*)$".extraConfig =
''
expires 365d;
- # gzip_static on;
alias ${cfg.package}/var/www/onlyoffice/documentserver/$2$3;
'';
"~* ^(\\/cache\\/files.*)(\\/.*)".extraConfig = ''
@@ -302,9 +299,8 @@ in
' /run/onlyoffice/config/default.json | sponge /run/onlyoffice/config/default.json
chmod u+w /run/onlyoffice/config/production-linux.json
- jq '
- .FileConverter.converter.x2tPath = "${cfg.x2t}/bin/x2t"
- ' /run/onlyoffice/config/production-linux.json | sponge /run/onlyoffice/config/production-linux.json
+ jq '.FileConverter.converter.x2tPath = "${cfg.x2t}/bin/x2t"' \
+ /run/onlyoffice/config/production-linux.json | sponge /run/onlyoffice/config/production-linux.json
if psql -d onlyoffice -c "SELECT 'task_result'::regclass;" >/dev/null; then
psql -f ${cfg.package}/var/www/onlyoffice/documentserver/server/schema/postgresql/removetbl.sql
diff --git a/nixos/modules/services/web-apps/opencloud.md b/nixos/modules/services/web-apps/opencloud.md
new file mode 100644
index 000000000000..5226e7922955
--- /dev/null
+++ b/nixos/modules/services/web-apps/opencloud.md
@@ -0,0 +1,64 @@
+# OpenCloud {#module-services-opencloud}
+
+[OpenCloud](https://opencloud.eu/en) is an open-source, modern file-sync and
+sharing platform. It is a fork of oCIS, a ground-up rewrite of the well-known
+PHP-based NextCloud server.
+
+The service can be configured using a combination of [](#opt-services.opencloud.settings),
+[](#opt-services.opencloud.environment) and [](#opt-services.opencloud.environmentFile).
+
+## Basic usage {#module-services-opencloud-basic-usage}
+
+OpenCloud is configured using a combination of YAML and environment
+variables. The full documentation can be found at
+[OpenCloud Admin Docs](https://docs.opencloud.eu/docs/admin/intro).
+
+The general flow of configuring OpenCloud is:
+- configure services with `services.opencloud.settings.` when possible
+- configure global settings that affect multiple services via `services.opencloud.environment`
+- allow NixOS to provision a default `opencloud.yaml` for you, containing default credentials
+ for communication between the microservices
+- provide additional secrets via `environmentFile`, provisioned out of band
+
+Please note that current NixOS module for OpenCloud is configured to run in
+`fullstack` mode, which starts all the services for OpenCloud in a single
+instance, in so called supervised mode. This will start multiple OpenCloud
+services and listen on multiple other ports.
+
+Current known services and their ports are as below:
+
+| Service | Group | Port |
+|--------------------|---------|-------|
+| gateway | api | 9142 |
+| sharing | api | 9150 |
+| app-registry | api | 9242 |
+| ocdav | web | 45023 |
+| auth-machine | api | 9166 |
+| storage-system | api | 9215 |
+| webdav | web | 9115 |
+| webfinger | web | 46871 |
+| storage-system | web | 9216 |
+| web | web | 9100 |
+| eventhistory | api | 33177 |
+| ocs | web | 9110 |
+| storage-publiclink | api | 9178 |
+| settings | web | 9190 |
+| ocm | api | 9282 |
+| settings | api | 9191 |
+| ocm | web | 9280 |
+| app-provider | api | 9164 |
+| storage-users | api | 9157 |
+| auth-service | api | 9199 |
+| thumbnails | web | 9186 |
+| thumbnails | api | 9185 |
+| storage-shares | api | 9154 |
+| sse | sse | 46833 |
+| userlog | userlog | 45363 |
+| search | api | 9220 |
+| proxy | web | 9200 |
+| idp | web | 9130 |
+| frontend | web | 9140 |
+| groups | api | 9160 |
+| graph | graph | 9120 |
+| users | api | 9144 |
+| auth-basic | api | 9146 |
diff --git a/nixos/modules/services/web-apps/opencloud.nix b/nixos/modules/services/web-apps/opencloud.nix
new file mode 100644
index 000000000000..0eded3cc96f9
--- /dev/null
+++ b/nixos/modules/services/web-apps/opencloud.nix
@@ -0,0 +1,242 @@
+{
+ config,
+ lib,
+ pkgs,
+ ...
+}:
+
+let
+ inherit (lib) types;
+ cfg = config.services.opencloud;
+
+ defaultUser = "opencloud";
+ defaultGroup = defaultUser;
+
+ settingsFormat = pkgs.formats.yaml { };
+in
+{
+ options = {
+ services.opencloud = {
+ enable = lib.mkEnableOption "OpenCloud";
+
+ package = lib.mkPackageOption pkgs "opencloud" { };
+ webPackage = lib.mkPackageOption pkgs [ "opencloud" "web" ] { };
+ idpWebPackage = lib.mkPackageOption pkgs [ "opencloud" "idp-web" ] { };
+
+ user = lib.mkOption {
+ type = types.str;
+ default = defaultUser;
+ example = "mycloud";
+ description = ''
+ The user to run OpenCloud as.
+ By default, a user named `${defaultUser}` will be created whose home
+ directory is [](#opt-services.opencloud.stateDir).
+ '';
+ };
+
+ group = lib.mkOption {
+ type = types.str;
+ default = defaultGroup;
+ example = "mycloud";
+ description = ''
+ The group to run OpenCloud under.
+ By default, a group named `${defaultGroup}` will be created.
+ '';
+ };
+
+ address = lib.mkOption {
+ type = types.str;
+ default = "127.0.0.1";
+ description = "Web server bind address.";
+ };
+
+ port = lib.mkOption {
+ type = types.port;
+ default = 9200;
+ description = "Web server port.";
+ };
+
+ url = lib.mkOption {
+ type = types.str;
+ default = "https://localhost:9200";
+ example = "https://cloud.example.com";
+ description = "Web interface root public URL, including scheme and port (if non-default).";
+ };
+
+ stateDir = lib.mkOption {
+ default = "/var/lib/opencloud";
+ type = types.str;
+ description = "OpenCloud data directory.";
+ };
+
+ settings = lib.mkOption {
+ type = lib.types.attrsOf settingsFormat.type;
+ default = { };
+ description = ''
+ Additional YAML configuration for OpenCloud services.
+
+ Every item in this attrset will be mapped to a .yaml file in /etc/opencloud.
+
+ The possible config options are currently not well documented, see source code:
+ https://github.com/opencloud-eu/opencloud/blob/main/pkg/config/config.go
+ '';
+ };
+
+ environmentFile = lib.mkOption {
+ type = types.nullOr types.path;
+ default = null;
+ example = "/run/keys/opencloud.env";
+ description = ''
+ An environment file as defined in {manpage}`systemd.exec(5)`.
+
+ Use this to inject secrets, e.g. database or auth credentials out of band.
+
+ Configuration provided here will override `settings` and `environment`.
+ '';
+ };
+
+ environment = lib.mkOption {
+ type = types.attrsOf types.str;
+ default = {
+ OC_INSECURE = "true";
+ };
+ description = ''
+ Extra environment variables to set for the service.
+
+ Use this to set configuration that may affect multiple microservices.
+
+ Configuration provided here will override `settings`.
+ '';
+ example = {
+ OC_INSECURE = "false";
+ OC_LOG_LEVEL = "error";
+ };
+ };
+ };
+ };
+
+ config = lib.mkIf cfg.enable {
+ users.users.${defaultUser} = lib.mkIf (cfg.user == defaultUser) {
+ group = cfg.group;
+ home = cfg.stateDir;
+ isSystemUser = true;
+ createHome = true;
+ description = "OpenCloud daemon user";
+ };
+
+ users.groups = lib.mkIf (cfg.group == defaultGroup) { ${defaultGroup} = { }; };
+
+ systemd = {
+ services =
+ let
+ environment = {
+ PROXY_HTTP_ADDR = "${cfg.address}:${toString cfg.port}";
+ OC_URL = cfg.url;
+ OC_BASE_DATA_PATH = cfg.stateDir;
+ WEB_ASSET_CORE_PATH = "${cfg.webPackage}";
+ IDP_ASSET_PATH = "${cfg.idpWebPackage}/assets";
+ OC_CONFIG_DIR = "/etc/opencloud";
+ } // cfg.environment;
+ commonServiceConfig = {
+ EnvironmentFile = lib.optional (cfg.environmentFile != null) cfg.environmentFile;
+ MemoryDenyWriteExecute = true;
+ NoNewPrivileges = true;
+ PrivateTmp = true;
+ PrivateDevices = true;
+ ProtectSystem = "strict";
+ ProtectHome = true;
+ ProtectControlGroups = true;
+ ProtectKernelModules = true;
+ ProtectKernelTunables = true;
+ ProtectKernelLogs = true;
+ RestrictAddressFamilies = [
+ "AF_UNIX"
+ "AF_INET"
+ "AF_INET6"
+ ];
+ RestrictNamespaces = true;
+ RestrictRealtime = true;
+ RestrictSUIDSGID = true;
+ LockPersonality = true;
+ SystemCallArchitectures = "native";
+ };
+ in
+ {
+ opencloud-init-config = lib.mkIf (cfg.settings.opencloud or { } == { }) {
+ description = "Provision initial OpenCloud config";
+ before = [ "opencloud.service" ];
+ wantedBy = [ "multi-user.target" ];
+
+ inherit environment;
+
+ serviceConfig = {
+ Type = "oneshot";
+ ReadWritePaths = [ "/etc/opencloud" ];
+ } // commonServiceConfig;
+
+ path = [ cfg.package ];
+ script = ''
+ set -x
+ config="''${OC_CONFIG_DIR}/opencloud.yaml"
+ if [ ! -e "$config" ]; then
+ echo "Provisioning initial OpenCloud config..."
+ opencloud init --insecure "''${OC_INSECURE:false}" --config-path "''${OC_CONFIG_DIR}"
+ chown ${cfg.user}:${cfg.group} "$config"
+ fi
+ '';
+ };
+
+ opencloud = {
+ description = "OpenCloud - a secure and private way to store, access, and share your files";
+ after = [ "network.target" ];
+ wantedBy = [ "multi-user.target" ];
+
+ inherit environment;
+
+ serviceConfig = {
+ Type = "simple";
+ ExecStart = "${lib.getExe cfg.package} server";
+ WorkingDirectory = cfg.stateDir;
+ User = cfg.user;
+ Group = cfg.group;
+ Restart = "always";
+ ReadWritePaths = [ cfg.stateDir ];
+ } // commonServiceConfig;
+
+ restartTriggers = lib.mapAttrsToList (
+ name: _: config.environment.etc."opencloud/${name}.yaml".source
+ ) cfg.settings;
+ };
+ };
+ };
+
+ systemd.tmpfiles.settings."10-opencloud" = {
+ ${cfg.stateDir}.d = {
+ inherit (cfg) user group;
+ mode = "0750";
+ };
+ "${cfg.stateDir}/idm".d = {
+ inherit (cfg) user group;
+ mode = "0750";
+ };
+ };
+
+ environment.etc =
+ (lib.mapAttrs' (name: value: {
+ name = "opencloud/${name}.yaml";
+ value.source = settingsFormat.generate "${name}.yaml" value;
+ }) cfg.settings)
+ // {
+ # ensure /etc/opencloud gets created, so we can provision the config
+ "opencloud/.keep".text = "";
+ };
+ };
+
+ meta = {
+ doc = ./opencloud.md;
+ maintainers = with lib.maintainers; [
+ christoph-heiss
+ k900
+ ];
+ };
+}
diff --git a/nixos/modules/services/web-apps/part-db.nix b/nixos/modules/services/web-apps/part-db.nix
index c165969aa620..85adf2d7b7e5 100644
--- a/nixos/modules/services/web-apps/part-db.nix
+++ b/nixos/modules/services/web-apps/part-db.nix
@@ -27,7 +27,7 @@ in
phpPackage = mkPackageOption pkgs "php" { } // {
apply =
pkg:
- pkg.override {
+ pkg.buildEnv {
extraConfig = ''
memory_limit = 256M;
'';
diff --git a/nixos/modules/services/web-apps/peertube.nix b/nixos/modules/services/web-apps/peertube.nix
index 2fe726e61525..c4b0186a77b4 100644
--- a/nixos/modules/services/web-apps/peertube.nix
+++ b/nixos/modules/services/web-apps/peertube.nix
@@ -489,7 +489,7 @@ in
environment = env;
path = with pkgs; [
- nodejs_18
+ nodejs_20
yarn
ffmpeg-headless
openssl
@@ -945,7 +945,7 @@ in
})
(lib.attrsets.setAttrByPath
[ cfg.user "packages" ]
- [ peertubeEnv pkgs.nodejs_18 pkgs.yarn pkgs.ffmpeg-headless ]
+ [ peertubeEnv pkgs.nodejs_20 pkgs.yarn pkgs.ffmpeg-headless ]
)
(lib.mkIf cfg.redis.enableUnixSocket {
${config.services.peertube.user}.extraGroups = [ "redis-peertube" ];
diff --git a/nixos/modules/services/web-apps/readeck.nix b/nixos/modules/services/web-apps/readeck.nix
index bd529fa488ef..f0e7252f40bd 100644
--- a/nixos/modules/services/web-apps/readeck.nix
+++ b/nixos/modules/services/web-apps/readeck.nix
@@ -68,7 +68,6 @@ in
ExecStart = "${lib.getExe cfg.package} serve -config ${configFile}";
ProtectSystem = "full";
SystemCallArchitectures = "native";
- MemoryDenyWriteExecute = true;
NoNewPrivileges = true;
PrivateTmp = true;
PrivateDevices = true;
@@ -89,7 +88,6 @@ in
ProtectKernelTunables = true;
LockPersonality = true;
Restart = "on-failure";
-
};
};
};
diff --git a/nixos/modules/services/web-apps/reposilite.nix b/nixos/modules/services/web-apps/reposilite.nix
new file mode 100644
index 000000000000..ac9c9937bcfa
--- /dev/null
+++ b/nixos/modules/services/web-apps/reposilite.nix
@@ -0,0 +1,439 @@
+{
+ lib,
+ config,
+ pkgs,
+ ...
+}:
+let
+ cfg = config.services.reposilite;
+ format = pkgs.formats.cdn { };
+ configFile = format.generate "reposilite.cdn" cfg.settings;
+
+ useEmbeddedDb = cfg.database.type == "sqlite" || cfg.database.type == "h2";
+ useMySQL = cfg.database.type == "mariadb" || cfg.database.type == "mysql";
+ usePostgres = cfg.database.type == "postgresql";
+
+ # db password is appended at runtime by the service script (if needed)
+ dbString =
+ if useEmbeddedDb then
+ "${cfg.database.type} ${cfg.database.path}"
+ else
+ "${cfg.database.type} ${cfg.database.host}:${builtins.toString cfg.database.port} ${cfg.database.dbname} ${cfg.database.user} $(<${cfg.database.passwordFile})";
+
+ certDir = config.security.acme.certs.${cfg.useACMEHost}.directory;
+
+ databaseModule = {
+ options = {
+ type = lib.mkOption {
+ type = lib.types.enum [
+ "h2"
+ "mariadb"
+ "mysql"
+ "postgresql"
+ "sqlite"
+ ];
+ description = ''
+ Database engine to use.
+ '';
+ default = "sqlite";
+ };
+
+ path = lib.mkOption {
+ type = lib.types.str;
+ description = ''
+ Path to the embedded database file. Set to `--temporary` to use an in-memory database.
+ '';
+ default = "reposilite.db";
+ };
+
+ host = lib.mkOption {
+ type = lib.types.str;
+ description = ''
+ Database host address.
+ '';
+ default = "127.0.0.1";
+ };
+
+ port = lib.mkOption {
+ type = lib.types.port;
+ description = ''
+ Database TCP port.
+ '';
+ defaultText = lib.literalExpression ''
+ if type == "postgresql" then 5432 else 3306
+ '';
+ default = if usePostgres then config.services.postgresql.settings.port else 3306;
+ };
+
+ dbname = lib.mkOption {
+ type = lib.types.str;
+ description = ''
+ Database name.
+ '';
+ default = "reposilite";
+ };
+
+ user = lib.mkOption {
+ type = lib.types.str;
+ description = ''
+ Database user.
+ '';
+ default = "reposilite";
+ };
+
+ passwordFile = lib.mkOption {
+ type = lib.types.nullOr lib.types.path;
+ description = ''
+ Path to the file containing the password for the database connection.
+ This file must be readable by {option}`services.reposilite.user`.
+ '';
+ default = null;
+ };
+ };
+ };
+
+ settingsModule = {
+ freeformType = format.type;
+ options = {
+ hostname = lib.mkOption {
+ type = lib.types.str;
+ description = ''
+ The hostname to bind to. Set to `0.0.0.0` to accept connections from everywhere, or `127.0.0.1` to restrict to localhost."
+ '';
+ default = "0.0.0.0";
+ example = "127.0.0.1";
+ };
+
+ port = lib.mkOption {
+ type = lib.types.port;
+ description = ''
+ The TCP port to bind to.
+ '';
+ default = 3000;
+ };
+
+ database = lib.mkOption {
+ type = lib.types.nullOr lib.types.str;
+ description = ''
+ Database connection string. Please use {option}`services.reposilite.database` instead.
+ See https://reposilite.com/guide/general#local-configuration for valid values.
+ '';
+ default = null;
+ };
+
+ sslEnabled = lib.mkOption {
+ type = lib.types.bool;
+ description = ''
+ Whether to listen for encrypted connections on {option}`settings.sslPort`.
+ '';
+ default = false;
+ };
+
+ sslPort = lib.mkOption {
+ type = lib.types.port; # cant be null
+ description = "SSL port to bind to. SSL needs to be enabled explicitly via {option}`settings.enableSsl`.";
+ default = 443;
+ };
+
+ keyPath = lib.mkOption {
+ type = lib.types.nullOr lib.types.str;
+ description = ''
+ Path to the .jsk KeyStore or paths to the PKCS#8 certificate and private key, separated by a space (see example).
+ You can use `''${WORKING_DIRECTORY}` to refer to paths relative to Reposilite's working directory.
+ If you are using a Java KeyStore, don't forget to specify the password via the {var}`REPOSILITE_LOCAL_KEYPASSWORD` environment variable.
+ See https://reposilite.com/guide/ssl for more information on how to set SSL up.
+ '';
+ default = null;
+ example = "\${WORKING_DIRECTORY}/cert.pem \${WORKING_DIRECTORY}/key.pem";
+ };
+
+ keyPassword = lib.mkOption {
+ type = lib.types.nullOr lib.types.str;
+ description = ''
+ Plaintext password used to unlock the Java KeyStore set in {option}`services.reposilite.settings.keyPath`.
+ WARNING: this option is insecure and should not be used to store the password.
+ Consider using {option}`services.reposilite.keyPasswordFile` instead.
+ '';
+ default = null;
+ };
+
+ enforceSsl = lib.mkOption {
+ type = lib.types.bool;
+ description = ''
+ Whether to redirect all traffic to SSL.
+ '';
+ default = false;
+ };
+
+ webThreadPool = lib.mkOption {
+ type = lib.types.ints.between 5 65535;
+ description = ''
+ Maximum amount of threads used by the core thread pool. (min: 5)
+ The web thread pool handles the first few steps of incoming HTTP connections, tasks are redirected as soon as possible to the IO thread pool.
+ '';
+ default = 16;
+ };
+
+ ioThreadPool = lib.mkOption {
+ type = lib.types.ints.between 2 65535;
+ description = ''
+ The IO thread pool handles all tasks that may benefit from non-blocking IO. (min: 2)
+ Because most tasks are redirected to IO thread pool, it might be a good idea to keep it at least equal to web thread pool.
+ '';
+ default = 8;
+ };
+
+ databaseThreadPool = lib.mkOption {
+ type = lib.types.ints.positive;
+ description = ''
+ Maximum amount of concurrent connections to the database. (one per thread)
+ Embedded databases (sqlite, h2) do not support truly concurrent connections, so the value will always be `1` if they are used.
+ '';
+ default = 1;
+ };
+
+ compressionStrategy = lib.mkOption {
+ type = lib.types.enum [
+ "none"
+ "gzip"
+ ];
+ description = ''
+ Compression algorithm used by this instance of Reposilite.
+ `none` reduces usage of CPU & memory, but requires transfering more data.
+ '';
+ default = "none";
+ };
+
+ idleTimeout = lib.mkOption {
+ type = lib.types.ints.unsigned;
+ description = ''
+ Default idle timeout used by Jetty.
+ '';
+ default = 30000;
+ };
+
+ bypassExternalCache = lib.mkOption {
+ type = lib.types.bool;
+ description = ''
+ Add cache bypass headers to responses from /api/* to avoid issues with proxies such as Cloudflare.
+ '';
+ default = true;
+ };
+
+ cachedLogSize = lib.mkOption {
+ type = lib.types.ints.unsigned;
+ description = ''
+ Amount of messages stored in the cache logger.
+ '';
+ default = 50;
+ };
+
+ defaultFrontend = lib.mkOption {
+ type = lib.types.bool;
+ description = ''
+ Whether to enable the default included frontend with a dashboard.
+ '';
+ default = true;
+ };
+
+ basePath = lib.mkOption {
+ type = lib.types.str;
+ description = ''
+ Custom base path for this Reposilite instance.
+ It is not recommended changing this, you should instead prioritize using a different subdomain.
+ '';
+ default = "/";
+ };
+
+ debugEnabled = lib.mkOption {
+ type = lib.types.bool;
+ description = ''
+ Whether to enable debug mode.
+ '';
+ default = false;
+ };
+ };
+ };
+in
+{
+ options.services.reposilite = {
+ enable = lib.mkEnableOption "Reposilite";
+ package = lib.mkPackageOption pkgs "reposilite" { } // {
+ apply =
+ pkg:
+ pkg.override (old: {
+ plugins = (old.plugins or [ ]) ++ cfg.plugins;
+ });
+ };
+
+ plugins = lib.mkOption {
+ type = lib.types.listOf lib.types.package;
+ description = ''
+ List of plugins to add to Reposilite.
+ '';
+ default = [ ];
+ example = "with reposilitePlugins; [ checksum groovy ]";
+ };
+
+ database = lib.mkOption {
+ description = "Database options.";
+ default = { };
+ type = lib.types.submodule databaseModule;
+ };
+
+ keyPasswordFile = lib.mkOption {
+ type = lib.types.nullOr lib.types.path;
+ description = ''
+ Path the the file containing the password used to unlock the Java KeyStore file specified in {option}`services.reposilite.settings.keyPath`.
+ This file must be readable my {option}`services.reposilite.user`.
+ '';
+ default = null;
+ };
+
+ useACMEHost = lib.mkOption {
+ type = lib.types.nullOr lib.types.str;
+ description = ''
+ Host of an existing Let's Encrypt certificate to use for SSL.
+ Make sure that the certificate directory is readable by the `reposilite` user or group, for example via {option}`security.acme.certs..group`.
+ *Note that this option does not create any certificates, nor it does add subdomains to existing ones – you will need to create them manually using {option}`security.acme.certs`*
+ '';
+ default = null;
+ };
+
+ settings = lib.mkOption {
+ description = "Configuration written to the reposilite.cdn file";
+ default = { };
+ type = lib.types.submodule settingsModule;
+ };
+
+ workingDirectory = lib.mkOption {
+ type = lib.types.path;
+ description = ''
+ Working directory for Reposilite.
+ '';
+ default = "/var/lib/reposilite";
+ };
+
+ extraArgs = lib.mkOption {
+ type = lib.types.listOf lib.types.str;
+ description = ''
+ Extra arguments/parameters passed to the Reposilite. Can be used for first token generation.
+ '';
+ default = [ ];
+ example = lib.literalExpression ''[ "--token" "name:tempsecrettoken" ]'';
+ };
+
+ user = lib.mkOption {
+ type = lib.types.str;
+ description = ''
+ The user to run Reposilite under.
+ '';
+ default = "reposilite";
+ };
+
+ group = lib.mkOption {
+ type = lib.types.str;
+ description = ''
+ The group to run Reposilite under.
+ '';
+ default = "reposilite";
+ };
+
+ openFirewall = lib.mkOption {
+ type = lib.types.bool;
+ description = ''
+ Whether to open the firewall ports for Reposilite. If SSL is enabled, its port will be opened too.
+ '';
+ default = false;
+ };
+ };
+
+ config = lib.mkIf cfg.enable {
+ assertions = [
+ {
+ assertion = cfg.settings.sslEnabled -> cfg.settings.keyPath != null;
+ message = ''
+ Reposilite was configured to enable SSL, but no valid paths to certificate files were provided via `settings.keyPath`.
+ Read more about SSL certificates here: https://reposilite.com/guide/ssl
+ '';
+ }
+ {
+ assertion = cfg.settings.enforceSsl -> cfg.settings.sslEnabled;
+ message = "You cannot enforce SSL if SSL is not enabled.";
+ }
+ {
+ assertion = !useEmbeddedDb -> cfg.database.passwordFile != null;
+ message = "You need to set `services.reposilite.database.passwordFile` when using MySQL or Postgres.";
+ }
+ ];
+
+ services.reposilite.settings.keyPath = lib.mkIf (
+ cfg.useACMEHost != null
+ ) "${certDir}/fullchain.pem ${certDir}/key.pem";
+
+ environment.systemPackages = [ cfg.package ];
+
+ users = {
+ groups.${cfg.group} = lib.mkIf (cfg.group == "reposilite") { };
+ users.${cfg.user} = lib.mkIf (cfg.user == "reposilite") {
+ isSystemUser = true;
+ group = cfg.group;
+ };
+ };
+
+ networking.firewall = lib.mkIf cfg.openFirewall (
+ lib.mkMerge [
+ {
+ allowedTCPPorts = [ cfg.settings.port ];
+ }
+ (lib.mkIf cfg.settings.sslEnabled {
+ allowedTCPPorts = [ cfg.settings.sslPort ];
+ })
+ ]
+ );
+
+ systemd.services.reposilite = {
+ enable = true;
+ wantedBy = [ "multi-user.target" ];
+ after =
+ [ "network.target" ]
+ ++ (lib.optional useMySQL "mysql.service")
+ ++ (lib.optional usePostgres "postgresql.service");
+
+ script =
+ lib.optionalString (cfg.keyPasswordFile != null && cfg.settings.keyPassword == null) ''
+ export REPOSILITE_LOCAL_KEYPASSWORD="$(<${cfg.keyPasswordFile})"
+ ''
+ + ''
+ export REPOSILITE_LOCAL_DATABASE="${dbString}"
+
+ ${lib.getExe cfg.package} --local-configuration ${configFile} --local-configuration-mode none --working-directory ${cfg.workingDirectory} ${lib.escapeShellArgs cfg.extraArgs}
+ '';
+
+ serviceConfig = lib.mkMerge [
+ (lib.mkIf (builtins.dirOf cfg.workingDirectory == "/var/lib") {
+ StateDirectory = builtins.baseNameOf cfg.workingDirectory;
+ StateDirectoryMode = "700";
+ })
+ {
+ Type = "exec";
+ Restart = "on-failure";
+
+ User = cfg.user;
+ Group = cfg.group;
+ WorkingDirectory = cfg.workingDirectory;
+
+ # TODO better hardening
+ LimitNOFILE = "1048576";
+ PrivateTmp = true;
+ PrivateDevices = true;
+ ProtectHome = true;
+ ProtectSystem = "strict";
+ AmbientCapabilities = "CAP_NET_BIND_SERVICE";
+ }
+ ];
+ };
+ };
+
+ meta.maintainers = [ lib.maintainers.uku3lig ];
+}
diff --git a/nixos/modules/services/web-apps/snipe-it.nix b/nixos/modules/services/web-apps/snipe-it.nix
index 0b780174068d..66e1e1266881 100644
--- a/nixos/modules/services/web-apps/snipe-it.nix
+++ b/nixos/modules/services/web-apps/snipe-it.nix
@@ -509,8 +509,9 @@ in
sed -i 's/APP_KEY=/APP_KEY=base64:/' "${cfg.dataDir}/.env"
fi
- # purge cache
- rm "${cfg.dataDir}"/bootstrap/cache/*.php || true
+ # pruge and rebuild caches
+ ${lib.getExe artisan} optimize:clear
+ ${lib.getExe artisan} optimize
# migrate db
${lib.getExe artisan} migrate --force
diff --git a/nixos/modules/services/web-apps/stash.nix b/nixos/modules/services/web-apps/stash.nix
index 60c8ad8a8eb4..47f66edb7ddb 100644
--- a/nixos/modules/services/web-apps/stash.nix
+++ b/nixos/modules/services/web-apps/stash.nix
@@ -225,7 +225,7 @@ let
};
gallery_cover_regex = mkOption {
type = types.str;
- default = "(poster|cover|folder|board)\.[^\.]+$";
+ default = "(poster|cover|folder|board)\\.[^.]+$";
description = "Regex used to identify images as gallery covers";
};
no_proxy = mkOption {
diff --git a/nixos/modules/services/web-apps/trilium.nix b/nixos/modules/services/web-apps/trilium.nix
index 2b0af61aa043..83f79589a947 100644
--- a/nixos/modules/services/web-apps/trilium.nix
+++ b/nixos/modules/services/web-apps/trilium.nix
@@ -108,11 +108,11 @@ in
};
};
+ meta.maintainers = with lib.maintainers; [ fliegendewurst ];
+
config = lib.mkIf cfg.enable (
lib.mkMerge [
{
- meta.maintainers = with lib.maintainers; [ fliegendewurst ];
-
users.groups.trilium = { };
users.users.trilium = {
description = "Trilium User";
diff --git a/nixos/modules/services/web-apps/whoami.nix b/nixos/modules/services/web-apps/whoami.nix
new file mode 100644
index 000000000000..a1b50049d4c6
--- /dev/null
+++ b/nixos/modules/services/web-apps/whoami.nix
@@ -0,0 +1,90 @@
+{
+ config,
+ lib,
+ pkgs,
+ ...
+}:
+
+let
+ cfg = config.services.whoami;
+in
+
+{
+ meta.maintainers = with lib.maintainers; [ defelo ];
+
+ options.services.whoami = {
+ enable = lib.mkEnableOption "whoami";
+
+ package = lib.mkPackageOption pkgs "whoami" { };
+
+ port = lib.mkOption {
+ type = lib.types.port;
+ description = "The port whoami should listen on.";
+ default = 8000;
+ };
+
+ extraArgs = lib.mkOption {
+ type = lib.types.listOf lib.types.str;
+ description = "Extra command line arguments to pass to whoami. See for details.";
+ default = [ ];
+ };
+ };
+
+ config = lib.mkIf cfg.enable {
+ systemd.services.whoami = {
+ wantedBy = [ "multi-user.target" ];
+
+ wants = [ "network-online.target" ];
+ after = [ "network-online.target" ];
+
+ serviceConfig = {
+ User = "whoami";
+ Group = "whoami";
+ DynamicUser = true;
+ ExecStart = lib.escapeShellArgs (
+ [
+ (lib.getExe cfg.package)
+ "-port"
+ cfg.port
+ ]
+ ++ cfg.extraArgs
+ );
+
+ # Hardening
+ AmbientCapabilities = "";
+ CapabilityBoundingSet = [ "" ];
+ DevicePolicy = "closed";
+ LockPersonality = true;
+ MemoryDenyWriteExecute = true;
+ NoNewPrivileges = true;
+ PrivateDevices = true;
+ PrivateTmp = true;
+ PrivateUsers = true;
+ ProcSubset = "pid";
+ ProtectClock = true;
+ ProtectControlGroups = true;
+ ProtectHome = true;
+ ProtectHostname = true;
+ ProtectKernelLogs = true;
+ ProtectKernelModules = true;
+ ProtectKernelTunables = true;
+ ProtectProc = "invisible";
+ ProtectSystem = "strict";
+ RemoveIPC = true;
+ RestrictAddressFamilies = [ "AF_INET AF_INET6" ];
+ RestrictNamespaces = true;
+ RestrictRealtime = true;
+ RestrictSUIDSGID = true;
+ SocketBindAllow = "tcp:${toString cfg.port}";
+ SocketBindDeny = "any";
+ SystemCallArchitectures = "native";
+ SystemCallFilter = [
+ "@system-service"
+ "~@privileged"
+ "~@resources"
+ ];
+ UMask = "0077";
+ };
+ };
+ };
+}
diff --git a/nixos/modules/services/web-apps/wiki-js.nix b/nixos/modules/services/web-apps/wiki-js.nix
index 4c742c26afad..f313804a6d28 100644
--- a/nixos/modules/services/web-apps/wiki-js.nix
+++ b/nixos/modules/services/web-apps/wiki-js.nix
@@ -151,7 +151,7 @@ in
WorkingDirectory = "/var/lib/${cfg.stateDirectoryName}";
DynamicUser = true;
PrivateTmp = true;
- ExecStart = "${pkgs.nodejs_18}/bin/node ${pkgs.wiki-js}/server";
+ ExecStart = "${pkgs.nodejs_20}/bin/node ${pkgs.wiki-js}/server";
};
};
};
diff --git a/nixos/modules/services/web-servers/nginx/default.nix b/nixos/modules/services/web-servers/nginx/default.nix
index 2ff0c3494e19..bf658564515c 100644
--- a/nixos/modules/services/web-servers/nginx/default.nix
+++ b/nixos/modules/services/web-servers/nginx/default.nix
@@ -339,7 +339,7 @@ let
map (
listen:
{
- port = cfg.defaultSSLListenPort;
+ port = if (hasPrefix "unix:" listen.addr) then null else cfg.defaultSSLListenPort;
ssl = true;
}
// listen
@@ -351,7 +351,7 @@ let
map (
listen:
{
- port = cfg.defaultHTTPListenPort;
+ port = if (hasPrefix "unix:" listen.addr) then null else cfg.defaultHTTPListenPort;
ssl = false;
}
// listen
diff --git a/nixos/modules/services/x11/desktop-managers/budgie.nix b/nixos/modules/services/x11/desktop-managers/budgie.nix
index e278f2f2f87a..6ed2f0d20aae 100644
--- a/nixos/modules/services/x11/desktop-managers/budgie.nix
+++ b/nixos/modules/services/x11/desktop-managers/budgie.nix
@@ -199,13 +199,6 @@ in
monospace = mkDefault [ "Hack" ];
};
- # Qt application style.
- qt = {
- enable = mkDefault true;
- style = mkDefault "gtk2";
- platformTheme = mkDefault "gtk2";
- };
-
environment.pathsToLink = [
"/share" # TODO: https://github.com/NixOS/nixpkgs/issues/47173
];
@@ -252,8 +245,7 @@ in
services.system-config-printer.enable = config.services.printing.enable;
# For BCC's Sharing panel.
- services.dleyna-renderer.enable = mkDefault true;
- services.dleyna-server.enable = mkDefault true;
+ services.dleyna.enable = mkDefault true;
services.gnome.gnome-user-share.enable = mkDefault true;
services.gnome.rygel.enable = mkDefault true;
diff --git a/nixos/modules/services/x11/desktop-managers/gnome.md b/nixos/modules/services/x11/desktop-managers/gnome.md
index f959c0912652..7f7801305e23 100644
--- a/nixos/modules/services/x11/desktop-managers/gnome.md
+++ b/nixos/modules/services/x11/desktop-managers/gnome.md
@@ -19,7 +19,7 @@ To enable the GNOME desktop use:
While it is not strictly necessary to use GDM as the display manager with GNOME, it is recommended, as some features such as screen lock [might not work](#sec-gnome-faq-can-i-use-lightdm-with-gnome) without it.
:::
-The default applications used in NixOS are very minimal, inspired by the defaults used in [gnome-build-meta](https://gitlab.gnome.org/GNOME/gnome-build-meta/blob/40.0/elements/core/meta-gnome-core-utilities.bst).
+The default applications used in NixOS are very minimal, inspired by the defaults used in [gnome-build-meta](https://gitlab.gnome.org/GNOME/gnome-build-meta/blob/48.0/elements/core/meta-gnome-core-apps.bst).
### GNOME without the apps {#sec-gnome-without-the-apps}
@@ -27,7 +27,7 @@ If you’d like to only use the GNOME desktop and not the apps, you can disable
```nix
{
- services.gnome.core-utilities.enable = false;
+ services.gnome.core-apps.enable = false;
}
```
diff --git a/nixos/modules/services/x11/desktop-managers/gnome.nix b/nixos/modules/services/x11/desktop-managers/gnome.nix
index c3a3465f3a45..1f980444e644 100644
--- a/nixos/modules/services/x11/desktop-managers/gnome.nix
+++ b/nixos/modules/services/x11/desktop-managers/gnome.nix
@@ -84,12 +84,19 @@ in
maintainers = lib.teams.gnome.members;
};
+ imports = [
+ (lib.mkRenamedOptionModule
+ [ "services" "gnome" "core-utilities" "enable" ]
+ [ "services" "gnome" "core-apps" "enable" ]
+ )
+ ];
+
options = {
services.gnome = {
core-os-services.enable = mkEnableOption "essential services for GNOME3";
core-shell.enable = mkEnableOption "GNOME Shell services";
- core-utilities.enable = mkEnableOption "GNOME core utilities";
+ core-apps.enable = mkEnableOption "GNOME core apps";
core-developer-tools.enable = mkEnableOption "GNOME core developer tools";
games.enable = mkEnableOption "GNOME games";
};
@@ -213,7 +220,7 @@ in
services.gnome.core-os-services.enable = true;
services.gnome.core-shell.enable = true;
- services.gnome.core-utilities.enable = mkDefault true;
+ services.gnome.core-apps.enable = mkDefault true;
services.displayManager.sessionPackages = [ pkgs.gnome-session.sessions ];
@@ -281,9 +288,9 @@ in
hardware.bluetooth.enable = mkDefault true;
programs.dconf.enable = true;
security.polkit.enable = true;
+ security.rtkit.enable = mkDefault true;
services.accounts-daemon.enable = true;
- services.dleyna-renderer.enable = mkDefault true;
- services.dleyna-server.enable = mkDefault true;
+ services.dleyna.enable = mkDefault true;
services.power-profiles-daemon.enable = mkDefault true;
services.gnome.at-spi2-core.enable = true;
services.gnome.evolution-data-server.enable = true;
@@ -326,17 +333,9 @@ in
})
(lib.mkIf serviceCfg.core-shell.enable {
- services.xserver.desktopManager.gnome.sessionPath =
- let
- mandatoryPackages = [
- pkgs.gnome-shell
- ];
- optionalPackages = [
- pkgs.gnome-shell-extensions
- ];
- in
- mandatoryPackages
- ++ utils.removePackagesByName optionalPackages config.environment.gnome.excludePackages;
+ services.xserver.desktopManager.gnome.sessionPath = [
+ pkgs.gnome-shell
+ ];
services.colord.enable = mkDefault true;
services.gnome.glib-networking.enable = true;
@@ -380,14 +379,11 @@ in
services.orca.enable = notExcluded pkgs.orca;
- fonts.packages = with pkgs; [
- cantarell-fonts
- dejavu_fonts
- source-code-pro # Default monospace font in 3.32
- source-sans
- ];
+ fonts.packages = utils.removePackagesByName [
+ pkgs.adwaita-fonts
+ ] config.environment.gnome.excludePackages;
- # Adapt from https://gitlab.gnome.org/GNOME/gnome-build-meta/blob/gnome-3-38/elements/core/meta-gnome-core-shell.bst
+ # Adapt from https://gitlab.gnome.org/GNOME/gnome-build-meta/blob/gnome-48/elements/core/meta-gnome-core-shell.bst
environment.systemPackages =
let
mandatoryPackages = [
@@ -400,7 +396,6 @@ in
pkgs.gnome-bluetooth
pkgs.gnome-color-manager
pkgs.gnome-control-center
- pkgs.gnome-shell-extensions
pkgs.gnome-tour # GNOME Shell detects the .desktop file on first log-in.
pkgs.gnome-user-docs
pkgs.glib # for gsettings program
@@ -414,11 +409,12 @@ in
++ utils.removePackagesByName optionalPackages config.environment.gnome.excludePackages;
})
- # Adapt from https://gitlab.gnome.org/GNOME/gnome-build-meta/-/blob/gnome-45/elements/core/meta-gnome-core-utilities.bst
- (lib.mkIf serviceCfg.core-utilities.enable {
+ # Adapt from https://gitlab.gnome.org/GNOME/gnome-build-meta/-/blob/gnome-48/elements/core/meta-gnome-core-apps.bst
+ (lib.mkIf serviceCfg.core-apps.enable {
environment.systemPackages = utils.removePackagesByName (
[
pkgs.baobab
+ pkgs.decibels
pkgs.epiphany
pkgs.gnome-text-editor
pkgs.gnome-calculator
@@ -500,17 +496,19 @@ in
] config.environment.gnome.excludePackages;
})
- # Adapt from https://gitlab.gnome.org/GNOME/gnome-build-meta/-/blob/3.38.0/elements/core/meta-gnome-core-developer-tools.bst
+ # Adapt from https://gitlab.gnome.org/GNOME/gnome-build-meta/-/blob/gnome-48/elements/core/meta-gnome-core-developer-tools.bst
(lib.mkIf serviceCfg.core-developer-tools.enable {
environment.systemPackages = utils.removePackagesByName [
pkgs.dconf-editor
pkgs.devhelp
+ pkgs.d-spy
pkgs.gnome-builder
# boxes would make sense in this option, however
# it doesn't function well enough to be included
# in default configurations.
# https://github.com/NixOS/nixpkgs/issues/60908
# pkgs.gnome-boxes
+ pkgs.sysprof
] config.environment.gnome.excludePackages;
services.sysprof.enable = notExcluded pkgs.sysprof;
diff --git a/nixos/modules/services/x11/display-managers/gdm.nix b/nixos/modules/services/x11/display-managers/gdm.nix
index d5bad47e52a4..5973191d6c7c 100644
--- a/nixos/modules/services/x11/display-managers/gdm.nix
+++ b/nixos/modules/services/x11/display-managers/gdm.nix
@@ -9,7 +9,7 @@ let
cfg = config.services.xserver.displayManager;
gdm = pkgs.gdm;
- pamCfg = config.security.pam.services;
+ pamLogin = config.security.pam.services.login;
settingsFormat = pkgs.formats.ini { };
configFile = settingsFormat.generate "custom.conf" cfg.gdm.settings;
@@ -206,12 +206,15 @@ in
];
# Otherwise GDM will not be able to start correctly and display Wayland sessions
- systemd.packages = with pkgs.gnome; [
+ systemd.packages = [
gdm
pkgs.gnome-session
pkgs.gnome-shell
];
- environment.systemPackages = [ pkgs.adwaita-icon-theme ];
+ environment.systemPackages = [
+ pkgs.adwaita-icon-theme
+ pkgs.gdm # For polkit rules
+ ];
# We dont use the upstream gdm service
# it has to be disabled since the gdm package has it
@@ -345,7 +348,7 @@ in
gdm-autologin.text = ''
auth requisite pam_nologin.so
auth required pam_succeed_if.so uid >= 1000 quiet
- ${lib.optionalString pamCfg.login.enableGnomeKeyring ''
+ ${lib.optionalString (pamLogin.enable && pamLogin.enableGnomeKeyring) ''
auth [success=ok default=1] ${gdm}/lib/security/pam_gdm.so
auth optional ${pkgs.gnome-keyring}/lib/security/pam_gnome_keyring.so
''}
@@ -369,7 +372,7 @@ in
auth requisite pam_faillock.so preauth
auth required ${pkgs.fprintd}/lib/security/pam_fprintd.so
auth required pam_env.so
- ${lib.optionalString pamCfg.login.enableGnomeKeyring ''
+ ${lib.optionalString (pamLogin.enable && pamLogin.enableGnomeKeyring) ''
auth [success=ok default=1] ${gdm}/lib/security/pam_gdm.so
auth optional ${pkgs.gnome-keyring}/lib/security/pam_gnome_keyring.so
''}
diff --git a/nixos/modules/services/x11/window-managers/exwm.nix b/nixos/modules/services/x11/window-managers/exwm.nix
index 301a3c85d9e1..f6a52ea808e3 100644
--- a/nixos/modules/services/x11/window-managers/exwm.nix
+++ b/nixos/modules/services/x11/window-managers/exwm.nix
@@ -13,7 +13,7 @@ let
${cfg.loadScript}
'';
packages = epkgs: cfg.extraPackages epkgs ++ [ epkgs.exwm ];
- exwm-emacs = pkgs.emacs.pkgs.withPackages packages;
+ exwm-emacs = cfg.package.pkgs.withPackages packages;
in
{
@@ -38,6 +38,10 @@ in
file.
'';
};
+ package = mkPackageOption pkgs "Emacs" {
+ default = "emacs";
+ example = [ "emacs-gtk" ];
+ };
extraPackages = mkOption {
type = types.functionTo (types.listOf types.package);
default = epkgs: [ ];
diff --git a/nixos/modules/system/activation/bootspec.nix b/nixos/modules/system/activation/bootspec.nix
index 4f534b240e67..99a859f91829 100644
--- a/nixos/modules/system/activation/bootspec.nix
+++ b/nixos/modules/system/activation/bootspec.nix
@@ -111,6 +111,8 @@ in
Enable this option if you want to ascertain that your documents are correct
'';
+ package = lib.mkPackageOption pkgs "bootspec" { };
+
extensions = lib.mkOption {
# NOTE(RaitoBezarius): this is not enough to validate: extensions."osRelease" = drv; those are picked up by cue validation.
type = lib.types.attrsOf lib.types.anything; # : { ...namespace-specific fields }
diff --git a/nixos/modules/system/activation/lib/test.sh b/nixos/modules/system/activation/lib/test.sh
index 9b146383ad4b..1f38eddfc231 100755
--- a/nixos/modules/system/activation/lib/test.sh
+++ b/nixos/modules/system/activation/lib/test.sh
@@ -26,6 +26,7 @@ onerr() {
}
trap onerr ERR
+# shellcheck source-path=SCRIPTDIR
source ./lib.sh
(warn hi, this works >/dev/null) 2>&1 | grep -E $'.*warning:.* hi, this works' >/dev/null
diff --git a/nixos/modules/system/activation/specialisation.nix b/nixos/modules/system/activation/specialisation.nix
index 1bc257248539..7acccbd724d5 100644
--- a/nixos/modules/system/activation/specialisation.nix
+++ b/nixos/modules/system/activation/specialisation.nix
@@ -10,6 +10,8 @@
let
inherit (lib)
concatStringsSep
+ escapeShellArg
+ hasInfix
mapAttrs
mapAttrsToList
mkOption
@@ -84,10 +86,18 @@ in
};
config = {
+ assertions = mapAttrsToList (name: _: {
+ assertion = !hasInfix "/" name;
+ message = ''
+ Specialisation names must not contain forward slashes.
+ Invalid specialisation name: ${name}
+ '';
+ }) config.specialisation;
+
system.systemBuilderCommands = ''
mkdir $out/specialisation
${concatStringsSep "\n" (
- mapAttrsToList (name: path: "ln -s ${path} $out/specialisation/${name}") children
+ mapAttrsToList (name: path: "ln -s ${path} $out/specialisation/${escapeShellArg name}") children
)}
'';
};
diff --git a/nixos/modules/system/activation/top-level.nix b/nixos/modules/system/activation/top-level.nix
index 75738bc0f5db..56e97d3fdea9 100644
--- a/nixos/modules/system/activation/top-level.nix
+++ b/nixos/modules/system/activation/top-level.nix
@@ -77,14 +77,7 @@ let
);
# Handle assertions and warnings
-
- failedAssertions = map (x: x.message) (filter (x: !x.assertion) config.assertions);
-
- baseSystemAssertWarn =
- if failedAssertions != [ ] then
- throw "\nFailed assertions:\n${concatStringsSep "\n" (map (x: "- ${x}") failedAssertions)}"
- else
- showWarnings config.warnings baseSystem;
+ baseSystemAssertWarn = lib.asserts.checkAssertWarn config.assertions config.warnings baseSystem;
# Replace runtime dependencies
system =
diff --git a/nixos/modules/system/boot/initrd-ssh.nix b/nixos/modules/system/boot/initrd-ssh.nix
index 474f89e1ccf3..76d036766101 100644
--- a/nixos/modules/system/boot/initrd-ssh.nix
+++ b/nixos/modules/system/boot/initrd-ssh.nix
@@ -210,6 +210,7 @@ in
}
${optionalString (!config.boot.initrd.systemd.enable) ''
+ SshdAuthPath /bin/sshd-auth
SshdSessionPath /bin/sshd-session
''}
@@ -239,6 +240,7 @@ in
boot.initrd.extraUtilsCommands = mkIf (!config.boot.initrd.systemd.enable) ''
copy_bin_and_libs ${package}/bin/sshd
+ copy_bin_and_libs ${package}/libexec/sshd-auth
copy_bin_and_libs ${package}/libexec/sshd-session
cp -pv ${pkgs.glibc.out}/lib/libnss_files.so.* $out/lib
'';
@@ -328,6 +330,7 @@ in
};
storePaths = [
"${package}/bin/sshd"
+ "${package}/libexec/sshd-auth"
"${package}/libexec/sshd-session"
];
diff --git a/nixos/modules/system/boot/loader/limine/limine-install.py b/nixos/modules/system/boot/loader/limine/limine-install.py
index 44dee4645e05..3407d778d62b 100644
--- a/nixos/modules/system/boot/loader/limine/limine-install.py
+++ b/nixos/modules/system/boot/loader/limine/limine-install.py
@@ -74,6 +74,7 @@ def is_encrypted(device: str) -> bool:
def is_fs_type_supported(fs_type: str) -> bool:
return fs_type.startswith('vfat')
+paths = {}
def get_copied_path_uri(path: str, target: str) -> str:
result = ''
@@ -85,6 +86,8 @@ def get_copied_path_uri(path: str, target: str) -> str:
if not os.path.exists(dest_path):
copy_file(path, dest_path)
+ else:
+ paths[dest_path] = True
path_with_prefix = os.path.join('/limine', target, dest_file)
result = f'boot():{path_with_prefix}'
@@ -203,7 +206,10 @@ def copy_file(from_path: str, to_path: str):
if not os.path.exists(dirname):
os.makedirs(dirname)
- shutil.copyfile(from_path, to_path)
+ shutil.copyfile(from_path, to_path + ".tmp")
+ os.rename(to_path + ".tmp", to_path)
+
+ paths[to_path] = True
def option_from_config(name: str, config_path: List[str], conversion: Callable[[str], str] | None = None) -> str:
if config(*config_path):
@@ -243,14 +249,16 @@ def main():
partition formatted as FAT.
'''))
+ if config('secureBoot')['enable'] and not config('secureBoot')['createAndEnrollKeys'] and not os.path.exists("/var/lib/sbctl"):
+ print("There are no sbctl secure boot keys present. Please generate some.")
+ sys.exit(1)
+
if not os.path.exists(limine_dir):
os.makedirs(limine_dir)
-
- if os.path.exists(os.path.join(limine_dir, 'kernels')):
- print(f'nuking {os.path.join(limine_dir, "kernels")}')
- shutil.rmtree(os.path.join(limine_dir, 'kernels'))
-
- os.makedirs(os.path.join(limine_dir, "kernels"))
+ else:
+ for dir, dirs, files in os.walk(limine_dir, topdown=True):
+ for file in files:
+ paths[os.path.join(dir, file)] = False
profiles = [('system', get_gens())]
@@ -270,13 +278,6 @@ def main():
default_entry: 2
''')
- if os.path.exists(os.path.join(limine_dir, 'wallpapers')):
- print(f'nuking {os.path.join(limine_dir, "wallpapers")}')
- shutil.rmtree(os.path.join(limine_dir, 'wallpapers'))
-
- if len(config('style', 'wallpapers')) > 0:
- os.makedirs(os.path.join(limine_dir, 'wallpapers'))
-
for wallpaper in config('style', 'wallpapers'):
config_file += f'''wallpaper: {get_copied_path_uri(wallpaper, 'wallpapers')}\n'''
@@ -318,6 +319,8 @@ def main():
file.truncate()
file.write(config_file.strip())
+ paths[config_file_path] = True
+
for dest_path, source_path in config('additionalFiles').items():
dest_path = os.path.join(limine_dir, dest_path)
@@ -353,6 +356,28 @@ def main():
print('error: failed to enroll limine config.', file=sys.stderr)
sys.exit(1)
+ if config('secureBoot')['enable']:
+ sbctl = os.path.join(config('secureBoot')['sbctl'], 'bin', 'sbctl')
+ if config('secureBoot')['createAndEnrollKeys']:
+ print("TEST MODE: creating and enrolling keys")
+ try:
+ subprocess.run([sbctl, 'create-keys'])
+ except:
+ print('error: failed to create keys', file=sys.stderr)
+ sys.exit(1)
+ try:
+ subprocess.run([sbctl, 'enroll-keys', '--yes-this-might-brick-my-machine'])
+ except:
+ print('error: failed to enroll keys', file=sys.stderr)
+ sys.exit(1)
+
+ print('signing limine...')
+ try:
+ subprocess.run([sbctl, 'sign', dest_path])
+ except:
+ print('error: failed to sign limine', file=sys.stderr)
+ sys.exit(1)
+
if not config('efiRemovable') and not config('canTouchEfiVariables'):
print('warning: boot.loader.efi.canTouchEfiVariables is set to false while boot.loader.limine.efiInstallAsRemovable.\n This may render the system unbootable.')
@@ -363,9 +388,16 @@ def main():
efibootmgr = os.path.join(config('efiBootMgrPath'), 'bin', 'efibootmgr')
efi_partition = find_mounted_device(config('efiMountPoint'))
efi_disk = find_disk_device(efi_partition)
+
+ efibootmgr_output = subprocess.check_output([efibootmgr], stderr=subprocess.STDOUT, universal_newlines=True)
+ create_flag = '-c'
+ # Check the output of `efibootmgr` to find if limine is already installed and present in the boot record
+ if matches := re.findall(r'Boot[0-9a-fA-F]{4}\*? Limine', efibootmgr_output):
+ create_flag = '-C' # if present, keep the same boot order
+
efibootmgr_output = subprocess.check_output([
efibootmgr,
- '-c',
+ create_flag,
'-d', efi_disk,
'-p', efi_partition.removeprefix(efi_disk).removeprefix('p'),
'-l', f'\\efi\\limine\\{boot_file}',
@@ -409,4 +441,9 @@ def main():
'Failed to deploy BIOS stage 1 Limine bootloader!\n' +
'You might want to try enabling the `boot.loader.limine.forceMbr` option.')
+ print("removing unused boot files...")
+ for path in paths:
+ if not paths[path]:
+ os.remove(path)
+
main()
diff --git a/nixos/modules/system/boot/loader/limine/limine.nix b/nixos/modules/system/boot/loader/limine/limine.nix
index 182868bd6973..50a000380287 100644
--- a/nixos/modules/system/boot/loader/limine/limine.nix
+++ b/nixos/modules/system/boot/loader/limine/limine.nix
@@ -18,6 +18,7 @@ let
canTouchEfiVariables = efi.canTouchEfiVariables;
efiSupport = cfg.efiSupport;
efiRemovable = cfg.efiInstallAsRemovable;
+ secureBoot = cfg.secureBoot;
biosSupport = cfg.biosSupport;
biosDevice = cfg.biosDevice;
partitionIndex = cfg.partitionIndex;
@@ -177,6 +178,41 @@ in
'';
};
+ secureBoot = {
+ enable = lib.mkEnableOption null // {
+ description = ''
+ Whether to use sign the limine binary with sbctl.
+
+ ::: {.note}
+ This requires you to already have generated the keys and enrolled them with {command}`sbctl`.
+
+ To create keys use {command}`sbctl create-keys`.
+
+ To enroll them first reset secure boot to "Setup Mode". This is device specific.
+ Then enroll them using {command}`sbctl enroll-keys -m -f`.
+
+ You can now rebuild your system with this option enabled.
+
+ Afterwards turn setup mode off and enable secure boot.
+ :::
+ '';
+ };
+
+ createAndEnrollKeys = lib.mkEnableOption null // {
+ internal = true;
+ description = ''
+ Creates secure boot signing keys and enrolls them during bootloader installation.
+
+ ::: {.note}
+ This is used for automated nixos tests.
+ NOT INTENDED to be used on a real system.
+ :::
+ '';
+ };
+
+ sbctl = lib.mkPackageOption pkgs "sbctl" { };
+ };
+
style = {
wallpapers = lib.mkOption {
default = [ ];
@@ -358,14 +394,67 @@ in
system = {
boot.loader.id = "limine";
- build.installBootLoader = pkgs.substituteAll {
+ build.installBootLoader = pkgs.replaceVarsWith {
src = ./limine-install.py;
isExecutable = true;
-
- python3 = pkgs.python3.withPackages (python-packages: [ python-packages.psutil ]);
- configPath = limineInstallConfig;
+ replacements = {
+ python3 = pkgs.python3.withPackages (python-packages: [ python-packages.psutil ]);
+ configPath = limineInstallConfig;
+ };
};
};
})
+ (lib.mkIf (cfg.enable && cfg.secureBoot.enable) {
+ assertions = [
+ {
+ assertion = cfg.enrollConfig;
+ message = "Disabling enrollConfig allows bypassing secure boot.";
+ }
+ {
+ assertion = cfg.validateChecksums;
+ message = "Disabling validateChecksums allows bypassing secure boot.";
+ }
+ {
+ assertion = cfg.panicOnChecksumMismatch;
+ message = "Disabling panicOnChecksumMismatch allows bypassing secure boot.";
+ }
+ {
+ assertion = cfg.efiSupport;
+ message = "Secure boot is only supported on EFI systems.";
+ }
+ ];
+
+ boot.loader.limine.enrollConfig = true;
+ boot.loader.limine.validateChecksums = true;
+ boot.loader.limine.panicOnChecksumMismatch = true;
+ })
+
+ # Fwupd binary needs to be signed in secure boot mode
+ (lib.mkIf (cfg.enable && cfg.secureBoot.enable && config.services.fwupd.enable) {
+ systemd.services.fwupd = {
+ environment.FWUPD_EFIAPPDIR = "/run/fwupd-efi";
+ };
+
+ systemd.services.fwupd-efi = {
+ description = "Sign fwupd EFI app for secure boot";
+ wantedBy = [ "fwupd.service" ];
+ partOf = [ "fwupd.service" ];
+ before = [ "fwupd.service" ];
+ serviceConfig = {
+ Type = "oneshot";
+ RemainAfterExit = true;
+ RuntimeDirectory = "fwupd-efi";
+ };
+ script = ''
+ cp ${config.services.fwupd.package.fwupd-efi}/libexec/fwupd/efi/fwupd*.efi /run/fwupd-efi/
+ chmod +w /run/fwupd-efi/fwupd*.efi
+ ${lib.getExe pkgs.sbctl} sign /run/fwupd-efi/fwupd*.efi
+ '';
+ };
+
+ services.fwupd.uefiCapsuleSettings = {
+ DisableShimForSecureBoot = true;
+ };
+ })
];
}
diff --git a/nixos/modules/system/boot/loader/systemd-boot/systemd-boot-builder.py b/nixos/modules/system/boot/loader/systemd-boot/systemd-boot-builder.py
index 380ac69bdf7e..bd2a6d43301d 100644
--- a/nixos/modules/system/boot/loader/systemd-boot/systemd-boot-builder.py
+++ b/nixos/modules/system/boot/loader/systemd-boot/systemd-boot-builder.py
@@ -335,7 +335,7 @@ def install_bootloader(args: argparse.Namespace) -> None:
available_match = re.search(r"^\((.*)\)$", available_out)
if installed_match is None:
- raise Exception("could not find any previously installed systemd-boot")
+ raise Exception("Could not find any previously installed systemd-boot. If you are switching to systemd-boot from a different bootloader, you need to run `nixos-rebuild switch --install-bootloader`")
if available_match is None:
raise Exception("could not determine systemd-boot version")
diff --git a/nixos/modules/system/boot/loader/systemd-boot/systemd-boot.nix b/nixos/modules/system/boot/loader/systemd-boot/systemd-boot.nix
index e5080bd8d191..f0afb3b8f575 100644
--- a/nixos/modules/system/boot/loader/systemd-boot/systemd-boot.nix
+++ b/nixos/modules/system/boot/loader/systemd-boot/systemd-boot.nix
@@ -47,7 +47,7 @@ let
systemd = config.systemd.package;
- bootspecTools = pkgs.bootspec;
+ bootspecTools = config.boot.bootspec.package;
nix = config.nix.package.out;
diff --git a/nixos/modules/system/boot/luksroot.nix b/nixos/modules/system/boot/luksroot.nix
index 59c5cdb71b00..af7362231ea5 100644
--- a/nixos/modules/system/boot/luksroot.nix
+++ b/nixos/modules/system/boot/luksroot.nix
@@ -408,13 +408,12 @@ let
return
fi
- if [ ! -z "$k_user" ]; then
- new_k_luks="$(echo -n $k_user | pbkdf2-sha512 ${toString dev.yubikey.keyLength} $new_iterations $new_response | rbtohex)"
+ if [ -n "$k_user" ]; then
+ echo -n $k_user
else
- new_k_luks="$(echo | pbkdf2-sha512 ${toString dev.yubikey.keyLength} $new_iterations $new_response | rbtohex)"
- fi
+ echo
+ fi | pbkdf2-sha512 ${toString dev.yubikey.keyLength} $new_iterations $new_response > /crypt-ramfs/new_key
- echo -n "$new_k_luks" | hextorb > /crypt-ramfs/new_key
echo -n "$k_luks" | hextorb | ${cschange} --key-file=- /crypt-ramfs/new_key
if [ $? == 0 ]; then
diff --git a/nixos/modules/system/boot/systemd.nix b/nixos/modules/system/boot/systemd.nix
index 24c317bcc46d..6ac7ff073e78 100644
--- a/nixos/modules/system/boot/systemd.nix
+++ b/nixos/modules/system/boot/systemd.nix
@@ -226,7 +226,32 @@ in
package = mkPackageOption pkgs "systemd" { };
enableStrictShellChecks = mkEnableOption "" // {
- description = "Whether to run shellcheck on the generated scripts for systemd units.";
+ description = ''
+ Whether to run `shellcheck` on the generated scripts for systemd
+ units.
+
+ When enabled, all systemd scripts generated by NixOS will be checked
+ with `shellcheck` and any errors or warnings will cause the build to
+ fail.
+
+ This affects all scripts that have been created through the `script`,
+ `reload`, `preStart`, `postStart`, `preStop` and `postStop` options for
+ systemd services. This does not affect command lines passed directly
+ to `ExecStart`, `ExecReload`, `ExecStartPre`, `ExecStartPost`,
+ `ExecStop` or `ExecStopPost`.
+
+ It therefore also does not affect systemd units that are coming from
+ packages and that are not defined through the NixOS config. This option
+ is disabled by default, and although some services have already been
+ fixed, it is still likely that you will encounter build failures when
+ enabling this.
+
+ We encourage people to enable this option when they are willing and
+ able to submit fixes for potential build failures to Nixpkgs. The
+ option can also be enabled or disabled for individual services using
+ the `enableStrictShellChecks` option on the service itself, which will
+ take precedence over the global setting.
+ '';
};
units = mkOption {
@@ -712,7 +737,12 @@ in
systemd.managerEnvironment = {
# Doesn't contain systemd itself - everything works so it seems to use the compiled-in value for its tools
# util-linux is needed for the main fsck utility wrapping the fs-specific ones
- PATH = lib.makeBinPath (config.system.fsPackages ++ [ cfg.package.util-linux ]);
+ PATH = lib.makeBinPath (
+ config.system.fsPackages
+ ++ [ cfg.package.util-linux ]
+ # systemd-ssh-generator needs sshd in PATH
+ ++ lib.optional config.services.openssh.enable config.services.openssh.package
+ );
LOCALE_ARCHIVE = "/run/current-system/sw/lib/locale/locale-archive";
TZDIR = "/etc/zoneinfo";
# If SYSTEMD_UNIT_PATH ends with an empty component (":"), the usual unit load path will be appended to the contents of the variable
diff --git a/nixos/modules/system/boot/systemd/initrd.nix b/nixos/modules/system/boot/systemd/initrd.nix
index 23b9e232c64d..65caee745124 100644
--- a/nixos/modules/system/boot/systemd/initrd.nix
+++ b/nixos/modules/system/boot/systemd/initrd.nix
@@ -132,13 +132,20 @@ let
initialRamdisk = pkgs.makeInitrdNG {
name = "initrd-${kernel-name}";
inherit (config.boot.initrd) compressor compressorArgs prepend;
- inherit (cfg) strip;
contents = lib.filter ({ source, ... }: !lib.elem source cfg.suppressedStorePaths) cfg.storePaths;
};
in
{
+ imports = [
+ (lib.mkRemovedOptionModule [ "boot" "initrd" "systemd" "strip" ] ''
+ The option to strip ELF files in initrd has been removed.
+ It only saved ~1MiB of initramfs size, but caused a few issues
+ like unloadable kernel modules.
+ '')
+ ];
+
options.boot.initrd.systemd = {
enable = mkEnableOption "systemd in initrd" // {
description = ''
@@ -208,19 +215,6 @@ in
default = [ ];
};
- strip = mkOption {
- description = ''
- Whether to completely strip executables and libraries copied to the initramfs.
-
- Setting this to false may save on the order of 30MiB on the
- machine building the system (by avoiding a binutils
- reference), at the cost of ~1MiB of initramfs size. This puts
- this option firmly in the territory of micro-optimisation.
- '';
- type = types.bool;
- default = true;
- };
-
extraBin = mkOption {
description = ''
Tools to add to /bin
diff --git a/nixos/modules/system/boot/systemd/journald.nix b/nixos/modules/system/boot/systemd/journald.nix
index c30630e04e4b..4d2ca7f11f21 100644
--- a/nixos/modules/system/boot/systemd/journald.nix
+++ b/nixos/modules/system/boot/systemd/journald.nix
@@ -116,22 +116,19 @@ in
};
config = {
- systemd.additionalUpstreamSystemUnits =
- [
- "systemd-journald.socket"
- "systemd-journald@.socket"
- "systemd-journald-varlink@.socket"
- "systemd-journald.service"
- "systemd-journald@.service"
- "systemd-journal-flush.service"
- "systemd-journal-catalog-update.service"
- "systemd-journald-sync@.service"
- ]
- ++ (lib.optional (!config.boot.isContainer) "systemd-journald-audit.socket")
- ++ [
- "systemd-journald-dev-log.socket"
- "syslog.socket"
- ];
+ systemd.additionalUpstreamSystemUnits = [
+ "systemd-journald.socket"
+ "systemd-journald@.socket"
+ "systemd-journald-varlink@.socket"
+ "systemd-journald.service"
+ "systemd-journald@.service"
+ "systemd-journal-flush.service"
+ "systemd-journal-catalog-update.service"
+ "systemd-journald-sync@.service"
+ "systemd-journald-audit.socket"
+ "systemd-journald-dev-log.socket"
+ "syslog.socket"
+ ];
systemd.sockets.systemd-journald-audit.wantedBy = [
"systemd-journald.service"
diff --git a/nixos/modules/system/boot/systemd/repart.nix b/nixos/modules/system/boot/systemd/repart.nix
index a8dc4745c26b..b7ca00cf6a03 100644
--- a/nixos/modules/system/boot/systemd/repart.nix
+++ b/nixos/modules/system/boot/systemd/repart.nix
@@ -72,6 +72,19 @@ in
example = "require";
default = "refuse";
};
+
+ discard = lib.mkOption {
+ type = lib.types.bool;
+ description = ''
+ Controls whether to issue the BLKDISCARD I/O control command on the
+ space taken up by any added partitions or on the space in between them.
+ Usually, it's a good idea to issue this request since it tells the underlying
+ hardware that the covered blocks shall be considered empty, improving performance.
+
+ See {manpage}`systemd-repart(8)` for details.
+ '';
+ default = true;
+ };
};
systemd.repart = {
@@ -163,6 +176,7 @@ in
--definitions=/etc/repart.d \
--dry-run=no \
--empty=${initrdCfg.empty} \
+ --discard=${lib.boolToString initrdCfg.discard} \
${lib.optionalString (initrdCfg.device != null) initrdCfg.device}
''
];
diff --git a/nixos/modules/system/boot/systemd/shutdown.nix b/nixos/modules/system/boot/systemd/shutdown.nix
index 1e8b8c6f863c..242b47cc40b1 100644
--- a/nixos/modules/system/boot/systemd/shutdown.nix
+++ b/nixos/modules/system/boot/systemd/shutdown.nix
@@ -52,6 +52,7 @@ in
what = "tmpfs";
where = "/run/initramfs";
type = "tmpfs";
+ options = "mode=0700";
}
];
diff --git a/nixos/modules/system/boot/tmp.nix b/nixos/modules/system/boot/tmp.nix
index 9ec02c594cf0..f927ba1c9c90 100644
--- a/nixos/modules/system/boot/tmp.nix
+++ b/nixos/modules/system/boot/tmp.nix
@@ -31,6 +31,23 @@ in
'';
};
+ tmpfsHugeMemoryPages = lib.mkOption {
+ type = lib.types.enum [
+ "never"
+ "always"
+ "within_size"
+ "advise"
+ ];
+ default = "never";
+ example = "within_size";
+ description = ''
+ never - Do not allocate huge memory pages. This is the default.
+ always - Attempt to allocate huge memory page every time a new page is needed.
+ within_size - Only allocate huge memory pages if it will be fully within i_size. Also respect madvise(2) hints. Recommended.
+ advise - Only allocate huge memory pages if requested with madvise(2).
+ '';
+ };
+
useTmpfs = lib.mkOption {
type = lib.types.bool;
default = false;
@@ -60,6 +77,7 @@ in
"nosuid"
"nodev"
"size=${toString cfg.tmpfsSize}"
+ "huge=${cfg.tmpfsHugeMemoryPages}"
];
}
];
diff --git a/nixos/modules/system/boot/uki.nix b/nixos/modules/system/boot/uki.nix
index fd9f1cadd340..d9f4713b307c 100644
--- a/nixos/modules/system/boot/uki.nix
+++ b/nixos/modules/system/boot/uki.nix
@@ -111,8 +111,7 @@ in
--config=${cfg.configFile} \
--output="$out/${config.system.boot.loader.ukiFile}"
'';
-
- meta.maintainers = with lib.maintainers; [ nikstur ];
-
};
+
+ meta.maintainers = with lib.maintainers; [ nikstur ];
}
diff --git a/nixos/modules/system/boot/unl0kr.nix b/nixos/modules/system/boot/unl0kr.nix
index 318090047b3a..7b7197cb0c10 100644
--- a/nixos/modules/system/boot/unl0kr.nix
+++ b/nixos/modules/system/boot/unl0kr.nix
@@ -75,6 +75,7 @@ in
"usbtouchscreen"
"evdev"
+ "psmouse"
]
++ lib.optionals cfg.allowVendorDrivers [
"intel_lpss_pci"
diff --git a/nixos/modules/system/etc/etc-activation.nix b/nixos/modules/system/etc/etc-activation.nix
index ed08ba928785..355e9695d8b9 100644
--- a/nixos/modules/system/etc/etc-activation.nix
+++ b/nixos/modules/system/etc/etc-activation.nix
@@ -52,7 +52,7 @@
where = "/run/nixos-etc-metadata";
what = "/etc-metadata-image";
type = "erofs";
- options = "loop,ro";
+ options = "loop,ro,nodev,nosuid";
unitConfig = {
# Since this unit depends on the nix store being mounted, it cannot
# be a dependency of local-fs.target, because if it did, we'd have
@@ -81,6 +81,8 @@
type = "overlay";
options = lib.concatStringsSep "," (
[
+ "nodev"
+ "nosuid"
"relatime"
"redirect_dir=on"
"metacopy=on"
diff --git a/nixos/modules/system/etc/etc.nix b/nixos/modules/system/etc/etc.nix
index 9fbfda5814d5..0be5a3b9ae82 100644
--- a/nixos/modules/system/etc/etc.nix
+++ b/nixos/modules/system/etc/etc.nix
@@ -41,8 +41,6 @@ let
if [ "$(readlink "$out/etc/$target")" != "$src" ]; then
echo "mismatched duplicate entry $(readlink "$out/etc/$target") <-> $src"
ret=1
-
- continue
fi
fi
@@ -285,23 +283,23 @@ in
''}
tmpMetadataMount=$(TMPDIR="/run" mktemp --directory -t nixos-etc-metadata.XXXXXXXXXX)
- mount --type erofs -o ro ${config.system.build.etcMetadataImage} $tmpMetadataMount
+ mount --type erofs --options ro,nodev,nosuid ${config.system.build.etcMetadataImage} $tmpMetadataMount
# There was no previous /etc mounted. This happens when we're called
# directly without an initrd, like with nixos-enter.
if ! mountpoint -q /etc; then
- mount --type overlay overlay \
- --options lowerdir=$tmpMetadataMount::${config.system.build.etcBasedir},${etcOverlayOptions} \
- /etc
+ mount --type overlay \
+ --options nodev,nosuid,lowerdir=$tmpMetadataMount::${config.system.build.etcBasedir},${etcOverlayOptions} \
+ overlay /etc
else
# Mount the new /etc overlay to a temporary private mount.
# This needs the indirection via a private bind mount because you
# cannot move shared mounts.
tmpEtcMount=$(TMPDIR="/run" mktemp --directory -t nixos-etc.XXXXXXXXXX)
mount --bind --make-private $tmpEtcMount $tmpEtcMount
- mount --type overlay overlay \
- --options lowerdir=$tmpMetadataMount::${config.system.build.etcBasedir},${etcOverlayOptions} \
- $tmpEtcMount
+ mount --type overlay \
+ --options nodev,nosuid,lowerdir=$tmpMetadataMount::${config.system.build.etcBasedir},${etcOverlayOptions} \
+ overlay $tmpEtcMount
# Before moving the new /etc overlay under the old /etc, we have to
# move mounts on top of /etc to the new /etc mountpoint.
diff --git a/nixos/modules/tasks/filesystems/zfs.nix b/nixos/modules/tasks/filesystems/zfs.nix
index dff0f99bdd29..f8e36eb1646c 100644
--- a/nixos/modules/tasks/filesystems/zfs.nix
+++ b/nixos/modules/tasks/filesystems/zfs.nix
@@ -233,7 +233,7 @@ let
tries=3
success=false
while [[ $success != true ]] && [[ $tries -gt 0 ]]; do
- ${systemd}/bin/systemd-ask-password --timeout=${toString cfgZfs.passwordTimeout} "Enter key for $ds:" | ${cfgZfs.package}/sbin/zfs load-key "$ds" \
+ ${systemd}/bin/systemd-ask-password ${lib.optionalString cfgZfs.useKeyringForCredentials ("--keyname=zfs-$ds")} --timeout=${toString cfgZfs.passwordTimeout} "Enter key for $ds:" | ${cfgZfs.package}/sbin/zfs load-key "$ds" \
&& success=true \
|| tries=$((tries - 1))
done
@@ -403,6 +403,8 @@ in
'';
};
+ useKeyringForCredentials = lib.mkEnableOption "Uses the kernel keyring for encryption credentials with keyname=zfs-";
+
passwordTimeout = lib.mkOption {
type = lib.types.int;
default = 0;
diff --git a/nixos/modules/tasks/network-interfaces.nix b/nixos/modules/tasks/network-interfaces.nix
index 68e5dc88d4be..fb8dab1c33ff 100644
--- a/nixos/modules/tasks/network-interfaces.nix
+++ b/nixos/modules/tasks/network-interfaces.nix
@@ -578,21 +578,22 @@ in
};
networking.fqdn = mkOption {
- readOnly = true;
type = types.str;
default =
if (cfg.hostName != "" && cfg.domain != null) then
"${cfg.hostName}.${cfg.domain}"
else
throw ''
- The FQDN is required but cannot be determined. Please make sure that
- both networking.hostName and networking.domain are set properly.
+ The FQDN is required but cannot be determined from `networking.hostName`
+ and `networking.domain`. Please ensure these options are set properly or
+ set `networking.fqdn` directly.
'';
defaultText = literalExpression ''"''${networking.hostName}.''${networking.domain}"'';
description = ''
- The fully qualified domain name (FQDN) of this host. It is the result
- of combining `networking.hostName` and `networking.domain.` Using this
- option will result in an evaluation error if the hostname is empty or
+ The fully qualified domain name (FQDN) of this host. By default, it is
+ the result of combining `networking.hostName` and `networking.domain.`
+
+ Using this option will result in an evaluation error if the hostname is empty or
no domain is specified.
Modules that accept a mere `networking.hostName` but prefer a fully qualified
@@ -603,17 +604,21 @@ in
networking.fqdnOrHostName = mkOption {
readOnly = true;
type = types.str;
- default = if cfg.domain == null then cfg.hostName else cfg.fqdn;
+ default =
+ if (cfg.domain != null || opt.fqdn.highestPrio < (mkOptionDefault { }).priority) then
+ cfg.fqdn
+ else
+ cfg.hostName;
defaultText = literalExpression ''
- if cfg.domain == null then cfg.hostName else cfg.fqdn
+ if config.networking.domain != null || config.networking.fqdn is set then config.networking.fqdn else config.networking.hostName
'';
description = ''
Either the fully qualified domain name (FQDN), or just the host name if
- it does not exists.
+ it does not exist.
This is a convenience option for modules to read instead of `fqdn` when
a mere `hostName` is also an acceptable value; this option does not
- throw an error when `domain` is unset.
+ throw an error when `domain` or `fqdn` is unset.
'';
};
diff --git a/nixos/modules/testing/test-instrumentation.nix b/nixos/modules/testing/test-instrumentation.nix
index 49594964ed5f..80852be51f1d 100644
--- a/nixos/modules/testing/test-instrumentation.nix
+++ b/nixos/modules/testing/test-instrumentation.nix
@@ -86,7 +86,6 @@ in
enables commands to be sent to test and debug stage 1. Use
machine.switch_root() to leave stage 1 and proceed to stage 2
'';
-
};
config = {
diff --git a/nixos/modules/virtualisation/cloudstack-config.nix b/nixos/modules/virtualisation/cloudstack-config.nix
index 7df3c9c613b4..bb08b68a8d39 100644
--- a/nixos/modules/virtualisation/cloudstack-config.nix
+++ b/nixos/modules/virtualisation/cloudstack-config.nix
@@ -8,9 +8,10 @@ with lib;
];
config = {
- fileSystems."/" = {
+ fileSystems."/" = lib.mkImageMediaOverride {
device = "/dev/disk/by-label/nixos";
autoResize = true;
+ fsType = "ext4";
};
boot.growPartition = true;
diff --git a/nixos/modules/virtualisation/gce-images.nix b/nixos/modules/virtualisation/gce-images.nix
deleted file mode 100644
index 79631ed025df..000000000000
--- a/nixos/modules/virtualisation/gce-images.nix
+++ /dev/null
@@ -1,20 +0,0 @@
-let
- self = {
- "14.12" = "gs://nixos-cloud-images/nixos-14.12.471.1f09b77-x86_64-linux.raw.tar.gz";
- "15.09" = "gs://nixos-cloud-images/nixos-15.09.425.7870f20-x86_64-linux.raw.tar.gz";
- "16.03" = "gs://nixos-cloud-images/nixos-image-16.03.847.8688c17-x86_64-linux.raw.tar.gz";
- "17.03" = "gs://nixos-cloud-images/nixos-image-17.03.1082.4aab5c5798-x86_64-linux.raw.tar.gz";
- "18.03" = "gs://nixos-cloud-images/nixos-image-18.03.132536.fdb5ba4cdf9-x86_64-linux.raw.tar.gz";
- "18.09" = "gs://nixos-cloud-images/nixos-image-18.09.1228.a4c4cbb613c-x86_64-linux.raw.tar.gz";
-
- # This format will be handled by the upcoming NixOPS 2.0 release.
- # The old images based on a GS object are deprecated.
- "20.09" = {
- project = "nixos-cloud";
- name = "nixos-image-20-09-3531-3858fbc08e6-x86-64-linux";
- };
-
- latest = self."20.09";
- };
-in
-self
diff --git a/nixos/modules/virtualisation/google-compute-image.nix b/nixos/modules/virtualisation/google-compute-image.nix
index 98190e7e2276..e1021c755aea 100644
--- a/nixos/modules/virtualisation/google-compute-image.nix
+++ b/nixos/modules/virtualisation/google-compute-image.nix
@@ -56,6 +56,33 @@ in
GZIP compression level of the resulting disk image (1-9).
'';
};
+
+ virtualisation.googleComputeImage.contents = mkOption {
+ type = with types; listOf attrs;
+ default = [ ];
+ description = ''
+ The files and directories to be placed in the image.
+ This is a list of attribute sets {source, target, mode, user, group} where
+ `source' is the file system object (regular file or directory) to be
+ grafted in the file system at path `target', `mode' is a string containing
+ the permissions that will be set (ex. "755"), `user' and `group' are the
+ user and group name that will be set as owner of the files.
+ `mode', `user', and `group' are optional.
+ When setting one of `user' or `group', the other needs to be set too.
+ '';
+ example = literalExpression ''
+ [
+ {
+ source = ./default.nix;
+ target = "/etc/nixos/default.nix";
+ mode = "0644";
+ user = "root";
+ group = "root";
+ }
+ ];
+ '';
+ };
+
virtualisation.googleComputeImage.efi = mkEnableOption "EFI booting";
};
@@ -99,6 +126,7 @@ in
'';
format = "raw";
configFile = if cfg.configFile == null then defaultConfigFile else cfg.configFile;
+ inherit (cfg) contents;
partitionTableType = if cfg.efi then "efi" else "legacy";
inherit (config.virtualisation) diskSize;
inherit config lib pkgs;
diff --git a/nixos/modules/virtualisation/incus.nix b/nixos/modules/virtualisation/incus.nix
index 136cd20eee55..6637141d0330 100644
--- a/nixos/modules/virtualisation/incus.nix
+++ b/nixos/modules/virtualisation/incus.nix
@@ -129,6 +129,7 @@ let
environment = lib.mkMerge [
{
+ INCUS_DOCUMENTATION = "${cfg.package.doc}/html";
INCUS_EDK2_PATH = ovmf;
INCUS_LXC_HOOK = "${cfg.lxcPackage}/share/lxc/hooks";
INCUS_LXC_TEMPLATE_CONFIG = "${pkgs.lxcfs}/share/lxc/config";
diff --git a/nixos/modules/virtualisation/libvirtd.nix b/nixos/modules/virtualisation/libvirtd.nix
index 2fe7406c8362..6d739cb0fa3d 100644
--- a/nixos/modules/virtualisation/libvirtd.nix
+++ b/nixos/modules/virtualisation/libvirtd.nix
@@ -410,7 +410,7 @@ in
etc."qemu/bridge.conf".text = lib.concatMapStringsSep "\n" (e: "allow ${e}") cfg.allowedBridges;
systemPackages = with pkgs; [
libressl.nc
- iptables
+ config.networking.firewall.package
cfg.package
cfg.qemu.package
];
diff --git a/nixos/modules/virtualisation/nixos-containers.nix b/nixos/modules/virtualisation/nixos-containers.nix
index 14d25ac76569..e5a0d37d6a9c 100644
--- a/nixos/modules/virtualisation/nixos-containers.nix
+++ b/nixos/modules/virtualisation/nixos-containers.nix
@@ -122,9 +122,12 @@ let
NIX_BIND_OPT=""
if [ -n "$PRIVATE_USERS" ]; then
extraFlags+=("--private-users=$PRIVATE_USERS")
- if [ "$PRIVATE_USERS" = "pick" ] || { [ "$PRIVATE_USERS" != "identity" ] && [ "$PRIVATE_USERS" -gt 0 ]; }; then
- # when user namespacing is enabled, we use `idmap` mount option
- # so that bind mounts under /nix get proper owner (and not nobody/nogroup).
+ if [[
+ "$PRIVATE_USERS" = "pick"
+ || ("$PRIVATE_USERS" =~ ^[[:digit:]]+$ && "$PRIVATE_USERS" -gt 0)
+ ]]; then
+ # when user namespacing is enabled, we use `idmap` mount option so that
+ # bind mounts under /nix get proper owner (and not nobody/nogroup).
NIX_BIND_OPT=":idmap"
fi
fi
diff --git a/nixos/modules/virtualisation/oci-containers.nix b/nixos/modules/virtualisation/oci-containers.nix
index d81246e31383..0cd9acd9a042 100644
--- a/nixos/modules/virtualisation/oci-containers.nix
+++ b/nixos/modules/virtualisation/oci-containers.nix
@@ -416,7 +416,7 @@ let
# try logging in, if it fails, check if image exists locally
${cfg.backend} login \
${container.login.registry} \
- --username ${container.login.username} \
+ --username ${escapeShellArg container.login.username} \
--password-stdin < ${container.login.passwordFile} \
|| ${cfg.backend} image inspect ${container.image} >/dev/null \
|| { echo "image doesn't exist locally and login failed" >&2 ; exit 1; }
@@ -434,6 +434,7 @@ let
};
effectiveUser = container.podman.user or "root";
+ inherit (config.users.users.${effectiveUser}) uid;
dependOnLingerService =
cfg.backend == "podman" && effectiveUser != "root" && config.users.users.${effectiveUser}.linger;
in
@@ -441,7 +442,7 @@ let
wantedBy = [ ] ++ optional (container.autoStart) "multi-user.target";
wants =
lib.optional (container.imageFile == null && container.imageStream == null) "network-online.target"
- ++ lib.optional dependOnLingerService "linger-users.service";
+ ++ lib.optionals dependOnLingerService [ "linger-users.service" ];
after =
lib.optionals (cfg.backend == "docker") [
"docker.service"
@@ -452,8 +453,15 @@ let
"network-online.target"
]
++ dependsOn
- ++ lib.optional dependOnLingerService "linger-users.service";
- requires = dependsOn;
+ ++ lib.optionals dependOnLingerService [ "linger-users.service" ]
+ ++ lib.optionals (effectiveUser != "root" && container.podman.sdnotify == "healthy") [
+ "user@${toString uid}.service"
+ ];
+ requires =
+ dependsOn
+ ++ lib.optionals (effectiveUser != "root" && container.podman.sdnotify == "healthy") [
+ "user@${toString uid}.service"
+ ];
environment = lib.mkMerge [
proxy_env
(mkIf (cfg.backend == "podman" && container.podman.user != "root") {
@@ -523,6 +531,10 @@ let
else
"${cfg.backend} rm -f ${name} || true";
+ unitConfig = mkIf (effectiveUser != "root") {
+ RequiresMountsFor = "/run/user/${toString uid}/containers";
+ };
+
serviceConfig =
{
### There is no generalized way of supporting `reload` for docker
@@ -616,6 +628,15 @@ in
assertion = cfg.backend == "docker" -> podman == null;
message = "virtualisation.oci-containers.containers.${name}: Cannot set `podman` option if backend is `docker`.";
}
+ {
+ assertion =
+ cfg.backend == "podman" && podman.sdnotify == "healthy" && podman.user != "root"
+ -> config.users.users.${podman.user}.uid != null;
+ message = ''
+ Rootless container ${name} (with podman and sdnotify=healthy)
+ requires that its running user ${podman.user} has a statically specified uid.
+ '';
+ }
];
in
concatMap (name: toAssertions name cfg.containers.${name}) (lib.attrNames cfg.containers);
diff --git a/nixos/modules/virtualisation/qemu-guest-agent.nix b/nixos/modules/virtualisation/qemu-guest-agent.nix
index 9f12177922be..aa66f7b682ad 100644
--- a/nixos/modules/virtualisation/qemu-guest-agent.nix
+++ b/nixos/modules/virtualisation/qemu-guest-agent.nix
@@ -25,7 +25,7 @@ in
{
services.udev.extraRules = ''
- SUBSYSTEM=="virtio-ports", ATTR{name}=="org.qemu.guest_agent.0", TAG+="systemd" ENV{SYSTEMD_WANTS}="qemu-guest-agent.service"
+ SUBSYSTEM=="virtio-ports", ATTR{name}=="org.qemu.guest_agent.0", TAG+="systemd", ENV{SYSTEMD_WANTS}="qemu-guest-agent.service"
'';
systemd.services.qemu-guest-agent = {
diff --git a/nixos/modules/virtualisation/vmware-host.nix b/nixos/modules/virtualisation/vmware-host.nix
index cb7156b74047..eaabc2fea551 100644
--- a/nixos/modules/virtualisation/vmware-host.nix
+++ b/nixos/modules/virtualisation/vmware-host.nix
@@ -75,10 +75,25 @@ in
environment.systemPackages = [ cfg.package ] ++ cfg.extraPackages;
services.printing.drivers = [ cfg.package ];
- environment.etc."vmware/config".text = ''
- ${builtins.readFile "${cfg.package}/etc/vmware/config"}
- ${cfg.extraConfig}
- '';
+ environment.etc."vmware/config".source =
+ let
+ packageConfig = "${cfg.package}/etc/vmware/config";
+ in
+ if cfg.extraConfig == "" then
+ packageConfig
+ else
+ pkgs.runCommandLocal "etc-vmware-config"
+ {
+ inherit packageConfig;
+ inherit (cfg) extraConfig;
+ }
+ ''
+ (
+ cat "$packageConfig"
+ printf "\n"
+ echo "$extraConfig"
+ ) >"$out"
+ '';
environment.etc."vmware/bootstrap".source = "${cfg.package}/etc/vmware/bootstrap";
environment.etc."vmware/icu".source = "${cfg.package}/etc/vmware/icu";
diff --git a/nixos/modules/virtualisation/waagent.nix b/nixos/modules/virtualisation/waagent.nix
index d09bb99939b6..4929b8435170 100644
--- a/nixos/modules/virtualisation/waagent.nix
+++ b/nixos/modules/virtualisation/waagent.nix
@@ -228,11 +228,11 @@ let
};
};
- AutoUpdate.Enable = lib.mkOption {
+ AutoUpdate.UpdateToLatestVersion = lib.mkOption {
type = types.bool;
default = false;
description = ''
- Whether or not to enable autoupdate for goal state processing.
+ Whether or not to enable auto-update of the Extension Handler.
'';
};
};
diff --git a/nixos/release.nix b/nixos/release.nix
index 316b66d9bd4e..b7b550fb4721 100644
--- a/nixos/release.nix
+++ b/nixos/release.nix
@@ -339,32 +339,6 @@ rec {
);
- # Test job for https://github.com/NixOS/nixpkgs/issues/121354 to test
- # automatic sizing without blocking the channel.
- amazonImageAutomaticSize = forMatchingSystems [ "x86_64-linux" "aarch64-linux" ] (
- system:
-
- with import ./.. { inherit system; };
-
- hydraJob (
- (import lib/eval-config.nix {
- inherit system;
- modules = [
- configuration
- versionModule
- ./maintainers/scripts/ec2/amazon-image.nix
- (
- { ... }:
- {
- virtualisation.diskSize = "auto";
- }
- )
- ];
- }).config.system.build.amazonImage
- )
-
- );
-
# An image that can be imported into incus and used for container creation
incusContainerImage =
forMatchingSystems
diff --git a/nixos/tests/acme/http01-builtin.nix b/nixos/tests/acme/http01-builtin.nix
index ddc784128fbd..8589190a2667 100644
--- a/nixos/tests/acme/http01-builtin.nix
+++ b/nixos/tests/acme/http01-builtin.nix
@@ -99,6 +99,45 @@ in
"builtin-3.${domain}".listenHTTP = ":80";
};
};
+
+ csr.configuration =
+ let
+ conf = pkgs.writeText "openssl.csr.conf" ''
+ [req]
+ default_bits = 2048
+ prompt = no
+ default_md = sha256
+ req_extensions = req_ext
+ distinguished_name = dn
+
+ [ dn ]
+ CN = ${config.networking.fqdn}
+
+ [ req_ext ]
+ subjectAltName = @alt_names
+
+ [ alt_names ]
+ DNS.1 = ${config.networking.fqdn}
+ '';
+ csrData =
+ pkgs.runCommandNoCC "csr-and-key"
+ {
+ buildInputs = [ pkgs.openssl ];
+ }
+ ''
+ mkdir -p $out
+ openssl req -new -newkey rsa:2048 -nodes \
+ -keyout $out/key.pem \
+ -out $out/request.csr \
+ -config ${conf}
+ '';
+ in
+ {
+ security.acme.certs."${config.networking.fqdn}" = {
+ csr = "${csrData}/request.csr";
+ csrKey = "${csrData}/key.pem";
+ };
+ };
};
};
};
@@ -211,5 +250,10 @@ in
with subtest("Validate permissions (self-signed)"):
check_permissions(builtin, cert, "acme")
+
+ with subtest("Can renew using a CSR"):
+ builtin.succeed(f"systemctl clean acme-{cert}.service --what=state")
+ switch_to(builtin, "csr")
+ check_issuer(builtin, cert, "pebble")
'';
}
diff --git a/nixos/tests/agnos.nix b/nixos/tests/agnos.nix
new file mode 100644
index 000000000000..b73f1c021412
--- /dev/null
+++ b/nixos/tests/agnos.nix
@@ -0,0 +1,209 @@
+{
+ system ? builtins.currentSystem,
+ pkgs ? import ../.. { inherit system; },
+ lib ? pkgs.lib,
+}:
+
+let
+ inherit (import ../lib/testing-python.nix { inherit system pkgs; }) makeTest;
+ nodeIP = n: n.networking.primaryIPAddress;
+ dnsZone =
+ nodes:
+ pkgs.writeText "agnos.test.zone" ''
+ $TTL 604800
+ @ IN SOA ns1.agnos.test. root.agnos.test. (
+ 3 ; Serial
+ 604800 ; Refresh
+ 86400 ; Retry
+ 2419200 ; Expire
+ 604800 ) ; Negative Cache TTL
+ ;
+ ; name servers - NS records
+ IN NS ns1.agnos.test.
+
+ ; name servers - A records
+ ns1.agnos.test. IN A ${nodeIP nodes.dnsserver}
+
+ agnos-ns.agnos.test. IN A ${nodeIP nodes.server}
+ _acme-challenge.a.agnos.test. IN NS agnos-ns.agnos.test.
+ _acme-challenge.b.agnos.test. IN NS agnos-ns.agnos.test.
+ _acme-challenge.c.agnos.test. IN NS agnos-ns.agnos.test.
+ _acme-challenge.d.agnos.test. IN NS agnos-ns.agnos.test.
+ '';
+
+ mkTest =
+ {
+ name,
+ extraServerConfig ? { },
+ checkFirewallClosed ? true,
+ }:
+ makeTest {
+ inherit name;
+ meta = {
+ maintainers = with lib.maintainers; [ justinas ];
+ };
+
+ nodes = {
+ # The fake ACME server which will respond to client requests
+ acme =
+ { nodes, pkgs, ... }:
+ {
+ imports = [ ./common/acme/server ];
+ environment.systemPackages = [ pkgs.netcat ];
+ networking.nameservers = lib.mkForce [ (nodeIP nodes.dnsserver) ];
+ };
+
+ # A fake DNS server which points _acme-challenge subdomains to "server"
+ dnsserver =
+ { nodes, ... }:
+ {
+ networking.firewall.allowedTCPPorts = [ 53 ];
+ networking.firewall.allowedUDPPorts = [ 53 ];
+ services.bind = {
+ cacheNetworks = [ "192.168.1.0/24" ];
+ enable = true;
+ extraOptions = ''
+ dnssec-validation no;
+ '';
+ zones."agnos.test" = {
+ file = dnsZone nodes;
+ master = true;
+ };
+ };
+ };
+
+ # The server using agnos to request certificates
+ server =
+ { nodes, ... }:
+ {
+ imports = [ extraServerConfig ];
+
+ networking.extraHosts = ''
+ ${nodeIP nodes.acme} acme.test
+ '';
+ security.agnos = {
+ enable = true;
+ generateKeys.enable = true;
+ persistent = false;
+ server = "https://acme.test/dir";
+ serverCa = ./common/acme/server/ca.cert.pem;
+ temporarilyOpenFirewall = true;
+
+ settings.accounts = [
+ {
+ email = "webmaster@agnos.test";
+ # account with an existing private key
+ private_key_path = "${./common/acme/server/acme.test.key.pem}";
+
+ certificates = [
+ {
+ domains = [ "a.agnos.test" ];
+ # Absolute paths
+ fullchain_output_file = "/tmp/a.agnos.test.crt";
+ key_output_file = "/tmp/a.agnos.test.key";
+ }
+
+ {
+ domains = [
+ "b.agnos.test"
+ "*.b.agnos.test"
+ ];
+ # Relative paths
+ fullchain_output_file = "b.agnos.test.crt";
+ key_output_file = "b.agnos.test.key";
+ }
+ ];
+ }
+
+ {
+ email = "webmaster2@agnos.test";
+ # account with a missing private key, should get generated
+ private_key_path = "webmaster2.key";
+
+ certificates = [
+ {
+ domains = [ "c.agnos.test" ];
+ # Absolute paths
+ fullchain_output_file = "/tmp/c.agnos.test.crt";
+ key_output_file = "/tmp/c.agnos.test.key";
+ }
+
+ {
+ domains = [
+ "d.agnos.test"
+ "*.d.agnos.test"
+ ];
+ # Relative paths
+ fullchain_output_file = "d.agnos.test.crt";
+ key_output_file = "d.agnos.test.key";
+ }
+ ];
+ }
+ ];
+ };
+ };
+ };
+
+ testScript = ''
+ def check_firewall_closed(caller):
+ """
+ Check that TCP port 53 is closed again.
+
+ Since we do not set `networking.firewall.rejectPackets`,
+ "timed out" indicates a closed port,
+ while "connection refused" (after agnos has shut down) indicates an open port.
+ """
+
+ out = caller.fail("nc -v -z -w 1 server 53 2>&1")
+ assert "Connection timed out" in out
+
+ start_all()
+ acme.wait_for_unit('pebble.service')
+ server.wait_for_unit('default.target')
+
+ # Test that agnos.timer is scheduled
+ server.succeed("systemctl status agnos.timer")
+ server.succeed('systemctl start agnos.service')
+
+ expected_perms = "640 agnos agnos"
+ outputs = [
+ "/tmp/a.agnos.test.crt",
+ "/tmp/a.agnos.test.key",
+ "/var/lib/agnos/b.agnos.test.crt",
+ "/var/lib/agnos/b.agnos.test.key",
+ "/var/lib/agnos/webmaster2.key",
+ "/tmp/c.agnos.test.crt",
+ "/tmp/c.agnos.test.key",
+ "/var/lib/agnos/d.agnos.test.crt",
+ "/var/lib/agnos/d.agnos.test.key",
+ ]
+ for o in outputs:
+ out = server.succeed(f"stat -c '%a %U %G' {o}").strip()
+ assert out == expected_perms, \
+ f"Expected mode/owner/group to be '{expected_perms}', but it was '{out}'"
+
+ ${lib.optionalString checkFirewallClosed "check_firewall_closed(acme)"}
+ '';
+ };
+in
+{
+ iptables = mkTest {
+ name = "iptables";
+ };
+
+ nftables = mkTest {
+ name = "nftables";
+ extraServerConfig = {
+ networking.nftables.enable = true;
+ };
+ };
+
+ no-firewall = mkTest {
+ name = "no-firewall";
+ extraServerConfig = {
+ networking.firewall.enable = lib.mkForce false;
+ security.agnos.temporarilyOpenFirewall = lib.mkForce false;
+ };
+ checkFirewallClosed = false;
+ };
+}
diff --git a/nixos/tests/all-tests.nix b/nixos/tests/all-tests.nix
index 9ee7f01fef1b..5501ffb8b92b 100644
--- a/nixos/tests/all-tests.nix
+++ b/nixos/tests/all-tests.nix
@@ -8,7 +8,7 @@
}:
# The return value of this function will be an attrset with arbitrary depth and
-# the `anything` returned by callTest at its test leafs.
+# the `anything` returned by callTest at its test leaves.
# The tests not supported by `system` will be replaced with `{}`, so that
# `passthru.tests` can contain links to those without breaking on architectures
# where said tests are unsupported.
@@ -177,6 +177,7 @@ in
agate = runTest ./web-servers/agate.nix;
agda = runTest ./agda.nix;
age-plugin-tpm-decrypt = runTest ./age-plugin-tpm-decrypt.nix;
+ agnos = discoverTests (import ./agnos.nix);
agorakit = runTest ./web-apps/agorakit.nix;
airsonic = runTest ./airsonic.nix;
akkoma = runTestOn [ "x86_64-linux" "aarch64-linux" ] {
@@ -197,6 +198,7 @@ in
amd-sev = runTest ./amd-sev.nix;
angie-api = runTest ./angie-api.nix;
anki-sync-server = runTest ./anki-sync-server.nix;
+ anubis = runTest ./anubis.nix;
anuko-time-tracker = runTest ./anuko-time-tracker.nix;
apcupsd = runTest ./apcupsd.nix;
apfs = runTest ./apfs.nix;
@@ -208,9 +210,10 @@ in
armagetronad = runTest ./armagetronad.nix;
artalk = runTest ./artalk.nix;
atd = runTest ./atd.nix;
- atop = handleTest ./atop.nix { };
+ atop = import ./atop.nix { inherit pkgs runTest; };
atticd = runTest ./atticd.nix;
atuin = runTest ./atuin.nix;
+ ax25 = handleTest ./ax25.nix { };
audiobookshelf = runTest ./audiobookshelf.nix;
auth-mysql = runTest ./auth-mysql.nix;
authelia = runTest ./authelia.nix;
@@ -243,7 +246,7 @@ in
imports = [ ./binary-cache.nix ];
_module.args.compression = "xz";
};
- bind = handleTest ./bind.nix { };
+ bind = runTest ./bind.nix;
bird = handleTest ./bird.nix { };
birdwatcher = handleTest ./birdwatcher.nix { };
bitbox-bridge = runTest ./bitbox-bridge.nix;
@@ -274,12 +277,10 @@ in
cadvisor = handleTestOn [ "x86_64-linux" ] ./cadvisor.nix { };
cage = handleTest ./cage.nix { };
cagebreak = handleTest ./cagebreak.nix { };
- calibre-web = handleTest ./calibre-web.nix { };
- calibre-server = handleTest ./calibre-server.nix { };
+ calibre-web = runTest ./calibre-web.nix;
+ calibre-server = import ./calibre-server.nix { inherit pkgs runTest; };
canaille = handleTest ./canaille.nix { };
castopod = handleTest ./castopod.nix { };
- cassandra_3_0 = handleTest ./cassandra.nix { testPackage = pkgs.cassandra_3_0; };
- cassandra_3_11 = handleTest ./cassandra.nix { testPackage = pkgs.cassandra_3_11; };
cassandra_4 = handleTest ./cassandra.nix { testPackage = pkgs.cassandra_4; };
centrifugo = runTest ./centrifugo.nix;
ceph-multi-node = handleTestOn [ "aarch64-linux" "x86_64-linux" ] ./ceph-multi-node.nix { };
@@ -294,7 +295,7 @@ in
] ./ceph-single-node-bluestore-dmcrypt.nix { };
certmgr = handleTest ./certmgr.nix { };
cfssl = handleTestOn [ "aarch64-linux" "x86_64-linux" ] ./cfssl.nix { };
- cgit = handleTest ./cgit.nix { };
+ cgit = runTest ./cgit.nix;
charliecloud = handleTest ./charliecloud.nix { };
chromadb = runTest ./chromadb.nix;
chromium = (handleTestOn [ "aarch64-linux" "x86_64-linux" ] ./chromium.nix { }).stable or { };
@@ -315,7 +316,6 @@ in
coder = handleTest ./coder.nix { };
collectd = handleTest ./collectd.nix { };
commafeed = handleTest ./commafeed.nix { };
- conduwuit = runTest ./matrix/conduwuit.nix;
connman = handleTest ./connman.nix { };
consul = handleTest ./consul.nix { };
consul-template = handleTest ./consul-template.nix { };
@@ -338,18 +338,41 @@ in
containers-unified-hierarchy = handleTest ./containers-unified-hierarchy.nix { };
convos = handleTest ./convos.nix { };
corerad = handleTest ./corerad.nix { };
+ cosmic = runTest {
+ imports = [ ./cosmic.nix ];
+ _module.args.testName = "cosmic";
+ _module.args.enableAutologin = false;
+ _module.args.enableXWayland = true;
+ };
+ cosmic-autologin = runTest {
+ imports = [ ./cosmic.nix ];
+ _module.args.testName = "cosmic-autologin";
+ _module.args.enableAutologin = true;
+ _module.args.enableXWayland = true;
+ };
+ cosmic-noxwayland = runTest {
+ imports = [ ./cosmic.nix ];
+ _module.args.testName = "cosmic-noxwayland";
+ _module.args.enableAutologin = false;
+ _module.args.enableXWayland = false;
+ };
+ cosmic-autologin-noxwayland = runTest {
+ imports = [ ./cosmic.nix ];
+ _module.args.testName = "cosmic-autologin-noxwayland";
+ _module.args.enableAutologin = true;
+ _module.args.enableXWayland = false;
+ };
coturn = handleTest ./coturn.nix { };
couchdb = handleTest ./couchdb.nix { };
crabfit = handleTest ./crabfit.nix { };
cri-o = handleTestOn [ "aarch64-linux" "x86_64-linux" ] ./cri-o.nix { };
cryptpad = runTest ./cryptpad.nix;
- cups-pdf = handleTest ./cups-pdf.nix { };
+ cups-pdf = runTest ./cups-pdf.nix;
curl-impersonate = handleTest ./curl-impersonate.nix { };
custom-ca = handleTest ./custom-ca.nix { };
croc = handleTest ./croc.nix { };
cross-seed = runTest ./cross-seed.nix;
cyrus-imap = runTest ./cyrus-imap.nix;
- darling = handleTest ./darling.nix { };
darling-dmg = runTest ./darling-dmg.nix;
dae = handleTest ./dae.nix { };
davis = runTest ./davis.nix;
@@ -397,7 +420,7 @@ in
ecryptfs = handleTest ./ecryptfs.nix { };
fscrypt = handleTest ./fscrypt.nix { };
fastnetmon-advanced = runTest ./fastnetmon-advanced.nix;
- eintopf = handleTest ./eintopf.nix { };
+ eintopf = runTest ./eintopf.nix;
ejabberd = handleTest ./xmpp/ejabberd.nix { };
elk = handleTestOn [ "x86_64-linux" ] ./elk.nix { };
emacs-daemon = runTest ./emacs-daemon.nix;
@@ -434,7 +457,7 @@ in
evcc = runTest ./evcc.nix;
fail2ban = runTest ./fail2ban.nix;
fakeroute = handleTest ./fakeroute.nix { };
- fancontrol = handleTest ./fancontrol.nix { };
+ fancontrol = runTest ./fancontrol.nix;
fanout = handleTest ./fanout.nix { };
fcitx5 = handleTest ./fcitx5 { };
fedimintd = runTest ./fedimintd.nix;
@@ -480,7 +503,7 @@ in
imports = [ ./firefox.nix ];
_module.args.firefoxPackage = pkgs.floorp;
};
- fluent-bit = handleTest ./fluent-bit.nix { };
+ fluent-bit = runTest ./fluent-bit.nix;
fluentd = handleTest ./fluentd.nix { };
fluidd = handleTest ./fluidd.nix { };
fontconfig-default-fonts = handleTest ./fontconfig-default-fonts.nix { };
@@ -511,12 +534,12 @@ in
gemstash = handleTest ./gemstash.nix { };
geoclue2 = runTest ./geoclue2.nix;
geoserver = runTest ./geoserver.nix;
- gerrit = handleTest ./gerrit.nix { };
+ gerrit = runTest ./gerrit.nix;
geth = handleTest ./geth.nix { };
ghostunnel = handleTest ./ghostunnel.nix { };
gitdaemon = handleTest ./gitdaemon.nix { };
gitea = handleTest ./gitea.nix { giteaPackage = pkgs.gitea; };
- github-runner = handleTest ./github-runner.nix { };
+ github-runner = runTest ./github-runner.nix;
gitlab = runTest ./gitlab.nix;
gitolite = handleTest ./gitolite.nix { };
gitolite-fcgiwrap = handleTest ./gitolite-fcgiwrap.nix { };
@@ -524,7 +547,7 @@ in
glances = runTest ./glances.nix;
glitchtip = runTest ./glitchtip.nix;
glusterfs = handleTest ./glusterfs.nix { };
- gnome = handleTest ./gnome.nix { };
+ gnome = runTest ./gnome.nix;
gnome-extensions = handleTest ./gnome-extensions.nix { };
gnome-flashback = handleTest ./gnome-flashback.nix { };
gnome-xorg = handleTest ./gnome-xorg.nix { };
@@ -569,9 +592,8 @@ in
inherit handleTestOn;
package = pkgs.hadoop2;
};
- haka = handleTest ./haka.nix { };
haste-server = handleTest ./haste-server.nix { };
- haproxy = handleTest ./haproxy.nix { };
+ haproxy = runTest ./haproxy.nix;
hardened = handleTest ./hardened.nix { };
harmonia = runTest ./harmonia.nix;
headscale = handleTest ./headscale.nix { };
@@ -584,7 +606,7 @@ in
herbstluftwm = handleTest ./herbstluftwm.nix { };
homebox = handleTest ./homebox.nix { };
homer = handleTest ./homer { };
- homepage-dashboard = handleTest ./homepage-dashboard.nix { };
+ homepage-dashboard = runTest ./homepage-dashboard.nix;
honk = runTest ./honk.nix;
installed-tests = pkgs.recurseIntoAttrs (handleTest ./installed-tests { });
invidious = handleTest ./invidious.nix { };
@@ -596,7 +618,7 @@ in
odoo = handleTest ./odoo.nix { };
odoo17 = handleTest ./odoo.nix { package = pkgs.odoo17; };
odoo16 = handleTest ./odoo.nix { package = pkgs.odoo16; };
- odoo15 = handleTest ./odoo.nix { package = pkgs.odoo15; };
+ oncall = runTest ./web-apps/oncall.nix;
# 9pnet_virtio used to mount /nix partition doesn't support
# hibernation. This test happens to work on x86_64-linux but
# not on other platforms.
@@ -610,7 +632,7 @@ in
home-assistant = runTest ./home-assistant.nix;
hostname = handleTest ./hostname.nix { };
hound = handleTest ./hound.nix { };
- hub = handleTest ./git/hub.nix { };
+ hub = runTest ./git/hub.nix;
hydra = runTest ./hydra;
i3wm = handleTest ./i3wm.nix { };
icingaweb2 = runTest ./icingaweb2.nix;
@@ -655,7 +677,7 @@ in
jool = import ./jool.nix { inherit pkgs runTest; };
jotta-cli = handleTest ./jotta-cli.nix { };
k3s = handleTest ./k3s { };
- kafka = handleTest ./kafka.nix { };
+ kafka = handleTest ./kafka { };
kanboard = runTest ./web-apps/kanboard.nix;
kanidm = handleTest ./kanidm.nix { };
kanidm-provisioning = handleTest ./kanidm-provisioning.nix { };
@@ -671,11 +693,12 @@ in
kernel-latest-ath-user-regd = handleTest ./kernel-latest-ath-user-regd.nix { };
kernel-rust = handleTest ./kernel-rust.nix { };
keter = handleTest ./keter.nix { };
- kexec = handleTest ./kexec.nix { };
+ kexec = runTest ./kexec.nix;
keycloak = discoverTests (import ./keycloak.nix);
keyd = handleTest ./keyd.nix { };
keymap = handleTest ./keymap.nix { };
- kimai = handleTest ./kimai.nix { };
+ kimai = runTest ./kimai.nix;
+ kismet = runTest ./kismet.nix;
kmonad = runTest ./kmonad.nix;
knot = runTest ./knot.nix;
komga = handleTest ./komga.nix { };
@@ -688,10 +711,11 @@ in
languagetool = handleTest ./languagetool.nix { };
lanraragi = handleTest ./lanraragi.nix { };
latestKernel.login = handleTest ./login.nix { latestKernel = true; };
+ lavalink = runTest ./lavalink.nix;
leaps = handleTest ./leaps.nix { };
lemmy = handleTest ./lemmy.nix { };
libinput = handleTest ./libinput.nix { };
- librenms = handleTest ./librenms.nix { };
+ librenms = runTest ./librenms.nix;
libresprite = handleTest ./libresprite.nix { };
libreswan = runTest ./libreswan.nix;
libreswan-nat = runTest ./libreswan-nat.nix;
@@ -703,17 +727,19 @@ in
libvirtd = handleTest ./libvirtd.nix { };
lidarr = handleTest ./lidarr.nix { };
lightdm = handleTest ./lightdm.nix { };
- lighttpd = handleTest ./lighttpd.nix { };
+ lighttpd = runTest ./lighttpd.nix;
+ livekit = runTest ./networking/livekit.nix;
limesurvey = handleTest ./limesurvey.nix { };
limine = import ./limine { inherit runTest; };
listmonk = handleTestOn [ "x86_64-linux" "aarch64-linux" ] ./listmonk.nix { };
litellm = runTest ./litellm.nix;
litestream = handleTest ./litestream.nix { };
+ lk-jwt-service = runTest ./matrix/lk-jwt-service.nix;
lldap = handleTest ./lldap.nix { };
localsend = handleTest ./localsend.nix { };
locate = handleTest ./locate.nix { };
login = handleTest ./login.nix { };
- logrotate = handleTest ./logrotate.nix { };
+ logrotate = runTest ./logrotate.nix;
loki = handleTest ./loki.nix { };
luks = handleTest ./luks.nix { };
lvm2 = handleTest ./lvm2 { };
@@ -740,10 +766,10 @@ in
magic-wormhole-mailbox-server = runTest ./magic-wormhole-mailbox-server.nix;
magnetico = handleTest ./magnetico.nix { };
mailcatcher = runTest ./mailcatcher.nix;
- mailhog = handleTest ./mailhog.nix { };
- mailpit = handleTest ./mailpit.nix { };
- mailman = handleTest ./mailman.nix { };
- man = handleTest ./man.nix { };
+ mailhog = runTest ./mailhog.nix;
+ mailpit = runTest ./mailpit.nix;
+ mailman = runTest ./mailman.nix;
+ man = runTest ./man.nix;
mariadb-galera = handleTest ./mysql/mariadb-galera.nix { };
marytts = handleTest ./marytts.nix { };
mastodon = pkgs.recurseIntoAttrs (handleTest ./web-apps/mastodon { inherit handleTestOn; });
@@ -755,6 +781,7 @@ in
matrix-alertmanager = runTest ./matrix/matrix-alertmanager.nix;
matrix-appservice-irc = runTest ./matrix/appservice-irc.nix;
matrix-conduit = handleTest ./matrix/conduit.nix { };
+ matrix-continuwuity = runTest ./matrix/continuwuity.nix;
matrix-synapse = handleTest ./matrix/synapse.nix { };
matrix-synapse-workers = handleTest ./matrix/synapse-workers.nix { };
mautrix-meta-postgres = handleTest ./matrix/mautrix-meta-postgres.nix { };
@@ -765,7 +792,7 @@ in
mediatomb = handleTest ./mediatomb.nix { };
mediawiki = handleTest ./mediawiki.nix { };
meilisearch = handleTest ./meilisearch.nix { };
- memcached = handleTest ./memcached.nix { };
+ memcached = runTest ./memcached.nix;
merecat = handleTest ./merecat.nix { };
metabase = handleTest ./metabase.nix { };
mihomo = handleTest ./mihomo.nix { };
@@ -795,7 +822,7 @@ in
defaults.services.mongodb.package = config.node.pkgs.mongodb-ce;
}
);
- moodle = handleTest ./moodle.nix { };
+ moodle = runTest ./moodle.nix;
moonraker = handleTest ./moonraker.nix { };
mopidy = handleTest ./mopidy.nix { };
morph-browser = runTest ./morph-browser.nix;
@@ -803,11 +830,11 @@ in
mosquitto = runTest ./mosquitto.nix;
moosefs = handleTest ./moosefs.nix { };
movim = import ./web-apps/movim { inherit recurseIntoAttrs runTest; };
- mpd = handleTest ./mpd.nix { };
+ mpd = runTest ./mpd.nix;
mpv = runTest ./mpv.nix;
mtp = handleTest ./mtp.nix { };
multipass = handleTest ./multipass.nix { };
- mumble = handleTest ./mumble.nix { };
+ mumble = runTest ./mumble.nix;
# Fails on aarch64-linux at the PDF creation step - need to debug this on an
# aarch64 machine..
musescore = handleTestOn [ "x86_64-linux" ] ./musescore.nix { };
@@ -859,7 +886,7 @@ in
# TODO: put in networking.nix after the test becomes more complete
networkingProxy = handleTest ./networking-proxy.nix { };
nextcloud = handleTest ./nextcloud { };
- nextflow = handleTestOn [ "x86_64-linux" ] ./nextflow.nix { };
+ nextflow = runTestOn [ "x86_64-linux" ] ./nextflow.nix;
nextjs-ollama-llm-ui = runTest ./web-apps/nextjs-ollama-llm-ui.nix;
nexus = handleTest ./nexus.nix { };
# TODO: Test nfsv3 + Kerberos
@@ -887,7 +914,7 @@ in
nifi = runTestOn [ "x86_64-linux" ] ./web-apps/nifi.nix;
nitter = handleTest ./nitter.nix { };
nix-config = handleTest ./nix-config.nix { };
- nix-ld = handleTest ./nix-ld.nix { };
+ nix-ld = runTest ./nix-ld.nix;
nix-misc = handleTest ./nix/misc.nix { };
nix-upgrade = handleTest ./nix/upgrade.nix { inherit (pkgs) nixVersions; };
nix-required-mounts = runTest ./nix-required-mounts;
@@ -923,7 +950,7 @@ in
nomad = runTest ./nomad.nix;
non-default-filesystems = handleTest ./non-default-filesystems.nix { };
non-switchable-system = runTest ./non-switchable-system.nix;
- noto-fonts = handleTest ./noto-fonts.nix { };
+ noto-fonts = runTest ./noto-fonts.nix;
noto-fonts-cjk-qt-default-weight = handleTest ./noto-fonts-cjk-qt-default-weight.nix { };
novacomd = handleTestOn [ "x86_64-linux" ] ./novacomd.nix { };
npmrc = handleTest ./npmrc.nix { };
@@ -939,13 +966,16 @@ in
nzbhydra2 = handleTest ./nzbhydra2.nix { };
ocis = handleTest ./ocis.nix { };
oddjobd = handleTestOn [ "x86_64-linux" "aarch64-linux" ] ./oddjobd.nix { };
- obs-studio = handleTest ./obs-studio.nix { };
+ obs-studio = runTest ./obs-studio.nix;
oh-my-zsh = handleTest ./oh-my-zsh.nix { };
+ olivetin = runTest ./olivetin.nix;
ollama = runTest ./ollama.nix;
ollama-cuda = runTestOn [ "x86_64-linux" "aarch64-linux" ] ./ollama-cuda.nix;
ollama-rocm = runTestOn [ "x86_64-linux" "aarch64-linux" ] ./ollama-rocm.nix;
ombi = handleTest ./ombi.nix { };
openarena = handleTest ./openarena.nix { };
+ openbao = runTest ./openbao.nix;
+ opencloud = runTest ./opencloud.nix;
openldap = handleTest ./openldap.nix { };
opensearch = discoverTests (import ./opensearch.nix);
openresty-lua = handleTest ./openresty-lua.nix { };
@@ -965,6 +995,7 @@ in
orthanc = runTest ./orthanc.nix;
owncast = handleTest ./owncast.nix { };
outline = handleTest ./outline.nix { };
+ i18n = runTest ./i18n.nix;
image-contents = handleTest ./image-contents.nix { };
openvscode-server = handleTest ./openvscode-server.nix { };
open-webui = runTest ./open-webui.nix;
@@ -990,13 +1021,14 @@ in
paperless = handleTest ./paperless.nix { };
parsedmarc = handleTest ./parsedmarc { };
password-option-override-ordering = handleTest ./password-option-override-ordering.nix { };
- pdns-recursor = handleTest ./pdns-recursor.nix { };
+ pdns-recursor = runTest ./pdns-recursor.nix;
pds = handleTest ./pds.nix { };
peerflix = handleTest ./peerflix.nix { };
peering-manager = handleTest ./web-apps/peering-manager.nix { };
peertube = handleTestOn [ "x86_64-linux" ] ./web-apps/peertube.nix { };
peroxide = handleTest ./peroxide.nix { };
pgadmin4 = runTest ./pgadmin4.nix;
+ pgbackrest = import ./pgbackrest { inherit runTest; };
pgbouncer = handleTest ./pgbouncer.nix { };
pghero = runTest ./pghero.nix;
pgweb = runTest ./pgweb.nix;
@@ -1039,6 +1071,7 @@ in
pleroma = handleTestOn [ "x86_64-linux" "aarch64-linux" ] ./pleroma.nix { };
plikd = handleTest ./plikd.nix { };
plotinus = handleTest ./plotinus.nix { };
+ pocket-id = handleTest ./pocket-id.nix { };
podgrab = handleTest ./podgrab.nix { };
podman = handleTestOn [ "aarch64-linux" "x86_64-linux" ] ./podman/default.nix { };
podman-tls-ghostunnel = handleTestOn [
@@ -1053,6 +1086,7 @@ in
handleTest ./postfix-raise-smtpd-tls-security-level.nix
{ };
postfixadmin = handleTest ./postfixadmin.nix { };
+ postgres-websockets = runTest ./postgres-websockets.nix;
postgresql = handleTest ./postgresql { };
postgrest = runTest ./postgrest.nix;
powerdns = handleTest ./powerdns.nix { };
@@ -1063,26 +1097,30 @@ in
pretalx = runTest ./web-apps/pretalx.nix;
prefect = runTest ./prefect.nix;
pretix = runTest ./web-apps/pretix.nix;
- printing-socket = handleTest ./printing.nix {
- socket = true;
- listenTcp = true;
+ printing-socket = runTest {
+ imports = [ ./printing.nix ];
+ _module.args.socket = true;
+ _module.args.listenTcp = true;
};
- printing-service = handleTest ./printing.nix {
- socket = false;
- listenTcp = true;
+ printing-service = runTest {
+ imports = [ ./printing.nix ];
+ _module.args.socket = false;
+ _module.args.listenTcp = true;
};
- printing-socket-notcp = handleTest ./printing.nix {
- socket = true;
- listenTcp = false;
+ printing-socket-notcp = runTest {
+ imports = [ ./printing.nix ];
+ _module.args.socket = true;
+ _module.args.listenTcp = false;
};
- printing-service-notcp = handleTest ./printing.nix {
- socket = false;
- listenTcp = false;
+ printing-service-notcp = runTest {
+ imports = [ ./printing.nix ];
+ _module.args.socket = false;
+ _module.args.listenTcp = false;
};
private-gpt = handleTest ./private-gpt.nix { };
privatebin = runTest ./privatebin.nix;
privoxy = handleTest ./privoxy.nix { };
- prometheus = handleTest ./prometheus { };
+ prometheus = import ./prometheus { inherit runTest; };
prometheus-exporters = handleTest ./prometheus-exporters.nix { };
prosody = handleTest ./xmpp/prosody.nix { };
prosody-mysql = handleTest ./xmpp/prosody-mysql.nix { };
@@ -1122,6 +1160,7 @@ in
redmine = handleTestOn [ "x86_64-linux" "aarch64-linux" ] ./redmine.nix { };
renovate = handleTest ./renovate.nix { };
replace-dependencies = handleTest ./replace-dependencies { };
+ reposilite = runTest ./reposilite.nix;
restartByActivationScript = handleTest ./restart-by-activation-script.nix { };
restic-rest-server = handleTest ./restic-rest-server.nix { };
restic = handleTest ./restic.nix { };
@@ -1143,10 +1182,11 @@ in
rsyslogd = handleTest ./rsyslogd.nix { };
rtkit = runTest ./rtkit.nix;
rtorrent = handleTest ./rtorrent.nix { };
+ rush = runTest ./rush.nix;
rustls-libssl = handleTest ./rustls-libssl.nix { };
rxe = handleTest ./rxe.nix { };
sabnzbd = handleTest ./sabnzbd.nix { };
- samba = handleTest ./samba.nix { };
+ samba = runTest ./samba.nix;
samba-wsdd = handleTest ./samba-wsdd.nix { };
sane = handleTest ./sane.nix { };
sanoid = handleTest ./sanoid.nix { };
@@ -1155,6 +1195,7 @@ in
schleuder = handleTest ./schleuder.nix { };
scion-freestanding-deployment = handleTest ./scion/freestanding-deployment { };
scrutiny = runTest ./scrutiny.nix;
+ scx = runTest ./scx/default.nix;
sddm = handleTest ./sddm.nix { };
sdl3 = handleTest ./sdl3.nix { };
seafile = handleTest ./seafile.nix { };
@@ -1171,7 +1212,7 @@ in
shadowsocks = handleTest ./shadowsocks { };
shattered-pixel-dungeon = handleTest ./shattered-pixel-dungeon.nix { };
shiori = handleTest ./shiori.nix { };
- signal-desktop = handleTest ./signal-desktop.nix { };
+ signal-desktop = runTest ./signal-desktop.nix;
silverbullet = handleTest ./silverbullet.nix { };
simple = handleTest ./simple.nix { };
sing-box = handleTest ./sing-box.nix { };
@@ -1202,9 +1243,9 @@ in
sssd-ldap = handleTestOn [ "x86_64-linux" "aarch64-linux" ] ./sssd-ldap.nix { };
stalwart-mail = handleTest ./stalwart-mail.nix { };
stargazer = runTest ./web-servers/stargazer.nix;
- starship = handleTest ./starship.nix { };
+ starship = runTest ./starship.nix;
stash = handleTestOn [ "x86_64-linux" "aarch64-linux" ] ./stash.nix { };
- static-web-server = handleTest ./web-servers/static-web-server.nix { };
+ static-web-server = runTest ./web-servers/static-web-server.nix;
step-ca = handleTestOn [ "x86_64-linux" ] ./step-ca.nix { };
stratis = handleTest ./stratis { };
strongswan-swanctl = handleTest ./strongswan-swanctl.nix { };
@@ -1235,6 +1276,7 @@ in
syncthing-no-settings = handleTest ./syncthing-no-settings.nix { };
syncthing-init = handleTest ./syncthing-init.nix { };
syncthing-many-devices = handleTest ./syncthing-many-devices.nix { };
+ syncthing-folders = runTest ./syncthing-folders.nix;
syncthing-relay = handleTest ./syncthing-relay.nix { };
sysinit-reactivation = runTest ./sysinit-reactivation.nix;
systemd = handleTest ./systemd.nix { };
@@ -1259,7 +1301,7 @@ in
systemd-initrd-luks-unl0kr = handleTest ./systemd-initrd-luks-unl0kr.nix { };
systemd-initrd-modprobe = handleTest ./systemd-initrd-modprobe.nix { };
systemd-initrd-shutdown = handleTest ./systemd-shutdown.nix { systemdStage1 = true; };
- systemd-initrd-simple = handleTest ./systemd-initrd-simple.nix { };
+ systemd-initrd-simple = runTest ./systemd-initrd-simple.nix;
systemd-initrd-swraid = handleTest ./systemd-initrd-swraid.nix { };
systemd-initrd-vconsole = handleTest ./systemd-initrd-vconsole.nix { };
systemd-initrd-networkd = handleTest ./systemd-initrd-networkd.nix { };
@@ -1291,6 +1333,7 @@ in
systemd-portabled = handleTest ./systemd-portabled.nix { };
systemd-repart = handleTest ./systemd-repart.nix { };
systemd-resolved = handleTest ./systemd-resolved.nix { };
+ systemd-ssh-proxy = runTest ./systemd-ssh-proxy.nix;
systemd-shutdown = handleTest ./systemd-shutdown.nix { };
systemd-sysupdate = runTest ./systemd-sysupdate.nix;
systemd-sysusers-mutable = runTest ./systemd-sysusers-mutable.nix;
@@ -1350,15 +1393,17 @@ in
tuptime = handleTest ./tuptime.nix { };
turbovnc-headless-server = handleTest ./turbovnc-headless-server.nix { };
turn-rs = handleTest ./turn-rs.nix { };
- tuxguitar = handleTest ./tuxguitar.nix { };
+ tusd = runTest ./tusd/default.nix;
+ tuxguitar = runTest ./tuxguitar.nix;
twingate = runTest ./twingate.nix;
typesense = handleTest ./typesense.nix { };
+ tzupdate = runTest ./tzupdate.nix;
ucarp = handleTest ./ucarp.nix { };
udisks2 = handleTest ./udisks2.nix { };
ulogd = handleTest ./ulogd/ulogd.nix { };
umurmur = handleTest ./umurmur.nix { };
unbound = handleTest ./unbound.nix { };
- unifi = handleTest ./unifi.nix { };
+ unifi = runTest ./unifi.nix;
unit-php = runTest ./web-servers/unit-php.nix;
unit-perl = handleTest ./web-servers/unit-perl.nix { };
upnp.iptables = handleTest ./upnp.nix { useNftables = false; };
@@ -1383,13 +1428,9 @@ in
imports = [ ./varnish.nix ];
_module.args.package = pkgs.varnish60;
};
- varnish75 = runTest {
+ varnish77 = runTest {
imports = [ ./varnish.nix ];
- _module.args.package = pkgs.varnish75;
- };
- varnish76 = runTest {
- imports = [ ./varnish.nix ];
- _module.args.package = pkgs.varnish76;
+ _module.args.package = pkgs.varnish77;
};
vault = handleTest ./vault.nix { };
vault-agent = handleTest ./vault-agent.nix { };
@@ -1411,11 +1452,12 @@ in
wakapi = runTest ./wakapi.nix;
warzone2100 = handleTest ./warzone2100.nix { };
wasabibackend = handleTest ./wasabibackend.nix { };
- wastebin = handleTest ./wastebin.nix { };
+ wastebin = runTest ./wastebin.nix;
watchdogd = handleTest ./watchdogd.nix { };
webhook = runTest ./webhook.nix;
weblate = handleTest ./web-apps/weblate.nix { };
whisparr = handleTest ./whisparr.nix { };
+ whoami = runTest ./whoami.nix;
whoogle-search = handleTest ./whoogle-search.nix { };
wiki-js = runTest ./wiki-js.nix;
wine = handleTest ./wine.nix { };
@@ -1427,7 +1469,7 @@ in
wpa_supplicant = import ./wpa_supplicant.nix { inherit pkgs runTest; };
wordpress = runTest ./wordpress.nix;
wrappers = handleTest ./wrappers.nix { };
- writefreely = handleTest ./web-apps/writefreely.nix { };
+ writefreely = import ./web-apps/writefreely.nix { inherit pkgs runTest; };
wstunnel = runTest ./wstunnel.nix;
xandikos = runTest ./xandikos.nix;
xautolock = runTest ./xautolock.nix;
@@ -1443,6 +1485,7 @@ in
xterm = runTest ./xterm.nix;
xxh = runTest ./xxh.nix;
yabar = runTest ./yabar.nix;
+ yarr = runTest ./yarr.nix;
ydotool = handleTest ./ydotool.nix { };
yggdrasil = runTest ./yggdrasil.nix;
your_spotify = runTest ./your_spotify.nix;
diff --git a/nixos/tests/anubis.nix b/nixos/tests/anubis.nix
new file mode 100644
index 000000000000..e5f643ed1cca
--- /dev/null
+++ b/nixos/tests/anubis.nix
@@ -0,0 +1,123 @@
+{ lib, ... }:
+{
+ name = "anubis";
+ meta.maintainers = with lib.maintainers; [
+ soopyc
+ nullcube
+ ];
+
+ nodes.machine =
+ {
+ config,
+ pkgs,
+ ...
+ }:
+ {
+ services.anubis = {
+ defaultOptions.settings = {
+ DIFFICULTY = 3;
+ USER_DEFINED_DEFAULT = true;
+ };
+ instances = {
+ "".settings = {
+ TARGET = "http://localhost:8080";
+ DIFFICULTY = 5;
+ USER_DEFINED_INSTANCE = true;
+ };
+
+ "tcp" = {
+ user = "anubis-tcp";
+ group = "anubis-tcp";
+ settings = {
+ TARGET = "http://localhost:8080";
+ BIND = ":9000";
+ BIND_NETWORK = "tcp";
+ METRICS_BIND = ":9001";
+ METRICS_BIND_NETWORK = "tcp";
+ };
+ };
+
+ "unix-upstream" = {
+ group = "nginx";
+ settings.TARGET = "unix:///run/nginx/nginx.sock";
+ };
+ };
+ };
+
+ # support
+ users.users.nginx.extraGroups = [ config.users.groups.anubis.name ];
+ services.nginx = {
+ enable = true;
+ recommendedProxySettings = true;
+ virtualHosts."basic.localhost".locations = {
+ "/".proxyPass = "http://unix:${config.services.anubis.instances."".settings.BIND}";
+ "/metrics".proxyPass = "http://unix:${config.services.anubis.instances."".settings.METRICS_BIND}";
+ };
+
+ virtualHosts."tcp.localhost".locations = {
+ "/".proxyPass = "http://localhost:9000";
+ "/metrics".proxyPass = "http://localhost:9001";
+ };
+
+ virtualHosts."unix.localhost".locations = {
+ "/".proxyPass = "http://unix:${config.services.anubis.instances.unix-upstream.settings.BIND}";
+ };
+
+ # emulate an upstream with nginx, listening on tcp and unix sockets.
+ virtualHosts."upstream.localhost" = {
+ default = true; # make nginx match this vhost for `localhost`
+ listen = [
+ { addr = "unix:/run/nginx/nginx.sock"; }
+ {
+ addr = "localhost";
+ port = 8080;
+ }
+ ];
+ locations."/" = {
+ tryFiles = "$uri $uri/index.html =404";
+ root = pkgs.runCommand "anubis-test-upstream" { } ''
+ mkdir $out
+ echo "it works" >> $out/index.html
+ '';
+ };
+ };
+ };
+ };
+
+ testScript = ''
+ for unit in ["nginx", "anubis", "anubis-tcp", "anubis-unix-upstream"]:
+ machine.wait_for_unit(unit + ".service")
+
+ for port in [9000, 9001]:
+ machine.wait_for_open_port(port)
+
+ for instance in ["anubis", "anubis-unix-upstream"]:
+ machine.wait_for_open_unix_socket(f"/run/anubis/{instance}.sock")
+ machine.wait_for_open_unix_socket(f"/run/anubis/{instance}-metrics.sock")
+
+ # Default unix socket mode
+ machine.succeed('curl -f http://basic.localhost | grep "it works"')
+ machine.succeed('curl -f http://basic.localhost -H "User-Agent: Mozilla" | grep anubis')
+ machine.succeed('curl -f http://basic.localhost/metrics | grep anubis_challenges_issued')
+ machine.succeed('curl -f -X POST http://basic.localhost/.within.website/x/cmd/anubis/api/make-challenge | grep challenge')
+
+ # TCP mode
+ machine.succeed('curl -f http://tcp.localhost -H "User-Agent: Mozilla" | grep anubis')
+ machine.succeed('curl -f http://tcp.localhost/metrics | grep anubis_challenges_issued')
+
+ # Upstream is a unix socket mode
+ machine.succeed('curl -f http://unix.localhost/index.html | grep "it works"')
+
+ # Default user-defined environment variables
+ machine.succeed('cat /run/current-system/etc/systemd/system/anubis.service | grep "USER_DEFINED_DEFAULT"')
+ machine.succeed('cat /run/current-system/etc/systemd/system/anubis-tcp.service | grep "USER_DEFINED_DEFAULT"')
+
+ # Instance-specific user-specified environment variables
+ machine.succeed('cat /run/current-system/etc/systemd/system/anubis.service | grep "USER_DEFINED_INSTANCE"')
+ machine.fail('cat /run/current-system/etc/systemd/system/anubis-tcp.service | grep "USER_DEFINED_INSTANCE"')
+
+ # Make sure defaults don't overwrite themselves
+ machine.succeed('cat /run/current-system/etc/systemd/system/anubis.service | grep "DIFFICULTY=5"')
+ machine.succeed('cat /run/current-system/etc/systemd/system/anubis-tcp.service | grep "DIFFICULTY=3"')
+ '';
+}
diff --git a/nixos/tests/armagetronad.nix b/nixos/tests/armagetronad.nix
index 392cdb0437bb..4e7833b3520b 100644
--- a/nixos/tests/armagetronad.nix
+++ b/nixos/tests/armagetronad.nix
@@ -115,7 +115,7 @@ in
self.node.wait_for_text(text)
self.send(*keys)
- Server = namedtuple('Server', ('node', 'name', 'address', 'port', 'welcome', 'attacker', 'victim', 'coredump_delay'))
+ Server = namedtuple('Server', ('node', 'name', 'address', 'port', 'welcome', 'player1', 'player2'))
# Clients and their in-game names
clients = (
@@ -125,9 +125,9 @@ in
# Server configs.
servers = (
- Server(server, 'high-rubber', 'server', 4534, 'NixOS Smoke Test Server', 'SmOoThIcE', 'Arduino', 8),
- Server(server, 'sty', 'server', 4535, 'NixOS Smoke Test sty+ct+ap Server', 'Arduino', 'SmOoThIcE', 8),
- Server(server, 'trunk', 'server', 4536, 'NixOS Smoke Test 0.4 Server', 'Arduino', 'SmOoThIcE', 8)
+ Server(server, 'high-rubber', 'server', 4534, 'NixOS Smoke Test Server', 'SmOoThIcE', 'Arduino'),
+ Server(server, 'sty', 'server', 4535, 'NixOS Smoke Test sty+ct+ap Server', 'Arduino', 'SmOoThIcE'),
+ Server(server, 'trunk', 'server', 4536, 'NixOS Smoke Test 0.4 Server', 'Arduino', 'SmOoThIcE')
)
"""
@@ -146,8 +146,55 @@ in
client.node.screenshot(f"screen_{client.name}_{screenshot_idx}")
return screenshot_idx + 1
- # Wait for the servers to come up.
+ """
+ Sets up a client, waiting for the given barrier on completion.
+ """
+ def client_setup(client, servers, barrier):
+ client.node.wait_for_x()
+
+ # Configure Armagetron so we skip the tutorial.
+ client.node.succeed(
+ run("mkdir -p ~/.armagetronad/var"),
+ run(f"echo 'PLAYER_1 {client.name}' >> ~/.armagetronad/var/autoexec.cfg"),
+ run("echo 'FIRST_USE 0' >> ~/.armagetronad/var/autoexec.cfg")
+ )
+ for idx, srv in enumerate(servers):
+ client.node.succeed(
+ run(f"echo 'BOOKMARK_{idx+1}_ADDRESS {srv.address}' >> ~/.armagetronad/var/autoexec.cfg"),
+ run(f"echo 'BOOKMARK_{idx+1}_NAME {srv.name}' >> ~/.armagetronad/var/autoexec.cfg"),
+ run(f"echo 'BOOKMARK_{idx+1}_PORT {srv.port}' >> ~/.armagetronad/var/autoexec.cfg")
+ )
+
+ # Start Armagetron. Use the recording mode since it skips the splashscreen.
+ client.node.succeed(run("cd; ulimit -c unlimited; armagetronad --record test.aarec >&2 & disown"))
+ client.node.wait_until_succeeds(
+ run(
+ "${xdo "create_new_win-select_main_window" ''
+ search --onlyvisible --name "Armagetron Advanced"
+ windowfocus --sync
+ windowactivate --sync
+ ''}"
+ )
+ )
+
+ # Get into the multiplayer menu.
+ client.send_on('Armagetron Advanced', 'ret')
+ client.send_on('Play Game', 'ret')
+
+ # Online > LAN > Network Setup > Mates > Server Bookmarks
+ client.send_on('Multiplayer', 'down', 'down', 'down', 'down', 'ret')
+
+ barrier.wait()
+
+ # Start everything.
start_all()
+
+ # Get to the Server Bookmarks screen on both clients. This takes a while so do it asynchronously.
+ barrier = threading.Barrier(len(clients) + 1, timeout=600)
+ for client in clients:
+ threading.Thread(target=client_setup, args=(client, servers, barrier)).start()
+
+ # Wait for the servers to come up.
for srv in servers:
srv.node.wait_for_unit(f"armagetronad-{srv.name}")
srv.node.wait_until_succeeds(f"ss --numeric --udp --listening | grep -q {srv.port}")
@@ -167,55 +214,7 @@ in
f"journalctl -u armagetronad-{srv.name} -e | grep -q 'Admin: Testing again!'"
)
- """
- Sets up a client, waiting for the given barrier on completion.
- """
- def client_setup(client, servers, barrier):
- client.node.wait_for_x()
-
- # Configure Armagetron.
- client.node.succeed(
- run("mkdir -p ~/.armagetronad/var"),
- run(f"echo 'PLAYER_1 {client.name}' >> ~/.armagetronad/var/autoexec.cfg")
- )
- for idx, srv in enumerate(servers):
- client.node.succeed(
- run(f"echo 'BOOKMARK_{idx+1}_ADDRESS {srv.address}' >> ~/.armagetronad/var/autoexec.cfg"),
- run(f"echo 'BOOKMARK_{idx+1}_NAME {srv.name}' >> ~/.armagetronad/var/autoexec.cfg"),
- run(f"echo 'BOOKMARK_{idx+1}_PORT {srv.port}' >> ~/.armagetronad/var/autoexec.cfg")
- )
-
- # Start Armagetron.
- client.node.succeed(run("ulimit -c unlimited; armagetronad >&2 & disown"))
- client.node.wait_until_succeeds(
- run(
- "${xdo "create_new_win-select_main_window" ''
- search --onlyvisible --name "Armagetron Advanced"
- windowfocus --sync
- windowactivate --sync
- ''}"
- )
- )
-
- # Get through the tutorial.
- client.send_on('Language Settings', 'ret')
- client.send_on('First Setup', 'ret')
- client.send_on('Welcome to Armagetron Advanced', 'ret')
- client.send_on('round 1', 'esc')
- client.send_on('Menu', 'up', 'up', 'ret')
- client.send_on('We hope you', 'ret')
- client.send_on('Armagetron Advanced', 'ret')
- client.send_on('Play Game', 'ret')
-
- # Online > LAN > Network Setup > Mates > Server Bookmarks
- client.send_on('Multiplayer', 'down', 'down', 'down', 'down', 'ret')
-
- barrier.wait()
-
- # Get to the Server Bookmarks screen on both clients. This takes a while so do it asynchronously.
- barrier = threading.Barrier(len(clients) + 1, timeout=240)
- for client in clients:
- threading.Thread(target=client_setup, args=(client, servers, barrier)).start()
+ # Wait for the client setup to complete.
barrier.wait()
# Main testing loop. Iterates through each server bookmark and connects to them in sequence.
@@ -245,18 +244,14 @@ in
f"journalctl -u armagetronad-{srv.name} -e | grep -q 'Go (round 1 of 10)'"
)
- # Wait a bit
- srv.node.sleep(srv.coredump_delay)
-
- # Turn the attacker player's lightcycle left
- attacker = next(client for client in clients if client.name == srv.attacker)
- victim = next(client for client in clients if client.name == srv.victim)
- attacker.send('left')
- screenshot_idx = take_screenshots(screenshot_idx)
-
- # Wait for coredump.
+ # Wait for the players to die by running into the wall.
+ player1 = next(client for client in clients if client.name == srv.player1)
+ player2 = next(client for client in clients if client.name == srv.player2)
srv.node.wait_until_succeeds(
- f"journalctl -u armagetronad-{srv.name} -e | grep -q '{attacker.name} core dumped {victim.name}'"
+ f"journalctl -u armagetronad-{srv.name} -e | grep -q '{player1.name}.*lost 4 points'"
+ )
+ srv.node.wait_until_succeeds(
+ f"journalctl -u armagetronad-{srv.name} -e | grep -q '{player2.name}.*lost 4 points'"
)
screenshot_idx = take_screenshots(screenshot_idx)
diff --git a/nixos/tests/atop.nix b/nixos/tests/atop.nix
index d2372c80c3f9..376c44f9bd88 100644
--- a/nixos/tests/atop.nix
+++ b/nixos/tests/atop.nix
@@ -1,12 +1,9 @@
{
- system ? builtins.currentSystem,
- config ? { },
- pkgs ? import ../.. { inherit system config; },
+ pkgs,
+ runTest,
+ ...
}:
-with import ../lib/testing-python.nix { inherit system pkgs; };
-with pkgs.lib;
-
let
assertions = rec {
path = program: path: ''
@@ -129,7 +126,7 @@ let
};
in
{
- justThePackage = makeTest {
+ justThePackage = runTest {
name = "atop-justThePackage";
nodes.machine = {
environment.systemPackages = [ pkgs.atop ];
@@ -148,7 +145,7 @@ in
];
inherit meta;
};
- defaults = makeTest {
+ defaults = runTest {
name = "atop-defaults";
nodes.machine = {
programs.atop = {
@@ -169,7 +166,7 @@ in
];
inherit meta;
};
- minimal = makeTest {
+ minimal = runTest {
name = "atop-minimal";
nodes.machine = {
programs.atop = {
@@ -193,7 +190,7 @@ in
];
inherit meta;
};
- netatop = makeTest {
+ netatop = runTest {
name = "atop-netatop";
nodes.machine = {
programs.atop = {
@@ -215,7 +212,7 @@ in
];
inherit meta;
};
- atopgpu = makeTest {
+ atopgpu = runTest {
name = "atop-atopgpu";
nodes.machine = {
programs.atop = {
@@ -237,7 +234,7 @@ in
];
inherit meta;
};
- everything = makeTest {
+ everything = runTest {
name = "atop-everything";
nodes.machine = {
programs.atop = {
diff --git a/nixos/tests/ax25.nix b/nixos/tests/ax25.nix
new file mode 100644
index 000000000000..f1092d5de101
--- /dev/null
+++ b/nixos/tests/ax25.nix
@@ -0,0 +1,131 @@
+import ./make-test-python.nix (
+ { pkgs, lib, ... }:
+ let
+
+ baud = 57600;
+ tty = "/dev/ttyACM0";
+ port = "tnc0";
+ socatPort = 1234;
+
+ createAX25Node = nodeId: {
+
+ boot.kernelPackages = pkgs.linuxPackages_ham;
+ boot.kernelModules = [ "ax25" ];
+
+ networking.firewall.allowedTCPPorts = [ socatPort ];
+
+ environment.systemPackages = with pkgs; [
+ libax25
+ ax25-tools
+ ax25-apps
+ socat
+ ];
+
+ services.ax25.axports."${port}" = {
+ inherit baud tty;
+ enable = true;
+ callsign = "NOCALL-${toString nodeId}";
+ description = "mocked tnc";
+ };
+
+ services.ax25.axlisten = {
+ enable = true;
+ };
+
+ # All mocks radios will connect back to socat-broker on node 1 in order to get
+ # all messages that are "broadcasted over the ether"
+ systemd.services.ax25-mock-hardware = {
+ description = "mock AX.25 TNC and Radio";
+ wantedBy = [ "default.target" ];
+ before = [
+ "ax25-kissattach-${port}.service"
+ "axlisten.service"
+ ];
+ after = [ "network.target" ];
+ serviceConfig = {
+ Type = "exec";
+ ExecStart = "${pkgs.socat}/bin/socat -d -d tcp:192.168.1.1:${toString socatPort} pty,link=${tty},b${toString baud},raw";
+ };
+ };
+ };
+ in
+ {
+ name = "ax25Simple";
+ nodes = {
+ node1 = lib.mkMerge [
+ (createAX25Node 1)
+ # mimicking radios on the same frequency
+ {
+ systemd.services.ax25-mock-ether = {
+ description = "mock radio ether";
+ wantedBy = [ "default.target" ];
+ requires = [ "network.target" ];
+ before = [ "ax25-mock-hardware.service" ];
+ # broken needs access to "ss" or "netstat"
+ path = [ pkgs.iproute2 ];
+ serviceConfig = {
+ Type = "exec";
+ ExecStart = "${pkgs.socat}/bin/socat-broker.sh tcp4-listen:${toString socatPort}";
+ };
+ postStart = "${pkgs.coreutils}/bin/sleep 2";
+ };
+ }
+ ];
+ node2 = createAX25Node 2;
+ node3 = createAX25Node 3;
+ };
+ testScript =
+ { ... }:
+ ''
+ def wait_for_machine(m):
+ m.succeed("lsmod | grep ax25")
+ m.wait_for_unit("ax25-axports.target")
+ m.wait_for_unit("axlisten.service")
+ m.fail("journalctl -o cat -u axlisten.service | grep -i \"no AX.25 port data configured\"")
+
+ # start the first node since the socat-broker needs to be running
+ node1.start()
+ node1.wait_for_unit("ax25-mock-ether.service")
+ wait_for_machine(node1)
+
+ node2.start()
+ node3.start()
+ wait_for_machine(node2)
+ wait_for_machine(node3)
+
+ # Node 1 -> Node 2
+ node1.succeed("echo hello | ax25_call ${port} NOCALL-1 NOCALL-2")
+ node2.sleep(1)
+ node2.succeed("journalctl -o cat -u axlisten.service | grep -A1 \"NOCALL-1 to NOCALL-2 ctl I00\" | grep hello")
+
+ # Node 1 -> Node 3
+ node1.succeed("echo hello | ax25_call ${port} NOCALL-1 NOCALL-3")
+ node3.sleep(1)
+ node3.succeed("journalctl -o cat -u axlisten.service | grep -A1 \"NOCALL-1 to NOCALL-3 ctl I00\" | grep hello")
+
+ # Node 2 -> Node 1
+ # must sleep due to previous ax25_call lingering
+ node2.sleep(5)
+ node2.succeed("echo hello | ax25_call ${port} NOCALL-2 NOCALL-1")
+ node1.sleep(1)
+ node1.succeed("journalctl -o cat -u axlisten.service | grep -A1 \"NOCALL-2 to NOCALL-1 ctl I00\" | grep hello")
+
+ # Node 2 -> Node 3
+ node2.succeed("echo hello | ax25_call ${port} NOCALL-2 NOCALL-3")
+ node3.sleep(1)
+ node3.succeed("journalctl -o cat -u axlisten.service | grep -A1 \"NOCALL-2 to NOCALL-3 ctl I00\" | grep hello")
+
+ # Node 3 -> Node 1
+ # must sleep due to previous ax25_call lingering
+ node3.sleep(5)
+ node3.succeed("echo hello | ax25_call ${port} NOCALL-3 NOCALL-1")
+ node1.sleep(1)
+ node1.succeed("journalctl -o cat -u axlisten.service | grep -A1 \"NOCALL-3 to NOCALL-1 ctl I00\" | grep hello")
+
+ # Node 3 -> Node 2
+ node3.succeed("echo hello | ax25_call ${port} NOCALL-3 NOCALL-2")
+ node2.sleep(1)
+ node2.succeed("journalctl -o cat -u axlisten.service | grep -A1 \"NOCALL-3 to NOCALL-2 ctl I00\" | grep hello")
+ '';
+ }
+)
diff --git a/nixos/tests/bind.nix b/nixos/tests/bind.nix
index 5eb75392a382..3b2edec9d19e 100644
--- a/nixos/tests/bind.nix
+++ b/nixos/tests/bind.nix
@@ -1,4 +1,5 @@
-import ./make-test-python.nix {
+{ ... }:
+{
name = "bind";
nodes.machine =
diff --git a/nixos/tests/bitbox-bridge.nix b/nixos/tests/bitbox-bridge.nix
index 0fe4ea94acb9..ab1140e2511c 100644
--- a/nixos/tests/bitbox-bridge.nix
+++ b/nixos/tests/bitbox-bridge.nix
@@ -5,13 +5,10 @@ let
in
{
name = "bitbox-bridge";
- meta = {
- platforms = lib.platforms.linux;
- maintainers = with lib.maintainers; [
- izelnakri
- tensor5
- ];
- };
+ meta.maintainers = with lib.maintainers; [
+ izelnakri
+ tensor5
+ ];
nodes.machine = {
services.bitbox-bridge = {
diff --git a/nixos/tests/boot.nix b/nixos/tests/boot.nix
index 11e88bd3d891..e15797765462 100644
--- a/nixos/tests/boot.nix
+++ b/nixos/tests/boot.nix
@@ -65,20 +65,24 @@ let
iso =
(import ../lib/eval-config.nix {
- inherit system;
+ system = null;
modules = [
../modules/installer/cd-dvd/installation-cd-minimal.nix
../modules/testing/test-instrumentation.nix
+ { nixpkgs.pkgs = pkgs; }
];
}).config.system.build.isoImage;
sd =
(import ../lib/eval-config.nix {
- inherit system;
+ system = null;
modules = [
../modules/installer/sd-card/sd-image-x86_64.nix
../modules/testing/test-instrumentation.nix
- { sdImage.compressImage = false; }
+ {
+ sdImage.compressImage = false;
+ nixpkgs.pkgs = pkgs;
+ }
];
}).config.system.build.sdImage;
@@ -109,7 +113,7 @@ let
let
config =
(import ../lib/eval-config.nix {
- inherit system;
+ system = null;
modules = [
../modules/installer/netboot/netboot.nix
../modules/testing/test-instrumentation.nix
@@ -118,6 +122,8 @@ let
"serial"
"live.nixos.passwordHash=$6$jnwR50SkbLYEq/Vp$wmggwioAkfmwuYqd5hIfatZWS/bO6hewzNIwIrWcgdh7k/fhUzZT29Vil3ioMo94sdji/nipbzwEpxecLZw0d0" # "password"
];
+
+ nixpkgs.pkgs = pkgs;
}
{
key = "serial";
diff --git a/nixos/tests/caddy.nix b/nixos/tests/caddy.nix
index b7cf6ff10cc9..357ebe77f060 100644
--- a/nixos/tests/caddy.nix
+++ b/nixos/tests/caddy.nix
@@ -74,7 +74,7 @@
services.caddy = {
package = pkgs.caddy.withPlugins {
plugins = [ "github.com/caddyserver/replace-response@v0.0.0-20241211194404-3865845790a7" ];
- hash = "sha256-WPmJPnyOrAnuJxvn3ywswqvLGV8SZzzn3gU1Tbtpao4=";
+ hash = "sha256-BJ+//h/bkj6y2Zhxas8oJyrryiTDR2Qpz7+VloqrbwQ=";
};
configFile = pkgs.writeText "Caddyfile" ''
{
diff --git a/nixos/tests/calibre-server.nix b/nixos/tests/calibre-server.nix
index 2a04dd8cfba4..059058778526 100644
--- a/nixos/tests/calibre-server.nix
+++ b/nixos/tests/calibre-server.nix
@@ -1,11 +1,10 @@
{
- system ? builtins.currentSystem,
- config ? { },
- pkgs ? import ../.. { inherit system config; },
+ pkgs,
+ runTest,
+ ...
}:
let
- inherit (import ../lib/testing-python.nix { inherit system pkgs; }) makeTest;
inherit (pkgs.lib)
concatStringsSep
maintainers
@@ -74,7 +73,7 @@ let
in
mapAttrs (
test: testConfig:
- (makeTest (
+ (runTest (
let
nodeName = testConfig.nodeName or test;
calibreConfig = {
diff --git a/nixos/tests/calibre-web.nix b/nixos/tests/calibre-web.nix
index d14f1b540809..cf66ed1f1921 100644
--- a/nixos/tests/calibre-web.nix
+++ b/nixos/tests/calibre-web.nix
@@ -1,44 +1,42 @@
-import ./make-test-python.nix (
- { pkgs, lib, ... }:
+{ lib, ... }:
- let
- port = 3142;
- defaultPort = 8083;
- in
- {
- name = "calibre-web";
- meta.maintainers = with lib.maintainers; [ pborzenkov ];
+let
+ port = 3142;
+ defaultPort = 8083;
+in
+{
+ name = "calibre-web";
+ meta.maintainers = with lib.maintainers; [ pborzenkov ];
- nodes = {
- customized =
- { pkgs, ... }:
- {
- services.calibre-web = {
- enable = true;
- listen.port = port;
- options = {
- calibreLibrary = "/tmp/books";
- reverseProxyAuth = {
- enable = true;
- header = "X-User";
- };
+ nodes = {
+ customized =
+ { pkgs, ... }:
+ {
+ services.calibre-web = {
+ enable = true;
+ listen.port = port;
+ options = {
+ calibreLibrary = "/tmp/books";
+ reverseProxyAuth = {
+ enable = true;
+ header = "X-User";
};
};
- environment.systemPackages = [ pkgs.calibre ];
};
- };
- testScript = ''
- start_all()
+ environment.systemPackages = [ pkgs.calibre ];
+ };
+ };
+ testScript = ''
+ start_all()
- customized.succeed(
- "mkdir /tmp/books && calibredb --library-path /tmp/books add -e --title test-book"
- )
- customized.succeed("systemctl restart calibre-web")
- customized.wait_for_unit("calibre-web.service")
- customized.wait_for_open_port(${toString port})
- customized.succeed(
- "curl --fail -H X-User:admin 'http://localhost:${toString port}' | grep test-book"
- )
- '';
- }
-)
+ customized.succeed(
+ "mkdir /tmp/books && calibredb --library-path /tmp/books add -e --title test-book"
+ )
+ customized.succeed("systemctl restart calibre-web")
+ customized.wait_for_unit("calibre-web.service")
+ customized.wait_for_open_port(${toString port})
+ customized.succeed(
+ "curl --fail -H X-User:admin 'http://localhost:${toString port}' | grep test-book"
+ )
+ '';
+}
diff --git a/nixos/tests/canaille.nix b/nixos/tests/canaille.nix
index a085f695fd45..58e81e058de0 100644
--- a/nixos/tests/canaille.nix
+++ b/nixos/tests/canaille.nix
@@ -56,7 +56,7 @@ import ./make-test-python.nix (
server.succeed("sudo -iu canaille -- canaille create user --user-name admin --password adminpass --emails admin@${domain}")
json_str = server.succeed("sudo -iu canaille -- canaille get user")
assert json.loads(json_str)[0]["user_name"] == "admin"
- server.succeed("sudo -iu canaille -- canaille check")
+ server.succeed("sudo -iu canaille -- canaille config check")
'';
}
)
diff --git a/nixos/tests/centrifugo.nix b/nixos/tests/centrifugo.nix
index 8ad44a011663..5867297c3d30 100644
--- a/nixos/tests/centrifugo.nix
+++ b/nixos/tests/centrifugo.nix
@@ -10,7 +10,10 @@ in
{ lib, ... }:
{
name = "centrifugo";
- meta.maintainers = [ lib.maintainers.tie ];
+ meta.maintainers = [
+ lib.maintainers.tie
+ lib.maintainers.valodim
+ ];
nodes = lib.listToAttrs (
lib.imap0 (index: name: {
@@ -21,12 +24,15 @@ in
services.centrifugo = {
enable = true;
settings = {
- inherit name;
- port = centrifugoPort;
- # See https://centrifugal.dev/docs/server/engines#redis-sharding
- engine = "redis";
- # Connect to local Redis shard via Unix socket.
- redis_address =
+ node = {
+ inherit name;
+ };
+ http_server.port = centrifugoPort;
+ http_api.insecure = true;
+ usage_stats.disabled = true;
+
+ engine.type = "redis";
+ engine.redis.address =
let
toRedisAddresses = map (name: "${name}:${toString redisPort}");
in
@@ -35,8 +41,6 @@ in
"unix://${config.services.redis.servers.centrifugo.unixSocket}"
]
++ toRedisAddresses (lib.drop (index + 1) nodes);
- usage_stats_disable = true;
- api_insecure = true;
};
extraGroups = [
config.services.redis.servers.centrifugo.user
diff --git a/nixos/tests/cgit.nix b/nixos/tests/cgit.nix
index fce3dc16b854..2c48e6bde47d 100644
--- a/nixos/tests/cgit.nix
+++ b/nixos/tests/cgit.nix
@@ -1,113 +1,111 @@
-import ./make-test-python.nix (
- { pkgs, ... }:
- let
- robotsTxt = pkgs.writeText "cgit-robots.txt" ''
- User-agent: *
- Disallow: /
- '';
- in
- {
- name = "cgit";
- meta = with pkgs.lib.maintainers; {
- maintainers = [ schnusch ];
- };
+{ pkgs, ... }:
+let
+ robotsTxt = pkgs.writeText "cgit-robots.txt" ''
+ User-agent: *
+ Disallow: /
+ '';
+in
+{
+ name = "cgit";
+ meta = with pkgs.lib.maintainers; {
+ maintainers = [ schnusch ];
+ };
- nodes = {
- server =
- { ... }:
- {
- services.cgit."localhost" = {
- enable = true;
- package = pkgs.cgit.overrideAttrs (
- { postInstall, ... }:
- {
- postInstall = ''
- ${postInstall}
- cp ${robotsTxt} "$out/cgit/robots.txt"
- '';
- }
- );
- nginx.location = "/(c)git/";
- repos = {
- some-repo = {
- path = "/tmp/git/some-repo";
- desc = "some-repo description";
- };
- };
- settings = {
- readme = [
- ":README.md"
- ":date.txt"
- ];
+ nodes = {
+ server =
+ { ... }:
+ {
+ services.cgit."localhost" = {
+ enable = true;
+ package = pkgs.cgit.overrideAttrs (
+ { postInstall, ... }:
+ {
+ postInstall = ''
+ ${postInstall}
+ cp ${robotsTxt} "$out/cgit/robots.txt"
+ '';
+ }
+ );
+ nginx.location = "/(c)git/";
+ repos = {
+ some-repo = {
+ path = "/tmp/git/some-repo";
+ desc = "some-repo description";
};
};
-
- environment.systemPackages = [ pkgs.git ];
+ settings = {
+ readme = [
+ ":README.md"
+ ":date.txt"
+ ];
+ };
};
- };
- testScript =
- { nodes, ... }:
- ''
- start_all()
+ environment.systemPackages = [ pkgs.git ];
+ };
+ };
- server.wait_for_unit("nginx.service")
- server.wait_for_unit("network.target")
- server.wait_for_open_port(80)
+ testScript =
+ { nodes, ... }:
+ ''
+ start_all()
- server.succeed("curl -fsS http://localhost/%28c%29git/cgit.css")
+ server.wait_for_unit("nginx.service")
+ server.wait_for_unit("network.target")
+ server.wait_for_open_port(80)
- server.succeed("curl -fsS http://localhost/%28c%29git/robots.txt | diff -u - ${robotsTxt}")
+ server.succeed("curl -fsS http://localhost/%28c%29git/cgit.css")
- server.succeed(
- "curl -fsS http://localhost/%28c%29git/ | grep -F 'some-repo description'"
- )
+ server.succeed("curl -fsS http://localhost/%28c%29git/robots.txt | diff -u - ${robotsTxt}")
- server.fail("curl -fsS http://localhost/robots.txt")
+ server.succeed(
+ "curl -fsS http://localhost/%28c%29git/ | grep -F 'some-repo description'"
+ )
- server.succeed("sudo -u cgit ${pkgs.writeShellScript "setup-cgit-test-repo" ''
- set -e
- git init --bare -b master /tmp/git/some-repo
- git init -b master reference
- cd reference
- git remote add origin /tmp/git/some-repo
- { echo -n "cgit NixOS Test at "; date; } > date.txt
- git add date.txt
- git -c user.name=test -c user.email=test@localhost commit -m 'add date'
- git push -u origin master
- ''}")
+ server.fail("curl -fsS http://localhost/robots.txt")
- # test web download
- server.succeed(
- "curl -fsS 'http://localhost/%28c%29git/some-repo/plain/date.txt?id=master' | diff -u reference/date.txt -"
- )
+ server.succeed("sudo -u cgit ${pkgs.writeShellScript "setup-cgit-test-repo" ''
+ set -e
+ git init --bare -b master /tmp/git/some-repo
+ git init -b master reference
+ cd reference
+ git remote add origin /tmp/git/some-repo
+ { echo -n "cgit NixOS Test at "; date; } > date.txt
+ git add date.txt
+ git -c user.name=test -c user.email=test@localhost commit -m 'add date'
+ git push -u origin master
+ ''}")
- # test http clone
- server.succeed(
- "git clone http://localhost/%28c%29git/some-repo && diff -u reference/date.txt some-repo/date.txt"
- )
+ # test web download
+ server.succeed(
+ "curl -fsS 'http://localhost/%28c%29git/some-repo/plain/date.txt?id=master' | diff -u reference/date.txt -"
+ )
- # test list settings by greping for the fallback readme
- server.succeed(
- "curl -fsS 'http://localhost/%28c%29git/some-repo/about/' | grep -F 'cgit NixOS Test at'"
- )
+ # test http clone
+ server.succeed(
+ "git clone http://localhost/%28c%29git/some-repo && diff -u reference/date.txt some-repo/date.txt"
+ )
- # add real readme
- server.succeed("sudo -u cgit ${pkgs.writeShellScript "cgit-commit-readme" ''
- set -e
- echo '# cgit NixOS test README' > reference/README.md
- git -C reference add README.md
- git -C reference -c user.name=test -c user.email=test@localhost commit -m 'add readme'
- git -C reference push
- ''}")
+ # test list settings by greping for the fallback readme
+ server.succeed(
+ "curl -fsS 'http://localhost/%28c%29git/some-repo/about/' | grep -F 'cgit NixOS Test at'"
+ )
- # test list settings by greping for the real readme
- server.succeed(
- "curl -fsS 'http://localhost/%28c%29git/some-repo/about/' | grep -F '# cgit NixOS test README'"
- )
- server.fail(
- "curl -fsS 'http://localhost/%28c%29git/some-repo/about/' | grep -F 'cgit NixOS Test at'"
- )
- '';
- }
-)
+ # add real readme
+ server.succeed("sudo -u cgit ${pkgs.writeShellScript "cgit-commit-readme" ''
+ set -e
+ echo '# cgit NixOS test README' > reference/README.md
+ git -C reference add README.md
+ git -C reference -c user.name=test -c user.email=test@localhost commit -m 'add readme'
+ git -C reference push
+ ''}")
+
+ # test list settings by greping for the real readme
+ server.succeed(
+ "curl -fsS 'http://localhost/%28c%29git/some-repo/about/' | grep -F '# cgit NixOS test README'"
+ )
+ server.fail(
+ "curl -fsS 'http://localhost/%28c%29git/some-repo/about/' | grep -F 'cgit NixOS Test at'"
+ )
+ '';
+}
diff --git a/nixos/tests/cockpit.nix b/nixos/tests/cockpit.nix
index 0433cebf3b56..29692f10aad2 100644
--- a/nixos/tests/cockpit.nix
+++ b/nixos/tests/cockpit.nix
@@ -23,11 +23,9 @@ import ./make-test-python.nix (
enable = true;
port = 7890;
openFirewall = true;
- settings = {
- WebService = {
- Origins = "https://server:7890";
- };
- };
+ allowed-origins = [
+ "https://server:${toString config.services.cockpit.port}"
+ ];
};
};
client =
@@ -123,10 +121,14 @@ import ./make-test-python.nix (
assert "Web console is running in limited access mode" in driver.page_source
log("Clicking the sudo button")
+ for button in driver.find_elements(By.TAG_NAME, "button"):
+ if 'admin' in button.text:
+ button.click()
driver.switch_to.default_content()
- driver.find_element(By.CSS_SELECTOR, 'button.ct-locked').click()
+
log("Checking that /nonexistent is not a thing")
assert '/nonexistent' not in driver.page_source
+ assert len(driver.find_elements(By.CSS_SELECTOR, '#machine-reconnect')) == 0
driver.close()
'';
diff --git a/nixos/tests/common/x11.nix b/nixos/tests/common/x11.nix
index b79cedb864de..3d4b2fb71105 100644
--- a/nixos/tests/common/x11.nix
+++ b/nixos/tests/common/x11.nix
@@ -14,4 +14,9 @@
# Don't use a desktop manager.
services.displayManager.defaultSession = lib.mkDefault "none+icewm";
services.xserver.windowManager.icewm.enable = true;
+
+ # Help with OCR
+ environment.etc."icewm/theme".text = ''
+ Theme="gtk2/default.theme"
+ '';
}
diff --git a/nixos/tests/cosmic.nix b/nixos/tests/cosmic.nix
new file mode 100644
index 000000000000..00b1f478d81e
--- /dev/null
+++ b/nixos/tests/cosmic.nix
@@ -0,0 +1,126 @@
+{
+ config,
+ lib,
+ testName,
+ enableAutologin,
+ enableXWayland,
+ ...
+}:
+
+{
+ name = testName;
+
+ meta.maintainers = lib.teams.cosmic.members;
+
+ nodes.machine = {
+ imports = [ ./common/user-account.nix ];
+
+ services = {
+ # For `cosmic-store` to be added to `environment.systemPackages`
+ # and for it to work correctly because Flatpak is a runtime
+ # dependency of `cosmic-store`.
+ flatpak.enable = true;
+
+ displayManager.cosmic-greeter.enable = true;
+ desktopManager.cosmic = {
+ enable = true;
+ xwayland.enable = enableXWayland;
+ };
+ };
+
+ services.displayManager.autoLogin = lib.mkIf enableAutologin {
+ enable = true;
+ user = "alice";
+ };
+
+ environment.systemPackages = with config.node.pkgs; [
+ # These two packages are used to check if a window was opened
+ # under the COSMIC session or not. Kinda important.
+ # TODO: Move the check from the test module to
+ # `nixos/lib/test-driver/src/test_driver/machine.py` so more
+ # Wayland-only testing can be done using the existing testing
+ # infrastructure.
+ jq
+ lswt
+ ];
+
+ # So far, all COSMIC tests launch a few GUI applications. In doing
+ # so, the default allocated memory to the guest of 1024M quickly
+ # poses a very high risk of an OOM-shutdown which is worse than an
+ # OOM-kill. Because now, the test failed, but not for a genuine
+ # reason, but an OOM-shutdown. That's an inconclusive failure
+ # which might possibly mask an actual failure. Not enabling
+ # systemd-oomd because we need said applications running for a
+ # few seconds. So instead, bump the allocated memory to the guest
+ # from 1024M to 4x; 4096M.
+ virtualisation.memorySize = 4096;
+ };
+
+ testScript =
+ { nodes, ... }:
+ let
+ cfg = nodes.machine;
+ user = cfg.users.users.alice;
+ DISPLAY = lib.strings.optionalString enableXWayland (
+ if enableAutologin then "DISPLAY=:0" else "DISPLAY=:1"
+ );
+ in
+ ''
+ #testName: ${testName}
+ ''
+ + (
+ if (enableAutologin) then
+ ''
+ with subtest("cosmic-greeter initialisation"):
+ machine.wait_for_unit("graphical.target", timeout=120)
+ ''
+ else
+ ''
+ from time import sleep
+
+ machine.wait_for_unit("graphical.target", timeout=120)
+ machine.wait_until_succeeds("pgrep --uid ${toString cfg.users.users.cosmic-greeter.name} --full cosmic-greeter", timeout=30)
+ # Sleep for 10 seconds for ensuring that `greetd` loads the
+ # password prompt for the login screen properly.
+ sleep(10)
+
+ with subtest("cosmic-session login"):
+ machine.send_chars("${user.password}\n", delay=0.2)
+ ''
+ )
+ + ''
+ # _One_ of the final processes to start as part of the
+ # `cosmic-session` target is the Workspaces applet. So, wait
+ # for it to start. The process existing means that COSMIC
+ # now handles any opened windows from now on.
+ machine.wait_until_succeeds("pgrep --uid ${toString user.uid} --full 'cosmic-panel-button com.system76.CosmicWorkspaces'", timeout=30)
+
+ # The best way to test for Wayland and XWayland is to launch
+ # the GUI applications and see the results yourself.
+ with subtest("Launch applications"):
+ # key: binary_name
+ # value: "app-id" as reported by `lswt`
+ gui_apps_to_launch = {}
+
+ # We want to ensure that the first-party applications
+ # start/launch properly.
+ gui_apps_to_launch['cosmic-edit'] = 'com.system76.CosmicEdit'
+ gui_apps_to_launch['cosmic-files'] = 'com.system76.CosmicFiles'
+ gui_apps_to_launch['cosmic-player'] = 'com.system76.CosmicPlayer'
+ gui_apps_to_launch['cosmic-settings'] = 'com.system76.CosmicSettings'
+ gui_apps_to_launch['cosmic-store'] = 'com.system76.CosmicStore'
+ gui_apps_to_launch['cosmic-term'] = 'com.system76.CosmicTerm'
+
+ for gui_app, app_id in gui_apps_to_launch.items():
+ machine.succeed(f"su - ${user.name} -c 'WAYLAND_DISPLAY=wayland-1 XDG_RUNTIME_DIR=/run/user/${toString user.uid} ${DISPLAY} {gui_app} >&2 &'", timeout=5)
+ # Nix builds the following non-commented expression to the following:
+ # `su - alice -c 'WAYLAND_DISPLAY=wayland-1 XDG_RUNTIME_DIR=/run/user/1000 lswt --json | jq ".toplevels" | grep "^ \\"app-id\\": \\"{app_id}\\"$"' `
+ machine.wait_until_succeeds(f''''su - ${user.name} -c 'WAYLAND_DISPLAY=wayland-1 XDG_RUNTIME_DIR=/run/user/${toString user.uid} lswt --json | jq ".toplevels" | grep "^ \\"app-id\\": \\"{app_id}\\"$"' '''', timeout=30)
+ machine.succeed(f"pkill {gui_app}", timeout=5)
+
+ machine.succeed("echo 'test completed succeessfully' > /${testName}", timeout=5)
+ machine.copy_from_vm('/${testName}')
+
+ machine.shutdown()
+ '';
+}
diff --git a/nixos/tests/cups-pdf.nix b/nixos/tests/cups-pdf.nix
index 917c3d8d4a8c..00623b14a9db 100644
--- a/nixos/tests/cups-pdf.nix
+++ b/nixos/tests/cups-pdf.nix
@@ -1,47 +1,45 @@
-import ./make-test-python.nix (
- { lib, pkgs, ... }:
- {
- name = "cups-pdf";
+{ hostPkgs, lib, ... }:
+{
+ name = "cups-pdf";
- nodes.machine =
- { pkgs, ... }:
- {
- imports = [ ./common/user-account.nix ];
- environment.systemPackages = [ pkgs.poppler-utils ];
- fonts.packages = [ pkgs.dejavu_fonts ]; # yields more OCR-able pdf
- services.printing.cups-pdf.enable = true;
- services.printing.cups-pdf.instances = {
- opt = { };
- noopt.installPrinter = false;
- };
- hardware.printers.ensurePrinters = [
- {
- name = "noopt";
- model = "CUPS-PDF_noopt.ppd";
- deviceUri = "cups-pdf:/noopt";
- }
- ];
+ nodes.machine =
+ { pkgs, ... }:
+ {
+ imports = [ ./common/user-account.nix ];
+ environment.systemPackages = [ pkgs.poppler-utils ];
+ fonts.packages = [ pkgs.dejavu_fonts ]; # yields more OCR-able pdf
+ services.printing.cups-pdf.enable = true;
+ services.printing.cups-pdf.instances = {
+ opt = { };
+ noopt.installPrinter = false;
};
+ hardware.printers.ensurePrinters = [
+ {
+ name = "noopt";
+ model = "CUPS-PDF_noopt.ppd";
+ deviceUri = "cups-pdf:/noopt";
+ }
+ ];
+ };
- # we cannot check the files with pdftotext, due to
- # https://github.com/alexivkin/CUPS-PDF-to-PDF/issues/7
- # we need `imagemagickBig` as it has ghostscript support
+ # we cannot check the files with pdftotext, due to
+ # https://github.com/alexivkin/CUPS-PDF-to-PDF/issues/7
+ # we need `imagemagickBig` as it has ghostscript support
- testScript = ''
- from subprocess import run
- machine.wait_for_unit("multi-user.target")
- for name in ("opt", "noopt"):
- text = f"test text {name}".upper()
- machine.wait_until_succeeds(f"lpstat -v {name}")
- machine.succeed(f"su - alice -c 'echo -e \"\n {text}\" | lp -d {name}'")
- # wait until the pdf files are completely produced and readable by alice
- machine.wait_until_succeeds(f"su - alice -c 'pdfinfo /var/spool/cups-pdf-{name}/users/alice/*.pdf'")
- machine.succeed(f"cp /var/spool/cups-pdf-{name}/users/alice/*.pdf /tmp/{name}.pdf")
- machine.copy_from_vm(f"/tmp/{name}.pdf", "")
- run(f"${pkgs.imagemagickBig}/bin/convert -density 300 $out/{name}.pdf $out/{name}.jpeg", shell=True, check=True)
- assert text.encode() in run(f"${lib.getExe pkgs.tesseract} $out/{name}.jpeg stdout", shell=True, check=True, capture_output=True).stdout
- '';
+ testScript = ''
+ from subprocess import run
+ machine.wait_for_unit("multi-user.target")
+ for name in ("opt", "noopt"):
+ text = f"test text {name}".upper()
+ machine.wait_until_succeeds(f"lpstat -v {name}")
+ machine.succeed(f"su - alice -c 'echo -e \"\n {text}\" | lp -d {name}'")
+ # wait until the pdf files are completely produced and readable by alice
+ machine.wait_until_succeeds(f"su - alice -c 'pdfinfo /var/spool/cups-pdf-{name}/users/alice/*.pdf'")
+ machine.succeed(f"cp /var/spool/cups-pdf-{name}/users/alice/*.pdf /tmp/{name}.pdf")
+ machine.copy_from_vm(f"/tmp/{name}.pdf", "")
+ run(f"${lib.getExe hostPkgs.imagemagickBig} -density 300 $out/{name}.pdf $out/{name}.jpeg", shell=True, check=True)
+ assert text.encode() in run(f"${lib.getExe hostPkgs.tesseract} $out/{name}.jpeg stdout", shell=True, check=True, capture_output=True).stdout
+ '';
- meta.maintainers = [ lib.maintainers.yarny ];
- }
-)
+ meta.maintainers = [ lib.maintainers.yarny ];
+}
diff --git a/nixos/tests/curl-impersonate.nix b/nixos/tests/curl-impersonate.nix
index 0846caeec6cb..28e741a2e19f 100644
--- a/nixos/tests/curl-impersonate.nix
+++ b/nixos/tests/curl-impersonate.nix
@@ -19,6 +19,9 @@
We do that by creating a trusted CA and issuing a cert that includes
all of the test domains as subject-alternative names and then spoofs the
hostnames in /etc/hosts.
+ - We started skipping the test_http2_headers test due to log format differences
+ between the nghttpd2 version in nixpkgs and the outdated one curl-impersonate
+ uses upstream for its tests.
*/
import ./make-test-python.nix (
@@ -125,7 +128,7 @@ import ./make-test-python.nix (
# Run tests
cd tests
- pytest . --install-dir ../usr --capture-interface eth1
+ pytest . --install-dir ../usr --capture-interface eth1 --exitfirst -k 'not test_http2_headers'
'';
in
{
diff --git a/nixos/tests/custom-ca.nix b/nixos/tests/custom-ca.nix
index b246f5b9ba57..e6aa31b5845a 100644
--- a/nixos/tests/custom-ca.nix
+++ b/nixos/tests/custom-ca.nix
@@ -1,6 +1,6 @@
# Checks that `security.pki` options are working in curl and the main browser
-# engines: Gecko (via Firefox), Chromium, QtWebEngine (via qutebrowser) and
-# WebKitGTK (via Midori). The test checks that certificates issued by a custom
+# engines: Gecko (via Firefox), Chromium, QtWebEngine (via qutebrowser).
+# The test checks that certificates issued by a custom
# trusted CA are accepted but those from an unknown CA are rejected.
{
@@ -210,8 +210,4 @@ in
args = "-T";
error = "Certificate error";
};
- midori = {
- args = "-p";
- error = "Security";
- };
}
diff --git a/nixos/tests/darling.nix b/nixos/tests/darling.nix
deleted file mode 100644
index 8514aac918ed..000000000000
--- a/nixos/tests/darling.nix
+++ /dev/null
@@ -1,52 +0,0 @@
-import ./make-test-python.nix (
- { pkgs, lib, ... }:
-
- let
- # Well, we _can_ cross-compile from Linux :)
- hello =
- pkgs.runCommand "hello"
- {
- sdk = "${pkgs.darling.sdk}/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk";
- nativeBuildInputs = with pkgs.llvmPackages_14; [
- clang-unwrapped
- lld
- ];
- src = pkgs.writeText "hello.c" ''
- #include
- int main() {
- printf("Hello, Darling!\n");
- return 0;
- }
- '';
- }
- ''
- clang \
- -target x86_64-apple-darwin \
- -fuse-ld=lld \
- -nostdinc -nostdlib \
- -mmacosx-version-min=10.15 \
- --sysroot $sdk \
- -isystem $sdk/usr/include \
- -L $sdk/usr/lib -lSystem \
- $src -o $out
- '';
- in
- {
- name = "darling";
-
- meta.maintainers = with lib.maintainers; [ zhaofengli ];
-
- nodes.machine = {
- programs.darling.enable = true;
- };
-
- testScript = ''
- start_all()
-
- # Darling holds stdout until the server is shutdown
- machine.succeed("darling ${hello} >hello.out")
- machine.succeed("grep Hello hello.out")
- machine.succeed("darling shutdown")
- '';
- }
-)
diff --git a/nixos/tests/davis.nix b/nixos/tests/davis.nix
index 480191704e2c..0dfa08342801 100644
--- a/nixos/tests/davis.nix
+++ b/nixos/tests/davis.nix
@@ -1,10 +1,14 @@
-{ pkgs, ... }:
+{
+ pkgs,
+ config,
+ ...
+}:
{
name = "davis";
meta.maintainers = pkgs.davis.meta.maintainers;
- nodes.machine =
+ nodes.machine1 =
{ config, ... }:
{
virtualisation = {
@@ -24,33 +28,67 @@
adminLogin = "admin";
appSecretFile = "${pkgs.writeText "davisAppSecret" "52882ef142066e09ab99ce816ba72522e789505caba224"}";
adminPasswordFile = "${pkgs.writeText "davisAdminPass" "nixos"}";
- nginx = { };
+ };
+ };
+ nodes.machine2 =
+ { nodes, config, ... }:
+ {
+ virtualisation = {
+ memorySize = 512;
+ };
+ environment.systemPackages = [ pkgs.fcgi ];
+
+ # no nginx, and no mail dsn
+ services.davis = {
+ enable = true;
+ hostname = "davis.example.com";
+ database = {
+ driver = "postgresql";
+ };
+ adminLogin = "admin";
+ appSecretFile = "${pkgs.writeText "davisAppSecret" "52882ef142066e09ab99ce816ba72522e789505caba224"}";
+ adminPasswordFile = "${pkgs.writeText "davisAdminPass" "nixos"}";
+ nginx = null;
};
};
testScript = ''
start_all()
- machine.wait_for_unit("postgresql.service")
- machine.wait_for_unit("davis-env-setup.service")
- machine.wait_for_unit("davis-db-migrate.service")
- machine.wait_for_unit("nginx.service")
- machine.wait_for_unit("phpfpm-davis.service")
+
+ machine1.wait_for_unit("postgresql.service")
+ machine1.wait_for_unit("davis-env-setup.service")
+ machine1.wait_for_unit("davis-db-migrate.service")
+ machine1.wait_for_unit("phpfpm-davis.service")
with subtest("welcome screen loads"):
- machine.succeed(
+ machine1.succeed(
"curl -sSfL --resolve davis.example.com:80:127.0.0.1 http://davis.example.com/ | grep 'Davis '"
)
with subtest("login works"):
- csrf_token = machine.succeed(
+ csrf_token = machine1.succeed(
"curl -c /tmp/cookies -sSfL --resolve davis.example.com:80:127.0.0.1 http://davis.example.com/login | grep '_csrf_token' | sed -E 's,.*value=\"(.*)\".*,\\1,g'"
)
- r = machine.succeed(
+ r = machine1.succeed(
f"curl -b /tmp/cookies --resolve davis.example.com:80:127.0.0.1 http://davis.example.com/login -X POST -F _username=admin -F _password=nixos -F _csrf_token={csrf_token.strip()} -D headers"
)
print(r)
- machine.succeed(
+ machine1.succeed(
"[[ $(grep -i 'location: ' headers | cut -d: -f2- | xargs echo) == /dashboard* ]]"
)
+ machine2.wait_for_unit("davis-env-setup.service")
+ machine2.wait_for_unit("davis-db-migrate.service")
+ machine2.wait_for_unit("phpfpm-davis.service")
+ r = machine2.succeed(
+ "find /var/lib/davis/var/log"
+ )
+ print(r)
+ env = (
+ "SCRIPT_NAME=/index.php",
+ "SCRIPT_FILENAME=${config.nodes.machine2.services.davis.package}/public/index.php",
+ "REMOTE_ADDR=127.0.0.1",
+ "REQUEST_METHOD=GET",
+ );
+ page = machine2.succeed(f"{' '.join(env)} ${pkgs.fcgi}/bin/cgi-fcgi -bind -connect ${config.nodes.machine2.services.phpfpm.pools.davis.socket}")
'';
}
diff --git a/nixos/tests/dependency-track.nix b/nixos/tests/dependency-track.nix
index 088ec8df82e3..baa55e779058 100644
--- a/nixos/tests/dependency-track.nix
+++ b/nixos/tests/dependency-track.nix
@@ -45,22 +45,27 @@ import ./make-test-python.nix (
};
};
- testScript = ''
- import json
+ testScript =
+ # python
+ ''
+ import json
- start_all()
+ start_all()
- server.wait_for_unit("dependency-track.service")
- server.wait_until_succeeds(
- "journalctl -o cat -u dependency-track.service | grep 'Dependency-Track is ready'"
- )
- server.wait_for_open_port(${toString dependencyTrackPort})
-
- with subtest("version api returns correct version"):
- version = json.loads(
- server.succeed("curl http://localhost/api/version")
+ server.wait_for_unit("dependency-track.service")
+ server.wait_until_succeeds(
+ "journalctl -o cat -u dependency-track.service | grep 'Dependency-Track is ready'"
)
- assert version["version"] == "${pkgs.dependency-track.version}"
- '';
+ server.wait_for_open_port(${toString dependencyTrackPort})
+
+ with subtest("version api returns correct version"):
+ version = json.loads(
+ server.succeed("curl http://localhost/api/version")
+ )
+ assert version["version"] == "${pkgs.dependency-track.version}"
+
+ with subtest("nginx serves frontend"):
+ server.succeed("curl http://localhost/ | grep \"Dependency-Track \"")
+ '';
}
)
diff --git a/nixos/tests/discourse.nix b/nixos/tests/discourse.nix
index 3accebe8f31f..1f8d1b7bdf4c 100644
--- a/nixos/tests/discourse.nix
+++ b/nixos/tests/discourse.nix
@@ -59,7 +59,7 @@ import ./make-test-python.nix (
environment.systemPackages = [ pkgs.jq ];
- services.postgresql.package = pkgs.postgresql_13;
+ services.postgresql.package = pkgs.postgresql_15;
services.discourse = {
enable = true;
@@ -104,7 +104,6 @@ import ./make-test-python.nix (
services.dovecot2 = {
enable = true;
protocols = [ "imap" ];
- modules = [ pkgs.dovecot_pigeonhole ];
};
services.postfix = {
diff --git a/nixos/tests/ec2.nix b/nixos/tests/ec2.nix
index 5964ea875088..b83d7e150207 100644
--- a/nixos/tests/ec2.nix
+++ b/nixos/tests/ec2.nix
@@ -12,7 +12,7 @@ with import common/ec2.nix { inherit makeTest pkgs; };
let
imageCfg =
(import ../lib/eval-config.nix {
- inherit system;
+ system = null;
modules = [
../maintainers/scripts/ec2/amazon-image.nix
../modules/testing/test-instrumentation.nix
@@ -54,6 +54,8 @@ let
apacheHttpd.man
valgrind.doc
]);
+
+ nixpkgs.pkgs = pkgs;
}
];
}).config;
diff --git a/nixos/tests/eintopf.nix b/nixos/tests/eintopf.nix
index 6e3992677da2..11e1158456f1 100644
--- a/nixos/tests/eintopf.nix
+++ b/nixos/tests/eintopf.nix
@@ -1,26 +1,24 @@
-import ./make-test-python.nix (
- { pkgs, ... }:
- {
- name = "eintopf";
- meta = with pkgs.lib.maintainers; {
- maintainers = [ onny ];
- };
+{
+ lib,
+ pkgs,
+ ...
+}:
- nodes = {
- eintopf =
- { config, pkgs, ... }:
- {
- services.eintopf = {
- enable = true;
- };
- };
- };
+{
+ name = "eintopf";
+ meta.maintainers = with lib.maintainers; [ onny ];
- testScript = ''
- eintopf.start
- eintopf.wait_for_unit("eintopf.service")
- eintopf.wait_for_open_port(3333)
- eintopf.succeed("curl -sSfL http://eintopf:3333 | grep 'Es sind keine Veranstaltungen eingetragen'")
- '';
- }
-)
+ nodes = {
+ eintopf = {
+ services.eintopf.enable = true;
+ };
+ };
+
+ testScript = ''
+ eintopf.start
+ eintopf.wait_for_unit("eintopf.service")
+ eintopf.wait_for_open_port(3333)
+ eintopf.succeed("curl -sSfL http://eintopf:3333 | grep 'No events available'")
+ '';
+
+}
diff --git a/nixos/tests/envfs.nix b/nixos/tests/envfs.nix
index 9e728698ebc3..b9067467eb2b 100644
--- a/nixos/tests/envfs.nix
+++ b/nixos/tests/envfs.nix
@@ -25,8 +25,6 @@ import ./make-test-python.nix (
"PATH= /usr/bin/env --version",
"PATH= test -e /usr/bin/sh",
"PATH= test -e /usr/bin/env",
- # no stat
- "! test -e /usr/bin/cp",
# also picks up PATH that was set after execve
"! /usr/bin/hello",
"PATH=${pkgs.hello}/bin /usr/bin/hello",
diff --git a/nixos/tests/fancontrol.nix b/nixos/tests/fancontrol.nix
index da2d5fbd8405..5bfd4b2bd37e 100644
--- a/nixos/tests/fancontrol.nix
+++ b/nixos/tests/fancontrol.nix
@@ -1,39 +1,37 @@
-import ./make-test-python.nix (
- { pkgs, ... }:
- {
- name = "fancontrol";
- meta = with pkgs.lib.maintainers; {
- maintainers = [ evils ];
+{ pkgs, ... }:
+{
+ name = "fancontrol";
+ meta = with pkgs.lib.maintainers; {
+ maintainers = [ evils ];
+ };
+
+ nodes.machine =
+ { ... }:
+ {
+ imports = [ ../modules/profiles/minimal.nix ];
+ hardware.fancontrol.enable = true;
+ hardware.fancontrol.config = ''
+ INTERVAL=42
+ DEVPATH=hwmon1=devices/platform/dummy
+ DEVNAME=hwmon1=dummy
+ FCTEMPS=hwmon1/device/pwm1=hwmon1/device/temp1_input
+ FCFANS=hwmon1/device/pwm1=hwmon1/device/fan1_input
+ MINTEMP=hwmon1/device/pwm1=25
+ MAXTEMP=hwmon1/device/pwm1=65
+ MINSTART=hwmon1/device/pwm1=150
+ MINSTOP=hwmon1/device/pwm1=0
+ '';
};
- nodes.machine =
- { ... }:
- {
- imports = [ ../modules/profiles/minimal.nix ];
- hardware.fancontrol.enable = true;
- hardware.fancontrol.config = ''
- INTERVAL=42
- DEVPATH=hwmon1=devices/platform/dummy
- DEVNAME=hwmon1=dummy
- FCTEMPS=hwmon1/device/pwm1=hwmon1/device/temp1_input
- FCFANS=hwmon1/device/pwm1=hwmon1/device/fan1_input
- MINTEMP=hwmon1/device/pwm1=25
- MAXTEMP=hwmon1/device/pwm1=65
- MINSTART=hwmon1/device/pwm1=150
- MINSTOP=hwmon1/device/pwm1=0
- '';
- };
-
- # This configuration cannot be valid for the test VM, so it's expected to get an 'outdated' error.
- testScript = ''
- start_all()
- # can't wait for unit fancontrol.service because it doesn't become active due to invalid config
- # fancontrol.service is WantedBy multi-user.target
- machine.wait_for_unit("multi-user.target")
- machine.succeed(
- "journalctl -eu fancontrol | tee /dev/stderr | grep 'Configuration appears to be outdated'"
- )
- machine.shutdown()
- '';
- }
-)
+ # This configuration cannot be valid for the test VM, so it's expected to get an 'outdated' error.
+ testScript = ''
+ start_all()
+ # can't wait for unit fancontrol.service because it doesn't become active due to invalid config
+ # fancontrol.service is WantedBy multi-user.target
+ machine.wait_for_unit("multi-user.target")
+ machine.succeed(
+ "journalctl -eu fancontrol | tee /dev/stderr | grep 'Configuration appears to be outdated'"
+ )
+ machine.shutdown()
+ '';
+}
diff --git a/nixos/tests/fluent-bit.nix b/nixos/tests/fluent-bit.nix
index 9dc68011ea85..f9923e3ada7e 100644
--- a/nixos/tests/fluent-bit.nix
+++ b/nixos/tests/fluent-bit.nix
@@ -1,40 +1,56 @@
-import ./make-test-python.nix (
- { lib, pkgs, ... }:
- {
- name = "fluent-bit";
-
- nodes.machine =
- { config, pkgs, ... }:
- {
- services.fluent-bit = {
- enable = true;
- settings = {
- pipeline = {
- inputs = [
- {
- name = "systemd";
- systemd_filter = "_SYSTEMD_UNIT=fluent-bit.service";
- }
- ];
- outputs = [
- {
- name = "file";
- path = "/var/log/fluent-bit";
- file = "fluent-bit.out";
- }
- ];
- };
- };
+# Regression test for https://github.com/NixOS/nixpkgs/pull/395128
+{
+ name = "fluent-bit";
+ nodes.machine = {
+ services.fluent-bit = {
+ enable = true;
+ settings = {
+ pipeline = {
+ inputs = [
+ {
+ name = "systemd";
+ systemd_filter = "_SYSTEMD_UNIT=fluent-bit-regression-395128.service";
+ }
+ ];
+ outputs = [
+ {
+ name = "file";
+ path = "/var/log/fluent-bit";
+ file = "fluent-bit.out";
+ }
+ ];
};
-
- systemd.services.fluent-bit.serviceConfig.LogsDirectory = "fluent-bit";
};
+ };
+ systemd.services.fluent-bit.serviceConfig.LogsDirectory = "fluent-bit";
- testScript = ''
- start_all()
+ # Logs get compressed when larger than 1024 bytes
+ # Lets generate some logs that trigger that
+ # This causes libzstd to be dlopen'd by systemd which breaks fluent-bit 3.2.7+
+ # https://www.freedesktop.org/software/systemd/man/latest/journald.conf.html#Compress=
+ systemd.services.fluent-bit-regression-395128 = {
+ serviceConfig = {
+ Type = "oneshot";
+ RemainAfterExit = true;
+ };
+ script = ''
+ for i in {1..20}; do
+ (head -c 1200 < /dev/zero | tr '\0' 'A') && echo
+ sleep 1
+ done
+ '';
+ };
+ };
- machine.wait_for_unit("fluent-bit.service")
- machine.wait_for_file("/var/log/fluent-bit/fluent-bit.out")
- '';
- }
-)
+ testScript = ''
+ start_all()
+
+ machine.wait_for_unit("fluent-bit.service")
+
+ with subtest("fluent-bit handles zstd-compressed journal logs"):
+ machine.succeed("systemctl start fluent-bit-regression-395128.service")
+ machine.succeed("systemctl show -p NRestarts fluent-bit.service | grep -q 'NRestarts=0'")
+
+ machine.wait_for_file("/var/log/fluent-bit/fluent-bit.out")
+ '';
+}
diff --git a/nixos/tests/forgejo.nix b/nixos/tests/forgejo.nix
index 38ef6cc57ed4..a35ee87ff68c 100644
--- a/nixos/tests/forgejo.nix
+++ b/nixos/tests/forgejo.nix
@@ -60,7 +60,6 @@ let
pkgs.gnupg
pkgs.jq
pkgs.file
- pkgs.htmlq
];
services.openssh.enable = true;
@@ -253,27 +252,22 @@ let
client.succeed("git -C /tmp/repo push origin main")
def poll_workflow_action_status(_) -> bool:
- output = server.succeed(
- "curl --fail http://localhost:3000/test/repo/actions | "
- + 'htmlq ".flex-item-leading span" --attribute "data-tooltip-content"'
- ).strip()
+ try:
+ response = server.succeed("curl --fail http://localhost:3000/api/v1/repos/test/repo/actions/tasks")
+ status = json.loads(response).get("workflow_runs")[0].get("status")
- # values taken from https://codeberg.org/forgejo/forgejo/src/commit/af47c583b4fb3190fa4c4c414500f9941cc02389/options/locale/locale_en-US.ini#L3649-L3661
- if output in [ "Failure", "Canceled", "Skipped", "Blocked" ]:
- raise Exception(f"Workflow status is '{output}', which we consider failed.")
- server.log(f"Command returned '{output}', which we consider failed.")
+ except IndexError:
+ status = "???"
- elif output in [ "Unknown", "Waiting", "Running", "" ]:
- server.log(f"Workflow status is '{output}'. Waiting some more...")
- return False
+ server.log(f"Workflow status: {status}")
- elif output in [ "Success" ]:
- return True
+ if status == "failure":
+ raise Exception("Workflow failed")
- raise Exception(f"Workflow status is '{output}', which we don't know. Value mappings likely need updating.")
+ return status == "success"
with server.nested("Waiting for the workflow run to be successful"):
- retry(poll_workflow_action_status)
+ retry(poll_workflow_action_status, 60)
with subtest("Testing backup service"):
server.succeed("${serverSystem}/specialisation/dump/bin/switch-to-configuration test")
diff --git a/nixos/tests/gerrit.nix b/nixos/tests/gerrit.nix
index 86e4cf90ed73..901187a6c5a5 100644
--- a/nixos/tests/gerrit.nix
+++ b/nixos/tests/gerrit.nix
@@ -1,57 +1,55 @@
-import ./make-test-python.nix (
- { pkgs, ... }:
+{ pkgs, ... }:
- {
- name = "gerrit";
+{
+ name = "gerrit";
- meta = with pkgs.lib.maintainers; {
- maintainers = [
- flokli
- zimbatm
- ];
- };
+ meta = with pkgs.lib.maintainers; {
+ maintainers = [
+ flokli
+ zimbatm
+ ];
+ };
- nodes = {
- server =
- { config, pkgs, ... }:
- {
- networking.firewall.allowedTCPPorts = [
- 80
- 2222
+ nodes = {
+ server =
+ { config, pkgs, ... }:
+ {
+ networking.firewall.allowedTCPPorts = [
+ 80
+ 2222
+ ];
+
+ services.gerrit = {
+ enable = true;
+ serverId = "aa76c84b-50b0-4711-a0a0-1ee30e45bbd0";
+ listenAddress = "[::]:80";
+ jvmHeapLimit = "1g";
+
+ builtinPlugins = [
+ "hooks"
+ "webhooks"
];
-
- services.gerrit = {
- enable = true;
- serverId = "aa76c84b-50b0-4711-a0a0-1ee30e45bbd0";
- listenAddress = "[::]:80";
- jvmHeapLimit = "1g";
-
- builtinPlugins = [
- "hooks"
- "webhooks"
- ];
- settings = {
- gerrit.canonicalWebUrl = "http://server";
- sshd.listenAddress = "[::]:2222";
- sshd.advertisedAddress = "[::]:2222";
- };
+ settings = {
+ gerrit.canonicalWebUrl = "http://server";
+ sshd.listenAddress = "[::]:2222";
+ sshd.advertisedAddress = "[::]:2222";
};
};
+ };
- client =
- { ... }:
- {
- };
- };
+ client =
+ { ... }:
+ {
+ };
+ };
- testScript = ''
- start_all()
- server.wait_for_unit("gerrit.service")
- server.wait_for_open_port(80)
- client.succeed("curl http://server")
+ testScript = ''
+ start_all()
+ server.wait_for_unit("gerrit.service")
+ server.wait_for_open_port(80)
+ client.succeed("curl http://server")
- server.wait_for_open_port(2222)
- client.succeed("nc -z server 2222")
- '';
- }
-)
+ server.wait_for_open_port(2222)
+ client.succeed("nc -z server 2222")
+ '';
+}
diff --git a/nixos/tests/git/hub.nix b/nixos/tests/git/hub.nix
index 43ae2b7ab30b..622f4b6dac05 100644
--- a/nixos/tests/git/hub.nix
+++ b/nixos/tests/git/hub.nix
@@ -1,20 +1,18 @@
-import ../make-test-python.nix (
- { pkgs, ... }:
- {
- name = "hub";
- meta = with pkgs.lib.maintainers; {
- maintainers = [ nequissimus ];
+{ pkgs, ... }:
+{
+ name = "hub";
+ meta = with pkgs.lib.maintainers; {
+ maintainers = [ nequissimus ];
+ };
+
+ nodes.hub =
+ { pkgs, ... }:
+ {
+ environment.systemPackages = [ pkgs.hub ];
};
- nodes.hub =
- { pkgs, ... }:
- {
- environment.systemPackages = [ pkgs.hub ];
- };
-
- testScript = ''
- assert "git version ${pkgs.git.version}\nhub version ${pkgs.hub.version}\n" in hub.succeed("hub version")
- assert "These GitHub commands are provided by hub" in hub.succeed("hub help")
- '';
- }
-)
+ testScript = ''
+ assert "git version ${pkgs.git.version}\nhub version ${pkgs.hub.version}\n" in hub.succeed("hub version")
+ assert "These GitHub commands are provided by hub" in hub.succeed("hub help")
+ '';
+}
diff --git a/nixos/tests/github-runner.nix b/nixos/tests/github-runner.nix
index ffc0dbcc374b..847da352ca6c 100644
--- a/nixos/tests/github-runner.nix
+++ b/nixos/tests/github-runner.nix
@@ -1,49 +1,47 @@
-import ./make-test-python.nix (
- { pkgs, ... }:
- {
- name = "github-runner";
- meta = with pkgs.lib.maintainers; {
- maintainers = [ veehaitch ];
- };
- nodes.machine =
- { pkgs, ... }:
- {
- services.github-runners.test = {
- enable = true;
- url = "https://github.com/yaxitech";
- tokenFile = builtins.toFile "github-runner.token" "not-so-secret";
- };
-
- services.github-runners.test-disabled = {
- enable = false;
- url = "https://github.com/yaxitech";
- tokenFile = builtins.toFile "github-runner.token" "not-so-secret";
- };
-
- systemd.services.dummy-github-com = {
- wantedBy = [ "multi-user.target" ];
- before = [ "github-runner-test.service" ];
- script = "${pkgs.netcat}/bin/nc -Fl 443 | true && touch /tmp/registration-connect";
- };
- networking.hosts."127.0.0.1" = [ "api.github.com" ];
+{ pkgs, ... }:
+{
+ name = "github-runner";
+ meta = with pkgs.lib.maintainers; {
+ maintainers = [ veehaitch ];
+ };
+ nodes.machine =
+ { pkgs, ... }:
+ {
+ services.github-runners.test = {
+ enable = true;
+ url = "https://github.com/yaxitech";
+ tokenFile = builtins.toFile "github-runner.token" "not-so-secret";
};
- testScript = ''
- start_all()
+ services.github-runners.test-disabled = {
+ enable = false;
+ url = "https://github.com/yaxitech";
+ tokenFile = builtins.toFile "github-runner.token" "not-so-secret";
+ };
- machine.wait_for_unit("dummy-github-com")
+ systemd.services.dummy-github-com = {
+ wantedBy = [ "multi-user.target" ];
+ before = [ "github-runner-test.service" ];
+ script = "${pkgs.netcat}/bin/nc -Fl 443 | true && touch /tmp/registration-connect";
+ };
+ networking.hosts."127.0.0.1" = [ "api.github.com" ];
+ };
- try:
- machine.wait_for_unit("github-runner-test")
- except Exception:
- pass
+ testScript = ''
+ start_all()
- out = machine.succeed("journalctl -u github-runner-test")
- assert "Self-hosted runner registration" in out, "did not read runner registration header"
+ machine.wait_for_unit("dummy-github-com")
- machine.wait_until_succeeds("test -f /tmp/registration-connect")
+ try:
+ machine.wait_for_unit("github-runner-test")
+ except Exception:
+ pass
- machine.fail("systemctl list-unit-files | grep test-disabled")
- '';
- }
-)
+ out = machine.succeed("journalctl -u github-runner-test")
+ assert "Self-hosted runner registration" in out, "did not read runner registration header"
+
+ machine.wait_until_succeeds("test -f /tmp/registration-connect")
+
+ machine.fail("systemctl list-unit-files | grep test-disabled")
+ '';
+}
diff --git a/nixos/tests/gitlab.nix b/nixos/tests/gitlab.nix
index da27cfbbc195..cead8f538a86 100644
--- a/nixos/tests/gitlab.nix
+++ b/nixos/tests/gitlab.nix
@@ -42,6 +42,10 @@ in
environment.systemPackages = with pkgs; [ git ];
+ networking.hosts."127.0.0.1" = [
+ "registry.localhost"
+ "pages.localhost"
+ ];
virtualisation.memorySize = 6144;
virtualisation.cores = 4;
virtualisation.useNixStoreImage = true;
@@ -59,6 +63,12 @@ in
localhost = {
locations."/".proxyPass = "http://unix:/run/gitlab/gitlab-workhorse.socket";
};
+ "pages.localhost" = {
+ locations."/".proxyPass = "http://localhost:8090";
+ };
+ "registry.localhost" = {
+ locations."/".proxyPass = "http://localhost:4567";
+ };
};
};
@@ -78,7 +88,7 @@ in
smtp.enable = true;
pages = {
enable = true;
- settings.pages-domain = "localhost";
+ settings.pages-domain = "pages.localhost";
};
extraConfig = {
incoming_email = {
@@ -96,6 +106,17 @@ in
otpFile = pkgs.writeText "otpsecret" "Riew9mue";
dbFile = pkgs.writeText "dbsecret" "we2quaeZ";
jwsFile = pkgs.runCommand "oidcKeyBase" { } "${pkgs.openssl}/bin/openssl genrsa 2048 > $out";
+ activeRecordPrimaryKeyFile = pkgs.writeText "arprimary" "vsaYPZjTRxcbG7W6gNr95AwBmzFUd4Eu";
+ activeRecordDeterministicKeyFile = pkgs.writeText "ardeterministic" "kQarv9wb2JVP7XzLTh5f6DFcMHms4nEC";
+ activeRecordSaltFile = pkgs.writeText "arsalt" "QkgR9CfFU3MXEWGqa7LbP24AntK5ZeYw";
+ };
+
+ registry = {
+ enable = true;
+ certFile = "/var/lib/gitlab/registry_auth_cert";
+ keyFile = "/var/lib/gitlab/registry_auth_key";
+ externalAddress = "registry.localhost";
+ externalPort = 443;
};
# reduce memory usage
@@ -210,6 +231,7 @@ in
gitlab.wait_for_unit("gitlab-sidekiq.service")
gitlab.wait_for_file("${nodes.gitlab.services.gitlab.statePath}/tmp/sockets/gitlab.socket")
gitlab.wait_until_succeeds("curl -sSf http://gitlab/users/sign_in")
+ gitlab.wait_for_unit("docker-registry.service")
'';
# The actual test of GitLab. Only push data to GitLab if
@@ -249,7 +271,7 @@ in
with subtest("Setup Git and SSH for Alice"):
gitlab.succeed("git config --global user.name Alice")
gitlab.succeed("git config --global user.email alice@nixos.invalid")
- gitlab.succeed("mkdir -m 700 /root/.ssh")
+ gitlab.succeed("mkdir -p -m 700 /root/.ssh")
gitlab.succeed("cat ${snakeOilPrivateKey} > /root/.ssh/id_ecdsa")
gitlab.succeed("chmod 600 /root/.ssh/id_ecdsa")
gitlab.succeed(
@@ -447,6 +469,10 @@ in
"""
)
gitlab.succeed("test -s /tmp/archive.tar.bz2")
+ ''
+ + ''
+ with subtest("Test docker registry http is available"):
+ gitlab.succeed("curl -sSf http://registry.localhost")
'';
in
@@ -454,6 +480,9 @@ in
gitlab.start()
''
+ waitForServices
+ + ''
+ gitlab.succeed("cp /var/gitlab/state/config/secrets.yml /root/gitlab-secrets.yml")
+ ''
+ test true
+ ''
gitlab.systemctl("start gitlab-backup.service")
@@ -473,5 +502,9 @@ in
gitlab.systemctl("start gitlab.target")
''
+ waitForServices
+ + ''
+ with subtest("Check that no secrets were auto-generated as these would be non-persistent"):
+ gitlab.succeed("diff -u /root/gitlab-secrets.yml /var/gitlab/state/config/secrets.yml")
+ ''
+ test false;
}
diff --git a/nixos/tests/glance.nix b/nixos/tests/glance.nix
index 455ef0868513..254173e1eb71 100644
--- a/nixos/tests/glance.nix
+++ b/nixos/tests/glance.nix
@@ -5,19 +5,47 @@
nodes = {
machine_default =
- { pkgs, ... }:
+ { ... }:
{
services.glance = {
enable = true;
};
};
- machine_custom_port =
+ machine_configured =
{ pkgs, ... }:
+ let
+ # Do not use this in production. This will make the secret world-readable
+ # in the Nix store
+ secrets.glance-location.path = builtins.toString (
+ pkgs.writeText "location-secret" "Nivelles, Belgium"
+ );
+ in
{
services.glance = {
enable = true;
- settings.server.port = 5678;
+ settings = {
+ server.port = 5678;
+ pages = [
+ {
+ name = "Home";
+ columns = [
+ {
+ size = "full";
+ widgets = [
+ { type = "calendar"; }
+ {
+ type = "weather";
+ location = {
+ _secret = secrets.glance-location.path;
+ };
+ }
+ ];
+ }
+ ];
+ }
+ ];
+ };
};
};
};
@@ -25,23 +53,31 @@
extraPythonPackages =
p: with p; [
beautifulsoup4
+ pyyaml
+ types-pyyaml
types-beautifulsoup4
];
testScript = ''
from bs4 import BeautifulSoup
+ import yaml
machine_default.start()
machine_default.wait_for_unit("glance.service")
machine_default.wait_for_open_port(8080)
- machine_custom_port.start()
- machine_custom_port.wait_for_unit("glance.service")
- machine_custom_port.wait_for_open_port(5678)
+ machine_configured.start()
+ machine_configured.wait_for_unit("glance.service")
+ machine_configured.wait_for_open_port(5678)
soup = BeautifulSoup(machine_default.succeed("curl http://localhost:8080"))
expected_version = "v${config.nodes.machine_default.services.glance.package.version}"
assert any(a.text == expected_version for a in soup.select(".footer a"))
+
+ yaml_contents = machine_configured.succeed("cat /run/glance/glance.yaml")
+ yaml_parsed = yaml.load(yaml_contents, Loader=yaml.FullLoader)
+ location = yaml_parsed["pages"][0]["columns"][0]["widgets"][1]["location"]
+ assert location == "Nivelles, Belgium"
'';
meta.maintainers = [ lib.maintainers.drupol ];
diff --git a/nixos/tests/gnome-extensions.nix b/nixos/tests/gnome-extensions.nix
index 9fb868f82454..d6381a899f3b 100644
--- a/nixos/tests/gnome-extensions.nix
+++ b/nixos/tests/gnome-extensions.nix
@@ -121,11 +121,14 @@ import ./make-test-python.nix (
# "${run "gsettings set org.gnome.shell disable-extension-version-validation true"}"
# )
- # Assert that some extension is in a specific state
- def checkState(target, extension):
- state = machine.succeed(
+ def getState(extension):
+ return machine.succeed(
f"${run "gnome-extensions info {extension}"} | grep '^ State: .*$'"
)
+
+ # Assert that some extension is in a specific state
+ def checkState(target, extension):
+ state = getState(extension)
assert target in state, f"{state} instead of {target}"
def checkExtension(extension, disable):
@@ -143,6 +146,10 @@ import ./make-test-python.nix (
# Enable and optionally disable
machine.succeed(f"${run "gnome-extensions enable {extension}"}")
+ wait_time = 5
+ while getState(extension) == "ACTIVATING" and (wait_time := wait_time - 1) > 0:
+ machine.log(f"Extension {extension} is still activating, waiting {wait_time} more seconds")
+ machine.sleep(1)
checkState("ACTIVE", extension)
if disable:
diff --git a/nixos/tests/gnome.nix b/nixos/tests/gnome.nix
index 08213e41f98d..17fdcf550ca3 100644
--- a/nixos/tests/gnome.nix
+++ b/nixos/tests/gnome.nix
@@ -1,103 +1,101 @@
-import ./make-test-python.nix (
- { pkgs, lib, ... }:
- {
- name = "gnome";
- meta.maintainers = lib.teams.gnome.members;
+{ pkgs, lib, ... }:
+{
+ name = "gnome";
+ meta.maintainers = lib.teams.gnome.members;
- nodes.machine =
- { ... }:
+ nodes.machine =
+ { ... }:
- {
- imports = [ ./common/user-account.nix ];
+ {
+ imports = [ ./common/user-account.nix ];
- services.xserver.enable = true;
-
- services.xserver.displayManager = {
- gdm.enable = true;
- gdm.debug = true;
- };
-
- services.displayManager.autoLogin = {
- enable = true;
- user = "alice";
- };
-
- services.xserver.desktopManager.gnome.enable = true;
- services.xserver.desktopManager.gnome.debug = true;
-
- systemd.user.services = {
- "org.gnome.Shell@wayland" = {
- serviceConfig = {
- ExecStart = [
- # Clear the list before overriding it.
- ""
- # Eval API is now internal so Shell needs to run in unsafe mode.
- # TODO: improve test driver so that it supports openqa-like manipulation
- # that would allow us to drop this mess.
- "${pkgs.gnome-shell}/bin/gnome-shell --unsafe-mode"
- ];
- };
- };
- };
+ services.xserver.enable = true;
+ services.xserver.displayManager = {
+ gdm.enable = true;
+ gdm.debug = true;
};
- testScript =
- { nodes, ... }:
- let
- # Keep line widths somewhat manageable
- user = nodes.machine.users.users.alice;
- uid = toString user.uid;
- bus = "DBUS_SESSION_BUS_ADDRESS=unix:path=/run/user/${uid}/bus";
- # Run a command in the appropriate user environment
- run = command: "su - ${user.name} -c '${bus} ${command}'";
+ services.displayManager.autoLogin = {
+ enable = true;
+ user = "alice";
+ };
- # Call javascript in gnome shell, returns a tuple (success, output), where
- # `success` is true if the dbus call was successful and output is what the
- # javascript evaluates to.
- eval =
- command:
- run "gdbus call --session -d org.gnome.Shell -o /org/gnome/Shell -m org.gnome.Shell.Eval ${command}";
+ services.xserver.desktopManager.gnome.enable = true;
+ services.xserver.desktopManager.gnome.debug = true;
- # False when startup is done
- startingUp = eval "Main.layoutManager._startingUp";
+ systemd.user.services = {
+ "org.gnome.Shell@wayland" = {
+ serviceConfig = {
+ ExecStart = [
+ # Clear the list before overriding it.
+ ""
+ # Eval API is now internal so Shell needs to run in unsafe mode.
+ # TODO: improve test driver so that it supports openqa-like manipulation
+ # that would allow us to drop this mess.
+ "${pkgs.gnome-shell}/bin/gnome-shell --unsafe-mode"
+ ];
+ };
+ };
+ };
- # Start Console
- launchConsole = run "gapplication launch org.gnome.Console";
+ };
- # Hopefully Console's wm class
- wmClass = eval "global.display.focus_window.wm_class";
- in
- ''
- with subtest("Login to GNOME with GDM"):
- # wait for gdm to start
- machine.wait_for_unit("display-manager.service")
- # wait for the wayland server
- machine.wait_for_file("/run/user/${uid}/wayland-0")
- # wait for alice to be logged in
- machine.wait_for_unit("default.target", "${user.name}")
- # check that logging in has given the user ownership of devices
- assert "alice" in machine.succeed("getfacl -p /dev/snd/timer")
+ testScript =
+ { nodes, ... }:
+ let
+ # Keep line widths somewhat manageable
+ user = nodes.machine.users.users.alice;
+ uid = toString user.uid;
+ bus = "DBUS_SESSION_BUS_ADDRESS=unix:path=/run/user/${uid}/bus";
+ # Run a command in the appropriate user environment
+ run = command: "su - ${user.name} -c '${bus} ${command}'";
- with subtest("Wait for GNOME Shell"):
- # correct output should be (true, 'false')
- machine.wait_until_succeeds(
- "${startingUp} | grep -q 'true,..false'"
- )
+ # Call javascript in gnome shell, returns a tuple (success, output), where
+ # `success` is true if the dbus call was successful and output is what the
+ # javascript evaluates to.
+ eval =
+ command:
+ run "gdbus call --session -d org.gnome.Shell -o /org/gnome/Shell -m org.gnome.Shell.Eval ${command}";
- with subtest("Open Console"):
- # Close the Activities view so that Shell can correctly track the focused window.
- machine.send_key("esc")
+ # False when startup is done
+ startingUp = eval "Main.layoutManager._startingUp";
- machine.succeed(
- "${launchConsole}"
- )
- # correct output should be (true, '"org.gnome.Console"')
- machine.wait_until_succeeds(
- "${wmClass} | grep -q 'true,...org.gnome.Console'"
- )
- machine.sleep(20)
- machine.screenshot("screen")
- '';
- }
-)
+ # Start Console
+ launchConsole = run "gapplication launch org.gnome.Console";
+
+ # Hopefully Console's wm class
+ wmClass = eval "global.display.focus_window.wm_class";
+ in
+ ''
+ with subtest("Login to GNOME with GDM"):
+ # wait for gdm to start
+ machine.wait_for_unit("display-manager.service")
+ # wait for the wayland server
+ machine.wait_for_file("/run/user/${uid}/wayland-0")
+ # wait for alice to be logged in
+ machine.wait_for_unit("default.target", "${user.name}")
+ # check that logging in has given the user ownership of devices
+ assert "alice" in machine.succeed("getfacl -p /dev/snd/timer")
+
+ with subtest("Wait for GNOME Shell"):
+ # correct output should be (true, 'false')
+ machine.wait_until_succeeds(
+ "${startingUp} | grep -q 'true,..false'"
+ )
+
+ with subtest("Open Console"):
+ # Close the Activities view so that Shell can correctly track the focused window.
+ machine.send_key("esc")
+
+ machine.succeed(
+ "${launchConsole}"
+ )
+ # correct output should be (true, '"org.gnome.Console"')
+ machine.wait_until_succeeds(
+ "${wmClass} | grep -q 'true,...org.gnome.Console'"
+ )
+ machine.sleep(20)
+ machine.screenshot("screen")
+ '';
+}
diff --git a/nixos/tests/grafana/provision/default.nix b/nixos/tests/grafana/provision/default.nix
index 3c19aa189375..6eba72fb9501 100644
--- a/nixos/tests/grafana/provision/default.nix
+++ b/nixos/tests/grafana/provision/default.nix
@@ -212,37 +212,37 @@ import ../../make-test-python.nix (
machine.wait_for_open_port(3000, addr="::1")
with subtest(f"Successful datasource provision with {description}"):
- machine.succeed(
+ machine.wait_until_succeeds(
"curl -sSfN -u testadmin:snakeoilpwd http://[::1]:3000/api/datasources/uid/test_datasource | grep Test\ Datasource"
)
with subtest(f"Successful dashboard provision with {description}"):
- machine.succeed(
+ machine.wait_until_succeeds(
"curl -sSfN -u testadmin:snakeoilpwd http://[::1]:3000/api/dashboards/uid/test_dashboard | grep Test\ Dashboard"
)
with subtest(f"Successful rule provision with {description}"):
- machine.succeed(
+ machine.wait_until_succeeds(
"curl -sSfN -u testadmin:snakeoilpwd http://[::1]:3000/api/v1/provisioning/alert-rules/test_rule | grep Test\ Rule"
)
with subtest(f"Successful contact point provision with {description}"):
- machine.succeed(
+ machine.wait_until_succeeds(
"curl -sSfN -u testadmin:snakeoilpwd http://[::1]:3000/api/v1/provisioning/contact-points | grep Test\ Contact\ Point"
)
with subtest(f"Successful policy provision with {description}"):
- machine.succeed(
+ machine.wait_until_succeeds(
"curl -sSfN -u testadmin:snakeoilpwd http://[::1]:3000/api/v1/provisioning/policies | grep Test\ Contact\ Point"
)
with subtest(f"Successful template provision with {description}"):
- machine.succeed(
+ machine.wait_until_succeeds(
"curl -sSfN -u testadmin:snakeoilpwd http://[::1]:3000/api/v1/provisioning/templates | grep Test\ Template"
)
with subtest("Successful mute timings provision with {description}"):
- machine.succeed(
+ machine.wait_until_succeeds(
"curl -sSfN -u testadmin:snakeoilpwd http://[::1]:3000/api/v1/provisioning/mute-timings | grep Test\ Mute\ Timing"
)
'';
diff --git a/nixos/tests/hadoop/hbase.nix b/nixos/tests/hadoop/hbase.nix
index efc85c869bf9..f6eb1f7e759a 100644
--- a/nixos/tests/hadoop/hbase.nix
+++ b/nixos/tests/hadoop/hbase.nix
@@ -116,6 +116,9 @@ import ../make-test-python.nix (
zookeeper.wait_for_unit("zookeeper")
zookeeper.wait_for_open_port(2181)
+ # wait for HDFS cluster to be RW
+ datanode.succeed("sudo -u hdfs hdfs dfsadmin -safemode wait")
+
# wait for HBase to start up
master.wait_for_unit("hbase-master")
regionserver.wait_for_unit("hbase-regionserver")
diff --git a/nixos/tests/haproxy.nix b/nixos/tests/haproxy.nix
index b3cc19696d7c..bd8b9b288a22 100644
--- a/nixos/tests/haproxy.nix
+++ b/nixos/tests/haproxy.nix
@@ -1,140 +1,138 @@
-import ./make-test-python.nix (
- { lib, pkgs, ... }:
- {
- name = "haproxy";
- nodes = {
- server =
- { ... }:
- {
- services.haproxy = {
- enable = true;
- config = ''
- global
- limited-quic
+{ lib, hostPkgs, ... }:
+{
+ name = "haproxy";
+ nodes = {
+ server =
+ { pkgs, ... }:
+ {
+ services.haproxy = {
+ enable = true;
+ config = ''
+ global
+ limited-quic
- defaults
- mode http
- timeout connect 10s
- timeout client 10s
- timeout server 10s
+ defaults
+ mode http
+ timeout connect 10s
+ timeout client 10s
+ timeout server 10s
- log /dev/log local0 debug err
- option logasap
- option httplog
- option httpslog
+ log /dev/log local0 debug err
+ option logasap
+ option httplog
+ option httpslog
- backend http_server
- server httpd [::1]:8000 alpn http/1.1
+ backend http_server
+ server httpd [::1]:8000 alpn http/1.1
- frontend http
- bind :80
- bind :443 ssl strict-sni crt /etc/ssl/fullchain.pem alpn h2,http/1.1
- bind quic4@:443 ssl strict-sni crt /etc/ssl/fullchain.pem alpn h3 allow-0rtt
+ frontend http
+ bind :80
+ bind :443 ssl strict-sni crt /etc/ssl/fullchain.pem alpn h2,http/1.1
+ bind quic4@:443 ssl strict-sni crt /etc/ssl/fullchain.pem alpn h3 allow-0rtt
- http-after-response add-header alt-svc 'h3=":443"; ma=60' if { ssl_fc }
+ http-after-response add-header alt-svc 'h3=":443"; ma=60' if { ssl_fc }
- http-request use-service prometheus-exporter if { path /metrics }
- use_backend http_server
+ http-request use-service prometheus-exporter if { path /metrics }
+ use_backend http_server
- frontend http-cert-auth
- bind :8443 ssl strict-sni crt /etc/ssl/fullchain.pem verify required ca-file /etc/ssl/cacert.crt
- bind quic4@:8443 ssl strict-sni crt /etc/ssl/fullchain.pem verify required ca-file /etc/ssl/cacert.crt alpn h3
+ frontend http-cert-auth
+ bind :8443 ssl strict-sni crt /etc/ssl/fullchain.pem verify required ca-file /etc/ssl/cacert.crt
+ bind quic4@:8443 ssl strict-sni crt /etc/ssl/fullchain.pem verify required ca-file /etc/ssl/cacert.crt alpn h3
- use_backend http_server
- '';
- };
- services.httpd = {
- enable = true;
- virtualHosts.localhost = {
- documentRoot = pkgs.writeTextDir "index.txt" "We are all good!";
- adminAddr = "notme@yourhost.local";
- listen = [
- {
- ip = "::1";
- port = 8000;
- }
- ];
- };
- };
- networking.firewall.allowedTCPPorts = [
- 80
- 443
- 8443
- ];
- networking.firewall.allowedUDPPorts = [
- 443
- 8443
- ];
+ use_backend http_server
+ '';
};
- client =
- { ... }:
- {
- environment.systemPackages = [ pkgs.curlHTTP3 ];
+ services.httpd = {
+ enable = true;
+ virtualHosts.localhost = {
+ documentRoot = pkgs.writeTextDir "index.txt" "We are all good!";
+ adminAddr = "notme@yourhost.local";
+ listen = [
+ {
+ ip = "::1";
+ port = 8000;
+ }
+ ];
+ };
};
- };
- testScript = ''
- # Helpers
- def cmd(command):
- print(f"+{command}")
- r = os.system(command)
- if r != 0:
- raise Exception(f"Command {command} failed with exit code {r}")
+ networking.firewall.allowedTCPPorts = [
+ 80
+ 443
+ 8443
+ ];
+ networking.firewall.allowedUDPPorts = [
+ 443
+ 8443
+ ];
+ };
+ client =
+ { pkgs, ... }:
+ {
+ environment.systemPackages = [ pkgs.curlHTTP3 ];
+ };
+ };
+ testScript = ''
+ # Helpers
+ def cmd(command):
+ print(f"+{command}")
+ r = os.system(command)
+ if r != 0:
+ raise Exception(f"Command {command} failed with exit code {r}")
- def openssl(command):
- cmd(f"${pkgs.openssl}/bin/openssl {command}")
+ def openssl(command):
+ cmd(f"${lib.getExe hostPkgs.openssl} {command}")
- # Generate CA.
- openssl("req -new -newkey rsa:4096 -nodes -x509 -days 7 -subj '/C=ZZ/ST=Cloud/L=Unspecified/O=NixOS/OU=Tests/CN=CA Certificate' -keyout cacert.key -out cacert.crt")
+ # Generate CA.
+ openssl("req -new -newkey rsa:4096 -nodes -x509 -days 7 -subj '/C=ZZ/ST=Cloud/L=Unspecified/O=NixOS/OU=Tests/CN=CA Certificate' -keyout cacert.key -out cacert.crt")
- # Generate and sign Server.
- openssl("req -newkey rsa:4096 -nodes -subj '/CN=server/OU=Tests/O=NixOS' -keyout server.key -out server.csr")
- openssl("x509 -req -in server.csr -out server.crt -CA cacert.crt -CAkey cacert.key -days 7")
- cmd("cat server.crt server.key > fullchain.pem")
+ # Generate and sign Server.
+ openssl("req -newkey rsa:4096 -nodes -subj '/CN=server/OU=Tests/O=NixOS' -keyout server.key -out server.csr")
+ openssl("x509 -req -in server.csr -out server.crt -CA cacert.crt -CAkey cacert.key -days 7")
+ cmd("cat server.crt server.key > fullchain.pem")
- # Generate and sign Client.
- openssl("req -newkey rsa:4096 -nodes -subj '/CN=client/OU=Tests/O=NixOS' -keyout client.key -out client.csr")
- openssl("x509 -req -in client.csr -out client.crt -CA cacert.crt -CAkey cacert.key -days 7")
- cmd("cat client.crt client.key > client.pem")
+ # Generate and sign Client.
+ openssl("req -newkey rsa:4096 -nodes -subj '/CN=client/OU=Tests/O=NixOS' -keyout client.key -out client.csr")
+ openssl("x509 -req -in client.csr -out client.crt -CA cacert.crt -CAkey cacert.key -days 7")
+ cmd("cat client.crt client.key > client.pem")
- # Start the actual test.
- start_all()
- server.copy_from_host("fullchain.pem", "/etc/ssl/fullchain.pem")
- server.copy_from_host("cacert.crt", "/etc/ssl/cacert.crt")
- server.succeed("chmod 0644 /etc/ssl/fullchain.pem /etc/ssl/cacert.crt")
+ # Start the actual test.
+ start_all()
+ server.copy_from_host("fullchain.pem", "/etc/ssl/fullchain.pem")
+ server.copy_from_host("cacert.crt", "/etc/ssl/cacert.crt")
+ server.succeed("chmod 0644 /etc/ssl/fullchain.pem /etc/ssl/cacert.crt")
- client.copy_from_host("cacert.crt", "/etc/ssl/cacert.crt")
- client.copy_from_host("client.pem", "/root/client.pem")
+ client.copy_from_host("cacert.crt", "/etc/ssl/cacert.crt")
+ client.copy_from_host("client.pem", "/root/client.pem")
- server.wait_for_unit("multi-user.target")
- server.wait_for_unit("haproxy.service")
- server.wait_for_unit("httpd.service")
+ server.wait_for_unit("multi-user.target")
+ server.wait_for_unit("haproxy.service")
+ server.wait_for_unit("httpd.service")
- assert "We are all good!" in client.succeed("curl -f http://server/index.txt")
- assert "haproxy_process_pool_allocated_bytes" in client.succeed("curl -f http://server/metrics")
+ assert "We are all good!" in client.succeed("curl -f http://server/index.txt")
+ assert "haproxy_process_pool_allocated_bytes" in client.succeed("curl -f http://server/metrics")
- with subtest("https"):
- assert "We are all good!" in client.succeed("curl -f --cacert /etc/ssl/cacert.crt https://server/index.txt")
+ with subtest("https"):
+ assert "We are all good!" in client.succeed("curl -f --cacert /etc/ssl/cacert.crt https://server/index.txt")
- with subtest("https-cert-auth"):
- # Client must succeed in authenticating with the right certificate.
- assert "We are all good!" in client.succeed("curl -f --cacert /etc/ssl/cacert.crt --cert-type pem --cert /root/client.pem https://server:8443/index.txt")
- # Client must fail without certificate.
- client.fail("curl --cacert /etc/ssl/cacert.crt https://server:8443/index.txt")
+ with subtest("https-cert-auth"):
+ # Client must succeed in authenticating with the right certificate.
+ assert "We are all good!" in client.succeed("curl -f --cacert /etc/ssl/cacert.crt --cert-type pem --cert /root/client.pem https://server:8443/index.txt")
+ # Client must fail without certificate.
+ client.fail("curl --cacert /etc/ssl/cacert.crt https://server:8443/index.txt")
- with subtest("h3"):
- assert "We are all good!" in client.succeed("curl -f --http3-only --cacert /etc/ssl/cacert.crt https://server/index.txt")
+ with subtest("h3"):
+ assert "We are all good!" in client.succeed("curl -f --http3-only --cacert /etc/ssl/cacert.crt https://server/index.txt")
- with subtest("h3-cert-auth"):
- # Client must succeed in authenticating with the right certificate.
- assert "We are all good!" in client.succeed("curl -f --http3-only --cacert /etc/ssl/cacert.crt --cert-type pem --cert /root/client.pem https://server:8443/index.txt")
- # Client must fail without certificate.
- client.fail("curl -f --http3-only --cacert /etc/ssl/cacert.crt https://server:8443/index.txt")
+ with subtest("h3-cert-auth"):
+ # Client must succeed in authenticating with the right certificate.
+ assert "We are all good!" in client.succeed("curl -f --http3-only --cacert /etc/ssl/cacert.crt --cert-type pem --cert /root/client.pem https://server:8443/index.txt")
+ # Client must fail without certificate.
+ client.fail("curl -f --http3-only --cacert /etc/ssl/cacert.crt https://server:8443/index.txt")
- with subtest("reload"):
- server.succeed("systemctl reload haproxy")
- # wait some time to ensure the following request hits the reloaded haproxy
- server.sleep(5)
- assert "We are all good!" in client.succeed("curl -f http://server/index.txt")
- '';
- }
-)
+ with subtest("reload"):
+ server.succeed("systemctl reload haproxy")
+ # wait some time to ensure the following request hits the reloaded haproxy
+ server.sleep(5)
+ assert "We are all good!" in client.succeed("curl -f http://server/index.txt")
+ '';
+}
diff --git a/nixos/tests/home-assistant.nix b/nixos/tests/home-assistant.nix
index 0420eb86d5e8..ce2cf17fab56 100644
--- a/nixos/tests/home-assistant.nix
+++ b/nixos/tests/home-assistant.nix
@@ -85,7 +85,6 @@ in
# include some popular integrations, that absolutely shouldn't break
knx = { };
- shelly = { };
zha = { };
# set up a wake-on-lan switch to test capset capability required
@@ -108,7 +107,7 @@ in
# https://www.home-assistant.io/integrations/logger/
logger = {
- default = "info";
+ default = "debug";
};
};
diff --git a/nixos/tests/homebox.nix b/nixos/tests/homebox.nix
index 2d14a153c976..aadcd4269774 100644
--- a/nixos/tests/homebox.nix
+++ b/nixos/tests/homebox.nix
@@ -8,19 +8,36 @@ import ./make-test-python.nix (
meta = with pkgs.lib.maintainers; {
maintainers = [ patrickdag ];
};
- nodes.machine = {
- services.homebox = {
- enable = true;
- settings.HBOX_WEB_PORT = port;
- };
- };
- testScript = ''
- machine.wait_for_unit("homebox.service")
- machine.wait_for_open_port(${port})
+ nodes =
+ let
+ self = {
+ simple = {
+ services.homebox = {
+ enable = true;
+ settings.HBOX_WEB_PORT = port;
+ };
+ };
- machine.succeed("curl --fail -X GET 'http://localhost:${port}/'")
- out = machine.succeed("curl --fail 'http://localhost:${port}/api/v1/status'")
- assert '"health":true' in out
+ postgres = {
+ imports = [ self.simple ];
+ services.homebox.database.createLocally = true;
+ };
+ };
+ in
+ self;
+ testScript = ''
+ def test_homebox(node):
+ node.wait_for_unit("homebox.service")
+ node.wait_for_open_port(${port})
+
+ node.succeed("curl --fail -X GET 'http://localhost:${port}/'")
+ out = node.succeed("curl --fail 'http://localhost:${port}/api/v1/status'")
+ assert '"health":true' in out
+
+ test_homebox(simple)
+ simple.send_monitor_command("quit")
+ simple.wait_for_shutdown()
+ test_homebox(postgres)
'';
}
)
diff --git a/nixos/tests/homepage-dashboard.nix b/nixos/tests/homepage-dashboard.nix
index ed70ead01820..d654dcaf53df 100644
--- a/nixos/tests/homepage-dashboard.nix
+++ b/nixos/tests/homepage-dashboard.nix
@@ -1,28 +1,26 @@
-import ./make-test-python.nix (
- { lib, ... }:
- {
- name = "homepage-dashboard";
- meta.maintainers = with lib.maintainers; [ jnsgruk ];
+{ lib, ... }:
+{
+ name = "homepage-dashboard";
+ meta.maintainers = with lib.maintainers; [ jnsgruk ];
- nodes.machine = _: {
- services.homepage-dashboard = {
- enable = true;
- settings.title = "test title rodUsEagid"; # something random/unique
- };
+ nodes.machine = _: {
+ services.homepage-dashboard = {
+ enable = true;
+ settings.title = "test title rodUsEagid"; # something random/unique
};
+ };
- testScript = ''
- # Ensure the services are started on managed machine
- machine.wait_for_unit("homepage-dashboard.service")
- machine.wait_for_open_port(8082)
- machine.succeed("curl --fail http://localhost:8082/")
+ testScript = ''
+ # Ensure the services are started on managed machine
+ machine.wait_for_unit("homepage-dashboard.service")
+ machine.wait_for_open_port(8082)
+ machine.succeed("curl --fail http://localhost:8082/")
- # Ensure /etc/homepage-dashboard is created.
- machine.succeed("test -d /etc/homepage-dashboard")
+ # Ensure /etc/homepage-dashboard is created.
+ machine.succeed("test -d /etc/homepage-dashboard")
- # Ensure that we see the custom title *only in the managed config*
- page = machine.succeed("curl --fail http://localhost:8082/")
- assert "test title rodUsEagid" in page, "Custom title not found"
- '';
- }
-)
+ # Ensure that we see the custom title *only in the managed config*
+ page = machine.succeed("curl --fail http://localhost:8082/")
+ assert "test title rodUsEagid" in page, "Custom title not found"
+ '';
+}
diff --git a/nixos/tests/i18n.nix b/nixos/tests/i18n.nix
new file mode 100644
index 000000000000..a0493b7c62f5
--- /dev/null
+++ b/nixos/tests/i18n.nix
@@ -0,0 +1,43 @@
+{ lib, ... }:
+{
+ name = "glibLocales-custom-builds";
+ meta.maintainers = with lib.maintainers; [ doronbehar ];
+
+ nodes = {
+ nonUTF8Charset = {
+ i18n = {
+ defaultLocale = "en_US";
+ defaultCharset = "ISO-8859-1";
+ };
+ };
+ extraLocales1 = {
+ i18n = {
+ defaultLocale = "en_US.UTF-8";
+ extraLocales = [
+ "nl_NL.UTF-8/UTF-8"
+ ];
+ };
+ };
+ extraLocaleSettings = {
+ i18n = {
+ defaultLocale = "en_US.UTF-8";
+ extraLocaleSettings = {
+ LC_MESSAGES = "en_US.UTF-8";
+ LC_TIME = "de_DE.UTF-8";
+ };
+ };
+ };
+ localeCharsets = {
+ i18n = {
+ defaultLocale = "en_US.UTF-8";
+ extraLocaleSettings = {
+ LC_TIME = "de_DE";
+ };
+ localeCharsets = {
+ LC_TIME = "ISO-8859-1";
+ };
+ };
+ };
+ };
+ testScript = { nodes, ... }: "";
+}
diff --git a/nixos/tests/image-contents.nix b/nixos/tests/image-contents.nix
index 473032cec73e..bfa4f08adc40 100644
--- a/nixos/tests/image-contents.nix
+++ b/nixos/tests/image-contents.nix
@@ -14,7 +14,7 @@ with import common/ec2.nix { inherit makeTest pkgs; };
let
config =
(import ../lib/eval-config.nix {
- inherit system;
+ system = null;
modules = [
../modules/testing/test-instrumentation.nix
../modules/profiles/qemu-guest.nix
@@ -22,6 +22,7 @@ let
fileSystems."/".device = "/dev/disk/by-label/nixos";
boot.loader.grub.device = "/dev/vda";
boot.loader.timeout = 0;
+ nixpkgs.pkgs = pkgs;
}
];
}).config;
diff --git a/nixos/tests/incus/ui.nix b/nixos/tests/incus/ui.nix
index 27b90c218d78..300388a19f93 100644
--- a/nixos/tests/incus/ui.nix
+++ b/nixos/tests/incus/ui.nix
@@ -66,6 +66,7 @@ import ../make-test-python.nix (
testScript = ''
machine.wait_for_unit("incus.service")
+ machine.wait_for_unit("incus-preseed.service")
# Check that the INCUS_UI environment variable is populated in the systemd unit
machine.succeed("systemctl cat incus.service | grep 'INCUS_UI'")
@@ -73,6 +74,9 @@ import ../make-test-python.nix (
# Ensure the endpoint returns an HTML page with 'Incus UI' in the title
machine.succeed("curl -kLs https://localhost:8443/ui | grep 'Incus UI '")
+ # Ensure the documentation is rendering correctly
+ machine.succeed("curl -kLs https://localhost:8443/documentation/ | grep 'Incus documentation '")
+
# Ensure the application is actually rendered by the Javascript
machine.succeed("PYTHONUNBUFFERED=1 selenium-script")
'';
diff --git a/nixos/tests/installed-tests/geocode-glib.nix b/nixos/tests/installed-tests/geocode-glib.nix
index 76a32ee2849a..3fc5dc313a24 100644
--- a/nixos/tests/installed-tests/geocode-glib.nix
+++ b/nixos/tests/installed-tests/geocode-glib.nix
@@ -11,5 +11,5 @@ makeInstalledTest {
];
};
- tested = pkgs.geocode-glib;
+ tested = pkgs.geocode-glib_2;
}
diff --git a/nixos/tests/installed-tests/libxmlb.nix b/nixos/tests/installed-tests/libxmlb.nix
index af2bbe9c35e2..f45b97e8341f 100644
--- a/nixos/tests/installed-tests/libxmlb.nix
+++ b/nixos/tests/installed-tests/libxmlb.nix
@@ -2,4 +2,10 @@
makeInstalledTest {
tested = pkgs.libxmlb;
+
+ testConfig = {
+ environment.variables = {
+ G_TEST_SRCDIR = "${pkgs.libxmlb.installedTests}/libexec/installed-tests/libxmlb";
+ };
+ };
}
diff --git a/nixos/tests/installer.nix b/nixos/tests/installer.nix
index 6be3346d9850..6c7b97d055bc 100644
--- a/nixos/tests/installer.nix
+++ b/nixos/tests/installer.nix
@@ -632,31 +632,30 @@ let
grubUseEfi ? false,
enableOCR ? false,
meta ? { },
+ passthru ? { },
testSpecialisationConfig ? false,
testFlakeSwitch ? false,
testByAttrSwitch ? false,
clevisTest ? false,
clevisFallbackTest ? false,
disableFileSystems ? false,
+ selectNixPackage ? pkgs: pkgs.nixVersions.stable,
}:
let
isEfi = bootLoader == "systemd-boot" || (bootLoader == "grub" && grubUseEfi);
in
makeTest {
- inherit enableOCR;
+ inherit enableOCR passthru;
name = "installer-" + name;
meta = {
# put global maintainers here, individuals go into makeInstallerTest fkt call
maintainers = (meta.maintainers or [ ]);
# non-EFI tests can only run on x86
- platforms =
- if isEfi then
- platforms.linux
- else
- [
- "x86_64-linux"
- "i686-linux"
- ];
+ platforms = mkIf (!isEfi) [
+ "x86_64-linux"
+ "x86_64-darwin"
+ "i686-linux"
+ ];
};
nodes =
let
@@ -679,94 +678,102 @@ let
in
{
# The configuration of the system used to run "nixos-install".
- installer = {
- imports = [
- commonConfig
- ../modules/profiles/installation-device.nix
- ../modules/profiles/base.nix
- extraInstallerConfig
- ./common/auto-format-root-device.nix
- ];
+ installer =
+ { config, pkgs, ... }:
+ {
+ imports = [
+ commonConfig
+ ../modules/profiles/installation-device.nix
+ ../modules/profiles/base.nix
+ extraInstallerConfig
+ ./common/auto-format-root-device.nix
+ ];
- # In systemdStage1, also automatically format the device backing the
- # root filesystem.
- virtualisation.fileSystems."/".autoFormat = systemdStage1;
+ # In systemdStage1, also automatically format the device backing the
+ # root filesystem.
+ virtualisation.fileSystems."/".autoFormat = systemdStage1;
- boot.initrd.systemd.enable = systemdStage1;
+ boot.initrd.systemd.enable = systemdStage1;
- # Use a small /dev/vdb as the root disk for the
- # installer. This ensures the target disk (/dev/vda) is
- # the same during and after installation.
- virtualisation.emptyDiskImages = [ 512 ];
- virtualisation.rootDevice = "/dev/vdb";
+ # Use a small /dev/vdb as the root disk for the
+ # installer. This ensures the target disk (/dev/vda) is
+ # the same during and after installation.
+ virtualisation.emptyDiskImages = [ 512 ];
+ virtualisation.rootDevice = "/dev/vdb";
- hardware.enableAllFirmware = mkForce false;
+ nix.package = selectNixPackage pkgs;
+ hardware.enableAllFirmware = mkForce false;
- # The test cannot access the network, so any packages we
- # need must be included in the VM.
- system.extraDependencies =
- with pkgs;
- [
- bintools
- brotli
- brotli.dev
- brotli.lib
- desktop-file-utils
- docbook5
- docbook_xsl_ns
- kbd.dev
- kmod.dev
- libarchive.dev
- libxml2.bin
- libxslt.bin
- nixos-artwork.wallpapers.simple-dark-gray-bottom
- ntp
- perlPackages.ConfigIniFiles
- perlPackages.FileSlurp
- perlPackages.JSON
- perlPackages.ListCompare
- perlPackages.XMLLibXML
- # make-options-doc/default.nix
- (python3.withPackages (p: [ p.mistune ]))
- shared-mime-info
- sudo
- switch-to-configuration-ng
- texinfo
- unionfs-fuse
- xorg.lndir
- shellcheck-minimal
-
- # add curl so that rather than seeing the test attempt to download
- # curl's tarball, we see what it's trying to download
- curl
- ]
- ++ optionals (bootLoader == "grub") (
- let
- zfsSupport = extraInstallerConfig.boot.supportedFilesystems.zfs or false;
- in
+ # The test cannot access the network, so any packages we
+ # need must be included in the VM.
+ system.extraDependencies =
+ with pkgs;
[
- (pkgs.grub2.override { inherit zfsSupport; })
- (pkgs.grub2_efi.override { inherit zfsSupport; })
- pkgs.nixos-artwork.wallpapers.simple-dark-gray-bootloader
- pkgs.perlPackages.FileCopyRecursive
- pkgs.perlPackages.XMLSAX
- pkgs.perlPackages.XMLSAXBase
- ]
- )
- ++ optionals (bootLoader == "systemd-boot") [
- pkgs.zstd.bin
- pkgs.mypy
- pkgs.bootspec
- ]
- ++ optionals clevisTest [ pkgs.klibc ]
- ++ optional systemdStage1 pkgs.chroot-realpath;
+ bintools
+ brotli
+ brotli.dev
+ brotli.lib
+ desktop-file-utils
+ docbook5
+ docbook_xsl_ns
+ kbd.dev
+ kmod.dev
+ libarchive.dev
+ libxml2.bin
+ libxslt.bin
+ nixos-artwork.wallpapers.simple-dark-gray-bottom
+ ntp
+ perlPackages.ConfigIniFiles
+ perlPackages.FileSlurp
+ perlPackages.JSON
+ perlPackages.ListCompare
+ perlPackages.XMLLibXML
+ # make-options-doc/default.nix
+ (python3.withPackages (p: [ p.mistune ]))
+ shared-mime-info
+ sudo
+ switch-to-configuration-ng
+ texinfo
+ unionfs-fuse
+ xorg.lndir
+ shellcheck-minimal
- nix.settings = {
- substituters = mkForce [ ];
- hashed-mirrors = null;
- connect-timeout = 1;
+ # Only the out output is included here, which is what is
+ # required to build the NixOS udev rules
+ # See the comment in services/hardware/udev.nix
+ systemdMinimal.out
+
+ # add curl so that rather than seeing the test attempt to download
+ # curl's tarball, we see what it's trying to download
+ curl
+ ]
+ ++ optionals (bootLoader == "grub") (
+ let
+ zfsSupport = extraInstallerConfig.boot.supportedFilesystems.zfs or false;
+ in
+ [
+ (pkgs.grub2.override { inherit zfsSupport; })
+ (pkgs.grub2_efi.override { inherit zfsSupport; })
+ pkgs.nixos-artwork.wallpapers.simple-dark-gray-bootloader
+ pkgs.perlPackages.FileCopyRecursive
+ pkgs.perlPackages.XMLSAX
+ pkgs.perlPackages.XMLSAXBase
+ ]
+ )
+ ++ optionals (bootLoader == "systemd-boot") [
+ pkgs.zstd.bin
+ pkgs.mypy
+ config.boot.bootspec.package
+ ]
+ ++ optionals clevisTest [ pkgs.klibc ]
+ ++ optional systemdStage1 pkgs.chroot-realpath;
+
+ nix.settings = {
+ substituters = mkForce [ ];
+ hashed-mirrors = null;
+ connect-timeout = 1;
+ };
};
- };
target = {
imports = [ commonConfig ];
@@ -1100,7 +1107,12 @@ in
# The (almost) simplest partitioning scheme: a swap partition and
# one big filesystem partition.
- simple = makeInstallerTest "simple" simple-test-config;
+ simple = makeInstallerTest "simple" (
+ simple-test-config
+ // {
+ passthru.override = args: makeInstallerTest "simple" simple-test-config // args;
+ }
+ );
switchToFlake = makeInstallerTest "switch-to-flake" simple-test-config-flake;
diff --git a/nixos/tests/k3s/airgap-images.nix b/nixos/tests/k3s/airgap-images.nix
index ade04c99840a..84a535feb7fc 100644
--- a/nixos/tests/k3s/airgap-images.nix
+++ b/nixos/tests/k3s/airgap-images.nix
@@ -31,7 +31,7 @@ import ../make-test-python.nix (
start_all()
machine.wait_for_unit("k3s")
- machine.wait_until_succeeds("journalctl -r --no-pager -u k3s | grep \"Imported images from /var/lib/rancher/k3s/agent/images/\"", timeout=120)
+ machine.wait_until_succeeds("journalctl -r --no-pager -u k3s | grep \"Imported images from /var/lib/rancher/k3s/agent/images/\"")
images = json.loads(machine.succeed("crictl img -o json"))
image_names = [i["repoTags"][0] for i in images["images"]]
with open("${k3s.imagesList}") as expected_images:
diff --git a/nixos/tests/k3s/default.nix b/nixos/tests/k3s/default.nix
index 4ee3cb760b39..5240f029139f 100644
--- a/nixos/tests/k3s/default.nix
+++ b/nixos/tests/k3s/default.nix
@@ -4,9 +4,11 @@
lib ? pkgs.lib,
}:
let
- allK3s = lib.filterAttrs (n: _: lib.strings.hasPrefix "k3s_" n) pkgs;
+ allK3s = lib.filterAttrs (
+ n: _: lib.strings.hasPrefix "k3s_" n && (builtins.tryEval pkgs.${n}).success
+ ) pkgs;
in
-{
+lib.recurseIntoAttrs {
airgap-images = lib.mapAttrs (
_: k3s: import ./airgap-images.nix { inherit system pkgs k3s; }
) allK3s;
diff --git a/nixos/tests/kafka.nix b/nixos/tests/kafka.nix
deleted file mode 100644
index 6bcc03dca95e..000000000000
--- a/nixos/tests/kafka.nix
+++ /dev/null
@@ -1,138 +0,0 @@
-{
- system ? builtins.currentSystem,
- config ? { },
- pkgs ? import ../.. { inherit system config; },
-}:
-
-with pkgs.lib;
-
-let
- makeKafkaTest =
- name:
- {
- kafkaPackage,
- mode ? "kraft",
- }:
- (import ./make-test-python.nix ({
- inherit name;
- meta = with pkgs.lib.maintainers; {
- maintainers = [ nequissimus ];
- };
-
- nodes =
- {
- kafka =
- { ... }:
- {
- services.apache-kafka = mkMerge [
- ({
- enable = true;
- package = kafkaPackage;
- settings = {
- "offsets.topic.replication.factor" = 1;
- "log.dirs" = [
- "/var/lib/kafka/logdir1"
- "/var/lib/kafka/logdir2"
- ];
- };
- })
- (mkIf (mode == "zookeeper") {
- settings = {
- "zookeeper.session.timeout.ms" = 600000;
- "zookeeper.connect" = [ "zookeeper1:2181" ];
- };
- })
- (mkIf (mode == "kraft") {
- clusterId = "ak2fIHr4S8WWarOF_ODD0g";
- formatLogDirs = true;
- settings = {
- "node.id" = 1;
- "process.roles" = [
- "broker"
- "controller"
- ];
- "listeners" = [
- "PLAINTEXT://:9092"
- "CONTROLLER://:9093"
- ];
- "listener.security.protocol.map" = [
- "PLAINTEXT:PLAINTEXT"
- "CONTROLLER:PLAINTEXT"
- ];
- "controller.quorum.voters" = [
- "1@kafka:9093"
- ];
- "controller.listener.names" = [ "CONTROLLER" ];
- };
- })
- ];
-
- networking.firewall.allowedTCPPorts = [
- 9092
- 9093
- ];
- # i686 tests: qemu-system-i386 can simulate max 2047MB RAM (not 2048)
- virtualisation.memorySize = 2047;
- };
- }
- // optionalAttrs (mode == "zookeeper") {
- zookeeper1 =
- { ... }:
- {
- services.zookeeper = {
- enable = true;
- };
-
- networking.firewall.allowedTCPPorts = [ 2181 ];
- };
- };
-
- testScript = ''
- start_all()
-
- ${optionalString (mode == "zookeeper") ''
- zookeeper1.wait_for_unit("default.target")
- zookeeper1.wait_for_unit("zookeeper.service")
- zookeeper1.wait_for_open_port(2181)
- ''}
-
- kafka.wait_for_unit("default.target")
- kafka.wait_for_unit("apache-kafka.service")
- kafka.wait_for_open_port(9092)
-
- kafka.wait_until_succeeds(
- "${kafkaPackage}/bin/kafka-topics.sh --create "
- + "--bootstrap-server localhost:9092 --partitions 1 "
- + "--replication-factor 1 --topic testtopic"
- )
- kafka.succeed(
- "echo 'test 1' | "
- + "${kafkaPackage}/bin/kafka-console-producer.sh "
- + "--bootstrap-server localhost:9092 --topic testtopic"
- )
- assert "test 1" in kafka.succeed(
- "${kafkaPackage}/bin/kafka-console-consumer.sh "
- + "--bootstrap-server localhost:9092 --topic testtopic "
- + "--from-beginning --max-messages 1"
- )
- '';
- }) { inherit system; });
-
-in
-with pkgs;
-{
- kafka_3_7 = makeKafkaTest "kafka_3_7" {
- kafkaPackage = apacheKafka_3_7;
- mode = "zookeeper";
- };
- kafka_3_8 = makeKafkaTest "kafka_3_8" {
- kafkaPackage = apacheKafka_3_8;
- mode = "zookeeper";
- };
- kafka_3_9 = makeKafkaTest "kafka_3_9" {
- kafkaPackage = apacheKafka_3_9;
- mode = "zookeeper";
- };
- kafka_4_0 = makeKafkaTest "kafka_4_0" { kafkaPackage = apacheKafka_4_0; };
- kafka = makeKafkaTest "kafka" { kafkaPackage = apacheKafka; };
-}
diff --git a/nixos/tests/kafka/base.nix b/nixos/tests/kafka/base.nix
new file mode 100644
index 000000000000..80121a722465
--- /dev/null
+++ b/nixos/tests/kafka/base.nix
@@ -0,0 +1,136 @@
+{ pkgs, ... }:
+
+with pkgs.lib;
+
+let
+ makeKafkaTest =
+ name:
+ {
+ kafkaPackage,
+ mode ? "kraft",
+ }:
+ (import ../make-test-python.nix ({
+ inherit name;
+ meta = with pkgs.lib.maintainers; {
+ maintainers = [ nequissimus ];
+ };
+
+ nodes =
+ {
+ kafka =
+ { ... }:
+ {
+ services.apache-kafka = mkMerge [
+ ({
+ enable = true;
+ package = kafkaPackage;
+ settings = {
+ "offsets.topic.replication.factor" = 1;
+ "log.dirs" = [
+ "/var/lib/kafka/logdir1"
+ "/var/lib/kafka/logdir2"
+ ];
+ };
+ })
+ (mkIf (mode == "zookeeper") {
+ settings = {
+ "zookeeper.session.timeout.ms" = 600000;
+ "zookeeper.connect" = [ "zookeeper1:2181" ];
+ };
+ })
+ (mkIf (mode == "kraft") {
+ clusterId = "ak2fIHr4S8WWarOF_ODD0g";
+ formatLogDirs = true;
+ settings = {
+ "node.id" = 1;
+ "process.roles" = [
+ "broker"
+ "controller"
+ ];
+ "listeners" = [
+ "PLAINTEXT://:9092"
+ "CONTROLLER://:9093"
+ ];
+ "listener.security.protocol.map" = [
+ "PLAINTEXT:PLAINTEXT"
+ "CONTROLLER:PLAINTEXT"
+ ];
+ "controller.quorum.voters" = [
+ "1@kafka:9093"
+ ];
+ "controller.listener.names" = [ "CONTROLLER" ];
+ };
+ })
+ ];
+
+ networking.firewall.allowedTCPPorts = [
+ 9092
+ 9093
+ ];
+ virtualisation.diskSize = 1024;
+ # i686 tests: qemu-system-i386 can simulate max 2047MB RAM (not 2048)
+ virtualisation.memorySize = 2047;
+ };
+ }
+ // optionalAttrs (mode == "zookeeper") {
+ zookeeper1 =
+ { ... }:
+ {
+ services.zookeeper = {
+ enable = true;
+ };
+
+ networking.firewall.allowedTCPPorts = [ 2181 ];
+ virtualisation.diskSize = 1024;
+ };
+ };
+
+ testScript = ''
+ start_all()
+
+ ${optionalString (mode == "zookeeper") ''
+ zookeeper1.wait_for_unit("default.target")
+ zookeeper1.wait_for_unit("zookeeper.service")
+ zookeeper1.wait_for_open_port(2181)
+ ''}
+
+ kafka.wait_for_unit("default.target")
+ kafka.wait_for_unit("apache-kafka.service")
+ kafka.wait_for_open_port(9092)
+
+ kafka.wait_until_succeeds(
+ "${kafkaPackage}/bin/kafka-topics.sh --create "
+ + "--bootstrap-server localhost:9092 --partitions 1 "
+ + "--replication-factor 1 --topic testtopic"
+ )
+ kafka.succeed(
+ "echo 'test 1' | "
+ + "${kafkaPackage}/bin/kafka-console-producer.sh "
+ + "--bootstrap-server localhost:9092 --topic testtopic"
+ )
+ assert "test 1" in kafka.succeed(
+ "${kafkaPackage}/bin/kafka-console-consumer.sh "
+ + "--bootstrap-server localhost:9092 --topic testtopic "
+ + "--from-beginning --max-messages 1"
+ )
+ '';
+ }));
+
+in
+with pkgs;
+{
+ kafka_3_7 = makeKafkaTest "kafka_3_7" {
+ kafkaPackage = apacheKafka_3_7;
+ mode = "zookeeper";
+ };
+ kafka_3_8 = makeKafkaTest "kafka_3_8" {
+ kafkaPackage = apacheKafka_3_8;
+ mode = "zookeeper";
+ };
+ kafka_3_9 = makeKafkaTest "kafka_3_9" {
+ kafkaPackage = apacheKafka_3_9;
+ mode = "zookeeper";
+ };
+ kafka_4_0 = makeKafkaTest "kafka_4_0" { kafkaPackage = apacheKafka_4_0; };
+ kafka = makeKafkaTest "kafka" { kafkaPackage = apacheKafka; };
+}
diff --git a/nixos/tests/kafka/cluster.nix b/nixos/tests/kafka/cluster.nix
new file mode 100644
index 000000000000..ace64a3d3da2
--- /dev/null
+++ b/nixos/tests/kafka/cluster.nix
@@ -0,0 +1,199 @@
+import ../make-test-python.nix (
+ { lib, pkgs, ... }:
+
+ let
+ inherit (lib) mkMerge;
+
+ # Generate with `kafka-storage.sh random-uuid`
+ clusterId = "ii5pZE5LRkSeWrnyBhMOYQ";
+
+ kafkaConfig = {
+ networking.firewall.allowedTCPPorts = [
+ 9092
+ 9093
+ ];
+
+ virtualisation.diskSize = 1024;
+ virtualisation.memorySize = 1024 * 2;
+
+ environment.systemPackages = [ pkgs.apacheKafka ];
+
+ services.apache-kafka = {
+ enable = true;
+
+ clusterId = "${clusterId}";
+
+ formatLogDirs = true;
+
+ settings = {
+ listeners = [
+ "PLAINTEXT://:9092"
+ "CONTROLLER://:9093"
+ ];
+ "listener.security.protocol.map" = [
+ "PLAINTEXT:PLAINTEXT"
+ "CONTROLLER:PLAINTEXT"
+ ];
+ "controller.quorum.voters" = lib.imap1 (i: name: "${toString i}@${name}:9093") (
+ builtins.attrNames kafkaNodes
+ );
+ "controller.listener.names" = [ "CONTROLLER" ];
+
+ "process.roles" = [
+ "broker"
+ "controller"
+ ];
+
+ "log.dirs" = [ "/var/lib/apache-kafka" ];
+ "num.partitions" = 6;
+ "offsets.topic.replication.factor" = 2;
+ "transaction.state.log.replication.factor" = 2;
+ "transaction.state.log.min.isr" = 2;
+ };
+ };
+
+ systemd.services.apache-kafka = {
+ after = [ "network-online.target" ];
+ requires = [ "network-online.target" ];
+ serviceConfig.StateDirectory = "apache-kafka";
+ };
+ };
+
+ extraKafkaConfig = {
+ kafka1 = {
+ services.apache-kafka.settings = {
+ "node.id" = 1;
+ "broker.rack" = 1;
+ };
+ };
+
+ kafka2 = {
+ services.apache-kafka.settings = {
+ "node.id" = 2;
+ "broker.rack" = 2;
+ };
+ };
+
+ kafka3 = {
+ services.apache-kafka.settings = {
+ "node.id" = 3;
+ "broker.rack" = 3;
+ };
+ };
+
+ kafka4 = {
+ services.apache-kafka.settings = {
+ "node.id" = 4;
+ "broker.rack" = 3;
+ };
+ };
+ };
+
+ kafkaNodes = builtins.mapAttrs (
+ _: val:
+ mkMerge [
+ val
+ kafkaConfig
+ ]
+ ) extraKafkaConfig;
+ in
+ {
+ name = "kafka-cluster";
+ meta = with pkgs.lib.maintainers; {
+ maintainers = [ jpds ];
+ };
+
+ nodes = {
+ inherit (kafkaNodes)
+ kafka1
+ kafka2
+ kafka3
+ kafka4
+ ;
+
+ client =
+ { config, ... }:
+ {
+ environment.systemPackages = [ pkgs.apacheKafka ];
+ virtualisation.diskSize = 1024;
+ };
+ };
+
+ testScript = ''
+ import json
+
+ for machine in kafka1, kafka2, kafka3, kafka4:
+ machine.wait_for_unit("apache-kafka")
+
+ for machine in kafka1, kafka2, kafka3, kafka4:
+ machine.wait_for_open_port(9092)
+ machine.wait_for_open_port(9093)
+
+ machine.wait_until_succeeds(
+ "journalctl -o cat -u apache-kafka.service | grep 'Transition from STARTING to STARTED'"
+ )
+
+ machine.wait_until_succeeds(
+ "journalctl -o cat -u apache-kafka.service | grep 'Kafka Server started'"
+ )
+
+ machine.wait_until_succeeds(
+ "journalctl -o cat -u apache-kafka.service | grep 'BrokerLifecycleManager' | grep 'Incarnation [[:graph:]]\+ of broker [[:digit:]] in cluster ${clusterId}'"
+ )
+
+ current_voters_json = kafka1.wait_until_succeeds(
+ "kafka-metadata-quorum.sh --bootstrap-server kafka1:9092,kafka2:9092,kafka3:9092 describe --status | grep CurrentVoters"
+ ).replace("CurrentVoters:", "")
+
+ voters = json.loads(current_voters_json)
+
+ assert len(voters) == 4
+
+ kafka1.wait_until_succeeds(
+ "kafka-topics.sh --bootstrap-server kafka1:9092 --create --topic test-123 --replication-factor 2"
+ )
+
+ for machine in kafka1, kafka2, kafka3, kafka4:
+ machine.wait_until_succeeds(
+ "journalctl -o cat -u apache-kafka.service | grep -E 'Created log for partition test-123-[[:digit:]] in /var/lib/apache-kafka/test-123-[[:digit:]] with properties'"
+ )
+
+ kafka1.wait_until_succeeds(
+ "kafka-topics.sh --bootstrap-server=kafka1:9092 --describe --topic test-123 | "
+ + "grep 'PartitionCount: 6'"
+ )
+
+ # Should never see a replica on both 3 and 4 as they're in the same rack
+ kafka1.fail(
+ "kafka-topics.sh --bootstrap-server=kafka1:9092 --describe --topic test-123 | "
+ + "grep -E 'Replicas: (3,4|4,3)'"
+ )
+
+ client.succeed(
+ "echo 'test 2' | "
+ + "kafka-console-producer.sh "
+ + "--bootstrap-server kafka1:9092 "
+ + "--topic test-123"
+ )
+ assert "test 2" in client.succeed(
+ "kafka-console-consumer.sh "
+ + "--bootstrap-server kafka2:9092 --topic test-123 "
+ + "--group readtest "
+ + "--from-beginning --max-messages 1"
+ )
+
+ client.succeed(
+ "echo 'test 3' | "
+ + "kafka-console-producer.sh "
+ + "--bootstrap-server kafka2:9092 "
+ + "--topic test-123"
+ )
+ assert "test 3" in client.succeed(
+ "kafka-console-consumer.sh "
+ + "--bootstrap-server kafka3:9092 --topic test-123 "
+ + "--group readtest "
+ + "--max-messages 1"
+ )
+ '';
+ }
+)
diff --git a/nixos/tests/kafka/default.nix b/nixos/tests/kafka/default.nix
new file mode 100644
index 000000000000..1056117a9a10
--- /dev/null
+++ b/nixos/tests/kafka/default.nix
@@ -0,0 +1,11 @@
+{
+ system ? builtins.currentSystem,
+ config ? { },
+ pkgs ? import ../../.. { inherit system config; },
+}:
+
+{
+ base = import ./base.nix { inherit system pkgs; };
+ cluster = import ./cluster.nix { inherit system pkgs; };
+ mirrormaker = import ./mirrormaker.nix { inherit system pkgs; };
+}
diff --git a/nixos/tests/kafka/mirrormaker.nix b/nixos/tests/kafka/mirrormaker.nix
new file mode 100644
index 000000000000..623f729d9fab
--- /dev/null
+++ b/nixos/tests/kafka/mirrormaker.nix
@@ -0,0 +1,240 @@
+import ../make-test-python.nix (
+ { lib, pkgs, ... }:
+
+ let
+ inherit (lib) mkMerge;
+
+ # Generate with `kafka-storage.sh random-uuid`
+ clusterAId = "ihzlrasUQ9O3Yy0ZWYkd6w";
+
+ clusterBId = "Bnu_zrzKRH6-7KcK7t3I5Q";
+
+ kafkaConfig = {
+ networking.firewall.allowedTCPPorts = [
+ 9092
+ 9093
+ ];
+
+ virtualisation.diskSize = 1024;
+ virtualisation.memorySize = 1024 * 2;
+
+ environment.systemPackages = [ pkgs.apacheKafka ];
+
+ services.apache-kafka = {
+ enable = true;
+
+ formatLogDirs = true;
+
+ settings = {
+ listeners = [
+ "PLAINTEXT://:9092"
+ "CONTROLLER://:9093"
+ ];
+ "listener.security.protocol.map" = [
+ "PLAINTEXT:PLAINTEXT"
+ "CONTROLLER:PLAINTEXT"
+ ];
+ "controller.listener.names" = [ "CONTROLLER" ];
+
+ "process.roles" = [
+ "broker"
+ "controller"
+ ];
+
+ "log.dirs" = [ "/var/lib/apache-kafka" ];
+ "num.partitions" = 1;
+ "offsets.topic.replication.factor" = 1;
+ "transaction.state.log.replication.factor" = 1;
+ "transaction.state.log.min.isr" = 1;
+ };
+ };
+
+ systemd.services.apache-kafka = {
+ after = [ "network-online.target" ];
+ requires = [ "network-online.target" ];
+ serviceConfig.StateDirectory = "apache-kafka";
+ };
+ };
+
+ extraKafkaConfig = {
+ kafkaa1 = {
+ services.apache-kafka = {
+ clusterId = "${clusterAId}";
+
+ settings = {
+ "node.id" = 1;
+ "controller.quorum.voters" = [ "1@kafkaa1:9093" ];
+ };
+ };
+ };
+
+ kafkab1 = {
+ services.apache-kafka = {
+ clusterId = "${clusterBId}";
+
+ settings = {
+ "node.id" = 1;
+ "controller.quorum.voters" = [ "1@kafkab1:9093" ];
+ };
+ };
+ };
+ };
+
+ kafkaNodes = builtins.mapAttrs (
+ _: val:
+ mkMerge [
+ val
+ kafkaConfig
+ ]
+ ) extraKafkaConfig;
+
+ mirrorMakerProperties = pkgs.writeText "mm2.properties" ''
+ name = A->B
+
+ clusters = A, B
+
+ A.bootstrap.servers = kafkaa1:9092
+ B.bootstrap.servers = kafkab1:9092
+
+ A->B.enabled = true
+ A->B.topics = .*
+
+ B->A.enabled = false
+ B->A.topics = .*
+
+ replication.factor=1
+ replication.policy.class=org.apache.kafka.connect.mirror.IdentityReplicationPolicy
+
+ tasks.max = 2
+ refresh.topics.enabled = true
+ refresh.topics.interval.seconds = 5
+ sync.topic.configs.enabled = true
+
+ checkpoints.topic.replication.factor=1
+ heartbeats.topic.replication.factor=1
+ offset-syncs.topic.replication.factor=1
+
+ offset.storage.replication.factor=1
+ status.storage.replication.factor=1
+ config.storage.replication.factor=1
+
+ emit.checkpoints.enabled = true
+ emit.checkpoints.interval.seconds = 5
+ '';
+ in
+ {
+ name = "kafka-mirrormaker";
+ meta = with pkgs.lib.maintainers; {
+ maintainers = [ jpds ];
+ };
+
+ nodes = {
+ inherit (kafkaNodes) kafkaa1 kafkab1;
+
+ mirrormaker =
+ { config, ... }:
+ {
+ virtualisation.diskSize = 1024;
+ virtualisation.memorySize = 1024 * 2;
+
+ # Define a mirrormaker systemd service
+ systemd.services.kafka-connect-mirror-maker = {
+ after = [ "network-online.target" ];
+ requires = [ "network-online.target" ];
+ wantedBy = [ "multi-user.target" ];
+
+ serviceConfig = {
+ ExecStart = ''
+ ${pkgs.apacheKafka}/bin/connect-mirror-maker.sh ${mirrorMakerProperties}
+ '';
+ Restart = "on-failure";
+ RestartSec = "5s";
+ };
+ };
+ };
+ };
+
+ testScript = ''
+ import json
+
+ for machine in kafkaa1, kafkab1:
+ machine.wait_for_unit("apache-kafka")
+
+ for machine in kafkaa1, kafkab1:
+ machine.wait_for_open_port(9092)
+ machine.wait_for_open_port(9093)
+
+ machine.wait_until_succeeds(
+ "journalctl -o cat -u apache-kafka.service | grep 'Transition from STARTING to STARTED'"
+ )
+
+ machine.wait_until_succeeds(
+ "journalctl -o cat -u apache-kafka.service | grep 'Kafka Server started'"
+ )
+
+ for machine in kafkaa1, kafkab1:
+ current_voters_json = machine.wait_until_succeeds(
+ f"kafka-metadata-quorum.sh --bootstrap-server {machine.name}:9092 describe --status | grep CurrentVoters"
+ ).replace("CurrentVoters:", "")
+
+ voters = json.loads(current_voters_json)
+
+ assert len(voters) == 1
+
+ mirrormaker.wait_for_unit("kafka-connect-mirror-maker")
+
+ mirrormaker.wait_until_succeeds(
+ "journalctl -o cat -u kafka-connect-mirror-maker.service | grep 'Kafka MirrorMaker initializing'"
+ )
+ mirrormaker.wait_until_succeeds(
+ "journalctl -o cat -u kafka-connect-mirror-maker.service | grep 'Targeting clusters \[A, B\]'"
+ )
+ mirrormaker.wait_until_succeeds(
+ "journalctl -o cat -u kafka-connect-mirror-maker.service | grep 'INFO \[Worker clientId=A->B, groupId=A-mm2\] Finished starting connectors and tasks'"
+ )
+
+ mirrormaker.wait_until_succeeds(
+ """
+ journalctl -o cat -u kafka-connect-mirror-maker.service | grep 'INFO \[MirrorSourceConnector\|task-0\] \[Producer clientId=A->B\|A->B-0\|offset-syncs-source-producer\] Cluster ID: ${clusterAId}'
+ """
+ )
+
+ kafkaa1.wait_until_succeeds(
+ "journalctl -o cat -u apache-kafka.service | grep 'Stabilized group B-mm2'"
+ )
+
+ kafkab1.wait_until_succeeds(
+ "journalctl -o cat -u apache-kafka.service | grep 'Stabilized group A-mm2'"
+ )
+
+ kafkaa1.wait_until_succeeds(
+ "kafka-topics.sh --bootstrap-server localhost:9092 --create --topic test-mm-1 --partitions 1 --replication-factor 1"
+ )
+
+ for machine in kafkaa1, kafkab1:
+ machine.succeed(
+ "kafka-topics.sh --bootstrap-server localhost:9092 --list | grep 'test-mm-1'"
+ )
+
+ mirrormaker.wait_until_succeeds(
+ "journalctl -o cat -u kafka-connect-mirror-maker.service | grep 'replicating [[:digit:]]\+ topic-partitions A->B: \[test-mm-1-0\]'"
+ )
+
+ mirrormaker.wait_until_succeeds(
+ "journalctl -o cat -u kafka-connect-mirror-maker.service | grep 'Found [[:digit:]]\+ new topic-partitions on A'"
+ )
+
+ kafkaa1.wait_until_succeeds(
+ "kafka-verifiable-producer.sh --bootstrap-server kafkaa1:9092 --throughput 10 --max-messages 100 --topic test-mm-1"
+ )
+
+ mirrormaker.wait_until_succeeds(
+ "journalctl -o cat -u kafka-connect-mirror-maker.service | grep 'Committing offsets for [[:digit:]]\+ acknowledged messages'"
+ )
+
+ kafkab1.wait_until_succeeds(
+ "kafka-verifiable-consumer.sh --bootstrap-server kafkab1:9092 --topic test-mm-1 --group-id testreplication --max-messages 100"
+ )
+ '';
+ }
+)
diff --git a/nixos/tests/kanidm-provisioning.nix b/nixos/tests/kanidm-provisioning.nix
index 16e39dba729c..8f0ca0ec0859 100644
--- a/nixos/tests/kanidm-provisioning.nix
+++ b/nixos/tests/kanidm-provisioning.nix
@@ -23,7 +23,7 @@ import ./make-test-python.nix (
{ pkgs, lib, ... }:
{
services.kanidm = {
- package = pkgs.kanidmWithSecretProvisioning;
+ package = pkgs.kanidmWithSecretProvisioning_1_6;
enableServer = true;
serverSettings = {
origin = "https://${serverDomain}";
diff --git a/nixos/tests/kanidm.nix b/nixos/tests/kanidm.nix
index b3b4e0213cbf..69dac0de6865 100644
--- a/nixos/tests/kanidm.nix
+++ b/nixos/tests/kanidm.nix
@@ -26,6 +26,7 @@ import ./make-test-python.nix (
{ pkgs, ... }:
{
services.kanidm = {
+ package = pkgs.kanidm_1_6;
enableServer = true;
serverSettings = {
origin = "https://${serverDomain}";
@@ -55,6 +56,7 @@ import ./make-test-python.nix (
{ nodes, ... }:
{
services.kanidm = {
+ package = pkgs.kanidm_1_6;
enableClient = true;
clientSettings = {
uri = "https://${serverDomain}";
diff --git a/nixos/tests/kernel-generic.nix b/nixos/tests/kernel-generic.nix
index 2a1ab38267a3..34e0597777a6 100644
--- a/nixos/tests/kernel-generic.nix
+++ b/nixos/tests/kernel-generic.nix
@@ -42,6 +42,7 @@ let
linux_6_6_hardened
linux_6_12_hardened
linux_6_13_hardened
+ linux_6_14_hardened
linux_rt_5_4
linux_rt_5_10
linux_rt_5_15
diff --git a/nixos/tests/kexec.nix b/nixos/tests/kexec.nix
index 06bd65405f6f..e06d39a8d826 100644
--- a/nixos/tests/kexec.nix
+++ b/nixos/tests/kexec.nix
@@ -1,62 +1,60 @@
-import ./make-test-python.nix (
- { pkgs, lib, ... }:
- {
- name = "kexec";
- meta = with lib.maintainers; {
- maintainers = [
- flokli
- lassulus
- ];
- };
+{ pkgs, lib, ... }:
+{
+ name = "kexec";
+ meta = with lib.maintainers; {
+ maintainers = [
+ flokli
+ lassulus
+ ];
+ };
- nodes = {
- node1 =
- { ... }:
- {
- virtualisation.vlans = [ ];
- virtualisation.memorySize = 4 * 1024;
- };
+ nodes = {
+ node1 =
+ { ... }:
+ {
+ virtualisation.vlans = [ ];
+ virtualisation.memorySize = 4 * 1024;
+ };
- node2 =
- { modulesPath, ... }:
- {
- virtualisation.vlans = [ ];
- environment.systemPackages = [ pkgs.hello ];
- imports = [
- "${modulesPath}/installer/netboot/netboot-minimal.nix"
- "${modulesPath}/testing/test-instrumentation.nix"
- "${modulesPath}/profiles/qemu-guest.nix"
- ];
- };
- };
+ node2 =
+ { modulesPath, ... }:
+ {
+ virtualisation.vlans = [ ];
+ environment.systemPackages = [ pkgs.hello ];
+ imports = [
+ "${modulesPath}/installer/netboot/netboot-minimal.nix"
+ "${modulesPath}/testing/test-instrumentation.nix"
+ "${modulesPath}/profiles/qemu-guest.nix"
+ ];
+ };
+ };
- testScript =
- { nodes, ... }:
- ''
- # Test whether reboot via kexec works.
- node1.wait_for_unit("multi-user.target")
- node1.succeed('kexec --load /run/current-system/kernel --initrd /run/current-system/initrd --command-line "$(&2 &", check_return=False)
- node1.connected = False
- node1.connect()
- node1.wait_for_unit("multi-user.target")
+ testScript =
+ { nodes, ... }:
+ ''
+ # Test whether reboot via kexec works.
+ node1.wait_for_unit("multi-user.target")
+ node1.succeed('kexec --load /run/current-system/kernel --initrd /run/current-system/initrd --command-line "$(&2 &", check_return=False)
+ node1.connected = False
+ node1.connect()
+ node1.wait_for_unit("multi-user.target")
- # Check if the machine with netboot-minimal.nix profile boots up
- node2.wait_for_unit("multi-user.target")
- node2.shutdown()
+ # Check if the machine with netboot-minimal.nix profile boots up
+ node2.wait_for_unit("multi-user.target")
+ node2.shutdown()
- # Kexec node1 to the toplevel of node2 via the kexec-boot script
- node1.succeed('touch /run/foo')
- node1.fail('hello')
- node1.execute('${nodes.node2.system.build.kexecTree}/kexec-boot', check_output=False)
- node1.connected = False
- node1.connect()
- node1.wait_for_unit("multi-user.target")
- node1.succeed('! test -e /run/foo')
- node1.succeed('hello')
- node1.succeed('[ "$(hostname)" = "node2" ]')
+ # Kexec node1 to the toplevel of node2 via the kexec-boot script
+ node1.succeed('touch /run/foo')
+ node1.fail('hello')
+ node1.execute('${nodes.node2.system.build.kexecTree}/kexec-boot', check_output=False)
+ node1.connected = False
+ node1.connect()
+ node1.wait_for_unit("multi-user.target")
+ node1.succeed('! test -e /run/foo')
+ node1.succeed('hello')
+ node1.succeed('[ "$(hostname)" = "node2" ]')
- node1.shutdown()
- '';
- }
-)
+ node1.shutdown()
+ '';
+}
diff --git a/nixos/tests/kimai.nix b/nixos/tests/kimai.nix
index dbe9b834edca..967d531f3c54 100644
--- a/nixos/tests/kimai.nix
+++ b/nixos/tests/kimai.nix
@@ -1,23 +1,21 @@
-import ./make-test-python.nix (
- { lib, ... }:
+{ lib, ... }:
- {
- name = "kimai";
- meta.maintainers = with lib.maintainers; [ peat-psuwit ];
+{
+ name = "kimai";
+ meta.maintainers = with lib.maintainers; [ peat-psuwit ];
- nodes.machine =
- { ... }:
- {
- services.kimai.sites."localhost" = {
- database.createLocally = true;
- };
+ nodes.machine =
+ { ... }:
+ {
+ services.kimai.sites."localhost" = {
+ database.createLocally = true;
};
+ };
- testScript = ''
- machine.wait_for_unit("phpfpm-kimai-localhost.service")
- machine.wait_for_unit("nginx.service")
- machine.wait_for_open_port(80)
- machine.succeed("curl -v --location --fail http://localhost/")
- '';
- }
-)
+ testScript = ''
+ machine.wait_for_unit("phpfpm-kimai-localhost.service")
+ machine.wait_for_unit("nginx.service")
+ machine.wait_for_open_port(80)
+ machine.succeed("curl -v --location --fail http://localhost/")
+ '';
+}
diff --git a/nixos/tests/kismet.nix b/nixos/tests/kismet.nix
new file mode 100644
index 000000000000..878a341cedea
--- /dev/null
+++ b/nixos/tests/kismet.nix
@@ -0,0 +1,266 @@
+{ pkgs, lib, ... }:
+
+let
+ ssid = "Hydra SmokeNet";
+ psk = "stayoffmywifi";
+ wlanInterface = "wlan0";
+in
+{
+ name = "kismet";
+
+ nodes =
+ let
+ hostAddress = id: "192.168.1.${toString (id + 1)}";
+ serverAddress = hostAddress 1;
+ in
+ {
+ airgap =
+ { config, ... }:
+ {
+ networking.interfaces.eth1.ipv4.addresses = lib.mkForce [
+ {
+ address = serverAddress;
+ prefixLength = 24;
+ }
+ ];
+ services.vwifi = {
+ server = {
+ enable = true;
+ ports.tcp = 8212;
+ ports.spy = 8213;
+ openFirewall = true;
+ };
+ };
+ };
+
+ ap =
+ { config, ... }:
+ {
+ networking.interfaces.eth1.ipv4.addresses = lib.mkForce [
+ {
+ address = hostAddress 2;
+ prefixLength = 24;
+ }
+ ];
+ services.hostapd = {
+ enable = true;
+ radios.${wlanInterface} = {
+ channel = 1;
+ networks.${wlanInterface} = {
+ inherit ssid;
+ authentication = {
+ mode = "wpa3-sae";
+ saePasswords = [ { password = psk; } ];
+ enableRecommendedPairwiseCiphers = true;
+ };
+ };
+ };
+ };
+ services.vwifi = {
+ module = {
+ enable = true;
+ macPrefix = "74:F8:F6:00:01";
+ };
+ client = {
+ enable = true;
+ inherit serverAddress;
+ };
+ };
+ };
+
+ station =
+ { config, ... }:
+ {
+ networking.interfaces.eth1.ipv4.addresses = lib.mkForce [
+ {
+ address = hostAddress 3;
+ prefixLength = 24;
+ }
+ ];
+ networking.wireless = {
+ # No, really, we want it enabled!
+ enable = lib.mkOverride 0 true;
+ interfaces = [ wlanInterface ];
+ networks = {
+ ${ssid} = {
+ inherit psk;
+ authProtocols = [ "SAE" ];
+ };
+ };
+ };
+ services.vwifi = {
+ module = {
+ enable = true;
+ macPrefix = "74:F8:F6:00:02";
+ };
+ client = {
+ enable = true;
+ inherit serverAddress;
+ };
+ };
+ };
+
+ monitor =
+ { config, ... }:
+ {
+ networking.interfaces.eth1.ipv4.addresses = lib.mkForce [
+ {
+ address = hostAddress 4;
+ prefixLength = 24;
+ }
+ ];
+
+ services.kismet = {
+ enable = true;
+ serverName = "NixOS Kismet Smoke Test";
+ serverDescription = "Server testing virtual wifi devices running on Hydra";
+ httpd.enable = true;
+ # Check that the settings all eval correctly
+ settings = {
+ # Should append to log_types
+ log_types' = "wiglecsv";
+
+ # Should all generate correctly
+ wepkey = [
+ "00:DE:AD:C0:DE:00"
+ "FEEDFACE42"
+ ];
+ alert = [
+ [
+ "ADHOCCONFLICT"
+ "5/min"
+ "1/sec"
+ ]
+ [
+ "ADVCRYPTCHANGE"
+ "5/min"
+ "1/sec"
+ ]
+ ];
+ gps.gpsd = {
+ host = "localhost";
+ port = 2947;
+ };
+ apspoof.Foo1 = [
+ {
+ ssid = "Bar1";
+ validmacs = [
+ "00:11:22:33:44:55"
+ "aa:bb:cc:dd:ee:ff"
+ ];
+ }
+ {
+ ssid = "Bar2";
+ validmacs = [
+ "01:12:23:34:45:56"
+ "ab:bc:cd:de:ef:f0"
+ ];
+ }
+ ];
+ apspoof.Foo2 = [
+ {
+ ssid = "Bar2";
+ validmacs = [
+ "00:11:22:33:44:55"
+ "aa:bb:cc:dd:ee:ff"
+ ];
+ }
+ ];
+
+ # The actual source
+ source.${wlanInterface} = {
+ name = "Virtual Wifi";
+ };
+ };
+ extraConfig = ''
+ # this comment should be ignored
+ '';
+ };
+
+ services.vwifi = {
+ module = {
+ enable = true;
+ macPrefix = "74:F8:F6:00:03";
+ };
+ client = {
+ enable = true;
+ spy = true;
+ inherit serverAddress;
+ };
+ };
+
+ environment.systemPackages = with pkgs; [
+ config.services.kismet.package
+ config.services.vwifi.package
+ jq
+ ];
+ };
+ };
+
+ testScript =
+ { nodes, ... }:
+ ''
+ import shlex
+
+ # Wait for the vwifi server to come up
+ airgap.start()
+ airgap.wait_for_unit("vwifi-server.service")
+ airgap.wait_for_open_port(${toString nodes.airgap.services.vwifi.server.ports.tcp})
+
+ httpd_port = ${toString nodes.monitor.services.kismet.httpd.port}
+ server_name = "${nodes.monitor.services.kismet.serverName}"
+ server_description = "${nodes.monitor.services.kismet.serverDescription}"
+ wlan_interface = "${wlanInterface}"
+ ap_essid = "${ssid}"
+ ap_mac_prefix = "${nodes.ap.services.vwifi.module.macPrefix}"
+ station_mac_prefix = "${nodes.station.services.vwifi.module.macPrefix}"
+
+ # Spawn the other nodes.
+ monitor.start()
+
+ # Wait for the monitor to come up
+ monitor.wait_for_unit("kismet.service")
+ monitor.wait_for_open_port(httpd_port)
+
+ # Should be up but require authentication.
+ url = f"http://localhost:{httpd_port}"
+ monitor.succeed(f"curl {url} | tee /dev/stderr | grep 'Kismet '")
+
+ # Have to set the password now.
+ monitor.succeed("echo httpd_username=nixos >> ~kismet/.kismet/kismet_httpd.conf")
+ monitor.succeed("echo httpd_password=hydra >> ~kismet/.kismet/kismet_httpd.conf")
+ monitor.systemctl("restart kismet.service")
+ monitor.wait_for_unit("kismet.service")
+ monitor.wait_for_open_port(httpd_port)
+
+ # Authentication should now work.
+ url = f"http://nixos:hydra@localhost:{httpd_port}"
+ monitor.succeed(f"curl {url}/system/status.json | tee /dev/stderr | jq -e --arg serverName {shlex.quote(server_name)} --arg serverDescription {shlex.quote(server_description)} '.\"kismet.system.server_name\" == $serverName and .\"kismet.system.server_description\" == $serverDescription'")
+
+ # Wait for the station to connect to the AP while Kismet is monitoring
+ ap.start()
+ station.start()
+
+ unit = f"wpa_supplicant-{wlan_interface}"
+
+ # Generate handshakes until we detect both devices
+ success = False
+ for i in range(100):
+ station.wait_for_unit(f"wpa_supplicant-{wlan_interface}.service")
+ station.succeed(f"ifconfig {wlan_interface} down && ifconfig {wlan_interface} up")
+ station.wait_until_succeeds(f"journalctl -u {shlex.quote(unit)} -e | grep -Eqi {shlex.quote(wlan_interface + ': CTRL-EVENT-CONNECTED - Connection to ' + ap_mac_prefix + '[0-9a-f:]* completed')}")
+ station.succeed(f"journalctl --rotate --unit={shlex.quote(unit)}")
+ station.succeed(f"sleep 3 && journalctl --vacuum-time=1s --unit={shlex.quote(unit)}")
+
+ # We're connected, make sure Kismet sees both of our devices
+ status, stdout = monitor.execute(f"curl {url}/devices/views/all/last-time/0/devices.json | tee /dev/stderr | jq -e --arg macPrefix {shlex.quote(ap_mac_prefix)} --arg ssid {shlex.quote(ap_essid)} '. | (map(select((.\"kismet.device.base.macaddr\"? | startswith($macPrefix)) and .\"dot11.device\"?.\"dot11.device.last_beaconed_ssid_record\"?.\"dot11.advertisedssid.ssid\" == $ssid)) | length) == 1'")
+ if status != 0:
+ continue
+ status, stdout = monitor.execute(f"curl {url}/devices/views/all/last-time/0/devices.json | tee /dev/stderr | jq -e --arg macPrefix {shlex.quote(station_mac_prefix)} '. | (map(select((.\"kismet.device.base.macaddr\"? | startswith($macPrefix)))) | length) == 1'")
+ if status == 0:
+ success = True
+ break
+
+ assert success
+ '';
+}
diff --git a/nixos/tests/lavalink.nix b/nixos/tests/lavalink.nix
new file mode 100644
index 000000000000..878f05094190
--- /dev/null
+++ b/nixos/tests/lavalink.nix
@@ -0,0 +1,43 @@
+{ lib, ... }:
+
+let
+ password = "s3cRe!p4SsW0rD";
+in
+
+{
+ name = "lavalink";
+ meta.maintainers = with lib.maintainers; [ nanoyaki ];
+
+ nodes = {
+ machine = {
+ services.lavalink = {
+ enable = true;
+ port = 1234;
+ inherit password;
+ };
+ };
+ machine2 =
+ { pkgs, ... }:
+ {
+ services.lavalink = {
+ enable = true;
+ port = 1235;
+ environmentFile = "${pkgs.writeText "passwordEnvFile" ''
+ LAVALINK_SERVER_PASSWORD=${password}
+ ''}";
+ };
+ };
+ };
+
+ testScript = ''
+ start_all()
+
+ machine.wait_for_unit("lavalink.service")
+ machine.wait_for_open_port(1234)
+ machine.succeed("curl --header \"User-Id: 1204475253028429844\" --header \"Client-Name: shoukaku/4.1.1\" --header \"Authorization: ${password}\" http://localhost:1234/v4/info --fail -v")
+
+ machine2.wait_for_unit("lavalink.service")
+ machine2.wait_for_open_port(1235)
+ machine2.succeed("curl --header \"User-Id: 1204475253028429844\" --header \"Client-Name: shoukaku/4.1.1\" --header \"Authorization: ${password}\" http://localhost:1235/v4/info --fail -v")
+ '';
+}
diff --git a/nixos/tests/librenms.nix b/nixos/tests/librenms.nix
index 05821dfa1000..d4aff22392cf 100644
--- a/nixos/tests/librenms.nix
+++ b/nixos/tests/librenms.nix
@@ -1,106 +1,101 @@
-import ./make-test-python.nix (
- { pkgs, lib, ... }:
+{ pkgs, lib, ... }:
- let
- api_token = "f87f42114e44b63ad1b9e3c3d33d6fbe"; # random md5 hash
- wrong_api_token = "e68ba041fcf1eab923a7a6de3af5f726"; # another random md5 hash
- in
- {
- name = "librenms";
- meta.maintainers = lib.teams.wdz.members;
+let
+ api_token = "f87f42114e44b63ad1b9e3c3d33d6fbe"; # random md5 hash
+ wrong_api_token = "e68ba041fcf1eab923a7a6de3af5f726"; # another random md5 hash
+in
+{
+ name = "librenms";
+ meta.maintainers = lib.teams.wdz.members;
- nodes.librenms = {
- time.timeZone = "Europe/Berlin";
+ nodes.librenms = {
+ time.timeZone = "Europe/Berlin";
- environment.systemPackages = with pkgs; [
- curl
- jq
- ];
+ environment.systemPackages = with pkgs; [
+ curl
+ jq
+ ];
- services.librenms = {
- enable = true;
- hostname = "librenms";
- database = {
- createLocally = true;
- host = "localhost";
- database = "librenms";
- username = "librenms";
- passwordFile = pkgs.writeText "librenms-db-pass" "librenmsdbpass";
- };
- nginx = {
- default = true;
- };
- enableOneMinutePolling = true;
- settings = {
- enable_billing = true;
- };
+ services.librenms = {
+ enable = true;
+ hostname = "librenms";
+ database = {
+ createLocally = true;
+ host = "localhost";
+ database = "librenms";
+ username = "librenms";
+ passwordFile = pkgs.writeText "librenms-db-pass" "librenmsdbpass";
};
-
- # systemd oneshot to create a dummy admin user and a API token for testing
- systemd.services.lnms-api-init = {
- description = "LibreNMS API init";
- after = [ "librenms-setup.service" ];
- wantedBy = [ "multi-user.target" ];
- serviceConfig = {
- Type = "oneshot";
- RemainAfterExit = true;
- User = "root";
- Group = "root";
- };
- script = ''
- API_USER_NAME=api
- API_TOKEN=${api_token} # random md5 hash
-
- # seeding database to get the admin roles
- ${pkgs.librenms}/artisan db:seed --force --no-interaction
-
- # we don't need to know the password, it just has to exist
- API_USER_PASS=$(${pkgs.pwgen}/bin/pwgen -s 64 1)
- ${pkgs.librenms}/artisan user:add $API_USER_NAME -r admin -p $API_USER_PASS
- API_USER_ID=$(${pkgs.mariadb}/bin/mysql -D librenms -N -B -e "SELECT user_id FROM users WHERE username = '$API_USER_NAME';")
-
- ${pkgs.mariadb}/bin/mysql -D librenms -e "INSERT INTO api_tokens (user_id, token_hash, description) VALUES ($API_USER_ID, '$API_TOKEN', 'API User')"
- '';
+ nginx = {
+ default = true;
+ };
+ enableOneMinutePolling = true;
+ settings = {
+ enable_billing = true;
};
};
- nodes.snmphost = {
-
- services.snmpd = {
- enable = true;
- openFirewall = true;
-
- configText = ''
- com2sec readonly default public
-
- group MyROGroup v2c readonly
- view all included .1 80
- access MyROGroup "" any noauth exact all none none
-
- syslocation Testcity, Testcountry
- syscontact Testi mc Test
- '';
-
+ # systemd oneshot to create a dummy admin user and a API token for testing
+ systemd.services.lnms-api-init = {
+ description = "LibreNMS API init";
+ after = [ "librenms-setup.service" ];
+ wantedBy = [ "multi-user.target" ];
+ serviceConfig = {
+ Type = "oneshot";
+ RemainAfterExit = true;
+ User = "root";
+ Group = "root";
};
+ script = ''
+ API_USER_NAME=api
+ API_TOKEN=${api_token} # random md5 hash
+
+ # we don't need to know the password, it just has to exist
+ API_USER_PASS=$(${pkgs.pwgen}/bin/pwgen -s 64 1)
+ ${pkgs.librenms}/artisan user:add $API_USER_NAME -r admin -p $API_USER_PASS
+ API_USER_ID=$(${pkgs.mariadb}/bin/mysql -D librenms -N -B -e "SELECT user_id FROM users WHERE username = '$API_USER_NAME';")
+
+ ${pkgs.mariadb}/bin/mysql -D librenms -e "INSERT INTO api_tokens (user_id, token_hash, description) VALUES ($API_USER_ID, '$API_TOKEN', 'API User')"
+ '';
};
+ };
- testScript = ''
- start_all()
+ nodes.snmphost = {
- snmphost.wait_for_unit("snmpd.service")
+ services.snmpd = {
+ enable = true;
+ openFirewall = true;
- librenms.wait_for_unit("lnms-api-init.service")
- librenms.wait_for_open_port(80)
+ configText = ''
+ com2sec readonly default public
- # Test that we can authenticate against the API
- librenms.succeed("curl --fail -H 'X-Auth-Token: ${api_token}' http://localhost/api/v0")
- librenms.fail("curl --fail -H 'X-Auth-Token: ${wrong_api_token}' http://localhost/api/v0")
+ group MyROGroup v2c readonly
+ view all included .1 80
+ access MyROGroup "" any noauth exact all none none
- # add snmphost as a device
- librenms.succeed("curl --fail -X POST -d '{\"hostname\":\"snmphost\",\"version\":\"v2c\",\"community\":\"public\"}' -H 'X-Auth-Token: ${api_token}' http://localhost/api/v0/devices")
+ syslocation Testcity, Testcountry
+ syscontact Testi mc Test
+ '';
- # wait until snmphost gets polled
- librenms.wait_until_succeeds("test $(curl -H 'X-Auth-Token: ${api_token}' http://localhost/api/v0/devices/snmphost | jq -Mr .devices[0].last_polled) != 'null'")
- '';
- }
-)
+ };
+ };
+
+ testScript = ''
+ start_all()
+
+ snmphost.wait_for_unit("snmpd.service")
+
+ librenms.wait_for_unit("lnms-api-init.service")
+ librenms.wait_for_open_port(80)
+
+ # Test that we can authenticate against the API
+ librenms.succeed("curl --fail -H 'X-Auth-Token: ${api_token}' http://localhost/api/v0")
+ librenms.fail("curl --fail -H 'X-Auth-Token: ${wrong_api_token}' http://localhost/api/v0")
+
+ # add snmphost as a device
+ librenms.succeed("curl --fail -X POST -d '{\"hostname\":\"snmphost\",\"version\":\"v2c\",\"community\":\"public\"}' -H 'X-Auth-Token: ${api_token}' http://localhost/api/v0/devices")
+
+ # wait until snmphost gets polled
+ librenms.wait_until_succeeds("test $(curl -H 'X-Auth-Token: ${api_token}' http://localhost/api/v0/devices/snmphost | jq -Mr .devices[0].last_polled) != 'null'")
+ '';
+}
diff --git a/nixos/tests/lighttpd.nix b/nixos/tests/lighttpd.nix
index 271dc19ef68c..db08fdf3de22 100644
--- a/nixos/tests/lighttpd.nix
+++ b/nixos/tests/lighttpd.nix
@@ -1,25 +1,23 @@
-import ./make-test-python.nix (
- { lib, pkgs, ... }:
- {
- name = "lighttpd";
- meta.maintainers = with lib.maintainers; [ bjornfor ];
+{ lib, pkgs, ... }:
+{
+ name = "lighttpd";
+ meta.maintainers = with lib.maintainers; [ bjornfor ];
- nodes = {
- server = {
- services.lighttpd.enable = true;
- services.lighttpd.document-root = pkgs.runCommand "document-root" { } ''
- mkdir -p "$out"
- echo "hello nixos test" > "$out/file.txt"
- '';
- };
+ nodes = {
+ server = {
+ services.lighttpd.enable = true;
+ services.lighttpd.document-root = pkgs.runCommand "document-root" { } ''
+ mkdir -p "$out"
+ echo "hello nixos test" > "$out/file.txt"
+ '';
};
+ };
- testScript = ''
- start_all()
- server.wait_for_unit("lighttpd.service")
- res = server.succeed("curl --fail http://localhost/file.txt")
- assert "hello nixos test" in res, f"bad server response: '{res}'"
- server.succeed("systemctl reload lighttpd")
- '';
- }
-)
+ testScript = ''
+ start_all()
+ server.wait_for_unit("lighttpd.service")
+ res = server.succeed("curl --fail http://localhost/file.txt")
+ assert "hello nixos test" in res, f"bad server response: '{res}'"
+ server.succeed("systemctl reload lighttpd")
+ '';
+}
diff --git a/nixos/tests/limine/checksum.nix b/nixos/tests/limine/checksum.nix
index 78352bf5da83..db01f8ccea92 100644
--- a/nixos/tests/limine/checksum.nix
+++ b/nixos/tests/limine/checksum.nix
@@ -6,11 +6,6 @@
phip1611
programmerlexi
];
- meta.platforms = [
- "aarch64-linux"
- "i686-linux"
- "x86_64-linux"
- ];
nodes.machine =
{ ... }:
{
diff --git a/nixos/tests/limine/default.nix b/nixos/tests/limine/default.nix
index 7458b9641633..9497e06a18f6 100644
--- a/nixos/tests/limine/default.nix
+++ b/nixos/tests/limine/default.nix
@@ -4,5 +4,6 @@
}:
{
checksum = runTest ./checksum.nix;
+ secureBoot = runTest ./secure-boot.nix;
uefi = runTest ./uefi.nix;
}
diff --git a/nixos/tests/limine/secure-boot.nix b/nixos/tests/limine/secure-boot.nix
new file mode 100644
index 000000000000..9f7969e626a0
--- /dev/null
+++ b/nixos/tests/limine/secure-boot.nix
@@ -0,0 +1,34 @@
+{ lib, ... }:
+{
+ name = "secureBoot";
+ meta.maintainers = with lib.maintainers; [
+ programmerlexi
+ ];
+ meta.platforms = [
+ "aarch64-linux"
+ "i686-linux"
+ "x86_64-linux"
+ ];
+ nodes.machine =
+ { pkgs, ... }:
+ {
+ virtualisation.useBootLoader = true;
+ virtualisation.useEFIBoot = true;
+ virtualisation.useSecureBoot = true;
+ virtualisation.efi.OVMF = pkgs.OVMFFull.fd;
+ virtualisation.efi.keepVariables = true;
+
+ boot.loader.efi.canTouchEfiVariables = true;
+
+ boot.loader.limine.enable = true;
+ boot.loader.limine.efiSupport = true;
+ boot.loader.limine.secureBoot.enable = true;
+ boot.loader.limine.secureBoot.createAndEnrollKeys = true;
+ boot.loader.timeout = 0;
+ };
+
+ testScript = ''
+ machine.start()
+ assert "Secure Boot: enabled (user)" in machine.succeed("bootctl status")
+ '';
+}
diff --git a/nixos/tests/limine/uefi.nix b/nixos/tests/limine/uefi.nix
index 12f2f695a865..4c9d0309d25b 100644
--- a/nixos/tests/limine/uefi.nix
+++ b/nixos/tests/limine/uefi.nix
@@ -6,11 +6,6 @@
phip1611
programmerlexi
];
- meta.platforms = [
- "aarch64-linux"
- "i686-linux"
- "x86_64-linux"
- ];
nodes.machine =
{ ... }:
{
diff --git a/nixos/tests/logrotate.nix b/nixos/tests/logrotate.nix
index 1efbcc4cfed5..603a0f164c46 100644
--- a/nixos/tests/logrotate.nix
+++ b/nixos/tests/logrotate.nix
@@ -10,137 +10,134 @@ let
};
in
+{ pkgs, ... }:
+{
+ name = "logrotate";
+ meta = with pkgs.lib.maintainers; {
+ maintainers = [ martinetd ];
+ };
-import ./make-test-python.nix (
- { pkgs, ... }:
- rec {
- name = "logrotate";
- meta = with pkgs.lib.maintainers; {
- maintainers = [ martinetd ];
- };
-
- nodes = {
- defaultMachine =
- { ... }:
- {
- services.logrotate.enable = true;
+ nodes = {
+ defaultMachine =
+ { ... }:
+ {
+ services.logrotate.enable = true;
+ };
+ failingMachine =
+ { ... }:
+ {
+ services.logrotate = {
+ enable = true;
+ configFile = pkgs.writeText "logrotate.conf" ''
+ # self-written config file
+ su notarealuser notagroupeither
+ '';
};
- failingMachine =
- { ... }:
- {
- services.logrotate = {
- enable = true;
- configFile = pkgs.writeText "logrotate.conf" ''
- # self-written config file
- su notarealuser notagroupeither
- '';
- };
- };
- machine =
- { config, ... }:
- {
- imports = [ importTest ];
+ };
+ machine =
+ { config, ... }:
+ {
+ imports = [ importTest ];
- services.logrotate = {
- enable = true;
- settings = {
- # remove default frequency header and add another
- header = {
- frequency = null;
- delaycompress = true;
- };
- # extra global setting... affecting nothing
- last_line = {
- global = true;
- priority = 2000;
- shred = true;
- };
- # using mail somewhere should add --mail to logrotate invocation
- sendmail = {
- mail = "user@domain.tld";
- };
- # postrotate should be suffixed by 'endscript'
- postrotate = {
- postrotate = "touch /dev/null";
- };
- # check checkConfig works as expected: there is nothing to check here
- # except that the file build passes
- checkConf = {
- su = "root utmp";
- createolddir = "0750 root utmp";
- create = "root utmp";
- "create " = "0750 root utmp";
- };
- # multiple paths should be aggregated
- multipath = {
- files = [
- "file1"
- "file2"
- ];
- };
- # overriding imported path should keep existing attributes
- # (e.g. olddir is still set)
- import = {
- notifempty = true;
- };
+ services.logrotate = {
+ enable = true;
+ settings = {
+ # remove default frequency header and add another
+ header = {
+ frequency = null;
+ delaycompress = true;
+ };
+ # extra global setting... affecting nothing
+ last_line = {
+ global = true;
+ priority = 2000;
+ shred = true;
+ };
+ # using mail somewhere should add --mail to logrotate invocation
+ sendmail = {
+ mail = "user@domain.tld";
+ };
+ # postrotate should be suffixed by 'endscript'
+ postrotate = {
+ postrotate = "touch /dev/null";
+ };
+ # check checkConfig works as expected: there is nothing to check here
+ # except that the file build passes
+ checkConf = {
+ su = "root utmp";
+ createolddir = "0750 root utmp";
+ create = "root utmp";
+ "create " = "0750 root utmp";
+ };
+ # multiple paths should be aggregated
+ multipath = {
+ files = [
+ "file1"
+ "file2"
+ ];
+ };
+ # overriding imported path should keep existing attributes
+ # (e.g. olddir is still set)
+ import = {
+ notifempty = true;
};
};
};
- };
+ };
+ };
- testScript = ''
- with subtest("whether logrotate works"):
- # we must rotate once first to create logrotate stamp
- defaultMachine.succeed("systemctl start logrotate.service")
- # we need to wait for console text once here to
- # clear console buffer up to this point for next wait
- defaultMachine.wait_for_console_text('logrotate.service: Deactivated successfully')
+ testScript = ''
+ with subtest("whether logrotate works"):
+ # we must rotate once first to create logrotate stamp
+ defaultMachine.succeed("systemctl start logrotate.service")
+ # we need to wait for console text once here to
+ # clear console buffer up to this point for next wait
+ defaultMachine.wait_for_console_text('logrotate.service: Deactivated successfully')
- defaultMachine.succeed(
- # wtmp is present in default config.
- "rm -f /var/log/wtmp*",
- # we need to give it at least 1MB
- "dd if=/dev/zero of=/var/log/wtmp bs=2M count=1",
+ defaultMachine.succeed(
+ # wtmp is present in default config.
+ "rm -f /var/log/wtmp*",
+ # we need to give it at least 1MB
+ "dd if=/dev/zero of=/var/log/wtmp bs=2M count=1",
- # move into the future and check rotation.
- "date -s 'now + 1 month + 1 day'")
- defaultMachine.wait_for_console_text('logrotate.service: Deactivated successfully')
- defaultMachine.succeed(
- # check rotate worked
- "[ -e /var/log/wtmp.1 ]",
- )
- with subtest("default config does not have mail"):
- defaultMachine.fail("systemctl cat logrotate.service | grep -- --mail")
- with subtest("using mails adds mail option"):
- machine.succeed("systemctl cat logrotate.service | grep -- --mail")
- with subtest("check generated config matches expectation"):
- machine.succeed(
- # copy conf to /tmp/logrotate.conf for easy grep
- "conf=$(systemctl cat logrotate | grep -oE '/nix/store[^ ]*logrotate.conf'); cp $conf /tmp/logrotate.conf",
- "! grep weekly /tmp/logrotate.conf",
- "grep -E '^delaycompress' /tmp/logrotate.conf",
- "tail -n 1 /tmp/logrotate.conf | grep shred",
- "sed -ne '/\"sendmail\" {/,/}/p' /tmp/logrotate.conf | grep 'mail user@domain.tld'",
- "sed -ne '/\"postrotate\" {/,/}/p' /tmp/logrotate.conf | grep endscript",
- "grep '\"file1\"\n\"file2\" {' /tmp/logrotate.conf",
- "sed -ne '/\"import\" {/,/}/p' /tmp/logrotate.conf | grep noolddir",
- )
- # also check configFile option
- failingMachine.succeed(
- "conf=$(systemctl cat logrotate | grep -oE '/nix/store[^ ]*logrotate.conf'); cp $conf /tmp/logrotate.conf",
- "grep 'self-written config' /tmp/logrotate.conf",
- )
- with subtest("Check logrotate-checkconf service"):
- machine.wait_for_unit("logrotate-checkconf.service")
- # wait_for_unit also asserts for success, so wait for
- # parent target instead and check manually.
- failingMachine.wait_for_unit("multi-user.target")
- info = failingMachine.get_unit_info("logrotate-checkconf.service")
- if info["ActiveState"] != "failed":
- raise Exception('logrotate-checkconf.service was not failed')
+ # move into the future and check rotation.
+ "date -s 'now + 1 month + 1 day'")
+ defaultMachine.wait_for_console_text('logrotate.service: Deactivated successfully')
+ defaultMachine.succeed(
+ # check rotate worked
+ "[ -e /var/log/wtmp.1 ]",
+ )
+ with subtest("default config does not have mail"):
+ defaultMachine.fail("systemctl cat logrotate.service | grep -- --mail")
+ with subtest("using mails adds mail option"):
+ machine.succeed("systemctl cat logrotate.service | grep -- --mail")
+ with subtest("check generated config matches expectation"):
+ machine.succeed(
+ # copy conf to /tmp/logrotate.conf for easy grep
+ "conf=$(systemctl cat logrotate | grep -oE '/nix/store[^ ]*logrotate.conf'); cp $conf /tmp/logrotate.conf",
+ "! grep weekly /tmp/logrotate.conf",
+ "grep -E '^delaycompress' /tmp/logrotate.conf",
+ "tail -n 1 /tmp/logrotate.conf | grep shred",
+ "sed -ne '/\"sendmail\" {/,/}/p' /tmp/logrotate.conf | grep 'mail user@domain.tld'",
+ "sed -ne '/\"postrotate\" {/,/}/p' /tmp/logrotate.conf | grep endscript",
+ "grep '\"file1\"\n\"file2\" {' /tmp/logrotate.conf",
+ "sed -ne '/\"import\" {/,/}/p' /tmp/logrotate.conf | grep noolddir",
+ )
+ # also check configFile option
+ failingMachine.succeed(
+ "conf=$(systemctl cat logrotate | grep -oE '/nix/store[^ ]*logrotate.conf'); cp $conf /tmp/logrotate.conf",
+ "grep 'self-written config' /tmp/logrotate.conf",
+ )
+ with subtest("Check logrotate-checkconf service"):
+ machine.wait_for_unit("logrotate-checkconf.service")
+ # wait_for_unit also asserts for success, so wait for
+ # parent target instead and check manually.
+ failingMachine.wait_for_unit("multi-user.target")
+ info = failingMachine.get_unit_info("logrotate-checkconf.service")
+ if info["ActiveState"] != "failed":
+ raise Exception('logrotate-checkconf.service was not failed')
- machine.log(machine.execute("systemd-analyze security logrotate.service | grep -v ✓")[1])
+ machine.log(machine.execute("systemd-analyze security logrotate.service | grep -v ✓")[1])
- '';
- }
-)
+ '';
+}
diff --git a/nixos/tests/lomiri-calendar-app.nix b/nixos/tests/lomiri-calendar-app.nix
index 5530e9e36075..41061a066638 100644
--- a/nixos/tests/lomiri-calendar-app.nix
+++ b/nixos/tests/lomiri-calendar-app.nix
@@ -1,11 +1,7 @@
{ pkgs, lib, ... }:
{
name = "lomiri-calendar-app-standalone";
- meta = {
- maintainers = lib.teams.lomiri.members;
- # This needs a Linux VM
- platforms = lib.platforms.linux;
- };
+ meta.maintainers = lib.teams.lomiri.members;
nodes.machine =
{ config, pkgs, ... }:
@@ -44,23 +40,33 @@
with subtest("lomiri calendar launches"):
machine.succeed("lomiri-calendar-app >&2 &")
- machine.wait_for_text(r"(January|February|March|April|May|June|July|August|September|October|November|December)")
+ machine.sleep(10)
+ machine.send_key("alt-f10")
+ machine.sleep(2)
+ # Default page is unbearably slow to OCR on, switch to another
+ machine.succeed("xdotool mousemove 580 50 click 1")
+ machine.sleep(2)
+ machine.wait_for_text(r"(January|February|March|April|May|June|July|August|September|October|November|December|Mon|Tue|Wed|Thu|Fri|Sat|Sun)")
machine.screenshot("lomiri-calendar")
with subtest("lomiri calendar works"):
# Switch to Agenda tab, less busy
- machine.succeed("xdotool mousemove 300 50 click 1")
+ machine.succeed("xdotool mousemove 380 50 click 1")
+ machine.sleep(2)
# Still on main page
- machine.succeed("xdotool mousemove 500 650 click 1")
+ machine.succeed("xdotool mousemove 500 720 click 1")
+ machine.sleep(2)
machine.wait_for_text(r"(Date|Monday|Tuesday|Wednesday|Thursday|Friday|Saturday|Sunday|All day|Name|Details|More)")
machine.screenshot("lomiri-calendar_newevent")
# On New Event page
machine.succeed("xdotool mousemove 500 230 click 1")
+ machine.sleep(2)
machine.send_chars("foobar")
machine.sleep(2) # make sure they're actually in there
- machine.succeed("xdotool mousemove 780 40 click 1")
+ machine.succeed("xdotool mousemove 1000 40 click 1")
+ machine.sleep(2)
machine.wait_for_text("Agenda")
machine.screenshot("lomiri-calendar_eventadded")
@@ -73,6 +79,9 @@
with subtest("lomiri calendar localisation works"):
machine.succeed("env LANG=de_DE.UTF-8 lomiri-calendar-app >&2 &")
+ machine.sleep(10)
+ machine.send_key("alt-f10")
+ machine.sleep(2)
machine.wait_for_text(r"(Montag|Dienstag|Mittwoch|Donnerstag|Freitag|Samstag|Sonntag)")
machine.screenshot("lomiri-calendar_localised")
'';
diff --git a/nixos/tests/lomiri-camera-app.nix b/nixos/tests/lomiri-camera-app.nix
index ccd53a37135b..ec3442966982 100644
--- a/nixos/tests/lomiri-camera-app.nix
+++ b/nixos/tests/lomiri-camera-app.nix
@@ -47,7 +47,7 @@
testScript =
let
- qrLabel = "Image";
+ qrLabel = "Feed";
qrContent = "Test";
in
''
@@ -55,6 +55,10 @@
with subtest("lomiri camera launches"):
machine.succeed("lomiri-camera-app >&2 &")
+ machine.wait_for_console_text("updateViewfinderResolution: viewfinder resolutions is not known yet")
+ machine.sleep(10)
+ machine.send_key("alt-f10")
+ machine.sleep(5)
machine.wait_for_text("Cannot access")
machine.screenshot("lomiri-camera_open")
@@ -64,21 +68,26 @@
machine.succeed("modprobe v4l2loopback video_nr=10 card_label=Video-Loopback exclusive_caps=1")
machine.succeed("qrtool encode '${qrContent}' -s 20 -m 10 > qr.png")
# Horizontal flip, add text, flip back. Camera displays image mirrored, so need reversed text for OCR
- machine.succeed("magick qr.png -flop -pointsize 70 -fill black -annotate +100+100 '${qrLabel}' -flop output.png")
+ machine.succeed("magick qr.png -flop -pointsize 30 -fill black -annotate +100+100 '${qrLabel}' -flop output.png")
machine.succeed("ffmpeg -re -loop 1 -i output.png -vf format=yuv420p -f v4l2 /dev/video10 -loglevel fatal >&2 &")
with subtest("lomiri camera uses camera"):
machine.succeed("lomiri-camera-app >&2 &")
+ machine.wait_for_console_text("updateViewfinderResolution: For target resolution")
+ machine.sleep(10)
+ machine.send_key("alt-f10")
+ machine.sleep(5)
machine.wait_for_text("${qrLabel}")
machine.screenshot("lomiri-camera_feed")
- machine.succeed("xdotool mousemove 320 610 click 1") # take photo
- machine.wait_until_succeeds("find /root/Pictures/camera.ubports -name '*.jpg'")
+ machine.succeed("xdotool mousemove 510 670 click 1") # take photo
+ machine.wait_until_succeeds("ls /root/Pictures/camera.ubports | grep '\\.jpg$'")
# Check that the image is correct
machine.send_key("ctrl-alt-right")
machine.succeed("magick /root/Pictures/camera.ubports/IMG_00000001.jpg -flop photo_flip.png")
machine.succeed("feh photo_flip.png >&2 &")
+ machine.sleep(10)
machine.wait_for_text("${qrLabel}")
machine.screenshot("lomiri-camera_photo")
@@ -88,18 +97,25 @@
with subtest("lomiri barcode scanner uses camera"):
machine.succeed("lomiri-camera-app --mode=barcode-reader >&2 &")
+ machine.wait_for_console_text("updateViewfinderResolution: For target resolution")
+ machine.sleep(10)
+ machine.send_key("alt-f10")
+ machine.sleep(5)
machine.wait_for_text("${qrLabel}")
- machine.succeed("xdotool mousemove 320 610 click 1") # open up QR decode result
+ machine.succeed("xdotool mousemove 510 670 click 1") # open up QR decode result
# OCR is struggling to recognise the text. Click the clipboard button and paste the result somewhere else
machine.sleep(5)
machine.screenshot("lomiri-barcode_decode")
- machine.succeed("xdotool mousemove 350 530 click 1")
+ machine.succeed("xdotool mousemove 540 590 click 1")
machine.sleep(5)
# Need to make a new window without closing camera app, otherwise clipboard content gets lost?
machine.send_key("ctrl-alt-right")
machine.succeed("gnome-text-editor >&2 &")
+ machine.sleep(10)
+ machine.send_key("alt-f10")
+ machine.sleep(5)
machine.wait_for_text("New")
# Font size up to help with OCR
@@ -129,6 +145,10 @@
with subtest("lomiri camera localisation works"):
machine.succeed("env LANG=de_DE.UTF-8 lomiri-camera-app >&2 &")
+ machine.wait_for_console_text("updateViewfinderResolution: For target resolution")
+ machine.sleep(10)
+ machine.send_key("alt-f10")
+ machine.sleep(5)
machine.wait_for_text("Kamera")
machine.screenshot("lomiri-camera_localised")
'';
diff --git a/nixos/tests/lomiri-clock-app.nix b/nixos/tests/lomiri-clock-app.nix
index 9db5cee49cf7..d5cb72394abc 100644
--- a/nixos/tests/lomiri-clock-app.nix
+++ b/nixos/tests/lomiri-clock-app.nix
@@ -34,14 +34,20 @@
machine.wait_for_x()
with subtest("lomiri clock launches"):
- machine.execute("lomiri-clock-app >&2 &")
+ machine.succeed("lomiri-clock-app >&2 &")
+ machine.sleep(10)
+ machine.send_key("alt-f10")
+ machine.sleep(5)
machine.wait_for_text(r"(clock.ubports|City|Alarms)")
machine.screenshot("lomiri-clock_open")
machine.succeed("pkill -f lomiri-clock-app")
with subtest("lomiri clock localisation works"):
- machine.execute("env LANG=de_DE.UTF-8 lomiri-clock-app >&2 &")
+ machine.succeed("env LANG=de_DE.UTF-8 lomiri-clock-app >&2 &")
+ machine.sleep(10)
+ machine.send_key("alt-f10")
+ machine.sleep(5)
machine.wait_for_text(r"(Stadt|Weckzeiten)")
machine.screenshot("lomiri-clock_localised")
'';
diff --git a/nixos/tests/lomiri-docviewer-app.nix b/nixos/tests/lomiri-docviewer-app.nix
index c21a121f6c02..ad9174e15d30 100644
--- a/nixos/tests/lomiri-docviewer-app.nix
+++ b/nixos/tests/lomiri-docviewer-app.nix
@@ -46,6 +46,9 @@ in
with subtest("lomiri docviewer launches"):
machine.succeed("lomiri-docviewer-app >&2 &")
+ machine.sleep(10)
+ machine.send_key("alt-f10")
+ machine.sleep(5)
machine.wait_for_text("No documents")
machine.screenshot("lomiri-docviewer_open")
@@ -57,6 +60,9 @@ in
with subtest("lomiri docviewer txt works"):
machine.succeed("lomiri-docviewer-app /etc/docviewer-sampletext.txt >&2 &")
+ machine.sleep(10)
+ machine.send_key("alt-f10")
+ machine.sleep(5)
machine.wait_for_text("${exampleText}")
machine.screenshot("lomiri-docviewer_txt")
@@ -64,6 +70,9 @@ in
with subtest("lomiri docviewer odt works"):
machine.succeed("lomiri-docviewer-app /root/docviewer-sampletext.odt >&2 &")
+ machine.sleep(10)
+ machine.send_key("alt-f10")
+ machine.sleep(5)
machine.wait_for_text("${exampleText}")
machine.screenshot("lomiri-docviewer_odt")
@@ -71,6 +80,9 @@ in
with subtest("lomiri docviewer pdf works"):
machine.succeed("lomiri-docviewer-app /root/docviewer-sampletext.pdf >&2 &")
+ machine.sleep(10)
+ machine.send_key("alt-f10")
+ machine.sleep(5)
machine.wait_for_text("${exampleText}")
machine.screenshot("lomiri-docviewer_pdf")
@@ -78,6 +90,9 @@ in
with subtest("lomiri docviewer localisation works"):
machine.succeed("env LANG=de_DE.UTF-8 lomiri-docviewer-app >&2 &")
+ machine.sleep(10)
+ machine.send_key("alt-f10")
+ machine.sleep(5)
machine.wait_for_text("Keine Dokumente")
machine.screenshot("lomiri-docviewer_localised")
'';
diff --git a/nixos/tests/lomiri-gallery-app.nix b/nixos/tests/lomiri-gallery-app.nix
index d8cf7466656a..c7cec10d875c 100644
--- a/nixos/tests/lomiri-gallery-app.nix
+++ b/nixos/tests/lomiri-gallery-app.nix
@@ -59,8 +59,15 @@
machine.succeed("mkdir /root/Pictures /root/Videos")
# Setup example data, OCR-friendly:
# - White square, black text
+ # - Small text for display OCR
+ # - Big text for gallery preview OCR
# - uppercase extension
- machine.succeed("magick -size 500x500 -background white -fill black canvas:white -pointsize 70 -annotate +100+300 '${imageLabel}' /root/Pictures/output.PNG")
+ machine.succeed(
+ "magick -size 500x500 -background white -fill black canvas:white "
+ + "-pointsize 20 -annotate +100+100 '${imageLabel}' "
+ + "-pointsize 50 -annotate +100+300 '${imageLabel}' "
+ + "/root/Pictures/output.PNG"
+ )
# Different image formats
machine.succeed("magick /root/Pictures/output.PNG /root/Pictures/output.JPG")
diff --git a/nixos/tests/lomiri-mediaplayer-app.nix b/nixos/tests/lomiri-mediaplayer-app.nix
index b4ac5dd4ad2a..e1f20a4cc203 100644
--- a/nixos/tests/lomiri-mediaplayer-app.nix
+++ b/nixos/tests/lomiri-mediaplayer-app.nix
@@ -1,6 +1,6 @@
{ lib, ... }:
let
- ocrContent = "Video Test";
+ ocrContent = "Feed";
videoFile = "test.webm";
in
{
@@ -25,8 +25,8 @@ in
];
}
''
- magick -size 400x400 canvas:white -pointsize 40 -fill black -annotate +100+100 '${ocrContent}' output.png
- ffmpeg -re -loop 1 -i output.png -c:v libvpx -b:v 100K -t 120 $out -loglevel fatal
+ magick -size 600x600 canvas:white -pointsize 20 -fill black -annotate +100+100 '${ocrContent}' output.png
+ ffmpeg -re -loop 1 -i output.png -c:v libvpx -b:v 200K -t 120 $out -loglevel fatal
'';
systemPackages = with pkgs.lomiri; [
suru-icon-theme
@@ -54,6 +54,8 @@ in
with subtest("lomiri mediaplayer launches"):
machine.succeed("lomiri-mediaplayer-app >&2 &")
+ machine.sleep(10)
+ machine.send_key("alt-f10")
machine.wait_for_text("Choose from")
machine.screenshot("lomiri-mediaplayer_open")
@@ -61,6 +63,8 @@ in
with subtest("lomiri mediaplayer plays video"):
machine.succeed("lomiri-mediaplayer-app /etc/${videoFile} >&2 &")
+ machine.sleep(10)
+ machine.send_key("alt-f10")
machine.wait_for_text("${ocrContent}")
machine.screenshot("lomiri-mediaplayer_playback")
@@ -71,6 +75,8 @@ in
# Cause an error, and look for the error popup
machine.succeed("touch invalid.mp4")
machine.succeed("env LANG=de_DE.UTF-8 lomiri-mediaplayer-app invalid.mp4 >&2 &")
+ machine.sleep(10)
+ machine.send_key("alt-f10")
machine.wait_for_text("Fehler")
machine.screenshot("lomiri-mediaplayer_localised")
'';
diff --git a/nixos/tests/lomiri-music-app.nix b/nixos/tests/lomiri-music-app.nix
index 93a3889c9127..87722db239d7 100644
--- a/nixos/tests/lomiri-music-app.nix
+++ b/nixos/tests/lomiri-music-app.nix
@@ -8,11 +8,7 @@ let
in
{
name = "lomiri-music-app-standalone";
- meta = {
- maintainers = lib.teams.lomiri.members;
- # This needs a Linux VM
- platforms = lib.platforms.linux;
- };
+ meta.maintainers = lib.teams.lomiri.members;
nodes.machine =
{ config, pkgs, ... }:
@@ -140,8 +136,10 @@ in
with subtest("lomiri music launches"):
machine.succeed("lomiri-music-app >&2 &")
- machine.wait_for_text("favorite music")
+ machine.sleep(10)
machine.send_key("alt-f10")
+ machine.sleep(2)
+ machine.wait_for_text("favorite music")
machine.screenshot("lomiri-music")
with subtest("lomiri music plays music"):
@@ -187,6 +185,9 @@ in
with subtest("lomiri music localisation works"):
machine.succeed("env LANG=de_DE.UTF-8 lomiri-music-app .mp4 >&2 &")
+ machine.sleep(10)
+ machine.send_key("alt-f10")
+ machine.sleep(2)
machine.wait_for_text("Titel")
machine.screenshot("lomiri-music_localised")
'';
diff --git a/nixos/tests/lomiri-system-settings.nix b/nixos/tests/lomiri-system-settings.nix
index ab6dfe53630b..8018419f8863 100644
--- a/nixos/tests/lomiri-system-settings.nix
+++ b/nixos/tests/lomiri-system-settings.nix
@@ -109,7 +109,11 @@
machine.wait_for_x()
with subtest("lomiri system settings launches"):
- machine.execute("lomiri-system-settings >&2 &")
+ machine.succeed("lomiri-system-settings >&2 &")
+ machine.wait_for_console_text("qml: Plugin about does not exist")
+ machine.sleep(10)
+ machine.send_key("alt-f10")
+ machine.sleep(5)
machine.wait_for_text("System Settings")
machine.screenshot("lss_open")
@@ -137,7 +141,11 @@
machine.execute("pkill -f lomiri-system-settings")
with subtest("lomiri system settings localisation works"):
- machine.execute("env LANG=de_DE.UTF-8 lomiri-system-settings >&2 &")
+ machine.succeed("env LANG=de_DE.UTF-8 lomiri-system-settings >&2 &")
+ machine.wait_for_console_text("qml: Plugin about does not exist")
+ machine.sleep(10)
+ machine.send_key("alt-f10")
+ machine.sleep(5)
machine.wait_for_text("Systemeinstellungen")
machine.screenshot("lss_localised_open")
diff --git a/nixos/tests/mailhog.nix b/nixos/tests/mailhog.nix
index 5192e3471e35..41d4a97abf3a 100644
--- a/nixos/tests/mailhog.nix
+++ b/nixos/tests/mailhog.nix
@@ -1,30 +1,26 @@
-import ./make-test-python.nix (
- { lib, ... }:
- {
- name = "mailhog";
- meta.maintainers = with lib.maintainers; [
- jojosch
- RTUnreal
- ];
+{ lib, ... }:
+{
+ name = "mailhog";
+ meta.maintainers = with lib.maintainers; [
+ jojosch
+ RTUnreal
+ ];
- nodes.machine =
- { pkgs, ... }:
- {
- services.mailhog.enable = true;
- };
+ nodes.machine = _: {
+ services.mailhog.enable = true;
+ };
- testScript = ''
- start_all()
+ testScript = ''
+ start_all()
- machine.wait_for_unit("mailhog.service")
- machine.wait_for_open_port(1025)
- machine.wait_for_open_port(8025)
- # Test sendmail wrapper (this uses smtp, which tests the connection)
- machine.succeed('printf "To: root@example.com\r\n\r\nthis is the body of the email" | sendmail -t -i -f sender@example.com')
- res = machine.succeed(
- "curl --fail http://localhost:8025/api/v2/messages"
- )
- assert all(msg in res for msg in ["this is the body of the email", "sender@example.com", "root@example.com"])
- '';
- }
-)
+ machine.wait_for_unit("mailhog.service")
+ machine.wait_for_open_port(1025)
+ machine.wait_for_open_port(8025)
+ # Test sendmail wrapper (this uses smtp, which tests the connection)
+ machine.succeed('printf "To: root@example.com\r\n\r\nthis is the body of the email" | sendmail -t -i -f sender@example.com')
+ res = machine.succeed(
+ "curl --fail http://localhost:8025/api/v2/messages"
+ )
+ assert all(msg in res for msg in ["this is the body of the email", "sender@example.com", "root@example.com"])
+ '';
+}
diff --git a/nixos/tests/mailman.nix b/nixos/tests/mailman.nix
index d4e09148a9dd..c513c1ed29d3 100644
--- a/nixos/tests/mailman.nix
+++ b/nixos/tests/mailman.nix
@@ -1,4 +1,5 @@
-import ./make-test-python.nix {
+{ ... }:
+{
name = "mailman";
nodes.machine =
diff --git a/nixos/tests/mailpit.nix b/nixos/tests/mailpit.nix
index 887f700ae684..5f92b5030fda 100644
--- a/nixos/tests/mailpit.nix
+++ b/nixos/tests/mailpit.nix
@@ -1,35 +1,33 @@
-import ./make-test-python.nix (
- { lib, ... }:
- {
- name = "mailpit";
- meta.maintainers = lib.teams.flyingcircus.members;
+{ lib, ... }:
+{
+ name = "mailpit";
+ meta.maintainers = lib.teams.flyingcircus.members;
- nodes.machine =
- { pkgs, ... }:
- {
- services.mailpit.instances.default = { };
+ nodes.machine =
+ { pkgs, ... }:
+ {
+ services.mailpit.instances.default = { };
- environment.systemPackages = with pkgs; [ swaks ];
- };
+ environment.systemPackages = with pkgs; [ swaks ];
+ };
- testScript = ''
- start_all()
+ testScript = ''
+ start_all()
- from json import loads
+ from json import loads
- machine.wait_for_unit("mailpit-default.service")
- machine.wait_for_open_port(1025)
- machine.wait_for_open_port(8025)
- machine.succeed(
- 'echo "this is the body of the email" | swaks --to root@example.org --body - --server localhost:1025'
- )
+ machine.wait_for_unit("mailpit-default.service")
+ machine.wait_for_open_port(1025)
+ machine.wait_for_open_port(8025)
+ machine.succeed(
+ 'echo "this is the body of the email" | swaks --to root@example.org --body - --server localhost:1025'
+ )
- received = loads(machine.succeed("curl http://localhost:8025/api/v1/messages"))
- assert received['total'] == 1
- message = received["messages"][0]
- assert len(message['To']) == 1
- assert message['To'][0]['Address'] == 'root@example.org'
- assert "this is the body of the email" in message['Snippet']
- '';
- }
-)
+ received = loads(machine.succeed("curl http://localhost:8025/api/v1/messages"))
+ assert received['total'] == 1
+ message = received["messages"][0]
+ assert len(message['To']) == 1
+ assert message['To'][0]['Address'] == 'root@example.org'
+ assert "this is the body of the email" in message['Snippet']
+ '';
+}
diff --git a/nixos/tests/man.nix b/nixos/tests/man.nix
index 510a46a46ec9..5155fcbe055a 100644
--- a/nixos/tests/man.nix
+++ b/nixos/tests/man.nix
@@ -1,111 +1,109 @@
-import ./make-test-python.nix (
- { pkgs, lib, ... }:
- let
- manImplementations = [
- "mandoc"
- "man-db"
+{ pkgs, lib, ... }:
+let
+ manImplementations = [
+ "mandoc"
+ "man-db"
+ ];
+
+ machineNames = builtins.map machineSafe manImplementations;
+
+ makeConfig = useImpl: {
+ # Note: mandoc currently can't index symlinked section directories.
+ # So if a man section comes from one package exclusively (e. g.
+ # 1p from man-pages-posix and 2 from man-pages), it isn't searchable.
+ environment.systemPackages = [
+ pkgs.man-pages
+ pkgs.openssl
+ pkgs.libunwind
];
- machineNames = builtins.map machineSafe manImplementations;
-
- makeConfig = useImpl: {
- # Note: mandoc currently can't index symlinked section directories.
- # So if a man section comes from one package exclusively (e. g.
- # 1p from man-pages-posix and 2 from man-pages), it isn't searchable.
- environment.systemPackages = [
- pkgs.man-pages
- pkgs.openssl
- pkgs.libunwind
- ];
-
- documentation = {
- enable = true;
- nixos.enable = lib.mkForce true;
- dev.enable = true;
- man =
- {
- enable = true;
- generateCaches = true;
- }
- // lib.listToAttrs (
- builtins.map (impl: {
- name = impl;
- value = {
- enable = useImpl == impl;
- };
- }) manImplementations
- );
- };
+ documentation = {
+ enable = true;
+ nixos.enable = lib.mkForce true;
+ dev.enable = true;
+ man =
+ {
+ enable = true;
+ generateCaches = true;
+ }
+ // lib.listToAttrs (
+ builtins.map (impl: {
+ name = impl;
+ value = {
+ enable = useImpl == impl;
+ };
+ }) manImplementations
+ );
};
+ };
- machineSafe = builtins.replaceStrings [ "-" ] [ "_" ];
- in
- {
- name = "man";
- meta.maintainers = [ lib.maintainers.sternenseemann ];
+ machineSafe = builtins.replaceStrings [ "-" ] [ "_" ];
+in
+{
+ name = "man";
+ meta.maintainers = [ lib.maintainers.sternenseemann ];
- nodes = lib.listToAttrs (
- builtins.map (i: {
- name = machineSafe i;
- value = makeConfig i;
- }) manImplementations
- );
+ nodes = lib.listToAttrs (
+ builtins.map (i: {
+ name = machineSafe i;
+ value = makeConfig i;
+ }) manImplementations
+ );
- testScript =
- ''
- import re
- start_all()
+ testScript =
+ ''
+ import re
+ start_all()
- def match_man_k(page, section, haystack):
- """
- Check if the man page {page}({section}) occurs in
- the output of `man -k` given as haystack. Note:
- This is not super reliable, e. g. it can't deal
- with man pages that are in multiple sections.
- """
+ def match_man_k(page, section, haystack):
+ """
+ Check if the man page {page}({section}) occurs in
+ the output of `man -k` given as haystack. Note:
+ This is not super reliable, e. g. it can't deal
+ with man pages that are in multiple sections.
+ """
- for line in haystack.split("\n"):
- # man -k can look like this:
- # page(3) - bla
- # page (3) - bla
- # pagea, pageb (3, 3P) - foo
- # pagea, pageb, pagec(3) - bar
- pages = line.split("(")[0]
- sections = re.search("\\([a-zA-Z1-9, ]+\\)", line)
- if sections is None:
- continue
- else:
- sections = sections.group(0)[1:-1]
+ for line in haystack.split("\n"):
+ # man -k can look like this:
+ # page(3) - bla
+ # page (3) - bla
+ # pagea, pageb (3, 3P) - foo
+ # pagea, pageb, pagec(3) - bar
+ pages = line.split("(")[0]
+ sections = re.search("\\([a-zA-Z1-9, ]+\\)", line)
+ if sections is None:
+ continue
+ else:
+ sections = sections.group(0)[1:-1]
- if page in pages and f'{section}' in sections:
- return True
+ if page in pages and f'{section}' in sections:
+ return True
- return False
+ return False
- ''
- + lib.concatMapStrings (machine: ''
- with subtest("Test direct man page lookups in ${machine}"):
- # man works
- ${machine}.succeed("man man > /dev/null")
- # devman works
- ${machine}.succeed("man 3 libunwind > /dev/null")
- # NixOS configuration man page is installed
- ${machine}.succeed("man configuration.nix > /dev/null")
+ ''
+ + lib.concatMapStrings (machine: ''
+ with subtest("Test direct man page lookups in ${machine}"):
+ # man works
+ ${machine}.succeed("man man > /dev/null")
+ # devman works
+ ${machine}.succeed("man 3 libunwind > /dev/null")
+ # NixOS configuration man page is installed
+ ${machine}.succeed("man configuration.nix > /dev/null")
- with subtest("Test generateCaches via man -k in ${machine}"):
- expected = [
- ("openssl", "ssl", 3),
- ("unwind", "libunwind", 3),
- ("user", "useradd", 8),
- ("user", "userdel", 8),
- ("mem", "free", 3),
- ("mem", "free", 1),
- ]
+ with subtest("Test generateCaches via man -k in ${machine}"):
+ expected = [
+ ("openssl", "ssl", 3),
+ ("unwind", "libunwind", 3),
+ ("user", "useradd", 8),
+ ("user", "userdel", 8),
+ ("mem", "free", 3),
+ ("mem", "free", 1),
+ ]
- for (keyword, page, section) in expected:
- matches = ${machine}.succeed(f"man -k {keyword}")
- if not match_man_k(page, section, matches):
- raise Exception(f"{page}({section}) missing in matches: {matches}")
- '') machineNames;
- }
-)
+ for (keyword, page, section) in expected:
+ matches = ${machine}.succeed(f"man -k {keyword}")
+ if not match_man_k(page, section, matches):
+ raise Exception(f"{page}({section}) missing in matches: {matches}")
+ '') machineNames;
+}
diff --git a/nixos/tests/matrix/conduwuit.nix b/nixos/tests/matrix/conduwuit.nix
deleted file mode 100644
index 3e1123b692a6..000000000000
--- a/nixos/tests/matrix/conduwuit.nix
+++ /dev/null
@@ -1,103 +0,0 @@
-{ lib, ... }:
-let
- name = "conduwuit";
-in
-{
- inherit name;
-
- nodes = {
- conduwuit = {
- services.conduwuit = {
- enable = true;
- settings.global = {
- server_name = name;
- address = [ "0.0.0.0" ];
- allow_registration = true;
- yes_i_am_very_very_sure_i_want_an_open_registration_server_prone_to_abuse = true;
- };
- extraEnvironment.RUST_BACKTRACE = "yes";
- };
- networking.firewall.allowedTCPPorts = [ 6167 ];
- };
- client =
- { pkgs, ... }:
- {
- environment.systemPackages = [
- (pkgs.writers.writePython3Bin "do_test" { libraries = [ pkgs.python3Packages.matrix-nio ]; } ''
- import asyncio
- import nio
-
-
- async def main() -> None:
- # Connect to conduwuit
- client = nio.AsyncClient("http://conduwuit:6167", "alice")
-
- # Register as user alice
- response = await client.register("alice", "my-secret-password")
-
- # Log in as user alice
- response = await client.login("my-secret-password")
-
- # Create a new room
- response = await client.room_create(federate=False)
- print("Matrix room create response:", response)
- assert isinstance(response, nio.RoomCreateResponse)
- room_id = response.room_id
-
- # Join the room
- response = await client.join(room_id)
- print("Matrix join response:", response)
- assert isinstance(response, nio.JoinResponse)
-
- # Send a message to the room
- response = await client.room_send(
- room_id=room_id,
- message_type="m.room.message",
- content={
- "msgtype": "m.text",
- "body": "Hello conduwuit!"
- }
- )
- print("Matrix room send response:", response)
- assert isinstance(response, nio.RoomSendResponse)
-
- # Sync responses
- response = await client.sync(timeout=30000)
- print("Matrix sync response:", response)
- assert isinstance(response, nio.SyncResponse)
-
- # Check the message was received by conduwuit
- last_message = response.rooms.join[room_id].timeline.events[-1].body
- assert last_message == "Hello conduwuit!"
-
- # Leave the room
- response = await client.room_leave(room_id)
- print("Matrix room leave response:", response)
- assert isinstance(response, nio.RoomLeaveResponse)
-
- # Close the client
- await client.close()
-
-
- if __name__ == "__main__":
- asyncio.run(main())
- '')
- ];
- };
- };
-
- testScript = ''
- start_all()
-
- with subtest("start conduwuit"):
- conduwuit.wait_for_unit("conduwuit.service")
- conduwuit.wait_for_open_port(6167)
-
- with subtest("ensure messages can be exchanged"):
- client.succeed("do_test >&2")
- '';
-
- meta.maintainers = with lib.maintainers; [
- niklaskorz
- ];
-}
diff --git a/nixos/tests/matrix/continuwuity.nix b/nixos/tests/matrix/continuwuity.nix
new file mode 100644
index 000000000000..308560421c79
--- /dev/null
+++ b/nixos/tests/matrix/continuwuity.nix
@@ -0,0 +1,104 @@
+{ lib, ... }:
+let
+ name = "continuwuity";
+in
+{
+ inherit name;
+
+ nodes = {
+ continuwuity = {
+ services.matrix-continuwuity = {
+ enable = true;
+ settings.global = {
+ server_name = name;
+ address = [ "0.0.0.0" ];
+ allow_registration = true;
+ yes_i_am_very_very_sure_i_want_an_open_registration_server_prone_to_abuse = true;
+ };
+ extraEnvironment.RUST_BACKTRACE = "yes";
+ };
+ networking.firewall.allowedTCPPorts = [ 6167 ];
+ };
+ client =
+ { pkgs, ... }:
+ {
+ environment.systemPackages = [
+ (pkgs.writers.writePython3Bin "do_test" { libraries = [ pkgs.python3Packages.matrix-nio ]; } ''
+ import asyncio
+ import nio
+
+
+ async def main() -> None:
+ # Connect to continuwuity
+ client = nio.AsyncClient("http://continuwuity:6167", "alice")
+
+ # Register as user alice
+ response = await client.register("alice", "my-secret-password")
+
+ # Log in as user alice
+ response = await client.login("my-secret-password")
+
+ # Create a new room
+ response = await client.room_create(federate=False)
+ print("Matrix room create response:", response)
+ assert isinstance(response, nio.RoomCreateResponse)
+ room_id = response.room_id
+
+ # Join the room
+ response = await client.join(room_id)
+ print("Matrix join response:", response)
+ assert isinstance(response, nio.JoinResponse)
+
+ # Send a message to the room
+ response = await client.room_send(
+ room_id=room_id,
+ message_type="m.room.message",
+ content={
+ "msgtype": "m.text",
+ "body": "Hello continuwuity!"
+ }
+ )
+ print("Matrix room send response:", response)
+ assert isinstance(response, nio.RoomSendResponse)
+
+ # Sync responses
+ response = await client.sync(timeout=30000)
+ print("Matrix sync response:", response)
+ assert isinstance(response, nio.SyncResponse)
+
+ # Check the message was received by continuwuity
+ last_message = response.rooms.join[room_id].timeline.events[-1].body
+ assert last_message == "Hello continuwuity!"
+
+ # Leave the room
+ response = await client.room_leave(room_id)
+ print("Matrix room leave response:", response)
+ assert isinstance(response, nio.RoomLeaveResponse)
+
+ # Close the client
+ await client.close()
+
+
+ if __name__ == "__main__":
+ asyncio.run(main())
+ '')
+ ];
+ };
+ };
+
+ testScript = ''
+ start_all()
+
+ with subtest("start continuwuity"):
+ continuwuity.wait_for_unit("continuwuity.service")
+ continuwuity.wait_for_open_port(6167)
+
+ with subtest("ensure messages can be exchanged"):
+ client.succeed("do_test >&2")
+ '';
+
+ meta.maintainers = with lib.maintainers; [
+ nyabinary
+ snaki
+ ];
+}
diff --git a/nixos/tests/matrix/lk-jwt-service.nix b/nixos/tests/matrix/lk-jwt-service.nix
new file mode 100644
index 000000000000..3914200babd6
--- /dev/null
+++ b/nixos/tests/matrix/lk-jwt-service.nix
@@ -0,0 +1,26 @@
+{
+ pkgs,
+ lib,
+ ...
+}:
+{
+ name = "lk-jwt-service";
+ meta.maintainers = [ lib.maintainers.quadradical ];
+
+ nodes.machine = {
+ services.lk-jwt-service = {
+ enable = true;
+ keyFile = pkgs.writers.writeYAML "keys.yaml" {
+ key = "f6lQGaHtM5HfgZjIcec3cOCRfiDqIine4CpZZnqdT5cE";
+ };
+ livekitUrl = "wss://127.0.0.1:8100";
+ port = 8000;
+ };
+ };
+
+ testScript = ''
+ machine.wait_for_unit("lk-jwt-service.service")
+ machine.wait_for_open_port(8000)
+ machine.succeed('curl 127.0.0.1:8000/sfu/get -sLX POST -w "%{http_code}" | grep -q "^400"')
+ '';
+}
diff --git a/nixos/tests/mattermost/default.nix b/nixos/tests/mattermost/default.nix
index eb7b814e3983..4cb9845df880 100644
--- a/nixos/tests/mattermost/default.nix
+++ b/nixos/tests/mattermost/default.nix
@@ -33,7 +33,7 @@ import ../make-test-python.nix (
);
};
- system.stateVersion = lib.mkDefault "25.05";
+ system.stateVersion = lib.mkDefault (lib.versions.majorMinor lib.version);
services.mattermost = lib.recursiveUpdate {
enable = true;
@@ -63,7 +63,7 @@ import ../make-test-python.nix (
# Upgrade to the latest Mattermost.
specialisation.latest.configuration = {
services.mattermost.package = lib.mkForce pkgs.mattermostLatest;
- system.stateVersion = lib.mkVMOverride "25.05";
+ system.stateVersion = lib.mkVMOverride (lib.versions.majorMinor lib.version);
};
}
)
@@ -90,57 +90,60 @@ import ../make-test-python.nix (
name = "mattermost";
nodes = rec {
- postgresMutable =
+ postgresMutable = makeMattermost {
+ mutableConfig = true;
+ preferNixConfig = false;
+ settings.SupportSettings.HelpLink = "https://search.nixos.org";
+ } { };
+ postgresMostlyMutable =
makeMattermost
{
mutableConfig = true;
- preferNixConfig = false;
- settings.SupportSettings.HelpLink = "https://search.nixos.org";
+ preferNixConfig = true;
+ plugins = with pkgs; [
+ # Build the demo plugin.
+ (mattermost.buildPlugin {
+ pname = "mattermost-plugin-starter-template";
+ version = "0.1.0";
+ src = fetchFromGitHub {
+ owner = "mattermost";
+ repo = "mattermost-plugin-starter-template";
+ # Newer versions have issues with their dependency lockfile.
+ rev = "7c98e89ac1a268ce8614bc665571b7bbc9a70df2";
+ hash = "sha256-uyfxB0GZ45qL9ssWUord0eKQC6S0TlCTtjTOXWtK4H0=";
+ };
+ vendorHash = "sha256-Jl4F9YkHNqiFP9/yeyi4vTntqxMk/J1zhEP6QLSvJQA=";
+ npmDepsHash = "sha256-z08nc4XwT+uQjQlZiUydJyh8mqeJoYdPFWuZpw9k99s=";
+ })
+
+ # Build the todos plugin.
+ (mattermost.buildPlugin {
+ pname = "mattermost-plugin-todo";
+ version = "0.8-pre";
+ src = fetchFromGitHub {
+ owner = "mattermost-community";
+ repo = "mattermost-plugin-todo";
+ # 0.7.1 didn't work, seems to use an older set of node dependencies.
+ rev = "f25dc91ea401c9f0dcd4abcebaff10eb8b9836e5";
+ hash = "sha256-OM+m4rTqVtolvL5tUE8RKfclqzoe0Y38jLU60Pz7+HI=";
+ };
+ vendorHash = "sha256-5KpechSp3z/Nq713PXYruyNxveo6CwrCSKf2JaErbgg=";
+ npmDepsHash = "sha256-o2UOEkwb8Vx2lDWayNYgng0GXvmS6lp/ExfOq3peyMY=";
+ extraGoModuleAttrs = {
+ npmFlags = [ "--legacy-peer-deps" ];
+ };
+ })
+ ];
}
{
# Last version to support the "old" config layout.
system.stateVersion = lib.mkForce "24.11";
- # First version to support the "new" config layout.
- specialisation.upgrade.configuration.system.stateVersion = lib.mkVMOverride "25.05";
+ # Supports the "new" config layout.
+ specialisation.upgrade.configuration.system.stateVersion = lib.mkVMOverride (
+ lib.versions.majorMinor lib.version
+ );
};
- postgresMostlyMutable = makeMattermost {
- mutableConfig = true;
- plugins = with pkgs; [
- # Build the demo plugin.
- (mattermost.buildPlugin {
- pname = "mattermost-plugin-starter-template";
- version = "0.1.0";
- src = fetchFromGitHub {
- owner = "mattermost";
- repo = "mattermost-plugin-starter-template";
- # Newer versions have issues with their dependency lockfile.
- rev = "7c98e89ac1a268ce8614bc665571b7bbc9a70df2";
- hash = "sha256-uyfxB0GZ45qL9ssWUord0eKQC6S0TlCTtjTOXWtK4H0=";
- };
- vendorHash = "sha256-Jl4F9YkHNqiFP9/yeyi4vTntqxMk/J1zhEP6QLSvJQA=";
- npmDepsHash = "sha256-z08nc4XwT+uQjQlZiUydJyh8mqeJoYdPFWuZpw9k99s=";
- })
-
- # Build the todos plugin.
- (mattermost.buildPlugin {
- pname = "mattermost-plugin-todo";
- version = "0.8-pre";
- src = fetchFromGitHub {
- owner = "mattermost-community";
- repo = "mattermost-plugin-todo";
- # 0.7.1 didn't work, seems to use an older set of node dependencies.
- rev = "f25dc91ea401c9f0dcd4abcebaff10eb8b9836e5";
- hash = "sha256-OM+m4rTqVtolvL5tUE8RKfclqzoe0Y38jLU60Pz7+HI=";
- };
- vendorHash = "sha256-5KpechSp3z/Nq713PXYruyNxveo6CwrCSKf2JaErbgg=";
- npmDepsHash = "sha256-o2UOEkwb8Vx2lDWayNYgng0GXvmS6lp/ExfOq3peyMY=";
- extraGoModuleAttrs = {
- npmFlags = [ "--legacy-peer-deps" ];
- };
- })
- ];
- } { };
postgresImmutable = makeMattermost {
package = pkgs.mattermost.overrideAttrs (prev: {
webapp = prev.webapp.overrideAttrs (prevWebapp: {
@@ -332,9 +335,23 @@ import ../make-test-python.nix (
if [ "$actualPostAttachmentHash" != "$postAttachmentHash" ]; then
echo "Post attachment hash mismatched!" >&2
exit 1
- else
+ fi
+
+ # Make sure it's on the filesystem in the expected place
+ fsPath="$(find /var/lib/mattermost/data -name "$(basename -- "$postAttachment")" -print -quit)"
+ if [ -z "$fsPath" ] || [ ! -f "$fsPath" ]; then
+ echo "Attachment didn't exist on the filesystem!" >&2
+ exit 1
+ fi
+
+ # And that the hash matches.
+ actualFsAttachmentHash="$(sha256sum "$fsPath" | awk '{print $1}')"
+ if [ "$actualFsAttachmentHash" == "$postAttachmentHash" ]; then
echo "Post attachment hash was OK!" >&2
exit 0
+ else
+ echo "Attachment hash mismatched on disk!" >&2
+ exit 1
fi
else
echo "Post didn't exist when it should have!" >&2
@@ -343,9 +360,14 @@ import ../make-test-python.nix (
'';
in
''
+ import sys
import shlex
+ import threading
+ import queue
def wait_mattermost_up(node, site_name="${siteName}"):
+ print(f"wait_mattermost_up({node.name!r}, site_name={site_name!r})", file=sys.stderr)
+ node.wait_for_unit("multi-user.target")
node.systemctl("start mattermost.service")
node.wait_for_unit("mattermost.service")
node.wait_for_open_port(8065)
@@ -353,20 +375,25 @@ import ../make-test-python.nix (
node.succeed(f"curl {shlex.quote('${url}')}/index.html | grep {shlex.quote(site_name)}")
def restart_mattermost(node, site_name="${siteName}"):
+ print(f"restart_mattermost({node.name!r}, site_name={site_name!r})", file=sys.stderr)
node.systemctl("restart mattermost.service")
wait_mattermost_up(node, site_name)
def expect_config(node, mattermost_version, *configs):
+ print(f"expect_config({node.name!r}, {mattermost_version!r}, *{configs!r})", file=sys.stderr)
for config in configs:
node.succeed(f"${expectConfig} {shlex.quote(config)} {shlex.quote(mattermost_version)}")
def expect_plugins(node, jq_or_code):
+ print(f"expect_plugins({node.name!r}, {jq_or_code!r})", file=sys.stderr)
node.succeed(f"${expectPlugins} {shlex.quote(str(jq_or_code))}")
def ensure_post(node, fail_if_not_found=False):
+ print(f"ensure_post({node.name!r}, fail_if_not_found={fail_if_not_found!r})", file=sys.stderr)
node.succeed(f"${ensurePost} {shlex.quote('${url}')} {1 if fail_if_not_found else 0}")
- def set_config(node, *configs, nixos_version='25.05'):
+ def set_config(node, *configs, nixos_version='${lib.versions.majorMinor lib.version}'):
+ print(f"set_config({node.name!r}, *{configs!r}, nixos_version={nixos_version!r})", file=sys.stderr)
for config in configs:
args = [shlex.quote("${setConfig}")]
args.append(shlex.quote(config))
@@ -374,8 +401,13 @@ import ../make-test-python.nix (
args.append(shlex.quote(str(nixos_version)))
node.succeed(' '.join(args))
- def run_mattermost_tests(mutableToplevel: str, mutable,
- mostlyMutableToplevel: str, mostlyMutable,
+ def switch_to_specialisation(node, toplevel: str, specialisation: str):
+ print(f"switch_to_specialisation({node.name!r}, {toplevel!r}, {specialisation!r})", file=sys.stderr)
+ node.succeed(f"{toplevel}/specialisation/{specialisation}/bin/switch-to-configuration switch || true")
+
+ def run_mattermost_tests(shutdown_queue: queue.Queue,
+ mutableToplevel: str, mutable,
+ mostlyMutableToplevel: str, mostlyMutablePlugins: str, mostlyMutable,
immutableToplevel: str, immutable,
environmentFileToplevel: str, environmentFile):
esr, latest = '${pkgs.mattermost.version}', '${pkgs.mattermostLatest.version}'
@@ -391,8 +423,7 @@ import ../make-test-python.nix (
set_config(
mutable,
'.SupportSettings.AboutLink = "https://mattermost.com"',
- '.SupportSettings.HelpLink = "https://nixos.org/nixos/manual"',
- nixos_version='24.11' # Default 'mutable' config is an old version
+ '.SupportSettings.HelpLink = "https://nixos.org/nixos/manual"'
)
ensure_post(mutable)
restart_mattermost(mutable)
@@ -401,23 +432,14 @@ import ../make-test-python.nix (
expect_config(mutable, esr, '.AboutLink == "https://mattermost.com" and .HelpLink == "https://nixos.org/nixos/manual"')
ensure_post(mutable, fail_if_not_found=True)
- # Switch to the newer config
- mutable.succeed(f"{mutableToplevel}/specialisation/upgrade/bin/switch-to-configuration switch")
- wait_mattermost_up(mutable)
-
- # AboutLink and HelpLink should be changed, still, and the post should still exist
- expect_config(mutable, esr, '.AboutLink == "https://mattermost.com" and .HelpLink == "https://nixos.org/nixos/manual"')
- ensure_post(mutable, fail_if_not_found=True)
-
# Switch to the latest Mattermost version
- mutable.succeed(f"{mutableToplevel}/specialisation/latest/bin/switch-to-configuration switch")
+ switch_to_specialisation(mutable, mutableToplevel, "latest")
wait_mattermost_up(mutable)
# AboutLink and HelpLink should be changed, still, and the post should still exist
expect_config(mutable, latest, '.AboutLink == "https://mattermost.com" and .HelpLink == "https://nixos.org/nixos/manual"')
ensure_post(mutable, fail_if_not_found=True)
-
- mutable.shutdown()
+ shutdown_queue.put(mutable)
## Mostly mutable node tests ##
mostlyMutable.start()
@@ -434,13 +456,38 @@ import ../make-test-python.nix (
mostlyMutable,
'.SupportSettings.AboutLink = "https://mattermost.com"',
'.SupportSettings.HelpLink = "https://nixos.org/nixos/manual"',
+ nixos_version='24.11' # Default 'mostlyMutable' config is an old version
+ )
+ ensure_post(mostlyMutable)
+ restart_mattermost(mostlyMutable)
+
+ # HelpLink should be changed but AboutLink should not, and the post should exist
+ expect_config(mostlyMutable, esr, '.AboutLink == "https://nixos.org" and .HelpLink == "https://nixos.org/nixos/manual"')
+ ensure_post(mostlyMutable, fail_if_not_found=True)
+
+ # Switch to the newer config and make sure the plugins directory is replaced with a directory,
+ # since it could have been a symlink on previous versions.
+ mostlyMutable.systemctl("stop mattermost.service")
+ mostlyMutable.succeed('[ -L /var/lib/mattermost/data/plugins ] && [ -d /var/lib/mattermost/data/plugins ]')
+ switch_to_specialisation(mostlyMutable, mostlyMutableToplevel, "upgrade")
+ wait_mattermost_up(mostlyMutable)
+
+ # HelpLink should be changed, still, and the post should still exist
+ expect_config(mostlyMutable, esr, '.AboutLink == "https://nixos.org" and .HelpLink == "https://nixos.org/nixos/manual"')
+ ensure_post(mostlyMutable, fail_if_not_found=True)
+
+ # Edit the config and make a post
+ set_config(
+ mostlyMutable,
+ '.SupportSettings.AboutLink = "https://mattermost.com/foo"',
+ '.SupportSettings.HelpLink = "https://nixos.org/nixos/manual/bar"',
'.PluginSettings.PluginStates."com.mattermost.plugin-todo".Enable = true'
)
ensure_post(mostlyMutable)
restart_mattermost(mostlyMutable)
# AboutLink should be overridden by NixOS configuration; HelpLink should be what we set above
- expect_config(mostlyMutable, esr, '.AboutLink == "https://nixos.org" and .HelpLink == "https://nixos.org/nixos/manual"')
+ expect_config(mostlyMutable, esr, '.AboutLink == "https://nixos.org" and .HelpLink == "https://nixos.org/nixos/manual/bar"')
# Single plugin that's now enabled.
expect_plugins(mostlyMutable, 'length == 1')
@@ -449,14 +496,14 @@ import ../make-test-python.nix (
ensure_post(mostlyMutable, fail_if_not_found=True)
# Switch to the latest Mattermost version
- mostlyMutable.succeed(f"{mostlyMutableToplevel}/specialisation/latest/bin/switch-to-configuration switch")
+ switch_to_specialisation(mostlyMutable, mostlyMutableToplevel, "latest")
wait_mattermost_up(mostlyMutable)
# AboutLink should be overridden and the post should still exist
- expect_config(mostlyMutable, latest, '.AboutLink == "https://nixos.org" and .HelpLink == "https://nixos.org/nixos/manual"')
+ expect_config(mostlyMutable, latest, '.AboutLink == "https://nixos.org" and .HelpLink == "https://nixos.org/nixos/manual/bar"')
ensure_post(mostlyMutable, fail_if_not_found=True)
- mostlyMutable.shutdown()
+ shutdown_queue.put(mostlyMutable)
## Immutable node tests ##
immutable.start()
@@ -484,14 +531,14 @@ import ../make-test-python.nix (
ensure_post(immutable, fail_if_not_found=True)
# Switch to the latest Mattermost version
- immutable.succeed(f"{immutableToplevel}/specialisation/latest/bin/switch-to-configuration switch")
+ switch_to_specialisation(immutable, immutableToplevel, "latest")
wait_mattermost_up(immutable)
# AboutLink and HelpLink should be changed, still, and the post should still exist
expect_config(immutable, latest, '.AboutLink == "https://nixos.org" and .HelpLink == "https://search.nixos.org"')
ensure_post(immutable, fail_if_not_found=True)
- immutable.shutdown()
+ shutdown_queue.put(immutable)
## Environment File node tests ##
environmentFile.start()
@@ -503,36 +550,56 @@ import ../make-test-python.nix (
ensure_post(environmentFile, fail_if_not_found=True)
# Switch to the latest Mattermost version
- environmentFile.succeed(f"{environmentFileToplevel}/specialisation/latest/bin/switch-to-configuration switch")
+ switch_to_specialisation(environmentFile, environmentFileToplevel, "latest")
wait_mattermost_up(environmentFile)
# AboutLink should be changed still, and the post should still exist
expect_config(environmentFile, latest, '.AboutLink == "https://nixos.org"')
ensure_post(environmentFile, fail_if_not_found=True)
- environmentFile.shutdown()
-
- run_mattermost_tests(
- "${nodes.mysqlMutable.system.build.toplevel}",
- mysqlMutable,
- "${nodes.mysqlMostlyMutable.system.build.toplevel}",
- mysqlMostlyMutable,
- "${nodes.mysqlImmutable.system.build.toplevel}",
- mysqlImmutable,
- "${nodes.mysqlEnvironmentFile.system.build.toplevel}",
- mysqlEnvironmentFile
- )
+ shutdown_queue.put(environmentFile)
+
+ # Run shutdowns asynchronously so we can pipeline them.
+ shutdown_queue: queue.Queue = queue.Queue()
+ def shutdown_worker():
+ while True:
+ node = shutdown_queue.get()
+ print(f"Shutting down node {node.name!r} asynchronously", file=sys.stderr)
+ node.shutdown()
+ shutdown_queue.task_done()
+ threading.Thread(target=shutdown_worker, daemon=True).start()
+
+ ${pkgs.lib.optionalString pkgs.stdenv.isx86_64 ''
+ # Only run the MySQL tests on x86_64 so we don't have to debug MySQL ARM issues.
+ run_mattermost_tests(
+ shutdown_queue,
+ "${nodes.mysqlMutable.system.build.toplevel}",
+ mysqlMutable,
+ "${nodes.mysqlMostlyMutable.system.build.toplevel}",
+ "${nodes.mysqlMostlyMutable.services.mattermost.pluginsBundle}",
+ mysqlMostlyMutable,
+ "${nodes.mysqlImmutable.system.build.toplevel}",
+ mysqlImmutable,
+ "${nodes.mysqlEnvironmentFile.system.build.toplevel}",
+ mysqlEnvironmentFile
+ )
+ ''}
run_mattermost_tests(
+ shutdown_queue,
"${nodes.postgresMutable.system.build.toplevel}",
postgresMutable,
"${nodes.postgresMostlyMutable.system.build.toplevel}",
+ "${nodes.postgresMostlyMutable.services.mattermost.pluginsBundle}",
postgresMostlyMutable,
"${nodes.postgresImmutable.system.build.toplevel}",
postgresImmutable,
"${nodes.postgresEnvironmentFile.system.build.toplevel}",
postgresEnvironmentFile
)
+
+ # Drain the queue
+ shutdown_queue.join()
'';
}
)
diff --git a/nixos/tests/mealie.nix b/nixos/tests/mealie.nix
index 57bff29731d8..21e96dea4114 100644
--- a/nixos/tests/mealie.nix
+++ b/nixos/tests/mealie.nix
@@ -10,20 +10,35 @@ import ./make-test-python.nix (
];
};
- nodes = {
- server = {
- services.mealie = {
- enable = true;
- port = 9001;
+ nodes =
+ let
+ sqlite = {
+ services.mealie = {
+ enable = true;
+ port = 9001;
+ };
};
+ postgres = {
+ imports = [ sqlite ];
+ services.mealie.database.createLocally = true;
+ };
+ in
+ {
+ inherit sqlite postgres;
};
- };
testScript = ''
start_all()
- server.wait_for_unit("mealie.service")
- server.wait_for_open_port(9001)
- server.succeed("curl --fail http://localhost:9001")
+
+ def test_mealie(node):
+ node.wait_for_unit("mealie.service")
+ node.wait_for_open_port(9001)
+ node.succeed("curl --fail http://localhost:9001")
+
+ test_mealie(sqlite)
+ simple.send_monitor_command("quit")
+ simple.wait_for_shutdown()
+ test_mealie(postgres)
'';
}
)
diff --git a/nixos/tests/memcached.nix b/nixos/tests/memcached.nix
index c3f8734e320c..de2d7ab421b6 100644
--- a/nixos/tests/memcached.nix
+++ b/nixos/tests/memcached.nix
@@ -1,32 +1,30 @@
-import ./make-test-python.nix (
- { pkgs, ... }:
- {
- name = "memcached";
+{ pkgs, ... }:
+{
+ name = "memcached";
- nodes.machine = {
- imports = [ ../modules/profiles/minimal.nix ];
- services.memcached.enable = true;
- };
+ nodes.machine = {
+ imports = [ ../modules/profiles/minimal.nix ];
+ services.memcached.enable = true;
+ };
- testScript =
- let
- testScript =
- pkgs.writers.writePython3 "test_memcache"
- {
- libraries = with pkgs.python3Packages; [ memcached ];
- }
- ''
- import memcache
- c = memcache.Client(['localhost:11211'])
- c.set('key', 'value')
- assert 'value' == c.get('key')
- '';
- in
- ''
- machine.start()
- machine.wait_for_unit("memcached.service")
- machine.wait_for_open_port(11211)
- machine.succeed("${testScript}")
- '';
- }
-)
+ testScript =
+ let
+ testScript =
+ pkgs.writers.writePython3 "test_memcache"
+ {
+ libraries = [ pkgs.python3Packages.python-memcached ];
+ }
+ ''
+ import memcache
+ c = memcache.Client(['localhost:11211'])
+ c.set('key', 'value')
+ assert 'value' == c.get('key')
+ '';
+ in
+ ''
+ machine.start()
+ machine.wait_for_unit("memcached.service")
+ machine.wait_for_open_port(11211)
+ machine.succeed("${testScript}")
+ '';
+}
diff --git a/nixos/tests/moodle.nix b/nixos/tests/moodle.nix
index 4bf1c5e6c702..f3eee04b0d62 100644
--- a/nixos/tests/moodle.nix
+++ b/nixos/tests/moodle.nix
@@ -1,26 +1,24 @@
-import ./make-test-python.nix (
- { pkgs, lib, ... }:
- {
- name = "moodle";
- meta.maintainers = [ lib.maintainers.aanderse ];
+{ lib, ... }:
+{
+ name = "moodle";
+ meta.maintainers = [ lib.maintainers.aanderse ];
- nodes.machine =
- { ... }:
- {
- services.moodle.enable = true;
- services.moodle.virtualHost.hostName = "localhost";
- services.moodle.virtualHost.adminAddr = "root@example.com";
- services.moodle.initialPassword = "correcthorsebatterystaple";
+ nodes.machine =
+ { ... }:
+ {
+ services.moodle.enable = true;
+ services.moodle.virtualHost.hostName = "localhost";
+ services.moodle.virtualHost.adminAddr = "root@example.com";
+ services.moodle.initialPassword = "correcthorsebatterystaple";
- # Ensure the virtual machine has enough memory to avoid errors like:
- # Fatal error: Out of memory (allocated 152047616) (tried to allocate 33554440 bytes)
- virtualisation.memorySize = 2000;
- };
+ # Ensure the virtual machine has enough memory to avoid errors like:
+ # Fatal error: Out of memory (allocated 152047616) (tried to allocate 33554440 bytes)
+ virtualisation.memorySize = 2000;
+ };
- testScript = ''
- start_all()
- machine.wait_for_unit("phpfpm-moodle.service", timeout=1800)
- machine.wait_until_succeeds("curl http://localhost/ | grep 'You are not logged in'")
- '';
- }
-)
+ testScript = ''
+ start_all()
+ machine.wait_for_unit("phpfpm-moodle.service", timeout=1800)
+ machine.wait_until_succeeds("curl http://localhost/ | grep 'You are not logged in'")
+ '';
+}
diff --git a/nixos/tests/mpd.nix b/nixos/tests/mpd.nix
index 36215b780c5d..f80c6658c621 100644
--- a/nixos/tests/mpd.nix
+++ b/nixos/tests/mpd.nix
@@ -1,150 +1,148 @@
-import ./make-test-python.nix (
- { pkgs, lib, ... }:
- let
- track = pkgs.fetchurl {
- # Sourced from http://freemusicarchive.org/music/Blue_Wave_Theory/Surf_Music_Month_Challenge/Skyhawk_Beach_fade_in
+{ pkgs, lib, ... }:
+let
+ track = pkgs.fetchurl {
+ # Sourced from http://freemusicarchive.org/music/Blue_Wave_Theory/Surf_Music_Month_Challenge/Skyhawk_Beach_fade_in
- name = "Blue_Wave_Theory-Skyhawk_Beach.mp3";
- url = "https://freemusicarchive.org/file/music/ccCommunity/Blue_Wave_Theory/Surf_Music_Month_Challenge/Blue_Wave_Theory_-_04_-_Skyhawk_Beach.mp3";
- hash = "sha256-91VDWwrcP6Cw4rk72VHvZ8RGfRBrpRE8xo/02dcJhHc=";
- meta.license = lib.licenses.cc-by-sa-40;
- };
+ name = "Blue_Wave_Theory-Skyhawk_Beach.mp3";
+ url = "https://freemusicarchive.org/file/music/ccCommunity/Blue_Wave_Theory/Surf_Music_Month_Challenge/Blue_Wave_Theory_-_04_-_Skyhawk_Beach.mp3";
+ hash = "sha256-91VDWwrcP6Cw4rk72VHvZ8RGfRBrpRE8xo/02dcJhHc=";
+ meta.license = lib.licenses.cc-by-sa-40;
+ };
- defaultCfg = rec {
- user = "mpd";
- group = "mpd";
- dataDir = "/var/lib/mpd";
- musicDirectory = "${dataDir}/music";
- };
+ defaultCfg = rec {
+ user = "mpd";
+ group = "mpd";
+ dataDir = "/var/lib/mpd";
+ musicDirectory = "${dataDir}/music";
+ };
- defaultMpdCfg = {
- inherit (defaultCfg)
- dataDir
- musicDirectory
- user
- group
- ;
- enable = true;
- };
+ defaultMpdCfg = {
+ inherit (defaultCfg)
+ dataDir
+ musicDirectory
+ user
+ group
+ ;
+ enable = true;
+ };
- musicService =
- {
- user,
- group,
- musicDirectory,
- }:
- {
- description = "Sets up the music file(s) for MPD to use.";
- requires = [ "mpd.service" ];
- after = [ "mpd.service" ];
- wantedBy = [ "default.target" ];
- script = ''
- cp ${track} ${musicDirectory}
- '';
- serviceConfig = {
- User = user;
- Group = group;
- };
+ musicService =
+ {
+ user,
+ group,
+ musicDirectory,
+ }:
+ {
+ description = "Sets up the music file(s) for MPD to use.";
+ requires = [ "mpd.service" ];
+ after = [ "mpd.service" ];
+ wantedBy = [ "default.target" ];
+ script = ''
+ cp ${track} ${musicDirectory}
+ '';
+ serviceConfig = {
+ User = user;
+ Group = group;
};
-
- mkServer =
- { mpd, musicService }:
- {
- boot.kernelModules = [ "snd-dummy" ];
- services.mpd = mpd;
- systemd.services.musicService = musicService;
- };
- in
- {
- name = "mpd";
- meta = {
- maintainers = with lib.maintainers; [ emmanuelrosa ];
};
- nodes = {
- client = { ... }: { };
-
- serverALSA =
- { ... }:
- lib.mkMerge [
- (mkServer {
- mpd = defaultMpdCfg // {
- network.listenAddress = "any";
- extraConfig = ''
- audio_output {
- type "alsa"
- name "ALSA"
- mixer_type "null"
- }
- '';
- };
- musicService = musicService { inherit (defaultMpdCfg) user group musicDirectory; };
- })
- { networking.firewall.allowedTCPPorts = [ 6600 ]; }
- ];
-
- serverPulseAudio =
- { ... }:
- lib.mkMerge [
- (mkServer {
- mpd = defaultMpdCfg // {
- extraConfig = ''
- audio_output {
- type "pulse"
- name "The Pulse"
- }
- '';
- };
-
- musicService = musicService { inherit (defaultMpdCfg) user group musicDirectory; };
- })
- {
- services.pulseaudio = {
- enable = true;
- systemWide = true;
- tcp.enable = true;
- tcp.anonymousClients.allowAll = true;
- };
- systemd.services.mpd.environment.PULSE_SERVER = "localhost";
- }
- ];
+ mkServer =
+ { mpd, musicService }:
+ {
+ boot.kernelModules = [ "snd-dummy" ];
+ services.mpd = mpd;
+ systemd.services.musicService = musicService;
};
+in
+{
+ name = "mpd";
+ meta = {
+ maintainers = with lib.maintainers; [ emmanuelrosa ];
+ };
- testScript = ''
- mpc = "${lib.getExe pkgs.mpc} --wait"
+ nodes = {
+ client = { ... }: { };
- # Connects to the given server and attempts to play a tune.
- def play_some_music(server):
- server.wait_for_unit("mpd.service")
- server.succeed(f"{mpc} update")
- _, tracks = server.execute(f"{mpc} ls")
+ serverALSA =
+ { ... }:
+ lib.mkMerge [
+ (mkServer {
+ mpd = defaultMpdCfg // {
+ network.listenAddress = "any";
+ extraConfig = ''
+ audio_output {
+ type "alsa"
+ name "ALSA"
+ mixer_type "null"
+ }
+ '';
+ };
+ musicService = musicService { inherit (defaultMpdCfg) user group musicDirectory; };
+ })
+ { networking.firewall.allowedTCPPorts = [ 6600 ]; }
+ ];
- for track in tracks.splitlines():
- server.succeed(f"{mpc} add {track}")
+ serverPulseAudio =
+ { ... }:
+ lib.mkMerge [
+ (mkServer {
+ mpd = defaultMpdCfg // {
+ extraConfig = ''
+ audio_output {
+ type "pulse"
+ name "The Pulse"
+ }
+ '';
+ };
- _, added_tracks = server.execute(f"{mpc} playlist")
+ musicService = musicService { inherit (defaultMpdCfg) user group musicDirectory; };
+ })
+ {
+ services.pulseaudio = {
+ enable = true;
+ systemWide = true;
+ tcp.enable = true;
+ tcp.anonymousClients.allowAll = true;
+ };
+ systemd.services.mpd.environment.PULSE_SERVER = "localhost";
+ }
+ ];
+ };
- # Check we succeeded adding audio tracks to the playlist
- assert len(added_tracks.splitlines()) > 0
+ testScript = ''
+ mpc = "${lib.getExe pkgs.mpc} --wait"
- server.succeed(f"{mpc} play")
+ # Connects to the given server and attempts to play a tune.
+ def play_some_music(server):
+ server.wait_for_unit("mpd.service")
+ server.succeed(f"{mpc} update")
+ _, tracks = server.execute(f"{mpc} ls")
- _, output = server.execute(f"{mpc} status")
- # Assure audio track is playing
- assert "playing" in output
+ for track in tracks.splitlines():
+ server.succeed(f"{mpc} add {track}")
- server.succeed(f"{mpc} stop")
+ _, added_tracks = server.execute(f"{mpc} playlist")
+
+ # Check we succeeded adding audio tracks to the playlist
+ assert len(added_tracks.splitlines()) > 0
+
+ server.succeed(f"{mpc} play")
+
+ _, output = server.execute(f"{mpc} status")
+ # Assure audio track is playing
+ assert "playing" in output
+
+ server.succeed(f"{mpc} stop")
- play_some_music(serverALSA)
- play_some_music(serverPulseAudio)
+ play_some_music(serverALSA)
+ play_some_music(serverPulseAudio)
- client.wait_for_unit("multi-user.target")
- client.succeed(f"{mpc} -h serverALSA status")
+ client.wait_for_unit("multi-user.target")
+ client.succeed(f"{mpc} -h serverALSA status")
- # The PulseAudio-based server is configured not to accept external client connections
- # to perform the following test:
- client.fail(f"{mpc} -h serverPulseAudio status")
- '';
- }
-)
+ # The PulseAudio-based server is configured not to accept external client connections
+ # to perform the following test:
+ client.fail(f"{mpc} -h serverPulseAudio status")
+ '';
+}
diff --git a/nixos/tests/mumble.nix b/nixos/tests/mumble.nix
index c8bbe473c2b1..f4b5405a2e4c 100644
--- a/nixos/tests/mumble.nix
+++ b/nixos/tests/mumble.nix
@@ -1,95 +1,93 @@
-import ./make-test-python.nix (
- { pkgs, ... }:
+{ pkgs, ... }:
- let
- client =
- { pkgs, ... }:
+let
+ client =
+ { pkgs, ... }:
+ {
+ imports = [ ./common/x11.nix ];
+ environment.systemPackages = [ pkgs.mumble ];
+ };
+
+ # outside of tests, this file should obviously not come from the nix store
+ envFile = pkgs.writeText "nixos-test-mumble-murmurd.env" ''
+ MURMURD_PASSWORD=testpassword
+ '';
+
+in
+{
+ name = "mumble";
+ meta = with pkgs.lib.maintainers; {
+ maintainers = [ thoughtpolice ];
+ };
+
+ nodes = {
+ server =
+ { config, ... }:
{
- imports = [ ./common/x11.nix ];
- environment.systemPackages = [ pkgs.mumble ];
+ security.apparmor.enable = true;
+ services.murmur.enable = true;
+ services.murmur.registerName = "NixOS tests";
+ services.murmur.password = "$MURMURD_PASSWORD";
+ services.murmur.environmentFile = envFile;
+ networking.firewall.allowedTCPPorts = [ config.services.murmur.port ];
};
- # outside of tests, this file should obviously not come from the nix store
- envFile = pkgs.writeText "nixos-test-mumble-murmurd.env" ''
- MURMURD_PASSWORD=testpassword
- '';
+ client1 = client;
+ client2 = client;
+ };
- in
- {
- name = "mumble";
- meta = with pkgs.lib.maintainers; {
- maintainers = [ thoughtpolice ];
- };
+ testScript = ''
+ start_all()
- nodes = {
- server =
- { config, ... }:
- {
- security.apparmor.enable = true;
- services.murmur.enable = true;
- services.murmur.registerName = "NixOS tests";
- services.murmur.password = "$MURMURD_PASSWORD";
- services.murmur.environmentFile = envFile;
- networking.firewall.allowedTCPPorts = [ config.services.murmur.port ];
- };
+ server.wait_for_unit("murmur.service")
+ client1.wait_for_x()
+ client2.wait_for_x()
- client1 = client;
- client2 = client;
- };
+ client1.execute("mumble mumble://client1:testpassword\@server/test >&2 &")
+ client2.execute("mumble mumble://client2:testpassword\@server/test >&2 &")
- testScript = ''
- start_all()
+ # cancel client audio configuration
+ client1.wait_for_window(r"Audio Tuning Wizard")
+ client2.wait_for_window(r"Audio Tuning Wizard")
+ server.sleep(5) # wait because mumble is slow to register event handlers
+ client1.send_key("esc")
+ client2.send_key("esc")
- server.wait_for_unit("murmur.service")
- client1.wait_for_x()
- client2.wait_for_x()
+ # cancel client cert configuration
+ client1.wait_for_window(r"Certificate Management")
+ client2.wait_for_window(r"Certificate Management")
+ server.sleep(5) # wait because mumble is slow to register event handlers
+ client1.send_key("esc")
+ client2.send_key("esc")
- client1.execute("mumble mumble://client1:testpassword\@server/test >&2 &")
- client2.execute("mumble mumble://client2:testpassword\@server/test >&2 &")
+ # accept server certificate
+ client1.wait_for_window(r"^Mumble$")
+ client2.wait_for_window(r"^Mumble$")
+ server.sleep(5) # wait because mumble is slow to register event handlers
+ client1.send_chars("y")
+ client2.send_chars("y")
+ server.sleep(5) # wait because mumble is slow to register event handlers
- # cancel client audio configuration
- client1.wait_for_window(r"Audio Tuning Wizard")
- client2.wait_for_window(r"Audio Tuning Wizard")
- server.sleep(5) # wait because mumble is slow to register event handlers
- client1.send_key("esc")
- client2.send_key("esc")
+ # sometimes the wrong of the 2 windows is focused, we switch focus and try pressing "y" again
+ client1.send_key("alt-tab")
+ client2.send_key("alt-tab")
+ server.sleep(5) # wait because mumble is slow to register event handlers
+ client1.send_chars("y")
+ client2.send_chars("y")
- # cancel client cert configuration
- client1.wait_for_window(r"Certificate Management")
- client2.wait_for_window(r"Certificate Management")
- server.sleep(5) # wait because mumble is slow to register event handlers
- client1.send_key("esc")
- client2.send_key("esc")
+ # Find clients in logs
+ server.wait_until_succeeds(
+ "journalctl -eu murmur -o cat | grep -q 'client1.\+Authenticated'"
+ )
+ server.wait_until_succeeds(
+ "journalctl -eu murmur -o cat | grep -q 'client2.\+Authenticated'"
+ )
- # accept server certificate
- client1.wait_for_window(r"^Mumble$")
- client2.wait_for_window(r"^Mumble$")
- server.sleep(5) # wait because mumble is slow to register event handlers
- client1.send_chars("y")
- client2.send_chars("y")
- server.sleep(5) # wait because mumble is slow to register event handlers
+ server.sleep(5) # wait to get screenshot
+ client1.screenshot("screen1")
+ client2.screenshot("screen2")
- # sometimes the wrong of the 2 windows is focused, we switch focus and try pressing "y" again
- client1.send_key("alt-tab")
- client2.send_key("alt-tab")
- server.sleep(5) # wait because mumble is slow to register event handlers
- client1.send_chars("y")
- client2.send_chars("y")
-
- # Find clients in logs
- server.wait_until_succeeds(
- "journalctl -eu murmur -o cat | grep -q 'client1.\+Authenticated'"
- )
- server.wait_until_succeeds(
- "journalctl -eu murmur -o cat | grep -q 'client2.\+Authenticated'"
- )
-
- server.sleep(5) # wait to get screenshot
- client1.screenshot("screen1")
- client2.screenshot("screen2")
-
- # check if apparmor denied anything
- server.fail('journalctl -b --no-pager --grep "^audit: .*apparmor=\\"DENIED\\""')
- '';
- }
-)
+ # check if apparmor denied anything
+ server.fail('journalctl -b --no-pager --grep "^audit: .*apparmor=\\"DENIED\\""')
+ '';
+}
diff --git a/nixos/tests/mysql/mariadb-galera.nix b/nixos/tests/mysql/mariadb-galera.nix
index 65705afbf82c..1f08eb697504 100644
--- a/nixos/tests/mysql/mariadb-galera.nix
+++ b/nixos/tests/mysql/mariadb-galera.nix
@@ -58,30 +58,6 @@ let
extraHosts = lib.concatMapStringsSep "\n" (i: "192.168.1.${toString i} galera_0${toString i}") (
lib.range 1 6
);
- firewall.allowedTCPPorts = [
- 3306
- 4444
- 4567
- 4568
- ];
- firewall.allowedUDPPorts = [ 4567 ];
- };
- systemd.services.mysql = with pkgs; {
- path = with pkgs; [
- bash
- gawk
- gnutar
- gzip
- inetutils
- iproute2
- netcat
- procps
- pv
- rsync
- socat
- stunnel
- which
- ];
};
services.mysql = {
enable = true;
@@ -101,27 +77,24 @@ let
FLUSH PRIVILEGES;
''
);
+
+ galeraCluster = {
+ enable = true;
+ package = galeraPackage;
+ sstMethod = method;
+
+ localAddress = address;
+ localName = "galera_0${toString id}";
+
+ clusterAddress =
+ "gcomm://"
+ + lib.optionalString (id == 2 || id == 3) "galera_01,galera_02,galera_03"
+ + lib.optionalString (id == 5 || id == 6) "galera_04,galera_05,galera_06";
+ };
+
settings = {
- mysqld = {
- bind_address = "0.0.0.0";
- };
galera = {
- wsrep_on = "ON";
wsrep_debug = "NONE";
- wsrep_retry_autocommit = "3";
- wsrep_provider = "${galeraPackage}/lib/galera/libgalera_smm.so";
- wsrep_cluster_address =
- "gcomm://"
- + lib.optionalString (id == 2 || id == 3) "galera_01,galera_02,galera_03"
- + lib.optionalString (id == 5 || id == 6) "galera_04,galera_05,galera_06";
- wsrep_cluster_name = "galera";
- wsrep_node_address = address;
- wsrep_node_name = "galera_0${toString id}";
- wsrep_sst_method = method;
- wsrep_sst_auth = "check_repl:check_pass";
- binlog_format = "ROW";
- enforce_storage_engine = "InnoDB";
- innodb_autoinc_lock_mode = "2";
};
};
};
diff --git a/nixos/tests/nebula.nix b/nixos/tests/nebula.nix
index b3096424a614..68a48d4c86a2 100644
--- a/nixos/tests/nebula.nix
+++ b/nixos/tests/nebula.nix
@@ -14,7 +14,10 @@ import ./make-test-python.nix (
lib.mkMerge [
{
# Expose nebula for doing cert signing.
- environment.systemPackages = [ pkgs.nebula ];
+ environment.systemPackages = [
+ pkgs.dig
+ pkgs.nebula
+ ];
users.users.root.openssh.authorizedKeys.keys = [ snakeOilPublicKey ];
services.openssh.enable = true;
networking.firewall.enable = true; # Implicitly true, but let's make sure.
@@ -51,6 +54,7 @@ import ./make-test-python.nix (
lighthouse =
{ ... }@args:
makeNebulaNode args "lighthouse" {
+ networking.firewall.allowedUDPPorts = [ 53 ];
networking.interfaces.eth1.ipv4.addresses = lib.mkForce [
{
address = "192.168.1.1";
@@ -77,6 +81,13 @@ import ./make-test-python.nix (
}
];
};
+ lighthouse = {
+ dns = {
+ enable = true;
+ host = "10.0.100.1"; # bind to lighthouse interface
+ port = 53; # answer on standard DNS port
+ };
+ };
};
};
@@ -338,6 +349,8 @@ import ./make-test-python.nix (
# allowAny can ping the lighthouse, but not allowFromLighthouse because of its inbound firewall
allowAny.succeed("ping -c3 10.0.100.1")
allowAny.fail("ping -c3 10.0.100.3")
+ # allowAny can also resolve DNS on lighthouse
+ allowAny.succeed("dig @10.0.100.1 allowToLighthouse | grep -E 'allowToLighthouse\.\s+[0-9]+\s+IN\s+A\s+10\.0\.100\.4'")
# allowFromLighthouse can ping the lighthouse and allowAny
allowFromLighthouse.succeed("ping -c3 10.0.100.1")
diff --git a/nixos/tests/netdata.nix b/nixos/tests/netdata.nix
index a88bf1a77fa1..77c34e70e7d0 100644
--- a/nixos/tests/netdata.nix
+++ b/nixos/tests/netdata.nix
@@ -22,6 +22,7 @@ import ./make-test-python.nix (
];
services.netdata = {
enable = true;
+ package = pkgs.netdataCloud;
python.recommendedPythonPackages = true;
configDir."apps_groups.conf" = pkgs.writeText "apps_groups.conf" ''
@@ -40,27 +41,17 @@ import ./make-test-python.nix (
netdata.wait_for_open_port(19999)
# check if the netdata main page loads.
- netdata.succeed("curl --fail http://localhost:19999/")
+ netdata.succeed("curl --fail http://127.0.0.1:19999")
netdata.succeed("sleep 4")
- # check if netdata can read disk ops for root owned processes.
- # if > 0, successful. verifies both netdata working and
- # apps.plugin has elevated capabilities.
- url = "http://localhost:19999/api/v1/data?chart=user.root_disk_physical_io"
- filter = '[.data[range(10)][2]] | add | . < 0'
+ # check if netdata api shows correct os
+ url = "http://127.0.0.1:19999/api/v3/info"
+ filter = '.agents[0].application.os.os | . == "NixOS"'
cmd = f"curl -s {url} | jq -e '{filter}'"
netdata.wait_until_succeeds(cmd)
# check if the control socket is available
netdata.succeed("sudo netdatacli ping")
-
- # check that custom groups in apps_groups.conf are used.
- # if > 0, successful. verifies that user-specified apps_group.conf
- # is used.
- url = "http://localhost:19999/api/v1/data?chart=app.netdata_test_cpu_utilization"
- filter = '[.data[range(10)][2]] | add | . > 0'
- cmd = f"curl -s {url} | jq -e '{filter}'"
- netdata.wait_until_succeeds(cmd, timeout=30)
'';
}
)
diff --git a/nixos/tests/networking/livekit.nix b/nixos/tests/networking/livekit.nix
new file mode 100644
index 000000000000..3f72ee5a050c
--- /dev/null
+++ b/nixos/tests/networking/livekit.nix
@@ -0,0 +1,25 @@
+{
+ pkgs,
+ lib,
+ ...
+}:
+{
+ name = "livekit";
+ meta.maintainers = [ lib.maintainers.quadradical ];
+
+ nodes.machine = {
+ services.livekit = {
+ enable = true;
+ keyFile = pkgs.writers.writeYAML "keys.yaml" {
+ key = "f6lQGaHtM5HfgZjIcec3cOCRfiDqIine4CpZZnqdT5cE";
+ };
+ settings.port = 8000;
+ };
+ };
+
+ testScript = ''
+ machine.wait_for_unit("livekit.service")
+ machine.wait_for_open_port(8000)
+ machine.succeed("curl 127.0.0.1:8000 -L --fail")
+ '';
+}
diff --git a/nixos/tests/networking/networkd-and-scripted.nix b/nixos/tests/networking/networkd-and-scripted.nix
index f2211738db2b..312981317735 100644
--- a/nixos/tests/networking/networkd-and-scripted.nix
+++ b/nixos/tests/networking/networkd-and-scripted.nix
@@ -178,6 +178,29 @@ let
router.wait_until_succeeds("ping -c 1 fd00:1234:5678:2::2")
'';
};
+ dhcpHostname = {
+ name = "hostnameDHCP";
+ nodes.router = router;
+ nodes.client = clientConfig {
+ # use the name given by the DHCP server
+ system.name = "client";
+ networking.hostName = lib.mkForce "";
+ security.polkit.enable = true;
+ virtualisation.interfaces.enp1s0.vlan = 1;
+ networking.interfaces.enp1s0.useDHCP = true;
+ };
+ testScript = ''
+ router.start()
+ router.systemctl("start network-online.target")
+ router.wait_for_unit("network-online.target")
+
+ client.start()
+ client.wait_for_unit("network.target")
+
+ with subtest("Wait until we have received the hostname"):
+ client.wait_until_succeeds("hostname | grep -q 'client1'")
+ '';
+ };
dhcpOneIf = {
name = "OneInterfaceDHCP";
nodes.router = router;
diff --git a/nixos/tests/nextcloud/default.nix b/nixos/tests/nextcloud/default.nix
index a5a5e49d3145..dc0bd933f21d 100644
--- a/nixos/tests/nextcloud/default.nix
+++ b/nixos/tests/nextcloud/default.nix
@@ -139,7 +139,6 @@ let
in
listToAttrs (
concatMap genTests [
- 29
30
31
]
diff --git a/nixos/tests/nextcloud/with-declarative-redis-and-secrets.nix b/nixos/tests/nextcloud/with-declarative-redis-and-secrets.nix
index 094732a10fa6..fb1a9c9baba0 100644
--- a/nixos/tests/nextcloud/with-declarative-redis-and-secrets.nix
+++ b/nixos/tests/nextcloud/with-declarative-redis-and-secrets.nix
@@ -84,13 +84,12 @@ runTest (
# This file is meant to contain secret options which should
# not go into the nix store. Here it is just used to set the
# redis password.
- environment.etc."nextcloud-secrets.json".text = ''
- {
- "redis": {
- "password": "secret"
- }
- }
- '';
+ environment.etc."nextcloud-secrets.json" = {
+ mode = "0600";
+ text = builtins.toJSON {
+ redis.password = "secret";
+ };
+ };
};
};
diff --git a/nixos/tests/nextcloud/with-objectstore.nix b/nixos/tests/nextcloud/with-objectstore.nix
index 802c070b879d..53800b8238bc 100644
--- a/nixos/tests/nextcloud/with-objectstore.nix
+++ b/nixos/tests/nextcloud/with-objectstore.nix
@@ -26,11 +26,13 @@ runTest (
nodes = {
nextcloud =
- { config, pkgs, ... }:
{
- networking.firewall.allowedTCPPorts = [ 9000 ];
- environment.systemPackages = [ pkgs.minio-client ];
-
+ config,
+ pkgs,
+ nodes,
+ ...
+ }:
+ {
services.nextcloud.config.dbtype = "sqlite";
services.nextcloud.config.objectstore.s3 = {
@@ -39,13 +41,66 @@ runTest (
autocreate = true;
key = accessKey;
secretFile = "${pkgs.writeText "secretKey" secretKey}";
- hostname = "nextcloud";
- useSsl = false;
- port = 9000;
+ hostname = "acme.test";
+ useSsl = true;
+ port = 443;
usePathStyle = true;
region = "us-east-1";
};
+ security.pki.certificates = [
+ (builtins.readFile ../common/acme/server/ca.cert.pem)
+ ];
+
+ environment.systemPackages = [ pkgs.minio-client ];
+
+ # The dummy certs are for acme.test, so we pretend that's the FQDN
+ # of the minio VM.
+ networking.extraHosts = ''
+ ${nodes.minio.networking.primaryIPAddress} acme.test
+ '';
+ };
+
+ client =
+ { nodes, ... }:
+ {
+ security.pki.certificates = [
+ (builtins.readFile ../common/acme/server/ca.cert.pem)
+ ];
+ networking.extraHosts = ''
+ ${nodes.minio.networking.primaryIPAddress} acme.test
+ '';
+ };
+
+ minio =
+ { ... }:
+ {
+ security.pki.certificates = [
+ (builtins.readFile ../common/acme/server/ca.cert.pem)
+ ];
+
+ services.nginx = {
+ enable = true;
+ recommendedProxySettings = true;
+
+ virtualHosts."acme.test" = {
+ onlySSL = true;
+ sslCertificate = ../common/acme/server/acme.test.cert.pem;
+ sslCertificateKey = ../common/acme/server/acme.test.key.pem;
+ locations."/".proxyPass = "http://127.0.0.1:9000";
+ };
+ };
+
+ networking.extraHosts = ''
+ 127.0.0.1 acme.test
+ '';
+
+ networking.firewall.allowedTCPPorts = [
+ 9000
+ 80
+ 443
+ ];
+
services.minio = {
enable = true;
listenAddress = "0.0.0.0:9000";
@@ -56,18 +111,22 @@ runTest (
};
test-helpers.init = ''
- nextcloud.wait_for_open_port(9000)
+ minio.start()
+ minio.wait_for_open_port(9000)
+ minio.wait_for_unit("nginx.service")
+ minio.wait_for_open_port(443)
'';
test-helpers.extraTests =
{ nodes, ... }:
''
+
with subtest("File is not on the filesystem"):
nextcloud.succeed("test ! -e ${nodes.nextcloud.services.nextcloud.home}/data/root/files/test-shared-file")
with subtest("Check if file is in S3"):
nextcloud.succeed(
- "mc config host add minio http://localhost:9000 ${accessKey} ${secretKey} --api s3v4"
+ "mc config host add minio https://acme.test ${accessKey} ${secretKey} --api s3v4"
)
files = nextcloud.succeed('mc ls minio/nextcloud|sort').strip().split('\n')
@@ -100,8 +159,8 @@ runTest (
with subtest("Test download from S3"):
client.succeed(
"env AWS_ACCESS_KEY_ID=${accessKey} AWS_SECRET_ACCESS_KEY=${secretKey} "
- + f"${lib.getExe pkgs.awscli2} s3 cp s3://nextcloud/{file} test --endpoint-url http://nextcloud:9000 "
- + "--region us-east-1"
+ + f"${lib.getExe pkgs.awscli2} s3 cp s3://nextcloud/{file} test --endpoint-url https://acme.test "
+ + "--region us-east-1 --ca-bundle /etc/ssl/certs/ca-bundle.crt"
)
client.succeed("test hi = $(cat test)")
diff --git a/nixos/tests/nextflow.nix b/nixos/tests/nextflow.nix
index b4aad98483b9..62ce7d3ea571 100644
--- a/nixos/tests/nextflow.nix
+++ b/nixos/tests/nextflow.nix
@@ -1,60 +1,58 @@
-import ./make-test-python.nix (
- { pkgs, ... }:
- let
- bash = pkgs.dockerTools.pullImage {
- imageName = "quay.io/nextflow/bash";
- imageDigest = "sha256:bea0e244b7c5367b2b0de687e7d28f692013aa18970941c7dd184450125163ac";
- sha256 = "161s9f24njjx87qrwq0c9nmnwvyc6iblcxka7hirw78lm7i9x4w5";
- finalImageName = "quay.io/nextflow/bash";
- };
+{ pkgs, ... }:
+let
+ bash = pkgs.dockerTools.pullImage {
+ imageName = "quay.io/nextflow/bash";
+ imageDigest = "sha256:bea0e244b7c5367b2b0de687e7d28f692013aa18970941c7dd184450125163ac";
+ sha256 = "161s9f24njjx87qrwq0c9nmnwvyc6iblcxka7hirw78lm7i9x4w5";
+ finalImageName = "quay.io/nextflow/bash";
+ };
- hello = pkgs.stdenv.mkDerivation {
- name = "nextflow-hello";
- src = pkgs.fetchFromGitHub {
- owner = "nextflow-io";
- repo = "hello";
- rev = "afff16a9b45c8e8a4f5a3743780ac13a541762f8";
- hash = "sha256-c8FirHc+J5Y439g0BdHxRtXVrOAzIrGEKA0m1mp9b/U=";
+ hello = pkgs.stdenv.mkDerivation {
+ name = "nextflow-hello";
+ src = pkgs.fetchFromGitHub {
+ owner = "nextflow-io";
+ repo = "hello";
+ rev = "afff16a9b45c8e8a4f5a3743780ac13a541762f8";
+ hash = "sha256-c8FirHc+J5Y439g0BdHxRtXVrOAzIrGEKA0m1mp9b/U=";
+ };
+ installPhase = ''
+ cp -r $src $out
+ '';
+ };
+ run-nextflow-pipeline = pkgs.writeShellApplication {
+ name = "run-nextflow-pipeline";
+ runtimeInputs = [ pkgs.nextflow ];
+ text = ''
+ export NXF_OFFLINE=true
+ for b in false true; do
+ echo "docker.enabled = $b" > nextflow.config
+ cat nextflow.config
+ nextflow run -ansi-log false ${hello}
+ done
+ '';
+ };
+in
+{
+ name = "nextflow";
+
+ nodes.machine =
+ { ... }:
+ {
+ environment.systemPackages = [
+ run-nextflow-pipeline
+ pkgs.nextflow
+ ];
+ virtualisation = {
+ docker.enable = true;
};
- installPhase = ''
- cp -r $src $out
- '';
};
- run-nextflow-pipeline = pkgs.writeShellApplication {
- name = "run-nextflow-pipeline";
- runtimeInputs = [ pkgs.nextflow ];
- text = ''
- export NXF_OFFLINE=true
- for b in false true; do
- echo "docker.enabled = $b" > nextflow.config
- cat nextflow.config
- nextflow run -ansi-log false ${hello}
- done
- '';
- };
- in
- {
- name = "nextflow";
- nodes.machine =
- { ... }:
- {
- environment.systemPackages = [
- run-nextflow-pipeline
- pkgs.nextflow
- ];
- virtualisation = {
- docker.enable = true;
- };
- };
-
- testScript =
- { nodes, ... }:
- ''
- start_all()
- machine.wait_for_unit("docker.service")
- machine.succeed("docker load < ${bash}")
- machine.succeed("run-nextflow-pipeline >&2")
- '';
- }
-)
+ testScript =
+ { nodes, ... }:
+ ''
+ start_all()
+ machine.wait_for_unit("docker.service")
+ machine.succeed("docker load < ${bash}")
+ machine.succeed("run-nextflow-pipeline >&2")
+ '';
+}
diff --git a/nixos/tests/nginx-unix-socket.nix b/nixos/tests/nginx-unix-socket.nix
index 19d9377868ef..d6949227d3be 100644
--- a/nixos/tests/nginx-unix-socket.nix
+++ b/nixos/tests/nginx-unix-socket.nix
@@ -1,5 +1,6 @@
{ ... }:
let
+ defaultNginxSocketPath = "/var/run/nginx/default-test.sock";
nginxSocketPath = "/var/run/nginx/test.sock";
in
{
@@ -11,6 +12,13 @@ in
{
services.nginx = {
enable = true;
+
+ defaultListen = [ { addr = "unix:${defaultNginxSocketPath}"; } ];
+ virtualHosts.defaultLocalhost = {
+ serverName = "defaultLocalhost";
+ locations."/default".return = "200 'bar'";
+ };
+
virtualHosts.localhost = {
serverName = "localhost";
listen = [ { addr = "unix:${nginxSocketPath}"; } ];
@@ -22,8 +30,10 @@ in
testScript = ''
webserver.wait_for_unit("nginx")
- webserver.wait_for_open_unix_socket("${nginxSocketPath}")
+ webserver.wait_for_open_unix_socket("${defaultNginxSocketPath}", timeout=1)
+ webserver.wait_for_open_unix_socket("${nginxSocketPath}", timeout=1)
+ webserver.succeed("curl --fail --silent --unix-socket '${defaultNginxSocketPath}' http://defaultLocalhost/default | grep '^bar$'")
webserver.succeed("curl --fail --silent --unix-socket '${nginxSocketPath}' http://localhost/test | grep '^foo$'")
'';
}
diff --git a/nixos/tests/nix-ld.nix b/nixos/tests/nix-ld.nix
index 03f5c5e89fbf..0b89b99a33b5 100644
--- a/nixos/tests/nix-ld.nix
+++ b/nixos/tests/nix-ld.nix
@@ -1,14 +1,12 @@
+{ ... }:
{
- system ? builtins.currentSystem,
- config ? { },
- pkgs ? import ../.. { inherit system config; },
-}:
-let
- inherit (import ../lib/testing-python.nix { inherit system pkgs; }) makeTest;
- shared =
+ name = "nix-ld";
+
+ nodes.machine =
{ config, pkgs, ... }:
{
programs.nix-ld.enable = true;
+
environment.systemPackages = [
(pkgs.runCommand "patched-hello" { } ''
install -D -m755 ${pkgs.hello}/bin/hello $out/bin/hello
@@ -16,25 +14,9 @@ let
'')
];
};
-in
-{
- nix-ld = makeTest {
- name = "nix-ld";
- nodes.machine = shared;
- testScript = ''
- start_all()
- machine.succeed("hello")
- '';
- };
- nix-ld-rs = makeTest {
- name = "nix-ld-rs";
- nodes.machine = {
- imports = [ shared ];
- programs.nix-ld.package = pkgs.nix-ld-rs;
- };
- testScript = ''
- start_all()
- machine.succeed("hello")
- '';
- };
+
+ testScript = ''
+ start_all()
+ machine.succeed("hello")
+ '';
}
diff --git a/nixos/tests/nix/misc.nix b/nixos/tests/nix/misc.nix
index be23d4e450e3..c594f4055cfc 100644
--- a/nixos/tests/nix/misc.nix
+++ b/nixos/tests/nix/misc.nix
@@ -3,20 +3,16 @@
let
inherit (pkgs) lib;
- tests = {
- default = testsForPackage { nixPackage = pkgs.nix; };
- lix = testsForPackage { nixPackage = pkgs.lix; };
- };
+ tests.default = testsForPackage { nixPackage = pkgs.nix; };
- testsForPackage =
- args:
- lib.recurseIntoAttrs {
- # If the attribute is not named 'test'
- # You will break all the universe on the release-*.nix side of things.
- # `discoverTests` relies on `test` existence to perform a `callTest`.
- test = testMiscFeatures args;
- passthru.override = args': testsForPackage (args // args');
+ testsForPackage = args: {
+ # If the attribute is not named 'test'
+ # You will break all the universe on the release-*.nix side of things.
+ # `discoverTests` relies on `test` existence to perform a `callTest`.
+ test = testMiscFeatures args // {
+ passthru.override = args': (testsForPackage (args // args')).test;
};
+ };
testMiscFeatures =
{ nixPackage, ... }:
diff --git a/nixos/tests/noto-fonts.nix b/nixos/tests/noto-fonts.nix
index 2d204131dcf9..719ea9f33eac 100644
--- a/nixos/tests/noto-fonts.nix
+++ b/nixos/tests/noto-fonts.nix
@@ -1,13 +1,14 @@
-import ./make-test-python.nix (
- { pkgs, lib, ... }:
- {
- name = "noto-fonts";
- meta.maintainers = with lib.maintainers; [
- nickcao
- midchildan
- ];
+{ lib, ... }:
+{
+ name = "noto-fonts";
+ meta.maintainers = with lib.maintainers; [
+ nickcao
+ midchildan
+ ];
- nodes.machine = {
+ nodes.machine =
+ { pkgs, ... }:
+ {
imports = [ ./common/x11.nix ];
environment.systemPackages = [ pkgs.gedit ];
fonts = {
@@ -36,24 +37,23 @@ import ./make-test-python.nix (
};
};
- testScript =
- # extracted from http://www.clagnut.com/blog/2380/
- let
- testText = builtins.toFile "test.txt" ''
- the quick brown fox jumps over the lazy dog
- 視野無限廣,窗外有藍天
- Eĥoŝanĝo ĉiuĵaŭde.
- いろはにほへと ちりぬるを わかよたれそ つねならむ うゐのおくやま けふこえて あさきゆめみし ゑひもせす
- 다람쥐 헌 쳇바퀴에 타고파
- 中国智造,慧及全球
- '';
- in
- ''
- machine.wait_for_x()
- machine.succeed("gedit ${testText} >&2 &")
- machine.wait_for_window(".* - gedit")
- machine.sleep(10)
- machine.screenshot("screen")
+ testScript =
+ # extracted from http://www.clagnut.com/blog/2380/
+ let
+ testText = builtins.toFile "test.txt" ''
+ the quick brown fox jumps over the lazy dog
+ 視野無限廣,窗外有藍天
+ Eĥoŝanĝo ĉiuĵaŭde.
+ いろはにほへと ちりぬるを わかよたれそ つねならむ うゐのおくやま けふこえて あさきゆめみし ゑひもせす
+ 다람쥐 헌 쳇바퀴에 타고파
+ 中国智造,慧及全球
'';
- }
-)
+ in
+ ''
+ machine.wait_for_x()
+ machine.succeed("gedit ${testText} >&2 &")
+ machine.wait_for_window(".* - gedit")
+ machine.sleep(10)
+ machine.screenshot("screen")
+ '';
+}
diff --git a/nixos/tests/ntpd-rs.nix b/nixos/tests/ntpd-rs.nix
index 2907c7558324..9459a9f4ac51 100644
--- a/nixos/tests/ntpd-rs.nix
+++ b/nixos/tests/ntpd-rs.nix
@@ -11,7 +11,7 @@ import ./make-test-python.nix (
client = {
services.ntpd-rs = {
enable = true;
- metrics.enable = true;
+ metrics.enable = false;
useNetworkingTimeServers = false;
settings = {
source = [
@@ -27,11 +27,22 @@ import ./make-test-python.nix (
};
};
server = {
- networking.firewall.allowedUDPPorts = [ 123 ];
+ networking.firewall = {
+ allowedTCPPorts = [
+ 9975
+ ];
+ allowedUDPPorts = [
+ 123
+ ];
+ };
+
services.ntpd-rs = {
enable = true;
metrics.enable = true;
settings = {
+ observability = {
+ metrics-exporter-listen = "[::]:9975";
+ };
server = [
{ listen = "[::]:123"; }
];
@@ -48,8 +59,19 @@ import ./make-test-python.nix (
for machine in (server, client):
machine.wait_for_unit('multi-user.target')
machine.succeed('systemctl is-active ntpd-rs.service')
- machine.succeed('systemctl is-active ntpd-rs-metrics.service')
- machine.succeed('curl http://localhost:9975/metrics | grep ntp_uptime_seconds')
+
+ client.fail('systemctl is-active ntpd-rs-metrics.service')
+ server.succeed('systemctl is-active ntpd-rs-metrics.service')
+
+ server.wait_for_open_port(9975)
+ client.succeed('curl http://server:9975/metrics | grep ntp_uptime_seconds')
+ server.fail('curl --fail --connect-timeout 2 http://client:9975/metrics | grep ntp_uptime_seconds')
+
+ client.succeed("ntp-ctl status | grep server:123")
+ server.succeed("ntp-ctl status | grep '\[::\]:123'")
+
+ client.succeed("grep '^mode = \"server\"' $(systemctl status ntpd-rs | grep -oE '/nix/store[^ ]*ntpd-rs.toml')")
+ server.succeed("grep '^mode = \"pool\"' $(systemctl status ntpd-rs | grep -oE '/nix/store[^ ]*ntpd-rs.toml')")
'';
}
)
diff --git a/nixos/tests/nvidia-container-toolkit.nix b/nixos/tests/nvidia-container-toolkit.nix
index b22b989c0814..1b894a1cddd9 100644
--- a/nixos/tests/nvidia-container-toolkit.nix
+++ b/nixos/tests/nvidia-container-toolkit.nix
@@ -90,7 +90,10 @@ in
{
name = "nvidia-container-toolkit";
meta = with lib.maintainers; {
- maintainers = [ ereslibre ];
+ maintainers = [
+ ereslibre
+ christoph-heiss
+ ];
};
defaults =
{ config, ... }:
diff --git a/nixos/tests/obs-studio.nix b/nixos/tests/obs-studio.nix
index a1b5bacf0428..7290119a12d7 100644
--- a/nixos/tests/obs-studio.nix
+++ b/nixos/tests/obs-studio.nix
@@ -1,40 +1,38 @@
-import ./make-test-python.nix (
- { ... }:
+{ ... }:
- {
- name = "obs-studio";
+{
+ name = "obs-studio";
- nodes.machine =
- { pkgs, ... }:
- {
- imports = [
- ./common/x11.nix
- ./common/user-account.nix
+ nodes.machine =
+ { pkgs, ... }:
+ {
+ imports = [
+ ./common/x11.nix
+ ./common/user-account.nix
+ ];
+
+ programs.obs-studio = {
+ enable = true;
+ plugins = with pkgs.obs-studio-plugins; [
+ wlrobs
+ obs-vkcapture
];
-
- programs.obs-studio = {
- enable = true;
- plugins = with pkgs.obs-studio-plugins; [
- wlrobs
- obs-vkcapture
- ];
- enableVirtualCamera = true;
- };
+ enableVirtualCamera = true;
};
+ };
- testScript = ''
- machine.wait_for_x()
- machine.succeed("obs --version")
+ testScript = ''
+ machine.wait_for_x()
+ machine.succeed("obs --version")
- # virtual camera tests
- machine.succeed("lsmod | grep v4l2loopback")
- machine.succeed("ls /dev/video1")
- machine.succeed("obs --startvirtualcam >&2 &")
- machine.wait_for_window("OBS")
- machine.sleep(5)
+ # virtual camera tests
+ machine.succeed("lsmod | grep v4l2loopback")
+ machine.succeed("ls /dev/video1")
+ machine.succeed("obs --startvirtualcam >&2 &")
+ machine.wait_for_window("OBS")
+ machine.sleep(5)
- # test plugins
- machine.succeed("which obs-vkcapture")
- '';
- }
-)
+ # test plugins
+ machine.succeed("which obs-vkcapture")
+ '';
+}
diff --git a/nixos/tests/oci-containers.nix b/nixos/tests/oci-containers.nix
index 073f62cf5155..a22bd9c6b431 100644
--- a/nixos/tests/oci-containers.nix
+++ b/nixos/tests/oci-containers.nix
@@ -80,6 +80,7 @@ let
home = "/var/lib/redis";
linger = type == "healthy";
createHome = true;
+ uid = 2342;
subUidRanges = [
{
count = 65536;
diff --git a/nixos/tests/olivetin.nix b/nixos/tests/olivetin.nix
new file mode 100644
index 000000000000..f9d9e98610ef
--- /dev/null
+++ b/nixos/tests/olivetin.nix
@@ -0,0 +1,57 @@
+{ lib, ... }:
+
+{
+ name = "olivetin";
+ meta.maintainers = with lib.maintainers; [ defelo ];
+
+ nodes.machine = {
+ services.olivetin = {
+ enable = true;
+ settings = {
+ actions = [
+ {
+ id = "hello_world";
+ title = "Say Hello";
+ shell = "echo -n 'Hello World!' | tee /tmp/result";
+ }
+ ];
+ };
+ extraConfigFiles = [
+ (builtins.toFile "secrets.yaml" ''
+ actions:
+ - id: secret
+ title: Secret Action
+ shell: echo -n secret > /tmp/result2
+ '')
+ ];
+ };
+ };
+
+ interactive.nodes.machine = {
+ services.olivetin.settings.ListenAddressSingleHTTPFrontend = "0.0.0.0:8000";
+ networking.firewall.allowedTCPPorts = [ 8000 ];
+ virtualisation.forwardPorts = [
+ {
+ from = "host";
+ host.port = 8000;
+ guest.port = 8000;
+ }
+ ];
+ };
+
+ testScript = ''
+ import json
+
+ machine.wait_for_unit("olivetin.service")
+ machine.wait_for_open_port(8000)
+
+ response = json.loads(machine.succeed("curl http://localhost:8000/api/StartActionByGetAndWait/hello_world"))
+ assert response["logEntry"]["exitCode"] == 0
+ assert response["logEntry"]["output"] == "Hello World!"
+ assert machine.succeed("cat /tmp/result") == "Hello World!"
+
+ response = json.loads(machine.succeed("curl http://localhost:8000/api/StartActionByGetAndWait/secret"))
+ assert response["logEntry"]["exitCode"] == 0
+ assert machine.succeed("cat /tmp/result2") == "secret"
+ '';
+}
diff --git a/nixos/tests/openbao.nix b/nixos/tests/openbao.nix
new file mode 100644
index 000000000000..94e1f0af7ec4
--- /dev/null
+++ b/nixos/tests/openbao.nix
@@ -0,0 +1,105 @@
+{ lib, ... }:
+let
+ certs = import ./common/acme/server/snakeoil-certs.nix;
+ domain = certs.domain;
+in
+{
+ name = "openbao";
+
+ meta.maintainers = with lib.maintainers; [ kranzes ];
+
+ nodes.machine =
+ { config, ... }:
+ {
+ security.pki.certificateFiles = [ certs.ca.cert ];
+
+ networking.extraHosts = ''
+ 127.0.0.1 ${domain}
+ '';
+
+ services.openbao = {
+ enable = true;
+
+ settings = {
+ ui = true;
+
+ listener = {
+ default = {
+ type = "tcp";
+ tls_cert_file = certs.${domain}.cert;
+ tls_key_file = certs.${domain}.key;
+ };
+
+ unix = {
+ type = "unix";
+ };
+ };
+
+ cluster_addr = "https://127.0.0.1:8201";
+ api_addr = "https://${domain}:8200";
+
+ storage.raft.path = "/var/lib/openbao";
+ };
+ };
+
+ environment.variables = {
+ BAO_ADDR = config.services.openbao.settings.api_addr;
+ BAO_FORMAT = "json";
+ };
+ };
+
+ testScript =
+ { nodes, ... }:
+ ''
+ import json
+
+ start_all()
+
+ with subtest("Wait for OpenBao to start up"):
+ machine.wait_for_unit("openbao.service")
+ machine.wait_for_open_port(8200)
+ machine.wait_for_open_unix_socket("${nodes.machine.services.openbao.settings.listener.unix.address}")
+
+ with subtest("Check that the web UI is being served"):
+ machine.succeed("curl -L --fail --show-error --silent $BAO_ADDR | grep 'OpenBao '")
+
+ with subtest("Check that OpenBao is not initialized"):
+ status_output = json.loads(machine.fail("bao status"))
+ assert not status_output["initialized"]
+
+ with subtest("Initialize OpenBao"):
+ init_output = json.loads(machine.succeed("bao operator init"))
+
+ with subtest("Check that OpenBao is initialized and sealed"):
+ status_output = json.loads(machine.fail("bao status"))
+ assert status_output["initialized"]
+ assert status_output["sealed"]
+
+ with subtest("Unseal OpenBao"):
+ for key in init_output["unseal_keys_b64"][:init_output["unseal_threshold"]]:
+ machine.succeed(f"bao operator unseal {key}")
+
+ with subtest("Check that OpenBao is not sealed"):
+ status_output = json.loads(machine.succeed("bao status"))
+ assert not status_output["sealed"]
+
+ with subtest("Login with root token"):
+ machine.succeed(f"bao login {init_output["root_token"]}")
+
+ with subtest("Enable userpass auth method"):
+ machine.succeed("bao auth enable userpass")
+
+ with subtest("Create a user in userpass"):
+ machine.succeed("bao write auth/userpass/users/testuser password=testpassword")
+
+ with subtest("Login to a user from userpass"):
+ machine.succeed("bao login -method userpass username=testuser password=testpassword")
+
+ with subtest("Write a secret to cubbyhole"):
+ machine.succeed("bao write cubbyhole/my-secret my-value=s3cr3t")
+
+ with subtest("Read a secret from cubbyhole"):
+ read_output = json.loads(machine.succeed("bao read cubbyhole/my-secret"))
+ assert read_output["data"]["my-value"] == "s3cr3t"
+ '';
+}
diff --git a/nixos/tests/opencloud.nix b/nixos/tests/opencloud.nix
new file mode 100644
index 000000000000..1fdac34b6ccd
--- /dev/null
+++ b/nixos/tests/opencloud.nix
@@ -0,0 +1,110 @@
+{ lib, pkgs, ... }:
+
+let
+ certs = import ./common/acme/server/snakeoil-certs.nix;
+ inherit (certs) domain;
+
+ # this is a demo user created by IDM_CREATE_DEMO_USERS=true
+ demoUser = "alan";
+ demoPassword = "demo";
+
+ adminUser = "admin";
+ adminPassword = "hunter2";
+ testRunner =
+ pkgs.writers.writePython3Bin "test-runner"
+ {
+ libraries = [ pkgs.python3Packages.selenium ];
+ flakeIgnore = [ "E501" ];
+ }
+ ''
+ import sys
+ from selenium.webdriver.common.by import By
+ from selenium.webdriver import Firefox
+ from selenium.webdriver.firefox.options import Options
+ from selenium.webdriver.support.ui import WebDriverWait
+ from selenium.webdriver.support import expected_conditions as EC
+
+ options = Options()
+ options.add_argument('--headless')
+ driver = Firefox(options=options)
+
+ host = sys.argv[1]
+ user = sys.argv[2]
+ password = sys.argv[3]
+
+ driver.get(f"https://{host}/")
+ wait = WebDriverWait(driver, 60)
+ wait.until(EC.title_contains("Sign in"))
+ wait.until(EC.url_contains(f"https://{host}/signin/v1/identifier"))
+ wait.until(EC.visibility_of_element_located((By.ID, 'oc-login-username')))
+ driver.find_element(By.ID, 'oc-login-username').send_keys(user)
+ driver.find_element(By.ID, 'oc-login-password').send_keys(password)
+ wait.until(EC.visibility_of_element_located((By.XPATH, '//button[@type="submit"]')))
+ driver.find_element(By.XPATH, '//button[@type="submit"]').click()
+ wait.until(EC.visibility_of_element_located((By.ID, 'new-file-menu-btn')))
+ wait.until(EC.title_contains("Personal"))
+ '';
+in
+
+{
+ name = "opencloud";
+
+ meta.maintainers = with lib.maintainers; [
+ christoph-heiss
+ k900
+ ];
+
+ nodes.machine = {
+ virtualisation.memorySize = 2048;
+ environment.systemPackages = [
+ pkgs.firefox-unwrapped
+ pkgs.geckodriver
+ testRunner
+ ];
+
+ networking.hosts."127.0.0.1" = [ domain ];
+ security.pki.certificateFiles = [ certs.ca.cert ];
+
+ services.opencloud = {
+ enable = true;
+ url = "https://${domain}:9200";
+ environment = {
+ ADMIN_PASSWORD = adminPassword;
+ IDM_CREATE_DEMO_USERS = "true";
+ IDM_LDAPS_CERT = "${certs.${domain}.cert}";
+ IDM_LDAPS_KEY = "${certs.${domain}.key}";
+ OC_INSECURE = "false";
+ OC_LDAP_URI = "ldaps://${domain}:9235";
+ OC_LDAP_CACERT = "${certs.${domain}.cert}";
+ OC_HTTP_TLS_ENABLED = "true";
+ OC_HTTP_TLS_CERTIFICATE = "${certs.${domain}.cert}";
+ OC_HTTP_TLS_KEY = "${certs.${domain}.key}";
+ PROXY_TLS = "true";
+ PROXY_TRANSPORT_TLS_CERT = "${certs.${domain}.cert}";
+ PROXY_TRANSPORT_TLS_KEY = "${certs.${domain}.key}";
+ PROXY_INSECURE_BACKENDS = "true";
+ };
+ };
+ };
+
+ testScript = ''
+ start_all()
+ machine.wait_for_unit("opencloud.service")
+ machine.wait_for_open_port(9200)
+
+ # wait for OpenCloud to fully come up
+ machine.sleep(10)
+
+ with subtest("opencloud bin works"):
+ machine.succeed("${lib.getExe pkgs.opencloud} version")
+
+ with subtest("web interface presents start page"):
+ machine.succeed("curl -sSf https://${domain}:9200 | grep 'OpenCloud '")
+
+ with subtest("use the web interface to log in with the provisioned admin user"):
+ machine.succeed("PYTHONUNBUFFERED=1 systemd-cat -t test-runner test-runner ${domain}:9200 ${adminUser} ${adminPassword}")
+
+ with subtest("use the web interface to log in with a demo user"):
+ machine.succeed("PYTHONUNBUFFERED=1 systemd-cat -t test-runner test-runner ${domain}:9200 ${demoUser} ${demoPassword}")
+ '';
+}
diff --git a/nixos/tests/openssh.nix b/nixos/tests/openssh.nix
index e0b95f75dfda..d7ddf478d91a 100644
--- a/nixos/tests/openssh.nix
+++ b/nixos/tests/openssh.nix
@@ -251,7 +251,6 @@ import ./make-test-python.nix (
server_lazy_socket.wait_for_unit("sshd.socket", timeout=30)
with subtest("manual-authkey"):
- client.succeed("mkdir -m 700 /root/.ssh")
client.succeed(
'${pkgs.openssh}/bin/ssh-keygen -t ed25519 -f /root/.ssh/id_ed25519 -N ""'
)
@@ -261,9 +260,7 @@ import ./make-test-python.nix (
public_key = public_key.strip()
client.succeed("chmod 600 /root/.ssh/id_ed25519")
- server.succeed("mkdir -m 700 /root/.ssh")
server.succeed("echo '{}' > /root/.ssh/authorized_keys".format(public_key))
- server_lazy.succeed("mkdir -m 700 /root/.ssh")
server_lazy.succeed("echo '{}' > /root/.ssh/authorized_keys".format(public_key))
client.wait_for_unit("network.target")
@@ -354,6 +351,9 @@ import ./make-test-python.nix (
"ssh -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no -i privkey.snakeoil server-no-pam true",
timeout=30
)
+
+ # None of the per-connection units should have failed.
+ server_lazy.fail("systemctl is-failed 'sshd@*.service'")
'';
}
)
diff --git a/nixos/tests/openstack-image.nix b/nixos/tests/openstack-image.nix
index 8fcfde974668..83c34fefef1f 100644
--- a/nixos/tests/openstack-image.nix
+++ b/nixos/tests/openstack-image.nix
@@ -12,7 +12,7 @@ with import common/ec2.nix { inherit makeTest pkgs; };
let
image =
(import ../lib/eval-config.nix {
- inherit system;
+ system = null;
modules = [
../maintainers/scripts/openstack/openstack-image.nix
../modules/testing/test-instrumentation.nix
@@ -22,6 +22,8 @@ let
system.extraDependencies = with pkgs; [
stdenv
];
+
+ nixpkgs.pkgs = pkgs;
}
];
}).config.system.build.openstackImage
diff --git a/nixos/tests/paretosecurity.nix b/nixos/tests/paretosecurity.nix
index a2b9317c678f..5f9562013011 100644
--- a/nixos/tests/paretosecurity.nix
+++ b/nixos/tests/paretosecurity.nix
@@ -4,58 +4,49 @@
meta.maintainers = [ lib.maintainers.zupo ];
nodes.terminal =
- {
- config,
- pkgs,
- lib,
- ...
- }:
- let
- # Create a patched version of the package that points to the local dashboard
- # for easier testing
- patchedPareto = pkgs.paretosecurity.overrideAttrs (oldAttrs: {
- postPatch = ''
- substituteInPlace team/report.go \
- --replace-warn 'const reportURL = "https://dash.paretosecurity.com"' \
- 'const reportURL = "http://dashboard"'
- '';
- });
- in
+ { pkgs, ... }:
{
imports = [ ./common/user-account.nix ];
+ networking.firewall.enable = true;
services.paretosecurity = {
enable = true;
- package = patchedPareto;
+
+ # Create a patched version of the package that points to the local dashboard
+ # for easier testing
+ package = pkgs.paretosecurity.overrideAttrs (oldAttrs: {
+ postPatch =
+ oldAttrs.postPatch or ""
+ + ''
+ substituteInPlace team/report.go \
+ --replace-warn 'const reportURL = "https://dash.paretosecurity.com"' \
+ 'const reportURL = "http://dashboard"'
+ '';
+ });
};
};
- nodes.dashboard =
- { config, pkgs, ... }:
- {
- networking.firewall.allowedTCPPorts = [ 80 ];
+ nodes.dashboard = {
+ networking.firewall.allowedTCPPorts = [ 80 ];
- services.nginx = {
- enable = true;
- virtualHosts."dashboard" = {
- locations."/api/v1/team/".extraConfig = ''
- add_header Content-Type application/json;
- return 200 '{"message": "Linked device."}';
- '';
- };
+ services.nginx = {
+ enable = true;
+ virtualHosts."dashboard" = {
+ locations."/api/v1/team/".extraConfig = ''
+ add_header Content-Type application/json;
+ return 200 '{"message": "Linked device."}';
+ '';
};
};
+ };
nodes.xfce =
- { config, pkgs, ... }:
+ { pkgs, ... }:
{
imports = [ ./common/user-account.nix ];
- services.paretosecurity = {
- enable = true;
- trayIcon = true;
- };
+ services.paretosecurity.enable = true;
services.xserver.enable = true;
services.xserver.displayManager.lightdm.enable = true;
@@ -64,11 +55,16 @@
services.displayManager.autoLogin = {
enable = true;
user = "alice";
+
+ };
+
+ virtualisation.resolution = {
+ x = 640;
+ y = 480;
};
environment.systemPackages = [ pkgs.xdotool ];
environment.variables.XAUTHORITY = "/home/alice/.Xauthority";
-
};
enableOCR = true;
@@ -94,7 +90,6 @@
+ " --skip 21830a4e-84f1-48fe-9c5b-beab436b2cdb" # Disk encryption
+ " --skip 44e4754a-0b42-4964-9cc2-b88b2023cb1e" # Pareto Security is up to date
+ " --skip f962c423-fdf5-428a-a57a-827abc9b253e" # Password manager installed
- + " --skip 2e46c89a-5461-4865-a92e-3b799c12034a" # Firewall is enabled
+ "'"
)
@@ -117,9 +112,22 @@
]:
status, out = xfce.systemctl("is-enabled " + unit, "alice")
assert status == 0, f"Unit {unit} is not enabled (status: {status}): {out}"
- xfce.succeed("xdotool mousemove 850 10")
+ xfce.succeed("xdotool mousemove 460 10")
xfce.wait_for_text("Pareto Security")
xfce.succeed("xdotool click 1")
xfce.wait_for_text("Run Checks")
+
+ # Test 5: Desktop entry
+ xfce.succeed("xdotool mousemove 10 10")
+ xfce.succeed("xdotool click 1") # hide the tray icon window
+ xfce.succeed("xdotool click 1") # show the Applications menu
+ xfce.succeed("xdotool mousemove 10 200")
+ xfce.succeed("xdotool click 1")
+ xfce.wait_for_text("Pareto Security")
+
+ # Test 6: paretosecurity:// URL handler is registered
+ xfce.execute("su - alice -c 'xdg-open paretosecurity://foo >/dev/null &'")
+ xfce.wait_for_text("Failed to add device")
+
'';
}
diff --git a/nixos/tests/pdns-recursor.nix b/nixos/tests/pdns-recursor.nix
index 42a227ae8eb5..0c326d7db965 100644
--- a/nixos/tests/pdns-recursor.nix
+++ b/nixos/tests/pdns-recursor.nix
@@ -1,20 +1,25 @@
-import ./make-test-python.nix (
- { pkgs, ... }:
- {
- name = "powerdns-recursor";
+{ lib, pkgs, ... }:
- nodes.server =
- { ... }:
- {
- services.pdns-recursor.enable = true;
- services.pdns-recursor.exportHosts = true;
- networking.hosts."192.0.2.1" = [ "example.com" ];
- };
+{
+ name = "powerdns-recursor";
+ meta.maintainers = with lib.maintainers; [ rnhmjoj ];
- testScript = ''
+ nodes.server = {
+ services.pdns-recursor.enable = true;
+ services.pdns-recursor.exportHosts = true;
+ services.pdns-recursor.old-settings.dnssec-log-bogus = true;
+ networking.hosts."192.0.2.1" = [ "example.com" ];
+ };
+
+ testScript = ''
+ with subtest("pdns-recursor is running"):
server.wait_for_unit("pdns-recursor")
server.wait_for_open_port(53)
+
+ with subtest("can resolve names"):
assert "192.0.2.1" in server.succeed("host example.com localhost")
- '';
- }
-)
+
+ with subtest("old-settings have been merged in"):
+ server.succeed("${lib.getExe pkgs.yq-go} -e .dnssec.log_bogus /etc/pdns-recursor/recursor.yml")
+ '';
+}
diff --git a/nixos/tests/pgbackrest/default.nix b/nixos/tests/pgbackrest/default.nix
new file mode 100644
index 000000000000..5f837e5c351c
--- /dev/null
+++ b/nixos/tests/pgbackrest/default.nix
@@ -0,0 +1,5 @@
+{ runTest }:
+{
+ posix = runTest ./posix.nix;
+ sftp = runTest ./sftp.nix;
+}
diff --git a/nixos/tests/pgbackrest/posix.nix b/nixos/tests/pgbackrest/posix.nix
new file mode 100644
index 000000000000..d5bfaa6d310c
--- /dev/null
+++ b/nixos/tests/pgbackrest/posix.nix
@@ -0,0 +1,147 @@
+{ lib, pkgs, ... }:
+let
+ inherit (import ../ssh-keys.nix pkgs) snakeOilPrivateKey snakeOilPublicKey;
+ backupPath = "/var/lib/pgbackrest";
+in
+{
+ name = "pgbackrest-posix";
+
+ meta = {
+ maintainers = with lib.maintainers; [ wolfgangwalther ];
+ };
+
+ nodes.primary =
+ {
+ pkgs,
+ ...
+ }:
+ {
+ services.openssh.enable = true;
+ users.users.postgres.openssh.authorizedKeys.keys = [
+ snakeOilPublicKey
+ ];
+
+ services.postgresql = {
+ enable = true;
+ initialScript = pkgs.writeText "init.sql" ''
+ CREATE TABLE t(c text);
+ INSERT INTO t VALUES ('hello world');
+ '';
+ };
+
+ services.pgbackrest = {
+ enable = true;
+ repos.backup = {
+ type = "posix";
+ path = backupPath;
+ host-user = "pgbackrest";
+ };
+ };
+ };
+
+ nodes.backup =
+ {
+ nodes,
+ ...
+ }:
+ {
+ services.openssh.enable = true;
+ users.users.pgbackrest.openssh.authorizedKeys.keys = [
+ snakeOilPublicKey
+ ];
+
+ services.pgbackrest = {
+ enable = true;
+ repos.localhost.path = backupPath;
+
+ stanzas.default = {
+ jobs.future = {
+ schedule = "3000-01-01";
+ type = "full";
+ };
+ instances.primary = {
+ path = nodes.primary.services.postgresql.dataDir;
+ user = "postgres";
+ };
+ };
+
+ # Examples from https://pgbackrest.org/configuration.html#introduction
+ # Not used for the test, except for dumping the config.
+ stanzas.config-format.settings = {
+ start-fast = true;
+ compress-level = 3;
+ buffer-size = "2MiB";
+ db-timeout = 600;
+ db-exclude = [
+ "db1"
+ "db2"
+ "db5"
+ ];
+ tablespace-map = {
+ ts_01 = "/db/ts_01";
+ ts_02 = "/db/ts_02";
+ };
+ };
+ };
+ };
+
+ testScript =
+ { nodes, ... }:
+ ''
+ start_all()
+
+ primary.wait_for_unit("multi-user.target")
+ backup.wait_for_unit("multi-user.target")
+
+ with subtest("config file is written correctly"):
+ from textwrap import dedent
+ have = backup.succeed("cat /etc/pgbackrest/pgbackrest.conf")
+ want = dedent("""\
+ [config-format]
+ buffer-size=2MiB
+ compress-level=3
+ db-exclude=db1
+ db-exclude=db2
+ db-exclude=db5
+ db-timeout=600
+ start-fast=y
+ tablespace-map=ts_01=/db/ts_01
+ tablespace-map=ts_02=/db/ts_02
+ """)
+ assert want in have, repr((want, have))
+
+ primary.log(primary.succeed("""
+ HOME="${nodes.primary.services.postgresql.dataDir}"
+ mkdir -m 700 -p ~/.ssh
+ cat ${snakeOilPrivateKey} > ~/.ssh/id_ecdsa
+ chmod 400 ~/.ssh/id_ecdsa
+ ssh-keyscan backup >> ~/.ssh/known_hosts
+ chown -R postgres:postgres ~/.ssh
+ """))
+
+ backup.log(backup.succeed("""
+ HOME="${backupPath}"
+ mkdir -m 700 -p ~/.ssh
+ cat ${snakeOilPrivateKey} > ~/.ssh/id_ecdsa
+ chmod 400 ~/.ssh/id_ecdsa
+ ssh-keyscan primary >> ~/.ssh/known_hosts
+ chown -R pgbackrest:pgbackrest ~
+ """))
+
+ with subtest("backup/restore works with remote instance/local repo (SSH)"):
+ backup.succeed("sudo -u pgbackrest pgbackrest --stanza=default stanza-create")
+ backup.succeed("sudo -u pgbackrest pgbackrest --stanza=default check")
+
+ backup.systemctl("start pgbackrest-default-future")
+
+ # corrupt cluster
+ primary.systemctl("stop postgresql")
+ primary.execute("rm ${nodes.primary.services.postgresql.dataDir}/global/pg_control")
+
+ primary.succeed("sudo -u postgres pgbackrest --stanza=default restore --delta")
+
+ primary.systemctl("start postgresql")
+ primary.wait_for_unit("postgresql.service")
+ assert "hello world" in primary.succeed("sudo -u postgres psql -c 'TABLE t;'")
+ '';
+}
diff --git a/nixos/tests/pgbackrest/sftp.nix b/nixos/tests/pgbackrest/sftp.nix
new file mode 100644
index 000000000000..8e97fb679980
--- /dev/null
+++ b/nixos/tests/pgbackrest/sftp.nix
@@ -0,0 +1,95 @@
+{ lib, pkgs, ... }:
+let
+ inherit (import ../ssh-keys.nix pkgs) snakeOilPrivateKey snakeOilPublicKey;
+ backupPath = "/home/backup";
+in
+{
+ name = "pgbackrest-sftp";
+
+ meta = {
+ maintainers = with lib.maintainers; [ wolfgangwalther ];
+ };
+
+ nodes.primary =
+ {
+ pkgs,
+ ...
+ }:
+ {
+ services.postgresql = {
+ enable = true;
+ initialScript = pkgs.writeText "init.sql" ''
+ CREATE TABLE t(c text);
+ INSERT INTO t VALUES ('hello world');
+ '';
+ };
+
+ services.pgbackrest = {
+ enable = true;
+ repos.backup = {
+ type = "sftp";
+ path = "/home/backup";
+ sftp-host-key-check-type = "none";
+ sftp-host-key-hash-type = "sha256";
+ sftp-host-user = "backup";
+ sftp-private-key-file = "/var/lib/pgbackrest/sftp_key";
+ };
+
+ stanzas.default.jobs.future = {
+ schedule = "3000-01-01";
+ type = "diff";
+ };
+ };
+ };
+
+ nodes.backup =
+ {
+ nodes,
+ ...
+ }:
+ {
+ services.openssh.enable = true;
+ users.users.backup = {
+ name = "backup";
+ group = "backup";
+ isNormalUser = true;
+ createHome = true;
+ openssh.authorizedKeys.keys = [
+ snakeOilPublicKey
+ ];
+ };
+ users.groups.backup = { };
+ };
+
+ testScript =
+ { nodes, ... }:
+ ''
+ start_all()
+
+ primary.wait_for_unit("multi-user.target")
+ backup.wait_for_unit("multi-user.target")
+
+ primary.log(primary.succeed("""
+ HOME="/var/lib/pgbackrest"
+ cat ${snakeOilPrivateKey} > ~/sftp_key
+ chown -R pgbackrest:pgbackrest ~/sftp_key
+ chmod 770 ~
+ """))
+
+ with subtest("backup/restore works with local instance/remote repo (SFTP)"):
+ primary.succeed("sudo -u pgbackrest pgbackrest --stanza=default stanza-create", timeout=10)
+ primary.succeed("sudo -u pgbackrest pgbackrest --stanza=default check")
+
+ primary.systemctl("start pgbackrest-default-future")
+
+ # corrupt cluster
+ primary.systemctl("stop postgresql")
+ primary.execute("rm ${nodes.primary.services.postgresql.dataDir}/global/pg_control")
+
+ primary.succeed("sudo -u postgres pgbackrest --stanza=default restore --delta")
+
+ primary.systemctl("start postgresql")
+ primary.wait_for_unit("postgresql.service")
+ assert "hello world" in primary.succeed("sudo -u postgres psql -c 'TABLE t;'")
+ '';
+}
diff --git a/nixos/tests/playwright-python.nix b/nixos/tests/playwright-python.nix
index 02d7b1cc5b9d..e479f3a91517 100644
--- a/nixos/tests/playwright-python.nix
+++ b/nixos/tests/playwright-python.nix
@@ -21,6 +21,7 @@ import ./make-test-python.nix (
}
''
import sys
+ import re
from playwright.sync_api import sync_playwright
from playwright.sync_api import expect
@@ -29,6 +30,7 @@ import ./make-test-python.nix (
"firefox": {},
"webkit": {}
}
+ needle = re.compile("Nix.*Reference Manual")
if len(sys.argv) != 3 or sys.argv[1] not in browsers.keys():
print(f"usage: {sys.argv[0]} [{'|'.join(browsers.keys())}] ")
sys.exit(1)
@@ -42,7 +44,7 @@ import ./make-test-python.nix (
context = browser.new_context()
page = context.new_page()
page.goto(url)
- expect(page.get_by_text("Nix Reference Manual")).to_be_visible()
+ expect(page.get_by_text(needle)).to_be_visible()
''
)
];
diff --git a/nixos/tests/please.nix b/nixos/tests/please.nix
index 9efd9649ec0f..6b461bff4938 100644
--- a/nixos/tests/please.nix
+++ b/nixos/tests/please.nix
@@ -2,7 +2,7 @@ import ./make-test-python.nix (
{ lib, ... }:
{
name = "please";
- meta.maintainers = with lib.maintainers; [ azahi ];
+ meta.maintainers = [ ];
nodes.machine =
{ ... }:
diff --git a/nixos/tests/pocket-id.nix b/nixos/tests/pocket-id.nix
new file mode 100644
index 000000000000..753fa251473f
--- /dev/null
+++ b/nixos/tests/pocket-id.nix
@@ -0,0 +1,47 @@
+import ./make-test-python.nix (
+ { lib, ... }:
+
+ {
+ name = "pocket-id";
+ meta.maintainers = with lib.maintainers; [
+ gepbird
+ ymstnt
+ ];
+
+ nodes = {
+ machine =
+ { ... }:
+ {
+ services.pocket-id = {
+ enable = true;
+ settings = {
+ PORT = 10001;
+ INTERNAL_BACKEND_URL = "http://localhost:10002";
+ BACKEND_PORT = 10002;
+ };
+ };
+ };
+ };
+
+ testScript =
+ { nodes, ... }:
+ let
+ inherit (nodes.machine.services.pocket-id) settings;
+ inherit (builtins) toString;
+ in
+ ''
+ machine.wait_for_unit("pocket-id-backend.service")
+ machine.wait_for_open_port(${toString settings.BACKEND_PORT})
+ machine.wait_for_unit("pocket-id-frontend.service")
+ machine.wait_for_open_port(${toString settings.PORT})
+
+ backend_status = machine.succeed("curl -L -o /tmp/backend-output -w '%{http_code}' http://localhost:${toString settings.BACKEND_PORT}/api/users/me")
+ assert backend_status == "401"
+ machine.succeed("grep 'You are not signed in' /tmp/backend-output")
+
+ frontend_status = machine.succeed("curl -L -o /tmp/frontend-output -w '%{http_code}' http://localhost:${toString settings.PORT}")
+ assert frontend_status == "200"
+ machine.succeed("grep 'Sign in to Pocket ID' /tmp/frontend-output")
+ '';
+ }
+)
diff --git a/nixos/tests/postgres-websockets.nix b/nixos/tests/postgres-websockets.nix
new file mode 100644
index 000000000000..c3badf22383e
--- /dev/null
+++ b/nixos/tests/postgres-websockets.nix
@@ -0,0 +1,84 @@
+{ lib, ... }:
+{
+ name = "postgres-websockets";
+
+ meta = {
+ maintainers = with lib.maintainers; [ wolfgangwalther ];
+ };
+
+ nodes.machine =
+ {
+ config,
+ lib,
+ pkgs,
+ ...
+ }:
+ {
+ environment.systemPackages = [ pkgs.websocat ];
+
+ services.postgresql = {
+ enable = true;
+ initialScript = pkgs.writeText "init.sql" ''
+ CREATE ROLE "postgres-websockets" LOGIN NOINHERIT;
+ CREATE ROLE "postgres-websockets_with_password" LOGIN NOINHERIT PASSWORD 'password';
+ '';
+ };
+
+ services.postgres-websockets = {
+ enable = true;
+ jwtSecretFile = "/run/secrets/jwt.secret";
+ environment.PGWS_DB_URI.dbname = "postgres";
+ environment.PGWS_LISTEN_CHANNEL = "websockets-listener";
+ };
+
+ specialisation.withPassword.configuration = {
+ services.postgresql.enableTCPIP = true;
+ services.postgres-websockets = {
+ pgpassFile = "/run/secrets/.pgpass";
+ environment.PGWS_DB_URI.host = "localhost";
+ environment.PGWS_DB_URI.user = "postgres-websockets_with_password";
+ };
+ };
+ };
+
+ extraPythonPackages = p: [ p.pyjwt ];
+
+ testScript =
+ { nodes, ... }:
+ let
+ withPassword = "${nodes.machine.system.build.toplevel}/specialisation/withPassword";
+ in
+ ''
+ machine.execute("""
+ mkdir -p /run/secrets
+ echo reallyreallyreallyreallyverysafe > /run/secrets/jwt.secret
+ """)
+
+ import jwt
+ token = jwt.encode({ "mode": "rw" }, "reallyreallyreallyreallyverysafe")
+
+ def test():
+ machine.wait_for_unit("postgresql.service")
+ machine.wait_for_unit("postgres-websockets.service")
+
+ machine.succeed(f"echo 'hi there' | websocat --no-close 'ws://localhost:3000/test/{token}' > output &")
+ machine.sleep(1)
+ machine.succeed("grep 'hi there' output")
+
+ machine.succeed("""
+ sudo -u postgres psql -c "SELECT pg_notify('websockets-listener', json_build_object('channel', 'test', 'event', 'message', 'payload', 'Hello World')::text);" >/dev/null
+ """)
+ machine.sleep(1)
+ machine.succeed("grep 'Hello World' output")
+
+ with subtest("without password"):
+ test()
+
+ with subtest("with password"):
+ machine.execute("""
+ echo "*:*:*:*:password" > /run/secrets/.pgpass
+ """)
+ machine.succeed("${withPassword}/bin/switch-to-configuration test >&2")
+ test()
+ '';
+}
diff --git a/nixos/tests/postgresql/anonymizer.nix b/nixos/tests/postgresql/anonymizer.nix
index d59a26f101e8..77c38f9344ef 100644
--- a/nixos/tests/postgresql/anonymizer.nix
+++ b/nixos/tests/postgresql/anonymizer.nix
@@ -20,7 +20,6 @@ let
services.postgresql = {
inherit package;
enable = true;
- enableJIT = lib.hasInfix "-jit-" package.name;
extensions = ps: [ ps.anonymizer ];
settings.shared_preload_libraries = [ "anon" ];
};
diff --git a/nixos/tests/postgresql/citus.nix b/nixos/tests/postgresql/citus.nix
deleted file mode 100644
index 6739b32d5642..000000000000
--- a/nixos/tests/postgresql/citus.nix
+++ /dev/null
@@ -1,73 +0,0 @@
-{
- pkgs,
- makeTest,
- genTests,
-}:
-
-let
- inherit (pkgs) lib;
-
- test-sql = pkgs.writeText "postgresql-test" ''
- CREATE EXTENSION citus;
-
- CREATE TABLE examples (
- id bigserial,
- shard_key int,
- PRIMARY KEY (id, shard_key)
- );
-
- SELECT create_distributed_table('examples', 'shard_key');
-
- INSERT INTO examples (shard_key) SELECT shard % 10 FROM generate_series(1,1000) shard;
- '';
-
- makeTestFor =
- package:
- makeTest {
- name = "citus-${package.name}";
- meta = with lib.maintainers; {
- maintainers = [ typetetris ];
- };
-
- nodes.machine =
- { ... }:
- {
- services.postgresql = {
- inherit package;
- enable = true;
- enableJIT = lib.hasInfix "-jit-" package.name;
- extensions =
- ps: with ps; [
- citus
- ];
- settings = {
- shared_preload_libraries = "citus";
- };
- };
- };
-
- testScript = ''
- def check_count(statement, lines):
- return 'test $(sudo -u postgres psql postgres -tAc "{}") -eq {}'.format(
- statement, lines
- )
-
-
- machine.start()
- machine.wait_for_unit("postgresql")
-
- with subtest("Postgresql with extension citus is available just after unit start"):
- machine.succeed(
- "sudo -u postgres psql -f ${test-sql}"
- )
-
- machine.succeed(check_count("SELECT count(*) FROM examples;", 1000))
-
- machine.shutdown()
- '';
- };
-in
-genTests {
- inherit makeTestFor;
- filter = _: p: !p.pkgs.citus.meta.broken;
-}
diff --git a/nixos/tests/postgresql/default.nix b/nixos/tests/postgresql/default.nix
index 474f54a17201..f7266c2e9db2 100644
--- a/nixos/tests/postgresql/default.nix
+++ b/nixos/tests/postgresql/default.nix
@@ -36,10 +36,6 @@ in
# extensions
anonymizer = importWithArgs ./anonymizer.nix;
- citus = importWithArgs ./citus.nix;
pgjwt = importWithArgs ./pgjwt.nix;
- pgvecto-rs = importWithArgs ./pgvecto-rs.nix;
- timescaledb = importWithArgs ./timescaledb.nix;
- tsja = importWithArgs ./tsja.nix;
wal2json = importWithArgs ./wal2json.nix;
}
diff --git a/nixos/tests/postgresql/pgjwt.nix b/nixos/tests/postgresql/pgjwt.nix
index f00d9a939d3d..9a5e12bc3d7d 100644
--- a/nixos/tests/postgresql/pgjwt.nix
+++ b/nixos/tests/postgresql/pgjwt.nix
@@ -24,7 +24,6 @@ let
services.postgresql = {
inherit package;
enable = true;
- enableJIT = lib.hasInfix "-jit-" package.name;
extensions =
ps: with ps; [
pgjwt
diff --git a/nixos/tests/postgresql/pgvecto-rs.nix b/nixos/tests/postgresql/pgvecto-rs.nix
deleted file mode 100644
index 506ef921acb7..000000000000
--- a/nixos/tests/postgresql/pgvecto-rs.nix
+++ /dev/null
@@ -1,79 +0,0 @@
-{
- pkgs,
- makeTest,
- genTests,
-}:
-
-let
- inherit (pkgs) lib;
-
- # Test cases from https://docs.vectorchord.ai/use-case/hybrid-search.html
- test-sql = pkgs.writeText "postgresql-test" ''
- CREATE EXTENSION vectors;
-
- CREATE TABLE items (
- id bigserial PRIMARY KEY,
- content text NOT NULL,
- embedding vectors.vector(3) NOT NULL -- 3 dimensions
- );
-
- INSERT INTO items (content, embedding) VALUES
- ('a fat cat sat on a mat and ate a fat rat', '[1, 2, 3]'),
- ('a fat dog sat on a mat and ate a fat rat', '[4, 5, 6]'),
- ('a thin cat sat on a mat and ate a thin rat', '[7, 8, 9]'),
- ('a thin dog sat on a mat and ate a thin rat', '[10, 11, 12]');
- '';
-
- makeTestFor =
- package:
- makeTest {
- name = "pgvecto-rs-${package.name}";
- meta = with lib.maintainers; {
- maintainers = [ diogotcorreia ];
- };
-
- nodes.machine =
- { ... }:
- {
- services.postgresql = {
- inherit package;
- enable = true;
- enableJIT = lib.hasInfix "-jit-" package.name;
- extensions =
- ps: with ps; [
- pgvecto-rs
- ];
- settings.shared_preload_libraries = "vectors";
- };
- };
-
- testScript =
- { nodes, ... }:
- let
- inherit (nodes.machine.services.postgresql.package.pkgs) pgvecto-rs;
- in
- ''
- def check_count(statement, lines):
- return 'test $(sudo -u postgres psql postgres -tAc "{}"|wc -l) -eq {}'.format(
- statement, lines
- )
-
-
- machine.start()
- machine.wait_for_unit("postgresql")
-
- with subtest("Postgresql with extension vectors is available just after unit start"):
- machine.succeed(check_count("SELECT * FROM pg_available_extensions WHERE name = 'vectors' AND default_version = '${pgvecto-rs.version}';", 1))
-
- machine.succeed("sudo -u postgres psql -f ${test-sql}")
-
- machine.succeed(check_count("SELECT content, embedding FROM items WHERE to_tsvector('english', content) @@ 'cat & rat'::tsquery;", 2))
-
- machine.shutdown()
- '';
- };
-in
-genTests {
- inherit makeTestFor;
- filter = _: p: !p.pkgs.pgvecto-rs.meta.broken;
-}
diff --git a/nixos/tests/postgresql/postgresql-jit.nix b/nixos/tests/postgresql/postgresql-jit.nix
index e082ff141327..53d35b3e9d64 100644
--- a/nixos/tests/postgresql/postgresql-jit.nix
+++ b/nixos/tests/postgresql/postgresql-jit.nix
@@ -51,5 +51,4 @@ let
in
genTests {
inherit makeTestFor;
- filter = n: _: lib.hasSuffix "_jit" n;
}
diff --git a/nixos/tests/postgresql/postgresql-tls-client-cert.nix b/nixos/tests/postgresql/postgresql-tls-client-cert.nix
index 117b9b0e24a9..6cb86d1ff8fc 100644
--- a/nixos/tests/postgresql/postgresql-tls-client-cert.nix
+++ b/nixos/tests/postgresql/postgresql-tls-client-cert.nix
@@ -51,7 +51,6 @@ let
services.postgresql = {
inherit package;
enable = true;
- enableJIT = lib.hasInfix "-jit-" package.name;
enableTCPIP = true;
ensureUsers = [
{
diff --git a/nixos/tests/postgresql/postgresql-wal-receiver.nix b/nixos/tests/postgresql/postgresql-wal-receiver.nix
index c99c3889c027..70f9983700c2 100644
--- a/nixos/tests/postgresql/postgresql-wal-receiver.nix
+++ b/nixos/tests/postgresql/postgresql-wal-receiver.nix
@@ -32,7 +32,6 @@ let
services.postgresql = {
inherit package;
enable = true;
- enableJIT = lib.hasInfix "-jit-" package.name;
settings = {
max_replication_slots = 10;
max_wal_senders = 10;
diff --git a/nixos/tests/postgresql/postgresql.nix b/nixos/tests/postgresql/postgresql.nix
index 7741d14808eb..ecf1b5552804 100644
--- a/nixos/tests/postgresql/postgresql.nix
+++ b/nixos/tests/postgresql/postgresql.nix
@@ -15,41 +15,33 @@ let
postgresql-clauses = makeEnsureTestFor package;
};
- test-sql =
- enablePLv8Test:
- pkgs.writeText "postgresql-test" (
- ''
- CREATE EXTENSION pgcrypto; -- just to check if lib loading works
- CREATE TABLE sth (
- id int
- );
- INSERT INTO sth (id) VALUES (1);
- INSERT INTO sth (id) VALUES (1);
- INSERT INTO sth (id) VALUES (1);
- INSERT INTO sth (id) VALUES (1);
- INSERT INTO sth (id) VALUES (1);
- CREATE TABLE xmltest ( doc xml );
- INSERT INTO xmltest (doc) VALUES ('ok '); -- check if libxml2 enabled
- ''
- + lib.optionalString enablePLv8Test ''
- -- check if hardening gets relaxed
- CREATE EXTENSION plv8;
- -- try to trigger the V8 JIT, which requires MemoryDenyWriteExecute
- DO $$
- let xs = [];
- for (let i = 0, n = 400000; i < n; i++) {
- xs.push(Math.round(Math.random() * n))
- }
- console.log(xs.reduce((acc, x) => acc + x, 0));
- $$ LANGUAGE plv8;
- ''
+ test-sql = pkgs.writeText "postgresql-test" (''
+ CREATE EXTENSION pgcrypto; -- just to check if lib loading works
+ CREATE TABLE sth (
+ id int
);
+ INSERT INTO sth (id) VALUES (1);
+ INSERT INTO sth (id) VALUES (1);
+ INSERT INTO sth (id) VALUES (1);
+ INSERT INTO sth (id) VALUES (1);
+ INSERT INTO sth (id) VALUES (1);
+ CREATE TABLE xmltest ( doc xml );
+ INSERT INTO xmltest (doc) VALUES ('ok '); -- check if libxml2 enabled
+
+ -- check if hardening gets relaxed
+ CREATE EXTENSION plv8;
+ -- try to trigger the V8 JIT, which requires MemoryDenyWriteExecute
+ DO $$
+ let xs = [];
+ for (let i = 0, n = 400000; i < n; i++) {
+ xs.push(Math.round(Math.random() * n))
+ }
+ console.log(xs.reduce((acc, x) => acc + x, 0));
+ $$ LANGUAGE plv8;
+ '');
makeTestForWithBackupAll =
package: backupAll:
- let
- enablePLv8Check = !package.pkgs.plv8.meta.broken;
- in
makeTest {
name = "postgresql${lib.optionalString backupAll "-backup-all"}-${package.name}";
meta = with lib.maintainers; {
@@ -62,12 +54,12 @@ let
services.postgresql = {
inherit package;
enable = true;
- enableJIT = lib.hasInfix "-jit-" package.name;
- # plv8 doesn't support postgresql with JIT, so we only run the test
- # for the non-jit variant.
+ identMap = ''
+ postgres root postgres
+ '';
# TODO(@Ma27) split this off into its own VM test and move a few other
# extension tests to use postgresqlTestExtension.
- extensions = lib.mkIf enablePLv8Check (ps: with ps; [ plv8 ]);
+ extensions = ps: with ps; [ plv8 ];
};
services.postgresqlBackup = {
@@ -84,7 +76,7 @@ let
in
''
def check_count(statement, lines):
- return 'test $(sudo -u postgres psql postgres -tAc "{}"|wc -l) -eq {}'.format(
+ return 'test $(psql -U postgres postgres -tAc "{}"|wc -l) -eq {}'.format(
statement, lines
)
@@ -94,7 +86,7 @@ let
with subtest("Postgresql is available just after unit start"):
machine.succeed(
- "cat ${test-sql enablePLv8Check} | sudo -u postgres psql"
+ "cat ${test-sql} | sudo -u postgres psql"
)
with subtest("Postgresql survives restart (bug #1735)"):
@@ -184,7 +176,6 @@ let
services.postgresql = {
inherit package;
enable = true;
- enableJIT = lib.hasInfix "-jit-" package.name;
ensureUsers = [
{
name = "all-clauses";
diff --git a/nixos/tests/postgresql/timescaledb.nix b/nixos/tests/postgresql/timescaledb.nix
deleted file mode 100644
index 7ad8b0fcc972..000000000000
--- a/nixos/tests/postgresql/timescaledb.nix
+++ /dev/null
@@ -1,98 +0,0 @@
-{
- pkgs,
- makeTest,
- genTests,
-}:
-
-let
- inherit (pkgs) lib;
-
- test-sql = pkgs.writeText "postgresql-test" ''
- CREATE EXTENSION timescaledb;
- CREATE EXTENSION timescaledb_toolkit;
-
- CREATE TABLE sth (
- time TIMESTAMPTZ NOT NULL,
- value DOUBLE PRECISION
- );
-
- SELECT create_hypertable('sth', 'time');
-
- INSERT INTO sth (time, value) VALUES
- ('2003-04-12 04:05:06 America/New_York', 1.0),
- ('2003-04-12 04:05:07 America/New_York', 2.0),
- ('2003-04-12 04:05:08 America/New_York', 3.0),
- ('2003-04-12 04:05:09 America/New_York', 4.0),
- ('2003-04-12 04:05:10 America/New_York', 5.0)
- ;
-
- WITH t AS (
- SELECT
- time_bucket('1 day'::interval, time) AS dt,
- stats_agg(value) AS stats
- FROM sth
- GROUP BY time_bucket('1 day'::interval, time)
- )
- SELECT
- average(stats)
- FROM t;
-
- SELECT * FROM sth;
- '';
-
- makeTestFor =
- package:
- makeTest {
- name = "timescaledb-${package.name}";
- meta = with lib.maintainers; {
- maintainers = [ typetetris ];
- };
-
- nodes.machine =
- { ... }:
- {
- services.postgresql = {
- inherit package;
- enable = true;
- enableJIT = lib.hasInfix "-jit-" package.name;
- extensions =
- ps: with ps; [
- timescaledb
- timescaledb_toolkit
- ];
- settings = {
- shared_preload_libraries = "timescaledb, timescaledb_toolkit";
- };
- };
- };
-
- testScript = ''
- def check_count(statement, lines):
- return 'test $(sudo -u postgres psql postgres -tAc "{}"|wc -l) -eq {}'.format(
- statement, lines
- )
-
-
- machine.start()
- machine.wait_for_unit("postgresql")
-
- with subtest("Postgresql with extensions timescaledb and timescaledb_toolkit is available just after unit start"):
- machine.succeed(
- "sudo -u postgres psql -f ${test-sql}"
- )
-
- machine.fail(check_count("SELECT * FROM sth;", 3))
- machine.succeed(check_count("SELECT * FROM sth;", 5))
- machine.fail(check_count("SELECT * FROM sth;", 4))
-
- machine.shutdown()
- '';
- };
-in
-# Not run by default, because this requires allowUnfree.
-# To run these tests:
-# NIXPKGS_ALLOW_UNFREE=1 nix-build -A nixosTests.postgresql.timescaledb
-lib.dontRecurseIntoAttrs (genTests {
- inherit makeTestFor;
- filter = _: p: !p.pkgs.timescaledb.meta.broken;
-})
diff --git a/nixos/tests/postgresql/tsja.nix b/nixos/tests/postgresql/tsja.nix
deleted file mode 100644
index 4cc5bd124139..000000000000
--- a/nixos/tests/postgresql/tsja.nix
+++ /dev/null
@@ -1,48 +0,0 @@
-{
- pkgs,
- makeTest,
- genTests,
-}:
-
-let
- inherit (pkgs) lib;
-
- makeTestFor =
- package:
- makeTest {
- name = "tsja-${package.name}";
- meta = {
- maintainers = with lib.maintainers; [ chayleaf ];
- };
-
- nodes.master =
- { ... }:
- {
- services.postgresql = {
- inherit package;
- enable = true;
- enableJIT = lib.hasInfix "-jit-" package.name;
- extensions =
- ps: with ps; [
- tsja
- ];
- };
- };
-
- testScript = ''
- start_all()
- master.wait_for_unit("postgresql")
- master.succeed("sudo -u postgres psql -f /run/current-system/sw/share/postgresql/extension/libtsja_dbinit.sql")
- # make sure "日本語" is parsed as a separate lexeme
- master.succeed("""
- sudo -u postgres \\
- psql -c "SELECT * FROM ts_debug('japanese', 'PostgreSQLで日本語のテキスト検索ができます。')" \\
- | grep "{日本語}"
- """)
- '';
- };
-in
-genTests {
- inherit makeTestFor;
- filter = _: p: !p.pkgs.tsja.meta.broken;
-}
diff --git a/nixos/tests/postgresql/wal2json.nix b/nixos/tests/postgresql/wal2json.nix
index abfe60673753..1252264353f8 100644
--- a/nixos/tests/postgresql/wal2json.nix
+++ b/nixos/tests/postgresql/wal2json.nix
@@ -17,7 +17,6 @@ let
services.postgresql = {
inherit package;
enable = true;
- enableJIT = lib.hasInfix "-jit-" package.name;
extensions = with package.pkgs; [ wal2json ];
settings = {
wal_level = "logical";
diff --git a/nixos/tests/printing.nix b/nixos/tests/printing.nix
index 5fee5c6a4989..890bc207a326 100644
--- a/nixos/tests/printing.nix
+++ b/nixos/tests/printing.nix
@@ -1,141 +1,138 @@
# Test printing via CUPS.
+{
+ pkgs,
+ socket ? true, # whether to use socket activation
+ listenTcp ? true, # whether to open port 631 on client
+ ...
+}:
-import ./make-test-python.nix (
- {
- pkgs,
- socket ? true, # whether to use socket activation
- listenTcp ? true, # whether to open port 631 on client
- ...
- }:
+let
+ inherit (pkgs) lib;
+in
- let
- inherit (pkgs) lib;
- in
+{
+ name = "printing";
+ meta = with lib.maintainers; {
+ maintainers = [
+ domenkozar
+ matthewbauer
+ ];
+ };
- {
- name = "printing";
- meta = with lib.maintainers; {
- maintainers = [
- domenkozar
- matthewbauer
+ nodes.server =
+ { ... }:
+ {
+ services.printing = {
+ enable = true;
+ stateless = true;
+ startWhenNeeded = socket;
+ listenAddresses = [ "*:631" ];
+ defaultShared = true;
+ openFirewall = true;
+ extraConf = ''
+
+ Order allow,deny
+ Allow from all
+
+ '';
+ };
+ # Add a HP Deskjet printer connected via USB to the server.
+ hardware.printers.ensurePrinters = [
+ {
+ name = "DeskjetLocal";
+ deviceUri = "usb://foobar/printers/foobar";
+ model = "drv:///sample.drv/deskjet.ppd";
+ }
];
};
- nodes.server =
- { ... }:
- {
- services.printing = {
- enable = true;
- stateless = true;
- startWhenNeeded = socket;
- listenAddresses = [ "*:631" ];
- defaultShared = true;
- openFirewall = true;
- extraConf = ''
-
- Order allow,deny
- Allow from all
-
- '';
- };
- # Add a HP Deskjet printer connected via USB to the server.
- hardware.printers.ensurePrinters = [
- {
- name = "DeskjetLocal";
- deviceUri = "usb://foobar/printers/foobar";
- model = "drv:///sample.drv/deskjet.ppd";
- }
- ];
- };
+ nodes.client =
+ { lib, ... }:
+ {
+ services.printing.enable = true;
+ services.printing.startWhenNeeded = socket;
+ services.printing.listenAddresses = lib.mkIf (!listenTcp) [ ];
+ # Add printer to the client as well, via IPP.
+ hardware.printers.ensurePrinters = [
+ {
+ name = "DeskjetRemote";
+ deviceUri = "ipp://server/printers/DeskjetLocal";
+ model = "drv:///sample.drv/deskjet.ppd";
+ }
+ ];
+ hardware.printers.ensureDefaultPrinter = "DeskjetRemote";
+ };
- nodes.client =
- { lib, ... }:
- {
- services.printing.enable = true;
- services.printing.startWhenNeeded = socket;
- services.printing.listenAddresses = lib.mkIf (!listenTcp) [ ];
- # Add printer to the client as well, via IPP.
- hardware.printers.ensurePrinters = [
- {
- name = "DeskjetRemote";
- deviceUri = "ipp://server/printers/DeskjetLocal";
- model = "drv:///sample.drv/deskjet.ppd";
- }
- ];
- hardware.printers.ensureDefaultPrinter = "DeskjetRemote";
- };
+ testScript = ''
+ import os
+ import re
- testScript = ''
- import os
- import re
+ start_all()
- start_all()
+ with subtest("Make sure that cups is up on both sides and printers are set up"):
+ server.wait_for_unit("ensure-printers.service")
+ client.wait_for_unit("ensure-printers.service")
- with subtest("Make sure that cups is up on both sides and printers are set up"):
- server.wait_for_unit("ensure-printers.service")
- client.wait_for_unit("ensure-printers.service")
+ assert "scheduler is running" in client.succeed("lpstat -r")
- assert "scheduler is running" in client.succeed("lpstat -r")
+ with subtest("UNIX socket is used for connections"):
+ assert "/var/run/cups/cups.sock" in client.succeed("lpstat -H")
- with subtest("UNIX socket is used for connections"):
- assert "/var/run/cups/cups.sock" in client.succeed("lpstat -H")
+ with subtest("HTTP server is available too"):
+ ${lib.optionalString listenTcp ''client.succeed("curl --fail http://localhost:631/")''}
+ client.succeed(f"curl --fail http://{server.name}:631/")
+ server.fail(f"curl --fail --connect-timeout 2 http://{client.name}:631/")
- with subtest("HTTP server is available too"):
- ${lib.optionalString listenTcp ''client.succeed("curl --fail http://localhost:631/")''}
- client.succeed(f"curl --fail http://{server.name}:631/")
- server.fail(f"curl --fail --connect-timeout 2 http://{client.name}:631/")
+ with subtest("LP status checks"):
+ assert "DeskjetRemote accepting requests" in client.succeed("lpstat -a")
+ assert "DeskjetLocal accepting requests" in client.succeed(
+ f"lpstat -h {server.name}:631 -a"
+ )
+ client.succeed("cupsdisable DeskjetRemote")
+ out = client.succeed("lpq")
+ print(out)
+ assert re.search(
+ "DeskjetRemote is not ready.*no entries",
+ client.succeed("lpq"),
+ flags=re.DOTALL,
+ )
+ client.succeed("cupsenable DeskjetRemote")
+ assert re.match(
+ "DeskjetRemote is ready.*no entries", client.succeed("lpq"), flags=re.DOTALL
+ )
- with subtest("LP status checks"):
- assert "DeskjetRemote accepting requests" in client.succeed("lpstat -a")
- assert "DeskjetLocal accepting requests" in client.succeed(
- f"lpstat -h {server.name}:631 -a"
- )
- client.succeed("cupsdisable DeskjetRemote")
- out = client.succeed("lpq")
- print(out)
- assert re.search(
- "DeskjetRemote is not ready.*no entries",
- client.succeed("lpq"),
- flags=re.DOTALL,
- )
- client.succeed("cupsenable DeskjetRemote")
- assert re.match(
- "DeskjetRemote is ready.*no entries", client.succeed("lpq"), flags=re.DOTALL
- )
+ # Test printing various file types.
+ for file in [
+ "${pkgs.groff.doc}/share/doc/*/examples/mom/penguin.pdf",
+ "${pkgs.groff.doc}/share/doc/*/meref.ps",
+ "${pkgs.cups.out}/share/doc/cups/images/cups.png",
+ "${pkgs.pcre.doc}/share/doc/pcre/pcre.txt",
+ ]:
+ file_name = os.path.basename(file)
+ with subtest(f"print {file_name}"):
+ # Print the file on the client.
+ print(client.succeed("lpq"))
+ client.succeed(f"lp {file}")
+ client.wait_until_succeeds(
+ f"lpq; lpq | grep -q -E 'active.*root.*{file_name}'"
+ )
- # Test printing various file types.
- for file in [
- "${pkgs.groff.doc}/share/doc/*/examples/mom/penguin.pdf",
- "${pkgs.groff.doc}/share/doc/*/meref.ps",
- "${pkgs.cups.out}/share/doc/cups/images/cups.png",
- "${pkgs.pcre.doc}/share/doc/pcre/pcre.txt",
- ]:
- file_name = os.path.basename(file)
- with subtest(f"print {file_name}"):
- # Print the file on the client.
- print(client.succeed("lpq"))
- client.succeed(f"lp {file}")
- client.wait_until_succeeds(
- f"lpq; lpq | grep -q -E 'active.*root.*{file_name}'"
- )
+ # Ensure that a raw PCL file appeared in the server's queue
+ # (showing that the right filters have been applied). Of
+ # course, since there is no actual USB printer attached, the
+ # file will stay in the queue forever.
+ server.wait_for_file("/var/spool/cups/d*-001")
+ server.wait_until_succeeds(f"lpq -a | grep -q -E '{file_name}'")
- # Ensure that a raw PCL file appeared in the server's queue
- # (showing that the right filters have been applied). Of
- # course, since there is no actual USB printer attached, the
- # file will stay in the queue forever.
- server.wait_for_file("/var/spool/cups/d*-001")
- server.wait_until_succeeds(f"lpq -a | grep -q -E '{file_name}'")
+ # Delete the job on the client. It should disappear on the
+ # server as well.
+ client.succeed("lprm")
+ client.wait_until_succeeds("lpq -a | grep -q -E 'no entries'")
- # Delete the job on the client. It should disappear on the
- # server as well.
- client.succeed("lprm")
- client.wait_until_succeeds("lpq -a | grep -q -E 'no entries'")
+ retry(lambda _: "no entries" in server.succeed("lpq -a"))
- retry(lambda _: "no entries" in server.succeed("lpq -a"))
-
- # The queue is empty already, so this should be safe.
- # Otherwise, pairs of "c*"-"d*-001" files might persist.
- server.execute("rm /var/spool/cups/*")
- '';
- }
-)
+ # The queue is empty already, so this should be safe.
+ # Otherwise, pairs of "c*"-"d*-001" files might persist.
+ server.execute("rm /var/spool/cups/*")
+ '';
+}
diff --git a/nixos/tests/prometheus-exporters.nix b/nixos/tests/prometheus-exporters.nix
index 46b8d13364c6..ef356dbc7bb1 100644
--- a/nixos/tests/prometheus-exporters.nix
+++ b/nixos/tests/prometheus-exporters.nix
@@ -407,6 +407,20 @@ let
'';
};
+ ebpf = {
+ exporterConfig = {
+ enable = true;
+ names = [ "timers" ];
+ };
+ exporterTest = ''
+ wait_for_unit("prometheus-ebpf-exporter.service")
+ wait_for_open_port(9435)
+ succeed(
+ "curl -sSf http://localhost:9435/metrics | grep 'ebpf_exporter_enabled_configs{name=\"timers\"} 1'"
+ )
+ '';
+ };
+
fastly = {
exporterConfig = {
enable = true;
diff --git a/nixos/tests/prometheus/alertmanager-ntfy.nix b/nixos/tests/prometheus/alertmanager-ntfy.nix
new file mode 100644
index 000000000000..8015354cbb57
--- /dev/null
+++ b/nixos/tests/prometheus/alertmanager-ntfy.nix
@@ -0,0 +1,99 @@
+{ lib, ... }:
+
+let
+ ports = {
+ alertmanager-ntfy = 8000;
+ ntfy-sh = 8001;
+ alertmanager = 8002;
+ };
+in
+
+{
+ name = "alertmanager-ntfy";
+ meta.maintainers = with lib.maintainers; [ defelo ];
+
+ nodes.machine = {
+ services.prometheus.alertmanager = {
+ enable = true;
+ listenAddress = "127.0.0.1";
+ port = ports.alertmanager;
+
+ configuration = {
+ route = {
+ receiver = "test";
+ group_by = [ "..." ];
+ group_wait = "0s";
+ group_interval = "1s";
+ repeat_interval = "2h";
+ };
+
+ receivers = [
+ {
+ name = "test";
+ webhook_configs = [ { url = "http://127.0.0.1:${toString ports.alertmanager-ntfy}/hook"; } ];
+ }
+ ];
+ };
+ };
+
+ services.prometheus.alertmanager-ntfy = {
+ enable = true;
+ settings = {
+ http.addr = "127.0.0.1:${toString ports.alertmanager-ntfy}";
+ ntfy = {
+ baseurl = "http://127.0.0.1:${toString ports.ntfy-sh}";
+ notification.topic = "alertmanager";
+ };
+ };
+ };
+
+ services.ntfy-sh = {
+ enable = true;
+ settings = {
+ listen-http = "127.0.0.1:${toString ports.ntfy-sh}";
+ base-url = "http://127.0.0.1:${toString ports.ntfy-sh}";
+ };
+ };
+ };
+
+ interactive.nodes.machine = {
+ services.prometheus.alertmanager.listenAddress = lib.mkForce "0.0.0.0";
+ services.prometheus.alertmanager-ntfy.settings.http.addr =
+ lib.mkForce "0.0.0.0:${toString ports.alertmanager-ntfy}";
+ services.ntfy-sh.settings.listen-http = lib.mkForce "0.0.0.0:${toString ports.ntfy-sh}";
+ networking.firewall.enable = false;
+ virtualisation.forwardPorts = lib.mapAttrsToList (_: port: {
+ from = "host";
+ host = { inherit port; };
+ guest = { inherit port; };
+ }) ports;
+ };
+
+ testScript = ''
+ import json
+ import time
+
+ machine.wait_for_unit("alertmanager.service")
+ machine.wait_for_unit("alertmanager-ntfy.service")
+ machine.wait_for_unit("ntfy-sh.service")
+ machine.wait_for_open_port(${toString ports.alertmanager})
+ machine.wait_for_open_port(${toString ports.alertmanager-ntfy})
+ machine.wait_for_open_port(${toString ports.ntfy-sh})
+
+ machine.succeed("""curl 127.0.0.1:${toString ports.alertmanager}/api/v2/alerts \
+ -X POST -H 'Content-Type: application/json' \
+ -d '[{ \
+ "labels": {"alertname": "test"},
+ "annotations": {"summary": "alert summary", "description": "alert description"} \
+ }]'""")
+
+ while not (resp := machine.succeed("curl '127.0.0.1:${toString ports.ntfy-sh}/alertmanager/json?poll=1'")):
+ time.sleep(1)
+
+ msg = json.loads(resp)
+ assert msg["title"] == "alert summary"
+ assert msg["message"] == "alert description"
+ assert msg["priority"] == 4
+ assert "red_circle" in msg["tags"]
+ '';
+}
diff --git a/nixos/tests/prometheus/alertmanager.nix b/nixos/tests/prometheus/alertmanager.nix
index 51151cbab534..794bbb8aab90 100644
--- a/nixos/tests/prometheus/alertmanager.nix
+++ b/nixos/tests/prometheus/alertmanager.nix
@@ -1,160 +1,146 @@
-import ../make-test-python.nix (
- { lib, pkgs, ... }:
+{ pkgs, ... }:
- {
- name = "prometheus-alertmanager";
+{
+ name = "prometheus-alertmanager";
- nodes = {
- prometheus =
- { config, pkgs, ... }:
- {
- environment.systemPackages = [ pkgs.jq ];
+ nodes = {
+ prometheus =
+ { config, pkgs, ... }:
+ {
+ environment.systemPackages = [ pkgs.jq ];
- networking.firewall.allowedTCPPorts = [ config.services.prometheus.port ];
+ networking.firewall.allowedTCPPorts = [ config.services.prometheus.port ];
- services.prometheus = {
- enable = true;
- globalConfig.scrape_interval = "2s";
+ services.prometheus = {
+ enable = true;
+ globalConfig.scrape_interval = "2s";
- alertmanagers = [
- {
- scheme = "http";
- static_configs = [
- {
- targets = [
- "alertmanager:${toString config.services.prometheus.alertmanager.port}"
- ];
- }
- ];
- }
- ];
-
- rules = [
- ''
- groups:
- - name: test
- rules:
- - alert: InstanceDown
- expr: up == 0
- for: 5s
- labels:
- severity: page
- annotations:
- summary: "Instance {{ $labels.instance }} down"
- ''
- ];
-
- scrapeConfigs = [
- {
- job_name = "alertmanager";
- static_configs = [
- {
- targets = [
- "alertmanager:${toString config.services.prometheus.alertmanager.port}"
- ];
- }
- ];
- }
- {
- job_name = "node";
- static_configs = [
- {
- targets = [
- "node:${toString config.services.prometheus.exporters.node.port}"
- ];
- }
- ];
- }
- ];
- };
- };
-
- alertmanager =
- { config, pkgs, ... }:
- {
- services.prometheus.alertmanager = {
- enable = true;
- openFirewall = true;
-
- configuration = {
- global = {
- resolve_timeout = "1m";
- };
-
- route = {
- # Root route node
- receiver = "test";
- group_by = [ "..." ];
- continue = false;
- group_wait = "1s";
- group_interval = "15s";
- repeat_interval = "24h";
- };
-
- receivers = [
- {
- name = "test";
- webhook_configs = [
- {
- url = "http://logger:6725";
- send_resolved = true;
- max_alerts = 0;
- }
- ];
- }
+ alertmanagers = [
+ {
+ scheme = "http";
+ static_configs = [
+ { targets = [ "alertmanager:${toString config.services.prometheus.alertmanager.port}" ]; }
];
+ }
+ ];
+
+ rules = [
+ ''
+ groups:
+ - name: test
+ rules:
+ - alert: InstanceDown
+ expr: up == 0
+ for: 5s
+ labels:
+ severity: page
+ annotations:
+ summary: "Instance {{ $labels.instance }} down"
+ ''
+ ];
+
+ scrapeConfigs = [
+ {
+ job_name = "alertmanager";
+ static_configs = [
+ { targets = [ "alertmanager:${toString config.services.prometheus.alertmanager.port}" ]; }
+ ];
+ }
+ {
+ job_name = "node";
+ static_configs = [
+ { targets = [ "node:${toString config.services.prometheus.exporters.node.port}" ]; }
+ ];
+ }
+ ];
+ };
+ };
+
+ alertmanager =
+ { config, pkgs, ... }:
+ {
+ services.prometheus.alertmanager = {
+ enable = true;
+ openFirewall = true;
+
+ configuration = {
+ global = {
+ resolve_timeout = "1m";
};
+
+ route = {
+ # Root route node
+ receiver = "test";
+ group_by = [ "..." ];
+ continue = false;
+ group_wait = "1s";
+ group_interval = "15s";
+ repeat_interval = "24h";
+ };
+
+ receivers = [
+ {
+ name = "test";
+ webhook_configs = [
+ {
+ url = "http://logger:6725";
+ send_resolved = true;
+ max_alerts = 0;
+ }
+ ];
+ }
+ ];
};
};
+ };
- logger =
- { config, pkgs, ... }:
- {
- networking.firewall.allowedTCPPorts = [ 6725 ];
+ logger =
+ { config, pkgs, ... }:
+ {
+ networking.firewall.allowedTCPPorts = [ 6725 ];
- services.prometheus.alertmanagerWebhookLogger.enable = true;
- };
- };
+ services.prometheus.alertmanagerWebhookLogger.enable = true;
+ };
+ };
- testScript = ''
- alertmanager.wait_for_unit("alertmanager")
- alertmanager.wait_for_open_port(9093)
- alertmanager.wait_until_succeeds("curl -s http://127.0.0.1:9093/-/ready")
- #alertmanager.wait_until_succeeds("journalctl -o cat -u alertmanager.service | grep 'version=${pkgs.prometheus-alertmanager.version}'")
+ testScript = ''
+ alertmanager.wait_for_unit("alertmanager")
+ alertmanager.wait_for_open_port(9093)
+ alertmanager.wait_until_succeeds("curl -s http://127.0.0.1:9093/-/ready")
+ #alertmanager.wait_until_succeeds("journalctl -o cat -u alertmanager.service | grep 'version=${pkgs.prometheus-alertmanager.version}'")
- logger.wait_for_unit("alertmanager-webhook-logger")
- logger.wait_for_open_port(6725)
+ logger.wait_for_unit("alertmanager-webhook-logger")
+ logger.wait_for_open_port(6725)
- prometheus.wait_for_unit("prometheus")
- prometheus.wait_for_open_port(9090)
+ prometheus.wait_for_unit("prometheus")
+ prometheus.wait_for_open_port(9090)
- prometheus.wait_until_succeeds(
- "curl -sf 'http://127.0.0.1:9090/api/v1/query?query=count(up\{job=\"alertmanager\"\}==1)' | "
- + "jq '.data.result[0].value[1]' | grep '\"1\"'"
- )
+ prometheus.wait_until_succeeds(
+ "curl -sf 'http://127.0.0.1:9090/api/v1/query?query=count(up\{job=\"alertmanager\"\}==1)' | "
+ + "jq '.data.result[0].value[1]' | grep '\"1\"'"
+ )
- prometheus.wait_until_succeeds(
- "curl -sf 'http://127.0.0.1:9090/api/v1/query?query=sum(alertmanager_build_info)%20by%20(version)' | "
- + "jq '.data.result[0].metric.version' | grep '\"${pkgs.prometheus-alertmanager.version}\"'"
- )
+ prometheus.wait_until_succeeds(
+ "curl -sf 'http://127.0.0.1:9090/api/v1/query?query=sum(alertmanager_build_info)%20by%20(version)' | "
+ + "jq '.data.result[0].metric.version' | grep '\"${pkgs.prometheus-alertmanager.version}\"'"
+ )
- prometheus.wait_until_succeeds(
- "curl -sf 'http://127.0.0.1:9090/api/v1/query?query=count(up\{job=\"node\"\}!=1)' | "
- + "jq '.data.result[0].value[1]' | grep '\"1\"'"
- )
+ prometheus.wait_until_succeeds(
+ "curl -sf 'http://127.0.0.1:9090/api/v1/query?query=count(up\{job=\"node\"\}!=1)' | "
+ + "jq '.data.result[0].value[1]' | grep '\"1\"'"
+ )
- prometheus.wait_until_succeeds(
- "curl -sf 'http://127.0.0.1:9090/api/v1/query?query=alertmanager_notifications_total\{integration=\"webhook\"\}' | "
- + "jq '.data.result[0].value[1]' | grep -v '\"0\"'"
- )
+ prometheus.wait_until_succeeds(
+ "curl -sf 'http://127.0.0.1:9090/api/v1/query?query=alertmanager_notifications_total\{integration=\"webhook\"\}' | "
+ + "jq '.data.result[0].value[1]' | grep -v '\"0\"'"
+ )
- logger.wait_until_succeeds(
- "journalctl -o cat -u alertmanager-webhook-logger.service | grep '\"alertname\":\"InstanceDown\"'"
- )
+ logger.wait_until_succeeds(
+ "journalctl -o cat -u alertmanager-webhook-logger.service | grep '\"alertname\":\"InstanceDown\"'"
+ )
- logger.log(logger.succeed("systemd-analyze security alertmanager-webhook-logger.service | grep -v '✓'"))
+ logger.log(logger.succeed("systemd-analyze security alertmanager-webhook-logger.service | grep -v '✓'"))
- alertmanager.log(alertmanager.succeed("systemd-analyze security alertmanager.service | grep -v '✓'"))
- '';
- }
-)
+ alertmanager.log(alertmanager.succeed("systemd-analyze security alertmanager.service | grep -v '✓'"))
+ '';
+}
diff --git a/nixos/tests/prometheus/config-reload.nix b/nixos/tests/prometheus/config-reload.nix
index 60f5bc6a1d23..bb20d249fe9c 100644
--- a/nixos/tests/prometheus/config-reload.nix
+++ b/nixos/tests/prometheus/config-reload.nix
@@ -1,120 +1,108 @@
-import ../make-test-python.nix (
- { lib, pkgs, ... }:
+{
+ name = "prometheus-config-reload";
- {
- name = "prometheus-config-reload";
+ nodes = {
+ prometheus =
+ { config, pkgs, ... }:
+ {
+ environment.systemPackages = [ pkgs.jq ];
- nodes = {
- prometheus =
- { config, pkgs, ... }:
- {
- environment.systemPackages = [ pkgs.jq ];
+ networking.firewall.allowedTCPPorts = [ config.services.prometheus.port ];
- networking.firewall.allowedTCPPorts = [ config.services.prometheus.port ];
+ services.prometheus = {
+ enable = true;
+ enableReload = true;
+ globalConfig.scrape_interval = "2s";
+ scrapeConfigs = [
+ {
+ job_name = "prometheus";
+ static_configs = [ { targets = [ "prometheus:${toString config.services.prometheus.port}" ]; } ];
+ }
+ ];
+ };
- services.prometheus = {
- enable = true;
- enableReload = true;
- globalConfig.scrape_interval = "2s";
- scrapeConfigs = [
- {
- job_name = "prometheus";
- static_configs = [
+ specialisation = {
+ "prometheus-config-change" = {
+ configuration = {
+ environment.systemPackages = [ pkgs.yq ];
+
+ # This configuration just adds a new prometheus job
+ # to scrape the node_exporter metrics of the s3 machine.
+ services.prometheus = {
+ scrapeConfigs = [
{
- targets = [
- "prometheus:${toString config.services.prometheus.port}"
+ job_name = "node";
+ static_configs = [
+ { targets = [ "node:${toString config.services.prometheus.exporters.node.port}" ]; }
];
}
];
- }
- ];
- };
-
- specialisation = {
- "prometheus-config-change" = {
- configuration = {
- environment.systemPackages = [ pkgs.yq ];
-
- # This configuration just adds a new prometheus job
- # to scrape the node_exporter metrics of the s3 machine.
- services.prometheus = {
- scrapeConfigs = [
- {
- job_name = "node";
- static_configs = [
- {
- targets = [ "node:${toString config.services.prometheus.exporters.node.port}" ];
- }
- ];
- }
- ];
- };
};
};
};
};
- };
+ };
+ };
- testScript = ''
- prometheus.wait_for_unit("prometheus")
- prometheus.wait_for_open_port(9090)
+ testScript = ''
+ prometheus.wait_for_unit("prometheus")
+ prometheus.wait_for_open_port(9090)
- # Check if switching to a NixOS configuration that changes the prometheus
- # configuration reloads (instead of restarts) prometheus before the switch
- # finishes successfully:
- with subtest("config change reloads prometheus"):
- import json
- # We check if prometheus has finished reloading by looking for the message
- # "Completed loading of configuration file" in the journal between the start
- # and finish of switching to the new NixOS configuration.
- #
- # To mark the start we record the journal cursor before starting the switch:
- cursor_before_switching = json.loads(
- prometheus.succeed("journalctl -n1 -o json --output-fields=__CURSOR")
- )["__CURSOR"]
+ # Check if switching to a NixOS configuration that changes the prometheus
+ # configuration reloads (instead of restarts) prometheus before the switch
+ # finishes successfully:
+ with subtest("config change reloads prometheus"):
+ import json
+ # We check if prometheus has finished reloading by looking for the message
+ # "Completed loading of configuration file" in the journal between the start
+ # and finish of switching to the new NixOS configuration.
+ #
+ # To mark the start we record the journal cursor before starting the switch:
+ cursor_before_switching = json.loads(
+ prometheus.succeed("journalctl -n1 -o json --output-fields=__CURSOR")
+ )["__CURSOR"]
- # Now we switch:
- prometheus_config_change = prometheus.succeed(
- "readlink /run/current-system/specialisation/prometheus-config-change"
- ).strip()
- prometheus.succeed(prometheus_config_change + "/bin/switch-to-configuration test")
+ # Now we switch:
+ prometheus_config_change = prometheus.succeed(
+ "readlink /run/current-system/specialisation/prometheus-config-change"
+ ).strip()
+ prometheus.succeed(prometheus_config_change + "/bin/switch-to-configuration test")
- # Next we retrieve all logs since the start of switching:
- logs_after_starting_switching = prometheus.succeed(
- """
- journalctl --after-cursor='{cursor_before_switching}' -o json --output-fields=MESSAGE
- """.format(
- cursor_before_switching=cursor_before_switching
- )
- )
-
- # Finally we check if the message "Completed loading of configuration file"
- # occurs before the "finished switching to system configuration" message:
- finished_switching_msg = (
- "finished switching to system configuration " + prometheus_config_change
- )
- reloaded_before_switching_finished = False
- finished_switching = False
- for log_line in logs_after_starting_switching.split("\n"):
- msg = json.loads(log_line)["MESSAGE"]
- if "Completed loading of configuration file" in msg:
- reloaded_before_switching_finished = True
- if msg == finished_switching_msg:
- finished_switching = True
- break
-
- assert reloaded_before_switching_finished
- assert finished_switching
-
- # Check if the reloaded config includes the new node job:
- prometheus.succeed(
+ # Next we retrieve all logs since the start of switching:
+ logs_after_starting_switching = prometheus.succeed(
"""
- curl -sf http://127.0.0.1:9090/api/v1/status/config \
- | jq -r .data.yaml \
- | yq '.scrape_configs | any(.job_name == "node")' \
- | grep true
- """
- )
- '';
- }
-)
+ journalctl --after-cursor='{cursor_before_switching}' -o json --output-fields=MESSAGE
+ """.format(
+ cursor_before_switching=cursor_before_switching
+ )
+ )
+
+ # Finally we check if the message "Completed loading of configuration file"
+ # occurs before the "finished switching to system configuration" message:
+ finished_switching_msg = (
+ "finished switching to system configuration " + prometheus_config_change
+ )
+ reloaded_before_switching_finished = False
+ finished_switching = False
+ for log_line in logs_after_starting_switching.split("\n"):
+ msg = json.loads(log_line)["MESSAGE"]
+ if "Completed loading of configuration file" in msg:
+ reloaded_before_switching_finished = True
+ if msg == finished_switching_msg:
+ finished_switching = True
+ break
+
+ assert reloaded_before_switching_finished
+ assert finished_switching
+
+ # Check if the reloaded config includes the new node job:
+ prometheus.succeed(
+ """
+ curl -sf http://127.0.0.1:9090/api/v1/status/config \
+ | jq -r .data.yaml \
+ | yq '.scrape_configs | any(.job_name == "node")' \
+ | grep true
+ """
+ )
+ '';
+}
diff --git a/nixos/tests/prometheus/default.nix b/nixos/tests/prometheus/default.nix
index ea6c61c85b80..53cfc80e8f64 100644
--- a/nixos/tests/prometheus/default.nix
+++ b/nixos/tests/prometheus/default.nix
@@ -1,14 +1,11 @@
-{
- system ? builtins.currentSystem,
- config ? { },
- pkgs ? import ../../.. { inherit system config; },
-}:
+{ runTest }:
{
- alertmanager = import ./alertmanager.nix { inherit system pkgs; };
- config-reload = import ./config-reload.nix { inherit system pkgs; };
- federation = import ./federation.nix { inherit system pkgs; };
- prometheus-pair = import ./prometheus-pair.nix { inherit system pkgs; };
- pushgateway = import ./pushgateway.nix { inherit system pkgs; };
- remote-write = import ./remote-write.nix { inherit system pkgs; };
+ alertmanager = runTest ./alertmanager.nix;
+ alertmanager-ntfy = runTest ./alertmanager-ntfy.nix;
+ config-reload = runTest ./config-reload.nix;
+ federation = runTest ./federation.nix;
+ prometheus-pair = runTest ./prometheus-pair.nix;
+ pushgateway = runTest ./pushgateway.nix;
+ remote-write = runTest ./remote-write.nix;
}
diff --git a/nixos/tests/prometheus/federation.nix b/nixos/tests/prometheus/federation.nix
index 15f84b2d88af..e81418dfba93 100644
--- a/nixos/tests/prometheus/federation.nix
+++ b/nixos/tests/prometheus/federation.nix
@@ -1,227 +1,203 @@
-import ../make-test-python.nix (
- { lib, pkgs, ... }:
+{
+ name = "prometheus-federation";
- {
- name = "prometheus-federation";
+ nodes = {
+ global1 =
+ { config, pkgs, ... }:
+ {
+ environment.systemPackages = [ pkgs.jq ];
- nodes = {
- global1 =
- { config, pkgs, ... }:
- {
- environment.systemPackages = [ pkgs.jq ];
+ networking.firewall.allowedTCPPorts = [ config.services.prometheus.port ];
- networking.firewall.allowedTCPPorts = [ config.services.prometheus.port ];
+ services.prometheus = {
+ enable = true;
+ globalConfig.scrape_interval = "2s";
- services.prometheus = {
- enable = true;
- globalConfig.scrape_interval = "2s";
+ scrapeConfigs = [
+ {
+ job_name = "federate";
+ honor_labels = true;
+ metrics_path = "/federate";
- scrapeConfigs = [
- {
- job_name = "federate";
- honor_labels = true;
- metrics_path = "/federate";
+ params = {
+ "match[]" = [
+ "{job=\"node\"}"
+ "{job=\"prometheus\"}"
+ ];
+ };
- params = {
- "match[]" = [
- "{job=\"node\"}"
- "{job=\"prometheus\"}"
+ static_configs = [
+ {
+ targets = [
+ "prometheus1:${toString config.services.prometheus.port}"
+ "prometheus2:${toString config.services.prometheus.port}"
];
- };
-
- static_configs = [
- {
- targets = [
- "prometheus1:${toString config.services.prometheus.port}"
- "prometheus2:${toString config.services.prometheus.port}"
- ];
- }
- ];
- }
- {
- job_name = "prometheus";
- static_configs = [
- {
- targets = [
- "global1:${toString config.services.prometheus.port}"
- "global2:${toString config.services.prometheus.port}"
- ];
- }
- ];
- }
- ];
- };
- };
-
- global2 =
- { config, pkgs, ... }:
- {
- environment.systemPackages = [ pkgs.jq ];
-
- networking.firewall.allowedTCPPorts = [ config.services.prometheus.port ];
-
- services.prometheus = {
- enable = true;
- globalConfig.scrape_interval = "2s";
-
- scrapeConfigs = [
- {
- job_name = "federate";
- honor_labels = true;
- metrics_path = "/federate";
-
- params = {
- "match[]" = [
- "{job=\"node\"}"
- "{job=\"prometheus\"}"
+ }
+ ];
+ }
+ {
+ job_name = "prometheus";
+ static_configs = [
+ {
+ targets = [
+ "global1:${toString config.services.prometheus.port}"
+ "global2:${toString config.services.prometheus.port}"
];
- };
-
- static_configs = [
- {
- targets = [
- "prometheus1:${toString config.services.prometheus.port}"
- "prometheus2:${toString config.services.prometheus.port}"
- ];
- }
- ];
- }
- {
- job_name = "prometheus";
- static_configs = [
- {
- targets = [
- "global1:${toString config.services.prometheus.port}"
- "global2:${toString config.services.prometheus.port}"
- ];
- }
- ];
- }
- ];
- };
+ }
+ ];
+ }
+ ];
};
+ };
- prometheus1 =
- { config, pkgs, ... }:
- {
- environment.systemPackages = [ pkgs.jq ];
+ global2 =
+ { config, pkgs, ... }:
+ {
+ environment.systemPackages = [ pkgs.jq ];
- networking.firewall.allowedTCPPorts = [ config.services.prometheus.port ];
+ networking.firewall.allowedTCPPorts = [ config.services.prometheus.port ];
- services.prometheus = {
- enable = true;
- globalConfig.scrape_interval = "2s";
+ services.prometheus = {
+ enable = true;
+ globalConfig.scrape_interval = "2s";
- scrapeConfigs = [
- {
- job_name = "node";
- static_configs = [
- {
- targets = [
- "node1:${toString config.services.prometheus.exporters.node.port}"
- ];
- }
+ scrapeConfigs = [
+ {
+ job_name = "federate";
+ honor_labels = true;
+ metrics_path = "/federate";
+
+ params = {
+ "match[]" = [
+ "{job=\"node\"}"
+ "{job=\"prometheus\"}"
];
- }
- {
- job_name = "prometheus";
- static_configs = [
- {
- targets = [
- "prometheus1:${toString config.services.prometheus.port}"
- ];
- }
- ];
- }
- ];
- };
+ };
+
+ static_configs = [
+ {
+ targets = [
+ "prometheus1:${toString config.services.prometheus.port}"
+ "prometheus2:${toString config.services.prometheus.port}"
+ ];
+ }
+ ];
+ }
+ {
+ job_name = "prometheus";
+ static_configs = [
+ {
+ targets = [
+ "global1:${toString config.services.prometheus.port}"
+ "global2:${toString config.services.prometheus.port}"
+ ];
+ }
+ ];
+ }
+ ];
};
+ };
- prometheus2 =
- { config, pkgs, ... }:
- {
- environment.systemPackages = [ pkgs.jq ];
+ prometheus1 =
+ { config, pkgs, ... }:
+ {
+ environment.systemPackages = [ pkgs.jq ];
- networking.firewall.allowedTCPPorts = [ config.services.prometheus.port ];
+ networking.firewall.allowedTCPPorts = [ config.services.prometheus.port ];
- services.prometheus = {
- enable = true;
- globalConfig.scrape_interval = "2s";
+ services.prometheus = {
+ enable = true;
+ globalConfig.scrape_interval = "2s";
- scrapeConfigs = [
- {
- job_name = "node";
- static_configs = [
- {
- targets = [
- "node2:${toString config.services.prometheus.exporters.node.port}"
- ];
- }
- ];
- }
- {
- job_name = "prometheus";
- static_configs = [
- {
- targets = [
- "prometheus2:${toString config.services.prometheus.port}"
- ];
- }
- ];
- }
- ];
- };
+ scrapeConfigs = [
+ {
+ job_name = "node";
+ static_configs = [
+ { targets = [ "node1:${toString config.services.prometheus.exporters.node.port}" ]; }
+ ];
+ }
+ {
+ job_name = "prometheus";
+ static_configs = [ { targets = [ "prometheus1:${toString config.services.prometheus.port}" ]; } ];
+ }
+ ];
};
+ };
- node1 =
- { config, pkgs, ... }:
- {
- services.prometheus.exporters.node = {
- enable = true;
- openFirewall = true;
- };
+ prometheus2 =
+ { config, pkgs, ... }:
+ {
+ environment.systemPackages = [ pkgs.jq ];
+
+ networking.firewall.allowedTCPPorts = [ config.services.prometheus.port ];
+
+ services.prometheus = {
+ enable = true;
+ globalConfig.scrape_interval = "2s";
+
+ scrapeConfigs = [
+ {
+ job_name = "node";
+ static_configs = [
+ { targets = [ "node2:${toString config.services.prometheus.exporters.node.port}" ]; }
+ ];
+ }
+ {
+ job_name = "prometheus";
+ static_configs = [ { targets = [ "prometheus2:${toString config.services.prometheus.port}" ]; } ];
+ }
+ ];
};
+ };
- node2 =
- { config, pkgs, ... }:
- {
- services.prometheus.exporters.node = {
- enable = true;
- openFirewall = true;
- };
+ node1 =
+ { config, pkgs, ... }:
+ {
+ services.prometheus.exporters.node = {
+ enable = true;
+ openFirewall = true;
};
- };
+ };
- testScript = ''
- for machine in node1, node2:
- machine.wait_for_unit("prometheus-node-exporter")
- machine.wait_for_open_port(9100)
+ node2 =
+ { config, pkgs, ... }:
+ {
+ services.prometheus.exporters.node = {
+ enable = true;
+ openFirewall = true;
+ };
+ };
+ };
- for machine in prometheus1, prometheus2, global1, global2:
- machine.wait_for_unit("prometheus")
- machine.wait_for_open_port(9090)
+ testScript = ''
+ for machine in node1, node2:
+ machine.wait_for_unit("prometheus-node-exporter")
+ machine.wait_for_open_port(9100)
- # Verify both servers got the same data from the exporter
- for machine in prometheus1, prometheus2:
- machine.wait_until_succeeds(
- "curl -sf 'http://127.0.0.1:9090/api/v1/query?query=count(up\{job=\"node\"\})' | "
- + "jq '.data.result[0].value[1]' | grep '\"1\"'"
- )
- machine.wait_until_succeeds(
- "curl -sf 'http://127.0.0.1:9090/api/v1/query?query=count(prometheus_build_info)' | "
- + "jq '.data.result[0].value[1]' | grep '\"1\"'"
- )
+ for machine in prometheus1, prometheus2, global1, global2:
+ machine.wait_for_unit("prometheus")
+ machine.wait_for_open_port(9090)
- for machine in global1, global2:
- machine.wait_until_succeeds(
- "curl -sf 'http://127.0.0.1:9090/api/v1/query?query=count(up\{job=\"node\"\})' | "
- + "jq '.data.result[0].value[1]' | grep '\"2\"'"
- )
+ # Verify both servers got the same data from the exporter
+ for machine in prometheus1, prometheus2:
+ machine.wait_until_succeeds(
+ "curl -sf 'http://127.0.0.1:9090/api/v1/query?query=count(up\{job=\"node\"\})' | "
+ + "jq '.data.result[0].value[1]' | grep '\"1\"'"
+ )
+ machine.wait_until_succeeds(
+ "curl -sf 'http://127.0.0.1:9090/api/v1/query?query=count(prometheus_build_info)' | "
+ + "jq '.data.result[0].value[1]' | grep '\"1\"'"
+ )
- machine.wait_until_succeeds(
- "curl -sf 'http://127.0.0.1:9090/api/v1/query?query=count(prometheus_build_info)' | "
- + "jq '.data.result[0].value[1]' | grep '\"4\"'"
- )
- '';
- }
-)
+ for machine in global1, global2:
+ machine.wait_until_succeeds(
+ "curl -sf 'http://127.0.0.1:9090/api/v1/query?query=count(up\{job=\"node\"\})' | "
+ + "jq '.data.result[0].value[1]' | grep '\"2\"'"
+ )
+
+ machine.wait_until_succeeds(
+ "curl -sf 'http://127.0.0.1:9090/api/v1/query?query=count(prometheus_build_info)' | "
+ + "jq '.data.result[0].value[1]' | grep '\"4\"'"
+ )
+ '';
+}
diff --git a/nixos/tests/prometheus/prometheus-pair.nix b/nixos/tests/prometheus/prometheus-pair.nix
index 98860fa6bf95..6d8d5900480f 100644
--- a/nixos/tests/prometheus/prometheus-pair.nix
+++ b/nixos/tests/prometheus/prometheus-pair.nix
@@ -1,93 +1,91 @@
-import ../make-test-python.nix (
- { lib, pkgs, ... }:
+{ pkgs, ... }:
- {
- name = "prometheus-pair";
+{
+ name = "prometheus-pair";
- nodes = {
- prometheus1 =
- { config, pkgs, ... }:
- {
- environment.systemPackages = [ pkgs.jq ];
+ nodes = {
+ prometheus1 =
+ { config, pkgs, ... }:
+ {
+ environment.systemPackages = [ pkgs.jq ];
- networking.firewall.allowedTCPPorts = [ config.services.prometheus.port ];
+ networking.firewall.allowedTCPPorts = [ config.services.prometheus.port ];
- services.prometheus = {
- enable = true;
- globalConfig.scrape_interval = "2s";
- scrapeConfigs = [
- {
- job_name = "prometheus";
- static_configs = [
- {
- targets = [
- "prometheus1:${toString config.services.prometheus.port}"
- "prometheus2:${toString config.services.prometheus.port}"
- ];
- }
- ];
- }
- ];
- };
+ services.prometheus = {
+ enable = true;
+ globalConfig.scrape_interval = "2s";
+ scrapeConfigs = [
+ {
+ job_name = "prometheus";
+ static_configs = [
+ {
+ targets = [
+ "prometheus1:${toString config.services.prometheus.port}"
+ "prometheus2:${toString config.services.prometheus.port}"
+ ];
+ }
+ ];
+ }
+ ];
};
+ };
- prometheus2 =
- { config, pkgs, ... }:
- {
- environment.systemPackages = [ pkgs.jq ];
+ prometheus2 =
+ { config, pkgs, ... }:
+ {
+ environment.systemPackages = [ pkgs.jq ];
- networking.firewall.allowedTCPPorts = [ config.services.prometheus.port ];
+ networking.firewall.allowedTCPPorts = [ config.services.prometheus.port ];
- services.prometheus = {
- enable = true;
- globalConfig.scrape_interval = "2s";
- scrapeConfigs = [
- {
- job_name = "prometheus";
- static_configs = [
- {
- targets = [
- "prometheus1:${toString config.services.prometheus.port}"
- "prometheus2:${toString config.services.prometheus.port}"
- ];
- }
- ];
- }
- ];
- };
+ services.prometheus = {
+ enable = true;
+ globalConfig.scrape_interval = "2s";
+ scrapeConfigs = [
+ {
+ job_name = "prometheus";
+ static_configs = [
+ {
+ targets = [
+ "prometheus1:${toString config.services.prometheus.port}"
+ "prometheus2:${toString config.services.prometheus.port}"
+ ];
+ }
+ ];
+ }
+ ];
};
- };
+ };
+ };
- testScript = ''
- for machine in prometheus1, prometheus2:
- machine.wait_for_unit("prometheus")
- machine.wait_for_open_port(9090)
- machine.wait_until_succeeds("journalctl -o cat -u prometheus.service | grep 'version=${pkgs.prometheus.version}'")
- machine.wait_until_succeeds("curl -sSf http://localhost:9090/-/healthy")
+ testScript = ''
+ for machine in prometheus1, prometheus2:
+ machine.wait_for_unit("prometheus")
+ machine.wait_for_open_port(9090)
+ machine.wait_until_succeeds("journalctl -o cat -u prometheus.service | grep 'version=${pkgs.prometheus.version}'")
+ machine.wait_until_succeeds("curl -sSf http://localhost:9090/-/healthy")
- # Prometheii ready - run some queries
- for machine in prometheus1, prometheus2:
- machine.wait_until_succeeds(
- "curl -sf 'http://127.0.0.1:9090/api/v1/query?query=prometheus_build_info\{instance=\"prometheus1:9090\",version=\"${pkgs.prometheus.version}\"\}' | "
- + "jq '.data.result[0].value[1]' | grep '\"1\"'"
- )
+ # Prometheii ready - run some queries
+ for machine in prometheus1, prometheus2:
+ machine.wait_until_succeeds(
+ "curl -sf 'http://127.0.0.1:9090/api/v1/query?query=prometheus_build_info\{instance=\"prometheus1:9090\",version=\"${pkgs.prometheus.version}\"\}' | "
+ + "jq '.data.result[0].value[1]' | grep '\"1\"'"
+ )
- machine.wait_until_succeeds(
- "curl -sf 'http://127.0.0.1:9090/api/v1/query?query=prometheus_build_info\{instance=\"prometheus1:9090\"\}' | "
- + "jq '.data.result[0].value[1]' | grep '\"1\"'"
- )
+ machine.wait_until_succeeds(
+ "curl -sf 'http://127.0.0.1:9090/api/v1/query?query=prometheus_build_info\{instance=\"prometheus1:9090\"\}' | "
+ + "jq '.data.result[0].value[1]' | grep '\"1\"'"
+ )
- machine.wait_until_succeeds(
- "curl -sf 'http://127.0.0.1:9090/api/v1/query?query=sum(prometheus_build_info)%20by%20(version)' | "
- + "jq '.data.result[0].metric.version' | grep '\"${pkgs.prometheus.version}\"'"
- )
+ machine.wait_until_succeeds(
+ "curl -sf 'http://127.0.0.1:9090/api/v1/query?query=sum(prometheus_build_info)%20by%20(version)' | "
+ + "jq '.data.result[0].metric.version' | grep '\"${pkgs.prometheus.version}\"'"
+ )
- machine.wait_until_succeeds(
- "curl -sf 'http://127.0.0.1:9090/api/v1/query?query=sum(prometheus_build_info)%20by%20(version)' | "
- + "jq '.data.result[0].value[1]' | grep '\"2\"'"
- )
+ machine.wait_until_succeeds(
+ "curl -sf 'http://127.0.0.1:9090/api/v1/query?query=sum(prometheus_build_info)%20by%20(version)' | "
+ + "jq '.data.result[0].value[1]' | grep '\"2\"'"
+ )
- prometheus1.log(prometheus1.succeed("systemd-analyze security prometheus.service | grep -v '✓'"))
- '';
- }
-)
+ prometheus1.log(prometheus1.succeed("systemd-analyze security prometheus.service | grep -v '✓'"))
+ '';
+}
diff --git a/nixos/tests/prometheus/pushgateway.nix b/nixos/tests/prometheus/pushgateway.nix
index fb8b6c091aa1..6b13447085d9 100644
--- a/nixos/tests/prometheus/pushgateway.nix
+++ b/nixos/tests/prometheus/pushgateway.nix
@@ -1,102 +1,91 @@
-import ../make-test-python.nix (
- { lib, pkgs, ... }:
+{ pkgs, ... }:
- {
- name = "prometheus-pushgateway";
+{
+ name = "prometheus-pushgateway";
- nodes = {
- prometheus =
- { config, pkgs, ... }:
- {
- environment.systemPackages = [ pkgs.jq ];
+ nodes = {
+ prometheus =
+ { config, pkgs, ... }:
+ {
+ environment.systemPackages = [ pkgs.jq ];
- networking.firewall.allowedTCPPorts = [ config.services.prometheus.port ];
+ networking.firewall.allowedTCPPorts = [ config.services.prometheus.port ];
- services.prometheus = {
- enable = true;
- globalConfig.scrape_interval = "2s";
+ services.prometheus = {
+ enable = true;
+ globalConfig.scrape_interval = "2s";
- scrapeConfigs = [
- {
- job_name = "pushgateway";
- static_configs = [
- {
- targets = [
- "pushgateway:9091"
- ];
- }
- ];
- }
- ];
- };
+ scrapeConfigs = [
+ {
+ job_name = "pushgateway";
+ static_configs = [ { targets = [ "pushgateway:9091" ]; } ];
+ }
+ ];
};
+ };
- pushgateway =
- { config, pkgs, ... }:
- {
- networking.firewall.allowedTCPPorts = [ 9091 ];
+ pushgateway =
+ { config, pkgs, ... }:
+ {
+ networking.firewall.allowedTCPPorts = [ 9091 ];
- services.prometheus.pushgateway = {
- enable = true;
- };
+ services.prometheus.pushgateway = {
+ enable = true;
};
+ };
- client =
- { config, pkgs, ... }:
- {
- };
- };
+ client = { config, pkgs, ... }: { };
+ };
- testScript = ''
- pushgateway.wait_for_unit("pushgateway")
- pushgateway.wait_for_open_port(9091)
- pushgateway.wait_until_succeeds("curl -s http://127.0.0.1:9091/-/ready")
- pushgateway.wait_until_succeeds("journalctl -o cat -u pushgateway.service | grep 'version=${pkgs.prometheus-pushgateway.version}'")
+ testScript = ''
+ pushgateway.wait_for_unit("pushgateway")
+ pushgateway.wait_for_open_port(9091)
+ pushgateway.wait_until_succeeds("curl -s http://127.0.0.1:9091/-/ready")
+ pushgateway.wait_until_succeeds("journalctl -o cat -u pushgateway.service | grep 'version=${pkgs.prometheus-pushgateway.version}'")
- prometheus.wait_for_unit("prometheus")
- prometheus.wait_for_open_port(9090)
+ prometheus.wait_for_unit("prometheus")
+ prometheus.wait_for_open_port(9090)
- prometheus.wait_until_succeeds(
- "curl -sf 'http://127.0.0.1:9090/api/v1/query?query=count(up\{job=\"pushgateway\"\})' | "
- + "jq '.data.result[0].value[1]' | grep '\"1\"'"
- )
+ prometheus.wait_until_succeeds(
+ "curl -sf 'http://127.0.0.1:9090/api/v1/query?query=count(up\{job=\"pushgateway\"\})' | "
+ + "jq '.data.result[0].value[1]' | grep '\"1\"'"
+ )
- prometheus.wait_until_succeeds(
- "curl -sf 'http://127.0.0.1:9090/api/v1/query?query=sum(pushgateway_build_info)%20by%20(version)' | "
- + "jq '.data.result[0].metric.version' | grep '\"${pkgs.prometheus-pushgateway.version}\"'"
- )
+ prometheus.wait_until_succeeds(
+ "curl -sf 'http://127.0.0.1:9090/api/v1/query?query=sum(pushgateway_build_info)%20by%20(version)' | "
+ + "jq '.data.result[0].metric.version' | grep '\"${pkgs.prometheus-pushgateway.version}\"'"
+ )
- # Add a metric and check in Prometheus
- client.wait_until_succeeds(
- "echo 'some_metric 3.14' | curl --data-binary @- http://pushgateway:9091/metrics/job/some_job"
- )
+ # Add a metric and check in Prometheus
+ client.wait_until_succeeds(
+ "echo 'some_metric 3.14' | curl --data-binary @- http://pushgateway:9091/metrics/job/some_job"
+ )
- prometheus.wait_until_succeeds(
- "curl -sf 'http://127.0.0.1:9090/api/v1/query?query=some_metric' | "
- + "jq '.data.result[0].value[1]' | grep '\"3.14\"'"
- )
+ prometheus.wait_until_succeeds(
+ "curl -sf 'http://127.0.0.1:9090/api/v1/query?query=some_metric' | "
+ + "jq '.data.result[0].value[1]' | grep '\"3.14\"'"
+ )
- prometheus.wait_until_succeeds(
- "curl -sf 'http://127.0.0.1:9090/api/v1/query?query=absent(some_metric)' | "
- + "jq '.data.result[0].value[1]' | grep 'null'"
- )
+ prometheus.wait_until_succeeds(
+ "curl -sf 'http://127.0.0.1:9090/api/v1/query?query=absent(some_metric)' | "
+ + "jq '.data.result[0].value[1]' | grep 'null'"
+ )
- # Delete the metric, check not in Prometheus
- client.wait_until_succeeds(
- "curl -X DELETE http://pushgateway:9091/metrics/job/some_job"
- )
+ # Delete the metric, check not in Prometheus
+ client.wait_until_succeeds(
+ "curl -X DELETE http://pushgateway:9091/metrics/job/some_job"
+ )
- prometheus.wait_until_fails(
- "curl -sf 'http://127.0.0.1:9090/api/v1/query?query=some_metric' | "
- + "jq '.data.result[0].value[1]' | grep '\"3.14\"'"
- )
+ prometheus.wait_until_fails(
+ "curl -sf 'http://127.0.0.1:9090/api/v1/query?query=some_metric' | "
+ + "jq '.data.result[0].value[1]' | grep '\"3.14\"'"
+ )
- prometheus.wait_until_succeeds(
- "curl -sf 'http://127.0.0.1:9090/api/v1/query?query=absent(some_metric)' | "
- + "jq '.data.result[0].value[1]' | grep '\"1\"'"
- )
+ prometheus.wait_until_succeeds(
+ "curl -sf 'http://127.0.0.1:9090/api/v1/query?query=absent(some_metric)' | "
+ + "jq '.data.result[0].value[1]' | grep '\"1\"'"
+ )
- pushgateway.log(pushgateway.succeed("systemd-analyze security pushgateway.service | grep -v '✓'"))
- '';
- }
-)
+ pushgateway.log(pushgateway.succeed("systemd-analyze security pushgateway.service | grep -v '✓'"))
+ '';
+}
diff --git a/nixos/tests/prometheus/remote-write.nix b/nixos/tests/prometheus/remote-write.nix
index 86c14901a6b0..23458c737e1a 100644
--- a/nixos/tests/prometheus/remote-write.nix
+++ b/nixos/tests/prometheus/remote-write.nix
@@ -1,81 +1,69 @@
-import ../make-test-python.nix (
- { lib, pkgs, ... }:
+{
+ name = "prometheus-remote-write";
- {
- name = "prometheus-remote-write";
+ nodes = {
+ receiver =
+ { config, pkgs, ... }:
+ {
+ environment.systemPackages = [ pkgs.jq ];
- nodes = {
- receiver =
- { config, pkgs, ... }:
- {
- environment.systemPackages = [ pkgs.jq ];
+ networking.firewall.allowedTCPPorts = [ config.services.prometheus.port ];
- networking.firewall.allowedTCPPorts = [ config.services.prometheus.port ];
+ services.prometheus = {
+ enable = true;
+ globalConfig.scrape_interval = "2s";
- services.prometheus = {
- enable = true;
- globalConfig.scrape_interval = "2s";
-
- extraFlags = [ "--web.enable-remote-write-receiver" ];
- };
+ extraFlags = [ "--web.enable-remote-write-receiver" ];
};
+ };
- prometheus =
- { config, pkgs, ... }:
- {
- environment.systemPackages = [ pkgs.jq ];
+ prometheus =
+ { config, pkgs, ... }:
+ {
+ environment.systemPackages = [ pkgs.jq ];
- networking.firewall.allowedTCPPorts = [ config.services.prometheus.port ];
+ networking.firewall.allowedTCPPorts = [ config.services.prometheus.port ];
- services.prometheus = {
- enable = true;
- globalConfig.scrape_interval = "2s";
+ services.prometheus = {
+ enable = true;
+ globalConfig.scrape_interval = "2s";
- remoteWrite = [
- {
- url = "http://receiver:9090/api/v1/write";
- }
- ];
+ remoteWrite = [ { url = "http://receiver:9090/api/v1/write"; } ];
- scrapeConfigs = [
- {
- job_name = "node";
- static_configs = [
- {
- targets = [
- "node:${toString config.services.prometheus.exporters.node.port}"
- ];
- }
- ];
- }
- ];
- };
+ scrapeConfigs = [
+ {
+ job_name = "node";
+ static_configs = [
+ { targets = [ "node:${toString config.services.prometheus.exporters.node.port}" ]; }
+ ];
+ }
+ ];
};
+ };
- node =
- { config, pkgs, ... }:
- {
- services.prometheus.exporters.node = {
- enable = true;
- openFirewall = true;
- };
+ node =
+ { config, pkgs, ... }:
+ {
+ services.prometheus.exporters.node = {
+ enable = true;
+ openFirewall = true;
};
- };
+ };
+ };
- testScript = ''
- node.wait_for_unit("prometheus-node-exporter")
- node.wait_for_open_port(9100)
+ testScript = ''
+ node.wait_for_unit("prometheus-node-exporter")
+ node.wait_for_open_port(9100)
- for machine in prometheus, receiver:
- machine.wait_for_unit("prometheus")
- machine.wait_for_open_port(9090)
+ for machine in prometheus, receiver:
+ machine.wait_for_unit("prometheus")
+ machine.wait_for_open_port(9090)
- # Verify both servers got the same data from the exporter
- for machine in prometheus, receiver:
- machine.wait_until_succeeds(
- "curl -sf 'http://127.0.0.1:9090/api/v1/query?query=node_exporter_build_info\{instance=\"node:9100\"\}' | "
- + "jq '.data.result[0].value[1]' | grep '\"1\"'"
- )
- '';
- }
-)
+ # Verify both servers got the same data from the exporter
+ for machine in prometheus, receiver:
+ machine.wait_until_succeeds(
+ "curl -sf 'http://127.0.0.1:9090/api/v1/query?query=node_exporter_build_info\{instance=\"node:9100\"\}' | "
+ + "jq '.data.result[0].value[1]' | grep '\"1\"'"
+ )
+ '';
+}
diff --git a/nixos/tests/rabbitmq.nix b/nixos/tests/rabbitmq.nix
index 52513b424bb4..cb6dba27a64c 100644
--- a/nixos/tests/rabbitmq.nix
+++ b/nixos/tests/rabbitmq.nix
@@ -56,7 +56,7 @@ import ./make-test-python.nix (
# The password is the plaintext that was encrypted with rabbitmqctl encode above.
machine.wait_until_succeeds(
- '${pkgs.rabbitmq-java-client}/bin/PerfTest --time 10 --uri amqp://alice:dJT8isYu6t0Xb6u56rPglSj1vK51SlNVlXfwsRxw@localhost'
+ 'echo Hello World | ${pkgs.lib.getExe pkgs.amqpcat} --producer --uri=amqp://alice:dJT8isYu6t0Xb6u56rPglSj1vK51SlNVlXfwsRxw@localhost --queue test'
)
'';
}
diff --git a/nixos/tests/reposilite.nix b/nixos/tests/reposilite.nix
new file mode 100644
index 000000000000..f66a95b47a1d
--- /dev/null
+++ b/nixos/tests/reposilite.nix
@@ -0,0 +1,53 @@
+{ lib, ... }:
+{
+ name = "reposilite";
+
+ nodes = {
+ machine =
+ { pkgs, ... }:
+ {
+ services = {
+ mysql = {
+ enable = true;
+ package = pkgs.mariadb;
+ ensureDatabases = [ "reposilite" ];
+ initialScript = pkgs.writeText "reposilite-test-db-init" ''
+ CREATE USER 'reposilite'@'localhost' IDENTIFIED BY 'ReposiliteDBPass';
+ GRANT ALL PRIVILEGES ON reposilite.* TO 'reposilite'@'localhost';
+ FLUSH PRIVILEGES;
+ '';
+ };
+
+ reposilite = {
+ enable = true;
+ plugins = with pkgs.reposilitePlugins; [
+ checksum
+ groovy
+ ];
+ extraArgs = [
+ "--token"
+ "test:SuperSecretTestToken"
+ ];
+ database = {
+ type = "mariadb";
+ passwordFile = "/run/reposiliteDbPass";
+ };
+ settings.port = 8080;
+ };
+ };
+ };
+ };
+
+ testScript = ''
+ machine.start()
+
+ machine.execute("echo \"ReposiliteDBPass\" > /run/reposiliteDbPass && chmod 600 /run/reposiliteDbPass && chown reposilite:reposilite /run/reposiliteDbPass")
+ machine.wait_for_unit("reposilite.service")
+ machine.wait_for_open_port(8080)
+
+ machine.fail("curl -Sf localhost:8080/api/auth/me")
+ machine.succeed("curl -Sfu test:SuperSecretTestToken localhost:8080/api/auth/me")
+ '';
+
+ meta.maintainers = [ lib.maintainers.uku3lig ];
+}
diff --git a/nixos/tests/rsyncd.nix b/nixos/tests/rsyncd.nix
index b75b3e011143..9935c0e6c6bf 100644
--- a/nixos/tests/rsyncd.nix
+++ b/nixos/tests/rsyncd.nix
@@ -15,13 +15,15 @@ import ./make-test-python.nix (
enable = true;
inherit socketActivated;
settings = {
- global = {
+ globalSection = {
"reverse lookup" = false;
"forward lookup" = false;
};
- tmp = {
- path = "/nix/store";
- comment = "test module";
+ sections = {
+ tmp = {
+ path = "/nix/store";
+ comment = "test module";
+ };
};
};
};
diff --git a/nixos/tests/rush.nix b/nixos/tests/rush.nix
new file mode 100644
index 000000000000..0fcd87d15f58
--- /dev/null
+++ b/nixos/tests/rush.nix
@@ -0,0 +1,88 @@
+{ pkgs, ... }:
+let
+ inherit (import ./ssh-keys.nix pkgs) snakeOilEd25519PrivateKey snakeOilEd25519PublicKey;
+ username = "nix-remote-builder";
+in
+{
+ name = "rush";
+ meta = { inherit (pkgs.rush.meta) maintainers platforms; };
+
+ nodes = {
+ client =
+ { ... }:
+ {
+ nix.settings.extra-experimental-features = [ "nix-command" ];
+ };
+
+ server =
+ { config, ... }:
+ {
+ nix.settings.trusted-users = [ "${username}" ];
+
+ programs.rush = {
+ enable = true;
+ global = "debug 1";
+
+ rules = {
+ daemon = ''
+ match $# == 2
+ match $0 == "nix-daemon"
+ match $1 == "--stdio"
+ match $user == "${username}"
+ chdir "${config.nix.package}/bin"
+ '';
+
+ whoami = ''
+ match $# == 1
+ match $0 == "whoami"
+ match $user == "${username}"
+ chdir "${dirOf config.environment.usrbinenv}"
+ '';
+ };
+ };
+
+ services.openssh = {
+ enable = true;
+
+ extraConfig = ''
+ Match User ${username}
+ AllowAgentForwarding no
+ AllowTcpForwarding no
+ PermitTTY no
+ PermitTunnel no
+ X11Forwarding no
+ Match All
+ '';
+ };
+
+ users = {
+ groups."${username}" = { };
+
+ users."${username}" = {
+ inherit (config.programs.rush) shell;
+ group = "${username}";
+ isSystemUser = true;
+ openssh.authorizedKeys.keys = [ snakeOilEd25519PublicKey ];
+ };
+ };
+ };
+ };
+
+ testScript = ''
+ start_all()
+
+ client.succeed("mkdir -m 700 /root/.ssh")
+ client.succeed("cat '${snakeOilEd25519PrivateKey}' | tee /root/.ssh/id_ed25519")
+ client.succeed("chmod 600 /root/.ssh/id_ed25519")
+
+ server.wait_for_unit("sshd")
+
+ client.succeed("ssh-keyscan -H server | tee -a /root/.ssh/known_hosts")
+
+ client.succeed("ssh ${username}@server -- whoami")
+ client.succeed("nix store info --store 'ssh-ng://${username}@server'")
+
+ client.fail("ssh ${username}@server -- date")
+ client.fail("nix store info --store 'ssh://${username}@server'")
+ '';
+}
diff --git a/nixos/tests/samba.nix b/nixos/tests/samba.nix
index 96f63730b613..b9f2f1384559 100644
--- a/nixos/tests/samba.nix
+++ b/nixos/tests/samba.nix
@@ -1,50 +1,48 @@
-import ./make-test-python.nix (
- { pkgs, lib, ... }:
- {
- name = "samba";
+{ lib, ... }:
+{
+ name = "samba";
- meta.maintainers = [ lib.maintainers.anthonyroussel ];
+ meta.maintainers = [ lib.maintainers.anthonyroussel ];
- nodes = {
- client =
- { ... }:
- {
- virtualisation.fileSystems = {
- "/public" = {
- fsType = "cifs";
- device = "//server/public";
- options = [ "guest" ];
+ nodes = {
+ client =
+ { ... }:
+ {
+ virtualisation.fileSystems = {
+ "/public" = {
+ fsType = "cifs";
+ device = "//server/public";
+ options = [ "guest" ];
+ };
+ };
+ };
+
+ server =
+ { ... }:
+ {
+ services.samba = {
+ enable = true;
+ openFirewall = true;
+ settings = {
+ "public" = {
+ "path" = "/public";
+ "read only" = true;
+ "browseable" = "yes";
+ "guest ok" = "yes";
+ "comment" = "Public samba share.";
};
};
};
+ };
+ };
- server =
- { ... }:
- {
- services.samba = {
- enable = true;
- openFirewall = true;
- settings = {
- "public" = {
- "path" = "/public";
- "read only" = true;
- "browseable" = "yes";
- "guest ok" = "yes";
- "comment" = "Public samba share.";
- };
- };
- };
- };
- };
+ testScript = ''
+ server.start()
+ server.wait_for_unit("samba.target")
+ server.succeed("mkdir -p /public; echo bar > /public/foo")
- testScript = ''
- server.start()
- server.wait_for_unit("samba.target")
- server.succeed("mkdir -p /public; echo bar > /public/foo")
-
- client.start()
- client.wait_for_unit("remote-fs.target")
- client.succeed("[[ $(cat /public/foo) = bar ]]")
- '';
- }
-)
+ client.start()
+ client.wait_for_unit("remote-fs.target")
+ client.succeed("[[ $(cat /public/foo) = bar ]]")
+ '';
+}
diff --git a/nixos/tests/scion/freestanding-deployment/default.nix b/nixos/tests/scion/freestanding-deployment/default.nix
index ca6c7cffc30e..7dbd10c225a5 100644
--- a/nixos/tests/scion/freestanding-deployment/default.nix
+++ b/nixos/tests/scion/freestanding-deployment/default.nix
@@ -23,7 +23,7 @@ import ../../make-test-python.nix (
networkConfig.Address = "192.168.1.${toString hostId}/24";
};
environment.etc = {
- "scion/topology.json".source = ./topology${toString hostId}.json;
+ "scion/topology.json".source = ./topology + "${toString hostId}.json";
"scion/crypto/as".source = trust-root-configuration-keys + "/AS${toString hostId}";
"scion/certs/ISD42-B1-S1.trc".source = trust-root-configuration-keys + "/ISD42-B1-S1.trc";
"scion/keys/master0.key".text = "U${toString hostId}v4k23ZXjGDwDofg/Eevw==";
diff --git a/nixos/tests/scx/default.nix b/nixos/tests/scx/default.nix
new file mode 100644
index 000000000000..67627d0ed3e3
--- /dev/null
+++ b/nixos/tests/scx/default.nix
@@ -0,0 +1,42 @@
+{ pkgs, ... }:
+
+{
+ name = "scx_full";
+ meta = {
+ inherit (pkgs.scx.full.meta) maintainers;
+ };
+
+ nodes.machine = {
+ boot.kernelPackages = pkgs.linuxPackages_latest;
+ services.scx.enable = true;
+
+ specialisation = {
+ bpfland.configuration.services.scx.scheduler = "scx_bpfland";
+ central.configuration.services.scx.scheduler = "scx_central";
+ lavd.configuration.services.scx.scheduler = "scx_lavd";
+ rlfifo.configuration.services.scx.scheduler = "scx_rlfifo";
+ rustland.configuration.services.scx.scheduler = "scx_rustland";
+ rusty.configuration.services.scx.scheduler = "scx_rusty";
+ };
+ };
+
+ testScript = ''
+ specialisation = [
+ "bpfland",
+ "central",
+ "lavd",
+ "rlfifo",
+ "rustland",
+ "rusty"
+ ]
+
+ def activate_specialisation(name: str):
+ machine.succeed(f"/run/booted-system/specialisation/{name}/bin/switch-to-configuration test >&2")
+
+ for sched in specialisation:
+ with subtest(f"{sched}"):
+ activate_specialisation(sched)
+ machine.succeed("systemctl restart scx.service")
+ machine.succeed(f"ps -U root -u root u | grep scx_{sched}")
+ '';
+}
diff --git a/nixos/tests/send.nix b/nixos/tests/send.nix
index b02f083fef9f..fb0021ce0b78 100644
--- a/nixos/tests/send.nix
+++ b/nixos/tests/send.nix
@@ -1,9 +1,16 @@
-{ lib, pkgs, ... }:
+{
+ lib,
+ pkgs,
+ ...
+}:
{
name = "send";
meta = {
- maintainers = with lib.maintainers; [ moraxyc ];
+ maintainers = with lib.maintainers; [
+ moraxyc
+ MrSom3body
+ ];
};
nodes.machine =
diff --git a/nixos/tests/shadps4.nix b/nixos/tests/shadps4.nix
index a41f952d6cb8..71d056cf001b 100644
--- a/nixos/tests/shadps4.nix
+++ b/nixos/tests/shadps4.nix
@@ -3,7 +3,6 @@
name = "shadps4-openorbis-example";
meta = {
inherit (pkgs.shadps4.meta) maintainers;
- platforms = lib.intersectLists lib.platforms.linux pkgs.shadps4.meta.platforms;
};
nodes.machine =
diff --git a/nixos/tests/signal-desktop.nix b/nixos/tests/signal-desktop.nix
index 22b21f4bcecf..eb9f7b4f5b58 100644
--- a/nixos/tests/signal-desktop.nix
+++ b/nixos/tests/signal-desktop.nix
@@ -1,82 +1,79 @@
-import ./make-test-python.nix (
- { pkgs, ... }:
+{ pkgs, ... }:
+let
+ sqlcipher-signal = pkgs.writeShellScriptBin "sqlcipher" ''
+ set -eu
- let
- sqlcipher-signal = pkgs.writeShellScriptBin "sqlcipher" ''
- set -eu
+ readonly CFG=~/.config/Signal/config.json
+ readonly KEY="$(${pkgs.jq}/bin/jq --raw-output '.key' $CFG)"
+ readonly DB="$1"
+ readonly SQL="SELECT * FROM sqlite_master where type='table'"
+ ${pkgs.sqlcipher}/bin/sqlcipher "$DB" "PRAGMA key = \"x'$KEY'\"; $SQL"
+ '';
+in
+{
+ name = "signal-desktop";
+ meta = with pkgs.lib.maintainers; {
+ maintainers = [
+ flokli
+ primeos
+ ];
+ };
- readonly CFG=~/.config/Signal/config.json
- readonly KEY="$(${pkgs.jq}/bin/jq --raw-output '.key' $CFG)"
- readonly DB="$1"
- readonly SQL="SELECT * FROM sqlite_master where type='table'"
- ${pkgs.sqlcipher}/bin/sqlcipher "$DB" "PRAGMA key = \"x'$KEY'\"; $SQL"
- '';
- in
- {
- name = "signal-desktop";
- meta = with pkgs.lib.maintainers; {
- maintainers = [
- flokli
- primeos
+ nodes.machine =
+ { ... }:
+
+ {
+ imports = [
+ ./common/user-account.nix
+ ./common/x11.nix
+ ];
+
+ services.xserver.enable = true;
+ test-support.displayManager.auto.user = "alice";
+ environment.systemPackages = with pkgs; [
+ signal-desktop
+ file
+ sqlite
+ sqlcipher-signal
];
};
- nodes.machine =
- { ... }:
+ enableOCR = true;
- {
- imports = [
- ./common/user-account.nix
- ./common/x11.nix
- ];
+ testScript =
+ { nodes, ... }:
+ let
+ user = nodes.machine.config.users.users.alice;
+ in
+ ''
+ start_all()
+ machine.wait_for_x()
- services.xserver.enable = true;
- test-support.displayManager.auto.user = "alice";
- environment.systemPackages = with pkgs; [
- signal-desktop
- file
- sqlite
- sqlcipher-signal
- ];
- };
+ # start signal desktop
+ machine.execute("su - alice -c signal-desktop >&2 &")
- enableOCR = true;
+ # Wait for the Signal window to appear. Since usually the tests
+ # are run sandboxed and therefore with no internet, we can not wait
+ # for the message "Link your phone ...". Nor should we wait for
+ # the "Failed to connect to server" message, because when manually
+ # running this test it will be not sandboxed.
+ machine.wait_for_text("Signal")
+ machine.wait_for_text("File Edit View Window Help")
+ machine.screenshot("signal_desktop")
- testScript =
- { nodes, ... }:
- let
- user = nodes.machine.config.users.users.alice;
- in
- ''
- start_all()
- machine.wait_for_x()
-
- # start signal desktop
- machine.execute("su - alice -c signal-desktop >&2 &")
-
- # Wait for the Signal window to appear. Since usually the tests
- # are run sandboxed and therefore with no internet, we can not wait
- # for the message "Link your phone ...". Nor should we wait for
- # the "Failed to connect to server" message, because when manually
- # running this test it will be not sandboxed.
- machine.wait_for_text("Signal")
- machine.wait_for_text("File Edit View Window Help")
- machine.screenshot("signal_desktop")
-
- # Test if the database is encrypted to prevent these issues:
- # - https://github.com/NixOS/nixpkgs/issues/108772
- # - https://github.com/NixOS/nixpkgs/pull/117555
- print(machine.succeed("su - alice -c 'file ~/.config/Signal/sql/db.sqlite'"))
- machine.fail(
- "su - alice -c 'file ~/.config/Signal/sql/db.sqlite' | grep -e SQLite -e database"
- )
- # Only SQLCipher should be able to read the encrypted DB:
- machine.fail(
- "su - alice -c 'sqlite3 ~/.config/Signal/sql/db.sqlite .tables'"
- )
- print(machine.succeed(
- "su - alice -c 'sqlcipher ~/.config/Signal/sql/db.sqlite'"
- ))
- '';
- }
-)
+ # Test if the database is encrypted to prevent these issues:
+ # - https://github.com/NixOS/nixpkgs/issues/108772
+ # - https://github.com/NixOS/nixpkgs/pull/117555
+ print(machine.succeed("su - alice -c 'file ~/.config/Signal/sql/db.sqlite'"))
+ machine.fail(
+ "su - alice -c 'file ~/.config/Signal/sql/db.sqlite' | grep -e SQLite -e database"
+ )
+ # Only SQLCipher should be able to read the encrypted DB:
+ machine.fail(
+ "su - alice -c 'sqlite3 ~/.config/Signal/sql/db.sqlite .tables'"
+ )
+ print(machine.succeed(
+ "su - alice -c 'sqlcipher ~/.config/Signal/sql/db.sqlite'"
+ ))
+ '';
+}
diff --git a/nixos/tests/sourcehut/builds.nix b/nixos/tests/sourcehut/builds.nix
index 71d5b3238453..1add972cc13f 100644
--- a/nixos/tests/sourcehut/builds.nix
+++ b/nixos/tests/sourcehut/builds.nix
@@ -49,15 +49,15 @@ import ../make-test-python.nix (
machine.wait_for_unit("multi-user.target")
with subtest("Check whether meta comes up"):
- machine.wait_for_unit("metasrht-api.service")
- machine.wait_for_unit("metasrht.service")
- machine.wait_for_unit("metasrht-webhooks.service")
+ machine.wait_for_unit("meta.sr.ht-api.service")
+ machine.wait_for_unit("meta.sr.ht.service")
+ machine.wait_for_unit("meta.sr.ht-webhooks.service")
machine.wait_for_open_port(5000)
machine.succeed("curl -sL http://localhost:5000 | grep meta.${domain}")
machine.succeed("curl -sL http://meta.${domain} | grep meta.${domain}")
with subtest("Check whether builds comes up"):
- machine.wait_for_unit("buildsrht.service")
+ machine.wait_for_unit("builds.sr.ht.service")
machine.wait_for_open_port(5002)
machine.succeed("curl -sL http://localhost:5002 | grep builds.${domain}")
#machine.wait_for_unit("buildsrht-worker.service")
diff --git a/nixos/tests/sourcehut/git.nix b/nixos/tests/sourcehut/git.nix
index 7f50752c7eb8..a06281aafbb4 100644
--- a/nixos/tests/sourcehut/git.nix
+++ b/nixos/tests/sourcehut/git.nix
@@ -63,25 +63,26 @@ import ../make-test-python.nix (
machine.wait_for_unit("sshd.service")
with subtest("Check whether meta comes up"):
- machine.wait_for_unit("metasrht-api.service")
- machine.wait_for_unit("metasrht.service")
- machine.wait_for_unit("metasrht-webhooks.service")
+ machine.wait_for_unit("meta.sr.ht-api.service")
+ machine.wait_for_unit("meta.sr.ht.service")
+ machine.wait_for_unit("meta.sr.ht-webhooks.service")
machine.wait_for_open_port(5000)
machine.succeed("curl -sL http://localhost:5000 | grep meta.${domain}")
machine.succeed("curl -sL http://meta.${domain} | grep meta.${domain}")
with subtest("Create a new user account and OAuth access key"):
- machine.succeed("echo ${userPass} | metasrht-manageuser -ps -e ${userName}@${domain}\
- -t active_paying ${userName}");
+ machine.succeed("echo ${userPass} | meta.sr.ht-manageuser -ps -e ${userName}@${domain}\
+ -t USER ${userName}");
+ cmd = "srht-gen-oauth-tok -i ${domain} -q ${userName} ${userPass}"
(_, token) = machine.execute("srht-gen-oauth-tok -i ${domain} -q ${userName} ${userPass}")
token = token.strip().replace("/", r"\\/") # Escape slashes in token before passing it to sed
machine.execute("mkdir -p ~/.config/hut/")
machine.execute("sed s/OAUTH-TOKEN/" + token + "/ ${hutConfig} > ~/.config/hut/config")
with subtest("Check whether git comes up"):
- machine.wait_for_unit("gitsrht-api.service")
- machine.wait_for_unit("gitsrht.service")
- machine.wait_for_unit("gitsrht-webhooks.service")
+ machine.wait_for_unit("git.sr.ht-api.service")
+ machine.wait_for_unit("git.sr.ht.service")
+ machine.wait_for_unit("git.sr.ht-webhooks.service")
machine.succeed("curl -sL http://git.${domain} | grep git.${domain}")
with subtest("Add an SSH key for Git access"):
@@ -95,7 +96,7 @@ import ../make-test-python.nix (
machine.execute("cd test && git add .")
machine.execute("cd test && git commit -m \"Initial commit\"")
machine.execute("cd test && git tag v0.1")
- machine.succeed("cd test && git remote add origin gitsrht@git.${domain}:~${userName}/test")
+ machine.succeed("cd test && git remote add origin git.sr.ht@git.${domain}:~${userName}/test")
machine.execute("( echo -n 'git.${domain} '; cat /etc/ssh/ssh_host_ed25519_key.pub ) > ~/.ssh/known_hosts")
machine.succeed("hut git create test")
machine.succeed("cd test && git push --tags --set-upstream origin master")
diff --git a/nixos/tests/spiped.nix b/nixos/tests/spiped.nix
index a39fc2fd722b..994ec2be02e4 100644
--- a/nixos/tests/spiped.nix
+++ b/nixos/tests/spiped.nix
@@ -5,7 +5,7 @@ in
{
name = "spiped";
meta = with pkgs.lib.maintainers; {
- maintainers = [ tomfitzhenry ];
+ maintainers = [ ];
};
nodes = {
diff --git a/nixos/tests/starship.nix b/nixos/tests/starship.nix
index 23e103fc000e..382666d8f176 100644
--- a/nixos/tests/starship.nix
+++ b/nixos/tests/starship.nix
@@ -1,53 +1,51 @@
-import ./make-test-python.nix (
- { pkgs, ... }:
- {
- name = "starship";
- meta.maintainers = pkgs.starship.meta.maintainers;
+{ pkgs, ... }:
+{
+ name = "starship";
+ meta.maintainers = pkgs.starship.meta.maintainers;
- nodes.machine = {
- programs = {
- fish.enable = true;
- zsh.enable = true;
+ nodes.machine = {
+ programs = {
+ fish.enable = true;
+ zsh.enable = true;
- starship = {
- enable = true;
- settings.format = "";
- };
+ starship = {
+ enable = true;
+ settings.format = "";
};
-
- environment.systemPackages =
- map
- (
- shell:
- pkgs.writeScriptBin "expect-${shell}" ''
- #!${pkgs.expect}/bin/expect -f
-
- spawn env TERM=xterm ${shell} -i
-
- expect "" {
- send "exit\n"
- } timeout {
- send_user "\n${shell} failed to display Starship\n"
- exit 1
- }
-
- expect eof
- ''
- )
- [
- "bash"
- "fish"
- "zsh"
- ];
};
- testScript = ''
- start_all()
- machine.wait_for_unit("default.target")
+ environment.systemPackages =
+ map
+ (
+ shell:
+ pkgs.writeScriptBin "expect-${shell}" ''
+ #!${pkgs.expect}/bin/expect -f
- machine.succeed("expect-bash")
- machine.succeed("expect-fish")
- machine.succeed("expect-zsh")
- '';
- }
-)
+ spawn env TERM=xterm ${shell} -i
+
+ expect "" {
+ send "exit\n"
+ } timeout {
+ send_user "\n${shell} failed to display Starship\n"
+ exit 1
+ }
+
+ expect eof
+ ''
+ )
+ [
+ "bash"
+ "fish"
+ "zsh"
+ ];
+ };
+
+ testScript = ''
+ start_all()
+ machine.wait_for_unit("default.target")
+
+ machine.succeed("expect-bash")
+ machine.succeed("expect-fish")
+ machine.succeed("expect-zsh")
+ '';
+}
diff --git a/nixos/tests/syncthing-folders.nix b/nixos/tests/syncthing-folders.nix
new file mode 100644
index 000000000000..d0623f7194d5
--- /dev/null
+++ b/nixos/tests/syncthing-folders.nix
@@ -0,0 +1,135 @@
+{ lib, pkgs, ... }:
+let
+ genNodeId =
+ name:
+ pkgs.runCommand "syncthing-test-certs-${name}" { } ''
+ mkdir -p $out
+ ${pkgs.syncthing}/bin/syncthing generate --config=$out
+ ${pkgs.libxml2}/bin/xmllint --xpath 'string(configuration/device/@id)' $out/config.xml > $out/id
+ '';
+ idA = genNodeId "a";
+ idB = genNodeId "b";
+ idC = genNodeId "c";
+ testPassword = "it's a secret";
+in
+{
+ name = "syncthing";
+ meta.maintainers = with pkgs.lib.maintainers; [ zarelit ];
+
+ nodes = {
+ a =
+ { config, ... }:
+ {
+ environment.etc.bar-encryption-password.text = testPassword;
+ services.syncthing = {
+ enable = true;
+ openDefaultPorts = true;
+ cert = "${idA}/cert.pem";
+ key = "${idA}/key.pem";
+ settings = {
+ devices.b.id = lib.fileContents "${idB}/id";
+ devices.c.id = lib.fileContents "${idC}/id";
+ folders.foo = {
+ path = "/var/lib/syncthing/foo";
+ devices = [ "b" ];
+ };
+ folders.bar = {
+ path = "/var/lib/syncthing/bar";
+ devices = [
+ {
+ name = "c";
+ encryptionPasswordFile = "/etc/${config.environment.etc.bar-encryption-password.target}";
+ }
+ ];
+ };
+ };
+ };
+ };
+ b =
+ { config, ... }:
+ {
+ environment.etc.bar-encryption-password.text = testPassword;
+ services.syncthing = {
+ enable = true;
+ openDefaultPorts = true;
+ cert = "${idB}/cert.pem";
+ key = "${idB}/key.pem";
+ settings = {
+ devices.a.id = lib.fileContents "${idA}/id";
+ devices.c.id = lib.fileContents "${idC}/id";
+ folders.foo = {
+ path = "/var/lib/syncthing/foo";
+ devices = [ "a" ];
+ };
+ folders.bar = {
+ path = "/var/lib/syncthing/bar";
+ devices = [
+ {
+ name = "c";
+ encryptionPasswordFile = "/etc/${config.environment.etc.bar-encryption-password.target}";
+ }
+ ];
+ };
+ };
+ };
+ };
+ c = {
+ services.syncthing = {
+ enable = true;
+ openDefaultPorts = true;
+ cert = "${idC}/cert.pem";
+ key = "${idC}/key.pem";
+ settings = {
+ devices.a.id = lib.fileContents "${idA}/id";
+ devices.b.id = lib.fileContents "${idB}/id";
+ folders.bar = {
+ path = "/var/lib/syncthing/bar";
+ devices = [
+ "a"
+ "b"
+ ];
+ type = "receiveencrypted";
+ };
+ };
+ };
+ };
+ };
+
+ testScript = ''
+ start_all()
+
+ a.wait_for_unit("syncthing.service")
+ b.wait_for_unit("syncthing.service")
+ c.wait_for_unit("syncthing.service")
+ a.wait_for_open_port(22000)
+ b.wait_for_open_port(22000)
+ c.wait_for_open_port(22000)
+
+ # Test foo
+
+ a.wait_for_file("/var/lib/syncthing/foo")
+ b.wait_for_file("/var/lib/syncthing/foo")
+
+ a.succeed("echo a2b > /var/lib/syncthing/foo/a2b")
+ b.succeed("echo b2a > /var/lib/syncthing/foo/b2a")
+
+ a.wait_for_file("/var/lib/syncthing/foo/b2a")
+ b.wait_for_file("/var/lib/syncthing/foo/a2b")
+
+ # Test bar
+
+ a.wait_for_file("/var/lib/syncthing/bar")
+ b.wait_for_file("/var/lib/syncthing/bar")
+ c.wait_for_file("/var/lib/syncthing/bar")
+
+ a.succeed("echo plaincontent > /var/lib/syncthing/bar/plainname")
+
+ # B should be able to decrypt, check that content of file matches
+ b.wait_for_file("/var/lib/syncthing/bar/plainname")
+ file_contents = b.succeed("cat /var/lib/syncthing/bar/plainname")
+ assert "plaincontent\n" == file_contents, f"Unexpected file contents: {file_contents=}"
+
+ # Bar on C is untrusted, check that content is not in cleartext
+ c.fail("grep -R plaincontent /var/lib/syncthing/bar")
+ '';
+}
diff --git a/nixos/tests/systemd-initrd-simple.nix b/nixos/tests/systemd-initrd-simple.nix
index 7a379404bbc2..191a23abb2e4 100644
--- a/nixos/tests/systemd-initrd-simple.nix
+++ b/nixos/tests/systemd-initrd-simple.nix
@@ -1,17 +1,17 @@
-import ./make-test-python.nix (
- { lib, pkgs, ... }:
- {
- name = "systemd-initrd-simple";
+{
+ name = "systemd-initrd-simple";
- nodes.machine =
- { pkgs, ... }:
- {
- testing.initrdBackdoor = true;
- boot.initrd.systemd.enable = true;
- virtualisation.fileSystems."/".autoResize = true;
- };
+ nodes.machine =
+ { pkgs, ... }:
+ {
+ testing.initrdBackdoor = true;
+ boot.initrd.systemd.enable = true;
+ virtualisation.fileSystems."/".autoResize = true;
+ };
- testScript = ''
+ testScript =
+ # python
+ ''
import subprocess
with subtest("testing initrd backdoor"):
@@ -50,6 +50,8 @@ import ./make-test-python.nix (
newAvail = machine.succeed("df --output=avail / | sed 1d")
assert int(oldAvail) < int(newAvail), "File system did not grow"
+
+ with subtest("no warnings from systemd about write permissions"):
+ machine.fail("journalctl -b 0 | grep 'is marked world-writable, which is a security risk as it is executed with privileges'")
'';
- }
-)
+}
diff --git a/nixos/tests/systemd-journal.nix b/nixos/tests/systemd-journal.nix
index 63ae58970e84..c39fc50e5006 100644
--- a/nixos/tests/systemd-journal.nix
+++ b/nixos/tests/systemd-journal.nix
@@ -12,11 +12,23 @@ import ./make-test-python.nix (
};
nodes.auditd = {
security.auditd.enable = true;
+ security.audit.enable = true;
environment.systemPackages = [ pkgs.audit ];
+ boot.kernel.sysctl."kernel.printk_ratelimit" = 0;
+ boot.kernelParams = [ "audit_backlog_limit=8192" ];
};
nodes.journaldAudit = {
services.journald.audit = true;
+ security.audit.enable = true;
environment.systemPackages = [ pkgs.audit ];
+ boot.kernel.sysctl."kernel.printk_ratelimit" = 0;
+ boot.kernelParams = [ "audit_backlog_limit=8192" ];
+ };
+ nodes.containerCheck = {
+ containers.c1 = {
+ autoStart = true;
+ config = { };
+ };
};
testScript = ''
@@ -50,6 +62,16 @@ import ./make-test-python.nix (
# logs ideally should NOT end up in kmesg, but they do due to
# https://github.com/systemd/systemd/issues/15324
journaldAudit.succeed("journalctl _TRANSPORT=kernel --grep 'unit=systemd-journald'")
+
+
+ with subtest("container systemd-journald-audit not running"):
+ containerCheck.wait_for_unit("multi-user.target");
+ containerCheck.wait_until_succeeds("systemctl -M c1 is-active default.target");
+
+ # systemd-journald-audit.socket should exist but not run due to the upstream unit's `Condition*` settings
+ (status, output) = containerCheck.execute("systemctl -M c1 is-active systemd-journald-audit.socket")
+ containerCheck.log(output)
+ assert status == 3 and output == "inactive\n", f"systemd-journald-audit.socket should exist in a container but remain inactive, was {output}"
'';
}
)
diff --git a/nixos/tests/systemd-machinectl.nix b/nixos/tests/systemd-machinectl.nix
index 5866c6d7603f..2e35d160f533 100644
--- a/nixos/tests/systemd-machinectl.nix
+++ b/nixos/tests/systemd-machinectl.nix
@@ -20,11 +20,13 @@ import ./make-test-python.nix (
imports = [ ../modules/profiles/minimal.nix ];
system.stateVersion = config.system.nixos.release;
+
+ nixpkgs.pkgs = pkgs;
};
containerSystem =
(import ../lib/eval-config.nix {
- inherit (pkgs) system;
+ system = null;
modules = [ container ];
}).config.system.build.toplevel;
diff --git a/nixos/tests/systemd-shutdown.nix b/nixos/tests/systemd-shutdown.nix
index 1f072086bdd1..fa0105cb90cf 100644
--- a/nixos/tests/systemd-shutdown.nix
+++ b/nixos/tests/systemd-shutdown.nix
@@ -17,12 +17,14 @@ import ./make-test-python.nix (
imports = [ ../modules/profiles/minimal.nix ];
systemd.shutdownRamfs.contents."/etc/systemd/system-shutdown/shutdown-message".source =
pkgs.writeShellScript "shutdown-message" ''
- echo "${msg}"
+ echo "${msg}" > /dev/kmsg
'';
boot.initrd.systemd.enable = systemdStage1;
};
testScript = ''
+ # Check that 'generate-shutdown-ramfs.service' is started
+ # automatically and that 'systemd-shutdown' runs our script.
machine.wait_for_unit("multi-user.target")
# .shutdown() would wait for the machine to power off
machine.succeed("systemctl poweroff")
@@ -31,6 +33,12 @@ import ./make-test-python.nix (
machine.wait_for_console_text("${msg}")
# Don't try to sync filesystems
machine.wait_for_shutdown()
+
+ # In a separate boot, start 'generate-shutdown-ramfs.service'
+ # manually in order to check the permissions on '/run/initramfs'.
+ machine.systemctl("start generate-shutdown-ramfs.service")
+ stat = machine.succeed("stat --printf=%a:%u:%g /run/initramfs")
+ assert stat == "700:0:0", f"Improper permissions on /run/initramfs: {stat}"
'';
}
)
diff --git a/nixos/tests/systemd-ssh-proxy.nix b/nixos/tests/systemd-ssh-proxy.nix
new file mode 100644
index 000000000000..6ccdc0012b4f
--- /dev/null
+++ b/nixos/tests/systemd-ssh-proxy.nix
@@ -0,0 +1,63 @@
+{
+ pkgs,
+ lib,
+ config,
+ ...
+}:
+# This tests that systemd-ssh-proxy and systemd-ssh-generator work correctly with:
+# - a local unix socket on the same system
+# - a unix socket inside a container
+let
+ inherit (import ./ssh-keys.nix pkgs)
+ snakeOilEd25519PrivateKey
+ snakeOilEd25519PublicKey
+ ;
+in
+{
+ name = "systemd-ssh-proxy";
+ meta.maintainers = with pkgs.lib.maintainers; [ marie ];
+
+ nodes = {
+ virthost = {
+ services.openssh = {
+ enable = true;
+ settings.PermitRootLogin = "prohibit-password";
+ };
+ users.users = {
+ root.openssh.authorizedKeys.keys = [ snakeOilEd25519PublicKey ];
+ nixos = {
+ isNormalUser = true;
+ };
+ };
+ containers.guest = {
+ autoStart = true;
+ config = {
+ users.users.root.openssh.authorizedKeys.keys = [ snakeOilEd25519PublicKey ];
+ services.openssh = {
+ enable = true;
+ settings.PermitRootLogin = "prohibit-password";
+ };
+ system.stateVersion = lib.trivial.release;
+ };
+ };
+ };
+ };
+
+ testScript = ''
+ virthost.succeed("mkdir -p ~/.ssh")
+ virthost.succeed("cp '${snakeOilEd25519PrivateKey}' ~/.ssh/id_ed25519")
+ virthost.succeed("chmod 600 ~/.ssh/id_ed25519")
+
+ with subtest("ssh into a container with AF_UNIX"):
+ virthost.wait_for_unit("container@guest.service")
+ virthost.wait_until_succeeds("ssh -i ~/.ssh/id_ed25519 unix/run/systemd/nspawn/unix-export/guest/ssh echo meow | grep meow")
+
+ with subtest("elevate permissions using local ssh socket"):
+ virthost.wait_for_unit("sshd-unix-local.socket")
+ virthost.succeed("sudo --user=nixos mkdir -p /home/nixos/.ssh")
+ virthost.succeed("cp ~/.ssh/id_ed25519 /home/nixos/.ssh/id_ed25519")
+ virthost.succeed("chmod 600 /home/nixos/.ssh/id_ed25519")
+ virthost.succeed("chown nixos /home/nixos/.ssh/id_ed25519")
+ virthost.succeed("sudo --user=nixos ssh -o StrictHostKeyChecking=no -o IdentitiesOnly=yes -i /home/nixos/.ssh/id_ed25519 root@.host whoami | grep root")
+ '';
+}
diff --git a/nixos/tests/taskchampion-sync-server.nix b/nixos/tests/taskchampion-sync-server.nix
index 42dfb0cbeca3..659a900fd2c2 100644
--- a/nixos/tests/taskchampion-sync-server.nix
+++ b/nixos/tests/taskchampion-sync-server.nix
@@ -6,6 +6,7 @@ import ./make-test-python.nix (
nodes = {
server = {
services.taskchampion-sync-server.enable = true;
+ services.taskchampion-sync-server.host = "0.0.0.0";
services.taskchampion-sync-server.openFirewall = true;
};
client =
diff --git a/nixos/tests/teleports.nix b/nixos/tests/teleports.nix
index a4293f954a45..613ad5a7fc84 100644
--- a/nixos/tests/teleports.nix
+++ b/nixos/tests/teleports.nix
@@ -34,14 +34,20 @@
machine.wait_for_x()
with subtest("teleports launches"):
- machine.execute("teleports >&2 &")
+ machine.succeed("teleports >&2 &")
+ machine.wait_for_console_text("authorizationStateWaitPhoneNumber")
+ machine.send_key("alt-f10")
+ machine.sleep(2)
machine.wait_for_text(r"(TELEports|Phone Number)")
machine.screenshot("teleports_open")
machine.succeed("pkill -f teleports")
with subtest("teleports localisation works"):
- machine.execute("env LANG=de_DE.UTF-8 teleports >&2 &")
+ machine.succeed("env LANG=de_DE.UTF-8 teleports >&2 &")
+ machine.wait_for_console_text("authorizationStateWaitPhoneNumber")
+ machine.send_key("alt-f10")
+ machine.sleep(2)
machine.wait_for_text("Telefonnummer")
machine.screenshot("teleports_localised")
'';
diff --git a/nixos/tests/tusd/default.nix b/nixos/tests/tusd/default.nix
new file mode 100644
index 000000000000..ec188dee56ca
--- /dev/null
+++ b/nixos/tests/tusd/default.nix
@@ -0,0 +1,51 @@
+{ pkgs, lib, ... }:
+
+let
+ port = 1080;
+
+ client =
+ { pkgs, ... }:
+ {
+ environment.systemPackages = [ pkgs.curl ];
+ };
+
+ server =
+ { pkgs, ... }:
+ {
+ # tusd does not have a NixOS service yet.
+ systemd.services.tusd = {
+ wantedBy = [ "multi-user.target" ];
+
+ serviceConfig = {
+ ExecStart = ''${pkgs.tusd}/bin/tusd -port "${toString port}" -upload-dir=/data'';
+ };
+ };
+ networking.firewall.allowedTCPPorts = [ port ];
+ };
+in
+{
+ name = "tusd";
+ meta.maintainers = with lib.maintainers; [
+ nh2
+ kalbasit
+ ];
+
+ nodes = {
+ inherit server;
+ inherit client;
+ };
+
+ testScript = ''
+ server.wait_for_unit("tusd.service")
+ server.wait_for_open_port(${toString port})
+
+ # Create large file.
+ client.succeed("${pkgs.coreutils}/bin/truncate --size=100M file-100M.bin")
+
+ # Upload it.
+ client.wait_for_unit("network.target")
+ client.succeed("${./tus-curl-upload.sh} file-100M.bin http://server:${toString port}/files/")
+
+ print("Upload succeeded")
+ '';
+}
diff --git a/nixos/tests/tusd/tus-curl-upload.sh b/nixos/tests/tusd/tus-curl-upload.sh
new file mode 100755
index 000000000000..a85d163cc25e
--- /dev/null
+++ b/nixos/tests/tusd/tus-curl-upload.sh
@@ -0,0 +1,50 @@
+#!/usr/bin/env bash
+
+# Adapted from:
+# - https://github.com/tus/tus.io/issues/96
+
+if [ ! -f "${1}" ]; then
+ echo -e "\n\033[1;31m✘\033[0m First argument needs to be an existing file.\n"
+ exit 1
+fi
+
+if [ -z "${2}" ]; then
+ echo -e "\n\033[1;31m✘\033[0m Second argument needs to be the TUS server's URL.\n"
+ exit 1
+fi
+
+file=${1}
+TUS_URL=${2}
+filename=$(basename "${file}" | base64)
+filesize="$(wc -c <"${file}")"
+
+# Apparently 'Location: ..' is terminated by CRLF. grep and awk faithfully
+# preserve the line ending, and the shell's $() substitution strips off the
+# final LF leaving you with a string that just ends with a CR.
+#
+# When the CR is printed, the cursor moves to the beginning of the line and
+# whatever gets printed next overwrites what was there.
+# ... | tr -d '\015'
+location=$(curl \
+ --silent --show-error \
+ -I \
+ -X POST \
+ -H "Tus-Resumable: 1.0.0" \
+ -H "Content-Length: 0" \
+ -H "Upload-Length: ${filesize}" \
+ -H "Upload-Metadata: name ${filename}" \
+ "${TUS_URL}" | grep 'Location:' | awk '{print $2}' | tr -d '\015')
+
+if [ -n "${location}" ]; then
+ curl \
+ -X PATCH \
+ -H "Tus-Resumable: 1.0.0" \
+ -H "Upload-Offset: 0" \
+ -H "Content-Length: ${filesize}" \
+ -H "Content-Type: application/offset+octet-stream" \
+ --data-binary "@${file}" \
+ "${location}" -v
+else
+ echo -e "\n\033[1;31m✘\033[0m File creation failed..\n"
+ exit 1
+fi
diff --git a/nixos/tests/tuxguitar.nix b/nixos/tests/tuxguitar.nix
index e491f96a89ce..d0df7d5c5bb5 100644
--- a/nixos/tests/tuxguitar.nix
+++ b/nixos/tests/tuxguitar.nix
@@ -1,29 +1,25 @@
-import ./make-test-python.nix (
- { pkgs, ... }:
- {
- name = "tuxguitar";
- meta = with pkgs.lib.maintainers; {
- maintainers = [ ];
+{ ... }:
+{
+ name = "tuxguitar";
+ meta.maintainers = [ ];
+
+ nodes.machine =
+ { config, pkgs, ... }:
+ {
+ imports = [
+ ./common/x11.nix
+ ];
+
+ services.xserver.enable = true;
+
+ environment.systemPackages = [ pkgs.tuxguitar ];
};
- nodes.machine =
- { config, pkgs, ... }:
- {
- imports = [
- ./common/x11.nix
- ];
-
- services.xserver.enable = true;
-
- environment.systemPackages = [ pkgs.tuxguitar ];
- };
-
- testScript = ''
- machine.wait_for_x()
- machine.succeed("tuxguitar >&2 &")
- machine.wait_for_window("TuxGuitar - Untitled.tg")
- machine.sleep(1)
- machine.screenshot("tuxguitar")
- '';
- }
-)
+ testScript = ''
+ machine.wait_for_x()
+ machine.succeed("tuxguitar >&2 &")
+ machine.wait_for_window("TuxGuitar - Untitled.tg")
+ machine.sleep(1)
+ machine.screenshot("tuxguitar")
+ '';
+}
diff --git a/nixos/tests/tzupdate.nix b/nixos/tests/tzupdate.nix
new file mode 100644
index 000000000000..a6defcfa18ea
--- /dev/null
+++ b/nixos/tests/tzupdate.nix
@@ -0,0 +1,22 @@
+{ lib, ... }:
+let
+ clientNodeName = "client";
+in
+{
+ name = "tzupdate";
+
+ # TODO: Test properly:
+ # - Add server node
+ # - Add client configuration to talk to the server node
+ # - Assert that the time zone changes appropriately
+ nodes.${clientNodeName} = {
+ services.tzupdate.enable = true;
+ };
+
+ testScript = ''
+ start_all()
+ ${clientNodeName}.wait_for_unit("multi-user.target")
+ '';
+
+ meta.maintainers = [ lib.maintainers.l0b0 ];
+}
diff --git a/nixos/tests/unifi.nix b/nixos/tests/unifi.nix
index a6ae0ab9fa58..ee3c8e1bdb84 100644
--- a/nixos/tests/unifi.nix
+++ b/nixos/tests/unifi.nix
@@ -1,45 +1,30 @@
-# Test UniFi controller
+{ lib, ... }:
{
- system ? builtins.currentSystem,
- config ? {
- allowUnfree = true;
- },
- pkgs ? import ../.. { inherit system config; },
-}:
+ name = "unifi";
-with import ../lib/testing-python.nix { inherit system pkgs; };
-with pkgs.lib;
+ meta.maintainers = with lib.maintainers; [
+ patryk27
+ zhaofengli
+ ];
-let
- makeAppTest =
- unifi:
- makeTest {
- name = "unifi-controller-${unifi.version}";
- meta = with pkgs.lib.maintainers; {
- maintainers = [
- patryk27
- zhaofengli
- ];
- };
+ node.pkgsReadOnly = false;
- nodes.server = {
- nixpkgs.config = config;
+ nodes.machine = {
+ nixpkgs.config.allowUnfree = true;
- services.unifi = {
- enable = true;
- unifiPackage = unifi;
- openFirewall = false;
- };
- };
+ services.unifi.enable = true;
+ };
- testScript = ''
- server.wait_for_unit("unifi.service")
- server.wait_until_succeeds("curl -Lk https://localhost:8443 >&2", timeout=300)
- '';
- };
-in
-with pkgs;
-{
- unifi8 = makeAppTest unifi8;
+ testScript = ''
+ import json
+
+ start_all()
+
+ machine.wait_for_unit("unifi.service")
+ machine.wait_for_open_port(8880)
+
+ status = json.loads(machine.succeed("curl --silent --show-error --fail-with-body http://localhost:8880/status"))
+ assert status["meta"]["rc"] == "ok"
+ '';
}
diff --git a/nixos/tests/vaultwarden.nix b/nixos/tests/vaultwarden.nix
index c656e9ef1a06..ce1e1de21abe 100644
--- a/nixos/tests/vaultwarden.nix
+++ b/nixos/tests/vaultwarden.nix
@@ -36,6 +36,17 @@ let
from selenium.webdriver.firefox.options import Options
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
+ from selenium.common.exceptions import ElementClickInterceptedException
+
+
+ def click_when_unobstructed(mark):
+ while True:
+ try:
+ wait.until(EC.element_to_be_clickable(mark)).click()
+ break
+ except ElementClickInterceptedException:
+ continue
+
options = Options()
options.add_argument('--headless')
@@ -74,7 +85,7 @@ let
)
driver.find_element(By.XPATH, "//button[contains(., 'Log in with master password')]").click()
- wait.until(EC.presence_of_element_located((By.CSS_SELECTOR, 'button#newItemDropdown'))).click()
+ click_when_unobstructed((By.CSS_SELECTOR, 'button#newItemDropdown'))
driver.find_element(By.XPATH, "//button[contains(., 'Item')]").click()
driver.find_element(By.CSS_SELECTOR, 'input#name').send_keys(
@@ -178,6 +189,8 @@ let
testScript
else
''
+ import json
+
start_all()
server.wait_for_unit("vaultwarden.service")
server.wait_for_open_port(8080)
@@ -202,11 +215,9 @@ let
client.succeed(f"bw --nointeraction --raw --session {key} sync -f")
with subtest("get the password with the cli"):
- password = client.wait_until_succeeds(
- f"bw --nointeraction --raw --session {key} list items | ${pkgs.jq}/bin/jq -r .[].login.password",
- timeout=60
- )
- assert password.strip() == "${storedPassword}"
+ output = json.loads(client.succeed(f"bw --nointeraction --raw --session {key} list items"))
+
+ assert output[0]['login']['password'] == "${storedPassword}"
with subtest("Check systemd unit hardening"):
server.log(server.succeed("systemd-analyze security vaultwarden.service | grep -v ✓"))
diff --git a/nixos/tests/vector/default.nix b/nixos/tests/vector/default.nix
index 7ec435bd848c..764689d5cb03 100644
--- a/nixos/tests/vector/default.nix
+++ b/nixos/tests/vector/default.nix
@@ -8,6 +8,7 @@
file-sink = import ./file-sink.nix { inherit system pkgs; };
api = import ./api.nix { inherit system pkgs; };
dnstap = import ./dnstap.nix { inherit system pkgs; };
+ journald-clickhouse = import ./journald-clickhouse.nix { inherit system pkgs; };
nginx-clickhouse = import ./nginx-clickhouse.nix { inherit system pkgs; };
syslog-quickwit = import ./syslog-quickwit.nix { inherit system pkgs; };
}
diff --git a/nixos/tests/vector/dnstap.nix b/nixos/tests/vector/dnstap.nix
index 935ea757e107..ec20ce1f6a1d 100644
--- a/nixos/tests/vector/dnstap.nix
+++ b/nixos/tests/vector/dnstap.nix
@@ -114,6 +114,7 @@ import ../make-test-python.nix (
unbound.wait_for_file("${dnstapSocket}")
unbound.succeed("test 770 -eq $(stat -c '%a' ${dnstapSocket})")
+ dnsclient.systemctl("start network-online.target")
dnsclient.wait_for_unit("network-online.target")
dnsclient.succeed(
"dig @unbound test.local"
diff --git a/nixos/tests/vector/journald-clickhouse.nix b/nixos/tests/vector/journald-clickhouse.nix
new file mode 100644
index 000000000000..6979ed5a9c87
--- /dev/null
+++ b/nixos/tests/vector/journald-clickhouse.nix
@@ -0,0 +1,157 @@
+import ../make-test-python.nix (
+ { lib, pkgs, ... }:
+ let
+ # Take the original journald message and create a new payload which only
+ # contains the relevant fields - these must match the database columns.
+ journalVrlRemapTransform = {
+ journald_remap = {
+ inputs = [ "journald" ];
+ type = "remap";
+ source = ''
+ m = {}
+ m.app = .SYSLOG_IDENTIFIER
+ m.host = .host
+ m.severity = to_int(.PRIORITY) ?? 0
+ m.level = to_syslog_level(m.severity) ?? ""
+ m.message = strip_ansi_escape_codes!(.message)
+ m.timestamp = .timestamp
+ m.uid = to_int(._UID) ?? 0
+ m.pid = to_int(._PID) ?? 0
+ . = [m]
+ '';
+ };
+ };
+ in
+ {
+ name = "vector-journald-clickhouse";
+ meta.maintainers = [ pkgs.lib.maintainers.happysalada ];
+
+ nodes = {
+ clickhouse =
+ { config, pkgs, ... }:
+ {
+ virtualisation.diskSize = 5 * 1024;
+ virtualisation.memorySize = 4096;
+
+ networking.firewall.allowedTCPPorts = [ 6000 ];
+
+ services.vector = {
+ enable = true;
+ journaldAccess = true;
+
+ settings = {
+ sources = {
+ journald = {
+ type = "journald";
+ };
+
+ vector_source = {
+ type = "vector";
+ address = "[::]:6000";
+ };
+ };
+
+ transforms = journalVrlRemapTransform;
+
+ sinks = {
+ clickhouse = {
+ type = "clickhouse";
+ inputs = [
+ "journald_remap"
+ "vector_source"
+ ];
+ endpoint = "http://localhost:8123";
+ database = "journald";
+ table = "logs";
+ date_time_best_effort = true;
+ };
+ };
+ };
+
+ };
+
+ services.clickhouse = {
+ enable = true;
+ };
+ };
+
+ vector =
+ { config, pkgs, ... }:
+ {
+ services.vector = {
+ enable = true;
+ journaldAccess = true;
+
+ settings = {
+ sources = {
+ journald = {
+ type = "journald";
+ };
+ };
+
+ transforms = journalVrlRemapTransform;
+
+ sinks = {
+ vector_sink = {
+ type = "vector";
+ inputs = [ "journald_remap" ];
+ address = "clickhouse:6000";
+ };
+ };
+ };
+ };
+ };
+ };
+
+ testScript =
+ let
+ # work around quote/substitution complexity by Nix, Perl, bash and SQL.
+ databaseDDL = pkgs.writeText "database.sql" "CREATE DATABASE IF NOT EXISTS journald";
+
+ # https://clickhouse.com/blog/storing-log-data-in-clickhouse-fluent-bit-vector-open-telemetry
+ tableDDL = pkgs.writeText "table.sql" ''
+ CREATE TABLE IF NOT EXISTS journald.logs (
+ timestamp DateTime64(6),
+ app LowCardinality(String),
+ host LowCardinality(String),
+ level LowCardinality(String),
+ severity UInt8,
+ message String,
+ uid UInt16,
+ pid UInt32,
+ )
+ ENGINE = MergeTree()
+ ORDER BY (host, app, timestamp)
+ PARTITION BY toYYYYMM(timestamp)
+ '';
+
+ selectQuery = pkgs.writeText "select.sql" ''
+ SELECT COUNT(host) FROM journald.logs
+ WHERE message LIKE '%Vector has started%'
+ '';
+ in
+ ''
+ clickhouse.wait_for_unit("clickhouse")
+ clickhouse.wait_for_open_port(6000)
+ clickhouse.wait_for_open_port(8123)
+
+ clickhouse.succeed(
+ "cat ${databaseDDL} | clickhouse-client"
+ )
+
+ clickhouse.succeed(
+ "cat ${tableDDL} | clickhouse-client"
+ )
+
+ for machine in clickhouse, vector:
+ machine.wait_for_unit("vector")
+ machine.wait_until_succeeds(
+ "journalctl -o cat -u vector.service | grep 'Vector has started'"
+ )
+
+ clickhouse.wait_until_succeeds(
+ "cat ${selectQuery} | clickhouse-client | grep 2"
+ )
+ '';
+ }
+)
diff --git a/nixos/tests/velocity.nix b/nixos/tests/velocity.nix
index 1e701977514d..d9ae4e75d87e 100644
--- a/nixos/tests/velocity.nix
+++ b/nixos/tests/velocity.nix
@@ -1,13 +1,7 @@
{ lib, pkgs, ... }:
{
name = "velocity";
- meta = {
- platforms = [
- "x86_64-linux"
- "aarch64-linux"
- ];
- maintainers = [ lib.maintainers.Tert0 ];
- };
+ meta.maintainers = [ lib.maintainers.Tert0 ];
nodes.server =
{ ... }:
diff --git a/nixos/tests/vscodium.nix b/nixos/tests/vscodium.nix
index 8600ccb81436..a3efd07bf501 100644
--- a/nixos/tests/vscodium.nix
+++ b/nixos/tests/vscodium.nix
@@ -62,14 +62,14 @@ let
codium_running.wait() # type: ignore[union-attr]
with codium_running: # type: ignore[union-attr]
# Wait until vscodium is visible. "File" is in the menu bar.
- machine.wait_for_text('Get Started with')
+ machine.wait_for_text('(Get|Started|with|Customize|theme)')
machine.screenshot('start_screen')
test_string = 'testfile'
# Create a new file
machine.send_key('ctrl-n')
- machine.wait_for_text('Untitled')
+ machine.wait_for_text('(Untitled|Select|language|template|dismiss)')
machine.screenshot('empty_editor')
# Type a string
diff --git a/nixos/tests/wastebin.nix b/nixos/tests/wastebin.nix
index dde1f658b948..79d05229eb7f 100644
--- a/nixos/tests/wastebin.nix
+++ b/nixos/tests/wastebin.nix
@@ -1,24 +1,22 @@
-import ./make-test-python.nix (
- { pkgs, lib, ... }:
- {
- name = "wastebin";
+{ lib, ... }:
+{
+ name = "wastebin";
- meta = {
- maintainers = with lib.maintainers; [ pinpox ];
+ meta = {
+ maintainers = with lib.maintainers; [ pinpox ];
+ };
+
+ nodes.machine =
+ { pkgs, ... }:
+ {
+ services.wastebin = {
+ enable = true;
+ };
};
- nodes.machine =
- { pkgs, ... }:
- {
- services.wastebin = {
- enable = true;
- };
- };
-
- testScript = ''
- machine.wait_for_unit("wastebin.service")
- machine.wait_for_open_port(8088)
- machine.succeed("curl --fail http://localhost:8088/")
- '';
- }
-)
+ testScript = ''
+ machine.wait_for_unit("wastebin.service")
+ machine.wait_for_open_port(8088)
+ machine.succeed("curl --fail http://localhost:8088/")
+ '';
+}
diff --git a/nixos/tests/web-apps/netbox-upgrade.nix b/nixos/tests/web-apps/netbox-upgrade.nix
index cc044b76f0b3..417df2a32af0 100644
--- a/nixos/tests/web-apps/netbox-upgrade.nix
+++ b/nixos/tests/web-apps/netbox-upgrade.nix
@@ -1,8 +1,18 @@
import ../make-test-python.nix (
{ lib, pkgs, ... }:
let
- oldNetbox = pkgs.netbox_3_7;
- newNetbox = pkgs.netbox_4_1;
+ oldNetbox = "netbox_4_1";
+ newNetbox = "netbox_4_2";
+
+ apiVersion =
+ version:
+ lib.pipe version [
+ (lib.splitString ".")
+ (lib.take 2)
+ (lib.concatStringsSep ".")
+ ];
+ oldApiVersion = apiVersion pkgs."${oldNetbox}".version;
+ newApiVersion = apiVersion pkgs."${newNetbox}".version;
in
{
name = "netbox-upgrade";
@@ -15,12 +25,14 @@ import ../make-test-python.nix (
};
nodes.machine =
- { config, ... }:
+ { config, pkgs, ... }:
{
virtualisation.memorySize = 2048;
services.netbox = {
enable = true;
- package = oldNetbox;
+ # Pick the NetBox package from this config's "pkgs" argument,
+ # so that `nixpkgs.config.permittedInsecurePackages` works
+ package = pkgs."${oldNetbox}";
secretKeyFile = pkgs.writeText "secret" ''
abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789
'';
@@ -42,22 +54,13 @@ import ../make-test-python.nix (
networking.firewall.allowedTCPPorts = [ 80 ];
- specialisation.upgrade.configuration.services.netbox.package = lib.mkForce newNetbox;
+ nixpkgs.config.permittedInsecurePackages = [ pkgs."${oldNetbox}".name ];
+
+ specialisation.upgrade.configuration.services.netbox.package = lib.mkForce pkgs."${newNetbox}";
};
testScript =
{ nodes, ... }:
- let
- apiVersion =
- version:
- lib.pipe version [
- (lib.splitString ".")
- (lib.take 2)
- (lib.concatStringsSep ".")
- ];
- oldApiVersion = apiVersion oldNetbox.version;
- newApiVersion = apiVersion newNetbox.version;
- in
''
start_all()
machine.wait_for_unit("netbox.target")
diff --git a/nixos/tests/web-apps/oncall.nix b/nixos/tests/web-apps/oncall.nix
new file mode 100644
index 000000000000..5fb765be6806
--- /dev/null
+++ b/nixos/tests/web-apps/oncall.nix
@@ -0,0 +1,156 @@
+{
+ lib,
+ pkgs,
+ config,
+ ...
+}:
+let
+ ldapDomain = "example.org";
+ ldapSuffix = "dc=example,dc=org";
+
+ ldapRootUser = "root";
+ ldapRootPassword = "foobar23";
+
+ testUser = "myuser";
+ testPassword = "foobar23";
+ teamName = "myteam";
+in
+{
+ name = "oncall";
+ meta.maintainers = with lib.maintainers; [ onny ];
+
+ nodes = {
+ machine = {
+ virtualisation.memorySize = 2048;
+
+ environment.etc."oncall-secrets.yml".text = ''
+ auth:
+ ldap_bind_password: "${ldapRootPassword}"
+ '';
+
+ environment.systemPackages = [ pkgs.jq ];
+
+ services.oncall = {
+ enable = true;
+ settings = {
+ auth = {
+ module = "oncall.auth.modules.ldap_import";
+ ldap_url = "ldap://localhost";
+ ldap_user_suffix = "";
+ ldap_bind_user = "cn=${ldapRootUser},${ldapSuffix}";
+ ldap_base_dn = "ou=accounts,${ldapSuffix}";
+ ldap_search_filter = "(uid=%s)";
+ import_user = true;
+ attrs = {
+ username = "uid";
+ full_name = "cn";
+ email = "mail";
+ call = "telephoneNumber";
+ sms = "mobile";
+ };
+ };
+ };
+ secretFile = "/etc/oncall-secrets.yml";
+ };
+
+ services.openldap = {
+ enable = true;
+ settings = {
+ children = {
+ "cn=schema".includes = [
+ "${pkgs.openldap}/etc/schema/core.ldif"
+ "${pkgs.openldap}/etc/schema/cosine.ldif"
+ "${pkgs.openldap}/etc/schema/inetorgperson.ldif"
+ "${pkgs.openldap}/etc/schema/nis.ldif"
+ ];
+ "olcDatabase={1}mdb" = {
+ attrs = {
+ objectClass = [
+ "olcDatabaseConfig"
+ "olcMdbConfig"
+ ];
+ olcDatabase = "{1}mdb";
+ olcDbDirectory = "/var/lib/openldap/db";
+ olcSuffix = ldapSuffix;
+ olcRootDN = "cn=${ldapRootUser},${ldapSuffix}";
+ olcRootPW = ldapRootPassword;
+ };
+ };
+ };
+ };
+ declarativeContents = {
+ ${ldapSuffix} = ''
+ dn: ${ldapSuffix}
+ objectClass: top
+ objectClass: dcObject
+ objectClass: organization
+ o: ${ldapDomain}
+
+ dn: ou=accounts,${ldapSuffix}
+ objectClass: top
+ objectClass: organizationalUnit
+
+ dn: uid=${testUser},ou=accounts,${ldapSuffix}
+ objectClass: top
+ objectClass: inetOrgPerson
+ uid: ${testUser}
+ userPassword: ${testPassword}
+ cn: Test User
+ sn: User
+ mail: test@example.org
+ telephoneNumber: 012345678910
+ mobile: 012345678910
+ '';
+ };
+ };
+ };
+ };
+
+ testScript = ''
+ start_all()
+ machine.wait_for_unit("uwsgi.service")
+ machine.wait_for_unit("nginx.service")
+ machine.wait_for_file("/run/uwsgi/oncall.sock")
+ machine.wait_for_unit("oncall-setup-database.service")
+
+ with subtest("Home screen loads"):
+ machine.succeed(
+ "curl -sSfL http://[::1]:80 | grep 'Oncall '"
+ )
+
+ with subtest("Staticfiles can be fetched"):
+ machine.wait_until_succeeds(
+ "curl -sSfL http://[::1]:80/static/bundles/libs.js"
+ )
+
+ with subtest("Staticfiles are generated"):
+ machine.succeed(
+ "test -e /var/lib/oncall/static/bundles/libs.js"
+ )
+
+ with subtest("Create and verify team via REST API"):
+ import json
+
+ # Log in and store the session cookie
+ login_response = machine.succeed("""
+ curl -sSfL -c cookies -X POST \
+ --data-raw 'username=${testUser}&password=${testPassword}' \
+ http://[::1]:80/login
+ """)
+
+ # Parse csrf token
+ login_response_data = json.loads(login_response)
+ csrf_token = login_response_data["csrf_token"]
+
+ # Create the team
+ machine.succeed(
+ f"""curl -sSfL -b cookies -X POST -H 'Content-Type: application/json' -H 'X-CSRF-Token: {csrf_token}' -d '{{"name": "${teamName}", "email": "test@example.com", "scheduling_timezone": "Europe/Berlin", "iris_enabled": false}}' http://[::1]:80/api/v0/teams/"""
+ )
+
+ # Query the created team
+ machine.succeed("""
+ curl -sSfL -b cookies http://[::1]:80/api/v0/teams/${teamName} | jq -e '.name == "${teamName}"'
+ """)
+
+ '';
+}
diff --git a/nixos/tests/web-apps/tt-rss.nix b/nixos/tests/web-apps/tt-rss.nix
index 7bd4413d7286..1dc2daf535d7 100644
--- a/nixos/tests/web-apps/tt-rss.nix
+++ b/nixos/tests/web-apps/tt-rss.nix
@@ -10,13 +10,38 @@ import ../make-test-python.nix (
enable = true;
virtualHost = "localhost";
selfUrlPath = "http://localhost/";
+ pluginPackages = with pkgs; [
+ tt-rss-plugin-auth-ldap
+ tt-rss-plugin-feediron
+ ];
+ plugins = [
+ "auth_internal"
+ "feediron"
+ "note"
+ ];
singleUserMode = true;
+ themePackages = with pkgs; [ tt-rss-theme-feedly ];
};
};
testScript = ''
+ import json
+ import re
machine.wait_for_unit("tt-rss.service")
- machine.succeed("curl -sSfL http://localhost/ | grep 'Tiny Tiny RSS'")
+
+ matches = re.search('__csrf_token = "([^"]*)"', machine.succeed("curl -sSfL --cookie cjar --cookie-jar cjar -sSfL http://localhost/"))
+ if matches is None:
+ assert False, "CSRF token not found"
+ csrf_token = matches.group(1)
+
+ # Ensure themes are loaded. No API found for these, so it's a crude check.
+ preference_page = machine.succeed("curl -sSfL --cookie cjar --cookie-jar cjar http://localhost/backend.php?op=Pref_Prefs")
+ assert "feedly" in preference_page
+
+ plugins = json.loads(machine.succeed(f"curl -sSfL --cookie cjar --cookie-jar cjar 'http://localhost/backend.php' -X POST --data-raw 'op=Pref_Prefs&method=getPluginsList&csrf_token={csrf_token}'"))["plugins"]
+ expected_plugins = ["auth_internal", "auth_ldap", "feediron", "note"];
+ found_plugins = [p["name"] for p in plugins if p["name"] in expected_plugins]
+ assert len(found_plugins) == len(expected_plugins), f"Expected plugins {expected_plugins}, found {found_plugins}"
'';
}
)
diff --git a/nixos/tests/web-apps/writefreely.nix b/nixos/tests/web-apps/writefreely.nix
index 3f56e1df8ead..fe9889e75332 100644
--- a/nixos/tests/web-apps/writefreely.nix
+++ b/nixos/tests/web-apps/writefreely.nix
@@ -1,16 +1,12 @@
{
- system ? builtins.currentSystem,
- config ? { },
- pkgs ? import ../../.. { inherit system config; },
+ runTest,
+ ...
}:
-with import ../../lib/testing-python.nix { inherit system pkgs; };
-with pkgs.lib;
-
let
writefreelyTest =
{ name, type }:
- makeTest {
+ runTest {
name = "writefreely-${name}";
nodes.machine =
diff --git a/nixos/tests/web-servers/static-web-server.nix b/nixos/tests/web-servers/static-web-server.nix
index 1c88e70a0957..0727b5cba021 100644
--- a/nixos/tests/web-servers/static-web-server.nix
+++ b/nixos/tests/web-servers/static-web-server.nix
@@ -1,41 +1,39 @@
-import ../make-test-python.nix (
- { pkgs, lib, ... }:
- {
- name = "static-web-server";
- meta = {
- maintainers = with lib.maintainers; [ mac-chaffee ];
- };
+{ pkgs, lib, ... }:
+{
+ name = "static-web-server";
+ meta = {
+ maintainers = with lib.maintainers; [ mac-chaffee ];
+ };
- nodes.machine =
- { pkgs, ... }:
- {
- services.static-web-server = {
- enable = true;
- listen = "[::]:8080";
- root = toString (
- pkgs.writeTextDir "nixos-test.html" ''
- Hello NixOS!
- ''
- );
- configuration = {
- general = {
- directory-listing = true;
- };
+ nodes.machine =
+ { pkgs, ... }:
+ {
+ services.static-web-server = {
+ enable = true;
+ listen = "[::]:8080";
+ root = toString (
+ pkgs.writeTextDir "nixos-test.html" ''
+ Hello NixOS!
+ ''
+ );
+ configuration = {
+ general = {
+ directory-listing = true;
};
};
};
+ };
- testScript = ''
- machine.start()
- machine.wait_for_unit("static-web-server.socket")
- machine.wait_for_open_port(8080)
- # We don't use wait_until_succeeds() because we're testing socket
- # activation which better work on the first request
- response = machine.succeed("curl -fsS localhost:8080")
- assert "nixos-test.html" in response, "The directory listing page did not include a link to our nixos-test.html file"
- response = machine.succeed("curl -fsS localhost:8080/nixos-test.html")
- assert "Hello NixOS!" in response
- machine.wait_for_unit("static-web-server.service")
- '';
- }
-)
+ testScript = ''
+ machine.start()
+ machine.wait_for_unit("static-web-server.socket")
+ machine.wait_for_open_port(8080)
+ # We don't use wait_until_succeeds() because we're testing socket
+ # activation which better work on the first request
+ response = machine.succeed("curl -fsS localhost:8080")
+ assert "nixos-test.html" in response, "The directory listing page did not include a link to our nixos-test.html file"
+ response = machine.succeed("curl -fsS localhost:8080/nixos-test.html")
+ assert "Hello NixOS!" in response
+ machine.wait_for_unit("static-web-server.service")
+ '';
+}
diff --git a/nixos/tests/whoami.nix b/nixos/tests/whoami.nix
new file mode 100644
index 000000000000..165d8ddb8072
--- /dev/null
+++ b/nixos/tests/whoami.nix
@@ -0,0 +1,32 @@
+{ lib, ... }:
+
+{
+ name = "echoip";
+ meta.maintainers = with lib.maintainers; [ defelo ];
+
+ nodes.machine = {
+ services.whoami.enable = true;
+ };
+
+ interactive.nodes.machine = {
+ networking.firewall.allowedTCPPorts = [ 8000 ];
+ virtualisation.forwardPorts = [
+ {
+ from = "host";
+ host.port = 8000;
+ guest.port = 8000;
+ }
+ ];
+ };
+
+ testScript = ''
+ import re
+
+ machine.wait_for_unit("whoami.service")
+ machine.wait_for_open_port(8000)
+
+ response = machine.succeed("curl -H 'X-Test-Header: Hello World!' http://127.0.0.1:8000/test")
+ assert re.search(r"^GET /test", response, re.M)
+ assert re.search(r"^X-Test-Header: Hello World!", response, re.M)
+ '';
+}
diff --git a/nixos/tests/wordpress.nix b/nixos/tests/wordpress.nix
index d639b3d6dfa2..0d789a4ac425 100644
--- a/nixos/tests/wordpress.nix
+++ b/nixos/tests/wordpress.nix
@@ -89,6 +89,7 @@ rec {
{ }
[
"6_7"
+ "6_8"
];
testScript = ''
diff --git a/nixos/tests/wstunnel.nix b/nixos/tests/wstunnel.nix
index 753f78061e7b..12547213f20b 100644
--- a/nixos/tests/wstunnel.nix
+++ b/nixos/tests/wstunnel.nix
@@ -8,8 +8,6 @@ in
{
name = "wstunnel";
- meta.platforms = lib.platforms.linux;
-
nodes = {
server = {
virtualisation.vlans = [ 1 ];
diff --git a/nixos/tests/xmpp/xmpp-sendmessage.nix b/nixos/tests/xmpp/xmpp-sendmessage.nix
index 62971d867304..21f471261cc6 100644
--- a/nixos/tests/xmpp/xmpp-sendmessage.nix
+++ b/nixos/tests/xmpp/xmpp-sendmessage.nix
@@ -15,7 +15,16 @@ let
'';
in
writeScriptBin "send-message" ''
- #!${(python3.withPackages (ps: [ ps.slixmpp ])).interpreter}
+ #!${
+ (python3.withPackages (
+ ps:
+ with ps;
+ [
+ slixmpp
+ ]
+ ++ slixmpp.optional-dependencies.xep-0363
+ )).interpreter
+ }
import logging
import sys
import signal
@@ -89,7 +98,7 @@ writeScriptBin "send-message" ''
# MUC
ct.register_plugin('xep_0045')
ct.connect(("${connectTo}", 5222))
- ct.process(forever=False)
+ ct.loop.run_until_complete(ct.disconnected)
if not ct.test_succeeded:
sys.exit(1)
diff --git a/nixos/tests/yarr.nix b/nixos/tests/yarr.nix
new file mode 100644
index 000000000000..a35d574a5af1
--- /dev/null
+++ b/nixos/tests/yarr.nix
@@ -0,0 +1,19 @@
+{ lib, pkgs, ... }:
+
+{
+ name = "yarr";
+ meta.maintainers = with lib.maintainers; [ christoph-heiss ];
+
+ nodes.machine =
+ { pkgs, ... }:
+ {
+ services.yarr.enable = true;
+ };
+
+ testScript = ''
+ machine.start()
+ machine.wait_for_unit("yarr.service")
+ machine.wait_for_open_port(7070)
+ machine.succeed("curl -sSf http://localhost:7070 | grep 'yarr! '")
+ '';
+}
diff --git a/nixos/tests/zram-generator.nix b/nixos/tests/zram-generator.nix
index 46414efee4b3..5f6432a78857 100644
--- a/nixos/tests/zram-generator.nix
+++ b/nixos/tests/zram-generator.nix
@@ -38,9 +38,9 @@
machine.wait_for_unit("systemd-zram-setup@zram0.service")
machine.wait_for_unit("systemd-zram-setup@zram1.service")
zram = machine.succeed("zramctl --noheadings --raw")
- swap = machine.succeed("swapon --show --noheadings")
+ swap = machine.succeed("swapon --show --noheadings --raw")
for i in range(2):
assert f"/dev/zram{i} lz4 10M" in zram
- assert f"/dev/zram{i} partition 10M" in swap
+ assert f"/dev/zram{i} partition 10M" in swap
'';
}
diff --git a/pkgs/README.md b/pkgs/README.md
index e6a05c19f834..878db5df8397 100644
--- a/pkgs/README.md
+++ b/pkgs/README.md
@@ -30,12 +30,32 @@ Before adding a new package, please consider the following questions:
* Is the package ready for general use? We don't want to include projects that are too immature or are going to be abandoned immediately. In case of doubt, check with upstream.
* Does the project have a clear license statement? Remember that software is unfree by default (all rights reserved), and merely providing access to the source code does not imply its redistribution. In case of doubt, ask upstream.
-* How realistic is it that it will be used by other people? It's good that nixpkgs caters to various niches, but if it's a niche of 5 people it's probably too small.
+* How realistic is it that it will be used by other people? It's good that nixpkgs caters to various niches, but if it's a niche of 5 people it's probably too small. A good estimate is checking upstream issues and pull requests, or other software repositories. Library packages should have at least one dependent.
+* Is the software actively maintained upstream? Especially packages that are security-critical, rely on fast-moving dependencies, or affect data integrity should see regular maintenance.
* Are you willing to maintain the package? You should care enough about the package to be willing to keep it up and running for at least one complete Nixpkgs' release life-cycle.
* In case you are not able to maintain the package you wrote, you can seek someone to fill that role, effectively adopting the package.
If any of these questions' answer is no, then you should probably not add the package.
+Special care has to be taken with security-critical software components. Because entries in the Nix store are inert and do nothing by themselves, packages should be considered by their intended use, e.g. when used together with a NixOS module.
+
+* Any package that immediately would need to be tagged with `meta.knownVulnerabilities` is unlikely to be fit for nixpkgs.
+* Any package depending on a known-vulnerable library should be considered carefully.
+* Packages typically used with untrusted data should have a maintained and responsible upstream. For example:
+ * Any package which does not follow upstream security policies should be considered vulnerable. In particular, packages that vendor or fork web engines like Blink, Gecko or Webkit need to keep up with the frequent updates of those projects.
+ * Any security-critical fast-moving package such as Chrome or Firefox (or their forks) must have at least one active committer among the maintainers. This ensures no critical fixes are delayed unnecessarily, endangering unsuspecting users.
+ * Services which typically work on web traffic are working on untrusted input.
+ * Data (such as archives or rich documents) commonly shared over untrusted channels (e.g. email) is untrusted.
+* Applications in the Unix authentication stack such as PAM/D-Bus modules or SUID binaries should be considered carefully, and should have a maintained and responsible upstream.
+* Encryption libraries should have a maintained and responsible upstream.
+* Security-critical components that are part of larger packages should be unvendored (=use the nixpkgs package as dependency, instead of vendored and pinned sources).
+* A "responsible upstream" includes various aspects, such as:
+ * channels to disclose security concerns
+ * being responsive to security concerns, providing fixes or workarounds
+ * transparent public disclosure of security issues when they are found or fixed
+ * These aspects are sometimes hard to verify, in which case an upstream that is not known to be irresponsible should be considered as responsible.
+* Source-available software should be built from source where possible. Binary blobs risk supply chain attacks and vendored outdated libraries.
+
This section describes a general framework of understanding and exceptions might apply.
Luckily it's pretty easy to maintain your own package set with Nix, which can then be added to the [Nix User Repository](https://github.com/nix-community/nur) project.
@@ -501,28 +521,29 @@ When using the `patches` parameter to `mkDerivation`, make sure the patch name c
### Fetching patches
-In the interest of keeping our maintenance burden and the size of Nixpkgs to a minimum, patches already merged upstream or published elsewhere _should_ be retrieved using `fetchpatch`:
+In the interest of keeping our maintenance burden and the size of Nixpkgs to a minimum, patches already merged upstream or published elsewhere _should_ be retrieved using `fetchpatch2`:
```nix
{
patches = [
- (fetchpatch {
+ (fetchpatch2 {
name = "fix-check-for-using-shared-freetype-lib.patch";
- url = "http://git.ghostscript.com/?p=ghostpdl.git;a=patch;h=8f5d285";
+ url = "https://cgit.ghostscript.com/cgi-bin/cgit.cgi/ghostpdl.git/patch/?id=8f5d28536e4518716fdfe974e580194c8f57871d";
hash = "sha256-uRcxaCjd+WAuGrXOmGfFeu79cUILwkRdBu48mwcBE7g=";
})
];
}
```
-If a patch is available online but does not cleanly apply, it can be modified in some fixed ways by using additional optional arguments for `fetchpatch`. Check [the `fetchpatch` reference](https://nixos.org/manual/nixpkgs/unstable/#fetchpatch) for details.
+If a patch is available online but does not cleanly apply, it can be modified in some fixed ways by using additional optional arguments for `fetchpatch2`. Check [the `fetchpatch` reference](https://nixos.org/manual/nixpkgs/unstable/#fetchpatch) for details.
+
+When adding patches in this manner you should be reasonably sure that the used URL is stable. Patches referencing open pull requests will change when the PR is updated and code forges (such as GitHub) usually garbage collect commits that are no longer reachable due to rebases/amends.
### Vendoring patches
In the following cases, a `.patch` file _should_ be added to Nixpkgs repository, instead of retrieved:
- solves problems unique to packaging in Nixpkgs
-- is already proposed upstream but not merged yet
- cannot be fetched easily
- has a high chance to disappear in the future due to unstable or unreliable URLs
diff --git a/pkgs/applications/audio/abcde/default.nix b/pkgs/applications/audio/abcde/default.nix
deleted file mode 100644
index 99dca3fe1716..000000000000
--- a/pkgs/applications/audio/abcde/default.nix
+++ /dev/null
@@ -1,91 +0,0 @@
-{
- lib,
- stdenv,
- fetchurl,
- libcdio-paranoia,
- cddiscid,
- wget,
- which,
- vorbis-tools,
- id3v2,
- eyed3,
- lame,
- flac,
- glyr,
- perlPackages,
- makeWrapper,
-}:
-
-let
- version = "2.9.3";
-in
-stdenv.mkDerivation {
- pname = "abcde";
- inherit version;
- src = fetchurl {
- url = "https://abcde.einval.com/download/abcde-${version}.tar.gz";
- sha256 = "091ip2iwb6b67bhjsj05l0sxyq2whqjycbzqpkfbpm4dlyxx0v04";
- };
-
- # FIXME: This package does not support `distmp3', `eject', etc.
-
- configurePhase = ''
- sed -i "s|^[[:blank:]]*prefix *=.*$|prefix = $out|g ;
- s|^[[:blank:]]*etcdir *=.*$|etcdir = $out/etc|g ;
- s|^[[:blank:]]*INSTALL *=.*$|INSTALL = install -c|g" \
- "Makefile";
-
- echo 'CDPARANOIA=${libcdio-paranoia}/bin/cd-paranoia' >>abcde.conf
- echo CDROMREADERSYNTAX=cdparanoia >>abcde.conf
-
- substituteInPlace "abcde" \
- --replace "/etc/abcde.conf" "$out/etc/abcde.conf"
- '';
-
- nativeBuildInputs = [ makeWrapper ];
-
- buildInputs = with perlPackages; [
- perl
- MusicBrainz
- MusicBrainzDiscID
- IOSocketSSL
- ];
-
- installFlags = [ "sysconfdir=$(out)/etc" ];
-
- postFixup = ''
- for cmd in abcde cddb-tool abcde-musicbrainz-tool; do
- wrapProgram "$out/bin/$cmd" \
- --prefix PERL5LIB : "$PERL5LIB" \
- --prefix PATH ":" ${
- lib.makeBinPath [
- "$out"
- which
- libcdio-paranoia
- cddiscid
- wget
- vorbis-tools
- id3v2
- eyed3
- lame
- flac
- glyr
- ]
- }
- done
- '';
-
- meta = with lib; {
- homepage = "http://abcde.einval.com/wiki/";
- license = licenses.gpl2Plus;
- maintainers = with maintainers; [ ];
- description = "Command-line audio CD ripper";
- longDescription = ''
- abcde is a front-end command-line utility (actually, a shell
- script) that grabs tracks off a CD, encodes them to
- Ogg/Vorbis, MP3, FLAC, Ogg/Speex and/or MPP/MP+ (Musepack)
- format, and tags them, all in one go.
- '';
- platforms = platforms.linux;
- };
-}
diff --git a/pkgs/applications/audio/adlplug/default.nix b/pkgs/applications/audio/adlplug/default.nix
deleted file mode 100644
index bf1278e42ce8..000000000000
--- a/pkgs/applications/audio/adlplug/default.nix
+++ /dev/null
@@ -1,111 +0,0 @@
-{
- lib,
- stdenv,
- fetchFromGitHub,
- cmake,
- pkg-config,
- fmt,
- liblo,
- alsa-lib,
- freetype,
- libX11,
- libXrandr,
- libXinerama,
- libXext,
- libXcursor,
-
- # Enabling JACK requires a JACK server at runtime, no fallback mechanism
- withJack ? false,
- jack,
-
- type ? "ADL",
-}:
-
-assert lib.assertOneOf "type" type [
- "ADL"
- "OPN"
-];
-let
- chip =
- {
- ADL = "OPL3";
- OPN = "OPN2";
- }
- .${type};
- mainProgram = "${type}plug";
-in
-stdenv.mkDerivation rec {
- pname = "${lib.strings.toLower type}plug";
- version = "unstable-2021-12-17";
-
- src = fetchFromGitHub {
- owner = "jpcima";
- repo = "ADLplug";
- rev = "a488abedf1783c61cb4f0caa689f1b01bf9aa17d";
- fetchSubmodules = true;
- sha256 = "1a5zw0rglqgc5wq1n0s5bxx7y59dsg6qy02236fakl34bvbk60yz";
- };
-
- cmakeFlags = [
- "-DADLplug_CHIP=${chip}"
- "-DADLplug_USE_SYSTEM_FMT=ON"
- "-DADLplug_Jack=${if withJack then "ON" else "OFF"}"
- ];
-
- NIX_LDFLAGS = toString (
- lib.optionals stdenv.hostPlatform.isDarwin [
- # Framework that JUCE needs which don't get linked properly
- "-framework CoreAudioKit"
- "-framework QuartzCore"
- "-framework AudioToolbox"
- ]
- ++ lib.optionals stdenv.hostPlatform.isLinux [
- # JUCE dlopen's these at runtime
- "-lX11"
- "-lXext"
- "-lXcursor"
- "-lXinerama"
- "-lXrandr"
- ]
- );
-
- nativeBuildInputs = [
- cmake
- pkg-config
- ];
-
- buildInputs =
- [
- fmt
- liblo
- ]
- ++ lib.optionals stdenv.hostPlatform.isLinux [
- alsa-lib
- freetype
- libX11
- libXrandr
- libXinerama
- libXext
- libXcursor
- ]
- ++ lib.optional withJack jack;
-
- postInstall = lib.optionalString stdenv.hostPlatform.isDarwin ''
- mkdir -p $out/{Applications,Library/Audio/Plug-Ins/{VST,Components}}
-
- mv $out/bin/${mainProgram}.app $out/Applications/
- ln -s $out/{Applications/${mainProgram}.app/Contents/MacOS,bin}/${mainProgram}
-
- mv vst2/${mainProgram}.vst $out/Library/Audio/Plug-Ins/VST/
- mv au/${mainProgram}.component $out/Library/Audio/Plug-Ins/Components/
- '';
-
- meta = with lib; {
- inherit mainProgram;
- description = "${chip} FM Chip Synthesizer";
- homepage = src.meta.homepage;
- license = licenses.boost;
- platforms = platforms.all;
- maintainers = with maintainers; [ OPNA2608 ];
- };
-}
diff --git a/pkgs/applications/audio/ardour/7.nix b/pkgs/applications/audio/ardour/7.nix
index 0830ff4ddf54..45c6b132d59b 100644
--- a/pkgs/applications/audio/ardour/7.nix
+++ b/pkgs/applications/audio/ardour/7.nix
@@ -7,7 +7,7 @@
fetchpatch2,
alsa-lib,
aubio,
- boost,
+ boost186,
cairomm,
cppunit,
curl,
@@ -127,7 +127,7 @@ stdenv.mkDerivation rec {
[
alsa-lib
aubio
- boost
+ boost186
cairomm
cppunit
curl
diff --git a/pkgs/applications/audio/bambootracker/default.nix b/pkgs/applications/audio/bambootracker/default.nix
deleted file mode 100644
index 5bf7445d2682..000000000000
--- a/pkgs/applications/audio/bambootracker/default.nix
+++ /dev/null
@@ -1,95 +0,0 @@
-{
- stdenv,
- lib,
- fetchFromGitHub,
- gitUpdater,
- pkg-config,
- qmake,
- qt5compat ? null,
- qtbase,
- qttools,
- qtwayland,
- rtaudio_6,
- rtmidi,
- wrapQtAppsHook,
-}:
-
-assert lib.versionAtLeast qtbase.version "6.0" -> qt5compat != null;
-
-stdenv.mkDerivation (finalAttrs: {
- pname = "bambootracker";
- version = "0.6.4";
-
- src = fetchFromGitHub {
- owner = "BambooTracker";
- repo = "BambooTracker";
- rev = "v${finalAttrs.version}";
- fetchSubmodules = true;
- hash = "sha256-tFUliKR55iZybNyYIF1FXh8RGf8jKEsGrWBuldB277g=";
- };
-
- postPatch = lib.optionalString (lib.versionAtLeast qtbase.version "6.0") ''
- # Work around lrelease finding in qmake being broken by using pre-Qt5.12 code path
- # https://github.com/NixOS/nixpkgs/issues/214765
- substituteInPlace BambooTracker/lang/lang.pri \
- --replace 'equals(QT_MAJOR_VERSION, 5):lessThan(QT_MINOR_VERSION, 12)' 'if(true)'
- '';
-
- nativeBuildInputs = [
- pkg-config
- qmake
- qttools
- wrapQtAppsHook
- ];
-
- buildInputs =
- [
- qtbase
- rtaudio_6
- rtmidi
- ]
- ++ lib.optionals stdenv.hostPlatform.isLinux [
- qtwayland
- ]
- ++ lib.optionals (lib.versionAtLeast qtbase.version "6.0") [
- qt5compat
- ];
-
- qmakeFlags =
- [
- "CONFIG+=system_rtaudio"
- "CONFIG+=system_rtmidi"
- ]
- ++ lib.optionals (stdenv.cc.isClang || (lib.versionAtLeast qtbase.version "6.0")) [
- # Clang is extra-strict about some deprecations
- # Latest Qt6 deprecated QCheckBox::stateChanged(int)
- "CONFIG+=no_warnings_are_errors"
- ];
-
- postConfigure = "make qmake_all";
-
- # Wrapping the inside of the app bundles, avoiding double-wrapping
- dontWrapQtApps = stdenv.hostPlatform.isDarwin;
-
- postInstall = lib.optionalString stdenv.hostPlatform.isDarwin ''
- mkdir -p $out/Applications
- mv $out/{bin,Applications}/BambooTracker.app
- ln -s $out/{Applications/BambooTracker.app/Contents/MacOS,bin}/BambooTracker
- wrapQtApp $out/Applications/BambooTracker.app/Contents/MacOS/BambooTracker
- '';
-
- passthru = {
- updateScript = gitUpdater {
- rev-prefix = "v";
- };
- };
-
- meta = with lib; {
- description = "Tracker for YM2608 (OPNA) which was used in NEC PC-8801/9801 series computers";
- mainProgram = "BambooTracker";
- homepage = "https://bambootracker.github.io/BambooTracker/";
- license = licenses.gpl2Plus;
- platforms = platforms.all;
- maintainers = with maintainers; [ OPNA2608 ];
- };
-})
diff --git a/pkgs/applications/audio/bitwig-studio/bitwig-studio5.nix b/pkgs/applications/audio/bitwig-studio/bitwig-studio5.nix
index 510c9b238c2e..38f8f1cac98c 100644
--- a/pkgs/applications/audio/bitwig-studio/bitwig-studio5.nix
+++ b/pkgs/applications/audio/bitwig-studio/bitwig-studio5.nix
@@ -16,6 +16,7 @@
libjack2,
libjpeg,
libnghttp2,
+ libudev-zero,
libxkbcommon,
makeWrapper,
pango,
@@ -30,12 +31,12 @@
stdenv.mkDerivation rec {
pname = "bitwig-studio-unwrapped";
- version = "5.3.1";
+ version = "5.3.5";
src = fetchurl {
name = "bitwig-studio-${version}.deb";
url = "https://www.bitwig.com/dl/Bitwig%20Studio/${version}/installer_linux/";
- hash = "sha256-mxodFCu4SDzofnoZZZ7TPDUIrRc3UJt8TuEBwDOo2wQ=";
+ hash = "sha256-dfEWOQTZVMUb6v+u2wQlFgTXupokFTjWgKKA6W/Rrzc=";
};
nativeBuildInputs = [
@@ -66,6 +67,7 @@ stdenv.mkDerivation rec {
xorg.libX11
xorg.libXtst
libxkbcommon
+ libudev-zero
pango
pipewire
(lib.getLib stdenv.cc.cc)
diff --git a/pkgs/applications/audio/calf/default.nix b/pkgs/applications/audio/calf/default.nix
deleted file mode 100644
index eee2d7f1b2dd..000000000000
--- a/pkgs/applications/audio/calf/default.nix
+++ /dev/null
@@ -1,62 +0,0 @@
-{
- lib,
- stdenv,
- cairo,
- expat,
- fftwSinglePrec,
- fluidsynth,
- glib,
- gtk2,
- libjack2,
- ladspaH,
- libglade,
- lv2,
- pkg-config,
- fetchFromGitHub,
- cmake,
-}:
-stdenv.mkDerivation rec {
- pname = "calf";
- version = "0.90.4";
-
- src = fetchFromGitHub {
- owner = "calf-studio-gear";
- repo = "calf";
- tag = version;
- hash = "sha256-E9H2YG1HAhIN+zJxDKIJTkJapbNz8h9dfd5YfZp9Zp0=";
- };
-
- outputs = [
- "out"
- "doc"
- ];
-
- enableParallelBuilding = true;
-
- nativeBuildInputs = [
- cmake
- pkg-config
- ];
-
- buildInputs = [
- cairo
- expat
- fftwSinglePrec
- fluidsynth
- glib
- gtk2
- libjack2
- ladspaH
- libglade
- lv2
- ];
-
- meta = {
- homepage = "https://calf-studio-gear.org";
- description = "Set of high quality open source audio plugins for musicians";
- license = lib.licenses.lgpl2;
- maintainers = [ ];
- platforms = lib.platforms.linux;
- mainProgram = "calfjackhost";
- };
-}
diff --git a/pkgs/applications/audio/cd-discid/default.nix b/pkgs/applications/audio/cd-discid/default.nix
deleted file mode 100644
index 25898ef8bd27..000000000000
--- a/pkgs/applications/audio/cd-discid/default.nix
+++ /dev/null
@@ -1,37 +0,0 @@
-{
- fetchurl,
- lib,
- stdenv,
- IOKit ? null,
-}:
-
-stdenv.mkDerivation rec {
- pname = "cd-discid";
- version = "1.4";
-
- src = fetchurl {
- url = "http://linukz.org/download/${pname}-${version}.tar.gz";
- sha256 = "0qrcvn7227qaayjcd5rm7z0k5q89qfy5qkdgwr5pd7ih0va8rmpz";
- };
-
- installFlags = [
- "PREFIX=$(out)"
- "INSTALL=install"
- ];
-
- buildInputs = [ ] ++ lib.optional stdenv.hostPlatform.isDarwin IOKit;
-
- meta = with lib; {
- homepage = "http://linukz.org/cd-discid.shtml";
- license = licenses.gpl2Plus;
- platforms = platforms.unix;
- description = "Command-line utility to get CDDB discid information from a CD-ROM disc";
- mainProgram = "cd-discid";
-
- longDescription = ''
- cd-discid is a backend utility to get CDDB discid information
- from a CD-ROM disc. It was originally designed for cdgrab (now
- abcde), but can be used for any purpose requiring CDDB data.
- '';
- };
-}
diff --git a/pkgs/applications/audio/chuck/default.nix b/pkgs/applications/audio/chuck/default.nix
index 54fe24415840..aa640b573ddd 100644
--- a/pkgs/applications/audio/chuck/default.nix
+++ b/pkgs/applications/audio/chuck/default.nix
@@ -9,13 +9,6 @@
which,
DarwinTools,
xcbuild,
- AppKit,
- Carbon,
- CoreAudio,
- CoreMIDI,
- CoreServices,
- Kernel,
- MultitouchSupport,
}:
stdenv.mkDerivation rec {
@@ -38,18 +31,7 @@ stdenv.mkDerivation rec {
xcbuild
];
- buildInputs =
- [ libsndfile ]
- ++ lib.optional (!stdenv.hostPlatform.isDarwin) alsa-lib
- ++ lib.optionals stdenv.hostPlatform.isDarwin [
- AppKit
- Carbon
- CoreAudio
- CoreMIDI
- CoreServices
- Kernel
- MultitouchSupport
- ];
+ buildInputs = [ libsndfile ] ++ lib.optional (!stdenv.hostPlatform.isDarwin) alsa-lib;
patches = [ ./darwin-limits.patch ];
diff --git a/pkgs/applications/audio/clementine/default.nix b/pkgs/applications/audio/clementine/default.nix
index a236c6867994..6d48c703765c 100644
--- a/pkgs/applications/audio/clementine/default.nix
+++ b/pkgs/applications/audio/clementine/default.nix
@@ -49,13 +49,13 @@ let
in
stdenv.mkDerivation (finalAttrs: {
pname = "clementine";
- version = "1.4.1-37-g3369f3085";
+ version = "1.4.1-44-g41bcdca7f";
src = fetchFromGitHub {
owner = "clementine-player";
repo = "Clementine";
tag = finalAttrs.version;
- hash = "sha256-zwt4PkCXVYJn8IsZL0JEJLX1LiAvDrNdhh0s2oDxGgY=";
+ hash = "sha256-LyYbcr0d0DI5nqNor6sXg7Hc/kYlORU9s8UJnQvSnZs=";
};
nativeBuildInputs = [
diff --git a/pkgs/applications/audio/cmus/default.nix b/pkgs/applications/audio/cmus/default.nix
index d4fef58281d0..167330ef6c6d 100644
--- a/pkgs/applications/audio/cmus/default.nix
+++ b/pkgs/applications/audio/cmus/default.nix
@@ -6,9 +6,6 @@
ncurses,
pkg-config,
libiconv,
- CoreAudio,
- AudioUnit,
- VideoToolbox,
alsaSupport ? stdenv.hostPlatform.isLinux,
alsa-lib ? null,
@@ -152,9 +149,6 @@ stdenv.mkDerivation rec {
[ ncurses ]
++ lib.optionals stdenv.hostPlatform.isDarwin [
libiconv
- CoreAudio
- AudioUnit
- VideoToolbox
]
++ lib.flatten (lib.concatMap (a: a.deps) opts);
diff --git a/pkgs/applications/audio/codecserver/default.nix b/pkgs/applications/audio/codecserver/default.nix
index c055c72c4eaf..2a632dc47319 100644
--- a/pkgs/applications/audio/codecserver/default.nix
+++ b/pkgs/applications/audio/codecserver/default.nix
@@ -41,7 +41,7 @@ stdenv.mkDerivation rec {
description = "Modular audio codec server";
license = licenses.gpl3Only;
platforms = platforms.unix;
- maintainers = teams.c3d2.members;
+ teams = [ teams.c3d2 ];
mainProgram = "codecserver";
};
}
diff --git a/pkgs/applications/audio/csound/default.nix b/pkgs/applications/audio/csound/default.nix
index 430bd1cb9fed..dc8989ef357c 100644
--- a/pkgs/applications/audio/csound/default.nix
+++ b/pkgs/applications/audio/csound/default.nix
@@ -9,10 +9,6 @@
bison,
boost,
gettext,
- Accelerate,
- AudioUnit,
- CoreAudio,
- CoreMIDI,
portaudio,
alsa-lib ? null,
libpulseaudio ? null,
@@ -61,10 +57,6 @@ stdenv.mkDerivation {
boost
]
++ lib.optionals stdenv.hostPlatform.isDarwin [
- Accelerate
- AudioUnit
- CoreAudio
- CoreMIDI
portaudio
]
++ lib.optionals stdenv.hostPlatform.isLinux (
@@ -93,5 +85,6 @@ stdenv.mkDerivation {
license = licenses.lgpl21Plus;
maintainers = [ maintainers.marcweber ];
platforms = platforms.unix;
+ broken = stdenv.hostPlatform.isDarwin;
};
}
diff --git a/pkgs/applications/audio/deadbeef/default.nix b/pkgs/applications/audio/deadbeef/default.nix
index 537ed9880665..222dfa3acb4e 100644
--- a/pkgs/applications/audio/deadbeef/default.nix
+++ b/pkgs/applications/audio/deadbeef/default.nix
@@ -70,7 +70,7 @@ assert gtk2Support || gtk3Support;
let
inherit (lib) optionals;
- version = "1.9.6";
+ version = "1.10.0";
in
clangStdenv.mkDerivation {
pname = "deadbeef";
@@ -81,7 +81,7 @@ clangStdenv.mkDerivation {
repo = "deadbeef";
fetchSubmodules = true;
rev = version;
- hash = "sha256-Q6hL4fOFPHn26ZqvrebgTMTgQZrhbXCEhM4ZFzNeyJE=";
+ hash = "sha256-qa0ULmE15lV2vkyXPNW9kSISQZEANrjwJwykTiifk5Q=";
};
buildInputs =
diff --git a/pkgs/applications/audio/drumkv1/default.nix b/pkgs/applications/audio/drumkv1/default.nix
index 9f2f4e2437b3..f9e224be71fa 100644
--- a/pkgs/applications/audio/drumkv1/default.nix
+++ b/pkgs/applications/audio/drumkv1/default.nix
@@ -15,11 +15,11 @@
stdenv.mkDerivation rec {
pname = "drumkv1";
- version = "1.3.0";
+ version = "1.3.1";
src = fetchurl {
url = "mirror://sourceforge/drumkv1/drumkv1-${version}.tar.gz";
- hash = "sha256-WcWhq1Li9dfj0piyW6F0mdfzcK+nvk5Rtl8pQZTYyt8=";
+ hash = "sha256-CzboTrMRxPr5O6caKrxW9X9uSi5Su5LRSQpwJBMGkGI=";
};
buildInputs = [
diff --git a/pkgs/applications/audio/easyabc/default.nix b/pkgs/applications/audio/easyabc/default.nix
deleted file mode 100644
index 16026b12750d..000000000000
--- a/pkgs/applications/audio/easyabc/default.nix
+++ /dev/null
@@ -1,94 +0,0 @@
-{
- lib,
- fetchFromGitHub,
- fetchPypi,
- replaceVars,
- python39,
- fluidsynth,
- soundfont-fluid,
- wrapGAppsHook3,
- abcmidi,
- abcm2ps,
- ghostscript,
-}:
-
-let
- # requires python39 due to https://stackoverflow.com/a/71902541 https://github.com/jwdj/EasyABC/issues/52
- python = python39.override {
- self = python;
- packageOverrides = self: super: {
- # currently broken with 4.2.1
- # https://github.com/jwdj/EasyABC/issues/75
- wxpython = super.wxpython.overrideAttrs (args: rec {
- version = "4.2.0";
- src = fetchPypi {
- inherit version;
- pname = "wxPython";
- hash = "sha256-ZjzrxFCdfl0RNRiGX+J093+VQ0xdV7w4btWNZc7thsc=";
- };
- });
- };
- };
-in
-python.pkgs.buildPythonApplication {
- pname = "easyabc";
- version = "1.3.8.6";
-
- src = fetchFromGitHub {
- owner = "jwdj";
- repo = "easyabc";
- rev = "6461b2c14280cb64224fc5299c31cfeef9b7d43c";
- hash = "sha256-leC3A4HQMeJNeZXArb3YAYr2mddGPcws618NrRh2Q1Y=";
- };
-
- nativeBuildInputs = [ wrapGAppsHook3 ];
-
- propagatedBuildInputs = with python.pkgs; [
- cx-freeze
- wxpython
- pygame
- ];
-
- # apparently setup.py only supports Windows and Darwin
- # everything is very non-standard in this project
- dontBuild = true;
- format = "other";
-
- # https://discourse.nixos.org/t/packaging-mcomix3-python-gtk-missing-gsettings-schemas-issue/10190/2
- strictDeps = false;
-
- patches = [
- (replaceVars ./hardcoded-paths.patch {
- fluidsynth = "${fluidsynth}/lib/libfluidsynth.so";
- soundfont = "${soundfont-fluid}/share/soundfonts/FluidR3_GM2-2.sf2";
- ghostscript = "${ghostscript}/bin/gs";
- })
- ];
-
- installPhase = ''
- runHook preInstall
-
- mkdir -p $out/share/easyabc
- mv * $out/share/easyabc
-
- ln -s ${abcmidi}/bin/abc2midi $out/share/easyabc/bin/abc2midi
- ln -s ${abcmidi}/bin/midi2abc $out/share/easyabc/bin/midi2abc
- ln -s ${abcmidi}/bin/abc2abc $out/share/easyabc/bin/abc2abc
- ln -s ${abcm2ps}/bin/abcm2ps $out/share/easyabc/bin/abcm2ps
-
- makeWrapper ${python.interpreter} $out/bin/easyabc \
- --set PYTHONPATH "$PYTHONPATH:$out/share/easyabc" \
- --add-flags "-O $out/share/easyabc/easy_abc.py"
-
- runHook postInstall
- '';
-
- meta = {
- description = "ABC music notation editor";
- mainProgram = "easyabc";
- homepage = "https://easyabc.sourceforge.net/";
- license = lib.licenses.gpl2Plus;
- platforms = lib.platforms.linux;
- maintainers = with lib.maintainers; [ mausch ];
- };
-}
diff --git a/pkgs/applications/audio/espeak-ng/default.nix b/pkgs/applications/audio/espeak-ng/default.nix
index 14835a9e0b60..19b5dd800b5b 100644
--- a/pkgs/applications/audio/espeak-ng/default.nix
+++ b/pkgs/applications/audio/espeak-ng/default.nix
@@ -17,9 +17,6 @@
pcaudiolib,
sonicSupport ? true,
sonic,
- CoreAudio,
- AudioToolbox,
- AudioUnit,
alsa-plugins,
makeWrapper,
}:
@@ -63,12 +60,7 @@ stdenv.mkDerivation rec {
buildInputs =
lib.optional mbrolaSupport mbrola
++ lib.optional pcaudiolibSupport pcaudiolib
- ++ lib.optional sonicSupport sonic
- ++ lib.optionals stdenv.hostPlatform.isDarwin [
- CoreAudio
- AudioToolbox
- AudioUnit
- ];
+ ++ lib.optional sonicSupport sonic;
# touch ChangeLog to avoid below error on darwin:
# Makefile.am: error: required file './ChangeLog.md' not found
diff --git a/pkgs/applications/audio/faust/faust2.nix b/pkgs/applications/audio/faust/faust2.nix
deleted file mode 100644
index 9304f4536e19..000000000000
--- a/pkgs/applications/audio/faust/faust2.nix
+++ /dev/null
@@ -1,289 +0,0 @@
-{
- lib,
- stdenv,
- coreutils,
- fetchFromGitHub,
- makeWrapper,
- pkg-config,
- cmake,
- llvm_18, # does not build with 19+ due to API changes
- emscripten,
- openssl,
- libsndfile,
- libmicrohttpd,
- gnutls,
- libtasn1,
- libxml2,
- p11-kit,
- vim,
- which,
- ncurses,
- fetchpatch,
-}:
-
-with lib.strings;
-
-let
-
- version = "2.79.3";
-
- src = fetchFromGitHub {
- owner = "grame-cncm";
- repo = "faust";
- rev = version;
- hash = "sha256-Rn+Cjpk4vttxARrkDSnpKdBdSRtgElsit8zu1BA8Jd4=";
- fetchSubmodules = true;
- };
-
- meta = with lib; {
- homepage = "https://faust.grame.fr/";
- downloadPage = "https://github.com/grame-cncm/faust/";
- license = licenses.gpl2;
- platforms = platforms.unix;
- maintainers = with maintainers; [
- magnetophon
- pmahoney
- ];
- };
-
- faust =
- let
- ncurses_static = ncurses.override { enableStatic = true; };
- in
- stdenv.mkDerivation {
-
- pname = "faust";
- inherit version;
-
- inherit src;
-
- nativeBuildInputs = [
- makeWrapper
- pkg-config
- cmake
- vim
- which
- ];
- buildInputs = [
- llvm_18
- emscripten
- openssl
- libsndfile
- libmicrohttpd
- gnutls
- libtasn1
- p11-kit
- ncurses_static
- libxml2
- ];
-
- passthru = { inherit wrap wrapWithBuildEnv faust2ApplBase; };
-
- preConfigure = ''
- # include llvm-config in path
- export PATH="${lib.getDev llvm_18}/bin:$PATH"
- cd build
- substituteInPlace Make.llvm.static \
- --replace 'mkdir -p $@ && cd $@ && ar -x ../../$<' 'mkdir -p $@ && cd $@ && ar -x ../source/build/lib/libfaust.a && cd ../source/build/'
- substituteInPlace Make.llvm.static \
- --replace 'rm -rf $(TMP)' ' ' \
- --replace-fail "ar" "${stdenv.cc.targetPrefix}ar"
- sed -i 's@LIBNCURSES_PATH ?= .*@LIBNCURSES_PATH ?= ${ncurses_static}/lib/libncurses.a@' Make.llvm.static
- cd ..
- shopt -s globstar
- for f in **/Makefile **/Makefile.library **/CMakeLists.txt build/Make.llvm.static embedded/faustjava/faust2engine architecture/autodiff/autodiff.sh source/tools/faust2appls/* **/llvm.cmake tools/benchmark/faust2object; do
- echo $f "llvm-config${lib.optionalString (stdenv.hostPlatform != stdenv.buildPlatform) "-native"}"
- substituteInPlace $f \
- --replace-quiet "llvm-config" "llvm-config${
- lib.optionalString (stdenv.hostPlatform != stdenv.buildPlatform) "-native"
- }"
- done
- shopt -u globstar
- cd build
- '';
-
- cmakeFlags = [
- "-C../backends/all.cmake"
- "-C../targets/all.cmake"
- ];
-
- postInstall = ''
- # syntax error when eval'd directly
- pattern="faust2!(*@(atomsnippets|graph|graphviewer|md|plot|sig|sigviewer|svg))"
- (shopt -s extglob; rm "$out"/bin/$pattern)
- '';
-
- postFixup = ''
- # The 'faustoptflags' is 'source'd into other faust scripts and
- # not used as an executable, so patch 'uname' usage directly
- # rather than use makeWrapper.
- substituteInPlace "$out"/bin/faustoptflags \
- --replace uname "${coreutils}/bin/uname"
-
- # wrapper for scripts that don't need faust.wrap*
- for script in "$out"/bin/faust2*; do
- wrapProgram "$script" \
- --prefix PATH : "$out"/bin
- done
- '';
-
- meta = meta // {
- description = "A functional programming language for realtime audio signal processing";
- longDescription = ''
- FAUST (Functional Audio Stream) is a functional programming
- language specifically designed for real-time signal processing
- and synthesis. FAUST targets high-performance signal processing
- applications and audio plug-ins for a variety of platforms and
- standards.
- The Faust compiler translates DSP specifications into very
- efficient C++ code. Thanks to the notion of architecture,
- FAUST programs can be easily deployed on a large variety of
- audio platforms and plugin formats (jack, alsa, ladspa, maxmsp,
- puredata, csound, supercollider, pure, vst, coreaudio) without
- any change to the FAUST code.
-
- This package has just the compiler, libraries, and headers.
- Install faust2* for specific faust2appl scripts.
- '';
- };
-
- };
-
- # Default values for faust2appl.
- faust2ApplBase =
- {
- baseName,
- dir ? "tools/faust2appls",
- scripts ? [ baseName ],
- ...
- }@args:
-
- args
- // {
- name = "${baseName}-${version}";
-
- inherit src;
-
- dontBuild = true;
-
- installPhase = ''
- runHook preInstall
-
- mkdir -p "$out/bin"
- for script in ${concatStringsSep " " scripts}; do
- cp "${dir}/$script" "$out/bin/"
- done
-
- runHook postInstall
- '';
-
- postInstall = ''
- # For the faust2appl script, change 'faustpath' and
- # 'faustoptflags' to absolute paths.
- for script in "$out"/bin/*; do
- substituteInPlace "$script" \
- --replace " error " "echo"
- done
- '';
-
- meta = meta // {
- description = "The ${baseName} script, part of faust functional programming language for realtime audio signal processing";
- };
- };
-
- # Some 'faust2appl' scripts, such as faust2alsa, run faust to
- # generate cpp code, then invoke the c++ compiler to build the code.
- # This builder wraps these scripts in parts of the stdenv such that
- # when the scripts are called outside any nix build, they behave as
- # if they were running inside a nix build in terms of compilers and
- # paths being configured (e.g. rpath is set so that compiled
- # binaries link to the libs inside the nix store)
- #
- # The function takes two main args: the appl name (e.g.
- # 'faust2alsa') and an optional list of propagatedBuildInputs. It
- # returns a derivation that contains only the bin/${appl} script,
- # wrapped up so that it will run as if it was inside a nix build
- # with those build inputs.
- #
- # The build input 'faust' is automatically added to the
- # propagatedBuildInputs.
- wrapWithBuildEnv =
- {
- baseName,
- propagatedBuildInputs ? [ ],
- ...
- }@args:
-
- stdenv.mkDerivation (
- (faust2ApplBase args)
- // {
-
- nativeBuildInputs = [
- pkg-config
- makeWrapper
- ];
-
- propagatedBuildInputs = [ faust ] ++ propagatedBuildInputs;
-
- libPath = lib.makeLibraryPath propagatedBuildInputs;
-
- postFixup = ''
-
- # export parts of the build environment
- for script in "$out"/bin/*; do
- # e.g. NIX_CC_WRAPPER_TARGET_HOST_x86_64_unknown_linux_gnu
- nix_cc_wrapper_target_host="$(printenv | grep ^NIX_CC_WRAPPER_TARGET_HOST | sed 's/=.*//')"
-
- # e.g. NIX_BINTOOLS_WRAPPER_TARGET_HOST_x86_64_unknown_linux_gnu
- nix_bintools_wrapper_target_host="$(printenv | grep ^NIX_BINTOOLS_WRAPPER_TARGET_HOST | sed 's/=.*//')"
-
- wrapProgram "$script" \
- --set FAUSTLDDIR "${faust}/lib" \
- --set FAUSTLIB "${faust}/share/faust" \
- --set FAUSTINC "${faust}/include/faust" \
- --set FAUSTARCH "${faust}/share/faust" \
- --prefix PATH : "$PATH" \
- --prefix PKG_CONFIG_PATH : "$PKG_CONFIG_PATH" \
- --set NIX_CFLAGS_COMPILE "$NIX_CFLAGS_COMPILE" \
- --set NIX_LDFLAGS "$NIX_LDFLAGS -lpthread" \
- --set "$nix_cc_wrapper_target_host" "''${!nix_cc_wrapper_target_host}" \
- --set "$nix_bintools_wrapper_target_host" "''${!nix_bintools_wrapper_target_host}" \
- --prefix LIBRARY_PATH "$libPath"
- done
- '';
- }
- );
-
- # Builder for 'faust2appl' scripts, such as faust2firefox that
- # simply need to be wrapped with some dependencies on PATH.
- #
- # The build input 'faust' is automatically added to the PATH.
- wrap =
- {
- baseName,
- runtimeInputs ? [ ],
- ...
- }@args:
-
- let
-
- runtimePath = concatStringsSep ":" (map (p: "${p}/bin") ([ faust ] ++ runtimeInputs));
-
- in
- stdenv.mkDerivation (
- (faust2ApplBase args)
- // {
-
- nativeBuildInputs = [ makeWrapper ];
-
- postFixup = ''
- for script in "$out"/bin/*; do
- wrapProgram "$script" --prefix PATH : "${runtimePath}"
- done
- '';
-
- }
- );
-
-in
-faust
diff --git a/pkgs/applications/audio/faust/faust2alqt.nix b/pkgs/applications/audio/faust/faust2alqt.nix
deleted file mode 100644
index 66d394e9d740..000000000000
--- a/pkgs/applications/audio/faust/faust2alqt.nix
+++ /dev/null
@@ -1,42 +0,0 @@
-{
- faust,
- alsa-lib,
- qtbase,
- writeText,
- buildPackages,
-}:
-let
- # Wrap the binary coming out of the the compilation script, so it knows QT_PLUGIN_PATH
- wrapBinary = writeText "wrapBinary" ''
- source ${buildPackages.makeWrapper}/nix-support/setup-hook
- for p in $FILES; do
- workpath=$PWD
- cd -- "$(dirname "$p")"
- binary=$(basename --suffix=.dsp "$p")
- rm -f .$binary-wrapped
- wrapProgram $binary --set QT_PLUGIN_PATH "${qtbase}/${qtbase.qtPluginPrefix}"
- sed -i $binary -e 's@exec@cd "$(dirname "$(readlink -f "''${BASH_SOURCE[0]}")")" \&\& exec@g'
- cd $workpath
- done
- '';
-in
-faust.wrapWithBuildEnv {
-
- baseName = "faust2alqt";
-
- propagatedBuildInputs = [
- alsa-lib
- qtbase
- ];
-
- dontWrapQtApps = true;
-
- preFixup = ''
- for script in "$out"/bin/*; do
- # append the wrapping code to the compilation script
- cat ${wrapBinary} >> $script
- # prevent the qmake error when running the script
- sed -i "/QMAKE=/c\ QMAKE="${qtbase.dev}/bin/qmake"" $script
- done
- '';
-}
diff --git a/pkgs/applications/audio/faust/faust2jack.nix b/pkgs/applications/audio/faust/faust2jack.nix
deleted file mode 100644
index 77f9b3910234..000000000000
--- a/pkgs/applications/audio/faust/faust2jack.nix
+++ /dev/null
@@ -1,29 +0,0 @@
-{
- faust,
- gtk2,
- jack2,
- alsa-lib,
- opencv,
- libsndfile,
- which,
-}:
-
-faust.wrapWithBuildEnv {
-
- baseName = "faust2jack";
-
- scripts = [
- "faust2jack"
- "faust2jackconsole"
- ];
-
- propagatedBuildInputs = [
- gtk2
- jack2
- alsa-lib
- opencv
- libsndfile
- which
- ];
-
-}
diff --git a/pkgs/applications/audio/faust/faust2jaqt.nix b/pkgs/applications/audio/faust/faust2jaqt.nix
deleted file mode 100644
index 5429d1bfb45a..000000000000
--- a/pkgs/applications/audio/faust/faust2jaqt.nix
+++ /dev/null
@@ -1,58 +0,0 @@
-{
- bash,
- faust,
- jack2,
- qtbase,
- libsndfile,
- alsa-lib,
- writeText,
- buildPackages,
- which,
-}:
-let
- # Wrap the binary coming out of the the compilation script, so it knows QT_PLUGIN_PATH
- wrapBinary = writeText "wrapBinary" ''
- source ${buildPackages.makeWrapper}/nix-support/setup-hook
- for p in $FILES; do
- workpath=$PWD
- cd -- "$(dirname "$p")"
- binary=$(basename --suffix=.dsp "$p")
- rm -f .$binary-wrapped
- wrapProgram $binary --set QT_PLUGIN_PATH "${qtbase}/${qtbase.qtPluginPrefix}"
- sed -i $binary -e 's@exec@cd "$(dirname "$(readlink -f "''${BASH_SOURCE[0]}")")" \&\& exec@g'
- cd $workpath
- done
- '';
-in
-faust.wrapWithBuildEnv {
-
- baseName = "faust2jaqt";
-
- scripts = [
- "faust2jaqt"
- "faust2jackserver"
- ];
-
- buildInputs = [
- bash
- ];
-
- propagatedBuildInputs = [
- jack2
- qtbase
- libsndfile
- alsa-lib
- which
- ];
-
- dontWrapQtApps = true;
-
- preFixup = ''
- for script in "$out"/bin/*; do
- # append the wrapping code to the compilation script
- cat ${wrapBinary} >> $script
- # prevent the qmake error when running the script
- sed -i "/QMAKE=/c\ QMAKE="${qtbase.dev}/bin/qmake"" $script
- done
- '';
-}
diff --git a/pkgs/applications/audio/faust/faust2lv2.nix b/pkgs/applications/audio/faust/faust2lv2.nix
deleted file mode 100644
index 4a310440ad8a..000000000000
--- a/pkgs/applications/audio/faust/faust2lv2.nix
+++ /dev/null
@@ -1,28 +0,0 @@
-{
- bash,
- boost,
- faust,
- lv2,
- qtbase,
-}:
-
-faust.wrapWithBuildEnv {
-
- baseName = "faust2lv2";
-
- buildInputs = [
- bash
- ];
-
- propagatedBuildInputs = [
- boost
- lv2
- qtbase
- ];
-
- dontWrapQtApps = true;
-
- preFixup = ''
- sed -i "/QMAKE=/c\ QMAKE="${qtbase.dev}/bin/qmake"" "$out"/bin/faust2lv2;
- '';
-}
diff --git a/pkgs/applications/audio/ft2-clone/default.nix b/pkgs/applications/audio/ft2-clone/default.nix
deleted file mode 100644
index 90e3d80ad14d..000000000000
--- a/pkgs/applications/audio/ft2-clone/default.nix
+++ /dev/null
@@ -1,53 +0,0 @@
-{
- lib,
- stdenv,
- fetchFromGitHub,
- cmake,
- nixosTests,
- alsa-lib,
- SDL2,
- libiconv,
- CoreAudio,
- CoreMIDI,
- CoreServices,
- Cocoa,
-}:
-
-stdenv.mkDerivation rec {
- pname = "ft2-clone";
- version = "1.95";
-
- src = fetchFromGitHub {
- owner = "8bitbubsy";
- repo = "ft2-clone";
- rev = "v${version}";
- hash = "sha256-Xb4LHoon56P6OmHvd7RkODrOc4MDa0+U8npypGhcyw4=";
- };
-
- nativeBuildInputs = [ cmake ];
- buildInputs =
- [ SDL2 ]
- ++ lib.optional stdenv.hostPlatform.isLinux alsa-lib
- ++ lib.optionals stdenv.hostPlatform.isDarwin [
- libiconv
- CoreAudio
- CoreMIDI
- CoreServices
- Cocoa
- ];
-
- passthru.tests = {
- ft2-clone-starts = nixosTests.ft2-clone;
- };
-
- meta = with lib; {
- description = "Highly accurate clone of the classic Fasttracker II software for MS-DOS";
- homepage = "https://16-bits.org/ft2.php";
- license = licenses.bsd3;
- maintainers = with maintainers; [ fgaz ];
- # From HOW-TO-COMPILE.txt:
- # > This code is NOT big-endian compatible
- platforms = platforms.littleEndian;
- mainProgram = "ft2-clone";
- };
-}
diff --git a/pkgs/applications/audio/grandorgue/default.nix b/pkgs/applications/audio/grandorgue/default.nix
deleted file mode 100644
index e31dbbed38b2..000000000000
--- a/pkgs/applications/audio/grandorgue/default.nix
+++ /dev/null
@@ -1,86 +0,0 @@
-{
- lib,
- stdenv,
- fetchFromGitHub,
- cmake,
- pkg-config,
- fftwFloat,
- alsa-lib,
- zlib,
- wavpack,
- wxGTK32,
- udev,
- jackaudioSupport ? false,
- libjack2,
- imagemagick,
- libicns,
- yaml-cpp,
- makeWrapper,
- Cocoa,
- includeDemo ? true,
-}:
-
-stdenv.mkDerivation rec {
- pname = "grandorgue";
- version = "3.15.4-1";
-
- src = fetchFromGitHub {
- owner = "GrandOrgue";
- repo = "grandorgue";
- rev = version;
- fetchSubmodules = true;
- hash = "sha256-9H7YpTtv9Y36Nc0WCyRy/ohpOQ3WVUd9gMahnGhANRc=";
- };
-
- patches = [ ./darwin-fixes.patch ];
-
- nativeBuildInputs = [
- cmake
- pkg-config
- imagemagick
- libicns
- makeWrapper
- ];
-
- buildInputs =
- [
- fftwFloat
- zlib
- wavpack
- wxGTK32
- yaml-cpp
- ]
- ++ lib.optionals stdenv.hostPlatform.isLinux [
- alsa-lib
- udev
- ]
- ++ lib.optionals stdenv.hostPlatform.isDarwin [ Cocoa ]
- ++ lib.optional jackaudioSupport libjack2;
-
- cmakeFlags =
- lib.optionals (!jackaudioSupport) [
- "-DRTAUDIO_USE_JACK=OFF"
- "-DRTMIDI_USE_JACK=OFF"
- "-DGO_USE_JACK=OFF"
- "-DINSTALL_DEPEND=OFF"
- ]
- ++ lib.optional (!includeDemo) "-DINSTALL_DEMO=OFF";
-
- postInstall = lib.optionalString stdenv.hostPlatform.isDarwin ''
- mkdir -p $out/{Applications,bin,lib}
- mv $out/GrandOrgue.app $out/Applications/
- for lib in $out/Applications/GrandOrgue.app/Contents/Frameworks/lib*; do
- ln -s $lib $out/lib/
- done
- makeWrapper $out/{Applications/GrandOrgue.app/Contents/MacOS,bin}/GrandOrgue
- '';
-
- meta = {
- description = "Virtual Pipe Organ Software";
- homepage = "https://github.com/GrandOrgue/grandorgue";
- license = lib.licenses.gpl2Plus;
- platforms = lib.platforms.unix;
- maintainers = [ lib.maintainers.puzzlewolf ];
- mainProgram = "GrandOrgue";
- };
-}
diff --git a/pkgs/applications/audio/hushboard/default.nix b/pkgs/applications/audio/hushboard/default.nix
index e5953a922e8e..19b072a361e2 100644
--- a/pkgs/applications/audio/hushboard/default.nix
+++ b/pkgs/applications/audio/hushboard/default.nix
@@ -70,6 +70,6 @@ buildPythonApplication {
description = "Mute your microphone while typing";
mainProgram = "hushboard";
platforms = platforms.linux;
- maintainers = with maintainers; [ sersorrel ];
+ maintainers = with maintainers; [ keysmashes ];
};
}
diff --git a/pkgs/applications/audio/jamesdsp/default.nix b/pkgs/applications/audio/jamesdsp/default.nix
index f98bdae72975..1849a8c428a2 100644
--- a/pkgs/applications/audio/jamesdsp/default.nix
+++ b/pkgs/applications/audio/jamesdsp/default.nix
@@ -49,6 +49,7 @@ stdenv.mkDerivation (finalAttrs: {
hash = "sha256-RtVKlw2ca8An4FodeD0RN95z9yHDHBgAxsEwLAmW7co=";
name = "fix-build-with-new-pipewire.patch";
})
+ ./fix-build-on-qt6_9.diff
];
buildInputs =
diff --git a/pkgs/applications/audio/jamesdsp/fix-build-on-qt6_9.diff b/pkgs/applications/audio/jamesdsp/fix-build-on-qt6_9.diff
new file mode 100644
index 000000000000..9706aa8eb614
--- /dev/null
+++ b/pkgs/applications/audio/jamesdsp/fix-build-on-qt6_9.diff
@@ -0,0 +1,22 @@
+diff --git a/src/subprojects/AutoEqIntegration/AeqPackageManager.cpp b/src/subprojects/AutoEqIntegration/AeqPackageManager.cpp
+index 01940a1..2ec9c5b 100644
+--- a/src/subprojects/AutoEqIntegration/AeqPackageManager.cpp
++++ b/src/subprojects/AutoEqIntegration/AeqPackageManager.cpp
+@@ -133,7 +133,7 @@ QtPromise::QPromise AeqPackageManager::getLocalVersion()
+ return QtPromise::QPromise{[&](
+ const QtPromise::QPromiseResolve& resolve,
+ const QtPromise::QPromiseReject& reject) {
+- QFile versionJson = (databaseDirectory() + "/version.json");
++ QFile versionJson(databaseDirectory() + "/version.json");
+ if(!versionJson.exists())
+ {
+ reject();
+@@ -159,7 +159,7 @@ QtPromise::QPromise> AeqPackageManager::getLocalIndex()
+ return QtPromise::QPromise